aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorHiroki Sato <hrs@FreeBSD.org>2018-04-04 04:21:19 +0000
committerHiroki Sato <hrs@FreeBSD.org>2018-04-04 04:21:19 +0000
commitd684f11da759490a8d98d7b790796106285f4084 (patch)
tree27b7356df710fdf1440fe2c23154b8121e99f2ab /lib
parentf52d4664e3f68828c06f85bfc1afa271e3e04713 (diff)
Import Heimdal 7.5.0.vendor/heimdal/7.5.0
Notes
Notes: svn path=/vendor-crypto/heimdal/dist/; revision=331978 svn path=/vendor-crypto/heimdal/7.5.0/; revision=331979; tag=vendor/heimdal/7.5.0
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am16
-rw-r--r--lib/Makefile.in437
-rw-r--r--lib/NTMakefile25
-rw-r--r--lib/asn1/Makefile.am39
-rw-r--r--lib/asn1/Makefile.in1029
-rw-r--r--lib/asn1/NTMakefile12
-rw-r--r--lib/asn1/README.template131
-rw-r--r--lib/asn1/asn1-common.h21
-rw-r--r--lib/asn1/asn1-template.h70
-rw-r--r--lib/asn1/asn1_gen.c4
-rw-r--r--lib/asn1/asn1_print.c19
-rw-r--r--lib/asn1/asn1parse.c273
-rw-r--r--lib/asn1/asn1parse.h4
-rw-r--r--lib/asn1/asn1parse.y57
-rw-r--r--lib/asn1/check-common.c4
-rw-r--r--lib/asn1/check-common.h19
-rw-r--r--lib/asn1/check-der.c189
-rw-r--r--lib/asn1/check-gen.c575
-rw-r--r--lib/asn1/check-template.c264
-rw-r--r--lib/asn1/der-private.h41
-rw-r--r--lib/asn1/der-protos.h72
-rw-r--r--lib/asn1/der.h2
-rw-r--r--lib/asn1/der_cmp.c17
-rw-r--r--lib/asn1/der_copy.c14
-rw-r--r--lib/asn1/der_format.c2
-rw-r--r--lib/asn1/der_free.c12
-rw-r--r--lib/asn1/der_get.c146
-rw-r--r--lib/asn1/der_length.c57
-rw-r--r--lib/asn1/der_put.c87
-rw-r--r--lib/asn1/digest.asn14
-rw-r--r--lib/asn1/fuzzer.c742
-rw-r--r--lib/asn1/gen.c105
-rw-r--r--lib/asn1/gen_decode.c36
-rw-r--r--lib/asn1/gen_encode.c21
-rw-r--r--lib/asn1/gen_glue.c3
-rw-r--r--lib/asn1/gen_length.c24
-rw-r--r--lib/asn1/gen_locl.h3
-rw-r--r--lib/asn1/gen_template.c187
-rw-r--r--lib/asn1/krb5.asn1146
-rw-r--r--lib/asn1/lex.c210
-rw-r--r--lib/asn1/lex.l7
-rw-r--r--lib/asn1/libasn1-exports.def36
-rw-r--r--lib/asn1/main.c41
-rw-r--r--lib/asn1/pkinit.asn12
-rw-r--r--lib/asn1/rfc2459.asn125
-rw-r--r--lib/asn1/roken_rename.h46
-rw-r--r--lib/asn1/symbol.c2
-rw-r--r--lib/asn1/symbol.h11
-rw-r--r--lib/asn1/template.c163
-rw-r--r--lib/asn1/test.asn160
-rw-r--r--lib/asn1/timegm.c4
-rw-r--r--lib/base/Makefile.am64
-rw-r--r--lib/base/Makefile.in1482
-rw-r--r--lib/base/NTMakefile82
-rw-r--r--lib/base/array.c478
-rw-r--r--lib/base/baselocl.h167
-rw-r--r--lib/base/bool.c59
-rw-r--r--lib/base/bsearch.c886
-rw-r--r--lib/base/data.c165
-rw-r--r--lib/base/db.c1727
-rw-r--r--lib/base/dict.c303
-rw-r--r--lib/base/dll.c324
-rw-r--r--lib/base/error.c178
-rw-r--r--lib/base/heimbase.c1079
-rw-r--r--lib/base/heimbase.h431
-rw-r--r--lib/base/heimbasepriv.h113
-rw-r--r--lib/base/heimqueue.h167
-rw-r--r--lib/base/json.c811
-rw-r--r--lib/base/null.c53
-rw-r--r--lib/base/number.c128
-rw-r--r--lib/base/roken_rename.h61
-rw-r--r--lib/base/string.c260
-rw-r--r--lib/base/test_base.c961
-rw-r--r--lib/base/version-script.map94
-rw-r--r--lib/com_err/Makefile.am3
-rw-r--r--lib/com_err/Makefile.in544
-rw-r--r--lib/com_err/com_err.3246
-rw-r--r--lib/com_err/com_err.c2
-rw-r--r--lib/com_err/com_err.h4
-rw-r--r--lib/com_err/com_right.h1
-rw-r--r--lib/com_err/compile_et.c9
-rw-r--r--lib/com_err/error.c2
-rw-r--r--lib/com_err/lex.c13
-rw-r--r--lib/com_err/lex.h2
-rw-r--r--lib/com_err/lex.l4
-rw-r--r--lib/gssapi/Makefile.am35
-rw-r--r--lib/gssapi/Makefile.in1365
-rw-r--r--lib/gssapi/NTMakefile9
-rwxr-xr-xlib/gssapi/gen-oid.pl141
-rw-r--r--lib/gssapi/gss-commands.in2
-rw-r--r--lib/gssapi/gss_acquire_cred.350
-rw-r--r--lib/gssapi/gss_acquire_cred.cat350
-rw-r--r--lib/gssapi/gssapi/gssapi.h82
-rw-r--r--lib/gssapi/gssapi/gssapi_krb5.h4
-rw-r--r--lib/gssapi/gssapi/gssapi_netlogon.h50
-rw-r--r--lib/gssapi/gssapi_mech.h64
-rw-r--r--lib/gssapi/gsstool.c23
-rw-r--r--lib/gssapi/krb5/8003.c2
-rw-r--r--lib/gssapi/krb5/accept_sec_context.c132
-rw-r--r--lib/gssapi/krb5/acquire_cred.c529
-rw-r--r--lib/gssapi/krb5/add_cred.c184
-rw-r--r--lib/gssapi/krb5/aeap.c98
-rw-r--r--lib/gssapi/krb5/arcfour.c650
-rw-r--r--lib/gssapi/krb5/authorize_localname.c2
-rw-r--r--lib/gssapi/krb5/canonicalize_name.c4
-rw-r--r--lib/gssapi/krb5/cfx.c6
-rw-r--r--lib/gssapi/krb5/compare_name.c4
-rw-r--r--lib/gssapi/krb5/context_time.c21
-rw-r--r--lib/gssapi/krb5/copy_ccache.c7
-rw-r--r--lib/gssapi/krb5/creds.c29
-rw-r--r--lib/gssapi/krb5/decapsulate.c3
-rw-r--r--lib/gssapi/krb5/display_name.c2
-rw-r--r--lib/gssapi/krb5/duplicate_name.c2
-rw-r--r--lib/gssapi/krb5/export_name.c2
-rw-r--r--lib/gssapi/krb5/export_sec_context.c12
-rw-r--r--lib/gssapi/krb5/external.c98
-rw-r--r--lib/gssapi/krb5/get_mic.c17
-rw-r--r--lib/gssapi/krb5/gsskrb5-private.h111
-rw-r--r--lib/gssapi/krb5/gsskrb5_locl.h7
-rw-r--r--lib/gssapi/krb5/import_name.c36
-rw-r--r--lib/gssapi/krb5/import_sec_context.c6
-rw-r--r--lib/gssapi/krb5/init_sec_context.c83
-rw-r--r--lib/gssapi/krb5/inquire_context.c4
-rw-r--r--lib/gssapi/krb5/inquire_cred.c257
-rw-r--r--lib/gssapi/krb5/inquire_cred_by_mech.c6
-rw-r--r--lib/gssapi/krb5/inquire_cred_by_oid.c2
-rw-r--r--lib/gssapi/krb5/inquire_mechs_for_name.c2
-rw-r--r--lib/gssapi/krb5/inquire_sec_context_by_oid.c7
-rw-r--r--lib/gssapi/krb5/pname_to_uid.c41
-rw-r--r--lib/gssapi/krb5/prf.c4
-rw-r--r--lib/gssapi/krb5/process_context_token.c2
-rw-r--r--lib/gssapi/krb5/set_sec_context_option.c18
-rw-r--r--lib/gssapi/krb5/store_cred.c83
-rw-r--r--lib/gssapi/krb5/test_acquire_cred.c162
-rw-r--r--lib/gssapi/krb5/test_cfx.c2
-rw-r--r--lib/gssapi/krb5/test_cred.c217
-rw-r--r--lib/gssapi/krb5/test_kcred.c152
-rw-r--r--lib/gssapi/krb5/test_oid.c51
-rw-r--r--lib/gssapi/krb5/unwrap.c17
-rw-r--r--lib/gssapi/krb5/verify_mic.c32
-rw-r--r--lib/gssapi/krb5/wrap.c34
-rw-r--r--lib/gssapi/libgssapi-exports.def3
-rw-r--r--lib/gssapi/mech/compat.h6
-rw-r--r--lib/gssapi/mech/doxygen.c23
-rw-r--r--lib/gssapi/mech/gss_accept_sec_context.c2
-rw-r--r--lib/gssapi/mech/gss_acquire_cred.c2
-rw-r--r--lib/gssapi/mech/gss_acquire_cred_ext.c24
-rw-r--r--lib/gssapi/mech/gss_acquire_cred_with_password.c5
-rw-r--r--lib/gssapi/mech/gss_add_cred.c4
-rw-r--r--lib/gssapi/mech/gss_add_cred_with_password.c4
-rw-r--r--lib/gssapi/mech/gss_aeap.c120
-rw-r--r--lib/gssapi/mech/gss_authorize_localname.c6
-rw-r--r--lib/gssapi/mech/gss_canonicalize_name.c2
-rw-r--r--lib/gssapi/mech/gss_compare_name.c6
-rw-r--r--lib/gssapi/mech/gss_context_time.c2
-rw-r--r--lib/gssapi/mech/gss_delete_sec_context.c4
-rw-r--r--lib/gssapi/mech/gss_display_name.c2
-rw-r--r--lib/gssapi/mech/gss_display_status.c26
-rw-r--r--lib/gssapi/mech/gss_duplicate_name.c2
-rw-r--r--lib/gssapi/mech/gss_export_name.c16
-rw-r--r--lib/gssapi/mech/gss_get_mic.c2
-rw-r--r--lib/gssapi/mech/gss_import_name.c12
-rw-r--r--lib/gssapi/mech/gss_indicate_mechs.c5
-rw-r--r--lib/gssapi/mech/gss_init_sec_context.c17
-rw-r--r--lib/gssapi/mech/gss_inquire_context.c2
-rw-r--r--lib/gssapi/mech/gss_inquire_cred.c2
-rw-r--r--lib/gssapi/mech/gss_inquire_cred_by_mech.c2
-rw-r--r--lib/gssapi/mech/gss_inquire_cred_by_oid.c2
-rw-r--r--lib/gssapi/mech/gss_inquire_mechs_for_name.c2
-rw-r--r--lib/gssapi/mech/gss_inquire_sec_context_by_oid.c2
-rw-r--r--lib/gssapi/mech/gss_mech_switch.c5
-rw-r--r--lib/gssapi/mech/gss_mo.c1
-rw-r--r--lib/gssapi/mech/gss_oid.c56
-rw-r--r--lib/gssapi/mech/gss_pname_to_uid.c174
-rw-r--r--lib/gssapi/mech/gss_process_context_token.c2
-rw-r--r--lib/gssapi/mech/gss_store_cred.c36
-rw-r--r--lib/gssapi/mech/gss_unwrap.c2
-rw-r--r--lib/gssapi/mech/gss_verify_mic.c2
-rw-r--r--lib/gssapi/mech/gss_wrap.c2
-rw-r--r--lib/gssapi/mech/gss_wrap_size_limit.c2
-rw-r--r--lib/gssapi/mech/mech.52
-rw-r--r--lib/gssapi/mech/mech.cat52
-rw-r--r--lib/gssapi/ntlm/accept_sec_context.c2
-rw-r--r--lib/gssapi/ntlm/acquire_cred.c29
-rw-r--r--lib/gssapi/ntlm/add_cred.c4
-rw-r--r--lib/gssapi/ntlm/canonicalize_name.c2
-rw-r--r--lib/gssapi/ntlm/compare_name.c4
-rw-r--r--lib/gssapi/ntlm/context_time.c2
-rw-r--r--lib/gssapi/ntlm/creds.c7
-rw-r--r--lib/gssapi/ntlm/crypto.c28
-rw-r--r--lib/gssapi/ntlm/display_name.c2
-rw-r--r--lib/gssapi/ntlm/duplicate_name.c2
-rw-r--r--lib/gssapi/ntlm/export_name.c2
-rw-r--r--lib/gssapi/ntlm/external.c3
-rw-r--r--lib/gssapi/ntlm/init_sec_context.c77
-rw-r--r--lib/gssapi/ntlm/inquire_context.c2
-rw-r--r--lib/gssapi/ntlm/inquire_cred_by_mech.c2
-rw-r--r--lib/gssapi/ntlm/inquire_mechs_for_name.c2
-rw-r--r--lib/gssapi/ntlm/inquire_sec_context_by_oid.c2
-rw-r--r--lib/gssapi/ntlm/kdc.c10
-rw-r--r--lib/gssapi/ntlm/ntlm-private.h56
-rw-r--r--lib/gssapi/ntlm/process_context_token.c2
-rw-r--r--lib/gssapi/oid.txt142
-rw-r--r--lib/gssapi/spnego/accept_sec_context.c6
-rw-r--r--lib/gssapi/spnego/compat.c2
-rw-r--r--lib/gssapi/spnego/context_stubs.c41
-rw-r--r--lib/gssapi/spnego/cred_stubs.c8
-rw-r--r--lib/gssapi/spnego/external.c17
-rw-r--r--lib/gssapi/spnego/init_sec_context.c12
-rw-r--r--lib/gssapi/spnego/spnego-private.h48
-rw-r--r--lib/gssapi/test_add_store_cred.c179
-rw-r--r--lib/gssapi/test_common.c12
-rw-r--r--lib/gssapi/test_context.c196
-rw-r--r--lib/gssapi/test_cred.c4
-rw-r--r--lib/gssapi/test_ntlm.c14
-rw-r--r--lib/gssapi/version-script.map3
-rw-r--r--lib/hcrypto/Makefile.am39
-rw-r--r--lib/hcrypto/Makefile.in3432
-rw-r--r--lib/hcrypto/NTMakefile91
-rw-r--r--lib/hcrypto/aes.c6
-rw-r--r--lib/hcrypto/bn.c8
-rw-r--r--lib/hcrypto/bn.h9
-rw-r--r--lib/hcrypto/camellia-ntt.c8
-rw-r--r--lib/hcrypto/camellia.c7
-rw-r--r--lib/hcrypto/common.c6
-rw-r--r--lib/hcrypto/des.c7
-rw-r--r--lib/hcrypto/destest.c6
-rw-r--r--lib/hcrypto/dh-ltm.c9
-rw-r--r--lib/hcrypto/dh-tfm.c248
-rw-r--r--lib/hcrypto/dh.c5
-rw-r--r--lib/hcrypto/dh.h2
-rw-r--r--lib/hcrypto/dsa.c16
-rw-r--r--lib/hcrypto/dsa.h2
-rw-r--r--lib/hcrypto/ec.h6
-rw-r--r--lib/hcrypto/engine.c7
-rw-r--r--lib/hcrypto/engine.h4
-rw-r--r--lib/hcrypto/evp-cc.c203
-rw-r--r--lib/hcrypto/evp-cc.h4
-rw-r--r--lib/hcrypto/evp-crypt.c217
-rw-r--r--lib/hcrypto/evp-hcrypto.c11
-rw-r--r--lib/hcrypto/evp-openssl.c644
-rw-r--r--lib/hcrypto/evp-openssl.h102
-rw-r--r--lib/hcrypto/evp-pkcs11.c796
-rw-r--r--lib/hcrypto/evp-pkcs11.h157
-rw-r--r--lib/hcrypto/evp-w32.c140
-rw-r--r--lib/hcrypto/evp-w32.h100
-rw-r--r--lib/hcrypto/evp-wincng.c705
-rw-r--r--lib/hcrypto/evp-wincng.h97
-rw-r--r--lib/hcrypto/evp.c57
-rw-r--r--lib/hcrypto/evp.h52
-rw-r--r--lib/hcrypto/example_evp_cipher.c33
-rw-r--r--lib/hcrypto/hash.h1
-rw-r--r--lib/hcrypto/hmac.c7
-rw-r--r--lib/hcrypto/libhcrypto-exports.def64
-rw-r--r--lib/hcrypto/libtommath/bn_error.c4
-rw-r--r--lib/hcrypto/libtommath/bn_mp_find_prime.c18
-rw-r--r--lib/hcrypto/libtommath/bn_mp_isprime.c75
-rw-r--r--lib/hcrypto/libtommath/bn_mp_mod.c8
-rw-r--r--lib/hcrypto/libtommath/bn_mp_prime_next_prime.c2
-rw-r--r--lib/hcrypto/libtommath/bn_mp_rand.c4
-rw-r--r--lib/hcrypto/libtommath/bn_mp_shrink.c11
-rw-r--r--lib/hcrypto/libtommath/tommath.h10
-rw-r--r--lib/hcrypto/libtommath/tommath_class.h2
-rw-r--r--lib/hcrypto/md2.c14
-rw-r--r--lib/hcrypto/md2.h6
-rw-r--r--lib/hcrypto/md4.c12
-rw-r--r--lib/hcrypto/md4.h6
-rw-r--r--lib/hcrypto/md5.c20
-rw-r--r--lib/hcrypto/md5.h6
-rw-r--r--lib/hcrypto/md5crypt_test.c4
-rw-r--r--lib/hcrypto/mdtest.c22
-rw-r--r--lib/hcrypto/passwd_dlg.c2
-rw-r--r--lib/hcrypto/pkcs12.c6
-rw-r--r--lib/hcrypto/pkcs5.c46
-rw-r--r--lib/hcrypto/rand-egd.c260
-rw-r--r--lib/hcrypto/rand-fortuna.c30
-rw-r--r--lib/hcrypto/rand-timer.c16
-rw-r--r--lib/hcrypto/rand-unix.c28
-rw-r--r--lib/hcrypto/rand-w32.c150
-rw-r--r--lib/hcrypto/rand.c11
-rw-r--r--lib/hcrypto/rand.h8
-rw-r--r--lib/hcrypto/randi.h1
-rw-r--r--lib/hcrypto/rc2.c4
-rw-r--r--lib/hcrypto/rc2test.c4
-rw-r--r--lib/hcrypto/rc4.c3
-rw-r--r--lib/hcrypto/rctest.c4
-rw-r--r--lib/hcrypto/rijndael-alg-fst.c5
-rw-r--r--lib/hcrypto/rnd_keys.c5
-rw-r--r--lib/hcrypto/rsa-gmp.c6
-rw-r--r--lib/hcrypto/rsa-ltm.c56
-rw-r--r--lib/hcrypto/rsa-tfm.c573
-rw-r--r--lib/hcrypto/rsa.c7
-rw-r--r--lib/hcrypto/rsa.h5
-rw-r--r--lib/hcrypto/sha.c12
-rw-r--r--lib/hcrypto/sha.h24
-rw-r--r--lib/hcrypto/sha256.c12
-rw-r--r--lib/hcrypto/sha512.c21
-rw-r--r--lib/hcrypto/test_bn.c7
-rw-r--r--lib/hcrypto/test_bulk.c314
-rw-r--r--lib/hcrypto/test_cipher.c102
-rw-r--r--lib/hcrypto/test_crypto.in2
-rw-r--r--lib/hcrypto/test_dh.c4
-rw-r--r--lib/hcrypto/test_engine_dso.c3
-rw-r--r--lib/hcrypto/test_hmac.c8
-rw-r--r--lib/hcrypto/test_pkcs12.c7
-rw-r--r--lib/hcrypto/test_pkcs5.c27
-rw-r--r--lib/hcrypto/test_rand.c68
-rw-r--r--lib/hcrypto/test_rsa.c15
-rw-r--r--lib/hcrypto/ui.c6
-rw-r--r--lib/hcrypto/undef.h169
-rw-r--r--lib/hcrypto/validate.c17
-rw-r--r--lib/hcrypto/version-script.map44
-rw-r--r--lib/hdb/Makefile.am41
-rw-r--r--lib/hdb/Makefile.in531
-rw-r--r--lib/hdb/NTMakefile17
-rw-r--r--lib/hdb/common.c140
-rw-r--r--lib/hdb/db.c143
-rw-r--r--lib/hdb/db3.c235
-rw-r--r--lib/hdb/dbinfo.c41
-rw-r--r--lib/hdb/ext.c106
-rw-r--r--lib/hdb/hdb-ldap.c231
-rw-r--r--lib/hdb/hdb-mdb.c399
-rw-r--r--lib/hdb/hdb-mitdb.c1013
-rw-r--r--lib/hdb/hdb-private.h22
-rw-r--r--lib/hdb/hdb-protos.h180
-rw-r--r--lib/hdb/hdb-sqlite.c446
-rw-r--r--lib/hdb/hdb.asn127
-rw-r--r--lib/hdb/hdb.c318
-rw-r--r--lib/hdb/hdb.h61
-rw-r--r--lib/hdb/hdb.schema7
-rw-r--r--lib/hdb/hdb_err.et3
-rw-r--r--lib/hdb/hdb_locl.h3
-rw-r--r--lib/hdb/keys.c527
-rw-r--r--lib/hdb/keytab.c24
-rw-r--r--lib/hdb/libhdb-exports.def20
-rw-r--r--lib/hdb/mkey.c187
-rw-r--r--lib/hdb/print.c372
-rw-r--r--lib/hdb/test_dbinfo.c4
-rw-r--r--lib/hdb/test_hdbkeys.c12
-rw-r--r--lib/hdb/test_hdbplugin.c107
-rw-r--r--lib/hdb/test_mkey.c6
-rw-r--r--lib/hdb/version-script.map34
-rw-r--r--lib/heimdal/NTMakefile8
-rw-r--r--lib/hx509/Makefile.am107
-rw-r--r--lib/hx509/Makefile.in1688
-rw-r--r--lib/hx509/NTMakefile10
-rw-r--r--lib/hx509/ca.c65
-rw-r--r--lib/hx509/cert.c148
-rw-r--r--lib/hx509/cms.c46
-rw-r--r--lib/hx509/crypto-ec.c533
-rw-r--r--lib/hx509/crypto.c635
-rw-r--r--lib/hx509/data/PKITS_data.zipbin0 -> 2149008 bytes
-rw-r--r--lib/hx509/data/eccurve.pem3
-rw-r--r--lib/hx509/data/https.crt53
-rw-r--r--lib/hx509/data/https.key16
-rwxr-xr-xlib/hx509/data/mkcert.sh84
-rw-r--r--lib/hx509/data/nist-result231
-rw-r--r--lib/hx509/data/openssl.cnf2
-rw-r--r--lib/hx509/data/secp160r1TestCA.cert.pem12
-rw-r--r--lib/hx509/data/secp160r1TestCA.key.pem4
-rw-r--r--lib/hx509/data/secp160r1TestCA.pem18
-rw-r--r--lib/hx509/data/secp160r2TestClient.cert.pem9
-rw-r--r--lib/hx509/data/secp160r2TestClient.key.pem4
-rw-r--r--lib/hx509/data/secp160r2TestClient.pem15
-rw-r--r--lib/hx509/data/secp160r2TestServer.cert.pem9
-rw-r--r--lib/hx509/data/secp160r2TestServer.key.pem4
-rw-r--r--lib/hx509/data/secp160r2TestServer.pem15
-rw-r--r--lib/hx509/data/secp256r1TestCA.cert.pem12
-rw-r--r--lib/hx509/data/secp256r1TestCA.key.pem5
-rw-r--r--lib/hx509/data/secp256r1TestCA.pem17
-rw-r--r--lib/hx509/data/secp256r2TestClient.cert.pem12
-rw-r--r--lib/hx509/data/secp256r2TestClient.key.pem5
-rw-r--r--lib/hx509/data/secp256r2TestClient.pem17
-rw-r--r--lib/hx509/data/secp256r2TestServer.cert.pem12
-rw-r--r--lib/hx509/data/secp256r2TestServer.key.pem5
-rw-r--r--lib/hx509/data/secp256r2TestServer.pem17
-rw-r--r--lib/hx509/doxygen.c2
-rw-r--r--lib/hx509/env.c16
-rw-r--r--lib/hx509/error.c66
-rw-r--r--lib/hx509/file.c4
-rw-r--r--lib/hx509/hx509-private.h20
-rw-r--r--lib/hx509/hx509-protos.h1925
-rw-r--r--lib/hx509/hx509.h1
-rw-r--r--lib/hx509/hx509_err.et22
-rw-r--r--lib/hx509/hx_locl.h103
-rw-r--r--lib/hx509/hxtool-commands.in20
-rw-r--r--lib/hx509/hxtool.c67
-rw-r--r--lib/hx509/keyset.c30
-rw-r--r--lib/hx509/ks_dir.c5
-rw-r--r--lib/hx509/ks_file.c15
-rw-r--r--lib/hx509/ks_keychain.c25
-rw-r--r--lib/hx509/ks_null.c5
-rw-r--r--lib/hx509/ks_p11.c70
-rw-r--r--lib/hx509/ks_p12.c13
-rw-r--r--lib/hx509/libhx509-exports.def3
-rw-r--r--lib/hx509/lock.c5
-rw-r--r--lib/hx509/name.c20
-rw-r--r--lib/hx509/print.c4
-rw-r--r--lib/hx509/ref/pkcs11.h387
-rw-r--r--lib/hx509/revoke.c234
-rw-r--r--lib/hx509/sel-gram.c76
-rw-r--r--lib/hx509/sel-gram.h2
-rw-r--r--lib/hx509/sel-gram.y14
-rw-r--r--lib/hx509/sel-lex.c49
-rw-r--r--lib/hx509/sel-lex.l12
-rw-r--r--lib/hx509/sel.h8
-rw-r--r--lib/hx509/softp11.c31
-rw-r--r--lib/hx509/test_ca.in28
-rw-r--r--lib/hx509/test_cert.in2
-rw-r--r--lib/hx509/test_chain.in8
-rw-r--r--lib/hx509/test_cms.in12
-rw-r--r--lib/hx509/test_name.c16
-rw-r--r--lib/hx509/test_soft_pkcs11.c2
-rw-r--r--lib/hx509/version-script.map7
-rw-r--r--lib/ipc/Makefile.am5
-rw-r--r--lib/ipc/Makefile.in847
-rw-r--r--lib/ipc/client.c6
-rw-r--r--lib/ipc/common.c2
-rw-r--r--lib/ipc/heim-ipc.h2
-rw-r--r--lib/ipc/server.c19
-rw-r--r--lib/ipc/tc.c6
-rw-r--r--lib/ipc/ts-http.c4
-rw-r--r--lib/ipc/ts.c4
-rw-r--r--lib/kadm5/Makefile.am33
-rw-r--r--lib/kadm5/Makefile.in895
-rw-r--r--lib/kadm5/NTMakefile10
-rw-r--r--lib/kadm5/acl.c3
-rw-r--r--lib/kadm5/ad.c115
-rw-r--r--lib/kadm5/admin.h53
-rw-r--r--lib/kadm5/chpass_c.c14
-rw-r--r--lib/kadm5/chpass_s.c192
-rw-r--r--lib/kadm5/common_glue.c304
-rw-r--r--lib/kadm5/context_s.c146
-rw-r--r--lib/kadm5/create_c.c10
-rw-r--r--lib/kadm5/create_s.c97
-rw-r--r--lib/kadm5/default_keys.c3
-rw-r--r--lib/kadm5/delete_s.c45
-rw-r--r--lib/kadm5/destroy_c.c3
-rw-r--r--lib/kadm5/destroy_s.c21
-rw-r--r--lib/kadm5/ent_setup.c11
-rw-r--r--lib/kadm5/free.c11
-rw-r--r--lib/kadm5/get_princs_s.c30
-rw-r--r--lib/kadm5/get_s.c215
-rw-r--r--lib/kadm5/init_c.c33
-rw-r--r--lib/kadm5/init_s.c63
-rw-r--r--lib/kadm5/iprop-commands.in65
-rw-r--r--lib/kadm5/iprop-log.8131
-rw-r--r--lib/kadm5/iprop-log.c215
-rw-r--r--lib/kadm5/iprop-log.cat871
-rw-r--r--lib/kadm5/iprop.818
-rw-r--r--lib/kadm5/iprop.cat825
-rw-r--r--lib/kadm5/iprop.h12
-rw-r--r--lib/kadm5/ipropd_common.c194
-rw-r--r--lib/kadm5/ipropd_master.c700
-rw-r--r--lib/kadm5/ipropd_slave.c725
-rw-r--r--lib/kadm5/kadm5-private.h103
-rw-r--r--lib/kadm5/kadm5-protos.h151
-rw-r--r--lib/kadm5/kadm5_err.et10
-rw-r--r--lib/kadm5/kadm5_locl.h1
-rw-r--r--lib/kadm5/keys.c36
-rw-r--r--lib/kadm5/libkadm5srv-exports.def23
-rw-r--r--lib/kadm5/log.c2515
-rw-r--r--lib/kadm5/marshall.c84
-rw-r--r--lib/kadm5/modify_s.c86
-rw-r--r--lib/kadm5/password_quality.c28
-rw-r--r--lib/kadm5/private.h62
-rw-r--r--lib/kadm5/randkey_c.c76
-rw-r--r--lib/kadm5/randkey_s.c86
-rw-r--r--lib/kadm5/rename_s.c74
-rw-r--r--lib/kadm5/send_recv.c6
-rw-r--r--lib/kadm5/set_keys.c221
-rw-r--r--lib/kadm5/setkey3_s.c135
-rw-r--r--lib/kadm5/test_pw_quality.c8
-rw-r--r--lib/kadm5/version-script-client.map59
-rw-r--r--lib/kadm5/version-script.map23
-rw-r--r--lib/kafs/Makefile.am5
-rw-r--r--lib/kafs/Makefile.in466
-rw-r--r--lib/kafs/afskrb5.c26
-rw-r--r--lib/kafs/afssys.c59
-rw-r--r--lib/kafs/common.c16
-rw-r--r--lib/kafs/kafs.h3
-rw-r--r--lib/kafs/kafs_locl.h5
-rw-r--r--lib/kafs/rxkad_kdf.c216
-rw-r--r--lib/kdfs/Makefile.in394
-rw-r--r--lib/kdfs/k5dfspag.c2
-rw-r--r--lib/krb5/Makefile.am83
-rw-r--r--lib/krb5/Makefile.in3009
-rw-r--r--lib/krb5/NTMakefile113
-rw-r--r--lib/krb5/acache.c21
-rw-r--r--lib/krb5/acl.c4
-rw-r--r--lib/krb5/addr_families.c40
-rw-r--r--lib/krb5/aes-test.c220
-rw-r--r--lib/krb5/an2ln_plugin.h91
-rw-r--r--lib/krb5/aname_to_localname.c410
-rw-r--r--lib/krb5/asn1_glue.c6
-rw-r--r--lib/krb5/auth_context.c88
-rw-r--r--lib/krb5/build_auth.c13
-rw-r--r--lib/krb5/cache.c309
-rw-r--r--lib/krb5/changepw.c8
-rw-r--r--lib/krb5/config_file.c37
-rw-r--r--lib/krb5/config_reg.c649
-rw-r--r--lib/krb5/constants.c17
-rw-r--r--lib/krb5/context.c206
-rw-r--r--lib/krb5/copy_host_realm.c11
-rw-r--r--lib/krb5/crc.c4
-rw-r--r--lib/krb5/creds.c15
-rw-r--r--lib/krb5/crypto-aes-sha1.c (renamed from lib/krb5/crypto-aes.c)34
-rw-r--r--lib/krb5/crypto-aes-sha2.c194
-rw-r--r--lib/krb5/crypto-algs.c4
-rw-r--r--lib/krb5/crypto-arcfour.c51
-rw-r--r--lib/krb5/crypto-des-common.c21
-rw-r--r--lib/krb5/crypto-des.c8
-rw-r--r--lib/krb5/crypto-des3.c65
-rw-r--r--lib/krb5/crypto-evp.c6
-rw-r--r--lib/krb5/crypto-null.c6
-rw-r--r--lib/krb5/crypto-pk.c22
-rw-r--r--lib/krb5/crypto-rand.c85
-rw-r--r--lib/krb5/crypto-stubs.c21
-rw-r--r--lib/krb5/crypto.c1011
-rw-r--r--lib/krb5/crypto.h42
-rw-r--r--lib/krb5/data.c9
-rw-r--r--lib/krb5/db_plugin.c31
-rw-r--r--lib/krb5/db_plugin.h68
-rw-r--r--lib/krb5/dcache.c699
-rw-r--r--lib/krb5/deprecated.c177
-rw-r--r--lib/krb5/derived-key-test.c34
-rw-r--r--lib/krb5/digest.c186
-rw-r--r--lib/krb5/dll.c76
-rw-r--r--lib/krb5/doxygen.c4
-rw-r--r--lib/krb5/enomem.c (renamed from lib/hcrypto/dllmain.c)21
-rw-r--r--lib/krb5/error_string.c68
-rw-r--r--lib/krb5/expand_hostname.c16
-rw-r--r--lib/krb5/expand_path.c192
-rw-r--r--lib/krb5/fast.c95
-rw-r--r--lib/krb5/fcache.c456
-rw-r--r--lib/krb5/generate_subkey.c8
-rw-r--r--lib/krb5/get_addrs.c12
-rw-r--r--lib/krb5/get_cred.c684
-rw-r--r--lib/krb5/get_default_principal.c4
-rw-r--r--lib/krb5/get_default_realm.c7
-rw-r--r--lib/krb5/get_for_creds.c21
-rw-r--r--lib/krb5/get_host_realm.c156
-rw-r--r--lib/krb5/get_in_tkt.c32
-rw-r--r--lib/krb5/heim_err.et6
-rw-r--r--lib/krb5/init_creds.c26
-rw-r--r--lib/krb5/init_creds_pw.c875
-rw-r--r--lib/krb5/kcm.c21
-rw-r--r--lib/krb5/kerberos.811
-rw-r--r--lib/krb5/kerberos.cat87
-rw-r--r--lib/krb5/keyblock.c8
-rw-r--r--lib/krb5/keytab.c168
-rw-r--r--lib/krb5/keytab_any.c15
-rw-r--r--lib/krb5/keytab_file.c53
-rw-r--r--lib/krb5/keytab_keyfile.c36
-rw-r--r--lib/krb5/keytab_memory.c19
-rw-r--r--lib/krb5/krb5-plugin.7245
-rw-r--r--lib/krb5/krb5-plugin.cat7168
-rw-r--r--lib/krb5/krb5-private.h262
-rw-r--r--lib/krb5/krb5-protos.h4824
-rw-r--r--lib/krb5/krb5.conf.5367
-rw-r--r--lib/krb5/krb5.conf.cat5406
-rw-r--r--lib/krb5/krb5.h70
-rw-r--r--lib/krb5/krb5_auth_context.32
-rw-r--r--lib/krb5/krb5_auth_context.cat36
-rw-r--r--lib/krb5/krb5_c_make_checksum.32
-rw-r--r--lib/krb5/krb5_c_make_checksum.cat32
-rw-r--r--lib/krb5/krb5_digest.32
-rw-r--r--lib/krb5/krb5_digest.cat32
-rw-r--r--lib/krb5/krb5_err.et7
-rw-r--r--lib/krb5/krb5_get_creds.32
-rw-r--r--lib/krb5/krb5_get_creds.cat33
-rw-r--r--lib/krb5/krb5_get_init_creds.37
-rw-r--r--lib/krb5/krb5_get_init_creds.cat36
-rw-r--r--lib/krb5/krb5_locl.h115
-rw-r--r--lib/krb5/krb5_parse_name.31
-rw-r--r--lib/krb5/krb5_parse_name.cat34
-rw-r--r--lib/krb5/krb5_principal.31
-rw-r--r--lib/krb5/krb5_principal.cat32
-rw-r--r--lib/krb5/krb5_set_default_realm.32
-rw-r--r--lib/krb5/krb5_set_default_realm.cat32
-rw-r--r--lib/krb5/krb5_timeofday.32
-rw-r--r--lib/krb5/krb5_timeofday.cat32
-rw-r--r--lib/krb5/krbhst.c380
-rw-r--r--lib/krb5/kuserok.c710
-rw-r--r--lib/krb5/kuserok_plugin.h91
-rw-r--r--lib/krb5/libkrb5-exports.def.in804
-rw-r--r--lib/krb5/locate_plugin.h19
-rw-r--r--lib/krb5/log.c98
-rw-r--r--lib/krb5/mcache.c195
-rw-r--r--lib/krb5/misc.c47
-rw-r--r--lib/krb5/mit_glue.c7
-rw-r--r--lib/krb5/mk_error.c49
-rw-r--r--lib/krb5/mk_priv.c4
-rw-r--r--lib/krb5/mk_rep.c7
-rw-r--r--lib/krb5/mk_req_ext.c2
-rw-r--r--lib/krb5/n-fold-test.c2
-rw-r--r--lib/krb5/n-fold.c145
-rw-r--r--lib/krb5/net_read.c9
-rw-r--r--lib/krb5/pac.c104
-rw-r--r--lib/krb5/padata.c7
-rw-r--r--lib/krb5/pcache.c46
-rw-r--r--lib/krb5/pkinit-ec.c241
-rw-r--r--lib/krb5/pkinit.c283
-rw-r--r--lib/krb5/plugin.c401
-rw-r--r--lib/krb5/principal.c1244
-rw-r--r--lib/krb5/pseudo-random-test.c112
-rw-r--r--lib/krb5/rd_cred.c12
-rw-r--r--lib/krb5/rd_priv.c2
-rw-r--r--lib/krb5/rd_rep.c28
-rw-r--r--lib/krb5/rd_req.c140
-rw-r--r--lib/krb5/rd_safe.c5
-rw-r--r--lib/krb5/recvauth.c60
-rw-r--r--lib/krb5/replay.c20
-rw-r--r--lib/krb5/salt-aes-sha1.c (renamed from lib/krb5/salt-aes.c)37
-rw-r--r--lib/krb5/salt-aes-sha2.c135
-rw-r--r--lib/krb5/salt-arcfour.c15
-rw-r--r--lib/krb5/salt-des.c8
-rw-r--r--lib/krb5/salt-des3.c20
-rw-r--r--lib/krb5/salt.c20
-rw-r--r--lib/krb5/scache.c68
-rw-r--r--lib/krb5/send_to_kdc.c1565
-rw-r--r--lib/krb5/send_to_kdc_plugin.h13
-rw-r--r--lib/krb5/sendauth.c23
-rw-r--r--lib/krb5/set_default_realm.c11
-rwxr-xr-xlib/krb5/sp800-108-kdf.c97
-rw-r--r--lib/krb5/store-int.c13
-rw-r--r--lib/krb5/store-int.h1
-rw-r--r--lib/krb5/store.c219
-rw-r--r--lib/krb5/store_emem.c2
-rw-r--r--lib/krb5/store_fd.c78
-rw-r--r--lib/krb5/store_mem.c3
-rw-r--r--lib/krb5/store_sock.c159
-rw-r--r--lib/krb5/string-to-key-test.c2
-rw-r--r--lib/krb5/test_alname.c103
-rwxr-xr-xlib/krb5/test_canon.c177
-rw-r--r--lib/krb5/test_cc.c95
-rw-r--r--lib/krb5/test_crypto.c6
-rw-r--r--lib/krb5/test_crypto_wrapping.c4
-rw-r--r--lib/krb5/test_expand_toks.c104
-rw-r--r--lib/krb5/test_fx.c55
-rw-r--r--lib/krb5/test_kuserok.c7
-rw-r--r--lib/krb5/test_pknistkdf.c10
-rw-r--r--lib/krb5/test_plugin.c7
-rw-r--r--lib/krb5/test_rfc3961.c4
-rw-r--r--lib/krb5/test_set_kvno0.c179
-rw-r--r--lib/krb5/test_store.c28
-rw-r--r--lib/krb5/test_time.c6
-rw-r--r--lib/krb5/ticket.c182
-rw-r--r--lib/krb5/transited.c246
-rw-r--r--lib/krb5/verify_init.c21
-rw-r--r--lib/krb5/verify_krb5_conf.c445
-rw-r--r--lib/krb5/verify_user.c7
-rw-r--r--lib/krb5/version-script.map66
-rw-r--r--lib/krb5/warn.c45
-rw-r--r--lib/libedit/Makefile.in421
-rw-r--r--lib/libedit/acinclude.m419
-rw-r--r--lib/libedit/aclocal.m43658
-rwxr-xr-xlib/libedit/compile348
-rwxr-xr-xlib/libedit/config.guess714
-rw-r--r--lib/libedit/config.h.in79
-rwxr-xr-xlib/libedit/config.sub361
-rwxr-xr-xlib/libedit/configure3909
-rw-r--r--lib/libedit/configure.ac33
-rwxr-xr-xlib/libedit/depcomp533
-rwxr-xr-xlib/libedit/install-sh387
-rwxr-xr-xlib/libedit/ltmain.sh5481
-rwxr-xr-xlib/libedit/missing463
-rw-r--r--lib/libedit/src/Makefile.am49
-rw-r--r--lib/libedit/src/Makefile.in342
-rw-r--r--lib/libedit/src/chared.c295
-rw-r--r--lib/libedit/src/chared.h96
-rw-r--r--lib/libedit/src/chartype.c270
-rw-r--r--lib/libedit/src/chartype.h169
-rw-r--r--lib/libedit/src/common.c408
-rw-r--r--lib/libedit/src/el.c266
-rw-r--r--lib/libedit/src/el.h50
-rw-r--r--lib/libedit/src/eln.c143
-rw-r--r--lib/libedit/src/emacs.c196
-rw-r--r--lib/libedit/src/fgetln.c107
-rw-r--r--lib/libedit/src/filecomplete.c192
-rw-r--r--lib/libedit/src/filecomplete.h4
-rw-r--r--lib/libedit/src/hist.c106
-rw-r--r--lib/libedit/src/hist.h38
-rw-r--r--lib/libedit/src/histedit.h26
-rw-r--r--lib/libedit/src/history.c438
-rw-r--r--lib/libedit/src/historyn.c3
-rw-r--r--lib/libedit/src/keymacro.c (renamed from lib/libedit/src/key.c)384
-rw-r--r--lib/libedit/src/keymacro.h (renamed from lib/libedit/src/key.h)60
-rw-r--r--lib/libedit/src/makelist112
-rw-r--r--lib/libedit/src/map.c320
-rw-r--r--lib/libedit/src/map.h28
-rw-r--r--lib/libedit/src/parse.c164
-rw-r--r--lib/libedit/src/parse.h10
-rw-r--r--lib/libedit/src/prompt.c38
-rw-r--r--lib/libedit/src/prompt.h18
-rw-r--r--lib/libedit/src/read.c568
-rw-r--r--lib/libedit/src/read.h19
-rw-r--r--lib/libedit/src/readline.c652
-rw-r--r--lib/libedit/src/readline/readline.h (renamed from lib/libedit/src/editline/readline.h)36
-rw-r--r--lib/libedit/src/refresh.c312
-rw-r--r--lib/libedit/src/refresh.h18
-rw-r--r--lib/libedit/src/search.c176
-rw-r--r--lib/libedit/src/search.h28
-rw-r--r--lib/libedit/src/shlib_version4
-rw-r--r--lib/libedit/src/sig.c33
-rw-r--r--lib/libedit/src/sig.h12
-rw-r--r--lib/libedit/src/strlcat.c74
-rw-r--r--lib/libedit/src/strlcpy.c70
-rw-r--r--lib/libedit/src/sys.h101
-rw-r--r--lib/libedit/src/terminal.c (renamed from lib/libedit/src/term.c)1003
-rw-r--r--lib/libedit/src/terminal.h (renamed from lib/libedit/src/el_term.h)87
-rw-r--r--lib/libedit/src/tokenizer.c107
-rw-r--r--lib/libedit/src/tokenizern.c3
-rw-r--r--lib/libedit/src/tty.c469
-rw-r--r--lib/libedit/src/tty.h28
-rw-r--r--lib/libedit/src/unvis.c97
-rw-r--r--lib/libedit/src/vi.c453
-rw-r--r--lib/libedit/src/vis.c763
-rw-r--r--lib/libedit/src/vis.h59
-rw-r--r--lib/ntlm/Makefile.am7
-rw-r--r--lib/ntlm/Makefile.in868
-rw-r--r--lib/ntlm/NTMakefile2
-rw-r--r--lib/ntlm/heimntlm-protos.h267
-rw-r--r--lib/ntlm/heimntlm.h13
-rw-r--r--lib/ntlm/ntlm.c508
-rw-r--r--lib/ntlm/ntlm_err.et38
-rw-r--r--lib/ntlm/test_ntlm.c261
-rw-r--r--lib/otp/Makefile.am15
-rw-r--r--lib/otp/Makefile.in442
-rw-r--r--lib/otp/otptest.c2
-rw-r--r--lib/roken/Makefile.am34
-rw-r--r--lib/roken/Makefile.in1822
-rw-r--r--lib/roken/NTMakefile41
-rw-r--r--lib/roken/base64-test.c10
-rw-r--r--lib/roken/base64.c4
-rw-r--r--lib/roken/base64.h4
-rw-r--r--lib/roken/bswap.c17
-rw-r--r--lib/roken/cloexec.c9
-rw-r--r--lib/roken/ct.c22
-rw-r--r--lib/roken/daemon.c2
-rw-r--r--lib/roken/detach.c213
-rw-r--r--lib/roken/dirent-test.c278
-rw-r--r--lib/roken/dirent.c235
-rw-r--r--lib/roken/dirent.hin66
-rw-r--r--lib/roken/dlfcn.hin92
-rw-r--r--lib/roken/dlfcn_w32.c185
-rw-r--r--lib/roken/dumpdata.c12
-rw-r--r--lib/roken/err.hin16
-rw-r--r--lib/roken/getarg.32
-rw-r--r--lib/roken/getarg.c2
-rw-r--r--lib/roken/getarg.cat32
-rw-r--r--lib/roken/getcap.c193
-rw-r--r--lib/roken/getifaddrs_w32.c161
-rw-r--r--lib/roken/getxxyyy.c137
-rw-r--r--lib/roken/glob.c16
-rw-r--r--lib/roken/hex-test.c2
-rw-r--r--lib/roken/issuid.c167
-rw-r--r--lib/roken/memset_s.c (renamed from lib/heimdal/dllmain.c)25
-rw-r--r--lib/roken/mkdir.c56
-rw-r--r--lib/roken/ndbm_wrap.c12
-rw-r--r--lib/roken/parse_bytes-test.c3
-rw-r--r--lib/roken/parse_time-test.c3
-rw-r--r--lib/roken/parse_time.314
-rw-r--r--lib/roken/parse_time.cat316
-rw-r--r--lib/roken/rand.c31
-rw-r--r--lib/roken/realloc.c1
-rw-r--r--lib/roken/rename.c49
-rw-r--r--lib/roken/resolve-test.c186
-rw-r--r--lib/roken/resolve.c57
-rw-r--r--lib/roken/rkpty.c10
-rw-r--r--lib/roken/roken-common.h32
-rw-r--r--lib/roken/roken.awk3
-rw-r--r--lib/roken/roken.h.in204
-rw-r--r--lib/roken/roken_gethostby.c3
-rw-r--r--lib/roken/rtbl.c103
-rw-r--r--lib/roken/rtbl.h8
-rw-r--r--lib/roken/simple_exec.c26
-rw-r--r--lib/roken/simple_exec_w32.c431
-rw-r--r--lib/roken/sleep.c55
-rw-r--r--lib/roken/snprintf-test.c10
-rw-r--r--lib/roken/snprintf.c8
-rw-r--r--lib/roken/socket.c22
-rw-r--r--lib/roken/sockstartup_w32.c75
-rw-r--r--lib/roken/stdbool.hin13
-rw-r--r--lib/roken/stdint.hin15
-rw-r--r--lib/roken/strtoll.c149
-rw-r--r--lib/roken/strtoull.c127
-rw-r--r--lib/roken/syslog.hin248
-rw-r--r--lib/roken/syslogc.c339
-rw-r--r--lib/roken/test-detach.c82
-rw-r--r--lib/roken/test-mem.c9
-rw-r--r--lib/roken/test-mini_inetd.c369
-rw-r--r--lib/roken/tsearch.c7
-rw-r--r--lib/roken/version-script.map19
-rw-r--r--lib/roken/win32_alloc.c76
-rw-r--r--lib/roken/write_pid.c66
-rw-r--r--lib/sl/Makefile.am6
-rw-r--r--lib/sl/Makefile.in906
-rw-r--r--lib/sl/NTMakefile2
-rw-r--r--lib/sl/sl.c108
-rw-r--r--lib/sl/sl.h2
-rw-r--r--lib/sl/slc-gram.c18
-rw-r--r--lib/sl/slc-gram.y18
-rw-r--r--lib/sl/slc-lex.c9
-rw-r--r--lib/sqlite/Makefile.am4
-rw-r--r--lib/sqlite/Makefile.in396
-rw-r--r--lib/sqlite/README25
-rw-r--r--lib/sqlite/sqlite3.c132346
-rw-r--r--lib/sqlite/sqlite3.h5070
-rw-r--r--lib/sqlite/sqlite3ext.h182
-rw-r--r--lib/vers/Makefile.am2
-rw-r--r--lib/vers/Makefile.in377
-rw-r--r--lib/vers/make-print-version.c66
-rw-r--r--lib/vers/print_version.c5
-rw-r--r--lib/wind/Makefile.in962
-rw-r--r--lib/wind/NTMakefile16
-rw-r--r--lib/wind/bidi.c4
-rw-r--r--lib/wind/bidi_table.c4
-rw-r--r--lib/wind/bidi_table.h2
-rw-r--r--lib/wind/combining.c2
-rw-r--r--lib/wind/combining_table.c4
-rw-r--r--lib/wind/combining_table.h2
-rw-r--r--lib/wind/errorlist.c2
-rw-r--r--lib/wind/errorlist_table.c4
-rw-r--r--lib/wind/errorlist_table.h2
-rw-r--r--lib/wind/gen-bidi.py2
-rw-r--r--lib/wind/gen-combining.py2
-rw-r--r--lib/wind/gen-errorlist.py2
-rw-r--r--lib/wind/gen-normalize.py2
-rw-r--r--lib/wind/libwind-exports.def1
-rw-r--r--lib/wind/map.c2
-rw-r--r--lib/wind/map_table.c2
-rw-r--r--lib/wind/map_table.h2
-rw-r--r--lib/wind/normalize.c2
-rw-r--r--lib/wind/normalize_table.c4
-rw-r--r--lib/wind/normalize_table.h2
-rw-r--r--lib/wind/punycode_examples.c2
-rw-r--r--lib/wind/punycode_examples.h2
-rw-r--r--lib/wind/test-normalize.c2
-rw-r--r--lib/wind/test-utf8.c36
840 files changed, 195499 insertions, 62569 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index bed19304addf..5f34eee6bb74 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -14,15 +14,19 @@ endif
if COM_ERR
dir_com_err = com_err
endif
-if !HAVE_OPENSSL
-dir_hcrypto = hcrypto
-endif
if !SQLITE3
dir_sqlite = sqlite
endif
+if MAINTAINER_MODE
+dir_sqlite = sqlite
+endif
+if !NO_AFS
+dir_afs = kafs
+endif
SUBDIRS = \
roken \
+ base \
vers \
$(dir_editline) \
$(dir_com_err) \
@@ -30,16 +34,16 @@ SUBDIRS = \
wind \
asn1 \
$(dir_sqlite) \
- $(dir_hcrypto) \
+ hcrypto \
ipc \
hx509 \
krb5 \
ntlm \
- kafs \
+ $(dir_afs) \
gssapi \
hdb \
kadm5 \
$(dir_otp) \
$(dir_dce)
-EXTRA_DIST = NTMakefile heimdal \ No newline at end of file
+EXTRA_DIST = NTMakefile heimdal
diff --git a/lib/Makefile.in b/lib/Makefile.in
index d1d771a4011b..c15b3a0955c1 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -21,6 +20,61 @@
# $Id$
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -39,9 +93,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
- $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common
subdir = lib
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -57,8 +108,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -71,6 +121,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -90,28 +141,70 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
SOURCES =
DIST_SOURCES =
-RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
- html-recursive info-recursive install-data-recursive \
- install-dvi-recursive install-exec-recursive \
- install-html-recursive install-info-recursive \
- install-pdf-recursive install-ps-recursive install-recursive \
- installcheck-recursive installdirs-recursive pdf-recursive \
- ps-recursive uninstall-recursive
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
-AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
- $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-DIST_SUBDIRS = roken vers libedit com_err sl wind asn1 sqlite hcrypto \
- ipc hx509 krb5 ntlm kafs gssapi hdb kadm5 otp kdfs
+DIST_SUBDIRS = roken base vers libedit com_err sl wind asn1 sqlite \
+ hcrypto ipc hx509 krb5 ntlm kafs gssapi hdb kadm5 otp kdfs
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
am__relativize = \
dir0=`pwd`; \
@@ -141,7 +234,9 @@ am__relativize = \
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -160,12 +255,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -175,17 +270,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -204,12 +299,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -218,6 +310,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -234,10 +327,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -245,6 +336,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -259,12 +351,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -281,6 +376,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -295,13 +391,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -325,6 +415,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -360,37 +452,47 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
@LIBEDIT_TRUE@dir_editline = libedit
@OTP_TRUE@dir_otp = otp
@DCE_TRUE@dir_dce = kdfs
@COM_ERR_TRUE@dir_com_err = com_err
-@HAVE_OPENSSL_FALSE@dir_hcrypto = hcrypto
+@MAINTAINER_MODE_TRUE@dir_sqlite = sqlite
@SQLITE3_FALSE@dir_sqlite = sqlite
+@NO_AFS_FALSE@dir_afs = kafs
SUBDIRS = \
roken \
+ base \
vers \
$(dir_editline) \
$(dir_com_err) \
@@ -398,12 +500,12 @@ SUBDIRS = \
wind \
asn1 \
$(dir_sqlite) \
- $(dir_hcrypto) \
+ hcrypto \
ipc \
hx509 \
krb5 \
ntlm \
- kafs \
+ $(dir_afs) \
gssapi \
hdb \
kadm5 \
@@ -414,7 +516,7 @@ EXTRA_DIST = NTMakefile heimdal
all: all-recursive
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -427,7 +529,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -436,6 +537,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -453,22 +555,25 @@ clean-libtool:
-rm -rf .libs _libs
# This directory's subdirectories are mostly independent; you can cd
-# into them and run `make' without going through this Makefile.
-# To change the values of `make' variables: instead of editing Makefiles,
-# (1) if the variable is set in `config.status', edit `config.status'
-# (which will cause the Makefiles to be regenerated when you run `make');
-# (2) otherwise, pass the desired values on the `make' command line.
-$(RECURSIVE_TARGETS):
- @fail= failcom='exit 1'; \
- for f in x $$MAKEFLAGS; do \
- case $$f in \
- *=* | --[!k]*);; \
- *k*) failcom='fail=yes';; \
- esac; \
- done; \
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
- list='$(SUBDIRS)'; for subdir in $$list; do \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
@@ -483,57 +588,12 @@ $(RECURSIVE_TARGETS):
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
-$(RECURSIVE_CLEAN_TARGETS):
- @fail= failcom='exit 1'; \
- for f in x $$MAKEFLAGS; do \
- case $$f in \
- *=* | --[!k]*);; \
- *k*) failcom='fail=yes';; \
- esac; \
- done; \
- dot_seen=no; \
- case "$@" in \
- distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
- *) list='$(SUBDIRS)' ;; \
- esac; \
- rev=''; for subdir in $$list; do \
- if test "$$subdir" = "."; then :; else \
- rev="$$subdir $$rev"; \
- fi; \
- done; \
- rev="$$rev ."; \
- target=`echo $@ | sed s/-recursive//`; \
- for subdir in $$rev; do \
- echo "Making $$target in $$subdir"; \
- if test "$$subdir" = "."; then \
- local_target="$$target-am"; \
- else \
- local_target="$$target"; \
- fi; \
- ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
- || eval $$failcom; \
- done && test -z "$$fail"
-tags-recursive:
- list='$(SUBDIRS)'; for subdir in $$list; do \
- test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
- done
-ctags-recursive:
- list='$(SUBDIRS)'; for subdir in $$list; do \
- test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
- done
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
@@ -549,12 +609,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -566,15 +621,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -583,6 +634,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -619,13 +685,10 @@ distdir: $(DISTFILES)
done
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
- test -d "$(distdir)/$$subdir" \
- || $(MKDIR_P) "$(distdir)/$$subdir" \
- || exit 1; \
- fi; \
- done
- @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
- if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
$(am__relativize); \
new_distdir=$$reldir; \
@@ -664,10 +727,15 @@ install-am: all-am
installcheck: installcheck-recursive
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -706,9 +774,8 @@ install-dvi: install-dvi-recursive
install-dvi-am:
-install-exec-am:
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local
+
install-html: install-html-recursive
install-html-am:
@@ -748,40 +815,53 @@ ps-am:
uninstall-am:
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) check-am \
- ctags-recursive install-am install-data-am install-exec-am \
- install-strip tags-recursive uninstall-am
-
-.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
- all all-am all-local check check-am check-local clean \
- clean-generic clean-libtool ctags ctags-recursive dist-hook \
- distclean distclean-generic distclean-libtool distclean-tags \
- distdir dvi dvi-am html html-am info info-am install \
- install-am install-data install-data-am install-data-hook \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-exec-hook install-html install-html-am install-info \
- install-info-am install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs installdirs-am maintainer-clean \
+.MAKE: $(am__recursive_targets) check-am install-am install-data-am \
+ install-strip uninstall-am
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am all-local \
+ check check-am check-local clean clean-generic clean-libtool \
+ cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-info install-info-am \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs installdirs-am maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \
- uninstall uninstall-am uninstall-hook
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-hook
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
-install-exec-hook: install-suid-programs
+install-exec-local: install-suid-programs
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -789,7 +869,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -846,6 +926,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -888,6 +970,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -901,13 +996,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/NTMakefile b/lib/NTMakefile
index 62d9754ab3c8..d5150b0b613d 100644
--- a/lib/NTMakefile
+++ b/lib/NTMakefile
@@ -1,6 +1,6 @@
########################################################################
#
-# Copyright (c) 2009,2011, Secure Endpoints Inc.
+# Copyright (c) 2009-2016, Secure Endpoints Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -30,22 +30,29 @@
#
-!ifdef KRB4
-dir_45 = 45
-!endif
!ifdef OTP
dir_otp = otp
!endif
!ifdef DCE
dir_dce = kdfs
!endif
-!ifndef HAVE_OPENSSL
-dir_hcrypto = hcrypto
+
+# Third party plugins must be added to the assembly
+# Permit third party assembly scripts
+
+!if exist(..\thirdparty\plugin\NTMakefile)
+plugin=..\thirdparty\plugin
+!endif
+
+!if exist(..\thirdparty\assembly\NTMakefile)
+assembly=..\thirdparty\assembly
+!else
+assembly=..\packages\windows\assembly
!endif
-SUBDIRS = vers com_err sl wind asn1 sqlite \
- $(dir_hcrypto) hx509 krb5 heimdal ntlm kafs gssapi hdb \
- kadm5 $(dir_45) $(dir_otp) $(dir_dce) ..\packages\windows\assembly
+SUBDIRS = roken base vers com_err sl wind asn1 sqlite \
+ hcrypto hx509 krb5 heimdal ntlm kafs gssapi hdb \
+ kadm5 $(dir_otp) $(dir_dce) $(plugin) $(assembly)
!include ../windows/NTMakefile.w32
diff --git a/lib/asn1/Makefile.am b/lib/asn1/Makefile.am
index 47158b88aa42..7c513d5bc120 100644
--- a/lib/asn1/Makefile.am
+++ b/lib/asn1/Makefile.am
@@ -4,6 +4,8 @@ include $(top_srcdir)/Makefile.am.common
YFLAGS = -d -t
+AM_CPPFLAGS += $(ROKEN_RENAME)
+
lib_LTLIBRARIES = libasn1.la
libasn1_la_LDFLAGS = -version-info 8:0:0
@@ -37,6 +39,7 @@ gen_files_pkinit = asn1_pkinit_asn1.x
gen_files_pkcs12 = asn1_pkcs12_asn1.x
gen_files_pkcs8 = asn1_pkcs8_asn1.x
gen_files_pkcs9 = asn1_pkcs9_asn1.x
+gen_files_test_template = test_template_asn1-template.x
gen_files_test = asn1_test_asn1.x
gen_files_digest = asn1_digest_asn1.x
gen_files_kx509 = asn1_kx509_asn1.x
@@ -53,7 +56,7 @@ asn1_print_SOURCES = asn1_print.c
check_der_SOURCES = check-der.c check-common.c check-common.h
check_template_SOURCES = check-template.c check-common.c check-common.h
-nodist_check_template_SOURCES = $(gen_files_test:.x=.c)
+nodist_check_template_SOURCES = $(gen_files_test_template)
dist_check_gen_SOURCES = check-gen.c check-common.c check-common.h
nodist_check_gen_SOURCES = $(gen_files_test:.x=.c)
@@ -94,8 +97,10 @@ dist_libasn1base_la_SOURCES = \
der_copy.c \
der_cmp.c \
der_format.c \
+ fuzzer.c \
heim_asn1.h \
extra.c \
+ roken_rename.h \
template.c \
timegm.c
@@ -134,7 +139,9 @@ CLEANFILES = \
$(gen_files_pkcs12) \
$(gen_files_digest) \
$(gen_files_kx509) \
- $(gen_files_test) $(nodist_check_gen_SOURCES) \
+ $(gen_files_test) \
+ $(gen_files_test_template) \
+ $(nodist_check_gen_SOURCES) \
asn1_err.c asn1_err.h \
rfc2459_asn1_files rfc2459_asn1*.h* \
cms_asn1_files cms_asn1*.h* \
@@ -145,9 +152,12 @@ CLEANFILES = \
pkcs12_asn1_files pkcs12_asn1*.h* \
digest_asn1_files digest_asn1*.h* \
kx509_asn1_files kx509_asn1*.h* \
- test_asn1_files test_asn1*.h*
+ test_asn1_files test_asn1*.h* \
+ test_template_asn1* \
+ asn1_*.x
-dist_include_HEADERS = der.h heim_asn1.h der-protos.h der-private.h
+dist_include_HEADERS = der.h heim_asn1.h
+dist_include_HEADERS += $(srcdir)/der-protos.h $(srcdir)/der-private.h
dist_include_HEADERS += asn1-common.h
nodist_include_HEADERS = asn1_err.h
@@ -170,6 +180,7 @@ priv_headers += pkcs9_asn1-priv.h
priv_headers += pkcs12_asn1-priv.h
priv_headers += digest_asn1-priv.h
priv_headers += kx509_asn1-priv.h
+priv_headers += test_template_asn1.h test_template_asn1-priv.h
priv_headers += test_asn1.h test_asn1-priv.h
@@ -193,6 +204,7 @@ $(gen_files_kx509) kx509_asn1.hx kx509_asn1-priv.hx: kx509_asn1_files
$(gen_files_rfc2459) rfc2459_asn1.hx rfc2459_asn1-priv.hx: rfc2459_asn1_files
$(gen_files_cms) cms_asn1.hx cms_asn1-priv.hx: cms_asn1_files
$(gen_files_test) test_asn1.hx test_asn1-priv.hx: test_asn1_files
+$(gen_files_test_template) test_template_asn1.hx test_template_asn1-priv.hx: test_template_asn1_files
rfc2459_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/rfc2459.asn1
$(ASN1_COMPILE) --one-code-file --preserve-binary=TBSCertificate --preserve-binary=TBSCRLCertList --preserve-binary=Name --sequence=GeneralNames --sequence=Extensions --sequence=CRLDistributionPoints $(srcdir)/rfc2459.asn1 rfc2459_asn1 || (rm -f rfc2459_asn1_files ; exit 1)
@@ -221,12 +233,16 @@ digest_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/digest.asn1
kx509_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/kx509.asn1
$(ASN1_COMPILE) --one-code-file $(srcdir)/kx509.asn1 kx509_asn1 || (rm -f kx509_asn1_files ; exit 1)
+test_template_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/test.asn1
+ $(ASN1_COMPILE) --template --sequence=TESTSeqOf $(srcdir)/test.asn1 test_template_asn1 || (rm -f test_template_asn1_files ; exit 1)
+
test_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/test.asn1
$(ASN1_COMPILE) --one-code-file --sequence=TESTSeqOf $(srcdir)/test.asn1 test_asn1 || (rm -f test_asn1_files ; exit 1)
EXTRA_DIST = \
NTMakefile \
+ README.template \
asn1_compile-version.rc \
libasn1-exports.def \
cms.asn1 \
@@ -247,8 +263,19 @@ EXTRA_DIST = \
test.gen \
version-script.map
-$(srcdir)/der-protos.h:
+DER_PROTOS = $(srcdir)/der-protos.h $(srcdir)/der-private.h
+
+ALL_OBJECTS = $(libasn1_la_OBJECTS)
+ALL_OBJECTS += $(libasn1base_la_OBJECTS)
+ALL_OBJECTS += $(asn1_print_OBJECTS)
+ALL_OBJECTS += $(asn1_compile_OBJECTS)
+ALL_OBJECTS += $(asn1_gen_OBJECTS)
+ALL_OBJECTS += $(check_template_OBJECTS)
+
+$(ALL_OBJECTS): $(DER_PROTOS) asn1_err.h
+
+$(srcdir)/der-protos.h: $(dist_libasn1base_la_SOURCES)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -o der-protos.h $(dist_libasn1base_la_SOURCES) || rm -f der-protos.h
-$(srcdir)/der-private.h:
+$(srcdir)/der-private.h: $(dist_libasn1base_la_SOURCES)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p der-private.h $(dist_libasn1base_la_SOURCES) || rm -f der-private.h
diff --git a/lib/asn1/Makefile.in b/lib/asn1/Makefile.in
index ab377b3090f2..6ab1aa514233 100644
--- a/lib/asn1/Makefile.in
+++ b/lib/asn1/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,10 +96,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(dist_include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog asn1parse.c \
- asn1parse.h lex.c
@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
noinst_PROGRAMS = asn1_gen$(EXEEXT)
libexec_heimdal_PROGRAMS = asn1_compile$(EXEEXT) asn1_print$(EXEEXT)
@@ -67,8 +117,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -81,6 +130,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -100,6 +150,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -125,6 +177,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" \
"$(DESTDIR)$(libexec_heimdaldir)" "$(DESTDIR)$(includedir)" \
"$(DESTDIR)$(includedir)"
@@ -145,13 +203,17 @@ am__objects_10 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
$(am__objects_7) $(am__objects_8) $(am__objects_9)
nodist_libasn1_la_OBJECTS = $(am__objects_10)
libasn1_la_OBJECTS = $(nodist_libasn1_la_OBJECTS)
-libasn1_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libasn1_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libasn1_la_LDFLAGS) $(LDFLAGS) -o $@
libasn1base_la_LIBADD =
dist_libasn1base_la_OBJECTS = der.lo der_get.lo der_put.lo der_free.lo \
- der_length.lo der_copy.lo der_cmp.lo der_format.lo extra.lo \
- template.lo timegm.lo
+ der_length.lo der_copy.lo der_cmp.lo der_format.lo fuzzer.lo \
+ extra.lo template.lo timegm.lo
nodist_libasn1base_la_OBJECTS = asn1_err.lo
libasn1base_la_OBJECTS = $(dist_libasn1base_la_OBJECTS) \
$(nodist_libasn1base_la_OBJECTS)
@@ -189,34 +251,66 @@ check_gen_OBJECTS = $(dist_check_gen_OBJECTS) \
check_gen_DEPENDENCIES = libasn1.la $(am__DEPENDENCIES_1)
am_check_template_OBJECTS = check-template.$(OBJEXT) \
check-common.$(OBJEXT)
-nodist_check_template_OBJECTS = $(am__objects_11)
+am__objects_12 = test_template_asn1-template.$(OBJEXT)
+nodist_check_template_OBJECTS = $(am__objects_12)
check_template_OBJECTS = $(am_check_template_OBJECTS) \
$(nodist_check_template_OBJECTS)
check_template_DEPENDENCIES = $(am__DEPENDENCIES_2)
check_timegm_SOURCES = check-timegm.c
check_timegm_OBJECTS = check-timegm.$(OBJEXT)
check_timegm_DEPENDENCIES = $(am__DEPENDENCIES_2)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
-LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
-LTLEXCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo " LEX " $@;
+am__v_LEX_1 =
YLWRAP = $(top_srcdir)/ylwrap
@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
-YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
-LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
+ -e s/c++$$/h++/ -e s/c$$/h/
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo " YACC " $@;
+am__v_YACC_1 =
SOURCES = $(nodist_libasn1_la_SOURCES) $(dist_libasn1base_la_SOURCES) \
$(nodist_libasn1base_la_SOURCES) $(asn1_compile_SOURCES) \
$(asn1_gen_SOURCES) $(asn1_print_SOURCES) check-ber.c \
@@ -227,16 +321,220 @@ DIST_SOURCES = $(dist_libasn1base_la_SOURCES) $(asn1_compile_SOURCES) \
$(asn1_gen_SOURCES) $(asn1_print_SOURCES) check-ber.c \
$(check_der_SOURCES) $(dist_check_gen_SOURCES) \
$(check_template_SOURCES) check-timegm.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(dist_include_HEADERS) $(nodist_include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver $(top_srcdir)/ylwrap ChangeLog \
+ asn1parse.c asn1parse.h lex.c
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -255,12 +553,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -270,17 +568,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -299,12 +597,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -313,6 +608,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -329,10 +625,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -340,6 +634,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -354,12 +649,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -376,6 +674,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -390,13 +689,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = -d -t
abs_builddir = @abs_builddir@
@@ -420,6 +713,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -455,29 +750,37 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
-AM_CPPFLAGS = $(INCLUDES_roken)
+AM_CPPFLAGS = $(INCLUDES_roken) $(ROKEN_RENAME)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
lib_LTLIBRARIES = libasn1.la
libasn1_la_LDFLAGS = -version-info 8:0:0 $(am__append_1)
noinst_LTLIBRARIES = libasn1base.la
@@ -504,6 +807,7 @@ gen_files_pkinit = asn1_pkinit_asn1.x
gen_files_pkcs12 = asn1_pkcs12_asn1.x
gen_files_pkcs8 = asn1_pkcs8_asn1.x
gen_files_pkcs9 = asn1_pkcs9_asn1.x
+gen_files_test_template = test_template_asn1-template.x
gen_files_test = asn1_test_asn1.x
gen_files_digest = asn1_digest_asn1.x
gen_files_kx509 = asn1_kx509_asn1.x
@@ -511,7 +815,7 @@ asn1_gen_SOURCES = asn1_gen.c
asn1_print_SOURCES = asn1_print.c
check_der_SOURCES = check-der.c check-common.c check-common.h
check_template_SOURCES = check-template.c check-common.c check-common.h
-nodist_check_template_SOURCES = $(gen_files_test:.x=.c)
+nodist_check_template_SOURCES = $(gen_files_test_template)
dist_check_gen_SOURCES = check-gen.c check-common.c check-common.h
nodist_check_gen_SOURCES = $(gen_files_test:.x=.c)
build_HEADERZ = asn1-template.h
@@ -549,8 +853,10 @@ dist_libasn1base_la_SOURCES = \
der_copy.c \
der_cmp.c \
der_format.c \
+ fuzzer.c \
heim_asn1.h \
extra.c \
+ roken_rename.h \
template.c \
timegm.c
@@ -586,7 +892,9 @@ CLEANFILES = \
$(gen_files_pkcs12) \
$(gen_files_digest) \
$(gen_files_kx509) \
- $(gen_files_test) $(nodist_check_gen_SOURCES) \
+ $(gen_files_test) \
+ $(gen_files_test_template) \
+ $(nodist_check_gen_SOURCES) \
asn1_err.c asn1_err.h \
rfc2459_asn1_files rfc2459_asn1*.h* \
cms_asn1_files cms_asn1*.h* \
@@ -597,19 +905,23 @@ CLEANFILES = \
pkcs12_asn1_files pkcs12_asn1*.h* \
digest_asn1_files digest_asn1*.h* \
kx509_asn1_files kx509_asn1*.h* \
- test_asn1_files test_asn1*.h*
+ test_asn1_files test_asn1*.h* \
+ test_template_asn1* \
+ asn1_*.x
-dist_include_HEADERS = der.h heim_asn1.h der-protos.h der-private.h \
- asn1-common.h
+dist_include_HEADERS = der.h heim_asn1.h $(srcdir)/der-protos.h \
+ $(srcdir)/der-private.h asn1-common.h
nodist_include_HEADERS = asn1_err.h krb5_asn1.h pkinit_asn1.h \
cms_asn1.h rfc2459_asn1.h pkcs8_asn1.h pkcs9_asn1.h \
pkcs12_asn1.h digest_asn1.h kx509_asn1.h
priv_headers = krb5_asn1-priv.h pkinit_asn1-priv.h cms_asn1-priv.h \
rfc2459_asn1-priv.h pkcs8_asn1-priv.h pkcs9_asn1-priv.h \
pkcs12_asn1-priv.h digest_asn1-priv.h kx509_asn1-priv.h \
- test_asn1.h test_asn1-priv.h
+ test_template_asn1.h test_template_asn1-priv.h test_asn1.h \
+ test_asn1-priv.h
EXTRA_DIST = \
NTMakefile \
+ README.template \
asn1_compile-version.rc \
libasn1-exports.def \
cms.asn1 \
@@ -630,11 +942,15 @@ EXTRA_DIST = \
test.gen \
version-script.map
+DER_PROTOS = $(srcdir)/der-protos.h $(srcdir)/der-private.h
+ALL_OBJECTS = $(libasn1_la_OBJECTS) $(libasn1base_la_OBJECTS) \
+ $(asn1_print_OBJECTS) $(asn1_compile_OBJECTS) \
+ $(asn1_gen_OBJECTS) $(check_template_OBJECTS)
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .l .lo .o .obj .y
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .l .lo .log .o .obj .test .test$(EXEEXT) .trs .y
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -647,7 +963,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/asn1/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/asn1/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -656,6 +971,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -665,9 +981,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -675,6 +991,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -690,25 +1008,31 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
- @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libasn1.la: $(libasn1_la_OBJECTS) $(libasn1_la_DEPENDENCIES)
- $(libasn1_la_LINK) -rpath $(libdir) $(libasn1_la_OBJECTS) $(libasn1_la_LIBADD) $(LIBS)
-libasn1base.la: $(libasn1base_la_OBJECTS) $(libasn1base_la_DEPENDENCIES)
- $(LINK) $(libasn1base_la_OBJECTS) $(libasn1base_la_LIBADD) $(LIBS)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libasn1.la: $(libasn1_la_OBJECTS) $(libasn1_la_DEPENDENCIES) $(EXTRA_libasn1_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libasn1_la_LINK) -rpath $(libdir) $(libasn1_la_OBJECTS) $(libasn1_la_LIBADD) $(LIBS)
+
+libasn1base.la: $(libasn1base_la_OBJECTS) $(libasn1base_la_DEPENDENCIES) $(EXTRA_libasn1base_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libasn1base_la_OBJECTS) $(libasn1base_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -720,14 +1044,19 @@ clean-checkPROGRAMS:
rm -f $$list
install-libexec_heimdalPROGRAMS: $(libexec_heimdal_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(libexec_heimdaldir)" || $(MKDIR_P) "$(DESTDIR)$(libexec_heimdaldir)"
@list='$(libexec_heimdal_PROGRAMS)'; test -n "$(libexec_heimdaldir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexec_heimdaldir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexec_heimdaldir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -748,7 +1077,8 @@ uninstall-libexec_heimdalPROGRAMS:
@list='$(libexec_heimdal_PROGRAMS)'; test -n "$(libexec_heimdaldir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(libexec_heimdaldir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(libexec_heimdaldir)" && rm -f $$files
@@ -770,30 +1100,38 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-asn1_compile$(EXEEXT): $(asn1_compile_OBJECTS) $(asn1_compile_DEPENDENCIES)
+
+asn1_compile$(EXEEXT): $(asn1_compile_OBJECTS) $(asn1_compile_DEPENDENCIES) $(EXTRA_asn1_compile_DEPENDENCIES)
@rm -f asn1_compile$(EXEEXT)
- $(LINK) $(asn1_compile_OBJECTS) $(asn1_compile_LDADD) $(LIBS)
-asn1_gen$(EXEEXT): $(asn1_gen_OBJECTS) $(asn1_gen_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(asn1_compile_OBJECTS) $(asn1_compile_LDADD) $(LIBS)
+
+asn1_gen$(EXEEXT): $(asn1_gen_OBJECTS) $(asn1_gen_DEPENDENCIES) $(EXTRA_asn1_gen_DEPENDENCIES)
@rm -f asn1_gen$(EXEEXT)
- $(LINK) $(asn1_gen_OBJECTS) $(asn1_gen_LDADD) $(LIBS)
-asn1_print$(EXEEXT): $(asn1_print_OBJECTS) $(asn1_print_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(asn1_gen_OBJECTS) $(asn1_gen_LDADD) $(LIBS)
+
+asn1_print$(EXEEXT): $(asn1_print_OBJECTS) $(asn1_print_DEPENDENCIES) $(EXTRA_asn1_print_DEPENDENCIES)
@rm -f asn1_print$(EXEEXT)
- $(LINK) $(asn1_print_OBJECTS) $(asn1_print_LDADD) $(LIBS)
-check-ber$(EXEEXT): $(check_ber_OBJECTS) $(check_ber_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(asn1_print_OBJECTS) $(asn1_print_LDADD) $(LIBS)
+
+check-ber$(EXEEXT): $(check_ber_OBJECTS) $(check_ber_DEPENDENCIES) $(EXTRA_check_ber_DEPENDENCIES)
@rm -f check-ber$(EXEEXT)
- $(LINK) $(check_ber_OBJECTS) $(check_ber_LDADD) $(LIBS)
-check-der$(EXEEXT): $(check_der_OBJECTS) $(check_der_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(check_ber_OBJECTS) $(check_ber_LDADD) $(LIBS)
+
+check-der$(EXEEXT): $(check_der_OBJECTS) $(check_der_DEPENDENCIES) $(EXTRA_check_der_DEPENDENCIES)
@rm -f check-der$(EXEEXT)
- $(LINK) $(check_der_OBJECTS) $(check_der_LDADD) $(LIBS)
-check-gen$(EXEEXT): $(check_gen_OBJECTS) $(check_gen_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(check_der_OBJECTS) $(check_der_LDADD) $(LIBS)
+
+check-gen$(EXEEXT): $(check_gen_OBJECTS) $(check_gen_DEPENDENCIES) $(EXTRA_check_gen_DEPENDENCIES)
@rm -f check-gen$(EXEEXT)
- $(LINK) $(check_gen_OBJECTS) $(check_gen_LDADD) $(LIBS)
-check-template$(EXEEXT): $(check_template_OBJECTS) $(check_template_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(check_gen_OBJECTS) $(check_gen_LDADD) $(LIBS)
+
+check-template$(EXEEXT): $(check_template_OBJECTS) $(check_template_DEPENDENCIES) $(EXTRA_check_template_DEPENDENCIES)
@rm -f check-template$(EXEEXT)
- $(LINK) $(check_template_OBJECTS) $(check_template_LDADD) $(LIBS)
-check-timegm$(EXEEXT): $(check_timegm_OBJECTS) $(check_timegm_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(check_template_OBJECTS) $(check_template_LDADD) $(LIBS)
+
+check-timegm$(EXEEXT): $(check_timegm_OBJECTS) $(check_timegm_DEPENDENCIES) $(EXTRA_check_timegm_DEPENDENCIES)
@rm -f check-timegm$(EXEEXT)
- $(LINK) $(check_timegm_OBJECTS) $(check_timegm_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(check_timegm_OBJECTS) $(check_timegm_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -830,6 +1168,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/der_length.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/der_put.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/extra.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzzer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen_copy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gen_decode.Po@am__quote@
@@ -844,34 +1183,35 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/symbol.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/template.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_template_asn1-template.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timegm.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
.l.c:
- $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
.y.c:
- $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
mostlyclean-libtool:
-rm -f *.lo
@@ -880,8 +1220,11 @@ clean-libtool:
-rm -rf .libs _libs
install-dist_includeHEADERS: $(dist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -895,13 +1238,14 @@ uninstall-dist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_includeHEADERS: $(nodist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -915,30 +1259,17 @@ uninstall-nodist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -950,15 +1281,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -967,101 +1294,215 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+check-der.log: check-der$(EXEEXT)
+ @p='check-der$(EXEEXT)'; \
+ b='check-der'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+check-gen.log: check-gen$(EXEEXT)
+ @p='check-gen$(EXEEXT)'; \
+ b='check-gen'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+check-timegm.log: check-timegm$(EXEEXT)
+ @p='check-timegm$(EXEEXT)'; \
+ b='check-timegm'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+check-ber.log: check-ber$(EXEEXT)
+ @p='check-ber$(EXEEXT)'; \
+ b='check-ber'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+check-template.log: check-template$(EXEEXT)
+ @p='check-template$(EXEEXT)'; \
+ b='check-template'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -1117,11 +1558,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -1169,10 +1618,9 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES \
+install-exec-am: install-exec-local install-libLTLIBRARIES \
install-libexec_heimdalPROGRAMS
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+
install-html: install-html-am
install-html-am:
@@ -1217,45 +1665,59 @@ uninstall-am: uninstall-dist_includeHEADERS uninstall-libLTLIBRARIES \
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
.MAKE: all check check-am install install-am install-data-am \
- install-exec-am install-strip uninstall-am
+ install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-checkPROGRAMS clean-generic \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-checkPROGRAMS clean-generic \
clean-libLTLIBRARIES clean-libexec_heimdalPROGRAMS \
clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
- ctags dist-hook distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-data \
- install-data-am install-data-hook install-dist_includeHEADERS \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-exec-hook install-html install-html-am install-info \
- install-info-am install-libLTLIBRARIES \
- install-libexec_heimdalPROGRAMS install-man \
- install-nodist_includeHEADERS install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
+ cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-hook install-dist_includeHEADERS install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-info install-info-am \
+ install-libLTLIBRARIES install-libexec_heimdalPROGRAMS \
+ install-man install-nodist_includeHEADERS install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-dist_includeHEADERS \
- uninstall-hook uninstall-libLTLIBRARIES \
- uninstall-libexec_heimdalPROGRAMS \
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-dist_includeHEADERS uninstall-hook \
+ uninstall-libLTLIBRARIES uninstall-libexec_heimdalPROGRAMS \
uninstall-nodist_includeHEADERS
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-exec-hook: install-suid-programs
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -1263,7 +1725,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -1320,6 +1782,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1362,6 +1826,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1375,13 +1852,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -1431,6 +1908,7 @@ $(gen_files_kx509) kx509_asn1.hx kx509_asn1-priv.hx: kx509_asn1_files
$(gen_files_rfc2459) rfc2459_asn1.hx rfc2459_asn1-priv.hx: rfc2459_asn1_files
$(gen_files_cms) cms_asn1.hx cms_asn1-priv.hx: cms_asn1_files
$(gen_files_test) test_asn1.hx test_asn1-priv.hx: test_asn1_files
+$(gen_files_test_template) test_template_asn1.hx test_template_asn1-priv.hx: test_template_asn1_files
rfc2459_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/rfc2459.asn1
$(ASN1_COMPILE) --one-code-file --preserve-binary=TBSCertificate --preserve-binary=TBSCRLCertList --preserve-binary=Name --sequence=GeneralNames --sequence=Extensions --sequence=CRLDistributionPoints $(srcdir)/rfc2459.asn1 rfc2459_asn1 || (rm -f rfc2459_asn1_files ; exit 1)
@@ -1459,13 +1937,18 @@ digest_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/digest.asn1
kx509_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/kx509.asn1
$(ASN1_COMPILE) --one-code-file $(srcdir)/kx509.asn1 kx509_asn1 || (rm -f kx509_asn1_files ; exit 1)
+test_template_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/test.asn1
+ $(ASN1_COMPILE) --template --sequence=TESTSeqOf $(srcdir)/test.asn1 test_template_asn1 || (rm -f test_template_asn1_files ; exit 1)
+
test_asn1_files: asn1_compile$(EXEEXT) $(srcdir)/test.asn1
$(ASN1_COMPILE) --one-code-file --sequence=TESTSeqOf $(srcdir)/test.asn1 test_asn1 || (rm -f test_asn1_files ; exit 1)
-$(srcdir)/der-protos.h:
+$(ALL_OBJECTS): $(DER_PROTOS) asn1_err.h
+
+$(srcdir)/der-protos.h: $(dist_libasn1base_la_SOURCES)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -o der-protos.h $(dist_libasn1base_la_SOURCES) || rm -f der-protos.h
-$(srcdir)/der-private.h:
+$(srcdir)/der-private.h: $(dist_libasn1base_la_SOURCES)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p der-private.h $(dist_libasn1base_la_SOURCES) || rm -f der-private.h
# Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/lib/asn1/NTMakefile b/lib/asn1/NTMakefile
index 1ee62b198795..01dc9971b86f 100644
--- a/lib/asn1/NTMakefile
+++ b/lib/asn1/NTMakefile
@@ -31,7 +31,7 @@
RELDIR=lib\asn1
-intcflags=-I$(SRCDIR) -I$(OBJ)
+intcflags=-I$(SRCDIR) -I$(OBJ) -DROKEN_RENAME
!include ../../windows/NTMakefile.w32
@@ -318,11 +318,11 @@ test-binaries: $(TEST_BINARIES)
test-run:
cd $(OBJ)
- check-der.exe
- check-gen.exe
- check-timegm.exe
- check-ber.exe
- check-template.exe
+ -check-der.exe
+ -check-gen.exe
+ -check-timegm.exe
+ -check-ber.exe
+ -check-template.exe
cd $(SRC)
test:: test-binaries test-run
diff --git a/lib/asn1/README.template b/lib/asn1/README.template
new file mode 100644
index 000000000000..874c8fb0b8f0
--- /dev/null
+++ b/lib/asn1/README.template
@@ -0,0 +1,131 @@
+#!/bin/sh
+
+size .libs/libasn1.dylib
+size .libs/libasn1base.a | awk '{sum += $1} END {print sum}' | sed 's/^/TEXT baselib: /'
+size .libs/asn1_*.o | awk '{sum += $1} END {print sum}' | sed 's/^/generated code stubs: /'
+size *_asn1-template.o | awk '{sum += $1} END {print sum}' | sed 's/^/TEXT stubs: /'
+
+exit 0
+
+Notes about the template parser:
+
+- assumption: code is large, tables smaller
+
+- how to generate template based stubs:
+
+ make check asn1_compile_FLAGS=--template > log
+
+- pretty much the same as the generate code, except uses tables instead of code
+
+TODO:
+ - Make hdb work
+
+ - Fuzzing tests
+
+ - Performance testing
+
+ - ASN1_MALLOC_ENCODE() as a function, replaces encode_ and length_
+
+ - Fix SIZE constraits
+
+ - Compact types that only contain on entry to not having a header.
+
+
+SIZE - Futher down is later generations of the template parser
+
+ code:
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 462848 12288 0 323584 798720 c3000 (O2)
+
+ trivial types:
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 446464 12288 0 323584 782336 bf000 (O2)
+
+ OPTIONAL
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 425984 16384 0 323584 765952 bb000 (O2)
+
+ SEQ OF
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 368640 32768 0 327680 729088 b2000 (O2)
+ 348160 32768 0 327680 708608 ad000 (Os)
+
+ BOOLEAN
+ ==================
+ 339968 32768 0 327680 700416 ab000 (Os)
+
+ TYPE_EXTERNAL:
+ ==================
+ 331776 32768 0 327680 692224 a9000 (Os)
+
+ SET OF
+ ==================
+ 327680 32768 0 327680 688128 a8000 (Os)
+
+ TYPE_EXTERNAL everywhere
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 167936 69632 0 327680 565248 8a000 (Os)
+
+ TAG uses ->ptr (header and trailer)
+ ==================
+ 229376 102400 0 421888 753664 b8000 (O0)
+
+ TAG uses ->ptr (header only)
+ ==================
+ 221184 77824 0 421888 720896 b0000 (O0)
+
+ BER support for octet string (not working)
+ ==================
+ 180224 73728 0 417792 671744 a4000 (O2)
+
+ CHOICE and BIT STRING missign
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 172032 73728 0 417792 663552 a2000 (Os)
+
+ No accessor functions to global variable
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 159744 73728 0 393216 626688 99000 (Os)
+
+ All types tables (except choice) (id still objects)
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 167936 77824 0 421888 667648 a3000
+ base lib: 22820
+
+ __TEXT __DATA __OBJC others dec hex
+ ==================
+ 167936 77824 0 421888 667648 a3000 (Os)
+ baselib: 22820
+ generated code stubs: 41472
+ TEXT stubs: 112560
+
+ All types, id still objects
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 155648 81920 0 430080 667648 a3000 (Os)
+ TEXT baselib: 23166
+ generated code stubs: 20796
+ TEXT stubs: 119891
+
+ All types, id still objects, dup compression
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 143360 65536 0 376832 585728 8f000 (Os)
+ TEXT baselib: 23166
+ generated code stubs: 20796
+ TEXT stubs: 107147
+
+ All types, dup compression, id vars
+ ==================
+ __TEXT __DATA __OBJC others dec hex
+ 131072 65536 0 352256 548864 86000
+ TEXT baselib: 23166
+ generated code stubs: 7536
+ TEXT stubs: 107147
diff --git a/lib/asn1/asn1-common.h b/lib/asn1/asn1-common.h
index 4083ebc23dd6..8a935d3740c2 100644
--- a/lib/asn1/asn1-common.h
+++ b/lib/asn1/asn1-common.h
@@ -7,21 +7,26 @@
#ifndef __asn1_common_definitions__
#define __asn1_common_definitions__
+#ifndef __HEIM_BASE_DATA__
+#define __HEIM_BASE_DATA__ 1
+struct heim_base_data {
+ size_t length;
+ void *data;
+};
+#endif
+
typedef struct heim_integer {
size_t length;
void *data;
int negative;
} heim_integer;
-typedef struct heim_octet_string {
- size_t length;
- void *data;
-} heim_octet_string;
+typedef struct heim_base_data heim_octet_string;
typedef char *heim_general_string;
typedef char *heim_utf8_string;
-typedef struct heim_octet_string heim_printable_string;
-typedef struct heim_octet_string heim_ia5_string;
+typedef struct heim_base_data heim_printable_string;
+typedef struct heim_base_data heim_ia5_string;
typedef struct heim_bmp_string {
size_t length;
@@ -45,8 +50,8 @@ typedef struct heim_bit_string {
void *data;
} heim_bit_string;
-typedef struct heim_octet_string heim_any;
-typedef struct heim_octet_string heim_any_set;
+typedef struct heim_base_data heim_any;
+typedef struct heim_base_data heim_any_set;
#define ASN1_MALLOC_ENCODE(T, B, BL, S, L, R) \
do { \
diff --git a/lib/asn1/asn1-template.h b/lib/asn1/asn1-template.h
index 107706ce8340..3c0400a4c706 100644
--- a/lib/asn1/asn1-template.h
+++ b/lib/asn1/asn1-template.h
@@ -90,7 +90,7 @@
struct asn1_template {
uint32_t tt;
- size_t offset;
+ uint32_t offset;
const void *ptr;
};
@@ -118,7 +118,9 @@ enum template_types {
A1T_IMEMBER = 0,
A1T_HEIM_INTEGER,
A1T_INTEGER,
+ A1T_INTEGER64,
A1T_UNSIGNED,
+ A1T_UNSIGNED64,
A1T_GENERAL_STRING,
A1T_OCTET_STRING,
A1T_OCTET_STRING_BER,
@@ -134,8 +136,72 @@ enum template_types {
A1T_BOOLEAN,
A1T_OID,
A1T_TELETEX_STRING,
- A1T_NULL
+ A1T_NUM_ENTRY
};
+extern struct asn1_type_func asn1_template_prim[A1T_NUM_ENTRY];
+
+#define ABORT_ON_ERROR() abort()
+
+#define DPOC(data,offset) ((const void *)(((const unsigned char *)data) + offset))
+#define DPO(data,offset) ((void *)(((unsigned char *)data) + offset))
+
+/*
+ * These functions are needed by the generated template stubs and are
+ * really internal functions. Since they are part of der-private.h
+ * that contains extra prototypes that really a private we included a
+ * copy here.
+ */
+
+int
+_asn1_copy_top (
+ const struct asn1_template * /*t*/,
+ const void * /*from*/,
+ void * /*to*/);
+
+void
+_asn1_free_top(const struct asn1_template *t,
+ void *data);
+
+int
+_asn1_decode_top (
+ const struct asn1_template * /*t*/,
+ unsigned /*flags*/,
+ const unsigned char * /*p*/,
+ size_t /*len*/,
+ void * /*data*/,
+ size_t * /*size*/);
+
+int
+_asn1_encode (
+ const struct asn1_template * /*t*/,
+ unsigned char * /*p*/,
+ size_t /*len*/,
+ const void * /*data*/,
+ size_t * /*size*/);
+
+int
+_asn1_encode_fuzzer (
+ const struct asn1_template * /*t*/,
+ unsigned char * /*p*/,
+ size_t /*len*/,
+ const void * /*data*/,
+ size_t * /*size*/);
+
+void
+_asn1_free (
+ const struct asn1_template * /*t*/,
+ void * /*data*/);
+
+size_t
+_asn1_length (
+ const struct asn1_template * /*t*/,
+ const void * /*data*/);
+
+size_t
+_asn1_length_fuzzer (
+ const struct asn1_template * /*t*/,
+ const void * /*data*/);
+
#endif
diff --git a/lib/asn1/asn1_gen.c b/lib/asn1/asn1_gen.c
index 01dc68051622..58532ee700a1 100644
--- a/lib/asn1/asn1_gen.c
+++ b/lib/asn1/asn1_gen.c
@@ -150,8 +150,8 @@ doit(const char *fn)
static int version_flag;
static int help_flag;
struct getargs args[] = {
- { "version", 0, arg_flag, &version_flag },
- { "help", 0, arg_flag, &help_flag }
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/asn1/asn1_print.c b/lib/asn1/asn1_print.c
index 84446e0d8b98..e90204f9cfe0 100644
--- a/lib/asn1/asn1_print.c
+++ b/lib/asn1/asn1_print.c
@@ -174,14 +174,14 @@ loop (unsigned char *buf, size_t len, int indent)
printf ("(length %lu), ", (unsigned long)length);
if (inner_flag) {
- Der_class class;
- Der_type type;
- unsigned int tag;
+ Der_class class2;
+ Der_type type2;
+ unsigned int tag2;
ret = der_get_tag(str.data, str.length,
- &class, &type, &tag, &sz);
+ &class2, &type2, &tag2, &sz);
if (ret || sz > str.length ||
- type != CONS || tag != UT_Sequence)
+ type2 != CONS || tag2 != UT_Sequence)
goto just_an_octet_string;
printf("{\n");
@@ -315,10 +315,11 @@ doit (const char *filename)
static int version_flag;
static int help_flag;
struct getargs args[] = {
- { "indent", 0, arg_negative_flag, &indent_flag },
- { "inner", 0, arg_flag, &inner_flag, "try to parse inner structures of OCTET STRING" },
- { "version", 0, arg_flag, &version_flag },
- { "help", 0, arg_flag, &help_flag }
+ { "indent", 0, arg_negative_flag, &indent_flag, NULL, NULL },
+ { "inner", 0, arg_flag, &inner_flag,
+ "try to parse inner structures of OCTET STRING", NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/asn1/asn1parse.c b/lib/asn1/asn1parse.c
index 0e04fabf6296..f05441eace9b 100644
--- a/lib/asn1/asn1parse.c
+++ b/lib/asn1/asn1parse.c
@@ -255,14 +255,13 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include "symbol.h"
#include "lex.h"
#include "gen_locl.h"
#include "der.h"
-RCSID("$Id$");
-
static Type *new_type (Typetype t);
static struct constraint_spec *new_constraint_spec(enum ctype);
static Type *new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype);
@@ -276,6 +275,8 @@ struct string_list {
struct string_list *next;
};
+static int default_tag_env = TE_EXPLICIT;
+
/* Declarations for Bison */
#define YYMALLOC malloc
#define YYFREE free
@@ -302,9 +303,9 @@ struct string_list {
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 71 "asn1parse.y"
+#line 72 "asn1parse.y"
{
- int constant;
+ int64_t constant;
struct value *value;
struct range *range;
char *name;
@@ -318,7 +319,7 @@ typedef union YYSTYPE
struct constraint_spec *constraint_spec;
}
/* Line 193 of yacc.c. */
-#line 322 "asn1parse.c"
+#line 323 "asn1parse.c"
YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
@@ -331,7 +332,7 @@ typedef union YYSTYPE
/* Line 216 of yacc.c. */
-#line 335 "asn1parse.c"
+#line 336 "asn1parse.c"
#ifdef short
# undef short
@@ -674,21 +675,21 @@ static const yytype_int16 yyrhs[] =
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint16 yyrline[] =
{
- 0, 239, 239, 246, 247, 249, 251, 254, 256, 259,
- 260, 263, 264, 267, 268, 271, 272, 275, 287, 293,
- 294, 297, 298, 301, 302, 305, 311, 319, 329, 330,
- 331, 334, 335, 336, 337, 338, 339, 340, 341, 342,
- 343, 344, 345, 346, 347, 350, 357, 367, 375, 383,
- 394, 399, 405, 413, 419, 424, 428, 441, 449, 452,
- 459, 467, 473, 482, 490, 491, 496, 502, 510, 519,
- 525, 533, 541, 548, 549, 552, 563, 568, 575, 591,
- 597, 600, 601, 604, 610, 618, 628, 634, 647, 656,
- 659, 663, 667, 674, 677, 681, 688, 699, 702, 707,
- 712, 717, 722, 727, 732, 737, 745, 751, 756, 767,
- 778, 784, 790, 798, 804, 811, 824, 825, 828, 835,
- 838, 849, 853, 864, 870, 871, 874, 875, 876, 877,
- 878, 881, 884, 887, 898, 906, 912, 920, 928, 931,
- 936
+ 0, 240, 240, 247, 249, 251, 253, 256, 258, 261,
+ 262, 265, 266, 269, 270, 273, 274, 277, 289, 295,
+ 296, 299, 300, 303, 304, 307, 313, 321, 331, 332,
+ 333, 336, 337, 338, 339, 340, 341, 342, 343, 344,
+ 345, 346, 347, 348, 349, 352, 359, 369, 377, 385,
+ 396, 401, 407, 415, 421, 426, 430, 443, 451, 454,
+ 461, 469, 475, 489, 497, 498, 503, 509, 517, 532,
+ 538, 546, 554, 561, 562, 565, 576, 581, 588, 604,
+ 610, 613, 614, 617, 623, 631, 641, 647, 665, 674,
+ 677, 681, 685, 692, 695, 699, 706, 717, 720, 725,
+ 730, 735, 740, 745, 750, 755, 763, 769, 774, 785,
+ 796, 802, 808, 816, 822, 829, 842, 843, 846, 853,
+ 856, 867, 871, 882, 888, 889, 892, 893, 894, 895,
+ 896, 899, 902, 905, 916, 924, 930, 938, 946, 949,
+ 954
};
#endif
@@ -1774,29 +1775,34 @@ yyreduce:
switch (yyn)
{
case 2:
-#line 241 "asn1parse.y"
+#line 242 "asn1parse.y"
{
checkundefined();
}
break;
- case 4:
+ case 3:
#line 248 "asn1parse.y"
- { lex_error_message("implicit tagging is not supported"); }
+ { default_tag_env = TE_EXPLICIT; }
break;
- case 5:
+ case 4:
#line 250 "asn1parse.y"
+ { default_tag_env = TE_IMPLICIT; }
+ break;
+
+ case 5:
+#line 252 "asn1parse.y"
{ lex_error_message("automatic tagging is not supported"); }
break;
case 7:
-#line 255 "asn1parse.y"
+#line 257 "asn1parse.y"
{ lex_error_message("no extensibility options supported"); }
break;
case 17:
-#line 276 "asn1parse.y"
+#line 278 "asn1parse.y"
{
struct string_list *sl;
for(sl = (yyvsp[(1) - (4)].sl); sl != NULL; sl = sl->next) {
@@ -1809,7 +1815,7 @@ yyreduce:
break;
case 18:
-#line 288 "asn1parse.y"
+#line 290 "asn1parse.y"
{
struct string_list *sl;
for(sl = (yyvsp[(2) - (3)].sl); sl != NULL; sl = sl->next)
@@ -1818,7 +1824,7 @@ yyreduce:
break;
case 25:
-#line 306 "asn1parse.y"
+#line 308 "asn1parse.y"
{
(yyval.sl) = emalloc(sizeof(*(yyval.sl)));
(yyval.sl)->string = (yyvsp[(1) - (3)].name);
@@ -1827,7 +1833,7 @@ yyreduce:
break;
case 26:
-#line 312 "asn1parse.y"
+#line 314 "asn1parse.y"
{
(yyval.sl) = emalloc(sizeof(*(yyval.sl)));
(yyval.sl)->string = (yyvsp[(1) - (1)].name);
@@ -1836,7 +1842,7 @@ yyreduce:
break;
case 27:
-#line 320 "asn1parse.y"
+#line 322 "asn1parse.y"
{
Symbol *s = addsym ((yyvsp[(1) - (3)].name));
s->stype = Stype;
@@ -1847,7 +1853,7 @@ yyreduce:
break;
case 45:
-#line 351 "asn1parse.y"
+#line 353 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_Boolean,
TE_EXPLICIT, new_type(TBoolean));
@@ -1855,7 +1861,7 @@ yyreduce:
break;
case 46:
-#line 358 "asn1parse.y"
+#line 360 "asn1parse.y"
{
if((yyvsp[(2) - (5)].value)->type != integervalue)
lex_error_message("Non-integer used in first part of range");
@@ -1868,29 +1874,29 @@ yyreduce:
break;
case 47:
-#line 368 "asn1parse.y"
+#line 370 "asn1parse.y"
{
if((yyvsp[(2) - (5)].value)->type != integervalue)
lex_error_message("Non-integer in first part of range");
(yyval.range) = ecalloc(1, sizeof(*(yyval.range)));
(yyval.range)->min = (yyvsp[(2) - (5)].value)->u.integervalue;
- (yyval.range)->max = (yyvsp[(2) - (5)].value)->u.integervalue - 1;
+ (yyval.range)->max = INT_MAX;
}
break;
case 48:
-#line 376 "asn1parse.y"
+#line 378 "asn1parse.y"
{
if((yyvsp[(4) - (5)].value)->type != integervalue)
lex_error_message("Non-integer in second part of range");
(yyval.range) = ecalloc(1, sizeof(*(yyval.range)));
- (yyval.range)->min = (yyvsp[(4) - (5)].value)->u.integervalue + 2;
+ (yyval.range)->min = INT_MIN;
(yyval.range)->max = (yyvsp[(4) - (5)].value)->u.integervalue;
}
break;
case 49:
-#line 384 "asn1parse.y"
+#line 386 "asn1parse.y"
{
if((yyvsp[(2) - (3)].value)->type != integervalue)
lex_error_message("Non-integer used in limit");
@@ -1901,7 +1907,7 @@ yyreduce:
break;
case 50:
-#line 395 "asn1parse.y"
+#line 397 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_Integer,
TE_EXPLICIT, new_type(TInteger));
@@ -1909,7 +1915,7 @@ yyreduce:
break;
case 51:
-#line 400 "asn1parse.y"
+#line 402 "asn1parse.y"
{
(yyval.type) = new_type(TInteger);
(yyval.type)->range = (yyvsp[(2) - (2)].range);
@@ -1918,7 +1924,7 @@ yyreduce:
break;
case 52:
-#line 406 "asn1parse.y"
+#line 408 "asn1parse.y"
{
(yyval.type) = new_type(TInteger);
(yyval.type)->members = (yyvsp[(3) - (4)].members);
@@ -1927,7 +1933,7 @@ yyreduce:
break;
case 53:
-#line 414 "asn1parse.y"
+#line 416 "asn1parse.y"
{
(yyval.members) = emalloc(sizeof(*(yyval.members)));
ASN1_TAILQ_INIT((yyval.members));
@@ -1936,7 +1942,7 @@ yyreduce:
break;
case 54:
-#line 420 "asn1parse.y"
+#line 422 "asn1parse.y"
{
ASN1_TAILQ_INSERT_TAIL((yyvsp[(1) - (3)].members), (yyvsp[(3) - (3)].member), members);
(yyval.members) = (yyvsp[(1) - (3)].members);
@@ -1944,12 +1950,12 @@ yyreduce:
break;
case 55:
-#line 425 "asn1parse.y"
+#line 427 "asn1parse.y"
{ (yyval.members) = (yyvsp[(1) - (3)].members); }
break;
case 56:
-#line 429 "asn1parse.y"
+#line 431 "asn1parse.y"
{
(yyval.member) = emalloc(sizeof(*(yyval.member)));
(yyval.member)->name = (yyvsp[(1) - (4)].name);
@@ -1963,7 +1969,7 @@ yyreduce:
break;
case 57:
-#line 442 "asn1parse.y"
+#line 444 "asn1parse.y"
{
(yyval.type) = new_type(TInteger);
(yyval.type)->members = (yyvsp[(3) - (4)].members);
@@ -1972,7 +1978,7 @@ yyreduce:
break;
case 59:
-#line 453 "asn1parse.y"
+#line 455 "asn1parse.y"
{
(yyval.type) = new_type(TBitString);
(yyval.type)->members = emalloc(sizeof(*(yyval.type)->members));
@@ -1982,7 +1988,7 @@ yyreduce:
break;
case 60:
-#line 460 "asn1parse.y"
+#line 462 "asn1parse.y"
{
(yyval.type) = new_type(TBitString);
(yyval.type)->members = (yyvsp[(4) - (5)].members);
@@ -1991,7 +1997,7 @@ yyreduce:
break;
case 61:
-#line 468 "asn1parse.y"
+#line 470 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_OID,
TE_EXPLICIT, new_type(TOID));
@@ -1999,17 +2005,22 @@ yyreduce:
break;
case 62:
-#line 474 "asn1parse.y"
+#line 476 "asn1parse.y"
{
Type *t = new_type(TOctetString);
t->range = (yyvsp[(3) - (3)].range);
+ if (t->range) {
+ if (t->range->min < 0)
+ lex_error_message("can't use a negative SIZE range "
+ "length for OCTET STRING");
+ }
(yyval.type) = new_tag(ASN1_C_UNIV, UT_OctetString,
TE_EXPLICIT, t);
}
break;
case 63:
-#line 483 "asn1parse.y"
+#line 490 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_Null,
TE_EXPLICIT, new_type(TNull));
@@ -2017,72 +2028,78 @@ yyreduce:
break;
case 64:
-#line 490 "asn1parse.y"
+#line 497 "asn1parse.y"
{ (yyval.range) = NULL; }
break;
case 65:
-#line 492 "asn1parse.y"
+#line 499 "asn1parse.y"
{ (yyval.range) = (yyvsp[(2) - (2)].range); }
break;
case 66:
-#line 497 "asn1parse.y"
+#line 504 "asn1parse.y"
{
(yyval.type) = new_type(TSequence);
(yyval.type)->members = (yyvsp[(3) - (4)].members);
- (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, (yyval.type));
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, (yyval.type));
}
break;
case 67:
-#line 503 "asn1parse.y"
+#line 510 "asn1parse.y"
{
(yyval.type) = new_type(TSequence);
(yyval.type)->members = NULL;
- (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, (yyval.type));
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, (yyval.type));
}
break;
case 68:
-#line 511 "asn1parse.y"
+#line 518 "asn1parse.y"
{
(yyval.type) = new_type(TSequenceOf);
(yyval.type)->range = (yyvsp[(2) - (4)].range);
+ if ((yyval.type)->range) {
+ if ((yyval.type)->range->min < 0)
+ lex_error_message("can't use a negative SIZE range "
+ "length for SEQUENCE OF");
+ }
+
(yyval.type)->subtype = (yyvsp[(4) - (4)].type);
- (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, (yyval.type));
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, (yyval.type));
}
break;
case 69:
-#line 520 "asn1parse.y"
+#line 533 "asn1parse.y"
{
(yyval.type) = new_type(TSet);
(yyval.type)->members = (yyvsp[(3) - (4)].members);
- (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, (yyval.type));
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, (yyval.type));
}
break;
case 70:
-#line 526 "asn1parse.y"
+#line 539 "asn1parse.y"
{
(yyval.type) = new_type(TSet);
(yyval.type)->members = NULL;
- (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, (yyval.type));
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, (yyval.type));
}
break;
case 71:
-#line 534 "asn1parse.y"
+#line 547 "asn1parse.y"
{
(yyval.type) = new_type(TSetOf);
(yyval.type)->subtype = (yyvsp[(3) - (3)].type);
- (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, (yyval.type));
+ (yyval.type) = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, (yyval.type));
}
break;
case 72:
-#line 542 "asn1parse.y"
+#line 555 "asn1parse.y"
{
(yyval.type) = new_type(TChoice);
(yyval.type)->members = (yyvsp[(3) - (4)].members);
@@ -2090,7 +2107,7 @@ yyreduce:
break;
case 75:
-#line 553 "asn1parse.y"
+#line 566 "asn1parse.y"
{
Symbol *s = addsym((yyvsp[(1) - (1)].name));
(yyval.type) = new_type(TType);
@@ -2102,7 +2119,7 @@ yyreduce:
break;
case 76:
-#line 564 "asn1parse.y"
+#line 577 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_GeneralizedTime,
TE_EXPLICIT, new_type(TGeneralizedTime));
@@ -2110,7 +2127,7 @@ yyreduce:
break;
case 77:
-#line 569 "asn1parse.y"
+#line 582 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_UTCTime,
TE_EXPLICIT, new_type(TUTCTime));
@@ -2118,7 +2135,7 @@ yyreduce:
break;
case 78:
-#line 576 "asn1parse.y"
+#line 589 "asn1parse.y"
{
/* if (Constraint.type == contentConstrant) {
assert(Constraint.u.constraint.type == octetstring|bitstring-w/o-NamedBitList); // remember to check type reference too
@@ -2134,14 +2151,14 @@ yyreduce:
break;
case 79:
-#line 592 "asn1parse.y"
+#line 605 "asn1parse.y"
{
(yyval.constraint_spec) = (yyvsp[(2) - (3)].constraint_spec);
}
break;
case 83:
-#line 605 "asn1parse.y"
+#line 618 "asn1parse.y"
{
(yyval.constraint_spec) = new_constraint_spec(CT_CONTENTS);
(yyval.constraint_spec)->u.content.type = (yyvsp[(2) - (2)].type);
@@ -2150,7 +2167,7 @@ yyreduce:
break;
case 84:
-#line 611 "asn1parse.y"
+#line 624 "asn1parse.y"
{
if ((yyvsp[(3) - (3)].value)->type != objectidentifiervalue)
lex_error_message("Non-OID used in ENCODED BY constraint");
@@ -2161,7 +2178,7 @@ yyreduce:
break;
case 85:
-#line 619 "asn1parse.y"
+#line 632 "asn1parse.y"
{
if ((yyvsp[(5) - (5)].value)->type != objectidentifiervalue)
lex_error_message("Non-OID used in ENCODED BY constraint");
@@ -2172,86 +2189,91 @@ yyreduce:
break;
case 86:
-#line 629 "asn1parse.y"
+#line 642 "asn1parse.y"
{
(yyval.constraint_spec) = new_constraint_spec(CT_USER);
}
break;
case 87:
-#line 635 "asn1parse.y"
+#line 648 "asn1parse.y"
{
(yyval.type) = new_type(TTag);
(yyval.type)->tag = (yyvsp[(1) - (3)].tag);
(yyval.type)->tag.tagenv = (yyvsp[(2) - (3)].constant);
- if((yyvsp[(3) - (3)].type)->type == TTag && (yyvsp[(2) - (3)].constant) == TE_IMPLICIT) {
+ if (template_flag) {
+ (yyval.type)->subtype = (yyvsp[(3) - (3)].type);
+ } else {
+ if((yyvsp[(3) - (3)].type)->type == TTag && (yyvsp[(2) - (3)].constant) == TE_IMPLICIT) {
(yyval.type)->subtype = (yyvsp[(3) - (3)].type)->subtype;
free((yyvsp[(3) - (3)].type));
- } else
+ } else {
(yyval.type)->subtype = (yyvsp[(3) - (3)].type);
+ }
+ }
}
break;
case 88:
-#line 648 "asn1parse.y"
+#line 666 "asn1parse.y"
{
(yyval.tag).tagclass = (yyvsp[(2) - (4)].constant);
(yyval.tag).tagvalue = (yyvsp[(3) - (4)].constant);
- (yyval.tag).tagenv = TE_EXPLICIT;
+ (yyval.tag).tagenv = default_tag_env;
}
break;
case 89:
-#line 656 "asn1parse.y"
+#line 674 "asn1parse.y"
{
(yyval.constant) = ASN1_C_CONTEXT;
}
break;
case 90:
-#line 660 "asn1parse.y"
+#line 678 "asn1parse.y"
{
(yyval.constant) = ASN1_C_UNIV;
}
break;
case 91:
-#line 664 "asn1parse.y"
+#line 682 "asn1parse.y"
{
(yyval.constant) = ASN1_C_APPL;
}
break;
case 92:
-#line 668 "asn1parse.y"
+#line 686 "asn1parse.y"
{
(yyval.constant) = ASN1_C_PRIVATE;
}
break;
case 93:
-#line 674 "asn1parse.y"
+#line 692 "asn1parse.y"
{
- (yyval.constant) = TE_EXPLICIT;
+ (yyval.constant) = default_tag_env;
}
break;
case 94:
-#line 678 "asn1parse.y"
+#line 696 "asn1parse.y"
{
- (yyval.constant) = TE_EXPLICIT;
+ (yyval.constant) = default_tag_env;
}
break;
case 95:
-#line 682 "asn1parse.y"
+#line 700 "asn1parse.y"
{
(yyval.constant) = TE_IMPLICIT;
}
break;
case 96:
-#line 689 "asn1parse.y"
+#line 707 "asn1parse.y"
{
Symbol *s;
s = addsym ((yyvsp[(1) - (4)].name));
@@ -2263,7 +2285,7 @@ yyreduce:
break;
case 98:
-#line 703 "asn1parse.y"
+#line 721 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_GeneralString,
TE_EXPLICIT, new_type(TGeneralString));
@@ -2271,7 +2293,7 @@ yyreduce:
break;
case 99:
-#line 708 "asn1parse.y"
+#line 726 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_TeletexString,
TE_EXPLICIT, new_type(TTeletexString));
@@ -2279,7 +2301,7 @@ yyreduce:
break;
case 100:
-#line 713 "asn1parse.y"
+#line 731 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_UTF8String,
TE_EXPLICIT, new_type(TUTF8String));
@@ -2287,7 +2309,7 @@ yyreduce:
break;
case 101:
-#line 718 "asn1parse.y"
+#line 736 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_PrintableString,
TE_EXPLICIT, new_type(TPrintableString));
@@ -2295,7 +2317,7 @@ yyreduce:
break;
case 102:
-#line 723 "asn1parse.y"
+#line 741 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_VisibleString,
TE_EXPLICIT, new_type(TVisibleString));
@@ -2303,7 +2325,7 @@ yyreduce:
break;
case 103:
-#line 728 "asn1parse.y"
+#line 746 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_IA5String,
TE_EXPLICIT, new_type(TIA5String));
@@ -2311,7 +2333,7 @@ yyreduce:
break;
case 104:
-#line 733 "asn1parse.y"
+#line 751 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_BMPString,
TE_EXPLICIT, new_type(TBMPString));
@@ -2319,7 +2341,7 @@ yyreduce:
break;
case 105:
-#line 738 "asn1parse.y"
+#line 756 "asn1parse.y"
{
(yyval.type) = new_tag(ASN1_C_UNIV, UT_UniversalString,
TE_EXPLICIT, new_type(TUniversalString));
@@ -2327,7 +2349,7 @@ yyreduce:
break;
case 106:
-#line 746 "asn1parse.y"
+#line 764 "asn1parse.y"
{
(yyval.members) = emalloc(sizeof(*(yyval.members)));
ASN1_TAILQ_INIT((yyval.members));
@@ -2336,7 +2358,7 @@ yyreduce:
break;
case 107:
-#line 752 "asn1parse.y"
+#line 770 "asn1parse.y"
{
ASN1_TAILQ_INSERT_TAIL((yyvsp[(1) - (3)].members), (yyvsp[(3) - (3)].member), members);
(yyval.members) = (yyvsp[(1) - (3)].members);
@@ -2344,7 +2366,7 @@ yyreduce:
break;
case 108:
-#line 757 "asn1parse.y"
+#line 775 "asn1parse.y"
{
struct member *m = ecalloc(1, sizeof(*m));
m->name = estrdup("...");
@@ -2356,7 +2378,7 @@ yyreduce:
break;
case 109:
-#line 768 "asn1parse.y"
+#line 786 "asn1parse.y"
{
(yyval.member) = emalloc(sizeof(*(yyval.member)));
(yyval.member)->name = (yyvsp[(1) - (2)].name);
@@ -2368,7 +2390,7 @@ yyreduce:
break;
case 110:
-#line 779 "asn1parse.y"
+#line 797 "asn1parse.y"
{
(yyval.member) = (yyvsp[(1) - (1)].member);
(yyval.member)->optional = 0;
@@ -2377,7 +2399,7 @@ yyreduce:
break;
case 111:
-#line 785 "asn1parse.y"
+#line 803 "asn1parse.y"
{
(yyval.member) = (yyvsp[(1) - (2)].member);
(yyval.member)->optional = 1;
@@ -2386,7 +2408,7 @@ yyreduce:
break;
case 112:
-#line 791 "asn1parse.y"
+#line 809 "asn1parse.y"
{
(yyval.member) = (yyvsp[(1) - (3)].member);
(yyval.member)->optional = 0;
@@ -2395,7 +2417,7 @@ yyreduce:
break;
case 113:
-#line 799 "asn1parse.y"
+#line 817 "asn1parse.y"
{
(yyval.members) = emalloc(sizeof(*(yyval.members)));
ASN1_TAILQ_INIT((yyval.members));
@@ -2404,7 +2426,7 @@ yyreduce:
break;
case 114:
-#line 805 "asn1parse.y"
+#line 823 "asn1parse.y"
{
ASN1_TAILQ_INSERT_TAIL((yyvsp[(1) - (3)].members), (yyvsp[(3) - (3)].member), members);
(yyval.members) = (yyvsp[(1) - (3)].members);
@@ -2412,7 +2434,7 @@ yyreduce:
break;
case 115:
-#line 812 "asn1parse.y"
+#line 830 "asn1parse.y"
{
(yyval.member) = emalloc(sizeof(*(yyval.member)));
(yyval.member)->name = (yyvsp[(1) - (4)].name);
@@ -2426,26 +2448,26 @@ yyreduce:
break;
case 117:
-#line 825 "asn1parse.y"
+#line 843 "asn1parse.y"
{ (yyval.objid) = NULL; }
break;
case 118:
-#line 829 "asn1parse.y"
+#line 847 "asn1parse.y"
{
(yyval.objid) = (yyvsp[(2) - (3)].objid);
}
break;
case 119:
-#line 835 "asn1parse.y"
+#line 853 "asn1parse.y"
{
(yyval.objid) = NULL;
}
break;
case 120:
-#line 839 "asn1parse.y"
+#line 857 "asn1parse.y"
{
if ((yyvsp[(2) - (2)].objid)) {
(yyval.objid) = (yyvsp[(2) - (2)].objid);
@@ -2457,14 +2479,14 @@ yyreduce:
break;
case 121:
-#line 850 "asn1parse.y"
+#line 868 "asn1parse.y"
{
(yyval.objid) = new_objid((yyvsp[(1) - (4)].name), (yyvsp[(3) - (4)].constant));
}
break;
case 122:
-#line 854 "asn1parse.y"
+#line 872 "asn1parse.y"
{
Symbol *s = addsym((yyvsp[(1) - (1)].name));
if(s->stype != SValue ||
@@ -2478,14 +2500,14 @@ yyreduce:
break;
case 123:
-#line 865 "asn1parse.y"
+#line 883 "asn1parse.y"
{
(yyval.objid) = new_objid(NULL, (yyvsp[(1) - (1)].constant));
}
break;
case 133:
-#line 888 "asn1parse.y"
+#line 906 "asn1parse.y"
{
Symbol *s = addsym((yyvsp[(1) - (1)].name));
if(s->stype != SValue)
@@ -2497,7 +2519,7 @@ yyreduce:
break;
case 134:
-#line 899 "asn1parse.y"
+#line 917 "asn1parse.y"
{
(yyval.value) = emalloc(sizeof(*(yyval.value)));
(yyval.value)->type = stringvalue;
@@ -2506,7 +2528,7 @@ yyreduce:
break;
case 135:
-#line 907 "asn1parse.y"
+#line 925 "asn1parse.y"
{
(yyval.value) = emalloc(sizeof(*(yyval.value)));
(yyval.value)->type = booleanvalue;
@@ -2515,7 +2537,7 @@ yyreduce:
break;
case 136:
-#line 913 "asn1parse.y"
+#line 931 "asn1parse.y"
{
(yyval.value) = emalloc(sizeof(*(yyval.value)));
(yyval.value)->type = booleanvalue;
@@ -2524,7 +2546,7 @@ yyreduce:
break;
case 137:
-#line 921 "asn1parse.y"
+#line 939 "asn1parse.y"
{
(yyval.value) = emalloc(sizeof(*(yyval.value)));
(yyval.value)->type = integervalue;
@@ -2533,13 +2555,13 @@ yyreduce:
break;
case 139:
-#line 932 "asn1parse.y"
+#line 950 "asn1parse.y"
{
}
break;
case 140:
-#line 937 "asn1parse.y"
+#line 955 "asn1parse.y"
{
(yyval.value) = emalloc(sizeof(*(yyval.value)));
(yyval.value)->type = objectidentifiervalue;
@@ -2549,7 +2571,7 @@ yyreduce:
/* Line 1267 of yacc.c. */
-#line 2553 "asn1parse.c"
+#line 2575 "asn1parse.c"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
@@ -2763,7 +2785,7 @@ yyreturn:
}
-#line 944 "asn1parse.y"
+#line 962 "asn1parse.y"
void
@@ -2810,11 +2832,14 @@ add_oid_to_tail(struct objid *head, struct objid *tail)
o->next = tail;
}
+static unsigned long idcounter;
+
static Type *
new_type (Typetype tt)
{
Type *t = ecalloc(1, sizeof(*t));
t->type = tt;
+ t->id = idcounter++;
return t;
}
diff --git a/lib/asn1/asn1parse.h b/lib/asn1/asn1parse.h
index 69b7d6dc1a4a..3b59959ffbd2 100644
--- a/lib/asn1/asn1parse.h
+++ b/lib/asn1/asn1parse.h
@@ -222,9 +222,9 @@
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 71 "asn1parse.y"
+#line 72 "asn1parse.y"
{
- int constant;
+ int64_t constant;
struct value *value;
struct range *range;
char *name;
diff --git a/lib/asn1/asn1parse.y b/lib/asn1/asn1parse.y
index e3bea6ce0ac9..88299f08b565 100644
--- a/lib/asn1/asn1parse.y
+++ b/lib/asn1/asn1parse.y
@@ -41,14 +41,13 @@
#include <stdio.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#include "symbol.h"
#include "lex.h"
#include "gen_locl.h"
#include "der.h"
-RCSID("$Id$");
-
static Type *new_type (Typetype t);
static struct constraint_spec *new_constraint_spec(enum ctype);
static Type *new_tag(int tagclass, int tagvalue, int tagenv, Type *oldtype);
@@ -62,6 +61,8 @@ struct string_list {
struct string_list *next;
};
+static int default_tag_env = TE_EXPLICIT;
+
/* Declarations for Bison */
#define YYMALLOC malloc
#define YYFREE free
@@ -69,7 +70,7 @@ struct string_list {
%}
%union {
- int constant;
+ int64_t constant;
struct value *value;
struct range *range;
char *name;
@@ -243,9 +244,10 @@ ModuleDefinition: IDENTIFIER objid_opt kw_DEFINITIONS TagDefault ExtensionDefaul
}
;
-TagDefault : kw_EXPLICIT kw_TAGS
+TagDefault : kw_EXPLICIT kw_TAGS
+ { default_tag_env = TE_EXPLICIT; }
| kw_IMPLICIT kw_TAGS
- { lex_error_message("implicit tagging is not supported"); }
+ { default_tag_env = TE_IMPLICIT; }
| kw_AUTOMATIC kw_TAGS
{ lex_error_message("automatic tagging is not supported"); }
| /* empty */
@@ -370,14 +372,14 @@ range : '(' Value RANGE Value ')'
lex_error_message("Non-integer in first part of range");
$$ = ecalloc(1, sizeof(*$$));
$$->min = $2->u.integervalue;
- $$->max = $2->u.integervalue - 1;
+ $$->max = INT_MAX;
}
| '(' kw_MIN RANGE Value ')'
{
if($4->type != integervalue)
lex_error_message("Non-integer in second part of range");
$$ = ecalloc(1, sizeof(*$$));
- $$->min = $4->u.integervalue + 2;
+ $$->min = INT_MIN;
$$->max = $4->u.integervalue;
}
| '(' Value ')'
@@ -474,6 +476,11 @@ OctetStringType : kw_OCTET kw_STRING size
{
Type *t = new_type(TOctetString);
t->range = $3;
+ if (t->range) {
+ if (t->range->min < 0)
+ lex_error_message("can't use a negative SIZE range "
+ "length for OCTET STRING");
+ }
$$ = new_tag(ASN1_C_UNIV, UT_OctetString,
TE_EXPLICIT, t);
}
@@ -497,13 +504,13 @@ SequenceType : kw_SEQUENCE '{' /* ComponentTypeLists */ ComponentTypeList '}'
{
$$ = new_type(TSequence);
$$->members = $3;
- $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
+ $$ = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, $$);
}
| kw_SEQUENCE '{' '}'
{
$$ = new_type(TSequence);
$$->members = NULL;
- $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
+ $$ = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, $$);
}
;
@@ -511,8 +518,14 @@ SequenceOfType : kw_SEQUENCE size kw_OF Type
{
$$ = new_type(TSequenceOf);
$$->range = $2;
+ if ($$->range) {
+ if ($$->range->min < 0)
+ lex_error_message("can't use a negative SIZE range "
+ "length for SEQUENCE OF");
+ }
+
$$->subtype = $4;
- $$ = new_tag(ASN1_C_UNIV, UT_Sequence, TE_EXPLICIT, $$);
+ $$ = new_tag(ASN1_C_UNIV, UT_Sequence, default_tag_env, $$);
}
;
@@ -520,13 +533,13 @@ SetType : kw_SET '{' /* ComponentTypeLists */ ComponentTypeList '}'
{
$$ = new_type(TSet);
$$->members = $3;
- $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
+ $$ = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, $$);
}
| kw_SET '{' '}'
{
$$ = new_type(TSet);
$$->members = NULL;
- $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
+ $$ = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, $$);
}
;
@@ -534,7 +547,7 @@ SetOfType : kw_SET kw_OF Type
{
$$ = new_type(TSetOf);
$$->subtype = $3;
- $$ = new_tag(ASN1_C_UNIV, UT_Set, TE_EXPLICIT, $$);
+ $$ = new_tag(ASN1_C_UNIV, UT_Set, default_tag_env, $$);
}
;
@@ -636,11 +649,16 @@ TaggedType : Tag tagenv Type
$$ = new_type(TTag);
$$->tag = $1;
$$->tag.tagenv = $2;
- if($3->type == TTag && $2 == TE_IMPLICIT) {
+ if (template_flag) {
+ $$->subtype = $3;
+ } else {
+ if($3->type == TTag && $2 == TE_IMPLICIT) {
$$->subtype = $3->subtype;
free($3);
- } else
+ } else {
$$->subtype = $3;
+ }
+ }
}
;
@@ -648,7 +666,7 @@ Tag : '[' Class NUMBER ']'
{
$$.tagclass = $2;
$$.tagvalue = $3;
- $$.tagenv = TE_EXPLICIT;
+ $$.tagenv = default_tag_env;
}
;
@@ -672,11 +690,11 @@ Class : /* */
tagenv : /* */
{
- $$ = TE_EXPLICIT;
+ $$ = default_tag_env;
}
| kw_EXPLICIT
{
- $$ = TE_EXPLICIT;
+ $$ = default_tag_env;
}
| kw_IMPLICIT
{
@@ -987,11 +1005,14 @@ add_oid_to_tail(struct objid *head, struct objid *tail)
o->next = tail;
}
+static unsigned long idcounter;
+
static Type *
new_type (Typetype tt)
{
Type *t = ecalloc(1, sizeof(*t));
t->type = tt;
+ t->id = idcounter++;
return t;
}
diff --git a/lib/asn1/check-common.c b/lib/asn1/check-common.c
index ac96b91b18ea..7eadc0109f52 100644
--- a/lib/asn1/check-common.c
+++ b/lib/asn1/check-common.c
@@ -33,9 +33,7 @@
* SUCH DAMAGE.
*/
-#ifdef HAVE_CONFIG_H
#include <config.h>
-#endif
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
@@ -47,8 +45,6 @@
#include "asn1-common.h"
#include "check-common.h"
-RCSID("$Id$");
-
struct map_page {
void *start;
size_t size;
diff --git a/lib/asn1/check-common.h b/lib/asn1/check-common.h
index 9ecbdbc35ded..c10fec28ca13 100644
--- a/lib/asn1/check-common.h
+++ b/lib/asn1/check-common.h
@@ -33,9 +33,26 @@
* SUCH DAMAGE.
*/
+#define IF_OPT_COMPARE(ac,bc,e) \
+ if (((ac)->e == NULL && (bc)->e != NULL) || (((ac)->e != NULL && (bc)->e == NULL))) return 1; if ((ac)->e)
+#define COMPARE_OPT_STRING(ac,bc,e) \
+ do { if (strcmp(*(ac)->e, *(bc)->e) != 0) return 1; } while(0)
+#define COMPARE_OPT_OCTET_STRING(ac,bc,e) \
+ do { if ((ac)->e->length != (bc)->e->length || memcmp((ac)->e->data, (bc)->e->data, (ac)->e->length) != 0) return 1; } while(0)
+#define COMPARE_STRING(ac,bc,e) \
+ do { if (strcmp((ac)->e, (bc)->e) != 0) return 1; } while(0)
+#define COMPARE_INTEGER(ac,bc,e) \
+ do { if ((ac)->e != (bc)->e) return 1; } while(0)
+#define COMPARE_OPT_INTEGER(ac,bc,e) \
+ do { if (*(ac)->e != *(bc)->e) return 1; } while(0)
+#define COMPARE_MEM(ac,bc,e,len) \
+ do { if (memcmp((ac)->e, (bc)->e,len) != 0) return 1; } while(0)
+#define COMPARE_OCTET_STRING(ac,bc,e) \
+ do { if ((ac)->e.length != (bc)->e.length || memcmp((ac)->e.data, (bc)->e.data, (ac)->e.length) != 0) return 1; } while(0)
+
struct test_case {
void *val;
- int byte_len;
+ ssize_t byte_len;
const char *bytes;
char *name;
};
diff --git a/lib/asn1/check-der.c b/lib/asn1/check-der.c
index fa80a425410f..007197eb1fdc 100644
--- a/lib/asn1/check-der.c
+++ b/lib/asn1/check-der.c
@@ -58,16 +58,16 @@ static int
test_integer (void)
{
struct test_case tests[] = {
- {NULL, 1, "\x00"},
- {NULL, 1, "\x7f"},
- {NULL, 2, "\x00\x80"},
- {NULL, 2, "\x01\x00"},
- {NULL, 1, "\x80"},
- {NULL, 2, "\xff\x7f"},
- {NULL, 1, "\xff"},
- {NULL, 2, "\xff\x01"},
- {NULL, 2, "\x00\xff"},
- {NULL, 4, "\x7f\xff\xff\xff"}
+ {NULL, 1, "\x00", NULL },
+ {NULL, 1, "\x7f", NULL },
+ {NULL, 2, "\x00\x80", NULL },
+ {NULL, 2, "\x01\x00", NULL },
+ {NULL, 1, "\x80", NULL },
+ {NULL, 2, "\xff\x7f", NULL },
+ {NULL, 1, "\xff", NULL },
+ {NULL, 2, "\xff\x01", NULL },
+ {NULL, 2, "\x00\xff", NULL },
+ {NULL, 4, "\x7f\xff\xff\xff", NULL }
};
int values[] = {0, 127, 128, 256, -128, -129, -1, -255, 255,
@@ -184,14 +184,14 @@ static int
test_unsigned (void)
{
struct test_case tests[] = {
- {NULL, 1, "\x00"},
- {NULL, 1, "\x7f"},
- {NULL, 2, "\x00\x80"},
- {NULL, 2, "\x01\x00"},
- {NULL, 2, "\x02\x00"},
- {NULL, 3, "\x00\x80\x00"},
- {NULL, 5, "\x00\x80\x00\x00\x00"},
- {NULL, 4, "\x7f\xff\xff\xff"}
+ {NULL, 1, "\x00", NULL },
+ {NULL, 1, "\x7f", NULL },
+ {NULL, 2, "\x00\x80", NULL },
+ {NULL, 2, "\x01\x00", NULL },
+ {NULL, 2, "\x02\x00", NULL },
+ {NULL, 3, "\x00\x80\x00", NULL },
+ {NULL, 5, "\x00\x80\x00\x00\x00", NULL },
+ {NULL, 4, "\x7f\xff\xff\xff", NULL }
};
unsigned int values[] = {0, 127, 128, 256, 512, 32768,
@@ -222,13 +222,7 @@ test_unsigned (void)
static int
cmp_octet_string (void *a, void *b)
{
- heim_octet_string *oa = (heim_octet_string *)a;
- heim_octet_string *ob = (heim_octet_string *)b;
-
- if (oa->length != ob->length)
- return ob->length - oa->length;
-
- return (memcmp (oa->data, ob->data, oa->length));
+ return der_heim_octet_string_cmp(a, b);
}
static int
@@ -237,7 +231,7 @@ test_octet_string (void)
heim_octet_string s1 = {8, "\x01\x23\x45\x67\x89\xab\xcd\xef"};
struct test_case tests[] = {
- {NULL, 8, "\x01\x23\x45\x67\x89\xab\xcd\xef"}
+ {NULL, 8, "\x01\x23\x45\x67\x89\xab\xcd\xef", NULL }
};
int ntests = sizeof(tests) / sizeof(*tests);
int ret;
@@ -278,8 +272,8 @@ test_bmp_string (void)
heim_bmp_string s2 = { 2, bmp_d2 };
struct test_case tests[] = {
- {NULL, 2, "\x00\x20"},
- {NULL, 4, "\x00\x20\x00\x20"}
+ {NULL, 2, "\x00\x20", NULL },
+ {NULL, 4, "\x00\x20\x00\x20", NULL }
};
int ntests = sizeof(tests) / sizeof(*tests);
int ret;
@@ -326,8 +320,8 @@ test_universal_string (void)
heim_universal_string s2 = { 2, universal_d2 };
struct test_case tests[] = {
- {NULL, 4, "\x00\x00\x00\x20"},
- {NULL, 8, "\x00\x00\x00\x20\x00\x00\x00\x20"}
+ {NULL, 4, "\x00\x00\x00\x20", NULL },
+ {NULL, 8, "\x00\x00\x00\x20\x00\x00\x00\x20", NULL }
};
int ntests = sizeof(tests) / sizeof(*tests);
int ret;
@@ -370,7 +364,7 @@ test_general_string (void)
char *s1 = "Test User 1";
struct test_case tests[] = {
- {NULL, 11, "\x54\x65\x73\x74\x20\x55\x73\x65\x72\x20\x31"}
+ {NULL, 11, "\x54\x65\x73\x74\x20\x55\x73\x65\x72\x20\x31", NULL }
};
int ret, ntests = sizeof(tests) / sizeof(*tests);
@@ -397,15 +391,15 @@ cmp_generalized_time (void *a, void *b)
time_t *ta = (time_t *)a;
time_t *tb = (time_t *)b;
- return *tb - *ta;
+ return (int)(*tb - *ta);
}
static int
test_generalized_time (void)
{
struct test_case tests[] = {
- {NULL, 15, "19700101000000Z"},
- {NULL, 15, "19851106210627Z"}
+ {NULL, 15, "19700101000000Z", NULL },
+ {NULL, 15, "19851106210627Z", NULL }
};
time_t values[] = {0, 500159187};
int i, ret;
@@ -446,10 +440,10 @@ static int
test_oid (void)
{
struct test_case tests[] = {
- {NULL, 2, "\x29\x01"},
- {NULL, 1, "\x29"},
- {NULL, 2, "\xff\x01"},
- {NULL, 1, "\xff"}
+ {NULL, 2, "\x29\x01", NULL },
+ {NULL, 1, "\x29", NULL },
+ {NULL, 2, "\xff\x01", NULL },
+ {NULL, 1, "\xff", NULL }
};
heim_oid values[] = {
{ 3, oid_comp1 },
@@ -490,7 +484,7 @@ static int
test_bit_string (void)
{
struct test_case tests[] = {
- {NULL, 1, "\x00"}
+ {NULL, 1, "\x00", NULL }
};
heim_bit_string values[] = {
{ 0, "" }
@@ -528,13 +522,13 @@ static int
test_heim_integer (void)
{
struct test_case tests[] = {
- {NULL, 2, "\xfe\x01"},
- {NULL, 2, "\xef\x01"},
- {NULL, 3, "\xff\x00\xff"},
- {NULL, 3, "\xff\x01\x00"},
- {NULL, 1, "\x00"},
- {NULL, 1, "\x01"},
- {NULL, 2, "\x00\x80"}
+ {NULL, 2, "\xfe\x01", NULL },
+ {NULL, 2, "\xef\x01", NULL },
+ {NULL, 3, "\xff\x00\xff", NULL },
+ {NULL, 3, "\xff\x01\x00", NULL },
+ {NULL, 1, "\x00", NULL },
+ {NULL, 1, "\x01", NULL },
+ {NULL, 2, "\x00\x80", NULL }
};
heim_integer values[] = {
@@ -592,8 +586,8 @@ static int
test_boolean (void)
{
struct test_case tests[] = {
- {NULL, 1, "\xff"},
- {NULL, 1, "\x00"}
+ {NULL, 1, "\xff", NULL },
+ {NULL, 1, "\x00", NULL }
};
int values[] = { 1, 0 };
@@ -1075,6 +1069,104 @@ corner_tag(void)
return 0;
}
+struct randomcheck {
+ asn1_type_decode decoder;
+ asn1_type_release release;
+ size_t typesize;
+ size_t inputsize;
+} randomcheck[] = {
+#define el(name, type, maxlen) { \
+ (asn1_type_decode)der_get_##name, \
+ (asn1_type_release)der_free_##name, \
+ sizeof(type), \
+ maxlen \
+ }
+ el(integer, int, 6),
+ el(heim_integer, heim_integer, 12),
+ el(integer, int, 6),
+ el(unsigned, unsigned, 6),
+ el(general_string, heim_general_string, 12),
+ el(octet_string, heim_octet_string, 12),
+ { (asn1_type_decode)der_get_octet_string_ber,
+ (asn1_type_release)der_free_octet_string,
+ sizeof(heim_octet_string), 20 },
+ el(generalized_time, time_t, 20),
+ el(utctime, time_t, 20),
+ el(bit_string, heim_bit_string, 10),
+ el(oid, heim_oid, 10),
+ { NULL, NULL, 0, 0 }
+#undef el
+};
+
+static void
+asn1rand(uint8_t *randbytes, size_t len)
+{
+ while (len) {
+ *randbytes++ = rk_random();
+ len--;
+ }
+}
+
+static int
+check_random(void)
+{
+ struct randomcheck *r = randomcheck;
+ uint8_t *input;
+ void *type;
+ size_t size, insize;
+ int ret;
+
+ while (r->decoder) {
+ type = emalloc(r->typesize);
+ memset(type, 0, r->typesize);
+
+ input = emalloc(r->inputsize);
+
+ /* try all zero first */
+ memset(input, 0, r->inputsize);
+
+ ret = r->decoder(input, r->inputsize, type, &size);
+ if (ret)
+ r->release(type);
+
+ /* try all one first */
+ memset(input, 0xff, r->inputsize);
+ ret = r->decoder(input, r->inputsize, type, &size);
+ if (ret)
+ r->release(type);
+
+ /* try 0x41 too */
+ memset(input, 0x41, r->inputsize);
+ ret = r->decoder(input, r->inputsize, type, &size);
+ if (ret)
+ r->release(type);
+
+ /* random */
+ asn1rand(input, r->inputsize);
+ ret = r->decoder(input, r->inputsize, type, &size);
+ if (ret)
+ r->release(type);
+
+ /* let make buffer smaller */
+ insize = r->inputsize;
+ do {
+ insize--;
+ asn1rand(input, insize);
+
+ ret = r->decoder(input, insize, type, &size);
+ if (ret == 0)
+ r->release(type);
+ } while(insize > 0);
+
+ free(type);
+
+ r++;
+ }
+ return 0;
+}
+
+
+
int
main(int argc, char **argv)
{
@@ -1110,6 +1202,7 @@ main(int argc, char **argv)
ret += test_misc_cmp();
ret += corner_generalized_time();
ret += corner_tag();
+ ret += check_random();
return ret;
}
diff --git a/lib/asn1/check-gen.c b/lib/asn1/check-gen.c
index e686f166cfa0..bf2da8e7519a 100644
--- a/lib/asn1/check-gen.c
+++ b/lib/asn1/check-gen.c
@@ -33,9 +33,7 @@
* SUCH DAMAGE.
*/
-#ifdef HAVE_CONFIG_H
#include <config.h>
-#endif
#include <stdio.h>
#include <string.h>
#include <err.h>
@@ -48,32 +46,15 @@
#include <heim_asn1.h>
#include <rfc2459_asn1.h>
#include <test_asn1.h>
+#include <cms_asn1.h>
#include "check-common.h"
-RCSID("$Id$");
-
static char *lha_principal[] = { "lha" };
static char *lharoot_princ[] = { "lha", "root" };
static char *datan_princ[] = { "host", "nutcracker.e.kth.se" };
static char *nada_tgt_principal[] = { "krbtgt", "NADA.KTH.SE" };
-
-#define IF_OPT_COMPARE(ac,bc,e) \
- if (((ac)->e == NULL && (bc)->e != NULL) || (((ac)->e != NULL && (bc)->e == NULL))) return 1; if ((ab)->e)
-#define COMPARE_OPT_STRING(ac,bc,e) \
- do { if (strcmp(*(ac)->e, *(bc)->e) != 0) return 1; } while(0)
-#define COMPARE_OPT_OCTECT_STRING(ac,bc,e) \
- do { if ((ac)->e->length != (bc)->e->length || memcmp((ac)->e->data, (bc)->e->data, (ac)->e->length) != 0) return 1; } while(0)
-#define COMPARE_STRING(ac,bc,e) \
- do { if (strcmp((ac)->e, (bc)->e) != 0) return 1; } while(0)
-#define COMPARE_INTEGER(ac,bc,e) \
- do { if ((ac)->e != (bc)->e) return 1; } while(0)
-#define COMPARE_OPT_INTEGER(ac,bc,e) \
- do { if (*(ac)->e != *(bc)->e) return 1; } while(0)
-#define COMPARE_MEM(ac,bc,e,len) \
- do { if (memcmp((ac)->e, (bc)->e,len) != 0) return 1; } while(0)
-
static int
cmp_principal (void *a, void *b)
{
@@ -98,18 +79,21 @@ test_principal (void)
struct test_case tests[] = {
{ NULL, 29,
"\x30\x1b\xa0\x10\x30\x0e\xa0\x03\x02\x01\x01\xa1\x07\x30\x05\x1b"
- "\x03\x6c\x68\x61\xa1\x07\x1b\x05\x53\x55\x2e\x53\x45"
+ "\x03\x6c\x68\x61\xa1\x07\x1b\x05\x53\x55\x2e\x53\x45",
+ NULL
},
{ NULL, 35,
"\x30\x21\xa0\x16\x30\x14\xa0\x03\x02\x01\x01\xa1\x0d\x30\x0b\x1b"
"\x03\x6c\x68\x61\x1b\x04\x72\x6f\x6f\x74\xa1\x07\x1b\x05\x53\x55"
- "\x2e\x53\x45"
+ "\x2e\x53\x45",
+ NULL
},
{ NULL, 54,
"\x30\x34\xa0\x26\x30\x24\xa0\x03\x02\x01\x03\xa1\x1d\x30\x1b\x1b"
"\x04\x68\x6f\x73\x74\x1b\x13\x6e\x75\x74\x63\x72\x61\x63\x6b\x65"
"\x72\x2e\x65\x2e\x6b\x74\x68\x2e\x73\x65\xa1\x0a\x1b\x08\x45\x2e"
- "\x4b\x54\x48\x2e\x53\x45"
+ "\x4b\x54\x48\x2e\x53\x45",
+ NULL
}
};
@@ -171,7 +155,8 @@ test_authenticator (void)
"\x45\x2e\x4b\x54\x48\x2e\x53\x45\xa2\x10\x30\x0e\xa0"
"\x03\x02\x01\x01\xa1\x07\x30\x05\x1b\x03\x6c\x68\x61"
"\xa4\x03\x02\x01\x0a\xa5\x11\x18\x0f\x31\x39\x37\x30"
- "\x30\x31\x30\x31\x30\x30\x30\x31\x33\x39\x5a"
+ "\x30\x31\x30\x31\x30\x30\x30\x31\x33\x39\x5a",
+ NULL
},
{ NULL, 67,
"\x62\x41\x30\x3f\xa0\x03\x02\x01\x05\xa1\x07\x1b\x05"
@@ -179,7 +164,8 @@ test_authenticator (void)
"\x01\xa1\x0d\x30\x0b\x1b\x03\x6c\x68\x61\x1b\x04\x72"
"\x6f\x6f\x74\xa4\x04\x02\x02\x01\x24\xa5\x11\x18\x0f"
"\x31\x39\x37\x30\x30\x31\x30\x31\x30\x30\x31\x36\x33"
- "\x39\x5a"
+ "\x39\x5a",
+ NULL
}
};
@@ -251,7 +237,7 @@ cmp_KRB_ERROR (void *a, void *b)
COMPARE_OPT_STRING(aa,ab,e_text);
}
IF_OPT_COMPARE(aa,ab,e_data) {
- /* COMPARE_OPT_OCTECT_STRING(aa,ab,e_data); */
+ /* COMPARE_OPT_OCTET_STRING(aa,ab,e_data); */
}
return 0;
@@ -532,7 +518,7 @@ test_time (void)
"time 1" },
{ NULL, 17,
"\x18\x0f\x32\x30\x30\x39\x30\x35\x32\x34\x30\x32\x30\x32\x34\x30"
- "\x5a"
+ "\x5a",
"time 2" }
};
@@ -668,6 +654,91 @@ test_cert(void)
return 0;
}
+struct {
+ const char *sd;
+ size_t len;
+} signeddata[] = {
+ {
+ "\x30\x80\x02\x01\x03\x31\x0b\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a"
+ "\x05\x00\x30\x80\x06\x07\x2b\x06\x01\x05\x02\x03\x03\xa0\x80\x24"
+ "\x80\x04\x50\x30\x4e\xa0\x2b\x30\x29\xa0\x03\x02\x01\x12\xa1\x22"
+ "\x04\x20\x78\xf4\x86\x31\xc6\xc2\xc9\xcb\xef\x0c\xd7\x3a\x2a\xcd"
+ "\x8c\x13\x34\x83\xb1\x5c\xa8\xbe\xbf\x2f\xea\xd2\xbb\xd8\x8c\x18"
+ "\x47\x01\xa1\x1f\x30\x1d\xa0\x03\x02\x01\x0c\xa1\x16\x04\x14\xa6"
+ "\x2c\x52\xb2\x80\x98\x30\x40\xbc\x5f\xb0\x77\x2d\x8a\xd7\xa1\xda"
+ "\x3c\xc5\x62\x00\x00\x00\x00\x00\x00\xa0\x82\x02\x09\x30\x82\x02"
+ "\x05\x30\x82\x01\x6e\xa0\x03\x02\x01\x02\x02\x04\x49\x75\x57\xbf"
+ "\x30\x0b\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05\x30\x3b\x31"
+ "\x1f\x30\x1d\x06\x03\x55\x04\x03\x0c\x16\x63\x6f\x6d\x2e\x61\x70"
+ "\x70\x6c\x65\x2e\x6b\x65\x72\x62\x65\x72\x6f\x73\x2e\x6b\x64\x63"
+ "\x31\x18\x30\x16\x06\x03\x55\x04\x0a\x0c\x0f\x53\x79\x73\x74\x65"
+ "\x6d\x20\x49\x64\x65\x6e\x74\x69\x74\x79\x30\x1e\x17\x0d\x30\x39"
+ "\x31\x32\x30\x34\x30\x30\x32\x30\x32\x34\x5a\x17\x0d\x32\x39\x31"
+ "\x31\x32\x39\x30\x30\x32\x30\x32\x34\x5a\x30\x3b\x31\x1f\x30\x1d"
+ "\x06\x03\x55\x04\x03\x0c\x16\x63\x6f\x6d\x2e\x61\x70\x70\x6c\x65"
+ "\x2e\x6b\x65\x72\x62\x65\x72\x6f\x73\x2e\x6b\x64\x63\x31\x18\x30"
+ "\x16\x06\x03\x55\x04\x0a\x0c\x0f\x53\x79\x73\x74\x65\x6d\x20\x49"
+ "\x64\x65\x6e\x74\x69\x74\x79\x30\x81\x9f\x30\x0d\x06\x09\x2a\x86"
+ "\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x03\x81\x8d\x00\x30\x81\x89"
+ "\x02\x81\x81\x00\xb2\xc5\x4b\x34\xe3\x93\x99\xbb\xaa\xd1\x70\x62"
+ "\x6c\x9c\xcc\xa6\xbc\x47\xc3\x23\xff\x15\xb9\x11\x27\x0a\xf8\x55"
+ "\x4c\xb2\x43\x34\x75\xad\x55\xbb\xb9\x8a\xd0\x25\x64\xa4\x8c\x82"
+ "\x74\x5d\x89\x52\xe2\x76\x75\x08\x67\xb5\x9c\x9c\x69\x86\x0c\x6d"
+ "\x79\xf7\xa0\xbe\x42\x8f\x90\x46\x0c\x18\xf4\x7a\x56\x17\xa4\x65"
+ "\x00\x3a\x5e\x3e\xbf\xbc\xf5\xe2\x2c\x26\x03\x52\xdd\xd4\x85\x3f"
+ "\x03\xd7\x0c\x45\x7f\xff\xdd\x1e\x70\x6c\x9f\xb0\x8c\xd0\x33\xad"
+ "\x92\x54\x17\x9d\x88\x89\x1a\xee\xef\xf7\x96\x3e\x68\xc3\xd1\x60"
+ "\x47\x86\x80\x5d\x02\x03\x01\x00\x01\xa3\x18\x30\x16\x30\x14\x06"
+ "\x03\x55\x1d\x25\x04\x0d\x30\x0b\x06\x09\x2a\x86\x48\x86\xf7\x63"
+ "\x64\x04\x04\x30\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x05"
+ "\x05\x00\x03\x81\x81\x00\x9b\xbb\xaa\x63\x66\xd8\x70\x84\x3e\xf6"
+ "\xa1\x3b\xf3\xe6\xd7\x3d\xfc\x4f\xc9\x45\xaa\x31\x43\x8d\xb5\x72"
+ "\xe4\x34\x95\x7b\x6e\x5f\xe5\xc8\x5e\xaf\x12\x08\x6d\xd7\x25\x76"
+ "\x40\xd5\xdc\x83\x7f\x2f\x74\xd1\x63\xc0\x7c\x26\x4d\x53\x10\xe7"
+ "\xfa\xcc\xf2\x60\x41\x63\xdf\x56\xd6\xd9\xc0\xb4\xd0\x73\x99\x54"
+ "\x40\xad\x90\x79\x2d\xd2\x5e\xcb\x13\x22\x2b\xd0\x76\xef\x8a\x48"
+ "\xfd\xb2\x6e\xca\x04\x4e\x91\x3f\xb4\x63\xad\x22\x3a\xf7\x20\x9c"
+ "\x4c\x0e\x47\x78\xe5\x2a\x85\x0e\x90\x7a\xce\x46\xe6\x15\x02\xb0"
+ "\x83\xe7\xac\xfa\x92\xf8\x31\x81\xe8\x30\x81\xe5\x02\x01\x01\x30"
+ "\x43\x30\x3b\x31\x1f\x30\x1d\x06\x03\x55\x04\x03\x0c\x16\x63\x6f"
+ "\x6d\x2e\x61\x70\x70\x6c\x65\x2e\x6b\x65\x72\x62\x65\x72\x6f\x73"
+ "\x2e\x6b\x64\x63\x31\x18\x30\x16\x06\x03\x55\x04\x0a\x0c\x0f\x53"
+ "\x79\x73\x74\x65\x6d\x20\x49\x64\x65\x6e\x74\x69\x74\x79\x02\x04"
+ "\x49\x75\x57\xbf\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x30"
+ "\x0d\x06\x09\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01\x05\x00\x04\x81"
+ "\x80\x50\x2c\x69\xe1\xd2\xc4\xd1\xcc\xdc\xe0\xe9\x8a\x6b\x6a\x97"
+ "\x1b\xb4\xe0\xa8\x20\xbe\x09\x6d\xe1\x55\x5f\x07\x70\x94\x2e\x14"
+ "\xed\x4e\xb1\x69\x75\x40\xbb\x99\x87\xed\x23\x50\x27\x5f\xaa\xc4"
+ "\x84\x60\x06\xfe\x45\xfd\x7e\x1b\x18\xe0\x0b\x77\x35\x2a\xb2\xf2"
+ "\xe0\x88\x31\xad\x82\x31\x4a\xbc\x6d\x71\x62\xe6\x4d\x33\xb4\x09"
+ "\x6e\x3f\x14\x12\xf2\x89\x29\x31\x84\x60\x2b\xa8\x2d\xe6\xca\x2f"
+ "\x03\x3d\xd4\x69\x89\xb3\x98\xfd\xac\x63\x14\xaf\x6a\x52\x2a\xac"
+ "\xe3\x8e\xfa\x21\x41\x8f\xcc\x04\x2d\x52\xee\x49\x54\x0d\x58\x51"
+ "\x77\x00\x00",
+ 883
+ }
+};
+
+static int
+test_SignedData(void)
+{
+ SignedData sd;
+ size_t size, i;
+ int ret;
+
+ for (i = 0; i < sizeof(signeddata) / sizeof(signeddata[0]); i++) {
+
+ ret = decode_SignedData((unsigned char *)signeddata[i].sd,
+ signeddata[i].len, &sd, &size);
+ if (ret)
+ return ret;
+
+ free_SignedData(&sd);
+ }
+
+ return 0;
+}
+
static int
cmp_TESTLargeTag (void *a, void *b)
@@ -765,6 +836,132 @@ check_tag_length(void)
}
static int
+check_tag_length64(void)
+{
+ struct test_data td[] = {
+ { 1, 3, 3, "\x02\x01\x00"},
+ { 1, 7, 7, "\x02\x05\x01\xff\xff\xff\xff"},
+ { 1, 7, 7, "\x02\x05\x02\x00\x00\x00\x00"},
+ { 1, 9, 9, "\x02\x07\x7f\xff\xff\xff\xff\xff\xff"},
+ { 1, 10, 10, "\x02\x08\x00\x80\x00\x00\x00\x00\x00\x00"},
+ { 1, 10, 10, "\x02\x08\x7f\xff\xff\xff\xff\xff\xff\xff"},
+ { 1, 11, 11, "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff"},
+ { 0, 3, 0, "\x02\x02\x00"},
+ { 0, 3, 0, "\x02\x7f\x7f"},
+ { 0, 4, 0, "\x02\x03\x00\x80"},
+ { 0, 4, 0, "\x02\x7f\x01\x00"},
+ { 0, 5, 0, "\x02\xff\x7f\x02\x00"}
+ };
+ size_t sz;
+ TESTuint64 values[] = {0, 8589934591LL, 8589934592LL,
+ 36028797018963967LL, 36028797018963968LL,
+ 9223372036854775807LL, 18446744073709551615ULL,
+ 0, 127, 128, 256, 512 };
+ TESTuint64 u;
+ int i, ret, failed = 0;
+ void *buf;
+
+ if (sizeof(TESTuint64) != sizeof(uint64_t)) {
+ ret += 1;
+ printf("sizeof(TESTuint64) %d != sizeof(uint64_t) %d\n",
+ (int)sizeof(TESTuint64), (int)sizeof(uint64_t));
+ }
+
+ for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) {
+ struct map_page *page;
+
+ buf = map_alloc(OVERRUN, td[i].data, td[i].len, &page);
+
+ ret = decode_TESTuint64(buf, td[i].len, &u, &sz);
+ if (ret) {
+ if (td[i].ok) {
+ printf("failed with tag len test %d\n", i);
+ printf("ret = %d\n", ret);
+ failed = 1;
+ }
+ } else {
+ if (td[i].ok == 0) {
+ printf("failed with success for tag len test %d\n", i);
+ failed = 1;
+ }
+ if (td[i].expected_len != sz) {
+ printf("wrong expected size for tag test %d\n", i);
+ printf("sz = %lu\n", (unsigned long)sz);
+ failed = 1;
+ }
+ if (values[i] != u) {
+ printf("wrong value for tag test %d\n", i);
+ printf("Expected value: %llu\nActual value: %llu\n",
+ (unsigned long long)values[i], (unsigned long long)u);
+ failed = 1;
+ }
+ }
+ map_free(page, "test", "decode");
+ }
+ return failed;
+}
+
+static int
+check_tag_length64s(void)
+{
+ struct test_data td[] = {
+ { 1, 3, 3, "\x02\x01\x00"},
+ { 1, 7, 7, "\x02\x05\xfe\x00\x00\x00\x01"},
+ { 1, 7, 7, "\x02\x05\xfe\x00\x00\x00\x00"},
+ { 1, 9, 9, "\x02\x07\x80\x00\x00\x00\x00\x00\x01"},
+ { 1, 9, 9, "\x02\x07\x80\x00\x00\x00\x00\x00\x00"},
+ { 1, 10, 10, "\x02\x08\x80\x00\x00\x00\x00\x00\x00\x01"},
+ { 1, 9, 9, "\x02\x07\x80\x00\x00\x00\x00\x00\x01"},
+ { 0, 3, 0, "\x02\x02\x00"},
+ { 0, 3, 0, "\x02\x7f\x7f"},
+ { 0, 4, 0, "\x02\x03\x00\x80"},
+ { 0, 4, 0, "\x02\x7f\x01\x00"},
+ { 0, 5, 0, "\x02\xff\x7f\x02\x00"}
+ };
+ size_t sz;
+ TESTint64 values[] = {0, -8589934591LL, -8589934592LL,
+ -36028797018963967LL, -36028797018963968LL,
+ -9223372036854775807LL, -36028797018963967LL,
+ 0, 127, 128, 256, 512 };
+ TESTint64 u;
+ int i, ret, failed = 0;
+ void *buf;
+
+ for (i = 0; i < sizeof(td)/sizeof(td[0]); i++) {
+ struct map_page *page;
+
+ buf = map_alloc(OVERRUN, td[i].data, td[i].len, &page);
+
+ ret = decode_TESTint64(buf, td[i].len, &u, &sz);
+ if (ret) {
+ if (td[i].ok) {
+ printf("failed with tag len test %d\n", i);
+ printf("ret = %d\n", ret);
+ failed = 1;
+ }
+ } else {
+ if (td[i].ok == 0) {
+ printf("failed with success for tag len test %d\n", i);
+ failed = 1;
+ }
+ if (td[i].expected_len != sz) {
+ printf("wrong expected size for tag test %d\n", i);
+ printf("sz = %lu\n", (unsigned long)sz);
+ failed = 1;
+ }
+ if (values[i] != u) {
+ printf("wrong value for tag test %d\n", i);
+ printf("Expected value: %lld\nActual value: %lld\n",
+ (long long)values[i], (long long)u);
+ failed = 1;
+ }
+ }
+ map_free(page, "test", "decode");
+ }
+ return failed;
+}
+
+static int
cmp_TESTChoice (void *a, void *b)
{
return 0;
@@ -818,6 +1015,7 @@ test_choice (void)
return ret;
}
+#ifdef IMPLICIT_TAGGING_WORKS
static int
cmp_TESTImplicit (void *a, void *b)
{
@@ -829,27 +1027,30 @@ cmp_TESTImplicit (void *a, void *b)
COMPARE_INTEGER(aa,ab,ti3);
return 0;
}
+#endif
/*
UNIV CONS Sequence 14
CONTEXT PRIM 0 1 00
CONTEXT CONS 1 6
- CONTEXT CONS 127 3
- UNIV PRIM Integer 1 02
+ CONTEXT CONS 127 3
+ UNIV PRIM Integer 1 02
CONTEXT PRIM 2 1 03
*/
static int
test_implicit (void)
{
+ int ret = 0;
+#ifdef IMPLICIT_TAGGING_WORKS
struct test_case tests[] = {
- { NULL, 16,
- "\x30\x0e\x80\x01\x00\xa1\x06\xbf"
- "\x7f\x03\x02\x01\x02\x82\x01\x03",
+ { NULL, 18,
+ "\x30\x10\x80\x01\x00\xa1\x06\xbf"
+ "\x7f\x03\x02\x01\x02\xa2\x03\x84\x01\x03",
"implicit 1" }
};
- int ret = 0, ntests = sizeof(tests) / sizeof(*tests);
+ int ntests = sizeof(tests) / sizeof(*tests);
TESTImplicit c0;
memset(&c0, 0, sizeof(c0));
@@ -866,7 +1067,6 @@ test_implicit (void)
cmp_TESTImplicit,
(generic_copy)copy_TESTImplicit);
-#ifdef IMPLICIT_TAGGING_WORKS
ret += generic_test (tests, ntests, sizeof(TESTImplicit2),
(generic_encode)encode_TESTImplicit2,
(generic_length)length_TESTImplicit2,
@@ -892,7 +1092,7 @@ cmp_TESTAlloc (void *a, void *b)
COMPARE_INTEGER(aa,ab,three);
IF_OPT_COMPARE(aa,ab,tagless2) {
- COMPARE_OPT_OCTECT_STRING(aa, ab, tagless2);
+ COMPARE_OPT_OCTET_STRING(aa, ab, tagless2);
}
return 0;
@@ -1059,7 +1259,7 @@ check_fail_largetag(void)
{NULL, 0, "", "empty buffer"},
{NULL, 7, "\x30\x05\xa1\x03\x02\x02\x01",
"one too short" },
- {NULL, 7, "\x30\x04\xa1\x03\x02\x02\x01"
+ {NULL, 7, "\x30\x04\xa1\x03\x02\x02\x01",
"two too short" },
{NULL, 7, "\x30\x03\xa1\x03\x02\x02\x01",
"three too short" },
@@ -1094,7 +1294,7 @@ check_fail_sequence(void)
{NULL, 0, "", "empty buffer"},
{NULL, 24,
"\x30\x16\xa0\x03\x02\x01\x01\xa1\x08\x30\x06\xbf\x7f\x03\x02\x01\x01"
- "\x02\x01\x01\xa2\x03\x02\x01\x01"
+ "\x02\x01\x01\xa2\x03\x02\x01\x01",
"missing one byte from the end, internal length ok"},
{NULL, 25,
"\x30\x18\xa0\x03\x02\x01\x01\xa1\x08\x30\x06\xbf\x7f\x03\x02\x01\x01"
@@ -1129,6 +1329,30 @@ check_fail_choice(void)
}
static int
+check_fail_Ticket(void)
+{
+ char buf[100];
+ size_t i;
+ int ret;
+ struct test_case test;
+ Ticket ticket;
+
+ for (i = 0; i < sizeof(buf); i++) {
+ memset(buf, 0, sizeof(buf));
+ memset(&ticket, 0, sizeof(ticket));
+ test.val = &ticket;
+ test.byte_len = i;
+ test.bytes = buf;
+ test.name = "zero life";
+ ret = generic_decode_fail(&test, 1, sizeof(Ticket),
+ (generic_decode)decode_Ticket);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+static int
check_seq(void)
{
TESTSeqOf seq;
@@ -1276,6 +1500,276 @@ check_TESTMechTypeList(void)
return 0;
}
+#ifdef IMPLICIT_TAGGING_WORKS
+static int
+cmp_TESTSeqOf4(void *a, void *b)
+{
+ TESTSeqOf4 *aa = a;
+ TESTSeqOf4 *ab = b;
+ int i;
+
+ IF_OPT_COMPARE(aa, ab, b1) {
+ COMPARE_INTEGER(aa->b1, ab->b1, len);
+ for (i = 0; i < aa->b1->len; ++i) {
+ COMPARE_INTEGER(aa->b1->val+i, ab->b1->val+i, u1);
+ COMPARE_INTEGER(aa->b1->val+i, ab->b1->val+i, u2);
+ COMPARE_OCTET_STRING(aa->b1->val+i, ab->b1->val+i, s1);
+ COMPARE_OCTET_STRING(aa->b1->val+i, ab->b1->val+i, s2);
+ }
+ }
+ IF_OPT_COMPARE(aa, ab, b2) {
+ COMPARE_INTEGER(aa->b2, ab->b2, len);
+ for (i = 0; i < aa->b2->len; ++i) {
+ COMPARE_INTEGER(aa->b2->val+i, ab->b2->val+i, u1);
+ COMPARE_INTEGER(aa->b2->val+i, ab->b2->val+i, u2);
+ COMPARE_INTEGER(aa->b2->val+i, ab->b2->val+i, u3);
+ COMPARE_OCTET_STRING(aa->b2->val+i, ab->b2->val+i, s1);
+ COMPARE_OCTET_STRING(aa->b2->val+i, ab->b2->val+i, s2);
+ COMPARE_OCTET_STRING(aa->b2->val+i, ab->b2->val+i, s3);
+ }
+ }
+ IF_OPT_COMPARE(aa, ab, b3) {
+ COMPARE_INTEGER(aa->b3, ab->b3, len);
+ for (i = 0; i < aa->b3->len; ++i) {
+ COMPARE_INTEGER(aa->b3->val+i, ab->b3->val+i, u1);
+ COMPARE_INTEGER(aa->b3->val+i, ab->b3->val+i, u2);
+ COMPARE_INTEGER(aa->b3->val+i, ab->b3->val+i, u3);
+ COMPARE_INTEGER(aa->b3->val+i, ab->b3->val+i, u4);
+ COMPARE_OCTET_STRING(aa->b3->val+i, ab->b3->val+i, s1);
+ COMPARE_OCTET_STRING(aa->b3->val+i, ab->b3->val+i, s2);
+ COMPARE_OCTET_STRING(aa->b3->val+i, ab->b3->val+i, s3);
+ COMPARE_OCTET_STRING(aa->b3->val+i, ab->b3->val+i, s4);
+ }
+ }
+ return 0;
+}
+#endif /* IMPLICIT_TAGGING_WORKS */
+
+static int
+test_seq4 (void)
+{
+ int ret = 0;
+#ifdef IMPLICIT_TAGGING_WORKS
+ struct test_case tests[] = {
+ { NULL, 2,
+ "\x30\x00",
+ "seq4 0" },
+ { NULL, 4,
+ "\x30\x02" "\xa1\x00",
+ "seq4 1" },
+ { NULL, 8,
+ "\x30\x06" "\xa0\x02\x30\x00" "\xa1\x00",
+ "seq4 2" },
+ { NULL, 2 + (2 + 0x18) + (2 + 0x27) + (2 + 0x31),
+ "\x30\x76" /* 2 SEQ */
+ "\xa0\x18\x30\x16" /* 4 [0] SEQ */
+ "\x30\x14" /* 2 SEQ */
+ "\x04\x00" /* 2 OCTET-STRING */
+ "\x04\x02\x01\x02" /* 4 OCTET-STRING */
+ "\x02\x01\x01" /* 3 INT */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff"
+ /* 11 INT */
+ "\xa1\x27" /* 2 [1] IMPL SEQ */
+ "\x30\x25" /* 2 SEQ */
+ "\x02\x01\x01" /* 3 INT */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff"
+ /* 11 INT */
+ "\x02\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00"
+ /* 11 INT */
+ "\x04\x00" /* 2 OCTET-STRING */
+ "\x04\x02\x01\x02" /* 4 OCTET-STRING */
+ "\x04\x04\x00\x01\x02\x03" /* 6 OCTET-STRING */
+ "\xa2\x31" /* 2 [2] IMPL SEQ */
+ "\x30\x2f" /* 2 SEQ */
+ "\x04\x00" /* 2 OCTET-STRING */
+ "\x02\x01\x01" /* 3 INT */
+ "\x04\x02\x01\x02" /* 4 OCTET-STRING */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff"
+ /* 11 INT */
+ "\x04\x04\x00\x01\x02\x03" /* 6 OCTET-STRING */
+ "\x02\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00"
+ /* 11 INT */
+ "\x04\x01\x00" /* 3 OCTET-STRING */
+ "\x02\x05\x01\x00\x00\x00\x00", /* 7 INT */
+ "seq4 3" },
+ };
+
+ int ntests = sizeof(tests) / sizeof(*tests);
+ TESTSeqOf4 c[4];
+ struct TESTSeqOf4_b1 b1[4];
+ struct TESTSeqOf4_b2 b2[4];
+ struct TESTSeqOf4_b3 b3[4];
+ struct TESTSeqOf4_b1_val b1val[4];
+ struct TESTSeqOf4_b2_val b2val[4];
+ struct TESTSeqOf4_b3_val b3val[4];
+
+ c[0].b1 = NULL;
+ c[0].b2 = NULL;
+ c[0].b3 = NULL;
+ tests[0].val = &c[0];
+
+ b2[1].len = 0;
+ b2[1].val = NULL;
+ c[1].b1 = NULL;
+ c[1].b2 = &b2[1];
+ c[1].b3 = NULL;
+ tests[1].val = &c[1];
+
+ b1[2].len = 0;
+ b1[2].val = NULL;
+ b2[2].len = 0;
+ b2[2].val = NULL;
+ c[2].b1 = &b1[2];
+ c[2].b2 = &b2[2];
+ c[2].b3 = NULL;
+ tests[2].val = &c[2];
+
+ b1val[3].s1.data = "";
+ b1val[3].s1.length = 0;
+ b1val[3].u1 = 1LL;
+ b1val[3].s2.data = "\x01\x02";
+ b1val[3].s2.length = 2;
+ b1val[3].u2 = -1LL;
+
+ b2val[3].s1.data = "";
+ b2val[3].s1.length = 0;
+ b2val[3].u1 = 1LL;
+ b2val[3].s2.data = "\x01\x02";
+ b2val[3].s2.length = 2;
+ b2val[3].u2 = -1LL;
+ b2val[3].s3.data = "\x00\x01\x02\x03";
+ b2val[3].s3.length = 4;
+ b2val[3].u3 = 1LL<<63;
+
+ b3val[3].s1.data = "";
+ b3val[3].s1.length = 0;
+ b3val[3].u1 = 1LL;
+ b3val[3].s2.data = "\x01\x02";
+ b3val[3].s2.length = 2;
+ b3val[3].u2 = -1LL;
+ b3val[3].s3.data = "\x00\x01\x02\x03";
+ b3val[3].s3.length = 4;
+ b3val[3].u3 = 1LL<<63;
+ b3val[3].s4.data = "\x00";
+ b3val[3].s4.length = 1;
+ b3val[3].u4 = 1LL<<32;
+
+ b1[3].len = 1;
+ b1[3].val = &b1val[3];
+ b2[3].len = 1;
+ b2[3].val = &b2val[3];
+ b3[3].len = 1;
+ b3[3].val = &b3val[3];
+ c[3].b1 = &b1[3];
+ c[3].b2 = &b2[3];
+ c[3].b3 = &b3[3];
+ tests[3].val = &c[3];
+
+ ret += generic_test (tests, ntests, sizeof(TESTSeqOf4),
+ (generic_encode)encode_TESTSeqOf4,
+ (generic_length)length_TESTSeqOf4,
+ (generic_decode)decode_TESTSeqOf4,
+ (generic_free)free_TESTSeqOf4,
+ cmp_TESTSeqOf4,
+ (generic_copy)copy_TESTSeqOf4);
+#endif /* IMPLICIT_TAGGING_WORKS */
+ return ret;
+}
+
+static int
+cmp_test_seqof5 (void *a, void *b)
+{
+ TESTSeqOf5 *aval = a;
+ TESTSeqOf5 *bval = b;
+
+ IF_OPT_COMPARE(aval, bval, outer) {
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u0);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s0);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u1);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s1);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u2);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s2);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u3);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s3);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u4);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s4);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u5);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s5);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u6);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s6);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u7);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s7);
+ }
+ return 0;
+}
+
+static int
+test_seqof5(void)
+{
+ struct test_case tests[] = {
+ { NULL, 2, "\x30\x00", "seq5 0" },
+ { NULL, 126,
+ "\x30\x7c" /* SEQ */
+ "\x30\x7a" /* SEQ */
+ "\x30\x78" /* SEQ */
+ "\x02\x01\x01" /* INT 1 */
+ "\x04\x06\x01\x01\x01\x01\x01\x01" /* "\0x1"x6 */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfe" /* INT ~1 */
+ "\x04\x06\x02\x02\x02\x02\x02\x02" /* "\x02"x6 */
+ "\x02\x01\x02" /* INT 2 */
+ "\x04\x06\x03\x03\x03\x03\x03\x03" /* "\x03"x6 */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfd" /* INT ~2 */
+ "\x04\x06\x04\x04\x04\x04\x04\x04" /* ... */
+ "\x02\x01\x03"
+ "\x04\x06\x05\x05\x05\x05\x05\x05"
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfc"
+ "\x04\x06\x06\x06\x06\x06\x06\x06"
+ "\x02\x01\x04"
+ "\x04\x06\x07\x07\x07\x07\x07\x07"
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfb"
+ "\x04\x06\x08\x08\x08\x08\x08\x08",
+ "seq5 1" },
+ };
+
+ int ret = 0, ntests = sizeof(tests) / sizeof(*tests);
+ TESTSeqOf5 c[2];
+ struct TESTSeqOf5_outer outer;
+ struct TESTSeqOf5_outer_inner inner;
+ TESTuint64 u[8];
+ heim_octet_string s[8];
+ int i;
+
+ c[0].outer = NULL;
+ tests[0].val = &c[0];
+
+ for (i = 0; i < 8; ++i) {
+ u[i] = (i&1) == 0 ? i/2+1 : ~(i/2+1);
+ s[i].data = memset(malloc(s[i].length = 6), i+1, 6);
+ }
+
+ inner.u0 = u[0]; inner.u1 = u[1]; inner.u2 = u[2]; inner.u3 = u[3];
+ inner.u4 = u[4]; inner.u5 = u[5]; inner.u6 = u[6]; inner.u7 = u[7];
+ inner.s0 = s[0]; inner.s1 = s[1]; inner.s2 = s[2]; inner.s3 = s[3];
+ inner.s4 = s[4]; inner.s5 = s[5]; inner.s6 = s[6]; inner.s7 = s[7];
+
+ outer.inner = inner;
+ c[1].outer = &outer;
+ tests[1].val = &c[1];
+
+ ret += generic_test (tests, ntests, sizeof(TESTSeqOf5),
+ (generic_encode)encode_TESTSeqOf5,
+ (generic_length)length_TESTSeqOf5,
+ (generic_decode)decode_TESTSeqOf5,
+ (generic_free)free_TESTSeqOf5,
+ cmp_test_seqof5,
+ NULL);
+
+ for (i = 0; i < 8; ++i)
+ free(s[i].data);
+
+ return ret;
+}
+
int
main(int argc, char **argv)
{
@@ -1291,21 +1785,28 @@ main(int argc, char **argv)
ret += test_cert();
ret += check_tag_length();
+ ret += check_tag_length64();
+ ret += check_tag_length64s();
ret += test_large_tag();
ret += test_choice();
ret += test_implicit();
+
ret += test_taglessalloc();
ret += test_optional();
ret += check_fail_largetag();
ret += check_fail_sequence();
ret += check_fail_choice();
+ ret += check_fail_Ticket();
ret += check_seq();
ret += check_seq_of_size();
+ ret += test_SignedData();
ret += check_TESTMechTypeList();
+ ret += test_seq4();
+ ret += test_seqof5();
return ret;
}
diff --git a/lib/asn1/check-template.c b/lib/asn1/check-template.c
index 44d50168a963..72e42274b3f7 100644
--- a/lib/asn1/check-template.c
+++ b/lib/asn1/check-template.c
@@ -46,6 +46,7 @@
#include <test_asn1.h>
#include "check-common.h"
+#include "der_locl.h"
static int
cmp_dummy (void *a, void *b)
@@ -54,6 +55,44 @@ cmp_dummy (void *a, void *b)
}
static int
+test_uint64(void)
+{
+ struct test_case tests[] = {
+ { NULL, 3, "\x02\x01\x00", "uint64 0" },
+ { NULL, 7, "\x02\x05\x01\xff\xff\xff\xff", "uint64 1" },
+ { NULL, 7, "\x02\x05\x02\x00\x00\x00\x00", "uint64 2" },
+ { NULL, 9, "\x02\x07\x7f\xff\xff\xff\xff\xff\xff", "uint64 3" },
+ { NULL, 10, "\x02\x08\x00\x80\x00\x00\x00\x00\x00\x00", "uint64 4" },
+ { NULL, 10, "\x02\x08\x7f\xff\xff\xff\xff\xff\xff\xff", "uint64 5" },
+ { NULL, 11, "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", "uint64 6" }
+ };
+
+ size_t i;
+ int ret = 0, ntests = sizeof(tests) / sizeof(*tests);
+ TESTuint64 values[] = { 0, 8589934591LL, 8589934592LL,
+ 36028797018963967LL, 36028797018963968LL,
+ 9223372036854775807LL, 18446744073709551615ULL };
+
+ for (i = 0; i < ntests; i++)
+ tests[i].val = &values[i];
+
+ if (sizeof(TESTuint64) != sizeof(uint64_t)) {
+ ret += 1;
+ printf("sizeof(TESTuint64) %d != sizeof(uint64_t) %d\n",
+ (int)sizeof(TESTuint64), (int)sizeof(uint64_t));
+ }
+
+ ret += generic_test (tests, ntests, sizeof(TESTuint64),
+ (generic_encode)encode_TESTuint64,
+ (generic_length)length_TESTuint64,
+ (generic_decode)decode_TESTuint64,
+ (generic_free)free_TESTuint64,
+ cmp_dummy,
+ NULL);
+ return ret;
+}
+
+static int
test_seqofseq(void)
{
struct test_case tests[] = {
@@ -241,15 +280,240 @@ test_seqof3(void)
}
+static int
+test_seqof4(void)
+{
+ struct test_case tests[] = {
+ { NULL, 2,
+ "\x30\x00",
+ "seq4 0" },
+ { NULL, 4,
+ "\x30\x02" "\xa1\x00",
+ "seq4 1" },
+ { NULL, 8,
+ "\x30\x06" "\xa0\x02\x30\x00" "\xa1\x00",
+ "seq4 2" },
+ { NULL, 2 + (2 + 0x18) + (2 + 0x27) + (2 + 0x31),
+ "\x30\x76" /* 2 SEQ */
+ "\xa0\x18\x30\x16" /* 4 [0] SEQ */
+ "\x30\x14" /* 2 SEQ */
+ "\x04\x00" /* 2 OCTET-STRING */
+ "\x04\x02\x01\x02" /* 4 OCTET-STRING */
+ "\x02\x01\x01" /* 3 INT */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff"
+ /* 11 INT */
+ "\xa1\x27" /* 2 [1] IMPL SEQ */
+ "\x30\x25" /* 2 SEQ */
+ "\x02\x01\x01" /* 3 INT */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff"
+ /* 11 INT */
+ "\x02\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00"
+ /* 11 INT */
+ "\x04\x00" /* 2 OCTET-STRING */
+ "\x04\x02\x01\x02" /* 4 OCTET-STRING */
+ "\x04\x04\x00\x01\x02\x03" /* 6 OCTET-STRING */
+ "\xa2\x31" /* 2 [2] IMPL SEQ */
+ "\x30\x2f" /* 2 SEQ */
+ "\x04\x00" /* 2 OCTET-STRING */
+ "\x02\x01\x01" /* 3 INT */
+ "\x04\x02\x01\x02" /* 4 OCTET-STRING */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff"
+ /* 11 INT */
+ "\x04\x04\x00\x01\x02\x03" /* 6 OCTET-STRING */
+ "\x02\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00"
+ /* 11 INT */
+ "\x04\x01\x00" /* 3 OCTET-STRING */
+ "\x02\x05\x01\x00\x00\x00\x00", /* 7 INT */
+ "seq4 3" },
+ };
+
+ int ret = 0, ntests = sizeof(tests) / sizeof(*tests);
+ TESTSeqOf4 c[4];
+ struct TESTSeqOf4_b1 b1[4];
+ struct TESTSeqOf4_b2 b2[4];
+ struct TESTSeqOf4_b3 b3[4];
+ struct TESTSeqOf4_b1_val b1val[4];
+ struct TESTSeqOf4_b2_val b2val[4];
+ struct TESTSeqOf4_b3_val b3val[4];
+
+ c[0].b1 = NULL;
+ c[0].b2 = NULL;
+ c[0].b3 = NULL;
+ tests[0].val = &c[0];
+
+ b2[1].len = 0;
+ b2[1].val = NULL;
+ c[1].b1 = NULL;
+ c[1].b2 = &b2[1];
+ c[1].b3 = NULL;
+ tests[1].val = &c[1];
+
+ b1[2].len = 0;
+ b1[2].val = NULL;
+ b2[2].len = 0;
+ b2[2].val = NULL;
+ c[2].b1 = &b1[2];
+ c[2].b2 = &b2[2];
+ c[2].b3 = NULL;
+ tests[2].val = &c[2];
+
+ b1val[3].s1.data = "";
+ b1val[3].s1.length = 0;
+ b1val[3].u1 = 1LL;
+ b1val[3].s2.data = "\x01\x02";
+ b1val[3].s2.length = 2;
+ b1val[3].u2 = -1LL;
+
+ b2val[3].s1.data = "";
+ b2val[3].s1.length = 0;
+ b2val[3].u1 = 1LL;
+ b2val[3].s2.data = "\x01\x02";
+ b2val[3].s2.length = 2;
+ b2val[3].u2 = -1LL;
+ b2val[3].s3.data = "\x00\x01\x02\x03";
+ b2val[3].s3.length = 4;
+ b2val[3].u3 = 1LL<<63;
+
+ b3val[3].s1.data = "";
+ b3val[3].s1.length = 0;
+ b3val[3].u1 = 1LL;
+ b3val[3].s2.data = "\x01\x02";
+ b3val[3].s2.length = 2;
+ b3val[3].u2 = -1LL;
+ b3val[3].s3.data = "\x00\x01\x02\x03";
+ b3val[3].s3.length = 4;
+ b3val[3].u3 = 1LL<<63;
+ b3val[3].s4.data = "\x00";
+ b3val[3].s4.length = 1;
+ b3val[3].u4 = 1LL<<32;
+
+ b1[3].len = 1;
+ b1[3].val = &b1val[3];
+ b2[3].len = 1;
+ b2[3].val = &b2val[3];
+ b3[3].len = 1;
+ b3[3].val = &b3val[3];
+ c[3].b1 = &b1[3];
+ c[3].b2 = &b2[3];
+ c[3].b3 = &b3[3];
+ tests[3].val = &c[3];
+
+ ret += generic_test (tests, ntests, sizeof(TESTSeqOf4),
+ (generic_encode)encode_TESTSeqOf4,
+ (generic_length)length_TESTSeqOf4,
+ (generic_decode)decode_TESTSeqOf4,
+ (generic_free)free_TESTSeqOf4,
+ cmp_dummy,
+ NULL);
+ return ret;
+}
+
+static int
+cmp_test_seqof5 (void *a, void *b)
+{
+ TESTSeqOf5 *aval = a;
+ TESTSeqOf5 *bval = b;
+
+ IF_OPT_COMPARE(aval, bval, outer) {
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u0);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s0);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u1);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s1);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u2);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s2);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u3);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s3);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u4);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s4);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u5);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s5);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u6);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s6);
+ COMPARE_INTEGER(&aval->outer->inner, &bval->outer->inner, u7);
+ COMPARE_OCTET_STRING(&aval->outer->inner, &bval->outer->inner, s7);
+ }
+ return 0;
+}
+
+static int
+test_seqof5(void)
+{
+ struct test_case tests[] = {
+ { NULL, 2, "\x30\x00", "seq5 0" },
+ { NULL, 126,
+ "\x30\x7c" /* SEQ */
+ "\x30\x7a" /* SEQ */
+ "\x30\x78" /* SEQ */
+ "\x02\x01\x01" /* INT 1 */
+ "\x04\x06\x01\x01\x01\x01\x01\x01" /* "\0x1"x6 */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfe" /* INT ~1 */
+ "\x04\x06\x02\x02\x02\x02\x02\x02" /* "\x02"x6 */
+ "\x02\x01\x02" /* INT 2 */
+ "\x04\x06\x03\x03\x03\x03\x03\x03" /* "\x03"x6 */
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfd" /* INT ~2 */
+ "\x04\x06\x04\x04\x04\x04\x04\x04" /* ... */
+ "\x02\x01\x03"
+ "\x04\x06\x05\x05\x05\x05\x05\x05"
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfc"
+ "\x04\x06\x06\x06\x06\x06\x06\x06"
+ "\x02\x01\x04"
+ "\x04\x06\x07\x07\x07\x07\x07\x07"
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xfb"
+ "\x04\x06\x08\x08\x08\x08\x08\x08",
+ "seq5 1" },
+ };
+
+ int ret = 0, ntests = sizeof(tests) / sizeof(*tests);
+ TESTSeqOf5 c[2];
+ struct TESTSeqOf5_outer outer;
+ struct TESTSeqOf5_outer_inner inner;
+ TESTuint64 u[8];
+ heim_octet_string s[8];
+ int i;
+
+ c[0].outer = NULL;
+ tests[0].val = &c[0];
+
+ for (i = 0; i < 8; ++i) {
+ u[i] = (i&1) == 0 ? i/2+1 : ~(i/2+1);
+ s[i].data = memset(malloc(s[i].length = 6), i+1, 6);
+ }
+
+ inner.u0 = u[0]; inner.u1 = u[1]; inner.u2 = u[2]; inner.u3 = u[3];
+ inner.u4 = u[4]; inner.u5 = u[5]; inner.u6 = u[6]; inner.u7 = u[7];
+ inner.s0 = s[0]; inner.s1 = s[1]; inner.s2 = s[2]; inner.s3 = s[3];
+ inner.s4 = s[4]; inner.s5 = s[5]; inner.s6 = s[6]; inner.s7 = s[7];
+
+ outer.inner = inner;
+ c[1].outer = &outer;
+ tests[1].val = &c[1];
+
+ ret += generic_test (tests, ntests, sizeof(TESTSeqOf5),
+ (generic_encode)encode_TESTSeqOf5,
+ (generic_length)length_TESTSeqOf5,
+ (generic_decode)decode_TESTSeqOf5,
+ (generic_free)free_TESTSeqOf5,
+ cmp_test_seqof5,
+ NULL);
+
+ for (i = 0; i < 8; ++i)
+ free(s[i].data);
+
+ return ret;
+}
+
int
main(int argc, char **argv)
{
int ret = 0;
+ ret += test_uint64();
ret += test_seqofseq();
ret += test_seqofseq2();
ret += test_seqof2();
ret += test_seqof3();
+ ret += test_seqof4();
+ ret += test_seqof5();
return ret;
}
diff --git a/lib/asn1/der-private.h b/lib/asn1/der-private.h
index 555f71bd004d..14866dccea57 100644
--- a/lib/asn1/der-private.h
+++ b/lib/asn1/der-private.h
@@ -5,6 +5,20 @@
#include <stdarg.h>
int
+_asn1_bmember_isset_bit (
+ const void */*data*/,
+ unsigned int /*bit*/,
+ size_t /*size*/);
+
+void
+_asn1_bmember_put_bit (
+ unsigned char */*p*/,
+ const void */*data*/,
+ unsigned int /*bit*/,
+ size_t /*size*/,
+ unsigned int */*bitset*/);
+
+int
_asn1_copy (
const struct asn1_template */*t*/,
const void */*from*/,
@@ -42,16 +56,37 @@ _asn1_encode (
const void */*data*/,
size_t */*size*/);
+int
+_asn1_encode_fuzzer (
+ const struct asn1_template */*t*/,
+ unsigned char */*p*/,
+ size_t /*len*/,
+ const void */*data*/,
+ size_t */*size*/);
+
void
_asn1_free (
const struct asn1_template */*t*/,
void */*data*/);
+void
+_asn1_free_top (
+ const struct asn1_template */*t*/,
+ void */*data*/);
+
size_t
_asn1_length (
const struct asn1_template */*t*/,
const void */*data*/);
+size_t
+_asn1_length_fuzzer (
+ const struct asn1_template */*t*/,
+ const void */*data*/);
+
+size_t
+_asn1_sizeofType (const struct asn1_template */*t*/);
+
struct tm *
_der_gmtime (
time_t /*t*/,
@@ -71,8 +106,14 @@ size_t
_heim_len_int (int /*val*/);
size_t
+_heim_len_int64 (int64_t /*val*/);
+
+size_t
_heim_len_unsigned (unsigned /*val*/);
+size_t
+_heim_len_unsigned64 (uint64_t /*val*/);
+
int
_heim_time2generalizedtime (
time_t /*t*/,
diff --git a/lib/asn1/der-protos.h b/lib/asn1/der-protos.h
index 3b3d81d175fb..9f709411778d 100644
--- a/lib/asn1/der-protos.h
+++ b/lib/asn1/der-protos.h
@@ -1,6 +1,7 @@
/* This is a generated file */
#ifndef __der_protos_h__
#define __der_protos_h__
+#ifndef DOXY
#include <stdarg.h>
@@ -9,6 +10,18 @@ extern "C" {
#endif
int
+asn1_fuzzer_done (void);
+
+int
+asn1_fuzzer_method (const char */*mode*/);
+
+void
+asn1_fuzzer_next (void);
+
+void
+asn1_fuzzer_reset (void);
+
+int
copy_heim_any (
const heim_any */*from*/,
heim_any */*to*/);
@@ -68,6 +81,11 @@ der_copy_integer (
int */*to*/);
int
+der_copy_integer64 (
+ const int64_t */*from*/,
+ int64_t */*to*/);
+
+int
der_copy_octet_string (
const heim_octet_string */*from*/,
heim_octet_string */*to*/);
@@ -93,6 +111,11 @@ der_copy_unsigned (
unsigned */*to*/);
int
+der_copy_unsigned64 (
+ const uint64_t */*from*/,
+ uint64_t */*to*/);
+
+int
der_copy_utctime (
const time_t */*from*/,
time_t */*to*/);
@@ -129,6 +152,9 @@ void
der_free_integer (int */*i*/);
void
+der_free_integer64 (int64_t */*i*/);
+
+void
der_free_octet_string (heim_octet_string */*k*/);
void
@@ -144,6 +170,9 @@ void
der_free_unsigned (unsigned */*u*/);
void
+der_free_unsigned64 (uint64_t */*u*/);
+
+void
der_free_utctime (time_t */*t*/);
void
@@ -215,6 +244,13 @@ der_get_integer (
size_t */*size*/);
int
+der_get_integer64 (
+ const unsigned char */*p*/,
+ size_t /*len*/,
+ int64_t */*ret*/,
+ size_t */*size*/);
+
+int
der_get_length (
const unsigned char */*p*/,
size_t /*len*/,
@@ -253,7 +289,7 @@ int
der_get_tag (
const unsigned char */*p*/,
size_t /*len*/,
- Der_class */*class*/,
+ Der_class */*cls*/,
Der_type */*type*/,
unsigned int */*tag*/,
size_t */*size*/);
@@ -285,6 +321,13 @@ der_get_unsigned (
size_t */*size*/);
int
+der_get_unsigned64 (
+ const unsigned char */*p*/,
+ size_t /*len*/,
+ uint64_t */*ret*/,
+ size_t */*size*/);
+
+int
der_get_utctime (
const unsigned char */*p*/,
size_t /*len*/,
@@ -368,6 +411,9 @@ size_t
der_length_integer (const int */*data*/);
size_t
+der_length_integer64 (const int64_t */*data*/);
+
+size_t
der_length_len (size_t /*len*/);
size_t
@@ -389,6 +435,9 @@ size_t
der_length_unsigned (const unsigned */*data*/);
size_t
+der_length_unsigned64 (const uint64_t */*data*/);
+
+size_t
der_length_utctime (const time_t */*t*/);
size_t
@@ -401,7 +450,7 @@ int
der_match_tag (
const unsigned char */*p*/,
size_t /*len*/,
- Der_class /*class*/,
+ Der_class /*cls*/,
Der_type /*type*/,
unsigned int /*tag*/,
size_t */*size*/);
@@ -410,7 +459,7 @@ int
der_match_tag2 (
const unsigned char */*p*/,
size_t /*len*/,
- Der_class /*class*/,
+ Der_class /*cls*/,
Der_type */*type*/,
unsigned int /*tag*/,
size_t */*size*/);
@@ -419,7 +468,7 @@ int
der_match_tag_and_length (
const unsigned char */*p*/,
size_t /*len*/,
- Der_class /*class*/,
+ Der_class /*cls*/,
Der_type */*type*/,
unsigned int /*tag*/,
size_t */*length_ret*/,
@@ -509,6 +558,13 @@ der_put_integer (
size_t */*size*/);
int
+der_put_integer64 (
+ unsigned char */*p*/,
+ size_t /*len*/,
+ const int64_t */*v*/,
+ size_t */*size*/);
+
+int
der_put_length (
unsigned char */*p*/,
size_t /*len*/,
@@ -570,6 +626,13 @@ der_put_unsigned (
size_t */*size*/);
int
+der_put_unsigned64 (
+ unsigned char */*p*/,
+ size_t /*len*/,
+ const uint64_t */*v*/,
+ size_t */*size*/);
+
+int
der_put_utctime (
unsigned char */*p*/,
size_t /*len*/,
@@ -625,4 +688,5 @@ length_heim_any_set (const heim_any */*data*/);
}
#endif
+#endif /* DOXY */
#endif /* __der_protos_h__ */
diff --git a/lib/asn1/der.h b/lib/asn1/der.h
index f20cdb83ca51..f7323486454d 100644
--- a/lib/asn1/der.h
+++ b/lib/asn1/der.h
@@ -36,6 +36,8 @@
#ifndef __DER_H__
#define __DER_H__
+#include <stdint.h>
+
typedef enum {
ASN1_C_UNIV = 0,
ASN1_C_APPL = 1,
diff --git a/lib/asn1/der_cmp.c b/lib/asn1/der_cmp.c
index 468ccb2d040c..f4612c469f60 100644
--- a/lib/asn1/der_cmp.c
+++ b/lib/asn1/der_cmp.c
@@ -37,7 +37,7 @@ int
der_heim_oid_cmp(const heim_oid *p, const heim_oid *q)
{
if (p->length != q->length)
- return p->length - q->length;
+ return (int)(p->length - q->length);
return memcmp(p->components,
q->components,
p->length * sizeof(*p->components));
@@ -48,7 +48,7 @@ der_heim_octet_string_cmp(const heim_octet_string *p,
const heim_octet_string *q)
{
if (p->length != q->length)
- return p->length - q->length;
+ return (int)(p->length - q->length);
return memcmp(p->data, q->data, p->length);
}
@@ -70,12 +70,13 @@ int
der_heim_bit_string_cmp(const heim_bit_string *p,
const heim_bit_string *q)
{
- int i, r1, r2;
+ int r1, r2;
+ size_t i;
if (p->length != q->length)
- return p->length - q->length;
+ return (int)(p->length - q->length);
i = memcmp(p->data, q->data, p->length / 8);
if (i)
- return i;
+ return (int)i;
if ((p->length % 8) == 0)
return 0;
i = (p->length / 8);
@@ -94,7 +95,7 @@ der_heim_integer_cmp(const heim_integer *p,
if (p->negative != q->negative)
return q->negative - p->negative;
if (p->length != q->length)
- return p->length - q->length;
+ return (int)(p->length - q->length);
return memcmp(p->data, q->data, p->length);
}
@@ -102,7 +103,7 @@ int
der_heim_bmp_string_cmp(const heim_bmp_string *p, const heim_bmp_string *q)
{
if (p->length != q->length)
- return p->length - q->length;
+ return (int)(p->length - q->length);
return memcmp(p->data, q->data, q->length * sizeof(q->data[0]));
}
@@ -111,6 +112,6 @@ der_heim_universal_string_cmp(const heim_universal_string *p,
const heim_universal_string *q)
{
if (p->length != q->length)
- return p->length - q->length;
+ return (int)(p->length - q->length);
return memcmp(p->data, q->data, q->length * sizeof(q->data[0]));
}
diff --git a/lib/asn1/der_copy.c b/lib/asn1/der_copy.c
index 3a0a8c5ffa6a..87f1a0d5ac81 100644
--- a/lib/asn1/der_copy.c
+++ b/lib/asn1/der_copy.c
@@ -55,6 +55,13 @@ der_copy_integer (const int *from, int *to)
}
int
+der_copy_integer64 (const int64_t *from, int64_t *to)
+{
+ *to = *from;
+ return 0;
+}
+
+int
der_copy_unsigned (const unsigned *from, unsigned *to)
{
*to = *from;
@@ -62,6 +69,13 @@ der_copy_unsigned (const unsigned *from, unsigned *to)
}
int
+der_copy_unsigned64 (const uint64_t *from, uint64_t *to)
+{
+ *to = *from;
+ return 0;
+}
+
+int
der_copy_generalized_time (const time_t *from, time_t *to)
{
*to = *from;
diff --git a/lib/asn1/der_format.c b/lib/asn1/der_format.c
index 4f06c1b01fd9..0aaabed87ed8 100644
--- a/lib/asn1/der_format.c
+++ b/lib/asn1/der_format.c
@@ -163,7 +163,7 @@ der_parse_heim_oid (const char *str, const char *sep, heim_oid *data)
free(s);
return EINVAL;
}
- data->components[data->length++] = l;
+ data->components[data->length++] = (unsigned int)l;
}
free(s);
return 0;
diff --git a/lib/asn1/der_free.c b/lib/asn1/der_free.c
index 4bae5fc2338f..1584cfcb299e 100644
--- a/lib/asn1/der_free.c
+++ b/lib/asn1/der_free.c
@@ -51,12 +51,24 @@ der_free_integer (int *i)
}
void
+der_free_integer64 (int64_t *i)
+{
+ *i = 0;
+}
+
+void
der_free_unsigned (unsigned *u)
{
*u = 0;
}
void
+der_free_unsigned64 (uint64_t *u)
+{
+ *u = 0;
+}
+
+void
der_free_generalized_time(time_t *t)
{
*t = 0;
diff --git a/lib/asn1/der_get.c b/lib/asn1/der_get.c
index 3112da86f93b..f0495f33e498 100644
--- a/lib/asn1/der_get.c
+++ b/lib/asn1/der_get.c
@@ -48,9 +48,28 @@ der_get_unsigned (const unsigned char *p, size_t len,
unsigned val = 0;
size_t oldlen = len;
- if (len == sizeof(unsigned) + 1 && p[0] == 0)
+ if (len == sizeof(val) + 1 && p[0] == 0)
;
- else if (len > sizeof(unsigned))
+ else if (len > sizeof(val))
+ return ASN1_OVERRUN;
+
+ while (len--)
+ val = val * 256 + *p++;
+ *ret = val;
+ if(size) *size = oldlen;
+ return 0;
+}
+
+int
+der_get_unsigned64 (const unsigned char *p, size_t len,
+ uint64_t *ret, size_t *size)
+{
+ uint64_t val = 0;
+ size_t oldlen = len;
+
+ if (len == sizeof(val) + 1 && p[0] == 0)
+ ;
+ else if (len > sizeof(val))
return ASN1_OVERRUN;
while (len--)
@@ -67,7 +86,7 @@ der_get_integer (const unsigned char *p, size_t len,
int val = 0;
size_t oldlen = len;
- if (len > sizeof(int))
+ if (len > sizeof(val))
return ASN1_OVERRUN;
if (len > 0) {
@@ -81,6 +100,27 @@ der_get_integer (const unsigned char *p, size_t len,
}
int
+der_get_integer64 (const unsigned char *p, size_t len,
+ int64_t *ret, size_t *size)
+{
+ int64_t val = 0;
+ size_t oldlen = len;
+
+ if (len > sizeof(val))
+ return ASN1_OVERRUN;
+
+ if (len > 0) {
+ val = (signed char)*p++;
+ while (--len)
+ val = val * 256 + *p++;
+ }
+ *ret = val;
+ if(size) *size = oldlen;
+ return 0;
+}
+
+
+int
der_get_length (const unsigned char *p, size_t len,
size_t *val, size_t *size)
{
@@ -143,18 +183,22 @@ der_get_general_string (const unsigned char *p, size_t len,
*/
while ((size_t)(p1 - p) < len && *p1 == '\0')
p1++;
- if ((size_t)(p1 - p) != len)
+ if ((size_t)(p1 - p) != len) {
+ *str = NULL;
return ASN1_BAD_CHARACTER;
+ }
}
- if (len > len + 1)
+ if (len == SIZE_MAX) {
+ *str = NULL;
return ASN1_BAD_LENGTH;
+ }
- s = malloc (len + 1);
+ *str = s = malloc (len + 1);
if (s == NULL)
return ENOMEM;
memcpy (s, p, len);
s[len] = '\0';
- *str = s;
+
if(size) *size = len;
return 0;
}
@@ -166,14 +210,23 @@ der_get_utf8string (const unsigned char *p, size_t len,
return der_get_general_string(p, len, str, size);
}
+#define gen_data_zero(_data) \
+ do { (_data)->length = 0; (_data)->data = NULL; } while(0)
+
int
der_get_printable_string(const unsigned char *p, size_t len,
heim_printable_string *str, size_t *size)
{
+ if (len == SIZE_MAX) {
+ gen_data_zero(str);
+ return ASN1_BAD_LENGTH;
+ }
str->length = len;
str->data = malloc(len + 1);
- if (str->data == NULL)
+ if (str->data == NULL) {
+ gen_data_zero(str);
return ENOMEM;
+ }
memcpy(str->data, p, len);
((char *)str->data)[len] = '\0';
if(size) *size = len;
@@ -193,14 +246,20 @@ der_get_bmp_string (const unsigned char *p, size_t len,
{
size_t i;
- if (len & 1)
+ if (len & 1) {
+ gen_data_zero(data);
return ASN1_BAD_FORMAT;
+ }
data->length = len / 2;
- if (data->length > UINT_MAX/sizeof(data->data[0]))
+ if (data->length > UINT_MAX/sizeof(data->data[0])) {
+ gen_data_zero(data);
return ERANGE;
+ }
data->data = malloc(data->length * sizeof(data->data[0]));
- if (data->data == NULL && data->length != 0)
+ if (data->data == NULL && data->length != 0) {
+ gen_data_zero(data);
return ENOMEM;
+ }
for (i = 0; i < data->length; i++) {
data->data[i] = (p[0] << 8) | p[1];
@@ -208,8 +267,7 @@ der_get_bmp_string (const unsigned char *p, size_t len,
/* check for NUL in the middle of the string */
if (data->data[i] == 0 && i != (data->length - 1)) {
free(data->data);
- data->data = NULL;
- data->length = 0;
+ gen_data_zero(data);
return ASN1_BAD_CHARACTER;
}
}
@@ -224,14 +282,20 @@ der_get_universal_string (const unsigned char *p, size_t len,
{
size_t i;
- if (len & 3)
+ if (len & 3) {
+ gen_data_zero(data);
return ASN1_BAD_FORMAT;
+ }
data->length = len / 4;
- if (data->length > UINT_MAX/sizeof(data->data[0]))
+ if (data->length > UINT_MAX/sizeof(data->data[0])) {
+ gen_data_zero(data);
return ERANGE;
+ }
data->data = malloc(data->length * sizeof(data->data[0]));
- if (data->data == NULL && data->length != 0)
+ if (data->data == NULL && data->length != 0) {
+ gen_data_zero(data);
return ENOMEM;
+ }
for (i = 0; i < data->length; i++) {
data->data[i] = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
@@ -239,8 +303,7 @@ der_get_universal_string (const unsigned char *p, size_t len,
/* check for NUL in the middle of the string */
if (data->data[i] == 0 && i != (data->length - 1)) {
free(data->data);
- data->data = NULL;
- data->length = 0;
+ gen_data_zero(data);
return ASN1_BAD_CHARACTER;
}
}
@@ -274,7 +337,7 @@ der_get_octet_string_ber (const unsigned char *p, size_t len,
{
int e;
Der_type type;
- Der_class class;
+ Der_class cls;
unsigned int tag, depth = 0;
size_t l, datalen, oldlen = len;
@@ -282,9 +345,9 @@ der_get_octet_string_ber (const unsigned char *p, size_t len,
data->data = NULL;
while (len) {
- e = der_get_tag (p, len, &class, &type, &tag, &l);
+ e = der_get_tag (p, len, &cls, &type, &tag, &l);
if (e) goto out;
- if (class != ASN1_C_UNIV) {
+ if (cls != ASN1_C_UNIV) {
e = ASN1_BAD_ID;
goto out;
}
@@ -430,7 +493,7 @@ der_get_time (const unsigned char *p, size_t len,
char *times;
int e;
- if (len > len + 1 || len == 0)
+ if (len == SIZE_MAX || len == 0)
return ASN1_BAD_LENGTH;
times = malloc(len + 1);
@@ -468,7 +531,7 @@ der_get_oid (const unsigned char *p, size_t len,
if (len < 1)
return ASN1_OVERRUN;
- if (len > len + 1)
+ if (len == SIZE_MAX)
return ASN1_BAD_LENGTH;
if (len + 1 > UINT_MAX/sizeof(data->components[0]))
@@ -508,13 +571,13 @@ der_get_oid (const unsigned char *p, size_t len,
int
der_get_tag (const unsigned char *p, size_t len,
- Der_class *class, Der_type *type,
+ Der_class *cls, Der_type *type,
unsigned int *tag, size_t *size)
{
size_t ret = 0;
if (len < 1)
return ASN1_OVERRUN;
- *class = (Der_class)(((*p) >> 6) & 0x03);
+ *cls = (Der_class)(((*p) >> 6) & 0x03);
*type = (Der_type)(((*p) >> 5) & 0x01);
*tag = (*p) & 0x1f;
p++; len--; ret++;
@@ -540,13 +603,13 @@ der_get_tag (const unsigned char *p, size_t len,
int
der_match_tag (const unsigned char *p, size_t len,
- Der_class class, Der_type type,
+ Der_class cls, Der_type type,
unsigned int tag, size_t *size)
{
Der_type thistype;
int e;
- e = der_match_tag2(p, len, class, &thistype, tag, size);
+ e = der_match_tag2(p, len, cls, &thistype, tag, size);
if (e) return e;
if (thistype != type) return ASN1_BAD_ID;
return 0;
@@ -554,7 +617,7 @@ der_match_tag (const unsigned char *p, size_t len,
int
der_match_tag2 (const unsigned char *p, size_t len,
- Der_class class, Der_type *type,
+ Der_class cls, Der_type *type,
unsigned int tag, size_t *size)
{
size_t l;
@@ -564,7 +627,7 @@ der_match_tag2 (const unsigned char *p, size_t len,
e = der_get_tag (p, len, &thisclass, type, &thistag, &l);
if (e) return e;
- if (class != thisclass)
+ if (cls != thisclass)
return ASN1_BAD_ID;
if(tag > thistag)
return ASN1_MISPLACED_FIELD;
@@ -576,13 +639,13 @@ der_match_tag2 (const unsigned char *p, size_t len,
int
der_match_tag_and_length (const unsigned char *p, size_t len,
- Der_class class, Der_type *type, unsigned int tag,
+ Der_class cls, Der_type *type, unsigned int tag,
size_t *length_ret, size_t *size)
{
size_t l, ret = 0;
int e;
- e = der_match_tag2 (p, len, class, type, tag, &l);
+ e = der_match_tag2 (p, len, cls, type, tag, &l);
if (e) return e;
p += l;
len -= l;
@@ -633,14 +696,19 @@ der_get_bit_string (const unsigned char *p, size_t len,
* any of them will cause a interger overrun */
if ((len - 1) >> (sizeof(len) * 8 - 3))
return ASN1_OVERRUN;
- data->length = (len - 1) * 8;
- data->data = malloc(len - 1);
- if (data->data == NULL && (len - 1) != 0)
- return ENOMEM;
- /* copy data is there is data to copy */
- if (len - 1 != 0) {
- memcpy (data->data, p + 1, len - 1);
- data->length -= p[0];
+ /*
+ * If there is data to copy, do that now.
+ */
+ if (len - 1 > 0) {
+ data->length = (len - 1) * 8;
+ data->data = malloc(len - 1);
+ if (data->data == NULL)
+ return ENOMEM;
+ memcpy (data->data, p + 1, len - 1);
+ data->length -= p[0];
+ } else {
+ data->data = NULL;
+ data->length = 0;
}
if(size) *size = len;
return 0;
diff --git a/lib/asn1/der_length.c b/lib/asn1/der_length.c
index db82025861ea..684ba9bc6f32 100644
--- a/lib/asn1/der_length.c
+++ b/lib/asn1/der_length.c
@@ -56,6 +56,24 @@ _heim_len_unsigned (unsigned val)
}
size_t
+_heim_len_unsigned64 (uint64_t val)
+{
+ size_t ret = 0;
+ int last_val_gt_128;
+
+ do {
+ ++ret;
+ last_val_gt_128 = (val >= 128);
+ val /= 256;
+ } while (val);
+
+ if(last_val_gt_128)
+ ret++;
+
+ return ret;
+}
+
+size_t
_heim_len_int (int val)
{
unsigned char q;
@@ -82,6 +100,33 @@ _heim_len_int (int val)
return ret;
}
+size_t
+_heim_len_int64 (int64_t val)
+{
+ unsigned char q;
+ size_t ret = 0;
+
+ if (val >= 0) {
+ do {
+ q = val % 256;
+ ret++;
+ val /= 256;
+ } while(val);
+ if(q >= 128)
+ ret++;
+ } else {
+ val = ~val;
+ do {
+ q = ~(val % 256);
+ ret++;
+ val /= 256;
+ } while(val);
+ if(q < 128)
+ ret++;
+ }
+ return ret;
+}
+
static size_t
len_oid (const heim_oid *oid)
{
@@ -135,12 +180,24 @@ der_length_integer (const int *data)
}
size_t
+der_length_integer64 (const int64_t *data)
+{
+ return _heim_len_int64 (*data);
+}
+
+size_t
der_length_unsigned (const unsigned *data)
{
return _heim_len_unsigned(*data);
}
size_t
+der_length_unsigned64 (const uint64_t *data)
+{
+ return _heim_len_unsigned64(*data);
+}
+
+size_t
der_length_enumerated (const unsigned *data)
{
return _heim_len_int (*data);
diff --git a/lib/asn1/der_put.c b/lib/asn1/der_put.c
index 0b276d1ebdce..843d09f5cdc1 100644
--- a/lib/asn1/der_put.c
+++ b/lib/asn1/der_put.c
@@ -76,6 +76,38 @@ der_put_unsigned (unsigned char *p, size_t len, const unsigned *v, size_t *size)
}
int
+der_put_unsigned64 (unsigned char *p, size_t len, const uint64_t *v, size_t *size)
+{
+ unsigned char *base = p;
+ uint64_t val = *v;
+
+ if (val) {
+ while (len > 0 && val) {
+ *p-- = val % 256;
+ val /= 256;
+ --len;
+ }
+ if (val != 0)
+ return ASN1_OVERFLOW;
+ else {
+ if(p[1] >= 128) {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0;
+ }
+ *size = base - p;
+ return 0;
+ }
+ } else if (len < 1)
+ return ASN1_OVERFLOW;
+ else {
+ *p = 0;
+ *size = 1;
+ return 0;
+ }
+}
+
+int
der_put_integer (unsigned char *p, size_t len, const int *v, size_t *size)
{
unsigned char *base = p;
@@ -115,6 +147,46 @@ der_put_integer (unsigned char *p, size_t len, const int *v, size_t *size)
return 0;
}
+int
+der_put_integer64 (unsigned char *p, size_t len, const int64_t *v, size_t *size)
+{
+ unsigned char *base = p;
+ int64_t val = *v;
+
+ if(val >= 0) {
+ do {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = val % 256;
+ len--;
+ val /= 256;
+ } while(val);
+ if(p[1] >= 128) {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0;
+ len--;
+ }
+ } else {
+ val = ~val;
+ do {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = ~(val % 256);
+ len--;
+ val /= 256;
+ } while(val);
+ if(p[1] < 128) {
+ if(len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = 0xff;
+ len--;
+ }
+ }
+ *size = base - p;
+ return 0;
+}
+
int
der_put_length (unsigned char *p, size_t len, size_t val, size_t *size)
@@ -267,7 +339,8 @@ der_put_heim_integer (unsigned char *p, size_t len,
len -= data->length;
if (data->negative) {
- int i, carry;
+ ssize_t i;
+ int carry;
for (i = data->length - 1, carry = 1; i >= 0; i--) {
*p = buf[i] ^ 0xff;
if (carry)
@@ -343,7 +416,7 @@ der_put_oid (unsigned char *p, size_t len,
const heim_oid *data, size_t *size)
{
unsigned char *base = p;
- int n;
+ size_t n;
for (n = data->length - 1; n >= 2; --n) {
unsigned u = data->components[n];
@@ -429,12 +502,14 @@ _heim_time2generalizedtime (time_t t, heim_octet_string *s, int gtimep)
struct tm tm;
const size_t len = gtimep ? 15 : 13;
+ s->data = NULL;
+ s->length = 0;
+ if (_der_gmtime(t, &tm) == NULL)
+ return ASN1_BAD_TIMEFORMAT;
s->data = malloc(len + 1);
if (s->data == NULL)
return ENOMEM;
s->length = len;
- if (_der_gmtime(t, &tm) == NULL)
- return ASN1_BAD_TIMEFORMAT;
if (gtimep)
snprintf (s->data, len + 1, "%04d%02d%02d%02d%02d%02dZ",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
@@ -468,12 +543,12 @@ der_put_bit_string (unsigned char *p, size_t len,
int
_heim_der_set_sort(const void *a1, const void *a2)
{
- const struct heim_octet_string *s1 = a1, *s2 = a2;
+ const heim_octet_string *s1 = a1, *s2 = a2;
int ret;
ret = memcmp(s1->data, s2->data,
s1->length < s2->length ? s1->length : s2->length);
if(ret)
return ret;
- return s1->length - s2->length;
+ return (int)(s1->length - s2->length);
}
diff --git a/lib/asn1/digest.asn1 b/lib/asn1/digest.asn1
index 027402f1efe2..7a73993e3cf5 100644
--- a/lib/asn1/digest.asn1
+++ b/lib/asn1/digest.asn1
@@ -24,7 +24,7 @@ DigestInit ::= SEQUENCE {
}
DigestInitReply ::= SEQUENCE {
- nonce UTF8String, -- service nonce/challange
+ nonce UTF8String, -- service nonce/challenge
opaque UTF8String, -- server state
identifier [0] UTF8String OPTIONAL
}
@@ -78,7 +78,7 @@ NTLMInitReply ::= SEQUENCE {
flags [0] INTEGER (0..4294967295),
opaque [1] OCTET STRING,
targetname [2] UTF8String,
- challange [3] OCTET STRING,
+ challenge [3] OCTET STRING,
targetinfo [4] OCTET STRING OPTIONAL
}
diff --git a/lib/asn1/fuzzer.c b/lib/asn1/fuzzer.c
new file mode 100644
index 000000000000..527f7e99856d
--- /dev/null
+++ b/lib/asn1/fuzzer.c
@@ -0,0 +1,742 @@
+/*
+ * Copyright (c) 2009 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "der_locl.h"
+#include <com_err.h>
+
+enum trigger_method { FOFF, FRANDOM, FLINEAR, FLINEAR_SIZE };
+
+#ifdef ASN1_FUZZER
+static enum trigger_method method = FOFF;
+
+/* FLINEAR */
+static unsigned long fnum, fcur, fsize;
+#endif
+
+int
+asn1_fuzzer_method(const char *mode)
+{
+#ifdef ASN1_FUZZER
+ if (mode == NULL || strcasecmp(mode, "off") == 0) {
+ method = FOFF;
+ } else if (strcasecmp(mode, "random") == 0) {
+ method = FRANDOM;
+ } else if (strcasecmp(mode, "linear") == 0) {
+ method = FLINEAR;
+ } else if (strcasecmp(mode, "linear-size") == 0) {
+ method = FLINEAR_SIZE;
+ } else
+ return 1;
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+void
+asn1_fuzzer_reset(void)
+{
+#ifdef ASN1_FUZZER
+ fcur = 0;
+ fsize = 0;
+ fnum = 0;
+#endif
+}
+
+void
+asn1_fuzzer_next(void)
+{
+#ifdef ASN1_FUZZER
+ fcur = 0;
+ fsize = 0;
+ fnum++;
+#endif
+}
+
+int
+asn1_fuzzer_done(void)
+{
+#ifndef ASN1_FUZZER
+ abort();
+#else
+ /* since code paths */
+ return (fnum > 10000);
+#endif
+}
+
+#ifdef ASN1_FUZZER
+
+static int
+fuzzer_trigger(unsigned int chance)
+{
+ switch(method) {
+ case FOFF:
+ return 0;
+ case FRANDOM:
+ if ((rk_random() % chance) != 1)
+ return 0;
+ return 1;
+ case FLINEAR:
+ if (fnum == fcur++)
+ return 1;
+ return 0;
+ case FLINEAR_SIZE:
+ return 0;
+ }
+ return 0;
+}
+
+static int
+fuzzer_size_trigger(unsigned long *cur)
+{
+ if (method != FLINEAR_SIZE)
+ return 0;
+ if (fnum == (*cur)++)
+ return 1;
+ return 0;
+}
+
+static size_t
+fuzzer_length_len (size_t len)
+{
+ if (fuzzer_size_trigger(&fsize)) {
+ len = 0;
+ } else if (fuzzer_size_trigger(&fsize)) {
+ len = 129;
+ } else if (fuzzer_size_trigger(&fsize)) {
+ len = 0xffff;
+ }
+
+ if (len < 128)
+ return 1;
+ else {
+ int ret = 0;
+ do {
+ ++ret;
+ len /= 256;
+ } while (len);
+ return ret + 1;
+ }
+}
+
+static int
+fuzzer_put_length (unsigned char *p, size_t len, size_t val, size_t *size)
+{
+ if (len < 1)
+ return ASN1_OVERFLOW;
+
+ if (fuzzer_size_trigger(&fcur)) {
+ val = 0;
+ } else if (fuzzer_size_trigger(&fcur)) {
+ val = 129;
+ } else if (fuzzer_size_trigger(&fcur)) {
+ val = 0xffff;
+ }
+
+ if (val < 128) {
+ *p = val;
+ *size = 1;
+ } else {
+ size_t l = 0;
+
+ while(val > 0) {
+ if(len < 2)
+ return ASN1_OVERFLOW;
+ *p-- = val % 256;
+ val /= 256;
+ len--;
+ l++;
+ }
+ *p = 0x80 | l;
+ if(size)
+ *size = l + 1;
+ }
+ return 0;
+}
+
+static int
+fuzzer_put_tag (unsigned char *p, size_t len, Der_class class, Der_type type,
+ unsigned int tag, size_t *size)
+{
+ unsigned fcont = 0;
+
+ if (tag <= 30) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ if (fuzzer_trigger(100))
+ *p = MAKE_TAG(class, type, 0x1f);
+ else
+ *p = MAKE_TAG(class, type, tag);
+ *size = 1;
+ } else {
+ size_t ret = 0;
+ unsigned int continuation = 0;
+
+ do {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = tag % 128 | continuation;
+ len--;
+ ret++;
+ tag /= 128;
+ continuation = 0x80;
+ } while(tag > 0);
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ if (fuzzer_trigger(100))
+ *p-- = MAKE_TAG(class, type, 0);
+ else
+ *p-- = MAKE_TAG(class, type, 0x1f);
+ ret++;
+ *size = ret;
+ }
+ return 0;
+}
+
+static int
+fuzzer_put_length_and_tag (unsigned char *p, size_t len, size_t len_val,
+ Der_class class, Der_type type,
+ unsigned int tag, size_t *size)
+{
+ size_t ret = 0;
+ size_t l;
+ int e;
+
+ e = fuzzer_put_length (p, len, len_val, &l);
+ if(e)
+ return e;
+ p -= l;
+ len -= l;
+ ret += l;
+ e = fuzzer_put_tag (p, len, class, type, tag, &l);
+ if(e)
+ return e;
+
+ ret += l;
+ *size = ret;
+ return 0;
+}
+
+static int
+fuzzer_put_general_string (unsigned char *p, size_t len,
+ const heim_general_string *str, size_t *size)
+{
+ size_t slen = strlen(*str);
+
+ if (len < slen)
+ return ASN1_OVERFLOW;
+ p -= slen;
+ if (slen >= 2 && fuzzer_trigger(100)) {
+ memcpy(p+1, *str, slen);
+ memcpy(p+1, "%s", 2);
+ } else if (slen >= 2 && fuzzer_trigger(100)) {
+ memcpy(p+1, *str, slen);
+ memcpy(p+1, "%n", 2);
+ } else if (slen >= 4 && fuzzer_trigger(100)) {
+ memcpy(p+1, *str, slen);
+ memcpy(p+1, "%10n", 4);
+ } else if (slen >= 10 && fuzzer_trigger(100)) {
+ memcpy(p+1, *str, slen);
+ memcpy(p+1, "%n%n%n%n%n", 10);
+ } else if (slen >= 10 && fuzzer_trigger(100)) {
+ memcpy(p+1, *str, slen);
+ memcpy(p+1, "%n%p%s%d%x", 10);
+ } else if (slen >= 7 && fuzzer_trigger(100)) {
+ memcpy(p+1, *str, slen);
+ memcpy(p+1, "%.1024d", 7);
+ } else if (slen >= 7 && fuzzer_trigger(100)) {
+ memcpy(p+1, *str, slen);
+ memcpy(p+1, "%.2049d", 7);
+ } else if (fuzzer_trigger(100)) {
+ memset(p+1, 0, slen);
+ } else if (fuzzer_trigger(100)) {
+ memset(p+1, 0xff, slen);
+ } else if (fuzzer_trigger(100)) {
+ memset(p+1, 'A', slen);
+ } else {
+ memcpy(p+1, *str, slen);
+ }
+ *size = slen;
+ return 0;
+}
+
+
+struct asn1_type_func fuzzerprim[A1T_NUM_ENTRY] = {
+#define fuzel(name, type) { \
+ (asn1_type_encode)fuzzer_put_##name, \
+ (asn1_type_decode)der_get_##name, \
+ (asn1_type_length)der_length_##name, \
+ (asn1_type_copy)der_copy_##name, \
+ (asn1_type_release)der_free_##name, \
+ sizeof(type) \
+ }
+#define el(name, type) { \
+ (asn1_type_encode)der_put_##name, \
+ (asn1_type_decode)der_get_##name, \
+ (asn1_type_length)der_length_##name, \
+ (asn1_type_copy)der_copy_##name, \
+ (asn1_type_release)der_free_##name, \
+ sizeof(type) \
+ }
+#define elber(name, type) { \
+ (asn1_type_encode)der_put_##name, \
+ (asn1_type_decode)der_get_##name##_ber, \
+ (asn1_type_length)der_length_##name, \
+ (asn1_type_copy)der_copy_##name, \
+ (asn1_type_release)der_free_##name, \
+ sizeof(type) \
+ }
+ el(integer, int),
+ el(integer64, int64_t),
+ el(heim_integer, heim_integer),
+ el(integer, int),
+ el(unsigned, unsigned),
+ el(uninteger64, uint64_t),
+ fuzel(general_string, heim_general_string),
+ el(octet_string, heim_octet_string),
+ elber(octet_string, heim_octet_string),
+ el(ia5_string, heim_ia5_string),
+ el(bmp_string, heim_bmp_string),
+ el(universal_string, heim_universal_string),
+ el(printable_string, heim_printable_string),
+ el(visible_string, heim_visible_string),
+ el(utf8string, heim_utf8_string),
+ el(generalized_time, time_t),
+ el(utctime, time_t),
+ el(bit_string, heim_bit_string),
+ { (asn1_type_encode)der_put_boolean, (asn1_type_decode)der_get_boolean,
+ (asn1_type_length)der_length_boolean, (asn1_type_copy)der_copy_integer,
+ (asn1_type_release)der_free_integer, sizeof(int)
+ },
+ el(oid, heim_oid),
+ el(general_string, heim_general_string),
+#undef fuzel
+#undef el
+#undef elber
+};
+
+
+
+int
+_asn1_encode_fuzzer(const struct asn1_template *t,
+ unsigned char *p, size_t len,
+ const void *data, size_t *size)
+{
+ size_t elements = A1_HEADER_LEN(t);
+ int ret = 0;
+ size_t oldlen = len;
+
+ t += A1_HEADER_LEN(t);
+
+ while (elements) {
+ switch (t->tt & A1_OP_MASK) {
+ case A1_OP_TYPE:
+ case A1_OP_TYPE_EXTERN: {
+ size_t newsize;
+ const void *el = DPOC(data, t->offset);
+
+ if (t->tt & A1_FLAG_OPTIONAL) {
+ void **pel = (void **)el;
+ if (*pel == NULL)
+ break;
+ el = *pel;
+ }
+
+ if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
+ ret = _asn1_encode_fuzzer(t->ptr, p, len, el, &newsize);
+ } else {
+ const struct asn1_type_func *f = t->ptr;
+ ret = (f->encode)(p, len, el, &newsize);
+ }
+
+ if (ret)
+ return ret;
+ p -= newsize; len -= newsize;
+
+ break;
+ }
+ case A1_OP_TAG: {
+ const void *olddata = data;
+ size_t l, datalen;
+
+ data = DPOC(data, t->offset);
+
+ if (t->tt & A1_FLAG_OPTIONAL) {
+ void **el = (void **)data;
+ if (*el == NULL) {
+ data = olddata;
+ break;
+ }
+ data = *el;
+ }
+
+ ret = _asn1_encode_fuzzer(t->ptr, p, len, data, &datalen);
+ if (ret)
+ return ret;
+
+ len -= datalen; p -= datalen;
+
+ ret = fuzzer_put_length_and_tag(p, len, datalen,
+ A1_TAG_CLASS(t->tt),
+ A1_TAG_TYPE(t->tt),
+ A1_TAG_TAG(t->tt), &l);
+ if (ret)
+ return ret;
+
+ p -= l; len -= l;
+
+ data = olddata;
+
+ break;
+ }
+ case A1_OP_PARSE: {
+ unsigned int type = A1_PARSE_TYPE(t->tt);
+ size_t newsize;
+ const void *el = DPOC(data, t->offset);
+
+ if (type > sizeof(fuzzerprim)/sizeof(fuzzerprim[0])) {
+ ABORT_ON_ERROR();
+ return ASN1_PARSE_ERROR;
+ }
+
+ ret = (fuzzerprim[type].encode)(p, len, el, &newsize);
+ if (ret)
+ return ret;
+ p -= newsize; len -= newsize;
+
+ break;
+ }
+ case A1_OP_SETOF: {
+ const struct template_of *el = DPOC(data, t->offset);
+ size_t ellen = _asn1_sizeofType(t->ptr);
+ heim_octet_string *val;
+ unsigned char *elptr = el->val;
+ size_t i, totallen;
+
+ if (el->len == 0)
+ break;
+
+ if (el->len > UINT_MAX/sizeof(val[0]))
+ return ERANGE;
+
+ val = malloc(sizeof(val[0]) * el->len);
+ if (val == NULL)
+ return ENOMEM;
+
+ for(totallen = 0, i = 0; i < el->len; i++) {
+ unsigned char *next;
+ size_t l;
+
+ val[i].length = _asn1_length(t->ptr, elptr);
+ val[i].data = malloc(val[i].length);
+
+ ret = _asn1_encode_fuzzer(t->ptr, DPO(val[i].data, val[i].length - 1),
+ val[i].length, elptr, &l);
+ if (ret)
+ break;
+
+ next = elptr + ellen;
+ if (next < elptr) {
+ ret = ASN1_OVERFLOW;
+ break;
+ }
+ elptr = next;
+ totallen += val[i].length;
+ }
+ if (ret == 0 && totallen > len)
+ ret = ASN1_OVERFLOW;
+ if (ret) {
+ do {
+ free(val[i].data);
+ } while(i-- > 0);
+ free(val);
+ return ret;
+ }
+
+ len -= totallen;
+
+ qsort(val, el->len, sizeof(val[0]), _heim_der_set_sort);
+
+ i = el->len - 1;
+ do {
+ p -= val[i].length;
+ memcpy(p + 1, val[i].data, val[i].length);
+ free(val[i].data);
+ } while(i-- > 0);
+ free(val);
+
+ break;
+
+ }
+ case A1_OP_SEQOF: {
+ struct template_of *el = DPO(data, t->offset);
+ size_t ellen = _asn1_sizeofType(t->ptr);
+ size_t newsize;
+ unsigned int i;
+ unsigned char *elptr = el->val;
+
+ if (el->len == 0)
+ break;
+
+ elptr += ellen * (el->len - 1);
+
+ for (i = 0; i < el->len; i++) {
+ ret = _asn1_encode_fuzzer(t->ptr, p, len,
+ elptr,
+ &newsize);
+ if (ret)
+ return ret;
+ p -= newsize; len -= newsize;
+ elptr -= ellen;
+ }
+
+ break;
+ }
+ case A1_OP_BMEMBER: {
+ const struct asn1_template *bmember = t->ptr;
+ size_t size = bmember->offset;
+ size_t elements = A1_HEADER_LEN(bmember);
+ size_t pos;
+ unsigned char c = 0;
+ unsigned int bitset = 0;
+ int rfc1510 = (bmember->tt & A1_HBF_RFC1510);
+
+ bmember += elements;
+
+ if (rfc1510)
+ pos = 31;
+ else
+ pos = bmember->offset;
+
+ while (elements && len) {
+ while (bmember->offset / 8 < pos / 8) {
+ if (rfc1510 || bitset || c) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = c; len--;
+ }
+ c = 0;
+ pos -= 8;
+ }
+ _asn1_bmember_put_bit(&c, data, bmember->offset, size, &bitset);
+ elements--; bmember--;
+ }
+ if (rfc1510 || bitset) {
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ *p-- = c; len--;
+ }
+
+ if (len < 1)
+ return ASN1_OVERFLOW;
+ if (rfc1510 || bitset == 0)
+ *p-- = 0;
+ else
+ *p-- = bitset - 1;
+
+ len--;
+
+ break;
+ }
+ case A1_OP_CHOICE: {
+ const struct asn1_template *choice = t->ptr;
+ const unsigned int *element = DPOC(data, choice->offset);
+ size_t datalen;
+ const void *el;
+
+ if (*element > A1_HEADER_LEN(choice)) {
+ printf("element: %d\n", *element);
+ return ASN1_PARSE_ERROR;
+ }
+
+ if (*element == 0) {
+ ret += der_put_octet_string(p, len,
+ DPOC(data, choice->tt), &datalen);
+ } else {
+ choice += *element;
+ el = DPOC(data, choice->offset);
+ ret = _asn1_encode_fuzzer(choice->ptr, p, len, el, &datalen);
+ if (ret)
+ return ret;
+ }
+ len -= datalen; p -= datalen;
+
+ break;
+ }
+ default:
+ ABORT_ON_ERROR();
+ }
+ t--;
+ elements--;
+ }
+
+ if (fuzzer_trigger(1000)) {
+ memset(p + 1, 0, oldlen - len);
+ } else if (fuzzer_trigger(1000)) {
+ memset(p + 1, 0x41, oldlen - len);
+ } else if (fuzzer_trigger(1000)) {
+ memset(p + 1, 0xff, oldlen - len);
+ }
+
+ if (size)
+ *size = oldlen - len;
+
+ return 0;
+}
+
+size_t
+_asn1_length_fuzzer(const struct asn1_template *t, const void *data)
+{
+ size_t elements = A1_HEADER_LEN(t);
+ size_t ret = 0;
+
+ t += A1_HEADER_LEN(t);
+
+ while (elements) {
+ switch (t->tt & A1_OP_MASK) {
+ case A1_OP_TYPE:
+ case A1_OP_TYPE_EXTERN: {
+ const void *el = DPOC(data, t->offset);
+
+ if (t->tt & A1_FLAG_OPTIONAL) {
+ void **pel = (void **)el;
+ if (*pel == NULL)
+ break;
+ el = *pel;
+ }
+
+ if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
+ ret += _asn1_length(t->ptr, el);
+ } else {
+ const struct asn1_type_func *f = t->ptr;
+ ret += (f->length)(el);
+ }
+ break;
+ }
+ case A1_OP_TAG: {
+ size_t datalen;
+ const void *olddata = data;
+
+ data = DPO(data, t->offset);
+
+ if (t->tt & A1_FLAG_OPTIONAL) {
+ void **el = (void **)data;
+ if (*el == NULL) {
+ data = olddata;
+ break;
+ }
+ data = *el;
+ }
+ datalen = _asn1_length(t->ptr, data);
+ ret += der_length_tag(A1_TAG_TAG(t->tt)) + fuzzer_length_len(datalen);
+ ret += datalen;
+ data = olddata;
+ break;
+ }
+ case A1_OP_PARSE: {
+ unsigned int type = A1_PARSE_TYPE(t->tt);
+ const void *el = DPOC(data, t->offset);
+
+ if (type >= sizeof(asn1_template_prim)/sizeof(asn1_template_prim[0])) {
+ ABORT_ON_ERROR();
+ break;
+ }
+ ret += (asn1_template_prim[type].length)(el);
+ break;
+ }
+ case A1_OP_SETOF:
+ case A1_OP_SEQOF: {
+ const struct template_of *el = DPOC(data, t->offset);
+ size_t ellen = _asn1_sizeofType(t->ptr);
+ const unsigned char *element = el->val;
+ unsigned int i;
+
+ for (i = 0; i < el->len; i++) {
+ ret += _asn1_length(t->ptr, element);
+ element += ellen;
+ }
+
+ break;
+ }
+ case A1_OP_BMEMBER: {
+ const struct asn1_template *bmember = t->ptr;
+ size_t size = bmember->offset;
+ size_t elements = A1_HEADER_LEN(bmember);
+ int rfc1510 = (bmember->tt & A1_HBF_RFC1510);
+
+ if (rfc1510) {
+ ret += 5;
+ } else {
+
+ ret += 1;
+
+ bmember += elements;
+
+ while (elements) {
+ if (_asn1_bmember_isset_bit(data, bmember->offset, size)) {
+ ret += (bmember->offset / 8) + 1;
+ break;
+ }
+ elements--; bmember--;
+ }
+ }
+ break;
+ }
+ case A1_OP_CHOICE: {
+ const struct asn1_template *choice = t->ptr;
+ const unsigned int *element = DPOC(data, choice->offset);
+
+ if (*element > A1_HEADER_LEN(choice))
+ break;
+
+ if (*element == 0) {
+ ret += der_length_octet_string(DPOC(data, choice->tt));
+ } else {
+ choice += *element;
+ ret += _asn1_length(choice->ptr, DPOC(data, choice->offset));
+ }
+ break;
+ }
+ default:
+ ABORT_ON_ERROR();
+ break;
+ }
+ elements--;
+ t--;
+ }
+ return ret;
+}
+
+#endif /* ASN1_FUZZER */
diff --git a/lib/asn1/gen.c b/lib/asn1/gen.c
index 2194b329ce1d..3773647c29d7 100644
--- a/lib/asn1/gen.c
+++ b/lib/asn1/gen.c
@@ -148,7 +148,7 @@ init_generate (const char *filename, const char *base)
fn = NULL;
/* template file */
- if (asprintf(&template, "%s-template.c", headerbase) < 0 || template == NULL)
+ if (asprintf(&template, "%s-template.x", headerbase) < 0 || template == NULL)
errx(1, "malloc");
fprintf (headerfile,
"/* Generated from %s */\n"
@@ -163,6 +163,15 @@ init_generate (const char *filename, const char *base)
fprintf (headerfile,
"#ifndef __asn1_common_definitions__\n"
"#define __asn1_common_definitions__\n\n");
+ fprintf (headerfile,
+ "#ifndef __HEIM_BASE_DATA__\n"
+ "#define __HEIM_BASE_DATA__ 1\n"
+ "struct heim_base_data {\n"
+ " size_t length;\n"
+ " void *data;\n"
+ "};\n"
+ "typedef struct heim_base_data heim_octet_string;\n"
+ "#endif\n\n");
fprintf (headerfile,
"typedef struct heim_integer {\n"
" size_t length;\n"
@@ -170,21 +179,16 @@ init_generate (const char *filename, const char *base)
" int negative;\n"
"} heim_integer;\n\n");
fprintf (headerfile,
- "typedef struct heim_octet_string {\n"
- " size_t length;\n"
- " void *data;\n"
- "} heim_octet_string;\n\n");
- fprintf (headerfile,
"typedef char *heim_general_string;\n\n"
);
fprintf (headerfile,
"typedef char *heim_utf8_string;\n\n"
);
fprintf (headerfile,
- "typedef struct heim_octet_string heim_printable_string;\n\n"
+ "typedef struct heim_base_data heim_printable_string;\n\n"
);
fprintf (headerfile,
- "typedef struct heim_octet_string heim_ia5_string;\n\n"
+ "typedef struct heim_base_data heim_ia5_string;\n\n"
);
fprintf (headerfile,
"typedef struct heim_bmp_string {\n"
@@ -210,8 +214,8 @@ init_generate (const char *filename, const char *base)
" void *data;\n"
"} heim_bit_string;\n\n");
fprintf (headerfile,
- "typedef struct heim_octet_string heim_any;\n"
- "typedef struct heim_octet_string heim_any_set;\n\n");
+ "typedef struct heim_base_data heim_any;\n"
+ "typedef struct heim_base_data heim_any_set;\n\n");
fputs("#define ASN1_MALLOC_ENCODE(T, B, BL, S, L, R) \\\n"
" do { \\\n"
" (BL) = length_##T((S)); \\\n"
@@ -265,14 +269,14 @@ init_generate (const char *filename, const char *base)
"#include <string.h>\n"
"#include <errno.h>\n"
"#include <limits.h>\n"
- "#include <krb5-types.h>\n",
- filename);
+ "#include <%s>\n",
+ filename,
+ type_file_string);
fprintf (templatefile,
"#include <%s>\n"
"#include <%s>\n"
"#include <der.h>\n"
- "#include <der-private.h>\n"
"#include <asn1-template.h>\n",
header, privheader);
@@ -290,9 +294,10 @@ close_generate (void)
fclose (privheaderfile);
if (templatefile)
fclose (templatefile);
- if (logfile)
+ if (logfile) {
fprintf (logfile, "\n");
fclose (logfile);
+ }
}
void
@@ -303,7 +308,8 @@ gen_assign_defval(const char *var, struct value *val)
fprintf(codefile, "if((%s = strdup(\"%s\")) == NULL)\nreturn ENOMEM;\n", var, val->u.stringvalue);
break;
case integervalue:
- fprintf(codefile, "%s = %d;\n", var, val->u.integervalue);
+ fprintf(codefile, "%s = %lld;\n",
+ var, (long long)val->u.integervalue);
break;
case booleanvalue:
if(val->u.booleanvalue)
@@ -324,7 +330,8 @@ gen_compare_defval(const char *var, struct value *val)
fprintf(codefile, "if(strcmp(%s, \"%s\") != 0)\n", var, val->u.stringvalue);
break;
case integervalue:
- fprintf(codefile, "if(%s != %d)\n", var, val->u.integervalue);
+ fprintf(codefile, "if(%s != %lld)\n",
+ var, (long long)val->u.integervalue);
break;
case booleanvalue:
if(val->u.booleanvalue)
@@ -350,7 +357,8 @@ generate_header_of_codefile(const char *name)
codefile = fopen (filename, "w");
if (codefile == NULL)
err (1, "fopen %s", filename);
- fprintf(logfile, "%s ", filename);
+ if (logfile)
+ fprintf(logfile, "%s ", filename);
free(filename);
filename = NULL;
fprintf (codefile,
@@ -363,19 +371,22 @@ generate_header_of_codefile(const char *name)
"#include <string.h>\n"
"#include <errno.h>\n"
"#include <limits.h>\n"
- "#include <krb5-types.h>\n",
- orig_filename);
+ "#include <%s>\n",
+ orig_filename,
+ type_file_string);
fprintf (codefile,
- "#include <%s>\n"
- "#include <%s>\n",
+ "#include \"%s\"\n"
+ "#include \"%s\"\n",
header, privheader);
fprintf (codefile,
"#include <asn1_err.h>\n"
"#include <der.h>\n"
- "#include <der-private.h>\n"
- "#include <asn1-template.h>\n"
- "#include <parse_units.h>\n\n");
+ "#include <asn1-template.h>\n\n");
+
+ if (parse_units_flag)
+ fprintf (codefile,
+ "#include <parse_units.h>\n\n");
}
@@ -397,8 +408,9 @@ generate_constant (const Symbol *s)
case booleanvalue:
break;
case integervalue:
- fprintf (headerfile, "enum { %s = %d };\n\n",
- s->gen_name, s->value->u.integervalue);
+ fprintf (headerfile, "enum { %s = %lld };\n\n",
+ s->gen_name,
+ (long long)s->value->u.integervalue);
break;
case nullvalue:
break;
@@ -406,7 +418,7 @@ generate_constant (const Symbol *s)
break;
case objectidentifiervalue: {
struct objid *o, **list;
- unsigned int i, len;
+ size_t i, len;
char *gen_upper;
if (!one_code_file)
@@ -433,16 +445,16 @@ generate_constant (const Symbol *s)
o->label ? o->label : "label-less", o->value);
}
- fprintf (codefile, "static unsigned oid_%s_variable_num[%d] = {",
- s->gen_name, len);
+ fprintf (codefile, "static unsigned oid_%s_variable_num[%lu] = {",
+ s->gen_name, (unsigned long)len);
for (i = len ; i > 0; i--) {
fprintf(codefile, "%d%s ", list[i - 1]->value, i > 1 ? "," : "");
}
fprintf(codefile, "};\n");
fprintf (codefile, "const heim_oid asn1_oid_%s = "
- "{ %d, oid_%s_variable_num };\n\n",
- s->gen_name, len, s->gen_name);
+ "{ %lu, oid_%s_variable_num };\n\n",
+ s->gen_name, (unsigned long)len, s->gen_name);
free(list);
@@ -540,8 +552,9 @@ define_asn1 (int level, Type *t)
if(t->members == NULL) {
fprintf (headerfile, "INTEGER");
if (t->range)
- fprintf (headerfile, " (%d..%d)",
- t->range->min, t->range->max);
+ fprintf (headerfile, " (%lld..%lld)",
+ (long long)t->range->min,
+ (long long)t->range->max);
} else {
Member *m;
fprintf (headerfile, "INTEGER {\n");
@@ -582,7 +595,7 @@ define_asn1 (int level, Type *t)
case TSet:
case TSequence: {
Member *m;
- int max_width = 0;
+ size_t max_width = 0;
if(t->type == TChoice)
fprintf(headerfile, "CHOICE {\n");
@@ -597,13 +610,13 @@ define_asn1 (int level, Type *t)
max_width += 3;
if(max_width < 16) max_width = 16;
ASN1_TAILQ_FOREACH(m, t->members, members) {
- int width = max_width;
+ size_t width = max_width;
space(level + 1);
if (m->ellipsis) {
fprintf (headerfile, "...");
} else {
width -= fprintf(headerfile, "%s", m->name);
- fprintf(headerfile, "%*s", width, "");
+ fprintf(headerfile, "%*s", (int)width, "");
define_asn1(level + 1, m->type);
if(m->optional)
fprintf(headerfile, " OPTIONAL");
@@ -723,15 +736,17 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ
fprintf (headerfile, "} %s;\n", name);
} else if (t->range == NULL) {
fprintf (headerfile, "heim_integer %s;\n", name);
- } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ } else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX) {
+ fprintf (headerfile, "int64_t %s;\n", name);
+ } else if (t->range->min >= 0 && t->range->max > UINT_MAX) {
+ fprintf (headerfile, "uint64_t %s;\n", name);
+ } else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX) {
fprintf (headerfile, "int %s;\n", name);
- } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
- fprintf (headerfile, "unsigned int %s;\n", name);
- } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ } else if (t->range->min >= 0 && t->range->max <= UINT_MAX) {
fprintf (headerfile, "unsigned int %s;\n", name);
} else
- errx(1, "%s: unsupported range %d -> %d",
- name, t->range->min, t->range->max);
+ errx(1, "%s: unsupported range %lld -> %lld",
+ name, (long long)t->range->min, (long long)t->range->max);
break;
case TBoolean:
space(level);
@@ -744,7 +759,7 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ
case TBitString: {
Member *m;
Type i;
- struct range range = { 0, INT_MAX };
+ struct range range = { 0, UINT_MAX };
i.type = TInteger;
i.range = &range;
@@ -843,7 +858,7 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ
case TSetOf:
case TSequenceOf: {
Type i;
- struct range range = { 0, INT_MAX };
+ struct range range = { 0, UINT_MAX };
getnewbasename(&newbasename, typedefp, basename, name);
@@ -888,7 +903,7 @@ define_type (int level, const char *name, const char *basename, Type *t, int typ
fprintf(headerfile, "heim_octet_string _save;\n");
}
space(level + 1);
- fprintf (headerfile, "enum {\n");
+ fprintf (headerfile, "enum %s_enum {\n", newbasename);
m = have_ellipsis(t);
if (m) {
space(level + 2);
diff --git a/lib/asn1/gen_decode.c b/lib/asn1/gen_decode.c
index 9d816d5400d7..650017fa6b90 100644
--- a/lib/asn1/gen_decode.c
+++ b/lib/asn1/gen_decode.c
@@ -189,22 +189,22 @@ range_check(const char *name,
{
if (r->min == r->max + 2 || r->min < r->max)
fprintf (codefile,
- "if ((%s)->%s > %d) {\n"
+ "if ((%s)->%s > %lld) {\n"
"e = ASN1_MAX_CONSTRAINT; %s;\n"
"}\n",
- name, length, r->max, forwstr);
- if (r->min - 1 == r->max || r->min < r->max)
+ name, length, (long long)r->max, forwstr);
+ if ((r->min - 1 == r->max || r->min < r->max) && r->min > 0)
fprintf (codefile,
- "if ((%s)->%s < %d) {\n"
+ "if ((%s)->%s < %lld) {\n"
"e = ASN1_MIN_CONSTRAINT; %s;\n"
"}\n",
- name, length, r->min, forwstr);
+ name, length, (long long)r->min, forwstr);
if (r->max == r->min)
fprintf (codefile,
- "if ((%s)->%s != %d) {\n"
+ "if ((%s)->%s != %lld) {\n"
"e = ASN1_EXACT_CONSTRAINT; %s;\n"
"}\n",
- name, length, r->min, forwstr);
+ name, length, (long long)r->min, forwstr);
}
static int
@@ -242,6 +242,14 @@ decode_type (const char *name, const Type *t, int optional,
}
case TInteger:
if(t->members) {
+ /*
+ * This will produce a worning, how its hard to fix since:
+ * if its enum to an NameType, we can add appriate
+ * type-cast. If its not though, we have to figure out if
+ * there is negative enum enum and use appropriate
+ * signness and size on the intertype we cast the result
+ * too.
+ */
fprintf(codefile,
"{\n"
"int enumint;\n");
@@ -252,15 +260,17 @@ decode_type (const char *name, const Type *t, int optional,
name);
} else if (t->range == NULL) {
decode_primitive ("heim_integer", name, forwstr);
- } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ } else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX) {
+ decode_primitive ("integer64", name, forwstr);
+ } else if (t->range->min >= 0 && t->range->max > UINT_MAX) {
+ decode_primitive ("unsigned64", name, forwstr);
+ } else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX) {
decode_primitive ("integer", name, forwstr);
- } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
- decode_primitive ("unsigned", name, forwstr);
- } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ } else if (t->range->min >= 0 && t->range->max <= UINT_MAX) {
decode_primitive ("unsigned", name, forwstr);
} else
- errx(1, "%s: unsupported range %d -> %d",
- name, t->range->min, t->range->max);
+ errx(1, "%s: unsupported range %lld -> %lld",
+ name, (long long)t->range->min, (long long)t->range->max);
break;
case TBoolean:
decode_primitive ("boolean", name, forwstr);
diff --git a/lib/asn1/gen_encode.c b/lib/asn1/gen_encode.c
index 1bd47484d83a..60433a00e705 100644
--- a/lib/asn1/gen_encode.c
+++ b/lib/asn1/gen_encode.c
@@ -33,8 +33,6 @@
#include "gen_locl.h"
-RCSID("$Id$");
-
static void
encode_primitive (const char *typename, const char *name)
{
@@ -50,7 +48,7 @@ classname(Der_class class)
{
const char *cn[] = { "ASN1_C_UNIV", "ASN1_C_APPL",
"ASN1_C_CONTEXT", "ASN1_C_PRIV" };
- if(class < ASN1_C_UNIV || class > ASN1_C_PRIVATE)
+ if ((int)class >= sizeof(cn) / sizeof(cn[0]))
return "???";
return cn[class];
}
@@ -129,15 +127,18 @@ encode_type (const char *name, const Type *t, const char *tmpstr)
fprintf(codefile, "}\n;");
} else if (t->range == NULL) {
encode_primitive ("heim_integer", name);
- } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ } else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX) {
+ encode_primitive ("integer64", name);
+ } else if (t->range->min >= 0 && t->range->max > UINT_MAX) {
+ encode_primitive ("unsigned64", name);
+ } else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX) {
encode_primitive ("integer", name);
- } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
- encode_primitive ("unsigned", name);
- } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ } else if (t->range->min >= 0 && t->range->max <= UINT_MAX) {
encode_primitive ("unsigned", name);
} else
- errx(1, "%s: unsupported range %d -> %d",
- name, t->range->min, t->range->max);
+ errx(1, "%s: unsupported range %lld -> %lld",
+ name, (long long)t->range->min, (long long)t->range->max);
+
constructed = 0;
break;
case TBoolean:
@@ -287,7 +288,7 @@ encode_type (const char *name, const Type *t, const char *tmpstr)
fprintf(codefile,
"{\n"
- "struct heim_octet_string *val;\n"
+ "heim_octet_string *val;\n"
"size_t elen = 0, totallen = 0;\n"
"int eret = 0;\n");
diff --git a/lib/asn1/gen_glue.c b/lib/asn1/gen_glue.c
index 5ab93305a24e..773ce787d5ed 100644
--- a/lib/asn1/gen_glue.c
+++ b/lib/asn1/gen_glue.c
@@ -147,7 +147,8 @@ generate_glue (const Type *t, const char *gen_name)
if (!ASN1_TAILQ_EMPTY(t->members)) {
generate_2int (t, gen_name);
generate_int2 (t, gen_name);
- generate_units (t, gen_name);
+ if (parse_units_flag)
+ generate_units (t, gen_name);
}
break;
default :
diff --git a/lib/asn1/gen_length.c b/lib/asn1/gen_length.c
index 20b5adfe5d02..7a8a725a0ad1 100644
--- a/lib/asn1/gen_length.c
+++ b/lib/asn1/gen_length.c
@@ -80,16 +80,17 @@ length_type (const char *name, const Type *t,
fprintf(codefile, "}\n");
} else if (t->range == NULL) {
length_primitive ("heim_integer", name, variable);
- } else if (t->range->min == INT_MIN && t->range->max == INT_MAX) {
+ } else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX) {
+ length_primitive ("integer64", name, variable);
+ } else if (t->range->min >= 0 && t->range->max > UINT_MAX) {
+ length_primitive ("unsigned64", name, variable);
+ } else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX) {
length_primitive ("integer", name, variable);
- } else if (t->range->min == 0 && t->range->max == UINT_MAX) {
- length_primitive ("unsigned", name, variable);
- } else if (t->range->min == 0 && t->range->max == INT_MAX) {
+ } else if (t->range->min >= 0 && t->range->max <= UINT_MAX) {
length_primitive ("unsigned", name, variable);
} else
- errx(1, "%s: unsupported range %d -> %d",
- name, t->range->min, t->range->max);
-
+ errx(1, "%s: unsupported range %lld -> %lld",
+ name, (long long)t->range->min, (long long)t->range->max);
break;
case TBoolean:
fprintf (codefile, "%s += 1;\n", variable);
@@ -188,14 +189,15 @@ length_type (const char *name, const Type *t,
fprintf (codefile,
"{\n"
"size_t %s_oldret = %s;\n"
- "int i;\n"
+ "unsigned int n_%s;\n"
"%s = 0;\n",
- tmpstr, variable, variable);
+ tmpstr, variable, tmpstr, variable);
- fprintf (codefile, "for(i = (%s)->len - 1; i >= 0; --i){\n", name);
+ fprintf (codefile, "for(n_%s = (%s)->len; n_%s > 0; --n_%s){\n",
+ tmpstr, name, tmpstr, tmpstr);
fprintf (codefile, "size_t %s_for_oldret = %s;\n"
"%s = 0;\n", tmpstr, variable, variable);
- if (asprintf (&n, "&(%s)->val[i]", name) < 0 || n == NULL)
+ if (asprintf (&n, "&(%s)->val[n_%s - 1]", name, tmpstr) < 0 || n == NULL)
errx(1, "malloc");
if (asprintf (&sname, "%s_S_Of", tmpstr) < 0 || sname == NULL)
errx(1, "malloc");
diff --git a/lib/asn1/gen_locl.h b/lib/asn1/gen_locl.h
index 9e87b0c578c7..288631e98469 100644
--- a/lib/asn1/gen_locl.h
+++ b/lib/asn1/gen_locl.h
@@ -92,10 +92,13 @@ void gen_template_import(const Symbol *);
extern FILE *privheaderfile, *headerfile, *codefile, *logfile, *templatefile;
+extern const char *fuzzer_string;
extern int support_ber;
extern int template_flag;
extern int rfc1510_bitstring;
extern int one_code_file;
+extern int parse_units_flag;
+extern char *type_file_string;
extern int error_flag;
diff --git a/lib/asn1/gen_template.c b/lib/asn1/gen_template.c
index edd68e122380..d469201e554d 100644
--- a/lib/asn1/gen_template.c
+++ b/lib/asn1/gen_template.c
@@ -3,7 +3,7 @@
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
- * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -52,11 +52,13 @@ integer_symbol(const char *basename, const Type *t)
return "int"; /* XXX enum foo */
else if (t->range == NULL)
return "heim_integer";
- else if (t->range->min == INT_MIN && t->range->max == INT_MAX)
+ else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX)
+ return "int64_t";
+ else if (t->range->min >= 0 && t->range->max > UINT_MAX)
+ return "uint64_t";
+ else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX)
return "int";
- else if (t->range->min == 0 && t->range->max == UINT_MAX)
- return "unsigned";
- else if (t->range->min == 0 && t->range->max == INT_MAX)
+ else if (t->range->min >= 0 && t->range->max <= UINT_MAX)
return "unsigned";
else {
abort();
@@ -114,6 +116,12 @@ ia5string_symbol(const char *basename, const Type *t)
}
static const char *
+teletexstring_symbol(const char *basename, const Type *t)
+{
+ return "heim_teletex_string";
+}
+
+static const char *
visiblestring_symbol(const char *basename, const Type *t)
{
return "heim_visible_string";
@@ -164,6 +172,7 @@ struct {
{ TGeneralString, generalstring_symbol, 0 },
{ TGeneralizedTime, time_symbol, 0 },
{ TIA5String, ia5string_symbol, 0 },
+ { TTeletexString, generalstring_symbol, 0 },
{ TInteger, integer_symbol, 0 },
{ TOID, oid_symbol, 0 },
{ TOctetString, octetstring_symbol, 0 },
@@ -175,6 +184,7 @@ struct {
{ TType, ttype_symbol, 1 },
{ TUTCTime, time_symbol, 0 },
{ TUniversalString, universalstring_symbol, 0 },
+ { TTeletexString, teletexstring_symbol, 0 },
{ TVisibleString, visiblestring_symbol, 0 },
{ TUTF8String, utf8string_symbol, 0 },
{ TChoice, sequence_symbol, 1 },
@@ -221,12 +231,12 @@ symbol_name(const char *basename, const Type *t)
static char *
-partial_offset(const char *basetype, const char *name, int need_offset)
+partial_offset(const char *basetype, const char *name, int need_offset, int isstruct)
{
char *str;
if (name == NULL || need_offset == 0)
return strdup("0");
- if (asprintf(&str, "offsetof(struct %s, %s)", basetype, name) < 0 || str == NULL)
+ if (asprintf(&str, "offsetof(%s%s, %s)", isstruct ? "struct " : "", basetype, name) < 0 || str == NULL)
errx(1, "malloc");
return str;
}
@@ -250,13 +260,13 @@ struct tlist {
ASN1_TAILQ_HEAD(tlisthead, tlist);
-static void tlist_header(struct tlist *, const char *, ...) __attribute__((__format__(__printf__, 2, 3)));
+static void tlist_header(struct tlist *, const char *, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
static struct template *
- add_line(struct templatehead *, const char *, ...) __attribute__((__format__(__printf__, 2, 3)));
+ add_line(struct templatehead *, const char *, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
static int tlist_cmp(const struct tlist *, const struct tlist *);
static void add_line_pointer(struct templatehead *, const char *, const char *, const char *, ...)
- __attribute__((__format__(__printf__, 4, 5)));
+ __attribute__ ((__format__ (__printf__, 4, 5)));
static struct tlisthead tlistmaster = ASN1_TAILQ_HEAD_INITIALIZER(tlistmaster);
@@ -306,7 +316,7 @@ tlist_print(struct tlist *tl)
unsigned int i = 1;
FILE *f = get_code_file();
- fprintf(f, "static const struct asn1_template asn1_%s[] = {\n", tl->name);
+ fprintf(f, "const struct asn1_template asn1_%s[] = {\n", tl->name);
fprintf(f, "/* 0 */ %s,\n", tl->header);
ASN1_TAILQ_FOREACH(q, &tl->template, members) {
int last = (ASN1_TAILQ_LAST(&tl->template, templatehead) == q);
@@ -331,6 +341,10 @@ tlist_cmp_name(const char *tname, const char *qname)
{
struct tlist *tl = tlist_find_by_name(tname);
struct tlist *ql = tlist_find_by_name(qname);
+ if (tl == NULL)
+ return 1;
+ if (ql == NULL)
+ return -1;
return tlist_cmp(tl, ql);
}
@@ -431,7 +445,7 @@ use_extern(const Symbol *s)
}
static int
-is_struct(Type *t, int isstruct)
+is_struct(const Type *t, int isstruct)
{
size_t i;
@@ -463,24 +477,28 @@ compact_tag(const Type *t)
}
static void
-template_members(struct templatehead *temp, const char *basetype, const char *name, const Type *t, int optional, int isstruct, int need_offset)
+template_members(struct templatehead *temp, const char *basetype, const char *name, const Type *t, int optional, int implicit, int isstruct, int need_offset)
{
char *poffset = NULL;
if (optional && t->type != TTag && t->type != TType)
errx(1, "%s...%s is optional and not a (TTag or TType)", basetype, name);
- poffset = partial_offset(basetype, name, need_offset);
+ poffset = partial_offset(basetype, name, need_offset, isstruct);
switch (t->type) {
case TType:
if (use_extern(t->symbol)) {
- add_line(temp, "{ A1_OP_TYPE_EXTERN %s, %s, &asn1_extern_%s}",
+ add_line(temp, "{ A1_OP_TYPE_EXTERN %s%s, %s, &asn1_extern_%s}",
optional ? "|A1_FLAG_OPTIONAL" : "",
+ implicit ? "|A1_FLAG_IMPLICIT" : "",
poffset, t->symbol->gen_name);
} else {
add_line_pointer(temp, t->symbol->gen_name, poffset,
- "A1_OP_TYPE %s", optional ? "|A1_FLAG_OPTIONAL" : "");
+ "A1_OP_TYPE %s%s",
+ optional ? "|A1_FLAG_OPTIONAL" : "",
+ implicit ? "|A1_FLAG_IMPLICIT" : "");
+
}
break;
case TInteger: {
@@ -490,15 +508,17 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
itype = "IMEMBER";
else if (t->range == NULL)
itype = "HEIM_INTEGER";
- else if (t->range->min == INT_MIN && t->range->max == INT_MAX)
+ else if (t->range->min < INT_MIN && t->range->max <= INT64_MAX)
+ itype = "INTEGER64";
+ else if (t->range->min >= 0 && t->range->max > UINT_MAX)
+ itype = "UNSIGNED64";
+ else if (t->range->min >= INT_MIN && t->range->max <= INT_MAX)
itype = "INTEGER";
- else if (t->range->min == 0 && t->range->max == UINT_MAX)
- itype = "UNSIGNED";
- else if (t->range->min == 0 && t->range->max == INT_MAX)
+ else if (t->range->min >= 0 && t->range->max <= UINT_MAX)
itype = "UNSIGNED";
else
- errx(1, "%s: unsupported range %d -> %d",
- name, t->range->min, t->range->max);
+ errx(1, "%s: unsupported range %lld -> %lld",
+ name, (long long)t->range->min, (long long)t->range->max);
add_line(temp, "{ A1_PARSE_T(A1T_%s), %s, NULL }", itype, poffset);
break;
@@ -545,19 +565,22 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
case TNull:
break;
case TBitString: {
- struct templatehead template = ASN1_TAILQ_HEAD_INITIALIZER(template);
+ struct templatehead template;
struct template *q;
Member *m;
size_t count = 0, i;
char *bname = NULL;
FILE *f = get_code_file();
+ static unsigned long bmember_counter = 0;
+
+ ASN1_TAILQ_INIT(&template);
if (ASN1_TAILQ_EMPTY(t->members)) {
add_line(temp, "{ A1_PARSE_T(A1T_HEIM_BIT_STRING), %s, NULL }", poffset);
break;
}
- if (asprintf(&bname, "bmember_%s_%p", name ? name : "", t) < 0 || bname == NULL)
+ if (asprintf(&bname, "bmember_%s_%lu", name ? name : "", bmember_counter++) < 0 || bname == NULL)
errx(1, "malloc");
output_name(bname);
@@ -589,6 +612,8 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
case TSequence: {
Member *m;
+ fprintf(get_code_file(), "/* tsequence: members isstruct: %d */\n", isstruct);
+
ASN1_TAILQ_FOREACH(m, t->members, members) {
char *newbasename = NULL;
@@ -603,7 +628,7 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
if (newbasename == NULL)
errx(1, "malloc");
- template_members(temp, newbasename, m->gen_name, m->type, m->optional, isstruct, 1);
+ template_members(temp, newbasename, m->gen_name, m->type, m->optional, 0, isstruct, 1);
free(newbasename);
}
@@ -614,13 +639,47 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
char *tname = NULL, *elname = NULL;
const char *sename, *dupname;
int subtype_is_struct = is_struct(t->subtype, isstruct);
+ static unsigned long tag_counter = 0;
+ int tagimplicit = (t->tag.tagenv == TE_IMPLICIT);
+ struct type *subtype;
+
+ fprintf(get_code_file(), "/* template_members: %s %s %s */\n", basetype, implicit ? "imp" : "exp", tagimplicit ? "imp" : "exp");
+
+ if (tagimplicit) {
+
+ struct type *type = t->subtype;
+ int have_tag = 0;
+
+ while (!have_tag) {
+ if (type->type == TTag) {
+ fprintf(get_code_file(), "/* template_members: imp skip tag */\n");
+ type = type->subtype;
+ have_tag = 1;
+ } else if(type->type == TType && type->symbol && type->symbol->type) {
+ /* XXX really, we should stop here and find a
+ * pointer to where this is encoded instead of
+ * generated an new structure and hope that the
+ * optimizer catch it later.
+ */
+ subtype_is_struct = is_struct(type, isstruct);
+ fprintf(get_code_file(), "/* template_members: imp skip type %s isstruct: %d */\n",
+ type->symbol->name, subtype_is_struct);
+ type = type->symbol->type;
+ } else {
+ have_tag = 1;
+ }
+ }
+ subtype = type;
+ } else {
+ subtype = t->subtype;
+ }
if (subtype_is_struct)
sename = basetype;
else
- sename = symbol_name(basetype, t->subtype);
+ sename = symbol_name(basetype, subtype);
- if (asprintf(&tname, "tag_%s_%p", name ? name : "", t) < 0 || tname == NULL)
+ if (asprintf(&tname, "tag_%s_%lu", name ? name : "", tag_counter++) < 0 || tname == NULL)
errx(1, "malloc");
output_name(tname);
@@ -628,14 +687,15 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
errx(1, "malloc");
generate_template_type(elname, &dupname, NULL, sename, name,
- t->subtype, 0, subtype_is_struct, 0);
+ subtype, 0, subtype_is_struct, 0);
add_line_pointer(temp, dupname, poffset,
- "A1_TAG_T(%s,%s,%s)%s",
+ "A1_TAG_T(%s,%s,%s)%s%s",
classname(t->tag.tagclass),
- is_primitive_type(t->subtype->type) ? "PRIM" : "CONS",
+ is_primitive_type(subtype->type) ? "PRIM" : "CONS",
valuename(t->tag.tagclass, t->tag.tagvalue),
- optional ? "|A1_FLAG_OPTIONAL" : "");
+ optional ? "|A1_FLAG_OPTIONAL" : "",
+ tagimplicit ? "|A1_FLAG_IMPLICIT" : "");
free(tname);
free(elname);
@@ -647,6 +707,7 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
const char *type = NULL, *tname, *dupname;
char *sename = NULL, *elname = NULL;
int subtype_is_struct = is_struct(t->subtype, 0);
+ static unsigned long seof_counter = 0;
if (name && subtype_is_struct) {
tname = "seofTstruct";
@@ -670,7 +731,7 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
else if (t->type == TSequenceOf) type = "A1_OP_SEQOF";
else abort();
- if (asprintf(&elname, "%s_%s_%p", basetype, tname, t) < 0 || elname == NULL)
+ if (asprintf(&elname, "%s_%s_%lu", basetype, tname, seof_counter++) < 0 || elname == NULL)
errx(1, "malloc");
generate_template_type(elname, &dupname, NULL, sename, NULL, t->subtype,
@@ -681,7 +742,7 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
break;
}
case TChoice: {
- struct templatehead template = ASN1_TAILQ_HEAD_INITIALIZER(template);
+ struct templatehead template;
struct template *q;
size_t count = 0, i;
char *tname = NULL;
@@ -689,9 +750,12 @@ template_members(struct templatehead *temp, const char *basetype, const char *na
Member *m;
int ellipsis = 0;
char *e;
+ static unsigned long choice_counter = 0;
+
+ ASN1_TAILQ_INIT(&template);
- if (asprintf(&tname, "asn1_choice_%s_%s%x",
- basetype, name ? name : "", (unsigned int)(uintptr_t)t) < 0 || tname == NULL)
+ if (asprintf(&tname, "asn1_choice_%s_%s%lu",
+ basetype, name ? name : "", choice_counter++) < 0 || tname == NULL)
errx(1, "malloc");
ASN1_TAILQ_FOREACH(m, t->members, members) {
@@ -803,12 +867,20 @@ generate_template_type(const char *varname,
int optional, int isstruct, int need_offset)
{
struct tlist *tl;
- const char *dup;
+ const char *d;
+ char *szt = NULL;
int have_ellipsis = 0;
+ int implicit = 0;
+ int n;
tl = tlist_new(varname);
- template_members(&tl->template, basetype, name, type, optional, isstruct, need_offset);
+ if (type->type == TTag)
+ implicit = (type->tag.tagenv == TE_IMPLICIT);
+
+ fprintf(get_code_file(), "extern const struct asn1_template asn1_%s[];\n", tl->name);
+
+ template_members(&tl->template, basetype, name, type, optional, implicit, isstruct, need_offset);
/* if its a sequence or set type, check if there is a ellipsis */
if (type->type == TSequence || type->type == TSet) {
@@ -819,19 +891,34 @@ generate_template_type(const char *varname,
}
}
+ if (isstruct)
+ if (name)
+ n = asprintf(&szt, "struct %s_%s", basetype, name);
+ else
+ n = asprintf(&szt, "struct %s", basetype);
+ else
+ n = asprintf(&szt, "%s", basetype);
+ if (n < 0 || szt == NULL)
+ errx(1, "malloc");
+
if (ASN1_TAILQ_EMPTY(&tl->template) && compact_tag(type)->type != TNull)
errx(1, "Tag %s...%s with no content ?", basetype, name ? name : "");
- tlist_header(tl, "{ 0%s%s, sizeof(%s%s), ((void *)%lu) }",
+ fprintf(get_code_file(), "/* generate_template_type: %s */\n", tl->name);
+
+ tlist_header(tl, "{ 0%s%s, sizeof(%s), ((void *)%lu) }",
(symname && preserve_type(symname)) ? "|A1_HF_PRESERVE" : "",
- have_ellipsis ? "|A1_HF_ELLIPSIS" : "",
- isstruct ? "struct " : "", basetype, tlist_count(tl));
-
- dup = tlist_find_dup(tl);
- if (dup) {
- if (strcmp(dup, tl->name) == 0)
- errx(1, "found dup of ourself");
- *dupname = dup;
+ have_ellipsis ? "|A1_HF_ELLIPSIS" : "", szt, tlist_count(tl));
+
+ free(szt);
+
+ d = tlist_find_dup(tl);
+ if (d) {
+#if 0
+ if (strcmp(d, tl->name) == 0)
+ errx(1, "found dup of ourself: %s", d);
+#endif
+ *dupname = d;
} else {
*dupname = tl->name;
tlist_print(tl);
@@ -871,11 +958,12 @@ generate_template(const Symbol *s)
"int\n"
"encode_%s(unsigned char *p, size_t len, const %s *data, size_t *size)\n"
"{\n"
- " return _asn1_encode(asn1_%s, p, len, data, size);\n"
+ " return _asn1_encode%s(asn1_%s, p, len, data, size);\n"
"}\n"
"\n",
s->gen_name,
s->gen_name,
+ fuzzer_string,
dupname);
fprintf(f,
@@ -883,11 +971,12 @@ generate_template(const Symbol *s)
"size_t\n"
"length_%s(const %s *data)\n"
"{\n"
- " return _asn1_length(asn1_%s, data);\n"
+ " return _asn1_length%s(asn1_%s, data);\n"
"}\n"
"\n",
s->gen_name,
s->gen_name,
+ fuzzer_string,
dupname);
@@ -896,7 +985,7 @@ generate_template(const Symbol *s)
"void\n"
"free_%s(%s *data)\n"
"{\n"
- " _asn1_free(asn1_%s, data);\n"
+ " _asn1_free_top(asn1_%s, data);\n"
"}\n"
"\n",
s->gen_name,
diff --git a/lib/asn1/krb5.asn1 b/lib/asn1/krb5.asn1
index 568fe0cd04b4..12986ea4e064 100644
--- a/lib/asn1/krb5.asn1
+++ b/lib/asn1/krb5.asn1
@@ -63,6 +63,8 @@ EXPORTS
PA-ServerReferralData,
PA-SvrReferralData,
PADATA-TYPE,
+ PA-FX-FAST-REQUEST,
+ PA-FX-FAST-REPLY,
Principal,
PrincipalName,
Principals,
@@ -72,7 +74,19 @@ EXPORTS
Ticket,
TicketFlags,
TransitedEncoding,
- TypedData
+ TypedData,
+ KrbFastResponse,
+ KrbFastFinished,
+ KrbFastReq,
+ KrbFastArmor,
+ KDCFastState,
+ KDCFastCookie,
+ KDC-PROXY-MESSAGE,
+ KERB-TIMES,
+ KERB-CRED,
+ KERB-TGS-REQ-IN,
+ KERB-TGS-REQ-OUT,
+ KERB-ARMOR-SERVICE-REPLY
;
NAME-TYPE ::= INTEGER {
@@ -86,10 +100,15 @@ NAME-TYPE ::= INTEGER {
KRB5_NT_SMTP_NAME(7), -- Name in form of SMTP email name
KRB5_NT_ENTERPRISE_PRINCIPAL(10), -- Windows 2000 UPN
KRB5_NT_WELLKNOWN(11), -- Wellknown
+ KRB5_NT_SRV_HST_DOMAIN(12), -- Domain based service with host name as instance (RFC5179)
KRB5_NT_ENT_PRINCIPAL_AND_ID(-130), -- Windows 2000 UPN and SID
KRB5_NT_MS_PRINCIPAL(-128), -- NT 4 style name
KRB5_NT_MS_PRINCIPAL_AND_ID(-129), -- NT style name and SID
- KRB5_NT_NTLM(-1200) -- NTLM name, realm is domain
+ KRB5_NT_NTLM(-1200), -- NTLM name, realm is domain
+ KRB5_NT_X509_GENERAL_NAME(-1201), -- x509 general name (base64 encoded)
+ KRB5_NT_GSS_HOSTBASED_SERVICE(-1202), -- not used; remove
+ KRB5_NT_CACHE_UUID(-1203), -- name is actually a uuid pointing to ccache, use client name in cache
+ KRB5_NT_SRV_HST_NEEDS_CANON (-195894762) -- Internal: indicates that name canonicalization is needed
}
-- message types
@@ -142,6 +161,7 @@ PADATA-TYPE ::= INTEGER {
KRB5-PADATA-SAM-CHALLENGE2(30), -- (kenh@pobox.com)
KRB5-PADATA-SAM-RESPONSE2(31), -- (kenh@pobox.com)
KRB5-PA-EXTRA-TGT(41), -- Reserved extra TGT
+ KRB5-PADATA-FX-FAST-ARMOR(71), -- fast armor
KRB5-PADATA-TD-KRB-PRINCIPAL(102), -- PrincipalName
KRB5-PADATA-PK-TD-TRUSTED-CERTIFIERS(104), -- PKINIT
KRB5-PADATA-PK-TD-CERTIFICATE-INDEX(105), -- PKINIT
@@ -157,7 +177,6 @@ PADATA-TYPE ::= INTEGER {
-- tell KDC that is supports
-- the asCheckSum in the
-- PK-AS-REP
- KRB5-PADATA-CLIENT-CANONICALIZED(133), -- referals
KRB5-PADATA-FX-COOKIE(133), -- krb-wg-preauth-framework
KRB5-PADATA-AUTHENTICATION-SET(134), -- krb-wg-preauth-framework
KRB5-PADATA-AUTH-SET-SELECTED(135), -- krb-wg-preauth-framework
@@ -214,6 +233,8 @@ CKSUMTYPE ::= INTEGER {
CKSUMTYPE_SHA1(14),
CKSUMTYPE_HMAC_SHA1_96_AES_128(15),
CKSUMTYPE_HMAC_SHA1_96_AES_256(16),
+ CKSUMTYPE_HMAC_SHA256_128_AES128(19),
+ CKSUMTYPE_HMAC_SHA384_192_AES256(20),
CKSUMTYPE_GSSAPI(0x8003),
CKSUMTYPE_HMAC_MD5(-138), -- unofficial microsoft number
CKSUMTYPE_HMAC_MD5_ENC(-1138) -- even more unofficial
@@ -233,6 +254,8 @@ ENCTYPE ::= INTEGER {
KRB5_ENCTYPE_DES3_CBC_SHA1(16), -- with key derivation
KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96(17),
KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96(18),
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128(19),
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192(20),
KRB5_ENCTYPE_ARCFOUR_HMAC_MD5(23),
KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56(24),
KRB5_ENCTYPE_ENCTYPE_PK_CROSS(48),
@@ -319,8 +342,8 @@ TicketFlags ::= BIT STRING {
hw-authent(11),
transited-policy-checked(12),
ok-as-delegate(13),
- anonymous(14),
- enc-pa-rep(15)
+ enc-pa-rep(15),
+ anonymous(16)
}
KDCOptions ::= BIT STRING {
@@ -332,9 +355,9 @@ KDCOptions ::= BIT STRING {
allow-postdate(5),
postdated(6),
renewable(8),
- request-anonymous(14),
+ constrained-delegation(14), -- ms extension (aka cname-in-addl-tkt)
canonicalize(15),
- constrained-delegation(16), -- ms extension
+ request-anonymous(16),
disable-transited-check(26),
renewable-ok(27),
enc-tkt-in-skey(28),
@@ -361,7 +384,7 @@ LastReq ::= SEQUENCE OF SEQUENCE {
EncryptedData ::= SEQUENCE {
etype[0] ENCTYPE, -- EncryptionType
- kvno[1] krb5uint32 OPTIONAL,
+ kvno[1] krb5int32 OPTIONAL,
cipher[2] OCTET STRING -- ciphertext
}
@@ -732,16 +755,6 @@ KRB5SignedPath ::= SEQUENCE {
method_data[3] METHOD-DATA OPTIONAL
}
-PA-ClientCanonicalizedNames ::= SEQUENCE{
- requested-name [0] PrincipalName,
- mapped-name [1] PrincipalName
-}
-
-PA-ClientCanonicalized ::= SEQUENCE {
- names [0] PA-ClientCanonicalizedNames,
- canon-checksum [1] Checksum
-}
-
AD-LoginAlias ::= SEQUENCE { -- ad-type number TBD --
login-alias [0] PrincipalName,
checksum [1] Checksum
@@ -766,12 +779,12 @@ PA-ServerReferralData ::= SEQUENCE {
FastOptions ::= BIT STRING {
reserved(0),
hide-client-names(1),
- kdc-follow--referrals(16)
+ kdc-follow-referrals(16)
}
KrbFastReq ::= SEQUENCE {
fast-options [0] FastOptions,
- padata [1] SEQUENCE OF PA-DATA,
+ padata [1] METHOD-DATA,
req-body [2] KDC-REQ-BODY,
...
}
@@ -798,15 +811,15 @@ KrbFastFinished ::= SEQUENCE {
usec [1] krb5int32,
crealm [2] Realm,
cname [3] PrincipalName,
- checksum [4] Checksum,
- ticket-checksum [5] Checksum,
+ ticket-checksum [4] Checksum,
...
}
KrbFastResponse ::= SEQUENCE {
- padata [0] SEQUENCE OF PA-DATA,
- rep-key [1] EncryptionKey OPTIONAL,
- finished [2] KrbFastFinished OPTIONAL,
+ padata [0] METHOD-DATA,
+ strengthen-key [1] EncryptionKey OPTIONAL,
+ finished [2] KrbFastFinished OPTIONAL,
+ nonce [3] krb5uint32,
...
}
@@ -820,6 +833,89 @@ PA-FX-FAST-REPLY ::= CHOICE {
...
}
+KDCFastFlags ::= BIT STRING {
+ use_reply_key(0),
+ reply_key_used(1),
+ reply_key_replaced(2),
+ kdc_verfied(3)
+}
+
+-- KDCFastState is stored in FX_COOKIE
+KDCFastState ::= SEQUENCE {
+ flags [0] KDCFastFlags,
+ expiration [1] GeneralizedTime,
+ fast-state [2] METHOD-DATA,
+ expected-pa-types [3] SEQUENCE OF PADATA-TYPE OPTIONAL
+}
+
+KDCFastCookie ::= SEQUENCE {
+ version [0] UTF8String,
+ cookie [1] EncryptedData
+}
+
+KDC-PROXY-MESSAGE ::= SEQUENCE {
+ kerb-message [0] OCTET STRING,
+ target-domain [1] Realm OPTIONAL,
+ dclocator-hint [2] INTEGER OPTIONAL
+}
+
+-- these messages are used in the GSSCred communication and is not part of Kerberos propper
+
+KERB-TIMES ::= SEQUENCE {
+ authtime [0] KerberosTime,
+ starttime [1] KerberosTime,
+ endtime [2] KerberosTime,
+ renew_till [3] KerberosTime
+}
+
+KERB-CRED ::= SEQUENCE {
+ client [0] Principal,
+ server [1] Principal,
+ keyblock [2] EncryptionKey,
+ times [3] KERB-TIMES,
+ ticket [4] OCTET STRING,
+ authdata [5] OCTET STRING,
+ addresses [6] HostAddresses,
+ flags [7] TicketFlags
+}
+
+KERB-TGS-REQ-IN ::= SEQUENCE {
+ cache [0] OCTET STRING SIZE (16),
+ addrs [1] HostAddresses,
+ flags [2] krb5uint32,
+ imp [3] Principal OPTIONAL,
+ ticket [4] OCTET STRING OPTIONAL,
+ in_cred [5] KERB-CRED,
+ krbtgt [6] KERB-CRED,
+ padata [7] METHOD-DATA
+}
+
+KERB-TGS-REQ-OUT ::= SEQUENCE {
+ subkey [0] EncryptionKey OPTIONAL,
+ t [1] TGS-REQ
+}
+
+
+
+KERB-TGS-REP-IN ::= SEQUENCE {
+ cache [0] OCTET STRING SIZE (16),
+ subkey [1] EncryptionKey OPTIONAL,
+ in_cred [2] KERB-CRED,
+ t [3] TGS-REP
+}
+
+KERB-TGS-REP-OUT ::= SEQUENCE {
+ cache [0] OCTET STRING SIZE (16),
+ cred [1] KERB-CRED,
+ subkey [2] EncryptionKey
+}
+
+KERB-ARMOR-SERVICE-REPLY ::= SEQUENCE {
+ armor [0] KrbFastArmor,
+ armor-key [1] EncryptionKey
+}
+
+
END
-- etags -r '/\([A-Za-z][-A-Za-z0-9]*\).*::=/\1/' k5.asn1
diff --git a/lib/asn1/lex.c b/lib/asn1/lex.c
index 3f2dc92e2775..810c00c30935 100644
--- a/lib/asn1/lex.c
+++ b/lib/asn1/lex.c
@@ -46,6 +46,7 @@ typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@@ -354,7 +355,7 @@ static void yy_fatal_error (yyconst char msg[] );
*/
#define YY_DO_BEFORE_ACTION \
(yytext_ptr) = yy_bp; \
- yyleng = (size_t) (yy_cp - yy_bp); \
+ yyleng = (yy_size_t) (yy_cp - yy_bp); \
(yy_hold_char) = *yy_cp; \
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
@@ -790,7 +791,7 @@ char *yytext;
#line 1 "lex.l"
#line 2 "lex.l"
/*
- * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 - 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -830,6 +831,7 @@ char *yytext;
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -847,7 +849,7 @@ static unsigned lineno = 1;
static void unterminated(const char *, unsigned);
/* This is for broken old lexes (solaris 10 and hpux) */
-#line 851 "lex.c"
+#line 853 "lex.c"
#define INITIAL 0
@@ -1029,9 +1031,9 @@ YY_DECL
register char *yy_cp, *yy_bp;
register int yy_act;
-#line 68 "lex.l"
+#line 69 "lex.l"
-#line 1035 "lex.c"
+#line 1037 "lex.c"
if ( !(yy_init) )
{
@@ -1116,427 +1118,427 @@ do_action: /* This label is used only to access EOF actions. */
case 1:
YY_RULE_SETUP
-#line 69 "lex.l"
+#line 70 "lex.l"
{ return kw_ABSENT; }
YY_BREAK
case 2:
YY_RULE_SETUP
-#line 70 "lex.l"
+#line 71 "lex.l"
{ return kw_ABSTRACT_SYNTAX; }
YY_BREAK
case 3:
YY_RULE_SETUP
-#line 71 "lex.l"
+#line 72 "lex.l"
{ return kw_ALL; }
YY_BREAK
case 4:
YY_RULE_SETUP
-#line 72 "lex.l"
+#line 73 "lex.l"
{ return kw_APPLICATION; }
YY_BREAK
case 5:
YY_RULE_SETUP
-#line 73 "lex.l"
+#line 74 "lex.l"
{ return kw_AUTOMATIC; }
YY_BREAK
case 6:
YY_RULE_SETUP
-#line 74 "lex.l"
+#line 75 "lex.l"
{ return kw_BEGIN; }
YY_BREAK
case 7:
YY_RULE_SETUP
-#line 75 "lex.l"
+#line 76 "lex.l"
{ return kw_BIT; }
YY_BREAK
case 8:
YY_RULE_SETUP
-#line 76 "lex.l"
+#line 77 "lex.l"
{ return kw_BMPString; }
YY_BREAK
case 9:
YY_RULE_SETUP
-#line 77 "lex.l"
+#line 78 "lex.l"
{ return kw_BOOLEAN; }
YY_BREAK
case 10:
YY_RULE_SETUP
-#line 78 "lex.l"
+#line 79 "lex.l"
{ return kw_BY; }
YY_BREAK
case 11:
YY_RULE_SETUP
-#line 79 "lex.l"
+#line 80 "lex.l"
{ return kw_CHARACTER; }
YY_BREAK
case 12:
YY_RULE_SETUP
-#line 80 "lex.l"
+#line 81 "lex.l"
{ return kw_CHOICE; }
YY_BREAK
case 13:
YY_RULE_SETUP
-#line 81 "lex.l"
+#line 82 "lex.l"
{ return kw_CLASS; }
YY_BREAK
case 14:
YY_RULE_SETUP
-#line 82 "lex.l"
+#line 83 "lex.l"
{ return kw_COMPONENT; }
YY_BREAK
case 15:
YY_RULE_SETUP
-#line 83 "lex.l"
+#line 84 "lex.l"
{ return kw_COMPONENTS; }
YY_BREAK
case 16:
YY_RULE_SETUP
-#line 84 "lex.l"
+#line 85 "lex.l"
{ return kw_CONSTRAINED; }
YY_BREAK
case 17:
YY_RULE_SETUP
-#line 85 "lex.l"
+#line 86 "lex.l"
{ return kw_CONTAINING; }
YY_BREAK
case 18:
YY_RULE_SETUP
-#line 86 "lex.l"
+#line 87 "lex.l"
{ return kw_DEFAULT; }
YY_BREAK
case 19:
YY_RULE_SETUP
-#line 87 "lex.l"
+#line 88 "lex.l"
{ return kw_DEFINITIONS; }
YY_BREAK
case 20:
YY_RULE_SETUP
-#line 88 "lex.l"
+#line 89 "lex.l"
{ return kw_EMBEDDED; }
YY_BREAK
case 21:
YY_RULE_SETUP
-#line 89 "lex.l"
+#line 90 "lex.l"
{ return kw_ENCODED; }
YY_BREAK
case 22:
YY_RULE_SETUP
-#line 90 "lex.l"
+#line 91 "lex.l"
{ return kw_END; }
YY_BREAK
case 23:
YY_RULE_SETUP
-#line 91 "lex.l"
+#line 92 "lex.l"
{ return kw_ENUMERATED; }
YY_BREAK
case 24:
YY_RULE_SETUP
-#line 92 "lex.l"
+#line 93 "lex.l"
{ return kw_EXCEPT; }
YY_BREAK
case 25:
YY_RULE_SETUP
-#line 93 "lex.l"
+#line 94 "lex.l"
{ return kw_EXPLICIT; }
YY_BREAK
case 26:
YY_RULE_SETUP
-#line 94 "lex.l"
+#line 95 "lex.l"
{ return kw_EXPORTS; }
YY_BREAK
case 27:
YY_RULE_SETUP
-#line 95 "lex.l"
+#line 96 "lex.l"
{ return kw_EXTENSIBILITY; }
YY_BREAK
case 28:
YY_RULE_SETUP
-#line 96 "lex.l"
+#line 97 "lex.l"
{ return kw_EXTERNAL; }
YY_BREAK
case 29:
YY_RULE_SETUP
-#line 97 "lex.l"
+#line 98 "lex.l"
{ return kw_FALSE; }
YY_BREAK
case 30:
YY_RULE_SETUP
-#line 98 "lex.l"
+#line 99 "lex.l"
{ return kw_FROM; }
YY_BREAK
case 31:
YY_RULE_SETUP
-#line 99 "lex.l"
+#line 100 "lex.l"
{ return kw_GeneralString; }
YY_BREAK
case 32:
YY_RULE_SETUP
-#line 100 "lex.l"
+#line 101 "lex.l"
{ return kw_GeneralizedTime; }
YY_BREAK
case 33:
YY_RULE_SETUP
-#line 101 "lex.l"
+#line 102 "lex.l"
{ return kw_GraphicString; }
YY_BREAK
case 34:
YY_RULE_SETUP
-#line 102 "lex.l"
+#line 103 "lex.l"
{ return kw_IA5String; }
YY_BREAK
case 35:
YY_RULE_SETUP
-#line 103 "lex.l"
+#line 104 "lex.l"
{ return kw_IDENTIFIER; }
YY_BREAK
case 36:
YY_RULE_SETUP
-#line 104 "lex.l"
+#line 105 "lex.l"
{ return kw_IMPLICIT; }
YY_BREAK
case 37:
YY_RULE_SETUP
-#line 105 "lex.l"
+#line 106 "lex.l"
{ return kw_IMPLIED; }
YY_BREAK
case 38:
YY_RULE_SETUP
-#line 106 "lex.l"
+#line 107 "lex.l"
{ return kw_IMPORTS; }
YY_BREAK
case 39:
YY_RULE_SETUP
-#line 107 "lex.l"
+#line 108 "lex.l"
{ return kw_INCLUDES; }
YY_BREAK
case 40:
YY_RULE_SETUP
-#line 108 "lex.l"
+#line 109 "lex.l"
{ return kw_INSTANCE; }
YY_BREAK
case 41:
YY_RULE_SETUP
-#line 109 "lex.l"
+#line 110 "lex.l"
{ return kw_INTEGER; }
YY_BREAK
case 42:
YY_RULE_SETUP
-#line 110 "lex.l"
+#line 111 "lex.l"
{ return kw_INTERSECTION; }
YY_BREAK
case 43:
YY_RULE_SETUP
-#line 111 "lex.l"
+#line 112 "lex.l"
{ return kw_ISO646String; }
YY_BREAK
case 44:
YY_RULE_SETUP
-#line 112 "lex.l"
+#line 113 "lex.l"
{ return kw_MAX; }
YY_BREAK
case 45:
YY_RULE_SETUP
-#line 113 "lex.l"
+#line 114 "lex.l"
{ return kw_MIN; }
YY_BREAK
case 46:
YY_RULE_SETUP
-#line 114 "lex.l"
+#line 115 "lex.l"
{ return kw_MINUS_INFINITY; }
YY_BREAK
case 47:
YY_RULE_SETUP
-#line 115 "lex.l"
+#line 116 "lex.l"
{ return kw_NULL; }
YY_BREAK
case 48:
YY_RULE_SETUP
-#line 116 "lex.l"
+#line 117 "lex.l"
{ return kw_NumericString; }
YY_BREAK
case 49:
YY_RULE_SETUP
-#line 117 "lex.l"
+#line 118 "lex.l"
{ return kw_OBJECT; }
YY_BREAK
case 50:
YY_RULE_SETUP
-#line 118 "lex.l"
+#line 119 "lex.l"
{ return kw_OCTET; }
YY_BREAK
case 51:
YY_RULE_SETUP
-#line 119 "lex.l"
+#line 120 "lex.l"
{ return kw_OF; }
YY_BREAK
case 52:
YY_RULE_SETUP
-#line 120 "lex.l"
+#line 121 "lex.l"
{ return kw_OPTIONAL; }
YY_BREAK
case 53:
YY_RULE_SETUP
-#line 121 "lex.l"
+#line 122 "lex.l"
{ return kw_ObjectDescriptor; }
YY_BREAK
case 54:
YY_RULE_SETUP
-#line 122 "lex.l"
+#line 123 "lex.l"
{ return kw_PATTERN; }
YY_BREAK
case 55:
YY_RULE_SETUP
-#line 123 "lex.l"
+#line 124 "lex.l"
{ return kw_PDV; }
YY_BREAK
case 56:
YY_RULE_SETUP
-#line 124 "lex.l"
+#line 125 "lex.l"
{ return kw_PLUS_INFINITY; }
YY_BREAK
case 57:
YY_RULE_SETUP
-#line 125 "lex.l"
+#line 126 "lex.l"
{ return kw_PRESENT; }
YY_BREAK
case 58:
YY_RULE_SETUP
-#line 126 "lex.l"
+#line 127 "lex.l"
{ return kw_PRIVATE; }
YY_BREAK
case 59:
YY_RULE_SETUP
-#line 127 "lex.l"
+#line 128 "lex.l"
{ return kw_PrintableString; }
YY_BREAK
case 60:
YY_RULE_SETUP
-#line 128 "lex.l"
+#line 129 "lex.l"
{ return kw_REAL; }
YY_BREAK
case 61:
YY_RULE_SETUP
-#line 129 "lex.l"
+#line 130 "lex.l"
{ return kw_RELATIVE_OID; }
YY_BREAK
case 62:
YY_RULE_SETUP
-#line 130 "lex.l"
+#line 131 "lex.l"
{ return kw_SEQUENCE; }
YY_BREAK
case 63:
YY_RULE_SETUP
-#line 131 "lex.l"
+#line 132 "lex.l"
{ return kw_SET; }
YY_BREAK
case 64:
YY_RULE_SETUP
-#line 132 "lex.l"
+#line 133 "lex.l"
{ return kw_SIZE; }
YY_BREAK
case 65:
YY_RULE_SETUP
-#line 133 "lex.l"
+#line 134 "lex.l"
{ return kw_STRING; }
YY_BREAK
case 66:
YY_RULE_SETUP
-#line 134 "lex.l"
+#line 135 "lex.l"
{ return kw_SYNTAX; }
YY_BREAK
case 67:
YY_RULE_SETUP
-#line 135 "lex.l"
+#line 136 "lex.l"
{ return kw_T61String; }
YY_BREAK
case 68:
YY_RULE_SETUP
-#line 136 "lex.l"
+#line 137 "lex.l"
{ return kw_TAGS; }
YY_BREAK
case 69:
YY_RULE_SETUP
-#line 137 "lex.l"
+#line 138 "lex.l"
{ return kw_TRUE; }
YY_BREAK
case 70:
YY_RULE_SETUP
-#line 138 "lex.l"
+#line 139 "lex.l"
{ return kw_TYPE_IDENTIFIER; }
YY_BREAK
case 71:
YY_RULE_SETUP
-#line 139 "lex.l"
+#line 140 "lex.l"
{ return kw_TeletexString; }
YY_BREAK
case 72:
YY_RULE_SETUP
-#line 140 "lex.l"
+#line 141 "lex.l"
{ return kw_UNION; }
YY_BREAK
case 73:
YY_RULE_SETUP
-#line 141 "lex.l"
+#line 142 "lex.l"
{ return kw_UNIQUE; }
YY_BREAK
case 74:
YY_RULE_SETUP
-#line 142 "lex.l"
+#line 143 "lex.l"
{ return kw_UNIVERSAL; }
YY_BREAK
case 75:
YY_RULE_SETUP
-#line 143 "lex.l"
+#line 144 "lex.l"
{ return kw_UTCTime; }
YY_BREAK
case 76:
YY_RULE_SETUP
-#line 144 "lex.l"
+#line 145 "lex.l"
{ return kw_UTF8String; }
YY_BREAK
case 77:
YY_RULE_SETUP
-#line 145 "lex.l"
+#line 146 "lex.l"
{ return kw_UniversalString; }
YY_BREAK
case 78:
YY_RULE_SETUP
-#line 146 "lex.l"
+#line 147 "lex.l"
{ return kw_VideotexString; }
YY_BREAK
case 79:
YY_RULE_SETUP
-#line 147 "lex.l"
+#line 148 "lex.l"
{ return kw_VisibleString; }
YY_BREAK
case 80:
YY_RULE_SETUP
-#line 148 "lex.l"
+#line 149 "lex.l"
{ return kw_WITH; }
YY_BREAK
case 81:
YY_RULE_SETUP
-#line 149 "lex.l"
+#line 150 "lex.l"
{ return *yytext; }
YY_BREAK
case 82:
YY_RULE_SETUP
-#line 150 "lex.l"
+#line 151 "lex.l"
{ return *yytext; }
YY_BREAK
case 83:
YY_RULE_SETUP
-#line 151 "lex.l"
+#line 152 "lex.l"
{ return *yytext; }
YY_BREAK
case 84:
YY_RULE_SETUP
-#line 152 "lex.l"
+#line 153 "lex.l"
{ return EEQUAL; }
YY_BREAK
case 85:
YY_RULE_SETUP
-#line 153 "lex.l"
+#line 154 "lex.l"
{
int c, start_lineno = lineno;
int f = 0;
@@ -1559,7 +1561,7 @@ YY_RULE_SETUP
YY_BREAK
case 86:
YY_RULE_SETUP
-#line 172 "lex.l"
+#line 173 "lex.l"
{
int c, start_lineno = lineno;
int level = 1;
@@ -1603,7 +1605,7 @@ YY_RULE_SETUP
YY_BREAK
case 87:
YY_RULE_SETUP
-#line 212 "lex.l"
+#line 213 "lex.l"
{
int start_lineno = lineno;
int c;
@@ -1651,9 +1653,9 @@ YY_RULE_SETUP
YY_BREAK
case 88:
YY_RULE_SETUP
-#line 257 "lex.l"
+#line 258 "lex.l"
{ char *e, *y = yytext;
- yylval.constant = strtol((const char *)yytext,
+ yylval.constant = strtoll((const char *)yytext,
&e, 0);
if(e == y)
lex_error_message("malformed constant (%s)", yytext);
@@ -1663,7 +1665,7 @@ YY_RULE_SETUP
YY_BREAK
case 89:
YY_RULE_SETUP
-#line 265 "lex.l"
+#line 266 "lex.l"
{
yylval.name = estrdup ((const char *)yytext);
return IDENTIFIER;
@@ -1671,36 +1673,36 @@ YY_RULE_SETUP
YY_BREAK
case 90:
YY_RULE_SETUP
-#line 269 "lex.l"
+#line 270 "lex.l"
;
YY_BREAK
case 91:
/* rule 91 can match eol */
YY_RULE_SETUP
-#line 270 "lex.l"
+#line 271 "lex.l"
{ ++lineno; }
YY_BREAK
case 92:
YY_RULE_SETUP
-#line 271 "lex.l"
+#line 272 "lex.l"
{ return ELLIPSIS; }
YY_BREAK
case 93:
YY_RULE_SETUP
-#line 272 "lex.l"
+#line 273 "lex.l"
{ return RANGE; }
YY_BREAK
case 94:
YY_RULE_SETUP
-#line 273 "lex.l"
+#line 274 "lex.l"
{ lex_error_message("Ignoring char(%c)\n", *yytext); }
YY_BREAK
case 95:
YY_RULE_SETUP
-#line 274 "lex.l"
+#line 275 "lex.l"
ECHO;
YY_BREAK
-#line 1704 "lex.c"
+#line 1706 "lex.c"
case YY_STATE_EOF(INITIAL):
yyterminate();
@@ -2697,17 +2699,15 @@ void yyfree (void * ptr )
#define YYTABLES_NAME "yytables"
-#line 274 "lex.l"
+#line 275 "lex.l"
-#ifndef yywrap /* XXX */
int
yywrap ()
{
return 1;
}
-#endif
void
lex_error_message (const char *format, ...)
diff --git a/lib/asn1/lex.l b/lib/asn1/lex.l
index 2d32020266c8..9f38665a70f9 100644
--- a/lib/asn1/lex.l
+++ b/lib/asn1/lex.l
@@ -1,6 +1,6 @@
%{
/*
- * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 - 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -40,6 +40,7 @@
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
+#include <stdint.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
@@ -255,7 +256,7 @@ WITH { return kw_WITH; }
}
-?0x[0-9A-Fa-f]+|-?[0-9]+ { char *e, *y = yytext;
- yylval.constant = strtol((const char *)yytext,
+ yylval.constant = strtoll((const char *)yytext,
&e, 0);
if(e == y)
lex_error_message("malformed constant (%s)", yytext);
@@ -273,13 +274,11 @@ WITH { return kw_WITH; }
. { lex_error_message("Ignoring char(%c)\n", *yytext); }
%%
-#ifndef yywrap /* XXX */
int
yywrap ()
{
return 1;
}
-#endif
void
lex_error_message (const char *format, ...)
diff --git a/lib/asn1/libasn1-exports.def b/lib/asn1/libasn1-exports.def
index 6dfb93ea054c..62f4337ad89f 100644
--- a/lib/asn1/libasn1-exports.def
+++ b/lib/asn1/libasn1-exports.def
@@ -58,9 +58,15 @@ EXPORTS
asn1_oid_id_ecPublicKey DATA
asn1_oid_id_ec_group_secp160r1 DATA
asn1_oid_id_ec_group_secp160r2 DATA
+ asn1_oid_id_ec_group_secp224r1 DATA
asn1_oid_id_ec_group_secp256r1 DATA
+ asn1_oid_id_ec_group_secp384r1 DATA
+ asn1_oid_id_ec_group_secp521r1 DATA
asn1_oid_id_ecdsa_with_SHA1 DATA
+ asn1_oid_id_ecdsa_with_SHA224 DATA
asn1_oid_id_ecdsa_with_SHA256 DATA
+ asn1_oid_id_ecdsa_with_SHA384 DATA
+ asn1_oid_id_ecdsa_with_SHA512 DATA
asn1_oid_id_heim_rsa_pkcs1_x509 DATA
asn1_oid_id_ms_cert_enroll_domaincontroller DATA
asn1_oid_id_ms_client_authentication DATA
@@ -299,6 +305,8 @@ EXPORTS
copy_IssuerAndSerialNumber
copy_KDCDHKeyInfo
copy_KDCDHKeyInfo_Win2k
+ copy_KDCFastCookie
+ copy_KDCFastState
copy_KDCOptions
copy_KDC_REP
copy_KDC_REQ
@@ -346,8 +354,6 @@ EXPORTS
copy_OriginatorInfo
copy_OtherName
copy_PADATA_TYPE
- copy_PA_ClientCanonicalized
- copy_PA_ClientCanonicalizedNames
copy_PA_DATA
copy_PA_ENC_SAM_RESPONSE_ENC
copy_PA_ENC_TS_ENC
@@ -551,6 +557,8 @@ EXPORTS
decode_IssuerAndSerialNumber
decode_KDCDHKeyInfo
decode_KDCDHKeyInfo_Win2k
+ decode_KDCFastCookie
+ decode_KDCFastState
decode_KDCOptions
decode_KDC_REP
decode_KDC_REQ
@@ -598,8 +606,6 @@ EXPORTS
decode_OriginatorInfo
decode_OtherName
decode_PADATA_TYPE
- decode_PA_ClientCanonicalized
- decode_PA_ClientCanonicalizedNames
decode_PA_DATA
decode_PA_ENC_SAM_RESPONSE_ENC
decode_PA_ENC_TS_ENC
@@ -699,11 +705,13 @@ EXPORTS
der_copy_heim_integer
der_copy_ia5_string
der_copy_integer
+ der_copy_integer64
der_copy_octet_string
der_copy_oid
der_copy_printable_string
der_copy_universal_string
der_copy_unsigned
+ der_copy_unsigned64
der_copy_utctime
der_copy_utf8string
der_copy_visible_string
@@ -714,11 +722,13 @@ EXPORTS
der_free_heim_integer
der_free_ia5_string
der_free_integer
+ der_free_integer64
der_free_octet_string
der_free_oid
der_free_printable_string
der_free_universal_string
der_free_unsigned
+ der_free_unsigned64
der_free_utctime
der_free_utf8string
der_free_visible_string
@@ -732,6 +742,7 @@ EXPORTS
der_get_heim_integer
der_get_ia5_string
der_get_integer
+ der_get_integer64
der_get_length
der_get_octet_string
der_get_octet_string_ber
@@ -744,6 +755,7 @@ EXPORTS
der_get_type_num
der_get_universal_string
der_get_unsigned
+ der_get_unsigned64
der_get_utctime
der_get_utf8string
der_get_visible_string
@@ -763,6 +775,7 @@ EXPORTS
der_length_heim_integer
der_length_ia5_string
der_length_integer
+ der_length_integer64
der_length_len
der_length_octet_string
der_length_oid
@@ -770,6 +783,7 @@ EXPORTS
der_length_tag
der_length_universal_string
der_length_unsigned
+ der_length_unsigned64
der_length_utctime
der_length_utf8string
der_length_visible_string
@@ -789,6 +803,7 @@ EXPORTS
der_put_heim_integer
der_put_ia5_string
der_put_integer
+ der_put_integer64
der_put_length
der_put_length_and_tag
der_put_octet_string
@@ -797,6 +812,7 @@ EXPORTS
der_put_tag
der_put_universal_string
der_put_unsigned
+ der_put_unsigned64
der_put_utctime
der_put_utf8string
der_put_visible_string
@@ -911,6 +927,8 @@ EXPORTS
encode_IssuerAndSerialNumber
encode_KDCDHKeyInfo
encode_KDCDHKeyInfo_Win2k
+ encode_KDCFastCookie
+ encode_KDCFastState
encode_KDCOptions
encode_KDC_REP
encode_KDC_REQ
@@ -958,8 +976,6 @@ EXPORTS
encode_OriginatorInfo
encode_OtherName
encode_PADATA_TYPE
- encode_PA_ClientCanonicalized
- encode_PA_ClientCanonicalizedNames
encode_PA_DATA
encode_PA_ENC_SAM_RESPONSE_ENC
encode_PA_ENC_TS_ENC
@@ -1163,6 +1179,8 @@ EXPORTS
free_IssuerAndSerialNumber
free_KDCDHKeyInfo
free_KDCDHKeyInfo_Win2k
+ free_KDCFastCookie
+ free_KDCFastState
free_KDCOptions
free_KDC_REP
free_KDC_REQ
@@ -1210,8 +1228,6 @@ EXPORTS
free_OriginatorInfo
free_OtherName
free_PADATA_TYPE
- free_PA_ClientCanonicalized
- free_PA_ClientCanonicalizedNames
free_PA_DATA
free_PA_ENC_SAM_RESPONSE_ENC
free_PA_ENC_TS_ENC
@@ -1426,6 +1442,8 @@ EXPORTS
length_IssuerAndSerialNumber
length_KDCDHKeyInfo
length_KDCDHKeyInfo_Win2k
+ length_KDCFastCookie
+ length_KDCFastState
length_KDCOptions
length_KDC_REP
length_KDC_REQ
@@ -1473,8 +1491,6 @@ EXPORTS
length_OriginatorInfo
length_OtherName
length_PADATA_TYPE
- length_PA_ClientCanonicalized
- length_PA_ClientCanonicalizedNames
length_PA_DATA
length_PA_ENC_SAM_RESPONSE_ENC
length_PA_ENC_TS_ENC
diff --git a/lib/asn1/main.c b/lib/asn1/main.c
index f22dc8792c27..80038cd37fdb 100644
--- a/lib/asn1/main.c
+++ b/lib/asn1/main.c
@@ -35,8 +35,6 @@
#include <getarg.h>
#include "lex.h"
-RCSID("$Id$");
-
extern FILE *yyin;
static getarg_strings preserve;
@@ -62,24 +60,31 @@ seq_type(const char *p)
return 0;
}
+const char *fuzzer_string = "";
+int fuzzer_flag;
int support_ber;
int template_flag;
int rfc1510_bitstring;
int one_code_file;
char *option_file;
+int parse_units_flag = 1;
+char *type_file_string = "krb5-types.h";
int version_flag;
int help_flag;
struct getargs args[] = {
- { "template", 0, arg_flag, &template_flag },
- { "encode-rfc1510-bit-string", 0, arg_flag, &rfc1510_bitstring },
- { "decode-dce-ber", 0, arg_flag, &support_ber },
- { "support-ber", 0, arg_flag, &support_ber },
- { "preserve-binary", 0, arg_strings, &preserve },
- { "sequence", 0, arg_strings, &seq },
- { "one-code-file", 0, arg_flag, &one_code_file },
- { "option-file", 0, arg_string, &option_file },
- { "version", 0, arg_flag, &version_flag },
- { "help", 0, arg_flag, &help_flag }
+ { "fuzzer", 0, arg_flag, &fuzzer_flag, NULL, NULL },
+ { "template", 0, arg_flag, &template_flag, NULL, NULL },
+ { "encode-rfc1510-bit-string", 0, arg_flag, &rfc1510_bitstring, NULL, NULL },
+ { "decode-dce-ber", 0, arg_flag, &support_ber, NULL, NULL },
+ { "support-ber", 0, arg_flag, &support_ber, NULL, NULL },
+ { "preserve-binary", 0, arg_strings, &preserve, NULL, NULL },
+ { "sequence", 0, arg_strings, &seq, NULL, NULL },
+ { "one-code-file", 0, arg_flag, &one_code_file, NULL, NULL },
+ { "option-file", 0, arg_string, &option_file, NULL, NULL },
+ { "parse-units", 0, arg_negative_flag, &parse_units_flag, NULL, NULL },
+ { "type-file", 0, arg_string, &type_file_string, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
int num_args = sizeof(args) / sizeof(args[0]);
@@ -100,7 +105,7 @@ main(int argc, char **argv)
const char *name = NULL;
int optidx = 0;
char **arg = NULL;
- size_t len = 0, i;
+ int len = 0, i;
setprogname(argv[0]);
if(getarg(args, num_args, argc, argv, &optidx))
@@ -180,6 +185,16 @@ main(int argc, char **argv)
}
}
+ if (fuzzer_flag) {
+ if (!template_flag) {
+ printf("can't do fuzzer w/o --template");
+ exit(1);
+ }
+#ifdef ASN1_FUZZER
+ fuzzer_string = "_fuzzer";
+#endif
+ }
+
init_generate (file, name);
diff --git a/lib/asn1/pkinit.asn1 b/lib/asn1/pkinit.asn1
index f36ebf0b32fb..325752f41fae 100644
--- a/lib/asn1/pkinit.asn1
+++ b/lib/asn1/pkinit.asn1
@@ -17,6 +17,8 @@ id-pkrkeydata OBJECT IDENTIFIER ::= { id-pkinit 3 }
id-pkekuoid OBJECT IDENTIFIER ::= { id-pkinit 4 }
id-pkkdcekuoid OBJECT IDENTIFIER ::= { id-pkinit 5 }
+id-apple-system-id OBJECT IDENTIFIER ::= { 1 2 840 113635 100 4 4 }
+
id-pkinit-kdf OBJECT IDENTIFIER ::= { id-pkinit 6 }
id-pkinit-kdf-ah-sha1 OBJECT IDENTIFIER ::= { id-pkinit-kdf 1 }
id-pkinit-kdf-ah-sha256 OBJECT IDENTIFIER ::= { id-pkinit-kdf 2 }
diff --git a/lib/asn1/rfc2459.asn1 b/lib/asn1/rfc2459.asn1
index 5df9e41fffd2..3b8bab7c73bf 100644
--- a/lib/asn1/rfc2459.asn1
+++ b/lib/asn1/rfc2459.asn1
@@ -88,10 +88,22 @@ id-ecMQV OBJECT IDENTIFIER ::= {
iso(1) identified-organization(3) certicom(132) schemes(1)
ecmqv(13) }
+id-ecdsa-with-SHA512 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+ ecdsa-with-SHA2(3) 4 }
+
+id-ecdsa-with-SHA384 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+ ecdsa-with-SHA2(3) 3 }
+
id-ecdsa-with-SHA256 OBJECT IDENTIFIER ::= {
iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
ecdsa-with-SHA2(3) 2 }
+id-ecdsa-with-SHA224 OBJECT IDENTIFIER ::= {
+ iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4)
+ ecdsa-with-SHA2(3) 1 }
+
id-ecdsa-with-SHA1 OBJECT IDENTIFIER ::= {
iso(1) member-body(2) us(840) ansi-X9-62(10045) signatures(4) 1 }
@@ -107,6 +119,15 @@ id-ec-group-secp160r1 OBJECT IDENTIFIER ::= {
id-ec-group-secp160r2 OBJECT IDENTIFIER ::= {
iso(1) identified-organization(3) certicom(132) 0 30 }
+id-ec-group-secp224r1 OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) certicom(132) 0 33 }
+
+id-ec-group-secp384r1 OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) certicom(132) 0 34 }
+
+id-ec-group-secp521r1 OBJECT IDENTIFIER ::= {
+ iso(1) identified-organization(3) certicom(132) 0 35 }
+
-- DSA
id-x9-57 OBJECT IDENTIFIER ::= {
@@ -128,6 +149,8 @@ id-at-stateOrProvinceName OBJECT IDENTIFIER ::= { id-x520-at 8 }
id-at-streetAddress OBJECT IDENTIFIER ::= { id-x520-at 9 }
id-at-organizationName OBJECT IDENTIFIER ::= { id-x520-at 10 }
id-at-organizationalUnitName OBJECT IDENTIFIER ::= { id-x520-at 11 }
+id-at-title OBJECT IDENTIFIER ::= { id-x520-at 12 }
+id-at-description OBJECT IDENTIFIER ::= { id-x520-at 13 }
id-at-name OBJECT IDENTIFIER ::= { id-x520-at 41 }
id-at-givenName OBJECT IDENTIFIER ::= { id-x520-at 42 }
id-at-initials OBJECT IDENTIFIER ::= { id-x520-at 43 }
@@ -239,7 +262,7 @@ ValidationParms ::= SEQUENCE {
DomainParameters ::= SEQUENCE {
p INTEGER, -- odd prime, p=jq +1
g INTEGER, -- generator, g
- q INTEGER, -- factor of p-1
+ q INTEGER OPTIONAL, -- factor of p-1
j INTEGER OPTIONAL, -- subgroup factor
validationParms ValidationParms OPTIONAL -- ValidationParms
}
diff --git a/lib/asn1/roken_rename.h b/lib/asn1/roken_rename.h
new file mode 100644
index 000000000000..76e108a5574f
--- /dev/null
+++ b/lib/asn1/roken_rename.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __roken_rename_h__
+#define __roken_rename_h__
+
+#ifndef HAVE_STRTOLL
+#define strtoll rk_strtoll
+#endif
+#ifndef HAVE_STRTOULL
+#define strtoull rk_strtoull
+#endif
+
+#endif /* __roken_rename_h__ */
diff --git a/lib/asn1/symbol.c b/lib/asn1/symbol.c
index b05f68fa74a9..a090ee95c7ab 100644
--- a/lib/asn1/symbol.c
+++ b/lib/asn1/symbol.c
@@ -78,7 +78,7 @@ addsym(char *name)
key.name = name;
s = (Symbol *) hashtabsearch(htab, (void *) &key);
if (s == NULL) {
- s = (Symbol *) emalloc(sizeof(*s));
+ s = (Symbol *) ecalloc(1, sizeof(*s));
s->name = name;
s->gen_name = estrdup(name);
output_name(s->gen_name);
diff --git a/lib/asn1/symbol.h b/lib/asn1/symbol.h
index a39c8f46512d..97b4db7263a7 100644
--- a/lib/asn1/symbol.h
+++ b/lib/asn1/symbol.h
@@ -78,7 +78,7 @@ struct value {
} type;
union {
int booleanvalue;
- int integervalue;
+ int64_t integervalue;
char *stringvalue;
struct objid *objectidentifiervalue;
} u;
@@ -109,8 +109,12 @@ struct tagtype {
};
struct range {
- int min;
- int max;
+ /*
+ * We can't represent unsigned 64-bit ranges because max might be
+ * negative...
+ */
+ int64_t min;
+ int64_t max;
};
enum ctype { CT_CONTENTS, CT_USER } ;
@@ -125,6 +129,7 @@ struct type {
struct tagtype tag;
struct range *range;
struct constraint_spec *constraint;
+ unsigned long id;
};
typedef struct type Type;
diff --git a/lib/asn1/template.c b/lib/asn1/template.c
index 3e0b6932357e..fe0dc6c2f773 100644
--- a/lib/asn1/template.c
+++ b/lib/asn1/template.c
@@ -3,7 +3,7 @@
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
- * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ * Portions Copyright (c) 2009 - 2010 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -36,17 +36,7 @@
#include "der_locl.h"
#include <com_err.h>
-#if 0
-#define ABORT_ON_ERROR() abort()
-#else
-#define ABORT_ON_ERROR() do { } while(0)
-#endif
-
-#define DPOC(data,offset) ((const void *)(((const unsigned char *)data) + offset))
-#define DPO(data,offset) ((void *)(((unsigned char *)data) + offset))
-
-
-static struct asn1_type_func prim[] = {
+struct asn1_type_func asn1_template_prim[A1T_NUM_ENTRY] = {
#define el(name, type) { \
(asn1_type_encode)der_put_##name, \
(asn1_type_decode)der_get_##name, \
@@ -66,7 +56,9 @@ static struct asn1_type_func prim[] = {
el(integer, int),
el(heim_integer, heim_integer),
el(integer, int),
+ el(integer64, int64_t),
el(unsigned, unsigned),
+ el(unsigned64, uint64_t),
el(general_string, heim_general_string),
el(octet_string, heim_octet_string),
elber(octet_string, heim_octet_string),
@@ -89,8 +81,8 @@ static struct asn1_type_func prim[] = {
#undef elber
};
-static size_t
-sizeofType(const struct asn1_template *t)
+size_t
+_asn1_sizeofType(const struct asn1_template *t)
{
return t->offset;
}
@@ -106,8 +98,8 @@ sizeofType(const struct asn1_template *t)
*/
static void
-bmember_get_bit(const unsigned char *p, void *data,
- unsigned int bit, size_t size)
+_asn1_bmember_get_bit(const unsigned char *p, void *data,
+ unsigned int bit, size_t size)
{
unsigned int localbit = bit % 8;
if ((*p >> (7 - localbit)) & 1) {
@@ -119,8 +111,8 @@ bmember_get_bit(const unsigned char *p, void *data,
}
}
-static int
-bmember_isset_bit(const void *data, unsigned int bit, size_t size)
+int
+_asn1_bmember_isset_bit(const void *data, unsigned int bit, size_t size)
{
#ifdef WORDS_BIGENDIAN
if ((*(unsigned int *)data) & (1 << ((size * 8) - bit - 1)))
@@ -133,13 +125,13 @@ bmember_isset_bit(const void *data, unsigned int bit, size_t size)
#endif
}
-static void
-bmember_put_bit(unsigned char *p, const void *data, unsigned int bit,
- size_t size, unsigned int *bitset)
+void
+_asn1_bmember_put_bit(unsigned char *p, const void *data, unsigned int bit,
+ size_t size, unsigned int *bitset)
{
unsigned int localbit = bit % 8;
- if (bmember_isset_bit(data, bit, size)) {
+ if (_asn1_bmember_isset_bit(data, bit, size)) {
*p |= (1 << (7 - localbit));
if (*bitset == 0)
*bitset = (7 - localbit) + 1;
@@ -166,19 +158,19 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
switch (t->tt & A1_OP_MASK) {
case A1_OP_TYPE:
case A1_OP_TYPE_EXTERN: {
- size_t newsize, size;
+ size_t newsize, elsize;
void *el = DPO(data, t->offset);
void **pel = (void **)el;
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
- size = sizeofType(t->ptr);
+ elsize = _asn1_sizeofType(t->ptr);
} else {
const struct asn1_type_func *f = t->ptr;
- size = f->size;
+ elsize = f->size;
}
if (t->tt & A1_FLAG_OPTIONAL) {
- *pel = calloc(1, size);
+ *pel = calloc(1, elsize);
if (*pel == NULL)
return ENOMEM;
el = *pel;
@@ -250,7 +242,7 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
if (t->tt & A1_FLAG_OPTIONAL) {
void **el = (void **)data;
- size_t ellen = sizeofType(t->ptr);
+ size_t ellen = _asn1_sizeofType(t->ptr);
*el = calloc(1, ellen);
if (*el == NULL)
@@ -262,8 +254,13 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
if (ret)
return ret;
- if (newsize != datalen)
+ if (is_indefinite) {
+ /* If we use indefinite encoding, the newsize is the datasize. */
+ datalen = newsize;
+ } else if (newsize != datalen) {
+ /* Check for hidden data that might be after the real tag */
return ASN1_EXTRA_DATA;
+ }
len -= datalen;
p += datalen;
@@ -300,12 +297,12 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
if (flags & A1_PF_INDEFINTE)
type++;
- if (type >= sizeof(prim)/sizeof(prim[0])) {
+ if (type >= sizeof(asn1_template_prim)/sizeof(asn1_template_prim[0])) {
ABORT_ON_ERROR();
return ASN1_PARSE_ERROR;
}
- ret = (prim[type].decode)(p, len, el, &newsize);
+ ret = (asn1_template_prim[type].decode)(p, len, el, &newsize);
if (ret)
return ret;
p += newsize; len -= newsize;
@@ -316,7 +313,7 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
case A1_OP_SEQOF: {
struct template_of *el = DPO(data, t->offset);
size_t newsize;
- size_t ellen = sizeofType(t->ptr);
+ size_t ellen = _asn1_sizeofType(t->ptr);
size_t vallength = 0;
while (len > 0) {
@@ -345,19 +342,19 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
}
case A1_OP_BMEMBER: {
const struct asn1_template *bmember = t->ptr;
- size_t size = bmember->offset;
- size_t elements = A1_HEADER_LEN(bmember);
+ size_t bsize = bmember->offset;
+ size_t belements = A1_HEADER_LEN(bmember);
size_t pos = 0;
bmember++;
- memset(data, 0, size);
+ memset(data, 0, bsize);
if (len < 1)
return ASN1_OVERRUN;
p++; len--;
- while (elements && len) {
+ while (belements && len) {
while (bmember->offset / 8 > pos / 8) {
if (len < 1)
break;
@@ -365,8 +362,8 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
pos += 8;
}
if (len) {
- bmember_get_bit(p, data, bmember->offset, size);
- elements--; bmember++;
+ _asn1_bmember_get_bit(p, data, bmember->offset, bsize);
+ belements--; bmember++;
}
}
len = 0;
@@ -378,6 +375,9 @@ _asn1_decode(const struct asn1_template *t, unsigned flags,
size_t datalen;
unsigned int i;
+ /* provide a saner value as default, we should have a NO element value */
+ *element = 1;
+
for (i = 1; i < A1_HEADER_LEN(choice) + 1; i++) {
/* should match first tag instead, store it in choice.tt */
ret = _asn1_decode(choice[i].ptr, 0, p, len,
@@ -513,12 +513,12 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
size_t newsize;
const void *el = DPOC(data, t->offset);
- if (type > sizeof(prim)/sizeof(prim[0])) {
+ if (type >= sizeof(asn1_template_prim)/sizeof(asn1_template_prim[0])) {
ABORT_ON_ERROR();
return ASN1_PARSE_ERROR;
}
- ret = (prim[type].encode)(p, len, el, &newsize);
+ ret = (asn1_template_prim[type].encode)(p, len, el, &newsize);
if (ret)
return ret;
p -= newsize; len -= newsize;
@@ -527,8 +527,8 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
}
case A1_OP_SETOF: {
const struct template_of *el = DPOC(data, t->offset);
- size_t ellen = sizeofType(t->ptr);
- struct heim_octet_string *val;
+ size_t ellen = _asn1_sizeofType(t->ptr);
+ heim_octet_string *val;
unsigned char *elptr = el->val;
size_t i, totallen;
@@ -538,7 +538,7 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
if (el->len > UINT_MAX/sizeof(val[0]))
return ERANGE;
- val = malloc(sizeof(val[0]) * el->len);
+ val = calloc(el->len, sizeof(val[0]));
if (val == NULL)
return ENOMEM;
@@ -547,7 +547,13 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
size_t l;
val[i].length = _asn1_length(t->ptr, elptr);
- val[i].data = malloc(val[i].length);
+ if (val[i].length) {
+ val[i].data = malloc(val[i].length);
+ if (val[i].data == NULL) {
+ ret = ENOMEM;
+ break;
+ }
+ }
ret = _asn1_encode(t->ptr, DPO(val[i].data, val[i].length - 1),
val[i].length, elptr, &l);
@@ -565,9 +571,8 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
if (ret == 0 && totallen > len)
ret = ASN1_OVERFLOW;
if (ret) {
- do {
+ for (i = 0; i < el->len; i++)
free(val[i].data);
- } while(i-- > 0);
free(val);
return ret;
}
@@ -589,7 +594,7 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
}
case A1_OP_SEQOF: {
struct template_of *el = DPO(data, t->offset);
- size_t ellen = sizeofType(t->ptr);
+ size_t ellen = _asn1_sizeofType(t->ptr);
size_t newsize;
unsigned int i;
unsigned char *elptr = el->val;
@@ -613,21 +618,21 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
}
case A1_OP_BMEMBER: {
const struct asn1_template *bmember = t->ptr;
- size_t size = bmember->offset;
- size_t elements = A1_HEADER_LEN(bmember);
+ size_t bsize = bmember->offset;
+ size_t belements = A1_HEADER_LEN(bmember);
size_t pos;
unsigned char c = 0;
unsigned int bitset = 0;
int rfc1510 = (bmember->tt & A1_HBF_RFC1510);
- bmember += elements;
+ bmember += belements;
if (rfc1510)
pos = 31;
else
pos = bmember->offset;
- while (elements && len) {
+ while (belements && len) {
while (bmember->offset / 8 < pos / 8) {
if (rfc1510 || bitset || c) {
if (len < 1)
@@ -637,8 +642,8 @@ _asn1_encode(const struct asn1_template *t, unsigned char *p, size_t len, const
c = 0;
pos -= 8;
}
- bmember_put_bit(&c, data, bmember->offset, size, &bitset);
- elements--; bmember--;
+ _asn1_bmember_put_bit(&c, data, bmember->offset, bsize, &bitset);
+ belements--; bmember--;
}
if (rfc1510 || bitset) {
if (len < 1)
@@ -747,17 +752,17 @@ _asn1_length(const struct asn1_template *t, const void *data)
unsigned int type = A1_PARSE_TYPE(t->tt);
const void *el = DPOC(data, t->offset);
- if (type > sizeof(prim)/sizeof(prim[0])) {
+ if (type >= sizeof(asn1_template_prim)/sizeof(asn1_template_prim[0])) {
ABORT_ON_ERROR();
break;
}
- ret += (prim[type].length)(el);
+ ret += (asn1_template_prim[type].length)(el);
break;
}
case A1_OP_SETOF:
case A1_OP_SEQOF: {
const struct template_of *el = DPOC(data, t->offset);
- size_t ellen = sizeofType(t->ptr);
+ size_t ellen = _asn1_sizeofType(t->ptr);
const unsigned char *element = el->val;
unsigned int i;
@@ -771,7 +776,7 @@ _asn1_length(const struct asn1_template *t, const void *data)
case A1_OP_BMEMBER: {
const struct asn1_template *bmember = t->ptr;
size_t size = bmember->offset;
- size_t elements = A1_HEADER_LEN(bmember);
+ size_t belements = A1_HEADER_LEN(bmember);
int rfc1510 = (bmember->tt & A1_HBF_RFC1510);
if (rfc1510) {
@@ -780,14 +785,14 @@ _asn1_length(const struct asn1_template *t, const void *data)
ret += 1;
- bmember += elements;
+ bmember += belements;
- while (elements) {
- if (bmember_isset_bit(data, bmember->offset, size)) {
+ while (belements) {
+ if (_asn1_bmember_isset_bit(data, bmember->offset, size)) {
ret += (bmember->offset / 8) + 1;
break;
}
- elements--; bmember--;
+ belements--; bmember--;
}
}
break;
@@ -855,11 +860,11 @@ _asn1_free(const struct asn1_template *t, void *data)
unsigned int type = A1_PARSE_TYPE(t->tt);
void *el = DPO(data, t->offset);
- if (type > sizeof(prim)/sizeof(prim[0])) {
+ if (type >= sizeof(asn1_template_prim)/sizeof(asn1_template_prim[0])) {
ABORT_ON_ERROR();
break;
}
- (prim[type].release)(el);
+ (asn1_template_prim[type].release)(el);
break;
}
case A1_OP_TAG: {
@@ -882,7 +887,7 @@ _asn1_free(const struct asn1_template *t, void *data)
case A1_OP_SETOF:
case A1_OP_SEQOF: {
struct template_of *el = DPO(data, t->offset);
- size_t ellen = sizeofType(t->ptr);
+ size_t ellen = _asn1_sizeofType(t->ptr);
unsigned char *element = el->val;
unsigned int i;
@@ -947,7 +952,7 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
size_t size;
if ((t->tt & A1_OP_MASK) == A1_OP_TYPE) {
- size = sizeofType(t->ptr);
+ size = _asn1_sizeofType(t->ptr);
} else {
const struct asn1_type_func *f = t->ptr;
size = f->size;
@@ -985,11 +990,11 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
const void *fel = DPOC(from, t->offset);
void *tel = DPO(to, t->offset);
- if (type > sizeof(prim)/sizeof(prim[0])) {
+ if (type >= sizeof(asn1_template_prim)/sizeof(asn1_template_prim[0])) {
ABORT_ON_ERROR();
return ASN1_PARSE_ERROR;
}
- ret = (prim[type].copy)(fel, tel);
+ ret = (asn1_template_prim[type].copy)(fel, tel);
if (ret)
return ret;
break;
@@ -1012,14 +1017,14 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
}
from = *fel;
- to = *tel = calloc(1, sizeofType(t->ptr));
+ to = *tel = calloc(1, _asn1_sizeofType(t->ptr));
if (to == NULL)
return ENOMEM;
}
ret = _asn1_copy(t->ptr, from, to);
if (ret) {
- if (t->tt & A1_FLAG_OPTIONAL) {
+ if (tel) {
free(*tel);
*tel = NULL;
}
@@ -1035,7 +1040,7 @@ _asn1_copy(const struct asn1_template *t, const void *from, void *to)
case A1_OP_SEQOF: {
const struct template_of *fel = DPOC(from, t->offset);
struct template_of *tel = DPO(to, t->offset);
- size_t ellen = sizeofType(t->ptr);
+ size_t ellen = _asn1_sizeofType(t->ptr);
unsigned int i;
tel->val = calloc(fel->len, ellen);
@@ -1097,10 +1102,8 @@ _asn1_decode_top(const struct asn1_template *t, unsigned flags, const unsigned c
int ret;
memset(data, 0, t->offset);
ret = _asn1_decode(t, flags, p, len, data, size);
- if (ret) {
- _asn1_free(t, data);
- memset(data, 0, t->offset);
- }
+ if (ret)
+ _asn1_free_top(t, data);
return ret;
}
@@ -1111,9 +1114,15 @@ _asn1_copy_top(const struct asn1_template *t, const void *from, void *to)
int ret;
memset(to, 0, t->offset);
ret = _asn1_copy(t, from, to);
- if (ret) {
- _asn1_free(t, to);
- memset(to, 0, t->offset);
- }
+ if (ret)
+ _asn1_free_top(t, to);
+
return ret;
}
+
+void
+_asn1_free_top(const struct asn1_template *t, void *data)
+{
+ _asn1_free(t, data);
+ memset(data, 0, t->offset);
+}
diff --git a/lib/asn1/test.asn1 b/lib/asn1/test.asn1
index 89154e337c8f..206e1c328d47 100644
--- a/lib/asn1/test.asn1
+++ b/lib/asn1/test.asn1
@@ -7,6 +7,8 @@ BEGIN
IMPORTS heim_any FROM heim;
TESTuint32 ::= INTEGER (0..4294967295)
+TESTuint64 ::= INTEGER(0..18446744073709551615)
+TESTint64 ::= INTEGER(-9223372036854775808..9223372036854775807)
TESTLargeTag ::= SEQUENCE {
foo[127] INTEGER (-2147483648..2147483647),
@@ -46,7 +48,7 @@ TESTImplicit ::= SEQUENCE {
TESTImplicit2 ::= SEQUENCE {
ti1[0] IMPLICIT TESTInteger,
- ti2[1] IMPLICIT TESTLargeTag,
+-- ti2[1] IMPLICIT TESTLargeTag, this is disabled since the IMPLICT encoder does't get the types right when stepping inside an structure --
ti3[2] IMPLICIT TESTInteger3
}
@@ -96,7 +98,7 @@ TESTSeqOf ::= SEQUENCE OF TESTInteger
TESTSeqSizeOf1 ::= SEQUENCE SIZE (2) OF TESTInteger
TESTSeqSizeOf2 ::= SEQUENCE SIZE (1..2) OF TESTInteger
TESTSeqSizeOf3 ::= SEQUENCE SIZE (1..MAX) OF TESTInteger
-TESTSeqSizeOf4 ::= SEQUENCE SIZE (MIN..2) OF TESTInteger
+TESTSeqSizeOf4 ::= SEQUENCE SIZE (0..2) OF TESTInteger
TESTOSSize1 ::= OCTET STRING SIZE (1..2)
@@ -121,6 +123,60 @@ TESTSeqOf3 ::= SEQUENCE {
strings SEQUENCE OF GeneralString OPTIONAL
}
+-- Larger/more complex to increase odds of out-of-bounds
+-- read/writes if miscoded
+
+TESTSeqOf4 ::= SEQUENCE {
+ b1 [0] SEQUENCE OF SEQUENCE {
+ s1 OCTET STRING,
+ s2 OCTET STRING,
+ u1 TESTuint64,
+ u2 TESTuint64
+ } OPTIONAL,
+ b2 [1] IMPLICIT SEQUENCE OF SEQUENCE {
+ u1 TESTuint64,
+ u2 TESTuint64,
+ u3 TESTuint64,
+ s1 OCTET STRING,
+ s2 OCTET STRING,
+ s3 OCTET STRING
+ } OPTIONAL,
+ b3 [2] IMPLICIT SEQUENCE OF SEQUENCE {
+ s1 OCTET STRING,
+ u1 TESTuint64,
+ s2 OCTET STRING,
+ u2 TESTuint64,
+ s3 OCTET STRING,
+ u3 TESTuint64,
+ s4 OCTET STRING,
+ u4 TESTuint64
+ } OPTIONAL
+}
+
+TESTSeqOf5 ::= SEQUENCE {
+ outer SEQUENCE {
+ inner SEQUENCE {
+ u0 TESTuint64,
+ s0 OCTET STRING,
+ u1 TESTuint64,
+ s1 OCTET STRING,
+ u2 TESTuint64,
+ s2 OCTET STRING,
+ u3 TESTuint64,
+ s3 OCTET STRING,
+ u4 TESTuint64,
+ s4 OCTET STRING,
+ u5 TESTuint64,
+ s5 OCTET STRING,
+ u6 TESTuint64,
+ s6 OCTET STRING,
+ u7 TESTuint64,
+ s7 OCTET STRING
+ }
+ }
+ OPTIONAL
+}
+
TESTPreserve ::= SEQUENCE {
zero [0] TESTInteger,
one [1] TESTInteger
diff --git a/lib/asn1/timegm.c b/lib/asn1/timegm.c
index d9f4adbd5591..4746fa80b891 100644
--- a/lib/asn1/timegm.c
+++ b/lib/asn1/timegm.c
@@ -102,7 +102,7 @@ _der_gmtime(time_t t, struct tm *tm)
tm->tm_sec = secday % 60;
tm->tm_min = (secday % 3600) / 60;
- tm->tm_hour = secday / 3600;
+ tm->tm_hour = (int)(secday / 3600);
/*
* Refuse to calculate time ~ 2000 years into the future, this is
@@ -130,7 +130,7 @@ _der_gmtime(time_t t, struct tm *tm)
days -= daysinmonth;
tm->tm_mon++;
}
- tm->tm_mday = days + 1;
+ tm->tm_mday = (int)(days + 1);
return tm;
}
diff --git a/lib/base/Makefile.am b/lib/base/Makefile.am
new file mode 100644
index 000000000000..ee5f120d3f5d
--- /dev/null
+++ b/lib/base/Makefile.am
@@ -0,0 +1,64 @@
+
+include $(top_srcdir)/Makefile.am.common
+
+if do_roken_rename
+ES = base64.c
+endif
+
+IMPLEMENT_TLS=
+if MAINTAINER_MODE
+IMPLEMENT_TLS += dll.c
+AM_CPPFLAGS += -DHEIM_BASE_MAINTAINER
+endif
+
+AM_CPPFLAGS += $(ROKEN_RENAME)
+
+lib_LTLIBRARIES = libheimbase.la
+check_PROGRAMS = test_base
+
+libheimbase_la_LDFLAGS = -version-info 1:0:0
+
+TESTS = test_base
+
+if versionscript
+libheimbase_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+endif
+
+libheimbase_la_LIBADD = $(PTHREAD_LIBADD)
+
+include_HEADERS = heimbase.h
+
+dist_libheimbase_la_SOURCES = \
+ array.c \
+ baselocl.h \
+ bsearch.c \
+ bool.c \
+ data.c \
+ db.c \
+ dict.c \
+ $(IMPLEMENT_TLS) \
+ error.c \
+ heimbase.c \
+ heimbasepriv.h \
+ heimqueue.h \
+ json.c \
+ null.c \
+ number.c \
+ roken_rename.h \
+ string.c
+
+nodist_libheimbase_la_SOURCES = $(ES)
+
+# install these?
+
+libheimbase_la_DEPENDENCIES = version-script.map
+
+test_base_LDADD = libheimbase.la $(LIB_roken)
+
+CLEANFILES = base64.c test_db.json
+
+EXTRA_DIST = NTMakefile version-script.map
+
+base64.c:
+ rm -f base64.c
+ $(LN_S) $(srcdir)/../roken/base64.c .
diff --git a/lib/base/Makefile.in b/lib/base/Makefile.in
new file mode 100644
index 000000000000..9c147666e795
--- /dev/null
+++ b/lib/base/Makefile.in
@@ -0,0 +1,1482 @@
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# $Id$
+
+# $Id$
+
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+@MAINTAINER_MODE_TRUE@am__append_1 = dll.c
+@MAINTAINER_MODE_TRUE@am__append_2 = -DHEIM_BASE_MAINTAINER
+check_PROGRAMS = test_base$(EXEEXT)
+TESTS = test_base$(EXEEXT)
+@versionscript_TRUE@am__append_3 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+subdir = lib/base
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
+ $(top_srcdir)/cf/auth-modules.m4 \
+ $(top_srcdir)/cf/broken-getaddrinfo.m4 \
+ $(top_srcdir)/cf/broken-glob.m4 \
+ $(top_srcdir)/cf/broken-realloc.m4 \
+ $(top_srcdir)/cf/broken-snprintf.m4 $(top_srcdir)/cf/broken.m4 \
+ $(top_srcdir)/cf/broken2.m4 $(top_srcdir)/cf/c-attribute.m4 \
+ $(top_srcdir)/cf/capabilities.m4 \
+ $(top_srcdir)/cf/check-compile-et.m4 \
+ $(top_srcdir)/cf/check-getpwnam_r-posix.m4 \
+ $(top_srcdir)/cf/check-man.m4 \
+ $(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
+ $(top_srcdir)/cf/check-type-extra.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
+ $(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
+ $(top_srcdir)/cf/find-func-no-libs.m4 \
+ $(top_srcdir)/cf/find-func-no-libs2.m4 \
+ $(top_srcdir)/cf/find-func.m4 \
+ $(top_srcdir)/cf/find-if-not-broken.m4 \
+ $(top_srcdir)/cf/framework-security.m4 \
+ $(top_srcdir)/cf/have-struct-field.m4 \
+ $(top_srcdir)/cf/have-type.m4 $(top_srcdir)/cf/irix.m4 \
+ $(top_srcdir)/cf/krb-bigendian.m4 \
+ $(top_srcdir)/cf/krb-func-getlogin.m4 \
+ $(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
+ $(top_srcdir)/cf/krb-readline.m4 \
+ $(top_srcdir)/cf/krb-struct-spwd.m4 \
+ $(top_srcdir)/cf/krb-struct-winsize.m4 \
+ $(top_srcdir)/cf/largefile.m4 $(top_srcdir)/cf/libtool.m4 \
+ $(top_srcdir)/cf/ltoptions.m4 $(top_srcdir)/cf/ltsugar.m4 \
+ $(top_srcdir)/cf/ltversion.m4 $(top_srcdir)/cf/lt~obsolete.m4 \
+ $(top_srcdir)/cf/mips-abi.m4 $(top_srcdir)/cf/misc.m4 \
+ $(top_srcdir)/cf/need-proto.m4 $(top_srcdir)/cf/osfc2.m4 \
+ $(top_srcdir)/cf/otp.m4 $(top_srcdir)/cf/pkg.m4 \
+ $(top_srcdir)/cf/proto-compat.m4 $(top_srcdir)/cf/pthreads.m4 \
+ $(top_srcdir)/cf/resolv.m4 $(top_srcdir)/cf/retsigtype.m4 \
+ $(top_srcdir)/cf/roken-frag.m4 \
+ $(top_srcdir)/cf/socket-wrapper.m4 $(top_srcdir)/cf/sunos.m4 \
+ $(top_srcdir)/cf/telnet.m4 $(top_srcdir)/cf/test-package.m4 \
+ $(top_srcdir)/cf/version-script.m4 $(top_srcdir)/cf/wflags.m4 \
+ $(top_srcdir)/cf/win32.m4 $(top_srcdir)/cf/with-all.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
+LTLIBRARIES = $(lib_LTLIBRARIES)
+am__DEPENDENCIES_1 =
+am__dist_libheimbase_la_SOURCES_DIST = array.c baselocl.h bsearch.c \
+ bool.c data.c db.c dict.c dll.c error.c heimbase.c \
+ heimbasepriv.h heimqueue.h json.c null.c number.c \
+ roken_rename.h string.c
+@MAINTAINER_MODE_TRUE@am__objects_1 = dll.lo
+am__objects_2 = $(am__objects_1)
+dist_libheimbase_la_OBJECTS = array.lo bsearch.lo bool.lo data.lo \
+ db.lo dict.lo $(am__objects_2) error.lo heimbase.lo json.lo \
+ null.lo number.lo string.lo
+@do_roken_rename_TRUE@am__objects_3 = base64.lo
+nodist_libheimbase_la_OBJECTS = $(am__objects_3)
+libheimbase_la_OBJECTS = $(dist_libheimbase_la_OBJECTS) \
+ $(nodist_libheimbase_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libheimbase_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libheimbase_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
+test_base_SOURCES = test_base.c
+test_base_OBJECTS = test_base.$(OBJEXT)
+test_base_DEPENDENCIES = libheimbase.la $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(dist_libheimbase_la_SOURCES) \
+ $(nodist_libheimbase_la_SOURCES) test_base.c
+DIST_SOURCES = $(am__dist_libheimbase_la_SOURCES_DIST) test_base.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+ASN1_COMPILE = @ASN1_COMPILE@
+ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CANONICAL_HOST = @CANONICAL_HOST@
+CAPNG_CFLAGS = @CAPNG_CFLAGS@
+CAPNG_LIBS = @CAPNG_LIBS@
+CATMAN = @CATMAN@
+CATMANEXT = @CATMANEXT@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+COMPILE_ET = @COMPILE_ET@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
+DBHEADER = @DBHEADER@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DIR_com_err = @DIR_com_err@
+DIR_hdbdir = @DIR_hdbdir@
+DIR_roken = @DIR_roken@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
+GREP = @GREP@
+GROFF = @GROFF@
+INCLUDES_roken = @INCLUDES_roken@
+INCLUDE_libedit = @INCLUDE_libedit@
+INCLUDE_libintl = @INCLUDE_libintl@
+INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
+INCLUDE_readline = @INCLUDE_readline@
+INCLUDE_sqlite3 = @INCLUDE_sqlite3@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_VERSION_SCRIPT = @LDFLAGS_VERSION_SCRIPT@
+LEX = @LEX@
+LEXLIB = @LEXLIB@
+LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@
+LIBADD_roken = @LIBADD_roken@
+LIBOBJS = @LIBOBJS@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
+LIB_bswap16 = @LIB_bswap16@
+LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
+LIB_com_err = @LIB_com_err@
+LIB_com_err_a = @LIB_com_err_a@
+LIB_com_err_so = @LIB_com_err_so@
+LIB_crypt = @LIB_crypt@
+LIB_db_create = @LIB_db_create@
+LIB_dbm_firstkey = @LIB_dbm_firstkey@
+LIB_dbopen = @LIB_dbopen@
+LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
+LIB_dlopen = @LIB_dlopen@
+LIB_dn_expand = @LIB_dn_expand@
+LIB_dns_search = @LIB_dns_search@
+LIB_door_create = @LIB_door_create@
+LIB_freeaddrinfo = @LIB_freeaddrinfo@
+LIB_gai_strerror = @LIB_gai_strerror@
+LIB_getaddrinfo = @LIB_getaddrinfo@
+LIB_gethostbyname = @LIB_gethostbyname@
+LIB_gethostbyname2 = @LIB_gethostbyname2@
+LIB_getnameinfo = @LIB_getnameinfo@
+LIB_getpwnam_r = @LIB_getpwnam_r@
+LIB_getsockopt = @LIB_getsockopt@
+LIB_hcrypto = @LIB_hcrypto@
+LIB_hcrypto_a = @LIB_hcrypto_a@
+LIB_hcrypto_appl = @LIB_hcrypto_appl@
+LIB_hcrypto_so = @LIB_hcrypto_so@
+LIB_hstrerror = @LIB_hstrerror@
+LIB_kdb = @LIB_kdb@
+LIB_libedit = @LIB_libedit@
+LIB_libintl = @LIB_libintl@
+LIB_loadquery = @LIB_loadquery@
+LIB_logout = @LIB_logout@
+LIB_logwtmp = @LIB_logwtmp@
+LIB_openldap = @LIB_openldap@
+LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
+LIB_otp = @LIB_otp@
+LIB_pidfile = @LIB_pidfile@
+LIB_readline = @LIB_readline@
+LIB_res_ndestroy = @LIB_res_ndestroy@
+LIB_res_nsearch = @LIB_res_nsearch@
+LIB_res_search = @LIB_res_search@
+LIB_roken = @LIB_roken@
+LIB_security = @LIB_security@
+LIB_setsockopt = @LIB_setsockopt@
+LIB_socket = @LIB_socket@
+LIB_sqlite3 = @LIB_sqlite3@
+LIB_syslog = @LIB_syslog@
+LIB_tgetent = @LIB_tgetent@
+LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
+NM = @NM@
+NMEDIT = @NMEDIT@
+NO_AFS = @NO_AFS@
+NROFF = @NROFF@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+PKG_CONFIG = @PKG_CONFIG@
+PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
+PTHREAD_LDADD = @PTHREAD_LDADD@
+PTHREAD_LIBADD = @PTHREAD_LIBADD@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SLC = @SLC@
+SLC_DEP = @SLC_DEP@
+STRIP = @STRIP@
+VERSION = @VERSION@
+VERSIONING = @VERSIONING@
+WFLAGS = @WFLAGS@
+WFLAGS_LITE = @WFLAGS_LITE@
+YACC = @YACC@
+YFLAGS = @YFLAGS@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
+docdir = @docdir@
+dpagaix_cflags = @dpagaix_cflags@
+dpagaix_ldadd = @dpagaix_ldadd@
+dpagaix_ldflags = @dpagaix_ldflags@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
+AM_CPPFLAGS = $(INCLUDES_roken) $(am__append_2) $(ROKEN_RENAME)
+@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
+AM_CFLAGS = $(WFLAGS)
+CP = cp
+buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
+LIB_el_init = @LIB_el_init@
+LIB_getattr = @LIB_getattr@
+LIB_getpwent_r = @LIB_getpwent_r@
+LIB_odm_initialize = @LIB_odm_initialize@
+LIB_setpcred = @LIB_setpcred@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
+libexec_heimdaldir = $(libexecdir)/heimdal
+NROFF_MAN = groff -mandoc -Tascii
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
+@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
+@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
+
+@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
+@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
+@do_roken_rename_TRUE@ES = base64.c
+IMPLEMENT_TLS = $(am__append_1)
+lib_LTLIBRARIES = libheimbase.la
+libheimbase_la_LDFLAGS = -version-info 1:0:0 $(am__append_3)
+libheimbase_la_LIBADD = $(PTHREAD_LIBADD)
+include_HEADERS = heimbase.h
+dist_libheimbase_la_SOURCES = \
+ array.c \
+ baselocl.h \
+ bsearch.c \
+ bool.c \
+ data.c \
+ db.c \
+ dict.c \
+ $(IMPLEMENT_TLS) \
+ error.c \
+ heimbase.c \
+ heimbasepriv.h \
+ heimqueue.h \
+ json.c \
+ null.c \
+ number.c \
+ roken_rename.h \
+ string.c
+
+nodist_libheimbase_la_SOURCES = $(ES)
+
+# install these?
+libheimbase_la_DEPENDENCIES = version-script.map
+test_base_LDADD = libheimbase.la $(LIB_roken)
+CLEANFILES = base64.c test_db.json
+EXTRA_DIST = NTMakefile version-script.map
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/base/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --foreign lib/base/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libheimbase.la: $(libheimbase_la_OBJECTS) $(libheimbase_la_DEPENDENCIES) $(EXTRA_libheimbase_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libheimbase_la_LINK) -rpath $(libdir) $(libheimbase_la_OBJECTS) $(libheimbase_la_LIBADD) $(LIBS)
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+test_base$(EXEEXT): $(test_base_OBJECTS) $(test_base_DEPENDENCIES) $(EXTRA_test_base_DEPENDENCIES)
+ @rm -f test_base$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_base_OBJECTS) $(test_base_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/array.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bool.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bsearch.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/db.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dict.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/heimbase.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/json.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/null.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/number.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_base.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-includeHEADERS: $(include_HEADERS)
+ @$(NORMAL_INSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includedir)'"; \
+ $(INSTALL_HEADER) $$files "$(DESTDIR)$(includedir)" || exit $$?; \
+ done
+
+uninstall-includeHEADERS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
+ fi; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
+ else \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
+ fi; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
+ else \
+ color_start= color_end=; \
+ fi; \
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test_base.log: test_base$(EXEEXT)
+ @p='test_base$(EXEEXT)'; \
+ b='test_base'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$(top_distdir)" distdir="$(distdir)" \
+ dist-hook
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(HEADERS) all-local
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-includeHEADERS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-data-hook
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-includeHEADERS \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+ uninstall uninstall-am uninstall-hook uninstall-includeHEADERS \
+ uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
+
+
+install-suid-programs:
+ @foo='$(bin_SUIDS)'; \
+ for file in $$foo; do \
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
+ for f in $$foo; do \
+ f=`basename $$f`; \
+ if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
+ else file="$$f"; fi; \
+ if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
+ : ; else \
+ echo " $(CP) $$file $(buildinclude)/$$f"; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
+ fi ; \
+ done ; \
+ foo='$(nobase_include_HEADERS)'; \
+ for f in $$foo; do \
+ if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
+ else file="$$f"; fi; \
+ $(mkdir_p) $(buildinclude)/`dirname $$f` ; \
+ if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
+ : ; else \
+ echo " $(CP) $$file $(buildinclude)/$$f"; \
+ $(CP) $$file $(buildinclude)/$$f; \
+ fi ; \
+ done
+
+all-local: install-build-headers
+
+check-local::
+ @if test '$(CHECK_LOCAL)' = "no-check-local"; then \
+ foo=''; elif test '$(CHECK_LOCAL)'; then \
+ foo='$(CHECK_LOCAL)'; else \
+ foo='$(PROGRAMS)'; fi; \
+ if test "$$foo"; then \
+ failed=0; all=0; \
+ for i in $$foo; do \
+ all=`expr $$all + 1`; \
+ if (./$$i --version && ./$$i --help) > /dev/null 2>&1; then \
+ echo "PASS: $$i"; \
+ else \
+ echo "FAIL: $$i"; \
+ failed=`expr $$failed + 1`; \
+ fi; \
+ done; \
+ if test "$$failed" -eq 0; then \
+ banner="All $$all tests passed"; \
+ else \
+ banner="$$failed of $$all tests failed"; \
+ fi; \
+ dashes=`echo "$$banner" | sed s/./=/g`; \
+ echo "$$dashes"; \
+ echo "$$banner"; \
+ echo "$$dashes"; \
+ test "$$failed" -eq 0 || exit 1; \
+ fi
+
+.x.c:
+ @cmp -s $< $@ 2> /dev/null || cp $< $@
+
+.hx.h:
+ @cmp -s $< $@ 2> /dev/null || cp $< $@
+#NROFF_MAN = nroff -man
+.1.cat1:
+ $(NROFF_MAN) $< > $@
+.3.cat3:
+ $(NROFF_MAN) $< > $@
+.5.cat5:
+ $(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
+.8.cat8:
+ $(NROFF_MAN) $< > $@
+
+dist-cat1-mans:
+ @foo='$(man1_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.1) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat1/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat3-mans:
+ @foo='$(man3_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.3) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat3/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat5-mans:
+ @foo='$(man5_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.5) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat5/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-cat8-mans:
+ @foo='$(man8_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.8) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat8/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
+
+install-cat-mans:
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
+
+uninstall-cat-mans:
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
+
+install-data-hook: install-cat-mans
+uninstall-hook: uninstall-cat-mans
+
+.et.h:
+ $(COMPILE_ET) $<
+.et.c:
+ $(COMPILE_ET) $<
+
+#
+# Useful target for debugging
+#
+
+check-valgrind:
+ tobjdir=`cd $(top_builddir) && pwd` ; \
+ tsrcdir=`cd $(top_srcdir) && pwd` ; \
+ env TESTS_ENVIRONMENT="$${tsrcdir}/cf/maybe-valgrind.sh -s $${tsrcdir} -o $${tobjdir}" make check
+
+#
+# Target to please samba build farm, builds distfiles in-tree.
+# Will break when automake changes...
+#
+
+distdir-in-tree: $(DISTFILES) $(INFO_DEPS)
+ list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" != .; then \
+ (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) distdir-in-tree) ; \
+ fi ; \
+ done
+
+base64.c:
+ rm -f base64.c
+ $(LN_S) $(srcdir)/../roken/base64.c .
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lib/base/NTMakefile b/lib/base/NTMakefile
new file mode 100644
index 000000000000..e5bda31dd722
--- /dev/null
+++ b/lib/base/NTMakefile
@@ -0,0 +1,82 @@
+########################################################################
+#
+# Copyright (c) 2010, Secure Endpoints Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# - Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# - Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+RELDIR=lib\base
+
+intcflags=-I$(SRCDIR) -I$(OBJ)
+
+!include ../../windows/NTMakefile.w32
+
+INCFILES=$(INCDIR)\heimbase.h
+
+test_binaries = $(OBJ)\test_base.exe
+
+libheimbase_OBJS = \
+ $(OBJ)\array.obj \
+ $(OBJ)\bool.obj \
+ $(OBJ)\bsearch.obj \
+ $(OBJ)\data.obj \
+ $(OBJ)\db.obj \
+ $(OBJ)\dict.obj \
+ $(OBJ)\dll.obj \
+ $(OBJ)\error.obj \
+ $(OBJ)\heimbase.obj \
+ $(OBJ)\json.obj \
+ $(OBJ)\null.obj \
+ $(OBJ)\number.obj \
+ $(OBJ)\string.obj
+
+$(LIBHEIMBASE): $(libheimbase_OBJS)
+ $(LIBCON_C) -OUT:$@ $(LIBROKEN) @<<
+$(libheimbase_OBJS: =
+)
+<<
+
+test:: test-binaries test-run
+
+test-run:
+ cd $(OBJ)
+ -test_base.exe
+ cd $(SRCDIR)
+
+all:: $(INCFILES) $(LIBHEIMBASE)
+
+clean::
+ -$(RM) $(INCFILES)
+
+test-binaries: $(test_binaries)
+
+$(test_binaries): $$(@R).obj $(LIBHEIMBASE) $(LIBVERS) $(LIBROKEN)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
+$(test_binaries:.exe=.obj): $$(@B).c
+ $(C2OBJ_C) -Fo$@ -Fd$(@D)\ $** -DBlah
diff --git a/lib/base/array.c b/lib/base/array.c
new file mode 100644
index 000000000000..b34f9de48800
--- /dev/null
+++ b/lib/base/array.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+/*
+ *
+ */
+
+struct heim_array_data {
+ size_t len;
+ heim_object_t *val;
+ size_t allocated_len;
+ heim_object_t *allocated;
+};
+
+static void
+array_dealloc(heim_object_t ptr)
+{
+ heim_array_t array = ptr;
+ size_t n;
+ for (n = 0; n < array->len; n++)
+ heim_release(array->val[n]);
+ free(array->allocated);
+}
+
+struct heim_type_data array_object = {
+ HEIM_TID_ARRAY,
+ "dict-object",
+ NULL,
+ array_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/**
+ * Allocate an array
+ *
+ * @return A new allocated array, free with heim_release()
+ */
+
+heim_array_t
+heim_array_create(void)
+{
+ heim_array_t array;
+
+ array = _heim_alloc_object(&array_object, sizeof(*array));
+ if (array == NULL)
+ return NULL;
+
+ array->allocated = NULL;
+ array->allocated_len = 0;
+ array->val = NULL;
+ array->len = 0;
+
+ return array;
+}
+
+/**
+ * Get type id of an dict
+ *
+ * @return the type id
+ */
+
+heim_tid_t
+heim_array_get_type_id(void)
+{
+ return HEIM_TID_ARRAY;
+}
+
+/**
+ * Append object to array
+ *
+ * @param array array to add too
+ * @param object the object to add
+ *
+ * @return zero if added, errno otherwise
+ */
+
+int
+heim_array_append_value(heim_array_t array, heim_object_t object)
+{
+ heim_object_t *ptr;
+ size_t leading = array->val - array->allocated; /* unused leading slots */
+ size_t trailing = array->allocated_len - array->len - leading;
+ size_t new_len;
+
+ if (trailing > 0) {
+ /* We have pre-allocated space; use it */
+ array->val[array->len++] = heim_retain(object);
+ return 0;
+ }
+
+ if (leading > (array->len + 1)) {
+ /*
+ * We must have appending to, and deleting at index 0 from this
+ * array a lot; don't want to grow forever!
+ */
+ (void) memmove(&array->allocated[0], &array->val[0],
+ array->len * sizeof(array->val[0]));
+ array->val = array->allocated;
+
+ /* We have pre-allocated space; use it */
+ array->val[array->len++] = heim_retain(object);
+ return 0;
+ }
+
+ /* Pre-allocate extra .5 times number of used slots */
+ new_len = leading + array->len + 1 + (array->len >> 1);
+ ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
+ if (ptr == NULL)
+ return ENOMEM;
+ array->allocated = ptr;
+ array->allocated_len = new_len;
+ array->val = &ptr[leading];
+ array->val[array->len++] = heim_retain(object);
+
+ return 0;
+}
+
+/*
+ * Internal function to insert at index 0, taking care to optimize the
+ * case where we're always inserting at index 0, particularly the case
+ * where we insert at index 0 and delete from the right end.
+ */
+static int
+heim_array_prepend_value(heim_array_t array, heim_object_t object)
+{
+ heim_object_t *ptr;
+ size_t leading = array->val - array->allocated; /* unused leading slots */
+ size_t trailing = array->allocated_len - array->len - leading;
+ size_t new_len;
+
+ if (leading > 0) {
+ /* We have pre-allocated space; use it */
+ array->val--;
+ array->val[0] = heim_retain(object);
+ array->len++;
+ return 0;
+ }
+ if (trailing > (array->len + 1)) {
+ /*
+ * We must have prepending to, and deleting at index
+ * array->len - 1 from this array a lot; don't want to grow
+ * forever!
+ */
+ (void) memmove(&array->allocated[array->len], &array->val[0],
+ array->len * sizeof(array->val[0]));
+ array->val = &array->allocated[array->len];
+
+ /* We have pre-allocated space; use it */
+ array->val--;
+ array->val[0] = heim_retain(object);
+ array->len++;
+ return 0;
+ }
+ /* Pre-allocate extra .5 times number of used slots */
+ new_len = array->len + 1 + trailing + (array->len >> 1);
+ ptr = realloc(array->allocated, new_len * sizeof(array->val[0]));
+ if (ptr == NULL)
+ return ENOMEM;
+ (void) memmove(&ptr[1], &ptr[0], array->len * sizeof (array->val[0]));
+ array->allocated = ptr;
+ array->allocated_len = new_len;
+ array->val = &ptr[0];
+ array->val[0] = heim_retain(object);
+ array->len++;
+
+ return 0;
+}
+
+/**
+ * Insert an object at a given index in an array
+ *
+ * @param array array to add too
+ * @param idx index where to add element (-1 == append, -2 next to last, ...)
+ * @param object the object to add
+ *
+ * @return zero if added, errno otherwise
+ */
+
+int
+heim_array_insert_value(heim_array_t array, size_t idx, heim_object_t object)
+{
+ int ret;
+
+ if (idx == 0)
+ return heim_array_prepend_value(array, object);
+ else if (idx > array->len)
+ heim_abort("index too large");
+
+ /*
+ * We cheat: append this element then rotate elements around so we
+ * have this new element at the desired location, unless we're truly
+ * appending the new element. This means reusing array growth in
+ * heim_array_append_value() instead of duplicating that here.
+ */
+ ret = heim_array_append_value(array, object);
+ if (ret != 0 || idx == (array->len - 1))
+ return ret;
+ /*
+ * Shift to the right by one all the elements after idx, then set
+ * [idx] to the new object.
+ */
+ (void) memmove(&array->val[idx + 1], &array->val[idx],
+ (array->len - idx - 1) * sizeof(array->val[0]));
+ array->val[idx] = heim_retain(object);
+
+ return 0;
+}
+
+/**
+ * Iterate over all objects in array
+ *
+ * @param array array to iterate over
+ * @param ctx context passed to fn
+ * @param fn function to call on each object
+ */
+
+void
+heim_array_iterate_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
+{
+ size_t n;
+ int stop = 0;
+ for (n = 0; n < array->len; n++) {
+ fn(array->val[n], ctx, &stop);
+ if (stop)
+ return;
+ }
+}
+
+#ifdef __BLOCKS__
+/**
+ * Iterate over all objects in array
+ *
+ * @param array array to iterate over
+ * @param fn block to call on each object
+ */
+
+void
+heim_array_iterate(heim_array_t array, void (^fn)(heim_object_t, int *))
+{
+ size_t n;
+ int stop = 0;
+ for (n = 0; n < array->len; n++) {
+ fn(array->val[n], &stop);
+ if (stop)
+ return;
+ }
+}
+#endif
+
+/**
+ * Iterate over all objects in array, backwards
+ *
+ * @param array array to iterate over
+ * @param ctx context passed to fn
+ * @param fn function to call on each object
+ */
+
+void
+heim_array_iterate_reverse_f(heim_array_t array, void *ctx, heim_array_iterator_f_t fn)
+{
+ size_t n;
+ int stop = 0;
+
+ for (n = array->len; n > 0; n--) {
+ fn(array->val[n - 1], ctx, &stop);
+ if (stop)
+ return;
+ }
+}
+
+#ifdef __BLOCKS__
+/**
+ * Iterate over all objects in array, backwards
+ *
+ * @param array array to iterate over
+ * @param fn block to call on each object
+ */
+
+void
+heim_array_iterate_reverse(heim_array_t array, void (^fn)(heim_object_t, int *))
+{
+ size_t n;
+ int stop = 0;
+ for (n = array->len; n > 0; n--) {
+ fn(array->val[n - 1], &stop);
+ if (stop)
+ return;
+ }
+}
+#endif
+
+/**
+ * Get length of array
+ *
+ * @param array array to get length of
+ *
+ * @return length of array
+ */
+
+size_t
+heim_array_get_length(heim_array_t array)
+{
+ return array->len;
+}
+
+/**
+ * Get value of element at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ *
+ * @return a not-retained copy of the object
+ */
+
+heim_object_t
+heim_array_get_value(heim_array_t array, size_t idx)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ return array->val[idx];
+}
+
+/**
+ * Get value of element at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ *
+ * @return a retained copy of the object
+ */
+
+heim_object_t
+heim_array_copy_value(heim_array_t array, size_t idx)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ return heim_retain(array->val[idx]);
+}
+
+/**
+ * Set value at array index
+ *
+ * @param array array copy object from
+ * @param idx index of object, 0 based, must be smaller then
+ * heim_array_get_length()
+ * @param value value to set
+ *
+ */
+
+void
+heim_array_set_value(heim_array_t array, size_t idx, heim_object_t value)
+{
+ if (idx >= array->len)
+ heim_abort("index too large");
+ heim_release(array->val[idx]);
+ array->val[idx] = heim_retain(value);
+}
+
+/**
+ * Delete value at idx
+ *
+ * @param array the array to modify
+ * @param idx the key to delete
+ */
+
+void
+heim_array_delete_value(heim_array_t array, size_t idx)
+{
+ heim_object_t obj;
+ if (idx >= array->len)
+ heim_abort("index too large");
+ obj = array->val[idx];
+
+ array->len--;
+
+ /*
+ * Deleting the first or last elements is cheap, as we leave
+ * allocated space for opportunistic reuse later; no realloc(), no
+ * memmove(). All others require a memmove().
+ *
+ * If we ever need to optimize deletion of non-last/ non-first
+ * element we can use a tagged object type to signify "deleted
+ * value" so we can leave holes in the array, avoid memmove()s on
+ * delete, and opportunistically re-use those holes on insert.
+ */
+ if (idx == 0)
+ array->val++;
+ else if (idx < array->len)
+ (void) memmove(&array->val[idx], &array->val[idx + 1],
+ (array->len - idx) * sizeof(array->val[0]));
+
+ heim_release(obj);
+}
+
+/**
+ * Filter out entres of array when function return true
+ *
+ * @param array the array to modify
+ * @param fn filter function
+ */
+
+void
+heim_array_filter_f(heim_array_t array, void *ctx, heim_array_filter_f_t fn)
+{
+ size_t n = 0;
+
+ while (n < array->len) {
+ if (fn(array->val[n], ctx)) {
+ heim_array_delete_value(array, n);
+ } else {
+ n++;
+ }
+ }
+}
+
+#ifdef __BLOCKS__
+
+/**
+ * Filter out entres of array when block return true
+ *
+ * @param array the array to modify
+ * @param block filter block
+ */
+
+void
+heim_array_filter(heim_array_t array, int (^block)(heim_object_t))
+{
+ size_t n = 0;
+
+ while (n < array->len) {
+ if (block(array->val[n])) {
+ heim_array_delete_value(array, n);
+ } else {
+ n++;
+ }
+ }
+}
+
+#endif /* __BLOCKS__ */
diff --git a/lib/base/baselocl.h b/lib/base/baselocl.h
new file mode 100644
index 000000000000..b24c13d4fb24
--- /dev/null
+++ b/lib/base/baselocl.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010 - 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include <roken.h>
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#define HEIMDAL_TEXTDOMAIN "heimdal_krb5"
+
+#ifdef LIBINTL
+#include <libintl.h>
+#define N_(x,y) dgettext(HEIMDAL_TEXTDOMAIN, x)
+#else
+#define N_(x,y) (x)
+#define bindtextdomain(package, localedir)
+#endif
+
+#include "heimqueue.h"
+#include "heim_threads.h"
+#include "heimbase.h"
+#include "heimbasepriv.h"
+
+#ifdef HAVE_DISPATCH_DISPATCH_H
+#include <dispatch/dispatch.h>
+#endif
+
+#if defined(__GNUC__) && defined(HAVE___SYNC_ADD_AND_FETCH)
+
+#define heim_base_atomic_inc(x) __sync_add_and_fetch((x), 1)
+#define heim_base_atomic_dec(x) __sync_sub_and_fetch((x), 1)
+#define heim_base_atomic_type unsigned int
+#define heim_base_atomic_max UINT_MAX
+
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
+#if __has_builtin(__sync_swap)
+#define heim_base_exchange_pointer(t,v) __sync_swap((t), (v))
+#else
+#define heim_base_exchange_pointer(t,v) __sync_lock_test_and_set((t), (v))
+#endif
+
+#elif defined(__sun)
+
+#include <sys/atomic.h>
+
+#define heim_base_atomic_inc(x) atomic_inc_uint_nv((volatile uint_t *)(x))
+#define heim_base_atomic_dec(x) atomic_dec_uint_nv((volatile uint_t *)(x))
+#define heim_base_atomic_type uint_t
+#define heim_base_atomic_max UINT_MAX
+
+#define heim_base_exchange_pointer(t,v) atomic_swap_ptr((volatile void *)(t), (void *)(v))
+
+#elif defined(_AIX)
+
+#include <sys/atomic_op.h>
+
+#define heim_base_atomic_inc(x) (fetch_and_add((atomic_p)(x)) + 1)
+#define heim_base_atomic_dec(x) (fetch_and_add((atomic_p)(x)) - 1)
+#define heim_base_atomic_type unsigned int
+#define heim_base_atomic_max UINT_MAX
+
+static inline void *
+heim_base_exchange_pointer(void *p, void *newval)
+{
+ void *val = *(void **)p;
+
+ while (!compare_and_swaplp((atomic_l)p, (long *)&val, (long)newval))
+ ;
+
+ return val;
+}
+
+#elif defined(_WIN32)
+
+#define heim_base_atomic_inc(x) InterlockedIncrement(x)
+#define heim_base_atomic_dec(x) InterlockedDecrement(x)
+#define heim_base_atomic_type LONG
+#define heim_base_atomic_max MAXLONG
+
+#define heim_base_exchange_pointer(t,v) InterlockedExchangePointer((t),(v))
+
+#else
+
+#define HEIM_BASE_NEED_ATOMIC_MUTEX 1
+extern HEIMDAL_MUTEX _heim_base_mutex;
+
+#define heim_base_atomic_type unsigned int
+
+static inline heim_base_atomic_type
+heim_base_atomic_inc(heim_base_atomic_type *x)
+{
+ heim_base_atomic_type t;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ t = ++(*x);
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return t;
+}
+
+static inline heim_base_atomic_type
+heim_base_atomic_dec(heim_base_atomic_type *x)
+{
+ heim_base_atomic_type t;
+ HEIMDAL_MUTEX_lock(&_heim_base_mutex);
+ t = --(*x);
+ HEIMDAL_MUTEX_unlock(&_heim_base_mutex);
+ return t;
+}
+
+#define heim_base_atomic_max UINT_MAX
+
+#endif
+
+/* tagged strings/object/XXX */
+#define heim_base_is_tagged(x) (((uintptr_t)(x)) & 0x3)
+
+#define heim_base_is_tagged_object(x) ((((uintptr_t)(x)) & 0x3) == 1)
+#define heim_base_make_tagged_object(x, tid) \
+ ((heim_object_t)((((uintptr_t)(x)) << 5) | ((tid) << 2) | 0x1))
+#define heim_base_tagged_object_tid(x) ((((uintptr_t)(x)) & 0x1f) >> 2)
+#define heim_base_tagged_object_value(x) (((uintptr_t)(x)) >> 5)
+
+/*
+ *
+ */
+
+#undef HEIMDAL_NORETURN_ATTRIBUTE
+#define HEIMDAL_NORETURN_ATTRIBUTE
+#undef HEIMDAL_PRINTF_ATTRIBUTE
+#define HEIMDAL_PRINTF_ATTRIBUTE(x)
diff --git a/lib/base/bool.c b/lib/base/bool.c
new file mode 100644
index 000000000000..f7b66ee35838
--- /dev/null
+++ b/lib/base/bool.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct heim_type_data _heim_bool_object = {
+ HEIM_TID_BOOL,
+ "bool-object",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_bool_t
+heim_bool_create(int val)
+{
+ return heim_base_make_tagged_object(!!val, HEIM_TID_BOOL);
+}
+
+int
+heim_bool_val(heim_bool_t ptr)
+{
+ return heim_base_tagged_object_value(ptr);
+}
diff --git a/lib/base/bsearch.c b/lib/base/bsearch.c
new file mode 100644
index 000000000000..278962172683
--- /dev/null
+++ b/lib/base/bsearch.c
@@ -0,0 +1,886 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "baselocl.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#include <errno.h>
+#include <assert.h>
+
+/*
+ * This file contains functions for binary searching flat text in memory
+ * and in text files where each line is a [variable length] record.
+ * Each record has a key and an optional value separated from the key by
+ * unquoted whitespace. Whitespace in the key, and leading whitespace
+ * for the value, can be quoted with backslashes (but CR and LF must be
+ * quoted in such a way that they don't appear in the quoted result).
+ *
+ * Binary searching a tree are normally a dead simple algorithm. It
+ * turns out that binary searching flat text with *variable* length
+ * records is... tricky. There's no indexes to record beginning bytes,
+ * thus any index selected during the search is likely to fall in the
+ * middle of a record. When deciding to search a left sub-tree one
+ * might fail to find the last record in that sub-tree on account of the
+ * right boundary falling in the middle of it -- the chosen solution to
+ * this makes left sub-tree searches slightly less efficient than right
+ * sub-tree searches.
+ *
+ * If binary searching flat text in memory is tricky, using block-wise
+ * I/O instead is trickier! But it's necessary in order to support
+ * large files (which we either can't or wouldn't want to read or map
+ * into memory). Each block we read has to be large enough that the
+ * largest record can fit in it. And each block might start and/or end
+ * in the middle of a record. Here it is the right sub-tree searches
+ * that are less efficient than left sub-tree searches.
+ *
+ * bsearch_common() contains the common text block binary search code.
+ *
+ * _bsearch_text() is the interface for searching in-core text.
+ * _bsearch_file() is the interface for block-wise searching files.
+ */
+
+struct bsearch_file_handle {
+ int fd; /* file descriptor */
+ char *cache; /* cache bytes */
+ char *page; /* one double-size page worth of bytes */
+ size_t file_sz; /* file size */
+ size_t cache_sz; /* cache size */
+ size_t page_sz; /* page size */
+};
+
+/* Find a new-line */
+static const char *
+find_line(const char *buf, size_t i, size_t right)
+{
+ if (i == 0)
+ return &buf[i];
+ for (; i < right; i++) {
+ if (buf[i] == '\n') {
+ if ((i + 1) < right)
+ return &buf[i + 1];
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Common routine for binary searching text in core.
+ *
+ * Perform a binary search of a char array containing a block from a
+ * text file where each line is a record (LF and CRLF supported). Each
+ * record consists of a key followed by an optional value separated from
+ * the key by whitespace. Whitespace can be quoted with backslashes.
+ * It's the caller's responsibility to encode/decode keys/values if
+ * quoting is desired; newlines should be encoded such that a newline
+ * does not appear in the result.
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * Inputs:
+ *
+ * @buf String to search
+ * @sz Size of string to search
+ * @key Key string to search for
+ * @buf_is_start True if the buffer starts with a record, false if it
+ * starts in the middle of a record or if the caller
+ * doesn't know.
+ *
+ * Outputs:
+ *
+ * @value Location to store a copy of the value (caller must free)
+ * @location Record location if found else the location where the
+ * record should be inserted (index into @buf)
+ * @cmp Set to less than or greater than 0 to indicate that a
+ * key not found would have fit in an earlier or later
+ * part of a file. Callers should use this to decide
+ * whether to read a block to the left or to the right and
+ * search that.
+ * @loops Location to store a count of bisections required for
+ * search (useful for confirming logarithmic performance)
+ */
+static int
+bsearch_common(const char *buf, size_t sz, const char *key,
+ int buf_is_start, char **value, size_t *location,
+ int *cmp, size_t *loops)
+{
+ const char *linep;
+ size_t key_start, key_len; /* key string in buf */
+ size_t val_start, val_len; /* value string in buf */
+ int key_cmp = -1;
+ size_t k;
+ size_t l; /* left side of buffer for binary search */
+ size_t r; /* right side of buffer for binary search */
+ size_t rmax; /* right side of buffer for binary search */
+ size_t i; /* index into buffer, typically in the middle of l and r */
+ size_t loop_count = 0;
+ int ret = -1;
+
+ if (value)
+ *value = NULL;
+ if (cmp)
+ *cmp = 0;
+ if (loops)
+ *loops = 0;
+
+ /* Binary search; file should be sorted */
+ for (l = 0, r = rmax = sz, i = sz >> 1; i >= l && i < rmax; loop_count++) {
+ heim_assert(i < sz, "invalid aname2lname db index");
+
+ /* buf[i] is likely in the middle of a line; find the next line */
+ linep = find_line(buf, i, rmax);
+ k = linep ? linep - buf : i;
+ if (linep == NULL || k >= rmax) {
+ /*
+ * No new line found to the right; search to the left then
+ * but don't change rmax (this isn't optimal, but it's
+ * simple).
+ */
+ if (i == l)
+ break;
+ r = i;
+ i = l + ((r - l) >> 1);
+ continue;
+ }
+ i = k;
+ heim_assert(i >= l && i < rmax, "invalid aname2lname db index");
+
+ /* Got a line; check it */
+
+ /* Search for and split on unquoted whitespace */
+ val_start = 0;
+ for (key_start = i, key_len = 0, val_len = 0, k = i; k < rmax; k++) {
+ if (buf[k] == '\\') {
+ k++;
+ continue;
+ }
+ if (buf[k] == '\r' || buf[k] == '\n') {
+ /* We now know where the key ends, and there's no value */
+ key_len = k - i;
+ break;
+ }
+ if (!isspace((unsigned char)buf[k]))
+ continue;
+
+ while (k < rmax && isspace((unsigned char)buf[k])) {
+ key_len = k - i;
+ k++;
+ }
+ if (k < rmax)
+ val_start = k;
+ /* Find end of value */
+ for (; k < rmax && buf[k] != '\0'; k++) {
+ if (buf[k] == '\r' || buf[k] == '\n') {
+ val_len = k - val_start;
+ break;
+ }
+ }
+ break;
+ }
+
+ /*
+ * The following logic is for dealing with partial buffers,
+ * which we use for block-wise binary searches of large files
+ */
+ if (key_start == 0 && !buf_is_start) {
+ /*
+ * We're at the beginning of a block that might have started
+ * in the middle of a record whose "key" might well compare
+ * as greater than the key we're looking for, so we don't
+ * bother comparing -- we know key_cmp must be -1 here.
+ */
+ key_cmp = -1;
+ break;
+ }
+ if ((val_len && buf[val_start + val_len] != '\n') ||
+ (!val_len && buf[key_start + key_len] != '\n')) {
+ /*
+ * We're at the end of a block that ends in the middle of a
+ * record whose "key" might well compare as less than the
+ * key we're looking for, so we don't bother comparing -- we
+ * know key_cmp must be >= 0 but we can't tell. Our caller
+ * will end up reading a double-size block to handle this.
+ */
+ key_cmp = 1;
+ break;
+ }
+
+ key_cmp = strncmp(key, &buf[key_start], key_len);
+ if (key_cmp == 0 && strlen(key) != key_len)
+ key_cmp = 1;
+ if (key_cmp < 0) {
+ /* search left */
+ r = rmax = (linep - buf);
+ i = l + ((r - l) >> 1);
+ if (location)
+ *location = key_start;
+ } else if (key_cmp > 0) {
+ /* search right */
+ if (l == i)
+ break; /* not found */
+ l = i;
+ i = l + ((r - l) >> 1);
+ if (location)
+ *location = val_start + val_len;
+ } else {
+ /* match! */
+ if (location)
+ *location = key_start;
+ ret = 0;
+ if (val_len && value) {
+ /* Avoid strndup() so we don't need libroken here yet */
+ *value = malloc(val_len + 1);
+ if (!*value)
+ ret = errno;
+ (void) memcpy(*value, &buf[val_start], val_len);
+ (*value)[val_len] = '\0';
+ }
+ break;
+ }
+ }
+
+ if (cmp)
+ *cmp = key_cmp;
+ if (loops)
+ *loops = loop_count;
+
+ return ret;
+}
+
+/*
+ * Binary search a char array containing sorted text records separated
+ * by new-lines (or CRLF). Each record consists of a key and an
+ * optional value following the key, separated from the key by unquoted
+ * whitespace.
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * Inputs:
+ *
+ * @buf Char array pointer
+ * @buf_sz Size of buf
+ * @key Key to search for
+ *
+ * Outputs:
+ *
+ * @value Location where to put the value, if any (caller must free)
+ * @location Record location if found else the location where the record
+ * should be inserted (index into @buf)
+ * @loops Location where to put a number of loops (or comparisons)
+ * needed for the search (useful for benchmarking)
+ */
+int
+_bsearch_text(const char *buf, size_t buf_sz, const char *key,
+ char **value, size_t *location, size_t *loops)
+{
+ return bsearch_common(buf, buf_sz, key, 1, value, location, NULL, loops);
+}
+
+#define MAX_BLOCK_SIZE (1024 * 1024)
+#define DEFAULT_MAX_FILE_SIZE (1024 * 1024)
+/*
+ * Open a file for binary searching. The file will be read in entirely
+ * if it is smaller than @max_sz, else a cache of @max_sz bytes will be
+ * allocated.
+ *
+ * Returns 0 on success, else an error number or -1 if the file is empty.
+ *
+ * Inputs:
+ *
+ * @fname Name of file to open
+ * @max_sz Maximum size of cache to allocate, in bytes (if zero, default)
+ * @page_sz Page size (must be a power of two, larger than 256, smaller
+ * than 1MB; if zero use default)
+ *
+ * Outputs:
+ *
+ * @bfh Handle for use with _bsearch_file() and _bsearch_file_close()
+ * @reads Number of reads performed
+ */
+int
+_bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
+ bsearch_file_handle *bfh, size_t *reads)
+{
+ bsearch_file_handle new_bfh = NULL;
+ struct stat st;
+ size_t i;
+ int fd;
+ int ret;
+
+ *bfh = NULL;
+
+ if (reads)
+ *reads = 0;
+
+ fd = open(fname, O_RDONLY);
+ if (fd == -1)
+ return errno;
+
+ if (fstat(fd, &st) == -1) {
+ ret = errno;
+ goto err;
+ }
+
+ if (st.st_size == 0) {
+ ret = -1; /* no data -> no binary search */
+ goto err;
+ }
+
+ /* Validate / default arguments */
+ if (max_sz == 0)
+ max_sz = DEFAULT_MAX_FILE_SIZE;
+ for (i = page_sz; i; i >>= 1) {
+ /* Make sure page_sz is a power of two */
+ if ((i % 2) && (i >> 1)) {
+ page_sz = 0;
+ break;
+ }
+ }
+ if (page_sz == 0)
+#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
+ page_sz = st.st_blksize;
+#else
+ page_sz = 4096;
+#endif
+ for (i = page_sz; i; i >>= 1) {
+ /* Make sure page_sz is a power of two */
+ if ((i % 2) && (i >> 1)) {
+ /* Can't happen! Filesystems always use powers of two! */
+ page_sz = 4096;
+ break;
+ }
+ }
+ if (page_sz > MAX_BLOCK_SIZE)
+ page_sz = MAX_BLOCK_SIZE;
+
+ new_bfh = calloc(1, sizeof (*new_bfh));
+ if (new_bfh == NULL) {
+ ret = ENOMEM;
+ goto err;
+ }
+
+ new_bfh->fd = fd;
+ new_bfh->page_sz = page_sz;
+ new_bfh->file_sz = st.st_size;
+
+ if (max_sz >= st.st_size) {
+ /* Whole-file method */
+ new_bfh->cache = malloc(st.st_size + 1);
+ if (new_bfh->cache) {
+ new_bfh->cache[st.st_size] = '\0';
+ new_bfh->cache_sz = st.st_size;
+ ret = read(fd, new_bfh->cache, st.st_size);
+ if (ret < 0) {
+ ret = errno;
+ goto err;
+ }
+ if (ret != st.st_size) {
+ ret = EIO; /* XXX ??? */
+ goto err;
+ }
+ if (reads)
+ *reads = 1;
+ (void) close(fd);
+ new_bfh->fd = -1;
+ *bfh = new_bfh;
+ return 0;
+ }
+ }
+
+ /* Block-size method, or above malloc() failed */
+ new_bfh->page = malloc(new_bfh->page_sz << 1);
+ if (new_bfh->page == NULL) {
+ /* Can't even allocate a single double-size page! */
+ ret = ENOMEM;
+ goto err;
+ }
+
+ new_bfh->cache_sz = max_sz < st.st_size ? max_sz : st.st_size;
+ new_bfh->cache = malloc(new_bfh->cache_sz);
+ *bfh = new_bfh;
+
+ /*
+ * malloc() may have failed because we were asking for a lot of
+ * memory, but we may still be able to operate without a cache,
+ * so let's not fail.
+ */
+ if (new_bfh->cache == NULL) {
+ new_bfh->cache_sz = 0;
+ return 0;
+ }
+
+ /* Initialize cache */
+ for (i = 0; i < new_bfh->cache_sz; i += new_bfh->page_sz)
+ new_bfh->cache[i] = '\0';
+ return 0;
+
+err:
+ (void) close(fd);
+ if (new_bfh) {
+ free(new_bfh->page);
+ free(new_bfh->cache);
+ free(new_bfh);
+ }
+ return ret;
+}
+
+/*
+ * Indicate whether the given binary search file handle will be searched
+ * with block-wise method.
+ */
+void
+_bsearch_file_info(bsearch_file_handle bfh,
+ size_t *page_sz, size_t *max_sz, int *blockwise)
+{
+ if (page_sz)
+ *page_sz = bfh->page_sz;
+ if (max_sz)
+ *max_sz = bfh->cache_sz;
+ if (blockwise)
+ *blockwise = (bfh->file_sz != bfh->cache_sz);
+}
+
+/*
+ * Close the given binary file search handle.
+ *
+ * Inputs:
+ *
+ * @bfh Pointer to variable containing handle to close.
+ */
+void
+_bsearch_file_close(bsearch_file_handle *bfh)
+{
+ if (!*bfh)
+ return;
+ if ((*bfh)->fd >= 0)
+ (void) close((*bfh)->fd);
+ if ((*bfh)->page)
+ free((*bfh)->page);
+ if ((*bfh)->cache)
+ free((*bfh)->cache);
+ free(*bfh);
+ *bfh = NULL;
+}
+
+/*
+ * Private function to get a page from a cache. The cache is a char
+ * array of 2^n - 1 double-size page worth of bytes, where n is the
+ * number of tree levels that the cache stores. The cache can be
+ * smaller than n implies.
+ *
+ * The page may or may not be valid. If the first byte of it is NUL
+ * then it's not valid, else it is.
+ *
+ * Returns 1 if page is in cache and valid, 0 if the cache is too small
+ * or the page is invalid. The page address is output in @buf if the
+ * cache is large enough to contain it regardless of whether the page is
+ * valid.
+ *
+ * Inputs:
+ *
+ * @bfh Binary search file handle
+ * @level Level in the tree that we want a page for
+ * @page_idx Page number in the given level (0..2^level - 1)
+ *
+ * Outputs:
+ *
+ * @buf Set to address of page if the cache is large enough
+ */
+static int
+get_page_from_cache(bsearch_file_handle bfh, size_t level, size_t page_idx,
+ char **buf)
+{
+ size_t idx = 0;
+ size_t page_sz;
+
+ page_sz = bfh->page_sz << 1; /* we use double-size pages in the cache */
+
+ *buf = NULL;
+
+ /*
+ * Compute index into cache. The cache is basically an array of
+ * double-size pages. The first (zeroth) double-size page in the
+ * cache will be the middle page of the file -- the root of the
+ * tree. The next two double-size pages will be the left and right
+ * pages of the second level in the tree. The next four double-size
+ * pages will be the four pages at the next level. And so on for as
+ * many pages as fit in the cache.
+ *
+ * The page index is the number of the page at the given level. We
+ * then compute (2^level - 1 + page index) * 2page size, check that
+ * we have that in the cache, check that the page has been read (it
+ * doesn't start with NUL).
+ */
+ if (level)
+ idx = (1 << level) - 1 + page_idx;
+ if (((idx + 1) * page_sz * 2) > bfh->cache_sz)
+ return 0;
+
+ *buf = &bfh->cache[idx * page_sz * 2];
+ if (bfh->cache[idx * page_sz * 2] == '\0')
+ return 0; /* cache[idx] == NUL -> page not loaded in cache */
+ return 1;
+}
+
+/*
+ * Private function to read a page of @page_sz from @fd at offset @off
+ * into @buf, outputing the number of bytes read, which will be the same
+ * as @page_sz unless the page being read is the last page, in which
+ * case the number of remaining bytes in the file will be output.
+ *
+ * Returns 0 on success or an errno value otherwise (EIO if reads are
+ * short).
+ *
+ * Inputs:
+ *
+ * @bfh Binary search file handle
+ * @level Level in the binary search tree that we're at
+ * @page_idx Page "index" at the @level of the tree that we want
+ * @page Actual page number that we want
+ * want_double Whether we need a page or double page read
+ *
+ * Outputs:
+ *
+ * @buf Page read or cached
+ * @bytes Bytes read (may be less than page or double page size in
+ * the case of the last page, of course)
+ */
+static int
+read_page(bsearch_file_handle bfh, size_t level, size_t page_idx, size_t page,
+ int want_double, const char **buf, size_t *bytes)
+{
+ int ret;
+ off_t off;
+ size_t expected;
+ size_t wanted;
+ char *page_buf;
+
+ /* Figure out where we're reading and how much */
+ off = page * bfh->page_sz;
+ if (off < 0)
+ return EOVERFLOW;
+
+ wanted = bfh->page_sz << want_double;
+ expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
+
+ if (get_page_from_cache(bfh, level, page_idx, &page_buf)) {
+ *buf = page_buf;
+ *bytes = expected;
+ return 0; /* found in cache */
+ }
+
+
+ *bytes = 0;
+ *buf = NULL;
+
+ /* OK, we have to read a page or double-size page */
+
+ if (page_buf)
+ want_double = 1; /* we'll be caching; we cache double-size pages */
+ else
+ page_buf = bfh->page; /* we won't cache this page */
+
+ wanted = bfh->page_sz << want_double;
+ expected = ((bfh->file_sz - off) > wanted) ? wanted : bfh->file_sz - off;
+
+#ifdef HAVE_PREAD
+ ret = pread(bfh->fd, page_buf, expected, off);
+#else
+ if (lseek(bfh->fd, off, SEEK_SET) == (off_t)-1)
+ return errno;
+ ret = read(bfh->fd, page_buf, expected);
+#endif
+ if (ret < 0)
+ return errno;
+
+ if (ret != expected)
+ return EIO; /* XXX ??? */
+
+ *buf = page_buf;
+ *bytes = expected;
+ return 0;
+}
+
+/*
+ * Perform a binary search of a file where each line is a record (LF and
+ * CRLF supported). Each record consists of a key followed by an
+ * optional value separated from the key by whitespace. Whitespace can
+ * be quoted with backslashes. It's the caller's responsibility to
+ * encode/decode keys/values if quoting is desired; newlines should be
+ * encoded such that a newline does not appear in the result.
+ *
+ * The search is done with block-wise I/O (i.e., the whole file is not
+ * read into memory).
+ *
+ * All output arguments are optional.
+ *
+ * Returns 0 if key is found, -1 if not found, or an error code such as
+ * ENOMEM in case of error.
+ *
+ * NOTE: We could improve this by not freeing the buffer, instead
+ * requiring that the caller provide it. Further, we could cache
+ * the top N levels of [double-size] pages (2^N - 1 pages), which
+ * should speed up most searches by reducing the number of reads
+ * by N.
+ *
+ * Inputs:
+ *
+ * @fd File descriptor (file to search)
+ * @page_sz Page size (if zero then the file's st_blksize will be used)
+ * @key Key string to search for
+ *
+ * Outputs:
+ *
+ * @value Location to store a copy of the value (caller must free)
+ * @location Record location if found else the location where the
+ * record should be inserted (index into @buf)
+ * @loops Location to store a count of bisections required for
+ * search (useful for confirming logarithmic performance)
+ * @reads Location to store a count of pages read during search
+ * (useful for confirming logarithmic performance)
+ */
+int
+_bsearch_file(bsearch_file_handle bfh, const char *key,
+ char **value, size_t *location, size_t *loops, size_t *reads)
+{
+ int ret;
+ const char *buf;
+ size_t buf_sz;
+ size_t page, l, r;
+ size_t my_reads = 0;
+ size_t my_loops_total = 0;
+ size_t my_loops;
+ size_t level; /* level in the tree */
+ size_t page_idx = 0; /* page number in the tree level */
+ size_t buf_location;
+ int cmp;
+ int buf_ends_in_eol = 0;
+ int buf_is_start = 0;
+
+ if (reads)
+ *reads = 0;
+
+ /* If whole file is in memory then search that and we're done */
+ if (bfh->file_sz == bfh->cache_sz)
+ return _bsearch_text(bfh->cache, bfh->cache_sz, key, value, location, loops);
+
+ /* Else block-wise binary search */
+
+ if (value)
+ *value = NULL;
+ if (loops)
+ *loops = 0;
+
+ l = 0;
+ r = (bfh->file_sz / bfh->page_sz) + 1;
+ for (level = 0, page = r >> 1; page >= l && page < r ; level++) {
+ ret = read_page(bfh, level, page_idx, page, 0, &buf, &buf_sz);
+ if (ret != 0)
+ return ret;
+ my_reads++;
+ if (buf[buf_sz - 1] == '\r' || buf[buf_sz - 1] == '\n')
+ buf_ends_in_eol = 1;
+ else
+ buf_ends_in_eol = 0;
+
+ buf_is_start = page == 0 ? 1 : 0;
+ ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
+ value, &buf_location, &cmp, &my_loops);
+ if (ret > 0)
+ return ret;
+ /* Found or no we update stats */
+ my_loops_total += my_loops;
+ if (loops)
+ *loops = my_loops_total;
+ if (reads)
+ *reads = my_reads;
+ if (location)
+ *location = page * bfh->page_sz + buf_location;
+ if (ret == 0)
+ return 0; /* found! */
+ /* Not found */
+ if (cmp < 0) {
+ /* Search left */
+ page_idx <<= 1;
+ r = page;
+ page = l + ((r - l) >> 1);
+ continue;
+ } else {
+ /*
+ * Search right, but first search the current and next
+ * blocks in case that the record we're looking for either
+ * straddles the boundary between this and the next record,
+ * or in case the record starts exactly at the next page.
+ */
+ heim_assert(cmp > 0, "cmp > 0");
+
+ if (!buf_ends_in_eol || page == l || page == (r - 1)) {
+ ret = read_page(bfh, level, page_idx, page, 1, &buf, &buf_sz);
+ if (ret != 0)
+ return ret;
+ my_reads++;
+
+ buf_is_start = page == l ? 1 : 0;
+
+ ret = bsearch_common(buf, (size_t)buf_sz, key, buf_is_start,
+ value, &buf_location, &cmp, &my_loops);
+ if (ret > 0)
+ return ret;
+ my_loops_total += my_loops;
+ if (loops)
+ *loops = my_loops_total;
+ if (reads)
+ *reads = my_reads;
+ if (location)
+ *location = page * bfh->page_sz + buf_location;
+ if (ret == 0)
+ return 0;
+ }
+
+ /* Oh well, search right */
+ if (l == page && r == (l + 1))
+ break;
+ page_idx = (page_idx << 1) + 1;
+ l = page;
+ page = l + ((r - l) >> 1);
+ continue;
+ }
+ }
+ return -1;
+}
+
+
+static int
+stdb_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ bsearch_file_handle bfh;
+ char *p;
+ int ret;
+
+ if (error)
+ *error = NULL;
+ if (dbname == NULL || *dbname == '\0') {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ N_("DB name required for sorted-text DB "
+ "plugin", ""));
+ return EINVAL;
+ }
+ p = strrchr(dbname, '.');
+ if (p == NULL || strcmp(p, ".txt") != 0) {
+ if (error)
+ *error = heim_error_create(ENOTSUP,
+ N_("Text file (name ending in .txt) "
+ "required for sorted-text DB plugin",
+ ""));
+ return ENOTSUP;
+ }
+
+ ret = _bsearch_file_open(dbname, 0, 0, &bfh, NULL);
+ if (ret)
+ return ret;
+
+ *db = bfh;
+ return 0;
+}
+
+static int
+stdb_close(void *db, heim_error_t *error)
+{
+ bsearch_file_handle bfh = db;
+
+ if (error)
+ *error = NULL;
+ _bsearch_file_close(&bfh);
+ return 0;
+}
+
+static heim_data_t
+stdb_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ bsearch_file_handle bfh = db;
+ const char *k;
+ char *v;
+ heim_data_t value;
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (table != HSTR(""))
+ return NULL;
+
+ if (heim_get_tid(key) == HEIM_TID_STRING)
+ k = heim_string_get_utf8((heim_string_t)key);
+ else
+ k = (const char *)heim_data_get_ptr(key);
+ ret = _bsearch_file(bfh, k, &v, NULL, NULL, NULL);
+ if (ret != 0) {
+ if (ret > 0 && error)
+ *error = heim_error_create(ret, "%s", strerror(ret));
+ return NULL;
+ }
+ value = heim_data_create(v, strlen(v));
+ free(v);
+ /* XXX Handle ENOMEM */
+ return value;
+}
+
+struct heim_db_type heim_sorted_text_file_dbtype = {
+ 1, stdb_open, NULL, stdb_close, NULL, NULL, NULL, NULL, NULL, NULL,
+ stdb_copy_value, NULL, NULL, NULL
+};
diff --git a/lib/base/data.c b/lib/base/data.c
new file mode 100644
index 000000000000..4aa6efc66774
--- /dev/null
+++ b/lib/base/data.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <string.h>
+
+static void
+data_dealloc(void *ptr)
+{
+ heim_data_t d = ptr;
+ heim_octet_string *os = (heim_octet_string *)d;
+ heim_data_free_f_t *deallocp;
+ heim_data_free_f_t dealloc;
+
+ if (os->data == NULL)
+ return;
+
+ /* Possible string ref */
+ deallocp = _heim_get_isaextra(os, 0);
+ dealloc = *deallocp;
+ if (dealloc != NULL)
+ dealloc(os->data);
+}
+
+static int
+data_cmp(void *a, void *b)
+{
+ heim_octet_string *osa = a, *osb = b;
+ if (osa->length != osb->length)
+ return osa->length - osb->length;
+ return memcmp(osa->data, osb->data, osa->length);
+}
+
+static unsigned long
+data_hash(void *ptr)
+{
+ heim_octet_string *os = ptr;
+ const unsigned char *s = os->data;
+
+ if (os->length < 4)
+ return os->length;
+ return s[0] | (s[1] << 8) |
+ (s[os->length - 2] << 16) | (s[os->length - 1] << 24);
+}
+
+struct heim_type_data _heim_data_object = {
+ HEIM_TID_DATA,
+ "data-object",
+ NULL,
+ data_dealloc,
+ NULL,
+ data_cmp,
+ data_hash,
+ NULL
+};
+
+/**
+ * Create a data object
+ *
+ * @param string the string to create, must be an utf8 string
+ *
+ * @return string object
+ */
+
+heim_data_t
+heim_data_create(const void *data, size_t length)
+{
+ heim_octet_string *os;
+
+ os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
+ if (os) {
+ os->data = (uint8_t *)os + sizeof(*os);
+ os->length = length;
+ memcpy(os->data, data, length);
+ }
+ return (heim_data_t)os;
+}
+
+heim_data_t
+heim_data_ref_create(const void *data, size_t length,
+ heim_data_free_f_t dealloc)
+{
+ heim_octet_string *os;
+ heim_data_free_f_t *deallocp;
+
+ os = _heim_alloc_object(&_heim_data_object, sizeof(*os) + length);
+ if (os) {
+ os->data = (void *)data;
+ os->length = length;
+ deallocp = _heim_get_isaextra(os, 0);
+ *deallocp = dealloc;
+ }
+ return (heim_data_t)os;
+}
+
+
+/**
+ * Return the type ID of data objects
+ *
+ * @return type id of data objects
+ */
+
+heim_tid_t
+heim_data_get_type_id(void)
+{
+ return HEIM_TID_DATA;
+}
+
+/**
+ * Get the data value of the content.
+ *
+ * @param data the data object to get the value from
+ *
+ * @return a heim_octet_string
+ */
+
+const heim_octet_string *
+heim_data_get_data(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return (const heim_octet_string *)data;
+}
+
+const void *
+heim_data_get_ptr(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return ((const heim_octet_string *)data)->data;
+}
+
+size_t heim_data_get_length(heim_data_t data)
+{
+ /* Note that this works for data and data_ref objects */
+ return ((const heim_octet_string *)data)->length;
+}
diff --git a/lib/base/db.c b/lib/base/db.c
new file mode 100644
index 000000000000..944091684882
--- /dev/null
+++ b/lib/base/db.c
@@ -0,0 +1,1727 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This is a pluggable simple DB abstraction, with a simple get/set/
+ * delete key/value pair interface.
+ *
+ * Plugins may provide any of the following optional features:
+ *
+ * - tables -- multiple attribute/value tables in one DB
+ * - locking
+ * - transactions (i.e., allow any heim_object_t as key or value)
+ * - transcoding of values
+ *
+ * Stackable plugins that provide missing optional features are
+ * possible.
+ *
+ * Any plugin that provides locking will also provide transactions, but
+ * those transactions will not be atomic in the face of failures (a
+ * memory-based rollback log is used).
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <io.h>
+#else
+#include <sys/file.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "baselocl.h"
+#include <base64.h>
+
+#define HEIM_ENOMEM(ep) \
+ (((ep) && !*(ep)) ? \
+ heim_error_get_code((*(ep) = heim_error_create_enomem())) : ENOMEM)
+
+#define HEIM_ERROR_HELPER(ep, ec, args) \
+ (((ep) && !*(ep)) ? \
+ heim_error_get_code((*(ep) = heim_error_create args)) : (ec))
+
+#define HEIM_ERROR(ep, ec, args) \
+ (ec == ENOMEM) ? HEIM_ENOMEM(ep) : HEIM_ERROR_HELPER(ep, ec, args);
+
+static heim_string_t to_base64(heim_data_t, heim_error_t *);
+static heim_data_t from_base64(heim_string_t, heim_error_t *);
+
+static int open_file(const char *, int , int, int *, heim_error_t *);
+static int read_json(const char *, heim_object_t *, heim_error_t *);
+static struct heim_db_type json_dbt;
+
+static void db_dealloc(void *ptr);
+
+struct heim_type_data db_object = {
+ HEIM_TID_DB,
+ "db-object",
+ NULL,
+ db_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+
+static heim_base_once_t db_plugin_init_once = HEIM_BASE_ONCE_INIT;
+
+static heim_dict_t db_plugins;
+
+typedef struct db_plugin {
+ heim_string_t name;
+ heim_db_plug_open_f_t openf;
+ heim_db_plug_clone_f_t clonef;
+ heim_db_plug_close_f_t closef;
+ heim_db_plug_lock_f_t lockf;
+ heim_db_plug_unlock_f_t unlockf;
+ heim_db_plug_sync_f_t syncf;
+ heim_db_plug_begin_f_t beginf;
+ heim_db_plug_commit_f_t commitf;
+ heim_db_plug_rollback_f_t rollbackf;
+ heim_db_plug_copy_value_f_t copyf;
+ heim_db_plug_set_value_f_t setf;
+ heim_db_plug_del_key_f_t delf;
+ heim_db_plug_iter_f_t iterf;
+ void *data;
+} db_plugin_desc, *db_plugin;
+
+struct heim_db_data {
+ db_plugin plug;
+ heim_string_t dbtype;
+ heim_string_t dbname;
+ heim_dict_t options;
+ void *db_data;
+ heim_data_t to_release;
+ heim_error_t error;
+ int ret;
+ unsigned int in_transaction:1;
+ unsigned int ro:1;
+ unsigned int ro_tx:1;
+ heim_dict_t set_keys;
+ heim_dict_t del_keys;
+ heim_string_t current_table;
+};
+
+static int
+db_do_log_actions(heim_db_t db, heim_error_t *error);
+static int
+db_replay_log(heim_db_t db, heim_error_t *error);
+
+static HEIMDAL_MUTEX db_type_mutex = HEIMDAL_MUTEX_INITIALIZER;
+
+static void
+db_init_plugins_once(void *arg)
+{
+ db_plugins = heim_retain(arg);
+}
+
+static void
+plugin_dealloc(void *arg)
+{
+ db_plugin plug = arg;
+
+ heim_release(plug->name);
+}
+
+/** heim_db_register
+ * @brief Registers a DB type for use with heim_db_create().
+ *
+ * @param dbtype Name of DB type
+ * @param data Private data argument to the dbtype's openf method
+ * @param plugin Structure with DB type methods (function pointers)
+ *
+ * Backends that provide begin/commit/rollback methods must provide ACID
+ * semantics.
+ *
+ * The registered DB type will have ACID semantics for backends that do
+ * not provide begin/commit/rollback methods but do provide lock/unlock
+ * and rdjournal/wrjournal methods (using a replay log journalling
+ * scheme).
+ *
+ * If the registered DB type does not natively provide read vs. write
+ * transaction isolation but does provide a lock method then the DB will
+ * provide read/write transaction isolation.
+ *
+ * @return ENOMEM on failure, else 0.
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_register(const char *dbtype,
+ void *data,
+ struct heim_db_type *plugin)
+{
+ heim_dict_t plugins;
+ heim_string_t s;
+ db_plugin plug, plug2;
+ int ret = 0;
+
+ if ((plugin->beginf != NULL && plugin->commitf == NULL) ||
+ (plugin->beginf != NULL && plugin->rollbackf == NULL) ||
+ (plugin->lockf != NULL && plugin->unlockf == NULL) ||
+ plugin->copyf == NULL)
+ heim_abort("Invalid DB plugin; make sure methods are paired");
+
+ /* Initialize */
+ plugins = heim_dict_create(11);
+ if (plugins == NULL)
+ return ENOMEM;
+ heim_base_once_f(&db_plugin_init_once, plugins, db_init_plugins_once);
+ heim_release(plugins);
+ heim_assert(db_plugins != NULL, "heim_db plugin table initialized");
+
+ s = heim_string_create(dbtype);
+ if (s == NULL)
+ return ENOMEM;
+
+ plug = heim_alloc(sizeof (*plug), "db_plug", plugin_dealloc);
+ if (plug == NULL) {
+ heim_release(s);
+ return ENOMEM;
+ }
+
+ plug->name = heim_retain(s);
+ plug->openf = plugin->openf;
+ plug->clonef = plugin->clonef;
+ plug->closef = plugin->closef;
+ plug->lockf = plugin->lockf;
+ plug->unlockf = plugin->unlockf;
+ plug->syncf = plugin->syncf;
+ plug->beginf = plugin->beginf;
+ plug->commitf = plugin->commitf;
+ plug->rollbackf = plugin->rollbackf;
+ plug->copyf = plugin->copyf;
+ plug->setf = plugin->setf;
+ plug->delf = plugin->delf;
+ plug->iterf = plugin->iterf;
+ plug->data = data;
+
+ HEIMDAL_MUTEX_lock(&db_type_mutex);
+ plug2 = heim_dict_get_value(db_plugins, s);
+ if (plug2 == NULL)
+ ret = heim_dict_set_value(db_plugins, s, plug);
+ HEIMDAL_MUTEX_unlock(&db_type_mutex);
+ heim_release(plug);
+ heim_release(s);
+
+ return ret;
+}
+
+static void
+db_dealloc(void *arg)
+{
+ heim_db_t db = arg;
+ heim_assert(!db->in_transaction,
+ "rollback or commit heim_db_t before releasing it");
+ if (db->db_data)
+ (void) db->plug->closef(db->db_data, NULL);
+ heim_release(db->to_release);
+ heim_release(db->dbtype);
+ heim_release(db->dbname);
+ heim_release(db->options);
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ heim_release(db->error);
+}
+
+struct dbtype_iter {
+ heim_db_t db;
+ const char *dbname;
+ heim_dict_t options;
+ heim_error_t *error;
+};
+
+/*
+ * Helper to create a DB handle with the first registered DB type that
+ * can open the given DB. This is useful when the app doesn't know the
+ * DB type a priori. This assumes that DB types can "taste" DBs, either
+ * from the filename extension or from the actual file contents.
+ */
+static void
+dbtype_iter2create_f(heim_object_t dbtype, heim_object_t junk, void *arg)
+{
+ struct dbtype_iter *iter_ctx = arg;
+
+ if (iter_ctx->db != NULL)
+ return;
+ iter_ctx->db = heim_db_create(heim_string_get_utf8(dbtype),
+ iter_ctx->dbname, iter_ctx->options,
+ iter_ctx->error);
+}
+
+/**
+ * Open a database of the given dbtype.
+ *
+ * Database type names can be composed of one or more pseudo-DB types
+ * and one concrete DB type joined with a '+' between each. For
+ * example: "transaction+bdb" might be a Berkeley DB with a layer above
+ * that provides transactions.
+ *
+ * Options may be provided via a dict (an associative array). Existing
+ * options include:
+ *
+ * - "create", with any value (create if DB doesn't exist)
+ * - "exclusive", with any value (exclusive create)
+ * - "truncate", with any value (truncate the DB)
+ * - "read-only", with any value (disallow writes)
+ * - "sync", with any value (make transactions durable)
+ * - "journal-name", with a string value naming a journal file name
+ *
+ * @param dbtype Name of DB type
+ * @param dbname Name of DB (likely a file path)
+ * @param options Options dict
+ * @param db Output open DB handle
+ * @param error Output error object
+ *
+ * @return a DB handle
+ *
+ * @addtogroup heimbase
+ */
+heim_db_t
+heim_db_create(const char *dbtype, const char *dbname,
+ heim_dict_t options, heim_error_t *error)
+{
+ heim_string_t s;
+ char *p;
+ db_plugin plug;
+ heim_db_t db;
+ int ret = 0;
+
+ if (options == NULL) {
+ options = heim_dict_create(11);
+ if (options == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ } else {
+ (void) heim_retain(options);
+ }
+
+ if (db_plugins == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ if (dbtype == NULL || *dbtype == '\0') {
+ struct dbtype_iter iter_ctx = { NULL, dbname, options, error};
+
+ /* Try all dbtypes */
+ heim_dict_iterate_f(db_plugins, &iter_ctx, dbtype_iter2create_f);
+ heim_release(options);
+ return iter_ctx.db;
+ } else if (strstr(dbtype, "json")) {
+ (void) heim_db_register(dbtype, NULL, &json_dbt);
+ }
+
+ /*
+ * Allow for dbtypes that are composed from pseudo-dbtypes chained
+ * to a real DB type with '+'. For example a pseudo-dbtype might
+ * add locking, transactions, transcoding of values, ...
+ */
+ p = strchr(dbtype, '+');
+ if (p != NULL)
+ s = heim_string_create_with_bytes(dbtype, p - dbtype);
+ else
+ s = heim_string_create(dbtype);
+ if (s == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ HEIMDAL_MUTEX_lock(&db_type_mutex);
+ plug = heim_dict_get_value(db_plugins, s);
+ HEIMDAL_MUTEX_unlock(&db_type_mutex);
+ heim_release(s);
+ if (plug == NULL) {
+ if (error)
+ *error = heim_error_create(ENOENT,
+ N_("Heimdal DB plugin not found: %s", ""),
+ dbtype);
+ heim_release(options);
+ return NULL;
+ }
+
+ db = _heim_alloc_object(&db_object, sizeof(*db));
+ if (db == NULL) {
+ heim_release(options);
+ return NULL;
+ }
+
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->plug = plug;
+ db->options = options;
+
+ ret = plug->openf(plug->data, dbtype, dbname, options, &db->db_data, error);
+ if (ret) {
+ heim_release(db);
+ if (error && *error == NULL)
+ *error = heim_error_create(ENOENT,
+ N_("Heimdal DB could not be opened: %s", ""),
+ dbname);
+ return NULL;
+ }
+
+ ret = db_replay_log(db, error);
+ if (ret) {
+ heim_release(db);
+ return NULL;
+ }
+
+ if (plug->clonef == NULL) {
+ db->dbtype = heim_string_create(dbtype);
+ db->dbname = heim_string_create(dbname);
+
+ if (!db->dbtype || ! db->dbname) {
+ heim_release(db);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ }
+
+ return db;
+}
+
+/**
+ * Clone (duplicate) an open DB handle.
+ *
+ * This is useful for multi-threaded applications. Applications must
+ * synchronize access to any given DB handle.
+ *
+ * Returns EBUSY if there is an open transaction for the input db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return a DB handle
+ *
+ * @addtogroup heimbase
+ */
+heim_db_t
+heim_db_clone(heim_db_t db, heim_error_t *error)
+{
+ heim_db_t result;
+ int ret;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ heim_abort("Expected a database");
+ if (db->in_transaction)
+ heim_abort("DB handle is busy");
+
+ if (db->plug->clonef == NULL) {
+ return heim_db_create(heim_string_get_utf8(db->dbtype),
+ heim_string_get_utf8(db->dbname),
+ db->options, error);
+ }
+
+ result = _heim_alloc_object(&db_object, sizeof(*result));
+ if (result == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ result->set_keys = NULL;
+ result->del_keys = NULL;
+ ret = db->plug->clonef(db->db_data, &result->db_data, error);
+ if (ret) {
+ heim_release(result);
+ if (error && !*error)
+ *error = heim_error_create(ENOENT,
+ N_("Could not re-open DB while cloning", ""));
+ return NULL;
+ }
+ db->db_data = NULL;
+ return result;
+}
+
+/**
+ * Open a transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_begin(heim_db_t db, int read_only, heim_error_t *error)
+{
+ int ret;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (db->in_transaction && (read_only || !db->ro_tx || (!read_only && !db->ro_tx)))
+ heim_abort("DB already in transaction");
+
+ if (db->plug->setf == NULL || db->plug->delf == NULL)
+ return EINVAL;
+
+ if (db->plug->beginf) {
+ ret = db->plug->beginf(db->db_data, read_only, error);
+ if (ret)
+ return ret;
+ } else if (!db->in_transaction) {
+ /* Try to emulate transactions */
+
+ if (db->plug->lockf == NULL)
+ return EINVAL; /* can't lock? -> no transactions */
+
+ /* Assume unlock provides sync/durability */
+ ret = db->plug->lockf(db->db_data, read_only, error);
+ if (ret)
+ return ret;
+
+ ret = db_replay_log(db, error);
+ if (ret) {
+ ret = db->plug->unlockf(db->db_data, error);
+ return ret;
+ }
+
+ db->set_keys = heim_dict_create(11);
+ if (db->set_keys == NULL)
+ return ENOMEM;
+ db->del_keys = heim_dict_create(11);
+ if (db->del_keys == NULL) {
+ heim_release(db->set_keys);
+ db->set_keys = NULL;
+ return ENOMEM;
+ }
+ } else {
+ heim_assert(read_only == 0, "Internal error");
+ ret = db->plug->lockf(db->db_data, 0, error);
+ if (ret)
+ return ret;
+ }
+ db->in_transaction = 1;
+ db->ro_tx = !!read_only;
+ return 0;
+}
+
+/**
+ * Commit an open transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_commit(heim_db_t db, heim_error_t *error)
+{
+ int ret, ret2;
+ heim_string_t journal_fname = NULL;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+ if (!db->in_transaction)
+ return 0;
+ if (db->plug->commitf == NULL && db->plug->lockf == NULL)
+ return EINVAL;
+
+ if (db->plug->commitf != NULL) {
+ ret = db->plug->commitf(db->db_data, error);
+ if (ret)
+ (void) db->plug->rollbackf(db->db_data, error);
+
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+ return ret;
+ }
+
+ if (db->ro_tx) {
+ ret = 0;
+ goto done;
+ }
+
+ if (db->options == NULL)
+ journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename"));
+
+ if (journal_fname != NULL) {
+ heim_array_t a;
+ heim_string_t journal_contents;
+ size_t len, bytes;
+ int save_errno;
+
+ /* Create contents for replay log */
+ ret = ENOMEM;
+ a = heim_array_create();
+ if (a == NULL)
+ goto err;
+ ret = heim_array_append_value(a, db->set_keys);
+ if (ret) {
+ heim_release(a);
+ goto err;
+ }
+ ret = heim_array_append_value(a, db->del_keys);
+ if (ret) {
+ heim_release(a);
+ goto err;
+ }
+ journal_contents = heim_json_copy_serialize(a, 0, error);
+ heim_release(a);
+
+ /* Write replay log */
+ if (journal_fname != NULL) {
+ int fd;
+
+ ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error);
+ if (ret) {
+ heim_release(journal_contents);
+ goto err;
+ }
+ len = strlen(heim_string_get_utf8(journal_contents));
+ bytes = write(fd, heim_string_get_utf8(journal_contents), len);
+ save_errno = errno;
+ heim_release(journal_contents);
+ ret = close(fd);
+ if (bytes != len) {
+ /* Truncate replay log */
+ (void) open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error);
+ ret = save_errno;
+ goto err;
+ }
+ if (ret)
+ goto err;
+ }
+ }
+
+ /* Apply logged actions */
+ ret = db_do_log_actions(db, error);
+ if (ret)
+ return ret;
+
+ if (db->plug->syncf != NULL) {
+ /* fsync() or whatever */
+ ret = db->plug->syncf(db->db_data, error);
+ if (ret)
+ return ret;
+ }
+
+ /* Truncate replay log and we're done */
+ if (journal_fname != NULL) {
+ int fd;
+
+ ret2 = open_file(heim_string_get_utf8(journal_fname), 1, 0, &fd, error);
+ if (ret2 == 0)
+ (void) close(fd);
+ }
+
+ /*
+ * Clean up; if we failed to remore the replay log that's OK, we'll
+ * handle that again in heim_db_commit()
+ */
+done:
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+
+ ret2 = db->plug->unlockf(db->db_data, error);
+ if (ret == 0)
+ ret = ret2;
+
+ return ret;
+
+err:
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Error while committing transaction: %s", ""),
+ strerror(ret)));
+}
+
+/**
+ * Rollback an open transaction on the given db.
+ *
+ * @param db Open DB handle
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_rollback(heim_db_t db, heim_error_t *error)
+{
+ int ret = 0;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+ if (!db->in_transaction)
+ return 0;
+
+ if (db->plug->rollbackf != NULL)
+ ret = db->plug->rollbackf(db->db_data, error);
+ else if (db->plug->unlockf != NULL)
+ ret = db->plug->unlockf(db->db_data, error);
+
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+ db->in_transaction = 0;
+ db->ro_tx = 0;
+
+ return ret;
+}
+
+/**
+ * Get type ID of heim_db_t objects.
+ *
+ * @addtogroup heimbase
+ */
+heim_tid_t
+heim_db_get_type_id(void)
+{
+ return HEIM_TID_DB;
+}
+
+heim_data_t
+_heim_db_get_value(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_release(db->to_release);
+ db->to_release = heim_db_copy_value(db, table, key, error);
+ return db->to_release;
+}
+
+/**
+ * Lookup a key's value in the DB.
+ *
+ * Returns 0 on success, -1 if the key does not exist in the DB, or a
+ * system error number on failure.
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param error Output error object
+ *
+ * @return the value (retained), if there is one for the given key
+ *
+ * @addtogroup heimbase
+ */
+heim_data_t
+heim_db_copy_value(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_object_t v;
+ heim_data_t result;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return NULL;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (db->in_transaction) {
+ heim_string_t key64;
+
+ key64 = to_base64(key, error);
+ if (key64 == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ v = heim_path_copy(db->set_keys, error, table, key64, NULL);
+ if (v != NULL) {
+ heim_release(key64);
+ return v;
+ }
+ v = heim_path_copy(db->del_keys, error, table, key64, NULL); /* can't be NULL */
+ heim_release(key64);
+ if (v != NULL)
+ return NULL;
+ }
+
+ result = db->plug->copyf(db->db_data, table, key, error);
+
+ return result;
+}
+
+/**
+ * Set a key's value in the DB.
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param value Value (if NULL the key will be deleted, but empty is OK)
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_set_value(heim_db_t db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ heim_string_t key64 = NULL;
+ int ret;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (value == NULL)
+ /* Use heim_null_t instead of NULL */
+ return heim_db_delete_key(db, table, key, error);
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (heim_get_tid(key) != HEIM_TID_DATA)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("DB keys must be data", "")));
+
+ if (db->plug->setf == NULL)
+ return EBADF;
+
+ if (!db->in_transaction) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ heim_assert(db->in_transaction, "Internal error");
+ ret = heim_db_set_value(db, table, key, value, error);
+ if (ret) {
+ (void) heim_db_rollback(db, NULL);
+ return ret;
+ }
+ return heim_db_commit(db, error);
+ }
+
+ /* Transaction emulation */
+ heim_assert(db->set_keys != NULL, "Internal error");
+ key64 = to_base64(key, error);
+ if (key64 == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (db->ro_tx) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ }
+ ret = heim_path_create(db->set_keys, 29, value, error, table, key64, NULL);
+ if (ret)
+ goto err;
+ heim_path_delete(db->del_keys, error, table, key64, NULL);
+ heim_release(key64);
+
+ return 0;
+
+err:
+ heim_release(key64);
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Could not set a dict value while while "
+ "setting a DB value", "")));
+}
+
+/**
+ * Delete a key and its value from the DB
+ *
+ *
+ * @param db Open DB handle
+ * @param key Key
+ * @param error Output error object
+ *
+ * @return 0 on success, system error otherwise
+ *
+ * @addtogroup heimbase
+ */
+int
+heim_db_delete_key(heim_db_t db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ heim_string_t key64 = NULL;
+ int ret;
+
+ if (error != NULL)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return EINVAL;
+
+ if (db->plug->delf == NULL)
+ return EBADF;
+
+ if (!db->in_transaction) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ heim_assert(db->in_transaction, "Internal error");
+ ret = heim_db_delete_key(db, table, key, error);
+ if (ret) {
+ (void) heim_db_rollback(db, NULL);
+ return ret;
+ }
+ return heim_db_commit(db, error);
+ }
+
+ /* Transaction emulation */
+ heim_assert(db->set_keys != NULL, "Internal error");
+ key64 = to_base64(key, error);
+ if (key64 == NULL)
+ return HEIM_ENOMEM(error);
+ if (db->ro_tx) {
+ ret = heim_db_begin(db, 0, error);
+ if (ret)
+ goto err;
+ }
+ ret = heim_path_create(db->del_keys, 29, heim_number_create(1), error, table, key64, NULL);
+ if (ret)
+ goto err;
+ heim_path_delete(db->set_keys, error, table, key64, NULL);
+ heim_release(key64);
+
+ return 0;
+
+err:
+ heim_release(key64);
+ return HEIM_ERROR(error, ret,
+ (ret, N_("Could not set a dict value while while "
+ "deleting a DB value", "")));
+}
+
+/**
+ * Iterate a callback function over keys and values from a DB.
+ *
+ * @param db Open DB handle
+ * @param iter_data Callback function's private data
+ * @param iter_f Callback function, called once per-key/value pair
+ * @param error Output error object
+ *
+ * @addtogroup heimbase
+ */
+void
+heim_db_iterate_f(heim_db_t db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ if (error != NULL)
+ *error = NULL;
+
+ if (heim_get_tid(db) != HEIM_TID_DB)
+ return;
+
+ if (!db->in_transaction)
+ db->plug->iterf(db->db_data, table, iter_data, iter_f, error);
+}
+
+static void
+db_replay_log_table_set_keys_iter(heim_object_t key, heim_object_t value,
+ void *arg)
+{
+ heim_db_t db = arg;
+ heim_data_t k, v;
+
+ if (db->ret)
+ return;
+
+ k = from_base64((heim_string_t)key, &db->error);
+ if (k == NULL) {
+ db->ret = ENOMEM;
+ return;
+ }
+ v = (heim_data_t)value;
+
+ db->ret = db->plug->setf(db->db_data, db->current_table, k, v, &db->error);
+ heim_release(k);
+}
+
+static void
+db_replay_log_table_del_keys_iter(heim_object_t key, heim_object_t value,
+ void *arg)
+{
+ heim_db_t db = arg;
+ heim_data_t k;
+
+ if (db->ret) {
+ db->ret = ENOMEM;
+ return;
+ }
+
+ k = from_base64((heim_string_t)key, &db->error);
+ if (k == NULL)
+ return;
+
+ db->ret = db->plug->delf(db->db_data, db->current_table, k, &db->error);
+ heim_release(k);
+}
+
+static void
+db_replay_log_set_keys_iter(heim_object_t table, heim_object_t table_dict,
+ void *arg)
+{
+ heim_db_t db = arg;
+
+ if (db->ret)
+ return;
+
+ db->current_table = table;
+ heim_dict_iterate_f(table_dict, db, db_replay_log_table_set_keys_iter);
+}
+
+static void
+db_replay_log_del_keys_iter(heim_object_t table, heim_object_t table_dict,
+ void *arg)
+{
+ heim_db_t db = arg;
+
+ if (db->ret)
+ return;
+
+ db->current_table = table;
+ heim_dict_iterate_f(table_dict, db, db_replay_log_table_del_keys_iter);
+}
+
+static int
+db_do_log_actions(heim_db_t db, heim_error_t *error)
+{
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ db->ret = 0;
+ db->error = NULL;
+ if (db->set_keys != NULL)
+ heim_dict_iterate_f(db->set_keys, db, db_replay_log_set_keys_iter);
+ if (db->del_keys != NULL)
+ heim_dict_iterate_f(db->del_keys, db, db_replay_log_del_keys_iter);
+
+ ret = db->ret;
+ db->ret = 0;
+ if (error && db->error) {
+ *error = db->error;
+ db->error = NULL;
+ } else {
+ heim_release(db->error);
+ db->error = NULL;
+ }
+ return ret;
+}
+
+static int
+db_replay_log(heim_db_t db, heim_error_t *error)
+{
+ int ret;
+ heim_string_t journal_fname = NULL;
+ heim_object_t journal;
+ size_t len;
+
+ heim_assert(!db->in_transaction, "DB transaction not open");
+ heim_assert(db->set_keys == NULL && db->set_keys == NULL, "DB transaction not open");
+
+ if (error)
+ *error = NULL;
+
+ if (db->options == NULL)
+ return 0;
+
+ journal_fname = heim_dict_get_value(db->options, HSTR("journal-filename"));
+ if (journal_fname == NULL)
+ return 0;
+
+ ret = read_json(heim_string_get_utf8(journal_fname), &journal, error);
+ if (ret == ENOENT) {
+ heim_release(journal_fname);
+ return 0;
+ }
+ if (ret == 0 && journal == NULL) {
+ heim_release(journal_fname);
+ return 0;
+ }
+ if (ret != 0) {
+ heim_release(journal_fname);
+ return ret;
+ }
+
+ if (heim_get_tid(journal) != HEIM_TID_ARRAY) {
+ heim_release(journal_fname);
+ return HEIM_ERROR(error, EINVAL,
+ (ret, N_("Invalid journal contents; delete journal",
+ "")));
+ }
+
+ len = heim_array_get_length(journal);
+
+ if (len > 0)
+ db->set_keys = heim_array_get_value(journal, 0);
+ if (len > 1)
+ db->del_keys = heim_array_get_value(journal, 1);
+ ret = db_do_log_actions(db, error);
+ if (ret) {
+ heim_release(journal_fname);
+ return ret;
+ }
+
+ /* Truncate replay log and we're done */
+ ret = open_file(heim_string_get_utf8(journal_fname), 1, 0, NULL, error);
+ heim_release(journal_fname);
+ if (ret)
+ return ret;
+ heim_release(db->set_keys);
+ heim_release(db->del_keys);
+ db->set_keys = NULL;
+ db->del_keys = NULL;
+
+ return 0;
+}
+
+static
+heim_string_t to_base64(heim_data_t data, heim_error_t *error)
+{
+ char *b64 = NULL;
+ heim_string_t s = NULL;
+ const heim_octet_string *d;
+ int ret;
+
+ d = heim_data_get_data(data);
+ ret = rk_base64_encode(d->data, d->length, &b64);
+ if (ret < 0 || b64 == NULL)
+ goto enomem;
+ s = heim_string_ref_create(b64, free);
+ if (s == NULL)
+ goto enomem;
+ return s;
+
+enomem:
+ free(b64);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+}
+
+static
+heim_data_t from_base64(heim_string_t s, heim_error_t *error)
+{
+ void *buf;
+ size_t len;
+ heim_data_t d;
+
+ buf = malloc(strlen(heim_string_get_utf8(s)));
+ if (buf == NULL)
+ goto enomem;
+
+ len = rk_base64_decode(heim_string_get_utf8(s), buf);
+ d = heim_data_ref_create(buf, len, free);
+ if (d == NULL)
+ goto enomem;
+ return d;
+
+enomem:
+ free(buf);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+}
+
+
+static int
+open_file(const char *dbname, int for_write, int excl, int *fd_out, heim_error_t *error)
+{
+#ifdef WIN32
+ HANDLE hFile;
+ int ret = 0;
+
+ if (fd_out)
+ *fd_out = -1;
+
+ if (for_write)
+ hFile = CreateFile(dbname, GENERIC_WRITE | GENERIC_READ, 0,
+ NULL, /* we'll close as soon as we read */
+ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+ else
+ hFile = CreateFile(dbname, GENERIC_READ, FILE_SHARE_READ,
+ NULL, /* we'll close as soon as we read */
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ ret = GetLastError();
+ _set_errno(ret); /* CreateFile() does not set errno */
+ goto err;
+ }
+ if (fd_out == NULL) {
+ (void) CloseHandle(hFile);
+ return 0;
+ }
+
+ *fd_out = _open_osfhandle((intptr_t) hFile, 0);
+ if (*fd_out < 0) {
+ ret = errno;
+ (void) CloseHandle(hFile);
+ goto err;
+ }
+
+ /* No need to lock given share deny mode */
+ return 0;
+
+err:
+ if (error != NULL) {
+ char *s = NULL;
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ 0, ret, 0, (LPTSTR) &s, 0, NULL);
+ *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""),
+ dbname, s ? s : "<error formatting error>");
+ LocalFree(s);
+ }
+ return ret;
+#else
+ int ret = 0;
+ int fd;
+
+ if (fd_out)
+ *fd_out = -1;
+
+ if (for_write && excl)
+ fd = open(dbname, O_CREAT | O_EXCL | O_WRONLY, 0600);
+ else if (for_write)
+ fd = open(dbname, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+ else
+ fd = open(dbname, O_RDONLY);
+ if (fd < 0) {
+ if (error != NULL)
+ *error = heim_error_create(ret, N_("Could not open JSON file %s: %s", ""),
+ dbname, strerror(errno));
+ return errno;
+ }
+
+ if (fd_out == NULL) {
+ (void) close(fd);
+ return 0;
+ }
+
+ ret = flock(fd, for_write ? LOCK_EX : LOCK_SH);
+ if (ret == -1) {
+ /* Note that we if O_EXCL we're leaving the [lock] file around */
+ (void) close(fd);
+ return HEIM_ERROR(error, errno,
+ (errno, N_("Could not lock JSON file %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+
+ *fd_out = fd;
+
+ return 0;
+#endif
+}
+
+static int
+read_json(const char *dbname, heim_object_t *out, heim_error_t *error)
+{
+ struct stat st;
+ char *str = NULL;
+ int ret;
+ int fd = -1;
+ ssize_t bytes;
+
+ *out = NULL;
+ ret = open_file(dbname, 0, 0, &fd, error);
+ if (ret)
+ return ret;
+
+ ret = fstat(fd, &st);
+ if (ret == -1) {
+ (void) close(fd);
+ return HEIM_ERROR(error, errno,
+ (ret, N_("Could not stat JSON DB %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+
+ if (st.st_size == 0) {
+ (void) close(fd);
+ return 0;
+ }
+
+ str = malloc(st.st_size + 1);
+ if (str == NULL) {
+ (void) close(fd);
+ return HEIM_ENOMEM(error);
+ }
+
+ bytes = read(fd, str, st.st_size);
+ (void) close(fd);
+ if (bytes != st.st_size) {
+ free(str);
+ if (bytes >= 0)
+ errno = EINVAL; /* ?? */
+ return HEIM_ERROR(error, errno,
+ (ret, N_("Could not read JSON DB %s: %s", ""),
+ dbname, strerror(errno)));
+ }
+ str[st.st_size] = '\0';
+ *out = heim_json_create(str, 10, 0, error);
+ free(str);
+ if (*out == NULL)
+ return (error && *error) ? heim_error_get_code(*error) : EINVAL;
+ return 0;
+}
+
+typedef struct json_db {
+ heim_dict_t dict;
+ heim_string_t dbname;
+ heim_string_t bkpname;
+ int fd;
+ time_t last_read_time;
+ unsigned int read_only:1;
+ unsigned int locked:1;
+ unsigned int locked_needs_unlink:1;
+} *json_db_t;
+
+static int
+json_db_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ json_db_t jsondb;
+ heim_dict_t contents = NULL;
+ heim_string_t dbname_s = NULL;
+ heim_string_t bkpname_s = NULL;
+
+ if (error)
+ *error = NULL;
+ if (dbtype && *dbtype && strcmp(dbtype, "json"))
+ return HEIM_ERROR(error, EINVAL, (EINVAL, N_("Wrong DB type", "")));
+ if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0) {
+ char *ext = strrchr(dbname, '.');
+ char *bkpname;
+ size_t len;
+ int ret;
+
+ if (ext == NULL || strcmp(ext, ".json") != 0)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB files must end in .json",
+ "")));
+
+ if (options) {
+ heim_object_t vc, ve, vt;
+
+ vc = heim_dict_get_value(options, HSTR("create"));
+ ve = heim_dict_get_value(options, HSTR("exclusive"));
+ vt = heim_dict_get_value(options, HSTR("truncate"));
+ if (vc && vt) {
+ ret = open_file(dbname, 1, ve ? 1 : 0, NULL, error);
+ if (ret)
+ return ret;
+ } else if (vc || ve || vt) {
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("Invalid JSON DB open options",
+ "")));
+ }
+ /*
+ * We don't want cloned handles to truncate the DB, eh?
+ *
+ * We should really just create a copy of the options dict
+ * rather than modify the caller's! But for that it'd be
+ * nicer to have copy utilities in heimbase, something like
+ * this:
+ *
+ * heim_object_t heim_copy(heim_object_t src, int depth,
+ * heim_error_t *error);
+ *
+ * so that options = heim_copy(options, 1); means copy the
+ * dict but nothing else (whereas depth == 0 would mean
+ * heim_retain(), and depth > 1 would be copy that many
+ * levels).
+ */
+ heim_dict_delete_key(options, HSTR("create"));
+ heim_dict_delete_key(options, HSTR("exclusive"));
+ heim_dict_delete_key(options, HSTR("truncate"));
+ }
+ dbname_s = heim_string_create(dbname);
+ if (dbname_s == NULL)
+ return HEIM_ENOMEM(error);
+
+ len = snprintf(NULL, 0, "%s~", dbname);
+ bkpname = malloc(len + 2);
+ if (bkpname == NULL) {
+ heim_release(dbname_s);
+ return HEIM_ENOMEM(error);
+ }
+ (void) snprintf(bkpname, len + 1, "%s~", dbname);
+ bkpname_s = heim_string_create(bkpname);
+ free(bkpname);
+ if (bkpname_s == NULL) {
+ heim_release(dbname_s);
+ return HEIM_ENOMEM(error);
+ }
+
+ ret = read_json(dbname, (heim_object_t *)&contents, error);
+ if (ret) {
+ heim_release(bkpname_s);
+ heim_release(dbname_s);
+ return ret;
+ }
+
+ if (contents != NULL && heim_get_tid(contents) != HEIM_TID_DICT) {
+ heim_release(bkpname_s);
+ heim_release(dbname_s);
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB contents not valid JSON",
+ "")));
+ }
+ }
+
+ jsondb = heim_alloc(sizeof (*jsondb), "json_db", NULL);
+ if (jsondb == NULL) {
+ heim_release(contents);
+ heim_release(dbname_s);
+ heim_release(bkpname_s);
+ return ENOMEM;
+ }
+
+ jsondb->last_read_time = time(NULL);
+ jsondb->fd = -1;
+ jsondb->dbname = dbname_s;
+ jsondb->bkpname = bkpname_s;
+ jsondb->read_only = 0;
+
+ if (contents != NULL)
+ jsondb->dict = contents;
+ else {
+ jsondb->dict = heim_dict_create(29);
+ if (jsondb->dict == NULL) {
+ heim_release(jsondb);
+ return ENOMEM;
+ }
+ }
+
+ *db = jsondb;
+ return 0;
+}
+
+static int
+json_db_close(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+
+ if (error)
+ *error = NULL;
+ if (jsondb->fd > -1)
+ (void) close(jsondb->fd);
+ jsondb->fd = -1;
+ heim_release(jsondb->dbname);
+ heim_release(jsondb->bkpname);
+ heim_release(jsondb->dict);
+ heim_release(jsondb);
+ return 0;
+}
+
+static int
+json_db_lock(void *db, int read_only, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ int ret;
+
+ heim_assert(jsondb->fd == -1 || (jsondb->read_only && !read_only),
+ "DB locks are not recursive");
+
+ jsondb->read_only = read_only ? 1 : 0;
+ if (jsondb->fd > -1)
+ return 0;
+
+ ret = open_file(heim_string_get_utf8(jsondb->bkpname), 1, 1, &jsondb->fd, error);
+ if (ret == 0) {
+ jsondb->locked_needs_unlink = 1;
+ jsondb->locked = 1;
+ }
+ return ret;
+}
+
+static int
+json_db_unlock(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ int ret = 0;
+
+ heim_assert(jsondb->locked, "DB not locked when unlock attempted");
+ if (jsondb->fd > -1)
+ ret = close(jsondb->fd);
+ jsondb->fd = -1;
+ jsondb->read_only = 0;
+ jsondb->locked = 0;
+ if (jsondb->locked_needs_unlink)
+ unlink(heim_string_get_utf8(jsondb->bkpname));
+ jsondb->locked_needs_unlink = 0;
+ return ret;
+}
+
+static int
+json_db_sync(void *db, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ size_t len, bytes;
+ heim_error_t e;
+ heim_string_t json;
+ const char *json_text = NULL;
+ int ret = 0;
+ int fd = -1;
+#ifdef WIN32
+ int tries = 3;
+#endif
+
+ heim_assert(jsondb->fd > -1, "DB not locked when sync attempted");
+
+ json = heim_json_copy_serialize(jsondb->dict, 0, &e);
+ if (json == NULL) {
+ if (error)
+ *error = e;
+ else
+ heim_release(e);
+ return heim_error_get_code(e);
+ }
+
+ json_text = heim_string_get_utf8(json);
+ len = strlen(json_text);
+ errno = 0;
+
+#ifdef WIN32
+ while (tries--) {
+ ret = open_file(heim_string_get_utf8(jsondb->dbname), 1, 0, &fd, error);
+ if (ret == 0)
+ break;
+ sleep(1);
+ }
+ if (ret) {
+ heim_release(json);
+ return ret;
+ }
+#else
+ fd = jsondb->fd;
+#endif /* WIN32 */
+
+ bytes = write(fd, json_text, len);
+ heim_release(json);
+ if (bytes != len)
+ return errno ? errno : EIO;
+ ret = fsync(fd);
+ if (ret)
+ return ret;
+
+#ifdef WIN32
+ ret = close(fd);
+ if (ret)
+ return GetLastError();
+#else
+ ret = rename(heim_string_get_utf8(jsondb->bkpname), heim_string_get_utf8(jsondb->dbname));
+ if (ret == 0) {
+ jsondb->locked_needs_unlink = 0;
+ return 0;
+ }
+#endif /* WIN32 */
+
+ return errno;
+}
+
+static heim_data_t
+json_db_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+ struct stat st;
+ heim_data_t result;
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length) {
+ HEIM_ERROR(error, EINVAL,
+ (EINVAL, N_("JSON DB requires keys that are actually "
+ "strings", "")));
+ return NULL;
+ }
+
+ if (stat(heim_string_get_utf8(jsondb->dbname), &st) == -1) {
+ HEIM_ERROR(error, errno,
+ (errno, N_("Could not stat JSON DB file", "")));
+ return NULL;
+ }
+
+ if (st.st_mtime > jsondb->last_read_time ||
+ st.st_ctime > jsondb->last_read_time) {
+ heim_dict_t contents = NULL;
+ int ret;
+
+ /* Ignore file is gone (ENOENT) */
+ ret = read_json(heim_string_get_utf8(jsondb->dbname),
+ (heim_object_t *)&contents, error);
+ if (ret)
+ return NULL;
+ if (contents == NULL)
+ contents = heim_dict_create(29);
+ heim_release(jsondb->dict);
+ jsondb->dict = contents;
+ jsondb->last_read_time = time(NULL);
+ }
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL) {
+ (void) HEIM_ENOMEM(error);
+ return NULL;
+ }
+
+ result = heim_path_copy(jsondb->dict, error, table, key_string, NULL);
+ heim_release(key_string);
+ return result;
+}
+
+static int
+json_db_set_value(void *db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL,
+ N_("JSON DB requires keys that are actually strings",
+ "")));
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (table == NULL)
+ table = HSTR("");
+
+ ret = heim_path_create(jsondb->dict, 29, value, error, table, key_string, NULL);
+ heim_release(key_string);
+ return ret;
+}
+
+static int
+json_db_del_key(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ heim_string_t key_string;
+ const heim_octet_string *key_data = heim_data_get_data(key);
+
+ if (error)
+ *error = NULL;
+
+ if (strnlen(key_data->data, key_data->length) != key_data->length)
+ return HEIM_ERROR(error, EINVAL,
+ (EINVAL,
+ N_("JSON DB requires keys that are actually strings",
+ "")));
+
+ key_string = heim_string_create_with_bytes(key_data->data,
+ key_data->length);
+ if (key_string == NULL)
+ return HEIM_ENOMEM(error);
+
+ if (table == NULL)
+ table = HSTR("");
+
+ heim_path_delete(jsondb->dict, error, table, key_string, NULL);
+ heim_release(key_string);
+ return 0;
+}
+
+struct json_db_iter_ctx {
+ heim_db_iterator_f_t iter_f;
+ void *iter_ctx;
+};
+
+static void json_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct json_db_iter_ctx *ctx = arg;
+ const char *key_string;
+ heim_data_t key_data;
+
+ key_string = heim_string_get_utf8((heim_string_t)key);
+ key_data = heim_data_ref_create(key_string, strlen(key_string), NULL);
+ ctx->iter_f(key_data, (heim_object_t)value, ctx->iter_ctx);
+ heim_release(key_data);
+}
+
+static void
+json_db_iter(void *db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ json_db_t jsondb = db;
+ struct json_db_iter_ctx ctx;
+ heim_dict_t table_dict;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ table_dict = heim_dict_get_value(jsondb->dict, table);
+ if (table_dict == NULL)
+ return;
+
+ ctx.iter_ctx = iter_data;
+ ctx.iter_f = iter_f;
+
+ heim_dict_iterate_f(table_dict, &ctx, json_db_iter_f);
+}
+
+static struct heim_db_type json_dbt = {
+ 1, json_db_open, NULL, json_db_close,
+ json_db_lock, json_db_unlock, json_db_sync,
+ NULL, NULL, NULL,
+ json_db_copy_value, json_db_set_value,
+ json_db_del_key, json_db_iter
+};
+
diff --git a/lib/base/dict.c b/lib/base/dict.c
new file mode 100644
index 000000000000..8d73846b2bbb
--- /dev/null
+++ b/lib/base/dict.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2002, 1997 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct hashentry {
+ struct hashentry **prev;
+ struct hashentry *next;
+ heim_object_t key;
+ heim_object_t value;
+};
+
+struct heim_dict_data {
+ size_t size;
+ struct hashentry **tab;
+};
+
+static void
+dict_dealloc(void *ptr)
+{
+ heim_dict_t dict = ptr;
+ struct hashentry **h, *g, *i;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h) {
+ for (g = h[0]; g; g = i) {
+ i = g->next;
+ heim_release(g->key);
+ heim_release(g->value);
+ free(g);
+ }
+ }
+ free(dict->tab);
+}
+
+struct heim_type_data dict_object = {
+ HEIM_TID_DICT,
+ "dict-object",
+ NULL,
+ dict_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static size_t
+isprime(size_t p)
+{
+ size_t q, i;
+
+ for(i = 2 ; i < p; i++) {
+ q = p / i;
+
+ if (i * q == p)
+ return 0;
+ if (i * i > p)
+ return 1;
+ }
+ return 1;
+}
+
+static size_t
+findprime(size_t p)
+{
+ if (p % 2 == 0)
+ p++;
+
+ while (isprime(p) == 0)
+ p += 2;
+
+ return p;
+}
+
+/**
+ * Allocate an array
+ *
+ * @return A new allocated array, free with heim_release()
+ */
+
+heim_dict_t
+heim_dict_create(size_t size)
+{
+ heim_dict_t dict;
+
+ dict = _heim_alloc_object(&dict_object, sizeof(*dict));
+
+ dict->size = findprime(size);
+ if (dict->size == 0) {
+ heim_release(dict);
+ return NULL;
+ }
+
+ dict->tab = calloc(dict->size, sizeof(dict->tab[0]));
+ if (dict->tab == NULL) {
+ dict->size = 0;
+ heim_release(dict);
+ return NULL;
+ }
+
+ return dict;
+}
+
+/**
+ * Get type id of an dict
+ *
+ * @return the type id
+ */
+
+heim_tid_t
+heim_dict_get_type_id(void)
+{
+ return HEIM_TID_DICT;
+}
+
+/* Intern search function */
+
+static struct hashentry *
+_search(heim_dict_t dict, heim_object_t ptr)
+{
+ unsigned long v = heim_get_hash(ptr);
+ struct hashentry *p;
+
+ for (p = dict->tab[v % dict->size]; p != NULL; p = p->next)
+ if (heim_cmp(ptr, p->key) == 0)
+ return p;
+
+ return NULL;
+}
+
+/**
+ * Search for element in hash table
+ *
+ * @value dict the dict to search in
+ * @value key the key to search for
+ *
+ * @return a not-retained copy of the value for key or NULL if not found
+ */
+
+heim_object_t
+heim_dict_get_value(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *p;
+ p = _search(dict, key);
+ if (p == NULL)
+ return NULL;
+
+ return p->value;
+}
+
+/**
+ * Search for element in hash table
+ *
+ * @value dict the dict to search in
+ * @value key the key to search for
+ *
+ * @return a retained copy of the value for key or NULL if not found
+ */
+
+heim_object_t
+heim_dict_copy_value(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *p;
+ p = _search(dict, key);
+ if (p == NULL)
+ return NULL;
+
+ return heim_retain(p->value);
+}
+
+/**
+ * Add key and value to dict
+ *
+ * @value dict the dict to add too
+ * @value key the key to add
+ * @value value the value to add
+ *
+ * @return 0 if added, errno if not
+ */
+
+int
+heim_dict_set_value(heim_dict_t dict, heim_object_t key, heim_object_t value)
+{
+ struct hashentry **tabptr, *h;
+
+ h = _search(dict, key);
+ if (h) {
+ heim_release(h->value);
+ h->value = heim_retain(value);
+ } else {
+ unsigned long v;
+
+ h = malloc(sizeof(*h));
+ if (h == NULL)
+ return ENOMEM;
+
+ h->key = heim_retain(key);
+ h->value = heim_retain(value);
+
+ v = heim_get_hash(key);
+
+ tabptr = &dict->tab[v % dict->size];
+ h->next = *tabptr;
+ *tabptr = h;
+ h->prev = tabptr;
+ if (h->next)
+ h->next->prev = &h->next;
+ }
+
+ return 0;
+}
+
+/**
+ * Delete element with key key
+ *
+ * @value dict the dict to delete from
+ * @value key the key to delete
+ */
+
+void
+heim_dict_delete_key(heim_dict_t dict, heim_object_t key)
+{
+ struct hashentry *h = _search(dict, key);
+
+ if (h == NULL)
+ return;
+
+ heim_release(h->key);
+ heim_release(h->value);
+
+ if ((*(h->prev) = h->next) != NULL)
+ h->next->prev = h->prev;
+
+ free(h);
+}
+
+/**
+ * Do something for each element
+ *
+ * @value dict the dict to interate over
+ * @value func the function to search for
+ * @value arg argument to func
+ */
+
+void
+heim_dict_iterate_f(heim_dict_t dict, void *arg, heim_dict_iterator_f_t func)
+{
+ struct hashentry **h, *g;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
+ for (g = *h; g; g = g->next)
+ func(g->key, g->value, arg);
+}
+
+#ifdef __BLOCKS__
+/**
+ * Do something for each element
+ *
+ * @value dict the dict to interate over
+ * @value func the function to search for
+ */
+
+void
+heim_dict_iterate(heim_dict_t dict, void (^func)(heim_object_t, heim_object_t))
+{
+ struct hashentry **h, *g;
+
+ for (h = dict->tab; h < &dict->tab[dict->size]; ++h)
+ for (g = *h; g; g = g->next)
+ func(g->key, g->value);
+}
+#endif
diff --git a/lib/base/dll.c b/lib/base/dll.c
new file mode 100644
index 000000000000..31017a01191b
--- /dev/null
+++ b/lib/base/dll.c
@@ -0,0 +1,324 @@
+/***********************************************************************
+ * Copyright (c) 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+/*
+ * This is an implementation of thread-specific storage with
+ * destructors. WIN32 doesn't quite have this. Instead it has
+ * DllMain(), an entry point in every DLL that gets called to notify the
+ * DLL of thread/process "attach"/"detach" events.
+ *
+ * We use __thread (or __declspec(thread)) for the thread-local itself
+ * and DllMain() DLL_THREAD_DETACH events to drive destruction of
+ * thread-local values.
+ *
+ * When building in maintainer mode on non-Windows pthread systems this
+ * uses a single pthread key instead to implement multiple keys. This
+ * keeps the code from rotting when modified by non-Windows developers.
+ */
+
+#include "baselocl.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#ifdef HEIM_WIN32_TLS
+#include <assert.h>
+#include <err.h>
+#include <heim_threads.h>
+
+#ifndef WIN32
+#include <pthread.h>
+#endif
+
+/* Logical array of keys that grows lock-lessly */
+typedef struct tls_keys tls_keys;
+struct tls_keys {
+ void (**keys_dtors)(void *); /* array of destructors */
+ size_t keys_start_idx; /* index of first destructor */
+ size_t keys_num;
+ tls_keys *keys_next;
+};
+
+/*
+ * Well, not quite locklessly. We need synchronization primitives to do
+ * this locklessly. An atomic CAS will do.
+ */
+static HEIMDAL_MUTEX tls_key_defs_lock = HEIMDAL_MUTEX_INITIALIZER;
+static tls_keys *tls_key_defs;
+
+/* Logical array of values (per-thread; no locking needed here) */
+struct tls_values {
+ void **values; /* realloc()ed */
+ size_t values_num;
+};
+
+static HEIMDAL_THREAD_LOCAL struct tls_values values;
+
+#define DEAD_KEY ((void *)8)
+
+void
+heim_w32_service_thread_detach(void *unused)
+{
+ tls_keys *key_defs;
+ void (*dtor)(void*);
+ size_t i;
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = tls_key_defs;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ if (key_defs == NULL)
+ return;
+
+ for (i = 0; i < values.values_num; i++) {
+ assert(i >= key_defs->keys_start_idx);
+ if (i >= key_defs->keys_start_idx + key_defs->keys_num) {
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = key_defs->keys_next;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ assert(key_defs != NULL);
+ assert(i >= key_defs->keys_start_idx);
+ assert(i < key_defs->keys_start_idx + key_defs->keys_num);
+ }
+ dtor = key_defs->keys_dtors[i - key_defs->keys_start_idx];
+ if (values.values[i] != NULL && dtor != NULL && dtor != DEAD_KEY)
+ dtor(values.values[i]);
+ values.values[i] = NULL;
+ }
+}
+
+#if !defined(WIN32)
+static pthread_key_t pt_key;
+pthread_once_t pt_once = PTHREAD_ONCE_INIT;
+
+static void
+atexit_del_tls_for_thread(void)
+{
+ heim_w32_service_thread_detach(NULL);
+}
+
+static void
+create_pt_key(void)
+{
+ int ret;
+
+ /* The main thread may not execute TLS destructors */
+ atexit(atexit_del_tls_for_thread);
+ ret = pthread_key_create(&pt_key, heim_w32_service_thread_detach);
+ if (ret != 0)
+ err(1, "pthread_key_create() failed");
+}
+
+#endif
+
+int
+heim_w32_key_create(HEIM_PRIV_thread_key *key, void (*dtor)(void *))
+{
+ tls_keys *key_defs, *new_key_defs;
+ size_t i, k;
+ int ret = ENOMEM;
+
+#if !defined(WIN32)
+ (void) pthread_once(&pt_once, create_pt_key);
+ (void) pthread_setspecific(pt_key, DEAD_KEY);
+#endif
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ if (tls_key_defs == NULL) {
+ /* First key */
+ new_key_defs = calloc(1, sizeof(*new_key_defs));
+ if (new_key_defs == NULL) {
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return ENOMEM;
+ }
+ new_key_defs->keys_num = 8;
+ new_key_defs->keys_dtors = calloc(new_key_defs->keys_num,
+ sizeof(*new_key_defs->keys_dtors));
+ if (new_key_defs->keys_dtors == NULL) {
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ free(new_key_defs);
+ return ENOMEM;
+ }
+ tls_key_defs = new_key_defs;
+ new_key_defs->keys_dtors[0] = dtor;
+ for (i = 1; i < new_key_defs->keys_num; i++)
+ new_key_defs->keys_dtors[i] = NULL;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return 0;
+ }
+
+ for (key_defs = tls_key_defs;
+ key_defs != NULL;
+ key_defs = key_defs->keys_next) {
+ k = key_defs->keys_start_idx;
+ for (i = 0; i < key_defs->keys_num; i++, k++) {
+ if (key_defs->keys_dtors[i] == NULL) {
+ /* Found free slot; use it */
+ key_defs->keys_dtors[i] = dtor;
+ *key = k;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return 0;
+ }
+ }
+ if (key_defs->keys_next != NULL)
+ continue;
+
+ /* Grow the registration array */
+ /* XXX DRY */
+ new_key_defs = calloc(1, sizeof(*new_key_defs));
+ if (new_key_defs == NULL)
+ break;
+
+ new_key_defs->keys_dtors =
+ calloc(key_defs->keys_num + key_defs->keys_num / 2,
+ sizeof(*new_key_defs->keys_dtors));
+ if (new_key_defs->keys_dtors == NULL) {
+ free(new_key_defs);
+ break;
+ }
+ new_key_defs->keys_start_idx = key_defs->keys_start_idx +
+ key_defs->keys_num;
+ new_key_defs->keys_num = key_defs->keys_num + key_defs->keys_num / 2;
+ new_key_defs->keys_dtors[i] = dtor;
+ for (i = 1; i < new_key_defs->keys_num; i++)
+ new_key_defs->keys_dtors[i] = NULL;
+ key_defs->keys_next = new_key_defs;
+ ret = 0;
+ break;
+ }
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ return ret;
+}
+
+static void
+key_lookup(HEIM_PRIV_thread_key key, tls_keys **kd,
+ size_t *dtor_idx, void (**dtor)(void *))
+{
+ tls_keys *key_defs;
+
+ if (kd != NULL)
+ *kd = NULL;
+ if (dtor_idx != NULL)
+ *dtor_idx = 0;
+ if (dtor != NULL)
+ *dtor = NULL;
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = tls_key_defs;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+
+ while (key_defs != NULL) {
+ if (key >= key_defs->keys_start_idx &&
+ key < key_defs->keys_start_idx + key_defs->keys_num) {
+ if (kd != NULL)
+ *kd = key_defs;
+ if (dtor_idx != NULL)
+ *dtor_idx = key - key_defs->keys_start_idx;
+ if (dtor != NULL)
+ *dtor = key_defs->keys_dtors[key - key_defs->keys_start_idx];
+ return;
+ }
+
+ HEIMDAL_MUTEX_lock(&tls_key_defs_lock);
+ key_defs = key_defs->keys_next;
+ HEIMDAL_MUTEX_unlock(&tls_key_defs_lock);
+ assert(key_defs != NULL);
+ assert(key >= key_defs->keys_start_idx);
+ }
+}
+
+int
+heim_w32_delete_key(HEIM_PRIV_thread_key key)
+{
+ tls_keys *key_defs;
+ size_t dtor_idx;
+
+ key_lookup(key, &key_defs, &dtor_idx, NULL);
+ if (key_defs == NULL)
+ return EINVAL;
+ key_defs->keys_dtors[dtor_idx] = DEAD_KEY;
+ return 0;
+}
+
+int
+heim_w32_setspecific(HEIM_PRIV_thread_key key, void *value)
+{
+ void **new_values;
+ size_t new_num;
+ void (*dtor)(void *);
+ size_t i;
+
+#if !defined(WIN32)
+ (void) pthread_setspecific(pt_key, DEAD_KEY);
+#endif
+
+ key_lookup(key, NULL, NULL, &dtor);
+ if (dtor == NULL)
+ return EINVAL;
+
+ if (key >= values.values_num) {
+ if (values.values_num == 0) {
+ values.values = NULL;
+ new_num = 8;
+ } else {
+ new_num = (values.values_num + values.values_num / 2);
+ }
+ new_values = realloc(values.values, sizeof(void *) * new_num);
+ if (new_values == NULL)
+ return ENOMEM;
+ for (i = values.values_num; i < new_num; i++)
+ new_values[i] = NULL;
+ values.values = new_values;
+ values.values_num = new_num;
+ }
+
+ assert(key < values.values_num);
+
+ if (values.values[key] != NULL && dtor != NULL && dtor != DEAD_KEY)
+ dtor(values.values[key]);
+
+ values.values[key] = value;
+ return 0;
+}
+
+void *
+heim_w32_getspecific(HEIM_PRIV_thread_key key)
+{
+ if (key >= values.values_num)
+ return NULL;
+ return values.values[key];
+}
+
+#else
+static char dummy;
+#endif /* HEIM_WIN32_TLS */
diff --git a/lib/base/error.c b/lib/base/error.c
new file mode 100644
index 000000000000..a1bbfa8a133f
--- /dev/null
+++ b/lib/base/error.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct heim_error {
+ int error_code;
+ heim_string_t msg;
+ struct heim_error *next;
+};
+
+static void
+error_dealloc(void *ptr)
+{
+ struct heim_error *p = ptr;
+ heim_release(p->msg);
+ heim_release(p->next);
+}
+
+static int
+error_cmp(void *a, void *b)
+{
+ struct heim_error *ap = a, *bp = b;
+ if (ap->error_code == ap->error_code)
+ return ap->error_code - ap->error_code;
+ return heim_cmp(ap->msg, bp->msg);
+}
+
+static unsigned long
+error_hash(void *ptr)
+{
+ struct heim_error *p = ptr;
+ return p->error_code;
+}
+
+struct heim_type_data _heim_error_object = {
+ HEIM_TID_ERROR,
+ "error-object",
+ NULL,
+ error_dealloc,
+ NULL,
+ error_cmp,
+ error_hash,
+ NULL
+};
+
+heim_error_t
+heim_error_create_enomem(void)
+{
+ /* This is an immediate object; see heim_number_create() */
+ return (heim_error_t)heim_number_create(ENOMEM);
+}
+
+
+void
+heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
+{
+ if (error) {
+ va_list ap;
+ va_start(ap, fmt);
+ *error = heim_error_createv(error_code, fmt, ap);
+ va_end(ap);
+ }
+}
+
+heim_error_t
+heim_error_create(int error_code, const char *fmt, ...)
+{
+ heim_error_t e;
+ va_list ap;
+
+ va_start(ap, fmt);
+ e = heim_error_createv(error_code, fmt, ap);
+ va_end(ap);
+
+ return e;
+}
+
+heim_error_t
+heim_error_createv(int error_code, const char *fmt, va_list ap)
+{
+ heim_error_t e;
+ char *str;
+ int len;
+ int save_errno = errno;
+
+ str = malloc(1024);
+ errno = save_errno;
+ if (str == NULL)
+ return heim_error_create_enomem();
+ len = vsnprintf(str, 1024, fmt, ap);
+ errno = save_errno;
+ if (len < 0) {
+ free(str);
+ return NULL; /* XXX We should have a special heim_error_t for this */
+ }
+
+ e = _heim_alloc_object(&_heim_error_object, sizeof(struct heim_error));
+ if (e) {
+ e->msg = heim_string_create(str);
+ e->error_code = error_code;
+ }
+ free(str);
+
+ errno = save_errno;
+ return e;
+}
+
+heim_string_t
+heim_error_copy_string(heim_error_t error)
+{
+ if (heim_get_tid(error) != HEIM_TID_ERROR) {
+ if (heim_get_tid(error) == heim_number_get_type_id())
+ return __heim_string_constant(strerror(heim_number_get_int((heim_number_t)error)));
+ heim_abort("invalid heim_error_t");
+ }
+ /* XXX concat all strings */
+ return heim_retain(error->msg);
+}
+
+int
+heim_error_get_code(heim_error_t error)
+{
+ if (error == NULL)
+ return -1;
+ if (heim_get_tid(error) != HEIM_TID_ERROR) {
+ if (heim_get_tid(error) == heim_number_get_type_id())
+ return heim_number_get_int((heim_number_t)error);
+ heim_abort("invalid heim_error_t");
+ }
+ return error->error_code;
+}
+
+heim_error_t
+heim_error_append(heim_error_t top, heim_error_t append)
+{
+ if (heim_get_tid(top) != HEIM_TID_ERROR) {
+ if (heim_get_tid(top) == heim_number_get_type_id())
+ return top;
+ heim_abort("invalid heim_error_t");
+ }
+ if (top->next)
+ heim_release(top->next);
+ top->next = heim_retain(append);
+ return top;
+}
diff --git a/lib/base/heimbase.c b/lib/base/heimbase.c
new file mode 100644
index 000000000000..6ad6ab258f09
--- /dev/null
+++ b/lib/base/heimbase.c
@@ -0,0 +1,1079 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <syslog.h>
+
+static heim_base_atomic_type tidglobal = HEIM_TID_USER;
+
+struct heim_base {
+ heim_type_t isa;
+ heim_base_atomic_type ref_cnt;
+ HEIM_TAILQ_ENTRY(heim_base) autorel;
+ heim_auto_release_t autorelpool;
+ uintptr_t isaextra[3];
+};
+
+/* specialized version of base */
+struct heim_base_mem {
+ heim_type_t isa;
+ heim_base_atomic_type ref_cnt;
+ HEIM_TAILQ_ENTRY(heim_base) autorel;
+ heim_auto_release_t autorelpool;
+ const char *name;
+ void (*dealloc)(void *);
+ uintptr_t isaextra[1];
+};
+
+#define PTR2BASE(ptr) (((struct heim_base *)ptr) - 1)
+#define BASE2PTR(ptr) ((void *)(((struct heim_base *)ptr) + 1))
+
+#ifdef HEIM_BASE_NEED_ATOMIC_MUTEX
+HEIMDAL_MUTEX _heim_base_mutex = HEIMDAL_MUTEX_INITIALIZER;
+#endif
+
+/*
+ * Auto release structure
+ */
+
+struct heim_auto_release {
+ HEIM_TAILQ_HEAD(, heim_base) pool;
+ HEIMDAL_MUTEX pool_mutex;
+ struct heim_auto_release *parent;
+};
+
+
+/**
+ * Retain object (i.e., take a reference)
+ *
+ * @param object to be released, NULL is ok
+ *
+ * @return the same object as passed in
+ */
+
+void *
+heim_retain(void *ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return ptr;
+
+ if (p->ref_cnt == heim_base_atomic_max)
+ return ptr;
+
+ if ((heim_base_atomic_inc(&p->ref_cnt) - 1) == 0)
+ heim_abort("resurection");
+ return ptr;
+}
+
+/**
+ * Release object, free if reference count reaches zero
+ *
+ * @param object to be released
+ */
+
+void
+heim_release(void *ptr)
+{
+ heim_base_atomic_type old;
+ struct heim_base *p = PTR2BASE(ptr);
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return;
+
+ if (p->ref_cnt == heim_base_atomic_max)
+ return;
+
+ old = heim_base_atomic_dec(&p->ref_cnt) + 1;
+
+ if (old > 1)
+ return;
+
+ if (old == 1) {
+ heim_auto_release_t ar = p->autorelpool;
+ /* remove from autorel pool list */
+ if (ar) {
+ p->autorelpool = NULL;
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+ }
+ if (p->isa->dealloc)
+ p->isa->dealloc(ptr);
+ free(p);
+ } else
+ heim_abort("over release");
+}
+
+/**
+ * If used require wrapped in autorelease pool
+ */
+
+heim_string_t
+heim_description(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ if (p->isa->desc == NULL)
+ return heim_auto_release(heim_string_ref_create(p->isa->name, NULL));
+ return heim_auto_release(p->isa->desc(ptr));
+}
+
+
+void
+_heim_make_permanent(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ p->ref_cnt = heim_base_atomic_max;
+}
+
+
+static heim_type_t tagged_isa[9] = {
+ &_heim_number_object,
+ &_heim_null_object,
+ &_heim_bool_object,
+
+ NULL,
+ NULL,
+ NULL,
+
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_type_t
+_heim_get_isa(heim_object_t ptr)
+{
+ struct heim_base *p;
+ if (heim_base_is_tagged(ptr)) {
+ if (heim_base_is_tagged_object(ptr))
+ return tagged_isa[heim_base_tagged_object_tid(ptr)];
+ heim_abort("not a supported tagged type");
+ }
+ p = PTR2BASE(ptr);
+ return p->isa;
+}
+
+/**
+ * Get type ID of object
+ *
+ * @param object object to get type id of
+ *
+ * @return type id of object
+ */
+
+heim_tid_t
+heim_get_tid(heim_object_t ptr)
+{
+ heim_type_t isa = _heim_get_isa(ptr);
+ return isa->tid;
+}
+
+/**
+ * Get hash value of object
+ *
+ * @param object object to get hash value for
+ *
+ * @return a hash value
+ */
+
+unsigned long
+heim_get_hash(heim_object_t ptr)
+{
+ heim_type_t isa = _heim_get_isa(ptr);
+ if (isa->hash)
+ return isa->hash(ptr);
+ return (unsigned long)ptr;
+}
+
+/**
+ * Compare two objects, returns 0 if equal, can use used for qsort()
+ * and friends.
+ *
+ * @param a first object to compare
+ * @param b first object to compare
+ *
+ * @return 0 if objects are equal
+ */
+
+int
+heim_cmp(heim_object_t a, heim_object_t b)
+{
+ heim_tid_t ta, tb;
+ heim_type_t isa;
+
+ ta = heim_get_tid(a);
+ tb = heim_get_tid(b);
+
+ if (ta != tb)
+ return ta - tb;
+
+ isa = _heim_get_isa(a);
+
+ if (isa->cmp)
+ return isa->cmp(a, b);
+
+ return (uintptr_t)a - (uintptr_t)b;
+}
+
+/*
+ * Private - allocates an memory object
+ */
+
+static void
+memory_dealloc(void *ptr)
+{
+ struct heim_base_mem *p = (struct heim_base_mem *)PTR2BASE(ptr);
+ if (p->dealloc)
+ p->dealloc(ptr);
+}
+
+struct heim_type_data memory_object = {
+ HEIM_TID_MEMORY,
+ "memory-object",
+ NULL,
+ memory_dealloc,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/**
+ * Allocate memory for an object of anonymous type
+ *
+ * @param size size of object to be allocated
+ * @param name name of ad-hoc type
+ * @param dealloc destructor function
+ *
+ * Objects allocated with this interface do not serialize.
+ *
+ * @return allocated object
+ */
+
+void *
+heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc)
+{
+ /* XXX use posix_memalign */
+
+ struct heim_base_mem *p = calloc(1, size + sizeof(*p));
+ if (p == NULL)
+ return NULL;
+ p->isa = &memory_object;
+ p->ref_cnt = 1;
+ p->name = name;
+ p->dealloc = dealloc;
+ return BASE2PTR(p);
+}
+
+heim_type_t
+_heim_create_type(const char *name,
+ heim_type_init init,
+ heim_type_dealloc dealloc,
+ heim_type_copy copy,
+ heim_type_cmp cmp,
+ heim_type_hash hash,
+ heim_type_description desc)
+{
+ heim_type_t type;
+
+ type = calloc(1, sizeof(*type));
+ if (type == NULL)
+ return NULL;
+
+ type->tid = heim_base_atomic_inc(&tidglobal);
+ type->name = name;
+ type->init = init;
+ type->dealloc = dealloc;
+ type->copy = copy;
+ type->cmp = cmp;
+ type->hash = hash;
+ type->desc = desc;
+
+ return type;
+}
+
+heim_object_t
+_heim_alloc_object(heim_type_t type, size_t size)
+{
+ /* XXX should use posix_memalign */
+ struct heim_base *p = calloc(1, size + sizeof(*p));
+ if (p == NULL)
+ return NULL;
+ p->isa = type;
+ p->ref_cnt = 1;
+
+ return BASE2PTR(p);
+}
+
+void *
+_heim_get_isaextra(heim_object_t ptr, size_t idx)
+{
+ struct heim_base *p = (struct heim_base *)PTR2BASE(ptr);
+
+ heim_assert(ptr != NULL, "internal error");
+ if (p->isa == &memory_object)
+ return NULL;
+ heim_assert(idx < 3, "invalid private heim_base extra data index");
+ return &p->isaextra[idx];
+}
+
+heim_tid_t
+_heim_type_get_tid(heim_type_t type)
+{
+ return type->tid;
+}
+
+#if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
+static pthread_once_t once_arg_key_once = PTHREAD_ONCE_INIT;
+static pthread_key_t once_arg_key;
+
+static void
+once_arg_key_once_init(void)
+{
+ errno = pthread_key_create(&once_arg_key, NULL);
+ if (errno != 0) {
+ fprintf(stderr,
+ "Error: pthread_key_create() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+}
+
+struct once_callback {
+ void (*fn)(void *);
+ void *data;
+};
+
+static void
+once_callback_caller(void)
+{
+ struct once_callback *once_callback = pthread_getspecific(once_arg_key);
+
+ if (once_callback == NULL) {
+ fprintf(stderr, "Error: pthread_once() calls callback on "
+ "different thread?! Cannot continue.\n");
+ abort();
+ }
+ once_callback->fn(once_callback->data);
+}
+#endif
+
+/**
+ * Call func once and only once
+ *
+ * @param once pointer to a heim_base_once_t
+ * @param ctx context passed to func
+ * @param func function to be called
+ */
+
+void
+heim_base_once_f(heim_base_once_t *once, void *ctx, void (*func)(void *))
+{
+#if defined(WIN32)
+ /*
+ * With a libroken wrapper for some CAS function and a libroken yield()
+ * wrapper we could make this the default implementation when we have
+ * neither Grand Central nor POSX threads.
+ *
+ * We could also adapt the double-checked lock pattern with CAS
+ * providing the necessary memory barriers in the absence of
+ * portable explicit memory barrier APIs.
+ */
+ /*
+ * We use CAS operations in large part to provide implied memory
+ * barriers.
+ *
+ * State 0 means that func() has never executed.
+ * State 1 means that func() is executing.
+ * State 2 means that func() has completed execution.
+ */
+ if (InterlockedCompareExchange(once, 1L, 0L) == 0L) {
+ /* State is now 1 */
+ (*func)(ctx);
+ (void)InterlockedExchange(once, 2L);
+ /* State is now 2 */
+ } else {
+ /*
+ * The InterlockedCompareExchange is being used to fetch
+ * the current state under a full memory barrier. As long
+ * as the current state is 1 continue to spin.
+ */
+ while (InterlockedCompareExchange(once, 2L, 0L) == 1L)
+ SwitchToThread();
+ }
+#elif defined(HAVE_DISPATCH_DISPATCH_H)
+ dispatch_once_f(once, ctx, func);
+#elif defined(ENABLE_PTHREAD_SUPPORT)
+ struct once_callback once_callback;
+
+ once_callback.fn = func;
+ once_callback.data = ctx;
+
+ errno = pthread_once(&once_arg_key_once, once_arg_key_once_init);
+ if (errno != 0) {
+ fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+ errno = pthread_setspecific(once_arg_key, &once_callback);
+ if (errno != 0) {
+ fprintf(stderr,
+ "Error: pthread_setspecific() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+ errno = pthread_once(once, once_callback_caller);
+ if (errno != 0) {
+ fprintf(stderr, "Error: pthread_once() failed, cannot continue: %s\n",
+ strerror(errno));
+ abort();
+ }
+#else
+ static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
+ HEIMDAL_MUTEX_lock(&mutex);
+ if (*once == 0) {
+ *once = 1;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ func(ctx);
+ HEIMDAL_MUTEX_lock(&mutex);
+ *once = 2;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ } else if (*once == 2) {
+ HEIMDAL_MUTEX_unlock(&mutex);
+ } else {
+ HEIMDAL_MUTEX_unlock(&mutex);
+ while (1) {
+ struct timeval tv = { 0, 1000 };
+ select(0, NULL, NULL, NULL, &tv);
+ HEIMDAL_MUTEX_lock(&mutex);
+ if (*once == 2)
+ break;
+ HEIMDAL_MUTEX_unlock(&mutex);
+ }
+ HEIMDAL_MUTEX_unlock(&mutex);
+ }
+#endif
+}
+
+/**
+ * Abort and log the failure (using syslog)
+ */
+
+void
+heim_abort(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ heim_abortv(fmt, ap);
+ va_end(ap);
+}
+
+/**
+ * Abort and log the failure (using syslog)
+ */
+
+void
+heim_abortv(const char *fmt, va_list ap)
+{
+ static char str[1024];
+
+ vsnprintf(str, sizeof(str), fmt, ap);
+ syslog(LOG_ERR, "heim_abort: %s", str);
+ abort();
+}
+
+/*
+ *
+ */
+
+static int ar_created = 0;
+static HEIMDAL_thread_key ar_key;
+
+struct ar_tls {
+ struct heim_auto_release *head;
+ struct heim_auto_release *current;
+ HEIMDAL_MUTEX tls_mutex;
+};
+
+static void
+ar_tls_delete(void *ptr)
+{
+ struct ar_tls *tls = ptr;
+ heim_auto_release_t next = NULL;
+
+ if (tls == NULL)
+ return;
+ for (; tls->current != NULL; tls->current = next) {
+ next = tls->current->parent;
+ heim_release(tls->current);
+ }
+ free(tls);
+}
+
+static void
+init_ar_tls(void *ptr)
+{
+ int ret;
+ HEIMDAL_key_create(&ar_key, ar_tls_delete, ret);
+ if (ret == 0)
+ ar_created = 1;
+}
+
+static struct ar_tls *
+autorel_tls(void)
+{
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT;
+ struct ar_tls *arp;
+ int ret;
+
+ heim_base_once_f(&once, NULL, init_ar_tls);
+ if (!ar_created)
+ return NULL;
+
+ arp = HEIMDAL_getspecific(ar_key);
+ if (arp == NULL) {
+
+ arp = calloc(1, sizeof(*arp));
+ if (arp == NULL)
+ return NULL;
+ HEIMDAL_setspecific(ar_key, arp, ret);
+ if (ret) {
+ free(arp);
+ return NULL;
+ }
+ }
+ return arp;
+
+}
+
+static void
+autorel_dealloc(void *ptr)
+{
+ heim_auto_release_t ar = ptr;
+ struct ar_tls *tls;
+
+ tls = autorel_tls();
+ if (tls == NULL)
+ heim_abort("autorelease pool released on thread w/o autorelease inited");
+
+ heim_auto_release_drain(ar);
+
+ if (!HEIM_TAILQ_EMPTY(&ar->pool))
+ heim_abort("pool not empty after draining");
+
+ HEIMDAL_MUTEX_lock(&tls->tls_mutex);
+ if (tls->current != ptr)
+ heim_abort("autorelease not releaseing top pool");
+
+ tls->current = ar->parent;
+ HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
+}
+
+static int
+autorel_cmp(void *a, void *b)
+{
+ return (a == b);
+}
+
+static unsigned long
+autorel_hash(void *ptr)
+{
+ return (unsigned long)ptr;
+}
+
+
+static struct heim_type_data _heim_autorel_object = {
+ HEIM_TID_AUTORELEASE,
+ "autorelease-pool",
+ NULL,
+ autorel_dealloc,
+ NULL,
+ autorel_cmp,
+ autorel_hash,
+ NULL
+};
+
+/**
+ * Create thread-specific object auto-release pool
+ *
+ * Objects placed on the per-thread auto-release pool (with
+ * heim_auto_release()) can be released in one fell swoop by calling
+ * heim_auto_release_drain().
+ */
+
+heim_auto_release_t
+heim_auto_release_create(void)
+{
+ struct ar_tls *tls = autorel_tls();
+ heim_auto_release_t ar;
+
+ if (tls == NULL)
+ heim_abort("Failed to create/get autorelease head");
+
+ ar = _heim_alloc_object(&_heim_autorel_object, sizeof(struct heim_auto_release));
+ if (ar) {
+ HEIMDAL_MUTEX_lock(&tls->tls_mutex);
+ if (tls->head == NULL)
+ tls->head = ar;
+ ar->parent = tls->current;
+ tls->current = ar;
+ HEIMDAL_MUTEX_unlock(&tls->tls_mutex);
+ }
+
+ return ar;
+}
+
+/**
+ * Place the current object on the thread's auto-release pool
+ *
+ * @param ptr object
+ */
+
+heim_object_t
+heim_auto_release(heim_object_t ptr)
+{
+ struct heim_base *p = PTR2BASE(ptr);
+ struct ar_tls *tls = autorel_tls();
+ heim_auto_release_t ar;
+
+ if (ptr == NULL || heim_base_is_tagged(ptr))
+ return ptr;
+
+ /* drop from old pool */
+ if ((ar = p->autorelpool) != NULL) {
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_REMOVE(&ar->pool, p, autorel);
+ p->autorelpool = NULL;
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+ }
+
+ if (tls == NULL || (ar = tls->current) == NULL)
+ heim_abort("no auto relase pool in place, would leak");
+
+ HEIMDAL_MUTEX_lock(&ar->pool_mutex);
+ HEIM_TAILQ_INSERT_HEAD(&ar->pool, p, autorel);
+ p->autorelpool = ar;
+ HEIMDAL_MUTEX_unlock(&ar->pool_mutex);
+
+ return ptr;
+}
+
+/**
+ * Release all objects on the given auto-release pool
+ */
+
+void
+heim_auto_release_drain(heim_auto_release_t autorel)
+{
+ heim_object_t obj;
+
+ /* release all elements on the tail queue */
+
+ HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
+ while(!HEIM_TAILQ_EMPTY(&autorel->pool)) {
+ obj = HEIM_TAILQ_FIRST(&autorel->pool);
+ HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
+ heim_release(BASE2PTR(obj));
+ HEIMDAL_MUTEX_lock(&autorel->pool_mutex);
+ }
+ HEIMDAL_MUTEX_unlock(&autorel->pool_mutex);
+}
+
+/*
+ * Helper for heim_path_vget() and heim_path_delete(). On success
+ * outputs the node named by the path and the parent node and key
+ * (useful for heim_path_delete()).
+ */
+
+static heim_object_t
+heim_path_vget2(heim_object_t ptr, heim_object_t *parent, heim_object_t *key,
+ heim_error_t *error, va_list ap)
+{
+ heim_object_t path_element;
+ heim_object_t node, next_node;
+ heim_tid_t node_type;
+
+ *parent = NULL;
+ *key = NULL;
+ if (ptr == NULL)
+ return NULL;
+
+ for (node = ptr; node != NULL; ) {
+ path_element = va_arg(ap, heim_object_t);
+ if (path_element == NULL) {
+ *parent = node;
+ *key = path_element;
+ return node;
+ }
+
+ node_type = heim_get_tid(node);
+ switch (node_type) {
+ case HEIM_TID_ARRAY:
+ case HEIM_TID_DICT:
+ case HEIM_TID_DB:
+ break;
+ default:
+ if (node == ptr)
+ heim_abort("heim_path_get() only operates on container types");
+ return NULL;
+ }
+
+ if (node_type == HEIM_TID_DICT) {
+ next_node = heim_dict_get_value(node, path_element);
+ } else if (node_type == HEIM_TID_DB) {
+ next_node = _heim_db_get_value(node, NULL, path_element, NULL);
+ } else if (node_type == HEIM_TID_ARRAY) {
+ int idx = -1;
+
+ if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
+ idx = heim_number_get_int(path_element);
+ if (idx < 0) {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path_get() path elements "
+ "for array nodes must be "
+ "numeric and positive");
+ return NULL;
+ }
+ next_node = heim_array_get_value(node, idx);
+ } else {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path_get() node in path "
+ "not a container type");
+ return NULL;
+ }
+ node = next_node;
+ }
+ return NULL;
+}
+
+/**
+ * Get a node in a heim_object tree by path
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ap NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return object (not retained) if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_vget(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t p, k;
+
+ return heim_path_vget2(ptr, &p, &k, error, ap);
+}
+
+/**
+ * Get a node in a tree by path, with retained reference
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ap NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return retained object if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_vcopy(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t p, k;
+
+ return heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
+}
+
+/**
+ * Get a node in a tree by path
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ... NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return object (not retained) if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_get(heim_object_t ptr, heim_error_t *error, ...)
+{
+ heim_object_t o;
+ heim_object_t p, k;
+ va_list ap;
+
+ if (ptr == NULL)
+ return NULL;
+
+ va_start(ap, error);
+ o = heim_path_vget2(ptr, &p, &k, error, ap);
+ va_end(ap);
+ return o;
+}
+
+/**
+ * Get a node in a tree by path, with retained reference
+ *
+ * @param ptr tree
+ * @param error error (output)
+ * @param ... NULL-terminated va_list of heim_object_ts that form a path
+ *
+ * @return retained object if found
+ *
+ * @addtogroup heimbase
+ */
+
+heim_object_t
+heim_path_copy(heim_object_t ptr, heim_error_t *error, ...)
+{
+ heim_object_t o;
+ heim_object_t p, k;
+ va_list ap;
+
+ if (ptr == NULL)
+ return NULL;
+
+ va_start(ap, error);
+ o = heim_retain(heim_path_vget2(ptr, &p, &k, error, ap));
+ va_end(ap);
+ return o;
+}
+
+/**
+ * Create a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param size the size of the heim_dict_t nodes to be created
+ * @param leaf leaf node to be added, if any
+ * @param error error (output)
+ * @param ap NULL-terminated of path component objects
+ *
+ * Create a path of heim_dict_t interior nodes in a given heim_object_t
+ * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
+ * then the leaf is not deleted).
+ *
+ * @return 0 on success, else a system error
+ *
+ * @addtogroup heimbase
+ */
+
+int
+heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, va_list ap)
+{
+ heim_object_t path_element = va_arg(ap, heim_object_t);
+ heim_object_t next_path_element = NULL;
+ heim_object_t node = ptr;
+ heim_object_t next_node = NULL;
+ heim_tid_t node_type;
+ int ret = 0;
+
+ if (ptr == NULL)
+ heim_abort("heim_path_vcreate() does not create root nodes");
+
+ while (path_element != NULL) {
+ next_path_element = va_arg(ap, heim_object_t);
+ node_type = heim_get_tid(node);
+
+ if (node_type == HEIM_TID_DICT) {
+ next_node = heim_dict_get_value(node, path_element);
+ } else if (node_type == HEIM_TID_ARRAY) {
+ int idx = -1;
+
+ if (heim_get_tid(path_element) == HEIM_TID_NUMBER)
+ idx = heim_number_get_int(path_element);
+ if (idx < 0) {
+ if (error)
+ *error = heim_error_create(EINVAL,
+ "heim_path() path elements for "
+ "array nodes must be numeric "
+ "and positive");
+ return EINVAL;
+ }
+ if (idx < heim_array_get_length(node))
+ next_node = heim_array_get_value(node, idx);
+ else
+ next_node = NULL;
+ } else if (node_type == HEIM_TID_DB && next_path_element != NULL) {
+ if (error)
+ *error = heim_error_create(EINVAL, "Interior node is a DB");
+ return EINVAL;
+ }
+
+ if (next_path_element == NULL)
+ break;
+
+ /* Create missing interior node */
+ if (next_node == NULL) {
+ next_node = heim_dict_create(size); /* no arrays or DBs, just dicts */
+ if (next_node == NULL) {
+ ret = ENOMEM;
+ goto err;
+ }
+
+ if (node_type == HEIM_TID_DICT) {
+ ret = heim_dict_set_value(node, path_element, next_node);
+ } else if (node_type == HEIM_TID_ARRAY &&
+ heim_number_get_int(path_element) <= heim_array_get_length(node)) {
+ ret = heim_array_insert_value(node,
+ heim_number_get_int(path_element),
+ next_node);
+ } else {
+ ret = EINVAL;
+ if (error)
+ *error = heim_error_create(ret, "Node in path not a "
+ "container");
+ }
+ heim_release(next_node);
+ if (ret)
+ goto err;
+ }
+
+ path_element = next_path_element;
+ node = next_node;
+ next_node = NULL;
+ }
+
+ if (path_element == NULL)
+ goto err;
+
+ /* Add the leaf */
+ if (leaf != NULL) {
+ if (node_type == HEIM_TID_DICT)
+ ret = heim_dict_set_value(node, path_element, leaf);
+ else
+ ret = heim_array_insert_value(node,
+ heim_number_get_int(path_element),
+ leaf);
+ }
+ return ret;
+
+err:
+ if (error && !*error) {
+ if (ret == ENOMEM)
+ *error = heim_error_create_enomem();
+ else
+ *error = heim_error_create(ret, "Could not set "
+ "dict value");
+ }
+ return ret;
+}
+
+/**
+ * Create a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param size the size of the heim_dict_t nodes to be created
+ * @param leaf leaf node to be added, if any
+ * @param error error (output)
+ * @param ... NULL-terminated list of path component objects
+ *
+ * Create a path of heim_dict_t interior nodes in a given heim_object_t
+ * tree, as necessary, and set/replace a leaf, if given (if leaf is NULL
+ * then the leaf is not deleted).
+ *
+ * @return 0 on success, else a system error
+ *
+ * @addtogroup heimbase
+ */
+
+int
+heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start(ap, error);
+ ret = heim_path_vcreate(ptr, size, leaf, error, ap);
+ va_end(ap);
+ return ret;
+}
+
+/**
+ * Delete leaf node named by a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param error error (output)
+ * @param ap NULL-terminated list of path component objects
+ *
+ * @addtogroup heimbase
+ */
+
+void
+heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap)
+{
+ heim_object_t parent, key, child;
+
+ child = heim_path_vget2(ptr, &parent, &key, error, ap);
+ if (child != NULL) {
+ if (heim_get_tid(parent) == HEIM_TID_DICT)
+ heim_dict_delete_key(parent, key);
+ else if (heim_get_tid(parent) == HEIM_TID_DB)
+ heim_db_delete_key(parent, NULL, key, error);
+ else if (heim_get_tid(parent) == HEIM_TID_ARRAY)
+ heim_array_delete_value(parent, heim_number_get_int(key));
+ heim_release(child);
+ }
+}
+
+/**
+ * Delete leaf node named by a path in a heim_object_t tree
+ *
+ * @param ptr the tree
+ * @param error error (output)
+ * @param ap NULL-terminated list of path component objects
+ *
+ * @addtogroup heimbase
+ */
+
+void
+heim_path_delete(heim_object_t ptr, heim_error_t *error, ...)
+{
+ va_list ap;
+
+ va_start(ap, error);
+ heim_path_vdelete(ptr, error, ap);
+ va_end(ap);
+ return;
+}
+
diff --git a/lib/base/heimbase.h b/lib/base/heimbase.h
new file mode 100644
index 000000000000..157cbd4c1105
--- /dev/null
+++ b/lib/base/heimbase.h
@@ -0,0 +1,431 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HEIM_BASE_H
+#define HEIM_BASE_H 1
+
+#include <sys/types.h>
+#if !defined(WIN32) && !defined(HAVE_DISPATCH_DISPATCH_H) && defined(ENABLE_PTHREAD_SUPPORT)
+#include <pthread.h>
+#endif
+#include <krb5-types.h>
+#include <stdarg.h>
+#ifdef HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+#ifndef false
+#define false 0
+#endif
+#ifndef true
+#define true 1
+#endif
+#endif
+
+#define HEIM_BASE_API_VERSION 20130210
+
+typedef void * heim_object_t;
+typedef unsigned int heim_tid_t;
+typedef heim_object_t heim_bool_t;
+typedef heim_object_t heim_null_t;
+#ifdef WIN32
+typedef LONG heim_base_once_t;
+#define HEIM_BASE_ONCE_INIT 0
+#elif defined(HAVE_DISPATCH_DISPATCH_H)
+typedef long heim_base_once_t; /* XXX arch dependant */
+#define HEIM_BASE_ONCE_INIT 0
+#elif defined(ENABLE_PTHREAD_SUPPORT)
+typedef pthread_once_t heim_base_once_t;
+#define HEIM_BASE_ONCE_INIT PTHREAD_ONCE_INIT
+#else
+typedef long heim_base_once_t; /* XXX arch dependant */
+#define HEIM_BASE_ONCE_INIT 0
+#endif
+
+#if !defined(__has_extension)
+#define __has_extension(x) 0
+#endif
+
+#define HEIM_REQUIRE_GNUC(m,n,p) \
+ (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100) + __GNUC_PATCHLEVEL__) >= \
+ (((m) * 10000) + ((n) * 100) + (p)))
+
+
+#if __has_extension(__builtin_expect) || HEIM_REQUIRE_GNUC(3,0,0)
+#define heim_builtin_expect(_op,_res) __builtin_expect(_op,_res)
+#else
+#define heim_builtin_expect(_op,_res) (_op)
+#endif
+
+
+void * heim_retain(heim_object_t);
+void heim_release(heim_object_t);
+
+void heim_show(heim_object_t);
+
+typedef void (*heim_type_dealloc)(void *);
+
+void *
+heim_alloc(size_t size, const char *name, heim_type_dealloc dealloc);
+
+heim_tid_t
+heim_get_tid(heim_object_t object);
+
+int
+heim_cmp(heim_object_t a, heim_object_t b);
+
+unsigned long
+heim_get_hash(heim_object_t ptr);
+
+void
+heim_base_once_f(heim_base_once_t *, void *, void (*)(void *));
+
+void
+heim_abort(const char *fmt, ...)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 2));
+
+void
+heim_abortv(const char *fmt, va_list ap)
+ HEIMDAL_NORETURN_ATTRIBUTE
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 1, 0));
+
+#define heim_assert(e,t) \
+ (heim_builtin_expect(!(e), 0) ? heim_abort(t ":" #e) : (void)0)
+
+/*
+ *
+ */
+
+heim_null_t
+heim_null_create(void);
+
+heim_bool_t
+heim_bool_create(int);
+
+int
+heim_bool_val(heim_bool_t);
+
+/*
+ * Array
+ */
+
+typedef struct heim_array_data *heim_array_t;
+
+heim_array_t heim_array_create(void);
+heim_tid_t heim_array_get_type_id(void);
+
+typedef void (*heim_array_iterator_f_t)(heim_object_t, void *, int *);
+typedef int (*heim_array_filter_f_t)(heim_object_t, void *);
+
+int heim_array_append_value(heim_array_t, heim_object_t);
+int heim_array_insert_value(heim_array_t, size_t idx, heim_object_t);
+void heim_array_iterate_f(heim_array_t, void *, heim_array_iterator_f_t);
+void heim_array_iterate_reverse_f(heim_array_t, void *, heim_array_iterator_f_t);
+#ifdef __BLOCKS__
+void heim_array_iterate(heim_array_t, void (^)(heim_object_t, int *));
+void heim_array_iterate_reverse(heim_array_t, void (^)(heim_object_t, int *));
+#endif
+size_t heim_array_get_length(heim_array_t);
+heim_object_t
+ heim_array_get_value(heim_array_t, size_t);
+heim_object_t
+ heim_array_copy_value(heim_array_t, size_t);
+void heim_array_set_value(heim_array_t, size_t, heim_object_t);
+void heim_array_delete_value(heim_array_t, size_t);
+void heim_array_filter_f(heim_array_t, void *, heim_array_filter_f_t);
+#ifdef __BLOCKS__
+void heim_array_filter(heim_array_t, int (^)(heim_object_t));
+#endif
+
+/*
+ * Dict
+ */
+
+typedef struct heim_dict_data *heim_dict_t;
+
+heim_dict_t heim_dict_create(size_t size);
+heim_tid_t heim_dict_get_type_id(void);
+
+typedef void (*heim_dict_iterator_f_t)(heim_object_t, heim_object_t, void *);
+
+int heim_dict_set_value(heim_dict_t, heim_object_t, heim_object_t);
+void heim_dict_iterate_f(heim_dict_t, void *, heim_dict_iterator_f_t);
+#ifdef __BLOCKS__
+void heim_dict_iterate(heim_dict_t, void (^)(heim_object_t, heim_object_t));
+#endif
+
+heim_object_t
+ heim_dict_get_value(heim_dict_t, heim_object_t);
+heim_object_t
+ heim_dict_copy_value(heim_dict_t, heim_object_t);
+void heim_dict_delete_key(heim_dict_t, heim_object_t);
+
+/*
+ * String
+ */
+
+typedef struct heim_string_data *heim_string_t;
+typedef void (*heim_string_free_f_t)(void *);
+
+heim_string_t heim_string_create(const char *);
+heim_string_t heim_string_ref_create(const char *, heim_string_free_f_t);
+heim_string_t heim_string_create_with_bytes(const void *, size_t);
+heim_string_t heim_string_ref_create_with_bytes(const void *, size_t,
+ heim_string_free_f_t);
+heim_string_t heim_string_create_with_format(const char *, ...);
+heim_tid_t heim_string_get_type_id(void);
+const char * heim_string_get_utf8(heim_string_t);
+
+#define HSTR(_str) (__heim_string_constant("" _str ""))
+heim_string_t __heim_string_constant(const char *);
+
+/*
+ * Errors
+ */
+
+typedef struct heim_error * heim_error_t;
+
+heim_error_t heim_error_create_enomem(void);
+
+heim_error_t heim_error_create(int, const char *, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 3));
+
+void heim_error_create_opt(heim_error_t *error, int error_code, const char *fmt, ...)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 3, 4));
+
+heim_error_t heim_error_createv(int, const char *, va_list)
+ HEIMDAL_PRINTF_ATTRIBUTE((__printf__, 2, 0));
+
+heim_string_t heim_error_copy_string(heim_error_t);
+int heim_error_get_code(heim_error_t);
+
+heim_error_t heim_error_append(heim_error_t, heim_error_t);
+
+/*
+ * Path
+ */
+
+heim_object_t heim_path_get(heim_object_t ptr, heim_error_t *error, ...);
+heim_object_t heim_path_copy(heim_object_t ptr, heim_error_t *error, ...);
+heim_object_t heim_path_vget(heim_object_t ptr, heim_error_t *error,
+ va_list ap);
+heim_object_t heim_path_vcopy(heim_object_t ptr, heim_error_t *error,
+ va_list ap);
+
+int heim_path_vcreate(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, va_list ap);
+int heim_path_create(heim_object_t ptr, size_t size, heim_object_t leaf,
+ heim_error_t *error, ...);
+
+void heim_path_vdelete(heim_object_t ptr, heim_error_t *error, va_list ap);
+void heim_path_delete(heim_object_t ptr, heim_error_t *error, ...);
+
+/*
+ * Data (octet strings)
+ */
+
+#ifndef __HEIM_BASE_DATA__
+#define __HEIM_BASE_DATA__
+struct heim_base_data {
+ size_t length;
+ void *data;
+};
+typedef struct heim_base_data heim_octet_string;
+#endif
+
+typedef struct heim_base_data * heim_data_t;
+typedef void (*heim_data_free_f_t)(void *);
+
+heim_data_t heim_data_create(const void *, size_t);
+heim_data_t heim_data_ref_create(const void *, size_t, heim_data_free_f_t);
+heim_tid_t heim_data_get_type_id(void);
+const heim_octet_string *
+ heim_data_get_data(heim_data_t);
+const void * heim_data_get_ptr(heim_data_t);
+size_t heim_data_get_length(heim_data_t);
+
+/*
+ * DB
+ */
+
+typedef struct heim_db_data *heim_db_t;
+
+typedef void (*heim_db_iterator_f_t)(heim_data_t, heim_data_t, void *);
+
+typedef int (*heim_db_plug_open_f_t)(void *, const char *, const char *,
+ heim_dict_t, void **, heim_error_t *);
+typedef int (*heim_db_plug_clone_f_t)(void *, void **, heim_error_t *);
+typedef int (*heim_db_plug_close_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_lock_f_t)(void *, int, heim_error_t *);
+typedef int (*heim_db_plug_unlock_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_sync_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_begin_f_t)(void *, int, heim_error_t *);
+typedef int (*heim_db_plug_commit_f_t)(void *, heim_error_t *);
+typedef int (*heim_db_plug_rollback_f_t)(void *, heim_error_t *);
+typedef heim_data_t (*heim_db_plug_copy_value_f_t)(void *, heim_string_t,
+ heim_data_t,
+ heim_error_t *);
+typedef int (*heim_db_plug_set_value_f_t)(void *, heim_string_t, heim_data_t,
+ heim_data_t, heim_error_t *);
+typedef int (*heim_db_plug_del_key_f_t)(void *, heim_string_t, heim_data_t,
+ heim_error_t *);
+typedef void (*heim_db_plug_iter_f_t)(void *, heim_string_t, void *,
+ heim_db_iterator_f_t, heim_error_t *);
+
+struct heim_db_type {
+ int version;
+ heim_db_plug_open_f_t openf;
+ heim_db_plug_clone_f_t clonef;
+ heim_db_plug_close_f_t closef;
+ heim_db_plug_lock_f_t lockf;
+ heim_db_plug_unlock_f_t unlockf;
+ heim_db_plug_sync_f_t syncf;
+ heim_db_plug_begin_f_t beginf;
+ heim_db_plug_commit_f_t commitf;
+ heim_db_plug_rollback_f_t rollbackf;
+ heim_db_plug_copy_value_f_t copyf;
+ heim_db_plug_set_value_f_t setf;
+ heim_db_plug_del_key_f_t delf;
+ heim_db_plug_iter_f_t iterf;
+};
+
+extern struct heim_db_type heim_sorted_text_file_dbtype;
+
+#define HEIM_DB_TYPE_VERSION_01 1
+
+int heim_db_register(const char *dbtype,
+ void *data,
+ struct heim_db_type *plugin);
+
+heim_db_t heim_db_create(const char *dbtype, const char *dbname,
+ heim_dict_t options, heim_error_t *error);
+heim_db_t heim_db_clone(heim_db_t, heim_error_t *);
+int heim_db_begin(heim_db_t, int, heim_error_t *);
+int heim_db_commit(heim_db_t, heim_error_t *);
+int heim_db_rollback(heim_db_t, heim_error_t *);
+heim_tid_t heim_db_get_type_id(void);
+
+int heim_db_set_value(heim_db_t, heim_string_t, heim_data_t, heim_data_t,
+ heim_error_t *);
+heim_data_t heim_db_copy_value(heim_db_t, heim_string_t, heim_data_t,
+ heim_error_t *);
+int heim_db_delete_key(heim_db_t, heim_string_t, heim_data_t,
+ heim_error_t *);
+void heim_db_iterate_f(heim_db_t, heim_string_t, void *,
+ heim_db_iterator_f_t, heim_error_t *);
+#ifdef __BLOCKS__
+void heim_db_iterate(heim_db_t, heim_string_t,
+ void (^)(heim_data_t, heim_data_t), heim_error_t *);
+#endif
+
+
+/*
+ * Number
+ */
+
+typedef struct heim_number_data *heim_number_t;
+
+heim_number_t heim_number_create(int);
+heim_tid_t heim_number_get_type_id(void);
+int heim_number_get_int(heim_number_t);
+
+/*
+ *
+ */
+
+typedef struct heim_auto_release * heim_auto_release_t;
+
+heim_auto_release_t heim_auto_release_create(void);
+void heim_auto_release_drain(heim_auto_release_t);
+heim_object_t heim_auto_release(heim_object_t);
+
+/*
+ * JSON
+ */
+typedef enum heim_json_flags {
+ HEIM_JSON_F_NO_C_NULL = 1,
+ HEIM_JSON_F_STRICT_STRINGS = 2,
+ HEIM_JSON_F_NO_DATA = 4,
+ HEIM_JSON_F_NO_DATA_DICT = 8,
+ HEIM_JSON_F_STRICT_DICT = 16,
+ HEIM_JSON_F_STRICT = 31,
+ HEIM_JSON_F_CNULL2JSNULL = 32,
+ HEIM_JSON_F_TRY_DECODE_DATA = 64,
+ HEIM_JSON_F_ONE_LINE = 128
+} heim_json_flags_t;
+
+heim_object_t heim_json_create(const char *, size_t, heim_json_flags_t,
+ heim_error_t *);
+heim_object_t heim_json_create_with_bytes(const void *, size_t, size_t,
+ heim_json_flags_t,
+ heim_error_t *);
+heim_string_t heim_json_copy_serialize(heim_object_t, heim_json_flags_t,
+ heim_error_t *);
+
+
+/*
+ * Debug
+ */
+
+heim_string_t
+heim_description(heim_object_t ptr);
+
+/*
+ * Binary search.
+ *
+ * Note: these are private until integrated into the heimbase object system.
+ */
+typedef struct bsearch_file_handle *bsearch_file_handle;
+int _bsearch_text(const char *buf, size_t buf_sz, const char *key,
+ char **value, size_t *location, size_t *loops);
+int _bsearch_file_open(const char *fname, size_t max_sz, size_t page_sz,
+ bsearch_file_handle *bfh, size_t *reads);
+int _bsearch_file(bsearch_file_handle bfh, const char *key, char **value,
+ size_t *location, size_t *loops, size_t *reads);
+void _bsearch_file_info(bsearch_file_handle bfh, size_t *page_sz,
+ size_t *max_sz, int *blockwise);
+void _bsearch_file_close(bsearch_file_handle *bfh);
+
+/*
+ * Thread-specific keys
+ */
+
+int heim_w32_key_create(unsigned long *, void (*)(void *));
+int heim_w32_delete_key(unsigned long);
+int heim_w32_setspecific(unsigned long, void *);
+void *heim_w32_getspecific(unsigned long);
+void heim_w32_service_thread_detach(void *);
+
+#endif /* HEIM_BASE_H */
diff --git a/lib/base/heimbasepriv.h b/lib/base/heimbasepriv.h
new file mode 100644
index 000000000000..eb155006fe0d
--- /dev/null
+++ b/lib/base/heimbasepriv.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(HEIM_BASE_MAINTAINER) && defined(ENABLE_PTHREAD_SUPPORT)
+#define HEIM_WIN32_TLS
+#elif defined(WIN32)
+#define HEIM_WIN32_TLS
+#endif
+
+typedef void (*heim_type_init)(void *);
+typedef heim_object_t (*heim_type_copy)(void *);
+typedef int (*heim_type_cmp)(void *, void *);
+typedef unsigned long (*heim_type_hash)(void *);
+typedef heim_string_t (*heim_type_description)(void *);
+
+typedef struct heim_type_data *heim_type_t;
+
+enum {
+ HEIM_TID_NUMBER = 0,
+ HEIM_TID_NULL = 1,
+ HEIM_TID_BOOL = 2,
+ HEIM_TID_TAGGED_UNUSED2 = 3, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED3 = 4, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED4 = 5, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED5 = 6, /* reserved for tagged object types */
+ HEIM_TID_TAGGED_UNUSED6 = 7, /* reserved for tagged object types */
+ HEIM_TID_MEMORY = 128,
+ HEIM_TID_ARRAY = 129,
+ HEIM_TID_DICT = 130,
+ HEIM_TID_STRING = 131,
+ HEIM_TID_AUTORELEASE = 132,
+ HEIM_TID_ERROR = 133,
+ HEIM_TID_DATA = 134,
+ HEIM_TID_DB = 135,
+ HEIM_TID_USER = 255
+
+};
+
+struct heim_type_data {
+ heim_tid_t tid;
+ const char *name;
+ heim_type_init init;
+ heim_type_dealloc dealloc;
+ heim_type_copy copy;
+ heim_type_cmp cmp;
+ heim_type_hash hash;
+ heim_type_description desc;
+};
+
+heim_type_t _heim_get_isa(heim_object_t);
+
+heim_type_t
+_heim_create_type(const char *name,
+ heim_type_init init,
+ heim_type_dealloc dealloc,
+ heim_type_copy copy,
+ heim_type_cmp cmp,
+ heim_type_hash hash,
+ heim_type_description desc);
+
+heim_object_t
+_heim_alloc_object(heim_type_t type, size_t size);
+
+void *
+_heim_get_isaextra(heim_object_t o, size_t idx);
+
+heim_tid_t
+_heim_type_get_tid(heim_type_t type);
+
+void
+_heim_make_permanent(heim_object_t ptr);
+
+heim_data_t
+_heim_db_get_value(heim_db_t, heim_string_t, heim_data_t, heim_error_t *);
+
+
+/* tagged tid */
+extern struct heim_type_data _heim_null_object;
+extern struct heim_type_data _heim_bool_object;
+extern struct heim_type_data _heim_number_object;
+extern struct heim_type_data _heim_string_object;
diff --git a/lib/base/heimqueue.h b/lib/base/heimqueue.h
new file mode 100644
index 000000000000..423a68478792
--- /dev/null
+++ b/lib/base/heimqueue.h
@@ -0,0 +1,167 @@
+/* $NetBSD: queue.h,v 1.38 2004/04/18 14:12:05 lukem Exp $ */
+/* $Id$ */
+
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ */
+
+#ifndef _HEIM_QUEUE_H_
+#define _HEIM_QUEUE_H_
+
+/*
+ * Tail queue definitions.
+ */
+#define HEIM_TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+}
+
+#define HEIM_TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+#define HEIM_TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if defined(_KERNEL) && defined(QUEUEDEBUG)
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field) \
+ if ((head)->tqh_first && \
+ (head)->tqh_first->field.tqe_prev != &(head)->tqh_first) \
+ panic("HEIM_TAILQ_INSERT_HEAD %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field) \
+ if (*(head)->tqh_last != NULL) \
+ panic("HEIM_TAILQ_INSERT_TAIL %p %s:%d", (head), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field) \
+ if ((elm)->field.tqe_next && \
+ (elm)->field.tqe_next->field.tqe_prev != \
+ &(elm)->field.tqe_next) \
+ panic("HEIM_TAILQ_* forw %p %s:%d", (elm), __FILE__, __LINE__);\
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("HEIM_TAILQ_* back %p %s:%d", (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field) \
+ if ((elm)->field.tqe_next == NULL && \
+ (head)->tqh_last != &(elm)->field.tqe_next) \
+ panic("HEIM_TAILQ_PREREMOVE head %p elm %p %s:%d", \
+ (head), (elm), __FILE__, __LINE__);
+#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field) \
+ (elm)->field.tqe_next = (void *)1L; \
+ (elm)->field.tqe_prev = (void *)1L;
+#else
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD(head, elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL(head, elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_OP(elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_PREREMOVE(head, elm, field)
+#define QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE(elm, field)
+#endif
+
+#define HEIM_TAILQ_INIT(head) do { \
+ (head)->tqh_first = NULL; \
+ (head)->tqh_last = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_INSERT_HEAD((head), (elm), field) \
+ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
+ (head)->tqh_first->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (head)->tqh_first = (elm); \
+ (elm)->field.tqe_prev = &(head)->tqh_first; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_INSERT_TAIL((head), (elm), field) \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \
+ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
+ (elm)->field.tqe_next->field.tqe_prev = \
+ &(elm)->field.tqe_next; \
+ else \
+ (head)->tqh_last = &(elm)->field.tqe_next; \
+ (listelm)->field.tqe_next = (elm); \
+ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_OP((listelm), field) \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ (elm)->field.tqe_next = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_REMOVE(head, elm, field) do { \
+ QUEUEDEBUG_HEIM_TAILQ_PREREMOVE((head), (elm), field) \
+ QUEUEDEBUG_HEIM_TAILQ_OP((elm), field) \
+ if (((elm)->field.tqe_next) != NULL) \
+ (elm)->field.tqe_next->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \
+ QUEUEDEBUG_HEIM_TAILQ_POSTREMOVE((elm), field); \
+} while (/*CONSTCOND*/0)
+
+#define HEIM_TAILQ_FOREACH(var, head, field) \
+ for ((var) = ((head)->tqh_first); \
+ (var); \
+ (var) = ((var)->field.tqe_next))
+
+#define HEIM_TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \
+ (var); \
+ (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)))
+
+/*
+ * Tail queue access methods.
+ */
+#define HEIM_TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+#define HEIM_TAILQ_FIRST(head) ((head)->tqh_first)
+#define HEIM_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define HEIM_TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+#define HEIM_TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+
+#endif /* !_HEIM_QUEUE_H_ */
diff --git a/lib/base/json.c b/lib/base/json.c
new file mode 100644
index 000000000000..2ef371b975ea
--- /dev/null
+++ b/lib/base/json.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <ctype.h>
+#include <base64.h>
+
+static heim_base_once_t heim_json_once = HEIM_BASE_ONCE_INIT;
+static heim_string_t heim_tid_data_uuid_key = NULL;
+static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static void
+json_init_once(void *arg)
+{
+ heim_tid_data_uuid_key = __heim_string_constant("heimdal-type-data-76d7fca2-d0da-4b20-a126-1a10f8a0eae6");
+}
+
+struct twojson {
+ void *ctx;
+ void (*out)(void *, const char *);
+ size_t indent;
+ heim_json_flags_t flags;
+ int ret;
+ int first;
+};
+
+struct heim_strbuf {
+ char *str;
+ size_t len;
+ size_t alloced;
+ int enomem;
+ heim_json_flags_t flags;
+};
+
+static int
+base2json(heim_object_t, struct twojson *);
+
+static void
+indent(struct twojson *j)
+{
+ size_t i = j->indent;
+ if (j->flags & HEIM_JSON_F_ONE_LINE)
+ return;
+ while (i--)
+ j->out(j->ctx, "\t");
+}
+
+static void
+array2json(heim_object_t value, void *ctx, int *stop)
+{
+ struct twojson *j = ctx;
+ if (j->ret)
+ return;
+ if (j->first) {
+ j->first = 0;
+ } else {
+ j->out(j->ctx, NULL); /* eat previous '\n' if possible */
+ j->out(j->ctx, ",\n");
+ }
+ j->ret = base2json(value, j);
+}
+
+static void
+dict2json(heim_object_t key, heim_object_t value, void *ctx)
+{
+ struct twojson *j = ctx;
+ if (j->ret)
+ return;
+ if (j->first) {
+ j->first = 0;
+ } else {
+ j->out(j->ctx, NULL); /* eat previous '\n' if possible */
+ j->out(j->ctx, ",\n");
+ }
+ j->ret = base2json(key, j);
+ if (j->ret)
+ return;
+ j->out(j->ctx, " : \n");
+ j->indent++;
+ j->ret = base2json(value, j);
+ if (j->ret)
+ return;
+ j->indent--;
+}
+
+static int
+base2json(heim_object_t obj, struct twojson *j)
+{
+ heim_tid_t type;
+ int first = 0;
+
+ if (obj == NULL) {
+ if (j->flags & HEIM_JSON_F_CNULL2JSNULL) {
+ obj = heim_null_create();
+ } else if (j->flags & HEIM_JSON_F_NO_C_NULL) {
+ return EINVAL;
+ } else {
+ indent(j);
+ j->out(j->ctx, "<NULL>\n"); /* This is NOT valid JSON! */
+ return 0;
+ }
+ }
+
+ type = heim_get_tid(obj);
+ switch (type) {
+ case HEIM_TID_ARRAY:
+ indent(j);
+ j->out(j->ctx, "[\n");
+ j->indent++;
+ first = j->first;
+ j->first = 1;
+ heim_array_iterate_f(obj, j, array2json);
+ j->indent--;
+ if (!j->first)
+ j->out(j->ctx, "\n");
+ indent(j);
+ j->out(j->ctx, "]\n");
+ j->first = first;
+ break;
+
+ case HEIM_TID_DICT:
+ indent(j);
+ j->out(j->ctx, "{\n");
+ j->indent++;
+ first = j->first;
+ j->first = 1;
+ heim_dict_iterate_f(obj, j, dict2json);
+ j->indent--;
+ if (!j->first)
+ j->out(j->ctx, "\n");
+ indent(j);
+ j->out(j->ctx, "}\n");
+ j->first = first;
+ break;
+
+ case HEIM_TID_STRING:
+ indent(j);
+ j->out(j->ctx, "\"");
+ j->out(j->ctx, heim_string_get_utf8(obj));
+ j->out(j->ctx, "\"");
+ break;
+
+ case HEIM_TID_DATA: {
+ heim_dict_t d;
+ heim_string_t v;
+ const heim_octet_string *data;
+ char *b64 = NULL;
+ int ret;
+
+ if (j->flags & HEIM_JSON_F_NO_DATA)
+ return EINVAL; /* JSON doesn't do binary */
+
+ data = heim_data_get_data(obj);
+ ret = rk_base64_encode(data->data, data->length, &b64);
+ if (ret < 0 || b64 == NULL)
+ return ENOMEM;
+
+ if (j->flags & HEIM_JSON_F_NO_DATA_DICT) {
+ indent(j);
+ j->out(j->ctx, "\"");
+ j->out(j->ctx, b64); /* base64-encode; hope there's no aliasing */
+ j->out(j->ctx, "\"");
+ free(b64);
+ } else {
+ /*
+ * JSON has no way to represent binary data, therefore the
+ * following is a Heimdal-specific convention.
+ *
+ * We encode binary data as a dict with a single very magic
+ * key with a base64-encoded value. The magic key includes
+ * a uuid, so we're not likely to alias accidentally.
+ */
+ d = heim_dict_create(2);
+ if (d == NULL) {
+ free(b64);
+ return ENOMEM;
+ }
+ v = heim_string_ref_create(b64, free);
+ if (v == NULL) {
+ free(b64);
+ heim_release(d);
+ return ENOMEM;
+ }
+ ret = heim_dict_set_value(d, heim_tid_data_uuid_key, v);
+ heim_release(v);
+ if (ret) {
+ heim_release(d);
+ return ENOMEM;
+ }
+ ret = base2json(d, j);
+ heim_release(d);
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+
+ case HEIM_TID_NUMBER: {
+ char num[32];
+ indent(j);
+ snprintf(num, sizeof (num), "%d", heim_number_get_int(obj));
+ j->out(j->ctx, num);
+ break;
+ }
+ case HEIM_TID_NULL:
+ indent(j);
+ j->out(j->ctx, "null");
+ break;
+ case HEIM_TID_BOOL:
+ indent(j);
+ j->out(j->ctx, heim_bool_val(obj) ? "true" : "false");
+ break;
+ default:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+heim_base2json(heim_object_t obj, void *ctx, heim_json_flags_t flags,
+ void (*out)(void *, const char *))
+{
+ struct twojson j;
+
+ if (flags & HEIM_JSON_F_STRICT_STRINGS)
+ return ENOTSUP; /* Sorry, not yet! */
+
+ heim_base_once_f(&heim_json_once, NULL, json_init_once);
+
+ j.indent = 0;
+ j.ctx = ctx;
+ j.out = out;
+ j.flags = flags;
+ j.ret = 0;
+ j.first = 1;
+
+ return base2json(obj, &j);
+}
+
+
+/*
+ *
+ */
+
+struct parse_ctx {
+ unsigned long lineno;
+ const uint8_t *p;
+ const uint8_t *pstart;
+ const uint8_t *pend;
+ heim_error_t error;
+ size_t depth;
+ heim_json_flags_t flags;
+};
+
+
+static heim_object_t
+parse_value(struct parse_ctx *ctx);
+
+/*
+ * This function eats whitespace, but, critically, it also succeeds
+ * only if there's anything left to parse.
+ */
+static int
+white_spaces(struct parse_ctx *ctx)
+{
+ while (ctx->p < ctx->pend) {
+ uint8_t c = *ctx->p;
+ if (c == ' ' || c == '\t' || c == '\r') {
+
+ } else if (c == '\n') {
+ ctx->lineno++;
+ } else
+ return 0;
+ (ctx->p)++;
+ }
+ return -1;
+}
+
+static int
+is_number(uint8_t n)
+{
+ return ('0' <= n && n <= '9');
+}
+
+static heim_number_t
+parse_number(struct parse_ctx *ctx)
+{
+ int number = 0, neg = 1;
+
+ if (ctx->p >= ctx->pend)
+ return NULL;
+
+ if (*ctx->p == '-') {
+ if (ctx->p + 1 >= ctx->pend)
+ return NULL;
+ neg = -1;
+ ctx->p += 1;
+ }
+
+ while (ctx->p < ctx->pend) {
+ if (is_number(*ctx->p)) {
+ number = (number * 10) + (*ctx->p - '0');
+ } else {
+ break;
+ }
+ ctx->p += 1;
+ }
+
+ return heim_number_create(number * neg);
+}
+
+static heim_string_t
+parse_string(struct parse_ctx *ctx)
+{
+ const uint8_t *start;
+ int quote = 0;
+
+ if (ctx->flags & HEIM_JSON_F_STRICT_STRINGS) {
+ ctx->error = heim_error_create(EINVAL, "Strict JSON string encoding "
+ "not yet supported");
+ return NULL;
+ }
+
+ if (*ctx->p != '"') {
+ ctx->error = heim_error_create(EINVAL, "Expected a JSON string but "
+ "found something else at line %lu",
+ ctx->lineno);
+ return NULL;
+ }
+ start = ++ctx->p;
+
+ while (ctx->p < ctx->pend) {
+ if (*ctx->p == '\n') {
+ ctx->lineno++;
+ } else if (*ctx->p == '\\') {
+ if (ctx->p + 1 == ctx->pend)
+ goto out;
+ ctx->p++;
+ quote = 1;
+ } else if (*ctx->p == '"') {
+ heim_object_t o;
+
+ if (quote) {
+ char *p0, *p;
+ p = p0 = malloc(ctx->p - start);
+ if (p == NULL)
+ goto out;
+ while (start < ctx->p) {
+ if (*start == '\\') {
+ start++;
+ /* XXX validate quoted char */
+ }
+ *p++ = *start++;
+ }
+ o = heim_string_create_with_bytes(p0, p - p0);
+ free(p0);
+ } else {
+ o = heim_string_create_with_bytes(start, ctx->p - start);
+ if (o == NULL) {
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ /* If we can decode as base64, then let's */
+ if (ctx->flags & HEIM_JSON_F_TRY_DECODE_DATA) {
+ void *buf;
+ size_t len;
+ const char *s;
+
+ s = heim_string_get_utf8(o);
+ len = strlen(s);
+
+ if (len >= 4 && strspn(s, base64_chars) >= len - 2) {
+ buf = malloc(len);
+ if (buf == NULL) {
+ heim_release(o);
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+ len = rk_base64_decode(s, buf);
+ if (len == -1) {
+ free(buf);
+ return o;
+ }
+ heim_release(o);
+ o = heim_data_ref_create(buf, len, free);
+ }
+ }
+ }
+ ctx->p += 1;
+
+ return o;
+ }
+ ctx->p += 1;
+ }
+ out:
+ ctx->error = heim_error_create(EINVAL, "ran out of string");
+ return NULL;
+}
+
+static int
+parse_pair(heim_dict_t dict, struct parse_ctx *ctx)
+{
+ heim_string_t key;
+ heim_object_t value;
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == '}') {
+ ctx->p++;
+ return 0;
+ }
+
+ if (ctx->flags & HEIM_JSON_F_STRICT_DICT)
+ /* JSON allows only string keys */
+ key = parse_string(ctx);
+ else
+ /* heim_dict_t allows any heim_object_t as key */
+ key = parse_value(ctx);
+ if (key == NULL)
+ /* Even heim_dict_t does not allow C NULLs as keys though! */
+ return -1;
+
+ if (white_spaces(ctx)) {
+ heim_release(key);
+ return -1;
+ }
+
+ if (*ctx->p != ':') {
+ heim_release(key);
+ return -1;
+ }
+
+ ctx->p += 1; /* safe because we call white_spaces() next */
+
+ if (white_spaces(ctx)) {
+ heim_release(key);
+ return -1;
+ }
+
+ value = parse_value(ctx);
+ if (value == NULL &&
+ (ctx->error != NULL || (ctx->flags & HEIM_JSON_F_NO_C_NULL))) {
+ if (ctx->error == NULL)
+ ctx->error = heim_error_create(EINVAL, "Invalid JSON encoding");
+ heim_release(key);
+ return -1;
+ }
+ heim_dict_set_value(dict, key, value);
+ heim_release(key);
+ heim_release(value);
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == '}') {
+ /*
+ * Return 1 but don't consume the '}' so we can count the one
+ * pair in a one-pair dict
+ */
+ return 1;
+ } else if (*ctx->p == ',') {
+ ctx->p++;
+ return 1;
+ }
+ return -1;
+}
+
+static heim_dict_t
+parse_dict(struct parse_ctx *ctx)
+{
+ heim_dict_t dict;
+ size_t count = 0;
+ int ret;
+
+ heim_assert(*ctx->p == '{', "string doesn't start with {");
+
+ dict = heim_dict_create(11);
+ if (dict == NULL) {
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+
+ ctx->p += 1; /* safe because parse_pair() calls white_spaces() first */
+
+ while ((ret = parse_pair(dict, ctx)) > 0)
+ count++;
+ if (ret < 0) {
+ heim_release(dict);
+ return NULL;
+ }
+ if (count == 1 && !(ctx->flags & HEIM_JSON_F_NO_DATA_DICT)) {
+ heim_object_t v = heim_dict_copy_value(dict, heim_tid_data_uuid_key);
+
+ /*
+ * Binary data encoded as a dict with a single magic key with
+ * base64-encoded value? Decode as heim_data_t.
+ */
+ if (v != NULL && heim_get_tid(v) == HEIM_TID_STRING) {
+ void *buf;
+ size_t len;
+
+ buf = malloc(strlen(heim_string_get_utf8(v)));
+ if (buf == NULL) {
+ heim_release(dict);
+ heim_release(v);
+ ctx->error = heim_error_create_enomem();
+ return NULL;
+ }
+ len = rk_base64_decode(heim_string_get_utf8(v), buf);
+ heim_release(v);
+ if (len == -1) {
+ free(buf);
+ return dict; /* assume aliasing accident */
+ }
+ heim_release(dict);
+ return (heim_dict_t)heim_data_ref_create(buf, len, free);
+ }
+ }
+ return dict;
+}
+
+static int
+parse_item(heim_array_t array, struct parse_ctx *ctx)
+{
+ heim_object_t value;
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == ']') {
+ ctx->p++; /* safe because parse_value() calls white_spaces() first */
+ return 0;
+ }
+
+ value = parse_value(ctx);
+ if (value == NULL &&
+ (ctx->error || (ctx->flags & HEIM_JSON_F_NO_C_NULL)))
+ return -1;
+
+ heim_array_append_value(array, value);
+ heim_release(value);
+
+ if (white_spaces(ctx))
+ return -1;
+
+ if (*ctx->p == ']') {
+ ctx->p++;
+ return 0;
+ } else if (*ctx->p == ',') {
+ ctx->p++;
+ return 1;
+ }
+ return -1;
+}
+
+static heim_array_t
+parse_array(struct parse_ctx *ctx)
+{
+ heim_array_t array = heim_array_create();
+ int ret;
+
+ heim_assert(*ctx->p == '[', "array doesn't start with [");
+ ctx->p += 1;
+
+ while ((ret = parse_item(array, ctx)) > 0)
+ ;
+ if (ret < 0) {
+ heim_release(array);
+ return NULL;
+ }
+ return array;
+}
+
+static heim_object_t
+parse_value(struct parse_ctx *ctx)
+{
+ size_t len;
+ heim_object_t o;
+
+ if (white_spaces(ctx))
+ return NULL;
+
+ if (*ctx->p == '"') {
+ return parse_string(ctx);
+ } else if (*ctx->p == '{') {
+ if (ctx->depth-- == 1) {
+ ctx->error = heim_error_create(EINVAL, "JSON object too deep");
+ return NULL;
+ }
+ o = parse_dict(ctx);
+ ctx->depth++;
+ return o;
+ } else if (*ctx->p == '[') {
+ if (ctx->depth-- == 1) {
+ ctx->error = heim_error_create(EINVAL, "JSON object too deep");
+ return NULL;
+ }
+ o = parse_array(ctx);
+ ctx->depth++;
+ return o;
+ } else if (is_number(*ctx->p) || *ctx->p == '-') {
+ return parse_number(ctx);
+ }
+
+ len = ctx->pend - ctx->p;
+
+ if ((ctx->flags & HEIM_JSON_F_NO_C_NULL) == 0 &&
+ len >= 6 && memcmp(ctx->p, "<NULL>", 6) == 0) {
+ ctx->p += 6;
+ return heim_null_create();
+ } else if (len >= 4 && memcmp(ctx->p, "null", 4) == 0) {
+ ctx->p += 4;
+ return heim_null_create();
+ } else if (len >= 4 && strncasecmp((char *)ctx->p, "true", 4) == 0) {
+ ctx->p += 4;
+ return heim_bool_create(1);
+ } else if (len >= 5 && strncasecmp((char *)ctx->p, "false", 5) == 0) {
+ ctx->p += 5;
+ return heim_bool_create(0);
+ }
+
+ ctx->error = heim_error_create(EINVAL, "unknown char %c at %lu line %lu",
+ (char)*ctx->p,
+ (unsigned long)(ctx->p - ctx->pstart),
+ ctx->lineno);
+ return NULL;
+}
+
+
+heim_object_t
+heim_json_create(const char *string, size_t max_depth, heim_json_flags_t flags,
+ heim_error_t *error)
+{
+ return heim_json_create_with_bytes(string, strlen(string), max_depth, flags,
+ error);
+}
+
+heim_object_t
+heim_json_create_with_bytes(const void *data, size_t length, size_t max_depth,
+ heim_json_flags_t flags, heim_error_t *error)
+{
+ struct parse_ctx ctx;
+ heim_object_t o;
+
+ heim_base_once_f(&heim_json_once, NULL, json_init_once);
+
+ ctx.lineno = 1;
+ ctx.p = data;
+ ctx.pstart = data;
+ ctx.pend = ((uint8_t *)data) + length;
+ ctx.error = NULL;
+ ctx.flags = flags;
+ ctx.depth = max_depth;
+
+ o = parse_value(&ctx);
+
+ if (o == NULL && error) {
+ *error = ctx.error;
+ } else if (ctx.error) {
+ heim_release(ctx.error);
+ }
+
+ return o;
+}
+
+
+static void
+show_printf(void *ctx, const char *str)
+{
+ if (str == NULL)
+ return;
+ fprintf(ctx, "%s", str);
+}
+
+/**
+ * Dump a heimbase object to stderr (useful from the debugger!)
+ *
+ * @param obj object to dump using JSON or JSON-like format
+ *
+ * @addtogroup heimbase
+ */
+void
+heim_show(heim_object_t obj)
+{
+ heim_base2json(obj, stderr, HEIM_JSON_F_NO_DATA_DICT, show_printf);
+}
+
+static void
+strbuf_add(void *ctx, const char *str)
+{
+ struct heim_strbuf *strbuf = ctx;
+ size_t len;
+
+ if (strbuf->enomem)
+ return;
+
+ if (str == NULL) {
+ /*
+ * Eat the last '\n'; this is used when formatting dict pairs
+ * and array items so that the ',' separating them is never
+ * preceded by a '\n'.
+ */
+ if (strbuf->len > 0 && strbuf->str[strbuf->len - 1] == '\n')
+ strbuf->len--;
+ return;
+ }
+
+ len = strlen(str);
+ if ((len + 1) > (strbuf->alloced - strbuf->len)) {
+ size_t new_len = strbuf->alloced + (strbuf->alloced >> 2) + len + 1;
+ char *s;
+
+ s = realloc(strbuf->str, new_len);
+ if (s == NULL) {
+ strbuf->enomem = 1;
+ return;
+ }
+ strbuf->str = s;
+ strbuf->alloced = new_len;
+ }
+ /* +1 so we copy the NUL */
+ (void) memcpy(strbuf->str + strbuf->len, str, len + 1);
+ strbuf->len += len;
+ if (strbuf->str[strbuf->len - 1] == '\n' &&
+ strbuf->flags & HEIM_JSON_F_ONE_LINE)
+ strbuf->len--;
+}
+
+#define STRBUF_INIT_SZ 64
+
+heim_string_t
+heim_json_copy_serialize(heim_object_t obj, heim_json_flags_t flags, heim_error_t *error)
+{
+ heim_string_t str;
+ struct heim_strbuf strbuf;
+ int ret;
+
+ if (error)
+ *error = NULL;
+
+ memset(&strbuf, 0, sizeof (strbuf));
+ strbuf.str = malloc(STRBUF_INIT_SZ);
+ if (strbuf.str == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ strbuf.len = 0;
+ strbuf.alloced = STRBUF_INIT_SZ;
+ strbuf.str[0] = '\0';
+ strbuf.flags = flags;
+
+ ret = heim_base2json(obj, &strbuf, flags, strbuf_add);
+ if (ret || strbuf.enomem) {
+ if (error) {
+ if (strbuf.enomem || ret == ENOMEM)
+ *error = heim_error_create_enomem();
+ else
+ *error = heim_error_create(1, "Impossible to JSON-encode "
+ "object");
+ }
+ free(strbuf.str);
+ return NULL;
+ }
+ if (flags & HEIM_JSON_F_ONE_LINE) {
+ strbuf.flags &= ~HEIM_JSON_F_ONE_LINE;
+ strbuf_add(&strbuf, "\n");
+ }
+ str = heim_string_ref_create(strbuf.str, free);
+ if (str == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ free(strbuf.str);
+ }
+ return str;
+}
diff --git a/lib/base/null.c b/lib/base/null.c
new file mode 100644
index 000000000000..43bde965c777
--- /dev/null
+++ b/lib/base/null.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+struct heim_type_data _heim_null_object = {
+ HEIM_TID_NULL,
+ "null-object",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+heim_null_t
+heim_null_create(void)
+{
+ return heim_base_make_tagged_object(0, HEIM_TID_NULL);
+}
diff --git a/lib/base/number.c b/lib/base/number.c
new file mode 100644
index 000000000000..c259f69971d0
--- /dev/null
+++ b/lib/base/number.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+
+static void
+number_dealloc(void *ptr)
+{
+}
+
+static int
+number_cmp(void *a, void *b)
+{
+ int na, nb;
+
+ if (heim_base_is_tagged_object(a))
+ na = heim_base_tagged_object_value(a);
+ else
+ na = *(int *)a;
+
+ if (heim_base_is_tagged_object(b))
+ nb = heim_base_tagged_object_value(b);
+ else
+ nb = *(int *)b;
+
+ return na - nb;
+}
+
+static unsigned long
+number_hash(void *ptr)
+{
+ if (heim_base_is_tagged_object(ptr))
+ return heim_base_tagged_object_value(ptr);
+ return (unsigned long)*(int *)ptr;
+}
+
+struct heim_type_data _heim_number_object = {
+ HEIM_TID_NUMBER,
+ "number-object",
+ NULL,
+ number_dealloc,
+ NULL,
+ number_cmp,
+ number_hash,
+ NULL
+};
+
+/**
+ * Create a number object
+ *
+ * @param the number to contain in the object
+ *
+ * @return a number object
+ */
+
+heim_number_t
+heim_number_create(int number)
+{
+ heim_number_t n;
+
+ if (number < 0xffffff && number >= 0)
+ return heim_base_make_tagged_object(number, HEIM_TID_NUMBER);
+
+ n = _heim_alloc_object(&_heim_number_object, sizeof(int));
+ if (n)
+ *((int *)n) = number;
+ return n;
+}
+
+/**
+ * Return the type ID of number objects
+ *
+ * @return type id of number objects
+ */
+
+heim_tid_t
+heim_number_get_type_id(void)
+{
+ return HEIM_TID_NUMBER;
+}
+
+/**
+ * Get the int value of the content
+ *
+ * @param number the number object to get the value from
+ *
+ * @return an int
+ */
+
+int
+heim_number_get_int(heim_number_t number)
+{
+ if (heim_base_is_tagged_object(number))
+ return heim_base_tagged_object_value(number);
+ return *(int *)number;
+}
diff --git a/lib/base/roken_rename.h b/lib/base/roken_rename.h
new file mode 100644
index 000000000000..ea72098933ac
--- /dev/null
+++ b/lib/base/roken_rename.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1998 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef __heimbase_roken_rename_h__
+#define __heimbase_roken_rename_h__
+
+#ifndef HAVE_VSNPRINTF
+#define rk_vsnprintf heimbase_vsnprintf
+#endif
+#ifndef HAVE_ASPRINTF
+#define rk_asprintf heimbase_asprintf
+#endif
+#ifndef HAVE_ASNPRINTF
+#define rk_asnprintf heimbase_asnprintf
+#endif
+#ifndef HAVE_VASPRINTF
+#define rk_vasprintf heimbase_vasprintf
+#endif
+#ifndef HAVE_VASNPRINTF
+#define rk_vasnprintf heimbase_vasnprintf
+#endif
+#ifndef HAVE_STRDUP
+#define rk_strdup heimbase_strdup
+#endif
+#ifndef HAVE_STRNDUP
+#define rk_strndup heimbase_strndup
+#endif
+
+#endif /* __heimbase_roken_rename_h__ */
diff --git a/lib/base/string.c b/lib/base/string.c
new file mode 100644
index 000000000000..35ea2182ba3e
--- /dev/null
+++ b/lib/base/string.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "baselocl.h"
+#include <string.h>
+
+static void
+string_dealloc(void *ptr)
+{
+ heim_string_t s = ptr;
+ heim_string_free_f_t *deallocp;
+ heim_string_free_f_t dealloc;
+
+ if (*(const char *)ptr != '\0')
+ return;
+
+ /* Possible string ref */
+ deallocp = _heim_get_isaextra(s, 0);
+ dealloc = *deallocp;
+ if (dealloc != NULL) {
+ char **strp = _heim_get_isaextra(s, 1);
+ dealloc(*strp);
+ }
+}
+
+static int
+string_cmp(void *a, void *b)
+{
+ if (*(char *)a == '\0') {
+ char **strp = _heim_get_isaextra(a, 1);
+
+ if (*strp != NULL)
+ a = *strp; /* a is a string ref */
+ }
+ if (*(char *)b == '\0') {
+ char **strp = _heim_get_isaextra(b, 1);
+
+ if (*strp != NULL)
+ b = *strp; /* b is a string ref */
+ }
+ return strcmp(a, b);
+}
+
+static unsigned long
+string_hash(void *ptr)
+{
+ const char *s = ptr;
+ unsigned long n;
+
+ for (n = 0; *s; ++s)
+ n += *s;
+ return n;
+}
+
+struct heim_type_data _heim_string_object = {
+ HEIM_TID_STRING,
+ "string-object",
+ NULL,
+ string_dealloc,
+ NULL,
+ string_cmp,
+ string_hash,
+ NULL
+};
+
+/**
+ * Create a string object
+ *
+ * @param string the string to create, must be an utf8 string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create(const char *string)
+{
+ return heim_string_create_with_bytes(string, strlen(string));
+}
+
+/**
+ * Create a string object without copying the source.
+ *
+ * @param string the string to referenced, must be UTF-8
+ * @param dealloc the function to use to release the referece to the string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_ref_create(const char *string, heim_string_free_f_t dealloc)
+{
+ heim_string_t s;
+ heim_string_free_f_t *deallocp;
+
+ s = _heim_alloc_object(&_heim_string_object, 1);
+ if (s) {
+ const char **strp;
+
+ ((char *)s)[0] = '\0';
+ deallocp = _heim_get_isaextra(s, 0);
+ *deallocp = dealloc;
+ strp = _heim_get_isaextra(s, 1);
+ *strp = string;
+ }
+ return s;
+}
+
+/**
+ * Create a string object
+ *
+ * @param string the string to create, must be an utf8 string
+ * @param len the length of the string
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create_with_bytes(const void *data, size_t len)
+{
+ heim_string_t s;
+
+ s = _heim_alloc_object(&_heim_string_object, len + 1);
+ if (s) {
+ memcpy(s, data, len);
+ ((char *)s)[len] = '\0';
+ }
+ return s;
+}
+
+/**
+ * Create a string object using a format string
+ *
+ * @param fmt format string
+ * @param ...
+ *
+ * @return string object
+ */
+
+heim_string_t
+heim_string_create_with_format(const char *fmt, ...)
+{
+ heim_string_t s;
+ char *str = NULL;
+ va_list ap;
+ int ret;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&str, fmt, ap);
+ va_end(ap);
+ if (ret < 0 || str == NULL)
+ return NULL;
+
+ s = heim_string_ref_create(str, string_dealloc);
+ if (s == NULL)
+ free(str);
+ return s;
+}
+
+/**
+ * Return the type ID of string objects
+ *
+ * @return type id of string objects
+ */
+
+heim_tid_t
+heim_string_get_type_id(void)
+{
+ return HEIM_TID_STRING;
+}
+
+/**
+ * Get the string value of the content.
+ *
+ * @param string the string object to get the value from
+ *
+ * @return a utf8 string
+ */
+
+const char *
+heim_string_get_utf8(heim_string_t string)
+{
+ if (*(const char *)string == '\0') {
+ const char **strp;
+
+ /* String ref */
+ strp = _heim_get_isaextra(string, 1);
+ if (*strp != NULL)
+ return *strp;
+ }
+ return (const char *)string;
+}
+
+/*
+ *
+ */
+
+static void
+init_string(void *ptr)
+{
+ heim_dict_t *dict = ptr;
+ *dict = heim_dict_create(101);
+ heim_assert(*dict != NULL, "__heim_string_constant");
+}
+
+heim_string_t
+__heim_string_constant(const char *_str)
+{
+ static HEIMDAL_MUTEX mutex = HEIMDAL_MUTEX_INITIALIZER;
+ static heim_base_once_t once;
+ static heim_dict_t dict = NULL;
+ heim_string_t s, s2;
+
+ heim_base_once_f(&once, &dict, init_string);
+ s = heim_string_create(_str);
+
+ HEIMDAL_MUTEX_lock(&mutex);
+ s2 = heim_dict_get_value(dict, s);
+ if (s2) {
+ heim_release(s);
+ s = s2;
+ } else {
+ _heim_make_permanent(s);
+ heim_dict_set_value(dict, s, s);
+ }
+ HEIMDAL_MUTEX_unlock(&mutex);
+
+ return s;
+}
diff --git a/lib/base/test_base.c b/lib/base/test_base.c
new file mode 100644
index 000000000000..250708df2ef4
--- /dev/null
+++ b/lib/base/test_base.c
@@ -0,0 +1,961 @@
+/*
+ * Copyright (c) 2010-2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * This is a test of libheimbase functionality. If you make any changes
+ * to libheimbase or to this test you should run it under valgrind with
+ * the following options:
+ *
+ * -v --track-fds=yes --num-callers=30 --leak-check=full
+ *
+ * and make sure that there are no leaks that don't have
+ * __heim_string_constant() or heim_db_register() in their stack trace.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifndef WIN32
+#include <sys/file.h>
+#endif
+#ifdef HAVE_IO_H
+#include <io.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+
+#include "baselocl.h"
+
+static void
+memory_free(heim_object_t obj)
+{
+}
+
+static int
+test_memory(void)
+{
+ void *ptr;
+
+ ptr = heim_alloc(10, "memory", memory_free);
+
+ heim_retain(ptr);
+ heim_release(ptr);
+
+ heim_retain(ptr);
+ heim_release(ptr);
+
+ heim_release(ptr);
+
+ ptr = heim_alloc(10, "memory", NULL);
+ heim_release(ptr);
+
+ return 0;
+}
+
+static int
+test_mutex(void)
+{
+ HEIMDAL_MUTEX m = HEIMDAL_MUTEX_INITIALIZER;
+
+ HEIMDAL_MUTEX_lock(&m);
+ HEIMDAL_MUTEX_unlock(&m);
+ HEIMDAL_MUTEX_destroy(&m);
+
+ HEIMDAL_MUTEX_init(&m);
+ HEIMDAL_MUTEX_lock(&m);
+ HEIMDAL_MUTEX_unlock(&m);
+ HEIMDAL_MUTEX_destroy(&m);
+
+ return 0;
+}
+
+static int
+test_rwlock(void)
+{
+ HEIMDAL_RWLOCK l = HEIMDAL_RWLOCK_INITIALIZER;
+
+ HEIMDAL_RWLOCK_rdlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_wrlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_trywrlock(&l) != 0)
+ err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_tryrdlock(&l))
+ err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_destroy(&l);
+
+ HEIMDAL_RWLOCK_init(&l);
+ HEIMDAL_RWLOCK_rdlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_wrlock(&l);
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_trywrlock(&l))
+ err(1, "HEIMDAL_RWLOCK_trywrlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ if (HEIMDAL_RWLOCK_tryrdlock(&l))
+ err(1, "HEIMDAL_RWLOCK_tryrdlock() failed with lock not held");
+ HEIMDAL_RWLOCK_unlock(&l);
+ HEIMDAL_RWLOCK_destroy(&l);
+
+ return 0;
+}
+
+static int
+test_dict(void)
+{
+ heim_dict_t dict;
+ heim_number_t a1 = heim_number_create(1);
+ heim_string_t a2 = heim_string_create("hejsan");
+ heim_number_t a3 = heim_number_create(3);
+ heim_string_t a4 = heim_string_create("foosan");
+
+ dict = heim_dict_create(10);
+
+ heim_dict_set_value(dict, a1, a2);
+ heim_dict_set_value(dict, a3, a4);
+
+ heim_dict_delete_key(dict, a3);
+ heim_dict_delete_key(dict, a1);
+
+ heim_release(a1);
+ heim_release(a2);
+ heim_release(a3);
+ heim_release(a4);
+
+ heim_release(dict);
+
+ return 0;
+}
+
+static int
+test_auto_release(void)
+{
+ heim_auto_release_t ar1, ar2;
+ heim_number_t n1;
+ heim_string_t s1;
+
+ ar1 = heim_auto_release_create();
+
+ s1 = heim_string_create("hejsan");
+ heim_auto_release(s1);
+
+ n1 = heim_number_create(1);
+ heim_auto_release(n1);
+
+ ar2 = heim_auto_release_create();
+
+ n1 = heim_number_create(1);
+ heim_auto_release(n1);
+
+ heim_release(ar2);
+ heim_release(ar1);
+
+ return 0;
+}
+
+static int
+test_string(void)
+{
+ heim_string_t s1, s2;
+ const char *string = "hejsan";
+
+ s1 = heim_string_create(string);
+ s2 = heim_string_create(string);
+
+ if (heim_cmp(s1, s2) != 0) {
+ printf("the same string is not the same\n");
+ exit(1);
+ }
+
+ heim_release(s1);
+ heim_release(s2);
+
+ return 0;
+}
+
+static int
+test_error(void)
+{
+ heim_error_t e;
+ heim_string_t s;
+
+ e = heim_error_create(10, "foo: %s", "bar");
+ heim_assert(heim_error_get_code(e) == 10, "error_code != 10");
+
+ s = heim_error_copy_string(e);
+ heim_assert(strcmp(heim_string_get_utf8(s), "foo: bar") == 0, "msg wrong");
+
+ heim_release(s);
+ heim_release(e);
+
+ return 0;
+}
+
+static int
+test_json(void)
+{
+ static char *j[] = {
+ "{ \"k1\" : \"s1\", \"k2\" : \"s2\" }",
+ "{ \"k1\" : [\"s1\", \"s2\", \"s3\"], \"k2\" : \"s3\" }",
+ "{ \"k1\" : {\"k2\":\"s1\",\"k3\":\"s2\",\"k4\":\"s3\"}, \"k5\" : \"s4\" }",
+ "[ \"v1\", \"v2\", [\"v3\",\"v4\",[\"v 5\",\" v 7 \"]], -123456789, "
+ "null, true, false, 123456789, \"\"]",
+ " -1"
+ };
+ char *s;
+ size_t i, k;
+ heim_object_t o, o2;
+ heim_string_t k1 = heim_string_create("k1");
+
+ o = heim_json_create("\"string\"", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("string", heim_string_get_utf8(o)) == 0, "wrong string");
+ heim_release(o);
+
+ o = heim_json_create(" \"foo\\\"bar\" ]", 10, 0, NULL);
+ heim_assert(o != NULL, "string");
+ heim_assert(heim_get_tid(o) == heim_string_get_type_id(), "string-tid");
+ heim_assert(strcmp("foo\"bar", heim_string_get_utf8(o)) == 0, "wrong string");
+ heim_release(o);
+
+ o = heim_json_create(" { \"key\" : \"value\" }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o);
+
+ o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
+ "{ \"k3\" : \"s4\" } : -1 }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o);
+
+ o = heim_json_create("{ { \"k1\" : \"s1\", \"k2\" : \"s2\" } : \"s3\", "
+ "{ \"k3\" : \"s4\" } : -1 }", 10,
+ HEIM_JSON_F_STRICT_DICT, NULL);
+ heim_assert(o == NULL, "dict");
+
+ o = heim_json_create(" { \"k1\" : \"s1\", \"k2\" : \"s2\" }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_string_get_type_id(), "string-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create(" { \"k1\" : { \"k2\" : \"s2\" } }", 10, 0, NULL);
+ heim_assert(o != NULL, "dict");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_dict_get_type_id(), "dict-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("{ \"k1\" : 1 }", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_dict_get_type_id(), "dict-tid");
+ o2 = heim_dict_copy_value(o, k1);
+ heim_assert(heim_get_tid(o2) == heim_number_get_type_id(), "number-tid");
+ heim_release(o2);
+ heim_release(o);
+
+ o = heim_json_create("-10", 10, 0, NULL);
+ heim_assert(o != NULL, "number");
+ heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ heim_release(o);
+
+ o = heim_json_create("99", 10, 0, NULL);
+ heim_assert(o != NULL, "number");
+ heim_assert(heim_get_tid(o) == heim_number_get_type_id(), "number-tid");
+ heim_release(o);
+
+ o = heim_json_create(" [ 1 ]", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ heim_release(o);
+
+ o = heim_json_create(" [ -1 ]", 10, 0, NULL);
+ heim_assert(o != NULL, "array");
+ heim_assert(heim_get_tid(o) == heim_array_get_type_id(), "array-tid");
+ heim_release(o);
+
+ for (i = 0; i < (sizeof (j) / sizeof (j[0])); i++) {
+ o = heim_json_create(j[i], 10, 0, NULL);
+ if (o == NULL) {
+ fprintf(stderr, "Failed to parse this JSON: %s\n", j[i]);
+ return 1;
+ }
+ heim_release(o);
+ /* Simple fuzz test */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ o = heim_json_create_with_bytes(j[i], k, 10, 0, NULL);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %.*s\n", (int)k, j[i]);
+ return EINVAL;
+ }
+ }
+ /* Again, but this time make it so valgrind can find invalid accesses */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ s = strndup(j[i], k);
+ if (s == NULL)
+ return ENOMEM;
+ o = heim_json_create(s, 10, 0, NULL);
+ free(s);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
+ return EINVAL;
+ }
+ }
+ /* Again, but with no NUL termination */
+ for (k = strlen(j[i]) - 1; k > 0; k--) {
+ s = malloc(k);
+ if (s == NULL)
+ return ENOMEM;
+ memcpy(s, j[i], k);
+ o = heim_json_create_with_bytes(s, k, 10, 0, NULL);
+ free(s);
+ if (o != NULL) {
+ fprintf(stderr, "Invalid JSON parsed: %s\n", j[i]);
+ return EINVAL;
+ }
+ }
+ }
+
+ heim_release(k1);
+
+ return 0;
+}
+
+static int
+test_path(void)
+{
+ heim_dict_t dict = heim_dict_create(11);
+ heim_string_t p1 = heim_string_create("abc");
+ heim_string_t p2a = heim_string_create("def");
+ heim_string_t p2b = heim_string_create("DEF");
+ heim_number_t p3 = heim_number_create(0);
+ heim_string_t p4a = heim_string_create("ghi");
+ heim_string_t p4b = heim_string_create("GHI");
+ heim_array_t a = heim_array_create();
+ heim_number_t l1 = heim_number_create(42);
+ heim_number_t l2 = heim_number_create(813);
+ heim_number_t l3 = heim_number_create(1234);
+ heim_string_t k1 = heim_string_create("k1");
+ heim_string_t k2 = heim_string_create("k2");
+ heim_string_t k3 = heim_string_create("k3");
+ heim_string_t k2_1 = heim_string_create("k2-1");
+ heim_string_t k2_2 = heim_string_create("k2-2");
+ heim_string_t k2_3 = heim_string_create("k2-3");
+ heim_string_t k2_4 = heim_string_create("k2-4");
+ heim_string_t k2_5 = heim_string_create("k2-5");
+ heim_string_t k2_5_1 = heim_string_create("k2-5-1");
+ heim_object_t o;
+ heim_object_t neg_num;
+ int ret;
+
+ if (!dict || !p1 || !p2a || !p2b || !p4a || !p4b)
+ return ENOMEM;
+
+ ret = heim_path_create(dict, 11, a, NULL, p1, p2a, NULL);
+ heim_release(a);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l3, NULL, p1, p2b, NULL);
+ if (ret)
+ return ret;
+ o = heim_path_get(dict, NULL, p1, p2b, NULL);
+ if (o != l3)
+ return 1;
+ ret = heim_path_create(dict, 11, NULL, NULL, p1, p2a, p3, NULL);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l1, NULL, p1, p2a, p3, p4a, NULL);
+ if (ret)
+ return ret;
+ ret = heim_path_create(dict, 11, l2, NULL, p1, p2a, p3, p4b, NULL);
+ if (ret)
+ return ret;
+
+ o = heim_path_get(dict, NULL, p1, p2a, p3, p4a, NULL);
+ if (o != l1)
+ return 1;
+ o = heim_path_get(dict, NULL, p1, p2a, p3, p4b, NULL);
+ if (o != l2)
+ return 1;
+
+ heim_release(dict);
+
+ /* Test that JSON parsing works right by using heim_path_get() */
+ dict = heim_json_create("{\"k1\":1,"
+ "\"k2\":{\"k2-1\":21,"
+ "\"k2-2\":null,"
+ "\"k2-3\":true,"
+ "\"k2-4\":false,"
+ "\"k2-5\":[1,2,3,{\"k2-5-1\":-1},-2]},"
+ "\"k3\":[true,false,0,42]}", 10, 0, NULL);
+ heim_assert(dict != NULL, "dict");
+ o = heim_path_get(dict, NULL, k1, NULL);
+ if (heim_cmp(o, heim_number_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, NULL);
+ if (heim_get_tid(o) != heim_dict_get_type_id()) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_1, NULL);
+ if (heim_cmp(o, heim_number_create(21))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_2, NULL);
+ if (heim_cmp(o, heim_null_create())) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_3, NULL);
+ if (heim_cmp(o, heim_bool_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_4, NULL);
+ if (heim_cmp(o, heim_bool_create(0))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, NULL);
+ if (heim_get_tid(o) != heim_array_get_type_id()) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(0), NULL);
+ if (heim_cmp(o, heim_number_create(1))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(1), NULL);
+ if (heim_cmp(o, heim_number_create(2))) return 1;
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(3), k2_5_1, NULL);
+ if (heim_cmp(o, neg_num = heim_number_create(-1))) return 1;
+ heim_release(neg_num);
+ o = heim_path_get(dict, NULL, k2, k2_5, heim_number_create(4), NULL);
+ if (heim_cmp(o, neg_num = heim_number_create(-2))) return 1;
+ heim_release(neg_num);
+ o = heim_path_get(dict, NULL, k3, heim_number_create(3), NULL);
+ if (heim_cmp(o, heim_number_create(42))) return 1;
+
+ heim_release(dict);
+ heim_release(p1);
+ heim_release(p2a);
+ heim_release(p2b);
+ heim_release(p4a);
+ heim_release(p4b);
+ heim_release(k1);
+ heim_release(k2);
+ heim_release(k3);
+ heim_release(k2_1);
+ heim_release(k2_2);
+ heim_release(k2_3);
+ heim_release(k2_4);
+ heim_release(k2_5);
+ heim_release(k2_5_1);
+
+ return 0;
+}
+
+typedef struct dict_db {
+ heim_dict_t dict;
+ int locked;
+} *dict_db_t;
+
+static int
+dict_db_open(void *plug, const char *dbtype, const char *dbname,
+ heim_dict_t options, void **db, heim_error_t *error)
+{
+ dict_db_t dictdb;
+ heim_dict_t contents = NULL;
+
+ if (error)
+ *error = NULL;
+ if (dbtype && *dbtype && strcmp(dbtype, "dictdb"))
+ return EINVAL;
+ if (dbname && *dbname && strcmp(dbname, "MEMORY") != 0)
+ return EINVAL;
+ dictdb = heim_alloc(sizeof (*dictdb), "dict_db", NULL);
+ if (dictdb == NULL)
+ return ENOMEM;
+
+ if (contents != NULL)
+ dictdb->dict = contents;
+ else {
+ dictdb->dict = heim_dict_create(29);
+ if (dictdb->dict == NULL) {
+ heim_release(dictdb);
+ return ENOMEM;
+ }
+ }
+
+ *db = dictdb;
+ return 0;
+}
+
+static int
+dict_db_close(void *db, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ heim_release(dictdb->dict);
+ heim_release(dictdb);
+ return 0;
+}
+
+static int
+dict_db_lock(void *db, int read_only, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ if (dictdb->locked)
+ return EWOULDBLOCK;
+ dictdb->locked = 1;
+ return 0;
+}
+
+static int
+dict_db_unlock(void *db, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+ dictdb->locked = 0;
+ return 0;
+}
+
+static heim_data_t
+dict_db_copy_value(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ return heim_retain(heim_path_get(dictdb->dict, error, table, key, NULL));
+}
+
+static int
+dict_db_set_value(void *db, heim_string_t table,
+ heim_data_t key, heim_data_t value, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ return heim_path_create(dictdb->dict, 29, value, error, table, key, NULL);
+}
+
+static int
+dict_db_del_key(void *db, heim_string_t table, heim_data_t key,
+ heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ heim_path_delete(dictdb->dict, error, table, key, NULL);
+ return 0;
+}
+
+struct dict_db_iter_ctx {
+ heim_db_iterator_f_t iter_f;
+ void *iter_ctx;
+};
+
+static void dict_db_iter_f(heim_object_t key, heim_object_t value, void *arg)
+{
+ struct dict_db_iter_ctx *ctx = arg;
+
+ ctx->iter_f((heim_object_t)key, (heim_object_t)value, ctx->iter_ctx);
+}
+
+static void
+dict_db_iter(void *db, heim_string_t table, void *iter_data,
+ heim_db_iterator_f_t iter_f, heim_error_t *error)
+{
+ dict_db_t dictdb = db;
+ struct dict_db_iter_ctx ctx;
+ heim_dict_t table_dict;
+
+ if (error)
+ *error = NULL;
+
+ if (table == NULL)
+ table = HSTR("");
+
+ table_dict = heim_dict_copy_value(dictdb->dict, table);
+ if (table_dict == NULL)
+ return;
+
+ ctx.iter_ctx = iter_data;
+ ctx.iter_f = iter_f;
+
+ heim_dict_iterate_f(table_dict, &ctx, dict_db_iter_f);
+ heim_release(table_dict);
+}
+
+static void
+test_db_iter(heim_data_t k, heim_data_t v, void *arg)
+{
+ int *ret = arg;
+ const void *kptr, *vptr;
+ size_t klen, vlen;
+
+ heim_assert(heim_get_tid(k) == heim_data_get_type_id(), "...");
+
+ kptr = heim_data_get_ptr(k);
+ klen = heim_data_get_length(k);
+ vptr = heim_data_get_ptr(v);
+ vlen = heim_data_get_length(v);
+
+ if (klen == strlen("msg") && !strncmp(kptr, "msg", strlen("msg")) &&
+ vlen == strlen("abc") && !strncmp(vptr, "abc", strlen("abc")))
+ *ret &= ~(1);
+ else if (klen == strlen("msg2") &&
+ !strncmp(kptr, "msg2", strlen("msg2")) &&
+ vlen == strlen("FooBar") && !strncmp(vptr, "FooBar", strlen("FooBar")))
+ *ret &= ~(2);
+ else
+ *ret |= 4;
+}
+
+static struct heim_db_type dbt = {
+ 1, dict_db_open, NULL, dict_db_close,
+ dict_db_lock, dict_db_unlock, NULL, NULL, NULL, NULL,
+ dict_db_copy_value, dict_db_set_value,
+ dict_db_del_key, dict_db_iter
+};
+
+static int
+test_db(const char *dbtype, const char *dbname)
+{
+ heim_data_t k1, k2, v, v1, v2, v3;
+ heim_db_t db;
+ int ret;
+
+ if (dbtype == NULL) {
+ ret = heim_db_register("dictdb", NULL, &dbt);
+ heim_assert(!ret, "...");
+ db = heim_db_create("dictdb", "foo", NULL, NULL);
+ heim_assert(!db, "...");
+ db = heim_db_create("foobar", "MEMORY", NULL, NULL);
+ heim_assert(!db, "...");
+ db = heim_db_create("dictdb", "MEMORY", NULL, NULL);
+ heim_assert(db, "...");
+ } else {
+ heim_dict_t options;
+
+ options = heim_dict_create(11);
+ if (options == NULL) return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("journal-filename"),
+ HSTR("json-journal")))
+ return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("create"), heim_null_create()))
+ return ENOMEM;
+ if (heim_dict_set_value(options, HSTR("truncate"), heim_null_create()))
+ return ENOMEM;
+ db = heim_db_create(dbtype, dbname, options, NULL);
+ heim_assert(db, "...");
+ heim_release(options);
+ }
+
+ k1 = heim_data_create("msg", strlen("msg"));
+ k2 = heim_data_create("msg2", strlen("msg2"));
+ v1 = heim_data_create("Hello world!", strlen("Hello world!"));
+ v2 = heim_data_create("FooBar", strlen("FooBar"));
+ v3 = heim_data_create("abc", strlen("abc"));
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_set_value(db, NULL, k2, v2, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k2, NULL);
+ heim_assert(v && !heim_cmp(v, v2), "...");
+ heim_release(v);
+
+ ret = heim_db_set_value(db, NULL, k1, v3, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v3), "...");
+ heim_release(v);
+
+ ret = 3;
+ heim_db_iterate_f(db, NULL, &ret, test_db_iter, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_commit(db, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v3), "...");
+ heim_release(v);
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_set_value(db, NULL, k1, v1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_commit(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ ret = heim_db_begin(db, 0, NULL);
+ heim_assert(!ret, "...");
+
+ ret = heim_db_delete_key(db, NULL, k1, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v == NULL, "...");
+ heim_release(v);
+
+ ret = heim_db_rollback(db, NULL);
+ heim_assert(!ret, "...");
+
+ v = heim_db_copy_value(db, NULL, k1, NULL);
+ heim_assert(v && !heim_cmp(v, v1), "...");
+ heim_release(v);
+
+ if (dbtype != NULL) {
+ heim_data_t k3 = heim_data_create("value-is-a-dict", strlen("value-is-a-dict"));
+ heim_dict_t vdict = heim_dict_create(11);
+ heim_db_t db2;
+
+ heim_assert(k3 && vdict, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k1"), heim_number_create(11));
+ heim_assert(!ret, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k2"), heim_null_create());
+ heim_assert(!ret, "...");
+ ret = heim_dict_set_value(vdict, HSTR("vdict-k3"), HSTR("a value"));
+ heim_assert(!ret, "...");
+ ret = heim_db_set_value(db, NULL, k3, (heim_data_t)vdict, NULL);
+ heim_assert(!ret, "...");
+
+ heim_release(vdict);
+
+ db2 = heim_db_create(dbtype, dbname, NULL, NULL);
+ heim_assert(db2, "...");
+
+ vdict = (heim_dict_t)heim_db_copy_value(db2, NULL, k3, NULL);
+ heim_release(db2);
+ heim_release(k3);
+ heim_assert(vdict, "...");
+ heim_assert(heim_get_tid(vdict) == heim_dict_get_type_id(), "...");
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k1"));
+ heim_assert(v && !heim_cmp(v, heim_number_create(11)), "...");
+ heim_release(v);
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k2"));
+ heim_assert(v && !heim_cmp(v, heim_null_create()), "...");
+ heim_release(v);
+
+ v = heim_dict_copy_value(vdict, HSTR("vdict-k3"));
+ heim_assert(v && !heim_cmp(v, HSTR("a value")), "...");
+ heim_release(v);
+
+ heim_release(vdict);
+ }
+
+ heim_release(db);
+ heim_release(k1);
+ heim_release(k2);
+ heim_release(v1);
+ heim_release(v2);
+ heim_release(v3);
+
+ return 0;
+}
+
+struct test_array_iter_ctx {
+ char buf[256];
+};
+
+static void test_array_iter(heim_object_t elt, void *arg, int *stop)
+{
+ struct test_array_iter_ctx *iter_ctx = arg;
+
+ strcat(iter_ctx->buf, heim_string_get_utf8((heim_string_t)elt));
+}
+
+static int
+test_array()
+{
+ struct test_array_iter_ctx iter_ctx;
+ heim_string_t s1 = heim_string_create("abc");
+ heim_string_t s2 = heim_string_create("def");
+ heim_string_t s3 = heim_string_create("ghi");
+ heim_string_t s4 = heim_string_create("jkl");
+ heim_string_t s5 = heim_string_create("mno");
+ heim_string_t s6 = heim_string_create("pqr");
+ heim_array_t a = heim_array_create();
+
+ if (!s1 || !s2 || !s3 || !s4 || !s5 || !s6 || !a)
+ return ENOMEM;
+
+ heim_array_append_value(a, s4);
+ heim_array_append_value(a, s5);
+ heim_array_insert_value(a, 0, s3);
+ heim_array_insert_value(a, 0, s2);
+ heim_array_append_value(a, s6);
+ heim_array_insert_value(a, 0, s1);
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefghijklmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefjklmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 0);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defmnopqr") != 0)
+ return 1;
+
+ iter_ctx.buf[0] = '\0';
+ heim_array_delete_value(a, 2);
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defmno") != 0)
+ return 1;
+
+ heim_array_insert_value(a, 0, s1);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "abcdefmno") != 0)
+ return 1;
+
+ heim_array_insert_value(a, 0, s2);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmno") != 0)
+ return 1;
+
+ heim_array_append_value(a, s3);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmnoghi") != 0)
+ return 1;
+
+ heim_array_append_value(a, s6);
+ iter_ctx.buf[0] = '\0';
+ heim_array_iterate_f(a, &iter_ctx, test_array_iter);
+ if (strcmp(iter_ctx.buf, "defabcdefmnoghipqr") != 0)
+ return 1;
+
+ heim_release(s1);
+ heim_release(s2);
+ heim_release(s3);
+ heim_release(s4);
+ heim_release(s5);
+ heim_release(s6);
+ heim_release(a);
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int res = 0;
+
+ res |= test_memory();
+ res |= test_mutex();
+ res |= test_rwlock();
+ res |= test_dict();
+ res |= test_auto_release();
+ res |= test_string();
+ res |= test_error();
+ res |= test_json();
+ res |= test_path();
+ res |= test_db(NULL, NULL);
+ res |= test_db("json", argc > 1 ? argv[1] : "test_db.json");
+ res |= test_array();
+
+ return res ? 1 : 0;
+}
diff --git a/lib/base/version-script.map b/lib/base/version-script.map
new file mode 100644
index 000000000000..656277e37da2
--- /dev/null
+++ b/lib/base/version-script.map
@@ -0,0 +1,94 @@
+
+HEIMDAL_BASE_1.0 {
+ global:
+ _bsearch_file;
+ _bsearch_file_close;
+ _bsearch_file_info;
+ _bsearch_file_open;
+ _bsearch_text;
+ __heim_string_constant;
+ DllMain;
+ heim_abort;
+ heim_abortv;
+ heim_alloc;
+ heim_array_append_value;
+ heim_array_copy_value;
+ heim_array_create;
+ heim_array_delete_value;
+ heim_array_filter_f;
+ heim_array_get_length;
+ heim_array_get_type_id;
+ heim_array_get_value;
+ heim_array_iterate_f;
+ heim_array_iterate_reverse_f;
+ heim_array_insert_value;
+ heim_array_set_value;
+ heim_auto_release;
+ heim_auto_release_create;
+ heim_auto_release_drain;
+ heim_base_once_f;
+ heim_bool_create;
+ heim_bool_val;
+ heim_cmp;
+ heim_data_create;
+ heim_data_ref_create;
+ heim_data_get_data;
+ heim_data_get_length;
+ heim_data_get_ptr;
+ heim_data_get_type_id;
+ heim_data_ref_get_type_id;
+ heim_db_begin;
+ heim_db_clone;
+ heim_db_commit;
+ heim_db_copy_value;
+ heim_db_delete_key;
+ heim_db_get_type_id;
+ heim_db_iterate_f;
+ heim_db_create;
+ heim_db_register;
+ heim_db_rollback;
+ heim_db_set_value;
+ heim_dict_copy_value;
+ heim_dict_create;
+ heim_dict_delete_key;
+ heim_dict_get_type_id;
+ heim_dict_get_value;
+ heim_dict_iterate_f;
+ heim_dict_set_value;
+ heim_error_append;
+ heim_error_copy_string;
+ heim_error_create_opt;
+ heim_error_create;
+ heim_error_createv;
+ heim_error_create_enomem;
+ heim_error_get_code;
+ heim_get_hash;
+ heim_get_tid;
+ heim_json_create;
+ heim_json_create_with_bytes;
+ heim_json_copy_serialize;
+ heim_null_create;
+ heim_number_create;
+ heim_number_get_int;
+ heim_number_get_type_id;
+ heim_path_create;
+ heim_path_delete;
+ heim_path_get;
+ heim_path_copy;
+ heim_path_vcreate;
+ heim_path_vdelete;
+ heim_path_vget;
+ heim_path_vcopy;
+ heim_release;
+ heim_retain;
+ heim_show;
+ heim_sorted_text_file_dbtype;
+ heim_string_create;
+ heim_string_create_with_bytes;
+ heim_string_create_with_format;
+ heim_string_get_type_id;
+ heim_string_get_utf8;
+ heim_string_ref_create;
+ local:
+ *;
+};
diff --git a/lib/com_err/Makefile.am b/lib/com_err/Makefile.am
index a970b640a4b3..7c06cf56f9c8 100644
--- a/lib/com_err/Makefile.am
+++ b/lib/com_err/Makefile.am
@@ -13,7 +13,7 @@ endif
libcom_err_la_LIBADD = $(LIB_libintl)
-bin_PROGRAMS = compile_et
+noinst_PROGRAMS = compile_et
include_HEADERS = com_err.h com_right.h
@@ -42,6 +42,7 @@ strlcpy.c:
EXTRA_DIST = \
NTMakefile \
+ com_err.3 \
compile_et-version.rc \
libcom_err-version.rc \
libcom_err-exports.def \
diff --git a/lib/com_err/Makefile.in b/lib/com_err/Makefile.in
index b8323c6e2041..71c992d1e39c 100644
--- a/lib/com_err/Makefile.in
+++ b/lib/com_err/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,12 +96,8 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog lex.c parse.c \
- parse.h
@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
-bin_PROGRAMS = compile_et$(EXEEXT)
+noinst_PROGRAMS = compile_et$(EXEEXT)
subdir = lib/com_err
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -63,8 +113,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -77,6 +126,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -96,6 +146,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -121,8 +173,13 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
-am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
- "$(DESTDIR)$(includedir)"
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
dist_libcom_err_la_OBJECTS = libcom_err_la-error.lo \
@@ -132,47 +189,110 @@ dist_libcom_err_la_OBJECTS = libcom_err_la-error.lo \
@do_roken_rename_TRUE@ libcom_err_la-strlcpy.lo
libcom_err_la_OBJECTS = $(dist_libcom_err_la_OBJECTS) \
$(nodist_libcom_err_la_OBJECTS)
-libcom_err_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libcom_err_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libcom_err_la_LDFLAGS) $(LDFLAGS) -o $@
-PROGRAMS = $(bin_PROGRAMS)
+PROGRAMS = $(noinst_PROGRAMS)
am_compile_et_OBJECTS = compile_et.$(OBJEXT) parse.$(OBJEXT) \
lex.$(OBJEXT)
compile_et_OBJECTS = $(am_compile_et_OBJECTS)
compile_et_DEPENDENCIES = libcom_err.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
-LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
-LTLEXCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo " LEX " $@;
+am__v_LEX_1 =
YLWRAP = $(top_srcdir)/ylwrap
@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
-YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
-LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
+ -e s/c++$$/h++/ -e s/c$$/h/
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo " YACC " $@;
+am__v_YACC_1 =
SOURCES = $(dist_libcom_err_la_SOURCES) \
$(nodist_libcom_err_la_SOURCES) $(compile_et_SOURCES)
DIST_SOURCES = $(dist_libcom_err_la_SOURCES) $(compile_et_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/ylwrap ChangeLog lex.c parse.c parse.h
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -191,12 +311,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -206,17 +326,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -235,12 +355,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -249,6 +366,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -265,10 +383,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -276,6 +392,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -290,12 +407,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -312,6 +432,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -326,13 +447,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = -d
abs_builddir = @abs_builddir@
@@ -356,6 +471,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -391,29 +508,37 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
lib_LTLIBRARIES = libcom_err.la
libcom_err_la_LDFLAGS = -version-info 2:3:1 $(am__append_1)
libcom_err_la_LIBADD = $(LIB_libintl)
@@ -430,6 +555,7 @@ compile_et_LDADD = \
EXTRA_DIST = \
NTMakefile \
+ com_err.3 \
compile_et-version.rc \
libcom_err-version.rc \
libcom_err-exports.def \
@@ -438,7 +564,7 @@ EXTRA_DIST = \
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .l .lo .o .obj .y
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .l .lo .o .obj .y
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -451,7 +577,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/com_err/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/com_err/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -460,6 +585,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -469,9 +595,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -479,6 +605,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -494,51 +622,20 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libcom_err.la: $(libcom_err_la_OBJECTS) $(libcom_err_la_DEPENDENCIES)
- $(libcom_err_la_LINK) -rpath $(libdir) $(libcom_err_la_OBJECTS) $(libcom_err_la_LIBADD) $(LIBS)
-install-binPROGRAMS: $(bin_PROGRAMS)
- @$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
- @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
- for p in $$list; do echo "$$p $$p"; done | \
- sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
- done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
- -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
- sed 'N;N;N;s,\n, ,g' | \
- $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
- { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
- if ($$2 == $$4) files[d] = files[d] " " $$1; \
- else { print "f", $$3 "/" $$4, $$1; } } \
- END { for (d in files) print "f", d, files[d] }' | \
- while read type dir files; do \
- if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
- test -z "$$files" || { \
- echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
- $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
- } \
- ; done
-
-uninstall-binPROGRAMS:
- @$(NORMAL_UNINSTALL)
- @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
- files=`for p in $$list; do echo "$$p"; done | \
- sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
- test -n "$$list" || exit 0; \
- echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(bindir)" && rm -f $$files
-
-clean-binPROGRAMS:
- @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libcom_err.la: $(libcom_err_la_OBJECTS) $(libcom_err_la_DEPENDENCIES) $(EXTRA_libcom_err_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libcom_err_la_LINK) -rpath $(libdir) $(libcom_err_la_OBJECTS) $(libcom_err_la_LIBADD) $(LIBS)
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
echo " rm -f" $$list; \
rm -f $$list || exit $$?; \
test -n "$(EXEEXT)" || exit 0; \
@@ -546,13 +643,12 @@ clean-binPROGRAMS:
echo " rm -f" $$list; \
rm -f $$list
parse.h: parse.c
- @if test ! -f $@; then \
- rm -f parse.c; \
- $(MAKE) $(AM_MAKEFLAGS) parse.c; \
- else :; fi
-compile_et$(EXEEXT): $(compile_et_OBJECTS) $(compile_et_DEPENDENCIES)
+ @if test ! -f $@; then rm -f parse.c; else :; fi
+ @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) parse.c; else :; fi
+
+compile_et$(EXEEXT): $(compile_et_OBJECTS) $(compile_et_DEPENDENCIES) $(EXTRA_compile_et_DEPENDENCIES)
@rm -f compile_et$(EXEEXT)
- $(LINK) $(compile_et_OBJECTS) $(compile_et_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(compile_et_OBJECTS) $(compile_et_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -569,59 +665,59 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libcom_err_la-error.lo: error.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-error.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-error.Tpo -c -o libcom_err_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libcom_err_la-error.Tpo $(DEPDIR)/libcom_err_la-error.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error.c' object='libcom_err_la-error.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-error.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-error.Tpo -c -o libcom_err_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcom_err_la-error.Tpo $(DEPDIR)/libcom_err_la-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='libcom_err_la-error.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
libcom_err_la-com_err.lo: com_err.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-com_err.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-com_err.Tpo -c -o libcom_err_la-com_err.lo `test -f 'com_err.c' || echo '$(srcdir)/'`com_err.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libcom_err_la-com_err.Tpo $(DEPDIR)/libcom_err_la-com_err.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='com_err.c' object='libcom_err_la-com_err.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-com_err.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-com_err.Tpo -c -o libcom_err_la-com_err.lo `test -f 'com_err.c' || echo '$(srcdir)/'`com_err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcom_err_la-com_err.Tpo $(DEPDIR)/libcom_err_la-com_err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='com_err.c' object='libcom_err_la-com_err.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-com_err.lo `test -f 'com_err.c' || echo '$(srcdir)/'`com_err.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-com_err.lo `test -f 'com_err.c' || echo '$(srcdir)/'`com_err.c
libcom_err_la-snprintf.lo: snprintf.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-snprintf.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-snprintf.Tpo -c -o libcom_err_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libcom_err_la-snprintf.Tpo $(DEPDIR)/libcom_err_la-snprintf.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='snprintf.c' object='libcom_err_la-snprintf.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-snprintf.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-snprintf.Tpo -c -o libcom_err_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcom_err_la-snprintf.Tpo $(DEPDIR)/libcom_err_la-snprintf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snprintf.c' object='libcom_err_la-snprintf.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
libcom_err_la-strlcpy.lo: strlcpy.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-strlcpy.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-strlcpy.Tpo -c -o libcom_err_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libcom_err_la-strlcpy.Tpo $(DEPDIR)/libcom_err_la-strlcpy.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strlcpy.c' object='libcom_err_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libcom_err_la-strlcpy.lo -MD -MP -MF $(DEPDIR)/libcom_err_la-strlcpy.Tpo -c -o libcom_err_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcom_err_la-strlcpy.Tpo $(DEPDIR)/libcom_err_la-strlcpy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strlcpy.c' object='libcom_err_la-strlcpy.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcom_err_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libcom_err_la-strlcpy.lo `test -f 'strlcpy.c' || echo '$(srcdir)/'`strlcpy.c
.l.c:
- $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
.y.c:
- $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
mostlyclean-libtool:
-rm -f *.lo
@@ -630,8 +726,11 @@ clean-libtool:
-rm -rf .libs _libs
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -645,30 +744,17 @@ uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -680,15 +766,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -697,6 +779,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -738,10 +835,8 @@ check-am: all-am
$(MAKE) $(AM_MAKEFLAGS) check-local
check: check-am
all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) all-local
-install-binPROGRAMS: install-libLTLIBRARIES
-
installdirs:
- for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
@@ -754,10 +849,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -774,8 +874,8 @@ maintainer-clean-generic:
-rm -f parse.h
clean: clean-am
-clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
- clean-libtool mostlyclean-am
+clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
+ clean-noinstPROGRAMS mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
@@ -802,9 +902,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -843,46 +942,58 @@ ps: ps-am
ps-am:
-uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \
- uninstall-libLTLIBRARIES
+uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \
- clean-libtool ctags dist-hook distclean distclean-compile \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am dist-hook distclean distclean-compile \
distclean-generic distclean-libtool distclean-tags distdir dvi \
dvi-am html html-am info info-am install install-am \
- install-binPROGRAMS install-data install-data-am \
- install-data-hook install-dvi install-dvi-am install-exec \
- install-exec-am install-exec-hook install-html install-html-am \
- install-includeHEADERS install-info install-info-am \
- install-libLTLIBRARIES install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-binPROGRAMS \
- uninstall-hook uninstall-includeHEADERS \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-includeHEADERS \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am uninstall-hook uninstall-includeHEADERS \
uninstall-libLTLIBRARIES
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-exec-hook: install-suid-programs
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -890,7 +1001,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -947,6 +1058,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -989,6 +1102,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1002,13 +1128,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/com_err/com_err.3 b/lib/com_err/com_err.3
new file mode 100644
index 000000000000..f9296497bb32
--- /dev/null
+++ b/lib/com_err/com_err.3
@@ -0,0 +1,246 @@
+.\" Copyright (c) 2005 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.\" This manpage was contributed by Gregory McGarry.
+.\"
+.Dd July 7, 2005
+.Dt COM_ERR 3
+.Os
+.Sh NAME
+.Nm com_err ,
+.Nm com_err_va ,
+.Nm error_message ,
+.Nm error_table_name ,
+.Nm init_error_table ,
+.Nm set_com_err_hook ,
+.Nm reset_com_err_hook ,
+.Nm add_to_error_table ,
+.Nm initialize_error_table_r
+.Nm free_error_table ,
+.Nm com_right
+.Nd common error display library
+.Sh LIBRARY
+Common Error Library (libcom_err, -lcom_err)
+.Sh SYNOPSIS
+.Fd #include <stdio.h>
+.Fd #include <stdarg.h>
+.Fd #include <krb5/com_err.h>
+.Fd #include \&"XXX_err.h\&"
+.Pp
+typedef void (*errf)(const char *, long, const char *, ...);
+.Ft void
+.Fn com_err "const char *whoami" "long code" "const char *format" "..."
+.Ft void
+.Fn com_err_va "const char *whoami" "long code" "const char *format" "..."
+.Ft const char *
+.Fn error_message "long code"
+.Ft const char *
+.Fn error_table_name "int num"
+.Ft int
+.Fn init_error_table "const char **msgs" "long base" "int count"
+.Ft errf
+.Fn set_com_err_hook "errf func"
+.Ft errf
+.Fn reset_com_err_hook ""
+.Ft void
+.Fn add_to_error_table "struct et_list *new_table"
+.Ft void
+.Fn initialize_error_table_r "struct et_list **et_list" "const char **msgs" "int base" "long count"
+.Ft void
+.Fn free_error_table "struct et_list *"
+.Ft const char *
+.Fn com_right "struct et_list *list" long code"
+.Sh DESCRIPTION
+The
+.Nm
+library provides a common error-reporting mechanism for defining and
+accessing error codes and descriptions for application software
+packages. Error descriptions are defined in a table and error codes
+are used to index the table. The error table, the descriptions and
+the error codes are generated using
+.Xr compile_et 1 .
+.Pp
+The error table is registered with the
+.Nm
+library by calling its initialisation function defined in its header
+file. The initialisation function is generally defined as
+.Fn initialize_<name>_error_table ,
+where
+.Em name
+is the name of the error table.
+.Pp
+If a thread-safe version of the library is needed
+.Fn initialize_<name>_error_table_r
+that internally calls
+.Fn initialize_error_table_r
+instead be used.
+.Pp
+Any variable which is to contain an error code should be declared
+.Em <name>_error_number
+where
+.Em name
+is the name of the error table.
+.Sh FUNCTIONS
+The following functions are available to the application developer:
+.Bl -tag -width compact
+.It Fn com_err "whoami" "code" "format" "..."
+Displays an error message on standard error composed of the
+.Fa whoami
+string, which should specify the program name, followed by an error
+message generated from
+.Fa code ,
+and a string produced using the
+.Xr printf 3
+.Fa format
+string and any following arguments. If
+.Fa format
+is NULL, the formatted message will not be
+printed. The argument
+.Fa format
+may not be omitted.
+.It Fn com_err_va "whoami" "code" "format" "va_list args"
+This routine provides an interface, equivalent to
+.Fn com_err ,
+which may be used by higher-level variadic functions (functions which
+accept variable numbers of arguments).
+.It Fn error_message "code"
+Returns the character string error message associated with
+.Fa code .
+If
+.Fa code
+is associated with an unknown error table, or if
+.Fa code
+is associated with a known error table but is not in the table, a
+string of the form `Unknown code XXXX NN' is returned, where XXXX is
+the error table name produced by reversing the compaction performed on
+the error table number implied by that error code, and NN is the
+offset from that base value.
+.Pp
+Although this routine is available for use when needed, its use should
+be left to circumstances which render
+.Fn com_err
+unusable.
+.Pp
+.Fn com_right
+returns the error string just like
+.Fa com_err
+but in a thread-safe way.
+.Pp
+.It Fn error_table_name "num"
+Convert a machine-independent error table number
+.Fa num
+into an error table name.
+.It Fn init_error_table "msgs" "base" "count"
+Initialise the internal error table with the array of character string
+error messages in
+.Fa msgs
+of length
+.Fa count .
+The error codes are assigned incrementally from
+.Fa base .
+This function is useful for using the error-reporting mechanism with
+custom error tables that have not been generated with
+.Xr compile_et 1 .
+Although this routine is available for use when needed, its use should
+be restricted.
+.Pp
+.Fn initialize_error_table_r
+initialize the
+.Fa et_list
+in the same way as
+.Fn init_error_table ,
+but in a thread-safe way.
+.Pp
+.It Fn set_com_err_hook "func"
+Provides a hook into the
+.Nm
+library to allow the routine
+.Fa func
+to be dynamically substituted for
+.Fn com_err .
+After
+.Fn set_com_err_hook
+ has been called, calls to
+.Fn com_err
+will turn into calls to the new hook routine. This function is
+intended to be used in daemons to use a routine which calls
+.Xr syslog 3 ,
+or in a window system application to pop up a dialogue box.
+.It Fn reset_com_err_hook ""
+Turns off the hook set in
+.Fn set_com_err_hook .
+.It Fn add_to_error_table "new_table"
+Add the error table, its messages strings and error codes in
+.Fa new_table
+to the internal error table.
+.El
+.Sh EXAMPLES
+The following is an example using the table defined in
+.Xr compile_et 1 :
+.Pp
+.Bd -literal
+ #include <stdio.h>
+ #include <stdarg.h>
+ #include <syslog.h>
+
+ #include "test_err.h"
+
+ void
+ hook(const char *whoami, long code,
+ const char *format, va_list args)
+ {
+ char buffer[BUFSIZ];
+ static int initialized = 0;
+
+ if (!initialized) {
+ openlog(whoami, LOG_NOWAIT, LOG_DAEMON);
+ initialized = 1;
+ }
+ vsprintf(buffer, format, args);
+ syslog(LOG_ERR, "%s %s", error_message(code), buffer);
+ }
+
+ int
+ main(int argc, char *argv[])
+ {
+ char *whoami = argv[0];
+
+ initialize_test_error_table();
+ com_err(whoami, TEST_INVAL, "before hook");
+ set_com_err_hook(hook);
+ com_err(whoami, TEST_IO, "after hook");
+ return (0);
+ }
+.Ed
+.Sh SEE ALSO
+.Xr compile_et 1
diff --git a/lib/com_err/com_err.c b/lib/com_err/com_err.c
index fe4cc2983c50..22a810f8f2a6 100644
--- a/lib/com_err/com_err.c
+++ b/lib/com_err/com_err.c
@@ -70,7 +70,7 @@ init_error_table(const char **msgs, long base, int count)
static void KRB5_CALLCONV
default_proc (const char *whoami, long code, const char *fmt, va_list args)
- __attribute__((__format__(__printf__, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
static void KRB5_CALLCONV
default_proc (const char *whoami, long code, const char *fmt, va_list args)
diff --git a/lib/com_err/com_err.h b/lib/com_err/com_err.h
index 5b8b7e28f778..e71eabf2eb59 100644
--- a/lib/com_err/com_err.h
+++ b/lib/com_err/com_err.h
@@ -55,11 +55,11 @@ init_error_table (const char**, long, int);
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
com_err_va (const char *, long, const char *, va_list)
- __attribute__((format(printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
com_err (const char *, long, const char *, ...)
- __attribute__((format(printf, 3, 4)));
+ __attribute__ ((__format__ (__printf__, 3, 4)));
KRB5_LIB_FUNCTION errf KRB5_LIB_CALL
set_com_err_hook (errf);
diff --git a/lib/com_err/com_right.h b/lib/com_err/com_right.h
index b0857d283acf..7741684f0e24 100644
--- a/lib/com_err/com_right.h
+++ b/lib/com_err/com_right.h
@@ -58,6 +58,7 @@
#ifdef __STDC__
#include <stdarg.h>
+#include <stddef.h>
#endif
struct error_table {
diff --git a/lib/com_err/compile_et.c b/lib/com_err/compile_et.c
index c72abdecc863..40934c055aad 100644
--- a/lib/com_err/compile_et.c
+++ b/lib/com_err/compile_et.c
@@ -45,7 +45,7 @@
int numerror;
extern FILE *yyin;
-extern void yyparse(void);
+int yyparse(void);
long base_id;
int number;
@@ -56,7 +56,8 @@ char name[128];
char Basename[128];
#ifdef YYDEBUG
-extern int yydebug = 1;
+extern int yydebug;
+int yydebug = 1;
#endif
char *filename;
@@ -186,8 +187,8 @@ generate(void)
int version_flag;
int help_flag;
struct getargs args[] = {
- { "version", 0, arg_flag, &version_flag },
- { "help", 0, arg_flag, &help_flag }
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/com_err/error.c b/lib/com_err/error.c
index 6864e870a41e..bf046da22557 100644
--- a/lib/com_err/error.c
+++ b/lib/com_err/error.c
@@ -65,7 +65,7 @@ com_right_r(struct et_list *list, long code, char *str, size_t len)
const char *msg = p->table->msgs[code - p->table->base];
#ifdef LIBINTL
char domain[12 + 20];
- snprintf(domain, sizeof(domain), "heim_com_err%d", p->table->base);
+ snprintf(domain, sizeof(domain), "heim_com_err%ld", p->table->base);
#endif
strlcpy(str, dgettext(domain, msg), len);
return str;
diff --git a/lib/com_err/lex.c b/lib/com_err/lex.c
index 42b5f64fae3f..2ebfad758d34 100644
--- a/lib/com_err/lex.c
+++ b/lib/com_err/lex.c
@@ -46,6 +46,7 @@ typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@@ -354,7 +355,7 @@ static void yy_fatal_error (yyconst char msg[] );
*/
#define YY_DO_BEFORE_ACTION \
(yytext_ptr) = yy_bp; \
- yyleng = (size_t) (yy_cp - yy_bp); \
+ yyleng = (yy_size_t) (yy_cp - yy_bp); \
(yy_hold_char) = *yy_cp; \
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
@@ -475,7 +476,7 @@ char *yytext;
#line 1 "lex.l"
#line 2 "lex.l"
/*
- * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * Copyright (c) 1998 - 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -526,7 +527,7 @@ static int getstring(void);
#undef ECHO
-#line 530 "lex.c"
+#line 531 "lex.c"
#define INITIAL 0
@@ -708,7 +709,7 @@ YY_DECL
#line 58 "lex.l"
-#line 712 "lex.c"
+#line 713 "lex.c"
if ( !(yy_init) )
{
@@ -872,7 +873,7 @@ YY_RULE_SETUP
#line 74 "lex.l"
ECHO;
YY_BREAK
-#line 876 "lex.c"
+#line 877 "lex.c"
case YY_STATE_EOF(INITIAL):
yyterminate();
@@ -1836,13 +1837,11 @@ void yyfree (void * ptr )
-#ifndef yywrap /* XXX */
int
yywrap ()
{
return 1;
}
-#endif
static int
getstring(void)
diff --git a/lib/com_err/lex.h b/lib/com_err/lex.h
index e158816bbb6b..dcf6e8710427 100644
--- a/lib/com_err/lex.h
+++ b/lib/com_err/lex.h
@@ -34,6 +34,6 @@
/* $Id$ */
void _lex_error_message (const char *, ...)
-__attribute__ ((format (printf, 1, 2)));
+__attribute__ ((__format__ (__printf__, 1, 2)));
int yylex(void);
diff --git a/lib/com_err/lex.l b/lib/com_err/lex.l
index eb39e0cfb832..04a852205682 100644
--- a/lib/com_err/lex.l
+++ b/lib/com_err/lex.l
@@ -1,6 +1,6 @@
%{
/*
- * Copyright (c) 1998 - 2000 Kungliga Tekniska Högskolan
+ * Copyright (c) 1998 - 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -73,13 +73,11 @@ end { return END; }
. { return *yytext; }
%%
-#ifndef yywrap /* XXX */
int
yywrap ()
{
return 1;
}
-#endif
static int
getstring(void)
diff --git a/lib/gssapi/Makefile.am b/lib/gssapi/Makefile.am
index 919799fa80b2..ad88f6bf5939 100644
--- a/lib/gssapi/Makefile.am
+++ b/lib/gssapi/Makefile.am
@@ -12,9 +12,7 @@ AM_CPPFLAGS += \
-I$(srcdir)/ntlm \
-I$(srcdir)/krb5 \
-I$(srcdir)/spnego \
- $(INCLUDE_libintl) \
- $(INCLUDE_hcrypto) \
- $(INCLUDE_krb4)
+ $(INCLUDE_libintl)
lib_LTLIBRARIES = libgssapi.la
@@ -46,7 +44,7 @@ krb5src = \
krb5/external.c \
krb5/get_mic.c \
krb5/gsskrb5_locl.h \
- krb5/gsskrb5-private.h \
+ $(srcdir)/krb5/gsskrb5-private.h \
krb5/import_name.c \
krb5/import_sec_context.c \
krb5/indicate_mechs.c \
@@ -165,7 +163,7 @@ spnegosrc = \
spnego/external.c \
spnego/init_sec_context.c \
spnego/spnego_locl.h \
- spnego/spnego-private.h
+ $(srcdir)/spnego/spnego-private.h
ntlmsrc = \
ntlm/accept_sec_context.c \
@@ -184,7 +182,6 @@ ntlmsrc = \
ntlm/export_sec_context.c \
ntlm/external.c \
ntlm/ntlm.h \
- ntlm/ntlm-private.h \
ntlm/import_name.c \
ntlm/import_sec_context.c \
ntlm/indicate_mechs.c \
@@ -200,7 +197,7 @@ ntlmsrc = \
ntlm/release_name.c \
ntlm/kdc.c
-$(srcdir)/ntlm/ntlm-private.h:
+$(srcdir)/ntlm/ntlm-private.h: $(ntlmsrc)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p ntlm/ntlm-private.h $(ntlmsrc) || rm -f ntlm/ntlm-private.h
dist_libgssapi_la_SOURCES = \
@@ -235,9 +232,9 @@ man_MANS = gssapi.3 gss_acquire_cred.3 mech/mech.5
include_HEADERS = gssapi.h
noinst_HEADERS = \
gssapi_mech.h \
- ntlm/ntlm-private.h \
- spnego/spnego-private.h \
- krb5/gsskrb5-private.h
+ $(srcdir)/ntlm/ntlm-private.h \
+ $(srcdir)/spnego/spnego-private.h \
+ $(srcdir)/krb5/gsskrb5-private.h
nobase_include_HEADERS = \
gssapi/gssapi.h \
@@ -274,10 +271,13 @@ $(libgssapi_la_OBJECTS): $(srcdir)/version-script.map
BUILT_SOURCES = $(spnego_files:.x=.c) $(gssapi_files:.x=.c)
+$(libgssapi_la_OBJECTS): gkrb5_err.h
+gkrb5_err.h: $(srcdir)/krb5/gkrb5_err.et
+
CLEANFILES = $(BUILT_SOURCES) \
gkrb5_err.h gkrb5_err.c \
- $(spnego_files) spnego_asn1*.h* spnego_asn1_files spnego_asn1-template.c \
- $(gssapi_files) gssapi_asn1*.h* gssapi_asn1_files gssapi_asn1-template.c \
+ $(spnego_files) spnego_asn1*.h* spnego_asn1_files spnego_asn1-template.[cx] \
+ $(gssapi_files) gssapi_asn1*.h* gssapi_asn1_files gssapi_asn1-template.[cx] \
gss-commands.h gss-commands.c
$(spnego_files) spnego_asn1.hx spnego_asn1-priv.hx: spnego_asn1_files
@@ -304,12 +304,14 @@ test_cfx_SOURCES = krb5/test_cfx.c
check_PROGRAMS = test_acquire_cred $(TESTS)
bin_PROGRAMS = gsstool
-noinst_PROGRAMS = test_cred test_kcred test_context test_ntlm
+noinst_PROGRAMS = test_cred test_kcred test_context test_ntlm test_add_store_cred
test_context_SOURCES = test_context.c test_common.c test_common.h
test_ntlm_SOURCES = test_ntlm.c test_common.c test_common.h
test_acquire_cred_SOURCES = test_acquire_cred.c test_common.c test_common.h
+test_add_store_cred_SOURCES = test_add_store_cred.c
+
test_ntlm_LDADD = \
$(top_builddir)/lib/ntlm/libheimntlm.la \
$(LDADD)
@@ -339,6 +341,13 @@ EXTRA_DIST = \
libgssapi-version.rc \
libgssapi-exports.def \
$(man_MANS) \
+ gen-oid.pl \
+ gssapi/gssapi_netlogon.h \
+ krb5/test_acquire_cred.c \
+ krb5/test_cred.c \
+ krb5/test_kcred.c \
+ krb5/test_oid.c \
+ oid.txt \
krb5/gkrb5_err.et \
mech/gssapi.asn1 \
spnego/spnego.asn1 \
diff --git a/lib/gssapi/Makefile.in b/lib/gssapi/Makefile.in
index 46499032d33d..5778556cc70c 100644
--- a/lib/gssapi/Makefile.in
+++ b/lib/gssapi/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,16 +96,13 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(include_HEADERS) $(nobase_include_HEADERS) \
- $(noinst_HEADERS) $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
- $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
TESTS = test_oid$(EXEEXT) test_names$(EXEEXT) test_cfx$(EXEEXT)
check_PROGRAMS = test_acquire_cred$(EXEEXT) $(am__EXEEXT_1)
bin_PROGRAMS = gsstool$(EXEEXT)
noinst_PROGRAMS = test_cred$(EXEEXT) test_kcred$(EXEEXT) \
- test_context$(EXEEXT) test_ntlm$(EXEEXT)
+ test_context$(EXEEXT) test_ntlm$(EXEEXT) \
+ test_add_store_cred$(EXEEXT)
subdir = lib/gssapi
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -67,8 +118,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -81,6 +131,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -100,6 +151,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(nobase_include_HEADERS) $(noinst_HEADERS) $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -125,6 +178,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
"$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" \
"$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)" \
@@ -222,7 +281,11 @@ am__objects_7 = $(am__objects_5) $(am__objects_6)
nodist_libgssapi_la_OBJECTS = gkrb5_err.lo $(am__objects_7)
libgssapi_la_OBJECTS = $(dist_libgssapi_la_OBJECTS) \
$(nodist_libgssapi_la_OBJECTS)
-libgssapi_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libgssapi_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libgssapi_la_LDFLAGS) $(LDFLAGS) -o $@
am__EXEEXT_1 = test_oid$(EXEEXT) test_names$(EXEEXT) test_cfx$(EXEEXT)
@@ -239,6 +302,11 @@ test_acquire_cred_OBJECTS = $(am_test_acquire_cred_OBJECTS)
test_acquire_cred_LDADD = $(LDADD)
test_acquire_cred_DEPENDENCIES = libgssapi.la \
$(top_builddir)/lib/krb5/libkrb5.la $(am__DEPENDENCIES_1)
+am_test_add_store_cred_OBJECTS = test_add_store_cred.$(OBJEXT)
+test_add_store_cred_OBJECTS = $(am_test_add_store_cred_OBJECTS)
+test_add_store_cred_LDADD = $(LDADD)
+test_add_store_cred_DEPENDENCIES = libgssapi.la \
+ $(top_builddir)/lib/krb5/libkrb5.la $(am__DEPENDENCIES_1)
am_test_cfx_OBJECTS = krb5/test_cfx.$(OBJEXT)
test_cfx_OBJECTS = $(am_test_cfx_OBJECTS)
test_cfx_LDADD = $(LDADD)
@@ -275,41 +343,265 @@ test_oid_OBJECTS = test_oid.$(OBJEXT)
test_oid_LDADD = $(LDADD)
test_oid_DEPENDENCIES = libgssapi.la \
$(top_builddir)/lib/krb5/libkrb5.la $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(dist_libgssapi_la_SOURCES) $(nodist_libgssapi_la_SOURCES) \
$(dist_gsstool_SOURCES) $(nodist_gsstool_SOURCES) \
- $(test_acquire_cred_SOURCES) $(test_cfx_SOURCES) \
- $(test_context_SOURCES) test_cred.c test_kcred.c test_names.c \
- $(test_ntlm_SOURCES) test_oid.c
+ $(test_acquire_cred_SOURCES) $(test_add_store_cred_SOURCES) \
+ $(test_cfx_SOURCES) $(test_context_SOURCES) test_cred.c \
+ test_kcred.c test_names.c $(test_ntlm_SOURCES) test_oid.c
DIST_SOURCES = $(dist_libgssapi_la_SOURCES) $(dist_gsstool_SOURCES) \
- $(test_acquire_cred_SOURCES) $(test_cfx_SOURCES) \
- $(test_context_SOURCES) test_cred.c test_kcred.c test_names.c \
- $(test_ntlm_SOURCES) test_oid.c
+ $(test_acquire_cred_SOURCES) $(test_add_store_cred_SOURCES) \
+ $(test_cfx_SOURCES) $(test_context_SOURCES) test_cred.c \
+ test_kcred.c test_names.c $(test_ntlm_SOURCES) test_oid.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
man3dir = $(mandir)/man3
man5dir = $(mandir)/man5
MANS = $(man_MANS)
HEADERS = $(include_HEADERS) $(nobase_include_HEADERS) \
$(nodist_gssapi_HEADERS) $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -328,12 +620,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -343,17 +635,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -372,12 +664,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -386,6 +675,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -402,10 +692,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -413,6 +701,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -427,12 +716,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -449,6 +741,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -463,13 +756,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -493,6 +780,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -528,32 +817,39 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken) -I$(srcdir)/../krb5 -I$(srcdir) \
-I$(srcdir)/gssapi -I$(srcdir)/mech -I$(srcdir)/ntlm \
- -I$(srcdir)/krb5 -I$(srcdir)/spnego $(INCLUDE_libintl) \
- $(INCLUDE_hcrypto) $(INCLUDE_krb4)
+ -I$(srcdir)/krb5 -I$(srcdir)/spnego $(INCLUDE_libintl)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
AUTOMAKE_OPTIONS = subdir-objects
lib_LTLIBRARIES = libgssapi.la
krb5src = \
@@ -584,7 +880,7 @@ krb5src = \
krb5/external.c \
krb5/get_mic.c \
krb5/gsskrb5_locl.h \
- krb5/gsskrb5-private.h \
+ $(srcdir)/krb5/gsskrb5-private.h \
krb5/import_name.c \
krb5/import_sec_context.c \
krb5/indicate_mechs.c \
@@ -703,7 +999,7 @@ spnegosrc = \
spnego/external.c \
spnego/init_sec_context.c \
spnego/spnego_locl.h \
- spnego/spnego-private.h
+ $(srcdir)/spnego/spnego-private.h
ntlmsrc = \
ntlm/accept_sec_context.c \
@@ -722,7 +1018,6 @@ ntlmsrc = \
ntlm/export_sec_context.c \
ntlm/external.c \
ntlm/ntlm.h \
- ntlm/ntlm-private.h \
ntlm/import_name.c \
ntlm/import_sec_context.c \
ntlm/indicate_mechs.c \
@@ -763,9 +1058,9 @@ man_MANS = gssapi.3 gss_acquire_cred.3 mech/mech.5
include_HEADERS = gssapi.h
noinst_HEADERS = \
gssapi_mech.h \
- ntlm/ntlm-private.h \
- spnego/spnego-private.h \
- krb5/gsskrb5-private.h
+ $(srcdir)/ntlm/ntlm-private.h \
+ $(srcdir)/spnego/spnego-private.h \
+ $(srcdir)/krb5/gsskrb5-private.h
nobase_include_HEADERS = \
gssapi/gssapi.h \
@@ -796,8 +1091,8 @@ BUILTHEADERS = \
BUILT_SOURCES = $(spnego_files:.x=.c) $(gssapi_files:.x=.c)
CLEANFILES = $(BUILT_SOURCES) \
gkrb5_err.h gkrb5_err.c \
- $(spnego_files) spnego_asn1*.h* spnego_asn1_files spnego_asn1-template.c \
- $(gssapi_files) gssapi_asn1*.h* gssapi_asn1_files gssapi_asn1-template.c \
+ $(spnego_files) spnego_asn1*.h* spnego_asn1_files spnego_asn1-template.[cx] \
+ $(gssapi_files) gssapi_asn1*.h* gssapi_asn1_files gssapi_asn1-template.[cx] \
gss-commands.h gss-commands.c
# test_sequence
@@ -805,6 +1100,7 @@ test_cfx_SOURCES = krb5/test_cfx.c
test_context_SOURCES = test_context.c test_common.c test_common.h
test_ntlm_SOURCES = test_ntlm.c test_common.c test_common.h
test_acquire_cred_SOURCES = test_acquire_cred.c test_common.c test_common.h
+test_add_store_cred_SOURCES = test_add_store_cred.c
test_ntlm_LDADD = \
$(top_builddir)/lib/ntlm/libheimntlm.la \
$(LDADD)
@@ -828,6 +1124,13 @@ EXTRA_DIST = \
libgssapi-version.rc \
libgssapi-exports.def \
$(man_MANS) \
+ gen-oid.pl \
+ gssapi/gssapi_netlogon.h \
+ krb5/test_acquire_cred.c \
+ krb5/test_cred.c \
+ krb5/test_kcred.c \
+ krb5/test_oid.c \
+ oid.txt \
krb5/gkrb5_err.et \
mech/gssapi.asn1 \
spnego/spnego.asn1 \
@@ -839,7 +1142,7 @@ all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -852,7 +1155,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/gssapi/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/gssapi/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -861,6 +1163,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -870,9 +1173,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -880,6 +1183,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -895,12 +1200,14 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
krb5/$(am__dirstamp):
@$(MKDIR_P) krb5
@: > krb5/$(am__dirstamp)
@@ -1211,18 +1518,24 @@ spnego/external.lo: spnego/$(am__dirstamp) \
spnego/$(DEPDIR)/$(am__dirstamp)
spnego/init_sec_context.lo: spnego/$(am__dirstamp) \
spnego/$(DEPDIR)/$(am__dirstamp)
-libgssapi.la: $(libgssapi_la_OBJECTS) $(libgssapi_la_DEPENDENCIES)
- $(libgssapi_la_LINK) -rpath $(libdir) $(libgssapi_la_OBJECTS) $(libgssapi_la_LIBADD) $(LIBS)
+
+libgssapi.la: $(libgssapi_la_OBJECTS) $(libgssapi_la_DEPENDENCIES) $(EXTRA_libgssapi_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgssapi_la_LINK) -rpath $(libdir) $(libgssapi_la_OBJECTS) $(libgssapi_la_LIBADD) $(LIBS)
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -1243,7 +1556,8 @@ uninstall-binPROGRAMS:
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
@@ -1274,357 +1588,59 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-gsstool$(EXEEXT): $(gsstool_OBJECTS) $(gsstool_DEPENDENCIES)
+
+gsstool$(EXEEXT): $(gsstool_OBJECTS) $(gsstool_DEPENDENCIES) $(EXTRA_gsstool_DEPENDENCIES)
@rm -f gsstool$(EXEEXT)
- $(LINK) $(gsstool_OBJECTS) $(gsstool_LDADD) $(LIBS)
-test_acquire_cred$(EXEEXT): $(test_acquire_cred_OBJECTS) $(test_acquire_cred_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(gsstool_OBJECTS) $(gsstool_LDADD) $(LIBS)
+
+test_acquire_cred$(EXEEXT): $(test_acquire_cred_OBJECTS) $(test_acquire_cred_DEPENDENCIES) $(EXTRA_test_acquire_cred_DEPENDENCIES)
@rm -f test_acquire_cred$(EXEEXT)
- $(LINK) $(test_acquire_cred_OBJECTS) $(test_acquire_cred_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_acquire_cred_OBJECTS) $(test_acquire_cred_LDADD) $(LIBS)
+
+test_add_store_cred$(EXEEXT): $(test_add_store_cred_OBJECTS) $(test_add_store_cred_DEPENDENCIES) $(EXTRA_test_add_store_cred_DEPENDENCIES)
+ @rm -f test_add_store_cred$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_add_store_cred_OBJECTS) $(test_add_store_cred_LDADD) $(LIBS)
krb5/test_cfx.$(OBJEXT): krb5/$(am__dirstamp) \
krb5/$(DEPDIR)/$(am__dirstamp)
-test_cfx$(EXEEXT): $(test_cfx_OBJECTS) $(test_cfx_DEPENDENCIES)
+
+test_cfx$(EXEEXT): $(test_cfx_OBJECTS) $(test_cfx_DEPENDENCIES) $(EXTRA_test_cfx_DEPENDENCIES)
@rm -f test_cfx$(EXEEXT)
- $(LINK) $(test_cfx_OBJECTS) $(test_cfx_LDADD) $(LIBS)
-test_context$(EXEEXT): $(test_context_OBJECTS) $(test_context_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_cfx_OBJECTS) $(test_cfx_LDADD) $(LIBS)
+
+test_context$(EXEEXT): $(test_context_OBJECTS) $(test_context_DEPENDENCIES) $(EXTRA_test_context_DEPENDENCIES)
@rm -f test_context$(EXEEXT)
- $(LINK) $(test_context_OBJECTS) $(test_context_LDADD) $(LIBS)
-test_cred$(EXEEXT): $(test_cred_OBJECTS) $(test_cred_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_context_OBJECTS) $(test_context_LDADD) $(LIBS)
+
+test_cred$(EXEEXT): $(test_cred_OBJECTS) $(test_cred_DEPENDENCIES) $(EXTRA_test_cred_DEPENDENCIES)
@rm -f test_cred$(EXEEXT)
- $(LINK) $(test_cred_OBJECTS) $(test_cred_LDADD) $(LIBS)
-test_kcred$(EXEEXT): $(test_kcred_OBJECTS) $(test_kcred_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_cred_OBJECTS) $(test_cred_LDADD) $(LIBS)
+
+test_kcred$(EXEEXT): $(test_kcred_OBJECTS) $(test_kcred_DEPENDENCIES) $(EXTRA_test_kcred_DEPENDENCIES)
@rm -f test_kcred$(EXEEXT)
- $(LINK) $(test_kcred_OBJECTS) $(test_kcred_LDADD) $(LIBS)
-test_names$(EXEEXT): $(test_names_OBJECTS) $(test_names_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_kcred_OBJECTS) $(test_kcred_LDADD) $(LIBS)
+
+test_names$(EXEEXT): $(test_names_OBJECTS) $(test_names_DEPENDENCIES) $(EXTRA_test_names_DEPENDENCIES)
@rm -f test_names$(EXEEXT)
- $(LINK) $(test_names_OBJECTS) $(test_names_LDADD) $(LIBS)
-test_ntlm$(EXEEXT): $(test_ntlm_OBJECTS) $(test_ntlm_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_names_OBJECTS) $(test_names_LDADD) $(LIBS)
+
+test_ntlm$(EXEEXT): $(test_ntlm_OBJECTS) $(test_ntlm_DEPENDENCIES) $(EXTRA_test_ntlm_DEPENDENCIES)
@rm -f test_ntlm$(EXEEXT)
- $(LINK) $(test_ntlm_OBJECTS) $(test_ntlm_LDADD) $(LIBS)
-test_oid$(EXEEXT): $(test_oid_OBJECTS) $(test_oid_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_ntlm_OBJECTS) $(test_ntlm_LDADD) $(LIBS)
+
+test_oid$(EXEEXT): $(test_oid_OBJECTS) $(test_oid_DEPENDENCIES) $(EXTRA_test_oid_DEPENDENCIES)
@rm -f test_oid$(EXEEXT)
- $(LINK) $(test_oid_OBJECTS) $(test_oid_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_oid_OBJECTS) $(test_oid_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
- -rm -f krb5/8003.$(OBJEXT)
- -rm -f krb5/8003.lo
- -rm -f krb5/accept_sec_context.$(OBJEXT)
- -rm -f krb5/accept_sec_context.lo
- -rm -f krb5/acquire_cred.$(OBJEXT)
- -rm -f krb5/acquire_cred.lo
- -rm -f krb5/add_cred.$(OBJEXT)
- -rm -f krb5/add_cred.lo
- -rm -f krb5/address_to_krb5addr.$(OBJEXT)
- -rm -f krb5/address_to_krb5addr.lo
- -rm -f krb5/aeap.$(OBJEXT)
- -rm -f krb5/aeap.lo
- -rm -f krb5/arcfour.$(OBJEXT)
- -rm -f krb5/arcfour.lo
- -rm -f krb5/authorize_localname.$(OBJEXT)
- -rm -f krb5/authorize_localname.lo
- -rm -f krb5/canonicalize_name.$(OBJEXT)
- -rm -f krb5/canonicalize_name.lo
- -rm -f krb5/ccache_name.$(OBJEXT)
- -rm -f krb5/ccache_name.lo
- -rm -f krb5/cfx.$(OBJEXT)
- -rm -f krb5/cfx.lo
- -rm -f krb5/compare_name.$(OBJEXT)
- -rm -f krb5/compare_name.lo
- -rm -f krb5/compat.$(OBJEXT)
- -rm -f krb5/compat.lo
- -rm -f krb5/context_time.$(OBJEXT)
- -rm -f krb5/context_time.lo
- -rm -f krb5/copy_ccache.$(OBJEXT)
- -rm -f krb5/copy_ccache.lo
- -rm -f krb5/creds.$(OBJEXT)
- -rm -f krb5/creds.lo
- -rm -f krb5/decapsulate.$(OBJEXT)
- -rm -f krb5/decapsulate.lo
- -rm -f krb5/delete_sec_context.$(OBJEXT)
- -rm -f krb5/delete_sec_context.lo
- -rm -f krb5/display_name.$(OBJEXT)
- -rm -f krb5/display_name.lo
- -rm -f krb5/display_status.$(OBJEXT)
- -rm -f krb5/display_status.lo
- -rm -f krb5/duplicate_name.$(OBJEXT)
- -rm -f krb5/duplicate_name.lo
- -rm -f krb5/encapsulate.$(OBJEXT)
- -rm -f krb5/encapsulate.lo
- -rm -f krb5/export_name.$(OBJEXT)
- -rm -f krb5/export_name.lo
- -rm -f krb5/export_sec_context.$(OBJEXT)
- -rm -f krb5/export_sec_context.lo
- -rm -f krb5/external.$(OBJEXT)
- -rm -f krb5/external.lo
- -rm -f krb5/get_mic.$(OBJEXT)
- -rm -f krb5/get_mic.lo
- -rm -f krb5/import_name.$(OBJEXT)
- -rm -f krb5/import_name.lo
- -rm -f krb5/import_sec_context.$(OBJEXT)
- -rm -f krb5/import_sec_context.lo
- -rm -f krb5/indicate_mechs.$(OBJEXT)
- -rm -f krb5/indicate_mechs.lo
- -rm -f krb5/init.$(OBJEXT)
- -rm -f krb5/init.lo
- -rm -f krb5/init_sec_context.$(OBJEXT)
- -rm -f krb5/init_sec_context.lo
- -rm -f krb5/inquire_context.$(OBJEXT)
- -rm -f krb5/inquire_context.lo
- -rm -f krb5/inquire_cred.$(OBJEXT)
- -rm -f krb5/inquire_cred.lo
- -rm -f krb5/inquire_cred_by_mech.$(OBJEXT)
- -rm -f krb5/inquire_cred_by_mech.lo
- -rm -f krb5/inquire_cred_by_oid.$(OBJEXT)
- -rm -f krb5/inquire_cred_by_oid.lo
- -rm -f krb5/inquire_mechs_for_name.$(OBJEXT)
- -rm -f krb5/inquire_mechs_for_name.lo
- -rm -f krb5/inquire_names_for_mech.$(OBJEXT)
- -rm -f krb5/inquire_names_for_mech.lo
- -rm -f krb5/inquire_sec_context_by_oid.$(OBJEXT)
- -rm -f krb5/inquire_sec_context_by_oid.lo
- -rm -f krb5/pname_to_uid.$(OBJEXT)
- -rm -f krb5/pname_to_uid.lo
- -rm -f krb5/prf.$(OBJEXT)
- -rm -f krb5/prf.lo
- -rm -f krb5/process_context_token.$(OBJEXT)
- -rm -f krb5/process_context_token.lo
- -rm -f krb5/release_buffer.$(OBJEXT)
- -rm -f krb5/release_buffer.lo
- -rm -f krb5/release_cred.$(OBJEXT)
- -rm -f krb5/release_cred.lo
- -rm -f krb5/release_name.$(OBJEXT)
- -rm -f krb5/release_name.lo
- -rm -f krb5/sequence.$(OBJEXT)
- -rm -f krb5/sequence.lo
- -rm -f krb5/set_cred_option.$(OBJEXT)
- -rm -f krb5/set_cred_option.lo
- -rm -f krb5/set_sec_context_option.$(OBJEXT)
- -rm -f krb5/set_sec_context_option.lo
- -rm -f krb5/store_cred.$(OBJEXT)
- -rm -f krb5/store_cred.lo
- -rm -f krb5/test_cfx.$(OBJEXT)
- -rm -f krb5/ticket_flags.$(OBJEXT)
- -rm -f krb5/ticket_flags.lo
- -rm -f krb5/unwrap.$(OBJEXT)
- -rm -f krb5/unwrap.lo
- -rm -f krb5/verify_mic.$(OBJEXT)
- -rm -f krb5/verify_mic.lo
- -rm -f krb5/wrap.$(OBJEXT)
- -rm -f krb5/wrap.lo
- -rm -f mech/context.$(OBJEXT)
- -rm -f mech/context.lo
- -rm -f mech/doxygen.$(OBJEXT)
- -rm -f mech/doxygen.lo
- -rm -f mech/gss_accept_sec_context.$(OBJEXT)
- -rm -f mech/gss_accept_sec_context.lo
- -rm -f mech/gss_acquire_cred.$(OBJEXT)
- -rm -f mech/gss_acquire_cred.lo
- -rm -f mech/gss_acquire_cred_ext.$(OBJEXT)
- -rm -f mech/gss_acquire_cred_ext.lo
- -rm -f mech/gss_acquire_cred_with_password.$(OBJEXT)
- -rm -f mech/gss_acquire_cred_with_password.lo
- -rm -f mech/gss_add_cred.$(OBJEXT)
- -rm -f mech/gss_add_cred.lo
- -rm -f mech/gss_add_cred_with_password.$(OBJEXT)
- -rm -f mech/gss_add_cred_with_password.lo
- -rm -f mech/gss_add_oid_set_member.$(OBJEXT)
- -rm -f mech/gss_add_oid_set_member.lo
- -rm -f mech/gss_aeap.$(OBJEXT)
- -rm -f mech/gss_aeap.lo
- -rm -f mech/gss_authorize_localname.$(OBJEXT)
- -rm -f mech/gss_authorize_localname.lo
- -rm -f mech/gss_buffer_set.$(OBJEXT)
- -rm -f mech/gss_buffer_set.lo
- -rm -f mech/gss_canonicalize_name.$(OBJEXT)
- -rm -f mech/gss_canonicalize_name.lo
- -rm -f mech/gss_compare_name.$(OBJEXT)
- -rm -f mech/gss_compare_name.lo
- -rm -f mech/gss_context_time.$(OBJEXT)
- -rm -f mech/gss_context_time.lo
- -rm -f mech/gss_create_empty_oid_set.$(OBJEXT)
- -rm -f mech/gss_create_empty_oid_set.lo
- -rm -f mech/gss_cred.$(OBJEXT)
- -rm -f mech/gss_cred.lo
- -rm -f mech/gss_decapsulate_token.$(OBJEXT)
- -rm -f mech/gss_decapsulate_token.lo
- -rm -f mech/gss_delete_name_attribute.$(OBJEXT)
- -rm -f mech/gss_delete_name_attribute.lo
- -rm -f mech/gss_delete_sec_context.$(OBJEXT)
- -rm -f mech/gss_delete_sec_context.lo
- -rm -f mech/gss_display_name.$(OBJEXT)
- -rm -f mech/gss_display_name.lo
- -rm -f mech/gss_display_name_ext.$(OBJEXT)
- -rm -f mech/gss_display_name_ext.lo
- -rm -f mech/gss_display_status.$(OBJEXT)
- -rm -f mech/gss_display_status.lo
- -rm -f mech/gss_duplicate_name.$(OBJEXT)
- -rm -f mech/gss_duplicate_name.lo
- -rm -f mech/gss_duplicate_oid.$(OBJEXT)
- -rm -f mech/gss_duplicate_oid.lo
- -rm -f mech/gss_encapsulate_token.$(OBJEXT)
- -rm -f mech/gss_encapsulate_token.lo
- -rm -f mech/gss_export_name.$(OBJEXT)
- -rm -f mech/gss_export_name.lo
- -rm -f mech/gss_export_name_composite.$(OBJEXT)
- -rm -f mech/gss_export_name_composite.lo
- -rm -f mech/gss_export_sec_context.$(OBJEXT)
- -rm -f mech/gss_export_sec_context.lo
- -rm -f mech/gss_get_mic.$(OBJEXT)
- -rm -f mech/gss_get_mic.lo
- -rm -f mech/gss_get_name_attribute.$(OBJEXT)
- -rm -f mech/gss_get_name_attribute.lo
- -rm -f mech/gss_import_name.$(OBJEXT)
- -rm -f mech/gss_import_name.lo
- -rm -f mech/gss_import_sec_context.$(OBJEXT)
- -rm -f mech/gss_import_sec_context.lo
- -rm -f mech/gss_indicate_mechs.$(OBJEXT)
- -rm -f mech/gss_indicate_mechs.lo
- -rm -f mech/gss_init_sec_context.$(OBJEXT)
- -rm -f mech/gss_init_sec_context.lo
- -rm -f mech/gss_inquire_context.$(OBJEXT)
- -rm -f mech/gss_inquire_context.lo
- -rm -f mech/gss_inquire_cred.$(OBJEXT)
- -rm -f mech/gss_inquire_cred.lo
- -rm -f mech/gss_inquire_cred_by_mech.$(OBJEXT)
- -rm -f mech/gss_inquire_cred_by_mech.lo
- -rm -f mech/gss_inquire_cred_by_oid.$(OBJEXT)
- -rm -f mech/gss_inquire_cred_by_oid.lo
- -rm -f mech/gss_inquire_mechs_for_name.$(OBJEXT)
- -rm -f mech/gss_inquire_mechs_for_name.lo
- -rm -f mech/gss_inquire_name.$(OBJEXT)
- -rm -f mech/gss_inquire_name.lo
- -rm -f mech/gss_inquire_names_for_mech.$(OBJEXT)
- -rm -f mech/gss_inquire_names_for_mech.lo
- -rm -f mech/gss_inquire_sec_context_by_oid.$(OBJEXT)
- -rm -f mech/gss_inquire_sec_context_by_oid.lo
- -rm -f mech/gss_krb5.$(OBJEXT)
- -rm -f mech/gss_krb5.lo
- -rm -f mech/gss_mech_switch.$(OBJEXT)
- -rm -f mech/gss_mech_switch.lo
- -rm -f mech/gss_mo.$(OBJEXT)
- -rm -f mech/gss_mo.lo
- -rm -f mech/gss_names.$(OBJEXT)
- -rm -f mech/gss_names.lo
- -rm -f mech/gss_oid.$(OBJEXT)
- -rm -f mech/gss_oid.lo
- -rm -f mech/gss_oid_equal.$(OBJEXT)
- -rm -f mech/gss_oid_equal.lo
- -rm -f mech/gss_oid_to_str.$(OBJEXT)
- -rm -f mech/gss_oid_to_str.lo
- -rm -f mech/gss_pname_to_uid.$(OBJEXT)
- -rm -f mech/gss_pname_to_uid.lo
- -rm -f mech/gss_process_context_token.$(OBJEXT)
- -rm -f mech/gss_process_context_token.lo
- -rm -f mech/gss_pseudo_random.$(OBJEXT)
- -rm -f mech/gss_pseudo_random.lo
- -rm -f mech/gss_release_buffer.$(OBJEXT)
- -rm -f mech/gss_release_buffer.lo
- -rm -f mech/gss_release_cred.$(OBJEXT)
- -rm -f mech/gss_release_cred.lo
- -rm -f mech/gss_release_name.$(OBJEXT)
- -rm -f mech/gss_release_name.lo
- -rm -f mech/gss_release_oid.$(OBJEXT)
- -rm -f mech/gss_release_oid.lo
- -rm -f mech/gss_release_oid_set.$(OBJEXT)
- -rm -f mech/gss_release_oid_set.lo
- -rm -f mech/gss_seal.$(OBJEXT)
- -rm -f mech/gss_seal.lo
- -rm -f mech/gss_set_cred_option.$(OBJEXT)
- -rm -f mech/gss_set_cred_option.lo
- -rm -f mech/gss_set_name_attribute.$(OBJEXT)
- -rm -f mech/gss_set_name_attribute.lo
- -rm -f mech/gss_set_sec_context_option.$(OBJEXT)
- -rm -f mech/gss_set_sec_context_option.lo
- -rm -f mech/gss_sign.$(OBJEXT)
- -rm -f mech/gss_sign.lo
- -rm -f mech/gss_store_cred.$(OBJEXT)
- -rm -f mech/gss_store_cred.lo
- -rm -f mech/gss_test_oid_set_member.$(OBJEXT)
- -rm -f mech/gss_test_oid_set_member.lo
- -rm -f mech/gss_unseal.$(OBJEXT)
- -rm -f mech/gss_unseal.lo
- -rm -f mech/gss_unwrap.$(OBJEXT)
- -rm -f mech/gss_unwrap.lo
- -rm -f mech/gss_utils.$(OBJEXT)
- -rm -f mech/gss_utils.lo
- -rm -f mech/gss_verify.$(OBJEXT)
- -rm -f mech/gss_verify.lo
- -rm -f mech/gss_verify_mic.$(OBJEXT)
- -rm -f mech/gss_verify_mic.lo
- -rm -f mech/gss_wrap.$(OBJEXT)
- -rm -f mech/gss_wrap.lo
- -rm -f mech/gss_wrap_size_limit.$(OBJEXT)
- -rm -f mech/gss_wrap_size_limit.lo
- -rm -f ntlm/accept_sec_context.$(OBJEXT)
- -rm -f ntlm/accept_sec_context.lo
- -rm -f ntlm/acquire_cred.$(OBJEXT)
- -rm -f ntlm/acquire_cred.lo
- -rm -f ntlm/add_cred.$(OBJEXT)
- -rm -f ntlm/add_cred.lo
- -rm -f ntlm/canonicalize_name.$(OBJEXT)
- -rm -f ntlm/canonicalize_name.lo
- -rm -f ntlm/compare_name.$(OBJEXT)
- -rm -f ntlm/compare_name.lo
- -rm -f ntlm/context_time.$(OBJEXT)
- -rm -f ntlm/context_time.lo
- -rm -f ntlm/creds.$(OBJEXT)
- -rm -f ntlm/creds.lo
- -rm -f ntlm/crypto.$(OBJEXT)
- -rm -f ntlm/crypto.lo
- -rm -f ntlm/delete_sec_context.$(OBJEXT)
- -rm -f ntlm/delete_sec_context.lo
- -rm -f ntlm/display_name.$(OBJEXT)
- -rm -f ntlm/display_name.lo
- -rm -f ntlm/display_status.$(OBJEXT)
- -rm -f ntlm/display_status.lo
- -rm -f ntlm/duplicate_name.$(OBJEXT)
- -rm -f ntlm/duplicate_name.lo
- -rm -f ntlm/export_name.$(OBJEXT)
- -rm -f ntlm/export_name.lo
- -rm -f ntlm/export_sec_context.$(OBJEXT)
- -rm -f ntlm/export_sec_context.lo
- -rm -f ntlm/external.$(OBJEXT)
- -rm -f ntlm/external.lo
- -rm -f ntlm/import_name.$(OBJEXT)
- -rm -f ntlm/import_name.lo
- -rm -f ntlm/import_sec_context.$(OBJEXT)
- -rm -f ntlm/import_sec_context.lo
- -rm -f ntlm/indicate_mechs.$(OBJEXT)
- -rm -f ntlm/indicate_mechs.lo
- -rm -f ntlm/init_sec_context.$(OBJEXT)
- -rm -f ntlm/init_sec_context.lo
- -rm -f ntlm/inquire_context.$(OBJEXT)
- -rm -f ntlm/inquire_context.lo
- -rm -f ntlm/inquire_cred_by_mech.$(OBJEXT)
- -rm -f ntlm/inquire_cred_by_mech.lo
- -rm -f ntlm/inquire_mechs_for_name.$(OBJEXT)
- -rm -f ntlm/inquire_mechs_for_name.lo
- -rm -f ntlm/inquire_names_for_mech.$(OBJEXT)
- -rm -f ntlm/inquire_names_for_mech.lo
- -rm -f ntlm/inquire_sec_context_by_oid.$(OBJEXT)
- -rm -f ntlm/inquire_sec_context_by_oid.lo
- -rm -f ntlm/iter_cred.$(OBJEXT)
- -rm -f ntlm/iter_cred.lo
- -rm -f ntlm/kdc.$(OBJEXT)
- -rm -f ntlm/kdc.lo
- -rm -f ntlm/process_context_token.$(OBJEXT)
- -rm -f ntlm/process_context_token.lo
- -rm -f ntlm/release_cred.$(OBJEXT)
- -rm -f ntlm/release_cred.lo
- -rm -f ntlm/release_name.$(OBJEXT)
- -rm -f ntlm/release_name.lo
- -rm -f spnego/accept_sec_context.$(OBJEXT)
- -rm -f spnego/accept_sec_context.lo
- -rm -f spnego/compat.$(OBJEXT)
- -rm -f spnego/compat.lo
- -rm -f spnego/context_stubs.$(OBJEXT)
- -rm -f spnego/context_stubs.lo
- -rm -f spnego/cred_stubs.$(OBJEXT)
- -rm -f spnego/cred_stubs.lo
- -rm -f spnego/external.$(OBJEXT)
- -rm -f spnego/external.lo
- -rm -f spnego/init_sec_context.$(OBJEXT)
- -rm -f spnego/init_sec_context.lo
+ -rm -f krb5/*.$(OBJEXT)
+ -rm -f krb5/*.lo
+ -rm -f mech/*.$(OBJEXT)
+ -rm -f mech/*.lo
+ -rm -f ntlm/*.$(OBJEXT)
+ -rm -f ntlm/*.lo
+ -rm -f spnego/*.$(OBJEXT)
+ -rm -f spnego/*.lo
distclean-compile:
-rm -f *.tab.c
@@ -1643,6 +1659,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gss-commands.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gsstool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_acquire_cred.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_add_store_cred.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_common.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_context.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cred.Po@am__quote@
@@ -1812,28 +1829,28 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@spnego/$(DEPDIR)/init_sec_context.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -1846,11 +1863,18 @@ clean-libtool:
-rm -rf spnego/.libs spnego/_libs
install-man3: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man3dir)" || $(MKDIR_P) "$(DESTDIR)$(man3dir)"
- @list=''; test -n "$(man3dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.3[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.3[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -1879,16 +1903,21 @@ uninstall-man3:
sed -n '/\.3[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man3dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man3dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
install-man5: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)"
- @list=''; test -n "$(man5dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.5[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.5[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -1917,13 +1946,14 @@ uninstall-man5:
sed -n '/\.5[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man5dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1937,20 +1967,21 @@ uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nobase_includeHEADERS: $(nobase_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
$(am__nobase_list) | while read dir files; do \
xfiles=; for file in $$files; do \
if test -f "$$file"; then xfiles="$$xfiles $$file"; \
else xfiles="$$xfiles $(srcdir)/$$file"; fi; done; \
test -z "$$xfiles" || { \
test "x$$dir" = x. || { \
- echo "$(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)/$$dir'"; \
$(MKDIR_P) "$(DESTDIR)$(includedir)/$$dir"; }; \
echo " $(INSTALL_HEADER) $$xfiles '$(DESTDIR)$(includedir)/$$dir'"; \
$(INSTALL_HEADER) $$xfiles "$(DESTDIR)$(includedir)/$$dir" || exit $$?; }; \
@@ -1960,13 +1991,14 @@ uninstall-nobase_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nobase_include_HEADERS)'; test -n "$(includedir)" || list=; \
$(am__nobase_strip_setup); files=`$(am__nobase_strip)`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_gssapiHEADERS: $(nodist_gssapi_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(gssapidir)" || $(MKDIR_P) "$(DESTDIR)$(gssapidir)"
@list='$(nodist_gssapi_HEADERS)'; test -n "$(gssapidir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(gssapidir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(gssapidir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1980,30 +2012,17 @@ uninstall-nodist_gssapiHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_gssapi_HEADERS)'; test -n "$(gssapidir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(gssapidir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(gssapidir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(gssapidir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -2015,15 +2034,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -2032,116 +2047,203 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test_oid.log: test_oid$(EXEEXT)
+ @p='test_oid$(EXEEXT)'; \
+ b='test_oid'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_names.log: test_names$(EXEEXT)
+ @p='test_names$(EXEEXT)'; \
+ b='test_names'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_cfx.log: test_cfx$(EXEEXT)
+ @p='test_cfx$(EXEEXT)'; \
+ b='test_cfx'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
- @list='$(MANS)'; if test -n "$$list"; then \
- list=`for p in $$list; do \
- if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
- if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
- if test -n "$$list" && \
- grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
- echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
- grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
- echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
- echo " typically \`make maintainer-clean' will remove them" >&2; \
- exit 1; \
- else :; fi; \
- else :; fi
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -2198,11 +2300,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -2255,9 +2365,9 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-binPROGRAMS install-exec-local \
+ install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -2304,17 +2414,18 @@ uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \
uninstall-man: uninstall-man3 uninstall-man5
.MAKE: all check check-am install install-am install-data-am \
- install-exec-am install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-binPROGRAMS clean-checkPROGRAMS \
- clean-generic clean-libLTLIBRARIES clean-libtool \
- clean-noinstPROGRAMS ctags dist-hook distclean \
- distclean-compile distclean-generic distclean-libtool \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-binPROGRAMS install-data \
- install-data-am install-data-hook install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
+ install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-binPROGRAMS \
+ clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-binPROGRAMS install-data install-data-am \
+ install-data-hook install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-local install-html \
install-html-am install-includeHEADERS install-info \
install-info-am install-libLTLIBRARIES install-man \
install-man3 install-man5 install-nobase_includeHEADERS \
@@ -2323,27 +2434,41 @@ uninstall-man: uninstall-man3 uninstall-man5
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-binPROGRAMS \
- uninstall-hook uninstall-includeHEADERS \
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-hook uninstall-includeHEADERS \
uninstall-libLTLIBRARIES uninstall-man uninstall-man3 \
uninstall-man5 uninstall-nobase_includeHEADERS \
uninstall-nodist_gssapiHEADERS
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
-install-exec-hook: install-suid-programs
+install-exec-local: install-suid-programs
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -2351,7 +2476,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -2408,6 +2533,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -2450,6 +2577,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -2463,13 +2603,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -2500,7 +2640,7 @@ distdir-in-tree: $(DISTFILES) $(INFO_DEPS)
fi ; \
done
-$(srcdir)/ntlm/ntlm-private.h:
+$(srcdir)/ntlm/ntlm-private.h: $(ntlmsrc)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p ntlm/ntlm-private.h $(ntlmsrc) || rm -f ntlm/ntlm-private.h
$(libgssapi_la_OBJECTS): $(BUILTHEADERS)
@@ -2508,6 +2648,9 @@ $(test_context_OBJECTS): $(BUILTHEADERS)
$(libgssapi_la_OBJECTS): $(srcdir)/version-script.map
+$(libgssapi_la_OBJECTS): gkrb5_err.h
+gkrb5_err.h: $(srcdir)/krb5/gkrb5_err.et
+
$(spnego_files) spnego_asn1.hx spnego_asn1-priv.hx: spnego_asn1_files
$(gssapi_files) gssapi_asn1.hx gssapi_asn1-priv.hx: gssapi_asn1_files
diff --git a/lib/gssapi/NTMakefile b/lib/gssapi/NTMakefile
index ecf44ba4d856..402b110a1b24 100644
--- a/lib/gssapi/NTMakefile
+++ b/lib/gssapi/NTMakefile
@@ -553,6 +553,7 @@ all-tools:: $(BINDIR)\gsstool.exe
$(BINDIR)\gsstool.exe: $(OBJ)\gsstool.obj $(OBJ)\gss-commands.obj $(LIBGSSAPI) $(LIBROKEN) $(LIBSL) $(LIBVERS)
$(EXECONLINK)
+ $(EXEPREP)
$(OBJ)\gss-commands.c $(OBJ)\gss-commands.h: gss-commands.in
cd $(OBJ)
@@ -630,10 +631,10 @@ test-binaries: $(LIBGSSAPI) $(TEST_BINARIES)
run-test:
cd $(OBJ)
- test_oid
- test_names
- test_cfx
- test_kcred
+ -test_oid
+ -test_names
+ -test_cfx
+ -test_kcred
cd $(SRCDIR)
test:: test-binaries run-test
diff --git a/lib/gssapi/gen-oid.pl b/lib/gssapi/gen-oid.pl
new file mode 100755
index 000000000000..a2341ad9816d
--- /dev/null
+++ b/lib/gssapi/gen-oid.pl
@@ -0,0 +1,141 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 2010 Kungliga Tekniska Högskolan
+# (Royal Institute of Technology, Stockholm, Sweden).
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# 3. Neither the name of the Institute nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+use Getopt::Std;
+
+my $output;
+my $CFILE, $HFILE;
+my $onlybase;
+my $header = 0;
+
+getopts('b:h') || die "USAGE: ./gen-oid [-b BASE] [-h HEADER]";
+
+if($opt_b) {
+ $onlybase = $opt_b;
+}
+
+$header = 1 if ($opt_h);
+
+printf "/* Generated file */\n";
+if ($header) {
+ printf "#ifndef GSSAPI_GSSAPI_OID\n";
+ printf "#define GSSAPI_GSSAPI_OID 1\n\n";
+} else {
+ printf "#include \"mech_locl.h\"\n\n";
+}
+
+my %tables;
+my %types;
+
+while(<>) {
+
+ if (/^\w*#(.*)/) {
+ my $comment = $1;
+
+ if ($header) {
+ printf("$comment\n");
+ }
+
+ } elsif (/^oid\s+([\w\.]+)\s+(\w+)\s+([\w\.]+)/) {
+ my ($base, $name, $oid) = ($1, $2, $3);
+
+ next if (defined $onlybase and $onlybase ne $base);
+
+ my $store = "__" . lc($name) . "_oid_desc";
+
+ # encode oid
+
+ my @array = split(/\./, $oid);
+ my $length = 0;
+ my $data = "";
+
+ my $num;
+
+ $n = $#array;
+ while ($n > 1) {
+ $num = $array[$n];
+
+ my $p = int($num % 128);
+ $data = sprintf("\\x%02x", $p) . $data;
+
+ $num = int($num / 128);
+
+ $length += 1;
+
+ while ($num > 0) {
+ $p = int($num % 128) + 128;
+ $num = int($num / 128);
+ $data = sprintf("\\x%02x", $p) . $data;
+ $length += 1;
+ }
+ $n--;
+ }
+ $num = int($array[0] * 40 + $array[1]);
+
+ $data = sprintf("\\x%x", $num) . $data;
+ $length += 1;
+
+ if ($header) {
+ printf "extern GSSAPI_LIB_VARIABLE gss_OID_desc $store;\n";
+ printf "#define $name (&$store)\n\n";
+ } else {
+ printf "/* $name - $oid */\n";
+ printf "gss_OID_desc GSSAPI_LIB_VARIABLE $store = { $length, rk_UNCONST(\"$data\") };\n\n";
+ }
+ } elsif (/^desc\s+([\w]+)\s+(\w+)\s+(\"[^\"]*\")\s+(\"[^\"]*\")/) {
+ my ($type, $oid, $short, $long) = ($1, $2, $3, $4);
+ my $object = { type=> $type, oid => $oid, short => $short, long => $long };
+
+ $tables{$oid} = \$object;
+ $types{$type} = 1;
+ }
+
+}
+
+foreach my $k (sort keys %types) {
+ if (!$header) {
+ print "struct _gss_oid_name_table _gss_ont_" . $k . "[] = {\n";
+ foreach my $m (sort {$$a->{oid} cmp $$b->{oid}} values %tables) {
+ if ($$m->{type} eq $k) {
+ printf " { %s, \"%s\", %s, %s },\n", $$m->{oid}, $$m->{oid}, $$m->{short}, $$m->{long};
+ }
+ }
+ printf " { NULL, NULL, NULL, NULL }\n";
+ printf "};\n\n";
+
+ }
+}
+
+if ($header) {
+ printf "#endif /* GSSAPI_GSSAPI_OID */\n";
+}
diff --git a/lib/gssapi/gss-commands.in b/lib/gssapi/gss-commands.in
index a2fc2288ee43..25ec1c802cc5 100644
--- a/lib/gssapi/gss-commands.in
+++ b/lib/gssapi/gss-commands.in
@@ -33,10 +33,12 @@
/* $Id$ */
command = {
+ name = "mechanisms"
name = "supported-mechanisms"
help = "Print the supported mechanisms"
}
command = {
+ name = "attributes"
name = "attrs-for-mech"
help = "Print the attributes for mechs"
option = {
diff --git a/lib/gssapi/gss_acquire_cred.3 b/lib/gssapi/gss_acquire_cred.3
index 25d7b4d7ffdb..36b1f4e750c3 100644
--- a/lib/gssapi/gss_acquire_cred.3
+++ b/lib/gssapi/gss_acquire_cred.3
@@ -91,7 +91,7 @@ GSS-API library (libgssapi, -lgssapi)
.Fo gss_accept_sec_context
.Fa "OM_uint32 * minor_status"
.Fa "gss_ctx_id_t * context_handle"
-.Fa "const gss_cred_id_t acceptor_cred_handle"
+.Fa "gss_const_cred_id_t acceptor_cred_handle"
.Fa "const gss_buffer_t input_token_buffer"
.Fa "const gss_channel_bindings_t input_chan_bindings"
.Fa "gss_name_t * src_name"
@@ -105,7 +105,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_acquire_cred
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_name_t desired_name"
+.Fa "gss_const_name_t desired_name"
.Fa "OM_uint32 time_req"
.Fa "const gss_OID_set desired_mechs"
.Fa "gss_cred_usage_t cred_usage"
@@ -116,8 +116,8 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_add_cred
.Fa "OM_uint32 *minor_status"
-.Fa "const gss_cred_id_t input_cred_handle"
-.Fa "const gss_name_t desired_name"
+.Fa "gss_const_cred_id_t input_cred_handle"
+.Fa "gss_const_name_t desired_name"
.Fa "const gss_OID desired_mech"
.Fa "gss_cred_usage_t cred_usage"
.Fa "OM_uint32 initiator_time_req"
@@ -136,21 +136,21 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_canonicalize_name
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_name_t input_name"
+.Fa "gss_const_name_t input_name"
.Fa "const gss_OID mech_type"
.Fa "gss_name_t * output_name"
.Fc
.Ft OM_uint32
.Fo gss_compare_name
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_name_t name1"
-.Fa "const gss_name_t name2"
+.Fa "gss_const_name_t name1"
+.Fa "gss_const_name_t name2"
.Fa "int * name_equal"
.Fc
.Ft OM_uint32
.Fo gss_context_time
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "OM_uint32 * time_rec"
.Fc
.Ft OM_uint32
@@ -167,7 +167,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_display_name
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_name_t input_name"
+.Fa "gss_const_name_t input_name"
.Fa "gss_buffer_t output_name_buffer"
.Fa "gss_OID * output_name_type"
.Fc
@@ -183,13 +183,13 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_duplicate_name
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_name_t src_name"
+.Fa "gss_const_name_t src_name"
.Fa "gss_name_t * dest_name"
.Fc
.Ft OM_uint32
.Fo gss_export_name
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_name_t input_name"
+.Fa "gss_const_name_t input_name"
.Fa "gss_buffer_t exported_name"
.Fc
.Ft OM_uint32
@@ -201,7 +201,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_get_mic
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "gss_qop_t qop_req"
.Fa "const gss_buffer_t message_buffer"
.Fa "gss_buffer_t message_token"
@@ -227,9 +227,9 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_init_sec_context
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_cred_id_t initiator_cred_handle"
+.Fa "gss_const_cred_id_t initiator_cred_handle"
.Fa "gss_ctx_id_t * context_handle"
-.Fa "const gss_name_t target_name"
+.Fa "gss_const_name_t target_name"
.Fa "const gss_OID mech_type"
.Fa "OM_uint32 req_flags"
.Fa "OM_uint32 time_req"
@@ -243,7 +243,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_inquire_context
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "gss_name_t * src_name"
.Fa "gss_name_t * targ_name"
.Fa "OM_uint32 * lifetime_rec"
@@ -255,7 +255,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_inquire_cred
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_cred_id_t cred_handle"
+.Fa "gss_const_cred_id_t cred_handle"
.Fa "gss_name_t * name"
.Fa "OM_uint32 * lifetime"
.Fa "gss_cred_usage_t * cred_usage"
@@ -264,7 +264,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_inquire_cred_by_mech
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_cred_id_t cred_handle"
+.Fa "gss_const_cred_id_t cred_handle"
.Fa "const gss_OID mech_type"
.Fa "gss_name_t * name"
.Fa "OM_uint32 * initiator_lifetime"
@@ -274,7 +274,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_inquire_mechs_for_name
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_name_t input_name"
+.Fa "gss_const_name_t input_name"
.Fa "gss_OID_set * mech_types"
.Fc
.Ft OM_uint32
@@ -336,7 +336,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_process_context_token
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "const gss_buffer_t token_buffer"
.Fc
.Ft OM_uint32
@@ -396,7 +396,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_unwrap
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "const gss_buffer_t input_message_buffer"
.Fa "gss_buffer_t output_message_buffer"
.Fa "int * conf_state"
@@ -413,7 +413,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_verify_mic
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "const gss_buffer_t message_buffer"
.Fa "const gss_buffer_t token_buffer"
.Fa "gss_qop_t * qop_state"
@@ -421,7 +421,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_wrap
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "int conf_req_flag"
.Fa "gss_qop_t qop_req"
.Fa "const gss_buffer_t input_message_buffer"
@@ -431,7 +431,7 @@ GSS-API library (libgssapi, -lgssapi)
.Ft OM_uint32
.Fo gss_wrap_size_limit
.Fa "OM_uint32 * minor_status"
-.Fa "const gss_ctx_id_t context_handle"
+.Fa "gss_const_ctx_id_t context_handle"
.Fa "int conf_req_flag"
.Fa "gss_qop_t qop_req"
.Fa "OM_uint32 req_output_size"
@@ -554,7 +554,7 @@ the default syntax is used for all mechanism the GSS-API
implementation supports.
When compare the imported name of
.Dv GSS_C_NO_OID
-it may match serveral mechanism names (MN).
+it may match several mechanism names (MN).
.Pp
The resulting name from
.Fn gss_display_name
@@ -584,7 +584,7 @@ Information is available even after the context have expired.
.Fa lifetime_rec
argument is set to
.Dv GSS_C_INDEFINITE
-(dont expire) or the number of seconds that the context is still valid.
+(don't expire) or the number of seconds that the context is still valid.
A value of 0 means that the context is expired.
.Fa mech_type
argument should be considered readonly and must not be released.
diff --git a/lib/gssapi/gss_acquire_cred.cat3 b/lib/gssapi/gss_acquire_cred.cat3
index b793918c1850..bf95976ad1c3 100644
--- a/lib/gssapi/gss_acquire_cred.cat3
+++ b/lib/gssapi/gss_acquire_cred.cat3
@@ -28,7 +28,7 @@ SSYYNNOOPPSSIISS
_O_M___u_i_n_t_3_2
ggssss__aacccceepptt__sseecc__ccoonntteexxtt(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
_g_s_s___c_t_x___i_d___t _* _c_o_n_t_e_x_t___h_a_n_d_l_e,
- _c_o_n_s_t _g_s_s___c_r_e_d___i_d___t _a_c_c_e_p_t_o_r___c_r_e_d___h_a_n_d_l_e,
+ _g_s_s___c_o_n_s_t___c_r_e_d___i_d___t _a_c_c_e_p_t_o_r___c_r_e_d___h_a_n_d_l_e,
_c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _i_n_p_u_t___t_o_k_e_n___b_u_f_f_e_r,
_c_o_n_s_t _g_s_s___c_h_a_n_n_e_l___b_i_n_d_i_n_g_s___t _i_n_p_u_t___c_h_a_n___b_i_n_d_i_n_g_s,
_g_s_s___n_a_m_e___t _* _s_r_c___n_a_m_e, _g_s_s___O_I_D _* _m_e_c_h___t_y_p_e,
@@ -36,14 +36,14 @@ SSYYNNOOPPSSIISS
_O_M___u_i_n_t_3_2 _* _t_i_m_e___r_e_c, _g_s_s___c_r_e_d___i_d___t _* _d_e_l_e_g_a_t_e_d___c_r_e_d___h_a_n_d_l_e);
_O_M___u_i_n_t_3_2
- ggssss__aaccqquuiirree__ccrreedd(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___n_a_m_e___t _d_e_s_i_r_e_d___n_a_m_e,
+ ggssss__aaccqquuiirree__ccrreedd(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___n_a_m_e___t _d_e_s_i_r_e_d___n_a_m_e,
_O_M___u_i_n_t_3_2 _t_i_m_e___r_e_q, _c_o_n_s_t _g_s_s___O_I_D___s_e_t _d_e_s_i_r_e_d___m_e_c_h_s,
_g_s_s___c_r_e_d___u_s_a_g_e___t _c_r_e_d___u_s_a_g_e, _g_s_s___c_r_e_d___i_d___t _* _o_u_t_p_u_t___c_r_e_d___h_a_n_d_l_e,
_g_s_s___O_I_D___s_e_t _* _a_c_t_u_a_l___m_e_c_h_s, _O_M___u_i_n_t_3_2 _* _t_i_m_e___r_e_c);
_O_M___u_i_n_t_3_2
ggssss__aadddd__ccrreedd(_O_M___u_i_n_t_3_2 _*_m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_r_e_d___i_d___t _i_n_p_u_t___c_r_e_d___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___n_a_m_e___t _d_e_s_i_r_e_d___n_a_m_e,
+ _g_s_s___c_o_n_s_t___c_r_e_d___i_d___t _i_n_p_u_t___c_r_e_d___h_a_n_d_l_e, _g_s_s___c_o_n_s_t___n_a_m_e___t _d_e_s_i_r_e_d___n_a_m_e,
_c_o_n_s_t _g_s_s___O_I_D _d_e_s_i_r_e_d___m_e_c_h, _g_s_s___c_r_e_d___u_s_a_g_e___t _c_r_e_d___u_s_a_g_e,
_O_M___u_i_n_t_3_2 _i_n_i_t_i_a_t_o_r___t_i_m_e___r_e_q, _O_M___u_i_n_t_3_2 _a_c_c_e_p_t_o_r___t_i_m_e___r_e_q,
_g_s_s___c_r_e_d___i_d___t _*_o_u_t_p_u_t___c_r_e_d___h_a_n_d_l_e, _g_s_s___O_I_D___s_e_t _*_a_c_t_u_a_l___m_e_c_h_s,
@@ -55,16 +55,16 @@ SSYYNNOOPPSSIISS
_O_M___u_i_n_t_3_2
ggssss__ccaannoonniiccaalliizzee__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___n_a_m_e___t _i_n_p_u_t___n_a_m_e, _c_o_n_s_t _g_s_s___O_I_D _m_e_c_h___t_y_p_e,
+ _g_s_s___c_o_n_s_t___n_a_m_e___t _i_n_p_u_t___n_a_m_e, _c_o_n_s_t _g_s_s___O_I_D _m_e_c_h___t_y_p_e,
_g_s_s___n_a_m_e___t _* _o_u_t_p_u_t___n_a_m_e);
_O_M___u_i_n_t_3_2
- ggssss__ccoommppaarree__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___n_a_m_e___t _n_a_m_e_1,
- _c_o_n_s_t _g_s_s___n_a_m_e___t _n_a_m_e_2, _i_n_t _* _n_a_m_e___e_q_u_a_l);
+ ggssss__ccoommppaarree__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___n_a_m_e___t _n_a_m_e_1,
+ _g_s_s___c_o_n_s_t___n_a_m_e___t _n_a_m_e_2, _i_n_t _* _n_a_m_e___e_q_u_a_l);
_O_M___u_i_n_t_3_2
ggssss__ccoonntteexxtt__ttiimmee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _O_M___u_i_n_t_3_2 _* _t_i_m_e___r_e_c);
+ _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _O_M___u_i_n_t_3_2 _* _t_i_m_e___r_e_c);
_O_M___u_i_n_t_3_2
ggssss__ccrreeaattee__eemmppttyy__ooiidd__sseett(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
@@ -75,7 +75,7 @@ SSYYNNOOPPSSIISS
_g_s_s___c_t_x___i_d___t _* _c_o_n_t_e_x_t___h_a_n_d_l_e, _g_s_s___b_u_f_f_e_r___t _o_u_t_p_u_t___t_o_k_e_n);
_O_M___u_i_n_t_3_2
- ggssss__ddiissppllaayy__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___n_a_m_e___t _i_n_p_u_t___n_a_m_e,
+ ggssss__ddiissppllaayy__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___n_a_m_e___t _i_n_p_u_t___n_a_m_e,
_g_s_s___b_u_f_f_e_r___t _o_u_t_p_u_t___n_a_m_e___b_u_f_f_e_r, _g_s_s___O_I_D _* _o_u_t_p_u_t___n_a_m_e___t_y_p_e);
_O_M___u_i_n_t_3_2
@@ -84,11 +84,11 @@ SSYYNNOOPPSSIISS
_g_s_s___b_u_f_f_e_r___t _s_t_a_t_u_s___s_t_r_i_n_g);
_O_M___u_i_n_t_3_2
- ggssss__dduupplliiccaattee__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___n_a_m_e___t _s_r_c___n_a_m_e,
+ ggssss__dduupplliiccaattee__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___n_a_m_e___t _s_r_c___n_a_m_e,
_g_s_s___n_a_m_e___t _* _d_e_s_t___n_a_m_e);
_O_M___u_i_n_t_3_2
- ggssss__eexxppoorrtt__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___n_a_m_e___t _i_n_p_u_t___n_a_m_e,
+ ggssss__eexxppoorrtt__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___n_a_m_e___t _i_n_p_u_t___n_a_m_e,
_g_s_s___b_u_f_f_e_r___t _e_x_p_o_r_t_e_d___n_a_m_e);
_O_M___u_i_n_t_3_2
@@ -96,7 +96,7 @@ SSYYNNOOPPSSIISS
_g_s_s___c_t_x___i_d___t _* _c_o_n_t_e_x_t___h_a_n_d_l_e, _g_s_s___b_u_f_f_e_r___t _i_n_t_e_r_p_r_o_c_e_s_s___t_o_k_e_n);
_O_M___u_i_n_t_3_2
- ggssss__ggeett__mmiicc(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e,
+ ggssss__ggeett__mmiicc(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e,
_g_s_s___q_o_p___t _q_o_p___r_e_q, _c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _m_e_s_s_a_g_e___b_u_f_f_e_r,
_g_s_s___b_u_f_f_e_r___t _m_e_s_s_a_g_e___t_o_k_e_n);
@@ -115,8 +115,8 @@ SSYYNNOOPPSSIISS
_O_M___u_i_n_t_3_2
ggssss__iinniitt__sseecc__ccoonntteexxtt(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_r_e_d___i_d___t _i_n_i_t_i_a_t_o_r___c_r_e_d___h_a_n_d_l_e,
- _g_s_s___c_t_x___i_d___t _* _c_o_n_t_e_x_t___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___n_a_m_e___t _t_a_r_g_e_t___n_a_m_e,
+ _g_s_s___c_o_n_s_t___c_r_e_d___i_d___t _i_n_i_t_i_a_t_o_r___c_r_e_d___h_a_n_d_l_e,
+ _g_s_s___c_t_x___i_d___t _* _c_o_n_t_e_x_t___h_a_n_d_l_e, _g_s_s___c_o_n_s_t___n_a_m_e___t _t_a_r_g_e_t___n_a_m_e,
_c_o_n_s_t _g_s_s___O_I_D _m_e_c_h___t_y_p_e, _O_M___u_i_n_t_3_2 _r_e_q___f_l_a_g_s, _O_M___u_i_n_t_3_2 _t_i_m_e___r_e_q,
_c_o_n_s_t _g_s_s___c_h_a_n_n_e_l___b_i_n_d_i_n_g_s___t _i_n_p_u_t___c_h_a_n___b_i_n_d_i_n_g_s,
_c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _i_n_p_u_t___t_o_k_e_n, _g_s_s___O_I_D _* _a_c_t_u_a_l___m_e_c_h___t_y_p_e,
@@ -125,26 +125,26 @@ SSYYNNOOPPSSIISS
_O_M___u_i_n_t_3_2
ggssss__iinnqquuiirree__ccoonntteexxtt(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _g_s_s___n_a_m_e___t _* _s_r_c___n_a_m_e,
+ _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _g_s_s___n_a_m_e___t _* _s_r_c___n_a_m_e,
_g_s_s___n_a_m_e___t _* _t_a_r_g___n_a_m_e, _O_M___u_i_n_t_3_2 _* _l_i_f_e_t_i_m_e___r_e_c,
_g_s_s___O_I_D _* _m_e_c_h___t_y_p_e, _O_M___u_i_n_t_3_2 _* _c_t_x___f_l_a_g_s, _i_n_t _* _l_o_c_a_l_l_y___i_n_i_t_i_a_t_e_d,
_i_n_t _* _o_p_e_n___c_o_n_t_e_x_t);
_O_M___u_i_n_t_3_2
ggssss__iinnqquuiirree__ccrreedd(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_r_e_d___i_d___t _c_r_e_d___h_a_n_d_l_e, _g_s_s___n_a_m_e___t _* _n_a_m_e,
+ _g_s_s___c_o_n_s_t___c_r_e_d___i_d___t _c_r_e_d___h_a_n_d_l_e, _g_s_s___n_a_m_e___t _* _n_a_m_e,
_O_M___u_i_n_t_3_2 _* _l_i_f_e_t_i_m_e, _g_s_s___c_r_e_d___u_s_a_g_e___t _* _c_r_e_d___u_s_a_g_e,
_g_s_s___O_I_D___s_e_t _* _m_e_c_h_a_n_i_s_m_s);
_O_M___u_i_n_t_3_2
ggssss__iinnqquuiirree__ccrreedd__bbyy__mmeecchh(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_r_e_d___i_d___t _c_r_e_d___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___O_I_D _m_e_c_h___t_y_p_e,
+ _g_s_s___c_o_n_s_t___c_r_e_d___i_d___t _c_r_e_d___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___O_I_D _m_e_c_h___t_y_p_e,
_g_s_s___n_a_m_e___t _* _n_a_m_e, _O_M___u_i_n_t_3_2 _* _i_n_i_t_i_a_t_o_r___l_i_f_e_t_i_m_e,
_O_M___u_i_n_t_3_2 _* _a_c_c_e_p_t_o_r___l_i_f_e_t_i_m_e, _g_s_s___c_r_e_d___u_s_a_g_e___t _* _c_r_e_d___u_s_a_g_e);
_O_M___u_i_n_t_3_2
ggssss__iinnqquuiirree__mmeecchhss__ffoorr__nnaammee(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___n_a_m_e___t _i_n_p_u_t___n_a_m_e, _g_s_s___O_I_D___s_e_t _* _m_e_c_h___t_y_p_e_s);
+ _g_s_s___c_o_n_s_t___n_a_m_e___t _i_n_p_u_t___n_a_m_e, _g_s_s___O_I_D___s_e_t _* _m_e_c_h___t_y_p_e_s);
_O_M___u_i_n_t_3_2
ggssss__iinnqquuiirree__nnaammeess__ffoorr__mmeecchh(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
@@ -184,7 +184,7 @@ SSYYNNOOPPSSIISS
_O_M___u_i_n_t_3_2
ggssss__pprroocceessss__ccoonntteexxtt__ttookkeenn(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _t_o_k_e_n___b_u_f_f_e_r);
+ _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _t_o_k_e_n___b_u_f_f_e_r);
_O_M___u_i_n_t_3_2
ggssss__rreelleeaassee__bbuuffffeerr(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___b_u_f_f_e_r___t _b_u_f_f_e_r);
@@ -219,7 +219,7 @@ SSYYNNOOPPSSIISS
_i_n_t _* _q_o_p___s_t_a_t_e);
_O_M___u_i_n_t_3_2
- ggssss__uunnwwrraapp(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e,
+ ggssss__uunnwwrraapp(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e,
_c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _i_n_p_u_t___m_e_s_s_a_g_e___b_u_f_f_e_r,
_g_s_s___b_u_f_f_e_r___t _o_u_t_p_u_t___m_e_s_s_a_g_e___b_u_f_f_e_r, _i_n_t _* _c_o_n_f___s_t_a_t_e,
_g_s_s___q_o_p___t _* _q_o_p___s_t_a_t_e);
@@ -231,18 +231,18 @@ SSYYNNOOPPSSIISS
_O_M___u_i_n_t_3_2
ggssss__vveerriiffyy__mmiicc(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _m_e_s_s_a_g_e___b_u_f_f_e_r,
+ _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _m_e_s_s_a_g_e___b_u_f_f_e_r,
_c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _t_o_k_e_n___b_u_f_f_e_r, _g_s_s___q_o_p___t _* _q_o_p___s_t_a_t_e);
_O_M___u_i_n_t_3_2
- ggssss__wwrraapp(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e,
+ ggssss__wwrraapp(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s, _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e,
_i_n_t _c_o_n_f___r_e_q___f_l_a_g, _g_s_s___q_o_p___t _q_o_p___r_e_q,
_c_o_n_s_t _g_s_s___b_u_f_f_e_r___t _i_n_p_u_t___m_e_s_s_a_g_e___b_u_f_f_e_r, _i_n_t _* _c_o_n_f___s_t_a_t_e,
_g_s_s___b_u_f_f_e_r___t _o_u_t_p_u_t___m_e_s_s_a_g_e___b_u_f_f_e_r);
_O_M___u_i_n_t_3_2
ggssss__wwrraapp__ssiizzee__lliimmiitt(_O_M___u_i_n_t_3_2 _* _m_i_n_o_r___s_t_a_t_u_s,
- _c_o_n_s_t _g_s_s___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _i_n_t _c_o_n_f___r_e_q___f_l_a_g,
+ _g_s_s___c_o_n_s_t___c_t_x___i_d___t _c_o_n_t_e_x_t___h_a_n_d_l_e, _i_n_t _c_o_n_f___r_e_q___f_l_a_g,
_g_s_s___q_o_p___t _q_o_p___r_e_q, _O_M___u_i_n_t_3_2 _r_e_q___o_u_t_p_u_t___s_i_z_e,
_O_M___u_i_n_t_3_2 _* _m_a_x___i_n_p_u_t___s_i_z_e);
@@ -332,8 +332,8 @@ AACCCCEESSSS CCOONNTTRROOLL
For example, if ggssss__iimmppoorrtt__nnaammee() name was used with GSS_C_NO_OID the
default syntax is used for all mechanism the GSS-API implementation sup-
- ports. When compare the imported name of GSS_C_NO_OID it may match
- serveral mechanism names (MN).
+ ports. When compare the imported name of GSS_C_NO_OID it may match sev-
+ eral mechanism names (MN).
The resulting name from ggssss__ddiissppllaayy__nnaammee() must not be used for acccess
control.
@@ -348,7 +348,7 @@ FFUUNNCCTTIIOONNSS
ggssss__iinnqquuiirree__ccoonntteexxtt() returns information about the context. Information
is available even after the context have expired. _l_i_f_e_t_i_m_e___r_e_c argument
- is set to GSS_C_INDEFINITE (dont expire) or the number of seconds that
+ is set to GSS_C_INDEFINITE (don't expire) or the number of seconds that
the context is still valid. A value of 0 means that the context is
expired. _m_e_c_h___t_y_p_e argument should be considered readonly and must not
be released. _s_r_c___n_a_m_e and ddeesstt__nnaammee() are both mechanims names and must
diff --git a/lib/gssapi/gssapi/gssapi.h b/lib/gssapi/gssapi/gssapi.h
index bbb2fd54c9bd..1a128cbe4a57 100644
--- a/lib/gssapi/gssapi/gssapi.h
+++ b/lib/gssapi/gssapi/gssapi.h
@@ -95,7 +95,7 @@ typedef const struct gss_name_t_desc_struct *gss_const_name_t;
struct gss_ctx_id_t_desc_struct;
typedef struct gss_ctx_id_t_desc_struct *gss_ctx_id_t;
-typedef const struct gss_ctx_id_t_desc_struct gss_const_ctx_id_t;
+typedef const struct gss_ctx_id_t_desc_struct *gss_const_ctx_id_t;
typedef struct gss_OID_desc_struct {
OM_uint32 length;
@@ -470,7 +470,7 @@ extern GSSAPI_LIB_VARIABLE gss_OID_desc __gss_c_nt_export_name_oid_desc;
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_acquire_cred
(OM_uint32 * /*minor_status*/,
- const gss_name_t /*desired_name*/,
+ gss_const_name_t /*desired_name*/,
OM_uint32 /*time_req*/,
const gss_OID_set /*desired_mechs*/,
gss_cred_usage_t /*cred_usage*/,
@@ -486,9 +486,9 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_release_cred
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_init_sec_context
(OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*initiator_cred_handle*/,
+ gss_const_cred_id_t /*initiator_cred_handle*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_name_t /*target_name*/,
+ gss_const_name_t /*target_name*/,
const gss_OID /*mech_type*/,
OM_uint32 /*req_flags*/,
OM_uint32 /*time_req*/,
@@ -503,7 +503,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_init_sec_context
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_accept_sec_context
(OM_uint32 * /*minor_status*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_cred_id_t /*acceptor_cred_handle*/,
+ gss_const_cred_id_t /*acceptor_cred_handle*/,
const gss_buffer_t /*input_token_buffer*/,
const gss_channel_bindings_t /*input_chan_bindings*/,
gss_name_t * /*src_name*/,
@@ -516,7 +516,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_accept_sec_context
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_process_context_token
(OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*token_buffer*/
);
@@ -528,13 +528,13 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_delete_sec_context
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_context_time
(OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
OM_uint32 * /*time_rec*/
);
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_get_mic
(OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*message_buffer*/,
gss_buffer_t /*message_token*/
@@ -542,7 +542,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_get_mic
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_verify_mic
(OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*message_buffer*/,
const gss_buffer_t /*token_buffer*/,
gss_qop_t * /*qop_state*/
@@ -550,7 +550,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_verify_mic
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_wrap
(OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*input_message_buffer*/,
@@ -560,7 +560,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_wrap
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_unwrap
(OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*input_message_buffer*/,
gss_buffer_t /*output_message_buffer*/,
int * /*conf_state*/,
@@ -583,14 +583,14 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_indicate_mechs
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_compare_name
(OM_uint32 * /*minor_status*/,
- const gss_name_t /*name1*/,
- const gss_name_t /*name2*/,
+ gss_const_name_t /*name1*/,
+ gss_const_name_t /*name2*/,
int * /*name_equal*/
);
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_display_name
(OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t /*output_name_buffer*/,
gss_OID * /*output_name_type*/
);
@@ -604,7 +604,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_import_name
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_export_name
(OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t /*exported_name*/
);
@@ -625,7 +625,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_release_oid_set
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_inquire_cred
(OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
gss_name_t * /*name*/,
OM_uint32 * /*lifetime*/,
gss_cred_usage_t * /*cred_usage*/,
@@ -634,7 +634,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_inquire_cred
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_inquire_context (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_name_t * /*src_name*/,
gss_name_t * /*targ_name*/,
OM_uint32 * /*lifetime_rec*/,
@@ -646,7 +646,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_inquire_context (
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_wrap_size_limit (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
OM_uint32 /*req_output_size*/,
@@ -655,8 +655,8 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_wrap_size_limit (
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_add_cred (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*input_cred_handle*/,
- const gss_name_t /*desired_name*/,
+ gss_const_cred_id_t /*input_cred_handle*/,
+ gss_const_name_t /*desired_name*/,
const gss_OID /*desired_mech*/,
gss_cred_usage_t /*cred_usage*/,
OM_uint32 /*initiator_time_req*/,
@@ -669,7 +669,7 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_add_cred (
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_inquire_cred_by_mech (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
const gss_OID /*mech_type*/,
gss_name_t * /*name*/,
OM_uint32 * /*initiator_lifetime*/,
@@ -715,20 +715,20 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_inquire_names_for_mech (
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_inquire_mechs_for_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_OID_set * /*mech_types*/
);
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_canonicalize_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
const gss_OID /*mech_type*/,
gss_name_t * /*output_name*/
);
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_duplicate_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*src_name*/,
+ gss_const_name_t /*src_name*/,
gss_name_t * /*dest_name*/
);
@@ -754,7 +754,7 @@ gss_oid_to_str(
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_sec_context_by_oid(
OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set
);
@@ -792,7 +792,7 @@ gss_release_buffer_set
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_cred_by_oid(OM_uint32 *minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set);
@@ -844,7 +844,7 @@ extern gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_attr_stream_sizes_oid_desc;
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_context_query_attributes(OM_uint32 * /* minor_status */,
- const gss_ctx_id_t /* context_handle */,
+ gss_const_ctx_id_t /* context_handle */,
const gss_OID /* attribute */,
void * /*data*/,
size_t /* len */);
@@ -934,6 +934,13 @@ gss_wrap_iov_length(OM_uint32 *, gss_ctx_id_t, int, gss_qop_t, int *,
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_release_iov_buffer(OM_uint32 *, gss_iov_buffer_desc *, int);
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_wrap_aead(OM_uint32 *, gss_ctx_id_t, int, gss_qop_t,
+ gss_buffer_t, gss_buffer_t, int *, gss_buffer_t);
+
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_unwrap_aead(OM_uint32 *, gss_ctx_id_t, gss_buffer_t,
+ gss_buffer_t, gss_buffer_t, int *, gss_qop_t *);
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_export_cred(OM_uint32 * /* minor_status */,
@@ -1004,7 +1011,7 @@ gss_display_mech_attr(OM_uint32 * minor_status,
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_acquire_cred_with_password
(OM_uint32 * /*minor_status*/,
- const gss_name_t /*desired_name*/,
+ gss_const_name_t /*desired_name*/,
const gss_buffer_t /*password*/,
OM_uint32 /*time_req*/,
const gss_OID_set /*desired_mechs*/,
@@ -1016,8 +1023,8 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_acquire_cred_with_password
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_add_cred_with_password (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*input_cred_handle*/,
- const gss_name_t /*desired_name*/,
+ gss_const_cred_id_t /*input_cred_handle*/,
+ gss_const_name_t /*desired_name*/,
const gss_OID /*desired_mech*/,
const gss_buffer_t /*password*/,
gss_cred_usage_t /*cred_usage*/,
@@ -1030,20 +1037,27 @@ GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL gss_add_cred_with_password (
);
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_localname(
+ OM_uint32 *minor,
+ gss_const_name_t name,
+ const gss_OID mech_type,
+ gss_buffer_t localname);
+
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_pname_to_uid(
OM_uint32 *minor,
- const gss_name_t name,
+ gss_const_name_t name,
const gss_OID mech_type,
uid_t *uidOut);
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_authorize_localname(
OM_uint32 *minor,
- const gss_name_t name,
- const gss_name_t user);
+ gss_const_name_t name,
+ gss_const_name_t user);
GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL
-gss_userok(const gss_name_t name,
+gss_userok(gss_const_name_t name,
const char *user);
extern GSSAPI_LIB_VARIABLE gss_buffer_desc __gss_c_attr_local_login_user;
diff --git a/lib/gssapi/gssapi/gssapi_krb5.h b/lib/gssapi/gssapi/gssapi_krb5.h
index 2f605f5ee113..74d5109aa19c 100644
--- a/lib/gssapi/gssapi/gssapi_krb5.h
+++ b/lib/gssapi/gssapi/gssapi_krb5.h
@@ -37,6 +37,7 @@
#define GSSAPI_KRB5_H_
#include <gssapi.h>
+#include <gssapi/gssapi_oid.h>
GSSAPI_CPP_START
@@ -60,9 +61,6 @@ extern gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_nt_principal_name_oid_desc;
#define GSS_KRB5_NT_MACHINE_UID_NAME (&__gss_c_nt_machine_uid_name_oid_desc)
#define GSS_KRB5_NT_STRING_UID_NAME (&__gss_c_nt_string_uid_name_oid_desc)
-extern gss_OID_desc GSSAPI_LIB_VARIABLE __gss_krb5_mechanism_oid_desc;
-#define GSS_KRB5_MECHANISM (&__gss_krb5_mechanism_oid_desc)
-
/* for compatibility with MIT api */
#define gss_mech_krb5 GSS_KRB5_MECHANISM
diff --git a/lib/gssapi/gssapi/gssapi_netlogon.h b/lib/gssapi/gssapi/gssapi_netlogon.h
new file mode 100644
index 000000000000..52201a6ba978
--- /dev/null
+++ b/lib/gssapi/gssapi/gssapi_netlogon.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2006 - 2009 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef GSSAPI_NETLOGON_H_
+#define GSSAPI_NETLOGON_H_
+
+#include <gssapi.h>
+
+GSSAPI_CPP_START
+
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_NETLOGON_MECHANISM;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_NETLOGON_NT_NETBIOS_DNS_NAME;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_NETLOGON_SET_SESSION_KEY_X;
+extern GSSAPI_LIB_VARIABLE gss_OID GSS_NETLOGON_SET_SIGN_ALGORITHM_X;
+
+GSSAPI_CPP_END
+
+#endif /* GSSAPI_NETLOGON_H_ */
diff --git a/lib/gssapi/gssapi_mech.h b/lib/gssapi/gssapi_mech.h
index e4ccfdb0cd99..59fd04682937 100644
--- a/lib/gssapi/gssapi_mech.h
+++ b/lib/gssapi/gssapi_mech.h
@@ -33,7 +33,7 @@
typedef OM_uint32 GSSAPI_CALLCONV _gss_acquire_cred_t
(OM_uint32 *, /* minor_status */
- const gss_name_t, /* desired_name */
+ gss_const_name_t, /* desired_name */
OM_uint32, /* time_req */
const gss_OID_set, /* desired_mechs */
gss_cred_usage_t, /* cred_usage */
@@ -49,9 +49,9 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_release_cred_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_init_sec_context_t
(OM_uint32 *, /* minor_status */
- const gss_cred_id_t, /* initiator_cred_handle */
+ gss_const_cred_id_t, /* initiator_cred_handle */
gss_ctx_id_t *, /* context_handle */
- const gss_name_t, /* target_name */
+ gss_const_name_t, /* target_name */
const gss_OID, /* mech_type */
OM_uint32, /* req_flags */
OM_uint32, /* time_req */
@@ -67,7 +67,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_init_sec_context_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_accept_sec_context_t
(OM_uint32 *, /* minor_status */
gss_ctx_id_t *, /* context_handle */
- const gss_cred_id_t, /* acceptor_cred_handle */
+ gss_const_cred_id_t, /* acceptor_cred_handle */
const gss_buffer_t, /* input_token_buffer */
const gss_channel_bindings_t,
/* input_chan_bindings */
@@ -81,7 +81,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_accept_sec_context_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_process_context_token_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
const gss_buffer_t /* token_buffer */
);
@@ -93,13 +93,13 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_delete_sec_context_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_context_time_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
OM_uint32 * /* time_rec */
);
typedef OM_uint32 GSSAPI_CALLCONV _gss_get_mic_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
gss_qop_t, /* qop_req */
const gss_buffer_t, /* message_buffer */
gss_buffer_t /* message_token */
@@ -107,7 +107,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_get_mic_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_verify_mic_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
const gss_buffer_t, /* message_buffer */
const gss_buffer_t, /* token_buffer */
gss_qop_t * /* qop_state */
@@ -115,7 +115,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_verify_mic_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_wrap_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
int, /* conf_req_flag */
gss_qop_t, /* qop_req */
const gss_buffer_t, /* input_message_buffer */
@@ -125,7 +125,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_wrap_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_unwrap_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
const gss_buffer_t, /* input_message_buffer */
gss_buffer_t, /* output_message_buffer */
int *, /* conf_state */
@@ -148,14 +148,14 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_indicate_mechs_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_compare_name_t
(OM_uint32 *, /* minor_status */
- const gss_name_t, /* name1 */
- const gss_name_t, /* name2 */
+ gss_const_name_t, /* name1 */
+ gss_const_name_t, /* name2 */
int * /* name_equal */
);
typedef OM_uint32 GSSAPI_CALLCONV _gss_display_name_t
(OM_uint32 *, /* minor_status */
- const gss_name_t, /* input_name */
+ gss_const_name_t, /* input_name */
gss_buffer_t, /* output_name_buffer */
gss_OID * /* output_name_type */
);
@@ -169,7 +169,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_import_name_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_export_name_t
(OM_uint32 *, /* minor_status */
- const gss_name_t, /* input_name */
+ gss_const_name_t, /* input_name */
gss_buffer_t /* exported_name */
);
@@ -180,7 +180,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_release_name_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_cred_t
(OM_uint32 *, /* minor_status */
- const gss_cred_id_t, /* cred_handle */
+ gss_const_cred_id_t, /* cred_handle */
gss_name_t *, /* name */
OM_uint32 *, /* lifetime */
gss_cred_usage_t *, /* cred_usage */
@@ -189,7 +189,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_cred_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_context_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
gss_name_t *, /* src_name */
gss_name_t *, /* targ_name */
OM_uint32 *, /* lifetime_rec */
@@ -201,7 +201,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_context_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_wrap_size_limit_t
(OM_uint32 *, /* minor_status */
- const gss_ctx_id_t, /* context_handle */
+ gss_const_ctx_id_t, /* context_handle */
int, /* conf_req_flag */
gss_qop_t, /* qop_req */
OM_uint32, /* req_output_size */
@@ -210,8 +210,8 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_wrap_size_limit_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_add_cred_t (
OM_uint32 *, /* minor_status */
- const gss_cred_id_t, /* input_cred_handle */
- const gss_name_t, /* desired_name */
+ gss_const_cred_id_t, /* input_cred_handle */
+ gss_const_name_t, /* desired_name */
const gss_OID, /* desired_mech */
gss_cred_usage_t, /* cred_usage */
OM_uint32, /* initiator_time_req */
@@ -224,7 +224,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_add_cred_t (
typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_cred_by_mech_t (
OM_uint32 *, /* minor_status */
- const gss_cred_id_t, /* cred_handle */
+ gss_const_cred_id_t, /* cred_handle */
const gss_OID, /* mech_type */
gss_name_t *, /* name */
OM_uint32 *, /* initiator_lifetime */
@@ -252,33 +252,33 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_names_for_mech_t (
typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_mechs_for_name_t (
OM_uint32 *, /* minor_status */
- const gss_name_t, /* input_name */
+ gss_const_name_t, /* input_name */
gss_OID_set * /* mech_types */
);
typedef OM_uint32 GSSAPI_CALLCONV _gss_canonicalize_name_t (
OM_uint32 *, /* minor_status */
- const gss_name_t, /* input_name */
+ gss_const_name_t, /* input_name */
const gss_OID, /* mech_type */
gss_name_t * /* output_name */
);
typedef OM_uint32 GSSAPI_CALLCONV _gss_duplicate_name_t (
OM_uint32 *, /* minor_status */
- const gss_name_t, /* src_name */
+ gss_const_name_t, /* src_name */
gss_name_t * /* dest_name */
);
typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_sec_context_by_oid (
OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set
);
typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_cred_by_oid (
OM_uint32 *minor_status,
- const gss_cred_id_t cred,
+ gss_const_cred_id_t cred,
const gss_OID desired_object,
gss_buffer_set_t *data_set
);
@@ -356,7 +356,7 @@ _gss_import_cred_t(OM_uint32 * minor_status,
typedef OM_uint32 GSSAPI_CALLCONV
_gss_acquire_cred_ext_t(OM_uint32 * /*minor_status */,
- const gss_name_t /* desired_name */,
+ gss_const_name_t /* desired_name */,
gss_const_OID /* credential_type */,
const void * /* credential_data */,
OM_uint32 /* time_req */,
@@ -460,16 +460,16 @@ struct gss_mo_desc_struct {
int (*set)(gss_const_OID, gss_mo_desc *, int, gss_buffer_t);
};
-typedef OM_uint32 GSSAPI_CALLCONV _gss_pname_to_uid_t (
+typedef OM_uint32 GSSAPI_CALLCONV _gss_localname_t (
OM_uint32 *, /* minor_status */
- const gss_name_t, /* name */
+ gss_const_name_t, /* name */
const gss_OID, /* mech_type */
- uid_t * /* uidOut */
+ gss_buffer_t /* localname */
);
typedef OM_uint32 GSSAPI_CALLCONV _gss_authorize_localname_t (
OM_uint32 *, /* minor_status */
- const gss_name_t, /* name */
+ gss_const_name_t, /* name */
gss_const_buffer_t, /* user */
gss_const_OID /* user_name_type */
);
@@ -536,7 +536,7 @@ typedef struct gssapi_mech_interface_desc {
_gss_cred_label_set_t *gm_cred_label_set;
gss_mo_desc *gm_mo;
size_t gm_mo_num;
- _gss_pname_to_uid_t *gm_pname_to_uid;
+ _gss_localname_t *gm_localname;
_gss_authorize_localname_t *gm_authorize_localname;
_gss_display_name_ext_t *gm_display_name_ext;
_gss_inquire_name_t *gm_inquire_name;
@@ -582,7 +582,7 @@ extern gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_cred_certificate_oid_desc;
OM_uint32 _gss_acquire_cred_ext
(OM_uint32 * /*minor_status*/,
- const gss_name_t /*desired_name*/,
+ gss_const_name_t /*desired_name*/,
gss_const_OID /*credential_type*/,
const void * /*credential_data*/,
OM_uint32 /*time_req*/,
diff --git a/lib/gssapi/gsstool.c b/lib/gssapi/gsstool.c
index 5ce3cb04a7d3..8df39535eed4 100644
--- a/lib/gssapi/gsstool.c
+++ b/lib/gssapi/gsstool.c
@@ -72,7 +72,7 @@ usage (int ret)
#define COL_SASL "SASL"
int
-supported_mechanisms(void *argptr, int argc, char **argv)
+mechanisms(void *argptr, int argc, char **argv)
{
OM_uint32 maj_stat, min_stat;
gss_OID_set mechs;
@@ -184,7 +184,7 @@ print_mech_attr(const char *mechname, gss_const_OID mech, gss_OID_set set)
int
-attrs_for_mech(struct attrs_for_mech_options *opt, int argc, char **argv)
+attributes(struct attributes_options *opt, int argc, char **argv)
{
gss_OID_set mech_attr = NULL, known_mech_attrs = NULL;
gss_OID mech = GSS_C_NO_OID;
@@ -229,9 +229,10 @@ help(void *opt, int argc, char **argv)
int
main(int argc, char **argv)
{
- int optidx = 0;
+ int exit_status = 0, ret, optidx = 0;
setprogname(argv[0]);
+
if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
@@ -246,10 +247,18 @@ main(int argc, char **argv)
argc -= optidx;
argv += optidx;
- if (argc == 0) {
- help(NULL, argc, argv);
- return 1;
+ if (argc != 0) {
+ ret = sl_command(commands, argc, argv);
+ if(ret == -1)
+ sl_did_you_mean(commands, argv[0]);
+ else if (ret == -2)
+ ret = 0;
+ if(ret != 0)
+ exit_status = 1;
+ } else {
+ sl_slc_help(commands, argc, argv);
+ exit_status = 1;
}
- return sl_command (commands, argc, argv);
+ return exit_status;
}
diff --git a/lib/gssapi/krb5/8003.c b/lib/gssapi/krb5/8003.c
index d4555c51042b..5af477fe99fc 100644
--- a/lib/gssapi/krb5/8003.c
+++ b/lib/gssapi/krb5/8003.c
@@ -157,7 +157,7 @@ _gsskrb5_create_8003_checksum (
*p++ = (fwd_data->length >> 8) & 0xFF; /* Dlgth */
memcpy(p, (unsigned char *) fwd_data->data, fwd_data->length);
- p += fwd_data->length;
+ /* p += fwd_data->length; */ /* commented out to quiet warning */
}
return GSS_S_COMPLETE;
diff --git a/lib/gssapi/krb5/accept_sec_context.c b/lib/gssapi/krb5/accept_sec_context.c
index 5a00e124c2cf..d4680e9e8fb6 100644
--- a/lib/gssapi/krb5/accept_sec_context.c
+++ b/lib/gssapi/krb5/accept_sec_context.c
@@ -104,7 +104,6 @@ _gsskrb5_register_acceptor_identity(OM_uint32 *min_stat, const char *identity)
void
_gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
{
- krb5_error_code ret;
krb5_keyblock *key;
if (acceptor) {
@@ -144,7 +143,8 @@ _gsskrb5i_is_cfx(krb5_context context, gsskrb5_ctx ctx, int acceptor)
}
if (ctx->crypto)
krb5_crypto_destroy(context, ctx->crypto);
- ret = krb5_crypto_init(context, key, 0, &ctx->crypto);
+ /* XXX We really shouldn't ignore this; will come back to this */
+ (void) krb5_crypto_init(context, key, 0, &ctx->crypto);
}
@@ -164,12 +164,13 @@ gsskrb5_accept_delegated_token
/* XXX Create a new delegated_cred_handle? */
if (delegated_cred_handle == NULL) {
- kret = krb5_cc_default (context, &ccache);
- } else {
- *delegated_cred_handle = NULL;
- kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
- NULL, &ccache);
+ ret = GSS_S_COMPLETE;
+ goto out;
}
+
+ *delegated_cred_handle = NULL;
+ kret = krb5_cc_new_unique (context, krb5_cc_type_memory,
+ NULL, &ccache);
if (kret) {
ctx->flags &= ~GSS_C_DELEG_FLAG;
goto out;
@@ -270,7 +271,7 @@ gsskrb5_acceptor_ready(OM_uint32 * minor_status,
ctx,
context,
delegated_cred_handle);
- if (ret)
+ if (ret != GSS_S_COMPLETE)
return ret;
} else {
/* Well, looks like it wasn't there after all */
@@ -347,7 +348,7 @@ static OM_uint32
gsskrb5_acceptor_start(OM_uint32 * minor_status,
gsskrb5_ctx ctx,
krb5_context context,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token_buffer,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t * src_name,
@@ -363,6 +364,7 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
krb5_flags ap_options;
krb5_keytab keytab = NULL;
int is_cfx = 0;
+ int close_kt = 0;
const gsskrb5_cred acceptor_cred = (gsskrb5_cred)acceptor_cred_handle;
/*
@@ -384,8 +386,20 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
* We need to get our keytab
*/
if (acceptor_cred == NULL) {
- if (_gsskrb5_keytab != NULL)
- keytab = _gsskrb5_keytab;
+ HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
+ if (_gsskrb5_keytab != NULL) {
+ char *name = NULL;
+ kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
+ if (kret == 0) {
+ kret = krb5_kt_resolve(context, name, &keytab);
+ krb5_xfree(name);
+ }
+ if (kret == 0)
+ close_kt = 1;
+ else
+ keytab = NULL;
+ }
+ HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
} else if (acceptor_cred->keytab != NULL) {
keytab = acceptor_cred->keytab;
}
@@ -408,6 +422,8 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
if (kret) {
if (in)
krb5_rd_req_in_ctx_free(context, in);
+ if (close_kt)
+ krb5_kt_close(context, keytab);
*minor_status = kret;
return GSS_S_FAILURE;
}
@@ -418,6 +434,8 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
server,
in, &out);
krb5_rd_req_in_ctx_free(context, in);
+ if (close_kt)
+ krb5_kt_close(context, keytab);
if (kret == KRB5KRB_AP_ERR_SKEW || kret == KRB5KRB_AP_ERR_TKT_NYV) {
/*
* No reply in non-MUTUAL mode, but we don't know that its
@@ -443,7 +461,7 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
if (kret == 0)
kret = krb5_rd_req_out_get_keyblock(context, out,
&ctx->service_keyblock);
- ctx->lifetime = ctx->ticket->ticket.endtime;
+ ctx->endtime = ctx->ticket->ticket.endtime;
krb5_rd_req_out_ctx_free(context, out);
if (kret) {
@@ -464,6 +482,7 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
if (kret) {
ret = GSS_S_FAILURE;
*minor_status = kret;
+ return ret;
}
kret = krb5_copy_principal(context,
@@ -510,62 +529,61 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
return ret;
}
- if (authenticator->cksum == NULL) {
- krb5_free_authenticator(context, &authenticator);
- *minor_status = 0;
- return GSS_S_BAD_BINDINGS;
- }
-
- if (authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
+ if (authenticator->cksum != NULL
+ && authenticator->cksum->cksumtype == CKSUMTYPE_GSSAPI) {
ret = _gsskrb5_verify_8003_checksum(minor_status,
input_chan_bindings,
authenticator->cksum,
&ctx->flags,
&ctx->fwd_data);
- krb5_free_authenticator(context, &authenticator);
if (ret) {
+ krb5_free_authenticator(context, &authenticator);
return ret;
}
} else {
- krb5_crypto crypto;
-
- kret = krb5_crypto_init(context,
- ctx->auth_context->keyblock,
- 0, &crypto);
- if(kret) {
- krb5_free_authenticator(context, &authenticator);
-
- ret = GSS_S_FAILURE;
- *minor_status = kret;
- return ret;
+ if (authenticator->cksum != NULL) {
+ krb5_crypto crypto;
+
+ kret = krb5_crypto_init(context,
+ ctx->auth_context->keyblock,
+ 0, &crypto);
+ if (kret) {
+ krb5_free_authenticator(context, &authenticator);
+ ret = GSS_S_FAILURE;
+ *minor_status = kret;
+ return ret;
+ }
+
+ /*
+ * Windows accepts Samba3's use of a kerberos, rather than
+ * GSSAPI checksum here
+ */
+
+ kret = krb5_verify_checksum(context,
+ crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
+ authenticator->cksum);
+ krb5_crypto_destroy(context, crypto);
+
+ if (kret) {
+ krb5_free_authenticator(context, &authenticator);
+ ret = GSS_S_BAD_SIG;
+ *minor_status = kret;
+ return ret;
+ }
}
/*
- * Windows accepts Samba3's use of a kerberos, rather than
- * GSSAPI checksum here
+ * If there is no checksum or a kerberos checksum (which Windows
+ * and Samba accept), we use the ap_options to guess the mutual
+ * flag.
*/
- kret = krb5_verify_checksum(context,
- crypto, KRB5_KU_AP_REQ_AUTH_CKSUM, NULL, 0,
- authenticator->cksum);
- krb5_free_authenticator(context, &authenticator);
- krb5_crypto_destroy(context, crypto);
-
- if(kret) {
- ret = GSS_S_BAD_SIG;
- *minor_status = kret;
- return ret;
- }
-
- /*
- * Samba style get some flags (but not DCE-STYLE), use
- * ap_options to guess the mutual flag.
- */
- ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
+ ctx->flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG;
if (ap_options & AP_OPTS_MUTUAL_REQUIRED)
ctx->flags |= GSS_C_MUTUAL_FLAG;
- }
+ }
+ krb5_free_authenticator(context, &authenticator);
}
if(ctx->flags & GSS_C_MUTUAL_FLAG) {
@@ -593,8 +611,8 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
rkey);
if (kret == 0)
use_subkey = 1;
- krb5_free_keyblock(context, rkey);
}
+ krb5_free_keyblock(context, rkey);
}
if (use_subkey) {
ctx->more_flags |= ACCEPTOR_SUBKEY;
@@ -630,7 +648,7 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
/* Remember the flags */
- ctx->lifetime = ctx->ticket->ticket.endtime;
+ ctx->endtime = ctx->ticket->ticket.endtime;
ctx->more_flags |= OPEN;
if (mech_type)
@@ -639,7 +657,7 @@ gsskrb5_acceptor_start(OM_uint32 * minor_status,
if (time_rec) {
ret = _gsskrb5_lifetime_left(minor_status,
context,
- ctx->lifetime,
+ ctx->endtime,
time_rec);
if (ret) {
return ret;
@@ -675,7 +693,7 @@ static OM_uint32
acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
gsskrb5_ctx ctx,
krb5_context context,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token_buffer,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t * src_name,
@@ -757,7 +775,7 @@ acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
ret = _gsskrb5_lifetime_left(minor_status,
context,
- ctx->lifetime,
+ ctx->endtime,
&lifetime_rec);
if (ret) {
return ret;
@@ -838,7 +856,7 @@ acceptor_wait_for_dcestyle(OM_uint32 * minor_status,
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_accept_sec_context(OM_uint32 * minor_status,
gss_ctx_id_t * context_handle,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token_buffer,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t * src_name,
diff --git a/lib/gssapi/krb5/acquire_cred.c b/lib/gssapi/krb5/acquire_cred.c
index 0f1f5f81cffc..9c880b334fd6 100644
--- a/lib/gssapi/krb5/acquire_cred.c
+++ b/lib/gssapi/krb5/acquire_cred.c
@@ -40,37 +40,16 @@ __gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
krb5_principal principal,
OM_uint32 *lifetime)
{
- krb5_creds in_cred, out_cred;
- krb5_const_realm realm;
krb5_error_code kret;
+ time_t left;
- memset(&in_cred, 0, sizeof(in_cred));
- in_cred.client = principal;
-
- realm = krb5_principal_get_realm(context, principal);
- if (realm == NULL) {
- _gsskrb5_clear_status ();
- *minor_status = KRB5_PRINC_NOMATCH; /* XXX */
- return GSS_S_FAILURE;
- }
-
- kret = krb5_make_principal(context, &in_cred.server,
- realm, KRB5_TGS_NAME, realm, NULL);
- if (kret) {
- *minor_status = kret;
- return GSS_S_FAILURE;
- }
-
- kret = krb5_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred);
- krb5_free_principal(context, in_cred.server);
+ kret = krb5_cc_get_lifetime(context, id, &left);
if (kret) {
- *minor_status = 0;
- *lifetime = 0;
- return GSS_S_COMPLETE;
+ *minor_status = kret;
+ return GSS_S_FAILURE;
}
- *lifetime = out_cred.times.endtime;
- krb5_free_cred_contents(context, &out_cred);
+ *lifetime = left;
return GSS_S_COMPLETE;
}
@@ -101,146 +80,282 @@ get_keytab(krb5_context context, krb5_keytab *keytab)
return (kret);
}
-static OM_uint32 acquire_initiator_cred
- (OM_uint32 * minor_status,
- krb5_context context,
- gss_const_OID credential_type,
- const void *credential_data,
- const gss_name_t desired_name,
- OM_uint32 time_req,
- gss_const_OID desired_mech,
- gss_cred_usage_t cred_usage,
- gsskrb5_cred handle
- )
+/*
+ * This function produces a cred with a MEMORY ccache containing a TGT
+ * acquired with a password.
+ */
+static OM_uint32
+acquire_cred_with_password(OM_uint32 *minor_status,
+ krb5_context context,
+ const char *password,
+ OM_uint32 time_req,
+ gss_const_OID desired_mech,
+ gss_cred_usage_t cred_usage,
+ gsskrb5_cred handle)
{
- OM_uint32 ret;
+ OM_uint32 ret = GSS_S_FAILURE;
krb5_creds cred;
- krb5_principal def_princ;
krb5_get_init_creds_opt *opt;
- krb5_ccache ccache;
- krb5_keytab keytab;
+ krb5_ccache ccache = NULL;
krb5_error_code kret;
+ time_t now;
+ OM_uint32 left;
+
+ if (cred_usage == GSS_C_ACCEPT) {
+ /*
+ * TODO: Here we should eventually support user2user (when we get
+ * support for that via an extension to the mechanism
+ * allowing for more than two security context tokens),
+ * and/or new unique MEMORY keytabs (we have MEMORY keytab
+ * support, but we don't have a keytab equivalent of
+ * krb5_cc_new_unique()). Either way, for now we can't
+ * support this.
+ */
+ *minor_status = ENOTSUP; /* XXX Better error? */
+ return GSS_S_FAILURE;
+ }
+
+ memset(&cred, 0, sizeof(cred));
+
+ if (handle->principal == NULL) {
+ kret = krb5_get_default_principal(context, &handle->principal);
+ if (kret)
+ goto end;
+ }
+ kret = krb5_get_init_creds_opt_alloc(context, &opt);
+ if (kret)
+ goto end;
+
+ /*
+ * Get the current time before the AS exchange so we don't
+ * accidentally end up returning a value that puts advertised
+ * expiration past the real expiration.
+ *
+ * We need to do this because krb5_cc_get_lifetime() returns a
+ * relative time that we need to add to the current time. We ought
+ * to have a version of krb5_cc_get_lifetime() that returns absolute
+ * time...
+ */
+ krb5_timeofday(context, &now);
+
+ kret = krb5_get_init_creds_password(context, &cred, handle->principal,
+ password, NULL, NULL, 0, NULL, opt);
+ krb5_get_init_creds_opt_free(context, opt);
+ if (kret)
+ goto end;
+
+ kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache);
+ if (kret)
+ goto end;
+
+ kret = krb5_cc_initialize(context, ccache, cred.client);
+ if (kret)
+ goto end;
- keytab = NULL;
+ kret = krb5_cc_store_cred(context, ccache, &cred);
+ if (kret)
+ goto end;
+
+ handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
+
+ ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache,
+ handle->principal, &left);
+ if (ret != GSS_S_COMPLETE)
+ goto end;
+ handle->endtime = now + left;
+ handle->ccache = ccache;
ccache = NULL;
- def_princ = NULL;
- ret = GSS_S_FAILURE;
+ ret = GSS_S_COMPLETE;
+ kret = 0;
+
+end:
+ if (ccache != NULL)
+ krb5_cc_destroy(context, ccache);
+ if (cred.client != NULL)
+ krb5_free_cred_contents(context, &cred);
+ if (ret != GSS_S_COMPLETE && kret != 0)
+ *minor_status = kret;
+ return (ret);
+}
+
+/*
+ * Acquires an initiator credential from a ccache or using a keytab.
+ */
+static OM_uint32
+acquire_initiator_cred(OM_uint32 *minor_status,
+ krb5_context context,
+ OM_uint32 time_req,
+ gss_const_OID desired_mech,
+ gss_cred_usage_t cred_usage,
+ gsskrb5_cred handle)
+{
+ OM_uint32 ret = GSS_S_FAILURE;
+ krb5_creds cred;
+ krb5_get_init_creds_opt *opt;
+ krb5_principal def_princ = NULL;
+ krb5_ccache def_ccache = NULL;
+ krb5_ccache ccache = NULL; /* we may store into this ccache */
+ krb5_keytab keytab = NULL;
+ krb5_error_code kret = 0;
+ OM_uint32 left;
+ time_t lifetime = 0;
+ time_t now;
+
memset(&cred, 0, sizeof(cred));
/*
- * If we have a preferred principal, lets try to find it in all
- * caches, otherwise, fall back to default cache, ignore all
- * errors while searching.
+ * Get current time early so we can set handle->endtime to a value that
+ * cannot accidentally be past the real endtime. We need a variant of
+ * krb5_cc_get_lifetime() that returns absolute endtime.
*/
+ krb5_timeofday(context, &now);
- if (credential_type != GSS_C_NO_OID &&
- !gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
- kret = KRB5_NOCREDS_SUPPLIED; /* XXX */
- goto end;
- }
+ /*
+ * First look for a ccache that has the desired_name (which may be
+ * the default credential name).
+ *
+ * If we don't have an unexpired credential, acquire one with a
+ * keytab.
+ *
+ * If we acquire one with a keytab, save it in the ccache we found
+ * with the expired credential, if any.
+ *
+ * If we don't have any such ccache, then use a MEMORY ccache.
+ */
- if (handle->principal) {
- kret = krb5_cc_cache_match (context,
- handle->principal,
- &ccache);
+ if (handle->principal != NULL) {
+ /*
+ * Not default credential case. See if we can find a ccache in
+ * the cccol for the desired_name.
+ */
+ kret = krb5_cc_cache_match(context,
+ handle->principal,
+ &ccache);
if (kret == 0) {
- ret = GSS_S_COMPLETE;
- goto found;
+ kret = krb5_cc_get_lifetime(context, ccache, &lifetime);
+ if (kret == 0) {
+ if (lifetime > 0)
+ goto found;
+ else
+ goto try_keytab;
+ }
}
+ /*
+ * Fall through. We shouldn't find this in the default ccache
+ * either, but we'll give it a try, then we'll try using a keytab.
+ */
}
- if (ccache == NULL) {
- kret = krb5_cc_default(context, &ccache);
- if (kret)
- goto end;
- }
- kret = krb5_cc_get_principal(context, ccache, &def_princ);
- if (kret != 0) {
- /* we'll try to use a keytab below */
- krb5_cc_close(context, ccache);
- def_princ = NULL;
- kret = 0;
- } else if (handle->principal == NULL) {
- kret = krb5_copy_principal(context, def_princ, &handle->principal);
- if (kret)
- goto end;
- } else if (handle->principal != NULL &&
- krb5_principal_compare(context, handle->principal,
- def_princ) == FALSE) {
- krb5_free_principal(context, def_princ);
- def_princ = NULL;
- krb5_cc_close(context, ccache);
- ccache = NULL;
+ /*
+ * Either desired_name was GSS_C_NO_NAME (default cred) or
+ * krb5_cc_cache_match() failed (or found expired).
+ */
+ kret = krb5_cc_default(context, &def_ccache);
+ if (kret != 0)
+ goto try_keytab;
+ kret = krb5_cc_get_lifetime(context, def_ccache, &lifetime);
+ if (kret != 0)
+ lifetime = 0;
+ kret = krb5_cc_get_principal(context, def_ccache, &def_princ);
+ if (kret != 0)
+ goto try_keytab;
+ /*
+ * Have a default ccache; see if it matches desired_name.
+ */
+ if (handle->principal == NULL ||
+ krb5_principal_compare(context, handle->principal,
+ def_princ) == TRUE) {
+ /*
+ * It matches.
+ *
+ * If we end up trying a keytab then we can write the result to
+ * the default ccache.
+ */
+ if (handle->principal == NULL) {
+ kret = krb5_copy_principal(context, def_princ, &handle->principal);
+ if (kret)
+ goto end;
+ }
+ if (ccache != NULL)
+ krb5_cc_close(context, ccache);
+ ccache = def_ccache;
+ def_ccache = NULL;
+ if (lifetime > 0)
+ goto found;
+ /* else we fall through and try using a keytab */
}
- if (def_princ == NULL) {
- /* We have no existing credentials cache,
- * so attempt to get a TGT using a keytab.
- */
- if (handle->principal == NULL) {
- kret = krb5_get_default_principal(context, &handle->principal);
- if (kret)
- goto end;
- }
- kret = krb5_get_init_creds_opt_alloc(context, &opt);
- if (kret)
- goto end;
- if (credential_type != GSS_C_NO_OID &&
- gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
- gss_buffer_t password = (gss_buffer_t)credential_data;
-
- /* XXX are we requiring password to be NUL terminated? */
-
- kret = krb5_get_init_creds_password(context, &cred,
- handle->principal,
- password->value,
- NULL, NULL, 0, NULL, opt);
- } else {
- kret = get_keytab(context, &keytab);
- if (kret) {
- krb5_get_init_creds_opt_free(context, opt);
- goto end;
- }
- kret = krb5_get_init_creds_keytab(context, &cred,
- handle->principal, keytab,
- 0, NULL, opt);
- }
- krb5_get_init_creds_opt_free(context, opt);
- if (kret)
- goto end;
- kret = krb5_cc_new_unique(context, krb5_cc_type_memory,
- NULL, &ccache);
- if (kret)
- goto end;
- kret = krb5_cc_initialize(context, ccache, cred.client);
- if (kret) {
- krb5_cc_destroy(context, ccache);
- goto end;
- }
- kret = krb5_cc_store_cred(context, ccache, &cred);
- if (kret) {
- krb5_cc_destroy(context, ccache);
- goto end;
- }
- handle->lifetime = cred.times.endtime;
- handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
- } else {
- ret = __gsskrb5_ccache_lifetime(minor_status,
- context,
- ccache,
- handle->principal,
- &handle->lifetime);
- if (ret != GSS_S_COMPLETE) {
- krb5_cc_close(context, ccache);
- goto end;
- }
- kret = 0;
+try_keytab:
+ if (handle->principal == NULL) {
+ /* We need to know what client principal to use */
+ kret = krb5_get_default_principal(context, &handle->principal);
+ if (kret)
+ goto end;
}
- found:
+ kret = get_keytab(context, &keytab);
+ if (kret)
+ goto end;
+
+ kret = krb5_get_init_creds_opt_alloc(context, &opt);
+ if (kret)
+ goto end;
+ krb5_timeofday(context, &now);
+ kret = krb5_get_init_creds_keytab(context, &cred, handle->principal,
+ keytab, 0, NULL, opt);
+ krb5_get_init_creds_opt_free(context, opt);
+ if (kret)
+ goto end;
+
+ /*
+ * We got a credential with a keytab. Save it if we can.
+ */
+ if (ccache == NULL) {
+ /*
+ * There's no ccache we can overwrite with the credentials we acquired
+ * with a keytab. We'll use a MEMORY ccache then.
+ *
+ * Note that an application that falls into this repeatedly will do an
+ * AS exchange every time it acquires a credential handle. Hopefully
+ * this doesn't happen much. A workaround is to kinit -k once so that
+ * we always re-initialize the matched/default ccache here. I.e., once
+ * there's a FILE/DIR ccache, we'll keep it frash automatically if we
+ * have a keytab, but if there's no FILE/DIR ccache, then we'll
+ * get a fresh credential *every* time we're asked.
+ */
+ kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache);
+ if (kret)
+ goto end;
+ handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
+ } /* else we'll re-initialize whichever ccache we matched above */
+
+ kret = krb5_cc_initialize(context, ccache, cred.client);
+ if (kret)
+ goto end;
+ kret = krb5_cc_store_cred(context, ccache, &cred);
+ if (kret)
+ goto end;
+
+found:
+ assert(handle->principal != NULL);
+ ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache,
+ handle->principal, &left);
+ if (ret != GSS_S_COMPLETE)
+ goto end;
+ handle->endtime = now + left;
handle->ccache = ccache;
+ ccache = NULL;
ret = GSS_S_COMPLETE;
+ kret = 0;
end:
+ if (ccache != NULL) {
+ if ((handle->cred_flags & GSS_CF_DESTROY_CRED_ON_RELEASE) != 0)
+ krb5_cc_destroy(context, ccache);
+ else
+ krb5_cc_close(context, ccache);
+ }
+ if (def_ccache != NULL)
+ krb5_cc_close(context, def_ccache);
if (cred.client != NULL)
krb5_free_cred_contents(context, &cred);
if (def_princ != NULL)
@@ -252,28 +367,19 @@ end:
return (ret);
}
-static OM_uint32 acquire_acceptor_cred
- (OM_uint32 * minor_status,
- krb5_context context,
- gss_const_OID credential_type,
- const void *credential_data,
- const gss_name_t desired_name,
- OM_uint32 time_req,
- gss_const_OID desired_mech,
- gss_cred_usage_t cred_usage,
- gsskrb5_cred handle
- )
+static OM_uint32
+acquire_acceptor_cred(OM_uint32 * minor_status,
+ krb5_context context,
+ OM_uint32 time_req,
+ gss_const_OID desired_mech,
+ gss_cred_usage_t cred_usage,
+ gsskrb5_cred handle)
{
OM_uint32 ret;
krb5_error_code kret;
ret = GSS_S_FAILURE;
- if (credential_type != GSS_C_NO_OID) {
- kret = EINVAL;
- goto end;
- }
-
kret = get_keytab(context, &handle->keytab);
if (kret)
goto end;
@@ -318,7 +424,7 @@ end:
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
(OM_uint32 * minor_status,
- const gss_name_t desired_name,
+ gss_const_name_t desired_name,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
@@ -366,7 +472,7 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
(OM_uint32 * minor_status,
- const gss_name_t desired_name,
+ gss_const_name_t desired_name,
gss_const_OID credential_type,
const void *credential_data,
OM_uint32 time_req,
@@ -381,25 +487,26 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
cred_usage &= GSS_C_OPTION_MASK;
- if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
+ if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE &&
+ cred_usage != GSS_C_BOTH) {
*minor_status = GSS_KRB5_S_G_BAD_USAGE;
return GSS_S_FAILURE;
}
GSSAPI_KRB5_INIT(&context);
- *output_cred_handle = NULL;
+ *output_cred_handle = GSS_C_NO_CREDENTIAL;
handle = calloc(1, sizeof(*handle));
if (handle == NULL) {
*minor_status = ENOMEM;
- return (GSS_S_FAILURE);
+ return GSS_S_FAILURE;
}
HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
if (desired_name != GSS_C_NO_NAME) {
- ret = _gsskrb5_canon_name(minor_status, context, 1, NULL,
+ ret = _gsskrb5_canon_name(minor_status, context,
desired_name, &handle->principal);
if (ret) {
HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
@@ -407,29 +514,75 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
return ret;
}
}
- if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
- ret = acquire_initiator_cred(minor_status, context,
- credential_type, credential_data,
- desired_name, time_req,
- desired_mech, cred_usage, handle);
- if (ret != GSS_S_COMPLETE) {
- HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
- krb5_free_principal(context, handle->principal);
- free(handle);
- return (ret);
- }
- }
- if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
- ret = acquire_acceptor_cred(minor_status, context,
- credential_type, credential_data,
- desired_name, time_req,
- desired_mech, cred_usage, handle);
- if (ret != GSS_S_COMPLETE) {
- HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
- krb5_free_principal(context, handle->principal);
- free(handle);
- return (ret);
- }
+
+ if (credential_type != GSS_C_NO_OID &&
+ gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
+ /* Acquire a cred with a password */
+ gss_const_buffer_t pwbuf = credential_data;
+ char *pw;
+
+ if (pwbuf == NULL) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ free(handle);
+ *minor_status = KRB5_NOCREDS_SUPPLIED; /* see below */
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+
+ /* NUL-terminate the password, if it wasn't already */
+ pw = strndup(pwbuf->value, pwbuf->length);
+ if (pw == NULL) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ free(handle);
+ *minor_status = krb5_enomem(context);
+ return GSS_S_CALL_INACCESSIBLE_READ;
+ }
+ ret = acquire_cred_with_password(minor_status, context, pw, time_req,
+ desired_mech, cred_usage, handle);
+ free(pw);
+ if (ret != GSS_S_COMPLETE) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ return (ret);
+ }
+ } else if (credential_type != GSS_C_NO_OID) {
+ /*
+ * _gss_acquire_cred_ext() called with something other than a password.
+ *
+ * Not supported.
+ *
+ * _gss_acquire_cred_ext() is not a supported public interface, so
+ * we don't have to try too hard as to minor status codes here.
+ */
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ free(handle);
+ *minor_status = ENOTSUP;
+ return GSS_S_FAILURE;
+ } else {
+ /*
+ * Acquire a credential from the background credential store (ccache,
+ * keytab).
+ */
+ if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
+ ret = acquire_initiator_cred(minor_status, context, time_req,
+ desired_mech, cred_usage, handle);
+ if (ret != GSS_S_COMPLETE) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ return (ret);
+ }
+ }
+ if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
+ ret = acquire_acceptor_cred(minor_status, context, time_req,
+ desired_mech, cred_usage, handle);
+ if (ret != GSS_S_COMPLETE) {
+ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
+ krb5_free_principal(context, handle->principal);
+ free(handle);
+ return (ret);
+ }
+ }
}
ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
if (ret == GSS_S_COMPLETE)
diff --git a/lib/gssapi/krb5/add_cred.c b/lib/gssapi/krb5/add_cred.c
index 00cf55f62d65..42f6b48181b4 100644
--- a/lib/gssapi/krb5/add_cred.c
+++ b/lib/gssapi/krb5/add_cred.c
@@ -35,8 +35,8 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_add_cred (
OM_uint32 *minor_status,
- const gss_cred_id_t input_cred_handle,
- const gss_name_t desired_name,
+ gss_const_cred_id_t input_cred_handle,
+ gss_const_name_t desired_name,
const gss_OID desired_mech,
gss_cred_usage_t cred_usage,
OM_uint32 initiator_time_req,
@@ -47,7 +47,7 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_add_cred (
OM_uint32 *acceptor_time_rec)
{
krb5_context context;
- OM_uint32 ret, lifetime;
+ OM_uint32 major, lifetime;
gsskrb5_cred cred, handle;
krb5_const_principal dname;
@@ -55,99 +55,120 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_add_cred (
cred = (gsskrb5_cred)input_cred_handle;
dname = (krb5_const_principal)desired_name;
+ if (cred == NULL && output_cred_handle == NULL) {
+ *minor_status = EINVAL;
+ return GSS_S_CALL_INACCESSIBLE_WRITE;
+ }
+
GSSAPI_KRB5_INIT (&context);
- if (gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0) {
+ if (desired_mech != GSS_C_NO_OID &&
+ gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0) {
*minor_status = 0;
return GSS_S_BAD_MECH;
}
- if (cred == NULL && output_cred_handle == NULL) {
- *minor_status = 0;
- return GSS_S_NO_CRED;
- }
-
- if (cred == NULL) { /* XXX standard conformance failure */
- *minor_status = 0;
- return GSS_S_NO_CRED;
- }
+ if (cred == NULL) {
+ /*
+ * Acquire a credential; output_cred_handle can't be NULL, see above.
+ */
+ heim_assert(output_cred_handle != NULL,
+ "internal error in _gsskrb5_add_cred()");
+
+ major = _gsskrb5_acquire_cred(minor_status, desired_name,
+ min(initiator_time_req,
+ acceptor_time_req),
+ GSS_C_NO_OID_SET,
+ cred_usage,
+ output_cred_handle,
+ actual_mechs, &lifetime);
+ if (major != GSS_S_COMPLETE)
+ goto failure;
+
+ } else {
+ /*
+ * Check that we're done or copy input to output if
+ * output_cred_handle != NULL.
+ */
- /* check if requested output usage is compatible with output usage */
- if (output_cred_handle != NULL) {
HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
+
+ /* Check if requested output usage is compatible with output usage */
if (cred->usage != cred_usage && cred->usage != GSS_C_BOTH) {
HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
*minor_status = GSS_KRB5_S_G_BAD_USAGE;
return(GSS_S_FAILURE);
}
- }
-
- /* check that we have the same name */
- if (dname != NULL &&
- krb5_principal_compare(context, dname,
- cred->principal) != FALSE) {
- if (output_cred_handle)
- HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
- *minor_status = 0;
- return GSS_S_BAD_NAME;
- }
- /* make a copy */
- if (output_cred_handle) {
- krb5_error_code kret;
+ /* Check that we have the same name */
+ if (dname != NULL &&
+ krb5_principal_compare(context, dname,
+ cred->principal) != FALSE) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = 0;
+ return GSS_S_BAD_NAME;
+ }
+
+ if (output_cred_handle == NULL) {
+ /*
+ * This case is basically useless as we implement a single
+ * mechanism here, so we can't add elements to the
+ * input_cred_handle.
+ */
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+ }
+
+ /*
+ * Copy input to output -- this works as if we were a
+ * GSS_Duplicate_cred() for one mechanism element.
+ */
handle = calloc(1, sizeof(*handle));
if (handle == NULL) {
- HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ if (cred != NULL)
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
*minor_status = ENOMEM;
return (GSS_S_FAILURE);
}
handle->usage = cred_usage;
- handle->lifetime = cred->lifetime;
+ handle->endtime = cred->endtime;
handle->principal = NULL;
handle->keytab = NULL;
handle->ccache = NULL;
handle->mechanisms = NULL;
HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
- ret = GSS_S_FAILURE;
+ major = GSS_S_FAILURE;
- kret = krb5_copy_principal(context, cred->principal,
- &handle->principal);
- if (kret) {
+ *minor_status = krb5_copy_principal(context, cred->principal,
+ &handle->principal);
+ if (*minor_status) {
HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
free(handle);
- *minor_status = kret;
return GSS_S_FAILURE;
}
if (cred->keytab) {
char *name = NULL;
- ret = GSS_S_FAILURE;
-
- kret = krb5_kt_get_full_name(context, cred->keytab, &name);
- if (kret) {
- *minor_status = kret;
+ *minor_status = krb5_kt_get_full_name(context, cred->keytab,
+ &name);
+ if (*minor_status)
goto failure;
- }
- kret = krb5_kt_resolve(context, name,
- &handle->keytab);
+ *minor_status = krb5_kt_resolve(context, name, &handle->keytab);
krb5_xfree(name);
- if (kret){
- *minor_status = kret;
+ if (*minor_status)
goto failure;
- }
}
if (cred->ccache) {
const char *type, *name;
char *type_name = NULL;
- ret = GSS_S_FAILURE;
-
type = krb5_cc_get_type(context, cred->ccache);
if (type == NULL){
*minor_status = ENOMEM;
@@ -155,19 +176,15 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_add_cred (
}
if (strcmp(type, "MEMORY") == 0) {
- ret = krb5_cc_new_unique(context, type,
- NULL, &handle->ccache);
- if (ret) {
- *minor_status = ret;
+ *minor_status = krb5_cc_new_unique(context, type,
+ NULL, &handle->ccache);
+ if (*minor_status)
goto failure;
- }
- ret = krb5_cc_copy_cache(context, cred->ccache,
- handle->ccache);
- if (ret) {
- *minor_status = ret;
+ *minor_status = krb5_cc_copy_cache(context, cred->ccache,
+ handle->ccache);
+ if (*minor_status)
goto failure;
- }
} else {
name = krb5_cc_get_name(context, cred->ccache);
@@ -176,52 +193,47 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_add_cred (
goto failure;
}
- kret = asprintf(&type_name, "%s:%s", type, name);
- if (kret < 0 || type_name == NULL) {
+ if (asprintf(&type_name, "%s:%s", type, name) == -1 ||
+ type_name == NULL) {
*minor_status = ENOMEM;
goto failure;
}
- kret = krb5_cc_resolve(context, type_name,
- &handle->ccache);
+ *minor_status = krb5_cc_resolve(context, type_name,
+ &handle->ccache);
free(type_name);
- if (kret) {
- *minor_status = kret;
+ if (*minor_status)
goto failure;
- }
}
}
- ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
- if (ret)
+ major = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
+ if (major != GSS_S_COMPLETE)
goto failure;
- ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
- &handle->mechanisms);
- if (ret)
+ major = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
+ &handle->mechanisms);
+ if (major != GSS_S_COMPLETE)
goto failure;
- }
- HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
- ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)cred,
- NULL, &lifetime, NULL, actual_mechs);
- if (ret)
- goto failure;
+ major = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)cred,
+ NULL, &lifetime, NULL, actual_mechs);
+ if (major != GSS_S_COMPLETE)
+ goto failure;
+
+ *output_cred_handle = (gss_cred_id_t)handle;
+ }
if (initiator_time_rec)
*initiator_time_rec = lifetime;
if (acceptor_time_rec)
*acceptor_time_rec = lifetime;
- if (output_cred_handle) {
- *output_cred_handle = (gss_cred_id_t)handle;
- }
-
*minor_status = 0;
- return ret;
-
- failure:
+ return major;
+failure:
if (handle) {
if (handle->principal)
krb5_free_principal(context, handle->principal);
@@ -233,7 +245,7 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_add_cred (
gss_release_oid_set(NULL, &handle->mechanisms);
free(handle);
}
- if (output_cred_handle)
+ if (cred && output_cred_handle)
HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
- return ret;
+ return major;
}
diff --git a/lib/gssapi/krb5/aeap.c b/lib/gssapi/krb5/aeap.c
index 47913e4aec03..fe95ecf0b9cc 100644
--- a/lib/gssapi/krb5/aeap.c
+++ b/lib/gssapi/krb5/aeap.c
@@ -44,17 +44,43 @@ _gk_wrap_iov(OM_uint32 * minor_status,
gss_iov_buffer_desc *iov,
int iov_count)
{
- const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
- krb5_context context;
+ const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
+ krb5_context context;
+ OM_uint32 ret;
+ krb5_keyblock *key;
+ krb5_keytype keytype;
+
+ GSSAPI_KRB5_INIT (&context);
+
+ if (ctx->more_flags & IS_CFX)
+ return _gssapi_wrap_cfx_iov(minor_status, ctx, context,
+ conf_req_flag, conf_state,
+ iov, iov_count);
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(ctx, context, &key);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ krb5_enctype_to_keytype(context, key->keytype, &keytype);
- GSSAPI_KRB5_INIT (&context);
+ switch (keytype) {
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ ret = _gssapi_wrap_iov_arcfour(minor_status, ctx, context,
+ conf_req_flag, conf_state,
+ iov, iov_count, key);
+ break;
- if (ctx->more_flags & IS_CFX)
- return _gssapi_wrap_cfx_iov(minor_status, ctx, context,
- conf_req_flag, conf_state,
- iov, iov_count);
+ default:
+ ret = GSS_S_FAILURE;
+ break;
+ }
- return GSS_S_FAILURE;
+ krb5_free_keyblock(context, key);
+ return ret;
}
OM_uint32 GSSAPI_CALLCONV
@@ -67,6 +93,9 @@ _gk_unwrap_iov(OM_uint32 *minor_status,
{
const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
krb5_context context;
+ OM_uint32 ret;
+ krb5_keytype keytype;
+ krb5_keyblock *key;
GSSAPI_KRB5_INIT (&context);
@@ -74,7 +103,30 @@ _gk_unwrap_iov(OM_uint32 *minor_status,
return _gssapi_unwrap_cfx_iov(minor_status, ctx, context,
conf_state, qop_state, iov, iov_count);
- return GSS_S_FAILURE;
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(ctx, context, &key);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ krb5_enctype_to_keytype(context, key->keytype, &keytype);
+
+ switch (keytype) {
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ ret = _gssapi_unwrap_iov_arcfour(minor_status, ctx, context,
+ conf_state, qop_state,
+ iov, iov_count, key);
+ break;
+
+ default:
+ ret = GSS_S_FAILURE;
+ break;
+ }
+
+ krb5_free_keyblock(context, key);
+ return ret;
}
OM_uint32 GSSAPI_CALLCONV
@@ -88,6 +140,9 @@ _gk_wrap_iov_length(OM_uint32 * minor_status,
{
const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
krb5_context context;
+ OM_uint32 ret;
+ krb5_keytype keytype;
+ krb5_keyblock *key;
GSSAPI_KRB5_INIT (&context);
@@ -96,5 +151,28 @@ _gk_wrap_iov_length(OM_uint32 * minor_status,
conf_req_flag, qop_req, conf_state,
iov, iov_count);
- return GSS_S_FAILURE;
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gsskrb5i_get_token_key(ctx, context, &key);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ krb5_enctype_to_keytype(context, key->keytype, &keytype);
+
+ switch (keytype) {
+ case KEYTYPE_ARCFOUR:
+ case KEYTYPE_ARCFOUR_56:
+ ret = _gssapi_wrap_iov_length_arcfour(minor_status, ctx, context,
+ conf_req_flag, qop_req, conf_state,
+ iov, iov_count);
+ break;
+
+ default:
+ ret = GSS_S_FAILURE;
+ break;
+ }
+
+ krb5_free_keyblock(context, key);
+ return ret;
}
diff --git a/lib/gssapi/krb5/arcfour.c b/lib/gssapi/krb5/arcfour.c
index 15b1b343409a..0aa2da0808bf 100644
--- a/lib/gssapi/krb5/arcfour.c
+++ b/lib/gssapi/krb5/arcfour.c
@@ -69,7 +69,7 @@
static krb5_error_code
arcfour_mic_key(krb5_context context, krb5_keyblock *key,
- void *cksum_data, size_t cksum_size,
+ const void *cksum_data, size_t cksum_size,
void *key6_data, size_t key6_size)
{
krb5_error_code ret;
@@ -86,7 +86,7 @@ arcfour_mic_key(krb5_context context, krb5_keyblock *key,
cksum_k5.checksum.data = k5_data;
cksum_k5.checksum.length = sizeof(k5_data);
- if (key->keytype == ENCTYPE_ARCFOUR_HMAC_MD5_56) {
+ if (key->keytype == KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56) {
char L40[14] = "fortybits";
memcpy(L40 + 10, T, sizeof(T));
@@ -100,7 +100,7 @@ arcfour_mic_key(krb5_context context, krb5_keyblock *key,
if (ret)
return ret;
- key5.keytype = ENCTYPE_ARCFOUR_HMAC_MD5;
+ key5.keytype = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5;
key5.keyvalue = cksum_k5.checksum;
cksum_k6.checksum.data = key6_data;
@@ -112,30 +112,73 @@ arcfour_mic_key(krb5_context context, krb5_keyblock *key,
static krb5_error_code
-arcfour_mic_cksum(krb5_context context,
- krb5_keyblock *key, unsigned usage,
- u_char *sgn_cksum, size_t sgn_cksum_sz,
- const u_char *v1, size_t l1,
- const void *v2, size_t l2,
- const void *v3, size_t l3)
+arcfour_mic_cksum_iov(krb5_context context,
+ krb5_keyblock *key, unsigned usage,
+ u_char *sgn_cksum, size_t sgn_cksum_sz,
+ const u_char *v1, size_t l1,
+ const void *v2, size_t l2,
+ const gss_iov_buffer_desc *iov,
+ int iov_count,
+ const gss_iov_buffer_desc *padding)
{
Checksum CKSUM;
u_char *ptr;
size_t len;
+ size_t ofs = 0;
+ int i;
krb5_crypto crypto;
krb5_error_code ret;
assert(sgn_cksum_sz == 8);
- len = l1 + l2 + l3;
+ len = l1 + l2;
+
+ for (i=0; i < iov_count; i++) {
+ switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
+ case GSS_IOV_BUFFER_TYPE_DATA:
+ case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
+ break;
+ default:
+ continue;
+ }
+
+ len += iov[i].buffer.length;
+ }
+
+ if (padding) {
+ len += padding->buffer.length;
+ }
ptr = malloc(len);
if (ptr == NULL)
return ENOMEM;
- memcpy(ptr, v1, l1);
- memcpy(ptr + l1, v2, l2);
- memcpy(ptr + l1 + l2, v3, l3);
+ memcpy(ptr + ofs, v1, l1);
+ ofs += l1;
+ memcpy(ptr + ofs, v2, l2);
+ ofs += l2;
+
+ for (i=0; i < iov_count; i++) {
+ switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
+ case GSS_IOV_BUFFER_TYPE_DATA:
+ case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
+ break;
+ default:
+ continue;
+ }
+
+ memcpy(ptr + ofs,
+ iov[i].buffer.value,
+ iov[i].buffer.length);
+ ofs += iov[i].buffer.length;
+ }
+
+ if (padding) {
+ memcpy(ptr + ofs,
+ padding->buffer.value,
+ padding->buffer.length);
+ ofs += padding->buffer.length;
+ }
ret = krb5_crypto_init(context, key, 0, &crypto);
if (ret) {
@@ -149,6 +192,7 @@ arcfour_mic_cksum(krb5_context context,
0,
ptr, len,
&CKSUM);
+ memset(ptr, 0, len);
free(ptr);
if (ret == 0) {
memcpy(sgn_cksum, CKSUM.checksum.data, sgn_cksum_sz);
@@ -159,6 +203,26 @@ arcfour_mic_cksum(krb5_context context,
return ret;
}
+static krb5_error_code
+arcfour_mic_cksum(krb5_context context,
+ krb5_keyblock *key, unsigned usage,
+ u_char *sgn_cksum, size_t sgn_cksum_sz,
+ const u_char *v1, size_t l1,
+ const void *v2, size_t l2,
+ const void *v3, size_t l3)
+{
+ gss_iov_buffer_desc iov;
+
+ iov.type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ iov.buffer.value = rk_UNCONST(v3);
+ iov.buffer.length = l3;
+
+ return arcfour_mic_cksum_iov(context, key, usage,
+ sgn_cksum, sgn_cksum_sz,
+ v1, l1, v2, l2,
+ &iov, 1, NULL);
+}
+
OM_uint32
_gssapi_get_mic_arcfour(OM_uint32 * minor_status,
@@ -760,3 +824,563 @@ _gssapi_wrap_size_arcfour(OM_uint32 *minor_status,
return GSS_S_COMPLETE;
}
+
+OM_uint32
+_gssapi_wrap_iov_length_arcfour(OM_uint32 *minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ int *conf_state,
+ gss_iov_buffer_desc *iov,
+ int iov_count)
+{
+ OM_uint32 major_status;
+ size_t data_len = 0;
+ int i;
+ gss_iov_buffer_desc *header = NULL;
+ gss_iov_buffer_desc *padding = NULL;
+ gss_iov_buffer_desc *trailer = NULL;
+
+ *minor_status = 0;
+
+ for (i = 0; i < iov_count; i++) {
+ switch(GSS_IOV_BUFFER_TYPE(iov[i].type)) {
+ case GSS_IOV_BUFFER_TYPE_EMPTY:
+ break;
+ case GSS_IOV_BUFFER_TYPE_DATA:
+ data_len += iov[i].buffer.length;
+ break;
+ case GSS_IOV_BUFFER_TYPE_HEADER:
+ if (header != NULL) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ header = &iov[i];
+ break;
+ case GSS_IOV_BUFFER_TYPE_TRAILER:
+ if (trailer != NULL) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ trailer = &iov[i];
+ break;
+ case GSS_IOV_BUFFER_TYPE_PADDING:
+ if (padding != NULL) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ padding = &iov[i];
+ break;
+ case GSS_IOV_BUFFER_TYPE_SIGN_ONLY:
+ break;
+ default:
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+ }
+
+ major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
+ if (major_status != GSS_S_COMPLETE) {
+ return major_status;
+ }
+
+ if (IS_DCE_STYLE(ctx)) {
+ size_t len = GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ size_t total_len;
+ _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+ header->buffer.length = total_len;
+ } else {
+ size_t len;
+ size_t total_len;
+ if (padding) {
+ data_len += 1; /* padding */
+ }
+ len = data_len + GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ _gssapi_encap_length(len, &len, &total_len, GSS_KRB5_MECHANISM);
+ header->buffer.length = total_len - data_len;
+ }
+
+ if (trailer) {
+ trailer->buffer.length = 0;
+ }
+
+ if (padding) {
+ padding->buffer.length = 1;
+ }
+
+ return GSS_S_COMPLETE;
+}
+
+OM_uint32
+_gssapi_wrap_iov_arcfour(OM_uint32 *minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ int conf_req_flag,
+ int *conf_state,
+ gss_iov_buffer_desc *iov,
+ int iov_count,
+ krb5_keyblock *key)
+{
+ OM_uint32 major_status, junk;
+ gss_iov_buffer_desc *header, *padding, *trailer;
+ krb5_error_code kret;
+ int32_t seq_number;
+ u_char Klocaldata[16], k6_data[16], *p, *p0;
+ size_t make_len = 0;
+ size_t header_len = 0;
+ size_t data_len = 0;
+ krb5_keyblock Klocal;
+ int i;
+
+ header = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
+ padding = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
+ trailer = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
+
+ major_status = _gk_verify_buffers(minor_status, ctx, header, padding, trailer);
+ if (major_status != GSS_S_COMPLETE) {
+ return major_status;
+ }
+
+ for (i = 0; i < iov_count; i++) {
+ switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
+ case GSS_IOV_BUFFER_TYPE_DATA:
+ break;
+ default:
+ continue;
+ }
+
+ data_len += iov[i].buffer.length;
+ }
+
+ if (padding) {
+ data_len += 1;
+ }
+
+ if (IS_DCE_STYLE(ctx)) {
+ size_t unwrapped_len;
+ unwrapped_len = GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+ _gssapi_encap_length(unwrapped_len,
+ &make_len,
+ &header_len,
+ GSS_KRB5_MECHANISM);
+ } else {
+ size_t unwrapped_len;
+ unwrapped_len = GSS_ARCFOUR_WRAP_TOKEN_SIZE + data_len;
+ _gssapi_encap_length(unwrapped_len,
+ &make_len,
+ &header_len,
+ GSS_KRB5_MECHANISM);
+ header_len -= data_len;
+ }
+
+ if (GSS_IOV_BUFFER_FLAGS(header->type) & GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE) {
+ major_status = _gk_allocate_buffer(minor_status, header,
+ header_len);
+ if (major_status != GSS_S_COMPLETE)
+ goto failure;
+ } else if (header->buffer.length < header_len) {
+ *minor_status = KRB5_BAD_MSIZE;
+ major_status = GSS_S_FAILURE;
+ goto failure;
+ } else {
+ header->buffer.length = header_len;
+ }
+
+ if (padding) {
+ if (GSS_IOV_BUFFER_FLAGS(padding->type) & GSS_IOV_BUFFER_TYPE_FLAG_ALLOCATE) {
+ major_status = _gk_allocate_buffer(minor_status, padding, 1);
+ if (major_status != GSS_S_COMPLETE)
+ goto failure;
+ } else if (padding->buffer.length < 1) {
+ *minor_status = KRB5_BAD_MSIZE;
+ major_status = GSS_S_FAILURE;
+ goto failure;
+ } else {
+ padding->buffer.length = 1;
+ }
+ memset(padding->buffer.value, 1, 1);
+ }
+
+ if (trailer) {
+ trailer->buffer.length = 0;
+ trailer->buffer.value = NULL;
+ }
+
+ p0 = _gssapi_make_mech_header(header->buffer.value,
+ make_len,
+ GSS_KRB5_MECHANISM);
+ p = p0;
+
+ *p++ = 0x02; /* TOK_ID */
+ *p++ = 0x01;
+ *p++ = 0x11; /* SGN_ALG */
+ *p++ = 0x00;
+ if (conf_req_flag) {
+ *p++ = 0x10; /* SEAL_ALG */
+ *p++ = 0x00;
+ } else {
+ *p++ = 0xff; /* SEAL_ALG */
+ *p++ = 0xff;
+ }
+ *p++ = 0xff; /* Filler */
+ *p++ = 0xff;
+
+ p = NULL;
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ krb5_auth_con_getlocalseqnumber(context,
+ ctx->auth_context,
+ &seq_number);
+ _gsskrb5_encode_be_om_uint32(seq_number, p0 + 8);
+
+ krb5_auth_con_setlocalseqnumber(context,
+ ctx->auth_context,
+ ++seq_number);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+
+ memset(p0 + 8 + 4,
+ (ctx->more_flags & LOCAL) ? 0 : 0xff,
+ 4);
+
+ krb5_generate_random_block(p0 + 24, 8); /* fill in Confounder */
+
+ /* Sign Data */
+ kret = arcfour_mic_cksum_iov(context,
+ key, KRB5_KU_USAGE_SEAL,
+ p0 + 16, 8, /* SGN_CKSUM */
+ p0, 8, /* TOK_ID, SGN_ALG, SEAL_ALG, Filler */
+ p0 + 24, 8, /* Confounder */
+ iov, iov_count, /* Data + SignOnly */
+ padding); /* padding */
+ if (kret) {
+ *minor_status = kret;
+ major_status = GSS_S_FAILURE;
+ goto failure;
+ }
+
+ Klocal.keytype = key->keytype;
+ Klocal.keyvalue.data = Klocaldata;
+ Klocal.keyvalue.length = sizeof(Klocaldata);
+
+ for (i = 0; i < 16; i++) {
+ Klocaldata[i] = ((u_char *)key->keyvalue.data)[i] ^ 0xF0;
+ }
+ kret = arcfour_mic_key(context, &Klocal,
+ p0 + 8, 4, /* SND_SEQ */
+ k6_data, sizeof(k6_data));
+ memset(Klocaldata, 0, sizeof(Klocaldata));
+ if (kret) {
+ *minor_status = kret;
+ major_status = GSS_S_FAILURE;
+ goto failure;
+ }
+
+ if (conf_req_flag) {
+ EVP_CIPHER_CTX rc4_key;
+
+ EVP_CIPHER_CTX_init(&rc4_key);
+ EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1);
+
+ /* Confounder */
+ EVP_Cipher(&rc4_key, p0 + 24, p0 + 24, 8);
+
+ /* Seal Data */
+ for (i=0; i < iov_count; i++) {
+ switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
+ case GSS_IOV_BUFFER_TYPE_DATA:
+ break;
+ default:
+ continue;
+ }
+
+ EVP_Cipher(&rc4_key, iov[i].buffer.value,
+ iov[i].buffer.value, iov[i].buffer.length);
+ }
+
+ /* Padding */
+ if (padding) {
+ EVP_Cipher(&rc4_key, padding->buffer.value,
+ padding->buffer.value, padding->buffer.length);
+ }
+
+ EVP_CIPHER_CTX_cleanup(&rc4_key);
+ }
+ memset(k6_data, 0, sizeof(k6_data));
+
+ kret = arcfour_mic_key(context, key,
+ p0 + 16, 8, /* SGN_CKSUM */
+ k6_data, sizeof(k6_data));
+ if (kret) {
+ *minor_status = kret;
+ major_status = GSS_S_FAILURE;
+ return major_status;
+ }
+
+ {
+ EVP_CIPHER_CTX rc4_key;
+
+ EVP_CIPHER_CTX_init(&rc4_key);
+ EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1);
+ EVP_Cipher(&rc4_key, p0 + 8, p0 + 8, 8); /* SND_SEQ */
+ EVP_CIPHER_CTX_cleanup(&rc4_key);
+
+ memset(k6_data, 0, sizeof(k6_data));
+ }
+
+ if (conf_state)
+ *conf_state = conf_req_flag;
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+
+failure:
+
+ gss_release_iov_buffer(&junk, iov, iov_count);
+
+ return major_status;
+}
+
+OM_uint32
+_gssapi_unwrap_iov_arcfour(OM_uint32 *minor_status,
+ gsskrb5_ctx ctx,
+ krb5_context context,
+ int *pconf_state,
+ gss_qop_t *pqop_state,
+ gss_iov_buffer_desc *iov,
+ int iov_count,
+ krb5_keyblock *key)
+{
+ OM_uint32 major_status;
+ gss_iov_buffer_desc *header, *padding, *trailer;
+ krb5_keyblock Klocal;
+ uint8_t Klocaldata[16];
+ uint8_t k6_data[16], snd_seq[8], Confounder[8];
+ uint8_t cksum_data[8];
+ uint8_t *_p = NULL;
+ const uint8_t *p, *p0;
+ size_t verify_len = 0;
+ uint32_t seq_number;
+ size_t hlen = 0;
+ int conf_state;
+ int cmp;
+ size_t i;
+ krb5_error_code kret;
+ OM_uint32 ret;
+
+ if (pconf_state != NULL) {
+ *pconf_state = 0;
+ }
+ if (pqop_state != NULL) {
+ *pqop_state = 0;
+ }
+
+ header = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_HEADER);
+ padding = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_PADDING);
+ trailer = _gk_find_buffer(iov, iov_count, GSS_IOV_BUFFER_TYPE_TRAILER);
+
+ /* Check if the packet is correct */
+ major_status = _gk_verify_buffers(minor_status,
+ ctx,
+ header,
+ padding,
+ trailer);
+ if (major_status != GSS_S_COMPLETE) {
+ return major_status;
+ }
+
+ if (padding != NULL && padding->buffer.length != 1) {
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
+ }
+
+ if (IS_DCE_STYLE(context)) {
+ verify_len = GSS_ARCFOUR_WRAP_TOKEN_SIZE +
+ GSS_ARCFOUR_WRAP_TOKEN_DCE_DER_HEADER_SIZE;
+ if (header->buffer.length > verify_len) {
+ return GSS_S_BAD_MECH;
+ }
+ } else {
+ verify_len = header->buffer.length;
+ }
+ _p = header->buffer.value;
+
+ ret = _gssapi_verify_mech_header(&_p,
+ verify_len,
+ GSS_KRB5_MECHANISM);
+ if (ret) {
+ return ret;
+ }
+ p0 = _p;
+
+ /* length of mech header */
+ hlen = (p0 - (uint8_t *)header->buffer.value);
+ hlen += GSS_ARCFOUR_WRAP_TOKEN_SIZE;
+
+ if (hlen > header->buffer.length) {
+ return GSS_S_BAD_MECH;
+ }
+
+ p = p0;
+
+ if (memcmp(p, "\x02\x01", 2) != 0)
+ return GSS_S_BAD_SIG;
+ p += 2;
+ if (memcmp(p, "\x11\x00", 2) != 0) /* SGN_ALG = HMAC MD5 ARCFOUR */
+ return GSS_S_BAD_SIG;
+ p += 2;
+
+ if (memcmp (p, "\x10\x00", 2) == 0)
+ conf_state = 1;
+ else if (memcmp (p, "\xff\xff", 2) == 0)
+ conf_state = 0;
+ else
+ return GSS_S_BAD_SIG;
+
+ p += 2;
+ if (memcmp (p, "\xff\xff", 2) != 0)
+ return GSS_S_BAD_MIC;
+ p = NULL;
+
+ kret = arcfour_mic_key(context,
+ key,
+ p0 + 16, /* SGN_CKSUM */
+ 8, /* SGN_CKSUM_LEN */
+ k6_data,
+ sizeof(k6_data));
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ {
+ EVP_CIPHER_CTX rc4_key;
+
+ EVP_CIPHER_CTX_init(&rc4_key);
+ EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1);
+ EVP_Cipher(&rc4_key, snd_seq, p0 + 8, 8); /* SND_SEQ */
+ EVP_CIPHER_CTX_cleanup(&rc4_key);
+
+ memset(k6_data, 0, sizeof(k6_data));
+ }
+
+ _gsskrb5_decode_be_om_uint32(snd_seq, &seq_number);
+
+ if (ctx->more_flags & LOCAL) {
+ cmp = memcmp(&snd_seq[4], "\xff\xff\xff\xff", 4);
+ } else {
+ cmp = memcmp(&snd_seq[4], "\x00\x00\x00\x00", 4);
+ }
+ if (cmp != 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_MIC;
+ }
+
+ if (ctx->more_flags & LOCAL) {
+ cmp = memcmp(&snd_seq[4], "\xff\xff\xff\xff", 4);
+ } else {
+ cmp = memcmp(&snd_seq[4], "\x00\x00\x00\x00", 4);
+ }
+ if (cmp != 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_MIC;
+ }
+
+ /* keyblock */
+ Klocal.keytype = key->keytype;
+ Klocal.keyvalue.data = Klocaldata;
+ Klocal.keyvalue.length = sizeof(Klocaldata);
+
+ for (i = 0; i < 16; i++) {
+ Klocaldata[i] = ((u_char *)key->keyvalue.data)[i] ^ 0xF0;
+ }
+
+ kret = arcfour_mic_key(context,
+ &Klocal,
+ snd_seq,
+ 4,
+ k6_data, sizeof(k6_data));
+ memset(Klocaldata, 0, sizeof(Klocaldata));
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ if (conf_state == 1) {
+ EVP_CIPHER_CTX rc4_key;
+
+ EVP_CIPHER_CTX_init(&rc4_key);
+ EVP_CipherInit_ex(&rc4_key, EVP_rc4(), NULL, k6_data, NULL, 1);
+
+ /* Confounder */
+ EVP_Cipher(&rc4_key, Confounder, p0 + 24, 8);
+
+ /* Data */
+ for (i = 0; i < iov_count; i++) {
+ switch (GSS_IOV_BUFFER_TYPE(iov[i].type)) {
+ case GSS_IOV_BUFFER_TYPE_DATA:
+ break;
+ default:
+ continue;
+ }
+
+ EVP_Cipher(&rc4_key, iov[i].buffer.value,
+ iov[i].buffer.value, iov[i].buffer.length);
+ }
+
+ /* Padding */
+ if (padding) {
+ EVP_Cipher(&rc4_key, padding->buffer.value,
+ padding->buffer.value, padding->buffer.length);
+ }
+
+ EVP_CIPHER_CTX_cleanup(&rc4_key);
+ } else {
+ /* Confounder */
+ memcpy(Confounder, p0 + 24, 8);
+ }
+ memset(k6_data, 0, sizeof(k6_data));
+
+ /* Prepare the buffer for signing */
+ kret = arcfour_mic_cksum_iov(context,
+ key, KRB5_KU_USAGE_SEAL,
+ cksum_data, sizeof(cksum_data),
+ p0, 8,
+ Confounder, sizeof(Confounder),
+ iov, iov_count,
+ padding);
+ if (kret) {
+ *minor_status = kret;
+ return GSS_S_FAILURE;
+ }
+
+ cmp = memcmp(cksum_data, p0 + 16, 8); /* SGN_CKSUM */
+ if (cmp != 0) {
+ *minor_status = 0;
+ return GSS_S_BAD_MIC;
+ }
+
+ if (padding) {
+ size_t plen;
+
+ ret = _gssapi_verify_pad(&padding->buffer, 1, &plen);
+ if (ret) {
+ *minor_status = 0;
+ return ret;
+ }
+ }
+
+ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
+ ret = _gssapi_msg_order_check(ctx->order, seq_number);
+ HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (pconf_state) {
+ *pconf_state = conf_state;
+ }
+
+ *minor_status = 0;
+ return GSS_S_COMPLETE;
+}
diff --git a/lib/gssapi/krb5/authorize_localname.c b/lib/gssapi/krb5/authorize_localname.c
index 4bab062ac4ce..5621c1f9cfe0 100644
--- a/lib/gssapi/krb5/authorize_localname.c
+++ b/lib/gssapi/krb5/authorize_localname.c
@@ -34,7 +34,7 @@
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_authorize_localname(OM_uint32 *minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_const_buffer_t user_name,
gss_const_OID user_name_type)
{
diff --git a/lib/gssapi/krb5/canonicalize_name.c b/lib/gssapi/krb5/canonicalize_name.c
index 7fc921bac095..62de42358f62 100644
--- a/lib/gssapi/krb5/canonicalize_name.c
+++ b/lib/gssapi/krb5/canonicalize_name.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_canonicalize_name (
OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
const gss_OID mech_type,
gss_name_t * output_name
)
@@ -48,7 +48,7 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_canonicalize_name (
GSSAPI_KRB5_INIT (&context);
- ret = _gsskrb5_canon_name(minor_status, context, 1, NULL, input_name, &name);
+ ret = _gsskrb5_canon_name(minor_status, context, input_name, &name);
if (ret)
return ret;
diff --git a/lib/gssapi/krb5/cfx.c b/lib/gssapi/krb5/cfx.c
index 3c1536b60ea5..29fecca861ce 100644
--- a/lib/gssapi/krb5/cfx.c
+++ b/lib/gssapi/krb5/cfx.c
@@ -391,7 +391,6 @@ _gssapi_wrap_cfx_iov(OM_uint32 *minor_status,
if (IS_DCE_STYLE(ctx))
rrc -= ec;
gsshsize += gsstsize;
- gsstsize = 0;
} else if (GSS_IOV_BUFFER_FLAGS(trailer->type) & GSS_IOV_BUFFER_FLAG_ALLOCATE) {
major_status = _gk_allocate_buffer(minor_status, trailer, gsstsize);
if (major_status)
@@ -683,6 +682,7 @@ unrotate_iov(OM_uint32 *minor_status, size_t rrc, gss_iov_buffer_desc *iov, int
if (iov[i].buffer.length <= skip) {
skip -= iov[i].buffer.length;
} else {
+ /* copy back to original buffer */
memcpy(((uint8_t *)iov[i].buffer.value) + skip, q, iov[i].buffer.length - skip);
q += iov[i].buffer.length - skip;
skip = 0;
@@ -697,13 +697,14 @@ unrotate_iov(OM_uint32 *minor_status, size_t rrc, gss_iov_buffer_desc *iov, int
GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_PADDING ||
GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_TRAILER)
{
- memcpy(q, iov[i].buffer.value, min(iov[i].buffer.length, skip));
+ memcpy(iov[i].buffer.value, q, min(iov[i].buffer.length, skip));
if (iov[i].buffer.length > skip)
break;
skip -= iov[i].buffer.length;
q += iov[i].buffer.length;
}
}
+ free(p);
return GSS_S_COMPLETE;
}
@@ -930,7 +931,6 @@ _gssapi_unwrap_cfx_iov(OM_uint32 *minor_status,
}
gsshsize += gsstsize;
- gsstsize = 0;
} else if (trailer->buffer.length != gsstsize) {
major_status = GSS_S_DEFECTIVE_TOKEN;
goto failure;
diff --git a/lib/gssapi/krb5/compare_name.c b/lib/gssapi/krb5/compare_name.c
index 7409d45fcb89..4a37e877df5d 100644
--- a/lib/gssapi/krb5/compare_name.c
+++ b/lib/gssapi/krb5/compare_name.c
@@ -35,8 +35,8 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_compare_name
(OM_uint32 * minor_status,
- const gss_name_t name1,
- const gss_name_t name2,
+ gss_const_name_t name1,
+ gss_const_name_t name2,
int * name_equal
)
{
diff --git a/lib/gssapi/krb5/context_time.c b/lib/gssapi/krb5/context_time.c
index cb1550011cd7..58249cb5a311 100644
--- a/lib/gssapi/krb5/context_time.c
+++ b/lib/gssapi/krb5/context_time.c
@@ -36,27 +36,28 @@
OM_uint32
_gsskrb5_lifetime_left(OM_uint32 *minor_status,
krb5_context context,
- OM_uint32 lifetime,
+ OM_uint32 endtime,
OM_uint32 *lifetime_rec)
{
- krb5_timestamp timeret;
+ krb5_timestamp now;
krb5_error_code kret;
- if (lifetime == 0) {
+ if (endtime == 0) {
*lifetime_rec = GSS_C_INDEFINITE;
return GSS_S_COMPLETE;
}
- kret = krb5_timeofday(context, &timeret);
+ kret = krb5_timeofday(context, &now);
if (kret) {
+ *lifetime_rec = 0;
*minor_status = kret;
return GSS_S_FAILURE;
}
- if (lifetime < timeret)
+ if (endtime < now)
*lifetime_rec = 0;
else
- *lifetime_rec = lifetime - timeret;
+ *lifetime_rec = endtime - now;
return GSS_S_COMPLETE;
}
@@ -64,23 +65,23 @@ _gsskrb5_lifetime_left(OM_uint32 *minor_status,
OM_uint32 GSSAPI_CALLCONV _gsskrb5_context_time
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
OM_uint32 * time_rec
)
{
krb5_context context;
- OM_uint32 lifetime;
+ OM_uint32 endtime;
OM_uint32 major_status;
const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
GSSAPI_KRB5_INIT (&context);
HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
- lifetime = ctx->lifetime;
+ endtime = ctx->endtime;
HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
major_status = _gsskrb5_lifetime_left(minor_status, context,
- lifetime, time_rec);
+ endtime, time_rec);
if (major_status != GSS_S_COMPLETE)
return major_status;
diff --git a/lib/gssapi/krb5/copy_ccache.c b/lib/gssapi/krb5/copy_ccache.c
index e332d29c84aa..14296bccd7e2 100644
--- a/lib/gssapi/krb5/copy_ccache.c
+++ b/lib/gssapi/krb5/copy_ccache.c
@@ -89,6 +89,8 @@ _gsskrb5_krb5_import_cred(OM_uint32 *minor_status,
handle->usage = 0;
if (id) {
+ time_t now;
+ OM_uint32 left;
char *str;
handle->usage |= GSS_C_INITIATE;
@@ -116,17 +118,18 @@ _gsskrb5_krb5_import_cred(OM_uint32 *minor_status,
}
}
+ krb5_timeofday(context, &now);
ret = __gsskrb5_ccache_lifetime(minor_status,
context,
id,
handle->principal,
- &handle->lifetime);
+ &left);
if (ret != GSS_S_COMPLETE) {
krb5_free_principal(context, handle->principal);
free(handle);
return ret;
}
-
+ handle->endtime = now + left;
kret = krb5_cc_get_full_name(context, id, &str);
if (kret)
diff --git a/lib/gssapi/krb5/creds.c b/lib/gssapi/krb5/creds.c
index fa45d19b9812..1cc3ac848ad0 100644
--- a/lib/gssapi/krb5/creds.c
+++ b/lib/gssapi/krb5/creds.c
@@ -62,6 +62,9 @@ _gsskrb5_export_cred(OM_uint32 *minor_status,
type = krb5_cc_get_type(context, handle->ccache);
if (strcmp(type, "MEMORY") == 0) {
krb5_creds *creds;
+ krb5_data config_start_realm;
+ char *start_realm;
+
ret = krb5_store_uint32(sp, 0);
if (ret) {
krb5_storage_free(sp);
@@ -69,9 +72,25 @@ _gsskrb5_export_cred(OM_uint32 *minor_status,
return GSS_S_FAILURE;
}
- ret = _krb5_get_krbtgt(context, handle->ccache,
- handle->principal->realm,
- &creds);
+ ret = krb5_cc_get_config(context, handle->ccache, NULL, "start_realm",
+ &config_start_realm);
+ if (ret == 0) {
+ start_realm = strndup(config_start_realm.data,
+ config_start_realm.length);
+ krb5_data_free(&config_start_realm);
+ } else {
+ start_realm = strdup(krb5_principal_get_realm(context,
+ handle->principal));
+ }
+ if (start_realm == NULL) {
+ *minor_status = krb5_enomem(context);
+ krb5_storage_free(sp);
+ return GSS_S_FAILURE;
+ }
+
+ ret = _krb5_get_krbtgt(context, handle->ccache, start_realm, &creds);
+ free(start_realm);
+ start_realm = NULL;
if (ret) {
krb5_storage_free(sp);
*minor_status = ret;
@@ -210,6 +229,10 @@ _gsskrb5_import_cred(OM_uint32 * minor_status,
ret = krb5_cc_store_cred(context, id, &creds);
krb5_free_cred_contents(context, &creds);
+ if (ret) {
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
diff --git a/lib/gssapi/krb5/decapsulate.c b/lib/gssapi/krb5/decapsulate.c
index 640c064d0bf1..86085f569501 100644
--- a/lib/gssapi/krb5/decapsulate.c
+++ b/lib/gssapi/krb5/decapsulate.c
@@ -190,6 +190,9 @@ _gssapi_verify_pad(gss_buffer_t wrapped_token,
size_t padlength;
int i;
+ if (wrapped_token->length < 1)
+ return GSS_S_BAD_MECH;
+
pad = (u_char *)wrapped_token->value + wrapped_token->length - 1;
padlength = *pad;
diff --git a/lib/gssapi/krb5/display_name.c b/lib/gssapi/krb5/display_name.c
index a296399cecad..67cb61e7cb81 100644
--- a/lib/gssapi/krb5/display_name.c
+++ b/lib/gssapi/krb5/display_name.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_display_name
(OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t output_name_buffer,
gss_OID * output_name_type
)
diff --git a/lib/gssapi/krb5/duplicate_name.c b/lib/gssapi/krb5/duplicate_name.c
index 0bc57e8a03ed..43519d6a2d53 100644
--- a/lib/gssapi/krb5/duplicate_name.c
+++ b/lib/gssapi/krb5/duplicate_name.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_duplicate_name (
OM_uint32 * minor_status,
- const gss_name_t src_name,
+ gss_const_name_t src_name,
gss_name_t * dest_name
)
{
diff --git a/lib/gssapi/krb5/export_name.c b/lib/gssapi/krb5/export_name.c
index 32368d3ccefd..1686a6570af5 100644
--- a/lib/gssapi/krb5/export_name.c
+++ b/lib/gssapi/krb5/export_name.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_export_name
(OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t exported_name
)
{
diff --git a/lib/gssapi/krb5/export_sec_context.c b/lib/gssapi/krb5/export_sec_context.c
index eeb2743b4322..b500f4230cd3 100644
--- a/lib/gssapi/krb5/export_sec_context.c
+++ b/lib/gssapi/krb5/export_sec_context.c
@@ -34,9 +34,9 @@
#include "gsskrb5_locl.h"
OM_uint32 GSSAPI_CALLCONV
-_gsskrb5_export_sec_context (
- OM_uint32 * minor_status,
- gss_ctx_id_t * context_handle,
+_gsskrb5_export_sec_context(
+ OM_uint32 *minor_status,
+ gss_ctx_id_t *context_handle,
gss_buffer_t interprocess_token
)
{
@@ -204,7 +204,11 @@ _gsskrb5_export_sec_context (
*minor_status = kret;
goto failure;
}
- kret = krb5_store_int32 (sp, ctx->lifetime);
+ /*
+ * XXX We should put a 64-bit int here, but we don't have a
+ * krb5_store_int64() yet.
+ */
+ kret = krb5_store_int32 (sp, ctx->endtime);
if (kret) {
*minor_status = kret;
goto failure;
diff --git a/lib/gssapi/krb5/external.c b/lib/gssapi/krb5/external.c
index 26ede2487d30..deae016bc96b 100644
--- a/lib/gssapi/krb5/external.c
+++ b/lib/gssapi/krb5/external.c
@@ -202,67 +202,131 @@ static gss_mo_desc krb5_mo[] = {
},
{
GSS_C_MA_MECH_CONCRETE,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_ITOK_FRAMED,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_AUTH_INIT,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_AUTH_TARG,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_AUTH_INIT_ANON,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_DELEG_CRED,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_INTEG_PROT,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_CONF_PROT,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_MIC,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_WRAP,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_PROT_READY,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_REPLAY_DET,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_OOS_DET,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_CBINDINGS,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_PFS,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_CTX_TRANS,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
}
};
@@ -324,7 +388,7 @@ static gssapi_mech_interface_desc krb5_mech = {
NULL,
krb5_mo,
sizeof(krb5_mo) / sizeof(krb5_mo[0]),
- _gsskrb5_pname_to_uid,
+ _gsskrb5_localname,
_gsskrb5_authorize_localname,
NULL,
NULL,
diff --git a/lib/gssapi/krb5/get_mic.c b/lib/gssapi/krb5/get_mic.c
index 0109ca7c6e76..643385d9e892 100644
--- a/lib/gssapi/krb5/get_mic.c
+++ b/lib/gssapi/krb5/get_mic.c
@@ -275,7 +275,7 @@ mic_des3
OM_uint32 GSSAPI_CALLCONV _gsskrb5_get_mic
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_qop_t qop_req,
const gss_buffer_t message_buffer,
gss_buffer_t message_token
@@ -285,7 +285,6 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_get_mic
const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
krb5_keyblock *key;
OM_uint32 ret;
- krb5_keytype keytype;
GSSAPI_KRB5_INIT (&context);
@@ -300,10 +299,11 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_get_mic
*minor_status = ret;
return GSS_S_FAILURE;
}
- krb5_enctype_to_keytype (context, key->keytype, &keytype);
- switch (keytype) {
- case KEYTYPE_DES :
+ switch (key->keytype) {
+ case KRB5_ENCTYPE_DES_CBC_CRC :
+ case KRB5_ENCTYPE_DES_CBC_MD4 :
+ case KRB5_ENCTYPE_DES_CBC_MD5 :
#ifdef HEIM_WEAK_CRYPTO
ret = mic_des (minor_status, ctx, context, qop_req,
message_buffer, message_token, key);
@@ -311,12 +311,13 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_get_mic
ret = GSS_S_FAILURE;
#endif
break;
- case KEYTYPE_DES3 :
+ case KRB5_ENCTYPE_DES3_CBC_MD5 :
+ case KRB5_ENCTYPE_DES3_CBC_SHA1 :
ret = mic_des3 (minor_status, ctx, context, qop_req,
message_buffer, message_token, key);
break;
- case KEYTYPE_ARCFOUR:
- case KEYTYPE_ARCFOUR_56:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56:
ret = _gssapi_get_mic_arcfour (minor_status, ctx, context, qop_req,
message_buffer, message_token, key);
break;
diff --git a/lib/gssapi/krb5/gsskrb5-private.h b/lib/gssapi/krb5/gsskrb5-private.h
index 2a669d867f16..e05d4a6b6da6 100644
--- a/lib/gssapi/krb5/gsskrb5-private.h
+++ b/lib/gssapi/krb5/gsskrb5-private.h
@@ -179,6 +179,17 @@ _gssapi_unwrap_cfx_iov (
int /*iov_count*/);
OM_uint32
+_gssapi_unwrap_iov_arcfour (
+ OM_uint32 */*minor_status*/,
+ gsskrb5_ctx /*ctx*/,
+ krb5_context /*context*/,
+ int */*pconf_state*/,
+ gss_qop_t */*pqop_state*/,
+ gss_iov_buffer_desc */*iov*/,
+ int /*iov_count*/,
+ krb5_keyblock */*key*/);
+
+OM_uint32
_gssapi_verify_mech_header (
u_char **/*str*/,
size_t /*total_len*/,
@@ -243,6 +254,28 @@ _gssapi_wrap_cfx_iov (
int /*iov_count*/);
OM_uint32
+_gssapi_wrap_iov_arcfour (
+ OM_uint32 */*minor_status*/,
+ gsskrb5_ctx /*ctx*/,
+ krb5_context /*context*/,
+ int /*conf_req_flag*/,
+ int */*conf_state*/,
+ gss_iov_buffer_desc */*iov*/,
+ int /*iov_count*/,
+ krb5_keyblock */*key*/);
+
+OM_uint32
+_gssapi_wrap_iov_length_arcfour (
+ OM_uint32 */*minor_status*/,
+ gsskrb5_ctx /*ctx*/,
+ krb5_context /*context*/,
+ int /*conf_req_flag*/,
+ gss_qop_t /*qop_req*/,
+ int */*conf_state*/,
+ gss_iov_buffer_desc */*iov*/,
+ int /*iov_count*/);
+
+OM_uint32
_gssapi_wrap_iov_length_cfx (
OM_uint32 */*minor_status*/,
gsskrb5_ctx /*ctx*/,
@@ -278,7 +311,7 @@ OM_uint32 GSSAPI_CALLCONV
_gsskrb5_accept_sec_context (
OM_uint32 * /*minor_status*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_cred_id_t /*acceptor_cred_handle*/,
+ gss_const_cred_id_t /*acceptor_cred_handle*/,
const gss_buffer_t /*input_token_buffer*/,
const gss_channel_bindings_t /*input_chan_bindings*/,
gss_name_t * /*src_name*/,
@@ -291,7 +324,7 @@ _gsskrb5_accept_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_acquire_cred (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*desired_name*/,
+ gss_const_name_t /*desired_name*/,
OM_uint32 /*time_req*/,
const gss_OID_set /*desired_mechs*/,
gss_cred_usage_t /*cred_usage*/,
@@ -302,7 +335,7 @@ _gsskrb5_acquire_cred (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_acquire_cred_ext (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*desired_name*/,
+ gss_const_name_t /*desired_name*/,
gss_const_OID /*credential_type*/,
const void */*credential_data*/,
OM_uint32 /*time_req*/,
@@ -313,8 +346,8 @@ _gsskrb5_acquire_cred_ext (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_add_cred (
OM_uint32 */*minor_status*/,
- const gss_cred_id_t /*input_cred_handle*/,
- const gss_name_t /*desired_name*/,
+ gss_const_cred_id_t /*input_cred_handle*/,
+ gss_const_name_t /*desired_name*/,
const gss_OID /*desired_mech*/,
gss_cred_usage_t /*cred_usage*/,
OM_uint32 /*initiator_time_req*/,
@@ -327,7 +360,7 @@ _gsskrb5_add_cred (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_authorize_localname (
OM_uint32 */*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_const_buffer_t /*user_name*/,
gss_const_OID /*user_name_type*/);
@@ -335,15 +368,13 @@ OM_uint32
_gsskrb5_canon_name (
OM_uint32 */*minor_status*/,
krb5_context /*context*/,
- int /*use_dns*/,
- krb5_const_principal /*sourcename*/,
- gss_name_t /*targetname*/,
+ gss_const_name_t /*targetname*/,
krb5_principal */*out*/);
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_canonicalize_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
const gss_OID /*mech_type*/,
gss_name_t * output_name );
@@ -353,14 +384,14 @@ _gsskrb5_clear_status (void);
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_compare_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*name1*/,
- const gss_name_t /*name2*/,
+ gss_const_name_t /*name1*/,
+ gss_const_name_t /*name2*/,
int * name_equal );
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_context_time (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
OM_uint32 * time_rec );
OM_uint32
@@ -406,7 +437,7 @@ _gsskrb5_delete_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_display_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t /*output_name_buffer*/,
gss_OID * output_name_type );
@@ -422,7 +453,7 @@ _gsskrb5_display_status (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_duplicate_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*src_name*/,
+ gss_const_name_t /*src_name*/,
gss_name_t * dest_name );
void
@@ -459,13 +490,13 @@ _gsskrb5_export_cred (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_export_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t exported_name );
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_export_sec_context (
- OM_uint32 * /*minor_status*/,
- gss_ctx_id_t * /*context_handle*/,
+ OM_uint32 */*minor_status*/,
+ gss_ctx_id_t */*context_handle*/,
gss_buffer_t interprocess_token );
ssize_t
@@ -477,7 +508,7 @@ _gsskrb5_get_mech (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_get_mic (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*message_buffer*/,
gss_buffer_t message_token );
@@ -518,9 +549,9 @@ _gsskrb5_init (krb5_context */*context*/);
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_init_sec_context (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_name_t /*target_name*/,
+ gss_const_name_t /*target_name*/,
const gss_OID /*mech_type*/,
OM_uint32 /*req_flags*/,
OM_uint32 /*time_req*/,
@@ -534,7 +565,7 @@ _gsskrb5_init_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_inquire_context (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_name_t * /*src_name*/,
gss_name_t * /*targ_name*/,
OM_uint32 * /*lifetime_rec*/,
@@ -546,7 +577,7 @@ _gsskrb5_inquire_context (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_inquire_cred (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
gss_name_t * /*output_name*/,
OM_uint32 * /*lifetime*/,
gss_cred_usage_t * /*cred_usage*/,
@@ -555,7 +586,7 @@ _gsskrb5_inquire_cred (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_inquire_cred_by_mech (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
const gss_OID /*mech_type*/,
gss_name_t * /*name*/,
OM_uint32 * /*initiator_lifetime*/,
@@ -565,14 +596,14 @@ _gsskrb5_inquire_cred_by_mech (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_inquire_cred_by_oid (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
const gss_OID /*desired_object*/,
gss_buffer_set_t */*data_set*/);
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_inquire_mechs_for_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_OID_set * mech_types );
OM_uint32 GSSAPI_CALLCONV
@@ -584,7 +615,7 @@ _gsskrb5_inquire_names_for_mech (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_inquire_sec_context_by_oid (
OM_uint32 */*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_OID /*desired_object*/,
gss_buffer_set_t */*data_set*/);
@@ -606,9 +637,16 @@ OM_uint32
_gsskrb5_lifetime_left (
OM_uint32 */*minor_status*/,
krb5_context /*context*/,
- OM_uint32 /*lifetime*/,
+ OM_uint32 /*endtime*/,
OM_uint32 */*lifetime_rec*/);
+OM_uint32 GSSAPI_CALLCONV
+_gsskrb5_localname (
+ OM_uint32 */*minor_status*/,
+ gss_const_name_t /*pname*/,
+ const gss_OID /*mech_type*/,
+ gss_buffer_t /*localname*/);
+
void *
_gsskrb5_make_header (
void */*ptr*/,
@@ -617,16 +655,9 @@ _gsskrb5_make_header (
const gss_OID /*mech*/);
OM_uint32 GSSAPI_CALLCONV
-_gsskrb5_pname_to_uid (
- OM_uint32 */*minor_status*/,
- const gss_name_t /*pname*/,
- const gss_OID /*mech_type*/,
- uid_t */*uidp*/);
-
-OM_uint32 GSSAPI_CALLCONV
_gsskrb5_process_context_token (
OM_uint32 */*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t token_buffer );
OM_uint32 GSSAPI_CALLCONV
@@ -692,7 +723,7 @@ _gsskrb5_store_cred (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_unwrap (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*input_message_buffer*/,
gss_buffer_t /*output_message_buffer*/,
int * /*conf_state*/,
@@ -716,7 +747,7 @@ _gsskrb5_verify_header (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_verify_mic (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*message_buffer*/,
const gss_buffer_t /*token_buffer*/,
gss_qop_t * qop_state );
@@ -734,7 +765,7 @@ _gsskrb5_verify_mic_internal (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_wrap (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*input_message_buffer*/,
@@ -744,7 +775,7 @@ _gsskrb5_wrap (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_wrap_size_limit (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
OM_uint32 /*req_output_size*/,
diff --git a/lib/gssapi/krb5/gsskrb5_locl.h b/lib/gssapi/krb5/gsskrb5_locl.h
index 6b9b03f34908..4119730576fc 100644
--- a/lib/gssapi/krb5/gsskrb5_locl.h
+++ b/lib/gssapi/krb5/gsskrb5_locl.h
@@ -81,7 +81,7 @@ typedef struct gsskrb5_ctx {
krb5_creds *kcred;
krb5_ccache ccache;
struct krb5_ticket *ticket;
- OM_uint32 lifetime;
+ time_t endtime;
HEIMDAL_MUTEX ctx_id_mutex;
struct gss_msg_order *order;
krb5_keyblock *service_keyblock;
@@ -95,7 +95,7 @@ typedef struct {
#define GSS_CF_DESTROY_CRED_ON_RELEASE 1
#define GSS_CF_NO_CI_FLAGS 2
struct krb5_keytab_data *keytab;
- OM_uint32 lifetime;
+ time_t endtime;
gss_cred_usage_t usage;
gss_OID_set mechanisms;
struct krb5_ccache_data *ccache;
@@ -134,7 +134,4 @@ extern HEIMDAL_MUTEX gssapi_keytab_mutex;
#define SC_LOCAL_SUBKEY 0x08
#define SC_REMOTE_SUBKEY 0x10
-/* type to signal that that dns canon maybe should be done */
-#define MAGIC_HOSTBASED_NAME_TYPE 4711
-
#endif
diff --git a/lib/gssapi/krb5/import_name.c b/lib/gssapi/krb5/import_name.c
index 5fe512672f95..6a362640b6fe 100644
--- a/lib/gssapi/krb5/import_name.c
+++ b/lib/gssapi/krb5/import_name.c
@@ -83,26 +83,21 @@ import_krb5_name (OM_uint32 *minor_status,
OM_uint32
_gsskrb5_canon_name(OM_uint32 *minor_status, krb5_context context,
- int use_dns, krb5_const_principal sourcename, gss_name_t targetname,
- krb5_principal *out)
+ gss_const_name_t targetname, krb5_principal *out)
{
- krb5_principal p = (krb5_principal)targetname;
+ krb5_const_principal p = (krb5_const_principal)targetname;
krb5_error_code ret;
char *hostname = NULL, *service;
+ int type;
+ const char *comp;
*minor_status = 0;
/* If its not a hostname */
- if (krb5_principal_get_type(context, p) != MAGIC_HOSTBASED_NAME_TYPE) {
- ret = krb5_copy_principal(context, p, out);
- } else if (!use_dns) {
- ret = krb5_copy_principal(context, p, out);
- if (ret)
- goto out;
- krb5_principal_set_type(context, *out, KRB5_NT_SRV_HST);
- if (sourcename)
- ret = krb5_principal_set_realm(context, *out, sourcename->realm);
- } else {
+ type = krb5_principal_get_type(context, p);
+ comp = krb5_principal_get_comp_string(context, p, 0);
+ if (type == KRB5_NT_SRV_HST || type == KRB5_NT_SRV_HST_NEEDS_CANON ||
+ (type == KRB5_NT_UNKNOWN && comp != NULL && strcmp(comp, "host") == 0)) {
if (p->name.name_string.len == 0)
return GSS_S_BAD_NAME;
else if (p->name.name_string.len > 1)
@@ -115,9 +110,10 @@ _gsskrb5_canon_name(OM_uint32 *minor_status, krb5_context context,
service,
KRB5_NT_SRV_HST,
out);
+ } else {
+ ret = krb5_copy_principal(context, p, out);
}
- out:
if (ret) {
*minor_status = ret;
return GSS_S_FAILURE;
@@ -128,10 +124,10 @@ _gsskrb5_canon_name(OM_uint32 *minor_status, krb5_context context,
static OM_uint32
-import_hostbased_name (OM_uint32 *minor_status,
- krb5_context context,
- const gss_buffer_t input_name_buffer,
- gss_name_t *output_name)
+import_hostbased_name(OM_uint32 *minor_status,
+ krb5_context context,
+ const gss_buffer_t input_name_buffer,
+ gss_name_t *output_name)
{
krb5_principal princ = NULL;
krb5_error_code kerr;
@@ -153,7 +149,7 @@ import_hostbased_name (OM_uint32 *minor_status,
host = p + 1;
}
- kerr = krb5_make_principal(context, &princ, NULL, tmp, host, NULL);
+ kerr = krb5_make_principal(context, &princ, "", tmp, host, NULL);
free (tmp);
*minor_status = kerr;
if (kerr == KRB5_PARSE_ILLCHAR || kerr == KRB5_PARSE_MALFORMED)
@@ -161,7 +157,7 @@ import_hostbased_name (OM_uint32 *minor_status,
else if (kerr)
return GSS_S_FAILURE;
- krb5_principal_set_type(context, princ, MAGIC_HOSTBASED_NAME_TYPE);
+ krb5_principal_set_type(context, princ, KRB5_NT_SRV_HST);
*output_name = (gss_name_t)princ;
return 0;
diff --git a/lib/gssapi/krb5/import_sec_context.c b/lib/gssapi/krb5/import_sec_context.c
index 3bab1802b3c9..e34e07115a5f 100644
--- a/lib/gssapi/krb5/import_sec_context.c
+++ b/lib/gssapi/krb5/import_sec_context.c
@@ -192,9 +192,13 @@ _gsskrb5_import_sec_context (
if (krb5_ret_int32 (sp, &tmp))
goto failure;
ctx->more_flags = tmp;
+ /*
+ * XXX endtime should be a 64-bit int, but we don't have
+ * krb5_ret_int64() yet.
+ */
if (krb5_ret_int32 (sp, &tmp))
goto failure;
- ctx->lifetime = tmp;
+ ctx->endtime = tmp;
ret = _gssapi_msg_order_import(minor_status, sp, &ctx->order);
if (ret)
diff --git a/lib/gssapi/krb5/init_sec_context.c b/lib/gssapi/krb5/init_sec_context.c
index 5f8b01b72707..4ef5c9c7123a 100644
--- a/lib/gssapi/krb5/init_sec_context.c
+++ b/lib/gssapi/krb5/init_sec_context.c
@@ -128,7 +128,7 @@ _gsskrb5_create_ctx(
ctx->service_keyblock = NULL;
ctx->ticket = NULL;
krb5_data_zero(&ctx->fwd_data);
- ctx->lifetime = GSS_C_INDEFINITE;
+ ctx->endtime = 0;
ctx->order = NULL;
ctx->crypto = NULL;
HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
@@ -137,6 +137,7 @@ _gsskrb5_create_ctx(
if (kret) {
*minor_status = kret;
HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
+ free(ctx);
return GSS_S_FAILURE;
}
@@ -145,6 +146,7 @@ _gsskrb5_create_ctx(
*minor_status = kret;
krb5_auth_con_free(context, ctx->auth_context);
HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
+ free(ctx);
return GSS_S_FAILURE;
}
@@ -156,7 +158,7 @@ _gsskrb5_create_ctx(
krb5_auth_con_free(context, ctx->deleg_auth_context);
HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
-
+ free(ctx);
return GSS_S_BAD_BINDINGS;
}
@@ -168,7 +170,7 @@ _gsskrb5_create_ctx(
krb5_auth_con_free(context, ctx->deleg_auth_context);
HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
-
+ free(ctx);
return GSS_S_BAD_BINDINGS;
}
@@ -204,8 +206,7 @@ gsskrb5_get_creds(
krb5_context context,
krb5_ccache ccache,
gsskrb5_ctx ctx,
- const gss_name_t target_name,
- int use_dns,
+ gss_const_name_t target_name,
OM_uint32 time_req,
OM_uint32 * time_rec)
{
@@ -223,8 +224,8 @@ gsskrb5_get_creds(
ctx->kcred = NULL;
}
- ret = _gsskrb5_canon_name(minor_status, context, use_dns,
- ctx->source, target_name, &ctx->target);
+ ret = _gsskrb5_canon_name(minor_status, context, target_name,
+ &ctx->target);
if (ret)
return ret;
@@ -253,10 +254,10 @@ gsskrb5_get_creds(
return GSS_S_FAILURE;
}
- ctx->lifetime = ctx->kcred->times.endtime;
+ ctx->endtime = ctx->kcred->times.endtime;
ret = _gsskrb5_lifetime_left(minor_status, context,
- ctx->lifetime, &lifetime_rec);
+ ctx->endtime, &lifetime_rec);
if (ret) return ret;
if (lifetime_rec == 0) {
@@ -344,8 +345,7 @@ do_delegation (krb5_context context,
fwd_flags.forwarded = 1;
fwd_flags.forwardable = 1;
- if ( /*target_name->name.name_type != KRB5_NT_SRV_HST ||*/
- name->name.name_string.len < 2)
+ if (name->name.name_string.len < 2)
goto out;
kret = krb5_get_forwarded_creds(context,
@@ -378,7 +378,7 @@ init_auth
gsskrb5_cred cred,
gsskrb5_ctx ctx,
krb5_context context,
- gss_name_t name,
+ gss_const_name_t name,
const gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
@@ -391,12 +391,9 @@ init_auth
{
OM_uint32 ret = GSS_S_FAILURE;
krb5_error_code kret;
- krb5_data outbuf;
krb5_data fwd_data;
OM_uint32 lifetime_rec;
- int allow_dns = 1;
- krb5_data_zero(&outbuf);
krb5_data_zero(&fwd_data);
*minor_status = 0;
@@ -425,44 +422,17 @@ init_auth
/*
* This is hideous glue for (NFS) clients that wants to limit the
* available enctypes to what it can support (encryption in
- * kernel). If there is no enctypes selected for this credential,
- * reset it to the default set of enctypes.
+ * kernel).
*/
- {
- krb5_enctype *enctypes = NULL;
+ if (cred && cred->enctypes)
+ krb5_set_default_in_tkt_etypes(context, cred->enctypes);
- if (cred && cred->enctypes)
- enctypes = cred->enctypes;
- krb5_set_default_in_tkt_etypes(context, enctypes);
- }
-
- /* canon name if needed for client + target realm */
- kret = krb5_cc_get_config(context, ctx->ccache, NULL,
- "realm-config", &outbuf);
- if (kret == 0) {
- /* XXX 2 is no server canon */
- if (outbuf.length < 1 || ((((unsigned char *)outbuf.data)[0]) & 2))
- allow_dns = 0;
- krb5_data_free(&outbuf);
- }
-
- /*
- * First we try w/o dns, hope that the KDC have register alias
- * (and referrals if cross realm) for this principal. If that
- * fails and if we are allowed to using this realm try again with
- * DNS canonicalizion.
- */
ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
- ctx, name, 0, time_req,
- time_rec);
- if (ret && allow_dns)
- ret = gsskrb5_get_creds(minor_status, context, ctx->ccache,
- ctx, name, 1, time_req,
- time_rec);
+ ctx, name, time_req, time_rec);
if (ret)
goto failure;
- ctx->lifetime = ctx->kcred->times.endtime;
+ ctx->endtime = ctx->kcred->times.endtime;
ret = _gss_DES3_get_mic_compat(minor_status, ctx, context);
if (ret)
@@ -470,7 +440,7 @@ init_auth
ret = _gsskrb5_lifetime_left(minor_status,
context,
- ctx->lifetime,
+ ctx->endtime,
&lifetime_rec);
if (ret)
goto failure;
@@ -817,14 +787,11 @@ repl_mutual
repl);
*minor_status = 0;
- if (time_rec) {
- ret = _gsskrb5_lifetime_left(minor_status,
- context,
- ctx->lifetime,
- time_rec);
- } else {
- ret = GSS_S_COMPLETE;
- }
+ if (time_rec)
+ _gsskrb5_lifetime_left(minor_status,
+ context,
+ ctx->endtime,
+ time_rec);
if (ret_flags)
*ret_flags = ctx->flags;
@@ -865,9 +832,9 @@ repl_mutual
OM_uint32 GSSAPI_CALLCONV _gsskrb5_init_sec_context
(OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
gss_ctx_id_t * context_handle,
- const gss_name_t target_name,
+ gss_const_name_t target_name,
const gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
diff --git a/lib/gssapi/krb5/inquire_context.c b/lib/gssapi/krb5/inquire_context.c
index ade8ec4b9cb6..e225c33ba2be 100644
--- a/lib/gssapi/krb5/inquire_context.c
+++ b/lib/gssapi/krb5/inquire_context.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_context (
OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_name_t * src_name,
gss_name_t * targ_name,
OM_uint32 * lifetime_rec,
@@ -76,7 +76,7 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_context (
if (lifetime_rec) {
ret = _gsskrb5_lifetime_left(minor_status,
context,
- ctx->lifetime,
+ ctx->endtime,
lifetime_rec);
if (ret)
goto failed;
diff --git a/lib/gssapi/krb5/inquire_cred.c b/lib/gssapi/krb5/inquire_cred.c
index f88199692cd7..1770fec76eab 100644
--- a/lib/gssapi/krb5/inquire_cred.c
+++ b/lib/gssapi/krb5/inquire_cred.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred
(OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
gss_name_t * output_name,
OM_uint32 * lifetime,
gss_cred_usage_t * cred_usage,
@@ -45,136 +45,179 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred
krb5_context context;
gss_cred_id_t aqcred_init = GSS_C_NO_CREDENTIAL;
gss_cred_id_t aqcred_accept = GSS_C_NO_CREDENTIAL;
- gsskrb5_cred acred = NULL, icred = NULL;
+ gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
+ gss_OID_set amechs = GSS_C_NO_OID_SET;
+ gss_OID_set imechs = GSS_C_NO_OID_SET;
+ OM_uint32 junk;
+ OM_uint32 aminor;
OM_uint32 ret;
+ OM_uint32 aret;
+ OM_uint32 alife = GSS_C_INDEFINITE;
+ OM_uint32 ilife = GSS_C_INDEFINITE;
+
+ /*
+ * XXX This function is more complex than it has to be. It should call
+ * _gsskrb5_inquire_cred_by_mech() twice and merge the results in the
+ * cred_handle == GSS_C_NO_CREDENTIAL case, but since
+ * _gsskrb5_inquire_cred_by_mech() is implemented in terms of this
+ * function, first we must fix _gsskrb5_inquire_cred_by_mech().
+ */
*minor_status = 0;
if (output_name)
- *output_name = NULL;
+ *output_name = GSS_C_NO_NAME;
+ if (cred_usage)
+ *cred_usage = GSS_C_BOTH; /* There's no NONE */
if (mechanisms)
- *mechanisms = GSS_C_NO_OID_SET;
+ *mechanisms = GSS_C_NO_OID_SET;
GSSAPI_KRB5_INIT (&context);
if (cred_handle == GSS_C_NO_CREDENTIAL) {
- ret = _gsskrb5_acquire_cred(minor_status,
- GSS_C_NO_NAME,
- GSS_C_INDEFINITE,
- GSS_C_NO_OID_SET,
- GSS_C_ACCEPT,
- &aqcred_accept,
- NULL,
- NULL);
- if (ret == GSS_S_COMPLETE)
- acred = (gsskrb5_cred)aqcred_accept;
-
- ret = _gsskrb5_acquire_cred(minor_status,
- GSS_C_NO_NAME,
- GSS_C_INDEFINITE,
- GSS_C_NO_OID_SET,
- GSS_C_INITIATE,
- &aqcred_init,
- NULL,
- NULL);
- if (ret == GSS_S_COMPLETE)
- icred = (gsskrb5_cred)aqcred_init;
-
- if (icred == NULL && acred == NULL) {
- *minor_status = 0;
- return GSS_S_NO_CRED;
- }
- } else
- acred = (gsskrb5_cred)cred_handle;
+ /*
+ * From here to the end of this if we should refactor into a separate
+ * function.
+ */
+ /* Get the info for the default ACCEPT credential */
+ aret = _gsskrb5_acquire_cred(&aminor,
+ GSS_C_NO_NAME,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ GSS_C_ACCEPT,
+ &aqcred_accept,
+ NULL,
+ NULL);
+ if (aret == GSS_S_COMPLETE) {
+ aret = _gsskrb5_inquire_cred(&aminor,
+ aqcred_accept,
+ output_name,
+ &alife,
+ NULL,
+ &amechs);
+ (void) _gsskrb5_release_cred(&junk, &aqcred_accept);
+ if (aret == GSS_S_COMPLETE) {
+ output_name = NULL; /* Can't merge names; output only one */
+ if (cred_usage)
+ *cred_usage = GSS_C_ACCEPT;
+ if (lifetime)
+ *lifetime = alife;
+ if (mechanisms) {
+ *mechanisms = amechs;
+ amechs = GSS_C_NO_OID_SET;
+ }
+ (void) gss_release_oid_set(&junk, &amechs);
+ } else if (aret != GSS_S_NO_CRED) {
+ *minor_status = aminor;
+ return aret;
+ } else {
+ alife = GSS_C_INDEFINITE;
+ }
+ }
+
+ /* Get the info for the default INITIATE credential */
+ ret = _gsskrb5_acquire_cred(minor_status,
+ GSS_C_NO_NAME,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ GSS_C_INITIATE,
+ &aqcred_init,
+ NULL,
+ NULL);
+ if (ret == GSS_S_COMPLETE) {
+ ret = _gsskrb5_inquire_cred(minor_status,
+ aqcred_init,
+ output_name,
+ &ilife,
+ NULL,
+ &imechs);
+ (void) _gsskrb5_release_cred(&junk, &aqcred_init);
+ if (ret == GSS_S_COMPLETE) {
+ /*
+ * Merge results for INITIATE with ACCEPT if we had ACCEPT and
+ * for those outputs that are desired.
+ */
+ if (cred_usage) {
+ *cred_usage = (*cred_usage == GSS_C_ACCEPT) ?
+ GSS_C_BOTH : GSS_C_INITIATE;
+ }
+ if (lifetime)
+ *lifetime = min(alife, ilife);
+ if (mechanisms) {
+ /*
+ * This is just one mechanism (IAKERB and such would live
+ * elsewhere). imechs will be equal to amechs, though not
+ * ==.
+ */
+ if (aret != GSS_S_COMPLETE) {
+ *mechanisms = imechs;
+ imechs = GSS_C_NO_OID_SET;
+ }
+ }
+ (void) gss_release_oid_set(&junk, &amechs);
+ } else if (ret != GSS_S_NO_CRED) {
+ *minor_status = aminor;
+ return aret;
+ }
+ }
+
+ if (aret != GSS_S_COMPLETE && ret != GSS_S_COMPLETE) {
+ *minor_status = aminor;
+ return aret;
+ }
+ *minor_status = 0; /* Even though 0 is not specified to be special */
+ return GSS_S_COMPLETE;
+ }
- if (acred)
- HEIMDAL_MUTEX_lock(&acred->cred_id_mutex);
- if (icred)
- HEIMDAL_MUTEX_lock(&icred->cred_id_mutex);
+ HEIMDAL_MUTEX_lock(&cred->cred_id_mutex);
if (output_name != NULL) {
- if (icred && icred->principal != NULL) {
- gss_name_t name;
-
- if (acred && acred->principal)
- name = (gss_name_t)acred->principal;
- else
- name = (gss_name_t)icred->principal;
-
+ if (cred->principal != NULL) {
+ gss_name_t name = (gss_name_t)cred->principal;
ret = _gsskrb5_duplicate_name(minor_status, name, output_name);
if (ret)
- goto out;
- } else if (acred && acred->usage == GSS_C_ACCEPT) {
- krb5_principal princ;
- *minor_status = krb5_sname_to_principal(context, NULL,
- NULL, KRB5_NT_SRV_HST,
- &princ);
- if (*minor_status) {
- ret = GSS_S_FAILURE;
- goto out;
- }
- *output_name = (gss_name_t)princ;
- } else {
- krb5_principal princ;
- *minor_status = krb5_get_default_principal(context,
- &princ);
- if (*minor_status) {
- ret = GSS_S_FAILURE;
- goto out;
- }
- *output_name = (gss_name_t)princ;
- }
+ goto out;
+ } else if (cred->usage == GSS_C_ACCEPT) {
+ /*
+ * Keytab case, princ may not be set (yet, ever, whatever).
+ *
+ * We used to unconditionally output the krb5_sname_to_principal()
+ * of the host service for the hostname, but we didn't know if we
+ * had keytab entries for it, so it was incorrect. We can't be
+ * breaking anything in tree by outputting GSS_C_NO_NAME, but we
+ * might be breaking other callers.
+ */
+ *output_name = GSS_C_NO_NAME;
+ } else {
+ /* This shouldn't happen */
+ *minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */
+ ret = GSS_S_NO_CRED;
+ goto out;
+ }
}
if (lifetime != NULL) {
- OM_uint32 alife = GSS_C_INDEFINITE, ilife = GSS_C_INDEFINITE;
-
- if (acred) alife = acred->lifetime;
- if (icred) ilife = icred->lifetime;
-
- ret = _gsskrb5_lifetime_left(minor_status,
- context,
- min(alife,ilife),
- lifetime);
- if (ret)
- goto out;
- }
- if (cred_usage != NULL) {
- if (acred && icred)
- *cred_usage = GSS_C_BOTH;
- else if (acred)
- *cred_usage = GSS_C_ACCEPT;
- else if (icred)
- *cred_usage = GSS_C_INITIATE;
- else
- abort();
+ ret = _gsskrb5_lifetime_left(minor_status,
+ context,
+ cred->endtime,
+ lifetime);
+ if (ret)
+ goto out;
}
-
+ if (cred_usage != NULL)
+ *cred_usage = cred->usage;
if (mechanisms != NULL) {
ret = gss_create_empty_oid_set(minor_status, mechanisms);
if (ret)
- goto out;
- if (acred)
- ret = gss_add_oid_set_member(minor_status,
- &acred->mechanisms->elements[0],
- mechanisms);
- if (ret == GSS_S_COMPLETE && icred)
- ret = gss_add_oid_set_member(minor_status,
- &icred->mechanisms->elements[0],
- mechanisms);
+ goto out;
+ ret = gss_add_oid_set_member(minor_status,
+ &cred->mechanisms->elements[0],
+ mechanisms);
if (ret)
- goto out;
+ goto out;
}
ret = GSS_S_COMPLETE;
-out:
- if (acred)
- HEIMDAL_MUTEX_unlock(&acred->cred_id_mutex);
- if (icred)
- HEIMDAL_MUTEX_unlock(&icred->cred_id_mutex);
-
- if (aqcred_init != GSS_C_NO_CREDENTIAL)
- ret = _gsskrb5_release_cred(minor_status, &aqcred_init);
- if (aqcred_accept != GSS_C_NO_CREDENTIAL)
- ret = _gsskrb5_release_cred(minor_status, &aqcred_accept);
+out:
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
return ret;
}
diff --git a/lib/gssapi/krb5/inquire_cred_by_mech.c b/lib/gssapi/krb5/inquire_cred_by_mech.c
index 7bd9c11c6056..6ce4994ebe08 100644
--- a/lib/gssapi/krb5/inquire_cred_by_mech.c
+++ b/lib/gssapi/krb5/inquire_cred_by_mech.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred_by_mech (
OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID mech_type,
gss_name_t * name,
OM_uint32 * initiator_lifetime,
@@ -47,6 +47,10 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred_by_mech (
OM_uint32 maj_stat;
OM_uint32 lifetime;
+ /*
+ * XXX This is busted. _gsskrb5_inquire_cred() should be implemented in
+ * terms of _gsskrb5_inquire_cred_by_mech(), NOT the other way around.
+ */
maj_stat =
_gsskrb5_inquire_cred (minor_status, cred_handle,
name, &lifetime, &usage, NULL);
diff --git a/lib/gssapi/krb5/inquire_cred_by_oid.c b/lib/gssapi/krb5/inquire_cred_by_oid.c
index d560ed4ba1c6..7dae3d25c4ff 100644
--- a/lib/gssapi/krb5/inquire_cred_by_oid.c
+++ b/lib/gssapi/krb5/inquire_cred_by_oid.c
@@ -34,7 +34,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred_by_oid
(OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
diff --git a/lib/gssapi/krb5/inquire_mechs_for_name.c b/lib/gssapi/krb5/inquire_mechs_for_name.c
index 6197a81b40a7..c6c67464fc55 100644
--- a/lib/gssapi/krb5/inquire_mechs_for_name.c
+++ b/lib/gssapi/krb5/inquire_mechs_for_name.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_mechs_for_name (
OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_OID_set * mech_types
)
{
diff --git a/lib/gssapi/krb5/inquire_sec_context_by_oid.c b/lib/gssapi/krb5/inquire_sec_context_by_oid.c
index b57217a4e830..f1ed99320975 100644
--- a/lib/gssapi/krb5/inquire_sec_context_by_oid.c
+++ b/lib/gssapi/krb5/inquire_sec_context_by_oid.c
@@ -149,7 +149,6 @@ static OM_uint32 inquire_sec_context_get_subkey
}
ret = krb5_store_keyblock(sp, *key);
- krb5_free_keyblock (context, key);
if (ret)
goto out;
@@ -169,6 +168,7 @@ static OM_uint32 inquire_sec_context_get_subkey
}
out:
+ krb5_free_keyblock(context, key);
krb5_data_free(&data);
if (sp)
krb5_storage_free(sp);
@@ -333,7 +333,8 @@ export_lucid_sec_context_v1(OM_uint32 *minor_status,
if (ret) goto out;
ret = krb5_store_int32(sp, (context_handle->more_flags & LOCAL) ? 1 : 0);
if (ret) goto out;
- ret = krb5_store_int32(sp, context_handle->lifetime);
+ /* XXX need krb5_store_int64() */
+ ret = krb5_store_int32(sp, context_handle->endtime);
if (ret) goto out;
krb5_auth_con_getlocalseqnumber (context,
context_handle->auth_context,
@@ -529,7 +530,7 @@ out:
OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_sec_context_by_oid
(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
diff --git a/lib/gssapi/krb5/pname_to_uid.c b/lib/gssapi/krb5/pname_to_uid.c
index ff754e779813..dca74645de6d 100644
--- a/lib/gssapi/krb5/pname_to_uid.c
+++ b/lib/gssapi/krb5/pname_to_uid.c
@@ -33,53 +33,38 @@
#include "gsskrb5_locl.h"
OM_uint32 GSSAPI_CALLCONV
-_gsskrb5_pname_to_uid(OM_uint32 *minor_status,
- const gss_name_t pname,
- const gss_OID mech_type,
- uid_t *uidp)
+_gsskrb5_localname(OM_uint32 *minor_status,
+ gss_const_name_t pname,
+ const gss_OID mech_type,
+ gss_buffer_t localname)
{
-#ifdef NO_LOCALNAME
- *minor_status = KRB5_NO_LOCALNAME;
- return GSS_S_FAILURE;
-#else
krb5_error_code ret;
krb5_context context;
krb5_const_principal princ = (krb5_const_principal)pname;
- char localname[256];
-#ifdef POSIX_GETPWNAM_R
- char pwbuf[2048];
- struct passwd pw, *pwd;
-#else
- struct passwd *pwd;
-#endif
+ char lnamebuf[256];
GSSAPI_KRB5_INIT(&context);
*minor_status = 0;
ret = krb5_aname_to_localname(context, princ,
- sizeof(localname), localname);
+ sizeof(lnamebuf), lnamebuf);
if (ret != 0) {
*minor_status = ret;
return GSS_S_FAILURE;
}
-#ifdef POSIX_GETPWNAM_R
- if (getpwnam_r(localname, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) {
- *minor_status = KRB5_NO_LOCALNAME;
- return GSS_S_FAILURE;
- }
-#else
- pwd = getpwnam(localname);
-#endif
+ localname->length = strlen(lnamebuf);
- if (pwd == NULL) {
- *minor_status = KRB5_NO_LOCALNAME;
+ localname->value = malloc(localname->length + 1);
+ if (localname->value == NULL) {
+ localname->length = 0;
+ *minor_status = ENOMEM;
return GSS_S_FAILURE;
}
- *uidp = pwd->pw_uid;
+ memcpy(localname->value, lnamebuf, localname->length + 1);
+ *minor_status = 0;
return GSS_S_COMPLETE;
-#endif /* NO_LOCALNAME */
}
diff --git a/lib/gssapi/krb5/prf.c b/lib/gssapi/krb5/prf.c
index 162a3097099f..671ab2c6d982 100644
--- a/lib/gssapi/krb5/prf.c
+++ b/lib/gssapi/krb5/prf.c
@@ -119,7 +119,7 @@ _gsskrb5_pseudo_random(OM_uint32 *minor_status,
while(dol > 0) {
size_t tsize;
- _gsskrb5_encode_om_uint32(num, input.data);
+ _gsskrb5_encode_be_om_uint32(num, input.data);
ret = krb5_crypto_prf(context, crypto, &input, &output);
if (ret) {
@@ -133,7 +133,7 @@ _gsskrb5_pseudo_random(OM_uint32 *minor_status,
tsize = min(dol, output.length);
memcpy(p, output.data, tsize);
- p += output.length;
+ p += tsize;
dol -= tsize;
krb5_data_free(&output);
num++;
diff --git a/lib/gssapi/krb5/process_context_token.c b/lib/gssapi/krb5/process_context_token.c
index 0cc1c07cfbe9..601b0e8a5a8a 100644
--- a/lib/gssapi/krb5/process_context_token.c
+++ b/lib/gssapi/krb5/process_context_token.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gsskrb5_process_context_token (
OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t token_buffer
)
{
diff --git a/lib/gssapi/krb5/set_sec_context_option.c b/lib/gssapi/krb5/set_sec_context_option.c
index 141ff722fb64..a0e6fd02c59a 100644
--- a/lib/gssapi/krb5/set_sec_context_option.c
+++ b/lib/gssapi/krb5/set_sec_context_option.c
@@ -178,23 +178,9 @@ _gsskrb5_set_sec_context_option
} else if (gss_oid_equal(desired_object, GSS_KRB5_SEND_TO_KDC_X)) {
- if (value == NULL || value->length == 0) {
- krb5_set_send_to_kdc_func(context, NULL, NULL);
- } else {
- struct gsskrb5_send_to_kdc c;
-
- if (value->length != sizeof(c)) {
- *minor_status = EINVAL;
- return GSS_S_FAILURE;
- }
- memcpy(&c, value->value, sizeof(c));
- krb5_set_send_to_kdc_func(context,
- (krb5_send_to_kdc_func)c.func,
- c.ptr);
- }
+ *minor_status = EINVAL;
+ return GSS_S_FAILURE;
- *minor_status = 0;
- return GSS_S_COMPLETE;
} else if (gss_oid_equal(desired_object, GSS_KRB5_CCACHE_NAME_X)) {
char *str;
diff --git a/lib/gssapi/krb5/store_cred.c b/lib/gssapi/krb5/store_cred.c
index a3aa2fb83e71..40b75771dd17 100644
--- a/lib/gssapi/krb5/store_cred.c
+++ b/lib/gssapi/krb5/store_cred.c
@@ -46,8 +46,11 @@ _gsskrb5_store_cred(OM_uint32 *minor_status,
krb5_context context;
krb5_error_code ret;
gsskrb5_cred cred;
- krb5_ccache id;
- int destroy = 0;
+ krb5_ccache id = NULL;
+ krb5_ccache def_ccache = NULL;
+ const char *def_type = NULL;
+ time_t exp_current;
+ time_t exp_new;
*minor_status = 0;
@@ -56,7 +59,8 @@ _gsskrb5_store_cred(OM_uint32 *minor_status,
return GSS_S_FAILURE;
}
- if (gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0)
+ if (desired_mech != GSS_C_NO_OID &&
+ gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0)
return GSS_S_BAD_MECH;
cred = (gsskrb5_cred)input_cred_handle;
@@ -69,48 +73,87 @@ _gsskrb5_store_cred(OM_uint32 *minor_status,
if (cred->usage != cred_usage && cred->usage != GSS_C_BOTH) {
HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
*minor_status = GSS_KRB5_S_G_BAD_USAGE;
- return(GSS_S_FAILURE);
+ return GSS_S_FAILURE;
+ }
+
+ ret = krb5_cc_get_lifetime(context, cred->ccache, &exp_new);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = ret;
+ return GSS_S_NO_CRED;
}
if (cred->principal == NULL) {
HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
*minor_status = GSS_KRB5_S_KG_TGT_MISSING;
- return(GSS_S_FAILURE);
+ return GSS_S_FAILURE;
}
- /* write out cred to credential cache */
+ ret = krb5_cc_default(context, &def_ccache);
+ if (ret == 0) {
+ def_type = krb5_cc_get_type(context, def_ccache);
+ krb5_cc_close(context, def_ccache);
+ }
+ def_ccache = NULL;
+ /* write out cred to credential cache */
ret = krb5_cc_cache_match(context, cred->principal, &id);
if (ret) {
- ret = krb5_cc_new_unique(context, NULL, NULL, &id);
- if (ret) {
- HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
- *minor_status = ret;
- return(GSS_S_FAILURE);
- }
- destroy = 1;
+ if (default_cred) {
+ ret = krb5_cc_default(context, &id);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ } else {
+ if (def_type == NULL ||
+ !krb5_cc_support_switch(context, def_type)) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = 0; /* XXX */
+ return GSS_S_NO_CRED; /* XXX */
+ }
+ ret = krb5_cc_new_unique(context, def_type, NULL, &id);
+ if (ret) {
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = ret;
+ return GSS_S_FAILURE;
+ }
+ overwrite_cred = 1;
+ }
+ }
+
+ if (!overwrite_cred) {
+ /* If current creds are expired or near it, overwrite */
+ ret = krb5_cc_get_lifetime(context, id, &exp_current);
+ if (ret != 0 || exp_new > exp_current)
+ overwrite_cred = 1;
+ }
+
+ if (!overwrite_cred) {
+ /* Nothing to do */
+ krb5_cc_close(context, id);
+ HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
+ *minor_status = 0;
+ return GSS_S_DUPLICATE_ELEMENT;
}
ret = krb5_cc_initialize(context, id, cred->principal);
if (ret == 0)
ret = krb5_cc_copy_match_f(context, cred->ccache, id, NULL, NULL, NULL);
if (ret) {
- if (destroy)
- krb5_cc_destroy(context, id);
- else
- krb5_cc_close(context, id);
+ krb5_cc_close(context, id);
HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
*minor_status = ret;
return(GSS_S_FAILURE);
}
- if (default_cred)
+ if (default_cred && def_type != NULL &&
+ krb5_cc_support_switch(context, def_type))
krb5_cc_switch(context, id);
krb5_cc_close(context, id);
-
HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex);
-
*minor_status = 0;
return GSS_S_COMPLETE;
}
diff --git a/lib/gssapi/krb5/test_acquire_cred.c b/lib/gssapi/krb5/test_acquire_cred.c
new file mode 100644
index 000000000000..9f7c9ef4e430
--- /dev/null
+++ b/lib/gssapi/krb5/test_acquire_cred.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2003-2005 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gsskrb5_locl.h"
+#include <err.h>
+
+static void
+print_time(OM_uint32 time_rec)
+{
+ if (time_rec == GSS_C_INDEFINITE) {
+ printf("cred never expire\n");
+ } else {
+ time_t t = time_rec + time(NULL);
+ printf("expiration time: %s", ctime(&t));
+ }
+}
+
+static void
+test_add(gss_cred_id_t cred_handle)
+{
+ OM_uint32 major_status, minor_status;
+ gss_cred_id_t copy_cred;
+ OM_uint32 time_rec;
+
+ major_status = gss_add_cred (&minor_status,
+ cred_handle,
+ GSS_C_NO_NAME,
+ GSS_KRB5_MECHANISM,
+ GSS_C_INITIATE,
+ 0,
+ 0,
+ &copy_cred,
+ NULL,
+ &time_rec,
+ NULL);
+
+ if (GSS_ERROR(major_status))
+ errx(1, "add_cred failed");
+
+ print_time(time_rec);
+
+ major_status = gss_release_cred(&minor_status,
+ &copy_cred);
+ if (GSS_ERROR(major_status))
+ errx(1, "release_cred failed");
+}
+
+static void
+copy_cred(void)
+{
+ OM_uint32 major_status, minor_status;
+ gss_cred_id_t cred_handle;
+ OM_uint32 time_rec;
+
+ major_status = gss_acquire_cred(&minor_status,
+ GSS_C_NO_NAME,
+ 0,
+ NULL,
+ GSS_C_INITIATE,
+ &cred_handle,
+ NULL,
+ &time_rec);
+ if (GSS_ERROR(major_status))
+ errx(1, "acquire_cred failed");
+
+ print_time(time_rec);
+
+ test_add(cred_handle);
+ test_add(cred_handle);
+ test_add(cred_handle);
+
+ major_status = gss_release_cred(&minor_status,
+ &cred_handle);
+ if (GSS_ERROR(major_status))
+ errx(1, "release_cred failed");
+}
+
+static void
+acquire_cred_service(const char *service)
+{
+ OM_uint32 major_status, minor_status;
+ gss_cred_id_t cred_handle;
+ OM_uint32 time_rec;
+ gss_buffer_desc name_buffer;
+ gss_name_t name;
+
+ name_buffer.value = rk_UNCONST(service);
+ name_buffer.length = strlen(service);
+
+ major_status = gss_import_name(&minor_status,
+ &name_buffer,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ &name);
+ if (GSS_ERROR(major_status))
+ errx(1, "import_name failed");
+
+
+ major_status = gss_acquire_cred(&minor_status,
+ name,
+ 0,
+ NULL,
+ GSS_C_ACCEPT,
+ &cred_handle,
+ NULL,
+ &time_rec);
+ if (GSS_ERROR(major_status))
+ errx(1, "acquire_cred failed");
+
+ print_time(time_rec);
+
+ major_status = gss_release_cred(&minor_status,
+ &cred_handle);
+ if (GSS_ERROR(major_status))
+ errx(1, "release_cred failed");
+
+
+ major_status = gss_release_name(&minor_status,
+ &name);
+ if (GSS_ERROR(major_status))
+ errx(1, "release_name failed");
+
+}
+
+int
+main(int argc, char **argv)
+{
+ copy_cred();
+
+ acquire_cred_service("host@xen2-heimdal-linux.lab.it.su.se");
+
+ return 0;
+}
diff --git a/lib/gssapi/krb5/test_cfx.c b/lib/gssapi/krb5/test_cfx.c
index 0b196fcad24d..15f853c6b602 100644
--- a/lib/gssapi/krb5/test_cfx.c
+++ b/lib/gssapi/krb5/test_cfx.c
@@ -148,7 +148,7 @@ main(int argc, char **argv)
errx(1, "krb5_context_init: %d", ret);
ret = krb5_generate_random_keyblock(context,
- ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
&keyblock);
if (ret)
krb5_err(context, 1, ret, "krb5_generate_random_keyblock");
diff --git a/lib/gssapi/krb5/test_cred.c b/lib/gssapi/krb5/test_cred.c
new file mode 100644
index 000000000000..06dd6632d0be
--- /dev/null
+++ b/lib/gssapi/krb5/test_cred.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2003-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gsskrb5_locl.h"
+#include <err.h>
+#include <getarg.h>
+
+static void
+gss_print_errors (int min_stat)
+{
+ OM_uint32 new_stat;
+ OM_uint32 msg_ctx = 0;
+ gss_buffer_desc status_string;
+ OM_uint32 ret;
+
+ do {
+ ret = gss_display_status (&new_stat,
+ min_stat,
+ GSS_C_MECH_CODE,
+ GSS_C_NO_OID,
+ &msg_ctx,
+ &status_string);
+ fprintf (stderr, "%.*s\n", (int)status_string.length,
+ (char *)status_string.value);
+ gss_release_buffer (&new_stat, &status_string);
+ } while (!GSS_ERROR(ret) && msg_ctx != 0);
+}
+
+static void
+gss_err(int exitval, int status, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vwarnx (fmt, args);
+ gss_print_errors (status);
+ va_end(args);
+ exit (exitval);
+}
+
+static void
+acquire_release_loop(gss_name_t name, int counter, gss_cred_usage_t usage)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_cred_id_t cred;
+ int i;
+
+ for (i = 0; i < counter; i++) {
+ maj_stat = gss_acquire_cred(&min_stat, name,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ usage,
+ &cred,
+ NULL,
+ NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "aquire %d %d != GSS_S_COMPLETE",
+ i, (int)maj_stat);
+
+ maj_stat = gss_release_cred(&min_stat, &cred);
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "release %d %d != GSS_S_COMPLETE",
+ i, (int)maj_stat);
+ }
+}
+
+
+static void
+acquire_add_release_add(gss_name_t name, gss_cred_usage_t usage)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_cred_id_t cred, cred2, cred3;
+
+ maj_stat = gss_acquire_cred(&min_stat, name,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET,
+ usage,
+ &cred,
+ NULL,
+ NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "aquire %d != GSS_S_COMPLETE", (int)maj_stat);
+
+ maj_stat = gss_add_cred(&min_stat,
+ cred,
+ GSS_C_NO_NAME,
+ GSS_KRB5_MECHANISM,
+ usage,
+ GSS_C_INDEFINITE,
+ GSS_C_INDEFINITE,
+ &cred2,
+ NULL,
+ NULL,
+ NULL);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "add_cred %d != GSS_S_COMPLETE", (int)maj_stat);
+
+ maj_stat = gss_release_cred(&min_stat, &cred);
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "release %d != GSS_S_COMPLETE", (int)maj_stat);
+
+ maj_stat = gss_add_cred(&min_stat,
+ cred2,
+ GSS_C_NO_NAME,
+ GSS_KRB5_MECHANISM,
+ GSS_C_BOTH,
+ GSS_C_INDEFINITE,
+ GSS_C_INDEFINITE,
+ &cred3,
+ NULL,
+ NULL,
+ NULL);
+
+ maj_stat = gss_release_cred(&min_stat, &cred2);
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "release 2 %d != GSS_S_COMPLETE", (int)maj_stat);
+
+ maj_stat = gss_release_cred(&min_stat, &cred3);
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "release 2 %d != GSS_S_COMPLETE", (int)maj_stat);
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag, "print version", NULL },
+ {"help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args),
+ NULL, "service@host");
+ exit (ret);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ struct gss_buffer_desc_struct name_buffer;
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t name;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc < 1)
+ errx(1, "argc < 1");
+
+ name_buffer.value = argv[0];
+ name_buffer.length = strlen(argv[0]);
+
+ maj_stat = gss_import_name(&min_stat, &name_buffer,
+ GSS_C_NT_HOSTBASED_SERVICE,
+ &name);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "import name error");
+
+ acquire_release_loop(name, 100, GSS_C_ACCEPT);
+ acquire_release_loop(name, 100, GSS_C_INITIATE);
+ acquire_release_loop(name, 100, GSS_C_BOTH);
+
+ acquire_add_release_add(name, GSS_C_ACCEPT);
+ acquire_add_release_add(name, GSS_C_INITIATE);
+ acquire_add_release_add(name, GSS_C_BOTH);
+
+ gss_release_name(&min_stat, &name);
+
+ return 0;
+}
diff --git a/lib/gssapi/krb5/test_kcred.c b/lib/gssapi/krb5/test_kcred.c
new file mode 100644
index 000000000000..f53ce783bb07
--- /dev/null
+++ b/lib/gssapi/krb5/test_kcred.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2003-2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "gsskrb5_locl.h"
+#include <err.h>
+#include <getarg.h>
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static void
+copy_import(void)
+{
+ gss_cred_id_t cred1, cred2;
+ OM_uint32 maj_stat, min_stat;
+ gss_name_t name1, name2;
+ OM_uint32 lifetime1, lifetime2;
+ gss_cred_usage_t usage1, usage2;
+ gss_OID_set mechs1, mechs2;
+ krb5_ccache id;
+ krb5_error_code ret;
+ krb5_context context;
+ int equal;
+
+ maj_stat = gss_acquire_cred(&min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE,
+ GSS_C_NO_OID_SET, GSS_C_INITIATE,
+ &cred1, NULL, NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_acquire_cred");
+
+ maj_stat = gss_inquire_cred(&min_stat, cred1, &name1, &lifetime1,
+ &usage1, &mechs1);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_inquire_cred");
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context");
+
+ ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &id);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_cc_new_unique");
+
+ maj_stat = gss_krb5_copy_ccache(&min_stat, context, cred1, id);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_krb5_copy_ccache");
+
+ maj_stat = gss_krb5_import_cred(&min_stat, id, NULL, NULL, &cred2);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_krb5_import_cred");
+
+ maj_stat = gss_inquire_cred(&min_stat, cred2, &name2, &lifetime2,
+ &usage2, &mechs2);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_inquire_cred 2");
+
+ maj_stat = gss_compare_name(&min_stat, name1, name2, &equal);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_compare_name");
+ if (!equal)
+ errx(1, "names not equal");
+
+ if (lifetime1 != lifetime1)
+ errx(1, "lifetime not equal");
+
+ if (usage1 != usage1)
+ errx(1, "usage not equal");
+
+ gss_release_cred(&min_stat, &cred1);
+ gss_release_cred(&min_stat, &cred2);
+
+ gss_release_name(&min_stat, &name1);
+ gss_release_name(&min_stat, &name2);
+
+#if 0
+ compare(mechs1, mechs2);
+#endif
+
+ gss_release_oid_set(&min_stat, &mechs1);
+ gss_release_oid_set(&min_stat, &mechs2);
+
+ krb5_cc_destroy(context, id);
+ krb5_free_context(context);
+}
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag, "print version", NULL },
+ {"help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args, sizeof(args)/sizeof(*args),
+ NULL, "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ int optidx = 0;
+
+ setprogname(argv[0]);
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if(version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ copy_import();
+
+ return 0;
+}
diff --git a/lib/gssapi/krb5/test_oid.c b/lib/gssapi/krb5/test_oid.c
new file mode 100644
index 000000000000..00219b91e066
--- /dev/null
+++ b/lib/gssapi/krb5/test_oid.c
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "gsskrb5_locl.h"
+
+int
+main(int argc, char **argv)
+{
+ OM_uint32 minor_status, maj_stat;
+ gss_buffer_desc data;
+ int ret;
+
+ maj_stat = gss_oid_to_str(&minor_status, GSS_KRB5_MECHANISM, &data);
+ if (GSS_ERROR(maj_stat))
+ errx(1, "gss_oid_to_str failed");
+ ret = strncmp(data.value, "1 2 840 113554 1 2 2", data.length);
+ gss_release_buffer(&maj_stat, &data);
+ if (ret)
+ return 1;
+ return 0;
+}
diff --git a/lib/gssapi/krb5/unwrap.c b/lib/gssapi/krb5/unwrap.c
index d6bc20477787..da939c052930 100644
--- a/lib/gssapi/krb5/unwrap.c
+++ b/lib/gssapi/krb5/unwrap.c
@@ -382,7 +382,7 @@ unwrap_des3
OM_uint32 GSSAPI_CALLCONV _gsskrb5_unwrap
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t input_message_buffer,
gss_buffer_t output_message_buffer,
int * conf_state,
@@ -392,7 +392,6 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_unwrap
krb5_keyblock *key;
krb5_context context;
OM_uint32 ret;
- krb5_keytype keytype;
gsskrb5_ctx ctx = (gsskrb5_ctx) context_handle;
output_message_buffer->value = NULL;
@@ -414,12 +413,13 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_unwrap
*minor_status = ret;
return GSS_S_FAILURE;
}
- krb5_enctype_to_keytype (context, key->keytype, &keytype);
*minor_status = 0;
- switch (keytype) {
- case KEYTYPE_DES :
+ switch (key->keytype) {
+ case KRB5_ENCTYPE_DES_CBC_CRC :
+ case KRB5_ENCTYPE_DES_CBC_MD4 :
+ case KRB5_ENCTYPE_DES_CBC_MD5 :
#ifdef HEIM_WEAK_CRYPTO
ret = unwrap_des (minor_status, ctx,
input_message_buffer, output_message_buffer,
@@ -428,13 +428,14 @@ OM_uint32 GSSAPI_CALLCONV _gsskrb5_unwrap
ret = GSS_S_FAILURE;
#endif
break;
- case KEYTYPE_DES3 :
+ case KRB5_ENCTYPE_DES3_CBC_MD5 :
+ case KRB5_ENCTYPE_DES3_CBC_SHA1 :
ret = unwrap_des3 (minor_status, ctx, context,
input_message_buffer, output_message_buffer,
conf_state, qop_state, key);
break;
- case KEYTYPE_ARCFOUR:
- case KEYTYPE_ARCFOUR_56:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56:
ret = _gssapi_unwrap_arcfour (minor_status, ctx, context,
input_message_buffer, output_message_buffer,
conf_state, qop_state, key);
diff --git a/lib/gssapi/krb5/verify_mic.c b/lib/gssapi/krb5/verify_mic.c
index 3814ef7062c1..9968ce403ef7 100644
--- a/lib/gssapi/krb5/verify_mic.c
+++ b/lib/gssapi/krb5/verify_mic.c
@@ -254,15 +254,11 @@ retry:
krb5_crypto_destroy (context, crypto);
ret = krb5_crypto_init(context, key,
ETYPE_DES3_CBC_SHA1, &crypto);
- if (ret){
- *minor_status = ret;
- return GSS_S_FAILURE;
- }
-
- ret = krb5_verify_checksum (context, crypto,
- KRB5_KU_USAGE_SIGN,
- tmp, message_buffer->length + 8,
- &csum);
+ if (ret == 0)
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_USAGE_SIGN,
+ tmp, message_buffer->length + 8,
+ &csum);
free (tmp);
if (ret) {
krb5_crypto_destroy (context, crypto);
@@ -289,7 +285,6 @@ _gsskrb5_verify_mic_internal
{
krb5_keyblock *key;
OM_uint32 ret;
- krb5_keytype keytype;
if (ctx->more_flags & IS_CFX)
return _gssapi_verify_mic_cfx (minor_status, ctx,
@@ -304,9 +299,11 @@ _gsskrb5_verify_mic_internal
return GSS_S_FAILURE;
}
*minor_status = 0;
- krb5_enctype_to_keytype (context, key->keytype, &keytype);
- switch (keytype) {
- case KEYTYPE_DES :
+
+ switch (key->keytype) {
+ case KRB5_ENCTYPE_DES_CBC_CRC :
+ case KRB5_ENCTYPE_DES_CBC_MD4 :
+ case KRB5_ENCTYPE_DES_CBC_MD5 :
#ifdef HEIM_WEAK_CRYPTO
ret = verify_mic_des (minor_status, ctx, context,
message_buffer, token_buffer, qop_state, key,
@@ -315,13 +312,14 @@ _gsskrb5_verify_mic_internal
ret = GSS_S_FAILURE;
#endif
break;
- case KEYTYPE_DES3 :
+ case KRB5_ENCTYPE_DES3_CBC_MD5 :
+ case KRB5_ENCTYPE_DES3_CBC_SHA1 :
ret = verify_mic_des3 (minor_status, ctx, context,
message_buffer, token_buffer, qop_state, key,
type);
break;
- case KEYTYPE_ARCFOUR :
- case KEYTYPE_ARCFOUR_56 :
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56:
ret = _gssapi_verify_mic_arcfour (minor_status, ctx,
context,
message_buffer, token_buffer,
@@ -338,7 +336,7 @@ _gsskrb5_verify_mic_internal
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_verify_mic
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t message_buffer,
const gss_buffer_t token_buffer,
gss_qop_t * qop_state
diff --git a/lib/gssapi/krb5/wrap.c b/lib/gssapi/krb5/wrap.c
index 1026e41914e9..481e30375a44 100644
--- a/lib/gssapi/krb5/wrap.c
+++ b/lib/gssapi/krb5/wrap.c
@@ -137,7 +137,7 @@ sub_wrap_size (
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_wrap_size_limit (
OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
OM_uint32 req_output_size,
@@ -147,7 +147,6 @@ _gsskrb5_wrap_size_limit (
krb5_context context;
krb5_keyblock *key;
OM_uint32 ret;
- krb5_keytype keytype;
const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
GSSAPI_KRB5_INIT (&context);
@@ -164,23 +163,25 @@ _gsskrb5_wrap_size_limit (
*minor_status = ret;
return GSS_S_FAILURE;
}
- krb5_enctype_to_keytype (context, key->keytype, &keytype);
- switch (keytype) {
- case KEYTYPE_DES :
+ switch (key->keytype) {
+ case KRB5_ENCTYPE_DES_CBC_CRC :
+ case KRB5_ENCTYPE_DES_CBC_MD4 :
+ case KRB5_ENCTYPE_DES_CBC_MD5 :
#ifdef HEIM_WEAK_CRYPTO
ret = sub_wrap_size(req_output_size, max_input_size, 8, 22);
#else
ret = GSS_S_FAILURE;
#endif
break;
- case ENCTYPE_ARCFOUR_HMAC_MD5:
- case ENCTYPE_ARCFOUR_HMAC_MD5_56:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56:
ret = _gssapi_wrap_size_arcfour(minor_status, ctx, context,
conf_req_flag, qop_req,
req_output_size, max_input_size, key);
break;
- case KEYTYPE_DES3 :
+ case KRB5_ENCTYPE_DES3_CBC_MD5 :
+ case KRB5_ENCTYPE_DES3_CBC_SHA1 :
ret = sub_wrap_size(req_output_size, max_input_size, 8, 34);
break;
default :
@@ -527,7 +528,7 @@ wrap_des3
OM_uint32 GSSAPI_CALLCONV
_gsskrb5_wrap
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
const gss_buffer_t input_message_buffer,
@@ -538,7 +539,6 @@ _gsskrb5_wrap
krb5_context context;
krb5_keyblock *key;
OM_uint32 ret;
- krb5_keytype keytype;
const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle;
output_message_buffer->value = NULL;
@@ -558,10 +558,11 @@ _gsskrb5_wrap
*minor_status = ret;
return GSS_S_FAILURE;
}
- krb5_enctype_to_keytype (context, key->keytype, &keytype);
- switch (keytype) {
- case KEYTYPE_DES :
+ switch (key->keytype) {
+ case KRB5_ENCTYPE_DES_CBC_CRC :
+ case KRB5_ENCTYPE_DES_CBC_MD4 :
+ case KRB5_ENCTYPE_DES_CBC_MD5 :
#ifdef HEIM_WEAK_CRYPTO
ret = wrap_des (minor_status, ctx, context, conf_req_flag,
qop_req, input_message_buffer, conf_state,
@@ -570,13 +571,14 @@ _gsskrb5_wrap
ret = GSS_S_FAILURE;
#endif
break;
- case KEYTYPE_DES3 :
+ case KRB5_ENCTYPE_DES3_CBC_MD5 :
+ case KRB5_ENCTYPE_DES3_CBC_SHA1 :
ret = wrap_des3 (minor_status, ctx, context, conf_req_flag,
qop_req, input_message_buffer, conf_state,
output_message_buffer, key);
break;
- case KEYTYPE_ARCFOUR:
- case KEYTYPE_ARCFOUR_56:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5:
+ case KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56:
ret = _gssapi_wrap_arcfour (minor_status, ctx, context, conf_req_flag,
qop_req, input_message_buffer, conf_state,
output_message_buffer, key);
diff --git a/lib/gssapi/libgssapi-exports.def b/lib/gssapi/libgssapi-exports.def
index 3cbcc4eb1844..b8e4dce4111c 100644
--- a/lib/gssapi/libgssapi-exports.def
+++ b/lib/gssapi/libgssapi-exports.def
@@ -65,6 +65,7 @@ EXPORTS
gss_krb5_get_tkt_flags
gss_krb5_import_cred
gss_krb5_set_allowable_enctypes
+ gss_localname
gss_mg_collect_error
gss_mo_get
gss_mo_set
@@ -93,11 +94,13 @@ EXPORTS
gss_test_oid_set_member
gss_unseal
gss_unwrap
+ gss_unwrap_aead
gss_unwrap_iov
gss_userok
gss_verify
gss_verify_mic
gss_wrap
+ gss_wrap_aead
gss_wrap_iov
gss_wrap_iov_length
gss_wrap_size_limit
diff --git a/lib/gssapi/mech/compat.h b/lib/gssapi/mech/compat.h
index e63f1e534306..d23a6e916a57 100644
--- a/lib/gssapi/mech/compat.h
+++ b/lib/gssapi/mech/compat.h
@@ -53,7 +53,7 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_inquire_attrs_for_mech_t (
typedef OM_uint32 GSSAPI_CALLCONV _gss_acquire_cred_with_password_t
(OM_uint32 *, /* minor_status */
- const gss_name_t, /* desired_name */
+ gss_const_name_t, /* desired_name */
const gss_buffer_t, /* password */
OM_uint32, /* time_req */
const gss_OID_set, /* desired_mechs */
@@ -65,8 +65,8 @@ typedef OM_uint32 GSSAPI_CALLCONV _gss_acquire_cred_with_password_t
typedef OM_uint32 GSSAPI_CALLCONV _gss_add_cred_with_password_t (
OM_uint32 *, /* minor_status */
- const gss_cred_id_t, /* input_cred_handle */
- const gss_name_t, /* desired_name */
+ gss_const_cred_id_t, /* input_cred_handle */
+ gss_const_name_t, /* desired_name */
const gss_OID, /* desired_mech */
const gss_buffer_t, /* password */
gss_cred_usage_t, /* cred_usage */
diff --git a/lib/gssapi/mech/doxygen.c b/lib/gssapi/mech/doxygen.c
index a341cba2dac1..4ead9f17e93a 100644
--- a/lib/gssapi/mech/doxygen.c
+++ b/lib/gssapi/mech/doxygen.c
@@ -39,13 +39,12 @@
* - SPNEGO
* - NTLM
*
- * See @ref gssapi_mechs for more describtion about these mechanisms.
- *
- * The project web page: http://www.h5l.org/
+ * @sa
*
* - @ref gssapi_services_intro
* - @ref gssapi_mechs
* - @ref gssapi_api_INvsMN
+ * - The project web page: http://www.h5l.org/
*/
/**
@@ -105,28 +104,30 @@
* @page internalVSmechname Internal names and mechanism names
* @section gssapi_api_INvsMN Name forms
*
- * There are two forms of name in GSS-API, Internal form and
- * Contiguous string ("flat") form. gss_export_name() and
+ * There are two name representations in GSS-API: Internal form and
+ * Contiguous string ("flat") form. Functions gss_export_name() and
* gss_import_name() can be used to convert between the two forms.
*
* - The contiguous string form is described by an oid specificing the
* type and an octet string. A special form of the contiguous
* string form is the exported name object. The exported name
* defined for each mechanism, is something that can be stored and
- * complared later. The exported name is what should be used for
+ * compared later. The exported name is what should be used for
* ACLs comparisons.
*
- * - The Internal form
+ * - The Internal form is opaque to the application programmer and
+ * is implementation-dependent.
*
- * There is also special form of the Internal Name (IN), and that is
+ * - There is also a special form of the Internal Name (IN), and that is
* the Mechanism Name (MN). In the mechanism name all the generic
* information is stripped of and only contain the information for
* one mechanism. In GSS-API some function return MN and some
* require MN as input. Each of these function is marked up as such.
*
- *
- * Describe relationship between import_name, canonicalize_name,
- * export_name and friends.
+ * @FIXME Describe relationship between import_name, canonicalize_name,
+ * export_name and friends. Also, update for RFC2743 language
+ * ("contiguous" and "flat" are gone, leaving just "exported name
+ * token", "internal", and "MN").
*/
/** @defgroup gssapi Heimdal GSS-API functions */
diff --git a/lib/gssapi/mech/gss_accept_sec_context.c b/lib/gssapi/mech/gss_accept_sec_context.c
index bf7ea03f72e4..25205f437acf 100644
--- a/lib/gssapi/mech/gss_accept_sec_context.c
+++ b/lib/gssapi/mech/gss_accept_sec_context.c
@@ -144,7 +144,7 @@ choose_mech(const gss_buffer_t input, gss_OID mech_oid)
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_accept_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t *src_name,
diff --git a/lib/gssapi/mech/gss_acquire_cred.c b/lib/gssapi/mech/gss_acquire_cred.c
index ade65df8ec86..095f9056ca69 100644
--- a/lib/gssapi/mech/gss_acquire_cred.c
+++ b/lib/gssapi/mech/gss_acquire_cred.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_acquire_cred(OM_uint32 *minor_status,
- const gss_name_t desired_name,
+ gss_const_name_t desired_name,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
diff --git a/lib/gssapi/mech/gss_acquire_cred_ext.c b/lib/gssapi/mech/gss_acquire_cred_ext.c
index 1cbb29f141f3..9f2674c26ecc 100644
--- a/lib/gssapi/mech/gss_acquire_cred_ext.c
+++ b/lib/gssapi/mech/gss_acquire_cred_ext.c
@@ -100,13 +100,22 @@ _gss_acquire_mech_cred(OM_uint32 *minor_status,
mc= NULL;
}
- *output_cred_handle = mc;
+ if (major_status != GSS_S_COMPLETE)
+ free(mc);
+ else
+ *output_cred_handle = mc;
return major_status;
}
+/**
+ * This function is not a public interface and is deprecated anyways, do
+ * not use. Use gss_acquire_cred_with_password() instead for now.
+ *
+ * @deprecated
+ */
OM_uint32
_gss_acquire_cred_ext(OM_uint32 *minor_status,
- const gss_name_t desired_name,
+ gss_const_name_t desired_name,
gss_const_OID credential_type,
const void *credential_data,
OM_uint32 time_req,
@@ -152,7 +161,6 @@ _gss_acquire_cred_ext(OM_uint32 *minor_status,
for (i = 0; i < mechs->count; i++) {
struct _gss_mechanism_name *mn = NULL;
struct _gss_mechanism_cred *mc = NULL;
- gss_name_t desired_mech_name = GSS_C_NO_NAME;
m = __gss_get_mechanism(&mechs->elements[i]);
if (!m)
@@ -163,16 +171,17 @@ _gss_acquire_cred_ext(OM_uint32 *minor_status,
&mechs->elements[i], &mn);
if (major_status != GSS_S_COMPLETE)
continue;
-
- desired_mech_name = mn->gmn_name;
}
major_status = _gss_acquire_mech_cred(minor_status, m, mn,
credential_type, credential_data,
time_req, desired_mech, cred_usage,
&mc);
- if (GSS_ERROR(major_status))
+ if (GSS_ERROR(major_status)) {
+ if (mechs->count == 1)
+ _gss_mg_error(m, major_status, *minor_status);
continue;
+ }
HEIM_SLIST_INSERT_HEAD(&cred->gc_mc, mc, gmc_link);
}
@@ -183,7 +192,8 @@ _gss_acquire_cred_ext(OM_uint32 *minor_status,
*/
if (!HEIM_SLIST_FIRST(&cred->gc_mc)) {
free(cred);
- *minor_status = 0;
+ if (mechs->count > 1)
+ *minor_status = 0;
return GSS_S_NO_CRED;
}
diff --git a/lib/gssapi/mech/gss_acquire_cred_with_password.c b/lib/gssapi/mech/gss_acquire_cred_with_password.c
index 8c2a6488f8ad..2f41f8906dc2 100644
--- a/lib/gssapi/mech/gss_acquire_cred_with_password.c
+++ b/lib/gssapi/mech/gss_acquire_cred_with_password.c
@@ -34,7 +34,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_acquire_cred_with_password(OM_uint32 *minor_status,
- const gss_name_t desired_name,
+ gss_const_name_t desired_name,
const gss_buffer_t password,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
@@ -93,7 +93,8 @@ gss_acquire_cred_with_password(OM_uint32 *minor_status,
if (!HEIM_SLIST_FIRST(&new_cred->gc_mc)) {
free(new_cred);
- *minor_status = 0;
+ if (desired_mechs->count > 1)
+ *minor_status = 0;
return GSS_S_NO_CRED;
}
diff --git a/lib/gssapi/mech/gss_add_cred.c b/lib/gssapi/mech/gss_add_cred.c
index a998bc60ff80..b56e3d760824 100644
--- a/lib/gssapi/mech/gss_add_cred.c
+++ b/lib/gssapi/mech/gss_add_cred.c
@@ -72,8 +72,8 @@ _gss_copy_cred(struct _gss_mechanism_cred *mc)
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_add_cred(OM_uint32 *minor_status,
- const gss_cred_id_t input_cred_handle,
- const gss_name_t desired_name,
+ gss_const_cred_id_t input_cred_handle,
+ gss_const_name_t desired_name,
const gss_OID desired_mech,
gss_cred_usage_t cred_usage,
OM_uint32 initiator_time_req,
diff --git a/lib/gssapi/mech/gss_add_cred_with_password.c b/lib/gssapi/mech/gss_add_cred_with_password.c
index f966305cfb16..b20f64f774b6 100644
--- a/lib/gssapi/mech/gss_add_cred_with_password.c
+++ b/lib/gssapi/mech/gss_add_cred_with_password.c
@@ -30,8 +30,8 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_add_cred_with_password(OM_uint32 *minor_status,
- const gss_cred_id_t input_cred_handle,
- const gss_name_t desired_name,
+ gss_const_cred_id_t input_cred_handle,
+ gss_const_name_t desired_name,
const gss_OID desired_mech,
const gss_buffer_t password,
gss_cred_usage_t cred_usage,
diff --git a/lib/gssapi/mech/gss_aeap.c b/lib/gssapi/mech/gss_aeap.c
index 3008c0d34484..6395d8442b8c 100644
--- a/lib/gssapi/mech/gss_aeap.c
+++ b/lib/gssapi/mech/gss_aeap.c
@@ -199,7 +199,7 @@ gss_OID_desc GSSAPI_LIB_FUNCTION __gss_c_attr_stream_sizes_oid_desc =
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_context_query_attributes(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_OID attribute,
void *data,
size_t len)
@@ -214,3 +214,121 @@ gss_context_query_attributes(OM_uint32 *minor_status,
return GSS_S_FAILURE;
}
+
+/*
+ * AEAD wrap API for a single piece of associated data, for compatibility
+ * with MIT and as specified by draft-howard-gssapi-aead-00.txt.
+ *
+ * @ingroup gssapi
+ */
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_wrap_aead(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ int conf_req_flag,
+ gss_qop_t qop_req,
+ gss_buffer_t input_assoc_buffer,
+ gss_buffer_t input_payload_buffer,
+ int *conf_state,
+ gss_buffer_t output_message_buffer)
+{
+ OM_uint32 major_status, tmp, flags = 0;
+ gss_iov_buffer_desc iov[5];
+ size_t i;
+ unsigned char *p;
+
+ memset(iov, 0, sizeof(iov));
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ if (input_assoc_buffer)
+ iov[1].buffer = *input_assoc_buffer;
+
+ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA;
+ if (input_payload_buffer)
+ iov[2].buffer.length = input_payload_buffer->length;
+
+ gss_inquire_context(minor_status, context_handle, NULL, NULL,
+ NULL, NULL, &flags, NULL, NULL);
+
+ /* krb5 mech rejects padding/trailer if DCE-style is set */
+ iov[3].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY
+ : GSS_IOV_BUFFER_TYPE_PADDING;
+ iov[4].type = (flags & GSS_C_DCE_STYLE) ? GSS_IOV_BUFFER_TYPE_EMPTY
+ : GSS_IOV_BUFFER_TYPE_TRAILER;
+
+ major_status = gss_wrap_iov_length(minor_status, context_handle,
+ conf_req_flag, qop_req, conf_state,
+ iov, 5);
+ if (GSS_ERROR(major_status))
+ return major_status;
+
+ for (i = 0, output_message_buffer->length = 0; i < 5; i++) {
+ if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
+ continue;
+
+ output_message_buffer->length += iov[i].buffer.length;
+ }
+
+ output_message_buffer->value = malloc(output_message_buffer->length);
+ if (output_message_buffer->value == NULL) {
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ for (i = 0, p = output_message_buffer->value; i < 5; i++) {
+ if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_SIGN_ONLY)
+ continue;
+ else if (GSS_IOV_BUFFER_TYPE(iov[i].type) == GSS_IOV_BUFFER_TYPE_DATA)
+ memcpy(p, input_payload_buffer->value, input_payload_buffer->length);
+
+ iov[i].buffer.value = p;
+ p += iov[i].buffer.length;
+ }
+
+ major_status = gss_wrap_iov(minor_status, context_handle, conf_req_flag,
+ qop_req, conf_state, iov, 5);
+ if (GSS_ERROR(major_status))
+ gss_release_buffer(&tmp, output_message_buffer);
+
+ return major_status;
+}
+
+/*
+ * AEAD unwrap for a single piece of associated data, for compatibility
+ * with MIT and as specified by draft-howard-gssapi-aead-00.txt.
+ *
+ * @ingroup gssapi
+ */
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_unwrap_aead(OM_uint32 *minor_status,
+ gss_ctx_id_t context_handle,
+ gss_buffer_t input_message_buffer,
+ gss_buffer_t input_assoc_buffer,
+ gss_buffer_t output_payload_buffer,
+ int *conf_state,
+ gss_qop_t *qop_state)
+{
+ OM_uint32 major_status, tmp;
+ gss_iov_buffer_desc iov[3];
+
+ memset(iov, 0, sizeof(iov));
+
+ iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ iov[0].buffer = *input_message_buffer;
+
+ iov[1].type = GSS_IOV_BUFFER_TYPE_SIGN_ONLY;
+ if (input_assoc_buffer)
+ iov[1].buffer = *input_assoc_buffer;
+
+ iov[2].type = GSS_IOV_BUFFER_TYPE_DATA | GSS_IOV_BUFFER_FLAG_ALLOCATE;
+
+ major_status = gss_unwrap_iov(minor_status, context_handle, conf_state,
+ qop_state, iov, 3);
+ if (GSS_ERROR(major_status))
+ gss_release_iov_buffer(&tmp, &iov[2], 1);
+ else
+ *output_payload_buffer = iov[2].buffer;
+
+ return major_status;
+}
diff --git a/lib/gssapi/mech/gss_authorize_localname.c b/lib/gssapi/mech/gss_authorize_localname.c
index a0ad065da887..c04cfe01879e 100644
--- a/lib/gssapi/mech/gss_authorize_localname.c
+++ b/lib/gssapi/mech/gss_authorize_localname.c
@@ -114,8 +114,8 @@ attr_authorize_localname(OM_uint32 *minor_status,
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_authorize_localname(OM_uint32 *minor_status,
- const gss_name_t gss_name,
- const gss_name_t gss_user)
+ gss_const_name_t gss_name,
+ gss_const_name_t gss_user)
{
OM_uint32 major_status;
@@ -164,7 +164,7 @@ gss_authorize_localname(OM_uint32 *minor_status,
}
GSSAPI_LIB_FUNCTION int GSSAPI_LIB_CALL
-gss_userok(const gss_name_t name,
+gss_userok(gss_const_name_t name,
const char *user)
{
OM_uint32 major_status, minor_status;
diff --git a/lib/gssapi/mech/gss_canonicalize_name.c b/lib/gssapi/mech/gss_canonicalize_name.c
index bd8ff5212071..4918e5e0034e 100644
--- a/lib/gssapi/mech/gss_canonicalize_name.c
+++ b/lib/gssapi/mech/gss_canonicalize_name.c
@@ -54,7 +54,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_canonicalize_name(OM_uint32 *minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
const gss_OID mech_type,
gss_name_t *output_name)
{
diff --git a/lib/gssapi/mech/gss_compare_name.c b/lib/gssapi/mech/gss_compare_name.c
index 70b4b1c20673..18a8536ab4fa 100644
--- a/lib/gssapi/mech/gss_compare_name.c
+++ b/lib/gssapi/mech/gss_compare_name.c
@@ -30,8 +30,8 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_compare_name(OM_uint32 *minor_status,
- const gss_name_t name1_arg,
- const gss_name_t name2_arg,
+ gss_const_name_t name1_arg,
+ gss_const_name_t name2_arg,
int *name_equal)
{
struct _gss_name *name1 = (struct _gss_name *) name1_arg;
@@ -47,7 +47,7 @@ gss_compare_name(OM_uint32 *minor_status,
if (!gss_oid_equal(&name1->gn_type, &name2->gn_type)) {
*name_equal = 0;
} else if (name1->gn_value.length != name2->gn_value.length ||
- memcmp(name1->gn_value.value, name1->gn_value.value,
+ memcmp(name1->gn_value.value, name2->gn_value.value,
name1->gn_value.length)) {
*name_equal = 0;
}
diff --git a/lib/gssapi/mech/gss_context_time.c b/lib/gssapi/mech/gss_context_time.c
index 69434ee898e5..a5b646cf6625 100644
--- a/lib/gssapi/mech/gss_context_time.c
+++ b/lib/gssapi/mech/gss_context_time.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_context_time(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
OM_uint32 *time_rec)
{
struct _gss_context *ctx = (struct _gss_context *) context_handle;
diff --git a/lib/gssapi/mech/gss_delete_sec_context.c b/lib/gssapi/mech/gss_delete_sec_context.c
index ce57a76682ab..69d9cb6a07d7 100644
--- a/lib/gssapi/mech/gss_delete_sec_context.c
+++ b/lib/gssapi/mech/gss_delete_sec_context.c
@@ -33,7 +33,7 @@ gss_delete_sec_context(OM_uint32 *minor_status,
gss_ctx_id_t *context_handle,
gss_buffer_t output_token)
{
- OM_uint32 major_status;
+ OM_uint32 major_status = GSS_S_COMPLETE;
struct _gss_context *ctx = (struct _gss_context *) *context_handle;
if (output_token)
@@ -53,5 +53,5 @@ gss_delete_sec_context(OM_uint32 *minor_status,
*context_handle = GSS_C_NO_CONTEXT;
}
- return (GSS_S_COMPLETE);
+ return (major_status);
}
diff --git a/lib/gssapi/mech/gss_display_name.c b/lib/gssapi/mech/gss_display_name.c
index 524a586fbef8..a4af66a90474 100644
--- a/lib/gssapi/mech/gss_display_name.c
+++ b/lib/gssapi/mech/gss_display_name.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_display_name(OM_uint32 *minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t output_name_buffer,
gss_OID *output_name_type)
{
diff --git a/lib/gssapi/mech/gss_display_status.c b/lib/gssapi/mech/gss_display_status.c
index 1e508caa9baf..a79ef350dc93 100644
--- a/lib/gssapi/mech/gss_display_status.c
+++ b/lib/gssapi/mech/gss_display_status.c
@@ -134,25 +134,43 @@ supplementary_error(OM_uint32 v)
return msgs[v];
}
-
+/**
+ * Convert a GSS-API status code to text
+ *
+ * @param minor_status minor status code
+ * @param status_value status value to convert
+ * @param status_type One of:
+ * GSS_C_GSS_CODE - status_value is a GSS status code,
+ * GSS_C_MECH_CODE - status_value is a mechanism status code
+ * @param mech_type underlying mechanism. Use GSS_C_NO_OID to obtain the
+ * system default.
+ * @param message_context state information to extract further messages from the
+ * status_value
+ * @param status_string the allocated text representation. Release with
+ * gss_release_buffer()
+ *
+ * @returns a gss_error code.
+ *
+ * @ingroup gssapi
+ */
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_display_status(OM_uint32 *minor_status,
OM_uint32 status_value,
int status_type,
const gss_OID mech_type,
- OM_uint32 *message_content,
+ OM_uint32 *message_context,
gss_buffer_t status_string)
{
OM_uint32 major_status;
_mg_buffer_zero(status_string);
- *message_content = 0;
+ *message_context = 0;
major_status = _gss_mg_get_error(mech_type, status_type,
status_value, status_string);
if (major_status == GSS_S_COMPLETE) {
- *message_content = 0;
+ *message_context = 0;
*minor_status = 0;
return GSS_S_COMPLETE;
}
diff --git a/lib/gssapi/mech/gss_duplicate_name.c b/lib/gssapi/mech/gss_duplicate_name.c
index a76c87cb852a..d6aaf49233fe 100644
--- a/lib/gssapi/mech/gss_duplicate_name.c
+++ b/lib/gssapi/mech/gss_duplicate_name.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_duplicate_name(OM_uint32 *minor_status,
- const gss_name_t src_name,
+ gss_const_name_t src_name,
gss_name_t *dest_name)
{
OM_uint32 major_status;
diff --git a/lib/gssapi/mech/gss_export_name.c b/lib/gssapi/mech/gss_export_name.c
index 3e6e62681913..7365c720d28e 100644
--- a/lib/gssapi/mech/gss_export_name.c
+++ b/lib/gssapi/mech/gss_export_name.c
@@ -28,9 +28,23 @@
#include "mech_locl.h"
+/**
+ * Convert a GGS-API name from internal form to contiguous string.
+ *
+ * @sa gss_import_name(), @ref internalVSmechname.
+ *
+ * @param minor_status minor status code
+ * @param input_name input name in internal name form
+ * @param exported_name output name in contiguos string form
+ *
+ * @returns a gss_error code, see gss_display_status() about printing
+ * the error code.
+ *
+ * @ingroup gssapi
+ */
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_export_name(OM_uint32 *minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t exported_name)
{
struct _gss_name *name = (struct _gss_name *) input_name;
diff --git a/lib/gssapi/mech/gss_get_mic.c b/lib/gssapi/mech/gss_get_mic.c
index 6eebfe0bbb03..8663053d48e7 100644
--- a/lib/gssapi/mech/gss_get_mic.c
+++ b/lib/gssapi/mech/gss_get_mic.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_get_mic(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_qop_t qop_req,
const gss_buffer_t message_buffer,
gss_buffer_t message_token)
diff --git a/lib/gssapi/mech/gss_import_name.c b/lib/gssapi/mech/gss_import_name.c
index d1b3dc95b4a4..4c1d940d9af8 100644
--- a/lib/gssapi/mech/gss_import_name.c
+++ b/lib/gssapi/mech/gss_import_name.c
@@ -149,7 +149,7 @@ _gss_import_export_name(OM_uint32 *minor_status,
}
/**
- * Import a name internal or mechanism name
+ * Convert a GGS-API name from contiguous string to internal form.
*
* Type of name and their format:
* - GSS_C_NO_OID
@@ -159,12 +159,12 @@ _gss_import_export_name(OM_uint32 *minor_status,
* - GSS_C_NT_ANONYMOUS
* - GSS_KRB5_NT_PRINCIPAL_NAME
*
- * For more information about @ref internalVSmechname.
+ * @sa gss_export_name(), @ref internalVSmechname.
*
- * @param minor_status minor status code
- * @param input_name_buffer import name buffer
- * @param input_name_type type of the import name buffer
- * @param output_name the resulting type, release with
+ * @param minor_status minor status code
+ * @param input_name_buffer import name buffer
+ * @param input_name_type type of the import name buffer
+ * @param output_name the resulting type, release with
* gss_release_name(), independent of input_name
*
* @returns a gss_error code, see gss_display_status() about printing
diff --git a/lib/gssapi/mech/gss_indicate_mechs.c b/lib/gssapi/mech/gss_indicate_mechs.c
index 8fd53d956d58..12d7f1ae300e 100644
--- a/lib/gssapi/mech/gss_indicate_mechs.c
+++ b/lib/gssapi/mech/gss_indicate_mechs.c
@@ -43,6 +43,7 @@ gss_indicate_mechs(OM_uint32 *minor_status,
if (major_status)
return (major_status);
+ /* XXX We ignore ENOMEM from gss_add_oid_set_member() */
HEIM_SLIST_FOREACH(m, &_gss_mechs, gm_link) {
if (m->gm_mech.gm_indicate_mechs) {
major_status = m->gm_mech.gm_indicate_mechs(
@@ -50,11 +51,11 @@ gss_indicate_mechs(OM_uint32 *minor_status,
if (major_status)
continue;
for (i = 0; i < set->count; i++)
- major_status = gss_add_oid_set_member(
+ gss_add_oid_set_member(
minor_status, &set->elements[i], mech_set);
gss_release_oid_set(minor_status, &set);
} else {
- major_status = gss_add_oid_set_member(
+ gss_add_oid_set_member(
minor_status, &m->gm_mech_oid, mech_set);
}
}
diff --git a/lib/gssapi/mech/gss_init_sec_context.c b/lib/gssapi/mech/gss_init_sec_context.c
index af0170a50a51..21e02aea6972 100644
--- a/lib/gssapi/mech/gss_init_sec_context.c
+++ b/lib/gssapi/mech/gss_init_sec_context.c
@@ -29,7 +29,7 @@
#include "mech_locl.h"
static gss_cred_id_t
-_gss_mech_cred_find(gss_cred_id_t cred_handle, gss_OID mech_type)
+_gss_mech_cred_find(gss_const_cred_id_t cred_handle, gss_OID mech_type)
{
struct _gss_cred *cred = (struct _gss_cred *)cred_handle;
struct _gss_mechanism_cred *mc;
@@ -107,9 +107,9 @@ _gss_mech_cred_find(gss_cred_id_t cred_handle, gss_OID mech_type)
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_init_sec_context(OM_uint32 * minor_status,
- const gss_cred_id_t initiator_cred_handle,
+ gss_const_cred_id_t initiator_cred_handle,
gss_ctx_id_t * context_handle,
- const gss_name_t target_name,
+ gss_const_name_t target_name,
const gss_OID input_mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
@@ -125,7 +125,7 @@ gss_init_sec_context(OM_uint32 * minor_status,
struct _gss_name *name = (struct _gss_name *) target_name;
struct _gss_mechanism_name *mn;
struct _gss_context *ctx = (struct _gss_context *) *context_handle;
- gss_cred_id_t cred_handle;
+ gss_const_cred_id_t cred_handle;
int allocated_ctx;
gss_OID mech_type = input_mech_type;
@@ -172,7 +172,7 @@ gss_init_sec_context(OM_uint32 * minor_status,
major_status = _gss_find_mn(minor_status, name, mech_type, &mn);
if (major_status != GSS_S_COMPLETE) {
if (allocated_ctx)
- free(ctx);
+ free(ctx);
return major_status;
}
@@ -184,6 +184,13 @@ gss_init_sec_context(OM_uint32 * minor_status,
else
cred_handle = _gss_mech_cred_find(initiator_cred_handle, mech_type);
+ if (initiator_cred_handle != GSS_C_NO_CREDENTIAL &&
+ cred_handle == NULL) {
+ if (allocated_ctx)
+ free(ctx);
+ return GSS_S_NO_CRED;
+ }
+
major_status = m->gm_init_sec_context(minor_status,
cred_handle,
&ctx->gc_ctx,
diff --git a/lib/gssapi/mech/gss_inquire_context.c b/lib/gssapi/mech/gss_inquire_context.c
index 2568075988f1..aedaa6cb9ff4 100644
--- a/lib/gssapi/mech/gss_inquire_context.c
+++ b/lib/gssapi/mech/gss_inquire_context.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_context(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_name_t *src_name,
gss_name_t *targ_name,
OM_uint32 *lifetime_rec,
diff --git a/lib/gssapi/mech/gss_inquire_cred.c b/lib/gssapi/mech/gss_inquire_cred.c
index 1db0f233033c..992514a9acdf 100644
--- a/lib/gssapi/mech/gss_inquire_cred.c
+++ b/lib/gssapi/mech/gss_inquire_cred.c
@@ -44,7 +44,7 @@ updateusage(gss_cred_usage_t usage, int *usagemask)
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_cred(OM_uint32 *minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
gss_name_t *name_ret,
OM_uint32 *lifetime,
gss_cred_usage_t *cred_usage,
diff --git a/lib/gssapi/mech/gss_inquire_cred_by_mech.c b/lib/gssapi/mech/gss_inquire_cred_by_mech.c
index e7746e46578d..7bd0bfaad90a 100644
--- a/lib/gssapi/mech/gss_inquire_cred_by_mech.c
+++ b/lib/gssapi/mech/gss_inquire_cred_by_mech.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_cred_by_mech(OM_uint32 *minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID mech_type,
gss_name_t *cred_name,
OM_uint32 *initiator_lifetime,
diff --git a/lib/gssapi/mech/gss_inquire_cred_by_oid.c b/lib/gssapi/mech/gss_inquire_cred_by_oid.c
index e674dd48f3e4..8836a09ffe47 100644
--- a/lib/gssapi/mech/gss_inquire_cred_by_oid.c
+++ b/lib/gssapi/mech/gss_inquire_cred_by_oid.c
@@ -34,7 +34,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_cred_by_oid (OM_uint32 *minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
diff --git a/lib/gssapi/mech/gss_inquire_mechs_for_name.c b/lib/gssapi/mech/gss_inquire_mechs_for_name.c
index f8eab82dc1cd..8fd2286ea7c3 100644
--- a/lib/gssapi/mech/gss_inquire_mechs_for_name.c
+++ b/lib/gssapi/mech/gss_inquire_mechs_for_name.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_mechs_for_name(OM_uint32 *minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_OID_set *mech_types)
{
OM_uint32 major_status;
diff --git a/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c b/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c
index cc6e5c9cb6e2..ac45265b3ed1 100644
--- a/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c
+++ b/lib/gssapi/mech/gss_inquire_sec_context_by_oid.c
@@ -34,7 +34,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_inquire_sec_context_by_oid (OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
diff --git a/lib/gssapi/mech/gss_mech_switch.c b/lib/gssapi/mech/gss_mech_switch.c
index 55e01094ff91..58b187eda296 100644
--- a/lib/gssapi/mech/gss_mech_switch.c
+++ b/lib/gssapi/mech/gss_mech_switch.c
@@ -315,6 +315,8 @@ _gss_load_mech(void)
goto bad;
m->gm_so = so;
+ m->gm_mech_oid = mech_oid;
+ m->gm_mech.gm_name = strdup(name);
m->gm_mech.gm_mech_oid = mech_oid;
m->gm_mech.gm_flags = 0;
m->gm_mech.gm_compat = calloc(1, sizeof(struct gss_mech_compat_desc_struct));
@@ -381,7 +383,7 @@ _gss_load_mech(void)
OPTSYM(set_name_attribute);
OPTSYM(delete_name_attribute);
OPTSYM(export_name_composite);
- OPTSYM(pname_to_uid);
+ OPTSYM(localname);
OPTSPISYM(authorize_localname);
mi = dlsym(so, "gss_mo_init");
@@ -414,6 +416,7 @@ _gss_load_mech(void)
if (m != NULL) {
free(m->gm_mech.gm_compat);
free(m->gm_mech.gm_mech_oid.elements);
+ free((char *)m->gm_mech.gm_name);
free(m);
}
dlclose(so);
diff --git a/lib/gssapi/mech/gss_mo.c b/lib/gssapi/mech/gss_mo.c
index ad74d9237a2d..d0cde08ef1b8 100644
--- a/lib/gssapi/mech/gss_mo.c
+++ b/lib/gssapi/mech/gss_mo.c
@@ -203,6 +203,7 @@ make_sasl_name(OM_uint32 *minor, const gss_OID mech, char sasl_name[16])
EVP_DigestUpdate(ctx, hdr, 2);
EVP_DigestUpdate(ctx, mech->elements, mech->length);
EVP_DigestFinal_ex(ctx, hash, NULL);
+ EVP_MD_CTX_destroy(ctx);
memcpy(p, "GS2-", 4);
p += 4;
diff --git a/lib/gssapi/mech/gss_oid.c b/lib/gssapi/mech/gss_oid.c
index 916d1e4dda5e..fe9686d53638 100644
--- a/lib/gssapi/mech/gss_oid.c
+++ b/lib/gssapi/mech/gss_oid.c
@@ -104,10 +104,10 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_mech_name_oid_desc = { 6, rk_UNCONST
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_mech_description_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x66") };
/* GSS_C_CRED_PASSWORD - 1.2.752.43.13.200 */
-gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_cred_password_oid_desc = { 7, "\x2a\x85\x70\x2b\x0d\x81\x48" };
+gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_cred_password_oid_desc = { 7, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x81\x48") };
/* GSS_C_CRED_CERTIFICATE - 1.2.752.43.13.201 */
-gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_cred_certificate_oid_desc = { 7, "\x2a\x85\x70\x2b\x0d\x81\x49" };
+gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_cred_certificate_oid_desc = { 7, rk_UNCONST("\x2a\x85\x70\x2b\x0d\x81\x49") };
/* GSS_SASL_DIGEST_MD5_MECHANISM - 1.2.752.43.14.1 */
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_sasl_digest_md5_mechanism_oid_desc = { 6, rk_UNCONST("\x2a\x85\x70\x2b\x0e\x01") };
@@ -139,8 +139,8 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_ntlm_mechanism_oid_desc = { 10, rk_UNCONS
/* GSS_SPNEGO_MECHANISM - 1.3.6.1.5.5.2 */
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_spnego_mechanism_oid_desc = { 6, rk_UNCONST("\x2b\x06\x01\x05\x05\x02") };
-/* GSS_C_PEER_HAS_UPDATED_SPNEGO - 1.3.6.1.4.1.9513.19.5 */
-gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_peer_has_updated_spnego_oid_desc = { 9, rk_UNCONST("\x2b\x06\x01\x04\x01\xca\x29\x13\x05") };
+/* GSS_C_PEER_HAS_UPDATED_SPNEGO - 1.3.6.1.4.1.5322.19.5 */
+gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_peer_has_updated_spnego_oid_desc = { 9, rk_UNCONST("\x2b\x06\x01\x04\x01\xa9\x4a\x13\x05") };
/* GSS_C_MA_MECH_CONCRETE - 1.3.6.1.5.5.13.1 */
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_mech_concrete_oid_desc = { 7, rk_UNCONST("\x2b\x06\x01\x05\x05\x0d\x01") };
@@ -224,43 +224,43 @@ gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_compress_oid_desc = { 7, rk_UNCONST(
gss_OID_desc GSSAPI_LIB_VARIABLE __gss_c_ma_ctx_trans_oid_desc = { 7, rk_UNCONST("\x2b\x06\x01\x05\x05\x0d\x1b") };
struct _gss_oid_name_table _gss_ont_ma[] = {
- { GSS_C_MA_COMPRESS, "GSS_C_MA_COMPRESS", "compress", "" },
+ { GSS_C_MA_AUTH_INIT, "GSS_C_MA_AUTH_INIT", "auth-init-princ", "" },
+ { GSS_C_MA_AUTH_INIT_ANON, "GSS_C_MA_AUTH_INIT_ANON", "auth-init-princ-anon", "" },
+ { GSS_C_MA_AUTH_INIT_INIT, "GSS_C_MA_AUTH_INIT_INIT", "auth-init-princ-initial", "" },
+ { GSS_C_MA_AUTH_TARG, "GSS_C_MA_AUTH_TARG", "auth-targ-princ", "" },
+ { GSS_C_MA_AUTH_TARG_ANON, "GSS_C_MA_AUTH_TARG_ANON", "auth-targ-princ-anon", "" },
{ GSS_C_MA_AUTH_TARG_INIT, "GSS_C_MA_AUTH_TARG_INIT", "auth-targ-princ-initial", "" },
{ GSS_C_MA_CBINDINGS, "GSS_C_MA_CBINDINGS", "channel-bindings", "" },
- { GSS_C_MA_WRAP, "GSS_C_MA_WRAP", "wrap", "" },
+ { GSS_C_MA_COMPRESS, "GSS_C_MA_COMPRESS", "compress", "" },
+ { GSS_C_MA_CONF_PROT, "GSS_C_MA_CONF_PROT", "conf-prot", "" },
+ { GSS_C_MA_CTX_TRANS, "GSS_C_MA_CTX_TRANS", "context-transfer", "" },
+ { GSS_C_MA_DELEG_CRED, "GSS_C_MA_DELEG_CRED", "deleg-cred", "" },
+ { GSS_C_MA_DEPRECATED, "GSS_C_MA_DEPRECATED", "mech-deprecated", "" },
+ { GSS_C_MA_INTEG_PROT, "GSS_C_MA_INTEG_PROT", "integ-prot", "" },
{ GSS_C_MA_ITOK_FRAMED, "GSS_C_MA_ITOK_FRAMED", "initial-is-framed", "" },
- { GSS_C_MA_MECH_NEGO, "GSS_C_MA_MECH_NEGO", "mech-negotiation-mech", "" },
{ GSS_C_MA_MECH_COMPOSITE, "GSS_C_MA_MECH_COMPOSITE", "composite-mech", "" },
- { GSS_C_MA_REPLAY_DET, "GSS_C_MA_REPLAY_DET", "replay-detection", "" },
- { GSS_C_MA_AUTH_INIT_ANON, "GSS_C_MA_AUTH_INIT_ANON", "auth-init-princ-anon", "" },
- { GSS_C_MA_PROT_READY, "GSS_C_MA_PROT_READY", "prot-ready", "" },
- { GSS_C_MA_AUTH_INIT, "GSS_C_MA_AUTH_INIT", "auth-init-princ", "" },
- { GSS_C_MA_PFS, "GSS_C_MA_PFS", "pfs", "" },
- { GSS_C_MA_CONF_PROT, "GSS_C_MA_CONF_PROT", "conf-prot", "" },
- { GSS_C_MA_MECH_PSEUDO, "GSS_C_MA_MECH_PSEUDO", "pseudo-mech", "" },
- { GSS_C_MA_AUTH_TARG, "GSS_C_MA_AUTH_TARG", "auth-targ-princ", "" },
+ { GSS_C_MA_MECH_CONCRETE, "GSS_C_MA_MECH_CONCRETE", "concrete-mech", "Indicates that a mech is neither a pseudo-mechanism nor a composite mechanism" },
+ { GSS_C_MA_MECH_DESCRIPTION, "GSS_C_MA_MECH_DESCRIPTION", "Mech description", "The long description of the mechanism" },
+ { GSS_C_MA_MECH_GLUE, "GSS_C_MA_MECH_GLUE", "mech-glue", "" },
{ GSS_C_MA_MECH_NAME, "GSS_C_MA_MECH_NAME", "GSS mech name", "The name of the GSS-API mechanism" },
- { GSS_C_MA_NOT_MECH, "GSS_C_MA_NOT_MECH", "not-mech", "" },
+ { GSS_C_MA_MECH_NEGO, "GSS_C_MA_MECH_NEGO", "mech-negotiation-mech", "" },
+ { GSS_C_MA_MECH_PSEUDO, "GSS_C_MA_MECH_PSEUDO", "pseudo-mech", "" },
{ GSS_C_MA_MIC, "GSS_C_MA_MIC", "mic", "" },
- { GSS_C_MA_DEPRECATED, "GSS_C_MA_DEPRECATED", "mech-deprecated", "" },
- { GSS_C_MA_MECH_GLUE, "GSS_C_MA_MECH_GLUE", "mech-glue", "" },
- { GSS_C_MA_DELEG_CRED, "GSS_C_MA_DELEG_CRED", "deleg-cred", "" },
{ GSS_C_MA_NOT_DFLT_MECH, "GSS_C_MA_NOT_DFLT_MECH", "mech-not-default", "" },
- { GSS_C_MA_AUTH_TARG_ANON, "GSS_C_MA_AUTH_TARG_ANON", "auth-targ-princ-anon", "" },
- { GSS_C_MA_INTEG_PROT, "GSS_C_MA_INTEG_PROT", "integ-prot", "" },
- { GSS_C_MA_CTX_TRANS, "GSS_C_MA_CTX_TRANS", "context-transfer", "" },
- { GSS_C_MA_MECH_DESCRIPTION, "GSS_C_MA_MECH_DESCRIPTION", "Mech description", "The long description of the mechanism" },
+ { GSS_C_MA_NOT_MECH, "GSS_C_MA_NOT_MECH", "not-mech", "" },
{ GSS_C_MA_OOS_DET, "GSS_C_MA_OOS_DET", "oos-detection", "" },
- { GSS_C_MA_AUTH_INIT_INIT, "GSS_C_MA_AUTH_INIT_INIT", "auth-init-princ-initial", "" },
- { GSS_C_MA_MECH_CONCRETE, "GSS_C_MA_MECH_CONCRETE", "concrete-mech", "Indicates that a mech is neither a pseudo-mechanism nor a composite mechanism" },
+ { GSS_C_MA_PFS, "GSS_C_MA_PFS", "pfs", "" },
+ { GSS_C_MA_PROT_READY, "GSS_C_MA_PROT_READY", "prot-ready", "" },
+ { GSS_C_MA_REPLAY_DET, "GSS_C_MA_REPLAY_DET", "replay-detection", "" },
{ GSS_C_MA_SASL_MECH_NAME, "GSS_C_MA_SASL_MECH_NAME", "SASL mechanism name", "The name of the SASL mechanism" },
- { NULL }
+ { GSS_C_MA_WRAP, "GSS_C_MA_WRAP", "wrap", "" },
+ { NULL, NULL, NULL, NULL }
};
struct _gss_oid_name_table _gss_ont_mech[] = {
{ GSS_KRB5_MECHANISM, "GSS_KRB5_MECHANISM", "Kerberos 5", "Heimdal Kerberos 5 mechanism" },
- { GSS_SPNEGO_MECHANISM, "GSS_SPNEGO_MECHANISM", "SPNEGO", "Heimdal SPNEGO mechanism" },
{ GSS_NTLM_MECHANISM, "GSS_NTLM_MECHANISM", "NTLM", "Heimdal NTLM mechanism" },
- { NULL }
+ { GSS_SPNEGO_MECHANISM, "GSS_SPNEGO_MECHANISM", "SPNEGO", "Heimdal SPNEGO mechanism" },
+ { NULL, NULL, NULL, NULL }
};
diff --git a/lib/gssapi/mech/gss_pname_to_uid.c b/lib/gssapi/mech/gss_pname_to_uid.c
index c5f26949f2ae..315f0e0d8147 100644
--- a/lib/gssapi/mech/gss_pname_to_uid.c
+++ b/lib/gssapi/mech/gss_pname_to_uid.c
@@ -33,21 +33,21 @@
#include "mech_locl.h"
static OM_uint32
-mech_pname_to_uid(OM_uint32 *minor_status,
- struct _gss_mechanism_name *mn,
- uid_t *uidp)
+mech_localname(OM_uint32 *minor_status,
+ struct _gss_mechanism_name *mn,
+ gss_buffer_t localname)
{
OM_uint32 major_status = GSS_S_UNAVAILABLE;
*minor_status = 0;
- if (mn->gmn_mech->gm_pname_to_uid == NULL)
+ if (mn->gmn_mech->gm_localname == NULL)
return GSS_S_UNAVAILABLE;
- major_status = mn->gmn_mech->gm_pname_to_uid(minor_status,
- mn->gmn_name,
- mn->gmn_mech_oid,
- uidp);
+ major_status = mn->gmn_mech->gm_localname(minor_status,
+ mn->gmn_name,
+ mn->gmn_mech_oid,
+ localname);
if (GSS_ERROR(major_status))
_gss_mg_error(mn->gmn_mech, major_status, *minor_status);
@@ -55,86 +55,55 @@ mech_pname_to_uid(OM_uint32 *minor_status,
}
static OM_uint32
-attr_pname_to_uid(OM_uint32 *minor_status,
- struct _gss_mechanism_name *mn,
- uid_t *uidp)
+attr_localname(OM_uint32 *minor_status,
+ struct _gss_mechanism_name *mn,
+ gss_buffer_t localname)
{
-#ifdef NO_LOCALNAME
- return GSS_S_UNAVAILABLE;
-#else
OM_uint32 major_status = GSS_S_UNAVAILABLE;
OM_uint32 tmpMinor;
+ gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc display_value = GSS_C_EMPTY_BUFFER;
+ int authenticated = 0, complete = 0;
int more = -1;
*minor_status = 0;
+ localname->length = 0;
+ localname->value = NULL;
+
if (mn->gmn_mech->gm_get_name_attribute == NULL)
return GSS_S_UNAVAILABLE;
- while (more != 0) {
- gss_buffer_desc value;
- gss_buffer_desc display_value;
- int authenticated = 0, complete = 0;
-#ifdef POSIX_GETPWNAM_R
- char pwbuf[2048];
- struct passwd pw, *pwd;
-#else
- struct passwd *pwd;
-#endif
- char *localname;
-
- major_status = mn->gmn_mech->gm_get_name_attribute(minor_status,
- mn->gmn_name,
- GSS_C_ATTR_LOCAL_LOGIN_USER,
- &authenticated,
- &complete,
- &value,
- &display_value,
- &more);
- if (GSS_ERROR(major_status)) {
- _gss_mg_error(mn->gmn_mech, major_status, *minor_status);
- break;
- }
-
- localname = malloc(value.length + 1);
- if (localname == NULL) {
- major_status = GSS_S_FAILURE;
- *minor_status = ENOMEM;
- break;
- }
-
- memcpy(localname, value.value, value.length);
- localname[value.length] = '\0';
-
-#ifdef POSIX_GETPWNAM_R
- if (getpwnam_r(localname, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0)
- pwd = NULL;
-#else
- pwd = getpwnam(localname);
-#endif
+ major_status = mn->gmn_mech->gm_get_name_attribute(minor_status,
+ mn->gmn_name,
+ GSS_C_ATTR_LOCAL_LOGIN_USER,
+ &authenticated,
+ &complete,
+ &value,
+ &display_value,
+ &more);
+ if (GSS_ERROR(major_status)) {
+ _gss_mg_error(mn->gmn_mech, major_status, *minor_status);
+ return major_status;
+ }
- free(localname);
+ if (authenticated) {
+ *localname = value;
+ } else {
+ major_status = GSS_S_UNAVAILABLE;
gss_release_buffer(&tmpMinor, &value);
- gss_release_buffer(&tmpMinor, &display_value);
-
- if (pwd != NULL) {
- *uidp = pwd->pw_uid;
- major_status = GSS_S_COMPLETE;
- *minor_status = 0;
- break;
- } else
- major_status = GSS_S_UNAVAILABLE;
}
+ gss_release_buffer(&tmpMinor, &display_value);
+
return major_status;
-#endif /* NO_LOCALNAME */
}
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
-gss_pname_to_uid(OM_uint32 *minor_status,
- const gss_name_t pname,
- const gss_OID mech_type,
- uid_t *uidp)
+gss_localname(OM_uint32 *minor_status,
+ gss_const_name_t pname,
+ const gss_OID mech_type,
+ gss_buffer_t localname)
{
OM_uint32 major_status = GSS_S_UNAVAILABLE;
struct _gss_name *name = (struct _gss_name *) pname;
@@ -147,14 +116,14 @@ gss_pname_to_uid(OM_uint32 *minor_status,
if (GSS_ERROR(major_status))
return major_status;
- major_status = mech_pname_to_uid(minor_status, mn, uidp);
+ major_status = mech_localname(minor_status, mn, localname);
if (major_status != GSS_S_COMPLETE)
- major_status = attr_pname_to_uid(minor_status, mn, uidp);
+ major_status = attr_localname(minor_status, mn, localname);
} else {
HEIM_SLIST_FOREACH(mn, &name->gn_mn, gmn_link) {
- major_status = mech_pname_to_uid(minor_status, mn, uidp);
+ major_status = mech_localname(minor_status, mn, localname);
if (major_status != GSS_S_COMPLETE)
- major_status = attr_pname_to_uid(minor_status, mn, uidp);
+ major_status = attr_localname(minor_status, mn, localname);
if (major_status != GSS_S_UNAVAILABLE)
break;
}
@@ -165,3 +134,60 @@ gss_pname_to_uid(OM_uint32 *minor_status,
return major_status;
}
+
+
+GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
+gss_pname_to_uid(OM_uint32 *minor_status,
+ gss_const_name_t pname,
+ const gss_OID mech_type,
+ uid_t *uidp)
+{
+#ifdef NO_LOCALNAME
+ return GSS_S_UNAVAILABLE;
+#else
+ OM_uint32 major, tmpMinor;
+ gss_buffer_desc localname = GSS_C_EMPTY_BUFFER;
+ char *szLocalname;
+#ifdef POSIX_GETPWNAM_R
+ char pwbuf[2048];
+ struct passwd pw, *pwd;
+#else
+ struct passwd *pwd;
+#endif
+
+ major = gss_localname(minor_status, pname, mech_type, &localname);
+ if (GSS_ERROR(major))
+ return major;
+
+ szLocalname = malloc(localname.length + 1);
+ if (szLocalname == NULL) {
+ gss_release_buffer(&tmpMinor, &localname);
+ *minor_status = ENOMEM;
+ return GSS_S_FAILURE;
+ }
+
+ memcpy(szLocalname, localname.value, localname.length);
+ szLocalname[localname.length] = '\0';
+
+#ifdef POSIX_GETPWNAM_R
+ if (getpwnam_r(szLocalname, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0)
+ pwd = NULL;
+#else
+ pwd = getpwnam(szLocalname);
+#endif
+
+ gss_release_buffer(&tmpMinor, &localname);
+ free(szLocalname);
+
+ *minor_status = 0;
+
+ if (pwd != NULL) {
+ *uidp = pwd->pw_uid;
+ major = GSS_S_COMPLETE;
+ } else {
+ major = GSS_S_UNAVAILABLE;
+ }
+
+ return major;
+#endif
+}
diff --git a/lib/gssapi/mech/gss_process_context_token.c b/lib/gssapi/mech/gss_process_context_token.c
index e8e9b56cdc7f..d10eb47dbae3 100644
--- a/lib/gssapi/mech/gss_process_context_token.c
+++ b/lib/gssapi/mech/gss_process_context_token.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_process_context_token(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t token_buffer)
{
struct _gss_context *ctx = (struct _gss_context *) context_handle;
diff --git a/lib/gssapi/mech/gss_store_cred.c b/lib/gssapi/mech/gss_store_cred.c
index 4d2bfdec8b1a..a92611570eb6 100644
--- a/lib/gssapi/mech/gss_store_cred.c
+++ b/lib/gssapi/mech/gss_store_cred.c
@@ -45,7 +45,9 @@ gss_store_cred(OM_uint32 *minor_status,
{
struct _gss_cred *cred = (struct _gss_cred *) input_cred_handle;
struct _gss_mechanism_cred *mc;
- OM_uint32 maj, junk;
+ OM_uint32 maj = GSS_S_FAILURE;
+ OM_uint32 junk;
+ size_t successes = 0;
if (minor_status == NULL)
return GSS_S_FAILURE;
@@ -69,26 +71,30 @@ gss_store_cred(OM_uint32 *minor_status,
if (m == NULL || m->gm_store_cred == NULL)
continue;
- if (desired_mech) {
- maj = gss_oid_equal(&m->gm_mech_oid, desired_mech);
- if (maj != 0)
- continue;
- }
+ if (desired_mech != GSS_C_NO_OID &&
+ !gss_oid_equal(&m->gm_mech_oid, desired_mech))
+ continue;
maj = (m->gm_store_cred)(minor_status, mc->gmc_cred,
cred_usage, desired_mech, overwrite_cred,
default_cred, NULL, cred_usage_stored);
- if (maj != GSS_S_COMPLETE) {
- gss_release_oid_set(&junk, elements_stored);
- return maj;
- }
+ if (maj == GSS_S_COMPLETE) {
+ if (elements_stored)
+ gss_add_oid_set_member(&junk, desired_mech, elements_stored);
+ successes++;
+ } else if (desired_mech != GSS_C_NO_OID) {
+ gss_release_oid_set(&junk, elements_stored);
+ return maj;
+ }
- if (elements_stored) {
- gss_add_oid_set_member(&junk,
- &m->gm_mech_oid,
- elements_stored);
- }
+ }
+ if (successes == 0) {
+ if (maj != GSS_S_COMPLETE)
+ return maj; /* last failure */
+ return GSS_S_FAILURE;
}
+
+ *minor_status = 0;
return GSS_S_COMPLETE;
}
diff --git a/lib/gssapi/mech/gss_unwrap.c b/lib/gssapi/mech/gss_unwrap.c
index d0d18aca25b1..6bf6088f37dd 100644
--- a/lib/gssapi/mech/gss_unwrap.c
+++ b/lib/gssapi/mech/gss_unwrap.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_unwrap(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t input_message_buffer,
gss_buffer_t output_message_buffer,
int *conf_state,
diff --git a/lib/gssapi/mech/gss_verify_mic.c b/lib/gssapi/mech/gss_verify_mic.c
index a791dc732761..ae3b52f72af5 100644
--- a/lib/gssapi/mech/gss_verify_mic.c
+++ b/lib/gssapi/mech/gss_verify_mic.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_verify_mic(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t message_buffer,
const gss_buffer_t token_buffer,
gss_qop_t *qop_state)
diff --git a/lib/gssapi/mech/gss_wrap.c b/lib/gssapi/mech/gss_wrap.c
index d9864b36ccb4..82378d3d0d05 100644
--- a/lib/gssapi/mech/gss_wrap.c
+++ b/lib/gssapi/mech/gss_wrap.c
@@ -45,7 +45,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_wrap(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
const gss_buffer_t input_message_buffer,
diff --git a/lib/gssapi/mech/gss_wrap_size_limit.c b/lib/gssapi/mech/gss_wrap_size_limit.c
index 9bebcf6cf08e..3bcd9eceeda2 100644
--- a/lib/gssapi/mech/gss_wrap_size_limit.c
+++ b/lib/gssapi/mech/gss_wrap_size_limit.c
@@ -30,7 +30,7 @@
GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
gss_wrap_size_limit(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
OM_uint32 req_output_size,
diff --git a/lib/gssapi/mech/mech.5 b/lib/gssapi/mech/mech.5
index e7b083d3158a..56e916e3aea3 100644
--- a/lib/gssapi/mech/mech.5
+++ b/lib/gssapi/mech/mech.5
@@ -91,4 +91,4 @@ manual page example first appeared in
.Sh AUTHORS
This
manual page was written by
-.An Doug Rabson Aq dfr@FreeBSD.org .
+.An Doug Rabson Aq Mt dfr@FreeBSD.org .
diff --git a/lib/gssapi/mech/mech.cat5 b/lib/gssapi/mech/mech.cat5
index 821a193df6e1..998079f2959c 100644
--- a/lib/gssapi/mech/mech.cat5
+++ b/lib/gssapi/mech/mech.cat5
@@ -56,6 +56,6 @@ HHIISSTTOORRYY
The mmeecchh manual page example first appeared in FreeBSD 7.0.
AAUUTTHHOORRSS
- This manual page was written by Doug Rabson <dfr@FreeBSD.org>.
+ This manual page was written by Doug Rabson <_d_f_r_@_F_r_e_e_B_S_D_._o_r_g>.
BSD November 14, 2005 BSD
diff --git a/lib/gssapi/ntlm/accept_sec_context.c b/lib/gssapi/ntlm/accept_sec_context.c
index 533fc15df8f3..7ba55cd33d04 100644
--- a/lib/gssapi/ntlm/accept_sec_context.c
+++ b/lib/gssapi/ntlm/accept_sec_context.c
@@ -68,7 +68,7 @@ OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_accept_sec_context
(OM_uint32 * minor_status,
gss_ctx_id_t * context_handle,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token_buffer,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t * src_name,
diff --git a/lib/gssapi/ntlm/acquire_cred.c b/lib/gssapi/ntlm/acquire_cred.c
index b186271f590f..9e2cca4ffc4b 100644
--- a/lib/gssapi/ntlm/acquire_cred.c
+++ b/lib/gssapi/ntlm/acquire_cred.c
@@ -33,18 +33,18 @@
#include "ntlm.h"
-OM_uint32 GSSAPI_CALLCONV _gss_ntlm_acquire_cred
- (OM_uint32 * min_stat,
- const gss_name_t desired_name,
- OM_uint32 time_req,
- const gss_OID_set desired_mechs,
- gss_cred_usage_t cred_usage,
- gss_cred_id_t * output_cred_handle,
- gss_OID_set * actual_mechs,
- OM_uint32 * time_rec
- )
+OM_uint32 GSSAPI_CALLCONV
+_gss_ntlm_acquire_cred(OM_uint32 *min_stat,
+ gss_const_name_t desired_name,
+ OM_uint32 time_req,
+ const gss_OID_set desired_mechs,
+ gss_cred_usage_t cred_usage,
+ gss_cred_id_t *output_cred_handle,
+ gss_OID_set *actual_mechs,
+ OM_uint32 *time_rec)
{
ntlm_name name = (ntlm_name) desired_name;
+ const char *domain = NULL;
OM_uint32 maj_stat;
ntlm_ctx ctx;
@@ -55,17 +55,14 @@ OM_uint32 GSSAPI_CALLCONV _gss_ntlm_acquire_cred
if (time_rec)
*time_rec = GSS_C_INDEFINITE;
- if (desired_name == NULL)
- return GSS_S_NO_CRED;
-
if (cred_usage == GSS_C_BOTH || cred_usage == GSS_C_ACCEPT) {
maj_stat = _gss_ntlm_allocate_ctx(min_stat, &ctx);
if (maj_stat != GSS_S_COMPLETE)
return maj_stat;
- maj_stat = (*ctx->server->nsi_probe)(min_stat, ctx->ictx,
- name->domain);
+ domain = name != NULL ? name->domain : NULL;
+ maj_stat = (*ctx->server->nsi_probe)(min_stat, ctx->ictx, domain);
{
gss_ctx_id_t context = (gss_ctx_id_t)ctx;
OM_uint32 junk;
@@ -79,7 +76,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_ntlm_acquire_cred
*min_stat = _gss_ntlm_get_user_cred(name, &cred);
if (*min_stat)
- return GSS_S_FAILURE;
+ return GSS_S_NO_CRED;
cred->usage = cred_usage;
*output_cred_handle = (gss_cred_id_t)cred;
diff --git a/lib/gssapi/ntlm/add_cred.c b/lib/gssapi/ntlm/add_cred.c
index 7c6b5ba255a0..151723262bc7 100644
--- a/lib/gssapi/ntlm/add_cred.c
+++ b/lib/gssapi/ntlm/add_cred.c
@@ -35,8 +35,8 @@
OM_uint32 GSSAPI_CALLCONV _gss_ntlm_add_cred (
OM_uint32 *minor_status,
- const gss_cred_id_t input_cred_handle,
- const gss_name_t desired_name,
+ gss_const_cred_id_t input_cred_handle,
+ gss_const_name_t desired_name,
const gss_OID desired_mech,
gss_cred_usage_t cred_usage,
OM_uint32 initiator_time_req,
diff --git a/lib/gssapi/ntlm/canonicalize_name.c b/lib/gssapi/ntlm/canonicalize_name.c
index 0ea64299dd0c..3409b3b759b9 100644
--- a/lib/gssapi/ntlm/canonicalize_name.c
+++ b/lib/gssapi/ntlm/canonicalize_name.c
@@ -36,7 +36,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_canonicalize_name (
OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
const gss_OID mech_type,
gss_name_t * output_name
)
diff --git a/lib/gssapi/ntlm/compare_name.c b/lib/gssapi/ntlm/compare_name.c
index 6e095bdee2a7..d185ab940e4c 100644
--- a/lib/gssapi/ntlm/compare_name.c
+++ b/lib/gssapi/ntlm/compare_name.c
@@ -35,8 +35,8 @@
OM_uint32 GSSAPI_CALLCONV _gss_ntlm_compare_name
(OM_uint32 * minor_status,
- const gss_name_t name1,
- const gss_name_t name2,
+ gss_const_name_t name1,
+ gss_const_name_t name2,
int * name_equal
)
{
diff --git a/lib/gssapi/ntlm/context_time.c b/lib/gssapi/ntlm/context_time.c
index 73debda48326..589ee2a71604 100644
--- a/lib/gssapi/ntlm/context_time.c
+++ b/lib/gssapi/ntlm/context_time.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gss_ntlm_context_time
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
OM_uint32 * time_rec
)
{
diff --git a/lib/gssapi/ntlm/creds.c b/lib/gssapi/ntlm/creds.c
index ffbf35504eea..84a710c69e60 100644
--- a/lib/gssapi/ntlm/creds.c
+++ b/lib/gssapi/ntlm/creds.c
@@ -38,7 +38,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_cred
(OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
gss_name_t * name,
OM_uint32 * lifetime,
gss_cred_usage_t * cred_usage,
@@ -60,8 +60,11 @@ _gss_ntlm_inquire_cred
n->domain = strdup(c->domain);
}
if (n == NULL || n->user == NULL || n->domain == NULL) {
- if (n)
+ if (n) {
free(n->user);
+ free(n->domain);
+ free(n);
+ }
*minor_status = ENOMEM;
return GSS_S_FAILURE;
}
diff --git a/lib/gssapi/ntlm/crypto.c b/lib/gssapi/ntlm/crypto.c
index 85dc638dda58..40469a7a7bda 100644
--- a/lib/gssapi/ntlm/crypto.c
+++ b/lib/gssapi/ntlm/crypto.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * Copyright (c) 2006-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -32,11 +32,17 @@
*/
#include "ntlm.h"
-
-uint32_t
-_krb5_crc_update (const char *p, size_t len, uint32_t res);
-void
-_krb5_crc_init_table(void);
+struct hx509_certs_data;
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+struct ContentInfo;
+struct AlgorithmIdentifier;
+struct _krb5_krb_auth_data;
+struct krb5_dh_moduli;
+struct _krb5_key_data;
+struct _krb5_encryption_type;
+struct _krb5_key_type;
+#include "krb5_locl.h"
/*
*
@@ -263,7 +269,7 @@ v2_unseal_message(gss_buffer_t in,
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_get_mic
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_qop_t qop_req,
const gss_buffer_t message_buffer,
gss_buffer_t message_token
@@ -338,7 +344,7 @@ _gss_ntlm_get_mic
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_verify_mic
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t message_buffer,
const gss_buffer_t token_buffer,
gss_qop_t * qop_state
@@ -424,7 +430,7 @@ _gss_ntlm_verify_mic
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_wrap_size_limit (
OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
OM_uint32 req_output_size,
@@ -455,7 +461,7 @@ _gss_ntlm_wrap_size_limit (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_wrap
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
const gss_buffer_t input_message_buffer,
@@ -526,7 +532,7 @@ _gss_ntlm_wrap
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_unwrap
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t input_message_buffer,
gss_buffer_t output_message_buffer,
int * conf_state,
diff --git a/lib/gssapi/ntlm/display_name.c b/lib/gssapi/ntlm/display_name.c
index 4f8e3e6828c4..59d2c8471915 100644
--- a/lib/gssapi/ntlm/display_name.c
+++ b/lib/gssapi/ntlm/display_name.c
@@ -36,7 +36,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_display_name
(OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t output_name_buffer,
gss_OID * output_name_type
)
diff --git a/lib/gssapi/ntlm/duplicate_name.c b/lib/gssapi/ntlm/duplicate_name.c
index 4ef574fb50ee..060fa55936ae 100644
--- a/lib/gssapi/ntlm/duplicate_name.c
+++ b/lib/gssapi/ntlm/duplicate_name.c
@@ -36,7 +36,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_duplicate_name (
OM_uint32 * minor_status,
- const gss_name_t src_name,
+ gss_const_name_t src_name,
gss_name_t * dest_name
)
{
diff --git a/lib/gssapi/ntlm/export_name.c b/lib/gssapi/ntlm/export_name.c
index 8fe69aaaf45e..e5bdca4c9dd7 100644
--- a/lib/gssapi/ntlm/export_name.c
+++ b/lib/gssapi/ntlm/export_name.c
@@ -36,7 +36,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_export_name
(OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t exported_name
)
{
diff --git a/lib/gssapi/ntlm/external.c b/lib/gssapi/ntlm/external.c
index d0474f4ec812..aea76cb783c9 100644
--- a/lib/gssapi/ntlm/external.c
+++ b/lib/gssapi/ntlm/external.c
@@ -120,6 +120,9 @@ static gssapi_mech_interface_desc ntlm_mech = {
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
+ NULL,
};
gssapi_mech_interface
diff --git a/lib/gssapi/ntlm/init_sec_context.c b/lib/gssapi/ntlm/init_sec_context.c
index bae04e174060..5499a1b814e7 100644
--- a/lib/gssapi/ntlm/init_sec_context.c
+++ b/lib/gssapi/ntlm/init_sec_context.c
@@ -35,11 +35,13 @@
static int
from_file(const char *fn, const char *target_domain,
- char **username, struct ntlm_buf *key)
+ char **domainp, char **usernamep, struct ntlm_buf *key)
{
char *str, buf[1024];
FILE *f;
+ *domainp = NULL;
+
f = fopen(fn, "r");
if (f == NULL)
return ENOENT;
@@ -52,14 +54,21 @@ from_file(const char *fn, const char *target_domain,
continue;
str = NULL;
d = strtok_r(buf, ":", &str);
- if (d && strcasecmp(target_domain, d) != 0)
+ free(*domainp);
+ *domainp = NULL;
+ if (d && target_domain != NULL && strcasecmp(target_domain, d) != 0)
continue;
+ *domainp = strdup(d);
+ if (*domainp == NULL)
+ return ENOMEM;
u = strtok_r(NULL, ":", &str);
p = strtok_r(NULL, ":", &str);
if (u == NULL || p == NULL)
continue;
- *username = strdup(u);
+ *usernamep = strdup(u);
+ if (*usernamep == NULL)
+ return ENOMEM;
heim_ntlm_nt_key(p, key);
@@ -74,17 +83,22 @@ from_file(const char *fn, const char *target_domain,
static int
get_user_file(const ntlm_name target_name,
- char **username, struct ntlm_buf *key)
+ char **domainp, char **usernamep, struct ntlm_buf *key)
{
+ const char *domain;
const char *fn;
+ *domainp = NULL;
+
if (issuid())
return ENOENT;
+ domain = target_name != NULL ? target_name->domain : NULL;
+
fn = getenv("NTLM_USER_FILE");
if (fn == NULL)
return ENOENT;
- if (from_file(fn, target_name->domain, username, key) == 0)
+ if (from_file(fn, domain, domainp, usernamep, key) == 0)
return 0;
return ENOENT;
@@ -95,7 +109,7 @@ get_user_file(const ntlm_name target_name,
*/
static int
-get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
+get_user_ccache(const ntlm_name name, char **domainp, char **usernamep, struct ntlm_buf *key)
{
krb5_context context = NULL;
krb5_principal client;
@@ -103,8 +117,10 @@ get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
krb5_error_code ret;
char *confname;
krb5_data data;
+ int aret;
- *username = NULL;
+ *domainp = NULL;
+ *usernamep = NULL;
krb5_data_zero(&data);
key->length = 0;
key->data = NULL;
@@ -123,22 +139,40 @@ get_user_ccache(const ntlm_name name, char **username, struct ntlm_buf *key)
ret = krb5_unparse_name_flags(context, client,
KRB5_PRINCIPAL_UNPARSE_NO_REALM,
- username);
+ usernamep);
krb5_free_principal(context, client);
if (ret)
goto out;
- asprintf(&confname, "ntlm-key-%s", name->domain);
- if (confname == NULL) {
- krb5_clear_error_message(context);
- ret = ENOMEM;
+ if (name != NULL) {
+ *domainp = strdup(name->domain);
+ } else {
+ krb5_data data_domain;
+
+ krb5_data_zero(&data_domain);
+ ret = krb5_cc_get_config(context, id, NULL, "default-ntlm-domain",
+ &data_domain);
+ if (ret)
+ goto out;
+
+ *domainp = strndup(data_domain.data, data_domain.length);
+ krb5_data_free(&data_domain);
+ }
+
+ if (*domainp == NULL) {
+ ret = krb5_enomem(context);
goto out;
}
- ret = krb5_cc_get_config(context, id, NULL,
- confname, &data);
- if (ret)
+ aret = asprintf(&confname, "ntlm-key-%s", *domainp);
+ if (aret == -1) {
+ ret = krb5_enomem(context);
goto out;
+ }
+
+ ret = krb5_cc_get_config(context, id, NULL, confname, &data);
+ if (ret)
+ goto out;
key->data = malloc(data.length);
if (key->data == NULL) {
@@ -169,15 +203,16 @@ _gss_ntlm_get_user_cred(const ntlm_name target_name,
if (cred == NULL)
return ENOMEM;
- ret = get_user_file(target_name, &cred->username, &cred->key);
+ ret = get_user_file(target_name,
+ &cred->domain, &cred->username, &cred->key);
if (ret)
- ret = get_user_ccache(target_name, &cred->username, &cred->key);
+ ret = get_user_ccache(target_name,
+ &cred->domain, &cred->username, &cred->key);
if (ret) {
free(cred);
return ret;
}
- cred->domain = strdup(target_name->domain);
*rcred = cred;
return ret;
@@ -216,9 +251,9 @@ _gss_copy_cred(ntlm_cred from, ntlm_cred *to)
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_init_sec_context
(OM_uint32 * minor_status,
- const gss_cred_id_t initiator_cred_handle,
+ gss_const_cred_id_t initiator_cred_handle,
gss_ctx_id_t * context_handle,
- const gss_name_t target_name,
+ gss_const_name_t target_name,
const gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
@@ -472,7 +507,7 @@ _gss_ntlm_init_sec_context
- ret = heim_ntlm_encode_type3(&type3, &data);
+ ret = heim_ntlm_encode_type3(&type3, &data, NULL);
free(type3.sessionkey.data);
if (type3.lm.data)
free(type3.lm.data);
diff --git a/lib/gssapi/ntlm/inquire_context.c b/lib/gssapi/ntlm/inquire_context.c
index fd0cb8799637..741ad6e14fa5 100644
--- a/lib/gssapi/ntlm/inquire_context.c
+++ b/lib/gssapi/ntlm/inquire_context.c
@@ -36,7 +36,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_context (
OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_name_t * src_name,
gss_name_t * targ_name,
OM_uint32 * lifetime_rec,
diff --git a/lib/gssapi/ntlm/inquire_cred_by_mech.c b/lib/gssapi/ntlm/inquire_cred_by_mech.c
index b5976b99d728..ed42094c9732 100644
--- a/lib/gssapi/ntlm/inquire_cred_by_mech.c
+++ b/lib/gssapi/ntlm/inquire_cred_by_mech.c
@@ -36,7 +36,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_cred_by_mech (
OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID mech_type,
gss_name_t * name,
OM_uint32 * initiator_lifetime,
diff --git a/lib/gssapi/ntlm/inquire_mechs_for_name.c b/lib/gssapi/ntlm/inquire_mechs_for_name.c
index 4fd5380946a4..25450ab17d69 100644
--- a/lib/gssapi/ntlm/inquire_mechs_for_name.c
+++ b/lib/gssapi/ntlm/inquire_mechs_for_name.c
@@ -36,7 +36,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_mechs_for_name (
OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_OID_set * mech_types
)
{
diff --git a/lib/gssapi/ntlm/inquire_sec_context_by_oid.c b/lib/gssapi/ntlm/inquire_sec_context_by_oid.c
index ee791b15adf2..c40bf492f52b 100644
--- a/lib/gssapi/ntlm/inquire_sec_context_by_oid.c
+++ b/lib/gssapi/ntlm/inquire_sec_context_by_oid.c
@@ -37,7 +37,7 @@
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_sec_context_by_oid(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
diff --git a/lib/gssapi/ntlm/kdc.c b/lib/gssapi/ntlm/kdc.c
index 7d56c7510fbb..06d738206375 100644
--- a/lib/gssapi/ntlm/kdc.c
+++ b/lib/gssapi/ntlm/kdc.c
@@ -251,7 +251,7 @@ kdc_type2(OM_uint32 *minor_status,
struct ntlmkrb5 *c = ctx;
krb5_error_code ret;
struct ntlm_type2 type2;
- krb5_data challange;
+ krb5_data challenge;
struct ntlm_buf data;
krb5_data ti;
@@ -293,18 +293,18 @@ kdc_type2(OM_uint32 *minor_status,
}
*ret_flags = type2.flags;
- ret = krb5_ntlm_init_get_challange(c->context, c->ntlm, &challange);
+ ret = krb5_ntlm_init_get_challenge(c->context, c->ntlm, &challenge);
if (ret) {
*minor_status = ret;
return GSS_S_FAILURE;
}
- if (challange.length != sizeof(type2.challenge)) {
+ if (challenge.length != sizeof(type2.challenge)) {
*minor_status = EINVAL;
return GSS_S_FAILURE;
}
- memcpy(type2.challenge, challange.data, sizeof(type2.challenge));
- krb5_data_free(&challange);
+ memcpy(type2.challenge, challenge.data, sizeof(type2.challenge));
+ krb5_data_free(&challenge);
ret = krb5_ntlm_init_get_targetname(c->context, c->ntlm,
&type2.targetname);
diff --git a/lib/gssapi/ntlm/ntlm-private.h b/lib/gssapi/ntlm/ntlm-private.h
index 0c62b3533937..b78f0b87b505 100644
--- a/lib/gssapi/ntlm/ntlm-private.h
+++ b/lib/gssapi/ntlm/ntlm-private.h
@@ -11,7 +11,7 @@ OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_accept_sec_context (
OM_uint32 * /*minor_status*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_cred_id_t /*acceptor_cred_handle*/,
+ gss_const_cred_id_t /*acceptor_cred_handle*/,
const gss_buffer_t /*input_token_buffer*/,
const gss_channel_bindings_t /*input_chan_bindings*/,
gss_name_t * /*src_name*/,
@@ -23,20 +23,20 @@ _gss_ntlm_accept_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_acquire_cred (
- OM_uint32 * /*min_stat*/,
- const gss_name_t /*desired_name*/,
+ OM_uint32 */*min_stat*/,
+ gss_const_name_t /*desired_name*/,
OM_uint32 /*time_req*/,
const gss_OID_set /*desired_mechs*/,
gss_cred_usage_t /*cred_usage*/,
- gss_cred_id_t * /*output_cred_handle*/,
- gss_OID_set * /*actual_mechs*/,
- OM_uint32 * time_rec );
+ gss_cred_id_t */*output_cred_handle*/,
+ gss_OID_set */*actual_mechs*/,
+ OM_uint32 */*time_rec*/);
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_add_cred (
OM_uint32 */*minor_status*/,
- const gss_cred_id_t /*input_cred_handle*/,
- const gss_name_t /*desired_name*/,
+ gss_const_cred_id_t /*input_cred_handle*/,
+ gss_const_name_t /*desired_name*/,
const gss_OID /*desired_mech*/,
gss_cred_usage_t /*cred_usage*/,
OM_uint32 /*initiator_time_req*/,
@@ -54,21 +54,21 @@ _gss_ntlm_allocate_ctx (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_canonicalize_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
const gss_OID /*mech_type*/,
gss_name_t * output_name );
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_compare_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*name1*/,
- const gss_name_t /*name2*/,
+ gss_const_name_t /*name1*/,
+ gss_const_name_t /*name2*/,
int * name_equal );
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_context_time (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
OM_uint32 * time_rec );
OM_uint32 GSSAPI_CALLCONV
@@ -85,7 +85,7 @@ _gss_ntlm_destroy_cred (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_display_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t /*output_name_buffer*/,
gss_OID * output_name_type );
@@ -101,13 +101,13 @@ _gss_ntlm_display_status (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_duplicate_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*src_name*/,
+ gss_const_name_t /*src_name*/,
gss_name_t * dest_name );
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_export_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t exported_name );
OM_uint32 GSSAPI_CALLCONV
@@ -119,7 +119,7 @@ _gss_ntlm_export_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_get_mic (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*message_buffer*/,
gss_buffer_t message_token );
@@ -150,9 +150,9 @@ _gss_ntlm_indicate_mechs (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_init_sec_context (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*initiator_cred_handle*/,
+ gss_const_cred_id_t /*initiator_cred_handle*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_name_t /*target_name*/,
+ gss_const_name_t /*target_name*/,
const gss_OID /*mech_type*/,
OM_uint32 /*req_flags*/,
OM_uint32 /*time_req*/,
@@ -166,7 +166,7 @@ _gss_ntlm_init_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_context (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_name_t * /*src_name*/,
gss_name_t * /*targ_name*/,
OM_uint32 * /*lifetime_rec*/,
@@ -178,7 +178,7 @@ _gss_ntlm_inquire_context (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_cred (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
gss_name_t * /*name*/,
OM_uint32 * /*lifetime*/,
gss_cred_usage_t * /*cred_usage*/,
@@ -187,7 +187,7 @@ _gss_ntlm_inquire_cred (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_cred_by_mech (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
const gss_OID /*mech_type*/,
gss_name_t * /*name*/,
OM_uint32 * /*initiator_lifetime*/,
@@ -197,7 +197,7 @@ _gss_ntlm_inquire_cred_by_mech (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_mechs_for_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_OID_set * mech_types );
OM_uint32 GSSAPI_CALLCONV
@@ -209,7 +209,7 @@ _gss_ntlm_inquire_names_for_mech (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_inquire_sec_context_by_oid (
OM_uint32 */*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_OID /*desired_object*/,
gss_buffer_set_t */*data_set*/);
@@ -222,7 +222,7 @@ _gss_ntlm_iter_creds_f (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_process_context_token (
OM_uint32 */*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t token_buffer );
OM_uint32 GSSAPI_CALLCONV
@@ -246,7 +246,7 @@ _gss_ntlm_set_key (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_unwrap (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*input_message_buffer*/,
gss_buffer_t /*output_message_buffer*/,
int * /*conf_state*/,
@@ -255,7 +255,7 @@ _gss_ntlm_unwrap (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_verify_mic (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*message_buffer*/,
const gss_buffer_t /*token_buffer*/,
gss_qop_t * qop_state );
@@ -263,7 +263,7 @@ _gss_ntlm_verify_mic (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_wrap (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*input_message_buffer*/,
@@ -273,7 +273,7 @@ _gss_ntlm_wrap (
OM_uint32 GSSAPI_CALLCONV
_gss_ntlm_wrap_size_limit (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
OM_uint32 /*req_output_size*/,
diff --git a/lib/gssapi/ntlm/process_context_token.c b/lib/gssapi/ntlm/process_context_token.c
index 16efcd1525d7..2add53b6d787 100644
--- a/lib/gssapi/ntlm/process_context_token.c
+++ b/lib/gssapi/ntlm/process_context_token.c
@@ -35,7 +35,7 @@
OM_uint32 GSSAPI_CALLCONV _gss_ntlm_process_context_token (
OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t token_buffer
)
{
diff --git a/lib/gssapi/oid.txt b/lib/gssapi/oid.txt
new file mode 100644
index 000000000000..cd6c2fa04fb5
--- /dev/null
+++ b/lib/gssapi/oid.txt
@@ -0,0 +1,142 @@
+# /* contact Love Hörnquist Åstrand <lha@h5l.org> for new oid arcs */
+
+# /*
+# * 1.2.752.43.13 Heimdal GSS-API Extentions
+# */
+
+oid base GSS_KRB5_COPY_CCACHE_X 1.2.752.43.13.1
+oid base GSS_KRB5_GET_TKT_FLAGS_X 1.2.752.43.13.2
+oid base GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X 1.2.752.43.13.3
+oid base GSS_KRB5_COMPAT_DES3_MIC_X 1.2.752.43.13.4
+oid base GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X 1.2.752.43.13.5
+oid base GSS_KRB5_EXPORT_LUCID_CONTEXT_X 1.2.752.43.13.6
+oid base GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X 1.2.752.43.13.6.1
+oid base GSS_KRB5_SET_DNS_CANONICALIZE_X 1.2.752.43.13.7
+oid base GSS_KRB5_GET_SUBKEY_X 1.2.752.43.13.8
+oid base GSS_KRB5_GET_INITIATOR_SUBKEY_X 1.2.752.43.13.9
+oid base GSS_KRB5_GET_ACCEPTOR_SUBKEY_X 1.2.752.43.13.10
+oid base GSS_KRB5_SEND_TO_KDC_X 1.2.752.43.13.11
+oid base GSS_KRB5_GET_AUTHTIME_X 1.2.752.43.13.12
+oid base GSS_KRB5_GET_SERVICE_KEYBLOCK_X 1.2.752.43.13.13
+oid base GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X 1.2.752.43.13.14
+oid base GSS_KRB5_SET_DEFAULT_REALM_X 1.2.752.43.13.15
+oid base GSS_KRB5_CCACHE_NAME_X 1.2.752.43.13.16
+oid base GSS_KRB5_SET_TIME_OFFSET_X 1.2.752.43.13.17
+oid base GSS_KRB5_GET_TIME_OFFSET_X 1.2.752.43.13.18
+oid base GSS_KRB5_PLUGIN_REGISTER_X 1.2.752.43.13.19
+oid base GSS_NTLM_GET_SESSION_KEY_X 1.2.752.43.13.20
+oid base GSS_C_NT_NTLM 1.2.752.43.13.21
+oid base GSS_C_NT_DN 1.2.752.43.13.22
+oid base GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL 1.2.752.43.13.23
+oid base GSS_C_NTLM_AVGUEST 1.2.752.43.13.24
+oid base GSS_C_NTLM_V1 1.2.752.43.13.25
+oid base GSS_C_NTLM_V2 1.2.752.43.13.26
+oid base GSS_C_NTLM_SESSION_KEY 1.2.752.43.13.27
+oid base GSS_C_NTLM_FORCE_V1 1.2.752.43.13.28
+oid base GSS_KRB5_CRED_NO_CI_FLAGS_X 1.2.752.43.13.29
+oid base GSS_KRB5_IMPORT_CRED_X 1.2.752.43.13.30
+
+# /* glue for gss_inquire_saslname_for_mech */
+oid base GSS_C_MA_SASL_MECH_NAME 1.2.752.43.13.100
+oid base GSS_C_MA_MECH_NAME 1.2.752.43.13.101
+oid base GSS_C_MA_MECH_DESCRIPTION 1.2.752.43.13.102
+
+# /* credential types */
+oid base GSS_C_CRED_PASSWORD 1.2.752.43.13.200
+oid base GSS_C_CRED_CERTIFICATE 1.2.752.43.13.201
+
+#/* Heimdal mechanisms - 1.2.752.43.14 */
+
+oid base GSS_SASL_DIGEST_MD5_MECHANISM 1.2.752.43.14.1
+oid base GSS_NETLOGON_MECHANISM 1.2.752.43.14.2
+oid base GSS_NETLOGON_SET_SESSION_KEY_X 1.2.752.43.14.3
+oid base GSS_NETLOGON_SET_SIGN_ALGORITHM_X 1.2.752.43.14.4
+oid base GSS_NETLOGON_NT_NETBIOS_DNS_NAME 1.2.752.43.14.5
+
+#/* GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X.128 */
+oid base GSS_C_INQ_WIN2K_PAC_X 1.2.752.43.13.3.128
+oid base GSS_C_INQ_SSPI_SESSION_KEY 1.2.840.113554.1.2.2.5.5
+
+#/*
+# * "Standard" mechs
+# */
+
+oid base GSS_KRB5_MECHANISM 1.2.840.113554.1.2.2
+oid base GSS_NTLM_MECHANISM 1.3.6.1.4.1.311.2.2.10
+oid base GSS_SPNEGO_MECHANISM 1.3.6.1.5.5.2
+
+# /* From Luke Howard */
+
+oid base GSS_C_PEER_HAS_UPDATED_SPNEGO 1.3.6.1.4.1.5322.19.5
+
+#/*
+# * OID mappings with name and short description and and slightly longer description
+# */
+
+desc mech GSS_KRB5_MECHANISM "Kerberos 5" "Heimdal Kerberos 5 mechanism"
+desc mech GSS_NTLM_MECHANISM "NTLM" "Heimdal NTLM mechanism"
+desc mech GSS_SPNEGO_MECHANISM "SPNEGO" "Heimdal SPNEGO mechanism"
+
+desc ma GSS_C_MA_MECH_NAME "GSS mech name" "The name of the GSS-API mechanism"
+desc ma GSS_C_MA_SASL_MECH_NAME "SASL mechanism name" "The name of the SASL mechanism"
+desc ma GSS_C_MA_MECH_DESCRIPTION "Mech description" "The long description of the mechanism"
+
+#/*
+# * RFC5587
+# */
+
+oid base GSS_C_MA_MECH_CONCRETE 1.3.6.1.5.5.13.1
+oid base GSS_C_MA_MECH_PSEUDO 1.3.6.1.5.5.13.2
+oid base GSS_C_MA_MECH_COMPOSITE 1.3.6.1.5.5.13.3
+oid base GSS_C_MA_MECH_NEGO 1.3.6.1.5.5.13.4
+oid base GSS_C_MA_MECH_GLUE 1.3.6.1.5.5.13.5
+oid base GSS_C_MA_NOT_MECH 1.3.6.1.5.5.13.6
+oid base GSS_C_MA_DEPRECATED 1.3.6.1.5.5.13.7
+oid base GSS_C_MA_NOT_DFLT_MECH 1.3.6.1.5.5.13.8
+oid base GSS_C_MA_ITOK_FRAMED 1.3.6.1.5.5.13.9
+oid base GSS_C_MA_AUTH_INIT 1.3.6.1.5.5.13.10
+oid base GSS_C_MA_AUTH_TARG 1.3.6.1.5.5.13.11
+oid base GSS_C_MA_AUTH_INIT_INIT 1.3.6.1.5.5.13.12
+oid base GSS_C_MA_AUTH_TARG_INIT 1.3.6.1.5.5.13.13
+oid base GSS_C_MA_AUTH_INIT_ANON 1.3.6.1.5.5.13.14
+oid base GSS_C_MA_AUTH_TARG_ANON 1.3.6.1.5.5.13.15
+oid base GSS_C_MA_DELEG_CRED 1.3.6.1.5.5.13.16
+oid base GSS_C_MA_INTEG_PROT 1.3.6.1.5.5.13.17
+oid base GSS_C_MA_CONF_PROT 1.3.6.1.5.5.13.18
+oid base GSS_C_MA_MIC 1.3.6.1.5.5.13.19
+oid base GSS_C_MA_WRAP 1.3.6.1.5.5.13.20
+oid base GSS_C_MA_PROT_READY 1.3.6.1.5.5.13.21
+oid base GSS_C_MA_REPLAY_DET 1.3.6.1.5.5.13.22
+oid base GSS_C_MA_OOS_DET 1.3.6.1.5.5.13.23
+oid base GSS_C_MA_CBINDINGS 1.3.6.1.5.5.13.24
+oid base GSS_C_MA_PFS 1.3.6.1.5.5.13.25
+oid base GSS_C_MA_COMPRESS 1.3.6.1.5.5.13.26
+oid base GSS_C_MA_CTX_TRANS 1.3.6.1.5.5.13.27
+
+desc ma GSS_C_MA_MECH_CONCRETE "concrete-mech" "Indicates that a mech is neither a pseudo-mechanism nor a composite mechanism"
+desc ma GSS_C_MA_MECH_PSEUDO "pseudo-mech" ""
+desc ma GSS_C_MA_MECH_COMPOSITE "composite-mech" ""
+desc ma GSS_C_MA_MECH_NEGO "mech-negotiation-mech" ""
+desc ma GSS_C_MA_MECH_GLUE "mech-glue" ""
+desc ma GSS_C_MA_NOT_MECH "not-mech" ""
+desc ma GSS_C_MA_DEPRECATED "mech-deprecated" ""
+desc ma GSS_C_MA_NOT_DFLT_MECH "mech-not-default" ""
+desc ma GSS_C_MA_ITOK_FRAMED "initial-is-framed" ""
+desc ma GSS_C_MA_AUTH_INIT "auth-init-princ" ""
+desc ma GSS_C_MA_AUTH_TARG "auth-targ-princ" ""
+desc ma GSS_C_MA_AUTH_INIT_INIT "auth-init-princ-initial" ""
+desc ma GSS_C_MA_AUTH_TARG_INIT "auth-targ-princ-initial" ""
+desc ma GSS_C_MA_AUTH_INIT_ANON "auth-init-princ-anon" ""
+desc ma GSS_C_MA_AUTH_TARG_ANON "auth-targ-princ-anon" ""
+desc ma GSS_C_MA_DELEG_CRED "deleg-cred" ""
+desc ma GSS_C_MA_INTEG_PROT "integ-prot" ""
+desc ma GSS_C_MA_CONF_PROT "conf-prot" ""
+desc ma GSS_C_MA_MIC "mic" ""
+desc ma GSS_C_MA_WRAP "wrap" ""
+desc ma GSS_C_MA_PROT_READY "prot-ready" ""
+desc ma GSS_C_MA_REPLAY_DET "replay-detection" ""
+desc ma GSS_C_MA_OOS_DET "oos-detection" ""
+desc ma GSS_C_MA_CBINDINGS "channel-bindings" ""
+desc ma GSS_C_MA_PFS "pfs" ""
+desc ma GSS_C_MA_COMPRESS "compress" ""
+desc ma GSS_C_MA_CTX_TRANS "context-transfer" ""
diff --git a/lib/gssapi/spnego/accept_sec_context.c b/lib/gssapi/spnego/accept_sec_context.c
index 3a51dd3a0a61..48b786c29ed1 100644
--- a/lib/gssapi/spnego/accept_sec_context.c
+++ b/lib/gssapi/spnego/accept_sec_context.c
@@ -470,7 +470,7 @@ static OM_uint32 GSSAPI_CALLCONV
acceptor_start
(OM_uint32 * minor_status,
gss_ctx_id_t * context_handle,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token_buffer,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t * src_name,
@@ -687,7 +687,7 @@ static OM_uint32 GSSAPI_CALLCONV
acceptor_continue
(OM_uint32 * minor_status,
gss_ctx_id_t * context_handle,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token_buffer,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t * src_name,
@@ -874,7 +874,7 @@ OM_uint32 GSSAPI_CALLCONV
_gss_spnego_accept_sec_context
(OM_uint32 * minor_status,
gss_ctx_id_t * context_handle,
- const gss_cred_id_t acceptor_cred_handle,
+ gss_const_cred_id_t acceptor_cred_handle,
const gss_buffer_t input_token_buffer,
const gss_channel_bindings_t input_chan_bindings,
gss_name_t * src_name,
diff --git a/lib/gssapi/spnego/compat.c b/lib/gssapi/spnego/compat.c
index cf5ee30a84a9..6e90fe6faf86 100644
--- a/lib/gssapi/spnego/compat.c
+++ b/lib/gssapi/spnego/compat.c
@@ -234,7 +234,7 @@ _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
gss_name_t target_name,
OM_uint32 (*func)(gss_name_t, gss_OID),
int includeMSCompatOID,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
MechTypeList *mechtypelist,
gss_OID *preferred_mech)
{
diff --git a/lib/gssapi/spnego/context_stubs.c b/lib/gssapi/spnego/context_stubs.c
index 60b348ec4676..836ce5859e67 100644
--- a/lib/gssapi/spnego/context_stubs.c
+++ b/lib/gssapi/spnego/context_stubs.c
@@ -68,18 +68,18 @@ spnego_supported_mechs(OM_uint32 *minor_status, gss_OID_set *mechs)
OM_uint32 GSSAPI_CALLCONV _gss_spnego_process_context_token
(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t token_buffer
)
{
- gss_ctx_id_t context ;
+ gss_ctx_id_t context;
gssspnego_ctx ctx;
OM_uint32 ret;
if (context_handle == GSS_C_NO_CONTEXT)
return GSS_S_NO_CONTEXT;
- context = context_handle;
+ context = (gss_ctx_id_t)context_handle;
ctx = (gssspnego_ctx)context_handle;
HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex);
@@ -121,7 +121,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_delete_sec_context
OM_uint32 GSSAPI_CALLCONV _gss_spnego_context_time
(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
OM_uint32 *time_rec
)
{
@@ -145,7 +145,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_context_time
OM_uint32 GSSAPI_CALLCONV _gss_spnego_get_mic
(OM_uint32 *minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_qop_t qop_req,
const gss_buffer_t message_buffer,
gss_buffer_t message_token
@@ -171,7 +171,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_get_mic
OM_uint32 GSSAPI_CALLCONV _gss_spnego_verify_mic
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t message_buffer,
const gss_buffer_t token_buffer,
gss_qop_t * qop_state
@@ -200,7 +200,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_verify_mic
OM_uint32 GSSAPI_CALLCONV _gss_spnego_wrap
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
const gss_buffer_t input_message_buffer,
@@ -233,7 +233,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_wrap
OM_uint32 GSSAPI_CALLCONV _gss_spnego_unwrap
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_buffer_t input_message_buffer,
gss_buffer_t output_message_buffer,
int * conf_state,
@@ -264,8 +264,8 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_unwrap
OM_uint32 GSSAPI_CALLCONV _gss_spnego_compare_name
(OM_uint32 *minor_status,
- const gss_name_t name1,
- const gss_name_t name2,
+ gss_const_name_t name1,
+ gss_const_name_t name2,
int * name_equal
)
{
@@ -288,7 +288,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_compare_name
OM_uint32 GSSAPI_CALLCONV _gss_spnego_display_name
(OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t output_name_buffer,
gss_OID * output_name_type
)
@@ -342,7 +342,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_import_name
OM_uint32 GSSAPI_CALLCONV _gss_spnego_export_name
(OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_buffer_t exported_name
)
{
@@ -382,7 +382,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_release_name
OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_context (
OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_name_t * src_name,
gss_name_t * targ_name,
OM_uint32 * lifetime_rec,
@@ -449,7 +449,7 @@ enomem:
OM_uint32 GSSAPI_CALLCONV _gss_spnego_wrap_size_limit (
OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
int conf_req_flag,
gss_qop_t qop_req,
OM_uint32 req_output_size,
@@ -531,6 +531,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_import_sec_context (
gss_ctx_id_t context;
gssspnego_ctx ctx;
+ *context_handle = GSS_C_NO_CONTEXT;
ret = _gss_spnego_alloc_sec_context(minor_status, &context);
if (ret != GSS_S_COMPLETE) {
return ret;
@@ -543,7 +544,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_import_sec_context (
interprocess_token,
&ctx->negotiated_ctx_id);
if (ret != GSS_S_COMPLETE) {
- _gss_spnego_internal_delete_sec_context(&minor, context_handle, GSS_C_NO_BUFFER);
+ _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER);
return ret;
}
@@ -602,7 +603,7 @@ out:
OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_mechs_for_name (
OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
gss_OID_set * mech_types
)
{
@@ -623,7 +624,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_mechs_for_name (
OM_uint32 GSSAPI_CALLCONV _gss_spnego_canonicalize_name (
OM_uint32 * minor_status,
- const gss_name_t input_name,
+ gss_const_name_t input_name,
const gss_OID mech_type,
gss_name_t * output_name
)
@@ -634,7 +635,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_canonicalize_name (
OM_uint32 GSSAPI_CALLCONV _gss_spnego_duplicate_name (
OM_uint32 * minor_status,
- const gss_name_t src_name,
+ gss_const_name_t src_name,
gss_name_t * dest_name
)
{
@@ -707,7 +708,7 @@ _gss_spnego_wrap_iov_length(OM_uint32 * minor_status,
#if 0
OM_uint32 GSSAPI_CALLCONV _gss_spnego_complete_auth_token
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
gss_buffer_t input_message_buffer)
{
gssspnego_ctx ctx;
@@ -732,7 +733,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_complete_auth_token
OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_sec_context_by_oid
(OM_uint32 * minor_status,
- const gss_ctx_id_t context_handle,
+ gss_const_ctx_id_t context_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
diff --git a/lib/gssapi/spnego/cred_stubs.c b/lib/gssapi/spnego/cred_stubs.c
index fc43d6a4a666..f82c4d1e80cf 100644
--- a/lib/gssapi/spnego/cred_stubs.c
+++ b/lib/gssapi/spnego/cred_stubs.c
@@ -56,7 +56,7 @@ _gss_spnego_release_cred(OM_uint32 *minor_status, gss_cred_id_t *cred_handle)
*/
OM_uint32 GSSAPI_CALLCONV _gss_spnego_acquire_cred
(OM_uint32 *minor_status,
- const gss_name_t desired_name,
+ gss_const_name_t desired_name,
OM_uint32 time_req,
const gss_OID_set desired_mechs,
gss_cred_usage_t cred_usage,
@@ -129,7 +129,7 @@ out:
OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_cred
(OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
gss_name_t * name,
OM_uint32 * lifetime,
gss_cred_usage_t * cred_usage,
@@ -171,7 +171,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_cred
OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_cred_by_mech (
OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID mech_type,
gss_name_t * name,
OM_uint32 * initiator_lifetime,
@@ -216,7 +216,7 @@ OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_cred_by_mech (
OM_uint32 GSSAPI_CALLCONV _gss_spnego_inquire_cred_by_oid
(OM_uint32 * minor_status,
- const gss_cred_id_t cred_handle,
+ gss_const_cred_id_t cred_handle,
const gss_OID desired_object,
gss_buffer_set_t *data_set)
{
diff --git a/lib/gssapi/spnego/external.c b/lib/gssapi/spnego/external.c
index ca06d46e8211..03678f9989b3 100644
--- a/lib/gssapi/spnego/external.c
+++ b/lib/gssapi/spnego/external.c
@@ -66,11 +66,19 @@ static gss_mo_desc spnego_mo[] = {
},
{
GSS_C_MA_MECH_NEGO,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
},
{
GSS_C_MA_MECH_PSEUDO,
- GSS_MO_MA
+ GSS_MO_MA,
+ NULL,
+ NULL,
+ NULL,
+ NULL
}
};
@@ -84,7 +92,7 @@ static gssapi_mech_interface_desc spnego_mech = {
_gss_spnego_init_sec_context,
_gss_spnego_accept_sec_context,
_gss_spnego_process_context_token,
- _gss_spnego_internal_delete_sec_context,
+ _gss_spnego_delete_sec_context,
_gss_spnego_context_time,
_gss_spnego_get_mic,
_gss_spnego_verify_mic,
@@ -134,6 +142,9 @@ static gssapi_mech_interface_desc spnego_mech = {
NULL,
NULL,
NULL,
+ NULL,
+ NULL,
+ NULL,
};
gssapi_mech_interface
diff --git a/lib/gssapi/spnego/init_sec_context.c b/lib/gssapi/spnego/init_sec_context.c
index b4b1bcefc5e5..a12ce3847573 100644
--- a/lib/gssapi/spnego/init_sec_context.c
+++ b/lib/gssapi/spnego/init_sec_context.c
@@ -177,9 +177,9 @@ spnego_reply_internal(OM_uint32 *minor_status,
static OM_uint32
spnego_initial
(OM_uint32 * minor_status,
- gss_cred_id_t cred,
+ gss_const_cred_id_t cred,
gss_ctx_id_t * context_handle,
- const gss_name_t target_name,
+ gss_const_name_t target_name,
const gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
@@ -374,9 +374,9 @@ spnego_initial
static OM_uint32
spnego_reply
(OM_uint32 * minor_status,
- const gss_cred_id_t cred,
+ gss_const_cred_id_t cred,
gss_ctx_id_t * context_handle,
- const gss_name_t target_name,
+ gss_const_name_t target_name,
const gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
@@ -614,9 +614,9 @@ spnego_reply
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_init_sec_context
(OM_uint32 * minor_status,
- const gss_cred_id_t initiator_cred_handle,
+ gss_const_cred_id_t initiator_cred_handle,
gss_ctx_id_t * context_handle,
- const gss_name_t target_name,
+ gss_const_name_t target_name,
const gss_OID mech_type,
OM_uint32 req_flags,
OM_uint32 time_req,
diff --git a/lib/gssapi/spnego/spnego-private.h b/lib/gssapi/spnego/spnego-private.h
index f50574d7ed37..7486b68fc48b 100644
--- a/lib/gssapi/spnego/spnego-private.h
+++ b/lib/gssapi/spnego/spnego-private.h
@@ -11,7 +11,7 @@ OM_uint32 GSSAPI_CALLCONV
_gss_spnego_accept_sec_context (
OM_uint32 * /*minor_status*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_cred_id_t /*acceptor_cred_handle*/,
+ gss_const_cred_id_t /*acceptor_cred_handle*/,
const gss_buffer_t /*input_token_buffer*/,
const gss_channel_bindings_t /*input_chan_bindings*/,
gss_name_t * /*src_name*/,
@@ -24,7 +24,7 @@ _gss_spnego_accept_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_acquire_cred (
OM_uint32 */*minor_status*/,
- const gss_name_t /*desired_name*/,
+ gss_const_name_t /*desired_name*/,
OM_uint32 /*time_req*/,
const gss_OID_set /*desired_mechs*/,
gss_cred_usage_t /*cred_usage*/,
@@ -40,21 +40,21 @@ _gss_spnego_alloc_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_canonicalize_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
const gss_OID /*mech_type*/,
gss_name_t * output_name );
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_compare_name (
OM_uint32 */*minor_status*/,
- const gss_name_t /*name1*/,
- const gss_name_t /*name2*/,
+ gss_const_name_t /*name1*/,
+ gss_const_name_t /*name2*/,
int * name_equal );
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_context_time (
OM_uint32 */*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
OM_uint32 *time_rec );
OM_uint32 GSSAPI_CALLCONV
@@ -66,14 +66,14 @@ _gss_spnego_delete_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_display_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t /*output_name_buffer*/,
gss_OID * output_name_type );
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_duplicate_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*src_name*/,
+ gss_const_name_t /*src_name*/,
gss_name_t * dest_name );
OM_uint32 GSSAPI_CALLCONV
@@ -85,7 +85,7 @@ _gss_spnego_export_cred (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_export_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_buffer_t exported_name );
OM_uint32 GSSAPI_CALLCONV
@@ -97,7 +97,7 @@ _gss_spnego_export_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_get_mic (
OM_uint32 */*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*message_buffer*/,
gss_buffer_t message_token );
@@ -127,16 +127,16 @@ _gss_spnego_indicate_mechtypelist (
gss_name_t /*target_name*/,
OM_uint32 (*/*func*/)(gss_name_t, gss_OID),
int /*includeMSCompatOID*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
MechTypeList */*mechtypelist*/,
gss_OID */*preferred_mech*/);
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_init_sec_context (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*initiator_cred_handle*/,
+ gss_const_cred_id_t /*initiator_cred_handle*/,
gss_ctx_id_t * /*context_handle*/,
- const gss_name_t /*target_name*/,
+ gss_const_name_t /*target_name*/,
const gss_OID /*mech_type*/,
OM_uint32 /*req_flags*/,
OM_uint32 /*time_req*/,
@@ -150,7 +150,7 @@ _gss_spnego_init_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_inquire_context (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
gss_name_t * /*src_name*/,
gss_name_t * /*targ_name*/,
OM_uint32 * /*lifetime_rec*/,
@@ -162,7 +162,7 @@ _gss_spnego_inquire_context (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_inquire_cred (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
gss_name_t * /*name*/,
OM_uint32 * /*lifetime*/,
gss_cred_usage_t * /*cred_usage*/,
@@ -171,7 +171,7 @@ _gss_spnego_inquire_cred (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_inquire_cred_by_mech (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
const gss_OID /*mech_type*/,
gss_name_t * /*name*/,
OM_uint32 * /*initiator_lifetime*/,
@@ -181,14 +181,14 @@ _gss_spnego_inquire_cred_by_mech (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_inquire_cred_by_oid (
OM_uint32 * /*minor_status*/,
- const gss_cred_id_t /*cred_handle*/,
+ gss_const_cred_id_t /*cred_handle*/,
const gss_OID /*desired_object*/,
gss_buffer_set_t */*data_set*/);
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_inquire_mechs_for_name (
OM_uint32 * /*minor_status*/,
- const gss_name_t /*input_name*/,
+ gss_const_name_t /*input_name*/,
gss_OID_set * mech_types );
OM_uint32 GSSAPI_CALLCONV
@@ -200,7 +200,7 @@ _gss_spnego_inquire_names_for_mech (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_inquire_sec_context_by_oid (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_OID /*desired_object*/,
gss_buffer_set_t */*data_set*/);
@@ -213,7 +213,7 @@ _gss_spnego_internal_delete_sec_context (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_process_context_token (
OM_uint32 */*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t token_buffer );
OM_uint32 GSSAPI_CALLCONV
@@ -258,7 +258,7 @@ _gss_spnego_set_sec_context_option (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_unwrap (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*input_message_buffer*/,
gss_buffer_t /*output_message_buffer*/,
int * /*conf_state*/,
@@ -276,7 +276,7 @@ _gss_spnego_unwrap_iov (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_verify_mic (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
const gss_buffer_t /*message_buffer*/,
const gss_buffer_t /*token_buffer*/,
gss_qop_t * qop_state );
@@ -284,7 +284,7 @@ _gss_spnego_verify_mic (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_wrap (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
const gss_buffer_t /*input_message_buffer*/,
@@ -314,7 +314,7 @@ _gss_spnego_wrap_iov_length (
OM_uint32 GSSAPI_CALLCONV
_gss_spnego_wrap_size_limit (
OM_uint32 * /*minor_status*/,
- const gss_ctx_id_t /*context_handle*/,
+ gss_const_ctx_id_t /*context_handle*/,
int /*conf_req_flag*/,
gss_qop_t /*qop_req*/,
OM_uint32 /*req_output_size*/,
diff --git a/lib/gssapi/test_add_store_cred.c b/lib/gssapi/test_add_store_cred.c
new file mode 100644
index 000000000000..981e8fc6e4a8
--- /dev/null
+++ b/lib/gssapi/test_add_store_cred.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015 Cryptonector LLC.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. The name Cryptonector LLC may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <roken.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <gssapi.h>
+#include <err.h>
+#include <getarg.h>
+
+static void
+print_gss_err(OM_uint32 stat, int status_type, gss_OID mech)
+{
+ gss_buffer_desc str;
+ OM_uint32 maj;
+ OM_uint32 min;
+ OM_uint32 msg_ctx = 0;
+ int first = 1;
+
+ do {
+ maj = gss_display_status(&min, stat, status_type, mech, &msg_ctx,
+ &str);
+ if (maj != GSS_S_COMPLETE) {
+ fprintf(stderr, "Error displaying GSS %s error (%lu): %lu, %lu",
+ status_type == GSS_C_GSS_CODE ? "major" : "minor",
+ (unsigned long)stat, (unsigned long)maj,
+ (unsigned long)min);
+ return;
+ }
+ if (first) {
+ fprintf(stderr, "GSS %s error: %.*s\n",
+ status_type == GSS_C_GSS_CODE ? "major" : "minor",
+ (int)str.length, (char *)str.value);
+ first = 0;
+ } else {
+ fprintf(stderr, "\t%.*s\n", (int)str.length, (char *)str.value);
+ }
+ gss_release_buffer(&min, &str);
+ } while (msg_ctx != 0);
+}
+
+static void
+print_gss_errs(OM_uint32 major, OM_uint32 minor, gss_OID mech)
+{
+ print_gss_err(major, GSS_C_GSS_CODE, GSS_C_NO_OID);
+ print_gss_err(major, GSS_C_MECH_CODE, mech);
+}
+
+static void
+gss_err(int exitval, OM_uint32 major, OM_uint32 minor, gss_OID mech,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ vwarnx(fmt, args);
+ va_end(args);
+ print_gss_errs(major, minor, mech);
+ exit(exitval);
+}
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"version", 0, arg_flag, &version_flag, "print version", NULL },
+ {"help", 0, arg_flag, &help_flag, NULL, NULL }
+};
+
+static void
+usage(int ret)
+{
+ arg_printusage(args, sizeof(args)/sizeof(*args),
+ NULL, "from_ccache to_ccache");
+ exit(ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ OM_uint32 major, minor;
+ gss_cred_id_t from_cred = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t to_cred = GSS_C_NO_CREDENTIAL;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ char *from_env;
+ char *to_env;
+ int optidx = 0;
+
+ setprogname(argv[0]);
+ if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ if (argc < 2)
+ errx(1, "required arguments missing");
+ if (argc > 2)
+ errx(1, "too many arguments");
+
+ if (asprintf(&from_env, "KRB5CCNAME=%s", argv[0]) == -1 || from_env == NULL)
+ err(1, "out of memory");
+ if (asprintf(&to_env, "KRB5CCNAME=%s", argv[1]) == -1 || to_env == NULL)
+ err(1, "out of memory");
+
+ putenv(from_env);
+ major = gss_add_cred(&minor, GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME,
+ GSS_KRB5_MECHANISM, GSS_C_INITIATE, GSS_C_INDEFINITE,
+ GSS_C_INDEFINITE, &from_cred, NULL, NULL, NULL);
+ if (major != GSS_S_COMPLETE)
+ gss_err(1, major, minor, GSS_KRB5_MECHANISM,
+ "failed to acquire creds from %s", argv[0]);
+
+ putenv(to_env);
+ major = gss_store_cred(&minor, from_cred, GSS_C_INITIATE,
+ GSS_KRB5_MECHANISM, 1, 1, NULL, NULL);
+ if (major != GSS_S_COMPLETE)
+ gss_err(1, major, minor, GSS_KRB5_MECHANISM,
+ "failed to store creds into %s", argv[1]);
+
+ (void) gss_release_cred(&minor, &from_cred);
+ (void) gss_release_cred(&minor, &to_cred);
+
+ major = gss_add_cred(&minor, GSS_C_NO_CREDENTIAL, GSS_C_NO_NAME,
+ GSS_KRB5_MECHANISM, GSS_C_INITIATE, GSS_C_INDEFINITE,
+ GSS_C_INDEFINITE, &cred, NULL, NULL, NULL);
+ if (major != GSS_S_COMPLETE)
+ gss_err(1, major, minor, GSS_KRB5_MECHANISM,
+ "failed to acquire creds from %s", argv[1]);
+ (void) gss_release_cred(&minor, &cred);
+ putenv("KRB5CCNAME");
+ free(from_env);
+ free(to_env);
+
+ return 0;
+}
diff --git a/lib/gssapi/test_common.c b/lib/gssapi/test_common.c
index 8251aef867c5..ebdcd7116580 100644
--- a/lib/gssapi/test_common.c
+++ b/lib/gssapi/test_common.c
@@ -38,7 +38,7 @@
char *
gssapi_err(OM_uint32 maj_stat, OM_uint32 min_stat, gss_OID mech)
{
- OM_uint32 disp_min_stat, disp_maj_stat;
+ OM_uint32 disp_min_stat;
gss_buffer_desc maj_error_message;
gss_buffer_desc min_error_message;
OM_uint32 msg_ctx = 0;
@@ -50,12 +50,10 @@ gssapi_err(OM_uint32 maj_stat, OM_uint32 min_stat, gss_OID mech)
min_error_message.length = 0;
min_error_message.value = NULL;
- disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat,
- GSS_C_GSS_CODE,
- mech, &msg_ctx, &maj_error_message);
- disp_maj_stat = gss_display_status(&disp_min_stat, min_stat,
- GSS_C_MECH_CODE,
- mech, &msg_ctx, &min_error_message);
+ (void) gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE,
+ mech, &msg_ctx, &maj_error_message);
+ (void) gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE,
+ mech, &msg_ctx, &min_error_message);
if (asprintf(&ret, "gss-code: %lu %.*s -- mech-code: %lu %.*s",
(unsigned long)maj_stat,
(int)maj_error_message.length,
diff --git a/lib/gssapi/test_context.c b/lib/gssapi/test_context.c
index 5bdf474d9b93..36a7389aba2f 100644
--- a/lib/gssapi/test_context.c
+++ b/lib/gssapi/test_context.c
@@ -42,6 +42,7 @@
static char *type_string;
static char *mech_string;
+static char *mechs_string;
static char *ret_mech_string;
static char *client_name;
static char *client_password;
@@ -50,6 +51,7 @@ static int mutual_auth_flag = 0;
static int dce_style_flag = 0;
static int wrapunwrap_flag = 0;
static int iov_flag = 0;
+static int aead_flag = 0;
static int getverifymic_flag = 0;
static int deleg_flag = 0;
static int policy_deleg_flag = 0;
@@ -90,17 +92,49 @@ init_o2n(void)
static gss_OID
string_to_oid(const char *name)
{
- int i;
+ size_t i;
for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
if (strcasecmp(name, o2n[i].name) == 0)
return o2n[i].oid;
errx(1, "name '%s' not unknown", name);
}
+static void
+string_to_oids(gss_OID_set *oidsetp, gss_OID_set oidset,
+ gss_OID_desc *oidarray, size_t oidarray_len,
+ char *names)
+{
+ char *name;
+ char *s;
+
+ if (names[0] == '\0') {
+ *oidsetp = GSS_C_NO_OID_SET;
+ return;
+ }
+
+ oidset->elements = &oidarray[0];
+ if (strcasecmp(names, "all") == 0) {
+ if (sizeof(o2n)/sizeof(o2n[0]) > oidarray_len)
+ errx(1, "internal error: oidarray must be enlarged");
+ for (oidset->count = 0; oidset->count < oidarray_len; oidset->count++)
+ oidset->elements[oidset->count] = *o2n[oidset->count].oid;
+ } else {
+ for (oidset->count = 0, name = strtok_r(names, ", ", &s);
+ name != NULL;
+ oidset->count++, name = strtok_r(NULL, ", ", &s)) {
+ if (oidset->count >= oidarray_len)
+ errx(1, "too many mech names given");
+ oidset->elements[oidset->count] = *string_to_oid(name);
+ }
+ oidset->count = oidset->count;
+ }
+ *oidsetp = oidset;
+}
+
static const char *
oid_to_string(const gss_OID oid)
{
- int i;
+ size_t i;
for (i = 0; i < sizeof(o2n)/sizeof(o2n[0]); i++)
if (gss_oid_equal(oid, o2n[i].oid))
return o2n[i].name;
@@ -411,11 +445,66 @@ wrapunwrap_iov(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
if (conf_state2 != conf_state)
errx(1, "conf state wrong for iov: %x", flags);
+ gss_release_iov_buffer(&min_stat, iov, iov_len);
free(token.data);
}
static void
+wrapunwrap_aead(gss_ctx_id_t cctx, gss_ctx_id_t sctx, int flags, gss_OID mechoid)
+{
+ gss_buffer_desc token, assoc, message = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc output;
+ OM_uint32 min_stat, maj_stat;
+ gss_qop_t qop_state;
+ int conf_state, conf_state2;
+ char assoc_data[9] = "ABCheader";
+ char token_data[16] = "0123456789abcdef";
+
+ if (flags & USE_SIGN_ONLY) {
+ assoc.value = assoc_data;
+ assoc.length = 9;
+ } else {
+ assoc.value = NULL;
+ assoc.length = 0;
+ }
+
+ token.value = token_data;
+ token.length = 16;
+
+ maj_stat = gss_wrap_aead(&min_stat, cctx, dce_style_flag || flags & USE_CONF,
+ GSS_C_QOP_DEFAULT, &assoc, &token,
+ &conf_state, &message);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_wrap_aead failed");
+
+ if ((flags & (USE_SIGN_ONLY|FORCE_IOV)) == 0) {
+ maj_stat = gss_unwrap(&min_stat, sctx, &message,
+ &output, &conf_state2, &qop_state);
+
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap from gss_wrap_aead failed: %s",
+ gssapi_err(maj_stat, min_stat, mechoid));
+ } else {
+ maj_stat = gss_unwrap_aead(&min_stat, sctx, &message, &assoc,
+ &output, &conf_state2, &qop_state);
+ if (maj_stat != GSS_S_COMPLETE)
+ errx(1, "gss_unwrap_aead failed: %x %s", flags,
+ gssapi_err(maj_stat, min_stat, mechoid));
+ }
+
+ if (output.length != token.length)
+ errx(1, "plaintext length wrong for aead");
+ else if (memcmp(output.value, token.value, token.length) != 0)
+ errx(1, "plaintext wrong for aead");
+ if (conf_state2 != conf_state)
+ errx(1, "conf state wrong for aead: %x", flags);
+
+ gss_release_buffer(&min_stat, &message);
+ gss_release_buffer(&min_stat, &output);
+}
+
+static void
getverifymic(gss_ctx_id_t cctx, gss_ctx_id_t sctx, gss_OID mechoid)
{
gss_buffer_desc input_token, output_token;
@@ -461,7 +550,8 @@ empty_release(void)
static struct getargs args[] = {
{"name-type",0, arg_string, &type_string, "type of name", NULL },
- {"mech-type",0, arg_string, &mech_string, "type of mech", NULL },
+ {"mech-type",0, arg_string, &mech_string, "mech type (name)", NULL },
+ {"mech-types",0, arg_string, &mechs_string, "mech types (names)", NULL },
{"ret-mech-type",0, arg_string, &ret_mech_string,
"type of return mech", NULL },
{"dns-canonicalize",0,arg_negative_flag, &dns_canon_flag,
@@ -473,6 +563,7 @@ static struct getargs args[] = {
{"dce-style",0, arg_flag, &dce_style_flag, "dce-style", NULL },
{"wrapunwrap",0, arg_flag, &wrapunwrap_flag, "wrap/unwrap", NULL },
{"iov", 0, arg_flag, &iov_flag, "wrap/unwrap iov", NULL },
+ {"aead", 0, arg_flag, &aead_flag, "wrap/unwrap aead", NULL },
{"getverifymic",0, arg_flag, &getverifymic_flag,
"get and verify mic", NULL },
{"delegate",0, arg_flag, &deleg_flag, "delegate credential", NULL },
@@ -501,7 +592,7 @@ usage (int ret)
int
main(int argc, char **argv)
{
- int optind = 0;
+ int optidx = 0;
OM_uint32 min_stat, maj_stat;
gss_ctx_id_t cctx, sctx;
void *ctx;
@@ -509,6 +600,9 @@ main(int argc, char **argv)
gss_cred_id_t client_cred = GSS_C_NO_CREDENTIAL, deleg_cred = GSS_C_NO_CREDENTIAL;
gss_name_t cname = GSS_C_NO_NAME;
gss_buffer_desc credential_data = GSS_C_EMPTY_BUFFER;
+ gss_OID_desc oids[4];
+ gss_OID_set_desc mechoid_descs;
+ gss_OID_set mechoids = GSS_C_NO_OID_SET;
setprogname(argv[0]);
@@ -519,7 +613,7 @@ main(int argc, char **argv)
cctx = sctx = GSS_C_NO_CONTEXT;
- if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
if (help_flag)
@@ -530,8 +624,8 @@ main(int argc, char **argv)
exit(0);
}
- argc -= optind;
- argv += optind;
+ argc -= optidx;
+ argv += optidx;
if (argc != 1)
usage(1);
@@ -546,13 +640,44 @@ main(int argc, char **argv)
else if (strcmp(type_string, "krb5-principal-name") == 0)
nameoid = GSS_KRB5_NT_PRINCIPAL_NAME;
else
- errx(1, "%s not suppported", type_string);
+ errx(1, "%s not supported", type_string);
if (mech_string == NULL)
mechoid = GSS_KRB5_MECHANISM;
else
mechoid = string_to_oid(mech_string);
+ if (mechs_string == NULL) {
+ /*
+ * We ought to be able to use the OID set of the one mechanism
+ * OID given. But there's some breakage that conspires to make
+ * that fail though it should succeed:
+ *
+ * - the NTLM gss_acquire_cred() refuses to work with
+ * desired_name == GSS_C_NO_NAME
+ * - gss_acquire_cred() with desired_mechs == GSS_C_NO_OID_SET
+ * does work here because we happen to have Kerberos
+ * credentials in check-ntlm, and the subsequent
+ * gss_init_sec_context() call finds no cred element for NTLM
+ * but plows on anyways, surprisingly enough, and then the
+ * NTLM gss_init_sec_context() just works.
+ *
+ * In summary, there's some breakage in gss_init_sec_context()
+ * and some breakage in NTLM that conspires against us here.
+ *
+ * We work around this in check-ntlm and check-spnego by adding
+ * --client-name=user1@${R} to the invocations of this test
+ * program that require it.
+ */
+ oids[0] = *mechoid;
+ mechoid_descs.elements = &oids[0];
+ mechoid_descs.count = 1;
+ mechoids = &mechoid_descs;
+ } else {
+ string_to_oids(&mechoids, &mechoid_descs,
+ oids, sizeof(oids)/sizeof(oids[0]), mechs_string);
+ }
+
if (gsskrb5_acceptor_identity) {
maj_stat = gsskrb5_register_acceptor_identity(gsskrb5_acceptor_identity);
if (maj_stat)
@@ -582,19 +707,24 @@ main(int argc, char **argv)
cname,
&credential_data,
GSS_C_INDEFINITE,
- GSS_C_NO_OID_SET,
+ mechoids,
GSS_C_INITIATE,
&client_cred,
NULL,
NULL);
- if (GSS_ERROR(maj_stat))
+ if (GSS_ERROR(maj_stat)) {
+ if (mechoids != GSS_C_NO_OID_SET && mechoids->count == 1)
+ mechoid = &mechoids->elements[0];
+ else
+ mechoid = GSS_C_NO_OID;
errx(1, "gss_acquire_cred_with_password: %s",
- gssapi_err(maj_stat, min_stat, GSS_C_NO_OID));
+ gssapi_err(maj_stat, min_stat, mechoid));
+ }
} else {
maj_stat = gss_acquire_cred(&min_stat,
cname,
GSS_C_INDEFINITE,
- GSS_C_NO_OID_SET,
+ mechoids,
GSS_C_INITIATE,
&client_cred,
NULL,
@@ -644,7 +774,7 @@ main(int argc, char **argv)
/* XXX should be actual_mech */
if (gss_oid_equal(mechoid, GSS_KRB5_MECHANISM)) {
- time_t time;
+ time_t sc_time;
gss_buffer_desc authz_data;
gss_buffer_desc in, out1, out2;
krb5_keyblock *keyblock, *keyblock2;
@@ -685,15 +815,15 @@ main(int argc, char **argv)
maj_stat = gsskrb5_extract_authtime_from_sec_context(&min_stat,
sctx,
- &time);
+ &sc_time);
if (maj_stat != GSS_S_COMPLETE)
errx(1, "gsskrb5_extract_authtime_from_sec_context failed: %s",
gssapi_err(maj_stat, min_stat, actual_mech));
- if (time > now)
+ if (sc_time > now)
errx(1, "gsskrb5_extract_authtime_from_sec_context failed: "
"time authtime is before now: %ld %ld",
- (long)time, (long)now);
+ (long)sc_time, (long)now);
maj_stat = gsskrb5_extract_service_keyblock(&min_stat,
sctx,
@@ -880,6 +1010,29 @@ main(int argc, char **argv)
wrapunwrap_iov(cctx, sctx, USE_CONF|USE_HEADER_ONLY|FORCE_IOV, actual_mech);
}
+ if (aead_flag) {
+ wrapunwrap_aead(cctx, sctx, 0, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, 0, actual_mech);
+ wrapunwrap_aead(cctx, sctx, FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_CONF, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+
+ wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY, actual_mech);
+ wrapunwrap_aead(cctx, sctx, USE_CONF|USE_SIGN_ONLY|FORCE_IOV, actual_mech);
+ }
+
if (getverifymic_flag) {
getverifymic(cctx, sctx, actual_mech);
getverifymic(cctx, sctx, actual_mech);
@@ -905,6 +1058,13 @@ main(int argc, char **argv)
gss_release_cred(&min_stat, &cred2);
+#if 0
+ /*
+ * XXX We can't do this. Delegated credentials only work with
+ * the actual_mech. We could gss_store_cred the delegated
+ * credentials *then* gss_add/acquire_cred() with SPNEGO, then
+ * we could try loop() with those credentials.
+ */
/* try again using SPNEGO */
if (verbose_flag)
printf("checking spnego on delegated cred\n");
@@ -915,6 +1075,7 @@ main(int argc, char **argv)
gss_delete_sec_context(&min_stat, &sctx, NULL);
gss_release_cred(&min_stat, &cred2);
+#endif
/* check export/import */
if (ei_flag) {
@@ -943,6 +1104,8 @@ main(int argc, char **argv)
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
+#if 0
+ /* XXX See above */
/* try again using SPNEGO */
if (verbose_flag)
printf("checking SPNEGO on export/imported cred\n");
@@ -953,6 +1116,7 @@ main(int argc, char **argv)
gss_delete_sec_context(&min_stat, &cctx, NULL);
gss_delete_sec_context(&min_stat, &sctx, NULL);
+#endif
gss_release_cred(&min_stat, &cred2);
diff --git a/lib/gssapi/test_cred.c b/lib/gssapi/test_cred.c
index 23428050d1e8..7ebab9a24978 100644
--- a/lib/gssapi/test_cred.c
+++ b/lib/gssapi/test_cred.c
@@ -154,6 +154,8 @@ acquire_add_release_add(gss_name_t name, gss_cred_usage_t usage)
NULL,
NULL,
NULL);
+ if (maj_stat != GSS_S_COMPLETE)
+ gss_err(1, min_stat, "add_cred 2 %d != GSS_S_COMPLETE", (int)maj_stat);
maj_stat = gss_release_cred(&min_stat, &cred2);
if (maj_stat != GSS_S_COMPLETE)
@@ -161,7 +163,7 @@ acquire_add_release_add(gss_name_t name, gss_cred_usage_t usage)
maj_stat = gss_release_cred(&min_stat, &cred3);
if (maj_stat != GSS_S_COMPLETE)
- gss_err(1, min_stat, "release 2 %d != GSS_S_COMPLETE", (int)maj_stat);
+ gss_err(1, min_stat, "release 3 %d != GSS_S_COMPLETE", (int)maj_stat);
}
static int version_flag = 0;
diff --git a/lib/gssapi/test_ntlm.c b/lib/gssapi/test_ntlm.c
index 9b289c27bd37..de3a8dd88422 100644
--- a/lib/gssapi/test_ntlm.c
+++ b/lib/gssapi/test_ntlm.c
@@ -131,7 +131,7 @@ test_libntlm_v1(int flags)
free(key.data);
}
- ret = heim_ntlm_encode_type3(&type3, &data);
+ ret = heim_ntlm_encode_type3(&type3, &data, NULL);
if (ret)
errx(1, "heim_ntlm_encode_type3");
@@ -260,7 +260,7 @@ test_libntlm_v2(int flags)
}
}
- ret = heim_ntlm_encode_type3(&type3, &data);
+ ret = heim_ntlm_encode_type3(&type3, &data, NULL);
if (ret)
errx(1, "heim_ntlm_encode_type3");
@@ -309,11 +309,11 @@ usage (int ret)
int
main(int argc, char **argv)
{
- int ret = 0, optind = 0;
+ int ret = 0, optidx = 0;
setprogname(argv[0]);
- if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
if (help_flag)
@@ -324,8 +324,8 @@ main(int argc, char **argv)
exit(0);
}
- argc -= optind;
- argv += optind;
+ argc -= optidx;
+ argv += optidx;
ret += test_libntlm_v1(0);
ret += test_libntlm_v1(NTLM_NEG_KEYEX);
@@ -333,5 +333,5 @@ main(int argc, char **argv)
ret += test_libntlm_v2(0);
ret += test_libntlm_v2(NTLM_NEG_KEYEX);
- return 0;
+ return ret;
}
diff --git a/lib/gssapi/version-script.map b/lib/gssapi/version-script.map
index bcb79bf8f76e..daff44e6568f 100644
--- a/lib/gssapi/version-script.map
+++ b/lib/gssapi/version-script.map
@@ -65,6 +65,7 @@ HEIMDAL_GSS_2.0 {
gss_krb5_get_tkt_flags;
gss_krb5_import_cred;
gss_krb5_set_allowable_enctypes;
+ gss_localname;
gss_mg_collect_error;
gss_oid_equal;
gss_oid_to_str;
@@ -87,11 +88,13 @@ HEIMDAL_GSS_2.0 {
gss_test_oid_set_member;
gss_unseal;
gss_unwrap;
+ gss_unwrap_aead;
gss_unwrap_iov;
gss_userok;
gss_verify;
gss_verify_mic;
gss_wrap;
+ gss_wrap_aead;
gss_wrap_iov;
gss_wrap_iov_length;
gss_wrap_size_limit;
diff --git a/lib/hcrypto/Makefile.am b/lib/hcrypto/Makefile.am
index 0286c8995feb..469176b6c604 100644
--- a/lib/hcrypto/Makefile.am
+++ b/lib/hcrypto/Makefile.am
@@ -2,7 +2,14 @@
include $(top_srcdir)/Makefile.am.common
-AM_CPPFLAGS += -I$(srcdir)/libtommath -DUSE_HCRYPTO_LTM=1
+AUTOMAKE_OPTIONS = subdir-objects
+
+if HAVE_HCRYPTO_W_OPENSSL
+AM_CPPFLAGS += $(INCLUDE_openssl_crypto)
+endif
+
+AM_CPPFLAGS += -I$(top_srcdir)/lib/hx509 \
+ -I$(srcdir)/libtommath -DUSE_HCRYPTO_LTM=1
lib_LTLIBRARIES = libhcrypto.la
check_LTLIBRARIES = libhctest.la
@@ -11,8 +18,13 @@ libhcrypto_la_LDFLAGS = -version-info 5:0:1
libhcrypto_la_LIBADD = \
$(top_builddir)/lib/asn1/libasn1.la \
$(LIB_dlopen) \
+ $(LIB_heimbase) \
$(LIBADD_roken)
+if HAVE_HCRYPTO_W_OPENSSL
+libhcrypto_la_LIBADD += $(LIB_openssl_crypto)
+endif
+
hcryptoincludedir = $(includedir)/hcrypto
buildhcryptoinclude = $(buildinclude)/hcrypto
@@ -29,6 +41,8 @@ hcryptoinclude_HEADERS = \
evp.h \
evp-hcrypto.h \
evp-cc.h \
+ evp-openssl.h \
+ evp-pkcs11.h \
hmac.h \
md2.h \
md4.h \
@@ -39,7 +53,8 @@ hcryptoinclude_HEADERS = \
rc4.h \
rsa.h \
sha.h \
- ui.h
+ ui.h \
+ undef.h
install-build-headers:: $(hcryptoinclude_HEADERS)
@foo='$(hcryptoinclude_HEADERS)'; \
@@ -60,6 +75,7 @@ PROGRAM_TESTS = \
rc2test \
rctest \
test_bn \
+ test_bulk \
test_cipher \
test_engine_dso \
test_hmac \
@@ -85,7 +101,8 @@ check_SCRIPTS = $(SCRIPT_TESTS)
TESTS = $(PROGRAM_TESTS) $(SCRIPT_TESTS)
-LDADD = $(lib_LTLIBRARIES) $(LIB_roken)
+LDADD = $(lib_LTLIBRARIES) $(LIB_roken) $(LIB_openssl_crypto)
+test_rand_LDADD = $(LDADD) -lm
libhcrypto_la_SOURCES = \
$(ltmsources) \
@@ -112,6 +129,8 @@ libhcrypto_la_SOURCES = \
evp.h \
evp-hcrypto.c \
evp-cc.c \
+ evp-openssl.c \
+ evp-pkcs11.c \
engine.c \
engine.h \
hash.h \
@@ -125,7 +144,6 @@ libhcrypto_la_SOURCES = \
md5.h \
pkcs5.c \
pkcs12.c \
- rand-egd.c \
rand-fortuna.c \
rand-timer.c \
rand-unix.c \
@@ -149,7 +167,8 @@ libhcrypto_la_SOURCES = \
sha512.c \
validate.c \
ui.c \
- ui.h
+ ui.h \
+ undef.h
ltmsources = \
libtommath/tommath.h \
@@ -240,7 +259,6 @@ ltmsources = \
libtommath/bn_mp_prime_is_prime.c \
libtommath/bn_mp_prime_next_prime.c \
libtommath/bn_mp_find_prime.c \
- libtommath/bn_mp_isprime.c \
libtommath/bn_mp_dr_reduce.c \
libtommath/bn_mp_dr_is_modulus.c \
libtommath/bn_mp_dr_setup.c \
@@ -317,10 +335,15 @@ EXTRA_DIST = \
DESperate.txt \
passwd_dialog.rc \
libhcrypto-exports.def \
- dllmain.c \
+ dh-tfm.c \
ec.h \
ecdh.h \
ecdsa.h \
+ evp-crypt.c \
+ evp-w32.c \
+ evp-w32.h \
+ evp-wincng.c \
+ evp-wincng.h \
gen-des.pl \
md5crypt_test.c \
passwd_dialog.aps \
@@ -329,7 +352,9 @@ EXTRA_DIST = \
passwd_dialog.res \
passwd_dlg.c \
passwd_dlg.h \
+ rand-w32.c \
resource.h \
+ rsa-tfm.c \
rsakey.der \
rsakey2048.der \
rsakey4096.der \
diff --git a/lib/hcrypto/Makefile.in b/lib/hcrypto/Makefile.in
index f1bbb98f7192..a9c54aa07e5d 100644
--- a/lib/hcrypto/Makefile.in
+++ b/lib/hcrypto/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,14 +96,13 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(hcryptoinclude_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
+@HAVE_HCRYPTO_W_OPENSSL_TRUE@am__append_1 = $(INCLUDE_openssl_crypto)
+@HAVE_HCRYPTO_W_OPENSSL_TRUE@am__append_2 = $(LIB_openssl_crypto)
noinst_PROGRAMS = test_rand$(EXEEXT)
check_PROGRAMS = $(am__EXEEXT_1) test_rsa$(EXEEXT) test_dh$(EXEEXT) \
example_evp_cipher$(EXEEXT)
TESTS = $(am__EXEEXT_1) $(SCRIPT_TESTS)
-@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+@versionscript_TRUE@am__append_3 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
subdir = lib/hcrypto
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -65,8 +118,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -79,6 +131,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -98,6 +151,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(hcryptoinclude_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -123,100 +178,143 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" \
"$(DESTDIR)$(hcryptoincludedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
+@HAVE_HCRYPTO_W_OPENSSL_TRUE@am__DEPENDENCIES_2 = \
+@HAVE_HCRYPTO_W_OPENSSL_TRUE@ $(am__DEPENDENCIES_1)
libhcrypto_la_DEPENDENCIES = $(top_builddir)/lib/asn1/libasn1.la \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
-am__objects_1 = libhcrypto_la-bncore.lo libhcrypto_la-bn_mp_init.lo \
- libhcrypto_la-bn_mp_clear.lo libhcrypto_la-bn_mp_exch.lo \
- libhcrypto_la-bn_mp_grow.lo libhcrypto_la-bn_mp_shrink.lo \
- libhcrypto_la-bn_mp_clamp.lo libhcrypto_la-bn_mp_zero.lo \
- libhcrypto_la-bn_mp_zero_multi.lo libhcrypto_la-bn_mp_set.lo \
- libhcrypto_la-bn_mp_set_int.lo \
- libhcrypto_la-bn_mp_init_size.lo libhcrypto_la-bn_mp_copy.lo \
- libhcrypto_la-bn_mp_init_copy.lo libhcrypto_la-bn_mp_abs.lo \
- libhcrypto_la-bn_mp_neg.lo libhcrypto_la-bn_mp_cmp_mag.lo \
- libhcrypto_la-bn_mp_cmp.lo libhcrypto_la-bn_mp_cmp_d.lo \
- libhcrypto_la-bn_mp_rshd.lo libhcrypto_la-bn_mp_lshd.lo \
- libhcrypto_la-bn_mp_mod_2d.lo libhcrypto_la-bn_mp_div_2d.lo \
- libhcrypto_la-bn_mp_mul_2d.lo libhcrypto_la-bn_mp_div_2.lo \
- libhcrypto_la-bn_mp_mul_2.lo libhcrypto_la-bn_s_mp_add.lo \
- libhcrypto_la-bn_s_mp_sub.lo \
- libhcrypto_la-bn_fast_s_mp_mul_digs.lo \
- libhcrypto_la-bn_s_mp_mul_digs.lo \
- libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo \
- libhcrypto_la-bn_s_mp_mul_high_digs.lo \
- libhcrypto_la-bn_fast_s_mp_sqr.lo libhcrypto_la-bn_s_mp_sqr.lo \
- libhcrypto_la-bn_mp_add.lo libhcrypto_la-bn_mp_sub.lo \
- libhcrypto_la-bn_mp_karatsuba_mul.lo \
- libhcrypto_la-bn_mp_mul.lo \
- libhcrypto_la-bn_mp_karatsuba_sqr.lo \
- libhcrypto_la-bn_mp_sqr.lo libhcrypto_la-bn_mp_div.lo \
- libhcrypto_la-bn_mp_mod.lo libhcrypto_la-bn_mp_add_d.lo \
- libhcrypto_la-bn_mp_sub_d.lo libhcrypto_la-bn_mp_mul_d.lo \
- libhcrypto_la-bn_mp_div_d.lo libhcrypto_la-bn_mp_mod_d.lo \
- libhcrypto_la-bn_mp_expt_d.lo libhcrypto_la-bn_mp_addmod.lo \
- libhcrypto_la-bn_mp_submod.lo libhcrypto_la-bn_mp_mulmod.lo \
- libhcrypto_la-bn_mp_sqrmod.lo libhcrypto_la-bn_mp_gcd.lo \
- libhcrypto_la-bn_mp_lcm.lo libhcrypto_la-bn_fast_mp_invmod.lo \
- libhcrypto_la-bn_mp_invmod.lo libhcrypto_la-bn_mp_reduce.lo \
- libhcrypto_la-bn_mp_montgomery_setup.lo \
- libhcrypto_la-bn_fast_mp_montgomery_reduce.lo \
- libhcrypto_la-bn_mp_montgomery_reduce.lo \
- libhcrypto_la-bn_mp_exptmod_fast.lo \
- libhcrypto_la-bn_mp_exptmod.lo libhcrypto_la-bn_mp_2expt.lo \
- libhcrypto_la-bn_mp_n_root.lo libhcrypto_la-bn_mp_jacobi.lo \
- libhcrypto_la-bn_reverse.lo libhcrypto_la-bn_mp_count_bits.lo \
- libhcrypto_la-bn_mp_read_unsigned_bin.lo \
- libhcrypto_la-bn_mp_read_signed_bin.lo \
- libhcrypto_la-bn_mp_to_unsigned_bin.lo \
- libhcrypto_la-bn_mp_to_signed_bin.lo \
- libhcrypto_la-bn_mp_unsigned_bin_size.lo \
- libhcrypto_la-bn_mp_signed_bin_size.lo \
- libhcrypto_la-bn_mp_xor.lo libhcrypto_la-bn_mp_and.lo \
- libhcrypto_la-bn_mp_or.lo libhcrypto_la-bn_mp_rand.lo \
- libhcrypto_la-bn_mp_montgomery_calc_normalization.lo \
- libhcrypto_la-bn_mp_prime_is_divisible.lo \
- libhcrypto_la-bn_prime_tab.lo \
- libhcrypto_la-bn_mp_prime_fermat.lo \
- libhcrypto_la-bn_mp_prime_miller_rabin.lo \
- libhcrypto_la-bn_mp_prime_is_prime.lo \
- libhcrypto_la-bn_mp_prime_next_prime.lo \
- libhcrypto_la-bn_mp_find_prime.lo \
- libhcrypto_la-bn_mp_isprime.lo \
- libhcrypto_la-bn_mp_dr_reduce.lo \
- libhcrypto_la-bn_mp_dr_is_modulus.lo \
- libhcrypto_la-bn_mp_dr_setup.lo \
- libhcrypto_la-bn_mp_reduce_setup.lo \
- libhcrypto_la-bn_mp_toom_mul.lo \
- libhcrypto_la-bn_mp_toom_sqr.lo libhcrypto_la-bn_mp_div_3.lo \
- libhcrypto_la-bn_s_mp_exptmod.lo \
- libhcrypto_la-bn_mp_reduce_2k.lo \
- libhcrypto_la-bn_mp_reduce_is_2k.lo \
- libhcrypto_la-bn_mp_reduce_2k_setup.lo \
- libhcrypto_la-bn_mp_reduce_2k_l.lo \
- libhcrypto_la-bn_mp_reduce_is_2k_l.lo \
- libhcrypto_la-bn_mp_reduce_2k_setup_l.lo \
- libhcrypto_la-bn_mp_radix_smap.lo \
- libhcrypto_la-bn_mp_read_radix.lo \
- libhcrypto_la-bn_mp_toradix.lo \
- libhcrypto_la-bn_mp_radix_size.lo libhcrypto_la-bn_mp_fread.lo \
- libhcrypto_la-bn_mp_fwrite.lo libhcrypto_la-bn_mp_cnt_lsb.lo \
- libhcrypto_la-bn_error.lo libhcrypto_la-bn_mp_init_multi.lo \
- libhcrypto_la-bn_mp_clear_multi.lo \
- libhcrypto_la-bn_mp_exteuclid.lo \
- libhcrypto_la-bn_mp_toradix_n.lo \
- libhcrypto_la-bn_mp_prime_random_ex.lo \
- libhcrypto_la-bn_mp_get_int.lo libhcrypto_la-bn_mp_sqrt.lo \
- libhcrypto_la-bn_mp_is_square.lo \
- libhcrypto_la-bn_mp_init_set.lo \
- libhcrypto_la-bn_mp_init_set_int.lo \
- libhcrypto_la-bn_mp_invmod_slow.lo \
- libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo \
- libhcrypto_la-bn_mp_to_signed_bin_n.lo \
- libhcrypto_la-bn_mp_to_unsigned_bin_n.lo
+ $(am__DEPENDENCIES_1) $(LIB_heimbase) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_2)
+am__dirstamp = $(am__leading_dot)dirstamp
+am__objects_1 = libtommath/libhcrypto_la-bncore.lo \
+ libtommath/libhcrypto_la-bn_mp_init.lo \
+ libtommath/libhcrypto_la-bn_mp_clear.lo \
+ libtommath/libhcrypto_la-bn_mp_exch.lo \
+ libtommath/libhcrypto_la-bn_mp_grow.lo \
+ libtommath/libhcrypto_la-bn_mp_shrink.lo \
+ libtommath/libhcrypto_la-bn_mp_clamp.lo \
+ libtommath/libhcrypto_la-bn_mp_zero.lo \
+ libtommath/libhcrypto_la-bn_mp_zero_multi.lo \
+ libtommath/libhcrypto_la-bn_mp_set.lo \
+ libtommath/libhcrypto_la-bn_mp_set_int.lo \
+ libtommath/libhcrypto_la-bn_mp_init_size.lo \
+ libtommath/libhcrypto_la-bn_mp_copy.lo \
+ libtommath/libhcrypto_la-bn_mp_init_copy.lo \
+ libtommath/libhcrypto_la-bn_mp_abs.lo \
+ libtommath/libhcrypto_la-bn_mp_neg.lo \
+ libtommath/libhcrypto_la-bn_mp_cmp_mag.lo \
+ libtommath/libhcrypto_la-bn_mp_cmp.lo \
+ libtommath/libhcrypto_la-bn_mp_cmp_d.lo \
+ libtommath/libhcrypto_la-bn_mp_rshd.lo \
+ libtommath/libhcrypto_la-bn_mp_lshd.lo \
+ libtommath/libhcrypto_la-bn_mp_mod_2d.lo \
+ libtommath/libhcrypto_la-bn_mp_div_2d.lo \
+ libtommath/libhcrypto_la-bn_mp_mul_2d.lo \
+ libtommath/libhcrypto_la-bn_mp_div_2.lo \
+ libtommath/libhcrypto_la-bn_mp_mul_2.lo \
+ libtommath/libhcrypto_la-bn_s_mp_add.lo \
+ libtommath/libhcrypto_la-bn_s_mp_sub.lo \
+ libtommath/libhcrypto_la-bn_fast_s_mp_mul_digs.lo \
+ libtommath/libhcrypto_la-bn_s_mp_mul_digs.lo \
+ libtommath/libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo \
+ libtommath/libhcrypto_la-bn_s_mp_mul_high_digs.lo \
+ libtommath/libhcrypto_la-bn_fast_s_mp_sqr.lo \
+ libtommath/libhcrypto_la-bn_s_mp_sqr.lo \
+ libtommath/libhcrypto_la-bn_mp_add.lo \
+ libtommath/libhcrypto_la-bn_mp_sub.lo \
+ libtommath/libhcrypto_la-bn_mp_karatsuba_mul.lo \
+ libtommath/libhcrypto_la-bn_mp_mul.lo \
+ libtommath/libhcrypto_la-bn_mp_karatsuba_sqr.lo \
+ libtommath/libhcrypto_la-bn_mp_sqr.lo \
+ libtommath/libhcrypto_la-bn_mp_div.lo \
+ libtommath/libhcrypto_la-bn_mp_mod.lo \
+ libtommath/libhcrypto_la-bn_mp_add_d.lo \
+ libtommath/libhcrypto_la-bn_mp_sub_d.lo \
+ libtommath/libhcrypto_la-bn_mp_mul_d.lo \
+ libtommath/libhcrypto_la-bn_mp_div_d.lo \
+ libtommath/libhcrypto_la-bn_mp_mod_d.lo \
+ libtommath/libhcrypto_la-bn_mp_expt_d.lo \
+ libtommath/libhcrypto_la-bn_mp_addmod.lo \
+ libtommath/libhcrypto_la-bn_mp_submod.lo \
+ libtommath/libhcrypto_la-bn_mp_mulmod.lo \
+ libtommath/libhcrypto_la-bn_mp_sqrmod.lo \
+ libtommath/libhcrypto_la-bn_mp_gcd.lo \
+ libtommath/libhcrypto_la-bn_mp_lcm.lo \
+ libtommath/libhcrypto_la-bn_fast_mp_invmod.lo \
+ libtommath/libhcrypto_la-bn_mp_invmod.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce.lo \
+ libtommath/libhcrypto_la-bn_mp_montgomery_setup.lo \
+ libtommath/libhcrypto_la-bn_fast_mp_montgomery_reduce.lo \
+ libtommath/libhcrypto_la-bn_mp_montgomery_reduce.lo \
+ libtommath/libhcrypto_la-bn_mp_exptmod_fast.lo \
+ libtommath/libhcrypto_la-bn_mp_exptmod.lo \
+ libtommath/libhcrypto_la-bn_mp_2expt.lo \
+ libtommath/libhcrypto_la-bn_mp_n_root.lo \
+ libtommath/libhcrypto_la-bn_mp_jacobi.lo \
+ libtommath/libhcrypto_la-bn_reverse.lo \
+ libtommath/libhcrypto_la-bn_mp_count_bits.lo \
+ libtommath/libhcrypto_la-bn_mp_read_unsigned_bin.lo \
+ libtommath/libhcrypto_la-bn_mp_read_signed_bin.lo \
+ libtommath/libhcrypto_la-bn_mp_to_unsigned_bin.lo \
+ libtommath/libhcrypto_la-bn_mp_to_signed_bin.lo \
+ libtommath/libhcrypto_la-bn_mp_unsigned_bin_size.lo \
+ libtommath/libhcrypto_la-bn_mp_signed_bin_size.lo \
+ libtommath/libhcrypto_la-bn_mp_xor.lo \
+ libtommath/libhcrypto_la-bn_mp_and.lo \
+ libtommath/libhcrypto_la-bn_mp_or.lo \
+ libtommath/libhcrypto_la-bn_mp_rand.lo \
+ libtommath/libhcrypto_la-bn_mp_montgomery_calc_normalization.lo \
+ libtommath/libhcrypto_la-bn_mp_prime_is_divisible.lo \
+ libtommath/libhcrypto_la-bn_prime_tab.lo \
+ libtommath/libhcrypto_la-bn_mp_prime_fermat.lo \
+ libtommath/libhcrypto_la-bn_mp_prime_miller_rabin.lo \
+ libtommath/libhcrypto_la-bn_mp_prime_is_prime.lo \
+ libtommath/libhcrypto_la-bn_mp_prime_next_prime.lo \
+ libtommath/libhcrypto_la-bn_mp_find_prime.lo \
+ libtommath/libhcrypto_la-bn_mp_dr_reduce.lo \
+ libtommath/libhcrypto_la-bn_mp_dr_is_modulus.lo \
+ libtommath/libhcrypto_la-bn_mp_dr_setup.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce_setup.lo \
+ libtommath/libhcrypto_la-bn_mp_toom_mul.lo \
+ libtommath/libhcrypto_la-bn_mp_toom_sqr.lo \
+ libtommath/libhcrypto_la-bn_mp_div_3.lo \
+ libtommath/libhcrypto_la-bn_s_mp_exptmod.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce_2k.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce_is_2k.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce_2k_setup.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce_2k_l.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce_is_2k_l.lo \
+ libtommath/libhcrypto_la-bn_mp_reduce_2k_setup_l.lo \
+ libtommath/libhcrypto_la-bn_mp_radix_smap.lo \
+ libtommath/libhcrypto_la-bn_mp_read_radix.lo \
+ libtommath/libhcrypto_la-bn_mp_toradix.lo \
+ libtommath/libhcrypto_la-bn_mp_radix_size.lo \
+ libtommath/libhcrypto_la-bn_mp_fread.lo \
+ libtommath/libhcrypto_la-bn_mp_fwrite.lo \
+ libtommath/libhcrypto_la-bn_mp_cnt_lsb.lo \
+ libtommath/libhcrypto_la-bn_error.lo \
+ libtommath/libhcrypto_la-bn_mp_init_multi.lo \
+ libtommath/libhcrypto_la-bn_mp_clear_multi.lo \
+ libtommath/libhcrypto_la-bn_mp_exteuclid.lo \
+ libtommath/libhcrypto_la-bn_mp_toradix_n.lo \
+ libtommath/libhcrypto_la-bn_mp_prime_random_ex.lo \
+ libtommath/libhcrypto_la-bn_mp_get_int.lo \
+ libtommath/libhcrypto_la-bn_mp_sqrt.lo \
+ libtommath/libhcrypto_la-bn_mp_is_square.lo \
+ libtommath/libhcrypto_la-bn_mp_init_set.lo \
+ libtommath/libhcrypto_la-bn_mp_init_set_int.lo \
+ libtommath/libhcrypto_la-bn_mp_invmod_slow.lo \
+ libtommath/libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo \
+ libtommath/libhcrypto_la-bn_mp_to_signed_bin_n.lo \
+ libtommath/libhcrypto_la-bn_mp_to_unsigned_bin_n.lo
am_libhcrypto_la_OBJECTS = $(am__objects_1) libhcrypto_la-aes.lo \
libhcrypto_la-bn.lo libhcrypto_la-common.lo \
libhcrypto_la-camellia.lo libhcrypto_la-camellia-ntt.lo \
@@ -224,29 +322,33 @@ am_libhcrypto_la_OBJECTS = $(am__objects_1) libhcrypto_la-aes.lo \
libhcrypto_la-dh-ltm.lo libhcrypto_la-dsa.lo \
libhcrypto_la-doxygen.lo libhcrypto_la-evp.lo \
libhcrypto_la-evp-hcrypto.lo libhcrypto_la-evp-cc.lo \
+ libhcrypto_la-evp-openssl.lo libhcrypto_la-evp-pkcs11.lo \
libhcrypto_la-engine.lo libhcrypto_la-hmac.lo \
libhcrypto_la-md2.lo libhcrypto_la-md4.lo libhcrypto_la-md5.lo \
libhcrypto_la-pkcs5.lo libhcrypto_la-pkcs12.lo \
- libhcrypto_la-rand-egd.lo libhcrypto_la-rand-fortuna.lo \
- libhcrypto_la-rand-timer.lo libhcrypto_la-rand-unix.lo \
- libhcrypto_la-rand.lo libhcrypto_la-rc2.lo \
- libhcrypto_la-rc4.lo libhcrypto_la-rijndael-alg-fst.lo \
- libhcrypto_la-rnd_keys.lo libhcrypto_la-rsa.lo \
- libhcrypto_la-rsa-gmp.lo libhcrypto_la-rsa-ltm.lo \
- libhcrypto_la-sha.lo libhcrypto_la-sha256.lo \
- libhcrypto_la-sha512.lo libhcrypto_la-validate.lo \
- libhcrypto_la-ui.lo
+ libhcrypto_la-rand-fortuna.lo libhcrypto_la-rand-timer.lo \
+ libhcrypto_la-rand-unix.lo libhcrypto_la-rand.lo \
+ libhcrypto_la-rc2.lo libhcrypto_la-rc4.lo \
+ libhcrypto_la-rijndael-alg-fst.lo libhcrypto_la-rnd_keys.lo \
+ libhcrypto_la-rsa.lo libhcrypto_la-rsa-gmp.lo \
+ libhcrypto_la-rsa-ltm.lo libhcrypto_la-sha.lo \
+ libhcrypto_la-sha256.lo libhcrypto_la-sha512.lo \
+ libhcrypto_la-validate.lo libhcrypto_la-ui.lo
libhcrypto_la_OBJECTS = $(am_libhcrypto_la_OBJECTS)
-libhcrypto_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libhcrypto_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libhcrypto_la_LDFLAGS) $(LDFLAGS) -o $@
libhctest_la_LIBADD =
am_libhctest_la_OBJECTS = des.lo ui.lo
libhctest_la_OBJECTS = $(am_libhctest_la_OBJECTS)
am__EXEEXT_1 = destest$(EXEEXT) mdtest$(EXEEXT) rc2test$(EXEEXT) \
- rctest$(EXEEXT) test_bn$(EXEEXT) test_cipher$(EXEEXT) \
- test_engine_dso$(EXEEXT) test_hmac$(EXEEXT) \
- test_pkcs12$(EXEEXT) test_pkcs5$(EXEEXT)
+ rctest$(EXEEXT) test_bn$(EXEEXT) test_bulk$(EXEEXT) \
+ test_cipher$(EXEEXT) test_engine_dso$(EXEEXT) \
+ test_hmac$(EXEEXT) test_pkcs12$(EXEEXT) test_pkcs5$(EXEEXT)
PROGRAMS = $(noinst_PROGRAMS)
destest_SOURCES = destest.c
destest_OBJECTS = destest.$(OBJEXT)
@@ -255,86 +357,327 @@ example_evp_cipher_SOURCES = example_evp_cipher.c
example_evp_cipher_OBJECTS = example_evp_cipher.$(OBJEXT)
example_evp_cipher_LDADD = $(LDADD)
example_evp_cipher_DEPENDENCIES = $(lib_LTLIBRARIES) \
- $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
mdtest_SOURCES = mdtest.c
mdtest_OBJECTS = mdtest.$(OBJEXT)
mdtest_LDADD = $(LDADD)
-mdtest_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+mdtest_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
rc2test_SOURCES = rc2test.c
rc2test_OBJECTS = rc2test.$(OBJEXT)
rc2test_LDADD = $(LDADD)
-rc2test_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+rc2test_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
rctest_SOURCES = rctest.c
rctest_OBJECTS = rctest.$(OBJEXT)
rctest_LDADD = $(LDADD)
-rctest_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+rctest_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_bn_SOURCES = test_bn.c
test_bn_OBJECTS = test_bn.$(OBJEXT)
test_bn_LDADD = $(LDADD)
-test_bn_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_bn_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+test_bulk_SOURCES = test_bulk.c
+test_bulk_OBJECTS = test_bulk.$(OBJEXT)
+test_bulk_LDADD = $(LDADD)
+test_bulk_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_cipher_SOURCES = test_cipher.c
test_cipher_OBJECTS = test_cipher.$(OBJEXT)
test_cipher_LDADD = $(LDADD)
-test_cipher_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_cipher_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_dh_SOURCES = test_dh.c
test_dh_OBJECTS = test_dh.$(OBJEXT)
test_dh_LDADD = $(LDADD)
-test_dh_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_dh_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_engine_dso_SOURCES = test_engine_dso.c
test_engine_dso_OBJECTS = test_engine_dso.$(OBJEXT)
test_engine_dso_LDADD = $(LDADD)
test_engine_dso_DEPENDENCIES = $(lib_LTLIBRARIES) \
- $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
test_hmac_SOURCES = test_hmac.c
test_hmac_OBJECTS = test_hmac.$(OBJEXT)
test_hmac_LDADD = $(LDADD)
-test_hmac_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_hmac_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_pkcs12_SOURCES = test_pkcs12.c
test_pkcs12_OBJECTS = test_pkcs12.$(OBJEXT)
test_pkcs12_LDADD = $(LDADD)
-test_pkcs12_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_pkcs12_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_pkcs5_SOURCES = test_pkcs5.c
test_pkcs5_OBJECTS = test_pkcs5.$(OBJEXT)
test_pkcs5_LDADD = $(LDADD)
-test_pkcs5_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_pkcs5_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_rand_SOURCES = test_rand.c
test_rand_OBJECTS = test_rand.$(OBJEXT)
-test_rand_LDADD = $(LDADD)
-test_rand_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+am__DEPENDENCIES_3 = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+test_rand_DEPENDENCIES = $(am__DEPENDENCIES_3)
test_rsa_SOURCES = test_rsa.c
test_rsa_OBJECTS = test_rsa.$(OBJEXT)
test_rsa_LDADD = $(LDADD)
-test_rsa_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1)
+test_rsa_DEPENDENCIES = $(lib_LTLIBRARIES) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(libhcrypto_la_SOURCES) $(libhctest_la_SOURCES) destest.c \
example_evp_cipher.c mdtest.c rc2test.c rctest.c test_bn.c \
- test_cipher.c test_dh.c test_engine_dso.c test_hmac.c \
- test_pkcs12.c test_pkcs5.c test_rand.c test_rsa.c
+ test_bulk.c test_cipher.c test_dh.c test_engine_dso.c \
+ test_hmac.c test_pkcs12.c test_pkcs5.c test_rand.c test_rsa.c
DIST_SOURCES = $(libhcrypto_la_SOURCES) $(libhctest_la_SOURCES) \
destest.c example_evp_cipher.c mdtest.c rc2test.c rctest.c \
- test_bn.c test_cipher.c test_dh.c test_engine_dso.c \
- test_hmac.c test_pkcs12.c test_pkcs5.c test_rand.c test_rsa.c
+ test_bn.c test_bulk.c test_cipher.c test_dh.c \
+ test_engine_dso.c test_hmac.c test_pkcs12.c test_pkcs5.c \
+ test_rand.c test_rsa.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(hcryptoinclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -353,12 +696,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -368,17 +711,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -397,12 +740,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -411,6 +751,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -427,10 +768,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -438,6 +777,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -452,12 +792,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -474,6 +817,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -488,13 +832,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -518,6 +856,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -553,38 +893,45 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
-AM_CPPFLAGS = $(INCLUDES_roken) -I$(srcdir)/libtommath \
+AM_CPPFLAGS = $(INCLUDES_roken) $(am__append_1) \
+ -I$(top_srcdir)/lib/hx509 -I$(srcdir)/libtommath \
-DUSE_HCRYPTO_LTM=1
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
+AUTOMAKE_OPTIONS = subdir-objects
lib_LTLIBRARIES = libhcrypto.la
check_LTLIBRARIES = libhctest.la
-libhcrypto_la_LDFLAGS = -version-info 5:0:1 $(am__append_1)
-libhcrypto_la_LIBADD = \
- $(top_builddir)/lib/asn1/libasn1.la \
- $(LIB_dlopen) \
- $(LIBADD_roken)
-
+libhcrypto_la_LDFLAGS = -version-info 5:0:1 $(am__append_3)
+libhcrypto_la_LIBADD = $(top_builddir)/lib/asn1/libasn1.la \
+ $(LIB_dlopen) $(LIB_heimbase) $(LIBADD_roken) $(am__append_2)
hcryptoincludedir = $(includedir)/hcrypto
buildhcryptoinclude = $(buildinclude)/hcrypto
hcryptoinclude_HEADERS = \
@@ -600,6 +947,8 @@ hcryptoinclude_HEADERS = \
evp.h \
evp-hcrypto.h \
evp-cc.h \
+ evp-openssl.h \
+ evp-pkcs11.h \
hmac.h \
md2.h \
md4.h \
@@ -610,7 +959,8 @@ hcryptoinclude_HEADERS = \
rc4.h \
rsa.h \
sha.h \
- ui.h
+ ui.h \
+ undef.h
PROGRAM_TESTS = \
destest \
@@ -618,6 +968,7 @@ PROGRAM_TESTS = \
rc2test \
rctest \
test_bn \
+ test_bulk \
test_cipher \
test_engine_dso \
test_hmac \
@@ -636,7 +987,8 @@ SCRIPT_TESTS = \
test_crypto
check_SCRIPTS = $(SCRIPT_TESTS)
-LDADD = $(lib_LTLIBRARIES) $(LIB_roken)
+LDADD = $(lib_LTLIBRARIES) $(LIB_roken) $(LIB_openssl_crypto)
+test_rand_LDADD = $(LDADD) -lm
libhcrypto_la_SOURCES = \
$(ltmsources) \
aes.c \
@@ -662,6 +1014,8 @@ libhcrypto_la_SOURCES = \
evp.h \
evp-hcrypto.c \
evp-cc.c \
+ evp-openssl.c \
+ evp-pkcs11.c \
engine.c \
engine.h \
hash.h \
@@ -675,7 +1029,6 @@ libhcrypto_la_SOURCES = \
md5.h \
pkcs5.c \
pkcs12.c \
- rand-egd.c \
rand-fortuna.c \
rand-timer.c \
rand-unix.c \
@@ -699,7 +1052,8 @@ libhcrypto_la_SOURCES = \
sha512.c \
validate.c \
ui.c \
- ui.h
+ ui.h \
+ undef.h
ltmsources = \
libtommath/tommath.h \
@@ -790,7 +1144,6 @@ ltmsources = \
libtommath/bn_mp_prime_is_prime.c \
libtommath/bn_mp_prime_next_prime.c \
libtommath/bn_mp_find_prime.c \
- libtommath/bn_mp_isprime.c \
libtommath/bn_mp_dr_reduce.c \
libtommath/bn_mp_dr_is_modulus.c \
libtommath/bn_mp_dr_setup.c \
@@ -847,10 +1200,15 @@ EXTRA_DIST = \
DESperate.txt \
passwd_dialog.rc \
libhcrypto-exports.def \
- dllmain.c \
+ dh-tfm.c \
ec.h \
ecdh.h \
ecdsa.h \
+ evp-crypt.c \
+ evp-w32.c \
+ evp-w32.h \
+ evp-wincng.c \
+ evp-wincng.h \
gen-des.pl \
md5crypt_test.c \
passwd_dialog.aps \
@@ -859,7 +1217,9 @@ EXTRA_DIST = \
passwd_dialog.res \
passwd_dlg.c \
passwd_dlg.h \
+ rand-w32.c \
resource.h \
+ rsa-tfm.c \
rsakey.der \
rsakey2048.der \
rsakey4096.der \
@@ -869,7 +1229,7 @@ EXTRA_DIST = \
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -882,7 +1242,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/hcrypto/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/hcrypto/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -891,6 +1250,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -903,15 +1263,17 @@ $(am__aclocal_m4_deps):
clean-checkLTLIBRARIES:
-test -z "$(check_LTLIBRARIES)" || rm -f $(check_LTLIBRARIES)
- @list='$(check_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @list='$(check_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -919,6 +1281,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -934,16 +1298,325 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libhcrypto.la: $(libhcrypto_la_OBJECTS) $(libhcrypto_la_DEPENDENCIES)
- $(libhcrypto_la_LINK) -rpath $(libdir) $(libhcrypto_la_OBJECTS) $(libhcrypto_la_LIBADD) $(LIBS)
-libhctest.la: $(libhctest_la_OBJECTS) $(libhctest_la_DEPENDENCIES)
- $(LINK) $(libhctest_la_OBJECTS) $(libhctest_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+libtommath/$(am__dirstamp):
+ @$(MKDIR_P) libtommath
+ @: > libtommath/$(am__dirstamp)
+libtommath/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) libtommath/$(DEPDIR)
+ @: > libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bncore.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_init.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_clear.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_exch.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_grow.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_shrink.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_clamp.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_zero.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_zero_multi.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_set.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_set_int.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_init_size.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_copy.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_init_copy.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_abs.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_neg.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_cmp_mag.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_cmp.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_cmp_d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_rshd.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_lshd.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mod_2d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_div_2d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mul_2d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_div_2.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mul_2.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_s_mp_add.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_s_mp_sub.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_fast_s_mp_mul_digs.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_s_mp_mul_digs.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_s_mp_mul_high_digs.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_fast_s_mp_sqr.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_s_mp_sqr.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_add.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_sub.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_karatsuba_mul.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mul.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_karatsuba_sqr.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_sqr.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_div.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mod.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_add_d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_sub_d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mul_d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_div_d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mod_d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_expt_d.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_addmod.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_submod.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_mulmod.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_sqrmod.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_gcd.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_lcm.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_fast_mp_invmod.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_invmod.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_montgomery_setup.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_fast_mp_montgomery_reduce.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_montgomery_reduce.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_exptmod_fast.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_exptmod.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_2expt.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_n_root.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_jacobi.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_reverse.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_count_bits.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_read_unsigned_bin.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_read_signed_bin.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_to_unsigned_bin.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_to_signed_bin.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_unsigned_bin_size.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_signed_bin_size.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_xor.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_and.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_or.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_rand.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_montgomery_calc_normalization.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_prime_is_divisible.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_prime_tab.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_prime_fermat.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_prime_miller_rabin.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_prime_is_prime.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_prime_next_prime.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_find_prime.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_dr_reduce.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_dr_is_modulus.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_dr_setup.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce_setup.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_toom_mul.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_toom_sqr.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_div_3.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_s_mp_exptmod.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce_2k.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce_is_2k.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce_2k_setup.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce_2k_l.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce_is_2k_l.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_reduce_2k_setup_l.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_radix_smap.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_read_radix.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_toradix.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_radix_size.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_fread.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_fwrite.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_cnt_lsb.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_error.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_init_multi.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_clear_multi.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_exteuclid.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_toradix_n.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_prime_random_ex.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_get_int.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_sqrt.lo: libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_is_square.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_init_set.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_init_set_int.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_invmod_slow.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_to_signed_bin_n.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+libtommath/libhcrypto_la-bn_mp_to_unsigned_bin_n.lo: \
+ libtommath/$(am__dirstamp) \
+ libtommath/$(DEPDIR)/$(am__dirstamp)
+
+libhcrypto.la: $(libhcrypto_la_OBJECTS) $(libhcrypto_la_DEPENDENCIES) $(EXTRA_libhcrypto_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libhcrypto_la_LINK) -rpath $(libdir) $(libhcrypto_la_OBJECTS) $(libhcrypto_la_LIBADD) $(LIBS)
+
+libhctest.la: $(libhctest_la_OBJECTS) $(libhctest_la_DEPENDENCIES) $(EXTRA_libhctest_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libhctest_la_OBJECTS) $(libhctest_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -962,51 +1635,71 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-destest$(EXEEXT): $(destest_OBJECTS) $(destest_DEPENDENCIES)
+
+destest$(EXEEXT): $(destest_OBJECTS) $(destest_DEPENDENCIES) $(EXTRA_destest_DEPENDENCIES)
@rm -f destest$(EXEEXT)
- $(LINK) $(destest_OBJECTS) $(destest_LDADD) $(LIBS)
-example_evp_cipher$(EXEEXT): $(example_evp_cipher_OBJECTS) $(example_evp_cipher_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(destest_OBJECTS) $(destest_LDADD) $(LIBS)
+
+example_evp_cipher$(EXEEXT): $(example_evp_cipher_OBJECTS) $(example_evp_cipher_DEPENDENCIES) $(EXTRA_example_evp_cipher_DEPENDENCIES)
@rm -f example_evp_cipher$(EXEEXT)
- $(LINK) $(example_evp_cipher_OBJECTS) $(example_evp_cipher_LDADD) $(LIBS)
-mdtest$(EXEEXT): $(mdtest_OBJECTS) $(mdtest_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(example_evp_cipher_OBJECTS) $(example_evp_cipher_LDADD) $(LIBS)
+
+mdtest$(EXEEXT): $(mdtest_OBJECTS) $(mdtest_DEPENDENCIES) $(EXTRA_mdtest_DEPENDENCIES)
@rm -f mdtest$(EXEEXT)
- $(LINK) $(mdtest_OBJECTS) $(mdtest_LDADD) $(LIBS)
-rc2test$(EXEEXT): $(rc2test_OBJECTS) $(rc2test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(mdtest_OBJECTS) $(mdtest_LDADD) $(LIBS)
+
+rc2test$(EXEEXT): $(rc2test_OBJECTS) $(rc2test_DEPENDENCIES) $(EXTRA_rc2test_DEPENDENCIES)
@rm -f rc2test$(EXEEXT)
- $(LINK) $(rc2test_OBJECTS) $(rc2test_LDADD) $(LIBS)
-rctest$(EXEEXT): $(rctest_OBJECTS) $(rctest_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(rc2test_OBJECTS) $(rc2test_LDADD) $(LIBS)
+
+rctest$(EXEEXT): $(rctest_OBJECTS) $(rctest_DEPENDENCIES) $(EXTRA_rctest_DEPENDENCIES)
@rm -f rctest$(EXEEXT)
- $(LINK) $(rctest_OBJECTS) $(rctest_LDADD) $(LIBS)
-test_bn$(EXEEXT): $(test_bn_OBJECTS) $(test_bn_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(rctest_OBJECTS) $(rctest_LDADD) $(LIBS)
+
+test_bn$(EXEEXT): $(test_bn_OBJECTS) $(test_bn_DEPENDENCIES) $(EXTRA_test_bn_DEPENDENCIES)
@rm -f test_bn$(EXEEXT)
- $(LINK) $(test_bn_OBJECTS) $(test_bn_LDADD) $(LIBS)
-test_cipher$(EXEEXT): $(test_cipher_OBJECTS) $(test_cipher_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_bn_OBJECTS) $(test_bn_LDADD) $(LIBS)
+
+test_bulk$(EXEEXT): $(test_bulk_OBJECTS) $(test_bulk_DEPENDENCIES) $(EXTRA_test_bulk_DEPENDENCIES)
+ @rm -f test_bulk$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_bulk_OBJECTS) $(test_bulk_LDADD) $(LIBS)
+
+test_cipher$(EXEEXT): $(test_cipher_OBJECTS) $(test_cipher_DEPENDENCIES) $(EXTRA_test_cipher_DEPENDENCIES)
@rm -f test_cipher$(EXEEXT)
- $(LINK) $(test_cipher_OBJECTS) $(test_cipher_LDADD) $(LIBS)
-test_dh$(EXEEXT): $(test_dh_OBJECTS) $(test_dh_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_cipher_OBJECTS) $(test_cipher_LDADD) $(LIBS)
+
+test_dh$(EXEEXT): $(test_dh_OBJECTS) $(test_dh_DEPENDENCIES) $(EXTRA_test_dh_DEPENDENCIES)
@rm -f test_dh$(EXEEXT)
- $(LINK) $(test_dh_OBJECTS) $(test_dh_LDADD) $(LIBS)
-test_engine_dso$(EXEEXT): $(test_engine_dso_OBJECTS) $(test_engine_dso_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_dh_OBJECTS) $(test_dh_LDADD) $(LIBS)
+
+test_engine_dso$(EXEEXT): $(test_engine_dso_OBJECTS) $(test_engine_dso_DEPENDENCIES) $(EXTRA_test_engine_dso_DEPENDENCIES)
@rm -f test_engine_dso$(EXEEXT)
- $(LINK) $(test_engine_dso_OBJECTS) $(test_engine_dso_LDADD) $(LIBS)
-test_hmac$(EXEEXT): $(test_hmac_OBJECTS) $(test_hmac_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_engine_dso_OBJECTS) $(test_engine_dso_LDADD) $(LIBS)
+
+test_hmac$(EXEEXT): $(test_hmac_OBJECTS) $(test_hmac_DEPENDENCIES) $(EXTRA_test_hmac_DEPENDENCIES)
@rm -f test_hmac$(EXEEXT)
- $(LINK) $(test_hmac_OBJECTS) $(test_hmac_LDADD) $(LIBS)
-test_pkcs12$(EXEEXT): $(test_pkcs12_OBJECTS) $(test_pkcs12_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_hmac_OBJECTS) $(test_hmac_LDADD) $(LIBS)
+
+test_pkcs12$(EXEEXT): $(test_pkcs12_OBJECTS) $(test_pkcs12_DEPENDENCIES) $(EXTRA_test_pkcs12_DEPENDENCIES)
@rm -f test_pkcs12$(EXEEXT)
- $(LINK) $(test_pkcs12_OBJECTS) $(test_pkcs12_LDADD) $(LIBS)
-test_pkcs5$(EXEEXT): $(test_pkcs5_OBJECTS) $(test_pkcs5_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_pkcs12_OBJECTS) $(test_pkcs12_LDADD) $(LIBS)
+
+test_pkcs5$(EXEEXT): $(test_pkcs5_OBJECTS) $(test_pkcs5_DEPENDENCIES) $(EXTRA_test_pkcs5_DEPENDENCIES)
@rm -f test_pkcs5$(EXEEXT)
- $(LINK) $(test_pkcs5_OBJECTS) $(test_pkcs5_LDADD) $(LIBS)
-test_rand$(EXEEXT): $(test_rand_OBJECTS) $(test_rand_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_pkcs5_OBJECTS) $(test_pkcs5_LDADD) $(LIBS)
+
+test_rand$(EXEEXT): $(test_rand_OBJECTS) $(test_rand_DEPENDENCIES) $(EXTRA_test_rand_DEPENDENCIES)
@rm -f test_rand$(EXEEXT)
- $(LINK) $(test_rand_OBJECTS) $(test_rand_LDADD) $(LIBS)
-test_rsa$(EXEEXT): $(test_rsa_OBJECTS) $(test_rsa_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_rand_OBJECTS) $(test_rand_LDADD) $(LIBS)
+
+test_rsa$(EXEEXT): $(test_rsa_OBJECTS) $(test_rsa_DEPENDENCIES) $(EXTRA_test_rsa_DEPENDENCIES)
@rm -f test_rsa$(EXEEXT)
- $(LINK) $(test_rsa_OBJECTS) $(test_rsa_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_rsa_OBJECTS) $(test_rsa_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
+ -rm -f libtommath/*.$(OBJEXT)
+ -rm -f libtommath/*.lo
distclean-compile:
-rm -f *.tab.c
@@ -1016,128 +1709,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/example_evp_cipher.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-aes.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_error.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_2expt.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_abs.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_add.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_add_d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_addmod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_and.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_clamp.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_clear.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_cmp.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_copy.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_div.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_div_2.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_div_3.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_div_d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_exch.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_fread.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_gcd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_get_int.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_grow.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_init.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_init_set.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_init_size.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_invmod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_is_square.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_isprime.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_lcm.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_lshd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mul.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_n_root.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_neg.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_or.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_rand.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_rshd.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_set.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_set_int.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_shrink.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_sqr.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_sub.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_submod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_toradix.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_xor.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_zero.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_prime_tab.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_reverse.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_s_mp_add.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-bncore.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-camellia-ntt.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-camellia.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-common.Plo@am__quote@
@@ -1149,6 +1720,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-engine.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-evp-cc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-evp-hcrypto.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-evp-openssl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-evp-pkcs11.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-evp.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-hmac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-md2.Plo@am__quote@
@@ -1156,7 +1729,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-md5.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-pkcs12.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-pkcs5.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-rand-egd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-rand-fortuna.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-rand-timer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhcrypto_la-rand-unix.Plo@am__quote@
@@ -1177,6 +1749,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rc2test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rctest.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bn.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bulk.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cipher.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dh.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_engine_dso.Po@am__quote@
@@ -1186,1150 +1759,1278 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_rand.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_rsa.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ui.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_2expt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_abs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add_d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_addmod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_and.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clamp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_copy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_3.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exch.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fread.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_gcd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_get_int.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_grow.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_size.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_is_square.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lcm.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lshd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_n_root.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_neg.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_or.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rand.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rshd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set_int.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_shrink.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_submod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_xor.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_prime_tab.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_reverse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_add.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@libtommath/$(DEPDIR)/libhcrypto_la-bncore.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\
+@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
-
-libhcrypto_la-bncore.lo: libtommath/bncore.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bncore.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bncore.Tpo -c -o libhcrypto_la-bncore.lo `test -f 'libtommath/bncore.c' || echo '$(srcdir)/'`libtommath/bncore.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bncore.Tpo $(DEPDIR)/libhcrypto_la-bncore.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bncore.c' object='libhcrypto_la-bncore.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\
+@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
+@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bncore.lo `test -f 'libtommath/bncore.c' || echo '$(srcdir)/'`libtommath/bncore.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
-libhcrypto_la-bn_mp_init.lo: libtommath/bn_mp_init.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_init.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_init.Tpo -c -o libhcrypto_la-bn_mp_init.lo `test -f 'libtommath/bn_mp_init.c' || echo '$(srcdir)/'`libtommath/bn_mp_init.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_init.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_init.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_init.c' object='libhcrypto_la-bn_mp_init.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bncore.lo: libtommath/bncore.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bncore.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bncore.Tpo -c -o libtommath/libhcrypto_la-bncore.lo `test -f 'libtommath/bncore.c' || echo '$(srcdir)/'`libtommath/bncore.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bncore.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bncore.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bncore.c' object='libtommath/libhcrypto_la-bncore.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_init.lo `test -f 'libtommath/bn_mp_init.c' || echo '$(srcdir)/'`libtommath/bn_mp_init.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bncore.lo `test -f 'libtommath/bncore.c' || echo '$(srcdir)/'`libtommath/bncore.c
-libhcrypto_la-bn_mp_clear.lo: libtommath/bn_mp_clear.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_clear.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_clear.Tpo -c -o libhcrypto_la-bn_mp_clear.lo `test -f 'libtommath/bn_mp_clear.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_clear.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_clear.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_clear.c' object='libhcrypto_la-bn_mp_clear.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_init.lo: libtommath/bn_mp_init.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_init.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init.Tpo -c -o libtommath/libhcrypto_la-bn_mp_init.lo `test -f 'libtommath/bn_mp_init.c' || echo '$(srcdir)/'`libtommath/bn_mp_init.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_init.c' object='libtommath/libhcrypto_la-bn_mp_init.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_clear.lo `test -f 'libtommath/bn_mp_clear.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_init.lo `test -f 'libtommath/bn_mp_init.c' || echo '$(srcdir)/'`libtommath/bn_mp_init.c
-libhcrypto_la-bn_mp_exch.lo: libtommath/bn_mp_exch.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_exch.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_exch.Tpo -c -o libhcrypto_la-bn_mp_exch.lo `test -f 'libtommath/bn_mp_exch.c' || echo '$(srcdir)/'`libtommath/bn_mp_exch.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_exch.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_exch.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_exch.c' object='libhcrypto_la-bn_mp_exch.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_clear.lo: libtommath/bn_mp_clear.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_clear.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear.Tpo -c -o libtommath/libhcrypto_la-bn_mp_clear.lo `test -f 'libtommath/bn_mp_clear.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_clear.c' object='libtommath/libhcrypto_la-bn_mp_clear.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_exch.lo `test -f 'libtommath/bn_mp_exch.c' || echo '$(srcdir)/'`libtommath/bn_mp_exch.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_clear.lo `test -f 'libtommath/bn_mp_clear.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear.c
-libhcrypto_la-bn_mp_grow.lo: libtommath/bn_mp_grow.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_grow.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_grow.Tpo -c -o libhcrypto_la-bn_mp_grow.lo `test -f 'libtommath/bn_mp_grow.c' || echo '$(srcdir)/'`libtommath/bn_mp_grow.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_grow.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_grow.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_grow.c' object='libhcrypto_la-bn_mp_grow.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_exch.lo: libtommath/bn_mp_exch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_exch.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exch.Tpo -c -o libtommath/libhcrypto_la-bn_mp_exch.lo `test -f 'libtommath/bn_mp_exch.c' || echo '$(srcdir)/'`libtommath/bn_mp_exch.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exch.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exch.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_exch.c' object='libtommath/libhcrypto_la-bn_mp_exch.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_grow.lo `test -f 'libtommath/bn_mp_grow.c' || echo '$(srcdir)/'`libtommath/bn_mp_grow.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_exch.lo `test -f 'libtommath/bn_mp_exch.c' || echo '$(srcdir)/'`libtommath/bn_mp_exch.c
-libhcrypto_la-bn_mp_shrink.lo: libtommath/bn_mp_shrink.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_shrink.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_shrink.Tpo -c -o libhcrypto_la-bn_mp_shrink.lo `test -f 'libtommath/bn_mp_shrink.c' || echo '$(srcdir)/'`libtommath/bn_mp_shrink.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_shrink.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_shrink.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_shrink.c' object='libhcrypto_la-bn_mp_shrink.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_grow.lo: libtommath/bn_mp_grow.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_grow.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_grow.Tpo -c -o libtommath/libhcrypto_la-bn_mp_grow.lo `test -f 'libtommath/bn_mp_grow.c' || echo '$(srcdir)/'`libtommath/bn_mp_grow.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_grow.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_grow.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_grow.c' object='libtommath/libhcrypto_la-bn_mp_grow.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_shrink.lo `test -f 'libtommath/bn_mp_shrink.c' || echo '$(srcdir)/'`libtommath/bn_mp_shrink.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_grow.lo `test -f 'libtommath/bn_mp_grow.c' || echo '$(srcdir)/'`libtommath/bn_mp_grow.c
-libhcrypto_la-bn_mp_clamp.lo: libtommath/bn_mp_clamp.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_clamp.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_clamp.Tpo -c -o libhcrypto_la-bn_mp_clamp.lo `test -f 'libtommath/bn_mp_clamp.c' || echo '$(srcdir)/'`libtommath/bn_mp_clamp.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_clamp.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_clamp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_clamp.c' object='libhcrypto_la-bn_mp_clamp.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_shrink.lo: libtommath/bn_mp_shrink.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_shrink.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_shrink.Tpo -c -o libtommath/libhcrypto_la-bn_mp_shrink.lo `test -f 'libtommath/bn_mp_shrink.c' || echo '$(srcdir)/'`libtommath/bn_mp_shrink.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_shrink.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_shrink.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_shrink.c' object='libtommath/libhcrypto_la-bn_mp_shrink.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_clamp.lo `test -f 'libtommath/bn_mp_clamp.c' || echo '$(srcdir)/'`libtommath/bn_mp_clamp.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_shrink.lo `test -f 'libtommath/bn_mp_shrink.c' || echo '$(srcdir)/'`libtommath/bn_mp_shrink.c
-libhcrypto_la-bn_mp_zero.lo: libtommath/bn_mp_zero.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_zero.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_zero.Tpo -c -o libhcrypto_la-bn_mp_zero.lo `test -f 'libtommath/bn_mp_zero.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_zero.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_zero.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_zero.c' object='libhcrypto_la-bn_mp_zero.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_clamp.lo: libtommath/bn_mp_clamp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_clamp.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clamp.Tpo -c -o libtommath/libhcrypto_la-bn_mp_clamp.lo `test -f 'libtommath/bn_mp_clamp.c' || echo '$(srcdir)/'`libtommath/bn_mp_clamp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clamp.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clamp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_clamp.c' object='libtommath/libhcrypto_la-bn_mp_clamp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_zero.lo `test -f 'libtommath/bn_mp_zero.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_clamp.lo `test -f 'libtommath/bn_mp_clamp.c' || echo '$(srcdir)/'`libtommath/bn_mp_clamp.c
-libhcrypto_la-bn_mp_zero_multi.lo: libtommath/bn_mp_zero_multi.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_zero_multi.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Tpo -c -o libhcrypto_la-bn_mp_zero_multi.lo `test -f 'libtommath/bn_mp_zero_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero_multi.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_zero_multi.c' object='libhcrypto_la-bn_mp_zero_multi.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_zero.lo: libtommath/bn_mp_zero.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_zero.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero.Tpo -c -o libtommath/libhcrypto_la-bn_mp_zero.lo `test -f 'libtommath/bn_mp_zero.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_zero.c' object='libtommath/libhcrypto_la-bn_mp_zero.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_zero_multi.lo `test -f 'libtommath/bn_mp_zero_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero_multi.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_zero.lo `test -f 'libtommath/bn_mp_zero.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero.c
-libhcrypto_la-bn_mp_set.lo: libtommath/bn_mp_set.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_set.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_set.Tpo -c -o libhcrypto_la-bn_mp_set.lo `test -f 'libtommath/bn_mp_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_set.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_set.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_set.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_set.c' object='libhcrypto_la-bn_mp_set.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_zero_multi.lo: libtommath/bn_mp_zero_multi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_zero_multi.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Tpo -c -o libtommath/libhcrypto_la-bn_mp_zero_multi.lo `test -f 'libtommath/bn_mp_zero_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero_multi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_zero_multi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_zero_multi.c' object='libtommath/libhcrypto_la-bn_mp_zero_multi.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_set.lo `test -f 'libtommath/bn_mp_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_set.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_zero_multi.lo `test -f 'libtommath/bn_mp_zero_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_zero_multi.c
-libhcrypto_la-bn_mp_set_int.lo: libtommath/bn_mp_set_int.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_set_int.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_set_int.Tpo -c -o libhcrypto_la-bn_mp_set_int.lo `test -f 'libtommath/bn_mp_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_set_int.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_set_int.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_set_int.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_set_int.c' object='libhcrypto_la-bn_mp_set_int.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_set.lo: libtommath/bn_mp_set.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_set.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set.Tpo -c -o libtommath/libhcrypto_la-bn_mp_set.lo `test -f 'libtommath/bn_mp_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_set.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_set.c' object='libtommath/libhcrypto_la-bn_mp_set.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_set_int.lo `test -f 'libtommath/bn_mp_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_set_int.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_set.lo `test -f 'libtommath/bn_mp_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_set.c
-libhcrypto_la-bn_mp_init_size.lo: libtommath/bn_mp_init_size.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_init_size.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_init_size.Tpo -c -o libhcrypto_la-bn_mp_init_size.lo `test -f 'libtommath/bn_mp_init_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_size.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_init_size.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_init_size.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_init_size.c' object='libhcrypto_la-bn_mp_init_size.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_set_int.lo: libtommath/bn_mp_set_int.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_set_int.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set_int.Tpo -c -o libtommath/libhcrypto_la-bn_mp_set_int.lo `test -f 'libtommath/bn_mp_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_set_int.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set_int.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_set_int.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_set_int.c' object='libtommath/libhcrypto_la-bn_mp_set_int.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_init_size.lo `test -f 'libtommath/bn_mp_init_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_size.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_set_int.lo `test -f 'libtommath/bn_mp_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_set_int.c
-libhcrypto_la-bn_mp_copy.lo: libtommath/bn_mp_copy.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_copy.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_copy.Tpo -c -o libhcrypto_la-bn_mp_copy.lo `test -f 'libtommath/bn_mp_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_copy.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_copy.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_copy.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_copy.c' object='libhcrypto_la-bn_mp_copy.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_init_size.lo: libtommath/bn_mp_init_size.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_init_size.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_size.Tpo -c -o libtommath/libhcrypto_la-bn_mp_init_size.lo `test -f 'libtommath/bn_mp_init_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_size.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_size.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_size.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_init_size.c' object='libtommath/libhcrypto_la-bn_mp_init_size.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_copy.lo `test -f 'libtommath/bn_mp_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_copy.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_init_size.lo `test -f 'libtommath/bn_mp_init_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_size.c
-libhcrypto_la-bn_mp_init_copy.lo: libtommath/bn_mp_init_copy.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_init_copy.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Tpo -c -o libhcrypto_la-bn_mp_init_copy.lo `test -f 'libtommath/bn_mp_init_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_copy.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_init_copy.c' object='libhcrypto_la-bn_mp_init_copy.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_copy.lo: libtommath/bn_mp_copy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_copy.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_copy.Tpo -c -o libtommath/libhcrypto_la-bn_mp_copy.lo `test -f 'libtommath/bn_mp_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_copy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_copy.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_copy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_copy.c' object='libtommath/libhcrypto_la-bn_mp_copy.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_init_copy.lo `test -f 'libtommath/bn_mp_init_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_copy.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_copy.lo `test -f 'libtommath/bn_mp_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_copy.c
-libhcrypto_la-bn_mp_abs.lo: libtommath/bn_mp_abs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_abs.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_abs.Tpo -c -o libhcrypto_la-bn_mp_abs.lo `test -f 'libtommath/bn_mp_abs.c' || echo '$(srcdir)/'`libtommath/bn_mp_abs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_abs.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_abs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_abs.c' object='libhcrypto_la-bn_mp_abs.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_init_copy.lo: libtommath/bn_mp_init_copy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_init_copy.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Tpo -c -o libtommath/libhcrypto_la-bn_mp_init_copy.lo `test -f 'libtommath/bn_mp_init_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_copy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_copy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_init_copy.c' object='libtommath/libhcrypto_la-bn_mp_init_copy.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_abs.lo `test -f 'libtommath/bn_mp_abs.c' || echo '$(srcdir)/'`libtommath/bn_mp_abs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_init_copy.lo `test -f 'libtommath/bn_mp_init_copy.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_copy.c
-libhcrypto_la-bn_mp_neg.lo: libtommath/bn_mp_neg.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_neg.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_neg.Tpo -c -o libhcrypto_la-bn_mp_neg.lo `test -f 'libtommath/bn_mp_neg.c' || echo '$(srcdir)/'`libtommath/bn_mp_neg.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_neg.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_neg.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_neg.c' object='libhcrypto_la-bn_mp_neg.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_abs.lo: libtommath/bn_mp_abs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_abs.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_abs.Tpo -c -o libtommath/libhcrypto_la-bn_mp_abs.lo `test -f 'libtommath/bn_mp_abs.c' || echo '$(srcdir)/'`libtommath/bn_mp_abs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_abs.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_abs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_abs.c' object='libtommath/libhcrypto_la-bn_mp_abs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_neg.lo `test -f 'libtommath/bn_mp_neg.c' || echo '$(srcdir)/'`libtommath/bn_mp_neg.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_abs.lo `test -f 'libtommath/bn_mp_abs.c' || echo '$(srcdir)/'`libtommath/bn_mp_abs.c
-libhcrypto_la-bn_mp_cmp_mag.lo: libtommath/bn_mp_cmp_mag.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_cmp_mag.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Tpo -c -o libhcrypto_la-bn_mp_cmp_mag.lo `test -f 'libtommath/bn_mp_cmp_mag.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_mag.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_cmp_mag.c' object='libhcrypto_la-bn_mp_cmp_mag.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_neg.lo: libtommath/bn_mp_neg.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_neg.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_neg.Tpo -c -o libtommath/libhcrypto_la-bn_mp_neg.lo `test -f 'libtommath/bn_mp_neg.c' || echo '$(srcdir)/'`libtommath/bn_mp_neg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_neg.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_neg.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_neg.c' object='libtommath/libhcrypto_la-bn_mp_neg.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_cmp_mag.lo `test -f 'libtommath/bn_mp_cmp_mag.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_mag.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_neg.lo `test -f 'libtommath/bn_mp_neg.c' || echo '$(srcdir)/'`libtommath/bn_mp_neg.c
-libhcrypto_la-bn_mp_cmp.lo: libtommath/bn_mp_cmp.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_cmp.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_cmp.Tpo -c -o libhcrypto_la-bn_mp_cmp.lo `test -f 'libtommath/bn_mp_cmp.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_cmp.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_cmp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_cmp.c' object='libhcrypto_la-bn_mp_cmp.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_cmp_mag.lo: libtommath/bn_mp_cmp_mag.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_cmp_mag.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Tpo -c -o libtommath/libhcrypto_la-bn_mp_cmp_mag.lo `test -f 'libtommath/bn_mp_cmp_mag.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_mag.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_mag.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_cmp_mag.c' object='libtommath/libhcrypto_la-bn_mp_cmp_mag.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_cmp.lo `test -f 'libtommath/bn_mp_cmp.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_cmp_mag.lo `test -f 'libtommath/bn_mp_cmp_mag.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_mag.c
-libhcrypto_la-bn_mp_cmp_d.lo: libtommath/bn_mp_cmp_d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_cmp_d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Tpo -c -o libhcrypto_la-bn_mp_cmp_d.lo `test -f 'libtommath/bn_mp_cmp_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_cmp_d.c' object='libhcrypto_la-bn_mp_cmp_d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_cmp.lo: libtommath/bn_mp_cmp.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_cmp.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp.Tpo -c -o libtommath/libhcrypto_la-bn_mp_cmp.lo `test -f 'libtommath/bn_mp_cmp.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_cmp.c' object='libtommath/libhcrypto_la-bn_mp_cmp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_cmp_d.lo `test -f 'libtommath/bn_mp_cmp_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_cmp.lo `test -f 'libtommath/bn_mp_cmp.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp.c
-libhcrypto_la-bn_mp_rshd.lo: libtommath/bn_mp_rshd.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_rshd.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_rshd.Tpo -c -o libhcrypto_la-bn_mp_rshd.lo `test -f 'libtommath/bn_mp_rshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_rshd.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_rshd.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_rshd.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_rshd.c' object='libhcrypto_la-bn_mp_rshd.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_cmp_d.lo: libtommath/bn_mp_cmp_d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_cmp_d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_cmp_d.lo `test -f 'libtommath/bn_mp_cmp_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cmp_d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_cmp_d.c' object='libtommath/libhcrypto_la-bn_mp_cmp_d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_rshd.lo `test -f 'libtommath/bn_mp_rshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_rshd.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_cmp_d.lo `test -f 'libtommath/bn_mp_cmp_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_cmp_d.c
-libhcrypto_la-bn_mp_lshd.lo: libtommath/bn_mp_lshd.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_lshd.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_lshd.Tpo -c -o libhcrypto_la-bn_mp_lshd.lo `test -f 'libtommath/bn_mp_lshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_lshd.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_lshd.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_lshd.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_lshd.c' object='libhcrypto_la-bn_mp_lshd.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_rshd.lo: libtommath/bn_mp_rshd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_rshd.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rshd.Tpo -c -o libtommath/libhcrypto_la-bn_mp_rshd.lo `test -f 'libtommath/bn_mp_rshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_rshd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rshd.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rshd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_rshd.c' object='libtommath/libhcrypto_la-bn_mp_rshd.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_lshd.lo `test -f 'libtommath/bn_mp_lshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_lshd.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_rshd.lo `test -f 'libtommath/bn_mp_rshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_rshd.c
-libhcrypto_la-bn_mp_mod_2d.lo: libtommath/bn_mp_mod_2d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mod_2d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Tpo -c -o libhcrypto_la-bn_mp_mod_2d.lo `test -f 'libtommath/bn_mp_mod_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_2d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mod_2d.c' object='libhcrypto_la-bn_mp_mod_2d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_lshd.lo: libtommath/bn_mp_lshd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_lshd.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lshd.Tpo -c -o libtommath/libhcrypto_la-bn_mp_lshd.lo `test -f 'libtommath/bn_mp_lshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_lshd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lshd.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lshd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_lshd.c' object='libtommath/libhcrypto_la-bn_mp_lshd.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mod_2d.lo `test -f 'libtommath/bn_mp_mod_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_2d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_lshd.lo `test -f 'libtommath/bn_mp_lshd.c' || echo '$(srcdir)/'`libtommath/bn_mp_lshd.c
-libhcrypto_la-bn_mp_div_2d.lo: libtommath/bn_mp_div_2d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_div_2d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Tpo -c -o libhcrypto_la-bn_mp_div_2d.lo `test -f 'libtommath/bn_mp_div_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_div_2d.c' object='libhcrypto_la-bn_mp_div_2d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mod_2d.lo: libtommath/bn_mp_mod_2d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mod_2d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mod_2d.lo `test -f 'libtommath/bn_mp_mod_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_2d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_2d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mod_2d.c' object='libtommath/libhcrypto_la-bn_mp_mod_2d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_div_2d.lo `test -f 'libtommath/bn_mp_div_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mod_2d.lo `test -f 'libtommath/bn_mp_mod_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_2d.c
-libhcrypto_la-bn_mp_mul_2d.lo: libtommath/bn_mp_mul_2d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mul_2d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Tpo -c -o libhcrypto_la-bn_mp_mul_2d.lo `test -f 'libtommath/bn_mp_mul_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mul_2d.c' object='libhcrypto_la-bn_mp_mul_2d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_div_2d.lo: libtommath/bn_mp_div_2d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_div_2d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_div_2d.lo `test -f 'libtommath/bn_mp_div_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_div_2d.c' object='libtommath/libhcrypto_la-bn_mp_div_2d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mul_2d.lo `test -f 'libtommath/bn_mp_mul_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_div_2d.lo `test -f 'libtommath/bn_mp_div_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2d.c
-libhcrypto_la-bn_mp_div_2.lo: libtommath/bn_mp_div_2.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_div_2.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_div_2.Tpo -c -o libhcrypto_la-bn_mp_div_2.lo `test -f 'libtommath/bn_mp_div_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_div_2.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_div_2.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_div_2.c' object='libhcrypto_la-bn_mp_div_2.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mul_2d.lo: libtommath/bn_mp_mul_2d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mul_2d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mul_2d.lo `test -f 'libtommath/bn_mp_mul_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mul_2d.c' object='libtommath/libhcrypto_la-bn_mp_mul_2d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_div_2.lo `test -f 'libtommath/bn_mp_div_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mul_2d.lo `test -f 'libtommath/bn_mp_mul_2d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2d.c
-libhcrypto_la-bn_mp_mul_2.lo: libtommath/bn_mp_mul_2.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mul_2.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Tpo -c -o libhcrypto_la-bn_mp_mul_2.lo `test -f 'libtommath/bn_mp_mul_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mul_2.c' object='libhcrypto_la-bn_mp_mul_2.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_div_2.lo: libtommath/bn_mp_div_2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_div_2.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2.Tpo -c -o libtommath/libhcrypto_la-bn_mp_div_2.lo `test -f 'libtommath/bn_mp_div_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_div_2.c' object='libtommath/libhcrypto_la-bn_mp_div_2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mul_2.lo `test -f 'libtommath/bn_mp_mul_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_div_2.lo `test -f 'libtommath/bn_mp_div_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_2.c
-libhcrypto_la-bn_s_mp_add.lo: libtommath/bn_s_mp_add.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_s_mp_add.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_s_mp_add.Tpo -c -o libhcrypto_la-bn_s_mp_add.lo `test -f 'libtommath/bn_s_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_add.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_s_mp_add.Tpo $(DEPDIR)/libhcrypto_la-bn_s_mp_add.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_s_mp_add.c' object='libhcrypto_la-bn_s_mp_add.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mul_2.lo: libtommath/bn_mp_mul_2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mul_2.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mul_2.lo `test -f 'libtommath/bn_mp_mul_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mul_2.c' object='libtommath/libhcrypto_la-bn_mp_mul_2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_s_mp_add.lo `test -f 'libtommath/bn_s_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_add.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mul_2.lo `test -f 'libtommath/bn_mp_mul_2.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_2.c
-libhcrypto_la-bn_s_mp_sub.lo: libtommath/bn_s_mp_sub.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_s_mp_sub.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Tpo -c -o libhcrypto_la-bn_s_mp_sub.lo `test -f 'libtommath/bn_s_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sub.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Tpo $(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_s_mp_sub.c' object='libhcrypto_la-bn_s_mp_sub.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_s_mp_add.lo: libtommath/bn_s_mp_add.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_s_mp_add.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_add.Tpo -c -o libtommath/libhcrypto_la-bn_s_mp_add.lo `test -f 'libtommath/bn_s_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_add.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_add.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_add.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_s_mp_add.c' object='libtommath/libhcrypto_la-bn_s_mp_add.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_s_mp_sub.lo `test -f 'libtommath/bn_s_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sub.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_s_mp_add.lo `test -f 'libtommath/bn_s_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_add.c
-libhcrypto_la-bn_fast_s_mp_mul_digs.lo: libtommath/bn_fast_s_mp_mul_digs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_fast_s_mp_mul_digs.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Tpo -c -o libhcrypto_la-bn_fast_s_mp_mul_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_digs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Tpo $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_fast_s_mp_mul_digs.c' object='libhcrypto_la-bn_fast_s_mp_mul_digs.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_s_mp_sub.lo: libtommath/bn_s_mp_sub.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_s_mp_sub.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Tpo -c -o libtommath/libhcrypto_la-bn_s_mp_sub.lo `test -f 'libtommath/bn_s_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sub.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sub.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_s_mp_sub.c' object='libtommath/libhcrypto_la-bn_s_mp_sub.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_fast_s_mp_mul_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_digs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_s_mp_sub.lo `test -f 'libtommath/bn_s_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sub.c
-libhcrypto_la-bn_s_mp_mul_digs.lo: libtommath/bn_s_mp_mul_digs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_s_mp_mul_digs.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Tpo -c -o libhcrypto_la-bn_s_mp_mul_digs.lo `test -f 'libtommath/bn_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_digs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Tpo $(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_s_mp_mul_digs.c' object='libhcrypto_la-bn_s_mp_mul_digs.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_fast_s_mp_mul_digs.lo: libtommath/bn_fast_s_mp_mul_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_fast_s_mp_mul_digs.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Tpo -c -o libtommath/libhcrypto_la-bn_fast_s_mp_mul_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_digs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_fast_s_mp_mul_digs.c' object='libtommath/libhcrypto_la-bn_fast_s_mp_mul_digs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_s_mp_mul_digs.lo `test -f 'libtommath/bn_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_digs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_fast_s_mp_mul_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_digs.c
-libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo: libtommath/bn_fast_s_mp_mul_high_digs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Tpo -c -o libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_high_digs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Tpo $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_fast_s_mp_mul_high_digs.c' object='libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_s_mp_mul_digs.lo: libtommath/bn_s_mp_mul_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_s_mp_mul_digs.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Tpo -c -o libtommath/libhcrypto_la-bn_s_mp_mul_digs.lo `test -f 'libtommath/bn_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_digs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_s_mp_mul_digs.c' object='libtommath/libhcrypto_la-bn_s_mp_mul_digs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_high_digs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_s_mp_mul_digs.lo `test -f 'libtommath/bn_s_mp_mul_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_digs.c
-libhcrypto_la-bn_s_mp_mul_high_digs.lo: libtommath/bn_s_mp_mul_high_digs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_s_mp_mul_high_digs.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Tpo -c -o libhcrypto_la-bn_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_high_digs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Tpo $(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_s_mp_mul_high_digs.c' object='libhcrypto_la-bn_s_mp_mul_high_digs.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo: libtommath/bn_fast_s_mp_mul_high_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Tpo -c -o libtommath/libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_high_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_mul_high_digs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_fast_s_mp_mul_high_digs.c' object='libtommath/libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_high_digs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_fast_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_fast_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_mul_high_digs.c
-libhcrypto_la-bn_fast_s_mp_sqr.lo: libtommath/bn_fast_s_mp_sqr.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_fast_s_mp_sqr.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Tpo -c -o libhcrypto_la-bn_fast_s_mp_sqr.lo `test -f 'libtommath/bn_fast_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_sqr.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Tpo $(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_fast_s_mp_sqr.c' object='libhcrypto_la-bn_fast_s_mp_sqr.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_s_mp_mul_high_digs.lo: libtommath/bn_s_mp_mul_high_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_s_mp_mul_high_digs.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Tpo -c -o libtommath/libhcrypto_la-bn_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_high_digs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_mul_high_digs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_s_mp_mul_high_digs.c' object='libtommath/libhcrypto_la-bn_s_mp_mul_high_digs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_fast_s_mp_sqr.lo `test -f 'libtommath/bn_fast_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_sqr.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_s_mp_mul_high_digs.lo `test -f 'libtommath/bn_s_mp_mul_high_digs.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_mul_high_digs.c
-libhcrypto_la-bn_s_mp_sqr.lo: libtommath/bn_s_mp_sqr.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_s_mp_sqr.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Tpo -c -o libhcrypto_la-bn_s_mp_sqr.lo `test -f 'libtommath/bn_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sqr.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Tpo $(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_s_mp_sqr.c' object='libhcrypto_la-bn_s_mp_sqr.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_fast_s_mp_sqr.lo: libtommath/bn_fast_s_mp_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_fast_s_mp_sqr.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Tpo -c -o libtommath/libhcrypto_la-bn_fast_s_mp_sqr.lo `test -f 'libtommath/bn_fast_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_s_mp_sqr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_fast_s_mp_sqr.c' object='libtommath/libhcrypto_la-bn_fast_s_mp_sqr.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_s_mp_sqr.lo `test -f 'libtommath/bn_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sqr.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_fast_s_mp_sqr.lo `test -f 'libtommath/bn_fast_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_fast_s_mp_sqr.c
-libhcrypto_la-bn_mp_add.lo: libtommath/bn_mp_add.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_add.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_add.Tpo -c -o libhcrypto_la-bn_mp_add.lo `test -f 'libtommath/bn_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_mp_add.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_add.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_add.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_add.c' object='libhcrypto_la-bn_mp_add.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_s_mp_sqr.lo: libtommath/bn_s_mp_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_s_mp_sqr.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Tpo -c -o libtommath/libhcrypto_la-bn_s_mp_sqr.lo `test -f 'libtommath/bn_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_sqr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_s_mp_sqr.c' object='libtommath/libhcrypto_la-bn_s_mp_sqr.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_add.lo `test -f 'libtommath/bn_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_mp_add.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_s_mp_sqr.lo `test -f 'libtommath/bn_s_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_sqr.c
-libhcrypto_la-bn_mp_sub.lo: libtommath/bn_mp_sub.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_sub.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_sub.Tpo -c -o libhcrypto_la-bn_mp_sub.lo `test -f 'libtommath/bn_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_sub.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_sub.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_sub.c' object='libhcrypto_la-bn_mp_sub.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_add.lo: libtommath/bn_mp_add.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_add.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add.Tpo -c -o libtommath/libhcrypto_la-bn_mp_add.lo `test -f 'libtommath/bn_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_mp_add.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_add.c' object='libtommath/libhcrypto_la-bn_mp_add.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_sub.lo `test -f 'libtommath/bn_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_add.lo `test -f 'libtommath/bn_mp_add.c' || echo '$(srcdir)/'`libtommath/bn_mp_add.c
-libhcrypto_la-bn_mp_karatsuba_mul.lo: libtommath/bn_mp_karatsuba_mul.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_karatsuba_mul.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Tpo -c -o libhcrypto_la-bn_mp_karatsuba_mul.lo `test -f 'libtommath/bn_mp_karatsuba_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_mul.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_karatsuba_mul.c' object='libhcrypto_la-bn_mp_karatsuba_mul.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_sub.lo: libtommath/bn_mp_sub.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_sub.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub.Tpo -c -o libtommath/libhcrypto_la-bn_mp_sub.lo `test -f 'libtommath/bn_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_sub.c' object='libtommath/libhcrypto_la-bn_mp_sub.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_karatsuba_mul.lo `test -f 'libtommath/bn_mp_karatsuba_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_mul.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_sub.lo `test -f 'libtommath/bn_mp_sub.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub.c
-libhcrypto_la-bn_mp_mul.lo: libtommath/bn_mp_mul.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mul.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mul.Tpo -c -o libhcrypto_la-bn_mp_mul.lo `test -f 'libtommath/bn_mp_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mul.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mul.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mul.c' object='libhcrypto_la-bn_mp_mul.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_karatsuba_mul.lo: libtommath/bn_mp_karatsuba_mul.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_karatsuba_mul.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Tpo -c -o libtommath/libhcrypto_la-bn_mp_karatsuba_mul.lo `test -f 'libtommath/bn_mp_karatsuba_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_mul.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_mul.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_karatsuba_mul.c' object='libtommath/libhcrypto_la-bn_mp_karatsuba_mul.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mul.lo `test -f 'libtommath/bn_mp_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_karatsuba_mul.lo `test -f 'libtommath/bn_mp_karatsuba_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_mul.c
-libhcrypto_la-bn_mp_karatsuba_sqr.lo: libtommath/bn_mp_karatsuba_sqr.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_karatsuba_sqr.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Tpo -c -o libhcrypto_la-bn_mp_karatsuba_sqr.lo `test -f 'libtommath/bn_mp_karatsuba_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_sqr.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_karatsuba_sqr.c' object='libhcrypto_la-bn_mp_karatsuba_sqr.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mul.lo: libtommath/bn_mp_mul.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mul.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mul.lo `test -f 'libtommath/bn_mp_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mul.c' object='libtommath/libhcrypto_la-bn_mp_mul.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_karatsuba_sqr.lo `test -f 'libtommath/bn_mp_karatsuba_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_sqr.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mul.lo `test -f 'libtommath/bn_mp_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul.c
-libhcrypto_la-bn_mp_sqr.lo: libtommath/bn_mp_sqr.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_sqr.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_sqr.Tpo -c -o libhcrypto_la-bn_mp_sqr.lo `test -f 'libtommath/bn_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqr.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_sqr.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_sqr.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_sqr.c' object='libhcrypto_la-bn_mp_sqr.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_karatsuba_sqr.lo: libtommath/bn_mp_karatsuba_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_karatsuba_sqr.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Tpo -c -o libtommath/libhcrypto_la-bn_mp_karatsuba_sqr.lo `test -f 'libtommath/bn_mp_karatsuba_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_karatsuba_sqr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_karatsuba_sqr.c' object='libtommath/libhcrypto_la-bn_mp_karatsuba_sqr.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_sqr.lo `test -f 'libtommath/bn_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqr.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_karatsuba_sqr.lo `test -f 'libtommath/bn_mp_karatsuba_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_karatsuba_sqr.c
-libhcrypto_la-bn_mp_div.lo: libtommath/bn_mp_div.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_div.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_div.Tpo -c -o libhcrypto_la-bn_mp_div.lo `test -f 'libtommath/bn_mp_div.c' || echo '$(srcdir)/'`libtommath/bn_mp_div.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_div.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_div.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_div.c' object='libhcrypto_la-bn_mp_div.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_sqr.lo: libtommath/bn_mp_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_sqr.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqr.Tpo -c -o libtommath/libhcrypto_la-bn_mp_sqr.lo `test -f 'libtommath/bn_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqr.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_sqr.c' object='libtommath/libhcrypto_la-bn_mp_sqr.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_div.lo `test -f 'libtommath/bn_mp_div.c' || echo '$(srcdir)/'`libtommath/bn_mp_div.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_sqr.lo `test -f 'libtommath/bn_mp_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqr.c
-libhcrypto_la-bn_mp_mod.lo: libtommath/bn_mp_mod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mod.Tpo -c -o libhcrypto_la-bn_mp_mod.lo `test -f 'libtommath/bn_mp_mod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mod.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mod.c' object='libhcrypto_la-bn_mp_mod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_div.lo: libtommath/bn_mp_div.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_div.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div.Tpo -c -o libtommath/libhcrypto_la-bn_mp_div.lo `test -f 'libtommath/bn_mp_div.c' || echo '$(srcdir)/'`libtommath/bn_mp_div.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_div.c' object='libtommath/libhcrypto_la-bn_mp_div.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mod.lo `test -f 'libtommath/bn_mp_mod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_div.lo `test -f 'libtommath/bn_mp_div.c' || echo '$(srcdir)/'`libtommath/bn_mp_div.c
-libhcrypto_la-bn_mp_add_d.lo: libtommath/bn_mp_add_d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_add_d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_add_d.Tpo -c -o libhcrypto_la-bn_mp_add_d.lo `test -f 'libtommath/bn_mp_add_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_add_d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_add_d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_add_d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_add_d.c' object='libhcrypto_la-bn_mp_add_d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mod.lo: libtommath/bn_mp_mod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mod.lo `test -f 'libtommath/bn_mp_mod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mod.c' object='libtommath/libhcrypto_la-bn_mp_mod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_add_d.lo `test -f 'libtommath/bn_mp_add_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_add_d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mod.lo `test -f 'libtommath/bn_mp_mod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod.c
-libhcrypto_la-bn_mp_sub_d.lo: libtommath/bn_mp_sub_d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_sub_d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Tpo -c -o libhcrypto_la-bn_mp_sub_d.lo `test -f 'libtommath/bn_mp_sub_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub_d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_sub_d.c' object='libhcrypto_la-bn_mp_sub_d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_add_d.lo: libtommath/bn_mp_add_d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_add_d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add_d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_add_d.lo `test -f 'libtommath/bn_mp_add_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_add_d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add_d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_add_d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_add_d.c' object='libtommath/libhcrypto_la-bn_mp_add_d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_sub_d.lo `test -f 'libtommath/bn_mp_sub_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub_d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_add_d.lo `test -f 'libtommath/bn_mp_add_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_add_d.c
-libhcrypto_la-bn_mp_mul_d.lo: libtommath/bn_mp_mul_d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mul_d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Tpo -c -o libhcrypto_la-bn_mp_mul_d.lo `test -f 'libtommath/bn_mp_mul_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mul_d.c' object='libhcrypto_la-bn_mp_mul_d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_sub_d.lo: libtommath/bn_mp_sub_d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_sub_d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_sub_d.lo `test -f 'libtommath/bn_mp_sub_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub_d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sub_d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_sub_d.c' object='libtommath/libhcrypto_la-bn_mp_sub_d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mul_d.lo `test -f 'libtommath/bn_mp_mul_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_sub_d.lo `test -f 'libtommath/bn_mp_sub_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_sub_d.c
-libhcrypto_la-bn_mp_div_d.lo: libtommath/bn_mp_div_d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_div_d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_div_d.Tpo -c -o libhcrypto_la-bn_mp_div_d.lo `test -f 'libtommath/bn_mp_div_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_div_d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_div_d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_div_d.c' object='libhcrypto_la-bn_mp_div_d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mul_d.lo: libtommath/bn_mp_mul_d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mul_d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mul_d.lo `test -f 'libtommath/bn_mp_mul_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mul_d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mul_d.c' object='libtommath/libhcrypto_la-bn_mp_mul_d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_div_d.lo `test -f 'libtommath/bn_mp_div_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mul_d.lo `test -f 'libtommath/bn_mp_mul_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mul_d.c
-libhcrypto_la-bn_mp_mod_d.lo: libtommath/bn_mp_mod_d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mod_d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Tpo -c -o libhcrypto_la-bn_mp_mod_d.lo `test -f 'libtommath/bn_mp_mod_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mod_d.c' object='libhcrypto_la-bn_mp_mod_d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_div_d.lo: libtommath/bn_mp_div_d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_div_d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_div_d.lo `test -f 'libtommath/bn_mp_div_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_div_d.c' object='libtommath/libhcrypto_la-bn_mp_div_d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mod_d.lo `test -f 'libtommath/bn_mp_mod_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_div_d.lo `test -f 'libtommath/bn_mp_div_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_d.c
-libhcrypto_la-bn_mp_expt_d.lo: libtommath/bn_mp_expt_d.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_expt_d.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Tpo -c -o libhcrypto_la-bn_mp_expt_d.lo `test -f 'libtommath/bn_mp_expt_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_expt_d.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_expt_d.c' object='libhcrypto_la-bn_mp_expt_d.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mod_d.lo: libtommath/bn_mp_mod_d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mod_d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mod_d.lo `test -f 'libtommath/bn_mp_mod_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mod_d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mod_d.c' object='libtommath/libhcrypto_la-bn_mp_mod_d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_expt_d.lo `test -f 'libtommath/bn_mp_expt_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_expt_d.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mod_d.lo `test -f 'libtommath/bn_mp_mod_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_mod_d.c
-libhcrypto_la-bn_mp_addmod.lo: libtommath/bn_mp_addmod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_addmod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_addmod.Tpo -c -o libhcrypto_la-bn_mp_addmod.lo `test -f 'libtommath/bn_mp_addmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_addmod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_addmod.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_addmod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_addmod.c' object='libhcrypto_la-bn_mp_addmod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_expt_d.lo: libtommath/bn_mp_expt_d.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_expt_d.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Tpo -c -o libtommath/libhcrypto_la-bn_mp_expt_d.lo `test -f 'libtommath/bn_mp_expt_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_expt_d.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_expt_d.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_expt_d.c' object='libtommath/libhcrypto_la-bn_mp_expt_d.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_addmod.lo `test -f 'libtommath/bn_mp_addmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_addmod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_expt_d.lo `test -f 'libtommath/bn_mp_expt_d.c' || echo '$(srcdir)/'`libtommath/bn_mp_expt_d.c
-libhcrypto_la-bn_mp_submod.lo: libtommath/bn_mp_submod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_submod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_submod.Tpo -c -o libhcrypto_la-bn_mp_submod.lo `test -f 'libtommath/bn_mp_submod.c' || echo '$(srcdir)/'`libtommath/bn_mp_submod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_submod.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_submod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_submod.c' object='libhcrypto_la-bn_mp_submod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_addmod.lo: libtommath/bn_mp_addmod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_addmod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_addmod.Tpo -c -o libtommath/libhcrypto_la-bn_mp_addmod.lo `test -f 'libtommath/bn_mp_addmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_addmod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_addmod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_addmod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_addmod.c' object='libtommath/libhcrypto_la-bn_mp_addmod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_submod.lo `test -f 'libtommath/bn_mp_submod.c' || echo '$(srcdir)/'`libtommath/bn_mp_submod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_addmod.lo `test -f 'libtommath/bn_mp_addmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_addmod.c
-libhcrypto_la-bn_mp_mulmod.lo: libtommath/bn_mp_mulmod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_mulmod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Tpo -c -o libhcrypto_la-bn_mp_mulmod.lo `test -f 'libtommath/bn_mp_mulmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mulmod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_mulmod.c' object='libhcrypto_la-bn_mp_mulmod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_submod.lo: libtommath/bn_mp_submod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_submod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_submod.Tpo -c -o libtommath/libhcrypto_la-bn_mp_submod.lo `test -f 'libtommath/bn_mp_submod.c' || echo '$(srcdir)/'`libtommath/bn_mp_submod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_submod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_submod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_submod.c' object='libtommath/libhcrypto_la-bn_mp_submod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_mulmod.lo `test -f 'libtommath/bn_mp_mulmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mulmod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_submod.lo `test -f 'libtommath/bn_mp_submod.c' || echo '$(srcdir)/'`libtommath/bn_mp_submod.c
-libhcrypto_la-bn_mp_sqrmod.lo: libtommath/bn_mp_sqrmod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_sqrmod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Tpo -c -o libhcrypto_la-bn_mp_sqrmod.lo `test -f 'libtommath/bn_mp_sqrmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrmod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_sqrmod.c' object='libhcrypto_la-bn_mp_sqrmod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_mulmod.lo: libtommath/bn_mp_mulmod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_mulmod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Tpo -c -o libtommath/libhcrypto_la-bn_mp_mulmod.lo `test -f 'libtommath/bn_mp_mulmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mulmod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_mulmod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_mulmod.c' object='libtommath/libhcrypto_la-bn_mp_mulmod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_sqrmod.lo `test -f 'libtommath/bn_mp_sqrmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrmod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_mulmod.lo `test -f 'libtommath/bn_mp_mulmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_mulmod.c
-libhcrypto_la-bn_mp_gcd.lo: libtommath/bn_mp_gcd.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_gcd.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_gcd.Tpo -c -o libhcrypto_la-bn_mp_gcd.lo `test -f 'libtommath/bn_mp_gcd.c' || echo '$(srcdir)/'`libtommath/bn_mp_gcd.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_gcd.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_gcd.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_gcd.c' object='libhcrypto_la-bn_mp_gcd.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_sqrmod.lo: libtommath/bn_mp_sqrmod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_sqrmod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Tpo -c -o libtommath/libhcrypto_la-bn_mp_sqrmod.lo `test -f 'libtommath/bn_mp_sqrmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrmod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrmod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_sqrmod.c' object='libtommath/libhcrypto_la-bn_mp_sqrmod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_gcd.lo `test -f 'libtommath/bn_mp_gcd.c' || echo '$(srcdir)/'`libtommath/bn_mp_gcd.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_sqrmod.lo `test -f 'libtommath/bn_mp_sqrmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrmod.c
-libhcrypto_la-bn_mp_lcm.lo: libtommath/bn_mp_lcm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_lcm.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_lcm.Tpo -c -o libhcrypto_la-bn_mp_lcm.lo `test -f 'libtommath/bn_mp_lcm.c' || echo '$(srcdir)/'`libtommath/bn_mp_lcm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_lcm.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_lcm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_lcm.c' object='libhcrypto_la-bn_mp_lcm.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_gcd.lo: libtommath/bn_mp_gcd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_gcd.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_gcd.Tpo -c -o libtommath/libhcrypto_la-bn_mp_gcd.lo `test -f 'libtommath/bn_mp_gcd.c' || echo '$(srcdir)/'`libtommath/bn_mp_gcd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_gcd.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_gcd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_gcd.c' object='libtommath/libhcrypto_la-bn_mp_gcd.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_lcm.lo `test -f 'libtommath/bn_mp_lcm.c' || echo '$(srcdir)/'`libtommath/bn_mp_lcm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_gcd.lo `test -f 'libtommath/bn_mp_gcd.c' || echo '$(srcdir)/'`libtommath/bn_mp_gcd.c
-libhcrypto_la-bn_fast_mp_invmod.lo: libtommath/bn_fast_mp_invmod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_fast_mp_invmod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Tpo -c -o libhcrypto_la-bn_fast_mp_invmod.lo `test -f 'libtommath/bn_fast_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_invmod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Tpo $(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_fast_mp_invmod.c' object='libhcrypto_la-bn_fast_mp_invmod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_lcm.lo: libtommath/bn_mp_lcm.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_lcm.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lcm.Tpo -c -o libtommath/libhcrypto_la-bn_mp_lcm.lo `test -f 'libtommath/bn_mp_lcm.c' || echo '$(srcdir)/'`libtommath/bn_mp_lcm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lcm.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_lcm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_lcm.c' object='libtommath/libhcrypto_la-bn_mp_lcm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_fast_mp_invmod.lo `test -f 'libtommath/bn_fast_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_invmod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_lcm.lo `test -f 'libtommath/bn_mp_lcm.c' || echo '$(srcdir)/'`libtommath/bn_mp_lcm.c
-libhcrypto_la-bn_mp_invmod.lo: libtommath/bn_mp_invmod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_invmod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_invmod.Tpo -c -o libhcrypto_la-bn_mp_invmod.lo `test -f 'libtommath/bn_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_invmod.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_invmod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_invmod.c' object='libhcrypto_la-bn_mp_invmod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_fast_mp_invmod.lo: libtommath/bn_fast_mp_invmod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_fast_mp_invmod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Tpo -c -o libtommath/libhcrypto_la-bn_fast_mp_invmod.lo `test -f 'libtommath/bn_fast_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_invmod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_invmod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_fast_mp_invmod.c' object='libtommath/libhcrypto_la-bn_fast_mp_invmod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_invmod.lo `test -f 'libtommath/bn_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_fast_mp_invmod.lo `test -f 'libtommath/bn_fast_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_invmod.c
-libhcrypto_la-bn_mp_reduce.lo: libtommath/bn_mp_reduce.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce.Tpo -c -o libhcrypto_la-bn_mp_reduce.lo `test -f 'libtommath/bn_mp_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce.c' object='libhcrypto_la-bn_mp_reduce.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_invmod.lo: libtommath/bn_mp_invmod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_invmod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod.Tpo -c -o libtommath/libhcrypto_la-bn_mp_invmod.lo `test -f 'libtommath/bn_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_invmod.c' object='libtommath/libhcrypto_la-bn_mp_invmod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce.lo `test -f 'libtommath/bn_mp_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_invmod.lo `test -f 'libtommath/bn_mp_invmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod.c
-libhcrypto_la-bn_mp_montgomery_setup.lo: libtommath/bn_mp_montgomery_setup.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_montgomery_setup.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Tpo -c -o libhcrypto_la-bn_mp_montgomery_setup.lo `test -f 'libtommath/bn_mp_montgomery_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_setup.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_montgomery_setup.c' object='libhcrypto_la-bn_mp_montgomery_setup.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce.lo: libtommath/bn_mp_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce.lo `test -f 'libtommath/bn_mp_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce.c' object='libtommath/libhcrypto_la-bn_mp_reduce.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_montgomery_setup.lo `test -f 'libtommath/bn_mp_montgomery_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_setup.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce.lo `test -f 'libtommath/bn_mp_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce.c
-libhcrypto_la-bn_fast_mp_montgomery_reduce.lo: libtommath/bn_fast_mp_montgomery_reduce.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_fast_mp_montgomery_reduce.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Tpo -c -o libhcrypto_la-bn_fast_mp_montgomery_reduce.lo `test -f 'libtommath/bn_fast_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_montgomery_reduce.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Tpo $(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_fast_mp_montgomery_reduce.c' object='libhcrypto_la-bn_fast_mp_montgomery_reduce.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_montgomery_setup.lo: libtommath/bn_mp_montgomery_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_montgomery_setup.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Tpo -c -o libtommath/libhcrypto_la-bn_mp_montgomery_setup.lo `test -f 'libtommath/bn_mp_montgomery_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_setup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_montgomery_setup.c' object='libtommath/libhcrypto_la-bn_mp_montgomery_setup.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_fast_mp_montgomery_reduce.lo `test -f 'libtommath/bn_fast_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_montgomery_reduce.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_montgomery_setup.lo `test -f 'libtommath/bn_mp_montgomery_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_setup.c
-libhcrypto_la-bn_mp_montgomery_reduce.lo: libtommath/bn_mp_montgomery_reduce.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_montgomery_reduce.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Tpo -c -o libhcrypto_la-bn_mp_montgomery_reduce.lo `test -f 'libtommath/bn_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_reduce.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_montgomery_reduce.c' object='libhcrypto_la-bn_mp_montgomery_reduce.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_fast_mp_montgomery_reduce.lo: libtommath/bn_fast_mp_montgomery_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_fast_mp_montgomery_reduce.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Tpo -c -o libtommath/libhcrypto_la-bn_fast_mp_montgomery_reduce.lo `test -f 'libtommath/bn_fast_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_montgomery_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_fast_mp_montgomery_reduce.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_fast_mp_montgomery_reduce.c' object='libtommath/libhcrypto_la-bn_fast_mp_montgomery_reduce.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_montgomery_reduce.lo `test -f 'libtommath/bn_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_reduce.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_fast_mp_montgomery_reduce.lo `test -f 'libtommath/bn_fast_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_fast_mp_montgomery_reduce.c
-libhcrypto_la-bn_mp_exptmod_fast.lo: libtommath/bn_mp_exptmod_fast.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_exptmod_fast.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Tpo -c -o libhcrypto_la-bn_mp_exptmod_fast.lo `test -f 'libtommath/bn_mp_exptmod_fast.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod_fast.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_exptmod_fast.c' object='libhcrypto_la-bn_mp_exptmod_fast.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_montgomery_reduce.lo: libtommath/bn_mp_montgomery_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_montgomery_reduce.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Tpo -c -o libtommath/libhcrypto_la-bn_mp_montgomery_reduce.lo `test -f 'libtommath/bn_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_reduce.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_montgomery_reduce.c' object='libtommath/libhcrypto_la-bn_mp_montgomery_reduce.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_exptmod_fast.lo `test -f 'libtommath/bn_mp_exptmod_fast.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod_fast.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_montgomery_reduce.lo `test -f 'libtommath/bn_mp_montgomery_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_reduce.c
-libhcrypto_la-bn_mp_exptmod.lo: libtommath/bn_mp_exptmod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_exptmod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Tpo -c -o libhcrypto_la-bn_mp_exptmod.lo `test -f 'libtommath/bn_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_exptmod.c' object='libhcrypto_la-bn_mp_exptmod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_exptmod_fast.lo: libtommath/bn_mp_exptmod_fast.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_exptmod_fast.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Tpo -c -o libtommath/libhcrypto_la-bn_mp_exptmod_fast.lo `test -f 'libtommath/bn_mp_exptmod_fast.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod_fast.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod_fast.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_exptmod_fast.c' object='libtommath/libhcrypto_la-bn_mp_exptmod_fast.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_exptmod.lo `test -f 'libtommath/bn_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_exptmod_fast.lo `test -f 'libtommath/bn_mp_exptmod_fast.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod_fast.c
-libhcrypto_la-bn_mp_2expt.lo: libtommath/bn_mp_2expt.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_2expt.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_2expt.Tpo -c -o libhcrypto_la-bn_mp_2expt.lo `test -f 'libtommath/bn_mp_2expt.c' || echo '$(srcdir)/'`libtommath/bn_mp_2expt.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_2expt.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_2expt.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_2expt.c' object='libhcrypto_la-bn_mp_2expt.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_exptmod.lo: libtommath/bn_mp_exptmod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_exptmod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Tpo -c -o libtommath/libhcrypto_la-bn_mp_exptmod.lo `test -f 'libtommath/bn_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exptmod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_exptmod.c' object='libtommath/libhcrypto_la-bn_mp_exptmod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_2expt.lo `test -f 'libtommath/bn_mp_2expt.c' || echo '$(srcdir)/'`libtommath/bn_mp_2expt.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_exptmod.lo `test -f 'libtommath/bn_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_mp_exptmod.c
-libhcrypto_la-bn_mp_n_root.lo: libtommath/bn_mp_n_root.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_n_root.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_n_root.Tpo -c -o libhcrypto_la-bn_mp_n_root.lo `test -f 'libtommath/bn_mp_n_root.c' || echo '$(srcdir)/'`libtommath/bn_mp_n_root.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_n_root.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_n_root.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_n_root.c' object='libhcrypto_la-bn_mp_n_root.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_2expt.lo: libtommath/bn_mp_2expt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_2expt.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_2expt.Tpo -c -o libtommath/libhcrypto_la-bn_mp_2expt.lo `test -f 'libtommath/bn_mp_2expt.c' || echo '$(srcdir)/'`libtommath/bn_mp_2expt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_2expt.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_2expt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_2expt.c' object='libtommath/libhcrypto_la-bn_mp_2expt.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_n_root.lo `test -f 'libtommath/bn_mp_n_root.c' || echo '$(srcdir)/'`libtommath/bn_mp_n_root.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_2expt.lo `test -f 'libtommath/bn_mp_2expt.c' || echo '$(srcdir)/'`libtommath/bn_mp_2expt.c
-libhcrypto_la-bn_mp_jacobi.lo: libtommath/bn_mp_jacobi.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_jacobi.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Tpo -c -o libhcrypto_la-bn_mp_jacobi.lo `test -f 'libtommath/bn_mp_jacobi.c' || echo '$(srcdir)/'`libtommath/bn_mp_jacobi.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_jacobi.c' object='libhcrypto_la-bn_mp_jacobi.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_n_root.lo: libtommath/bn_mp_n_root.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_n_root.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_n_root.Tpo -c -o libtommath/libhcrypto_la-bn_mp_n_root.lo `test -f 'libtommath/bn_mp_n_root.c' || echo '$(srcdir)/'`libtommath/bn_mp_n_root.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_n_root.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_n_root.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_n_root.c' object='libtommath/libhcrypto_la-bn_mp_n_root.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_jacobi.lo `test -f 'libtommath/bn_mp_jacobi.c' || echo '$(srcdir)/'`libtommath/bn_mp_jacobi.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_n_root.lo `test -f 'libtommath/bn_mp_n_root.c' || echo '$(srcdir)/'`libtommath/bn_mp_n_root.c
-libhcrypto_la-bn_reverse.lo: libtommath/bn_reverse.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_reverse.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_reverse.Tpo -c -o libhcrypto_la-bn_reverse.lo `test -f 'libtommath/bn_reverse.c' || echo '$(srcdir)/'`libtommath/bn_reverse.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_reverse.Tpo $(DEPDIR)/libhcrypto_la-bn_reverse.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_reverse.c' object='libhcrypto_la-bn_reverse.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_jacobi.lo: libtommath/bn_mp_jacobi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_jacobi.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Tpo -c -o libtommath/libhcrypto_la-bn_mp_jacobi.lo `test -f 'libtommath/bn_mp_jacobi.c' || echo '$(srcdir)/'`libtommath/bn_mp_jacobi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_jacobi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_jacobi.c' object='libtommath/libhcrypto_la-bn_mp_jacobi.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_reverse.lo `test -f 'libtommath/bn_reverse.c' || echo '$(srcdir)/'`libtommath/bn_reverse.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_jacobi.lo `test -f 'libtommath/bn_mp_jacobi.c' || echo '$(srcdir)/'`libtommath/bn_mp_jacobi.c
-libhcrypto_la-bn_mp_count_bits.lo: libtommath/bn_mp_count_bits.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_count_bits.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Tpo -c -o libhcrypto_la-bn_mp_count_bits.lo `test -f 'libtommath/bn_mp_count_bits.c' || echo '$(srcdir)/'`libtommath/bn_mp_count_bits.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_count_bits.c' object='libhcrypto_la-bn_mp_count_bits.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_reverse.lo: libtommath/bn_reverse.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_reverse.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_reverse.Tpo -c -o libtommath/libhcrypto_la-bn_reverse.lo `test -f 'libtommath/bn_reverse.c' || echo '$(srcdir)/'`libtommath/bn_reverse.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_reverse.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_reverse.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_reverse.c' object='libtommath/libhcrypto_la-bn_reverse.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_count_bits.lo `test -f 'libtommath/bn_mp_count_bits.c' || echo '$(srcdir)/'`libtommath/bn_mp_count_bits.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_reverse.lo `test -f 'libtommath/bn_reverse.c' || echo '$(srcdir)/'`libtommath/bn_reverse.c
-libhcrypto_la-bn_mp_read_unsigned_bin.lo: libtommath/bn_mp_read_unsigned_bin.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_read_unsigned_bin.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Tpo -c -o libhcrypto_la-bn_mp_read_unsigned_bin.lo `test -f 'libtommath/bn_mp_read_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_unsigned_bin.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_read_unsigned_bin.c' object='libhcrypto_la-bn_mp_read_unsigned_bin.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_count_bits.lo: libtommath/bn_mp_count_bits.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_count_bits.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Tpo -c -o libtommath/libhcrypto_la-bn_mp_count_bits.lo `test -f 'libtommath/bn_mp_count_bits.c' || echo '$(srcdir)/'`libtommath/bn_mp_count_bits.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_count_bits.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_count_bits.c' object='libtommath/libhcrypto_la-bn_mp_count_bits.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_read_unsigned_bin.lo `test -f 'libtommath/bn_mp_read_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_unsigned_bin.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_count_bits.lo `test -f 'libtommath/bn_mp_count_bits.c' || echo '$(srcdir)/'`libtommath/bn_mp_count_bits.c
-libhcrypto_la-bn_mp_read_signed_bin.lo: libtommath/bn_mp_read_signed_bin.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_read_signed_bin.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Tpo -c -o libhcrypto_la-bn_mp_read_signed_bin.lo `test -f 'libtommath/bn_mp_read_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_signed_bin.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_read_signed_bin.c' object='libhcrypto_la-bn_mp_read_signed_bin.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_read_unsigned_bin.lo: libtommath/bn_mp_read_unsigned_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_read_unsigned_bin.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Tpo -c -o libtommath/libhcrypto_la-bn_mp_read_unsigned_bin.lo `test -f 'libtommath/bn_mp_read_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_unsigned_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_unsigned_bin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_read_unsigned_bin.c' object='libtommath/libhcrypto_la-bn_mp_read_unsigned_bin.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_read_signed_bin.lo `test -f 'libtommath/bn_mp_read_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_signed_bin.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_read_unsigned_bin.lo `test -f 'libtommath/bn_mp_read_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_unsigned_bin.c
-libhcrypto_la-bn_mp_to_unsigned_bin.lo: libtommath/bn_mp_to_unsigned_bin.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_to_unsigned_bin.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Tpo -c -o libhcrypto_la-bn_mp_to_unsigned_bin.lo `test -f 'libtommath/bn_mp_to_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_to_unsigned_bin.c' object='libhcrypto_la-bn_mp_to_unsigned_bin.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_read_signed_bin.lo: libtommath/bn_mp_read_signed_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_read_signed_bin.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Tpo -c -o libtommath/libhcrypto_la-bn_mp_read_signed_bin.lo `test -f 'libtommath/bn_mp_read_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_signed_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_signed_bin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_read_signed_bin.c' object='libtommath/libhcrypto_la-bn_mp_read_signed_bin.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_to_unsigned_bin.lo `test -f 'libtommath/bn_mp_to_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_read_signed_bin.lo `test -f 'libtommath/bn_mp_read_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_signed_bin.c
-libhcrypto_la-bn_mp_to_signed_bin.lo: libtommath/bn_mp_to_signed_bin.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_to_signed_bin.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Tpo -c -o libhcrypto_la-bn_mp_to_signed_bin.lo `test -f 'libtommath/bn_mp_to_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_to_signed_bin.c' object='libhcrypto_la-bn_mp_to_signed_bin.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_to_unsigned_bin.lo: libtommath/bn_mp_to_unsigned_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_to_unsigned_bin.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Tpo -c -o libtommath/libhcrypto_la-bn_mp_to_unsigned_bin.lo `test -f 'libtommath/bn_mp_to_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_to_unsigned_bin.c' object='libtommath/libhcrypto_la-bn_mp_to_unsigned_bin.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_to_signed_bin.lo `test -f 'libtommath/bn_mp_to_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_to_unsigned_bin.lo `test -f 'libtommath/bn_mp_to_unsigned_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin.c
-libhcrypto_la-bn_mp_unsigned_bin_size.lo: libtommath/bn_mp_unsigned_bin_size.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_unsigned_bin_size.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Tpo -c -o libhcrypto_la-bn_mp_unsigned_bin_size.lo `test -f 'libtommath/bn_mp_unsigned_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_unsigned_bin_size.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_unsigned_bin_size.c' object='libhcrypto_la-bn_mp_unsigned_bin_size.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_to_signed_bin.lo: libtommath/bn_mp_to_signed_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_to_signed_bin.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Tpo -c -o libtommath/libhcrypto_la-bn_mp_to_signed_bin.lo `test -f 'libtommath/bn_mp_to_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_to_signed_bin.c' object='libtommath/libhcrypto_la-bn_mp_to_signed_bin.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_unsigned_bin_size.lo `test -f 'libtommath/bn_mp_unsigned_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_unsigned_bin_size.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_to_signed_bin.lo `test -f 'libtommath/bn_mp_to_signed_bin.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin.c
-libhcrypto_la-bn_mp_signed_bin_size.lo: libtommath/bn_mp_signed_bin_size.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_signed_bin_size.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Tpo -c -o libhcrypto_la-bn_mp_signed_bin_size.lo `test -f 'libtommath/bn_mp_signed_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_signed_bin_size.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_signed_bin_size.c' object='libhcrypto_la-bn_mp_signed_bin_size.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_unsigned_bin_size.lo: libtommath/bn_mp_unsigned_bin_size.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_unsigned_bin_size.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Tpo -c -o libtommath/libhcrypto_la-bn_mp_unsigned_bin_size.lo `test -f 'libtommath/bn_mp_unsigned_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_unsigned_bin_size.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_unsigned_bin_size.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_unsigned_bin_size.c' object='libtommath/libhcrypto_la-bn_mp_unsigned_bin_size.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_signed_bin_size.lo `test -f 'libtommath/bn_mp_signed_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_signed_bin_size.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_unsigned_bin_size.lo `test -f 'libtommath/bn_mp_unsigned_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_unsigned_bin_size.c
-libhcrypto_la-bn_mp_xor.lo: libtommath/bn_mp_xor.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_xor.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_xor.Tpo -c -o libhcrypto_la-bn_mp_xor.lo `test -f 'libtommath/bn_mp_xor.c' || echo '$(srcdir)/'`libtommath/bn_mp_xor.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_xor.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_xor.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_xor.c' object='libhcrypto_la-bn_mp_xor.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_signed_bin_size.lo: libtommath/bn_mp_signed_bin_size.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_signed_bin_size.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Tpo -c -o libtommath/libhcrypto_la-bn_mp_signed_bin_size.lo `test -f 'libtommath/bn_mp_signed_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_signed_bin_size.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_signed_bin_size.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_signed_bin_size.c' object='libtommath/libhcrypto_la-bn_mp_signed_bin_size.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_xor.lo `test -f 'libtommath/bn_mp_xor.c' || echo '$(srcdir)/'`libtommath/bn_mp_xor.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_signed_bin_size.lo `test -f 'libtommath/bn_mp_signed_bin_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_signed_bin_size.c
-libhcrypto_la-bn_mp_and.lo: libtommath/bn_mp_and.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_and.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_and.Tpo -c -o libhcrypto_la-bn_mp_and.lo `test -f 'libtommath/bn_mp_and.c' || echo '$(srcdir)/'`libtommath/bn_mp_and.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_and.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_and.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_and.c' object='libhcrypto_la-bn_mp_and.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_xor.lo: libtommath/bn_mp_xor.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_xor.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_xor.Tpo -c -o libtommath/libhcrypto_la-bn_mp_xor.lo `test -f 'libtommath/bn_mp_xor.c' || echo '$(srcdir)/'`libtommath/bn_mp_xor.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_xor.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_xor.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_xor.c' object='libtommath/libhcrypto_la-bn_mp_xor.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_and.lo `test -f 'libtommath/bn_mp_and.c' || echo '$(srcdir)/'`libtommath/bn_mp_and.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_xor.lo `test -f 'libtommath/bn_mp_xor.c' || echo '$(srcdir)/'`libtommath/bn_mp_xor.c
-libhcrypto_la-bn_mp_or.lo: libtommath/bn_mp_or.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_or.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_or.Tpo -c -o libhcrypto_la-bn_mp_or.lo `test -f 'libtommath/bn_mp_or.c' || echo '$(srcdir)/'`libtommath/bn_mp_or.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_or.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_or.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_or.c' object='libhcrypto_la-bn_mp_or.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_and.lo: libtommath/bn_mp_and.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_and.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_and.Tpo -c -o libtommath/libhcrypto_la-bn_mp_and.lo `test -f 'libtommath/bn_mp_and.c' || echo '$(srcdir)/'`libtommath/bn_mp_and.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_and.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_and.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_and.c' object='libtommath/libhcrypto_la-bn_mp_and.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_or.lo `test -f 'libtommath/bn_mp_or.c' || echo '$(srcdir)/'`libtommath/bn_mp_or.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_and.lo `test -f 'libtommath/bn_mp_and.c' || echo '$(srcdir)/'`libtommath/bn_mp_and.c
-libhcrypto_la-bn_mp_rand.lo: libtommath/bn_mp_rand.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_rand.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_rand.Tpo -c -o libhcrypto_la-bn_mp_rand.lo `test -f 'libtommath/bn_mp_rand.c' || echo '$(srcdir)/'`libtommath/bn_mp_rand.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_rand.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_rand.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_rand.c' object='libhcrypto_la-bn_mp_rand.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_or.lo: libtommath/bn_mp_or.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_or.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_or.Tpo -c -o libtommath/libhcrypto_la-bn_mp_or.lo `test -f 'libtommath/bn_mp_or.c' || echo '$(srcdir)/'`libtommath/bn_mp_or.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_or.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_or.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_or.c' object='libtommath/libhcrypto_la-bn_mp_or.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_rand.lo `test -f 'libtommath/bn_mp_rand.c' || echo '$(srcdir)/'`libtommath/bn_mp_rand.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_or.lo `test -f 'libtommath/bn_mp_or.c' || echo '$(srcdir)/'`libtommath/bn_mp_or.c
-libhcrypto_la-bn_mp_montgomery_calc_normalization.lo: libtommath/bn_mp_montgomery_calc_normalization.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_montgomery_calc_normalization.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Tpo -c -o libhcrypto_la-bn_mp_montgomery_calc_normalization.lo `test -f 'libtommath/bn_mp_montgomery_calc_normalization.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_calc_normalization.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_montgomery_calc_normalization.c' object='libhcrypto_la-bn_mp_montgomery_calc_normalization.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_rand.lo: libtommath/bn_mp_rand.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_rand.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rand.Tpo -c -o libtommath/libhcrypto_la-bn_mp_rand.lo `test -f 'libtommath/bn_mp_rand.c' || echo '$(srcdir)/'`libtommath/bn_mp_rand.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rand.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_rand.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_rand.c' object='libtommath/libhcrypto_la-bn_mp_rand.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_montgomery_calc_normalization.lo `test -f 'libtommath/bn_mp_montgomery_calc_normalization.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_calc_normalization.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_rand.lo `test -f 'libtommath/bn_mp_rand.c' || echo '$(srcdir)/'`libtommath/bn_mp_rand.c
-libhcrypto_la-bn_mp_prime_is_divisible.lo: libtommath/bn_mp_prime_is_divisible.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_prime_is_divisible.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Tpo -c -o libhcrypto_la-bn_mp_prime_is_divisible.lo `test -f 'libtommath/bn_mp_prime_is_divisible.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_divisible.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_prime_is_divisible.c' object='libhcrypto_la-bn_mp_prime_is_divisible.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_montgomery_calc_normalization.lo: libtommath/bn_mp_montgomery_calc_normalization.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_montgomery_calc_normalization.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Tpo -c -o libtommath/libhcrypto_la-bn_mp_montgomery_calc_normalization.lo `test -f 'libtommath/bn_mp_montgomery_calc_normalization.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_calc_normalization.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_montgomery_calc_normalization.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_montgomery_calc_normalization.c' object='libtommath/libhcrypto_la-bn_mp_montgomery_calc_normalization.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_prime_is_divisible.lo `test -f 'libtommath/bn_mp_prime_is_divisible.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_divisible.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_montgomery_calc_normalization.lo `test -f 'libtommath/bn_mp_montgomery_calc_normalization.c' || echo '$(srcdir)/'`libtommath/bn_mp_montgomery_calc_normalization.c
-libhcrypto_la-bn_prime_tab.lo: libtommath/bn_prime_tab.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_prime_tab.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_prime_tab.Tpo -c -o libhcrypto_la-bn_prime_tab.lo `test -f 'libtommath/bn_prime_tab.c' || echo '$(srcdir)/'`libtommath/bn_prime_tab.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_prime_tab.Tpo $(DEPDIR)/libhcrypto_la-bn_prime_tab.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_prime_tab.c' object='libhcrypto_la-bn_prime_tab.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_prime_is_divisible.lo: libtommath/bn_mp_prime_is_divisible.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_prime_is_divisible.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Tpo -c -o libtommath/libhcrypto_la-bn_mp_prime_is_divisible.lo `test -f 'libtommath/bn_mp_prime_is_divisible.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_divisible.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_divisible.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_prime_is_divisible.c' object='libtommath/libhcrypto_la-bn_mp_prime_is_divisible.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_prime_tab.lo `test -f 'libtommath/bn_prime_tab.c' || echo '$(srcdir)/'`libtommath/bn_prime_tab.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_prime_is_divisible.lo `test -f 'libtommath/bn_mp_prime_is_divisible.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_divisible.c
-libhcrypto_la-bn_mp_prime_fermat.lo: libtommath/bn_mp_prime_fermat.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_prime_fermat.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Tpo -c -o libhcrypto_la-bn_mp_prime_fermat.lo `test -f 'libtommath/bn_mp_prime_fermat.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_fermat.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_prime_fermat.c' object='libhcrypto_la-bn_mp_prime_fermat.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_prime_tab.lo: libtommath/bn_prime_tab.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_prime_tab.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_prime_tab.Tpo -c -o libtommath/libhcrypto_la-bn_prime_tab.lo `test -f 'libtommath/bn_prime_tab.c' || echo '$(srcdir)/'`libtommath/bn_prime_tab.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_prime_tab.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_prime_tab.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_prime_tab.c' object='libtommath/libhcrypto_la-bn_prime_tab.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_prime_fermat.lo `test -f 'libtommath/bn_mp_prime_fermat.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_fermat.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_prime_tab.lo `test -f 'libtommath/bn_prime_tab.c' || echo '$(srcdir)/'`libtommath/bn_prime_tab.c
-libhcrypto_la-bn_mp_prime_miller_rabin.lo: libtommath/bn_mp_prime_miller_rabin.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_prime_miller_rabin.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Tpo -c -o libhcrypto_la-bn_mp_prime_miller_rabin.lo `test -f 'libtommath/bn_mp_prime_miller_rabin.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_miller_rabin.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_prime_miller_rabin.c' object='libhcrypto_la-bn_mp_prime_miller_rabin.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_prime_fermat.lo: libtommath/bn_mp_prime_fermat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_prime_fermat.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Tpo -c -o libtommath/libhcrypto_la-bn_mp_prime_fermat.lo `test -f 'libtommath/bn_mp_prime_fermat.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_fermat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_fermat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_prime_fermat.c' object='libtommath/libhcrypto_la-bn_mp_prime_fermat.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_prime_miller_rabin.lo `test -f 'libtommath/bn_mp_prime_miller_rabin.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_miller_rabin.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_prime_fermat.lo `test -f 'libtommath/bn_mp_prime_fermat.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_fermat.c
-libhcrypto_la-bn_mp_prime_is_prime.lo: libtommath/bn_mp_prime_is_prime.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_prime_is_prime.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Tpo -c -o libhcrypto_la-bn_mp_prime_is_prime.lo `test -f 'libtommath/bn_mp_prime_is_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_prime.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_prime_is_prime.c' object='libhcrypto_la-bn_mp_prime_is_prime.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_prime_miller_rabin.lo: libtommath/bn_mp_prime_miller_rabin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_prime_miller_rabin.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Tpo -c -o libtommath/libhcrypto_la-bn_mp_prime_miller_rabin.lo `test -f 'libtommath/bn_mp_prime_miller_rabin.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_miller_rabin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_miller_rabin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_prime_miller_rabin.c' object='libtommath/libhcrypto_la-bn_mp_prime_miller_rabin.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_prime_is_prime.lo `test -f 'libtommath/bn_mp_prime_is_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_prime.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_prime_miller_rabin.lo `test -f 'libtommath/bn_mp_prime_miller_rabin.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_miller_rabin.c
-libhcrypto_la-bn_mp_prime_next_prime.lo: libtommath/bn_mp_prime_next_prime.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_prime_next_prime.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Tpo -c -o libhcrypto_la-bn_mp_prime_next_prime.lo `test -f 'libtommath/bn_mp_prime_next_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_next_prime.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_prime_next_prime.c' object='libhcrypto_la-bn_mp_prime_next_prime.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_prime_is_prime.lo: libtommath/bn_mp_prime_is_prime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_prime_is_prime.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Tpo -c -o libtommath/libhcrypto_la-bn_mp_prime_is_prime.lo `test -f 'libtommath/bn_mp_prime_is_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_prime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_is_prime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_prime_is_prime.c' object='libtommath/libhcrypto_la-bn_mp_prime_is_prime.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_prime_next_prime.lo `test -f 'libtommath/bn_mp_prime_next_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_next_prime.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_prime_is_prime.lo `test -f 'libtommath/bn_mp_prime_is_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_is_prime.c
-libhcrypto_la-bn_mp_find_prime.lo: libtommath/bn_mp_find_prime.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_find_prime.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Tpo -c -o libhcrypto_la-bn_mp_find_prime.lo `test -f 'libtommath/bn_mp_find_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_find_prime.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_find_prime.c' object='libhcrypto_la-bn_mp_find_prime.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_prime_next_prime.lo: libtommath/bn_mp_prime_next_prime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_prime_next_prime.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Tpo -c -o libtommath/libhcrypto_la-bn_mp_prime_next_prime.lo `test -f 'libtommath/bn_mp_prime_next_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_next_prime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_next_prime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_prime_next_prime.c' object='libtommath/libhcrypto_la-bn_mp_prime_next_prime.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_find_prime.lo `test -f 'libtommath/bn_mp_find_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_find_prime.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_prime_next_prime.lo `test -f 'libtommath/bn_mp_prime_next_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_next_prime.c
-libhcrypto_la-bn_mp_isprime.lo: libtommath/bn_mp_isprime.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_isprime.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_isprime.Tpo -c -o libhcrypto_la-bn_mp_isprime.lo `test -f 'libtommath/bn_mp_isprime.c' || echo '$(srcdir)/'`libtommath/bn_mp_isprime.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_isprime.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_isprime.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_isprime.c' object='libhcrypto_la-bn_mp_isprime.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_find_prime.lo: libtommath/bn_mp_find_prime.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_find_prime.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Tpo -c -o libtommath/libhcrypto_la-bn_mp_find_prime.lo `test -f 'libtommath/bn_mp_find_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_find_prime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_find_prime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_find_prime.c' object='libtommath/libhcrypto_la-bn_mp_find_prime.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_isprime.lo `test -f 'libtommath/bn_mp_isprime.c' || echo '$(srcdir)/'`libtommath/bn_mp_isprime.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_find_prime.lo `test -f 'libtommath/bn_mp_find_prime.c' || echo '$(srcdir)/'`libtommath/bn_mp_find_prime.c
-libhcrypto_la-bn_mp_dr_reduce.lo: libtommath/bn_mp_dr_reduce.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_dr_reduce.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Tpo -c -o libhcrypto_la-bn_mp_dr_reduce.lo `test -f 'libtommath/bn_mp_dr_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_reduce.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_dr_reduce.c' object='libhcrypto_la-bn_mp_dr_reduce.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_dr_reduce.lo: libtommath/bn_mp_dr_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_dr_reduce.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Tpo -c -o libtommath/libhcrypto_la-bn_mp_dr_reduce.lo `test -f 'libtommath/bn_mp_dr_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_reduce.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_reduce.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_dr_reduce.c' object='libtommath/libhcrypto_la-bn_mp_dr_reduce.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_dr_reduce.lo `test -f 'libtommath/bn_mp_dr_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_reduce.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_dr_reduce.lo `test -f 'libtommath/bn_mp_dr_reduce.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_reduce.c
-libhcrypto_la-bn_mp_dr_is_modulus.lo: libtommath/bn_mp_dr_is_modulus.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_dr_is_modulus.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Tpo -c -o libhcrypto_la-bn_mp_dr_is_modulus.lo `test -f 'libtommath/bn_mp_dr_is_modulus.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_is_modulus.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_dr_is_modulus.c' object='libhcrypto_la-bn_mp_dr_is_modulus.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_dr_is_modulus.lo: libtommath/bn_mp_dr_is_modulus.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_dr_is_modulus.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Tpo -c -o libtommath/libhcrypto_la-bn_mp_dr_is_modulus.lo `test -f 'libtommath/bn_mp_dr_is_modulus.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_is_modulus.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_is_modulus.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_dr_is_modulus.c' object='libtommath/libhcrypto_la-bn_mp_dr_is_modulus.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_dr_is_modulus.lo `test -f 'libtommath/bn_mp_dr_is_modulus.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_is_modulus.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_dr_is_modulus.lo `test -f 'libtommath/bn_mp_dr_is_modulus.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_is_modulus.c
-libhcrypto_la-bn_mp_dr_setup.lo: libtommath/bn_mp_dr_setup.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_dr_setup.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Tpo -c -o libhcrypto_la-bn_mp_dr_setup.lo `test -f 'libtommath/bn_mp_dr_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_setup.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_dr_setup.c' object='libhcrypto_la-bn_mp_dr_setup.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_dr_setup.lo: libtommath/bn_mp_dr_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_dr_setup.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Tpo -c -o libtommath/libhcrypto_la-bn_mp_dr_setup.lo `test -f 'libtommath/bn_mp_dr_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_dr_setup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_dr_setup.c' object='libtommath/libhcrypto_la-bn_mp_dr_setup.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_dr_setup.lo `test -f 'libtommath/bn_mp_dr_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_setup.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_dr_setup.lo `test -f 'libtommath/bn_mp_dr_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_dr_setup.c
-libhcrypto_la-bn_mp_reduce_setup.lo: libtommath/bn_mp_reduce_setup.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce_setup.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Tpo -c -o libhcrypto_la-bn_mp_reduce_setup.lo `test -f 'libtommath/bn_mp_reduce_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_setup.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce_setup.c' object='libhcrypto_la-bn_mp_reduce_setup.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce_setup.lo: libtommath/bn_mp_reduce_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce_setup.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce_setup.lo `test -f 'libtommath/bn_mp_reduce_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_setup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce_setup.c' object='libtommath/libhcrypto_la-bn_mp_reduce_setup.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce_setup.lo `test -f 'libtommath/bn_mp_reduce_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_setup.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce_setup.lo `test -f 'libtommath/bn_mp_reduce_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_setup.c
-libhcrypto_la-bn_mp_toom_mul.lo: libtommath/bn_mp_toom_mul.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_toom_mul.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Tpo -c -o libhcrypto_la-bn_mp_toom_mul.lo `test -f 'libtommath/bn_mp_toom_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_mul.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_toom_mul.c' object='libhcrypto_la-bn_mp_toom_mul.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_toom_mul.lo: libtommath/bn_mp_toom_mul.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_toom_mul.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Tpo -c -o libtommath/libhcrypto_la-bn_mp_toom_mul.lo `test -f 'libtommath/bn_mp_toom_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_mul.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_mul.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_toom_mul.c' object='libtommath/libhcrypto_la-bn_mp_toom_mul.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_toom_mul.lo `test -f 'libtommath/bn_mp_toom_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_mul.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_toom_mul.lo `test -f 'libtommath/bn_mp_toom_mul.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_mul.c
-libhcrypto_la-bn_mp_toom_sqr.lo: libtommath/bn_mp_toom_sqr.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_toom_sqr.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Tpo -c -o libhcrypto_la-bn_mp_toom_sqr.lo `test -f 'libtommath/bn_mp_toom_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_sqr.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_toom_sqr.c' object='libhcrypto_la-bn_mp_toom_sqr.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_toom_sqr.lo: libtommath/bn_mp_toom_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_toom_sqr.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Tpo -c -o libtommath/libhcrypto_la-bn_mp_toom_sqr.lo `test -f 'libtommath/bn_mp_toom_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_sqr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toom_sqr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_toom_sqr.c' object='libtommath/libhcrypto_la-bn_mp_toom_sqr.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_toom_sqr.lo `test -f 'libtommath/bn_mp_toom_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_sqr.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_toom_sqr.lo `test -f 'libtommath/bn_mp_toom_sqr.c' || echo '$(srcdir)/'`libtommath/bn_mp_toom_sqr.c
-libhcrypto_la-bn_mp_div_3.lo: libtommath/bn_mp_div_3.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_div_3.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_div_3.Tpo -c -o libhcrypto_la-bn_mp_div_3.lo `test -f 'libtommath/bn_mp_div_3.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_3.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_div_3.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_div_3.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_div_3.c' object='libhcrypto_la-bn_mp_div_3.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_div_3.lo: libtommath/bn_mp_div_3.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_div_3.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_3.Tpo -c -o libtommath/libhcrypto_la-bn_mp_div_3.lo `test -f 'libtommath/bn_mp_div_3.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_3.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_div_3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_div_3.c' object='libtommath/libhcrypto_la-bn_mp_div_3.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_div_3.lo `test -f 'libtommath/bn_mp_div_3.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_3.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_div_3.lo `test -f 'libtommath/bn_mp_div_3.c' || echo '$(srcdir)/'`libtommath/bn_mp_div_3.c
-libhcrypto_la-bn_s_mp_exptmod.lo: libtommath/bn_s_mp_exptmod.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_s_mp_exptmod.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Tpo -c -o libhcrypto_la-bn_s_mp_exptmod.lo `test -f 'libtommath/bn_s_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_exptmod.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Tpo $(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_s_mp_exptmod.c' object='libhcrypto_la-bn_s_mp_exptmod.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_s_mp_exptmod.lo: libtommath/bn_s_mp_exptmod.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_s_mp_exptmod.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Tpo -c -o libtommath/libhcrypto_la-bn_s_mp_exptmod.lo `test -f 'libtommath/bn_s_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_exptmod.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_s_mp_exptmod.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_s_mp_exptmod.c' object='libtommath/libhcrypto_la-bn_s_mp_exptmod.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_s_mp_exptmod.lo `test -f 'libtommath/bn_s_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_exptmod.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_s_mp_exptmod.lo `test -f 'libtommath/bn_s_mp_exptmod.c' || echo '$(srcdir)/'`libtommath/bn_s_mp_exptmod.c
-libhcrypto_la-bn_mp_reduce_2k.lo: libtommath/bn_mp_reduce_2k.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce_2k.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Tpo -c -o libhcrypto_la-bn_mp_reduce_2k.lo `test -f 'libtommath/bn_mp_reduce_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce_2k.c' object='libhcrypto_la-bn_mp_reduce_2k.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce_2k.lo: libtommath/bn_mp_reduce_2k.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce_2k.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k.lo `test -f 'libtommath/bn_mp_reduce_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce_2k.c' object='libtommath/libhcrypto_la-bn_mp_reduce_2k.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce_2k.lo `test -f 'libtommath/bn_mp_reduce_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k.lo `test -f 'libtommath/bn_mp_reduce_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k.c
-libhcrypto_la-bn_mp_reduce_is_2k.lo: libtommath/bn_mp_reduce_is_2k.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce_is_2k.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Tpo -c -o libhcrypto_la-bn_mp_reduce_is_2k.lo `test -f 'libtommath/bn_mp_reduce_is_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce_is_2k.c' object='libhcrypto_la-bn_mp_reduce_is_2k.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce_is_2k.lo: libtommath/bn_mp_reduce_is_2k.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce_is_2k.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce_is_2k.lo `test -f 'libtommath/bn_mp_reduce_is_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce_is_2k.c' object='libtommath/libhcrypto_la-bn_mp_reduce_is_2k.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce_is_2k.lo `test -f 'libtommath/bn_mp_reduce_is_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce_is_2k.lo `test -f 'libtommath/bn_mp_reduce_is_2k.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k.c
-libhcrypto_la-bn_mp_reduce_2k_setup.lo: libtommath/bn_mp_reduce_2k_setup.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce_2k_setup.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Tpo -c -o libhcrypto_la-bn_mp_reduce_2k_setup.lo `test -f 'libtommath/bn_mp_reduce_2k_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce_2k_setup.c' object='libhcrypto_la-bn_mp_reduce_2k_setup.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce_2k_setup.lo: libtommath/bn_mp_reduce_2k_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce_2k_setup.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k_setup.lo `test -f 'libtommath/bn_mp_reduce_2k_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce_2k_setup.c' object='libtommath/libhcrypto_la-bn_mp_reduce_2k_setup.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce_2k_setup.lo `test -f 'libtommath/bn_mp_reduce_2k_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k_setup.lo `test -f 'libtommath/bn_mp_reduce_2k_setup.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup.c
-libhcrypto_la-bn_mp_reduce_2k_l.lo: libtommath/bn_mp_reduce_2k_l.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce_2k_l.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Tpo -c -o libhcrypto_la-bn_mp_reduce_2k_l.lo `test -f 'libtommath/bn_mp_reduce_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_l.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce_2k_l.c' object='libhcrypto_la-bn_mp_reduce_2k_l.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce_2k_l.lo: libtommath/bn_mp_reduce_2k_l.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce_2k_l.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k_l.lo `test -f 'libtommath/bn_mp_reduce_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_l.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_l.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce_2k_l.c' object='libtommath/libhcrypto_la-bn_mp_reduce_2k_l.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce_2k_l.lo `test -f 'libtommath/bn_mp_reduce_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_l.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k_l.lo `test -f 'libtommath/bn_mp_reduce_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_l.c
-libhcrypto_la-bn_mp_reduce_is_2k_l.lo: libtommath/bn_mp_reduce_is_2k_l.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce_is_2k_l.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Tpo -c -o libhcrypto_la-bn_mp_reduce_is_2k_l.lo `test -f 'libtommath/bn_mp_reduce_is_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k_l.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce_is_2k_l.c' object='libhcrypto_la-bn_mp_reduce_is_2k_l.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce_is_2k_l.lo: libtommath/bn_mp_reduce_is_2k_l.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce_is_2k_l.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce_is_2k_l.lo `test -f 'libtommath/bn_mp_reduce_is_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k_l.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_is_2k_l.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce_is_2k_l.c' object='libtommath/libhcrypto_la-bn_mp_reduce_is_2k_l.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce_is_2k_l.lo `test -f 'libtommath/bn_mp_reduce_is_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k_l.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce_is_2k_l.lo `test -f 'libtommath/bn_mp_reduce_is_2k_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_is_2k_l.c
-libhcrypto_la-bn_mp_reduce_2k_setup_l.lo: libtommath/bn_mp_reduce_2k_setup_l.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_reduce_2k_setup_l.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Tpo -c -o libhcrypto_la-bn_mp_reduce_2k_setup_l.lo `test -f 'libtommath/bn_mp_reduce_2k_setup_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup_l.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_reduce_2k_setup_l.c' object='libhcrypto_la-bn_mp_reduce_2k_setup_l.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_reduce_2k_setup_l.lo: libtommath/bn_mp_reduce_2k_setup_l.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_reduce_2k_setup_l.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Tpo -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k_setup_l.lo `test -f 'libtommath/bn_mp_reduce_2k_setup_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup_l.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_reduce_2k_setup_l.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_reduce_2k_setup_l.c' object='libtommath/libhcrypto_la-bn_mp_reduce_2k_setup_l.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_reduce_2k_setup_l.lo `test -f 'libtommath/bn_mp_reduce_2k_setup_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup_l.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_reduce_2k_setup_l.lo `test -f 'libtommath/bn_mp_reduce_2k_setup_l.c' || echo '$(srcdir)/'`libtommath/bn_mp_reduce_2k_setup_l.c
-libhcrypto_la-bn_mp_radix_smap.lo: libtommath/bn_mp_radix_smap.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_radix_smap.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Tpo -c -o libhcrypto_la-bn_mp_radix_smap.lo `test -f 'libtommath/bn_mp_radix_smap.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_smap.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_radix_smap.c' object='libhcrypto_la-bn_mp_radix_smap.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_radix_smap.lo: libtommath/bn_mp_radix_smap.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_radix_smap.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Tpo -c -o libtommath/libhcrypto_la-bn_mp_radix_smap.lo `test -f 'libtommath/bn_mp_radix_smap.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_smap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_smap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_radix_smap.c' object='libtommath/libhcrypto_la-bn_mp_radix_smap.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_radix_smap.lo `test -f 'libtommath/bn_mp_radix_smap.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_smap.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_radix_smap.lo `test -f 'libtommath/bn_mp_radix_smap.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_smap.c
-libhcrypto_la-bn_mp_read_radix.lo: libtommath/bn_mp_read_radix.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_read_radix.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Tpo -c -o libhcrypto_la-bn_mp_read_radix.lo `test -f 'libtommath/bn_mp_read_radix.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_radix.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_read_radix.c' object='libhcrypto_la-bn_mp_read_radix.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_read_radix.lo: libtommath/bn_mp_read_radix.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_read_radix.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Tpo -c -o libtommath/libhcrypto_la-bn_mp_read_radix.lo `test -f 'libtommath/bn_mp_read_radix.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_radix.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_read_radix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_read_radix.c' object='libtommath/libhcrypto_la-bn_mp_read_radix.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_read_radix.lo `test -f 'libtommath/bn_mp_read_radix.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_radix.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_read_radix.lo `test -f 'libtommath/bn_mp_read_radix.c' || echo '$(srcdir)/'`libtommath/bn_mp_read_radix.c
-libhcrypto_la-bn_mp_toradix.lo: libtommath/bn_mp_toradix.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_toradix.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_toradix.Tpo -c -o libhcrypto_la-bn_mp_toradix.lo `test -f 'libtommath/bn_mp_toradix.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_toradix.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_toradix.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_toradix.c' object='libhcrypto_la-bn_mp_toradix.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_toradix.lo: libtommath/bn_mp_toradix.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_toradix.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix.Tpo -c -o libtommath/libhcrypto_la-bn_mp_toradix.lo `test -f 'libtommath/bn_mp_toradix.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_toradix.c' object='libtommath/libhcrypto_la-bn_mp_toradix.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_toradix.lo `test -f 'libtommath/bn_mp_toradix.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_toradix.lo `test -f 'libtommath/bn_mp_toradix.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix.c
-libhcrypto_la-bn_mp_radix_size.lo: libtommath/bn_mp_radix_size.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_radix_size.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Tpo -c -o libhcrypto_la-bn_mp_radix_size.lo `test -f 'libtommath/bn_mp_radix_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_size.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_radix_size.c' object='libhcrypto_la-bn_mp_radix_size.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_radix_size.lo: libtommath/bn_mp_radix_size.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_radix_size.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Tpo -c -o libtommath/libhcrypto_la-bn_mp_radix_size.lo `test -f 'libtommath/bn_mp_radix_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_size.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_radix_size.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_radix_size.c' object='libtommath/libhcrypto_la-bn_mp_radix_size.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_radix_size.lo `test -f 'libtommath/bn_mp_radix_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_size.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_radix_size.lo `test -f 'libtommath/bn_mp_radix_size.c' || echo '$(srcdir)/'`libtommath/bn_mp_radix_size.c
-libhcrypto_la-bn_mp_fread.lo: libtommath/bn_mp_fread.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_fread.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_fread.Tpo -c -o libhcrypto_la-bn_mp_fread.lo `test -f 'libtommath/bn_mp_fread.c' || echo '$(srcdir)/'`libtommath/bn_mp_fread.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_fread.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_fread.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_fread.c' object='libhcrypto_la-bn_mp_fread.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_fread.lo: libtommath/bn_mp_fread.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_fread.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fread.Tpo -c -o libtommath/libhcrypto_la-bn_mp_fread.lo `test -f 'libtommath/bn_mp_fread.c' || echo '$(srcdir)/'`libtommath/bn_mp_fread.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fread.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fread.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_fread.c' object='libtommath/libhcrypto_la-bn_mp_fread.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_fread.lo `test -f 'libtommath/bn_mp_fread.c' || echo '$(srcdir)/'`libtommath/bn_mp_fread.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_fread.lo `test -f 'libtommath/bn_mp_fread.c' || echo '$(srcdir)/'`libtommath/bn_mp_fread.c
-libhcrypto_la-bn_mp_fwrite.lo: libtommath/bn_mp_fwrite.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_fwrite.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Tpo -c -o libhcrypto_la-bn_mp_fwrite.lo `test -f 'libtommath/bn_mp_fwrite.c' || echo '$(srcdir)/'`libtommath/bn_mp_fwrite.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_fwrite.c' object='libhcrypto_la-bn_mp_fwrite.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_fwrite.lo: libtommath/bn_mp_fwrite.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_fwrite.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Tpo -c -o libtommath/libhcrypto_la-bn_mp_fwrite.lo `test -f 'libtommath/bn_mp_fwrite.c' || echo '$(srcdir)/'`libtommath/bn_mp_fwrite.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_fwrite.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_fwrite.c' object='libtommath/libhcrypto_la-bn_mp_fwrite.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_fwrite.lo `test -f 'libtommath/bn_mp_fwrite.c' || echo '$(srcdir)/'`libtommath/bn_mp_fwrite.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_fwrite.lo `test -f 'libtommath/bn_mp_fwrite.c' || echo '$(srcdir)/'`libtommath/bn_mp_fwrite.c
-libhcrypto_la-bn_mp_cnt_lsb.lo: libtommath/bn_mp_cnt_lsb.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_cnt_lsb.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Tpo -c -o libhcrypto_la-bn_mp_cnt_lsb.lo `test -f 'libtommath/bn_mp_cnt_lsb.c' || echo '$(srcdir)/'`libtommath/bn_mp_cnt_lsb.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_cnt_lsb.c' object='libhcrypto_la-bn_mp_cnt_lsb.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_cnt_lsb.lo: libtommath/bn_mp_cnt_lsb.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_cnt_lsb.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Tpo -c -o libtommath/libhcrypto_la-bn_mp_cnt_lsb.lo `test -f 'libtommath/bn_mp_cnt_lsb.c' || echo '$(srcdir)/'`libtommath/bn_mp_cnt_lsb.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_cnt_lsb.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_cnt_lsb.c' object='libtommath/libhcrypto_la-bn_mp_cnt_lsb.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_cnt_lsb.lo `test -f 'libtommath/bn_mp_cnt_lsb.c' || echo '$(srcdir)/'`libtommath/bn_mp_cnt_lsb.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_cnt_lsb.lo `test -f 'libtommath/bn_mp_cnt_lsb.c' || echo '$(srcdir)/'`libtommath/bn_mp_cnt_lsb.c
-libhcrypto_la-bn_error.lo: libtommath/bn_error.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_error.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_error.Tpo -c -o libhcrypto_la-bn_error.lo `test -f 'libtommath/bn_error.c' || echo '$(srcdir)/'`libtommath/bn_error.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_error.Tpo $(DEPDIR)/libhcrypto_la-bn_error.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_error.c' object='libhcrypto_la-bn_error.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_error.lo: libtommath/bn_error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_error.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_error.Tpo -c -o libtommath/libhcrypto_la-bn_error.lo `test -f 'libtommath/bn_error.c' || echo '$(srcdir)/'`libtommath/bn_error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_error.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_error.c' object='libtommath/libhcrypto_la-bn_error.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_error.lo `test -f 'libtommath/bn_error.c' || echo '$(srcdir)/'`libtommath/bn_error.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_error.lo `test -f 'libtommath/bn_error.c' || echo '$(srcdir)/'`libtommath/bn_error.c
-libhcrypto_la-bn_mp_init_multi.lo: libtommath/bn_mp_init_multi.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_init_multi.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Tpo -c -o libhcrypto_la-bn_mp_init_multi.lo `test -f 'libtommath/bn_mp_init_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_multi.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_init_multi.c' object='libhcrypto_la-bn_mp_init_multi.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_init_multi.lo: libtommath/bn_mp_init_multi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_init_multi.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Tpo -c -o libtommath/libhcrypto_la-bn_mp_init_multi.lo `test -f 'libtommath/bn_mp_init_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_multi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_multi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_init_multi.c' object='libtommath/libhcrypto_la-bn_mp_init_multi.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_init_multi.lo `test -f 'libtommath/bn_mp_init_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_multi.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_init_multi.lo `test -f 'libtommath/bn_mp_init_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_multi.c
-libhcrypto_la-bn_mp_clear_multi.lo: libtommath/bn_mp_clear_multi.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_clear_multi.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Tpo -c -o libhcrypto_la-bn_mp_clear_multi.lo `test -f 'libtommath/bn_mp_clear_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear_multi.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_clear_multi.c' object='libhcrypto_la-bn_mp_clear_multi.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_clear_multi.lo: libtommath/bn_mp_clear_multi.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_clear_multi.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Tpo -c -o libtommath/libhcrypto_la-bn_mp_clear_multi.lo `test -f 'libtommath/bn_mp_clear_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear_multi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_clear_multi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_clear_multi.c' object='libtommath/libhcrypto_la-bn_mp_clear_multi.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_clear_multi.lo `test -f 'libtommath/bn_mp_clear_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear_multi.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_clear_multi.lo `test -f 'libtommath/bn_mp_clear_multi.c' || echo '$(srcdir)/'`libtommath/bn_mp_clear_multi.c
-libhcrypto_la-bn_mp_exteuclid.lo: libtommath/bn_mp_exteuclid.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_exteuclid.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Tpo -c -o libhcrypto_la-bn_mp_exteuclid.lo `test -f 'libtommath/bn_mp_exteuclid.c' || echo '$(srcdir)/'`libtommath/bn_mp_exteuclid.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_exteuclid.c' object='libhcrypto_la-bn_mp_exteuclid.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_exteuclid.lo: libtommath/bn_mp_exteuclid.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_exteuclid.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Tpo -c -o libtommath/libhcrypto_la-bn_mp_exteuclid.lo `test -f 'libtommath/bn_mp_exteuclid.c' || echo '$(srcdir)/'`libtommath/bn_mp_exteuclid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_exteuclid.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_exteuclid.c' object='libtommath/libhcrypto_la-bn_mp_exteuclid.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_exteuclid.lo `test -f 'libtommath/bn_mp_exteuclid.c' || echo '$(srcdir)/'`libtommath/bn_mp_exteuclid.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_exteuclid.lo `test -f 'libtommath/bn_mp_exteuclid.c' || echo '$(srcdir)/'`libtommath/bn_mp_exteuclid.c
-libhcrypto_la-bn_mp_toradix_n.lo: libtommath/bn_mp_toradix_n.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_toradix_n.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Tpo -c -o libhcrypto_la-bn_mp_toradix_n.lo `test -f 'libtommath/bn_mp_toradix_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix_n.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_toradix_n.c' object='libhcrypto_la-bn_mp_toradix_n.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_toradix_n.lo: libtommath/bn_mp_toradix_n.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_toradix_n.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Tpo -c -o libtommath/libhcrypto_la-bn_mp_toradix_n.lo `test -f 'libtommath/bn_mp_toradix_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix_n.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_toradix_n.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_toradix_n.c' object='libtommath/libhcrypto_la-bn_mp_toradix_n.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_toradix_n.lo `test -f 'libtommath/bn_mp_toradix_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix_n.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_toradix_n.lo `test -f 'libtommath/bn_mp_toradix_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_toradix_n.c
-libhcrypto_la-bn_mp_prime_random_ex.lo: libtommath/bn_mp_prime_random_ex.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_prime_random_ex.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Tpo -c -o libhcrypto_la-bn_mp_prime_random_ex.lo `test -f 'libtommath/bn_mp_prime_random_ex.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_random_ex.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_prime_random_ex.c' object='libhcrypto_la-bn_mp_prime_random_ex.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_prime_random_ex.lo: libtommath/bn_mp_prime_random_ex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_prime_random_ex.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Tpo -c -o libtommath/libhcrypto_la-bn_mp_prime_random_ex.lo `test -f 'libtommath/bn_mp_prime_random_ex.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_random_ex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_random_ex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_prime_random_ex.c' object='libtommath/libhcrypto_la-bn_mp_prime_random_ex.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_prime_random_ex.lo `test -f 'libtommath/bn_mp_prime_random_ex.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_random_ex.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_prime_random_ex.lo `test -f 'libtommath/bn_mp_prime_random_ex.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_random_ex.c
-libhcrypto_la-bn_mp_get_int.lo: libtommath/bn_mp_get_int.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_get_int.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_get_int.Tpo -c -o libhcrypto_la-bn_mp_get_int.lo `test -f 'libtommath/bn_mp_get_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_get_int.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_get_int.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_get_int.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_get_int.c' object='libhcrypto_la-bn_mp_get_int.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_get_int.lo: libtommath/bn_mp_get_int.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_get_int.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_get_int.Tpo -c -o libtommath/libhcrypto_la-bn_mp_get_int.lo `test -f 'libtommath/bn_mp_get_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_get_int.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_get_int.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_get_int.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_get_int.c' object='libtommath/libhcrypto_la-bn_mp_get_int.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_get_int.lo `test -f 'libtommath/bn_mp_get_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_get_int.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_get_int.lo `test -f 'libtommath/bn_mp_get_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_get_int.c
-libhcrypto_la-bn_mp_sqrt.lo: libtommath/bn_mp_sqrt.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_sqrt.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Tpo -c -o libhcrypto_la-bn_mp_sqrt.lo `test -f 'libtommath/bn_mp_sqrt.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrt.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_sqrt.c' object='libhcrypto_la-bn_mp_sqrt.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_sqrt.lo: libtommath/bn_mp_sqrt.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_sqrt.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Tpo -c -o libtommath/libhcrypto_la-bn_mp_sqrt.lo `test -f 'libtommath/bn_mp_sqrt.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_sqrt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_sqrt.c' object='libtommath/libhcrypto_la-bn_mp_sqrt.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_sqrt.lo `test -f 'libtommath/bn_mp_sqrt.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrt.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_sqrt.lo `test -f 'libtommath/bn_mp_sqrt.c' || echo '$(srcdir)/'`libtommath/bn_mp_sqrt.c
-libhcrypto_la-bn_mp_is_square.lo: libtommath/bn_mp_is_square.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_is_square.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_is_square.Tpo -c -o libhcrypto_la-bn_mp_is_square.lo `test -f 'libtommath/bn_mp_is_square.c' || echo '$(srcdir)/'`libtommath/bn_mp_is_square.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_is_square.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_is_square.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_is_square.c' object='libhcrypto_la-bn_mp_is_square.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_is_square.lo: libtommath/bn_mp_is_square.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_is_square.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_is_square.Tpo -c -o libtommath/libhcrypto_la-bn_mp_is_square.lo `test -f 'libtommath/bn_mp_is_square.c' || echo '$(srcdir)/'`libtommath/bn_mp_is_square.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_is_square.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_is_square.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_is_square.c' object='libtommath/libhcrypto_la-bn_mp_is_square.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_is_square.lo `test -f 'libtommath/bn_mp_is_square.c' || echo '$(srcdir)/'`libtommath/bn_mp_is_square.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_is_square.lo `test -f 'libtommath/bn_mp_is_square.c' || echo '$(srcdir)/'`libtommath/bn_mp_is_square.c
-libhcrypto_la-bn_mp_init_set.lo: libtommath/bn_mp_init_set.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_init_set.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_init_set.Tpo -c -o libhcrypto_la-bn_mp_init_set.lo `test -f 'libtommath/bn_mp_init_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_init_set.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_init_set.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_init_set.c' object='libhcrypto_la-bn_mp_init_set.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_init_set.lo: libtommath/bn_mp_init_set.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_init_set.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set.Tpo -c -o libtommath/libhcrypto_la-bn_mp_init_set.lo `test -f 'libtommath/bn_mp_init_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_init_set.c' object='libtommath/libhcrypto_la-bn_mp_init_set.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_init_set.lo `test -f 'libtommath/bn_mp_init_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_init_set.lo `test -f 'libtommath/bn_mp_init_set.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set.c
-libhcrypto_la-bn_mp_init_set_int.lo: libtommath/bn_mp_init_set_int.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_init_set_int.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Tpo -c -o libhcrypto_la-bn_mp_init_set_int.lo `test -f 'libtommath/bn_mp_init_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set_int.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_init_set_int.c' object='libhcrypto_la-bn_mp_init_set_int.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_init_set_int.lo: libtommath/bn_mp_init_set_int.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_init_set_int.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Tpo -c -o libtommath/libhcrypto_la-bn_mp_init_set_int.lo `test -f 'libtommath/bn_mp_init_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set_int.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_init_set_int.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_init_set_int.c' object='libtommath/libhcrypto_la-bn_mp_init_set_int.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_init_set_int.lo `test -f 'libtommath/bn_mp_init_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set_int.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_init_set_int.lo `test -f 'libtommath/bn_mp_init_set_int.c' || echo '$(srcdir)/'`libtommath/bn_mp_init_set_int.c
-libhcrypto_la-bn_mp_invmod_slow.lo: libtommath/bn_mp_invmod_slow.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_invmod_slow.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Tpo -c -o libhcrypto_la-bn_mp_invmod_slow.lo `test -f 'libtommath/bn_mp_invmod_slow.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod_slow.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_invmod_slow.c' object='libhcrypto_la-bn_mp_invmod_slow.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_invmod_slow.lo: libtommath/bn_mp_invmod_slow.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_invmod_slow.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Tpo -c -o libtommath/libhcrypto_la-bn_mp_invmod_slow.lo `test -f 'libtommath/bn_mp_invmod_slow.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod_slow.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_invmod_slow.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_invmod_slow.c' object='libtommath/libhcrypto_la-bn_mp_invmod_slow.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_invmod_slow.lo `test -f 'libtommath/bn_mp_invmod_slow.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod_slow.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_invmod_slow.lo `test -f 'libtommath/bn_mp_invmod_slow.c' || echo '$(srcdir)/'`libtommath/bn_mp_invmod_slow.c
-libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo: libtommath/bn_mp_prime_rabin_miller_trials.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Tpo -c -o libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo `test -f 'libtommath/bn_mp_prime_rabin_miller_trials.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_rabin_miller_trials.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_prime_rabin_miller_trials.c' object='libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo: libtommath/bn_mp_prime_rabin_miller_trials.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Tpo -c -o libtommath/libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo `test -f 'libtommath/bn_mp_prime_rabin_miller_trials.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_rabin_miller_trials.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_prime_rabin_miller_trials.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_prime_rabin_miller_trials.c' object='libtommath/libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo `test -f 'libtommath/bn_mp_prime_rabin_miller_trials.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_rabin_miller_trials.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_prime_rabin_miller_trials.lo `test -f 'libtommath/bn_mp_prime_rabin_miller_trials.c' || echo '$(srcdir)/'`libtommath/bn_mp_prime_rabin_miller_trials.c
-libhcrypto_la-bn_mp_to_signed_bin_n.lo: libtommath/bn_mp_to_signed_bin_n.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_to_signed_bin_n.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Tpo -c -o libhcrypto_la-bn_mp_to_signed_bin_n.lo `test -f 'libtommath/bn_mp_to_signed_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin_n.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_to_signed_bin_n.c' object='libhcrypto_la-bn_mp_to_signed_bin_n.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_to_signed_bin_n.lo: libtommath/bn_mp_to_signed_bin_n.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_to_signed_bin_n.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Tpo -c -o libtommath/libhcrypto_la-bn_mp_to_signed_bin_n.lo `test -f 'libtommath/bn_mp_to_signed_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin_n.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_signed_bin_n.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_to_signed_bin_n.c' object='libtommath/libhcrypto_la-bn_mp_to_signed_bin_n.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_to_signed_bin_n.lo `test -f 'libtommath/bn_mp_to_signed_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin_n.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_to_signed_bin_n.lo `test -f 'libtommath/bn_mp_to_signed_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_signed_bin_n.c
-libhcrypto_la-bn_mp_to_unsigned_bin_n.lo: libtommath/bn_mp_to_unsigned_bin_n.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn_mp_to_unsigned_bin_n.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Tpo -c -o libhcrypto_la-bn_mp_to_unsigned_bin_n.lo `test -f 'libtommath/bn_mp_to_unsigned_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin_n.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Tpo $(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libtommath/bn_mp_to_unsigned_bin_n.c' object='libhcrypto_la-bn_mp_to_unsigned_bin_n.lo' libtool=yes @AMDEPBACKSLASH@
+libtommath/libhcrypto_la-bn_mp_to_unsigned_bin_n.lo: libtommath/bn_mp_to_unsigned_bin_n.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libtommath/libhcrypto_la-bn_mp_to_unsigned_bin_n.lo -MD -MP -MF libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Tpo -c -o libtommath/libhcrypto_la-bn_mp_to_unsigned_bin_n.lo `test -f 'libtommath/bn_mp_to_unsigned_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin_n.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Tpo libtommath/$(DEPDIR)/libhcrypto_la-bn_mp_to_unsigned_bin_n.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libtommath/bn_mp_to_unsigned_bin_n.c' object='libtommath/libhcrypto_la-bn_mp_to_unsigned_bin_n.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn_mp_to_unsigned_bin_n.lo `test -f 'libtommath/bn_mp_to_unsigned_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin_n.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libtommath/libhcrypto_la-bn_mp_to_unsigned_bin_n.lo `test -f 'libtommath/bn_mp_to_unsigned_bin_n.c' || echo '$(srcdir)/'`libtommath/bn_mp_to_unsigned_bin_n.c
libhcrypto_la-aes.lo: aes.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-aes.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-aes.Tpo -c -o libhcrypto_la-aes.lo `test -f 'aes.c' || echo '$(srcdir)/'`aes.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-aes.Tpo $(DEPDIR)/libhcrypto_la-aes.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='aes.c' object='libhcrypto_la-aes.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-aes.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-aes.Tpo -c -o libhcrypto_la-aes.lo `test -f 'aes.c' || echo '$(srcdir)/'`aes.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-aes.Tpo $(DEPDIR)/libhcrypto_la-aes.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='aes.c' object='libhcrypto_la-aes.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-aes.lo `test -f 'aes.c' || echo '$(srcdir)/'`aes.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-aes.lo `test -f 'aes.c' || echo '$(srcdir)/'`aes.c
libhcrypto_la-bn.lo: bn.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn.Tpo -c -o libhcrypto_la-bn.lo `test -f 'bn.c' || echo '$(srcdir)/'`bn.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-bn.Tpo $(DEPDIR)/libhcrypto_la-bn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bn.c' object='libhcrypto_la-bn.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-bn.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-bn.Tpo -c -o libhcrypto_la-bn.lo `test -f 'bn.c' || echo '$(srcdir)/'`bn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-bn.Tpo $(DEPDIR)/libhcrypto_la-bn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bn.c' object='libhcrypto_la-bn.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn.lo `test -f 'bn.c' || echo '$(srcdir)/'`bn.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-bn.lo `test -f 'bn.c' || echo '$(srcdir)/'`bn.c
libhcrypto_la-common.lo: common.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-common.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-common.Tpo -c -o libhcrypto_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-common.Tpo $(DEPDIR)/libhcrypto_la-common.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='common.c' object='libhcrypto_la-common.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-common.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-common.Tpo -c -o libhcrypto_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-common.Tpo $(DEPDIR)/libhcrypto_la-common.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common.c' object='libhcrypto_la-common.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-common.lo `test -f 'common.c' || echo '$(srcdir)/'`common.c
libhcrypto_la-camellia.lo: camellia.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-camellia.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-camellia.Tpo -c -o libhcrypto_la-camellia.lo `test -f 'camellia.c' || echo '$(srcdir)/'`camellia.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-camellia.Tpo $(DEPDIR)/libhcrypto_la-camellia.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='camellia.c' object='libhcrypto_la-camellia.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-camellia.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-camellia.Tpo -c -o libhcrypto_la-camellia.lo `test -f 'camellia.c' || echo '$(srcdir)/'`camellia.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-camellia.Tpo $(DEPDIR)/libhcrypto_la-camellia.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='camellia.c' object='libhcrypto_la-camellia.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-camellia.lo `test -f 'camellia.c' || echo '$(srcdir)/'`camellia.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-camellia.lo `test -f 'camellia.c' || echo '$(srcdir)/'`camellia.c
libhcrypto_la-camellia-ntt.lo: camellia-ntt.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-camellia-ntt.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-camellia-ntt.Tpo -c -o libhcrypto_la-camellia-ntt.lo `test -f 'camellia-ntt.c' || echo '$(srcdir)/'`camellia-ntt.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-camellia-ntt.Tpo $(DEPDIR)/libhcrypto_la-camellia-ntt.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='camellia-ntt.c' object='libhcrypto_la-camellia-ntt.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-camellia-ntt.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-camellia-ntt.Tpo -c -o libhcrypto_la-camellia-ntt.lo `test -f 'camellia-ntt.c' || echo '$(srcdir)/'`camellia-ntt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-camellia-ntt.Tpo $(DEPDIR)/libhcrypto_la-camellia-ntt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='camellia-ntt.c' object='libhcrypto_la-camellia-ntt.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-camellia-ntt.lo `test -f 'camellia-ntt.c' || echo '$(srcdir)/'`camellia-ntt.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-camellia-ntt.lo `test -f 'camellia-ntt.c' || echo '$(srcdir)/'`camellia-ntt.c
libhcrypto_la-des.lo: des.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-des.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-des.Tpo -c -o libhcrypto_la-des.lo `test -f 'des.c' || echo '$(srcdir)/'`des.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-des.Tpo $(DEPDIR)/libhcrypto_la-des.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='des.c' object='libhcrypto_la-des.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-des.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-des.Tpo -c -o libhcrypto_la-des.lo `test -f 'des.c' || echo '$(srcdir)/'`des.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-des.Tpo $(DEPDIR)/libhcrypto_la-des.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='des.c' object='libhcrypto_la-des.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-des.lo `test -f 'des.c' || echo '$(srcdir)/'`des.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-des.lo `test -f 'des.c' || echo '$(srcdir)/'`des.c
libhcrypto_la-dh.lo: dh.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-dh.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-dh.Tpo -c -o libhcrypto_la-dh.lo `test -f 'dh.c' || echo '$(srcdir)/'`dh.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-dh.Tpo $(DEPDIR)/libhcrypto_la-dh.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dh.c' object='libhcrypto_la-dh.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-dh.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-dh.Tpo -c -o libhcrypto_la-dh.lo `test -f 'dh.c' || echo '$(srcdir)/'`dh.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-dh.Tpo $(DEPDIR)/libhcrypto_la-dh.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dh.c' object='libhcrypto_la-dh.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-dh.lo `test -f 'dh.c' || echo '$(srcdir)/'`dh.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-dh.lo `test -f 'dh.c' || echo '$(srcdir)/'`dh.c
libhcrypto_la-dh-ltm.lo: dh-ltm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-dh-ltm.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-dh-ltm.Tpo -c -o libhcrypto_la-dh-ltm.lo `test -f 'dh-ltm.c' || echo '$(srcdir)/'`dh-ltm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-dh-ltm.Tpo $(DEPDIR)/libhcrypto_la-dh-ltm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dh-ltm.c' object='libhcrypto_la-dh-ltm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-dh-ltm.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-dh-ltm.Tpo -c -o libhcrypto_la-dh-ltm.lo `test -f 'dh-ltm.c' || echo '$(srcdir)/'`dh-ltm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-dh-ltm.Tpo $(DEPDIR)/libhcrypto_la-dh-ltm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dh-ltm.c' object='libhcrypto_la-dh-ltm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-dh-ltm.lo `test -f 'dh-ltm.c' || echo '$(srcdir)/'`dh-ltm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-dh-ltm.lo `test -f 'dh-ltm.c' || echo '$(srcdir)/'`dh-ltm.c
libhcrypto_la-dsa.lo: dsa.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-dsa.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-dsa.Tpo -c -o libhcrypto_la-dsa.lo `test -f 'dsa.c' || echo '$(srcdir)/'`dsa.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-dsa.Tpo $(DEPDIR)/libhcrypto_la-dsa.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dsa.c' object='libhcrypto_la-dsa.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-dsa.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-dsa.Tpo -c -o libhcrypto_la-dsa.lo `test -f 'dsa.c' || echo '$(srcdir)/'`dsa.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-dsa.Tpo $(DEPDIR)/libhcrypto_la-dsa.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dsa.c' object='libhcrypto_la-dsa.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-dsa.lo `test -f 'dsa.c' || echo '$(srcdir)/'`dsa.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-dsa.lo `test -f 'dsa.c' || echo '$(srcdir)/'`dsa.c
libhcrypto_la-doxygen.lo: doxygen.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-doxygen.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-doxygen.Tpo -c -o libhcrypto_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-doxygen.Tpo $(DEPDIR)/libhcrypto_la-doxygen.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='doxygen.c' object='libhcrypto_la-doxygen.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-doxygen.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-doxygen.Tpo -c -o libhcrypto_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-doxygen.Tpo $(DEPDIR)/libhcrypto_la-doxygen.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doxygen.c' object='libhcrypto_la-doxygen.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
libhcrypto_la-evp.lo: evp.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp.Tpo -c -o libhcrypto_la-evp.lo `test -f 'evp.c' || echo '$(srcdir)/'`evp.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-evp.Tpo $(DEPDIR)/libhcrypto_la-evp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='evp.c' object='libhcrypto_la-evp.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp.Tpo -c -o libhcrypto_la-evp.lo `test -f 'evp.c' || echo '$(srcdir)/'`evp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-evp.Tpo $(DEPDIR)/libhcrypto_la-evp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='evp.c' object='libhcrypto_la-evp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp.lo `test -f 'evp.c' || echo '$(srcdir)/'`evp.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp.lo `test -f 'evp.c' || echo '$(srcdir)/'`evp.c
libhcrypto_la-evp-hcrypto.lo: evp-hcrypto.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp-hcrypto.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp-hcrypto.Tpo -c -o libhcrypto_la-evp-hcrypto.lo `test -f 'evp-hcrypto.c' || echo '$(srcdir)/'`evp-hcrypto.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-evp-hcrypto.Tpo $(DEPDIR)/libhcrypto_la-evp-hcrypto.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='evp-hcrypto.c' object='libhcrypto_la-evp-hcrypto.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp-hcrypto.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp-hcrypto.Tpo -c -o libhcrypto_la-evp-hcrypto.lo `test -f 'evp-hcrypto.c' || echo '$(srcdir)/'`evp-hcrypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-evp-hcrypto.Tpo $(DEPDIR)/libhcrypto_la-evp-hcrypto.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='evp-hcrypto.c' object='libhcrypto_la-evp-hcrypto.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp-hcrypto.lo `test -f 'evp-hcrypto.c' || echo '$(srcdir)/'`evp-hcrypto.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp-hcrypto.lo `test -f 'evp-hcrypto.c' || echo '$(srcdir)/'`evp-hcrypto.c
libhcrypto_la-evp-cc.lo: evp-cc.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp-cc.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp-cc.Tpo -c -o libhcrypto_la-evp-cc.lo `test -f 'evp-cc.c' || echo '$(srcdir)/'`evp-cc.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-evp-cc.Tpo $(DEPDIR)/libhcrypto_la-evp-cc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='evp-cc.c' object='libhcrypto_la-evp-cc.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp-cc.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp-cc.Tpo -c -o libhcrypto_la-evp-cc.lo `test -f 'evp-cc.c' || echo '$(srcdir)/'`evp-cc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-evp-cc.Tpo $(DEPDIR)/libhcrypto_la-evp-cc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='evp-cc.c' object='libhcrypto_la-evp-cc.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp-cc.lo `test -f 'evp-cc.c' || echo '$(srcdir)/'`evp-cc.c
+
+libhcrypto_la-evp-openssl.lo: evp-openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp-openssl.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp-openssl.Tpo -c -o libhcrypto_la-evp-openssl.lo `test -f 'evp-openssl.c' || echo '$(srcdir)/'`evp-openssl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-evp-openssl.Tpo $(DEPDIR)/libhcrypto_la-evp-openssl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='evp-openssl.c' object='libhcrypto_la-evp-openssl.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp-cc.lo `test -f 'evp-cc.c' || echo '$(srcdir)/'`evp-cc.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp-openssl.lo `test -f 'evp-openssl.c' || echo '$(srcdir)/'`evp-openssl.c
+
+libhcrypto_la-evp-pkcs11.lo: evp-pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-evp-pkcs11.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-evp-pkcs11.Tpo -c -o libhcrypto_la-evp-pkcs11.lo `test -f 'evp-pkcs11.c' || echo '$(srcdir)/'`evp-pkcs11.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-evp-pkcs11.Tpo $(DEPDIR)/libhcrypto_la-evp-pkcs11.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='evp-pkcs11.c' object='libhcrypto_la-evp-pkcs11.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-evp-pkcs11.lo `test -f 'evp-pkcs11.c' || echo '$(srcdir)/'`evp-pkcs11.c
libhcrypto_la-engine.lo: engine.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-engine.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-engine.Tpo -c -o libhcrypto_la-engine.lo `test -f 'engine.c' || echo '$(srcdir)/'`engine.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-engine.Tpo $(DEPDIR)/libhcrypto_la-engine.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='engine.c' object='libhcrypto_la-engine.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-engine.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-engine.Tpo -c -o libhcrypto_la-engine.lo `test -f 'engine.c' || echo '$(srcdir)/'`engine.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-engine.Tpo $(DEPDIR)/libhcrypto_la-engine.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='engine.c' object='libhcrypto_la-engine.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-engine.lo `test -f 'engine.c' || echo '$(srcdir)/'`engine.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-engine.lo `test -f 'engine.c' || echo '$(srcdir)/'`engine.c
libhcrypto_la-hmac.lo: hmac.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-hmac.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-hmac.Tpo -c -o libhcrypto_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-hmac.Tpo $(DEPDIR)/libhcrypto_la-hmac.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hmac.c' object='libhcrypto_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-hmac.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-hmac.Tpo -c -o libhcrypto_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-hmac.Tpo $(DEPDIR)/libhcrypto_la-hmac.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hmac.c' object='libhcrypto_la-hmac.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
libhcrypto_la-md2.lo: md2.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-md2.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-md2.Tpo -c -o libhcrypto_la-md2.lo `test -f 'md2.c' || echo '$(srcdir)/'`md2.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-md2.Tpo $(DEPDIR)/libhcrypto_la-md2.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md2.c' object='libhcrypto_la-md2.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-md2.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-md2.Tpo -c -o libhcrypto_la-md2.lo `test -f 'md2.c' || echo '$(srcdir)/'`md2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-md2.Tpo $(DEPDIR)/libhcrypto_la-md2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md2.c' object='libhcrypto_la-md2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-md2.lo `test -f 'md2.c' || echo '$(srcdir)/'`md2.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-md2.lo `test -f 'md2.c' || echo '$(srcdir)/'`md2.c
libhcrypto_la-md4.lo: md4.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-md4.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-md4.Tpo -c -o libhcrypto_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-md4.Tpo $(DEPDIR)/libhcrypto_la-md4.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md4.c' object='libhcrypto_la-md4.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-md4.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-md4.Tpo -c -o libhcrypto_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-md4.Tpo $(DEPDIR)/libhcrypto_la-md4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md4.c' object='libhcrypto_la-md4.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-md4.lo `test -f 'md4.c' || echo '$(srcdir)/'`md4.c
libhcrypto_la-md5.lo: md5.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-md5.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-md5.Tpo -c -o libhcrypto_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-md5.Tpo $(DEPDIR)/libhcrypto_la-md5.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='md5.c' object='libhcrypto_la-md5.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-md5.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-md5.Tpo -c -o libhcrypto_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-md5.Tpo $(DEPDIR)/libhcrypto_la-md5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='md5.c' object='libhcrypto_la-md5.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-md5.lo `test -f 'md5.c' || echo '$(srcdir)/'`md5.c
libhcrypto_la-pkcs5.lo: pkcs5.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-pkcs5.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-pkcs5.Tpo -c -o libhcrypto_la-pkcs5.lo `test -f 'pkcs5.c' || echo '$(srcdir)/'`pkcs5.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-pkcs5.Tpo $(DEPDIR)/libhcrypto_la-pkcs5.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pkcs5.c' object='libhcrypto_la-pkcs5.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-pkcs5.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-pkcs5.Tpo -c -o libhcrypto_la-pkcs5.lo `test -f 'pkcs5.c' || echo '$(srcdir)/'`pkcs5.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-pkcs5.Tpo $(DEPDIR)/libhcrypto_la-pkcs5.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pkcs5.c' object='libhcrypto_la-pkcs5.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-pkcs5.lo `test -f 'pkcs5.c' || echo '$(srcdir)/'`pkcs5.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-pkcs5.lo `test -f 'pkcs5.c' || echo '$(srcdir)/'`pkcs5.c
libhcrypto_la-pkcs12.lo: pkcs12.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-pkcs12.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-pkcs12.Tpo -c -o libhcrypto_la-pkcs12.lo `test -f 'pkcs12.c' || echo '$(srcdir)/'`pkcs12.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-pkcs12.Tpo $(DEPDIR)/libhcrypto_la-pkcs12.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pkcs12.c' object='libhcrypto_la-pkcs12.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-pkcs12.lo `test -f 'pkcs12.c' || echo '$(srcdir)/'`pkcs12.c
-
-libhcrypto_la-rand-egd.lo: rand-egd.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand-egd.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand-egd.Tpo -c -o libhcrypto_la-rand-egd.lo `test -f 'rand-egd.c' || echo '$(srcdir)/'`rand-egd.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rand-egd.Tpo $(DEPDIR)/libhcrypto_la-rand-egd.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rand-egd.c' object='libhcrypto_la-rand-egd.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-pkcs12.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-pkcs12.Tpo -c -o libhcrypto_la-pkcs12.lo `test -f 'pkcs12.c' || echo '$(srcdir)/'`pkcs12.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-pkcs12.Tpo $(DEPDIR)/libhcrypto_la-pkcs12.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pkcs12.c' object='libhcrypto_la-pkcs12.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand-egd.lo `test -f 'rand-egd.c' || echo '$(srcdir)/'`rand-egd.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-pkcs12.lo `test -f 'pkcs12.c' || echo '$(srcdir)/'`pkcs12.c
libhcrypto_la-rand-fortuna.lo: rand-fortuna.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand-fortuna.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand-fortuna.Tpo -c -o libhcrypto_la-rand-fortuna.lo `test -f 'rand-fortuna.c' || echo '$(srcdir)/'`rand-fortuna.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rand-fortuna.Tpo $(DEPDIR)/libhcrypto_la-rand-fortuna.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rand-fortuna.c' object='libhcrypto_la-rand-fortuna.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand-fortuna.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand-fortuna.Tpo -c -o libhcrypto_la-rand-fortuna.lo `test -f 'rand-fortuna.c' || echo '$(srcdir)/'`rand-fortuna.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rand-fortuna.Tpo $(DEPDIR)/libhcrypto_la-rand-fortuna.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand-fortuna.c' object='libhcrypto_la-rand-fortuna.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand-fortuna.lo `test -f 'rand-fortuna.c' || echo '$(srcdir)/'`rand-fortuna.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand-fortuna.lo `test -f 'rand-fortuna.c' || echo '$(srcdir)/'`rand-fortuna.c
libhcrypto_la-rand-timer.lo: rand-timer.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand-timer.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand-timer.Tpo -c -o libhcrypto_la-rand-timer.lo `test -f 'rand-timer.c' || echo '$(srcdir)/'`rand-timer.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rand-timer.Tpo $(DEPDIR)/libhcrypto_la-rand-timer.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rand-timer.c' object='libhcrypto_la-rand-timer.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand-timer.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand-timer.Tpo -c -o libhcrypto_la-rand-timer.lo `test -f 'rand-timer.c' || echo '$(srcdir)/'`rand-timer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rand-timer.Tpo $(DEPDIR)/libhcrypto_la-rand-timer.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand-timer.c' object='libhcrypto_la-rand-timer.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand-timer.lo `test -f 'rand-timer.c' || echo '$(srcdir)/'`rand-timer.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand-timer.lo `test -f 'rand-timer.c' || echo '$(srcdir)/'`rand-timer.c
libhcrypto_la-rand-unix.lo: rand-unix.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand-unix.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand-unix.Tpo -c -o libhcrypto_la-rand-unix.lo `test -f 'rand-unix.c' || echo '$(srcdir)/'`rand-unix.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rand-unix.Tpo $(DEPDIR)/libhcrypto_la-rand-unix.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rand-unix.c' object='libhcrypto_la-rand-unix.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand-unix.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand-unix.Tpo -c -o libhcrypto_la-rand-unix.lo `test -f 'rand-unix.c' || echo '$(srcdir)/'`rand-unix.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rand-unix.Tpo $(DEPDIR)/libhcrypto_la-rand-unix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand-unix.c' object='libhcrypto_la-rand-unix.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand-unix.lo `test -f 'rand-unix.c' || echo '$(srcdir)/'`rand-unix.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand-unix.lo `test -f 'rand-unix.c' || echo '$(srcdir)/'`rand-unix.c
libhcrypto_la-rand.lo: rand.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand.Tpo -c -o libhcrypto_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rand.Tpo $(DEPDIR)/libhcrypto_la-rand.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rand.c' object='libhcrypto_la-rand.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rand.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rand.Tpo -c -o libhcrypto_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rand.Tpo $(DEPDIR)/libhcrypto_la-rand.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libhcrypto_la-rand.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
libhcrypto_la-rc2.lo: rc2.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rc2.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rc2.Tpo -c -o libhcrypto_la-rc2.lo `test -f 'rc2.c' || echo '$(srcdir)/'`rc2.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rc2.Tpo $(DEPDIR)/libhcrypto_la-rc2.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rc2.c' object='libhcrypto_la-rc2.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rc2.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rc2.Tpo -c -o libhcrypto_la-rc2.lo `test -f 'rc2.c' || echo '$(srcdir)/'`rc2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rc2.Tpo $(DEPDIR)/libhcrypto_la-rc2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rc2.c' object='libhcrypto_la-rc2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rc2.lo `test -f 'rc2.c' || echo '$(srcdir)/'`rc2.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rc2.lo `test -f 'rc2.c' || echo '$(srcdir)/'`rc2.c
libhcrypto_la-rc4.lo: rc4.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rc4.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rc4.Tpo -c -o libhcrypto_la-rc4.lo `test -f 'rc4.c' || echo '$(srcdir)/'`rc4.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rc4.Tpo $(DEPDIR)/libhcrypto_la-rc4.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rc4.c' object='libhcrypto_la-rc4.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rc4.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rc4.Tpo -c -o libhcrypto_la-rc4.lo `test -f 'rc4.c' || echo '$(srcdir)/'`rc4.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rc4.Tpo $(DEPDIR)/libhcrypto_la-rc4.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rc4.c' object='libhcrypto_la-rc4.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rc4.lo `test -f 'rc4.c' || echo '$(srcdir)/'`rc4.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rc4.lo `test -f 'rc4.c' || echo '$(srcdir)/'`rc4.c
libhcrypto_la-rijndael-alg-fst.lo: rijndael-alg-fst.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rijndael-alg-fst.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rijndael-alg-fst.Tpo -c -o libhcrypto_la-rijndael-alg-fst.lo `test -f 'rijndael-alg-fst.c' || echo '$(srcdir)/'`rijndael-alg-fst.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rijndael-alg-fst.Tpo $(DEPDIR)/libhcrypto_la-rijndael-alg-fst.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rijndael-alg-fst.c' object='libhcrypto_la-rijndael-alg-fst.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rijndael-alg-fst.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rijndael-alg-fst.Tpo -c -o libhcrypto_la-rijndael-alg-fst.lo `test -f 'rijndael-alg-fst.c' || echo '$(srcdir)/'`rijndael-alg-fst.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rijndael-alg-fst.Tpo $(DEPDIR)/libhcrypto_la-rijndael-alg-fst.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rijndael-alg-fst.c' object='libhcrypto_la-rijndael-alg-fst.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rijndael-alg-fst.lo `test -f 'rijndael-alg-fst.c' || echo '$(srcdir)/'`rijndael-alg-fst.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rijndael-alg-fst.lo `test -f 'rijndael-alg-fst.c' || echo '$(srcdir)/'`rijndael-alg-fst.c
libhcrypto_la-rnd_keys.lo: rnd_keys.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rnd_keys.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rnd_keys.Tpo -c -o libhcrypto_la-rnd_keys.lo `test -f 'rnd_keys.c' || echo '$(srcdir)/'`rnd_keys.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rnd_keys.Tpo $(DEPDIR)/libhcrypto_la-rnd_keys.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rnd_keys.c' object='libhcrypto_la-rnd_keys.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rnd_keys.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rnd_keys.Tpo -c -o libhcrypto_la-rnd_keys.lo `test -f 'rnd_keys.c' || echo '$(srcdir)/'`rnd_keys.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rnd_keys.Tpo $(DEPDIR)/libhcrypto_la-rnd_keys.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rnd_keys.c' object='libhcrypto_la-rnd_keys.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rnd_keys.lo `test -f 'rnd_keys.c' || echo '$(srcdir)/'`rnd_keys.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rnd_keys.lo `test -f 'rnd_keys.c' || echo '$(srcdir)/'`rnd_keys.c
libhcrypto_la-rsa.lo: rsa.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rsa.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rsa.Tpo -c -o libhcrypto_la-rsa.lo `test -f 'rsa.c' || echo '$(srcdir)/'`rsa.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rsa.Tpo $(DEPDIR)/libhcrypto_la-rsa.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rsa.c' object='libhcrypto_la-rsa.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rsa.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rsa.Tpo -c -o libhcrypto_la-rsa.lo `test -f 'rsa.c' || echo '$(srcdir)/'`rsa.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rsa.Tpo $(DEPDIR)/libhcrypto_la-rsa.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rsa.c' object='libhcrypto_la-rsa.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rsa.lo `test -f 'rsa.c' || echo '$(srcdir)/'`rsa.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rsa.lo `test -f 'rsa.c' || echo '$(srcdir)/'`rsa.c
libhcrypto_la-rsa-gmp.lo: rsa-gmp.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rsa-gmp.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rsa-gmp.Tpo -c -o libhcrypto_la-rsa-gmp.lo `test -f 'rsa-gmp.c' || echo '$(srcdir)/'`rsa-gmp.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rsa-gmp.Tpo $(DEPDIR)/libhcrypto_la-rsa-gmp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rsa-gmp.c' object='libhcrypto_la-rsa-gmp.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rsa-gmp.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rsa-gmp.Tpo -c -o libhcrypto_la-rsa-gmp.lo `test -f 'rsa-gmp.c' || echo '$(srcdir)/'`rsa-gmp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rsa-gmp.Tpo $(DEPDIR)/libhcrypto_la-rsa-gmp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rsa-gmp.c' object='libhcrypto_la-rsa-gmp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rsa-gmp.lo `test -f 'rsa-gmp.c' || echo '$(srcdir)/'`rsa-gmp.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rsa-gmp.lo `test -f 'rsa-gmp.c' || echo '$(srcdir)/'`rsa-gmp.c
libhcrypto_la-rsa-ltm.lo: rsa-ltm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rsa-ltm.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rsa-ltm.Tpo -c -o libhcrypto_la-rsa-ltm.lo `test -f 'rsa-ltm.c' || echo '$(srcdir)/'`rsa-ltm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-rsa-ltm.Tpo $(DEPDIR)/libhcrypto_la-rsa-ltm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rsa-ltm.c' object='libhcrypto_la-rsa-ltm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-rsa-ltm.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-rsa-ltm.Tpo -c -o libhcrypto_la-rsa-ltm.lo `test -f 'rsa-ltm.c' || echo '$(srcdir)/'`rsa-ltm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-rsa-ltm.Tpo $(DEPDIR)/libhcrypto_la-rsa-ltm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rsa-ltm.c' object='libhcrypto_la-rsa-ltm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rsa-ltm.lo `test -f 'rsa-ltm.c' || echo '$(srcdir)/'`rsa-ltm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-rsa-ltm.lo `test -f 'rsa-ltm.c' || echo '$(srcdir)/'`rsa-ltm.c
libhcrypto_la-sha.lo: sha.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-sha.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-sha.Tpo -c -o libhcrypto_la-sha.lo `test -f 'sha.c' || echo '$(srcdir)/'`sha.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-sha.Tpo $(DEPDIR)/libhcrypto_la-sha.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sha.c' object='libhcrypto_la-sha.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-sha.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-sha.Tpo -c -o libhcrypto_la-sha.lo `test -f 'sha.c' || echo '$(srcdir)/'`sha.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-sha.Tpo $(DEPDIR)/libhcrypto_la-sha.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha.c' object='libhcrypto_la-sha.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-sha.lo `test -f 'sha.c' || echo '$(srcdir)/'`sha.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-sha.lo `test -f 'sha.c' || echo '$(srcdir)/'`sha.c
libhcrypto_la-sha256.lo: sha256.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-sha256.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-sha256.Tpo -c -o libhcrypto_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-sha256.Tpo $(DEPDIR)/libhcrypto_la-sha256.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sha256.c' object='libhcrypto_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-sha256.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-sha256.Tpo -c -o libhcrypto_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-sha256.Tpo $(DEPDIR)/libhcrypto_la-sha256.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha256.c' object='libhcrypto_la-sha256.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-sha256.lo `test -f 'sha256.c' || echo '$(srcdir)/'`sha256.c
libhcrypto_la-sha512.lo: sha512.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-sha512.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-sha512.Tpo -c -o libhcrypto_la-sha512.lo `test -f 'sha512.c' || echo '$(srcdir)/'`sha512.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-sha512.Tpo $(DEPDIR)/libhcrypto_la-sha512.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sha512.c' object='libhcrypto_la-sha512.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-sha512.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-sha512.Tpo -c -o libhcrypto_la-sha512.lo `test -f 'sha512.c' || echo '$(srcdir)/'`sha512.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-sha512.Tpo $(DEPDIR)/libhcrypto_la-sha512.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sha512.c' object='libhcrypto_la-sha512.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-sha512.lo `test -f 'sha512.c' || echo '$(srcdir)/'`sha512.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-sha512.lo `test -f 'sha512.c' || echo '$(srcdir)/'`sha512.c
libhcrypto_la-validate.lo: validate.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-validate.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-validate.Tpo -c -o libhcrypto_la-validate.lo `test -f 'validate.c' || echo '$(srcdir)/'`validate.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-validate.Tpo $(DEPDIR)/libhcrypto_la-validate.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='validate.c' object='libhcrypto_la-validate.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-validate.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-validate.Tpo -c -o libhcrypto_la-validate.lo `test -f 'validate.c' || echo '$(srcdir)/'`validate.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-validate.Tpo $(DEPDIR)/libhcrypto_la-validate.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='validate.c' object='libhcrypto_la-validate.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-validate.lo `test -f 'validate.c' || echo '$(srcdir)/'`validate.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-validate.lo `test -f 'validate.c' || echo '$(srcdir)/'`validate.c
libhcrypto_la-ui.lo: ui.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-ui.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-ui.Tpo -c -o libhcrypto_la-ui.lo `test -f 'ui.c' || echo '$(srcdir)/'`ui.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhcrypto_la-ui.Tpo $(DEPDIR)/libhcrypto_la-ui.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ui.c' object='libhcrypto_la-ui.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhcrypto_la-ui.lo -MD -MP -MF $(DEPDIR)/libhcrypto_la-ui.Tpo -c -o libhcrypto_la-ui.lo `test -f 'ui.c' || echo '$(srcdir)/'`ui.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libhcrypto_la-ui.Tpo $(DEPDIR)/libhcrypto_la-ui.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ui.c' object='libhcrypto_la-ui.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-ui.lo `test -f 'ui.c' || echo '$(srcdir)/'`ui.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhcrypto_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhcrypto_la-ui.lo `test -f 'ui.c' || echo '$(srcdir)/'`ui.c
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
+ -rm -rf libtommath/.libs libtommath/_libs
install-hcryptoincludeHEADERS: $(hcryptoinclude_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(hcryptoincludedir)" || $(MKDIR_P) "$(DESTDIR)$(hcryptoincludedir)"
@list='$(hcryptoinclude_HEADERS)'; test -n "$(hcryptoincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(hcryptoincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(hcryptoincludedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -2343,30 +3044,17 @@ uninstall-hcryptoincludeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(hcryptoinclude_HEADERS)'; test -n "$(hcryptoincludedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(hcryptoincludedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(hcryptoincludedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(hcryptoincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -2378,15 +3066,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -2395,101 +3079,264 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_LTLIBRARIES) $(check_PROGRAMS) $(check_SCRIPTS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+destest.log: destest$(EXEEXT)
+ @p='destest$(EXEEXT)'; \
+ b='destest'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+mdtest.log: mdtest$(EXEEXT)
+ @p='mdtest$(EXEEXT)'; \
+ b='mdtest'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+rc2test.log: rc2test$(EXEEXT)
+ @p='rc2test$(EXEEXT)'; \
+ b='rc2test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+rctest.log: rctest$(EXEEXT)
+ @p='rctest$(EXEEXT)'; \
+ b='rctest'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_bn.log: test_bn$(EXEEXT)
+ @p='test_bn$(EXEEXT)'; \
+ b='test_bn'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_bulk.log: test_bulk$(EXEEXT)
+ @p='test_bulk$(EXEEXT)'; \
+ b='test_bulk'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_cipher.log: test_cipher$(EXEEXT)
+ @p='test_cipher$(EXEEXT)'; \
+ b='test_cipher'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_engine_dso.log: test_engine_dso$(EXEEXT)
+ @p='test_engine_dso$(EXEEXT)'; \
+ b='test_engine_dso'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_hmac.log: test_hmac$(EXEEXT)
+ @p='test_hmac$(EXEEXT)'; \
+ b='test_hmac'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_pkcs12.log: test_pkcs12$(EXEEXT)
+ @p='test_pkcs12$(EXEEXT)'; \
+ b='test_pkcs12'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_pkcs5.log: test_pkcs5$(EXEEXT)
+ @p='test_pkcs5$(EXEEXT)'; \
+ b='test_pkcs5'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_crypto.log: test_crypto
+ @p='test_crypto'; \
+ b='test_crypto'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -2544,11 +3391,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -2556,6 +3411,8 @@ clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f libtommath/$(DEPDIR)/$(am__dirstamp)
+ -rm -f libtommath/$(am__dirstamp)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -2567,7 +3424,7 @@ clean-am: clean-checkLTLIBRARIES clean-checkPROGRAMS clean-generic \
mostlyclean-am
distclean: distclean-am
- -rm -rf ./$(DEPDIR)
+ -rm -rf ./$(DEPDIR) libtommath/$(DEPDIR)
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -2591,9 +3448,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -2615,7 +3471,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -rf ./$(DEPDIR)
+ -rm -rf ./$(DEPDIR) libtommath/$(DEPDIR)
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -2635,43 +3491,56 @@ ps-am:
uninstall-am: uninstall-hcryptoincludeHEADERS uninstall-libLTLIBRARIES
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-checkLTLIBRARIES clean-checkPROGRAMS \
- clean-generic clean-libLTLIBRARIES clean-libtool \
- clean-noinstPROGRAMS ctags dist-hook distclean \
- distclean-compile distclean-generic distclean-libtool \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am \
- install-data-hook install-dvi install-dvi-am install-exec \
- install-exec-am install-exec-hook \
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-checkLTLIBRARIES \
+ clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
install-hcryptoincludeHEADERS install-html install-html-am \
install-info install-info-am install-libLTLIBRARIES \
install-man install-pdf install-pdf-am install-ps \
install-ps-am install-strip installcheck installcheck-am \
installdirs maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
- uninstall-am uninstall-hcryptoincludeHEADERS uninstall-hook \
- uninstall-libLTLIBRARIES
+ mostlyclean-libtool pdf pdf-am ps ps-am recheck tags tags-am \
+ uninstall uninstall-am uninstall-hcryptoincludeHEADERS \
+ uninstall-hook uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
-install-exec-hook: install-suid-programs
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -2679,7 +3548,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -2736,6 +3605,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -2778,6 +3649,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -2791,13 +3675,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/hcrypto/NTMakefile b/lib/hcrypto/NTMakefile
index eced950c604c..8063ca616e1c 100644
--- a/lib/hcrypto/NTMakefile
+++ b/lib/hcrypto/NTMakefile
@@ -1,6 +1,6 @@
########################################################################
#
-# Copyright (c) 2009, Secure Endpoints Inc.
+# Copyright (c) 2009-2016, Secure Endpoints Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -33,10 +33,21 @@ SUBDIRS=libtommath
RELDIR=lib\hcrypto
-intcflags=-DKRB5 -DASN1_LIB -I$(HCRYPTOINCLUDEDIR) -DUSE_HCRYPTO_LTM=1
-
!include ../../windows/NTMakefile.w32
+HCRYPTOINCLUDEDIR = $(INCDIR)\hcrypto
+
+HEIMBASEDIR = $(SRC)\lib\base
+
+HX509DIR = $(SRC)\lib\hx509
+
+!ifdef INCLUDE_openssl_crypto
+openssl_inc=-I$(INCLUDE_openssl_crypto)
+!endif
+
+intcflags=-DKRB5 -DASN1_LIB -I$(HCRYPTOINCLUDEDIR) -DUSE_HCRYPTO_LTM=1 \
+ -I$(HX509DIR) $(openssl_inc)
+
# Do dependencies first
all:: subdirs
@@ -47,8 +58,6 @@ test:: test-subdirs
# Include files
-HCRYPTOINCLUDEDIR=$(INCDIR)\hcrypto
-
INCFILES= \
$(HCRYPTOINCLUDEDIR)\aes.h \
$(HCRYPTOINCLUDEDIR)\bn.h \
@@ -62,6 +71,10 @@ INCFILES= \
$(HCRYPTOINCLUDEDIR)\evp.h \
$(HCRYPTOINCLUDEDIR)\evp-hcrypto.h \
$(HCRYPTOINCLUDEDIR)\evp-cc.h \
+ $(HCRYPTOINCLUDEDIR)\evp-openssl.h \
+ $(HCRYPTOINCLUDEDIR)\evp-pkcs11.h \
+ $(HCRYPTOINCLUDEDIR)\evp-wincng.h \
+ $(HCRYPTOINCLUDEDIR)\evp-w32.h \
$(HCRYPTOINCLUDEDIR)\hmac.h \
$(HCRYPTOINCLUDEDIR)\md2.h \
$(HCRYPTOINCLUDEDIR)\md4.h \
@@ -73,7 +86,8 @@ INCFILES= \
$(HCRYPTOINCLUDEDIR)\rc4.h \
$(HCRYPTOINCLUDEDIR)\rsa.h \
$(HCRYPTOINCLUDEDIR)\sha.h \
- $(HCRYPTOINCLUDEDIR)\ui.h
+ $(HCRYPTOINCLUDEDIR)\ui.h \
+ $(HCRYPTOINCLUDEDIR)\undef.h
mkincdir:
!if !exist($(HCRYPTOINCLUDEDIR))
@@ -103,6 +117,10 @@ libhcrypto_OBJs = \
$(OBJ)\evp.obj \
$(OBJ)\evp-hcrypto.obj \
$(OBJ)\evp-cc.obj \
+ $(OBJ)\evp-openssl.obj \
+ $(OBJ)\evp-pkcs11.obj \
+ $(OBJ)\evp-wincng.obj \
+ $(OBJ)\evp-w32.obj \
$(OBJ)\engine.obj \
$(OBJ)\hmac.obj \
$(OBJ)\md2.obj \
@@ -150,6 +168,7 @@ test-binaries: \
$(OBJ)\rc2test.exe \
$(OBJ)\rctest.exe \
$(OBJ)\test_bn.exe \
+ $(OBJ)\test_bulk.exe \
$(OBJ)\test_cipher.exe \
$(OBJ)\test_engine_dso.exe \
$(OBJ)\test_hmac.exe \
@@ -160,59 +179,63 @@ test-binaries: \
$(OBJ)\test_rand.exe \
$(OBJ)\test_crypto.sh
-$(OBJ)\destest.exe: $(OBJ)\destest.obj $(TESTLIB) $(LIBROKEN)
+$(OBJ)\destest.exe: $(OBJ)\destest.obj $(TESTLIB) $(LIBROKEN) $(LIBHEIMBASE)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
+$(OBJ)\example_evp_cipher.exe: $(OBJ)\example_evp_cipher.obj $(TESTLIB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\example_evp_cipher.exe: $(OBJ)\example_evp_cipher.obj $(TESTLIB) $(LIBHEIMDAL) $(LIBROKEN)
+$(OBJ)\mdtest.exe: $(OBJ)\mdtest.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE) $(OBJ)\sha512.obj
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\mdtest.exe: $(OBJ)\mdtest.obj $(LIBHEIMDAL) $(LIBROKEN) $(OBJ)\sha512.obj
+$(OBJ)\rc2test.exe: $(OBJ)\rc2test.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\rc2test.exe: $(OBJ)\rc2test.obj $(LIBHEIMDAL) $(LIBROKEN)
+$(OBJ)\rctest.exe: $(OBJ)\rctest.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\rctest.exe: $(OBJ)\rctest.obj $(LIBHEIMDAL) $(LIBROKEN)
+$(OBJ)\test_bn.exe: $(OBJ)\test_bn.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_bn.exe: $(OBJ)\test_bn.obj $(LIBHEIMDAL) $(LIBROKEN)
+$(OBJ)\test_bulk.exe: $(OBJ)\test_bulk.obj $(TESTLIB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE) $(LIBVERS)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_cipher.exe: $(OBJ)\test_cipher.obj $(TESTLIB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS)
+$(OBJ)\test_cipher.exe: $(OBJ)\test_cipher.obj $(TESTLIB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE) $(LIBVERS)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_engine_dso.exe: $(OBJ)\test_engine_dso.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS)
+$(OBJ)\test_engine_dso.exe: $(OBJ)\test_engine_dso.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE) $(LIBVERS)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_hmac.exe: $(OBJ)\test_hmac.obj $(LIBHEIMDAL) $(LIBROKEN)
+$(OBJ)\test_hmac.exe: $(OBJ)\test_hmac.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_pkcs5.exe: $(OBJ)\test_pkcs5.obj $(LIBHEIMDAL) $(LIBROKEN)
+$(OBJ)\test_pkcs5.exe: $(OBJ)\test_pkcs5.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_pkcs12.exe: $(OBJ)\test_pkcs12.obj $(LIBHEIMDAL) $(LIBROKEN)
+$(OBJ)\test_pkcs12.exe: $(OBJ)\test_pkcs12.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_rsa.exe: $(OBJ)\test_rsa.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS)
+$(OBJ)\test_rsa.exe: $(OBJ)\test_rsa.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE) $(LIBVERS)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_dh.exe: $(OBJ)\test_dh.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS)
+$(OBJ)\test_dh.exe: $(OBJ)\test_dh.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE) $(LIBVERS)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_rand.exe: $(OBJ)\test_rand.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS)
+$(OBJ)\test_rand.exe: $(OBJ)\test_rand.obj $(LIBHEIMDAL) $(LIBROKEN) $(LIBHEIMBASE) $(LIBVERS)
$(EXECONLINK)
$(EXEPREP_NODIST)
@@ -224,20 +247,22 @@ $(OBJ)\test_crypto.sh: test_crypto.in NTMakefile
test-run:
cd $(OBJ)
!ifdef SH
- $(SH) test_crypto.sh
+ -$(SH) test_crypto.sh
!endif
- destest.exe
- mdtest.exe
- rc2test.exe
- rctest.exe
- test_bn.exe
- test_cipher.exe
- test_engine_dso.exe
- test_hmac.exe
- test_pkcs5.exe
- test_pkcs12.exe
- test_rsa.exe
- test_dh.exe
+ -destest.exe
+ -mdtest.exe
+ -rc2test.exe
+ -rctest.exe
+ -test_bn.exe
+ -test_bulk.exe --provider=hcrypto
+ -test_bulk.exe --provider=w32crypto
+ -test_cipher.exe
+ -test_engine_dso.exe
+ -test_hmac.exe
+ -test_pkcs5.exe
+ -test_pkcs12.exe
+ -test_rsa.exe
+ -test_dh.exe
cd $(SRCDIR)
test:: $(TESTLIB) test-binaries test-run
diff --git a/lib/hcrypto/aes.c b/lib/hcrypto/aes.c
index 77847e460e3b..2e15d5d0e829 100644
--- a/lib/hcrypto/aes.c
+++ b/lib/hcrypto/aes.c
@@ -31,15 +31,13 @@
* SUCH DAMAGE.
*/
-#include "config.h"
-
+#include <config.h>
+#include <roken.h>
#ifdef KRB5
#include <krb5-types.h>
#endif
-#include <string.h>
-
#include "rijndael-alg-fst.h"
#include "aes.h"
diff --git a/lib/hcrypto/bn.c b/lib/hcrypto/bn.c
index 17c1ec79b7f0..e7d5b0473716 100644
--- a/lib/hcrypto/bn.c
+++ b/lib/hcrypto/bn.c
@@ -32,15 +32,9 @@
*/
#include <config.h>
-
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <limits.h>
+#include <roken.h>
#include <krb5-types.h>
-#include <roken.h>
#include <rfc2459_asn1.h> /* XXX */
#include <der.h>
diff --git a/lib/hcrypto/bn.h b/lib/hcrypto/bn.h
index 2fa25ac1a08a..727b919a3e65 100644
--- a/lib/hcrypto/bn.h
+++ b/lib/hcrypto/bn.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * Copyright (c) 2006-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -68,6 +68,13 @@
#define BN_CTX_start hc_BN_CTX_start
#define BN_CTX_end hc_BN_CTX_end
+#define BIGNUM hc_BIGNUM
+#define BN_GENCB hc_BN_GENCB
+#define BN_CTX hc_BN_CTX
+#define BN_BLINDING hc_BN_BLINDING
+#define BN_MONT_CTX hc_BN_MONT_CTX
+
+
/*
*
*/
diff --git a/lib/hcrypto/camellia-ntt.c b/lib/hcrypto/camellia-ntt.c
index 4a27a94aa4f9..2555003cb148 100644
--- a/lib/hcrypto/camellia-ntt.c
+++ b/lib/hcrypto/camellia-ntt.c
@@ -30,16 +30,12 @@
* http://info.isl.ntt.co.jp/crypt/eng/camellia/specifications.html
*/
-#include "config.h"
-
-#include <string.h>
-#include <stdlib.h>
+#include <config.h>
+#include <roken.h>
#include <krb5-types.h>
#include "camellia-ntt.h"
-#include <roken.h>
-
/* key constants */
#define CAMELLIA_SIGMA1L (0xA09E667FL)
diff --git a/lib/hcrypto/camellia.c b/lib/hcrypto/camellia.c
index c88822db5c7d..bc861a07d285 100644
--- a/lib/hcrypto/camellia.c
+++ b/lib/hcrypto/camellia.c
@@ -31,19 +31,16 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#ifdef KRB5
#include <krb5-types.h>
#endif
-#include <string.h>
-
#include "camellia-ntt.h"
#include "camellia.h"
-#include <roken.h>
-
int
CAMELLIA_set_key(const unsigned char *userkey,
const int bits, CAMELLIA_KEY *key)
diff --git a/lib/hcrypto/common.c b/lib/hcrypto/common.c
index 136bf1db101f..cf30304c4c82 100644
--- a/lib/hcrypto/common.c
+++ b/lib/hcrypto/common.c
@@ -34,11 +34,7 @@
*/
#include <config.h>
-
-#include <errno.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <roken.h>
#include <krb5-types.h>
#include <rfc2459_asn1.h>
diff --git a/lib/hcrypto/des.c b/lib/hcrypto/des.c
index 2e3192bff8ad..c16f33ba2685 100644
--- a/lib/hcrypto/des.c
+++ b/lib/hcrypto/des.c
@@ -83,17 +83,12 @@
*/
#include <config.h>
+#include <roken.h>
#define HC_DEPRECATED
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <krb5-types.h>
#include <assert.h>
-#include <roken.h>
-
#include "des.h"
#include "ui.h"
diff --git a/lib/hcrypto/destest.c b/lib/hcrypto/destest.c
index 2f70d8bee87e..0761ded96836 100644
--- a/lib/hcrypto/destest.c
+++ b/lib/hcrypto/destest.c
@@ -31,15 +31,13 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#ifdef KRB5
#include <krb5-types.h>
#endif
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
diff --git a/lib/hcrypto/dh-ltm.c b/lib/hcrypto/dh-ltm.c
index 6af43cf044ee..774f0e1176ab 100644
--- a/lib/hcrypto/dh-ltm.c
+++ b/lib/hcrypto/dh-ltm.c
@@ -34,13 +34,10 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#include <roken.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <dh.h>
-#include <roken.h>
-
#include "tommath.h"
static void
@@ -108,8 +105,10 @@ ltm_dh_generate_key(DH *dh)
return 0;
}
}
- if (dh->pub_key)
+ if (dh->pub_key) {
BN_free(dh->pub_key);
+ dh->pub_key = NULL;
+ }
mp_init_multi(&pub, &priv_key, &g, &p, NULL);
diff --git a/lib/hcrypto/dh-tfm.c b/lib/hcrypto/dh-tfm.c
new file mode 100644
index 000000000000..dce8a9fb2373
--- /dev/null
+++ b/lib/hcrypto/dh-tfm.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <roken.h>
+
+#include <dh.h>
+
+#ifdef USE_HCRYPTO_TFM
+
+#include "tfm.h"
+
+static void
+BN2mpz(fp_int *s, const BIGNUM *bn)
+{
+ size_t len;
+ void *p;
+
+ len = BN_num_bytes(bn);
+ p = malloc(len);
+ BN_bn2bin(bn, p);
+ fp_read_unsigned_bin(s, p, len);
+ free(p);
+}
+
+
+static BIGNUM *
+mpz2BN(fp_int *s)
+{
+ size_t size;
+ BIGNUM *bn;
+ void *p;
+
+ size = fp_unsigned_bin_size(s);
+ p = malloc(size);
+ if (p == NULL && size != 0)
+ return NULL;
+ fp_to_unsigned_bin(s, p);
+
+ bn = BN_bin2bn(p, size, NULL);
+ free(p);
+ return bn;
+}
+
+/*
+ *
+ */
+
+#define DH_NUM_TRIES 10
+
+static int
+tfm_dh_generate_key(DH *dh)
+{
+ fp_int pub, priv_key, g, p;
+ int have_private_key = (dh->priv_key != NULL);
+ int codes, times = 0;
+ int res;
+
+ if (dh->p == NULL || dh->g == NULL)
+ return 0;
+
+ while (times++ < DH_NUM_TRIES) {
+ if (!have_private_key) {
+ size_t bits = BN_num_bits(dh->p);
+
+ if (dh->priv_key)
+ BN_free(dh->priv_key);
+
+ dh->priv_key = BN_new();
+ if (dh->priv_key == NULL)
+ return 0;
+ if (!BN_rand(dh->priv_key, bits - 1, 0, 0)) {
+ BN_clear_free(dh->priv_key);
+ dh->priv_key = NULL;
+ return 0;
+ }
+ }
+ if (dh->pub_key)
+ BN_free(dh->pub_key);
+
+ fp_init_multi(&pub, &priv_key, &g, &p, NULL);
+
+ BN2mpz(&priv_key, dh->priv_key);
+ BN2mpz(&g, dh->g);
+ BN2mpz(&p, dh->p);
+
+ res = fp_exptmod(&g, &priv_key, &p, &pub);
+
+ fp_zero(&priv_key);
+ fp_zero(&g);
+ fp_zero(&p);
+ if (res != 0)
+ continue;
+
+ dh->pub_key = mpz2BN(&pub);
+ fp_zero(&pub);
+ if (dh->pub_key == NULL)
+ return 0;
+
+ if (DH_check_pubkey(dh, dh->pub_key, &codes) && codes == 0)
+ break;
+ if (have_private_key)
+ return 0;
+ }
+
+ if (times >= DH_NUM_TRIES) {
+ if (!have_private_key && dh->priv_key) {
+ BN_free(dh->priv_key);
+ dh->priv_key = NULL;
+ }
+ if (dh->pub_key) {
+ BN_free(dh->pub_key);
+ dh->pub_key = NULL;
+ }
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+tfm_dh_compute_key(unsigned char *shared, const BIGNUM * pub, DH *dh)
+{
+ fp_int s, priv_key, p, peer_pub;
+ size_t size = 0;
+ int ret;
+
+ if (dh->pub_key == NULL || dh->g == NULL || dh->priv_key == NULL)
+ return -1;
+
+ fp_init(&p);
+ BN2mpz(&p, dh->p);
+
+ fp_init(&peer_pub);
+ BN2mpz(&peer_pub, pub);
+
+ /* check if peers pubkey is reasonable */
+ if (fp_isneg(&peer_pub)
+ || fp_cmp(&peer_pub, &p) >= 0
+ || fp_cmp_d(&peer_pub, 1) <= 0)
+ {
+ fp_zero(&p);
+ fp_zero(&peer_pub);
+ return -1;
+ }
+
+ fp_init(&priv_key);
+ BN2mpz(&priv_key, dh->priv_key);
+
+ fp_init(&s);
+
+ ret = fp_exptmod(&peer_pub, &priv_key, &p, &s);
+
+ fp_zero(&p);
+ fp_zero(&peer_pub);
+ fp_zero(&priv_key);
+
+ if (ret != 0)
+ return -1;
+
+ size = fp_unsigned_bin_size(&s);
+ fp_to_unsigned_bin(&s, shared);
+ fp_zero(&s);
+
+ return size;
+}
+
+static int
+tfm_dh_generate_params(DH *dh, int a, int b, BN_GENCB *callback)
+{
+ /* groups should already be known, we don't care about this */
+ return 0;
+}
+
+static int
+tfm_dh_init(DH *dh)
+{
+ return 1;
+}
+
+static int
+tfm_dh_finish(DH *dh)
+{
+ return 1;
+}
+
+
+/*
+ *
+ */
+
+const DH_METHOD _hc_dh_tfm_method = {
+ "hcrypto tfm DH",
+ tfm_dh_generate_key,
+ tfm_dh_compute_key,
+ NULL,
+ tfm_dh_init,
+ tfm_dh_finish,
+ 0,
+ NULL,
+ tfm_dh_generate_params
+};
+
+/**
+ * DH implementation using tfm.
+ *
+ * @return the DH_METHOD for the DH implementation using tfm.
+ *
+ * @ingroup hcrypto_dh
+ */
+
+const DH_METHOD *
+DH_tfm_method(void)
+{
+ return &_hc_dh_tfm_method;
+}
+
+#endif
diff --git a/lib/hcrypto/dh.c b/lib/hcrypto/dh.c
index e1f82bfd3bba..ef717d50c0a6 100644
--- a/lib/hcrypto/dh.c
+++ b/lib/hcrypto/dh.c
@@ -34,16 +34,13 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#include <roken.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <krb5-types.h>
#include <rfc2459_asn1.h>
#include <dh.h>
-#include <roken.h>
-
/**
* @page page_dh DH - Diffie-Hellman key exchange
*
diff --git a/lib/hcrypto/dh.h b/lib/hcrypto/dh.h
index 637f218bcf82..61868054b65b 100644
--- a/lib/hcrypto/dh.h
+++ b/lib/hcrypto/dh.h
@@ -39,6 +39,8 @@
#define _HEIM_DH_H 1
/* symbol renaming */
+#define DH hc_DH
+#define DH_METHOD hc_DH_METHOD
#define DH_null_method hc_DH_null_method
#define DH_tfm_method hc_DH_tfm_method
#define DH_ltm_method hc_DH_ltm_method
diff --git a/lib/hcrypto/dsa.c b/lib/hcrypto/dsa.c
index a5bdbabad165..bbb34601c31f 100644
--- a/lib/hcrypto/dsa.c
+++ b/lib/hcrypto/dsa.c
@@ -32,13 +32,10 @@
*/
#include <config.h>
+#include <roken.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <dsa.h>
-#include <roken.h>
-
/*
*
*/
@@ -89,7 +86,16 @@ DSA_up_ref(DSA *dsa)
*/
static const DSA_METHOD dsa_null_method = {
- "hcrypto null DSA"
+ "hcrypto null DSA",
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ NULL
};
const DSA_METHOD *
diff --git a/lib/hcrypto/dsa.h b/lib/hcrypto/dsa.h
index 686d05087448..a8f087f79af0 100644
--- a/lib/hcrypto/dsa.h
+++ b/lib/hcrypto/dsa.h
@@ -41,6 +41,8 @@
#include <hcrypto/bn.h>
/* symbol renaming */
+#define DSA hc_DSA
+#define DSA_METHOD hc_DSA_METHOD
#define DSA_null_method hc_DSA_null_method
#define DSA_new hc_DSA_new
#define DSA_free hc_DSA_free
diff --git a/lib/hcrypto/ec.h b/lib/hcrypto/ec.h
index d92a46efc239..726deec6169f 100644
--- a/lib/hcrypto/ec.h
+++ b/lib/hcrypto/ec.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2009 Kungliga Tekniska Högskolan
+ * Copyright (c) 2009-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -34,6 +34,10 @@
#ifndef HEIM_EC_H
#define HEIM_EC_H 1
+#define EC_KEY hc_EC_KEY
+#define EC_GROUP hc_EC_GROUP
+#define EC_GROUP_ID hc_EC_GROUP_ID
+#define EC_GROUP_ID_s hc_EC_GROUP_ID_s
#define EC_GROUP_get_degree hc_EC_GROUP_get_degree
#define EC_KEY_get0_group hc_EC_KEY_get0_group
#define EC_GROUP_get_order hc_EC_GROUP_get_order
diff --git a/lib/hcrypto/engine.c b/lib/hcrypto/engine.c
index 3b22e5620112..04ba6b645f66 100644
--- a/lib/hcrypto/engine.c
+++ b/lib/hcrypto/engine.c
@@ -32,10 +32,7 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <roken.h>
#include <engine.h>
@@ -88,7 +85,7 @@ ENGINE_finish(ENGINE *engine)
if(engine->destroy)
(*engine->destroy)(engine);
- memset(engine, 0, sizeof(engine));
+ memset(engine, 0, sizeof(*engine));
engine->references = -1;
diff --git a/lib/hcrypto/engine.h b/lib/hcrypto/engine.h
index 7fd8f3e1e5a2..e6fc5b92e415 100644
--- a/lib/hcrypto/engine.h
+++ b/lib/hcrypto/engine.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * Copyright (c) 2006-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -62,6 +62,8 @@
#define ENGINE_set_default_DH hc_ENGINE_set_default_DH
#define ENGINE_set_default_RSA hc_ENGINE_set_default_RSA
+#define ENGINE hc_ENGINE
+
/*
*
*/
diff --git a/lib/hcrypto/evp-cc.c b/lib/hcrypto/evp-cc.c
index bd084a25e20d..4a377f1c3bb1 100644
--- a/lib/hcrypto/evp-cc.c
+++ b/lib/hcrypto/evp-cc.c
@@ -37,12 +37,9 @@
#ifdef __APPLE__
-#include "config.h"
+#include <config.h>
+#include <roken.h>
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <assert.h>
#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H
@@ -53,6 +50,7 @@
#endif
#include <evp.h>
+#include <evp-hcrypto.h>
#include <evp-cc.h>
/*
@@ -88,43 +86,6 @@ cc_do_cipher(EVP_CIPHER_CTX *ctx,
}
static int
-cc_do_cfb8_cipher(EVP_CIPHER_CTX *ctx,
- unsigned char *out,
- const unsigned char *in,
- unsigned int size)
-{
- struct cc_key *cc = ctx->cipher_data;
- CCCryptorStatus ret;
- size_t moved;
- unsigned int i;
-
- for (i = 0; i < size; i++) {
- unsigned char oiv[EVP_MAX_IV_LENGTH + 1];
-
- assert(ctx->cipher->iv_len + 1 <= sizeof(oiv));
- memcpy(oiv, ctx->iv, ctx->cipher->iv_len);
-
- ret = CCCryptorUpdate(cc->href, ctx->iv, ctx->cipher->iv_len,
- ctx->iv, ctx->cipher->iv_len, &moved);
- if (ret)
- return 0;
-
- if (moved != ctx->cipher->iv_len)
- return 0;
-
- if (!ctx->encrypt)
- oiv[ctx->cipher->iv_len] = in[i];
- out[i] = in[i] ^ ctx->iv[0];
- if (ctx->encrypt)
- oiv[ctx->cipher->iv_len] = out[i];
-
- memcpy(ctx->iv, &oiv[1], ctx->cipher->iv_len);
- }
-
- return 1;
-}
-
-static int
cc_cleanup(EVP_CIPHER_CTX *ctx)
{
struct cc_key *cc = ctx->cipher_data;
@@ -134,10 +95,13 @@ cc_cleanup(EVP_CIPHER_CTX *ctx)
}
static int
-init_cc_key(int encp, CCAlgorithm alg, CCOptions opts, const void *key,
- size_t keylen, const void *iv, CCCryptorRef *ref)
+init_cc_key(int encp, unsigned long flags,
+ CCAlgorithm alg, const void *key, size_t keylen,
+ const void *iv, CCCryptorRef *ref)
{
CCOperation op = encp ? kCCEncrypt : kCCDecrypt;
+ CCMode mode;
+ CCModeOptions options = 0;
CCCryptorStatus ret;
if (*ref) {
@@ -148,9 +112,26 @@ init_cc_key(int encp, CCAlgorithm alg, CCOptions opts, const void *key,
CCCryptorRelease(*ref);
}
- ret = CCCryptorCreate(op, alg, opts, key, keylen, iv, ref);
- if (ret)
- return 0;
+ if (key) {
+ switch (flags & EVP_CIPH_MODE) {
+ case EVP_CIPH_STREAM_CIPHER:
+ mode = kCCModeRC4;
+ break;
+ case EVP_CIPH_CFB8_MODE:
+ mode = kCCModeCFB8;
+ break;
+ default:
+ mode = kCCModeCBC;
+ break;
+ }
+
+ ret = CCCryptorCreateWithMode(op, mode, alg, ccNoPadding,
+ iv, key, keylen, NULL, 0, 0,
+ options, ref);
+ if (ret)
+ return 0;
+ }
+
return 1;
}
@@ -161,13 +142,14 @@ cc_des_ede3_cbc_init(EVP_CIPHER_CTX *ctx,
int encp)
{
struct cc_key *cc = ctx->cipher_data;
- return init_cc_key(encp, kCCAlgorithm3DES, 0, key, kCCKeySize3DES, iv, &cc->href);
+ return init_cc_key(encp, ctx->cipher->flags, kCCAlgorithm3DES,
+ key, kCCKeySize3DES, iv, &cc->href);
}
#endif /* HAVE_COMMONCRYPTO_COMMONCRYPTOR_H */
/**
- * The tripple DES cipher type (Apple CommonCrypto provider)
+ * The triple DES cipher type (Apple CommonCrypto provider)
*
* @return the DES-EDE3-CBC EVP_CIPHER pointer.
*
@@ -194,6 +176,8 @@ EVP_cc_des_ede3_cbc(void)
NULL
};
return &des_ede3_cbc;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_des_ede3_cbc();
#else
return NULL;
#endif
@@ -211,7 +195,8 @@ cc_des_cbc_init(EVP_CIPHER_CTX *ctx,
int encp)
{
struct cc_key *cc = ctx->cipher_data;
- return init_cc_key(encp, kCCAlgorithmDES, 0, key, kCCBlockSizeDES, iv, &cc->href);
+ return init_cc_key(encp, ctx->cipher->flags, kCCAlgorithmDES,
+ key, kCCBlockSizeDES, iv, &cc->href);
}
#endif
@@ -243,6 +228,8 @@ EVP_cc_des_cbc(void)
NULL
};
return &des_ede3_cbc;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_des_cbc();
#else
return NULL;
#endif
@@ -260,7 +247,8 @@ cc_aes_cbc_init(EVP_CIPHER_CTX *ctx,
int encp)
{
struct cc_key *cc = ctx->cipher_data;
- return init_cc_key(encp, kCCAlgorithmAES128, 0, key, ctx->cipher->key_len, iv, &cc->href);
+ return init_cc_key(encp, ctx->cipher->flags, kCCAlgorithmAES128,
+ key, ctx->cipher->key_len, iv, &cc->href);
}
#endif
@@ -292,6 +280,8 @@ EVP_cc_aes_128_cbc(void)
NULL
};
return &c;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_aes_128_cbc();
#else
return NULL;
#endif
@@ -325,6 +315,8 @@ EVP_cc_aes_192_cbc(void)
NULL
};
return &c;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_aes_192_cbc();
#else
return NULL;
#endif
@@ -358,6 +350,8 @@ EVP_cc_aes_256_cbc(void)
NULL
};
return &c;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_aes_256_cbc();
#else
return NULL;
#endif
@@ -375,8 +369,7 @@ cc_aes_cfb8_init(EVP_CIPHER_CTX *ctx,
int encp)
{
struct cc_key *cc = ctx->cipher_data;
- memcpy(ctx->iv, iv, ctx->cipher->iv_len);
- return init_cc_key(1, kCCAlgorithmAES128, kCCOptionECBMode,
+ return init_cc_key(encp, ctx->cipher->flags, kCCAlgorithmAES128,
key, ctx->cipher->key_len, NULL, &cc->href);
}
#endif
@@ -400,7 +393,7 @@ EVP_cc_aes_128_cfb8(void)
kCCBlockSizeAES128,
EVP_CIPH_CFB8_MODE|EVP_CIPH_ALWAYS_CALL_INIT,
cc_aes_cfb8_init,
- cc_do_cfb8_cipher,
+ cc_do_cipher,
cc_cleanup,
sizeof(struct cc_key),
NULL,
@@ -409,6 +402,8 @@ EVP_cc_aes_128_cfb8(void)
NULL
};
return &c;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_aes_128_cfb8();
#else
return NULL;
#endif
@@ -433,7 +428,7 @@ EVP_cc_aes_192_cfb8(void)
kCCBlockSizeAES128,
EVP_CIPH_CFB8_MODE|EVP_CIPH_ALWAYS_CALL_INIT,
cc_aes_cfb8_init,
- cc_do_cfb8_cipher,
+ cc_do_cipher,
cc_cleanup,
sizeof(struct cc_key),
NULL,
@@ -442,6 +437,8 @@ EVP_cc_aes_192_cfb8(void)
NULL
};
return &c;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_aes_192_cfb8();
#else
return NULL;
#endif
@@ -466,7 +463,7 @@ EVP_cc_aes_256_cfb8(void)
kCCBlockSizeAES128,
EVP_CIPH_CFB8_MODE|EVP_CIPH_ALWAYS_CALL_INIT,
cc_aes_cfb8_init,
- cc_do_cfb8_cipher,
+ cc_do_cipher,
cc_cleanup,
sizeof(struct cc_key),
NULL,
@@ -475,6 +472,8 @@ EVP_cc_aes_256_cfb8(void)
NULL
};
return &c;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_aes_256_cfb8();
#else
return NULL;
#endif
@@ -492,7 +491,8 @@ cc_rc2_cbc_init(EVP_CIPHER_CTX *ctx,
int encp)
{
struct cc_key *cc = ctx->cipher_data;
- return init_cc_key(encp, kCCAlgorithmRC2, 0, key, ctx->cipher->key_len, iv, &cc->href);
+ return init_cc_key(encp, ctx->cipher->flags, kCCAlgorithmRC2,
+ key, ctx->cipher->key_len, iv, &cc->href);
}
#endif
@@ -525,6 +525,8 @@ EVP_cc_rc2_cbc(void)
NULL
};
return &rc2_cbc;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_rc2_cbc();
#else
return NULL;
#endif
@@ -559,6 +561,8 @@ EVP_cc_rc2_40_cbc(void)
NULL
};
return &rc2_40_cbc;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_rc2_40_cbc();
#else
return NULL;
#endif
@@ -594,6 +598,8 @@ EVP_cc_rc2_64_cbc(void)
NULL
};
return &rc2_64_cbc;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_rc2_64_cbc();
#else
return NULL;
#endif
@@ -619,6 +625,8 @@ EVP_cc_md2(void)
(hc_evp_md_cleanup)NULL
};
return &md2;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_md2();
#else
return NULL;
#endif
@@ -644,6 +652,8 @@ EVP_cc_md4(void)
(hc_evp_md_cleanup)NULL
};
return &md4;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_md4();
#else
return NULL;
#endif
@@ -669,6 +679,8 @@ EVP_cc_md5(void)
(hc_evp_md_cleanup)NULL
};
return &md5;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_md5();
#else
return NULL;
#endif
@@ -694,6 +706,8 @@ EVP_cc_sha1(void)
(hc_evp_md_cleanup)NULL
};
return &sha1;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_sha1();
#else
return NULL;
#endif
@@ -719,6 +733,62 @@ EVP_cc_sha256(void)
(hc_evp_md_cleanup)NULL
};
return &sha256;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_sha256();
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * The CommonCrypto sha384 provider
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_cc_sha384(void)
+{
+#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H
+ static const struct hc_evp_md sha384 = {
+ CC_SHA384_DIGEST_LENGTH,
+ CC_SHA384_BLOCK_BYTES,
+ sizeof(CC_SHA512_CTX),
+ (hc_evp_md_init)CC_SHA384_Init,
+ (hc_evp_md_update)CC_SHA384_Update,
+ (hc_evp_md_final)CC_SHA384_Final,
+ (hc_evp_md_cleanup)NULL
+ };
+ return &sha384;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_sha384();
+#else
+ return NULL;
+#endif
+}
+
+/**
+ * The CommonCrypto sha512 provider
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_MD *
+EVP_cc_sha512(void)
+{
+#ifdef HAVE_COMMONCRYPTO_COMMONDIGEST_H
+ static const struct hc_evp_md sha512 = {
+ CC_SHA512_DIGEST_LENGTH,
+ CC_SHA512_BLOCK_BYTES,
+ sizeof(CC_SHA512_CTX),
+ (hc_evp_md_init)CC_SHA512_Init,
+ (hc_evp_md_update)CC_SHA512_Update,
+ (hc_evp_md_final)CC_SHA512_Final,
+ (hc_evp_md_cleanup)NULL
+ };
+ return &sha512;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_sha512();
#else
return NULL;
#endif
@@ -735,7 +805,11 @@ EVP_cc_sha256(void)
const EVP_CIPHER *
EVP_cc_camellia_128_cbc(void)
{
+#if HCRYPTO_FALLBACK
+ return EVP_hcrypto_camellia_128_cbc();
+#else
return NULL;
+#endif
}
/**
@@ -749,7 +823,11 @@ EVP_cc_camellia_128_cbc(void)
const EVP_CIPHER *
EVP_cc_camellia_192_cbc(void)
{
+#if HCRYPTO_FALLBACK
+ return EVP_hcrypto_camellia_192_cbc();
+#else
return NULL;
+#endif
}
/**
@@ -763,7 +841,11 @@ EVP_cc_camellia_192_cbc(void)
const EVP_CIPHER *
EVP_cc_camellia_256_cbc(void)
{
+#if HCRYPTO_FALLBACK
+ return EVP_hcrypto_camellia_256_cbc();
+#else
return NULL;
+#endif
}
#ifdef HAVE_COMMONCRYPTO_COMMONCRYPTOR_H
@@ -779,7 +861,8 @@ cc_rc4_init(EVP_CIPHER_CTX *ctx,
int encp)
{
struct cc_key *cc = ctx->cipher_data;
- return init_cc_key(encp, kCCAlgorithmRC4, 0, key, ctx->key_len, iv, &cc->href);
+ return init_cc_key(encp, ctx->cipher->flags, kCCAlgorithmRC4,
+ key, ctx->key_len, iv, &cc->href);
}
#endif
@@ -813,6 +896,8 @@ EVP_cc_rc4(void)
NULL
};
return &rc4;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_rc4();
#else
return NULL;
#endif
@@ -847,6 +932,8 @@ EVP_cc_rc4_40(void)
NULL
};
return &rc4_40;
+#elif HCRYPTO_FALLBACK
+ return EVP_hcrypto_rc4_40();
#else
return NULL;
#endif
diff --git a/lib/hcrypto/evp-cc.h b/lib/hcrypto/evp-cc.h
index 9249bb226e1e..4d131de01eb9 100644
--- a/lib/hcrypto/evp-cc.h
+++ b/lib/hcrypto/evp-cc.h
@@ -42,6 +42,8 @@
#define EVP_cc_md5 hc_EVP_cc_md5
#define EVP_cc_sha1 hc_EVP_cc_sha1
#define EVP_cc_sha256 hc_EVP_cc_sha256
+#define EVP_cc_sha384 hc_EVP_cc_sha384
+#define EVP_cc_sha512 hc_EVP_cc_sha512
#define EVP_cc_des_cbc hc_EVP_cc_des_cbc
#define EVP_cc_des_ede3_cbc hc_EVP_cc_des_ede3_cbc
#define EVP_cc_aes_128_cbc hc_EVP_cc_aes_128_cbc
@@ -70,6 +72,8 @@ const EVP_MD * EVP_cc_md4(void);
const EVP_MD * EVP_cc_md5(void);
const EVP_MD * EVP_cc_sha1(void);
const EVP_MD * EVP_cc_sha256(void);
+const EVP_MD * EVP_cc_sha384(void);
+const EVP_MD * EVP_cc_sha512(void);
const EVP_CIPHER * EVP_cc_rc2_cbc(void);
const EVP_CIPHER * EVP_cc_rc2_40_cbc(void);
diff --git a/lib/hcrypto/evp-crypt.c b/lib/hcrypto/evp-crypt.c
new file mode 100644
index 000000000000..4e8489ebc5c4
--- /dev/null
+++ b/lib/hcrypto/evp-crypt.c
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Windows crypto provider plugin, sample */
+
+#include <config.h>
+#include <roken.h>
+
+#define HC_DEPRECATED
+
+#include <assert.h>
+
+#include <evp.h>
+
+#include <crypt.h>
+
+
+static HCRYPTPROV hCryptProv = NULL;
+
+/*
+ *
+ */
+
+struct generic_key {
+ HCRYPTKEY *hKey;
+};
+
+static int
+generic_cbc_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ struct generic_key *gk = ctx->cipher_data;
+ BOOL bResult;
+ DWORD length = size;
+
+ bResult = CryptSetKeyParam(gk->hKey, KP_IV, ctx->iv, 0);
+ _ASSERT(bResult);
+
+ memcpy(out, in, size);
+
+ if (ctx->encrypt)
+ bResult = CryptEncrypt(gk->hKey, 0, TRUE, 0, out, &length, size);
+ else
+ bResult = CryptDecrypt(gk->hKey, 0, TRUE, 0, out, &length);
+ _ASSERT(bResult);
+
+ return 1;
+}
+
+static int
+generic_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ struct generic_key *gk = ctx->cipher_data;
+ CryptDestroyKey(gk->hKey);
+ gk->hKey = NULL;
+ return 1;
+}
+
+static HCRYPTKEY
+import_key(int alg, const unsigned char *key, size_t keylen)
+{
+ struct {
+ BLOBHEADER hdr;
+ DWORD len;
+ BYTE key[1];
+ } *key_blob;
+ size_t bloblen = sizeof(*key_blob) - 1 + keylen;
+
+ key_blob = malloc(bloblen);
+
+ key_blob->hdr.bType = PLAINTEXTKEYBLOB;
+ key_blob->hdr.bVersion = CUR_BLOB_VERSION;
+ key_blob->hdr.reserved = 0;
+ key_blob->hdr.aiKeyAlg = alg;
+ key_blob->len = 24;
+ memcpy(key_blob->key, key, keylen);
+
+ bResult = CryptImportKey(hCryptProv,
+ (void *)key_blob, bloblen, 0, 0,
+ &gk->hKey);
+ free(key_blob);
+ _ASSERT(bResult);
+
+ return hKey;
+}
+
+static int
+crypto_des_ede3_cbc_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char * key,
+ const unsigned char * iv,
+ int encp)
+{
+ struct generic_key *gk = ctx->cipher_data;
+ DWORD paramData;
+
+ gk->hKey = import_key(CALG_3DES,
+ key->key->keyvalue.data,
+ key->key->keyvalue.len);
+
+ return 1;
+}
+
+/**
+ * The triple DES cipher type (Micrsoft crypt provider)
+ *
+ * @return the DES-EDE3-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+const EVP_CIPHER *
+EVP_wincrypt_des_ede3_cbc(void)
+{
+ static const EVP_CIPHER des_ede3_cbc = {
+ 0,
+ 8,
+ 24,
+ 8,
+ EVP_CIPH_CBC_MODE,
+ crypto_des_ede3_cbc_init,
+ generic_cbc_do_cipher,
+ generic_cleanup,
+ sizeof(struct generic_key),
+ NULL,
+ NULL,
+ NULL,
+ NULL
+ };
+ return &des_ede3_cbc;
+}
+
+/*
+ *
+ */
+
+struct generic_hash {
+ HCRYPTHASH hHash;
+};
+
+static void
+crypto_md5_init(struct generic_hash *m);
+{
+ BOOL bResult;
+ bResult = CryptCreateHash(hCryptProv, CALG_MD5, 0, 0, &m->hHash);
+ _ASSERT(bResult);
+}
+
+static void
+generic_hash_update (struct generic_hash *m, const void *p, size_t len)
+{
+ BOOL bResult;
+ bResult = CryptHashData(m->hHash, data, ( DWORD )len, 0 );
+ _ASSERT(bResult);
+}
+
+static void
+generic_hash_final (void *res, struct generic_hash *m);
+{
+ DWORD length;
+ BOOL bResult;
+ bResult = CryptGetHashParam(m->hHash, HP_HASHVAL, res, &length, 0)
+ _ASSERT(bResult);
+}
+
+static void
+generic_hash_cleanup(struct generic_hash *m)
+{
+ CryptDestroyHash(m->hHash);
+ m->hHash = NULL;
+}
+
+const EVP_MD *
+EVP_wincrypt_md5(void)
+{
+ static const struct hc_evp_md md5 = {
+ 16,
+ 64,
+ sizeof(struct generic_hash),
+ (hc_evp_md_init)crypto_md5_init,
+ (hc_evp_md_update)generic_hash_update,
+ (hc_evp_md_final)generic_hash_final,
+ (hc_evp_md_cleanup)generic_hash_cleanup
+ };
+ return &md5;
+}
diff --git a/lib/hcrypto/evp-hcrypto.c b/lib/hcrypto/evp-hcrypto.c
index bf37b42edcac..321efb08c52a 100644
--- a/lib/hcrypto/evp-hcrypto.c
+++ b/lib/hcrypto/evp-hcrypto.c
@@ -32,13 +32,10 @@
*/
#include <config.h>
+#include <roken.h>
#define HC_DEPRECATED
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <assert.h>
#include <evp.h>
@@ -69,7 +66,7 @@ aes_init(EVP_CIPHER_CTX *ctx,
int encp)
{
AES_KEY *k = ctx->cipher_data;
- if (ctx->encrypt)
+ if (ctx->encrypt || EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB8_MODE)
AES_set_encrypt_key(key, ctx->cipher->key_len * 8, k);
else
AES_set_decrypt_key(key, ctx->cipher->key_len * 8, k);
@@ -83,7 +80,7 @@ aes_do_cipher(EVP_CIPHER_CTX *ctx,
unsigned int size)
{
AES_KEY *k = ctx->cipher_data;
- if (ctx->flags & EVP_CIPH_CFB8_MODE)
+ if (EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_CFB8_MODE)
AES_cfb8_encrypt(in, out, size, k, ctx->iv, ctx->encrypt);
else
AES_cbc_encrypt(in, out, size, k, ctx->iv, ctx->encrypt);
@@ -531,7 +528,7 @@ des_ede3_cbc_do_cipher(EVP_CIPHER_CTX *ctx,
}
/**
- * The tripple DES cipher type - hcrypto
+ * The triple DES cipher type - hcrypto
*
* @return the DES-EDE3-CBC EVP_CIPHER pointer.
*
diff --git a/lib/hcrypto/evp-openssl.c b/lib/hcrypto/evp-openssl.c
new file mode 100644
index 000000000000..a651184c6ea0
--- /dev/null
+++ b/lib/hcrypto/evp-openssl.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (c) 2016, Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* OpenSSL provider */
+
+#include "config.h"
+#include <roken.h>
+#include <heimbase.h>
+
+#include <assert.h>
+#include <evp.h>
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+
+/*
+ * This is the OpenSSL 1.x backend for hcrypto. It has been tested with
+ * OpenSSL 1.0.1f and OpenSSL 1.1.0-pre3-dev.
+ *
+ * NOTE: In order for this to work with OpenSSL 1.1.x and up, it is
+ * critical to use opaque OpenSSL type accessors everywhere /
+ * never use knowledge of opaque OpenSSL type internals.
+ */
+
+#include <evp-openssl.h>
+
+/*
+ * This being an OpenSSL backend for hcrypto... we need to be able to
+ * refer to types and objects (functions) from both, OpenSSL and
+ * hcrypto.
+ *
+ * The hcrypto API is *very* similar to the OpenSSL 1.0.x API, with the
+ * same type and symbol names in many cases, except that the hcrypto
+ * names are prefixed with hc_*. hcrypto has convenience macros that
+ * provide OpenSSL aliases for the hcrypto interfaces, and hcrypto
+ * applications are expected to use the OpenSSL names.
+ *
+ * Since here we must be able to refer to types and objects from both
+ * OpenSSL and from hcrypto, we disable the hcrypto renaming for the
+ * rest of this file. These #undefs could be collected into an
+ * <hcrypto/undef.h> for the purpose of permitting other applications to
+ * use both, hcrypto and OpenSSL in the same source files (provided that
+ * such applications refer to hcrypto types and objects by their proper
+ * hc_-prefixed names).
+ */
+#include <undef.h>
+
+/* Now it's safe to include OpenSSL headers */
+#include <openssl/evp.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#define EVP_MD_CTX_new EVP_MD_CTX_create
+#define EVP_MD_CTX_free EVP_MD_CTX_destroy
+#endif
+
+/* A HEIM_BASE_ONCE argument struct for per-EVP one-time initialization */
+struct once_init_cipher_ctx {
+ const hc_EVP_CIPHER **hc_memoizep;
+ hc_EVP_CIPHER *hc_memoize;
+ const hc_EVP_CIPHER *fallback;
+ unsigned long flags;
+ int nid;
+};
+
+/* Our wrapper for OpenSSL EVP_CIPHER_CTXs */
+struct ossl_cipher_ctx {
+ EVP_CIPHER_CTX *ossl_cipher_ctx; /* OpenSSL cipher ctx */
+ const EVP_CIPHER *ossl_cipher; /* OpenSSL cipher */
+ int initialized;
+};
+
+/*
+ * Our hc_EVP_CIPHER init() method; wraps around OpenSSL
+ * EVP_CipherInit_ex().
+ *
+ * This is very similar to the init() function pointer in an OpenSSL
+ * EVP_CIPHER, but a) we can't access them in 1.1, and b) the method
+ * invocation protocols in hcrypto and OpenSSL are similar but not the
+ * same, thus we must have this wrapper.
+ */
+static int
+cipher_ctx_init(hc_EVP_CIPHER_CTX *ctx, const unsigned char *key,
+ const unsigned char *iv, int enc)
+{
+ struct ossl_cipher_ctx *ossl_ctx = ctx->cipher_data; /* EVP_CIPHER_CTX wrapper */
+ const EVP_CIPHER *c;
+
+ assert(ossl_ctx != NULL);
+ assert(ctx->cipher != NULL);
+ assert(ctx->cipher->app_data != NULL);
+
+ /*
+ * Here be dragons.
+ *
+ * We need to make sure that the OpenSSL EVP_CipherInit_ex() is
+ * called with cipher!=NULL just once per EVP_CIPHER_CTX, otherwise
+ * state in the OpenSSL EVP_CIPHER_CTX will get cleaned up and then
+ * we'll segfault.
+ *
+ * hcrypto applications can re-initialize an (hc_)EVP_CIPHER_CTX as
+ * usual by calling (hc)EVP_CipherInit_ex() with a non-NULL cipher
+ * argument, and that will cause cipher_cleanup() (below) to be
+ * called.
+ */
+ c = ossl_ctx->ossl_cipher = ctx->cipher->app_data; /* OpenSSL's EVP_CIPHER * */
+ if (!ossl_ctx->initialized) {
+ ossl_ctx->ossl_cipher_ctx = EVP_CIPHER_CTX_new();
+ if (ossl_ctx->ossl_cipher_ctx == NULL)
+ return 0;
+ /*
+ * So we always call EVP_CipherInit_ex() with c!=NULL, but other
+ * things NULL...
+ */
+ if (!EVP_CipherInit_ex(ossl_ctx->ossl_cipher_ctx, c, NULL, NULL, NULL, enc))
+ return 0;
+ ossl_ctx->initialized = 1;
+ }
+
+ /* ...and from here on always call EVP_CipherInit_ex() with c=NULL */
+ if ((ctx->cipher->flags & hc_EVP_CIPH_VARIABLE_LENGTH) &&
+ ctx->key_len > 0)
+ EVP_CIPHER_CTX_set_key_length(ossl_ctx->ossl_cipher_ctx, ctx->key_len);
+
+ return EVP_CipherInit_ex(ossl_ctx->ossl_cipher_ctx, NULL, NULL, key, iv, enc);
+}
+
+static int
+cipher_do_cipher(hc_EVP_CIPHER_CTX *ctx, unsigned char *out,
+ const unsigned char *in, unsigned int len)
+{
+ struct ossl_cipher_ctx *ossl_ctx = ctx->cipher_data;
+
+ assert(ossl_ctx != NULL);
+ return EVP_Cipher(ossl_ctx->ossl_cipher_ctx, out, in, len);
+}
+
+static int
+cipher_cleanup(hc_EVP_CIPHER_CTX *ctx)
+{
+ struct ossl_cipher_ctx *ossl_ctx = ctx->cipher_data;
+
+ if (ossl_ctx == NULL || !ossl_ctx->initialized)
+ return 1;
+
+ if (ossl_ctx->ossl_cipher_ctx != NULL)
+ EVP_CIPHER_CTX_free(ossl_ctx->ossl_cipher_ctx);
+
+ ossl_ctx->ossl_cipher_ctx = NULL;
+ ossl_ctx->ossl_cipher = NULL;
+ ossl_ctx->initialized = 0;
+ return 1;
+}
+
+static int
+cipher_ctrl(hc_EVP_CIPHER_CTX *ctx, int type, int arg, void *ptr)
+{
+ struct ossl_cipher_ctx *ossl_ctx = ctx->cipher_data;
+
+ assert(ossl_ctx != NULL);
+ return EVP_CIPHER_CTX_ctrl(ossl_ctx->ossl_cipher_ctx, type, arg, ptr);
+}
+
+
+static void
+get_EVP_CIPHER_once_cb(void *d)
+{
+ struct once_init_cipher_ctx *arg = d;
+ const EVP_CIPHER *ossl_evp;
+ hc_EVP_CIPHER *hc_evp;
+
+ hc_evp = arg->hc_memoize;
+
+ /*
+ * We lookup EVP_CIPHER *s by NID so that we don't fail to find a
+ * symbol such as EVP_aes...() when libcrypto changes after build
+ * time (e.g., updates, LD_LIBRARY_PATH/LD_PRELOAD).
+ */
+ ossl_evp = EVP_get_cipherbynid(arg->nid);
+ if (ossl_evp == NULL) {
+ (void) memset(hc_evp, 0, sizeof(*hc_evp));
+#if HCRYPTO_FALLBACK
+ *arg->hc_memoizep = arg->fallback;
+#endif
+ return;
+ }
+
+ /* Build the hc_EVP_CIPHER */
+ hc_evp->nid = EVP_CIPHER_nid(ossl_evp); /* We would an hcrypto NIDs if we had them */
+ hc_evp->block_size = EVP_CIPHER_block_size(ossl_evp);
+ hc_evp->key_len = EVP_CIPHER_key_length(ossl_evp);
+ hc_evp->iv_len = EVP_CIPHER_iv_length(ossl_evp);
+
+ /*
+ * We force hc_EVP_CipherInit_ex to always call our init() function,
+ * otherwise we don't get a chance to call EVP_CipherInit_ex()
+ * correctly.
+ */
+ hc_evp->flags = hc_EVP_CIPH_ALWAYS_CALL_INIT | arg->flags;
+
+ /* Our cipher context */
+ hc_evp->ctx_size = sizeof(struct ossl_cipher_ctx);
+
+ /* Our wrappers */
+ hc_evp->init = cipher_ctx_init;
+ hc_evp->do_cipher = cipher_do_cipher;
+ hc_evp->cleanup = cipher_cleanup;
+ hc_evp->set_asn1_parameters = NULL;
+ hc_evp->get_asn1_parameters = NULL;
+ hc_evp->ctrl = cipher_ctrl;
+
+ /* Our link to the OpenSSL EVP_CIPHER */
+ hc_evp->app_data = (void *)ossl_evp;
+
+ /* Finally, set the static hc_EVP_CIPHER * to the one we just built */
+ *arg->hc_memoizep = hc_evp;
+}
+
+static const hc_EVP_CIPHER *
+get_EVP_CIPHER(heim_base_once_t *once, hc_EVP_CIPHER *hc_memoize,
+ const hc_EVP_CIPHER **hc_memoizep,
+ const hc_EVP_CIPHER *fallback,
+ unsigned long flags, int nid)
+{
+ struct once_init_cipher_ctx arg;
+
+ arg.flags = flags;
+ arg.hc_memoizep = hc_memoizep;
+ arg.hc_memoize = hc_memoize;
+ arg.fallback = fallback;
+ arg.nid = nid;
+ heim_base_once_f(once, &arg, get_EVP_CIPHER_once_cb);
+ return *hc_memoizep; /* May be NULL */
+}
+
+#define OSSL_CIPHER_ALGORITHM(name, flags) \
+ extern const hc_EVP_CIPHER *hc_EVP_hcrypto_##name(void); \
+ const hc_EVP_CIPHER *hc_EVP_ossl_##name(void) \
+ { \
+ static hc_EVP_CIPHER ossl_##name##_st; \
+ static const hc_EVP_CIPHER *ossl_##name; \
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT; \
+ return get_EVP_CIPHER(&once, &ossl_##name##_st, &ossl_##name, \
+ hc_EVP_hcrypto_##name(), \
+ flags, NID_##name); \
+ }
+
+/* As above, but for EVP_MDs */
+
+struct ossl_md_ctx {
+ EVP_MD_CTX *ossl_md_ctx; /* OpenSSL md ctx */
+ const EVP_MD *ossl_md; /* OpenSSL md */
+ int initialized;
+};
+
+static int
+ossl_md_init(struct ossl_md_ctx *ctx, const EVP_MD *md)
+{
+ if (ctx->initialized)
+ EVP_MD_CTX_free(ctx->ossl_md_ctx);
+ ctx->initialized = 0;
+
+ ctx->ossl_md = md;
+ ctx->ossl_md_ctx = EVP_MD_CTX_new();
+ if (!EVP_DigestInit(ctx->ossl_md_ctx, md)) {
+ EVP_MD_CTX_free(ctx->ossl_md_ctx);
+ ctx->ossl_md_ctx = NULL;
+ ctx->ossl_md = NULL;
+ return 0;
+ }
+ ctx->initialized = 1;
+ return 1;
+}
+
+static int
+ossl_md_update(hc_EVP_MD_CTX *d, const void *data, size_t count)
+{
+ struct ossl_md_ctx *ctx = (void *)d;
+
+ return EVP_DigestUpdate(ctx->ossl_md_ctx, data, count);
+}
+
+static int
+ossl_md_final(void *md_data, hc_EVP_MD_CTX *d)
+{
+ struct ossl_md_ctx *ctx = (void *)d;
+
+ return EVP_DigestFinal(ctx->ossl_md_ctx, md_data, NULL);
+}
+
+static int
+ossl_md_cleanup(hc_EVP_MD_CTX *d)
+{
+ struct ossl_md_ctx *ctx = (void *)d;
+
+ if (!ctx->initialized)
+ return 1;
+ EVP_MD_CTX_free(ctx->ossl_md_ctx);
+ ctx->ossl_md = NULL;
+ ctx->initialized = 0;
+
+ return 1;
+}
+
+struct once_init_md_ctx {
+ const EVP_MD **ossl_memoizep;
+ const hc_EVP_MD **hc_memoizep;
+ hc_EVP_MD *hc_memoize;
+ const hc_EVP_MD *fallback;
+ hc_evp_md_init md_init;
+ int nid;
+};
+
+static void
+get_EVP_MD_once_cb(void *d)
+{
+ struct once_init_md_ctx *arg = d;
+ const EVP_MD *ossl_evp;
+ hc_EVP_MD *hc_evp;
+
+ hc_evp = arg->hc_memoize;
+ *arg->ossl_memoizep = ossl_evp = EVP_get_digestbynid(arg->nid);
+
+ if (ossl_evp == NULL) {
+ (void) memset(hc_evp, 0, sizeof(*hc_evp));
+#if HCRYPTO_FALLBACK
+ *arg->hc_memoizep = arg->fallback;
+#endif
+ return;
+ }
+
+ /* Build the hc_EVP_MD */
+ hc_evp->block_size = EVP_MD_block_size(ossl_evp);
+ hc_evp->hash_size = EVP_MD_size(ossl_evp);
+ hc_evp->ctx_size = sizeof(struct ossl_md_ctx);
+ hc_evp->init = arg->md_init;
+ hc_evp->update = ossl_md_update;
+ hc_evp->final = ossl_md_final;
+ hc_evp->cleanup = ossl_md_cleanup;
+
+ *arg->hc_memoizep = hc_evp;
+}
+
+static const hc_EVP_MD *
+get_EVP_MD(heim_base_once_t *once, hc_EVP_MD *hc_memoize,
+ const hc_EVP_MD **hc_memoizep, const EVP_MD **ossl_memoizep,
+ const hc_EVP_MD *fallback,
+ hc_evp_md_init md_init, int nid)
+{
+ struct once_init_md_ctx ctx;
+
+ ctx.ossl_memoizep = ossl_memoizep;
+ ctx.hc_memoizep = hc_memoizep;
+ ctx.hc_memoize = hc_memoize;
+ ctx.fallback = fallback;
+ ctx.md_init = md_init;
+ ctx.nid = nid;
+ heim_base_once_f(once, &ctx, get_EVP_MD_once_cb);
+ return *hc_memoizep; /* May be NULL */
+}
+
+#define OSSL_MD_ALGORITHM(name) \
+ extern const hc_EVP_MD *hc_EVP_hcrypto_##name(void); \
+ static const EVP_MD *ossl_EVP_##name; \
+ static const hc_EVP_MD *ossl_##name; \
+ static int ossl_init_##name(hc_EVP_MD_CTX *d) \
+ { \
+ return ossl_md_init((void *)d, ossl_EVP_##name); \
+ } \
+ const hc_EVP_MD *hc_EVP_ossl_##name(void) \
+ { \
+ static hc_EVP_MD ossl_##name##_st; \
+ static heim_base_once_t once = HEIM_BASE_ONCE_INIT; \
+ return get_EVP_MD(&once, &ossl_##name##_st, &ossl_##name, \
+ &ossl_EVP_##name, hc_EVP_hcrypto_##name(), \
+ ossl_init_##name, NID_##name); \
+ }
+
+#else /* HAVE_HCRYPTO_W_OPENSSL */
+
+#include "evp-hcrypto.h"
+
+#define OSSL_CIPHER_ALGORITHM(name, flags) \
+ extern const hc_EVP_CIPHER *hc_EVP_ossl_##name(void); \
+ const hc_EVP_CIPHER *hc_EVP_ossl_##name(void) \
+ { \
+ return hc_EVP_hcrypto_##name(); \
+ }
+
+#define OSSL_MD_ALGORITHM(name) \
+ extern const hc_EVP_MD *hc_EVP_ossl_##name(void); \
+ const hc_EVP_MD *hc_EVP_ossl_##name(void) \
+ { \
+ return hc_EVP_hcrypto_##name(); \
+ }
+
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+
+/**
+ * The triple DES cipher type (OpenSSL provider)
+ *
+ * @return the DES-EDE3-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(des_ede3_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The DES cipher type (OpenSSL provider)
+ *
+ * @return the DES-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(des_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-128 cipher type (OpenSSL provider)
+ *
+ * @return the AES-128-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(aes_128_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-192 cipher type (OpenSSL provider)
+ *
+ * @return the AES-192-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(aes_192_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-256 cipher type (OpenSSL provider)
+ *
+ * @return the AES-256-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(aes_256_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-128 CFB8 cipher type (OpenSSL provider)
+ *
+ * @return the AES-128-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(aes_128_cfb8, hc_EVP_CIPH_CFB8_MODE)
+
+/**
+ * The AES-192 CFB8 cipher type (OpenSSL provider)
+ *
+ * @return the AES-192-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(aes_192_cfb8, hc_EVP_CIPH_CFB8_MODE)
+
+/**
+ * The AES-256 CFB8 cipher type (OpenSSL provider)
+ *
+ * @return the AES-256-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(aes_256_cfb8, hc_EVP_CIPH_CFB8_MODE)
+
+/*
+ * RC2 is only needed for tests of PKCS#12 support, which currently uses
+ * the RC2 PBE. So no RC2 -> tests fail.
+ */
+
+/**
+ * The RC2 cipher type - OpenSSL
+ *
+ * @return the RC2 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(rc2_cbc,
+ hc_EVP_CIPH_CBC_MODE |
+ hc_EVP_CIPH_VARIABLE_LENGTH)
+
+/**
+ * The RC2-40 cipher type - OpenSSL
+ *
+ * @return the RC2-40 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(rc2_40_cbc,
+ hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The RC2-64 cipher type - OpenSSL
+ *
+ * @return the RC2-64 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(rc2_64_cbc,
+ hc_EVP_CIPH_CBC_MODE |
+ hc_EVP_CIPH_VARIABLE_LENGTH)
+
+/**
+ * The Camellia-128 cipher type - OpenSSL
+ *
+ * @return the Camellia-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(camellia_128_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The Camellia-198 cipher type - OpenSSL
+ *
+ * @return the Camellia-198 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(camellia_192_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The Camellia-256 cipher type - OpenSSL
+ *
+ * @return the Camellia-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(camellia_256_cbc, hc_EVP_CIPH_CBC_MODE)
+
+/**
+ * The RC4 cipher type (OpenSSL provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(rc4,
+ hc_EVP_CIPH_STREAM_CIPHER |
+ hc_EVP_CIPH_VARIABLE_LENGTH)
+
+/**
+ * The RC4-40 cipher type (OpenSSL provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_CIPHER_ALGORITHM(rc4_40,
+ hc_EVP_CIPH_STREAM_CIPHER |
+ hc_EVP_CIPH_VARIABLE_LENGTH)
+
+/**
+ * The MD2 hash algorithm (OpenSSL provider)
+ *
+ * @return the MD2 EVP_MD pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_MD_ALGORITHM(md2)
+
+/**
+ * The MD4 hash algorithm (OpenSSL provider)
+ *
+ * @return the MD4 EVP_MD pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_MD_ALGORITHM(md4)
+
+/**
+ * The MD5 hash algorithm (OpenSSL provider)
+ *
+ * @return the MD5 EVP_MD pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_MD_ALGORITHM(md5)
+
+/**
+ * The SHA-1 hash algorithm (OpenSSL provider)
+ *
+ * @return the SHA-1 EVP_MD pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_MD_ALGORITHM(sha1)
+
+/**
+ * The SHA-256 hash algorithm (OpenSSL provider)
+ *
+ * @return the SHA-256 EVP_MD pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_MD_ALGORITHM(sha256)
+
+/**
+ * The SHA-384 hash algorithm (OpenSSL provider)
+ *
+ * @return the SHA-384 EVP_MD pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_MD_ALGORITHM(sha384)
+
+/**
+ * The SHA-512 hash algorithm (OpenSSL provider)
+ *
+ * @return the SHA-512 EVP_MD pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+OSSL_MD_ALGORITHM(sha512)
diff --git a/lib/hcrypto/evp-openssl.h b/lib/hcrypto/evp-openssl.h
new file mode 100644
index 000000000000..225ff454a874
--- /dev/null
+++ b/lib/hcrypto/evp-openssl.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009-2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_EVP_OSSL_H
+#define HEIM_EVP_OSSL_H 1
+
+/* symbol renaming */
+#define EVP_ossl_md2 hc_EVP_ossl_md2
+#define EVP_ossl_md4 hc_EVP_ossl_md4
+#define EVP_ossl_md5 hc_EVP_ossl_md5
+#define EVP_ossl_sha1 hc_EVP_ossl_sha1
+#define EVP_ossl_sha256 hc_EVP_ossl_sha256
+#define EVP_ossl_sha384 hc_EVP_ossl_sha384
+#define EVP_ossl_sha512 hc_EVP_ossl_sha512
+#define EVP_ossl_des_cbc hc_EVP_ossl_des_cbc
+#define EVP_ossl_des_ede3_cbc hc_EVP_ossl_des_ede3_cbc
+#define EVP_ossl_aes_128_cbc hc_EVP_ossl_aes_128_cbc
+#define EVP_ossl_aes_192_cbc hc_EVP_ossl_aes_192_cbc
+#define EVP_ossl_aes_256_cbc hc_EVP_ossl_aes_256_cbc
+#define EVP_ossl_aes_128_cfb8 hc_EVP_ossl_aes_128_cfb8
+#define EVP_ossl_aes_192_cfb8 hc_EVP_ossl_aes_192_cfb8
+#define EVP_ossl_aes_256_cfb8 hc_EVP_ossl_aes_256_cfb8
+#define EVP_ossl_rc4 hc_EVP_ossl_rc4
+#define EVP_ossl_rc4_40 hc_EVP_ossl_rc4_40
+#define EVP_ossl_rc2_40_cbc hc_EVP_ossl_rc2_40_cbc
+#define EVP_ossl_rc2_64_cbc hc_EVP_ossl_rc2_64_cbc
+#define EVP_ossl_rc2_cbc hc_EVP_ossl_rc2_cbc
+#define EVP_ossl_camellia_128_cbc hc_EVP_ossl_camellia_128_cbc
+#define EVP_ossl_camellia_192_cbc hc_EVP_ossl_camellia_192_cbc
+#define EVP_ossl_camellia_256_cbc hc_EVP_ossl_camellia_256_cbc
+
+/*
+ *
+ */
+
+HC_CPP_BEGIN
+
+const hc_EVP_MD * hc_EVP_ossl_md2(void);
+const hc_EVP_MD * hc_EVP_ossl_md4(void);
+const hc_EVP_MD * hc_EVP_ossl_md5(void);
+const hc_EVP_MD * hc_EVP_ossl_sha1(void);
+const hc_EVP_MD * hc_EVP_ossl_sha256(void);
+const hc_EVP_MD * hc_EVP_ossl_sha384(void);
+const hc_EVP_MD * hc_EVP_ossl_sha512(void);
+
+const hc_EVP_CIPHER * hc_EVP_ossl_rc2_cbc(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_rc2_40_cbc(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_rc2_64_cbc(void);
+
+const hc_EVP_CIPHER * hc_EVP_ossl_rc4(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_rc4_40(void);
+
+const hc_EVP_CIPHER * hc_EVP_ossl_des_cbc(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_des_ede3_cbc(void);
+
+const hc_EVP_CIPHER * hc_EVP_ossl_aes_128_cbc(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_aes_192_cbc(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_aes_256_cbc(void);
+
+const hc_EVP_CIPHER * hc_EVP_ossl_aes_128_cfb8(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_aes_192_cfb8(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_aes_256_cfb8(void);
+
+const hc_EVP_CIPHER * hc_EVP_ossl_camellia_128_cbc(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_camellia_192_cbc(void);
+const hc_EVP_CIPHER * hc_EVP_ossl_camellia_256_cbc(void);
+
+HC_CPP_END
+
+#endif /* HEIM_EVP_OSSL_H */
diff --git a/lib/hcrypto/evp-pkcs11.c b/lib/hcrypto/evp-pkcs11.c
new file mode 100644
index 000000000000..93af9b4564c0
--- /dev/null
+++ b/lib/hcrypto/evp-pkcs11.c
@@ -0,0 +1,796 @@
+/*
+ * Copyright (c) 2015-2016, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* PKCS#11 provider */
+
+#include <config.h>
+#include <roken.h>
+#include <assert.h>
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#ifndef RTLD_LAZY
+#define RTLD_LAZY 0
+#endif
+#ifndef RTLD_LOCAL
+#define RTLD_LOCAL 0
+#endif
+#ifndef RTLD_GROUP
+#define RTLD_GROUP 0
+#endif
+#ifndef RTLD_NODELETE
+#define RTLD_NODELETE 0
+#endif
+#else
+#error PKCS11 support requires dlfcn.h
+#endif
+
+#include <heimbase.h>
+
+#include <evp.h>
+#include <evp-hcrypto.h>
+#include <evp-pkcs11.h>
+
+#include <ref/pkcs11.h>
+
+#if __sun && !defined(PKCS11_MODULE_PATH)
+# if _LP64
+# define PKCS11_MODULE_PATH "/usr/lib/64/libpkcs11.so"
+# else
+# define PKCS11_MODULE_PATH "/usr/lib/libpkcs11.so"
+# endif
+#elif defined(__linux__)
+/*
+ * XXX We should have an autoconf check for OpenCryptoki and such
+ * things. However, there's no AC_CHECK_OBJECT(), and we'd have to
+ * write one. Today I'm feeling lazy. Another possibility would be to
+ * have a symlink from the libdir we'll install into, and then we could
+ * dlopen() that on all platforms.
+ *
+ * XXX Also, we should pick an appropriate shared object based on 32- vs
+ * 64-bits.
+ */
+# define PKCS11_MODULE_PATH "/usr/lib/pkcs11/PKCS11_API.so"
+#endif
+
+static CK_FUNCTION_LIST_PTR p11_module;
+
+static int
+p11_cleanup(EVP_CIPHER_CTX *ctx);
+
+struct pkcs11_cipher_ctx {
+ CK_SESSION_HANDLE hSession;
+ CK_OBJECT_HANDLE hSecret;
+ int cipher_init_done;
+};
+
+struct pkcs11_md_ctx {
+ CK_SESSION_HANDLE hSession;
+};
+
+static void *pkcs11_module_handle;
+static void
+p11_module_init_once(void *context)
+{
+ CK_RV rv;
+ CK_FUNCTION_LIST_PTR module;
+ CK_RV (*C_GetFunctionList_fn)(CK_FUNCTION_LIST_PTR_PTR);
+
+ if (!issuid()) {
+ char *pkcs11ModulePath = getenv("PKCS11_MODULE_PATH");
+ if (pkcs11ModulePath != NULL) {
+ pkcs11_module_handle =
+ dlopen(pkcs11ModulePath,
+ RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP | RTLD_NODELETE);
+ if (pkcs11_module_handle == NULL)
+ fprintf(stderr, "p11_module_init(%s): %s\n", pkcs11ModulePath, dlerror());
+ }
+ }
+#ifdef PKCS11_MODULE_PATH
+ if (pkcs11_module_handle == NULL) {
+ pkcs11_module_handle =
+ dlopen(PKCS11_MODULE_PATH,
+ RTLD_LAZY | RTLD_LOCAL | RTLD_GROUP | RTLD_NODELETE);
+ if (pkcs11_module_handle == NULL)
+ fprintf(stderr, "p11_module_init(%s): %s\n", PKCS11_MODULE_PATH, dlerror());
+ }
+#endif
+ if (pkcs11_module_handle == NULL)
+ goto cleanup;
+
+ C_GetFunctionList_fn = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR))
+ dlsym(pkcs11_module_handle, "C_GetFunctionList");
+ if (C_GetFunctionList_fn == NULL)
+ goto cleanup;
+
+ rv = C_GetFunctionList_fn(&module);
+ if (rv != CKR_OK)
+ goto cleanup;
+
+ rv = module->C_Initialize(NULL);
+ if (rv == CKR_CRYPTOKI_ALREADY_INITIALIZED)
+ rv = CKR_OK;
+ if (rv == CKR_OK)
+ *((CK_FUNCTION_LIST_PTR_PTR)context) = module;
+
+cleanup:
+ if (pkcs11_module_handle != NULL && p11_module == NULL) {
+ dlclose(pkcs11_module_handle);
+ pkcs11_module_handle = NULL;
+ }
+ /* else leak pkcs11_module_handle */
+}
+
+static CK_RV
+p11_module_init(void)
+{
+ static heim_base_once_t init_module = HEIM_BASE_ONCE_INIT;
+
+ heim_base_once_f(&init_module, &p11_module, p11_module_init_once);
+
+ return p11_module != NULL ? CKR_OK : CKR_LIBRARY_LOAD_FAILED;
+}
+
+static CK_RV
+p11_session_init(CK_MECHANISM_TYPE mechanismType, CK_SESSION_HANDLE_PTR phSession)
+{
+ CK_RV rv;
+ CK_ULONG i, ulSlotCount = 0;
+ CK_SLOT_ID_PTR pSlotList = NULL;
+ CK_MECHANISM_INFO info;
+
+ if (phSession != NULL)
+ *phSession = CK_INVALID_HANDLE;
+
+ rv = p11_module_init();
+ if (rv != CKR_OK)
+ goto cleanup;
+
+ assert(p11_module != NULL);
+
+ rv = p11_module->C_GetSlotList(CK_FALSE, NULL, &ulSlotCount);
+ if (rv != CKR_OK)
+ goto cleanup;
+
+ pSlotList = (CK_SLOT_ID_PTR)calloc(ulSlotCount, sizeof(CK_SLOT_ID));
+ if (pSlotList == NULL) {
+ rv = CKR_HOST_MEMORY;
+ goto cleanup;
+ }
+
+ rv = p11_module->C_GetSlotList(CK_FALSE, pSlotList, &ulSlotCount);
+ if (rv != CKR_OK)
+ goto cleanup;
+
+ /*
+ * Note that this approach of using the first slot that supports the desired
+ * mechanism may not always be what the user wants (for example it may prefer
+ * software to hardware crypto). We're going to assume that this code will be
+ * principally used on Solaris (which has a meta-slot provider that sorts by
+ * hardware first) or in situations where the user can configure the slots in
+ * order of provider preference. In the future we should make this configurable.
+ */
+ for (i = 0; i < ulSlotCount; i++) {
+ rv = p11_module->C_GetMechanismInfo(pSlotList[i], mechanismType, &info);
+ if (rv == CKR_OK)
+ break;
+ }
+
+ if (i == ulSlotCount) {
+ rv = CKR_MECHANISM_INVALID;
+ goto cleanup;
+ }
+
+ if (phSession != NULL) {
+ rv = p11_module->C_OpenSession(pSlotList[i], CKF_SERIAL_SESSION, NULL, NULL, phSession);
+ if (rv != CKR_OK)
+ goto cleanup;
+ }
+
+cleanup:
+ free(pSlotList);
+
+ return rv;
+}
+
+static int
+p11_mech_available_p(CK_MECHANISM_TYPE mechanismType)
+{
+ return p11_session_init(mechanismType, NULL) == CKR_OK;
+}
+
+static CK_KEY_TYPE
+p11_key_type_for_mech(CK_MECHANISM_TYPE mechanismType)
+{
+ CK_KEY_TYPE keyType = 0;
+
+ switch (mechanismType) {
+ case CKM_RC2_CBC:
+ keyType = CKK_RC2;
+ break;
+ case CKM_RC4:
+ keyType = CKK_RC4;
+ break;
+ case CKM_DES_CBC:
+ keyType = CKK_DES;
+ break;
+ case CKM_DES3_CBC:
+ keyType = CKK_DES3;
+ break;
+ case CKM_AES_CBC:
+ case CKM_AES_CFB8:
+ keyType = CKK_AES;
+ break;
+ case CKM_CAMELLIA_CBC:
+ keyType = CKK_CAMELLIA;
+ break;
+ default:
+ assert(0 && "Unknown PKCS#11 mechanism type");
+ break;
+ }
+
+ return keyType;
+}
+
+static int
+p11_key_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ const unsigned char *iv,
+ int encp)
+{
+ CK_RV rv;
+ CK_BBOOL bFalse = CK_FALSE;
+ CK_BBOOL bTrue = CK_TRUE;
+ CK_MECHANISM_TYPE mechanismType = (CK_MECHANISM_TYPE)ctx->cipher->app_data;
+ CK_KEY_TYPE keyType = p11_key_type_for_mech(mechanismType);
+ CK_OBJECT_CLASS objectClass = CKO_SECRET_KEY;
+ CK_ATTRIBUTE_TYPE op = encp ? CKA_ENCRYPT : CKA_DECRYPT;
+ CK_ATTRIBUTE attributes[] = {
+ { CKA_EXTRACTABLE, &bFalse, sizeof(bFalse) },
+ { CKA_CLASS, &objectClass, sizeof(objectClass) },
+ { CKA_KEY_TYPE, &keyType, sizeof(keyType) },
+ { CKA_TOKEN, &bFalse, sizeof(bFalse) },
+ { CKA_PRIVATE, &bFalse, sizeof(bFalse) },
+ { CKA_SENSITIVE, &bTrue, sizeof(bTrue) },
+ { CKA_VALUE, (void *)key, ctx->key_len },
+ { op, &bTrue, sizeof(bTrue) }
+ };
+ struct pkcs11_cipher_ctx *p11ctx = (struct pkcs11_cipher_ctx *)ctx->cipher_data;
+ p11ctx->cipher_init_done = 0;
+
+ rv = p11_session_init(mechanismType, &p11ctx->hSession);
+ if (rv != CKR_OK)
+ goto cleanup;
+
+ assert(p11_module != NULL);
+
+ rv = p11_module->C_CreateObject(p11ctx->hSession, attributes,
+ sizeof(attributes) / sizeof(attributes[0]),
+ &p11ctx->hSecret);
+ if (rv != CKR_OK)
+ goto cleanup;
+
+cleanup:
+ if (rv != CKR_OK)
+ p11_cleanup(ctx);
+
+ return rv == CKR_OK;
+}
+
+static int
+p11_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ struct pkcs11_cipher_ctx *p11ctx = (struct pkcs11_cipher_ctx *)ctx->cipher_data;
+ CK_RV rv = CKR_OK;
+ CK_ULONG ulCipherTextLen = size;
+ CK_MECHANISM_TYPE mechanismType = (CK_MECHANISM_TYPE)ctx->cipher->app_data;
+ CK_MECHANISM mechanism = {
+ mechanismType,
+ ctx->cipher->iv_len ? ctx->iv : NULL,
+ ctx->cipher->iv_len
+ };
+
+ assert(p11_module != NULL);
+ /* The EVP layer only ever calls us with complete cipher blocks */
+ assert(EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_STREAM_CIPHER ||
+ (size % ctx->cipher->block_size) == 0);
+
+ if (ctx->encrypt) {
+ if (!p11ctx->cipher_init_done) {
+ rv = p11_module->C_EncryptInit(p11ctx->hSession, &mechanism, p11ctx->hSecret);
+ if (rv == CKR_OK)
+ p11ctx->cipher_init_done = 1;
+ }
+ if (rv == CKR_OK)
+ rv = p11_module->C_EncryptUpdate(p11ctx->hSession, (unsigned char *)in, size, out, &ulCipherTextLen);
+ } else {
+ if (!p11ctx->cipher_init_done) {
+ rv = p11_module->C_DecryptInit(p11ctx->hSession, &mechanism, p11ctx->hSecret);
+ if (rv == CKR_OK)
+ p11ctx->cipher_init_done = 1;
+ }
+ if (rv == CKR_OK)
+ rv = p11_module->C_DecryptUpdate(p11ctx->hSession, (unsigned char *)in, size, out, &ulCipherTextLen);
+ }
+
+ return rv == CKR_OK;
+}
+
+static int
+p11_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ struct pkcs11_cipher_ctx *p11ctx = (struct pkcs11_cipher_ctx *)ctx->cipher_data;
+
+ assert(p11_module != NULL);
+
+ if (p11ctx->hSecret != CK_INVALID_HANDLE) {
+ p11_module->C_DestroyObject(p11ctx->hSession, p11ctx->hSecret);
+ p11ctx->hSecret = CK_INVALID_HANDLE;
+ }
+ if (p11ctx->hSession != CK_INVALID_HANDLE) {
+ p11_module->C_CloseSession(p11ctx->hSession);
+ p11ctx->hSession = CK_INVALID_HANDLE;
+ }
+
+ return 1;
+}
+
+static int
+p11_md_hash_init(CK_MECHANISM_TYPE mechanismType, EVP_MD_CTX *ctx)
+{
+ struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx;
+ CK_RV rv;
+
+ rv = p11_session_init(mechanismType, &p11ctx->hSession);
+ if (rv == CKR_OK) {
+ CK_MECHANISM mechanism = { mechanismType, NULL, 0 };
+
+ assert(p11_module != NULL);
+
+ rv = p11_module->C_DigestInit(p11ctx->hSession, &mechanism);
+ }
+
+ return rv == CKR_OK;
+}
+
+static int
+p11_md_update(EVP_MD_CTX *ctx, const void *data, size_t length)
+{
+ struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx;
+ CK_RV rv;
+
+ assert(p11_module != NULL);
+
+ rv = p11_module->C_DigestUpdate(p11ctx->hSession, (unsigned char *)data, length);
+
+ return rv == CKR_OK;
+}
+
+static int
+p11_md_final(void *digest, EVP_MD_CTX *ctx)
+{
+ struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx;
+ CK_RV rv;
+ CK_ULONG digestLen = 0;
+
+ assert(p11_module != NULL);
+
+ rv = p11_module->C_DigestFinal(p11ctx->hSession, NULL, &digestLen);
+ if (rv == CKR_OK)
+ rv = p11_module->C_DigestFinal(p11ctx->hSession, digest, &digestLen);
+
+ return rv == CKR_OK;
+}
+
+static int
+p11_md_cleanup(EVP_MD_CTX *ctx)
+{
+ struct pkcs11_md_ctx *p11ctx = (struct pkcs11_md_ctx *)ctx;
+ CK_RV rv;
+
+ assert(p11_module != NULL);
+
+ rv = p11_module->C_CloseSession(p11ctx->hSession);
+ if (rv == CKR_OK)
+ p11ctx->hSession = CK_INVALID_HANDLE;
+
+ return rv == CKR_OK;
+}
+
+#define PKCS11_CIPHER_ALGORITHM(name, mechanismType, block_size, \
+ key_len, iv_len, flags) \
+ \
+ static EVP_CIPHER \
+ pkcs11_##name = { \
+ 0, \
+ block_size, \
+ key_len, \
+ iv_len, \
+ flags, \
+ p11_key_init, \
+ p11_do_cipher, \
+ p11_cleanup, \
+ sizeof(struct pkcs11_cipher_ctx), \
+ NULL, \
+ NULL, \
+ NULL, \
+ (void *)mechanismType \
+ }; \
+ \
+ const EVP_CIPHER * \
+ hc_EVP_pkcs11_##name(void) \
+ { \
+ if (p11_mech_available_p(mechanismType)) \
+ return &pkcs11_##name; \
+ else \
+ return NULL; \
+ } \
+ \
+ static void \
+ pkcs11_hcrypto_##name##_init_once(void *context) \
+ { \
+ const EVP_CIPHER *cipher; \
+ \
+ cipher = hc_EVP_pkcs11_ ##name(); \
+ if (cipher == NULL && HCRYPTO_FALLBACK) \
+ cipher = hc_EVP_hcrypto_ ##name(); \
+ \
+ *((const EVP_CIPHER **)context) = cipher; \
+ } \
+ \
+ const EVP_CIPHER * \
+ hc_EVP_pkcs11_hcrypto_##name(void) \
+ { \
+ static const EVP_CIPHER *__cipher; \
+ static heim_base_once_t __init = HEIM_BASE_ONCE_INIT; \
+ \
+ heim_base_once_f(&__init, &__cipher, \
+ pkcs11_hcrypto_##name##_init_once); \
+ \
+ return __cipher; \
+ }
+
+#define PKCS11_MD_ALGORITHM(name, mechanismType, hash_size, block_size) \
+ \
+ static int p11_##name##_init(EVP_MD_CTX *ctx) \
+ { \
+ return p11_md_hash_init(mechanismType, ctx); \
+ } \
+ \
+ const EVP_MD * \
+ hc_EVP_pkcs11_##name(void) \
+ { \
+ static struct hc_evp_md name = { \
+ hash_size, \
+ block_size, \
+ sizeof(struct pkcs11_md_ctx), \
+ p11_##name##_init, \
+ p11_md_update, \
+ p11_md_final, \
+ p11_md_cleanup \
+ }; \
+ \
+ if (p11_mech_available_p(mechanismType)) \
+ return &name; \
+ else \
+ return NULL; \
+ } \
+ \
+ static void \
+ pkcs11_hcrypto_##name##_init_once(void *context) \
+ { \
+ const EVP_MD *md; \
+ \
+ md = hc_EVP_pkcs11_ ##name(); \
+ if (md == NULL && HCRYPTO_FALLBACK) \
+ md = hc_EVP_hcrypto_ ##name(); \
+ \
+ *((const EVP_MD **)context) = md; \
+ } \
+ \
+ const EVP_MD * \
+ hc_EVP_pkcs11_hcrypto_##name(void) \
+ { \
+ static const EVP_MD *__md; \
+ static heim_base_once_t __init = HEIM_BASE_ONCE_INIT; \
+ \
+ heim_base_once_f(&__init, &__md, \
+ pkcs11_hcrypto_##name##_init_once); \
+ \
+ return __md; \
+ }
+
+#define PKCS11_MD_ALGORITHM_UNAVAILABLE(name) \
+ \
+ const EVP_MD * \
+ hc_EVP_pkcs11_##name(void) \
+ { \
+ return NULL; \
+ } \
+ \
+ const EVP_MD * \
+ hc_EVP_pkcs11_hcrypto_##name(void) \
+ { \
+ return hc_EVP_hcrypto_ ##name(); \
+ }
+
+/**
+ * The triple DES cipher type (PKCS#11 provider)
+ *
+ * @return the DES-EDE3-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(des_ede3_cbc,
+ CKM_DES3_CBC,
+ 8,
+ 24,
+ 8,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The DES cipher type (PKCS#11 provider)
+ *
+ * @return the DES-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(des_cbc,
+ CKM_DES_CBC,
+ 8,
+ 8,
+ 8,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-128 cipher type (PKCS#11 provider)
+ *
+ * @return the AES-128-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(aes_128_cbc,
+ CKM_AES_CBC,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-192 cipher type (PKCS#11 provider)
+ *
+ * @return the AES-192-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(aes_192_cbc,
+ CKM_AES_CBC,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-256 cipher type (PKCS#11 provider)
+ *
+ * @return the AES-256-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(aes_256_cbc,
+ CKM_AES_CBC,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The AES-128 CFB8 cipher type (PKCS#11 provider)
+ *
+ * @return the AES-128-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(aes_128_cfb8,
+ CKM_AES_CFB8,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CFB8_MODE)
+
+/**
+ * The AES-192 CFB8 cipher type (PKCS#11 provider)
+ *
+ * @return the AES-192-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(aes_192_cfb8,
+ CKM_AES_CFB8,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CFB8_MODE)
+
+/**
+ * The AES-256 CFB8 cipher type (PKCS#11 provider)
+ *
+ * @return the AES-256-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(aes_256_cfb8,
+ CKM_AES_CFB8,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CFB8_MODE)
+
+/**
+ * The RC2 cipher type - PKCS#11
+ *
+ * @return the RC2 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(rc2_cbc,
+ CKM_RC2_CBC,
+ 8,
+ 16,
+ 8,
+ EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH)
+
+/**
+ * The RC2-40 cipher type - PKCS#11
+ *
+ * @return the RC2-40 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(rc2_40_cbc,
+ CKM_RC2_CBC,
+ 8,
+ 5,
+ 8,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The RC2-64 cipher type - PKCS#11
+ *
+ * @return the RC2-64 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(rc2_64_cbc,
+ CKM_RC2_CBC,
+ 8,
+ 8,
+ 8,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The Camellia-128 cipher type - PKCS#11
+ *
+ * @return the Camellia-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(camellia_128_cbc,
+ CKM_CAMELLIA_CBC,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The Camellia-198 cipher type - PKCS#11
+ *
+ * @return the Camellia-198 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(camellia_192_cbc,
+ CKM_CAMELLIA_CBC,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The Camellia-256 cipher type - PKCS#11
+ *
+ * @return the Camellia-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(camellia_256_cbc,
+ CKM_CAMELLIA_CBC,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CBC_MODE)
+
+/**
+ * The RC4 cipher type (PKCS#11 provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(rc4,
+ CKM_RC4,
+ 1,
+ 16,
+ 0,
+ EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH)
+
+/**
+ * The RC4-40 cipher type (PKCS#11 provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+PKCS11_CIPHER_ALGORITHM(rc4_40,
+ CKM_RC4,
+ 1,
+ 5,
+ 0,
+ EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH)
+
+PKCS11_MD_ALGORITHM(md2, CKM_MD2, 16, 16)
+#ifdef CKM_MD4 /* non-standard extension */
+PKCS11_MD_ALGORITHM(md4, CKM_MD4, 16, 64)
+#else
+PKCS11_MD_ALGORITHM_UNAVAILABLE(md4)
+#endif
+PKCS11_MD_ALGORITHM(md5, CKM_MD5, 16, 64)
+PKCS11_MD_ALGORITHM(sha1, CKM_SHA_1, 20, 64)
+PKCS11_MD_ALGORITHM(sha256, CKM_SHA256, 32, 64)
+PKCS11_MD_ALGORITHM(sha384, CKM_SHA384, 48, 128)
+PKCS11_MD_ALGORITHM(sha512, CKM_SHA512, 64, 128)
diff --git a/lib/hcrypto/evp-pkcs11.h b/lib/hcrypto/evp-pkcs11.h
new file mode 100644
index 000000000000..6d8a21f528bb
--- /dev/null
+++ b/lib/hcrypto/evp-pkcs11.h
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2015, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_EVP_PKCS11_H
+#define HEIM_EVP_PKCS11_H 1
+
+/* symbol renaming */
+
+#define EVP_pkcs11_md2() hc_EVP_pkcs11_md2()
+#define EVP_pkcs11_md4() hc_EVP_pkcs11_md4()
+#define EVP_pkcs11_md5() hc_EVP_pkcs11_md5()
+#define EVP_pkcs11_sha1() hc_EVP_pkcs11_sha1()
+#define EVP_pkcs11_sha256() hc_EVP_pkcs11_sha256()
+#define EVP_pkcs11_sha384() hc_EVP_pkcs11_sha384()
+#define EVP_pkcs11_sha512() hc_EVP_pkcs11_sha512()
+#define EVP_pkcs11_des_cbc() hc_EVP_pkcs11_des_cbc()
+#define EVP_pkcs11_des_ede3_cbc() hc_EVP_pkcs11_des_ede3_cbc()
+#define EVP_pkcs11_aes_128_cbc() hc_EVP_pkcs11_aes_128_cbc()
+#define EVP_pkcs11_aes_192_cbc() hc_EVP_pkcs11_aes_192_cbc()
+#define EVP_pkcs11_aes_256_cbc() hc_EVP_pkcs11_aes_256_cbc()
+#define EVP_pkcs11_aes_128_cfb8() hc_EVP_pkcs11_aes_128_cfb8()
+#define EVP_pkcs11_aes_192_cfb8() hc_EVP_pkcs11_aes_192_cfb8()
+#define EVP_pkcs11_aes_256_cfb8() hc_EVP_pkcs11_aes_256_cfb8()
+#define EVP_pkcs11_rc4() hc_EVP_pkcs11_rc4()
+#define EVP_pkcs11_rc4_40() hc_EVP_pkcs11_rc4_40()
+#define EVP_pkcs11_rc2_40_cbc() hc_EVP_pkcs11_rc2_40_cbc()
+#define EVP_pkcs11_rc2_64_cbc() hc_EVP_pkcs11_rc2_64_cbc()
+#define EVP_pkcs11_rc2_cbc() hc_EVP_pkcs11_rc2_cbc()
+#define EVP_pkcs11_camellia_128_cbc() hc_EVP_pkcs11_camellia_128_cbc()
+#define EVP_pkcs11_camellia_192_cbc() hc_EVP_pkcs11_camellia_192_cbc()
+#define EVP_pkcs11_camellia_256_cbc() hc_EVP_pkcs11_camellia_256_cbc()
+
+#define EVP_pkcs11_hcrypto_md2() hc_EVP_pkcs11_hcrypto_md2()
+#define EVP_pkcs11_hcrypto_md4() hc_EVP_pkcs11_hcrypto_md4()
+#define EVP_pkcs11_hcrypto_md5() hc_EVP_pkcs11_hcrypto_md5()
+#define EVP_pkcs11_hcrypto_sha1() hc_EVP_pkcs11_hcrypto_sha1()
+#define EVP_pkcs11_hcrypto_sha256() hc_EVP_pkcs11_hcrypto_sha256()
+#define EVP_pkcs11_hcrypto_sha384() hc_EVP_pkcs11_hcrypto_sha384()
+#define EVP_pkcs11_hcrypto_sha512() hc_EVP_pkcs11_hcrypto_sha512()
+#define EVP_pkcs11_hcrypto_des_cbc() hc_EVP_pkcs11_hcrypto_des_cbc()
+#define EVP_pkcs11_hcrypto_des_ede3_cbc() hc_EVP_pkcs11_hcrypto_des_ede3_cbc()
+#define EVP_pkcs11_hcrypto_aes_128_cbc() hc_EVP_pkcs11_hcrypto_aes_128_cbc()
+#define EVP_pkcs11_hcrypto_aes_192_cbc() hc_EVP_pkcs11_hcrypto_aes_192_cbc()
+#define EVP_pkcs11_hcrypto_aes_256_cbc() hc_EVP_pkcs11_hcrypto_aes_256_cbc()
+#define EVP_pkcs11_hcrypto_aes_128_cfb8() hc_EVP_pkcs11_hcrypto_aes_128_cfb8()
+#define EVP_pkcs11_hcrypto_aes_192_cfb8() hc_EVP_pkcs11_hcrypto_aes_192_cfb8()
+#define EVP_pkcs11_hcrypto_aes_256_cfb8() hc_EVP_pkcs11_hcrypto_aes_256_cfb8()
+#define EVP_pkcs11_hcrypto_rc4() hc_EVP_pkcs11_hcrypto_rc4()
+#define EVP_pkcs11_hcrypto_rc4_40() hc_EVP_pkcs11_hcrypto_rc4_40()
+#define EVP_pkcs11_hcrypto_rc2_40_cbc() hc_EVP_pkcs11_hcrypto_rc2_40_cbc()
+#define EVP_pkcs11_hcrypto_rc2_64_cbc() hc_EVP_pkcs11_hcrypto_rc2_64_cbc()
+#define EVP_pkcs11_hcrypto_rc2_cbc() hc_EVP_pkcs11_hcrypto_rc2_cbc()
+#define EVP_pkcs11_hcrypto_camellia_128_cbc() hc_EVP_pkcs11_hcrypto_camellia_128_cbc()
+#define EVP_pkcs11_hcrypto_camellia_192_cbc() hc_EVP_pkcs11_hcrypto_camellia_192_cbc()
+#define EVP_pkcs11_hcrypto_camellia_256_cbc() hc_EVP_pkcs11_hcrypto_camellia_256_cbc()
+
+HC_CPP_BEGIN
+
+/*
+ * Strict PKCS#11 implementations (these will return NULL if the underlying
+ * PKCS#11 implementation does not implement the cipher or hash).
+ */
+const EVP_MD * hc_EVP_pkcs11_md2(void);
+const EVP_MD * hc_EVP_pkcs11_md4(void);
+const EVP_MD * hc_EVP_pkcs11_md5(void);
+const EVP_MD * hc_EVP_pkcs11_sha1(void);
+const EVP_MD * hc_EVP_pkcs11_sha256(void);
+const EVP_MD * hc_EVP_pkcs11_sha384(void);
+const EVP_MD * hc_EVP_pkcs11_sha512(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_rc2_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_rc2_40_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_rc2_64_cbc(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_rc4(void);
+const EVP_CIPHER * hc_EVP_pkcs11_rc4_40(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_des_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_des_ede3_cbc(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_aes_128_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_aes_192_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_aes_256_cbc(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_aes_128_cfb8(void);
+const EVP_CIPHER * hc_EVP_pkcs11_aes_192_cfb8(void);
+const EVP_CIPHER * hc_EVP_pkcs11_aes_256_cfb8(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_camellia_128_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_camellia_192_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_camellia_256_cbc(void);
+
+/*
+ * PKCS#11 implementations with fallback to hcrypto.
+ */
+const EVP_MD * hc_EVP_pkcs11_hcrypto_md2(void);
+const EVP_MD * hc_EVP_pkcs11_hcrypto_md4(void);
+const EVP_MD * hc_EVP_pkcs11_hcrypto_md5(void);
+const EVP_MD * hc_EVP_pkcs11_hcrypto_sha1(void);
+const EVP_MD * hc_EVP_pkcs11_hcrypto_sha256(void);
+const EVP_MD * hc_EVP_pkcs11_hcrypto_sha384(void);
+const EVP_MD * hc_EVP_pkcs11_hcrypto_sha512(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_rc2_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_rc2_40_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_rc2_64_cbc(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_rc4(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_rc4_40(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_des_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_des_ede3_cbc(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_aes_128_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_aes_192_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_aes_256_cbc(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_aes_128_cfb8(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_aes_192_cfb8(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_aes_256_cfb8(void);
+
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_camellia_128_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_camellia_192_cbc(void);
+const EVP_CIPHER * hc_EVP_pkcs11_hcrypto_camellia_256_cbc(void);
+
+HC_CPP_END
+
+#endif /* HEIM_EVP_PKCS11_H */
diff --git a/lib/hcrypto/evp-w32.c b/lib/hcrypto/evp-w32.c
new file mode 100644
index 000000000000..7d14d1f4f309
--- /dev/null
+++ b/lib/hcrypto/evp-w32.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2015, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Windows fallback provider: decides whether to use hcrypto or
+ * wincng depending on whether bcrypt.dll is available (i.e. it
+ * is runtime compatible back to XP, but will use the native
+ * crypto APIs from Vista onwards).
+ */
+
+#include <config.h>
+#include <roken.h>
+
+#include <assert.h>
+
+#include <evp.h>
+#include <evp-w32.h>
+#include <evp-hcrypto.h>
+
+#include <evp-wincng.h>
+
+static LONG wincng_available = -1;
+
+static __inline int
+wincng_check_availability(void)
+{
+ if (wincng_available == -1) {
+ char szBCryptDllPath[MAX_PATH];
+ UINT cbBCryptDllPath;
+
+ cbBCryptDllPath = GetSystemDirectory(szBCryptDllPath,
+ sizeof(szBCryptDllPath));
+ if (cbBCryptDllPath > 0 &&
+ cbBCryptDllPath < sizeof(szBCryptDllPath) &&
+ strncat_s(szBCryptDllPath,
+ sizeof(szBCryptDllPath), "\\bcrypt.dll", 11) == 0) {
+ HANDLE hBCryptDll = LoadLibrary(szBCryptDllPath);
+
+ InterlockedCompareExchangeRelease(&wincng_available,
+ !!hBCryptDll, -1);
+ if (hBCryptDll)
+ FreeLibrary(hBCryptDll);
+ }
+ }
+
+ return wincng_available == 1;
+}
+
+BOOL WINAPI
+_hc_w32crypto_DllMain(HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+ if (fdwReason == DLL_PROCESS_DETACH) {
+ /*
+ * Don't bother cleaning up on process exit, only on
+ * FreeLibrary() (in which case lpvReserved will be NULL).
+ */
+ if (lpvReserved == NULL)
+ _hc_wincng_cleanup();
+ }
+
+ return TRUE;
+}
+
+#define EVP_W32CRYPTO_PROVIDER(type, name) \
+ \
+ const type *hc_EVP_w32crypto_ ##name (void) \
+ { \
+ if (wincng_check_availability()) \
+ return hc_EVP_wincng_ ##name (); \
+ else if (HCRYPTO_FALLBACK) \
+ return hc_EVP_hcrypto_ ##name (); \
+ else \
+ return NULL; \
+ }
+
+#define EVP_W32CRYPTO_PROVIDER_CNG_UNAVAILABLE(type, name) \
+ \
+ const type *hc_EVP_w32crypto_ ##name (void) \
+ { \
+ return hc_EVP_hcrypto_ ##name (); \
+ }
+
+EVP_W32CRYPTO_PROVIDER(EVP_MD, md2)
+EVP_W32CRYPTO_PROVIDER(EVP_MD, md4)
+EVP_W32CRYPTO_PROVIDER(EVP_MD, md5)
+EVP_W32CRYPTO_PROVIDER(EVP_MD, sha1)
+EVP_W32CRYPTO_PROVIDER(EVP_MD, sha256)
+EVP_W32CRYPTO_PROVIDER(EVP_MD, sha384)
+EVP_W32CRYPTO_PROVIDER(EVP_MD, sha512)
+
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, rc2_cbc)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, rc2_40_cbc)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, rc2_64_cbc)
+
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, rc4)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, rc4_40)
+
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, des_cbc)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, des_ede3_cbc)
+
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, aes_128_cbc)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, aes_192_cbc)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, aes_256_cbc)
+
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, aes_128_cfb8)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, aes_192_cfb8)
+EVP_W32CRYPTO_PROVIDER(EVP_CIPHER, aes_256_cfb8)
+
+EVP_W32CRYPTO_PROVIDER_CNG_UNAVAILABLE(EVP_CIPHER, camellia_128_cbc)
+EVP_W32CRYPTO_PROVIDER_CNG_UNAVAILABLE(EVP_CIPHER, camellia_192_cbc)
+EVP_W32CRYPTO_PROVIDER_CNG_UNAVAILABLE(EVP_CIPHER, camellia_256_cbc)
diff --git a/lib/hcrypto/evp-w32.h b/lib/hcrypto/evp-w32.h
new file mode 100644
index 000000000000..89bfa4d0af3d
--- /dev/null
+++ b/lib/hcrypto/evp-w32.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2015, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_EVP_W32_H
+#define HEIM_EVP_W32_H 1
+
+/* symbol renaming */
+#define EVP_w32crypto_md2() hc_EVP_w32crypto_md2()
+#define EVP_w32crypto_md4() hc_EVP_w32crypto_md4()
+#define EVP_w32crypto_md5() hc_EVP_w32crypto_md5()
+#define EVP_w32crypto_sha1() hc_EVP_w32crypto_sha1()
+#define EVP_w32crypto_sha256() hc_EVP_w32crypto_sha256()
+#define EVP_w32crypto_sha384() hc_EVP_w32crypto_sha384()
+#define EVP_w32crypto_sha512() hc_EVP_w32crypto_sha512()
+#define EVP_w32crypto_des_cbc() hc_EVP_w32crypto_des_cbc()
+#define EVP_w32crypto_des_ede3_cbc() hc_EVP_w32crypto_des_ede3_cbc()
+#define EVP_w32crypto_aes_128_cbc() hc_EVP_w32crypto_aes_128_cbc()
+#define EVP_w32crypto_aes_192_cbc() hc_EVP_w32crypto_aes_192_cbc()
+#define EVP_w32crypto_aes_256_cbc() hc_EVP_w32crypto_aes_256_cbc()
+#define EVP_w32crypto_aes_128_cfb8() hc_EVP_w32crypto_aes_128_cfb8()
+#define EVP_w32crypto_aes_192_cfb8() hc_EVP_w32crypto_aes_192_cfb8()
+#define EVP_w32crypto_aes_256_cfb8() hc_EVP_w32crypto_aes_256_cfb8()
+#define EVP_w32crypto_rc4() hc_EVP_w32crypto_rc4()
+#define EVP_w32crypto_rc4_40() hc_EVP_w32crypto_rc4_40()
+#define EVP_w32crypto_rc2_40_cbc() hc_EVP_w32crypto_rc2_40_cbc()
+#define EVP_w32crypto_rc2_64_cbc() hc_EVP_w32crypto_rc2_64_cbc()
+#define EVP_w32crypto_rc2_cbc() hc_EVP_w32crypto_rc2_cbc()
+#define EVP_w32crypto_camellia_128_cbc() hc_EVP_w32crypto_camellia_128_cbc()
+#define EVP_w32crypto_camellia_192_cbc() hc_EVP_w32crypto_camellia_192_cbc()
+#define EVP_w32crypto_camellia_256_cbc() hc_EVP_w32crypto_camellia_256_cbc()
+
+/*
+ * This provider dynamically selects between Windows CNG (if running
+ * on Vista or above) or the inbuilt provider (if running on XP).
+ */
+
+HC_CPP_BEGIN
+
+const EVP_MD * hc_EVP_w32crypto_md2(void);
+const EVP_MD * hc_EVP_w32crypto_md4(void);
+const EVP_MD * hc_EVP_w32crypto_md5(void);
+const EVP_MD * hc_EVP_w32crypto_sha1(void);
+const EVP_MD * hc_EVP_w32crypto_sha256(void);
+const EVP_MD * hc_EVP_w32crypto_sha384(void);
+const EVP_MD * hc_EVP_w32crypto_sha512(void);
+
+const EVP_CIPHER * hc_EVP_w32crypto_des_cbc(void);
+const EVP_CIPHER * hc_EVP_w32crypto_des_ede3_cbc(void);
+
+const EVP_CIPHER * hc_EVP_w32crypto_aes_128_cbc(void);
+const EVP_CIPHER * hc_EVP_w32crypto_aes_192_cbc(void);
+const EVP_CIPHER * hc_EVP_w32crypto_aes_256_cbc(void);
+
+const EVP_CIPHER * hc_EVP_w32crypto_aes_128_cfb8(void);
+const EVP_CIPHER * hc_EVP_w32crypto_aes_192_cfb8(void);
+const EVP_CIPHER * hc_EVP_w32crypto_aes_256_cfb8(void);
+
+const EVP_CIPHER * hc_EVP_w32crypto_rc4(void);
+const EVP_CIPHER * hc_EVP_w32crypto_rc4_40(void);
+
+const EVP_CIPHER * hc_EVP_w32crypto_rc2_cbc(void);
+const EVP_CIPHER * hc_EVP_w32crypto_rc2_40_cbc(void);
+const EVP_CIPHER * hc_EVP_w32crypto_rc2_64_cbc(void);
+
+const EVP_CIPHER * hc_EVP_w32crypto_camellia_128_cbc(void);
+const EVP_CIPHER * hc_EVP_w32crypto_camellia_192_cbc(void);
+const EVP_CIPHER * hc_EVP_w32crypto_camellia_256_cbc(void);
+
+HC_CPP_END
+
+#endif /* HEIM_EVP_W32_H */
diff --git a/lib/hcrypto/evp-wincng.c b/lib/hcrypto/evp-wincng.c
new file mode 100644
index 000000000000..8a8b91cb1ab9
--- /dev/null
+++ b/lib/hcrypto/evp-wincng.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 2015, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Windows CNG provider */
+
+#include <config.h>
+#include <roken.h>
+#include <assert.h>
+
+#include <evp.h>
+#include <evp-wincng.h>
+
+#include <bcrypt.h>
+
+/*
+ * CNG cipher provider
+ */
+
+struct wincng_key {
+ BCRYPT_KEY_HANDLE hKey;
+ UCHAR rgbKeyObject[1];
+};
+
+#define WINCNG_KEY_OBJECT_SIZE(ctx) \
+ ((ctx)->cipher->ctx_size - sizeof(struct wincng_key) + 1)
+
+static int
+wincng_do_cipher(EVP_CIPHER_CTX *ctx,
+ unsigned char *out,
+ const unsigned char *in,
+ unsigned int size)
+{
+ struct wincng_key *cng = ctx->cipher_data;
+ NTSTATUS status;
+ ULONG cbResult;
+
+ assert(EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_STREAM_CIPHER ||
+ (size % ctx->cipher->block_size) == 0);
+
+ if (ctx->encrypt) {
+ status = BCryptEncrypt(cng->hKey,
+ (PUCHAR)in,
+ size,
+ NULL, /* pPaddingInfo */
+ ctx->cipher->iv_len ? ctx->iv : NULL,
+ ctx->cipher->iv_len,
+ out,
+ size,
+ &cbResult,
+ 0);
+ } else {
+ status = BCryptDecrypt(cng->hKey,
+ (PUCHAR)in,
+ size,
+ NULL, /* pPaddingInfo */
+ ctx->cipher->iv_len ? ctx->iv : NULL,
+ ctx->cipher->iv_len,
+ out,
+ size,
+ &cbResult,
+ 0);
+ }
+
+ return BCRYPT_SUCCESS(status) && cbResult == size;
+}
+
+static int
+wincng_cleanup(EVP_CIPHER_CTX *ctx)
+{
+ struct wincng_key *cng = ctx->cipher_data;
+
+ if (cng->hKey)
+ BCryptDestroyKey(cng->hKey);
+ SecureZeroMemory(cng->rgbKeyObject, WINCNG_KEY_OBJECT_SIZE(ctx));
+
+ return 1;
+}
+
+static int
+wincng_cipher_algorithm_init(EVP_CIPHER *cipher,
+ LPWSTR pszAlgId)
+{
+ BCRYPT_ALG_HANDLE hAlgorithm = NULL;
+ NTSTATUS status;
+ LPCWSTR pszChainingMode;
+ ULONG cbKeyObject, cbChainingMode, cbData;
+
+ if (cipher->app_data)
+ return 1;
+
+ status = BCryptOpenAlgorithmProvider(&hAlgorithm,
+ pszAlgId,
+ NULL,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return 0;
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR)&cbKeyObject,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return 0;
+ }
+
+ cipher->ctx_size = sizeof(struct wincng_key) + cbKeyObject - 1;
+
+ switch (cipher->flags & EVP_CIPH_MODE) {
+ case EVP_CIPH_CBC_MODE:
+ pszChainingMode = BCRYPT_CHAIN_MODE_CBC;
+ cbChainingMode = sizeof(BCRYPT_CHAIN_MODE_CBC);
+ break;
+ case EVP_CIPH_CFB8_MODE:
+ pszChainingMode = BCRYPT_CHAIN_MODE_CFB;
+ cbChainingMode = sizeof(BCRYPT_CHAIN_MODE_CFB);
+ break;
+ default:
+ pszChainingMode = NULL;
+ cbChainingMode = 0;
+ break;
+ }
+
+ if (cbChainingMode) {
+ status = BCryptSetProperty(hAlgorithm,
+ BCRYPT_CHAINING_MODE,
+ (PUCHAR)pszChainingMode,
+ cbChainingMode,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return 0;
+ }
+ }
+
+ if (wcscmp(pszAlgId, BCRYPT_RC2_ALGORITHM) == 0) {
+ ULONG cbEffectiveKeyLength = EVP_CIPHER_key_length(cipher) * 8;
+
+ status = BCryptSetProperty(hAlgorithm,
+ BCRYPT_EFFECTIVE_KEY_LENGTH,
+ (PUCHAR)&cbEffectiveKeyLength,
+ sizeof(cbEffectiveKeyLength),
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return 0;
+ }
+ }
+
+ InterlockedCompareExchangePointerRelease(&cipher->app_data,
+ hAlgorithm, NULL);
+ return 1;
+}
+
+static int
+wincng_key_init(EVP_CIPHER_CTX *ctx,
+ const unsigned char *key,
+ const unsigned char *iv,
+ int encp)
+{
+ struct wincng_key *cng = ctx->cipher_data;
+ NTSTATUS status;
+
+ assert(cng != NULL);
+ assert(ctx->cipher != NULL);
+
+ if (ctx->cipher->app_data == NULL)
+ return 0;
+
+ /*
+ * Note: ctx->key_len not EVP_CIPHER_CTX_key_length() for
+ * variable length key support.
+ */
+ status = BCryptGenerateSymmetricKey(ctx->cipher->app_data,
+ &cng->hKey,
+ cng->rgbKeyObject,
+ WINCNG_KEY_OBJECT_SIZE(ctx),
+ (PUCHAR)key,
+ ctx->key_len,
+ 0);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+#define WINCNG_CIPHER_ALGORITHM(name, alg_id, block_size, key_len, \
+ iv_len, flags) \
+ \
+ static EVP_CIPHER \
+ wincng_##name = { \
+ 0, \
+ block_size, \
+ key_len, \
+ iv_len, \
+ flags, \
+ wincng_key_init, \
+ wincng_do_cipher, \
+ wincng_cleanup, \
+ 0, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL \
+ }; \
+ \
+ const EVP_CIPHER * \
+ hc_EVP_wincng_##name(void) \
+ { \
+ wincng_cipher_algorithm_init(&wincng_##name, alg_id); \
+ return wincng_##name.app_data ? &wincng_##name : NULL; \
+ }
+
+#define WINCNG_CIPHER_ALGORITHM_CLEANUP(name) do { \
+ if (wincng_##name.app_data) { \
+ BCryptCloseAlgorithmProvider(wincng_##name.app_data, 0); \
+ wincng_##name.app_data = NULL; \
+ } \
+ } while (0)
+
+#define WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(name) \
+ \
+ const EVP_CIPHER * \
+ hc_EVP_wincng_##name(void) \
+ { \
+ return NULL; \
+ }
+
+/**
+ * The triple DES cipher type (Windows CNG provider)
+ *
+ * @return the DES-EDE3-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(des_ede3_cbc,
+ BCRYPT_3DES_ALGORITHM,
+ 8,
+ 24,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The DES cipher type (Windows CNG provider)
+ *
+ * @return the DES-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(des_cbc,
+ BCRYPT_DES_ALGORITHM,
+ 8,
+ 8,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-128 cipher type (Windows CNG provider)
+ *
+ * @return the AES-128-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_128_cbc,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-192 cipher type (Windows CNG provider)
+ *
+ * @return the AES-192-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_192_cbc,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-256 cipher type (Windows CNG provider)
+ *
+ * @return the AES-256-CBC EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_256_cbc,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The AES-128 CFB8 cipher type (Windows CNG provider)
+ *
+ * @return the AES-128-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_128_cfb8,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 16,
+ 16,
+ EVP_CIPH_CFB8_MODE);
+
+/**
+ * The AES-192 CFB8 cipher type (Windows CNG provider)
+ *
+ * @return the AES-192-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_192_cfb8,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 24,
+ 16,
+ EVP_CIPH_CFB8_MODE);
+
+/**
+ * The AES-256 CFB8 cipher type (Windows CNG provider)
+ *
+ * @return the AES-256-CFB8 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(aes_256_cfb8,
+ BCRYPT_AES_ALGORITHM,
+ 16,
+ 32,
+ 16,
+ EVP_CIPH_CFB8_MODE);
+
+/**
+ * The RC2 cipher type - Windows CNG
+ *
+ * @return the RC2 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc2_cbc,
+ BCRYPT_RC2_ALGORITHM,
+ 8,
+ 16,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The RC2-40 cipher type - Windows CNG
+ *
+ * @return the RC2-40 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc2_40_cbc,
+ BCRYPT_RC2_ALGORITHM,
+ 8,
+ 5,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The RC2-64 cipher type - Windows CNG
+ *
+ * @return the RC2-64 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc2_64_cbc,
+ BCRYPT_RC2_ALGORITHM,
+ 8,
+ 8,
+ 8,
+ EVP_CIPH_CBC_MODE);
+
+/**
+ * The Camellia-128 cipher type - CommonCrypto
+ *
+ * @return the Camellia-128 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_128_cbc);
+
+/**
+ * The Camellia-198 cipher type - CommonCrypto
+ *
+ * @return the Camellia-198 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_192_cbc);
+
+/**
+ * The Camellia-256 cipher type - CommonCrypto
+ *
+ * @return the Camellia-256 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_256_cbc);
+
+/**
+ * The RC4 cipher type (Windows CNG provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc4,
+ BCRYPT_RC4_ALGORITHM,
+ 1,
+ 16,
+ 0,
+ EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH);
+
+/**
+ * The RC4-40 cipher type (Windows CNG provider)
+ *
+ * @return the RC4 EVP_CIPHER pointer.
+ *
+ * @ingroup hcrypto_evp
+ */
+
+WINCNG_CIPHER_ALGORITHM(rc4_40,
+ BCRYPT_RC4_ALGORITHM,
+ 1,
+ 5,
+ 0,
+ EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH);
+
+static void
+wincng_cipher_algorithm_cleanup(void)
+{
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(des_ede3_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(des_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_128_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_192_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_256_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_128_cfb8);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_192_cfb8);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_256_cfb8);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_40_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_64_cbc);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc4);
+ WINCNG_CIPHER_ALGORITHM_CLEANUP(rc4_40);
+}
+
+/*
+ * CNG digest provider
+ */
+
+struct wincng_md_ctx {
+ BCRYPT_HASH_HANDLE hHash;
+ ULONG cbHashObject;
+ UCHAR rgbHashObject[1];
+};
+
+static BCRYPT_ALG_HANDLE
+wincng_md_algorithm_init(EVP_MD *md,
+ LPCWSTR pszAlgId)
+{
+ BCRYPT_ALG_HANDLE hAlgorithm;
+ NTSTATUS status;
+ ULONG cbHashObject, cbData;
+ ULONG cbHash = 0, cbBlock = 0;
+
+ status = BCryptOpenAlgorithmProvider(&hAlgorithm,
+ pszAlgId,
+ NULL,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return NULL;
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_HASH_LENGTH,
+ (PUCHAR)&cbHash,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return NULL;
+ }
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_HASH_BLOCK_LENGTH,
+ (PUCHAR)&cbBlock,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return NULL;
+ }
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR)&cbHashObject,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status)) {
+ BCryptCloseAlgorithmProvider(hAlgorithm, 0);
+ return NULL;
+ }
+
+ md->hash_size = cbHash;
+ md->block_size = cbBlock;
+ md->ctx_size = sizeof(struct wincng_md_ctx) + cbHashObject - 1;
+
+ return hAlgorithm;
+}
+
+static int
+wincng_md_hash_init(BCRYPT_ALG_HANDLE hAlgorithm,
+ EVP_MD_CTX *ctx)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+ NTSTATUS status;
+ ULONG cbData;
+
+ status = BCryptGetProperty(hAlgorithm,
+ BCRYPT_OBJECT_LENGTH,
+ (PUCHAR)&cng->cbHashObject,
+ sizeof(ULONG),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return 0;
+
+ status = BCryptCreateHash(hAlgorithm,
+ &cng->hHash,
+ cng->rgbHashObject,
+ cng->cbHashObject,
+ NULL,
+ 0,
+ 0);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+static int
+wincng_md_update(EVP_MD_CTX *ctx,
+ const void *data,
+ size_t length)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+ NTSTATUS status;
+
+ status = BCryptHashData(cng->hHash, (PUCHAR)data, length, 0);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+static int
+wincng_md_final(void *digest,
+ EVP_MD_CTX *ctx)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+ NTSTATUS status;
+ ULONG cbHash, cbData;
+
+ status = BCryptGetProperty(cng->hHash,
+ BCRYPT_HASH_LENGTH,
+ (PUCHAR)&cbHash,
+ sizeof(DWORD),
+ &cbData,
+ 0);
+ if (!BCRYPT_SUCCESS(status))
+ return 0;
+
+ status = BCryptFinishHash(cng->hHash,
+ digest,
+ cbHash,
+ 0);
+
+ return BCRYPT_SUCCESS(status);
+}
+
+static int
+wincng_md_cleanup(EVP_MD_CTX *ctx)
+{
+ struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx;
+
+ if (cng->hHash)
+ BCryptDestroyHash(cng->hHash);
+ SecureZeroMemory(cng->rgbHashObject, cng->cbHashObject);
+
+ return 1;
+}
+
+#define WINCNG_MD_ALGORITHM(name, alg_id) \
+ \
+ static BCRYPT_ALG_HANDLE wincng_hAlgorithm_##name; \
+ \
+ static int wincng_##name##_init(EVP_MD_CTX *ctx) \
+ { \
+ return wincng_md_hash_init(wincng_hAlgorithm_##name, ctx); \
+ } \
+ \
+ const EVP_MD * \
+ hc_EVP_wincng_##name(void) \
+ { \
+ static struct hc_evp_md name = { \
+ 0, \
+ 0, \
+ 0, \
+ wincng_##name##_init, \
+ wincng_md_update, \
+ wincng_md_final, \
+ wincng_md_cleanup \
+ }; \
+ \
+ if (wincng_hAlgorithm_##name == NULL) { \
+ BCRYPT_ALG_HANDLE hAlgorithm = \
+ wincng_md_algorithm_init(&name, alg_id); \
+ InterlockedCompareExchangePointerRelease( \
+ &wincng_hAlgorithm_##name, hAlgorithm, NULL); \
+ } \
+ return wincng_hAlgorithm_##name ? &name : NULL; \
+ }
+
+#define WINCNG_MD_ALGORITHM_CLEANUP(name) do { \
+ if (wincng_hAlgorithm_##name) { \
+ BCryptCloseAlgorithmProvider(wincng_hAlgorithm_##name, 0); \
+ wincng_hAlgorithm_##name = NULL; \
+ } \
+ } while (0)
+
+WINCNG_MD_ALGORITHM(md2, BCRYPT_MD2_ALGORITHM);
+WINCNG_MD_ALGORITHM(md4, BCRYPT_MD4_ALGORITHM);
+WINCNG_MD_ALGORITHM(md5, BCRYPT_MD5_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha1, BCRYPT_SHA1_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha256, BCRYPT_SHA256_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha384, BCRYPT_SHA384_ALGORITHM);
+WINCNG_MD_ALGORITHM(sha512, BCRYPT_SHA512_ALGORITHM);
+
+static void
+wincng_md_algorithm_cleanup(void)
+{
+ WINCNG_MD_ALGORITHM_CLEANUP(md2);
+ WINCNG_MD_ALGORITHM_CLEANUP(md4);
+ WINCNG_MD_ALGORITHM_CLEANUP(md5);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha1);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha256);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha384);
+ WINCNG_MD_ALGORITHM_CLEANUP(sha512);
+}
+
+void _hc_wincng_cleanup(void)
+{
+ wincng_md_algorithm_cleanup();
+ wincng_cipher_algorithm_cleanup();
+}
diff --git a/lib/hcrypto/evp-wincng.h b/lib/hcrypto/evp-wincng.h
new file mode 100644
index 000000000000..ed7037a11450
--- /dev/null
+++ b/lib/hcrypto/evp-wincng.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIM_EVP_WINCNG_H
+#define HEIM_EVP_WINCNG_H 1
+
+/* symbol renaming */
+#define EVP_wincng_md2() EVP_wincng_md2()
+#define EVP_wincng_md4() EVP_wincng_md4()
+#define EVP_wincng_md5() EVP_wincng_md5()
+#define EVP_wincng_sha1() EVP_wincng_sha1()
+#define EVP_wincng_sha256() EVP_wincng_sha256()
+#define EVP_wincng_sha384() EVP_wincng_sha384()
+#define EVP_wincng_sha512() EVP_wincng_sha512()
+#define EVP_wincng_des_cbc() EVP_wincng_des_cbc()
+#define EVP_wincng_des_ede3_cbc() EVP_wincng_des_ede3_cbc()
+#define EVP_wincng_aes_128_cbc() EVP_wincng_aes_128_cbc()
+#define EVP_wincng_aes_192_cbc() EVP_wincng_aes_192_cbc()
+#define EVP_wincng_aes_256_cbc() EVP_wincng_aes_256_cbc()
+#define EVP_wincng_aes_128_cfb8() EVP_wincng_aes_128_cfb8()
+#define EVP_wincng_aes_192_cfb8() EVP_wincng_aes_192_cfb8()
+#define EVP_wincng_aes_256_cfb8() EVP_wincng_aes_256_cfb8()
+#define EVP_wincng_rc4() EVP_wincng_rc4()
+#define EVP_wincng_rc4_40() EVP_wincng_rc4_40()
+#define EVP_wincng_rc2_40_cbc() EVP_wincng_rc2_40_cbc()
+#define EVP_wincng_rc2_64_cbc() EVP_wincng_rc2_64_cbc()
+#define EVP_wincng_rc2_cbc() EVP_wincng_rc2_cbc()
+#define EVP_wincng_camellia_128_cbc() EVP_wincng_camellia_128_cbc()
+#define EVP_wincng_camellia_192_cbc() EVP_wincng_camellia_192_cbc()
+#define EVP_wincng_camellia_256_cbc() EVP_wincng_camellia_256_cbc()
+
+/*
+ *
+ */
+
+HC_CPP_BEGIN
+
+const EVP_MD * hc_EVP_wincng_md2(void);
+const EVP_MD * hc_EVP_wincng_md4(void);
+const EVP_MD * hc_EVP_wincng_md5(void);
+const EVP_MD * hc_EVP_wincng_sha1(void);
+const EVP_MD * hc_EVP_wincng_sha256(void);
+const EVP_MD * hc_EVP_wincng_sha384(void);
+const EVP_MD * hc_EVP_wincng_sha512(void);
+
+const EVP_CIPHER * hc_EVP_wincng_rc2_cbc(void);
+const EVP_CIPHER * hc_EVP_wincng_rc2_40_cbc(void);
+const EVP_CIPHER * hc_EVP_wincng_rc2_64_cbc(void);
+
+const EVP_CIPHER * hc_EVP_wincng_rc4(void);
+const EVP_CIPHER * hc_EVP_wincng_rc4_40(void);
+
+const EVP_CIPHER * hc_EVP_wincng_des_cbc(void);
+const EVP_CIPHER * hc_EVP_wincng_des_ede3_cbc(void);
+
+const EVP_CIPHER * hc_EVP_wincng_aes_128_cbc(void);
+const EVP_CIPHER * hc_EVP_wincng_aes_192_cbc(void);
+const EVP_CIPHER * hc_EVP_wincng_aes_256_cbc(void);
+
+const EVP_CIPHER * hc_EVP_wincng_aes_128_cfb8(void);
+const EVP_CIPHER * hc_EVP_wincng_aes_192_cfb8(void);
+const EVP_CIPHER * hc_EVP_wincng_aes_256_cfb8(void);
+
+void _hc_wincng_cleanup(void);
+
+HC_CPP_END
+
+#endif /* HEIM_EVP_WINCNG_H */
diff --git a/lib/hcrypto/evp.c b/lib/hcrypto/evp.c
index 75eefc49312e..23838709c8e9 100644
--- a/lib/hcrypto/evp.c
+++ b/lib/hcrypto/evp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 - 2008 Kungliga Tekniska Högskolan
+ * Copyright (c) 2006 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -34,25 +34,34 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
+#include <roken.h>
#define HC_DEPRECATED
#define HC_DEPRECATED_CRYPTO
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <assert.h>
#include <evp.h>
#include <evp-hcrypto.h>
#include <evp-cc.h>
+#if defined(_WIN32)
+#include <evp-w32.h>
+#endif
+#include <evp-pkcs11.h>
+#include <evp-openssl.h>
#include <krb5-types.h>
-#include <roken.h>
#ifndef HCRYPTO_DEF_PROVIDER
-#define HCRYPTO_DEF_PROVIDER hcrypto
+# ifdef __APPLE__
+# define HCRYPTO_DEF_PROVIDER cc
+# elif __sun
+# define HCRYPTO_DEF_PROVIDER pkcs11_hcrypto
+# elif HAVE_HCRYPTO_W_OPENSSL
+# define HCRYPTO_DEF_PROVIDER ossl
+# else
+# define HCRYPTO_DEF_PROVIDER hcrypto
+# endif
#endif
#define HC_CONCAT4(x,y,z,aa) x ## y ## z ## aa
@@ -175,10 +184,13 @@ EVP_MD_CTX_destroy(EVP_MD_CTX *ctx)
int
EVP_MD_CTX_cleanup(EVP_MD_CTX *ctx) HC_DEPRECATED
{
- if (ctx->md && ctx->md->cleanup)
- (ctx->md->cleanup)(ctx);
- else if (ctx->md)
+ if (ctx->md && ctx->md->cleanup) {
+ int ret = (ctx->md->cleanup)(ctx->ptr);
+ if (!ret)
+ return ret;
+ } else if (ctx->md) {
memset(ctx->ptr, 0, ctx->md->ctx_size);
+ }
ctx->md = NULL;
ctx->engine = NULL;
free(ctx->ptr);
@@ -253,13 +265,16 @@ EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, ENGINE *engine)
EVP_MD_CTX_cleanup(ctx);
ctx->md = md;
ctx->engine = engine;
+ if (md == NULL)
+ return 0;
ctx->ptr = calloc(1, md->ctx_size);
if (ctx->ptr == NULL)
return 0;
}
- (ctx->md->init)(ctx->ptr);
- return 1;
+ if (ctx->md == 0)
+ return 0;
+ return (ctx->md->init)(ctx->ptr);
}
/**
@@ -582,10 +597,14 @@ EVP_CIPHER_CTX_init(EVP_CIPHER_CTX *c)
int
EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *c)
{
- if (c->cipher && c->cipher->cleanup)
- c->cipher->cleanup(c);
+ if (c->cipher && c->cipher->cleanup) {
+ int ret = c->cipher->cleanup(c);
+ if (!ret)
+ return ret;
+ }
if (c->cipher_data) {
- memset(c->cipher_data, 0, c->cipher->ctx_size);
+ if (c->cipher)
+ memset(c->cipher_data, 0, c->cipher->ctx_size);
free(c->cipher_data);
c->cipher_data = NULL;
}
@@ -787,6 +806,10 @@ EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *c, ENGINE *engine,
/* assume block size is a multiple of 2 */
ctx->block_mask = EVP_CIPHER_block_size(c) - 1;
+ if ((ctx->cipher->flags & EVP_CIPH_CTRL_INIT) &&
+ !EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_INIT, 0, NULL))
+ return 0;
+
} else if (ctx->cipher == NULL) {
/* reuse of cipher, but not any cipher ever set! */
return 0;
@@ -814,7 +837,7 @@ EVP_CipherInit_ex(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *c, ENGINE *engine,
}
if (key || (ctx->cipher->flags & EVP_CIPH_ALWAYS_CALL_INIT))
- ctx->cipher->init(ctx, key, iv, encp);
+ return ctx->cipher->init(ctx, key, iv, encp);
return 1;
}
@@ -1116,7 +1139,7 @@ EVP_des_cbc(void)
}
/**
- * The tripple DES cipher type
+ * The triple DES cipher type
*
* @return the DES-EDE3-CBC EVP_CIPHER pointer.
*
diff --git a/lib/hcrypto/evp.h b/lib/hcrypto/evp.h
index 626c46329614..a0369db72847 100644
--- a/lib/hcrypto/evp.h
+++ b/lib/hcrypto/evp.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2005 - 2008 Kungliga Tekniska Högskolan
+ * Copyright (c) 2005 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -98,6 +98,7 @@
#define EVP_sha256 hc_EVP_sha256
#define EVP_sha384 hc_EVP_sha384
#define EVP_sha512 hc_EVP_sha512
+#define PKCS5_PBKDF2_HMAC hc_PKCS5_PBKDF2_HMAC
#define PKCS5_PBKDF2_HMAC_SHA1 hc_PKCS5_PBKDF2_HMAC_SHA1
#define EVP_BytesToKey hc_EVP_BytesToKey
#define EVP_get_cipherbyname hc_EVP_get_cipherbyname
@@ -108,6 +109,27 @@
#define EVP_CIPHER_CTX_rand_key hc_EVP_CIPHER_CTX_rand_key
#define hcrypto_validate hc_hcrypto_validate
+/* Type name renaming */
+#define EVP_MD_CTX hc_EVP_MD_CTX
+#define EVP_PKEY hc_EVP_PKEY
+#define EVP_MD hc_EVP_MD
+#define EVP_CIPHER hc_EVP_CIPHER
+#define EVP_CIPHER_CTX hc_EVP_CIPHER_CTX
+
+/* Constant renaming */
+#define EVP_CIPH_STREAM_CIPHER hc_EVP_CIPH_STREAM_CIPHER
+#define EVP_CIPH_CBC_MODE hc_EVP_CIPH_CBC_MODE
+#define EVP_CIPH_CFB8_MODE hc_EVP_CIPH_CFB8_MODE
+#define EVP_CIPH_MODE hc_EVP_CIPH_MODE
+#define EVP_CIPH_CTRL_INIT hc_EVP_CIPH_CTRL_INIT
+
+#define EVP_CIPH_VARIABLE_LENGTH hc_EVP_CIPH_VARIABLE_LENGTH
+#define EVP_CIPH_ALWAYS_CALL_INIT hc_EVP_CIPH_ALWAYS_CALL_INIT
+#define EVP_CIPH_RAND_KEY hc_EVP_CIPH_RAND_KEY
+
+#define EVP_CTRL_INIT hc_EVP_CTRL_INIT
+
+
/*
*
*/
@@ -133,22 +155,25 @@ struct hc_CIPHER {
* cipher is used in (use EVP_CIPHER.._mode() to extract the
* mode). The rest of the flag field is a bitfield.
*/
-#define EVP_CIPH_STREAM_CIPHER 0
-#define EVP_CIPH_CBC_MODE 2
-#define EVP_CIPH_CFB8_MODE 4
-#define EVP_CIPH_MODE 0x7
+#define hc_EVP_CIPH_STREAM_CIPHER 0
+#define hc_EVP_CIPH_CBC_MODE 2
+#define hc_EVP_CIPH_CFB8_MODE 4
+#define hc_EVP_CIPH_MODE 0x7
+#define hc_EVP_CIPH_CTRL_INIT 0x40
-#define EVP_CIPH_VARIABLE_LENGTH 0x008 /* variable key length */
-#define EVP_CIPH_ALWAYS_CALL_INIT 0x020
-#define EVP_CIPH_RAND_KEY 0x200
+#define hc_EVP_CTRL_INIT 0x0
+
+#define hc_EVP_CIPH_VARIABLE_LENGTH 0x008 /* variable key length */
+#define hc_EVP_CIPH_ALWAYS_CALL_INIT 0x020
+#define hc_EVP_CIPH_RAND_KEY 0x200
int (*init)(EVP_CIPHER_CTX*,const unsigned char*,const unsigned char*,int);
int (*do_cipher)(EVP_CIPHER_CTX *, unsigned char *,
const unsigned char *, unsigned int);
int (*cleanup)(EVP_CIPHER_CTX *);
int ctx_size;
- void *set_asn1_parameters;
- void *get_asn1_parameters;
+ int (*set_asn1_parameters)(void);
+ int (*get_asn1_parameters)(void);
int (*ctrl)(EVP_CIPHER_CTX *, int type, int arg, void *ptr);
#define EVP_CTRL_RAND_KEY 0x6
@@ -173,6 +198,10 @@ struct hc_CIPHER_CTX {
unsigned char final[EVP_MAX_BLOCK_LENGTH];
};
+/*
+ * LIES. It's not an EVP_MD_CTX that gets passed to these functions
+ * here in hcrypto, but an object of ctx_size.
+ */
typedef int (*hc_evp_md_init)(EVP_MD_CTX *);
typedef int (*hc_evp_md_update)(EVP_MD_CTX *,const void *, size_t);
typedef int (*hc_evp_md_final)(void *, EVP_MD_CTX *);
@@ -305,6 +334,9 @@ int EVP_CipherFinal_ex(EVP_CIPHER_CTX *, void *, int *);
int EVP_Cipher(EVP_CIPHER_CTX *,void *,const void *,size_t);
+int PKCS5_PBKDF2_HMAC(const void *, size_t, const void *, size_t,
+ unsigned long, const EVP_MD *, size_t, void *);
+
int PKCS5_PBKDF2_HMAC_SHA1(const void *, size_t, const void *, size_t,
unsigned long, size_t, void *);
diff --git a/lib/hcrypto/example_evp_cipher.c b/lib/hcrypto/example_evp_cipher.c
index f07ba8f99890..3bf76b9789ea 100644
--- a/lib/hcrypto/example_evp_cipher.c
+++ b/lib/hcrypto/example_evp_cipher.c
@@ -31,17 +31,22 @@
* SUCH DAMAGE.
*/
+#include <config.h>
+#include <roken.h>
+
#include <krb5-types.h> /* should really be stdint.h */
#include <hcrypto/evp.h>
+#include <hcrypto/evp-pkcs11.h>
+#ifdef __APPLE__
+#include <hcrypto/evp-cc.h>
+#endif
+#ifdef _WIN32
+#include <hcrypto/evp-w32.h>
+#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <err.h>
#include <assert.h>
-#include "roken.h"
-
/* key and initial vector */
static char key[16] =
"\xaa\xbb\x45\xd4\xaa\xbb\x45\xd4"
@@ -56,7 +61,7 @@ usage(int exit_code) __attribute__((noreturn));
static void
usage(int exit_code)
{
- printf("usage: %s in out\n", getprogname());
+ printf("usage: %s in out [pkcs11 | cc | w32]\n", getprogname());
exit(exit_code);
}
@@ -84,12 +89,26 @@ main(int argc, char **argv)
if (strcmp(argv[1], "--help") == 0)
usage(0);
usage(1);
- } else if (argc == 4) {
+ } else if (argc == 4 || argc == 5) {
block_size = atoi(argv[1]);
if (block_size == 0)
errx(1, "invalid blocksize %s", argv[1]);
ifn = argv[2];
ofn = argv[3];
+ if (argc == 5) {
+ if (strcmp(argv[4], "pkcs11") == 0)
+ c = hc_EVP_pkcs11_aes_128_cbc();
+#ifdef __APPLE__
+ else if (strcmp(argv[4], "cc") == 0)
+ c = hc_EVP_cc_aes_128_cbc();
+#endif
+#ifdef _WIN32
+ else if (strcmp(argv[4], "w32") == 0)
+ c = hc_EVP_w32crypto_aes_128_cbc();
+#endif
+ else
+ usage(1);
+ }
} else
usage(1);
diff --git a/lib/hcrypto/hash.h b/lib/hcrypto/hash.h
index 498e5b1af01c..019081487bb7 100644
--- a/lib/hcrypto/hash.h
+++ b/lib/hcrypto/hash.h
@@ -40,7 +40,6 @@
#ifdef KRB5
#include <krb5-types.h>
#endif
-#include <roken.h>
#ifndef min
#define min(a,b) (((a)>(b))?(b):(a))
diff --git a/lib/hcrypto/hmac.c b/lib/hcrypto/hmac.c
index d11bd9876988..29e6af479d6b 100644
--- a/lib/hcrypto/hmac.c
+++ b/lib/hcrypto/hmac.c
@@ -31,10 +31,9 @@
* SUCH DAMAGE.
*/
-#include <sys/types.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <config.h>
+#include <roken.h>
+
#include <hmac.h>
void
diff --git a/lib/hcrypto/libhcrypto-exports.def b/lib/hcrypto/libhcrypto-exports.def
index a7208f9319fc..ab692b4e5e4d 100644
--- a/lib/hcrypto/libhcrypto-exports.def
+++ b/lib/hcrypto/libhcrypto-exports.def
@@ -167,6 +167,8 @@ EXPORTS
;! hc_EVP_cc_md5
;! hc_EVP_cc_sha1
;! hc_EVP_cc_sha256
+;! hc_EVP_cc_sha384
+;! hc_EVP_cc_sha512
;! hc_EVP_cc_des_ede3_cbc
;! hc_EVP_cc_aes_128_cbc
;! hc_EVP_cc_aes_192_cbc
@@ -175,6 +177,63 @@ EXPORTS
;! hc_EVP_cc_aes_192_cfb8
;! hc_EVP_cc_aes_256_cfb8
+ hc_EVP_ossl_md2
+ hc_EVP_ossl_md4
+ hc_EVP_ossl_md5
+ hc_EVP_ossl_sha1
+ hc_EVP_ossl_sha256
+ hc_EVP_ossl_sha384
+ hc_EVP_ossl_sha512
+ hc_EVP_ossl_des_ede3_cbc
+ hc_EVP_ossl_aes_128_cbc
+ hc_EVP_ossl_aes_192_cbc
+ hc_EVP_ossl_aes_256_cbc
+ hc_EVP_ossl_aes_128_cfb8
+ hc_EVP_ossl_aes_192_cfb8
+ hc_EVP_ossl_aes_256_cfb8
+ hc_EVP_ossl_rc2_cbc
+ hc_EVP_ossl_rc2_40_cbc
+ hc_EVP_ossl_rc4
+ hc_EVP_ossl_rc4_40
+
+ hc_EVP_pkcs11_md2
+ hc_EVP_pkcs11_md4
+ hc_EVP_pkcs11_md5
+ hc_EVP_pkcs11_sha1
+ hc_EVP_pkcs11_sha256
+ hc_EVP_pkcs11_des_ede3_cbc
+ hc_EVP_pkcs11_aes_128_cbc
+ hc_EVP_pkcs11_aes_192_cbc
+ hc_EVP_pkcs11_aes_256_cbc
+ hc_EVP_pkcs11_aes_128_cfb8
+ hc_EVP_pkcs11_aes_192_cfb8
+ hc_EVP_pkcs11_aes_256_cfb8
+ hc_EVP_pkcs11_rc2_40_cbc
+ hc_EVP_pkcs11_rc2_64_cbc
+ hc_EVP_pkcs11_rc2_cbc
+ hc_EVP_pkcs11_rc4
+ hc_EVP_pkcs11_rc4_40
+
+ hc_EVP_w32crypto_md2 ;!
+ hc_EVP_w32crypto_md4 ;!
+ hc_EVP_w32crypto_md5 ;!
+ hc_EVP_w32crypto_sha1 ;!
+ hc_EVP_w32crypto_sha256 ;!
+ hc_EVP_w32crypto_sha384 ;!
+ hc_EVP_w32crypto_sha512 ;!
+ hc_EVP_w32crypto_des_ede3_cbc ;!
+ hc_EVP_w32crypto_aes_128_cbc ;!
+ hc_EVP_w32crypto_aes_192_cbc ;!
+ hc_EVP_w32crypto_aes_256_cbc ;!
+ hc_EVP_w32crypto_rc2_40_cbc ;!
+ hc_EVP_w32crypto_rc2_cbc ;!
+ hc_EVP_w32crypto_rc4 ;!
+ hc_EVP_w32crypto_rc4_40 ;!
+
+ hc_EVP_w32crypto_aes_128_cfb8 ;!
+ hc_EVP_w32crypto_aes_192_cfb8 ;!
+ hc_EVP_w32crypto_aes_256_cfb8 ;!
+
hc_EVP_hcrypto_md2
hc_EVP_hcrypto_md4
hc_EVP_hcrypto_md5
@@ -217,13 +276,11 @@ EXPORTS
hc_OpenSSL_add_all_algorithms_conf
hc_OpenSSL_add_all_algorithms_noconf
hc_PKCS12_key_gen
+ hc_PKCS5_PBKDF2_HMAC
hc_PKCS5_PBKDF2_HMAC_SHA1
hc_RAND_add
hc_RAND_bytes
hc_RAND_cleanup
-;! hc_RAND_egd
-;! hc_RAND_egd_bytes
-;! hc_RAND_egd_method
hc_RAND_file_name
;! hc_RAND_fortuna_method
hc_RAND_get_rand_method
@@ -284,6 +341,7 @@ EXPORTS
hc_EVP_CIPHER_CTX_ctrl
hc_EVP_CIPHER_CTX_rand_key
hc_EVP_CIPHER_CTX_set_key_length
+ hc_EVP_hcrypto_rc2_cbc
hc_EVP_hcrypto_rc2_40_cbc
hc_EVP_hcrypto_camellia_128_cbc
hc_EVP_CipherUpdate
diff --git a/lib/hcrypto/libtommath/bn_error.c b/lib/hcrypto/libtommath/bn_error.c
index b1b7177e61f0..fbba7aa1fb14 100644
--- a/lib/hcrypto/libtommath/bn_error.c
+++ b/lib/hcrypto/libtommath/bn_error.c
@@ -17,7 +17,7 @@
static const struct {
int code;
- char *msg;
+ const char *msg;
} msgs[] = {
{ MP_OKAY, "Successful" },
{ MP_MEM, "Out of heap" },
@@ -25,7 +25,7 @@ static const struct {
};
/* return a char * string for a given code */
-char *mp_error_to_string(int code)
+const char *mp_error_to_string(int code)
{
int x;
diff --git a/lib/hcrypto/libtommath/bn_mp_find_prime.c b/lib/hcrypto/libtommath/bn_mp_find_prime.c
index ef7b6532c5cd..29ef8747d623 100644
--- a/lib/hcrypto/libtommath/bn_mp_find_prime.c
+++ b/lib/hcrypto/libtommath/bn_mp_find_prime.c
@@ -5,17 +5,26 @@
* Love Hornquist Astrand <lha@h5l.org>
*/
#include <tommath.h>
-
-int mp_find_prime(mp_int *a)
+#ifdef BN_MP_FIND_PRIME_C
+int mp_find_prime(mp_int *a, int t)
{
- int res;
+ int res = MP_NO;
+
+ /* valid value of t? */
+ if (t <= 0 || t > PRIME_SIZE) {
+ return MP_VAL;
+ }
if (mp_iseven(a))
mp_add_d(a, 1, a);
do {
+ if (mp_prime_is_prime(a, t, &res) != 0) {
+ res = MP_VAL;
+ break;
+ }
- if ((res = mp_isprime(a)) == MP_NO) {
+ if (res == MP_NO) {
mp_add_d(a, 2, a);
continue;
}
@@ -24,3 +33,4 @@ int mp_find_prime(mp_int *a)
return res;
}
+#endif
diff --git a/lib/hcrypto/libtommath/bn_mp_isprime.c b/lib/hcrypto/libtommath/bn_mp_isprime.c
deleted file mode 100644
index d3678d5dc11a..000000000000
--- a/lib/hcrypto/libtommath/bn_mp_isprime.c
+++ /dev/null
@@ -1,75 +0,0 @@
-/* TomsFastMath, a fast ISO C bignum library.
- *
- * This project is meant to fill in where LibTomMath
- * falls short. That is speed ;-)
- *
- * This project is public domain and free for all purposes.
- *
- * Tom St Denis, tomstdenis@gmail.com
- */
-#include <tommath.h>
-
-/* a few primes */
-static const mp_digit primes[256] = {
- 0x0002, 0x0003, 0x0005, 0x0007, 0x000B, 0x000D, 0x0011, 0x0013,
- 0x0017, 0x001D, 0x001F, 0x0025, 0x0029, 0x002B, 0x002F, 0x0035,
- 0x003B, 0x003D, 0x0043, 0x0047, 0x0049, 0x004F, 0x0053, 0x0059,
- 0x0061, 0x0065, 0x0067, 0x006B, 0x006D, 0x0071, 0x007F, 0x0083,
- 0x0089, 0x008B, 0x0095, 0x0097, 0x009D, 0x00A3, 0x00A7, 0x00AD,
- 0x00B3, 0x00B5, 0x00BF, 0x00C1, 0x00C5, 0x00C7, 0x00D3, 0x00DF,
- 0x00E3, 0x00E5, 0x00E9, 0x00EF, 0x00F1, 0x00FB, 0x0101, 0x0107,
- 0x010D, 0x010F, 0x0115, 0x0119, 0x011B, 0x0125, 0x0133, 0x0137,
-
- 0x0139, 0x013D, 0x014B, 0x0151, 0x015B, 0x015D, 0x0161, 0x0167,
- 0x016F, 0x0175, 0x017B, 0x017F, 0x0185, 0x018D, 0x0191, 0x0199,
- 0x01A3, 0x01A5, 0x01AF, 0x01B1, 0x01B7, 0x01BB, 0x01C1, 0x01C9,
- 0x01CD, 0x01CF, 0x01D3, 0x01DF, 0x01E7, 0x01EB, 0x01F3, 0x01F7,
- 0x01FD, 0x0209, 0x020B, 0x021D, 0x0223, 0x022D, 0x0233, 0x0239,
- 0x023B, 0x0241, 0x024B, 0x0251, 0x0257, 0x0259, 0x025F, 0x0265,
- 0x0269, 0x026B, 0x0277, 0x0281, 0x0283, 0x0287, 0x028D, 0x0293,
- 0x0295, 0x02A1, 0x02A5, 0x02AB, 0x02B3, 0x02BD, 0x02C5, 0x02CF,
-
- 0x02D7, 0x02DD, 0x02E3, 0x02E7, 0x02EF, 0x02F5, 0x02F9, 0x0301,
- 0x0305, 0x0313, 0x031D, 0x0329, 0x032B, 0x0335, 0x0337, 0x033B,
- 0x033D, 0x0347, 0x0355, 0x0359, 0x035B, 0x035F, 0x036D, 0x0371,
- 0x0373, 0x0377, 0x038B, 0x038F, 0x0397, 0x03A1, 0x03A9, 0x03AD,
- 0x03B3, 0x03B9, 0x03C7, 0x03CB, 0x03D1, 0x03D7, 0x03DF, 0x03E5,
- 0x03F1, 0x03F5, 0x03FB, 0x03FD, 0x0407, 0x0409, 0x040F, 0x0419,
- 0x041B, 0x0425, 0x0427, 0x042D, 0x043F, 0x0443, 0x0445, 0x0449,
- 0x044F, 0x0455, 0x045D, 0x0463, 0x0469, 0x047F, 0x0481, 0x048B,
-
- 0x0493, 0x049D, 0x04A3, 0x04A9, 0x04B1, 0x04BD, 0x04C1, 0x04C7,
- 0x04CD, 0x04CF, 0x04D5, 0x04E1, 0x04EB, 0x04FD, 0x04FF, 0x0503,
- 0x0509, 0x050B, 0x0511, 0x0515, 0x0517, 0x051B, 0x0527, 0x0529,
- 0x052F, 0x0551, 0x0557, 0x055D, 0x0565, 0x0577, 0x0581, 0x058F,
- 0x0593, 0x0595, 0x0599, 0x059F, 0x05A7, 0x05AB, 0x05AD, 0x05B3,
- 0x05BF, 0x05C9, 0x05CB, 0x05CF, 0x05D1, 0x05D5, 0x05DB, 0x05E7,
- 0x05F3, 0x05FB, 0x0607, 0x060D, 0x0611, 0x0617, 0x061F, 0x0623,
- 0x062B, 0x062F, 0x063D, 0x0641, 0x0647, 0x0649, 0x064D, 0x0653
-};
-
-int mp_isprime(mp_int *a)
-{
- mp_int b;
- mp_digit d;
- int r, res;
-
- /* do trial division */
- for (r = 0; r < 256; r++) {
- mp_mod_d(a, primes[r], &d);
- if (d == 0) {
- return MP_NO;
- }
- }
-
- /* now do 8 miller rabins */
- mp_init(&b);
- for (r = 0; r < 128; r++) {
- mp_set(&b, primes[r]);
- mp_prime_miller_rabin(a, &b, &res);
- if (res == MP_NO) {
- return MP_NO;
- }
- }
- return MP_YES;
-}
diff --git a/lib/hcrypto/libtommath/bn_mp_mod.c b/lib/hcrypto/libtommath/bn_mp_mod.c
index f5cf8d09f2dc..757335aebc5b 100644
--- a/lib/hcrypto/libtommath/bn_mp_mod.c
+++ b/lib/hcrypto/libtommath/bn_mp_mod.c
@@ -15,7 +15,7 @@
* Tom St Denis, tomstdenis@gmail.com, http://libtom.org
*/
-/* c = a mod b, 0 <= c < b */
+/* c = a mod b, 0 <= c < b if b > 0, b < c <= 0 if b < 0 */
int
mp_mod (mp_int * a, mp_int * b, mp_int * c)
{
@@ -31,11 +31,11 @@ mp_mod (mp_int * a, mp_int * b, mp_int * c)
return res;
}
- if (t.sign != b->sign) {
- res = mp_add (b, &t, c);
- } else {
+ if (mp_iszero(&t) || t.sign == b->sign) {
res = MP_OKAY;
mp_exch (&t, c);
+ } else {
+ res = mp_add (b, &t, c);
}
mp_clear (&t);
diff --git a/lib/hcrypto/libtommath/bn_mp_prime_next_prime.c b/lib/hcrypto/libtommath/bn_mp_prime_next_prime.c
index db2f46ddbf9b..8cd3ec2a5270 100644
--- a/lib/hcrypto/libtommath/bn_mp_prime_next_prime.c
+++ b/lib/hcrypto/libtommath/bn_mp_prime_next_prime.c
@@ -142,7 +142,7 @@ int mp_prime_next_prime(mp_int *a, int t, int bbs_style)
}
/* is this prime? */
- for (x = 0; x < t && x < PRIME_SIZE; x++) {
+ for (x = 0; x < t; x++) {
mp_set(&b, ltm_prime_tab[x]);
if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
goto LBL_ERR;
diff --git a/lib/hcrypto/libtommath/bn_mp_rand.c b/lib/hcrypto/libtommath/bn_mp_rand.c
index af66a67eea14..14f5b7503bc7 100644
--- a/lib/hcrypto/libtommath/bn_mp_rand.c
+++ b/lib/hcrypto/libtommath/bn_mp_rand.c
@@ -29,7 +29,7 @@ mp_rand (mp_int * a, int digits)
/* first place a random non-zero digit */
do {
- d = ((mp_digit) abs (rand ())) & MP_MASK;
+ d = ((mp_digit) labs (rand ())) & MP_MASK;
} while (d == 0);
if ((res = mp_add_d (a, d, a)) != MP_OKAY) {
@@ -41,7 +41,7 @@ mp_rand (mp_int * a, int digits)
return res;
}
- if ((res = mp_add_d (a, ((mp_digit) abs (rand ())), a)) != MP_OKAY) {
+ if ((res = mp_add_d (a, ((mp_digit) labs (rand ())), a)) != MP_OKAY) {
return res;
}
}
diff --git a/lib/hcrypto/libtommath/bn_mp_shrink.c b/lib/hcrypto/libtommath/bn_mp_shrink.c
index 4b8c5ef11adb..c600efc58164 100644
--- a/lib/hcrypto/libtommath/bn_mp_shrink.c
+++ b/lib/hcrypto/libtommath/bn_mp_shrink.c
@@ -19,12 +19,17 @@
int mp_shrink (mp_int * a)
{
mp_digit *tmp;
- if (a->alloc != a->used && a->used > 0) {
- if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * a->used)) == NULL) {
+ int used = 1;
+
+ if(a->used > 0)
+ used = a->used;
+
+ if (a->alloc != used) {
+ if ((tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * used)) == NULL) {
return MP_MEM;
}
a->dp = tmp;
- a->alloc = a->used;
+ a->alloc = used;
}
return MP_OKAY;
}
diff --git a/lib/hcrypto/libtommath/tommath.h b/lib/hcrypto/libtommath/tommath.h
index 67d3b06af66d..e5d8d5367a82 100644
--- a/lib/hcrypto/libtommath/tommath.h
+++ b/lib/hcrypto/libtommath/tommath.h
@@ -46,7 +46,7 @@ extern "C" {
/* detect 64-bit mode if possible */
-#if defined(__x86_64__)
+#if defined(__x86_64__) && !defined(__ILP32__)
#if !(defined(MP_64BIT) && defined(MP_16BIT) && defined(MP_8BIT))
#define MP_64BIT
#endif
@@ -189,8 +189,8 @@ typedef int ltm_prime_callback(unsigned char *dst, int len, void *dat);
#define DIGIT(m,k) ((m)->dp[(k)])
#define SIGN(m) ((m)->sign)
-/* error code to char* string */
-char *mp_error_to_string(int code);
+/* error code to const char* string */
+const char *mp_error_to_string(int code);
/* ---> init and deinit bignum functions <--- */
/* init a bignum */
@@ -520,9 +520,7 @@ int mp_prime_next_prime(mp_int *a, int t, int bbs_style);
*/
int mp_prime_random_ex(mp_int *a, int t, int size, int flags, ltm_prime_callback cb, void *dat);
-int mp_find_prime(mp_int *a);
-
-int mp_isprime(mp_int *a);
+int mp_find_prime(mp_int *a, int t);
/* ---> radix conversion <--- */
int mp_count_bits(mp_int *a);
diff --git a/lib/hcrypto/libtommath/tommath_class.h b/lib/hcrypto/libtommath/tommath_class.h
index fa95a0277a1e..8fd0f52f49bb 100644
--- a/lib/hcrypto/libtommath/tommath_class.h
+++ b/lib/hcrypto/libtommath/tommath_class.h
@@ -42,6 +42,7 @@
#define BN_MP_EXPTMOD_C
#define BN_MP_EXPTMOD_FAST_C
#define BN_MP_EXTEUCLID_C
+#define BN_MP_FIND_PRIME_C
#define BN_MP_FREAD_C
#define BN_MP_FWRITE_C
#define BN_MP_GCD_C
@@ -55,6 +56,7 @@
#define BN_MP_INIT_SIZE_C
#define BN_MP_INVMOD_C
#define BN_MP_INVMOD_SLOW_C
+#define BN_MP_ISPRIME_C
#define BN_MP_IS_SQUARE_C
#define BN_MP_JACOBI_C
#define BN_MP_KARATSUBA_MUL_C
diff --git a/lib/hcrypto/md2.c b/lib/hcrypto/md2.c
index 26254acee5a8..25341a93b965 100644
--- a/lib/hcrypto/md2.c
+++ b/lib/hcrypto/md2.c
@@ -31,7 +31,8 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#include "hash.h"
#include "md2.h"
@@ -57,10 +58,11 @@ static const unsigned char subst[256] = {
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
};
-void
+int
MD2_Init (struct md2 *m)
{
memset(m, 0, sizeof(*m));
+ return 1;
}
static void
@@ -91,7 +93,7 @@ calc(struct md2 *m, const void *v)
memset(x, 0, sizeof(x));
}
-void
+int
MD2_Update (struct md2 *m, const void *v, size_t len)
{
size_t idx = m->len & 0xf;
@@ -114,9 +116,10 @@ MD2_Update (struct md2 *m, const void *v, size_t len)
}
memcpy(m->data + idx, p, len);
+ return 1;
}
-void
+int
MD2_Final (void *res, struct md2 *m)
{
unsigned char pad[16];
@@ -130,5 +133,6 @@ MD2_Final (void *res, struct md2 *m)
MD2_Update(m, pad, 16);
memcpy(res, m->state, MD2_DIGEST_LENGTH);
- memset(m, 0, sizeof(m));
+ memset(m, 0, sizeof(*m));
+ return 1;
}
diff --git a/lib/hcrypto/md2.h b/lib/hcrypto/md2.h
index af765060aa53..d82334eac948 100644
--- a/lib/hcrypto/md2.h
+++ b/lib/hcrypto/md2.h
@@ -56,8 +56,8 @@ struct md2 {
typedef struct md2 MD2_CTX;
-void MD2_Init (struct md2 *m);
-void MD2_Update (struct md2 *m, const void *p, size_t len);
-void MD2_Final (void *res, struct md2 *m);
+int MD2_Init (struct md2 *m);
+int MD2_Update (struct md2 *m, const void *p, size_t len);
+int MD2_Final (void *res, struct md2 *m);
#endif /* HEIM_MD2_H */
diff --git a/lib/hcrypto/md4.c b/lib/hcrypto/md4.c
index 1a9f77fed250..2a803ce0d360 100644
--- a/lib/hcrypto/md4.c
+++ b/lib/hcrypto/md4.c
@@ -31,7 +31,8 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#include "hash.h"
#include "md4.h"
@@ -42,7 +43,7 @@
#define D m->counter[3]
#define X data
-void
+int
MD4_Init (struct md4 *m)
{
m->sz[0] = 0;
@@ -51,6 +52,7 @@ MD4_Init (struct md4 *m)
C = 0x98badcfe;
B = 0xefcdab89;
A = 0x67452301;
+ return 1;
}
#define F(x,y,z) CRAYFIX((x & y) | (~x & z))
@@ -170,7 +172,7 @@ struct x32{
unsigned int b:32;
};
-void
+int
MD4_Update (struct md4 *m, const void *v, size_t len)
{
const unsigned char *p = v;
@@ -203,9 +205,10 @@ MD4_Update (struct md4 *m, const void *v, size_t len)
offset = 0;
}
}
+ return 1;
}
-void
+int
MD4_Final (void *res, struct md4 *m)
{
unsigned char zeros[72];
@@ -243,4 +246,5 @@ MD4_Final (void *res, struct md4 *m)
r[i] = swap_uint32_t (m->counter[i]);
}
#endif
+ return 1;
}
diff --git a/lib/hcrypto/md4.h b/lib/hcrypto/md4.h
index ce17d0f0885b..06575c5c34a7 100644
--- a/lib/hcrypto/md4.h
+++ b/lib/hcrypto/md4.h
@@ -55,8 +55,8 @@ struct md4 {
typedef struct md4 MD4_CTX;
-void MD4_Init (struct md4 *m);
-void MD4_Update (struct md4 *m, const void *p, size_t len);
-void MD4_Final (void *res, struct md4 *m);
+int MD4_Init (struct md4 *m);
+int MD4_Update (struct md4 *m, const void *p, size_t len);
+int MD4_Final (void *res, struct md4 *m);
#endif /* HEIM_MD4_H */
diff --git a/lib/hcrypto/md5.c b/lib/hcrypto/md5.c
index b35c76e2937e..ca5d160417a9 100644
--- a/lib/hcrypto/md5.c
+++ b/lib/hcrypto/md5.c
@@ -31,7 +31,8 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#include "hash.h"
#include "md5.h"
@@ -42,7 +43,7 @@
#define D m->counter[3]
#define X data
-void
+int
MD5_Init (struct md5 *m)
{
m->sz[0] = 0;
@@ -51,6 +52,7 @@ MD5_Init (struct md5 *m)
C = 0x98badcfe;
B = 0xefcdab89;
A = 0x67452301;
+ return 1;
}
#define F(x,y,z) CRAYFIX((x & y) | (~x & z))
@@ -194,7 +196,7 @@ struct x32{
unsigned int b:32;
};
-void
+int
MD5_Update (struct md5 *m, const void *v, size_t len)
{
const unsigned char *p = v;
@@ -214,22 +216,23 @@ MD5_Update (struct md5 *m, const void *v, size_t len)
if(offset == 64){
#if defined(WORDS_BIGENDIAN)
int i;
- uint32_t current[16];
+ uint32_t swapped[16];
struct x32 *us = (struct x32*)m->save;
for(i = 0; i < 8; i++){
- current[2*i+0] = swap_uint32_t(us[i].a);
- current[2*i+1] = swap_uint32_t(us[i].b);
+ swapped[2*i+0] = swap_uint32_t(us[i].a);
+ swapped[2*i+1] = swap_uint32_t(us[i].b);
}
- calc(m, current);
+ calc(m, swapped);
#else
calc(m, (uint32_t*)m->save);
#endif
offset = 0;
}
}
+ return 1;
}
-void
+int
MD5_Final (void *res, struct md5 *m)
{
unsigned char zeros[72];
@@ -267,4 +270,5 @@ MD5_Final (void *res, struct md5 *m)
r[i] = swap_uint32_t (m->counter[i]);
}
#endif
+ return 1;
}
diff --git a/lib/hcrypto/md5.h b/lib/hcrypto/md5.h
index b2df6e56fcd2..ab1ac3540bc3 100644
--- a/lib/hcrypto/md5.h
+++ b/lib/hcrypto/md5.h
@@ -55,8 +55,8 @@ struct md5 {
typedef struct md5 MD5_CTX;
-void MD5_Init (struct md5 *m);
-void MD5_Update (struct md5 *m, const void *p, size_t len);
-void MD5_Final (void *res, struct md5 *m); /* uint32_t res[4] */
+int MD5_Init (struct md5 *m);
+int MD5_Update (struct md5 *m, const void *p, size_t len);
+int MD5_Final (void *res, struct md5 *m); /* uint32_t res[4] */
#endif /* HEIM_MD5_H */
diff --git a/lib/hcrypto/md5crypt_test.c b/lib/hcrypto/md5crypt_test.c
index a0c2df411b65..f57c3ed14b31 100644
--- a/lib/hcrypto/md5crypt_test.c
+++ b/lib/hcrypto/md5crypt_test.c
@@ -32,9 +32,7 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <string.h>
+#include <roken.h>
#include <des.h>
struct test {
diff --git a/lib/hcrypto/mdtest.c b/lib/hcrypto/mdtest.c
index 3e06bf9c12fd..d301c5c4a422 100644
--- a/lib/hcrypto/mdtest.c
+++ b/lib/hcrypto/mdtest.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995 - 2002 Kungliga Tekniska Högskolan
+ * Copyright (c) 1995 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -32,14 +32,10 @@
*/
#include <config.h>
+#include <roken.h>
#define HC_DEPRECATED_CRYPTO
-#include <stdio.h>
-#ifdef HAVE_STDLIB_H
-#include <stdlib.h>
-#endif
-#include <string.h>
#ifdef KRB5
#include <krb5-types.h>
#endif
@@ -140,7 +136,7 @@ struct test md2_tests[] = {
"\xda\x33\xde\xf2\xa4\x2d\xf1\x39\x75\x35\x28\x46\xc3\x03\x38\xcd" },
{"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
"\xd5\x97\x6f\x79\xd8\x3d\x3a\x0d\xc9\x80\x6c\x3c\x66\xf3\xef\xd8" },
- {NULL }
+ {NULL, { 0 } }
};
struct test md4_tests[] = {
@@ -184,7 +180,7 @@ struct test sha1_tests[] = {
{0x34, 0xaa, 0x97, 0x3c, 0xd4, 0xc4, 0xda, 0xa4,
0xf6, 0x1e, 0xeb, 0x2b, 0xdb, 0xad, 0x27, 0x31,
0x65, 0x34, 0x01, 0x6f}},
- { NULL }
+ { NULL, { 0 } }
};
struct test sha256_tests[] = {
@@ -203,7 +199,7 @@ struct test sha256_tests[] = {
0x81,0xa1,0xc7,0xe2, 0x84,0xd7,0x3e,0x67,
0xf1,0x80,0x9a,0x48, 0xa4,0x97,0x20,0x0e,
0x04,0x6d,0x39,0xcc, 0xc7,0x11,0x2c,0xd0 }},
- { NULL }
+ { NULL, { 0 } }
};
struct test sha384_tests[] = {
@@ -229,7 +225,7 @@ struct test sha384_tests[] = {
0x79,0x72,0xce,0xc5,0x70,0x4c,0x2a,0x5b,
0x07,0xb8,0xb3,0xdc,0x38,0xec,0xc4,0xeb,
0xae,0x97,0xdd,0xd8,0x7f,0x3d,0x89,0x85}},
- {NULL}
+ {NULL, { 0 }}
};
struct test sha512_tests[] = {
@@ -261,7 +257,7 @@ struct test sha512_tests[] = {
0x4c,0xb0,0x43,0x2c,0xe5,0x77,0xc3,0x1b,
0xeb,0x00,0x9c,0x5c,0x2c,0x49,0xaa,0x2e,
0x4e,0xad,0xb2,0x17,0xad,0x8c,0xc0,0x9b }},
- { NULL }
+ { NULL, { 0 } }
};
static int
@@ -278,6 +274,10 @@ hash_test (struct hash_foo *hash, struct test *tests)
char buf[1000];
ectx = EVP_MD_CTX_create();
+ if (hash->evp() == NULL) {
+ printf("unavailable\n");
+ continue;
+ }
EVP_DigestInit_ex(ectx, hash->evp(), NULL);
(*hash->init)(ctx);
diff --git a/lib/hcrypto/passwd_dlg.c b/lib/hcrypto/passwd_dlg.c
index c16f28327056..037fa7436a43 100644
--- a/lib/hcrypto/passwd_dlg.c
+++ b/lib/hcrypto/passwd_dlg.c
@@ -37,9 +37,9 @@
*/
#include <config.h>
+#include <roken.h>
#ifdef WIN32 /* Visual C++ 4.0 (Windows95/NT) */
-#include <Windows.h>
#include "passwd_dlg.h"
#include "Resource.h"
#define passwdBufSZ 64
diff --git a/lib/hcrypto/pkcs12.c b/lib/hcrypto/pkcs12.c
index ff0f77644ea8..5f0791feee3f 100644
--- a/lib/hcrypto/pkcs12.c
+++ b/lib/hcrypto/pkcs12.c
@@ -32,16 +32,12 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <roken.h>
#include <assert.h>
#include <pkcs12.h>
#include <bn.h>
-#include <roken.h>
-
int
PKCS12_key_gen(const void *key, size_t keylen,
const void *salt, size_t saltlen,
diff --git a/lib/hcrypto/pkcs5.c b/lib/hcrypto/pkcs5.c
index 18045e236f2c..dff3ccea3986 100644
--- a/lib/hcrypto/pkcs5.c
+++ b/lib/hcrypto/pkcs5.c
@@ -32,19 +32,15 @@
*/
#include <config.h>
+#include <roken.h>
#ifdef KRB5
#include <krb5-types.h>
#endif
-#include <stdio.h>
-#include <stdlib.h>
-
#include <evp.h>
#include <hmac.h>
-#include <roken.h>
-
/**
* As descriped in PKCS5, convert a password, salt, and iteration counter into a crypto key.
*
@@ -53,6 +49,7 @@
* @param salt Salt
* @param salt_len Length of salt.
* @param iter iteration counter.
+ * @param md the digest function.
* @param keylen the output key length.
* @param key the output key.
*
@@ -62,21 +59,23 @@
*/
int
-PKCS5_PBKDF2_HMAC_SHA1(const void * password, size_t password_len,
- const void * salt, size_t salt_len,
- unsigned long iter,
- size_t keylen, void *key)
+PKCS5_PBKDF2_HMAC(const void * password, size_t password_len,
+ const void * salt, size_t salt_len,
+ unsigned long iter,
+ const EVP_MD *md,
+ size_t keylen, void *key)
{
size_t datalen, leftofkey, checksumsize;
char *data, *tmpcksum;
uint32_t keypart;
- const EVP_MD *md;
unsigned long i;
int j;
char *p;
unsigned int hmacsize;
- md = EVP_sha1();
+ if (md == NULL)
+ return 0;
+
checksumsize = EVP_MD_size(md);
datalen = salt_len + 4;
@@ -126,3 +125,28 @@ PKCS5_PBKDF2_HMAC_SHA1(const void * password, size_t password_len,
return 1;
}
+
+/**
+ * As descriped in PKCS5, convert a password, salt, and iteration counter into a crypto key.
+ *
+ * @param password Password.
+ * @param password_len Length of password.
+ * @param salt Salt
+ * @param salt_len Length of salt.
+ * @param iter iteration counter.
+ * @param keylen the output key length.
+ * @param key the output key.
+ *
+ * @return 1 on success, non 1 on failure.
+ *
+ * @ingroup hcrypto_misc
+ */
+int
+PKCS5_PBKDF2_HMAC_SHA1(const void * password, size_t password_len,
+ const void * salt, size_t salt_len,
+ unsigned long iter,
+ size_t keylen, void *key)
+{
+ return PKCS5_PBKDF2_HMAC(password, password_len, salt, salt_len, iter,
+ EVP_sha1(), keylen, key);
+}
diff --git a/lib/hcrypto/rand-egd.c b/lib/hcrypto/rand-egd.c
deleted file mode 100644
index dd2d3e13ecd9..000000000000
--- a/lib/hcrypto/rand-egd.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (c) 2007 Kungliga Tekniska Högskolan
- * (Royal Institute of Technology, Stockholm, Sweden).
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * 3. Neither the name of the Institute nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <config.h>
-
-#include <sys/types.h>
-#ifdef HAVE_SYS_UN_H
-#include <sys/un.h>
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#include <assert.h>
-
-#include <rand.h>
-#include <randi.h>
-
-#include <roken.h>
-
-static const char *egd_path = "/var/run/egd-pool";
-
-#define MAX_EGD_DATA 255
-
-static int
-connect_egd(const char *path)
-{
- struct sockaddr_un addr;
- int fd;
-
- memset(&addr, 0, sizeof(addr));
-
- if (strlen(path) > sizeof(addr.sun_path))
- return -1;
-
- addr.sun_family = AF_UNIX;
- strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
-
- fd = socket(AF_UNIX, SOCK_STREAM, 0);
- if (fd < 0)
- return -1;
-
- rk_cloexec(fd);
-
- if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
- close(fd);
- return -1;
- }
-
- return fd;
-}
-
-static int
-get_entropy(int fd, void *data, size_t len)
-{
- unsigned char msg[2];
-
- assert(len <= MAX_EGD_DATA);
-
- msg[0] = 0x02; /* read blocking data */
- msg[1] = len; /* wanted length */
-
- if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
- return 0;
-
- if (net_read(fd, data, len) != len)
- return 0;
-
- return 1;
-}
-
-static int
-put_entropy(int fd, const void *data, size_t len)
-{
- unsigned char msg[4];
-
- assert (len <= MAX_EGD_DATA);
-
- msg[0] = 0x03; /* write data */
- msg[1] = 0; /* dummy */
- msg[2] = 0; /* entropy */
- msg[3] = len; /* length */
-
- if (net_write(fd, msg, sizeof(msg)) != sizeof(msg))
- return 0;
- if (net_write(fd, data, len) != len)
- return 0;
-
- return 1;
-}
-
-/*
- *
- */
-
-static void
-egd_seed(const void *indata, int size)
-{
- size_t len;
- int fd, ret = 1;
-
- fd = connect_egd(egd_path);
- if (fd < 0)
- return;
-
- while(size) {
- len = size;
- if (len > MAX_EGD_DATA)
- len = MAX_EGD_DATA;
- ret = put_entropy(fd, indata, len);
- if (ret != 1)
- break;
- indata = ((unsigned char *)indata) + len;
- size -= len;
- }
- close(fd);
-}
-
-static int
-get_bytes(const char *path, unsigned char *outdata, int size)
-{
- size_t len;
- int fd, ret = 1;
-
- if (path == NULL)
- path = egd_path;
-
- fd = connect_egd(path);
- if (fd < 0)
- return 0;
-
- while(size) {
- len = size;
- if (len > MAX_EGD_DATA)
- len = MAX_EGD_DATA;
- ret = get_entropy(fd, outdata, len);
- if (ret != 1)
- break;
- outdata += len;
- size -= len;
- }
- close(fd);
-
- return ret;
-}
-
-static int
-egd_bytes(unsigned char *outdata, int size)
-{
- return get_bytes(NULL, outdata, size);
-}
-
-static void
-egd_cleanup(void)
-{
-}
-
-static void
-egd_add(const void *indata, int size, double entropi)
-{
- egd_seed(indata, size);
-}
-
-static int
-egd_pseudorand(unsigned char *outdata, int size)
-{
- return get_bytes(NULL, outdata, size);
-}
-
-static int
-egd_status(void)
-{
- int fd;
- fd = connect_egd(egd_path);
- if (fd < 0)
- return 0;
- close(fd);
- return 1;
-}
-
-const RAND_METHOD hc_rand_egd_method = {
- egd_seed,
- egd_bytes,
- egd_cleanup,
- egd_add,
- egd_pseudorand,
- egd_status
-};
-
-const RAND_METHOD *
-RAND_egd_method(void)
-{
- return &hc_rand_egd_method;
-}
-
-
-int
-RAND_egd(const char *filename)
-{
- return RAND_egd_bytes(filename, 128);
-}
-
-int
-RAND_egd_bytes(const char *filename, int size)
-{
- void *data;
- int ret;
-
- if (size <= 0)
- return 0;
-
- data = malloc(size);
- if (data == NULL)
- return 0;
-
- ret = get_bytes(filename, data, size);
- if (ret != 1) {
- free(data);
- return ret;
- }
-
- RAND_seed(data, size);
-
- memset(data, 0, size);
- free(data);
-
- return 1;
-}
diff --git a/lib/hcrypto/rand-fortuna.c b/lib/hcrypto/rand-fortuna.c
index 11027b46cf4a..529bf8f301a8 100644
--- a/lib/hcrypto/rand-fortuna.c
+++ b/lib/hcrypto/rand-fortuna.c
@@ -30,16 +30,13 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <roken.h>
#include <rand.h>
#include <heim_threads.h>
#ifdef KRB5
#include <krb5-types.h>
#endif
-#include <roken.h>
#include "randi.h"
#include "aes.h"
@@ -486,20 +483,6 @@ fortuna_reseed(void)
entropy_p = 1;
}
#endif
-#ifndef NO_RAND_EGD_METHOD
- /*
- * Only to get egd entropy if /dev/random or arc4rand failed since
- * it can be horribly slow to generate new bits.
- */
- if (!entropy_p) {
- unsigned char buf[INIT_BYTES];
- if ((*hc_rand_egd_method.bytes)(buf, sizeof(buf)) == 1) {
- add_entropy(&main_state, buf, sizeof(buf));
- entropy_p = 1;
- memset(buf, 0, sizeof(buf));
- }
- }
-#endif
/*
* Fall back to gattering data from timer and secret files, this
* is really the last resort.
@@ -639,6 +622,16 @@ fortuna_status(void)
return result ? 1 : 0;
}
+#if defined(__GNUC__) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
+const RAND_METHOD hc_rand_fortuna_method = {
+ .seed = fortuna_seed,
+ .bytes = fortuna_bytes,
+ .cleanup = fortuna_cleanup,
+ .add = fortuna_add,
+ .pseudorand = fortuna_pseudorand,
+ .status = fortuna_status
+};
+#else
const RAND_METHOD hc_rand_fortuna_method = {
fortuna_seed,
fortuna_bytes,
@@ -647,6 +640,7 @@ const RAND_METHOD hc_rand_fortuna_method = {
fortuna_pseudorand,
fortuna_status
};
+#endif
const RAND_METHOD *
RAND_fortuna_method(void)
diff --git a/lib/hcrypto/rand-timer.c b/lib/hcrypto/rand-timer.c
index 994c3210e941..935a3a7baa2c 100644
--- a/lib/hcrypto/rand-timer.c
+++ b/lib/hcrypto/rand-timer.c
@@ -32,13 +32,10 @@
*/
#include <config.h>
+#include <roken.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <rand.h>
-#include <roken.h>
-
#include "randi.h"
#ifndef WIN32 /* don't bother with this on windows */
@@ -186,6 +183,16 @@ timer_status(void)
#endif
}
+#if defined(__GNUC__) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
+const RAND_METHOD hc_rand_timer_method = {
+ .seed = timer_seed,
+ .bytes = timer_bytes,
+ .cleanup = timer_cleanup,
+ .add = timer_add,
+ .pseudorand = timer_pseudorand,
+ .status = timer_status
+};
+#else
const RAND_METHOD hc_rand_timer_method = {
timer_seed,
timer_bytes,
@@ -194,6 +201,7 @@ const RAND_METHOD hc_rand_timer_method = {
timer_pseudorand,
timer_status
};
+#endif
const RAND_METHOD *
RAND_timer_method(void)
diff --git a/lib/hcrypto/rand-unix.c b/lib/hcrypto/rand-unix.c
index c52155baaa81..2d6013eb348e 100644
--- a/lib/hcrypto/rand-unix.c
+++ b/lib/hcrypto/rand-unix.c
@@ -32,14 +32,11 @@
*/
#include <config.h>
+#include <roken.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <rand.h>
#include <heim_threads.h>
-#include <roken.h>
-
#include "randi.h"
/*
@@ -71,20 +68,33 @@ _hc_unix_device_fd(int flags, const char **fn)
}
static void
-unix_seed(const void *indata, int size)
+unix_seed(const void *p, int size)
{
+ const unsigned char *indata = p;
+ ssize_t count;
int fd;
- if (size <= 0)
+ if (size < 0)
+ return;
+ else if (size == 0)
return;
- fd = _hc_unix_device_fd(O_WRONLY, NULL);
+ fd = _hc_unix_device_fd(O_RDONLY, NULL);
if (fd < 0)
return;
- write(fd, indata, size);
+ while (size > 0) {
+ count = write(fd, indata, size);
+ if (count < 0 && errno == EINTR)
+ continue;
+ else if (count <= 0) {
+ close(fd);
+ return;
+ }
+ indata += count;
+ size -= count;
+ }
close(fd);
-
}
diff --git a/lib/hcrypto/rand-w32.c b/lib/hcrypto/rand-w32.c
new file mode 100644
index 000000000000..f36903ae3b08
--- /dev/null
+++ b/lib/hcrypto/rand-w32.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <roken.h>
+
+#include <wincrypt.h>
+
+#include <rand.h>
+#include <heim_threads.h>
+
+#include "randi.h"
+
+volatile static HCRYPTPROV g_cryptprovider = NULL;
+
+static HCRYPTPROV
+_hc_CryptProvider(void)
+{
+ BOOL rv;
+ HCRYPTPROV cryptprovider = NULL;
+
+ if (g_cryptprovider != NULL)
+ goto out;
+
+ rv = CryptAcquireContext(&cryptprovider, NULL,
+ MS_ENHANCED_PROV, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT);
+
+ if (GetLastError() == NTE_BAD_KEYSET) {
+ rv = CryptAcquireContext(&cryptprovider, NULL,
+ MS_ENHANCED_PROV, PROV_RSA_FULL,
+ CRYPT_NEWKEYSET);
+ }
+
+ if (rv) {
+ /* try the default provider */
+ rv = CryptAcquireContext(&cryptprovider, NULL, 0, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT);
+
+ if (GetLastError() == NTE_BAD_KEYSET) {
+ rv = CryptAcquireContext(&cryptprovider, NULL,
+ MS_ENHANCED_PROV, PROV_RSA_FULL,
+ CRYPT_NEWKEYSET);
+ }
+ }
+
+ if (rv) {
+ /* try just a default random number generator */
+ rv = CryptAcquireContext(&cryptprovider, NULL, 0, PROV_RNG,
+ CRYPT_VERIFYCONTEXT);
+ }
+
+ if (rv == 0 &&
+ InterlockedCompareExchangePointer((PVOID *) &g_cryptprovider,
+ (PVOID) cryptprovider, NULL) != 0) {
+
+ CryptReleaseContext(cryptprovider, 0);
+ }
+
+out:
+ return g_cryptprovider;
+}
+
+/*
+ *
+ */
+
+
+static void
+w32crypto_seed(const void *indata, int size)
+{
+}
+
+
+static int
+w32crypto_bytes(unsigned char *outdata, int size)
+{
+ if (CryptGenRandom(_hc_CryptProvider(), size, outdata))
+ return 1;
+ return 0;
+}
+
+static void
+w32crypto_cleanup(void)
+{
+ HCRYPTPROV cryptprovider;
+
+ if (InterlockedCompareExchangePointer((PVOID *) &cryptprovider,
+ 0, (PVOID) g_cryptprovider) == 0) {
+ CryptReleaseContext(cryptprovider, 0);
+ }
+}
+
+static void
+w32crypto_add(const void *indata, int size, double entropi)
+{
+}
+
+static int
+w32crypto_status(void)
+{
+ if (_hc_CryptProvider() == 0)
+ return 0;
+ return 1;
+}
+
+const RAND_METHOD hc_rand_w32crypto_method = {
+ w32crypto_seed,
+ w32crypto_bytes,
+ w32crypto_cleanup,
+ w32crypto_add,
+ w32crypto_bytes,
+ w32crypto_status
+};
+
+const RAND_METHOD *
+RAND_w32crypto_method(void)
+{
+ return &hc_rand_w32crypto_method;
+}
diff --git a/lib/hcrypto/rand.c b/lib/hcrypto/rand.c
index d5c1f687b910..98432e75ed89 100644
--- a/lib/hcrypto/rand.c
+++ b/lib/hcrypto/rand.c
@@ -34,14 +34,11 @@
*/
#include <config.h>
+#include <roken.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <rand.h>
#include <randi.h>
-#include <roken.h>
-
#ifndef O_BINARY
#define O_BINARY 0
#endif
@@ -56,7 +53,7 @@
* See the library functions here: @ref hcrypto_rand
*/
-const static RAND_METHOD *selected_meth = NULL;
+static const RAND_METHOD *selected_meth = NULL;
static ENGINE *selected_engine = NULL;
static void
@@ -209,6 +206,8 @@ RAND_set_rand_method(const RAND_METHOD *meth)
/**
* Get the default random method.
*
+ * @return Returns a RAND_METHOD
+ *
* @ingroup hcrypto_rand
*/
@@ -265,6 +264,8 @@ RAND_set_rand_engine(ENGINE *engine)
* @param filename name of file to read.
* @param size minimum size to read.
*
+ * @return Returns the number of seed bytes loaded (0 indicates failure)
+ *
* @ingroup hcrypto_rand
*/
diff --git a/lib/hcrypto/rand.h b/lib/hcrypto/rand.h
index 590bd8cf1a43..7b5553803fc9 100644
--- a/lib/hcrypto/rand.h
+++ b/lib/hcrypto/rand.h
@@ -39,6 +39,8 @@
#ifndef _HEIM_RAND_H
#define _HEIM_RAND_H 1
+#define RAND_METHOD hc_RAND_METHOD
+
typedef struct RAND_METHOD RAND_METHOD;
#include <hcrypto/engine.h>
@@ -56,10 +58,7 @@ typedef struct RAND_METHOD RAND_METHOD;
#define RAND_load_file hc_RAND_load_file
#define RAND_write_file hc_RAND_write_file
#define RAND_status hc_RAND_status
-#define RAND_egd hc_RAND_egd
-#define RAND_egd_bytes hc_RAND_egd_bytes
#define RAND_fortuna_method hc_RAND_fortuna_method
-#define RAND_egd_method hc_RAND_egd_method
#define RAND_unix_method hc_RAND_unix_method
#define RAND_w32crypto_method hc_RAND_w32crypto_method
@@ -97,13 +96,10 @@ const char *
int RAND_load_file(const char *, size_t);
int RAND_write_file(const char *);
int RAND_status(void);
-int RAND_egd(const char *);
-int RAND_egd_bytes(const char *, int);
const RAND_METHOD * RAND_fortuna_method(void);
const RAND_METHOD * RAND_unix_method(void);
-const RAND_METHOD * RAND_egd_method(void);
const RAND_METHOD * RAND_w32crypto_method(void);
#endif /* _HEIM_RAND_H */
diff --git a/lib/hcrypto/randi.h b/lib/hcrypto/randi.h
index fe021a80ecdd..85d5d6605d20 100644
--- a/lib/hcrypto/randi.h
+++ b/lib/hcrypto/randi.h
@@ -40,7 +40,6 @@
extern const RAND_METHOD hc_rand_fortuna_method;
extern const RAND_METHOD hc_rand_unix_method;
-extern const RAND_METHOD hc_rand_egd_method;
extern const RAND_METHOD hc_rand_timer_method;
extern const RAND_METHOD hc_rand_w32crypto_method;
diff --git a/lib/hcrypto/rc2.c b/lib/hcrypto/rc2.c
index 63bd3daa00e6..02b684cbd4b0 100644
--- a/lib/hcrypto/rc2.c
+++ b/lib/hcrypto/rc2.c
@@ -32,11 +32,9 @@
*/
#include <config.h>
+#include <roken.h>
#include "rc2.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
/*
* Implemented from Peter Gutmann's "Specification for Ron Rivests Cipher No.2"
diff --git a/lib/hcrypto/rc2test.c b/lib/hcrypto/rc2test.c
index 01701adf7b5b..553bed33fccb 100644
--- a/lib/hcrypto/rc2test.c
+++ b/lib/hcrypto/rc2test.c
@@ -32,11 +32,9 @@
*/
#include <config.h>
+#include <roken.h>
#include <rc2.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
struct {
const void *key;
diff --git a/lib/hcrypto/rc4.c b/lib/hcrypto/rc4.c
index 988c42424efc..cc7882d298b4 100644
--- a/lib/hcrypto/rc4.c
+++ b/lib/hcrypto/rc4.c
@@ -33,7 +33,8 @@
/* implemented from description in draft-kaukonen-cipher-arcfour-03.txt */
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#include <rc4.h>
diff --git a/lib/hcrypto/rctest.c b/lib/hcrypto/rctest.c
index b952fcc9da54..031d8cd5788f 100644
--- a/lib/hcrypto/rctest.c
+++ b/lib/hcrypto/rctest.c
@@ -32,9 +32,7 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <string.h>
+#include <roken.h>
#include <rc4.h>
static unsigned char plain1[8] =
diff --git a/lib/hcrypto/rijndael-alg-fst.c b/lib/hcrypto/rijndael-alg-fst.c
index bf335afd9f32..be05cdd13eeb 100644
--- a/lib/hcrypto/rijndael-alg-fst.c
+++ b/lib/hcrypto/rijndael-alg-fst.c
@@ -28,10 +28,9 @@
/* "$NetBSD: rijndael-alg-fst.c,v 1.5 2001/11/13 01:40:10 lukem Exp $" */
-#include "config.h"
+#include <config.h>
+#include <roken.h>
-
-#include <stdlib.h>
#ifdef KRB5
#include <krb5-types.h>
#endif
diff --git a/lib/hcrypto/rnd_keys.c b/lib/hcrypto/rnd_keys.c
index 49c7634c381d..ec35021dccde 100644
--- a/lib/hcrypto/rnd_keys.c
+++ b/lib/hcrypto/rnd_keys.c
@@ -31,15 +31,14 @@
* SUCH DAMAGE.
*/
-#include "config.h"
-
+#include <config.h>
+#include <roken.h>
#define HC_DEPRECATED
#ifdef KRB5
#include <krb5-types.h>
#endif
-#include <stdlib.h>
#include <des.h>
#include <rand.h>
diff --git a/lib/hcrypto/rsa-gmp.c b/lib/hcrypto/rsa-gmp.c
index b3a994b80349..8fb28edff213 100644
--- a/lib/hcrypto/rsa-gmp.c
+++ b/lib/hcrypto/rsa-gmp.c
@@ -32,16 +32,12 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <roken.h>
#include <krb5-types.h>
#include <assert.h>
#include <rsa.h>
-#include <roken.h>
-
#ifdef HAVE_GMP
#include <gmp.h>
diff --git a/lib/hcrypto/rsa-ltm.c b/lib/hcrypto/rsa-ltm.c
index 5cd3e9361eb1..4ac5617cb6ba 100644
--- a/lib/hcrypto/rsa-ltm.c
+++ b/lib/hcrypto/rsa-ltm.c
@@ -32,16 +32,12 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <roken.h>
#include <krb5-types.h>
#include <assert.h>
#include <rsa.h>
-#include <roken.h>
-
#include "tommath.h"
static int
@@ -157,13 +153,18 @@ ltm_rsa_public_encrypt(int flen, const unsigned char* from,
size = RSA_size(rsa);
if (size < RSA_PKCS1_PADDING_SIZE || size - RSA_PKCS1_PADDING_SIZE < flen) {
- mp_clear_multi(&n, &e, &enc, &dec);
+ mp_clear_multi(&n, &e, &enc, &dec, NULL);
return -2;
}
BN2mpz(&n, rsa->n);
BN2mpz(&e, rsa->e);
+ if (mp_cmp_d(&e, 3) == MP_LT) {
+ mp_clear_multi(&e, &n, &enc, &dec, NULL);
+ return -2;
+ }
+
p = p0 = malloc(size - 1);
if (p0 == NULL) {
mp_clear_multi(&e, &n, &enc, &dec, NULL);
@@ -233,13 +234,10 @@ ltm_rsa_public_decrypt(int flen, const unsigned char* from,
BN2mpz(&n, rsa->n);
BN2mpz(&e, rsa->e);
-#if 0
- /* Check that the exponent is larger then 3 */
- if (mp_int_compare_value(&e, 3) <= 0) {
+ if (mp_cmp_d(&e, 3) == MP_LT) {
mp_clear_multi(&e, &n, &s, &us, NULL);
return -3;
}
-#endif
mp_read_unsigned_bin(&s, rk_UNCONST(from), flen);
@@ -287,7 +285,7 @@ static int
ltm_rsa_private_encrypt(int flen, const unsigned char* from,
unsigned char* to, RSA* rsa, int padding)
{
- unsigned char *p, *p0;
+ unsigned char *ptr, *ptr0;
int res;
int size;
mp_int in, out, n, e;
@@ -305,21 +303,26 @@ ltm_rsa_private_encrypt(int flen, const unsigned char* from,
if (size < RSA_PKCS1_PADDING_SIZE || size - RSA_PKCS1_PADDING_SIZE < flen)
return -2;
- p0 = p = malloc(size);
- *p++ = 0;
- *p++ = 1;
- memset(p, 0xff, size - flen - 3);
- p += size - flen - 3;
- *p++ = 0;
- memcpy(p, from, flen);
- p += flen;
- assert((p - p0) == size);
+ ptr0 = ptr = malloc(size);
+ *ptr++ = 0;
+ *ptr++ = 1;
+ memset(ptr, 0xff, size - flen - 3);
+ ptr += size - flen - 3;
+ *ptr++ = 0;
+ memcpy(ptr, from, flen);
+ ptr += flen;
+ assert((ptr - ptr0) == size);
BN2mpz(&n, rsa->n);
BN2mpz(&e, rsa->e);
- mp_read_unsigned_bin(&in, p0, size);
- free(p0);
+ if (mp_cmp_d(&e, 3) == MP_LT) {
+ size = -3;
+ goto out;
+ }
+
+ mp_read_unsigned_bin(&in, ptr0, size);
+ free(ptr0);
if(mp_isneg(&in) || mp_cmp(&in, &n) >= 0) {
size = -3;
@@ -402,6 +405,11 @@ ltm_rsa_private_decrypt(int flen, const unsigned char* from,
BN2mpz(&n, rsa->n);
BN2mpz(&e, rsa->e);
+ if (mp_cmp_d(&e, 3) == MP_LT) {
+ size = -2;
+ goto out;
+ }
+
mp_read_unsigned_bin(&in, rk_UNCONST(from), flen);
if(mp_isneg(&in) || mp_cmp(&in, &n) >= 0) {
@@ -528,7 +536,7 @@ ltm_rsa_generate_key(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
do {
BN_GENCB_call(cb, 2, counter++);
CHECK(random_num(&p, bitsp), 0);
- CHECK(mp_find_prime(&p), MP_YES);
+ CHECK(mp_find_prime(&p,128), MP_YES);
mp_sub_d(&p, 1, &t1);
mp_gcd(&t1, &el, &t2);
@@ -540,7 +548,7 @@ ltm_rsa_generate_key(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
do {
BN_GENCB_call(cb, 2, counter++);
CHECK(random_num(&q, bits - bitsp), 0);
- CHECK(mp_find_prime(&q), MP_YES);
+ CHECK(mp_find_prime(&q,128), MP_YES);
if (mp_cmp(&p, &q) == 0) /* don't let p and q be the same */
continue;
diff --git a/lib/hcrypto/rsa-tfm.c b/lib/hcrypto/rsa-tfm.c
new file mode 100644
index 000000000000..41b0fc18afca
--- /dev/null
+++ b/lib/hcrypto/rsa-tfm.c
@@ -0,0 +1,573 @@
+/*
+ * Copyright (c) 2006 - 2007, 2010 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <roken.h>
+#include <krb5-types.h>
+#include <assert.h>
+
+#include <rsa.h>
+
+#ifdef USE_HCRYPTO_TFM
+
+#include "tfm.h"
+
+static void
+BN2mpz(fp_int *s, const BIGNUM *bn)
+{
+ size_t len;
+ void *p;
+
+ fp_init(s);
+
+ len = BN_num_bytes(bn);
+ p = malloc(len);
+ BN_bn2bin(bn, p);
+ fp_read_unsigned_bin(s, p, len);
+ free(p);
+}
+
+static int
+tfm_rsa_private_calculate(fp_int * in, fp_int * p, fp_int * q,
+ fp_int * dmp1, fp_int * dmq1, fp_int * iqmp,
+ fp_int * out)
+{
+ fp_int vp, vq, u;
+
+ fp_init_multi(&vp, &vq, &u, NULL);
+
+ /* vq = c ^ (d mod (q - 1)) mod q */
+ /* vp = c ^ (d mod (p - 1)) mod p */
+ fp_mod(in, p, &u);
+ fp_exptmod(&u, dmp1, p, &vp);
+ fp_mod(in, q, &u);
+ fp_exptmod(&u, dmq1, q, &vq);
+
+ /* C2 = 1/q mod p (iqmp) */
+ /* u = (vp - vq)C2 mod p. */
+ fp_sub(&vp, &vq, &u);
+ if (fp_isneg(&u))
+ fp_add(&u, p, &u);
+ fp_mul(&u, iqmp, &u);
+ fp_mod(&u, p, &u);
+
+ /* c ^ d mod n = vq + u q */
+ fp_mul(&u, q, &u);
+ fp_add(&u, &vq, out);
+
+ fp_zero_multi(&vp, &vq, &u, NULL);
+
+ return 0;
+}
+
+/*
+ *
+ */
+
+static int
+tfm_rsa_public_encrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *p, *p0;
+ int res;
+ size_t size, padlen;
+ fp_int enc, dec, n, e;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ size = RSA_size(rsa);
+
+ if (size < RSA_PKCS1_PADDING_SIZE || size - RSA_PKCS1_PADDING_SIZE < flen)
+ return -2;
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+ p = p0 = malloc(size - 1);
+ if (p0 == NULL) {
+ fp_zero_multi(&e, &n, NULL);
+ return -3;
+ }
+
+ padlen = size - flen - 3;
+
+ *p++ = 2;
+ if (RAND_bytes(p, padlen) != 1) {
+ fp_zero_multi(&e, &n, NULL);
+ free(p0);
+ return -4;
+ }
+ while(padlen) {
+ if (*p == 0)
+ *p = 1;
+ padlen--;
+ p++;
+ }
+ *p++ = 0;
+ memcpy(p, from, flen);
+ p += flen;
+ assert((p - p0) == size - 1);
+
+ fp_init_multi(&enc, &dec, NULL);
+ fp_read_unsigned_bin(&dec, p0, size - 1);
+ free(p0);
+
+ res = fp_exptmod(&dec, &e, &n, &enc);
+
+ fp_zero_multi(&dec, &e, &n, NULL);
+
+ if (res != 0)
+ return -4;
+
+ {
+ size_t ssize;
+ ssize = fp_unsigned_bin_size(&enc);
+ assert(size >= ssize);
+ fp_to_unsigned_bin(&enc, to);
+ size = ssize;
+ }
+ fp_zero(&enc);
+
+ return size;
+}
+
+static int
+tfm_rsa_public_decrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *p;
+ int res;
+ size_t size;
+ fp_int s, us, n, e;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ if (flen > RSA_size(rsa))
+ return -2;
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+#if 0
+ /* Check that the exponent is larger then 3 */
+ if (mp_int_compare_value(&e, 3) <= 0) {
+ fp_zero_multi(&e, &n, NULL);
+ return -3;
+ }
+#endif
+
+ fp_init_multi(&s, &us, NULL);
+ fp_read_unsigned_bin(&s, rk_UNCONST(from), flen);
+
+ if (fp_cmp(&s, &n) >= 0) {
+ fp_zero_multi(&e, &n, NULL);
+ return -4;
+ }
+
+ res = fp_exptmod(&s, &e, &n, &us);
+
+ fp_zero_multi(&s, &e, &n, NULL);
+
+ if (res != 0)
+ return -5;
+ p = to;
+
+
+ size = fp_unsigned_bin_size(&us);
+ assert(size <= RSA_size(rsa));
+ fp_to_unsigned_bin(&us, p);
+
+ fp_zero(&us);
+
+ /* head zero was skipped by fp_to_unsigned_bin */
+ if (*p == 0)
+ return -6;
+ if (*p != 1)
+ return -7;
+ size--; p++;
+ while (size && *p == 0xff) {
+ size--; p++;
+ }
+ if (size == 0 || *p != 0)
+ return -8;
+ size--; p++;
+
+ memmove(to, p, size);
+
+ return size;
+}
+
+static int
+tfm_rsa_private_encrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *p, *p0;
+ int res;
+ int size;
+ fp_int in, out, n, e;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ size = RSA_size(rsa);
+
+ if (size < RSA_PKCS1_PADDING_SIZE || size - RSA_PKCS1_PADDING_SIZE < flen)
+ return -2;
+
+ p0 = p = malloc(size);
+ *p++ = 0;
+ *p++ = 1;
+ memset(p, 0xff, size - flen - 3);
+ p += size - flen - 3;
+ *p++ = 0;
+ memcpy(p, from, flen);
+ p += flen;
+ assert((p - p0) == size);
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+ fp_init_multi(&in, &out, NULL);
+ fp_read_unsigned_bin(&in, p0, size);
+ free(p0);
+
+ if(fp_isneg(&in) || fp_cmp(&in, &n) >= 0) {
+ size = -3;
+ goto out;
+ }
+
+ if (rsa->p && rsa->q && rsa->dmp1 && rsa->dmq1 && rsa->iqmp) {
+ fp_int p, q, dmp1, dmq1, iqmp;
+
+ BN2mpz(&p, rsa->p);
+ BN2mpz(&q, rsa->q);
+ BN2mpz(&dmp1, rsa->dmp1);
+ BN2mpz(&dmq1, rsa->dmq1);
+ BN2mpz(&iqmp, rsa->iqmp);
+
+ res = tfm_rsa_private_calculate(&in, &p, &q, &dmp1, &dmq1, &iqmp, &out);
+
+ fp_zero_multi(&p, &q, &dmp1, &dmq1, &iqmp, NULL);
+
+ if (res != 0) {
+ size = -4;
+ goto out;
+ }
+ } else {
+ fp_int d;
+
+ BN2mpz(&d, rsa->d);
+ res = fp_exptmod(&in, &d, &n, &out);
+ fp_zero(&d);
+ if (res != 0) {
+ size = -5;
+ goto out;
+ }
+ }
+
+ if (size > 0) {
+ size_t ssize;
+ ssize = fp_unsigned_bin_size(&out);
+ assert(size >= ssize);
+ fp_to_unsigned_bin(&out, to);
+ size = ssize;
+ }
+
+ out:
+ fp_zero_multi(&e, &n, &in, &out, NULL);
+
+ return size;
+}
+
+static int
+tfm_rsa_private_decrypt(int flen, const unsigned char* from,
+ unsigned char* to, RSA* rsa, int padding)
+{
+ unsigned char *ptr;
+ int res;
+ int size;
+ fp_int in, out, n, e;
+
+ if (padding != RSA_PKCS1_PADDING)
+ return -1;
+
+ size = RSA_size(rsa);
+ if (flen > size)
+ return -2;
+
+ fp_init_multi(&in, &out, NULL);
+
+ BN2mpz(&n, rsa->n);
+ BN2mpz(&e, rsa->e);
+
+ fp_read_unsigned_bin(&in, rk_UNCONST(from), flen);
+
+ if(fp_isneg(&in) || fp_cmp(&in, &n) >= 0) {
+ size = -2;
+ goto out;
+ }
+
+ if (rsa->p && rsa->q && rsa->dmp1 && rsa->dmq1 && rsa->iqmp) {
+ fp_int p, q, dmp1, dmq1, iqmp;
+
+ BN2mpz(&p, rsa->p);
+ BN2mpz(&q, rsa->q);
+ BN2mpz(&dmp1, rsa->dmp1);
+ BN2mpz(&dmq1, rsa->dmq1);
+ BN2mpz(&iqmp, rsa->iqmp);
+
+ res = tfm_rsa_private_calculate(&in, &p, &q, &dmp1, &dmq1, &iqmp, &out);
+
+ fp_zero_multi(&p, &q, &dmp1, &dmq1, &iqmp, NULL);
+
+ if (res != 0) {
+ size = -3;
+ goto out;
+ }
+
+ } else {
+ fp_int d;
+
+ if(fp_isneg(&in) || fp_cmp(&in, &n) >= 0)
+ return -4;
+
+ BN2mpz(&d, rsa->d);
+ res = fp_exptmod(&in, &d, &n, &out);
+ fp_zero(&d);
+ if (res != 0) {
+ size = -5;
+ goto out;
+ }
+ }
+
+ ptr = to;
+ {
+ size_t ssize;
+ ssize = fp_unsigned_bin_size(&out);
+ assert(size >= ssize);
+ fp_to_unsigned_bin(&out, ptr);
+ size = ssize;
+ }
+
+ /* head zero was skipped by mp_int_to_unsigned */
+ if (*ptr != 2) {
+ size = -6;
+ goto out;
+ }
+ size--; ptr++;
+ while (size && *ptr != 0) {
+ size--; ptr++;
+ }
+ if (size == 0)
+ return -7;
+ size--; ptr++;
+
+ memmove(to, ptr, size);
+
+ out:
+ fp_zero_multi(&e, &n, &in, &out, NULL);
+
+ return size;
+}
+
+static BIGNUM *
+mpz2BN(fp_int *s)
+{
+ size_t size;
+ BIGNUM *bn;
+ void *p;
+
+ size = fp_unsigned_bin_size(s);
+ p = malloc(size);
+ if (p == NULL && size != 0)
+ return NULL;
+
+ fp_to_unsigned_bin(s, p);
+
+ bn = BN_bin2bn(p, size, NULL);
+ free(p);
+ return bn;
+}
+
+static int
+random_num(fp_int *num, size_t len)
+{
+ unsigned char *p;
+
+ len = (len + 7) / 8;
+ p = malloc(len);
+ if (p == NULL)
+ return 1;
+ if (RAND_bytes(p, len) != 1) {
+ free(p);
+ return 1;
+ }
+ fp_read_unsigned_bin(num, p, len);
+ free(p);
+ return 0;
+}
+
+#define CHECK(f, v) if ((f) != (v)) { goto out; }
+
+static int
+tfm_rsa_generate_key(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb)
+{
+ fp_int el, p, q, n, d, dmp1, dmq1, iqmp, t1, t2, t3;
+ int counter, ret, bitsp;
+
+ if (bits < 789)
+ return -1;
+
+ bitsp = (bits + 1) / 2;
+
+ ret = -1;
+
+ fp_init_multi(&el, &p, &q, &n, &n, &d, &dmp1, &dmq1, &iqmp, &t1, &t2, &t3, NULL);
+
+ BN2mpz(&el, e);
+
+ /* generate p and q so that p != q and bits(pq) ~ bits */
+ counter = 0;
+ do {
+ BN_GENCB_call(cb, 2, counter++);
+ CHECK(random_num(&p, bitsp), 0);
+ CHECK(fp_find_prime(&p), FP_YES);
+
+ fp_sub_d(&p, 1, &t1);
+ fp_gcd(&t1, &el, &t2);
+ } while(fp_cmp_d(&t2, 1) != 0);
+
+ BN_GENCB_call(cb, 3, 0);
+
+ counter = 0;
+ do {
+ BN_GENCB_call(cb, 2, counter++);
+ CHECK(random_num(&q, bits - bitsp), 0);
+ CHECK(fp_find_prime(&q), FP_YES);
+
+ if (fp_cmp(&p, &q) == 0) /* don't let p and q be the same */
+ continue;
+
+ fp_sub_d(&q, 1, &t1);
+ fp_gcd(&t1, &el, &t2);
+ } while(fp_cmp_d(&t2, 1) != 0);
+
+ /* make p > q */
+ if (fp_cmp(&p, &q) < 0) {
+ fp_int c;
+ fp_copy(&p, &c);
+ fp_copy(&q, &p);
+ fp_copy(&c, &q);
+ }
+
+ BN_GENCB_call(cb, 3, 1);
+
+ /* calculate n, n = p * q */
+ fp_mul(&p, &q, &n);
+
+ /* calculate d, d = 1/e mod (p - 1)(q - 1) */
+ fp_sub_d(&p, 1, &t1);
+ fp_sub_d(&q, 1, &t2);
+ fp_mul(&t1, &t2, &t3);
+ fp_invmod(&el, &t3, &d);
+
+ /* calculate dmp1 dmp1 = d mod (p-1) */
+ fp_mod(&d, &t1, &dmp1);
+ /* calculate dmq1 dmq1 = d mod (q-1) */
+ fp_mod(&d, &t2, &dmq1);
+ /* calculate iqmp iqmp = 1/q mod p */
+ fp_invmod(&q, &p, &iqmp);
+
+ /* fill in RSA key */
+
+ rsa->e = mpz2BN(&el);
+ rsa->p = mpz2BN(&p);
+ rsa->q = mpz2BN(&q);
+ rsa->n = mpz2BN(&n);
+ rsa->d = mpz2BN(&d);
+ rsa->dmp1 = mpz2BN(&dmp1);
+ rsa->dmq1 = mpz2BN(&dmq1);
+ rsa->iqmp = mpz2BN(&iqmp);
+
+ ret = 1;
+
+out:
+ fp_zero_multi(&el, &p, &q, &n, &d, &dmp1,
+ &dmq1, &iqmp, &t1, &t2, &t3, NULL);
+
+ return ret;
+}
+
+static int
+tfm_rsa_init(RSA *rsa)
+{
+ return 1;
+}
+
+static int
+tfm_rsa_finish(RSA *rsa)
+{
+ return 1;
+}
+
+const RSA_METHOD hc_rsa_tfm_method = {
+ "hcrypto tfm RSA",
+ tfm_rsa_public_encrypt,
+ tfm_rsa_public_decrypt,
+ tfm_rsa_private_encrypt,
+ tfm_rsa_private_decrypt,
+ NULL,
+ NULL,
+ tfm_rsa_init,
+ tfm_rsa_finish,
+ 0,
+ NULL,
+ NULL,
+ NULL,
+ tfm_rsa_generate_key
+};
+
+#endif
+
+const RSA_METHOD *
+RSA_tfm_method(void)
+{
+#ifdef USE_HCRYPTO_TFM
+ return &hc_rsa_tfm_method;
+#else
+ return NULL;
+#endif
+}
+
diff --git a/lib/hcrypto/rsa.c b/lib/hcrypto/rsa.c
index c71ded1b7a8b..c99b2b6cbe95 100644
--- a/lib/hcrypto/rsa.c
+++ b/lib/hcrypto/rsa.c
@@ -32,9 +32,7 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
+#include <roken.h>
#include <krb5-types.h>
#include <rfc2459_asn1.h>
@@ -44,8 +42,6 @@
#include "common.h"
-#include <roken.h>
-
/**
* @page page_rsa RSA - public-key cryptography
*
@@ -508,6 +504,7 @@ static const RSA_METHOD rsa_null_method = {
0,
NULL,
NULL,
+ NULL,
NULL
};
diff --git a/lib/hcrypto/rsa.h b/lib/hcrypto/rsa.h
index 798852d3f158..d59213b3923e 100644
--- a/lib/hcrypto/rsa.h
+++ b/lib/hcrypto/rsa.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * Copyright (c) 2006-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -67,6 +67,9 @@
#define i2d_RSAPublicKey hc_i2d_RSAPublicKey
#define d2i_RSAPublicKey hc_d2i_RSAPublicKey
+#define RSA hc_RSA
+#define RSA_METHOD hc_RSA_METHOD
+
/*
*
*/
diff --git a/lib/hcrypto/sha.c b/lib/hcrypto/sha.c
index 9c8b39e24190..8eae9b4166bc 100644
--- a/lib/hcrypto/sha.c
+++ b/lib/hcrypto/sha.c
@@ -31,7 +31,8 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#include "hash.h"
#include "sha.h"
@@ -43,7 +44,7 @@
#define E m->counter[4]
#define X data
-void
+int
SHA1_Init (struct sha *m)
{
m->sz[0] = 0;
@@ -53,6 +54,7 @@ SHA1_Init (struct sha *m)
C = 0x98badcfe;
D = 0x10325476;
E = 0xc3d2e1f0;
+ return 1;
}
@@ -220,7 +222,7 @@ struct x32{
unsigned int b:32;
};
-void
+int
SHA1_Update (struct sha *m, const void *v, size_t len)
{
const unsigned char *p = v;
@@ -253,9 +255,10 @@ SHA1_Update (struct sha *m, const void *v, size_t len)
offset = 0;
}
}
+ return 1;
}
-void
+int
SHA1_Final (void *res, struct sha *m)
{
unsigned char zeros[72];
@@ -293,4 +296,5 @@ SHA1_Final (void *res, struct sha *m)
r[i] = swap_uint32_t (m->counter[i]);
}
#endif
+ return 1;
}
diff --git a/lib/hcrypto/sha.h b/lib/hcrypto/sha.h
index fbc1810b4aee..59d72408c579 100644
--- a/lib/hcrypto/sha.h
+++ b/lib/hcrypto/sha.h
@@ -64,9 +64,9 @@ struct sha {
typedef struct sha SHA_CTX;
-void SHA1_Init (struct sha *m);
-void SHA1_Update (struct sha *m, const void *v, size_t len);
-void SHA1_Final (void *res, struct sha *m);
+int SHA1_Init (struct sha *m);
+int SHA1_Update (struct sha *m, const void *v, size_t len);
+int SHA1_Final (void *res, struct sha *m);
/*
* SHA-2 256
@@ -82,9 +82,9 @@ struct hc_sha256state {
typedef struct hc_sha256state SHA256_CTX;
-void SHA256_Init (SHA256_CTX *);
-void SHA256_Update (SHA256_CTX *, const void *, size_t);
-void SHA256_Final (void *, SHA256_CTX *);
+int SHA256_Init (SHA256_CTX *);
+int SHA256_Update (SHA256_CTX *, const void *, size_t);
+int SHA256_Final (void *, SHA256_CTX *);
/*
* SHA-2 512
@@ -100,16 +100,16 @@ struct hc_sha512state {
typedef struct hc_sha512state SHA512_CTX;
-void SHA512_Init (SHA512_CTX *);
-void SHA512_Update (SHA512_CTX *, const void *, size_t);
-void SHA512_Final (void *, SHA512_CTX *);
+int SHA512_Init (SHA512_CTX *);
+int SHA512_Update (SHA512_CTX *, const void *, size_t);
+int SHA512_Final (void *, SHA512_CTX *);
#define SHA384_DIGEST_LENGTH 48
typedef struct hc_sha512state SHA384_CTX;
-void SHA384_Init (SHA384_CTX *);
-void SHA384_Update (SHA384_CTX *, const void *, size_t);
-void SHA384_Final (void *, SHA384_CTX *);
+int SHA384_Init (SHA384_CTX *);
+int SHA384_Update (SHA384_CTX *, const void *, size_t);
+int SHA384_Final (void *, SHA384_CTX *);
#endif /* HEIM_SHA_H */
diff --git a/lib/hcrypto/sha256.c b/lib/hcrypto/sha256.c
index 108afdccc800..1b62ba20c74e 100644
--- a/lib/hcrypto/sha256.c
+++ b/lib/hcrypto/sha256.c
@@ -31,7 +31,8 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#include "hash.h"
#include "sha.h"
@@ -74,7 +75,7 @@ static const uint32_t constant_256[64] = {
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
-void
+int
SHA256_Init (SHA256_CTX *m)
{
m->sz[0] = 0;
@@ -87,6 +88,7 @@ SHA256_Init (SHA256_CTX *m)
F = 0x9b05688c;
G = 0x1f83d9ab;
H = 0x5be0cd19;
+ return 1;
}
static void
@@ -162,7 +164,7 @@ struct x32{
unsigned int b:32;
};
-void
+int
SHA256_Update (SHA256_CTX *m, const void *v, size_t len)
{
const unsigned char *p = v;
@@ -195,9 +197,10 @@ SHA256_Update (SHA256_CTX *m, const void *v, size_t len)
offset = 0;
}
}
+ return 1;
}
-void
+int
SHA256_Final (void *res, SHA256_CTX *m)
{
unsigned char zeros[72];
@@ -226,4 +229,5 @@ SHA256_Final (void *res, SHA256_CTX *m)
r[4*i] = (m->counter[i] >> 24) & 0xFF;
}
}
+ return 1;
}
diff --git a/lib/hcrypto/sha512.c b/lib/hcrypto/sha512.c
index 4bea21666852..98c418e1ee20 100644
--- a/lib/hcrypto/sha512.c
+++ b/lib/hcrypto/sha512.c
@@ -31,7 +31,8 @@
* SUCH DAMAGE.
*/
-#include "config.h"
+#include <config.h>
+#include <roken.h>
#include "hash.h"
#include "sha.h"
@@ -98,7 +99,7 @@ static const uint64_t constant_512[80] = {
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
};
-void
+int
SHA512_Init (SHA512_CTX *m)
{
m->sz[0] = 0;
@@ -111,6 +112,7 @@ SHA512_Init (SHA512_CTX *m)
F = 0x9b05688c2b3e6c1fULL;
G = 0x1f83d9abfb41bd6bULL;
H = 0x5be0cd19137e2179ULL;
+ return 1;
}
static void
@@ -184,7 +186,7 @@ struct x64{
};
#endif
-void
+int
SHA512_Update (SHA512_CTX *m, const void *v, size_t len)
{
const unsigned char *p = v;
@@ -217,9 +219,10 @@ SHA512_Update (SHA512_CTX *m, const void *v, size_t len)
offset = 0;
}
}
+ return 1;
}
-void
+int
SHA512_Final (void *res, SHA512_CTX *m)
{
unsigned char zeros[128 + 16];
@@ -261,9 +264,10 @@ SHA512_Final (void *res, SHA512_CTX *m)
r[8*i] = (m->counter[i] >> 56) & 0xFF;
}
}
+ return 1;
}
-void
+int
SHA384_Init(SHA384_CTX *m)
{
m->sz[0] = 0;
@@ -276,19 +280,22 @@ SHA384_Init(SHA384_CTX *m)
F = 0x8eb44a8768581511ULL;
G = 0xdb0c2e0d64f98fa7ULL;
H = 0x47b5481dbefa4fa4ULL;
+ return 1;
}
-void
+int
SHA384_Update (SHA384_CTX *m, const void *v, size_t len)
{
SHA512_Update(m, v, len);
+ return 1;
}
-void
+int
SHA384_Final (void *res, SHA384_CTX *m)
{
unsigned char data[SHA512_DIGEST_LENGTH];
SHA512_Final(data, m);
memcpy(res, data, SHA384_DIGEST_LENGTH);
+ return 1;
}
diff --git a/lib/hcrypto/test_bn.c b/lib/hcrypto/test_bn.c
index 03db8912843a..74709fe2369f 100644
--- a/lib/hcrypto/test_bn.c
+++ b/lib/hcrypto/test_bn.c
@@ -32,12 +32,7 @@
*/
#include <config.h>
-
-#include <sys/types.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <roken.h>
#include <bn.h>
#include <rand.h>
diff --git a/lib/hcrypto/test_bulk.c b/lib/hcrypto/test_bulk.c
new file mode 100644
index 000000000000..e3737fd1e953
--- /dev/null
+++ b/lib/hcrypto/test_bulk.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <roken.h>
+#include <assert.h>
+#include <getarg.h>
+
+#include <evp.h>
+#include <evp-hcrypto.h>
+#include <evp-cc.h>
+#if defined(_WIN32)
+#include <evp-w32.h>
+#endif
+#include <evp-pkcs11.h>
+#include <hex.h>
+#include <err.h>
+
+#ifdef WIN32
+#define STATS_START(M) \
+ LARGE_INTEGER StartingTime, EndingTime, ElapsedMicroseconds; \
+ LARGE_INTEGER Frequency; \
+ \
+ QueryPerformanceFrequency(&Frequency); \
+ QueryPerformanceCounter(&StartingTime);
+
+#define STATS_END(M) \
+ QueryPerformanceCounter(&EndingTime); \
+ ElapsedMicroseconds.QuadPart = EndingTime.QuadPart - StartingTime.QuadPart; \
+ ElapsedMicroseconds.QuadPart *= 1000000; \
+ ElapsedMicroseconds.QuadPart /= Frequency.QuadPart; \
+ \
+ M += (ElapsedMicroseconds.QuadPart - M) / (i + 1);
+#else
+#define STATS_START(M) \
+ struct timeval StartingTime, EndingTime; \
+ \
+ gettimeofday(&StartingTime, NULL);
+
+#define STATS_END(M) \
+ gettimeofday(&EndingTime, NULL); \
+ timevalsub(&EndingTime, &StartingTime); \
+ M += (EndingTime.tv_sec * 1000000 + EndingTime.tv_usec - M) / (i + 1);
+#endif
+
+static int version_flag;
+static int help_flag;
+static int len = 1;
+static int loops = 20;
+static char *provider = "hcrypto";
+static unsigned char *d;
+
+#ifdef __APPLE__
+#define PROVIDER_USAGE "hcrypto|cc"
+#elif defined(WIN32)
+#define PROVIDER_USAGE "hcrypto|w32crypto"
+#elif __sun || defined(PKCS11_MODULE_PATH)
+#define PROVIDER_USAGE "hcrypto|pkcs11"
+#else
+#define PROVIDER_USAGE "hcrypto"
+#endif
+
+static struct getargs args[] = {
+ { "provider", 0, arg_string, &provider,
+ "crypto provider", PROVIDER_USAGE },
+ { "loops", 0, arg_integer, &loops,
+ "number of loops", "loops" },
+ { "size", 0, arg_integer, &len,
+ "size (KB)", NULL },
+ { "version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ { "help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+static int
+test_bulk_cipher(const char *cname, const EVP_CIPHER *c)
+{
+ static unsigned char key[16];
+ static unsigned char iv[16];
+ int i;
+ int64_t M = 0;
+
+ if (c == NULL) {
+ printf("%s not supported\n", cname);
+ return 0;
+ }
+
+ for (i = 0; i < loops; i++) {
+ EVP_CIPHER_CTX ectx;
+ EVP_CIPHER_CTX dctx;
+
+ STATS_START(M)
+
+ EVP_CIPHER_CTX_init(&ectx);
+ EVP_CIPHER_CTX_init(&dctx);
+
+ if (EVP_CipherInit_ex(&ectx, c, NULL, NULL, NULL, 1) != 1)
+ errx(1, "can't init encrypt");
+ if (EVP_CipherInit_ex(&dctx, c, NULL, NULL, NULL, 0) != 1)
+ errx(1, "can't init decrypt");
+
+ EVP_CIPHER_CTX_set_key_length(&ectx, sizeof(key));
+ EVP_CIPHER_CTX_set_key_length(&dctx, sizeof(key));
+
+ if (EVP_CipherInit_ex(&ectx, NULL, NULL, key, iv, 1) != 1)
+ errx(1, "can't init encrypt");
+ if (EVP_CipherInit_ex(&dctx, NULL, NULL, key, iv, 0) != 1)
+ errx(1, "can't init decrypt");
+
+ if (!EVP_Cipher(&ectx, d, d, len))
+ errx(1, "can't encrypt");
+ if (!EVP_Cipher(&dctx, d, d, len))
+ errx(1, "can't decrypt");
+
+ EVP_CIPHER_CTX_cleanup(&ectx);
+ EVP_CIPHER_CTX_cleanup(&dctx);
+
+ STATS_END(M);
+
+ if (d[0] != 0x00 || d[len - 1] != ((len - 1) & 0xff))
+ errx(1, "encrypt/decrypt inconsistent");
+ }
+
+ printf("%s: mean time %llu usec%s\n", cname, (unsigned long long)M,
+ (M == 1) ? "" : "s");
+
+ return 0;
+}
+
+static int
+test_bulk_digest(const char *cname, const EVP_MD *md)
+{
+ char digest[EVP_MAX_MD_SIZE];
+ int i;
+ unsigned int tmp = sizeof(digest);
+ int64_t M = 0;
+
+ if (md == NULL) {
+ printf("%s not supported\n", cname);
+ return 0;
+ }
+
+ for (i = 0; i < loops; i++) {
+ STATS_START(M);
+ EVP_Digest(d, len, digest, &tmp, md, NULL);
+ STATS_END(M);
+ }
+
+ printf("%s: mean time %llu usec%s\n", cname, (unsigned long long)M,
+ (M == 1) ? "" : "s");
+
+ return 0;
+}
+
+static void
+test_bulk_provider_hcrypto(void)
+{
+ test_bulk_cipher("hcrypto_aes_256_cbc", EVP_hcrypto_aes_256_cbc());
+#if 0
+ test_bulk_cipher("hcrypto_aes_256_cfb8", EVP_hcrypto_aes_256_cfb8());
+#endif
+ test_bulk_cipher("hcrypto_rc4", EVP_hcrypto_rc4());
+ test_bulk_digest("hcrypto_md2", EVP_hcrypto_md2());
+ test_bulk_digest("hcrypto_md4", EVP_hcrypto_md4());
+ test_bulk_digest("hcrypto_md5", EVP_hcrypto_md5());
+ test_bulk_digest("hcrypto_sha1", EVP_hcrypto_sha1());
+ test_bulk_digest("hcrypto_sha256", EVP_hcrypto_sha256());
+ test_bulk_digest("hcrypto_sha384", EVP_hcrypto_sha384());
+ test_bulk_digest("hcrypto_sha512", EVP_hcrypto_sha512());
+}
+
+#ifdef __APPLE__
+static void
+test_bulk_provider_cc(void)
+{
+ test_bulk_cipher("cc_aes_256_cbc", EVP_cc_aes_256_cbc());
+#if 0
+ test_bulk_cipher("cc_aes_256_cfb8", EVP_cc_aes_256_cfb8());
+#endif
+ test_bulk_cipher("cc_rc4", EVP_cc_rc4());
+ test_bulk_digest("cc_md2", EVP_cc_md2());
+ test_bulk_digest("cc_md4", EVP_cc_md4());
+ test_bulk_digest("cc_md5", EVP_cc_md5());
+ test_bulk_digest("cc_sha1", EVP_cc_sha1());
+ test_bulk_digest("cc_sha256", EVP_cc_sha256());
+ test_bulk_digest("cc_sha384", EVP_cc_sha384());
+ test_bulk_digest("cc_sha512", EVP_cc_sha512());
+}
+#endif /* __APPLE__ */
+
+#ifdef WIN32
+static void
+test_bulk_provider_w32crypto(void)
+{
+ test_bulk_cipher("w32crypto_aes_256_cbc", EVP_w32crypto_aes_256_cbc());
+#if 0
+ test_bulk_cipher("w32crypto_aes_256_cfb8", EVP_w32crypto_aes_256_cfb8());
+#endif
+ test_bulk_cipher("w32crypto_rc4", EVP_w32crypto_rc4());
+ test_bulk_digest("w32crypto_md2", EVP_w32crypto_md2());
+ test_bulk_digest("w32crypto_md4", EVP_w32crypto_md4());
+ test_bulk_digest("w32crypto_md5", EVP_w32crypto_md5());
+ test_bulk_digest("w32crypto_sha1", EVP_w32crypto_sha1());
+ test_bulk_digest("w32crypto_sha256", EVP_w32crypto_sha256());
+ test_bulk_digest("w32crypto_sha384", EVP_w32crypto_sha384());
+ test_bulk_digest("w32crypto_sha512", EVP_w32crypto_sha512());
+}
+#endif /* WIN32 */
+
+#if __sun || defined(PKCS11_MODULE_PATH)
+static void
+test_bulk_provider_pkcs11(void)
+{
+ test_bulk_cipher("pkcs11_aes_256_cbc", EVP_pkcs11_aes_256_cbc());
+ test_bulk_cipher("pkcs11_rc4", EVP_pkcs11_rc4());
+ test_bulk_digest("pkcs11_md5", EVP_pkcs11_md5());
+ test_bulk_digest("pkcs11_sha1", EVP_pkcs11_sha1());
+ test_bulk_digest("pkcs11_sha256", EVP_pkcs11_sha256());
+ test_bulk_digest("pkcs11_sha384", EVP_pkcs11_sha384());
+ test_bulk_digest("pkcs11_sha512", EVP_pkcs11_sha512());
+}
+#endif /* __sun || PKCS11_MODULE_PATH */
+
+int
+main(int argc, char **argv)
+{
+ int ret = 0;
+ int idx = 0;
+ int i;
+
+ setprogname(argv[0]);
+
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &idx))
+ usage(1);
+
+ if (help_flag)
+ usage(0);
+
+ if(version_flag) {
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= idx;
+ argv += idx;
+
+ len *= 1024;
+
+ d = emalloc(len);
+ for (i = 0; i < len; i++)
+ d[i] = i & 0xff;
+
+ if (strcmp(provider, "hcrypto") == 0)
+ test_bulk_provider_hcrypto();
+#ifdef __APPLE__
+ else if (strcmp(provider, "cc") == 0)
+ test_bulk_provider_cc();
+#endif
+#ifdef WIN32
+ else if (strcmp(provider, "w32crypto") == 0)
+ test_bulk_provider_w32crypto();
+#endif
+#if __sun || defined(PKCS11_MODULE_PATH)
+ else if (strcmp(provider, "pkcs11") == 0)
+ test_bulk_provider_pkcs11();
+#endif
+ else
+ usage(1);
+
+ free(d);
+
+ return ret;
+}
diff --git a/lib/hcrypto/test_cipher.c b/lib/hcrypto/test_cipher.c
index 8e54435a093b..0131e148b172 100644
--- a/lib/hcrypto/test_cipher.c
+++ b/lib/hcrypto/test_cipher.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * Copyright (c) 2006-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -32,20 +32,20 @@
*/
#include <config.h>
+#include <roken.h>
#define HC_DEPRECATED_CRYPTO
-#include <sys/types.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <getarg.h>
-#include <roken.h>
#include <evp.h>
#include <evp-hcrypto.h>
#include <evp-cc.h>
+#if defined(_WIN32)
+#include <evp-w32.h>
+#endif
+#include <evp-pkcs11.h>
+#include <evp-openssl.h>
#include <hex.h>
#include <err.h>
@@ -68,7 +68,8 @@ struct tests aes_tests[] = {
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
16,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
- "\xdc\x95\xc0\x78\xa2\x40\x89\x89\xad\x48\xa2\x14\x92\x84\x20\x87"
+ "\xdc\x95\xc0\x78\xa2\x40\x89\x89\xad\x48\xa2\x14\x92\x84\x20\x87",
+ NULL
}
};
@@ -79,10 +80,25 @@ struct tests aes_cfb_tests[] = {
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
16,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
- "\x66\xe9\x4b\xd4\xef\x8a\x2c\x3b\x88\x4c\xfa\x59\xca\x34\x2b\x2e"
+ "\x66\x16\xf9\x2e\x42\xa8\xf1\x1a\x91\x16\x68\x57\x8e\xc3\xaa\x0f",
+ NULL
+ }
+};
+
+
+struct tests rc2_tests[] = {
+ { "rc2",
+ "\x88\xbc\xa9\x0e\x90\x87\x5a\x7f\x0f\x79\xc3\x84\x62\x7b\xaf\xb2",
+ 16,
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ 8,
+ "\x00\x00\x00\x00\x00\x00\x00\x00",
+ "\x22\x69\x55\x2a\xb0\xf8\x5c\xa6",
+ NULL
}
};
+
struct tests rc2_40_tests[] = {
{ "rc2-40",
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
@@ -90,7 +106,8 @@ struct tests rc2_40_tests[] = {
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
16,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
- "\xc0\xb8\xff\xa5\xd6\xeb\xc9\x62\xcc\x52\x5f\xfe\x9a\x3c\x97\xe6"
+ "\xc0\xb8\xff\xa5\xd6\xeb\xc9\x62\xcc\x52\x5f\xfe\x9a\x3c\x97\xe6",
+ NULL
}
};
@@ -103,7 +120,8 @@ struct tests des_ede3_tests[] = {
"\xbf\x9a\x12\xb7\x26\x69\xfd\x05",
16,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
- "\x55\x95\x97\x76\xa9\x6c\x66\x40\x64\xc7\xf4\x1c\x21\xb7\x14\x1b"
+ "\x55\x95\x97\x76\xa9\x6c\x66\x40\x64\xc7\xf4\x1c\x21\xb7\x14\x1b",
+ NULL
}
};
@@ -223,7 +241,8 @@ struct tests rc4_tests[] = {
"\x89\x21\xc2\xf5\xa4\x63\x93\x8c"
"\xe0\x98\x22\x65\xee\xf7\x01\x79"
"\xbc\x55\x3f\x33\x9e\xb1\xa4\xc1"
- "\xaf\x5f\x6a\x54\x7f"
+ "\xaf\x5f\x6a\x54\x7f",
+ NULL
}
};
@@ -259,7 +278,7 @@ test_cipher(int i, const EVP_CIPHER *c, struct tests *t)
d = emalloc(t->datasize);
if (!EVP_Cipher(&ectx, d, t->indata, t->datasize))
- return 1;
+ errx(1, "%s: %d EVP_Cipher encrypt failed", t->name, i);
if (memcmp(d, t->outdata, t->datasize) != 0) {
char *s, *s2;
@@ -269,7 +288,7 @@ test_cipher(int i, const EVP_CIPHER *c, struct tests *t)
}
if (!EVP_Cipher(&dctx, d, d, t->datasize))
- return 1;
+ errx(1, "%s: %d EVP_Cipher decrypt failed", t->name, i);
if (memcmp(d, t->indata, t->datasize) != 0) {
char *s;
@@ -333,7 +352,8 @@ main(int argc, char **argv)
ret += test_cipher(i, EVP_hcrypto_aes_256_cbc(), &aes_tests[i]);
for (i = 0; i < sizeof(aes_cfb_tests)/sizeof(aes_cfb_tests[0]); i++)
ret += test_cipher(i, EVP_hcrypto_aes_128_cfb8(), &aes_cfb_tests[i]);
-
+ for (i = 0; i < sizeof(rc2_tests)/sizeof(rc2_tests[0]); i++)
+ ret += test_cipher(i, EVP_hcrypto_rc2_cbc(), &rc2_tests[i]);
for (i = 0; i < sizeof(rc2_40_tests)/sizeof(rc2_40_tests[0]); i++)
ret += test_cipher(i, EVP_hcrypto_rc2_40_cbc(), &rc2_40_tests[i]);
for (i = 0; i < sizeof(des_ede3_tests)/sizeof(des_ede3_tests[0]); i++)
@@ -348,10 +368,8 @@ main(int argc, char **argv)
#ifdef __APPLE__
for (i = 0; i < sizeof(aes_tests)/sizeof(aes_tests[0]); i++)
ret += test_cipher(i, EVP_cc_aes_256_cbc(), &aes_tests[i]);
-#if 0
for (i = 0; i < sizeof(aes_cfb_tests)/sizeof(aes_cfb_tests[0]); i++)
ret += test_cipher(i, EVP_cc_aes_128_cfb8(), &aes_cfb_tests[i]);
-#endif
for (i = 0; i < sizeof(rc2_40_tests)/sizeof(rc2_40_tests[0]); i++)
ret += test_cipher(i, EVP_cc_rc2_40_cbc(), &rc2_40_tests[i]);
for (i = 0; i < sizeof(des_ede3_tests)/sizeof(des_ede3_tests[0]); i++)
@@ -361,7 +379,55 @@ main(int argc, char **argv)
&camellia128_tests[i]);
for (i = 0; i < sizeof(rc4_tests)/sizeof(rc4_tests[0]); i++)
ret += test_cipher(i, EVP_cc_rc4(), &rc4_tests[i]);
-#endif
+#endif /* __APPLE__ */
+
+ /* Windows CNG (if available) */
+#ifdef WIN32
+ for (i = 0; i < sizeof(aes_tests)/sizeof(aes_tests[0]); i++)
+ ret += test_cipher(i, EVP_w32crypto_aes_256_cbc(), &aes_tests[i]);
+ for (i = 0; i < sizeof(aes_cfb_tests)/sizeof(aes_cfb_tests[0]); i++)
+ ret += test_cipher(i, EVP_w32crypto_aes_128_cfb8(), &aes_cfb_tests[i]);
+ for (i = 0; i < sizeof(rc2_tests)/sizeof(rc2_tests[0]); i++)
+ ret += test_cipher(i, EVP_w32crypto_rc2_cbc(), &rc2_tests[i]);
+ for (i = 0; i < sizeof(rc2_40_tests)/sizeof(rc2_40_tests[0]); i++)
+ ret += test_cipher(i, EVP_w32crypto_rc2_40_cbc(), &rc2_40_tests[i]);
+ for (i = 0; i < sizeof(des_ede3_tests)/sizeof(des_ede3_tests[0]); i++)
+ ret += test_cipher(i, EVP_w32crypto_des_ede3_cbc(), &des_ede3_tests[i]);
+ for (i = 0; i < sizeof(rc4_tests)/sizeof(rc4_tests[0]); i++)
+ ret += test_cipher(i, EVP_w32crypto_rc4(), &rc4_tests[i]);
+#endif /* WIN32 */
+
+ /* PKCS#11 */
+#if __sun || defined(PKCS11_MODULE_PATH)
+ for (i = 0; i < sizeof(aes_tests)/sizeof(aes_tests[0]); i++)
+ ret += test_cipher(i, EVP_pkcs11_aes_256_cbc(), &aes_tests[i]);
+ for (i = 0; i < sizeof(aes_cfb_tests)/sizeof(aes_cfb_tests[0]); i++)
+ ret += test_cipher(i, EVP_pkcs11_aes_128_cfb8(), &aes_cfb_tests[i]);
+ for (i = 0; i < sizeof(rc2_tests)/sizeof(rc2_tests[0]); i++)
+ ret += test_cipher(i, EVP_pkcs11_rc2_cbc(), &rc2_tests[i]);
+ for (i = 0; i < sizeof(rc2_40_tests)/sizeof(rc2_40_tests[0]); i++)
+ ret += test_cipher(i, EVP_pkcs11_rc2_40_cbc(), &rc2_40_tests[i]);
+ for (i = 0; i < sizeof(des_ede3_tests)/sizeof(des_ede3_tests[0]); i++)
+ ret += test_cipher(i, EVP_pkcs11_des_ede3_cbc(), &des_ede3_tests[i]);
+ for (i = 0; i < sizeof(rc4_tests)/sizeof(rc4_tests[0]); i++)
+ ret += test_cipher(i, EVP_pkcs11_rc4(), &rc4_tests[i]);
+#endif /* PKCS11_MODULE_PATH */
+
+ /* OpenSSL */
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ for (i = 0; i < sizeof(aes_tests)/sizeof(aes_tests[0]); i++)
+ ret += test_cipher(i, EVP_ossl_aes_256_cbc(), &aes_tests[i]);
+ for (i = 0; i < sizeof(aes_cfb_tests)/sizeof(aes_cfb_tests[0]); i++)
+ ret += test_cipher(i, EVP_ossl_aes_128_cfb8(), &aes_cfb_tests[i]);
+ for (i = 0; i < sizeof(rc2_tests)/sizeof(rc2_tests[0]); i++)
+ ret += test_cipher(i, EVP_ossl_rc2_cbc(), &rc2_tests[i]);
+ for (i = 0; i < sizeof(rc2_40_tests)/sizeof(rc2_40_tests[0]); i++)
+ ret += test_cipher(i, EVP_ossl_rc2_40_cbc(), &rc2_40_tests[i]);
+ for (i = 0; i < sizeof(des_ede3_tests)/sizeof(des_ede3_tests[0]); i++)
+ ret += test_cipher(i, EVP_ossl_des_ede3_cbc(), &des_ede3_tests[i]);
+ for (i = 0; i < sizeof(rc4_tests)/sizeof(rc4_tests[0]); i++)
+ ret += test_cipher(i, EVP_ossl_rc4(), &rc4_tests[i]);
+#endif /* PKCS11_MODULE_PATH */
return ret;
}
diff --git a/lib/hcrypto/test_crypto.in b/lib/hcrypto/test_crypto.in
index f24ef266e0fe..d5b3893164ab 100644
--- a/lib/hcrypto/test_crypto.in
+++ b/lib/hcrypto/test_crypto.in
@@ -92,7 +92,7 @@ done
#
# Last time we run is w/o HOME and RANDFILE to make sure we can do
-# RAND_file_name() when the enviroment is lacking those.
+# RAND_file_name() when the environment is lacking those.
#
if [ -r /dev/random -o -r /dev/urandom -o -r /dev/srandom -o -r /dev/arandom ] ; then
diff --git a/lib/hcrypto/test_dh.c b/lib/hcrypto/test_dh.c
index 7a6c8c09a911..638d25f706db 100644
--- a/lib/hcrypto/test_dh.c
+++ b/lib/hcrypto/test_dh.c
@@ -38,11 +38,9 @@
*/
#include <config.h>
+#include <roken.h>
-#include <stdio.h>
#include <ctype.h>
-
-#include <roken.h>
#include <getarg.h>
#include <dh.h>
diff --git a/lib/hcrypto/test_engine_dso.c b/lib/hcrypto/test_engine_dso.c
index f6a38effd0d0..00a672e63e8a 100644
--- a/lib/hcrypto/test_engine_dso.c
+++ b/lib/hcrypto/test_engine_dso.c
@@ -32,9 +32,6 @@
*/
#include <config.h>
-
-#include <stdio.h>
-
#include <roken.h>
#include <getarg.h>
diff --git a/lib/hcrypto/test_hmac.c b/lib/hcrypto/test_hmac.c
index d8c3e69cd2ef..063a461cc161 100644
--- a/lib/hcrypto/test_hmac.c
+++ b/lib/hcrypto/test_hmac.c
@@ -32,16 +32,10 @@
*/
#include <config.h>
-
-#include <sys/types.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <roken.h>
#include <hmac.h>
#include <evp.h>
-#include <roken.h>
int
main(int argc, char **argv)
diff --git a/lib/hcrypto/test_pkcs12.c b/lib/hcrypto/test_pkcs12.c
index aa27af0e6ade..3c7f5da131af 100644
--- a/lib/hcrypto/test_pkcs12.c
+++ b/lib/hcrypto/test_pkcs12.c
@@ -32,12 +32,7 @@
*/
#include <config.h>
-
-#include <sys/types.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <roken.h>
#include <pkcs12.h>
#include <evp.h>
diff --git a/lib/hcrypto/test_pkcs5.c b/lib/hcrypto/test_pkcs5.c
index 46ca52ac70c6..3641a6efa262 100644
--- a/lib/hcrypto/test_pkcs5.c
+++ b/lib/hcrypto/test_pkcs5.c
@@ -32,12 +32,7 @@
*/
#include <config.h>
-
-#include <sys/types.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <roken.h>
#include <err.h>
#include <evp.h>
@@ -112,24 +107,24 @@ test_pkcs5_pbe2(const struct tests *t)
unsigned char key[32];
int ret, error = 0;
- ret = PKCS5_PBKDF2_HMAC_SHA1(t->password, strlen(t->password),
- t->salt, strlen(t->salt),
- t->iterations,
- 16, key);
+ ret = PKCS5_PBKDF2_HMAC(t->password, strlen(t->password),
+ t->salt, strlen(t->salt),
+ t->iterations, EVP_sha1(),
+ 16, key);
if (ret != 1)
- errx(1, "PKCS5_PBKDF2_HMAC_SHA1: %d", ret);
+ errx(1, "PKCS5_PBKDF2_HMAC: %d", ret);
if (memcmp(t->pbkdf2_128, key, 16) != 0) {
printf("incorrect 128 key\n");
error++;
}
- ret = PKCS5_PBKDF2_HMAC_SHA1(t->password, strlen(t->password),
- t->salt, strlen(t->salt),
- t->iterations,
- 32, key);
+ ret = PKCS5_PBKDF2_HMAC(t->password, strlen(t->password),
+ t->salt, strlen(t->salt),
+ t->iterations, EVP_sha1(),
+ 32, key);
if (ret != 1)
- errx(1, "PKCS5_PBKDF2_HMAC_SHA1: %d", ret);
+ errx(1, "PKCS5_PBKDF2_HMAC: %d", ret);
if (memcmp(t->pbkdf2_256, key, 32) != 0) {
printf("incorrect 256 key\n");
diff --git a/lib/hcrypto/test_rand.c b/lib/hcrypto/test_rand.c
index c90ed3cba0b9..b3ee2b104a63 100644
--- a/lib/hcrypto/test_rand.c
+++ b/lib/hcrypto/test_rand.c
@@ -34,10 +34,9 @@
*/
#include <config.h>
-
-#include <stdio.h>
-
#include <roken.h>
+#include <math.h>
+
#include <getarg.h>
#include "rand.h"
@@ -104,10 +103,7 @@ main(int argc, char **argv)
exit(0);
}
- argc -= idx;
- argv += idx;
-
- if (argc != 0)
+ if (argc != idx)
usage(1);
buffer = emalloc(len);
@@ -123,10 +119,6 @@ main(int argc, char **argv)
else if (strcasecmp(rand_method, "unix") == 0)
RAND_set_rand_method(RAND_unix_method());
#endif
-#ifndef NO_RAND_EGD_METHOD
- else if (strcasecmp(rand_method, "egd") == 0)
- RAND_set_rand_method(RAND_egd_method());
-#endif
#ifdef WIN32
else if (strcasecmp(rand_method, "w32crypto") == 0)
RAND_set_rand_method(RAND_w32crypto_method());
@@ -149,12 +141,20 @@ main(int argc, char **argv)
/* head vs tail */
if (len >= 100000) {
- int bit, i;
+ unsigned bytes[256];
+ unsigned bits[8];
+ size_t bit, i;
double res;
- int bits[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ double slen = sqrt((double)len);
+
+ memset(bits, 0, sizeof(bits));
+ memset(bytes, 0, sizeof(bytes));
for (i = 0; i < len; i++) {
unsigned char c = ((unsigned char *)buffer)[i];
+
+ bytes[c]++;
+
for (bit = 0; bit < 8 && c; bit++) {
if (c & 1)
bits[bit]++;
@@ -162,14 +162,44 @@ main(int argc, char **argv)
}
}
+ /*
+ * The count for each bit value has a mean of n*p = len/2,
+ * and a standard deviation of sqrt(n*p*q) ~ sqrt(len/4).
+ * Normalizing by dividing by "n*p", we get a mean of 1 and
+ * a standard deviation of sqrt(q/n*p) = 1/sqrt(len).
+ *
+ * A 5.33-sigma event happens 1 time in 10 million.
+ * A 5.73-sigma event happens 1 time in 100 million.
+ * A 6.11-sigma event happens 1 time in 1000 million.
+ *
+ * We tolerate 5.33-sigma events (we have 8 not entirely
+ * independent chances of skewed results) and want to fail
+ * with a good RNG less often than 1 time in million.
+ */
for (bit = 0; bit < 8; bit++) {
+ res = slen * fabs(1.0 - 2 * (double)bits[bit] / len);
+ if (res > 5.33)
+ errx(1, "head%d vs tail%d: %.1f-sigma (%d of %d)",
+ (int)bit, (int)bit, res, bits[bit], len);
+ printf("head vs tails bit%d: %f-sigma\n", (int)bit, res);
+ }
- res = ((double)abs(len - bits[bit] * 2)) / (double)len;
- if (res > 0.005)
- errx(1, "head%d vs tail%d > 0.5%%%% %lf == %d vs %d",
- bit, bit, res, len, bits[bit]);
-
- printf("head vs tails bit%d: %lf\n", bit, res);
+ /*
+ * The count of each byte value has a mean of n*p = len/256,
+ * and a standard deviation of sqrt(n*p*q) ~ sqrt(len/256).
+ * Normalizing by dividing by "n*p", we get a mean of 1 and
+ * a standard deviation of sqrt(q/n*p) ~ 16/sqrt(len).
+ *
+ * We tolerate 5.73-sigma events (we have 256 not entirely
+ * independent chances of skewed results). Note, for example,
+ * a 5.2-sigma event was observed in ~5,000 runs.
+ */
+ for (i = 0; i < 256; i++) {
+ res = (slen / 16) * fabs(1.0 - 256 * (double)bytes[i] / len);
+ if (res > 5.73)
+ errx(1, "byte %d: %.1f-sigma (%d of %d)",
+ (int) i, res, bytes[i], len);
+ printf("byte %d: %f-sigma\n", (int)i, res);
}
}
diff --git a/lib/hcrypto/test_rsa.c b/lib/hcrypto/test_rsa.c
index 3449d9b2a91c..54b7a66c5ba1 100644
--- a/lib/hcrypto/test_rsa.c
+++ b/lib/hcrypto/test_rsa.c
@@ -32,9 +32,6 @@
*/
#include <config.h>
-
-#include <stdio.h>
-
#include <roken.h>
#include <getarg.h>
@@ -149,7 +146,7 @@ cb_func(int a, int b, BN_GENCB *c)
}
static RSA *
-read_key(ENGINE *engine, const char *rsa_key)
+read_key(ENGINE *engine, const char *keyfile)
{
unsigned char buf[1024 * 4];
const unsigned char *p;
@@ -157,22 +154,22 @@ read_key(ENGINE *engine, const char *rsa_key)
RSA *rsa;
FILE *f;
- f = fopen(rsa_key, "rb");
+ f = fopen(keyfile, "rb");
if (f == NULL)
- err(1, "could not open file %s", rsa_key);
+ err(1, "could not open file %s", keyfile);
rk_cloexec_file(f);
size = fread(buf, 1, sizeof(buf), f);
fclose(f);
if (size == 0)
- err(1, "failed to read file %s", rsa_key);
+ err(1, "failed to read file %s", keyfile);
if (size == sizeof(buf))
- err(1, "key too long in file %s!", rsa_key);
+ err(1, "key too long in file %s!", keyfile);
p = buf;
rsa = d2i_RSAPrivateKey(NULL, &p, size);
if (rsa == NULL)
- err(1, "failed to parse key in file %s", rsa_key);
+ err(1, "failed to parse key in file %s", keyfile);
RSA_set_method(rsa, ENGINE_get_RSA(engine));
diff --git a/lib/hcrypto/ui.c b/lib/hcrypto/ui.c
index d0714fe6d553..40f47a21d0e7 100644
--- a/lib/hcrypto/ui.c
+++ b/lib/hcrypto/ui.c
@@ -32,15 +32,11 @@
*/
#include <config.h>
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include <roken.h>
#include <signal.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
-#include <roken.h>
#include <ui.h>
#ifdef HAVE_CONIO_H
diff --git a/lib/hcrypto/undef.h b/lib/hcrypto/undef.h
new file mode 100644
index 000000000000..512b8c3f02e8
--- /dev/null
+++ b/lib/hcrypto/undef.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef HEIM_UNDEF_H
+#define HEIM_UNDEF_H 1
+
+#undef BIGNUM
+#undef BN_CTX
+#undef BN_BLINDING
+#undef BN_MONT_CTX
+#undef BN_GENCB
+#undef DH
+#undef DH_METHOD
+#undef DSA
+#undef DSA_METHOD
+#undef RSA
+#undef RSA_METHOD
+#undef RAND_METHOD
+#undef ENGINE
+#undef BN_GENCB_call
+#undef BN_GENCB_set
+#undef BN_CTX_new
+#undef BN_CTX_free
+#undef BN_CTX_start
+#undef BN_CTX_get
+#undef BN_CTX_end
+#undef BN_is_negative
+#undef BN_rand
+#undef BN_num_bits
+#undef BN_num_bytes
+#undef BN_new
+#undef BN_clear_free
+#undef BN_bin2bn
+#undef BN_bn2bin
+#undef BN_uadd
+#undef BN_set_negative
+#undef BN_set_word
+#undef BN_get_word
+#undef BN_cmp
+#undef BN_free
+#undef BN_is_bit_set
+#undef BN_clear
+#undef BN_dup
+#undef BN_set_bit
+#undef BN_clear_bit
+#undef BN_bn2hex
+#undef BN_hex2bn
+#undef EVP_CIPHER_CTX_block_size
+#undef EVP_CIPHER_CTX_cipher
+#undef EVP_CIPHER_CTX_cleanup
+#undef EVP_CIPHER_CTX_flags
+#undef EVP_CIPHER_CTX_get_app_data
+#undef EVP_CIPHER_CTX_init
+#undef EVP_CIPHER_CTX_iv_length
+#undef EVP_CIPHER_CTX_key_length
+#undef EVP_CIPHER_CTX_mode
+#undef EVP_CIPHER_CTX_set_app_data
+#undef EVP_CIPHER_CTX_set_key_length
+#undef EVP_CIPHER_CTX_set_padding
+#undef EVP_CIPHER_block_size
+#undef EVP_CIPHER_iv_length
+#undef EVP_CIPHER_key_length
+#undef EVP_Cipher
+#undef EVP_CipherInit_ex
+#undef EVP_CipherUpdate
+#undef EVP_CipherFinal_ex
+#undef EVP_Digest
+#undef EVP_DigestFinal_ex
+#undef EVP_DigestInit_ex
+#undef EVP_DigestUpdate
+#undef EVP_MD_CTX_block_size
+#undef EVP_MD_CTX_cleanup
+#undef EVP_MD_CTX_create
+#undef EVP_MD_CTX_init
+#undef EVP_MD_CTX_destroy
+#undef EVP_MD_CTX_md
+#undef EVP_MD_CTX_size
+#undef EVP_MD_block_size
+#undef EVP_MD_size
+#undef EVP_aes_128_cbc
+#undef EVP_aes_192_cbc
+#undef EVP_aes_256_cbc
+#undef EVP_aes_128_cfb8
+#undef EVP_aes_192_cfb8
+#undef EVP_aes_256_cfb8
+#undef EVP_des_cbc
+#undef EVP_des_ede3_cbc
+#undef EVP_enc_null
+#undef EVP_md2
+#undef EVP_md4
+#undef EVP_md5
+#undef EVP_md_null
+#undef EVP_rc2_40_cbc
+#undef EVP_rc2_64_cbc
+#undef EVP_rc2_cbc
+#undef EVP_rc4
+#undef EVP_rc4_40
+#undef EVP_camellia_128_cbc
+#undef EVP_camellia_192_cbc
+#undef EVP_camellia_256_cbc
+#undef EVP_sha
+#undef EVP_sha1
+#undef EVP_sha256
+#undef EVP_sha384
+#undef EVP_sha512
+#undef PKCS5_PBKDF2_HMAC
+#undef PKCS5_PBKDF2_HMAC_SHA1
+#undef EVP_BytesToKey
+#undef EVP_get_cipherbyname
+#undef OpenSSL_add_all_algorithms
+#undef OpenSSL_add_all_algorithms_conf
+#undef OpenSSL_add_all_algorithms_noconf
+#undef EVP_CIPHER_CTX_ctrl
+#undef EVP_CIPHER_CTX_rand_key
+#undef hcrypto_validate
+#undef EVP_MD_CTX
+#undef EVP_PKEY
+#undef EVP_MD
+#undef EVP_CIPHER
+#undef EVP_CIPHER_CTX
+#undef EVP_CIPH_STREAM_CIPHER
+#undef EVP_CIPH_CBC_MODE
+#undef EVP_CIPH_CFB8_MODE
+#undef EVP_CIPH_MODE
+#undef EVP_CIPH_CTRL_INIT
+#undef EVP_CTRL_INIT
+#undef EVP_CIPH_VARIABLE_LENGTH
+#undef EVP_CIPH_ALWAYS_CALL_INIT
+#undef EVP_CIPH_RAND_KEY
+#undef EVP_CTRL_RAND_KEY
+#undef NID_md2
+#undef NID_md4
+#undef NID_md5
+#undef NID_sha1
+#undef NID_sha256
+#undef NID_sha384
+#undef NID_sha512
+
+#endif /* HEIM_UNDEF_H */
diff --git a/lib/hcrypto/validate.c b/lib/hcrypto/validate.c
index 6f61cc53b307..562e5aa4dd02 100644
--- a/lib/hcrypto/validate.c
+++ b/lib/hcrypto/validate.c
@@ -32,12 +32,6 @@
*/
#include <config.h>
-
-#include <sys/types.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <roken.h>
#include <evp.h>
@@ -66,7 +60,8 @@ static struct tests hc_tests[] = {
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
16,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
- "\xdc\x95\xc0\x78\xa2\x40\x89\x89\xad\x48\xa2\x14\x92\x84\x20\x87"
+ "\xdc\x95\xc0\x78\xa2\x40\x89\x89\xad\x48\xa2\x14\x92\x84\x20\x87",
+ NULL
},
#if 0
{
@@ -90,7 +85,8 @@ static struct tests hc_tests[] = {
"\xbf\x9a\x12\xb7\x26\x69\xfd\x05",
16,
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
- "\x55\x95\x97\x76\xa9\x6c\x66\x40\x64\xc7\xf4\x1c\x21\xb7\x14\x1b"
+ "\x55\x95\x97\x76\xa9\x6c\x66\x40\x64\xc7\xf4\x1c\x21\xb7\x14\x1b",
+ NULL
},
#if 0
{
@@ -211,7 +207,8 @@ static struct tests hc_tests[] = {
"\x89\x21\xc2\xf5\xa4\x63\x93\x8c"
"\xe0\x98\x22\x65\xee\xf7\x01\x79"
"\xbc\x55\x3f\x33\x9e\xb1\xa4\xc1"
- "\xaf\x5f\x6a\x54\x7f"
+ "\xaf\x5f\x6a\x54\x7f",
+ NULL
}
};
@@ -253,8 +250,10 @@ test_cipher(struct tests *t)
if (memcmp(d, t->indata, t->datasize) != 0)
errx(1, "%s: decrypt not the same", t->name);
+#if 0
if (t->outiv)
/* XXXX check */;
+#endif
EVP_CIPHER_CTX_cleanup(&ectx);
EVP_CIPHER_CTX_cleanup(&dctx);
diff --git a/lib/hcrypto/version-script.map b/lib/hcrypto/version-script.map
index 6985d1b9e226..5b9d34a459b3 100644
--- a/lib/hcrypto/version-script.map
+++ b/lib/hcrypto/version-script.map
@@ -174,6 +174,8 @@ HEIMDAL_CRYPTO_1.0 {
hc_EVP_cc_md5;
hc_EVP_cc_sha1;
hc_EVP_cc_sha256;
+ hc_EVP_cc_sha384;
+ hc_EVP_cc_sha512;
hc_EVP_cc_des_ede3_cbc;
hc_EVP_cc_aes_128_cbc;
hc_EVP_cc_aes_192_cbc;
@@ -199,6 +201,43 @@ HEIMDAL_CRYPTO_1.0 {
hc_EVP_hcrypto_rc4;
hc_EVP_hcrypto_rc4_40;
+ hc_EVP_ossl_md2;
+ hc_EVP_ossl_md4;
+ hc_EVP_ossl_md5;
+ hc_EVP_ossl_sha1;
+ hc_EVP_ossl_sha256;
+ hc_EVP_ossl_sha384;
+ hc_EVP_ossl_sha512;
+ hc_EVP_ossl_des_ede3_cbc;
+ hc_EVP_ossl_aes_128_cbc;
+ hc_EVP_ossl_aes_192_cbc;
+ hc_EVP_ossl_aes_256_cbc;
+ hc_EVP_ossl_aes_128_cfb8;
+ hc_EVP_ossl_aes_192_cfb8;
+ hc_EVP_ossl_aes_256_cfb8;
+ hc_EVP_ossl_rc2_cbc;
+ hc_EVP_ossl_rc2_40_cbc;
+ hc_EVP_ossl_rc4;
+ hc_EVP_ossl_rc4_40;
+
+ hc_EVP_pkcs11_md2;
+ hc_EVP_pkcs11_md4;
+ hc_EVP_pkcs11_md5;
+ hc_EVP_pkcs11_sha1;
+ hc_EVP_pkcs11_sha256;
+ hc_EVP_pkcs11_des_ede3_cbc;
+ hc_EVP_pkcs11_aes_128_cbc;
+ hc_EVP_pkcs11_aes_192_cbc;
+ hc_EVP_pkcs11_aes_256_cbc;
+ hc_EVP_pkcs11_aes_128_cfb8;
+ hc_EVP_pkcs11_aes_192_cfb8;
+ hc_EVP_pkcs11_aes_256_cfb8;
+ hc_EVP_pkcs11_rc2_40_cbc;
+ hc_EVP_pkcs11_rc2_64_cbc;
+ hc_EVP_pkcs11_rc2_cbc;
+ hc_EVP_pkcs11_rc4;
+ hc_EVP_pkcs11_rc4_40;
+
hc_EVP_hcrypto_aes_128_cts;
hc_EVP_hcrypto_aes_192_cts;
hc_EVP_hcrypto_aes_256_cts;
@@ -222,13 +261,11 @@ HEIMDAL_CRYPTO_1.0 {
hc_OpenSSL_add_all_algorithms_conf;
hc_OpenSSL_add_all_algorithms_noconf;
hc_PKCS12_key_gen;
+ hc_PKCS5_PBKDF2_HMAC;
hc_PKCS5_PBKDF2_HMAC_SHA1;
hc_RAND_add;
hc_RAND_bytes;
hc_RAND_cleanup;
- hc_RAND_egd;
- hc_RAND_egd_bytes;
- hc_RAND_egd_method;
hc_RAND_file_name;
hc_RAND_fortuna_method;
hc_RAND_get_rand_method;
@@ -289,6 +326,7 @@ HEIMDAL_CRYPTO_1.0 {
hc_EVP_CIPHER_CTX_ctrl;
hc_EVP_CIPHER_CTX_rand_key;
hc_EVP_CIPHER_CTX_set_key_length;
+ hc_EVP_hcrypto_rc2_cbc;
hc_EVP_hcrypto_rc2_40_cbc;
hc_EVP_hcrypto_camellia_128_cbc;
hc_EVP_CipherUpdate;
diff --git a/lib/hdb/Makefile.am b/lib/hdb/Makefile.am
index b629f56258d2..448b0242f3e7 100644
--- a/lib/hdb/Makefile.am
+++ b/lib/hdb/Makefile.am
@@ -2,7 +2,7 @@
include $(top_srcdir)/Makefile.am.common
-AM_CPPFLAGS += -I../asn1 -I$(srcdir)/../asn1 $(INCLUDE_hcrypto)
+AM_CPPFLAGS += -I../asn1 -I$(srcdir)/../asn1
AM_CPPFLAGS += $(INCLUDE_openldap) -DHDB_DB_DIR=\"$(DIR_hdbdir)\"
AM_CPPFLAGS += -I$(srcdir)/../krb5
AM_CPPFLAGS += $(INCLUDE_sqlite3)
@@ -29,22 +29,24 @@ gen_files_hdb = \
asn1_HDB_Ext_Lan_Manager_OWF.x \
asn1_HDB_Ext_Password.x \
asn1_HDB_Ext_Aliases.x \
+ asn1_HDB_Ext_KeySet.x \
asn1_HDB_extension.x \
asn1_HDB_extensions.x \
asn1_hdb_entry.x \
asn1_hdb_entry_alias.x \
- asn1_hdb_keyset.x
+ asn1_hdb_keyset.x \
+ asn1_Keys.x
CLEANFILES = $(BUILT_SOURCES) $(gen_files_hdb) \
- hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.c*
+ hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.[cx]
LDADD = libhdb.la \
- $(LIB_openldap) \
- $(LIB_libintl) \
../krb5/libkrb5.la \
../asn1/libasn1.la \
$(LIB_hcrypto) \
$(LIB_roken) \
+ $(LIB_openldap) \
+ $(LIB_libintl) \
$(LIB_ldopen)
@@ -70,7 +72,7 @@ if versionscript
libhdb_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
endif
-noinst_PROGRAMS = test_dbinfo test_hdbkeys test_mkey
+noinst_PROGRAMS = test_dbinfo test_hdbkeys test_mkey test_hdbplugin
dist_libhdb_la_SOURCES = \
common.c \
@@ -81,9 +83,9 @@ dist_libhdb_la_SOURCES = \
hdb.c \
hdb-sqlite.c \
hdb-keytab.c \
+ hdb-mdb.c \
hdb-mitdb.c \
hdb_locl.h \
- hdb-private.h \
keys.c \
keytab.c \
dbinfo.c \
@@ -95,9 +97,11 @@ nodist_libhdb_la_SOURCES = $(BUILT_SOURCES)
libhdb_la_DEPENDENCIES = version-script.map
-include_HEADERS = hdb.h hdb-protos.h
+include_HEADERS = hdb.h $(srcdir)/hdb-protos.h
nodist_include_HEADERS = hdb_err.h hdb_asn1.h
+noinst_HEADERS = $(srcdir)/hdb-private.h
+
libhdb_la_LIBADD = \
$(LIB_com_err) \
../krb5/libkrb5.la \
@@ -106,27 +110,34 @@ libhdb_la_LIBADD = \
$(LIBADD_roken) \
$(ldap_lib) \
$(LIB_dlopen) \
- $(DBLIB) \
- $(LIB_NDBM)
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB)
+
+HDB_PROTOS = $(srcdir)/hdb-protos.h $(srcdir)/hdb-private.h
+
+ALL_OBJECTS = $(libhdb_la_OBJECTS)
+ALL_OBJECTS += $(test_dbinfo_OBJECTS)
+ALL_OBJECTS += $(test_hdbkeys_OBJECTS)
+ALL_OBJECTS += $(test_mkey_OBJECTS)
+ALL_OBJECTS += $(test_hdbplugin_OBJECTS)
-$(libhdb_la_OBJECTS): $(srcdir)/hdb-protos.h $(srcdir)/hdb-private.h
-$(libhdb_la_OBJECTS): hdb_asn1.h hdb_asn1-priv.h hdb_err.h
+$(ALL_OBJECTS): $(HDB_PROTOS) hdb_asn1.h hdb_asn1-priv.h hdb_err.h
-$(srcdir)/hdb-protos.h:
+$(srcdir)/hdb-protos.h: $(dist_libhdb_la_SOURCES)
cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -o hdb-protos.h $(dist_libhdb_la_SOURCES) || rm -f hdb-protos.h
-$(srcdir)/hdb-private.h:
+$(srcdir)/hdb-private.h: $(dist_libhdb_la_SOURCES)
cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -p hdb-private.h $(dist_libhdb_la_SOURCES) || rm -f hdb-private.h
$(gen_files_hdb) hdb_asn1.hx hdb_asn1-priv.hx: hdb_asn1_files
hdb_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/hdb.asn1
- $(ASN1_COMPILE) $(srcdir)/hdb.asn1 hdb_asn1
+ $(ASN1_COMPILE) --sequence=HDB-Ext-KeySet --sequence=Keys $(srcdir)/hdb.asn1 hdb_asn1
test_dbinfo_LIBS = libhdb.la
test_hdbkeys_LIBS = ../krb5/libkrb5.la libhdb.la
test_mkey_LIBS = $(test_hdbkeys_LIBS)
+test_hdbplugin_LIBS = $(test_hdbkeys_LIBS)
# to help stupid solaris make
diff --git a/lib/hdb/Makefile.in b/lib/hdb/Makefile.in
index 9fcd77000c47..065c4fc1ae5e 100644
--- a/lib/hdb/Makefile.in
+++ b/lib/hdb/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,13 +96,10 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common
@HAVE_DBHEADER_TRUE@am__append_1 = -I$(DBHEADER)
@versionscript_TRUE@am__append_2 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
noinst_PROGRAMS = test_dbinfo$(EXEEXT) test_hdbkeys$(EXEEXT) \
- test_mkey$(EXEEXT)
+ test_mkey$(EXEEXT) test_hdbplugin$(EXEEXT)
subdir = lib/hdb
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -64,8 +115,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -78,6 +128,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -97,6 +148,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(noinst_HEADERS) $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -122,6 +175,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" \
"$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
@@ -131,17 +190,21 @@ am__DEPENDENCIES_1 =
am__hdb_ldap_la_SOURCES_DIST = hdb-ldap.c
@OPENLDAP_MODULE_TRUE@am_hdb_ldap_la_OBJECTS = hdb-ldap.lo
hdb_ldap_la_OBJECTS = $(am_hdb_ldap_la_OBJECTS)
-hdb_ldap_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+hdb_ldap_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(hdb_ldap_la_LDFLAGS) $(LDFLAGS) -o $@
@OPENLDAP_MODULE_TRUE@am_hdb_ldap_la_rpath = -rpath $(libdir)
@OPENLDAP_MODULE_FALSE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
am__dist_libhdb_la_SOURCES_DIST = common.c db.c db3.c ext.c hdb-ldap.c \
- hdb.c hdb-sqlite.c hdb-keytab.c hdb-mitdb.c hdb_locl.h \
- hdb-private.h keys.c keytab.c dbinfo.c mkey.c ndbm.c print.c
+ hdb.c hdb-sqlite.c hdb-keytab.c hdb-mdb.c hdb-mitdb.c \
+ hdb_locl.h keys.c keytab.c dbinfo.c mkey.c ndbm.c print.c
@OPENLDAP_MODULE_FALSE@am__objects_1 = hdb-ldap.lo
dist_libhdb_la_OBJECTS = common.lo db.lo db3.lo ext.lo \
- $(am__objects_1) hdb.lo hdb-sqlite.lo hdb-keytab.lo \
+ $(am__objects_1) hdb.lo hdb-sqlite.lo hdb-keytab.lo hdb-mdb.lo \
hdb-mitdb.lo keys.lo keytab.lo dbinfo.lo mkey.lo ndbm.lo \
print.lo
am__objects_2 = asn1_Salt.lo asn1_Key.lo asn1_Event.lo \
@@ -149,61 +212,116 @@ am__objects_2 = asn1_Salt.lo asn1_Key.lo asn1_Event.lo \
asn1_HDB_Ext_PKINIT_cert.lo asn1_HDB_Ext_PKINIT_hash.lo \
asn1_HDB_Ext_Constrained_delegation_acl.lo \
asn1_HDB_Ext_Lan_Manager_OWF.lo asn1_HDB_Ext_Password.lo \
- asn1_HDB_Ext_Aliases.lo asn1_HDB_extension.lo \
- asn1_HDB_extensions.lo asn1_hdb_entry.lo \
- asn1_hdb_entry_alias.lo asn1_hdb_keyset.lo
+ asn1_HDB_Ext_Aliases.lo asn1_HDB_Ext_KeySet.lo \
+ asn1_HDB_extension.lo asn1_HDB_extensions.lo asn1_hdb_entry.lo \
+ asn1_hdb_entry_alias.lo asn1_hdb_keyset.lo asn1_Keys.lo
am__objects_3 = $(am__objects_2) hdb_err.lo
nodist_libhdb_la_OBJECTS = $(am__objects_3)
libhdb_la_OBJECTS = $(dist_libhdb_la_OBJECTS) \
$(nodist_libhdb_la_OBJECTS)
-libhdb_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+libhdb_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libhdb_la_LDFLAGS) $(LDFLAGS) -o $@
PROGRAMS = $(noinst_PROGRAMS)
test_dbinfo_SOURCES = test_dbinfo.c
test_dbinfo_OBJECTS = test_dbinfo.$(OBJEXT)
test_dbinfo_LDADD = $(LDADD)
-test_dbinfo_DEPENDENCIES = libhdb.la $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) ../krb5/libkrb5.la ../asn1/libasn1.la \
+test_dbinfo_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \
+ ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
test_hdbkeys_SOURCES = test_hdbkeys.c
test_hdbkeys_OBJECTS = test_hdbkeys.$(OBJEXT)
test_hdbkeys_LDADD = $(LDADD)
-test_hdbkeys_DEPENDENCIES = libhdb.la $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) ../krb5/libkrb5.la ../asn1/libasn1.la \
+test_hdbkeys_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \
+ ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+test_hdbplugin_SOURCES = test_hdbplugin.c
+test_hdbplugin_OBJECTS = test_hdbplugin.$(OBJEXT)
+test_hdbplugin_LDADD = $(LDADD)
+test_hdbplugin_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \
+ ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
test_mkey_SOURCES = test_mkey.c
test_mkey_OBJECTS = test_mkey.$(OBJEXT)
test_mkey_LDADD = $(LDADD)
-test_mkey_DEPENDENCIES = libhdb.la $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) ../krb5/libkrb5.la ../asn1/libasn1.la \
+test_mkey_DEPENDENCIES = libhdb.la ../krb5/libkrb5.la \
+ ../asn1/libasn1.la $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(hdb_ldap_la_SOURCES) $(dist_libhdb_la_SOURCES) \
$(nodist_libhdb_la_SOURCES) test_dbinfo.c test_hdbkeys.c \
- test_mkey.c
+ test_hdbplugin.c test_mkey.c
DIST_SOURCES = $(am__hdb_ldap_la_SOURCES_DIST) \
$(am__dist_libhdb_la_SOURCES_DIST) test_dbinfo.c \
- test_hdbkeys.c test_mkey.c
-HEADERS = $(include_HEADERS) $(nodist_include_HEADERS)
+ test_hdbkeys.c test_hdbplugin.c test_mkey.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(include_HEADERS) $(nodist_include_HEADERS) \
+ $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -222,12 +340,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -237,17 +355,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -266,12 +384,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -280,6 +395,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -296,10 +412,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -307,6 +421,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -321,12 +436,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -343,6 +461,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -357,13 +476,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -387,6 +500,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -422,32 +537,40 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken) -I../asn1 -I$(srcdir)/../asn1 \
- $(INCLUDE_hcrypto) $(INCLUDE_openldap) \
- -DHDB_DB_DIR=\"$(DIR_hdbdir)\" -I$(srcdir)/../krb5 \
- $(INCLUDE_sqlite3) $(INCLUDE_libintl) $(am__append_1)
+ $(INCLUDE_openldap) -DHDB_DB_DIR=\"$(DIR_hdbdir)\" \
+ -I$(srcdir)/../krb5 $(INCLUDE_sqlite3) $(INCLUDE_libintl) \
+ $(am__append_1)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
BUILT_SOURCES = \
$(gen_files_hdb:.x=.c) \
hdb_err.c \
@@ -466,22 +589,24 @@ gen_files_hdb = \
asn1_HDB_Ext_Lan_Manager_OWF.x \
asn1_HDB_Ext_Password.x \
asn1_HDB_Ext_Aliases.x \
+ asn1_HDB_Ext_KeySet.x \
asn1_HDB_extension.x \
asn1_HDB_extensions.x \
asn1_hdb_entry.x \
asn1_hdb_entry_alias.x \
- asn1_hdb_keyset.x
+ asn1_hdb_keyset.x \
+ asn1_Keys.x
CLEANFILES = $(BUILT_SOURCES) $(gen_files_hdb) \
- hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.c*
+ hdb_asn1{,-priv}.h* hdb_asn1_files hdb_asn1-template.[cx]
LDADD = libhdb.la \
- $(LIB_openldap) \
- $(LIB_libintl) \
../krb5/libkrb5.la \
../asn1/libasn1.la \
$(LIB_hcrypto) \
$(LIB_roken) \
+ $(LIB_openldap) \
+ $(LIB_libintl) \
$(LIB_ldopen)
@OPENLDAP_MODULE_TRUE@ldap_so = hdb_ldap.la
@@ -501,9 +626,9 @@ dist_libhdb_la_SOURCES = \
hdb.c \
hdb-sqlite.c \
hdb-keytab.c \
+ hdb-mdb.c \
hdb-mitdb.c \
hdb_locl.h \
- hdb-private.h \
keys.c \
keytab.c \
dbinfo.c \
@@ -513,8 +638,9 @@ dist_libhdb_la_SOURCES = \
nodist_libhdb_la_SOURCES = $(BUILT_SOURCES)
libhdb_la_DEPENDENCIES = version-script.map
-include_HEADERS = hdb.h hdb-protos.h
+include_HEADERS = hdb.h $(srcdir)/hdb-protos.h
nodist_include_HEADERS = hdb_err.h hdb_asn1.h
+noinst_HEADERS = $(srcdir)/hdb-private.h
libhdb_la_LIBADD = \
$(LIB_com_err) \
../krb5/libkrb5.la \
@@ -523,12 +649,16 @@ libhdb_la_LIBADD = \
$(LIBADD_roken) \
$(ldap_lib) \
$(LIB_dlopen) \
- $(DBLIB) \
- $(LIB_NDBM)
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB)
+HDB_PROTOS = $(srcdir)/hdb-protos.h $(srcdir)/hdb-private.h
+ALL_OBJECTS = $(libhdb_la_OBJECTS) $(test_dbinfo_OBJECTS) \
+ $(test_hdbkeys_OBJECTS) $(test_mkey_OBJECTS) \
+ $(test_hdbplugin_OBJECTS)
test_dbinfo_LIBS = libhdb.la
test_hdbkeys_LIBS = ../krb5/libkrb5.la libhdb.la
test_mkey_LIBS = $(test_hdbkeys_LIBS)
+test_hdbplugin_LIBS = $(test_hdbkeys_LIBS)
EXTRA_DIST = \
NTMakefile \
libhdb-version.rc \
@@ -544,7 +674,7 @@ all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -557,7 +687,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/hdb/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/hdb/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -566,6 +695,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -575,9 +705,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -585,6 +715,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -600,16 +732,20 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-hdb_ldap.la: $(hdb_ldap_la_OBJECTS) $(hdb_ldap_la_DEPENDENCIES)
- $(hdb_ldap_la_LINK) $(am_hdb_ldap_la_rpath) $(hdb_ldap_la_OBJECTS) $(hdb_ldap_la_LIBADD) $(LIBS)
-libhdb.la: $(libhdb_la_OBJECTS) $(libhdb_la_DEPENDENCIES)
- $(libhdb_la_LINK) -rpath $(libdir) $(libhdb_la_OBJECTS) $(libhdb_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+hdb_ldap.la: $(hdb_ldap_la_OBJECTS) $(hdb_ldap_la_DEPENDENCIES) $(EXTRA_hdb_ldap_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(hdb_ldap_la_LINK) $(am_hdb_ldap_la_rpath) $(hdb_ldap_la_OBJECTS) $(hdb_ldap_la_LIBADD) $(LIBS)
+
+libhdb.la: $(libhdb_la_OBJECTS) $(libhdb_la_DEPENDENCIES) $(EXTRA_libhdb_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libhdb_la_LINK) -rpath $(libdir) $(libhdb_la_OBJECTS) $(libhdb_la_LIBADD) $(LIBS)
clean-noinstPROGRAMS:
@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -619,15 +755,22 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-test_dbinfo$(EXEEXT): $(test_dbinfo_OBJECTS) $(test_dbinfo_DEPENDENCIES)
+
+test_dbinfo$(EXEEXT): $(test_dbinfo_OBJECTS) $(test_dbinfo_DEPENDENCIES) $(EXTRA_test_dbinfo_DEPENDENCIES)
@rm -f test_dbinfo$(EXEEXT)
- $(LINK) $(test_dbinfo_OBJECTS) $(test_dbinfo_LDADD) $(LIBS)
-test_hdbkeys$(EXEEXT): $(test_hdbkeys_OBJECTS) $(test_hdbkeys_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_dbinfo_OBJECTS) $(test_dbinfo_LDADD) $(LIBS)
+
+test_hdbkeys$(EXEEXT): $(test_hdbkeys_OBJECTS) $(test_hdbkeys_DEPENDENCIES) $(EXTRA_test_hdbkeys_DEPENDENCIES)
@rm -f test_hdbkeys$(EXEEXT)
- $(LINK) $(test_hdbkeys_OBJECTS) $(test_hdbkeys_LDADD) $(LIBS)
-test_mkey$(EXEEXT): $(test_mkey_OBJECTS) $(test_mkey_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_hdbkeys_OBJECTS) $(test_hdbkeys_LDADD) $(LIBS)
+
+test_hdbplugin$(EXEEXT): $(test_hdbplugin_OBJECTS) $(test_hdbplugin_DEPENDENCIES) $(EXTRA_test_hdbplugin_DEPENDENCIES)
+ @rm -f test_hdbplugin$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_hdbplugin_OBJECTS) $(test_hdbplugin_LDADD) $(LIBS)
+
+test_mkey$(EXEEXT): $(test_mkey_OBJECTS) $(test_mkey_DEPENDENCIES) $(EXTRA_test_mkey_DEPENDENCIES)
@rm -f test_mkey$(EXEEXT)
- $(LINK) $(test_mkey_OBJECTS) $(test_mkey_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_mkey_OBJECTS) $(test_mkey_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -640,6 +783,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDBFlags.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_Aliases.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_Constrained_delegation_acl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_KeySet.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_Lan_Manager_OWF.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_PKINIT_acl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_Ext_PKINIT_cert.Plo@am__quote@
@@ -648,6 +792,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_extension.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_HDB_extensions.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_Key.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_Keys.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_Salt.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_hdb_entry.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_hdb_entry_alias.Plo@am__quote@
@@ -659,6 +804,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ext.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-keytab.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-ldap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-mdb.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-mitdb.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb-sqlite.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hdb.Plo@am__quote@
@@ -670,28 +816,29 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_dbinfo.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_hdbkeys.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_hdbplugin.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_mkey.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -700,8 +847,11 @@ clean-libtool:
-rm -rf .libs _libs
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -715,13 +865,14 @@ uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_includeHEADERS: $(nodist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -735,30 +886,17 @@ uninstall-nodist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -770,15 +908,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -787,6 +921,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -844,10 +993,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -891,9 +1045,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -937,42 +1090,56 @@ uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
.MAKE: all check check-am install install-am install-data-am \
- install-exec-am install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-generic clean-libLTLIBRARIES clean-libtool \
- clean-noinstPROGRAMS ctags dist-hook distclean \
- distclean-compile distclean-generic distclean-libtool \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am \
- install-data-hook install-dvi install-dvi-am install-exec \
- install-exec-am install-exec-hook install-html install-html-am \
- install-includeHEADERS install-info install-info-am \
- install-libLTLIBRARIES install-man \
- install-nodist_includeHEADERS install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
+ install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstPROGRAMS cscopelist-am ctags \
+ ctags-am dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-includeHEADERS \
+ install-info install-info-am install-libLTLIBRARIES \
+ install-man install-nodist_includeHEADERS install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-hook \
+ tags tags-am uninstall uninstall-am uninstall-hook \
uninstall-includeHEADERS uninstall-libLTLIBRARIES \
uninstall-nodist_includeHEADERS
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
-install-exec-hook: install-suid-programs
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -980,7 +1147,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -1037,6 +1204,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1079,6 +1248,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1092,13 +1274,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -1129,19 +1311,18 @@ distdir-in-tree: $(DISTFILES) $(INFO_DEPS)
fi ; \
done
-$(libhdb_la_OBJECTS): $(srcdir)/hdb-protos.h $(srcdir)/hdb-private.h
-$(libhdb_la_OBJECTS): hdb_asn1.h hdb_asn1-priv.h hdb_err.h
+$(ALL_OBJECTS): $(HDB_PROTOS) hdb_asn1.h hdb_asn1-priv.h hdb_err.h
-$(srcdir)/hdb-protos.h:
+$(srcdir)/hdb-protos.h: $(dist_libhdb_la_SOURCES)
cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -o hdb-protos.h $(dist_libhdb_la_SOURCES) || rm -f hdb-protos.h
-$(srcdir)/hdb-private.h:
+$(srcdir)/hdb-private.h: $(dist_libhdb_la_SOURCES)
cd $(srcdir); perl ../../cf/make-proto.pl -q -P comment -p hdb-private.h $(dist_libhdb_la_SOURCES) || rm -f hdb-private.h
$(gen_files_hdb) hdb_asn1.hx hdb_asn1-priv.hx: hdb_asn1_files
hdb_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/hdb.asn1
- $(ASN1_COMPILE) $(srcdir)/hdb.asn1 hdb_asn1
+ $(ASN1_COMPILE) --sequence=HDB-Ext-KeySet --sequence=Keys $(srcdir)/hdb.asn1 hdb_asn1
# to help stupid solaris make
diff --git a/lib/hdb/NTMakefile b/lib/hdb/NTMakefile
index ef696fb3a93c..01671b07805c 100644
--- a/lib/hdb/NTMakefile
+++ b/lib/hdb/NTMakefile
@@ -37,7 +37,7 @@ gen_files_hdb = $(OBJ)\asn1_hdb_asn1.x
$(gen_files_hdb) $(OBJ)\hdb_asn1.hx $(OBJ)\hdb_asn1-priv.hx: $(BINDIR)\asn1_compile.exe hdb.asn1
cd $(OBJ)
- $(BINDIR)\asn1_compile.exe --sequence=HDB-Ext-Keyset --sequence=Keys --one-code-file $(SRCDIR)\hdb.asn1 hdb_asn1
+ $(BINDIR)\asn1_compile.exe --sequence=HDB-Ext-KeySet --sequence=Keys --one-code-file $(SRCDIR)\hdb.asn1 hdb_asn1
cd $(SRCDIR)
$(gen_files_hdb:.x=.c): $$(@R).x
@@ -73,6 +73,7 @@ dist_libhdb_la_SOURCES = \
hdb-sqlite.c \
hdb-keytab.c \
hdb-mitdb.c \
+ hdb-mdb.c \
hdb_locl.h \
keys.c \
keytab.c \
@@ -127,9 +128,9 @@ RES=$(OBJ)\libhdb-version.res
$(LIBHDB): $(BINDIR)\libhdb.dll
-$(BINDIR)\libhdb.dll: $(libhdb_OBJs) $(ldap_lib) $(LIBHEIMDAL) $(LIBSQLITE) $(LIBCOMERR) $(LIBROKEN) $(RES)
+$(BINDIR)\libhdb.dll: $(libhdb_OBJs) $(ldap_lib) $(LIBHEIMBASE) $(LIBHEIMDAL) $(LIBSQLITE) $(LIBCOMERR) $(LIBROKEN) $(RES)
$(DLLGUILINK) -def:libhdb-exports.def -implib:$(LIBHDB)
- $(DLLPREP)
+ $(DLLPREP_NODIST)
clean::
-$(RM) $(BINDIR)\libhdb.*
@@ -149,7 +150,7 @@ clean::
test:: test-binaries test-run
-test-binaries: $(OBJ)\test_dbinfo.exe $(OBJ)\test_hdbkeys.exe
+test-binaries: $(OBJ)\test_dbinfo.exe $(OBJ)\test_hdbkeys.exe $(OBJ)\test_hdbplugin.exe
$(OBJ)\test_dbinfo.exe: $(OBJ)\test_dbinfo.obj $(LIBHDB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS)
$(EXECONLINK)
@@ -159,9 +160,15 @@ $(OBJ)\test_hdbkeys.exe: $(OBJ)\test_hdbkeys.obj $(LIBHDB) $(LIBHEIMDAL) $(LIBRO
$(EXECONLINK)
$(EXEPREP_NODIST)
+$(OBJ)\test_hdbplugin.exe: $(OBJ)\test_hdbplugin.obj $(LIBHDB) $(LIBHEIMDAL) $(LIBROKEN) $(LIBVERS)
+ $(EXECONLINK)
+ $(EXEPREP_NODIST)
+
test-run:
cd $(OBJ)
- test_dbinfo.exe
+ -test_dbinfo.exe
+ -test_hdbkeys.exe
+ -test_hdbplugin.exe
cd $(SRCDIR)
!ifdef OPENLDAP_INC
diff --git a/lib/hdb/common.c b/lib/hdb/common.c
index 2715adf63dca..2c8bb9f305e0 100644
--- a/lib/hdb/common.c
+++ b/lib/hdb/common.c
@@ -105,7 +105,6 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
krb5_principal enterprise_principal = NULL;
krb5_data key, value;
krb5_error_code ret;
- int code;
if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
if (principal->name.name_string.len != 1) {
@@ -125,43 +124,74 @@ _hdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
hdb_principal2key(context, principal, &key);
if (enterprise_principal)
krb5_free_principal(context, enterprise_principal);
- code = db->hdb__get(context, db, key, &value);
+ ret = db->hdb__get(context, db, key, &value);
krb5_data_free(&key);
- if(code)
- return code;
- code = hdb_value2entry(context, &value, &entry->entry);
- if (code == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
+ if(ret)
+ return ret;
+ ret = hdb_value2entry(context, &value, &entry->entry);
+ if (ret == ASN1_BAD_ID && (flags & HDB_F_CANON) == 0) {
krb5_data_free(&value);
return HDB_ERR_NOENTRY;
- } else if (code == ASN1_BAD_ID) {
+ } else if (ret == ASN1_BAD_ID) {
hdb_entry_alias alias;
- code = hdb_value2entry_alias(context, &value, &alias);
- if (code) {
+ ret = hdb_value2entry_alias(context, &value, &alias);
+ if (ret) {
krb5_data_free(&value);
- return code;
+ return ret;
}
hdb_principal2key(context, alias.principal, &key);
krb5_data_free(&value);
free_hdb_entry_alias(&alias);
- code = db->hdb__get(context, db, key, &value);
+ ret = db->hdb__get(context, db, key, &value);
krb5_data_free(&key);
- if (code)
- return code;
- code = hdb_value2entry(context, &value, &entry->entry);
- if (code) {
+ if (ret)
+ return ret;
+ ret = hdb_value2entry(context, &value, &entry->entry);
+ if (ret) {
krb5_data_free(&value);
- return code;
+ return ret;
}
}
krb5_data_free(&value);
- if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
- code = hdb_unseal_keys (context, db, &entry->entry);
- if (code)
+ if ((flags & HDB_F_DECRYPT) && (flags & HDB_F_ALL_KVNOS)) {
+ /* Decrypt the current keys */
+ ret = hdb_unseal_keys(context, db, &entry->entry);
+ if (ret) {
hdb_free_entry(context, entry);
+ return ret;
+ }
+ /* Decrypt the key history too */
+ ret = hdb_unseal_keys_kvno(context, db, 0, flags, &entry->entry);
+ if (ret) {
+ hdb_free_entry(context, entry);
+ return ret;
+ }
+ } else if ((flags & HDB_F_DECRYPT)) {
+ if ((flags & HDB_F_KVNO_SPECIFIED) == 0 || kvno == entry->entry.kvno) {
+ /* Decrypt the current keys */
+ ret = hdb_unseal_keys(context, db, &entry->entry);
+ if (ret) {
+ hdb_free_entry(context, entry);
+ return ret;
+ }
+ } else {
+ if ((flags & HDB_F_ALL_KVNOS))
+ kvno = 0;
+ /*
+ * Find and decrypt the keys from the history that we want,
+ * and swap them with the current keys
+ */
+ ret = hdb_unseal_keys_kvno(context, db, kvno, flags, &entry->entry);
+ if (ret) {
+ hdb_free_entry(context, entry);
+ return ret;
+ }
+ }
}
- return code;
+
+ return 0;
}
static krb5_error_code
@@ -192,9 +222,11 @@ hdb_remove_aliases(krb5_context context, HDB *db, krb5_data *key)
for (i = 0; i < aliases->aliases.len; i++) {
krb5_data akey;
- hdb_principal2key(context, &aliases->aliases.val[i], &akey);
- code = db->hdb__del(context, db, akey);
- krb5_data_free(&akey);
+ code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
+ if (code == 0) {
+ code = db->hdb__del(context, db, akey);
+ krb5_data_free(&akey);
+ }
if (code) {
free_hdb_entry(&oldentry);
return code;
@@ -221,14 +253,15 @@ hdb_add_aliases(krb5_context context, HDB *db,
hdb_entry_alias entryalias;
entryalias.principal = entry->entry.principal;
- hdb_principal2key(context, &aliases->aliases.val[i], &key);
code = hdb_entry_alias2value(context, &entryalias, &value);
- if (code) {
- krb5_data_free(&key);
+ if (code)
return code;
- }
- code = db->hdb__put(context, db, flags, key, value);
- krb5_data_free(&key);
+
+ code = hdb_principal2key(context, &aliases->aliases.val[i], &key);
+ if (code == 0) {
+ code = db->hdb__put(context, db, flags, key, value);
+ krb5_data_free(&key);
+ }
krb5_data_free(&value);
if (code)
return code;
@@ -253,9 +286,11 @@ hdb_check_aliases(krb5_context context, HDB *db, hdb_entry_ex *entry)
hdb_entry_alias alias;
krb5_data akey, value;
- hdb_principal2key(context, &aliases->aliases.val[i], &akey);
- code = db->hdb__get(context, db, akey, &value);
- krb5_data_free(&akey);
+ code = hdb_principal2key(context, &aliases->aliases.val[i], &akey);
+ if (code == 0) {
+ code = db->hdb__get(context, db, akey, &value);
+ krb5_data_free(&akey);
+ }
if (code == HDB_ERR_NOENTRY)
continue;
else if (code)
@@ -284,11 +319,29 @@ _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
krb5_data key, value;
int code;
+ if (entry->entry.flags.do_not_store)
+ return HDB_ERR_MISUSE;
/* check if new aliases already is used */
code = hdb_check_aliases(context, db, entry);
if (code)
return code;
+ if ((flags & HDB_F_PRECHECK) && (flags & HDB_F_REPLACE))
+ return 0;
+
+ if ((flags & HDB_F_PRECHECK)) {
+ code = hdb_principal2key(context, entry->entry.principal, &key);
+ if (code)
+ return code;
+ code = db->hdb__get(context, db, key, &value);
+ krb5_data_free(&key);
+ if (code == 0)
+ krb5_data_free(&value);
+ if (code == HDB_ERR_NOENTRY)
+ return 0;
+ return code ? code : HDB_ERR_EXISTS;
+ }
+
if(entry->entry.generation == NULL) {
struct timeval t;
entry->entry.generation = malloc(sizeof(*entry->entry.generation));
@@ -328,13 +381,32 @@ _hdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
}
krb5_error_code
-_hdb_remove(krb5_context context, HDB *db, krb5_const_principal principal)
+_hdb_remove(krb5_context context, HDB *db,
+ unsigned flags, krb5_const_principal principal)
{
- krb5_data key;
+ krb5_data key, value;
int code;
hdb_principal2key(context, principal, &key);
+ if ((flags & HDB_F_PRECHECK)) {
+ /*
+ * We don't check that we can delete the aliases because we
+ * assume that the DB is consistent. If we did check for alias
+ * consistency we'd also have to provide a way to fsck the DB,
+ * otherwise admins would have no way to recover -- papering
+ * over this here is less work, but we really ought to provide
+ * an HDB fsck.
+ */
+ code = db->hdb__get(context, db, key, &value);
+ krb5_data_free(&key);
+ if (code == 0) {
+ krb5_data_free(&value);
+ return 0;
+ }
+ return code;
+ }
+
code = hdb_remove_aliases(context, db, &key);
if (code) {
krb5_data_free(&key);
diff --git a/lib/hdb/db.c b/lib/hdb/db.c
index 69940edf89dc..c6cf58813814 100644
--- a/lib/hdb/db.c
+++ b/lib/hdb/db.c
@@ -33,7 +33,7 @@
#include "hdb_locl.h"
-#if HAVE_DB1
+#if defined(HAVE_DB1)
#if defined(HAVE_DB_185_H)
#include <db_185.h>
@@ -41,11 +41,27 @@
#include <db.h>
#endif
+typedef struct {
+ HDB hdb; /* generic members */
+ int lock_fd; /* DB-specific */
+} DB1_HDB;
+
static krb5_error_code
DB_close(krb5_context context, HDB *db)
{
+ DB1_HDB *db1 = (DB1_HDB *)db;
DB *d = (DB*)db->hdb_db;
+
+ heim_assert(d != 0, "Closing already closed HDB");
+
(*d->close)(d);
+ db->hdb_db = 0;
+
+ if (db1->lock_fd >= 0) {
+ close(db1->lock_fd);
+ db1->lock_fd = -1;
+ }
+
return 0;
}
@@ -63,27 +79,15 @@ DB_destroy(krb5_context context, HDB *db)
static krb5_error_code
DB_lock(krb5_context context, HDB *db, int operation)
{
- DB *d = (DB*)db->hdb_db;
- int fd = (*d->fd)(d);
- if(fd < 0) {
- krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
- "Can't lock database: %s", db->hdb_name);
- return HDB_ERR_CANT_LOCK_DB;
- }
- return hdb_lock(fd, operation);
+
+ return 0;
}
static krb5_error_code
DB_unlock(krb5_context context, HDB *db)
{
- DB *d = (DB*)db->hdb_db;
- int fd = (*d->fd)(d);
- if(fd < 0) {
- krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
- "Can't unlock database: %s", db->hdb_name);
- return HDB_ERR_CANT_LOCK_DB;
- }
- return hdb_unlock(fd);
+
+ return 0;
}
@@ -96,13 +100,7 @@ DB_seq(krb5_context context, HDB *db,
krb5_data key_data, data;
int code;
- code = db->hdb_lock(context, db, HDB_RLOCK);
- if(code == -1) {
- krb5_set_error_message(context, HDB_ERR_DB_INUSE, "Database %s in use", db->hdb_name);
- return HDB_ERR_DB_INUSE;
- }
code = (*d->seq)(d, &key, &value, flag);
- db->hdb_unlock(context, db); /* XXX check value */
if(code == -1) {
code = errno;
krb5_set_error_message(context, code, "Database %s seq error: %s",
@@ -159,6 +157,10 @@ DB_rename(krb5_context context, HDB *db, const char *new_name)
int ret;
char *old, *new;
+ if (strncmp(new_name, "db:", sizeof("db:") - 1) == 0)
+ new_name += sizeof("db:") - 1;
+ else if (strncmp(new_name, "db1:", sizeof("db1:") - 1) == 0)
+ new_name += sizeof("db1:") - 1;
asprintf(&old, "%s.db", db->hdb_name);
asprintf(&new, "%s.db", new_name);
ret = rename(old, new);
@@ -181,11 +183,7 @@ DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
k.data = key.data;
k.size = key.length;
- code = db->hdb_lock(context, db, HDB_RLOCK);
- if(code)
- return code;
code = (*d->get)(d, &k, &v, 0);
- db->hdb_unlock(context, db);
if(code < 0) {
code = errno;
krb5_set_error_message(context, code, "Database %s get error: %s",
@@ -213,11 +211,8 @@ DB__put(krb5_context context, HDB *db, int replace,
k.size = key.length;
v.data = value.data;
v.size = value.length;
- code = db->hdb_lock(context, db, HDB_WLOCK);
- if(code)
- return code;
+ krb5_clear_error_message(context);
code = (*d->put)(d, &k, &v, replace ? 0 : R_NOOVERWRITE);
- db->hdb_unlock(context, db);
if(code < 0) {
code = errno;
krb5_set_error_message(context, code, "Database %s put error: %s",
@@ -225,9 +220,15 @@ DB__put(krb5_context context, HDB *db, int replace,
return code;
}
if(code == 1) {
- krb5_clear_error_message(context);
return HDB_ERR_EXISTS;
}
+ code = (*d->sync)(d, 0);
+ if (code == -1) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s put sync error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
return 0;
}
@@ -239,25 +240,65 @@ DB__del(krb5_context context, HDB *db, krb5_data key)
krb5_error_code code;
k.data = key.data;
k.size = key.length;
- code = db->hdb_lock(context, db, HDB_WLOCK);
- if(code)
- return code;
+ krb5_clear_error_message(context);
code = (*d->del)(d, &k, 0);
- db->hdb_unlock(context, db);
- if(code == 1) {
+ if (code == 1)
+ return HDB_ERR_NOENTRY;
+ if (code < 0) {
code = errno;
- krb5_set_error_message(context, code, "Database %s put error: %s",
+ krb5_set_error_message(context, code, "Database %s del error: %s",
+ db->hdb_name, strerror(code));
+ return code;
+ }
+ code = (*d->sync)(d, 0);
+ if (code == -1) {
+ code = errno;
+ krb5_set_error_message(context, code, "Database %s del sync error: %s",
db->hdb_name, strerror(code));
return code;
}
- if(code < 0)
- return errno;
return 0;
}
+static DB *
+_open_db(char *fn, int flags, int mode, int *fd)
+{
+#ifndef O_EXLOCK
+ int op;
+ int ret;
+
+ *fd = open(fn, flags, mode);
+ if (*fd == -1)
+ return NULL;
+
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ op = LOCK_SH;
+ else
+ op = LOCK_EX;
+
+ ret = flock(*fd, op);
+ if (ret == -1) {
+ int saved_errno;
+
+ saved_errno = errno;
+ close(*fd);
+ errno = saved_errno;
+ return NULL;
+ }
+#else
+ if ((flags & O_ACCMODE) == O_RDONLY)
+ flags |= O_SHLOCK;
+ else
+ flags |= O_EXLOCK;
+#endif
+
+ return dbopen(fn, flags, mode, DB_BTREE, NULL);
+}
+
static krb5_error_code
DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
{
+ DB1_HDB *db1 = (DB1_HDB *)db;
char *fn;
krb5_error_code ret;
@@ -266,16 +307,15 @@ DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
- db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
+ db->hdb_db = _open_db(fn, flags, mode, &db1->lock_fd);
free(fn);
/* try to open without .db extension */
if(db->hdb_db == NULL && errno == ENOENT)
- db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
+ db->hdb_db = _open_db(db->hdb_name, flags, mode, &db1->lock_fd);
if(db->hdb_db == NULL) {
- ret = errno;
- krb5_set_error_message(context, ret, "dbopen (%s): %s",
- db->hdb_name, strerror(ret));
- return ret;
+ krb5_set_error_message(context, errno, "dbopen (%s): %s",
+ db->hdb_name, strerror(errno));
+ return errno;
}
if((flags & O_ACCMODE) == O_RDONLY)
ret = hdb_check_db_format(context, db);
@@ -296,10 +336,11 @@ DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
}
krb5_error_code
-hdb_db_create(krb5_context context, HDB **db,
- const char *filename)
+hdb_db1_create(krb5_context context, HDB **db,
+ const char *filename)
{
- *db = calloc(1, sizeof(**db));
+ DB1_HDB **db1 = (DB1_HDB **)db;
+ *db = calloc(1, sizeof(**db1)); /* Allocate space for the larger db1 */
if (*db == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
@@ -330,7 +371,9 @@ hdb_db_create(krb5_context context, HDB **db,
(*db)->hdb__put = DB__put;
(*db)->hdb__del = DB__del;
(*db)->hdb_destroy = DB_destroy;
+
+ (*db1)->lock_fd = -1;
return 0;
}
-#endif /* HAVE_DB1 */
+#endif /* defined(HAVE_DB1) */
diff --git a/lib/hdb/db3.c b/lib/hdb/db3.c
index 58f892ff6777..cd2e33f067ad 100644
--- a/lib/hdb/db3.c
+++ b/lib/hdb/db3.c
@@ -33,10 +33,14 @@
#include "hdb_locl.h"
+#include <fcntl.h>
+
#if HAVE_DB3
#ifdef HAVE_DBHEADER
#include <db.h>
+#elif HAVE_DB6_DB_H
+#include <db6/db.h>
#elif HAVE_DB5_DB_H
#include <db5/db.h>
#elif HAVE_DB4_DB_H
@@ -47,15 +51,32 @@
#include <db.h>
#endif
+typedef struct {
+ HDB hdb; /* generic members */
+ int lock_fd; /* DB3-specific */
+} DB3_HDB;
+
+
static krb5_error_code
DB_close(krb5_context context, HDB *db)
{
+ DB3_HDB *db3 = (DB3_HDB *)db;
DB *d = (DB*)db->hdb_db;
DBC *dbcp = (DBC*)db->hdb_dbc;
- (*dbcp->c_close)(dbcp);
+ heim_assert(d != 0, "Closing already closed HDB");
+
+ if (dbcp != NULL)
+ dbcp->c_close(dbcp);
+ if (d != NULL)
+ d->close(d, 0);
+ if (db3->lock_fd >= 0)
+ close(db3->lock_fd);
+
+ db3->lock_fd = -1;
db->hdb_dbc = 0;
- (*d->close)(d, 0);
+ db->hdb_db = 0;
+
return 0;
}
@@ -73,21 +94,15 @@ DB_destroy(krb5_context context, HDB *db)
static krb5_error_code
DB_lock(krb5_context context, HDB *db, int operation)
{
- DB *d = (DB*)db->hdb_db;
- int fd;
- if ((*d->fd)(d, &fd))
- return HDB_ERR_CANT_LOCK_DB;
- return hdb_lock(fd, operation);
+
+ return 0;
}
static krb5_error_code
DB_unlock(krb5_context context, HDB *db)
{
- DB *d = (DB*)db->hdb_db;
- int fd;
- if ((*d->fd)(d, &fd))
- return HDB_ERR_CANT_LOCK_DB;
- return hdb_unlock(fd);
+
+ return 0;
}
@@ -102,10 +117,7 @@ DB_seq(krb5_context context, HDB *db,
memset(&key, 0, sizeof(DBT));
memset(&value, 0, sizeof(DBT));
- if ((*db->hdb_lock)(context, db, HDB_RLOCK))
- return HDB_ERR_DB_INUSE;
code = (*dbcp->c_get)(dbcp, &key, &value, flag);
- (*db->hdb_unlock)(context, db); /* XXX check value */
if (code == DB_NOTFOUND)
return HDB_ERR_NOENTRY;
if (code)
@@ -156,16 +168,29 @@ DB_rename(krb5_context context, HDB *db, const char *new_name)
int ret;
char *old, *new;
- asprintf(&old, "%s.db", db->hdb_name);
- asprintf(&new, "%s.db", new_name);
+ if (strncmp(new_name, "db:", sizeof("db:") - 1) == 0)
+ new_name += sizeof("db:") - 1;
+ else if (strncmp(new_name, "db3:", sizeof("db3:") - 1) == 0)
+ new_name += sizeof("db3:") - 1;
+
+ ret = asprintf(&old, "%s.db", db->hdb_name);
+ if (ret == -1)
+ return ENOMEM;
+ ret = asprintf(&new, "%s.db", new_name);
+ if (ret == -1) {
+ free(old);
+ return ENOMEM;
+ }
ret = rename(old, new);
free(old);
- free(new);
- if(ret)
+ if(ret) {
+ free(new);
return errno;
+ }
free(db->hdb_name);
- db->hdb_name = strdup(new_name);
+ new[strlen(new) - 3] = '\0';
+ db->hdb_name = new;
return 0;
}
@@ -181,10 +206,7 @@ DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
k.data = key.data;
k.size = key.length;
k.flags = 0;
- if ((code = (*db->hdb_lock)(context, db, HDB_RLOCK)))
- return code;
code = (*d->get)(d, NULL, &k, &v, 0);
- (*db->hdb_unlock)(context, db);
if(code == DB_NOTFOUND)
return HDB_ERR_NOENTRY;
if(code)
@@ -210,14 +232,49 @@ DB__put(krb5_context context, HDB *db, int replace,
v.data = value.data;
v.size = value.length;
v.flags = 0;
- if ((code = (*db->hdb_lock)(context, db, HDB_WLOCK)))
- return code;
code = (*d->put)(d, NULL, &k, &v, replace ? 0 : DB_NOOVERWRITE);
- (*db->hdb_unlock)(context, db);
if(code == DB_KEYEXIST)
return HDB_ERR_EXISTS;
- if(code)
- return errno;
+ if (code) {
+ /*
+ * Berkeley DB 3 and up have a terrible error reporting
+ * interface...
+ *
+ * DB->err() doesn't output a string.
+ * DB->set_errcall()'s callback function doesn't have a void *
+ * argument that can be used to place the error somewhere.
+ *
+ * The only thing we could do is fopen()/fdopen() a file, set it
+ * with DB->set_errfile(), then call DB->err(), then read the
+ * message from the file, unset it with DB->set_errfile(), close
+ * it and delete it. That's a lot of work... so we don't do it.
+ */
+ if (code == EACCES || code == ENOSPC || code == EINVAL) {
+ krb5_set_error_message(context, code,
+ "Database %s put error: %s",
+ db->hdb_name, strerror(code));
+ } else {
+ code = HDB_ERR_UK_SERROR;
+ krb5_set_error_message(context, code,
+ "Database %s put error: unknown (%d)",
+ db->hdb_name, code);
+ }
+ return code;
+ }
+ code = (*d->sync)(d, 0);
+ if (code) {
+ if (code == EACCES || code == ENOSPC || code == EINVAL) {
+ krb5_set_error_message(context, code,
+ "Database %s put sync error: %s",
+ db->hdb_name, strerror(code));
+ } else {
+ code = HDB_ERR_UK_SERROR;
+ krb5_set_error_message(context, code,
+ "Database %s put sync error: unknown (%d)",
+ db->hdb_name, code);
+ }
+ return code;
+ }
return 0;
}
@@ -231,26 +288,96 @@ DB__del(krb5_context context, HDB *db, krb5_data key)
k.data = key.data;
k.size = key.length;
k.flags = 0;
- code = (*db->hdb_lock)(context, db, HDB_WLOCK);
- if(code)
- return code;
code = (*d->del)(d, NULL, &k, 0);
- (*db->hdb_unlock)(context, db);
if(code == DB_NOTFOUND)
return HDB_ERR_NOENTRY;
- if(code)
+ if (code) {
+ if (code == EACCES || code == ENOSPC || code == EINVAL) {
+ krb5_set_error_message(context, code,
+ "Database %s del error: %s",
+ db->hdb_name, strerror(code));
+ } else {
+ code = HDB_ERR_UK_SERROR;
+ krb5_set_error_message(context, code,
+ "Database %s del error: unknown (%d)",
+ db->hdb_name, code);
+ }
return code;
+ }
+ code = (*d->sync)(d, 0);
+ if (code) {
+ if (code == EACCES || code == ENOSPC || code == EINVAL) {
+ krb5_set_error_message(context, code,
+ "Database %s del sync error: %s",
+ db->hdb_name, strerror(code));
+ } else {
+ code = HDB_ERR_UK_SERROR;
+ krb5_set_error_message(context, code,
+ "Database %s del sync error: unknown (%d)",
+ db->hdb_name, code);
+ }
+ return code;
+ }
return 0;
}
+#define RD_CACHE_SZ 0x8000 /* Minimal read cache size */
+#define WR_CACHE_SZ 0x8000 /* Minimal write cache size */
+
+static int
+_open_db(DB *d, char *fn, int myflags, int flags, mode_t mode, int *fd)
+{
+ int ret;
+ int cache_size = (myflags & DB_RDONLY) ? RD_CACHE_SZ : WR_CACHE_SZ;
+
+ *fd = open(fn, flags, mode);
+
+ if (*fd == -1)
+ return errno;
+
+ /*
+ * Without DB_FCNTL_LOCKING, the DB library complains when initializing
+ * a database in an empty file. Since the database is our lock file,
+ * we create it before Berkeley DB does, so a new DB always starts empty.
+ */
+ myflags |= DB_FCNTL_LOCKING;
+
+ ret = flock(*fd, (myflags&DB_RDONLY) ? LOCK_SH : LOCK_EX);
+ if (ret == -1) {
+ ret = errno;
+ close(*fd);
+ *fd = -1;
+ return ret;
+ }
+
+ d->set_cachesize(d, 0, cache_size, 0);
+
+#if (DB_VERSION_MAJOR > 4) || ((DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR >= 1))
+ ret = (*d->open)(d, NULL, fn, NULL, DB_BTREE, myflags, mode);
+#else
+ ret = (*d->open)(d, fn, NULL, DB_BTREE, myflags, mode);
+#endif
+
+ if (ret != 0) {
+ close(*fd);
+ *fd = -1;
+ }
+
+ return ret;
+}
+
static krb5_error_code
DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
{
+ DB3_HDB *db3 = (DB3_HDB *)db;
DBC *dbc = NULL;
char *fn;
krb5_error_code ret;
DB *d;
int myflags = 0;
+ int aret;
+
+ heim_assert(db->hdb_db == 0, "Opening already open HDB");
if (flags & O_CREAT)
myflags |= DB_CREATE;
@@ -264,11 +391,12 @@ DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
if (flags & O_TRUNC)
myflags |= DB_TRUNCATE;
- asprintf(&fn, "%s.db", db->hdb_name);
- if (fn == NULL) {
+ aret = asprintf(&fn, "%s.db", db->hdb_name);
+ if (aret == -1) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
+
if (db_create(&d, NULL, 0) != 0) {
free(fn);
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
@@ -276,33 +404,29 @@ DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
}
db->hdb_db = d;
-#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
- ret = (*d->open)(db->hdb_db, NULL, fn, NULL, DB_BTREE, myflags, mode);
-#else
- ret = (*d->open)(db->hdb_db, fn, NULL, DB_BTREE, myflags, mode);
-#endif
+ /* From here on out always DB_close() before returning on error */
+ ret = _open_db(d, fn, myflags, flags, mode, &db3->lock_fd);
+ free(fn);
if (ret == ENOENT) {
/* try to open without .db extension */
-#if (DB_VERSION_MAJOR >= 4) && (DB_VERSION_MINOR >= 1)
- ret = (*d->open)(db->hdb_db, NULL, db->hdb_name, NULL, DB_BTREE,
- myflags, mode);
-#else
- ret = (*d->open)(db->hdb_db, db->hdb_name, NULL, DB_BTREE,
- myflags, mode);
-#endif
+ ret = _open_db(d, db->hdb_name, myflags, flags, mode, &db3->lock_fd);
}
if (ret) {
- free(fn);
+ DB_close(context, db);
krb5_set_error_message(context, ret, "opening %s: %s",
- db->hdb_name, strerror(ret));
+ db->hdb_name, strerror(ret));
return ret;
}
- free(fn);
- ret = (*d->cursor)(d, NULL, &dbc, 0);
+#ifndef DB_CURSOR_BULK
+# define DB_CURSOR_BULK 0 /* Missing with DB < 4.8 */
+#endif
+ ret = (*d->cursor)(d, NULL, &dbc, DB_CURSOR_BULK);
+
if (ret) {
+ DB_close(context, db);
krb5_set_error_message(context, ret, "d->cursor: %s", strerror(ret));
return ret;
}
@@ -326,10 +450,11 @@ DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
}
krb5_error_code
-hdb_db_create(krb5_context context, HDB **db,
- const char *filename)
+hdb_db3_create(krb5_context context, HDB **db,
+ const char *filename)
{
- *db = calloc(1, sizeof(**db));
+ DB3_HDB **db3 = (DB3_HDB **)db;
+ *db3 = calloc(1, sizeof(**db3)); /* Allocate space for the larger db3 */
if (*db == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
@@ -360,6 +485,8 @@ hdb_db_create(krb5_context context, HDB **db,
(*db)->hdb__put = DB__put;
(*db)->hdb__del = DB__del;
(*db)->hdb_destroy = DB_destroy;
+
+ (*db3)->lock_fd = -1;
return 0;
}
#endif /* HAVE_DB3 */
diff --git a/lib/hdb/dbinfo.c b/lib/hdb/dbinfo.c
index 52e394106eca..e2890255b2ef 100644
--- a/lib/hdb/dbinfo.c
+++ b/lib/hdb/dbinfo.c
@@ -108,11 +108,9 @@ hdb_get_dbinfo(krb5_context context, struct hdb_dbinfo **dbp)
NULL);
if (db_binding) {
- ret = get_dbinfo(context, db_binding, "default", &di);
- if (ret == 0 && di) {
- databases = di;
- dt = &di->next;
- }
+ ret = get_dbinfo(context, db_binding, "default", &databases);
+ if (ret == 0 && databases != NULL)
+ dt = &databases->next;
for ( ; db_binding != NULL; db_binding = db_binding->next) {
@@ -129,36 +127,41 @@ hdb_get_dbinfo(krb5_context context, struct hdb_dbinfo **dbp)
if (dt)
*dt = di;
- else
+ else {
+ hdb_free_dbinfo(context, &databases);
databases = di;
+ }
dt = &di->next;
}
}
- if(databases == NULL) {
+ if (databases == NULL) {
/* if there are none specified, create one and use defaults */
- di = calloc(1, sizeof(*di));
- databases = di;
- di->label = strdup("default");
+ databases = calloc(1, sizeof(*databases));
+ databases->label = strdup("default");
}
- for(di = databases; di; di = di->next) {
- if(di->dbname == NULL) {
+ for (di = databases; di; di = di->next) {
+ if (di->dbname == NULL) {
di->dbname = strdup(default_dbname);
if (di->mkey_file == NULL)
di->mkey_file = strdup(default_mkey);
}
- if(di->mkey_file == NULL) {
+ if (di->mkey_file == NULL) {
p = strrchr(di->dbname, '.');
if(p == NULL || strchr(p, '/') != NULL)
/* final pathname component does not contain a . */
- asprintf(&di->mkey_file, "%s.mkey", di->dbname);
+ ret = asprintf(&di->mkey_file, "%s.mkey", di->dbname);
else
/* the filename is something.else, replace .else with
.mkey */
- asprintf(&di->mkey_file, "%.*s.mkey",
- (int)(p - di->dbname), di->dbname);
+ ret = asprintf(&di->mkey_file, "%.*s.mkey",
+ (int)(p - di->dbname), di->dbname);
+ if (ret == -1) {
+ hdb_free_dbinfo(context, &databases);
+ return ENOMEM;
+ }
}
if(di->acl_file == NULL)
di->acl_file = strdup(default_acl);
@@ -248,6 +251,12 @@ hdb_free_dbinfo(krb5_context context, struct hdb_dbinfo **dbp)
const char *
hdb_db_dir(krb5_context context)
{
+ const char *p;
+
+ p = krb5_config_get_string(context, NULL, "hdb", "db-dir", NULL);
+ if (p)
+ return p;
+
return HDB_DB_DIR;
}
diff --git a/lib/hdb/ext.c b/lib/hdb/ext.c
index d2a4373b9b38..ecefe931b74f 100644
--- a/lib/hdb/ext.c
+++ b/lib/hdb/ext.c
@@ -101,7 +101,7 @@ hdb_replace_extension(krb5_context context,
ext2 = hdb_find_extension(entry, ext->data.element);
} else {
/*
- * This is an unknown extention, and we are asked to replace a
+ * This is an unknown extension, and we are asked to replace a
* possible entry in `entry' that is of the same type. This
* might seem impossible, but ASN.1 CHOICE comes to our
* rescue. The first tag in each branch in the CHOICE is
@@ -120,7 +120,7 @@ hdb_replace_extension(krb5_context context,
&size);
if (ret) {
krb5_set_error_message(context, ret, "hdb: failed to decode "
- "replacement hdb extention");
+ "replacement hdb extension");
return ret;
}
@@ -136,7 +136,7 @@ hdb_replace_extension(krb5_context context,
&size);
if (ret) {
krb5_set_error_message(context, ret, "hdb: failed to decode "
- "present hdb extention");
+ "present hdb extension");
return ret;
}
@@ -153,7 +153,7 @@ hdb_replace_extension(krb5_context context,
ret = copy_HDB_extension(ext, ext2);
if (ret)
krb5_set_error_message(context, ret, "hdb: failed to copy replacement "
- "hdb extention");
+ "hdb extension");
return ret;
}
@@ -432,3 +432,101 @@ hdb_entry_get_aliases(const hdb_entry *entry, const HDB_Ext_Aliases **a)
return 0;
}
+
+unsigned int
+hdb_entry_get_kvno_diff_clnt(const hdb_entry *entry)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry,
+ choice_HDB_extension_data_hist_kvno_diff_clnt);
+ if (ext)
+ return ext->data.u.hist_kvno_diff_clnt;
+ return 1;
+}
+
+krb5_error_code
+hdb_entry_set_kvno_diff_clnt(krb5_context context, hdb_entry *entry,
+ unsigned int diff)
+{
+ HDB_extension ext;
+
+ if (diff > 16384)
+ return EINVAL;
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_kvno_diff_clnt;
+ ext.data.u.hist_kvno_diff_clnt = diff;
+ return hdb_replace_extension(context, entry, &ext);
+}
+
+krb5_error_code
+hdb_entry_clear_kvno_diff_clnt(krb5_context context, hdb_entry *entry)
+{
+ return hdb_clear_extension(context, entry,
+ choice_HDB_extension_data_hist_kvno_diff_clnt);
+}
+
+unsigned int
+hdb_entry_get_kvno_diff_svc(const hdb_entry *entry)
+{
+ const HDB_extension *ext;
+
+ ext = hdb_find_extension(entry,
+ choice_HDB_extension_data_hist_kvno_diff_svc);
+ if (ext)
+ return ext->data.u.hist_kvno_diff_svc;
+ return 1024; /* max_life effectively provides a better default */
+}
+
+krb5_error_code
+hdb_entry_set_kvno_diff_svc(krb5_context context, hdb_entry *entry,
+ unsigned int diff)
+{
+ HDB_extension ext;
+
+ if (diff > 16384)
+ return EINVAL;
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_kvno_diff_svc;
+ ext.data.u.hist_kvno_diff_svc = diff;
+ return hdb_replace_extension(context, entry, &ext);
+}
+
+krb5_error_code
+hdb_entry_clear_kvno_diff_svc(krb5_context context, hdb_entry *entry)
+{
+ return hdb_clear_extension(context, entry,
+ choice_HDB_extension_data_hist_kvno_diff_svc);
+}
+
+krb5_error_code
+hdb_set_last_modified_by(krb5_context context, hdb_entry *entry,
+ krb5_principal modby, time_t modtime)
+{
+ krb5_error_code ret;
+ Event *old_ev;
+ Event *ev;
+
+ old_ev = entry->modified_by;
+
+ ev = calloc(1, sizeof (*ev));
+ if (!ev)
+ return ENOMEM;
+ if (modby)
+ ret = krb5_copy_principal(context, modby, &ev->principal);
+ else
+ ret = krb5_parse_name(context, "root/admin", &ev->principal);
+ if (ret) {
+ free(ev);
+ return ret;
+ }
+ ev->time = modtime;
+ if (!ev->time)
+ time(&ev->time);
+
+ entry->modified_by = ev;
+ if (old_ev)
+ free_Event(old_ev);
+ return 0;
+}
+
diff --git a/lib/hdb/hdb-ldap.c b/lib/hdb/hdb-ldap.c
index 1b4024aa540a..9ce4ba74f823 100644
--- a/lib/hdb/hdb-ldap.c
+++ b/lib/hdb/hdb-ldap.c
@@ -2,6 +2,7 @@
* Copyright (c) 1999-2001, 2003, PADL Software Pty Ltd.
* Copyright (c) 2004, Andrew Bartlett.
* Copyright (c) 2003 - 2008, Kungliga Tekniska Högskolan.
+ * Copyright (c) 2015, Timothy Pearson.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -50,6 +51,7 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
static const char *default_structural_object = "account";
static char *structural_object;
+static const char *default_ldap_url = "ldapi:///";
static krb5_boolean samba_forwardable;
struct hdbldapdb {
@@ -57,6 +59,9 @@ struct hdbldapdb {
int h_msgid;
char *h_base;
char *h_url;
+ char *h_bind_dn;
+ char *h_bind_password;
+ krb5_boolean h_start_tls;
char *h_createbase;
};
@@ -66,6 +71,8 @@ struct hdbldapdb {
do { ((struct hdbldapdb *)(db)->hdb_db)->h_msgid = msgid; } while(0)
#define HDB2BASE(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_base)
#define HDB2URL(dn) (((struct hdbldapdb *)(db)->hdb_db)->h_url)
+#define HDB2BINDDN(db) (((struct hdbldapdb *)(db)->hdb_db)->h_bind_dn)
+#define HDB2BINDPW(db) (((struct hdbldapdb *)(db)->hdb_db)->h_bind_password)
#define HDB2CREATE(db) (((struct hdbldapdb *)(db)->hdb_db)->h_createbase)
/*
@@ -85,6 +92,7 @@ static char * krb5kdcentry_attrs[] = {
"krb5PasswordEnd",
"krb5PrincipalName",
"krb5PrincipalRealm",
+ "krb5ExtendedAttributes",
"krb5ValidEnd",
"krb5ValidStart",
"modifiersName",
@@ -510,6 +518,33 @@ LDAP_entry2mods(krb5_context context, HDB * db, hdb_entry_ex * ent,
goto out;
}
+ if (is_heimdal_entry && ent->entry.extensions) {
+ if (!is_new_entry) {
+ vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5ExtendedAttributes");
+ if (vals) {
+ ldap_value_free_len(vals);
+ ret = LDAP_addmod(&mods, LDAP_MOD_DELETE, "krb5ExtendedAttributes", NULL);
+ if (ret)
+ goto out;
+ }
+ }
+
+ for (i = 0; i < ent->entry.extensions->len; i++) {
+ unsigned char *buf;
+ size_t size, sz = 0;
+
+ ASN1_MALLOC_ENCODE(HDB_extension, buf, size, &ent->entry.extensions->val[i], &sz, ret);
+ if (ret)
+ goto out;
+ if (size != sz)
+ krb5_abortx(context, "internal error in ASN.1 encoder");
+
+ ret = LDAP_addmod_len(&mods, LDAP_MOD_ADD, "krb5ExtendedAttributes", buf, sz);
+ if (ret)
+ goto out;
+ }
+ }
+
if (is_heimdal_entry && ent->entry.valid_start) {
if (orig.entry.valid_end == NULL
|| (*(ent->entry.valid_start) != *(orig.entry.valid_start))) {
@@ -797,10 +832,10 @@ need_quote(unsigned char c)
(c == 0x7f);
}
-const static char hexchar[] = "0123456789ABCDEF";
+static const char hexchar[] = "0123456789ABCDEF";
static krb5_error_code
-escape_value(krb5_context context, const unsigned char *unquoted, char **quoted)
+escape_value(krb5_context context, const char *unquoted, char **quoted)
{
size_t i, len;
@@ -816,7 +851,7 @@ escape_value(krb5_context context, const unsigned char *unquoted, char **quoted)
}
for (i = 0; unquoted[0] ; unquoted++) {
- if (need_quote((unsigned char *)unquoted[0])) {
+ if (need_quote((unsigned char)unquoted[0])) {
(*quoted)[i++] = '\\';
(*quoted)[i++] = hexchar[(unquoted[0] >> 4) & 0xf];
(*quoted)[i++] = hexchar[(unquoted[0] ) & 0xf];
@@ -975,6 +1010,7 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
char *unparsed_name = NULL, *dn = NULL, *ntPasswordIN = NULL;
char *samba_acct_flags = NULL;
struct berval **keys;
+ struct berval **extensions;
struct berval **vals;
int tmp, tmp_time, i, ret, have_arcfour = 0;
@@ -1013,7 +1049,6 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
keys = ldap_get_values_len(HDB2LDAP(db), msg, "krb5Key");
if (keys != NULL) {
- int i;
size_t l;
ent->entry.keys.len = ldap_count_values_len(keys);
@@ -1043,10 +1078,35 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
#endif
}
+ extensions = ldap_get_values_len(HDB2LDAP(db), msg, "krb5ExtendedAttributes");
+ if (extensions != NULL) {
+ size_t l;
+
+ ent->entry.extensions = calloc(1, sizeof(*(ent->entry.extensions)));
+ if (ent->entry.extensions == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ ent->entry.extensions->len = ldap_count_values_len(extensions);
+ ent->entry.extensions->val = (HDB_extension *) calloc(ent->entry.extensions->len, sizeof(HDB_extension));
+ if (ent->entry.extensions->val == NULL) {
+ ent->entry.extensions->len = 0;
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ for (i = 0; i < ent->entry.extensions->len; i++) {
+ ret = decode_HDB_extension((unsigned char *) extensions[i]->bv_val,
+ (size_t) extensions[i]->bv_len, &ent->entry.extensions->val[i], &l);
+ if (ret)
+ krb5_set_error_message(context, ret, "decode_HDB_extension failed");
+ }
+ ber_bvecfree(extensions);
+ } else {
+ ent->entry.extensions = NULL;
+ }
+
vals = ldap_get_values_len(HDB2LDAP(db), msg, "krb5EncryptionType");
if (vals != NULL) {
- int i;
-
ent->entry.etypes = malloc(sizeof(*(ent->entry.etypes)));
if (ent->entry.etypes == NULL) {
ret = ENOMEM;
@@ -1054,7 +1114,8 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
goto out;
}
ent->entry.etypes->len = ldap_count_values_len(vals);
- ent->entry.etypes->val = calloc(ent->entry.etypes->len, sizeof(int));
+ ent->entry.etypes->val = calloc(ent->entry.etypes->len,
+ sizeof(ent->entry.etypes->val[0]));
if (ent->entry.etypes->val == NULL) {
ret = ENOMEM;
krb5_set_error_message(context, ret, "malloc: out of memory");
@@ -1089,24 +1150,22 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
ret = LDAP_get_string_value(db, msg, "sambaNTPassword", &ntPasswordIN);
if (ret == 0 && have_arcfour == 0) {
unsigned *etypes;
- Key *keys;
- int i;
+ Key *ks;
- keys = realloc(ent->entry.keys.val,
- (ent->entry.keys.len + 1) * sizeof(ent->entry.keys.val[0]));
- if (keys == NULL) {
- free(ntPasswordIN);
+ ks = realloc(ent->entry.keys.val,
+ (ent->entry.keys.len + 1) *
+ sizeof(ent->entry.keys.val[0]));
+ if (ks == NULL) {
ret = ENOMEM;
krb5_set_error_message(context, ret, "malloc: out of memory");
goto out;
}
- ent->entry.keys.val = keys;
+ ent->entry.keys.val = ks;
memset(&ent->entry.keys.val[ent->entry.keys.len], 0, sizeof(Key));
ent->entry.keys.val[ent->entry.keys.len].key.keytype = ETYPE_ARCFOUR_HMAC_MD5;
ret = krb5_data_alloc (&ent->entry.keys.val[ent->entry.keys.len].key.keyvalue, 16);
if (ret) {
krb5_set_error_message(context, ret, "malloc: out of memory");
- free(ntPasswordIN);
ret = ENOMEM;
goto out;
}
@@ -1239,21 +1298,24 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
if (ret == 0) {
time_t delta;
- if (ent->entry.pw_end == NULL) {
- ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
- if (ent->entry.pw_end == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, "malloc: out of memory");
- goto out;
- }
- }
-
delta = krb5_config_get_time_default(context, NULL,
- 365 * 24 * 60 * 60,
+ 0,
"kadmin",
"password_lifetime",
NULL);
- *ent->entry.pw_end = tmp_time + delta;
+
+ if (delta) {
+ if (ent->entry.pw_end == NULL) {
+ ent->entry.pw_end = malloc(sizeof(*ent->entry.pw_end));
+ if (ent->entry.pw_end == NULL) {
+ ret = ENOMEM;
+ krb5_set_error_message(context, ret, "malloc: out of memory");
+ goto out;
+ }
+ }
+
+ *ent->entry.pw_end = tmp_time + delta;
+ }
}
ret = LDAP_get_integer_value(db, msg, "sambaPwdMustChange", &tmp_time);
@@ -1333,7 +1395,6 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
*/
- int i;
int flags_len = strlen(samba_acct_flags);
if (flags_len < 2)
@@ -1397,8 +1458,8 @@ LDAP_message2entry(krb5_context context, HDB * db, LDAPMessage * msg,
ret = 0;
out:
- if (unparsed_name)
- free(unparsed_name);
+ free(unparsed_name);
+ free(ntPasswordIN);
if (ret)
hdb_free_entry(context, ent);
@@ -1513,7 +1574,7 @@ LDAP_firstkey(krb5_context context, HDB *db, unsigned flags,
"(|(objectClass=krb5Principal)(objectClass=sambaSamAccount))",
krb5kdcentry_attrs, 0,
NULL, NULL, NULL, 0, &msgid);
- if (msgid < 0)
+ if (ret != LDAP_SUCCESS || msgid < 0)
return HDB_ERR_NOENTRY;
HDBSETMSGID(db, msgid);
@@ -1539,6 +1600,16 @@ LDAP__connect(krb5_context context, HDB * db)
* bind in progress message.
*/
struct berval bv = { 0, "" };
+ const char *sasl_method = "EXTERNAL";
+ const char *bind_dn = NULL;
+
+ if (HDB2BINDDN(db) != NULL && HDB2BINDPW(db) != NULL) {
+ /* A bind DN was specified; use SASL SIMPLE */
+ bind_dn = HDB2BINDDN(db);
+ sasl_method = LDAP_SASL_SIMPLE;
+ bv.bv_val = HDB2BINDPW(db);
+ bv.bv_len = strlen(bv.bv_val);
+ }
if (HDB2LDAP(db)) {
/* connection has been opened. ping server. */
@@ -1572,7 +1643,18 @@ LDAP__connect(krb5_context context, HDB * db)
return HDB_ERR_BADVERSION;
}
- rc = ldap_sasl_bind_s(HDB2LDAP(db), NULL, "EXTERNAL", &bv,
+ if (((struct hdbldapdb *)db->hdb_db)->h_start_tls) {
+ rc = ldap_start_tls_s(HDB2LDAP(db), NULL, NULL);
+
+ if (rc != LDAP_SUCCESS) {
+ krb5_set_error_message(context, HDB_ERR_BADVERSION,
+ "ldap_start_tls_s: %s", ldap_err2string(rc));
+ LDAP_close(context, db);
+ return HDB_ERR_BADVERSION;
+ }
+ }
+
+ rc = ldap_sasl_bind_s(HDB2LDAP(db), bind_dn, sasl_method, &bv,
NULL, NULL, NULL);
if (rc != LDAP_SUCCESS) {
krb5_set_error_message(context, HDB_ERR_BADVERSION,
@@ -1635,6 +1717,7 @@ LDAP_fetch_kvno(krb5_context context, HDB * db, krb5_const_principal principal,
return ret;
}
+#if 0
static krb5_error_code
LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
unsigned flags, hdb_entry_ex * entry)
@@ -1642,6 +1725,7 @@ LDAP_fetch(krb5_context context, HDB * db, krb5_const_principal principal,
return LDAP_fetch_kvno(context, db, principal,
flags & (~HDB_F_KVNO_SPECIFIED), 0, entry);
}
+#endif
static krb5_error_code
LDAP_store(krb5_context context, HDB * db, unsigned flags,
@@ -1654,6 +1738,9 @@ LDAP_store(krb5_context context, HDB * db, unsigned flags,
LDAPMessage *msg = NULL, *e = NULL;
char *dn = NULL, *name = NULL;
+ if ((flags & HDB_F_PRECHECK))
+ return 0; /* we can't guarantee whether we'll be able to perform it */
+
ret = LDAP_principal2message(context, db, entry->entry.principal, &msg);
if (ret == 0)
e = ldap_first_entry(HDB2LDAP(db), msg);
@@ -1725,13 +1812,17 @@ LDAP_store(krb5_context context, HDB * db, unsigned flags,
}
static krb5_error_code
-LDAP_remove(krb5_context context, HDB *db, krb5_const_principal principal)
+LDAP_remove(krb5_context context, HDB *db,
+ unsigned flags, krb5_const_principal principal)
{
krb5_error_code ret;
LDAPMessage *msg, *e;
char *dn = NULL;
int rc, limit = LDAP_NO_LIMIT;
+ if ((flags & HDB_F_PRECHECK))
+ return 0; /* we can't guarantee whether we'll be able to perform it */
+
ret = LDAP_principal2message(context, db, principal, &msg);
if (ret)
goto out;
@@ -1803,8 +1894,19 @@ hdb_ldap_common(krb5_context context,
{
struct hdbldapdb *h;
const char *create_base = NULL;
+ const char *ldap_secret_file = NULL;
+
+ if (url == NULL || url[0] == '\0') {
+ const char *p;
+ p = krb5_config_get_string(context, NULL, "kdc",
+ "hdb-ldap-url", NULL);
+ if (p == NULL)
+ p = default_ldap_url;
- if (search_base == NULL && search_base[0] == '\0') {
+ url = p;
+ }
+
+ if (search_base == NULL || search_base[0] == '\0') {
krb5_set_error_message(context, ENOMEM, "ldap search base not configured");
return ENOMEM; /* XXX */
}
@@ -1860,6 +1962,34 @@ hdb_ldap_common(krb5_context context,
return ENOMEM;
}
+ ldap_secret_file = krb5_config_get_string(context, NULL, "kdc",
+ "hdb-ldap-secret-file", NULL);
+ if (ldap_secret_file != NULL) {
+ krb5_config_binding *tmp;
+ krb5_error_code ret;
+ const char *p;
+
+ ret = krb5_config_parse_file(context, ldap_secret_file, &tmp);
+ if (ret)
+ return ret;
+
+ p = krb5_config_get_string(context, tmp, "kdc",
+ "hdb-ldap-bind-dn", NULL);
+ if (p != NULL)
+ h->h_bind_dn = strdup(p);
+
+ p = krb5_config_get_string(context, tmp, "kdc",
+ "hdb-ldap-bind-password", NULL);
+ if (p != NULL)
+ h->h_bind_password = strdup(p);
+
+ krb5_config_file_free(context, tmp);
+ }
+
+ h->h_start_tls =
+ krb5_config_get_bool_default(context, NULL, FALSE,
+ "kdc", "hdb-ldap-start-tls", NULL);
+
create_base = krb5_config_get_string(context, NULL, "kdc",
"hdb-ldap-create-base", NULL);
if (create_base == NULL)
@@ -1875,7 +2005,7 @@ hdb_ldap_common(krb5_context context,
(*db)->hdb_master_key_set = 0;
(*db)->hdb_openp = 0;
- (*db)->hdb_capability_flags = 0;
+ (*db)->hdb_capability_flags = HDB_CAP_F_SHARED_DIRECTORY;
(*db)->hdb_open = LDAP_open;
(*db)->hdb_close = LDAP_close;
(*db)->hdb_fetch_kvno = LDAP_fetch_kvno;
@@ -1894,20 +2024,27 @@ hdb_ldap_common(krb5_context context,
return 0;
}
+#ifdef OPENLDAP_MODULE
+static
+#endif
+
krb5_error_code
hdb_ldap_create(krb5_context context, HDB ** db, const char *arg)
{
- return hdb_ldap_common(context, db, arg, "ldapi:///");
+ return hdb_ldap_common(context, db, arg, NULL);
}
+#ifdef OPENLDAP_MODULE
+static
+#endif
+
krb5_error_code
hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)
{
krb5_error_code ret;
char *search_base, *p;
- asprintf(&p, "ldapi:%s", arg);
- if (p == NULL) {
+ if (asprintf(&p, "ldapi:%s", arg) == -1 || p == NULL) {
*db = NULL;
krb5_set_error_message(context, ENOMEM, "out of memory");
return ENOMEM;
@@ -1928,19 +2065,33 @@ hdb_ldapi_create(krb5_context context, HDB ** db, const char *arg)
}
#ifdef OPENLDAP_MODULE
+static krb5_error_code
+init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
+ return 0;
+}
-struct hdb_so_method hdb_ldap_interface = {
+static void
+fini(void *ctx)
+{
+}
+
+struct hdb_method hdb_ldap_interface = {
HDB_INTERFACE_VERSION,
+ init,
+ fini,
"ldap",
hdb_ldap_create
};
-struct hdb_so_method hdb_ldapi_interface = {
+struct hdb_method hdb_ldapi_interface = {
HDB_INTERFACE_VERSION,
+ init,
+ fini,
"ldapi",
hdb_ldapi_create
};
-
#endif
#endif /* OPENLDAP */
diff --git a/lib/hdb/hdb-mdb.c b/lib/hdb/hdb-mdb.c
new file mode 100644
index 000000000000..920d7780e844
--- /dev/null
+++ b/lib/hdb/hdb-mdb.c
@@ -0,0 +1,399 @@
+/*
+ * Copyright (c) 1997 - 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * Copyright (c) 2011 - Howard Chu, Symas Corp.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+
+#if HAVE_LMDB
+
+/* LMDB */
+
+#include <lmdb.h>
+
+#define KILO 1024
+
+typedef struct mdb_info {
+ MDB_env *e;
+ MDB_txn *t;
+ MDB_dbi d;
+ MDB_cursor *c;
+} mdb_info;
+
+static krb5_error_code
+DB_close(krb5_context context, HDB *db)
+{
+ mdb_info *mi = (mdb_info *)db->hdb_db;
+
+ mdb_cursor_close(mi->c);
+ mdb_txn_abort(mi->t);
+ mdb_env_close(mi->e);
+ mi->c = 0;
+ mi->t = 0;
+ mi->e = 0;
+ return 0;
+}
+
+static krb5_error_code
+DB_destroy(krb5_context context, HDB *db)
+{
+ krb5_error_code ret;
+
+ ret = hdb_clear_master_key (context, db);
+ free(db->hdb_name);
+ free(db->hdb_db);
+ free(db);
+ return ret;
+}
+
+static krb5_error_code
+DB_lock(krb5_context context, HDB *db, int operation)
+{
+ db->lock_count++;
+ return 0;
+}
+
+static krb5_error_code
+DB_unlock(krb5_context context, HDB *db)
+{
+ if (db->lock_count > 1) {
+ db->lock_count--;
+ return 0;
+ }
+ heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
+ db->lock_count--;
+ return 0;
+}
+
+
+static krb5_error_code
+DB_seq(krb5_context context, HDB *db,
+ unsigned flags, hdb_entry_ex *entry, int flag)
+{
+ mdb_info *mi = db->hdb_db;
+ MDB_val key, value;
+ krb5_data key_data, data;
+ int code;
+
+ key.mv_size = 0;
+ value.mv_size = 0;
+ code = mdb_cursor_get(mi->c, &key, &value, flag);
+ if (code == MDB_NOTFOUND)
+ return HDB_ERR_NOENTRY;
+ if (code)
+ return code;
+
+ key_data.data = key.mv_data;
+ key_data.length = key.mv_size;
+ data.data = value.mv_data;
+ data.length = value.mv_size;
+ memset(entry, 0, sizeof(*entry));
+ if (hdb_value2entry(context, &data, &entry->entry))
+ return DB_seq(context, db, flags, entry, MDB_NEXT);
+ if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
+ code = hdb_unseal_keys (context, db, &entry->entry);
+ if (code)
+ hdb_free_entry (context, entry);
+ }
+ if (entry->entry.principal == NULL) {
+ entry->entry.principal = malloc(sizeof(*entry->entry.principal));
+ if (entry->entry.principal == NULL) {
+ hdb_free_entry (context, entry);
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ } else {
+ hdb_key2principal(context, &key_data, entry->entry.principal);
+ }
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+DB_firstkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ mdb_info *mi = db->hdb_db;
+ int code;
+
+ /* Always start with a fresh cursor to pick up latest DB state */
+ if (mi->t)
+ mdb_txn_abort(mi->t);
+
+ code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &mi->t);
+ if (code)
+ return code;
+
+ code = mdb_cursor_open(mi->t, mi->d, &mi->c);
+ if (code)
+ return code;
+
+ return DB_seq(context, db, flags, entry, MDB_FIRST);
+}
+
+
+static krb5_error_code
+DB_nextkey(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
+{
+ return DB_seq(context, db, flags, entry, MDB_NEXT);
+}
+
+static krb5_error_code
+DB_rename(krb5_context context, HDB *db, const char *new_name)
+{
+ int ret;
+ char *old, *new;
+
+ if (strncmp(new_name, "mdb:", sizeof("mdb:") - 1) == 0)
+ new_name += sizeof("mdb:") - 1;
+ else if (strncmp(new_name, "lmdb:", sizeof("lmdb:") - 1) == 0)
+ new_name += sizeof("lmdb:") - 1;
+ if (asprintf(&old, "%s.mdb", db->hdb_name) == -1)
+ return ENOMEM;
+ if (asprintf(&new, "%s.mdb", new_name) == -1) {
+ free(old);
+ return ENOMEM;
+ }
+ ret = rename(old, new);
+ free(old);
+ free(new);
+ if(ret)
+ return errno;
+
+ free(db->hdb_name);
+ db->hdb_name = strdup(new_name);
+ return 0;
+}
+
+static krb5_error_code
+DB__get(krb5_context context, HDB *db, krb5_data key, krb5_data *reply)
+{
+ mdb_info *mi = (mdb_info*)db->hdb_db;
+ MDB_txn *txn;
+ MDB_val k, v;
+ int code;
+
+ k.mv_data = key.data;
+ k.mv_size = key.length;
+
+ code = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &txn);
+ if (code)
+ return code;
+
+ code = mdb_get(txn, mi->d, &k, &v);
+ if (code == 0)
+ krb5_data_copy(reply, v.mv_data, v.mv_size);
+ mdb_txn_abort(txn);
+ if(code == MDB_NOTFOUND)
+ return HDB_ERR_NOENTRY;
+ return code;
+}
+
+static krb5_error_code
+DB__put(krb5_context context, HDB *db, int replace,
+ krb5_data key, krb5_data value)
+{
+ mdb_info *mi = (mdb_info*)db->hdb_db;
+ MDB_txn *txn;
+ MDB_val k, v;
+ int code;
+
+ k.mv_data = key.data;
+ k.mv_size = key.length;
+ v.mv_data = value.data;
+ v.mv_size = value.length;
+
+ code = mdb_txn_begin(mi->e, NULL, 0, &txn);
+ if (code)
+ return code;
+
+ code = mdb_put(txn, mi->d, &k, &v, replace ? 0 : MDB_NOOVERWRITE);
+ if (code)
+ mdb_txn_abort(txn);
+ else
+ code = mdb_txn_commit(txn);
+ if(code == MDB_KEYEXIST)
+ return HDB_ERR_EXISTS;
+ return code;
+}
+
+static krb5_error_code
+DB__del(krb5_context context, HDB *db, krb5_data key)
+{
+ mdb_info *mi = (mdb_info*)db->hdb_db;
+ MDB_txn *txn;
+ MDB_val k;
+ krb5_error_code code;
+
+ k.mv_data = key.data;
+ k.mv_size = key.length;
+
+ code = mdb_txn_begin(mi->e, NULL, 0, &txn);
+ if (code)
+ return code;
+
+ code = mdb_del(txn, mi->d, &k, NULL);
+ if (code)
+ mdb_txn_abort(txn);
+ else
+ code = mdb_txn_commit(txn);
+ if(code == MDB_NOTFOUND)
+ return HDB_ERR_NOENTRY;
+ return code;
+}
+
+static krb5_error_code
+DB_open(krb5_context context, HDB *db, int flags, mode_t mode)
+{
+ mdb_info *mi = (mdb_info *)db->hdb_db;
+ MDB_txn *txn;
+ char *fn;
+ krb5_error_code ret;
+ int myflags = MDB_NOSUBDIR, tmp;
+
+ if((flags & O_ACCMODE) == O_RDONLY)
+ myflags |= MDB_RDONLY;
+
+ if (asprintf(&fn, "%s.mdb", db->hdb_name) == -1)
+ return krb5_enomem(context);
+ if (mdb_env_create(&mi->e)) {
+ free(fn);
+ return krb5_enomem(context);
+ }
+
+ tmp = krb5_config_get_int_default(context, NULL, 0, "kdc",
+ "hdb-mdb-maxreaders", NULL);
+ if (tmp) {
+ ret = mdb_env_set_maxreaders(mi->e, tmp);
+ if (ret) {
+ free(fn);
+ krb5_set_error_message(context, ret, "setting maxreaders on %s: %s",
+ db->hdb_name, mdb_strerror(ret));
+ return ret;
+ }
+ }
+
+ tmp = krb5_config_get_int_default(context, NULL, 0, "kdc",
+ "hdb-mdb-mapsize", NULL);
+ if (tmp) {
+ size_t maps = tmp;
+ maps *= KILO;
+ ret = mdb_env_set_mapsize(mi->e, maps);
+ if (ret) {
+ free(fn);
+ krb5_set_error_message(context, ret, "setting mapsize on %s: %s",
+ db->hdb_name, mdb_strerror(ret));
+ return ret;
+ }
+ }
+
+ ret = mdb_env_open(mi->e, fn, myflags, mode);
+ free(fn);
+ if (ret) {
+fail:
+ mdb_env_close(mi->e);
+ mi->e = 0;
+ krb5_set_error_message(context, ret, "opening %s: %s",
+ db->hdb_name, mdb_strerror(ret));
+ return ret;
+ }
+
+ ret = mdb_txn_begin(mi->e, NULL, MDB_RDONLY, &txn);
+ if (ret)
+ goto fail;
+
+ ret = mdb_open(txn, NULL, 0, &mi->d);
+ mdb_txn_abort(txn);
+ if (ret)
+ goto fail;
+
+ if((flags & O_ACCMODE) == O_RDONLY)
+ ret = hdb_check_db_format(context, db);
+ else
+ ret = hdb_init_db(context, db);
+ if(ret == HDB_ERR_NOENTRY)
+ return 0;
+ if (ret) {
+ DB_close(context, db);
+ krb5_set_error_message(context, ret, "hdb_open: failed %s database %s",
+ (flags & O_ACCMODE) == O_RDONLY ?
+ "checking format of" : "initialize",
+ db->hdb_name);
+ }
+
+ return ret;
+}
+
+krb5_error_code
+hdb_mdb_create(krb5_context context, HDB **db,
+ const char *filename)
+{
+ *db = calloc(1, sizeof(**db));
+ if (*db == NULL) {
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+
+ (*db)->hdb_db = calloc(1, sizeof(mdb_info));
+ if ((*db)->hdb_db == NULL) {
+ free(*db);
+ *db = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*db)->hdb_name = strdup(filename);
+ if ((*db)->hdb_name == NULL) {
+ free((*db)->hdb_db);
+ free(*db);
+ *db = NULL;
+ krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
+ return ENOMEM;
+ }
+ (*db)->hdb_master_key_set = 0;
+ (*db)->hdb_openp = 0;
+ (*db)->hdb_capability_flags = HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL;
+ (*db)->hdb_open = DB_open;
+ (*db)->hdb_close = DB_close;
+ (*db)->hdb_fetch_kvno = _hdb_fetch_kvno;
+ (*db)->hdb_store = _hdb_store;
+ (*db)->hdb_remove = _hdb_remove;
+ (*db)->hdb_firstkey = DB_firstkey;
+ (*db)->hdb_nextkey= DB_nextkey;
+ (*db)->hdb_lock = DB_lock;
+ (*db)->hdb_unlock = DB_unlock;
+ (*db)->hdb_rename = DB_rename;
+ (*db)->hdb__get = DB__get;
+ (*db)->hdb__put = DB__put;
+ (*db)->hdb__del = DB__del;
+ (*db)->hdb_destroy = DB_destroy;
+ return 0;
+}
+#endif /* HAVE_LMDB */
diff --git a/lib/hdb/hdb-mitdb.c b/lib/hdb/hdb-mitdb.c
index cd619b3b8eb4..4e4fcdc58556 100644
--- a/lib/hdb/hdb-mitdb.c
+++ b/lib/hdb/hdb-mitdb.c
@@ -91,18 +91,28 @@ salt:
#include "hdb_locl.h"
-#define KDB_V1_BASE_LENGTH 38
-
-#if HAVE_DB1
+static void
+attr_to_flags(unsigned attr, HDBFlags *flags)
+{
+ flags->postdate = !(attr & KRB5_KDB_DISALLOW_POSTDATED);
+ flags->forwardable = !(attr & KRB5_KDB_DISALLOW_FORWARDABLE);
+ flags->initial = !!(attr & KRB5_KDB_DISALLOW_TGT_BASED);
+ flags->renewable = !(attr & KRB5_KDB_DISALLOW_RENEWABLE);
+ flags->proxiable = !(attr & KRB5_KDB_DISALLOW_PROXIABLE);
+ /* DUP_SKEY */
+ flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX);
+ flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH);
+ flags->require_hwauth = !!(attr & KRB5_KDB_REQUIRES_HW_AUTH);
+ flags->server = !(attr & KRB5_KDB_DISALLOW_SVR);
+ flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE);
+ flags->client = 1; /* XXX */
+}
-#if defined(HAVE_DB_185_H)
-#include <db_185.h>
-#elif defined(HAVE_DB_H)
-#include <db.h>
-#endif
+#define KDB_V1_BASE_LENGTH 38
#define CHECK(x) do { if ((x)) goto out; } while(0)
+#ifdef HAVE_DB1
static krb5_error_code
mdb_principal2key(krb5_context context,
krb5_const_principal principal,
@@ -118,6 +128,7 @@ mdb_principal2key(krb5_context context,
key->length = strlen(str) + 1;
return 0;
}
+#endif /* HAVE_DB1 */
#define KRB5_KDB_SALTTYPE_NORMAL 0
#define KRB5_KDB_SALTTYPE_V4 1
@@ -128,10 +139,10 @@ mdb_principal2key(krb5_context context,
#define KRB5_KDB_SALTTYPE_CERTHASH 6
static krb5_error_code
-fix_salt(krb5_context context, hdb_entry *ent, int key_num)
+fix_salt(krb5_context context, hdb_entry *ent, Key *k)
{
krb5_error_code ret;
- Salt *salt = ent->keys.val[key_num].salt;
+ Salt *salt = k->salt;
/* fix salt type */
switch((int)salt->type) {
case KRB5_KDB_SALTTYPE_NORMAL:
@@ -187,8 +198,8 @@ fix_salt(krb5_context context, hdb_entry *ent, int key_num)
break;
case KRB5_KDB_SALTTYPE_CERTHASH:
krb5_data_free(&salt->salt);
- free(ent->keys.val[key_num].salt);
- ent->keys.val[key_num].salt = NULL;
+ free(k->salt);
+ k->salt = NULL;
break;
default:
abort();
@@ -197,16 +208,216 @@ fix_salt(krb5_context context, hdb_entry *ent, int key_num)
}
+/**
+ * This function takes a key from a krb5_storage from an MIT KDB encoded
+ * entry and places it in the given Key object.
+ *
+ * @param context Context
+ * @param entry HDB entry
+ * @param sp krb5_storage with current offset set to the beginning of a
+ * key
+ * @param version See comments in caller body for the backstory on this
+ * @param k Key * to load the key into
+ */
+static krb5_error_code
+mdb_keyvalue2key(krb5_context context, hdb_entry *entry, krb5_storage *sp, uint16_t version, Key *k)
+{
+ size_t i;
+ uint16_t u16, type;
+ krb5_error_code ret;
+
+ k->mkvno = malloc(sizeof(*k->mkvno));
+ if (k->mkvno == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ *k->mkvno = 1;
+
+ for (i = 0; i < version; i++) {
+ CHECK(ret = krb5_ret_uint16(sp, &type));
+ CHECK(ret = krb5_ret_uint16(sp, &u16));
+ if (i == 0) {
+ /* This "version" means we have a key */
+ k->key.keytype = type;
+ /*
+ * MIT stores keys encrypted keys as {16-bit length
+ * of plaintext key, {encrypted key}}. The reason
+ * for this is that the Kerberos cryptosystem is not
+ * length-preserving. Heimdal's approach is to
+ * truncate the plaintext to the expected length of
+ * the key given its enctype, so we ignore this
+ * 16-bit length-of-plaintext-key field.
+ */
+ if (u16 > 2) {
+ krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
+ k->key.keyvalue.length = u16 - 2; /* adjust cipher len */
+ k->key.keyvalue.data = malloc(k->key.keyvalue.length);
+ krb5_storage_read(sp, k->key.keyvalue.data,
+ k->key.keyvalue.length);
+ } else {
+ /* We'll ignore this key; see our caller */
+ k->key.keyvalue.length = 0;
+ k->key.keyvalue.data = NULL;
+ krb5_storage_seek(sp, u16, SEEK_CUR); /* skip real length */
+ }
+ } else if (i == 1) {
+ /* This "version" means we have a salt */
+ k->salt = calloc(1, sizeof(*k->salt));
+ if (k->salt == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ k->salt->type = type;
+ if (u16 != 0) {
+ k->salt->salt.data = malloc(u16);
+ if (k->salt->salt.data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ k->salt->salt.length = u16;
+ krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
+ }
+ fix_salt(context, entry, k);
+ } else {
+ /*
+ * Whatever this "version" might be, we skip it
+ *
+ * XXX A krb5.conf parameter requesting that we log
+ * about strangeness like this, or return an error
+ * from here, might be nice.
+ */
+ krb5_storage_seek(sp, u16, SEEK_CUR);
+ }
+ }
+
+ return 0;
+
+out:
+ free_Key(k);
+ return ret;
+}
+
+
+static krb5_error_code
+add_1des_dup(krb5_context context, Keys *keys, Key *key, krb5_keytype keytype)
+{
+ key->key.keytype = keytype;
+ return add_Keys(keys, key);
+}
+
+/*
+ * This monstrosity is here so we can avoid having to do enctype
+ * similarity checking in the KDC. This helper function dups 1DES keys
+ * in a keyset for all the similar 1DES enctypes for which keys are
+ * missing. And, of course, we do this only if there's any 1DES keys in
+ * the keyset to begin with.
+ */
static krb5_error_code
-mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry *entry)
+dup_similar_keys_in_keyset(krb5_context context, Keys *keys)
+{
+ krb5_error_code ret;
+ size_t i, k;
+ Key key;
+ int keyset_has_1des_crc = 0;
+ int keyset_has_1des_md4 = 0;
+ int keyset_has_1des_md5 = 0;
+
+ memset(&key, 0, sizeof (key));
+ k = keys->len;
+ for (i = 0; i < keys->len; i++) {
+ if (keys->val[i].key.keytype == ETYPE_DES_CBC_CRC) {
+ keyset_has_1des_crc = 1;
+ if (k == keys->len)
+ k = i;
+ } else if (keys->val[i].key.keytype == ETYPE_DES_CBC_MD4) {
+ keyset_has_1des_crc = 1;
+ if (k == keys->len)
+ k = i;
+ } else if (keys->val[i].key.keytype == ETYPE_DES_CBC_MD5) {
+ keyset_has_1des_crc = 1;
+ if (k == keys->len)
+ k = i;
+ }
+ }
+ if (k == keys->len)
+ return 0;
+
+ ret = copy_Key(&keys->val[k], &key);
+ if (ret)
+ return ret;
+ if (!keyset_has_1des_crc) {
+ ret = add_1des_dup(context, keys, &key, ETYPE_DES_CBC_CRC);
+ if (ret)
+ goto out;
+ }
+ if (!keyset_has_1des_md4) {
+ ret = add_1des_dup(context, keys, &key, ETYPE_DES_CBC_MD4);
+ if (ret)
+ goto out;
+ }
+ if (!keyset_has_1des_md5) {
+ ret = add_1des_dup(context, keys, &key, ETYPE_DES_CBC_MD5);
+ if (ret)
+ goto out;
+ }
+
+out:
+ free_Key(&key);
+ return ret;
+}
+
+
+static krb5_error_code
+dup_similar_keys(krb5_context context, hdb_entry *entry)
+{
+ krb5_error_code ret;
+ HDB_Ext_KeySet *hist_keys;
+ HDB_extension *extp;
+ size_t i;
+
+ ret = dup_similar_keys_in_keyset(context, &entry->keys);
+ if (ret)
+ return ret;
+ extp = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys);
+ if (extp == NULL)
+ return 0;
+
+ hist_keys = &extp->data.u.hist_keys;
+ for (i = 0; i < hist_keys->len; i++) {
+ ret = dup_similar_keys_in_keyset(context, &hist_keys->val[i].keys);
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+
+/**
+ * This function parses an MIT krb5 encoded KDB entry and fills in the
+ * given HDB entry with it.
+ *
+ * @param context krb5_context
+ * @param data Encoded MIT KDB entry
+ * @param target_kvno Desired kvno, or 0 for the entry's current kvno
+ * @param entry Desired kvno, or 0 for the entry's current kvno
+ */
+krb5_error_code
+_hdb_mdb_value2entry(krb5_context context, krb5_data *data,
+ krb5_kvno target_kvno, hdb_entry *entry)
{
krb5_error_code ret;
krb5_storage *sp;
+ Key k;
+ krb5_kvno key_kvno;
uint32_t u32;
uint16_t u16, num_keys, num_tl;
- size_t i, j;
+ ssize_t sz;
+ size_t i;
char *p;
+ memset(&k, 0, sizeof (k));
+ memset(entry, 0, sizeof(*entry));
+
sp = krb5_storage_from_data(data);
if (sp == NULL) {
krb5_set_error_message(context, ENOMEM, "out of memory");
@@ -228,24 +439,14 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
* XXX But... surely we ought to log about this extra data, or skip
* it, or something, in case anyone has MIT KDBs with ancient
* entries in them... Logging would allow the admin to know which
- * entries to dump with MIT krb5's kdb5_util.
+ * entries to dump with MIT krb5's kdb5_util. But logging would be
+ * noisy. For now we do nothing.
*/
CHECK(ret = krb5_ret_uint16(sp, &u16));
if (u16 != KDB_V1_BASE_LENGTH) { ret = EINVAL; goto out; }
/* 32: attributes */
CHECK(ret = krb5_ret_uint32(sp, &u32));
- entry->flags.postdate = !(u32 & KRB5_KDB_DISALLOW_POSTDATED);
- entry->flags.forwardable = !(u32 & KRB5_KDB_DISALLOW_FORWARDABLE);
- entry->flags.initial = !!(u32 & KRB5_KDB_DISALLOW_TGT_BASED);
- entry->flags.renewable = !(u32 & KRB5_KDB_DISALLOW_RENEWABLE);
- entry->flags.proxiable = !(u32 & KRB5_KDB_DISALLOW_PROXIABLE);
- /* DUP_SKEY */
- entry->flags.invalid = !!(u32 & KRB5_KDB_DISALLOW_ALL_TIX);
- entry->flags.require_preauth =!!(u32 & KRB5_KDB_REQUIRES_PRE_AUTH);
- entry->flags.require_hwauth =!!(u32 & KRB5_KDB_REQUIRES_HW_AUTH);
- entry->flags.server = !(u32 & KRB5_KDB_DISALLOW_SVR);
- entry->flags.change_pw = !!(u32 & KRB5_KDB_PWCHANGE_SERVICE);
- entry->flags.client = 1; /* XXX */
+ attr_to_flags(u32, &entry->flags);
/* 32: max time */
CHECK(ret = krb5_ret_uint32(sp, &u32));
@@ -296,7 +497,11 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
ret = ENOMEM;
goto out;
}
- krb5_storage_read(sp, p, u16);
+ sz = krb5_storage_read(sp, p, u16);
+ if (sz != u16) {
+ ret = EINVAL; /* XXX */
+ goto out;
+ }
p[u16] = '\0';
CHECK(ret = krb5_parse_name(context, p, &entry->principal));
free(p);
@@ -305,12 +510,53 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
16: tl data type
16: tl data length
length: length */
+#define mit_KRB5_TL_LAST_PWD_CHANGE 1
+#define mit_KRB5_TL_MOD_PRINC 2
for (i = 0; i < num_tl; i++) {
+ int tl_type;
+ krb5_principal modby;
/* 16: TL data type */
CHECK(ret = krb5_ret_uint16(sp, &u16));
+ tl_type = u16;
/* 16: TL data length */
CHECK(ret = krb5_ret_uint16(sp, &u16));
- krb5_storage_seek(sp, u16, SEEK_CUR);
+ /*
+ * For rollback to MIT purposes we really must understand some
+ * TL data!
+ *
+ * XXX Move all this to separate functions, one per-TL type.
+ */
+ switch (tl_type) {
+ case mit_KRB5_TL_LAST_PWD_CHANGE:
+ CHECK(ret = krb5_ret_uint32(sp, &u32));
+ CHECK(ret = hdb_entry_set_pw_change_time(context, entry, u32));
+ break;
+ case mit_KRB5_TL_MOD_PRINC:
+ if (u16 < 5) {
+ ret = EINVAL; /* XXX */
+ goto out;
+ }
+ CHECK(ret = krb5_ret_uint32(sp, &u32)); /* mod time */
+ p = malloc(u16 - 4 + 1);
+ if (!p) {
+ ret = ENOMEM;
+ goto out;
+ }
+ p[u16 - 4] = '\0';
+ sz = krb5_storage_read(sp, p, u16 - 4);
+ if (sz != u16 - 4) {
+ ret = EINVAL; /* XXX */
+ goto out;
+ }
+ CHECK(ret = krb5_parse_name(context, p, &modby));
+ ret = hdb_set_last_modified_by(context, entry, modby, u32);
+ krb5_free_principal(context, modby);
+ free(p);
+ break;
+ default:
+ krb5_storage_seek(sp, u16, SEEK_CUR);
+ break;
+ }
}
/*
* for num key data times
@@ -326,140 +572,92 @@ mdb_value2entry(krb5_context context, krb5_data *data, krb5_kvno kvno, hdb_entry
* That's right... hold that gag reflex, you can do it.
*/
for (i = 0; i < num_keys; i++) {
- int keep = 0;
uint16_t version;
- void *ptr;
CHECK(ret = krb5_ret_uint16(sp, &u16));
version = u16;
CHECK(ret = krb5_ret_uint16(sp, &u16));
+ key_kvno = u16;
- /*
- * First time through, and until we find one matching key,
- * entry->kvno == 0.
- */
- if ((entry->kvno < u16) && (kvno == 0 || kvno == u16)) {
- keep = 1;
- entry->kvno = u16;
+ ret = mdb_keyvalue2key(context, entry, sp, version, &k);
+ if (ret)
+ goto out;
+ if (k.key.keytype == 0 || k.key.keyvalue.length == 0) {
/*
- * Found a higher kvno than earlier, so free the old highest
- * kvno keys.
+ * Older MIT KDBs may have enctype 0 / length 0 keys. We
+ * ignore these.
+ */
+ free_Key(&k);
+ continue;
+ }
+
+ if ((target_kvno == 0 && entry->kvno < key_kvno) ||
+ (target_kvno == key_kvno && entry->kvno != target_kvno)) {
+ /*
+ * MIT's KDB doesn't keep track of kvno. The highest kvno
+ * is the current kvno, and we just found a new highest
+ * kvno or the desired kvno.
+ *
+ * Note that there's no guarantee of any key ordering, but
+ * generally MIT KDB entries have keys in strictly
+ * descending kvno order.
*
- * XXX Of course, we actually want to extract the old kvnos
- * as well, for some of the kadm5 APIs. We shouldn't free
- * these keys, but keep them elsewhere.
+ * XXX We do assume that keys are clustered by kvno. If
+ * not, then bad. It might be possible to construct
+ * non-clustered keys via the kadm5 API. It wouldn't be
+ * hard to cope with this, since if it happens the worst
+ * that will happen is that some of the current keys can be
+ * found in the history extension, and we could just pull
+ * them back out in that case.
*/
- for (j = 0; j < entry->keys.len; j++)
- free_Key(&entry->keys.val[j]);
- free(entry->keys.val);
- entry->keys.len = 0;
- entry->keys.val = NULL;
- } else if (entry->kvno == u16)
- /* Accumulate keys */
- keep = 1;
-
- if (keep) {
- Key *k;
-
- ptr = realloc(entry->keys.val, sizeof(entry->keys.val[0]) * (entry->keys.len + 1));
- if (ptr == NULL) {
- ret = ENOMEM;
+ ret = hdb_add_current_keys_to_history(context, entry);
+ if (ret)
goto out;
- }
- entry->keys.val = ptr;
-
- /* k points to current Key */
- k = &entry->keys.val[entry->keys.len];
-
- memset(k, 0, sizeof(*k));
- entry->keys.len += 1;
-
- k->mkvno = malloc(sizeof(*k->mkvno));
- if (k->mkvno == NULL) {
- ret = ENOMEM;
+ free_Keys(&entry->keys);
+ ret = add_Keys(&entry->keys, &k);
+ free_Key(&k);
+ if (ret)
goto out;
- }
- *k->mkvno = 1;
-
- for (j = 0; j < version; j++) {
- uint16_t type;
- CHECK(ret = krb5_ret_uint16(sp, &type));
- CHECK(ret = krb5_ret_uint16(sp, &u16));
- if (j == 0) {
- /* This "version" means we have a key */
- k->key.keytype = type;
- if (u16 < 2) {
- ret = EINVAL;
- goto out;
- }
- /*
- * MIT stores keys encrypted keys as {16-bit length
- * of plaintext key, {encrypted key}}. The reason
- * for this is that the Kerberos cryptosystem is not
- * length-preserving. Heimdal's approach is to
- * truncate the plaintext to the expected length of
- * the key given its enctype, so we ignore this
- * 16-bit length-of-plaintext-key field.
- */
- krb5_storage_seek(sp, 2, SEEK_CUR); /* skip real length */
- k->key.keyvalue.length = u16 - 2; /* adjust cipher len */
- k->key.keyvalue.data = malloc(k->key.keyvalue.length);
- krb5_storage_read(sp, k->key.keyvalue.data,
- k->key.keyvalue.length);
- } else if (j == 1) {
- /* This "version" means we have a salt */
- k->salt = calloc(1, sizeof(*k->salt));
- if (k->salt == NULL) {
- ret = ENOMEM;
- goto out;
- }
- k->salt->type = type;
- if (u16 != 0) {
- k->salt->salt.data = malloc(u16);
- if (k->salt->salt.data == NULL) {
- ret = ENOMEM;
- goto out;
- }
- k->salt->salt.length = u16;
- krb5_storage_read(sp, k->salt->salt.data, k->salt->salt.length);
- }
- fix_salt(context, entry, entry->keys.len - 1);
- } else {
- /*
- * Whatever this "version" might be, we skip it
- *
- * XXX A krb5.conf parameter requesting that we log
- * about strangeness like this, or return an error
- * from here, might be nice.
- */
- krb5_storage_seek(sp, u16, SEEK_CUR);
- }
- }
- } else {
+ entry->kvno = key_kvno;
+ continue;
+ }
+
+ if (entry->kvno == key_kvno) {
/*
- * XXX For now we skip older kvnos, but we should extract
- * them...
+ * Note that if key_kvno == 0 and target_kvno == 0 then we
+ * end up adding those keys here. Yeah, kvno 0 is very
+ * special for us, but just in case, we keep such keys.
*/
- for (j = 0; j < version; j++) {
- /* enctype */
- CHECK(ret = krb5_ret_uint16(sp, &u16));
- /* encrypted key (or plaintext salt) */
- CHECK(ret = krb5_ret_uint16(sp, &u16));
- krb5_storage_seek(sp, u16, SEEK_CUR);
- }
+ ret = add_Keys(&entry->keys, &k);
+ free_Key(&k);
+ if (ret)
+ goto out;
+ entry->kvno = key_kvno;
+ } else {
+ ret = hdb_add_history_key(context, entry, key_kvno, &k);
+ if (ret)
+ goto out;
+ free_Key(&k);
}
}
- if (entry->kvno == 0 && kvno != 0) {
- ret = HDB_ERR_NOT_FOUND_HERE;
+ if (target_kvno != 0 && entry->kvno != target_kvno) {
+ ret = HDB_ERR_KVNO_NOT_FOUND;
goto out;
}
- return 0;
- out:
+ krb5_storage_free(sp);
+
+ return dup_similar_keys(context, entry);
+
+out:
+ krb5_storage_free(sp);
+
if (ret == HEIM_ERR_EOF)
/* Better error code than "end of file" */
ret = HEIM_ERR_BAD_HDBENT_ENCODING;
+ free_hdb_entry(entry);
+ free_Key(&k);
return ret;
}
@@ -471,6 +669,14 @@ mdb_entry2value(krb5_context context, hdb_entry *entry, krb5_data *data)
}
#endif
+#if HAVE_DB1
+
+#if defined(HAVE_DB_185_H)
+#include <db_185.h>
+#elif defined(HAVE_DB_H)
+#include <db.h>
+#endif
+
static krb5_error_code
mdb_close(krb5_context context, HDB *db)
@@ -496,12 +702,24 @@ mdb_lock(krb5_context context, HDB *db, int operation)
{
DB *d = (DB*)db->hdb_db;
int fd = (*d->fd)(d);
+ krb5_error_code ret;
+
+ if (db->lock_count > 1) {
+ db->lock_count++;
+ if (db->lock_type == HDB_WLOCK || db->lock_count == operation)
+ return 0;
+ }
+
if(fd < 0) {
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
"Can't lock database: %s", db->hdb_name);
return HDB_ERR_CANT_LOCK_DB;
}
- return hdb_lock(fd, operation);
+ ret = hdb_lock(fd, operation);
+ if (ret)
+ return ret;
+ db->lock_count++;
+ return 0;
}
static krb5_error_code
@@ -509,6 +727,14 @@ mdb_unlock(krb5_context context, HDB *db)
{
DB *d = (DB*)db->hdb_db;
int fd = (*d->fd)(d);
+
+ if (db->lock_count > 1) {
+ db->lock_count--;
+ return 0;
+ }
+ heim_assert(db->lock_count == 1, "HDB lock/unlock sequence does not match");
+ db->lock_count--;
+
if(fd < 0) {
krb5_set_error_message(context, HDB_ERR_CANT_LOCK_DB,
"Can't unlock database: %s", db->hdb_name);
@@ -551,7 +777,7 @@ mdb_seq(krb5_context context, HDB *db,
data.length = value.size;
memset(entry, 0, sizeof(*entry));
- if (mdb_value2entry(context, &data, 0, &entry->entry))
+ if (_hdb_mdb_value2entry(context, &data, 0, &entry->entry))
return mdb_seq(context, db, flags, entry, R_NEXT);
if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
@@ -581,19 +807,25 @@ static krb5_error_code
mdb_rename(krb5_context context, HDB *db, const char *new_name)
{
int ret;
- char *old, *new;
+ char *old = NULL;
+ char *new = NULL;
- asprintf(&old, "%s.db", db->hdb_name);
- asprintf(&new, "%s.db", new_name);
+ if (asprintf(&old, "%s.db", db->hdb_name) < 0)
+ goto out;
+ if (asprintf(&new, "%s.db", new_name) < 0)
+ goto out;
ret = rename(old, new);
- free(old);
- free(new);
if(ret)
- return errno;
+ goto out;
free(db->hdb_name);
db->hdb_name = strdup(new_name);
- return 0;
+ errno = 0;
+
+out:
+ free(old);
+ free(new);
+ return errno;
}
static krb5_error_code
@@ -684,24 +916,26 @@ mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
unsigned flags, krb5_kvno kvno, hdb_entry_ex *entry)
{
krb5_data key, value;
- krb5_error_code code;
+ krb5_error_code ret;
- code = mdb_principal2key(context, principal, &key);
- if (code)
- return code;
- code = db->hdb__get(context, db, key, &value);
+ ret = mdb_principal2key(context, principal, &key);
+ if (ret)
+ return ret;
+ ret = db->hdb__get(context, db, key, &value);
krb5_data_free(&key);
- if(code)
- return code;
- code = mdb_value2entry(context, &value, kvno, &entry->entry);
+ if(ret)
+ return ret;
+ ret = _hdb_mdb_value2entry(context, &value, kvno, &entry->entry);
krb5_data_free(&value);
- if (code)
- return code;
+ if (ret)
+ return ret;
if (db->hdb_master_key_set && (flags & HDB_F_DECRYPT)) {
- code = hdb_unseal_keys (context, db, &entry->entry);
- if (code)
+ ret = hdb_unseal_keys (context, db, &entry->entry);
+ if (ret) {
hdb_free_entry(context, entry);
+ return ret;
+ }
}
return 0;
@@ -710,15 +944,83 @@ mdb_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal principal,
static krb5_error_code
mdb_store(krb5_context context, HDB *db, unsigned flags, hdb_entry_ex *entry)
{
- krb5_set_error_message(context, EINVAL, "can't set principal in mdb");
- return EINVAL;
+ krb5_error_code ret;
+ krb5_storage *sp = NULL;
+ krb5_storage *spent = NULL;
+ krb5_data line = { 0, 0 };
+ krb5_data kdb_ent = { 0, 0 };
+ krb5_data key = { 0, 0 };
+ krb5_data value = { 0, 0 };
+ ssize_t sz;
+
+ if ((flags & HDB_F_PRECHECK) && (flags & HDB_F_REPLACE))
+ return 0;
+
+ if ((flags & HDB_F_PRECHECK)) {
+ ret = mdb_principal2key(context, entry->entry.principal, &key);
+ if (ret) return ret;
+ ret = db->hdb__get(context, db, key, &value);
+ krb5_data_free(&key);
+ if (ret == 0)
+ krb5_data_free(&value);
+ if (ret == HDB_ERR_NOENTRY)
+ return 0;
+ return ret ? ret : HDB_ERR_EXISTS;
+ }
+
+ sp = krb5_storage_emem();
+ if (!sp) return ENOMEM;
+ ret = _hdb_set_master_key_usage(context, db, 0); /* MIT KDB uses KU 0 */
+ ret = hdb_seal_keys(context, db, &entry->entry);
+ if (ret) return ret;
+ ret = entry2mit_string_int(context, sp, &entry->entry);
+ if (ret) goto out;
+ sz = krb5_storage_write(sp, "\n", 2); /* NUL-terminate */
+ ret = ENOMEM;
+ if (sz == -1) goto out;
+ ret = krb5_storage_to_data(sp, &line);
+ if (ret) goto out;
+
+ ret = ENOMEM;
+ spent = krb5_storage_emem();
+ if (!spent) goto out;
+ ret = _hdb_mit_dump2mitdb_entry(context, line.data, spent);
+ if (ret) goto out;
+ ret = krb5_storage_to_data(spent, &kdb_ent);
+ if (ret) goto out;
+ ret = mdb_principal2key(context, entry->entry.principal, &key);
+ if (ret) goto out;
+ ret = mdb__put(context, db, 1, key, kdb_ent);
+
+out:
+ if (sp)
+ krb5_storage_free(sp);
+ if (spent)
+ krb5_storage_free(spent);
+ krb5_data_free(&line);
+ krb5_data_free(&kdb_ent);
+ krb5_data_free(&key);
+
+ return ret;
}
static krb5_error_code
-mdb_remove(krb5_context context, HDB *db, krb5_const_principal principal)
+mdb_remove(krb5_context context, HDB *db,
+ unsigned flags, krb5_const_principal principal)
{
krb5_error_code code;
krb5_data key;
+ krb5_data value = { 0, 0 };
+
+ if ((flags & HDB_F_PRECHECK)) {
+ code = db->hdb__get(context, db, key, &value);
+ krb5_data_free(&key);
+ if (code == 0) {
+ krb5_data_free(&value);
+ return 0;
+ }
+ return code;
+ }
mdb_principal2key(context, principal, &key);
code = db->hdb__del(context, db, key);
@@ -730,40 +1032,47 @@ static krb5_error_code
mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
{
char *fn;
+ char *actual_fn;
krb5_error_code ret;
+ struct stat st;
- asprintf(&fn, "%s.db", db->hdb_name);
- if (fn == NULL) {
+ if (asprintf(&fn, "%s.db", db->hdb_name) < 0) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
- db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
- free(fn);
+ if (stat(fn, &st) == 0)
+ actual_fn = fn;
+ else
+ actual_fn = db->hdb_name;
+ db->hdb_db = dbopen(actual_fn, flags, mode, DB_BTREE, NULL);
if (db->hdb_db == NULL) {
switch (errno) {
#ifdef EFTYPE
case EFTYPE:
#endif
case EINVAL:
- db->hdb_db = dbopen(fn, flags, mode, DB_BTREE, NULL);
+ db->hdb_db = dbopen(actual_fn, flags, mode, DB_HASH, NULL);
}
}
+ free(fn);
- /* try to open without .db extension */
- if(db->hdb_db == NULL && errno == ENOENT)
- db->hdb_db = dbopen(db->hdb_name, flags, mode, DB_BTREE, NULL);
- if(db->hdb_db == NULL) {
+ if (db->hdb_db == NULL) {
ret = errno;
krb5_set_error_message(context, ret, "dbopen (%s): %s",
db->hdb_name, strerror(ret));
return ret;
}
- if((flags & O_ACCMODE) == O_RDONLY)
- ret = hdb_check_db_format(context, db);
- else
+#if 0
+ /*
+ * Don't do this -- MIT won't be able to handle the
+ * HDB_DB_FORMAT_ENTRY key.
+ */
+ if ((flags & O_ACCMODE) != O_RDONLY)
ret = hdb_init_db(context, db);
- if(ret == HDB_ERR_NOENTRY) {
+#endif
+ ret = hdb_check_db_format(context, db);
+ if (ret == HDB_ERR_NOENTRY) {
krb5_clear_error_message(context);
return 0;
}
@@ -778,8 +1087,8 @@ mdb_open(krb5_context context, HDB *db, int flags, mode_t mode)
}
krb5_error_code
-hdb_mdb_create(krb5_context context, HDB **db,
- const char *filename)
+hdb_mitdb_create(krb5_context context, HDB **db,
+ const char *filename)
{
*db = calloc(1, sizeof(**db));
if (*db == NULL) {
@@ -816,3 +1125,347 @@ hdb_mdb_create(krb5_context context, HDB **db,
}
#endif /* HAVE_DB1 */
+
+/*
+can have any number of princ stanzas.
+format is as follows (only \n indicates newlines)
+princ\t%d\t (%d is KRB5_KDB_V1_BASE_LENGTH, always 38)
+%d\t (strlen of principal e.g. shadow/foo@ANDREW.CMU.EDU)
+%d\t (number of tl_data)
+%d\t (number of key data, e.g. how many keys for this user)
+%d\t (extra data length)
+%s\t (principal name)
+%d\t (attributes)
+%d\t (max lifetime, seconds)
+%d\t (max renewable life, seconds)
+%d\t (expiration, seconds since epoch or 2145830400 for never)
+%d\t (password expiration, seconds, 0 for never)
+%d\t (last successful auth, seconds since epoch)
+%d\t (last failed auth, per above)
+%d\t (failed auth count)
+foreach tl_data 0 to number of tl_data - 1 as above
+ %d\t%d\t (data type, data length)
+ foreach tl_data 0 to length-1
+ %02x (tl data contents[element n])
+ except if tl_data length is 0
+ %d (always -1)
+ \t
+foreach key 0 to number of keys - 1 as above
+ %d\t%d\t (key data version, kvno)
+ foreach version 0 to key data version - 1 (a key or a salt)
+ %d\t%d\t(data type for this key, data length for this key)
+ foreach key data length 0 to length-1
+ %02x (key data contents[element n])
+ except if key_data length is 0
+ %d (always -1)
+ \t
+foreach extra data length 0 to length - 1
+ %02x (extra data part)
+unless no extra data
+ %d (always -1)
+;\n
+
+*/
+
+#if 0
+/* Why ever did we loop? */
+static char *
+nexttoken(char **p)
+{
+ char *q;
+ do {
+ q = strsep(p, " \t");
+ } while(q && *q == '\0');
+ return q;
+}
+#endif
+
+static char *
+nexttoken(char **p, size_t len, const char *what)
+{
+ char *q;
+
+ if (*p == NULL)
+ return NULL;
+
+ q = *p;
+ *p += len;
+ /* Must be followed by a delimiter (right?) */
+ if (strsep(p, " \t") != q + len) {
+ warnx("No tokens left in dump entry while looking for %s", what);
+ return NULL;
+ }
+ if (*q == '\0')
+ warnx("Empty last token in dump entry while looking for %s", what);
+ return q;
+}
+
+static size_t
+getdata(char **p, unsigned char *buf, size_t len, const char *what)
+{
+ size_t i;
+ int v;
+ char *q = nexttoken(p, 0, what);
+ if (q == NULL) {
+ warnx("Failed to find hex-encoded binary data (%s) in dump", what);
+ return 0;
+ }
+ i = 0;
+ while (*q && i < len) {
+ if (sscanf(q, "%02x", &v) != 1)
+ break;
+ buf[i++] = v;
+ q += 2;
+ }
+ return i;
+}
+
+static int
+getint(char **p, const char *what)
+{
+ int val;
+ char *q = nexttoken(p, 0, what);
+ if (!q) {
+ warnx("Failed to find a signed integer (%s) in dump", what);
+ return -1;
+ }
+ if (sscanf(q, "%d", &val) != 1)
+ return -1;
+ return val;
+}
+
+static unsigned int
+getuint(char **p, const char *what)
+{
+ int val;
+ char *q = nexttoken(p, 0, what);
+ if (!q) {
+ warnx("Failed to find an unsigned integer (%s) in dump", what);
+ return 0;
+ }
+ if (sscanf(q, "%u", &val) != 1)
+ return 0;
+ return val;
+}
+
+#define KRB5_KDB_SALTTYPE_NORMAL 0
+#define KRB5_KDB_SALTTYPE_V4 1
+#define KRB5_KDB_SALTTYPE_NOREALM 2
+#define KRB5_KDB_SALTTYPE_ONLYREALM 3
+#define KRB5_KDB_SALTTYPE_SPECIAL 4
+#define KRB5_KDB_SALTTYPE_AFS3 5
+
+#define CHECK_UINT(num) \
+ if ((num) < 0 || (num) > INT_MAX) return EINVAL
+#define CHECK_UINT16(num) \
+ if ((num) < 0 || (num) > 1<<15) return EINVAL
+#define CHECK_NUM(num, maxv) \
+ if ((num) > (maxv)) return EINVAL
+
+/*
+ * This utility function converts an MIT dump entry to an MIT on-disk
+ * encoded entry, which can then be decoded with _hdb_mdb_value2entry().
+ * This allows us to have a single decoding function (_hdb_mdb_value2entry),
+ * which makes the code cleaner (less code duplication), if a bit less
+ * efficient. It also will allow us to have a function to dump an HDB
+ * entry in MIT format so we can dump HDB into MIT format for rollback
+ * purposes. And that will allow us to write to MIT KDBs, again
+ * somewhat inefficiently, also for migration/rollback purposes.
+ */
+int
+_hdb_mit_dump2mitdb_entry(krb5_context context, char *line, krb5_storage *sp)
+{
+ krb5_error_code ret = EINVAL;
+ char *p = line, *q;
+ char *princ;
+ ssize_t sz;
+ size_t i;
+ size_t princ_len;
+ unsigned int num_tl_data;
+ size_t num_key_data;
+ unsigned int attributes;
+ int tmp;
+
+ krb5_storage_set_byteorder(sp, KRB5_STORAGE_BYTEORDER_LE);
+
+ q = nexttoken(&p, 0, "record type (princ or policy)");
+ if (strcmp(q, "kdb5_util") == 0 || strcmp(q, "policy") == 0 ||
+ strcmp(q, "princ") != 0) {
+ warnx("Supposed MIT dump entry does not start with 'kdb5_util', "
+ "'policy', nor 'princ'");
+ return -1;
+ }
+ if (getint(&p, "constant '38'") != 38) {
+ warnx("Dump entry does not start with '38<TAB>'");
+ return EINVAL;
+ }
+#define KDB_V1_BASE_LENGTH 38
+ ret = krb5_store_int16(sp, KDB_V1_BASE_LENGTH);
+ if (ret) return ret;
+
+ princ_len = getuint(&p, "principal name length");
+ if (princ_len > (1<<15) - 1) {
+ warnx("Principal name in dump entry too long (%llu)",
+ (unsigned long long)princ_len);
+ return EINVAL;
+ }
+ num_tl_data = getuint(&p, "number of TL data");
+ num_key_data = getuint(&p, "number of key data");
+ getint(&p, "5th field, length of 'extra data'");
+ princ = nexttoken(&p, (int)princ_len, "principal name");
+ if (princ == NULL) {
+ warnx("Failed to read principal name (expected length %llu)",
+ (unsigned long long)princ_len);
+ return -1;
+ }
+
+ attributes = getuint(&p, "attributes");
+ ret = krb5_store_uint32(sp, attributes);
+ if (ret) return ret;
+
+ tmp = getint(&p, "max life");
+ CHECK_UINT(tmp);
+ ret = krb5_store_uint32(sp, tmp);
+ if (ret) return ret;
+
+ tmp = getint(&p, "max renewable life");
+ CHECK_UINT(tmp);
+ ret = krb5_store_uint32(sp, tmp);
+ if (ret) return ret;
+
+ tmp = getint(&p, "expiration");
+ CHECK_UINT(tmp);
+ ret = krb5_store_uint32(sp, tmp);
+ if (ret) return ret;
+
+ tmp = getint(&p, "pw expiration");
+ CHECK_UINT(tmp);
+ ret = krb5_store_uint32(sp, tmp);
+ if (ret) return ret;
+
+ tmp = getint(&p, "last auth");
+ CHECK_UINT(tmp);
+ ret = krb5_store_uint32(sp, tmp);
+ if (ret) return ret;
+
+ tmp = getint(&p, "last failed auth");
+ CHECK_UINT(tmp);
+ ret = krb5_store_uint32(sp, tmp);
+ if (ret) return ret;
+
+ tmp = getint(&p,"fail auth count");
+ CHECK_UINT(tmp);
+ ret = krb5_store_uint32(sp, tmp);
+ if (ret) return ret;
+
+ /* add TL data count */
+ CHECK_NUM(num_tl_data, 1023);
+ ret = krb5_store_uint16(sp, num_tl_data);
+ if (ret) return ret;
+
+ /* add key count */
+ CHECK_NUM(num_key_data, 1023);
+ ret = krb5_store_uint16(sp, num_key_data);
+ if (ret) return ret;
+
+ /* add principal unparsed name length and unparsed name */
+ princ_len = strlen(princ);
+ princ_len++; /* must count and write the NUL in the on-disk encoding */
+ ret = krb5_store_uint16(sp, princ_len);
+ if (ret) return ret;
+ sz = krb5_storage_write(sp, princ, princ_len);
+ if (sz == -1) return ENOMEM;
+
+ /* scan and write TL data */
+ for (i = 0; i < num_tl_data; i++) {
+ char *reading_what;
+ int tl_type, tl_length;
+ unsigned char *buf;
+
+ tl_type = getint(&p, "TL data type");
+ tl_length = getint(&p, "data length");
+
+ if (asprintf(&reading_what, "TL data type %d (length %d)",
+ tl_type, tl_length) < 0)
+ return ENOMEM;
+
+ /*
+ * XXX Leaking reading_what, but only on ENOMEM cases anyways,
+ * so we don't care.
+ */
+ CHECK_UINT16(tl_type);
+ ret = krb5_store_uint16(sp, tl_type);
+ if (ret) return ret;
+ CHECK_UINT16(tl_length);
+ ret = krb5_store_uint16(sp, tl_length);
+ if (ret) return ret;
+
+ if (tl_length) {
+ buf = malloc(tl_length);
+ if (!buf) return ENOMEM;
+ if (getdata(&p, buf, tl_length, reading_what) != tl_length)
+ return EINVAL;
+ sz = krb5_storage_write(sp, buf, tl_length);
+ free(buf);
+ if (sz == -1) return ENOMEM;
+ } else {
+ if (strcmp(nexttoken(&p, 0, "'-1' field"), "-1") != 0) return EINVAL;
+ }
+ free(reading_what);
+ }
+
+ for (i = 0; i < num_key_data; i++) {
+ unsigned char *buf;
+ int key_versions;
+ int kvno;
+ int keytype;
+ int keylen;
+ size_t k;
+
+ key_versions = getint(&p, "key data 'version'");
+ CHECK_UINT16(key_versions);
+ ret = krb5_store_int16(sp, key_versions);
+ if (ret) return ret;
+
+ kvno = getint(&p, "kvno");
+ CHECK_UINT16(kvno);
+ ret = krb5_store_int16(sp, kvno);
+ if (ret) return ret;
+
+ for (k = 0; k < key_versions; k++) {
+ keytype = getint(&p, "enctype");
+ CHECK_UINT16(keytype);
+ ret = krb5_store_int16(sp, keytype);
+ if (ret) return ret;
+
+ keylen = getint(&p, "encrypted key length");
+ CHECK_UINT16(keylen);
+ ret = krb5_store_int16(sp, keylen);
+ if (ret) return ret;
+
+ if (keylen) {
+ buf = malloc(keylen);
+ if (!buf) return ENOMEM;
+ if (getdata(&p, buf, keylen, "key (or salt) data") != keylen)
+ return EINVAL;
+ sz = krb5_storage_write(sp, buf, keylen);
+ free(buf);
+ if (sz == -1) return ENOMEM;
+ } else {
+ if (strcmp(nexttoken(&p, 0,
+ "'-1' zero-length key/salt field"),
+ "-1") != 0) {
+ warnx("Expected '-1' field because key/salt length is 0");
+ return -1;
+ }
+ }
+ }
+ }
+ /*
+ * The rest is "extra data", but there's never any and we wouldn't
+ * know what to do with it.
+ */
+ /* nexttoken(&p, 0, "extra data"); */
+ return 0;
+}
+
diff --git a/lib/hdb/hdb-private.h b/lib/hdb/hdb-private.h
index 8a748694424f..82681997681f 100644
--- a/lib/hdb/hdb-private.h
+++ b/lib/hdb/hdb-private.h
@@ -15,7 +15,7 @@ _hdb_fetch_kvno (
hdb_master_key
_hdb_find_master_key (
- uint32_t */*mkvno*/,
+ unsigned int */*mkvno*/,
hdb_master_key /*mkey*/);
krb5_error_code
@@ -24,6 +24,19 @@ _hdb_keytab2hdb_entry (
const krb5_keytab_entry */*ktentry*/,
hdb_entry_ex */*entry*/);
+krb5_error_code
+_hdb_mdb_value2entry (
+ krb5_context /*context*/,
+ krb5_data */*data*/,
+ krb5_kvno /*target_kvno*/,
+ hdb_entry */*entry*/);
+
+int
+_hdb_mit_dump2mitdb_entry (
+ krb5_context /*context*/,
+ char */*line*/,
+ krb5_storage */*sp*/);
+
int
_hdb_mkey_decrypt (
krb5_context /*context*/,
@@ -49,9 +62,16 @@ krb5_error_code
_hdb_remove (
krb5_context /*context*/,
HDB */*db*/,
+ unsigned /*flags*/,
krb5_const_principal /*principal*/);
krb5_error_code
+_hdb_set_master_key_usage (
+ krb5_context /*context*/,
+ HDB */*db*/,
+ unsigned int /*key_usage*/);
+
+krb5_error_code
_hdb_store (
krb5_context /*context*/,
HDB */*db*/,
diff --git a/lib/hdb/hdb-protos.h b/lib/hdb/hdb-protos.h
index 44a1bddc7625..76855095beed 100644
--- a/lib/hdb/hdb-protos.h
+++ b/lib/hdb/hdb-protos.h
@@ -1,6 +1,7 @@
/* This is a generated file */
#ifndef __hdb_protos_h__
#define __hdb_protos_h__
+#ifndef DOXY
#include <stdarg.h>
@@ -9,11 +10,63 @@ extern "C" {
#endif
krb5_error_code
+entry2mit_string_int (
+ krb5_context /*context*/,
+ krb5_storage */*sp*/,
+ hdb_entry */*ent*/);
+
+/**
+ * This function adds an HDB entry's current keyset to the entry's key
+ * history. The current keyset is left alone; the caller is responsible
+ * for freeing it.
+ *
+ * @param context Context
+ * @param entry HDB entry
+ */
+
+krb5_error_code
+hdb_add_current_keys_to_history (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/);
+
+/**
+ * This function adds a key to an HDB entry's key history.
+ *
+ * @param context Context
+ * @param entry HDB entry
+ * @param kvno Key version number of the key to add to the history
+ * @param key The Key to add
+ */
+
+krb5_error_code
+hdb_add_history_key (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/,
+ krb5_kvno /*kvno*/,
+ Key */*key*/);
+
+krb5_error_code
hdb_add_master_key (
krb5_context /*context*/,
krb5_keyblock */*key*/,
hdb_master_key */*inout*/);
+/**
+ * This function changes an hdb_entry's kvno, swapping the current key
+ * set with a historical keyset. If no historical keys are found then
+ * an error is returned (the caller can still set entry->kvno directly).
+ *
+ * @param context krb5_context
+ * @param new_kvno New kvno for the entry
+ * @param entry hdb_entry to modify
+ */
+
+krb5_error_code
+hdb_change_kvno (
+ krb5_context /*context*/,
+ krb5_kvno /*new_kvno*/,
+ hdb_entry */*entry*/);
+
krb5_error_code
hdb_check_db_format (
krb5_context /*context*/,
@@ -30,6 +83,14 @@ hdb_clear_master_key (
krb5_context /*context*/,
HDB */*db*/);
+/**
+ * Create a handle for a Kerberos database
+ *
+ * Create a handle for a Kerberos database backend specified by a
+ * filename. Doesn't create a file if its doesn't exists, you have to
+ * use O_CREAT to tell the backend to create the file.
+ */
+
krb5_error_code
hdb_create (
krb5_context /*context*/,
@@ -37,11 +98,25 @@ hdb_create (
const char */*filename*/);
krb5_error_code
-hdb_db_create (
+hdb_db1_create (
krb5_context /*context*/,
HDB **/*db*/,
const char */*filename*/);
+krb5_error_code
+hdb_db3_create (
+ krb5_context /*context*/,
+ HDB **/*db*/,
+ const char */*filename*/);
+
+/**
+ * Return the directory where the hdb database resides.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return string pointing to directory.
+ */
+
const char *
hdb_db_dir (krb5_context /*context*/);
@@ -85,6 +160,14 @@ hdb_dbinfo_get_realm (
krb5_context /*context*/,
struct hdb_dbinfo */*dbp*/);
+/**
+ * Return the default hdb database resides.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return string pointing to directory.
+ */
+
const char *
hdb_default_db (krb5_context /*context*/);
@@ -92,6 +175,7 @@ krb5_error_code
hdb_enctype2key (
krb5_context /*context*/,
hdb_entry */*e*/,
+ const Keys */*keyset*/,
krb5_enctype /*enctype*/,
Key **/*key*/);
@@ -118,6 +202,16 @@ hdb_entry_check_mandatory (
krb5_context /*context*/,
const hdb_entry */*ent*/);
+krb5_error_code
+hdb_entry_clear_kvno_diff_clnt (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/);
+
+krb5_error_code
+hdb_entry_clear_kvno_diff_svc (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/);
+
int
hdb_entry_clear_password (
krb5_context /*context*/,
@@ -133,6 +227,12 @@ hdb_entry_get_aliases (
const hdb_entry */*entry*/,
const HDB_Ext_Aliases **/*a*/);
+unsigned int
+hdb_entry_get_kvno_diff_clnt (const hdb_entry */*entry*/);
+
+unsigned int
+hdb_entry_get_kvno_diff_svc (const hdb_entry */*entry*/);
+
int
hdb_entry_get_password (
krb5_context /*context*/,
@@ -160,6 +260,18 @@ hdb_entry_get_pw_change_time (
const hdb_entry */*entry*/,
time_t */*t*/);
+krb5_error_code
+hdb_entry_set_kvno_diff_clnt (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/,
+ unsigned int /*diff*/);
+
+krb5_error_code
+hdb_entry_set_kvno_diff_svc (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/,
+ unsigned int /*diff*/);
+
int
hdb_entry_set_password (
krb5_context /*context*/,
@@ -214,6 +326,8 @@ krb5_error_code
hdb_generate_key_set (
krb5_context /*context*/,
krb5_principal /*principal*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
+ int /*n_ks_tuple*/,
Key **/*ret_key_set*/,
size_t */*nkeyset*/,
int /*no_salt*/);
@@ -226,6 +340,16 @@ hdb_generate_key_set_password (
Key **/*keys*/,
size_t */*num_keys*/);
+krb5_error_code
+hdb_generate_key_set_password_with_ks_tuple (
+ krb5_context /*context*/,
+ krb5_principal /*principal*/,
+ const char */*password*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
+ int /*n_ks_tuple*/,
+ Key **/*keys*/,
+ size_t */*num_keys*/);
+
int
hdb_get_dbinfo (
krb5_context /*context*/,
@@ -248,6 +372,12 @@ hdb_keytab_create (
HDB ** /*db*/,
const char */*arg*/);
+const Keys *
+hdb_kvno2keys (
+ krb5_context /*context*/,
+ const hdb_entry */*e*/,
+ krb5_kvno /*kvno*/);
+
krb5_error_code
hdb_ldap_create (
krb5_context /*context*/,
@@ -277,6 +407,12 @@ hdb_mdb_create (
const char */*filename*/);
krb5_error_code
+hdb_mitdb_create (
+ krb5_context /*context*/,
+ HDB **/*db*/,
+ const char */*filename*/);
+
+krb5_error_code
hdb_ndbm_create (
krb5_context /*context*/,
HDB **/*db*/,
@@ -286,6 +422,7 @@ krb5_error_code
hdb_next_enctype2key (
krb5_context /*context*/,
const hdb_entry */*e*/,
+ const Keys */*keyset*/,
krb5_enctype /*enctype*/,
Key **/*key*/);
@@ -310,6 +447,19 @@ hdb_process_master_key (
krb5_enctype /*etype*/,
hdb_master_key */*mkey*/);
+/**
+ * This function prunes an HDB entry's keys that are too old to have been used
+ * to mint still valid tickets (based on the entry's maximum ticket lifetime).
+ *
+ * @param context Context
+ * @param entry HDB entry
+ */
+
+krb5_error_code
+hdb_prune_keys (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/);
+
krb5_error_code
hdb_read_master_key (
krb5_context /*context*/,
@@ -347,6 +497,13 @@ hdb_seal_keys_mkey (
hdb_master_key /*mkey*/);
krb5_error_code
+hdb_set_last_modified_by (
+ krb5_context /*context*/,
+ hdb_entry */*entry*/,
+ krb5_principal /*modby*/,
+ time_t /*modtime*/);
+
+krb5_error_code
hdb_set_master_key (
krb5_context /*context*/,
HDB */*db*/,
@@ -358,11 +515,21 @@ hdb_set_master_keyfile (
HDB */*db*/,
const char */*keyfile*/);
+/**
+ * Create SQLITE object, and creates the on disk database if its doesn't exists.
+ *
+ * @param context A Kerberos 5 context.
+ * @param db a returned database handle.
+ * @param filename filename
+ *
+ * @return 0 on success, an error code if not
+ */
+
krb5_error_code
hdb_sqlite_create (
krb5_context /*context*/,
HDB **/*db*/,
- const char */*argument*/);
+ const char */*filename*/);
krb5_error_code
hdb_unlock (int /*fd*/);
@@ -386,6 +553,14 @@ hdb_unseal_keys (
hdb_entry */*ent*/);
krb5_error_code
+hdb_unseal_keys_kvno (
+ krb5_context /*context*/,
+ HDB */*db*/,
+ krb5_kvno /*kvno*/,
+ unsigned /*flags*/,
+ hdb_entry */*ent*/);
+
+krb5_error_code
hdb_unseal_keys_mkey (
krb5_context /*context*/,
hdb_entry */*ent*/,
@@ -413,4 +588,5 @@ hdb_write_master_key (
}
#endif
+#endif /* DOXY */
#endif /* __hdb_protos_h__ */
diff --git a/lib/hdb/hdb-sqlite.c b/lib/hdb/hdb-sqlite.c
index e063588874ae..bc176b2bb87e 100644
--- a/lib/hdb/hdb-sqlite.c
+++ b/lib/hdb/hdb-sqlite.c
@@ -141,15 +141,115 @@ hdb_sqlite_prepare_stmt(krb5_context context,
}
if (ret != SQLITE_OK) {
- krb5_set_error_message(context, EINVAL,
+ krb5_set_error_message(context, HDB_ERR_UK_RERROR,
"Failed to prepare stmt %s: %s",
str, sqlite3_errmsg(db));
- return EINVAL;
+ return HDB_ERR_UK_RERROR;
}
return 0;
}
+static krb5_error_code
+prep_stmts(krb5_context context, hdb_sqlite_db *hsdb)
+{
+ int ret;
+
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->get_version,
+ HDBSQLITE_GET_VERSION);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->fetch,
+ HDBSQLITE_FETCH);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->get_ids,
+ HDBSQLITE_GET_IDS);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->add_entry,
+ HDBSQLITE_ADD_ENTRY);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->add_principal,
+ HDBSQLITE_ADD_PRINCIPAL);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->add_alias,
+ HDBSQLITE_ADD_ALIAS);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->delete_aliases,
+ HDBSQLITE_DELETE_ALIASES);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->update_entry,
+ HDBSQLITE_UPDATE_ENTRY);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->remove,
+ HDBSQLITE_REMOVE);
+ if (ret)
+ return ret;
+ ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
+ &hsdb->get_all_entries,
+ HDBSQLITE_GET_ALL_ENTRIES);
+ return ret;
+}
+
+static void
+finalize_stmts(krb5_context context, hdb_sqlite_db *hsdb)
+{
+ if (hsdb->get_version != NULL)
+ sqlite3_finalize(hsdb->get_version);
+ hsdb->get_version = NULL;
+
+ if (hsdb->fetch != NULL)
+ sqlite3_finalize(hsdb->fetch);
+ hsdb->fetch = NULL;
+
+ if (hsdb->get_ids != NULL)
+ sqlite3_finalize(hsdb->get_ids);
+ hsdb->get_ids = NULL;
+
+ if (hsdb->add_entry != NULL)
+ sqlite3_finalize(hsdb->add_entry);
+ hsdb->add_entry = NULL;
+
+ if (hsdb->add_principal != NULL)
+ sqlite3_finalize(hsdb->add_principal);
+ hsdb->add_principal = NULL;
+
+ if (hsdb->add_alias != NULL)
+ sqlite3_finalize(hsdb->add_alias);
+ hsdb->add_alias = NULL;
+
+ if (hsdb->delete_aliases != NULL)
+ sqlite3_finalize(hsdb->delete_aliases);
+ hsdb->delete_aliases = NULL;
+
+ if (hsdb->update_entry != NULL)
+ sqlite3_finalize(hsdb->update_entry);
+ hsdb->update_entry = NULL;
+
+ if (hsdb->remove != NULL)
+ sqlite3_finalize(hsdb->remove);
+ hsdb->remove = NULL;
+
+ if (hsdb->get_all_entries != NULL)
+ sqlite3_finalize(hsdb->get_all_entries);
+ hsdb->get_all_entries = NULL;
+}
+
/**
* A wrapper around sqlite3_exec.
*
@@ -162,17 +262,23 @@ hdb_sqlite_prepare_stmt(krb5_context context,
*/
static krb5_error_code
hdb_sqlite_exec_stmt(krb5_context context,
- sqlite3 *database,
+ hdb_sqlite_db *hsdb,
const char *statement,
krb5_error_code error_code)
{
int ret;
+ int reinit_stmts = 0;
+ sqlite3 *database = hsdb->db;
ret = sqlite3_exec(database, statement, NULL, NULL, NULL);
while(((ret == SQLITE_BUSY) ||
(ret == SQLITE_IOERR_BLOCKED) ||
(ret == SQLITE_LOCKED))) {
+ if (reinit_stmts == 0 && ret == SQLITE_BUSY) {
+ finalize_stmts(context, hsdb);
+ reinit_stmts = 1;
+ }
krb5_warnx(context, "hdb-sqlite: exec busy: %d", (int)getpid());
sleep(1);
ret = sqlite3_exec(database, statement, NULL, NULL, NULL);
@@ -185,6 +291,28 @@ hdb_sqlite_exec_stmt(krb5_context context,
return error_code;
}
+ if (reinit_stmts)
+ return prep_stmts(context, hsdb);
+
+ return 0;
+}
+
+/**
+ *
+ */
+
+static krb5_error_code
+bind_principal(krb5_context context, krb5_const_principal principal, sqlite3_stmt *stmt, int key)
+{
+ krb5_error_code ret;
+ char *str = NULL;
+
+ ret = krb5_unparse_name(context, principal, &str);
+ if (ret)
+ return ret;
+
+ sqlite3_bind_text(stmt, key, str, -1, SQLITE_TRANSIENT);
+ free(str);
return 0;
}
@@ -249,18 +377,15 @@ hdb_sqlite_close_database(krb5_context context, HDB *db)
{
hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
- sqlite3_finalize(hsdb->get_version);
- sqlite3_finalize(hsdb->fetch);
- sqlite3_finalize(hsdb->get_ids);
- sqlite3_finalize(hsdb->add_entry);
- sqlite3_finalize(hsdb->add_principal);
- sqlite3_finalize(hsdb->add_alias);
- sqlite3_finalize(hsdb->delete_aliases);
- sqlite3_finalize(hsdb->update_entry);
- sqlite3_finalize(hsdb->remove);
- sqlite3_finalize(hsdb->get_all_entries);
+ finalize_stmts(context, hsdb);
- sqlite3_close(hsdb->db);
+ /* XXX Use sqlite3_close_v2() when we upgrade SQLite3 */
+ if (sqlite3_close(hsdb->db) != SQLITE_OK) {
+ krb5_set_error_message(context, HDB_ERR_UK_SERROR,
+ "SQLite BEGIN TRANSACTION failed: %s",
+ sqlite3_errmsg(hsdb->db));
+ return HDB_ERR_UK_SERROR;
+ }
return 0;
}
@@ -293,56 +418,18 @@ hdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename)
created_file = 1;
- ret = hdb_sqlite_exec_stmt(context, hsdb->db,
+ ret = hdb_sqlite_exec_stmt(context, hsdb,
HDBSQLITE_CREATE_TABLES,
- EINVAL);
+ HDB_ERR_UK_SERROR);
if (ret) goto out;
- ret = hdb_sqlite_exec_stmt(context, hsdb->db,
+ ret = hdb_sqlite_exec_stmt(context, hsdb,
HDBSQLITE_CREATE_TRIGGERS,
- EINVAL);
+ HDB_ERR_UK_SERROR);
if (ret) goto out;
}
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->get_version,
- HDBSQLITE_GET_VERSION);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->fetch,
- HDBSQLITE_FETCH);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->get_ids,
- HDBSQLITE_GET_IDS);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->add_entry,
- HDBSQLITE_ADD_ENTRY);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->add_principal,
- HDBSQLITE_ADD_PRINCIPAL);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->add_alias,
- HDBSQLITE_ADD_ALIAS);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->delete_aliases,
- HDBSQLITE_DELETE_ALIASES);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->update_entry,
- HDBSQLITE_UPDATE_ENTRY);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->remove,
- HDBSQLITE_REMOVE);
- if (ret) goto out;
- ret = hdb_sqlite_prepare_stmt(context, hsdb->db,
- &hsdb->get_all_entries,
- HDBSQLITE_GET_ALL_ENTRIES);
+ ret = prep_stmts(context, hsdb);
if (ret) goto out;
ret = hdb_sqlite_step(context, hsdb->db, hsdb->get_version);
@@ -353,7 +440,7 @@ hdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename)
ret = 0;
if(hsdb->version != HDBSQLITE_VERSION) {
- ret = EINVAL;
+ ret = HDB_ERR_UK_SERROR;
krb5_set_error_message(context, ret, "HDBSQLITE_VERSION mismatch");
}
@@ -366,6 +453,8 @@ hdb_sqlite_make_database(krb5_context context, HDB *db, const char *filename)
sqlite3_close(hsdb->db);
if (created_file)
unlink(hsdb->db_file);
+ free(hsdb->db_file);
+ hsdb->db_file = NULL;
return ret;
}
@@ -389,18 +478,30 @@ hdb_sqlite_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal princi
{
int sqlite_error;
krb5_error_code ret;
- char *principal_string;
hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db);
sqlite3_stmt *fetch = hsdb->fetch;
krb5_data value;
-
- ret = krb5_unparse_name(context, principal, &principal_string);
- if (ret) {
- free(principal_string);
- return ret;
+ krb5_principal enterprise_principal = NULL;
+
+ if (principal->name.name_type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+ if (principal->name.name_string.len != 1) {
+ ret = KRB5_PARSE_MALFORMED;
+ krb5_set_error_message(context, ret, "malformed principal: "
+ "enterprise name with %d name components",
+ principal->name.name_string.len);
+ return ret;
+ }
+ ret = krb5_parse_name(context, principal->name.name_string.val[0],
+ &enterprise_principal);
+ if (ret)
+ return ret;
+ principal = enterprise_principal;
}
- sqlite3_bind_text(fetch, 1, principal_string, -1, SQLITE_STATIC);
+ ret = bind_principal(context, principal, fetch, 1);
+ krb5_free_principal(context, enterprise_principal);
+ if (ret)
+ return ret;
sqlite_error = hdb_sqlite_step(context, hsdb->db, fetch);
if (sqlite_error != SQLITE_ROW) {
@@ -408,7 +509,7 @@ hdb_sqlite_fetch_kvno(krb5_context context, HDB *db, krb5_const_principal princi
ret = HDB_ERR_NOENTRY;
goto out;
} else {
- ret = EINVAL;
+ ret = HDB_ERR_UK_RERROR;
krb5_set_error_message(context, ret,
"sqlite fetch failed: %d",
sqlite_error);
@@ -438,7 +539,6 @@ out:
sqlite3_clear_bindings(fetch);
sqlite3_reset(fetch);
- free(principal_string);
return ret;
}
@@ -484,30 +584,25 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags,
int ret;
int i;
sqlite_int64 entry_id;
- char *principal_string = NULL;
- char *alias_string;
const HDB_Ext_Aliases *aliases;
hdb_sqlite_db *hsdb = (hdb_sqlite_db *)(db->hdb_db);
krb5_data value;
sqlite3_stmt *get_ids = hsdb->get_ids;
- ret = hdb_sqlite_exec_stmt(context, hsdb->db,
- "BEGIN IMMEDIATE TRANSACTION", EINVAL);
+ krb5_data_zero(&value);
+
+ ret = hdb_sqlite_exec_stmt(context, hsdb,
+ "BEGIN IMMEDIATE TRANSACTION",
+ HDB_ERR_UK_SERROR);
if(ret != SQLITE_OK) {
- ret = EINVAL;
+ ret = HDB_ERR_UK_SERROR;
krb5_set_error_message(context, ret,
"SQLite BEGIN TRANSACTION failed: %s",
sqlite3_errmsg(hsdb->db));
goto rollback;
}
- ret = krb5_unparse_name(context,
- entry->entry.principal, &principal_string);
- if (ret) {
- goto rollback;
- }
-
ret = hdb_seal_keys(context, db, &entry->entry);
if(ret) {
goto rollback;
@@ -518,7 +613,10 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags,
goto rollback;
}
- sqlite3_bind_text(get_ids, 1, principal_string, -1, SQLITE_STATIC);
+ ret = bind_principal(context, entry->entry.principal, get_ids, 1);
+ if (ret)
+ goto rollback;
+
ret = hdb_sqlite_step(context, hsdb->db, get_ids);
if(ret == SQLITE_DONE) { /* No such principal */
@@ -528,16 +626,38 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags,
ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_entry);
sqlite3_clear_bindings(hsdb->add_entry);
sqlite3_reset(hsdb->add_entry);
- if(ret != SQLITE_DONE)
+ if (ret != SQLITE_DONE && ret != SQLITE_CONSTRAINT) {
+ ret = HDB_ERR_UK_SERROR;
+ goto rollback;
+ }
+ if (ret == SQLITE_CONSTRAINT) {
+ ret = HDB_ERR_EXISTS;
goto rollback;
+ }
+
+ ret = bind_principal(context, entry->entry.principal, hsdb->add_principal, 1);
+ if (ret)
+ goto rollback;
- sqlite3_bind_text(hsdb->add_principal, 1,
- principal_string, -1, SQLITE_STATIC);
ret = hdb_sqlite_step(context, hsdb->db, hsdb->add_principal);
sqlite3_clear_bindings(hsdb->add_principal);
sqlite3_reset(hsdb->add_principal);
- if(ret != SQLITE_DONE)
+ if (ret != SQLITE_DONE && ret != SQLITE_CONSTRAINT) {
+ ret = HDB_ERR_UK_SERROR;
+ goto rollback;
+ }
+ if (ret == SQLITE_CONSTRAINT) {
+ ret = HDB_ERR_EXISTS;
goto rollback;
+ }
+
+ /* Now let's learn what Entry ID we got for the new principal */
+ sqlite3_reset(get_ids);
+ ret = hdb_sqlite_step(context, hsdb->db, get_ids);
+ if (ret != SQLITE_ROW) {
+ ret = HDB_ERR_UK_SERROR;
+ goto rollback;
+ }
entry_id = sqlite3_column_int64(get_ids, 1);
@@ -550,18 +670,23 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags,
sqlite3_bind_int64(hsdb->delete_aliases, 1, entry_id);
ret = hdb_sqlite_step_once(context, db, hsdb->delete_aliases);
- if(ret != SQLITE_DONE)
+ if (ret != SQLITE_DONE) {
+ ret = HDB_ERR_UK_SERROR;
goto rollback;
+ }
sqlite3_bind_blob(hsdb->update_entry, 1,
value.data, value.length, SQLITE_STATIC);
sqlite3_bind_int64(hsdb->update_entry, 2, entry_id);
ret = hdb_sqlite_step_once(context, db, hsdb->update_entry);
- if(ret != SQLITE_DONE)
+ if (ret != SQLITE_DONE) {
+ ret = HDB_ERR_UK_SERROR;
goto rollback;
+ }
} else {
/* Error! */
+ ret = HDB_ERR_UK_SERROR;
goto rollback;
}
@@ -571,51 +696,47 @@ hdb_sqlite_store(krb5_context context, HDB *db, unsigned flags,
for(i = 0; i < aliases->aliases.len; i++) {
- ret = krb5_unparse_name(context, &aliases->aliases.val[i],
- &alias_string);
- if (ret) {
- free(alias_string);
+ ret = bind_principal(context, &aliases->aliases.val[i], hsdb->add_alias, 1);
+ if (ret)
goto rollback;
- }
- sqlite3_bind_text(hsdb->add_alias, 1, alias_string,
- -1, SQLITE_STATIC);
sqlite3_bind_int64(hsdb->add_alias, 2, entry_id);
ret = hdb_sqlite_step_once(context, db, hsdb->add_alias);
-
- free(alias_string);
-
- if(ret != SQLITE_DONE)
+ if (ret == SQLITE_CONSTRAINT) {
+ ret = HDB_ERR_EXISTS;
+ goto rollback;
+ }
+ if (ret != SQLITE_DONE) {
+ ret = HDB_ERR_UK_SERROR;
goto rollback;
+ }
}
- ret = 0;
-
commit:
-
- free(principal_string);
-
krb5_data_free(&value);
-
sqlite3_clear_bindings(get_ids);
sqlite3_reset(get_ids);
- ret = hdb_sqlite_exec_stmt(context, hsdb->db, "COMMIT", EINVAL);
+ if ((flags & HDB_F_PRECHECK)) {
+ (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0);
+ return 0;
+ }
+
+ ret = hdb_sqlite_exec_stmt(context, hsdb, "COMMIT", HDB_ERR_UK_SERROR);
if(ret != SQLITE_OK)
- krb5_warnx(context, "hdb-sqlite: COMMIT problem: %d: %s",
- ret, sqlite3_errmsg(hsdb->db));
+ krb5_warnx(context, "hdb-sqlite: COMMIT problem: %ld: %s",
+ (long)HDB_ERR_UK_SERROR, sqlite3_errmsg(hsdb->db));
- return ret;
+ return ret == SQLITE_OK ? 0 : HDB_ERR_UK_SERROR;
rollback:
-
+ krb5_data_free(&value);
+ sqlite3_clear_bindings(get_ids);
+ sqlite3_reset(get_ids);
krb5_warnx(context, "hdb-sqlite: store rollback problem: %d: %s",
ret, sqlite3_errmsg(hsdb->db));
- free(principal_string);
-
- ret = hdb_sqlite_exec_stmt(context, hsdb->db,
- "ROLLBACK", EINVAL);
+ (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0);
return ret;
}
@@ -666,12 +787,12 @@ hdb_sqlite_open(krb5_context context, HDB *db, int flags, mode_t mode)
static krb5_error_code
hdb_sqlite_destroy(krb5_context context, HDB *db)
{
- int ret;
+ int ret, ret2;
hdb_sqlite_db *hsdb;
ret = hdb_clear_master_key(context, db);
- hdb_sqlite_close_database(context, db);
+ ret2 = hdb_sqlite_close_database(context, db);
hsdb = (hdb_sqlite_db*)(db->hdb_db);
@@ -679,7 +800,7 @@ hdb_sqlite_destroy(krb5_context context, HDB *db)
free(db->hdb_db);
free(db);
- return ret;
+ return ret ? ret : ret2;
}
/*
@@ -731,8 +852,11 @@ hdb_sqlite_nextkey(krb5_context context, HDB *db, unsigned flags,
sqlite3_reset(hsdb->get_all_entries);
}
else {
- /* XXX SQLite error. Should be handled in some way. */
- ret = EINVAL;
+ ret = HDB_ERR_UK_RERROR;
+ krb5_set_error_message(context, HDB_ERR_UK_RERROR,
+ "SELECT failed after returning one or "
+ "more rows: %s", sqlite3_errmsg(hsdb->db));
+
}
return ret;
@@ -764,22 +888,22 @@ hdb_sqlite_firstkey(krb5_context context, HDB *db, unsigned flags,
static krb5_error_code
hdb_sqlite_rename(krb5_context context, HDB *db, const char *new_name)
{
+ krb5_error_code ret, ret2;
hdb_sqlite_db *hsdb = (hdb_sqlite_db *) db->hdb_db;
- int ret;
krb5_warnx(context, "hdb_sqlite_rename");
if (strncasecmp(new_name, "sqlite:", 7) == 0)
new_name += 7;
- hdb_sqlite_close_database(context, db);
-
- ret = rename(hsdb->db_file, new_name);
- free(hsdb->db_file);
+ ret = hdb_sqlite_close_database(context, db);
- hdb_sqlite_make_database(context, db, new_name);
+ if (rename(hsdb->db_file, new_name) == -1)
+ return errno;
- return ret;
+ free(hsdb->db_file);
+ ret2 = hdb_sqlite_make_database(context, db, new_name);
+ return ret ? ret : ret2;
}
/*
@@ -787,34 +911,62 @@ hdb_sqlite_rename(krb5_context context, HDB *db, const char *new_name)
*/
static krb5_error_code
hdb_sqlite_remove(krb5_context context, HDB *db,
- krb5_const_principal principal)
+ unsigned flags, krb5_const_principal principal)
{
krb5_error_code ret;
- char *principal_string;
hdb_sqlite_db *hsdb = (hdb_sqlite_db*)(db->hdb_db);
- sqlite3_stmt *remove = hsdb->remove;
+ sqlite3_stmt *get_ids = hsdb->get_ids;
+ sqlite3_stmt *rm = hsdb->remove;
- ret = krb5_unparse_name(context, principal, &principal_string);
- if (ret) {
- free(principal_string);
+ bind_principal(context, principal, rm, 1);
+
+ ret = hdb_sqlite_exec_stmt(context, hsdb,
+ "BEGIN IMMEDIATE TRANSACTION",
+ HDB_ERR_UK_SERROR);
+ if (ret != SQLITE_OK) {
+ ret = HDB_ERR_UK_SERROR;
+ (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0);
+ krb5_set_error_message(context, ret,
+ "SQLite BEGIN TRANSACTION failed: %s",
+ sqlite3_errmsg(hsdb->db));
return ret;
}
- sqlite3_bind_text(remove, 1, principal_string, -1, SQLITE_STATIC);
+ if ((flags & HDB_F_PRECHECK)) {
+ ret = bind_principal(context, principal, get_ids, 1);
+ if (ret)
+ return ret;
+
+ ret = hdb_sqlite_step(context, hsdb->db, get_ids);
+ sqlite3_clear_bindings(get_ids);
+ sqlite3_reset(get_ids);
+ if (ret == SQLITE_DONE) {
+ (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0);
+ return HDB_ERR_NOENTRY;
+ }
+ }
- ret = hdb_sqlite_step(context, hsdb->db, remove);
+ ret = hdb_sqlite_step(context, hsdb->db, rm);
+ sqlite3_clear_bindings(rm);
+ sqlite3_reset(rm);
if (ret != SQLITE_DONE) {
- ret = EINVAL;
- krb5_set_error_message(context, ret,
- "sqlite remove failed: %d",
- ret);
- } else
- ret = 0;
+ (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0);
+ ret = HDB_ERR_UK_SERROR;
+ krb5_set_error_message(context, ret, "sqlite remove failed: %d", ret);
+ return ret;
+ }
- sqlite3_clear_bindings(remove);
- sqlite3_reset(remove);
+ if ((flags & HDB_F_PRECHECK)) {
+ (void) hdb_sqlite_exec_stmt(context, hsdb, "ROLLBACK", 0);
+ return 0;
+ }
- return ret;
+ ret = hdb_sqlite_exec_stmt(context, hsdb, "COMMIT", HDB_ERR_UK_SERROR);
+ if (ret != SQLITE_OK)
+ krb5_warnx(context, "hdb-sqlite: COMMIT problem: %ld: %s",
+ (long)HDB_ERR_UK_SERROR, sqlite3_errmsg(hsdb->db));
+
+ return 0;
}
/**
@@ -822,13 +974,13 @@ hdb_sqlite_remove(krb5_context context, HDB *db,
*
* @param context A Kerberos 5 context.
* @param db a returned database handle.
- * @param argument filename
+ * @param filename filename
*
* @return 0 on success, an error code if not
*/
krb5_error_code
-hdb_sqlite_create(krb5_context context, HDB **db, const char *argument)
+hdb_sqlite_create(krb5_context context, HDB **db, const char *filename)
{
krb5_error_code ret;
hdb_sqlite_db *hsdb;
@@ -837,8 +989,16 @@ hdb_sqlite_create(krb5_context context, HDB **db, const char *argument)
if (*db == NULL)
return krb5_enomem(context);
+ (*db)->hdb_name = strdup(filename);
+ if ((*db)->hdb_name == NULL) {
+ free(*db);
+ *db = NULL;
+ return krb5_enomem(context);
+ }
+
hsdb = (hdb_sqlite_db*) calloc(1, sizeof (*hsdb));
if (hsdb == NULL) {
+ free((*db)->hdb_name);
free(*db);
*db = NULL;
return krb5_enomem(context);
@@ -847,7 +1007,7 @@ hdb_sqlite_create(krb5_context context, HDB **db, const char *argument)
(*db)->hdb_db = hsdb;
/* XXX make_database should make sure everything else is freed on error */
- ret = hdb_sqlite_make_database(context, *db, argument);
+ ret = hdb_sqlite_make_database(context, *db, filename);
if (ret) {
free((*db)->hdb_db);
free(*db);
diff --git a/lib/hdb/hdb.asn1 b/lib/hdb/hdb.asn1
index a72851c9f201..333ccb064196 100644
--- a/lib/hdb/hdb.asn1
+++ b/lib/hdb/hdb.asn1
@@ -46,8 +46,10 @@ HDBFlags ::= BIT STRING {
trusted-for-delegation(14), -- Trusted to print forwardabled tickets
allow-kerberos4(15), -- Allow Kerberos 4 requests
allow-digest(16), -- Allow digest requests
- locked-out(17) -- Account is locked out,
+ locked-out(17), -- Account is locked out,
-- authentication will be denied
+ require-pwchange(18), -- require a passwd change
+ do-not-store(31) -- Not to be modified and stored in HDB
}
GENERATION ::= SEQUENCE {
@@ -87,6 +89,17 @@ HDB-Ext-Aliases ::= SEQUENCE {
aliases[1] SEQUENCE OF Principal -- all names, inc primary
}
+Keys ::= SEQUENCE OF Key
+
+hdb_keyset ::= SEQUENCE {
+ kvno[0] INTEGER (0..4294967295),
+ keys[1] Keys,
+ set-time[2] KerberosTime OPTIONAL, -- time this keyset was created/set
+ ...
+}
+
+HDB-Ext-KeySet ::= SEQUENCE OF hdb_keyset
+
HDB-extension ::= SEQUENCE {
mandatory[0] BOOLEAN, -- kdc MUST understand this extension,
@@ -102,6 +115,11 @@ HDB-extension ::= SEQUENCE {
aliases[6] HDB-Ext-Aliases,
last-pw-change[7] KerberosTime,
pkinit-cert[8] HDB-Ext-PKINIT-cert,
+ hist-keys[9] HDB-Ext-KeySet,
+ hist-kvno-diff-clnt[10] INTEGER (0..4294967295),
+ hist-kvno-diff-svc[11] INTEGER (0..4294967295),
+ policy[12] UTF8String,
+ principal-id[13] INTEGER(-9223372036854775808..9223372036854775807),
...
},
...
@@ -109,16 +127,11 @@ HDB-extension ::= SEQUENCE {
HDB-extensions ::= SEQUENCE OF HDB-extension
-hdb_keyset ::= SEQUENCE {
- kvno[1] INTEGER (0..4294967295),
- keys[0] SEQUENCE OF Key
-}
-
hdb_entry ::= SEQUENCE {
principal[0] Principal OPTIONAL, -- this is optional only
-- for compatibility with libkrb5
kvno[1] INTEGER (0..4294967295),
- keys[2] SEQUENCE OF Key,
+ keys[2] Keys,
created-by[3] Event,
modified-by[4] Event OPTIONAL,
valid-start[5] KerberosTime OPTIONAL,
diff --git a/lib/hdb/hdb.c b/lib/hdb/hdb.c
index ca05cc4a1785..1cb33df0885c 100644
--- a/lib/hdb/hdb.c
+++ b/lib/hdb/hdb.c
@@ -64,47 +64,97 @@
const int hdb_interface_version = HDB_INTERFACE_VERSION;
static struct hdb_method methods[] = {
-#if HAVE_DB1 || HAVE_DB3
- { HDB_INTERFACE_VERSION, "db:", hdb_db_create},
+ /* "db:" should be db3 if we have db3, or db1 if we have db1 */
+#if HAVE_DB3
+ { HDB_INTERFACE_VERSION, NULL, NULL, "db:", hdb_db3_create},
+#elif HAVE_DB1
+ { HDB_INTERFACE_VERSION, NULL, NULL, "db:", hdb_db1_create},
#endif
#if HAVE_DB1
- { HDB_INTERFACE_VERSION, "mit-db:", hdb_mdb_create},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "db1:", hdb_db1_create},
+#endif
+#if HAVE_DB3
+ { HDB_INTERFACE_VERSION, NULL, NULL, "db3:", hdb_db3_create},
+#endif
+#if HAVE_DB1
+ { HDB_INTERFACE_VERSION, NULL, NULL, "mit-db:", hdb_mitdb_create},
+#endif
+#if HAVE_LMDB
+ { HDB_INTERFACE_VERSION, NULL, NULL, "mdb:", hdb_mdb_create},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "lmdb:", hdb_mdb_create},
#endif
#if HAVE_NDBM
- { HDB_INTERFACE_VERSION, "ndbm:", hdb_ndbm_create},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "ndbm:", hdb_ndbm_create},
#endif
- { HDB_INTERFACE_VERSION, "keytab:", hdb_keytab_create},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "keytab:", hdb_keytab_create},
#if defined(OPENLDAP) && !defined(OPENLDAP_MODULE)
- { HDB_INTERFACE_VERSION, "ldap:", hdb_ldap_create},
- { HDB_INTERFACE_VERSION, "ldapi:", hdb_ldapi_create},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "ldap:", hdb_ldap_create},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "ldapi:", hdb_ldapi_create},
+#elif defined(OPENLDAP)
+ { HDB_INTERFACE_VERSION, NULL, NULL, "ldap:", NULL},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "ldapi:", NULL},
#endif
#ifdef HAVE_SQLITE3
- { HDB_INTERFACE_VERSION, "sqlite:", hdb_sqlite_create},
+ { HDB_INTERFACE_VERSION, NULL, NULL, "sqlite:", hdb_sqlite_create},
#endif
- {0, NULL, NULL}
+ { 0, NULL, NULL, NULL, NULL}
};
-#if HAVE_DB1 || HAVE_DB3
-static struct hdb_method dbmetod =
- { HDB_INTERFACE_VERSION, "", hdb_db_create };
+/*
+ * It'd be nice if we could try opening an HDB with each supported
+ * backend until one works or all fail. It may not be possible for all
+ * flavors, but where it's possible we should.
+ */
+#if defined(HAVE_LMDB)
+static struct hdb_method default_dbmethod =
+ { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_mdb_create };
+#elif defined(HAVE_DB3)
+static struct hdb_method default_dbmethod =
+ { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_db3_create };
+#elif defined(HAVE_DB1)
+static struct hdb_method default_dbmethod =
+ { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_db1_create };
#elif defined(HAVE_NDBM)
-static struct hdb_method dbmetod =
- { HDB_INTERFACE_VERSION, "", hdb_ndbm_create };
+static struct hdb_method default_dbmethod =
+ { HDB_INTERFACE_VERSION, NULL, NULL, "", hdb_ndbm_create };
#endif
+const Keys *
+hdb_kvno2keys(krb5_context context,
+ const hdb_entry *e,
+ krb5_kvno kvno)
+{
+ HDB_Ext_KeySet *hist_keys;
+ HDB_extension *extp;
+ size_t i;
+
+ if (kvno == 0)
+ return &e->keys;
+
+ extp = hdb_find_extension(e, choice_HDB_extension_data_hist_keys);
+ if (extp == NULL)
+ return 0;
+
+ hist_keys = &extp->data.u.hist_keys;
+ for (i = 0; i < hist_keys->len; i++) {
+ if (hist_keys->val[i].kvno == kvno)
+ return &hist_keys->val[i].keys;
+ }
+
+ return NULL;
+}
krb5_error_code
hdb_next_enctype2key(krb5_context context,
const hdb_entry *e,
+ const Keys *keyset,
krb5_enctype enctype,
Key **key)
{
+ const Keys *keys = keyset ? keyset : &e->keys;
Key *k;
- for (k = *key ? (*key) + 1 : e->keys.val;
- k < e->keys.val + e->keys.len;
- k++)
- {
+ for (k = *key ? (*key) + 1 : keys->val; k < keys->val + keys->len; k++) {
if(k->key.keytype == enctype){
*key = k;
return 0;
@@ -119,11 +169,12 @@ hdb_next_enctype2key(krb5_context context,
krb5_error_code
hdb_enctype2key(krb5_context context,
hdb_entry *e,
+ const Keys *keyset,
krb5_enctype enctype,
Key **key)
{
*key = NULL;
- return hdb_next_enctype2key(context, e, enctype, key);
+ return hdb_next_enctype2key(context, e, keyset, enctype, key);
}
void
@@ -168,13 +219,14 @@ hdb_unlock(int fd)
void
hdb_free_entry(krb5_context context, hdb_entry_ex *ent)
{
+ Key *k;
size_t i;
if (ent->free_entry)
(*ent->free_entry)(context, ent);
- for(i = 0; i < ent->entry.keys.len; ++i) {
- Key *k = &ent->entry.keys.val[i];
+ for(i = 0; i < ent->entry.keys.len; i++) {
+ k = &ent->entry.keys.val[i];
memset (k->key.keyvalue.data, 0, k->key.keyvalue.length);
}
@@ -265,100 +317,6 @@ hdb_init_db(krb5_context context, HDB *db)
return ret2;
}
-#ifdef HAVE_DLOPEN
-
- /*
- * Load a dynamic backend from /usr/heimdal/lib/hdb_NAME.so,
- * looking for the hdb_NAME_create symbol.
- */
-
-static const struct hdb_method *
-find_dynamic_method (krb5_context context,
- const char *filename,
- const char **rest)
-{
- static struct hdb_method method;
- struct hdb_so_method *mso;
- char *prefix, *path, *symbol;
- const char *p;
- void *dl;
- size_t len;
-
- p = strchr(filename, ':');
-
- /* if no prefix, don't know what module to load, just ignore it */
- if (p == NULL)
- return NULL;
-
- len = p - filename;
- *rest = filename + len + 1;
-
- prefix = malloc(len + 1);
- if (prefix == NULL)
- krb5_errx(context, 1, "out of memory");
- strlcpy(prefix, filename, len + 1);
-
- if (asprintf(&path, LIBDIR "/hdb_%s.so", prefix) == -1)
- krb5_errx(context, 1, "out of memory");
-
-#ifndef RTLD_NOW
-#define RTLD_NOW 0
-#endif
-#ifndef RTLD_GLOBAL
-#define RTLD_GLOBAL 0
-#endif
-
- dl = dlopen(path, RTLD_NOW | RTLD_GLOBAL);
- if (dl == NULL) {
- krb5_warnx(context, "error trying to load dynamic module %s: %s\n",
- path, dlerror());
- free(prefix);
- free(path);
- return NULL;
- }
-
- if (asprintf(&symbol, "hdb_%s_interface", prefix) == -1)
- krb5_errx(context, 1, "out of memory");
-
- mso = (struct hdb_so_method *) dlsym(dl, symbol);
- if (mso == NULL) {
- krb5_warnx(context, "error finding symbol %s in %s: %s\n",
- symbol, path, dlerror());
- dlclose(dl);
- free(symbol);
- free(prefix);
- free(path);
- return NULL;
- }
- free(path);
- free(symbol);
-
- if (mso->version != HDB_INTERFACE_VERSION) {
- krb5_warnx(context,
- "error wrong version in shared module %s "
- "version: %d should have been %d\n",
- prefix, mso->version, HDB_INTERFACE_VERSION);
- dlclose(dl);
- free(prefix);
- return NULL;
- }
-
- if (mso->create == NULL) {
- krb5_errx(context, 1,
- "no entry point function in shared mod %s ",
- prefix);
- dlclose(dl);
- free(prefix);
- return NULL;
- }
-
- method.create = mso->create;
- method.prefix = prefix;
-
- return &method;
-}
-#endif /* HAVE_DLOPEN */
-
/*
* find the relevant method for `filename', returning a pointer to the
* rest in `rest'.
@@ -376,19 +334,62 @@ find_method (const char *filename, const char **rest)
return h;
}
}
-#if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_NDBM)
- if (strncmp(filename, "/", 1) == 0
- || strncmp(filename, "./", 2) == 0
- || strncmp(filename, "../", 3) == 0)
+#if defined(HAVE_DB1) || defined(HAVE_DB3) || defined(HAVE_LMDB) || defined(HAVE_NDBM)
+ if (strncmp(filename, "/", sizeof("/") - 1) == 0
+ || strncmp(filename, "./", sizeof("./") - 1) == 0
+ || strncmp(filename, "../", sizeof("../") - 1) == 0
+#ifdef WIN32
+ || strncmp(filename, "\\\\", sizeof("\\\\") - 1)
+ || (isalpha(filename[0]) && filename[1] == ':')
+#endif
+ )
{
*rest = filename;
- return &dbmetod;
+ return &default_dbmethod;
}
#endif
return NULL;
}
+struct cb_s {
+ const char *residual;
+ const char *filename;
+ const struct hdb_method *h;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+callback(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ const struct hdb_method *h = (const struct hdb_method *)plug;
+ struct cb_s *cb_ctx = (struct cb_s *)userctx;
+
+ if (strncmp(cb_ctx->filename, h->prefix, strlen(h->prefix)) == 0) {
+ cb_ctx->residual = cb_ctx->filename + strlen(h->prefix) + 1;
+ cb_ctx->h = h;
+ return 0;
+ }
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+static char *
+make_sym(const char *prefix)
+{
+ char *s, *sym;
+
+ errno = 0;
+ if (prefix == NULL || prefix[0] == '\0')
+ return NULL;
+ if ((s = strdup(prefix)) == NULL)
+ return NULL;
+ if (strchr(s, ':') != NULL)
+ *strchr(s, ':') = '\0';
+ if (asprintf(&sym, "hdb_%s_interface", s) == -1)
+ sym = NULL;
+ free(s);
+ return sym;
+}
+
krb5_error_code
hdb_list_builtin(krb5_context context, char **list)
{
@@ -405,12 +406,35 @@ hdb_list_builtin(krb5_context context, char **list)
len += 1;
buf = malloc(len);
if (buf == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
+ return krb5_enomem(context);
}
buf[0] = '\0';
for (h = methods; h->prefix != NULL; ++h) {
+ if (h->create == NULL) {
+ struct cb_s cb_ctx;
+ char *f;
+ char *sym;
+
+ /* Try loading the plugin */
+ if (asprintf(&f, "%sfoo", h->prefix) == -1)
+ f = NULL;
+ if ((sym = make_sym(h->prefix)) == NULL) {
+ free(buf);
+ free(f);
+ return krb5_enomem(context);
+ }
+ cb_ctx.filename = f;
+ cb_ctx.residual = NULL;
+ cb_ctx.h = NULL;
+ (void)_krb5_plugin_run_f(context, "krb5", sym,
+ HDB_INTERFACE_VERSION, 0, &cb_ctx,
+ callback);
+ free(f);
+ free(sym);
+ if (cb_ctx.h == NULL || cb_ctx.h->create == NULL)
+ continue;
+ }
if (h != methods)
strlcat(buf, ", ", len);
strlcat(buf, h->prefix, len);
@@ -451,39 +475,25 @@ _hdb_keytab2hdb_entry(krb5_context context,
krb5_error_code
hdb_create(krb5_context context, HDB **db, const char *filename)
{
- const struct hdb_method *h;
- const char *residual;
- krb5_error_code ret;
- struct krb5_plugin *list = NULL, *e;
+ struct cb_s cb_ctx;
- if(filename == NULL)
+ if (filename == NULL)
filename = HDB_DEFAULT_DB;
- krb5_add_et_list(context, initialize_hdb_error_table_r);
- h = find_method (filename, &residual);
-
- if (h == NULL) {
- ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, "hdb", &list);
- if(ret == 0 && list != NULL) {
- for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
- h = _krb5_plugin_get_symbol(e);
- if (strncmp (filename, h->prefix, strlen(h->prefix)) == 0
- && h->interface_version == HDB_INTERFACE_VERSION) {
- residual = filename + strlen(h->prefix);
- break;
- }
- }
- if (e == NULL) {
- h = NULL;
- _krb5_plugin_free(list);
- }
- }
- }
+ cb_ctx.h = find_method (filename, &cb_ctx.residual);
+ cb_ctx.filename = filename;
-#ifdef HAVE_DLOPEN
- if (h == NULL)
- h = find_dynamic_method (context, filename, &residual);
-#endif
- if (h == NULL)
- krb5_errx(context, 1, "No database support for %s", filename);
- return (*h->create)(context, db, residual);
+ if (cb_ctx.h == NULL || cb_ctx.h->create == NULL) {
+ char *sym;
+
+ if ((sym = make_sym(filename)) == NULL)
+ return krb5_enomem(context);
+
+ (void)_krb5_plugin_run_f(context, "krb5", sym, HDB_INTERFACE_VERSION,
+ 0, &cb_ctx, callback);
+
+ free(sym);
+ }
+ if (cb_ctx.h == NULL)
+ krb5_errx(context, 1, "No database support for %s", cb_ctx.filename);
+ return (*cb_ctx.h->create)(context, db, cb_ctx.residual);
}
diff --git a/lib/hdb/hdb.h b/lib/hdb/hdb.h
index a1692ce82ca2..892b8e598581 100644
--- a/lib/hdb/hdb.h
+++ b/lib/hdb/hdb.h
@@ -36,6 +36,8 @@
#ifndef __HDB_H__
#define __HDB_H__
+#include <stdio.h>
+
#include <krb5.h>
#include <hdb_err.h>
@@ -58,15 +60,18 @@ enum hdb_lockop{ HDB_RLOCK, HDB_WLOCK };
#define HDB_F_ADMIN_DATA 64 /* want data that kdc don't use */
#define HDB_F_KVNO_SPECIFIED 128 /* we want a particular KVNO */
#define HDB_F_CURRENT_KVNO 256 /* we want the current KVNO */
-/* 512, 1024, 2048 are reserved for kvno operations that is not part of the 1.5 branch */
+#define HDB_F_LIVE_CLNT_KVNOS 512 /* we want all live keys for pre-auth */
+#define HDB_F_LIVE_SVC_KVNOS 1024 /* we want all live keys for tix */
#define HDB_F_ALL_KVNOS 2048 /* we want all the keys, live or not */
#define HDB_F_FOR_AS_REQ 4096 /* fetch is for a AS REQ */
#define HDB_F_FOR_TGS_REQ 8192 /* fetch is for a TGS REQ */
+#define HDB_F_PRECHECK 16384 /* check that the operation would succeed */
/* hdb_capability_flags */
#define HDB_CAP_F_HANDLE_ENTERPRISE_PRINCIPAL 1
#define HDB_CAP_F_HANDLE_PASSWORDS 2
#define HDB_CAP_F_PASSWORD_UPDATE_KEYS 4
+#define HDB_CAP_F_SHARED_DIRECTORY 8
/* auth status values */
#define HDB_AUTH_SUCCESS 0
@@ -99,7 +104,7 @@ typedef struct hdb_entry_ex {
* query the backend database when talking about principals.
*/
-typedef struct HDB{
+typedef struct HDB {
void *hdb_db;
void *hdb_dbc; /** don't use, only for DB3 */
char *hdb_name;
@@ -107,6 +112,8 @@ typedef struct HDB{
hdb_master_key hdb_master_key;
int hdb_openp;
int hdb_capability_flags;
+ int lock_count;
+ int lock_type;
/**
* Open (or create) the a Kerberos database.
*
@@ -149,7 +156,7 @@ typedef struct HDB{
* Remove an entry from the database.
*/
krb5_error_code (*hdb_remove)(krb5_context, struct HDB*,
- krb5_const_principal);
+ unsigned, krb5_const_principal);
/**
* As part of iteration, fetch one entry
*/
@@ -181,25 +188,33 @@ typedef struct HDB{
/**
* Get an hdb_entry from a classical DB backend
*
- * If the database is a classical DB (ie BDB, NDBM, GDBM, etc)
- * backend, this function will take a principal key (krb5_data)
- * and return all data related to principal in the return
- * krb5_data. The returned encoded entry is of type hdb_entry or
- * hdb_entry_alias.
+ * This function takes a principal key (krb5_data) and returns all
+ * data related to principal in the return krb5_data. The returned
+ * encoded entry is of type hdb_entry or hdb_entry_alias.
*/
krb5_error_code (*hdb__get)(krb5_context, struct HDB*,
krb5_data, krb5_data*);
/**
* Store an hdb_entry from a classical DB backend
*
- * Same discussion as in @ref HDB::hdb__get
+ * This function takes a principal key (krb5_data) and encoded
+ * hdb_entry or hdb_entry_alias as the data to store.
+ *
+ * For a file-based DB, this must synchronize to disk when done.
+ * This is sub-optimal for kadm5_s_rename_principal(), and for
+ * kadm5_s_modify_principal() when using principal aliases; to
+ * improve this so that only one fsync() need be done
+ * per-transaction will require HDB API extensions.
*/
krb5_error_code (*hdb__put)(krb5_context, struct HDB*, int,
krb5_data, krb5_data);
/**
* Delete and hdb_entry from a classical DB backend
*
- * Same discussion as in @ref HDB::hdb__get
+ * This function takes a principal key (krb5_data) naming the record
+ * to delete.
+ *
+ * Same discussion as in @ref HDB::hdb__put
*/
krb5_error_code (*hdb__del)(krb5_context, struct HDB*, krb5_data);
/**
@@ -258,23 +273,31 @@ typedef struct HDB{
krb5_error_code (*hdb_check_s4u2self)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal);
}HDB;
-#define HDB_INTERFACE_VERSION 7
+#define HDB_INTERFACE_VERSION 9
-struct hdb_so_method {
- int version;
+struct hdb_method {
+ int version;
+ krb5_error_code (*init)(krb5_context, void **);
+ void (*fini)(void *);
const char *prefix;
krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
};
+/* dump entry format, for hdb_print_entry() */
+typedef enum hdb_dump_format {
+ HDB_DUMP_HEIMDAL = 0,
+ HDB_DUMP_MIT = 1,
+} hdb_dump_format_t;
+
+struct hdb_print_entry_arg {
+ FILE *out;
+ hdb_dump_format_t fmt;
+};
+
typedef krb5_error_code (*hdb_foreach_func_t)(krb5_context, HDB*,
hdb_entry_ex*, void*);
extern krb5_kt_ops hdb_kt_ops;
-
-struct hdb_method {
- int interface_version;
- const char *prefix;
- krb5_error_code (*create)(krb5_context, HDB **, const char *filename);
-};
+extern krb5_kt_ops hdb_get_kt_ops;
extern const int hdb_interface_version;
diff --git a/lib/hdb/hdb.schema b/lib/hdb/hdb.schema
index 57303900dcee..f9fb08098ed9 100644
--- a/lib/hdb/hdb.schema
+++ b/lib/hdb/hdb.schema
@@ -113,6 +113,11 @@ attributetype ( 1.3.6.1.4.1.5322.10.1.12
EQUALITY octetStringMatch
SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )
+attributetype ( 1.3.6.1.4.1.5322.10.1.13
+ NAME 'krb5ExtendedAttributes'
+ DESC 'Encoded ASN1 HDB Extension Attributes as an octet string'
+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )
+
# Object class definitions
objectclass ( 1.3.6.1.4.1.5322.10.2.1
@@ -129,7 +134,7 @@ objectclass ( 1.3.6.1.4.1.5322.10.2.2
MUST ( krb5KeyVersionNumber )
MAY ( krb5ValidStart $ krb5ValidEnd $ krb5PasswordEnd $
krb5MaxLife $ krb5MaxRenew $ krb5KDCFlags $
- krb5EncryptionType $ krb5Key ) )
+ krb5EncryptionType $ krb5Key $ krb5ExtendedAttributes ) )
objectclass ( 1.3.6.1.4.1.5322.10.2.3
NAME 'krb5Realm'
diff --git a/lib/hdb/hdb_err.et b/lib/hdb/hdb_err.et
index 2cad4daba414..6a79ffa29c16 100644
--- a/lib/hdb/hdb_err.et
+++ b/lib/hdb/hdb_err.et
@@ -26,5 +26,8 @@ error_code NO_MKEY, "No correct master key"
error_code MANDATORY_OPTION, "Entry contains unknown mandatory extension"
error_code NO_WRITE_SUPPORT, "HDB backend doesn't contain write support"
error_code NOT_FOUND_HERE, "The secret for this entry is not replicated to this database"
+error_code MISUSE, "Incorrect use of the API"
+error_code KVNO_NOT_FOUND, "Entry key version number not found"
+error_code WRONG_REALM, "The principal exists in another realm."
end
diff --git a/lib/hdb/hdb_locl.h b/lib/hdb/hdb_locl.h
index e896b5802575..fd7b1849daee 100644
--- a/lib/hdb/hdb_locl.h
+++ b/lib/hdb/hdb_locl.h
@@ -38,6 +38,9 @@
#include <config.h>
+#include <assert.h>
+#include <heimbase.h>
+
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
diff --git a/lib/hdb/keys.c b/lib/hdb/keys.c
index 3d0b9d7c1b31..96c221ed275e 100644
--- a/lib/hdb/keys.c
+++ b/lib/hdb/keys.c
@@ -1,6 +1,5 @@
-
/*
- * Copyright (c) 1997 - 2001, 2003 - 2004 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 - 2011 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -34,14 +33,29 @@
#include "hdb_locl.h"
+struct hx509_certs_data;
+struct krb5_pk_identity;
+struct krb5_pk_cert;
+struct ContentInfo;
+struct AlgorithmIdentifier;
+struct _krb5_krb_auth_data;
+typedef struct krb5_pk_init_ctx_data *krb5_pk_init_ctx;
+struct krb5_dh_moduli;
+struct _krb5_key_data;
+struct _krb5_encryption_type;
+struct _krb5_key_type;
+#include <pkinit_asn1.h>
+#include <krb5-private.h>
+#include <base64.h>
+
/*
* free all the memory used by (len, keys)
*/
void
-hdb_free_keys (krb5_context context, int len, Key *keys)
+hdb_free_keys(krb5_context context, int len, Key *keys)
{
- int i;
+ size_t i;
for (i = 0; i < len; i++) {
free(keys[i].mkvno);
@@ -68,15 +82,15 @@ hdb_free_keys (krb5_context context, int len, Key *keys)
*/
static const krb5_enctype des_etypes[] = {
- ETYPE_DES_CBC_MD5,
- ETYPE_DES_CBC_MD4,
- ETYPE_DES_CBC_CRC
+ KRB5_ENCTYPE_DES_CBC_MD5,
+ KRB5_ENCTYPE_DES_CBC_MD4,
+ KRB5_ENCTYPE_DES_CBC_CRC
};
static const krb5_enctype all_etypes[] = {
- ETYPE_AES256_CTS_HMAC_SHA1_96,
- ETYPE_ARCFOUR_HMAC_MD5,
- ETYPE_DES3_CBC_SHA1
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ KRB5_ENCTYPE_DES3_CBC_SHA1,
+ KRB5_ENCTYPE_ARCFOUR_HMAC_MD5
};
static krb5_error_code
@@ -114,7 +128,7 @@ parse_key_set(krb5_context context, const char *key,
enctypes = des_etypes;
num_enctypes = sizeof(des_etypes)/sizeof(des_etypes[0]);
} else if(strcmp(buf[i], "des3") == 0) {
- e = ETYPE_DES3_CBC_SHA1;
+ e = KRB5_ENCTYPE_DES3_CBC_SHA1;
enctypes = &e;
num_enctypes = 1;
} else {
@@ -148,29 +162,30 @@ parse_key_set(krb5_context context, const char *key,
continue;
}
- {
- /* if there is a final string, use it as the string to
- salt with, this is mostly useful with null salt for
- v4 compat, and a cell name for afs compat */
- salt->saltvalue.data = strdup(buf[i]);
- if (salt->saltvalue.data == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
- }
- salt->saltvalue.length = strlen(buf[i]);
- }
+ if (salt->saltvalue.data != NULL)
+ free(salt->saltvalue.data);
+ /* if there is a final string, use it as the string to
+ salt with, this is mostly useful with null salt for
+ v4 compat, and a cell name for afs compat */
+ salt->saltvalue.data = strdup(buf[i]);
+ if (salt->saltvalue.data == NULL)
+ return krb5_enomem(context);
+ salt->saltvalue.length = strlen(buf[i]);
}
if(enctypes == NULL || salt->salttype == 0) {
+ krb5_free_salt(context, *salt);
krb5_set_error_message(context, EINVAL, "bad value for default_keys `%s'", key);
return EINVAL;
}
/* if no salt was specified make up default salt */
if(salt->saltvalue.data == NULL) {
- if(salt->salttype == KRB5_PW_SALT)
+ if(salt->salttype == KRB5_PW_SALT) {
ret = krb5_get_pw_salt(context, principal, salt);
- else if(salt->salttype == KRB5_AFS3_SALT) {
+ if (ret)
+ return ret;
+ } else if(salt->salttype == KRB5_AFS3_SALT) {
krb5_const_realm realm = krb5_principal_get_realm(context, principal);
salt->saltvalue.data = strdup(realm);
if(salt->saltvalue.data == NULL) {
@@ -196,6 +211,256 @@ parse_key_set(krb5_context context, const char *key,
return 0;
}
+/**
+ * This function prunes an HDB entry's keys that are too old to have been used
+ * to mint still valid tickets (based on the entry's maximum ticket lifetime).
+ *
+ * @param context Context
+ * @param entry HDB entry
+ */
+krb5_error_code
+hdb_prune_keys(krb5_context context, hdb_entry *entry)
+{
+ HDB_extension *ext;
+ HDB_Ext_KeySet *keys;
+ size_t nelem;
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys);
+ if (ext == NULL)
+ return 0;
+ keys = &ext->data.u.hist_keys;
+ nelem = keys->len;
+
+ /* Optionally drop key history for keys older than now - max_life */
+ if (entry->max_life != NULL && nelem > 0
+ && krb5_config_get_bool_default(context, NULL, FALSE,
+ "kadmin", "prune-key-history", NULL)) {
+ hdb_keyset *elem;
+ time_t ceiling = time(NULL) - *entry->max_life;
+ time_t keep_time = 0;
+ size_t i;
+
+ /*
+ * Compute most recent key timestamp that predates the current time
+ * by at least the entry's maximum ticket lifetime.
+ */
+ for (i = 0; i < nelem; ++i) {
+ elem = &keys->val[i];
+ if (elem->set_time && *elem->set_time < ceiling
+ && (keep_time == 0 || *elem->set_time > keep_time))
+ keep_time = *elem->set_time;
+ }
+
+ /* Drop obsolete entries */
+ if (keep_time) {
+ for (i = 0; i < nelem; /* see below */) {
+ elem = &keys->val[i];
+ if (elem->set_time && *elem->set_time < keep_time) {
+ remove_HDB_Ext_KeySet(keys, i);
+ /*
+ * Removing the i'th element shifts the tail down, continue
+ * at same index with reduced upper bound.
+ */
+ --nelem;
+ continue;
+ }
+ ++i;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * This function adds an HDB entry's current keyset to the entry's key
+ * history. The current keyset is left alone; the caller is responsible
+ * for freeing it.
+ *
+ * @param context Context
+ * @param entry HDB entry
+ */
+krb5_error_code
+hdb_add_current_keys_to_history(krb5_context context, hdb_entry *entry)
+{
+ krb5_boolean replace = FALSE;
+ krb5_error_code ret;
+ HDB_extension *ext;
+ HDB_Ext_KeySet *keys;
+ hdb_keyset newkeyset;
+ time_t newtime;
+
+ if (entry->keys.len == 0)
+ return 0; /* nothing to do */
+
+ ext = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys);
+ if (ext == NULL) {
+ replace = TRUE;
+ ext = calloc(1, sizeof (*ext));
+ if (ext == NULL)
+ return krb5_enomem(context);
+
+ ext->data.element = choice_HDB_extension_data_hist_keys;
+ }
+ keys = &ext->data.u.hist_keys;
+
+ ext->mandatory = FALSE;
+
+ /*
+ * Copy in newest old keyset
+ */
+ ret = hdb_entry_get_pw_change_time(entry, &newtime);
+ if (ret)
+ goto out;
+
+ memset(&newkeyset, 0, sizeof(newkeyset));
+ newkeyset.keys = entry->keys;
+ newkeyset.kvno = entry->kvno;
+ newkeyset.set_time = &newtime;
+
+ ret = add_HDB_Ext_KeySet(keys, &newkeyset);
+ if (ret)
+ goto out;
+
+ if (replace) {
+ /* hdb_replace_extension() deep-copies ext; what a waste */
+ ret = hdb_replace_extension(context, entry, ext);
+ if (ret)
+ goto out;
+ }
+
+ ret = hdb_prune_keys(context, entry);
+ if (ret)
+ goto out;
+
+ out:
+ if (replace && ext) {
+ free_HDB_extension(ext);
+ free(ext);
+ }
+ return ret;
+}
+
+/**
+ * This function adds a key to an HDB entry's key history.
+ *
+ * @param context Context
+ * @param entry HDB entry
+ * @param kvno Key version number of the key to add to the history
+ * @param key The Key to add
+ */
+krb5_error_code
+hdb_add_history_key(krb5_context context, hdb_entry *entry, krb5_kvno kvno, Key *key)
+{
+ size_t i;
+ hdb_keyset keyset;
+ HDB_Ext_KeySet *hist_keys;
+ HDB_extension ext;
+ HDB_extension *extp;
+ krb5_error_code ret;
+
+ memset(&keyset, 0, sizeof (keyset));
+ memset(&ext, 0, sizeof (ext));
+
+ extp = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys);
+ if (extp == NULL) {
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ extp = &ext;
+ }
+
+ extp->mandatory = FALSE;
+ hist_keys = &extp->data.u.hist_keys;
+
+ for (i = 0; i < hist_keys->len; i++) {
+ if (hist_keys->val[i].kvno == kvno) {
+ ret = add_Keys(&hist_keys->val[i].keys, key);
+ goto out;
+ }
+ }
+
+ keyset.kvno = kvno;
+ ret = add_Keys(&keyset.keys, key);
+ if (ret)
+ goto out;
+ ret = add_HDB_Ext_KeySet(hist_keys, &keyset);
+ if (ret)
+ goto out;
+ if (extp == &ext) {
+ ret = hdb_replace_extension(context, entry, &ext);
+ if (ret)
+ goto out;
+ }
+
+out:
+ free_hdb_keyset(&keyset);
+ free_HDB_extension(&ext);
+ return ret;
+}
+
+
+/**
+ * This function changes an hdb_entry's kvno, swapping the current key
+ * set with a historical keyset. If no historical keys are found then
+ * an error is returned (the caller can still set entry->kvno directly).
+ *
+ * @param context krb5_context
+ * @param new_kvno New kvno for the entry
+ * @param entry hdb_entry to modify
+ */
+krb5_error_code
+hdb_change_kvno(krb5_context context, krb5_kvno new_kvno, hdb_entry *entry)
+{
+ HDB_extension ext;
+ HDB_extension *extp;
+ hdb_keyset keyset;
+ HDB_Ext_KeySet *hist_keys;
+ size_t i;
+ int found = 0;
+ krb5_error_code ret;
+
+ if (entry->kvno == new_kvno)
+ return 0;
+
+ extp = hdb_find_extension(entry, choice_HDB_extension_data_hist_keys);
+ if (extp == NULL) {
+ memset(&ext, 0, sizeof (ext));
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ extp = &ext;
+ }
+
+ memset(&keyset, 0, sizeof (keyset));
+ hist_keys = &extp->data.u.hist_keys;
+ for (i = 0; i < hist_keys->len; i++) {
+ if (hist_keys->val[i].kvno == new_kvno) {
+ found = 1;
+ ret = copy_hdb_keyset(&hist_keys->val[i], &keyset);
+ if (ret)
+ goto out;
+ ret = remove_HDB_Ext_KeySet(hist_keys, i);
+ if (ret)
+ goto out;
+ break;
+ }
+ }
+
+ if (!found)
+ return HDB_ERR_KVNO_NOT_FOUND;
+
+ ret = hdb_add_current_keys_to_history(context, entry);
+ if (ret)
+ goto out;
+
+ /* Note: we do nothing with keyset.set_time */
+ entry->kvno = new_kvno;
+ entry->keys = keyset.keys; /* shortcut */
+ memset(&keyset.keys, 0, sizeof (keyset.keys));
+
+out:
+ free_hdb_keyset(&keyset);
+ return ret;
+}
+
+
static krb5_error_code
add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
krb5_enctype enctype, krb5_salt *salt)
@@ -243,6 +508,122 @@ add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
}
+static
+krb5_error_code
+ks_tuple2str(krb5_context context, int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple, char ***ks_tuple_strs)
+{
+ size_t i;
+ char **ksnames;
+ krb5_error_code rc = KRB5_PROG_ETYPE_NOSUPP;
+
+ *ks_tuple_strs = NULL;
+ if (n_ks_tuple < 1)
+ return 0;
+
+ if ((ksnames = calloc(n_ks_tuple + 1, sizeof (*ksnames))) == NULL)
+ return (errno);
+
+ for (i = 0; i < n_ks_tuple; i++) {
+ char *ename, *sname;
+
+ if (krb5_enctype_to_string(context, ks_tuple[i].ks_enctype, &ename))
+ goto out;
+ if (krb5_salttype_to_string(context, ks_tuple[i].ks_enctype,
+ ks_tuple[i].ks_salttype, &sname)) {
+ free(ename);
+ goto out;
+ }
+
+ if (asprintf(&ksnames[i], "%s:%s", ename, sname) == -1) {
+ rc = errno;
+ free(ename);
+ free(sname);
+ goto out;
+ }
+ free(ename);
+ free(sname);
+ }
+
+ ksnames[i] = NULL;
+ *ks_tuple_strs = ksnames;
+ return 0;
+
+out:
+ for (i = 0; i < n_ks_tuple; i++)
+ free(ksnames[i]);
+ free(ksnames);
+ return (rc);
+}
+
+/*
+ *
+ */
+
+static char **
+glob_rules_keys(krb5_context context, krb5_const_principal principal)
+{
+ const krb5_config_binding *list;
+ krb5_principal pattern;
+ krb5_error_code ret;
+
+ list = krb5_config_get_list(context, NULL, "kadmin",
+ "default_key_rules", NULL);
+ if (list == NULL)
+ return NULL;
+
+ while (list) {
+ if (list->type == krb5_config_string) {
+ ret = krb5_parse_name(context, list->name, &pattern);
+ if (ret == 0) {
+ ret = krb5_principal_match(context, principal, pattern);
+ krb5_free_principal(context, pattern);
+ if (ret) {
+ return krb5_config_get_strings(context, list,
+ list->name, NULL);
+ }
+ }
+ }
+ list = list->next;
+ }
+ return NULL;
+}
+
+/*
+ * NIST guidance in Section 5.1 of [SP800-132] requires that a portion
+ * of the salt of at least 128 bits shall be randomly generated.
+ */
+static krb5_error_code
+add_random_to_salt(krb5_context context, krb5_salt *in, krb5_salt *out)
+{
+ krb5_error_code ret;
+ char *p;
+ unsigned char random[16];
+ char *s;
+ int slen;
+
+ krb5_generate_random_block(random, sizeof(random));
+
+ slen = rk_base64_encode(random, sizeof(random), &s);
+ if (slen < 0)
+ return ENOMEM;
+
+ ret = krb5_data_alloc(&out->saltvalue, slen + in->saltvalue.length);
+ if (ret) {
+ free(s);
+ return ret;
+ }
+
+ p = out->saltvalue.data;
+ memcpy(p, s, slen);
+ memcpy(&p[slen], in->saltvalue.data, in->saltvalue.length);
+
+ out->salttype = in->salttype;
+ free(s);
+
+ return 0;
+}
+
/*
* Generate the `key_set' from the [kadmin]default_keys statement. If
* `no_salt' is set, salt is not important (and will not be set) since
@@ -251,12 +632,16 @@ add_enctype_to_key_set(Key **key_set, size_t *nkeyset,
krb5_error_code
hdb_generate_key_set(krb5_context context, krb5_principal principal,
+ krb5_key_salt_tuple *ks_tuple, int n_ks_tuple,
Key **ret_key_set, size_t *nkeyset, int no_salt)
{
- char **ktypes, **kp;
+ char **ktypes = NULL;
+ char **kp;
krb5_error_code ret;
Key *k, *key_set;
size_t i, j;
+ char **ks_tuple_strs;
+ char **config_ktypes = NULL;
static const char *default_keytypes[] = {
"aes256-cts-hmac-sha1-96:pw-salt",
"des3-cbc-sha1:pw-salt",
@@ -264,16 +649,24 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal,
NULL
};
- ktypes = krb5_config_get_strings(context, NULL, "kadmin",
- "default_keys", NULL);
+ if ((ret = ks_tuple2str(context, n_ks_tuple, ks_tuple, &ks_tuple_strs)))
+ return ret;
+
+ ktypes = ks_tuple_strs;
+ if (ktypes == NULL) {
+ ktypes = glob_rules_keys(context, principal);
+ }
+ if (ktypes == NULL) {
+ config_ktypes = krb5_config_get_strings(context, NULL, "kadmin",
+ "default_keys", NULL);
+ ktypes = config_ktypes;
+ }
if (ktypes == NULL)
ktypes = (char **)(intptr_t)default_keytypes;
*ret_key_set = key_set = NULL;
*nkeyset = 0;
- ret = 0;
-
for(kp = ktypes; kp && *kp; kp++) {
const char *p;
krb5_salt salt;
@@ -298,10 +691,14 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal,
if (ret) {
krb5_warn(context, ret, "bad value for default_keys `%s'", *kp);
ret = 0;
+ krb5_free_salt(context, salt);
continue;
}
for (i = 0; i < num_enctypes; i++) {
+ krb5_salt *saltp = no_salt ? NULL : &salt;
+ krb5_salt rsalt;
+
/* find duplicates */
for (j = 0; j < *nkeyset; j++) {
@@ -320,14 +717,27 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal,
}
}
/* not a duplicate, lets add it */
- if (j == *nkeyset) {
+ if (j < *nkeyset)
+ continue;
+
+ memset(&rsalt, 0, sizeof(rsalt));
+
+ /* prepend salt with randomness if required */
+ if (!no_salt &&
+ _krb5_enctype_requires_random_salt(context, enctypes[i])) {
+ saltp = &rsalt;
+ ret = add_random_to_salt(context, &salt, &rsalt);
+ }
+
+ if (ret == 0)
ret = add_enctype_to_key_set(&key_set, nkeyset, enctypes[i],
- no_salt ? NULL : &salt);
- if (ret) {
- free(enctypes);
- krb5_free_salt(context, salt);
- goto out;
- }
+ saltp);
+ krb5_free_salt(context, rsalt);
+
+ if (ret) {
+ free(enctypes);
+ krb5_free_salt(context, salt);
+ goto out;
}
}
free(enctypes);
@@ -337,8 +747,12 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal,
*ret_key_set = key_set;
out:
- if (ktypes != (char **)(intptr_t)default_keytypes)
- krb5_config_free_strings(ktypes);
+ if (config_ktypes != NULL)
+ krb5_config_free_strings(config_ktypes);
+
+ for(kp = ks_tuple_strs; kp && *kp; kp++)
+ free(*kp);
+ free(ks_tuple_strs);
if (ret) {
krb5_warn(context, ret,
@@ -358,32 +772,34 @@ hdb_generate_key_set(krb5_context context, krb5_principal principal,
krb5_error_code
-hdb_generate_key_set_password(krb5_context context,
- krb5_principal principal,
- const char *password,
- Key **keys, size_t *num_keys)
+hdb_generate_key_set_password_with_ks_tuple(krb5_context context,
+ krb5_principal principal,
+ const char *password,
+ krb5_key_salt_tuple *ks_tuple,
+ int n_ks_tuple,
+ Key **keys, size_t *num_keys)
{
krb5_error_code ret;
size_t i;
- ret = hdb_generate_key_set(context, principal,
+ ret = hdb_generate_key_set(context, principal, ks_tuple, n_ks_tuple,
keys, num_keys, 0);
if (ret)
return ret;
for (i = 0; i < (*num_keys); i++) {
krb5_salt salt;
+ Key *key = &(*keys)[i];
- salt.salttype = (*keys)[i].salt->type;
- salt.saltvalue.length = (*keys)[i].salt->salt.length;
- salt.saltvalue.data = (*keys)[i].salt->salt.data;
+ salt.salttype = key->salt->type;
+ salt.saltvalue.length = key->salt->salt.length;
+ salt.saltvalue.data = key->salt->salt.data;
ret = krb5_string_to_key_salt (context,
- (*keys)[i].key.keytype,
+ key->key.keytype,
password,
salt,
- &(*keys)[i].key);
-
+ &key->key);
if(ret)
break;
}
@@ -394,3 +810,16 @@ hdb_generate_key_set_password(krb5_context context,
}
return ret;
}
+
+
+krb5_error_code
+hdb_generate_key_set_password(krb5_context context,
+ krb5_principal principal,
+ const char *password,
+ Key **keys, size_t *num_keys)
+{
+
+ return hdb_generate_key_set_password_with_ks_tuple(context, principal,
+ password, NULL, 0,
+ keys, num_keys);
+}
diff --git a/lib/hdb/keytab.c b/lib/hdb/keytab.c
index c72b797dab4d..966c12f89fb0 100644
--- a/lib/hdb/keytab.c
+++ b/lib/hdb/keytab.c
@@ -65,7 +65,7 @@ hdb_resolve(krb5_context context, const char *name, krb5_keytab id)
}
db = name;
mkey = strstr(name, ":mkey=");
- if(mkey == NULL || mkey[5] == '\0') {
+ if(mkey == NULL || mkey[6] == '\0') {
if(*name == '\0')
d->dbname = NULL;
else {
@@ -87,7 +87,7 @@ hdb_resolve(krb5_context context, const char *name, krb5_keytab id)
memmove(d->dbname, db, mkey - db);
d->dbname[mkey - db] = '\0';
- d->mkey = strdup(mkey + 5);
+ d->mkey = strdup(mkey + 6);
if(d->mkey == NULL) {
free(d->dbname);
free(d);
@@ -420,5 +420,23 @@ krb5_kt_ops hdb_kt_ops = {
hdb_next_entry,
hdb_end_seq_get,
NULL, /* add */
- NULL /* remove */
+ NULL, /* remove */
+ NULL,
+ 0
+};
+
+krb5_kt_ops hdb_get_kt_ops = {
+ "HDBGET",
+ hdb_resolve,
+ hdb_get_name,
+ hdb_close,
+ NULL,
+ hdb_get_entry,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ 0
};
diff --git a/lib/hdb/libhdb-exports.def b/lib/hdb/libhdb-exports.def
index 4a93f7f92ada..67c0e33e0357 100644
--- a/lib/hdb/libhdb-exports.def
+++ b/lib/hdb/libhdb-exports.def
@@ -1,6 +1,8 @@
EXPORTS
encode_hdb_keyset
hdb_add_master_key
+ hdb_add_current_keys_to_history
+ hdb_change_kvno
hdb_check_db_format
hdb_clear_extension
hdb_clear_master_key
@@ -39,22 +41,26 @@ EXPORTS
hdb_free_master_key
hdb_generate_key_set
hdb_generate_key_set_password
+ hdb_generate_key_set_password_with_ks_tuple
hdb_get_dbinfo
hdb_init_db
hdb_interface_version DATA
hdb_key2principal
+ hdb_kvno2keys
hdb_list_builtin
hdb_lock
hdb_next_enctype2key
hdb_principal2key
hdb_print_entry
hdb_process_master_key
+ hdb_prune_keys
hdb_read_master_key
hdb_replace_extension
hdb_seal_key
hdb_seal_key_mkey
hdb_seal_keys
hdb_seal_keys_mkey
+ hdb_set_last_modified_by
hdb_set_master_key
hdb_set_master_keyfile
hdb_unlock
@@ -69,6 +75,11 @@ EXPORTS
initialize_hdb_error_table_r
hdb_kt_ops
+ hdb_get_kt_ops
+
+; MIT KDB related entries
+ _hdb_mdb_value2entry
+ _hdb_mit_dump2mitdb_entry
; some random bits needed for libkadm
HDBFlags2int
@@ -76,25 +87,34 @@ EXPORTS
copy_Event
copy_HDB_extensions
copy_Key
+ copy_Keys
copy_Salt
decode_HDB_Ext_Aliases
decode_HDB_Ext_PKINIT_acl
decode_HDB_extension
decode_Key
+ decode_Keys
encode_HDB_Ext_Aliases
encode_HDB_Ext_PKINIT_acl
encode_HDB_extension
encode_Key
+ encode_Keys
free_Event
free_HDB_Ext_Aliases
free_HDB_Ext_PKINIT_acl
free_HDB_extension
free_HDB_extensions
free_Key
+ free_Keys
free_Salt
free_hdb_entry
+ free_hdb_keyset
int2HDBFlags
length_HDB_Ext_Aliases
length_HDB_Ext_PKINIT_acl
length_HDB_extension
length_Key
+ length_Keys
+ add_Keys
+ add_HDB_Ext_KeySet
+ remove_Keys
diff --git a/lib/hdb/mkey.c b/lib/hdb/mkey.c
index 9eb98fca32c0..8265776bf7c8 100644
--- a/lib/hdb/mkey.c
+++ b/lib/hdb/mkey.c
@@ -40,6 +40,7 @@ struct hdb_master_key_data {
krb5_keytab_entry keytab;
krb5_crypto crypto;
struct hdb_master_key_data *next;
+ unsigned int key_usage;
};
void
@@ -68,6 +69,7 @@ hdb_process_master_key(krb5_context context,
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
+ (*mkey)->key_usage = HDB_KU_MKEY;
(*mkey)->keytab.vno = kvno;
ret = krb5_parse_name(context, "K/M", &(*mkey)->keytab.principal);
if(ret)
@@ -117,6 +119,7 @@ read_master_keytab(krb5_context context, const char *filename,
krb5_keytab_entry entry;
hdb_master_key p;
+ *mkey = NULL;
ret = krb5_kt_resolve(context, filename, &id);
if(ret)
return ret;
@@ -124,22 +127,26 @@ read_master_keytab(krb5_context context, const char *filename,
ret = krb5_kt_start_seq_get(context, id, &cursor);
if(ret)
goto out;
- *mkey = NULL;
while(krb5_kt_next_entry(context, id, &entry, &cursor) == 0) {
p = calloc(1, sizeof(*p));
- if(p == NULL) {
- krb5_kt_end_seq_get(context, id, &cursor);
+ if (p == NULL) {
ret = ENOMEM;
- goto out;
+ break;
}
p->keytab = entry;
- ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
p->next = *mkey;
*mkey = p;
+ ret = krb5_crypto_init(context, &p->keytab.keyblock, 0, &p->crypto);
+ if (ret)
+ break;
}
krb5_kt_end_seq_get(context, id, &cursor);
out:
krb5_kt_close(context, id);
+ if (ret) {
+ hdb_free_master_key(context, *mkey);
+ *mkey = NULL;
+ }
return ret;
}
@@ -362,8 +369,17 @@ hdb_write_master_key(krb5_context context, const char *filename,
return ret;
}
+krb5_error_code
+_hdb_set_master_key_usage(krb5_context context, HDB *db, unsigned int key_usage)
+{
+ if (db->hdb_master_key_set == 0)
+ return HDB_ERR_NO_MKEY;
+ db->hdb_master_key->key_usage = key_usage;
+ return 0;
+}
+
hdb_master_key
-_hdb_find_master_key(uint32_t *mkvno, hdb_master_key mkey)
+_hdb_find_master_key(unsigned int *mkvno, hdb_master_key mkey)
{
hdb_master_key ret = NULL;
while(mkey) {
@@ -479,6 +495,135 @@ hdb_unseal_keys(krb5_context context, HDB *db, hdb_entry *ent)
return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
}
+/*
+ * Unseal the keys for the given kvno (or all of them) of entry.
+ *
+ * If kvno == 0 -> unseal all.
+ * if kvno != 0 -> unseal the requested kvno and make sure it's the one listed
+ * as the current keyset for the entry (swapping it with a
+ * historical keyset if need be).
+ */
+krb5_error_code
+hdb_unseal_keys_kvno(krb5_context context, HDB *db, krb5_kvno kvno,
+ unsigned flags, hdb_entry *ent)
+{
+ krb5_error_code ret = HDB_ERR_NOENTRY;
+ HDB_extension *ext;
+ HDB_Ext_KeySet *hist_keys;
+ Key *tmp_val;
+ time_t tmp_set_time;
+ unsigned int tmp_len;
+ unsigned int kvno_diff = 0;
+ krb5_kvno tmp_kvno;
+ size_t i, k;
+ int exclude_dead = 0;
+ KerberosTime now = 0;
+
+ if (kvno == 0)
+ ret = 0;
+
+ if ((flags & HDB_F_LIVE_CLNT_KVNOS) || (flags & HDB_F_LIVE_SVC_KVNOS)) {
+ exclude_dead = 1;
+ now = time(NULL);
+ if (HDB_F_LIVE_CLNT_KVNOS)
+ kvno_diff = hdb_entry_get_kvno_diff_clnt(ent);
+ else
+ kvno_diff = hdb_entry_get_kvno_diff_svc(ent);
+ }
+
+ ext = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
+ if (ext == NULL || (&ext->data.u.hist_keys)->len == 0)
+ return hdb_unseal_keys_mkey(context, ent, db->hdb_master_key);
+
+ /* For swapping; see below */
+ tmp_len = ent->keys.len;
+ tmp_val = ent->keys.val;
+ tmp_kvno = ent->kvno;
+ (void) hdb_entry_get_pw_change_time(ent, &tmp_set_time);
+
+ hist_keys = &ext->data.u.hist_keys;
+
+ for (i = 0; i < hist_keys->len; i++) {
+ if (kvno != 0 && hist_keys->val[i].kvno != kvno)
+ continue;
+
+ if (exclude_dead &&
+ ((ent->max_life != NULL &&
+ hist_keys->val[i].set_time != NULL &&
+ (*hist_keys->val[i].set_time) < (now - (*ent->max_life))) ||
+ (hist_keys->val[i].kvno < kvno &&
+ (kvno - hist_keys->val[i].kvno) > kvno_diff)))
+ /*
+ * The KDC may want to to check for this keyset's set_time
+ * is within the TGS principal's max_life, say. But we stop
+ * here.
+ */
+ continue;
+
+ /* Either the keys we want, or all the keys */
+ for (k = 0; k < hist_keys->val[i].keys.len; k++) {
+ ret = hdb_unseal_key_mkey(context,
+ &hist_keys->val[i].keys.val[k],
+ db->hdb_master_key);
+ /*
+ * If kvno == 0 we might not want to bail here! E.g., if we
+ * no longer have the right master key, so just ignore this.
+ *
+ * We could filter out keys that we can't decrypt here
+ * because of HDB_ERR_NO_MKEY. However, it seems safest to
+ * filter them out only where necessary, say, in kadm5.
+ */
+ if (ret && kvno != 0)
+ return ret;
+ if (ret && ret != HDB_ERR_NO_MKEY)
+ return (ret);
+ }
+
+ if (kvno == 0)
+ continue;
+
+ /*
+ * What follows is a bit of a hack.
+ *
+ * This is the keyset we're being asked for, but it's not the
+ * current keyset. So we add the current keyset to the history,
+ * leave the one we were asked for in the history, and pretend
+ * the one we were asked for is also the current keyset.
+ *
+ * This is a bit of a defensive hack in case an entry fetched
+ * this way ever gets modified then stored: if the keyset is not
+ * changed we can detect this and put things back, else we won't
+ * drop any keysets from history by accident.
+ *
+ * Note too that we only ever get called with a non-zero kvno
+ * either in the KDC or in cases where we aren't changing the
+ * HDB entry anyways, which is why this is just a defensive
+ * hack. We also don't fetch specific kvnos in the dump case,
+ * so there's no danger that we'll dump this entry and load it
+ * again, repeatedly causing the history to grow boundelessly.
+ */
+
+ /* Swap key sets */
+ ent->kvno = hist_keys->val[i].kvno;
+ ent->keys.val = hist_keys->val[i].keys.val;
+ ent->keys.len = hist_keys->val[i].keys.len;
+ if (hist_keys->val[i].set_time != NULL)
+ /* Sloppy, but the callers we expect won't care */
+ (void) hdb_entry_set_pw_change_time(context, ent,
+ *hist_keys->val[i].set_time);
+ hist_keys->val[i].kvno = tmp_kvno;
+ hist_keys->val[i].keys.val = tmp_val;
+ hist_keys->val[i].keys.len = tmp_len;
+ if (hist_keys->val[i].set_time != NULL)
+ /* Sloppy, but the callers we expect won't care */
+ *hist_keys->val[i].set_time = tmp_set_time;
+
+ return 0;
+ }
+
+ return (ret);
+}
+
krb5_error_code
hdb_unseal_key(krb5_context context, HDB *db, Key *k)
{
@@ -526,14 +671,31 @@ hdb_seal_key_mkey(krb5_context context, Key *k, hdb_master_key mkey)
krb5_error_code
hdb_seal_keys_mkey(krb5_context context, hdb_entry *ent, hdb_master_key mkey)
{
- size_t i;
- for(i = 0; i < ent->keys.len; i++){
- krb5_error_code ret;
+ HDB_extension *ext;
+ HDB_Ext_KeySet *hist_keys;
+ size_t i, k;
+ krb5_error_code ret;
+ for(i = 0; i < ent->keys.len; i++){
ret = hdb_seal_key_mkey(context, &ent->keys.val[i], mkey);
if (ret)
return ret;
}
+
+ ext = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
+ if (ext == NULL)
+ return 0;
+ hist_keys = &ext->data.u.hist_keys;
+
+ for (i = 0; i < hist_keys->len; i++) {
+ for (k = 0; k < hist_keys->val[i].keys.len; k++) {
+ ret = hdb_seal_key_mkey(context, &hist_keys->val[i].keys.val[k],
+ mkey);
+ if (ret)
+ return ret;
+ }
+ }
+
return 0;
}
@@ -556,9 +718,9 @@ hdb_seal_key(krb5_context context, HDB *db, Key *k)
}
krb5_error_code
-hdb_set_master_key (krb5_context context,
- HDB *db,
- krb5_keyblock *key)
+hdb_set_master_key(krb5_context context,
+ HDB *db,
+ krb5_keyblock *key)
{
krb5_error_code ret;
hdb_master_key mkey;
@@ -571,6 +733,7 @@ hdb_set_master_key (krb5_context context,
des_set_random_generator_seed(key.keyvalue.data);
#endif
db->hdb_master_key_set = 1;
+ db->hdb_master_key->key_usage = HDB_KU_MKEY;
return 0;
}
diff --git a/lib/hdb/print.c b/lib/hdb/print.c
index 697d32d2909c..0d1e2855217d 100644
--- a/lib/hdb/print.c
+++ b/lib/hdb/print.c
@@ -57,44 +57,57 @@
generation number
*/
-static krb5_error_code
+/*
+ * These utility functions return the number of bytes written or -1, and
+ * they set an error in the context.
+ */
+static ssize_t
append_string(krb5_context context, krb5_storage *sp, const char *fmt, ...)
{
- krb5_error_code ret;
+ ssize_t sz;
char *s;
+ int rc;
va_list ap;
va_start(ap, fmt);
- vasprintf(&s, fmt, ap);
+ rc = vasprintf(&s, fmt, ap);
va_end(ap);
- if(s == NULL) {
+ if(rc < 0) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
+ return -1;
}
- ret = krb5_storage_write(sp, s, strlen(s));
+ sz = krb5_storage_write(sp, s, strlen(s));
free(s);
- return ret;
+ return sz;
}
static krb5_error_code
-append_hex(krb5_context context, krb5_storage *sp, krb5_data *data)
+append_hex(krb5_context context, krb5_storage *sp,
+ int always_encode, int lower, krb5_data *data)
{
+ ssize_t sz;
int printable = 1;
size_t i;
char *p;
p = data->data;
- for(i = 0; i < data->length; i++)
- if(!isalnum((unsigned char)p[i]) && p[i] != '.'){
- printable = 0;
- break;
- }
- if(printable)
+ if (!always_encode) {
+ for (i = 0; i < data->length; i++) {
+ if (!isalnum((unsigned char)p[i]) && p[i] != '.'){
+ printable = 0;
+ break;
+ }
+ }
+ }
+ if (printable && !always_encode)
return append_string(context, sp, "\"%.*s\"",
data->length, data->data);
- hex_encode(data->data, data->length, &p);
- append_string(context, sp, "%s", p);
+ sz = hex_encode(data->data, data->length, &p);
+ if (sz == -1) return sz;
+ if (lower)
+ strlwr(p);
+ sz = append_string(context, sp, "%s", p);
free(p);
- return 0;
+ return sz;
}
static char *
@@ -105,22 +118,99 @@ time2str(time_t t)
return buf;
}
-static krb5_error_code
+static ssize_t
append_event(krb5_context context, krb5_storage *sp, Event *ev)
{
- char *pr = NULL;
krb5_error_code ret;
+ ssize_t sz;
+ char *pr = NULL;
if(ev == NULL)
return append_string(context, sp, "- ");
if (ev->principal != NULL) {
ret = krb5_unparse_name(context, ev->principal, &pr);
- if(ret)
- return ret;
+ if (ret) return -1; /* krb5_unparse_name() sets error info */
}
- ret = append_string(context, sp, "%s:%s ",
- time2str(ev->time), pr ? pr : "UNKNOWN");
+ sz = append_string(context, sp, "%s:%s ", time2str(ev->time),
+ pr ? pr : "UNKNOWN");
free(pr);
- return ret;
+ return sz;
+}
+
+#define KRB5_KDB_SALTTYPE_NORMAL 0
+#define KRB5_KDB_SALTTYPE_V4 1
+#define KRB5_KDB_SALTTYPE_NOREALM 2
+#define KRB5_KDB_SALTTYPE_ONLYREALM 3
+#define KRB5_KDB_SALTTYPE_SPECIAL 4
+#define KRB5_KDB_SALTTYPE_AFS3 5
+
+static ssize_t
+append_mit_key(krb5_context context, krb5_storage *sp,
+ krb5_const_principal princ,
+ unsigned int kvno, Key *key)
+{
+ krb5_error_code ret;
+ krb5_salt k5salt;
+ ssize_t sz;
+ size_t key_versions = key->salt ? 2 : 1;
+ size_t decrypted_key_length;
+ char buf[2];
+ krb5_data keylenbytes;
+ unsigned int salttype;
+
+ sz = append_string(context, sp, "\t%u\t%u\t%d\t%d\t", key_versions, kvno,
+ key->key.keytype, key->key.keyvalue.length + 2);
+ if (sz == -1) return sz;
+ ret = krb5_enctype_keysize(context, key->key.keytype, &decrypted_key_length);
+ if (ret) return -1; /* XXX we lose the error code */
+ buf[0] = decrypted_key_length & 0xff;
+ buf[1] = (decrypted_key_length & 0xff00) >> 8;
+ keylenbytes.data = buf;
+ keylenbytes.length = sizeof (buf);
+ sz = append_hex(context, sp, 1, 1, &keylenbytes);
+ if (sz == -1) return sz;
+ sz = append_hex(context, sp, 1, 1, &key->key.keyvalue);
+ if (!key->salt)
+ return sz;
+
+ /* Map salt to MIT KDB style */
+ switch (key->salt->type) {
+ case KRB5_PADATA_PW_SALT:
+
+ /*
+ * Compute normal salt and then see whether it matches the stored one
+ */
+ ret = krb5_get_pw_salt(context, princ, &k5salt);
+ if (ret) return -1;
+ if (k5salt.saltvalue.length == key->salt->salt.length &&
+ memcmp(k5salt.saltvalue.data, key->salt->salt.data,
+ k5salt.saltvalue.length) == 0)
+ salttype = KRB5_KDB_SALTTYPE_NORMAL; /* matches */
+ else if (key->salt->salt.length == strlen(princ->realm) &&
+ memcmp(key->salt->salt.data, princ->realm,
+ key->salt->salt.length) == 0)
+ salttype = KRB5_KDB_SALTTYPE_ONLYREALM; /* matches realm */
+ else if (key->salt->salt.length ==
+ k5salt.saltvalue.length - strlen(princ->realm) &&
+ memcmp((char *)k5salt.saltvalue.data + strlen(princ->realm),
+ key->salt->salt.data, key->salt->salt.length) == 0)
+ salttype = KRB5_KDB_SALTTYPE_NOREALM; /* matches w/o realm */
+ else
+ salttype = KRB5_KDB_SALTTYPE_NORMAL; /* hope for best */
+
+ break;
+
+ case KRB5_PADATA_AFS3_SALT:
+ salttype = KRB5_KDB_SALTTYPE_AFS3;
+ break;
+
+ default:
+ return -1;
+ }
+
+ sz = append_string(context, sp, "\t%u\t%u\t", salttype,
+ key->salt->salt.length);
+ if (sz == -1) return sz;
+ return append_hex(context, sp, 1, 1, &key->salt->salt);
}
static krb5_error_code
@@ -149,12 +239,12 @@ entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
append_string(context, sp, "::%d:",
ent->keys.val[i].key.keytype);
/* --- keydata */
- append_hex(context, sp, &ent->keys.val[i].key.keyvalue);
+ append_hex(context, sp, 0, 0, &ent->keys.val[i].key.keyvalue);
append_string(context, sp, ":");
/* --- salt */
if(ent->keys.val[i].salt){
append_string(context, sp, "%u/", ent->keys.val[i].salt->type);
- append_hex(context, sp, &ent->keys.val[i].salt->salt);
+ append_hex(context, sp, 0, 0, &ent->keys.val[i].salt->salt);
}else
append_string(context, sp, "-");
}
@@ -234,25 +324,224 @@ entry2string_int (krb5_context context, krb5_storage *sp, hdb_entry *ent)
} else
append_string(context, sp, "-");
+ return 0;
+}
+
+#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
+#define KRB5_KDB_DISALLOW_FORWARDABLE 0x00000002
+#define KRB5_KDB_DISALLOW_TGT_BASED 0x00000004
+#define KRB5_KDB_DISALLOW_RENEWABLE 0x00000008
+#define KRB5_KDB_DISALLOW_PROXIABLE 0x00000010
+#define KRB5_KDB_DISALLOW_DUP_SKEY 0x00000020
+#define KRB5_KDB_DISALLOW_ALL_TIX 0x00000040
+#define KRB5_KDB_REQUIRES_PRE_AUTH 0x00000080
+#define KRB5_KDB_REQUIRES_HW_AUTH 0x00000100
+#define KRB5_KDB_REQUIRES_PWCHANGE 0x00000200
+#define KRB5_KDB_DISALLOW_SVR 0x00001000
+#define KRB5_KDB_PWCHANGE_SERVICE 0x00002000
+#define KRB5_KDB_SUPPORT_DESMD5 0x00004000
+#define KRB5_KDB_NEW_PRINC 0x00008000
+
+static int
+flags_to_attr(HDBFlags flags)
+{
+ int a = 0;
+
+ if (!flags.postdate)
+ a |= KRB5_KDB_DISALLOW_POSTDATED;
+ if (!flags.forwardable)
+ a |= KRB5_KDB_DISALLOW_FORWARDABLE;
+ if (flags.initial)
+ a |= KRB5_KDB_DISALLOW_TGT_BASED;
+ if (!flags.renewable)
+ a |= KRB5_KDB_DISALLOW_RENEWABLE;
+ if (!flags.proxiable)
+ a |= KRB5_KDB_DISALLOW_PROXIABLE;
+ if (flags.invalid)
+ a |= KRB5_KDB_DISALLOW_ALL_TIX;
+ if (flags.require_preauth)
+ a |= KRB5_KDB_REQUIRES_PRE_AUTH;
+ if (flags.require_hwauth)
+ a |= KRB5_KDB_REQUIRES_HW_AUTH;
+ if (!flags.server)
+ a |= KRB5_KDB_DISALLOW_SVR;
+ if (flags.change_pw)
+ a |= KRB5_KDB_PWCHANGE_SERVICE;
+ return a;
+}
+
+krb5_error_code
+entry2mit_string_int(krb5_context context, krb5_storage *sp, hdb_entry *ent)
+{
+ krb5_error_code ret;
+ ssize_t sz;
+ size_t i, k;
+ size_t num_tl_data = 0;
+ size_t num_key_data = 0;
+ char *p;
+ HDB_Ext_KeySet *hist_keys = NULL;
+ HDB_extension *extp;
+ time_t last_pw_chg = 0;
+ time_t exp = 0;
+ time_t pwexp = 0;
+ unsigned int max_life = 0;
+ unsigned int max_renew = 0;
+
+ if (ent->modified_by)
+ num_tl_data++;
+
+ ret = hdb_entry_get_pw_change_time(ent, &last_pw_chg);
+ if (ret) return ret;
+ if (last_pw_chg)
+ num_tl_data++;
+
+ extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
+ if (extp)
+ hist_keys = &extp->data.u.hist_keys;
+
+ for (i = 0; i < ent->keys.len;i++) {
+ if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 ||
+ ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5)
+ continue;
+ num_key_data++;
+ }
+ if (hist_keys) {
+ for (i = 0; i < hist_keys->len; i++) {
+ /*
+ * MIT uses the highest kvno as the current kvno instead of
+ * tracking kvno separately, so we can't dump keysets with kvno
+ * higher than the entry's kvno.
+ */
+ if (hist_keys->val[i].kvno >= ent->kvno)
+ continue;
+ for (k = 0; k < hist_keys->val[i].keys.len; k++) {
+ if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
+ ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
+ continue;
+ num_key_data++;
+ }
+ }
+ }
+ ret = krb5_unparse_name(context, ent->principal, &p);
+ if (ret) return ret;
+ sz = append_string(context, sp, "princ\t38\t%u\t%u\t%u\t0\t%s\t%d",
+ strlen(p), num_tl_data, num_key_data, p,
+ flags_to_attr(ent->flags));
+ free(p);
+ if (sz == -1) return ENOMEM;
+
+ if (ent->max_life)
+ max_life = *ent->max_life;
+ if (ent->max_renew)
+ max_renew = *ent->max_renew;
+ if (ent->valid_end)
+ exp = *ent->valid_end;
+ if (ent->pw_end)
+ pwexp = *ent->pw_end;
+
+ sz = append_string(context, sp, "\t%u\t%u\t%u\t%u\t0\t0\t0",
+ max_life, max_renew, exp, pwexp);
+ if (sz == -1) return ENOMEM;
+
+ /* Dump TL data we know: last pw chg and modified_by */
+#define mit_KRB5_TL_LAST_PWD_CHANGE 1
+#define mit_KRB5_TL_MOD_PRINC 2
+ if (last_pw_chg) {
+ krb5_data d;
+ time_t val;
+ unsigned char *ptr;
+
+ ptr = (unsigned char *)&last_pw_chg;
+ val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+ d.data = &val;
+ d.length = sizeof (last_pw_chg);
+ sz = append_string(context, sp, "\t%u\t%u\t",
+ mit_KRB5_TL_LAST_PWD_CHANGE, d.length);
+ if (sz == -1) return ENOMEM;
+ sz = append_hex(context, sp, 1, 1, &d);
+ if (sz == -1) return ENOMEM;
+ }
+ if (ent->modified_by) {
+ krb5_data d;
+ unsigned int val;
+ size_t plen;
+ unsigned char *ptr;
+ char *modby_p;
+
+ ptr = (unsigned char *)&ent->modified_by->time;
+ val = ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24);
+ d.data = &val;
+ d.length = sizeof (ent->modified_by->time);
+ ret = krb5_unparse_name(context, ent->modified_by->principal, &modby_p);
+ if (ret) return ret;
+ plen = strlen(modby_p);
+ sz = append_string(context, sp, "\t%u\t%u\t",
+ mit_KRB5_TL_MOD_PRINC,
+ d.length + plen + 1 /* NULL counted */);
+ if (sz == -1) return ENOMEM;
+ sz = append_hex(context, sp, 1, 1, &d);
+ if (sz == -1) {
+ free(modby_p);
+ return ENOMEM;
+ }
+ d.data = modby_p;
+ d.length = plen + 1;
+ sz = append_hex(context, sp, 1, 1, &d);
+ free(modby_p);
+ if (sz == -1) return ENOMEM;
+ }
+ /*
+ * Dump keys (remembering to not include any with kvno higher than
+ * the entry's because MIT doesn't track entry kvno separately from
+ * the entry's keys -- max kvno is it)
+ */
+ for (i = 0; i < ent->keys.len; i++) {
+ if (ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD4 ||
+ ent->keys.val[i].key.keytype == ETYPE_DES_CBC_MD5)
+ continue;
+ sz = append_mit_key(context, sp, ent->principal, ent->kvno,
+ &ent->keys.val[i]);
+ if (sz == -1) return ENOMEM;
+ }
+ for (i = 0; hist_keys && i < ent->kvno; i++) {
+ size_t m;
+
+ /* dump historical keys */
+ for (k = 0; k < hist_keys->len; k++) {
+ if (hist_keys->val[k].kvno != ent->kvno - i)
+ continue;
+ for (m = 0; m < hist_keys->val[k].keys.len; m++) {
+ if (ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD4 ||
+ ent->keys.val[k].key.keytype == ETYPE_DES_CBC_MD5)
+ continue;
+ sz = append_mit_key(context, sp, ent->principal,
+ hist_keys->val[k].kvno,
+ &hist_keys->val[k].keys.val[m]);
+ if (sz == -1) return ENOMEM;
+ }
+ }
+ }
+ sz = append_string(context, sp, "\t-1;"); /* "extra data" */
+ if (sz == -1) return ENOMEM;
return 0;
}
krb5_error_code
-hdb_entry2string (krb5_context context, hdb_entry *ent, char **str)
+hdb_entry2string(krb5_context context, hdb_entry *ent, char **str)
{
krb5_error_code ret;
krb5_data data;
krb5_storage *sp;
sp = krb5_storage_emem();
- if(sp == NULL) {
+ if (sp == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
ret = entry2string_int(context, sp, ent);
- if(ret) {
+ if (ret) {
krb5_storage_free(sp);
return ret;
}
@@ -267,22 +556,31 @@ hdb_entry2string (krb5_context context, hdb_entry *ent, char **str)
/* print a hdb_entry to (FILE*)data; suitable for hdb_foreach */
krb5_error_code
-hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry, void *data)
+hdb_print_entry(krb5_context context, HDB *db, hdb_entry_ex *entry,
+ void *data)
{
+ struct hdb_print_entry_arg *parg = data;
krb5_error_code ret;
krb5_storage *sp;
- FILE *f = data;
-
- fflush(f);
- sp = krb5_storage_from_fd(fileno(f));
- if(sp == NULL) {
+ fflush(parg->out);
+ sp = krb5_storage_from_fd(fileno(parg->out));
+ if (sp == NULL) {
krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
return ENOMEM;
}
- ret = entry2string_int(context, sp, &entry->entry);
- if(ret) {
+ switch (parg->fmt) {
+ case HDB_DUMP_HEIMDAL:
+ ret = entry2string_int(context, sp, &entry->entry);
+ break;
+ case HDB_DUMP_MIT:
+ ret = entry2mit_string_int(context, sp, &entry->entry);
+ break;
+ default:
+ heim_abort("Only two dump formats supported: Heimdal and MIT");
+ }
+ if (ret) {
krb5_storage_free(sp);
return ret;
}
diff --git a/lib/hdb/test_dbinfo.c b/lib/hdb/test_dbinfo.c
index efe50afb6a2b..b94b75bb3746 100644
--- a/lib/hdb/test_dbinfo.c
+++ b/lib/hdb/test_dbinfo.c
@@ -38,8 +38,8 @@ static int help_flag;
static int version_flag;
struct getargs args[] = {
- { "help", 'h', arg_flag, &help_flag },
- { "version", 0, arg_flag, &version_flag }
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL }
};
static int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/hdb/test_hdbkeys.c b/lib/hdb/test_hdbkeys.c
index 1c3216d143a6..7cf4629b6ce0 100644
--- a/lib/hdb/test_hdbkeys.c
+++ b/lib/hdb/test_hdbkeys.c
@@ -40,9 +40,9 @@ static int version_flag;
static int kvno_integer = 1;
struct getargs args[] = {
- { "kvno", 'd', arg_integer, &kvno_integer },
- { "help", 'h', arg_flag, &help_flag },
- { "version", 0, arg_flag, &version_flag }
+ { "kvno", 'd', arg_integer, &kvno_integer, NULL, NULL },
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL }
};
static int num_args = sizeof(args) / sizeof(args[0]);
@@ -88,6 +88,10 @@ main(int argc, char **argv)
memset(&keyset, 0, sizeof(keyset));
keyset.kvno = kvno_integer;
+ keyset.set_time = malloc(sizeof (*keyset.set_time));
+ if (keyset.set_time == NULL)
+ errx(1, "couldn't allocate set_time field of keyset");
+ *keyset.set_time = time(NULL);
ret = hdb_generate_key_set_password(context, principal, password_str,
&keyset.keys.val, &len);
@@ -108,7 +112,7 @@ main(int argc, char **argv)
krb5_free_context(context);
- ret = base64_encode(data, length, &str);
+ ret = rk_base64_encode(data, length, &str);
if (ret < 0)
errx(1, "base64_encode");
diff --git a/lib/hdb/test_hdbplugin.c b/lib/hdb/test_hdbplugin.c
new file mode 100644
index 000000000000..4ebceb8da072
--- /dev/null
+++ b/lib/hdb/test_hdbplugin.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013 Jeffrey Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "hdb_locl.h"
+
+struct hdb_called {
+ int create;
+ int init;
+ int fini;
+};
+struct hdb_called testresult;
+
+static krb5_error_code
+hdb_test_create(krb5_context context, struct HDB **db, const char *arg)
+{
+ testresult.create = 1;
+ return 0;
+}
+
+static krb5_error_code
+hdb_test_init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
+ testresult.init = 1;
+ return 0;
+}
+
+static void hdb_test_fini(void *ctx)
+{
+ testresult.fini = 1;
+}
+
+struct hdb_method hdb_test =
+{
+#ifdef WIN32
+ /* Not c99 */
+ HDB_INTERFACE_VERSION,
+ hdb_test_init,
+ hdb_test_fini,
+ "test",
+ hdb_test_create
+#else
+ .version = HDB_INTERFACE_VERSION,
+ .init = hdb_test_init,
+ .fini = hdb_test_fini,
+ .prefix = "test",
+ .create = hdb_test_create
+#endif
+};
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ HDB *db;
+
+ setprogname(argv[0]);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_contex");
+
+ ret = krb5_plugin_register(context,
+ PLUGIN_TYPE_DATA, "hdb_test_interface",
+ &hdb_test);
+ if(ret) {
+ krb5_err(context, 1, ret, "krb5_plugin_register");
+ }
+
+ ret = hdb_create(context, &db, "test:test&1234");
+ if(ret) {
+ krb5_err(context, 1, ret, "hdb_create");
+ }
+
+ krb5_free_context(context);
+ return 0;
+}
diff --git a/lib/hdb/test_mkey.c b/lib/hdb/test_mkey.c
index 11032d078bee..97399c6b56af 100644
--- a/lib/hdb/test_mkey.c
+++ b/lib/hdb/test_mkey.c
@@ -8,9 +8,9 @@ static int help_flag;
static int version_flag;
struct getargs args[] = {
- { "mkey-file", 0, arg_string, &mkey_file },
- { "help", 'h', arg_flag, &help_flag },
- { "version", 0, arg_flag, &version_flag }
+ { "mkey-file", 0, arg_string, &mkey_file, NULL, NULL },
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL }
};
static int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/hdb/version-script.map b/lib/hdb/version-script.map
index 50a36cec0aa9..579a2efe7c41 100644
--- a/lib/hdb/version-script.map
+++ b/lib/hdb/version-script.map
@@ -4,6 +4,8 @@ HEIMDAL_HDB_1.0 {
global:
encode_hdb_keyset;
hdb_add_master_key;
+ hdb_add_current_keys_to_history;
+ hdb_change_kvno;
hdb_check_db_format;
hdb_clear_extension;
hdb_clear_master_key;
@@ -42,21 +44,25 @@ HEIMDAL_HDB_1.0 {
hdb_free_master_key;
hdb_generate_key_set;
hdb_generate_key_set_password;
+ hdb_generate_key_set_password_with_ks_tuple;
hdb_get_dbinfo;
hdb_init_db;
hdb_key2principal;
+ hdb_kvno2keys;
hdb_list_builtin;
hdb_lock;
hdb_next_enctype2key;
hdb_principal2key;
hdb_print_entry;
hdb_process_master_key;
+ hdb_prune_keys;
hdb_read_master_key;
hdb_replace_extension;
hdb_seal_key;
hdb_seal_key_mkey;
hdb_seal_keys;
hdb_seal_keys_mkey;
+ hdb_set_last_modified_by;
hdb_set_master_key;
hdb_set_master_keyfile;
hdb_unlock;
@@ -71,36 +77,52 @@ HEIMDAL_HDB_1.0 {
hdb_interface_version;
initialize_hdb_error_table_r;
+ # MIT KDB related entries
+ _hdb_mdb_value2entry;
+ _hdb_mit_dump2mitdb_entry;
+
hdb_kt_ops;
+ hdb_get_kt_ops;
# some random bits needed for libkadm
- HDBFlags2int;
+ add_HDB_Ext_KeySet;
+ add_Keys;
asn1_HDBFlags_units;
copy_Event;
copy_HDB_extensions;
copy_Key;
+ copy_Keys;
copy_Salt;
decode_HDB_Ext_Aliases;
- decode_HDB_Ext_PKINIT_acl;
decode_HDB_extension;
+ decode_HDB_Ext_PKINIT_acl;
decode_Key;
+ decode_Keys;
encode_HDB_Ext_Aliases;
- encode_HDB_Ext_PKINIT_acl;
encode_HDB_extension;
+ encode_HDB_Ext_PKINIT_acl;
encode_Key;
+ encode_Keys;
free_Event;
+ free_hdb_entry;
free_HDB_Ext_Aliases;
- free_HDB_Ext_PKINIT_acl;
free_HDB_extension;
free_HDB_extensions;
+ free_HDB_Ext_PKINIT_acl;
+ free_hdb_keyset;
free_Key;
+ free_Keys;
free_Salt;
- free_hdb_entry;
+ HDBFlags2int;
int2HDBFlags;
length_HDB_Ext_Aliases;
- length_HDB_Ext_PKINIT_acl;
length_HDB_extension;
+ length_HDB_Ext_PKINIT_acl;
length_Key;
+ length_Keys;
+ remove_Keys;
+ add_Keys;
+ add_HDB_Ext_KeySet;
local:
*;
diff --git a/lib/heimdal/NTMakefile b/lib/heimdal/NTMakefile
index 833f4ebe2125..b206cd3a8ba1 100644
--- a/lib/heimdal/NTMakefile
+++ b/lib/heimdal/NTMakefile
@@ -54,6 +54,11 @@ DLLSDKDEPS= \
dnsapi.lib \
shlwapi.lib
+dlllflags=$(dlllflags) /DELAYLOAD:bcrypt.dll
+DLLSDKDEPS=$(DLLSDKDEPS)\
+ bcrypt.lib \
+ delayimp.lib
+
DEF=$(OBJ)\heimdal.def
RES=$(OBJ)\heimdal-version.res
@@ -88,6 +93,3 @@ $(LIBHEIMDAL): $(LIBASN1) $(LIBWIND) $(LIBHCRYPTO) $(LIBHX509) $(LIBKRB5) $(LIBH
!endif
all:: $(LIBHEIMDAL)
-
-clean::
- -$(RM) $(LIBHEIMDAL)
diff --git a/lib/hx509/Makefile.am b/lib/hx509/Makefile.am
index 53669cb7c523..b58deb3e37aa 100644
--- a/lib/hx509/Makefile.am
+++ b/lib/hx509/Makefile.am
@@ -1,5 +1,7 @@
include $(top_srcdir)/Makefile.am.common
+AM_CPPFLAGS += $(INCLUDE_openssl_crypto)
+
lib_LTLIBRARIES = libhx509.la
libhx509_la_LDFLAGS = -version-info 5:0:0
@@ -58,12 +60,11 @@ dist_libhx509_la_SOURCES = \
cms.c \
collector.c \
crypto.c \
+ crypto-ec.c \
doxygen.c \
error.c \
env.c \
file.c \
- hx509-private.h \
- hx509-protos.h \
hx509.h \
hx_locl.h \
sel.c \
@@ -94,8 +95,10 @@ libhx509_la_DEPENDENCIES = version-script.map
libhx509_la_LIBADD = \
$(LIB_com_err) \
$(LIB_hcrypto) \
+ $(LIB_openssl_crypto) \
$(top_builddir)/lib/asn1/libasn1.la \
$(top_builddir)/lib/wind/libwind.la \
+ $(top_builddir)/lib/base/libheimbase.la \
$(LIBADD_roken) \
$(LIB_dlopen)
@@ -108,14 +111,15 @@ libhx509_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
endif
$(libhx509_la_OBJECTS): $(srcdir)/version-script.map $(nodist_include_HEADERS) $(priv_headers)
-libhx509_la_CPPFLAGS = -I$(srcdir)/ref $(INCLUDE_hcrypto)
nodist_libhx509_la_SOURCES = $(BUILT_SOURCES)
$(gen_files_ocsp) ocsp_asn1.hx ocsp_asn1-priv.hx: ocsp_asn1_files
$(gen_files_pkcs10) pkcs10_asn1.hx pkcs10_asn1-priv.hx: pkcs10_asn1_files
$(gen_files_crmf) crmf_asn1.hx crmf_asn1-priv.hx: crmf_asn1_files
-dist_include_HEADERS = hx509.h hx509-protos.h
+dist_include_HEADERS = hx509.h $(srcdir)/hx509-protos.h
+
+noinst_HEADERS = $(srcdir)/hx509-private.h
nodist_include_HEADERS = hx509_err.h
nodist_include_HEADERS += ocsp_asn1.h
@@ -128,34 +132,40 @@ priv_headers += crmf_asn1-priv.h
ocsp_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/ocsp.asn1 $(srcdir)/ocsp.opt
- $(ASN1_COMPILE) --option-file=$(srcdir)/ocsp.opt $(srcdir)/ocsp.asn1 ocsp_asn1 || (rm -f ocsp_asn1_files ; exit 1)
+ $(heim_verbose)$(ASN1_COMPILE) --option-file=$(srcdir)/ocsp.opt $(srcdir)/ocsp.asn1 ocsp_asn1 || (rm -f ocsp_asn1_files ; exit 1)
pkcs10_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/pkcs10.asn1 $(srcdir)/pkcs10.opt
- $(ASN1_COMPILE) --option-file=$(srcdir)/pkcs10.opt $(srcdir)/pkcs10.asn1 pkcs10_asn1 || (rm -f pkcs10_asn1_files ; exit 1)
+ $(heim_verbose)$(ASN1_COMPILE) --option-file=$(srcdir)/pkcs10.opt $(srcdir)/pkcs10.asn1 pkcs10_asn1 || (rm -f pkcs10_asn1_files ; exit 1)
crmf_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/crmf.asn1
- $(ASN1_COMPILE) $(srcdir)/crmf.asn1 crmf_asn1 || (rm -f crmf_asn1_files ; exit 1)
+ $(heim_verbose)$(ASN1_COMPILE) $(srcdir)/crmf.asn1 crmf_asn1 || (rm -f crmf_asn1_files ; exit 1)
+
+ALL_OBJECTS = $(libhx509_la_OBJECTS)
+ALL_OBJECTS += $(hxtool_OBJECTS)
+
+HX509_PROTOS = $(srcdir)/hx509-protos.h $(srcdir)/hx509-private.h
+
+$(ALL_OBJECTS): $(HX509_PROTOS)
-$(libhx509_la_OBJECTS): $(srcdir)/hx509-protos.h $(srcdir)/hx509-private.h $(srcdir)/hx_locl.h
+$(libhx509_la_OBJECTS): $(srcdir)/hx_locl.h
$(libhx509_la_OBJECTS): ocsp_asn1.h pkcs10_asn1.h
-$(srcdir)/hx509-protos.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -R '^(_|^C)' -E HX509_LIB -q -P comment -o hx509-protos.h $(dist_libhx509_la_SOURCES) || rm -f hx509-protos.h
+$(srcdir)/hx509-protos.h: $(dist_libhx509_la_SOURCES)
+ $(heim_verbose)cd $(srcdir) && perl ../../cf/make-proto.pl -R '^(_|^C)' -E HX509_LIB -q -P comment -o hx509-protos.h $(dist_libhx509_la_SOURCES) || rm -f hx509-protos.h
-$(srcdir)/hx509-private.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p hx509-private.h $(dist_libhx509_la_SOURCES) || rm -f hx509-private.h
+$(srcdir)/hx509-private.h: $(dist_libhx509_la_SOURCES)
+ $(heim_verbose)cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p hx509-private.h $(dist_libhx509_la_SOURCES) || rm -f hx509-private.h
bin_PROGRAMS = hxtool
hxtool-commands.c hxtool-commands.h: hxtool-commands.in $(SLC)
- $(SLC) $(srcdir)/hxtool-commands.in
+ $(heim_verbose)$(SLC) $(srcdir)/hxtool-commands.in
dist_hxtool_SOURCES = hxtool.c
nodist_hxtool_SOURCES = hxtool-commands.c hxtool-commands.h
-$(hxtool_OBJECTS): hxtool-commands.h
+$(hxtool_OBJECTS): hxtool-commands.h hx509_err.h
-hxtool_CPPFLAGS = $(INCLUDE_hcrypto)
hxtool_LDADD = \
libhx509.la \
$(top_builddir)/lib/asn1/libasn1.la \
@@ -165,11 +175,11 @@ hxtool_LDADD = \
CLEANFILES = $(BUILT_SOURCES) sel-gram.c sel-lex.c \
$(gen_files_ocsp) ocsp_asn1_files ocsp_asn1{,-priv}.h* \
- ocsp_asn1-template.[ch]* \
+ ocsp_asn1-template.[chx]* \
$(gen_files_pkcs10) pkcs10_asn1_files pkcs10_asn1{,-priv}.h* \
- pkcs10_asn1-template.[ch]* \
+ pkcs10_asn1-template.[chx]* \
$(gen_files_crmf) crmf_asn1_files crmf_asn1{,-priv}.h* \
- crmf_asn1-template.[ch]* \
+ crmf_asn1-template.[chx]* \
$(TESTS) \
hxtool-commands.c hxtool-commands.h *.tmp \
request.out \
@@ -198,11 +208,10 @@ check_PROGRAMS = $(PROGRAM_TESTS) test_soft_pkcs11
LDADD = libhx509.la
-test_soft_pkcs11_LDADD = libhx509.la
-test_soft_pkcs11_CPPFLAGS = -I$(srcdir)/ref
+test_soft_pkcs11_LDADD = libhx509.la $(top_builddir)/lib/asn1/libasn1.la
-test_name_CPPFLAGS = $(INCLUDE_hcrypto)
-test_name_LDADD = libhx509.la $(LIB_roken)
+test_name_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
+test_expr_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
TESTS = $(SCRIPT_TESTS) $(PROGRAM_TESTS)
@@ -226,78 +235,78 @@ SCRIPT_TESTS = \
test_windows \
test_query
-do_subst = sed -e 's,[@]srcdir[@],$(srcdir),g' \
+do_subst = $(heim_verbose)sed -e 's,[@]srcdir[@],$(srcdir),g' \
-e 's,[@]objdir[@],$(top_builddir)/lib/hx509,g' \
-e 's,[@]egrep[@],$(EGREP),g'
test_ca: test_ca.in Makefile
$(do_subst) < $(srcdir)/test_ca.in > test_ca.tmp
- chmod +x test_ca.tmp
+ $(heim_verbose)chmod +x test_ca.tmp
mv test_ca.tmp test_ca
test_cert: test_cert.in Makefile
$(do_subst) < $(srcdir)/test_cert.in > test_cert.tmp
- chmod +x test_cert.tmp
+ $(heim_verbose)chmod +x test_cert.tmp
mv test_cert.tmp test_cert
test_chain: test_chain.in Makefile
$(do_subst) < $(srcdir)/test_chain.in > test_chain.tmp
- chmod +x test_chain.tmp
+ $(heim_verbose)chmod +x test_chain.tmp
mv test_chain.tmp test_chain
test_cms: test_cms.in Makefile
$(do_subst) < $(srcdir)/test_cms.in > test_cms.tmp
- chmod +x test_cms.tmp
+ $(heim_verbose)chmod +x test_cms.tmp
mv test_cms.tmp test_cms
test_crypto: test_crypto.in Makefile
$(do_subst) < $(srcdir)/test_crypto.in > test_crypto.tmp
- chmod +x test_crypto.tmp
+ $(heim_verbose)chmod +x test_crypto.tmp
mv test_crypto.tmp test_crypto
test_nist: test_nist.in Makefile
$(do_subst) < $(srcdir)/test_nist.in > test_nist.tmp
- chmod +x test_nist.tmp
+ $(heim_verbose)chmod +x test_nist.tmp
mv test_nist.tmp test_nist
test_nist2: test_nist2.in Makefile
$(do_subst) < $(srcdir)/test_nist2.in > test_nist2.tmp
- chmod +x test_nist2.tmp
+ $(heim_verbose)chmod +x test_nist2.tmp
mv test_nist2.tmp test_nist2
test_pkcs11: test_pkcs11.in Makefile
$(do_subst) < $(srcdir)/test_pkcs11.in > test_pkcs11.tmp
- chmod +x test_pkcs11.tmp
+ $(heim_verbose)chmod +x test_pkcs11.tmp
mv test_pkcs11.tmp test_pkcs11
test_java_pkcs11: test_java_pkcs11.in Makefile
$(do_subst) < $(srcdir)/test_java_pkcs11.in > test_java_pkcs11.tmp
- chmod +x test_java_pkcs11.tmp
+ $(heim_verbose)chmod +x test_java_pkcs11.tmp
mv test_java_pkcs11.tmp test_java_pkcs11
test_nist_cert: test_nist_cert.in Makefile
$(do_subst) < $(srcdir)/test_nist_cert.in > test_nist_cert.tmp
- chmod +x test_nist_cert.tmp
+ $(heim_verbose)chmod +x test_nist_cert.tmp
mv test_nist_cert.tmp test_nist_cert
test_nist_pkcs12: test_nist_pkcs12.in Makefile
$(do_subst) < $(srcdir)/test_nist_pkcs12.in > test_nist_pkcs12.tmp
- chmod +x test_nist_pkcs12.tmp
+ $(heim_verbose)chmod +x test_nist_pkcs12.tmp
mv test_nist_pkcs12.tmp test_nist_pkcs12
test_req: test_req.in Makefile
$(do_subst) < $(srcdir)/test_req.in > test_req.tmp
- chmod +x test_req.tmp
+ $(heim_verbose)chmod +x test_req.tmp
mv test_req.tmp test_req
test_windows: test_windows.in Makefile
$(do_subst) < $(srcdir)/test_windows.in > test_windows.tmp
- chmod +x test_windows.tmp
+ $(heim_verbose)chmod +x test_windows.tmp
mv test_windows.tmp test_windows
test_query: test_query.in Makefile
$(do_subst) < $(srcdir)/test_query.in > test_query.tmp
- chmod +x test_query.tmp
+ $(heim_verbose)chmod +x test_query.tmp
mv test_query.tmp test_query
EXTRA_DIST = \
@@ -338,16 +347,22 @@ EXTRA_DIST = \
tst-crypto-select5 \
tst-crypto-select6 \
tst-crypto-select7 \
+ data/PKITS_data.zip \
+ data/eccurve.pem \
+ data/https.crt \
+ data/https.key \
+ data/mkcert.sh \
+ data/nist-result2 \
data/n0ll.pem \
- data/secp160r1TestCA.cert.pem \
- data/secp160r1TestCA.key.pem \
- data/secp160r1TestCA.pem \
- data/secp160r2TestClient.cert.pem \
- data/secp160r2TestClient.key.pem \
- data/secp160r2TestClient.pem \
- data/secp160r2TestServer.cert.pem \
- data/secp160r2TestServer.key.pem \
- data/secp160r2TestServer.pem \
+ data/secp256r1TestCA.cert.pem \
+ data/secp256r1TestCA.key.pem \
+ data/secp256r1TestCA.pem \
+ data/secp256r2TestClient.cert.pem \
+ data/secp256r2TestClient.key.pem \
+ data/secp256r2TestClient.pem \
+ data/secp256r2TestServer.cert.pem \
+ data/secp256r2TestServer.key.pem \
+ data/secp256r2TestServer.pem \
data/bleichenbacher-bad.pem \
data/bleichenbacher-good.pem \
data/bleichenbacher-sf-pad-correct.pem \
diff --git a/lib/hx509/Makefile.in b/lib/hx509/Makefile.in
index 98de7d540dd6..cca95bbc04ae 100644
--- a/lib/hx509/Makefile.in
+++ b/lib/hx509/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -22,6 +21,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -40,10 +94,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(dist_include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog TODO sel-gram.c \
- sel-gram.h sel-lex.c
@FRAMEWORK_SECURITY_TRUE@am__append_1 = -framework Security -framework CoreFoundation
@versionscript_TRUE@am__append_2 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
bin_PROGRAMS = hxtool$(EXEEXT)
@@ -64,8 +114,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -78,6 +127,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -97,6 +147,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_include_HEADERS) \
+ $(noinst_HEADERS) $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -122,106 +174,335 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
"$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
-dist_libhx509_la_OBJECTS = libhx509_la-ca.lo libhx509_la-cert.lo \
- libhx509_la-cms.lo libhx509_la-collector.lo \
- libhx509_la-crypto.lo libhx509_la-doxygen.lo \
- libhx509_la-error.lo libhx509_la-env.lo libhx509_la-file.lo \
- libhx509_la-sel.lo libhx509_la-sel-gram.lo \
- libhx509_la-sel-lex.lo libhx509_la-keyset.lo \
- libhx509_la-ks_dir.lo libhx509_la-ks_file.lo \
- libhx509_la-ks_mem.lo libhx509_la-ks_null.lo \
- libhx509_la-ks_p11.lo libhx509_la-ks_p12.lo \
- libhx509_la-ks_keychain.lo libhx509_la-lock.lo \
- libhx509_la-name.lo libhx509_la-peer.lo libhx509_la-print.lo \
- libhx509_la-softp11.lo libhx509_la-req.lo \
- libhx509_la-revoke.lo
-am__objects_1 = libhx509_la-asn1_OCSPBasicOCSPResponse.lo \
- libhx509_la-asn1_OCSPCertID.lo \
- libhx509_la-asn1_OCSPCertStatus.lo \
- libhx509_la-asn1_OCSPInnerRequest.lo \
- libhx509_la-asn1_OCSPKeyHash.lo \
- libhx509_la-asn1_OCSPRequest.lo \
- libhx509_la-asn1_OCSPResponderID.lo \
- libhx509_la-asn1_OCSPResponse.lo \
- libhx509_la-asn1_OCSPResponseBytes.lo \
- libhx509_la-asn1_OCSPResponseData.lo \
- libhx509_la-asn1_OCSPResponseStatus.lo \
- libhx509_la-asn1_OCSPSignature.lo \
- libhx509_la-asn1_OCSPSingleResponse.lo \
- libhx509_la-asn1_OCSPTBSRequest.lo \
- libhx509_la-asn1_OCSPVersion.lo \
- libhx509_la-asn1_id_pkix_ocsp.lo \
- libhx509_la-asn1_id_pkix_ocsp_basic.lo \
- libhx509_la-asn1_id_pkix_ocsp_nonce.lo
-am__objects_2 = libhx509_la-asn1_CertificationRequestInfo.lo \
- libhx509_la-asn1_CertificationRequest.lo
-am__objects_3 = $(am__objects_1) $(am__objects_2) \
- libhx509_la-hx509_err.lo
+dist_libhx509_la_OBJECTS = ca.lo cert.lo cms.lo collector.lo crypto.lo \
+ crypto-ec.lo doxygen.lo error.lo env.lo file.lo sel.lo \
+ sel-gram.lo sel-lex.lo keyset.lo ks_dir.lo ks_file.lo \
+ ks_mem.lo ks_null.lo ks_p11.lo ks_p12.lo ks_keychain.lo \
+ lock.lo name.lo peer.lo print.lo softp11.lo req.lo revoke.lo
+am__objects_1 = asn1_OCSPBasicOCSPResponse.lo asn1_OCSPCertID.lo \
+ asn1_OCSPCertStatus.lo asn1_OCSPInnerRequest.lo \
+ asn1_OCSPKeyHash.lo asn1_OCSPRequest.lo \
+ asn1_OCSPResponderID.lo asn1_OCSPResponse.lo \
+ asn1_OCSPResponseBytes.lo asn1_OCSPResponseData.lo \
+ asn1_OCSPResponseStatus.lo asn1_OCSPSignature.lo \
+ asn1_OCSPSingleResponse.lo asn1_OCSPTBSRequest.lo \
+ asn1_OCSPVersion.lo asn1_id_pkix_ocsp.lo \
+ asn1_id_pkix_ocsp_basic.lo asn1_id_pkix_ocsp_nonce.lo
+am__objects_2 = asn1_CertificationRequestInfo.lo \
+ asn1_CertificationRequest.lo
+am__objects_3 = $(am__objects_1) $(am__objects_2) hx509_err.lo
nodist_libhx509_la_OBJECTS = $(am__objects_3)
libhx509_la_OBJECTS = $(dist_libhx509_la_OBJECTS) \
$(nodist_libhx509_la_OBJECTS)
-libhx509_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libhx509_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libhx509_la_LDFLAGS) $(LDFLAGS) -o $@
am__EXEEXT_1 = test_name$(EXEEXT) test_expr$(EXEEXT)
PROGRAMS = $(bin_PROGRAMS)
-dist_hxtool_OBJECTS = hxtool-hxtool.$(OBJEXT)
-nodist_hxtool_OBJECTS = hxtool-hxtool-commands.$(OBJEXT)
+dist_hxtool_OBJECTS = hxtool.$(OBJEXT)
+nodist_hxtool_OBJECTS = hxtool-commands.$(OBJEXT)
hxtool_OBJECTS = $(dist_hxtool_OBJECTS) $(nodist_hxtool_OBJECTS)
hxtool_DEPENDENCIES = libhx509.la $(top_builddir)/lib/asn1/libasn1.la \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/sl/libsl.la
test_expr_SOURCES = test_expr.c
test_expr_OBJECTS = test_expr.$(OBJEXT)
-test_expr_LDADD = $(LDADD)
-test_expr_DEPENDENCIES = libhx509.la
+test_expr_DEPENDENCIES = libhx509.la $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la
test_name_SOURCES = test_name.c
-test_name_OBJECTS = test_name-test_name.$(OBJEXT)
-test_name_DEPENDENCIES = libhx509.la $(am__DEPENDENCIES_1)
+test_name_OBJECTS = test_name.$(OBJEXT)
+test_name_DEPENDENCIES = libhx509.la $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la
test_soft_pkcs11_SOURCES = test_soft_pkcs11.c
-test_soft_pkcs11_OBJECTS = \
- test_soft_pkcs11-test_soft_pkcs11.$(OBJEXT)
-test_soft_pkcs11_DEPENDENCIES = libhx509.la
+test_soft_pkcs11_OBJECTS = test_soft_pkcs11.$(OBJEXT)
+test_soft_pkcs11_DEPENDENCIES = libhx509.la \
+ $(top_builddir)/lib/asn1/libasn1.la
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
-LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
-LTLEXCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo " LEX " $@;
+am__v_LEX_1 =
YLWRAP = $(top_srcdir)/ylwrap
@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
-YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
-LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
+ -e s/c++$$/h++/ -e s/c$$/h/
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo " YACC " $@;
+am__v_YACC_1 =
SOURCES = $(dist_libhx509_la_SOURCES) $(nodist_libhx509_la_SOURCES) \
$(dist_hxtool_SOURCES) $(nodist_hxtool_SOURCES) test_expr.c \
test_name.c test_soft_pkcs11.c
DIST_SOURCES = $(dist_libhx509_la_SOURCES) $(dist_hxtool_SOURCES) \
test_expr.c test_name.c test_soft_pkcs11.c
-HEADERS = $(dist_include_HEADERS) $(nodist_include_HEADERS)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+HEADERS = $(dist_include_HEADERS) $(nodist_include_HEADERS) \
+ $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver $(top_srcdir)/ylwrap ChangeLog TODO \
+ sel-gram.c sel-gram.h sel-lex.c
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -240,12 +521,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -255,17 +536,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -284,12 +565,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -298,6 +576,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -314,10 +593,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -325,6 +602,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -339,12 +617,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -361,6 +642,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -375,13 +657,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -405,6 +681,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -440,29 +718,37 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
-AM_CPPFLAGS = $(INCLUDES_roken)
+AM_CPPFLAGS = $(INCLUDES_roken) $(INCLUDE_openssl_crypto)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
lib_LTLIBRARIES = libhx509.la
libhx509_la_LDFLAGS = -version-info 5:0:0 $(am__append_1) \
$(am__append_2)
@@ -520,12 +806,11 @@ dist_libhx509_la_SOURCES = \
cms.c \
collector.c \
crypto.c \
+ crypto-ec.c \
doxygen.c \
error.c \
env.c \
file.c \
- hx509-private.h \
- hx509-protos.h \
hx509.h \
hx_locl.h \
sel.c \
@@ -553,20 +838,23 @@ libhx509_la_DEPENDENCIES = version-script.map
libhx509_la_LIBADD = \
$(LIB_com_err) \
$(LIB_hcrypto) \
+ $(LIB_openssl_crypto) \
$(top_builddir)/lib/asn1/libasn1.la \
$(top_builddir)/lib/wind/libwind.la \
+ $(top_builddir)/lib/base/libheimbase.la \
$(LIBADD_roken) \
$(LIB_dlopen)
-libhx509_la_CPPFLAGS = -I$(srcdir)/ref $(INCLUDE_hcrypto)
nodist_libhx509_la_SOURCES = $(BUILT_SOURCES)
-dist_include_HEADERS = hx509.h hx509-protos.h
+dist_include_HEADERS = hx509.h $(srcdir)/hx509-protos.h
+noinst_HEADERS = $(srcdir)/hx509-private.h
nodist_include_HEADERS = hx509_err.h ocsp_asn1.h pkcs10_asn1.h \
crmf_asn1.h
priv_headers = ocsp_asn1-priv.h pkcs10_asn1-priv.h crmf_asn1-priv.h
+ALL_OBJECTS = $(libhx509_la_OBJECTS) $(hxtool_OBJECTS)
+HX509_PROTOS = $(srcdir)/hx509-protos.h $(srcdir)/hx509-private.h
dist_hxtool_SOURCES = hxtool.c
nodist_hxtool_SOURCES = hxtool-commands.c hxtool-commands.h
-hxtool_CPPFLAGS = $(INCLUDE_hcrypto)
hxtool_LDADD = \
libhx509.la \
$(top_builddir)/lib/asn1/libasn1.la \
@@ -576,11 +864,11 @@ hxtool_LDADD = \
CLEANFILES = $(BUILT_SOURCES) sel-gram.c sel-lex.c \
$(gen_files_ocsp) ocsp_asn1_files ocsp_asn1{,-priv}.h* \
- ocsp_asn1-template.[ch]* \
+ ocsp_asn1-template.[chx]* \
$(gen_files_pkcs10) pkcs10_asn1_files pkcs10_asn1{,-priv}.h* \
- pkcs10_asn1-template.[ch]* \
+ pkcs10_asn1-template.[chx]* \
$(gen_files_crmf) crmf_asn1_files crmf_asn1{,-priv}.h* \
- crmf_asn1-template.[ch]* \
+ crmf_asn1-template.[chx]* \
$(TESTS) \
hxtool-commands.c hxtool-commands.h *.tmp \
request.out \
@@ -603,10 +891,9 @@ CLEANFILES = $(BUILT_SOURCES) sel-gram.c sel-lex.c \
#
check_SCRIPTS = $(SCRIPT_TESTS)
LDADD = libhx509.la
-test_soft_pkcs11_LDADD = libhx509.la
-test_soft_pkcs11_CPPFLAGS = -I$(srcdir)/ref
-test_name_CPPFLAGS = $(INCLUDE_hcrypto)
-test_name_LDADD = libhx509.la $(LIB_roken)
+test_soft_pkcs11_LDADD = libhx509.la $(top_builddir)/lib/asn1/libasn1.la
+test_name_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
+test_expr_LDADD = libhx509.la $(LIB_roken) $(top_builddir)/lib/asn1/libasn1.la
PROGRAM_TESTS = \
test_name \
test_expr
@@ -627,7 +914,7 @@ SCRIPT_TESTS = \
test_windows \
test_query
-do_subst = sed -e 's,[@]srcdir[@],$(srcdir),g' \
+do_subst = $(heim_verbose)sed -e 's,[@]srcdir[@],$(srcdir),g' \
-e 's,[@]objdir[@],$(top_builddir)/lib/hx509,g' \
-e 's,[@]egrep[@],$(EGREP),g'
@@ -669,16 +956,22 @@ EXTRA_DIST = \
tst-crypto-select5 \
tst-crypto-select6 \
tst-crypto-select7 \
+ data/PKITS_data.zip \
+ data/eccurve.pem \
+ data/https.crt \
+ data/https.key \
+ data/mkcert.sh \
+ data/nist-result2 \
data/n0ll.pem \
- data/secp160r1TestCA.cert.pem \
- data/secp160r1TestCA.key.pem \
- data/secp160r1TestCA.pem \
- data/secp160r2TestClient.cert.pem \
- data/secp160r2TestClient.key.pem \
- data/secp160r2TestClient.pem \
- data/secp160r2TestServer.cert.pem \
- data/secp160r2TestServer.key.pem \
- data/secp160r2TestServer.pem \
+ data/secp256r1TestCA.cert.pem \
+ data/secp256r1TestCA.key.pem \
+ data/secp256r1TestCA.pem \
+ data/secp256r2TestClient.cert.pem \
+ data/secp256r2TestClient.key.pem \
+ data/secp256r2TestClient.pem \
+ data/secp256r2TestServer.cert.pem \
+ data/secp256r2TestServer.key.pem \
+ data/secp256r2TestServer.pem \
data/bleichenbacher-bad.pem \
data/bleichenbacher-good.pem \
data/bleichenbacher-sf-pad-correct.pem \
@@ -770,7 +1063,7 @@ all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .l .lo .o .obj .y
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .l .lo .log .o .obj .test .test$(EXEEXT) .trs .y
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -783,7 +1076,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/hx509/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/hx509/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -792,6 +1084,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -801,9 +1094,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -811,6 +1104,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -826,29 +1121,35 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
sel-gram.h: sel-gram.c
- @if test ! -f $@; then \
- rm -f sel-gram.c; \
- $(MAKE) $(AM_MAKEFLAGS) sel-gram.c; \
- else :; fi
-libhx509.la: $(libhx509_la_OBJECTS) $(libhx509_la_DEPENDENCIES)
- $(libhx509_la_LINK) -rpath $(libdir) $(libhx509_la_OBJECTS) $(libhx509_la_LIBADD) $(LIBS)
+ @if test ! -f $@; then rm -f sel-gram.c; else :; fi
+ @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) sel-gram.c; else :; fi
+
+libhx509.la: $(libhx509_la_OBJECTS) $(libhx509_la_DEPENDENCIES) $(EXTRA_libhx509_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libhx509_la_LINK) -rpath $(libdir) $(libhx509_la_OBJECTS) $(libhx509_la_LIBADD) $(LIBS)
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -869,7 +1170,8 @@ uninstall-binPROGRAMS:
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
@@ -891,18 +1193,22 @@ clean-checkPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-hxtool$(EXEEXT): $(hxtool_OBJECTS) $(hxtool_DEPENDENCIES)
+
+hxtool$(EXEEXT): $(hxtool_OBJECTS) $(hxtool_DEPENDENCIES) $(EXTRA_hxtool_DEPENDENCIES)
@rm -f hxtool$(EXEEXT)
- $(LINK) $(hxtool_OBJECTS) $(hxtool_LDADD) $(LIBS)
-test_expr$(EXEEXT): $(test_expr_OBJECTS) $(test_expr_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(hxtool_OBJECTS) $(hxtool_LDADD) $(LIBS)
+
+test_expr$(EXEEXT): $(test_expr_OBJECTS) $(test_expr_DEPENDENCIES) $(EXTRA_test_expr_DEPENDENCIES)
@rm -f test_expr$(EXEEXT)
- $(LINK) $(test_expr_OBJECTS) $(test_expr_LDADD) $(LIBS)
-test_name$(EXEEXT): $(test_name_OBJECTS) $(test_name_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_expr_OBJECTS) $(test_expr_LDADD) $(LIBS)
+
+test_name$(EXEEXT): $(test_name_OBJECTS) $(test_name_DEPENDENCIES) $(EXTRA_test_name_DEPENDENCIES)
@rm -f test_name$(EXEEXT)
- $(LINK) $(test_name_OBJECTS) $(test_name_LDADD) $(LIBS)
-test_soft_pkcs11$(EXEEXT): $(test_soft_pkcs11_OBJECTS) $(test_soft_pkcs11_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_name_OBJECTS) $(test_name_LDADD) $(LIBS)
+
+test_soft_pkcs11$(EXEEXT): $(test_soft_pkcs11_OBJECTS) $(test_soft_pkcs11_DEPENDENCIES) $(EXTRA_test_soft_pkcs11_DEPENDENCIES)
@rm -f test_soft_pkcs11$(EXEEXT)
- $(LINK) $(test_soft_pkcs11_OBJECTS) $(test_soft_pkcs11_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_soft_pkcs11_OBJECTS) $(test_soft_pkcs11_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -910,478 +1216,87 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hxtool-hxtool-commands.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hxtool-hxtool.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_CertificationRequest.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_CertificationRequestInfo.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPBasicOCSPResponse.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPCertID.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPCertStatus.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPInnerRequest.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPKeyHash.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPRequest.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPResponderID.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPResponse.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPResponseBytes.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPResponseData.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPResponseStatus.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPSignature.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPSingleResponse.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPTBSRequest.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_OCSPVersion.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_basic.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_nonce.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ca.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-cert.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-cms.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-collector.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-crypto.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-doxygen.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-env.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-error.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-file.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-hx509_err.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-keyset.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ks_dir.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ks_file.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ks_keychain.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ks_mem.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ks_null.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ks_p11.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-ks_p12.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-lock.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-name.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-peer.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-print.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-req.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-revoke.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-sel-gram.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-sel-lex.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-sel.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhx509_la-softp11.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_CertificationRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_CertificationRequestInfo.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPBasicOCSPResponse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPCertID.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPCertStatus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPInnerRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPKeyHash.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPResponderID.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPResponse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPResponseBytes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPResponseData.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPResponseStatus.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPSignature.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPSingleResponse.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPTBSRequest.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_OCSPVersion.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_id_pkix_ocsp.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_id_pkix_ocsp_basic.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asn1_id_pkix_ocsp_nonce.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ca.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cert.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cms.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/collector.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto-ec.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/doxygen.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/env.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/file.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hx509_err.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hxtool-commands.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hxtool.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyset.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks_dir.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks_file.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks_keychain.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks_mem.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks_null.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks_p11.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ks_p12.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lock.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/name.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/req.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/revoke.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sel-gram.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sel-lex.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sel.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/softp11.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_expr.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_name-test_name.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_soft_pkcs11-test_soft_pkcs11.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_name.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_soft_pkcs11.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
-
-libhx509_la-ca.lo: ca.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ca.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ca.Tpo -c -o libhx509_la-ca.lo `test -f 'ca.c' || echo '$(srcdir)/'`ca.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ca.Tpo $(DEPDIR)/libhx509_la-ca.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ca.c' object='libhx509_la-ca.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ca.lo `test -f 'ca.c' || echo '$(srcdir)/'`ca.c
-
-libhx509_la-cert.lo: cert.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-cert.lo -MD -MP -MF $(DEPDIR)/libhx509_la-cert.Tpo -c -o libhx509_la-cert.lo `test -f 'cert.c' || echo '$(srcdir)/'`cert.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-cert.Tpo $(DEPDIR)/libhx509_la-cert.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cert.c' object='libhx509_la-cert.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-cert.lo `test -f 'cert.c' || echo '$(srcdir)/'`cert.c
-
-libhx509_la-cms.lo: cms.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-cms.lo -MD -MP -MF $(DEPDIR)/libhx509_la-cms.Tpo -c -o libhx509_la-cms.lo `test -f 'cms.c' || echo '$(srcdir)/'`cms.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-cms.Tpo $(DEPDIR)/libhx509_la-cms.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cms.c' object='libhx509_la-cms.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-cms.lo `test -f 'cms.c' || echo '$(srcdir)/'`cms.c
-
-libhx509_la-collector.lo: collector.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-collector.lo -MD -MP -MF $(DEPDIR)/libhx509_la-collector.Tpo -c -o libhx509_la-collector.lo `test -f 'collector.c' || echo '$(srcdir)/'`collector.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-collector.Tpo $(DEPDIR)/libhx509_la-collector.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='collector.c' object='libhx509_la-collector.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-collector.lo `test -f 'collector.c' || echo '$(srcdir)/'`collector.c
-
-libhx509_la-crypto.lo: crypto.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-crypto.lo -MD -MP -MF $(DEPDIR)/libhx509_la-crypto.Tpo -c -o libhx509_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-crypto.Tpo $(DEPDIR)/libhx509_la-crypto.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto.c' object='libhx509_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
-
-libhx509_la-doxygen.lo: doxygen.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-doxygen.lo -MD -MP -MF $(DEPDIR)/libhx509_la-doxygen.Tpo -c -o libhx509_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-doxygen.Tpo $(DEPDIR)/libhx509_la-doxygen.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='doxygen.c' object='libhx509_la-doxygen.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
-
-libhx509_la-error.lo: error.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-error.lo -MD -MP -MF $(DEPDIR)/libhx509_la-error.Tpo -c -o libhx509_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-error.Tpo $(DEPDIR)/libhx509_la-error.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error.c' object='libhx509_la-error.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
-
-libhx509_la-env.lo: env.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-env.lo -MD -MP -MF $(DEPDIR)/libhx509_la-env.Tpo -c -o libhx509_la-env.lo `test -f 'env.c' || echo '$(srcdir)/'`env.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-env.Tpo $(DEPDIR)/libhx509_la-env.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='env.c' object='libhx509_la-env.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-env.lo `test -f 'env.c' || echo '$(srcdir)/'`env.c
-
-libhx509_la-file.lo: file.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-file.lo -MD -MP -MF $(DEPDIR)/libhx509_la-file.Tpo -c -o libhx509_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-file.Tpo $(DEPDIR)/libhx509_la-file.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='file.c' object='libhx509_la-file.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
-
-libhx509_la-sel.lo: sel.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-sel.lo -MD -MP -MF $(DEPDIR)/libhx509_la-sel.Tpo -c -o libhx509_la-sel.lo `test -f 'sel.c' || echo '$(srcdir)/'`sel.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-sel.Tpo $(DEPDIR)/libhx509_la-sel.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sel.c' object='libhx509_la-sel.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-sel.lo `test -f 'sel.c' || echo '$(srcdir)/'`sel.c
-
-libhx509_la-sel-gram.lo: sel-gram.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-sel-gram.lo -MD -MP -MF $(DEPDIR)/libhx509_la-sel-gram.Tpo -c -o libhx509_la-sel-gram.lo `test -f 'sel-gram.c' || echo '$(srcdir)/'`sel-gram.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-sel-gram.Tpo $(DEPDIR)/libhx509_la-sel-gram.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sel-gram.c' object='libhx509_la-sel-gram.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-sel-gram.lo `test -f 'sel-gram.c' || echo '$(srcdir)/'`sel-gram.c
-
-libhx509_la-sel-lex.lo: sel-lex.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-sel-lex.lo -MD -MP -MF $(DEPDIR)/libhx509_la-sel-lex.Tpo -c -o libhx509_la-sel-lex.lo `test -f 'sel-lex.c' || echo '$(srcdir)/'`sel-lex.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-sel-lex.Tpo $(DEPDIR)/libhx509_la-sel-lex.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sel-lex.c' object='libhx509_la-sel-lex.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-sel-lex.lo `test -f 'sel-lex.c' || echo '$(srcdir)/'`sel-lex.c
-
-libhx509_la-keyset.lo: keyset.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-keyset.lo -MD -MP -MF $(DEPDIR)/libhx509_la-keyset.Tpo -c -o libhx509_la-keyset.lo `test -f 'keyset.c' || echo '$(srcdir)/'`keyset.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-keyset.Tpo $(DEPDIR)/libhx509_la-keyset.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keyset.c' object='libhx509_la-keyset.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-keyset.lo `test -f 'keyset.c' || echo '$(srcdir)/'`keyset.c
-
-libhx509_la-ks_dir.lo: ks_dir.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ks_dir.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ks_dir.Tpo -c -o libhx509_la-ks_dir.lo `test -f 'ks_dir.c' || echo '$(srcdir)/'`ks_dir.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ks_dir.Tpo $(DEPDIR)/libhx509_la-ks_dir.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ks_dir.c' object='libhx509_la-ks_dir.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ks_dir.lo `test -f 'ks_dir.c' || echo '$(srcdir)/'`ks_dir.c
-
-libhx509_la-ks_file.lo: ks_file.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ks_file.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ks_file.Tpo -c -o libhx509_la-ks_file.lo `test -f 'ks_file.c' || echo '$(srcdir)/'`ks_file.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ks_file.Tpo $(DEPDIR)/libhx509_la-ks_file.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ks_file.c' object='libhx509_la-ks_file.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ks_file.lo `test -f 'ks_file.c' || echo '$(srcdir)/'`ks_file.c
-
-libhx509_la-ks_mem.lo: ks_mem.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ks_mem.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ks_mem.Tpo -c -o libhx509_la-ks_mem.lo `test -f 'ks_mem.c' || echo '$(srcdir)/'`ks_mem.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ks_mem.Tpo $(DEPDIR)/libhx509_la-ks_mem.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ks_mem.c' object='libhx509_la-ks_mem.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ks_mem.lo `test -f 'ks_mem.c' || echo '$(srcdir)/'`ks_mem.c
-
-libhx509_la-ks_null.lo: ks_null.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ks_null.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ks_null.Tpo -c -o libhx509_la-ks_null.lo `test -f 'ks_null.c' || echo '$(srcdir)/'`ks_null.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ks_null.Tpo $(DEPDIR)/libhx509_la-ks_null.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ks_null.c' object='libhx509_la-ks_null.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ks_null.lo `test -f 'ks_null.c' || echo '$(srcdir)/'`ks_null.c
-
-libhx509_la-ks_p11.lo: ks_p11.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ks_p11.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ks_p11.Tpo -c -o libhx509_la-ks_p11.lo `test -f 'ks_p11.c' || echo '$(srcdir)/'`ks_p11.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ks_p11.Tpo $(DEPDIR)/libhx509_la-ks_p11.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ks_p11.c' object='libhx509_la-ks_p11.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ks_p11.lo `test -f 'ks_p11.c' || echo '$(srcdir)/'`ks_p11.c
-
-libhx509_la-ks_p12.lo: ks_p12.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ks_p12.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ks_p12.Tpo -c -o libhx509_la-ks_p12.lo `test -f 'ks_p12.c' || echo '$(srcdir)/'`ks_p12.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ks_p12.Tpo $(DEPDIR)/libhx509_la-ks_p12.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ks_p12.c' object='libhx509_la-ks_p12.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ks_p12.lo `test -f 'ks_p12.c' || echo '$(srcdir)/'`ks_p12.c
-
-libhx509_la-ks_keychain.lo: ks_keychain.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-ks_keychain.lo -MD -MP -MF $(DEPDIR)/libhx509_la-ks_keychain.Tpo -c -o libhx509_la-ks_keychain.lo `test -f 'ks_keychain.c' || echo '$(srcdir)/'`ks_keychain.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-ks_keychain.Tpo $(DEPDIR)/libhx509_la-ks_keychain.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ks_keychain.c' object='libhx509_la-ks_keychain.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-ks_keychain.lo `test -f 'ks_keychain.c' || echo '$(srcdir)/'`ks_keychain.c
-
-libhx509_la-lock.lo: lock.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-lock.lo -MD -MP -MF $(DEPDIR)/libhx509_la-lock.Tpo -c -o libhx509_la-lock.lo `test -f 'lock.c' || echo '$(srcdir)/'`lock.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-lock.Tpo $(DEPDIR)/libhx509_la-lock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='lock.c' object='libhx509_la-lock.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-lock.lo `test -f 'lock.c' || echo '$(srcdir)/'`lock.c
-
-libhx509_la-name.lo: name.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-name.lo -MD -MP -MF $(DEPDIR)/libhx509_la-name.Tpo -c -o libhx509_la-name.lo `test -f 'name.c' || echo '$(srcdir)/'`name.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-name.Tpo $(DEPDIR)/libhx509_la-name.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='name.c' object='libhx509_la-name.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-name.lo `test -f 'name.c' || echo '$(srcdir)/'`name.c
-
-libhx509_la-peer.lo: peer.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-peer.lo -MD -MP -MF $(DEPDIR)/libhx509_la-peer.Tpo -c -o libhx509_la-peer.lo `test -f 'peer.c' || echo '$(srcdir)/'`peer.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-peer.Tpo $(DEPDIR)/libhx509_la-peer.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='peer.c' object='libhx509_la-peer.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-peer.lo `test -f 'peer.c' || echo '$(srcdir)/'`peer.c
-
-libhx509_la-print.lo: print.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-print.lo -MD -MP -MF $(DEPDIR)/libhx509_la-print.Tpo -c -o libhx509_la-print.lo `test -f 'print.c' || echo '$(srcdir)/'`print.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-print.Tpo $(DEPDIR)/libhx509_la-print.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='print.c' object='libhx509_la-print.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-print.lo `test -f 'print.c' || echo '$(srcdir)/'`print.c
-
-libhx509_la-softp11.lo: softp11.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-softp11.lo -MD -MP -MF $(DEPDIR)/libhx509_la-softp11.Tpo -c -o libhx509_la-softp11.lo `test -f 'softp11.c' || echo '$(srcdir)/'`softp11.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-softp11.Tpo $(DEPDIR)/libhx509_la-softp11.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='softp11.c' object='libhx509_la-softp11.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-softp11.lo `test -f 'softp11.c' || echo '$(srcdir)/'`softp11.c
-
-libhx509_la-req.lo: req.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-req.lo -MD -MP -MF $(DEPDIR)/libhx509_la-req.Tpo -c -o libhx509_la-req.lo `test -f 'req.c' || echo '$(srcdir)/'`req.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-req.Tpo $(DEPDIR)/libhx509_la-req.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='req.c' object='libhx509_la-req.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-req.lo `test -f 'req.c' || echo '$(srcdir)/'`req.c
-
-libhx509_la-revoke.lo: revoke.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-revoke.lo -MD -MP -MF $(DEPDIR)/libhx509_la-revoke.Tpo -c -o libhx509_la-revoke.lo `test -f 'revoke.c' || echo '$(srcdir)/'`revoke.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-revoke.Tpo $(DEPDIR)/libhx509_la-revoke.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='revoke.c' object='libhx509_la-revoke.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-revoke.lo `test -f 'revoke.c' || echo '$(srcdir)/'`revoke.c
-
-libhx509_la-asn1_OCSPBasicOCSPResponse.lo: asn1_OCSPBasicOCSPResponse.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPBasicOCSPResponse.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPBasicOCSPResponse.Tpo -c -o libhx509_la-asn1_OCSPBasicOCSPResponse.lo `test -f 'asn1_OCSPBasicOCSPResponse.c' || echo '$(srcdir)/'`asn1_OCSPBasicOCSPResponse.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPBasicOCSPResponse.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPBasicOCSPResponse.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPBasicOCSPResponse.c' object='libhx509_la-asn1_OCSPBasicOCSPResponse.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPBasicOCSPResponse.lo `test -f 'asn1_OCSPBasicOCSPResponse.c' || echo '$(srcdir)/'`asn1_OCSPBasicOCSPResponse.c
-
-libhx509_la-asn1_OCSPCertID.lo: asn1_OCSPCertID.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPCertID.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPCertID.Tpo -c -o libhx509_la-asn1_OCSPCertID.lo `test -f 'asn1_OCSPCertID.c' || echo '$(srcdir)/'`asn1_OCSPCertID.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPCertID.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPCertID.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPCertID.c' object='libhx509_la-asn1_OCSPCertID.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPCertID.lo `test -f 'asn1_OCSPCertID.c' || echo '$(srcdir)/'`asn1_OCSPCertID.c
-
-libhx509_la-asn1_OCSPCertStatus.lo: asn1_OCSPCertStatus.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPCertStatus.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPCertStatus.Tpo -c -o libhx509_la-asn1_OCSPCertStatus.lo `test -f 'asn1_OCSPCertStatus.c' || echo '$(srcdir)/'`asn1_OCSPCertStatus.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPCertStatus.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPCertStatus.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPCertStatus.c' object='libhx509_la-asn1_OCSPCertStatus.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPCertStatus.lo `test -f 'asn1_OCSPCertStatus.c' || echo '$(srcdir)/'`asn1_OCSPCertStatus.c
-
-libhx509_la-asn1_OCSPInnerRequest.lo: asn1_OCSPInnerRequest.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPInnerRequest.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPInnerRequest.Tpo -c -o libhx509_la-asn1_OCSPInnerRequest.lo `test -f 'asn1_OCSPInnerRequest.c' || echo '$(srcdir)/'`asn1_OCSPInnerRequest.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPInnerRequest.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPInnerRequest.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPInnerRequest.c' object='libhx509_la-asn1_OCSPInnerRequest.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPInnerRequest.lo `test -f 'asn1_OCSPInnerRequest.c' || echo '$(srcdir)/'`asn1_OCSPInnerRequest.c
-
-libhx509_la-asn1_OCSPKeyHash.lo: asn1_OCSPKeyHash.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPKeyHash.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPKeyHash.Tpo -c -o libhx509_la-asn1_OCSPKeyHash.lo `test -f 'asn1_OCSPKeyHash.c' || echo '$(srcdir)/'`asn1_OCSPKeyHash.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPKeyHash.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPKeyHash.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPKeyHash.c' object='libhx509_la-asn1_OCSPKeyHash.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPKeyHash.lo `test -f 'asn1_OCSPKeyHash.c' || echo '$(srcdir)/'`asn1_OCSPKeyHash.c
-
-libhx509_la-asn1_OCSPRequest.lo: asn1_OCSPRequest.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPRequest.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPRequest.Tpo -c -o libhx509_la-asn1_OCSPRequest.lo `test -f 'asn1_OCSPRequest.c' || echo '$(srcdir)/'`asn1_OCSPRequest.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPRequest.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPRequest.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPRequest.c' object='libhx509_la-asn1_OCSPRequest.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPRequest.lo `test -f 'asn1_OCSPRequest.c' || echo '$(srcdir)/'`asn1_OCSPRequest.c
-
-libhx509_la-asn1_OCSPResponderID.lo: asn1_OCSPResponderID.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPResponderID.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPResponderID.Tpo -c -o libhx509_la-asn1_OCSPResponderID.lo `test -f 'asn1_OCSPResponderID.c' || echo '$(srcdir)/'`asn1_OCSPResponderID.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPResponderID.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPResponderID.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPResponderID.c' object='libhx509_la-asn1_OCSPResponderID.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPResponderID.lo `test -f 'asn1_OCSPResponderID.c' || echo '$(srcdir)/'`asn1_OCSPResponderID.c
-
-libhx509_la-asn1_OCSPResponse.lo: asn1_OCSPResponse.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPResponse.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPResponse.Tpo -c -o libhx509_la-asn1_OCSPResponse.lo `test -f 'asn1_OCSPResponse.c' || echo '$(srcdir)/'`asn1_OCSPResponse.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPResponse.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPResponse.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPResponse.c' object='libhx509_la-asn1_OCSPResponse.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPResponse.lo `test -f 'asn1_OCSPResponse.c' || echo '$(srcdir)/'`asn1_OCSPResponse.c
-
-libhx509_la-asn1_OCSPResponseBytes.lo: asn1_OCSPResponseBytes.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPResponseBytes.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPResponseBytes.Tpo -c -o libhx509_la-asn1_OCSPResponseBytes.lo `test -f 'asn1_OCSPResponseBytes.c' || echo '$(srcdir)/'`asn1_OCSPResponseBytes.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPResponseBytes.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPResponseBytes.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPResponseBytes.c' object='libhx509_la-asn1_OCSPResponseBytes.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPResponseBytes.lo `test -f 'asn1_OCSPResponseBytes.c' || echo '$(srcdir)/'`asn1_OCSPResponseBytes.c
-
-libhx509_la-asn1_OCSPResponseData.lo: asn1_OCSPResponseData.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPResponseData.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPResponseData.Tpo -c -o libhx509_la-asn1_OCSPResponseData.lo `test -f 'asn1_OCSPResponseData.c' || echo '$(srcdir)/'`asn1_OCSPResponseData.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPResponseData.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPResponseData.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPResponseData.c' object='libhx509_la-asn1_OCSPResponseData.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPResponseData.lo `test -f 'asn1_OCSPResponseData.c' || echo '$(srcdir)/'`asn1_OCSPResponseData.c
-
-libhx509_la-asn1_OCSPResponseStatus.lo: asn1_OCSPResponseStatus.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPResponseStatus.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPResponseStatus.Tpo -c -o libhx509_la-asn1_OCSPResponseStatus.lo `test -f 'asn1_OCSPResponseStatus.c' || echo '$(srcdir)/'`asn1_OCSPResponseStatus.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPResponseStatus.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPResponseStatus.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPResponseStatus.c' object='libhx509_la-asn1_OCSPResponseStatus.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPResponseStatus.lo `test -f 'asn1_OCSPResponseStatus.c' || echo '$(srcdir)/'`asn1_OCSPResponseStatus.c
-
-libhx509_la-asn1_OCSPSignature.lo: asn1_OCSPSignature.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPSignature.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPSignature.Tpo -c -o libhx509_la-asn1_OCSPSignature.lo `test -f 'asn1_OCSPSignature.c' || echo '$(srcdir)/'`asn1_OCSPSignature.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPSignature.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPSignature.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPSignature.c' object='libhx509_la-asn1_OCSPSignature.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPSignature.lo `test -f 'asn1_OCSPSignature.c' || echo '$(srcdir)/'`asn1_OCSPSignature.c
-
-libhx509_la-asn1_OCSPSingleResponse.lo: asn1_OCSPSingleResponse.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPSingleResponse.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPSingleResponse.Tpo -c -o libhx509_la-asn1_OCSPSingleResponse.lo `test -f 'asn1_OCSPSingleResponse.c' || echo '$(srcdir)/'`asn1_OCSPSingleResponse.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPSingleResponse.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPSingleResponse.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPSingleResponse.c' object='libhx509_la-asn1_OCSPSingleResponse.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPSingleResponse.lo `test -f 'asn1_OCSPSingleResponse.c' || echo '$(srcdir)/'`asn1_OCSPSingleResponse.c
-
-libhx509_la-asn1_OCSPTBSRequest.lo: asn1_OCSPTBSRequest.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPTBSRequest.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPTBSRequest.Tpo -c -o libhx509_la-asn1_OCSPTBSRequest.lo `test -f 'asn1_OCSPTBSRequest.c' || echo '$(srcdir)/'`asn1_OCSPTBSRequest.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPTBSRequest.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPTBSRequest.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPTBSRequest.c' object='libhx509_la-asn1_OCSPTBSRequest.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPTBSRequest.lo `test -f 'asn1_OCSPTBSRequest.c' || echo '$(srcdir)/'`asn1_OCSPTBSRequest.c
-
-libhx509_la-asn1_OCSPVersion.lo: asn1_OCSPVersion.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_OCSPVersion.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_OCSPVersion.Tpo -c -o libhx509_la-asn1_OCSPVersion.lo `test -f 'asn1_OCSPVersion.c' || echo '$(srcdir)/'`asn1_OCSPVersion.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_OCSPVersion.Tpo $(DEPDIR)/libhx509_la-asn1_OCSPVersion.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_OCSPVersion.c' object='libhx509_la-asn1_OCSPVersion.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_OCSPVersion.lo `test -f 'asn1_OCSPVersion.c' || echo '$(srcdir)/'`asn1_OCSPVersion.c
-
-libhx509_la-asn1_id_pkix_ocsp.lo: asn1_id_pkix_ocsp.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_id_pkix_ocsp.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp.Tpo -c -o libhx509_la-asn1_id_pkix_ocsp.lo `test -f 'asn1_id_pkix_ocsp.c' || echo '$(srcdir)/'`asn1_id_pkix_ocsp.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp.Tpo $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_id_pkix_ocsp.c' object='libhx509_la-asn1_id_pkix_ocsp.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_id_pkix_ocsp.lo `test -f 'asn1_id_pkix_ocsp.c' || echo '$(srcdir)/'`asn1_id_pkix_ocsp.c
-
-libhx509_la-asn1_id_pkix_ocsp_basic.lo: asn1_id_pkix_ocsp_basic.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_id_pkix_ocsp_basic.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_basic.Tpo -c -o libhx509_la-asn1_id_pkix_ocsp_basic.lo `test -f 'asn1_id_pkix_ocsp_basic.c' || echo '$(srcdir)/'`asn1_id_pkix_ocsp_basic.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_basic.Tpo $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_basic.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_id_pkix_ocsp_basic.c' object='libhx509_la-asn1_id_pkix_ocsp_basic.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_id_pkix_ocsp_basic.lo `test -f 'asn1_id_pkix_ocsp_basic.c' || echo '$(srcdir)/'`asn1_id_pkix_ocsp_basic.c
-
-libhx509_la-asn1_id_pkix_ocsp_nonce.lo: asn1_id_pkix_ocsp_nonce.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_id_pkix_ocsp_nonce.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_nonce.Tpo -c -o libhx509_la-asn1_id_pkix_ocsp_nonce.lo `test -f 'asn1_id_pkix_ocsp_nonce.c' || echo '$(srcdir)/'`asn1_id_pkix_ocsp_nonce.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_nonce.Tpo $(DEPDIR)/libhx509_la-asn1_id_pkix_ocsp_nonce.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_id_pkix_ocsp_nonce.c' object='libhx509_la-asn1_id_pkix_ocsp_nonce.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_id_pkix_ocsp_nonce.lo `test -f 'asn1_id_pkix_ocsp_nonce.c' || echo '$(srcdir)/'`asn1_id_pkix_ocsp_nonce.c
-
-libhx509_la-asn1_CertificationRequestInfo.lo: asn1_CertificationRequestInfo.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_CertificationRequestInfo.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_CertificationRequestInfo.Tpo -c -o libhx509_la-asn1_CertificationRequestInfo.lo `test -f 'asn1_CertificationRequestInfo.c' || echo '$(srcdir)/'`asn1_CertificationRequestInfo.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_CertificationRequestInfo.Tpo $(DEPDIR)/libhx509_la-asn1_CertificationRequestInfo.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_CertificationRequestInfo.c' object='libhx509_la-asn1_CertificationRequestInfo.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_CertificationRequestInfo.lo `test -f 'asn1_CertificationRequestInfo.c' || echo '$(srcdir)/'`asn1_CertificationRequestInfo.c
-
-libhx509_la-asn1_CertificationRequest.lo: asn1_CertificationRequest.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-asn1_CertificationRequest.lo -MD -MP -MF $(DEPDIR)/libhx509_la-asn1_CertificationRequest.Tpo -c -o libhx509_la-asn1_CertificationRequest.lo `test -f 'asn1_CertificationRequest.c' || echo '$(srcdir)/'`asn1_CertificationRequest.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-asn1_CertificationRequest.Tpo $(DEPDIR)/libhx509_la-asn1_CertificationRequest.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_CertificationRequest.c' object='libhx509_la-asn1_CertificationRequest.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-asn1_CertificationRequest.lo `test -f 'asn1_CertificationRequest.c' || echo '$(srcdir)/'`asn1_CertificationRequest.c
-
-libhx509_la-hx509_err.lo: hx509_err.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libhx509_la-hx509_err.lo -MD -MP -MF $(DEPDIR)/libhx509_la-hx509_err.Tpo -c -o libhx509_la-hx509_err.lo `test -f 'hx509_err.c' || echo '$(srcdir)/'`hx509_err.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libhx509_la-hx509_err.Tpo $(DEPDIR)/libhx509_la-hx509_err.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hx509_err.c' object='libhx509_la-hx509_err.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libhx509_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libhx509_la-hx509_err.lo `test -f 'hx509_err.c' || echo '$(srcdir)/'`hx509_err.c
-
-hxtool-hxtool.o: hxtool.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hxtool-hxtool.o -MD -MP -MF $(DEPDIR)/hxtool-hxtool.Tpo -c -o hxtool-hxtool.o `test -f 'hxtool.c' || echo '$(srcdir)/'`hxtool.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/hxtool-hxtool.Tpo $(DEPDIR)/hxtool-hxtool.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hxtool.c' object='hxtool-hxtool.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hxtool-hxtool.o `test -f 'hxtool.c' || echo '$(srcdir)/'`hxtool.c
-
-hxtool-hxtool.obj: hxtool.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hxtool-hxtool.obj -MD -MP -MF $(DEPDIR)/hxtool-hxtool.Tpo -c -o hxtool-hxtool.obj `if test -f 'hxtool.c'; then $(CYGPATH_W) 'hxtool.c'; else $(CYGPATH_W) '$(srcdir)/hxtool.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/hxtool-hxtool.Tpo $(DEPDIR)/hxtool-hxtool.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hxtool.c' object='hxtool-hxtool.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hxtool-hxtool.obj `if test -f 'hxtool.c'; then $(CYGPATH_W) 'hxtool.c'; else $(CYGPATH_W) '$(srcdir)/hxtool.c'; fi`
-
-hxtool-hxtool-commands.o: hxtool-commands.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hxtool-hxtool-commands.o -MD -MP -MF $(DEPDIR)/hxtool-hxtool-commands.Tpo -c -o hxtool-hxtool-commands.o `test -f 'hxtool-commands.c' || echo '$(srcdir)/'`hxtool-commands.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/hxtool-hxtool-commands.Tpo $(DEPDIR)/hxtool-hxtool-commands.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hxtool-commands.c' object='hxtool-hxtool-commands.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hxtool-hxtool-commands.o `test -f 'hxtool-commands.c' || echo '$(srcdir)/'`hxtool-commands.c
-
-hxtool-hxtool-commands.obj: hxtool-commands.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hxtool-hxtool-commands.obj -MD -MP -MF $(DEPDIR)/hxtool-hxtool-commands.Tpo -c -o hxtool-hxtool-commands.obj `if test -f 'hxtool-commands.c'; then $(CYGPATH_W) 'hxtool-commands.c'; else $(CYGPATH_W) '$(srcdir)/hxtool-commands.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/hxtool-hxtool-commands.Tpo $(DEPDIR)/hxtool-hxtool-commands.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hxtool-commands.c' object='hxtool-hxtool-commands.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hxtool_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hxtool-hxtool-commands.obj `if test -f 'hxtool-commands.c'; then $(CYGPATH_W) 'hxtool-commands.c'; else $(CYGPATH_W) '$(srcdir)/hxtool-commands.c'; fi`
-
-test_name-test_name.o: test_name.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_name_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_name-test_name.o -MD -MP -MF $(DEPDIR)/test_name-test_name.Tpo -c -o test_name-test_name.o `test -f 'test_name.c' || echo '$(srcdir)/'`test_name.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_name-test_name.Tpo $(DEPDIR)/test_name-test_name.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test_name.c' object='test_name-test_name.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_name_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_name-test_name.o `test -f 'test_name.c' || echo '$(srcdir)/'`test_name.c
-
-test_name-test_name.obj: test_name.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_name_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_name-test_name.obj -MD -MP -MF $(DEPDIR)/test_name-test_name.Tpo -c -o test_name-test_name.obj `if test -f 'test_name.c'; then $(CYGPATH_W) 'test_name.c'; else $(CYGPATH_W) '$(srcdir)/test_name.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_name-test_name.Tpo $(DEPDIR)/test_name-test_name.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test_name.c' object='test_name-test_name.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_name_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_name-test_name.obj `if test -f 'test_name.c'; then $(CYGPATH_W) 'test_name.c'; else $(CYGPATH_W) '$(srcdir)/test_name.c'; fi`
-
-test_soft_pkcs11-test_soft_pkcs11.o: test_soft_pkcs11.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_soft_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_soft_pkcs11-test_soft_pkcs11.o -MD -MP -MF $(DEPDIR)/test_soft_pkcs11-test_soft_pkcs11.Tpo -c -o test_soft_pkcs11-test_soft_pkcs11.o `test -f 'test_soft_pkcs11.c' || echo '$(srcdir)/'`test_soft_pkcs11.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_soft_pkcs11-test_soft_pkcs11.Tpo $(DEPDIR)/test_soft_pkcs11-test_soft_pkcs11.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test_soft_pkcs11.c' object='test_soft_pkcs11-test_soft_pkcs11.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_soft_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_soft_pkcs11-test_soft_pkcs11.o `test -f 'test_soft_pkcs11.c' || echo '$(srcdir)/'`test_soft_pkcs11.c
-
-test_soft_pkcs11-test_soft_pkcs11.obj: test_soft_pkcs11.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_soft_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT test_soft_pkcs11-test_soft_pkcs11.obj -MD -MP -MF $(DEPDIR)/test_soft_pkcs11-test_soft_pkcs11.Tpo -c -o test_soft_pkcs11-test_soft_pkcs11.obj `if test -f 'test_soft_pkcs11.c'; then $(CYGPATH_W) 'test_soft_pkcs11.c'; else $(CYGPATH_W) '$(srcdir)/test_soft_pkcs11.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/test_soft_pkcs11-test_soft_pkcs11.Tpo $(DEPDIR)/test_soft_pkcs11-test_soft_pkcs11.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='test_soft_pkcs11.c' object='test_soft_pkcs11-test_soft_pkcs11.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(test_soft_pkcs11_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o test_soft_pkcs11-test_soft_pkcs11.obj `if test -f 'test_soft_pkcs11.c'; then $(CYGPATH_W) 'test_soft_pkcs11.c'; else $(CYGPATH_W) '$(srcdir)/test_soft_pkcs11.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
.l.c:
- $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
.y.c:
- $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
mostlyclean-libtool:
-rm -f *.lo
@@ -1390,8 +1305,11 @@ clean-libtool:
-rm -rf .libs _libs
install-dist_includeHEADERS: $(dist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1405,13 +1323,14 @@ uninstall-dist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_includeHEADERS: $(nodist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1425,30 +1344,17 @@ uninstall-nodist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -1460,15 +1366,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -1477,101 +1379,292 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS) $(check_SCRIPTS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test_ca.log: test_ca
+ @p='test_ca'; \
+ b='test_ca'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_cert.log: test_cert
+ @p='test_cert'; \
+ b='test_cert'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_chain.log: test_chain
+ @p='test_chain'; \
+ b='test_chain'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_cms.log: test_cms
+ @p='test_cms'; \
+ b='test_cms'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_crypto.log: test_crypto
+ @p='test_crypto'; \
+ b='test_crypto'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_nist.log: test_nist
+ @p='test_nist'; \
+ b='test_nist'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_nist2.log: test_nist2
+ @p='test_nist2'; \
+ b='test_nist2'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_pkcs11.log: test_pkcs11
+ @p='test_pkcs11'; \
+ b='test_pkcs11'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_java_pkcs11.log: test_java_pkcs11
+ @p='test_java_pkcs11'; \
+ b='test_java_pkcs11'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_nist_cert.log: test_nist_cert
+ @p='test_nist_cert'; \
+ b='test_nist_cert'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_nist_pkcs12.log: test_nist_pkcs12
+ @p='test_nist_pkcs12'; \
+ b='test_nist_pkcs12'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_req.log: test_req
+ @p='test_req'; \
+ b='test_req'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_windows.log: test_windows
+ @p='test_windows'; \
+ b='test_windows'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_query.log: test_query
+ @p='test_query'; \
+ b='test_query'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_name.log: test_name$(EXEEXT)
+ @p='test_name$(EXEEXT)'; \
+ b='test_name'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_expr.log: test_expr$(EXEEXT)
+ @p='test_expr$(EXEEXT)'; \
+ b='test_expr'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -1629,11 +1722,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -1680,9 +1781,9 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-binPROGRAMS install-exec-local \
+ install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -1726,17 +1827,18 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_includeHEADERS \
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
.MAKE: all check check-am install install-am install-data-am \
- install-exec-am install-strip uninstall-am
+ install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-binPROGRAMS clean-checkPROGRAMS \
- clean-generic clean-libLTLIBRARIES clean-libtool clean-local \
- ctags dist-hook distclean distclean-compile distclean-generic \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-binPROGRAMS \
+ clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-local cscopelist-am ctags ctags-am \
+ dist-hook distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-binPROGRAMS \
install-data install-data-am install-data-hook \
install-dist_includeHEADERS install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
+ install-exec install-exec-am install-exec-local install-html \
install-html-am install-info install-info-am \
install-libLTLIBRARIES install-man \
install-nodist_includeHEADERS install-pdf install-pdf-am \
@@ -1744,25 +1846,40 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_includeHEADERS \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-binPROGRAMS \
- uninstall-dist_includeHEADERS uninstall-hook \
- uninstall-libLTLIBRARIES uninstall-nodist_includeHEADERS
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-dist_includeHEADERS \
+ uninstall-hook uninstall-libLTLIBRARIES \
+ uninstall-nodist_includeHEADERS
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
-install-exec-hook: install-suid-programs
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -1770,7 +1887,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -1827,6 +1944,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1869,6 +1988,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1882,13 +2014,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -1927,99 +2059,101 @@ $(gen_files_pkcs10) pkcs10_asn1.hx pkcs10_asn1-priv.hx: pkcs10_asn1_files
$(gen_files_crmf) crmf_asn1.hx crmf_asn1-priv.hx: crmf_asn1_files
ocsp_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/ocsp.asn1 $(srcdir)/ocsp.opt
- $(ASN1_COMPILE) --option-file=$(srcdir)/ocsp.opt $(srcdir)/ocsp.asn1 ocsp_asn1 || (rm -f ocsp_asn1_files ; exit 1)
+ $(heim_verbose)$(ASN1_COMPILE) --option-file=$(srcdir)/ocsp.opt $(srcdir)/ocsp.asn1 ocsp_asn1 || (rm -f ocsp_asn1_files ; exit 1)
pkcs10_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/pkcs10.asn1 $(srcdir)/pkcs10.opt
- $(ASN1_COMPILE) --option-file=$(srcdir)/pkcs10.opt $(srcdir)/pkcs10.asn1 pkcs10_asn1 || (rm -f pkcs10_asn1_files ; exit 1)
+ $(heim_verbose)$(ASN1_COMPILE) --option-file=$(srcdir)/pkcs10.opt $(srcdir)/pkcs10.asn1 pkcs10_asn1 || (rm -f pkcs10_asn1_files ; exit 1)
crmf_asn1_files: $(ASN1_COMPILE_DEP) $(srcdir)/crmf.asn1
- $(ASN1_COMPILE) $(srcdir)/crmf.asn1 crmf_asn1 || (rm -f crmf_asn1_files ; exit 1)
+ $(heim_verbose)$(ASN1_COMPILE) $(srcdir)/crmf.asn1 crmf_asn1 || (rm -f crmf_asn1_files ; exit 1)
+
+$(ALL_OBJECTS): $(HX509_PROTOS)
-$(libhx509_la_OBJECTS): $(srcdir)/hx509-protos.h $(srcdir)/hx509-private.h $(srcdir)/hx_locl.h
+$(libhx509_la_OBJECTS): $(srcdir)/hx_locl.h
$(libhx509_la_OBJECTS): ocsp_asn1.h pkcs10_asn1.h
-$(srcdir)/hx509-protos.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -R '^(_|^C)' -E HX509_LIB -q -P comment -o hx509-protos.h $(dist_libhx509_la_SOURCES) || rm -f hx509-protos.h
+$(srcdir)/hx509-protos.h: $(dist_libhx509_la_SOURCES)
+ $(heim_verbose)cd $(srcdir) && perl ../../cf/make-proto.pl -R '^(_|^C)' -E HX509_LIB -q -P comment -o hx509-protos.h $(dist_libhx509_la_SOURCES) || rm -f hx509-protos.h
-$(srcdir)/hx509-private.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p hx509-private.h $(dist_libhx509_la_SOURCES) || rm -f hx509-private.h
+$(srcdir)/hx509-private.h: $(dist_libhx509_la_SOURCES)
+ $(heim_verbose)cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p hx509-private.h $(dist_libhx509_la_SOURCES) || rm -f hx509-private.h
hxtool-commands.c hxtool-commands.h: hxtool-commands.in $(SLC)
- $(SLC) $(srcdir)/hxtool-commands.in
+ $(heim_verbose)$(SLC) $(srcdir)/hxtool-commands.in
-$(hxtool_OBJECTS): hxtool-commands.h
+$(hxtool_OBJECTS): hxtool-commands.h hx509_err.h
clean-local:
@echo "cleaning PKITS" ; rm -rf PKITS_data
test_ca: test_ca.in Makefile
$(do_subst) < $(srcdir)/test_ca.in > test_ca.tmp
- chmod +x test_ca.tmp
+ $(heim_verbose)chmod +x test_ca.tmp
mv test_ca.tmp test_ca
test_cert: test_cert.in Makefile
$(do_subst) < $(srcdir)/test_cert.in > test_cert.tmp
- chmod +x test_cert.tmp
+ $(heim_verbose)chmod +x test_cert.tmp
mv test_cert.tmp test_cert
test_chain: test_chain.in Makefile
$(do_subst) < $(srcdir)/test_chain.in > test_chain.tmp
- chmod +x test_chain.tmp
+ $(heim_verbose)chmod +x test_chain.tmp
mv test_chain.tmp test_chain
test_cms: test_cms.in Makefile
$(do_subst) < $(srcdir)/test_cms.in > test_cms.tmp
- chmod +x test_cms.tmp
+ $(heim_verbose)chmod +x test_cms.tmp
mv test_cms.tmp test_cms
test_crypto: test_crypto.in Makefile
$(do_subst) < $(srcdir)/test_crypto.in > test_crypto.tmp
- chmod +x test_crypto.tmp
+ $(heim_verbose)chmod +x test_crypto.tmp
mv test_crypto.tmp test_crypto
test_nist: test_nist.in Makefile
$(do_subst) < $(srcdir)/test_nist.in > test_nist.tmp
- chmod +x test_nist.tmp
+ $(heim_verbose)chmod +x test_nist.tmp
mv test_nist.tmp test_nist
test_nist2: test_nist2.in Makefile
$(do_subst) < $(srcdir)/test_nist2.in > test_nist2.tmp
- chmod +x test_nist2.tmp
+ $(heim_verbose)chmod +x test_nist2.tmp
mv test_nist2.tmp test_nist2
test_pkcs11: test_pkcs11.in Makefile
$(do_subst) < $(srcdir)/test_pkcs11.in > test_pkcs11.tmp
- chmod +x test_pkcs11.tmp
+ $(heim_verbose)chmod +x test_pkcs11.tmp
mv test_pkcs11.tmp test_pkcs11
test_java_pkcs11: test_java_pkcs11.in Makefile
$(do_subst) < $(srcdir)/test_java_pkcs11.in > test_java_pkcs11.tmp
- chmod +x test_java_pkcs11.tmp
+ $(heim_verbose)chmod +x test_java_pkcs11.tmp
mv test_java_pkcs11.tmp test_java_pkcs11
test_nist_cert: test_nist_cert.in Makefile
$(do_subst) < $(srcdir)/test_nist_cert.in > test_nist_cert.tmp
- chmod +x test_nist_cert.tmp
+ $(heim_verbose)chmod +x test_nist_cert.tmp
mv test_nist_cert.tmp test_nist_cert
test_nist_pkcs12: test_nist_pkcs12.in Makefile
$(do_subst) < $(srcdir)/test_nist_pkcs12.in > test_nist_pkcs12.tmp
- chmod +x test_nist_pkcs12.tmp
+ $(heim_verbose)chmod +x test_nist_pkcs12.tmp
mv test_nist_pkcs12.tmp test_nist_pkcs12
test_req: test_req.in Makefile
$(do_subst) < $(srcdir)/test_req.in > test_req.tmp
- chmod +x test_req.tmp
+ $(heim_verbose)chmod +x test_req.tmp
mv test_req.tmp test_req
test_windows: test_windows.in Makefile
$(do_subst) < $(srcdir)/test_windows.in > test_windows.tmp
- chmod +x test_windows.tmp
+ $(heim_verbose)chmod +x test_windows.tmp
mv test_windows.tmp test_windows
test_query: test_query.in Makefile
$(do_subst) < $(srcdir)/test_query.in > test_query.tmp
- chmod +x test_query.tmp
+ $(heim_verbose)chmod +x test_query.tmp
mv test_query.tmp test_query
# Tell versions [3.59,3.63) of GNU make to not export all variables.
diff --git a/lib/hx509/NTMakefile b/lib/hx509/NTMakefile
index 99116d76ed26..ee1bb69d09d6 100644
--- a/lib/hx509/NTMakefile
+++ b/lib/hx509/NTMakefile
@@ -1,6 +1,6 @@
########################################################################
#
-# Copyright (c) 2009, Secure Endpoints Inc.
+# Copyright (c) 2009-2017, Secure Endpoints Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -30,7 +30,7 @@
#
RELDIR=lib\hx509
-intcflags=-I$(SRCDIR)\ref -I$(OBJ)
+intcflags=-I$(OBJ)
localcflags=-DASN1_LIB
!include ../../windows/NTMakefile.w32
@@ -47,6 +47,7 @@ libhx509_la_OBJS = \
$(OBJ)\cms.obj \
$(OBJ)\collector.obj \
$(OBJ)\crypto.obj \
+ $(OBJ)\crypto-ec.obj \
$(OBJ)\error.obj \
$(OBJ)\env.obj \
$(OBJ)\file.obj \
@@ -81,6 +82,7 @@ dist_libhx509_la_SOURCES = \
$(SRCDIR)\cms.c \
$(SRCDIR)\collector.c \
$(SRCDIR)\crypto.c \
+ $(SRCDIR)\crypto-ec.c \
$(SRCDIR)\doxygen.c \
$(SRCDIR)\error.c \
$(SRCDIR)\env.c \
@@ -162,7 +164,7 @@ $(OBJ)\hxtool-commands.c $(OBJ)\hxtool-commands.h: hxtool-commands.in $(SLC)
cd $(SRCDIR)
$(BINDIR)\hxtool.exe: $(OBJ)\tool\hxtool.obj $(OBJ)\tool\hxtool-commands.obj $(LIBHEIMDAL) $(OBJ)\hxtool-version.res
- $(EXECONLINK) $(LIBHEIMDAL) $(LIBROKEN) $(LIBSL) $(LIBVERS) $(LIBCOMERR)
+ $(EXECONLINK) $(LIBHEIMDAL) $(LIBROKEN) $(LIBSL) $(LIBVERS) $(LIBCOMERR) $(LIB_openssl_crypto)
$(EXEPREP)
$(OBJ)\hx509-protos.h:
@@ -190,7 +192,7 @@ $(OBJ)\sel-gram.c: sel-gram.y
$(YACC) -o $@ --defines=$(OBJ)\sel-gram.h sel-gram.y
$(OBJ)\sel-lex.c: sel-lex.l
- $(LEX) -o$@ sel-lex.l
+ $(LEX) -P_hx509_sel_yy -o$@ sel-lex.l
all:: $(INCFILES) $(LIBHX509)
diff --git a/lib/hx509/ca.c b/lib/hx509/ca.c
index cb5a7be62cc3..418a404b4aa9 100644
--- a/lib/hx509/ca.c
+++ b/lib/hx509/ca.c
@@ -61,7 +61,7 @@ struct hx509_ca_tbs {
CRLDistributionPoints crldp;
heim_bit_string subjectUniqueID;
heim_bit_string issuerUniqueID;
-
+ AlgorithmIdentifier *sigalg;
};
/**
@@ -109,6 +109,10 @@ hx509_ca_tbs_free(hx509_ca_tbs *tbs)
der_free_bit_string(&(*tbs)->subjectUniqueID);
der_free_bit_string(&(*tbs)->issuerUniqueID);
hx509_name_free(&(*tbs)->subject);
+ if ((*tbs)->sigalg) {
+ free_AlgorithmIdentifier((*tbs)->sigalg);
+ free((*tbs)->sigalg);
+ }
memset(*tbs, 0, sizeof(**tbs));
free(*tbs);
@@ -888,7 +892,7 @@ hx509_ca_tbs_set_unique(hx509_context context,
*
* @param context A hx509 context.
* @param tbs object to be signed.
- * @param env enviroment variable to expand variables in the subject
+ * @param env environment variable to expand variables in the subject
* name, see hx509_env_init().
*
* @return An hx509 error code, see hx509_get_error_string().
@@ -904,6 +908,39 @@ hx509_ca_tbs_subject_expand(hx509_context context,
return hx509_name_expand(context, tbs->subject, env);
}
+/**
+ * Set signature algorithm on the to be signed certificate
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param sigalg signature algorithm to use
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_signature_algorithm(hx509_context context,
+ hx509_ca_tbs tbs,
+ const AlgorithmIdentifier *sigalg)
+{
+ int ret;
+
+ tbs->sigalg = calloc(1, sizeof(*tbs->sigalg));
+ if (tbs->sigalg == NULL) {
+ hx509_set_error_string(context, 0, ENOMEM, "Out of memory");
+ return ENOMEM;
+ }
+ ret = copy_AlgorithmIdentifier(sigalg, tbs->sigalg);
+ if (ret) {
+ free(tbs->sigalg);
+ tbs->sigalg = NULL;
+ return ret;
+ }
+ return 0;
+}
+
/*
*
*/
@@ -965,8 +1002,8 @@ build_proxy_prefix(hx509_context context, const Name *issuer, Name *subject)
}
t = time(NULL);
- asprintf(&tstr, "ts-%lu", (unsigned long)t);
- if (tstr == NULL) {
+ ret = asprintf(&tstr, "ts-%lu", (unsigned long)t);
+ if (ret == -1 || tstr == NULL) {
hx509_set_error_string(context, 0, ENOMEM,
"Failed to copy subject name");
return ENOMEM;
@@ -987,6 +1024,7 @@ ca_sign(hx509_context context,
const Name *issuername,
hx509_cert *certificate)
{
+ heim_error_t error = NULL;
heim_octet_string data;
Certificate c;
TBSCertificate *tbsc;
@@ -997,7 +1035,9 @@ ca_sign(hx509_context context,
time_t notAfter;
unsigned key_usage;
- sigalg = _hx509_crypto_default_sig_alg;
+ sigalg = tbs->sigalg;
+ if (sigalg == NULL)
+ sigalg = _hx509_crypto_default_sig_alg;
memset(&c, 0, sizeof(c));
@@ -1086,6 +1126,12 @@ ca_sign(hx509_context context,
goto out;
}
} else {
+ /*
+ * If no explicit serial number is specified, 20 random bytes should be
+ * sufficiently collision resistant. Since the serial number must be a
+ * positive integer, ensure minimal ASN.1 DER form by forcing the high
+ * bit off and the next bit on (thus avoiding an all zero first octet).
+ */
tbsc->serialNumber.length = 20;
tbsc->serialNumber.data = malloc(tbsc->serialNumber.length);
if (tbsc->serialNumber.data == NULL){
@@ -1093,9 +1139,9 @@ ca_sign(hx509_context context,
hx509_set_error_string(context, 0, ret, "Out of memory");
goto out;
}
- /* XXX diffrent */
RAND_bytes(tbsc->serialNumber.data, tbsc->serialNumber.length);
((unsigned char *)tbsc->serialNumber.data)[0] &= 0x7f;
+ ((unsigned char *)tbsc->serialNumber.data)[0] |= 0x40;
}
/* signature AlgorithmIdentifier, */
ret = copy_AlgorithmIdentifier(sigalg, &tbsc->signature);
@@ -1408,9 +1454,12 @@ ca_sign(hx509_context context,
if (ret)
goto out;
- ret = hx509_cert_init(context, &c, certificate);
- if (ret)
+ *certificate = hx509_cert_init(context, &c, &error);
+ if (*certificate == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
goto out;
+ }
free_Certificate(&c);
diff --git a/lib/hx509/cert.c b/lib/hx509/cert.c
index 70e575603779..dd6d38917499 100644
--- a/lib/hx509/cert.c
+++ b/lib/hx509/cert.c
@@ -93,6 +93,14 @@ typedef struct hx509_name_constraints {
#define GeneralSubtrees_SET(g,var) \
(g)->len = (var)->len, (g)->val = (var)->val;
+static void
+init_context_once(void *ignored)
+{
+
+ ENGINE_add_conf_module();
+ OpenSSL_add_all_algorithms();
+}
+
/**
* Creates a hx509 context that most functions in the library
* uses. The context is only allowed to be used by one thread at each
@@ -108,10 +116,14 @@ typedef struct hx509_name_constraints {
int
hx509_context_init(hx509_context *context)
{
+ static heim_base_once_t init_context = HEIM_BASE_ONCE_INIT;
+
*context = calloc(1, sizeof(**context));
if (*context == NULL)
return ENOMEM;
+ heim_base_once_f(&init_context, NULL, init_context_once);
+
_hx509_ks_null_register(*context);
_hx509_ks_mem_register(*context);
_hx509_ks_file_register(*context);
@@ -120,9 +132,6 @@ hx509_context_init(hx509_context *context)
_hx509_ks_dir_register(*context);
_hx509_ks_keychain_register(*context);
- ENGINE_add_conf_module();
- OpenSSL_add_all_algorithms();
-
(*context)->ocsp_time_diff = HX509_DEFAULT_OCSP_TIME_DIFF;
initialize_hx_error_table_r(&(*context)->et_list);
@@ -209,42 +218,48 @@ _hx509_cert_get_version(const Certificate *t)
*
* @param context A hx509 context.
* @param c
- * @param cert
+ * @param error
*
- * @return Returns an hx509 error code.
+ * @return Returns an hx509 certificate
*
* @ingroup hx509_cert
*/
-int
-hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert)
+hx509_cert
+hx509_cert_init(hx509_context context, const Certificate *c, heim_error_t *error)
{
+ hx509_cert cert;
int ret;
- *cert = malloc(sizeof(**cert));
- if (*cert == NULL)
- return ENOMEM;
- (*cert)->ref = 1;
- (*cert)->friendlyname = NULL;
- (*cert)->attrs.len = 0;
- (*cert)->attrs.val = NULL;
- (*cert)->private_key = NULL;
- (*cert)->basename = NULL;
- (*cert)->release = NULL;
- (*cert)->ctx = NULL;
-
- (*cert)->data = calloc(1, sizeof(*(*cert)->data));
- if ((*cert)->data == NULL) {
- free(*cert);
- return ENOMEM;
+ cert = malloc(sizeof(*cert));
+ if (cert == NULL) {
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
+ }
+ cert->ref = 1;
+ cert->friendlyname = NULL;
+ cert->attrs.len = 0;
+ cert->attrs.val = NULL;
+ cert->private_key = NULL;
+ cert->basename = NULL;
+ cert->release = NULL;
+ cert->ctx = NULL;
+
+ cert->data = calloc(1, sizeof(*(cert->data)));
+ if (cert->data == NULL) {
+ free(cert);
+ if (error)
+ *error = heim_error_create_enomem();
+ return NULL;
}
- ret = copy_Certificate(c, (*cert)->data);
+ ret = copy_Certificate(c, cert->data);
if (ret) {
- free((*cert)->data);
- free(*cert);
- *cert = NULL;
+ free(cert->data);
+ free(cert);
+ cert = NULL;
}
- return ret;
+ return cert;
}
/**
@@ -259,39 +274,41 @@ hx509_cert_init(hx509_context context, const Certificate *c, hx509_cert *cert)
* @param context A hx509 context.
* @param ptr pointer to memory region containing encoded certificate.
* @param len length of memory region.
- * @param cert a return pointer to a hx509 certificate object, will
- * contain NULL on error.
+ * @param error possibly returns an error
*
- * @return An hx509 error code, see hx509_get_error_string().
+ * @return An hx509 certificate
*
* @ingroup hx509_cert
*/
-int
+hx509_cert
hx509_cert_init_data(hx509_context context,
const void *ptr,
size_t len,
- hx509_cert *cert)
+ heim_error_t *error)
{
+ hx509_cert cert;
Certificate t;
size_t size;
int ret;
ret = decode_Certificate(ptr, len, &t, &size);
if (ret) {
- hx509_set_error_string(context, 0, ret, "Failed to decode certificate");
- return ret;
+ if (error)
+ *error = heim_error_create(ret, "Failed to decode certificate");
+ return NULL;
}
if (size != len) {
free_Certificate(&t);
- hx509_set_error_string(context, 0, HX509_EXTRA_DATA_AFTER_STRUCTURE,
- "Extra data after certificate");
- return HX509_EXTRA_DATA_AFTER_STRUCTURE;
+ if (error)
+ *error = heim_error_create(HX509_EXTRA_DATA_AFTER_STRUCTURE,
+ "Extra data after certificate");
+ return NULL;
}
- ret = hx509_cert_init(context, &t, cert);
+ cert = hx509_cert_init(context, &t, error);
free_Certificate(&t);
- return ret;
+ return cert;
}
void
@@ -827,7 +844,8 @@ check_key_usage(hx509_context context, const Certificate *cert,
_hx509_unparse_Name(&cert->tbsCertificate.subject, &name);
hx509_set_error_string(context, 0, HX509_KU_CERT_MISSING,
"Key usage %s required but missing "
- "from certifiate %s", buf, name);
+ "from certifiate %s", buf,
+ name ? name : "<unknown>");
free(name);
return HX509_KU_CERT_MISSING;
}
@@ -970,7 +988,7 @@ _hx509_cert_is_parent_cmp(const Certificate *subject,
if (ai.authorityCertIssuer->val[0].element != choice_GeneralName_directoryName)
return -1;
- name.element =
+ name.element = (enum Name_enum)
ai.authorityCertIssuer->val[0].u.directoryName.element;
name.u.rdnSequence =
ai.authorityCertIssuer->val[0].u.directoryName.u.rdnSequence;
@@ -1803,12 +1821,12 @@ match_general_name(const GeneralName *c, const GeneralName *n, int *match)
c_name._save.data = NULL;
c_name._save.length = 0;
- c_name.element = c->u.directoryName.element;
+ c_name.element = (enum Name_enum)c->u.directoryName.element;
c_name.u.rdnSequence = c->u.directoryName.u.rdnSequence;
n_name._save.data = NULL;
n_name._save.length = 0;
- n_name.element = n->u.directoryName.element;
+ n_name.element = (enum Name_enum)n->u.directoryName.element;
n_name.u.rdnSequence = n->u.directoryName.u.rdnSequence;
ret = match_X501Name(&c_name, &n_name);
@@ -1829,7 +1847,7 @@ match_alt_name(const GeneralName *n, const Certificate *c,
int *same, int *match)
{
GeneralNames sa;
- int ret;
+ int ret = 0;
size_t i, j;
i = 0;
@@ -1844,7 +1862,7 @@ match_alt_name(const GeneralName *n, const Certificate *c,
for (j = 0; j < sa.len; j++) {
if (n->element == sa.val[j].element) {
*same = 1;
- ret = match_general_name(n, &sa.val[j], match);
+ match_general_name(n, &sa.val[j], match);
}
}
free_GeneralNames(&sa);
@@ -1878,12 +1896,12 @@ match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)
memset(&certname, 0, sizeof(certname));
certname.element = choice_GeneralName_directoryName;
- certname.u.directoryName.element =
+ certname.u.directoryName.element = (enum GeneralName_directoryName_enum)
c->tbsCertificate.subject.element;
certname.u.directoryName.u.rdnSequence =
c->tbsCertificate.subject.u.rdnSequence;
- ret = match_general_name(&t->val[i].base, &certname, &name);
+ match_general_name(&t->val[i].base, &certname, &name);
}
/* Handle subjectAltNames, this is icky since they
@@ -1891,7 +1909,7 @@ match_tree(const GeneralSubtrees *t, const Certificate *c, int *match)
* same type. So if there have been a match of type, require
* altname to be set.
*/
- ret = match_alt_name(&t->val[i].base, c, &same, &alt_name);
+ match_alt_name(&t->val[i].base, c, &same, &alt_name);
}
if (name && (!same || alt_name))
*match = 1;
@@ -1985,6 +2003,16 @@ hx509_verify_path(hx509_context context,
memset(&proxy_issuer, 0, sizeof(proxy_issuer));
+ if ((ctx->flags & HX509_VERIFY_CTX_F_ALLOW_PROXY_CERTIFICATE) == 0 &&
+ is_proxy_cert(context, cert->data, NULL) == 0)
+ {
+ ret = HX509_PROXY_CERT_INVALID;
+ hx509_set_error_string(context, 0, ret,
+ "Proxy certificate is not allowed as an EE "
+ "certificae if proxy certificate is disabled");
+ return ret;
+ }
+
ret = init_name_constraints(&nc);
if (ret)
return ret;
@@ -2353,20 +2381,12 @@ hx509_verify_path(hx509_context context,
goto out;
}
/*
- * Verify that the sigature algorithm "best-before" date is
- * before the creation date of the certificate, do this for
- * trust anchors too, since any trust anchor that is created
- * after a algorithm is known to be bad deserved to be invalid.
- *
- * Skip the leaf certificate for now...
+ * Verify that the sigature algorithm is not weak. Ignore
+ * trust anchors since they are provisioned by the user.
*/
- if (i != 0 && (ctx->flags & HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK) == 0) {
- time_t notBefore =
- _hx509_Time2time_t(&c->tbsCertificate.validity.notBefore);
- ret = _hx509_signature_best_before(context,
- &c->signatureAlgorithm,
- notBefore);
+ if (i + 1 != path.len && (ctx->flags & HX509_VERIFY_CTX_F_NO_BEST_BEFORE_CHECK) == 0) {
+ ret = _hx509_signature_is_weak(context, &c->signatureAlgorithm);
if (ret)
goto out;
}
@@ -3384,7 +3404,7 @@ hx509_cert_binary(hx509_context context, hx509_cert c, heim_octet_string *os)
void
_hx509_abort(const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 1, 2)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 1, 2)))
{
va_list ap;
va_start(ap, fmt);
@@ -3425,7 +3445,9 @@ _hx509_cert_to_env(hx509_context context, hx509_cert cert, hx509_env *env)
*env = NULL;
/* version */
- asprintf(&buf, "%d", _hx509_cert_get_version(_hx509_get_cert(cert)));
+ ret = asprintf(&buf, "%d", _hx509_cert_get_version(_hx509_get_cert(cert)));
+ if (ret == -1)
+ goto out;
ret = hx509_env_add(context, &envcert, "version", buf);
free(buf);
if (ret)
diff --git a/lib/hx509/cms.c b/lib/hx509/cms.c
index 4e0a2e03fcb6..7aa159cbb2d6 100644
--- a/lib/hx509/cms.c
+++ b/lib/hx509/cms.c
@@ -209,7 +209,7 @@ unparse_CMSIdentifier(hx509_context context,
CMSIdentifier *id,
char **str)
{
- int ret;
+ int ret = -1;
*str = NULL;
switch (id->element) {
@@ -227,8 +227,8 @@ unparse_CMSIdentifier(hx509_context context,
free(name);
return ret;
}
- asprintf(str, "certificate issued by %s with serial number %s",
- name, serial);
+ ret = asprintf(str, "certificate issued by %s with serial number %s",
+ name, serial);
free(name);
free(serial);
break;
@@ -242,15 +242,19 @@ unparse_CMSIdentifier(hx509_context context,
if (len < 0)
return ENOMEM;
- asprintf(str, "certificate with id %s", keyid);
+ ret = asprintf(str, "certificate with id %s", keyid);
free(keyid);
break;
}
default:
- asprintf(str, "certificate have unknown CMSidentifier type");
+ ret = asprintf(str, "certificate have unknown CMSidentifier type");
break;
}
- if (*str == NULL)
+ /*
+ * In the following if, we check ret and *str which should be returned/set
+ * by asprintf(3) in every branch of the switch statement.
+ */
+ if (ret == -1 || *str == NULL)
return ENOMEM;
return 0;
}
@@ -340,6 +344,8 @@ find_CMSIdentifier(hx509_context context,
* @param contentType output type oid, should be freed with der_free_oid().
* @param content the data, free with der_free_octet_string().
*
+ * @return an hx509 error code.
+ *
* @ingroup hx509_cms
*/
@@ -544,6 +550,8 @@ out:
* @param content the output of the function,
* free with der_free_octet_string().
*
+ * @return an hx509 error code.
+ *
* @ingroup hx509_cms
*/
@@ -726,14 +734,18 @@ any_to_certs(hx509_context context, const SignedData *sd, hx509_certs certs)
return 0;
for (i = 0; i < sd->certificates->len; i++) {
+ heim_error_t error;
hx509_cert c;
- ret = hx509_cert_init_data(context,
- sd->certificates->val[i].data,
- sd->certificates->val[i].length,
- &c);
- if (ret)
+ c = hx509_cert_init_data(context,
+ sd->certificates->val[i].data,
+ sd->certificates->val[i].length,
+ &error);
+ if (c == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
return ret;
+ }
ret = hx509_certs_add(context, certs, c);
hx509_cert_free(c);
if (ret)
@@ -772,6 +784,8 @@ find_attribute(const CMSAttributes *attr, const heim_oid *oid)
* @param signer_certs list of the cerficates used to sign this
* request, free with hx509_certs_free().
*
+ * @return an hx509 error code.
+ *
* @ingroup hx509_cms
*/
@@ -855,7 +869,7 @@ hx509_cms_verify_signed(hx509_context context,
}
for (found_valid_sig = 0, i = 0; i < sd.signerInfos.len; i++) {
- heim_octet_string signed_data;
+ heim_octet_string signed_data = { 0, 0 };
const heim_oid *match_oid;
heim_oid decode_oid;
@@ -1016,8 +1030,10 @@ hx509_cms_verify_signed(hx509_context context,
"Failed to verify signature in "
"CMS SignedData");
}
- if (signer_info->signedAttrs)
- free(signed_data.data);
+ if (signed_data.data != NULL && content->data != signed_data.data) {
+ free(signed_data.data);
+ signed_data.data = NULL;
+ }
if (ret)
goto next_sigature;
@@ -1137,6 +1153,8 @@ add_one_attribute(Attribute **attr,
* @param signed_data the output of the function, free with
* der_free_octet_string().
*
+ * @return Returns an hx509 error code.
+ *
* @ingroup hx509_cms
*/
diff --git a/lib/hx509/crypto-ec.c b/lib/hx509/crypto-ec.c
new file mode 100644
index 000000000000..4777171cae52
--- /dev/null
+++ b/lib/hx509/crypto-ec.c
@@ -0,0 +1,533 @@
+/*
+ * Copyright (c) 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+#include <openssl/ec.h>
+#include <openssl/ecdsa.h>
+#include <openssl/rsa.h>
+#include <openssl/bn.h>
+#include <openssl/objects.h>
+#define HEIM_NO_CRYPTO_HDRS
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+
+#include "hx_locl.h"
+
+extern const AlgorithmIdentifier _hx509_signature_sha512_data;
+extern const AlgorithmIdentifier _hx509_signature_sha384_data;
+extern const AlgorithmIdentifier _hx509_signature_sha256_data;
+extern const AlgorithmIdentifier _hx509_signature_sha1_data;
+
+void
+_hx509_private_eckey_free(void *eckey)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ EC_KEY_free(eckey);
+#endif
+}
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+static int
+heim_oid2ecnid(heim_oid *oid)
+{
+ /*
+ * Now map to openssl OID fun
+ */
+
+ if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP256R1) == 0)
+ return NID_X9_62_prime256v1;
+#ifdef NID_secp521r1
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP521R1) == 0)
+ return NID_secp521r1;
+#endif
+#ifdef NID_secp384r1
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP384R1) == 0)
+ return NID_secp384r1;
+#endif
+#ifdef NID_secp160r1
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R1) == 0)
+ return NID_secp160r1;
+#endif
+#ifdef NID_secp160r2
+ else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R2) == 0)
+ return NID_secp160r2;
+#endif
+
+ return NID_undef;
+}
+
+static int
+parse_ECParameters(hx509_context context,
+ heim_octet_string *parameters, int *nid)
+{
+ ECParameters ecparam;
+ size_t size;
+ int ret;
+
+ if (parameters == NULL) {
+ ret = HX509_PARSING_KEY_FAILED;
+ hx509_set_error_string(context, 0, ret,
+ "EC parameters missing");
+ return ret;
+ }
+
+ ret = decode_ECParameters(parameters->data, parameters->length,
+ &ecparam, &size);
+ if (ret) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to decode EC parameters");
+ return ret;
+ }
+
+ if (ecparam.element != choice_ECParameters_namedCurve) {
+ free_ECParameters(&ecparam);
+ hx509_set_error_string(context, 0, ret,
+ "EC parameters is not a named curve");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ *nid = heim_oid2ecnid(&ecparam.u.namedCurve);
+ free_ECParameters(&ecparam);
+ if (*nid == NID_undef) {
+ hx509_set_error_string(context, 0, ret,
+ "Failed to find matcing NID for EC curve");
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+static int
+ecdsa_verify_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const Certificate *signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ const heim_octet_string *sig)
+{
+ const AlgorithmIdentifier *digest_alg;
+ const SubjectPublicKeyInfo *spi;
+ heim_octet_string digest;
+ int ret;
+ EC_KEY *key = NULL;
+ int groupnid;
+ EC_GROUP *group;
+ const unsigned char *p;
+ long len;
+
+ digest_alg = sig_alg->digest_alg;
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ digest_alg,
+ data,
+ NULL,
+ &digest);
+ if (ret)
+ return ret;
+
+ /* set up EC KEY */
+ spi = &signer->tbsCertificate.subjectPublicKeyInfo;
+
+ if (der_heim_oid_cmp(&spi->algorithm.algorithm, ASN1_OID_ID_ECPUBLICKEY) != 0)
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+
+ /*
+ * Find the group id
+ */
+
+ ret = parse_ECParameters(context, spi->algorithm.parameters, &groupnid);
+ if (ret) {
+ der_free_octet_string(&digest);
+ return ret;
+ }
+
+ /*
+ * Create group, key, parse key
+ */
+
+ key = EC_KEY_new();
+ group = EC_GROUP_new_by_curve_name(groupnid);
+ EC_KEY_set_group(key, group);
+ EC_GROUP_free(group);
+
+ p = spi->subjectPublicKey.data;
+ len = spi->subjectPublicKey.length / 8;
+
+ if (o2i_ECPublicKey(&key, &p, len) == NULL) {
+ EC_KEY_free(key);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ ret = ECDSA_verify(-1, digest.data, digest.length,
+ sig->data, sig->length, key);
+ der_free_octet_string(&digest);
+ EC_KEY_free(key);
+ if (ret != 1) {
+ ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
+ return ret;
+ }
+
+ return 0;
+}
+
+static int
+ecdsa_create_signature(hx509_context context,
+ const struct signature_alg *sig_alg,
+ const hx509_private_key signer,
+ const AlgorithmIdentifier *alg,
+ const heim_octet_string *data,
+ AlgorithmIdentifier *signatureAlgorithm,
+ heim_octet_string *sig)
+{
+ const AlgorithmIdentifier *digest_alg;
+ heim_octet_string indata;
+ const heim_oid *sig_oid;
+ unsigned int siglen;
+ int ret;
+
+ if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0)
+ _hx509_abort("internal error passing private key to wrong ops");
+
+ sig_oid = sig_alg->sig_oid;
+ digest_alg = sig_alg->digest_alg;
+
+ if (signatureAlgorithm) {
+ ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid,
+ "\x05\x00", 2);
+ if (ret) {
+ hx509_clear_error_string(context);
+ return ret;
+ }
+ }
+
+ ret = _hx509_create_signature(context,
+ NULL,
+ digest_alg,
+ data,
+ NULL,
+ &indata);
+ if (ret)
+ goto error;
+
+ sig->length = ECDSA_size(signer->private_key.ecdsa);
+ sig->data = malloc(sig->length);
+ if (sig->data == NULL) {
+ der_free_octet_string(&indata);
+ ret = ENOMEM;
+ hx509_set_error_string(context, 0, ret, "out of memory");
+ goto error;
+ }
+
+ siglen = sig->length;
+
+ ret = ECDSA_sign(-1, indata.data, indata.length,
+ sig->data, &siglen, signer->private_key.ecdsa);
+ der_free_octet_string(&indata);
+ if (ret != 1) {
+ ret = HX509_CMS_FAILED_CREATE_SIGATURE;
+ hx509_set_error_string(context, 0, ret,
+ "ECDSA sign failed: %d", ret);
+ goto error;
+ }
+ if (siglen > sig->length)
+ _hx509_abort("ECDSA signature prelen longer the output len");
+
+ sig->length = siglen;
+
+ return 0;
+ error:
+ if (signatureAlgorithm)
+ free_AlgorithmIdentifier(signatureAlgorithm);
+ return ret;
+}
+
+static int
+ecdsa_available(const hx509_private_key signer,
+ const AlgorithmIdentifier *sig_alg)
+{
+ const struct signature_alg *sig;
+ const EC_GROUP *group;
+ BN_CTX *bnctx = NULL;
+ BIGNUM *order = NULL;
+ int ret = 0;
+
+ if (der_heim_oid_cmp(signer->ops->key_oid, &asn1_oid_id_ecPublicKey) != 0)
+ _hx509_abort("internal error passing private key to wrong ops");
+
+ sig = _hx509_find_sig_alg(&sig_alg->algorithm);
+
+ if (sig == NULL || sig->digest_size == 0)
+ return 0;
+
+ group = EC_KEY_get0_group(signer->private_key.ecdsa);
+ if (group == NULL)
+ return 0;
+
+ bnctx = BN_CTX_new();
+ order = BN_new();
+ if (order == NULL)
+ goto err;
+
+ if (EC_GROUP_get_order(group, order, bnctx) != 1)
+ goto err;
+
+#if 0
+ /* If anything, require a digest at least as wide as the EC key size */
+ if (BN_num_bytes(order) > sig->digest_size)
+#endif
+ ret = 1;
+ err:
+ if (bnctx)
+ BN_CTX_free(bnctx);
+ if (order)
+ BN_clear_free(order);
+
+ return ret;
+}
+
+static int
+ecdsa_private_key2SPKI(hx509_context context,
+ hx509_private_key private_key,
+ SubjectPublicKeyInfo *spki)
+{
+ memset(spki, 0, sizeof(*spki));
+ return ENOMEM;
+}
+
+static int
+ecdsa_private_key_export(hx509_context context,
+ const hx509_private_key key,
+ hx509_key_format_t format,
+ heim_octet_string *data)
+{
+ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
+}
+
+static int
+ecdsa_private_key_import(hx509_context context,
+ const AlgorithmIdentifier *keyai,
+ const void *data,
+ size_t len,
+ hx509_key_format_t format,
+ hx509_private_key private_key)
+{
+ const unsigned char *p = data;
+ EC_KEY **pkey = NULL;
+ EC_KEY *key;
+
+ if (keyai->parameters) {
+ EC_GROUP *group;
+ int groupnid;
+ int ret;
+
+ ret = parse_ECParameters(context, keyai->parameters, &groupnid);
+ if (ret)
+ return ret;
+
+ key = EC_KEY_new();
+ if (key == NULL)
+ return ENOMEM;
+
+ group = EC_GROUP_new_by_curve_name(groupnid);
+ if (group == NULL) {
+ EC_KEY_free(key);
+ return ENOMEM;
+ }
+ EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
+ if (EC_KEY_set_group(key, group) == 0) {
+ EC_KEY_free(key);
+ EC_GROUP_free(group);
+ return ENOMEM;
+ }
+ EC_GROUP_free(group);
+ pkey = &key;
+ }
+
+ switch (format) {
+ case HX509_KEY_FORMAT_DER:
+
+ private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len);
+ if (private_key->private_key.ecdsa == NULL) {
+ hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
+ "Failed to parse EC private key");
+ return HX509_PARSING_KEY_FAILED;
+ }
+ private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256;
+ break;
+
+ default:
+ return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
+ }
+
+ return 0;
+}
+
+static int
+ecdsa_generate_private_key(hx509_context context,
+ struct hx509_generate_private_context *ctx,
+ hx509_private_key private_key)
+{
+ return ENOMEM;
+}
+
+static BIGNUM *
+ecdsa_get_internal(hx509_context context,
+ hx509_private_key key,
+ const char *type)
+{
+ return NULL;
+}
+
+static const unsigned ecPublicKey[] ={ 1, 2, 840, 10045, 2, 1 };
+const AlgorithmIdentifier _hx509_signature_ecPublicKey = {
+ { 6, rk_UNCONST(ecPublicKey) }, NULL
+};
+
+static const unsigned ecdsa_with_sha256_oid[] ={ 1, 2, 840, 10045, 4, 3, 2 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha256_data = {
+ { 7, rk_UNCONST(ecdsa_with_sha256_oid) }, NULL
+};
+
+static const unsigned ecdsa_with_sha384_oid[] ={ 1, 2, 840, 10045, 4, 3, 3 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha384_data = {
+ { 7, rk_UNCONST(ecdsa_with_sha384_oid) }, NULL
+};
+
+static const unsigned ecdsa_with_sha512_oid[] ={ 1, 2, 840, 10045, 4, 3, 4 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha512_data = {
+ { 7, rk_UNCONST(ecdsa_with_sha512_oid) }, NULL
+};
+
+static const unsigned ecdsa_with_sha1_oid[] ={ 1, 2, 840, 10045, 4, 1 };
+const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha1_data = {
+ { 6, rk_UNCONST(ecdsa_with_sha1_oid) }, NULL
+};
+
+hx509_private_key_ops ecdsa_private_key_ops = {
+ "EC PRIVATE KEY",
+ ASN1_OID_ID_ECPUBLICKEY,
+ ecdsa_available,
+ ecdsa_private_key2SPKI,
+ ecdsa_private_key_export,
+ ecdsa_private_key_import,
+ ecdsa_generate_private_key,
+ ecdsa_get_internal
+};
+
+const struct signature_alg ecdsa_with_sha512_alg = {
+ "ecdsa-with-sha512",
+ ASN1_OID_ID_ECDSA_WITH_SHA512,
+ &_hx509_signature_ecdsa_with_sha512_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha512_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 64
+};
+
+const struct signature_alg ecdsa_with_sha384_alg = {
+ "ecdsa-with-sha384",
+ ASN1_OID_ID_ECDSA_WITH_SHA384,
+ &_hx509_signature_ecdsa_with_sha384_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha384_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 48
+};
+
+const struct signature_alg ecdsa_with_sha256_alg = {
+ "ecdsa-with-sha256",
+ ASN1_OID_ID_ECDSA_WITH_SHA256,
+ &_hx509_signature_ecdsa_with_sha256_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha256_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 32
+};
+
+const struct signature_alg ecdsa_with_sha1_alg = {
+ "ecdsa-with-sha1",
+ ASN1_OID_ID_ECDSA_WITH_SHA1,
+ &_hx509_signature_ecdsa_with_sha1_data,
+ ASN1_OID_ID_ECPUBLICKEY,
+ &_hx509_signature_sha1_data,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|
+ SIG_PUBLIC_SIG|SELF_SIGNED_OK,
+ 0,
+ NULL,
+ ecdsa_verify_signature,
+ ecdsa_create_signature,
+ 20
+};
+
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+
+const AlgorithmIdentifier *
+hx509_signature_ecPublicKey(void)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ return &_hx509_signature_ecPublicKey;
+#else
+ return NULL;
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+}
+
+const AlgorithmIdentifier *
+hx509_signature_ecdsa_with_sha256(void)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ return &_hx509_signature_ecdsa_with_sha256_data;
+#else
+ return NULL;
+#endif /* HAVE_HCRYPTO_W_OPENSSL */
+}
diff --git a/lib/hx509/crypto.c b/lib/hx509/crypto.c
index 4559a9c49391..5ddc54b9f232 100644
--- a/lib/hx509/crypto.c
+++ b/lib/hx509/crypto.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * Copyright (c) 2004 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -33,95 +33,12 @@
#include "hx_locl.h"
-struct hx509_crypto;
-
-struct signature_alg;
-
-struct hx509_generate_private_context {
- const heim_oid *key_oid;
- int isCA;
- unsigned long num_bits;
-};
-
-struct hx509_private_key_ops {
- const char *pemtype;
- const heim_oid *key_oid;
- int (*available)(const hx509_private_key,
- const AlgorithmIdentifier *);
- int (*get_spki)(hx509_context,
- const hx509_private_key,
- SubjectPublicKeyInfo *);
- int (*export)(hx509_context context,
- const hx509_private_key,
- hx509_key_format_t,
- heim_octet_string *);
- int (*import)(hx509_context, const AlgorithmIdentifier *,
- const void *, size_t, hx509_key_format_t,
- hx509_private_key);
- int (*generate_private_key)(hx509_context,
- struct hx509_generate_private_context *,
- hx509_private_key);
- BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *);
-};
-
-struct hx509_private_key {
- unsigned int ref;
- const struct signature_alg *md;
- const heim_oid *signature_alg;
- union {
- RSA *rsa;
- void *keydata;
-#ifdef HAVE_OPENSSL
- EC_KEY *ecdsa;
-#endif
- } private_key;
- hx509_private_key_ops *ops;
-};
-
-/*
- *
- */
-
-struct signature_alg {
- const char *name;
- const heim_oid *sig_oid;
- const AlgorithmIdentifier *sig_alg;
- const heim_oid *key_oid;
- const AlgorithmIdentifier *digest_alg;
- int flags;
-#define PROVIDE_CONF 0x1
-#define REQUIRE_SIGNER 0x2
-#define SELF_SIGNED_OK 0x4
-
-#define SIG_DIGEST 0x100
-#define SIG_PUBLIC_SIG 0x200
-#define SIG_SECRET 0x400
-
-#define RA_RSA_USES_DIGEST_INFO 0x1000000
-
- time_t best_before; /* refuse signature made after best before date */
- const EVP_MD *(*evp_md)(void);
- int (*verify_signature)(hx509_context context,
- const struct signature_alg *,
- const Certificate *,
- const AlgorithmIdentifier *,
- const heim_octet_string *,
- const heim_octet_string *);
- int (*create_signature)(hx509_context,
- const struct signature_alg *,
- const hx509_private_key,
- const AlgorithmIdentifier *,
- const heim_octet_string *,
- AlgorithmIdentifier *,
- heim_octet_string *);
- int digest_size;
-};
-
-static const struct signature_alg *
-find_sig_alg(const heim_oid *oid);
-
-/*
+/*-
+ * RFC5758 specifies no parameters for ecdsa-with-SHA<N> signatures
+ * RFC5754 specifies NULL parameters for sha<N>WithRSAEncryption signatures
*
+ * XXX: Make sure that the parameters are either NULL in both the tbs and the
+ * signature, or absent from both the tbs and the signature.
*/
static const heim_octet_string null_entry_oid = { 2, rk_UNCONST("\x05\x00") };
@@ -151,44 +68,29 @@ const AlgorithmIdentifier _hx509_signature_md5_data = {
{ 6, rk_UNCONST(md5_oid_tree) }, rk_UNCONST(&null_entry_oid)
};
-static const unsigned ecPublicKey[] ={ 1, 2, 840, 10045, 2, 1 };
-const AlgorithmIdentifier _hx509_signature_ecPublicKey = {
- { 6, rk_UNCONST(ecPublicKey) }, NULL
-};
-
-static const unsigned ecdsa_with_sha256_oid[] ={ 1, 2, 840, 10045, 4, 3, 2 };
-const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha256_data = {
- { 7, rk_UNCONST(ecdsa_with_sha256_oid) }, NULL
-};
-
-static const unsigned ecdsa_with_sha1_oid[] ={ 1, 2, 840, 10045, 4, 1 };
-const AlgorithmIdentifier _hx509_signature_ecdsa_with_sha1_data = {
- { 6, rk_UNCONST(ecdsa_with_sha1_oid) }, NULL
-};
-
static const unsigned rsa_with_sha512_oid[] ={ 1, 2, 840, 113549, 1, 1, 13 };
const AlgorithmIdentifier _hx509_signature_rsa_with_sha512_data = {
- { 7, rk_UNCONST(rsa_with_sha512_oid) }, NULL
+ { 7, rk_UNCONST(rsa_with_sha512_oid) }, rk_UNCONST(&null_entry_oid)
};
static const unsigned rsa_with_sha384_oid[] ={ 1, 2, 840, 113549, 1, 1, 12 };
const AlgorithmIdentifier _hx509_signature_rsa_with_sha384_data = {
- { 7, rk_UNCONST(rsa_with_sha384_oid) }, NULL
+ { 7, rk_UNCONST(rsa_with_sha384_oid) }, rk_UNCONST(&null_entry_oid)
};
static const unsigned rsa_with_sha256_oid[] ={ 1, 2, 840, 113549, 1, 1, 11 };
const AlgorithmIdentifier _hx509_signature_rsa_with_sha256_data = {
- { 7, rk_UNCONST(rsa_with_sha256_oid) }, NULL
+ { 7, rk_UNCONST(rsa_with_sha256_oid) }, rk_UNCONST(&null_entry_oid)
};
static const unsigned rsa_with_sha1_oid[] ={ 1, 2, 840, 113549, 1, 1, 5 };
const AlgorithmIdentifier _hx509_signature_rsa_with_sha1_data = {
- { 7, rk_UNCONST(rsa_with_sha1_oid) }, NULL
+ { 7, rk_UNCONST(rsa_with_sha1_oid) }, rk_UNCONST(&null_entry_oid)
};
static const unsigned rsa_with_md5_oid[] ={ 1, 2, 840, 113549, 1, 1, 4 };
const AlgorithmIdentifier _hx509_signature_rsa_with_md5_data = {
- { 7, rk_UNCONST(rsa_with_md5_oid) }, NULL
+ { 7, rk_UNCONST(rsa_with_md5_oid) }, rk_UNCONST(&null_entry_oid)
};
static const unsigned rsa_oid[] ={ 1, 2, 840, 113549, 1, 1, 1 };
@@ -234,10 +136,10 @@ heim_int2BN(const heim_integer *i)
*
*/
-static int
-set_digest_alg(DigestAlgorithmIdentifier *id,
- const heim_oid *oid,
- const void *param, size_t length)
+int
+_hx509_set_digest_alg(DigestAlgorithmIdentifier *id,
+ const heim_oid *oid,
+ const void *param, size_t length)
{
int ret;
if (param) {
@@ -266,265 +168,6 @@ set_digest_alg(DigestAlgorithmIdentifier *id,
return 0;
}
-#ifdef HAVE_OPENSSL
-
-static int
-heim_oid2ecnid(heim_oid *oid)
-{
- /*
- * Now map to openssl OID fun
- */
-
- if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP256R1) == 0)
- return NID_X9_62_prime256v1;
- else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R1) == 0)
- return NID_secp160r1;
- else if (der_heim_oid_cmp(oid, ASN1_OID_ID_EC_GROUP_SECP160R2) == 0)
- return NID_secp160r2;
-
- return -1;
-}
-
-static int
-parse_ECParameters(hx509_context context,
- heim_octet_string *parameters, int *nid)
-{
- ECParameters ecparam;
- size_t size;
- int ret;
-
- if (parameters == NULL) {
- ret = HX509_PARSING_KEY_FAILED;
- hx509_set_error_string(context, 0, ret,
- "EC parameters missing");
- return ret;
- }
-
- ret = decode_ECParameters(parameters->data, parameters->length,
- &ecparam, &size);
- if (ret) {
- hx509_set_error_string(context, 0, ret,
- "Failed to decode EC parameters");
- return ret;
- }
-
- if (ecparam.element != choice_ECParameters_namedCurve) {
- free_ECParameters(&ecparam);
- hx509_set_error_string(context, 0, ret,
- "EC parameters is not a named curve");
- return HX509_CRYPTO_SIG_INVALID_FORMAT;
- }
-
- *nid = heim_oid2ecnid(&ecparam.u.namedCurve);
- free_ECParameters(&ecparam);
- if (*nid == -1) {
- hx509_set_error_string(context, 0, ret,
- "Failed to find matcing NID for EC curve");
- return HX509_CRYPTO_SIG_INVALID_FORMAT;
- }
- return 0;
-}
-
-
-/*
- *
- */
-
-static int
-ecdsa_verify_signature(hx509_context context,
- const struct signature_alg *sig_alg,
- const Certificate *signer,
- const AlgorithmIdentifier *alg,
- const heim_octet_string *data,
- const heim_octet_string *sig)
-{
- const AlgorithmIdentifier *digest_alg;
- const SubjectPublicKeyInfo *spi;
- heim_octet_string digest;
- int ret;
- EC_KEY *key = NULL;
- int groupnid;
- EC_GROUP *group;
- const unsigned char *p;
- long len;
-
- digest_alg = sig_alg->digest_alg;
-
- ret = _hx509_create_signature(context,
- NULL,
- digest_alg,
- data,
- NULL,
- &digest);
- if (ret)
- return ret;
-
- /* set up EC KEY */
- spi = &signer->tbsCertificate.subjectPublicKeyInfo;
-
- if (der_heim_oid_cmp(&spi->algorithm.algorithm, ASN1_OID_ID_ECPUBLICKEY) != 0)
- return HX509_CRYPTO_SIG_INVALID_FORMAT;
-
-#ifdef HAVE_OPENSSL
- /*
- * Find the group id
- */
-
- ret = parse_ECParameters(context, spi->algorithm.parameters, &groupnid);
- if (ret) {
- der_free_octet_string(&digest);
- return ret;
- }
-
- /*
- * Create group, key, parse key
- */
-
- key = EC_KEY_new();
- group = EC_GROUP_new_by_curve_name(groupnid);
- EC_KEY_set_group(key, group);
- EC_GROUP_free(group);
-
- p = spi->subjectPublicKey.data;
- len = spi->subjectPublicKey.length / 8;
-
- if (o2i_ECPublicKey(&key, &p, len) == NULL) {
- EC_KEY_free(key);
- return HX509_CRYPTO_SIG_INVALID_FORMAT;
- }
-#else
- key = SubjectPublicKeyInfo2EC_KEY(spi);
-#endif
-
- ret = ECDSA_verify(-1, digest.data, digest.length,
- sig->data, sig->length, key);
- der_free_octet_string(&digest);
- EC_KEY_free(key);
- if (ret != 1) {
- ret = HX509_CRYPTO_SIG_INVALID_FORMAT;
- return ret;
- }
-
- return 0;
-}
-
-static int
-ecdsa_create_signature(hx509_context context,
- const struct signature_alg *sig_alg,
- const hx509_private_key signer,
- const AlgorithmIdentifier *alg,
- const heim_octet_string *data,
- AlgorithmIdentifier *signatureAlgorithm,
- heim_octet_string *sig)
-{
- const AlgorithmIdentifier *digest_alg;
- heim_octet_string indata;
- const heim_oid *sig_oid;
- unsigned int siglen;
- int ret;
-
- if (signer->ops && der_heim_oid_cmp(signer->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) != 0)
- _hx509_abort("internal error passing private key to wrong ops");
-
- sig_oid = sig_alg->sig_oid;
- digest_alg = sig_alg->digest_alg;
-
- if (signatureAlgorithm) {
- ret = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2);
- if (ret) {
- hx509_clear_error_string(context);
- goto error;
- }
- }
-
- ret = _hx509_create_signature(context,
- NULL,
- digest_alg,
- data,
- NULL,
- &indata);
- if (ret) {
- if (signatureAlgorithm)
- free_AlgorithmIdentifier(signatureAlgorithm);
- goto error;
- }
-
- sig->length = ECDSA_size(signer->private_key.ecdsa);
- sig->data = malloc(sig->length);
- if (sig->data == NULL) {
- der_free_octet_string(&indata);
- ret = ENOMEM;
- hx509_set_error_string(context, 0, ret, "out of memory");
- goto error;
- }
-
- siglen = sig->length;
-
- ret = ECDSA_sign(-1, indata.data, indata.length,
- sig->data, &siglen, signer->private_key.ecdsa);
- der_free_octet_string(&indata);
- if (ret != 1) {
- ret = HX509_CMS_FAILED_CREATE_SIGATURE;
- hx509_set_error_string(context, 0, ret,
- "ECDSA sign failed: %d", ret);
- goto error;
- }
- if (siglen > sig->length)
- _hx509_abort("ECDSA signature prelen longer the output len");
-
- sig->length = siglen;
-
- return 0;
- error:
- if (signatureAlgorithm)
- free_AlgorithmIdentifier(signatureAlgorithm);
- return ret;
-}
-
-static int
-ecdsa_available(const hx509_private_key signer,
- const AlgorithmIdentifier *sig_alg)
-{
- const struct signature_alg *sig;
- const EC_GROUP *group;
- BN_CTX *bnctx = NULL;
- BIGNUM *order = NULL;
- int ret = 0;
-
- if (der_heim_oid_cmp(signer->ops->key_oid, &asn1_oid_id_ecPublicKey) != 0)
- _hx509_abort("internal error passing private key to wrong ops");
-
- sig = find_sig_alg(&sig_alg->algorithm);
-
- if (sig == NULL || sig->digest_size == 0)
- return 0;
-
- group = EC_KEY_get0_group(signer->private_key.ecdsa);
- if (group == NULL)
- return 0;
-
- bnctx = BN_CTX_new();
- order = BN_new();
- if (order == NULL)
- goto err;
-
- if (EC_GROUP_get_order(group, order, bnctx) != 1)
- goto err;
-
- if (BN_num_bytes(order) > sig->digest_size)
- ret = 1;
- err:
- if (bnctx)
- BN_CTX_free(bnctx);
- if (order)
- BN_clear_free(order);
-
- return ret;
-}
-
-
-#endif /* HAVE_OPENSSL */
-
/*
*
*/
@@ -619,6 +262,9 @@ rsa_verify_signature(hx509_context context,
&di.digestAlgorithm,
data,
&di.digest);
+ if (ret)
+ goto out;
+
} else {
if ((size_t)retsize != data->length ||
ct_memcmp(to, data->data, retsize) != 0)
@@ -628,8 +274,8 @@ rsa_verify_signature(hx509_context context,
goto out;
}
free(to);
+ ret = 0;
}
- ret = 0;
out:
free_DigestInfo(&di);
@@ -683,7 +329,8 @@ rsa_create_signature(hx509_context context,
return HX509_ALG_NOT_SUPP;
if (signatureAlgorithm) {
- ret = set_digest_alg(signatureAlgorithm, sig_oid, "\x05\x00", 2);
+ ret = _hx509_set_digest_alg(signatureAlgorithm, sig_oid,
+ "\x05\x00", 2);
if (ret) {
hx509_clear_error_string(context);
return ret;
@@ -739,11 +386,13 @@ rsa_create_signature(hx509_context context,
"RSA private encrypt failed: %d", ret);
return ret;
}
- if ((size_t)ret > sig->length)
+ if (sig->length > (size_t)ret) {
+ size = sig->length - ret;
+ memmove((uint8_t *)sig->data + size, sig->data, ret);
+ memset(sig->data, 0, size);
+ } else if (sig->length < (size_t)ret)
_hx509_abort("RSA signature prelen longer the output len");
- sig->length = ret;
-
return 0;
}
@@ -795,8 +444,9 @@ rsa_private_key2SPKI(hx509_context context,
}
spki->subjectPublicKey.length = len * 8;
- ret = set_digest_alg(&spki->algorithm, ASN1_OID_ID_PKCS1_RSAENCRYPTION,
- "\x05\x00", 2);
+ ret = _hx509_set_digest_alg(&spki->algorithm,
+ ASN1_OID_ID_PKCS1_RSAENCRYPTION,
+ "\x05\x00", 2);
if (ret) {
hx509_set_error_string(context, 0, ret, "malloc - out of memory");
free(spki->subjectPublicKey.data);
@@ -920,115 +570,6 @@ static hx509_private_key_ops rsa_private_key_ops = {
rsa_get_internal
};
-#ifdef HAVE_OPENSSL
-
-static int
-ecdsa_private_key2SPKI(hx509_context context,
- hx509_private_key private_key,
- SubjectPublicKeyInfo *spki)
-{
- memset(spki, 0, sizeof(*spki));
- return ENOMEM;
-}
-
-static int
-ecdsa_private_key_export(hx509_context context,
- const hx509_private_key key,
- hx509_key_format_t format,
- heim_octet_string *data)
-{
- return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
-}
-
-static int
-ecdsa_private_key_import(hx509_context context,
- const AlgorithmIdentifier *keyai,
- const void *data,
- size_t len,
- hx509_key_format_t format,
- hx509_private_key private_key)
-{
- const unsigned char *p = data;
- EC_KEY **pkey = NULL;
-
- if (keyai->parameters) {
- EC_GROUP *group;
- int groupnid;
- EC_KEY *key;
- int ret;
-
- ret = parse_ECParameters(context, keyai->parameters, &groupnid);
- if (ret)
- return ret;
-
- key = EC_KEY_new();
- if (key == NULL)
- return ENOMEM;
-
- group = EC_GROUP_new_by_curve_name(groupnid);
- if (group == NULL) {
- EC_KEY_free(key);
- return ENOMEM;
- }
- EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
- if (EC_KEY_set_group(key, group) == 0) {
- EC_KEY_free(key);
- EC_GROUP_free(group);
- return ENOMEM;
- }
- EC_GROUP_free(group);
- pkey = &key;
- }
-
- switch (format) {
- case HX509_KEY_FORMAT_DER:
-
- private_key->private_key.ecdsa = d2i_ECPrivateKey(pkey, &p, len);
- if (private_key->private_key.ecdsa == NULL) {
- hx509_set_error_string(context, 0, HX509_PARSING_KEY_FAILED,
- "Failed to parse EC private key");
- return HX509_PARSING_KEY_FAILED;
- }
- private_key->signature_alg = ASN1_OID_ID_ECDSA_WITH_SHA256;
- break;
-
- default:
- return HX509_CRYPTO_KEY_FORMAT_UNSUPPORTED;
- }
-
- return 0;
-}
-
-static int
-ecdsa_generate_private_key(hx509_context context,
- struct hx509_generate_private_context *ctx,
- hx509_private_key private_key)
-{
- return ENOMEM;
-}
-
-static BIGNUM *
-ecdsa_get_internal(hx509_context context,
- hx509_private_key key,
- const char *type)
-{
- return NULL;
-}
-
-
-static hx509_private_key_ops ecdsa_private_key_ops = {
- "EC PRIVATE KEY",
- ASN1_OID_ID_ECPUBLICKEY,
- ecdsa_available,
- ecdsa_private_key2SPKI,
- ecdsa_private_key_export,
- ecdsa_private_key_import,
- ecdsa_generate_private_key,
- ecdsa_get_internal
-};
-
-#endif /* HAVE_OPENSSL */
-
/*
*
*/
@@ -1157,8 +698,8 @@ evp_md_create_signature(hx509_context context,
if (signatureAlgorithm) {
int ret;
- ret = set_digest_alg(signatureAlgorithm, sig_alg->sig_oid,
- "\x05\x00", 2);
+ ret = _hx509_set_digest_alg(signatureAlgorithm,
+ sig_alg->sig_oid, "\x05\x00", 2);
if (ret)
return ret;
}
@@ -1214,36 +755,11 @@ evp_md_verify_signature(hx509_context context,
return 0;
}
-#ifdef HAVE_OPENSSL
-
-static const struct signature_alg ecdsa_with_sha256_alg = {
- "ecdsa-with-sha256",
- ASN1_OID_ID_ECDSA_WITH_SHA256,
- &_hx509_signature_ecdsa_with_sha256_data,
- ASN1_OID_ID_ECPUBLICKEY,
- &_hx509_signature_sha256_data,
- PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
- 0,
- NULL,
- ecdsa_verify_signature,
- ecdsa_create_signature,
- 32
-};
-
-static const struct signature_alg ecdsa_with_sha1_alg = {
- "ecdsa-with-sha1",
- ASN1_OID_ID_ECDSA_WITH_SHA1,
- &_hx509_signature_ecdsa_with_sha1_data,
- ASN1_OID_ID_ECPUBLICKEY,
- &_hx509_signature_sha1_data,
- PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|SELF_SIGNED_OK,
- 0,
- NULL,
- ecdsa_verify_signature,
- ecdsa_create_signature,
- 20
-};
-
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+extern const struct signature_alg ecdsa_with_sha512_alg;
+extern const struct signature_alg ecdsa_with_sha384_alg;
+extern const struct signature_alg ecdsa_with_sha256_alg;
+extern const struct signature_alg ecdsa_with_sha1_alg;
#endif
static const struct signature_alg heim_rsa_pkcs1_x509 = {
@@ -1350,7 +866,7 @@ static const struct signature_alg rsa_with_md5_alg = {
&_hx509_signature_rsa_with_md5_data,
ASN1_OID_ID_PKCS1_RSAENCRYPTION,
&_hx509_signature_md5_data,
- PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG,
+ PROVIDE_CONF|REQUIRE_SIGNER|RA_RSA_USES_DIGEST_INFO|SIG_PUBLIC_SIG|WEAK_SIG_ALG,
1230739889,
NULL,
rsa_verify_signature,
@@ -1434,7 +950,7 @@ static const struct signature_alg md5_alg = {
&_hx509_signature_md5_data,
NULL,
NULL,
- SIG_DIGEST,
+ SIG_DIGEST|WEAK_SIG_ALG,
0,
EVP_md5,
evp_md_verify_signature,
@@ -1448,7 +964,9 @@ static const struct signature_alg md5_alg = {
*/
static const struct signature_alg *sig_algs[] = {
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ &ecdsa_with_sha512_alg,
+ &ecdsa_with_sha384_alg,
&ecdsa_with_sha256_alg,
&ecdsa_with_sha1_alg,
#endif
@@ -1469,8 +987,8 @@ static const struct signature_alg *sig_algs[] = {
NULL
};
-static const struct signature_alg *
-find_sig_alg(const heim_oid *oid)
+const struct signature_alg *
+_hx509_find_sig_alg(const heim_oid *oid)
{
unsigned int i;
for (i = 0; sig_algs[i]; i++)
@@ -1511,10 +1029,13 @@ alg_for_privatekey(const hx509_private_key pk, int type)
/*
*
*/
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+extern hx509_private_key_ops ecdsa_private_key_ops;
+#endif
static struct hx509_private_key_ops *private_algs[] = {
&rsa_private_key_ops,
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_HCRYPTO_W_OPENSSL
&ecdsa_private_key_ops,
#endif
NULL
@@ -1539,21 +1060,18 @@ hx509_find_private_alg(const heim_oid *oid)
*/
int
-_hx509_signature_best_before(hx509_context context,
- const AlgorithmIdentifier *alg,
- time_t t)
+_hx509_signature_is_weak(hx509_context context, const AlgorithmIdentifier *alg)
{
const struct signature_alg *md;
- md = find_sig_alg(&alg->algorithm);
+ md = _hx509_find_sig_alg(&alg->algorithm);
if (md == NULL) {
hx509_clear_error_string(context);
return HX509_SIG_ALG_NO_SUPPORTED;
}
- if (md->best_before && md->best_before < t) {
+ if (md->flags & WEAK_SIG_ALG) {
hx509_set_error_string(context, 0, HX509_CRYPTO_ALGORITHM_BEST_BEFORE,
- "Algorithm %s has passed it best before date",
- md->name);
+ "Algorithm %s is weak", md->name);
return HX509_CRYPTO_ALGORITHM_BEST_BEFORE;
}
return 0;
@@ -1565,7 +1083,7 @@ _hx509_self_signed_valid(hx509_context context,
{
const struct signature_alg *md;
- md = find_sig_alg(&alg->algorithm);
+ md = _hx509_find_sig_alg(&alg->algorithm);
if (md == NULL) {
hx509_clear_error_string(context);
return HX509_SIG_ALG_NO_SUPPORTED;
@@ -1593,7 +1111,7 @@ _hx509_verify_signature(hx509_context context,
if (cert)
signer = _hx509_get_cert(cert);
- md = find_sig_alg(&alg->algorithm);
+ md = _hx509_find_sig_alg(&alg->algorithm);
if (md == NULL) {
hx509_clear_error_string(context);
return HX509_SIG_ALG_NO_SUPPORTED;
@@ -1628,7 +1146,7 @@ _hx509_create_signature(hx509_context context,
{
const struct signature_alg *md;
- md = find_sig_alg(&alg->algorithm);
+ md = _hx509_find_sig_alg(&alg->algorithm);
if (md == NULL) {
hx509_set_error_string(context, 0, HX509_SIG_ALG_NO_SUPPORTED,
"algorithm no supported");
@@ -1923,18 +1441,6 @@ hx509_signature_md5(void)
{ return &_hx509_signature_md5_data; }
const AlgorithmIdentifier *
-hx509_signature_ecPublicKey(void)
-{ return &_hx509_signature_ecPublicKey; }
-
-const AlgorithmIdentifier *
-hx509_signature_ecdsa_with_sha256(void)
-{ return &_hx509_signature_ecdsa_with_sha256_data; }
-
-const AlgorithmIdentifier *
-hx509_signature_ecdsa_with_sha1(void)
-{ return &_hx509_signature_ecdsa_with_sha1_data; }
-
-const AlgorithmIdentifier *
hx509_signature_rsa_with_sha512(void)
{ return &_hx509_signature_rsa_with_sha512_data; }
@@ -2034,11 +1540,10 @@ hx509_private_key_free(hx509_private_key *key)
if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0) {
if ((*key)->private_key.rsa)
RSA_free((*key)->private_key.rsa);
-#ifdef HAVE_OPENSSL
- } else if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) == 0) {
- if ((*key)->private_key.ecdsa)
- EC_KEY_free((*key)->private_key.ecdsa);
-#endif
+ } else if ((*key)->ops && der_heim_oid_cmp((*key)->ops->key_oid,
+ ASN1_OID_ID_ECPUBLICKEY) == 0 &&
+ (*key)->private_key.ecdsa != NULL) {
+ _hx509_private_eckey_free((*key)->private_key.ecdsa);
}
(*key)->private_key.rsa = NULL;
free(*key);
@@ -2806,29 +2311,49 @@ find_string2key(const heim_oid *oid,
{
if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC2_CBC) == 0) {
*c = EVP_rc2_40_cbc();
+ if (*c == NULL)
+ return NULL;
*md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
*s2k = PBE_string2key;
return &asn1_oid_private_rc2_40;
} else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC2_CBC) == 0) {
*c = EVP_rc2_cbc();
+ if (*c == NULL)
+ return NULL;
*md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
*s2k = PBE_string2key;
return ASN1_OID_ID_PKCS3_RC2_CBC;
#if 0
} else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND40BITRC4) == 0) {
*c = EVP_rc4_40();
+ if (*c == NULL)
+ return NULL;
*md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
*s2k = PBE_string2key;
return NULL;
} else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND128BITRC4) == 0) {
*c = EVP_rc4();
+ if (*c == NULL)
+ return NULL;
*md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
*s2k = PBE_string2key;
return ASN1_OID_ID_PKCS3_RC4;
#endif
} else if (der_heim_oid_cmp(oid, ASN1_OID_ID_PBEWITHSHAAND3_KEYTRIPLEDES_CBC) == 0) {
*c = EVP_des_ede3_cbc();
+ if (*c == NULL)
+ return NULL;
*md = EVP_sha1();
+ if (*md == NULL)
+ return NULL;
*s2k = PBE_string2key;
return ASN1_OID_ID_PKCS3_DES_EDE3_CBC;
}
@@ -3008,6 +2533,8 @@ match_keys_ec(hx509_cert c, hx509_private_key private_key)
int
_hx509_match_keys(hx509_cert c, hx509_private_key key)
{
+ if (!key->ops)
+ return 0;
if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_PKCS1_RSAENCRYPTION) == 0)
return match_keys_rsa(c, key);
if (der_heim_oid_cmp(key->ops->key_oid, ASN1_OID_ID_ECPUBLICKEY) == 0)
@@ -3025,7 +2552,7 @@ find_keytype(const hx509_private_key key)
if (key == NULL)
return NULL;
- md = find_sig_alg(key->signature_alg);
+ md = _hx509_find_sig_alg(key->signature_alg);
if (md == NULL)
return NULL;
return md->key_oid;
diff --git a/lib/hx509/data/PKITS_data.zip b/lib/hx509/data/PKITS_data.zip
new file mode 100644
index 000000000000..50d6fbb375ce
--- /dev/null
+++ b/lib/hx509/data/PKITS_data.zip
Binary files differ
diff --git a/lib/hx509/data/eccurve.pem b/lib/hx509/data/eccurve.pem
new file mode 100644
index 000000000000..a76e47d9590b
--- /dev/null
+++ b/lib/hx509/data/eccurve.pem
@@ -0,0 +1,3 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
diff --git a/lib/hx509/data/https.crt b/lib/hx509/data/https.crt
new file mode 100644
index 000000000000..2056c899c8cb
--- /dev/null
+++ b/lib/hx509/data/https.crt
@@ -0,0 +1,53 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 9 (0x9)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: CN=hx509 Test Root CA, C=SE
+ Validity
+ Not Before: Apr 26 20:29:41 2009 GMT
+ Not After : Apr 24 20:29:41 2019 GMT
+ Subject: C=SE, CN=www.test.h5l.se
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:c6:f4:94:25:2b:d5:fa:e9:3d:00:a9:46:24:f1:
+ bf:fe:61:df:bd:cc:da:74:b7:f9:36:c1:ce:51:d2:
+ 01:6f:79:ba:b3:4a:d5:a4:43:5a:c7:ad:e4:e0:50:
+ e2:a6:bf:54:73:ad:a5:86:0a:bd:56:c4:4f:b5:f5:
+ 7f:7e:fe:10:78:17:e2:35:4c:bb:cc:4b:74:35:d0:
+ ab:63:b1:02:72:94:a7:9a:dc:10:ef:28:82:a9:6c:
+ dc:19:8d:b8:3e:5b:21:52:1f:88:51:a6:5a:f8:67:
+ cd:cb:48:6f:f5:8b:71:7c:4d:52:da:bb:f9:26:8a:
+ 27:9c:7e:8a:d1:99:54:35:7f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Subject Key Identifier:
+ 8A:BB:2D:06:4B:BD:DE:9A:BA:7C:5A:35:D0:E2:19:37:48:29:0E:9C
+ Signature Algorithm: sha1WithRSAEncryption
+ 90:35:ec:8c:f2:62:14:76:8a:29:52:99:44:c1:d7:c8:9e:74:
+ ef:90:4f:e7:ea:4d:cf:8f:c1:73:0c:d9:49:06:93:30:b0:19:
+ 5e:de:9c:11:93:66:02:4d:8f:e9:8c:52:fc:26:26:9e:09:69:
+ a5:a7:63:d2:2d:40:de:e5:d8:d5:51:c3:32:60:6a:2f:26:13:
+ 91:69:36:f6:67:e4:b0:54:9e:77:68:22:5f:51:b8:3c:42:bd:
+ e3:09:dc:11:9b:ed:db:63:df:90:57:38:00:90:be:89:e7:ea:
+ 8e:d8:21:cd:96:68:69:4b:a3:15:50:ce:63:80:2a:99:4b:ff:
+ dd:1c
+-----BEGIN CERTIFICATE-----
+MIICADCCAWmgAwIBAgIBCTANBgkqhkiG9w0BAQUFADAqMRswGQYDVQQDDBJoeDUw
+OSBUZXN0IFJvb3QgQ0ExCzAJBgNVBAYTAlNFMB4XDTA5MDQyNjIwMjk0MVoXDTE5
+MDQyNDIwMjk0MVowJzELMAkGA1UEBhMCU0UxGDAWBgNVBAMMD3d3dy50ZXN0Lmg1
+bC5zZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxvSUJSvV+uk9AKlGJPG/
+/mHfvczadLf5NsHOUdIBb3m6s0rVpENax63k4FDipr9Uc62lhgq9VsRPtfV/fv4Q
+eBfiNUy7zEt0NdCrY7ECcpSnmtwQ7yiCqWzcGY24PlshUh+IUaZa+GfNy0hv9Ytx
+fE1S2rv5JoonnH6K0ZlUNX8CAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMC
+BeAwHQYDVR0OBBYEFIq7LQZLvd6aunxaNdDiGTdIKQ6cMA0GCSqGSIb3DQEBBQUA
+A4GBAJA17IzyYhR2iilSmUTB18iedO+QT+fqTc+PwXMM2UkGkzCwGV7enBGTZgJN
+j+mMUvwmJp4JaaWnY9ItQN7l2NVRwzJgai8mE5FpNvZn5LBUnndoIl9RuDxCveMJ
+3BGb7dtj35BXOACQvonn6o7YIc2WaGlLoxVQzmOAKplL/90c
+-----END CERTIFICATE-----
diff --git a/lib/hx509/data/https.key b/lib/hx509/data/https.key
new file mode 100644
index 000000000000..ee6065a0006f
--- /dev/null
+++ b/lib/hx509/data/https.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAMb0lCUr1frpPQCp
+RiTxv/5h373M2nS3+TbBzlHSAW95urNK1aRDWset5OBQ4qa/VHOtpYYKvVbET7X1
+f37+EHgX4jVMu8xLdDXQq2OxAnKUp5rcEO8ogqls3BmNuD5bIVIfiFGmWvhnzctI
+b/WLcXxNUtq7+SaKJ5x+itGZVDV/AgMBAAECgYBfO282I7d3NPGYQW5r/LPUBfFd
+HpNqzy0hQr+JdqZtP61YaPe+eucXMWue29jBzE+WV4YllTpwL+Ofy3VNyjsDCIva
+acqVrimYl5EAT1yiqvC1DNC0SvAfEsBlpMJr7w8F4M7wbSxvGIWjRVeZtLd7H4pw
+8ooDNZNlcXPyrBozQQJBAPGxPPiO66EpiN66ffRiqnof1lGUFaZPqBKYF/M3mybt
+X7vMKQsrQpdNQTbtR2u42yBUJGw4trhIn1qDInkgXfECQQDSu61Z/m5xRVlBk3mj
+QMqSVX+FoD3WtSry003lcxGfNsuguJtYHXHHhPbPNMUaDEtErkbUMQHNFX5mEjGp
+0RpvAkEAwbDhhOy8pw5rMtvP3w9HQdHL5tq/MuY5cpVS9EaG335yL0VhSyMjHa/6
+6HLlvs2JRnJIMjaNMEh69IWNFfc7cQJBAIOzIy3BI0jLLHMdNcHfdjpqEJ50fPE4
+nDTR9jbV6Ud1uWEivoMdM8SbxpvMwPn8gPXVbRKj5hpDupEUAdG9iyUCQQCNSVcl
+NREl42G5ZQ2Q+zYtYIJbe9SAxu7WcfzctFleRbmKPLqrcnCLWenWWHtrzZLRgFhw
+rLiglEkVDRXivfhq
+-----END PRIVATE KEY-----
diff --git a/lib/hx509/data/mkcert.sh b/lib/hx509/data/mkcert.sh
new file mode 100755
index 000000000000..5faa571206a8
--- /dev/null
+++ b/lib/hx509/data/mkcert.sh
@@ -0,0 +1,84 @@
+#! /bin/bash
+
+set -e
+
+# For now, avoid going past the 2038 32-bit clock rollover
+DAYS=$(( ( 0x7fffffff - $(date +%s) ) / 86400 - 1 ))
+
+key() {
+ local key=$1; shift
+
+ if [ ! -f "${key}.pem" ]; then
+ openssl genpkey \
+ -paramfile <(openssl ecparam -name prime256v1) \
+ -out "${key}.pem"
+ fi
+}
+
+req() {
+ local key=$1; shift
+ local dn=$1; shift
+
+ openssl req -new -sha256 -key "${key}.pem" \
+ -config <(printf "[req]\n%s\n%s\n[dn]\nCN_default=foo\n" \
+ "prompt = yes" "distinguished_name = dn") \
+ -subj "${dn}"
+}
+
+cert() {
+ local cert=$1; shift
+ local exts=$1; shift
+
+ openssl x509 -req -sha256 -out "${cert}.pem" \
+ -extfile <(printf "%s\n" "$exts") "$@"
+}
+
+genroot() {
+ local dn=$1; shift
+ local key=$1; shift
+ local cert=$1; shift
+
+ exts=$(printf "%s\n%s\n%s\n%s\n" \
+ "subjectKeyIdentifier = hash" \
+ "authorityKeyIdentifier = keyid" \
+ "basicConstraints = CA:true" \
+ "keyUsage = keyCertSign, cRLSign" )
+ key "$key"; req "$key" "$dn" |
+ cert "$cert" "$exts" -signkey "${key}.pem" \
+ -set_serial 1 -days "${DAYS}"
+}
+
+genee() {
+ local dn=$1; shift
+ local key=$1; shift
+ local cert=$1; shift
+ local cakey=$1; shift
+ local cacert=$1; shift
+
+ exts=$(printf "%s\n%s\n%s\n%s\n" \
+ "subjectKeyIdentifier = hash" \
+ "authorityKeyIdentifier = keyid, issuer" \
+ "basicConstraints = CA:false" \
+ "keyUsage = digitalSignature, keyEncipherment, dataEncipherment" \
+ )
+ key "$key"; req "$key" "$dn" |
+ cert "$cert" "$exts" -CA "${cacert}.pem" -CAkey "${cakey}.pem" \
+ -set_serial 2 -days "${DAYS}" "$@"
+}
+
+
+genroot "/C=SE/O=Heimdal/CN=CA secp256r1" \
+ secp256r1TestCA.key secp256r1TestCA.cert
+genee "/C=SE/O=Heimdal/CN=Server" \
+ secp256r2TestServer.key secp256r2TestServer.cert \
+ secp256r1TestCA.key secp256r1TestCA.cert
+genee "/C=SE/O=Heimdal/CN=Client" \
+ secp256r2TestClient.key secp256r2TestClient.cert \
+ secp256r1TestCA.key secp256r1TestCA.cert
+
+cat secp256r1TestCA.key.pem secp256r1TestCA.cert.pem > \
+ secp256r1TestCA.pem
+cat secp256r2TestClient.cert.pem secp256r2TestClient.key.pem > \
+ secp256r2TestClient.pem
+cat secp256r2TestServer.cert.pem secp256r2TestServer.key.pem > \
+ secp256r2TestServer.pem
diff --git a/lib/hx509/data/nist-result2 b/lib/hx509/data/nist-result2
new file mode 100644
index 000000000000..93a22e7aea75
--- /dev/null
+++ b/lib/hx509/data/nist-result2
@@ -0,0 +1,31 @@
+# $Id$
+# id FAIL
+4.2.8 EITHER depeneds on if time_t is 64 bit or not
+4.3.5 FAIL
+4.4.13 EITHER depeneds on if time_t is 64 bit or not
+4.5.1 FAIL
+4.5.4 FAIL
+4.5.6 FAIL
+4.6.15 FAIL
+4.6.17 FAIL
+4.11.2 FAIL
+4.12.2 FAIL
+4.13.19 FAIL
+4.13.21 FAIL
+4.13.23 FAIL
+4.13.26 FAIL
+4.13.27 FAIL
+4.13.30 FAIL
+4.13.33 FAIL
+4.13.34 FAIL
+4.13.37 FAIL
+4.14.1 FAIL
+4.14.4 FAIL
+4.14.5 FAIL
+4.14.7 FAIL
+4.14.13 FAIL
+4.14.18 FAIL
+4.14.19 FAIL
+4.15.4 FAIL
+4.15.5 FAIL
+4.16.2 FAIL
diff --git a/lib/hx509/data/openssl.cnf b/lib/hx509/data/openssl.cnf
index a6054009d955..b0146564592a 100644
--- a/lib/hx509/data/openssl.cnf
+++ b/lib/hx509/data/openssl.cnf
@@ -82,7 +82,7 @@ certs = .
[req]
distinguished_name = req_distinguished_name
-x509_extensions = v3_ca # The extentions to add to the self signed cert
+x509_extensions = v3_ca # The extensions to add to the self signed cert
string_mask = utf8only
diff --git a/lib/hx509/data/secp160r1TestCA.cert.pem b/lib/hx509/data/secp160r1TestCA.cert.pem
deleted file mode 100644
index 2d30fab2c608..000000000000
--- a/lib/hx509/data/secp160r1TestCA.cert.pem
+++ /dev/null
@@ -1,12 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBxjCCAYagAwIBAgIJAKjMYS/6EOLdMAkGByqGSM49BAEwNjELMAkGA1UEBhMC
-U0UxEDAOBgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMTAeFw0w
-OTAyMTQxNzUwMDRaFw0yMDAxMjgxNzUwMDRaMDYxCzAJBgNVBAYTAlNFMRAwDgYD
-VQQKEwdIZWltZGFsMRUwEwYDVQQDEwxDQSBzZWNwMTYwcjEwPjAQBgcqhkjOPQIB
-BgUrgQQACAMqAASMHokF13aCVrlhMSr9Vgofj7loM2a7ZrU3h8/j1n/cO24ceyN/
-DpsOo4GYMIGVMB0GA1UdDgQWBBS58EWwgNdBwkYVhUSNzwIehHhEDzBmBgNVHSME
-XzBdgBS58EWwgNdBwkYVhUSNzwIehHhED6E6pDgwNjELMAkGA1UEBhMCU0UxEDAO
-BgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMYIJAKjMYS/6EOLd
-MAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0EAQMvADAsAhRZPKbqMYDATJasRcXQfEh5
-8oHCywIUGZ0h6FqSvPgpkZ7hoU+ZEFJ/D88=
------END CERTIFICATE-----
diff --git a/lib/hx509/data/secp160r1TestCA.key.pem b/lib/hx509/data/secp160r1TestCA.key.pem
deleted file mode 100644
index f0ce773cf8ac..000000000000
--- a/lib/hx509/data/secp160r1TestCA.key.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MFACAQEEFHegiSlX0311KSBjNrbzq6HrKjkYoAcGBSuBBAAIoSwDKgAEjB6JBdd2
-gla5YTEq/VYKH4+5aDNmu2a1N4fP49Z/3DtuHHsjfw6bDg==
------END EC PRIVATE KEY-----
diff --git a/lib/hx509/data/secp160r1TestCA.pem b/lib/hx509/data/secp160r1TestCA.pem
deleted file mode 100644
index a6f068d5476a..000000000000
--- a/lib/hx509/data/secp160r1TestCA.pem
+++ /dev/null
@@ -1,18 +0,0 @@
-issuer= /C=SE/O=Heimdal/CN=CA secp160r1
-subject= /C=SE/O=Heimdal/CN=CA secp160r1
------BEGIN CERTIFICATE-----
-MIIBxjCCAYagAwIBAgIJAKjMYS/6EOLdMAkGByqGSM49BAEwNjELMAkGA1UEBhMC
-U0UxEDAOBgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMTAeFw0w
-OTAyMTQxNzUwMDRaFw0yMDAxMjgxNzUwMDRaMDYxCzAJBgNVBAYTAlNFMRAwDgYD
-VQQKEwdIZWltZGFsMRUwEwYDVQQDEwxDQSBzZWNwMTYwcjEwPjAQBgcqhkjOPQIB
-BgUrgQQACAMqAASMHokF13aCVrlhMSr9Vgofj7loM2a7ZrU3h8/j1n/cO24ceyN/
-DpsOo4GYMIGVMB0GA1UdDgQWBBS58EWwgNdBwkYVhUSNzwIehHhEDzBmBgNVHSME
-XzBdgBS58EWwgNdBwkYVhUSNzwIehHhED6E6pDgwNjELMAkGA1UEBhMCU0UxEDAO
-BgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMYIJAKjMYS/6EOLd
-MAwGA1UdEwQFMAMBAf8wCQYHKoZIzj0EAQMvADAsAhRZPKbqMYDATJasRcXQfEh5
-8oHCywIUGZ0h6FqSvPgpkZ7hoU+ZEFJ/D88=
------END CERTIFICATE-----
------BEGIN EC PRIVATE KEY-----
-MFACAQEEFHegiSlX0311KSBjNrbzq6HrKjkYoAcGBSuBBAAIoSwDKgAEjB6JBdd2
-gla5YTEq/VYKH4+5aDNmu2a1N4fP49Z/3DtuHHsjfw6bDg==
------END EC PRIVATE KEY-----
diff --git a/lib/hx509/data/secp160r2TestClient.cert.pem b/lib/hx509/data/secp160r2TestClient.cert.pem
deleted file mode 100644
index 716395bf8e1e..000000000000
--- a/lib/hx509/data/secp160r2TestClient.cert.pem
+++ /dev/null
@@ -1,9 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBIDCB4AIJAN1XzNknE3lDMAkGByqGSM49BAEwNjELMAkGA1UEBhMCU0UxEDAO
-BgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMTAeFw0wOTAyMTQx
-NzUwMDRaFw0yMDAxMjgxNzUwMDRaMDAxCzAJBgNVBAYTAlNFMRAwDgYDVQQKEwdI
-ZWltZGFsMQ8wDQYDVQQDEwZDbGllbnQwPjAQBgcqhkjOPQIBBgUrgQQAHgMqAASA
-oVzj3A0W1FaSmc0NwTRdX4A8eCbDb6pf07vMpUcOqvdXVGwWN3HhMAkGByqGSM49
-BAEDMAAwLQIURJ9Jdesm0rqwpOAn8K23GdWlCkYCFQDmJtqiOLs4jjUUP6T7O17M
-Iwyrvg==
------END CERTIFICATE-----
diff --git a/lib/hx509/data/secp160r2TestClient.key.pem b/lib/hx509/data/secp160r2TestClient.key.pem
deleted file mode 100644
index e5a2fef7941b..000000000000
--- a/lib/hx509/data/secp160r2TestClient.key.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MFACAQEEFNz0QJPbDlTBMSOfUoxNSzOOpRKyoAcGBSuBBAAeoSwDKgAEgKFc49wN
-FtRWkpnNDcE0XV+APHgmw2+qX9O7zKVHDqr3V1RsFjdx4Q==
------END EC PRIVATE KEY-----
diff --git a/lib/hx509/data/secp160r2TestClient.pem b/lib/hx509/data/secp160r2TestClient.pem
deleted file mode 100644
index ca4bb331d767..000000000000
--- a/lib/hx509/data/secp160r2TestClient.pem
+++ /dev/null
@@ -1,15 +0,0 @@
-issuer= /C=SE/O=Heimdal/CN=CA secp160r1
-subject= /C=SE/O=Heimdal/CN=Client
------BEGIN CERTIFICATE-----
-MIIBIDCB4AIJAN1XzNknE3lDMAkGByqGSM49BAEwNjELMAkGA1UEBhMCU0UxEDAO
-BgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMTAeFw0wOTAyMTQx
-NzUwMDRaFw0yMDAxMjgxNzUwMDRaMDAxCzAJBgNVBAYTAlNFMRAwDgYDVQQKEwdI
-ZWltZGFsMQ8wDQYDVQQDEwZDbGllbnQwPjAQBgcqhkjOPQIBBgUrgQQAHgMqAASA
-oVzj3A0W1FaSmc0NwTRdX4A8eCbDb6pf07vMpUcOqvdXVGwWN3HhMAkGByqGSM49
-BAEDMAAwLQIURJ9Jdesm0rqwpOAn8K23GdWlCkYCFQDmJtqiOLs4jjUUP6T7O17M
-Iwyrvg==
------END CERTIFICATE-----
------BEGIN EC PRIVATE KEY-----
-MFACAQEEFNz0QJPbDlTBMSOfUoxNSzOOpRKyoAcGBSuBBAAeoSwDKgAEgKFc49wN
-FtRWkpnNDcE0XV+APHgmw2+qX9O7zKVHDqr3V1RsFjdx4Q==
------END EC PRIVATE KEY-----
diff --git a/lib/hx509/data/secp160r2TestServer.cert.pem b/lib/hx509/data/secp160r2TestServer.cert.pem
deleted file mode 100644
index 6b56036583cb..000000000000
--- a/lib/hx509/data/secp160r2TestServer.cert.pem
+++ /dev/null
@@ -1,9 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIBIDCB4AIJAN1XzNknE3lCMAkGByqGSM49BAEwNjELMAkGA1UEBhMCU0UxEDAO
-BgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMTAeFw0wOTAyMTQx
-NzUwMDRaFw0yMDAxMjgxNzUwMDRaMDAxCzAJBgNVBAYTAlNFMRAwDgYDVQQKEwdI
-ZWltZGFsMQ8wDQYDVQQDEwZTZXJ2ZXIwPjAQBgcqhkjOPQIBBgUrgQQAHgMqAARA
-IUAwnwABnZAs378hcEgnk8efxE35RF6B+MmxSq1Twhp2C1ophD6yMAkGByqGSM49
-BAEDMAAwLQIVAO0hl59KWXRMBaJ2iKsiu/j73/bPAhRfsTT6SIBL5+3gjLhl7SqK
-1kTMQw==
------END CERTIFICATE-----
diff --git a/lib/hx509/data/secp160r2TestServer.key.pem b/lib/hx509/data/secp160r2TestServer.key.pem
deleted file mode 100644
index a903d0f76655..000000000000
--- a/lib/hx509/data/secp160r2TestServer.key.pem
+++ /dev/null
@@ -1,4 +0,0 @@
------BEGIN EC PRIVATE KEY-----
-MFACAQEEFBR1r2nPL1Ln1U5Nk1kW9XtNEkk1oAcGBSuBBAAeoSwDKgAEQCFAMJ8A
-AZ2QLN+/IXBIJ5PHn8RN+URegfjJsUqtU8IadgtaKYQ+sg==
------END EC PRIVATE KEY-----
diff --git a/lib/hx509/data/secp160r2TestServer.pem b/lib/hx509/data/secp160r2TestServer.pem
deleted file mode 100644
index 329d871534c7..000000000000
--- a/lib/hx509/data/secp160r2TestServer.pem
+++ /dev/null
@@ -1,15 +0,0 @@
-issuer= /C=SE/O=Heimdal/CN=CA secp160r1
-subject= /C=SE/O=Heimdal/CN=Server
------BEGIN CERTIFICATE-----
-MIIBIDCB4AIJAN1XzNknE3lCMAkGByqGSM49BAEwNjELMAkGA1UEBhMCU0UxEDAO
-BgNVBAoTB0hlaW1kYWwxFTATBgNVBAMTDENBIHNlY3AxNjByMTAeFw0wOTAyMTQx
-NzUwMDRaFw0yMDAxMjgxNzUwMDRaMDAxCzAJBgNVBAYTAlNFMRAwDgYDVQQKEwdI
-ZWltZGFsMQ8wDQYDVQQDEwZTZXJ2ZXIwPjAQBgcqhkjOPQIBBgUrgQQAHgMqAARA
-IUAwnwABnZAs378hcEgnk8efxE35RF6B+MmxSq1Twhp2C1ophD6yMAkGByqGSM49
-BAEDMAAwLQIVAO0hl59KWXRMBaJ2iKsiu/j73/bPAhRfsTT6SIBL5+3gjLhl7SqK
-1kTMQw==
------END CERTIFICATE-----
------BEGIN EC PRIVATE KEY-----
-MFACAQEEFBR1r2nPL1Ln1U5Nk1kW9XtNEkk1oAcGBSuBBAAeoSwDKgAEQCFAMJ8A
-AZ2QLN+/IXBIJ5PHn8RN+URegfjJsUqtU8IadgtaKYQ+sg==
------END EC PRIVATE KEY-----
diff --git a/lib/hx509/data/secp256r1TestCA.cert.pem b/lib/hx509/data/secp256r1TestCA.cert.pem
new file mode 100644
index 000000000000..6cac58ee11ff
--- /dev/null
+++ b/lib/hx509/data/secp256r1TestCA.cert.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBuDCCAV6gAwIBAgIBATAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UEChMHSGVpbWRhbDEVMBMGA1UEAxMMQ0Egc2VjcDI1NnIxMB4XDTE0MDMxMDE5
+NDAyM1oXDTM4MDExNzE5NDAyM1owNjELMAkGA1UEBhMCU0UxEDAOBgNVBAoTB0hl
+aW1kYWwxFTATBgNVBAMTDENBIHNlY3AyNTZyMTBZMBMGByqGSM49AgEGCCqGSM49
+AwEHA0IABOUrhSvioYJaKUK9WjI5eGRAWsOB2DNslTkcgTkFsd3vD4/dGxaHBOIM
+kuD9ldGK2sQArEIDKfrOHvP+oFz3jLajXTBbMB0GA1UdDgQWBBTrUd8AqGhfZvHV
+spcznXeb328JgzAfBgNVHSMEGDAWgBTrUd8AqGhfZvHVspcznXeb328JgzAMBgNV
+HRMEBTADAQH/MAsGA1UdDwQEAwIBBjAKBggqhkjOPQQDAgNIADBFAiBd6J2N4B6L
+mtn0ZP/6vOyPkA7YMq2EwbVyTGlnBTwYsQIhALjsLWHQVSkt08rly48ns93DeSbM
+XejBzmT8QXEdib+1
+-----END CERTIFICATE-----
diff --git a/lib/hx509/data/secp256r1TestCA.key.pem b/lib/hx509/data/secp256r1TestCA.key.pem
new file mode 100644
index 000000000000..388826621dc7
--- /dev/null
+++ b/lib/hx509/data/secp256r1TestCA.key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgL2N0gdHhAjBGcJ40
+gHePPMwGKygIVDXTfjysn9zPiSOhRANCAATlK4Ur4qGCWilCvVoyOXhkQFrDgdgz
+bJU5HIE5BbHd7w+P3RsWhwTiDJLg/ZXRitrEAKxCAyn6zh7z/qBc94y2
+-----END PRIVATE KEY-----
diff --git a/lib/hx509/data/secp256r1TestCA.pem b/lib/hx509/data/secp256r1TestCA.pem
new file mode 100644
index 000000000000..d0c7431880ae
--- /dev/null
+++ b/lib/hx509/data/secp256r1TestCA.pem
@@ -0,0 +1,17 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgL2N0gdHhAjBGcJ40
+gHePPMwGKygIVDXTfjysn9zPiSOhRANCAATlK4Ur4qGCWilCvVoyOXhkQFrDgdgz
+bJU5HIE5BbHd7w+P3RsWhwTiDJLg/ZXRitrEAKxCAyn6zh7z/qBc94y2
+-----END PRIVATE KEY-----
+-----BEGIN CERTIFICATE-----
+MIIBuDCCAV6gAwIBAgIBATAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UEChMHSGVpbWRhbDEVMBMGA1UEAxMMQ0Egc2VjcDI1NnIxMB4XDTE0MDMxMDE5
+NDAyM1oXDTM4MDExNzE5NDAyM1owNjELMAkGA1UEBhMCU0UxEDAOBgNVBAoTB0hl
+aW1kYWwxFTATBgNVBAMTDENBIHNlY3AyNTZyMTBZMBMGByqGSM49AgEGCCqGSM49
+AwEHA0IABOUrhSvioYJaKUK9WjI5eGRAWsOB2DNslTkcgTkFsd3vD4/dGxaHBOIM
+kuD9ldGK2sQArEIDKfrOHvP+oFz3jLajXTBbMB0GA1UdDgQWBBTrUd8AqGhfZvHV
+spcznXeb328JgzAfBgNVHSMEGDAWgBTrUd8AqGhfZvHVspcznXeb328JgzAMBgNV
+HRMEBTADAQH/MAsGA1UdDwQEAwIBBjAKBggqhkjOPQQDAgNIADBFAiBd6J2N4B6L
+mtn0ZP/6vOyPkA7YMq2EwbVyTGlnBTwYsQIhALjsLWHQVSkt08rly48ns93DeSbM
+XejBzmT8QXEdib+1
+-----END CERTIFICATE-----
diff --git a/lib/hx509/data/secp256r2TestClient.cert.pem b/lib/hx509/data/secp256r2TestClient.cert.pem
new file mode 100644
index 000000000000..f0f3a2445c55
--- /dev/null
+++ b/lib/hx509/data/secp256r2TestClient.cert.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBrzCCAVWgAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UEChMHSGVpbWRhbDEVMBMGA1UEAxMMQ0Egc2VjcDI1NnIxMB4XDTE0MDMxMDE5
+NDAyM1oXDTM4MDExNzE5NDAyM1owMDELMAkGA1UEBhMCU0UxEDAOBgNVBAoTB0hl
+aW1kYWwxDzANBgNVBAMTBkNsaWVudDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BO7/MCIBHf8gQLQ5ltp1uyCOCAw8uylZZ7+v/rB3oKHuAIyL6q/QjZXZH3FR5VcI
+zANavN5SAfx9CFJpPk+pUISjWjBYMB0GA1UdDgQWBBSjXg4X3fs5xOQgTumjZQwF
+I13RejAfBgNVHSMEGDAWgBTrUd8AqGhfZvHVspcznXeb328JgzAJBgNVHRMEAjAA
+MAsGA1UdDwQEAwIEsDAKBggqhkjOPQQDAgNIADBFAiAa9d6aCxlioep3ViYqujWv
+A28/16yXOrmLY1a2wcj3awIhAMeVjMiUTP/U4yXfb3uJjJmq8hfyNZ/CAiTQKORx
+JjIt
+-----END CERTIFICATE-----
diff --git a/lib/hx509/data/secp256r2TestClient.key.pem b/lib/hx509/data/secp256r2TestClient.key.pem
new file mode 100644
index 000000000000..36c67f9db6f2
--- /dev/null
+++ b/lib/hx509/data/secp256r2TestClient.key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6oD5CbNzN7oAWqcq
+dKJKw2WU5EwnUV05+7S9gXgeW/qhRANCAATu/zAiAR3/IEC0OZbadbsgjggMPLsp
+WWe/r/6wd6Ch7gCMi+qv0I2V2R9xUeVXCMwDWrzeUgH8fQhSaT5PqVCE
+-----END PRIVATE KEY-----
diff --git a/lib/hx509/data/secp256r2TestClient.pem b/lib/hx509/data/secp256r2TestClient.pem
new file mode 100644
index 000000000000..acf11b0413ff
--- /dev/null
+++ b/lib/hx509/data/secp256r2TestClient.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIBrzCCAVWgAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UEChMHSGVpbWRhbDEVMBMGA1UEAxMMQ0Egc2VjcDI1NnIxMB4XDTE0MDMxMDE5
+NDAyM1oXDTM4MDExNzE5NDAyM1owMDELMAkGA1UEBhMCU0UxEDAOBgNVBAoTB0hl
+aW1kYWwxDzANBgNVBAMTBkNsaWVudDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BO7/MCIBHf8gQLQ5ltp1uyCOCAw8uylZZ7+v/rB3oKHuAIyL6q/QjZXZH3FR5VcI
+zANavN5SAfx9CFJpPk+pUISjWjBYMB0GA1UdDgQWBBSjXg4X3fs5xOQgTumjZQwF
+I13RejAfBgNVHSMEGDAWgBTrUd8AqGhfZvHVspcznXeb328JgzAJBgNVHRMEAjAA
+MAsGA1UdDwQEAwIEsDAKBggqhkjOPQQDAgNIADBFAiAa9d6aCxlioep3ViYqujWv
+A28/16yXOrmLY1a2wcj3awIhAMeVjMiUTP/U4yXfb3uJjJmq8hfyNZ/CAiTQKORx
+JjIt
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg6oD5CbNzN7oAWqcq
+dKJKw2WU5EwnUV05+7S9gXgeW/qhRANCAATu/zAiAR3/IEC0OZbadbsgjggMPLsp
+WWe/r/6wd6Ch7gCMi+qv0I2V2R9xUeVXCMwDWrzeUgH8fQhSaT5PqVCE
+-----END PRIVATE KEY-----
diff --git a/lib/hx509/data/secp256r2TestServer.cert.pem b/lib/hx509/data/secp256r2TestServer.cert.pem
new file mode 100644
index 000000000000..91acde8f60e5
--- /dev/null
+++ b/lib/hx509/data/secp256r2TestServer.cert.pem
@@ -0,0 +1,12 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVWgAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UEChMHSGVpbWRhbDEVMBMGA1UEAxMMQ0Egc2VjcDI1NnIxMB4XDTE0MDMxMDE5
+NDAyM1oXDTM4MDExNzE5NDAyM1owMDELMAkGA1UEBhMCU0UxEDAOBgNVBAoTB0hl
+aW1kYWwxDzANBgNVBAMTBlNlcnZlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BItZgn1C8ZBvKkkNoEofWL0JLCTaHT2lJj7d9jRtSKiR2PlOtd5HhteDqP78K4eg
+lRMk5nqsmEooalfbNsFBy8SjWjBYMB0GA1UdDgQWBBTqMDTOezcRsax6lf6E/Xk+
+QzPorjAfBgNVHSMEGDAWgBTrUd8AqGhfZvHVspcznXeb328JgzAJBgNVHRMEAjAA
+MAsGA1UdDwQEAwIEsDAKBggqhkjOPQQDAgNJADBGAiEAsvf//YdUWCD6OLZesENa
+1mH8+b+kZDR6jx1JchRXAEQCIQDkTvTZrlmmxUaWEsf08/4xbxkYbrPAg4+VX2uI
+QcEwUA==
+-----END CERTIFICATE-----
diff --git a/lib/hx509/data/secp256r2TestServer.key.pem b/lib/hx509/data/secp256r2TestServer.key.pem
new file mode 100644
index 000000000000..fb57e798c73e
--- /dev/null
+++ b/lib/hx509/data/secp256r2TestServer.key.pem
@@ -0,0 +1,5 @@
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKo/47DaveCl90GxH
+LCE7IGBua2XsE+jI4RUWZrqjhBGhRANCAASLWYJ9QvGQbypJDaBKH1i9CSwk2h09
+pSY+3fY0bUiokdj5TrXeR4bXg6j+/CuHoJUTJOZ6rJhKKGpX2zbBQcvE
+-----END PRIVATE KEY-----
diff --git a/lib/hx509/data/secp256r2TestServer.pem b/lib/hx509/data/secp256r2TestServer.pem
new file mode 100644
index 000000000000..0e9edd782bcf
--- /dev/null
+++ b/lib/hx509/data/secp256r2TestServer.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIIBsDCCAVWgAwIBAgIBAjAKBggqhkjOPQQDAjA2MQswCQYDVQQGEwJTRTEQMA4G
+A1UEChMHSGVpbWRhbDEVMBMGA1UEAxMMQ0Egc2VjcDI1NnIxMB4XDTE0MDMxMDE5
+NDAyM1oXDTM4MDExNzE5NDAyM1owMDELMAkGA1UEBhMCU0UxEDAOBgNVBAoTB0hl
+aW1kYWwxDzANBgNVBAMTBlNlcnZlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
+BItZgn1C8ZBvKkkNoEofWL0JLCTaHT2lJj7d9jRtSKiR2PlOtd5HhteDqP78K4eg
+lRMk5nqsmEooalfbNsFBy8SjWjBYMB0GA1UdDgQWBBTqMDTOezcRsax6lf6E/Xk+
+QzPorjAfBgNVHSMEGDAWgBTrUd8AqGhfZvHVspcznXeb328JgzAJBgNVHRMEAjAA
+MAsGA1UdDwQEAwIEsDAKBggqhkjOPQQDAgNJADBGAiEAsvf//YdUWCD6OLZesENa
+1mH8+b+kZDR6jx1JchRXAEQCIQDkTvTZrlmmxUaWEsf08/4xbxkYbrPAg4+VX2uI
+QcEwUA==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKo/47DaveCl90GxH
+LCE7IGBua2XsE+jI4RUWZrqjhBGhRANCAASLWYJ9QvGQbypJDaBKH1i9CSwk2h09
+pSY+3fY0bUiokdj5TrXeR4bXg6j+/CuHoJUTJOZ6rJhKKGpX2zbBQcvE
+-----END PRIVATE KEY-----
diff --git a/lib/hx509/doxygen.c b/lib/hx509/doxygen.c
index 0c7dd780aedb..a6d3d9ca0ab1 100644
--- a/lib/hx509/doxygen.c
+++ b/lib/hx509/doxygen.c
@@ -82,4 +82,4 @@
* See the @ref page_ca for description and examples. */
/** @defgroup hx509_peer hx509 certificate selecting functions */
/** @defgroup hx509_print hx509 printing functions */
-/** @defgroup hx509_env hx509 enviroment functions */
+/** @defgroup hx509_env hx509 environment functions */
diff --git a/lib/hx509/env.c b/lib/hx509/env.c
index 7598aebaae74..70969504b3a8 100644
--- a/lib/hx509/env.c
+++ b/lib/hx509/env.c
@@ -34,7 +34,7 @@
#include "hx_locl.h"
/**
- * @page page_env Hx509 enviroment functions
+ * @page page_env Hx509 environment functions
*
* See the library functions here: @ref hx509_env
*/
@@ -43,7 +43,7 @@
* Add a new key/value pair to the hx509_env.
*
* @param context A hx509 context.
- * @param env enviroment to add the enviroment variable too.
+ * @param env environment to add the environment variable too.
* @param key key to add
* @param value value to add
*
@@ -94,7 +94,7 @@ hx509_env_add(hx509_context context, hx509_env *env,
* Add a new key/binding pair to the hx509_env.
*
* @param context A hx509 context.
- * @param env enviroment to add the enviroment variable too.
+ * @param env environment to add the environment variable too.
* @param key key to add
* @param list binding list to add
*
@@ -141,7 +141,7 @@ hx509_env_add_binding(hx509_context context, hx509_env *env,
* Search the hx509_env for a length based key.
*
* @param context A hx509 context.
- * @param env enviroment to add the enviroment variable too.
+ * @param env environment to add the environment variable too.
* @param key key to search for.
* @param len length of key.
*
@@ -167,7 +167,7 @@ hx509_env_lfind(hx509_context context, hx509_env env,
* Search the hx509_env for a key.
*
* @param context A hx509 context.
- * @param env enviroment to add the enviroment variable too.
+ * @param env environment to add the environment variable too.
* @param key key to search for.
*
* @return the value if the key is found, NULL otherwise.
@@ -190,7 +190,7 @@ hx509_env_find(hx509_context context, hx509_env env, const char *key)
* Search the hx509_env for a binding.
*
* @param context A hx509 context.
- * @param env enviroment to add the enviroment variable too.
+ * @param env environment to add the environment variable too.
* @param key key to search for.
*
* @return the binding if the key is found, NULL if not found.
@@ -229,9 +229,9 @@ env_free(hx509_env b)
}
/**
- * Free an hx509_env enviroment context.
+ * Free an hx509_env environment context.
*
- * @param env the enviroment to free.
+ * @param env the environment to free.
*
* @ingroup hx509_env
*/
diff --git a/lib/hx509/error.c b/lib/hx509/error.c
index fc3cf90b3255..be09414bfffa 100644
--- a/lib/hx509/error.c
+++ b/lib/hx509/error.c
@@ -45,17 +45,6 @@ struct hx509_error_data {
char *msg;
};
-static void
-free_error_string(hx509_error msg)
-{
- while(msg) {
- hx509_error m2 = msg->next;
- free(msg->msg);
- free(msg);
- msg = m2;
- }
-}
-
/**
* Resets the error strings the hx509 context.
*
@@ -68,7 +57,7 @@ void
hx509_clear_error_string(hx509_context context)
{
if (context) {
- free_error_string(context->error);
+ heim_release(context->error);
context->error = NULL;
}
}
@@ -91,31 +80,18 @@ void
hx509_set_error_stringv(hx509_context context, int flags, int code,
const char *fmt, va_list ap)
{
- hx509_error msg;
+ heim_error_t msg;
if (context == NULL)
return;
- msg = calloc(1, sizeof(*msg));
- if (msg == NULL) {
- hx509_clear_error_string(context);
- return;
- }
-
- if (vasprintf(&msg->msg, fmt, ap) == -1) {
- hx509_clear_error_string(context);
- free(msg);
- return;
- }
- msg->code = code;
-
- if (flags & HX509_ERROR_APPEND) {
- msg->next = context->error;
- context->error = msg;
- } else {
- free_error_string(context->error);
- context->error = msg;
+ msg = heim_error_createv(code, fmt, ap);
+ if (msg) {
+ if (flags & HX509_ERROR_APPEND)
+ heim_error_append(msg, context->error);
+ heim_release(context->error);
}
+ context->error = msg;
}
/**
@@ -157,12 +133,12 @@ hx509_set_error_string(hx509_context context, int flags, int code,
char *
hx509_get_error_string(hx509_context context, int error_code)
{
- struct rk_strpool *p = NULL;
- hx509_error msg = context->error;
+ heim_error_t msg = context->error;
+ heim_string_t s;
+ char *str = NULL;
- if (msg == NULL || msg->code != error_code) {
+ if (msg == NULL || heim_error_get_code(msg) != error_code) {
const char *cstr;
- char *str;
cstr = com_right(context->et_list, error_code);
if (cstr)
@@ -175,11 +151,14 @@ hx509_get_error_string(hx509_context context, int error_code)
return str;
}
- for (msg = context->error; msg; msg = msg->next)
- p = rk_strpoolprintf(p, "%s%s", msg->msg,
- msg->next != NULL ? "; " : "");
-
- return rk_strpoolcollect(p);
+ s = heim_error_copy_string(msg);
+ if (s) {
+ const char *cstr = heim_string_get_utf8(s);
+ if (cstr)
+ str = strdup(cstr);
+ heim_release(s);
+ }
+ return str;
}
/**
@@ -215,13 +194,14 @@ hx509_err(hx509_context context, int exit_code,
va_list ap;
const char *msg;
char *str;
+ int ret;
va_start(ap, fmt);
- vasprintf(&str, fmt, ap);
+ ret = vasprintf(&str, fmt, ap);
va_end(ap);
msg = hx509_get_error_string(context, error_code);
if (msg == NULL)
msg = "no error";
- errx(exit_code, "%s: %s", str, msg);
+ errx(exit_code, "%s: %s", ret != -1 ? str : "ENOMEM", msg);
}
diff --git a/lib/hx509/file.c b/lib/hx509/file.c
index 4f7e87f070ae..5401af7a0f43 100644
--- a/lib/hx509/file.c
+++ b/lib/hx509/file.c
@@ -98,7 +98,7 @@ hx509_pem_write(hx509_context context, const char *type,
if (length > ENCODE_LINE_LENGTH)
length = ENCODE_LINE_LENGTH;
- l = base64_encode(p, length, &line);
+ l = rk_base64_encode(p, length, &line);
if (l < 0) {
hx509_set_error_string(context, 0, ENOMEM,
"malloc - out of memory");
@@ -255,7 +255,7 @@ hx509_pem_read(hx509_context context,
}
p = emalloc(i);
- i = base64_decode(buf, p);
+ i = rk_base64_decode(buf, p);
if (i < 0) {
free(p);
goto out;
diff --git a/lib/hx509/hx509-private.h b/lib/hx509/hx509-private.h
index 60891f27fca6..72d3bbdfa748 100644
--- a/lib/hx509/hx509-private.h
+++ b/lib/hx509/hx509-private.h
@@ -30,7 +30,7 @@ void
_hx509_abort (
const char */*fmt*/,
...)
- __attribute__ ((noreturn, format (printf, 1, 2)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 1, 2)));
int
_hx509_calculate_path (
@@ -193,6 +193,9 @@ _hx509_find_extension_subject_key_id (
const Certificate */*issuer*/,
SubjectKeyIdentifier */*si*/);
+const struct signature_alg *
+_hx509_find_sig_alg (const heim_oid */*oid*/);
+
int
_hx509_generate_private_key (
hx509_context /*context*/,
@@ -333,6 +336,9 @@ _hx509_pi_printf (
const char */*fmt*/,
...);
+void
+_hx509_private_eckey_free (void */*eckey*/);
+
int
_hx509_private_key_export (
hx509_context /*context*/,
@@ -440,10 +446,16 @@ _hx509_set_cert_attribute (
const heim_octet_string */*attr*/);
int
-_hx509_signature_best_before (
+_hx509_set_digest_alg (
+ DigestAlgorithmIdentifier */*id*/,
+ const heim_oid */*oid*/,
+ const void */*param*/,
+ size_t /*length*/);
+
+int
+_hx509_signature_is_weak (
hx509_context /*context*/,
- const AlgorithmIdentifier */*alg*/,
- time_t /*t*/);
+ const AlgorithmIdentifier */*alg*/);
void
_hx509_unmap_file_os (heim_octet_string */*os*/);
diff --git a/lib/hx509/hx509-protos.h b/lib/hx509/hx509-protos.h
index d03c7767e541..8de7a67ab7df 100644
--- a/lib/hx509/hx509-protos.h
+++ b/lib/hx509/hx509-protos.h
@@ -1,6 +1,7 @@
/* This is a generated file */
#ifndef __hx509_protos_h__
#define __hx509_protos_h__
+#ifndef DOXY
#include <stdarg.h>
@@ -21,12 +22,47 @@ extern "C" {
#endif
#endif
#endif
+/**
+ * Print a bitstring using a hx509_vprint_func function. To print to
+ * stdout use hx509_print_stdout().
+ *
+ * @param b bit string to print.
+ * @param func hx509_vprint_func to print with.
+ * @param ctx context variable to hx509_vprint_func function.
+ *
+ * @ingroup hx509_print
+ */
+
void
hx509_bitstring_print (
const heim_bit_string */*b*/,
hx509_vprint_func /*func*/,
void */*ctx*/);
+/**
+ * Sign a to-be-signed certificate object with a issuer certificate.
+ *
+ * The caller needs to at least have called the following functions on the
+ * to-be-signed certificate object:
+ * - hx509_ca_tbs_init()
+ * - hx509_ca_tbs_set_subject()
+ * - hx509_ca_tbs_set_spki()
+ *
+ * When done the to-be-signed certificate object should be freed with
+ * hx509_ca_tbs_free().
+ *
+ * When creating self-signed certificate use hx509_ca_sign_self() instead.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param signer the CA certificate object to sign with (need private key).
+ * @param certificate return cerificate, free with hx509_cert_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_sign (
hx509_context /*context*/,
@@ -34,6 +70,19 @@ hx509_ca_sign (
hx509_cert /*signer*/,
hx509_cert */*certificate*/);
+/**
+ * Work just like hx509_ca_sign() but signs it-self.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param signer private key to sign with.
+ * @param certificate return cerificate, free with hx509_cert_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_sign_self (
hx509_context /*context*/,
@@ -41,6 +90,20 @@ hx509_ca_sign_self (
hx509_private_key /*signer*/,
hx509_cert */*certificate*/);
+/**
+ * Add CRL distribution point URI to the to-be-signed certificate
+ * object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param uri uri to the CRL.
+ * @param issuername name of the issuer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_crl_dp_uri (
hx509_context /*context*/,
@@ -48,30 +111,99 @@ hx509_ca_tbs_add_crl_dp_uri (
const char */*uri*/,
hx509_name /*issuername*/);
+/**
+ * An an extended key usage to the to-be-signed certificate object.
+ * Duplicates will detected and not added.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid extended key usage to add.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_eku (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const heim_oid */*oid*/);
+/**
+ * Add a Subject Alternative Name hostname to to-be-signed certificate
+ * object. A domain match starts with ., an exact match does not.
+ *
+ * Example of a an domain match: .domain.se matches the hostname
+ * host.domain.se.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param dnsname a hostame.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_san_hostname (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const char */*dnsname*/);
+/**
+ * Add a Jabber/XMPP jid Subject Alternative Name to the to-be-signed
+ * certificate object. The jid is an UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param jid string of an a jabber id in UTF8.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_san_jid (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const char */*jid*/);
+/**
+ * Add Microsoft UPN Subject Alternative Name to the to-be-signed
+ * certificate object. The principal string is a UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param principal Microsoft UPN string.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_san_ms_upn (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const char */*principal*/);
+/**
+ * Add Subject Alternative Name otherName to the to-be-signed
+ * certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param oid the oid of the OtherName.
+ * @param os data in the other name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_san_otherName (
hx509_context /*context*/,
@@ -79,79 +211,273 @@ hx509_ca_tbs_add_san_otherName (
const heim_oid */*oid*/,
const heim_octet_string */*os*/);
+/**
+ * Add Kerberos Subject Alternative Name to the to-be-signed
+ * certificate object. The principal string is a UTF8 string.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param principal Kerberos principal to add to the certificate.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_san_pkinit (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const char */*principal*/);
+/**
+ * Add a Subject Alternative Name rfc822 (email address) to
+ * to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param rfc822Name a string to a email address.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_add_san_rfc822name (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const char */*rfc822Name*/);
+/**
+ * Free an To Be Signed object.
+ *
+ * @param tbs object to free.
+ *
+ * @ingroup hx509_ca
+ */
+
void
hx509_ca_tbs_free (hx509_ca_tbs */*tbs*/);
+/**
+ * Allocate an to-be-signed certificate object that will be converted
+ * into an certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs returned to-be-signed certicate object, free with
+ * hx509_ca_tbs_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_init (
hx509_context /*context*/,
hx509_ca_tbs */*tbs*/);
+/**
+ * Make the to-be-signed certificate object a CA certificate. If the
+ * pathLenConstraint is negative path length constraint is used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param pathLenConstraint path length constraint, negative, no
+ * constraint.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_ca (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
int /*pathLenConstraint*/);
+/**
+ * Make the to-be-signed certificate object a windows domain controller certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_domaincontroller (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/);
+/**
+ * Set the absolute time when the certificate is valid to.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param t time when the certificate will expire
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_notAfter (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
time_t /*t*/);
+/**
+ * Set the relative time when the certificiate is going to expire.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param delta seconds to the certificate is going to expire.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_notAfter_lifetime (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
time_t /*delta*/);
+/**
+ * Set the absolute time when the certificate is valid from. If not
+ * set the current time will be used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param t time the certificated will start to be valid
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_notBefore (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
time_t /*t*/);
+/**
+ * Make the to-be-signed certificate object a proxy certificate. If the
+ * pathLenConstraint is negative path length constraint is used.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param pathLenConstraint path length constraint, negative, no
+ * constraint.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_proxy (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
int /*pathLenConstraint*/);
+/**
+ * Set the serial number to use for to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param serialNumber serial number to use for the to-be-signed
+ * certificate object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_serialnumber (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const heim_integer */*serialNumber*/);
+/**
+ * Set signature algorithm on the to be signed certificate
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param sigalg signature algorithm to use
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
+int
+hx509_ca_tbs_set_signature_algorithm (
+ hx509_context /*context*/,
+ hx509_ca_tbs /*tbs*/,
+ const AlgorithmIdentifier */*sigalg*/);
+
+/**
+ * Set the subject public key info (SPKI) in the to-be-signed certificate
+ * object. SPKI is the public key and key related parameters in the
+ * certificate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param spki subject public key info to use for the to-be-signed certificate object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_spki (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
const SubjectPublicKeyInfo */*spki*/);
+/**
+ * Set the subject name of a to-be-signed certificate object.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param subject the name to set a subject.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_subject (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
hx509_name /*subject*/);
+/**
+ * Initialize the to-be-signed certificate object from a template certifiate.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param flags bit field selecting what to copy from the template
+ * certifiate.
+ * @param cert template certificate.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_template (
hx509_context /*context*/,
@@ -159,6 +485,24 @@ hx509_ca_tbs_set_template (
int /*flags*/,
hx509_cert /*cert*/);
+/**
+ * Set the issuerUniqueID and subjectUniqueID
+ *
+ * These are only supposed to be used considered with version 2
+ * certificates, replaced by the two extensions SubjectKeyIdentifier
+ * and IssuerKeyIdentifier. This function is to allow application
+ * using legacy protocol to issue them.
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param issuerUniqueID to be set
+ * @param subjectUniqueID to be set
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_set_unique (
hx509_context /*context*/,
@@ -166,21 +510,71 @@ hx509_ca_tbs_set_unique (
const heim_bit_string */*subjectUniqueID*/,
const heim_bit_string */*issuerUniqueID*/);
+/**
+ * Expand the the subject name in the to-be-signed certificate object
+ * using hx509_name_expand().
+ *
+ * @param context A hx509 context.
+ * @param tbs object to be signed.
+ * @param env environment variable to expand variables in the subject
+ * name, see hx509_env_init().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_ca
+ */
+
int
hx509_ca_tbs_subject_expand (
hx509_context /*context*/,
hx509_ca_tbs /*tbs*/,
hx509_env /*env*/);
+/**
+ * Make of template units, use to build flags argument to
+ * hx509_ca_tbs_set_template() with parse_units().
+ *
+ * @return an units structure.
+ *
+ * @ingroup hx509_ca
+ */
+
const struct units *
hx509_ca_tbs_template_units (void);
+/**
+ * Encodes the hx509 certificate as a DER encode binary.
+ *
+ * @param context A hx509 context.
+ * @param c the certificate to encode.
+ * @param os the encode certificate, set to NULL, 0 on case of
+ * error. Free the os->data with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_binary (
hx509_context /*context*/,
hx509_cert /*c*/,
heim_octet_string */*os*/);
+/**
+ * Check the extended key usage on the hx509 certificate.
+ *
+ * @param context A hx509 context.
+ * @param cert A hx509 context.
+ * @param eku the EKU to check for
+ * @param allow_any_eku if the any EKU is set, allow that to be a
+ * substitute.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_check_eku (
hx509_context /*context*/,
@@ -188,11 +582,40 @@ hx509_cert_check_eku (
const heim_oid */*eku*/,
int /*allow_any_eku*/);
+/**
+ * Compare to hx509 certificate object, useful for sorting.
+ *
+ * @param p a hx509 certificate object.
+ * @param q a hx509 certificate object.
+ *
+ * @return 0 the objects are the same, returns > 0 is p is "larger"
+ * then q, < 0 if p is "smaller" then q.
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_cmp (
hx509_cert /*p*/,
hx509_cert /*q*/);
+/**
+ * Return a list of subjectAltNames specified by oid in the
+ * certificate. On error the
+ *
+ * The returned list of octet string should be freed with
+ * hx509_free_octet_string_list().
+ *
+ * @param context A hx509 context.
+ * @param cert a hx509 certificate object.
+ * @param oid an oid to for SubjectAltName.
+ * @param list list of matching SubjectAltName.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_find_subjectAltName_otherName (
hx509_context /*context*/,
@@ -200,62 +623,223 @@ hx509_cert_find_subjectAltName_otherName (
const heim_oid */*oid*/,
hx509_octet_string_list */*list*/);
+/**
+ * Free reference to the hx509 certificate object, if the refcounter
+ * reaches 0, the object if freed. Its allowed to pass in NULL.
+ *
+ * @param cert the cert to free.
+ *
+ * @ingroup hx509_cert
+ */
+
void
hx509_cert_free (hx509_cert /*cert*/);
+/**
+ * Get the SubjectPublicKeyInfo structure from the hx509 certificate.
+ *
+ * @param context a hx509 context.
+ * @param p a hx509 certificate object.
+ * @param spki SubjectPublicKeyInfo, should be freed with
+ * free_SubjectPublicKeyInfo().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_SPKI (
hx509_context /*context*/,
hx509_cert /*p*/,
SubjectPublicKeyInfo */*spki*/);
+/**
+ * Get the AlgorithmIdentifier from the hx509 certificate.
+ *
+ * @param context a hx509 context.
+ * @param p a hx509 certificate object.
+ * @param alg AlgorithmIdentifier, should be freed with
+ * free_AlgorithmIdentifier(). The algorithmidentifier is
+ * typicly rsaEncryption, or id-ecPublicKey, or some other
+ * public key mechanism.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_SPKI_AlgorithmIdentifier (
hx509_context /*context*/,
hx509_cert /*p*/,
AlgorithmIdentifier */*alg*/);
+/**
+ * Get an external attribute for the certificate, examples are
+ * friendly name and id.
+ *
+ * @param cert hx509 certificate object to search
+ * @param oid an oid to search for.
+ *
+ * @return an hx509_cert_attribute, only valid as long as the
+ * certificate is referenced.
+ *
+ * @ingroup hx509_cert
+ */
+
hx509_cert_attribute
hx509_cert_get_attribute (
hx509_cert /*cert*/,
const heim_oid */*oid*/);
+/**
+ * Return the name of the base subject of the hx509 certificate. If
+ * the certiicate is a verified proxy certificate, the this function
+ * return the base certificate (root of the proxy chain). If the proxy
+ * certificate is not verified with the base certificate
+ * HX509_PROXY_CERTIFICATE_NOT_CANONICALIZED is returned.
+ *
+ * @param context a hx509 context.
+ * @param c a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free(). See also hx509_cert_get_subject().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_base_subject (
hx509_context /*context*/,
hx509_cert /*c*/,
hx509_name */*name*/);
+/**
+ * Get friendly name of the certificate.
+ *
+ * @param cert cert to get the friendly name from.
+ *
+ * @return an friendly name or NULL if there is. The friendly name is
+ * only valid as long as the certificate is referenced.
+ *
+ * @ingroup hx509_cert
+ */
+
const char *
hx509_cert_get_friendly_name (hx509_cert /*cert*/);
+/**
+ * Return the name of the issuer of the hx509 certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_issuer (
hx509_cert /*p*/,
hx509_name */*name*/);
+/**
+ * Get a copy of the Issuer Unique ID
+ *
+ * @param context a hx509_context
+ * @param p a hx509 certificate
+ * @param issuer the issuer id returned, free with der_free_bit_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string(). The
+ * error code HX509_EXTENSION_NOT_FOUND is returned if the certificate
+ * doesn't have a issuerUniqueID
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_issuer_unique_id (
hx509_context /*context*/,
hx509_cert /*p*/,
heim_bit_string */*issuer*/);
+/**
+ * Get notAfter time of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ *
+ * @return return not after time.
+ *
+ * @ingroup hx509_cert
+ */
+
time_t
hx509_cert_get_notAfter (hx509_cert /*p*/);
+/**
+ * Get notBefore time of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ *
+ * @return return not before time
+ *
+ * @ingroup hx509_cert
+ */
+
time_t
hx509_cert_get_notBefore (hx509_cert /*p*/);
+/**
+ * Get serial number of the certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param i serial number, should be freed ith der_free_heim_integer().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_serialnumber (
hx509_cert /*p*/,
heim_integer */*i*/);
+/**
+ * Return the name of the subject of the hx509 certificate.
+ *
+ * @param p a hx509 certificate object.
+ * @param name a pointer to a hx509 name, should be freed by
+ * hx509_name_free(). See also hx509_cert_get_base_subject().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_subject (
hx509_cert /*p*/,
hx509_name */*name*/);
+/**
+ * Get a copy of the Subect Unique ID
+ *
+ * @param context a hx509_context
+ * @param p a hx509 certificate
+ * @param subject the subject id returned, free with der_free_bit_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string(). The
+ * error code HX509_EXTENSION_NOT_FOUND is returned if the certificate
+ * doesn't have a subjectUniqueID
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_get_subject_unique_id (
hx509_context /*context*/,
@@ -265,18 +849,63 @@ hx509_cert_get_subject_unique_id (
int
hx509_cert_have_private_key (hx509_cert /*p*/);
-int
+/**
+ * Allocate and init an hx509 certificate object from the decoded
+ * certificate `c´.
+ *
+ * @param context A hx509 context.
+ * @param c
+ * @param error
+ *
+ * @return Returns an hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
+
+hx509_cert
hx509_cert_init (
hx509_context /*context*/,
const Certificate */*c*/,
- hx509_cert */*cert*/);
+ heim_error_t */*error*/);
+
+/**
+ * Just like hx509_cert_init(), but instead of a decode certificate
+ * takes an pointer and length to a memory region that contains a
+ * DER/BER encoded certificate.
+ *
+ * If the memory region doesn't contain just the certificate and
+ * nothing more the function will fail with
+ * HX509_EXTRA_DATA_AFTER_STRUCTURE.
+ *
+ * @param context A hx509 context.
+ * @param ptr pointer to memory region containing encoded certificate.
+ * @param len length of memory region.
+ * @param error possibly returns an error
+ *
+ * @return An hx509 certificate
+ *
+ * @ingroup hx509_cert
+ */
-int
+hx509_cert
hx509_cert_init_data (
hx509_context /*context*/,
const void */*ptr*/,
size_t /*len*/,
- hx509_cert */*cert*/);
+ heim_error_t */*error*/);
+
+/**
+ * Print certificate usage for a certificate to a string.
+ *
+ * @param context A hx509 context.
+ * @param c a certificate print the keyusage for.
+ * @param s the return string with the keysage printed in to, free
+ * with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
int
hx509_cert_keyusage_print (
@@ -292,20 +921,72 @@ hx509_cert_public_encrypt (
heim_oid */*encryption_oid*/,
heim_octet_string */*ciphertext*/);
+/**
+ * Add a reference to a hx509 certificate object.
+ *
+ * @param cert a pointer to an hx509 certificate object.
+ *
+ * @return the same object as is passed in.
+ *
+ * @ingroup hx509_cert
+ */
+
hx509_cert
hx509_cert_ref (hx509_cert /*cert*/);
+/**
+ * Set the friendly name on the certificate.
+ *
+ * @param cert The certificate to set the friendly name on
+ * @param name Friendly name.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_cert_set_friendly_name (
hx509_cert /*cert*/,
const char */*name*/);
+/**
+ * Add a certificate to the certificiate store.
+ *
+ * The receiving keyset certs will either increase reference counter
+ * of the cert or make a deep copy, either way, the caller needs to
+ * free the cert itself.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to add the certificate to.
+ * @param cert certificate to add.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_add (
hx509_context /*context*/,
hx509_certs /*certs*/,
hx509_cert /*cert*/);
+/**
+ * Same a hx509_certs_merge() but use a lock and name to describe the
+ * from source.
+ *
+ * @param context a hx509 context.
+ * @param to the store to merge into.
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ * @param name name of the source store
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_append (
hx509_context /*context*/,
@@ -313,12 +994,38 @@ hx509_certs_append (
hx509_lock /*lock*/,
const char */*name*/);
+/**
+ * End the iteration over certificates.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param cursor cursor that will keep track of progress, freed.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_end_seq (
hx509_context /*context*/,
hx509_certs /*certs*/,
hx509_cursor /*cursor*/);
+/**
+ * Filter certificate matching the query.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to search.
+ * @param q query allocated with @ref hx509_query functions.
+ * @param result the filtered certificate store, caller must free with
+ * hx509_certs_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_filter (
hx509_context /*context*/,
@@ -326,6 +1033,20 @@ hx509_certs_filter (
const hx509_query */*q*/,
hx509_certs */*result*/);
+/**
+ * Find a certificate matching the query.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to search.
+ * @param q query allocated with @ref hx509_query functions.
+ * @param r return certificate (or NULL on error), should be freed
+ * with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_find (
hx509_context /*context*/,
@@ -333,9 +1054,32 @@ hx509_certs_find (
const hx509_query */*q*/,
hx509_cert */*r*/);
+/**
+ * Free a certificate store.
+ *
+ * @param certs certificate store to free.
+ *
+ * @ingroup hx509_keyset
+ */
+
void
hx509_certs_free (hx509_certs */*certs*/);
+/**
+ * Print some info about the certificate store.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to print information about.
+ * @param func function that will get each line of the information, if
+ * NULL is used the data is printed on a FILE descriptor that should
+ * be passed in ctx, if ctx also is NULL, stdout is used.
+ * @param ctx parameter to func.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_info (
hx509_context /*context*/,
@@ -343,6 +1087,24 @@ hx509_certs_info (
int (*/*func*/)(void *, const char *),
void */*ctx*/);
+/**
+ * Open or creates a new hx509 certificate store.
+ *
+ * @param context A hx509 context
+ * @param name name of the store, format is TYPE:type-specific-string,
+ * if NULL is used the MEMORY store is used.
+ * @param flags list of flags:
+ * - HX509_CERTS_CREATE create a new keystore of the specific TYPE.
+ * - HX509_CERTS_UNPROTECT_ALL fails if any private key failed to be extracted.
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ * @param certs return pointer, free with hx509_certs_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_init (
hx509_context /*context*/,
@@ -351,6 +1113,21 @@ hx509_certs_init (
hx509_lock /*lock*/,
hx509_certs */*certs*/);
+/**
+ * Iterate over all certificates in a keystore and call a block
+ * for each of them.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param func block to call for each certificate. The function
+ * should return non-zero to abort the iteration, that value is passed
+ * back to the caller of hx509_certs_iter().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
#ifdef __BLOCKS__
int
hx509_certs_iter (
@@ -359,6 +1136,22 @@ hx509_certs_iter (
int (^func)(hx509_cert));
#endif /* __BLOCKS__ */
+/**
+ * Iterate over all certificates in a keystore and call a function
+ * for each of them.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param func function to call for each certificate. The function
+ * should return non-zero to abort the iteration, that value is passed
+ * back to the caller of hx509_certs_iter_f().
+ * @param ctx context variable that will passed to the function.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_iter_f (
hx509_context /*context*/,
@@ -366,12 +1159,40 @@ hx509_certs_iter_f (
int (*/*func*/)(hx509_context, void *, hx509_cert),
void */*ctx*/);
+/**
+ * Merge a certificate store into another. The from store is keep
+ * intact.
+ *
+ * @param context a hx509 context.
+ * @param to the store to merge into.
+ * @param from the store to copy the object from.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_merge (
hx509_context /*context*/,
hx509_certs /*to*/,
hx509_certs /*from*/);
+/**
+ * Get next ceritificate from the certificate keystore pointed out by
+ * cursor.
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over.
+ * @param cursor cursor that keeps track of progress.
+ * @param cert return certificate next in store, NULL if the store
+ * contains no more certificates. Free with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_next_cert (
hx509_context /*context*/,
@@ -382,12 +1203,42 @@ hx509_certs_next_cert (
hx509_certs
hx509_certs_ref (hx509_certs /*certs*/);
+/**
+ * Start the integration
+ *
+ * @param context a hx509 context.
+ * @param certs certificate store to iterate over
+ * @param cursor cursor that will keep track of progress, free with
+ * hx509_certs_end_seq().
+ *
+ * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION is
+ * returned if the certificate store doesn't support the iteration
+ * operation.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_start_seq (
hx509_context /*context*/,
hx509_certs /*certs*/,
hx509_cursor */*cursor*/);
+/**
+ * Write the certificate store to stable storage.
+ *
+ * @param context A hx509 context.
+ * @param certs a certificate store to store.
+ * @param flags currently unused, use 0.
+ * @param lock a lock that unlocks the certificates store, use NULL to
+ * select no password/certifictes/prompt lock (see @ref page_lock).
+ *
+ * @return Returns an hx509 error code. HX509_UNSUPPORTED_OPERATION if
+ * the certificate store doesn't support the store operation.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_certs_store (
hx509_context /*context*/,
@@ -395,12 +1246,33 @@ hx509_certs_store (
int /*flags*/,
hx509_lock /*lock*/);
+/**
+ * Function to use to hx509_certs_iter_f() as a function argument, the
+ * ctx variable to hx509_certs_iter_f() should be a FILE file descriptor.
+ *
+ * @param context a hx509 context.
+ * @param ctx used by hx509_certs_iter_f().
+ * @param c a certificate
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_ci_print_names (
hx509_context /*context*/,
void */*ctx*/,
hx509_cert /*c*/);
+/**
+ * Resets the error strings the hx509 context.
+ *
+ * @param context A hx509 context.
+ *
+ * @ingroup hx509_error
+ */
+
void
hx509_clear_error_string (hx509_context /*context*/);
@@ -418,6 +1290,31 @@ hx509_cms_create_signed (
hx509_certs /*pool*/,
heim_octet_string */*signed_data*/);
+/**
+ * Decode SignedData and verify that the signature is correct.
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * @param eContentType the type of the data.
+ * @param data data to sign
+ * @param length length of the data that data point to.
+ * @param digest_alg digest algorithm to use, use NULL to get the
+ * default or the peer determined algorithm.
+ * @param cert certificate to use for sign the data.
+ * @param peer info about the peer the message to send the message to,
+ * like what digest algorithm to use.
+ * @param anchors trust anchors that the client will use, used to
+ * polulate the certificates included in the message
+ * @param pool certificates to use in try to build the path to the
+ * trust anchors.
+ * @param signed_data the output of the function, free with
+ * der_free_octet_string().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
int
hx509_cms_create_signed_1 (
hx509_context /*context*/,
@@ -432,6 +1329,11 @@ hx509_cms_create_signed_1 (
hx509_certs /*pool*/,
heim_octet_string */*signed_data*/);
+/**
+ * Use HX509_CMS_SIGNATURE_NO_SIGNER to create no sigInfo (no
+ * signatures).
+ */
+
int
hx509_cms_decrypt_encrypted (
hx509_context /*context*/,
@@ -441,6 +1343,34 @@ hx509_cms_decrypt_encrypted (
heim_oid */*contentType*/,
heim_octet_string */*content*/);
+/**
+ * Encrypt end encode EnvelopedData.
+ *
+ * Encrypt and encode EnvelopedData. The data is encrypted with a
+ * random key and the the random key is encrypted with the
+ * certificates private key. This limits what private key type can be
+ * used to RSA.
+ *
+ * @param context A hx509 context.
+ * @param flags flags to control the behavior.
+ * - HX509_CMS_EV_NO_KU_CHECK - Dont check KU on certificate
+ * - HX509_CMS_EV_ALLOW_WEAK - Allow weak crytpo
+ * - HX509_CMS_EV_ID_NAME - prefer issuer name and serial number
+ * @param cert Certificate to encrypt the EnvelopedData encryption key
+ * with.
+ * @param data pointer the data to encrypt.
+ * @param length length of the data that data point to.
+ * @param encryption_type Encryption cipher to use for the bulk data,
+ * use NULL to get default.
+ * @param contentType type of the data that is encrypted
+ * @param content the output of the function,
+ * free with der_free_octet_string().
+ *
+ * @return an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
int
hx509_cms_envelope_1 (
hx509_context /*context*/,
@@ -452,6 +1382,30 @@ hx509_cms_envelope_1 (
const heim_oid */*contentType*/,
heim_octet_string */*content*/);
+/**
+ * Decode and unencrypt EnvelopedData.
+ *
+ * Extract data and parameteres from from the EnvelopedData. Also
+ * supports using detached EnvelopedData.
+ *
+ * @param context A hx509 context.
+ * @param certs Certificate that can decrypt the EnvelopedData
+ * encryption key.
+ * @param flags HX509_CMS_UE flags to control the behavior.
+ * @param data pointer the structure the contains the DER/BER encoded
+ * EnvelopedData stucture.
+ * @param length length of the data that data point to.
+ * @param encryptedContent in case of detached signature, this
+ * contains the actual encrypted data, othersize its should be NULL.
+ * @param time_now set the current time, if zero the library uses now as the date.
+ * @param contentType output type oid, should be freed with der_free_oid().
+ * @param content the data, free with der_free_octet_string().
+ *
+ * @return an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
int
hx509_cms_unenvelope (
hx509_context /*context*/,
@@ -464,6 +1418,20 @@ hx509_cms_unenvelope (
heim_oid */*contentType*/,
heim_octet_string */*content*/);
+/**
+ * Decode an ContentInfo and unwrap data and oid it.
+ *
+ * @param in the encoded buffer.
+ * @param oid type of the content.
+ * @param out data to be wrapped.
+ * @param have_data since the data is optional, this flags show dthe
+ * diffrence between no data and the zero length data.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
int
hx509_cms_unwrap_ContentInfo (
const heim_octet_string */*in*/,
@@ -471,6 +1439,30 @@ hx509_cms_unwrap_ContentInfo (
heim_octet_string */*out*/,
int */*have_data*/);
+/**
+ * Decode SignedData and verify that the signature is correct.
+ *
+ * @param context A hx509 context.
+ * @param ctx a hx509 verify context.
+ * @param flags to control the behaivor of the function.
+ * - HX509_CMS_VS_NO_KU_CHECK - Don't check KeyUsage
+ * - HX509_CMS_VS_ALLOW_DATA_OID_MISMATCH - allow oid mismatch
+ * - HX509_CMS_VS_ALLOW_ZERO_SIGNER - no signer, see below.
+ * @param data pointer to CMS SignedData encoded data.
+ * @param length length of the data that data point to.
+ * @param signedContent external data used for signature.
+ * @param pool certificate pool to build certificates paths.
+ * @param contentType free with der_free_oid().
+ * @param content the output of the function, free with
+ * der_free_octet_string().
+ * @param signer_certs list of the cerficates used to sign this
+ * request, free with hx509_certs_free().
+ *
+ * @return an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
int
hx509_cms_verify_signed (
hx509_context /*context*/,
@@ -484,45 +1476,152 @@ hx509_cms_verify_signed (
heim_octet_string */*content*/,
hx509_certs */*signer_certs*/);
+/**
+ * Wrap data and oid in a ContentInfo and encode it.
+ *
+ * @param oid type of the content.
+ * @param buf data to be wrapped. If a NULL pointer is passed in, the
+ * optional content field in the ContentInfo is not going be filled
+ * in.
+ * @param res the encoded buffer, the result should be freed with
+ * der_free_octet_string().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_cms
+ */
+
int
hx509_cms_wrap_ContentInfo (
const heim_oid */*oid*/,
const heim_octet_string */*buf*/,
heim_octet_string */*res*/);
+/**
+ * Free the context allocated by hx509_context_init().
+ *
+ * @param context context to be freed.
+ *
+ * @ingroup hx509
+ */
+
void
hx509_context_free (hx509_context */*context*/);
+/**
+ * Creates a hx509 context that most functions in the library
+ * uses. The context is only allowed to be used by one thread at each
+ * moment. Free the context with hx509_context_free().
+ *
+ * @param context Returns a pointer to new hx509 context.
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509
+ */
+
int
hx509_context_init (hx509_context */*context*/);
+/**
+ * Selects if the hx509_revoke_verify() function is going to require
+ * the existans of a revokation method (OCSP, CRL) or not. Note that
+ * hx509_verify_path(), hx509_cms_verify_signed(), and other function
+ * call hx509_revoke_verify().
+ *
+ * @param context hx509 context to change the flag for.
+ * @param flag zero, revokation method required, non zero missing
+ * revokation method ok
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_context_set_missing_revoke (
hx509_context /*context*/,
int /*flag*/);
+/**
+ * Add revoked certificate to an CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl the CRL to add the revoked certificate to.
+ * @param certs keyset of certificate to revoke.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_crl_add_revoked_certs (
hx509_context /*context*/,
hx509_crl /*crl*/,
hx509_certs /*certs*/);
+/**
+ * Create a CRL context. Use hx509_crl_free() to free the CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl return pointer to a newly allocated CRL context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_crl_alloc (
hx509_context /*context*/,
hx509_crl */*crl*/);
+/**
+ * Free a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context to free.
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_crl_free (
hx509_context /*context*/,
hx509_crl */*crl*/);
+/**
+ * Set the lifetime of a CRL context.
+ *
+ * @param context a hx509 context.
+ * @param crl a CRL context
+ * @param delta delta time the certificate is valid, library adds the
+ * current time to this.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_crl_lifetime (
hx509_context /*context*/,
hx509_crl /*crl*/,
int /*delta*/);
+/**
+ * Sign a CRL and return an encode certificate.
+ *
+ * @param context a hx509 context.
+ * @param signer certificate to sign the CRL with
+ * @param crl the CRL to sign
+ * @param os return the signed and encoded CRL, free with
+ * free_heim_octet_string()
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_crl_sign (
hx509_context /*context*/,
@@ -635,6 +1734,19 @@ hx509_crypto_set_random_key (
hx509_crypto /*crypto*/,
heim_octet_string */*key*/);
+/**
+ * Add a new key/value pair to the hx509_env.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to add
+ * @param value value to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_env
+ */
+
int
hx509_env_add (
hx509_context /*context*/,
@@ -642,6 +1754,19 @@ hx509_env_add (
const char */*key*/,
const char */*value*/);
+/**
+ * Add a new key/binding pair to the hx509_env.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to add
+ * @param list binding list to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_env
+ */
+
int
hx509_env_add_binding (
hx509_context /*context*/,
@@ -649,21 +1774,66 @@ hx509_env_add_binding (
const char */*key*/,
hx509_env /*list*/);
+/**
+ * Search the hx509_env for a key.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to search for.
+ *
+ * @return the value if the key is found, NULL otherwise.
+ *
+ * @ingroup hx509_env
+ */
+
const char *
hx509_env_find (
hx509_context /*context*/,
hx509_env /*env*/,
const char */*key*/);
+/**
+ * Search the hx509_env for a binding.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to search for.
+ *
+ * @return the binding if the key is found, NULL if not found.
+ *
+ * @ingroup hx509_env
+ */
+
hx509_env
hx509_env_find_binding (
hx509_context /*context*/,
hx509_env /*env*/,
const char */*key*/);
+/**
+ * Free an hx509_env environment context.
+ *
+ * @param env the environment to free.
+ *
+ * @ingroup hx509_env
+ */
+
void
hx509_env_free (hx509_env */*env*/);
+/**
+ * Search the hx509_env for a length based key.
+ *
+ * @param context A hx509 context.
+ * @param env environment to add the environment variable too.
+ * @param key key to search for.
+ * @param len length of key.
+ *
+ * @return the value if the key is found, NULL otherwise.
+ *
+ * @ingroup hx509_env
+ */
+
const char *
hx509_env_lfind (
hx509_context /*context*/,
@@ -671,6 +1841,18 @@ hx509_env_lfind (
const char */*key*/,
size_t /*len*/);
+/**
+ * Print error message and fatally exit from error code
+ *
+ * @param context A hx509 context.
+ * @param exit_code exit() code from process.
+ * @param error_code Error code for the reason to exit.
+ * @param fmt format string with the exit message.
+ * @param ... argument to format string.
+ *
+ * @ingroup hx509_error
+ */
+
void
hx509_err (
hx509_context /*context*/,
@@ -682,22 +1864,73 @@ hx509_err (
hx509_private_key_ops *
hx509_find_private_alg (const heim_oid */*oid*/);
+/**
+ * Free error string returned by hx509_get_error_string().
+ *
+ * @param str error string to free.
+ *
+ * @ingroup hx509_error
+ */
+
void
hx509_free_error_string (char */*str*/);
+/**
+ * Free a list of octet strings returned by another hx509 library
+ * function.
+ *
+ * @param list list to be freed.
+ *
+ * @ingroup hx509_misc
+ */
+
void
hx509_free_octet_string_list (hx509_octet_string_list */*list*/);
+/**
+ * Unparse the hx509 name in name into a string.
+ *
+ * @param name the name to print
+ * @param str an allocated string returns the name in string form
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_general_name_unparse (
GeneralName */*name*/,
char **/*str*/);
+/**
+ * Get an error string from context associated with error_code.
+ *
+ * @param context A hx509 context.
+ * @param error_code Get error message for this error code.
+ *
+ * @return error string, free with hx509_free_error_string().
+ *
+ * @ingroup hx509_error
+ */
+
char *
hx509_get_error_string (
hx509_context /*context*/,
int /*error_code*/);
+/**
+ * Get one random certificate from the certificate store.
+ *
+ * @param context a hx509 context.
+ * @param certs a certificate store to get the certificate from.
+ * @param c return certificate, should be freed with hx509_cert_free().
+ *
+ * @return Returns an hx509 error code.
+ *
+ * @ingroup hx509_keyset
+ */
+
int
hx509_get_one_cert (
hx509_context /*context*/,
@@ -729,6 +1962,12 @@ hx509_lock_command_string (
void
hx509_lock_free (hx509_lock /*lock*/);
+/**
+ * @page page_lock Locking and unlocking certificates and encrypted data.
+ *
+ * See the library functions here: @ref hx509_lock
+ */
+
int
hx509_lock_init (
hx509_context /*context*/,
@@ -756,31 +1995,98 @@ hx509_lock_set_prompter (
hx509_prompter_fct /*prompt*/,
void */*data*/);
+/**
+ * Convert a hx509_name object to DER encoded name.
+ *
+ * @param name name to concert
+ * @param os data to a DER encoded name, free the resulting octet
+ * string with hx509_xfree(os->data).
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_name_binary (
const hx509_name /*name*/,
heim_octet_string */*os*/);
+/**
+ * Compare to hx509 name object, useful for sorting.
+ *
+ * @param n1 a hx509 name object.
+ * @param n2 a hx509 name object.
+ *
+ * @return 0 the objects are the same, returns > 0 is n2 is "larger"
+ * then n2, < 0 if n1 is "smaller" then n2.
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_name_cmp (
hx509_name /*n1*/,
hx509_name /*n2*/);
+/**
+ * Copy a hx509 name object.
+ *
+ * @param context A hx509 cotext.
+ * @param from the name to copy from
+ * @param to the name to copy to
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_name_copy (
hx509_context /*context*/,
const hx509_name /*from*/,
hx509_name */*to*/);
+/**
+ * Expands variables in the name using env. Variables are on the form
+ * ${name}. Useful when dealing with certificate templates.
+ *
+ * @param context A hx509 cotext.
+ * @param name the name to expand.
+ * @param env environment variable to expand.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_name_expand (
hx509_context /*context*/,
hx509_name /*name*/,
hx509_env /*env*/);
+/**
+ * Free a hx509 name object, upond return *name will be NULL.
+ *
+ * @param name a hx509 name object to be freed.
+ *
+ * @ingroup hx509_name
+ */
+
void
hx509_name_free (hx509_name */*name*/);
+/**
+ * Unparse the hx509 name in name into a string.
+ *
+ * @param name the name to check if its empty/null.
+ *
+ * @return non zero if the name is empty/null.
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_name_is_null_p (const hx509_name /*name*/);
@@ -789,16 +2095,56 @@ hx509_name_normalize (
hx509_context /*context*/,
hx509_name /*name*/);
+/**
+ * Convert a hx509_name into a Name.
+ *
+ * @param from the name to copy from
+ * @param to the name to copy to
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_name_to_Name (
const hx509_name /*from*/,
Name */*to*/);
+/**
+ * Convert the hx509 name object into a printable string.
+ * The resulting string should be freed with free().
+ *
+ * @param name name to print
+ * @param str the string to return
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_name_to_string (
const hx509_name /*name*/,
char **/*str*/);
+/**
+ * Create an OCSP request for a set of certificates.
+ *
+ * @param context a hx509 context
+ * @param reqcerts list of certificates to request ocsp data for
+ * @param pool certificate pool to use when signing
+ * @param signer certificate to use to sign the request
+ * @param digest the signing algorithm in the request, if NULL use the
+ * default signature algorithm,
+ * @param request the encoded request, free with free_heim_octet_string().
+ * @param nonce nonce in the request, free with free_heim_octet_string().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_ocsp_request (
hx509_context /*context*/,
@@ -809,6 +2155,25 @@ hx509_ocsp_request (
heim_octet_string */*request*/,
heim_octet_string */*nonce*/);
+/**
+ * Verify that the certificate is part of the OCSP reply and it's not
+ * expired. Doesn't verify signature the OCSP reply or it's done by a
+ * authorized sender, that is assumed to be already done.
+ *
+ * @param context a hx509 context
+ * @param now the time right now, if 0, use the current time.
+ * @param cert the certificate to verify
+ * @param flags flags control the behavior
+ * @param data pointer to the encode ocsp reply
+ * @param length the length of the encode ocsp reply
+ * @param expiration return the time the OCSP will expire and need to
+ * be rechecked.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_ocsp_verify (
hx509_context /*context*/,
@@ -819,17 +2184,51 @@ hx509_ocsp_verify (
size_t /*length*/,
time_t */*expiration*/);
+/**
+ * Print a oid using a hx509_vprint_func function. To print to stdout
+ * use hx509_print_stdout().
+ *
+ * @param oid oid to print
+ * @param func hx509_vprint_func to print with.
+ * @param ctx context variable to hx509_vprint_func function.
+ *
+ * @ingroup hx509_print
+ */
+
void
hx509_oid_print (
const heim_oid */*oid*/,
hx509_vprint_func /*func*/,
void */*ctx*/);
+/**
+ * Print a oid to a string.
+ *
+ * @param oid oid to print
+ * @param str allocated string, free with hx509_xfree().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
int
hx509_oid_sprint (
const heim_oid */*oid*/,
char **/*str*/);
+/**
+ * Parse a string into a hx509 name object.
+ *
+ * @param context A hx509 context.
+ * @param str a string to parse.
+ * @param name the resulting object, NULL in case of error.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_parse_name (
hx509_context /*context*/,
@@ -845,25 +2244,80 @@ hx509_parse_private_key (
hx509_key_format_t /*format*/,
hx509_private_key */*private_key*/);
+/**
+ * Add an additional algorithm that the peer supports.
+ *
+ * @param context A hx509 context.
+ * @param peer the peer to set the new algorithms for
+ * @param val an AlgorithmsIdentier to add
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
int
hx509_peer_info_add_cms_alg (
hx509_context /*context*/,
hx509_peer_info /*peer*/,
const AlgorithmIdentifier */*val*/);
+/**
+ * Allocate a new peer info structure an init it to default values.
+ *
+ * @param context A hx509 context.
+ * @param peer return an allocated peer, free with hx509_peer_info_free().
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
int
hx509_peer_info_alloc (
hx509_context /*context*/,
hx509_peer_info */*peer*/);
+/**
+ * Free a peer info structure.
+ *
+ * @param peer peer info to be freed.
+ *
+ * @ingroup hx509_peer
+ */
+
void
hx509_peer_info_free (hx509_peer_info /*peer*/);
+/**
+ * Set the certificate that remote peer is using.
+ *
+ * @param peer peer info to update
+ * @param cert cerificate of the remote peer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
int
hx509_peer_info_set_cert (
hx509_peer_info /*peer*/,
hx509_cert /*cert*/);
+/**
+ * Set the algorithms that the peer supports.
+ *
+ * @param context A hx509 context.
+ * @param peer the peer to set the new algorithms for
+ * @param val array of supported AlgorithmsIdentiers
+ * @param len length of array val.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_peer
+ */
+
int
hx509_peer_info_set_cms_algs (
hx509_context /*context*/,
@@ -901,12 +2355,38 @@ hx509_pem_write (
const void */*data*/,
size_t /*size*/);
+/**
+ * Print a simple representation of a certificate
+ *
+ * @param context A hx509 context, can be NULL
+ * @param cert certificate to print
+ * @param out the stdio output stream, if NULL, stdout is used
+ *
+ * @return An hx509 error code
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_print_cert (
hx509_context /*context*/,
hx509_cert /*cert*/,
FILE */*out*/);
+/**
+ * Helper function to print on stdout for:
+ * - hx509_oid_print(),
+ * - hx509_bitstring_print(),
+ * - hx509_validate_ctx_set_print().
+ *
+ * @param ctx the context to the print function. If the ctx is NULL,
+ * stdout is used.
+ * @param fmt the printing format.
+ * @param va the argumet list.
+ *
+ * @ingroup hx509_print
+ */
+
void
hx509_print_stdout (
void */*ctx*/,
@@ -944,22 +2424,68 @@ hx509_private_key_private_decrypt (
int
hx509_prompt_hidden (hx509_prompt_type /*type*/);
+/**
+ * Allocate an query controller. Free using hx509_query_free().
+ *
+ * @param context A hx509 context.
+ * @param q return pointer to a hx509_query.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_query_alloc (
hx509_context /*context*/,
hx509_query **/*q*/);
+/**
+ * Free the query controller.
+ *
+ * @param context A hx509 context.
+ * @param q a pointer to the query controller.
+ *
+ * @ingroup hx509_cert
+ */
+
void
hx509_query_free (
hx509_context /*context*/,
hx509_query */*q*/);
+/**
+ * Set the query controller to match using a specific match function.
+ *
+ * @param q a hx509 query controller.
+ * @param func function to use for matching, if the argument is NULL,
+ * the match function is removed.
+ * @param ctx context passed to the function.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_query_match_cmp_func (
hx509_query */*q*/,
int (*/*func*/)(hx509_context, hx509_cert, void *),
void */*ctx*/);
+/**
+ * Set the query controller to require an one specific EKU (extended
+ * key usage). Any previous EKU matching is overwitten. If NULL is
+ * passed in as the eku, the EKU requirement is reset.
+ *
+ * @param q a hx509 query controller.
+ * @param eku an EKU to match on.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_query_match_eku (
hx509_query */*q*/,
@@ -971,27 +2497,81 @@ hx509_query_match_expr (
hx509_query */*q*/,
const char */*expr*/);
+/**
+ * Set the query controller to match on a friendly name
+ *
+ * @param q a hx509 query controller.
+ * @param name a friendly name to match on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_query_match_friendly_name (
hx509_query */*q*/,
const char */*name*/);
+/**
+ * Set the issuer and serial number of match in the query
+ * controller. The function make copies of the isser and serial number.
+ *
+ * @param q a hx509 query controller
+ * @param issuer issuer to search for
+ * @param serialNumber the serialNumber of the issuer.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_query_match_issuer_serial (
hx509_query */*q*/,
const Name */*issuer*/,
const heim_integer */*serialNumber*/);
+/**
+ * Set match options for the hx509 query controller.
+ *
+ * @param q query controller.
+ * @param option options to control the query controller.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
void
hx509_query_match_option (
hx509_query */*q*/,
hx509_query_option /*option*/);
+/**
+ * Set a statistic file for the query statistics.
+ *
+ * @param context A hx509 context.
+ * @param fn statistics file name
+ *
+ * @ingroup hx509_cert
+ */
+
void
hx509_query_statistic_file (
hx509_context /*context*/,
const char */*fn*/);
+/**
+ * Unparse the statistics file and print the result on a FILE descriptor.
+ *
+ * @param context A hx509 context.
+ * @param printtype tyep to print
+ * @param out the FILE to write the data on.
+ *
+ * @ingroup hx509_cert
+ */
+
void
hx509_query_unparse_stats (
hx509_context /*context*/,
@@ -1030,26 +2610,81 @@ hx509_request_set_name (
hx509_request /*req*/,
hx509_name /*name*/);
+/**
+ * Add a CRL file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_add_crl (
hx509_context /*context*/,
hx509_revoke_ctx /*ctx*/,
const char */*path*/);
+/**
+ * Add a OCSP file to the revokation context.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param path path to file that is going to be added to the context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_add_ocsp (
hx509_context /*context*/,
hx509_revoke_ctx /*ctx*/,
const char */*path*/);
+/**
+ * Free a hx509 revokation context.
+ *
+ * @param ctx context to be freed
+ *
+ * @ingroup hx509_revoke
+ */
+
void
hx509_revoke_free (hx509_revoke_ctx */*ctx*/);
+/**
+ * Allocate a revokation context. Free with hx509_revoke_free().
+ *
+ * @param context A hx509 context.
+ * @param ctx returns a newly allocated revokation context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_init (
hx509_context /*context*/,
hx509_revoke_ctx */*ctx*/);
+/**
+ * Print the OCSP reply stored in a file.
+ *
+ * @param context a hx509 context
+ * @param path path to a file with a OCSP reply
+ * @param out the out FILE descriptor to print the reply on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
int
hx509_revoke_ocsp_print (
hx509_context /*context*/,
@@ -1057,6 +2692,29 @@ hx509_revoke_ocsp_print (
FILE */*out*/);
int
+hx509_revoke_print (
+ hx509_context /*context*/,
+ hx509_revoke_ctx /*ctx*/,
+ FILE */*out*/);
+
+/**
+ * Check that a certificate is not expired according to a revokation
+ * context. Also need the parent certificte to the check OCSP
+ * parent identifier.
+ *
+ * @param context hx509 context
+ * @param ctx hx509 revokation context
+ * @param certs
+ * @param now
+ * @param cert
+ * @param parent_cert
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+int
hx509_revoke_verify (
hx509_context /*context*/,
hx509_revoke_ctx /*ctx*/,
@@ -1065,6 +2723,20 @@ hx509_revoke_verify (
hx509_cert /*cert*/,
hx509_cert /*parent_cert*/);
+/**
+ * See hx509_set_error_stringv().
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * - HX509_ERROR_APPEND appends the error string to the old messages
+ (code is updated).
+ * @param code error code related to error message
+ * @param fmt error message format
+ * @param ... arguments to error message format
+ *
+ * @ingroup hx509_error
+ */
+
void
hx509_set_error_string (
hx509_context /*context*/,
@@ -1073,6 +2745,20 @@ hx509_set_error_string (
const char */*fmt*/,
...);
+/**
+ * Add an error message to the hx509 context.
+ *
+ * @param context A hx509 context.
+ * @param flags
+ * - HX509_ERROR_APPEND appends the error string to the old messages
+ (code is updated).
+ * @param code error code related to error message
+ * @param fmt error message format
+ * @param ap arguments to error message format
+ *
+ * @ingroup hx509_error
+ */
+
void
hx509_set_error_stringv (
hx509_context /*context*/,
@@ -1085,9 +2771,6 @@ const AlgorithmIdentifier *
hx509_signature_ecPublicKey (void);
const AlgorithmIdentifier *
-hx509_signature_ecdsa_with_sha1 (void);
-
-const AlgorithmIdentifier *
hx509_signature_ecdsa_with_sha256 (void);
const AlgorithmIdentifier *
@@ -1126,42 +2809,135 @@ hx509_signature_sha384 (void);
const AlgorithmIdentifier *
hx509_signature_sha512 (void);
+/**
+ * Convert a DER encoded name info a string.
+ *
+ * @param data data to a DER/BER encoded name
+ * @param length length of data
+ * @param str the resulting string, is NULL on failure.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_name
+ */
+
int
hx509_unparse_der_name (
const void */*data*/,
size_t /*length*/,
char **/*str*/);
+/**
+ * Validate/Print the status of the certificate.
+ *
+ * @param context A hx509 context.
+ * @param ctx A hx509 validation context.
+ * @param cert the cerificate to validate/print.
+
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
int
hx509_validate_cert (
hx509_context /*context*/,
hx509_validate_ctx /*ctx*/,
hx509_cert /*cert*/);
+/**
+ * Add flags to control the behaivor of the hx509_validate_cert()
+ * function.
+ *
+ * @param ctx A hx509 validation context.
+ * @param flags flags to add to the validation context.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
void
hx509_validate_ctx_add_flags (
hx509_validate_ctx /*ctx*/,
int /*flags*/);
+/**
+ * Free an hx509 validate context.
+ *
+ * @param ctx the hx509 validate context to free.
+ *
+ * @ingroup hx509_print
+ */
+
void
hx509_validate_ctx_free (hx509_validate_ctx /*ctx*/);
+/**
+ * Allocate a hx509 validation/printing context.
+ *
+ * @param context A hx509 context.
+ * @param ctx a new allocated hx509 validation context, free with
+ * hx509_validate_ctx_free().
+
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
int
hx509_validate_ctx_init (
hx509_context /*context*/,
hx509_validate_ctx */*ctx*/);
+/**
+ * Set the printing functions for the validation context.
+ *
+ * @param ctx a hx509 valication context.
+ * @param func the printing function to usea.
+ * @param c the context variable to the printing function.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_print
+ */
+
void
hx509_validate_ctx_set_print (
hx509_validate_ctx /*ctx*/,
hx509_vprint_func /*func*/,
void */*c*/);
+/**
+ * Set the trust anchors in the verification context, makes an
+ * reference to the keyset, so the consumer can free the keyset
+ * independent of the destruction of the verification context (ctx).
+ * If there already is a keyset attached, it's released.
+ *
+ * @param ctx a verification context
+ * @param set a keyset containing the trust anchors.
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_verify_attach_anchors (
hx509_verify_ctx /*ctx*/,
hx509_certs /*set*/);
+/**
+ * Attach an revocation context to the verfication context, , makes an
+ * reference to the revoke context, so the consumer can free the
+ * revoke context independent of the destruction of the verification
+ * context. If there is no revoke context, the verification process is
+ * NOT going to check any verification status.
+ *
+ * @param ctx a verification context.
+ * @param revoke_ctx a revoke context.
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_verify_attach_revoke (
hx509_verify_ctx /*ctx*/,
@@ -1172,14 +2948,56 @@ hx509_verify_ctx_f_allow_best_before_signature_algs (
hx509_context /*ctx*/,
int /*boolean*/);
+/**
+ * Allow using the operating system builtin trust anchors if no other
+ * trust anchors are configured.
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, useing the operating systems builtin
+ * trust anchors.
+ *
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
void
hx509_verify_ctx_f_allow_default_trustanchors (
hx509_verify_ctx /*ctx*/,
int /*boolean*/);
+/**
+ * Free an hx509 verification context.
+ *
+ * @param ctx the context to be freed.
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_verify_destroy_ctx (hx509_verify_ctx /*ctx*/);
+/**
+ * Verify that the certificate is allowed to be used for the hostname
+ * and address.
+ *
+ * @param context A hx509 context.
+ * @param cert the certificate to match with
+ * @param flags Flags to modify the behavior:
+ * - HX509_VHN_F_ALLOW_NO_MATCH no match is ok
+ * @param type type of hostname:
+ * - HX509_HN_HOSTNAME for plain hostname.
+ * - HX509_HN_DNSSRV for DNS SRV names.
+ * @param hostname the hostname to check
+ * @param sa address of the host
+ * @param sa_size length of address
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_cert
+ */
+
int
hx509_verify_hostname (
hx509_context /*context*/,
@@ -1190,11 +3008,38 @@ hx509_verify_hostname (
const struct sockaddr */*sa*/,
int /*sa_size*/);
+/**
+ * Allocate an verification context that is used fo control the
+ * verification process.
+ *
+ * @param context A hx509 context.
+ * @param ctx returns a pointer to a hx509_verify_ctx object.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_verify_init_ctx (
hx509_context /*context*/,
hx509_verify_ctx */*ctx*/);
+/**
+ * Build and verify the path for the certificate to the trust anchor
+ * specified in the verify context. The path is constructed from the
+ * certificate, the pool and the trust anchors.
+ *
+ * @param context A hx509 context.
+ * @param ctx A hx509 verification context.
+ * @param cert the certificate to build the path from.
+ * @param pool A keyset of certificates to build the chain from.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_verify
+ */
+
int
hx509_verify_path (
hx509_context /*context*/,
@@ -1202,26 +3047,83 @@ hx509_verify_path (
hx509_cert /*cert*/,
hx509_certs /*pool*/);
+/**
+ * Set the maximum depth of the certificate chain that the path
+ * builder is going to try.
+ *
+ * @param ctx a verification context
+ * @param max_depth maxium depth of the certificate chain, include
+ * trust anchor.
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_verify_set_max_depth (
hx509_verify_ctx /*ctx*/,
unsigned int /*max_depth*/);
+/**
+ * Allow or deny the use of proxy certificates
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, allow proxy certificates.
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_verify_set_proxy_certificate (
hx509_verify_ctx /*ctx*/,
int /*boolean*/);
+/**
+ * Select strict RFC3280 verification of certificiates. This means
+ * checking key usage on CA certificates, this will make version 1
+ * certificiates unuseable.
+ *
+ * @param ctx a verification context
+ * @param boolean if non zero, use strict verification.
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_verify_set_strict_rfc3280_verification (
hx509_verify_ctx /*ctx*/,
int /*boolean*/);
+/**
+ * Set the clock time the the verification process is going to
+ * use. Used to check certificate in the past and future time. If not
+ * set the current time will be used.
+ *
+ * @param ctx a verification context.
+ * @param t the time the verifiation is using.
+ *
+ *
+ * @ingroup hx509_verify
+ */
+
void
hx509_verify_set_time (
hx509_verify_ctx /*ctx*/,
time_t /*t*/);
+/**
+ * Verify a signature made using the private key of an certificate.
+ *
+ * @param context A hx509 context.
+ * @param signer the certificate that made the signature.
+ * @param alg algorthm that was used to sign the data.
+ * @param data the data that was signed.
+ * @param sig the sigature to verify.
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_crypto
+ */
+
int
hx509_verify_signature (
hx509_context /*context*/,
@@ -1230,6 +3132,14 @@ hx509_verify_signature (
const heim_octet_string */*data*/,
const heim_octet_string */*sig*/);
+/**
+ * Free a data element allocated in the library.
+ *
+ * @param ptr data to be freed.
+ *
+ * @ingroup hx509_misc
+ */
+
void
hx509_xfree (void */*ptr*/);
@@ -1240,4 +3150,5 @@ yywrap (void);
}
#endif
+#endif /* DOXY */
#endif /* __hx509_protos_h__ */
diff --git a/lib/hx509/hx509.h b/lib/hx509/hx509.h
index 3954b54b1c04..781f4a59cc73 100644
--- a/lib/hx509/hx509.h
+++ b/lib/hx509/hx509.h
@@ -39,6 +39,7 @@
#include <rfc2459_asn1.h>
#include <stdarg.h>
#include <stdio.h>
+#include <heimbase.h>
typedef struct hx509_cert_attribute_data *hx509_cert_attribute;
typedef struct hx509_cert_data *hx509_cert;
diff --git a/lib/hx509/hx509_err.et b/lib/hx509/hx509_err.et
index 6225f125fb20..f0a27e83620c 100644
--- a/lib/hx509/hx509_err.et
+++ b/lib/hx509/hx509_err.et
@@ -8,7 +8,7 @@ id "$Id$"
error_table hx
prefix HX509
-# path validateion and construction related errors
+# path validation and construction related errors
error_code BAD_TIMEFORMAT, "ASN.1 failed call to system time library"
error_code EXTENSION_NOT_FOUND, "Extension not found"
error_code NO_PATH, "Certification path not found"
@@ -21,9 +21,9 @@ error_code CERT_USED_AFTER_TIME, "Certificate used after it became invalid"
error_code PRIVATE_KEY_MISSING, "Private key required for the operation is missing"
error_code ALG_NOT_SUPP, "Algorithm not supported"
error_code ISSUER_NOT_FOUND, "Issuer couldn't be found"
-error_code VERIFY_CONSTRAINTS, "Error verifing constraints"
+error_code VERIFY_CONSTRAINTS, "Error verifying constraints"
error_code RANGE, "Number too large"
-error_code NAME_CONSTRAINT_ERROR, "Error while verifing name constraints"
+error_code NAME_CONSTRAINT_ERROR, "Error while verifying name constraints"
error_code PATH_TOO_LONG, "Path is too long, failed to find valid anchor"
error_code KU_CERT_MISSING, "Required keyusage for this certificate is missing"
error_code CERT_NOT_FOUND, "Certificate not found"
@@ -32,10 +32,10 @@ error_code PARENT_IS_CA, "Parent certificate is a CA"
error_code EXTRA_DATA_AFTER_STRUCTURE, "Extra data was found after the structure"
error_code PROXY_CERT_INVALID, "Proxy certificate is invalid"
error_code PROXY_CERT_NAME_WRONG, "Proxy certificate name is wrong"
-error_code NAME_MALFORMED, "Name is malformated"
-error_code CERTIFICATE_MALFORMED, "Certificate is malformated"
+error_code NAME_MALFORMED, "Name is malformed"
+error_code CERTIFICATE_MALFORMED, "Certificate is malformed"
error_code CERTIFICATE_MISSING_EKU, "Certificate is missing a required EKU"
-error_code PROXY_CERTIFICATE_NOT_CANONICALIZED, "Proxy certificate not canonicalize"
+error_code PROXY_CERTIFICATE_NOT_CANONICALIZED, "Proxy certificate not canonicalized"
# cms related errors
index 32
@@ -58,9 +58,9 @@ error_code SIGNATURE_MISSING, "Signature missing for data"
error_code BAD_SIGNATURE, "Signature is not valid"
error_code SIG_NO_CONF, "Sigature doesn't provide confidentiality"
error_code SIG_INVALID_FORMAT, "Invalid format on signature"
-error_code OID_MISMATCH, "Mismatch bewteen oids"
+error_code OID_MISMATCH, "Mismatch between oids"
error_code NO_PROMPTER, "No prompter function defined"
-error_code SIGNATURE_WITHOUT_SIGNER, "Signature require signer, but non available"
+error_code SIGNATURE_WITHOUT_SIGNER, "Signature requires signer, but none available"
error_code RSA_PUBLIC_ENCRYPT, "RSA public encyption failed"
error_code RSA_PRIVATE_ENCRYPT, "RSA private encyption failed"
error_code RSA_PUBLIC_DECRYPT, "RSA public decryption failed"
@@ -100,4 +100,10 @@ error_code OPEN_SESSION, "Failed to open session to slot"
error_code LOGIN, "Failed to login to slot"
error_code LOAD, "Failed to load PKCS module"
+# pkinit related errors
+error_code PIN_INCORRECT, "Incorrect User PIN"
+error_code PIN_LOCKED, "User PIN locked"
+error_code PIN_NOT_INITIALIZED, "User PIN not initialized"
+error_code PIN_EXPIRED, "User PIN expired"
+
end
diff --git a/lib/hx509/hx_locl.h b/lib/hx509/hx_locl.h
index a0a5235c7586..44d241f350ae 100644
--- a/lib/hx509/hx_locl.h
+++ b/lib/hx509/hx_locl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 - 2006 Kungliga Tekniska Högskolan
+ * Copyright (c) 2004 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -70,14 +70,24 @@
#include <der.h>
+/*
+ * We use OpenSSL for EC, but to do this we need to disable cross-references
+ * between OpenSSL and hcrypto bn.h and such. Source files that use OpenSSL EC
+ * must define HEIM_NO_CRYPTO_HDRS before including this file.
+ */
+
#define HC_DEPRECATED_CRYPTO
+#ifndef HEIM_NO_CRYPTO_HDRS
#include "crypto-headers.h"
+#endif
struct hx509_keyset_ops;
struct hx509_collector;
struct hx509_generate_private_context;
typedef struct hx509_path hx509_path;
+#include <heimbase.h>
+
#include <hx509.h>
typedef void (*_hx509_cert_release_func)(struct hx509_cert_data *, void *);
@@ -186,7 +196,7 @@ struct hx509_context_data {
#define HX509_CTX_VERIFY_MISSING_OK 1
int ocsp_time_diff;
#define HX509_DEFAULT_OCSP_TIME_DIFF (5*60)
- hx509_error error;
+ heim_error_t error;
struct et_list *et_list;
char *querystat;
hx509_certs default_trust_anchors;
@@ -212,6 +222,95 @@ extern const AlgorithmIdentifier * _hx509_crypto_default_digest_alg;
extern const AlgorithmIdentifier * _hx509_crypto_default_secret_alg;
/*
+ * Private bits from crypto.c, so crypto-ec.c can also see them.
+ *
+ * This is part of the use-OpenSSL-for-EC hack.
+ */
+
+struct hx509_crypto;
+
+struct signature_alg;
+
+struct hx509_generate_private_context {
+ const heim_oid *key_oid;
+ int isCA;
+ unsigned long num_bits;
+};
+
+struct hx509_private_key_ops {
+ const char *pemtype;
+ const heim_oid *key_oid;
+ int (*available)(const hx509_private_key,
+ const AlgorithmIdentifier *);
+ int (*get_spki)(hx509_context,
+ const hx509_private_key,
+ SubjectPublicKeyInfo *);
+ int (*export)(hx509_context context,
+ const hx509_private_key,
+ hx509_key_format_t,
+ heim_octet_string *);
+ int (*import)(hx509_context, const AlgorithmIdentifier *,
+ const void *, size_t, hx509_key_format_t,
+ hx509_private_key);
+ int (*generate_private_key)(hx509_context,
+ struct hx509_generate_private_context *,
+ hx509_private_key);
+ BIGNUM *(*get_internal)(hx509_context, hx509_private_key, const char *);
+};
+
+struct hx509_private_key {
+ unsigned int ref;
+ const struct signature_alg *md;
+ const heim_oid *signature_alg;
+ union {
+ RSA *rsa;
+ void *keydata;
+ void *ecdsa; /* EC_KEY */
+ } private_key;
+ hx509_private_key_ops *ops;
+};
+
+/*
+ *
+ */
+
+struct signature_alg {
+ const char *name;
+ const heim_oid *sig_oid;
+ const AlgorithmIdentifier *sig_alg;
+ const heim_oid *key_oid;
+ const AlgorithmIdentifier *digest_alg;
+ int flags;
+#define PROVIDE_CONF 0x1
+#define REQUIRE_SIGNER 0x2
+#define SELF_SIGNED_OK 0x4
+#define WEAK_SIG_ALG 0x8
+
+#define SIG_DIGEST 0x100
+#define SIG_PUBLIC_SIG 0x200
+#define SIG_SECRET 0x400
+
+#define RA_RSA_USES_DIGEST_INFO 0x1000000
+
+ time_t best_before; /* refuse signature made after best before date */
+ const EVP_MD *(*evp_md)(void);
+ int (*verify_signature)(hx509_context context,
+ const struct signature_alg *,
+ const Certificate *,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ const heim_octet_string *);
+ int (*create_signature)(hx509_context,
+ const struct signature_alg *,
+ const hx509_private_key,
+ const AlgorithmIdentifier *,
+ const heim_octet_string *,
+ AlgorithmIdentifier *,
+ heim_octet_string *);
+ int digest_size;
+};
+
+/*
* Configurable options
*/
diff --git a/lib/hx509/hxtool-commands.in b/lib/hx509/hxtool-commands.in
index ab517224ecd7..49e392d038ef 100644
--- a/lib/hx509/hxtool-commands.in
+++ b/lib/hx509/hxtool-commands.in
@@ -107,7 +107,7 @@ command = {
option = {
long = "embedded-certs"
type = "-flag"
- help = "dont embedded certficiates"
+ help = "don't embed certificates"
}
option = {
long = "embed-leaf-only"
@@ -409,6 +409,17 @@ command = {
help = "Print the OCSP responses"
}
command = {
+ name = "revoke-print"
+ option = {
+ long = "verbose"
+ type = "flag"
+ help = "verbose"
+ }
+ min_args="1"
+ argument="ocsp/crl files"
+ help = "Print the OCSP/CRL files"
+}
+command = {
name = "request-create"
option = {
long = "subject"
@@ -624,6 +635,11 @@ command = {
help = "Lifetime of certificate"
}
option = {
+ long = "signature-algorithm"
+ type = "string"
+ help = "Signature algorithm to use"
+ }
+ option = {
long = "serial-number"
type = "string"
help = "serial-number of certificate"
@@ -646,7 +662,7 @@ command = {
}
option = {
long = "pk-init-principal"
- type = "string"
+ type = "strings"
help = "PK-INIT principal (for SAN)"
}
option = {
diff --git a/lib/hx509/hxtool.c b/lib/hx509/hxtool.c
index 4bd467f4284a..0a7048bdf428 100644
--- a/lib/hx509/hxtool.c
+++ b/lib/hx509/hxtool.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2004 - 2007 Kungliga Tekniska Högskolan
+ * Copyright (c) 2004 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -372,9 +372,9 @@ cms_create_sd(struct cms_create_sd_options *opt, int argc, char **argv)
infile = argv[0];
if (argc < 2) {
- asprintf(&outfile, "%s.%s", infile,
- opt->pem_flag ? "pem" : "cms-signeddata");
- if (outfile == NULL)
+ ret = asprintf(&outfile, "%s.%s", infile,
+ opt->pem_flag ? "pem" : "cms-signeddata");
+ if (ret == -1 || outfile == NULL)
errx(1, "out of memory");
} else
outfile = argv[1];
@@ -1135,6 +1135,45 @@ ocsp_print(struct ocsp_print_options *opt, int argc, char **argv)
return 0;
}
+int
+revoke_print(struct revoke_print_options *opt, int argc, char **argv)
+{
+ hx509_revoke_ctx revoke_ctx;
+ int ret;
+
+ ret = hx509_revoke_init(context, &revoke_ctx);
+ if (ret)
+ errx(1, "hx509_revoke_init: %d", ret);
+
+ while(argc--) {
+ char *s = *argv++;
+
+ if (strncmp(s, "crl:", 4) == 0) {
+ s += 4;
+
+ ret = hx509_revoke_add_crl(context, revoke_ctx, s);
+ if (ret)
+ errx(1, "hx509_revoke_add_crl: %s: %d", s, ret);
+
+ } else if (strncmp(s, "ocsp:", 4) == 0) {
+ s += 5;
+
+ ret = hx509_revoke_add_ocsp(context, revoke_ctx, s);
+ if (ret)
+ errx(1, "hx509_revoke_add_ocsp: %s: %d", s, ret);
+
+ } else {
+ errx(1, "unknown option to verify: `%s'\n", s);
+ }
+ }
+
+ ret = hx509_revoke_print(context, revoke_ctx, stdout);
+ if (ret)
+ warnx("hx509_revoke_print: %d", ret);
+
+ return ret;
+}
+
/*
*
*/
@@ -1394,7 +1433,7 @@ info(void *opt, int argc, char **argv)
if (m != NULL)
printf("dh: %s\n", m->name);
}
-#ifdef HAVE_OPENSSL
+#ifdef HAVE_HCRYPTO_W_OPENSSL
{
printf("ecdsa: ECDSA_METHOD-not-export\n");
}
@@ -1692,12 +1731,13 @@ eval_types(hx509_context contextp,
}
}
- if (opt->pk_init_principal_string) {
+ for (i = 0; i < opt->pk_init_principal_strings.num_strings; i++) {
+ const char *pk_init_princ = opt->pk_init_principal_strings.strings[i];
+
if (!ctopt.pkinit)
errx(1, "pk-init principal given but no pk-init oid");
- ret = hx509_ca_tbs_add_san_pkinit(contextp, tbs,
- opt->pk_init_principal_string);
+ ret = hx509_ca_tbs_add_san_pkinit(contextp, tbs, pk_init_princ);
if (ret)
hx509_err(contextp, 1, ret, "hx509_ca_tbs_add_san_pkinit");
}
@@ -1888,6 +1928,17 @@ hxtool_ca(struct certificate_sign_options *opt, int argc, char **argv)
if (ret)
hx509_err(context, 1, ret, "hx509_ca_tbs_init");
+ if (opt->signature_algorithm_string) {
+ const AlgorithmIdentifier *sigalg;
+ if (strcasecmp(opt->signature_algorithm_string, "rsa-with-sha1") == 0)
+ sigalg = hx509_signature_rsa_with_sha1();
+ else if (strcasecmp(opt->signature_algorithm_string, "rsa-with-sha256") == 0)
+ sigalg = hx509_signature_rsa_with_sha256();
+ else
+ errx(1, "unsupported sigature algorithm");
+ hx509_ca_tbs_set_signature_algorithm(context, tbs, sigalg);
+ }
+
if (opt->template_certificate_string) {
hx509_cert template;
hx509_certs tcerts;
diff --git a/lib/hx509/keyset.c b/lib/hx509/keyset.c
index c0275d949d06..ed5b22b981d3 100644
--- a/lib/hx509/keyset.c
+++ b/lib/hx509/keyset.c
@@ -107,6 +107,8 @@ _hx509_ks_register(hx509_context context, struct hx509_keyset_ops *ops)
* select no password/certifictes/prompt lock (see @ref page_lock).
* @param certs return pointer, free with hx509_certs_free().
*
+ * @return Returns an hx509 error code.
+ *
* @ingroup hx509_keyset
*/
@@ -318,8 +320,8 @@ hx509_certs_end_seq(hx509_context context,
}
/**
- * Iterate over all certificates in a keystore and call an function
- * for each fo them.
+ * Iterate over all certificates in a keystore and call a function
+ * for each of them.
*
* @param context a hx509 context.
* @param certs certificate store to iterate over.
@@ -366,21 +368,6 @@ hx509_certs_iter_f(hx509_context context,
return ret;
}
-/**
- * Iterate over all certificates in a keystore and call an function
- * for each fo them.
- *
- * @param context a hx509 context.
- * @param certs certificate store to iterate over.
- * @param func function to call for each certificate. The function
- * should return non-zero to abort the iteration, that value is passed
- * back to the caller of hx509_certs_iter().
- *
- * @return Returns an hx509 error code.
- *
- * @ingroup hx509_keyset
- */
-
#ifdef __BLOCKS__
static int
@@ -391,8 +378,8 @@ certs_iter(hx509_context context, void *ctx, hx509_cert cert)
}
/**
- * Iterate over all certificates in a keystore and call an block
- * for each fo them.
+ * Iterate over all certificates in a keystore and call a block
+ * for each of them.
*
* @param context a hx509 context.
* @param certs certificate store to iterate over.
@@ -752,11 +739,12 @@ _hx509_pi_printf(int (*func)(void *, const char *), void *ctx,
{
va_list ap;
char *str;
+ int ret;
va_start(ap, fmt);
- vasprintf(&str, fmt, ap);
+ ret = vasprintf(&str, fmt, ap);
va_end(ap);
- if (str == NULL)
+ if (ret == -1 || str == NULL)
return;
(*func)(ctx, str);
free(str);
diff --git a/lib/hx509/ks_dir.c b/lib/hx509/ks_dir.c
index 264b1bf552d8..1740dfe42c74 100644
--- a/lib/hx509/ks_dir.c
+++ b/lib/hx509/ks_dir.c
@@ -211,7 +211,10 @@ static struct hx509_keyset_ops keyset_dir = {
NULL,
dir_iter_start,
dir_iter,
- dir_iter_end
+ dir_iter_end,
+ NULL,
+ NULL,
+ NULL
};
void
diff --git a/lib/hx509/ks_file.c b/lib/hx509/ks_file.c
index d21d88928708..642dd173b53c 100644
--- a/lib/hx509/ks_file.c
+++ b/lib/hx509/ks_file.c
@@ -52,12 +52,16 @@ parse_certificate(hx509_context context, const char *fn,
const void *data, size_t len,
const AlgorithmIdentifier *ai)
{
+ heim_error_t error = NULL;
hx509_cert cert;
int ret;
- ret = hx509_cert_init_data(context, data, len, &cert);
- if (ret)
+ cert = hx509_cert_init_data(context, data, len, &error);
+ if (cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
return ret;
+ }
ret = _hx509_collector_certs_add(context, c, cert);
hx509_cert_free(cert);
@@ -92,9 +96,10 @@ try_decrypt(hx509_context context,
password, passwordlen,
1, key, NULL);
if (ret <= 0) {
- hx509_set_error_string(context, 0, HX509_CRYPTO_INTERNAL_ERROR,
+ ret = HX509_CRYPTO_INTERNAL_ERROR;
+ hx509_set_error_string(context, 0, ret,
"Failed to do string2key for private key");
- return HX509_CRYPTO_INTERNAL_ERROR;
+ goto out;
}
clear.data = malloc(len);
@@ -315,7 +320,9 @@ struct pem_formats {
{ "CERTIFICATE", parse_certificate, NULL },
{ "PRIVATE KEY", parse_pkcs8_private_key, NULL },
{ "RSA PRIVATE KEY", parse_pem_private_key, hx509_signature_rsa },
+#ifdef HAVE_HCRYPTO_W_OPENSSL
{ "EC PRIVATE KEY", parse_pem_private_key, hx509_signature_ecPublicKey }
+#endif
};
diff --git a/lib/hx509/ks_keychain.c b/lib/hx509/ks_keychain.c
index 0552d8f7e97a..9b8224f1d237 100644
--- a/lib/hx509/ks_keychain.c
+++ b/lib/hx509/ks_keychain.c
@@ -35,6 +35,9 @@
#ifdef HAVE_FRAMEWORK_SECURITY
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
#include <Security/Security.h>
/* Missing function decls in pre Leopard */
@@ -246,6 +249,7 @@ static const RSA_METHOD kc_rsa_pkcs1_method = {
0,
NULL,
NULL,
+ NULL,
NULL
};
@@ -340,11 +344,13 @@ keychain_init(hx509_context context,
if (ret != noErr) {
hx509_set_error_string(context, 0, ENOENT,
"Failed to open %s", residue);
+ free(ctx);
return ENOENT;
}
} else {
hx509_set_error_string(context, 0, ENOENT,
"Unknown subtype %s", residue);
+ free(ctx);
return ENOENT;
}
}
@@ -420,8 +426,8 @@ keychain_iter_start(hx509_context context,
SecCertificateGetData(cr, &cssm);
- ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert);
- if (ret)
+ cert = hx509_cert_init_data(context, cssm.Data, cssm.Length, NULL);
+ if (cert == NULL)
continue;
ret = hx509_certs_add(context, iter->certs, cert);
@@ -470,6 +476,7 @@ keychain_iter(hx509_context context,
UInt32 attrFormat[1] = { 0 };
SecKeychainItemRef itemRef;
SecItemAttr item[1];
+ heim_error_t error = NULL;
struct iter *iter = cursor;
OSStatus ret;
UInt32 len;
@@ -501,9 +508,12 @@ keychain_iter(hx509_context context,
if (ret)
return EINVAL;
- ret = hx509_cert_init_data(context, ptr, len, cert);
- if (ret)
+ *cert = hx509_cert_init_data(context, ptr, len, &error);
+ if (*cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
goto out;
+ }
/*
* Find related private key if there is one by looking at
@@ -586,9 +596,14 @@ struct hx509_keyset_ops keyset_keychain = {
NULL,
keychain_iter_start,
keychain_iter,
- keychain_iter_end
+ keychain_iter_end,
+ NULL,
+ NULL,
+ NULL
};
+#pragma clang diagnostic pop
+
#endif /* HAVE_FRAMEWORK_SECURITY */
/*
diff --git a/lib/hx509/ks_null.c b/lib/hx509/ks_null.c
index 136d2d43459f..5ac0beb7bf91 100644
--- a/lib/hx509/ks_null.c
+++ b/lib/hx509/ks_null.c
@@ -87,7 +87,10 @@ struct hx509_keyset_ops keyset_null = {
NULL,
null_iter_start,
null_iter,
- null_iter_end
+ null_iter_end,
+ NULL,
+ NULL,
+ NULL
};
void
diff --git a/lib/hx509/ks_p11.c b/lib/hx509/ks_p11.c
index 120bf43ef437..1b2309e20d50 100644
--- a/lib/hx509/ks_p11.c
+++ b/lib/hx509/ks_p11.c
@@ -38,7 +38,7 @@
#ifdef HAVE_DLOPEN
-#include "pkcs11.h"
+#include "ref/pkcs11.h"
struct p11_slot {
int flags;
@@ -65,6 +65,7 @@ struct p11_module {
CK_FUNCTION_LIST_PTR funcs;
CK_ULONG num_slots;
unsigned int ref;
+ unsigned int selected_slot;
struct p11_slot *slot;
};
@@ -226,6 +227,7 @@ static const RSA_METHOD p11_rsa_pkcs1_method = {
0,
NULL,
NULL,
+ NULL,
NULL
};
@@ -330,8 +332,10 @@ p11_init_slot(hx509_context context,
break;
}
- asprintf(&slot->name, "%.*s",
- (int)i, slot_info.slotDescription);
+ ret = asprintf(&slot->name, "%.*s", (int)i,
+ slot_info.slotDescription);
+ if (ret == -1)
+ return ENOMEM;
if ((slot_info.flags & CKF_TOKEN_PRESENT) == 0)
return 0;
@@ -340,7 +344,7 @@ p11_init_slot(hx509_context context,
if (ret) {
hx509_set_error_string(context, 0, HX509_PKCS11_NO_TOKEN,
"Failed to init PKCS11 slot %d "
- "with error 0x08x",
+ "with error 0x%08x",
num, ret);
return HX509_PKCS11_NO_TOKEN;
}
@@ -422,7 +426,12 @@ p11_get_session(hx509_context context,
memset(&prompt, 0, sizeof(prompt));
- asprintf(&str, "PIN code for %s: ", slot->name);
+ ret = asprintf(&str, "PIN code for %s: ", slot->name);
+ if (ret == -1 || str == NULL) {
+ if (context)
+ hx509_set_error_string(context, 0, ENOMEM, "out of memory");
+ return ENOMEM;
+ }
prompt.prompt = str;
prompt.type = HX509_PROMPT_TYPE_PASSWORD;
prompt.reply.data = pin;
@@ -451,7 +460,18 @@ p11_get_session(hx509_context context,
"Failed to login on slot id %d "
"with error: 0x%08x",
(int)slot->id, ret);
- return HX509_PKCS11_LOGIN;
+ switch(ret) {
+ case CKR_PIN_LOCKED:
+ return HX509_PKCS11_PIN_LOCKED;
+ case CKR_PIN_EXPIRED:
+ return HX509_PKCS11_PIN_EXPIRED;
+ case CKR_PIN_INCORRECT:
+ return HX509_PKCS11_PIN_INCORRECT;
+ case CKR_USER_PIN_NOT_INITIALIZED:
+ return HX509_PKCS11_PIN_NOT_INITIALIZED;
+ default:
+ return HX509_PKCS11_LOGIN;
+ }
} else
slot->flags |= P11_LOGIN_DONE;
@@ -680,6 +700,7 @@ collect_cert(hx509_context context,
void *ptr, CK_ATTRIBUTE *query, int num_query)
{
struct hx509_collector *collector = ptr;
+ heim_error_t error = NULL;
hx509_cert cert;
int ret;
@@ -689,10 +710,13 @@ collect_cert(hx509_context context,
return 0;
}
- ret = hx509_cert_init_data(context, query[1].pValue,
- query[1].ulValueLen, &cert);
- if (ret)
+ cert = hx509_cert_init_data(context, query[1].pValue,
+ query[1].ulValueLen, &error);
+ if (cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
return ret;
+ }
if (p->ref == 0)
_hx509_abort("pkcs11 ref == 0 on alloc");
@@ -717,9 +741,9 @@ collect_cert(hx509_context context,
if ((CK_LONG)query[2].ulValueLen != -1) {
char *str;
- asprintf(&str, "%.*s",
- (int)query[2].ulValueLen, (char *)query[2].pValue);
- if (str) {
+ ret = asprintf(&str, "%.*s",
+ (int)query[2].ulValueLen, (char *)query[2].pValue);
+ if (ret != -1 && str) {
hx509_cert_set_friendly_name(cert, str);
free(str);
}
@@ -810,6 +834,7 @@ p11_init(hx509_context context,
}
p->ref = 1;
+ p->selected_slot = 0;
str = strchr(list, ',');
if (str)
@@ -819,15 +844,12 @@ p11_init(hx509_context context,
strnext = strchr(str, ',');
if (strnext)
*strnext++ = '\0';
-#if 0
if (strncasecmp(str, "slot=", 5) == 0)
p->selected_slot = atoi(str + 5);
-#endif
str = strnext;
}
p->dl_handle = dlopen(list, RTLD_NOW);
- free(list);
if (p->dl_handle == NULL) {
ret = HX509_PKCS11_LOAD;
hx509_set_error_string(context, 0, ret,
@@ -908,11 +930,13 @@ p11_init(hx509_context context,
}
for (i = 0; i < p->num_slots; i++) {
+ if ((p->selected_slot != 0) && (slot_ids[i] != (p->selected_slot - 1)))
+ continue;
ret = p11_init_slot(context, p, lock, slot_ids[i], i, &p->slot[i]);
- if (ret)
- break;
- if (p->slot[i].flags & P11_TOKEN_PRESENT)
- num_tokens++;
+ if (!ret) {
+ if (p->slot[i].flags & P11_TOKEN_PRESENT)
+ num_tokens++;
+ }
}
free(slot_ids);
if (ret)
@@ -923,10 +947,14 @@ p11_init(hx509_context context,
}
}
+ free(list);
+
*data = p;
return 0;
out:
+ if (list)
+ free(list);
p11_release_module(p);
return ret;
}
@@ -1176,7 +1204,9 @@ static struct hx509_keyset_ops keyset_pkcs11 = {
p11_iter_start,
p11_iter,
p11_iter_end,
- p11_printinfo
+ p11_printinfo,
+ NULL,
+ NULL
};
#endif /* HAVE_DLOPEN */
diff --git a/lib/hx509/ks_p12.c b/lib/hx509/ks_p12.c
index 0ca13de1eb34..b7df0be32aca 100644
--- a/lib/hx509/ks_p12.c
+++ b/lib/hx509/ks_p12.c
@@ -130,6 +130,7 @@ certBag_parser(hx509_context context,
const void *data, size_t length,
const PKCS12_Attributes *attrs)
{
+ heim_error_t error = NULL;
heim_octet_string os;
hx509_cert cert;
PKCS12_CertBag cb;
@@ -152,10 +153,13 @@ certBag_parser(hx509_context context,
if (ret)
return ret;
- ret = hx509_cert_init_data(context, os.data, os.length, &cert);
+ cert = hx509_cert_init_data(context, os.data, os.length, &error);
der_free_octet_string(&os);
- if (ret)
+ if (cert == NULL) {
+ ret = heim_error_get_code(error);
+ heim_release(error);
return ret;
+ }
ret = _hx509_collector_certs_add(context, c, cert);
if (ret) {
@@ -697,7 +701,10 @@ static struct hx509_keyset_ops keyset_pkcs12 = {
NULL,
p12_iter_start,
p12_iter,
- p12_iter_end
+ p12_iter_end,
+ NULL,
+ NULL,
+ NULL
};
void
diff --git a/lib/hx509/libhx509-exports.def b/lib/hx509/libhx509-exports.def
index f8973a091396..f4417730158c 100644
--- a/lib/hx509/libhx509-exports.def
+++ b/lib/hx509/libhx509-exports.def
@@ -1,3 +1,4 @@
+
EXPORTS
_hx509_cert_assign_key
_hx509_cert_private_key
@@ -51,6 +52,7 @@ EXPORTS
hx509_ca_tbs_set_notBefore
hx509_ca_tbs_set_proxy
hx509_ca_tbs_set_serialnumber
+ hx509_ca_tbs_set_signature_algorithm
hx509_ca_tbs_set_spki
hx509_ca_tbs_set_subject
hx509_ca_tbs_set_template
@@ -196,6 +198,7 @@ EXPORTS
hx509_revoke_free
hx509_revoke_init
hx509_revoke_ocsp_print
+ hx509_revoke_print
hx509_revoke_verify
hx509_set_error_string
hx509_set_error_stringv
diff --git a/lib/hx509/lock.c b/lib/hx509/lock.c
index b72d45962b62..52f72dba1b71 100644
--- a/lib/hx509/lock.c
+++ b/lib/hx509/lock.c
@@ -47,7 +47,10 @@ struct hx509_lock_data {
};
static struct hx509_lock_data empty_lock_data = {
- { 0, NULL }
+ { 0, NULL },
+ NULL,
+ NULL,
+ NULL
};
hx509_lock _hx509_empty_lock = &empty_lock_data;
diff --git a/lib/hx509/name.c b/lib/hx509/name.c
index efd7b703422f..ee192e593a90 100644
--- a/lib/hx509/name.c
+++ b/lib/hx509/name.c
@@ -238,15 +238,22 @@ _hx509_Name_to_string(const Name *n, char **str)
size_t k;
ret = wind_ucs2utf8_length(bmp, bmplen, &k);
- if (ret)
+ if (ret) {
+ free(oidname);
+ free(*str);
+ *str = NULL;
return ret;
+ }
ss = malloc(k + 1);
if (ss == NULL)
_hx509_abort("allocation failure"); /* XXX */
ret = wind_ucs2utf8(bmp, bmplen, ss, NULL);
if (ret) {
+ free(oidname);
free(ss);
+ free(*str);
+ *str = NULL;
return ret;
}
ss[k] = '\0';
@@ -263,8 +270,12 @@ _hx509_Name_to_string(const Name *n, char **str)
size_t k;
ret = wind_ucs4utf8_length(uni, unilen, &k);
- if (ret)
+ if (ret) {
+ free(oidname);
+ free(*str);
+ *str = NULL;
return ret;
+ }
ss = malloc(k + 1);
if (ss == NULL)
@@ -272,6 +283,9 @@ _hx509_Name_to_string(const Name *n, char **str)
ret = wind_ucs4utf8(uni, unilen, ss, NULL);
if (ret) {
free(ss);
+ free(oidname);
+ free(*str);
+ *str = NULL;
return ret;
}
ss[k] = '\0';
@@ -966,7 +980,7 @@ hx509_general_name_unparse(GeneralName *name, char **str)
char *s;
int ret;
memset(&dir, 0, sizeof(dir));
- dir.element = name->u.directoryName.element;
+ dir.element = (enum Name_enum)name->u.directoryName.element;
dir.u.rdnSequence = name->u.directoryName.u.rdnSequence;
ret = _hx509_unparse_Name(&dir, &s);
if (ret)
diff --git a/lib/hx509/print.c b/lib/hx509/print.c
index 1e8bcabfa7e9..4d2c3e2a421d 100644
--- a/lib/hx509/print.c
+++ b/lib/hx509/print.c
@@ -969,7 +969,7 @@ hx509_validate_cert(hx509_context context,
}
validate_print(ctx,
HX509_VALIDATE_F_VALIDATE|HX509_VALIDATE_F_VERBOSE,
- "checking extention: %s\n",
+ "checking extension: %s\n",
check_extension[j].name);
(*check_extension[j].func)(ctx,
&status,
@@ -977,7 +977,7 @@ hx509_validate_cert(hx509_context context,
&t->extensions->val[i]);
}
} else
- validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "no extentions\n");
+ validate_print(ctx, HX509_VALIDATE_F_VERBOSE, "no extensions\n");
if (status.isca) {
if (!status.haveSKI)
diff --git a/lib/hx509/ref/pkcs11.h b/lib/hx509/ref/pkcs11.h
index 2e6a1e3ed307..a294c5e94e5c 100644
--- a/lib/hx509/ref/pkcs11.h
+++ b/lib/hx509/ref/pkcs11.h
@@ -64,8 +64,9 @@ extern "C" {
(you may use a macro with a different name to keep track of your
versions). */
#define CRYPTOKI_VERSION_MAJOR 2
-#define CRYPTOKI_VERSION_MINOR 20
-#define CRYPTOKI_VERSION_REVISION 6
+#define CRYPTOKI_VERSION_MINOR 30
+#define CRYPTOKI_VERSION_REVISION 0
+#define CRYPTOKI_VERSION_AMENDMENT 0
/* Compatibility interface is default, unless CRYPTOKI_GNU is
@@ -91,7 +92,11 @@ extern "C" {
#else
+#if defined(CRYPTOKI_VISIBILITY) && defined(CRYPTOKI_EXPORTS)
+#define CK_SPEC __attribute__((visibility("default")))
+#else
#define CK_SPEC
+#endif
#endif
@@ -162,6 +167,34 @@ extern "C" {
#define min_key_size ulMinKeySize
#define max_key_size ulMaxKeySize
+#define hash_alg hashAlg
+#define source_data pSourceData
+#define source_data_len ulSourceDataLen
+
+#define slen sLen
+
+#define ck_ec_kdf_type_t CK_EC_KDF_TYPE
+
+#define shared_data_len ulSharedDataLen
+#define shared_data pSharedData
+#define public_data_len ulPublicDataLen
+#define public_data pPublicData
+
+#define private_data_len ulPrivateDataLen
+#define private_data hPrivateData
+#define public_data_len2 ulPublicDataLen2
+#define public_data2 pPublicData2
+
+#define public_key publicKey
+
+#define ck_x9_42_dh_kdf_type_t CK_X9_42_DH_KDF_TYPE
+
+#define other_info_len ulOtherInfoLen
+#define other_info pOtherInfo
+
+#define data pData
+#define len ulLen
+
#define ck_rv_t CK_RV
#define ck_notify_t CK_NOTIFY
@@ -205,7 +238,7 @@ struct ck_info
typedef unsigned long ck_notification_t;
#define CKN_SURRENDER (0)
-
+#define CKN_OTP_CHANGED (1)
typedef unsigned long ck_slot_id_t;
@@ -267,6 +300,7 @@ struct ck_token_info
#define CKF_SO_PIN_FINAL_TRY (1 << 21)
#define CKF_SO_PIN_LOCKED (1 << 22)
#define CKF_SO_PIN_TO_BE_CHANGED (1 << 23)
+#define CKF_ERROR_STATE (1 << 24)
#define CK_UNAVAILABLE_INFORMATION ((unsigned long) -1)
#define CK_EFFECTIVELY_INFINITE (0)
@@ -318,7 +352,8 @@ typedef unsigned long ck_object_class_t;
#define CKO_HW_FEATURE (5)
#define CKO_DOMAIN_PARAMETERS (6)
#define CKO_MECHANISM (7)
-#define CKO_VENDOR_DEFINED ((unsigned long) (1 << 31))
+#define CKO_OTP_KEY (8)
+#define CKO_VENDOR_DEFINED ((unsigned long) (1ul << 31))
typedef unsigned long ck_hw_feature_type_t;
@@ -326,7 +361,7 @@ typedef unsigned long ck_hw_feature_type_t;
#define CKH_MONOTONIC_COUNTER (1)
#define CKH_CLOCK (2)
#define CKH_USER_INTERFACE (3)
-#define CKH_VENDOR_DEFINED ((unsigned long) (1 << 31))
+#define CKH_VENDOR_DEFINED ((unsigned long) (1ul << 31))
typedef unsigned long ck_key_type_t;
@@ -346,6 +381,7 @@ typedef unsigned long ck_key_type_t;
#define CKK_DES3 (0x15)
#define CKK_CAST (0x16)
#define CKK_CAST3 (0x17)
+#define CKK_CAST5 (0x18)
#define CKK_CAST128 (0x18)
#define CKK_RC5 (0x19)
#define CKK_IDEA (0x1a)
@@ -356,7 +392,24 @@ typedef unsigned long ck_key_type_t;
#define CKK_AES (0x1f)
#define CKK_BLOWFISH (0x20)
#define CKK_TWOFISH (0x21)
-#define CKK_VENDOR_DEFINED ((unsigned long) (1 << 31))
+#define CKK_SECURID (0x22)
+#define CKK_HOTP (0x23)
+#define CKK_ACTI (0x24)
+#define CKK_CAMELLIA (0x25)
+#define CKK_ARIA (0x26)
+#define CKK_MD5_HMAC (0x27)
+#define CKK_SHA_1_HMAC (0x28)
+#define CKK_RIPEMD128_HMAC (0x29)
+#define CKK_RIPEMD160_HMAC (0x2A)
+#define CKK_SHA256_HMAC (0x2B)
+#define CKK_SHA384_HMAC (0x2C)
+#define CKK_SHA512_HMAC (0x2D)
+#define CKK_SHA224_HMAC (0x2E)
+#define CKK_SEED (0x2F)
+#define CKK_GOSTR3410 (0x30)
+#define CKK_GOSTR3411 (0x31)
+#define CKK_GOST28147 (0x32)
+#define CKK_VENDOR_DEFINED ((unsigned long) (1ul << 31))
typedef unsigned long ck_certificate_type_t;
@@ -364,8 +417,17 @@ typedef unsigned long ck_certificate_type_t;
#define CKC_X_509 (0)
#define CKC_X_509_ATTR_CERT (1)
#define CKC_WTLS (2)
-#define CKC_VENDOR_DEFINED ((unsigned long) (1 << 31))
+#define CKC_VENDOR_DEFINED ((unsigned long) (1ul << 31))
+#define CKC_OPENPGP (CKC_VENDOR_DEFINED|0x00504750)
+
+#define CK_OTP_FORMAT_DECIMAL (0)
+#define CK_OTP_FORMAT_HEXADECIMAL (1)
+#define CK_OTP_FORMAT_ALPHANUMERIC (2)
+#define CK_OTP_FORMAT_BINARY (3)
+#define CK_OTP_PARAM_IGNORED (0)
+#define CK_OTP_PARAM_OPTIONAL (1)
+#define CK_OTP_PARAM_MANDATORY (2)
typedef unsigned long ck_attribute_type_t;
@@ -388,6 +450,7 @@ typedef unsigned long ck_attribute_type_t;
#define CKA_URL (0x89)
#define CKA_HASH_OF_SUBJECT_PUBLIC_KEY (0x8a)
#define CKA_HASH_OF_ISSUER_PUBLIC_KEY (0x8b)
+#define CKA_NAME_HASH_ALGORITHM (0x8c)
#define CKA_CHECK_VALUE (0x90)
#define CKA_KEY_TYPE (0x100)
#define CKA_SUBJECT (0x101)
@@ -418,6 +481,7 @@ typedef unsigned long ck_attribute_type_t;
#define CKA_BASE (0x132)
#define CKA_PRIME_BITS (0x133)
#define CKA_SUB_PRIME_BITS (0x134)
+#define CKA_SUBPRIME_BITS (0x134)
#define CKA_VALUE_BITS (0x160)
#define CKA_VALUE_LEN (0x161)
#define CKA_EXTRACTABLE (0x162)
@@ -426,6 +490,7 @@ typedef unsigned long ck_attribute_type_t;
#define CKA_ALWAYS_SENSITIVE (0x165)
#define CKA_KEY_GEN_MECHANISM (0x166)
#define CKA_MODIFIABLE (0x170)
+#define CKA_COPYABLE (0x171)
#define CKA_ECDSA_PARAMS (0x180)
#define CKA_EC_PARAMS (0x180)
#define CKA_EC_POINT (0x181)
@@ -433,6 +498,23 @@ typedef unsigned long ck_attribute_type_t;
#define CKA_AUTH_PIN_FLAGS (0x201)
#define CKA_ALWAYS_AUTHENTICATE (0x202)
#define CKA_WRAP_WITH_TRUSTED (0x210)
+#define CKA_OTP_FORMAT (0x220)
+#define CKA_OTP_LENGTH (0x221)
+#define CKA_OTP_TIME_INTERVAL (0x222)
+#define CKA_OTP_USER_FRIENDLY_MODE (0x223)
+#define CKA_OTP_CHALLENGE_REQUIREMENT (0x224)
+#define CKA_OTP_TIME_REQUIREMENT (0x225)
+#define CKA_OTP_COUNTER_REQUIREMENT (0x226)
+#define CKA_OTP_PIN_REQUIREMENT (0x227)
+#define CKA_OTP_COUNTER (0x22E)
+#define CKA_OTP_TIME (0x22F)
+#define CKA_OTP_USER_IDENTIFIER (0x22A)
+#define CKA_OTP_SERVICE_IDENTIFIER (0x22B)
+#define CKA_OTP_SERVICE_LOGO (0x22C)
+#define CKA_OTP_SERVICE_LOGO_TYPE (0x22D)
+#define CKA_GOSTR3410_PARAMS (0x250)
+#define CKA_GOSTR3411_PARAMS (0x251)
+#define CKA_GOST28147_PARAMS (0x252)
#define CKA_HW_FEATURE_TYPE (0x300)
#define CKA_RESET_ON_INIT (0x301)
#define CKA_HAS_RESET (0x302)
@@ -452,8 +534,9 @@ typedef unsigned long ck_attribute_type_t;
#define CKA_SUPPORTED_CMS_ATTRIBUTES (0x503)
#define CKA_WRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x211)
#define CKA_UNWRAP_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x212)
+#define CKA_DERIVE_TEMPLATE (CKF_ARRAY_ATTRIBUTE | 0x213)
#define CKA_ALLOWED_MECHANISMS (CKF_ARRAY_ATTRIBUTE | 0x600)
-#define CKA_VENDOR_DEFINED ((unsigned long) (1 << 31))
+#define CKA_VENDOR_DEFINED ((unsigned long) (1ul << 31))
struct ck_attribute
@@ -492,6 +575,10 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_DSA_KEY_PAIR_GEN (0x10)
#define CKM_DSA (0x11)
#define CKM_DSA_SHA1 (0x12)
+#define CKM_DSA_SHA224 (0x13)
+#define CKM_DSA_SHA256 (0x14)
+#define CKM_DSA_SHA384 (0x15)
+#define CKM_DSA_SHA512 (0x16)
#define CKM_DH_PKCS_KEY_PAIR_GEN (0x20)
#define CKM_DH_PKCS_DERIVE (0x21)
#define CKM_X9_42_DH_KEY_PAIR_GEN (0x30)
@@ -504,6 +591,8 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_SHA256_RSA_PKCS_PSS (0x43)
#define CKM_SHA384_RSA_PKCS_PSS (0x44)
#define CKM_SHA512_RSA_PKCS_PSS (0x45)
+#define CKM_SHA224_RSA_PKCS (0x46)
+#define CKM_SHA224_RSA_PKCS_PSS (0x47)
#define CKM_RC2_KEY_GEN (0x100)
#define CKM_RC2_ECB (0x101)
#define CKM_RC2_CBC (0x102)
@@ -525,12 +614,18 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_DES3_MAC (0x134)
#define CKM_DES3_MAC_GENERAL (0x135)
#define CKM_DES3_CBC_PAD (0x136)
+#define CKM_DES3_CMAC_GENERAL (0x137)
+#define CKM_DES3_CMAC (0x138)
#define CKM_CDMF_KEY_GEN (0x140)
#define CKM_CDMF_ECB (0x141)
#define CKM_CDMF_CBC (0x142)
#define CKM_CDMF_MAC (0x143)
#define CKM_CDMF_MAC_GENERAL (0x144)
#define CKM_CDMF_CBC_PAD (0x145)
+#define CKM_DES_OFB64 (0x150)
+#define CKM_DES_OFB8 (0x151)
+#define CKM_DES_CFB64 (0x152)
+#define CKM_DES_CFB8 (0x153)
#define CKM_MD2 (0x200)
#define CKM_MD2_HMAC (0x201)
#define CKM_MD2_HMAC_GENERAL (0x202)
@@ -549,12 +644,21 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_SHA256 (0x250)
#define CKM_SHA256_HMAC (0x251)
#define CKM_SHA256_HMAC_GENERAL (0x252)
+#define CKM_SHA224 (0x255)
+#define CKM_SHA224_HMAC (0x256)
+#define CKM_SHA224_HMAC_GENERAL (0x257)
#define CKM_SHA384 (0x260)
#define CKM_SHA384_HMAC (0x261)
#define CKM_SHA384_HMAC_GENERAL (0x262)
#define CKM_SHA512 (0x270)
#define CKM_SHA512_HMAC (0x271)
#define CKM_SHA512_HMAC_GENERAL (0x272)
+#define CKM_SECURID_KEY_GEN (0x280)
+#define CKM_SECURID (0x282)
+#define CKM_HOTP_KEY_GEN (0x290)
+#define CKM_HOTP (0x291)
+#define CKM_ACTI (0x2A0)
+#define CKM_ACTI_KEY_GEN (0x2A1)
#define CKM_CAST_KEY_GEN (0x300)
#define CKM_CAST_ECB (0x301)
#define CKM_CAST_CBC (0x302)
@@ -605,11 +709,16 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_TLS_MASTER_KEY_DERIVE (0x375)
#define CKM_TLS_KEY_AND_MAC_DERIVE (0x376)
#define CKM_TLS_MASTER_KEY_DERIVE_DH (0x377)
+#define CKM_TLS_PRF (0x378)
#define CKM_SSL3_MD5_MAC (0x380)
#define CKM_SSL3_SHA1_MAC (0x381)
#define CKM_MD5_KEY_DERIVATION (0x390)
#define CKM_MD2_KEY_DERIVATION (0x391)
#define CKM_SHA1_KEY_DERIVATION (0x392)
+#define CKM_SHA256_KEY_DERIVATION (0x393)
+#define CKM_SHA384_KEY_DERIVATION (0x394)
+#define CKM_SHA512_KEY_DERIVATION (0x395)
+#define CKM_SHA224_KEY_DERIVATION (0x396)
#define CKM_PBE_MD2_DES_CBC (0x3a0)
#define CKM_PBE_MD5_DES_CBC (0x3a1)
#define CKM_PBE_MD5_CAST_CBC (0x3a2)
@@ -626,8 +735,43 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_PBE_SHA1_RC2_40_CBC (0x3ab)
#define CKM_PKCS5_PBKD2 (0x3b0)
#define CKM_PBA_SHA1_WITH_SHA1_HMAC (0x3c0)
+#define CKM_WTLS_PRE_MASTER_KEY_GEN (0x3d0)
+#define CKM_WTLS_MASTER_KEY_DERIVE (0x3d1)
+#define CKM_WTLS_MASTER_KEY_DERIVE_DH_ECC (0x3d2)
+#define CKM_WTLS_PRF (0x3d3)
+#define CKM_WTLS_SERVER_KEY_AND_MAC_DERIVE (0x3d4)
+#define CKM_WTLS_CLIENT_KEY_AND_MAC_DERIVE (0x3d5)
#define CKM_KEY_WRAP_LYNKS (0x400)
#define CKM_KEY_WRAP_SET_OAEP (0x401)
+#define CKM_CMS_SIG (0x500)
+#define CKM_KIP_DERIVE (0x510)
+#define CKM_KIP_WRAP (0x511)
+#define CKM_KIP_MAC (0x512)
+#define CKM_CAMELLIA_KEY_GEN (0x550)
+#define CKM_CAMELLIA_ECB (0x551)
+#define CKM_CAMELLIA_CBC (0x552)
+#define CKM_CAMELLIA_MAC (0x553)
+#define CKM_CAMELLIA_MAC_GENERAL (0x554)
+#define CKM_CAMELLIA_CBC_PAD (0x555)
+#define CKM_CAMELLIA_ECB_ENCRYPT_DATA (0x556)
+#define CKM_CAMELLIA_CBC_ENCRYPT_DATA (0x557)
+#define CKM_CAMELLIA_CTR (0x558)
+#define CKM_ARIA_KEY_GEN (0x560)
+#define CKM_ARIA_ECB (0x561)
+#define CKM_ARIA_CBC (0x562)
+#define CKM_ARIA_MAC (0x563)
+#define CKM_ARIA_MAC_GENERAL (0x564)
+#define CKM_ARIA_CBC_PAD (0x565)
+#define CKM_ARIA_ECB_ENCRYPT_DATA (0x566)
+#define CKM_ARIA_CBC_ENCRYPT_DATA (0x567)
+#define CKM_SEED_KEY_GEN (0x650)
+#define CKM_SEED_ECB (0x651)
+#define CKM_SEED_CBC (0x652)
+#define CKM_SEED_MAC (0x653)
+#define CKM_SEED_MAC_GENERAL (0x654)
+#define CKM_SEED_CBC_PAD (0x655)
+#define CKM_SEED_ECB_ENCRYPT_DATA (0x656)
+#define CKM_SEED_CBC_ENCRYPT_DATA (0x657)
#define CKM_SKIPJACK_KEY_GEN (0x1000)
#define CKM_SKIPJACK_ECB64 (0x1001)
#define CKM_SKIPJACK_CBC64 (0x1002)
@@ -653,6 +797,10 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_EC_KEY_PAIR_GEN (0x1040)
#define CKM_ECDSA (0x1041)
#define CKM_ECDSA_SHA1 (0x1042)
+#define CKM_ECDSA_SHA224 (0x1043)
+#define CKM_ECDSA_SHA256 (0x1044)
+#define CKM_ECDSA_SHA384 (0x1045)
+#define CKM_ECDSA_SHA512 (0x1046)
#define CKM_ECDH1_DERIVE (0x1050)
#define CKM_ECDH1_COFACTOR_DERIVE (0x1051)
#define CKM_ECMQV_DERIVE (0x1052)
@@ -669,10 +817,48 @@ typedef unsigned long ck_mechanism_type_t;
#define CKM_AES_MAC (0x1083)
#define CKM_AES_MAC_GENERAL (0x1084)
#define CKM_AES_CBC_PAD (0x1085)
+#define CKM_AES_CTR (0x1086)
+#define CKM_AES_GCM (0x1087)
+#define CKM_AES_CCM (0x1088)
+#define CKM_AES_CTS (0x1089)
+#define CKM_AES_CMAC (0x108a)
+#define CKM_AES_CMAC_GENERAL (0x108b)
+#define CKM_BLOWFISH_KEY_GEN (0x1090)
+#define CKM_BLOWFISH_CBC (0x1091)
+#define CKM_TWOFISH_KEY_GEN (0x1092)
+#define CKM_TWOFISH_CBC (0x1093)
+#define CKM_BLOWFISH_CBC_PAD (0x1094)
+#define CKM_TWOFISH_CBC_PAD (0x1095)
+#define CKM_DES_ECB_ENCRYPT_DATA (0x1100)
+#define CKM_DES_CBC_ENCRYPT_DATA (0x1101)
+#define CKM_DES3_ECB_ENCRYPT_DATA (0x1102)
+#define CKM_DES3_CBC_ENCRYPT_DATA (0x1103)
+#define CKM_AES_ECB_ENCRYPT_DATA (0x1104)
+#define CKM_AES_CBC_ENCRYPT_DATA (0x1105)
+#define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200)
+#define CKM_GOSTR3410 (0x1201)
+#define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202)
+#define CKM_GOSTR3410_KEY_WRAP (0x1203)
+#define CKM_GOSTR3410_DERIVE (0x1204)
+#define CKM_GOSTR3411 (0x1210)
+#define CKM_GOSTR3411_HMAC (0x1211)
+#define CKM_GOST28147_KEY_GEN (0x1220)
+#define CKM_GOST28147_ECB (0x1221)
+#define CKM_GOST28147 (0x1222)
+#define CKM_GOST28147_MAC (0x1223)
+#define CKM_GOST28147_KEY_WRAP (0x1224)
#define CKM_DSA_PARAMETER_GEN (0x2000)
#define CKM_DH_PKCS_PARAMETER_GEN (0x2001)
#define CKM_X9_42_DH_PARAMETER_GEN (0x2002)
-#define CKM_VENDOR_DEFINED ((unsigned long) (1 << 31))
+#define CKM_AES_OFB (0x2104)
+#define CKM_AES_CFB64 (0x2105)
+#define CKM_AES_CFB8 (0x2106)
+#define CKM_AES_CFB128 (0x2107)
+#define CKM_AES_KEY_WRAP (0x2109)
+#define CKM_AES_KEY_WRAP_PAD (0x210a)
+#define CKM_RSA_PKCS_TPM_1_1 (0x4001)
+#define CKM_RSA_PKCS_OAEPTPM_1_1 (0x4002)
+#define CKM_VENDOR_DEFINED ((unsigned long) (1ul << 31))
struct ck_mechanism
@@ -703,8 +889,137 @@ struct ck_mechanism_info
#define CKF_WRAP (1 << 17)
#define CKF_UNWRAP (1 << 18)
#define CKF_DERIVE (1 << 19)
-#define CKF_EXTENSION ((unsigned long) (1 << 31))
+#define CKF_EC_F_P (1 << 20)
+#define CKF_EC_F_2M (1 << 21)
+#define CKF_EC_ECPARAMETERS (1 << 22)
+#define CKF_EC_NAMEDCURVE (1 << 23)
+#define CKF_EC_UNCOMPRESS (1 << 24)
+#define CKF_EC_COMPRESS (1 << 25)
+#define CKF_EXTENSION ((unsigned long) (1ul << 31))
+
+
+/* The following MGFs are defined */
+#define CKG_MGF1_SHA1 (0x00000001)
+#define CKG_MGF1_SHA256 (0x00000002)
+#define CKG_MGF1_SHA384 (0x00000003)
+#define CKG_MGF1_SHA512 (0x00000004)
+#define CKG_MGF1_SHA224 (0x00000005)
+
+#define CKZ_DATA_SPECIFIED (0x00000001)
+
+struct ck_rsa_pkcs_oaep_params {
+ ck_mechanism_type_t hash_alg;
+ unsigned long mgf;
+ unsigned long source;
+ void *source_data;
+ unsigned long source_data_len;
+};
+
+struct ck_rsa_pkcs_pss_params {
+ ck_mechanism_type_t hash_alg;
+ unsigned long mgf;
+ unsigned long slen;
+};
+
+typedef unsigned long ck_ec_kdf_type_t;
+
+/* The following EC Key Derivation Functions are defined */
+#define CKD_NULL (0x00000001)
+#define CKD_SHA1_KDF (0x00000002)
+
+struct ck_ecdh1_derive_params {
+ ck_ec_kdf_type_t kdf;
+ unsigned long shared_data_len;
+ unsigned char *shared_data;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+};
+
+struct ck_ecdh2_derive_params {
+ ck_ec_kdf_type_t kdf;
+ unsigned long shared_data_len;
+ unsigned char *shared_data;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+};
+
+struct ck_ecmqv_derive_params {
+ ck_ec_kdf_type_t kdf;
+ unsigned long shared_data_len;
+ unsigned char *shared_data;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+ ck_object_handle_t public_key;
+};
+typedef unsigned long ck_x9_42_dh_kdf_type_t;
+
+/* The following X9.42 DH key derivation functions are defined */
+#define CKD_SHA1_KDF_ASN1 (0x00000003)
+#define CKD_SHA1_KDF_CONCATENATE (0x00000004)
+#define CKD_SHA224_KDF (0x00000005)
+#define CKD_SHA256_KDF (0x00000006)
+#define CKD_SHA384_KDF (0x00000007)
+#define CKD_SHA512_KDF (0x00000008)
+#define CKD_CPDIVERSIFY_KDF (0x00000009)
+
+struct ck_x9_42_dh1_derive_params {
+ ck_x9_42_dh_kdf_type_t kdf;
+ unsigned long other_info_len;
+ unsigned char *other_info;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+};
+
+struct ck_x9_42_dh2_derive_params {
+ ck_x9_42_dh_kdf_type_t kdf;
+ unsigned long other_info_len;
+ unsigned char *other_info;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+};
+
+struct ck_x9_42_mqv_derive_params {
+ ck_x9_42_dh_kdf_type_t kdf;
+ unsigned long other_info_len;
+ unsigned char *other_info;
+ unsigned long public_data_len;
+ unsigned char *public_data;
+ unsigned long private_data_len;
+ ck_object_handle_t private_data;
+ unsigned long public_data_len2;
+ unsigned char *public_data2;
+ ck_object_handle_t public_key;
+};
+
+struct ck_des_cbc_encrypt_data_params {
+ unsigned char iv[8];
+ unsigned char *data;
+ unsigned long length;
+};
+
+struct ck_aes_cbc_encrypt_data_params {
+ unsigned char iv[16];
+ unsigned char *data;
+ unsigned long length;
+};
+
+struct ck_key_derivation_string_data {
+ unsigned char *data;
+ unsigned long len;
+};
/* Flags for C_WaitForSlotEvent. */
#define CKF_DONT_BLOCK (1)
@@ -1108,6 +1423,7 @@ struct ck_c_initialize_args
#define CKR_ATTRIBUTE_SENSITIVE (0x11)
#define CKR_ATTRIBUTE_TYPE_INVALID (0x12)
#define CKR_ATTRIBUTE_VALUE_INVALID (0x13)
+#define CKR_COPY_PROHIBITED (0x1A)
#define CKR_DATA_INVALID (0x20)
#define CKR_DATA_LEN_RANGE (0x21)
#define CKR_DEVICE_ERROR (0x30)
@@ -1178,8 +1494,15 @@ struct ck_c_initialize_args
#define CKR_CRYPTOKI_ALREADY_INITIALIZED (0x191)
#define CKR_MUTEX_BAD (0x1a0)
#define CKR_MUTEX_NOT_LOCKED (0x1a1)
+#define CKR_NEW_PIN_MODE (0x1b0)
+#define CKR_NEXT_OTP (0x1b1)
+#define CKR_EXCEEDED_MAX_ITERATIONS (0x1b5)
+#define CKR_FIPS_SELF_TEST_FAILED (0x1b6)
+#define CKR_LIBRARY_LOAD_FAILED (0x1b7)
+#define CKR_PIN_TOO_WEAK (0x1b8)
+#define CKR_PUBLIC_KEY_INVALID (0x1b9)
#define CKR_FUNCTION_REJECTED (0x200)
-#define CKR_VENDOR_DEFINED ((unsigned long) (1 << 31))
+#define CKR_VENDOR_DEFINED ((unsigned long) (1ul << 31))
@@ -1253,6 +1576,24 @@ typedef struct ck_mechanism *CK_MECHANISM_PTR;
typedef struct ck_mechanism_info CK_MECHANISM_INFO;
typedef struct ck_mechanism_info *CK_MECHANISM_INFO_PTR;
+typedef struct ck_rsa_pkcs_oaep_params CK_RSA_PKCS_OAEP_PARAMS;
+typedef struct ck_rsa_pkcs_oaep_params *CK_RSA_PKCS_OAEP_PARAMS_PTR;
+
+typedef struct ck_rsa_pkcs_pss_params CK_RSA_PKCS_PSS_PARAMS;
+typedef struct ck_rsa_pkcs_pss_params *CK_RSA_PKCS_PSS_PARAMS_PTR;
+
+typedef struct ck_ecdh1_derive_params CK_ECDH1_DERIVE_PARAMS;
+typedef struct ck_ecdh1_derive_params *CK_ECDH1_DERIVE_PARAMS_PTR;
+
+typedef struct ck_des_cbc_encrypt_data_params CK_DES_CBC_ENCRYPT_DATA_PARAMS;
+typedef struct ck_des_cbc_encrypt_data_params *CK_DES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct ck_aes_cbc_encrypt_data_params CK_AES_CBC_ENCRYPT_DATA_PARAMS;
+typedef struct ck_aes_cbc_encrypt_data_params *CK_AES_CBC_ENCRYPT_DATA_PARAMS_PTR;
+
+typedef struct ck_key_derivation_string_data CK_KEY_DERIVATION_STRING_DATA;
+typedef struct ck_key_derivation_string_data *CK_KEY_DERIVATION_STRING_DATA_PTR;
+
typedef struct ck_function_list CK_FUNCTION_LIST;
typedef struct ck_function_list *CK_FUNCTION_LIST_PTR;
typedef struct ck_function_list **CK_FUNCTION_LIST_PTR_PTR;
@@ -1325,6 +1666,30 @@ typedef struct ck_c_initialize_args *CK_C_INITIALIZE_ARGS_PTR;
#undef min_key_size
#undef max_key_size
+#undef ck_rsa_pkcs_oaep_params
+#undef hash_alg
+#undef source_data
+#undef source_data_len
+#undef slen
+
+#undef ck_ec_kdf_type_t
+#undef shared_data_len
+#undef shared_data
+#undef public_data_len
+#undef public_data
+#undef private_data_len
+#undef private_data
+#undef public_data_len2
+#undef public_data2
+#undef public_key
+
+#undef ck_x9_42_dh_kdf_type_t
+#undef other_info_len
+#undef other_info
+
+#undef data
+#undef len
+
#undef ck_rv_t
#undef ck_notify_t
diff --git a/lib/hx509/revoke.c b/lib/hx509/revoke.c
index 29322807487c..a777226db29e 100644
--- a/lib/hx509/revoke.c
+++ b/lib/hx509/revoke.c
@@ -337,8 +337,10 @@ load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
return ret;
ret = stat(ocsp->path, &sb);
- if (ret)
+ if (ret) {
+ rk_xfree(data);
return errno;
+ }
ret = parse_ocsp_basic(data, length, &basic);
rk_xfree(data);
@@ -361,8 +363,8 @@ load_ocsp(hx509_context context, struct revoke_ocsp *ocsp)
for (i = 0; i < basic.certs->len; i++) {
hx509_cert c;
- ret = hx509_cert_init(context, &basic.certs->val[i], &c);
- if (ret)
+ c = hx509_cert_init(context, &basic.certs->val[i], NULL);
+ if (c == NULL)
continue;
ret = hx509_certs_add(context, certs, c);
@@ -561,36 +563,65 @@ out:
}
static int
-load_crl(const char *path, time_t *t, CRLCertificateList *crl)
+crl_parser(hx509_context context, const char *type,
+ const hx509_pem_header *header,
+ const void *data, size_t len, void *ctx)
{
- size_t length, size;
- struct stat sb;
- void *data;
+ CRLCertificateList *crl = (CRLCertificateList *)ctx;
+ size_t size;
int ret;
- memset(crl, 0, sizeof(*crl));
+ if (strcasecmp("X509 CRL", type) != 0)
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
- ret = rk_undumpdata(path, &data, &length);
+ ret = decode_CRLCertificateList(data, len, crl, &size);
if (ret)
return ret;
+ /* check signature is aligned */
+ if (crl->signatureValue.length & 7) {
+ free_CRLCertificateList(crl);
+ return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ }
+
+ return 0;
+}
+
+static int
+load_crl(hx509_context context, const char *path, time_t *t, CRLCertificateList *crl)
+{
+ struct stat sb;
+ size_t length;
+ void *data;
+ FILE *f;
+ int ret;
+
+ memset(crl, 0, sizeof(*crl));
+
ret = stat(path, &sb);
if (ret)
return errno;
-
+
*t = sb.st_mtime;
+
+ if ((f = fopen(path, "r")) == NULL)
+ return errno;
- ret = decode_CRLCertificateList(data, length, crl, &size);
- rk_xfree(data);
- if (ret)
- return ret;
+ rk_cloexec_file(f);
- /* check signature is aligned */
- if (crl->signatureValue.length & 7) {
- free_CRLCertificateList(crl);
- return HX509_CRYPTO_SIG_INVALID_FORMAT;
+ ret = hx509_pem_read(context, f, crl_parser, crl);
+ fclose(f);
+
+ if (ret == HX509_PARSING_KEY_FAILED) {
+
+ ret = rk_undumpdata(path, &data, &length);
+ if (ret)
+ return ret;
+
+ ret = crl_parser(context, "X509 CRL", NULL, data, length, crl);
+ rk_xfree(data);
}
- return 0;
+ return ret;
}
/**
@@ -624,7 +655,7 @@ hx509_revoke_add_crl(hx509_context context,
path += 5;
for (i = 0; i < ctx->crls.len; i++) {
- if (strcmp(ctx->crls.val[0].path, path) == 0)
+ if (strcmp(ctx->crls.val[i].path, path) == 0)
return 0;
}
@@ -644,7 +675,8 @@ hx509_revoke_add_crl(hx509_context context,
return ENOMEM;
}
- ret = load_crl(path,
+ ret = load_crl(context,
+ path,
&ctx->crls.val[ctx->crls.len].last_modfied,
&ctx->crls.val[ctx->crls.len].crl);
if (ret) {
@@ -674,7 +706,6 @@ hx509_revoke_add_crl(hx509_context context,
* @ingroup hx509_revoke
*/
-
int
hx509_revoke_verify(hx509_context context,
hx509_revoke_ctx ctx,
@@ -781,7 +812,7 @@ hx509_revoke_verify(hx509_context context,
if (ret == 0 && crl->last_modfied != sb.st_mtime) {
CRLCertificateList cl;
- ret = load_crl(crl->path, &crl->last_modfied, &cl);
+ ret = load_crl(context, crl->path, &crl->last_modfied, &cl);
if (ret == 0) {
free_CRLCertificateList(&crl->crl);
crl->crl = cl;
@@ -1064,47 +1095,23 @@ printable_time(time_t t)
return s;
}
-/**
- * Print the OCSP reply stored in a file.
- *
- * @param context a hx509 context
- * @param path path to a file with a OCSP reply
- * @param out the out FILE descriptor to print the reply on
- *
- * @return An hx509 error code, see hx509_get_error_string().
+/*
*
- * @ingroup hx509_revoke
*/
-int
-hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
+static int
+print_ocsp(hx509_context context, struct revoke_ocsp *ocsp, FILE *out)
{
- struct revoke_ocsp ocsp;
- int ret;
+ int ret = 0;
size_t i;
- if (out == NULL)
- out = stdout;
-
- memset(&ocsp, 0, sizeof(ocsp));
-
- ocsp.path = strdup(path);
- if (ocsp.path == NULL)
- return ENOMEM;
-
- ret = load_ocsp(context, &ocsp);
- if (ret) {
- free_ocsp(&ocsp);
- return ret;
- }
-
fprintf(out, "signer: ");
- switch(ocsp.ocsp.tbsResponseData.responderID.element) {
+ switch(ocsp->ocsp.tbsResponseData.responderID.element) {
case choice_OCSPResponderID_byName: {
hx509_name n;
char *s;
- _hx509_name_from_Name(&ocsp.ocsp.tbsResponseData.responderID.u.byName, &n);
+ _hx509_name_from_Name(&ocsp->ocsp.tbsResponseData.responderID.u.byName, &n);
hx509_name_to_string(n, &s);
hx509_name_free(&n);
fprintf(out, " byName: %s\n", s);
@@ -1113,8 +1120,8 @@ hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
}
case choice_OCSPResponderID_byKey: {
char *s;
- hex_encode(ocsp.ocsp.tbsResponseData.responderID.u.byKey.data,
- ocsp.ocsp.tbsResponseData.responderID.u.byKey.length,
+ hex_encode(ocsp->ocsp.tbsResponseData.responderID.u.byKey.data,
+ ocsp->ocsp.tbsResponseData.responderID.u.byKey.length,
&s);
fprintf(out, " byKey: %s\n", s);
free(s);
@@ -1126,13 +1133,13 @@ hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
}
fprintf(out, "producedAt: %s\n",
- printable_time(ocsp.ocsp.tbsResponseData.producedAt));
+ printable_time(ocsp->ocsp.tbsResponseData.producedAt));
- fprintf(out, "replies: %d\n", ocsp.ocsp.tbsResponseData.responses.len);
+ fprintf(out, "replies: %d\n", ocsp->ocsp.tbsResponseData.responses.len);
- for (i = 0; i < ocsp.ocsp.tbsResponseData.responses.len; i++) {
+ for (i = 0; i < ocsp->ocsp.tbsResponseData.responses.len; i++) {
const char *status;
- switch (ocsp.ocsp.tbsResponseData.responses.val[i].certStatus.element) {
+ switch (ocsp->ocsp.tbsResponseData.responses.val[i].certStatus.element) {
case choice_OCSPCertStatus_good:
status = "good";
break;
@@ -1146,19 +1153,116 @@ hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
status = "element unknown";
}
- fprintf(out, "\t%zu. status: %s\n", i, status);
+ fprintf(out, "\t%llu. status: %s\n", (unsigned long long)i, status);
fprintf(out, "\tthisUpdate: %s\n",
- printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
- if (ocsp.ocsp.tbsResponseData.responses.val[i].nextUpdate)
+ printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
+ if (ocsp->ocsp.tbsResponseData.responses.val[i].nextUpdate)
fprintf(out, "\tproducedAt: %s\n",
- printable_time(ocsp.ocsp.tbsResponseData.responses.val[i].thisUpdate));
+ printable_time(ocsp->ocsp.tbsResponseData.responses.val[i].thisUpdate));
}
fprintf(out, "appended certs:\n");
- if (ocsp.certs)
- ret = hx509_certs_iter_f(context, ocsp.certs, hx509_ci_print_names, out);
+ if (ocsp->certs)
+ ret = hx509_certs_iter_f(context, ocsp->certs, hx509_ci_print_names, out);
+
+ return ret;
+}
+
+static int
+print_crl(hx509_context context, struct revoke_crl *crl, FILE *out)
+{
+ {
+ hx509_name n;
+ char *s;
+ _hx509_name_from_Name(&crl->crl.tbsCertList.issuer, &n);
+ hx509_name_to_string(n, &s);
+ hx509_name_free(&n);
+ fprintf(out, " issuer: %s\n", s);
+ free(s);
+ }
+
+ fprintf(out, " thisUpdate: %s\n",
+ printable_time(_hx509_Time2time_t(&crl->crl.tbsCertList.thisUpdate)));
+
+ return 0;
+}
+
+
+/*
+ *
+ */
+
+int
+hx509_revoke_print(hx509_context context,
+ hx509_revoke_ctx ctx,
+ FILE *out)
+{
+ int saved_ret = 0, ret;
+ size_t n;
+
+ for (n = 0; n < ctx->ocsps.len; n++) {
+ struct revoke_ocsp *ocsp = &ctx->ocsps.val[n];
+
+ fprintf(out, "OCSP %s\n", ocsp->path);
+
+ ret = print_ocsp(context, ocsp, out);
+ if (ret) {
+ fprintf(out, "failure printing OCSP: %d\n", ret);
+ saved_ret = ret;
+ }
+ }
+
+ for (n = 0; n < ctx->crls.len; n++) {
+ struct revoke_crl *crl = &ctx->crls.val[n];
+
+ fprintf(out, "CRL %s\n", crl->path);
+
+ ret = print_crl(context, crl, out);
+ if (ret) {
+ fprintf(out, "failure printing CRL: %d\n", ret);
+ saved_ret = ret;
+ }
+ }
+ return saved_ret;
+
+}
+
+/**
+ * Print the OCSP reply stored in a file.
+ *
+ * @param context a hx509 context
+ * @param path path to a file with a OCSP reply
+ * @param out the out FILE descriptor to print the reply on
+ *
+ * @return An hx509 error code, see hx509_get_error_string().
+ *
+ * @ingroup hx509_revoke
+ */
+
+int
+hx509_revoke_ocsp_print(hx509_context context, const char *path, FILE *out)
+{
+ struct revoke_ocsp ocsp;
+ int ret;
+
+ if (out == NULL)
+ out = stdout;
+
+ memset(&ocsp, 0, sizeof(ocsp));
+
+ ocsp.path = strdup(path);
+ if (ocsp.path == NULL)
+ return ENOMEM;
+
+ ret = load_ocsp(context, &ocsp);
+ if (ret) {
+ free_ocsp(&ocsp);
+ return ret;
+ }
+
+ ret = print_ocsp(context, &ocsp, out);
free_ocsp(&ocsp);
return ret;
diff --git a/lib/hx509/sel-gram.c b/lib/hx509/sel-gram.c
index 70d83d645db6..4d60c918f794 100644
--- a/lib/hx509/sel-gram.c
+++ b/lib/hx509/sel-gram.c
@@ -101,6 +101,18 @@
#include <stdlib.h>
#include <hx_locl.h>
+#if !defined(yylex)
+#define yylex _hx509_sel_yylex
+#define yywrap _hx509_sel_yywrap
+#endif
+#if !defined(yyparse)
+#define yyparse _hx509_sel_yyparse
+#define yyerror _hx509_sel_yyerror
+#define yylval _hx509_sel_yylval
+#define yychar _hx509_sel_yychar
+#define yydebug _hx509_sel_yydebug
+#define yynerrs _hx509_sel_yynerrs
+#endif
@@ -124,13 +136,13 @@
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 45 "sel-gram.y"
+#line 57 "sel-gram.y"
{
char *string;
struct hx_expr *expr;
}
/* Line 193 of yacc.c. */
-#line 134 "sel-gram.c"
+#line 146 "sel-gram.c"
YYSTYPE;
# define yystype YYSTYPE /* obsolescent; will be withdrawn */
# define YYSTYPE_IS_DECLARED 1
@@ -143,7 +155,7 @@ typedef union YYSTYPE
/* Line 216 of yacc.c. */
-#line 147 "sel-gram.c"
+#line 159 "sel-gram.c"
#ifdef short
# undef short
@@ -435,9 +447,9 @@ static const yytype_int8 yyrhs[] =
/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
static const yytype_uint8 yyrline[] =
{
- 0, 73, 73, 75, 76, 77, 78, 79, 80, 81,
- 84, 85, 88, 89, 90, 91, 92, 95, 96, 97,
- 98, 101, 102, 104, 107, 110, 112
+ 0, 85, 85, 87, 88, 89, 90, 91, 92, 93,
+ 96, 97, 100, 101, 102, 103, 104, 107, 108, 109,
+ 110, 113, 114, 116, 119, 122, 124
};
#endif
@@ -1367,136 +1379,136 @@ yyreduce:
switch (yyn)
{
case 2:
-#line 73 "sel-gram.y"
+#line 85 "sel-gram.y"
{ _hx509_expr_input.expr = (yyvsp[(1) - (1)].expr); }
break;
case 3:
-#line 75 "sel-gram.y"
+#line 87 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(op_TRUE, NULL, NULL); }
break;
case 4:
-#line 76 "sel-gram.y"
+#line 88 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(op_FALSE, NULL, NULL); }
break;
case 5:
-#line 77 "sel-gram.y"
+#line 89 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(op_NOT, (yyvsp[(2) - (2)].expr), NULL); }
break;
case 6:
-#line 78 "sel-gram.y"
+#line 90 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(op_AND, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
break;
case 7:
-#line 79 "sel-gram.y"
+#line 91 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(op_OR, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
break;
case 8:
-#line 80 "sel-gram.y"
+#line 92 "sel-gram.y"
{ (yyval.expr) = (yyvsp[(2) - (3)].expr); }
break;
case 9:
-#line 81 "sel-gram.y"
+#line 93 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(op_COMP, (yyvsp[(1) - (1)].expr), NULL); }
break;
case 10:
-#line 84 "sel-gram.y"
+#line 96 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(expr_WORDS, (yyvsp[(1) - (1)].expr), NULL); }
break;
case 11:
-#line 85 "sel-gram.y"
+#line 97 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(expr_WORDS, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
break;
case 12:
-#line 88 "sel-gram.y"
+#line 100 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(comp_EQ, (yyvsp[(1) - (4)].expr), (yyvsp[(4) - (4)].expr)); }
break;
case 13:
-#line 89 "sel-gram.y"
+#line 101 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(comp_NE, (yyvsp[(1) - (4)].expr), (yyvsp[(4) - (4)].expr)); }
break;
case 14:
-#line 90 "sel-gram.y"
+#line 102 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(comp_TAILEQ, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
break;
case 15:
-#line 91 "sel-gram.y"
+#line 103 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(comp_IN, (yyvsp[(1) - (5)].expr), (yyvsp[(4) - (5)].expr)); }
break;
case 16:
-#line 92 "sel-gram.y"
+#line 104 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(comp_IN, (yyvsp[(1) - (3)].expr), (yyvsp[(3) - (3)].expr)); }
break;
case 17:
-#line 95 "sel-gram.y"
+#line 107 "sel-gram.y"
{ (yyval.expr) = (yyvsp[(1) - (1)].expr); }
break;
case 18:
-#line 96 "sel-gram.y"
+#line 108 "sel-gram.y"
{ (yyval.expr) = (yyvsp[(1) - (1)].expr); }
break;
case 19:
-#line 97 "sel-gram.y"
+#line 109 "sel-gram.y"
{ (yyval.expr) = (yyvsp[(1) - (1)].expr); }
break;
case 20:
-#line 98 "sel-gram.y"
+#line 110 "sel-gram.y"
{ (yyval.expr) = (yyvsp[(1) - (1)].expr); }
break;
case 21:
-#line 101 "sel-gram.y"
+#line 113 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(expr_NUMBER, (yyvsp[(1) - (1)].string), NULL); }
break;
case 22:
-#line 102 "sel-gram.y"
+#line 114 "sel-gram.y"
{ (yyval.expr) = _hx509_make_expr(expr_STRING, (yyvsp[(1) - (1)].string), NULL); }
break;
case 23:
-#line 104 "sel-gram.y"
+#line 116 "sel-gram.y"
{
(yyval.expr) = _hx509_make_expr(expr_FUNCTION, (yyvsp[(1) - (4)].string), (yyvsp[(3) - (4)].expr)); }
break;
case 24:
-#line 107 "sel-gram.y"
+#line 119 "sel-gram.y"
{ (yyval.expr) = (yyvsp[(3) - (4)].expr); }
break;
case 25:
-#line 110 "sel-gram.y"
+#line 122 "sel-gram.y"
{
(yyval.expr) = _hx509_make_expr(expr_VAR, (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].expr)); }
break;
case 26:
-#line 112 "sel-gram.y"
+#line 124 "sel-gram.y"
{
(yyval.expr) = _hx509_make_expr(expr_VAR, (yyvsp[(1) - (1)].string), NULL); }
break;
/* Line 1267 of yacc.c. */
-#line 1500 "sel-gram.c"
+#line 1512 "sel-gram.c"
default: break;
}
YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
diff --git a/lib/hx509/sel-gram.h b/lib/hx509/sel-gram.h
index 13be3f9ab19c..9642ac7fdb12 100644
--- a/lib/hx509/sel-gram.h
+++ b/lib/hx509/sel-gram.h
@@ -66,7 +66,7 @@
#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
typedef union YYSTYPE
-#line 45 "sel-gram.y"
+#line 57 "sel-gram.y"
{
char *string;
struct hx_expr *expr;
diff --git a/lib/hx509/sel-gram.y b/lib/hx509/sel-gram.y
index 7f7c9980e03b..7e9d4f26d9c2 100644
--- a/lib/hx509/sel-gram.y
+++ b/lib/hx509/sel-gram.y
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2008 Kungliga Tekniska Högskolan
+ * Copyright (c) 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -39,6 +39,18 @@
#include <stdlib.h>
#include <hx_locl.h>
+#if !defined(yylex)
+#define yylex _hx509_sel_yylex
+#define yywrap _hx509_sel_yywrap
+#endif
+#if !defined(yyparse)
+#define yyparse _hx509_sel_yyparse
+#define yyerror _hx509_sel_yyerror
+#define yylval _hx509_sel_yylval
+#define yychar _hx509_sel_yychar
+#define yydebug _hx509_sel_yydebug
+#define yynerrs _hx509_sel_yynerrs
+#endif
%}
diff --git a/lib/hx509/sel-lex.c b/lib/hx509/sel-lex.c
index 51f1e8407807..c2044a435dc8 100644
--- a/lib/hx509/sel-lex.c
+++ b/lib/hx509/sel-lex.c
@@ -46,6 +46,7 @@ typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@@ -354,7 +355,7 @@ static void yy_fatal_error (yyconst char msg[] );
*/
#define YY_DO_BEFORE_ACTION \
(yytext_ptr) = yy_bp; \
- yyleng = (size_t) (yy_cp - yy_bp); \
+ yyleng = (yy_size_t) (yy_cp - yy_bp); \
(yy_hold_char) = *yy_cp; \
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
@@ -470,7 +471,7 @@ char *yytext;
#line 1 "sel-lex.l"
#line 2 "sel-lex.l"
/*
- * Copyright (c) 2004, 2008 Kungliga Tekniska Högskolan
+ * Copyright (c) 2004 - 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -504,6 +505,11 @@ char *yytext;
/* $Id$ */
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -520,7 +526,6 @@ unsigned lineno = 1;
static char * handle_string(void);
static int lex_input(char *, int);
-static int lex_classic_input(void);
struct hx_expr_input _hx509_expr_input;
@@ -535,7 +540,7 @@ struct hx_expr_input _hx509_expr_input;
#undef ECHO
-#line 539 "sel-lex.c"
+#line 544 "sel-lex.c"
#define INITIAL 0
@@ -717,10 +722,10 @@ YY_DECL
register char *yy_cp, *yy_bp;
register int yy_act;
-#line 69 "sel-lex.l"
+#line 73 "sel-lex.l"
-#line 724 "sel-lex.c"
+#line 729 "sel-lex.c"
if ( !(yy_init) )
{
@@ -805,37 +810,37 @@ do_action: /* This label is used only to access EOF actions. */
case 1:
YY_RULE_SETUP
-#line 71 "sel-lex.l"
+#line 75 "sel-lex.l"
{ return kw_TRUE; }
YY_BREAK
case 2:
YY_RULE_SETUP
-#line 72 "sel-lex.l"
+#line 76 "sel-lex.l"
{ return kw_FALSE; }
YY_BREAK
case 3:
YY_RULE_SETUP
-#line 73 "sel-lex.l"
+#line 77 "sel-lex.l"
{ return kw_AND; }
YY_BREAK
case 4:
YY_RULE_SETUP
-#line 74 "sel-lex.l"
+#line 78 "sel-lex.l"
{ return kw_OR; }
YY_BREAK
case 5:
YY_RULE_SETUP
-#line 75 "sel-lex.l"
+#line 79 "sel-lex.l"
{ return kw_IN; }
YY_BREAK
case 6:
YY_RULE_SETUP
-#line 76 "sel-lex.l"
+#line 80 "sel-lex.l"
{ return kw_TAILMATCH; }
YY_BREAK
case 7:
YY_RULE_SETUP
-#line 78 "sel-lex.l"
+#line 82 "sel-lex.l"
{
yylval.string = strdup ((const char *)yytext);
return IDENTIFIER;
@@ -843,31 +848,31 @@ YY_RULE_SETUP
YY_BREAK
case 8:
YY_RULE_SETUP
-#line 82 "sel-lex.l"
+#line 86 "sel-lex.l"
{ yylval.string = handle_string(); return STRING; }
YY_BREAK
case 9:
/* rule 9 can match eol */
YY_RULE_SETUP
-#line 83 "sel-lex.l"
+#line 87 "sel-lex.l"
{ ++lineno; }
YY_BREAK
case 10:
YY_RULE_SETUP
-#line 84 "sel-lex.l"
+#line 88 "sel-lex.l"
{ return *yytext; }
YY_BREAK
case 11:
YY_RULE_SETUP
-#line 85 "sel-lex.l"
+#line 89 "sel-lex.l"
;
YY_BREAK
case 12:
YY_RULE_SETUP
-#line 86 "sel-lex.l"
+#line 90 "sel-lex.l"
ECHO;
YY_BREAK
-#line 871 "sel-lex.c"
+#line 876 "sel-lex.c"
case YY_STATE_EOF(INITIAL):
yyterminate();
@@ -1864,7 +1869,7 @@ void yyfree (void * ptr )
#define YYTABLES_NAME "yytables"
-#line 86 "sel-lex.l"
+#line 90 "sel-lex.l"
@@ -1899,6 +1904,10 @@ handle_string(void)
return strdup(x);
}
+#if !defined(yywrap)
+#define yywrap _hx509_sel_yywrap
+#endif
+
int
yywrap ()
{
diff --git a/lib/hx509/sel-lex.l b/lib/hx509/sel-lex.l
index bb7e8374c7d1..f401e40e3601 100644
--- a/lib/hx509/sel-lex.l
+++ b/lib/hx509/sel-lex.l
@@ -1,6 +1,6 @@
%{
/*
- * Copyright (c) 2004, 2008 Kungliga Tekniska Högskolan
+ * Copyright (c) 2004 - 2017 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -34,6 +34,11 @@
/* $Id$ */
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wunused-function"
+#endif
+
+
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -50,7 +55,6 @@ unsigned lineno = 1;
static char * handle_string(void);
static int lex_input(char *, int);
-static int lex_classic_input(void);
struct hx_expr_input _hx509_expr_input;
@@ -116,6 +120,10 @@ handle_string(void)
return strdup(x);
}
+#if !defined(yywrap)
+#define yywrap _hx509_sel_yywrap
+#endif
+
int
yywrap ()
{
diff --git a/lib/hx509/sel.h b/lib/hx509/sel.h
index 177ec0a65b27..52a84d31c5ae 100644
--- a/lib/hx509/sel.h
+++ b/lib/hx509/sel.h
@@ -67,14 +67,18 @@ struct hx_expr_input {
extern struct hx_expr_input _hx509_expr_input;
-#define yyparse _hx509_sel_yyparse
+#if !defined(yylex)
#define yylex _hx509_sel_yylex
+#define yywrap _hx509_sel_yywrap
+#endif
+#if !defined(yyparse)
+#define yyparse _hx509_sel_yyparse
#define yyerror _hx509_sel_yyerror
#define yylval _hx509_sel_yylval
#define yychar _hx509_sel_yychar
#define yydebug _hx509_sel_yydebug
#define yynerrs _hx509_sel_yynerrs
-#define yywrap _hx509_sel_yywrap
+#endif
int _hx509_sel_yyparse(void);
int _hx509_sel_yylex(void);
diff --git a/lib/hx509/softp11.c b/lib/hx509/softp11.c
index 38f587e0fea2..f93863b7c980 100644
--- a/lib/hx509/softp11.c
+++ b/lib/hx509/softp11.c
@@ -34,7 +34,7 @@
#define CRYPTOKI_EXPORTS 1
#include "hx_locl.h"
-#include "pkcs11.h"
+#include "ref/pkcs11.h"
#define OBJECT_ID_MASK 0xfff
#define HANDLE_OBJECT_ID(h) ((h) & OBJECT_ID_MASK)
@@ -543,6 +543,8 @@ add_cert(hx509_context hxctx, void *ctx, hx509_cert cert)
CK_FLAGS flags;
type = CKO_PRIVATE_KEY;
+
+ /* Note to static analyzers: `o' is still referred to via globals */
o = add_st_object();
if (o == NULL) {
ret = CKR_DEVICE_MEMORY;
@@ -593,6 +595,7 @@ add_cert(hx509_context hxctx, void *ctx, hx509_cert cert)
hx509_xfree(issuer_data.data);
hx509_xfree(subject_data.data);
+ /* Note to static analyzers: `o' is still referred to via globals */
return 0;
}
@@ -615,7 +618,11 @@ add_certificate(const char *cert_file,
if (pin) {
char *str;
- asprintf(&str, "PASS:%s", pin);
+ ret = asprintf(&str, "PASS:%s", pin);
+ if (ret == -1 || !str) {
+ st_logf("failed to allocate memory\n");
+ return CKR_GENERAL_ERROR;
+ }
hx509_lock_init(context, &lock);
hx509_lock_command_string(lock, str);
@@ -815,6 +822,7 @@ get_config_file_for_user(void)
#ifndef _WIN32
char *home = NULL;
+ int ret;
if (!issuid()) {
fn = getenv("SOFTPKCS11RC");
@@ -828,9 +836,11 @@ get_config_file_for_user(void)
home = pw->pw_dir;
}
if (fn == NULL) {
- if (home)
- asprintf(&fn, "%s/.soft-token.rc", home);
- else
+ if (home) {
+ ret = asprintf(&fn, "%s/.soft-token.rc", home);
+ if (ret == -1)
+ fn = NULL;
+ } else
fn = strdup("/etc/soft-token.rc");
}
#else /* Windows */
@@ -1077,7 +1087,7 @@ C_GetMechanismList(CK_SLOT_ID slotID,
*pulCount = 1;
if (pMechanismList == NULL_PTR)
return CKR_OK;
- pMechanismList[1] = CKM_RSA_PKCS;
+ pMechanismList[0] = CKM_RSA_PKCS;
return CKR_OK;
}
@@ -1205,8 +1215,13 @@ C_Login(CK_SESSION_HANDLE hSession,
VERIFY_SESSION_HANDLE(hSession, NULL);
if (pPin != NULL_PTR) {
- asprintf(&pin, "%.*s", (int)ulPinLen, pPin);
- st_logf("type: %d password: %s\n", (int)userType, pin);
+ int aret;
+
+ aret = asprintf(&pin, "%.*s", (int)ulPinLen, pPin);
+ if (aret != -1 && pin)
+ st_logf("type: %d password: %s\n", (int)userType, pin);
+ else
+ st_logf("memory error: asprintf failed\n");
}
/*
diff --git a/lib/hx509/test_ca.in b/lib/hx509/test_ca.in
index 2ca294ea79e9..0264116bbe69 100644
--- a/lib/hx509/test_ca.in
+++ b/lib/hx509/test_ca.in
@@ -421,4 +421,32 @@ ${hxtool} verify --missing-revoke \
cert:FILE:cert-ee.pem \
anchor:FILE:cert-ca.pem > /dev/null || exit 1
+echo "+++++++++++ test sigalg"
+
+echo "issue cert with sha256"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --signature-algorithm=rsa-with-sha256 \
+ --subject="cn=foo" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+
+echo "verify certificate"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
+echo "issue cert with sha1"
+${hxtool} issue-certificate \
+ --ca-certificate=FILE:cert-ca.pem \
+ --signature-algorithm=rsa-with-sha1 \
+ --subject="cn=foo" \
+ --req="PKCS10:pkcs10-request.der" \
+ --certificate="FILE:cert-ee.pem" || exit 1
+
+echo "verify certificate"
+${hxtool} verify --missing-revoke \
+ cert:FILE:cert-ee.pem \
+ anchor:FILE:cert-ca.pem > /dev/null || exit 1
+
exit 0
diff --git a/lib/hx509/test_cert.in b/lib/hx509/test_cert.in
index 6cbf21bf0571..5fa14d0f8397 100644
--- a/lib/hx509/test_cert.in
+++ b/lib/hx509/test_cert.in
@@ -46,7 +46,7 @@ if ${hxtool} info | grep 'rand: not available' > /dev/null ; then
fi
echo "print DIR"
-${hxtool} print --content DIR:$srcdir/data > /dev/null || exit 1
+${hxtool} print --content DIR:$srcdir/data > /dev/null 2>/dev/null || exit 1
echo "print FILE"
for a in $srcdir/data/*.crt; do
diff --git a/lib/hx509/test_chain.in b/lib/hx509/test_chain.in
index df551d9c0a93..b8c8cf527684 100644
--- a/lib/hx509/test_chain.in
+++ b/lib/hx509/test_chain.in
@@ -192,13 +192,13 @@ if ${hxtool} info | grep 'ecdsa: hcrypto null' > /dev/null ; then
else
echo "eccert -> root"
${hxtool} verify --missing-revoke \
- cert:FILE:$srcdir/data/secp160r2TestServer.cert.pem \
- anchor:FILE:$srcdir/data/secp160r1TestCA.cert.pem > /dev/null || exit 1
+ cert:FILE:$srcdir/data/secp256r2TestServer.cert.pem \
+ anchor:FILE:$srcdir/data/secp256r1TestCA.cert.pem > /dev/null || exit 1
echo "eccert -> root"
${hxtool} verify --missing-revoke \
- cert:FILE:$srcdir/data/secp160r2TestClient.cert.pem \
- anchor:FILE:$srcdir/data/secp160r1TestCA.cert.pem > /dev/null || exit 1
+ cert:FILE:$srcdir/data/secp256r2TestClient.cert.pem \
+ anchor:FILE:$srcdir/data/secp256r1TestCA.cert.pem > /dev/null || exit 1
fi
echo "proxy cert"
diff --git a/lib/hx509/test_cms.in b/lib/hx509/test_cms.in
index d519d25a22b2..8b3de76efd6b 100644
--- a/lib/hx509/test_cms.in
+++ b/lib/hx509/test_cms.in
@@ -53,15 +53,15 @@ if ${hxtool} info | grep 'ecdsa: hcrypto null' > /dev/null ; then
else
echo "create signed data (ec)"
${hxtool} cms-create-sd \
- --certificate=FILE:$srcdir/data/secp160r2TestClient.pem \
- "$srcdir/test_chain.in" \
- sd.data > /dev/null || exit 1
+ --certificate=FILE:$srcdir/data/secp256r2TestClient.pem \
+ "$srcdir/test_chain.in" \
+ sd.data > /dev/null || exit 1
echo "verify signed data (ec)"
${hxtool} cms-verify-sd \
- --missing-revoke \
- --anchors=FILE:$srcdir/data/secp160r1TestCA.cert.pem \
- sd.data sd.data.out > /dev/null || exit 1
+ --missing-revoke \
+ --anchors=FILE:$srcdir/data/secp256r1TestCA.cert.pem \
+ sd.data sd.data.out > /dev/null || exit 1
cmp "$srcdir/test_chain.in" sd.data.out || exit 1
fi
diff --git a/lib/hx509/test_name.c b/lib/hx509/test_name.c
index d932221ddf04..9d21a7f65b03 100644
--- a/lib/hx509/test_name.c
+++ b/lib/hx509/test_name.c
@@ -319,14 +319,14 @@ test_compare(hx509_context context)
/* check transative properties of name compare function */
- ret = hx509_cert_init_data(context, certdata1, sizeof(certdata1) - 1, &c1);
- if (ret) return 1;
-
- ret = hx509_cert_init_data(context, certdata2, sizeof(certdata2) - 1, &c2);
- if (ret) return 1;
-
- ret = hx509_cert_init_data(context, certdata3, sizeof(certdata3) - 1, &c3);
- if (ret) return 1;
+ c1 = hx509_cert_init_data(context, certdata1, sizeof(certdata1) - 1, NULL);
+ if (c1 == NULL) return 1;
+
+ c2 = hx509_cert_init_data(context, certdata2, sizeof(certdata2) - 1, NULL);
+ if (c2 == NULL) return 1;
+
+ c3 = hx509_cert_init_data(context, certdata3, sizeof(certdata3) - 1, NULL);
+ if (c3 == NULL) return 1;
ret = compare_subject(c1, c1, &l0);
if (ret) return 1;
diff --git a/lib/hx509/test_soft_pkcs11.c b/lib/hx509/test_soft_pkcs11.c
index c8fc2448ee76..cdffcf803b0e 100644
--- a/lib/hx509/test_soft_pkcs11.c
+++ b/lib/hx509/test_soft_pkcs11.c
@@ -32,7 +32,7 @@
*/
#include "hx_locl.h"
-#include "pkcs11.h"
+#include "ref/pkcs11.h"
#include <err.h>
static CK_FUNCTION_LIST_PTR func;
diff --git a/lib/hx509/version-script.map b/lib/hx509/version-script.map
index b05198c42c5d..f040cd834496 100644
--- a/lib/hx509/version-script.map
+++ b/lib/hx509/version-script.map
@@ -23,7 +23,6 @@ HEIMDAL_X509_1.2 {
_hx509_request_print;
_hx509_request_set_email;
_hx509_request_to_pkcs10;
- _hx509_request_to_pkcs10;
_hx509_unmap_file_os;
_hx509_write_file;
hx509_bitstring_print;
@@ -209,6 +208,7 @@ HEIMDAL_X509_1.2 {
hx509_revoke_init;
hx509_revoke_ocsp_print;
hx509_revoke_verify;
+ hx509_revoke_print;
hx509_set_error_string;
hx509_set_error_stringv;
hx509_signature_md5;
@@ -248,3 +248,8 @@ HEIMDAL_X509_1.2 {
*;
};
+HEIMDAL_X509_1.3 {
+ global:
+ hx509_ca_tbs_set_signature_algorithm;
+};
+
diff --git a/lib/ipc/Makefile.am b/lib/ipc/Makefile.am
index fc3ba469d481..9338b141e171 100644
--- a/lib/ipc/Makefile.am
+++ b/lib/ipc/Makefile.am
@@ -33,10 +33,11 @@ ts_LDADD = libheim-ipcs.la $(LIB_roken)
ts_http_LDADD = $(ts_LDADD)
tc_LDADD = libheim-ipcc.la $(LIB_roken)
-if have_gcd
EXTRA_DIST = heim_ipc.defs heim_ipc_async.defs heim_ipc_reply.defs
+if have_gcd
+
heim_ipc.h heim_ipcUser.c heim_ipcServer.c heim_ipcServer.h: heim_ipc.defs
mig -header heim_ipc.h -user heim_ipcUser.c -sheader heim_ipcServer.h -server heim_ipcServer.c -I$(srcdir) $(srcdir)/heim_ipc.defs
@@ -64,4 +65,4 @@ CLEANFILES = $(built_ipcc) $(built_ipcs)
$(srcdir)/client.c: $(built_ipcc)
$(srcdir)/server.c: $(built_ipcs)
-endif \ No newline at end of file
+endif
diff --git a/lib/ipc/Makefile.in b/lib/ipc/Makefile.in
index 1fd0c227bd6e..2c9cab8f66a6 100644
--- a/lib/ipc/Makefile.in
+++ b/lib/ipc/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -22,6 +21,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -40,9 +94,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common
TESTS =
noinst_PROGRAMS = tc$(EXEEXT) ts$(EXEEXT) ts-http$(EXEEXT)
@have_gcd_TRUE@am__append_1 = -lbsm
@@ -61,8 +112,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -75,6 +125,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -94,6 +145,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -107,6 +160,10 @@ dist_libheim_ipcc_la_OBJECTS = client.lo common.lo
@have_gcd_TRUE@nodist_libheim_ipcc_la_OBJECTS = $(am__objects_1)
libheim_ipcc_la_OBJECTS = $(dist_libheim_ipcc_la_OBJECTS) \
$(nodist_libheim_ipcc_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
am__DEPENDENCIES_2 = $(LIB_heimbase) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
libheim_ipcs_la_DEPENDENCIES = $(am__DEPENDENCIES_2) \
@@ -128,24 +185,50 @@ ts_http_SOURCES = ts-http.c
ts_http_OBJECTS = ts-http.$(OBJEXT)
am__DEPENDENCIES_3 = libheim-ipcs.la $(am__DEPENDENCIES_1)
ts_http_DEPENDENCIES = $(am__DEPENDENCIES_3)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(dist_libheim_ipcc_la_SOURCES) \
$(nodist_libheim_ipcc_la_SOURCES) \
$(dist_libheim_ipcs_la_SOURCES) \
$(nodist_libheim_ipcs_la_SOURCES) tc.c ts.c ts-http.c
DIST_SOURCES = $(dist_libheim_ipcc_la_SOURCES) \
$(dist_libheim_ipcs_la_SOURCES) tc.c ts.c ts-http.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
@@ -167,17 +250,219 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(includedir)"
HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -196,12 +481,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -211,17 +496,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -240,12 +525,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -254,6 +536,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -270,10 +553,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -281,6 +562,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -295,12 +577,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -317,6 +602,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -331,13 +617,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -361,6 +641,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -396,29 +678,37 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
noinst_LTLIBRARIES = libheim-ipcc.la libheim-ipcs.la
dist_libheim_ipcc_la_SOURCES = hi_locl.h heim_ipc_types.h client.c common.c
dist_libheim_ipcs_la_SOURCES = hi_locl.h heim_ipc_types.h server.c common.c
@@ -440,7 +730,7 @@ libheim_ipcs_la_LIBADD = $(libheim_ipcc_la_LIBADD) $(am__append_1)
ts_LDADD = libheim-ipcs.la $(LIB_roken)
ts_http_LDADD = $(ts_LDADD)
tc_LDADD = libheim-ipcc.la $(LIB_roken)
-@have_gcd_TRUE@EXTRA_DIST = heim_ipc.defs heim_ipc_async.defs heim_ipc_reply.defs
+EXTRA_DIST = heim_ipc.defs heim_ipc_async.defs heim_ipc_reply.defs
@have_gcd_TRUE@built_ipcc = heim_ipc.h heim_ipcUser.c \
@have_gcd_TRUE@ heim_ipc_asyncServer.c heim_ipc_asyncServer.h
@have_gcd_TRUE@nodist_libheim_ipcc_la_SOURCES = $(built_ipcc)
@@ -452,7 +742,7 @@ tc_LDADD = libheim-ipcc.la $(LIB_roken)
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -465,7 +755,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/ipc/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/ipc/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -474,6 +763,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -486,16 +776,20 @@ $(am__aclocal_m4_deps):
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
- @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libheim-ipcc.la: $(libheim_ipcc_la_OBJECTS) $(libheim_ipcc_la_DEPENDENCIES)
- $(LINK) $(libheim_ipcc_la_OBJECTS) $(libheim_ipcc_la_LIBADD) $(LIBS)
-libheim-ipcs.la: $(libheim_ipcs_la_OBJECTS) $(libheim_ipcs_la_DEPENDENCIES)
- $(LINK) $(libheim_ipcs_la_OBJECTS) $(libheim_ipcs_la_LIBADD) $(LIBS)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libheim-ipcc.la: $(libheim_ipcc_la_OBJECTS) $(libheim_ipcc_la_DEPENDENCIES) $(EXTRA_libheim_ipcc_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libheim_ipcc_la_OBJECTS) $(libheim_ipcc_la_LIBADD) $(LIBS)
+
+libheim-ipcs.la: $(libheim_ipcs_la_OBJECTS) $(libheim_ipcs_la_DEPENDENCIES) $(EXTRA_libheim_ipcs_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libheim_ipcs_la_OBJECTS) $(libheim_ipcs_la_LIBADD) $(LIBS)
clean-noinstPROGRAMS:
@list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -505,15 +799,18 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-tc$(EXEEXT): $(tc_OBJECTS) $(tc_DEPENDENCIES)
+
+tc$(EXEEXT): $(tc_OBJECTS) $(tc_DEPENDENCIES) $(EXTRA_tc_DEPENDENCIES)
@rm -f tc$(EXEEXT)
- $(LINK) $(tc_OBJECTS) $(tc_LDADD) $(LIBS)
-ts$(EXEEXT): $(ts_OBJECTS) $(ts_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(tc_OBJECTS) $(tc_LDADD) $(LIBS)
+
+ts$(EXEEXT): $(ts_OBJECTS) $(ts_DEPENDENCIES) $(EXTRA_ts_DEPENDENCIES)
@rm -f ts$(EXEEXT)
- $(LINK) $(ts_OBJECTS) $(ts_LDADD) $(LIBS)
-ts-http$(EXEEXT): $(ts_http_OBJECTS) $(ts_http_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(ts_OBJECTS) $(ts_LDADD) $(LIBS)
+
+ts-http$(EXEEXT): $(ts_http_OBJECTS) $(ts_http_DEPENDENCIES) $(EXTRA_ts_http_DEPENDENCIES)
@rm -f ts-http$(EXEEXT)
- $(LINK) $(ts_http_OBJECTS) $(ts_http_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(ts_http_OBJECTS) $(ts_http_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -534,25 +831,25 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ts.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -561,8 +858,11 @@ clean-libtool:
-rm -rf .libs _libs
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -576,30 +876,17 @@ uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -611,15 +898,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -628,101 +911,180 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -775,11 +1137,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -821,9 +1191,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am:
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local
+
install-html: install-html-am
install-html-am:
@@ -865,41 +1234,54 @@ ps-am:
uninstall-am: uninstall-includeHEADERS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-generic clean-libtool \
- clean-noinstLTLIBRARIES clean-noinstPROGRAMS ctags dist-hook \
- distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-data \
- install-data-am install-data-hook install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
- install-html-am install-includeHEADERS install-info \
- install-info-am install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES clean-noinstPROGRAMS cscopelist-am \
+ ctags ctags-am dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-hook install-dvi \
+ install-dvi-am install-exec install-exec-am install-exec-local \
+ install-html install-html-am install-includeHEADERS \
+ install-info install-info-am install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-hook \
+ recheck tags tags-am uninstall uninstall-am uninstall-hook \
uninstall-includeHEADERS
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-exec-hook: install-suid-programs
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -907,7 +1289,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -964,6 +1346,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1006,6 +1390,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1019,13 +1416,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/ipc/client.c b/lib/ipc/client.c
index bb7d9750bffa..a51e91c99bf1 100644
--- a/lib/ipc/client.c
+++ b/lib/ipc/client.c
@@ -352,10 +352,12 @@ common_path_init(const char *service,
return ENOMEM;
s->fd = -1;
- asprintf(&s->path, "/var/run/.heim_%s-%s", service, file);
+ if (asprintf(&s->path, "/var/run/.heim_%s-%s", service, file) == -1) {
+ free(s);
+ return ENOMEM;
+ }
*ctx = s;
-
return 0;
}
diff --git a/lib/ipc/common.c b/lib/ipc/common.c
index 0e8f36dd2090..bbe2df4904ba 100644
--- a/lib/ipc/common.c
+++ b/lib/ipc/common.c
@@ -94,7 +94,9 @@ _heim_ipc_create_cred(uid_t uid, gid_t gid, pid_t pid, pid_t session, heim_icred
#ifndef HAVE_GCD
struct heim_isemaphore {
HEIMDAL_MUTEX mutex;
+#ifdef ENABLE_PTHREAD_SUPPORT
pthread_cond_t cond;
+#endif
long counter;
};
#endif
diff --git a/lib/ipc/heim-ipc.h b/lib/ipc/heim-ipc.h
index 6fbf309f7cd4..b5b919b64626 100644
--- a/lib/ipc/heim-ipc.h
+++ b/lib/ipc/heim-ipc.h
@@ -39,7 +39,7 @@ typedef struct heim_ipc *heim_ipc;
typedef struct heim_sipc *heim_sipc;
typedef struct heim_icred *heim_icred;
typedef struct heim_isemaphore *heim_isemaphore;
-typedef struct heim_octet_string heim_idata;
+typedef struct heim_base_data heim_idata;
typedef struct heim_sipc_call *heim_sipc_call;
/* common */
diff --git a/lib/ipc/server.c b/lib/ipc/server.c
index 245f2e3c5d82..ef91f6a09044 100644
--- a/lib/ipc/server.c
+++ b/lib/ipc/server.c
@@ -35,6 +35,7 @@
#include "hi_locl.h"
#include <assert.h>
+#include <err.h>
#define MAX_PACKET_SIZE (128 * 1024)
@@ -336,14 +337,14 @@ mach_init(const char *service, mach_port_t sport, heim_sipc ctx)
});
dispatch_source_set_cancel_handler(s->source, ^{
- heim_sipc ctx = dispatch_get_context(dispatch_get_current_queue());
- struct mach_service *st = ctx->mech;
+ heim_sipc sctx = dispatch_get_context(dispatch_get_current_queue());
+ struct mach_service *st = sctx->mech;
mach_port_mod_refs(mach_task_self(), st->sport,
MACH_PORT_RIGHT_RECEIVE, -1);
dispatch_release(st->queue);
dispatch_release(st->source);
free(st);
- free(ctx);
+ free(sctx);
});
dispatch_resume(s->source);
@@ -525,7 +526,7 @@ update_client_creds(struct client *c)
}
}
#endif
-#ifdef SO_PEERCRED
+#if defined(SO_PEERCRED) && defined(__linux__)
/* Linux */
{
struct ucred pc;
@@ -822,7 +823,7 @@ handle_http_tcp(struct client *c)
free(data);
return NULL;
}
- len = base64_decode(t, data);
+ len = rk_base64_decode(t, data);
if(len <= 0){
const char *msg =
" 404 Not found\r\n"
@@ -980,7 +981,7 @@ process_loop(void)
unsigned n;
unsigned num_fds;
- while(num_clients > 0) {
+ while (num_clients > 0) {
fds = malloc(num_clients * sizeof(fds[0]));
if(fds == NULL)
@@ -999,7 +1000,11 @@ process_loop(void)
fds[n].revents = 0;
}
- poll(fds, num_fds, -1);
+ while (poll(fds, num_fds, -1) == -1) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ err(1, "poll(2) failed");
+ }
for (n = 0 ; n < num_fds; n++) {
if (clients[n] == NULL)
diff --git a/lib/ipc/tc.c b/lib/ipc/tc.c
index 8b56d21aa472..fb799bba94b7 100644
--- a/lib/ipc/tc.c
+++ b/lib/ipc/tc.c
@@ -46,8 +46,8 @@ static int help_flag;
static int version_flag;
static struct getargs args[] = {
- { "help", 'h', arg_flag, &help_flag },
- { "version", 'v', arg_flag, &version_flag }
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 'v', arg_flag, &version_flag, NULL, NULL }
};
static int num_args = sizeof(args) / sizeof(args[0]);
@@ -60,7 +60,7 @@ usage(int ret)
}
static void
-reply(void *ctx, int errorcode, heim_idata *reply, heim_icred cred)
+reply(void *ctx, int errorcode, heim_idata *rep, heim_icred cred)
{
printf("got reply\n");
heim_ipc_semaphore_signal((heim_isemaphore)ctx); /* tell caller we are done */
diff --git a/lib/ipc/ts-http.c b/lib/ipc/ts-http.c
index b493079d94a3..fbeb319564c5 100644
--- a/lib/ipc/ts-http.c
+++ b/lib/ipc/ts-http.c
@@ -44,8 +44,8 @@ static int help_flag;
static int version_flag;
static struct getargs args[] = {
- { "help", 'h', arg_flag, &help_flag },
- { "version", 'v', arg_flag, &version_flag }
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 'v', arg_flag, &version_flag, NULL, NULL }
};
static int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/ipc/ts.c b/lib/ipc/ts.c
index 680d77bc913e..e0f846eee423 100644
--- a/lib/ipc/ts.c
+++ b/lib/ipc/ts.c
@@ -44,8 +44,8 @@ static int help_flag;
static int version_flag;
static struct getargs args[] = {
- { "help", 'h', arg_flag, &help_flag },
- { "version", 'v', arg_flag, &version_flag }
+ { "help", 'h', arg_flag, &help_flag, NULL, NULL },
+ { "version", 'v', arg_flag, &version_flag, NULL, NULL }
};
static int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/kadm5/Makefile.am b/lib/kadm5/Makefile.am
index e25ccd11784e..de9a208227a3 100644
--- a/lib/kadm5/Makefile.am
+++ b/lib/kadm5/Makefile.am
@@ -2,11 +2,14 @@
include $(top_srcdir)/Makefile.am.common
+libkadm5srv_la_CPPFLAGS = -I$(srcdir)/../krb5
+
lib_LTLIBRARIES = libkadm5srv.la libkadm5clnt.la
libkadm5srv_la_LDFLAGS = -version-info 8:1:0
libkadm5clnt_la_LDFLAGS = -version-info 7:1:0
if versionscript
+libkadm5clnt_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script-client.map
libkadm5srv_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
endif
@@ -33,7 +36,7 @@ kadm5includedir = $(includedir)/kadm5
buildkadm5include = $(buildinclude)/kadm5
dist_kadm5include_HEADERS = admin.h private.h kadm5-pwcheck.h
-dist_kadm5include_HEADERS += kadm5-protos.h kadm5-private.h
+dist_kadm5include_HEADERS += $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
nodist_kadm5include_HEADERS = kadm5_err.h
@@ -105,6 +108,7 @@ dist_libkadm5srv_la_SOURCES = \
randkey_s.c \
rename_s.c \
server_glue.c \
+ setkey3_s.c \
set_keys.c \
set_modifier.c \
admin.h
@@ -132,7 +136,7 @@ LDADD = \
$(top_builddir)/lib/asn1/libasn1.la \
$(LIB_hcrypto) \
$(LIB_roken) \
- $(DBLIB) \
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) \
$(LIB_dlopen) \
$(LIB_pidfile)
@@ -145,7 +149,7 @@ iprop_log_LDADD = \
$(top_builddir)/lib/sl/libsl.la \
$(LIB_readline) \
$(LIB_roken) \
- $(DBLIB) \
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) \
$(LIB_dlopen) \
$(LIB_pidfile)
@@ -154,6 +158,7 @@ iprop-commands.c iprop-commands.h: iprop-commands.in
$(SLC) $(srcdir)/iprop-commands.in
$(libkadm5srv_la_OBJECTS): kadm5_err.h
+$(libkadm5clnt_la_OBJECTS): kadm5_err.h
$(iprop_log_OBJECTS): iprop-commands.h
client_glue.lo server_glue.lo: $(srcdir)/common_glue.c
@@ -164,17 +169,30 @@ CLEANFILES = kadm5_err.c kadm5_err.h iprop-commands.h iprop-commands.c
kadm5_err.h: kadm5_err.et
-$(libkadm5clnt_la_OBJECTS) $(libkadm5srv_la_OBJECTS): $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
+ALL_OBJECTS = $(libkadm5clnt_la_OBJECTS)
+ALL_OBJECTS += $(libkadm5srv_la_OBJECTS)
+ALL_OBJECTS += $(ipropd_master_OBJECTS)
+ALL_OBJECTS += $(ipropd_slave_OBJECTS)
+ALL_OBJECTS += $(iprop_log_OBJECTS)
+ALL_OBJECTS += $(test_pw_quality_OBJECTS)
+ALL_OBJECTS += $(sample_passwd_check_la_OBJECTS)
+ALL_OBJECTS += $(default_keys_OBJECTS)
+
+$(ALL_OBJECTS): $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
+$(ALL_OBJECTS): kadm5_err.h
+
+KADM5_PROTOS_SRCS = $(dist_libkadm5clnt_la_SOURCES)
+KADM5_PROTOS_SRCS += $(dist_libkadm5srv_la_SOURCES)
proto_opts = -q -R '^(_|kadm5_c_|kadm5_s_|kadm5_log)' -P comment
-$(srcdir)/kadm5-protos.h:
+$(srcdir)/kadm5-protos.h: $(KADM5_PROTOS_SRCS)
cd $(srcdir); perl ../../cf/make-proto.pl $(proto_opts) \
-o kadm5-protos.h \
$(dist_libkadm5clnt_la_SOURCES) \
$(dist_libkadm5srv_la_SOURCES) \
|| rm -f kadm5-protos.h
-$(srcdir)/kadm5-private.h:
+$(srcdir)/kadm5-private.h: $(KADM5_PROTOS_SRCS)
cd $(srcdir); perl ../../cf/make-proto.pl $(proto_opts) \
-p kadm5-private.h \
$(dist_libkadm5clnt_la_SOURCES) \
@@ -194,4 +212,5 @@ EXTRA_DIST = \
check-cracklib.pl \
flush.c \
sample_passwd_check.c \
- version-script.map
+ version-script.map \
+ version-script-client.map
diff --git a/lib/kadm5/Makefile.in b/lib/kadm5/Makefile.in
index 71f7659c2482..4bbec3368320 100644
--- a/lib/kadm5/Makefile.in
+++ b/lib/kadm5/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,10 +96,8 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(dist_kadm5include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
-@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script-client.map
+@versionscript_TRUE@am__append_2 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
sbin_PROGRAMS = iprop-log$(EXEEXT)
check_PROGRAMS = default_keys$(EXEEXT)
noinst_PROGRAMS = test_pw_quality$(EXEEXT)
@@ -65,8 +117,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -79,6 +130,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -98,6 +150,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_kadm5include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -123,6 +177,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(libexecdir)" \
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man3dir)" \
"$(DESTDIR)$(man8dir)" "$(DESTDIR)$(kadm5includedir)" \
@@ -138,27 +198,42 @@ dist_libkadm5clnt_la_OBJECTS = ad.lo chpass_c.lo client_glue.lo \
nodist_libkadm5clnt_la_OBJECTS = kadm5_err.lo
libkadm5clnt_la_OBJECTS = $(dist_libkadm5clnt_la_OBJECTS) \
$(nodist_libkadm5clnt_la_OBJECTS)
-libkadm5clnt_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(libkadm5clnt_la_LDFLAGS) $(LDFLAGS) -o $@
-dist_libkadm5srv_la_OBJECTS = acl.lo bump_pw_expire.lo chpass_s.lo \
- common_glue.lo context_s.lo create_s.lo delete_s.lo \
- destroy_s.lo ent_setup.lo error.lo flush_s.lo free.lo \
- get_princs_s.lo get_s.lo init_s.lo keys.lo log.lo marshall.lo \
- modify_s.lo password_quality.lo privs_s.lo randkey_s.lo \
- rename_s.lo server_glue.lo set_keys.lo set_modifier.lo
-nodist_libkadm5srv_la_OBJECTS = kadm5_err.lo
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libkadm5clnt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libkadm5clnt_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+dist_libkadm5srv_la_OBJECTS = libkadm5srv_la-acl.lo \
+ libkadm5srv_la-bump_pw_expire.lo libkadm5srv_la-chpass_s.lo \
+ libkadm5srv_la-common_glue.lo libkadm5srv_la-context_s.lo \
+ libkadm5srv_la-create_s.lo libkadm5srv_la-delete_s.lo \
+ libkadm5srv_la-destroy_s.lo libkadm5srv_la-ent_setup.lo \
+ libkadm5srv_la-error.lo libkadm5srv_la-flush_s.lo \
+ libkadm5srv_la-free.lo libkadm5srv_la-get_princs_s.lo \
+ libkadm5srv_la-get_s.lo libkadm5srv_la-init_s.lo \
+ libkadm5srv_la-keys.lo libkadm5srv_la-log.lo \
+ libkadm5srv_la-marshall.lo libkadm5srv_la-modify_s.lo \
+ libkadm5srv_la-password_quality.lo libkadm5srv_la-privs_s.lo \
+ libkadm5srv_la-randkey_s.lo libkadm5srv_la-rename_s.lo \
+ libkadm5srv_la-server_glue.lo libkadm5srv_la-setkey3_s.lo \
+ libkadm5srv_la-set_keys.lo libkadm5srv_la-set_modifier.lo
+nodist_libkadm5srv_la_OBJECTS = libkadm5srv_la-kadm5_err.lo
libkadm5srv_la_OBJECTS = $(dist_libkadm5srv_la_OBJECTS) \
$(nodist_libkadm5srv_la_OBJECTS)
-libkadm5srv_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(libkadm5srv_la_LDFLAGS) $(LDFLAGS) -o $@
+libkadm5srv_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libkadm5srv_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
sample_passwd_check_la_LIBADD =
am_sample_passwd_check_la_OBJECTS = sample_passwd_check.lo
sample_passwd_check_la_OBJECTS = $(am_sample_passwd_check_la_OBJECTS)
-sample_passwd_check_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(sample_passwd_check_la_LDFLAGS) $(LDFLAGS) -o $@
+sample_passwd_check_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(sample_passwd_check_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
PROGRAMS = $(libexec_PROGRAMS) $(noinst_PROGRAMS) $(sbin_PROGRAMS)
am_default_keys_OBJECTS = default_keys.$(OBJEXT)
default_keys_OBJECTS = $(am_default_keys_OBJECTS)
@@ -168,7 +243,9 @@ default_keys_DEPENDENCIES = libkadm5srv.la \
$(top_builddir)/lib/krb5/libkrb5.la \
$(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
dist_iprop_log_OBJECTS = iprop-log.$(OBJEXT)
nodist_iprop_log_OBJECTS = iprop-commands.$(OBJEXT)
iprop_log_OBJECTS = $(dist_iprop_log_OBJECTS) \
@@ -179,7 +256,9 @@ iprop_log_DEPENDENCIES = libkadm5srv.la \
$(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/sl/libsl.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
am_ipropd_master_OBJECTS = ipropd_master.$(OBJEXT) \
ipropd_common.$(OBJEXT)
ipropd_master_OBJECTS = $(am_ipropd_master_OBJECTS)
@@ -189,7 +268,9 @@ ipropd_master_DEPENDENCIES = libkadm5srv.la \
$(top_builddir)/lib/krb5/libkrb5.la \
$(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
am_ipropd_slave_OBJECTS = ipropd_slave.$(OBJEXT) \
ipropd_common.$(OBJEXT)
ipropd_slave_OBJECTS = $(am_ipropd_slave_OBJECTS)
@@ -199,7 +280,9 @@ ipropd_slave_DEPENDENCIES = libkadm5srv.la \
$(top_builddir)/lib/krb5/libkrb5.la \
$(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
test_pw_quality_SOURCES = test_pw_quality.c
test_pw_quality_OBJECTS = test_pw_quality.$(OBJEXT)
test_pw_quality_LDADD = $(LDADD)
@@ -208,19 +291,42 @@ test_pw_quality_DEPENDENCIES = libkadm5srv.la \
$(top_builddir)/lib/krb5/libkrb5.la \
$(top_builddir)/lib/asn1/libasn1.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(dist_libkadm5clnt_la_SOURCES) \
$(nodist_libkadm5clnt_la_SOURCES) \
$(dist_libkadm5srv_la_SOURCES) \
@@ -234,17 +340,45 @@ DIST_SOURCES = $(dist_libkadm5clnt_la_SOURCES) \
$(sample_passwd_check_la_SOURCES) $(default_keys_SOURCES) \
$(dist_iprop_log_SOURCES) $(ipropd_master_SOURCES) \
$(ipropd_slave_SOURCES) test_pw_quality.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
man3dir = $(mandir)/man3
man8dir = $(mandir)/man8
MANS = $(man_MANS)
HEADERS = $(dist_kadm5include_HEADERS) $(nodist_kadm5include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -263,12 +397,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -278,17 +412,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -307,12 +441,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -321,6 +452,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -337,10 +469,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -348,6 +478,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -362,12 +493,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -384,6 +518,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -398,13 +533,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -428,6 +557,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -463,32 +594,41 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
+libkadm5srv_la_CPPFLAGS = -I$(srcdir)/../krb5
lib_LTLIBRARIES = libkadm5srv.la libkadm5clnt.la
-libkadm5srv_la_LDFLAGS = -version-info 8:1:0 $(am__append_1)
-libkadm5clnt_la_LDFLAGS = -version-info 7:1:0
+libkadm5srv_la_LDFLAGS = -version-info 8:1:0 $(am__append_2)
+libkadm5clnt_la_LDFLAGS = -version-info 7:1:0 $(am__append_1)
noinst_LTLIBRARIES = sample_passwd_check.la
sample_passwd_check_la_SOURCES = sample_passwd_check.c
sample_passwd_check_la_LDFLAGS = -module
@@ -503,7 +643,7 @@ default_keys_SOURCES = default_keys.c
kadm5includedir = $(includedir)/kadm5
buildkadm5include = $(buildinclude)/kadm5
dist_kadm5include_HEADERS = admin.h private.h kadm5-pwcheck.h \
- kadm5-protos.h kadm5-private.h
+ $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
nodist_kadm5include_HEADERS = kadm5_err.h
dist_libkadm5clnt_la_SOURCES = \
ad.c \
@@ -560,6 +700,7 @@ dist_libkadm5srv_la_SOURCES = \
randkey_s.c \
rename_s.c \
server_glue.c \
+ setkey3_s.c \
set_keys.c \
set_modifier.c \
admin.h
@@ -583,7 +724,7 @@ LDADD = \
$(top_builddir)/lib/asn1/libasn1.la \
$(LIB_hcrypto) \
$(LIB_roken) \
- $(DBLIB) \
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) \
$(LIB_dlopen) \
$(LIB_pidfile)
@@ -596,11 +737,17 @@ iprop_log_LDADD = \
$(top_builddir)/lib/sl/libsl.la \
$(LIB_readline) \
$(LIB_roken) \
- $(DBLIB) \
+ $(DB3LIB) $(DB1LIB) $(LMDBLIB) $(NDBMLIB) \
$(LIB_dlopen) \
$(LIB_pidfile)
CLEANFILES = kadm5_err.c kadm5_err.h iprop-commands.h iprop-commands.c
+ALL_OBJECTS = $(libkadm5clnt_la_OBJECTS) $(libkadm5srv_la_OBJECTS) \
+ $(ipropd_master_OBJECTS) $(ipropd_slave_OBJECTS) \
+ $(iprop_log_OBJECTS) $(test_pw_quality_OBJECTS) \
+ $(sample_passwd_check_la_OBJECTS) $(default_keys_OBJECTS)
+KADM5_PROTOS_SRCS = $(dist_libkadm5clnt_la_SOURCES) \
+ $(dist_libkadm5srv_la_SOURCES)
proto_opts = -q -R '^(_|kadm5_c_|kadm5_s_|kadm5_log)' -P comment
EXTRA_DIST = \
NTMakefile \
@@ -615,12 +762,13 @@ EXTRA_DIST = \
check-cracklib.pl \
flush.c \
sample_passwd_check.c \
- version-script.map
+ version-script.map \
+ version-script-client.map
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -633,7 +781,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/kadm5/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/kadm5/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -642,6 +789,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -651,9 +799,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -661,6 +809,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -676,27 +826,34 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
- @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libkadm5clnt.la: $(libkadm5clnt_la_OBJECTS) $(libkadm5clnt_la_DEPENDENCIES)
- $(libkadm5clnt_la_LINK) -rpath $(libdir) $(libkadm5clnt_la_OBJECTS) $(libkadm5clnt_la_LIBADD) $(LIBS)
-libkadm5srv.la: $(libkadm5srv_la_OBJECTS) $(libkadm5srv_la_DEPENDENCIES)
- $(libkadm5srv_la_LINK) -rpath $(libdir) $(libkadm5srv_la_OBJECTS) $(libkadm5srv_la_LIBADD) $(LIBS)
-sample_passwd_check.la: $(sample_passwd_check_la_OBJECTS) $(sample_passwd_check_la_DEPENDENCIES)
- $(sample_passwd_check_la_LINK) $(sample_passwd_check_la_OBJECTS) $(sample_passwd_check_la_LIBADD) $(LIBS)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libkadm5clnt.la: $(libkadm5clnt_la_OBJECTS) $(libkadm5clnt_la_DEPENDENCIES) $(EXTRA_libkadm5clnt_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libkadm5clnt_la_LINK) -rpath $(libdir) $(libkadm5clnt_la_OBJECTS) $(libkadm5clnt_la_LIBADD) $(LIBS)
+
+libkadm5srv.la: $(libkadm5srv_la_OBJECTS) $(libkadm5srv_la_DEPENDENCIES) $(EXTRA_libkadm5srv_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libkadm5srv_la_LINK) -rpath $(libdir) $(libkadm5srv_la_OBJECTS) $(libkadm5srv_la_LIBADD) $(LIBS)
+
+sample_passwd_check.la: $(sample_passwd_check_la_OBJECTS) $(sample_passwd_check_la_DEPENDENCIES) $(EXTRA_sample_passwd_check_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(sample_passwd_check_la_LINK) $(sample_passwd_check_la_OBJECTS) $(sample_passwd_check_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -708,14 +865,19 @@ clean-checkPROGRAMS:
rm -f $$list
install-libexecPROGRAMS: $(libexec_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)"
@list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -736,7 +898,8 @@ uninstall-libexecPROGRAMS:
@list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(libexecdir)" && rm -f $$files
@@ -760,14 +923,19 @@ clean-noinstPROGRAMS:
rm -f $$list
install-sbinPROGRAMS: $(sbin_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)"
@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -788,7 +956,8 @@ uninstall-sbinPROGRAMS:
@list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(sbindir)" && rm -f $$files
@@ -801,21 +970,26 @@ clean-sbinPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-default_keys$(EXEEXT): $(default_keys_OBJECTS) $(default_keys_DEPENDENCIES)
+
+default_keys$(EXEEXT): $(default_keys_OBJECTS) $(default_keys_DEPENDENCIES) $(EXTRA_default_keys_DEPENDENCIES)
@rm -f default_keys$(EXEEXT)
- $(LINK) $(default_keys_OBJECTS) $(default_keys_LDADD) $(LIBS)
-iprop-log$(EXEEXT): $(iprop_log_OBJECTS) $(iprop_log_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(default_keys_OBJECTS) $(default_keys_LDADD) $(LIBS)
+
+iprop-log$(EXEEXT): $(iprop_log_OBJECTS) $(iprop_log_DEPENDENCIES) $(EXTRA_iprop_log_DEPENDENCIES)
@rm -f iprop-log$(EXEEXT)
- $(LINK) $(iprop_log_OBJECTS) $(iprop_log_LDADD) $(LIBS)
-ipropd-master$(EXEEXT): $(ipropd_master_OBJECTS) $(ipropd_master_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(iprop_log_OBJECTS) $(iprop_log_LDADD) $(LIBS)
+
+ipropd-master$(EXEEXT): $(ipropd_master_OBJECTS) $(ipropd_master_DEPENDENCIES) $(EXTRA_ipropd_master_DEPENDENCIES)
@rm -f ipropd-master$(EXEEXT)
- $(LINK) $(ipropd_master_OBJECTS) $(ipropd_master_LDADD) $(LIBS)
-ipropd-slave$(EXEEXT): $(ipropd_slave_OBJECTS) $(ipropd_slave_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(ipropd_master_OBJECTS) $(ipropd_master_LDADD) $(LIBS)
+
+ipropd-slave$(EXEEXT): $(ipropd_slave_OBJECTS) $(ipropd_slave_DEPENDENCIES) $(EXTRA_ipropd_slave_DEPENDENCIES)
@rm -f ipropd-slave$(EXEEXT)
- $(LINK) $(ipropd_slave_OBJECTS) $(ipropd_slave_LDADD) $(LIBS)
-test_pw_quality$(EXEEXT): $(test_pw_quality_OBJECTS) $(test_pw_quality_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(ipropd_slave_OBJECTS) $(ipropd_slave_LDADD) $(LIBS)
+
+test_pw_quality$(EXEEXT): $(test_pw_quality_OBJECTS) $(test_pw_quality_DEPENDENCIES) $(EXTRA_test_pw_quality_DEPENDENCIES)
@rm -f test_pw_quality$(EXEEXT)
- $(LINK) $(test_pw_quality_OBJECTS) $(test_pw_quality_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_pw_quality_OBJECTS) $(test_pw_quality_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -823,77 +997,278 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/acl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ad.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bump_pw_expire.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chpass_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/chpass_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client_glue.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common_glue.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/context_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/create_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/default_keys.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delete_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/destroy_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/destroy_s.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ent_setup.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flush_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/flush_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/free.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_c.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_princs_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_princs_s.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/get_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/init_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iprop-commands.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iprop-log.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipropd_common.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipropd_master.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ipropd_slave.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kadm5_err.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keys.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-acl.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-bump_pw_expire.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-chpass_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-common_glue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-context_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-create_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-delete_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-destroy_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-ent_setup.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-error.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-flush_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-free.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-get_princs_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-get_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-init_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-kadm5_err.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-keys.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-log.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-marshall.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-modify_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-password_quality.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-privs_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-randkey_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-rename_s.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-server_glue.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-set_keys.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-set_modifier.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkadm5srv_la-setkey3_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/marshall.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modify_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/modify_s.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/password_quality.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privs_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privs_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/randkey_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/randkey_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rename_c.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rename_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sample_passwd_check.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/send_recv.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_glue.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_keys.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/set_modifier.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_pw_quality.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+libkadm5srv_la-acl.lo: acl.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-acl.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-acl.Tpo -c -o libkadm5srv_la-acl.lo `test -f 'acl.c' || echo '$(srcdir)/'`acl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-acl.Tpo $(DEPDIR)/libkadm5srv_la-acl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='libkadm5srv_la-acl.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-acl.lo `test -f 'acl.c' || echo '$(srcdir)/'`acl.c
+
+libkadm5srv_la-bump_pw_expire.lo: bump_pw_expire.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-bump_pw_expire.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-bump_pw_expire.Tpo -c -o libkadm5srv_la-bump_pw_expire.lo `test -f 'bump_pw_expire.c' || echo '$(srcdir)/'`bump_pw_expire.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-bump_pw_expire.Tpo $(DEPDIR)/libkadm5srv_la-bump_pw_expire.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bump_pw_expire.c' object='libkadm5srv_la-bump_pw_expire.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-bump_pw_expire.lo `test -f 'bump_pw_expire.c' || echo '$(srcdir)/'`bump_pw_expire.c
+
+libkadm5srv_la-chpass_s.lo: chpass_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-chpass_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-chpass_s.Tpo -c -o libkadm5srv_la-chpass_s.lo `test -f 'chpass_s.c' || echo '$(srcdir)/'`chpass_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-chpass_s.Tpo $(DEPDIR)/libkadm5srv_la-chpass_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='chpass_s.c' object='libkadm5srv_la-chpass_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-chpass_s.lo `test -f 'chpass_s.c' || echo '$(srcdir)/'`chpass_s.c
+
+libkadm5srv_la-common_glue.lo: common_glue.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-common_glue.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-common_glue.Tpo -c -o libkadm5srv_la-common_glue.lo `test -f 'common_glue.c' || echo '$(srcdir)/'`common_glue.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-common_glue.Tpo $(DEPDIR)/libkadm5srv_la-common_glue.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='common_glue.c' object='libkadm5srv_la-common_glue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-common_glue.lo `test -f 'common_glue.c' || echo '$(srcdir)/'`common_glue.c
+
+libkadm5srv_la-context_s.lo: context_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-context_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-context_s.Tpo -c -o libkadm5srv_la-context_s.lo `test -f 'context_s.c' || echo '$(srcdir)/'`context_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-context_s.Tpo $(DEPDIR)/libkadm5srv_la-context_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context_s.c' object='libkadm5srv_la-context_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-context_s.lo `test -f 'context_s.c' || echo '$(srcdir)/'`context_s.c
+
+libkadm5srv_la-create_s.lo: create_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-create_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-create_s.Tpo -c -o libkadm5srv_la-create_s.lo `test -f 'create_s.c' || echo '$(srcdir)/'`create_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-create_s.Tpo $(DEPDIR)/libkadm5srv_la-create_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='create_s.c' object='libkadm5srv_la-create_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-create_s.lo `test -f 'create_s.c' || echo '$(srcdir)/'`create_s.c
+
+libkadm5srv_la-delete_s.lo: delete_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-delete_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-delete_s.Tpo -c -o libkadm5srv_la-delete_s.lo `test -f 'delete_s.c' || echo '$(srcdir)/'`delete_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-delete_s.Tpo $(DEPDIR)/libkadm5srv_la-delete_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='delete_s.c' object='libkadm5srv_la-delete_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-delete_s.lo `test -f 'delete_s.c' || echo '$(srcdir)/'`delete_s.c
+
+libkadm5srv_la-destroy_s.lo: destroy_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-destroy_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-destroy_s.Tpo -c -o libkadm5srv_la-destroy_s.lo `test -f 'destroy_s.c' || echo '$(srcdir)/'`destroy_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-destroy_s.Tpo $(DEPDIR)/libkadm5srv_la-destroy_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='destroy_s.c' object='libkadm5srv_la-destroy_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-destroy_s.lo `test -f 'destroy_s.c' || echo '$(srcdir)/'`destroy_s.c
+
+libkadm5srv_la-ent_setup.lo: ent_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-ent_setup.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-ent_setup.Tpo -c -o libkadm5srv_la-ent_setup.lo `test -f 'ent_setup.c' || echo '$(srcdir)/'`ent_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-ent_setup.Tpo $(DEPDIR)/libkadm5srv_la-ent_setup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ent_setup.c' object='libkadm5srv_la-ent_setup.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-ent_setup.lo `test -f 'ent_setup.c' || echo '$(srcdir)/'`ent_setup.c
+
+libkadm5srv_la-error.lo: error.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-error.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-error.Tpo -c -o libkadm5srv_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-error.Tpo $(DEPDIR)/libkadm5srv_la-error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error.c' object='libkadm5srv_la-error.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-error.lo `test -f 'error.c' || echo '$(srcdir)/'`error.c
+
+libkadm5srv_la-flush_s.lo: flush_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-flush_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-flush_s.Tpo -c -o libkadm5srv_la-flush_s.lo `test -f 'flush_s.c' || echo '$(srcdir)/'`flush_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-flush_s.Tpo $(DEPDIR)/libkadm5srv_la-flush_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='flush_s.c' object='libkadm5srv_la-flush_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-flush_s.lo `test -f 'flush_s.c' || echo '$(srcdir)/'`flush_s.c
+
+libkadm5srv_la-free.lo: free.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-free.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-free.Tpo -c -o libkadm5srv_la-free.lo `test -f 'free.c' || echo '$(srcdir)/'`free.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-free.Tpo $(DEPDIR)/libkadm5srv_la-free.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='free.c' object='libkadm5srv_la-free.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-free.lo `test -f 'free.c' || echo '$(srcdir)/'`free.c
+
+libkadm5srv_la-get_princs_s.lo: get_princs_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-get_princs_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-get_princs_s.Tpo -c -o libkadm5srv_la-get_princs_s.lo `test -f 'get_princs_s.c' || echo '$(srcdir)/'`get_princs_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-get_princs_s.Tpo $(DEPDIR)/libkadm5srv_la-get_princs_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_princs_s.c' object='libkadm5srv_la-get_princs_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-get_princs_s.lo `test -f 'get_princs_s.c' || echo '$(srcdir)/'`get_princs_s.c
+
+libkadm5srv_la-get_s.lo: get_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-get_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-get_s.Tpo -c -o libkadm5srv_la-get_s.lo `test -f 'get_s.c' || echo '$(srcdir)/'`get_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-get_s.Tpo $(DEPDIR)/libkadm5srv_la-get_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_s.c' object='libkadm5srv_la-get_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-get_s.lo `test -f 'get_s.c' || echo '$(srcdir)/'`get_s.c
+
+libkadm5srv_la-init_s.lo: init_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-init_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-init_s.Tpo -c -o libkadm5srv_la-init_s.lo `test -f 'init_s.c' || echo '$(srcdir)/'`init_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-init_s.Tpo $(DEPDIR)/libkadm5srv_la-init_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init_s.c' object='libkadm5srv_la-init_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-init_s.lo `test -f 'init_s.c' || echo '$(srcdir)/'`init_s.c
+
+libkadm5srv_la-keys.lo: keys.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-keys.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-keys.Tpo -c -o libkadm5srv_la-keys.lo `test -f 'keys.c' || echo '$(srcdir)/'`keys.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-keys.Tpo $(DEPDIR)/libkadm5srv_la-keys.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keys.c' object='libkadm5srv_la-keys.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-keys.lo `test -f 'keys.c' || echo '$(srcdir)/'`keys.c
+
+libkadm5srv_la-log.lo: log.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-log.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-log.Tpo -c -o libkadm5srv_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-log.Tpo $(DEPDIR)/libkadm5srv_la-log.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='libkadm5srv_la-log.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+
+libkadm5srv_la-marshall.lo: marshall.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-marshall.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-marshall.Tpo -c -o libkadm5srv_la-marshall.lo `test -f 'marshall.c' || echo '$(srcdir)/'`marshall.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-marshall.Tpo $(DEPDIR)/libkadm5srv_la-marshall.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='marshall.c' object='libkadm5srv_la-marshall.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-marshall.lo `test -f 'marshall.c' || echo '$(srcdir)/'`marshall.c
+
+libkadm5srv_la-modify_s.lo: modify_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-modify_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-modify_s.Tpo -c -o libkadm5srv_la-modify_s.lo `test -f 'modify_s.c' || echo '$(srcdir)/'`modify_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-modify_s.Tpo $(DEPDIR)/libkadm5srv_la-modify_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='modify_s.c' object='libkadm5srv_la-modify_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-modify_s.lo `test -f 'modify_s.c' || echo '$(srcdir)/'`modify_s.c
+
+libkadm5srv_la-password_quality.lo: password_quality.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-password_quality.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-password_quality.Tpo -c -o libkadm5srv_la-password_quality.lo `test -f 'password_quality.c' || echo '$(srcdir)/'`password_quality.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-password_quality.Tpo $(DEPDIR)/libkadm5srv_la-password_quality.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='password_quality.c' object='libkadm5srv_la-password_quality.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-password_quality.lo `test -f 'password_quality.c' || echo '$(srcdir)/'`password_quality.c
+
+libkadm5srv_la-privs_s.lo: privs_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-privs_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-privs_s.Tpo -c -o libkadm5srv_la-privs_s.lo `test -f 'privs_s.c' || echo '$(srcdir)/'`privs_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-privs_s.Tpo $(DEPDIR)/libkadm5srv_la-privs_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='privs_s.c' object='libkadm5srv_la-privs_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-privs_s.lo `test -f 'privs_s.c' || echo '$(srcdir)/'`privs_s.c
+
+libkadm5srv_la-randkey_s.lo: randkey_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-randkey_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-randkey_s.Tpo -c -o libkadm5srv_la-randkey_s.lo `test -f 'randkey_s.c' || echo '$(srcdir)/'`randkey_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-randkey_s.Tpo $(DEPDIR)/libkadm5srv_la-randkey_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='randkey_s.c' object='libkadm5srv_la-randkey_s.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-randkey_s.lo `test -f 'randkey_s.c' || echo '$(srcdir)/'`randkey_s.c
+
+libkadm5srv_la-rename_s.lo: rename_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-rename_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-rename_s.Tpo -c -o libkadm5srv_la-rename_s.lo `test -f 'rename_s.c' || echo '$(srcdir)/'`rename_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-rename_s.Tpo $(DEPDIR)/libkadm5srv_la-rename_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rename_s.c' object='libkadm5srv_la-rename_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-rename_s.lo `test -f 'rename_s.c' || echo '$(srcdir)/'`rename_s.c
+
+libkadm5srv_la-server_glue.lo: server_glue.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-server_glue.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-server_glue.Tpo -c -o libkadm5srv_la-server_glue.lo `test -f 'server_glue.c' || echo '$(srcdir)/'`server_glue.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-server_glue.Tpo $(DEPDIR)/libkadm5srv_la-server_glue.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='server_glue.c' object='libkadm5srv_la-server_glue.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-server_glue.lo `test -f 'server_glue.c' || echo '$(srcdir)/'`server_glue.c
+
+libkadm5srv_la-setkey3_s.lo: setkey3_s.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-setkey3_s.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-setkey3_s.Tpo -c -o libkadm5srv_la-setkey3_s.lo `test -f 'setkey3_s.c' || echo '$(srcdir)/'`setkey3_s.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-setkey3_s.Tpo $(DEPDIR)/libkadm5srv_la-setkey3_s.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setkey3_s.c' object='libkadm5srv_la-setkey3_s.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-setkey3_s.lo `test -f 'setkey3_s.c' || echo '$(srcdir)/'`setkey3_s.c
+
+libkadm5srv_la-set_keys.lo: set_keys.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-set_keys.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-set_keys.Tpo -c -o libkadm5srv_la-set_keys.lo `test -f 'set_keys.c' || echo '$(srcdir)/'`set_keys.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-set_keys.Tpo $(DEPDIR)/libkadm5srv_la-set_keys.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='set_keys.c' object='libkadm5srv_la-set_keys.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-set_keys.lo `test -f 'set_keys.c' || echo '$(srcdir)/'`set_keys.c
+
+libkadm5srv_la-set_modifier.lo: set_modifier.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-set_modifier.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-set_modifier.Tpo -c -o libkadm5srv_la-set_modifier.lo `test -f 'set_modifier.c' || echo '$(srcdir)/'`set_modifier.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-set_modifier.Tpo $(DEPDIR)/libkadm5srv_la-set_modifier.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='set_modifier.c' object='libkadm5srv_la-set_modifier.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-set_modifier.lo `test -f 'set_modifier.c' || echo '$(srcdir)/'`set_modifier.c
+
+libkadm5srv_la-kadm5_err.lo: kadm5_err.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkadm5srv_la-kadm5_err.lo -MD -MP -MF $(DEPDIR)/libkadm5srv_la-kadm5_err.Tpo -c -o libkadm5srv_la-kadm5_err.lo `test -f 'kadm5_err.c' || echo '$(srcdir)/'`kadm5_err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkadm5srv_la-kadm5_err.Tpo $(DEPDIR)/libkadm5srv_la-kadm5_err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kadm5_err.c' object='libkadm5srv_la-kadm5_err.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkadm5srv_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkadm5srv_la-kadm5_err.lo `test -f 'kadm5_err.c' || echo '$(srcdir)/'`kadm5_err.c
mostlyclean-libtool:
-rm -f *.lo
@@ -902,11 +1277,18 @@ clean-libtool:
-rm -rf .libs _libs
install-man3: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man3dir)" || $(MKDIR_P) "$(DESTDIR)$(man3dir)"
- @list=''; test -n "$(man3dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.3[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.3[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -935,16 +1317,21 @@ uninstall-man3:
sed -n '/\.3[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man3dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man3dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
install-man8: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
- @list=''; test -n "$(man8dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.8[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -973,13 +1360,14 @@ uninstall-man8:
sed -n '/\.8[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
install-dist_kadm5includeHEADERS: $(dist_kadm5include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(kadm5includedir)" || $(MKDIR_P) "$(DESTDIR)$(kadm5includedir)"
@list='$(dist_kadm5include_HEADERS)'; test -n "$(kadm5includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(kadm5includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(kadm5includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -993,13 +1381,14 @@ uninstall-dist_kadm5includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_kadm5include_HEADERS)'; test -n "$(kadm5includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(kadm5includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(kadm5includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(kadm5includedir)'; $(am__uninstall_files_from_dir)
install-nodist_kadm5includeHEADERS: $(nodist_kadm5include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(kadm5includedir)" || $(MKDIR_P) "$(DESTDIR)$(kadm5includedir)"
@list='$(nodist_kadm5include_HEADERS)'; test -n "$(kadm5includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(kadm5includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(kadm5includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1013,30 +1402,17 @@ uninstall-nodist_kadm5includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_kadm5include_HEADERS)'; test -n "$(kadm5includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(kadm5includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(kadm5includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(kadm5includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -1048,15 +1424,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -1065,24 +1437,26 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
- @list='$(MANS)'; if test -n "$$list"; then \
- list=`for p in $$list; do \
- if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
- if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
- if test -n "$$list" && \
- grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
- echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
- grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
- echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
- echo " typically \`make maintainer-clean' will remove them" >&2; \
- exit 1; \
- else :; fi; \
- else :; fi
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -1135,10 +1509,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -1183,10 +1562,9 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES install-libexecPROGRAMS \
- install-sbinPROGRAMS
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES \
+ install-libexecPROGRAMS install-sbinPROGRAMS
+
install-html: install-html-am
install-html-am:
@@ -1233,19 +1611,19 @@ uninstall-am: uninstall-dist_kadm5includeHEADERS \
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
uninstall-man: uninstall-man3 uninstall-man8
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
- clean-libexecPROGRAMS clean-libtool clean-noinstLTLIBRARIES \
- clean-noinstPROGRAMS clean-sbinPROGRAMS ctags dist-hook \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libexecPROGRAMS clean-libtool \
+ clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ clean-sbinPROGRAMS cscopelist-am ctags ctags-am dist-hook \
distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-data-hook \
install-dist_kadm5includeHEADERS install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
+ install-exec install-exec-am install-exec-local install-html \
install-html-am install-info install-info-am \
install-libLTLIBRARIES install-libexecPROGRAMS install-man \
install-man3 install-man8 install-nodist_kadm5includeHEADERS \
@@ -1254,27 +1632,41 @@ uninstall-man: uninstall-man3 uninstall-man8
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-dist_kadm5includeHEADERS \
- uninstall-hook uninstall-libLTLIBRARIES \
- uninstall-libexecPROGRAMS uninstall-man uninstall-man3 \
- uninstall-man8 uninstall-nodist_kadm5includeHEADERS \
- uninstall-sbinPROGRAMS
+ tags tags-am uninstall uninstall-am \
+ uninstall-dist_kadm5includeHEADERS uninstall-hook \
+ uninstall-libLTLIBRARIES uninstall-libexecPROGRAMS \
+ uninstall-man uninstall-man3 uninstall-man8 \
+ uninstall-nodist_kadm5includeHEADERS uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-exec-hook: install-suid-programs
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -1282,7 +1674,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -1339,6 +1731,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1381,6 +1775,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1394,13 +1801,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -1448,6 +1855,7 @@ iprop-commands.c iprop-commands.h: iprop-commands.in
$(SLC) $(srcdir)/iprop-commands.in
$(libkadm5srv_la_OBJECTS): kadm5_err.h
+$(libkadm5clnt_la_OBJECTS): kadm5_err.h
$(iprop_log_OBJECTS): iprop-commands.h
client_glue.lo server_glue.lo: $(srcdir)/common_glue.c
@@ -1456,15 +1864,16 @@ client_glue.lo server_glue.lo: $(srcdir)/common_glue.c
kadm5_err.h: kadm5_err.et
-$(libkadm5clnt_la_OBJECTS) $(libkadm5srv_la_OBJECTS): $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
-$(srcdir)/kadm5-protos.h:
+$(ALL_OBJECTS): $(srcdir)/kadm5-protos.h $(srcdir)/kadm5-private.h
+$(ALL_OBJECTS): kadm5_err.h
+$(srcdir)/kadm5-protos.h: $(KADM5_PROTOS_SRCS)
cd $(srcdir); perl ../../cf/make-proto.pl $(proto_opts) \
-o kadm5-protos.h \
$(dist_libkadm5clnt_la_SOURCES) \
$(dist_libkadm5srv_la_SOURCES) \
|| rm -f kadm5-protos.h
-$(srcdir)/kadm5-private.h:
+$(srcdir)/kadm5-private.h: $(KADM5_PROTOS_SRCS)
cd $(srcdir); perl ../../cf/make-proto.pl $(proto_opts) \
-p kadm5-private.h \
$(dist_libkadm5clnt_la_SOURCES) \
diff --git a/lib/kadm5/NTMakefile b/lib/kadm5/NTMakefile
index 3afe8e899543..392a884382b0 100644
--- a/lib/kadm5/NTMakefile
+++ b/lib/kadm5/NTMakefile
@@ -86,6 +86,7 @@ dist_libkadm5srv_la_SOURCES = \
rename_s.c \
server_glue.c \
set_keys.c \
+ setkey3_s.c \
set_modifier.c \
kadm5-pwcheck.h \
admin.h
@@ -137,6 +138,7 @@ LIBKADM5SRV_OBJS= \
$(OBJ)\rename_s.obj \
$(OBJ)\server_glue.obj \
$(OBJ)\set_keys.obj \
+ $(OBJ)\setkey3_s.obj \
$(OBJ)\set_modifier.obj \
$(OBJ)\kadm5_err.obj
@@ -216,9 +218,9 @@ LIBKADM5SRVRES=$(OBJ)\libkadm5srv-version.res
$(LIBKADM5SRV): $(BINDIR)\libkadm5srv.dll
-$(BINDIR)\libkadm5srv.dll: $(LIBKADM5SRV_OBJS) $(LIBHEIMDAL) $(LIBROKEN) $(LIBHDB) $(LIBCOMERR) $(LIBSQLITE) $(LIBKADM5SRVRES)
+$(BINDIR)\libkadm5srv.dll: $(LIBKADM5SRV_OBJS) $(LIBHEIMDAL) $(LIBROKEN) $(LIBHDB) $(LIBCOMERR) $(LIBSQLITE) $(LIBKADM5SRVRES) $(LIBHEIMBASE)
$(DLLGUILINK) -implib:$(LIBKADM5SRV) -def:libkadm5srv-exports.def
- $(DLLPREP)
+ $(DLLPREP_NODIST)
all:: $(INCFILES) $(LIBKADM5SRV) $(LIBKADM5CLNT)
@@ -257,6 +259,10 @@ EXPORTS
$(DLLPREP_NODIST)
test-run:
+ cd $(OBJ)
+ -default_keys.exe
+ -test_pw_quality.exe
+ cd $(SRCDIR)
{$(OBJ)}.h{$(KADM5INCDIR)}.h:
$(CP) $< $@
diff --git a/lib/kadm5/acl.c b/lib/kadm5/acl.c
index 5e263a32513a..fdee715f99be 100644
--- a/lib/kadm5/acl.c
+++ b/lib/kadm5/acl.c
@@ -44,6 +44,7 @@ static struct units acl_units[] = {
{ "modify", KADM5_PRIV_MODIFY },
{ "add", KADM5_PRIV_ADD },
{ "get", KADM5_PRIV_GET },
+ { "get-keys", KADM5_PRIV_GET_KEYS },
{ NULL, 0 }
};
@@ -177,6 +178,8 @@ check_flags (unsigned op,
if(res & KADM5_PRIV_GET)
return KADM5_AUTH_GET;
+ if(res & KADM5_PRIV_GET_KEYS)
+ return KADM5_AUTH_GET_KEYS;
if(res & KADM5_PRIV_ADD)
return KADM5_AUTH_ADD;
if(res & KADM5_PRIV_MODIFY)
diff --git a/lib/kadm5/ad.c b/lib/kadm5/ad.c
index 4ea5cdb08e85..787081c8cd2b 100644
--- a/lib/kadm5/ad.c
+++ b/lib/kadm5/ad.c
@@ -484,13 +484,14 @@ ad_get_cred(kadm5_ad_context *context, const char *password)
kadm5_ret_t ret;
krb5_ccache cc;
char *service;
+ int aret;
if (context->ccache)
return 0;
- asprintf(&service, "%s/%s@%s", KRB5_TGS_NAME,
- context->realm, context->realm);
- if (service == NULL)
+ aret = asprintf(&service, "%s/%s@%s", KRB5_TGS_NAME,
+ context->realm, context->realm);
+ if (aret == -1 || service == NULL)
return ENOMEM;
ret = _kadm5_c_get_cred_cache(context->context,
@@ -508,6 +509,9 @@ ad_get_cred(kadm5_ad_context *context, const char *password)
static kadm5_ret_t
kadm5_ad_chpass_principal(void *server_handle,
krb5_principal principal,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password)
{
kadm5_ad_context *context = server_handle;
@@ -515,6 +519,12 @@ kadm5_ad_chpass_principal(void *server_handle,
int result_code;
kadm5_ret_t ret;
+ if (keepold)
+ return KADM5_KEEPOLD_NOSUPP;
+
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
ret = ad_get_cred(context, NULL);
if (ret)
return ret;
@@ -562,6 +572,8 @@ static kadm5_ret_t
kadm5_ad_create_principal(void *server_handle,
kadm5_principal_ent_t entry,
uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password)
{
kadm5_ad_context *context = server_handle;
@@ -587,6 +599,14 @@ kadm5_ad_create_principal(void *server_handle,
if ((mask & KADM5_PRINCIPAL) == 0)
return KADM5_BAD_MASK;
+ /*
+ * We should get around to implementing this... At the moment, the
+ * the server side API is implemented but the wire protocol has not
+ * been updated.
+ */
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
for (i = 0; i < sizeof(rattrs)/sizeof(rattrs[0]); i++)
attrs[i] = &rattrs[i];
attrs[i] = NULL;
@@ -1224,14 +1244,21 @@ kadm5_ad_modify_principal(void *server_handle,
#endif
}
+/*ARGSUSED*/
static kadm5_ret_t
kadm5_ad_randkey_principal(void *server_handle,
krb5_principal principal,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **keys,
int *n_keys)
{
kadm5_ad_context *context = server_handle;
+ if (keepold)
+ return KADM5_KEEPOLD_NOSUPP;
+
/*
* random key
*/
@@ -1248,7 +1275,7 @@ kadm5_ad_randkey_principal(void *server_handle,
{
char p[64];
krb5_generate_random_block(p, sizeof(p));
- plen = base64_encode(p, sizeof(p), &password);
+ plen = rk_base64_encode(p, sizeof(p), &password);
if (plen < 0)
return ENOMEM;
}
@@ -1259,45 +1286,44 @@ kadm5_ad_randkey_principal(void *server_handle,
return ret;
}
- krb5_data_zero (&result_code_string);
- krb5_data_zero (&result_string);
+ krb5_data_zero(&result_code_string);
+ krb5_data_zero(&result_string);
- ret = krb5_set_password_using_ccache (context->context,
- context->ccache,
- password,
- principal,
- &result_code,
- &result_code_string,
- &result_string);
+ ret = krb5_set_password_using_ccache(context->context,
+ context->ccache,
+ password,
+ principal,
+ &result_code,
+ &result_code_string,
+ &result_string);
+ krb5_data_free(&result_code_string);
+ krb5_data_free(&result_string);
- krb5_data_free (&result_code_string);
- krb5_data_free (&result_string);
+ if (ret)
+ goto out;
- if (ret == 0) {
+ *keys = malloc(sizeof(**keys) * 1);
+ if (*keys == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ *n_keys = 1;
- *keys = malloc(sizeof(**keys) * 1);
- if (*keys == NULL) {
- ret = ENOMEM;
- goto out;
- }
- *n_keys = 1;
-
- ret = krb5_string_to_key(context->context,
- ENCTYPE_ARCFOUR_HMAC_MD5,
- password,
- principal,
- &(*keys)[0]);
- memset(password, 0, sizeof(password));
- if (ret) {
- free(*keys);
- *keys = NULL;
- *n_keys = 0;
- goto out;
- }
+ ret = krb5_string_to_key(context->context,
+ ENCTYPE_ARCFOUR_HMAC_MD5,
+ password,
+ principal,
+ &(*keys)[0]);
+ if (ret) {
+ free(*keys);
+ *keys = NULL;
+ *n_keys = 0;
+ goto out;
}
+
+ out:
memset(password, 0, plen);
free(password);
- out:
return ret;
#else
*keys = NULL;
@@ -1321,6 +1347,7 @@ kadm5_ad_rename_principal(void *server_handle,
static kadm5_ret_t
kadm5_ad_chpass_principal_with_key(void *server_handle,
krb5_principal princ,
+ int keepold,
int n_key_data,
krb5_key_data *key_data)
{
@@ -1329,10 +1356,23 @@ kadm5_ad_chpass_principal_with_key(void *server_handle,
return KADM5_RPC_ERROR;
}
+static kadm5_ret_t
+kadm5_ad_lock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
+static kadm5_ret_t
+kadm5_ad_unlock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
static void
set_funcs(kadm5_ad_context *c)
{
#define SET(C, F) (C)->funcs.F = kadm5_ad_ ## F
+#define SETNOTIMP(C, F) (C)->funcs.F = 0
SET(c, chpass_principal);
SET(c, chpass_principal_with_key);
SET(c, create_principal);
@@ -1345,6 +1385,9 @@ set_funcs(kadm5_ad_context *c)
SET(c, modify_principal);
SET(c, randkey_principal);
SET(c, rename_principal);
+ SET(c, lock);
+ SET(c, unlock);
+ SETNOTIMP(c, setkey_principal_3);
}
kadm5_ret_t
diff --git a/lib/kadm5/admin.h b/lib/kadm5/admin.h
index e3e6755c361d..5b22d178fd26 100644
--- a/lib/kadm5/admin.h
+++ b/lib/kadm5/admin.h
@@ -48,6 +48,11 @@
#define KADM5_STRUCT_VERSION 0
+/* For kadm5_log_get_version_fd() */
+#define LOG_VERSION_LAST -1
+#define LOG_VERSION_FIRST 1
+#define LOG_VERSION_UBER 0
+
#include <krb5.h>
#define KRB5_KDB_DISALLOW_POSTDATED 0x00000001
@@ -112,6 +117,11 @@ typedef struct {
void* key_data_contents[2];/* Array of pointers */
} krb5_key_data;
+typedef struct _krb5_keysalt {
+ int16_t type;
+ krb5_data data; /* Length, data */
+} krb5_keysalt;
+
typedef struct _krb5_tl_data {
struct _krb5_tl_data* tl_data_next;
int16_t tl_data_type;
@@ -129,6 +139,8 @@ typedef struct _krb5_tl_data {
#define KRB5_TL_EXTENSION 0x0008
#define KRB5_TL_PKINIT_ACL 0x0009
#define KRB5_TL_ALIASES 0x000a
+#define KRB5_TL_HIST_KVNO_DIFF_CLNT 0x000b
+#define KRB5_TL_HIST_KVNO_DIFF_SVC 0x000c
typedef struct _kadm5_principal_ent_t {
krb5_principal principal;
@@ -193,11 +205,12 @@ typedef struct _kadm5_policy_ent_t {
#define KADM5_PRIV_DELETE (1 << 3)
#define KADM5_PRIV_LIST (1 << 4)
#define KADM5_PRIV_CPW (1 << 5)
+#define KADM5_PRIV_GET_KEYS (1 << 6)
+
+/* Note: KADM5_PRIV_GET_KEYS not included */
#define KADM5_PRIV_ALL (KADM5_PRIV_GET | KADM5_PRIV_ADD | KADM5_PRIV_MODIFY | KADM5_PRIV_DELETE | KADM5_PRIV_LIST | KADM5_PRIV_CPW)
-typedef struct {
- int XXX;
-}krb5_key_salt_tuple;
+#define KADM5_BOGUS_KEY_DATA "\xe5\xe5\xe5\xe5"
typedef struct _kadm5_config_params {
uint32_t mask;
@@ -221,38 +234,4 @@ typedef krb5_error_code kadm5_ret_t;
#include "kadm5-protos.h"
-#if 0
-/* unimplemented functions */
-kadm5_ret_t
-kadm5_decrypt_key(void *server_handle,
- kadm5_principal_ent_t entry, int32_t
- ktype, int32_t stype, int32_t
- kvno, krb5_keyblock *keyblock,
- krb5_keysalt *keysalt, int *kvnop);
-
-kadm5_ret_t
-kadm5_create_policy(void *server_handle,
- kadm5_policy_ent_t policy, uint32_t mask);
-
-kadm5_ret_t
-kadm5_delete_policy(void *server_handle, char *policy);
-
-
-kadm5_ret_t
-kadm5_modify_policy(void *server_handle,
- kadm5_policy_ent_t policy,
- uint32_t mask);
-
-kadm5_ret_t
-kadm5_get_policy(void *server_handle, char *policy, kadm5_policy_ent_t ent);
-
-kadm5_ret_t
-kadm5_get_policies(void *server_handle, char *exp,
- char ***pols, int *count);
-
-void
-kadm5_free_policy_ent(kadm5_policy_ent_t policy);
-
-#endif
-
#endif /* __KADM5_ADMIN_H__ */
diff --git a/lib/kadm5/chpass_c.c b/lib/kadm5/chpass_c.c
index af4328c9c4fc..4512942b4626 100644
--- a/lib/kadm5/chpass_c.c
+++ b/lib/kadm5/chpass_c.c
@@ -38,6 +38,9 @@ RCSID("$Id$");
kadm5_ret_t
kadm5_c_chpass_principal(void *server_handle,
krb5_principal princ,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password)
{
kadm5_client_context *context = server_handle;
@@ -47,6 +50,14 @@ kadm5_c_chpass_principal(void *server_handle,
int32_t tmp;
krb5_data reply;
+ /*
+ * We should get around to implementing this... At the moment, the
+ * the server side API is implemented but the wire protocol has not
+ * been updated.
+ */
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
ret = _kadm5_connect(server_handle);
if(ret)
return ret;
@@ -59,6 +70,7 @@ kadm5_c_chpass_principal(void *server_handle,
krb5_store_int32(sp, kadm_chpass);
krb5_store_principal(sp, princ);
krb5_store_string(sp, password);
+ krb5_store_int32(sp, keepold); /* extension */
ret = _kadm5_client_send(context, sp);
krb5_storage_free(sp);
if (ret)
@@ -82,6 +94,7 @@ kadm5_c_chpass_principal(void *server_handle,
kadm5_ret_t
kadm5_c_chpass_principal_with_key(void *server_handle,
krb5_principal princ,
+ int keepold,
int n_key_data,
krb5_key_data *key_data)
{
@@ -107,6 +120,7 @@ kadm5_c_chpass_principal_with_key(void *server_handle,
krb5_store_int32(sp, n_key_data);
for (i = 0; i < n_key_data; ++i)
kadm5_store_key_data (sp, &key_data[i]);
+ krb5_store_int32(sp, keepold); /* extension */
ret = _kadm5_client_send(context, sp);
krb5_storage_free(sp);
if (ret)
diff --git a/lib/kadm5/chpass_s.c b/lib/kadm5/chpass_s.c
index d5407d1549f4..d1ed73207420 100644
--- a/lib/kadm5/chpass_s.c
+++ b/lib/kadm5/chpass_s.c
@@ -38,6 +38,9 @@ RCSID("$Id$");
static kadm5_ret_t
change(void *server_handle,
krb5_principal princ,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password,
int cond)
{
@@ -49,20 +52,36 @@ change(void *server_handle,
int existsp = 0;
memset(&ent, 0, sizeof(ent));
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret)
- return ret;
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
- if(ret)
- goto out;
+ if (ret)
+ goto out2;
+
+ if (keepold || cond) {
+ /*
+ * We save these for now so we can handle password history checking;
+ * we handle keepold further below.
+ */
+ ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
+ if (ret)
+ goto out3;
+ }
if (context->db->hdb_capability_flags & HDB_CAP_F_HANDLE_PASSWORDS) {
ret = context->db->hdb_password(context->context, context->db,
&ent, password, cond);
if (ret)
- goto out2;
+ goto out3;
} else {
num_keys = ent.entry.keys.len;
@@ -71,54 +90,76 @@ change(void *server_handle,
ent.entry.keys.len = 0;
ent.entry.keys.val = NULL;
- ret = _kadm5_set_keys(context, &ent.entry, password);
+ ret = _kadm5_set_keys(context, &ent.entry, n_ks_tuple, ks_tuple,
+ password);
if(ret) {
- _kadm5_free_keys (context->context, num_keys, keys);
- goto out2;
+ _kadm5_free_keys(context->context, num_keys, keys);
+ goto out3;
}
+ _kadm5_free_keys(context->context, num_keys, keys);
- if (cond)
- existsp = _kadm5_exists_keys (ent.entry.keys.val,
- ent.entry.keys.len,
- keys, num_keys);
- _kadm5_free_keys (context->context, num_keys, keys);
+ if (cond) {
+ HDB_extension *ext;
+
+ ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys);
+ if (ext != NULL)
+ existsp = _kadm5_exists_keys_hist(ent.entry.keys.val,
+ ent.entry.keys.len,
+ &ext->data.u.hist_keys);
+ }
if (existsp) {
ret = KADM5_PASS_REUSE;
krb5_set_error_message(context->context, ret,
"Password reuse forbidden");
- goto out2;
+ goto out3;
}
+ }
+ ent.entry.kvno++;
- ret = hdb_seal_keys(context->context, context->db, &ent.entry);
+ ent.entry.flags.require_pwchange = 0;
+
+ if (!keepold) {
+ HDB_extension ext;
+
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ ret = hdb_replace_extension(context->context, &ent.entry, &ext);
if (ret)
- goto out2;
+ goto out3;
}
- ent.entry.kvno++;
+
+ ret = hdb_seal_keys(context->context, context->db, &ent.entry);
+ if (ret)
+ goto out3;
ret = _kadm5_set_modifier(context, &ent.entry);
if(ret)
- goto out2;
+ goto out3;
ret = _kadm5_bump_pw_expire(context, &ent.entry);
if (ret)
- goto out2;
-
- ret = context->db->hdb_store(context->context, context->db,
- HDB_F_REPLACE, &ent);
- if (ret)
- goto out2;
+ goto out3;
- kadm5_log_modify (context,
- &ent.entry,
- KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME |
- KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION |
- KADM5_TL_DATA);
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent.entry,
+ KADM5_ATTRIBUTES | KADM5_PRINCIPAL |
+ KADM5_MOD_NAME | KADM5_MOD_TIME |
+ KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
-out2:
+ out3:
hdb_free_entry(context->context, &ent);
-out:
- context->db->hdb_close(context->context, context->db);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
return _kadm5_error_code(ret);
}
@@ -131,9 +172,10 @@ out:
kadm5_ret_t
kadm5_s_chpass_principal_cond(void *server_handle,
krb5_principal princ,
+ int keepold,
const char *password)
{
- return change (server_handle, princ, password, 1);
+ return change (server_handle, princ, keepold, 0, NULL, password, 1);
}
/*
@@ -143,9 +185,13 @@ kadm5_s_chpass_principal_cond(void *server_handle,
kadm5_ret_t
kadm5_s_chpass_principal(void *server_handle,
krb5_principal princ,
+ int keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password)
{
- return change (server_handle, princ, password, 0);
+ return change (server_handle, princ, keepold,
+ n_ks_tuple, ks_tuple, password, 0);
}
/*
@@ -155,6 +201,7 @@ kadm5_s_chpass_principal(void *server_handle,
kadm5_ret_t
kadm5_s_chpass_principal_with_key(void *server_handle,
krb5_principal princ,
+ int keepold,
int n_key_data,
krb5_key_data *key_data)
{
@@ -163,42 +210,67 @@ kadm5_s_chpass_principal_with_key(void *server_handle,
kadm5_ret_t ret;
memset(&ent, 0, sizeof(ent));
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret)
- return ret;
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ, 0,
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, &ent);
- if(ret == HDB_ERR_NOENTRY)
- goto out;
- ret = _kadm5_set_keys2(context, &ent.entry, n_key_data, key_data);
- if(ret)
+ if (ret == HDB_ERR_NOENTRY)
goto out2;
+ if (keepold) {
+ ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
+ if (ret)
+ goto out3;
+ }
+ ret = _kadm5_set_keys2(context, &ent.entry, n_key_data, key_data);
+ if (ret)
+ goto out3;
ent.entry.kvno++;
ret = _kadm5_set_modifier(context, &ent.entry);
- if(ret)
- goto out2;
+ if (ret)
+ goto out3;
ret = _kadm5_bump_pw_expire(context, &ent.entry);
if (ret)
- goto out2;
+ goto out3;
- ret = hdb_seal_keys(context->context, context->db, &ent.entry);
- if (ret)
- goto out2;
+ if (keepold) {
+ ret = hdb_seal_keys(context->context, context->db, &ent.entry);
+ if (ret)
+ goto out3;
+ } else {
+ HDB_extension ext;
- ret = context->db->hdb_store(context->context, context->db,
- HDB_F_REPLACE, &ent);
- if (ret)
- goto out2;
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ ext.data.u.hist_keys.len = 0;
+ ext.data.u.hist_keys.val = NULL;
+ hdb_replace_extension(context->context, &ent.entry, &ext);
+ }
- kadm5_log_modify (context,
- &ent.entry,
- KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME |
- KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION |
- KADM5_TL_DATA);
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent.entry,
+ KADM5_PRINCIPAL | KADM5_MOD_NAME |
+ KADM5_MOD_TIME | KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
-out2:
+ out3:
hdb_free_entry(context->context, &ent);
-out:
- context->db->hdb_close(context->context, context->db);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
return _kadm5_error_code(ret);
}
diff --git a/lib/kadm5/common_glue.c b/lib/kadm5/common_glue.c
index 59579223373f..6dc42fd2adee 100644
--- a/lib/kadm5/common_glue.c
+++ b/lib/kadm5/common_glue.c
@@ -35,14 +35,28 @@
RCSID("$Id$");
-#define __CALL(F, P) (*((kadm5_common_context*)server_handle)->funcs.F)P;
+#define __CALL(F, P) (*((kadm5_common_context*)server_handle)->funcs.F)P
+#define __CALLABLE(F) (((kadm5_common_context*)server_handle)->funcs.F != 0)
kadm5_ret_t
kadm5_chpass_principal(void *server_handle,
krb5_principal princ,
const char *password)
{
- return __CALL(chpass_principal, (server_handle, princ, password));
+ return __CALL(chpass_principal, (server_handle, princ, 0,
+ 0, NULL, password));
+}
+
+kadm5_ret_t
+kadm5_chpass_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ const char *password)
+{
+ return __CALL(chpass_principal, (server_handle, princ, keepold,
+ n_ks_tuple, ks_tuple, password));
}
kadm5_ret_t
@@ -52,7 +66,30 @@ kadm5_chpass_principal_with_key(void *server_handle,
krb5_key_data *key_data)
{
return __CALL(chpass_principal_with_key,
- (server_handle, princ, n_key_data, key_data));
+ (server_handle, princ, 0, n_key_data, key_data));
+}
+
+kadm5_ret_t
+kadm5_chpass_principal_with_key_3(void *server_handle,
+ krb5_principal princ,
+ int keepold,
+ int n_key_data,
+ krb5_key_data *key_data)
+{
+ return __CALL(chpass_principal_with_key,
+ (server_handle, princ, keepold, n_key_data, key_data));
+}
+
+kadm5_ret_t
+kadm5_create_principal_3(void *server_handle,
+ kadm5_principal_ent_t princ,
+ uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ char *password)
+{
+ return __CALL(create_principal,
+ (server_handle, princ, mask, n_ks_tuple, ks_tuple, password));
}
kadm5_ret_t
@@ -61,7 +98,8 @@ kadm5_create_principal(void *server_handle,
uint32_t mask,
const char *password)
{
- return __CALL(create_principal, (server_handle, princ, mask, password));
+ return __CALL(create_principal,
+ (server_handle, princ, mask, 0, NULL, password));
}
kadm5_ret_t
@@ -92,6 +130,49 @@ kadm5_get_principal(void *server_handle,
return __CALL(get_principal, (server_handle, princ, out, mask));
}
+/**
+ * Extract decrypted keys from kadm5_principal_ent_t object. Mostly a
+ * no-op for Heimdal because we fetch the entry with decrypted keys.
+ * Sadly this is not fully a no-op, as we have to allocate a copy.
+ *
+ * @server_handle is the kadm5 handle
+ * @entry is the HDB entry for the principal in question
+ * @ktype is the enctype to get a key for, or -1 to get the first one
+ * @stype is the salttype to get a key for, or -1 to get the first match
+ * @kvno is the kvno to search for, or -1 to get the first match (highest kvno)
+ * @keyblock is where the key will be placed
+ * @keysalt, if not NULL, is where the salt will be placed
+ * @kvnop, if not NULL, is where the selected kvno will be placed
+ */
+kadm5_ret_t
+kadm5_decrypt_key(void *server_handle,
+ kadm5_principal_ent_t entry,
+ int32_t ktype, int32_t stype,
+ int32_t kvno, krb5_keyblock *keyblock,
+ krb5_keysalt *keysalt, int *kvnop)
+{
+ size_t i;
+
+ if (kvno < 1 || stype != -1)
+ return KADM5_DECRYPT_USAGE_NOSUPP;
+
+ for (i = 0; i < entry->n_key_data; i++) {
+ if (ktype != entry->key_data[i].key_data_kvno)
+ continue;
+
+ keyblock->keytype = ktype;
+ keyblock->keyvalue.length = entry->key_data[i].key_data_length[0];
+ keyblock->keyvalue.data = malloc(keyblock->keyvalue.length);
+ if (keyblock->keyvalue.data == NULL)
+ return ENOMEM;
+ memcpy(keyblock->keyvalue.data,
+ entry->key_data[i].key_data_contents[0],
+ keyblock->keyvalue.length);
+ }
+
+ return 0;
+}
+
kadm5_ret_t
kadm5_modify_principal(void *server_handle,
kadm5_principal_ent_t princ,
@@ -106,7 +187,21 @@ kadm5_randkey_principal(void *server_handle,
krb5_keyblock **new_keys,
int *n_keys)
{
- return __CALL(randkey_principal, (server_handle, princ, new_keys, n_keys));
+ return __CALL(randkey_principal, (server_handle, princ, FALSE, 0, NULL,
+ new_keys, n_keys));
+}
+
+kadm5_ret_t
+kadm5_randkey_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock **new_keys,
+ int *n_keys)
+{
+ return __CALL(randkey_principal, (server_handle, princ, keepold,
+ n_ks_tuple, ks_tuple, new_keys, n_keys));
}
kadm5_ret_t
@@ -132,3 +227,202 @@ kadm5_get_privs(void *server_handle,
{
return __CALL(get_privs, (server_handle, privs));
}
+
+
+/**
+ * This function is allows the caller to set new keys for a principal.
+ * This is a trivial wrapper around kadm5_setkey_principal_3().
+ */
+kadm5_ret_t
+kadm5_setkey_principal(void *server_handle,
+ krb5_principal princ,
+ krb5_keyblock *new_keys,
+ int n_keys)
+{
+ return kadm5_setkey_principal_3(server_handle, princ, 0, 0, NULL,
+ new_keys, n_keys);
+}
+
+/**
+ * This function is allows the caller to set new keys for a principal.
+ * This is a simple wrapper around kadm5_get_principal() and
+ * kadm5_modify_principal().
+ */
+kadm5_ret_t
+kadm5_setkey_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple, krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock *keyblocks,
+ int n_keys)
+{
+ kadm5_principal_ent_rec princ_ent;
+ kadm5_ret_t ret;
+ krb5_key_data *new_key_data = NULL;
+ size_t i;
+
+ if (n_keys < 1)
+ return EINVAL;
+ if (n_ks_tuple > 0 && n_ks_tuple != n_keys)
+ return KADM5_SETKEY3_ETYPE_MISMATCH;
+
+ /*
+ * If setkey_principal_3 is defined in the server handle, use that.
+ */
+ if (__CALLABLE(setkey_principal_3))
+ return __CALL(setkey_principal_3,
+ (server_handle, princ, keepold, n_ks_tuple, ks_tuple,
+ keyblocks, n_keys));
+
+ /*
+ * Otherwise, simulate it via a get, update, modify sequence.
+ */
+ ret = kadm5_get_principal(server_handle, princ, &princ_ent,
+ KADM5_KVNO | KADM5_PRINCIPAL | KADM5_KEY_DATA);
+ if (ret)
+ return ret;
+
+ if (keepold) {
+ new_key_data = calloc((n_keys + princ_ent.n_key_data),
+ sizeof(*new_key_data));
+ if (new_key_data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ memcpy(&new_key_data[n_keys], &princ_ent.key_data[0],
+ princ_ent.n_key_data * sizeof (princ_ent.key_data[0]));
+ } else {
+ new_key_data = calloc(n_keys, sizeof(*new_key_data));
+ if (new_key_data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+
+ princ_ent.kvno++;
+ for (i = 0; i < n_keys; i++) {
+ new_key_data[i].key_data_ver = 2;
+
+ /* Key */
+ new_key_data[i].key_data_kvno = princ_ent.kvno;
+ new_key_data[i].key_data_type[0] = keyblocks[i].keytype;
+ new_key_data[i].key_data_length[0] = keyblocks[i].keyvalue.length;
+ new_key_data[i].key_data_contents[0] =
+ malloc(keyblocks[i].keyvalue.length);
+ if (new_key_data[i].key_data_contents[0] == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ memcpy(new_key_data[i].key_data_contents[0],
+ keyblocks[i].keyvalue.data,
+ keyblocks[i].keyvalue.length);
+
+ /*
+ * Salt (but there's no salt, just salttype, which is kinda
+ * silly -- what's the point of setkey_3() then, besides
+ * keepold?!)
+ */
+ new_key_data[i].key_data_type[1] = 0;
+ if (n_ks_tuple > 0) {
+ if (ks_tuple[i].ks_enctype != keyblocks[i].keytype) {
+ ret = KADM5_SETKEY3_ETYPE_MISMATCH;
+ goto out;
+ }
+ new_key_data[i].key_data_type[1] = ks_tuple[i].ks_salttype;
+ }
+ new_key_data[i].key_data_length[1] = 0;
+ new_key_data[i].key_data_contents[1] = NULL;
+ }
+
+ /* Free old keys */
+ if (!keepold) {
+ for (i = 0; i < princ_ent.n_key_data; i++) {
+ free(princ_ent.key_data[i].key_data_contents[0]);
+ free(princ_ent.key_data[i].key_data_contents[1]);
+ }
+ }
+ free(princ_ent.key_data);
+ princ_ent.key_data = new_key_data;
+ princ_ent.n_key_data = n_keys + (keepold ? princ_ent.n_key_data : 0);
+ new_key_data = NULL;
+
+ /* Modify the principal */
+ ret = kadm5_modify_principal(server_handle, &princ_ent, KADM5_KVNO | KADM5_KEY_DATA);
+
+out:
+ if (new_key_data != NULL) {
+ for (i = 0; i < n_keys; i++) {
+ free(new_key_data[i].key_data_contents[0]);
+ free(new_key_data[i].key_data_contents[1]);
+ }
+ free(new_key_data);
+ }
+ kadm5_free_principal_ent(server_handle, &princ_ent);
+ return ret;
+}
+
+
+kadm5_ret_t
+kadm5_lock(void *server_handle)
+{
+ return __CALL(lock, (server_handle));
+}
+
+kadm5_ret_t
+kadm5_unlock(void *server_handle)
+{
+ return __CALL(unlock, (server_handle));
+}
+
+
+kadm5_ret_t
+kadm5_create_policy(void *server_handle,
+ kadm5_policy_ent_t policy, long mask)
+{
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+kadm5_ret_t
+kadm5_delete_policy(void *server_handle, char *name)
+{
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+
+kadm5_ret_t
+kadm5_modify_policy(void *server_handle, kadm5_policy_ent_t policy,
+ uint32_t mask)
+{
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+kadm5_ret_t
+kadm5_get_policy(void *server_handle, char *policy, kadm5_policy_ent_t ent)
+{
+ memset(ent, 0, sizeof (*ent));
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+
+kadm5_ret_t
+kadm5_get_policies(void *server_handle, char *exp, char ***pols, int *count)
+{
+ *count = 0;
+ *pols = NULL;
+
+ return KADM5_POLICY_OP_NOSUPP;
+}
+
+kadm5_ret_t
+kadm5_free_policy_ent(kadm5_policy_ent_t ent)
+{
+ if (ent->policy)
+ free(ent->policy);
+ /*
+ * Not clear if we should free ent or not. It might be an automatic
+ * struct, so we don't free it for now, just in case.
+ */
+ return 0;
+}
+
diff --git a/lib/kadm5/context_s.c b/lib/kadm5/context_s.c
index e121a48994b8..4aeee5df0176 100644
--- a/lib/kadm5/context_s.c
+++ b/lib/kadm5/context_s.c
@@ -35,6 +35,63 @@
RCSID("$Id$");
+static kadm5_ret_t
+kadm5_s_lock(void *server_handle)
+{
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret;
+
+ if (context->keep_open) {
+ /*
+ * We open/close around every operation, but we retain the DB
+ * open if the DB was locked with a prior call to kadm5_lock(),
+ * so if it's open here that must be because the DB is locked.
+ */
+ heim_assert(context->db->lock_count > 0,
+ "Internal error in tracking HDB locks");
+ return KADM5_ALREADY_LOCKED;
+ }
+
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret)
+ return ret;
+
+ ret = context->db->hdb_lock(context->context, context->db, HDB_WLOCK);
+ if (ret) {
+ (void) context->db->hdb_close(context->context, context->db);
+ return ret;
+ }
+
+ /*
+ * Attempt to recover the log. This will generally fail on slaves,
+ * and we can't tell if we're on a slave here.
+ *
+ * Perhaps we could set a flag in the kadm5_server_context to
+ * indicate whether a read has been done without recovering the log,
+ * in which case we could fail any subsequent writes.
+ */
+ if (kadm5_log_init(context) == 0)
+ (void) kadm5_log_end(context);
+
+ context->keep_open = 1;
+ return 0;
+}
+
+static kadm5_ret_t
+kadm5_s_unlock(void *server_handle)
+{
+ kadm5_server_context *context = server_handle;
+ kadm5_ret_t ret;
+
+ if (!context->keep_open)
+ return KADM5_NOT_LOCKED;
+
+ context->keep_open = 0;
+ ret = context->db->hdb_unlock(context->context, context->db);
+ (void) context->db->hdb_close(context->context, context->db);
+ return ret;
+}
+
static void
set_funcs(kadm5_server_context *c)
{
@@ -51,6 +108,9 @@ set_funcs(kadm5_server_context *c)
SET(c, modify_principal);
SET(c, randkey_principal);
SET(c, rename_principal);
+ SET(c, lock);
+ SET(c, unlock);
+ SET(c, setkey_principal_3);
}
#ifndef NO_UNIX_SOCKETS
@@ -81,6 +141,7 @@ find_db_spec(kadm5_server_context *ctx)
krb5_context context = ctx->context;
struct hdb_dbinfo *info, *d;
krb5_error_code ret;
+ int aret;
if (ctx->config.realm) {
/* fetch the databases */
@@ -97,20 +158,32 @@ find_db_spec(kadm5_server_context *ctx)
continue;
p = hdb_dbinfo_get_dbname(context, d);
- if (p)
+ if (p) {
ctx->config.dbname = strdup(p);
+ if (ctx->config.dbname == NULL)
+ return ENOMEM;
+ }
p = hdb_dbinfo_get_acl_file(context, d);
- if (p)
+ if (p) {
ctx->config.acl_file = strdup(p);
+ if (ctx->config.acl_file == NULL)
+ return ENOMEM;
+ }
p = hdb_dbinfo_get_mkey_file(context, d);
- if (p)
+ if (p) {
ctx->config.stash_file = strdup(p);
+ if (ctx->config.stash_file == NULL)
+ return ENOMEM;
+ }
p = hdb_dbinfo_get_log_file(context, d);
- if (p)
+ if (p) {
ctx->log_context.log_file = strdup(p);
+ if (ctx->log_context.log_file == NULL)
+ return ENOMEM;
+ }
break;
}
hdb_free_dbinfo(context, &info);
@@ -118,14 +191,29 @@ find_db_spec(kadm5_server_context *ctx)
/* If any of the values was unset, pick up the default value */
- if (ctx->config.dbname == NULL)
+ if (ctx->config.dbname == NULL) {
ctx->config.dbname = strdup(hdb_default_db(context));
- if (ctx->config.acl_file == NULL)
- asprintf(&ctx->config.acl_file, "%s/kadmind.acl", hdb_db_dir(context));
- if (ctx->config.stash_file == NULL)
- asprintf(&ctx->config.stash_file, "%s/m-key", hdb_db_dir(context));
- if (ctx->log_context.log_file == NULL)
- asprintf(&ctx->log_context.log_file, "%s/log", hdb_db_dir(context));
+ if (ctx->config.dbname == NULL)
+ return ENOMEM;
+ }
+ if (ctx->config.acl_file == NULL) {
+ aret = asprintf(&ctx->config.acl_file, "%s/kadmind.acl",
+ hdb_db_dir(context));
+ if (aret == -1)
+ return ENOMEM;
+ }
+ if (ctx->config.stash_file == NULL) {
+ aret = asprintf(&ctx->config.stash_file, "%s/m-key",
+ hdb_db_dir(context));
+ if (aret == -1)
+ return ENOMEM;
+ }
+ if (ctx->log_context.log_file == NULL) {
+ aret = asprintf(&ctx->log_context.log_file, "%s/log",
+ hdb_db_dir(context));
+ if (aret == -1)
+ return ENOMEM;
+ }
#ifndef NO_UNIX_SOCKETS
set_socket_name(context, &ctx->log_context.socket_name);
@@ -141,24 +229,42 @@ _kadm5_s_init_context(kadm5_server_context **ctx,
kadm5_config_params *params,
krb5_context context)
{
- *ctx = malloc(sizeof(**ctx));
- if(*ctx == NULL)
+ kadm5_ret_t ret = 0;
+
+ *ctx = calloc(1, sizeof(**ctx));
+ if (*ctx == NULL)
return ENOMEM;
- memset(*ctx, 0, sizeof(**ctx));
+ (*ctx)->log_context.socket_fd = rk_INVALID_SOCKET;
+
set_funcs(*ctx);
(*ctx)->context = context;
krb5_add_et_list (context, initialize_kadm5_error_table_r);
+
#define is_set(M) (params && params->mask & KADM5_CONFIG_ ## M)
- if(is_set(REALM))
+ if (is_set(REALM)) {
(*ctx)->config.realm = strdup(params->realm);
- else
- krb5_get_default_realm(context, &(*ctx)->config.realm);
- if(is_set(DBNAME))
+ if ((*ctx)->config.realm == NULL)
+ return ENOMEM;
+ } else {
+ ret = krb5_get_default_realm(context, &(*ctx)->config.realm);
+ if (ret)
+ return ret;
+ }
+ if (is_set(DBNAME)) {
(*ctx)->config.dbname = strdup(params->dbname);
- if(is_set(ACL_FILE))
+ if ((*ctx)->config.dbname == NULL)
+ return ENOMEM;
+ }
+ if (is_set(ACL_FILE)) {
(*ctx)->config.acl_file = strdup(params->acl_file);
- if(is_set(STASH_FILE))
+ if ((*ctx)->config.acl_file == NULL)
+ return ENOMEM;
+ }
+ if (is_set(STASH_FILE)) {
(*ctx)->config.stash_file = strdup(params->stash_file);
+ if ((*ctx)->config.stash_file == NULL)
+ return ENOMEM;
+ }
find_db_spec(*ctx);
diff --git a/lib/kadm5/create_c.c b/lib/kadm5/create_c.c
index e36b2969cf17..f6706b027ec6 100644
--- a/lib/kadm5/create_c.c
+++ b/lib/kadm5/create_c.c
@@ -39,6 +39,8 @@ kadm5_ret_t
kadm5_c_create_principal(void *server_handle,
kadm5_principal_ent_t princ,
uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password)
{
kadm5_client_context *context = server_handle;
@@ -48,6 +50,14 @@ kadm5_c_create_principal(void *server_handle,
int32_t tmp;
krb5_data reply;
+ /*
+ * We should get around to implementing this... At the moment, the
+ * the server side API is implemented but the wire protocol has not
+ * been updated.
+ */
+ if (n_ks_tuple > 0)
+ return KADM5_KS_TUPLE_NOSUPP;
+
ret = _kadm5_connect(server_handle);
if(ret)
return ret;
diff --git a/lib/kadm5/create_s.c b/lib/kadm5/create_s.c
index 04312c024ed3..7d8f89837d5d 100644
--- a/lib/kadm5/create_s.c
+++ b/lib/kadm5/create_s.c
@@ -65,6 +65,7 @@ create_principal(kadm5_server_context *context,
kadm5_principal_ent_rec defrec, *defent;
uint32_t def_mask;
+ memset(ent, 0, sizeof(*ent));
if((mask & required_mask) != required_mask)
return KADM5_BAD_MASK;
if((mask & forbidden_mask))
@@ -72,7 +73,6 @@ create_principal(kadm5_server_context *context,
if((mask & KADM5_POLICY) && strcmp(princ->policy, "default"))
/* XXX no real policies for now */
return KADM5_UNK_POLICY;
- memset(ent, 0, sizeof(*ent));
ret = krb5_copy_principal(context->context, princ->principal,
&ent->entry.principal);
if(ret)
@@ -111,6 +111,12 @@ kadm5_s_create_principal_with_key(void *server_handle,
hdb_entry_ex ent;
kadm5_server_context *context = server_handle;
+ if ((mask & KADM5_KVNO) == 0) {
+ /* create_principal() through _kadm5_setup_entry(), will need this */
+ princ->kvno = 1;
+ mask |= KADM5_KVNO;
+ }
+
ret = create_principal(context, princ, mask, &ent,
KADM5_PRINCIPAL | KADM5_KEY_DATA,
KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
@@ -118,26 +124,37 @@ kadm5_s_create_principal_with_key(void *server_handle,
| KADM5_AUX_ATTRIBUTES
| KADM5_POLICY_CLR | KADM5_LAST_SUCCESS
| KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT);
- if(ret)
- goto out;
+ if (ret)
+ return ret;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret) {
+ hdb_free_entry(context->context, &ent);
+ return ret;
+ }
+ }
- if ((mask & KADM5_KVNO) == 0)
- ent.entry.kvno = 1;
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
- goto out;
+ goto out2;
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret)
- goto out;
- ret = context->db->hdb_store(context->context, context->db, 0, &ent);
- context->db->hdb_close(context->context, context->db);
- if (ret)
- goto out;
- kadm5_log_create (context, &ent.entry);
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_create(context, &ent.entry);
-out:
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
hdb_free_entry(context->context, &ent);
return _kadm5_error_code(ret);
}
@@ -147,12 +164,20 @@ kadm5_ret_t
kadm5_s_create_principal(void *server_handle,
kadm5_principal_ent_t princ,
uint32_t mask,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password)
{
kadm5_ret_t ret;
hdb_entry_ex ent;
kadm5_server_context *context = server_handle;
+ if ((mask & KADM5_KVNO) == 0) {
+ /* create_principal() through _kadm5_setup_entry(), will need this */
+ princ->kvno = 1;
+ mask |= KADM5_KVNO;
+ }
+
ret = create_principal(context, princ, mask, &ent,
KADM5_PRINCIPAL,
KADM5_LAST_PWD_CHANGE | KADM5_MOD_TIME
@@ -160,34 +185,44 @@ kadm5_s_create_principal(void *server_handle,
| KADM5_AUX_ATTRIBUTES | KADM5_KEY_DATA
| KADM5_POLICY_CLR | KADM5_LAST_SUCCESS
| KADM5_LAST_FAILED | KADM5_FAIL_AUTH_COUNT);
- if(ret)
- goto out;
+ if (ret)
+ return ret;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret) {
+ hdb_free_entry(context->context, &ent);
+ return ret;
+ }
+ }
- if ((mask & KADM5_KVNO) == 0)
- ent.entry.kvno = 1;
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
ent.entry.keys.len = 0;
ent.entry.keys.val = NULL;
- ret = _kadm5_set_keys(context, &ent.entry, password);
+ ret = _kadm5_set_keys(context, &ent.entry, n_ks_tuple, ks_tuple, password);
if (ret)
- goto out;
+ goto out2;
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
- goto out;
+ goto out2;
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret)
- goto out;
- ret = context->db->hdb_store(context->context, context->db, 0, &ent);
- context->db->hdb_close(context->context, context->db);
- if (ret)
- goto out;
-
- kadm5_log_create (context, &ent.entry);
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_create(context, &ent.entry);
+ out2:
+ (void) kadm5_log_end(context);
out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
hdb_free_entry(context->context, &ent);
return _kadm5_error_code(ret);
}
diff --git a/lib/kadm5/default_keys.c b/lib/kadm5/default_keys.c
index 6719e38461db..c3494e5b84bc 100644
--- a/lib/kadm5/default_keys.c
+++ b/lib/kadm5/default_keys.c
@@ -85,7 +85,8 @@ parse_file(krb5_context context, krb5_principal principal, int no_salt)
size_t nkeys;
Key *keys;
- ret = hdb_generate_key_set(context, principal, &keys, &nkeys, no_salt);
+ ret = hdb_generate_key_set(context, principal, NULL, 0, &keys, &nkeys,
+ no_salt);
if (ret)
krb5_err(context, 1, ret, "hdb_generate_key_set");
diff --git a/lib/kadm5/delete_s.c b/lib/kadm5/delete_s.c
index 7f8f537b06c0..2e4aeff537fa 100644
--- a/lib/kadm5/delete_s.c
+++ b/lib/kadm5/delete_s.c
@@ -43,33 +43,44 @@ kadm5_s_delete_principal(void *server_handle, krb5_principal princ)
hdb_entry_ex ent;
memset(&ent, 0, sizeof(ent));
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret) {
- krb5_warn(context->context, ret, "opening database");
- return ret;
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret) {
+ krb5_warn(context->context, ret, "opening database");
+ return ret;
+ }
}
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
- if(ret == HDB_ERR_NOENTRY)
- goto out;
- if(ent.entry.flags.immutable) {
- ret = KADM5_PROTECT_PRINCIPAL;
+ if (ret == HDB_ERR_NOENTRY)
goto out2;
+ if (ent.entry.flags.immutable) {
+ ret = KADM5_PROTECT_PRINCIPAL;
+ goto out3;
}
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
- goto out2;
+ goto out3;
- ret = context->db->hdb_remove(context->context, context->db, princ);
- if (ret)
- goto out2;
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_delete(context, princ);
- kadm5_log_delete (context, princ);
-
-out2:
+ out3:
hdb_free_entry(context->context, &ent);
-out:
- context->db->hdb_close(context->context, context->db);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
return _kadm5_error_code(ret);
}
diff --git a/lib/kadm5/destroy_c.c b/lib/kadm5/destroy_c.c
index 06a08025694e..9a4ab83bec52 100644
--- a/lib/kadm5/destroy_c.c
+++ b/lib/kadm5/destroy_c.c
@@ -42,7 +42,7 @@ kadm5_c_destroy(void *server_handle)
free(context->realm);
free(context->admin_server);
- close(context->sock);
+ rk_closesocket(context->sock);
if (context->client_name)
free(context->client_name);
if (context->service_name)
@@ -51,5 +51,6 @@ kadm5_c_destroy(void *server_handle)
krb5_auth_con_free(context->context, context->ac);
if(context->my_context)
krb5_free_context(context->context);
+ free(context);
return 0;
}
diff --git a/lib/kadm5/destroy_s.c b/lib/kadm5/destroy_s.c
index 25fc48ba214e..242436637948 100644
--- a/lib/kadm5/destroy_s.c
+++ b/lib/kadm5/destroy_s.c
@@ -55,8 +55,9 @@ destroy_config (kadm5_config_params *c)
static void
destroy_kadm5_log_context (kadm5_log_context *c)
{
- free (c->log_file);
- rk_closesocket (c->socket_fd);
+ free(c->log_file);
+ if (c->socket_fd != rk_INVALID_SOCKET)
+ rk_closesocket(c->socket_fd);
#ifdef NO_UNIX_SOCKETS
if (c->socket_info) {
freeaddrinfo(c->socket_info);
@@ -72,16 +73,18 @@ destroy_kadm5_log_context (kadm5_log_context *c)
kadm5_ret_t
kadm5_s_destroy(void *server_handle)
{
- kadm5_ret_t ret;
+ kadm5_ret_t ret = 0;
kadm5_server_context *context = server_handle;
krb5_context kcontext = context->context;
- ret = context->db->hdb_destroy(kcontext, context->db);
- destroy_kadm5_log_context (&context->log_context);
- destroy_config (&context->config);
- krb5_free_principal (kcontext, context->caller);
- if(context->my_context)
+ if (context->db != NULL)
+ ret = context->db->hdb_destroy(kcontext, context->db);
+ destroy_kadm5_log_context(&context->log_context);
+ destroy_config(&context->config);
+ krb5_free_principal(kcontext, context->caller);
+ if (context->my_context)
krb5_free_context(kcontext);
- free (context);
+ free(context);
+
return ret;
}
diff --git a/lib/kadm5/ent_setup.c b/lib/kadm5/ent_setup.c
index f2d1f2f285bd..1266f204b7eb 100644
--- a/lib/kadm5/ent_setup.c
+++ b/lib/kadm5/ent_setup.c
@@ -51,6 +51,7 @@ attr_to_flags(unsigned attr, HDBFlags *flags)
/* DUP_SKEY */
flags->invalid = !!(attr & KRB5_KDB_DISALLOW_ALL_TIX);
flags->require_preauth = !!(attr & KRB5_KDB_REQUIRES_PRE_AUTH);
+ flags->require_pwchange = !!(attr & KRB5_KDB_REQUIRES_PWCHANGE);
/* HW_AUTH */
flags->server = !(attr & KRB5_KDB_DISALLOW_SVR);
flags->change_pw = !!(attr & KRB5_KDB_PWCHANGE_SERVICE);
@@ -178,8 +179,14 @@ _kadm5_setup_entry(kadm5_server_context *context,
}
}
if(mask & KADM5_KVNO
- && princ_mask & KADM5_KVNO)
- ent->entry.kvno = princ->kvno;
+ && (princ_mask & KADM5_KVNO)) {
+ krb5_error_code ret;
+
+ ret = hdb_change_kvno(context->context, princ->kvno, &ent->entry);
+ if (ret && ret != HDB_ERR_KVNO_NOT_FOUND)
+ return ret;
+ ent->entry.kvno = princ->kvno; /* force it */
+ }
if(mask & KADM5_MAX_RLIFE) {
if(princ_mask & KADM5_MAX_RLIFE) {
if(princ->max_renewable_life)
diff --git a/lib/kadm5/free.c b/lib/kadm5/free.c
index 670bc2c2aba5..e8bb0f94314d 100644
--- a/lib/kadm5/free.c
+++ b/lib/kadm5/free.c
@@ -60,12 +60,13 @@ kadm5_free_principal_ent(void *server_handle,
kadm5_principal_ent_t princ)
{
kadm5_server_context *context = server_handle;
- if(princ->principal)
+
+ if (princ->principal)
krb5_free_principal(context->context, princ->principal);
- if(princ->mod_name)
+ if (princ->mod_name)
krb5_free_principal(context->context, princ->mod_name);
kadm5_free_key_data(server_handle, &princ->n_key_data, princ->key_data);
- while(princ->n_tl_data && princ->tl_data) {
+ while (princ->n_tl_data && princ->tl_data) {
krb5_tl_data *tp;
tp = princ->tl_data;
princ->tl_data = tp->tl_data_next;
@@ -74,8 +75,8 @@ kadm5_free_principal_ent(void *server_handle,
free(tp->tl_data_contents);
free(tp);
}
- if (princ->key_data != NULL)
- free (princ->key_data);
+ free(princ->key_data);
+ free(princ->policy);
}
void
diff --git a/lib/kadm5/get_princs_s.c b/lib/kadm5/get_princs_s.c
index 55c8f2e98b60..22b2091be7bb 100644
--- a/lib/kadm5/get_princs_s.c
+++ b/lib/kadm5/get_princs_s.c
@@ -85,29 +85,41 @@ kadm5_s_get_principals(void *server_handle,
struct foreach_data d;
kadm5_server_context *context = server_handle;
kadm5_ret_t ret;
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret) {
- krb5_warn(context->context, ret, "opening database");
- return ret;
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
+ if (ret) {
+ krb5_warn(context->context, ret, "opening database");
+ return ret;
+ }
}
d.exp = expression;
{
krb5_realm r;
+ int aret;
+
krb5_get_default_realm(context->context, &r);
- asprintf(&d.exp2, "%s@%s", expression, r);
+ aret = asprintf(&d.exp2, "%s@%s", expression, r);
free(r);
+ if (aret == -1 || d.exp2 == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
}
d.princs = NULL;
d.count = 0;
ret = hdb_foreach(context->context, context->db, HDB_F_ADMIN_DATA, foreach, &d);
- context->db->hdb_close(context->context, context->db);
- if(ret == 0)
+
+ if (ret == 0)
ret = add_princ(&d, NULL);
- if(ret == 0){
+ if (ret == 0){
*princs = d.princs;
*count = d.count - 1;
- }else
+ } else
kadm5_free_name_list(context, d.princs, &d.count);
free(d.exp2);
+ out:
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
return _kadm5_error_code(ret);
}
diff --git a/lib/kadm5/get_s.c b/lib/kadm5/get_s.c
index e03585e222a8..8f9e7e9089f1 100644
--- a/lib/kadm5/get_s.c
+++ b/lib/kadm5/get_s.c
@@ -31,7 +31,9 @@
* SUCH DAMAGE.
*/
+#include <krb5_locl.h>
#include "kadm5_locl.h"
+#include <assert.h>
RCSID("$Id$");
@@ -61,8 +63,56 @@ add_tl_data(kadm5_principal_ent_t ent, int16_t type,
return 0;
}
-KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
-_krb5_put_int(void *buffer, unsigned long value, size_t size); /* XXX */
+static
+krb5_error_code
+copy_keyset_to_kadm5(kadm5_server_context *context, krb5_kvno kvno,
+ size_t n_keys, Key *keys, krb5_salt *salt,
+ kadm5_principal_ent_t out)
+{
+ size_t i;
+ Key *key;
+ krb5_key_data *kd;
+ krb5_data *sp;
+ krb5_error_code ret = 0;
+
+ for (i = 0; i < n_keys; i++) {
+ key = &keys[i];
+ kd = &out->key_data[out->n_key_data];
+ kd->key_data_ver = 2;
+ kd->key_data_kvno = kvno;
+ kd->key_data_type[0] = key->key.keytype;
+ if(key->salt)
+ kd->key_data_type[1] = key->salt->type;
+ else
+ kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
+ /* setup key */
+ kd->key_data_length[0] = key->key.keyvalue.length;
+ kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
+ if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
+ ret = ENOMEM;
+ break;
+ }
+ memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
+ kd->key_data_length[0]);
+ /* setup salt */
+ if(key->salt)
+ sp = &key->salt->salt;
+ else
+ sp = &salt->saltvalue;
+ kd->key_data_length[1] = sp->length;
+ kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
+ if(kd->key_data_length[1] != 0
+ && kd->key_data_contents[1] == NULL) {
+ memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
+ ret = ENOMEM;
+ break;
+ }
+ memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
+ out->n_key_data++;
+ }
+
+ return ret;
+}
kadm5_ret_t
kadm5_s_get_principal(void *server_handle,
@@ -73,18 +123,41 @@ kadm5_s_get_principal(void *server_handle,
kadm5_server_context *context = server_handle;
kadm5_ret_t ret;
hdb_entry_ex ent;
+ int hdb_is_rw = 1;
memset(&ent, 0, sizeof(ent));
- ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
- if(ret)
- return ret;
+ memset(out, 0, sizeof(*out));
+
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret == EPERM || ret == EACCES) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDONLY, 0);
+ hdb_is_rw = 0;
+ }
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Attempt to recover the log. This will generally fail on slaves,
+ * and we can't tell if we're on a slave here.
+ *
+ * Perhaps we could set a flag in the kadm5_server_context to
+ * indicate whether a read has been done without recovering the log,
+ * in which case we could fail any subsequent writes.
+ */
+ if (hdb_is_rw && kadm5_log_init_nb(context) == 0)
+ (void) kadm5_log_end(context);
+
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
- HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
- context->db->hdb_close(context->context, context->db);
+ HDB_F_DECRYPT|HDB_F_ALL_KVNOS|
+ HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
+
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
if(ret)
return _kadm5_error_code(ret);
- memset(out, 0, sizeof(*out));
if(mask & KADM5_PRINCIPAL)
ret = krb5_copy_principal(context->context, ent.entry.principal,
&out->principal);
@@ -104,6 +177,7 @@ kadm5_s_get_principal(void *server_handle,
out->attributes |= ent.entry.flags.proxiable ? 0 : KRB5_KDB_DISALLOW_PROXIABLE;
out->attributes |= ent.entry.flags.invalid ? KRB5_KDB_DISALLOW_ALL_TIX : 0;
out->attributes |= ent.entry.flags.require_preauth ? KRB5_KDB_REQUIRES_PRE_AUTH : 0;
+ out->attributes |= ent.entry.flags.require_pwchange ? KRB5_KDB_REQUIRES_PWCHANGE : 0;
out->attributes |= ent.entry.flags.server ? 0 : KRB5_KDB_DISALLOW_SVR;
out->attributes |= ent.entry.flags.change_pw ? KRB5_KDB_PWCHANGE_SERVICE : 0;
out->attributes |= ent.entry.flags.ok_as_delegate ? KRB5_KDB_OK_AS_DELEGATE : 0;
@@ -160,8 +234,21 @@ kadm5_s_get_principal(void *server_handle,
if(mask & KADM5_FAIL_AUTH_COUNT)
;
#endif
- if(mask & KADM5_POLICY)
- out->policy = NULL;
+ if(mask & KADM5_POLICY) {
+ HDB_extension *ext;
+
+ ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_policy);
+ if (ext == NULL) {
+ out->policy = strdup("default");
+ /* It's OK if we retun NULL instead of "default" */
+ } else {
+ out->policy = strdup(ext->data.u.policy);
+ if (out->policy == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ }
+ }
if(mask & KADM5_MAX_RLIFE) {
if(ent.entry.max_renew)
out->max_renewable_life = *ent.entry.max_renew;
@@ -170,57 +257,45 @@ kadm5_s_get_principal(void *server_handle,
}
if(mask & KADM5_KEY_DATA){
size_t i;
- Key *key;
- krb5_key_data *kd;
+ size_t n_keys = ent.entry.keys.len;
krb5_salt salt;
- krb5_data *sp;
+ HDB_extension *ext;
+ HDB_Ext_KeySet *hist_keys = NULL;
+
+ /* Don't return stale keys to kadm5 clients */
+ ret = hdb_prune_keys(context->context, &ent.entry);
+ if (ret)
+ goto out;
+ ext = hdb_find_extension(&ent.entry, choice_HDB_extension_data_hist_keys);
+ if (ext != NULL)
+ hist_keys = &ext->data.u.hist_keys;
+
krb5_get_pw_salt(context->context, ent.entry.principal, &salt);
- out->key_data = malloc(ent.entry.keys.len * sizeof(*out->key_data));
- if (out->key_data == NULL && ent.entry.keys.len != 0) {
+ for (i = 0; hist_keys != NULL && i < hist_keys->len; i++)
+ n_keys += hist_keys->val[i].keys.len;
+ out->key_data = malloc(n_keys * sizeof(*out->key_data));
+ if (out->key_data == NULL && n_keys != 0) {
ret = ENOMEM;
goto out;
}
- for(i = 0; i < ent.entry.keys.len; i++){
- key = &ent.entry.keys.val[i];
- kd = &out->key_data[i];
- kd->key_data_ver = 2;
- kd->key_data_kvno = ent.entry.kvno;
- kd->key_data_type[0] = key->key.keytype;
- if(key->salt)
- kd->key_data_type[1] = key->salt->type;
- else
- kd->key_data_type[1] = KRB5_PADATA_PW_SALT;
- /* setup key */
- kd->key_data_length[0] = key->key.keyvalue.length;
- kd->key_data_contents[0] = malloc(kd->key_data_length[0]);
- if(kd->key_data_contents[0] == NULL && kd->key_data_length[0] != 0){
- ret = ENOMEM;
- break;
- }
- memcpy(kd->key_data_contents[0], key->key.keyvalue.data,
- kd->key_data_length[0]);
- /* setup salt */
- if(key->salt)
- sp = &key->salt->salt;
- else
- sp = &salt.saltvalue;
- kd->key_data_length[1] = sp->length;
- kd->key_data_contents[1] = malloc(kd->key_data_length[1]);
- if(kd->key_data_length[1] != 0
- && kd->key_data_contents[1] == NULL) {
- memset(kd->key_data_contents[0], 0, kd->key_data_length[0]);
- ret = ENOMEM;
- break;
- }
- memcpy(kd->key_data_contents[1], sp->data, kd->key_data_length[1]);
- out->n_key_data = i + 1;
+ out->n_key_data = 0;
+ ret = copy_keyset_to_kadm5(context, ent.entry.kvno, ent.entry.keys.len,
+ ent.entry.keys.val, &salt, out);
+ if (ret)
+ goto out;
+ for (i = 0; hist_keys != NULL && i < hist_keys->len; i++) {
+ ret = copy_keyset_to_kadm5(context, hist_keys->val[i].kvno,
+ hist_keys->val[i].keys.len,
+ hist_keys->val[i].keys.val,
+ &salt, out);
+ if (ret)
+ goto out;
}
krb5_free_salt(context->context, salt);
+ assert( out->n_key_data == n_keys );
}
- if(ret){
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
if(mask & KADM5_TL_DATA) {
time_t last_pw_expire;
const HDB_Ext_PKINIT_acl *acl;
@@ -232,15 +307,13 @@ kadm5_s_get_principal(void *server_handle,
_krb5_put_int(buf, last_pw_expire, sizeof(buf));
ret = add_tl_data(out, KRB5_TL_LAST_PWD_CHANGE, buf, sizeof(buf));
}
- if(ret){
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
/*
* If the client was allowed to get key data, let it have the
* password too.
*/
- if(mask & KADM5_KEY_DATA) {
+ if (mask & KADM5_KEY_DATA) {
heim_utf8_string pw;
ret = hdb_entry_get_password(context->context,
@@ -259,24 +332,18 @@ kadm5_s_get_principal(void *server_handle,
ASN1_MALLOC_ENCODE(HDB_Ext_PKINIT_acl, buf.data, buf.length,
acl, &len, ret);
- if (ret) {
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
if (len != buf.length)
krb5_abortx(context->context,
"internal ASN.1 encoder error");
ret = add_tl_data(out, KRB5_TL_PKINIT_ACL, buf.data, buf.length);
free(buf.data);
- if (ret) {
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
}
- if(ret){
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
ret = hdb_entry_get_aliases(&ent.entry, &aliases);
if (ret == 0 && aliases) {
@@ -285,27 +352,23 @@ kadm5_s_get_principal(void *server_handle,
ASN1_MALLOC_ENCODE(HDB_Ext_Aliases, buf.data, buf.length,
aliases, &len, ret);
- if (ret) {
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
if (len != buf.length)
krb5_abortx(context->context,
"internal ASN.1 encoder error");
ret = add_tl_data(out, KRB5_TL_ALIASES, buf.data, buf.length);
free(buf.data);
- if (ret) {
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
}
- if(ret){
- kadm5_free_principal_ent(context, out);
+ if (ret)
goto out;
- }
-
}
-out:
+
+ out:
+ if (ret)
+ kadm5_free_principal_ent(context, out);
hdb_free_entry(context->context, &ent);
return _kadm5_error_code(ret);
diff --git a/lib/kadm5/init_c.c b/lib/kadm5/init_c.c
index 1623ed1a995d..6eddfb8711a0 100644
--- a/lib/kadm5/init_c.c
+++ b/lib/kadm5/init_c.c
@@ -45,10 +45,23 @@
RCSID("$Id$");
+static kadm5_ret_t
+kadm5_c_lock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
+static kadm5_ret_t
+kadm5_c_unlock(void *server_handle)
+{
+ return ENOTSUP;
+}
+
static void
set_funcs(kadm5_client_context *c)
{
#define SET(C, F) (C)->funcs.F = kadm5 ## _c_ ## F
+#define SETNOTIMP(C, F) (C)->funcs.F = 0
SET(c, chpass_principal);
SET(c, chpass_principal_with_key);
SET(c, create_principal);
@@ -61,6 +74,9 @@ set_funcs(kadm5_client_context *c)
SET(c, modify_principal);
SET(c, randkey_principal);
SET(c, rename_principal);
+ SET(c, lock);
+ SET(c, unlock);
+ SETNOTIMP(c, setkey_principal_3);
}
kadm5_ret_t
@@ -465,11 +481,12 @@ kadm_connect(kadm5_client_context *ctx)
}
if (ctx->realm)
- asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE, ctx->realm);
+ error = asprintf(&service_name, "%s@%s", KADM5_ADMIN_SERVICE,
+ ctx->realm);
else
- asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
+ error = asprintf(&service_name, "%s", KADM5_ADMIN_SERVICE);
- if (service_name == NULL) {
+ if (error == -1 || service_name == NULL) {
freeaddrinfo (ai);
rk_closesocket(s);
krb5_clear_error_message(context);
@@ -571,16 +588,18 @@ kadm5_c_init_with_context(krb5_context context,
krb5_ccache cc;
ret = _kadm5_c_init_context(&ctx, realm_params, context);
- if(ret)
+ if (ret)
return ret;
- if(password != NULL && *password != '\0') {
+ if (password != NULL && *password != '\0') {
ret = _kadm5_c_get_cred_cache(context,
client_name,
service_name,
password, prompter, keytab, ccache, &cc);
- if(ret)
- return ret; /* XXX */
+ if (ret) {
+ kadm5_c_destroy(ctx);
+ return ret;
+ }
ccache = cc;
}
diff --git a/lib/kadm5/init_s.c b/lib/kadm5/init_s.c
index 1001fce6ddc6..ada584825ba9 100644
--- a/lib/kadm5/init_s.c
+++ b/lib/kadm5/init_s.c
@@ -32,8 +32,7 @@
*/
#include "kadm5_locl.h"
-
-RCSID("$Id$");
+#include <fcntl.h>
static kadm5_ret_t
@@ -47,12 +46,26 @@ kadm5_s_init_with_context(krb5_context context,
{
kadm5_ret_t ret;
kadm5_server_context *ctx;
+ char *dbname;
+ char *stash_file;
+
+ *server_handle = NULL;
ret = _kadm5_s_init_context(&ctx, realm_params, context);
- if(ret)
+ if (ret)
return ret;
- assert(ctx->config.dbname != NULL);
- assert(ctx->config.stash_file != NULL);
+ if (realm_params->mask & KADM5_CONFIG_DBNAME)
+ dbname = realm_params->dbname;
+ else
+ dbname = ctx->config.dbname;
+
+ if (realm_params->mask & KADM5_CONFIG_STASH_FILE)
+ stash_file = realm_params->stash_file;
+ else
+ stash_file = ctx->config.stash_file;
+
+ assert(dbname != NULL);
+ assert(stash_file != NULL);
assert(ctx->config.acl_file != NULL);
assert(ctx->log_context.log_file != NULL);
#ifndef NO_UNIX_SOCKETS
@@ -61,33 +74,35 @@ kadm5_s_init_with_context(krb5_context context,
assert(ctx->log_context.socket_info != NULL);
#endif
- ret = hdb_create(ctx->context, &ctx->db, ctx->config.dbname);
- if(ret)
- return ret;
- ret = hdb_set_master_keyfile (ctx->context,
- ctx->db, ctx->config.stash_file);
- if(ret)
+ ret = hdb_create(ctx->context, &ctx->db, dbname);
+ if (ret == 0)
+ ret = hdb_set_master_keyfile(ctx->context,
+ ctx->db, stash_file);
+ if (ret) {
+ kadm5_s_destroy(ctx);
return ret;
+ }
- ctx->log_context.log_fd = -1;
+ ctx->log_context.log_fd = -1;
#ifndef NO_UNIX_SOCKETS
- ctx->log_context.socket_fd = socket (AF_UNIX, SOCK_DGRAM, 0);
+ ctx->log_context.socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0);
#else
- ctx->log_context.socket_fd = socket (ctx->log_context.socket_info->ai_family,
- ctx->log_context.socket_info->ai_socktype,
- ctx->log_context.socket_info->ai_protocol);
+ ctx->log_context.socket_fd = socket(ctx->log_context.socket_info->ai_family,
+ ctx->log_context.socket_info->ai_socktype,
+ ctx->log_context.socket_info->ai_protocol);
#endif
- ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
- if(ret)
- return ret;
-
- ret = _kadm5_acl_init(ctx);
- if(ret)
- return ret;
+ if (ctx->log_context.socket_fd != rk_INVALID_SOCKET)
+ socket_set_nonblocking(ctx->log_context.socket_fd, 1);
- *server_handle = ctx;
+ ret = krb5_parse_name(ctx->context, client_name, &ctx->caller);
+ if (ret == 0)
+ ret = _kadm5_acl_init(ctx);
+ if (ret)
+ kadm5_s_destroy(ctx);
+ else
+ *server_handle = ctx;
return 0;
}
diff --git a/lib/kadm5/iprop-commands.in b/lib/kadm5/iprop-commands.in
index 78d88c9e36e3..5b59e5a3309e 100644
--- a/lib/kadm5/iprop-commands.in
+++ b/lib/kadm5/iprop-commands.in
@@ -42,6 +42,18 @@ command = {
argument = "file"
}
option = {
+ long = "no-lock"
+ short = "n"
+ type = "flag"
+ help = "don't lock iprop log"
+ }
+ option = {
+ long = "reverse"
+ short = "R"
+ type = "flag"
+ help = "dump the log in reverse order"
+ }
+ option = {
long = "realm"
short = "r"
type = "string"
@@ -49,7 +61,7 @@ command = {
}
function = "iprop_dump"
help = "Prints the iprop transaction log in text."
- max_args = "0"
+ max_args = "1"
}
command = {
name = "truncate"
@@ -66,9 +78,30 @@ command = {
type = "string"
help = "realm"
}
+ option = {
+ long = "keep-entries"
+ short = "K"
+ type = "integer"
+ help = "number of entries to keep"
+ default = "-1"
+ }
+ option = {
+ long = "max-bytes"
+ short = "B"
+ type = "integer"
+ help = "keep entries that fit in the given number of bytes"
+ default = "-1"
+ }
+ option = {
+ long = "reset"
+ short = "R"
+ type = "flag"
+ help = "reset the log to version 1; forces full propagation"
+ default = "0";
+ }
function = "iprop_truncate"
- help = "Truncate the log, preserve the version number."
- max_args = "0"
+ help = "Truncate the log, preserve the version number. Keeps 100 entries by default."
+ max_args = "1"
}
command = {
name = "replay"
@@ -101,7 +134,7 @@ command = {
}
function = "iprop_replay"
help = "Replay the log on the database."
- max_args = "0"
+ max_args = "1"
}
command = {
name = "last-version"
@@ -113,6 +146,12 @@ command = {
argument = "file"
}
option = {
+ long = "no-lock"
+ short = "n"
+ type = "flag"
+ help = "don't lock iprop log"
+ }
+ option = {
long = "realm"
short = "r"
type = "string"
@@ -120,6 +159,24 @@ command = {
}
function = "last_version"
help = "Print the last version of the log-file."
+}
+command = {
+ name = "signal"
+ option = {
+ long = "config-file"
+ short = "c"
+ type = "string"
+ help = "configuration file"
+ argument = "file"
+ }
+ option = {
+ long = "realm"
+ short = "r"
+ type = "string"
+ help = "realm"
+ }
+ function = "signal_master"
+ help = "Print the last version of the log-file."
max_args = "0"
}
command = {
diff --git a/lib/kadm5/iprop-log.8 b/lib/kadm5/iprop-log.8
index 7f84b0909d3a..f34d800f17a6 100644
--- a/lib/kadm5/iprop-log.8
+++ b/lib/kadm5/iprop-log.8
@@ -35,10 +35,10 @@
.\"
.Dd February 18, 2007
.Dt IPROP-LOG 8
-.Os Heimdal
+.Os
.Sh NAME
.Nm iprop-log
-.Nd maintain the iprop log file
+.Nd examine and maintain the iprop log file
.Sh SYNOPSIS
.Nm
.Op Fl Fl version
@@ -54,7 +54,20 @@
.Fl Fl realm= Ns Ar string
.Xc
.Oc
+.Oo Fl K Ar integer \*(Ba Xo
+.Fl Fl keep-entries= Ns Ar integer
+.Xc
+.Oc
+.Oo Fl B Ar integer \*(Ba Xo
+.Fl Fl max-bytes= Ns Ar integer
+.Xc
+.Oc
+.Oo Fl R \*(Ba Xo
+.Fl Fl reset
+.Xc
+.Oc
.Op Fl h | Fl Fl help
+.Op Ar log-file
.Pp
.Nm iprop-log dump
.Oo Fl c Ar file \*(Ba Xo
@@ -65,7 +78,16 @@
.Fl Fl realm= Ns Ar string
.Xc
.Oc
+.Oo Fl n \*(Ba Xo
+.Fl Fl no-lock
+.Xc
+.Oc
+.Oo Fl R \*(Ba Xo
+.Fl Fl reverse
+.Xc
+.Oc
.Op Fl h | Fl Fl help
+.Op Ar log-file
.Pp
.Nm iprop-log replay
.Op Fl Fl start-version= Ns Ar version-number
@@ -79,6 +101,35 @@
.Xc
.Oc
.Op Fl h | Fl Fl help
+.Op Ar log-file
+.Pp
+.Nm iprop-log last-version
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Oo Fl n \*(Ba Xo
+.Fl Fl no-lock
+.Xc
+.Oc
+.Op Fl h | Fl Fl help
+.Op Ar log-files
+.Pp
+.Nm iprop-log signal
+.Oo Fl c Ar file \*(Ba Xo
+.Fl Fl config-file= Ns Ar file
+.Xc
+.Oc
+.Oo Fl r Ar string \*(Ba Xo
+.Fl Fl realm= Ns Ar string
+.Xc
+.Oc
+.Op Fl h | Fl Fl help
+.Pp
.Sh DESCRIPTION
Supported options:
.Bl -tag -width Ds
@@ -94,11 +145,42 @@ command can be one of the following:
configuration file
.It Fl r Ar string , Fl Fl realm= Ns Ar string
realm
+.It Xo
+.Fl K Ar integer ,
+.Fl Fl keep-entries= Ns Ar integer
+.Xc
+.It Xo
+.Fl B Ar integer ,
+.Fl Fl max-bytes= Ns Ar integer
+.Xc
+.It Xo
+.Fl R ,
+.Fl Fl reset
+.Xc
.El
.Pp
-Truncates the log. Sets the new logs version number for the to the
-last entry of the old log. If the log is truncted by emptying the
-file, the log will start over at the first version (0).
+If
+.Fl Fl reset
+is given, then the given, configured, or default log file will be
+truncated and will start at version 1. This forces full propagations to
+all slave KDCs.
+.Pp
+Otherwise the log will be truncated but some entries will be preserved,
+as specified by the
+.Fl Fl keep-entries
+and/or
+.Fl Fl max-bytes
+options. The largest number of
+.Fl Fl keep-entries
+entries that are available and fit in the given
+.Fl Fl max-bytes
+option will be used. The
+.Fl Fl keep-entries option defaults to 100, and the
+.Fl Fl max-bytes
+option defaults to the
+.Ar log-max-size
+parameter in the configuration.
+.Pp
.It dump
.Bl -tag -width Ds
.It Fl c Ar file , Fl Fl config-file= Ns Ar file
@@ -108,9 +190,23 @@ configuration file
.Fl Fl realm= Ns Ar string
.Xc
realm
+.It Xo
+.Fl n Ar string ,
+.Fl Fl no-lock
+.Xc
+.It Xo
+.Fl R Ar string ,
+.Fl Fl reverse
+.Xc
.El
.Pp
-Print out all entries in the log to standard output.
+Print out all entries in the given, configured, or default log file to
+standard output. If the
+.Fl n
+option is used then don't lock the iprop log file. If the
+.Fl R
+option is used, then print the entries in reverse order
+(this can be useful when the log is very large).
.It replay
.Bl -tag -width Ds
.It Fl Fl start-version= Ns Ar version-number
@@ -125,17 +221,34 @@ configuration file
realm
.El
.Pp
-Replay the changes from specified entries (or all if none is
-specified) in the transaction log to the database.
+Replay the changes from specified entries (or all if none is specified)
+in the given, configured, or default transaction log file to the
+database.
.It last-version
.Bl -tag -width Ds
.It Fl c Ar file , Fl Fl config-file= Ns Ar file
configuration file
.It Fl r Ar string , Fl Fl realm= Ns Ar string
realm
+.It Xo
+.Fl n Ar string ,
+.Fl Fl no-lock
+.Xc
+.El
+.Pp
+prints the version of the last record in each of the given log files, or
+the configured, or the default log file if none is given.
+.It signal
+.Bl -tag -width Ds
+.It Fl c Ar file , Fl Fl config-file= Ns Ar file
+configuration file
+.It Fl r Ar string , Fl Fl realm= Ns Ar string
+realm
.El
.Pp
-prints the version of the last log entry.
+Signals the ipropd-master daemon to send updates to slaves. Normally
+kadmin does this every time it writes to the database, so this should
+rarely be needed.
.El
.Sh SEE ALSO
.Xr iprop 8
diff --git a/lib/kadm5/iprop-log.c b/lib/kadm5/iprop-log.c
index b201de66d1f6..9c18f832e132 100644
--- a/lib/kadm5/iprop-log.c
+++ b/lib/kadm5/iprop-log.c
@@ -46,17 +46,19 @@ get_kadmin_context(const char *config_file, char *realm)
kadm5_config_params conf;
krb5_error_code ret;
void *kadm_handle;
+ char *file = NULL;
char **files;
+ int aret;
if (config_file == NULL) {
- char *file;
- asprintf(&file, "%s/kdc.conf", hdb_db_dir(context));
- if (file == NULL)
+ aret = asprintf(&file, "%s/kdc.conf", hdb_db_dir(context));
+ if (aret == -1 || file == NULL)
errx(1, "out of memory");
config_file = file;
}
ret = krb5_prepend_config_files_default(config_file, &files);
+ free(file);
if (ret)
krb5_err(context, 1, ret, "getting configuration files");
@@ -101,7 +103,7 @@ static const char *op_names[] = {
"nop"
};
-static void
+static kadm5_ret_t
print_entry(kadm5_server_context *server_context,
uint32_t ver,
time_t timestamp,
@@ -111,27 +113,28 @@ print_entry(kadm5_server_context *server_context,
void *ctx)
{
char t[256];
+ const char *entry_kind = ctx;
int32_t mask;
+ int32_t nop_time;
+ uint32_t nop_ver;
hdb_entry ent;
krb5_principal source;
char *name1, *name2;
krb5_data data;
krb5_context scontext = server_context->context;
-
- off_t end = krb5_storage_seek(sp, 0, SEEK_CUR) + len;
-
krb5_error_code ret;
+ krb5_data_zero(&data);
+
strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
if((int)op < (int)kadm_get || (int)op > (int)kadm_nop) {
printf("unknown op: %d\n", op);
- krb5_storage_seek(sp, end, SEEK_SET);
- return;
+ return 0;
}
- printf ("%s: ver = %u, timestamp = %s, len = %u\n",
- op_names[op], ver, t, len);
+ printf ("%s%s: ver = %u, timestamp = %s, len = %u\n",
+ entry_kind, op_names[op], ver, t, len);
switch(op) {
case kadm_delete:
krb5_ret_principal(sp, &source);
@@ -258,11 +261,29 @@ print_entry(kadm5_server_context *server_context,
free_hdb_entry(&ent);
break;
case kadm_nop :
+ if (len == 16) {
+ uint64_t off;
+ krb5_ret_uint64(sp, &off);
+ printf("uberblock offset %llu ", (unsigned long long)off);
+ } else {
+ printf("nop");
+ }
+ if (len == 16 || len == 8) {
+ krb5_ret_int32(sp, &nop_time);
+ krb5_ret_uint32(sp, &nop_ver);
+
+ timestamp = nop_time;
+ strftime(t, sizeof(t), "%Y-%m-%d %H:%M:%S", localtime(&timestamp));
+ printf("timestamp %s version %u", t, nop_ver);
+ }
+ printf("\n");
break;
default:
abort();
}
- krb5_storage_seek(sp, end, SEEK_SET);
+ krb5_data_free(&data);
+
+ return 0;
}
int
@@ -270,21 +291,61 @@ iprop_dump(struct dump_options *opt, int argc, char **argv)
{
kadm5_server_context *server_context;
krb5_error_code ret;
+ enum kadm_iter_opts iter_opts_1st = 0;
+ enum kadm_iter_opts iter_opts_2nd = 0;
+ char *desc_1st = "";
+ char *desc_2nd = "";
server_context = get_kadmin_context(opt->config_file_string,
opt->realm_string);
- ret = kadm5_log_init (server_context);
+ if (argc > 0) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[0]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ }
+
+ if (opt->reverse_flag) {
+ iter_opts_1st = kadm_backward | kadm_unconfirmed;
+ iter_opts_2nd = kadm_backward | kadm_confirmed;
+ desc_1st = "unconfirmed ";
+ } else {
+ iter_opts_1st = kadm_forward | kadm_confirmed;
+ iter_opts_2nd = kadm_forward | kadm_unconfirmed;
+ desc_2nd = "unconfirmed";
+ }
+
+ if (opt->no_lock_flag) {
+ ret = kadm5_log_init_sharedlock(server_context, LOCK_NB);
+ if (ret == EAGAIN || ret == EWOULDBLOCK) {
+ warnx("Not locking the iprop log");
+ ret = kadm5_log_init_nolock(server_context);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_nolock");
+ }
+ } else {
+ warnx("If this command appears to block, try the --no-lock option");
+ ret = kadm5_log_init_sharedlock(server_context, 0);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_sharedlock");
+ }
+
+ ret = kadm5_log_foreach(server_context, iter_opts_1st,
+ NULL, print_entry, desc_1st);
if (ret)
- krb5_err (context, 1, ret, "kadm5_log_init");
+ krb5_warn(context, ret, "kadm5_log_foreach");
- ret = kadm5_log_foreach (server_context, print_entry, NULL);
- if(ret)
+ ret = kadm5_log_foreach(server_context, iter_opts_2nd,
+ NULL, print_entry, desc_2nd);
+ if (ret)
krb5_warn(context, ret, "kadm5_log_foreach");
ret = kadm5_log_end (server_context);
if (ret)
krb5_warn(context, ret, "kadm5_log_end");
+
+ kadm5_destroy(server_context);
return 0;
}
@@ -297,10 +358,41 @@ iprop_truncate(struct truncate_options *opt, int argc, char **argv)
server_context = get_kadmin_context(opt->config_file_string,
opt->realm_string);
- ret = kadm5_log_truncate (server_context);
+ if (argc > 0) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[0]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ }
+
+ if (opt->keep_entries_integer < 0 &&
+ opt->max_bytes_integer < 0) {
+ opt->keep_entries_integer = 100;
+ opt->max_bytes_integer = 0;
+ }
+ if (opt->keep_entries_integer < 0)
+ opt->keep_entries_integer = 0;
+ if (opt->max_bytes_integer < 0)
+ opt->max_bytes_integer = 0;
+
+ if (opt->reset_flag) {
+ /* First recover unconfirmed records */
+ ret = kadm5_log_init(server_context);
+ if (ret == 0)
+ ret = kadm5_log_reinit(server_context, 0);
+ } else {
+ ret = kadm5_log_init(server_context);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init");
+ ret = kadm5_log_truncate(server_context, opt->keep_entries_integer,
+ opt->max_bytes_integer);
+ }
if (ret)
- krb5_err (context, 1, ret, "kadm5_log_truncate");
+ krb5_err(context, 1, ret, "kadm5_log_truncate");
+ kadm5_log_signal_master(server_context);
+
+ kadm5_destroy(server_context);
return 0;
}
@@ -308,26 +400,71 @@ int
last_version(struct last_version_options *opt, int argc, char **argv)
{
kadm5_server_context *server_context;
+ char *alt_argv[2] = { NULL, NULL };
krb5_error_code ret;
uint32_t version;
+ size_t i;
server_context = get_kadmin_context(opt->config_file_string,
opt->realm_string);
- ret = kadm5_log_init (server_context);
- if (ret)
- krb5_err (context, 1, ret, "kadm5_log_init");
+ if (argc == 0) {
+ alt_argv[0] = strdup(server_context->log_context.log_file);
+ if (alt_argv[0] == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ argv = alt_argv;
+ argc = 1;
+ }
- ret = kadm5_log_get_version (server_context, &version);
- if (ret)
- krb5_err (context, 1, ret, "kadm5_log_get_version");
+ for (i = 0; i < argc; i++) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[i]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+
+ if (opt->no_lock_flag) {
+ ret = kadm5_log_init_sharedlock(server_context, LOCK_NB);
+ if (ret == EAGAIN || ret == EWOULDBLOCK) {
+ warnx("Not locking the iprop log");
+ ret = kadm5_log_init_nolock(server_context);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_nolock");
+ }
+ } else {
+ warnx("If this command appears to block, try the "
+ "--no-lock option");
+ ret = kadm5_log_init_sharedlock(server_context, 0);
+ if (ret)
+ krb5_err(context, 1, ret, "kadm5_log_init_sharedlock");
+ }
+
+ ret = kadm5_log_get_version (server_context, &version);
+ if (ret)
+ krb5_err (context, 1, ret, "kadm5_log_get_version");
+
+ ret = kadm5_log_end (server_context);
+ if (ret)
+ krb5_warn(context, ret, "kadm5_log_end");
+
+ printf("version: %lu\n", (unsigned long)version);
+ }
- ret = kadm5_log_end (server_context);
- if (ret)
- krb5_warn(context, ret, "kadm5_log_end");
+ kadm5_destroy(server_context);
+ free(alt_argv[0]);
+ return 0;
+}
- printf("version: %lu\n", (unsigned long)version);
+int
+signal_master(struct signal_options *opt, int argc, char **argv)
+{
+ kadm5_server_context *server_context;
+
+ server_context = get_kadmin_context(opt->config_file_string,
+ opt->realm_string);
+ kadm5_log_signal_master(server_context);
+
+ kadm5_destroy(server_context);
return 0;
}
@@ -338,7 +475,7 @@ last_version(struct last_version_options *opt, int argc, char **argv)
int start_version = -1;
int end_version = -1;
-static void
+static kadm5_ret_t
apply_entry(kadm5_server_context *server_context,
uint32_t ver,
time_t timestamp,
@@ -353,18 +490,18 @@ apply_entry(kadm5_server_context *server_context,
if((opt->start_version_integer != -1 && ver < (uint32_t)opt->start_version_integer) ||
(opt->end_version_integer != -1 && ver > (uint32_t)opt->end_version_integer)) {
/* XXX skip this entry */
- krb5_storage_seek(sp, len, SEEK_CUR);
- return;
+ return 0;
}
printf ("ver %u... ", ver);
fflush (stdout);
- ret = kadm5_log_replay (server_context,
- op, ver, len, sp);
+ ret = kadm5_log_replay(server_context, op, ver, len, sp);
if (ret)
krb5_warn (server_context->context, ret, "kadm5_log_replay");
printf ("done\n");
+
+ return 0;
}
int
@@ -376,6 +513,13 @@ iprop_replay(struct replay_options *opt, int argc, char **argv)
server_context = get_kadmin_context(opt->config_file_string,
opt->realm_string);
+ if (argc > 0) {
+ free(server_context->log_context.log_file);
+ server_context->log_context.log_file = strdup(argv[0]);
+ if (server_context->log_context.log_file == NULL)
+ krb5_err(context, 1, errno, "strdup");
+ }
+
ret = server_context->db->hdb_open(context,
server_context->db,
O_RDWR | O_CREAT, 0600);
@@ -386,7 +530,9 @@ iprop_replay(struct replay_options *opt, int argc, char **argv)
if (ret)
krb5_err (context, 1, ret, "kadm5_log_init");
- ret = kadm5_log_foreach (server_context, apply_entry, opt);
+ ret = kadm5_log_foreach(server_context,
+ kadm_forward | kadm_confirmed | kadm_unconfirmed,
+ NULL, apply_entry, opt);
if(ret)
krb5_warn(context, ret, "kadm5_log_foreach");
ret = kadm5_log_end (server_context);
@@ -396,6 +542,7 @@ iprop_replay(struct replay_options *opt, int argc, char **argv)
if (ret)
krb5_err (context, 1, ret, "db->close");
+ kadm5_destroy(server_context);
return 0;
}
diff --git a/lib/kadm5/iprop-log.cat8 b/lib/kadm5/iprop-log.cat8
index be5630ff1e52..a71587396e42 100644
--- a/lib/kadm5/iprop-log.cat8
+++ b/lib/kadm5/iprop-log.cat8
@@ -2,20 +2,29 @@
IPROP-LOG(8) BSD System Manager's Manual IPROP-LOG(8)
NNAAMMEE
- iipprroopp--lloogg -- maintain the iprop log file
+ iipprroopp--lloogg -- examine and maintain the iprop log file
SSYYNNOOPPSSIISS
iipprroopp--lloogg [----vveerrssiioonn] [--hh | ----hheellpp] _c_o_m_m_a_n_d
iipprroopp--lloogg ttrruunnccaattee [--cc _f_i_l_e | ----ccoonnffiigg--ffiillee==_f_i_l_e] [--rr _s_t_r_i_n_g |
- ----rreeaallmm==_s_t_r_i_n_g] [--hh | ----hheellpp]
+ ----rreeaallmm==_s_t_r_i_n_g] [--KK _i_n_t_e_g_e_r | ----kkeeeepp--eennttrriieess==_i_n_t_e_g_e_r] [--BB
+ _i_n_t_e_g_e_r | ----mmaaxx--bbyytteess==_i_n_t_e_g_e_r] [--RR | ----rreesseett] [--hh | ----hheellpp]
+ [_l_o_g_-_f_i_l_e]
iipprroopp--lloogg dduummpp [--cc _f_i_l_e | ----ccoonnffiigg--ffiillee==_f_i_l_e] [--rr _s_t_r_i_n_g |
- ----rreeaallmm==_s_t_r_i_n_g] [--hh | ----hheellpp]
+ ----rreeaallmm==_s_t_r_i_n_g] [--nn | ----nnoo--lloocckk] [--RR | ----rreevveerrssee] [--hh | ----hheellpp]
+ [_l_o_g_-_f_i_l_e]
iipprroopp--lloogg rreeppllaayy [----ssttaarrtt--vveerrssiioonn==_v_e_r_s_i_o_n_-_n_u_m_b_e_r]
[----eenndd--vveerrssiioonn==_v_e_r_s_i_o_n_-_n_u_m_b_e_r] [--cc _f_i_l_e | ----ccoonnffiigg--ffiillee==_f_i_l_e]
- [--rr _s_t_r_i_n_g | ----rreeaallmm==_s_t_r_i_n_g] [--hh | ----hheellpp]
+ [--rr _s_t_r_i_n_g | ----rreeaallmm==_s_t_r_i_n_g] [--hh | ----hheellpp] [_l_o_g_-_f_i_l_e]
+
+ iipprroopp--lloogg llaasstt--vveerrssiioonn [--cc _f_i_l_e | ----ccoonnffiigg--ffiillee==_f_i_l_e] [--rr _s_t_r_i_n_g |
+ ----rreeaallmm==_s_t_r_i_n_g] [--nn | ----nnoo--lloocckk] [--hh | ----hheellpp] [_l_o_g_-_f_i_l_e_s]
+
+ iipprroopp--lloogg ssiiggnnaall [--cc _f_i_l_e | ----ccoonnffiigg--ffiillee==_f_i_l_e] [--rr _s_t_r_i_n_g |
+ ----rreeaallmm==_s_t_r_i_n_g] [--hh | ----hheellpp]
DDEESSCCRRIIPPTTIIOONN
Supported options:
@@ -34,10 +43,23 @@ DDEESSCCRRIIPPTTIIOONN
--rr _s_t_r_i_n_g, ----rreeaallmm==_s_t_r_i_n_g
realm
- Truncates the log. Sets the new logs version number for the to
- the last entry of the old log. If the log is truncted by emp-
- tying the file, the log will start over at the first version
- (0).
+ --KK _i_n_t_e_g_e_r, ----kkeeeepp--eennttrriieess==_i_n_t_e_g_e_r
+
+ --BB _i_n_t_e_g_e_r, ----mmaaxx--bbyytteess==_i_n_t_e_g_e_r
+
+ --RR, ----rreesseett
+
+ If ----rreesseett is given, then the given, configured, or default log
+ file will be truncated and will start at version 1. This
+ forces full propagations to all slave KDCs.
+
+ Otherwise the log will be truncated but some entries will be
+ preserved, as specified by the ----kkeeeepp--eennttrriieess and/or
+ ----mmaaxx--bbyytteess options. The largest number of ----kkeeeepp--eennttrriieess
+ entries that are available and fit in the given ----mmaaxx--bbyytteess
+ option will be used. The ----kkeeeepp--eennttrriieess --ooppttiioonn --ddeeffaauullttss --ttoo
+ --110000,, --aanndd --tthhee ----mmaaxx--bbyytteess option defaults to the _l_o_g_-_m_a_x_-_s_i_z_e
+ parameter in the configuration.
dump
@@ -47,7 +69,15 @@ DDEESSCCRRIIPPTTIIOONN
--rr _s_t_r_i_n_g, ----rreeaallmm==_s_t_r_i_n_g
realm
- Print out all entries in the log to standard output.
+ --nn _s_t_r_i_n_g, ----nnoo--lloocckk
+
+ --RR _s_t_r_i_n_g, ----rreevveerrssee
+
+ Print out all entries in the given, configured, or default log
+ file to standard output. If the --nn option is used then don't
+ lock the iprop log file. If the --RR option is used, then print
+ the entries in reverse order (this can be useful when the log
+ is very large).
replay
@@ -64,7 +94,8 @@ DDEESSCCRRIIPPTTIIOONN
realm
Replay the changes from specified entries (or all if none is
- specified) in the transaction log to the database.
+ specified) in the given, configured, or default transaction log
+ file to the database.
last-version
@@ -74,9 +105,25 @@ DDEESSCCRRIIPPTTIIOONN
--rr _s_t_r_i_n_g, ----rreeaallmm==_s_t_r_i_n_g
realm
- prints the version of the last log entry.
+ --nn _s_t_r_i_n_g, ----nnoo--lloocckk
+
+ prints the version of the last record in each of the given log
+ files, or the configured, or the default log file if none is
+ given.
+
+ signal
+
+ --cc _f_i_l_e, ----ccoonnffiigg--ffiillee==_f_i_l_e
+ configuration file
+
+ --rr _s_t_r_i_n_g, ----rreeaallmm==_s_t_r_i_n_g
+ realm
+
+ Signals the ipropd-master daemon to send updates to slaves.
+ Normally kadmin does this every time it writes to the database,
+ so this should rarely be needed.
SSEEEE AALLSSOO
iprop(8)
-Heimdal February 18, 2007 Heimdal
+BSD February 18, 2007 BSD
diff --git a/lib/kadm5/iprop.8 b/lib/kadm5/iprop.8
index 6be1f110c45c..6de7a1705705 100644
--- a/lib/kadm5/iprop.8
+++ b/lib/kadm5/iprop.8
@@ -33,12 +33,12 @@
.\"
.Dd May 24, 2005
.Dt IPROP 8
-.Os Heimdal
+.Os
.Sh NAME
.Nm iprop ,
.Nm ipropd-master ,
.Nm ipropd-slave
-.Nd propagate changes to a Heimdal Kerberos master KDC to slave KDCs
+.Nd propagate transactions from a Heimdal Kerberos master KDC to slave KDCs
.Sh SYNOPSIS
.Nm ipropd-master
.Oo Fl c Ar string \*(Ba Xo
@@ -110,13 +110,18 @@ which sends the whole database to the slaves regularly,
.Nm
normally sends only the changes as they happen on the master.
The master keeps track of all the changes by assigning a version
-number to every change to the database.
+number to every transaction to the database.
The slaves know which was the latest version they saw, and in this
way it can be determined if they are in sync or not.
-A log of all the changes is kept on the master.
+A log of all the transactions is kept on the master.
When a slave is at an older version than the oldest one in the log,
the whole database has to be sent.
.Pp
+The log of transactions is also used to implement a two-phase commit
+(with roll-forward for recovery) method of updating the HDB.
+Transactions are first recorded in the log, then in the HDB, then
+the log is updated to mark the transaction as committed.
+.Pp
The changes are propagated over a secure channel (on port 2121 by
default).
This should normally be defined as
@@ -175,6 +180,11 @@ like 5 min, 300 s, or simply a number of seconds.
.Pa slaves ,
.Pa slave-stats
in the database directory.
+.Pa ipropd-master.pid ,
+.Pa ipropd-slave.pid
+in the database directory, or in the directory named by the
+.Ev HEIM_PIDFILE_DIR
+environment variable.
.Sh SEE ALSO
.Xr krb5.conf 5 ,
.Xr hprop 8 ,
diff --git a/lib/kadm5/iprop.cat8 b/lib/kadm5/iprop.cat8
index b2aebb839651..36b87b1a351b 100644
--- a/lib/kadm5/iprop.cat8
+++ b/lib/kadm5/iprop.cat8
@@ -2,8 +2,8 @@
IPROP(8) BSD System Manager's Manual IPROP(8)
NNAAMMEE
- iipprroopp, iipprrooppdd--mmaasstteerr, iipprrooppdd--ssllaavvee -- propagate changes to a Heimdal Ker-
- beros master KDC to slave KDCs
+ iipprroopp, iipprrooppdd--mmaasstteerr, iipprrooppdd--ssllaavvee -- propagate transactions from a Heim-
+ dal Kerberos master KDC to slave KDCs
SSYYNNOOPPSSIISS
iipprrooppdd--mmaasstteerr [--cc _s_t_r_i_n_g | ----ccoonnffiigg--ffiillee==_s_t_r_i_n_g] [--rr _s_t_r_i_n_g |
@@ -32,11 +32,16 @@ DDEESSCCRRIIPPTTIIOONN
In contrast to hprop(8), which sends the whole database to the slaves
regularly, iipprroopp normally sends only the changes as they happen on the
master. The master keeps track of all the changes by assigning a version
- number to every change to the database. The slaves know which was the
- latest version they saw, and in this way it can be determined if they are
- in sync or not. A log of all the changes is kept on the master. When a
- slave is at an older version than the oldest one in the log, the whole
- database has to be sent.
+ number to every transaction to the database. The slaves know which was
+ the latest version they saw, and in this way it can be determined if they
+ are in sync or not. A log of all the transactions is kept on the master.
+ When a slave is at an older version than the oldest one in the log, the
+ whole database has to be sent.
+
+ The log of transactions is also used to implement a two-phase commit
+ (with roll-forward for recovery) method of updating the HDB. Transac-
+ tions are first recorded in the log, then in the HDB, then the log is
+ updated to mark the transaction as committed.
The changes are propagated over a secure channel (on port 2121 by
default). This should normally be defined as ``iprop/tcp'' in
@@ -98,9 +103,11 @@ DDEESSCCRRIIPPTTIIOONN
like 5 min, 300 s, or simply a number of seconds.
FFIILLEESS
- _s_l_a_v_e_s, _s_l_a_v_e_-_s_t_a_t_s in the database directory.
+ _s_l_a_v_e_s, _s_l_a_v_e_-_s_t_a_t_s in the database directory. _i_p_r_o_p_d_-_m_a_s_t_e_r_._p_i_d,
+ _i_p_r_o_p_d_-_s_l_a_v_e_._p_i_d in the database directory, or in the directory named by
+ the HEIM_PIDFILE_DIR environment variable.
SSEEEE AALLSSOO
krb5.conf(5), hprop(8), hpropd(8), iprop-log(8), kdc(8).
-Heimdal May 24, 2005 Heimdal
+BSD May 24, 2005 BSD
diff --git a/lib/kadm5/iprop.h b/lib/kadm5/iprop.h
index 32a80cec92ec..945fb90b1685 100644
--- a/lib/kadm5/iprop.h
+++ b/lib/kadm5/iprop.h
@@ -61,10 +61,20 @@ enum iprop_cmd { I_HAVE = 1,
ONE_PRINC = 4,
NOW_YOU_HAVE = 5,
ARE_YOU_THERE = 6,
- I_AM_HERE = 7
+ I_AM_HERE = 7,
+ YOU_HAVE_LAST_VERSION = 8
};
extern sig_atomic_t exit_flag;
void setup_signal(void);
+enum ipropd_exit_code {
+ IPROPD_DONE = 0,
+ IPROPD_RESTART = 1,
+ IPROPD_RESTART_SLOW = 2,
+ IPROPD_FATAL = 3,
+};
+
+int restarter(krb5_context, size_t *);
+
#endif /* __IPROP_H__ */
diff --git a/lib/kadm5/ipropd_common.c b/lib/kadm5/ipropd_common.c
index 2e3d94107c90..be0adc1b3802 100644
--- a/lib/kadm5/ipropd_common.c
+++ b/lib/kadm5/ipropd_common.c
@@ -32,7 +32,11 @@
*/
#include "iprop.h"
-RCSID("$Id$");
+
+#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
sig_atomic_t exit_flag;
@@ -71,3 +75,191 @@ setup_signal(void)
#endif
#endif
}
+
+/*
+ * Fork a child to run the service, and restart it if it dies.
+ *
+ * Returns -1 if not supported, else a file descriptor that the service
+ * should select() for. Any events on that file descriptor should cause
+ * the caller to exit immediately, as that means that the restarter
+ * exited.
+ *
+ * The service's normal exit status values should be should be taken
+ * from enum ipropd_exit_code. IPROPD_FATAL causes the restarter to
+ * stop restarting the service and to exit.
+ *
+ * A count of restarts is output via the `countp' argument, if it is
+ * non-NULL. This is useful for testing this function (e.g., kill the
+ * restarter after N restarts and check that the child gets the signal
+ * sent to it).
+ *
+ * This requires fork() and waitpid() (otherwise returns -1). Ignoring
+ * SIGCHLD, of course, would be bad.
+ *
+ * We could support this on Windows by spawning a child with mostly the
+ * same arguments as the restarter process.
+ */
+int
+restarter(krb5_context context, size_t *countp)
+{
+#if defined(HAVE_FORK) && defined(HAVE_WAITPID)
+ struct timeval tmout;
+ pid_t pid = -1;
+ pid_t wpid = -1;
+ int status;
+ int fds[2];
+ int fds2[2];
+ size_t count = 0;
+ fd_set readset;
+
+ fds[0] = -1;
+ fds[1] = -1;
+ fds2[0] = -1;
+ fds2[1] = -1;
+
+ signal(SIGCHLD, SIG_DFL);
+
+ while (!exit_flag) {
+ /* Close the pipe ends we keep open */
+ if (fds[1] != -1)
+ (void) close(fds[1]);
+ if (fds2[0] != -1)
+ (void) close(fds2[1]);
+
+ /* A pipe so the child can detect the parent's death */
+ if (pipe(fds) == -1) {
+ krb5_err(context, 1, errno,
+ "Could not setup pipes in service restarter");
+ }
+
+ /* A pipe so the parent can detect the child's death */
+ if (pipe(fds2) == -1) {
+ krb5_err(context, 1, errno,
+ "Could not setup pipes in service restarter");
+ }
+
+ fflush(stdout);
+ fflush(stderr);
+
+ pid = fork();
+ if (pid == -1)
+ krb5_err(context, 1, errno, "Could not fork in service restarter");
+ if (pid == 0) {
+ if (countp != NULL)
+ *countp = count;
+ (void) close(fds[1]);
+ (void) close(fds2[0]);
+ return fds[0];
+ }
+
+ count++;
+
+ (void) close(fds[0]);
+ (void) close(fds2[1]);
+
+ do {
+ wpid = waitpid(pid, &status, 0);
+ } while (wpid == -1 && errno == EINTR && !exit_flag);
+ if (wpid == -1 && errno == EINTR)
+ break; /* We were signaled; gotta kill the child and exit */
+ if (wpid == -1) {
+ if (errno != ECHILD) {
+ warn("waitpid() failed; killing restarter's child process");
+ kill(pid, SIGTERM);
+ }
+ krb5_err(context, 1, errno, "restarter failed waiting for child");
+ }
+
+ assert(wpid == pid);
+ wpid = -1;
+ pid = -1;
+ if (WIFEXITED(status)) {
+ switch (WEXITSTATUS(status)) {
+ case IPROPD_DONE:
+ exit(0);
+ case IPROPD_RESTART_SLOW:
+ if (exit_flag)
+ exit(1);
+ krb5_warnx(context, "Waiting 2 minutes to restart");
+ sleep(120);
+ continue;
+ case IPROPD_FATAL:
+ krb5_errx(context, WEXITSTATUS(status),
+ "Sockets and pipes not supported for "
+ "iprop log files");
+ case IPROPD_RESTART:
+ default:
+ if (exit_flag)
+ exit(1);
+ /* Add exponential backoff (with max backoff)? */
+ krb5_warnx(context, "Waiting 30 seconds to restart");
+ sleep(30);
+ continue;
+ }
+ }
+ /* else */
+ krb5_warnx(context, "Child was killed; waiting 30 seconds to restart");
+ sleep(30);
+ }
+
+ if (pid == -1)
+ exit(0); /* No dead child to reap; done */
+
+ assert(pid > 0);
+ if (wpid != pid) {
+ warnx("Interrupted; killing child (pid %ld) with %d",
+ (long)pid, exit_flag);
+ krb5_warnx(context, "Interrupted; killing child (pid %ld) with %d",
+ (long)pid, exit_flag);
+ kill(pid, exit_flag);
+
+ /* Wait up to one second for the child */
+ tmout.tv_sec = 1;
+ tmout.tv_usec = 0;
+ FD_ZERO(&readset);
+ FD_SET(fds2[0], &readset);
+ /* We don't care why select() returns */
+ (void) select(fds2[0] + 1, &readset, NULL, NULL, &tmout);
+ /*
+ * We haven't reaped the child yet; if it's a zombie, then
+ * SIGKILLing it won't hurt. If it's not a zombie yet, well,
+ * we're out of patience.
+ */
+ kill(pid, SIGKILL);
+ do {
+ wpid = waitpid(pid, &status, 0);
+ } while (wpid != pid && errno == EINTR);
+ if (wpid == -1)
+ krb5_err(context, 1, errno, "restarter failed waiting for child");
+ }
+
+ /* Finally, the child is dead and reaped */
+ if (WIFEXITED(status))
+ exit(WEXITSTATUS(status));
+ if (WIFSIGNALED(status)) {
+ switch (WTERMSIG(status)) {
+ case SIGTERM:
+ case SIGXCPU:
+ case SIGINT:
+ exit(0);
+ default:
+ /*
+ * Attempt to set the same exit status for the parent as for
+ * the child.
+ */
+ kill(getpid(), WTERMSIG(status));
+ /*
+ * We can get past the self-kill if we inherited a SIG_IGN
+ * disposition that the child reset to SIG_DFL.
+ */
+ }
+ }
+ exit(1);
+#else
+ if (countp != NULL)
+ *countp = 0;
+ errno = ENOTSUP;
+ return -1;
+#endif
+}
+
diff --git a/lib/kadm5/ipropd_master.c b/lib/kadm5/ipropd_master.c
index 492bbadbd2c2..d0fdc8e26f10 100644
--- a/lib/kadm5/ipropd_master.c
+++ b/lib/kadm5/ipropd_master.c
@@ -36,6 +36,8 @@
static krb5_log_facility *log_facility;
+static int verbose;
+
const char *slave_stats_file;
const char *slave_time_missing = "2 min";
const char *slave_time_gone = "5 min";
@@ -125,6 +127,7 @@ struct slave {
char *name;
krb5_auth_context ac;
uint32_t version;
+ uint32_t version_tstamp;
time_t seen;
unsigned long flags;
#define SLAVE_F_DEAD 0x1
@@ -310,44 +313,40 @@ error:
remove_slave(context, s, root);
}
-struct prop_context {
- krb5_auth_context auth_context;
- krb5_socket_t fd;
-};
-
static int
-prop_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v)
+dump_one (krb5_context context, HDB *db, hdb_entry_ex *entry, void *v)
{
krb5_error_code ret;
+ krb5_storage *dump = (krb5_storage *)v;
krb5_storage *sp;
krb5_data data;
- struct slave *s = (struct slave *)v;
ret = hdb_entry2value (context, &entry->entry, &data);
if (ret)
return ret;
ret = krb5_data_realloc (&data, data.length + 4);
- if (ret) {
- krb5_data_free (&data);
- return ret;
- }
+ if (ret)
+ goto done;
memmove ((char *)data.data + 4, data.data, data.length - 4);
sp = krb5_storage_from_data(&data);
if (sp == NULL) {
- krb5_data_free (&data);
- return ENOMEM;
+ ret = ENOMEM;
+ goto done;
}
- krb5_store_int32(sp, ONE_PRINC);
+ ret = krb5_store_uint32(sp, ONE_PRINC);
krb5_storage_free(sp);
- ret = krb5_write_priv_message (context, s->ac, &s->fd, &data);
+ if (ret == 0)
+ ret = krb5_store_data(dump, data);
+
+done:
krb5_data_free (&data);
return ret;
}
static int
-send_complete (krb5_context context, slave *s,
- const char *database, uint32_t current_version)
+write_dump (krb5_context context, krb5_storage *dump,
+ const char *database, uint32_t current_version)
{
krb5_error_code ret;
krb5_storage *sp;
@@ -355,34 +354,50 @@ send_complete (krb5_context context, slave *s,
krb5_data data;
char buf[8];
+ /* we assume that the caller has obtained an exclusive lock */
+
+ ret = krb5_storage_truncate(dump, 0);
+ if (ret)
+ return ret;
+
+ if (krb5_storage_seek(dump, 0, SEEK_SET) != 0)
+ return errno;
+
+ /*
+ * First we store zero as the HDB version, this will indicate to a
+ * later reader that the dumpfile is invalid. We later write the
+ * correct version in the file after we have written all of the
+ * messages. A dump with a zero version will not be considered
+ * to be valid.
+ */
+
+ ret = krb5_store_uint32(dump, 0);
+
ret = hdb_create (context, &db, database);
if (ret)
- krb5_err (context, 1, ret, "hdb_create: %s", database);
+ krb5_err (context, IPROPD_RESTART, ret, "hdb_create: %s", database);
ret = db->hdb_open (context, db, O_RDONLY, 0);
if (ret)
- krb5_err (context, 1, ret, "db->open");
+ krb5_err (context, IPROPD_RESTART, ret, "db->open");
sp = krb5_storage_from_mem (buf, 4);
if (sp == NULL)
- krb5_errx (context, 1, "krb5_storage_from_mem");
- krb5_store_int32 (sp, TELL_YOU_EVERYTHING);
+ krb5_errx (context, IPROPD_RESTART, "krb5_storage_from_mem");
+ krb5_store_uint32 (sp, TELL_YOU_EVERYTHING);
krb5_storage_free (sp);
data.data = buf;
data.length = 4;
- ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
-
+ ret = krb5_store_data(dump, data);
if (ret) {
- krb5_warn (context, ret, "krb5_write_priv_message");
- slave_dead(context, s);
+ krb5_warn (context, ret, "write_dump");
return ret;
}
- ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, prop_one, s);
+ ret = hdb_foreach (context, db, HDB_F_ADMIN_DATA, dump_one, dump);
if (ret) {
- krb5_warn (context, ret, "hdb_foreach");
- slave_dead(context, s);
+ krb5_warn (context, ret, "write_dump: hdb_foreach");
return ret;
}
@@ -391,25 +406,220 @@ send_complete (krb5_context context, slave *s,
sp = krb5_storage_from_mem (buf, 8);
if (sp == NULL)
- krb5_errx (context, 1, "krb5_storage_from_mem");
- krb5_store_int32 (sp, NOW_YOU_HAVE);
- krb5_store_int32 (sp, current_version);
+ krb5_errx (context, IPROPD_RESTART, "krb5_storage_from_mem");
+ ret = krb5_store_uint32(sp, NOW_YOU_HAVE);
+ if (ret == 0)
+ krb5_store_uint32(sp, current_version);
krb5_storage_free (sp);
data.length = 8;
- s->version = current_version;
+ if (ret == 0)
+ ret = krb5_store_data(dump, data);
- ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
- if (ret) {
- slave_dead(context, s);
- krb5_warn (context, ret, "krb5_write_priv_message");
- return ret;
+ /*
+ * We must ensure that the entire valid dump is written to disk
+ * before we write the current version at the front thus making
+ * it a valid dump file. If we crash around here, this can be
+ * important upon reboot.
+ */
+
+ if (ret == 0)
+ ret = krb5_storage_fsync(dump);
+
+ if (ret == 0 && krb5_storage_seek(dump, 0, SEEK_SET) == -1)
+ ret = errno;
+
+ /* Write current version at the front making the dump valid */
+
+ if (ret == 0)
+ ret = krb5_store_uint32(dump, current_version);
+
+ /*
+ * We don't need to fsync(2) after the real version is written as
+ * it is not a disaster if it doesn't make it to disk if we crash.
+ * After all, we'll just create a new dumpfile.
+ */
+
+ if (ret == 0)
+ krb5_warnx(context, "wrote new dumpfile (version %u)",
+ current_version);
+ else
+ krb5_warn(context, ret, "failed to write new dumpfile (version %u)",
+ current_version);
+
+ return ret;
+}
+
+static int
+send_complete (krb5_context context, slave *s, const char *database,
+ uint32_t current_version, uint32_t oldest_version,
+ uint32_t initial_log_tstamp)
+{
+ krb5_error_code ret;
+ krb5_storage *dump = NULL;
+ uint32_t vno = 0;
+ krb5_data data;
+ int fd = -1;
+ struct stat st;
+ char *dfn;
+
+ ret = asprintf(&dfn, "%s/ipropd.dumpfile", hdb_db_dir(context));
+ if (ret == -1 || !dfn) {
+ krb5_warn(context, ENOMEM, "Cannot allocate memory");
+ return ENOMEM;
}
- slave_seen(s);
+ fd = open(dfn, O_CREAT|O_RDWR, 0600);
+ if (fd == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "Cannot open/create iprop dumpfile %s", dfn);
+ free(dfn);
+ return ret;
+ }
+ free(dfn);
- return 0;
+ dump = krb5_storage_from_fd(fd);
+ if (!dump) {
+ ret = errno;
+ krb5_warn(context, ret, "krb5_storage_from_fd");
+ goto done;
+ }
+
+ for (;;) {
+ ret = flock(fd, LOCK_SH);
+ if (ret == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "flock(fd, LOCK_SH)");
+ goto done;
+ }
+
+ if (krb5_storage_seek(dump, 0, SEEK_SET) == (off_t)-1) {
+ ret = errno;
+ krb5_warn(context, ret, "krb5_storage_seek(dump, 0, SEEK_SET)");
+ goto done;
+ }
+
+ vno = 0;
+ ret = krb5_ret_uint32(dump, &vno);
+ if (ret && ret != HEIM_ERR_EOF) {
+ krb5_warn(context, ret, "krb5_ret_uint32(dump, &vno)");
+ goto done;
+ }
+
+ if (fstat(fd, &st) == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "send_complete: could not stat dump file");
+ goto done;
+ }
+
+ /*
+ * If the current dump has an appropriate version, then we can
+ * break out of the loop and send the file below.
+ */
+
+ if (ret == 0 && vno != 0 && st.st_mtime > initial_log_tstamp &&
+ vno >= oldest_version && vno <= current_version)
+ break;
+
+ if (verbose)
+ krb5_warnx(context, "send_complete: dumping HDB");
+
+ /*
+ * Otherwise, we may need to write a new dump file. We
+ * obtain an exclusive lock on the fd. Because this is
+ * not guaranteed to be an upgrade of our existing shared
+ * lock, someone else may have written a new dumpfile while
+ * we were waiting and so we must first check the vno of
+ * the dump to see if that happened. If it did, we need
+ * to go back to the top of the loop so that we can downgrade
+ * our lock to a shared one.
+ */
+
+ ret = flock(fd, LOCK_EX);
+ if (ret == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "flock(fd, LOCK_EX)");
+ goto done;
+ }
+
+ ret = krb5_storage_seek(dump, 0, SEEK_SET);
+ if (ret == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "krb5_storage_seek(dump, 0, SEEK_SET)");
+ goto done;
+ }
+
+ vno = 0;
+ ret = krb5_ret_uint32(dump, &vno);
+ if (ret && ret != HEIM_ERR_EOF) {
+ krb5_warn(context, ret, "krb5_ret_uint32(dump, &vno)");
+ goto done;
+ }
+
+ if (fstat(fd, &st) == -1) {
+ ret = errno;
+ krb5_warn(context, ret, "send_complete: could not stat dump file");
+ goto done;
+ }
+
+ /* check if someone wrote a better version for us */
+ if (ret == 0 && vno != 0 && st.st_mtime > initial_log_tstamp &&
+ vno >= oldest_version && vno <= current_version)
+ continue;
+
+ /* Now, we know that we must write a new dump file. */
+
+ ret = write_dump(context, dump, database, current_version);
+ if (ret)
+ goto done;
+
+ /*
+ * And we must continue to the top of the loop so that we can
+ * downgrade to a shared lock.
+ */
+ }
+
+ /*
+ * Leaving the above loop, dump should have a ptr right after the initial
+ * 4 byte DB version number and we should have a shared lock on the file
+ * (which we may have just created), so we are reading to simply blast
+ * the data down the wire.
+ */
+
+ for (;;) {
+ ret = krb5_ret_data(dump, &data);
+ if (ret == HEIM_ERR_EOF) {
+ ret = 0; /* EOF is not an error, it's success */
+ goto done;
+ }
+
+ if (ret) {
+ krb5_warn(context, ret, "krb5_ret_data(dump, &data)");
+ slave_dead(context, s);
+ goto done;
+ }
+
+ ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
+ krb5_data_free(&data);
+
+ if (ret) {
+ krb5_warn (context, ret, "krb5_write_priv_message");
+ slave_dead(context, s);
+ goto done;
+ }
+ }
+
+done:
+ if (!ret) {
+ s->version = vno;
+ slave_seen(s);
+ }
+ if (fd != -1)
+ close(fd);
+ if (dump)
+ krb5_storage_free(dump);
+ return ret;
}
static int
@@ -436,71 +646,152 @@ send_are_you_there (krb5_context context, slave *s)
slave_dead(context, s);
return 1;
}
- krb5_store_int32 (sp, ARE_YOU_THERE);
+ ret = krb5_store_uint32(sp, ARE_YOU_THERE);
krb5_storage_free (sp);
- ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
+ if (ret == 0) {
+ ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
- if (ret) {
- krb5_warn (context, ret, "are_you_there: krb5_write_priv_message");
- slave_dead(context, s);
- return 1;
+ if (ret) {
+ krb5_warn(context, ret, "are_you_there: krb5_write_priv_message");
+ slave_dead(context, s);
+ return 1;
+ }
}
return 0;
}
static int
-send_diffs (krb5_context context, slave *s, int log_fd,
- const char *database, uint32_t current_version)
+send_diffs (kadm5_server_context *server_context, slave *s, int log_fd,
+ const char *database, uint32_t current_version,
+ uint32_t current_tstamp)
{
+ krb5_context context = server_context->context;
krb5_storage *sp;
- uint32_t ver;
- time_t timestamp;
+ uint32_t ver, initial_version, initial_version2;
+ uint32_t initial_tstamp, initial_tstamp2;
enum kadm_ops op;
uint32_t len;
off_t right, left;
+ krb5_ssize_t bytes;
krb5_data data;
int ret = 0;
- if (s->version == current_version) {
- krb5_warnx(context, "slave %s in sync already at version %ld",
- s->name, (long)s->version);
- return 0;
+ if (s->flags & SLAVE_F_DEAD) {
+ krb5_warnx(context, "not sending diffs to dead slave %s", s->name);
+ return 0;
}
- if (s->flags & SLAVE_F_DEAD)
- return 0;
+ if (s->version == current_version) {
+ char buf[4];
- /* if slave is a fresh client, starting over */
- if (s->version == 0) {
- krb5_warnx(context, "sending complete log to fresh slave %s",
- s->name);
- return send_complete (context, s, database, current_version);
+ sp = krb5_storage_from_mem(buf, 4);
+ if (sp == NULL)
+ krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_mem");
+ ret = krb5_store_uint32(sp, YOU_HAVE_LAST_VERSION);
+ krb5_storage_free(sp);
+ data.data = buf;
+ data.length = 4;
+ if (ret == 0) {
+ ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
+ if (ret) {
+ krb5_warn(context, ret, "send_diffs: failed to send to slave");
+ slave_dead(context, s);
+ }
+ krb5_warnx(context, "slave %s in sync already at version %ld",
+ s->name, (long)s->version);
+ }
+ return ret;
}
- sp = kadm5_log_goto_end (log_fd);
+ if (verbose)
+ krb5_warnx(context, "sending diffs to live-seeming slave %s", s->name);
+
+ /*
+ * XXX The code that makes the diffs should be made a separate function,
+ * then error handling (send_are_you_there() or slave_dead()) can be done
+ * here.
+ */
+
+ if (flock(log_fd, LOCK_SH) == -1) {
+ krb5_warn(context, errno, "could not obtain shared lock on log file");
+ send_are_you_there(context, s);
+ return errno;
+ }
+ ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST,
+ &initial_version, &initial_tstamp);
+ sp = kadm5_log_goto_end(server_context, log_fd);
+ flock(log_fd, LOCK_UN);
+ if (ret) {
+ if (sp != NULL)
+ krb5_storage_free(sp);
+ krb5_warn(context, ret, "send_diffs: failed to read log");
+ send_are_you_there(context, s);
+ return ret;
+ }
+ if (sp == NULL) {
+ send_are_you_there(context, s);
+ krb5_warn(context, errno ? errno : EINVAL,
+ "send_diffs: failed to read log");
+ return errno ? errno : EINVAL;
+ }
+ /*
+ * We're not holding any locks here, so we can't prevent truncations.
+ *
+ * We protect against this by re-checking that the initial version and
+ * timestamp are the same before and after this loop.
+ */
right = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (right == (off_t)-1) {
+ krb5_storage_free(sp);
+ send_are_you_there(context, s);
+ return errno;
+ }
for (;;) {
- ret = kadm5_log_previous (context, sp, &ver, &timestamp, &op, &len);
+ ret = kadm5_log_previous (context, sp, &ver, NULL, &op, &len);
if (ret)
- krb5_err(context, 1, ret,
+ krb5_err(context, IPROPD_RESTART, ret,
"send_diffs: failed to find previous entry");
left = krb5_storage_seek(sp, -16, SEEK_CUR);
- if (ver == s->version)
- return 0;
+ if (left == (off_t)-1) {
+ krb5_storage_free(sp);
+ send_are_you_there(context, s);
+ return errno;
+ }
if (ver == s->version + 1)
break;
- if (left == 0) {
+
+ /*
+ * We don't expect to reach the slave's version, except when it is
+ * starting empty with the uber record.
+ */
+ if (ver == s->version && !(ver == 0 && op == kadm_nop)) {
+ /*
+ * This shouldn't happen, but recall we're not holding a lock on
+ * the log.
+ */
+ krb5_storage_free(sp);
+ krb5_warnx(context, "iprop log truncated while sending diffs to "
+ "slave?? ver = %lu", (unsigned long)ver);
+ send_are_you_there(context, s);
+ return 0;
+ }
+
+ /* If we've reached the uber record, send the complete database */
+ if (left == 0 || (ver == 0 && op == kadm_nop)) {
krb5_storage_free(sp);
krb5_warnx(context,
"slave %s (version %lu) out of sync with master "
"(first version in log %lu), sending complete database",
s->name, (unsigned long)s->version, (unsigned long)ver);
- return send_complete (context, s, database, current_version);
+ return send_complete (context, s, database, current_version, ver,
+ initial_tstamp);
}
}
+ assert(ver == s->version + 1);
+
krb5_warnx(context,
"syncing slave %s from version %lu to version %lu",
s->name, (unsigned long)s->version,
@@ -510,19 +801,52 @@ send_diffs (krb5_context context, slave *s, int log_fd,
if (ret) {
krb5_storage_free(sp);
krb5_warn (context, ret, "send_diffs: krb5_data_alloc");
- slave_dead(context, s);
+ send_are_you_there(context, s);
return 1;
}
- krb5_storage_read (sp, (char *)data.data + 4, data.length - 4);
+ bytes = krb5_storage_read(sp, (char *)data.data + 4, data.length - 4);
krb5_storage_free(sp);
+ if (bytes != data.length - 4) {
+ krb5_warnx(context, "iprop log truncated while sending diffs to "
+ "slave?? ver = %lu", (unsigned long)ver);
+ send_are_you_there(context, s);
+ return 1;
+ }
+
+ /*
+ * Check that we have the same log initial version and timestamp now as
+ * when we dropped the shared lock on the log file! Else we could be
+ * sending garbage to the slave.
+ */
+ if (flock(log_fd, LOCK_SH) == -1) {
+ krb5_warn(context, errno, "could not obtain shared lock on log file");
+ send_are_you_there(context, s);
+ return 1;
+ }
+ ret = kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_FIRST,
+ &initial_version2, &initial_tstamp2);
+ flock(log_fd, LOCK_UN);
+ if (ret) {
+ krb5_warn(context, ret,
+ "send_diffs: failed to read log while producing diffs");
+ send_are_you_there(context, s);
+ return 1;
+ }
+ if (initial_version != initial_version2 ||
+ initial_tstamp != initial_tstamp2) {
+ krb5_warn(context, ret,
+ "send_diffs: log truncated while producing diffs");
+ send_are_you_there(context, s);
+ return 1;
+ }
sp = krb5_storage_from_data (&data);
if (sp == NULL) {
krb5_warnx (context, "send_diffs: krb5_storage_from_data");
- slave_dead(context, s);
+ send_are_you_there(context, s);
return 1;
}
- krb5_store_int32 (sp, FOR_YOU);
+ krb5_store_uint32 (sp, FOR_YOU);
krb5_storage_free(sp);
ret = krb5_write_priv_message(context, s->ac, &s->fd, &data);
@@ -537,70 +861,79 @@ send_diffs (krb5_context context, slave *s, int log_fd,
s->version = current_version;
+ krb5_warnx(context, "slave %s is now up to date (%u)", s->name, s->version);
+
return 0;
}
static int
-process_msg (krb5_context context, slave *s, int log_fd,
- const char *database, uint32_t current_version)
+process_msg (kadm5_server_context *server_context, slave *s, int log_fd,
+ const char *database, uint32_t current_version,
+ uint32_t current_tstamp)
{
+ krb5_context context = server_context->context;
int ret = 0;
krb5_data out;
krb5_storage *sp;
- int32_t tmp;
+ uint32_t tmp;
ret = krb5_read_priv_message(context, s->ac, &s->fd, &out);
if(ret) {
- krb5_warn (context, ret, "error reading message from %s", s->name);
+ krb5_warn(context, ret, "error reading message from %s", s->name);
return 1;
}
- sp = krb5_storage_from_mem (out.data, out.length);
+ sp = krb5_storage_from_mem(out.data, out.length);
if (sp == NULL) {
- krb5_warnx (context, "process_msg: no memory");
- krb5_data_free (&out);
+ krb5_warnx(context, "process_msg: no memory");
+ krb5_data_free(&out);
return 1;
}
- if (krb5_ret_int32 (sp, &tmp) != 0) {
- krb5_warnx (context, "process_msg: client send too short command");
- krb5_data_free (&out);
+ if (krb5_ret_uint32(sp, &tmp) != 0) {
+ krb5_warnx(context, "process_msg: client send too short command");
+ krb5_data_free(&out);
return 1;
}
switch (tmp) {
case I_HAVE :
- ret = krb5_ret_int32 (sp, &tmp);
+ ret = krb5_ret_uint32(sp, &tmp);
if (ret != 0) {
- krb5_warnx (context, "process_msg: client send too I_HAVE data");
+ krb5_warnx(context, "process_msg: client send too little I_HAVE data");
break;
}
/* new started slave that have old log */
if (s->version == 0 && tmp != 0) {
- if (current_version < (uint32_t)tmp) {
- krb5_warnx (context, "Slave %s (version %lu) have later version "
- "the master (version %lu) OUT OF SYNC",
- s->name, (unsigned long)tmp,
- (unsigned long)current_version);
+ if (current_version < tmp) {
+ krb5_warnx(context, "Slave %s (version %u) have later version "
+ "the master (version %u) OUT OF SYNC",
+ s->name, tmp, current_version);
}
+ if (verbose)
+ krb5_warnx(context, "slave %s updated from %u to %u",
+ s->name, s->version, tmp);
s->version = tmp;
}
- if ((uint32_t)tmp < s->version) {
- krb5_warnx (context, "Slave claims to not have "
- "version we already sent to it");
- } else {
- ret = send_diffs (context, s, log_fd, database, current_version);
+ if (tmp < s->version) {
+ krb5_warnx(context, "Slave %s claims to not have "
+ "version we already sent to it", s->name);
+ s->version = tmp;
}
- break;
+ ret = send_diffs(server_context, s, log_fd, database, current_version,
+ current_tstamp);
+ break;
case I_AM_HERE :
+ if (verbose)
+ krb5_warnx(context, "slave %s is there", s->name);
break;
case ARE_YOU_THERE:
case FOR_YOU :
default :
- krb5_warnx (context, "Ignoring command %d", tmp);
+ krb5_warnx(context, "Ignoring command %d", tmp);
break;
}
- krb5_data_free (&out);
- krb5_storage_free (sp);
+ krb5_data_free(&out);
+ krb5_storage_free(sp);
slave_seen(s);
@@ -617,25 +950,28 @@ static FILE *
open_stats(krb5_context context)
{
char *statfile = NULL;
- const char *fn;
- FILE *f;
+ const char *fn = NULL;
+ FILE *out = NULL;
+ /*
+ * krb5_config_get_string_default() returs default value as-is,
+ * delay free() of "statfile" until we're done with "fn".
+ */
if (slave_stats_file)
fn = slave_stats_file;
- else {
- asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context));
+ else if (asprintf(&statfile, "%s/slaves-stats", hdb_db_dir(context)) != -1
+ && statfile != NULL)
fn = krb5_config_get_string_default(context,
NULL,
statfile,
"kdc",
"iprop-stats",
NULL);
- }
- f = fopen(fn, "w");
- if (statfile)
+ if (fn != NULL)
+ out = fopen(fn, "w");
+ if (statfile != NULL)
free(statfile);
-
- return f;
+ return out;
}
static void
@@ -720,7 +1056,7 @@ write_stats(krb5_context context, slave *slaves, uint32_t current_version)
}
-static char sHDB[] = "HDB:";
+static char sHDB[] = "HDBGET:";
static char *realm;
static int version_flag;
static int help_flag;
@@ -728,9 +1064,8 @@ static char *keytab_str = sHDB;
static char *database;
static char *config_file;
static char *port_str;
-#ifdef SUPPORT_DETACH
-static int detach_from_console = 0;
-#endif
+static int detach_from_console;
+static int daemon_child = -1;
static struct getargs args[] = {
{ "config-file", 'c', arg_string, &config_file, NULL, NULL },
@@ -746,12 +1081,13 @@ static struct getargs args[] = {
"time of inactivity after which a slave is considered gone", "time"},
{ "port", 0, arg_string, &port_str,
"port ipropd will listen to", "port"},
-#ifdef SUPPORT_DETACH
{ "detach", 0, arg_flag, &detach_from_console,
"detach from console", NULL },
-#endif
+ { "daemon-child", 0 , arg_integer, &daemon_child,
+ "private argument, do not use", NULL },
{ "hostname", 0, arg_string, rk_UNCONST(&master_hostname),
"hostname of master (if not same as hostname)", "hostname" },
+ { "verbose", 0, arg_flag, &verbose, NULL, NULL },
{ "version", 0, arg_flag, &version_flag, NULL, NULL },
{ "help", 0, arg_flag, &help_flag, NULL, NULL }
};
@@ -769,24 +1105,40 @@ main(int argc, char **argv)
int log_fd;
slave *slaves = NULL;
uint32_t current_version = 0, old_version = 0;
+ uint32_t current_tstamp = 0;
krb5_keytab keytab;
- int optidx;
char **files;
+ int aret;
+ int optidx = 0;
+ int restarter_fd = -1;
+ struct stat st;
- optidx = krb5_program_setup(&context, argc, argv, args, num_args, NULL);
+ setprogname(argv[0]);
- if(help_flag)
+ if (getarg(args, num_args, argc, argv, &optidx))
+ krb5_std_usage(1, args, num_args);
+
+ if (help_flag)
krb5_std_usage(0, args, num_args);
- if(version_flag) {
+
+ if (version_flag) {
print_version(NULL);
exit(0);
}
+ if (detach_from_console && daemon_child == -1)
+ roken_detach_prep(argc, argv, "--daemon-child");
+ rk_pidfile(NULL);
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx(1, "krb5_init_context failed: %d", ret);
+
setup_signal();
if (config_file == NULL) {
- asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
- if (config_file == NULL)
+ aret = asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
+ if (aret == -1 || config_file == NULL)
errx(1, "out of memory");
}
@@ -806,15 +1158,10 @@ main(int argc, char **argv)
if (time_before_missing < 0)
krb5_errx (context, 1, "couldn't parse time: %s", slave_time_missing);
-#ifdef SUPPORT_DETACH
- if (detach_from_console)
- daemon(0, 0);
-#endif
- pidfile (NULL);
- krb5_openlog (context, "ipropd-master", &log_facility);
+ krb5_openlog(context, "ipropd-master", &log_facility);
krb5_set_warn_dest(context, log_facility);
- ret = krb5_kt_register(context, &hdb_kt_ops);
+ ret = krb5_kt_register(context, &hdb_get_kt_ops);
if(ret)
krb5_err(context, 1, ret, "krb5_kt_register");
@@ -843,24 +1190,38 @@ main(int argc, char **argv)
krb5_err (context, 1, errno, "open %s",
server_context->log_context.log_file);
+ if (fstat(log_fd, &st) == -1)
+ krb5_err(context, 1, errno, "stat %s",
+ server_context->log_context.log_file);
+
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, 1, errno, "shared flock %s",
+ server_context->log_context.log_file);
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, &current_tstamp);
+ flock(log_fd, LOCK_UN);
+
signal_fd = make_signal_socket (context);
listen_fd = make_listen_socket (context, port_str);
- kadm5_log_get_version_fd (log_fd, &current_version);
-
krb5_warnx(context, "ipropd-master started at version: %lu",
(unsigned long)current_version);
- while(exit_flag == 0){
+ roken_detach_finish(NULL, daemon_child);
+ restarter_fd = restarter(context, NULL);
+
+ while (exit_flag == 0){
slave *p;
fd_set readset;
int max_fd = 0;
struct timeval to = {30, 0};
uint32_t vers;
+ struct stat st2;;
#ifndef NO_LIMIT_FD_SETSIZE
- if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE)
- krb5_errx (context, 1, "fd too large");
+ if (signal_fd >= FD_SETSIZE || listen_fd >= FD_SETSIZE ||
+ restarter_fd >= FD_SETSIZE)
+ krb5_errx (context, IPROPD_RESTART, "fd too large");
#endif
FD_ZERO(&readset);
@@ -868,6 +1229,10 @@ main(int argc, char **argv)
max_fd = max(max_fd, signal_fd);
FD_SET(listen_fd, &readset);
max_fd = max(max_fd, listen_fd);
+ if (restarter_fd > -1) {
+ FD_SET(restarter_fd, &readset);
+ max_fd = max(max_fd, restarter_fd);
+ }
for (p = slaves; p != NULL; p = p->next) {
if (p->flags & SLAVE_F_DEAD)
@@ -882,12 +1247,45 @@ main(int argc, char **argv)
if (errno == EINTR)
continue;
else
- krb5_err (context, 1, errno, "select");
+ krb5_err (context, IPROPD_RESTART, errno, "select");
}
+ if (stat(server_context->log_context.log_file, &st2) == -1) {
+ krb5_warn(context, errno, "could not stat log file by path");
+ st2 = st;
+ }
+
+ if (st2.st_dev != st.st_dev || st2.st_ino != st.st_ino) {
+ (void) close(log_fd);
+
+ log_fd = open(server_context->log_context.log_file, O_RDONLY, 0);
+ if (log_fd < 0)
+ krb5_err(context, 1, IPROPD_RESTART_SLOW, "open %s",
+ server_context->log_context.log_file);
+
+ if (fstat(log_fd, &st) == -1)
+ krb5_err(context, IPROPD_RESTART_SLOW, errno, "stat %s",
+ server_context->log_context.log_file);
+
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, IPROPD_RESTART, errno, "shared flock %s",
+ server_context->log_context.log_file);
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, &current_tstamp);
+ flock(log_fd, LOCK_UN);
+ }
+
if (ret == 0) {
- old_version = current_version;
- kadm5_log_get_version_fd (log_fd, &current_version);
+ /* Recover from failed transactions */
+ if (kadm5_log_init_nb(server_context) == 0)
+ kadm5_log_end(server_context);
+
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, IPROPD_RESTART, errno,
+ "could not lock log file");
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, &current_tstamp);
+ flock(log_fd, LOCK_UN);
if (current_version > old_version) {
krb5_warnx(context,
@@ -897,11 +1295,18 @@ main(int argc, char **argv)
for (p = slaves; p != NULL; p = p->next) {
if (p->flags & SLAVE_F_DEAD)
continue;
- send_diffs (context, p, log_fd, database, current_version);
+ send_diffs (server_context, p, log_fd, database,
+ current_version, current_tstamp);
}
+ old_version = current_version;
}
}
+ if (ret && FD_ISSET(restarter_fd, &readset)) {
+ exit_flag = SIGTERM;
+ break;
+ }
+
if (ret && FD_ISSET(signal_fd, &readset)) {
#ifndef NO_UNIX_SOCKETS
struct sockaddr_un peer_addr;
@@ -918,8 +1323,25 @@ main(int argc, char **argv)
--ret;
assert(ret >= 0);
old_version = current_version;
- kadm5_log_get_version_fd (log_fd, &current_version);
- if (current_version > old_version) {
+ if (flock(log_fd, LOCK_SH) == -1)
+ krb5_err(context, IPROPD_RESTART, errno, "shared flock %s",
+ server_context->log_context.log_file);
+ kadm5_log_get_version_fd(server_context, log_fd, LOG_VERSION_LAST,
+ &current_version, &current_tstamp);
+ flock(log_fd, LOCK_UN);
+ if (current_version != old_version) {
+ /*
+ * If current_version < old_version then the log got
+ * truncated and we'll end up doing full propagations.
+ *
+ * Truncating the log when the current version is
+ * numerically small can lead to race conditions.
+ * Ideally we should identify log versions as
+ * {init_or_trunc_time, vno}, then we could not have any
+ * such race conditions, but this would either require
+ * breaking backwards compatibility for the protocol or
+ * adding new messages to it.
+ */
krb5_warnx(context,
"Got a signal, updating slaves %lu to %lu",
(unsigned long)old_version,
@@ -927,7 +1349,8 @@ main(int argc, char **argv)
for (p = slaves; p != NULL; p = p->next) {
if (p->flags & SLAVE_F_DEAD)
continue;
- send_diffs (context, p, log_fd, database, current_version);
+ send_diffs (server_context, p, log_fd, database,
+ current_version, current_tstamp);
}
} else {
krb5_warnx(context,
@@ -942,7 +1365,8 @@ main(int argc, char **argv)
if (ret && FD_ISSET(p->fd, &readset)) {
--ret;
assert(ret >= 0);
- if(process_msg (context, p, log_fd, database, current_version))
+ if(process_msg (server_context, p, log_fd, database,
+ current_version, current_tstamp))
slave_dead(context, p);
} else if (slave_gone_p (p))
slave_dead(context, p);
diff --git a/lib/kadm5/ipropd_slave.c b/lib/kadm5/ipropd_slave.c
index 38e9a7b7108c..868dc35cbb28 100644
--- a/lib/kadm5/ipropd_slave.c
+++ b/lib/kadm5/ipropd_slave.c
@@ -37,6 +37,8 @@ RCSID("$Id$");
static const char *config_name = "ipropd-slave";
+static int verbose;
+
static krb5_log_facility *log_facility;
static char five_min[] = "5 min";
static char *server_time_lost = five_min;
@@ -51,9 +53,10 @@ connect_to_master (krb5_context context, const char *master,
struct addrinfo *ai, *a;
struct addrinfo hints;
int error;
+ int one = 1;
int s = -1;
- memset (&hints, 0, sizeof(hints));
+ memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
if (port_str == NULL) {
@@ -61,7 +64,7 @@ connect_to_master (krb5_context context, const char *master,
port_str = port;
}
- error = getaddrinfo (master, port_str, &hints, &ai);
+ error = getaddrinfo(master, port_str, &hints, &ai);
if (error) {
krb5_warnx(context, "Failed to get address of to %s: %s",
master, gai_strerror(error));
@@ -75,24 +78,27 @@ connect_to_master (krb5_context context, const char *master,
if (error)
strlcpy(node, "[unknown-addr]", sizeof(node));
- s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
+ s = socket(a->ai_family, a->ai_socktype, a->ai_protocol);
if (s < 0)
continue;
- if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
+ if (connect(s, a->ai_addr, a->ai_addrlen) < 0) {
krb5_warn(context, errno, "connection failed to %s[%s]",
master, node);
- close (s);
+ close(s);
continue;
}
krb5_warnx(context, "connection successful "
"to master: %s[%s]", master, node);
break;
}
- freeaddrinfo (ai);
+ freeaddrinfo(ai);
if (a == NULL)
return -1;
+ if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(one)) < 0)
+ krb5_warn(context, errno, "setsockopt(SO_KEEPALIVE) failed");
+
return s;
}
@@ -107,6 +113,7 @@ get_creds(krb5_context context, const char *keytab_str,
krb5_creds creds;
char *server;
char keytab_buf[256];
+ int aret;
if (keytab_str == NULL) {
ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf));
@@ -127,8 +134,8 @@ get_creds(krb5_context context, const char *keytab_str,
ret = krb5_get_init_creds_opt_alloc(context, &init_opts);
if (ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
- asprintf (&server, "%s/%s", IPROP_NAME, serverhost);
- if (server == NULL)
+ aret = asprintf (&server, "%s/%s", IPROP_NAME, serverhost);
+ if (aret == -1 || server == NULL)
krb5_errx (context, 1, "malloc: no memory");
ret = krb5_get_init_creds_keytab(context, &creds, client, keytab,
@@ -143,7 +150,7 @@ get_creds(krb5_context context, const char *keytab_str,
ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache);
if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique");
- ret = krb5_cc_initialize(context, *cache, client);
+ ret = krb5_cc_initialize(context, *cache, creds.client);
if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
ret = krb5_cc_store_cred(context, *cache, &creds);
@@ -154,200 +161,331 @@ get_creds(krb5_context context, const char *keytab_str,
}
static krb5_error_code
-ihave (krb5_context context, krb5_auth_context auth_context,
- int fd, uint32_t version)
+ihave(krb5_context context, krb5_auth_context auth_context,
+ int fd, uint32_t version)
{
int ret;
u_char buf[8];
krb5_storage *sp;
krb5_data data;
- sp = krb5_storage_from_mem (buf, 8);
- krb5_store_int32 (sp, I_HAVE);
- krb5_store_int32 (sp, version);
- krb5_storage_free (sp);
+ sp = krb5_storage_from_mem(buf, 8);
+ ret = krb5_store_uint32(sp, I_HAVE);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, version);
+ krb5_storage_free(sp);
data.length = 8;
data.data = buf;
- ret = krb5_write_priv_message(context, auth_context, &fd, &data);
- if (ret)
- krb5_warn (context, ret, "krb5_write_message");
+ if (ret == 0) {
+ if (verbose)
+ krb5_warnx(context, "telling master we are at %u", version);
+
+ ret = krb5_write_priv_message(context, auth_context, &fd, &data);
+ if (ret)
+ krb5_warn(context, ret, "krb5_write_message");
+ }
return ret;
}
-static void
+#ifndef EDQUOT
+/* There's no EDQUOT on WIN32, for example */
+#define EDQUOT ENOSPC
+#endif
+
+static int
+append_to_log_file(krb5_context context,
+ kadm5_server_context *server_context,
+ krb5_storage *sp, off_t start, ssize_t slen)
+{
+ size_t len;
+ ssize_t sret;
+ off_t log_off;
+ int ret, ret2;
+ void *buf;
+
+ if (verbose)
+ krb5_warnx(context, "appending diffs to log");
+
+ if (slen == 0)
+ return 0;
+ if (slen < 0)
+ return EINVAL;
+ len = slen;
+ if (len != slen)
+ return EOVERFLOW;
+
+ buf = malloc(len);
+ if (buf == NULL && len != 0) {
+ krb5_warn(context, errno, "malloc: no memory");
+ return ENOMEM;
+ }
+
+ if (krb5_storage_seek(sp, start, SEEK_SET) != start) {
+ krb5_errx(context, IPROPD_RESTART,
+ "krb5_storage_seek() failed"); /* can't happen */
+ }
+ sret = krb5_storage_read(sp, buf, len);
+ if (sret < 0)
+ return errno;
+ if (len != (size_t)sret) {
+ /* Can't happen */
+ krb5_errx(context, IPROPD_RESTART,
+ "short krb5_storage_read() from memory buffer");
+ }
+ log_off = lseek(server_context->log_context.log_fd, 0, SEEK_CUR);
+ if (log_off == -1)
+ return errno;
+
+ /*
+ * Use net_write() so we get an errno if less that len bytes were
+ * written.
+ */
+ sret = net_write(server_context->log_context.log_fd, buf, len);
+ free(buf);
+ if (sret != slen)
+ ret = errno;
+ else
+ ret = fsync(server_context->log_context.log_fd);
+ if (ret == 0)
+ return 0;
+
+ /*
+ * Attempt to recover from this. First, truncate the log file
+ * and reset the fd offset. Failure to do this -> unlink the
+ * log file and re-create it. Since we're the slave, we ought to be
+ * able to recover from the log being unlinked...
+ */
+ if (ftruncate(server_context->log_context.log_fd, log_off) == -1 ||
+ lseek(server_context->log_context.log_fd, log_off, SEEK_SET) == -1) {
+ (void) kadm5_log_end(server_context);
+ if (unlink(server_context->log_context.log_file) == -1) {
+ krb5_err(context, IPROPD_FATAL, errno,
+ "Failed to recover from failure to write log "
+ "entries from master to disk");
+ }
+ ret2 = kadm5_log_init(server_context);
+ if (ret2) {
+ krb5_err(context, IPROPD_RESTART_SLOW, ret2,
+ "Failed to initialize log to recover from "
+ "failure to write log entries from master to disk");
+ }
+ }
+ if (ret == ENOSPC || ret == EDQUOT || ret == EFBIG) {
+ /* Unlink the file in these cases. */
+ krb5_warn(context, IPROPD_RESTART_SLOW,
+ "Failed to write log entries from master to disk");
+ (void) kadm5_log_end(server_context);
+ if (unlink(server_context->log_context.log_file) == -1) {
+ krb5_err(context, IPROPD_FATAL, errno,
+ "Failed to recover from failure to write log "
+ "entries from master to disk");
+ }
+ ret2 = kadm5_log_init(server_context);
+ if (ret2) {
+ krb5_err(context, IPROPD_RESTART_SLOW, ret2,
+ "Failed to initialize log to recover from "
+ "failure to write log entries from master to disk");
+ }
+ return ret;
+ }
+ /*
+ * All other errors we treat as fatal here. This includes, for
+ * example, EIO and EPIPE (sorry, can't log to pipes nor sockets).
+ */
+ krb5_err(context, IPROPD_FATAL, ret,
+ "Failed to write log entries from master to disk");
+}
+
+static int
receive_loop (krb5_context context,
krb5_storage *sp,
kadm5_server_context *server_context)
{
int ret;
- off_t left, right;
- void *buf;
- int32_t vers, vers2;
- ssize_t sret;
+ off_t left, right, off;
+ uint32_t len, vers;
+
+ if (verbose)
+ krb5_warnx(context, "receiving diffs");
/*
- * Seek to the current version of the local database.
+ * Seek to the first entry in the message from the master that is
+ * past the current version of the local database.
*/
do {
- int32_t len, timestamp, tmp;
- enum kadm_ops op;
-
- if(krb5_ret_int32 (sp, &vers) != 0)
- return;
- krb5_ret_int32 (sp, &timestamp);
- krb5_ret_int32 (sp, &tmp);
- op = tmp;
- krb5_ret_int32 (sp, &len);
- if ((uint32_t)vers <= server_context->log_context.version)
- krb5_storage_seek(sp, len + 8, SEEK_CUR);
- } while((uint32_t)vers <= server_context->log_context.version);
+ uint32_t timestamp;
+ uint32_t op;
+
+ if ((ret = krb5_ret_uint32(sp, &vers)) == HEIM_ERR_EOF) {
+ krb5_warnx(context, "master sent no new iprop entries");
+ return 0;
+ }
+
+ /*
+ * TODO We could do more to validate the entries from the master
+ * here. And we could use/reuse more kadm5_log_*() code here.
+ *
+ * Alternatively we should trust that the master sent us exactly
+ * what we needed and just write this to the log file and let
+ * kadm5_log_recover() do the rest.
+ */
+ if (ret || krb5_ret_uint32(sp, &timestamp) != 0 ||
+ krb5_ret_uint32(sp, &op) != 0 ||
+ krb5_ret_uint32(sp, &len) != 0) {
+
+ /*
+ * This shouldn't happen. Reconnecting probably won't help
+ * if it does happen, but by reconnecting we get a chance to
+ * connect to a new master if a new one is configured.
+ */
+ krb5_warnx(context, "iprop entries from master were truncated");
+ return EINVAL;
+ }
+ if (vers > server_context->log_context.version) {
+ break;
+ }
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (krb5_storage_seek(sp, len + 8, SEEK_CUR) != off + len + 8) {
+ krb5_warnx(context, "iprop entries from master were truncated");
+ return EINVAL;
+ }
+ if (verbose) {
+ krb5_warnx(context, "diff contains old log record version "
+ "%u %lld %u length %u",
+ vers, (long long)timestamp, op, len);
+ }
+ } while(vers <= server_context->log_context.version);
/*
- * Read up rest of the entires into the memory...
+ * Read the remaining entries into memory...
*/
- left = krb5_storage_seek (sp, -16, SEEK_CUR);
- right = krb5_storage_seek (sp, 0, SEEK_END);
- buf = malloc (right - left);
- if (buf == NULL && (right - left) != 0)
- krb5_errx (context, 1, "malloc: no memory");
+ /* SEEK_CUR is a header into the first entry we care about */
+ left = krb5_storage_seek(sp, -16, SEEK_CUR);
+ right = krb5_storage_seek(sp, 0, SEEK_END);
+ if (right - left < 24 + len) {
+ krb5_warnx(context, "iprop entries from master were truncated");
+ return EINVAL;
+ }
/*
* ...and then write them out to the on-disk log.
*/
- krb5_storage_seek (sp, left, SEEK_SET);
- krb5_storage_read (sp, buf, right - left);
- sret = write (server_context->log_context.log_fd, buf, right-left);
- if (sret != right - left)
- krb5_err(context, 1, errno, "Failed to write log to disk");
- ret = fsync (server_context->log_context.log_fd);
+
+ ret = append_to_log_file(context, server_context, sp, left, right - left);
if (ret)
- krb5_err(context, 1, errno, "Failed to sync log to disk");
- free (buf);
+ return ret;
/*
- * Go back to the startpoint and start to commit the entires to
- * the database.
+ * Replay the new entries.
*/
- krb5_storage_seek (sp, left, SEEK_SET);
-
- for(;;) {
- int32_t len, len2, timestamp, tmp;
- off_t cur, cur2;
- enum kadm_ops op;
-
- if(krb5_ret_int32 (sp, &vers) != 0)
- break;
- ret = krb5_ret_int32 (sp, &timestamp);
- if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
- ret = krb5_ret_int32 (sp, &tmp);
- if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
- op = tmp;
- ret = krb5_ret_int32 (sp, &len);
- if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
- if (len < 0)
- krb5_errx(context, 1, "log is corrupted, "
- "negative length of entry version %ld: %ld",
- (long)vers, (long)len);
- cur = krb5_storage_seek(sp, 0, SEEK_CUR);
-
- krb5_warnx (context, "replaying entry %d", (int)vers);
-
- ret = kadm5_log_replay (server_context,
- op, vers, len, sp);
- if (ret) {
- const char *s = krb5_get_error_message(server_context->context, ret);
- krb5_warnx (context,
- "kadm5_log_replay: %ld. Lost entry entry, "
- "Database out of sync ?: %s (%d)",
- (long)vers, s ? s : "unknown error", ret);
- krb5_free_error_message(context, s);
- }
-
- {
- /*
- * Make sure the krb5_log_replay does the right thing wrt
- * reading out data from the sp.
- */
- cur2 = krb5_storage_seek(sp, 0, SEEK_CUR);
- if (cur + len != cur2)
- krb5_errx(context, 1,
- "kadm5_log_reply version: %ld didn't read the whole entry",
- (long)vers);
- }
-
- if (krb5_ret_int32 (sp, &len2) != 0)
- krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers);
- if(krb5_ret_int32 (sp, &vers2) != 0)
- krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers);
+ if (verbose)
+ krb5_warnx(context, "replaying entries from master");
+ ret = kadm5_log_recover(server_context, kadm_recover_replay);
+ if (ret) {
+ krb5_warn(context, ret, "replay failed");
+ return ret;
+ }
- if (len != len2)
- krb5_errx(context, 1, "entry %ld: len != len2", (long)vers);
- if (vers != vers2)
- krb5_errx(context, 1, "entry %ld: vers != vers2", (long)vers);
+ ret = kadm5_log_get_version(server_context, &vers);
+ if (ret) {
+ krb5_warn(context, ret,
+ "could not get log version after applying diffs!");
+ return ret;
}
+ if (verbose)
+ krb5_warnx(context, "slave at version %u", vers);
- /*
- * Update version
- */
+ if (vers != server_context->log_context.version) {
+ krb5_warnx(context, "slave's log_context version (%u) is "
+ "inconsistent with log's version (%u)",
+ server_context->log_context.version, vers);
+ }
- server_context->log_context.version = vers;
+ return 0;
}
-static void
-receive (krb5_context context,
- krb5_storage *sp,
- kadm5_server_context *server_context)
+static int
+receive(krb5_context context,
+ krb5_storage *sp,
+ kadm5_server_context *server_context)
{
- int ret;
+ krb5_error_code ret, ret2;
ret = server_context->db->hdb_open(context,
server_context->db,
O_RDWR | O_CREAT, 0600);
if (ret)
- krb5_err (context, 1, ret, "db->open");
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->open");
- receive_loop (context, sp, server_context);
+ ret2 = receive_loop(context, sp, server_context);
+ if (ret2)
+ krb5_warn(context, ret2, "receive from ipropd-master had errors");
- ret = server_context->db->hdb_close (context, server_context->db);
+ ret = server_context->db->hdb_close(context, server_context->db);
if (ret)
- krb5_err (context, 1, ret, "db->close");
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
+
+ return ret2;
}
static void
-send_im_here (krb5_context context, int fd,
- krb5_auth_context auth_context)
+send_im_here(krb5_context context, int fd,
+ krb5_auth_context auth_context)
{
krb5_storage *sp;
krb5_data data;
- int ret;
+ krb5_error_code ret;
- ret = krb5_data_alloc (&data, 4);
+ ret = krb5_data_alloc(&data, 4);
if (ret)
- krb5_err (context, 1, ret, "send_im_here");
+ krb5_err(context, IPROPD_RESTART, ret, "send_im_here");
sp = krb5_storage_from_data (&data);
if (sp == NULL)
- krb5_errx (context, 1, "krb5_storage_from_data");
- krb5_store_int32(sp, I_AM_HERE);
+ krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_data");
+ ret = krb5_store_uint32(sp, I_AM_HERE);
krb5_storage_free(sp);
- ret = krb5_write_priv_message(context, auth_context, &fd, &data);
- krb5_data_free(&data);
+ if (ret == 0) {
+ ret = krb5_write_priv_message(context, auth_context, &fd, &data);
+ krb5_data_free(&data);
+
+ if (ret)
+ krb5_err(context, IPROPD_RESTART, ret, "krb5_write_priv_message");
+
+ if (verbose)
+ krb5_warnx(context, "pinged master");
+ }
+
+ return;
+}
+static void
+reinit_log(krb5_context context,
+ kadm5_server_context *server_context,
+ uint32_t vno)
+{
+ krb5_error_code ret;
+
+ if (verbose)
+ krb5_warnx(context, "truncating log on slave");
+
+ ret = kadm5_log_reinit(server_context, vno);
if (ret)
- krb5_err (context, 1, ret, "krb5_write_priv_message");
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "kadm5_log_reinit");
}
+
static krb5_error_code
-receive_everything (krb5_context context, int fd,
- kadm5_server_context *server_context,
- krb5_auth_context auth_context)
+receive_everything(krb5_context context, int fd,
+ kadm5_server_context *server_context,
+ krb5_auth_context auth_context)
{
int ret;
krb5_data data;
- int32_t vno = 0;
- int32_t opcode;
+ uint32_t vno = 0;
+ uint32_t opcode;
krb5_storage *sp;
char *dbname;
@@ -355,36 +493,39 @@ receive_everything (krb5_context context, int fd,
krb5_warnx(context, "receive complete database");
- asprintf(&dbname, "%s-NEW", server_context->db->hdb_name);
+ ret = asprintf(&dbname, "%s-NEW", server_context->db->hdb_name);
+ if (ret == -1)
+ krb5_err(context, IPROPD_RESTART, ENOMEM, "asprintf");
ret = hdb_create(context, &mydb, dbname);
if(ret)
- krb5_err(context,1, ret, "hdb_create");
+ krb5_err(context, IPROPD_RESTART, ret, "hdb_create");
free(dbname);
- ret = hdb_set_master_keyfile (context,
- mydb, server_context->config.stash_file);
+ ret = hdb_set_master_keyfile(context,
+ mydb, server_context->config.stash_file);
if(ret)
- krb5_err(context,1, ret, "hdb_set_master_keyfile");
+ krb5_err(context, IPROPD_RESTART, ret, "hdb_set_master_keyfile");
/* I really want to use O_EXCL here, but given that I can't easily clean
up on error, I won't */
ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600);
if (ret)
- krb5_err (context, 1, ret, "db->open");
+ krb5_err(context, IPROPD_RESTART, ret, "db->open");
sp = NULL;
+ krb5_data_zero(&data);
do {
ret = krb5_read_priv_message(context, auth_context, &fd, &data);
if (ret) {
- krb5_warn (context, ret, "krb5_read_priv_message");
+ krb5_warn(context, ret, "krb5_read_priv_message");
goto cleanup;
}
- sp = krb5_storage_from_data (&data);
+ sp = krb5_storage_from_data(&data);
if (sp == NULL)
- krb5_errx (context, 1, "krb5_storage_from_data");
- krb5_ret_int32 (sp, &opcode);
+ krb5_errx(context, IPROPD_RESTART, "krb5_storage_from_data");
+ krb5_ret_uint32(sp, &opcode);
if (opcode == ONE_PRINC) {
krb5_data fake_data;
hdb_entry_ex entry;
@@ -396,69 +537,116 @@ receive_everything (krb5_context context, int fd,
memset(&entry, 0, sizeof(entry));
- ret = hdb_value2entry (context, &fake_data, &entry.entry);
+ ret = hdb_value2entry(context, &fake_data, &entry.entry);
if (ret)
- krb5_err (context, 1, ret, "hdb_value2entry");
+ krb5_err(context, IPROPD_RESTART, ret, "hdb_value2entry");
ret = mydb->hdb_store(server_context->context,
mydb,
0, &entry);
if (ret)
- krb5_err (context, 1, ret, "hdb_store");
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "hdb_store");
- hdb_free_entry (context, &entry);
- krb5_data_free (&data);
+ hdb_free_entry(context, &entry);
+ krb5_data_free(&data);
} else if (opcode == NOW_YOU_HAVE)
;
else
- krb5_errx (context, 1, "strange opcode %d", opcode);
+ krb5_errx(context, 1, "strange opcode %d", opcode);
} while (opcode == ONE_PRINC);
if (opcode != NOW_YOU_HAVE)
- krb5_errx (context, 1, "receive_everything: strange %d", opcode);
+ krb5_errx(context, IPROPD_RESTART_SLOW,
+ "receive_everything: strange %d", opcode);
- krb5_ret_int32 (sp, &vno);
+ krb5_ret_uint32(sp, &vno);
krb5_storage_free(sp);
- ret = kadm5_log_reinit (server_context);
- if (ret)
- krb5_err(context, 1, ret, "kadm5_log_reinit");
+ reinit_log(context, server_context, vno);
- ret = kadm5_log_set_version (server_context, vno - 1);
+ ret = mydb->hdb_close(context, mydb);
if (ret)
- krb5_err (context, 1, ret, "kadm5_log_set_version");
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
- ret = kadm5_log_nop (server_context);
+ ret = mydb->hdb_rename(context, mydb, server_context->db->hdb_name);
if (ret)
- krb5_err (context, 1, ret, "kadm5_log_nop");
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->rename");
- ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name);
- if (ret)
- krb5_err (context, 1, ret, "db->rename");
+
+ return 0;
cleanup:
- krb5_data_free (&data);
+ krb5_data_free(&data);
- ret = mydb->hdb_close (context, mydb);
if (ret)
- krb5_err (context, 1, ret, "db->close");
+ krb5_err(context, IPROPD_RESTART_SLOW, ret, "db->close");
- ret = mydb->hdb_destroy (context, mydb);
+ ret = mydb->hdb_destroy(context, mydb);
if (ret)
- krb5_err (context, 1, ret, "db->destroy");
+ krb5_err(context, IPROPD_RESTART, ret, "db->destroy");
krb5_warnx(context, "receive complete database, version %ld", (long)vno);
return ret;
}
+static void
+slave_status(krb5_context context,
+ const char *file,
+ const char *status, ...)
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+
+static void
+slave_status(krb5_context context,
+ const char *file,
+ const char *fmt, ...)
+{
+ char *status;
+ char *fmt2;
+ va_list args;
+ int len;
+
+ if (asprintf(&fmt2, "%s\n", fmt) == -1 || fmt2 == NULL) {
+ (void) unlink(file);
+ return;
+ }
+ va_start(args, fmt);
+ len = vasprintf(&status, fmt2, args);
+ free(fmt2);
+ va_end(args);
+ if (len < 0 || status == NULL) {
+ (void) unlink(file);
+ return;
+ }
+ krb5_warnx(context, "slave status change: %s", status);
+
+ rk_dumpdata(file, status, len);
+ free(status);
+}
+
+static void
+is_up_to_date(krb5_context context, const char *file,
+ kadm5_server_context *server_context)
+{
+ krb5_error_code ret;
+ char buf[80];
+ ret = krb5_format_time(context, time(NULL), buf, sizeof(buf), 1);
+ if (ret) {
+ unlink(file);
+ return;
+ }
+ slave_status(context, file, "up-to-date with version: %lu at %s",
+ (unsigned long)server_context->log_context.version, buf);
+}
+
+static char *status_file;
static char *config_file;
static char *realm;
static int version_flag;
static int help_flag;
static char *keytab_str;
static char *port_str;
-#ifdef SUPPORT_DETACH
-static int detach_from_console = 0;
-#endif
+static int detach_from_console;
+static int daemon_child = -1;
static struct getargs args[] = {
{ "config-file", 'c', arg_string, &config_file, NULL, NULL },
@@ -467,14 +655,17 @@ static struct getargs args[] = {
"keytab to get authentication from", "kspec" },
{ "time-lost", 0, arg_string, &server_time_lost,
"time before server is considered lost", "time" },
+ { "status-file", 0, arg_string, &status_file,
+ "file to write out status into", "file" },
{ "port", 0, arg_string, &port_str,
"port ipropd-slave will connect to", "port"},
-#ifdef SUPPORT_DETACH
{ "detach", 0, arg_flag, &detach_from_console,
"detach from console", NULL },
-#endif
+ { "daemon-child", 0 , arg_integer, &daemon_child,
+ "private argument, do not use", NULL },
{ "hostname", 0, arg_string, rk_UNCONST(&slave_str),
"hostname of slave (if not same as hostname)", "hostname" },
+ { "verbose", 0, arg_flag, &verbose, NULL, NULL },
{ "version", 0, arg_flag, &version_flag, NULL, NULL },
{ "help", 0, arg_flag, &help_flag, NULL, NULL }
};
@@ -491,7 +682,7 @@ usage(int status)
int
main(int argc, char **argv)
{
- krb5_error_code ret;
+ krb5_error_code ret, ret2;
krb5_context context;
krb5_auth_context auth_context;
void *kadm_handle;
@@ -507,21 +698,27 @@ main(int argc, char **argv)
time_t reconnect_max;
time_t reconnect;
time_t before = 0;
+ int restarter_fd = -1;
const char *master;
setprogname(argv[0]);
- if(getarg(args, num_args, argc, argv, &optidx))
+ if (getarg(args, num_args, argc, argv, &optidx))
usage(1);
- if(help_flag)
+ if (help_flag)
usage(0);
- if(version_flag) {
+
+ if (version_flag) {
print_version(NULL);
exit(0);
}
+ if (detach_from_console && daemon_child == -1)
+ roken_detach_prep(argc, argv, "--daemon-child");
+ rk_pidfile(NULL);
+
ret = krb5_init_context(&context);
if (ret)
errx (1, "krb5_init_context failed: %d", ret);
@@ -551,15 +748,17 @@ main(int argc, char **argv)
master = argv[0];
-#ifdef SUPPORT_DETACH
- if (detach_from_console)
- daemon(0, 0);
-#endif
- pidfile (NULL);
- krb5_openlog (context, "ipropd-slave", &log_facility);
+ if (status_file == NULL) {
+ if (asprintf(&status_file, "%s/ipropd-slave-status", hdb_db_dir(context)) < 0 || status_file == NULL)
+ krb5_errx(context, 1, "can't allocate status file buffer");
+ }
+
+ krb5_openlog(context, "ipropd-slave", &log_facility);
krb5_set_warn_dest(context, log_facility);
- ret = krb5_kt_register(context, &hdb_kt_ops);
+ slave_status(context, status_file, "bootstrapping");
+
+ ret = krb5_kt_register(context, &hdb_get_kt_ops);
if(ret)
krb5_err(context, 1, ret, "krb5_kt_register");
@@ -567,6 +766,8 @@ main(int argc, char **argv)
if (time_before_lost < 0)
krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost);
+ slave_status(context, status_file, "getting credentials from keytab/database");
+
memset(&conf, 0, sizeof(conf));
if(realm) {
conf.mask |= KADM5_CONFIG_REALM;
@@ -583,6 +784,8 @@ main(int argc, char **argv)
server_context = (kadm5_server_context *)kadm_handle;
+ slave_status(context, status_file, "creating log file");
+
ret = kadm5_log_init (server_context);
if (ret)
krb5_err (context, 1, ret, "kadm5_log_init");
@@ -605,10 +808,26 @@ main(int argc, char **argv)
10, &backoff);
reconnect = reconnect_min;
+ slave_status(context, status_file, "ipropd-slave started");
+
+ roken_detach_finish(NULL, daemon_child);
+ restarter_fd = restarter(context, NULL);
+
while (!exit_flag) {
+ struct timeval to;
time_t now, elapsed;
+ fd_set readset;
int connected = FALSE;
+#ifndef NO_LIMIT_FD_SETSIZE
+ if (restarter_fd >= FD_SETSIZE)
+ krb5_errx(context, IPROPD_RESTART, "fd too large");
+#endif
+
+ FD_ZERO(&readset);
+ if (restarter_fd > -1)
+ FD_SET(restarter_fd, &readset);
+
now = time(NULL);
elapsed = now - before;
@@ -616,10 +835,17 @@ main(int argc, char **argv)
time_t left = reconnect - elapsed;
krb5_warnx(context, "sleeping %d seconds before "
"retrying to connect", (int)left);
- sleep(left);
+ to.tv_sec = left;
+ to.tv_usec = 0;
+ if (select(restarter_fd + 1, &readset, NULL, NULL, &to) == 1) {
+ exit_flag = SIGTERM;
+ continue;
+ }
}
before = now;
+ slave_status(context, status_file, "connecting to master: %s\n", master);
+
master_fd = connect_to_master (context, master, port_str);
if (master_fd < 0)
goto retry;
@@ -632,6 +858,8 @@ main(int argc, char **argv)
krb5_cc_destroy(context, ccache);
get_creds(context, keytab_str, &ccache, master);
}
+ if (verbose)
+ krb5_warnx(context, "authenticating to master");
ret = krb5_sendauth (context, &auth_context, &master_fd,
IPROP_VERSION, NULL, server,
AP_OPTS_MUTUAL_REQUIRED, NULL, NULL,
@@ -644,32 +872,41 @@ main(int argc, char **argv)
krb5_warnx(context, "ipropd-slave started at version: %ld",
(long)server_context->log_context.version);
- ret = ihave (context, auth_context, master_fd,
- server_context->log_context.version);
+ ret = ihave(context, auth_context, master_fd,
+ server_context->log_context.version);
if (ret)
goto retry;
connected = TRUE;
+ if (verbose)
+ krb5_warnx(context, "connected to master");
+
+ slave_status(context, status_file, "connected to master, waiting instructions");
+
while (connected && !exit_flag) {
krb5_data out;
krb5_storage *sp;
- int32_t tmp;
- fd_set readset;
- struct timeval to;
+ uint32_t tmp;
+ int max_fd;
#ifndef NO_LIMIT_FD_SETSIZE
if (master_fd >= FD_SETSIZE)
- krb5_errx (context, 1, "fd too large");
+ krb5_errx(context, IPROPD_RESTART, "fd too large");
+ if (restarter_fd >= FD_SETSIZE)
+ krb5_errx(context, IPROPD_RESTART, "fd too large");
+ max_fd = max(restarter_fd, master_fd);
#endif
FD_ZERO(&readset);
FD_SET(master_fd, &readset);
+ if (restarter_fd != -1)
+ FD_SET(restarter_fd, &readset);
to.tv_sec = time_before_lost;
to.tv_usec = 0;
- ret = select (master_fd + 1,
+ ret = select (max_fd + 1,
&readset, NULL, NULL, &to);
if (ret < 0) {
if (errno == EINTR)
@@ -677,35 +914,101 @@ main(int argc, char **argv)
else
krb5_err (context, 1, errno, "select");
}
- if (ret == 0)
- krb5_errx (context, 1, "server didn't send a message "
- "in %d seconds", time_before_lost);
+ if (ret == 0) {
+ krb5_warnx(context, "server didn't send a message "
+ "in %d seconds", time_before_lost);
+ connected = FALSE;
+ continue;
+ }
+
+ if (restarter_fd > -1 && FD_ISSET(restarter_fd, &readset)) {
+ if (verbose)
+ krb5_warnx(context, "slave restarter exited");
+ exit_flag = SIGTERM;
+ }
+
+ if (!FD_ISSET(master_fd, &readset))
+ continue;
+
+ if (verbose)
+ krb5_warnx(context, "message from master");
ret = krb5_read_priv_message(context, auth_context, &master_fd, &out);
if (ret) {
- krb5_warn (context, ret, "krb5_read_priv_message");
+ krb5_warn(context, ret, "krb5_read_priv_message");
connected = FALSE;
continue;
}
sp = krb5_storage_from_mem (out.data, out.length);
- krb5_ret_int32 (sp, &tmp);
+ if (sp == NULL)
+ krb5_err(context, IPROPD_RESTART, errno, "krb5_storage_from_mem");
+ ret = krb5_ret_uint32(sp, &tmp);
+ if (ret == HEIM_ERR_EOF) {
+ krb5_warn(context, ret, "master sent zero-length message");
+ connected = FALSE;
+ continue;
+ }
+ if (ret != 0) {
+ krb5_warn(context, ret, "couldn't read master's message");
+ connected = FALSE;
+ continue;
+ }
+
+ ret = kadm5_log_init(server_context);
+ if (ret) {
+ krb5_err(context, IPROPD_RESTART, ret, "kadm5_log_init while "
+ "handling a message from the master");
+ }
switch (tmp) {
case FOR_YOU :
- receive (context, sp, server_context);
- ret = ihave (context, auth_context, master_fd,
- server_context->log_context.version);
- if (ret)
+ if (verbose)
+ krb5_warnx(context, "master sent us diffs");
+ ret2 = receive(context, sp, server_context);
+ if (ret2)
+ krb5_warn(context, ret2,
+ "receive from ipropd-master had errors");
+ ret = ihave(context, auth_context, master_fd,
+ server_context->log_context.version);
+ if (ret || ret2)
connected = FALSE;
+
+ /*
+ * If it returns an error, receive() may nonetheless
+ * have committed some entries successfully, so we must
+ * update the slave_status even if there were errors.
+ */
+ is_up_to_date(context, status_file, server_context);
break;
case TELL_YOU_EVERYTHING :
- ret = receive_everything (context, master_fd, server_context,
- auth_context);
- if (ret)
+ if (verbose)
+ krb5_warnx(context, "master sent us a full dump");
+ ret = receive_everything(context, master_fd, server_context,
+ auth_context);
+ if (ret == 0) {
+ ret = ihave(context, auth_context, master_fd,
+ server_context->log_context.version);
+ }
+ if (ret)
connected = FALSE;
+ else
+ is_up_to_date(context, status_file, server_context);
break;
case ARE_YOU_THERE :
- send_im_here (context, master_fd, auth_context);
+ if (verbose)
+ krb5_warnx(context, "master sent us a ping");
+ is_up_to_date(context, status_file, server_context);
+ ret = ihave(context, auth_context, master_fd,
+ server_context->log_context.version);
+ if (ret)
+ connected = FALSE;
+
+ send_im_here(context, master_fd, auth_context);
+ break;
+ case YOU_HAVE_LAST_VERSION:
+ if (verbose)
+ krb5_warnx(context, "master tells us we are up to date");
+ is_up_to_date(context, status_file, server_context);
break;
case NOW_YOU_HAVE :
case I_HAVE :
@@ -719,9 +1022,12 @@ main(int argc, char **argv)
krb5_data_free (&out);
}
+
+ slave_status(context, status_file, "disconnected from master");
retry:
if (connected == FALSE)
krb5_warnx (context, "disconnected for server");
+
if (exit_flag)
krb5_warnx (context, "got an exit signal");
@@ -729,8 +1035,15 @@ main(int argc, char **argv)
close(master_fd);
reconnect += backoff;
- if (reconnect > reconnect_max)
+ if (reconnect > reconnect_max) {
+ slave_status(context, status_file, "disconnected from master for a long time");
reconnect = reconnect_max;
+ }
+ }
+
+ if (status_file) {
+ /* XXX It'd be better to leave it saying we're not here */
+ unlink(status_file);
}
if (0);
diff --git a/lib/kadm5/kadm5-private.h b/lib/kadm5/kadm5-private.h
index ab8d694dda74..3f102fd89878 100644
--- a/lib/kadm5/kadm5-private.h
+++ b/lib/kadm5/kadm5-private.h
@@ -52,11 +52,10 @@ kadm5_ret_t
_kadm5_error_code (kadm5_ret_t /*code*/);
int
-_kadm5_exists_keys (
+_kadm5_exists_keys_hist (
Key */*keys1*/,
int /*len1*/,
- Key */*keys2*/,
- int /*len2*/);
+ HDB_Ext_KeySet */*hist_keys*/);
void
_kadm5_free_keys (
@@ -94,6 +93,8 @@ kadm5_ret_t
_kadm5_set_keys (
kadm5_server_context */*context*/,
hdb_entry */*ent*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
const char */*password*/);
kadm5_ret_t
@@ -114,6 +115,8 @@ kadm5_ret_t
_kadm5_set_keys_randomly (
kadm5_server_context */*context*/,
hdb_entry */*ent*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
krb5_keyblock **/*new_keys*/,
int */*n_keys*/);
@@ -147,12 +150,16 @@ kadm5_ret_t
kadm5_c_chpass_principal (
void */*server_handle*/,
krb5_principal /*princ*/,
+ int /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
const char */*password*/);
kadm5_ret_t
kadm5_c_chpass_principal_with_key (
void */*server_handle*/,
krb5_principal /*princ*/,
+ int /*keepold*/,
int /*n_key_data*/,
krb5_key_data */*key_data*/);
@@ -161,6 +168,8 @@ kadm5_c_create_principal (
void */*server_handle*/,
kadm5_principal_ent_t /*princ*/,
uint32_t /*mask*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
const char */*password*/);
kadm5_ret_t
@@ -266,6 +275,9 @@ kadm5_ret_t
kadm5_c_randkey_principal (
void */*server_handle*/,
krb5_principal /*princ*/,
+ krb5_boolean /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
krb5_keyblock **/*new_keys*/,
int */*n_keys*/);
@@ -278,7 +290,7 @@ kadm5_c_rename_principal (
kadm5_ret_t
kadm5_log_create (
kadm5_server_context */*context*/,
- hdb_entry */*ent*/);
+ hdb_entry */*entry*/);
kadm5_ret_t
kadm5_log_delete (
@@ -286,56 +298,83 @@ kadm5_log_delete (
krb5_principal /*princ*/);
kadm5_ret_t
-kadm5_log_end (kadm5_server_context */*context*/);
+kadm5_log_end (kadm5_server_context */*server_context*/);
kadm5_ret_t
kadm5_log_foreach (
kadm5_server_context */*context*/,
- void (*/*func*/)(kadm5_server_context *server_context, uint32_t ver, time_t timestamp, enum kadm_ops op, uint32_t len, krb5_storage *, void *),
+ enum kadm_iter_opts /*iter_opts*/,
+ off_t */*off_lastp*/,
+ kadm5_ret_t (*/*func*/)(kadm5_server_context *server_context, uint32_t ver, time_t timestamp, enum kadm_ops op, uint32_t len, krb5_storage *sp, void *ctx),
void */*ctx*/);
kadm5_ret_t
kadm5_log_get_version (
- kadm5_server_context */*context*/,
+ kadm5_server_context */*server_context*/,
uint32_t */*ver*/);
kadm5_ret_t
kadm5_log_get_version_fd (
+ kadm5_server_context */*server_context*/,
int /*fd*/,
- uint32_t */*ver*/);
+ int /*which*/,
+ uint32_t */*ver*/,
+ uint32_t */*tstamp*/);
krb5_storage *
-kadm5_log_goto_end (int /*fd*/);
+kadm5_log_goto_end (
+ kadm5_server_context */*server_context*/,
+ int /*fd*/);
kadm5_ret_t
-kadm5_log_init (kadm5_server_context */*context*/);
+kadm5_log_init (kadm5_server_context */*server_context*/);
+
+kadm5_ret_t
+kadm5_log_init_nb (kadm5_server_context */*server_context*/);
+
+kadm5_ret_t
+kadm5_log_init_nolock (kadm5_server_context */*server_context*/);
+
+kadm5_ret_t
+kadm5_log_init_sharedlock (
+ kadm5_server_context */*server_context*/,
+ int /*lock_flags*/);
kadm5_ret_t
kadm5_log_modify (
kadm5_server_context */*context*/,
- hdb_entry */*ent*/,
+ hdb_entry */*entry*/,
uint32_t /*mask*/);
kadm5_ret_t
-kadm5_log_nop (kadm5_server_context */*context*/);
+kadm5_log_nop (
+ kadm5_server_context */*context*/,
+ enum kadm_nop_type /*nop_type*/);
kadm5_ret_t
kadm5_log_previous (
krb5_context /*context*/,
krb5_storage */*sp*/,
- uint32_t */*ver*/,
- time_t */*timestamp*/,
- enum kadm_ops */*op*/,
- uint32_t */*len*/);
+ uint32_t */*verp*/,
+ time_t */*tstampp*/,
+ enum kadm_ops */*opp*/,
+ uint32_t */*lenp*/);
kadm5_ret_t
-kadm5_log_reinit (kadm5_server_context */*context*/);
+kadm5_log_recover (
+ kadm5_server_context */*context*/,
+ enum kadm_recover_mode /*mode*/);
+
+kadm5_ret_t
+kadm5_log_reinit (
+ kadm5_server_context */*server_context*/,
+ uint32_t /*vno*/);
kadm5_ret_t
kadm5_log_rename (
kadm5_server_context */*context*/,
krb5_principal /*source*/,
- hdb_entry */*ent*/);
+ hdb_entry */*entry*/);
kadm5_ret_t
kadm5_log_replay (
@@ -350,6 +389,9 @@ kadm5_log_set_version (
kadm5_server_context */*context*/,
uint32_t /*vno*/);
+void
+kadm5_log_signal_master (kadm5_server_context */*context*/);
+
const char *
kadm5_log_signal_socket (krb5_context /*context*/);
@@ -360,24 +402,32 @@ kadm5_log_signal_socket_info (
struct addrinfo **/*ret_addrs*/);
kadm5_ret_t
-kadm5_log_truncate (kadm5_server_context */*server_context*/);
+kadm5_log_truncate (
+ kadm5_server_context */*context*/,
+ size_t /*keep*/,
+ size_t /*maxbytes*/);
kadm5_ret_t
kadm5_s_chpass_principal (
void */*server_handle*/,
krb5_principal /*princ*/,
+ int /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
const char */*password*/);
kadm5_ret_t
kadm5_s_chpass_principal_cond (
void */*server_handle*/,
krb5_principal /*princ*/,
+ int /*keepold*/,
const char */*password*/);
kadm5_ret_t
kadm5_s_chpass_principal_with_key (
void */*server_handle*/,
krb5_principal /*princ*/,
+ int /*keepold*/,
int /*n_key_data*/,
krb5_key_data */*key_data*/);
@@ -386,6 +436,8 @@ kadm5_s_create_principal (
void */*server_handle*/,
kadm5_principal_ent_t /*princ*/,
uint32_t /*mask*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
const char */*password*/);
kadm5_ret_t
@@ -497,6 +549,9 @@ kadm5_ret_t
kadm5_s_randkey_principal (
void */*server_handle*/,
krb5_principal /*princ*/,
+ krb5_boolean /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
krb5_keyblock **/*new_keys*/,
int */*n_keys*/);
@@ -506,4 +561,14 @@ kadm5_s_rename_principal (
krb5_principal /*source*/,
krb5_principal /*target*/);
+kadm5_ret_t
+kadm5_s_setkey_principal_3 (
+ void */*server_handle*/,
+ krb5_principal /*princ*/,
+ krb5_boolean /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
+ krb5_keyblock */*keyblocks*/,
+ int /*n_keys*/);
+
#endif /* __kadm5_private_h__ */
diff --git a/lib/kadm5/kadm5-protos.h b/lib/kadm5/kadm5-protos.h
index eebae95d8f28..8c5f29c8c4f6 100644
--- a/lib/kadm5/kadm5-protos.h
+++ b/lib/kadm5/kadm5-protos.h
@@ -1,6 +1,7 @@
/* This is a generated file */
#ifndef __kadm5_protos_h__
#define __kadm5_protos_h__
+#ifndef DOXY
#include <stdarg.h>
@@ -34,6 +35,11 @@ kadm5_add_passwd_quality_verifier (
krb5_context /*context*/,
const char */*check_library*/);
+int
+kadm5_all_keys_are_bogus (
+ size_t /*n_keys*/,
+ krb5_key_data */*keys*/);
+
const char *
kadm5_check_password_quality (
krb5_context /*context*/,
@@ -47,6 +53,15 @@ kadm5_chpass_principal (
const char */*password*/);
kadm5_ret_t
+kadm5_chpass_principal_3 (
+ void */*server_handle*/,
+ krb5_principal /*princ*/,
+ krb5_boolean /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
+ const char */*password*/);
+
+kadm5_ret_t
kadm5_chpass_principal_with_key (
void */*server_handle*/,
krb5_principal /*princ*/,
@@ -54,6 +69,20 @@ kadm5_chpass_principal_with_key (
krb5_key_data */*key_data*/);
kadm5_ret_t
+kadm5_chpass_principal_with_key_3 (
+ void */*server_handle*/,
+ krb5_principal /*princ*/,
+ int /*keepold*/,
+ int /*n_key_data*/,
+ krb5_key_data */*key_data*/);
+
+kadm5_ret_t
+kadm5_create_policy (
+ void */*server_handle*/,
+ kadm5_policy_ent_t /*policy*/,
+ long /*mask*/);
+
+kadm5_ret_t
kadm5_create_principal (
void */*server_handle*/,
kadm5_principal_ent_t /*princ*/,
@@ -61,6 +90,46 @@ kadm5_create_principal (
const char */*password*/);
kadm5_ret_t
+kadm5_create_principal_3 (
+ void */*server_handle*/,
+ kadm5_principal_ent_t /*princ*/,
+ uint32_t /*mask*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
+ char */*password*/);
+
+/**
+ * Extract decrypted keys from kadm5_principal_ent_t object. Mostly a
+ * no-op for Heimdal because we fetch the entry with decrypted keys.
+ * Sadly this is not fully a no-op, as we have to allocate a copy.
+ *
+ * @server_handle is the kadm5 handle
+ * @entry is the HDB entry for the principal in question
+ * @ktype is the enctype to get a key for, or -1 to get the first one
+ * @stype is the salttype to get a key for, or -1 to get the first match
+ * @kvno is the kvno to search for, or -1 to get the first match (highest kvno)
+ * @keyblock is where the key will be placed
+ * @keysalt, if not NULL, is where the salt will be placed
+ * @kvnop, if not NULL, is where the selected kvno will be placed
+ */
+
+kadm5_ret_t
+kadm5_decrypt_key (
+ void */*server_handle*/,
+ kadm5_principal_ent_t /*entry*/,
+ int32_t /*ktype*/,
+ int32_t /*stype*/,
+ int32_t /*kvno*/,
+ krb5_keyblock */*keyblock*/,
+ krb5_keysalt */*keysalt*/,
+ int */*kvnop*/);
+
+kadm5_ret_t
+kadm5_delete_policy (
+ void */*server_handle*/,
+ char */*name*/);
+
+kadm5_ret_t
kadm5_delete_principal (
void */*server_handle*/,
krb5_principal /*princ*/);
@@ -83,12 +152,28 @@ kadm5_free_name_list (
char **/*names*/,
int */*count*/);
+kadm5_ret_t
+kadm5_free_policy_ent (kadm5_policy_ent_t /*ent*/);
+
void
kadm5_free_principal_ent (
void */*server_handle*/,
kadm5_principal_ent_t /*princ*/);
kadm5_ret_t
+kadm5_get_policies (
+ void */*server_handle*/,
+ char */*exp*/,
+ char ***/*pols*/,
+ int */*count*/);
+
+kadm5_ret_t
+kadm5_get_policy (
+ void */*server_handle*/,
+ char */*policy*/,
+ kadm5_policy_ent_t /*ent*/);
+
+kadm5_ret_t
kadm5_get_principal (
void */*server_handle*/,
krb5_principal /*princ*/,
@@ -171,6 +256,15 @@ kadm5_init_with_skey_ctx (
void **/*server_handle*/);
kadm5_ret_t
+kadm5_lock (void */*server_handle*/);
+
+kadm5_ret_t
+kadm5_modify_policy (
+ void */*server_handle*/,
+ kadm5_policy_ent_t /*policy*/,
+ uint32_t /*mask*/);
+
+kadm5_ret_t
kadm5_modify_principal (
void */*server_handle*/,
kadm5_principal_ent_t /*princ*/,
@@ -184,6 +278,16 @@ kadm5_randkey_principal (
int */*n_keys*/);
kadm5_ret_t
+kadm5_randkey_principal_3 (
+ void */*server_handle*/,
+ krb5_principal /*princ*/,
+ krb5_boolean /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
+ krb5_keyblock **/*new_keys*/,
+ int */*n_keys*/);
+
+kadm5_ret_t
kadm5_rename_principal (
void */*server_handle*/,
krb5_principal /*source*/,
@@ -210,12 +314,50 @@ kadm5_ret_tl_data (
krb5_storage */*sp*/,
krb5_tl_data */*tl*/);
+/**
+ * This function is allows the caller to set new keys for a principal.
+ * This is a trivial wrapper around kadm5_setkey_principal_3().
+ */
+
+kadm5_ret_t
+kadm5_setkey_principal (
+ void */*server_handle*/,
+ krb5_principal /*princ*/,
+ krb5_keyblock */*new_keys*/,
+ int /*n_keys*/);
+
+/**
+ * This function is allows the caller to set new keys for a principal.
+ * This is a simple wrapper around kadm5_get_principal() and
+ * kadm5_modify_principal().
+ */
+
+kadm5_ret_t
+kadm5_setkey_principal_3 (
+ void */*server_handle*/,
+ krb5_principal /*princ*/,
+ krb5_boolean /*keepold*/,
+ int /*n_ks_tuple*/,
+ krb5_key_salt_tuple */*ks_tuple*/,
+ krb5_keyblock */*keyblocks*/,
+ int /*n_keys*/);
+
void
kadm5_setup_passwd_quality_check (
krb5_context /*context*/,
const char */*check_library*/,
const char */*check_function*/);
+int
+kadm5_some_keys_are_bogus (
+ size_t /*n_keys*/,
+ krb5_key_data */*keys*/);
+
+kadm5_ret_t
+kadm5_store_fake_key_data (
+ krb5_storage */*sp*/,
+ krb5_key_data */*key*/);
+
kadm5_ret_t
kadm5_store_key_data (
krb5_storage */*sp*/,
@@ -233,12 +375,21 @@ kadm5_store_principal_ent_mask (
uint32_t /*mask*/);
kadm5_ret_t
+kadm5_store_principal_ent_nokeys (
+ krb5_storage */*sp*/,
+ kadm5_principal_ent_t /*princ*/);
+
+kadm5_ret_t
kadm5_store_tl_data (
krb5_storage */*sp*/,
krb5_tl_data */*tl*/);
+kadm5_ret_t
+kadm5_unlock (void */*server_handle*/);
+
#ifdef __cplusplus
}
#endif
+#endif /* DOXY */
#endif /* __kadm5_protos_h__ */
diff --git a/lib/kadm5/kadm5_err.et b/lib/kadm5/kadm5_err.et
index ae7847275c55..bcbaea846447 100644
--- a/lib/kadm5/kadm5_err.et
+++ b/lib/kadm5/kadm5_err.et
@@ -57,3 +57,13 @@ error_code AUTH_CHANGEPW, "Operation requires `change-password' privilege"
error_code BAD_TL_TYPE, "Invalid tagged data list element type"
error_code MISSING_CONF_PARAMS, "Required parameters in kdc.conf missing"
error_code BAD_SERVER_NAME, "Bad krb5 admin server hostname"
+error_code KS_TUPLE_NOSUPP, "Key/salt tuples not supported by this function"
+error_code SETKEY3_ETYPE_MISMATCH, "Key/salt tuples don't match keys"
+error_code DECRYPT_USAGE_NOSUPP, "Given usage of kadm5_decrypt() not supported"
+error_code POLICY_OP_NOSUPP, "Policy operations not supported"
+error_code KEEPOLD_NOSUPP, "Keep old keys option not supported"
+error_code AUTH_GET_KEYS, "Operation requires `get-keys' privilege"
+error_code ALREADY_LOCKED, "Database already locked"
+error_code NOT_LOCKED, "Database not locked"
+error_code LOG_CORRUPT, "Incremental propagation log got corrupted"
+error_code LOG_NEEDS_UPGRADE, "Incremental propagation log must be upgraded"
diff --git a/lib/kadm5/kadm5_locl.h b/lib/kadm5/kadm5_locl.h
index 68b6a5ebf024..11c3ed1bd201 100644
--- a/lib/kadm5/kadm5_locl.h
+++ b/lib/kadm5/kadm5_locl.h
@@ -38,6 +38,7 @@
#include <config.h>
#include <roken.h>
+#include <heimbase.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/lib/kadm5/keys.c b/lib/kadm5/keys.c
index d46b8db73865..ed7cc83463f2 100644
--- a/lib/kadm5/keys.c
+++ b/lib/kadm5/keys.c
@@ -63,16 +63,18 @@ _kadm5_init_keys (Key *keys, int len)
}
}
+
/*
* return 1 if any key in `keys1, len1' exists in `keys2, len2'
*/
-
-int
+static int
_kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
{
- int i, j;
+ size_t i, j;
+ size_t optimize;
for (i = 0; i < len1; ++i) {
+ optimize = 0;
for (j = 0; j < len2; j++) {
if ((keys1[i].salt != NULL && keys2[j].salt == NULL)
|| (keys1[i].salt == NULL && keys2[j].salt != NULL))
@@ -89,6 +91,7 @@ _kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
}
if (keys1[i].key.keytype != keys2[j].key.keytype)
continue;
+ optimize = 1;
if (keys1[i].key.keyvalue.length != keys2[j].key.keyvalue.length)
continue;
if (memcmp (keys1[i].key.keyvalue.data, keys2[j].key.keyvalue.data,
@@ -97,6 +100,33 @@ _kadm5_exists_keys(Key *keys1, int len1, Key *keys2, int len2)
return 1;
}
+
+ /*
+ * Optimization: no need to check all of keys1[] if one there
+ * was one key in keys2[] with matching enctype and salt but not
+ * matching key. Assumption: all keys in keys1[] and keys2[]
+ * are output by string2key.
+ */
+ if (optimize)
+ return 0;
}
return 0;
}
+
+/*
+ * return 1 if any key in `keys1, len1' exists in hist_keys
+ */
+int
+_kadm5_exists_keys_hist(Key *keys1, int len1, HDB_Ext_KeySet *hist_keys)
+{
+ size_t i;
+
+ for (i = 0; i < hist_keys->len; i++) {
+ if (_kadm5_exists_keys(keys1, len1,
+ hist_keys->val[i].keys.val,
+ hist_keys->val[i].keys.len))
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/lib/kadm5/libkadm5srv-exports.def b/lib/kadm5/libkadm5srv-exports.def
index ad5fa37e9ea2..ecffbb91c48c 100644
--- a/lib/kadm5/libkadm5srv-exports.def
+++ b/lib/kadm5/libkadm5srv-exports.def
@@ -2,16 +2,26 @@ EXPORTS
;! kadm5_ad_init_with_password
;! kadm5_ad_init_with_password_ctx
kadm5_add_passwd_quality_verifier
+ kadm5_all_keys_are_bogus
kadm5_check_password_quality
kadm5_chpass_principal
+ kadm5_chpass_principal_3
kadm5_chpass_principal_with_key
+ kadm5_chpass_principal_with_key_3
+ kadm5_create_policy
kadm5_create_principal
+ kadm5_create_principal_3
+ kadm5_decrypt_key
+ kadm5_delete_policy
kadm5_delete_principal
kadm5_destroy
kadm5_flush
kadm5_free_key_data
kadm5_free_name_list
+ kadm5_free_policy_ent
kadm5_free_principal_ent
+ kadm5_get_policies
+ kadm5_get_policy
kadm5_get_principal
kadm5_get_principals
kadm5_get_privs
@@ -21,18 +31,26 @@ EXPORTS
kadm5_init_with_password_ctx
kadm5_init_with_skey
kadm5_init_with_skey_ctx
+ kadm5_lock
+ kadm5_modify_policy
kadm5_modify_principal
kadm5_randkey_principal
+ kadm5_randkey_principal_3
kadm5_rename_principal
kadm5_ret_key_data
kadm5_ret_principal_ent
kadm5_ret_principal_ent_mask
kadm5_ret_tl_data
+ kadm5_setkey_principal
+ kadm5_setkey_principal_3
kadm5_setup_passwd_quality_check
+ kadm5_some_keys_are_bogus
kadm5_store_key_data
kadm5_store_principal_ent
kadm5_store_principal_ent_mask
+ kadm5_store_principal_ent_nokeys
kadm5_store_tl_data
+ kadm5_unlock
kadm5_s_init_with_password_ctx
kadm5_s_init_with_password
kadm5_s_init_with_skey_ctx
@@ -41,6 +59,7 @@ EXPORTS
kadm5_s_init_with_creds
kadm5_s_chpass_principal_cond
kadm5_log_set_version
+ kadm5_log_signal_master
;! kadm5_log_signal_socket
kadm5_log_signal_socket_info ;!
kadm5_log_previous
@@ -48,10 +67,14 @@ EXPORTS
kadm5_log_foreach
kadm5_log_get_version_fd
kadm5_log_get_version
+ kadm5_log_recover
kadm5_log_replay
kadm5_log_end
kadm5_log_reinit
kadm5_log_init
+ kadm5_log_init_nb
+ kadm5_log_init_nolock
+ kadm5_log_init_sharedlock
kadm5_log_nop
kadm5_log_truncate
kadm5_log_modify
diff --git a/lib/kadm5/log.c b/lib/kadm5/log.c
index 05b84b1e07cc..a284fd3e5d3c 100644
--- a/lib/kadm5/log.c
+++ b/lib/kadm5/log.c
@@ -37,50 +37,519 @@
RCSID("$Id$");
/*
- * A log record consists of:
+ * A log consists of a sequence of records of this form:
*
- * version number 4 bytes
- * time in seconds 4 bytes
- * operation (enum kadm_ops) 4 bytes
- * length of record 4 bytes
- * data... n bytes
- * length of record 4 bytes
- * version number 4 bytes
+ * version number 4 bytes -\
+ * time in seconds 4 bytes +> preamble --+> header
+ * operation (enum kadm_ops) 4 bytes -/ /
+ * n, length of payload 4 bytes --------------+
+ * PAYLOAD DATA... n bytes
+ * n, length of payload 4 bytes ----------------+> trailer
+ * version number 4 bytes ->postamble ---/
*
+ * I.e., records have a header and a trailer so that knowing the offset
+ * of an record's start or end one can traverse the log forwards and
+ * backwards.
+ *
+ * The log always starts with a nop record (uber record) that contains the
+ * offset (8 bytes) of the first unconfirmed record (typically EOF), and the
+ * version number and timestamp of the preceding last confirmed record:
+ *
+ * offset of next new record 8 bytes
+ * last record time 4 bytes
+ * last record version number 4 bytes
+ *
+ * When an iprop slave receives a complete database, it saves that version as
+ * the last confirmed version, without writing any other records to the log. We
+ * use that version as the basis for further updates.
+ *
+ * kadm5 write operations are done in this order:
+ *
+ * - replay unconfirmed log records
+ * - write (append) and fsync() the log record for the kadm5 update
+ * - update the HDB (which includes fsync() or moral equivalent)
+ * - update the log uber record to mark the log record written as
+ * confirmed (not fsync()ed)
+ *
+ * This makes it possible and safe to seek to the logical end of the log
+ * (that is, the end of the last confirmed record) without traversing
+ * the whole log forward from offset zero. Unconfirmed records (which
+ * -currently- should never be more than one) can then be found (and
+ * rolled forward) by traversing forward from the logical end of the
+ * log. The trailers make it possible to traverse the log backwards
+ * from the logical end.
+ *
+ * This also makes the log + the HDB a two-phase commit with
+ * roll-forward system.
+ *
+ * HDB entry exists and HDB entry does not exist errors occurring during
+ * replay of unconfirmed records are ignored. This is because the
+ * corresponding HDB update might have completed. But also because a
+ * change to add aliases to a principal can fail because we don't check
+ * for alias conflicts before going ahead with the write operation.
+ *
+ * Non-sensical and incomplete log records found during roll-forward are
+ * truncated. A log record is non-sensical if its header and trailer
+ * don't match.
+ *
+ * Recovery (by rolling forward) occurs at the next read or write by a
+ * kadm5 API reader (e.g., kadmin), but not by an hdb API reader (e.g.,
+ * the KDC). This means that, e.g., a principal rename could fail in
+ * between the store and the delete, and recovery might not take place
+ * until the next write operation.
+ *
+ * The log record payload format for create is:
+ *
+ * DER-encoded HDB_entry n bytes
+ *
+ * The log record payload format for update is:
+ *
+ * mask 4 bytes
+ * DER-encoded HDB_entry n-4 bytes
+ *
+ * The log record payload format for delete is:
+ *
+ * krb5_store_principal n bytes
+ *
+ * The log record payload format for rename is:
+ *
+ * krb5_store_principal m bytes (old principal name)
+ * DER-encoded HDB_entry n-m bytes (new record)
+ *
+ * The log record payload format for nop varies:
+ *
+ * - The zeroth record in new logs is a nop with a 16 byte payload:
+ *
+ * offset of end of last confirmed record 8 bytes
+ * timestamp of last confirmed record 4 bytes
+ * version number of last confirmed record 4 bytes
+ *
+ * - New non-zeroth nop records:
+ *
+ * nop type 4 bytes
+ *
+ * - Old nop records:
+ *
+ * version number 4 bytes
+ * timestamp 4 bytes
+ *
+ * Upon initialization, the log's uber record will have version 1, and
+ * will be followed by a nop record with version 2. The version numbers
+ * of additional records will be monotonically increasing.
+ *
+ * Truncation (kadm5_log_truncate()) takes some N > 0 records from the
+ * tail of the log and writes them to the beginning of the log after an
+ * uber record whose version will then be one less than the first of
+ * those records.
+ *
+ * On masters the log should never have more than one unconfirmed
+ * record, but slaves append all of a master's "diffs" and then call
+ * kadm5_log_recover() to recover.
+ */
+
+/*
+ * HDB and log lock order on the master:
+ *
+ * 1) open and lock the HDB
+ * 2) open and lock the log
+ * 3) do something
+ * 4) unlock and close the log
+ * 5) repeat (2)..(4) if desired
+ * 6) unlock and close the HDB
+ *
+ * The kadmin -l lock command can be used to hold the HDB open and
+ * locked for multiple operations.
+ *
+ * HDB and log lock order on the slave:
+ *
+ * 1) open and lock the log
+ * 2) open and lock the HDB
+ * 3) replay entries
+ * 4) unlock and close the HDB
+ * 5) repeat (2)..(4) until signaled
+ * 6) unlock and close the HDB
+ *
+ * The slave doesn't want to allow other local writers, after all, thus
+ * the order is reversed. This means that using "kadmin -l" on a slave
+ * will deadlock with ipropd-slave -- don't do that.
+ */
+
+#define LOG_HEADER_SZ ((off_t)(sizeof(uint32_t) * 4))
+#define LOG_TRAILER_SZ ((off_t)(sizeof(uint32_t) * 2))
+#define LOG_WRAPPER_SZ ((off_t)(LOG_HEADER_SZ + LOG_TRAILER_SZ))
+#define LOG_UBER_LEN ((off_t)(sizeof(uint64_t) + sizeof(uint32_t) * 2))
+#define LOG_UBER_SZ ((off_t)(LOG_WRAPPER_SZ + LOG_UBER_LEN))
+
+#define LOG_NOPEEK 0
+#define LOG_DOPEEK 1
+
+/*
+ * Read the header of the record starting at the current offset into sp.
+ *
+ * Preserves sp's offset on success if `peek', else skips the header.
+ *
+ * Preserves sp's offset on failure where possible.
*/
+static kadm5_ret_t
+get_header(krb5_storage *sp, int peek, uint32_t *verp, uint32_t *tstampp,
+ enum kadm_ops *opp, uint32_t *lenp)
+{
+ krb5_error_code ret;
+ uint32_t tstamp, op, len;
+ off_t off, new_off;
+
+ if (tstampp == NULL)
+ tstampp = &tstamp;
+ if (lenp == NULL)
+ lenp = &len;
+ *verp = 0;
+ *tstampp = 0;
+ if (opp != NULL)
+ *opp = kadm_nop;
+ *lenp = 0;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return errno;
+ ret = krb5_ret_uint32(sp, verp);
+ if (ret == HEIM_ERR_EOF) {
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return HEIM_ERR_EOF;
+ }
+ if (ret)
+ goto log_corrupt;
+ ret = krb5_ret_uint32(sp, tstampp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Note: sizeof(*opp) might not == sizeof(op) */
+ ret = krb5_ret_uint32(sp, &op);
+ if (ret)
+ goto log_corrupt;
+ if (opp != NULL)
+ *opp = op;
+
+ ret = krb5_ret_uint32(sp, lenp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Restore offset if requested */
+ if (peek == LOG_DOPEEK) {
+ new_off = krb5_storage_seek(sp, off, SEEK_SET);
+ if (new_off == -1)
+ return errno;
+ if (new_off != off)
+ return EIO;
+ }
+
+ return 0;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return KADM5_LOG_CORRUPT;
+}
+
+/*
+ * Seek to the start of the preceding record's header and returns its
+ * offset. If sp is at offset zero this sets *verp = 0 and returns 0.
+ *
+ * Does not verify the header of the previous entry.
+ *
+ * On error returns -1, setting errno (possibly to a kadm5_ret_t or
+ * krb5_error_code value) and preserves sp's offset where possible.
+ */
+static off_t
+seek_prev(krb5_storage *sp, uint32_t *verp, uint32_t *lenp)
+{
+ krb5_error_code ret;
+ uint32_t len, ver;
+ off_t off_len;
+ off_t off, new_off;
+
+ if (lenp == NULL)
+ lenp = &len;
+ if (verp == NULL)
+ verp = &ver;
+
+ *verp = 0;
+ *lenp = 0;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return off;
+ if (off == 0)
+ return 0;
+
+ /* Check that `off' allows for the record's header and trailer */
+ if (off < LOG_WRAPPER_SZ)
+ goto log_corrupt;
+
+ /* Get the previous entry's length and version from its trailer */
+ new_off = krb5_storage_seek(sp, -8, SEEK_CUR);
+ if (new_off == -1)
+ return -1;
+ if (new_off != off - 8) {
+ errno = EIO;
+ return -1;
+ }
+ ret = krb5_ret_uint32(sp, lenp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Check for overflow/sign extension */
+ off_len = (off_t)*lenp;
+ if (off_len < 0 || *lenp != (uint32_t)off_len)
+ goto log_corrupt;
+
+ ret = krb5_ret_uint32(sp, verp);
+ if (ret)
+ goto log_corrupt;
+
+ /* Check that `off' allows for the record */
+ if (off < LOG_WRAPPER_SZ + off_len)
+ goto log_corrupt;
+
+ /* Seek backwards to the entry's start */
+ new_off = krb5_storage_seek(sp, -(LOG_WRAPPER_SZ + off_len), SEEK_CUR);
+ if (new_off == -1)
+ return -1;
+ if (new_off != off - (LOG_WRAPPER_SZ + off_len)) {
+ errno = EIO;
+ return -1;
+ }
+ return new_off;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ errno = KADM5_LOG_CORRUPT;
+ return -1;
+}
+
+/*
+ * Seek to the start of the next entry's header.
+ *
+ * On error returns -1 and preserves sp's offset.
+ */
+static off_t
+seek_next(krb5_storage *sp)
+{
+ krb5_error_code ret;
+ uint32_t ver, ver2, len, len2;
+ enum kadm_ops op;
+ uint32_t tstamp;
+ off_t off, off_len, new_off;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return off;
+
+ errno = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
+ if (errno)
+ return -1;
+
+ /* Check for overflow */
+ off_len = len;
+ if (off_len < 0)
+ goto log_corrupt;
+
+ new_off = krb5_storage_seek(sp, off_len, SEEK_CUR);
+ if (new_off == -1) {
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return -1;
+ }
+ if (new_off != off + LOG_HEADER_SZ + off_len)
+ goto log_corrupt;
+ ret = krb5_ret_uint32(sp, &len2);
+ if (ret || len2 != len)
+ goto log_corrupt;
+ ret = krb5_ret_uint32(sp, &ver2);
+ if (ret || ver2 != ver)
+ goto log_corrupt;
+ new_off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (new_off == -1) {
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return -1;
+ }
+ if (new_off != off + off_len + LOG_WRAPPER_SZ)
+ goto log_corrupt;
+
+ return off + off_len + LOG_WRAPPER_SZ;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ errno = KADM5_LOG_CORRUPT;
+ return -1;
+}
+
+/*
+ * Get the version of the entry ending at the current offset into sp.
+ * If it is the uber record, return its nominal version instead.
+ *
+ * Returns HEIM_ERR_EOF if sp is at offset zero.
+ *
+ * Preserves sp's offset.
+ */
+static kadm5_ret_t
+get_version_prev(krb5_storage *sp, uint32_t *verp, uint32_t *tstampp)
+{
+ krb5_error_code ret;
+ uint32_t ver, ver2, len, len2;
+ off_t off, prev_off, new_off;
+
+ *verp = 0;
+ if (tstampp != NULL)
+ *tstampp = 0;
+
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off < 0)
+ return errno;
+ if (off == 0)
+ return HEIM_ERR_EOF;
+
+ /* Read the trailer and seek back */
+ prev_off = seek_prev(sp, &ver, &len);
+ if (prev_off == -1)
+ return errno;
+
+ /* Uber record? Return nominal version. */
+ if (prev_off == 0 && len == LOG_UBER_LEN && ver == 0) {
+ /* Skip 8 byte offset and 4 byte time */
+ if (krb5_storage_seek(sp, LOG_HEADER_SZ + 12, SEEK_SET)
+ != LOG_HEADER_SZ + 12)
+ return errno;
+ ret = krb5_ret_uint32(sp, verp);
+ if (krb5_storage_seek(sp, 0, SEEK_SET) != 0)
+ return errno;
+ if (ret != 0)
+ return ret;
+ } else {
+ *verp = ver;
+ }
+
+ /* Verify that the trailer matches header */
+ ret = get_header(sp, LOG_NOPEEK, &ver2, tstampp, NULL, &len2);
+ if (ret || ver != ver2 || len != len2)
+ goto log_corrupt;
+
+ /* Preserve offset */
+ new_off = krb5_storage_seek(sp, off, SEEK_SET);
+ if (new_off == -1)
+ return errno;
+ if (new_off != off) {
+ errno = EIO;
+ return errno;
+ }
+ return 0;
+
+log_corrupt:
+ (void) krb5_storage_seek(sp, off, SEEK_SET);
+ return KADM5_LOG_CORRUPT;
+}
+
+static size_t
+get_max_log_size(krb5_context context)
+{
+ off_t n;
+
+ /* Use database-label-specific lookup? No, ETOOHARD. */
+ /* Default to 50MB max log size */
+ n = krb5_config_get_int_default(context, NULL, 52428800,
+ "kdc",
+ "log-max-size",
+ NULL);
+ if (n >= 4 * (LOG_UBER_LEN + LOG_WRAPPER_SZ) && n == (size_t)n)
+ return (size_t)n;
+ return 0;
+}
+
+static kadm5_ret_t truncate_if_needed(kadm5_server_context *);
+static krb5_storage *log_goto_first(kadm5_server_context *, int);
+
+/*
+ * Get the version and timestamp metadata of either the first, or last
+ * confirmed entry in the log.
+ *
+ * If `which' is LOG_VERSION_UBER, then this gets the version number of the uber
+ * uber record which must be 0, or else we need to upgrade the log.
+ *
+ * If `which' is LOG_VERSION_FIRST, then this gets the metadata for the
+ * logically first entry past the uberblock, or returns HEIM_EOF if
+ * only the uber record is present.
+ *
+ * If `which' is LOG_VERSION_LAST, then this gets metadata for the last
+ * confirmed entry's version and timestamp. If only the uber record is present,
+ * then the version will be its "nominal" version, which may differ from its
+ * actual version (0).
+ *
+ * The `fd''s offset will be set to the start of the header of the entry
+ * identified by `which'.
+ */
kadm5_ret_t
-kadm5_log_get_version_fd (int fd,
- uint32_t *ver)
+kadm5_log_get_version_fd(kadm5_server_context *server_context, int fd,
+ int which, uint32_t *ver, uint32_t *tstamp)
{
- int ret;
+ kadm5_ret_t ret = 0;
krb5_storage *sp;
- int32_t old_version;
+ enum kadm_ops op = kadm_get;
+ uint32_t len = 0;
+ uint32_t tmp;
+
+ if (fd == -1)
+ return 0; /* /dev/null */
+
+ if (tstamp == NULL)
+ tstamp = &tmp;
+
+ *ver = 0;
+ *tstamp = 0;
+
+ switch (which) {
+ case LOG_VERSION_LAST:
+ sp = kadm5_log_goto_end(server_context, fd);
+ if (sp == NULL)
+ return errno;
+ ret = get_version_prev(sp, ver, tstamp);
+ krb5_storage_free(sp);
+ break;
+ case LOG_VERSION_FIRST:
+ sp = log_goto_first(server_context, fd);
+ if (sp == NULL)
+ return errno;
+ ret = get_header(sp, LOG_DOPEEK, ver, tstamp, NULL, NULL);
+ krb5_storage_free(sp);
+ break;
+ case LOG_VERSION_UBER:
+ sp = krb5_storage_from_fd(server_context->log_context.log_fd);
+ if (sp == NULL)
+ return errno;
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == 0)
+ ret = get_header(sp, LOG_DOPEEK, ver, tstamp, &op, &len);
+ else
+ ret = errno;
+ if (ret == 0 && (op != kadm_nop || len != LOG_UBER_LEN || *ver != 0))
+ ret = KADM5_LOG_NEEDS_UPGRADE;
+ krb5_storage_free(sp);
+ break;
+ default:
+ return ENOTSUP;
+ }
- ret = lseek (fd, 0, SEEK_END);
- if(ret < 0)
- return errno;
- if(ret == 0) {
- *ver = 0;
- return 0;
- }
- sp = krb5_storage_from_fd (fd);
- krb5_storage_seek(sp, -4, SEEK_CUR);
- krb5_ret_int32 (sp, &old_version);
- *ver = old_version;
- krb5_storage_free(sp);
- lseek (fd, 0, SEEK_END);
- return 0;
+ return ret;
}
+/* Get the version of the last confirmed entry in the log */
kadm5_ret_t
-kadm5_log_get_version (kadm5_server_context *context, uint32_t *ver)
+kadm5_log_get_version(kadm5_server_context *server_context, uint32_t *ver)
{
- return kadm5_log_get_version_fd (context->log_context.log_fd, ver);
+ return kadm5_log_get_version_fd(server_context,
+ server_context->log_context.log_fd,
+ LOG_VERSION_LAST, ver, NULL);
}
+/* Sets the version in the context, but NOT in the log */
kadm5_ret_t
-kadm5_log_set_version (kadm5_server_context *context, uint32_t vno)
+kadm5_log_set_version(kadm5_server_context *context, uint32_t vno)
{
kadm5_log_context *log_context = &context->log_context;
@@ -88,187 +557,439 @@ kadm5_log_set_version (kadm5_server_context *context, uint32_t vno)
return 0;
}
-kadm5_ret_t
-kadm5_log_init (kadm5_server_context *context)
+/*
+ * Open the log and setup server_context->log_context
+ */
+static kadm5_ret_t
+log_open(kadm5_server_context *server_context, int lock_mode)
{
- int fd;
+ int fd = -1;
+ int lock_it = 0;
+ int lock_nb = 0;
+ int oflags = O_RDWR;
kadm5_ret_t ret;
- kadm5_log_context *log_context = &context->log_context;
+ kadm5_log_context *log_context = &server_context->log_context;
- if (log_context->log_fd != -1)
- return 0;
- fd = open (log_context->log_file, O_RDWR | O_CREAT, 0600);
- if (fd < 0) {
- ret = errno;
- krb5_set_error_message(context->context, ret, "kadm5_log_init: open %s",
- log_context->log_file);
- return ret;
+ if (lock_mode & LOCK_NB) {
+ lock_mode &= ~LOCK_NB;
+ lock_nb = LOCK_NB;
}
- if (flock (fd, LOCK_EX) < 0) {
- ret = errno;
- krb5_set_error_message(context->context, ret, "kadm5_log_init: flock %s",
- log_context->log_file);
- close (fd);
- return errno;
+
+ if (lock_mode == log_context->lock_mode && log_context->log_fd != -1)
+ return 0;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0) {
+ /* log_context->log_fd should be -1 here */
+ return 0;
}
- ret = kadm5_log_get_version_fd (fd, &log_context->version);
- if (ret)
+ if (log_context->log_fd != -1) {
+ /* Lock or change lock */
+ fd = log_context->log_fd;
+ if (lseek(fd, 0, SEEK_SET) == -1)
+ return errno;
+ lock_it = (lock_mode != log_context->lock_mode);
+ } else {
+ /* Open and lock */
+ if (lock_mode != LOCK_UN)
+ oflags |= O_CREAT;
+ fd = open(log_context->log_file, oflags, 0600);
+ if (fd < 0) {
+ ret = errno;
+ krb5_set_error_message(server_context->context, ret,
+ "log_open: open %s", log_context->log_file);
+ return ret;
+ }
+ lock_it = (lock_mode != LOCK_UN);
+ }
+ if (lock_it && flock(fd, lock_mode | lock_nb) < 0) {
+ ret = errno;
+ krb5_set_error_message(server_context->context, ret,
+ "log_open: flock %s", log_context->log_file);
+ if (fd != log_context->log_fd)
+ (void) close(fd);
return ret;
+ }
+
+ log_context->log_fd = fd;
+ log_context->lock_mode = lock_mode;
+ log_context->read_only = (lock_mode != LOCK_EX);
- log_context->log_fd = fd;
return 0;
}
-kadm5_ret_t
-kadm5_log_reinit (kadm5_server_context *context)
+/*
+ * Open the log and setup server_context->log_context
+ */
+static kadm5_ret_t
+log_init(kadm5_server_context *server_context, int lock_mode)
{
int fd;
- kadm5_log_context *log_context = &context->log_context;
+ struct stat st;
+ uint32_t vno;
+ size_t maxbytes = get_max_log_size(server_context->context);
+ kadm5_ret_t ret;
+ kadm5_log_context *log_context = &server_context->log_context;
- if (log_context->log_fd != -1) {
- flock (log_context->log_fd, LOCK_UN);
- close (log_context->log_fd);
- log_context->log_fd = -1;
+ if (strcmp(log_context->log_file, "/dev/null") == 0) {
+ /* log_context->log_fd should be -1 here */
+ return 0;
}
- fd = open (log_context->log_file, O_RDWR | O_CREAT | O_TRUNC, 0600);
- if (fd < 0)
- return errno;
- if (flock (fd, LOCK_EX) < 0) {
- close (fd);
- return errno;
+
+ ret = log_open(server_context, lock_mode);
+ if (ret)
+ return ret;
+
+ fd = log_context->log_fd;
+ if (!log_context->read_only) {
+ if (fstat(fd, &st) == -1)
+ ret = errno;
+ if (ret == 0 && st.st_size == 0) {
+ /* Write first entry */
+ log_context->version = 0;
+ ret = kadm5_log_nop(server_context, kadm_nop_plain);
+ if (ret == 0)
+ return 0; /* no need to truncate_if_needed(): it's not */
+ }
+ if (ret == 0) {
+ ret = kadm5_log_get_version_fd(server_context, fd,
+ LOG_VERSION_UBER, &vno, NULL);
+
+ /* Upgrade the log if it was an old-style log */
+ if (ret == KADM5_LOG_NEEDS_UPGRADE)
+ ret = kadm5_log_truncate(server_context, 0, maxbytes / 4);
+ }
+ if (ret == 0)
+ ret = kadm5_log_recover(server_context, kadm_recover_replay);
}
- log_context->version = 0;
- log_context->log_fd = fd;
- return 0;
+ if (ret == 0) {
+ ret = kadm5_log_get_version_fd(server_context, fd, LOG_VERSION_LAST,
+ &log_context->version, NULL);
+ if (ret == HEIM_ERR_EOF)
+ ret = 0;
+ }
+
+ if (ret == 0)
+ ret = truncate_if_needed(server_context);
+
+ if (ret != 0)
+ (void) kadm5_log_end(server_context);
+ return ret;
}
+/* Open the log with an exclusive lock */
+kadm5_ret_t
+kadm5_log_init(kadm5_server_context *server_context)
+{
+ return log_init(server_context, LOCK_EX);
+}
+/* Open the log with an exclusive non-blocking lock */
kadm5_ret_t
-kadm5_log_end (kadm5_server_context *context)
+kadm5_log_init_nb(kadm5_server_context *server_context)
{
- kadm5_log_context *log_context = &context->log_context;
+ return log_init(server_context, LOCK_EX | LOCK_NB);
+}
+
+/* Open the log with no locks */
+kadm5_ret_t
+kadm5_log_init_nolock(kadm5_server_context *server_context)
+{
+ return log_init(server_context, LOCK_UN);
+}
+
+/* Open the log with a shared lock */
+kadm5_ret_t
+kadm5_log_init_sharedlock(kadm5_server_context *server_context, int lock_flags)
+{
+ return log_init(server_context, LOCK_SH | lock_flags);
+}
+
+/*
+ * Reinitialize the log and open it
+ */
+kadm5_ret_t
+kadm5_log_reinit(kadm5_server_context *server_context, uint32_t vno)
+{
+ int ret;
+ kadm5_log_context *log_context = &server_context->log_context;
+
+ ret = log_open(server_context, LOCK_EX);
+ if (ret)
+ return ret;
+ if (log_context->log_fd != -1) {
+ if (ftruncate(log_context->log_fd, 0) < 0) {
+ ret = errno;
+ return ret;
+ }
+ if (lseek(log_context->log_fd, 0, SEEK_SET) < 0) {
+ ret = errno;
+ return ret;
+ }
+ }
+
+ /* Write uber entry and truncation nop with version `vno` */
+ log_context->version = vno;
+ return kadm5_log_nop(server_context, kadm_nop_plain);
+}
+
+/* Close the server_context->log_context. */
+kadm5_ret_t
+kadm5_log_end(kadm5_server_context *server_context)
+{
+ kadm5_log_context *log_context = &server_context->log_context;
+ kadm5_ret_t ret = 0;
int fd = log_context->log_fd;
- flock (fd, LOCK_UN);
- close(fd);
+ if (fd != -1) {
+ if (log_context->lock_mode != LOCK_UN) {
+ if (flock(fd, LOCK_UN) == -1 && errno == EBADF)
+ ret = errno;
+ }
+ if (ret != EBADF && close(fd) == -1)
+ ret = errno;
+ }
log_context->log_fd = -1;
- return 0;
+ log_context->lock_mode = LOCK_UN;
+ return ret;
}
+/*
+ * Write the version, timestamp, and op for a new entry.
+ *
+ * Note that the sp should be a krb5_storage_emem(), not a file.
+ *
+ * On success the sp's offset will be where the length of the payload
+ * should be written.
+ */
static kadm5_ret_t
-kadm5_log_preamble (kadm5_server_context *context,
- krb5_storage *sp,
- enum kadm_ops op)
+kadm5_log_preamble(kadm5_server_context *context,
+ krb5_storage *sp,
+ enum kadm_ops op,
+ uint32_t vno)
{
kadm5_log_context *log_context = &context->log_context;
- kadm5_ret_t kadm_ret;
+ time_t now = time(NULL);
+ kadm5_ret_t ret;
- kadm_ret = kadm5_log_init (context);
- if (kadm_ret)
- return kadm_ret;
+ ret = krb5_store_uint32(sp, vno);
+ if (ret)
+ return ret;
+ ret = krb5_store_uint32(sp, now);
+ if (ret)
+ return ret;
+ log_context->last_time = now;
- krb5_store_int32 (sp, ++log_context->version);
- krb5_store_int32 (sp, time(NULL));
- krb5_store_int32 (sp, op);
- return 0;
+ if (op < kadm_first || op > kadm_last)
+ return ERANGE;
+ return krb5_store_uint32(sp, op);
}
+/* Writes the version part of the trailer */
static kadm5_ret_t
-kadm5_log_postamble (kadm5_log_context *context,
- krb5_storage *sp)
+kadm5_log_postamble(kadm5_log_context *context,
+ krb5_storage *sp,
+ uint32_t vno)
{
- krb5_store_int32 (sp, context->version);
- return 0;
+ return krb5_store_uint32(sp, vno);
+}
+
+/*
+ * Signal the ipropd-master about changes to the log.
+ */
+/*
+ * XXX Get rid of the ifdef by having a sockaddr in log_context in both
+ * cases.
+ *
+ * XXX Better yet, just connect to the master's socket that slaves
+ * connect to, and then disconnect. The master should then check the
+ * log on every connection accepted. Then we wouldn't need IPC to
+ * signal the master.
+ */
+void
+kadm5_log_signal_master(kadm5_server_context *context)
+{
+ kadm5_log_context *log_context = &context->log_context;
+#ifndef NO_UNIX_SOCKETS
+ sendto(log_context->socket_fd,
+ (void *)&log_context->version,
+ sizeof(log_context->version),
+ 0,
+ (struct sockaddr *)&log_context->socket_name,
+ sizeof(log_context->socket_name));
+#else
+ sendto(log_context->socket_fd,
+ (void *)&log_context->version,
+ sizeof(log_context->version),
+ 0,
+ log_context->socket_info->ai_addr,
+ log_context->socket_info->ai_addrlen);
+#endif
}
/*
- * flush the log record in `sp'.
+ * Write sp's contents (which must be a fully formed record, complete
+ * with header, payload, and trailer) to the log and fsync the log.
+ *
+ * Does not free sp.
*/
static kadm5_ret_t
-kadm5_log_flush (kadm5_log_context *log_context,
- krb5_storage *sp)
+kadm5_log_flush(kadm5_server_context *context, krb5_storage *sp)
{
+ kadm5_log_context *log_context = &context->log_context;
+ kadm5_ret_t ret;
krb5_data data;
size_t len;
- ssize_t ret;
+ krb5_ssize_t bytes;
+ uint32_t new_ver, prev_ver;
+ off_t off, end;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return 0;
+
+ if (log_context->read_only)
+ return EROFS;
+
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1)
+ return errno;
+
+ ret = get_header(sp, LOG_DOPEEK, &new_ver, NULL, NULL, NULL);
+ if (ret)
+ return ret;
+
+ ret = krb5_storage_to_data(sp, &data);
+ if (ret)
+ return ret;
+
+ /* Abandon the emem storage reference */
+ sp = krb5_storage_from_fd(log_context->log_fd);
+ if (sp == NULL) {
+ krb5_data_free(&data);
+ return ENOMEM;
+ }
+
+ /* Check that we are at the end of the log and fail if not */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off == -1) {
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return errno;
+ }
+ end = krb5_storage_seek(sp, 0, SEEK_END);
+ if (end == -1) {
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return errno;
+ }
+ if (end != off) {
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return KADM5_LOG_CORRUPT;
+ }
+
+ /* Enforce monotonically incremented versioning of records */
+ if (seek_prev(sp, &prev_ver, NULL) == -1 ||
+ krb5_storage_seek(sp, end, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ if (prev_ver != 0 && prev_ver != log_context->version)
+ return EINVAL; /* Internal error, really; just a consistency check */
+
+ if (prev_ver != 0 && new_ver != prev_ver + 1) {
+ krb5_warnx(context->context, "refusing to write a log record "
+ "with non-monotonic version (new: %u, old: %u)",
+ new_ver, prev_ver);
+ return KADM5_LOG_CORRUPT;
+ }
- krb5_storage_to_data(sp, &data);
len = data.length;
- ret = write (log_context->log_fd, data.data, len);
- if (ret < 0 || (size_t)ret != len) {
- krb5_data_free(&data);
+ bytes = krb5_storage_write(sp, data.data, len);
+ krb5_data_free(&data);
+ if (bytes < 0) {
+ krb5_storage_free(sp);
return errno;
}
- if (fsync (log_context->log_fd) < 0) {
- krb5_data_free(&data);
- return errno;
+ if (bytes != (krb5_ssize_t)len) {
+ krb5_storage_free(sp);
+ return EIO;
}
- /*
- * Try to send a signal to any running `ipropd-master'
- */
-#ifndef NO_UNIX_SOCKETS
- sendto (log_context->socket_fd,
- (void *)&log_context->version,
- sizeof(log_context->version),
- 0,
- (struct sockaddr *)&log_context->socket_name,
- sizeof(log_context->socket_name));
-#else
- sendto (log_context->socket_fd,
- (void *)&log_context->version,
- sizeof(log_context->version),
- 0,
- log_context->socket_info->ai_addr,
- log_context->socket_info->ai_addrlen);
-#endif
+ ret = krb5_storage_fsync(sp);
+ krb5_storage_free(sp);
+ if (ret)
+ return ret;
- krb5_data_free(&data);
+ /* Retain the nominal database version when flushing the uber record */
+ if (new_ver != 0)
+ log_context->version = new_ver;
return 0;
}
/*
- * Add a `create' operation to the log.
+ * Add a `create' operation to the log and perform the create against the HDB.
*/
-
kadm5_ret_t
-kadm5_log_create (kadm5_server_context *context,
- hdb_entry *ent)
+kadm5_log_create(kadm5_server_context *context, hdb_entry *entry)
{
krb5_storage *sp;
kadm5_ret_t ret;
krb5_data value;
+ hdb_entry_ex ent;
kadm5_log_context *log_context = &context->log_context;
+ memset(&ent, 0, sizeof(ent));
+ ent.ctx = 0;
+ ent.free_entry = 0;
+ ent.entry = *entry;
+
+ /*
+ * If we're not logging then we can't recover-to-perform, so just
+ * perform.
+ */
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return context->db->hdb_store(context->context, context->db, 0, &ent);
+
+ /*
+ * Test for any conflicting entries before writing the log. If we commit
+ * to the log we'll end-up rolling forward on recovery, but that would be
+ * wrong if the initial create is rejected.
+ */
+ ret = context->db->hdb_store(context->context, context->db,
+ HDB_F_PRECHECK, &ent);
+ if (ret == 0)
+ ret = hdb_entry2value(context->context, entry, &value);
+ if (ret)
+ return ret;
sp = krb5_storage_emem();
- ret = hdb_entry2value (context->context, ent, &value);
- if (ret) {
- krb5_storage_free(sp);
- return ret;
- }
- ret = kadm5_log_preamble (context, sp, kadm_create);
- if (ret) {
- krb5_data_free (&value);
- krb5_storage_free(sp);
- return ret;
- }
- krb5_store_int32 (sp, value.length);
- krb5_storage_write(sp, value.data, value.length);
- krb5_store_int32 (sp, value.length);
- krb5_data_free (&value);
- ret = kadm5_log_postamble (log_context, sp);
- if (ret) {
- krb5_storage_free (sp);
- return ret;
+ if (sp == NULL)
+ ret = ENOMEM;
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_create,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, value.length);
+ if (ret == 0) {
+ if (krb5_storage_write(sp, value.data, value.length) !=
+ (krb5_ssize_t)value.length)
+ ret = errno;
}
- ret = kadm5_log_flush (log_context, sp);
- krb5_storage_free (sp);
- if (ret)
- return ret;
- ret = kadm5_log_end (context);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, value.length);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ krb5_storage_free(sp);
+ krb5_data_free(&value);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
return ret;
}
@@ -276,12 +997,11 @@ kadm5_log_create (kadm5_server_context *context,
* Read the data of a create log record from `sp' and change the
* database.
*/
-
static kadm5_ret_t
-kadm5_log_replay_create (kadm5_server_context *context,
- uint32_t ver,
- uint32_t len,
- krb5_storage *sp)
+kadm5_log_replay_create(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
{
krb5_error_code ret;
krb5_data data;
@@ -289,158 +1009,237 @@ kadm5_log_replay_create (kadm5_server_context *context,
memset(&ent, 0, sizeof(ent));
- ret = krb5_data_alloc (&data, len);
+ ret = krb5_data_alloc(&data, len);
if (ret) {
krb5_set_error_message(context->context, ret, "out of memory");
return ret;
}
- krb5_storage_read (sp, data.data, len);
- ret = hdb_value2entry (context->context, &data, &ent.entry);
+ krb5_storage_read(sp, data.data, len);
+ ret = hdb_value2entry(context->context, &data, &ent.entry);
krb5_data_free(&data);
if (ret) {
krb5_set_error_message(context->context, ret,
- "Unmarshaling hdb entry failed");
+ "Unmarshaling hdb entry in log failed, "
+ "version: %ld", (long)ver);
return ret;
}
ret = context->db->hdb_store(context->context, context->db, 0, &ent);
- hdb_free_entry (context->context, &ent);
+ hdb_free_entry(context->context, &ent);
return ret;
}
/*
* Add a `delete' operation to the log.
*/
-
kadm5_ret_t
-kadm5_log_delete (kadm5_server_context *context,
- krb5_principal princ)
+kadm5_log_delete(kadm5_server_context *context,
+ krb5_principal princ)
{
- krb5_storage *sp;
kadm5_ret_t ret;
- off_t off;
- off_t len;
kadm5_log_context *log_context = &context->log_context;
+ krb5_storage *sp;
+ uint32_t len = 0; /* So dumb compilers don't warn */
+ off_t end_off = 0; /* Ditto; this allows de-indentation by two levels */
+ off_t off;
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return context->db->hdb_remove(context->context, context->db, 0,
+ princ);
+ ret = context->db->hdb_remove(context->context, context->db,
+ HDB_F_PRECHECK, princ);
+ if (ret)
+ return ret;
sp = krb5_storage_emem();
if (sp == NULL)
- return ENOMEM;
- ret = kadm5_log_preamble (context, sp, kadm_delete);
- if (ret)
- goto out;
- ret = krb5_store_int32 (sp, 0);
- if (ret)
- goto out;
- off = krb5_storage_seek (sp, 0, SEEK_CUR);
- ret = krb5_store_principal (sp, princ);
- if (ret)
- goto out;
- len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
- krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
- ret = krb5_store_int32 (sp, len);
- if (ret)
- goto out;
- krb5_storage_seek(sp, len, SEEK_CUR);
- ret = krb5_store_int32 (sp, len);
- if (ret)
- goto out;
- ret = kadm5_log_postamble (log_context, sp);
- if (ret)
- goto out;
- ret = kadm5_log_flush (log_context, sp);
- if (ret)
- goto out;
- ret = kadm5_log_end (context);
-out:
- krb5_storage_free (sp);
+ ret = ENOMEM;
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_delete,
+ log_context->version + 1);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
+ }
+
+ /*
+ * Write a 0 length which we overwrite once we know the length of
+ * the principal name payload.
+ */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_principal(sp, princ);
+ if (ret == 0) {
+ end_off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (end_off == -1)
+ ret = errno;
+ else if (end_off < off)
+ ret = KADM5_LOG_CORRUPT;
+ }
+ if (ret == 0) {
+ /* We wrote sizeof(uint32_t) + payload length bytes */
+ len = (uint32_t)(end_off - off);
+ if (end_off - off != len || len < sizeof(len))
+ ret = KADM5_LOG_CORRUPT;
+ else
+ len -= sizeof(len);
+ }
+ if (ret == 0 && krb5_storage_seek(sp, off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0 && krb5_storage_seek(sp, end_off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+ krb5_storage_free(sp);
return ret;
}
/*
* Read a `delete' log operation from `sp' and apply it.
*/
-
static kadm5_ret_t
-kadm5_log_replay_delete (kadm5_server_context *context,
- uint32_t ver,
- uint32_t len,
- krb5_storage *sp)
+kadm5_log_replay_delete(kadm5_server_context *context,
+ uint32_t ver, uint32_t len, krb5_storage *sp)
{
krb5_error_code ret;
krb5_principal principal;
- ret = krb5_ret_principal (sp, &principal);
+ ret = krb5_ret_principal(sp, &principal);
if (ret) {
krb5_set_error_message(context->context, ret, "Failed to read deleted "
"principal from log version: %ld", (long)ver);
return ret;
}
- ret = context->db->hdb_remove(context->context, context->db, principal);
- krb5_free_principal (context->context, principal);
+ ret = context->db->hdb_remove(context->context, context->db, 0, principal);
+ krb5_free_principal(context->context, principal);
return ret;
}
+static kadm5_ret_t kadm5_log_replay_rename(kadm5_server_context *,
+ uint32_t, uint32_t,
+ krb5_storage *);
+
/*
* Add a `rename' operation to the log.
*/
-
kadm5_ret_t
-kadm5_log_rename (kadm5_server_context *context,
- krb5_principal source,
- hdb_entry *ent)
+kadm5_log_rename(kadm5_server_context *context,
+ krb5_principal source,
+ hdb_entry *entry)
{
krb5_storage *sp;
kadm5_ret_t ret;
+ uint32_t len = 0; /* So dumb compilers don't warn */
+ off_t end_off = 0; /* Ditto; this allows de-indentation by two levels */
off_t off;
- off_t len;
krb5_data value;
+ hdb_entry_ex ent;
kadm5_log_context *log_context = &context->log_context;
- krb5_data_zero(&value);
-
- sp = krb5_storage_emem();
- ret = hdb_entry2value (context->context, ent, &value);
- if (ret)
- goto failed;
-
- ret = kadm5_log_preamble (context, sp, kadm_rename);
- if (ret)
- goto failed;
-
- ret = krb5_store_int32 (sp, 0);
- if (ret)
- goto failed;
- off = krb5_storage_seek (sp, 0, SEEK_CUR);
- ret = krb5_store_principal (sp, source);
- if (ret)
- goto failed;
-
- krb5_storage_write(sp, value.data, value.length);
- len = krb5_storage_seek (sp, 0, SEEK_CUR) - off;
-
- krb5_storage_seek(sp, -(len + 4), SEEK_CUR);
- ret = krb5_store_int32 (sp, len);
- if (ret)
- goto failed;
-
- krb5_storage_seek(sp, len, SEEK_CUR);
- ret = krb5_store_int32 (sp, len);
- if (ret)
- goto failed;
-
- ret = kadm5_log_postamble (log_context, sp);
- if (ret)
- goto failed;
+ memset(&ent, 0, sizeof(ent));
+ ent.ctx = 0;
+ ent.free_entry = 0;
+ ent.entry = *entry;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0) {
+ ret = context->db->hdb_store(context->context, context->db, 0, &ent);
+ if (ret == 0)
+ return context->db->hdb_remove(context->context, context->db, 0,
+ source);
+ return ret;
+ }
- ret = kadm5_log_flush (log_context, sp);
+ /*
+ * Pre-check that the transaction will succeed.
+ *
+ * Note that rename doesn't work to swap a principal's canonical
+ * name with one of its aliases. To make that work would require
+ * adding an hdb_rename() method for renaming principals (there's an
+ * hdb_rename() method already, but for renaming the HDB), which is
+ * ETOOMUCHWORK for the time being.
+ */
+ ret = context->db->hdb_store(context->context, context->db,
+ HDB_F_PRECHECK, &ent);
+ if (ret == 0)
+ ret = context->db->hdb_remove(context->context, context->db,
+ HDB_F_PRECHECK, source);
if (ret)
- goto failed;
- krb5_storage_free (sp);
- krb5_data_free (&value);
+ return ret;
- return kadm5_log_end (context);
+ sp = krb5_storage_emem();
+ krb5_data_zero(&value);
+ if (sp == NULL)
+ ret = ENOMEM;
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_rename,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = hdb_entry2value(context->context, entry, &value);
+ if (ret) {
+ krb5_data_free(&value);
+ krb5_storage_free(sp);
+ return ret;
+ }
-failed:
+ /*
+ * Write a zero length which we'll overwrite once we know the length of the
+ * payload.
+ */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (off == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_principal(sp, source);
+ if (ret == 0) {
+ errno = 0;
+ if (krb5_storage_write(sp, value.data, value.length) !=
+ (krb5_ssize_t)value.length)
+ ret = errno ? errno : EIO;
+ }
+ if (ret == 0) {
+ end_off = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (end_off == -1)
+ ret = errno;
+ else if (end_off < off)
+ ret = KADM5_LOG_CORRUPT;
+ }
+ if (ret == 0) {
+ /* We wrote sizeof(uint32_t) + payload length bytes */
+ len = (uint32_t)(end_off - off);
+ if (end_off - off != len || len < sizeof(len))
+ ret = KADM5_LOG_CORRUPT;
+ else
+ len -= sizeof(len);
+ if (ret == 0 && krb5_storage_seek(sp, off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0 && krb5_storage_seek(sp, end_off, SEEK_SET) == -1)
+ ret = errno;
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+ }
krb5_data_free(&value);
krb5_storage_free(sp);
return ret;
@@ -451,10 +1250,10 @@ failed:
*/
static kadm5_ret_t
-kadm5_log_replay_rename (kadm5_server_context *context,
- uint32_t ver,
- uint32_t len,
- krb5_storage *sp)
+kadm5_log_replay_rename(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
{
krb5_error_code ret;
krb5_principal source;
@@ -466,7 +1265,7 @@ kadm5_log_replay_rename (kadm5_server_context *context,
memset(&target_ent, 0, sizeof(target_ent));
off = krb5_storage_seek(sp, 0, SEEK_CUR);
- ret = krb5_ret_principal (sp, &source);
+ ret = krb5_ret_principal(sp, &source);
if (ret) {
krb5_set_error_message(context->context, ret, "Failed to read renamed "
"principal in log, version: %ld", (long)ver);
@@ -474,79 +1273,96 @@ kadm5_log_replay_rename (kadm5_server_context *context,
}
princ_len = krb5_storage_seek(sp, 0, SEEK_CUR) - off;
data_len = len - princ_len;
- ret = krb5_data_alloc (&value, data_len);
+ ret = krb5_data_alloc(&value, data_len);
if (ret) {
krb5_free_principal (context->context, source);
return ret;
}
- krb5_storage_read (sp, value.data, data_len);
- ret = hdb_value2entry (context->context, &value, &target_ent.entry);
+ krb5_storage_read(sp, value.data, data_len);
+ ret = hdb_value2entry(context->context, &value, &target_ent.entry);
krb5_data_free(&value);
if (ret) {
- krb5_free_principal (context->context, source);
+ krb5_free_principal(context->context, source);
return ret;
}
- ret = context->db->hdb_store (context->context, context->db,
- 0, &target_ent);
- hdb_free_entry (context->context, &target_ent);
+ ret = context->db->hdb_store(context->context, context->db,
+ 0, &target_ent);
+ hdb_free_entry(context->context, &target_ent);
if (ret) {
- krb5_free_principal (context->context, source);
+ krb5_free_principal(context->context, source);
return ret;
}
- ret = context->db->hdb_remove (context->context, context->db, source);
- krb5_free_principal (context->context, source);
+ ret = context->db->hdb_remove(context->context, context->db, 0, source);
+ krb5_free_principal(context->context, source);
+
return ret;
}
-
/*
* Add a `modify' operation to the log.
*/
-
kadm5_ret_t
-kadm5_log_modify (kadm5_server_context *context,
- hdb_entry *ent,
- uint32_t mask)
+kadm5_log_modify(kadm5_server_context *context,
+ hdb_entry *entry,
+ uint32_t mask)
{
krb5_storage *sp;
kadm5_ret_t ret;
krb5_data value;
uint32_t len;
+ hdb_entry_ex ent;
kadm5_log_context *log_context = &context->log_context;
- krb5_data_zero(&value);
+ memset(&ent, 0, sizeof(ent));
+ ent.ctx = 0;
+ ent.free_entry = 0;
+ ent.entry = *entry;
- sp = krb5_storage_emem();
- ret = hdb_entry2value (context->context, ent, &value);
- if (ret)
- goto failed;
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return context->db->hdb_store(context->context, context->db,
+ HDB_F_REPLACE, &ent);
- ret = kadm5_log_preamble (context, sp, kadm_modify);
+ ret = context->db->hdb_store(context->context, context->db,
+ HDB_F_PRECHECK | HDB_F_REPLACE, &ent);
if (ret)
- goto failed;
+ return ret;
- len = value.length + 4;
- ret = krb5_store_int32 (sp, len);
- if (ret)
- goto failed;
- ret = krb5_store_int32 (sp, mask);
- if (ret)
- goto failed;
- krb5_storage_write (sp, value.data, value.length);
+ sp = krb5_storage_emem();
+ krb5_data_zero(&value);
+ if (sp == NULL)
+ ret = ENOMEM;
+ if (ret == 0)
+ ret = hdb_entry2value(context->context, entry, &value);
+ if (ret) {
+ krb5_data_free(&value);
+ krb5_storage_free(sp);
+ return ret;
+ }
- ret = krb5_store_int32 (sp, len);
- if (ret)
- goto failed;
- ret = kadm5_log_postamble (log_context, sp);
- if (ret)
- goto failed;
- ret = kadm5_log_flush (log_context, sp);
- if (ret)
- goto failed;
- krb5_data_free(&value);
- krb5_storage_free (sp);
- return kadm5_log_end (context);
-failed:
+ len = value.length + sizeof(len);
+ if (value.length > len || len > INT32_MAX)
+ ret = E2BIG;
+ if (ret == 0)
+ ret = kadm5_log_preamble(context, sp, kadm_modify,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, mask);
+ if (ret == 0) {
+ if (krb5_storage_write(sp, value.data, value.length) !=
+ (krb5_ssize_t)value.length)
+ ret = errno;
+ }
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, len);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp,
+ log_context->version + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+ if (ret == 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
krb5_data_free(&value);
krb5_storage_free(sp);
return ret;
@@ -555,28 +1371,33 @@ failed:
/*
* Read a `modify' log operation from `sp' and apply it.
*/
-
static kadm5_ret_t
-kadm5_log_replay_modify (kadm5_server_context *context,
- uint32_t ver,
- uint32_t len,
- krb5_storage *sp)
+kadm5_log_replay_modify(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
{
krb5_error_code ret;
- int32_t mask;
+ uint32_t mask;
krb5_data value;
hdb_entry_ex ent, log_ent;
memset(&log_ent, 0, sizeof(log_ent));
- krb5_ret_int32 (sp, &mask);
+ ret = krb5_ret_uint32(sp, &mask);
+ if (ret)
+ return ret;
len -= 4;
ret = krb5_data_alloc (&value, len);
if (ret) {
krb5_set_error_message(context->context, ret, "out of memory");
return ret;
}
- krb5_storage_read (sp, value.data, len);
+ errno = 0;
+ if (krb5_storage_read (sp, value.data, len) != (krb5_ssize_t)len) {
+ ret = errno ? errno : EIO;
+ return ret;
+ }
ret = hdb_value2entry (context->context, &value, &log_ent.entry);
krb5_data_free(&value);
if (ret)
@@ -585,7 +1406,8 @@ kadm5_log_replay_modify (kadm5_server_context *context,
memset(&ent, 0, sizeof(ent));
ret = context->db->hdb_fetch_kvno(context->context, context->db,
log_ent.entry.principal,
- HDB_F_DECRYPT|HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
+ HDB_F_DECRYPT|HDB_F_ALL_KVNOS|
+ HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if (ret)
goto out;
if (mask & KADM5_PRINC_EXPIRE_TIME) {
@@ -619,7 +1441,8 @@ kadm5_log_replay_modify (kadm5_server_context *context,
}
}
if (mask & KADM5_LAST_PWD_CHANGE) {
- abort (); /* XXX */
+ krb5_warnx (context->context,
+ "Unimplemented mask KADM5_LAST_PWD_CHANGE");
}
if (mask & KADM5_ATTRIBUTES) {
ent.entry.flags = log_ent.entry.flags;
@@ -659,16 +1482,14 @@ kadm5_log_replay_modify (kadm5_server_context *context,
ent.entry.kvno = log_ent.entry.kvno;
}
if (mask & KADM5_MKVNO) {
- abort (); /* XXX */
+ krb5_warnx(context->context, "Unimplemented mask KADM5_KVNO");
}
if (mask & KADM5_AUX_ATTRIBUTES) {
- abort (); /* XXX */
- }
- if (mask & KADM5_POLICY) {
- abort (); /* XXX */
+ krb5_warnx(context->context,
+ "Unimplemented mask KADM5_AUX_ATTRIBUTES");
}
if (mask & KADM5_POLICY_CLR) {
- abort (); /* XXX */
+ krb5_warnx(context->context, "Unimplemented mask KADM5_POLICY_CLR");
}
if (mask & KADM5_MAX_RLIFE) {
if (log_ent.entry.max_renew == NULL) {
@@ -686,18 +1507,28 @@ kadm5_log_replay_modify (kadm5_server_context *context,
}
}
if (mask & KADM5_LAST_SUCCESS) {
- abort (); /* XXX */
+ krb5_warnx(context->context, "Unimplemented mask KADM5_LAST_SUCCESS");
}
if (mask & KADM5_LAST_FAILED) {
- abort (); /* XXX */
+ krb5_warnx(context->context, "Unimplemented mask KADM5_LAST_FAILED");
}
if (mask & KADM5_FAIL_AUTH_COUNT) {
- abort (); /* XXX */
+ krb5_warnx(context->context,
+ "Unimplemented mask KADM5_FAIL_AUTH_COUNT");
}
if (mask & KADM5_KEY_DATA) {
size_t num;
size_t i;
+ /*
+ * We don't need to do anything about key history here because
+ * the log entry contains a complete entry, including hdb
+ * extensions. We do need to make sure that KADM5_TL_DATA is in
+ * the mask though, since that's what it takes to update the
+ * extensions (see below).
+ */
+ mask |= KADM5_TL_DATA;
+
for (i = 0; i < ent.entry.keys.len; ++i)
free_Key(&ent.entry.keys.val[i]);
free (ent.entry.keys.val);
@@ -708,7 +1539,8 @@ kadm5_log_replay_modify (kadm5_server_context *context,
ent.entry.keys.val = malloc(len * sizeof(*ent.entry.keys.val));
if (ent.entry.keys.val == NULL) {
krb5_set_error_message(context->context, ENOMEM, "out of memory");
- return ENOMEM;
+ ret = ENOMEM;
+ goto out;
}
for (i = 0; i < ent.entry.keys.len; ++i) {
ret = copy_Key(&log_ent.entry.keys.val[i],
@@ -742,179 +1574,652 @@ kadm5_log_replay_modify (kadm5_server_context *context,
ret = context->db->hdb_store(context->context, context->db,
HDB_F_REPLACE, &ent);
out:
- hdb_free_entry (context->context, &ent);
- hdb_free_entry (context->context, &log_ent);
+ hdb_free_entry(context->context, &ent);
+ hdb_free_entry(context->context, &log_ent);
return ret;
}
/*
- * Add a `nop' operation to the log. Does not close the log.
+ * Update the first entry (which should be a `nop'), the "uber-entry".
*/
+static kadm5_ret_t
+log_update_uber(kadm5_server_context *context, off_t off)
+{
+ kadm5_log_context *log_context = &context->log_context;
+ kadm5_ret_t ret = 0;
+ krb5_storage *sp, *mem_sp;
+ krb5_data data;
+ uint32_t op, len;
+ ssize_t bytes;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return 0;
+
+ if (log_context->read_only)
+ return EROFS;
+
+ krb5_data_zero(&data);
+ mem_sp = krb5_storage_emem();
+ if (mem_sp == NULL)
+ return ENOMEM;
+
+ sp = krb5_storage_from_fd(log_context->log_fd);
+ if (sp == NULL) {
+ krb5_storage_free(mem_sp);
+ return ENOMEM;
+ }
+
+ /* Skip first entry's version and timestamp */
+ if (krb5_storage_seek(sp, 8, SEEK_SET) == -1) {
+ ret = errno;
+ goto out;
+ }
+
+ /* If the first entry is not a nop, there's nothing we can do here */
+ ret = krb5_ret_uint32(sp, &op);
+ if (ret || op != kadm_nop)
+ goto out;
+
+ /* If the first entry is not a 16-byte nop, ditto */
+ ret = krb5_ret_uint32(sp, &len);
+ if (ret || len != LOG_UBER_LEN)
+ goto out;
+
+ /*
+ * Try to make the writes here as close to atomic as possible: a
+ * single write() call.
+ */
+ ret = krb5_store_uint64(mem_sp, off);
+ if (ret)
+ goto out;
+ ret = krb5_store_uint32(mem_sp, log_context->last_time);
+ if (ret)
+ goto out;
+ ret = krb5_store_uint32(mem_sp, log_context->version);
+ if (ret)
+ goto out;
+
+ krb5_storage_to_data(mem_sp, &data);
+ bytes = krb5_storage_write(sp, data.data, data.length);
+ if (bytes < 0)
+ ret = errno;
+ else if (bytes != data.length)
+ ret = EIO;
+
+ /*
+ * We don't fsync() this write because we can recover if the write
+ * doesn't complete, though for now we don't have code for properly
+ * dealing with the offset not getting written completely.
+ *
+ * We should probably have two copies of the offset so we can use
+ * one copy to verify the other, and when they don't match we could
+ * traverse the whole log forwards, replaying just the last entry.
+ */
+
+out:
+ if (ret == 0)
+ kadm5_log_signal_master(context);
+ krb5_data_free(&data);
+ krb5_storage_free(sp);
+ krb5_storage_free(mem_sp);
+ if (lseek(log_context->log_fd, off, SEEK_SET) == -1)
+ ret = ret ? ret : errno;
+
+ return ret;
+}
+
+/*
+ * Add a `nop' operation to the log. Does not close the log.
+ */
kadm5_ret_t
-kadm5_log_nop (kadm5_server_context *context)
+kadm5_log_nop(kadm5_server_context *context, enum kadm_nop_type nop_type)
{
krb5_storage *sp;
kadm5_ret_t ret;
kadm5_log_context *log_context = &context->log_context;
+ off_t off;
+ uint32_t vno = log_context->version;
+
+ if (strcmp(log_context->log_file, "/dev/null") == 0)
+ return 0;
+
+ off = lseek(log_context->log_fd, 0, SEEK_CUR);
+ if (off == -1)
+ return errno;
sp = krb5_storage_emem();
- ret = kadm5_log_preamble (context, sp, kadm_nop);
- if (ret) {
- krb5_storage_free (sp);
- return ret;
- }
- krb5_store_int32 (sp, 0);
- krb5_store_int32 (sp, 0);
- ret = kadm5_log_postamble (log_context, sp);
- if (ret) {
- krb5_storage_free (sp);
- return ret;
+ ret = kadm5_log_preamble(context, sp, kadm_nop, off == 0 ? 0 : vno + 1);
+ if (ret)
+ goto out;
+
+ if (off == 0) {
+ /*
+ * First entry (uber-entry) gets room for offset of next new
+ * entry and time and version of last entry.
+ */
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN);
+ /* These get overwritten with the same values below */
+ if (ret == 0)
+ ret = krb5_store_uint64(sp, LOG_UBER_SZ);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, log_context->last_time);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, vno);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN);
+ } else if (nop_type == kadm_nop_plain) {
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0);
+ } else {
+ ret = krb5_store_uint32(sp, sizeof(uint32_t));
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, nop_type);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, sizeof(uint32_t));
}
- ret = kadm5_log_flush (log_context, sp);
- krb5_storage_free (sp);
+ if (ret == 0)
+ ret = kadm5_log_postamble(log_context, sp, off == 0 ? 0 : vno + 1);
+ if (ret == 0)
+ ret = kadm5_log_flush(context, sp);
+
+ if (ret == 0 && off == 0 && nop_type != kadm_nop_plain)
+ ret = kadm5_log_nop(context, nop_type);
+
+ if (ret == 0 && off != 0)
+ ret = kadm5_log_recover(context, kadm_recover_commit);
+
+out:
+ krb5_storage_free(sp);
return ret;
}
/*
- * Read a `nop' log operation from `sp' and apply it.
+ * Read a `nop' log operation from `sp' and "apply" it (there's nothing
+ * to do).
+ *
+ * FIXME Actually, if the nop payload is 4 bytes and contains an enum
+ * kadm_nop_type value of kadm_nop_trunc then we should truncate the
+ * log, and if it contains a kadm_nop_close then we should rename a new
+ * log into place. However, this is not implemented yet.
*/
-
static kadm5_ret_t
-kadm5_log_replay_nop (kadm5_server_context *context,
- uint32_t ver,
- uint32_t len,
- krb5_storage *sp)
+kadm5_log_replay_nop(kadm5_server_context *context,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
{
return 0;
}
+struct replay_cb_data {
+ size_t count;
+ uint32_t ver;
+ enum kadm_recover_mode mode;
+};
+
+
/*
- * Call `func' for each log record in the log in `context'
+ * Recover or perform the initial commit of an unconfirmed log entry
*/
+static kadm5_ret_t
+recover_replay(kadm5_server_context *context,
+ uint32_t ver, time_t timestamp, enum kadm_ops op,
+ uint32_t len, krb5_storage *sp, void *ctx)
+{
+ struct replay_cb_data *data = ctx;
+ kadm5_ret_t ret;
+ off_t off;
+
+ /* On initial commit there must be just one pending unconfirmed entry */
+ if (data->count > 0 && data->mode == kadm_recover_commit)
+ return KADM5_LOG_CORRUPT;
+
+ /* We're at the start of the payload; compute end of entry offset */
+ off = krb5_storage_seek(sp, 0, SEEK_CUR) + len + LOG_TRAILER_SZ;
+
+ /* We cannot perform log recovery on LDAP and such backends */
+ if (data->mode == kadm_recover_replay &&
+ (context->db->hdb_capability_flags & HDB_CAP_F_SHARED_DIRECTORY))
+ ret = 0;
+ else
+ ret = kadm5_log_replay(context, op, ver, len, sp);
+ switch (ret) {
+ case HDB_ERR_NOENTRY:
+ case HDB_ERR_EXISTS:
+ if (data->mode != kadm_recover_replay)
+ return ret;
+ case 0:
+ break;
+ case KADM5_LOG_CORRUPT:
+ return -1;
+ default:
+ krb5_warn(context->context, ret, "unexpected error while replaying");
+ return -1;
+ }
+ data->count++;
+ data->ver = ver;
+
+ /*
+ * With replay we may be making multiple HDB changes. We must sync the
+ * confirmation of each one before moving on to the next. Otherwise, we
+ * might attempt to replay multiple already applied updates, and this may
+ * introduce unintended intermediate states or fail to yield the same final
+ * result.
+ */
+ kadm5_log_set_version(context, ver);
+ ret = log_update_uber(context, off);
+ if (ret == 0 && data->mode != kadm_recover_commit)
+ ret = krb5_storage_fsync(sp);
+ return ret;
+}
+
kadm5_ret_t
-kadm5_log_foreach (kadm5_server_context *context,
- void (*func)(kadm5_server_context *server_context,
- uint32_t ver,
- time_t timestamp,
- enum kadm_ops op,
- uint32_t len,
- krb5_storage *,
- void *),
- void *ctx)
+kadm5_log_recover(kadm5_server_context *context, enum kadm_recover_mode mode)
{
+ kadm5_ret_t ret;
+ krb5_storage *sp;
+ struct replay_cb_data replay_data;
+
+ replay_data.count = 0;
+ replay_data.ver = 0;
+ replay_data.mode = mode;
+
+ sp = kadm5_log_goto_end(context, context->log_context.log_fd);
+ if (sp == NULL)
+ return errno ? errno : EIO;
+
+ ret = kadm5_log_foreach(context, kadm_forward | kadm_unconfirmed,
+ NULL, recover_replay, &replay_data);
+ if (ret == 0 && mode == kadm_recover_commit && replay_data.count != 1)
+ ret = KADM5_LOG_CORRUPT;
+ krb5_storage_free(sp);
+ return ret;
+}
+
+/*
+ * Call `func' for each log record in the log in `context'.
+ *
+ * `func' is optional.
+ *
+ * If `func' returns -1 then log traversal terminates and this returns 0.
+ * Otherwise `func''s return is returned if there are no other errors.
+ */
+kadm5_ret_t
+kadm5_log_foreach(kadm5_server_context *context,
+ enum kadm_iter_opts iter_opts,
+ off_t *off_lastp,
+ kadm5_ret_t (*func)(kadm5_server_context *server_context,
+ uint32_t ver, time_t timestamp,
+ enum kadm_ops op, uint32_t len,
+ krb5_storage *sp, void *ctx),
+ void *ctx)
+{
+ kadm5_ret_t ret = 0;
int fd = context->log_context.log_fd;
krb5_storage *sp;
+ off_t off_last;
+ off_t this_entry = 0;
+ off_t log_end = 0;
+
+ if (strcmp(context->log_context.log_file, "/dev/null") == 0)
+ return 0;
+
+ if (off_lastp == NULL)
+ off_lastp = &off_last;
+ *off_lastp = -1;
+
+ if (((iter_opts & kadm_forward) && (iter_opts & kadm_backward)) ||
+ (!(iter_opts & kadm_confirmed) && !(iter_opts & kadm_unconfirmed)))
+ return EINVAL;
+
+ if ((iter_opts & kadm_forward) && (iter_opts & kadm_confirmed) &&
+ (iter_opts & kadm_unconfirmed)) {
+ /*
+ * We want to traverse all log entries, confirmed or not, from
+ * the start, then there's no need to kadm5_log_goto_end()
+ * -- no reason to try to find the end.
+ */
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ return errno;
+
+ log_end = krb5_storage_seek(sp, 0, SEEK_END);
+ if (log_end == -1 ||
+ krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_storage_free(sp);
+ return ret;
+ }
+ } else {
+ /* Get the end of the log based on the uber entry */
+ sp = kadm5_log_goto_end(context, fd);
+ if (sp == NULL)
+ return errno;
+ log_end = krb5_storage_seek(sp, 0, SEEK_CUR);
+ }
- lseek (fd, 0, SEEK_SET);
- sp = krb5_storage_from_fd (fd);
- for (;;) {
- int32_t ver, timestamp, op, len, len2, ver2;
+ *off_lastp = log_end;
+
+ if ((iter_opts & kadm_forward) && (iter_opts & kadm_confirmed)) {
+ /* Start at the beginning */
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_storage_free(sp);
+ return ret;
+ }
+ } else if ((iter_opts & kadm_backward) && (iter_opts & kadm_unconfirmed)) {
+ /*
+ * We're at the confirmed end but need to be at the unconfirmed
+ * end. Skip forward to the real end, re-entering to do it.
+ */
+ ret = kadm5_log_foreach(context, kadm_forward | kadm_unconfirmed,
+ &log_end, NULL, NULL);
+ if (ret)
+ return ret;
+ if (krb5_storage_seek(sp, log_end, SEEK_SET) == -1) {
+ ret = errno;
+ krb5_storage_free(sp);
+ return ret;
+ }
+ }
- if(krb5_ret_int32 (sp, &ver) != 0)
+ for (;;) {
+ uint32_t ver, ver2, len, len2;
+ uint32_t tstamp;
+ time_t timestamp;
+ enum kadm_ops op;
+
+ if ((iter_opts & kadm_backward)) {
+ off_t o;
+
+ o = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (o == 0 ||
+ ((iter_opts & kadm_unconfirmed) && o <= *off_lastp))
+ break;
+ ret = kadm5_log_previous(context->context, sp, &ver,
+ &timestamp, &op, &len);
+ if (ret)
+ break;
+
+ /* Offset is now at payload of current entry */
+
+ o = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (o == -1) {
+ ret = errno;
+ break;
+ }
+ this_entry = o - LOG_HEADER_SZ;
+ if (this_entry < 0) {
+ ret = KADM5_LOG_CORRUPT;
+ break;
+ }
+ } else {
+ /* Offset is now at start of current entry, read header */
+ this_entry = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (!(iter_opts & kadm_unconfirmed) && this_entry == log_end)
+ break;
+ ret = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
+ if (ret == HEIM_ERR_EOF) {
+ ret = 0;
+ break;
+ }
+ timestamp = tstamp;
+ if (ret)
+ break;
+ /* Offset is now at payload of current entry */
+ }
+
+ /* Validate trailer before calling the callback */
+ if (krb5_storage_seek(sp, len, SEEK_CUR) == -1) {
+ ret = errno;
+ break;
+ }
+
+ ret = krb5_ret_uint32(sp, &len2);
+ if (ret)
+ break;
+ ret = krb5_ret_uint32(sp, &ver2);
+ if (ret)
+ break;
+ if (len != len2 || ver != ver2) {
+ ret = KADM5_LOG_CORRUPT;
break;
- krb5_ret_int32 (sp, &timestamp);
- krb5_ret_int32 (sp, &op);
- krb5_ret_int32 (sp, &len);
- (*func)(context, ver, timestamp, op, len, sp, ctx);
- krb5_ret_int32 (sp, &len2);
- krb5_ret_int32 (sp, &ver2);
- if (len != len2)
- abort();
- if (ver != ver2)
- abort();
+ }
+
+ /* Rewind to start of payload and call callback if we have one */
+ if (krb5_storage_seek(sp, this_entry + LOG_HEADER_SZ,
+ SEEK_SET) == -1) {
+ ret = errno;
+ break;
+ }
+
+ if (func != NULL) {
+ ret = (*func)(context, ver, timestamp, op, len, sp, ctx);
+ if (ret) {
+ /* Callback signals desire to stop by returning -1 */
+ if (ret == -1)
+ ret = 0;
+ break;
+ }
+ }
+ if ((iter_opts & kadm_forward)) {
+ off_t o;
+
+ o = krb5_storage_seek(sp, this_entry+LOG_WRAPPER_SZ+len, SEEK_SET);
+ if (o == -1) {
+ ret = errno;
+ break;
+ }
+ if (o > log_end)
+ *off_lastp = o;
+ } else if ((iter_opts & kadm_backward)) {
+ /*
+ * Rewind to the start of this entry so kadm5_log_previous()
+ * can find the previous one.
+ */
+ if (krb5_storage_seek(sp, this_entry, SEEK_SET) == -1) {
+ ret = errno;
+ break;
+ }
+ }
+ }
+ if ((ret == HEIM_ERR_EOF || ret == KADM5_LOG_CORRUPT) &&
+ (iter_opts & kadm_forward) &&
+ context->log_context.lock_mode == LOCK_EX) {
+ /*
+ * Truncate partially written last log entry so we can write
+ * again.
+ */
+ ret = krb5_storage_truncate(sp, this_entry);
+ if (ret == 0 &&
+ krb5_storage_seek(sp, this_entry, SEEK_SET) == -1)
+ ret = errno;
+ krb5_warnx(context->context, "Truncating log at partial or "
+ "corrupt %s entry",
+ this_entry > log_end ? "unconfirmed" : "confirmed");
}
krb5_storage_free(sp);
- return 0;
+ return ret;
+}
+
+/*
+ * Go to the second record, which, if we have an uber record, will be
+ * the first record.
+ */
+static krb5_storage *
+log_goto_first(kadm5_server_context *server_context, int fd)
+{
+ krb5_storage *sp;
+ enum kadm_ops op;
+ uint32_t ver, len;
+ kadm5_ret_t ret;
+
+ if (fd == -1) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ return NULL;
+
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1)
+ return NULL;
+
+ ret = get_header(sp, LOG_DOPEEK, &ver, NULL, &op, &len);
+ if (ret) {
+ krb5_storage_free(sp);
+ errno = ret;
+ return NULL;
+ }
+ if (op == kadm_nop && len == LOG_UBER_LEN && seek_next(sp) == -1) {
+ krb5_storage_free(sp);
+ return NULL;
+ }
+ return sp;
}
/*
* Go to end of log.
+ *
+ * XXX This really needs to return a kadm5_ret_t and either output a
+ * krb5_storage * via an argument, or take one as input.
*/
krb5_storage *
-kadm5_log_goto_end (int fd)
+kadm5_log_goto_end(kadm5_server_context *server_context, int fd)
{
+ krb5_error_code ret = 0;
krb5_storage *sp;
+ enum kadm_ops op;
+ uint32_t ver, len;
+ uint32_t tstamp;
+ uint64_t off;
+
+ if (fd == -1) {
+ errno = EINVAL;
+ return NULL;
+ }
- sp = krb5_storage_from_fd (fd);
- krb5_storage_seek(sp, 0, SEEK_END);
+ sp = krb5_storage_from_fd(fd);
+ if (sp == NULL)
+ return NULL;
+
+ if (krb5_storage_seek(sp, 0, SEEK_SET) == -1) {
+ ret = errno;
+ goto fail;
+ }
+ ret = get_header(sp, LOG_NOPEEK, &ver, &tstamp, &op, &len);
+ if (ret == HEIM_ERR_EOF) {
+ (void) krb5_storage_seek(sp, 0, SEEK_SET);
+ return sp;
+ }
+ if (ret == KADM5_LOG_CORRUPT)
+ goto truncate;
+ if (ret)
+ goto fail;
+
+ if (op == kadm_nop && len == LOG_UBER_LEN) {
+ /* New style log */
+ ret = krb5_ret_uint64(sp, &off);
+ if (ret)
+ goto truncate;
+
+ if (krb5_storage_seek(sp, off, SEEK_SET) == -1)
+ goto fail;
+
+ if (off >= LOG_UBER_SZ) {
+ ret = get_version_prev(sp, &ver, NULL);
+ if (ret == 0)
+ return sp;
+ }
+ /* Invalid offset in uber entry */
+ goto truncate;
+ }
+
+ /* Old log with no uber entry */
+ if (krb5_storage_seek(sp, 0, SEEK_END) == -1) {
+ static int warned = 0;
+ if (!warned) {
+ warned = 1;
+ krb5_warnx(server_context->context,
+ "Old log found; truncate it to upgrade");
+ }
+ }
+ ret = get_version_prev(sp, &ver, NULL);
+ if (ret)
+ goto truncate;
return sp;
+
+truncate:
+ /* If we can, truncate */
+ if (server_context->log_context.lock_mode == LOCK_EX) {
+ ret = kadm5_log_reinit(server_context, 0);
+ if (ret == 0) {
+ krb5_warn(server_context->context, ret,
+ "Invalid log; truncating to recover");
+ if (krb5_storage_seek(sp, 0, SEEK_END) == -1)
+ return NULL;
+ return sp;
+ }
+ }
+ krb5_warn(server_context->context, ret,
+ "Invalid log; truncate to recover");
+
+fail:
+ errno = ret;
+ krb5_storage_free(sp);
+ return NULL;
}
/*
* Return previous log entry.
*
- * The pointer in `sp´ is assumed to be at the top of the entry before
- * previous entry. On success, the `sp´ pointer is set to data portion
- * of previous entry. In case of error, it's not changed at all.
+ * The pointer in `sp' is assumed to be at the top of the entry after
+ * previous entry (e.g., at EOF). On success, the `sp' pointer is set to
+ * data portion of previous entry. In case of error, it's not changed
+ * at all.
*/
-
kadm5_ret_t
-kadm5_log_previous (krb5_context context,
- krb5_storage *sp,
- uint32_t *ver,
- time_t *timestamp,
- enum kadm_ops *op,
- uint32_t *len)
+kadm5_log_previous(krb5_context context,
+ krb5_storage *sp,
+ uint32_t *verp,
+ time_t *tstampp,
+ enum kadm_ops *opp,
+ uint32_t *lenp)
{
krb5_error_code ret;
- off_t off, oldoff;
- int32_t tmp;
+ off_t oldoff;
+ uint32_t ver2, len2;
+ uint32_t tstamp;
oldoff = krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (oldoff == -1)
+ goto log_corrupt;
- krb5_storage_seek(sp, -8, SEEK_CUR);
- ret = krb5_ret_int32 (sp, &tmp);
- if (ret)
- goto end_of_storage;
- *len = tmp;
- ret = krb5_ret_int32 (sp, &tmp);
- if (ret)
- goto end_of_storage;
- *ver = tmp;
- off = 24 + *len;
- krb5_storage_seek(sp, -off, SEEK_CUR);
- ret = krb5_ret_int32 (sp, &tmp);
- if (ret)
- goto end_of_storage;
- if ((uint32_t)tmp != *ver) {
- krb5_storage_seek(sp, oldoff, SEEK_SET);
- krb5_set_error_message(context, KADM5_BAD_DB,
- "kadm5_log_previous: log entry "
- "have consistency failure, version number wrong "
- "(tmp %lu ver %lu)",
- (unsigned long)tmp,
- (unsigned long)*ver);
- return KADM5_BAD_DB;
- }
- ret = krb5_ret_int32 (sp, &tmp);
- if (ret)
- goto end_of_storage;
- *timestamp = tmp;
- ret = krb5_ret_int32 (sp, &tmp);
- if (ret)
- goto end_of_storage;
- *op = tmp;
- ret = krb5_ret_int32 (sp, &tmp);
- if (ret)
- goto end_of_storage;
- if ((uint32_t)tmp != *len) {
- krb5_storage_seek(sp, oldoff, SEEK_SET);
- krb5_set_error_message(context, KADM5_BAD_DB,
- "kadm5_log_previous: log entry "
- "have consistency failure, length wrong");
- return KADM5_BAD_DB;
+ /* This reads the physical version of the uber record */
+ if (seek_prev(sp, verp, lenp) == -1)
+ goto log_corrupt;
+
+ ret = get_header(sp, LOG_NOPEEK, &ver2, &tstamp, opp, &len2);
+ if (ret) {
+ (void) krb5_storage_seek(sp, oldoff, SEEK_SET);
+ return ret;
}
+ if (tstampp)
+ *tstampp = tstamp;
+ if (ver2 != *verp || len2 != *lenp)
+ goto log_corrupt;
+
return 0;
- end_of_storage:
- krb5_storage_seek(sp, oldoff, SEEK_SET);
- krb5_set_error_message(context, ret, "kadm5_log_previous: end of storage "
- "reached before end");
- return ret;
+log_corrupt:
+ (void) krb5_storage_seek(sp, oldoff, SEEK_SET);
+ return KADM5_LOG_CORRUPT;
}
/*
@@ -922,65 +2227,367 @@ kadm5_log_previous (krb5_context context,
*/
kadm5_ret_t
-kadm5_log_replay (kadm5_server_context *context,
- enum kadm_ops op,
- uint32_t ver,
- uint32_t len,
- krb5_storage *sp)
+kadm5_log_replay(kadm5_server_context *context,
+ enum kadm_ops op,
+ uint32_t ver,
+ uint32_t len,
+ krb5_storage *sp)
{
switch (op) {
case kadm_create :
- return kadm5_log_replay_create (context, ver, len, sp);
+ return kadm5_log_replay_create(context, ver, len, sp);
case kadm_delete :
- return kadm5_log_replay_delete (context, ver, len, sp);
+ return kadm5_log_replay_delete(context, ver, len, sp);
case kadm_rename :
- return kadm5_log_replay_rename (context, ver, len, sp);
+ return kadm5_log_replay_rename(context, ver, len, sp);
case kadm_modify :
- return kadm5_log_replay_modify (context, ver, len, sp);
+ return kadm5_log_replay_modify(context, ver, len, sp);
case kadm_nop :
- return kadm5_log_replay_nop (context, ver, len, sp);
+ return kadm5_log_replay_nop(context, ver, len, sp);
default :
+ /*
+ * FIXME This default arm makes it difficult to add new kadm_ops
+ * values.
+ */
krb5_set_error_message(context->context, KADM5_FAILURE,
"Unsupported replay op %d", (int)op);
+ (void) krb5_storage_seek(sp, len, SEEK_CUR);
return KADM5_FAILURE;
}
}
+struct load_entries_data {
+ krb5_data *entries;
+ unsigned char *p;
+ uint32_t first;
+ uint32_t last;
+ size_t bytes;
+ size_t nentries;
+ size_t maxbytes;
+ size_t maxentries;
+};
+
+
/*
- * truncate the log - i.e. create an empty file with just (nop vno + 2)
+ * Prepend one entry with header and trailer to the entry buffer, stopping when
+ * we've reached either of the byte or entry-count limits (if non-zero).
+ *
+ * This is a two-pass algorithm:
+ *
+ * In the first pass, when entries->entries == NULL, we compute the space
+ * required, and count the entries that fit up from zero.
+ *
+ * In the second pass we fill the buffer, and count the entries back down to
+ * zero. The space used must be an exact fit, and the number of entries must
+ * reach zero at that point or an error is returned.
+ *
+ * The caller MUST check that entries->nentries == 0 at the end of the second
+ * pass.
*/
+static kadm5_ret_t
+load_entries_cb(kadm5_server_context *server_context,
+ uint32_t ver,
+ time_t timestamp,
+ enum kadm_ops op,
+ uint32_t len,
+ krb5_storage *sp,
+ void *ctx)
+{
+ struct load_entries_data *entries = ctx;
+ kadm5_ret_t ret;
+ ssize_t bytes;
+ size_t entry_len = len + LOG_WRAPPER_SZ;
+ unsigned char *base;
+
+ if (entries->entries == NULL) {
+ size_t total = entries->bytes + entry_len;
+
+ /*
+ * First run: find the size of krb5_data buffer needed.
+ *
+ * If the log was huge we'd have to perhaps open a temp file for this.
+ * For now KISS.
+ */
+ if ((op == kadm_nop && entry_len == LOG_UBER_SZ) ||
+ entry_len < len /*overflow?*/ ||
+ (entries->maxbytes > 0 && total > entries->maxbytes) ||
+ total < entries->bytes /*overflow?*/ ||
+ (entries->maxentries > 0 && entries->nentries == entries->maxentries))
+ return -1; /* stop iteration */
+ entries->bytes = total;
+ entries->first = ver;
+ if (entries->nentries++ == 0)
+ entries->last = ver;
+ return 0;
+ }
-kadm5_ret_t
-kadm5_log_truncate (kadm5_server_context *server_context)
+ /* Second run: load the data into memory */
+ base = (unsigned char *)entries->entries->data;
+ if (entries->p - base < entry_len && entries->p != base) {
+ /*
+ * This can't happen normally: we stop the log record iteration
+ * above before we get here. This could happen if someone wrote
+ * garbage to the log while we were traversing it. We return an
+ * error instead of asserting.
+ */
+ return KADM5_LOG_CORRUPT;
+ }
+
+ /*
+ * sp here is a krb5_storage_from_fd() of the log file, and the
+ * offset pointer points at the current log record payload.
+ *
+ * Seek back to the start of the record poayload so we can read the
+ * whole record.
+ */
+ if (krb5_storage_seek(sp, -LOG_HEADER_SZ, SEEK_CUR) == -1)
+ return errno;
+
+ /*
+ * We read the header, payload, and trailer into the buffer we have, that
+ * many bytes before the previous record we read.
+ */
+ errno = 0;
+ bytes = krb5_storage_read(sp, entries->p - entry_len, entry_len);
+ ret = errno;
+ if (bytes < 0 || bytes != entry_len)
+ return ret ? ret : EIO;
+
+ entries->first = ver;
+ --entries->nentries;
+ entries->p -= entry_len;
+ return (entries->p == base) ? -1 : 0;
+}
+
+
+/*
+ * Serialize a tail fragment of the log as a krb5_data, this is constrained to
+ * at most `maxbytes' bytes and to at most `maxentries' entries if not zero.
+ */
+static kadm5_ret_t
+load_entries(kadm5_server_context *context, krb5_data *p,
+ size_t maxentries, size_t maxbytes,
+ uint32_t *first, uint32_t *last)
{
+ struct load_entries_data entries;
kadm5_ret_t ret;
- uint32_t vno;
+ unsigned char *base;
- ret = kadm5_log_init (server_context);
- if (ret)
- return ret;
+ krb5_data_zero(p);
- ret = kadm5_log_get_version (server_context, &vno);
- if (ret)
- return ret;
+ *first = 0;
- ret = kadm5_log_reinit (server_context);
- if (ret)
- return ret;
+ memset(&entries, 0, sizeof(entries));
+ entries.entries = NULL;
+ entries.p = NULL;
+ entries.maxentries = maxentries;
+ entries.maxbytes = maxbytes;
- ret = kadm5_log_set_version (server_context, vno);
+ /* Figure out how many bytes it will take */
+ ret = kadm5_log_foreach(context, kadm_backward | kadm_confirmed,
+ NULL, load_entries_cb, &entries);
if (ret)
- return ret;
+ return ret;
+
+ /*
+ * If no entries fit our limits, we do not truncate, instead the caller can
+ * call kadm5_log_reinit() if desired.
+ */
+ if (entries.bytes == 0)
+ return 0;
- ret = kadm5_log_nop (server_context);
+ ret = krb5_data_alloc(p, entries.bytes);
if (ret)
- return ret;
+ return ret;
+
+ *first = entries.first;
+ *last = entries.last;
+ entries.entries = p;
+ base = (unsigned char *)entries.entries->data;
+ entries.p = base + entries.bytes;
+
+ ret = kadm5_log_foreach(context, kadm_backward | kadm_confirmed,
+ NULL, load_entries_cb, &entries);
+ if (ret == 0 &&
+ (entries.nentries || entries.p != base || entries.first != *first))
+ ret = KADM5_LOG_CORRUPT;
+ if (ret)
+ krb5_data_free(p);
+ return ret;
+}
- ret = kadm5_log_end (server_context);
+/*
+ * Truncate the log, retaining at most `keep' entries and at most `maxbytes'.
+ * If `maxbytes' is zero, keep at most the default log size limit.
+ */
+kadm5_ret_t
+kadm5_log_truncate(kadm5_server_context *context, size_t keep, size_t maxbytes)
+{
+ kadm5_ret_t ret;
+ uint32_t first, last, last_tstamp;
+ time_t now = time(NULL);
+ krb5_data entries;
+ krb5_storage *sp;
+ ssize_t bytes;
+ uint64_t sz;
+ off_t off;
+
+ if (maxbytes == 0)
+ maxbytes = get_max_log_size(context->context);
+
+ if (strcmp(context->log_context.log_file, "/dev/null") == 0)
+ return 0;
+
+ if (context->log_context.read_only)
+ return EROFS;
+
+ /* Get the desired records. */
+ krb5_data_zero(&entries);
+ ret = load_entries(context, &entries, keep, maxbytes, &first, &last);
if (ret)
- return ret;
- return 0;
+ return ret;
+
+ if (first == 0) {
+ /*
+ * No records found/fit within resource limits. The caller should call
+ * kadm5_log_reinit(context) to truly truncate and reset the log to
+ * version 0, else call again with better limits.
+ */
+ krb5_data_free(&entries);
+ return EINVAL;
+ }
+
+ /* Check that entries.length won't overflow off_t */
+ sz = LOG_UBER_SZ + entries.length;
+ off = (off_t)sz;
+ if (off < 0 || off != sz || sz < entries.length) {
+ krb5_data_free(&entries);
+ return EOVERFLOW; /* caller should ask for fewer entries */
+ }
+
+ /* Truncate to zero size and seek to zero offset */
+ if (ftruncate(context->log_context.log_fd, 0) < 0 ||
+ lseek(context->log_context.log_fd, 0, SEEK_SET) < 0) {
+ krb5_data_free(&entries);
+ return errno;
+ }
+
+ /*
+ * Write the uber record and then the records loaded. Confirm the entries
+ * after writing them.
+ *
+ * If we crash then the log may not have all the entries we want, and
+ * replaying only some of the entries will leave us in a bad state.
+ * Additionally, we don't have mathematical proof that replaying the last
+ * N>1 entries is always idempotent. And though we believe we can make
+ * such replays idempotent, they would still leave the HDB with
+ * intermediate states that would not have occurred on the master.
+ *
+ * By initially setting the offset in the uber record to 0, the log will be
+ * seen as invalid should we crash here, thus the only
+ * harm will be that we'll reinitialize the log and force full props.
+ *
+ * We can't use the normal kadm5_log_*() machinery for this because
+ * we must set specific version numbers and timestamps. To keep
+ * things simple we don't try to do a single atomic write here as we
+ * do in kadm5_log_flush().
+ *
+ * We really do want to keep the new first entry's version and
+ * timestamp so we don't trip up iprop.
+ *
+ * Keep this in sync with kadm5_log_nop().
+ */
+ sp = krb5_storage_from_fd(context->log_context.log_fd);
+ if (sp == NULL) {
+ ret = errno;
+ krb5_warn(context->context, ret, "Unable to keep entries");
+ krb5_data_free(&entries);
+ return errno;
+ }
+ ret = krb5_store_uint32(sp, 0);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, now);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, kadm_nop); /* end of preamble */
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN); /* end of header */
+ if (ret == 0)
+ ret = krb5_store_uint64(sp, LOG_UBER_SZ);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, now);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, last);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, LOG_UBER_LEN);
+ if (ret == 0)
+ ret = krb5_store_uint32(sp, 0); /* end of trailer */
+ if (ret == 0) {
+ bytes = krb5_storage_write(sp, entries.data, entries.length);
+ if (bytes == -1)
+ ret = errno;
+ }
+ if (ret == 0)
+ ret = krb5_storage_fsync(sp);
+ /* Confirm all the records now */
+ if (ret == 0) {
+ if (krb5_storage_seek(sp, LOG_HEADER_SZ, SEEK_SET) == -1)
+ ret = errno;
+ }
+ if (ret == 0)
+ ret = krb5_store_uint64(sp, off);
+ krb5_data_free(&entries);
+ krb5_storage_free(sp);
+
+ if (ret) {
+ krb5_warn(context->context, ret, "Unable to keep entries");
+ (void) ftruncate(context->log_context.log_fd, LOG_UBER_SZ);
+ (void) lseek(context->log_context.log_fd, 0, SEEK_SET);
+ return ret;
+ }
+
+ /* Done. Now rebuild the log_context state. */
+ (void) lseek(context->log_context.log_fd, off, SEEK_SET);
+ sp = kadm5_log_goto_end(context, context->log_context.log_fd);
+ if (sp == NULL)
+ return ENOMEM;
+ ret = get_version_prev(sp, &context->log_context.version, &last_tstamp);
+ context->log_context.last_time = last_tstamp;
+ krb5_storage_free(sp);
+ return ret;
+}
+/*
+ * "Truncate" the log if not read only and over the desired maximum size. We
+ * attempt to retain 1/4 of the existing storage.
+ *
+ * Called after successful log recovery, so at this point we must have no
+ * unconfirmed entries in the log.
+ */
+static kadm5_ret_t
+truncate_if_needed(kadm5_server_context *context)
+{
+ kadm5_ret_t ret = 0;
+ kadm5_log_context *log_context = &context->log_context;
+ size_t maxbytes;
+ struct stat st;
+
+ if (log_context->log_fd == -1 || log_context->read_only)
+ return 0;
+ if (strcmp(context->log_context.log_file, "/dev/null") == 0)
+ return 0;
+
+ maxbytes = get_max_log_size(context->context);
+ if (maxbytes <= 0)
+ return 0;
+
+ if (fstat(log_context->log_fd, &st) == -1)
+ return errno;
+ if (st.st_size == (size_t)st.st_size && (size_t)st.st_size <= maxbytes)
+ return 0;
+
+ /* Shrink the log by a factor of 4 */
+ ret = kadm5_log_truncate(context, 0, maxbytes/4);
+ return ret == EINVAL ? 0 : ret;
}
#ifndef NO_UNIX_SOCKETS
@@ -991,9 +2598,13 @@ static HEIMDAL_MUTEX signal_mutex = HEIMDAL_MUTEX_INITIALIZER;
const char *
kadm5_log_signal_socket(krb5_context context)
{
+ int ret = 0;
+
HEIMDAL_MUTEX_lock(&signal_mutex);
if (!default_signal)
- asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
+ ret = asprintf(&default_signal, "%s/signal", hdb_db_dir(context));
+ if (ret == -1)
+ default_signal = NULL;
HEIMDAL_MUTEX_unlock(&signal_mutex);
return krb5_config_get_string_default(context,
diff --git a/lib/kadm5/marshall.c b/lib/kadm5/marshall.c
index 65804afbf92b..250b2def960a 100644
--- a/lib/kadm5/marshall.c
+++ b/lib/kadm5/marshall.c
@@ -35,6 +35,39 @@
RCSID("$Id$");
+int
+kadm5_some_keys_are_bogus(size_t n_keys, krb5_key_data *keys)
+{
+ size_t i;
+
+ for (i = 0; i < n_keys; i++) {
+ krb5_key_data *key = &keys[i];
+ if (key->key_data_length[0] == sizeof(KADM5_BOGUS_KEY_DATA) - 1 &&
+ ct_memcmp(key->key_data_contents[1], KADM5_BOGUS_KEY_DATA,
+ key->key_data_length[0]) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+int
+kadm5_all_keys_are_bogus(size_t n_keys, krb5_key_data *keys)
+{
+ size_t i;
+
+ if (n_keys == 0)
+ return 0;
+
+ for (i = 0; i < n_keys; i++) {
+ krb5_key_data *key = &keys[i];
+ if (key->key_data_length[0] != sizeof(KADM5_BOGUS_KEY_DATA) - 1 ||
+ ct_memcmp(key->key_data_contents[1], KADM5_BOGUS_KEY_DATA,
+ key->key_data_length[0]) != 0)
+ return 0;
+ }
+ return 1;
+}
+
kadm5_ret_t
kadm5_store_key_data(krb5_storage *sp,
krb5_key_data *key)
@@ -54,6 +87,36 @@ kadm5_store_key_data(krb5_storage *sp,
}
kadm5_ret_t
+kadm5_store_fake_key_data(krb5_storage *sp,
+ krb5_key_data *key)
+{
+ krb5_data c;
+
+ krb5_store_int32(sp, key->key_data_ver);
+ krb5_store_int32(sp, key->key_data_kvno);
+ krb5_store_int32(sp, key->key_data_type[0]);
+
+ /*
+ * This is the key contents. We want it to be obvious to the client
+ * (if it really did want the keys) that the key won't work.
+ * 32-bit keys are no good for any enctype, so that should do.
+ * Clients that didn't need keys will ignore this, and clients that
+ * did want keys will either fail or they'll, say, create bogus
+ * keytab entries that will subsequently fail to be useful.
+ */
+ c.length = sizeof (KADM5_BOGUS_KEY_DATA) - 1;
+ c.data = KADM5_BOGUS_KEY_DATA;
+ krb5_store_data(sp, c);
+
+ /* This is the salt -- no need to send garbage */
+ krb5_store_int32(sp, key->key_data_type[1]);
+ c.length = key->key_data_length[1];
+ c.data = key->key_data_contents[1];
+ krb5_store_data(sp, c);
+ return 0;
+}
+
+kadm5_ret_t
kadm5_ret_key_data(krb5_storage *sp,
krb5_key_data *key)
{
@@ -105,7 +168,7 @@ kadm5_ret_tl_data(krb5_storage *sp,
static kadm5_ret_t
store_principal_ent(krb5_storage *sp,
kadm5_principal_ent_t princ,
- uint32_t mask)
+ uint32_t mask, int wkeys)
{
int i;
@@ -149,8 +212,12 @@ store_principal_ent(krb5_storage *sp,
krb5_store_int32(sp, princ->fail_auth_count);
if (mask & KADM5_KEY_DATA) {
krb5_store_int32(sp, princ->n_key_data);
- for(i = 0; i < princ->n_key_data; i++)
- kadm5_store_key_data(sp, &princ->key_data[i]);
+ for(i = 0; i < princ->n_key_data; i++) {
+ if (wkeys)
+ kadm5_store_key_data(sp, &princ->key_data[i]);
+ else
+ kadm5_store_fake_key_data(sp, &princ->key_data[i]);
+ }
}
if (mask & KADM5_TL_DATA) {
krb5_tl_data *tp;
@@ -167,7 +234,14 @@ kadm5_ret_t
kadm5_store_principal_ent(krb5_storage *sp,
kadm5_principal_ent_t princ)
{
- return store_principal_ent (sp, princ, ~0);
+ return store_principal_ent (sp, princ, ~0, 1);
+}
+
+kadm5_ret_t
+kadm5_store_principal_ent_nokeys(krb5_storage *sp,
+ kadm5_principal_ent_t princ)
+{
+ return store_principal_ent (sp, princ, ~0, 0);
}
kadm5_ret_t
@@ -176,7 +250,7 @@ kadm5_store_principal_ent_mask(krb5_storage *sp,
uint32_t mask)
{
krb5_store_int32(sp, mask);
- return store_principal_ent (sp, princ, mask);
+ return store_principal_ent (sp, princ, mask, 1);
}
static kadm5_ret_t
diff --git a/lib/kadm5/modify_s.c b/lib/kadm5/modify_s.c
index 7907995ec3b8..78f26734f0f7 100644
--- a/lib/kadm5/modify_s.c
+++ b/lib/kadm5/modify_s.c
@@ -44,43 +44,87 @@ modify_principal(void *server_handle,
kadm5_server_context *context = server_handle;
hdb_entry_ex ent;
kadm5_ret_t ret;
+
+ memset(&ent, 0, sizeof(ent));
+
if((mask & forbidden_mask))
return KADM5_BAD_MASK;
if((mask & KADM5_POLICY) && strcmp(princ->policy, "default"))
return KADM5_UNK_POLICY;
- memset(&ent, 0, sizeof(ent));
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret)
- return ret;
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
ret = context->db->hdb_fetch_kvno(context->context, context->db,
princ->principal, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
- if(ret)
- goto out;
- ret = _kadm5_setup_entry(context, &ent, mask, princ, mask, NULL, 0);
- if(ret)
+ if (ret)
goto out2;
+ ret = _kadm5_setup_entry(context, &ent, mask, princ, mask, NULL, 0);
+ if (ret)
+ goto out3;
ret = _kadm5_set_modifier(context, &ent.entry);
- if(ret)
- goto out2;
+ if (ret)
+ goto out3;
+
+ /*
+ * If any keys are bogus, disallow the modify. If the keys were
+ * bogus as stored in the HDB we could allow those through, but
+ * distinguishing that case from a pre-1.6 client using add_enctype
+ * without the get-keys privilege requires more work (mainly: checking that
+ * the bogus keys in princ->key_data[] have corresponding bogus keys in ent
+ * before calling _kadm5_setup_entry()).
+ */
+ if ((mask & KADM5_KEY_DATA) &&
+ kadm5_some_keys_are_bogus(princ->n_key_data, princ->key_data)) {
+ ret = KADM5_AUTH_GET_KEYS; /* Not quite appropriate, but it'll do */
+ goto out3;
+ }
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
if (ret)
- goto out2;
+ goto out3;
- ret = context->db->hdb_store(context->context, context->db,
- HDB_F_REPLACE, &ent);
- if (ret)
- goto out2;
+ if ((mask & KADM5_POLICY)) {
+ HDB_extension ext;
+
+ memset(&ext, 0, sizeof(ext));
+ /* XXX should be TRUE, but we don't yet support policies */
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_policy;
+ ext.data.u.policy = strdup(princ->policy);
+ if (ext.data.u.policy == NULL) {
+ ret = ENOMEM;
+ goto out3;
+ }
+ /* This calls free_HDB_extension(), freeing ext.data.u.policy */
+ ret = hdb_replace_extension(context->context, &ent.entry, &ext);
+ free(ext.data.u.policy);
+ if (ret)
+ goto out3;
+ }
- kadm5_log_modify (context,
- &ent.entry,
- mask | KADM5_MOD_NAME | KADM5_MOD_TIME);
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent.entry,
+ mask | KADM5_MOD_NAME | KADM5_MOD_TIME);
-out2:
+ out3:
hdb_free_entry(context->context, &ent);
-out:
- context->db->hdb_close(context->context, context->db);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
return _kadm5_error_code(ret);
}
diff --git a/lib/kadm5/password_quality.c b/lib/kadm5/password_quality.c
index eb72ba6182ee..8b17a481f017 100644
--- a/lib/kadm5/password_quality.c
+++ b/lib/kadm5/password_quality.c
@@ -91,7 +91,7 @@ char_class_passwd_quality (krb5_context context,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"abcdefghijklmnopqrstuvwxyz",
"1234567890",
- "!@#$%^&*()/?<>,.{[]}\\|'~`\" "
+ " !\"#$%&'()*+,-./:;<=>?@\\]^_`{|}~"
};
int counter = 0, req_classes;
size_t i, len;
@@ -120,11 +120,12 @@ char_class_passwd_quality (krb5_context context,
if (counter < req_classes) {
snprintf(message, length,
"Password doesn't meet complexity requirement.\n"
- "Add more characters from the following classes:\n"
+ "Add more characters from at least %d of the\n"
+ "following classes:\n"
"1. English uppercase characters (A through Z)\n"
"2. English lowercase characters (a through z)\n"
"3. Base 10 digits (0 through 9)\n"
- "4. Nonalphanumeric characters (e.g., !, $, #, %%)");
+ "4. Nonalphanumeric characters (e.g., !, $, #, %%)", req_classes);
return 1;
}
return 0;
@@ -377,23 +378,24 @@ kadm5_add_passwd_quality_verifier(krb5_context context,
#ifdef HAVE_DLOPEN
if(check_library == NULL) {
- krb5_error_code ret;
+ krb5_error_code ret = 0;
+ char **strs;
char **tmp;
- tmp = krb5_config_get_strings(context, NULL,
- "password_quality",
- "policy_libraries",
- NULL);
- if(tmp == NULL || *tmp == NULL)
+ strs = krb5_config_get_strings(context, NULL,
+ "password_quality",
+ "policy_libraries",
+ NULL);
+ if (strs == NULL)
return 0;
- while (*tmp) {
+ for (tmp = strs; *tmp; tmp++) {
ret = add_verifier(context, *tmp);
if (ret)
- return ret;
- tmp++;
+ break;
}
- return 0;
+ krb5_config_free_strings(strs);
+ return ret;
} else {
return add_verifier(context, check_library);
}
diff --git a/lib/kadm5/private.h b/lib/kadm5/private.h
index 7c5b27f17691..0b14ebd68f14 100644
--- a/lib/kadm5/private.h
+++ b/lib/kadm5/private.h
@@ -36,10 +36,16 @@
#ifndef __kadm5_privatex_h__
#define __kadm5_privatex_h__
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
+
struct kadm_func {
- kadm5_ret_t (*chpass_principal) (void *, krb5_principal, const char*);
- kadm5_ret_t (*create_principal) (void*, kadm5_principal_ent_t,
- uint32_t, const char*);
+ kadm5_ret_t (*chpass_principal) (void *, krb5_principal, int,
+ int, krb5_key_salt_tuple*, const char*);
+ kadm5_ret_t (*create_principal) (void*, kadm5_principal_ent_t, uint32_t,
+ int, krb5_key_salt_tuple *,
+ const char*);
kadm5_ret_t (*delete_principal) (void*, krb5_principal);
kadm5_ret_t (*destroy) (void*);
kadm5_ret_t (*flush) (void*);
@@ -48,11 +54,17 @@ struct kadm_func {
kadm5_ret_t (*get_principals) (void*, const char*, char***, int*);
kadm5_ret_t (*get_privs) (void*, uint32_t*);
kadm5_ret_t (*modify_principal) (void*, kadm5_principal_ent_t, uint32_t);
- kadm5_ret_t (*randkey_principal) (void*, krb5_principal,
- krb5_keyblock**, int*);
+ kadm5_ret_t (*randkey_principal) (void*, krb5_principal, krb5_boolean, int,
+ krb5_key_salt_tuple*, krb5_keyblock**,
+ int*);
kadm5_ret_t (*rename_principal) (void*, krb5_principal, krb5_principal);
- kadm5_ret_t (*chpass_principal_with_key) (void *, krb5_principal,
+ kadm5_ret_t (*chpass_principal_with_key) (void *, krb5_principal, int,
int, krb5_key_data *);
+ kadm5_ret_t (*lock) (void *);
+ kadm5_ret_t (*unlock) (void *);
+ kadm5_ret_t (*setkey_principal_3) (void *, krb5_principal, krb5_boolean,
+ int, krb5_key_salt_tuple *,
+ krb5_keyblock *, int);
};
/* XXX should be integrated */
@@ -61,7 +73,7 @@ typedef struct kadm5_common_context {
krb5_boolean my_context;
struct kadm_func funcs;
void *data;
-}kadm5_common_context;
+} kadm5_common_context;
typedef struct kadm5_log_peer {
int fd;
@@ -73,7 +85,10 @@ typedef struct kadm5_log_peer {
typedef struct kadm5_log_context {
char *log_file;
int log_fd;
+ int read_only;
+ int lock_mode;
uint32_t version;
+ time_t last_time;
#ifndef NO_UNIX_SOCKETS
struct sockaddr_un socket_name;
#else
@@ -89,6 +104,7 @@ typedef struct kadm5_server_context {
/* */
kadm5_config_params config;
HDB *db;
+ int keep_open;
krb5_principal caller;
unsigned acl_flags;
kadm5_log_context log_context;
@@ -103,14 +119,14 @@ typedef struct kadm5_client_context {
char *realm;
char *admin_server;
int kadmind_port;
- int sock;
+ krb5_socket_t sock;
char *client_name;
char *service_name;
krb5_prompter_fct prompter;
const char *keytab;
krb5_ccache ccache;
kadm5_config_params *realm_params;
-}kadm5_client_context;
+} kadm5_client_context;
typedef struct kadm5_ad_context {
krb5_context context;
@@ -126,6 +142,11 @@ typedef struct kadm5_ad_context {
char *base_dn;
} kadm5_ad_context;
+/*
+ * This enum is used in the iprop log file and on the wire in the iprop
+ * protocol. DO NOT CHANGE, except to add new op types at the end, and
+ * look for places in lib/kadm5/log.c to update.
+ */
enum kadm_ops {
kadm_get,
kadm_delete,
@@ -137,7 +158,28 @@ enum kadm_ops {
kadm_get_privs,
kadm_get_princs,
kadm_chpass_with_key,
- kadm_nop
+ kadm_nop,
+ kadm_first = kadm_get,
+ kadm_last = kadm_nop
+};
+
+/* FIXME nop types are currently not implemented */
+enum kadm_nop_type {
+ kadm_nop_plain, /* plain nop, not relevance except as uberblock */
+ kadm_nop_trunc, /* indicates that the master truncated the log */
+ kadm_nop_close /* indicates that the master closed this log */
+};
+
+enum kadm_iter_opts {
+ kadm_forward = 1,
+ kadm_backward = 2,
+ kadm_confirmed = 4,
+ kadm_unconfirmed = 8
+};
+
+enum kadm_recover_mode {
+ kadm_recover_commit,
+ kadm_recover_replay
};
#define KADMIN_APPL_VERSION "KADM0.1"
diff --git a/lib/kadm5/randkey_c.c b/lib/kadm5/randkey_c.c
index bfa12084f7e2..24f38c591912 100644
--- a/lib/kadm5/randkey_c.c
+++ b/lib/kadm5/randkey_c.c
@@ -38,14 +38,18 @@ RCSID("$Id$");
kadm5_ret_t
kadm5_c_randkey_principal(void *server_handle,
krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **new_keys,
int *n_keys)
{
kadm5_client_context *context = server_handle;
kadm5_ret_t ret;
krb5_storage *sp;
- unsigned char buf[1024];
+ unsigned char buf[1536];
int32_t tmp;
+ size_t i;
krb5_data reply;
ret = _kadm5_connect(server_handle);
@@ -57,8 +61,41 @@ kadm5_c_randkey_principal(void *server_handle,
krb5_clear_error_message(context->context);
return ENOMEM;
}
- krb5_store_int32(sp, kadm_randkey);
- krb5_store_principal(sp, princ);
+
+ /*
+ * NOTE WELL: This message is extensible. It currently consists of:
+ *
+ * - opcode (kadm_randkey)
+ * - principal name (princ)
+ *
+ * followed by optional items, each of which must be present if
+ * there are any items following them that are also present:
+ *
+ * - keepold boolean (whether to delete old kvnos)
+ * - number of key/salt type tuples
+ * - array of {enctype, salttype}
+ *
+ * Eventually we may add:
+ *
+ * - opaque string2key parameters (salt, rounds, ...)
+ */
+ ret = krb5_store_int32(sp, kadm_randkey);
+ if (ret == 0)
+ ret = krb5_store_principal(sp, princ);
+
+ if (ret == 0 && (keepold == TRUE || n_ks_tuple > 0))
+ ret = krb5_store_uint32(sp, keepold);
+ if (ret == 0 && n_ks_tuple > 0)
+ ret = krb5_store_uint32(sp, n_ks_tuple);
+ for (i = 0; ret == 0 && i < n_ks_tuple; i++) {
+ ret = krb5_store_int32(sp, ks_tuple[i].ks_enctype);
+ if (ret == 0)
+ krb5_store_int32(sp, ks_tuple[i].ks_salttype);
+ }
+ if (ret)
+ return ret;
+ /* Future extensions go here */
+
ret = _kadm5_client_send(context, sp);
krb5_storage_free(sp);
if (ret)
@@ -73,22 +110,35 @@ kadm5_c_randkey_principal(void *server_handle,
return ENOMEM;
}
krb5_clear_error_message(context->context);
- krb5_ret_int32(sp, &tmp);
- ret = tmp;
- if(ret == 0){
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret == 0)
+ ret = tmp;
+ if (ret == 0){
krb5_keyblock *k;
- int i;
- krb5_ret_int32(sp, &tmp);
- k = malloc(tmp * sizeof(*k));
+ ret = krb5_ret_int32(sp, &tmp);
+ if (ret)
+ goto out;
+ if (tmp < 0) {
+ ret = EOVERFLOW;
+ goto out;
+ }
+ k = calloc(tmp, sizeof(*k));
if (k == NULL) {
ret = ENOMEM;
goto out;
}
- for(i = 0; i < tmp; i++)
- krb5_ret_keyblock(sp, &k[i]);
- *n_keys = tmp;
- *new_keys = k;
+ for(i = 0; ret == 0 && i < tmp; i++)
+ ret = krb5_ret_keyblock(sp, &k[i]);
+ if (ret == 0 && n_keys && new_keys) {
+ *n_keys = tmp;
+ *new_keys = k;
+ } else {
+ krb5_free_keyblock_contents(context->context, &k[i]);
+ for (; i > 0; i--)
+ krb5_free_keyblock_contents(context->context, &k[i - 1]);
+ free(k);
+ }
}
out:
krb5_storage_free(sp);
diff --git a/lib/kadm5/randkey_s.c b/lib/kadm5/randkey_s.c
index dcb179aac40f..64578ab49998 100644
--- a/lib/kadm5/randkey_s.c
+++ b/lib/kadm5/randkey_s.c
@@ -43,6 +43,9 @@ RCSID("$Id$");
kadm5_ret_t
kadm5_s_randkey_principal(void *server_handle,
krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **new_keys,
int *n_keys)
{
@@ -51,57 +54,84 @@ kadm5_s_randkey_principal(void *server_handle,
kadm5_ret_t ret;
memset(&ent, 0, sizeof(ent));
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret)
- return ret;
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
if(ret)
- goto out;
+ goto out2;
+
+ if (keepold) {
+ ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
+ if (ret)
+ goto out3;
+ }
- ret = _kadm5_set_keys_randomly (context,
- &ent.entry,
- new_keys,
- n_keys);
+ ret = _kadm5_set_keys_randomly(context, &ent.entry, n_ks_tuple, ks_tuple,
+ new_keys, n_keys);
if (ret)
- goto out2;
+ goto out3;
ent.entry.kvno++;
+ ent.entry.flags.require_pwchange = 0;
+
ret = _kadm5_set_modifier(context, &ent.entry);
if(ret)
- goto out3;
+ goto out4;
ret = _kadm5_bump_pw_expire(context, &ent.entry);
if (ret)
- goto out2;
+ goto out4;
- ret = hdb_seal_keys(context->context, context->db, &ent.entry);
- if (ret)
- goto out2;
+ if (keepold) {
+ ret = hdb_seal_keys(context->context, context->db, &ent.entry);
+ if (ret)
+ goto out4;
+ } else {
+ HDB_extension ext;
- ret = context->db->hdb_store(context->context, context->db,
- HDB_F_REPLACE, &ent);
- if (ret)
- goto out2;
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ ext.data.u.hist_keys.len = 0;
+ ext.data.u.hist_keys.val = NULL;
+ hdb_replace_extension(context->context, &ent.entry, &ext);
+ }
- kadm5_log_modify (context,
- &ent.entry,
- KADM5_PRINCIPAL | KADM5_MOD_NAME | KADM5_MOD_TIME |
- KADM5_KEY_DATA | KADM5_KVNO | KADM5_PW_EXPIRATION |
- KADM5_TL_DATA);
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_modify(context, &ent.entry,
+ KADM5_ATTRIBUTES | KADM5_PRINCIPAL |
+ KADM5_MOD_NAME | KADM5_MOD_TIME |
+ KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
-out3:
+ out4:
if (ret) {
int i;
for (i = 0; i < *n_keys; ++i)
- krb5_free_keyblock_contents (context->context, &(*new_keys)[i]);
+ krb5_free_keyblock_contents(context->context, &(*new_keys)[i]);
free (*new_keys);
*new_keys = NULL;
*n_keys = 0;
}
-out2:
+ out3:
hdb_free_entry(context->context, &ent);
-out:
- context->db->hdb_close(context->context, context->db);
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
return _kadm5_error_code(ret);
}
diff --git a/lib/kadm5/rename_s.c b/lib/kadm5/rename_s.c
index 08351290c9a6..16a5e11c1dde 100644
--- a/lib/kadm5/rename_s.c
+++ b/lib/kadm5/rename_s.c
@@ -46,20 +46,26 @@ kadm5_s_rename_principal(void *server_handle,
krb5_principal oldname;
memset(&ent, 0, sizeof(ent));
- if(krb5_principal_compare(context->context, source, target))
+ if (krb5_principal_compare(context->context, source, target))
return KADM5_DUP; /* XXX is this right? */
- ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
- if(ret)
- return ret;
+ if (!context->keep_open) {
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if(ret)
+ return ret;
+ }
+
+ ret = kadm5_log_init(context);
+ if (ret)
+ goto out;
+
ret = context->db->hdb_fetch_kvno(context->context, context->db,
source, HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
- if(ret){
- context->db->hdb_close(context->context, context->db);
- goto out;
- }
- ret = _kadm5_set_modifier(context, &ent.entry);
- if(ret)
+ if (ret)
goto out2;
+ oldname = ent.entry.principal;
+ ret = _kadm5_set_modifier(context, &ent.entry);
+ if (ret)
+ goto out3;
{
/* fix salt */
size_t i;
@@ -73,39 +79,41 @@ kadm5_s_rename_principal(void *server_handle,
if(ent.entry.keys.val[i].salt == NULL){
ent.entry.keys.val[i].salt =
malloc(sizeof(*ent.entry.keys.val[i].salt));
- if(ent.entry.keys.val[i].salt == NULL)
- return ENOMEM;
- ret = copy_Salt(&salt, ent.entry.keys.val[i].salt);
- if(ret)
+ if (ent.entry.keys.val[i].salt == NULL)
+ ret = ENOMEM;
+ else
+ ret = copy_Salt(&salt, ent.entry.keys.val[i].salt);
+ if (ret)
break;
}
}
krb5_free_salt(context->context, salt2);
}
- if(ret)
- goto out2;
- oldname = ent.entry.principal;
- ent.entry.principal = target;
+ if (ret)
+ goto out3;
+ /* Borrow target */
+ ent.entry.principal = target;
ret = hdb_seal_keys(context->context, context->db, &ent.entry);
- if (ret) {
- ent.entry.principal = oldname;
- goto out2;
- }
+ if (ret)
+ goto out3;
- kadm5_log_rename (context, source, &ent.entry);
+ /* This logs the change for iprop and writes to the HDB */
+ ret = kadm5_log_rename(context, source, &ent.entry);
- ret = context->db->hdb_store(context->context, context->db, 0, &ent);
- if(ret){
- ent.entry.principal = oldname;
- goto out2;
- }
- ret = context->db->hdb_remove(context->context, context->db, oldname);
- ent.entry.principal = oldname;
-out2:
- context->db->hdb_close(context->context, context->db);
+ out3:
+ ent.entry.principal = oldname; /* Unborrow target */
hdb_free_entry(context->context, &ent);
-out:
+
+ out2:
+ (void) kadm5_log_end(context);
+ out:
+ if (!context->keep_open) {
+ kadm5_ret_t ret2;
+ ret2 = context->db->hdb_close(context->context, context->db);
+ if (ret == 0 && ret2 != 0)
+ ret = ret2;
+ }
return _kadm5_error_code(ret);
}
diff --git a/lib/kadm5/send_recv.c b/lib/kadm5/send_recv.c
index cd63293f9e4a..3384ddeabec1 100644
--- a/lib/kadm5/send_recv.c
+++ b/lib/kadm5/send_recv.c
@@ -43,7 +43,7 @@ _kadm5_client_send(kadm5_client_context *context, krb5_storage *sp)
size_t len;
krb5_storage *sock;
- assert(context->sock != -1);
+ assert(context->sock != rk_INVALID_SOCKET);
len = krb5_storage_seek(sp, 0, SEEK_CUR);
ret = krb5_data_alloc(&msg, len);
@@ -59,7 +59,7 @@ _kadm5_client_send(kadm5_client_context *context, krb5_storage *sp)
if(ret)
return ret;
- sock = krb5_storage_from_fd(context->sock);
+ sock = krb5_storage_from_socket(context->sock);
if(sock == NULL) {
krb5_clear_error_message(context->context);
krb5_data_free(&out);
@@ -81,7 +81,7 @@ _kadm5_client_recv(kadm5_client_context *context, krb5_data *reply)
krb5_data data;
krb5_storage *sock;
- sock = krb5_storage_from_fd(context->sock);
+ sock = krb5_storage_from_socket(context->sock);
if(sock == NULL) {
krb5_clear_error_message(context->context);
return ENOMEM;
diff --git a/lib/kadm5/set_keys.c b/lib/kadm5/set_keys.c
index ea0b816c1361..c3fcc2e6d700 100644
--- a/lib/kadm5/set_keys.c
+++ b/lib/kadm5/set_keys.c
@@ -42,15 +42,19 @@ RCSID("$Id$");
kadm5_ret_t
_kadm5_set_keys(kadm5_server_context *context,
hdb_entry *ent,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
const char *password)
{
Key *keys;
size_t num_keys;
kadm5_ret_t ret;
- ret = hdb_generate_key_set_password(context->context,
- ent->principal,
- password, &keys, &num_keys);
+ ret = hdb_generate_key_set_password_with_ks_tuple(context->context,
+ ent->principal,
+ password,
+ ks_tuple, n_ks_tuple,
+ &keys, &num_keys);
if (ret)
return ret;
@@ -72,6 +76,23 @@ _kadm5_set_keys(kadm5_server_context *context,
return 0;
}
+static void
+setup_Key(Key *k, Salt *s, krb5_key_data *kd, size_t kd_offset)
+{
+ memset(k, 0, sizeof (*k)); /* sets mkvno and salt */
+ k->key.keytype = kd[kd_offset].key_data_type[0];
+ k->key.keyvalue.length = kd[kd_offset].key_data_length[0];
+ k->key.keyvalue.data = kd[kd_offset].key_data_contents[0];
+
+ if(kd[kd_offset].key_data_ver == 2) {
+ memset(s, 0, sizeof (*s));
+ s->type = kd[kd_offset].key_data_type[1];
+ s->salt.length = kd[kd_offset].key_data_length[1];
+ s->salt.data = kd[kd_offset].key_data_contents[1];
+ k->salt = s;
+ }
+}
+
/*
* Set the keys of `ent' to (`n_key_data', `key_data')
*/
@@ -83,51 +104,167 @@ _kadm5_set_keys2(kadm5_server_context *context,
krb5_key_data *key_data)
{
krb5_error_code ret;
- int i;
- unsigned len;
- Key *keys;
+ size_t i, k;
+ HDB_extension ext;
+ HDB_extension *extp = NULL;
+ HDB_Ext_KeySet *hist_keys = &ext.data.u.hist_keys;
+ Key key;
+ Salt salt;
+ Keys keys;
+ hdb_keyset hkset;
+ krb5_kvno kvno = -1;
+ int one_key_set = 1;
+ int replace_hist_keys = 0;
+
+ if (n_key_data == 0) {
+ /* Clear all keys! */
+ ret = hdb_clear_extension(context->context, ent,
+ choice_HDB_extension_data_hist_keys);
+ if (ret)
+ return ret;
+ free_Keys(&ent->keys);
+ return 0;
+ }
- len = n_key_data;
- keys = malloc (len * sizeof(*keys));
- if (keys == NULL && len != 0)
- return ENOMEM;
+ memset(&keys, 0, sizeof (keys));
+ memset(&hkset, 0, sizeof (hkset)); /* set set_time */
+ memset(&ext, 0, sizeof (ext));
+ ext.mandatory = FALSE;
+ ext.data.element = choice_HDB_extension_data_hist_keys;
+ memset(hist_keys, 0, sizeof (*hist_keys));
+
+ for (i = 0; i < n_key_data; i++) {
+ if (kvno != -1 && kvno != key_data[i].key_data_kvno) {
+ one_key_set = 0;
+ break;
+ }
+ kvno = key_data[i].key_data_kvno;
+ }
+ if (one_key_set) {
+ /*
+ * If we're updating KADM5_KEY_DATA with a single keyset then we
+ * assume we must be setting the principal's kvno as well!
+ *
+ * Just have to be careful about old clients that might have
+ * sent 0 as the kvno... This may seem ugly, but it's the price
+ * of backwards compatibility with pre-multi-kvno kadmin clients
+ * (besides, who's to say that updating KADM5_KEY_DATA requires
+ * updating the entry's kvno?)
+ *
+ * Note that we do nothing special for the case where multiple
+ * keysets are given but the entry's kvno is not set and not in
+ * the given set of keysets. If this happens we'll just update
+ * the key history only and leave the current keyset alone.
+ */
+ if (kvno == 0) {
+ /* Force kvno to 1 if it was 0; (ank would do this anyways) */
+ if (ent->kvno == 0)
+ ent->kvno = 1;
+ /* Below we need key_data[*].kvno to be reasonable */
+ for (i = 0; i < n_key_data; i++)
+ key_data[i].key_data_kvno = ent->kvno;
+ } else {
+ /*
+ * Or force the entry's kvno to match the one from the new,
+ * singular keyset
+ */
+ ent->kvno = kvno;
+ }
+ }
- _kadm5_init_keys (keys, len);
+ for (i = 0; i < n_key_data; i++) {
+ if (key_data[i].key_data_kvno == ent->kvno) {
+ /* A current key; add to current key set */
+ setup_Key(&key, &salt, key_data, i);
+ ret = add_Keys(&keys, &key);
+ continue;
+ }
- for(i = 0; i < n_key_data; i++) {
- keys[i].mkvno = NULL;
- keys[i].key.keytype = key_data[i].key_data_type[0];
- ret = krb5_data_copy(&keys[i].key.keyvalue,
- key_data[i].key_data_contents[0],
- key_data[i].key_data_length[0]);
- if(ret)
+ /*
+ * This kvno is historical. Build an hdb_keyset for keys of
+ * this enctype and add them to the new key history.
+ */
+ for (k = 0; k < hist_keys->len; k++) {
+ if (hist_keys->val[k].kvno == key_data[i].key_data_kvno)
+ break;
+ }
+ if (hist_keys->len > k &&
+ hist_keys->val[k].kvno == key_data[i].key_data_kvno)
+ /* We've added all keys of this kvno already (see below) */
+ continue;
+
+ memset(&hkset, 0, sizeof (hkset)); /* set set_time */
+ hkset.kvno = key_data[i].key_data_kvno;
+ for (k = 0; k < n_key_data; k++) {
+ /* Find all keys of this kvno and add them to the new keyset */
+ if (key_data[k].key_data_kvno != hkset.kvno)
+ continue;
+
+ setup_Key(&key, &salt, key_data, k);
+ ret = add_Keys(&hkset.keys, &key);
+ if (ret) {
+ free_hdb_keyset(&hkset);
+ goto out;
+ }
+ }
+ ret = add_HDB_Ext_KeySet(hist_keys, &hkset);
+ free_hdb_keyset(&hkset);
+ if (ret)
goto out;
- if(key_data[i].key_data_ver == 2) {
- Salt *salt;
+ replace_hist_keys = 1;
+ }
- salt = calloc(1, sizeof(*salt));
- if(salt == NULL) {
- ret = ENOMEM;
- goto out;
+ if (replace_hist_keys)
+ /* No key history given -> leave it alone */
+ extp = hdb_find_extension(ent, choice_HDB_extension_data_hist_keys);
+ if (extp != NULL) {
+ HDB_Ext_KeySet *old_hist_keys;
+
+ /*
+ * Try to keep the very useful set_time values from the old hist
+ * keys. kadm5 loses this info, so this heuristic is the best we
+ * can do.
+ */
+ old_hist_keys = &extp->data.u.hist_keys;
+ for (i = 0; i < old_hist_keys->len; i++) {
+ if (old_hist_keys->val[i].set_time == NULL)
+ continue;
+ for (k = 0; k < hist_keys->len; k++) {
+ if (hist_keys->val[k].kvno != old_hist_keys->val[k].kvno)
+ continue;
+ hist_keys->val[k].set_time = old_hist_keys->val[k].set_time;
+ old_hist_keys->val[k].set_time = NULL;
}
- keys[i].salt = salt;
- salt->type = key_data[i].key_data_type[1];
- krb5_data_copy(&salt->salt,
- key_data[i].key_data_contents[1],
- key_data[i].key_data_length[1]);
- } else
- keys[i].salt = NULL;
+ }
}
- _kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
- ent->keys.len = len;
- ent->keys.val = keys;
+ if (replace_hist_keys) {
+ /* If hist keys not given in key_data then don't blow away hist_keys */
+ ret = hdb_replace_extension(context->context, ent, &ext);
+ if (ret)
+ goto out;
+ }
+
+ /*
+ * A structure copy is more efficient here than this would be:
+ *
+ * copy_Keys(&keys, &ent->keys);
+ * free_Keys(&keys);
+ *
+ * Of course, the above hdb_replace_extension() is not at all efficient...
+ */
+ free_HDB_extension(&ext);
+ free_Keys(&ent->keys);
+ free_hdb_keyset(&hkset);
+ ent->keys = keys;
hdb_entry_set_pw_change_time(context->context, ent, 0);
hdb_entry_clear_password(context->context, ent);
return 0;
- out:
- _kadm5_free_keys (context->context, len, keys);
+
+out:
+ free_Keys(&keys);
+ free_HDB_extension(&ext);
return ret;
}
@@ -196,6 +333,8 @@ is_des_key_p(int keytype)
kadm5_ret_t
_kadm5_set_keys_randomly (kadm5_server_context *context,
hdb_entry *ent,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
krb5_keyblock **new_keys,
int *n_keys)
{
@@ -206,7 +345,7 @@ _kadm5_set_keys_randomly (kadm5_server_context *context,
Key *keys;
ret = hdb_generate_key_set(context->context, ent->principal,
- &keys, &num_keys, 1);
+ ks_tuple, n_ks_tuple, &keys, &num_keys, 1);
if (ret)
return ret;
@@ -263,8 +402,12 @@ out:
_kadm5_free_keys (context->context, ent->keys.len, ent->keys.val);
ent->keys.val = keys;
ent->keys.len = num_keys;
- *new_keys = kblock;
- *n_keys = num_keys;
+ if (n_keys && new_keys) {
+ *new_keys = kblock;
+ *n_keys = num_keys;
+ } else {
+ free(kblock);
+ }
hdb_entry_set_pw_change_time(context->context, ent, 0);
hdb_entry_clear_password(context->context, ent);
diff --git a/lib/kadm5/setkey3_s.c b/lib/kadm5/setkey3_s.c
new file mode 100644
index 000000000000..03b5d81018e5
--- /dev/null
+++ b/lib/kadm5/setkey3_s.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1997-2001, 2003, 2005-2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kadm5_locl.h"
+
+/**
+ * Server-side function to set new keys for a principal.
+ */
+kadm5_ret_t
+kadm5_s_setkey_principal_3(void *server_handle,
+ krb5_principal princ,
+ krb5_boolean keepold,
+ int n_ks_tuple,
+ krb5_key_salt_tuple *ks_tuple,
+ krb5_keyblock *keyblocks, int n_keys)
+{
+ kadm5_server_context *context = server_handle;
+ hdb_entry_ex ent;
+ kadm5_ret_t ret = 0;
+
+ memset(&ent, 0, sizeof(ent));
+ if (!context->keep_open)
+ ret = context->db->hdb_open(context->context, context->db, O_RDWR, 0);
+ if (ret)
+ return ret;
+
+ ret = kadm5_log_init(context);
+ if (ret) {
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return ret;
+ }
+
+ ret = context->db->hdb_fetch_kvno(context->context, context->db, princ,
+ HDB_F_GET_ANY|HDB_F_ADMIN_DATA, 0, &ent);
+ if (ret) {
+ (void) kadm5_log_end(context);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return ret;
+ }
+
+ if (keepold) {
+ ret = hdb_add_current_keys_to_history(context->context, &ent.entry);
+ } else
+ ret = hdb_clear_extension(context->context, &ent.entry,
+ choice_HDB_extension_data_hist_keys);
+
+ /*
+ * Though in practice all real calls to this function will pass an empty
+ * ks_tuple, and cannot in any case employ any salts that require
+ * additional data, we go the extra mile to set any requested salt type
+ * along with a zero length salt value. While we're at it we check that
+ * each ks_tuple's enctype matches the corresponding key enctype.
+ */
+ if (ret == 0) {
+ int i;
+
+ free_Keys(&ent.entry.keys);
+ for (i = 0; i < n_keys; ++i) {
+ Key k;
+ Salt s;
+
+ k.mkvno = 0;
+ k.key = keyblocks[i];
+ if (n_ks_tuple == 0)
+ k.salt = 0;
+ else {
+ if (ks_tuple[i].ks_enctype != keyblocks[i].keytype) {
+ ret = KADM5_SETKEY3_ETYPE_MISMATCH;
+ break;
+ }
+ s.type = ks_tuple[i].ks_salttype;
+ s.salt.data = 0;
+ s.opaque = 0;
+ k.salt = &s;
+ }
+ if ((ret = add_Keys(&ent.entry.keys, &k)) != 0)
+ break;
+ }
+ }
+
+ if (ret == 0) {
+ ent.entry.kvno++;
+ ent.entry.flags.require_pwchange = 0;
+ hdb_entry_set_pw_change_time(context->context, &ent.entry, 0);
+ hdb_entry_clear_password(context->context, &ent.entry);
+
+ if ((ret = hdb_seal_keys(context->context, context->db,
+ &ent.entry)) == 0
+ && (ret = _kadm5_set_modifier(context, &ent.entry)) == 0
+ && (ret = _kadm5_bump_pw_expire(context, &ent.entry)) == 0)
+ ret = kadm5_log_modify(context, &ent.entry,
+ KADM5_ATTRIBUTES | KADM5_PRINCIPAL |
+ KADM5_MOD_NAME | KADM5_MOD_TIME |
+ KADM5_KEY_DATA | KADM5_KVNO |
+ KADM5_PW_EXPIRATION | KADM5_TL_DATA);
+ }
+
+ hdb_free_entry(context->context, &ent);
+ (void) kadm5_log_end(context);
+ if (!context->keep_open)
+ context->db->hdb_close(context->context, context->db);
+ return _kadm5_error_code(ret);
+}
diff --git a/lib/kadm5/test_pw_quality.c b/lib/kadm5/test_pw_quality.c
index e3c8d2f0f432..ebef429c4753 100644
--- a/lib/kadm5/test_pw_quality.c
+++ b/lib/kadm5/test_pw_quality.c
@@ -42,10 +42,10 @@ static char *principal;
static char *password;
static struct getargs args[] = {
- { "principal", 0, arg_string, &principal },
- { "password", 0, arg_string, &password },
- { "version", 0, arg_flag, &version_flag },
- { "help", 0, arg_flag, &help_flag }
+ { "principal", 0, arg_string, &principal, NULL, NULL },
+ { "password", 0, arg_string, &password, NULL, NULL },
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/kadm5/version-script-client.map b/lib/kadm5/version-script-client.map
new file mode 100644
index 000000000000..de0ed6749163
--- /dev/null
+++ b/lib/kadm5/version-script-client.map
@@ -0,0 +1,59 @@
+HEIMDAL_KADM5_CLIENT_1.0 {
+ global:
+ et_kadm5_error_table;
+ initialize_kadm5_error_table;
+ initialize_kadm5_error_table_r;
+ kadm5_ad_init_with_password;
+ kadm5_ad_init_with_password_ctx;
+ kadm5_all_keys_are_bogus;
+ kadm5_c_chpass_principal;
+ kadm5_c_chpass_principal_with_key;
+ kadm5_c_create_principal;
+ kadm5_c_delete_principal;
+ kadm5_c_destroy;
+ kadm5_c_flush;
+ kadm5_c_get_principal;
+ kadm5_c_get_principals;
+ kadm5_c_get_privs;
+ kadm5_c_init_with_creds;
+ kadm5_c_init_with_creds_ctx;
+ kadm5_c_init_with_password;
+ kadm5_c_init_with_password_ctx;
+ kadm5_c_init_with_skey;
+ kadm5_c_init_with_skey_ctx;
+ kadm5_c_modify_principal;
+ kadm5_c_randkey_principal;
+ kadm5_c_rename_principal;
+ kadm5_chpass_principal;
+ kadm5_chpass_principal_with_key;
+ kadm5_create_principal;
+ kadm5_delete_principal;
+ kadm5_destroy;
+ kadm5_flush;
+ kadm5_free_key_data;
+ kadm5_free_name_list;
+ kadm5_free_principal_ent;
+ kadm5_get_principal;
+ kadm5_get_principals;
+ kadm5_get_privs;
+ kadm5_init_with_creds;
+ kadm5_init_with_creds_ctx;
+ kadm5_init_with_password;
+ kadm5_init_with_password_ctx;
+ kadm5_init_with_skey;
+ kadm5_init_with_skey_ctx;
+ kadm5_modify_principal;
+ kadm5_randkey_principal;
+ kadm5_rename_principal;
+ kadm5_ret_key_data;
+ kadm5_ret_principal_ent;
+ kadm5_ret_principal_ent_mask;
+ kadm5_ret_tl_data;
+ kadm5_some_keys_are_bogus;
+ kadm5_store_key_data;
+ kadm5_store_principal_ent;
+ kadm5_store_principal_ent_mask;
+ kadm5_store_tl_data;
+ local:
+ *;
+};
diff --git a/lib/kadm5/version-script.map b/lib/kadm5/version-script.map
index 90bd6fee10b1..cc1282d50f10 100644
--- a/lib/kadm5/version-script.map
+++ b/lib/kadm5/version-script.map
@@ -4,17 +4,27 @@ HEIMDAL_KAMD5_SERVER_1.0 {
global:
kadm5_ad_init_with_password;
kadm5_ad_init_with_password_ctx;
+ kadm5_all_keys_are_bogus;
kadm5_add_passwd_quality_verifier;
kadm5_check_password_quality;
kadm5_chpass_principal;
+ kadm5_chpass_principal_3;
kadm5_chpass_principal_with_key;
+ kadm5_chpass_principal_with_key_3;
+ kadm5_create_policy;
kadm5_create_principal;
+ kadm5_create_principal_3;
kadm5_delete_principal;
kadm5_destroy;
+ kadm5_decrypt_key;
+ kadm5_delete_policy;
kadm5_flush;
kadm5_free_key_data;
kadm5_free_name_list;
+ kadm5_free_policy_ent;
kadm5_free_principal_ent;
+ kadm5_get_policy;
+ kadm5_get_policies;
kadm5_get_principal;
kadm5_get_principals;
kadm5_get_privs;
@@ -24,18 +34,26 @@ HEIMDAL_KAMD5_SERVER_1.0 {
kadm5_init_with_password_ctx;
kadm5_init_with_skey;
kadm5_init_with_skey_ctx;
+ kadm5_lock;
kadm5_modify_principal;
+ kadm5_modify_policy;
kadm5_randkey_principal;
+ kadm5_randkey_principal_3;
kadm5_rename_principal;
kadm5_ret_key_data;
kadm5_ret_principal_ent;
kadm5_ret_principal_ent_mask;
kadm5_ret_tl_data;
kadm5_setup_passwd_quality_check;
+ kadm5_setkey_principal;
+ kadm5_setkey_principal_3;
+ kadm5_some_keys_are_bogus;
kadm5_store_key_data;
kadm5_store_principal_ent;
kadm5_store_principal_ent_mask;
+ kadm5_store_principal_ent_nokeys;
kadm5_store_tl_data;
+ kadm5_unlock;
kadm5_s_init_with_password_ctx;
kadm5_s_init_with_password;
kadm5_s_init_with_skey_ctx;
@@ -44,16 +62,21 @@ HEIMDAL_KAMD5_SERVER_1.0 {
kadm5_s_init_with_creds;
kadm5_s_chpass_principal_cond;
kadm5_log_set_version;
+ kadm5_log_signal_master;
kadm5_log_signal_socket;
kadm5_log_previous;
kadm5_log_goto_end;
kadm5_log_foreach;
kadm5_log_get_version_fd;
kadm5_log_get_version;
+ kadm5_log_recover;
kadm5_log_replay;
kadm5_log_end;
kadm5_log_reinit;
kadm5_log_init;
+ kadm5_log_init_nb;
+ kadm5_log_init_nolock;
+ kadm5_log_init_sharedlock;
kadm5_log_nop;
kadm5_log_truncate;
kadm5_log_modify;
diff --git a/lib/kafs/Makefile.am b/lib/kafs/Makefile.am
index 34a6144c7a17..dd23aef7665a 100644
--- a/lib/kafs/Makefile.am
+++ b/lib/kafs/Makefile.am
@@ -5,8 +5,8 @@ include $(top_srcdir)/Makefile.am.common
AM_CPPFLAGS += $(AFS_EXTRA_DEFS) $(ROKEN_RENAME)
if KRB5
-DEPLIB_krb5 = ../krb5/libkrb5.la
-krb5_am_workaround = $(INCLUDE_hcrypto) -I$(top_srcdir)/lib/krb5
+DEPLIB_krb5 = ../krb5/libkrb5.la $(LIB_hcrypto)
+krb5_am_workaround = -I$(top_srcdir)/lib/krb5
else
DEPLIB_krb5 =
krb5_am_workaround =
@@ -61,6 +61,7 @@ endif
dist_libkafs_la_SOURCES = \
afssys.c \
afskrb5.c \
+ rxkad_kdf.c \
common.c \
$(AIX_SRC) \
kafs_locl.h \
diff --git a/lib/kafs/Makefile.in b/lib/kafs/Makefile.in
index 07356bce6c1d..63d0b7184765 100644
--- a/lib/kafs/Makefile.in
+++ b/lib/kafs/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,9 +96,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
subdir = lib/kafs
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -60,8 +111,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -74,6 +124,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -93,6 +144,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -118,52 +171,112 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)" \
"$(DESTDIR)$(foodir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
-@KRB5_TRUE@am__DEPENDENCIES_1 = ../krb5/libkrb5.la
-am__DEPENDENCIES_2 =
-libkafs_la_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
-am__dist_libkafs_la_SOURCES_DIST = afssys.c afskrb5.c common.c \
- afslib.c kafs_locl.h afssysdefs.h roken_rename.h
+am__DEPENDENCIES_1 =
+@KRB5_TRUE@am__DEPENDENCIES_2 = ../krb5/libkrb5.la \
+@KRB5_TRUE@ $(am__DEPENDENCIES_1)
+libkafs_la_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1)
+am__dist_libkafs_la_SOURCES_DIST = afssys.c afskrb5.c rxkad_kdf.c \
+ common.c afslib.c kafs_locl.h afssysdefs.h roken_rename.h
@AIX_DYNAMIC_AFS_FALSE@@AIX_TRUE@am__objects_1 = afslib.lo
-dist_libkafs_la_OBJECTS = afssys.lo afskrb5.lo common.lo \
+dist_libkafs_la_OBJECTS = afssys.lo afskrb5.lo rxkad_kdf.lo common.lo \
$(am__objects_1)
@do_roken_rename_TRUE@am__objects_2 = resolve.lo strtok_r.lo \
@do_roken_rename_TRUE@ strlcpy.lo strsep.lo
nodist_libkafs_la_OBJECTS = $(am__objects_2)
libkafs_la_OBJECTS = $(dist_libkafs_la_OBJECTS) \
$(nodist_libkafs_la_OBJECTS)
-libkafs_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libkafs_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libkafs_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(EXTRA_libkafs_la_SOURCES) $(dist_libkafs_la_SOURCES) \
$(nodist_libkafs_la_SOURCES)
DIST_SOURCES = $(EXTRA_libkafs_la_SOURCES) \
$(am__dist_libkafs_la_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
man3dir = $(mandir)/man3
MANS = $(man_MANS)
DATA = $(foo_DATA)
HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -182,12 +295,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -197,17 +310,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -226,12 +339,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -240,6 +350,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -256,10 +367,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -267,6 +376,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -281,12 +391,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -303,6 +416,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -317,13 +431,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -347,6 +455,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -382,7 +492,8 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken) $(AFS_EXTRA_DEFS) $(ROKEN_RENAME) \
$(krb5_am_workaround)
@@ -390,26 +501,33 @@ AM_CPPFLAGS = $(INCLUDES_roken) $(AFS_EXTRA_DEFS) $(ROKEN_RENAME) \
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
@KRB5_FALSE@DEPLIB_krb5 =
-@KRB5_TRUE@DEPLIB_krb5 = ../krb5/libkrb5.la
+@KRB5_TRUE@DEPLIB_krb5 = ../krb5/libkrb5.la $(LIB_hcrypto)
@KRB5_FALSE@krb5_am_workaround =
-@KRB5_TRUE@krb5_am_workaround = $(INCLUDE_hcrypto) -I$(top_srcdir)/lib/krb5
+@KRB5_TRUE@krb5_am_workaround = -I$(top_srcdir)/lib/krb5
@AIX_FALSE@AFSL_EXP =
@AIX_TRUE@AFSL_EXP = $(srcdir)/afsl.exp
@AIX4_FALSE@@AIX_TRUE@AFS_EXTRA_LD = -e _nostart
@@ -434,6 +552,7 @@ include_HEADERS = kafs.h
dist_libkafs_la_SOURCES = \
afssys.c \
afskrb5.c \
+ rxkad_kdf.c \
common.c \
$(AIX_SRC) \
kafs_locl.h \
@@ -447,7 +566,7 @@ man_MANS = kafs.3
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -460,7 +579,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/kafs/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/kafs/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -469,6 +587,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -478,9 +597,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -488,6 +607,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -503,14 +624,17 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libkafs.la: $(libkafs_la_OBJECTS) $(libkafs_la_DEPENDENCIES)
- $(libkafs_la_LINK) -rpath $(libdir) $(libkafs_la_OBJECTS) $(libkafs_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libkafs.la: $(libkafs_la_OBJECTS) $(libkafs_la_DEPENDENCIES) $(EXTRA_libkafs_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libkafs_la_LINK) -rpath $(libdir) $(libkafs_la_OBJECTS) $(libkafs_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -523,30 +647,31 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/afssys.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolve.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rxkad_kdf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strsep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strtok_r.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -555,11 +680,18 @@ clean-libtool:
-rm -rf .libs _libs
install-man3: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man3dir)" || $(MKDIR_P) "$(DESTDIR)$(man3dir)"
- @list=''; test -n "$(man3dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.3[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.3[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -588,13 +720,14 @@ uninstall-man3:
sed -n '/\.3[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man3dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man3dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
install-fooDATA: $(foo_DATA)
@$(NORMAL_INSTALL)
- test -z "$(foodir)" || $(MKDIR_P) "$(DESTDIR)$(foodir)"
@list='$(foo_DATA)'; test -n "$(foodir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(foodir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(foodir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -608,13 +741,14 @@ uninstall-fooDATA:
@$(NORMAL_UNINSTALL)
@list='$(foo_DATA)'; test -n "$(foodir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(foodir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(foodir)" && rm -f $$files
+ dir='$(DESTDIR)$(foodir)'; $(am__uninstall_files_from_dir)
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -628,30 +762,17 @@ uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -663,15 +784,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -680,24 +797,26 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
- @list='$(MANS)'; if test -n "$$list"; then \
- list=`for p in $$list; do \
- if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
- if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
- if test -n "$$list" && \
- grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
- echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
- grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
- echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
- echo " typically \`make maintainer-clean' will remove them" >&2; \
- exit 1; \
- else :; fi; \
- else :; fi
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -748,10 +867,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -794,9 +918,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -841,42 +964,56 @@ uninstall-am: uninstall-fooDATA uninstall-includeHEADERS \
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
uninstall-man: uninstall-man3
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-generic clean-libLTLIBRARIES clean-libtool ctags \
- dist-hook distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-data \
- install-data-am install-data-hook install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-fooDATA \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-hook install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-local install-fooDATA \
install-html install-html-am install-includeHEADERS \
install-info install-info-am install-libLTLIBRARIES \
install-man install-man3 install-pdf install-pdf-am install-ps \
install-ps-am install-strip installcheck installcheck-am \
installdirs maintainer-clean maintainer-clean-generic \
mostlyclean mostlyclean-compile mostlyclean-generic \
- mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-fooDATA uninstall-hook \
uninstall-includeHEADERS uninstall-libLTLIBRARIES \
uninstall-man uninstall-man3
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-exec-hook: install-suid-programs
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -884,7 +1021,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -941,6 +1078,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -983,6 +1122,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -996,13 +1148,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/kafs/afskrb5.c b/lib/kafs/afskrb5.c
index c04f43abbc25..6033f2958b45 100644
--- a/lib/kafs/afskrb5.c
+++ b/lib/kafs/afskrb5.c
@@ -51,10 +51,6 @@ v5_to_kt(krb5_creds *cred, uid_t uid, struct kafs_token *kt, int local524)
kt->ticket = NULL;
- /* check if des key */
- if (cred->session.keyvalue.length != 8)
- return EINVAL;
-
if (local524) {
Ticket t;
unsigned char *buf;
@@ -98,8 +94,16 @@ v5_to_kt(krb5_creds *cred, uid_t uid, struct kafs_token *kt, int local524)
* Build a struct ClearToken
*/
+ ret = _kafs_derive_des_key(cred->session.keytype,
+ cred->session.keyvalue.data,
+ cred->session.keyvalue.length,
+ kt->ct.HandShakeKey);
+ if (ret) {
+ free(kt->ticket);
+ kt->ticket = NULL;
+ return ret;
+ }
kt->ct.AuthHandle = kvno;
- memcpy(kt->ct.HandShakeKey, cred->session.keyvalue.data, 8);
kt->ct.ViceId = uid;
kt->ct.BeginTimestamp = cred->times.starttime;
kt->ct.EndTimestamp = cred->times.endtime;
@@ -163,21 +167,15 @@ get_cred(struct kafs_data *data, const char *name, const char *inst,
return ret;
}
- in_creds.session.keytype = ETYPE_DES_CBC_CRC;
-
/* check if des is disable, and in that case enable it for afs */
- invalid = krb5_enctype_valid(d->context, in_creds.session.keytype);
+ invalid = krb5_enctype_valid(d->context, ETYPE_DES_CBC_CRC);
if (invalid)
- krb5_enctype_enable(d->context, in_creds.session.keytype);
+ krb5_enctype_enable(d->context, ETYPE_DES_CBC_CRC);
ret = krb5_get_credentials(d->context, 0, d->id, &in_creds, &out_creds);
- if (ret) {
- in_creds.session.keytype = ETYPE_DES_CBC_MD5;
- ret = krb5_get_credentials(d->context, 0, d->id, &in_creds, &out_creds);
- }
if (invalid)
- krb5_enctype_disable(d->context, in_creds.session.keytype);
+ krb5_enctype_disable(d->context, ETYPE_DES_CBC_CRC);
krb5_free_principal(d->context, in_creds.server);
krb5_free_principal(d->context, in_creds.client);
diff --git a/lib/kafs/afssys.c b/lib/kafs/afssys.c
index 740df0450584..fe37c346b62b 100644
--- a/lib/kafs/afssys.c
+++ b/lib/kafs/afssys.c
@@ -40,7 +40,12 @@ struct procdata {
unsigned long param1;
unsigned long syscall;
};
+#ifdef __GNU__
+#define _IOT_procdata _IOT(_IOTS(long), 5, 0, 0, 0, 0)
+#define VIOC_SYSCALL_PROC _IOW('C', 1, struct procdata)
+#else
#define VIOC_SYSCALL_PROC _IOW('C', 1, void *)
+#endif
struct devdata {
unsigned long syscall;
@@ -52,11 +57,40 @@ struct devdata {
unsigned long param6;
unsigned long retval;
};
+#ifdef __GNU__
+#define _IOT_devdata _IOT(_IOTS(long), 8, 0, 0, 0, 0)
+#endif
#ifdef _IOWR
#define VIOC_SYSCALL_DEV _IOWR('C', 2, struct devdata)
#define VIOC_SYSCALL_DEV_OPENAFS _IOWR('C', 1, struct devdata)
#endif
+#ifdef _IOW
+#ifdef _ILP32
+struct sundevdata {
+ uint32_t param6;
+ uint32_t param5;
+ uint32_t param4;
+ uint32_t param3;
+ uint32_t param2;
+ uint32_t param1;
+ uint32_t syscall;
+};
+#define VIOC_SUN_SYSCALL_DEV _IOW('C', 2, struct sundevdata)
+#else
+struct sundevdata {
+ uint64_t param6;
+ uint64_t param5;
+ uint64_t param4;
+ uint64_t param3;
+ uint64_t param2;
+ uint64_t param1;
+ uint64_t syscall;
+};
+#define VIOC_SUN_SYSCALL_DEV _IOW('C', 1, struct sundevdata)
+#endif
+#endif /* _IOW */
+
int _kafs_debug; /* this should be done in a better way */
@@ -69,6 +103,7 @@ int _kafs_debug; /* this should be done in a better way */
#define LINUX_PROC_POINT 5
#define AIX_ENTRY_POINTS 6
#define MACOS_DEV_POINT 7
+#define SUN_PROC_POINT 8
static int afs_entry_point = UNKNOWN_ENTRY_POINT;
static int afs_syscalls[2];
@@ -184,6 +219,12 @@ try_ioctlpath(const char *path, unsigned long ioctlnum, int entrypoint)
ret = ioctl(fd, ioctlnum, &data);
break;
}
+ case SUN_PROC_POINT: {
+ struct sundevdata data = { 0, 0, 0, 0, 0, 0, AFSCALL_PIOCTL };
+ data.param2 = (unsigned long)VIOCGETTOK;
+ ret = ioctl(fd, ioctlnum, &data);
+ break;
+ }
default:
abort();
}
@@ -265,6 +306,14 @@ k_pioctl(char *a_path,
return data.retval;
}
+ case SUN_PROC_POINT: {
+ struct sundevdata data = { 0, 0, 0, 0, 0, 0, AFSCALL_PIOCTL };
+ data.param1 = (unsigned long)a_path;
+ data.param2 = (unsigned long)o_opcode;
+ data.param3 = (unsigned long)a_paramsP;
+ data.param4 = (unsigned long)a_followSymlinks;
+ return do_ioctl(&data);
+ }
#ifdef _AIX
case AIX_ENTRY_POINTS:
return Pioctl(a_path, o_opcode, a_paramsP, a_followSymlinks);
@@ -323,6 +372,10 @@ k_setpag(void)
return ret;
return data.retval;
}
+ case SUN_PROC_POINT: {
+ struct sundevdata data = { 0, 0, 0, 0, 0, 0, AFSCALL_SETPAG };
+ return do_ioctl(&data);
+ }
#ifdef _AIX
case AIX_ENTRY_POINTS:
return Setpag();
@@ -470,6 +523,12 @@ k_hasafs(void)
if (ret == 0)
goto done;
#endif
+#ifdef VIOC_SUN_SYSCALL_DEV
+ ret = try_ioctlpath("/dev/afs", VIOC_SUN_SYSCALL_DEV, SUN_PROC_POINT);
+ if (ret == 0)
+ goto done;
+#endif
+
#if defined(AFS_SYSCALL) || defined(AFS_SYSCALL2) || defined(AFS_SYSCALL3)
{
diff --git a/lib/kafs/common.c b/lib/kafs/common.c
index a14eea8dd2d2..ff42cf7ecbd6 100644
--- a/lib/kafs/common.c
+++ b/lib/kafs/common.c
@@ -349,13 +349,19 @@ _kafs_try_get_cred(struct kafs_data *data, const char *user, const char *cell,
if (kafs_verbose) {
const char *estr = (*data->get_error)(data, ret);
char *str;
- asprintf(&str, "%s tried afs%s%s@%s -> %s (%d)",
- data->name, cell ? "/" : "",
- cell ? cell : "", realm, estr ? estr : "unknown", ret);
- (*kafs_verbose)(kafs_verbose_ctx, str);
+ int aret;
+
+ aret = asprintf(&str, "%s tried afs%s%s@%s -> %s (%d)",
+ data->name, cell ? "/" : "",
+ cell ? cell : "", realm, estr ? estr : "unknown", ret);
+ if (aret != -1) {
+ (*kafs_verbose)(kafs_verbose_ctx, str);
+ free(str);
+ } else {
+ (*kafs_verbose)(kafs_verbose_ctx, "out of memory");
+ }
if (estr)
(*data->free_error)(data, estr);
- free(str);
}
return ret;
diff --git a/lib/kafs/kafs.h b/lib/kafs/kafs.h
index dd20cab52048..6f6eb631d40a 100644
--- a/lib/kafs/kafs.h
+++ b/lib/kafs/kafs.h
@@ -46,6 +46,9 @@
#define AFSCALL_SETPAG 21
#ifndef _VICEIOCTL
+#ifdef __GNU__
+#define _IOT_ViceIoctl _IOT(_IOTS(caddr_t), 2, _IOTS(short), 2, 0, 0)
+#endif
#define _VICEIOCTL(id) ((unsigned int ) _IOW('V', id, struct ViceIoctl))
#define _AFSCIOCTL(id) ((unsigned int ) _IOW('C', id, struct ViceIoctl))
#endif /* _VICEIOCTL */
diff --git a/lib/kafs/kafs_locl.h b/lib/kafs/kafs_locl.h
index ea670f31388d..6f34ca631709 100644
--- a/lib/kafs/kafs_locl.h
+++ b/lib/kafs/kafs_locl.h
@@ -99,7 +99,9 @@
#include <krb5-v4compat.h>
typedef struct credentials CREDENTIALS;
#endif /* KRB5 */
+#ifndef NO_AFS
#include <kafs.h>
+#endif
#include <resolve.h>
@@ -150,6 +152,9 @@ _kafs_v4_to_kt(CREDENTIALS *, uid_t, struct kafs_token *);
void
_kafs_fixup_viceid(struct ClearToken *, uid_t);
+int
+_kafs_derive_des_key(krb5_enctype, void *, size_t, char[8]);
+
#ifdef _AIX
int aix_pioctl(char*, int, struct ViceIoctl*, int);
int aix_setpag(void);
diff --git a/lib/kafs/rxkad_kdf.c b/lib/kafs/rxkad_kdf.c
new file mode 100644
index 000000000000..21dd3543d836
--- /dev/null
+++ b/lib/kafs/rxkad_kdf.c
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 1995-2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2013-2014 Carnegie Mellon University
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2013 by the Massachusetts Institute of Technology
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "kafs_locl.h"
+
+static int rxkad_derive_des_key(const void *, size_t, char[8]);
+static int compress_parity_bits(void *, size_t *);
+
+/**
+ * Use NIST SP800-108 with HMAC(MD5) in counter mode as the PRF to derive a
+ * des key from another type of key.
+ *
+ * L is 64, as we take 64 random bits and turn them into a 56-bit des key.
+ * The output of hmac_md5 is 128 bits; we take the first 64 only, so n
+ * properly should be 1. However, we apply a slight variation due to the
+ * possibility of producing a weak des key. If the output key is weak, do NOT
+ * simply correct it, instead, the counter is advanced and the next output
+ * used. As such, we code so as to have n be the full 255 permitted by our
+ * encoding of the counter i in an 8-bit field. L itself is encoded as a
+ * 32-bit field, big-endian. We use the constant string "rxkad" as a label
+ * for this key derivation, the standard NUL byte separator, and omit a
+ * key-derivation context. The input key is unique to the krb5 service ticket,
+ * which is unlikely to be used in an other location. If it is used in such
+ * a fashion, both locations will derive the same des key from the PRF, but
+ * this is no different from if a krb5 des key had been used in the same way,
+ * as traditional krb5 rxkad uses the ticket session key directly as the token
+ * key.
+ *
+ * @param[in] in pointer to input key data
+ * @param[in] insize length of input key data
+ * @param[out] out 8-byte buffer to hold the derived key
+ *
+ * @return Returns 0 to indicate success, or an error code.
+ *
+ * @retval KRB5DES_WEAK_KEY Successive derivation attempts with all
+ * 255 possible counter values each produced weak DES keys. This input
+ * cannot be used to produce a usable key.
+ */
+static int
+rxkad_derive_des_key(const void *in, size_t insize, char out[8])
+{
+ unsigned char i;
+ static unsigned char label[] = "rxkad";
+ /* bits of output, as 32 bit word, MSB first */
+ static unsigned char Lbuf[4] = { 0, 0, 0, 64 };
+ /* only needs to be 16 for md5, but lets be sure it fits */
+ unsigned char tmp[64];
+ unsigned int mdsize;
+ DES_cblock ktmp;
+ HMAC_CTX mctx;
+
+ /* stop when 8 bit counter wraps to 0 */
+ for (i = 1; i; i++) {
+ HMAC_CTX_init(&mctx);
+ HMAC_Init_ex(&mctx, in, insize, EVP_md5(), NULL);
+ HMAC_Update(&mctx, &i, 1);
+ HMAC_Update(&mctx, label, sizeof(label)); /* includes label and separator */
+ HMAC_Update(&mctx, Lbuf, 4);
+ mdsize = sizeof(tmp);
+ HMAC_Final(&mctx, tmp, &mdsize);
+ memcpy(ktmp, tmp, 8);
+ DES_set_odd_parity(&ktmp);
+ if (!DES_is_weak_key(&ktmp)) {
+ memcpy(out, ktmp, 8);
+ return 0;
+ }
+ }
+ return KRB5DES_WEAK_KEY;
+}
+
+/**
+ * This is the inverse of the random-to-key for 3des specified in
+ * rfc3961, converting blocks of 8 bytes to blocks of 7 bytes by distributing
+ * the bits of each 8th byte as the lsb of the previous 7 bytes.
+ *
+ * @param[in,out] buffer Buffer containing the key to be converted
+ * @param[in,out] bufsiz Points to the size of the key data. On
+ * return, this is updated to reflect the size of the compressed data.
+ *
+ * @return Returns 0 to indicate success, or an error code.
+ *
+ * @retval KRB5_BAD_KEYSIZE The key size was not a multiple of 8 bytes.
+ */
+static int
+compress_parity_bits(void *buffer, size_t *bufsiz)
+{
+ unsigned char *cb, tmp;
+ int i, j, nk;
+
+ if (*bufsiz % 8 != 0)
+ return KRB5_BAD_KEYSIZE;
+ cb = (unsigned char *)buffer;
+ nk = *bufsiz / 8;
+ for (i = 0; i < nk; i++) {
+ tmp = cb[8 * i + 7] >> 1;
+ for (j = 0; j < 7; j++) {
+ cb[8 * i + j] &= 0xfe;
+ cb[8 * i + j] |= tmp & 0x1;
+ tmp >>= 1;
+ }
+ }
+ for (i = 1; i < nk; i++)
+ memmove(cb + 7 * i, cb + 8 * i, 7);
+ *bufsiz = 7 * nk;
+ return 0;
+}
+
+/**
+ * Derive a DES key for use with rxkad and fcrypt from a given Kerberos
+ * key of (almost) any type. This function encodes enctype-specific
+ * knowledge about how to derive a DES key from a given key type.
+ * If given a des key, use it directly; otherwise, perform any parity
+ * fixup that may be needed and pass through to the hmad-md5 bits.
+ *
+ * @param[in] enctype Kerberos enctype of the input key
+ * @param[in] keydata Input key data
+ * @param[in] keylen Size of input key data
+ * @param[out] output 8-byte buffer to hold the derived key
+ *
+ * @return Returns 0 to indicate success, or an error code.
+ *
+ * @retval KRB5_PROG_ETYPE_NOSUPP The enctype is one for which rxkad-kdf
+ * is not supported. This includes several reserved enctypes, enctype
+ * values used in PKINIT to stand for CMS algorithm identifiers, and all
+ * private-use (negative) enctypes.
+ *
+ * @retval KRB5_BAD_KEYSIZE The key size was not a multiple of 8 bytes
+ * (for 3DES key types), exactly 8 bytes (for DES key types), or at least
+ * 8 bytes (for other key types).
+ *
+ * @retval KRB5DES_WEAK_KEY Successive derivation attempts with all
+ * 255 possible counter values each produced weak DES keys. This input
+ * cannot be used to produce a usable key.
+ */
+int
+_kafs_derive_des_key(krb5_enctype enctype, void *keydata, size_t keylen,
+ char output[8])
+{
+ int ret = 0;
+
+ switch ((int)enctype) {
+ case ETYPE_DES_CBC_CRC:
+ case ETYPE_DES_CBC_MD4:
+ case ETYPE_DES_CBC_MD5:
+ if (keylen != 8)
+ return KRB5_BAD_KEYSIZE;
+
+ /* Extract session key */
+ memcpy(output, keydata, 8);
+ break;
+ case ETYPE_NULL:
+ case 4:
+ case 6:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ return KRB5_PROG_ETYPE_NOSUPP;
+ /*In order to become a "Cryptographic Key" as specified in
+ * SP800-108, it must be indistinguishable from a random bitstring. */
+ case ETYPE_DES3_CBC_MD5:
+ case ETYPE_OLD_DES3_CBC_SHA1:
+ case ETYPE_DES3_CBC_SHA1:
+ ret = compress_parity_bits(keydata, &keylen);
+ if (ret)
+ return ret;
+ /* FALLTHROUGH */
+ default:
+ if (enctype < 0)
+ return KRB5_PROG_ETYPE_NOSUPP;
+ if (keylen < 7)
+ return KRB5_BAD_KEYSIZE;
+ ret = rxkad_derive_des_key(keydata, keylen, output);
+ }
+ return ret;
+}
diff --git a/lib/kdfs/Makefile.in b/lib/kdfs/Makefile.in
index e8ce07b8e5ff..b9579b318707 100644
--- a/lib/kdfs/Makefile.in
+++ b/lib/kdfs/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -22,6 +21,61 @@
# $Id$
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -40,9 +94,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
- $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
subdir = lib/kdfs
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -58,8 +109,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -72,6 +122,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -91,6 +142,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -116,35 +168,94 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libkdfs_la_LIBADD =
am_libkdfs_la_OBJECTS = k5dfspag.lo
libkdfs_la_OBJECTS = $(am_libkdfs_la_OBJECTS)
-libkdfs_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libkdfs_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libkdfs_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(libkdfs_la_SOURCES)
DIST_SOURCES = $(libkdfs_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -163,12 +274,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -178,17 +289,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -207,12 +318,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -221,6 +329,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -237,10 +346,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -248,6 +355,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -262,12 +370,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -284,6 +395,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -298,13 +410,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -328,6 +434,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -363,29 +471,37 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
lib_LTLIBRARIES = libkdfs.la
libkdfs_la_SOURCES = \
k5dfspag.c
@@ -395,7 +511,7 @@ EXTRA_DIST = NTMakefile
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -408,7 +524,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/kdfs/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/kdfs/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -417,6 +532,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -426,9 +542,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -436,6 +552,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -451,14 +569,17 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libkdfs.la: $(libkdfs_la_OBJECTS) $(libkdfs_la_DEPENDENCIES)
- $(libkdfs_la_LINK) -rpath $(libdir) $(libkdfs_la_OBJECTS) $(libkdfs_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libkdfs.la: $(libkdfs_la_OBJECTS) $(libkdfs_la_DEPENDENCIES) $(EXTRA_libkdfs_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libkdfs_la_LINK) -rpath $(libdir) $(libkdfs_la_OBJECTS) $(libkdfs_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -469,25 +590,25 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/k5dfspag.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -495,26 +616,15 @@ mostlyclean-libtool:
clean-libtool:
-rm -rf .libs _libs
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -526,15 +636,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -543,6 +649,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -598,10 +719,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -643,9 +769,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -687,40 +812,54 @@ ps-am:
uninstall-am: uninstall-libLTLIBRARIES
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-generic clean-libLTLIBRARIES clean-libtool ctags \
- dist-hook distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-data \
- install-data-am install-data-hook install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-hook install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-local install-html \
install-html-am install-info install-info-am \
install-libLTLIBRARIES install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-hook \
+ tags tags-am uninstall uninstall-am uninstall-hook \
uninstall-libLTLIBRARIES
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
-install-exec-hook: install-suid-programs
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -728,7 +867,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -785,6 +924,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -827,6 +968,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -840,13 +994,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/kdfs/k5dfspag.c b/lib/kdfs/k5dfspag.c
index 2db635bee860..66d291a07cf1 100644
--- a/lib/kdfs/k5dfspag.c
+++ b/lib/kdfs/k5dfspag.c
@@ -236,7 +236,7 @@ int krb5_dfs_getpag()
* user.
*
* And it should be called before access to the home directory
- * as this may be in DFS, not accessable by root, and require
+ * as this may be in DFS, not accessible by root, and require
* the PAG to have been setup.
*
* The krb5_afs_pag can be called after this routine to
diff --git a/lib/krb5/Makefile.am b/lib/krb5/Makefile.am
index 9429535eb962..74d8ae171c5a 100644
--- a/lib/krb5/Makefile.am
+++ b/lib/krb5/Makefile.am
@@ -2,20 +2,21 @@
include $(top_srcdir)/Makefile.am.common
-AM_CPPFLAGS += $(INCLUDE_krb4) $(INCLUDE_hcrypto) -I../com_err -I$(srcdir)/../com_err $(INCLUDE_sqlite3) $(INCLUDE_libintl)
+AM_CPPFLAGS += -I../com_err -I$(srcdir)/../com_err $(INCLUDE_sqlite3) $(INCLUDE_libintl) $(INCLUDE_openssl_crypto)
bin_PROGRAMS = verify_krb5_conf
noinst_PROGRAMS = \
krbhst-test \
- test_gic \
test_alname \
test_crypto \
- test_rfc3961 \
+ test_forward \
test_get_addrs \
+ test_gic \
test_kuserok \
test_renew \
- test_forward
+ test_rfc3961
+
noinst_LTLIBRARIES = \
librfc3961.la
@@ -25,6 +26,7 @@ TESTS = \
derived-key-test \
n-fold-test \
parse-name-test \
+ pseudo-random-test \
store-test \
string-to-key-test \
test_acl \
@@ -43,17 +45,18 @@ TESTS = \
test_pkinit_dh2key \
test_pknistkdf \
test_time \
+ test_expand_toks \
test_x500
check_DATA = test_config_strings.out
-check_PROGRAMS = $(TESTS) test_hostname test_ap-req
+check_PROGRAMS = $(TESTS) test_hostname test_ap-req test_canon test_set_kvno0
LDADD = libkrb5.la \
$(LIB_hcrypto) \
$(top_builddir)/lib/asn1/libasn1.la \
$(top_builddir)/lib/wind/libwind.la \
- $(LIB_roken)
+ $(LIB_heimbase) $(LIB_roken)
if PKINIT
LIB_pkinit = ../hx509/libhx509.la
@@ -67,8 +70,9 @@ libkrb5_la_LIBADD = \
$(top_builddir)/lib/asn1/libasn1.la \
$(top_builddir)/lib/ipc/libheim-ipcc.la \
$(top_builddir)/lib/wind/libwind.la \
- $(top_builddir)/base/libheimbase.la \
+ $(top_builddir)/lib/base/libheimbase.la \
$(LIB_pkinit) \
+ $(LIB_openssl_crypto) \
$(use_sqlite) \
$(LIB_com_err) \
$(LIB_hcrypto) \
@@ -111,6 +115,7 @@ dist_libkrb5_la_SOURCES = \
acl.c \
add_et_list.c \
addr_families.c \
+ an2ln_plugin.h \
aname_to_localname.c \
appdefault.c \
asn1_glue.c \
@@ -129,7 +134,8 @@ dist_libkrb5_la_SOURCES = \
creds.c \
crypto.c \
crypto.h \
- crypto-aes.c \
+ crypto-aes-sha1.c \
+ crypto-aes-sha2.c \
crypto-algs.c \
crypto-arcfour.c \
crypto-des.c \
@@ -141,12 +147,17 @@ dist_libkrb5_la_SOURCES = \
crypto-rand.c \
doxygen.c \
data.c \
+ db_plugin.c \
+ db_plugin.h \
+ dcache.c \
deprecated.c \
digest.c \
eai_to_heim_errno.c \
+ enomem.c \
error_string.c \
expand_hostname.c \
expand_path.c \
+ fast.c \
fcache.c \
free.c \
free_host_realm.c \
@@ -174,6 +185,7 @@ dist_libkrb5_la_SOURCES = \
krb5-v4compat.h \
krbhst.c \
kuserok.c \
+ kuserok_plugin.h \
log.c \
mcache.c \
misc.c \
@@ -191,6 +203,7 @@ dist_libkrb5_la_SOURCES = \
padata.c \
pcache.c \
pkinit.c \
+ pkinit-ec.c \
principal.c \
prog_setup.c \
prompter_posix.c \
@@ -204,10 +217,12 @@ dist_libkrb5_la_SOURCES = \
recvauth.c \
replay.c \
salt.c \
- salt-aes.c \
+ salt-aes-sha1.c \
+ salt-aes-sha2.c \
salt-arcfour.c \
salt-des.c \
salt-des3.c \
+ sp800-108-kdf.c \
scache.c \
send_to_kdc.c \
sendauth.c \
@@ -219,6 +234,7 @@ dist_libkrb5_la_SOURCES = \
store_emem.c \
store_fd.c \
store_mem.c \
+ store_sock.c \
plugin.c \
ticket.c \
time.c \
@@ -241,13 +257,29 @@ if versionscript
libkrb5_la_LDFLAGS += $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
endif
-$(libkrb5_la_OBJECTS) $(verify_krb5_conf_OBJECTS) $(librfc3961_la_OBJECTS): $(srcdir)/krb5-protos.h $(srcdir)/krb5-private.h krb5_err.h heim_err.h k524_err.h krb5_err.h krb_err.h k524_err.h
+ALL_OBJECTS = $(libkrb5_la_OBJECTS)
+ALL_OBJECTS += $(verify_krb5_conf_OBJECTS)
+ALL_OBJECTS += $(librfc3961_la_OBJECTS)
+ALL_OBJECTS += $(librfc3961_la_OBJECTS)
+ALL_OBJECTS += $(krbhst_test_OBJECTS)
+ALL_OBJECTS += $(test_alname_OBJECTS)
+ALL_OBJECTS += $(test_crypto_OBJECTS)
+ALL_OBJECTS += $(test_forward_OBJECTS)
+ALL_OBJECTS += $(test_get_addrs_OBJECTS)
+ALL_OBJECTS += $(test_gic_OBJECTS)
+ALL_OBJECTS += $(test_kuserok_OBJECTS)
+ALL_OBJECTS += $(test_renew_OBJECTS)
+ALL_OBJECTS += $(test_rfc3961_OBJECTS)
+
+$(ALL_OBJECTS): $(srcdir)/krb5-protos.h $(srcdir)/krb5-private.h
+$(ALL_OBJECTS): krb5_err.h heim_err.h k524_err.h krb5_err.h krb_err.h k524_err.h
librfc3961_la_SOURCES = \
crc.c \
crypto.c \
crypto.h \
- crypto-aes.c \
+ crypto-aes-sha1.c \
+ crypto-aes-sha2.c \
crypto-algs.c \
crypto-arcfour.c \
crypto-des.c \
@@ -259,14 +291,17 @@ librfc3961_la_SOURCES = \
crypto-rand.c \
crypto-stubs.c \
data.c \
+ enomem.c \
error_string.c \
keyblock.c \
n-fold.c \
salt.c \
- salt-aes.c \
+ salt-aes-sha1.c \
+ salt-aes-sha2.c \
salt-arcfour.c \
salt-des.c \
salt-des3.c \
+ sp800-108-kdf.c \
store-int.c \
warn.c
@@ -277,15 +312,20 @@ test_rfc3961_LDADD = \
$(LIB_hcrypto) \
$(LIB_roken)
-$(srcdir)/krb5-protos.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -E KRB5_LIB -q -P comment -o krb5-protos.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-protos.h
+if DEVELOPER_MODE
+headerdeps = $(dist_libkrb5_la_SOURCES)
+endif
+
+$(srcdir)/krb5-protos.h: $(headerdeps)
+ @cd $(srcdir) && perl ../../cf/make-proto.pl -E KRB5_LIB -q -P comment -o krb5-protos.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-protos.h
-$(srcdir)/krb5-private.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p krb5-private.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-private.h
+$(srcdir)/krb5-private.h: $(headerdeps)
+ @cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p krb5-private.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-private.h
man_MANS = \
kerberos.8 \
krb5.conf.5 \
+ krb5-plugin.7 \
krb524_convert_creds_kdc.3 \
krb5_425_conv_principal.3 \
krb5_acl_match_file.3 \
@@ -330,15 +370,17 @@ man_MANS = \
dist_include_HEADERS = \
krb5.h \
- krb5-protos.h \
- krb5-private.h \
+ $(srcdir)/krb5-protos.h \
krb5_ccapi.h
+noinst_HEADERS = $(srcdir)/krb5-private.h
+
+
nodist_include_HEADERS = krb5_err.h heim_err.h k524_err.h
# XXX use nobase_include_HEADERS = krb5/locate_plugin.h
krb5dir = $(includedir)/krb5
-krb5_HEADERS = locate_plugin.h send_to_kdc_plugin.h ccache_plugin.h
+krb5_HEADERS = locate_plugin.h send_to_kdc_plugin.h ccache_plugin.h an2ln_plugin.h db_plugin.h
build_HEADERZ = \
$(krb5_HEADERS) \
@@ -359,6 +401,9 @@ test_config_strings.out: test_config_strings.cfg
EXTRA_DIST = \
NTMakefile \
+ config_reg.c \
+ dll.c \
+ libkrb5-exports.def.in \
verify_krb5_conf-version.rc \
krb5_err.et \
krb_err.et \
diff --git a/lib/krb5/Makefile.in b/lib/krb5/Makefile.in
index 2473eb2cae51..8107e6bcd60e 100644
--- a/lib/krb5/Makefile.in
+++ b/lib/krb5/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,28 +96,26 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(dist_include_HEADERS) $(krb5_HEADERS) \
- $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
- $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common
bin_PROGRAMS = verify_krb5_conf$(EXEEXT)
-noinst_PROGRAMS = krbhst-test$(EXEEXT) test_gic$(EXEEXT) \
- test_alname$(EXEEXT) test_crypto$(EXEEXT) \
- test_rfc3961$(EXEEXT) test_get_addrs$(EXEEXT) \
+noinst_PROGRAMS = krbhst-test$(EXEEXT) test_alname$(EXEEXT) \
+ test_crypto$(EXEEXT) test_forward$(EXEEXT) \
+ test_get_addrs$(EXEEXT) test_gic$(EXEEXT) \
test_kuserok$(EXEEXT) test_renew$(EXEEXT) \
- test_forward$(EXEEXT)
+ test_rfc3961$(EXEEXT)
TESTS = aes-test$(EXEEXT) derived-key-test$(EXEEXT) \
n-fold-test$(EXEEXT) parse-name-test$(EXEEXT) \
- store-test$(EXEEXT) string-to-key-test$(EXEEXT) \
- test_acl$(EXEEXT) test_addr$(EXEEXT) test_cc$(EXEEXT) \
- test_config$(EXEEXT) test_fx$(EXEEXT) test_prf$(EXEEXT) \
- test_store$(EXEEXT) test_crypto_wrapping$(EXEEXT) \
- test_keytab$(EXEEXT) test_mem$(EXEEXT) test_pac$(EXEEXT) \
- test_plugin$(EXEEXT) test_princ$(EXEEXT) \
- test_pkinit_dh2key$(EXEEXT) test_pknistkdf$(EXEEXT) \
- test_time$(EXEEXT) test_x500$(EXEEXT)
+ pseudo-random-test$(EXEEXT) store-test$(EXEEXT) \
+ string-to-key-test$(EXEEXT) test_acl$(EXEEXT) \
+ test_addr$(EXEEXT) test_cc$(EXEEXT) test_config$(EXEEXT) \
+ test_fx$(EXEEXT) test_prf$(EXEEXT) test_store$(EXEEXT) \
+ test_crypto_wrapping$(EXEEXT) test_keytab$(EXEEXT) \
+ test_mem$(EXEEXT) test_pac$(EXEEXT) test_plugin$(EXEEXT) \
+ test_princ$(EXEEXT) test_pkinit_dh2key$(EXEEXT) \
+ test_pknistkdf$(EXEEXT) test_time$(EXEEXT) \
+ test_expand_toks$(EXEEXT) test_x500$(EXEEXT)
check_PROGRAMS = $(am__EXEEXT_1) test_hostname$(EXEEXT) \
- test_ap-req$(EXEEXT)
+ test_ap-req$(EXEEXT) test_canon$(EXEEXT) \
+ test_set_kvno0$(EXEEXT)
@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
subdir = lib/krb5
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -80,8 +132,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -94,6 +145,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -113,6 +165,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_include_HEADERS) \
+ $(krb5_HEADERS) $(noinst_HEADERS) $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -138,10 +192,17 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
"$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" \
- "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(includedir)" \
- "$(DESTDIR)$(krb5dir)" "$(DESTDIR)$(includedir)"
+ "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(man8dir)" \
+ "$(DESTDIR)$(includedir)" "$(DESTDIR)$(krb5dir)" \
+ "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
am__DEPENDENCIES_1 =
@have_scc_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
@@ -155,16 +216,18 @@ dist_libkrb5_la_OBJECTS = libkrb5_la-acache.lo libkrb5_la-acl.lo \
libkrb5_la-constants.lo libkrb5_la-context.lo \
libkrb5_la-copy_host_realm.lo libkrb5_la-crc.lo \
libkrb5_la-creds.lo libkrb5_la-crypto.lo \
- libkrb5_la-crypto-aes.lo libkrb5_la-crypto-algs.lo \
- libkrb5_la-crypto-arcfour.lo libkrb5_la-crypto-des.lo \
- libkrb5_la-crypto-des-common.lo libkrb5_la-crypto-des3.lo \
- libkrb5_la-crypto-evp.lo libkrb5_la-crypto-null.lo \
- libkrb5_la-crypto-pk.lo libkrb5_la-crypto-rand.lo \
- libkrb5_la-doxygen.lo libkrb5_la-data.lo \
- libkrb5_la-deprecated.lo libkrb5_la-digest.lo \
- libkrb5_la-eai_to_heim_errno.lo libkrb5_la-error_string.lo \
+ libkrb5_la-crypto-aes-sha1.lo libkrb5_la-crypto-aes-sha2.lo \
+ libkrb5_la-crypto-algs.lo libkrb5_la-crypto-arcfour.lo \
+ libkrb5_la-crypto-des.lo libkrb5_la-crypto-des-common.lo \
+ libkrb5_la-crypto-des3.lo libkrb5_la-crypto-evp.lo \
+ libkrb5_la-crypto-null.lo libkrb5_la-crypto-pk.lo \
+ libkrb5_la-crypto-rand.lo libkrb5_la-doxygen.lo \
+ libkrb5_la-data.lo libkrb5_la-db_plugin.lo \
+ libkrb5_la-dcache.lo libkrb5_la-deprecated.lo \
+ libkrb5_la-digest.lo libkrb5_la-eai_to_heim_errno.lo \
+ libkrb5_la-enomem.lo libkrb5_la-error_string.lo \
libkrb5_la-expand_hostname.lo libkrb5_la-expand_path.lo \
- libkrb5_la-fcache.lo libkrb5_la-free.lo \
+ libkrb5_la-fast.lo libkrb5_la-fcache.lo libkrb5_la-free.lo \
libkrb5_la-free_host_realm.lo \
libkrb5_la-generate_seq_number.lo \
libkrb5_la-generate_subkey.lo libkrb5_la-get_addrs.lo \
@@ -184,20 +247,23 @@ dist_libkrb5_la_OBJECTS = libkrb5_la-acache.lo libkrb5_la-acl.lo \
libkrb5_la-net_read.lo libkrb5_la-net_write.lo \
libkrb5_la-n-fold.lo libkrb5_la-pac.lo libkrb5_la-padata.lo \
libkrb5_la-pcache.lo libkrb5_la-pkinit.lo \
- libkrb5_la-principal.lo libkrb5_la-prog_setup.lo \
- libkrb5_la-prompter_posix.lo libkrb5_la-rd_cred.lo \
- libkrb5_la-rd_error.lo libkrb5_la-rd_priv.lo \
- libkrb5_la-rd_rep.lo libkrb5_la-rd_req.lo \
- libkrb5_la-rd_safe.lo libkrb5_la-read_message.lo \
- libkrb5_la-recvauth.lo libkrb5_la-replay.lo libkrb5_la-salt.lo \
- libkrb5_la-salt-aes.lo libkrb5_la-salt-arcfour.lo \
- libkrb5_la-salt-des.lo libkrb5_la-salt-des3.lo \
+ libkrb5_la-pkinit-ec.lo libkrb5_la-principal.lo \
+ libkrb5_la-prog_setup.lo libkrb5_la-prompter_posix.lo \
+ libkrb5_la-rd_cred.lo libkrb5_la-rd_error.lo \
+ libkrb5_la-rd_priv.lo libkrb5_la-rd_rep.lo \
+ libkrb5_la-rd_req.lo libkrb5_la-rd_safe.lo \
+ libkrb5_la-read_message.lo libkrb5_la-recvauth.lo \
+ libkrb5_la-replay.lo libkrb5_la-salt.lo \
+ libkrb5_la-salt-aes-sha1.lo libkrb5_la-salt-aes-sha2.lo \
+ libkrb5_la-salt-arcfour.lo libkrb5_la-salt-des.lo \
+ libkrb5_la-salt-des3.lo libkrb5_la-sp800-108-kdf.lo \
libkrb5_la-scache.lo libkrb5_la-send_to_kdc.lo \
libkrb5_la-sendauth.lo libkrb5_la-set_default_realm.lo \
libkrb5_la-sock_principal.lo libkrb5_la-store.lo \
libkrb5_la-store-int.lo libkrb5_la-store_emem.lo \
libkrb5_la-store_fd.lo libkrb5_la-store_mem.lo \
- libkrb5_la-plugin.lo libkrb5_la-ticket.lo libkrb5_la-time.lo \
+ libkrb5_la-store_sock.lo libkrb5_la-plugin.lo \
+ libkrb5_la-ticket.lo libkrb5_la-time.lo \
libkrb5_la-transited.lo libkrb5_la-verify_init.lo \
libkrb5_la-verify_user.lo libkrb5_la-version.lo \
libkrb5_la-warn.lo libkrb5_la-write_message.lo
@@ -206,7 +272,11 @@ am__objects_1 = libkrb5_la-krb5_err.lo libkrb5_la-krb_err.lo \
nodist_libkrb5_la_OBJECTS = $(am__objects_1)
libkrb5_la_OBJECTS = $(dist_libkrb5_la_OBJECTS) \
$(nodist_libkrb5_la_OBJECTS)
-libkrb5_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libkrb5_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libkrb5_la_LDFLAGS) $(LDFLAGS) -o $@
librfc3961_la_DEPENDENCIES = $(top_builddir)/lib/asn1/libasn1.la \
@@ -217,288 +287,581 @@ librfc3961_la_DEPENDENCIES = $(top_builddir)/lib/asn1/libasn1.la \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
am_librfc3961_la_OBJECTS = librfc3961_la-crc.lo \
- librfc3961_la-crypto.lo librfc3961_la-crypto-aes.lo \
- librfc3961_la-crypto-algs.lo librfc3961_la-crypto-arcfour.lo \
- librfc3961_la-crypto-des.lo librfc3961_la-crypto-des-common.lo \
+ librfc3961_la-crypto.lo librfc3961_la-crypto-aes-sha1.lo \
+ librfc3961_la-crypto-aes-sha2.lo librfc3961_la-crypto-algs.lo \
+ librfc3961_la-crypto-arcfour.lo librfc3961_la-crypto-des.lo \
+ librfc3961_la-crypto-des-common.lo \
librfc3961_la-crypto-des3.lo librfc3961_la-crypto-evp.lo \
librfc3961_la-crypto-null.lo librfc3961_la-crypto-pk.lo \
librfc3961_la-crypto-rand.lo librfc3961_la-crypto-stubs.lo \
- librfc3961_la-data.lo librfc3961_la-error_string.lo \
- librfc3961_la-keyblock.lo librfc3961_la-n-fold.lo \
- librfc3961_la-salt.lo librfc3961_la-salt-aes.lo \
+ librfc3961_la-data.lo librfc3961_la-enomem.lo \
+ librfc3961_la-error_string.lo librfc3961_la-keyblock.lo \
+ librfc3961_la-n-fold.lo librfc3961_la-salt.lo \
+ librfc3961_la-salt-aes-sha1.lo librfc3961_la-salt-aes-sha2.lo \
librfc3961_la-salt-arcfour.lo librfc3961_la-salt-des.lo \
- librfc3961_la-salt-des3.lo librfc3961_la-store-int.lo \
- librfc3961_la-warn.lo
+ librfc3961_la-salt-des3.lo librfc3961_la-sp800-108-kdf.lo \
+ librfc3961_la-store-int.lo librfc3961_la-warn.lo
librfc3961_la_OBJECTS = $(am_librfc3961_la_OBJECTS)
am__EXEEXT_1 = aes-test$(EXEEXT) derived-key-test$(EXEEXT) \
n-fold-test$(EXEEXT) parse-name-test$(EXEEXT) \
- store-test$(EXEEXT) string-to-key-test$(EXEEXT) \
- test_acl$(EXEEXT) test_addr$(EXEEXT) test_cc$(EXEEXT) \
- test_config$(EXEEXT) test_fx$(EXEEXT) test_prf$(EXEEXT) \
- test_store$(EXEEXT) test_crypto_wrapping$(EXEEXT) \
- test_keytab$(EXEEXT) test_mem$(EXEEXT) test_pac$(EXEEXT) \
- test_plugin$(EXEEXT) test_princ$(EXEEXT) \
- test_pkinit_dh2key$(EXEEXT) test_pknistkdf$(EXEEXT) \
- test_time$(EXEEXT) test_x500$(EXEEXT)
+ pseudo-random-test$(EXEEXT) store-test$(EXEEXT) \
+ string-to-key-test$(EXEEXT) test_acl$(EXEEXT) \
+ test_addr$(EXEEXT) test_cc$(EXEEXT) test_config$(EXEEXT) \
+ test_fx$(EXEEXT) test_prf$(EXEEXT) test_store$(EXEEXT) \
+ test_crypto_wrapping$(EXEEXT) test_keytab$(EXEEXT) \
+ test_mem$(EXEEXT) test_pac$(EXEEXT) test_plugin$(EXEEXT) \
+ test_princ$(EXEEXT) test_pkinit_dh2key$(EXEEXT) \
+ test_pknistkdf$(EXEEXT) test_time$(EXEEXT) \
+ test_expand_toks$(EXEEXT) test_x500$(EXEEXT)
PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
aes_test_SOURCES = aes-test.c
aes_test_OBJECTS = aes-test.$(OBJEXT)
aes_test_LDADD = $(LDADD)
aes_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
derived_key_test_SOURCES = derived-key-test.c
derived_key_test_OBJECTS = derived-key-test.$(OBJEXT)
derived_key_test_LDADD = $(LDADD)
derived_key_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
krbhst_test_SOURCES = krbhst-test.c
krbhst_test_OBJECTS = krbhst-test.$(OBJEXT)
krbhst_test_LDADD = $(LDADD)
krbhst_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
n_fold_test_SOURCES = n-fold-test.c
n_fold_test_OBJECTS = n-fold-test.$(OBJEXT)
n_fold_test_LDADD = $(LDADD)
n_fold_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
parse_name_test_SOURCES = parse-name-test.c
parse_name_test_OBJECTS = parse-name-test.$(OBJEXT)
parse_name_test_LDADD = $(LDADD)
parse_name_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
+pseudo_random_test_SOURCES = pseudo-random-test.c
+pseudo_random_test_OBJECTS = pseudo-random-test.$(OBJEXT)
+pseudo_random_test_LDADD = $(LDADD)
+pseudo_random_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
store_test_SOURCES = store-test.c
store_test_OBJECTS = store-test.$(OBJEXT)
store_test_LDADD = $(LDADD)
store_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
string_to_key_test_SOURCES = string-to-key-test.c
string_to_key_test_OBJECTS = string-to-key-test.$(OBJEXT)
string_to_key_test_LDADD = $(LDADD)
string_to_key_test_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_acl_SOURCES = test_acl.c
test_acl_OBJECTS = test_acl.$(OBJEXT)
test_acl_LDADD = $(LDADD)
test_acl_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_addr_SOURCES = test_addr.c
test_addr_OBJECTS = test_addr.$(OBJEXT)
test_addr_LDADD = $(LDADD)
test_addr_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_alname_SOURCES = test_alname.c
test_alname_OBJECTS = test_alname.$(OBJEXT)
test_alname_LDADD = $(LDADD)
test_alname_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_ap_req_SOURCES = test_ap-req.c
test_ap_req_OBJECTS = test_ap-req.$(OBJEXT)
test_ap_req_LDADD = $(LDADD)
test_ap_req_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
+test_canon_SOURCES = test_canon.c
+test_canon_OBJECTS = test_canon.$(OBJEXT)
+test_canon_LDADD = $(LDADD)
+test_canon_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_cc_SOURCES = test_cc.c
test_cc_OBJECTS = test_cc.$(OBJEXT)
test_cc_LDADD = $(LDADD)
test_cc_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_config_SOURCES = test_config.c
test_config_OBJECTS = test_config.$(OBJEXT)
test_config_LDADD = $(LDADD)
test_config_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_crypto_SOURCES = test_crypto.c
test_crypto_OBJECTS = test_crypto.$(OBJEXT)
test_crypto_LDADD = $(LDADD)
test_crypto_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_crypto_wrapping_SOURCES = test_crypto_wrapping.c
test_crypto_wrapping_OBJECTS = test_crypto_wrapping.$(OBJEXT)
test_crypto_wrapping_LDADD = $(LDADD)
test_crypto_wrapping_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
+test_expand_toks_SOURCES = test_expand_toks.c
+test_expand_toks_OBJECTS = test_expand_toks.$(OBJEXT)
+test_expand_toks_LDADD = $(LDADD)
+test_expand_toks_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_forward_SOURCES = test_forward.c
test_forward_OBJECTS = test_forward.$(OBJEXT)
test_forward_LDADD = $(LDADD)
test_forward_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_fx_SOURCES = test_fx.c
test_fx_OBJECTS = test_fx.$(OBJEXT)
test_fx_LDADD = $(LDADD)
test_fx_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_get_addrs_SOURCES = test_get_addrs.c
test_get_addrs_OBJECTS = test_get_addrs.$(OBJEXT)
test_get_addrs_LDADD = $(LDADD)
test_get_addrs_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_gic_SOURCES = test_gic.c
test_gic_OBJECTS = test_gic.$(OBJEXT)
test_gic_LDADD = $(LDADD)
test_gic_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_hostname_SOURCES = test_hostname.c
test_hostname_OBJECTS = test_hostname.$(OBJEXT)
test_hostname_LDADD = $(LDADD)
test_hostname_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_keytab_SOURCES = test_keytab.c
test_keytab_OBJECTS = test_keytab.$(OBJEXT)
test_keytab_LDADD = $(LDADD)
test_keytab_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_kuserok_SOURCES = test_kuserok.c
test_kuserok_OBJECTS = test_kuserok.$(OBJEXT)
test_kuserok_LDADD = $(LDADD)
test_kuserok_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_mem_SOURCES = test_mem.c
test_mem_OBJECTS = test_mem.$(OBJEXT)
test_mem_LDADD = $(LDADD)
test_mem_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_pac_SOURCES = test_pac.c
test_pac_OBJECTS = test_pac.$(OBJEXT)
test_pac_LDADD = $(LDADD)
test_pac_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_pkinit_dh2key_SOURCES = test_pkinit_dh2key.c
test_pkinit_dh2key_OBJECTS = test_pkinit_dh2key.$(OBJEXT)
test_pkinit_dh2key_LDADD = $(LDADD)
test_pkinit_dh2key_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_pknistkdf_SOURCES = test_pknistkdf.c
test_pknistkdf_OBJECTS = test_pknistkdf.$(OBJEXT)
test_pknistkdf_LDADD = $(LDADD)
test_pknistkdf_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_plugin_SOURCES = test_plugin.c
test_plugin_OBJECTS = test_plugin.$(OBJEXT)
test_plugin_LDADD = $(LDADD)
test_plugin_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_prf_SOURCES = test_prf.c
test_prf_OBJECTS = test_prf.$(OBJEXT)
test_prf_LDADD = $(LDADD)
test_prf_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_princ_SOURCES = test_princ.c
test_princ_OBJECTS = test_princ.$(OBJEXT)
test_princ_LDADD = $(LDADD)
test_princ_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_renew_SOURCES = test_renew.c
test_renew_OBJECTS = test_renew.$(OBJEXT)
test_renew_LDADD = $(LDADD)
test_renew_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_rfc3961_SOURCES = test_rfc3961.c
test_rfc3961_OBJECTS = test_rfc3961.$(OBJEXT)
test_rfc3961_DEPENDENCIES = librfc3961.la \
$(top_builddir)/lib/asn1/libasn1.la \
$(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
+test_set_kvno0_SOURCES = test_set_kvno0.c
+test_set_kvno0_OBJECTS = test_set_kvno0.$(OBJEXT)
+test_set_kvno0_LDADD = $(LDADD)
+test_set_kvno0_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
+ $(top_builddir)/lib/asn1/libasn1.la \
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_store_SOURCES = test_store.c
test_store_OBJECTS = test_store.$(OBJEXT)
test_store_LDADD = $(LDADD)
test_store_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_time_SOURCES = test_time.c
test_time_OBJECTS = test_time.$(OBJEXT)
test_time_LDADD = $(LDADD)
test_time_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
test_x500_SOURCES = test_x500.c
test_x500_OBJECTS = test_x500.$(OBJEXT)
test_x500_LDADD = $(LDADD)
test_x500_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
verify_krb5_conf_SOURCES = verify_krb5_conf.c
verify_krb5_conf_OBJECTS = verify_krb5_conf.$(OBJEXT)
verify_krb5_conf_LDADD = $(LDADD)
verify_krb5_conf_DEPENDENCIES = libkrb5.la $(am__DEPENDENCIES_1) \
$(top_builddir)/lib/asn1/libasn1.la \
- $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1)
+ $(top_builddir)/lib/wind/libwind.la $(LIB_heimbase) \
+ $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(dist_libkrb5_la_SOURCES) $(nodist_libkrb5_la_SOURCES) \
$(librfc3961_la_SOURCES) aes-test.c derived-key-test.c \
- krbhst-test.c n-fold-test.c parse-name-test.c store-test.c \
- string-to-key-test.c test_acl.c test_addr.c test_alname.c \
- test_ap-req.c test_cc.c test_config.c test_crypto.c \
- test_crypto_wrapping.c test_forward.c test_fx.c \
- test_get_addrs.c test_gic.c test_hostname.c test_keytab.c \
- test_kuserok.c test_mem.c test_pac.c test_pkinit_dh2key.c \
- test_pknistkdf.c test_plugin.c test_prf.c test_princ.c \
- test_renew.c test_rfc3961.c test_store.c test_time.c \
- test_x500.c verify_krb5_conf.c
+ krbhst-test.c n-fold-test.c parse-name-test.c \
+ pseudo-random-test.c store-test.c string-to-key-test.c \
+ test_acl.c test_addr.c test_alname.c test_ap-req.c \
+ test_canon.c test_cc.c test_config.c test_crypto.c \
+ test_crypto_wrapping.c test_expand_toks.c test_forward.c \
+ test_fx.c test_get_addrs.c test_gic.c test_hostname.c \
+ test_keytab.c test_kuserok.c test_mem.c test_pac.c \
+ test_pkinit_dh2key.c test_pknistkdf.c test_plugin.c test_prf.c \
+ test_princ.c test_renew.c test_rfc3961.c test_set_kvno0.c \
+ test_store.c test_time.c test_x500.c verify_krb5_conf.c
DIST_SOURCES = $(dist_libkrb5_la_SOURCES) $(librfc3961_la_SOURCES) \
aes-test.c derived-key-test.c krbhst-test.c n-fold-test.c \
- parse-name-test.c store-test.c string-to-key-test.c test_acl.c \
- test_addr.c test_alname.c test_ap-req.c test_cc.c \
- test_config.c test_crypto.c test_crypto_wrapping.c \
+ parse-name-test.c pseudo-random-test.c store-test.c \
+ string-to-key-test.c test_acl.c test_addr.c test_alname.c \
+ test_ap-req.c test_canon.c test_cc.c test_config.c \
+ test_crypto.c test_crypto_wrapping.c test_expand_toks.c \
test_forward.c test_fx.c test_get_addrs.c test_gic.c \
test_hostname.c test_keytab.c test_kuserok.c test_mem.c \
test_pac.c test_pkinit_dh2key.c test_pknistkdf.c test_plugin.c \
test_prf.c test_princ.c test_renew.c test_rfc3961.c \
- test_store.c test_time.c test_x500.c verify_krb5_conf.c
+ test_set_kvno0.c test_store.c test_time.c test_x500.c \
+ verify_krb5_conf.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
man3dir = $(mandir)/man3
man5dir = $(mandir)/man5
+man7dir = $(mandir)/man7
man8dir = $(mandir)/man8
MANS = $(man_MANS)
HEADERS = $(dist_include_HEADERS) $(krb5_HEADERS) \
- $(nodist_include_HEADERS)
+ $(nodist_include_HEADERS) $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -517,12 +880,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -532,17 +895,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -561,12 +924,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -575,6 +935,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -591,10 +952,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -602,6 +961,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -616,12 +976,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -638,6 +1001,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -652,13 +1016,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -682,6 +1040,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -717,31 +1077,39 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
-AM_CPPFLAGS = $(INCLUDES_roken) $(INCLUDE_krb4) $(INCLUDE_hcrypto) \
- -I../com_err -I$(srcdir)/../com_err $(INCLUDE_sqlite3) \
- $(INCLUDE_libintl)
+AM_CPPFLAGS = $(INCLUDES_roken) -I../com_err -I$(srcdir)/../com_err \
+ $(INCLUDE_sqlite3) $(INCLUDE_libintl) \
+ $(INCLUDE_openssl_crypto)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
noinst_LTLIBRARIES = \
librfc3961.la
@@ -750,7 +1118,7 @@ LDADD = libkrb5.la \
$(LIB_hcrypto) \
$(top_builddir)/lib/asn1/libasn1.la \
$(top_builddir)/lib/wind/libwind.la \
- $(LIB_roken)
+ $(LIB_heimbase) $(LIB_roken)
@PKINIT_TRUE@LIB_pkinit = ../hx509/libhx509.la
@have_scc_TRUE@use_sqlite = $(LIB_sqlite3)
@@ -758,8 +1126,9 @@ libkrb5_la_LIBADD = \
$(top_builddir)/lib/asn1/libasn1.la \
$(top_builddir)/lib/ipc/libheim-ipcc.la \
$(top_builddir)/lib/wind/libwind.la \
- $(top_builddir)/base/libheimbase.la \
+ $(top_builddir)/lib/base/libheimbase.la \
$(LIB_pkinit) \
+ $(LIB_openssl_crypto) \
$(use_sqlite) \
$(LIB_com_err) \
$(LIB_hcrypto) \
@@ -800,6 +1169,7 @@ dist_libkrb5_la_SOURCES = \
acl.c \
add_et_list.c \
addr_families.c \
+ an2ln_plugin.h \
aname_to_localname.c \
appdefault.c \
asn1_glue.c \
@@ -818,7 +1188,8 @@ dist_libkrb5_la_SOURCES = \
creds.c \
crypto.c \
crypto.h \
- crypto-aes.c \
+ crypto-aes-sha1.c \
+ crypto-aes-sha2.c \
crypto-algs.c \
crypto-arcfour.c \
crypto-des.c \
@@ -830,12 +1201,17 @@ dist_libkrb5_la_SOURCES = \
crypto-rand.c \
doxygen.c \
data.c \
+ db_plugin.c \
+ db_plugin.h \
+ dcache.c \
deprecated.c \
digest.c \
eai_to_heim_errno.c \
+ enomem.c \
error_string.c \
expand_hostname.c \
expand_path.c \
+ fast.c \
fcache.c \
free.c \
free_host_realm.c \
@@ -863,6 +1239,7 @@ dist_libkrb5_la_SOURCES = \
krb5-v4compat.h \
krbhst.c \
kuserok.c \
+ kuserok_plugin.h \
log.c \
mcache.c \
misc.c \
@@ -880,6 +1257,7 @@ dist_libkrb5_la_SOURCES = \
padata.c \
pcache.c \
pkinit.c \
+ pkinit-ec.c \
principal.c \
prog_setup.c \
prompter_posix.c \
@@ -893,10 +1271,12 @@ dist_libkrb5_la_SOURCES = \
recvauth.c \
replay.c \
salt.c \
- salt-aes.c \
+ salt-aes-sha1.c \
+ salt-aes-sha2.c \
salt-arcfour.c \
salt-des.c \
salt-des3.c \
+ sp800-108-kdf.c \
scache.c \
send_to_kdc.c \
sendauth.c \
@@ -908,6 +1288,7 @@ dist_libkrb5_la_SOURCES = \
store_emem.c \
store_fd.c \
store_mem.c \
+ store_sock.c \
plugin.c \
ticket.c \
time.c \
@@ -925,11 +1306,19 @@ libkrb5_la_DEPENDENCIES = \
version-script.map
libkrb5_la_LDFLAGS = -version-info 26:0:0 $(am__append_1)
+ALL_OBJECTS = $(libkrb5_la_OBJECTS) $(verify_krb5_conf_OBJECTS) \
+ $(librfc3961_la_OBJECTS) $(librfc3961_la_OBJECTS) \
+ $(krbhst_test_OBJECTS) $(test_alname_OBJECTS) \
+ $(test_crypto_OBJECTS) $(test_forward_OBJECTS) \
+ $(test_get_addrs_OBJECTS) $(test_gic_OBJECTS) \
+ $(test_kuserok_OBJECTS) $(test_renew_OBJECTS) \
+ $(test_rfc3961_OBJECTS)
librfc3961_la_SOURCES = \
crc.c \
crypto.c \
crypto.h \
- crypto-aes.c \
+ crypto-aes-sha1.c \
+ crypto-aes-sha2.c \
crypto-algs.c \
crypto-arcfour.c \
crypto-des.c \
@@ -941,14 +1330,17 @@ librfc3961_la_SOURCES = \
crypto-rand.c \
crypto-stubs.c \
data.c \
+ enomem.c \
error_string.c \
keyblock.c \
n-fold.c \
salt.c \
- salt-aes.c \
+ salt-aes-sha1.c \
+ salt-aes-sha2.c \
salt-arcfour.c \
salt-des.c \
salt-des3.c \
+ sp800-108-kdf.c \
store-int.c \
warn.c
@@ -959,9 +1351,11 @@ test_rfc3961_LDADD = \
$(LIB_hcrypto) \
$(LIB_roken)
+@DEVELOPER_MODE_TRUE@headerdeps = $(dist_libkrb5_la_SOURCES)
man_MANS = \
kerberos.8 \
krb5.conf.5 \
+ krb5-plugin.7 \
krb524_convert_creds_kdc.3 \
krb5_425_conv_principal.3 \
krb5_acl_match_file.3 \
@@ -1006,15 +1400,15 @@ man_MANS = \
dist_include_HEADERS = \
krb5.h \
- krb5-protos.h \
- krb5-private.h \
+ $(srcdir)/krb5-protos.h \
krb5_ccapi.h
+noinst_HEADERS = $(srcdir)/krb5-private.h
nodist_include_HEADERS = krb5_err.h heim_err.h k524_err.h
# XXX use nobase_include_HEADERS = krb5/locate_plugin.h
krb5dir = $(includedir)/krb5
-krb5_HEADERS = locate_plugin.h send_to_kdc_plugin.h ccache_plugin.h
+krb5_HEADERS = locate_plugin.h send_to_kdc_plugin.h ccache_plugin.h an2ln_plugin.h db_plugin.h
build_HEADERZ = \
$(krb5_HEADERS) \
krb_err.h
@@ -1029,6 +1423,9 @@ CLEANFILES = \
EXTRA_DIST = \
NTMakefile \
+ config_reg.c \
+ dll.c \
+ libkrb5-exports.def.in \
verify_krb5_conf-version.rc \
krb5_err.et \
krb_err.et \
@@ -1042,7 +1439,7 @@ EXTRA_DIST = \
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -1055,7 +1452,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/krb5/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/krb5/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -1064,6 +1460,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -1073,9 +1470,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -1083,6 +1480,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -1098,35 +1497,46 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
- @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libkrb5.la: $(libkrb5_la_OBJECTS) $(libkrb5_la_DEPENDENCIES)
- $(libkrb5_la_LINK) -rpath $(libdir) $(libkrb5_la_OBJECTS) $(libkrb5_la_LIBADD) $(LIBS)
-librfc3961.la: $(librfc3961_la_OBJECTS) $(librfc3961_la_DEPENDENCIES)
- $(LINK) $(librfc3961_la_OBJECTS) $(librfc3961_la_LIBADD) $(LIBS)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libkrb5.la: $(libkrb5_la_OBJECTS) $(libkrb5_la_DEPENDENCIES) $(EXTRA_libkrb5_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libkrb5_la_LINK) -rpath $(libdir) $(libkrb5_la_OBJECTS) $(libkrb5_la_LIBADD) $(LIBS)
+
+librfc3961.la: $(librfc3961_la_OBJECTS) $(librfc3961_la_DEPENDENCIES) $(EXTRA_librfc3961_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(librfc3961_la_OBJECTS) $(librfc3961_la_LIBADD) $(LIBS)
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -1147,7 +1557,8 @@ uninstall-binPROGRAMS:
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
@@ -1178,111 +1589,162 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-aes-test$(EXEEXT): $(aes_test_OBJECTS) $(aes_test_DEPENDENCIES)
+
+aes-test$(EXEEXT): $(aes_test_OBJECTS) $(aes_test_DEPENDENCIES) $(EXTRA_aes_test_DEPENDENCIES)
@rm -f aes-test$(EXEEXT)
- $(LINK) $(aes_test_OBJECTS) $(aes_test_LDADD) $(LIBS)
-derived-key-test$(EXEEXT): $(derived_key_test_OBJECTS) $(derived_key_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(aes_test_OBJECTS) $(aes_test_LDADD) $(LIBS)
+
+derived-key-test$(EXEEXT): $(derived_key_test_OBJECTS) $(derived_key_test_DEPENDENCIES) $(EXTRA_derived_key_test_DEPENDENCIES)
@rm -f derived-key-test$(EXEEXT)
- $(LINK) $(derived_key_test_OBJECTS) $(derived_key_test_LDADD) $(LIBS)
-krbhst-test$(EXEEXT): $(krbhst_test_OBJECTS) $(krbhst_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(derived_key_test_OBJECTS) $(derived_key_test_LDADD) $(LIBS)
+
+krbhst-test$(EXEEXT): $(krbhst_test_OBJECTS) $(krbhst_test_DEPENDENCIES) $(EXTRA_krbhst_test_DEPENDENCIES)
@rm -f krbhst-test$(EXEEXT)
- $(LINK) $(krbhst_test_OBJECTS) $(krbhst_test_LDADD) $(LIBS)
-n-fold-test$(EXEEXT): $(n_fold_test_OBJECTS) $(n_fold_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(krbhst_test_OBJECTS) $(krbhst_test_LDADD) $(LIBS)
+
+n-fold-test$(EXEEXT): $(n_fold_test_OBJECTS) $(n_fold_test_DEPENDENCIES) $(EXTRA_n_fold_test_DEPENDENCIES)
@rm -f n-fold-test$(EXEEXT)
- $(LINK) $(n_fold_test_OBJECTS) $(n_fold_test_LDADD) $(LIBS)
-parse-name-test$(EXEEXT): $(parse_name_test_OBJECTS) $(parse_name_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(n_fold_test_OBJECTS) $(n_fold_test_LDADD) $(LIBS)
+
+parse-name-test$(EXEEXT): $(parse_name_test_OBJECTS) $(parse_name_test_DEPENDENCIES) $(EXTRA_parse_name_test_DEPENDENCIES)
@rm -f parse-name-test$(EXEEXT)
- $(LINK) $(parse_name_test_OBJECTS) $(parse_name_test_LDADD) $(LIBS)
-store-test$(EXEEXT): $(store_test_OBJECTS) $(store_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(parse_name_test_OBJECTS) $(parse_name_test_LDADD) $(LIBS)
+
+pseudo-random-test$(EXEEXT): $(pseudo_random_test_OBJECTS) $(pseudo_random_test_DEPENDENCIES) $(EXTRA_pseudo_random_test_DEPENDENCIES)
+ @rm -f pseudo-random-test$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(pseudo_random_test_OBJECTS) $(pseudo_random_test_LDADD) $(LIBS)
+
+store-test$(EXEEXT): $(store_test_OBJECTS) $(store_test_DEPENDENCIES) $(EXTRA_store_test_DEPENDENCIES)
@rm -f store-test$(EXEEXT)
- $(LINK) $(store_test_OBJECTS) $(store_test_LDADD) $(LIBS)
-string-to-key-test$(EXEEXT): $(string_to_key_test_OBJECTS) $(string_to_key_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(store_test_OBJECTS) $(store_test_LDADD) $(LIBS)
+
+string-to-key-test$(EXEEXT): $(string_to_key_test_OBJECTS) $(string_to_key_test_DEPENDENCIES) $(EXTRA_string_to_key_test_DEPENDENCIES)
@rm -f string-to-key-test$(EXEEXT)
- $(LINK) $(string_to_key_test_OBJECTS) $(string_to_key_test_LDADD) $(LIBS)
-test_acl$(EXEEXT): $(test_acl_OBJECTS) $(test_acl_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(string_to_key_test_OBJECTS) $(string_to_key_test_LDADD) $(LIBS)
+
+test_acl$(EXEEXT): $(test_acl_OBJECTS) $(test_acl_DEPENDENCIES) $(EXTRA_test_acl_DEPENDENCIES)
@rm -f test_acl$(EXEEXT)
- $(LINK) $(test_acl_OBJECTS) $(test_acl_LDADD) $(LIBS)
-test_addr$(EXEEXT): $(test_addr_OBJECTS) $(test_addr_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_acl_OBJECTS) $(test_acl_LDADD) $(LIBS)
+
+test_addr$(EXEEXT): $(test_addr_OBJECTS) $(test_addr_DEPENDENCIES) $(EXTRA_test_addr_DEPENDENCIES)
@rm -f test_addr$(EXEEXT)
- $(LINK) $(test_addr_OBJECTS) $(test_addr_LDADD) $(LIBS)
-test_alname$(EXEEXT): $(test_alname_OBJECTS) $(test_alname_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_addr_OBJECTS) $(test_addr_LDADD) $(LIBS)
+
+test_alname$(EXEEXT): $(test_alname_OBJECTS) $(test_alname_DEPENDENCIES) $(EXTRA_test_alname_DEPENDENCIES)
@rm -f test_alname$(EXEEXT)
- $(LINK) $(test_alname_OBJECTS) $(test_alname_LDADD) $(LIBS)
-test_ap-req$(EXEEXT): $(test_ap_req_OBJECTS) $(test_ap_req_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_alname_OBJECTS) $(test_alname_LDADD) $(LIBS)
+
+test_ap-req$(EXEEXT): $(test_ap_req_OBJECTS) $(test_ap_req_DEPENDENCIES) $(EXTRA_test_ap_req_DEPENDENCIES)
@rm -f test_ap-req$(EXEEXT)
- $(LINK) $(test_ap_req_OBJECTS) $(test_ap_req_LDADD) $(LIBS)
-test_cc$(EXEEXT): $(test_cc_OBJECTS) $(test_cc_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_ap_req_OBJECTS) $(test_ap_req_LDADD) $(LIBS)
+
+test_canon$(EXEEXT): $(test_canon_OBJECTS) $(test_canon_DEPENDENCIES) $(EXTRA_test_canon_DEPENDENCIES)
+ @rm -f test_canon$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_canon_OBJECTS) $(test_canon_LDADD) $(LIBS)
+
+test_cc$(EXEEXT): $(test_cc_OBJECTS) $(test_cc_DEPENDENCIES) $(EXTRA_test_cc_DEPENDENCIES)
@rm -f test_cc$(EXEEXT)
- $(LINK) $(test_cc_OBJECTS) $(test_cc_LDADD) $(LIBS)
-test_config$(EXEEXT): $(test_config_OBJECTS) $(test_config_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_cc_OBJECTS) $(test_cc_LDADD) $(LIBS)
+
+test_config$(EXEEXT): $(test_config_OBJECTS) $(test_config_DEPENDENCIES) $(EXTRA_test_config_DEPENDENCIES)
@rm -f test_config$(EXEEXT)
- $(LINK) $(test_config_OBJECTS) $(test_config_LDADD) $(LIBS)
-test_crypto$(EXEEXT): $(test_crypto_OBJECTS) $(test_crypto_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_config_OBJECTS) $(test_config_LDADD) $(LIBS)
+
+test_crypto$(EXEEXT): $(test_crypto_OBJECTS) $(test_crypto_DEPENDENCIES) $(EXTRA_test_crypto_DEPENDENCIES)
@rm -f test_crypto$(EXEEXT)
- $(LINK) $(test_crypto_OBJECTS) $(test_crypto_LDADD) $(LIBS)
-test_crypto_wrapping$(EXEEXT): $(test_crypto_wrapping_OBJECTS) $(test_crypto_wrapping_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_OBJECTS) $(test_crypto_LDADD) $(LIBS)
+
+test_crypto_wrapping$(EXEEXT): $(test_crypto_wrapping_OBJECTS) $(test_crypto_wrapping_DEPENDENCIES) $(EXTRA_test_crypto_wrapping_DEPENDENCIES)
@rm -f test_crypto_wrapping$(EXEEXT)
- $(LINK) $(test_crypto_wrapping_OBJECTS) $(test_crypto_wrapping_LDADD) $(LIBS)
-test_forward$(EXEEXT): $(test_forward_OBJECTS) $(test_forward_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_wrapping_OBJECTS) $(test_crypto_wrapping_LDADD) $(LIBS)
+
+test_expand_toks$(EXEEXT): $(test_expand_toks_OBJECTS) $(test_expand_toks_DEPENDENCIES) $(EXTRA_test_expand_toks_DEPENDENCIES)
+ @rm -f test_expand_toks$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_expand_toks_OBJECTS) $(test_expand_toks_LDADD) $(LIBS)
+
+test_forward$(EXEEXT): $(test_forward_OBJECTS) $(test_forward_DEPENDENCIES) $(EXTRA_test_forward_DEPENDENCIES)
@rm -f test_forward$(EXEEXT)
- $(LINK) $(test_forward_OBJECTS) $(test_forward_LDADD) $(LIBS)
-test_fx$(EXEEXT): $(test_fx_OBJECTS) $(test_fx_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_forward_OBJECTS) $(test_forward_LDADD) $(LIBS)
+
+test_fx$(EXEEXT): $(test_fx_OBJECTS) $(test_fx_DEPENDENCIES) $(EXTRA_test_fx_DEPENDENCIES)
@rm -f test_fx$(EXEEXT)
- $(LINK) $(test_fx_OBJECTS) $(test_fx_LDADD) $(LIBS)
-test_get_addrs$(EXEEXT): $(test_get_addrs_OBJECTS) $(test_get_addrs_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_fx_OBJECTS) $(test_fx_LDADD) $(LIBS)
+
+test_get_addrs$(EXEEXT): $(test_get_addrs_OBJECTS) $(test_get_addrs_DEPENDENCIES) $(EXTRA_test_get_addrs_DEPENDENCIES)
@rm -f test_get_addrs$(EXEEXT)
- $(LINK) $(test_get_addrs_OBJECTS) $(test_get_addrs_LDADD) $(LIBS)
-test_gic$(EXEEXT): $(test_gic_OBJECTS) $(test_gic_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_get_addrs_OBJECTS) $(test_get_addrs_LDADD) $(LIBS)
+
+test_gic$(EXEEXT): $(test_gic_OBJECTS) $(test_gic_DEPENDENCIES) $(EXTRA_test_gic_DEPENDENCIES)
@rm -f test_gic$(EXEEXT)
- $(LINK) $(test_gic_OBJECTS) $(test_gic_LDADD) $(LIBS)
-test_hostname$(EXEEXT): $(test_hostname_OBJECTS) $(test_hostname_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_gic_OBJECTS) $(test_gic_LDADD) $(LIBS)
+
+test_hostname$(EXEEXT): $(test_hostname_OBJECTS) $(test_hostname_DEPENDENCIES) $(EXTRA_test_hostname_DEPENDENCIES)
@rm -f test_hostname$(EXEEXT)
- $(LINK) $(test_hostname_OBJECTS) $(test_hostname_LDADD) $(LIBS)
-test_keytab$(EXEEXT): $(test_keytab_OBJECTS) $(test_keytab_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_hostname_OBJECTS) $(test_hostname_LDADD) $(LIBS)
+
+test_keytab$(EXEEXT): $(test_keytab_OBJECTS) $(test_keytab_DEPENDENCIES) $(EXTRA_test_keytab_DEPENDENCIES)
@rm -f test_keytab$(EXEEXT)
- $(LINK) $(test_keytab_OBJECTS) $(test_keytab_LDADD) $(LIBS)
-test_kuserok$(EXEEXT): $(test_kuserok_OBJECTS) $(test_kuserok_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_keytab_OBJECTS) $(test_keytab_LDADD) $(LIBS)
+
+test_kuserok$(EXEEXT): $(test_kuserok_OBJECTS) $(test_kuserok_DEPENDENCIES) $(EXTRA_test_kuserok_DEPENDENCIES)
@rm -f test_kuserok$(EXEEXT)
- $(LINK) $(test_kuserok_OBJECTS) $(test_kuserok_LDADD) $(LIBS)
-test_mem$(EXEEXT): $(test_mem_OBJECTS) $(test_mem_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_kuserok_OBJECTS) $(test_kuserok_LDADD) $(LIBS)
+
+test_mem$(EXEEXT): $(test_mem_OBJECTS) $(test_mem_DEPENDENCIES) $(EXTRA_test_mem_DEPENDENCIES)
@rm -f test_mem$(EXEEXT)
- $(LINK) $(test_mem_OBJECTS) $(test_mem_LDADD) $(LIBS)
-test_pac$(EXEEXT): $(test_pac_OBJECTS) $(test_pac_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_mem_OBJECTS) $(test_mem_LDADD) $(LIBS)
+
+test_pac$(EXEEXT): $(test_pac_OBJECTS) $(test_pac_DEPENDENCIES) $(EXTRA_test_pac_DEPENDENCIES)
@rm -f test_pac$(EXEEXT)
- $(LINK) $(test_pac_OBJECTS) $(test_pac_LDADD) $(LIBS)
-test_pkinit_dh2key$(EXEEXT): $(test_pkinit_dh2key_OBJECTS) $(test_pkinit_dh2key_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_pac_OBJECTS) $(test_pac_LDADD) $(LIBS)
+
+test_pkinit_dh2key$(EXEEXT): $(test_pkinit_dh2key_OBJECTS) $(test_pkinit_dh2key_DEPENDENCIES) $(EXTRA_test_pkinit_dh2key_DEPENDENCIES)
@rm -f test_pkinit_dh2key$(EXEEXT)
- $(LINK) $(test_pkinit_dh2key_OBJECTS) $(test_pkinit_dh2key_LDADD) $(LIBS)
-test_pknistkdf$(EXEEXT): $(test_pknistkdf_OBJECTS) $(test_pknistkdf_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_pkinit_dh2key_OBJECTS) $(test_pkinit_dh2key_LDADD) $(LIBS)
+
+test_pknistkdf$(EXEEXT): $(test_pknistkdf_OBJECTS) $(test_pknistkdf_DEPENDENCIES) $(EXTRA_test_pknistkdf_DEPENDENCIES)
@rm -f test_pknistkdf$(EXEEXT)
- $(LINK) $(test_pknistkdf_OBJECTS) $(test_pknistkdf_LDADD) $(LIBS)
-test_plugin$(EXEEXT): $(test_plugin_OBJECTS) $(test_plugin_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_pknistkdf_OBJECTS) $(test_pknistkdf_LDADD) $(LIBS)
+
+test_plugin$(EXEEXT): $(test_plugin_OBJECTS) $(test_plugin_DEPENDENCIES) $(EXTRA_test_plugin_DEPENDENCIES)
@rm -f test_plugin$(EXEEXT)
- $(LINK) $(test_plugin_OBJECTS) $(test_plugin_LDADD) $(LIBS)
-test_prf$(EXEEXT): $(test_prf_OBJECTS) $(test_prf_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_plugin_OBJECTS) $(test_plugin_LDADD) $(LIBS)
+
+test_prf$(EXEEXT): $(test_prf_OBJECTS) $(test_prf_DEPENDENCIES) $(EXTRA_test_prf_DEPENDENCIES)
@rm -f test_prf$(EXEEXT)
- $(LINK) $(test_prf_OBJECTS) $(test_prf_LDADD) $(LIBS)
-test_princ$(EXEEXT): $(test_princ_OBJECTS) $(test_princ_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_prf_OBJECTS) $(test_prf_LDADD) $(LIBS)
+
+test_princ$(EXEEXT): $(test_princ_OBJECTS) $(test_princ_DEPENDENCIES) $(EXTRA_test_princ_DEPENDENCIES)
@rm -f test_princ$(EXEEXT)
- $(LINK) $(test_princ_OBJECTS) $(test_princ_LDADD) $(LIBS)
-test_renew$(EXEEXT): $(test_renew_OBJECTS) $(test_renew_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_princ_OBJECTS) $(test_princ_LDADD) $(LIBS)
+
+test_renew$(EXEEXT): $(test_renew_OBJECTS) $(test_renew_DEPENDENCIES) $(EXTRA_test_renew_DEPENDENCIES)
@rm -f test_renew$(EXEEXT)
- $(LINK) $(test_renew_OBJECTS) $(test_renew_LDADD) $(LIBS)
-test_rfc3961$(EXEEXT): $(test_rfc3961_OBJECTS) $(test_rfc3961_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_renew_OBJECTS) $(test_renew_LDADD) $(LIBS)
+
+test_rfc3961$(EXEEXT): $(test_rfc3961_OBJECTS) $(test_rfc3961_DEPENDENCIES) $(EXTRA_test_rfc3961_DEPENDENCIES)
@rm -f test_rfc3961$(EXEEXT)
- $(LINK) $(test_rfc3961_OBJECTS) $(test_rfc3961_LDADD) $(LIBS)
-test_store$(EXEEXT): $(test_store_OBJECTS) $(test_store_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_rfc3961_OBJECTS) $(test_rfc3961_LDADD) $(LIBS)
+
+test_set_kvno0$(EXEEXT): $(test_set_kvno0_OBJECTS) $(test_set_kvno0_DEPENDENCIES) $(EXTRA_test_set_kvno0_DEPENDENCIES)
+ @rm -f test_set_kvno0$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_set_kvno0_OBJECTS) $(test_set_kvno0_LDADD) $(LIBS)
+
+test_store$(EXEEXT): $(test_store_OBJECTS) $(test_store_DEPENDENCIES) $(EXTRA_test_store_DEPENDENCIES)
@rm -f test_store$(EXEEXT)
- $(LINK) $(test_store_OBJECTS) $(test_store_LDADD) $(LIBS)
-test_time$(EXEEXT): $(test_time_OBJECTS) $(test_time_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_store_OBJECTS) $(test_store_LDADD) $(LIBS)
+
+test_time$(EXEEXT): $(test_time_OBJECTS) $(test_time_DEPENDENCIES) $(EXTRA_test_time_DEPENDENCIES)
@rm -f test_time$(EXEEXT)
- $(LINK) $(test_time_OBJECTS) $(test_time_LDADD) $(LIBS)
-test_x500$(EXEEXT): $(test_x500_OBJECTS) $(test_x500_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_time_OBJECTS) $(test_time_LDADD) $(LIBS)
+
+test_x500$(EXEEXT): $(test_x500_OBJECTS) $(test_x500_DEPENDENCIES) $(EXTRA_test_x500_DEPENDENCIES)
@rm -f test_x500$(EXEEXT)
- $(LINK) $(test_x500_OBJECTS) $(test_x500_LDADD) $(LIBS)
-verify_krb5_conf$(EXEEXT): $(verify_krb5_conf_OBJECTS) $(verify_krb5_conf_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_x500_OBJECTS) $(test_x500_LDADD) $(LIBS)
+
+verify_krb5_conf$(EXEEXT): $(verify_krb5_conf_OBJECTS) $(verify_krb5_conf_DEPENDENCIES) $(EXTRA_verify_krb5_conf_DEPENDENCIES)
@rm -f verify_krb5_conf$(EXEEXT)
- $(LINK) $(verify_krb5_conf_OBJECTS) $(verify_krb5_conf_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(verify_krb5_conf_OBJECTS) $(verify_krb5_conf_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -1313,7 +1775,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-copy_host_realm.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crc.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-creds.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto-aes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto-aes-sha1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto-aes-sha2.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto-algs.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto-arcfour.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto-des-common.Plo@am__quote@
@@ -1325,13 +1788,17 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto-rand.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-crypto.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-db_plugin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-dcache.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-deprecated.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-digest.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-doxygen.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-eai_to_heim_errno.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-enomem.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-error_string.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-expand_hostname.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-expand_path.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-fast.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-fcache.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-free.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-free_host_realm.Plo@am__quote@
@@ -1376,6 +1843,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-pac.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-padata.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-pcache.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-pkinit-ec.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-pkinit.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-plugin.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-principal.Plo@am__quote@
@@ -1390,7 +1858,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-read_message.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-recvauth.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-replay.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-salt-aes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-salt-aes-sha1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-salt-aes-sha2.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-salt-arcfour.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-salt-des.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-salt-des3.Plo@am__quote@
@@ -1400,11 +1869,13 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-sendauth.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-set_default_realm.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-sock_principal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-sp800-108-kdf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-store-int.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-store.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-store_emem.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-store_fd.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-store_mem.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-store_sock.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-ticket.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-time.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-transited.Plo@am__quote@
@@ -1414,7 +1885,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-warn.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libkrb5_la-write_message.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crc.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto-aes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto-aes-sha1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto-aes-sha2.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto-algs.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto-arcfour.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto-des-common.Plo@am__quote@
@@ -1427,28 +1899,34 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto-stubs.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-crypto.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-enomem.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-error_string.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-keyblock.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-n-fold.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-salt-aes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-salt-aes-sha1.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-salt-aes-sha2.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-salt-arcfour.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-salt-des.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-salt-des3.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-salt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-sp800-108-kdf.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-store-int.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/librfc3961_la-warn.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/n-fold-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse-name-test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pseudo-random-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/store-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/string-to-key-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_acl.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_addr.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_alname.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_ap-req.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_canon.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_cc.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_config.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_wrapping.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_expand_toks.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_forward.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_fx.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_get_addrs.Po@am__quote@
@@ -1465,1039 +1943,1131 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_princ.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_renew.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_rfc3961.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_set_kvno0.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_store.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_time.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_x500.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify_krb5_conf.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libkrb5_la-acache.lo: acache.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-acache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-acache.Tpo -c -o libkrb5_la-acache.lo `test -f 'acache.c' || echo '$(srcdir)/'`acache.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-acache.Tpo $(DEPDIR)/libkrb5_la-acache.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='acache.c' object='libkrb5_la-acache.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-acache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-acache.Tpo -c -o libkrb5_la-acache.lo `test -f 'acache.c' || echo '$(srcdir)/'`acache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-acache.Tpo $(DEPDIR)/libkrb5_la-acache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acache.c' object='libkrb5_la-acache.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-acache.lo `test -f 'acache.c' || echo '$(srcdir)/'`acache.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-acache.lo `test -f 'acache.c' || echo '$(srcdir)/'`acache.c
libkrb5_la-acl.lo: acl.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-acl.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-acl.Tpo -c -o libkrb5_la-acl.lo `test -f 'acl.c' || echo '$(srcdir)/'`acl.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-acl.Tpo $(DEPDIR)/libkrb5_la-acl.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='acl.c' object='libkrb5_la-acl.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-acl.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-acl.Tpo -c -o libkrb5_la-acl.lo `test -f 'acl.c' || echo '$(srcdir)/'`acl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-acl.Tpo $(DEPDIR)/libkrb5_la-acl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='acl.c' object='libkrb5_la-acl.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-acl.lo `test -f 'acl.c' || echo '$(srcdir)/'`acl.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-acl.lo `test -f 'acl.c' || echo '$(srcdir)/'`acl.c
libkrb5_la-add_et_list.lo: add_et_list.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-add_et_list.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-add_et_list.Tpo -c -o libkrb5_la-add_et_list.lo `test -f 'add_et_list.c' || echo '$(srcdir)/'`add_et_list.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-add_et_list.Tpo $(DEPDIR)/libkrb5_la-add_et_list.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='add_et_list.c' object='libkrb5_la-add_et_list.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-add_et_list.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-add_et_list.Tpo -c -o libkrb5_la-add_et_list.lo `test -f 'add_et_list.c' || echo '$(srcdir)/'`add_et_list.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-add_et_list.Tpo $(DEPDIR)/libkrb5_la-add_et_list.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='add_et_list.c' object='libkrb5_la-add_et_list.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-add_et_list.lo `test -f 'add_et_list.c' || echo '$(srcdir)/'`add_et_list.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-add_et_list.lo `test -f 'add_et_list.c' || echo '$(srcdir)/'`add_et_list.c
libkrb5_la-addr_families.lo: addr_families.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-addr_families.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-addr_families.Tpo -c -o libkrb5_la-addr_families.lo `test -f 'addr_families.c' || echo '$(srcdir)/'`addr_families.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-addr_families.Tpo $(DEPDIR)/libkrb5_la-addr_families.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='addr_families.c' object='libkrb5_la-addr_families.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-addr_families.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-addr_families.Tpo -c -o libkrb5_la-addr_families.lo `test -f 'addr_families.c' || echo '$(srcdir)/'`addr_families.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-addr_families.Tpo $(DEPDIR)/libkrb5_la-addr_families.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='addr_families.c' object='libkrb5_la-addr_families.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-addr_families.lo `test -f 'addr_families.c' || echo '$(srcdir)/'`addr_families.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-addr_families.lo `test -f 'addr_families.c' || echo '$(srcdir)/'`addr_families.c
libkrb5_la-aname_to_localname.lo: aname_to_localname.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-aname_to_localname.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-aname_to_localname.Tpo -c -o libkrb5_la-aname_to_localname.lo `test -f 'aname_to_localname.c' || echo '$(srcdir)/'`aname_to_localname.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-aname_to_localname.Tpo $(DEPDIR)/libkrb5_la-aname_to_localname.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='aname_to_localname.c' object='libkrb5_la-aname_to_localname.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-aname_to_localname.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-aname_to_localname.Tpo -c -o libkrb5_la-aname_to_localname.lo `test -f 'aname_to_localname.c' || echo '$(srcdir)/'`aname_to_localname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-aname_to_localname.Tpo $(DEPDIR)/libkrb5_la-aname_to_localname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='aname_to_localname.c' object='libkrb5_la-aname_to_localname.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-aname_to_localname.lo `test -f 'aname_to_localname.c' || echo '$(srcdir)/'`aname_to_localname.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-aname_to_localname.lo `test -f 'aname_to_localname.c' || echo '$(srcdir)/'`aname_to_localname.c
libkrb5_la-appdefault.lo: appdefault.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-appdefault.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-appdefault.Tpo -c -o libkrb5_la-appdefault.lo `test -f 'appdefault.c' || echo '$(srcdir)/'`appdefault.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-appdefault.Tpo $(DEPDIR)/libkrb5_la-appdefault.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='appdefault.c' object='libkrb5_la-appdefault.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-appdefault.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-appdefault.Tpo -c -o libkrb5_la-appdefault.lo `test -f 'appdefault.c' || echo '$(srcdir)/'`appdefault.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-appdefault.Tpo $(DEPDIR)/libkrb5_la-appdefault.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='appdefault.c' object='libkrb5_la-appdefault.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-appdefault.lo `test -f 'appdefault.c' || echo '$(srcdir)/'`appdefault.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-appdefault.lo `test -f 'appdefault.c' || echo '$(srcdir)/'`appdefault.c
libkrb5_la-asn1_glue.lo: asn1_glue.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-asn1_glue.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-asn1_glue.Tpo -c -o libkrb5_la-asn1_glue.lo `test -f 'asn1_glue.c' || echo '$(srcdir)/'`asn1_glue.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-asn1_glue.Tpo $(DEPDIR)/libkrb5_la-asn1_glue.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='asn1_glue.c' object='libkrb5_la-asn1_glue.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-asn1_glue.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-asn1_glue.Tpo -c -o libkrb5_la-asn1_glue.lo `test -f 'asn1_glue.c' || echo '$(srcdir)/'`asn1_glue.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-asn1_glue.Tpo $(DEPDIR)/libkrb5_la-asn1_glue.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asn1_glue.c' object='libkrb5_la-asn1_glue.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-asn1_glue.lo `test -f 'asn1_glue.c' || echo '$(srcdir)/'`asn1_glue.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-asn1_glue.lo `test -f 'asn1_glue.c' || echo '$(srcdir)/'`asn1_glue.c
libkrb5_la-auth_context.lo: auth_context.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-auth_context.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-auth_context.Tpo -c -o libkrb5_la-auth_context.lo `test -f 'auth_context.c' || echo '$(srcdir)/'`auth_context.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-auth_context.Tpo $(DEPDIR)/libkrb5_la-auth_context.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='auth_context.c' object='libkrb5_la-auth_context.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-auth_context.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-auth_context.Tpo -c -o libkrb5_la-auth_context.lo `test -f 'auth_context.c' || echo '$(srcdir)/'`auth_context.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-auth_context.Tpo $(DEPDIR)/libkrb5_la-auth_context.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='auth_context.c' object='libkrb5_la-auth_context.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-auth_context.lo `test -f 'auth_context.c' || echo '$(srcdir)/'`auth_context.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-auth_context.lo `test -f 'auth_context.c' || echo '$(srcdir)/'`auth_context.c
libkrb5_la-build_ap_req.lo: build_ap_req.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-build_ap_req.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-build_ap_req.Tpo -c -o libkrb5_la-build_ap_req.lo `test -f 'build_ap_req.c' || echo '$(srcdir)/'`build_ap_req.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-build_ap_req.Tpo $(DEPDIR)/libkrb5_la-build_ap_req.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='build_ap_req.c' object='libkrb5_la-build_ap_req.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-build_ap_req.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-build_ap_req.Tpo -c -o libkrb5_la-build_ap_req.lo `test -f 'build_ap_req.c' || echo '$(srcdir)/'`build_ap_req.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-build_ap_req.Tpo $(DEPDIR)/libkrb5_la-build_ap_req.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='build_ap_req.c' object='libkrb5_la-build_ap_req.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-build_ap_req.lo `test -f 'build_ap_req.c' || echo '$(srcdir)/'`build_ap_req.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-build_ap_req.lo `test -f 'build_ap_req.c' || echo '$(srcdir)/'`build_ap_req.c
libkrb5_la-build_auth.lo: build_auth.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-build_auth.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-build_auth.Tpo -c -o libkrb5_la-build_auth.lo `test -f 'build_auth.c' || echo '$(srcdir)/'`build_auth.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-build_auth.Tpo $(DEPDIR)/libkrb5_la-build_auth.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='build_auth.c' object='libkrb5_la-build_auth.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-build_auth.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-build_auth.Tpo -c -o libkrb5_la-build_auth.lo `test -f 'build_auth.c' || echo '$(srcdir)/'`build_auth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-build_auth.Tpo $(DEPDIR)/libkrb5_la-build_auth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='build_auth.c' object='libkrb5_la-build_auth.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-build_auth.lo `test -f 'build_auth.c' || echo '$(srcdir)/'`build_auth.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-build_auth.lo `test -f 'build_auth.c' || echo '$(srcdir)/'`build_auth.c
libkrb5_la-cache.lo: cache.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-cache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-cache.Tpo -c -o libkrb5_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-cache.Tpo $(DEPDIR)/libkrb5_la-cache.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cache.c' object='libkrb5_la-cache.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-cache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-cache.Tpo -c -o libkrb5_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-cache.Tpo $(DEPDIR)/libkrb5_la-cache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cache.c' object='libkrb5_la-cache.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-cache.lo `test -f 'cache.c' || echo '$(srcdir)/'`cache.c
libkrb5_la-changepw.lo: changepw.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-changepw.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-changepw.Tpo -c -o libkrb5_la-changepw.lo `test -f 'changepw.c' || echo '$(srcdir)/'`changepw.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-changepw.Tpo $(DEPDIR)/libkrb5_la-changepw.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='changepw.c' object='libkrb5_la-changepw.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-changepw.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-changepw.Tpo -c -o libkrb5_la-changepw.lo `test -f 'changepw.c' || echo '$(srcdir)/'`changepw.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-changepw.Tpo $(DEPDIR)/libkrb5_la-changepw.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='changepw.c' object='libkrb5_la-changepw.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-changepw.lo `test -f 'changepw.c' || echo '$(srcdir)/'`changepw.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-changepw.lo `test -f 'changepw.c' || echo '$(srcdir)/'`changepw.c
libkrb5_la-codec.lo: codec.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-codec.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-codec.Tpo -c -o libkrb5_la-codec.lo `test -f 'codec.c' || echo '$(srcdir)/'`codec.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-codec.Tpo $(DEPDIR)/libkrb5_la-codec.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='codec.c' object='libkrb5_la-codec.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-codec.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-codec.Tpo -c -o libkrb5_la-codec.lo `test -f 'codec.c' || echo '$(srcdir)/'`codec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-codec.Tpo $(DEPDIR)/libkrb5_la-codec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='codec.c' object='libkrb5_la-codec.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-codec.lo `test -f 'codec.c' || echo '$(srcdir)/'`codec.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-codec.lo `test -f 'codec.c' || echo '$(srcdir)/'`codec.c
libkrb5_la-config_file.lo: config_file.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-config_file.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-config_file.Tpo -c -o libkrb5_la-config_file.lo `test -f 'config_file.c' || echo '$(srcdir)/'`config_file.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-config_file.Tpo $(DEPDIR)/libkrb5_la-config_file.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='config_file.c' object='libkrb5_la-config_file.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-config_file.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-config_file.Tpo -c -o libkrb5_la-config_file.lo `test -f 'config_file.c' || echo '$(srcdir)/'`config_file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-config_file.Tpo $(DEPDIR)/libkrb5_la-config_file.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='config_file.c' object='libkrb5_la-config_file.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-config_file.lo `test -f 'config_file.c' || echo '$(srcdir)/'`config_file.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-config_file.lo `test -f 'config_file.c' || echo '$(srcdir)/'`config_file.c
libkrb5_la-convert_creds.lo: convert_creds.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-convert_creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-convert_creds.Tpo -c -o libkrb5_la-convert_creds.lo `test -f 'convert_creds.c' || echo '$(srcdir)/'`convert_creds.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-convert_creds.Tpo $(DEPDIR)/libkrb5_la-convert_creds.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='convert_creds.c' object='libkrb5_la-convert_creds.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-convert_creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-convert_creds.Tpo -c -o libkrb5_la-convert_creds.lo `test -f 'convert_creds.c' || echo '$(srcdir)/'`convert_creds.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-convert_creds.Tpo $(DEPDIR)/libkrb5_la-convert_creds.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='convert_creds.c' object='libkrb5_la-convert_creds.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-convert_creds.lo `test -f 'convert_creds.c' || echo '$(srcdir)/'`convert_creds.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-convert_creds.lo `test -f 'convert_creds.c' || echo '$(srcdir)/'`convert_creds.c
libkrb5_la-constants.lo: constants.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-constants.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-constants.Tpo -c -o libkrb5_la-constants.lo `test -f 'constants.c' || echo '$(srcdir)/'`constants.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-constants.Tpo $(DEPDIR)/libkrb5_la-constants.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='constants.c' object='libkrb5_la-constants.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-constants.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-constants.Tpo -c -o libkrb5_la-constants.lo `test -f 'constants.c' || echo '$(srcdir)/'`constants.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-constants.Tpo $(DEPDIR)/libkrb5_la-constants.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='constants.c' object='libkrb5_la-constants.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-constants.lo `test -f 'constants.c' || echo '$(srcdir)/'`constants.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-constants.lo `test -f 'constants.c' || echo '$(srcdir)/'`constants.c
libkrb5_la-context.lo: context.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-context.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-context.Tpo -c -o libkrb5_la-context.lo `test -f 'context.c' || echo '$(srcdir)/'`context.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-context.Tpo $(DEPDIR)/libkrb5_la-context.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='context.c' object='libkrb5_la-context.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-context.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-context.Tpo -c -o libkrb5_la-context.lo `test -f 'context.c' || echo '$(srcdir)/'`context.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-context.Tpo $(DEPDIR)/libkrb5_la-context.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='context.c' object='libkrb5_la-context.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-context.lo `test -f 'context.c' || echo '$(srcdir)/'`context.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-context.lo `test -f 'context.c' || echo '$(srcdir)/'`context.c
libkrb5_la-copy_host_realm.lo: copy_host_realm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-copy_host_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-copy_host_realm.Tpo -c -o libkrb5_la-copy_host_realm.lo `test -f 'copy_host_realm.c' || echo '$(srcdir)/'`copy_host_realm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-copy_host_realm.Tpo $(DEPDIR)/libkrb5_la-copy_host_realm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='copy_host_realm.c' object='libkrb5_la-copy_host_realm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-copy_host_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-copy_host_realm.Tpo -c -o libkrb5_la-copy_host_realm.lo `test -f 'copy_host_realm.c' || echo '$(srcdir)/'`copy_host_realm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-copy_host_realm.Tpo $(DEPDIR)/libkrb5_la-copy_host_realm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='copy_host_realm.c' object='libkrb5_la-copy_host_realm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-copy_host_realm.lo `test -f 'copy_host_realm.c' || echo '$(srcdir)/'`copy_host_realm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-copy_host_realm.lo `test -f 'copy_host_realm.c' || echo '$(srcdir)/'`copy_host_realm.c
libkrb5_la-crc.lo: crc.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crc.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crc.Tpo -c -o libkrb5_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crc.Tpo $(DEPDIR)/libkrb5_la-crc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crc.c' object='libkrb5_la-crc.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crc.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crc.Tpo -c -o libkrb5_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crc.Tpo $(DEPDIR)/libkrb5_la-crc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crc.c' object='libkrb5_la-crc.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
libkrb5_la-creds.lo: creds.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-creds.Tpo -c -o libkrb5_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-creds.Tpo $(DEPDIR)/libkrb5_la-creds.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='creds.c' object='libkrb5_la-creds.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-creds.Tpo -c -o libkrb5_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-creds.Tpo $(DEPDIR)/libkrb5_la-creds.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='creds.c' object='libkrb5_la-creds.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-creds.lo `test -f 'creds.c' || echo '$(srcdir)/'`creds.c
libkrb5_la-crypto.lo: crypto.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto.Tpo -c -o libkrb5_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto.Tpo $(DEPDIR)/libkrb5_la-crypto.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto.c' object='libkrb5_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto.Tpo -c -o libkrb5_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto.Tpo $(DEPDIR)/libkrb5_la-crypto.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto.c' object='libkrb5_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
-libkrb5_la-crypto-aes.lo: crypto-aes.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-aes.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-aes.Tpo -c -o libkrb5_la-crypto-aes.lo `test -f 'crypto-aes.c' || echo '$(srcdir)/'`crypto-aes.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-aes.Tpo $(DEPDIR)/libkrb5_la-crypto-aes.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-aes.c' object='libkrb5_la-crypto-aes.lo' libtool=yes @AMDEPBACKSLASH@
+libkrb5_la-crypto-aes-sha1.lo: crypto-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-aes-sha1.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-aes-sha1.Tpo -c -o libkrb5_la-crypto-aes-sha1.lo `test -f 'crypto-aes-sha1.c' || echo '$(srcdir)/'`crypto-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-aes-sha1.Tpo $(DEPDIR)/libkrb5_la-crypto-aes-sha1.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-aes-sha1.c' object='libkrb5_la-crypto-aes-sha1.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-aes.lo `test -f 'crypto-aes.c' || echo '$(srcdir)/'`crypto-aes.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-aes-sha1.lo `test -f 'crypto-aes-sha1.c' || echo '$(srcdir)/'`crypto-aes-sha1.c
+
+libkrb5_la-crypto-aes-sha2.lo: crypto-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-aes-sha2.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-aes-sha2.Tpo -c -o libkrb5_la-crypto-aes-sha2.lo `test -f 'crypto-aes-sha2.c' || echo '$(srcdir)/'`crypto-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-aes-sha2.Tpo $(DEPDIR)/libkrb5_la-crypto-aes-sha2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-aes-sha2.c' object='libkrb5_la-crypto-aes-sha2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-aes-sha2.lo `test -f 'crypto-aes-sha2.c' || echo '$(srcdir)/'`crypto-aes-sha2.c
libkrb5_la-crypto-algs.lo: crypto-algs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-algs.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-algs.Tpo -c -o libkrb5_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-algs.Tpo $(DEPDIR)/libkrb5_la-crypto-algs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-algs.c' object='libkrb5_la-crypto-algs.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-algs.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-algs.Tpo -c -o libkrb5_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-algs.Tpo $(DEPDIR)/libkrb5_la-crypto-algs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-algs.c' object='libkrb5_la-crypto-algs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
libkrb5_la-crypto-arcfour.lo: crypto-arcfour.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-arcfour.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-arcfour.Tpo -c -o libkrb5_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-arcfour.Tpo $(DEPDIR)/libkrb5_la-crypto-arcfour.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-arcfour.c' object='libkrb5_la-crypto-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-arcfour.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-arcfour.Tpo -c -o libkrb5_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-arcfour.Tpo $(DEPDIR)/libkrb5_la-crypto-arcfour.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-arcfour.c' object='libkrb5_la-crypto-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
libkrb5_la-crypto-des.lo: crypto-des.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-des.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-des.Tpo -c -o libkrb5_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-des.Tpo $(DEPDIR)/libkrb5_la-crypto-des.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-des.c' object='libkrb5_la-crypto-des.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-des.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-des.Tpo -c -o libkrb5_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-des.Tpo $(DEPDIR)/libkrb5_la-crypto-des.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-des.c' object='libkrb5_la-crypto-des.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
libkrb5_la-crypto-des-common.lo: crypto-des-common.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-des-common.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-des-common.Tpo -c -o libkrb5_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-des-common.Tpo $(DEPDIR)/libkrb5_la-crypto-des-common.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-des-common.c' object='libkrb5_la-crypto-des-common.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-des-common.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-des-common.Tpo -c -o libkrb5_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-des-common.Tpo $(DEPDIR)/libkrb5_la-crypto-des-common.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-des-common.c' object='libkrb5_la-crypto-des-common.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
libkrb5_la-crypto-des3.lo: crypto-des3.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-des3.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-des3.Tpo -c -o libkrb5_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-des3.Tpo $(DEPDIR)/libkrb5_la-crypto-des3.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-des3.c' object='libkrb5_la-crypto-des3.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-des3.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-des3.Tpo -c -o libkrb5_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-des3.Tpo $(DEPDIR)/libkrb5_la-crypto-des3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-des3.c' object='libkrb5_la-crypto-des3.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
libkrb5_la-crypto-evp.lo: crypto-evp.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-evp.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-evp.Tpo -c -o libkrb5_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-evp.Tpo $(DEPDIR)/libkrb5_la-crypto-evp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-evp.c' object='libkrb5_la-crypto-evp.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-evp.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-evp.Tpo -c -o libkrb5_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-evp.Tpo $(DEPDIR)/libkrb5_la-crypto-evp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-evp.c' object='libkrb5_la-crypto-evp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
libkrb5_la-crypto-null.lo: crypto-null.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-null.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-null.Tpo -c -o libkrb5_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-null.Tpo $(DEPDIR)/libkrb5_la-crypto-null.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-null.c' object='libkrb5_la-crypto-null.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-null.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-null.Tpo -c -o libkrb5_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-null.Tpo $(DEPDIR)/libkrb5_la-crypto-null.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-null.c' object='libkrb5_la-crypto-null.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
libkrb5_la-crypto-pk.lo: crypto-pk.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-pk.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-pk.Tpo -c -o libkrb5_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-pk.Tpo $(DEPDIR)/libkrb5_la-crypto-pk.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-pk.c' object='libkrb5_la-crypto-pk.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-pk.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-pk.Tpo -c -o libkrb5_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-pk.Tpo $(DEPDIR)/libkrb5_la-crypto-pk.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-pk.c' object='libkrb5_la-crypto-pk.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
libkrb5_la-crypto-rand.lo: crypto-rand.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-rand.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-rand.Tpo -c -o libkrb5_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-crypto-rand.Tpo $(DEPDIR)/libkrb5_la-crypto-rand.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-rand.c' object='libkrb5_la-crypto-rand.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-crypto-rand.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-crypto-rand.Tpo -c -o libkrb5_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-crypto-rand.Tpo $(DEPDIR)/libkrb5_la-crypto-rand.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-rand.c' object='libkrb5_la-crypto-rand.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
libkrb5_la-doxygen.lo: doxygen.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-doxygen.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-doxygen.Tpo -c -o libkrb5_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-doxygen.Tpo $(DEPDIR)/libkrb5_la-doxygen.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='doxygen.c' object='libkrb5_la-doxygen.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-doxygen.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-doxygen.Tpo -c -o libkrb5_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-doxygen.Tpo $(DEPDIR)/libkrb5_la-doxygen.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doxygen.c' object='libkrb5_la-doxygen.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
libkrb5_la-data.lo: data.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-data.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-data.Tpo -c -o libkrb5_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-data.Tpo $(DEPDIR)/libkrb5_la-data.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data.c' object='libkrb5_la-data.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-data.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-data.Tpo -c -o libkrb5_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-data.Tpo $(DEPDIR)/libkrb5_la-data.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data.c' object='libkrb5_la-data.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
+
+libkrb5_la-db_plugin.lo: db_plugin.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-db_plugin.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-db_plugin.Tpo -c -o libkrb5_la-db_plugin.lo `test -f 'db_plugin.c' || echo '$(srcdir)/'`db_plugin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-db_plugin.Tpo $(DEPDIR)/libkrb5_la-db_plugin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='db_plugin.c' object='libkrb5_la-db_plugin.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-db_plugin.lo `test -f 'db_plugin.c' || echo '$(srcdir)/'`db_plugin.c
+
+libkrb5_la-dcache.lo: dcache.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-dcache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-dcache.Tpo -c -o libkrb5_la-dcache.lo `test -f 'dcache.c' || echo '$(srcdir)/'`dcache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-dcache.Tpo $(DEPDIR)/libkrb5_la-dcache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcache.c' object='libkrb5_la-dcache.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-dcache.lo `test -f 'dcache.c' || echo '$(srcdir)/'`dcache.c
libkrb5_la-deprecated.lo: deprecated.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-deprecated.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-deprecated.Tpo -c -o libkrb5_la-deprecated.lo `test -f 'deprecated.c' || echo '$(srcdir)/'`deprecated.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-deprecated.Tpo $(DEPDIR)/libkrb5_la-deprecated.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='deprecated.c' object='libkrb5_la-deprecated.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-deprecated.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-deprecated.Tpo -c -o libkrb5_la-deprecated.lo `test -f 'deprecated.c' || echo '$(srcdir)/'`deprecated.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-deprecated.Tpo $(DEPDIR)/libkrb5_la-deprecated.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='deprecated.c' object='libkrb5_la-deprecated.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-deprecated.lo `test -f 'deprecated.c' || echo '$(srcdir)/'`deprecated.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-deprecated.lo `test -f 'deprecated.c' || echo '$(srcdir)/'`deprecated.c
libkrb5_la-digest.lo: digest.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-digest.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-digest.Tpo -c -o libkrb5_la-digest.lo `test -f 'digest.c' || echo '$(srcdir)/'`digest.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-digest.Tpo $(DEPDIR)/libkrb5_la-digest.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='digest.c' object='libkrb5_la-digest.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-digest.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-digest.Tpo -c -o libkrb5_la-digest.lo `test -f 'digest.c' || echo '$(srcdir)/'`digest.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-digest.Tpo $(DEPDIR)/libkrb5_la-digest.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='digest.c' object='libkrb5_la-digest.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-digest.lo `test -f 'digest.c' || echo '$(srcdir)/'`digest.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-digest.lo `test -f 'digest.c' || echo '$(srcdir)/'`digest.c
libkrb5_la-eai_to_heim_errno.lo: eai_to_heim_errno.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-eai_to_heim_errno.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-eai_to_heim_errno.Tpo -c -o libkrb5_la-eai_to_heim_errno.lo `test -f 'eai_to_heim_errno.c' || echo '$(srcdir)/'`eai_to_heim_errno.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-eai_to_heim_errno.Tpo $(DEPDIR)/libkrb5_la-eai_to_heim_errno.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eai_to_heim_errno.c' object='libkrb5_la-eai_to_heim_errno.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-eai_to_heim_errno.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-eai_to_heim_errno.Tpo -c -o libkrb5_la-eai_to_heim_errno.lo `test -f 'eai_to_heim_errno.c' || echo '$(srcdir)/'`eai_to_heim_errno.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-eai_to_heim_errno.Tpo $(DEPDIR)/libkrb5_la-eai_to_heim_errno.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eai_to_heim_errno.c' object='libkrb5_la-eai_to_heim_errno.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-eai_to_heim_errno.lo `test -f 'eai_to_heim_errno.c' || echo '$(srcdir)/'`eai_to_heim_errno.c
+
+libkrb5_la-enomem.lo: enomem.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-enomem.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-enomem.Tpo -c -o libkrb5_la-enomem.lo `test -f 'enomem.c' || echo '$(srcdir)/'`enomem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-enomem.Tpo $(DEPDIR)/libkrb5_la-enomem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='enomem.c' object='libkrb5_la-enomem.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-eai_to_heim_errno.lo `test -f 'eai_to_heim_errno.c' || echo '$(srcdir)/'`eai_to_heim_errno.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-enomem.lo `test -f 'enomem.c' || echo '$(srcdir)/'`enomem.c
libkrb5_la-error_string.lo: error_string.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-error_string.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-error_string.Tpo -c -o libkrb5_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-error_string.Tpo $(DEPDIR)/libkrb5_la-error_string.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error_string.c' object='libkrb5_la-error_string.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-error_string.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-error_string.Tpo -c -o libkrb5_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-error_string.Tpo $(DEPDIR)/libkrb5_la-error_string.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error_string.c' object='libkrb5_la-error_string.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
libkrb5_la-expand_hostname.lo: expand_hostname.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-expand_hostname.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-expand_hostname.Tpo -c -o libkrb5_la-expand_hostname.lo `test -f 'expand_hostname.c' || echo '$(srcdir)/'`expand_hostname.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-expand_hostname.Tpo $(DEPDIR)/libkrb5_la-expand_hostname.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='expand_hostname.c' object='libkrb5_la-expand_hostname.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-expand_hostname.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-expand_hostname.Tpo -c -o libkrb5_la-expand_hostname.lo `test -f 'expand_hostname.c' || echo '$(srcdir)/'`expand_hostname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-expand_hostname.Tpo $(DEPDIR)/libkrb5_la-expand_hostname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='expand_hostname.c' object='libkrb5_la-expand_hostname.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-expand_hostname.lo `test -f 'expand_hostname.c' || echo '$(srcdir)/'`expand_hostname.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-expand_hostname.lo `test -f 'expand_hostname.c' || echo '$(srcdir)/'`expand_hostname.c
libkrb5_la-expand_path.lo: expand_path.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-expand_path.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-expand_path.Tpo -c -o libkrb5_la-expand_path.lo `test -f 'expand_path.c' || echo '$(srcdir)/'`expand_path.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-expand_path.Tpo $(DEPDIR)/libkrb5_la-expand_path.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='expand_path.c' object='libkrb5_la-expand_path.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-expand_path.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-expand_path.Tpo -c -o libkrb5_la-expand_path.lo `test -f 'expand_path.c' || echo '$(srcdir)/'`expand_path.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-expand_path.Tpo $(DEPDIR)/libkrb5_la-expand_path.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='expand_path.c' object='libkrb5_la-expand_path.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-expand_path.lo `test -f 'expand_path.c' || echo '$(srcdir)/'`expand_path.c
+
+libkrb5_la-fast.lo: fast.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-fast.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-fast.Tpo -c -o libkrb5_la-fast.lo `test -f 'fast.c' || echo '$(srcdir)/'`fast.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-fast.Tpo $(DEPDIR)/libkrb5_la-fast.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fast.c' object='libkrb5_la-fast.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-expand_path.lo `test -f 'expand_path.c' || echo '$(srcdir)/'`expand_path.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-fast.lo `test -f 'fast.c' || echo '$(srcdir)/'`fast.c
libkrb5_la-fcache.lo: fcache.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-fcache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-fcache.Tpo -c -o libkrb5_la-fcache.lo `test -f 'fcache.c' || echo '$(srcdir)/'`fcache.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-fcache.Tpo $(DEPDIR)/libkrb5_la-fcache.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='fcache.c' object='libkrb5_la-fcache.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-fcache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-fcache.Tpo -c -o libkrb5_la-fcache.lo `test -f 'fcache.c' || echo '$(srcdir)/'`fcache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-fcache.Tpo $(DEPDIR)/libkrb5_la-fcache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fcache.c' object='libkrb5_la-fcache.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-fcache.lo `test -f 'fcache.c' || echo '$(srcdir)/'`fcache.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-fcache.lo `test -f 'fcache.c' || echo '$(srcdir)/'`fcache.c
libkrb5_la-free.lo: free.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-free.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-free.Tpo -c -o libkrb5_la-free.lo `test -f 'free.c' || echo '$(srcdir)/'`free.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-free.Tpo $(DEPDIR)/libkrb5_la-free.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='free.c' object='libkrb5_la-free.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-free.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-free.Tpo -c -o libkrb5_la-free.lo `test -f 'free.c' || echo '$(srcdir)/'`free.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-free.Tpo $(DEPDIR)/libkrb5_la-free.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='free.c' object='libkrb5_la-free.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-free.lo `test -f 'free.c' || echo '$(srcdir)/'`free.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-free.lo `test -f 'free.c' || echo '$(srcdir)/'`free.c
libkrb5_la-free_host_realm.lo: free_host_realm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-free_host_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-free_host_realm.Tpo -c -o libkrb5_la-free_host_realm.lo `test -f 'free_host_realm.c' || echo '$(srcdir)/'`free_host_realm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-free_host_realm.Tpo $(DEPDIR)/libkrb5_la-free_host_realm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='free_host_realm.c' object='libkrb5_la-free_host_realm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-free_host_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-free_host_realm.Tpo -c -o libkrb5_la-free_host_realm.lo `test -f 'free_host_realm.c' || echo '$(srcdir)/'`free_host_realm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-free_host_realm.Tpo $(DEPDIR)/libkrb5_la-free_host_realm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='free_host_realm.c' object='libkrb5_la-free_host_realm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-free_host_realm.lo `test -f 'free_host_realm.c' || echo '$(srcdir)/'`free_host_realm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-free_host_realm.lo `test -f 'free_host_realm.c' || echo '$(srcdir)/'`free_host_realm.c
libkrb5_la-generate_seq_number.lo: generate_seq_number.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-generate_seq_number.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-generate_seq_number.Tpo -c -o libkrb5_la-generate_seq_number.lo `test -f 'generate_seq_number.c' || echo '$(srcdir)/'`generate_seq_number.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-generate_seq_number.Tpo $(DEPDIR)/libkrb5_la-generate_seq_number.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='generate_seq_number.c' object='libkrb5_la-generate_seq_number.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-generate_seq_number.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-generate_seq_number.Tpo -c -o libkrb5_la-generate_seq_number.lo `test -f 'generate_seq_number.c' || echo '$(srcdir)/'`generate_seq_number.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-generate_seq_number.Tpo $(DEPDIR)/libkrb5_la-generate_seq_number.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='generate_seq_number.c' object='libkrb5_la-generate_seq_number.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-generate_seq_number.lo `test -f 'generate_seq_number.c' || echo '$(srcdir)/'`generate_seq_number.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-generate_seq_number.lo `test -f 'generate_seq_number.c' || echo '$(srcdir)/'`generate_seq_number.c
libkrb5_la-generate_subkey.lo: generate_subkey.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-generate_subkey.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-generate_subkey.Tpo -c -o libkrb5_la-generate_subkey.lo `test -f 'generate_subkey.c' || echo '$(srcdir)/'`generate_subkey.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-generate_subkey.Tpo $(DEPDIR)/libkrb5_la-generate_subkey.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='generate_subkey.c' object='libkrb5_la-generate_subkey.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-generate_subkey.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-generate_subkey.Tpo -c -o libkrb5_la-generate_subkey.lo `test -f 'generate_subkey.c' || echo '$(srcdir)/'`generate_subkey.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-generate_subkey.Tpo $(DEPDIR)/libkrb5_la-generate_subkey.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='generate_subkey.c' object='libkrb5_la-generate_subkey.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-generate_subkey.lo `test -f 'generate_subkey.c' || echo '$(srcdir)/'`generate_subkey.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-generate_subkey.lo `test -f 'generate_subkey.c' || echo '$(srcdir)/'`generate_subkey.c
libkrb5_la-get_addrs.lo: get_addrs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_addrs.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_addrs.Tpo -c -o libkrb5_la-get_addrs.lo `test -f 'get_addrs.c' || echo '$(srcdir)/'`get_addrs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_addrs.Tpo $(DEPDIR)/libkrb5_la-get_addrs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_addrs.c' object='libkrb5_la-get_addrs.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_addrs.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_addrs.Tpo -c -o libkrb5_la-get_addrs.lo `test -f 'get_addrs.c' || echo '$(srcdir)/'`get_addrs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_addrs.Tpo $(DEPDIR)/libkrb5_la-get_addrs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_addrs.c' object='libkrb5_la-get_addrs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_addrs.lo `test -f 'get_addrs.c' || echo '$(srcdir)/'`get_addrs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_addrs.lo `test -f 'get_addrs.c' || echo '$(srcdir)/'`get_addrs.c
libkrb5_la-get_cred.lo: get_cred.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_cred.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_cred.Tpo -c -o libkrb5_la-get_cred.lo `test -f 'get_cred.c' || echo '$(srcdir)/'`get_cred.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_cred.Tpo $(DEPDIR)/libkrb5_la-get_cred.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_cred.c' object='libkrb5_la-get_cred.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_cred.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_cred.Tpo -c -o libkrb5_la-get_cred.lo `test -f 'get_cred.c' || echo '$(srcdir)/'`get_cred.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_cred.Tpo $(DEPDIR)/libkrb5_la-get_cred.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_cred.c' object='libkrb5_la-get_cred.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_cred.lo `test -f 'get_cred.c' || echo '$(srcdir)/'`get_cred.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_cred.lo `test -f 'get_cred.c' || echo '$(srcdir)/'`get_cred.c
libkrb5_la-get_default_principal.lo: get_default_principal.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_default_principal.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_default_principal.Tpo -c -o libkrb5_la-get_default_principal.lo `test -f 'get_default_principal.c' || echo '$(srcdir)/'`get_default_principal.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_default_principal.Tpo $(DEPDIR)/libkrb5_la-get_default_principal.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_default_principal.c' object='libkrb5_la-get_default_principal.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_default_principal.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_default_principal.Tpo -c -o libkrb5_la-get_default_principal.lo `test -f 'get_default_principal.c' || echo '$(srcdir)/'`get_default_principal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_default_principal.Tpo $(DEPDIR)/libkrb5_la-get_default_principal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_default_principal.c' object='libkrb5_la-get_default_principal.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_default_principal.lo `test -f 'get_default_principal.c' || echo '$(srcdir)/'`get_default_principal.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_default_principal.lo `test -f 'get_default_principal.c' || echo '$(srcdir)/'`get_default_principal.c
libkrb5_la-get_default_realm.lo: get_default_realm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_default_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_default_realm.Tpo -c -o libkrb5_la-get_default_realm.lo `test -f 'get_default_realm.c' || echo '$(srcdir)/'`get_default_realm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_default_realm.Tpo $(DEPDIR)/libkrb5_la-get_default_realm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_default_realm.c' object='libkrb5_la-get_default_realm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_default_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_default_realm.Tpo -c -o libkrb5_la-get_default_realm.lo `test -f 'get_default_realm.c' || echo '$(srcdir)/'`get_default_realm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_default_realm.Tpo $(DEPDIR)/libkrb5_la-get_default_realm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_default_realm.c' object='libkrb5_la-get_default_realm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_default_realm.lo `test -f 'get_default_realm.c' || echo '$(srcdir)/'`get_default_realm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_default_realm.lo `test -f 'get_default_realm.c' || echo '$(srcdir)/'`get_default_realm.c
libkrb5_la-get_for_creds.lo: get_for_creds.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_for_creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_for_creds.Tpo -c -o libkrb5_la-get_for_creds.lo `test -f 'get_for_creds.c' || echo '$(srcdir)/'`get_for_creds.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_for_creds.Tpo $(DEPDIR)/libkrb5_la-get_for_creds.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_for_creds.c' object='libkrb5_la-get_for_creds.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_for_creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_for_creds.Tpo -c -o libkrb5_la-get_for_creds.lo `test -f 'get_for_creds.c' || echo '$(srcdir)/'`get_for_creds.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_for_creds.Tpo $(DEPDIR)/libkrb5_la-get_for_creds.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_for_creds.c' object='libkrb5_la-get_for_creds.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_for_creds.lo `test -f 'get_for_creds.c' || echo '$(srcdir)/'`get_for_creds.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_for_creds.lo `test -f 'get_for_creds.c' || echo '$(srcdir)/'`get_for_creds.c
libkrb5_la-get_host_realm.lo: get_host_realm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_host_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_host_realm.Tpo -c -o libkrb5_la-get_host_realm.lo `test -f 'get_host_realm.c' || echo '$(srcdir)/'`get_host_realm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_host_realm.Tpo $(DEPDIR)/libkrb5_la-get_host_realm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_host_realm.c' object='libkrb5_la-get_host_realm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_host_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_host_realm.Tpo -c -o libkrb5_la-get_host_realm.lo `test -f 'get_host_realm.c' || echo '$(srcdir)/'`get_host_realm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_host_realm.Tpo $(DEPDIR)/libkrb5_la-get_host_realm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_host_realm.c' object='libkrb5_la-get_host_realm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_host_realm.lo `test -f 'get_host_realm.c' || echo '$(srcdir)/'`get_host_realm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_host_realm.lo `test -f 'get_host_realm.c' || echo '$(srcdir)/'`get_host_realm.c
libkrb5_la-get_in_tkt.lo: get_in_tkt.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_in_tkt.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_in_tkt.Tpo -c -o libkrb5_la-get_in_tkt.lo `test -f 'get_in_tkt.c' || echo '$(srcdir)/'`get_in_tkt.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_in_tkt.Tpo $(DEPDIR)/libkrb5_la-get_in_tkt.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_in_tkt.c' object='libkrb5_la-get_in_tkt.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_in_tkt.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_in_tkt.Tpo -c -o libkrb5_la-get_in_tkt.lo `test -f 'get_in_tkt.c' || echo '$(srcdir)/'`get_in_tkt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_in_tkt.Tpo $(DEPDIR)/libkrb5_la-get_in_tkt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_in_tkt.c' object='libkrb5_la-get_in_tkt.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_in_tkt.lo `test -f 'get_in_tkt.c' || echo '$(srcdir)/'`get_in_tkt.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_in_tkt.lo `test -f 'get_in_tkt.c' || echo '$(srcdir)/'`get_in_tkt.c
libkrb5_la-get_port.lo: get_port.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_port.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_port.Tpo -c -o libkrb5_la-get_port.lo `test -f 'get_port.c' || echo '$(srcdir)/'`get_port.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-get_port.Tpo $(DEPDIR)/libkrb5_la-get_port.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_port.c' object='libkrb5_la-get_port.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-get_port.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-get_port.Tpo -c -o libkrb5_la-get_port.lo `test -f 'get_port.c' || echo '$(srcdir)/'`get_port.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-get_port.Tpo $(DEPDIR)/libkrb5_la-get_port.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_port.c' object='libkrb5_la-get_port.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_port.lo `test -f 'get_port.c' || echo '$(srcdir)/'`get_port.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-get_port.lo `test -f 'get_port.c' || echo '$(srcdir)/'`get_port.c
libkrb5_la-init_creds.lo: init_creds.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-init_creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-init_creds.Tpo -c -o libkrb5_la-init_creds.lo `test -f 'init_creds.c' || echo '$(srcdir)/'`init_creds.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-init_creds.Tpo $(DEPDIR)/libkrb5_la-init_creds.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='init_creds.c' object='libkrb5_la-init_creds.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-init_creds.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-init_creds.Tpo -c -o libkrb5_la-init_creds.lo `test -f 'init_creds.c' || echo '$(srcdir)/'`init_creds.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-init_creds.Tpo $(DEPDIR)/libkrb5_la-init_creds.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init_creds.c' object='libkrb5_la-init_creds.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-init_creds.lo `test -f 'init_creds.c' || echo '$(srcdir)/'`init_creds.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-init_creds.lo `test -f 'init_creds.c' || echo '$(srcdir)/'`init_creds.c
libkrb5_la-init_creds_pw.lo: init_creds_pw.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-init_creds_pw.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-init_creds_pw.Tpo -c -o libkrb5_la-init_creds_pw.lo `test -f 'init_creds_pw.c' || echo '$(srcdir)/'`init_creds_pw.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-init_creds_pw.Tpo $(DEPDIR)/libkrb5_la-init_creds_pw.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='init_creds_pw.c' object='libkrb5_la-init_creds_pw.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-init_creds_pw.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-init_creds_pw.Tpo -c -o libkrb5_la-init_creds_pw.lo `test -f 'init_creds_pw.c' || echo '$(srcdir)/'`init_creds_pw.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-init_creds_pw.Tpo $(DEPDIR)/libkrb5_la-init_creds_pw.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='init_creds_pw.c' object='libkrb5_la-init_creds_pw.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-init_creds_pw.lo `test -f 'init_creds_pw.c' || echo '$(srcdir)/'`init_creds_pw.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-init_creds_pw.lo `test -f 'init_creds_pw.c' || echo '$(srcdir)/'`init_creds_pw.c
libkrb5_la-kcm.lo: kcm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-kcm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-kcm.Tpo -c -o libkrb5_la-kcm.lo `test -f 'kcm.c' || echo '$(srcdir)/'`kcm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-kcm.Tpo $(DEPDIR)/libkrb5_la-kcm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='kcm.c' object='libkrb5_la-kcm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-kcm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-kcm.Tpo -c -o libkrb5_la-kcm.lo `test -f 'kcm.c' || echo '$(srcdir)/'`kcm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-kcm.Tpo $(DEPDIR)/libkrb5_la-kcm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kcm.c' object='libkrb5_la-kcm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-kcm.lo `test -f 'kcm.c' || echo '$(srcdir)/'`kcm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-kcm.lo `test -f 'kcm.c' || echo '$(srcdir)/'`kcm.c
libkrb5_la-keyblock.lo: keyblock.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keyblock.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keyblock.Tpo -c -o libkrb5_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-keyblock.Tpo $(DEPDIR)/libkrb5_la-keyblock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keyblock.c' object='libkrb5_la-keyblock.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keyblock.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keyblock.Tpo -c -o libkrb5_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-keyblock.Tpo $(DEPDIR)/libkrb5_la-keyblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keyblock.c' object='libkrb5_la-keyblock.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
libkrb5_la-keytab.lo: keytab.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab.Tpo -c -o libkrb5_la-keytab.lo `test -f 'keytab.c' || echo '$(srcdir)/'`keytab.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-keytab.Tpo $(DEPDIR)/libkrb5_la-keytab.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keytab.c' object='libkrb5_la-keytab.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab.Tpo -c -o libkrb5_la-keytab.lo `test -f 'keytab.c' || echo '$(srcdir)/'`keytab.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-keytab.Tpo $(DEPDIR)/libkrb5_la-keytab.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keytab.c' object='libkrb5_la-keytab.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab.lo `test -f 'keytab.c' || echo '$(srcdir)/'`keytab.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab.lo `test -f 'keytab.c' || echo '$(srcdir)/'`keytab.c
libkrb5_la-keytab_any.lo: keytab_any.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_any.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_any.Tpo -c -o libkrb5_la-keytab_any.lo `test -f 'keytab_any.c' || echo '$(srcdir)/'`keytab_any.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-keytab_any.Tpo $(DEPDIR)/libkrb5_la-keytab_any.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keytab_any.c' object='libkrb5_la-keytab_any.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_any.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_any.Tpo -c -o libkrb5_la-keytab_any.lo `test -f 'keytab_any.c' || echo '$(srcdir)/'`keytab_any.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-keytab_any.Tpo $(DEPDIR)/libkrb5_la-keytab_any.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keytab_any.c' object='libkrb5_la-keytab_any.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_any.lo `test -f 'keytab_any.c' || echo '$(srcdir)/'`keytab_any.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_any.lo `test -f 'keytab_any.c' || echo '$(srcdir)/'`keytab_any.c
libkrb5_la-keytab_file.lo: keytab_file.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_file.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_file.Tpo -c -o libkrb5_la-keytab_file.lo `test -f 'keytab_file.c' || echo '$(srcdir)/'`keytab_file.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-keytab_file.Tpo $(DEPDIR)/libkrb5_la-keytab_file.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keytab_file.c' object='libkrb5_la-keytab_file.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_file.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_file.Tpo -c -o libkrb5_la-keytab_file.lo `test -f 'keytab_file.c' || echo '$(srcdir)/'`keytab_file.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-keytab_file.Tpo $(DEPDIR)/libkrb5_la-keytab_file.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keytab_file.c' object='libkrb5_la-keytab_file.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_file.lo `test -f 'keytab_file.c' || echo '$(srcdir)/'`keytab_file.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_file.lo `test -f 'keytab_file.c' || echo '$(srcdir)/'`keytab_file.c
libkrb5_la-keytab_keyfile.lo: keytab_keyfile.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_keyfile.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_keyfile.Tpo -c -o libkrb5_la-keytab_keyfile.lo `test -f 'keytab_keyfile.c' || echo '$(srcdir)/'`keytab_keyfile.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-keytab_keyfile.Tpo $(DEPDIR)/libkrb5_la-keytab_keyfile.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keytab_keyfile.c' object='libkrb5_la-keytab_keyfile.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_keyfile.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_keyfile.Tpo -c -o libkrb5_la-keytab_keyfile.lo `test -f 'keytab_keyfile.c' || echo '$(srcdir)/'`keytab_keyfile.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-keytab_keyfile.Tpo $(DEPDIR)/libkrb5_la-keytab_keyfile.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keytab_keyfile.c' object='libkrb5_la-keytab_keyfile.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_keyfile.lo `test -f 'keytab_keyfile.c' || echo '$(srcdir)/'`keytab_keyfile.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_keyfile.lo `test -f 'keytab_keyfile.c' || echo '$(srcdir)/'`keytab_keyfile.c
libkrb5_la-keytab_memory.lo: keytab_memory.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_memory.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_memory.Tpo -c -o libkrb5_la-keytab_memory.lo `test -f 'keytab_memory.c' || echo '$(srcdir)/'`keytab_memory.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-keytab_memory.Tpo $(DEPDIR)/libkrb5_la-keytab_memory.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keytab_memory.c' object='libkrb5_la-keytab_memory.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-keytab_memory.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-keytab_memory.Tpo -c -o libkrb5_la-keytab_memory.lo `test -f 'keytab_memory.c' || echo '$(srcdir)/'`keytab_memory.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-keytab_memory.Tpo $(DEPDIR)/libkrb5_la-keytab_memory.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keytab_memory.c' object='libkrb5_la-keytab_memory.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_memory.lo `test -f 'keytab_memory.c' || echo '$(srcdir)/'`keytab_memory.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-keytab_memory.lo `test -f 'keytab_memory.c' || echo '$(srcdir)/'`keytab_memory.c
libkrb5_la-krbhst.lo: krbhst.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-krbhst.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-krbhst.Tpo -c -o libkrb5_la-krbhst.lo `test -f 'krbhst.c' || echo '$(srcdir)/'`krbhst.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-krbhst.Tpo $(DEPDIR)/libkrb5_la-krbhst.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='krbhst.c' object='libkrb5_la-krbhst.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-krbhst.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-krbhst.Tpo -c -o libkrb5_la-krbhst.lo `test -f 'krbhst.c' || echo '$(srcdir)/'`krbhst.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-krbhst.Tpo $(DEPDIR)/libkrb5_la-krbhst.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krbhst.c' object='libkrb5_la-krbhst.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-krbhst.lo `test -f 'krbhst.c' || echo '$(srcdir)/'`krbhst.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-krbhst.lo `test -f 'krbhst.c' || echo '$(srcdir)/'`krbhst.c
libkrb5_la-kuserok.lo: kuserok.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-kuserok.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-kuserok.Tpo -c -o libkrb5_la-kuserok.lo `test -f 'kuserok.c' || echo '$(srcdir)/'`kuserok.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-kuserok.Tpo $(DEPDIR)/libkrb5_la-kuserok.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='kuserok.c' object='libkrb5_la-kuserok.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-kuserok.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-kuserok.Tpo -c -o libkrb5_la-kuserok.lo `test -f 'kuserok.c' || echo '$(srcdir)/'`kuserok.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-kuserok.Tpo $(DEPDIR)/libkrb5_la-kuserok.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='kuserok.c' object='libkrb5_la-kuserok.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-kuserok.lo `test -f 'kuserok.c' || echo '$(srcdir)/'`kuserok.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-kuserok.lo `test -f 'kuserok.c' || echo '$(srcdir)/'`kuserok.c
libkrb5_la-log.lo: log.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-log.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-log.Tpo -c -o libkrb5_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-log.Tpo $(DEPDIR)/libkrb5_la-log.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='log.c' object='libkrb5_la-log.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-log.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-log.Tpo -c -o libkrb5_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-log.Tpo $(DEPDIR)/libkrb5_la-log.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='log.c' object='libkrb5_la-log.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-log.lo `test -f 'log.c' || echo '$(srcdir)/'`log.c
libkrb5_la-mcache.lo: mcache.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mcache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mcache.Tpo -c -o libkrb5_la-mcache.lo `test -f 'mcache.c' || echo '$(srcdir)/'`mcache.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mcache.Tpo $(DEPDIR)/libkrb5_la-mcache.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mcache.c' object='libkrb5_la-mcache.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mcache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mcache.Tpo -c -o libkrb5_la-mcache.lo `test -f 'mcache.c' || echo '$(srcdir)/'`mcache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mcache.Tpo $(DEPDIR)/libkrb5_la-mcache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mcache.c' object='libkrb5_la-mcache.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mcache.lo `test -f 'mcache.c' || echo '$(srcdir)/'`mcache.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mcache.lo `test -f 'mcache.c' || echo '$(srcdir)/'`mcache.c
libkrb5_la-misc.lo: misc.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-misc.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-misc.Tpo -c -o libkrb5_la-misc.lo `test -f 'misc.c' || echo '$(srcdir)/'`misc.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-misc.Tpo $(DEPDIR)/libkrb5_la-misc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='misc.c' object='libkrb5_la-misc.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-misc.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-misc.Tpo -c -o libkrb5_la-misc.lo `test -f 'misc.c' || echo '$(srcdir)/'`misc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-misc.Tpo $(DEPDIR)/libkrb5_la-misc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='misc.c' object='libkrb5_la-misc.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-misc.lo `test -f 'misc.c' || echo '$(srcdir)/'`misc.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-misc.lo `test -f 'misc.c' || echo '$(srcdir)/'`misc.c
libkrb5_la-mk_error.lo: mk_error.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_error.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_error.Tpo -c -o libkrb5_la-mk_error.lo `test -f 'mk_error.c' || echo '$(srcdir)/'`mk_error.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mk_error.Tpo $(DEPDIR)/libkrb5_la-mk_error.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mk_error.c' object='libkrb5_la-mk_error.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_error.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_error.Tpo -c -o libkrb5_la-mk_error.lo `test -f 'mk_error.c' || echo '$(srcdir)/'`mk_error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mk_error.Tpo $(DEPDIR)/libkrb5_la-mk_error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mk_error.c' object='libkrb5_la-mk_error.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_error.lo `test -f 'mk_error.c' || echo '$(srcdir)/'`mk_error.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_error.lo `test -f 'mk_error.c' || echo '$(srcdir)/'`mk_error.c
libkrb5_la-mk_priv.lo: mk_priv.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_priv.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_priv.Tpo -c -o libkrb5_la-mk_priv.lo `test -f 'mk_priv.c' || echo '$(srcdir)/'`mk_priv.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mk_priv.Tpo $(DEPDIR)/libkrb5_la-mk_priv.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mk_priv.c' object='libkrb5_la-mk_priv.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_priv.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_priv.Tpo -c -o libkrb5_la-mk_priv.lo `test -f 'mk_priv.c' || echo '$(srcdir)/'`mk_priv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mk_priv.Tpo $(DEPDIR)/libkrb5_la-mk_priv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mk_priv.c' object='libkrb5_la-mk_priv.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_priv.lo `test -f 'mk_priv.c' || echo '$(srcdir)/'`mk_priv.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_priv.lo `test -f 'mk_priv.c' || echo '$(srcdir)/'`mk_priv.c
libkrb5_la-mk_rep.lo: mk_rep.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_rep.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_rep.Tpo -c -o libkrb5_la-mk_rep.lo `test -f 'mk_rep.c' || echo '$(srcdir)/'`mk_rep.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mk_rep.Tpo $(DEPDIR)/libkrb5_la-mk_rep.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mk_rep.c' object='libkrb5_la-mk_rep.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_rep.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_rep.Tpo -c -o libkrb5_la-mk_rep.lo `test -f 'mk_rep.c' || echo '$(srcdir)/'`mk_rep.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mk_rep.Tpo $(DEPDIR)/libkrb5_la-mk_rep.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mk_rep.c' object='libkrb5_la-mk_rep.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_rep.lo `test -f 'mk_rep.c' || echo '$(srcdir)/'`mk_rep.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_rep.lo `test -f 'mk_rep.c' || echo '$(srcdir)/'`mk_rep.c
libkrb5_la-mk_req.lo: mk_req.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_req.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_req.Tpo -c -o libkrb5_la-mk_req.lo `test -f 'mk_req.c' || echo '$(srcdir)/'`mk_req.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mk_req.Tpo $(DEPDIR)/libkrb5_la-mk_req.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mk_req.c' object='libkrb5_la-mk_req.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_req.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_req.Tpo -c -o libkrb5_la-mk_req.lo `test -f 'mk_req.c' || echo '$(srcdir)/'`mk_req.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mk_req.Tpo $(DEPDIR)/libkrb5_la-mk_req.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mk_req.c' object='libkrb5_la-mk_req.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_req.lo `test -f 'mk_req.c' || echo '$(srcdir)/'`mk_req.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_req.lo `test -f 'mk_req.c' || echo '$(srcdir)/'`mk_req.c
libkrb5_la-mk_req_ext.lo: mk_req_ext.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_req_ext.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_req_ext.Tpo -c -o libkrb5_la-mk_req_ext.lo `test -f 'mk_req_ext.c' || echo '$(srcdir)/'`mk_req_ext.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mk_req_ext.Tpo $(DEPDIR)/libkrb5_la-mk_req_ext.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mk_req_ext.c' object='libkrb5_la-mk_req_ext.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_req_ext.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_req_ext.Tpo -c -o libkrb5_la-mk_req_ext.lo `test -f 'mk_req_ext.c' || echo '$(srcdir)/'`mk_req_ext.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mk_req_ext.Tpo $(DEPDIR)/libkrb5_la-mk_req_ext.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mk_req_ext.c' object='libkrb5_la-mk_req_ext.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_req_ext.lo `test -f 'mk_req_ext.c' || echo '$(srcdir)/'`mk_req_ext.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_req_ext.lo `test -f 'mk_req_ext.c' || echo '$(srcdir)/'`mk_req_ext.c
libkrb5_la-mk_safe.lo: mk_safe.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_safe.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_safe.Tpo -c -o libkrb5_la-mk_safe.lo `test -f 'mk_safe.c' || echo '$(srcdir)/'`mk_safe.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mk_safe.Tpo $(DEPDIR)/libkrb5_la-mk_safe.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mk_safe.c' object='libkrb5_la-mk_safe.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mk_safe.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mk_safe.Tpo -c -o libkrb5_la-mk_safe.lo `test -f 'mk_safe.c' || echo '$(srcdir)/'`mk_safe.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mk_safe.Tpo $(DEPDIR)/libkrb5_la-mk_safe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mk_safe.c' object='libkrb5_la-mk_safe.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_safe.lo `test -f 'mk_safe.c' || echo '$(srcdir)/'`mk_safe.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mk_safe.lo `test -f 'mk_safe.c' || echo '$(srcdir)/'`mk_safe.c
libkrb5_la-mit_glue.lo: mit_glue.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mit_glue.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mit_glue.Tpo -c -o libkrb5_la-mit_glue.lo `test -f 'mit_glue.c' || echo '$(srcdir)/'`mit_glue.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-mit_glue.Tpo $(DEPDIR)/libkrb5_la-mit_glue.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mit_glue.c' object='libkrb5_la-mit_glue.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-mit_glue.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-mit_glue.Tpo -c -o libkrb5_la-mit_glue.lo `test -f 'mit_glue.c' || echo '$(srcdir)/'`mit_glue.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-mit_glue.Tpo $(DEPDIR)/libkrb5_la-mit_glue.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mit_glue.c' object='libkrb5_la-mit_glue.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mit_glue.lo `test -f 'mit_glue.c' || echo '$(srcdir)/'`mit_glue.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-mit_glue.lo `test -f 'mit_glue.c' || echo '$(srcdir)/'`mit_glue.c
libkrb5_la-net_read.lo: net_read.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-net_read.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-net_read.Tpo -c -o libkrb5_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-net_read.Tpo $(DEPDIR)/libkrb5_la-net_read.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='net_read.c' object='libkrb5_la-net_read.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-net_read.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-net_read.Tpo -c -o libkrb5_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-net_read.Tpo $(DEPDIR)/libkrb5_la-net_read.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net_read.c' object='libkrb5_la-net_read.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
libkrb5_la-net_write.lo: net_write.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-net_write.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-net_write.Tpo -c -o libkrb5_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-net_write.Tpo $(DEPDIR)/libkrb5_la-net_write.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='net_write.c' object='libkrb5_la-net_write.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-net_write.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-net_write.Tpo -c -o libkrb5_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-net_write.Tpo $(DEPDIR)/libkrb5_la-net_write.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net_write.c' object='libkrb5_la-net_write.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
libkrb5_la-n-fold.lo: n-fold.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-n-fold.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-n-fold.Tpo -c -o libkrb5_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-n-fold.Tpo $(DEPDIR)/libkrb5_la-n-fold.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='n-fold.c' object='libkrb5_la-n-fold.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-n-fold.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-n-fold.Tpo -c -o libkrb5_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-n-fold.Tpo $(DEPDIR)/libkrb5_la-n-fold.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='n-fold.c' object='libkrb5_la-n-fold.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
libkrb5_la-pac.lo: pac.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-pac.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-pac.Tpo -c -o libkrb5_la-pac.lo `test -f 'pac.c' || echo '$(srcdir)/'`pac.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-pac.Tpo $(DEPDIR)/libkrb5_la-pac.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pac.c' object='libkrb5_la-pac.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-pac.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-pac.Tpo -c -o libkrb5_la-pac.lo `test -f 'pac.c' || echo '$(srcdir)/'`pac.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-pac.Tpo $(DEPDIR)/libkrb5_la-pac.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pac.c' object='libkrb5_la-pac.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-pac.lo `test -f 'pac.c' || echo '$(srcdir)/'`pac.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-pac.lo `test -f 'pac.c' || echo '$(srcdir)/'`pac.c
libkrb5_la-padata.lo: padata.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-padata.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-padata.Tpo -c -o libkrb5_la-padata.lo `test -f 'padata.c' || echo '$(srcdir)/'`padata.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-padata.Tpo $(DEPDIR)/libkrb5_la-padata.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='padata.c' object='libkrb5_la-padata.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-padata.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-padata.Tpo -c -o libkrb5_la-padata.lo `test -f 'padata.c' || echo '$(srcdir)/'`padata.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-padata.Tpo $(DEPDIR)/libkrb5_la-padata.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='padata.c' object='libkrb5_la-padata.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-padata.lo `test -f 'padata.c' || echo '$(srcdir)/'`padata.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-padata.lo `test -f 'padata.c' || echo '$(srcdir)/'`padata.c
libkrb5_la-pcache.lo: pcache.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-pcache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-pcache.Tpo -c -o libkrb5_la-pcache.lo `test -f 'pcache.c' || echo '$(srcdir)/'`pcache.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-pcache.Tpo $(DEPDIR)/libkrb5_la-pcache.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pcache.c' object='libkrb5_la-pcache.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-pcache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-pcache.Tpo -c -o libkrb5_la-pcache.lo `test -f 'pcache.c' || echo '$(srcdir)/'`pcache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-pcache.Tpo $(DEPDIR)/libkrb5_la-pcache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pcache.c' object='libkrb5_la-pcache.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-pcache.lo `test -f 'pcache.c' || echo '$(srcdir)/'`pcache.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-pcache.lo `test -f 'pcache.c' || echo '$(srcdir)/'`pcache.c
libkrb5_la-pkinit.lo: pkinit.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-pkinit.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-pkinit.Tpo -c -o libkrb5_la-pkinit.lo `test -f 'pkinit.c' || echo '$(srcdir)/'`pkinit.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-pkinit.Tpo $(DEPDIR)/libkrb5_la-pkinit.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='pkinit.c' object='libkrb5_la-pkinit.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-pkinit.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-pkinit.Tpo -c -o libkrb5_la-pkinit.lo `test -f 'pkinit.c' || echo '$(srcdir)/'`pkinit.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-pkinit.Tpo $(DEPDIR)/libkrb5_la-pkinit.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pkinit.c' object='libkrb5_la-pkinit.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-pkinit.lo `test -f 'pkinit.c' || echo '$(srcdir)/'`pkinit.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-pkinit.lo `test -f 'pkinit.c' || echo '$(srcdir)/'`pkinit.c
+
+libkrb5_la-pkinit-ec.lo: pkinit-ec.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-pkinit-ec.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-pkinit-ec.Tpo -c -o libkrb5_la-pkinit-ec.lo `test -f 'pkinit-ec.c' || echo '$(srcdir)/'`pkinit-ec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-pkinit-ec.Tpo $(DEPDIR)/libkrb5_la-pkinit-ec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='pkinit-ec.c' object='libkrb5_la-pkinit-ec.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-pkinit-ec.lo `test -f 'pkinit-ec.c' || echo '$(srcdir)/'`pkinit-ec.c
libkrb5_la-principal.lo: principal.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-principal.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-principal.Tpo -c -o libkrb5_la-principal.lo `test -f 'principal.c' || echo '$(srcdir)/'`principal.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-principal.Tpo $(DEPDIR)/libkrb5_la-principal.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='principal.c' object='libkrb5_la-principal.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-principal.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-principal.Tpo -c -o libkrb5_la-principal.lo `test -f 'principal.c' || echo '$(srcdir)/'`principal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-principal.Tpo $(DEPDIR)/libkrb5_la-principal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='principal.c' object='libkrb5_la-principal.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-principal.lo `test -f 'principal.c' || echo '$(srcdir)/'`principal.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-principal.lo `test -f 'principal.c' || echo '$(srcdir)/'`principal.c
libkrb5_la-prog_setup.lo: prog_setup.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-prog_setup.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-prog_setup.Tpo -c -o libkrb5_la-prog_setup.lo `test -f 'prog_setup.c' || echo '$(srcdir)/'`prog_setup.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-prog_setup.Tpo $(DEPDIR)/libkrb5_la-prog_setup.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='prog_setup.c' object='libkrb5_la-prog_setup.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-prog_setup.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-prog_setup.Tpo -c -o libkrb5_la-prog_setup.lo `test -f 'prog_setup.c' || echo '$(srcdir)/'`prog_setup.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-prog_setup.Tpo $(DEPDIR)/libkrb5_la-prog_setup.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='prog_setup.c' object='libkrb5_la-prog_setup.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-prog_setup.lo `test -f 'prog_setup.c' || echo '$(srcdir)/'`prog_setup.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-prog_setup.lo `test -f 'prog_setup.c' || echo '$(srcdir)/'`prog_setup.c
libkrb5_la-prompter_posix.lo: prompter_posix.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-prompter_posix.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-prompter_posix.Tpo -c -o libkrb5_la-prompter_posix.lo `test -f 'prompter_posix.c' || echo '$(srcdir)/'`prompter_posix.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-prompter_posix.Tpo $(DEPDIR)/libkrb5_la-prompter_posix.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='prompter_posix.c' object='libkrb5_la-prompter_posix.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-prompter_posix.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-prompter_posix.Tpo -c -o libkrb5_la-prompter_posix.lo `test -f 'prompter_posix.c' || echo '$(srcdir)/'`prompter_posix.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-prompter_posix.Tpo $(DEPDIR)/libkrb5_la-prompter_posix.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='prompter_posix.c' object='libkrb5_la-prompter_posix.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-prompter_posix.lo `test -f 'prompter_posix.c' || echo '$(srcdir)/'`prompter_posix.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-prompter_posix.lo `test -f 'prompter_posix.c' || echo '$(srcdir)/'`prompter_posix.c
libkrb5_la-rd_cred.lo: rd_cred.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_cred.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_cred.Tpo -c -o libkrb5_la-rd_cred.lo `test -f 'rd_cred.c' || echo '$(srcdir)/'`rd_cred.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-rd_cred.Tpo $(DEPDIR)/libkrb5_la-rd_cred.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rd_cred.c' object='libkrb5_la-rd_cred.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_cred.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_cred.Tpo -c -o libkrb5_la-rd_cred.lo `test -f 'rd_cred.c' || echo '$(srcdir)/'`rd_cred.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-rd_cred.Tpo $(DEPDIR)/libkrb5_la-rd_cred.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rd_cred.c' object='libkrb5_la-rd_cred.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_cred.lo `test -f 'rd_cred.c' || echo '$(srcdir)/'`rd_cred.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_cred.lo `test -f 'rd_cred.c' || echo '$(srcdir)/'`rd_cred.c
libkrb5_la-rd_error.lo: rd_error.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_error.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_error.Tpo -c -o libkrb5_la-rd_error.lo `test -f 'rd_error.c' || echo '$(srcdir)/'`rd_error.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-rd_error.Tpo $(DEPDIR)/libkrb5_la-rd_error.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rd_error.c' object='libkrb5_la-rd_error.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_error.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_error.Tpo -c -o libkrb5_la-rd_error.lo `test -f 'rd_error.c' || echo '$(srcdir)/'`rd_error.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-rd_error.Tpo $(DEPDIR)/libkrb5_la-rd_error.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rd_error.c' object='libkrb5_la-rd_error.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_error.lo `test -f 'rd_error.c' || echo '$(srcdir)/'`rd_error.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_error.lo `test -f 'rd_error.c' || echo '$(srcdir)/'`rd_error.c
libkrb5_la-rd_priv.lo: rd_priv.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_priv.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_priv.Tpo -c -o libkrb5_la-rd_priv.lo `test -f 'rd_priv.c' || echo '$(srcdir)/'`rd_priv.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-rd_priv.Tpo $(DEPDIR)/libkrb5_la-rd_priv.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rd_priv.c' object='libkrb5_la-rd_priv.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_priv.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_priv.Tpo -c -o libkrb5_la-rd_priv.lo `test -f 'rd_priv.c' || echo '$(srcdir)/'`rd_priv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-rd_priv.Tpo $(DEPDIR)/libkrb5_la-rd_priv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rd_priv.c' object='libkrb5_la-rd_priv.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_priv.lo `test -f 'rd_priv.c' || echo '$(srcdir)/'`rd_priv.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_priv.lo `test -f 'rd_priv.c' || echo '$(srcdir)/'`rd_priv.c
libkrb5_la-rd_rep.lo: rd_rep.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_rep.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_rep.Tpo -c -o libkrb5_la-rd_rep.lo `test -f 'rd_rep.c' || echo '$(srcdir)/'`rd_rep.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-rd_rep.Tpo $(DEPDIR)/libkrb5_la-rd_rep.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rd_rep.c' object='libkrb5_la-rd_rep.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_rep.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_rep.Tpo -c -o libkrb5_la-rd_rep.lo `test -f 'rd_rep.c' || echo '$(srcdir)/'`rd_rep.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-rd_rep.Tpo $(DEPDIR)/libkrb5_la-rd_rep.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rd_rep.c' object='libkrb5_la-rd_rep.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_rep.lo `test -f 'rd_rep.c' || echo '$(srcdir)/'`rd_rep.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_rep.lo `test -f 'rd_rep.c' || echo '$(srcdir)/'`rd_rep.c
libkrb5_la-rd_req.lo: rd_req.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_req.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_req.Tpo -c -o libkrb5_la-rd_req.lo `test -f 'rd_req.c' || echo '$(srcdir)/'`rd_req.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-rd_req.Tpo $(DEPDIR)/libkrb5_la-rd_req.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rd_req.c' object='libkrb5_la-rd_req.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_req.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_req.Tpo -c -o libkrb5_la-rd_req.lo `test -f 'rd_req.c' || echo '$(srcdir)/'`rd_req.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-rd_req.Tpo $(DEPDIR)/libkrb5_la-rd_req.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rd_req.c' object='libkrb5_la-rd_req.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_req.lo `test -f 'rd_req.c' || echo '$(srcdir)/'`rd_req.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_req.lo `test -f 'rd_req.c' || echo '$(srcdir)/'`rd_req.c
libkrb5_la-rd_safe.lo: rd_safe.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_safe.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_safe.Tpo -c -o libkrb5_la-rd_safe.lo `test -f 'rd_safe.c' || echo '$(srcdir)/'`rd_safe.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-rd_safe.Tpo $(DEPDIR)/libkrb5_la-rd_safe.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rd_safe.c' object='libkrb5_la-rd_safe.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-rd_safe.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-rd_safe.Tpo -c -o libkrb5_la-rd_safe.lo `test -f 'rd_safe.c' || echo '$(srcdir)/'`rd_safe.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-rd_safe.Tpo $(DEPDIR)/libkrb5_la-rd_safe.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rd_safe.c' object='libkrb5_la-rd_safe.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_safe.lo `test -f 'rd_safe.c' || echo '$(srcdir)/'`rd_safe.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-rd_safe.lo `test -f 'rd_safe.c' || echo '$(srcdir)/'`rd_safe.c
libkrb5_la-read_message.lo: read_message.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-read_message.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-read_message.Tpo -c -o libkrb5_la-read_message.lo `test -f 'read_message.c' || echo '$(srcdir)/'`read_message.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-read_message.Tpo $(DEPDIR)/libkrb5_la-read_message.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='read_message.c' object='libkrb5_la-read_message.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-read_message.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-read_message.Tpo -c -o libkrb5_la-read_message.lo `test -f 'read_message.c' || echo '$(srcdir)/'`read_message.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-read_message.Tpo $(DEPDIR)/libkrb5_la-read_message.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='read_message.c' object='libkrb5_la-read_message.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-read_message.lo `test -f 'read_message.c' || echo '$(srcdir)/'`read_message.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-read_message.lo `test -f 'read_message.c' || echo '$(srcdir)/'`read_message.c
libkrb5_la-recvauth.lo: recvauth.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-recvauth.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-recvauth.Tpo -c -o libkrb5_la-recvauth.lo `test -f 'recvauth.c' || echo '$(srcdir)/'`recvauth.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-recvauth.Tpo $(DEPDIR)/libkrb5_la-recvauth.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='recvauth.c' object='libkrb5_la-recvauth.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-recvauth.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-recvauth.Tpo -c -o libkrb5_la-recvauth.lo `test -f 'recvauth.c' || echo '$(srcdir)/'`recvauth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-recvauth.Tpo $(DEPDIR)/libkrb5_la-recvauth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='recvauth.c' object='libkrb5_la-recvauth.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-recvauth.lo `test -f 'recvauth.c' || echo '$(srcdir)/'`recvauth.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-recvauth.lo `test -f 'recvauth.c' || echo '$(srcdir)/'`recvauth.c
libkrb5_la-replay.lo: replay.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-replay.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-replay.Tpo -c -o libkrb5_la-replay.lo `test -f 'replay.c' || echo '$(srcdir)/'`replay.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-replay.Tpo $(DEPDIR)/libkrb5_la-replay.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='replay.c' object='libkrb5_la-replay.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-replay.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-replay.Tpo -c -o libkrb5_la-replay.lo `test -f 'replay.c' || echo '$(srcdir)/'`replay.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-replay.Tpo $(DEPDIR)/libkrb5_la-replay.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='replay.c' object='libkrb5_la-replay.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-replay.lo `test -f 'replay.c' || echo '$(srcdir)/'`replay.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-replay.lo `test -f 'replay.c' || echo '$(srcdir)/'`replay.c
libkrb5_la-salt.lo: salt.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt.Tpo -c -o libkrb5_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-salt.Tpo $(DEPDIR)/libkrb5_la-salt.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt.c' object='libkrb5_la-salt.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt.Tpo -c -o libkrb5_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-salt.Tpo $(DEPDIR)/libkrb5_la-salt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt.c' object='libkrb5_la-salt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
+
+libkrb5_la-salt-aes-sha1.lo: salt-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-aes-sha1.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-aes-sha1.Tpo -c -o libkrb5_la-salt-aes-sha1.lo `test -f 'salt-aes-sha1.c' || echo '$(srcdir)/'`salt-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-salt-aes-sha1.Tpo $(DEPDIR)/libkrb5_la-salt-aes-sha1.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-aes-sha1.c' object='libkrb5_la-salt-aes-sha1.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-aes-sha1.lo `test -f 'salt-aes-sha1.c' || echo '$(srcdir)/'`salt-aes-sha1.c
-libkrb5_la-salt-aes.lo: salt-aes.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-aes.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-aes.Tpo -c -o libkrb5_la-salt-aes.lo `test -f 'salt-aes.c' || echo '$(srcdir)/'`salt-aes.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-salt-aes.Tpo $(DEPDIR)/libkrb5_la-salt-aes.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-aes.c' object='libkrb5_la-salt-aes.lo' libtool=yes @AMDEPBACKSLASH@
+libkrb5_la-salt-aes-sha2.lo: salt-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-aes-sha2.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-aes-sha2.Tpo -c -o libkrb5_la-salt-aes-sha2.lo `test -f 'salt-aes-sha2.c' || echo '$(srcdir)/'`salt-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-salt-aes-sha2.Tpo $(DEPDIR)/libkrb5_la-salt-aes-sha2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-aes-sha2.c' object='libkrb5_la-salt-aes-sha2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-aes.lo `test -f 'salt-aes.c' || echo '$(srcdir)/'`salt-aes.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-aes-sha2.lo `test -f 'salt-aes-sha2.c' || echo '$(srcdir)/'`salt-aes-sha2.c
libkrb5_la-salt-arcfour.lo: salt-arcfour.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-arcfour.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-arcfour.Tpo -c -o libkrb5_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-salt-arcfour.Tpo $(DEPDIR)/libkrb5_la-salt-arcfour.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-arcfour.c' object='libkrb5_la-salt-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-arcfour.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-arcfour.Tpo -c -o libkrb5_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-salt-arcfour.Tpo $(DEPDIR)/libkrb5_la-salt-arcfour.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-arcfour.c' object='libkrb5_la-salt-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
libkrb5_la-salt-des.lo: salt-des.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-des.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-des.Tpo -c -o libkrb5_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-salt-des.Tpo $(DEPDIR)/libkrb5_la-salt-des.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-des.c' object='libkrb5_la-salt-des.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-des.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-des.Tpo -c -o libkrb5_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-salt-des.Tpo $(DEPDIR)/libkrb5_la-salt-des.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-des.c' object='libkrb5_la-salt-des.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
libkrb5_la-salt-des3.lo: salt-des3.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-des3.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-des3.Tpo -c -o libkrb5_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-salt-des3.Tpo $(DEPDIR)/libkrb5_la-salt-des3.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-des3.c' object='libkrb5_la-salt-des3.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-salt-des3.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-salt-des3.Tpo -c -o libkrb5_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-salt-des3.Tpo $(DEPDIR)/libkrb5_la-salt-des3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-des3.c' object='libkrb5_la-salt-des3.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
+
+libkrb5_la-sp800-108-kdf.lo: sp800-108-kdf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-sp800-108-kdf.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-sp800-108-kdf.Tpo -c -o libkrb5_la-sp800-108-kdf.lo `test -f 'sp800-108-kdf.c' || echo '$(srcdir)/'`sp800-108-kdf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-sp800-108-kdf.Tpo $(DEPDIR)/libkrb5_la-sp800-108-kdf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sp800-108-kdf.c' object='libkrb5_la-sp800-108-kdf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-sp800-108-kdf.lo `test -f 'sp800-108-kdf.c' || echo '$(srcdir)/'`sp800-108-kdf.c
libkrb5_la-scache.lo: scache.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-scache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-scache.Tpo -c -o libkrb5_la-scache.lo `test -f 'scache.c' || echo '$(srcdir)/'`scache.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-scache.Tpo $(DEPDIR)/libkrb5_la-scache.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='scache.c' object='libkrb5_la-scache.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-scache.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-scache.Tpo -c -o libkrb5_la-scache.lo `test -f 'scache.c' || echo '$(srcdir)/'`scache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-scache.Tpo $(DEPDIR)/libkrb5_la-scache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scache.c' object='libkrb5_la-scache.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-scache.lo `test -f 'scache.c' || echo '$(srcdir)/'`scache.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-scache.lo `test -f 'scache.c' || echo '$(srcdir)/'`scache.c
libkrb5_la-send_to_kdc.lo: send_to_kdc.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-send_to_kdc.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-send_to_kdc.Tpo -c -o libkrb5_la-send_to_kdc.lo `test -f 'send_to_kdc.c' || echo '$(srcdir)/'`send_to_kdc.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-send_to_kdc.Tpo $(DEPDIR)/libkrb5_la-send_to_kdc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='send_to_kdc.c' object='libkrb5_la-send_to_kdc.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-send_to_kdc.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-send_to_kdc.Tpo -c -o libkrb5_la-send_to_kdc.lo `test -f 'send_to_kdc.c' || echo '$(srcdir)/'`send_to_kdc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-send_to_kdc.Tpo $(DEPDIR)/libkrb5_la-send_to_kdc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='send_to_kdc.c' object='libkrb5_la-send_to_kdc.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-send_to_kdc.lo `test -f 'send_to_kdc.c' || echo '$(srcdir)/'`send_to_kdc.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-send_to_kdc.lo `test -f 'send_to_kdc.c' || echo '$(srcdir)/'`send_to_kdc.c
libkrb5_la-sendauth.lo: sendauth.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-sendauth.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-sendauth.Tpo -c -o libkrb5_la-sendauth.lo `test -f 'sendauth.c' || echo '$(srcdir)/'`sendauth.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-sendauth.Tpo $(DEPDIR)/libkrb5_la-sendauth.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sendauth.c' object='libkrb5_la-sendauth.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-sendauth.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-sendauth.Tpo -c -o libkrb5_la-sendauth.lo `test -f 'sendauth.c' || echo '$(srcdir)/'`sendauth.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-sendauth.Tpo $(DEPDIR)/libkrb5_la-sendauth.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sendauth.c' object='libkrb5_la-sendauth.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-sendauth.lo `test -f 'sendauth.c' || echo '$(srcdir)/'`sendauth.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-sendauth.lo `test -f 'sendauth.c' || echo '$(srcdir)/'`sendauth.c
libkrb5_la-set_default_realm.lo: set_default_realm.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-set_default_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-set_default_realm.Tpo -c -o libkrb5_la-set_default_realm.lo `test -f 'set_default_realm.c' || echo '$(srcdir)/'`set_default_realm.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-set_default_realm.Tpo $(DEPDIR)/libkrb5_la-set_default_realm.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='set_default_realm.c' object='libkrb5_la-set_default_realm.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-set_default_realm.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-set_default_realm.Tpo -c -o libkrb5_la-set_default_realm.lo `test -f 'set_default_realm.c' || echo '$(srcdir)/'`set_default_realm.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-set_default_realm.Tpo $(DEPDIR)/libkrb5_la-set_default_realm.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='set_default_realm.c' object='libkrb5_la-set_default_realm.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-set_default_realm.lo `test -f 'set_default_realm.c' || echo '$(srcdir)/'`set_default_realm.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-set_default_realm.lo `test -f 'set_default_realm.c' || echo '$(srcdir)/'`set_default_realm.c
libkrb5_la-sock_principal.lo: sock_principal.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-sock_principal.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-sock_principal.Tpo -c -o libkrb5_la-sock_principal.lo `test -f 'sock_principal.c' || echo '$(srcdir)/'`sock_principal.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-sock_principal.Tpo $(DEPDIR)/libkrb5_la-sock_principal.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='sock_principal.c' object='libkrb5_la-sock_principal.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-sock_principal.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-sock_principal.Tpo -c -o libkrb5_la-sock_principal.lo `test -f 'sock_principal.c' || echo '$(srcdir)/'`sock_principal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-sock_principal.Tpo $(DEPDIR)/libkrb5_la-sock_principal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sock_principal.c' object='libkrb5_la-sock_principal.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-sock_principal.lo `test -f 'sock_principal.c' || echo '$(srcdir)/'`sock_principal.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-sock_principal.lo `test -f 'sock_principal.c' || echo '$(srcdir)/'`sock_principal.c
libkrb5_la-store.lo: store.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store.Tpo -c -o libkrb5_la-store.lo `test -f 'store.c' || echo '$(srcdir)/'`store.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-store.Tpo $(DEPDIR)/libkrb5_la-store.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='store.c' object='libkrb5_la-store.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store.Tpo -c -o libkrb5_la-store.lo `test -f 'store.c' || echo '$(srcdir)/'`store.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-store.Tpo $(DEPDIR)/libkrb5_la-store.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='store.c' object='libkrb5_la-store.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store.lo `test -f 'store.c' || echo '$(srcdir)/'`store.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store.lo `test -f 'store.c' || echo '$(srcdir)/'`store.c
libkrb5_la-store-int.lo: store-int.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store-int.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store-int.Tpo -c -o libkrb5_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-store-int.Tpo $(DEPDIR)/libkrb5_la-store-int.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='store-int.c' object='libkrb5_la-store-int.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store-int.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store-int.Tpo -c -o libkrb5_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-store-int.Tpo $(DEPDIR)/libkrb5_la-store-int.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='store-int.c' object='libkrb5_la-store-int.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
libkrb5_la-store_emem.lo: store_emem.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store_emem.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store_emem.Tpo -c -o libkrb5_la-store_emem.lo `test -f 'store_emem.c' || echo '$(srcdir)/'`store_emem.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-store_emem.Tpo $(DEPDIR)/libkrb5_la-store_emem.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='store_emem.c' object='libkrb5_la-store_emem.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store_emem.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store_emem.Tpo -c -o libkrb5_la-store_emem.lo `test -f 'store_emem.c' || echo '$(srcdir)/'`store_emem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-store_emem.Tpo $(DEPDIR)/libkrb5_la-store_emem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='store_emem.c' object='libkrb5_la-store_emem.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store_emem.lo `test -f 'store_emem.c' || echo '$(srcdir)/'`store_emem.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store_emem.lo `test -f 'store_emem.c' || echo '$(srcdir)/'`store_emem.c
libkrb5_la-store_fd.lo: store_fd.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store_fd.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store_fd.Tpo -c -o libkrb5_la-store_fd.lo `test -f 'store_fd.c' || echo '$(srcdir)/'`store_fd.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-store_fd.Tpo $(DEPDIR)/libkrb5_la-store_fd.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='store_fd.c' object='libkrb5_la-store_fd.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store_fd.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store_fd.Tpo -c -o libkrb5_la-store_fd.lo `test -f 'store_fd.c' || echo '$(srcdir)/'`store_fd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-store_fd.Tpo $(DEPDIR)/libkrb5_la-store_fd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='store_fd.c' object='libkrb5_la-store_fd.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store_fd.lo `test -f 'store_fd.c' || echo '$(srcdir)/'`store_fd.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store_fd.lo `test -f 'store_fd.c' || echo '$(srcdir)/'`store_fd.c
libkrb5_la-store_mem.lo: store_mem.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store_mem.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store_mem.Tpo -c -o libkrb5_la-store_mem.lo `test -f 'store_mem.c' || echo '$(srcdir)/'`store_mem.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-store_mem.Tpo $(DEPDIR)/libkrb5_la-store_mem.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='store_mem.c' object='libkrb5_la-store_mem.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store_mem.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store_mem.Tpo -c -o libkrb5_la-store_mem.lo `test -f 'store_mem.c' || echo '$(srcdir)/'`store_mem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-store_mem.Tpo $(DEPDIR)/libkrb5_la-store_mem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='store_mem.c' object='libkrb5_la-store_mem.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store_mem.lo `test -f 'store_mem.c' || echo '$(srcdir)/'`store_mem.c
+
+libkrb5_la-store_sock.lo: store_sock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-store_sock.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-store_sock.Tpo -c -o libkrb5_la-store_sock.lo `test -f 'store_sock.c' || echo '$(srcdir)/'`store_sock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-store_sock.Tpo $(DEPDIR)/libkrb5_la-store_sock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='store_sock.c' object='libkrb5_la-store_sock.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store_mem.lo `test -f 'store_mem.c' || echo '$(srcdir)/'`store_mem.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-store_sock.lo `test -f 'store_sock.c' || echo '$(srcdir)/'`store_sock.c
libkrb5_la-plugin.lo: plugin.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-plugin.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-plugin.Tpo -c -o libkrb5_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-plugin.Tpo $(DEPDIR)/libkrb5_la-plugin.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='plugin.c' object='libkrb5_la-plugin.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-plugin.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-plugin.Tpo -c -o libkrb5_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-plugin.Tpo $(DEPDIR)/libkrb5_la-plugin.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='plugin.c' object='libkrb5_la-plugin.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-plugin.lo `test -f 'plugin.c' || echo '$(srcdir)/'`plugin.c
libkrb5_la-ticket.lo: ticket.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-ticket.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-ticket.Tpo -c -o libkrb5_la-ticket.lo `test -f 'ticket.c' || echo '$(srcdir)/'`ticket.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-ticket.Tpo $(DEPDIR)/libkrb5_la-ticket.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ticket.c' object='libkrb5_la-ticket.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-ticket.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-ticket.Tpo -c -o libkrb5_la-ticket.lo `test -f 'ticket.c' || echo '$(srcdir)/'`ticket.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-ticket.Tpo $(DEPDIR)/libkrb5_la-ticket.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ticket.c' object='libkrb5_la-ticket.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-ticket.lo `test -f 'ticket.c' || echo '$(srcdir)/'`ticket.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-ticket.lo `test -f 'ticket.c' || echo '$(srcdir)/'`ticket.c
libkrb5_la-time.lo: time.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-time.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-time.Tpo -c -o libkrb5_la-time.lo `test -f 'time.c' || echo '$(srcdir)/'`time.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-time.Tpo $(DEPDIR)/libkrb5_la-time.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='time.c' object='libkrb5_la-time.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-time.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-time.Tpo -c -o libkrb5_la-time.lo `test -f 'time.c' || echo '$(srcdir)/'`time.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-time.Tpo $(DEPDIR)/libkrb5_la-time.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='time.c' object='libkrb5_la-time.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-time.lo `test -f 'time.c' || echo '$(srcdir)/'`time.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-time.lo `test -f 'time.c' || echo '$(srcdir)/'`time.c
libkrb5_la-transited.lo: transited.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-transited.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-transited.Tpo -c -o libkrb5_la-transited.lo `test -f 'transited.c' || echo '$(srcdir)/'`transited.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-transited.Tpo $(DEPDIR)/libkrb5_la-transited.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='transited.c' object='libkrb5_la-transited.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-transited.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-transited.Tpo -c -o libkrb5_la-transited.lo `test -f 'transited.c' || echo '$(srcdir)/'`transited.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-transited.Tpo $(DEPDIR)/libkrb5_la-transited.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='transited.c' object='libkrb5_la-transited.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-transited.lo `test -f 'transited.c' || echo '$(srcdir)/'`transited.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-transited.lo `test -f 'transited.c' || echo '$(srcdir)/'`transited.c
libkrb5_la-verify_init.lo: verify_init.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-verify_init.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-verify_init.Tpo -c -o libkrb5_la-verify_init.lo `test -f 'verify_init.c' || echo '$(srcdir)/'`verify_init.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-verify_init.Tpo $(DEPDIR)/libkrb5_la-verify_init.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='verify_init.c' object='libkrb5_la-verify_init.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-verify_init.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-verify_init.Tpo -c -o libkrb5_la-verify_init.lo `test -f 'verify_init.c' || echo '$(srcdir)/'`verify_init.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-verify_init.Tpo $(DEPDIR)/libkrb5_la-verify_init.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='verify_init.c' object='libkrb5_la-verify_init.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-verify_init.lo `test -f 'verify_init.c' || echo '$(srcdir)/'`verify_init.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-verify_init.lo `test -f 'verify_init.c' || echo '$(srcdir)/'`verify_init.c
libkrb5_la-verify_user.lo: verify_user.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-verify_user.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-verify_user.Tpo -c -o libkrb5_la-verify_user.lo `test -f 'verify_user.c' || echo '$(srcdir)/'`verify_user.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-verify_user.Tpo $(DEPDIR)/libkrb5_la-verify_user.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='verify_user.c' object='libkrb5_la-verify_user.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-verify_user.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-verify_user.Tpo -c -o libkrb5_la-verify_user.lo `test -f 'verify_user.c' || echo '$(srcdir)/'`verify_user.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-verify_user.Tpo $(DEPDIR)/libkrb5_la-verify_user.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='verify_user.c' object='libkrb5_la-verify_user.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-verify_user.lo `test -f 'verify_user.c' || echo '$(srcdir)/'`verify_user.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-verify_user.lo `test -f 'verify_user.c' || echo '$(srcdir)/'`verify_user.c
libkrb5_la-version.lo: version.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-version.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-version.Tpo -c -o libkrb5_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-version.Tpo $(DEPDIR)/libkrb5_la-version.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='version.c' object='libkrb5_la-version.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-version.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-version.Tpo -c -o libkrb5_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-version.Tpo $(DEPDIR)/libkrb5_la-version.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libkrb5_la-version.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
libkrb5_la-warn.lo: warn.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-warn.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-warn.Tpo -c -o libkrb5_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-warn.Tpo $(DEPDIR)/libkrb5_la-warn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='warn.c' object='libkrb5_la-warn.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-warn.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-warn.Tpo -c -o libkrb5_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-warn.Tpo $(DEPDIR)/libkrb5_la-warn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warn.c' object='libkrb5_la-warn.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
libkrb5_la-write_message.lo: write_message.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-write_message.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-write_message.Tpo -c -o libkrb5_la-write_message.lo `test -f 'write_message.c' || echo '$(srcdir)/'`write_message.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-write_message.Tpo $(DEPDIR)/libkrb5_la-write_message.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='write_message.c' object='libkrb5_la-write_message.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-write_message.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-write_message.Tpo -c -o libkrb5_la-write_message.lo `test -f 'write_message.c' || echo '$(srcdir)/'`write_message.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-write_message.Tpo $(DEPDIR)/libkrb5_la-write_message.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='write_message.c' object='libkrb5_la-write_message.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-write_message.lo `test -f 'write_message.c' || echo '$(srcdir)/'`write_message.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-write_message.lo `test -f 'write_message.c' || echo '$(srcdir)/'`write_message.c
libkrb5_la-krb5_err.lo: krb5_err.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-krb5_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-krb5_err.Tpo -c -o libkrb5_la-krb5_err.lo `test -f 'krb5_err.c' || echo '$(srcdir)/'`krb5_err.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-krb5_err.Tpo $(DEPDIR)/libkrb5_la-krb5_err.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='krb5_err.c' object='libkrb5_la-krb5_err.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-krb5_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-krb5_err.Tpo -c -o libkrb5_la-krb5_err.lo `test -f 'krb5_err.c' || echo '$(srcdir)/'`krb5_err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-krb5_err.Tpo $(DEPDIR)/libkrb5_la-krb5_err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb5_err.c' object='libkrb5_la-krb5_err.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-krb5_err.lo `test -f 'krb5_err.c' || echo '$(srcdir)/'`krb5_err.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-krb5_err.lo `test -f 'krb5_err.c' || echo '$(srcdir)/'`krb5_err.c
libkrb5_la-krb_err.lo: krb_err.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-krb_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-krb_err.Tpo -c -o libkrb5_la-krb_err.lo `test -f 'krb_err.c' || echo '$(srcdir)/'`krb_err.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-krb_err.Tpo $(DEPDIR)/libkrb5_la-krb_err.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='krb_err.c' object='libkrb5_la-krb_err.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-krb_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-krb_err.Tpo -c -o libkrb5_la-krb_err.lo `test -f 'krb_err.c' || echo '$(srcdir)/'`krb_err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-krb_err.Tpo $(DEPDIR)/libkrb5_la-krb_err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='krb_err.c' object='libkrb5_la-krb_err.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-krb_err.lo `test -f 'krb_err.c' || echo '$(srcdir)/'`krb_err.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-krb_err.lo `test -f 'krb_err.c' || echo '$(srcdir)/'`krb_err.c
libkrb5_la-heim_err.lo: heim_err.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-heim_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-heim_err.Tpo -c -o libkrb5_la-heim_err.lo `test -f 'heim_err.c' || echo '$(srcdir)/'`heim_err.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-heim_err.Tpo $(DEPDIR)/libkrb5_la-heim_err.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='heim_err.c' object='libkrb5_la-heim_err.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-heim_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-heim_err.Tpo -c -o libkrb5_la-heim_err.lo `test -f 'heim_err.c' || echo '$(srcdir)/'`heim_err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-heim_err.Tpo $(DEPDIR)/libkrb5_la-heim_err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='heim_err.c' object='libkrb5_la-heim_err.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-heim_err.lo `test -f 'heim_err.c' || echo '$(srcdir)/'`heim_err.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-heim_err.lo `test -f 'heim_err.c' || echo '$(srcdir)/'`heim_err.c
libkrb5_la-k524_err.lo: k524_err.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-k524_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-k524_err.Tpo -c -o libkrb5_la-k524_err.lo `test -f 'k524_err.c' || echo '$(srcdir)/'`k524_err.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libkrb5_la-k524_err.Tpo $(DEPDIR)/libkrb5_la-k524_err.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='k524_err.c' object='libkrb5_la-k524_err.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libkrb5_la-k524_err.lo -MD -MP -MF $(DEPDIR)/libkrb5_la-k524_err.Tpo -c -o libkrb5_la-k524_err.lo `test -f 'k524_err.c' || echo '$(srcdir)/'`k524_err.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libkrb5_la-k524_err.Tpo $(DEPDIR)/libkrb5_la-k524_err.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='k524_err.c' object='libkrb5_la-k524_err.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-k524_err.lo `test -f 'k524_err.c' || echo '$(srcdir)/'`k524_err.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libkrb5_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libkrb5_la-k524_err.lo `test -f 'k524_err.c' || echo '$(srcdir)/'`k524_err.c
librfc3961_la-crc.lo: crc.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crc.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crc.Tpo -c -o librfc3961_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crc.Tpo $(DEPDIR)/librfc3961_la-crc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crc.c' object='librfc3961_la-crc.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crc.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crc.Tpo -c -o librfc3961_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crc.Tpo $(DEPDIR)/librfc3961_la-crc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crc.c' object='librfc3961_la-crc.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crc.lo `test -f 'crc.c' || echo '$(srcdir)/'`crc.c
librfc3961_la-crypto.lo: crypto.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto.Tpo -c -o librfc3961_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto.Tpo $(DEPDIR)/librfc3961_la-crypto.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto.c' object='librfc3961_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto.Tpo -c -o librfc3961_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto.Tpo $(DEPDIR)/librfc3961_la-crypto.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto.c' object='librfc3961_la-crypto.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto.lo `test -f 'crypto.c' || echo '$(srcdir)/'`crypto.c
-librfc3961_la-crypto-aes.lo: crypto-aes.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-aes.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-aes.Tpo -c -o librfc3961_la-crypto-aes.lo `test -f 'crypto-aes.c' || echo '$(srcdir)/'`crypto-aes.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-aes.Tpo $(DEPDIR)/librfc3961_la-crypto-aes.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-aes.c' object='librfc3961_la-crypto-aes.lo' libtool=yes @AMDEPBACKSLASH@
+librfc3961_la-crypto-aes-sha1.lo: crypto-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-aes-sha1.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-aes-sha1.Tpo -c -o librfc3961_la-crypto-aes-sha1.lo `test -f 'crypto-aes-sha1.c' || echo '$(srcdir)/'`crypto-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-aes-sha1.Tpo $(DEPDIR)/librfc3961_la-crypto-aes-sha1.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-aes-sha1.c' object='librfc3961_la-crypto-aes-sha1.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-aes.lo `test -f 'crypto-aes.c' || echo '$(srcdir)/'`crypto-aes.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-aes-sha1.lo `test -f 'crypto-aes-sha1.c' || echo '$(srcdir)/'`crypto-aes-sha1.c
+
+librfc3961_la-crypto-aes-sha2.lo: crypto-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-aes-sha2.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-aes-sha2.Tpo -c -o librfc3961_la-crypto-aes-sha2.lo `test -f 'crypto-aes-sha2.c' || echo '$(srcdir)/'`crypto-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-aes-sha2.Tpo $(DEPDIR)/librfc3961_la-crypto-aes-sha2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-aes-sha2.c' object='librfc3961_la-crypto-aes-sha2.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-aes-sha2.lo `test -f 'crypto-aes-sha2.c' || echo '$(srcdir)/'`crypto-aes-sha2.c
librfc3961_la-crypto-algs.lo: crypto-algs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-algs.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-algs.Tpo -c -o librfc3961_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-algs.Tpo $(DEPDIR)/librfc3961_la-crypto-algs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-algs.c' object='librfc3961_la-crypto-algs.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-algs.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-algs.Tpo -c -o librfc3961_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-algs.Tpo $(DEPDIR)/librfc3961_la-crypto-algs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-algs.c' object='librfc3961_la-crypto-algs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-algs.lo `test -f 'crypto-algs.c' || echo '$(srcdir)/'`crypto-algs.c
librfc3961_la-crypto-arcfour.lo: crypto-arcfour.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-arcfour.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-arcfour.Tpo -c -o librfc3961_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-arcfour.Tpo $(DEPDIR)/librfc3961_la-crypto-arcfour.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-arcfour.c' object='librfc3961_la-crypto-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-arcfour.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-arcfour.Tpo -c -o librfc3961_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-arcfour.Tpo $(DEPDIR)/librfc3961_la-crypto-arcfour.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-arcfour.c' object='librfc3961_la-crypto-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-arcfour.lo `test -f 'crypto-arcfour.c' || echo '$(srcdir)/'`crypto-arcfour.c
librfc3961_la-crypto-des.lo: crypto-des.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-des.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-des.Tpo -c -o librfc3961_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-des.Tpo $(DEPDIR)/librfc3961_la-crypto-des.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-des.c' object='librfc3961_la-crypto-des.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-des.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-des.Tpo -c -o librfc3961_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-des.Tpo $(DEPDIR)/librfc3961_la-crypto-des.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-des.c' object='librfc3961_la-crypto-des.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-des.lo `test -f 'crypto-des.c' || echo '$(srcdir)/'`crypto-des.c
librfc3961_la-crypto-des-common.lo: crypto-des-common.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-des-common.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-des-common.Tpo -c -o librfc3961_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-des-common.Tpo $(DEPDIR)/librfc3961_la-crypto-des-common.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-des-common.c' object='librfc3961_la-crypto-des-common.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-des-common.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-des-common.Tpo -c -o librfc3961_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-des-common.Tpo $(DEPDIR)/librfc3961_la-crypto-des-common.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-des-common.c' object='librfc3961_la-crypto-des-common.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-des-common.lo `test -f 'crypto-des-common.c' || echo '$(srcdir)/'`crypto-des-common.c
librfc3961_la-crypto-des3.lo: crypto-des3.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-des3.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-des3.Tpo -c -o librfc3961_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-des3.Tpo $(DEPDIR)/librfc3961_la-crypto-des3.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-des3.c' object='librfc3961_la-crypto-des3.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-des3.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-des3.Tpo -c -o librfc3961_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-des3.Tpo $(DEPDIR)/librfc3961_la-crypto-des3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-des3.c' object='librfc3961_la-crypto-des3.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-des3.lo `test -f 'crypto-des3.c' || echo '$(srcdir)/'`crypto-des3.c
librfc3961_la-crypto-evp.lo: crypto-evp.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-evp.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-evp.Tpo -c -o librfc3961_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-evp.Tpo $(DEPDIR)/librfc3961_la-crypto-evp.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-evp.c' object='librfc3961_la-crypto-evp.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-evp.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-evp.Tpo -c -o librfc3961_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-evp.Tpo $(DEPDIR)/librfc3961_la-crypto-evp.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-evp.c' object='librfc3961_la-crypto-evp.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-evp.lo `test -f 'crypto-evp.c' || echo '$(srcdir)/'`crypto-evp.c
librfc3961_la-crypto-null.lo: crypto-null.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-null.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-null.Tpo -c -o librfc3961_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-null.Tpo $(DEPDIR)/librfc3961_la-crypto-null.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-null.c' object='librfc3961_la-crypto-null.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-null.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-null.Tpo -c -o librfc3961_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-null.Tpo $(DEPDIR)/librfc3961_la-crypto-null.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-null.c' object='librfc3961_la-crypto-null.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-null.lo `test -f 'crypto-null.c' || echo '$(srcdir)/'`crypto-null.c
librfc3961_la-crypto-pk.lo: crypto-pk.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-pk.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-pk.Tpo -c -o librfc3961_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-pk.Tpo $(DEPDIR)/librfc3961_la-crypto-pk.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-pk.c' object='librfc3961_la-crypto-pk.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-pk.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-pk.Tpo -c -o librfc3961_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-pk.Tpo $(DEPDIR)/librfc3961_la-crypto-pk.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-pk.c' object='librfc3961_la-crypto-pk.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-pk.lo `test -f 'crypto-pk.c' || echo '$(srcdir)/'`crypto-pk.c
librfc3961_la-crypto-rand.lo: crypto-rand.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-rand.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-rand.Tpo -c -o librfc3961_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-rand.Tpo $(DEPDIR)/librfc3961_la-crypto-rand.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-rand.c' object='librfc3961_la-crypto-rand.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-rand.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-rand.Tpo -c -o librfc3961_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-rand.Tpo $(DEPDIR)/librfc3961_la-crypto-rand.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-rand.c' object='librfc3961_la-crypto-rand.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-rand.lo `test -f 'crypto-rand.c' || echo '$(srcdir)/'`crypto-rand.c
librfc3961_la-crypto-stubs.lo: crypto-stubs.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-stubs.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-stubs.Tpo -c -o librfc3961_la-crypto-stubs.lo `test -f 'crypto-stubs.c' || echo '$(srcdir)/'`crypto-stubs.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-crypto-stubs.Tpo $(DEPDIR)/librfc3961_la-crypto-stubs.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='crypto-stubs.c' object='librfc3961_la-crypto-stubs.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-crypto-stubs.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-crypto-stubs.Tpo -c -o librfc3961_la-crypto-stubs.lo `test -f 'crypto-stubs.c' || echo '$(srcdir)/'`crypto-stubs.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-crypto-stubs.Tpo $(DEPDIR)/librfc3961_la-crypto-stubs.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='crypto-stubs.c' object='librfc3961_la-crypto-stubs.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-stubs.lo `test -f 'crypto-stubs.c' || echo '$(srcdir)/'`crypto-stubs.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-crypto-stubs.lo `test -f 'crypto-stubs.c' || echo '$(srcdir)/'`crypto-stubs.c
librfc3961_la-data.lo: data.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-data.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-data.Tpo -c -o librfc3961_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-data.Tpo $(DEPDIR)/librfc3961_la-data.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='data.c' object='librfc3961_la-data.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-data.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-data.Tpo -c -o librfc3961_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-data.Tpo $(DEPDIR)/librfc3961_la-data.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='data.c' object='librfc3961_la-data.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-data.lo `test -f 'data.c' || echo '$(srcdir)/'`data.c
+
+librfc3961_la-enomem.lo: enomem.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-enomem.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-enomem.Tpo -c -o librfc3961_la-enomem.lo `test -f 'enomem.c' || echo '$(srcdir)/'`enomem.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-enomem.Tpo $(DEPDIR)/librfc3961_la-enomem.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='enomem.c' object='librfc3961_la-enomem.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-enomem.lo `test -f 'enomem.c' || echo '$(srcdir)/'`enomem.c
librfc3961_la-error_string.lo: error_string.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-error_string.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-error_string.Tpo -c -o librfc3961_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-error_string.Tpo $(DEPDIR)/librfc3961_la-error_string.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='error_string.c' object='librfc3961_la-error_string.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-error_string.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-error_string.Tpo -c -o librfc3961_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-error_string.Tpo $(DEPDIR)/librfc3961_la-error_string.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='error_string.c' object='librfc3961_la-error_string.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-error_string.lo `test -f 'error_string.c' || echo '$(srcdir)/'`error_string.c
librfc3961_la-keyblock.lo: keyblock.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-keyblock.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-keyblock.Tpo -c -o librfc3961_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-keyblock.Tpo $(DEPDIR)/librfc3961_la-keyblock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='keyblock.c' object='librfc3961_la-keyblock.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-keyblock.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-keyblock.Tpo -c -o librfc3961_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-keyblock.Tpo $(DEPDIR)/librfc3961_la-keyblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='keyblock.c' object='librfc3961_la-keyblock.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-keyblock.lo `test -f 'keyblock.c' || echo '$(srcdir)/'`keyblock.c
librfc3961_la-n-fold.lo: n-fold.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-n-fold.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-n-fold.Tpo -c -o librfc3961_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-n-fold.Tpo $(DEPDIR)/librfc3961_la-n-fold.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='n-fold.c' object='librfc3961_la-n-fold.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-n-fold.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-n-fold.Tpo -c -o librfc3961_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-n-fold.Tpo $(DEPDIR)/librfc3961_la-n-fold.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='n-fold.c' object='librfc3961_la-n-fold.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-n-fold.lo `test -f 'n-fold.c' || echo '$(srcdir)/'`n-fold.c
librfc3961_la-salt.lo: salt.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt.Tpo -c -o librfc3961_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-salt.Tpo $(DEPDIR)/librfc3961_la-salt.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt.c' object='librfc3961_la-salt.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt.Tpo -c -o librfc3961_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-salt.Tpo $(DEPDIR)/librfc3961_la-salt.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt.c' object='librfc3961_la-salt.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
+
+librfc3961_la-salt-aes-sha1.lo: salt-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-aes-sha1.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-aes-sha1.Tpo -c -o librfc3961_la-salt-aes-sha1.lo `test -f 'salt-aes-sha1.c' || echo '$(srcdir)/'`salt-aes-sha1.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-salt-aes-sha1.Tpo $(DEPDIR)/librfc3961_la-salt-aes-sha1.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-aes-sha1.c' object='librfc3961_la-salt-aes-sha1.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt.lo `test -f 'salt.c' || echo '$(srcdir)/'`salt.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-aes-sha1.lo `test -f 'salt-aes-sha1.c' || echo '$(srcdir)/'`salt-aes-sha1.c
-librfc3961_la-salt-aes.lo: salt-aes.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-aes.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-aes.Tpo -c -o librfc3961_la-salt-aes.lo `test -f 'salt-aes.c' || echo '$(srcdir)/'`salt-aes.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-salt-aes.Tpo $(DEPDIR)/librfc3961_la-salt-aes.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-aes.c' object='librfc3961_la-salt-aes.lo' libtool=yes @AMDEPBACKSLASH@
+librfc3961_la-salt-aes-sha2.lo: salt-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-aes-sha2.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-aes-sha2.Tpo -c -o librfc3961_la-salt-aes-sha2.lo `test -f 'salt-aes-sha2.c' || echo '$(srcdir)/'`salt-aes-sha2.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-salt-aes-sha2.Tpo $(DEPDIR)/librfc3961_la-salt-aes-sha2.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-aes-sha2.c' object='librfc3961_la-salt-aes-sha2.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-aes.lo `test -f 'salt-aes.c' || echo '$(srcdir)/'`salt-aes.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-aes-sha2.lo `test -f 'salt-aes-sha2.c' || echo '$(srcdir)/'`salt-aes-sha2.c
librfc3961_la-salt-arcfour.lo: salt-arcfour.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-arcfour.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-arcfour.Tpo -c -o librfc3961_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-salt-arcfour.Tpo $(DEPDIR)/librfc3961_la-salt-arcfour.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-arcfour.c' object='librfc3961_la-salt-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-arcfour.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-arcfour.Tpo -c -o librfc3961_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-salt-arcfour.Tpo $(DEPDIR)/librfc3961_la-salt-arcfour.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-arcfour.c' object='librfc3961_la-salt-arcfour.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-arcfour.lo `test -f 'salt-arcfour.c' || echo '$(srcdir)/'`salt-arcfour.c
librfc3961_la-salt-des.lo: salt-des.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-des.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-des.Tpo -c -o librfc3961_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-salt-des.Tpo $(DEPDIR)/librfc3961_la-salt-des.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-des.c' object='librfc3961_la-salt-des.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-des.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-des.Tpo -c -o librfc3961_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-salt-des.Tpo $(DEPDIR)/librfc3961_la-salt-des.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-des.c' object='librfc3961_la-salt-des.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-des.lo `test -f 'salt-des.c' || echo '$(srcdir)/'`salt-des.c
librfc3961_la-salt-des3.lo: salt-des3.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-des3.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-des3.Tpo -c -o librfc3961_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-salt-des3.Tpo $(DEPDIR)/librfc3961_la-salt-des3.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='salt-des3.c' object='librfc3961_la-salt-des3.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-salt-des3.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-salt-des3.Tpo -c -o librfc3961_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-salt-des3.Tpo $(DEPDIR)/librfc3961_la-salt-des3.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='salt-des3.c' object='librfc3961_la-salt-des3.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-salt-des3.lo `test -f 'salt-des3.c' || echo '$(srcdir)/'`salt-des3.c
+
+librfc3961_la-sp800-108-kdf.lo: sp800-108-kdf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-sp800-108-kdf.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-sp800-108-kdf.Tpo -c -o librfc3961_la-sp800-108-kdf.lo `test -f 'sp800-108-kdf.c' || echo '$(srcdir)/'`sp800-108-kdf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-sp800-108-kdf.Tpo $(DEPDIR)/librfc3961_la-sp800-108-kdf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='sp800-108-kdf.c' object='librfc3961_la-sp800-108-kdf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-sp800-108-kdf.lo `test -f 'sp800-108-kdf.c' || echo '$(srcdir)/'`sp800-108-kdf.c
librfc3961_la-store-int.lo: store-int.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-store-int.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-store-int.Tpo -c -o librfc3961_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-store-int.Tpo $(DEPDIR)/librfc3961_la-store-int.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='store-int.c' object='librfc3961_la-store-int.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-store-int.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-store-int.Tpo -c -o librfc3961_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-store-int.Tpo $(DEPDIR)/librfc3961_la-store-int.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='store-int.c' object='librfc3961_la-store-int.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-store-int.lo `test -f 'store-int.c' || echo '$(srcdir)/'`store-int.c
librfc3961_la-warn.lo: warn.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-warn.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-warn.Tpo -c -o librfc3961_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/librfc3961_la-warn.Tpo $(DEPDIR)/librfc3961_la-warn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='warn.c' object='librfc3961_la-warn.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT librfc3961_la-warn.lo -MD -MP -MF $(DEPDIR)/librfc3961_la-warn.Tpo -c -o librfc3961_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/librfc3961_la-warn.Tpo $(DEPDIR)/librfc3961_la-warn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warn.c' object='librfc3961_la-warn.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(librfc3961_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o librfc3961_la-warn.lo `test -f 'warn.c' || echo '$(srcdir)/'`warn.c
mostlyclean-libtool:
-rm -f *.lo
@@ -2506,11 +3076,18 @@ clean-libtool:
-rm -rf .libs _libs
install-man3: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man3dir)" || $(MKDIR_P) "$(DESTDIR)$(man3dir)"
- @list=''; test -n "$(man3dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.3[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.3[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -2539,16 +3116,21 @@ uninstall-man3:
sed -n '/\.3[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man3dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man3dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
install-man5: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man5dir)" || $(MKDIR_P) "$(DESTDIR)$(man5dir)"
- @list=''; test -n "$(man5dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.5[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.5[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -2577,16 +3159,64 @@ uninstall-man5:
sed -n '/\.5[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man5dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man5dir)" && rm -f $$files; }
-install-man8: $(man_MANS)
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
+install-man7: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man8dir)" || $(MKDIR_P) "$(DESTDIR)$(man8dir)"
- @list=''; test -n "$(man8dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man7dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man7dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man7dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.7[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man7dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man7dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man7dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man7dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man7:
+ @$(NORMAL_UNINSTALL)
+ @list=''; test -n "$(man7dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.8[a-z]*$$/p'; \
+ sed -n '/\.7[a-z]*$$/p'; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^7][0-9a-z]*$$,7,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man7dir)'; $(am__uninstall_files_from_dir)
+install-man8: $(man_MANS)
+ @$(NORMAL_INSTALL)
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -2615,13 +3245,14 @@ uninstall-man8:
sed -n '/\.8[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man8dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man8dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
install-dist_includeHEADERS: $(dist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -2635,13 +3266,14 @@ uninstall-dist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-krb5HEADERS: $(krb5_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(krb5dir)" || $(MKDIR_P) "$(DESTDIR)$(krb5dir)"
@list='$(krb5_HEADERS)'; test -n "$(krb5dir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(krb5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(krb5dir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -2655,13 +3287,14 @@ uninstall-krb5HEADERS:
@$(NORMAL_UNINSTALL)
@list='$(krb5_HEADERS)'; test -n "$(krb5dir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(krb5dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(krb5dir)" && rm -f $$files
+ dir='$(DESTDIR)$(krb5dir)'; $(am__uninstall_files_from_dir)
install-nodist_includeHEADERS: $(nodist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -2675,30 +3308,17 @@ uninstall-nodist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -2710,15 +3330,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -2727,116 +3343,357 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS) $(check_DATA)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+aes-test.log: aes-test$(EXEEXT)
+ @p='aes-test$(EXEEXT)'; \
+ b='aes-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+derived-key-test.log: derived-key-test$(EXEEXT)
+ @p='derived-key-test$(EXEEXT)'; \
+ b='derived-key-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+n-fold-test.log: n-fold-test$(EXEEXT)
+ @p='n-fold-test$(EXEEXT)'; \
+ b='n-fold-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+parse-name-test.log: parse-name-test$(EXEEXT)
+ @p='parse-name-test$(EXEEXT)'; \
+ b='parse-name-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+pseudo-random-test.log: pseudo-random-test$(EXEEXT)
+ @p='pseudo-random-test$(EXEEXT)'; \
+ b='pseudo-random-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+store-test.log: store-test$(EXEEXT)
+ @p='store-test$(EXEEXT)'; \
+ b='store-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+string-to-key-test.log: string-to-key-test$(EXEEXT)
+ @p='string-to-key-test$(EXEEXT)'; \
+ b='string-to-key-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_acl.log: test_acl$(EXEEXT)
+ @p='test_acl$(EXEEXT)'; \
+ b='test_acl'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_addr.log: test_addr$(EXEEXT)
+ @p='test_addr$(EXEEXT)'; \
+ b='test_addr'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_cc.log: test_cc$(EXEEXT)
+ @p='test_cc$(EXEEXT)'; \
+ b='test_cc'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_config.log: test_config$(EXEEXT)
+ @p='test_config$(EXEEXT)'; \
+ b='test_config'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_fx.log: test_fx$(EXEEXT)
+ @p='test_fx$(EXEEXT)'; \
+ b='test_fx'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_prf.log: test_prf$(EXEEXT)
+ @p='test_prf$(EXEEXT)'; \
+ b='test_prf'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_store.log: test_store$(EXEEXT)
+ @p='test_store$(EXEEXT)'; \
+ b='test_store'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_crypto_wrapping.log: test_crypto_wrapping$(EXEEXT)
+ @p='test_crypto_wrapping$(EXEEXT)'; \
+ b='test_crypto_wrapping'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_keytab.log: test_keytab$(EXEEXT)
+ @p='test_keytab$(EXEEXT)'; \
+ b='test_keytab'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_mem.log: test_mem$(EXEEXT)
+ @p='test_mem$(EXEEXT)'; \
+ b='test_mem'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_pac.log: test_pac$(EXEEXT)
+ @p='test_pac$(EXEEXT)'; \
+ b='test_pac'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_plugin.log: test_plugin$(EXEEXT)
+ @p='test_plugin$(EXEEXT)'; \
+ b='test_plugin'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_princ.log: test_princ$(EXEEXT)
+ @p='test_princ$(EXEEXT)'; \
+ b='test_princ'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_pkinit_dh2key.log: test_pkinit_dh2key$(EXEEXT)
+ @p='test_pkinit_dh2key$(EXEEXT)'; \
+ b='test_pkinit_dh2key'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_pknistkdf.log: test_pknistkdf$(EXEEXT)
+ @p='test_pknistkdf$(EXEEXT)'; \
+ b='test_pknistkdf'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_time.log: test_time$(EXEEXT)
+ @p='test_time$(EXEEXT)'; \
+ b='test_time'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_expand_toks.log: test_expand_toks$(EXEEXT)
+ @p='test_expand_toks$(EXEEXT)'; \
+ b='test_expand_toks'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test_x500.log: test_x500$(EXEEXT)
+ @p='test_x500$(EXEEXT)'; \
+ b='test_x500'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
- @list='$(MANS)'; if test -n "$$list"; then \
- list=`for p in $$list; do \
- if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
- if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
- if test -n "$$list" && \
- grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
- echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
- grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
- echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
- echo " typically \`make maintainer-clean' will remove them" >&2; \
- exit 1; \
- else :; fi; \
- else :; fi
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -2878,7 +3735,7 @@ all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(MANS) $(HEADERS) \
install-binPROGRAMS: install-libLTLIBRARIES
installdirs:
- for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(includedir)" "$(DESTDIR)$(krb5dir)" "$(DESTDIR)$(includedir)"; do \
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man3dir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man7dir)" "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(includedir)" "$(DESTDIR)$(krb5dir)" "$(DESTDIR)$(includedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
@@ -2891,11 +3748,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -2939,9 +3804,9 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-binPROGRAMS install-exec-local \
+ install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -2950,7 +3815,7 @@ install-info: install-info-am
install-info-am:
-install-man: install-man3 install-man5 install-man8
+install-man: install-man3 install-man5 install-man7 install-man8
install-pdf: install-pdf-am
@@ -2985,50 +3850,64 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_includeHEADERS \
uninstall-nodist_includeHEADERS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-uninstall-man: uninstall-man3 uninstall-man5 uninstall-man8
-
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-binPROGRAMS clean-checkPROGRAMS \
- clean-generic clean-libLTLIBRARIES clean-libtool \
- clean-noinstLTLIBRARIES clean-noinstPROGRAMS ctags dist-hook \
- distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-binPROGRAMS \
- install-data install-data-am install-data-hook \
- install-dist_includeHEADERS install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
- install-html-am install-info install-info-am \
- install-krb5HEADERS install-libLTLIBRARIES install-man \
- install-man3 install-man5 install-man8 \
- install-nodist_includeHEADERS install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
+uninstall-man: uninstall-man3 uninstall-man5 uninstall-man7 \
+ uninstall-man8
+
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-binPROGRAMS \
+ clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool clean-noinstLTLIBRARIES clean-noinstPROGRAMS \
+ cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-data-hook install-dist_includeHEADERS \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-exec-local install-html install-html-am install-info \
+ install-info-am install-krb5HEADERS install-libLTLIBRARIES \
+ install-man install-man3 install-man5 install-man7 \
+ install-man8 install-nodist_includeHEADERS install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-binPROGRAMS \
- uninstall-dist_includeHEADERS uninstall-hook \
- uninstall-krb5HEADERS uninstall-libLTLIBRARIES uninstall-man \
- uninstall-man3 uninstall-man5 uninstall-man8 \
- uninstall-nodist_includeHEADERS
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-dist_includeHEADERS \
+ uninstall-hook uninstall-krb5HEADERS uninstall-libLTLIBRARIES \
+ uninstall-man uninstall-man3 uninstall-man5 uninstall-man7 \
+ uninstall-man8 uninstall-nodist_includeHEADERS
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
-install-exec-hook: install-suid-programs
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -3036,7 +3915,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -3093,6 +3972,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -3135,6 +4016,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -3148,13 +4042,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -3185,13 +4079,14 @@ distdir-in-tree: $(DISTFILES) $(INFO_DEPS)
fi ; \
done
-$(libkrb5_la_OBJECTS) $(verify_krb5_conf_OBJECTS) $(librfc3961_la_OBJECTS): $(srcdir)/krb5-protos.h $(srcdir)/krb5-private.h krb5_err.h heim_err.h k524_err.h krb5_err.h krb_err.h k524_err.h
+$(ALL_OBJECTS): $(srcdir)/krb5-protos.h $(srcdir)/krb5-private.h
+$(ALL_OBJECTS): krb5_err.h heim_err.h k524_err.h krb5_err.h krb_err.h k524_err.h
-$(srcdir)/krb5-protos.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -E KRB5_LIB -q -P comment -o krb5-protos.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-protos.h
+$(srcdir)/krb5-protos.h: $(headerdeps)
+ @cd $(srcdir) && perl ../../cf/make-proto.pl -E KRB5_LIB -q -P comment -o krb5-protos.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-protos.h
-$(srcdir)/krb5-private.h:
- cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p krb5-private.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-private.h
+$(srcdir)/krb5-private.h: $(headerdeps)
+ @cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -p krb5-private.h $(dist_libkrb5_la_SOURCES) || rm -f krb5-private.h
$(libkrb5_la_OBJECTS): krb5_err.h krb_err.h heim_err.h k524_err.h
diff --git a/lib/krb5/NTMakefile b/lib/krb5/NTMakefile
index 733121c80a9f..b0848716cd1a 100644
--- a/lib/krb5/NTMakefile
+++ b/lib/krb5/NTMakefile
@@ -1,6 +1,6 @@
########################################################################
#
-# Copyright (c) 2009, Secure Endpoints Inc.
+# Copyright (c) 2009 - 2016, Secure Endpoints Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -51,11 +51,13 @@ libkrb5_OBJS = \
$(OBJ)\config_reg.obj \
$(OBJ)\constants.obj \
$(OBJ)\context.obj \
+ $(OBJ)\convert_creds.obj \
$(OBJ)\copy_host_realm.obj \
$(OBJ)\crc.obj \
$(OBJ)\creds.obj \
$(OBJ)\crypto.obj \
- $(OBJ)\crypto-aes.obj \
+ $(OBJ)\crypto-aes-sha1.obj \
+ $(OBJ)\crypto-aes-sha2.obj \
$(OBJ)\crypto-algs.obj \
$(OBJ)\crypto-arcfour.obj \
$(OBJ)\crypto-des-common.obj \
@@ -66,13 +68,17 @@ libkrb5_OBJS = \
$(OBJ)\crypto-pk.obj \
$(OBJ)\crypto-rand.obj \
$(OBJ)\data.obj \
+ $(OBJ)\dcache.obj \
+ $(OBJ)\db_plugin.obj \
$(OBJ)\deprecated.obj \
$(OBJ)\digest.obj \
$(OBJ)\dll.obj \
$(OBJ)\eai_to_heim_errno.obj \
+ $(OBJ)\enomem.obj \
$(OBJ)\error_string.obj \
$(OBJ)\expand_hostname.obj \
$(OBJ)\expand_path.obj \
+ $(OBJ)\fast.obj \
$(OBJ)\fcache.obj \
$(OBJ)\free.obj \
$(OBJ)\free_host_realm.obj \
@@ -112,7 +118,10 @@ libkrb5_OBJS = \
$(OBJ)\n-fold.obj \
$(OBJ)\pac.obj \
$(OBJ)\padata.obj \
+ $(OBJ)\pcache.obj \
$(OBJ)\pkinit.obj \
+ $(OBJ)\pkinit-ec.obj \
+ $(OBJ)\plugin.obj \
$(OBJ)\principal.obj \
$(OBJ)\prog_setup.obj \
$(OBJ)\prompter_posix.obj \
@@ -125,7 +134,8 @@ libkrb5_OBJS = \
$(OBJ)\read_message.obj \
$(OBJ)\recvauth.obj \
$(OBJ)\replay.obj \
- $(OBJ)\salt-aes.obj \
+ $(OBJ)\salt-aes-sha1.obj \
+ $(OBJ)\salt-aes-sha2.obj \
$(OBJ)\salt-arcfour.obj \
$(OBJ)\salt-des.obj \
$(OBJ)\salt-des3.obj \
@@ -135,13 +145,13 @@ libkrb5_OBJS = \
$(OBJ)\sendauth.obj \
$(OBJ)\set_default_realm.obj \
$(OBJ)\sock_principal.obj \
+ $(OBJ)\sp800-108-kdf.obj \
$(OBJ)\store.obj \
$(OBJ)\store-int.obj \
$(OBJ)\store_emem.obj \
$(OBJ)\store_fd.obj \
$(OBJ)\store_mem.obj \
- $(OBJ)\pcache.obj \
- $(OBJ)\plugin.obj \
+ $(OBJ)\store_sock.obj \
$(OBJ)\ticket.obj \
$(OBJ)\time.obj \
$(OBJ)\transited.obj \
@@ -199,7 +209,8 @@ dist_libkrb5_la_SOURCES = \
creds.c \
crypto.c \
crypto.h \
- crypto-aes.c \
+ crypto-aes-sha1.c \
+ crypto-aes-sha2.c \
crypto-algs.c \
crypto-arcfour.c \
crypto-des.c \
@@ -208,14 +219,18 @@ dist_libkrb5_la_SOURCES = \
crypto-evp.c \
crypto-pk.c \
crypto-rand.c \
+ db_plugin.c \
doxygen.c \
data.c \
+ dcache.c \
deprecated.c \
digest.c \
eai_to_heim_errno.c \
+ enomem.c \
error_string.c \
expand_hostname.c \
expand_path.c \
+ fast.c \
fcache.c \
free.c \
free_host_realm.c \
@@ -259,6 +274,8 @@ dist_libkrb5_la_SOURCES = \
pac.c \
padata.c \
pkinit.c \
+ pkinit-ec.c \
+ plugin.c \
principal.c \
prog_setup.c \
prompter_posix.c \
@@ -272,7 +289,8 @@ dist_libkrb5_la_SOURCES = \
recvauth.c \
replay.c \
salt.c \
- salt-aes.c \
+ salt-aes-sha1.c \
+ salt-aes-sha2.c \
salt-arcfour.c \
salt-des.c \
salt-des3.c \
@@ -281,12 +299,14 @@ dist_libkrb5_la_SOURCES = \
sendauth.c \
set_default_realm.c \
sock_principal.c \
+ sp800-108-kdf.c \
store.c \
store-int.c \
store-int.h \
store_emem.c \
store_fd.c \
store_mem.c \
+ store_sock.c \
pcache.c \
plugin.c \
ticket.c \
@@ -327,8 +347,13 @@ $(OBJ)\k524_err.c $(OBJ)\k524_err.h: k524_err.et
#----------------------------------------------------------------------
# libkrb5
-$(LIBKRB5): $(libkrb5_OBJS) $(libkrb5_gen_OBJS)
- $(LIBCON)
+$(LIBKRB5): $(libkrb5_OBJS) $(libkrb5_gen_OBJS)
+ $(LIBCON_C) -OUT:$@ $(LIBHEIMBASE) $(LIB_openssl_crypto) @<<
+$(libkrb5_OBJS: =
+)
+$(libkrb5_gen_OBJS: =
+)
+<<
all:: $(LIBKRB5)
@@ -349,7 +374,8 @@ clean::
librfc3961_OBJS=\
$(OBJ)\crc.obj \
$(OBJ)\crypto.obj \
- $(OBJ)\crypto-aes.obj \
+ $(OBJ)\crypto-aes-sha1.obj \
+ $(OBJ)\crypto-aes-sha2.obj \
$(OBJ)\crypto-algs.obj \
$(OBJ)\crypto-arcfour.obj \
$(OBJ)\crypto-des.obj \
@@ -365,10 +391,12 @@ librfc3961_OBJS=\
$(OBJ)\keyblock.obj \
$(OBJ)\n-fold.obj \
$(OBJ)\salt.obj \
- $(OBJ)\salt-aes.obj \
+ $(OBJ)\salt-aes-sha1.obj \
+ $(OBJ)\salt-aes-sha2.obj \
$(OBJ)\salt-arcfour.obj \
$(OBJ)\salt-des.obj \
$(OBJ)\salt-des3.obj \
+ $(OBJ)\sp800-108-kdf.obj \
$(OBJ)\store-int.obj \
$(OBJ)\warn.obj
@@ -409,6 +437,7 @@ test_binaries = \
$(OBJ)\krbhst-test.exe \
$(OBJ)\n-fold-test.exe \
$(OBJ)\parse-name-test.exe \
+ $(OBJ)\pseudo-random-test.exe \
$(OBJ)\store-test.exe \
$(OBJ)\string-to-key-test.exe \
$(OBJ)\test_acl.exe \
@@ -431,11 +460,10 @@ test_binaries = \
$(OBJ)\test_prf.exe \
$(OBJ)\test_princ.exe \
$(OBJ)\test_renew.exe \
- $(OBJ)\test_rfc3961.exe \
$(OBJ)\test_store.exe \
$(OBJ)\test_time.exe \
-test-binaries: $(test_binaries)
+test-binaries: $(test_binaries) $(OBJ)\test_rfc3961.exe
test-files: $(OBJ)\test_config_strings.out
@@ -444,35 +472,46 @@ $(OBJ)\test_config_strings.out: test_config_strings.cfg
test-run:
cd $(OBJ)
- aes-test.exe
- derived-key-test.exe
- n-fold-test.exe
- parse-name-test.exe
- store-test.exe
- string-to-key-test.exe
- test_acl.exe
- test_addr.exe
- test_cc.exe
- test_config.exe
- test_prf.exe
- test_store.exe
- test_crypto.exe
- test_crypto_wrapping.exe
- test_keytab.exe
- test_mem.exe
- test_pac.exe
- test_plugin.exe
- test_princ.exe
- test_pkinit_dh2key.exe
- test_pknistkdf.exe
- test_time.exe
+ -aes-test.exe
+ -derived-key-test.exe
+ -krbhst-test.exe
+ -n-fold-test.exe
+ -parse-name-test.exe
+ -pseudo-random-test.exe
+ -store-test.exe
+ -string-to-key-test.exe
+ -test_acl.exe
+ -test_addr.exe
+# Skip alname due to lack of .k5login and "root"
+# -test_alname.exe
+ -test_cc.exe
+ -test_config.exe
+ -test_crypto.exe
+ -test_crypto_wrapping.exe
+# Skip forward due to need for existing hostname
+# -test_forward.exe
+ -test_get_addrs.exe
+ -test_hostname.exe
+ -test_keytab.exe
+# Skip kuserok requires principal and localname
+# -test_kuserok.exe
+ -test_mem.exe
+ -test_pac.exe
+ -test_pkinit_dh2key.exe
+ -test_pknistkdf.exe
+ -test_plugin.exe
+ -test_prf.exe
+ -test_renew.exe
+ -test_rfc3961.exe
+ -test_store.exe
+ -test_time.exe
cd $(SRCDIR)
-$(test_binaries): $$(@R).obj $(LIBHEIMDAL) $(LIBVERS) $(LIBROKEN)
+$(test_binaries): $$(@R).obj $(LIBHEIMDAL) $(LIBVERS) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
-$(OBJ)\test_rfc3961.exe: $(OBJ)\test_rfc3961.obj $(LIBRFC3961) $(LIBHEIMDAL) $(LIBVERS) $(LIBROKEN)
+$(OBJ)\test_rfc3961.exe: $(OBJ)\test_rfc3961.obj $(LIBRFC3961) $(LIBHEIMDAL) $(LIBVERS) $(LIBCOMERR) $(LIBROKEN) $(LIBHEIMBASE)
$(EXECONLINK)
$(EXEPREP_NODIST)
diff --git a/lib/krb5/acache.c b/lib/krb5/acache.c
index 19eeecda429b..9d33df133c27 100644
--- a/lib/krb5/acache.c
+++ b/lib/krb5/acache.c
@@ -125,7 +125,7 @@ init_ccapi(krb5_context context)
#ifdef KRB5_USE_PATH_TOKENS
{
char * explib = NULL;
- if (_krb5_expand_path_tokens(context, lib, &explib) == 0) {
+ if (_krb5_expand_path_tokens(context, lib, 0, &explib) == 0) {
cc_handle = dlopen(explib, RTLD_LAZY|RTLD_LOCAL);
free(explib);
}
@@ -168,7 +168,7 @@ init_ccapi(krb5_context context)
#endif
}
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_heim_krb5_ipc_client_set_target_uid(uid_t uid)
{
init_ccapi(NULL);
@@ -176,7 +176,7 @@ _heim_krb5_ipc_client_set_target_uid(uid_t uid)
(*set_target_uid)(uid);
}
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_heim_krb5_ipc_client_clear_target(void)
{
init_ccapi(NULL);
@@ -303,8 +303,7 @@ make_cred_from_ccred(krb5_context context,
return 0;
nomem:
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", "malloc"));
+ ret = krb5_enomem(context);
fail:
krb5_free_cred_contents(context, cred);
@@ -905,10 +904,8 @@ acc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
return ret;
iter = calloc(1, sizeof(*iter));
- if (iter == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
- }
+ if (iter == NULL)
+ return krb5_enomem(context);
error = (*init_func)(&iter->context, ccapi_version_3, NULL, NULL);
if (error) {
@@ -1036,10 +1033,8 @@ acc_get_default_name(krb5_context context, char **str)
(*name->func->release)(name);
(*cc->func->release)(cc);
- if (error < 0 || *str == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (error < 0 || *str == NULL)
+ return krb5_enomem(context);
return 0;
}
diff --git a/lib/krb5/acl.c b/lib/krb5/acl.c
index c94aae361b8e..90c91e661c0d 100644
--- a/lib/krb5/acl.c
+++ b/lib/krb5/acl.c
@@ -81,10 +81,8 @@ acl_parse_format(krb5_context context,
for(p = format; *p != '\0'; p++) {
tmp = malloc(sizeof(*tmp));
if(tmp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
acl_free_list(acl, 0);
- return ENOMEM;
+ return krb5_enomem(context);
}
if(*p == 's') {
tmp->type = acl_string;
diff --git a/lib/krb5/addr_families.c b/lib/krb5/addr_families.c
index 5d321a7e917d..7ac0fa93f9d5 100644
--- a/lib/krb5/addr_families.c
+++ b/lib/krb5/addr_families.c
@@ -799,6 +799,7 @@ static struct addr_operations at[] = {
NULL,
NULL,
NULL,
+ NULL,
NULL
}
};
@@ -1160,6 +1161,7 @@ krb5_parse_address(krb5_context context,
{
int i, n;
struct addrinfo *ai, *a;
+ struct addrinfo hint;
int error;
int save_errno;
@@ -1171,18 +1173,18 @@ krb5_parse_address(krb5_context context,
krb5_address addr;
if((*at[i].parse_addr)(context, string, &addr) == 0) {
ALLOC_SEQ(addresses, 1);
- if (addresses->val == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (addresses->val == NULL)
+ return krb5_enomem(context);
addresses->val[0] = addr;
return 0;
}
}
}
- error = getaddrinfo (string, NULL, NULL, &ai);
+ /* if not parsed as numeric address, do a name lookup */
+ memset(&hint, 0, sizeof(hint));
+ hint.ai_family = AF_UNSPEC;
+ error = getaddrinfo (string, NULL, &hint, &ai);
if (error) {
krb5_error_code ret2;
save_errno = errno;
@@ -1198,15 +1200,13 @@ krb5_parse_address(krb5_context context,
ALLOC_SEQ(addresses, n);
if (addresses->val == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
freeaddrinfo(ai);
- return ENOMEM;
+ return krb5_enomem(context);
}
addresses->len = 0;
for (a = ai, i = 0; a != NULL; a = a->ai_next) {
- if (krb5_sockaddr2address (context, ai->ai_addr, &addresses->val[i]))
+ if (krb5_sockaddr2address (context, a->ai_addr, &addresses->val[i]))
continue;
if(krb5_address_search(context, &addresses->val[i], addresses)) {
krb5_free_address(context, &addresses->val[i]);
@@ -1411,7 +1411,7 @@ krb5_copy_addresses(krb5_context context,
size_t i;
ALLOC_SEQ(outaddr, inaddr->len);
if(inaddr->len > 0 && outaddr->val == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
for(i = 0; i < inaddr->len; i++)
krb5_copy_address(context, &inaddr->val[i], &outaddr->val[i]);
return 0;
@@ -1440,11 +1440,8 @@ krb5_append_addresses(krb5_context context,
size_t i;
if(source->len > 0) {
tmp = realloc(dest->val, (dest->len + source->len) * sizeof(*tmp));
- if(tmp == NULL) {
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (tmp == NULL)
+ return krb5_enomem(context);
dest->val = tmp;
for(i = 0; i < source->len; i++) {
/* skip duplicates */
@@ -1483,19 +1480,14 @@ krb5_make_addrport (krb5_context context,
u_char *p;
*res = malloc (sizeof(**res));
- if (*res == NULL) {
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*res == NULL)
+ return krb5_enomem(context);
(*res)->addr_type = KRB5_ADDRESS_ADDRPORT;
ret = krb5_data_alloc (&(*res)->address, len);
if (ret) {
- krb5_set_error_message (context, ret,
- N_("malloc: out of memory", ""));
free (*res);
*res = NULL;
- return ret;
+ return krb5_enomem(context);
}
p = (*res)->address.data;
*p++ = 0;
diff --git a/lib/krb5/aes-test.c b/lib/krb5/aes-test.c
index 19b0ddd07502..7bca78ab6068 100644
--- a/lib/krb5/aes-test.c
+++ b/lib/krb5/aes-test.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * Copyright (c) 2003-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -35,10 +35,6 @@
#include <err.h>
#include <assert.h>
-#ifdef HAVE_OPENSSL
-#include <openssl/evp.h>
-#endif
-
static int verbose = 0;
static void
@@ -62,6 +58,29 @@ struct {
char *key;
} keys[] = {
{
+ "password",
+ "\x10\xDF\x9D\xD7\x83\xE5\xBC\x8A\xCE\xA1\x73\x0E\x74\x35\x5F\x61"
+ "ATHENA.MIT.EDUraeburn",
+ 37,
+ 32768,
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ 16,
+ NULL,
+ "\x08\x9B\xCA\x48\xB1\x05\xEA\x6E\xA7\x7C\xA5\xD2\xF3\x9D\xC5\xE7"
+ },
+ {
+ "password",
+ "\x10\xDF\x9D\xD7\x83\xE5\xBC\x8A\xCE\xA1\x73\x0E\x74\x35\x5F\x61"
+ "ATHENA.MIT.EDUraeburn",
+ 37,
+ 32768,
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ 32,
+ NULL,
+ "\x45\xBD\x80\x6D\xBF\x6A\x83\x3A\x9C\xFF\xC1\xC9\x45\x89\xA2\x22"
+ "\x36\x7A\x79\xBC\x21\xC4\x13\x71\x89\x06\xE9\xF5\x78\xA7\x84\x67"
+ },
+ {
"password", "ATHENA.MIT.EDUraeburn", -1,
1,
ETYPE_AES128_CTS_HMAC_SHA1_96, 16,
@@ -160,7 +179,6 @@ struct {
"\x1a\x8b\x4d\x28\x26\x01\xdb\x3b\x36\xbe\x92\x46\x91\x5e\xc8\x2a",
"\xd7\x8c\x5c\x9c\xb8\x72\xa8\xc9\xda\xd4\x69\x7f\x0b\xb5\xb2\xd2"
"\x14\x96\xc8\x2b\xeb\x2c\xae\xda\x21\x12\xfc\xee\xa0\x57\x40\x1b"
-
},
{
"\xf0\x9d\x84\x9e" /* g-clef */, "EXAMPLE.COMpianist", -1,
@@ -225,14 +243,15 @@ string_to_key_test(krb5_context context)
if (keys[i].keylen > sizeof(keyout))
abort();
- PKCS5_PBKDF2_HMAC_SHA1(password.data, password.length,
- salt.saltvalue.data, salt.saltvalue.length,
- keys[i].iterations,
- keys[i].keylen, keyout);
+ PKCS5_PBKDF2_HMAC(password.data, password.length,
+ salt.saltvalue.data, salt.saltvalue.length,
+ keys[i].iterations, EVP_sha1(),
+ keys[i].keylen, keyout);
if (memcmp(keyout, keys[i].pbkdf2, keys[i].keylen) != 0) {
krb5_warnx(context, "%d: pbkdf2", i);
val = 1;
+ hex_dump_data(keyout, keys[i].keylen);
continue;
}
@@ -269,6 +288,8 @@ string_to_key_test(krb5_context context)
if (memcmp(key.keyvalue.data, keys[i].key, keys[i].keylen) != 0) {
krb5_warnx(context, "%d: key wrong", i);
val = 1;
+ hex_dump_data(key.keyvalue.data, key.keyvalue.length);
+ hex_dump_data(keys[i].key, keys[i].keylen);
continue;
}
@@ -478,9 +499,10 @@ static int
krb_checksum_iov(krb5_context context,
krb5_crypto crypto,
unsigned usage,
- krb5_data *plain)
+ krb5_data *plain,
+ krb5_data *verify)
{
- krb5_crypto_iov iov[4];
+ krb5_crypto_iov iov[3];
int ret;
char *p;
size_t len;
@@ -489,8 +511,12 @@ krb_checksum_iov(krb5_context context,
len = plain->length;
iov[0].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
- krb5_crypto_length(context, crypto, iov[0].flags, &iov[0].data.length);
- iov[0].data.data = emalloc(iov[0].data.length);
+ if (verify) {
+ iov[0].data = *verify;
+ } else {
+ krb5_crypto_length(context, crypto, iov[0].flags, &iov[0].data.length);
+ iov[0].data.data = emalloc(iov[0].data.length);
+ }
iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
iov[1].data.length = len;
@@ -500,16 +526,19 @@ krb_checksum_iov(krb5_context context,
krb5_crypto_length(context, crypto, iov[0].flags, &iov[2].data.length);
iov[2].data.data = malloc(iov[2].data.length);
- ret = krb5_create_checksum_iov(context, crypto, usage,
- iov, sizeof(iov)/sizeof(iov[0]), NULL);
- if (ret)
- krb5_err(context, 1, ret, "krb5_create_checksum_iov failed");
+ if (verify == NULL) {
+ ret = krb5_create_checksum_iov(context, crypto, usage,
+ iov, sizeof(iov)/sizeof(iov[0]), NULL);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_create_checksum_iov failed");
+ }
ret = krb5_verify_checksum_iov(context, crypto, usage, iov, sizeof(iov)/sizeof(iov[0]), NULL);
if (ret)
krb5_err(context, 1, ret, "krb5_verify_checksum_iov");
- free(iov[0].data.data);
+ if (verify == NULL)
+ free(iov[0].data.data);
free(iov[2].data.data);
return 0;
@@ -559,7 +588,6 @@ krb_enc_mit(krb5_context context,
return 0;
}
-
struct {
krb5_enctype enctype;
unsigned usage;
@@ -569,6 +597,8 @@ struct {
void* edata;
size_t plen;
void *pdata;
+ size_t clen; /* checksum length */
+ void *cdata; /* checksum data */
} krbencs[] = {
{
ETYPE_AES256_CTS_HMAC_SHA1_96,
@@ -581,11 +611,133 @@ struct {
"\xa9\xec\x1c\x5c\x21\xfb\x6e\xef\x1a\x7a\xc8\xc1\xcc\x5a\x95\x24"
"\x6f\x9f\xf4\xd5\xbe\x5d\x59\x97\x44\xd8\x47\xcd",
16,
- "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x2e\x0a"
+ "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x2e\x0a",
+ 0,
+ NULL
+ },
+ {
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ 2,
+ 16,
+ "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C",
+ 32,
+ "\xEF\x85\xFB\x89\x0B\xB8\x47\x2F\x4D\xAB\x20\x39\x4D\xCA\x78\x1D"
+ "\xAD\x87\x7E\xDA\x39\xD5\x0C\x87\x0C\x0D\x5A\x0A\x8E\x48\xC7\x18",
+ 0,
+ "",
+ 0,
+ NULL
+ },
+ {
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ 2,
+ 16,
+ "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C",
+ 38,
+ "\x84\xD7\xF3\x07\x54\xED\x98\x7B\xAB\x0B\xF3\x50\x6B\xEB\x09\xCF"
+ "\xB5\x54\x02\xCE\xF7\xE6\x87\x7C\xE9\x9E\x24\x7E\x52\xD1\x6E\xD4"
+ "\x42\x1D\xFD\xF8\x97\x6C",
+ 6,
+ "\x00\x01\x02\x03\x04\x05",
+ 0,
+ NULL
+ },
+ {
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ 2,
+ 16,
+ "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C",
+ 48,
+ "\x35\x17\xD6\x40\xF5\x0D\xDC\x8A\xD3\x62\x87\x22\xB3\x56\x9D\x2A"
+ "\xE0\x74\x93\xFA\x82\x63\x25\x40\x80\xEA\x65\xC1\x00\x8E\x8F\xC2"
+ "\x95\xFB\x48\x52\xE7\xD8\x3E\x1E\x7C\x48\xC3\x7E\xEB\xE6\xB0\xD3",
+ 16,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ 0,
+ NULL
+ },
+ {
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ 2,
+ 16,
+ "\x37\x05\xD9\x60\x80\xC1\x77\x28\xA0\xE8\x00\xEA\xB6\xE0\xD2\x3C",
+ 53,
+ "\x72\x0F\x73\xB1\x8D\x98\x59\xCD\x6C\xCB\x43\x46\x11\x5C\xD3\x36"
+ "\xC7\x0F\x58\xED\xC0\xC4\x43\x7C\x55\x73\x54\x4C\x31\xC8\x13\xBC"
+ "\xE1\xE6\xD0\x72\xC1\x86\xB3\x9A\x41\x3C\x2F\x92\xCA\x9B\x83\x34"
+ "\xA2\x87\xFF\xCB\xFC",
+ 21,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13\x14",
+ 16,
+ "\xD7\x83\x67\x18\x66\x43\xD6\x7B\x41\x1C\xBA\x91\x39\xFC\x1D\xEE"
+ },
+ {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ 2,
+ 32,
+ "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98"
+ "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52",
+ 40,
+ "\x41\xF5\x3F\xA5\xBF\xE7\x02\x6D\x91\xFA\xF9\xBE\x95\x91\x95\xA0"
+ "\x58\x70\x72\x73\xA9\x6A\x40\xF0\xA0\x19\x60\x62\x1A\xC6\x12\x74"
+ "\x8B\x9B\xBF\xBE\x7E\xB4\xCE\x3C",
+ 0,
+ "",
+ 0,
+ NULL
+ },
+ {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ 2,
+ 32,
+ "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98"
+ "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52",
+ 46,
+ "\x4E\xD7\xB3\x7C\x2B\xCA\xC8\xF7\x4F\x23\xC1\xCF\x07\xE6\x2B\xC7"
+ "\xB7\x5F\xB3\xF6\x37\xB9\xF5\x59\xC7\xF6\x64\xF6\x9E\xAB\x7B\x60"
+ "\x92\x23\x75\x26\xEA\x0D\x1F\x61\xCB\x20\xD6\x9D\x10\xF2",
+ 6,
+ "\x00\x01\x02\x03\x04\x05",
+ 0,
+ NULL
+ },
+ {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ 2,
+ 32,
+ "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98"
+ "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52",
+ 56,
+ "\xBC\x47\xFF\xEC\x79\x98\xEB\x91\xE8\x11\x5C\xF8\xD1\x9D\xAC\x4B"
+ "\xBB\xE2\xE1\x63\xE8\x7D\xD3\x7F\x49\xBE\xCA\x92\x02\x77\x64\xF6"
+ "\x8C\xF5\x1F\x14\xD7\x98\xC2\x27\x3F\x35\xDF\x57\x4D\x1F\x93\x2E"
+ "\x40\xC4\xFF\x25\x5B\x36\xA2\x66",
+ 16,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F",
+ 0,
+ NULL
+ },
+ {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ 2,
+ 32,
+ "\x6D\x40\x4D\x37\xFA\xF7\x9F\x9D\xF0\xD3\x35\x68\xD3\x20\x66\x98"
+ "\x00\xEB\x48\x36\x47\x2E\xA8\xA0\x26\xD1\x6B\x71\x82\x46\x0C\x52",
+ 61,
+ "\x40\x01\x3E\x2D\xF5\x8E\x87\x51\x95\x7D\x28\x78\xBC\xD2\xD6\xFE"
+ "\x10\x1C\xCF\xD5\x56\xCB\x1E\xAE\x79\xDB\x3C\x3E\xE8\x64\x29\xF2"
+ "\xB2\xA6\x02\xAC\x86\xFE\xF6\xEC\xB6\x47\xD6\x29\x5F\xAE\x07\x7A"
+ "\x1F\xEB\x51\x75\x08\xD2\xC1\x6B\x41\x92\xE0\x1F\x62",
+ 21,
+ "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
+ "\x10\x11\x12\x13\x14",
+ 24,
+ "\x45\xEE\x79\x15\x67\xEE\xFC\xA3\x7F\x4A\xC1\xE0\x22\x2D\xE8\x0D"
+ "\x43\xC3\xBF\xA0\x66\x99\x67\x2A"
}
};
-
static int
krb_enc_test(krb5_context context)
{
@@ -622,10 +774,22 @@ krb_enc_test(krb5_context context)
if (ret)
errx(1, "krb_enc_iov2 failed with %d for test %d", ret, i);
- ret = krb_checksum_iov(context, crypto, krbencs[i].usage, &plain);
+ ret = krb_checksum_iov(context, crypto, krbencs[i].usage, &plain, NULL);
if (ret)
errx(1, "krb_checksum_iov failed with %d for test %d", ret, i);
+ if (krbencs[i].cdata) {
+ krb5_data checksum;
+
+ checksum.length = krbencs[i].clen;
+ checksum.data = krbencs[i].cdata;
+
+ ret = krb_checksum_iov(context, crypto, krbencs[i].usage,
+ &plain, &checksum);
+ if (ret)
+ errx(1, "krb_checksum_iov(2) failed with %d for test %d", ret, i);
+ }
+
krb5_crypto_destroy(context, crypto);
ret = krb_enc_mit(context, krbencs[i].enctype, &kb,
@@ -638,9 +802,8 @@ krb_enc_test(krb5_context context)
}
static int
-iov_test(krb5_context context)
+iov_test(krb5_context context, krb5_enctype enctype)
{
- krb5_enctype enctype = ENCTYPE_AES256_CTS_HMAC_SHA1_96;
krb5_error_code ret;
krb5_crypto crypto;
krb5_keyblock key;
@@ -857,6 +1020,9 @@ main(int argc, char **argv)
krb5_context context;
int val = 0;
+ if (argc > 1 && strcmp(argv[1], "-v") == 0)
+ verbose = 1;
+
ret = krb5_init_context (&context);
if (ret)
errx (1, "krb5_init_context failed: %d", ret);
@@ -865,7 +1031,9 @@ main(int argc, char **argv)
val |= krb_enc_test(context);
val |= random_to_key(context);
- val |= iov_test(context);
+ val |= iov_test(context, KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96);
+ val |= iov_test(context, KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128);
+ val |= iov_test(context, KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192);
if (verbose && val == 0)
printf("all ok\n");
diff --git a/lib/krb5/an2ln_plugin.h b/lib/krb5/an2ln_plugin.h
new file mode 100644
index 000000000000..89913b5780a3
--- /dev/null
+++ b/lib/krb5/an2ln_plugin.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2006 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_AN2LN_PLUGIN_H
+#define HEIMDAL_KRB5_AN2LN_PLUGIN_H 1
+
+#define KRB5_PLUGIN_AN2LN "an2ln"
+#define KRB5_PLUGIN_AN2LN_VERSION_0 0
+
+typedef krb5_error_code (KRB5_LIB_CALL *set_result_f)(void *, const char *);
+
+/** @struct krb5plugin_an2ln_ftable_desc
+ *
+ * @brief Description of the krb5_aname_to_lname(3) plugin facility.
+ *
+ * The krb5_aname_to_lname(3) function is pluggable. The plugin is
+ * named KRB5_PLUGIN_AN2LN ("an2ln"), with a single minor version,
+ * KRB5_PLUGIN_AN2LN_VERSION_0 (0).
+ *
+ * The plugin for krb5_aname_to_lname(3) consists of a data symbol
+ * referencing a structure of type krb5plugin_an2ln_ftable, with four
+ * fields:
+ *
+ * @param init Plugin initialization function (see krb5-plugin(7))
+ *
+ * @param minor_version The plugin minor version number (0)
+ *
+ * @param fini Plugin finalization function
+ *
+ * @param an2ln Plugin aname_to_lname function
+ *
+ * The an2ln field is the plugin entry point that performs the
+ * traditional aname_to_lname operation however the plugin desires. It
+ * is invoked in no particular order relative to other an2ln plugins,
+ * but it has a 'rule' argument that indicates which plugin is intended
+ * to act on the rule. The plugin an2ln function must return
+ * KRB5_PLUGIN_NO_HANDLE if the rule is not applicable to it.
+ *
+ * The plugin an2ln function has the following arguments, in this order:
+ *
+ * -# plug_ctx, the context value output by the plugin's init function
+ * -# context, a krb5_context
+ * -# rule, the aname_to_lname rule being evaluated (from krb5.conf(5))
+ * -# aname, the krb5_principal to be mapped to an lname
+ * -# set_res_f, a function the plugin must call to set its result
+ * -# set_res_ctx, the first argument to set_res_f (the second is the result lname string)
+ *
+ * @ingroup krb5_support
+ */
+typedef struct krb5plugin_an2ln_ftable_desc {
+ int minor_version;
+ krb5_error_code (KRB5_LIB_CALL *init)(krb5_context, void **);
+ void (KRB5_LIB_CALL *fini)(void *);
+ krb5_error_code (KRB5_LIB_CALL *an2ln)(void *, krb5_context, const char *,
+ krb5_const_principal, set_result_f, void *);
+} krb5plugin_an2ln_ftable;
+
+#endif /* HEIMDAL_KRB5_AN2LN_PLUGIN_H */
+
diff --git a/lib/krb5/aname_to_localname.c b/lib/krb5/aname_to_localname.c
index 7bfd861da94e..e4818c360b37 100644
--- a/lib/krb5/aname_to_localname.c
+++ b/lib/krb5/aname_to_localname.c
@@ -31,23 +31,109 @@
* SUCH DAMAGE.
*/
+#include <string.h>
#include "krb5_locl.h"
+#include "an2ln_plugin.h"
+#include "db_plugin.h"
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_aname_to_localname (krb5_context context,
- krb5_const_principal aname,
- size_t lnsize,
- char *lname)
+/* Default plugin (DB using binary search of sorted text file) follows */
+static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_init(krb5_context, void **);
+static void KRB5_LIB_CALL an2ln_def_plug_fini(void *);
+static krb5_error_code KRB5_LIB_CALL an2ln_def_plug_an2ln(void *, krb5_context, const char *,
+ krb5_const_principal, set_result_f,
+ void *);
+
+static krb5plugin_an2ln_ftable an2ln_def_plug = {
+ 0,
+ an2ln_def_plug_init,
+ an2ln_def_plug_fini,
+ an2ln_def_plug_an2ln,
+};
+
+/* Plugin engine code follows */
+struct plctx {
+ krb5_const_principal aname;
+ heim_string_t luser;
+ const char *rule;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+set_res(void *userctx, const char *res)
+{
+ struct plctx *plctx = userctx;
+ plctx->luser = heim_string_create(res);
+ if (plctx->luser == NULL)
+ return ENOMEM;
+ return 0;
+}
+
+static krb5_error_code KRB5_LIB_CALL
+plcallback(krb5_context context,
+ const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_an2ln_ftable *locate = plug;
+ struct plctx *plctx = userctx;
+
+ if (plctx->luser)
+ return 0;
+
+ return locate->an2ln(plugctx, context, plctx->rule, plctx->aname, set_res, plctx);
+}
+
+static krb5_error_code
+an2ln_plugin(krb5_context context, const char *rule, krb5_const_principal aname,
+ size_t lnsize, char *lname)
{
krb5_error_code ret;
- krb5_realm *lrealms, *r;
+ struct plctx ctx;
+
+ ctx.rule = rule;
+ ctx.aname = aname;
+ ctx.luser = NULL;
+
+ /*
+ * Order of plugin invocation is non-deterministic, but there should
+ * really be no more than one plugin that can handle any given kind
+ * rule, so the effect should be deterministic anyways.
+ */
+ ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_AN2LN,
+ KRB5_PLUGIN_AN2LN_VERSION_0, 0, &ctx, plcallback);
+ if (ret != 0) {
+ heim_release(ctx.luser);
+ return ret;
+ }
+
+ if (ctx.luser == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ if (strlcpy(lname, heim_string_get_utf8(ctx.luser), lnsize) >= lnsize)
+ ret = KRB5_CONFIG_NOTENUFSPACE;
+
+ heim_release(ctx.luser);
+ return ret;
+}
+
+static void
+reg_def_plugins_once(void *ctx)
+{
+ krb5_context context = ctx;
+
+ krb5_plugin_register(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_AN2LN,
+ &an2ln_def_plug);
+}
+
+static int
+princ_realm_is_default(krb5_context context,
+ krb5_const_principal aname)
+{
+ krb5_error_code ret;
+ krb5_realm *lrealms = NULL;
+ krb5_realm *r;
int valid;
- size_t len;
- const char *res;
- ret = krb5_get_default_realms (context, &lrealms);
+ ret = krb5_get_default_realms(context, &lrealms);
if (ret)
- return ret;
+ return 0;
valid = 0;
for (r = lrealms; *r != NULL; ++r) {
@@ -57,13 +143,107 @@ krb5_aname_to_localname (krb5_context context,
}
}
krb5_free_host_realm (context, lrealms);
- if (valid == 0)
+ return valid;
+}
+
+/*
+ * This function implements MIT's auth_to_local_names configuration for
+ * configuration compatibility. Specifically:
+ *
+ * [realms]
+ * <realm-name> = {
+ * auth_to_local_names = {
+ * <unparsed-principal-name> = <username>
+ * }
+ * }
+ *
+ * If multiple usernames are configured then the last one is taken.
+ *
+ * The configuration can only be expected to hold a relatively small
+ * number of mappings. For lots of mappings use a DB.
+ */
+static krb5_error_code
+an2ln_local_names(krb5_context context,
+ krb5_const_principal aname,
+ size_t lnsize,
+ char *lname)
+{
+ krb5_error_code ret;
+ char *unparsed;
+ char **values;
+ char *res;
+ size_t i;
+
+ if (!princ_realm_is_default(context, aname))
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ ret = krb5_unparse_name_flags(context, aname,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &unparsed);
+ if (ret)
+ return ret;
+
+ ret = KRB5_PLUGIN_NO_HANDLE;
+ values = krb5_config_get_strings(context, NULL, "realms", aname->realm,
+ "auth_to_local_names", unparsed, NULL);
+ free(unparsed);
+ if (!values)
+ return ret;
+ /* Take the last value, just like MIT */
+ for (res = NULL, i = 0; values[i]; i++)
+ res = values[i];
+ if (res) {
+ ret = 0;
+ if (strlcpy(lname, res, lnsize) >= lnsize)
+ ret = KRB5_CONFIG_NOTENUFSPACE;
+
+ if (!*res || strcmp(res, ":") == 0)
+ ret = KRB5_NO_LOCALNAME;
+ }
+
+ krb5_config_free_strings(values);
+ return ret;
+}
+
+/*
+ * Heimdal's default aname2lname mapping.
+ */
+static krb5_error_code
+an2ln_default(krb5_context context,
+ char *rule,
+ krb5_const_principal aname,
+ size_t lnsize, char *lname)
+{
+ krb5_error_code ret;
+ const char *res;
+ int root_princs_ok;
+
+ if (strcmp(rule, "NONE") == 0)
return KRB5_NO_LOCALNAME;
- if (aname->name.name_string.len == 1)
+ if (strcmp(rule, "DEFAULT") == 0)
+ root_princs_ok = 0;
+ else if (strcmp(rule, "HEIMDAL_DEFAULT") == 0)
+ root_princs_ok = 1;
+ else
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ if (!princ_realm_is_default(context, aname))
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ if (aname->name.name_string.len == 1) {
+ /*
+ * One component principal names in default realm -> the one
+ * component is the username.
+ */
res = aname->name.name_string.val[0];
- else if (aname->name.name_string.len == 2
- && strcmp (aname->name.name_string.val[1], "root") == 0) {
+ } else if (root_princs_ok && aname->name.name_string.len == 2 &&
+ strcmp (aname->name.name_string.val[1], "root") == 0) {
+ /*
+ * Two-component principal names in default realm where the
+ * first component is "root" -> root IFF the principal is in
+ * root's .k5login (or whatever krb5_kuserok() does).
+ */
krb5_principal rootprinc;
krb5_boolean userok;
@@ -73,18 +253,206 @@ krb5_aname_to_localname (krb5_context context,
if (ret)
return ret;
- userok = krb5_kuserok(context, rootprinc, res);
+ userok = _krb5_kuserok(context, rootprinc, res, FALSE);
krb5_free_principal(context, rootprinc);
if (!userok)
return KRB5_NO_LOCALNAME;
+ } else {
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+
+ if (strlcpy(lname, res, lnsize) >= lnsize)
+ return KRB5_CONFIG_NOTENUFSPACE;
+
+ return 0;
+}
+
+/**
+ * Map a principal name to a local username.
+ *
+ * Returns 0 on success, KRB5_NO_LOCALNAME if no mapping was found, or
+ * some Kerberos or system error.
+ *
+ * Inputs:
+ *
+ * @param context A krb5_context
+ * @param aname A principal name
+ * @param lnsize The size of the buffer into which the username will be written
+ * @param lname The buffer into which the username will be written
+ *
+ * @ingroup krb5_support
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_aname_to_localname(krb5_context context,
+ krb5_const_principal aname,
+ size_t lnsize,
+ char *lname)
+{
+ static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT;
+ krb5_error_code ret;
+ krb5_realm realm;
+ size_t i;
+ char **rules = NULL;
+ char *rule;
+
+ if (lnsize)
+ lname[0] = '\0';
+
+ heim_base_once_f(&reg_def_plugins, context, reg_def_plugins_once);
+
+ /* Try MIT's auth_to_local_names config first */
+ ret = an2ln_local_names(context, aname, lnsize, lname);
+ if (ret != KRB5_PLUGIN_NO_HANDLE)
+ return ret;
+
+ ret = krb5_get_default_realm(context, &realm);
+ if (ret)
+ return ret;
+
+ rules = krb5_config_get_strings(context, NULL, "realms", realm,
+ "auth_to_local", NULL);
+ krb5_xfree(realm);
+ if (!rules) {
+ /* Heimdal's default rule */
+ ret = an2ln_default(context, "HEIMDAL_DEFAULT", aname, lnsize, lname);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ return KRB5_NO_LOCALNAME;
+ return ret;
+ }
- } else
- return KRB5_NO_LOCALNAME;
+ /*
+ * MIT rules.
+ *
+ * Note that RULEs and DBs only have white-list functionality,
+ * thus RULEs and DBs that we don't understand we simply ignore.
+ *
+ * This means that plugins that implement black-lists are
+ * dangerous: if a black-list plugin isn't found, the black-list
+ * won't be enforced. But black-lists are dangerous anyways.
+ */
+ for (ret = KRB5_PLUGIN_NO_HANDLE, i = 0; rules[i]; i++) {
+ rule = rules[i];
+
+ /* Try NONE, DEFAULT, and HEIMDAL_DEFAULT rules */
+ ret = an2ln_default(context, rule, aname, lnsize, lname);
+ if (ret == KRB5_PLUGIN_NO_HANDLE)
+ /* Try DB, RULE, ... plugins */
+ ret = an2ln_plugin(context, rule, aname, lnsize, lname);
+
+ if (ret == 0 && lnsize && !lname[0])
+ continue; /* Success but no lname?! lies! */
+ else if (ret != KRB5_PLUGIN_NO_HANDLE)
+ break;
+ }
- len = strlen (res);
- if (len >= lnsize)
- return ERANGE;
- strlcpy (lname, res, lnsize);
+ if (ret == KRB5_PLUGIN_NO_HANDLE) {
+ if (lnsize)
+ lname[0] = '\0';
+ ret = KRB5_NO_LOCALNAME;
+ }
+
+ krb5_config_free_strings(rules);
+ return ret;
+}
+static krb5_error_code KRB5_LIB_CALL
+an2ln_def_plug_init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
return 0;
}
+
+static void KRB5_LIB_CALL
+an2ln_def_plug_fini(void *ctx)
+{
+}
+
+static heim_base_once_t sorted_text_db_init_once = HEIM_BASE_ONCE_INIT;
+
+static void
+sorted_text_db_init_f(void *arg)
+{
+ (void) heim_db_register("sorted-text", NULL, &heim_sorted_text_file_dbtype);
+}
+
+static krb5_error_code KRB5_LIB_CALL
+an2ln_def_plug_an2ln(void *plug_ctx, krb5_context context,
+ const char *rule,
+ krb5_const_principal aname,
+ set_result_f set_res_f, void *set_res_ctx)
+{
+ krb5_error_code ret;
+ const char *an2ln_db_fname;
+ heim_db_t dbh = NULL;
+ heim_dict_t db_options;
+ heim_data_t k, v;
+ heim_error_t error;
+ char *unparsed = NULL;
+ char *value = NULL;
+
+ _krb5_load_db_plugins(context);
+ heim_base_once_f(&sorted_text_db_init_once, NULL, sorted_text_db_init_f);
+
+ if (strncmp(rule, "DB:", strlen("DB:")) != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ an2ln_db_fname = &rule[strlen("DB:")];
+ if (!*an2ln_db_fname)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ ret = krb5_unparse_name(context, aname, &unparsed);
+ if (ret)
+ return ret;
+
+ db_options = heim_dict_create(11);
+ if (db_options != NULL)
+ heim_dict_set_value(db_options, HSTR("read-only"),
+ heim_number_create(1));
+ dbh = heim_db_create(NULL, an2ln_db_fname, db_options, &error);
+ if (dbh == NULL) {
+ krb5_set_error_message(context, heim_error_get_code(error),
+ N_("Couldn't open aname2lname-text-db", ""));
+ ret = KRB5_PLUGIN_NO_HANDLE;
+ goto cleanup;
+ }
+
+ /* Binary search; file should be sorted (in C locale) */
+ k = heim_data_ref_create(unparsed, strlen(unparsed), NULL);
+ if (k == NULL) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ v = heim_db_copy_value(dbh, NULL, k, &error);
+ heim_release(k);
+ if (v == NULL && error != NULL) {
+ krb5_set_error_message(context, heim_error_get_code(error),
+ N_("Lookup in aname2lname-text-db failed", ""));
+ ret = heim_error_get_code(error);
+ goto cleanup;
+ } else if (v == NULL) {
+ ret = KRB5_PLUGIN_NO_HANDLE;
+ goto cleanup;
+ } else {
+ /* found */
+ if (heim_data_get_length(v) == 0) {
+ krb5_set_error_message(context, ret,
+ N_("Principal mapped to empty username", ""));
+ ret = KRB5_NO_LOCALNAME;
+ goto cleanup;
+ }
+ value = strndup(heim_data_get_ptr(v), heim_data_get_length(v));
+ heim_release(v);
+ if (value == NULL) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ ret = set_res_f(set_res_ctx, value);
+ }
+
+cleanup:
+ heim_release(dbh);
+ free(unparsed);
+ free(value);
+ return ret;
+}
+
diff --git a/lib/krb5/asn1_glue.c b/lib/krb5/asn1_glue.c
index a821faff93e8..6df8defbce9a 100644
--- a/lib/krb5/asn1_glue.c
+++ b/lib/krb5/asn1_glue.c
@@ -53,9 +53,9 @@ _krb5_principalname2krb5_principal (krb5_context context,
krb5_error_code ret;
krb5_principal p;
- p = malloc(sizeof(*p));
+ p = calloc(1, sizeof(*p));
if (p == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
ret = copy_PrincipalName(&from, &p->name);
if (ret) {
free(p);
@@ -65,7 +65,7 @@ _krb5_principalname2krb5_principal (krb5_context context,
if (p->realm == NULL) {
free_PrincipalName(&p->name);
free(p);
- return ENOMEM;
+ return krb5_enomem(context);
}
*principal = p;
return 0;
diff --git a/lib/krb5/auth_context.c b/lib/krb5/auth_context.c
index 25ae15cf0f3b..9c6c0c40f9e4 100644
--- a/lib/krb5/auth_context.c
+++ b/lib/krb5/auth_context.c
@@ -33,6 +33,16 @@
#include "krb5_locl.h"
+/**
+ * Allocate and initialize an autentication context.
+ *
+ * @param context A kerberos context.
+ * @param auth_context The authentication context to be initialized.
+ *
+ * Use krb5_auth_con_free() to release the memory when done using the context.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_init(krb5_context context,
krb5_auth_context *auth_context)
@@ -40,16 +50,13 @@ krb5_auth_con_init(krb5_context context,
krb5_auth_context p;
ALLOC(p, 1);
- if(!p) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (!p)
+ return krb5_enomem(context);
memset(p, 0, sizeof(*p));
ALLOC(p->authenticator, 1);
if (!p->authenticator) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(p);
- return ENOMEM;
+ return krb5_enomem(context);
}
memset (p->authenticator, 0, sizeof(*p->authenticator));
p->flags = KRB5_AUTH_CONTEXT_DO_TIME;
@@ -58,12 +65,22 @@ krb5_auth_con_init(krb5_context context,
p->remote_address = NULL;
p->local_port = 0;
p->remote_port = 0;
- p->keytype = ENCTYPE_NULL;
+ p->keytype = KRB5_ENCTYPE_NULL;
p->cksumtype = CKSUMTYPE_NONE;
+ p->auth_data = NULL;
*auth_context = p;
return 0;
}
+/**
+ * Deallocate an authentication context previously initialized with
+ * krb5_auth_con_init().
+ *
+ * @param context A kerberos context.
+ * @param auth_context The authentication context to be deallocated.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_free(krb5_context context,
krb5_auth_context auth_context)
@@ -81,6 +98,10 @@ krb5_auth_con_free(krb5_context context,
krb5_free_keyblock(context, auth_context->keyblock);
krb5_free_keyblock(context, auth_context->remote_subkey);
krb5_free_keyblock(context, auth_context->local_subkey);
+ if (auth_context->auth_data) {
+ free_AuthorizationData(auth_context->auth_data);
+ free(auth_context->auth_data);
+ }
free (auth_context);
}
return 0;
@@ -140,7 +161,7 @@ krb5_auth_con_setaddrs(krb5_context context,
krb5_free_address (context, auth_context->local_address);
else
if ((auth_context->local_address = malloc(sizeof(krb5_address))) == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
krb5_copy_address(context, local_addr, auth_context->local_address);
}
if (remote_addr) {
@@ -148,12 +169,18 @@ krb5_auth_con_setaddrs(krb5_context context,
krb5_free_address (context, auth_context->remote_address);
else
if ((auth_context->remote_address = malloc(sizeof(krb5_address))) == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
krb5_copy_address(context, remote_addr, auth_context->remote_address);
}
return 0;
}
+/**
+ * Update the authentication context \a auth_context with the local
+ * and remote addresses from socket \a fd, according to \a flags.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_genaddrs(krb5_context context,
krb5_auth_context auth_context,
@@ -239,10 +266,8 @@ krb5_auth_con_getaddrs(krb5_context context,
if(*local_addr)
krb5_free_address (context, *local_addr);
*local_addr = malloc (sizeof(**local_addr));
- if (*local_addr == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*local_addr == NULL)
+ return krb5_enomem(context);
krb5_copy_address(context,
auth_context->local_address,
*local_addr);
@@ -251,10 +276,9 @@ krb5_auth_con_getaddrs(krb5_context context,
krb5_free_address (context, *remote_addr);
*remote_addr = malloc (sizeof(**remote_addr));
if (*remote_addr == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
krb5_free_address (context, *local_addr);
*local_addr = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
krb5_copy_address(context,
auth_context->remote_address,
@@ -268,9 +292,9 @@ copy_key(krb5_context context,
krb5_keyblock *in,
krb5_keyblock **out)
{
- if(in)
+ *out = NULL;
+ if (in)
return krb5_copy_keyblock(context, in, out);
- *out = NULL; /* is this right? */
return 0;
}
@@ -385,6 +409,28 @@ krb5_auth_con_getkeytype (krb5_context context,
return 0;
}
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_auth_con_add_AuthorizationData(krb5_context context,
+ krb5_auth_context auth_context,
+ int type,
+ krb5_data *data)
+{
+ AuthorizationDataElement el;
+
+ if (auth_context->auth_data == NULL) {
+ auth_context->auth_data = calloc(1, sizeof(*auth_context->auth_data));
+ if (auth_context->auth_data == NULL)
+ return krb5_enomem(context);
+ }
+ el.ad_type = type;
+ el.ad_data.data = data->data;
+ el.ad_data.length = data->length;
+
+ return add_AuthorizationData(auth_context->auth_data, &el);
+}
+
+
+
#if 0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_setenctype(krb5_context context,
@@ -395,7 +441,7 @@ krb5_auth_con_setenctype(krb5_context context,
krb5_free_keyblock(context, auth_context->keyblock);
ALLOC(auth_context->keyblock, 1);
if(auth_context->keyblock == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
auth_context->keyblock->keytype = etype;
return 0;
}
@@ -452,10 +498,8 @@ krb5_auth_con_getauthenticator(krb5_context context,
krb5_authenticator *authenticator)
{
*authenticator = malloc(sizeof(**authenticator));
- if (*authenticator == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*authenticator == NULL)
+ return krb5_enomem(context);
copy_Authenticator(auth_context->authenticator,
*authenticator);
diff --git a/lib/krb5/build_auth.c b/lib/krb5/build_auth.c
index 01145a28c604..003ccae8cc87 100644
--- a/lib/krb5/build_auth.c
+++ b/lib/krb5/build_auth.c
@@ -62,8 +62,7 @@ make_etypelist(krb5_context context,
ALLOC_SEQ(&ad, 1);
if (ad.val == NULL) {
free(buf);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
ad.val[0].ad_type = KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION;
@@ -82,16 +81,14 @@ make_etypelist(krb5_context context,
ALLOC(*auth_data, 1);
if (*auth_data == NULL) {
free(buf);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
ALLOC_SEQ(*auth_data, 1);
if ((*auth_data)->val == NULL) {
free(*auth_data);
free(buf);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
(*auth_data)->val[0].ad_type = KRB5_AUTHDATA_IF_RELEVANT;
@@ -136,7 +133,7 @@ _krb5_build_authenticator (krb5_context context,
&auth_context->local_seqnumber);
ALLOC(auth.seq_number, 1);
if(auth.seq_number == NULL) {
- ret = ENOMEM;
+ ret = krb5_enomem(context);
goto fail;
}
*auth.seq_number = auth_context->local_seqnumber;
@@ -147,7 +144,7 @@ _krb5_build_authenticator (krb5_context context,
if (cksum) {
ALLOC(auth.cksum, 1);
if (auth.cksum == NULL) {
- ret = ENOMEM;
+ ret = krb5_enomem(context);
goto fail;
}
ret = copy_Checksum(cksum, auth.cksum);
diff --git a/lib/krb5/cache.c b/lib/krb5/cache.c
index 88040cbc6f35..c43cd0ab7096 100644
--- a/lib/krb5/cache.c
+++ b/lib/krb5/cache.c
@@ -155,15 +155,15 @@ krb5_cc_register(krb5_context context,
* `ops'. Returns 0 or and error code.
*/
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_cc_allocate(krb5_context context,
const krb5_cc_ops *ops,
krb5_ccache *id)
{
krb5_ccache p;
- p = malloc (sizeof(*p));
- if(p == NULL) {
+ p = calloc(1, sizeof(*p));
+ if (p == NULL) {
krb5_set_error_message(context, KRB5_CC_NOMEM,
N_("malloc: out of memory", ""));
return KRB5_CC_NOMEM;
@@ -188,8 +188,13 @@ allocate_ccache (krb5_context context,
krb5_error_code ret;
#ifdef KRB5_USE_PATH_TOKENS
char * exp_residual = NULL;
+ int filepath;
+
+ filepath = (strcmp("FILE", ops->prefix) == 0
+ || strcmp("DIR", ops->prefix) == 0
+ || strcmp("SCC", ops->prefix) == 0);
- ret = _krb5_expand_path_tokens(context, residual, &exp_residual);
+ ret = _krb5_expand_path_tokens(context, residual, filepath, &exp_residual);
if (ret)
return ret;
@@ -383,9 +388,8 @@ krb5_cc_get_full_name(krb5_context context,
}
if (asprintf(str, "%s:%s", type, name) == -1) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
*str = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -407,10 +411,16 @@ krb5_cc_get_ops(krb5_context context, krb5_ccache id)
* Expand variables in `str' into `res'
*/
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_expand_default_cc_name(krb5_context context, const char *str, char **res)
{
- return _krb5_expand_path_tokens(context, str, res);
+ int filepath;
+
+ filepath = (strncmp("FILE:", str, 5) == 0
+ || strncmp("DIR:", str, 4) == 0
+ || strncmp("SCC:", str, 4) == 0);
+
+ return _krb5_expand_path_tokens(context, str, filepath, res);
}
/*
@@ -502,11 +512,13 @@ krb5_cc_set_default_name(krb5_context context, const char *name)
{
krb5_error_code ret = 0;
char *p = NULL, *exp_p = NULL;
+ int filepath;
+ const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
if (name == NULL) {
const char *e = NULL;
- if(!issuid()) {
+ if (!issuid()) {
e = getenv("KRB5CCNAME");
if (e) {
p = strdup(e);
@@ -517,11 +529,11 @@ krb5_cc_set_default_name(krb5_context context, const char *name)
}
#ifdef _WIN32
- if (e == NULL) {
- e = p = _krb5_get_default_cc_name_from_registry(context);
+ if (p == NULL) {
+ p = _krb5_get_default_cc_name_from_registry(context);
}
#endif
- if (e == NULL) {
+ if (p == NULL) {
e = krb5_config_get_string(context, NULL, "libdefaults",
"default_cc_name", NULL);
if (e) {
@@ -529,45 +541,78 @@ krb5_cc_set_default_name(krb5_context context, const char *name)
if (ret)
return ret;
}
- if (e == NULL) {
- const krb5_cc_ops *ops = KRB5_DEFAULT_CCTYPE;
- e = krb5_config_get_string(context, NULL, "libdefaults",
- "default_cc_type", NULL);
- if (e) {
- ops = krb5_cc_get_prefix_ops(context, e);
- if (ops == NULL) {
- krb5_set_error_message(context,
- KRB5_CC_UNKNOWN_TYPE,
- "Credential cache type %s "
- "is unknown", e);
- return KRB5_CC_UNKNOWN_TYPE;
- }
+ }
+ if (p == NULL) {
+ e = krb5_config_get_string(context, NULL, "libdefaults",
+ "default_cc_type", NULL);
+ if (e) {
+ ops = krb5_cc_get_prefix_ops(context, e);
+ if (ops == NULL) {
+ krb5_set_error_message(context,
+ KRB5_CC_UNKNOWN_TYPE,
+ "Credential cache type %s "
+ "is unknown", e);
+ return KRB5_CC_UNKNOWN_TYPE;
+ }
+ }
+ }
+#ifdef _WIN32
+ if (p == NULL) {
+ /*
+ * If the MSLSA ccache type has a principal name,
+ * use it as the default.
+ */
+ krb5_ccache id;
+ ret = krb5_cc_resolve(context, "MSLSA:", &id);
+ if (ret == 0) {
+ krb5_principal princ;
+ ret = krb5_cc_get_principal(context, id, &princ);
+ if (ret == 0) {
+ krb5_free_principal(context, princ);
+ p = strdup("MSLSA:");
}
- ret = (*ops->get_default_name)(context, &p);
- if (ret)
- return ret;
+ krb5_cc_close(context, id);
}
}
+ if (p == NULL) {
+ /*
+ * If the API:krb5cc ccache can be resolved,
+ * use it as the default.
+ */
+ krb5_ccache api_id;
+ ret = krb5_cc_resolve(context, "API:krb5cc", &api_id);
+ if (ret == 0)
+ krb5_cc_close(context, api_id);
+ }
+ /* Otherwise, fallback to the FILE ccache */
+#endif
+ if (p == NULL) {
+ ret = (*ops->get_default_name)(context, &p);
+ if (ret)
+ return ret;
+ }
context->default_cc_name_set = 0;
} else {
p = strdup(name);
+ if (p == NULL)
+ return krb5_enomem(context);
context->default_cc_name_set = 1;
}
- if (p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ filepath = (strncmp("FILE:", p, 5) == 0
+ || strncmp("DIR:", p, 4) == 0
+ || strncmp("SCC:", p, 4) == 0);
- ret = _krb5_expand_path_tokens(context, p, &exp_p);
+ ret = _krb5_expand_path_tokens(context, p, filepath, &exp_p);
free(p);
+ p = exp_p;
if (ret)
return ret;
if (context->default_cc_name)
free(context->default_cc_name);
- context->default_cc_name = exp_p;
+ context->default_cc_name = p;
return 0;
}
@@ -606,10 +651,8 @@ krb5_cc_default(krb5_context context,
{
const char *p = krb5_cc_default_name(context);
- if (p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (p == NULL)
+ return krb5_enomem(context);
return krb5_cc_resolve(context, p, id);
}
@@ -627,7 +670,12 @@ krb5_cc_initialize(krb5_context context,
krb5_ccache id,
krb5_principal primary_principal)
{
- return (*id->ops->init)(context, id, primary_principal);
+ krb5_error_code ret;
+
+ ret = (*id->ops->init)(context, id, primary_principal);
+ if (ret == 0)
+ id->initialized = 1;
+ return ret;
}
@@ -684,7 +732,35 @@ krb5_cc_store_cred(krb5_context context,
krb5_ccache id,
krb5_creds *creds)
{
- return (*id->ops->store)(context, id, creds);
+ krb5_error_code ret;
+ krb5_data realm;
+
+ ret = (*id->ops->store)(context, id, creds);
+
+ /* Look for and mark the first root TGT's realm as the start realm */
+ if (ret == 0 && id->initialized &&
+ krb5_principal_is_root_krbtgt(context, creds->server)) {
+
+ id->initialized = 0;
+ realm.length = strlen(creds->server->realm);
+ realm.data = creds->server->realm;
+ (void) krb5_cc_set_config(context, id, NULL, "start_realm", &realm);
+ } else if (ret == 0 && id->initialized &&
+ krb5_is_config_principal(context, creds->server) &&
+ strcmp(creds->server->name.name_string.val[1], "start_realm") == 0) {
+
+ /*
+ * But if the caller is storing a start_realm ccconfig, then
+ * stop looking for root TGTs to mark as the start_realm.
+ *
+ * By honoring any start_realm cc config stored, we interop
+ * both, with ccache implementations that don't preserve
+ * insertion order, and Kerberos implementations that store this
+ * cc config before the TGT.
+ */
+ id->initialized = 0;
+ }
+ return ret;
}
/**
@@ -903,7 +979,7 @@ krb5_cc_copy_match_f(krb5_context context,
}
while ((ret = krb5_cc_next_cred(context, from, &cursor, &cred)) == 0) {
- if (match == NULL || (*match)(context, matchctx, &cred) == 0) {
+ if (match == NULL || (*match)(context, matchctx, &cred)) {
if (matched)
(*matched)++;
ret = krb5_cc_store_cred(context, to, &cred);
@@ -984,12 +1060,20 @@ krb5_cc_get_prefix_ops(krb5_context context, const char *prefix)
if (prefix == NULL)
return KRB5_DEFAULT_CCTYPE;
- if (prefix[0] == '/')
+
+ /* Is absolute path? Or UNC path? */
+ if (ISPATHSEP(prefix[0]))
return &krb5_fcc_ops;
+#ifdef _WIN32
+ /* Is drive letter? */
+ if (isalpha(prefix[0]) && prefix[1] == ':')
+ return &krb5_fcc_ops;
+#endif
+
p = strdup(prefix);
if (p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ krb5_enomem(context);
return NULL;
}
p1 = strchr(p, ':');
@@ -1053,10 +1137,8 @@ krb5_cc_cache_get_first (krb5_context context,
}
*cursor = calloc(1, sizeof(**cursor));
- if (*cursor == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*cursor == NULL)
+ return krb5_enomem(context);
(*cursor)->ops = ops;
@@ -1134,6 +1216,7 @@ krb5_cc_cache_match (krb5_context context,
krb5_cccol_cursor cursor;
krb5_error_code ret;
krb5_ccache cache = NULL;
+ krb5_ccache expired_match = NULL;
*id = NULL;
@@ -1141,26 +1224,46 @@ krb5_cc_cache_match (krb5_context context,
if (ret)
return ret;
- while (krb5_cccol_cursor_next (context, cursor, &cache) == 0 && cache != NULL) {
+ while (krb5_cccol_cursor_next(context, cursor, &cache) == 0 && cache != NULL) {
krb5_principal principal;
+ krb5_boolean match;
+ time_t lifetime;
ret = krb5_cc_get_principal(context, cache, &principal);
- if (ret == 0) {
- krb5_boolean match;
+ if (ret)
+ goto next;
+ if (client->name.name_string.len == 0)
+ match = (strcmp(client->realm, principal->realm) == 0);
+ else
match = krb5_principal_compare(context, principal, client);
- krb5_free_principal(context, principal);
- if (match)
- break;
+ krb5_free_principal(context, principal);
+
+ if (!match)
+ goto next;
+
+ if (expired_match == NULL &&
+ (krb5_cc_get_lifetime(context, cache, &lifetime) != 0 || lifetime == 0)) {
+ expired_match = cache;
+ cache = NULL;
+ goto next;
}
+ break;
- krb5_cc_close(context, cache);
+ next:
+ if (cache)
+ krb5_cc_close(context, cache);
cache = NULL;
}
krb5_cccol_cursor_free(context, &cursor);
- if (cache == NULL) {
+ if (cache == NULL && expired_match) {
+ cache = expired_match;
+ expired_match = NULL;
+ } else if (expired_match) {
+ krb5_cc_close(context, expired_match);
+ } else if (cache == NULL) {
char *str;
krb5_unparse_name(context, client, &str);
@@ -1173,6 +1276,7 @@ krb5_cc_cache_match (krb5_context context,
free(str);
return KRB5_CC_NOTFOUND;
}
+
*id = cache;
return 0;
@@ -1388,10 +1492,8 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_cursor_new(krb5_context context, krb5_cccol_cursor *cursor)
{
*cursor = calloc(1, sizeof(**cursor));
- if (*cursor == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*cursor == NULL)
+ return krb5_enomem(context);
(*cursor)->idx = 0;
(*cursor)->cursor = NULL;
@@ -1577,10 +1679,9 @@ krb5_cc_get_friendly_name(krb5_context context,
} else {
ret = asprintf(name, "%.*s", (int)data.length, (char *)data.data);
krb5_data_free(&data);
- if (ret <= 0) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
- } else
+ if (ret <= 0)
+ ret = krb5_enomem(context);
+ else
ret = 0;
}
@@ -1626,27 +1727,72 @@ krb5_cc_set_friendly_name(krb5_context context,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_lifetime(krb5_context context, krb5_ccache id, time_t *t)
{
+ krb5_data config_start_realm;
+ char *start_realm;
krb5_cc_cursor cursor;
krb5_error_code ret;
krb5_creds cred;
- time_t now;
+ time_t now, endtime = 0;
*t = 0;
- now = time(NULL);
+ krb5_timeofday(context, &now);
+
+ ret = krb5_cc_get_config(context, id, NULL, "start_realm", &config_start_realm);
+ if (ret == 0) {
+ start_realm = strndup(config_start_realm.data, config_start_realm.length);
+ krb5_data_free(&config_start_realm);
+ } else {
+ krb5_principal client;
+
+ ret = krb5_cc_get_principal(context, id, &client);
+ if (ret)
+ return ret;
+ start_realm = strdup(krb5_principal_get_realm(context, client));
+ krb5_free_principal(context, client);
+ }
+ if (start_realm == NULL)
+ return krb5_enomem(context);
ret = krb5_cc_start_seq_get(context, id, &cursor);
- if (ret)
+ if (ret) {
+ free(start_realm);
return ret;
+ }
while ((ret = krb5_cc_next_cred(context, id, &cursor, &cred)) == 0) {
- if (cred.flags.b.initial) {
+ /**
+ * If we find the start krbtgt in the cache, use that as the lifespan.
+ */
+ if (krb5_principal_is_root_krbtgt(context, cred.server) &&
+ strcmp(cred.server->realm, start_realm) == 0) {
if (now < cred.times.endtime)
- *t = cred.times.endtime - now;
+ endtime = cred.times.endtime;
krb5_free_cred_contents(context, &cred);
break;
}
+ /*
+ * Skip config entries
+ */
+ if (krb5_is_config_principal(context, cred.server)) {
+ krb5_free_cred_contents(context, &cred);
+ continue;
+ }
+ /**
+ * If there was no krbtgt, use the shortest lifetime of
+ * service tickets that have yet to expire. If all
+ * credentials are expired, krb5_cc_get_lifetime() will fail.
+ */
+ if ((endtime == 0 || cred.times.endtime < endtime) && now < cred.times.endtime)
+ endtime = cred.times.endtime;
krb5_free_cred_contents(context, &cred);
}
+ free(start_realm);
+
+ /* if we found an endtime use that */
+ if (endtime) {
+ *t = endtime - now;
+ ret = 0;
+ }
krb5_cc_end_seq_get(context, id, &cursor);
@@ -1702,18 +1848,17 @@ krb5_cc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *offset
return (*id->ops->get_kdc_offset)(context, id, offset);
}
-
#ifdef _WIN32
-
#define REGPATH_MIT_KRB5 "SOFTWARE\\MIT\\Kerberos5"
-char *
-_krb5_get_default_cc_name_from_registry(krb5_context context)
+
+static char *
+_get_default_cc_name_from_registry(krb5_context context, HKEY hkBase)
{
HKEY hk_k5 = 0;
LONG code;
- char * ccname = NULL;
+ char *ccname = NULL;
- code = RegOpenKeyEx(HKEY_CURRENT_USER,
+ code = RegOpenKeyEx(hkBase,
REGPATH_MIT_KRB5,
0, KEY_READ, &hk_k5);
@@ -1728,7 +1873,20 @@ _krb5_get_default_cc_name_from_registry(krb5_context context)
return ccname;
}
-int
+KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
+_krb5_get_default_cc_name_from_registry(krb5_context context)
+{
+ char *ccname;
+
+ ccname = _get_default_cc_name_from_registry(context, HKEY_CURRENT_USER);
+ if (ccname == NULL)
+ ccname = _get_default_cc_name_from_registry(context,
+ HKEY_LOCAL_MACHINE);
+
+ return ccname;
+}
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_set_default_cc_name_to_registry(krb5_context context, krb5_ccache id)
{
HKEY hk_k5 = 0;
@@ -1759,5 +1917,4 @@ _krb5_set_default_cc_name_to_registry(krb5_context context, krb5_ccache id)
return ret;
}
-
#endif
diff --git a/lib/krb5/changepw.c b/lib/krb5/changepw.c
index 1e7cd0d464f0..e028753dbcfd 100644
--- a/lib/krb5/changepw.c
+++ b/lib/krb5/changepw.c
@@ -40,7 +40,7 @@
static void
str2data (krb5_data *d,
const char *fmt,
- ...) __attribute__ ((format (printf, 2, 3)));
+ ...) __attribute__ ((__format__ (__printf__, 2, 3)));
static void
str2data (krb5_data *d,
@@ -302,6 +302,10 @@ process_reply (krb5_context context,
_krb5_get_int(reply, &size, 4);
if (size + 4 < len)
continue;
+ if (sizeof(reply) - 4 < size) {
+ krb5_set_error_message(context, ERANGE, "size from server too large %s", host);
+ return ERANGE;
+ }
memmove(reply, reply + 4, size);
len = size;
break;
@@ -326,7 +330,7 @@ process_reply (krb5_context context,
if (len < 6) {
str2data (result_string, "server %s sent to too short message "
- "(%zu bytes)", host, len);
+ "(%llu bytes)", host, (unsigned long long)len);
*result_code = KRB5_KPASSWD_MALFORMED;
return 0;
}
diff --git a/lib/krb5/config_file.c b/lib/krb5/config_file.c
index 4ac25ae28703..b9e40666620b 100644
--- a/lib/krb5/config_file.c
+++ b/lib/krb5/config_file.c
@@ -82,7 +82,7 @@ static krb5_error_code parse_list(struct fileptr *f, unsigned *lineno,
krb5_config_binding **parent,
const char **err_message);
-krb5_config_section *
+KRB5_LIB_FUNCTION krb5_config_section * KRB5_LIB_CALL
_krb5_config_get_entry(krb5_config_section **parent, const char *name, int type)
{
krb5_config_section **q;
@@ -370,11 +370,11 @@ krb5_config_parse_debug (struct fileptr *f,
b = NULL;
} else if (*p == '}') {
*err_message = "unmatched }";
- return EINVAL; /* XXX */
+ return KRB5_CONFIG_BADFORMAT;
} else if(*p != '\0') {
if (s == NULL) {
*err_message = "binding before section";
- return EINVAL;
+ return KRB5_CONFIG_BADFORMAT;
}
ret = parse_binding(f, lineno, p, &b, &s->u.list, err_message);
if (ret)
@@ -425,7 +425,7 @@ krb5_config_parse_file_multi (krb5_context context,
* current users home directory. The behavior can be disabled and
* enabled by calling krb5_set_home_dir_access().
*/
- if (fname[0] == '~' && fname[1] == '/') {
+ if (ISTILDE(fname[0]) && ISPATHSEP(fname[1])) {
#ifndef KRB5_USE_PATH_TOKENS
const char *home = NULL;
@@ -444,22 +444,17 @@ krb5_config_parse_file_multi (krb5_context context,
home = pw->pw_dir;
}
if (home) {
- asprintf(&newfname, "%s%s", home, &fname[1]);
- if (newfname == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ int aret;
+
+ aret = asprintf(&newfname, "%s%s", home, &fname[1]);
+ if (aret == -1 || newfname == NULL)
+ return krb5_enomem(context);
fname = newfname;
}
#else /* KRB5_USE_PATH_TOKENS */
if (asprintf(&newfname, "%%{USERCONFIG}%s", &fname[1]) < 0 ||
newfname == NULL)
- {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ return krb5_enomem(context);
fname = newfname;
#endif
}
@@ -483,7 +478,7 @@ krb5_config_parse_file_multi (krb5_context context,
#ifdef KRB5_USE_PATH_TOKENS
char * exp_fname = NULL;
- ret = _krb5_expand_path_tokens(context, fname, &exp_fname);
+ ret = _krb5_expand_path_tokens(context, fname, 1, &exp_fname);
if (ret) {
if (newfname)
free(newfname);
@@ -697,7 +692,7 @@ _krb5_config_get (krb5_context context,
}
-const void *
+KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
_krb5_config_vget (krb5_context context,
const krb5_config_section *c,
int type,
@@ -939,13 +934,17 @@ krb5_config_vget_strings(krb5_context context,
s = next_component_string(tmp, " \t", &pos);
while(s){
char **tmp2 = realloc(strings, (nstr + 1) * sizeof(*strings));
- if(tmp2 == NULL)
+ if(tmp2 == NULL) {
+ free(tmp);
goto cleanup;
+ }
strings = tmp2;
strings[nstr] = strdup(s);
nstr++;
- if(strings[nstr-1] == NULL)
+ if(strings[nstr-1] == NULL) {
+ free(tmp);
goto cleanup;
+ }
s = next_component_string(NULL, " \t", &pos);
}
free(tmp);
diff --git a/lib/krb5/config_reg.c b/lib/krb5/config_reg.c
new file mode 100644
index 000000000000..6ee6a6496f4b
--- /dev/null
+++ b/lib/krb5/config_reg.c
@@ -0,0 +1,649 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include "krb5_locl.h"
+
+#ifndef _WIN32
+#error config_reg.c is only for Windows
+#endif
+
+#include <shlwapi.h>
+
+#ifndef MAX_DWORD
+#define MAX_DWORD 0xFFFFFFFF
+#endif
+
+#define REGPATH_KERBEROS "SOFTWARE\\Kerberos"
+#define REGPATH_HEIMDAL "SOFTWARE\\Heimdal"
+
+/**
+ * Store a string as a registry value of the specified type
+ *
+ * The following registry types are handled:
+ *
+ * - REG_DWORD: The string is converted to a number.
+ *
+ * - REG_SZ: The string is stored as is.
+ *
+ * - REG_EXPAND_SZ: The string is stored as is.
+ *
+ * - REG_MULTI_SZ:
+ *
+ * . If a separator is specified, the input string is broken
+ * up into multiple strings and stored as a multi-sz.
+ *
+ * . If no separator is provided, the input string is stored
+ * as a multi-sz.
+ *
+ * - REG_NONE:
+ *
+ * . If the string is all numeric, it will be stored as a
+ * REG_DWORD.
+ *
+ * . Otherwise, the string is stored as a REG_SZ.
+ *
+ * Other types are rejected.
+ *
+ * If cb_data is MAX_DWORD, the string pointed to by data must be nul-terminated
+ * otherwise a buffer overrun will occur.
+ *
+ * @param [in]valuename Name of the registry value to be modified or created
+ * @param [in]type Type of the value. REG_NONE if unknown
+ * @param [in]data The input string to be stored in the registry.
+ * @param [in]cb_data Size of the input string in bytes. MAX_DWORD if unknown.
+ * @param [in]separator Separator character for parsing strings.
+ *
+ * @retval 0 if success or non-zero on error.
+ * If non-zero is returned, an error message has been set using
+ * krb5_set_error_message().
+ *
+ */
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+_krb5_store_string_to_reg_value(krb5_context context,
+ HKEY key, const char * valuename,
+ DWORD type, const char *data, DWORD cb_data,
+ const char * separator)
+{
+ LONG rcode;
+ DWORD dwData;
+ BYTE static_buffer[16384];
+ BYTE *pbuffer = &static_buffer[0];
+
+ if (data == NULL)
+ {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "'data' must not be NULL");
+ return -1;
+ }
+
+ if (cb_data == MAX_DWORD)
+ {
+ cb_data = (DWORD)strlen(data) + 1;
+ }
+ else if ((type == REG_MULTI_SZ && cb_data >= sizeof(static_buffer) - 1) ||
+ cb_data >= sizeof(static_buffer))
+ {
+ if (context)
+ krb5_set_error_message(context, 0, "cb_data too big");
+ return -1;
+ }
+ else if (data[cb_data-1] != '\0')
+ {
+ memcpy(static_buffer, data, cb_data);
+ static_buffer[cb_data++] = '\0';
+ if (type == REG_MULTI_SZ)
+ static_buffer[cb_data++] = '\0';
+ data = static_buffer;
+ }
+
+ if (type == REG_NONE)
+ {
+ /*
+ * If input is all numeric, convert to DWORD and save as REG_DWORD.
+ * Otherwise, store as REG_SZ.
+ */
+ if ( StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
+ {
+ type = REG_DWORD;
+ } else {
+ type = REG_SZ;
+ }
+ }
+
+ switch (type) {
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+ rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
+ if (rcode)
+ {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ break;
+ case REG_MULTI_SZ:
+ if (separator && *separator)
+ {
+ char *cp;
+
+ if (data != static_buffer)
+ static_buffer[cb_data++] = '\0';
+
+ for ( cp = static_buffer; cp < static_buffer+cb_data; cp++)
+ {
+ if (*cp == *separator)
+ *cp = '\0';
+ }
+
+ rcode = RegSetValueEx(key, valuename, 0, type, data, cb_data);
+ if (rcode)
+ {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ }
+ break;
+ case REG_DWORD:
+ if ( !StrToIntExA( data, STIF_SUPPORT_HEX, &dwData) )
+ {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected error when parsing %s as number gle 0x%x",
+ data,
+ GetLastError());
+ }
+
+ rcode = RegSetValueEx(key, valuename, 0, type, (BYTE *)&dwData, sizeof(DWORD));
+ if (rcode)
+ {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected error when setting registry value %s gle 0x%x",
+ valuename,
+ GetLastError());
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Parse a registry value as a string
+ *
+ * @see _krb5_parse_reg_value_as_multi_string()
+ */
+KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
+_krb5_parse_reg_value_as_string(krb5_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cb_data)
+{
+ return _krb5_parse_reg_value_as_multi_string(context, key, valuename,
+ type, cb_data, " ");
+}
+
+/**
+ * Parse a registry value as a multi string
+ *
+ * The following registry value types are handled:
+ *
+ * - REG_DWORD: The decimal string representation is used as the
+ * value.
+ *
+ * - REG_SZ: The string is used as-is.
+ *
+ * - REG_EXPAND_SZ: Environment variables in the string are expanded
+ * and the result is used as the value.
+ *
+ * - REG_MULTI_SZ: The list of strings is concatenated using the
+ * separator. No quoting is performed.
+ *
+ * Any other value type is rejected.
+ *
+ * @param [in]valuename Name of the registry value to be queried
+ * @param [in]type Type of the value. REG_NONE if unknown
+ * @param [in]cbdata Size of value. 0 if unknown.
+ * @param [in]separator Separator character for concatenating strings.
+ *
+ * @a type and @a cbdata are only considered valid if both are
+ * specified.
+ *
+ * @retval The registry value string, or NULL if there was an error.
+ * If NULL is returned, an error message has been set using
+ * krb5_set_error_message().
+ */
+KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
+_krb5_parse_reg_value_as_multi_string(krb5_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cb_data, char *separator)
+{
+ LONG rcode = ERROR_MORE_DATA;
+
+ BYTE static_buffer[16384];
+ BYTE *pbuffer = &static_buffer[0];
+ DWORD cb_alloc = sizeof(static_buffer);
+ char *ret_string = NULL;
+
+ /* If we know a type and cb_data from a previous call to
+ * RegEnumValue(), we use it. Otherwise we use the
+ * static_buffer[] and query directly. We do this to minimize the
+ * number of queries. */
+
+ if (type == REG_NONE || cb_data == 0) {
+
+ pbuffer = &static_buffer[0];
+ cb_alloc = cb_data = sizeof(static_buffer);
+ rcode = RegQueryValueExA(key, valuename, NULL, &type, pbuffer, &cb_data);
+
+ if (rcode == ERROR_SUCCESS &&
+
+ ((type != REG_SZ &&
+ type != REG_EXPAND_SZ) || cb_data + 1 <= sizeof(static_buffer)) &&
+
+ (type != REG_MULTI_SZ || cb_data + 2 <= sizeof(static_buffer)))
+ goto have_data;
+
+ if (rcode != ERROR_MORE_DATA && rcode != ERROR_SUCCESS)
+ return NULL;
+ }
+
+ /* Either we don't have the data or we aren't sure of the size
+ * (due to potentially missing terminating NULs). */
+
+ switch (type) {
+ case REG_DWORD:
+ if (cb_data != sizeof(DWORD)) {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected size while reading registry value %s",
+ valuename);
+ return NULL;
+ }
+ break;
+
+ case REG_SZ:
+ case REG_EXPAND_SZ:
+
+ if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0')
+ goto have_data;
+
+ cb_data += sizeof(char); /* Accout for potential missing NUL
+ * terminator. */
+ break;
+
+ case REG_MULTI_SZ:
+
+ if (rcode == ERROR_SUCCESS && cb_data > 0 && pbuffer[cb_data - 1] == '\0' &&
+ (cb_data == 1 || pbuffer[cb_data - 2] == '\0'))
+ goto have_data;
+
+ cb_data += sizeof(char) * 2; /* Potential missing double NUL
+ * terminator. */
+ break;
+
+ default:
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected type while reading registry value %s",
+ valuename);
+ return NULL;
+ }
+
+ if (cb_data <= sizeof(static_buffer))
+ pbuffer = &static_buffer[0];
+ else {
+ pbuffer = malloc(cb_data);
+ if (pbuffer == NULL)
+ return NULL;
+ }
+
+ cb_alloc = cb_data;
+ rcode = RegQueryValueExA(key, valuename, NULL, NULL, pbuffer, &cb_data);
+
+ if (rcode != ERROR_SUCCESS) {
+
+ /* This can potentially be from a race condition. I.e. some
+ * other process or thread went and modified the registry
+ * value between the time we queried its size and queried for
+ * its value. Ideally we would retry the query in a loop. */
+
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected error while reading registry value %s",
+ valuename);
+ goto done;
+ }
+
+ if (cb_data > cb_alloc || cb_data == 0) {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected size while reading registry value %s",
+ valuename);
+ goto done;
+ }
+
+have_data:
+ switch (type) {
+ case REG_DWORD:
+ asprintf(&ret_string, "%d", *((DWORD *) pbuffer));
+ break;
+
+ case REG_SZ:
+ {
+ char * str = (char *) pbuffer;
+
+ if (str[cb_data - 1] != '\0') {
+ if (cb_data < cb_alloc)
+ str[cb_data] = '\0';
+ else
+ break;
+ }
+
+ if (pbuffer != static_buffer) {
+ ret_string = (char *) pbuffer;
+ pbuffer = NULL;
+ } else {
+ ret_string = strdup((char *) pbuffer);
+ }
+ }
+ break;
+
+ case REG_EXPAND_SZ:
+ {
+ char *str = (char *) pbuffer;
+ char expsz[32768]; /* Size of output buffer for
+ * ExpandEnvironmentStrings() is
+ * limited to 32K. */
+
+ if (str[cb_data - 1] != '\0') {
+ if (cb_data < cb_alloc)
+ str[cb_data] = '\0';
+ else
+ break;
+ }
+
+ if (ExpandEnvironmentStrings(str, expsz, sizeof(expsz)/sizeof(char)) != 0) {
+ ret_string = strdup(expsz);
+ } else {
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Overflow while expanding environment strings "
+ "for registry value %s", valuename);
+ }
+ }
+ break;
+
+ case REG_MULTI_SZ:
+ {
+ char * str = (char *) pbuffer;
+ char * iter;
+
+ str[cb_alloc - 1] = '\0';
+ str[cb_alloc - 2] = '\0';
+
+ for (iter = str; *iter;) {
+ size_t len = strlen(iter);
+
+ iter += len;
+ if (iter[1] != '\0')
+ *iter++ = *separator;
+ else
+ break;
+ }
+
+ if (pbuffer != static_buffer) {
+ ret_string = str;
+ pbuffer = NULL;
+ } else {
+ ret_string = strdup(str);
+ }
+ }
+ break;
+
+ default:
+ if (context)
+ krb5_set_error_message(context, 0,
+ "Unexpected type while reading registry value %s",
+ valuename);
+ }
+
+done:
+ if (pbuffer != static_buffer && pbuffer != NULL)
+ free(pbuffer);
+
+ return ret_string;
+}
+
+/**
+ * Parse a registry value as a configuration value
+ *
+ * @see parse_reg_value_as_string()
+ */
+static krb5_error_code
+parse_reg_value(krb5_context context,
+ HKEY key, const char * valuename,
+ DWORD type, DWORD cbdata, krb5_config_section ** parent)
+{
+ char *reg_string = NULL;
+ krb5_config_section *value;
+ krb5_error_code code = 0;
+
+ reg_string = _krb5_parse_reg_value_as_string(context, key, valuename, type, cbdata);
+
+ if (reg_string == NULL)
+ return KRB5_CONFIG_BADFORMAT;
+
+ value = _krb5_config_get_entry(parent, valuename, krb5_config_string);
+ if (value == NULL) {
+ code = ENOMEM;
+ goto done;
+ }
+
+ if (value->u.string != NULL)
+ free(value->u.string);
+
+ value->u.string = reg_string;
+ reg_string = NULL;
+
+done:
+ if (reg_string != NULL)
+ free(reg_string);
+
+ return code;
+}
+
+static krb5_error_code
+parse_reg_values(krb5_context context,
+ HKEY key,
+ krb5_config_section ** parent)
+{
+ DWORD index;
+ LONG rcode;
+
+ for (index = 0; ; index ++) {
+ char name[16385];
+ DWORD cch = sizeof(name)/sizeof(name[0]);
+ DWORD type;
+ DWORD cbdata = 0;
+ krb5_error_code code;
+
+ rcode = RegEnumValue(key, index, name, &cch, NULL,
+ &type, NULL, &cbdata);
+ if (rcode != ERROR_SUCCESS)
+ break;
+
+ if (cbdata == 0)
+ continue;
+
+ code = parse_reg_value(context, key, name, type, cbdata, parent);
+ if (code != 0)
+ return code;
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+parse_reg_subkeys(krb5_context context,
+ HKEY key,
+ krb5_config_section ** parent)
+{
+ DWORD index;
+ LONG rcode;
+
+ for (index = 0; ; index ++) {
+ HKEY subkey = NULL;
+ char name[256];
+ DWORD cch = sizeof(name)/sizeof(name[0]);
+ krb5_config_section *section = NULL;
+ krb5_error_code code;
+
+ rcode = RegEnumKeyEx(key, index, name, &cch, NULL, NULL, NULL, NULL);
+ if (rcode != ERROR_SUCCESS)
+ break;
+
+ rcode = RegOpenKeyEx(key, name, 0, KEY_READ, &subkey);
+ if (rcode != ERROR_SUCCESS)
+ continue;
+
+ section = _krb5_config_get_entry(parent, name, krb5_config_list);
+ if (section == NULL) {
+ RegCloseKey(subkey);
+ return ENOMEM;
+ }
+
+ code = parse_reg_values(context, subkey, &section->u.list);
+ if (code) {
+ RegCloseKey(subkey);
+ return code;
+ }
+
+ code = parse_reg_subkeys(context, subkey, &section->u.list);
+ if (code) {
+ RegCloseKey(subkey);
+ return code;
+ }
+
+ RegCloseKey(subkey);
+ }
+
+ return 0;
+}
+
+static krb5_error_code
+parse_reg_root(krb5_context context,
+ HKEY key,
+ krb5_config_section ** parent)
+{
+ krb5_config_section *libdefaults = NULL;
+ krb5_error_code code = 0;
+
+ libdefaults = _krb5_config_get_entry(parent, "libdefaults", krb5_config_list);
+ if (libdefaults == NULL)
+ return krb5_enomem(context);
+
+ code = parse_reg_values(context, key, &libdefaults->u.list);
+ if (code)
+ return code;
+
+ return parse_reg_subkeys(context, key, parent);
+}
+
+static krb5_error_code
+load_config_from_regpath(krb5_context context,
+ HKEY hk_root,
+ const char* key_path,
+ krb5_config_section ** res)
+{
+ HKEY key = NULL;
+ LONG rcode;
+ krb5_error_code code = 0;
+
+ rcode = RegOpenKeyEx(hk_root, key_path, 0, KEY_READ, &key);
+ if (rcode == ERROR_SUCCESS) {
+ code = parse_reg_root(context, key, res);
+ RegCloseKey(key);
+ key = NULL;
+ }
+
+ return code;
+}
+
+/**
+ * Load configuration from registry
+ *
+ * The registry keys 'HKCU\Software\Heimdal' and
+ * 'HKLM\Software\Heimdal' are treated as krb5.conf files. Each
+ * registry key corresponds to a configuration section (or bound list)
+ * and each value in a registry key is treated as a bound value. The
+ * set of values that are directly under the Heimdal key are treated
+ * as if they were defined in the [libdefaults] section.
+ *
+ * @see parse_reg_value() for details about how each type of value is handled.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_load_config_from_registry(krb5_context context,
+ krb5_config_section ** res)
+{
+ krb5_error_code code;
+
+ code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
+ REGPATH_KERBEROS, res);
+ if (code)
+ return code;
+
+ code = load_config_from_regpath(context, HKEY_LOCAL_MACHINE,
+ REGPATH_HEIMDAL, res);
+ if (code)
+ return code;
+
+ code = load_config_from_regpath(context, HKEY_CURRENT_USER,
+ REGPATH_KERBEROS, res);
+ if (code)
+ return code;
+
+ code = load_config_from_regpath(context, HKEY_CURRENT_USER,
+ REGPATH_HEIMDAL, res);
+ if (code)
+ return code;
+ return 0;
+}
diff --git a/lib/krb5/constants.c b/lib/krb5/constants.c
index bbb4832657fb..87147c22bcb7 100644
--- a/lib/krb5/constants.c
+++ b/lib/krb5/constants.c
@@ -36,6 +36,9 @@
#include "krb5_locl.h"
KRB5_LIB_VARIABLE const char *krb5_config_file =
+#ifdef KRB5_DEFAULT_CONFIG_FILE
+KRB5_DEFAULT_CONFIG_FILE
+#else
#ifdef __APPLE__
"~/Library/Preferences/com.apple.Kerberos.plist" PATH_SEP
"/Library/Preferences/com.apple.Kerberos.plist" PATH_SEP
@@ -43,13 +46,14 @@ KRB5_LIB_VARIABLE const char *krb5_config_file =
"/Library/Preferences/edu.mit.Kerberos" PATH_SEP
#endif /* __APPLE__ */
"~/.krb5/config" PATH_SEP
-SYSCONFDIR "/krb5.conf"
+SYSCONFDIR "/krb5.conf" PATH_SEP
#ifdef _WIN32
-PATH_SEP "%{COMMON_APPDATA}/Kerberos/krb5.conf"
-PATH_SEP "%{WINDOWS}/krb5.ini"
-#else
-PATH_SEP "/etc/krb5.conf"
-#endif
+"%{COMMON_APPDATA}/Kerberos/krb5.conf" PATH_SEP
+"%{WINDOWS}/krb5.ini"
+#else /* _WIN32 */
+"/etc/krb5.conf"
+#endif /* _WIN32 */
+#endif /* KRB5_DEFAULT_CONFIG_FILE */
;
KRB5_LIB_VARIABLE const char *krb5_defkeyname = KEYTAB_DEFAULT;
@@ -59,3 +63,4 @@ KRB5_LIB_VARIABLE const char *krb5_cc_type_file = "FILE";
KRB5_LIB_VARIABLE const char *krb5_cc_type_memory = "MEMORY";
KRB5_LIB_VARIABLE const char *krb5_cc_type_kcm = "KCM";
KRB5_LIB_VARIABLE const char *krb5_cc_type_scc = "SCC";
+KRB5_LIB_VARIABLE const char *krb5_cc_type_dcc = "DIR";
diff --git a/lib/krb5/context.c b/lib/krb5/context.c
index 99bf1b419b0a..5660f7f36b9b 100644
--- a/lib/krb5/context.c
+++ b/lib/krb5/context.c
@@ -48,6 +48,11 @@
} \
} while(0)
+static krb5_error_code
+copy_enctypes(krb5_context context,
+ const krb5_enctype *in,
+ krb5_enctype **out);
+
/*
* Set the list of etypes `ret_etypes' from the configuration variable
* `name'
@@ -69,8 +74,7 @@ set_etypes (krb5_context context,
etypes = malloc((i+1) * sizeof(*etypes));
if (etypes == NULL) {
krb5_config_free_strings (etypes_str);
- krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
for(j = 0, k = 0; j < i; j++) {
krb5_enctype e;
@@ -100,7 +104,8 @@ init_context_from_config_file(krb5_context context)
krb5_enctype *tmptypes;
INIT_FIELD(context, time, max_skew, 5 * 60, "clockskew");
- INIT_FIELD(context, time, kdc_timeout, 3, "kdc_timeout");
+ INIT_FIELD(context, time, kdc_timeout, 30, "kdc_timeout");
+ INIT_FIELD(context, time, host_timeout, 3, "host_timeout");
INIT_FIELD(context, int, max_retries, 3, "max_retries");
INIT_FIELD(context, string, http_proxy, NULL, "http_proxy");
@@ -123,6 +128,18 @@ init_context_from_config_file(krb5_context context)
free(context->etypes);
context->etypes = tmptypes;
+ /* The etypes member may change during the lifetime
+ * of the context. To be able to reset it to
+ * config value, we keep another copy.
+ */
+ free(context->cfg_etypes);
+ context->cfg_etypes = NULL;
+ if (tmptypes) {
+ ret = copy_enctypes(context, tmptypes, &context->cfg_etypes);
+ if (ret)
+ return ret;
+ }
+
ret = set_etypes (context, "default_etypes_des", &tmptypes);
if(ret)
return ret;
@@ -147,15 +164,8 @@ init_context_from_config_file(krb5_context context)
free(context->permitted_enctypes);
context->permitted_enctypes = tmptypes;
- /* default keytab name */
- tmp = NULL;
- if(!issuid())
- tmp = getenv("KRB5_KTNAME");
- if(tmp != NULL)
- context->default_keytab = tmp;
- else
- INIT_FIELD(context, string, default_keytab,
- KEYTAB_DEFAULT, "default_keytab_name");
+ INIT_FIELD(context, string, default_keytab,
+ KEYTAB_DEFAULT, "default_keytab_name");
INIT_FIELD(context, string, default_keytab_modify,
NULL, "default_keytab_modify_name");
@@ -220,14 +230,22 @@ init_context_from_config_file(krb5_context context)
INIT_FIELD(context, bool, srv_lookup, TRUE, "srv_lookup");
INIT_FIELD(context, bool, srv_lookup, context->srv_lookup, "dns_lookup_kdc");
INIT_FIELD(context, int, large_msg_size, 1400, "large_message_size");
+ INIT_FIELD(context, int, max_msg_size, 1000 * 1024, "maximum_message_size");
INIT_FLAG(context, flags, KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME, TRUE, "dns_canonicalize_hostname");
INIT_FLAG(context, flags, KRB5_CTX_F_CHECK_PAC, TRUE, "check_pac");
+
+ if (context->default_cc_name)
+ free(context->default_cc_name);
context->default_cc_name = NULL;
context->default_cc_name_set = 0;
s = krb5_config_get_strings(context, NULL, "logging", "krb5", NULL);
if(s) {
char **p;
+
+ if (context->debug_dest)
+ krb5_closelog(context, context->debug_dest);
+
krb5_initlog(context, "libkrb5", &context->debug_dest);
for(p = s; *p; p++)
krb5_addlog_dest(context, context->debug_dest, *p);
@@ -242,6 +260,11 @@ init_context_from_config_file(krb5_context context)
if (strcasecmp(tmp, "ignore") == 0)
context->flags |= KRB5_CTX_F_RD_REQ_IGNORE;
}
+ ret = krb5_config_get_bool_default(context, NULL, TRUE,
+ "libdefaults",
+ "fcache_strict_checking", NULL);
+ if (ret)
+ context->flags |= KRB5_CTX_F_FCACHE_STRICT_CHECKING;
return 0;
}
@@ -256,6 +279,7 @@ cc_ops_register(krb5_context context)
krb5_cc_register(context, &krb5_acc_ops, TRUE);
#endif
krb5_cc_register(context, &krb5_fcc_ops, TRUE);
+ krb5_cc_register(context, &krb5_dcc_ops, TRUE);
krb5_cc_register(context, &krb5_mcc_ops, TRUE);
#ifdef HAVE_SCC
krb5_cc_register(context, &krb5_scc_ops, TRUE);
@@ -323,11 +347,8 @@ kt_ops_copy(krb5_context context, const krb5_context src_context)
return 0;
context->kt_types = malloc(sizeof(context->kt_types[0]) * src_context->num_kt_types);
- if (context->kt_types == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (context->kt_types == NULL)
+ return krb5_enomem(context);
context->num_kt_types = src_context->num_kt_types;
memcpy(context->kt_types, src_context->kt_types,
@@ -337,11 +358,18 @@ kt_ops_copy(krb5_context context, const krb5_context src_context)
}
static const char *sysplugin_dirs[] = {
- LIBDIR "/plugin/krb5",
+#ifdef _WIN32
+ "$ORIGIN",
+#else
+ "$ORIGIN/../lib/plugin/krb5",
+#endif
#ifdef __APPLE__
+ LIBDIR "/plugin/krb5",
+#ifdef HEIM_PLUGINS_SEARCH_SYSTEM
"/Library/KerberosPlugins/KerberosFrameworkPlugins",
"/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
#endif
+#endif
NULL
};
@@ -349,8 +377,21 @@ static void
init_context_once(void *ctx)
{
krb5_context context = ctx;
+ char **dirs;
- _krb5_load_plugins(context, "krb5", sysplugin_dirs);
+#ifdef _WIN32
+ dirs = rk_UNCONST(sysplugin_dirs);
+#else
+ dirs = krb5_config_get_strings(context, NULL, "libdefaults",
+ "plugin_dir", NULL);
+ if (dirs == NULL)
+ dirs = rk_UNCONST(sysplugin_dirs);
+#endif
+
+ _krb5_load_plugins(context, "krb5", (const char **)dirs);
+
+ if (dirs != rk_UNCONST(sysplugin_dirs))
+ krb5_config_free_strings(dirs);
bindtextdomain(HEIMDAL_TEXTDOMAIN, HEIMDAL_LOCALEDIR);
}
@@ -366,7 +407,8 @@ init_context_once(void *ctx)
* @return Returns 0 to indicate success. Otherwise an errno code is
* returned. Failure means either that something bad happened during
* initialization (typically ENOMEM) or that Kerberos should not be
- * used ENXIO.
+ * used ENXIO. If the function returns HEIM_ERR_RANDOM_OFFLINE, the
+ * random source is not available and later Kerberos calls might fail.
*
* @ingroup krb5
*/
@@ -378,19 +420,28 @@ krb5_init_context(krb5_context *context)
krb5_context p;
krb5_error_code ret;
char **files;
+ uint8_t rnd;
*context = NULL;
+ /**
+ * krb5_init_context() will get one random byte to make sure our
+ * random is alive. Assumption is that once the non blocking
+ * source allows us to pull bytes, its all seeded and allows us to
+ * pull more bytes.
+ *
+ * Most Kerberos users calls krb5_init_context(), so this is
+ * useful point where we can do the checking.
+ */
+ ret = krb5_generate_random(&rnd, sizeof(rnd));
+ if (ret)
+ return ret;
+
p = calloc(1, sizeof(*p));
if(!p)
return ENOMEM;
- p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
- if (p->mutex == NULL) {
- free(p);
- return ENOMEM;
- }
- HEIMDAL_MUTEX_init(p->mutex);
+ HEIMDAL_MUTEX_init(&p->mutex);
p->flags |= KRB5_CTX_F_HOMEDIR_ACCESS;
@@ -402,6 +453,9 @@ krb5_init_context(krb5_context *context)
if(ret)
goto out;
+ /* done enough to load plugins */
+ heim_base_once_f(&init_context, p, init_context_once);
+
/* init error tables */
krb5_init_ets(p);
cc_ops_register(p);
@@ -419,8 +473,6 @@ out:
if(ret) {
krb5_free_context(p);
p = NULL;
- } else {
- heim_base_once_f(&init_context, p, init_context_once);
}
*context = p;
return ret;
@@ -450,13 +502,10 @@ copy_etypes (krb5_context context,
;
i++;
- *ret_enctypes = malloc(sizeof(ret_enctypes[0]) * i);
- if (*ret_enctypes == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
- memcpy(*ret_enctypes, enctypes, sizeof(ret_enctypes[0]) * i);
+ *ret_enctypes = malloc(sizeof(enctypes[0]) * i);
+ if (*ret_enctypes == NULL)
+ return krb5_enomem(context);
+ memcpy(*ret_enctypes, enctypes, sizeof(enctypes[0]) * i);
return 0;
}
@@ -482,19 +531,10 @@ krb5_copy_context(krb5_context context, krb5_context *out)
*out = NULL;
p = calloc(1, sizeof(*p));
- if (p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
-
- p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
- if (p->mutex == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- free(p);
- return ENOMEM;
- }
- HEIMDAL_MUTEX_init(p->mutex);
+ if (p == NULL)
+ return krb5_enomem(context);
+ HEIMDAL_MUTEX_init(&p->mutex);
if (context->default_cc_name)
p->default_cc_name = strdup(context->default_cc_name);
@@ -506,6 +546,11 @@ krb5_copy_context(krb5_context context, krb5_context *out)
if (ret)
goto out;
}
+ if (context->cfg_etypes) {
+ ret = copy_etypes(context, context->cfg_etypes, &p->cfg_etypes);
+ if (ret)
+ goto out;
+ }
if (context->etypes_des) {
ret = copy_etypes(context, context->etypes_des, &p->etypes_des);
if (ret)
@@ -569,11 +614,13 @@ krb5_copy_context(krb5_context context, krb5_context *out)
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_context(krb5_context context)
{
+ _krb5_free_name_canon_rules(context, context->name_canon_rules);
if (context->default_cc_name)
free(context->default_cc_name);
if (context->default_cc_name_env)
free(context->default_cc_name_env);
free(context->etypes);
+ free(context->cfg_etypes);
free(context->etypes_des);
krb5_free_host_realm (context, context->default_realms);
krb5_config_file_free (context, context->cf);
@@ -594,8 +641,7 @@ krb5_free_context(krb5_context context)
hx509_context_free(&context->hx509ctx);
#endif
- HEIMDAL_MUTEX_destroy(context->mutex);
- free(context->mutex);
+ HEIMDAL_MUTEX_destroy(&context->mutex);
if (context->flags & KRB5_CTX_F_SOCKETS_INITIALIZED) {
rk_SOCK_EXIT();
}
@@ -623,7 +669,8 @@ krb5_set_config_files(krb5_context context, char **filenames)
krb5_config_binding *tmp = NULL;
while(filenames != NULL && *filenames != NULL && **filenames != '\0') {
ret = krb5_config_parse_file_multi(context, *filenames, &tmp);
- if(ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM) {
+ if (ret != 0 && ret != ENOENT && ret != EACCES && ret != EPERM
+ && ret != KRB5_CONFIG_BADFORMAT) {
krb5_config_file_free(context, tmp);
return ret;
}
@@ -769,7 +816,7 @@ krb5_prepend_config_files_default(const char *filelist, char ***pfilenames)
* to find the configuration file location in the
* SOFTWARE\MIT\Kerberos registry key under the value "config".
*/
-char *
+KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
_krb5_get_default_config_config_files_from_registry()
{
static const char * KeyName = "Software\\MIT\\Kerberos";
@@ -881,6 +928,18 @@ krb5_kerberos_enctypes(krb5_context context)
static const krb5_enctype p[] = {
ETYPE_AES256_CTS_HMAC_SHA1_96,
ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA384_192,
+ ETYPE_AES128_CTS_HMAC_SHA256_128,
+ ETYPE_DES3_CBC_SHA1,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_NULL
+ };
+
+ static const krb5_enctype weak[] = {
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA1_96,
+ ETYPE_AES256_CTS_HMAC_SHA384_192,
+ ETYPE_AES128_CTS_HMAC_SHA256_128,
ETYPE_DES3_CBC_SHA1,
ETYPE_DES3_CBC_MD5,
ETYPE_ARCFOUR_HMAC_MD5,
@@ -889,6 +948,20 @@ krb5_kerberos_enctypes(krb5_context context)
ETYPE_DES_CBC_CRC,
ETYPE_NULL
};
+
+ /*
+ * if the list of enctypes enabled by "allow_weak_crypto"
+ * are valid, then return the former default enctype list
+ * that contained the weak entries.
+ */
+ if (krb5_enctype_valid(context, ETYPE_DES_CBC_CRC) == 0 &&
+ krb5_enctype_valid(context, ETYPE_DES_CBC_MD4) == 0 &&
+ krb5_enctype_valid(context, ETYPE_DES_CBC_MD5) == 0 &&
+ krb5_enctype_valid(context, ETYPE_DES_CBC_NONE) == 0 &&
+ krb5_enctype_valid(context, ETYPE_DES_CFB64_NONE) == 0 &&
+ krb5_enctype_valid(context, ETYPE_DES_PCBC_NONE) == 0)
+ return weak;
+
return p;
}
@@ -944,6 +1017,8 @@ default_etypes(krb5_context context, krb5_enctype **etype)
*
* @param context Kerberos 5 context.
* @param etypes Encryption types, array terminated with ETYPE_NULL (0).
+ * A value of NULL resets the encryption types to the defaults set in the
+ * configuration file.
*
* @return Returns 0 to indicate success. Otherwise an kerberos et
* error code is returned, see krb5_get_error_message().
@@ -958,6 +1033,10 @@ krb5_set_default_in_tkt_etypes(krb5_context context,
krb5_error_code ret;
krb5_enctype *p = NULL;
+ if(!etypes) {
+ etypes = context->cfg_etypes;
+ }
+
if(etypes) {
ret = copy_enctypes(context, etypes, &p);
if (ret)
@@ -974,6 +1053,7 @@ krb5_set_default_in_tkt_etypes(krb5_context context,
* with the KDC, clients and servers.
*
* @param context Kerberos 5 context.
+ * @param pdu_type request type (AS, TGS or none)
* @param etypes Encryption types, array terminated with
* ETYPE_NULL(0), caller should free array with krb5_xfree():
*
@@ -994,7 +1074,7 @@ krb5_get_default_in_tkt_etypes(krb5_context context,
heim_assert(pdu_type == KRB5_PDU_AS_REQUEST ||
pdu_type == KRB5_PDU_TGS_REQUEST ||
- pdu_type == KRB5_PDU_NONE, "pdu contant not as expected");
+ pdu_type == KRB5_PDU_NONE, "unexpected pdu type");
if (pdu_type == KRB5_PDU_AS_REQUEST && context->as_etypes != NULL)
enctypes = context->as_etypes;
@@ -1133,10 +1213,8 @@ krb5_set_extra_addresses(krb5_context context, const krb5_addresses *addresses)
}
if(context->extra_addresses == NULL) {
context->extra_addresses = malloc(sizeof(*context->extra_addresses));
- if(context->extra_addresses == NULL) {
- krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (context->extra_addresses == NULL)
+ return krb5_enomem(context);
}
return krb5_copy_addresses(context, addresses, context->extra_addresses);
}
@@ -1215,10 +1293,8 @@ krb5_set_ignore_addresses(krb5_context context, const krb5_addresses *addresses)
}
if(context->ignore_addresses == NULL) {
context->ignore_addresses = malloc(sizeof(*context->ignore_addresses));
- if(context->ignore_addresses == NULL) {
- krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (context->ignore_addresses == NULL)
+ return krb5_enomem(context);
}
return krb5_copy_addresses(context, addresses, context->ignore_addresses);
}
@@ -1457,17 +1533,11 @@ _krb5_init_etype(krb5_context context,
static HEIMDAL_MUTEX homedir_mutex = HEIMDAL_MUTEX_INITIALIZER;
static krb5_boolean allow_homedir = TRUE;
-krb5_boolean
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_homedir_access(krb5_context context)
{
krb5_boolean allow;
-#ifdef HAVE_GETEUID
- /* is never allowed for root */
- if (geteuid() == 0)
- return FALSE;
-#endif
-
if (context && (context->flags & KRB5_CTX_F_HOMEDIR_ACCESS) == 0)
return FALSE;
@@ -1486,8 +1556,6 @@ _krb5_homedir_access(krb5_context context)
* For home directory access to be allowed, both the global state and
* the krb5_context state have to be allowed.
*
- * Administrator (root user), never uses the home directory.
- *
* @param context a Kerberos 5 context or NULL
* @param allow allow if TRUE home directory
* @return the old value
diff --git a/lib/krb5/copy_host_realm.c b/lib/krb5/copy_host_realm.c
index 73bc117f12af..f68d0d7dc464 100644
--- a/lib/krb5/copy_host_realm.c
+++ b/lib/krb5/copy_host_realm.c
@@ -58,19 +58,14 @@ krb5_copy_host_realm(krb5_context context,
++n;
*to = calloc (n, sizeof(**to));
- if (*to == NULL) {
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*to == NULL)
+ return krb5_enomem(context);
for (i = 0, p = from; *p != NULL; ++p, ++i) {
(*to)[i] = strdup(*p);
if ((*to)[i] == NULL) {
krb5_free_host_realm (context, *to);
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
}
return 0;
diff --git a/lib/krb5/crc.c b/lib/krb5/crc.c
index eab946541db7..3090760d8190 100644
--- a/lib/krb5/crc.c
+++ b/lib/krb5/crc.c
@@ -37,7 +37,7 @@ static u_long table[256];
#define CRC_GEN 0xEDB88320L
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_crc_init_table(void)
{
static int flag = 0;
@@ -60,7 +60,7 @@ _krb5_crc_init_table(void)
flag = 1;
}
-uint32_t
+KRB5_LIB_FUNCTION uint32_t KRB5_LIB_CALL
_krb5_crc_update (const char *p, size_t len, uint32_t res)
{
while (len--)
diff --git a/lib/krb5/creds.c b/lib/krb5/creds.c
index 7ef8eb96095f..16e3f5780802 100644
--- a/lib/krb5/creds.c
+++ b/lib/krb5/creds.c
@@ -138,13 +138,9 @@ krb5_copy_creds (krb5_context context,
{
krb5_creds *c;
- c = malloc (sizeof (*c));
- if (c == NULL) {
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
- memset (c, 0, sizeof(*c));
+ c = calloc(1, sizeof(*c));
+ if (c == NULL)
+ return krb5_enomem(context);
*outcred = c;
return krb5_copy_creds_contents (context, incred, c);
}
@@ -164,8 +160,9 @@ krb5_copy_creds (krb5_context context,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_creds (krb5_context context, krb5_creds *c)
{
- krb5_free_cred_contents (context, c);
- free (c);
+ if (c != NULL)
+ krb5_free_cred_contents(context, c);
+ free(c);
return 0;
}
diff --git a/lib/krb5/crypto-aes.c b/lib/krb5/crypto-aes-sha1.c
index b97854206ced..30df0ee86b84 100644
--- a/lib/krb5/crypto-aes.c
+++ b/lib/krb5/crypto-aes-sha1.c
@@ -37,29 +37,29 @@
* AES
*/
-static struct _krb5_key_type keytype_aes128 = {
- ENCTYPE_AES128_CTS_HMAC_SHA1_96,
+static struct _krb5_key_type keytype_aes128_sha1 = {
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
"aes-128",
128,
16,
sizeof(struct _krb5_evp_schedule),
NULL,
_krb5_evp_schedule,
- _krb5_AES_salt,
+ _krb5_AES_SHA1_salt,
NULL,
_krb5_evp_cleanup,
EVP_aes_128_cbc
};
-static struct _krb5_key_type keytype_aes256 = {
- ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+static struct _krb5_key_type keytype_aes256_sha1 = {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
"aes-256",
256,
32,
sizeof(struct _krb5_evp_schedule),
NULL,
_krb5_evp_schedule,
- _krb5_AES_salt,
+ _krb5_AES_SHA1_salt,
NULL,
_krb5_evp_cleanup,
EVP_aes_256_cbc
@@ -86,10 +86,10 @@ struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256 = {
};
static krb5_error_code
-AES_PRF(krb5_context context,
- krb5_crypto crypto,
- const krb5_data *in,
- krb5_data *out)
+AES_SHA1_PRF(krb5_context context,
+ krb5_crypto crypto,
+ const krb5_data *in,
+ krb5_data *out)
{
struct _krb5_checksum_type *ct = crypto->et->checksum;
krb5_error_code ret;
@@ -142,29 +142,31 @@ AES_PRF(krb5_context context,
struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1 = {
ETYPE_AES128_CTS_HMAC_SHA1_96,
"aes128-cts-hmac-sha1-96",
+ "aes128-cts",
16,
1,
16,
- &keytype_aes128,
+ &keytype_aes128_sha1,
&_krb5_checksum_sha1,
&_krb5_checksum_hmac_sha1_aes128,
- F_DERIVED,
+ F_DERIVED | F_RFC3961_ENC | F_RFC3961_KDF,
_krb5_evp_encrypt_cts,
16,
- AES_PRF
+ AES_SHA1_PRF
};
struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1 = {
ETYPE_AES256_CTS_HMAC_SHA1_96,
"aes256-cts-hmac-sha1-96",
+ "aes256-cts",
16,
1,
16,
- &keytype_aes256,
+ &keytype_aes256_sha1,
&_krb5_checksum_sha1,
&_krb5_checksum_hmac_sha1_aes256,
- F_DERIVED,
+ F_DERIVED | F_RFC3961_ENC | F_RFC3961_KDF,
_krb5_evp_encrypt_cts,
16,
- AES_PRF
+ AES_SHA1_PRF
};
diff --git a/lib/krb5/crypto-aes-sha2.c b/lib/krb5/crypto-aes-sha2.c
new file mode 100644
index 000000000000..4630ce071527
--- /dev/null
+++ b/lib/krb5/crypto-aes-sha2.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+/*
+ * AES HMAC-SHA2
+ */
+
+krb5_error_code
+_krb5_aes_sha2_md_for_enctype(krb5_context context,
+ krb5_enctype enctype,
+ const EVP_MD **md)
+{
+ switch (enctype) {
+ case ETYPE_AES128_CTS_HMAC_SHA256_128:
+ *md = EVP_sha256();
+ break;
+ case ETYPE_AES256_CTS_HMAC_SHA384_192:
+ *md = EVP_sha384();
+ break;
+ default:
+ return KRB5_PROG_ETYPE_NOSUPP;
+ break;
+ }
+ return 0;
+}
+
+static krb5_error_code
+SP_HMAC_SHA2_checksum(krb5_context context,
+ struct _krb5_key_data *key,
+ const void *data,
+ size_t len,
+ unsigned usage,
+ Checksum *result)
+{
+ krb5_error_code ret;
+ const EVP_MD *md;
+ unsigned char hmac[EVP_MAX_MD_SIZE];
+ unsigned int hmaclen = sizeof(hmac);
+
+ ret = _krb5_aes_sha2_md_for_enctype(context, key->key->keytype, &md);
+ if (ret)
+ return ret;
+
+ HMAC(md, key->key->keyvalue.data, key->key->keyvalue.length,
+ data, len, hmac, &hmaclen);
+
+ heim_assert(result->checksum.length <= hmaclen, "SHA2 internal error");
+
+ memcpy(result->checksum.data, hmac, result->checksum.length);
+
+ return 0;
+}
+
+static struct _krb5_key_type keytype_aes128_sha2 = {
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ "aes-128-sha2",
+ 128,
+ 16,
+ sizeof(struct _krb5_evp_schedule),
+ NULL,
+ _krb5_evp_schedule,
+ _krb5_AES_SHA2_salt,
+ NULL,
+ _krb5_evp_cleanup,
+ EVP_aes_128_cbc
+};
+
+static struct _krb5_key_type keytype_aes256_sha2 = {
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
+ "aes-256-sha2",
+ 256,
+ 32,
+ sizeof(struct _krb5_evp_schedule),
+ NULL,
+ _krb5_evp_schedule,
+ _krb5_AES_SHA2_salt,
+ NULL,
+ _krb5_evp_cleanup,
+ EVP_aes_256_cbc
+};
+
+struct _krb5_checksum_type _krb5_checksum_hmac_sha256_128_aes128 = {
+ CKSUMTYPE_HMAC_SHA256_128_AES128,
+ "hmac-sha256-128-aes128",
+ 64,
+ 16,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA2_checksum,
+ NULL
+};
+
+struct _krb5_checksum_type _krb5_checksum_hmac_sha384_192_aes256 = {
+ CKSUMTYPE_HMAC_SHA384_192_AES256,
+ "hmac-sha384-192-aes256",
+ 128,
+ 24,
+ F_KEYED | F_CPROOF | F_DERIVED,
+ SP_HMAC_SHA2_checksum,
+ NULL
+};
+
+static krb5_error_code
+AES_SHA2_PRF(krb5_context context,
+ krb5_crypto crypto,
+ const krb5_data *in,
+ krb5_data *out)
+{
+ krb5_error_code ret;
+ krb5_data label;
+ const EVP_MD *md = NULL;
+
+ ret = _krb5_aes_sha2_md_for_enctype(context, crypto->et->type, &md);
+ if (ret)
+ return ret;
+
+ label.data = "prf";
+ label.length = 3;
+
+ ret = krb5_data_alloc(out, EVP_MD_size(md));
+ if (ret)
+ return ret;
+
+ ret = _krb5_SP800_108_HMAC_KDF(context, &crypto->key.key->keyvalue,
+ &label, in, md, out);
+
+ if (ret)
+ krb5_data_free(out);
+
+ return ret;
+}
+
+struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha256_128 = {
+ ETYPE_AES128_CTS_HMAC_SHA256_128,
+ "aes128-cts-hmac-sha256-128",
+ "aes128-cts-sha256",
+ 16,
+ 1,
+ 16,
+ &keytype_aes128_sha2,
+ NULL, /* should never be called */
+ &_krb5_checksum_hmac_sha256_128_aes128,
+ F_DERIVED | F_ENC_THEN_CKSUM | F_SP800_108_HMAC_KDF,
+ _krb5_evp_encrypt_cts,
+ 16,
+ AES_SHA2_PRF
+};
+
+struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha384_192 = {
+ ETYPE_AES256_CTS_HMAC_SHA384_192,
+ "aes256-cts-hmac-sha384-192",
+ "aes256-cts-sha384",
+ 16,
+ 1,
+ 16,
+ &keytype_aes256_sha2,
+ NULL, /* should never be called */
+ &_krb5_checksum_hmac_sha384_192_aes256,
+ F_DERIVED | F_ENC_THEN_CKSUM | F_SP800_108_HMAC_KDF,
+ _krb5_evp_encrypt_cts,
+ 16,
+ AES_SHA2_PRF
+};
diff --git a/lib/krb5/crypto-algs.c b/lib/krb5/crypto-algs.c
index ed31377bd070..c0540257a441 100644
--- a/lib/krb5/crypto-algs.c
+++ b/lib/krb5/crypto-algs.c
@@ -53,6 +53,8 @@ struct _krb5_checksum_type *_krb5_checksum_types[] = {
&_krb5_checksum_hmac_sha1_des3,
&_krb5_checksum_hmac_sha1_aes128,
&_krb5_checksum_hmac_sha1_aes256,
+ &_krb5_checksum_hmac_sha256_128_aes128,
+ &_krb5_checksum_hmac_sha384_192_aes256,
&_krb5_checksum_hmac_md5
};
@@ -64,6 +66,8 @@ int _krb5_num_checksums
* (only relevant for !F_PSEUDO) */
struct _krb5_encryption_type *_krb5_etypes[] = {
+ &_krb5_enctype_aes256_cts_hmac_sha384_192,
+ &_krb5_enctype_aes128_cts_hmac_sha256_128,
&_krb5_enctype_aes256_cts_hmac_sha1,
&_krb5_enctype_aes128_cts_hmac_sha1,
&_krb5_enctype_des3_cbc_sha1,
diff --git a/lib/krb5/crypto-arcfour.c b/lib/krb5/crypto-arcfour.c
index 7f7e21d0d22c..e195bd3a0d7b 100644
--- a/lib/krb5/crypto-arcfour.c
+++ b/lib/krb5/crypto-arcfour.c
@@ -38,7 +38,7 @@
#include "krb5_locl.h"
static struct _krb5_key_type keytype_arcfour = {
- ENCTYPE_ARCFOUR_HMAC_MD5,
+ KRB5_ENCTYPE_ARCFOUR_HMAC_MD5,
"arcfour",
128,
16,
@@ -75,10 +75,8 @@ _krb5_HMAC_MD5_checksum(krb5_context context,
krb5_error_code ret;
m = EVP_MD_CTX_create();
- if (m == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (m == NULL)
+ return krb5_enomem(context);
ksign_c.checksum.length = sizeof(ksign_c_data);
ksign_c.checksum.data = ksign_c_data;
ret = _krb5_internal_hmac(context, c, signature, sizeof(signature),
@@ -147,7 +145,7 @@ ARCFOUR_subencrypt(krb5_context context,
k1_c.checksum.length = sizeof(k1_c_data);
k1_c.checksum.data = k1_c_data;
- ret = _krb5_internal_hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
+ ret = _krb5_internal_hmac(context, c, t, sizeof(t), 0, key, &k1_c);
if (ret)
krb5_abortx(context, "hmac failed");
@@ -162,7 +160,7 @@ ARCFOUR_subencrypt(krb5_context context,
cksum.checksum.length = 16;
cksum.checksum.data = data;
- ret = _krb5_internal_hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
+ ret = _krb5_internal_hmac(context, c, cdata + 16, len - 16, 0, &ke, &cksum);
if (ret)
krb5_abortx(context, "hmac failed");
@@ -172,7 +170,7 @@ ARCFOUR_subencrypt(krb5_context context,
k3_c.checksum.length = sizeof(k3_c_data);
k3_c.checksum.data = k3_c_data;
- ret = _krb5_internal_hmac(NULL, c, data, 16, 0, &ke, &k3_c);
+ ret = _krb5_internal_hmac(context, c, data, 16, 0, &ke, &k3_c);
if (ret)
krb5_abortx(context, "hmac failed");
@@ -215,7 +213,7 @@ ARCFOUR_subdecrypt(krb5_context context,
k1_c.checksum.length = sizeof(k1_c_data);
k1_c.checksum.data = k1_c_data;
- ret = _krb5_internal_hmac(NULL, c, t, sizeof(t), 0, key, &k1_c);
+ ret = _krb5_internal_hmac(context, c, t, sizeof(t), 0, key, &k1_c);
if (ret)
krb5_abortx(context, "hmac failed");
@@ -230,7 +228,7 @@ ARCFOUR_subdecrypt(krb5_context context,
k3_c.checksum.length = sizeof(k3_c_data);
k3_c.checksum.data = k3_c_data;
- ret = _krb5_internal_hmac(NULL, c, cdata, 16, 0, &ke, &k3_c);
+ ret = _krb5_internal_hmac(context, c, cdata, 16, 0, &ke, &k3_c);
if (ret)
krb5_abortx(context, "hmac failed");
@@ -245,7 +243,7 @@ ARCFOUR_subdecrypt(krb5_context context,
cksum.checksum.length = 16;
cksum.checksum.data = cksum_data;
- ret = _krb5_internal_hmac(NULL, c, cdata + 16, len - 16, 0, &ke, &cksum);
+ ret = _krb5_internal_hmac(context, c, cdata + 16, len - 16, 0, &ke, &cksum);
if (ret)
krb5_abortx(context, "hmac failed");
@@ -267,7 +265,7 @@ ARCFOUR_subdecrypt(krb5_context context,
* draft-brezak-win2k-krb-rc4-hmac-04.txt
*/
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_usage2arcfour(krb5_context context, unsigned *usage)
{
switch (*usage) {
@@ -309,17 +307,42 @@ ARCFOUR_encrypt(krb5_context context,
return ARCFOUR_subdecrypt (context, key, data, len, keyusage, ivec);
}
+static krb5_error_code
+ARCFOUR_prf(krb5_context context,
+ krb5_crypto crypto,
+ const krb5_data *in,
+ krb5_data *out)
+{
+ struct _krb5_checksum_type *c = _krb5_find_checksum(CKSUMTYPE_SHA1);
+ krb5_error_code ret;
+ Checksum res;
+
+ ret = krb5_data_alloc(out, c->checksumsize);
+ if (ret)
+ return ret;
+
+ res.checksum.data = out->data;
+ res.checksum.length = out->length;
+
+ ret = _krb5_internal_hmac(context, c, in->data, in->length, 0, &crypto->key, &res);
+ if (ret)
+ krb5_data_free(out);
+ return 0;
+}
+
+
struct _krb5_encryption_type _krb5_enctype_arcfour_hmac_md5 = {
ETYPE_ARCFOUR_HMAC_MD5,
"arcfour-hmac-md5",
+ "rc4-hmac",
1,
1,
8,
&keytype_arcfour,
&_krb5_checksum_hmac_md5,
&_krb5_checksum_hmac_md5,
- F_SPECIAL,
+ F_SPECIAL | F_WEAK,
ARCFOUR_encrypt,
0,
- NULL
+ ARCFOUR_prf
};
diff --git a/lib/krb5/crypto-des-common.c b/lib/krb5/crypto-des-common.c
index f8313952dc7c..2565914de830 100644
--- a/lib/krb5/crypto-des-common.c
+++ b/lib/krb5/crypto-des-common.c
@@ -39,10 +39,9 @@
* A = A xor B. A & B are 8 bytes.
*/
-void
-_krb5_xor (DES_cblock *key, const unsigned char *b)
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_xor8(unsigned char *a, const unsigned char *b)
{
- unsigned char *a = (unsigned char*)key;
a[0] ^= b[0];
a[1] ^= b[1];
a[2] ^= b[2];
@@ -54,7 +53,7 @@ _krb5_xor (DES_cblock *key, const unsigned char *b)
}
#if defined(DES3_OLD_ENCTYPE) || defined(HEIM_WEAK_CRYPTO)
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_des_checksum(krb5_context context,
const EVP_MD *evp_md,
struct _krb5_key_data *key,
@@ -70,10 +69,8 @@ _krb5_des_checksum(krb5_context context,
krb5_generate_random_block(p, 8);
m = EVP_MD_CTX_create();
- if (m == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (m == NULL)
+ return krb5_enomem(context);
EVP_DigestInit_ex(m, evp_md, NULL);
EVP_DigestUpdate(m, p, 8);
@@ -87,7 +84,7 @@ _krb5_des_checksum(krb5_context context,
return 0;
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_des_verify(krb5_context context,
const EVP_MD *evp_md,
struct _krb5_key_data *key,
@@ -103,10 +100,8 @@ _krb5_des_verify(krb5_context context,
krb5_error_code ret = 0;
m = EVP_MD_CTX_create();
- if (m == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (m == NULL)
+ return krb5_enomem(context);
memset(&ivec, 0, sizeof(ivec));
EVP_CipherInit_ex(&ctx->dctx, NULL, NULL, NULL, (void *)&ivec, -1);
diff --git a/lib/krb5/crypto-des.c b/lib/krb5/crypto-des.c
index f6f1c6881c91..8ea145b56d4b 100644
--- a/lib/krb5/crypto-des.c
+++ b/lib/krb5/crypto-des.c
@@ -65,7 +65,7 @@ krb5_DES_random_to_key(krb5_context context,
memcpy(k, data, key->keyvalue.length);
DES_set_odd_parity(k);
if(DES_is_weak_key(k))
- _krb5_xor(k, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ _krb5_xor8(*k, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
}
static struct _krb5_key_type keytype_des_old = {
@@ -288,6 +288,7 @@ DES_PCBC_encrypt_key_ivec(krb5_context context,
struct _krb5_encryption_type _krb5_enctype_des_cbc_crc = {
ETYPE_DES_CBC_CRC,
"des-cbc-crc",
+ NULL,
8,
8,
8,
@@ -303,6 +304,7 @@ struct _krb5_encryption_type _krb5_enctype_des_cbc_crc = {
struct _krb5_encryption_type _krb5_enctype_des_cbc_md4 = {
ETYPE_DES_CBC_MD4,
"des-cbc-md4",
+ NULL,
8,
8,
8,
@@ -318,6 +320,7 @@ struct _krb5_encryption_type _krb5_enctype_des_cbc_md4 = {
struct _krb5_encryption_type _krb5_enctype_des_cbc_md5 = {
ETYPE_DES_CBC_MD5,
"des-cbc-md5",
+ NULL,
8,
8,
8,
@@ -333,6 +336,7 @@ struct _krb5_encryption_type _krb5_enctype_des_cbc_md5 = {
struct _krb5_encryption_type _krb5_enctype_des_cbc_none = {
ETYPE_DES_CBC_NONE,
"des-cbc-none",
+ NULL,
8,
8,
0,
@@ -348,6 +352,7 @@ struct _krb5_encryption_type _krb5_enctype_des_cbc_none = {
struct _krb5_encryption_type _krb5_enctype_des_cfb64_none = {
ETYPE_DES_CFB64_NONE,
"des-cfb64-none",
+ NULL,
1,
1,
0,
@@ -363,6 +368,7 @@ struct _krb5_encryption_type _krb5_enctype_des_cfb64_none = {
struct _krb5_encryption_type _krb5_enctype_des_pcbc_none = {
ETYPE_DES_PCBC_NONE,
"des-pcbc-none",
+ NULL,
8,
8,
0,
diff --git a/lib/krb5/crypto-des3.c b/lib/krb5/crypto-des3.c
index 43806038b714..ed3e7c960e99 100644
--- a/lib/krb5/crypto-des3.c
+++ b/lib/krb5/crypto-des3.c
@@ -52,6 +52,59 @@ DES3_random_key(krb5_context context,
DES_is_weak_key(&k[2]));
}
+static krb5_error_code
+DES3_prf(krb5_context context,
+ krb5_crypto crypto,
+ const krb5_data *in,
+ krb5_data *out)
+{
+ struct _krb5_checksum_type *ct = crypto->et->checksum;
+ krb5_error_code ret;
+ Checksum result;
+ krb5_keyblock *derived;
+
+ result.cksumtype = ct->type;
+ ret = krb5_data_alloc(&result.checksum, ct->checksumsize);
+ if (ret) {
+ krb5_set_error_message(context, ret, N_("malloc: out memory", ""));
+ return ret;
+ }
+
+ ret = (*ct->checksum)(context, NULL, in->data, in->length, 0, &result);
+ if (ret) {
+ krb5_data_free(&result.checksum);
+ return ret;
+ }
+
+ if (result.checksum.length < crypto->et->blocksize)
+ krb5_abortx(context, "internal prf error");
+
+ derived = NULL;
+ ret = krb5_derive_key(context, crypto->key.key,
+ crypto->et->type, "prf", 3, &derived);
+ if (ret)
+ krb5_abortx(context, "krb5_derive_key");
+
+ ret = krb5_data_alloc(out, crypto->et->prf_length);
+ if (ret)
+ krb5_abortx(context, "malloc failed");
+
+ {
+ const EVP_CIPHER *c = (*crypto->et->keytype->evp)();
+ EVP_CIPHER_CTX ctx;
+
+ EVP_CIPHER_CTX_init(&ctx); /* ivec all zero */
+ EVP_CipherInit_ex(&ctx, c, NULL, derived->keyvalue.data, NULL, 1);
+ EVP_Cipher(&ctx, out->data, result.checksum.data,
+ crypto->et->prf_length);
+ EVP_CIPHER_CTX_cleanup(&ctx);
+ }
+
+ krb5_data_free(&result.checksum);
+ krb5_free_keyblock(context, derived);
+
+ return ret;
+}
#ifdef DES3_OLD_ENCTYPE
static struct _krb5_key_type keytype_des3 = {
@@ -131,6 +184,7 @@ struct _krb5_checksum_type _krb5_checksum_hmac_sha1_des3 = {
struct _krb5_encryption_type _krb5_enctype_des3_cbc_md5 = {
ETYPE_DES3_CBC_MD5,
"des3-cbc-md5",
+ NULL,
8,
8,
8,
@@ -147,22 +201,24 @@ struct _krb5_encryption_type _krb5_enctype_des3_cbc_md5 = {
struct _krb5_encryption_type _krb5_enctype_des3_cbc_sha1 = {
ETYPE_DES3_CBC_SHA1,
"des3-cbc-sha1",
+ NULL,
8,
8,
8,
&keytype_des3_derived,
&_krb5_checksum_sha1,
&_krb5_checksum_hmac_sha1_des3,
- F_DERIVED,
+ F_DERIVED | F_RFC3961_ENC | F_RFC3961_KDF,
_krb5_evp_encrypt,
- 0,
- NULL
+ 16,
+ DES3_prf
};
#ifdef DES3_OLD_ENCTYPE
struct _krb5_encryption_type _krb5_enctype_old_des3_cbc_sha1 = {
ETYPE_OLD_DES3_CBC_SHA1,
"old-des3-cbc-sha1",
+ NULL,
8,
8,
8,
@@ -179,6 +235,7 @@ struct _krb5_encryption_type _krb5_enctype_old_des3_cbc_sha1 = {
struct _krb5_encryption_type _krb5_enctype_des3_cbc_none = {
ETYPE_DES3_CBC_NONE,
"des3-cbc-none",
+ NULL,
8,
8,
0,
@@ -221,6 +278,6 @@ _krb5_DES3_random_to_key(krb5_context context,
for (i = 0; i < 3; i++) {
DES_set_odd_parity(&k[i]);
if(DES_is_weak_key(&k[i]))
- _krb5_xor(&k[i], (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ _krb5_xor8(k[i], (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
}
}
diff --git a/lib/krb5/crypto-evp.c b/lib/krb5/crypto-evp.c
index e8fb1caf6ae8..cab7c29061f7 100644
--- a/lib/krb5/crypto-evp.c
+++ b/lib/krb5/crypto-evp.c
@@ -72,10 +72,8 @@ _krb5_evp_encrypt(krb5_context context,
/* alloca ? */
size_t len2 = EVP_CIPHER_CTX_iv_length(c);
void *loiv = malloc(len2);
- if (loiv == NULL) {
- krb5_clear_error_message(context);
- return ENOMEM;
- }
+ if (loiv == NULL)
+ return krb5_enomem(context);
memset(loiv, 0, len2);
EVP_CipherInit_ex(c, NULL, NULL, NULL, loiv, -1);
free(loiv);
diff --git a/lib/krb5/crypto-null.c b/lib/krb5/crypto-null.c
index 3b643123f529..96b77994708c 100644
--- a/lib/krb5/crypto-null.c
+++ b/lib/krb5/crypto-null.c
@@ -38,13 +38,16 @@
#endif
static struct _krb5_key_type keytype_null = {
- ENCTYPE_NULL,
+ KRB5_ENCTYPE_NULL,
"null",
0,
0,
0,
NULL,
NULL,
+ NULL,
+ NULL,
+ NULL,
NULL
};
@@ -84,6 +87,7 @@ NULL_encrypt(krb5_context context,
struct _krb5_encryption_type _krb5_enctype_null = {
ETYPE_NULL,
"null",
+ NULL,
1,
1,
0,
diff --git a/lib/krb5/crypto-pk.c b/lib/krb5/crypto-pk.c
index 7fedb65c9ede..c5d87a1b566e 100644
--- a/lib/krb5/crypto-pk.c
+++ b/lib/krb5/crypto-pk.c
@@ -35,7 +35,7 @@
#include <pkinit_asn1.h>
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_octetstring2key(krb5_context context,
krb5_enctype type,
const void *dhdata,
@@ -61,16 +61,13 @@ _krb5_pk_octetstring2key(krb5_context context,
keylen = (et->keytype->bits + 7) / 8;
keydata = malloc(keylen);
- if (keydata == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (keydata == NULL)
+ return krb5_enomem(context);
m = EVP_MD_CTX_create();
if (m == NULL) {
free(keydata);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
counter = 0;
@@ -194,7 +191,7 @@ encode_otherinfo(krb5_context context,
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_kdf(krb5_context context,
const struct AlgorithmIdentifier *ai,
const void *dhdata,
@@ -248,10 +245,8 @@ _krb5_pk_kdf(krb5_context context,
keylen = (et->keytype->bits + 7) / 8;
keydata = malloc(keylen);
- if (keydata == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (keydata == NULL)
+ return krb5_enomem(context);
ret = encode_otherinfo(context, ai, client, server,
enctype, as_req, pk_as_rep, ticket, &other);
@@ -264,8 +259,7 @@ _krb5_pk_kdf(krb5_context context,
if (m == NULL) {
free(keydata);
free(other.data);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
offset = 0;
diff --git a/lib/krb5/crypto-rand.c b/lib/krb5/crypto-rand.c
index 49bd67936259..7b126c6958c4 100644
--- a/lib/krb5/crypto-rand.c
+++ b/lib/krb5/crypto-rand.c
@@ -33,6 +33,9 @@
#include "krb5_locl.h"
+#undef HEIMDAL_WARN_UNUSED_RESULT_ATTRIBUTE
+#define HEIMDAL_WARN_UNUSED_RESULT_ATTRIBUTE
+
#define ENTROPY_NEEDED 128
static HEIMDAL_MUTEX crypto_mutex = HEIMDAL_MUTEX_INITIALIZER;
@@ -40,6 +43,7 @@ static HEIMDAL_MUTEX crypto_mutex = HEIMDAL_MUTEX_INITIALIZER;
static int
seed_something(void)
{
+#ifndef NO_RANDFILE
char buf[1024], seedfile[256];
/* If there is a seed file, load it. But such a file cannot be trusted,
@@ -58,52 +62,91 @@ seed_something(void)
seedfile[0] = '\0';
} else
seedfile[0] = '\0';
+#endif
/* Calling RAND_status() will try to use /dev/urandom if it exists so
we do not have to deal with it. */
if (RAND_status() != 1) {
-#ifndef _WIN32
- krb5_context context;
- const char *p;
-
- /* Try using egd */
- if (!krb5_init_context(&context)) {
- p = krb5_config_get_string(context, NULL, "libdefaults",
- "egd_socket", NULL);
- if (p != NULL)
- RAND_egd_bytes(p, ENTROPY_NEEDED);
- krb5_free_context(context);
- }
-#else
/* TODO: Once a Windows CryptoAPI RAND method is defined, we
can use that and failover to another method. */
-#endif
}
if (RAND_status() == 1) {
+#ifndef NO_RANDFILE
/* Update the seed file */
if (seedfile[0])
RAND_write_file(seedfile);
+#endif
return 0;
} else
return -1;
}
-KRB5_LIB_FUNCTION void KRB5_LIB_CALL
-krb5_generate_random_block(void *buf, size_t len)
+/**
+ * Fill buffer buf with len bytes of PRNG randomness that is ok to use
+ * for key generation, padding and public diclosing the randomness w/o
+ * disclosing the randomness source.
+ *
+ * This function can fail, and callers must check the return value.
+ *
+ * @param buf a buffer to fill with randomness
+ * @param len length of memory that buf points to.
+ *
+ * @return return 0 on success or HEIM_ERR_RANDOM_OFFLINE if the
+ * funcation failed to initialize the randomness source.
+ *
+ * @ingroup krb5_crypto
+ */
+
+HEIMDAL_WARN_UNUSED_RESULT_ATTRIBUTE
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_generate_random(void *buf, size_t len)
{
static int rng_initialized = 0;
+ int ret;
HEIMDAL_MUTEX_lock(&crypto_mutex);
if (!rng_initialized) {
- if (seed_something())
- krb5_abortx(NULL, "Fatal: could not seed the "
- "random number generator");
-
+ if (seed_something()) {
+ HEIMDAL_MUTEX_unlock(&crypto_mutex);
+ return HEIM_ERR_RANDOM_OFFLINE;
+ }
rng_initialized = 1;
}
- HEIMDAL_MUTEX_unlock(&crypto_mutex);
if (RAND_bytes(buf, len) <= 0)
+ ret = HEIM_ERR_RANDOM_OFFLINE;
+ else
+ ret = 0;
+ HEIMDAL_MUTEX_unlock(&crypto_mutex);
+
+ return ret;
+}
+
+/**
+ * Fill buffer buf with len bytes of PRNG randomness that is ok to use
+ * for key generation, padding and public diclosing the randomness w/o
+ * disclosing the randomness source.
+ *
+ * This function can NOT fail, instead it will abort() and program will crash.
+ *
+ * If this function is called after a successful krb5_init_context(),
+ * the chance of it failing is low due to that krb5_init_context()
+ * pulls out some random, and quite commonly the randomness sources
+ * will not fail once it have started to produce good output,
+ * /dev/urandom behavies that way.
+ *
+ * @param buf a buffer to fill with randomness
+ * @param len length of memory that buf points to.
+ *
+ * @ingroup krb5_crypto
+ */
+
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_generate_random_block(void *buf, size_t len)
+{
+ int ret = krb5_generate_random(buf, len);
+ if (ret)
krb5_abortx(NULL, "Failed to generate random block");
}
diff --git a/lib/krb5/crypto-stubs.c b/lib/krb5/crypto-stubs.c
index b462680643fb..2398a4630a04 100644
--- a/lib/krb5/crypto-stubs.c
+++ b/lib/krb5/crypto-stubs.c
@@ -49,12 +49,7 @@ krb5_init_context(krb5_context *context)
if(!p)
return ENOMEM;
- p->mutex = malloc(sizeof(HEIMDAL_MUTEX));
- if (p->mutex == NULL) {
- free(p);
- return ENOMEM;
- }
- HEIMDAL_MUTEX_init(p->mutex);
+ HEIMDAL_MUTEX_init(&p->mutex);
*context = p;
return 0;
@@ -65,8 +60,7 @@ krb5_free_context(krb5_context context)
{
krb5_clear_error_message(context);
- HEIMDAL_MUTEX_destroy(context->mutex);
- free(context->mutex);
+ HEIMDAL_MUTEX_destroy(&context->mutex);
if (context->flags & KRB5_CTX_F_SOCKETS_INITIALIZED) {
rk_SOCK_EXIT();
}
@@ -75,7 +69,7 @@ krb5_free_context(krb5_context context)
free(context);
}
-krb5_boolean
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_homedir_access(krb5_context context) {
return 0;
}
@@ -90,6 +84,15 @@ krb5_log(krb5_context context,
return 0;
}
+void KRB5_LIB_FUNCTION
+_krb5_debug(krb5_context context,
+ int level,
+ const char *fmt,
+ ...)
+{
+}
+
+
/* This function is currently just used to get the location of the EGD
* socket. If we're not using an EGD, then we can just return NULL */
diff --git a/lib/krb5/crypto.c b/lib/krb5/crypto.c
index 67ecef62e875..1c30629db704 100644
--- a/lib/krb5/crypto.c
+++ b/lib/krb5/crypto.c
@@ -147,10 +147,8 @@ _key_schedule(krb5_context context,
if (key->schedule != NULL)
return 0;
ALLOC(key->schedule, 1);
- if(key->schedule == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (key->schedule == NULL)
+ return krb5_enomem(context);
ret = krb5_data_alloc(key->schedule, kt->schedule_size);
if(ret) {
free(key->schedule);
@@ -179,7 +177,7 @@ SHA1_checksum(krb5_context context,
}
/* HMAC according to RFC2104 */
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_internal_hmac(krb5_context context,
struct _krb5_checksum_type *cm,
const void *data,
@@ -300,7 +298,7 @@ struct _krb5_checksum_type _krb5_checksum_sha1 = {
NULL
};
-struct _krb5_checksum_type *
+KRB5_LIB_FUNCTION struct _krb5_checksum_type * KRB5_LIB_CALL
_krb5_find_checksum(krb5_cksumtype type)
{
int i;
@@ -325,10 +323,8 @@ get_checksum_key(krb5_context context,
size_t i;
*key = _new_derived_key(crypto, 0xff/* KRB5_KU_RFC1510_VARIANT */);
- if(*key == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*key == NULL)
+ return krb5_enomem(context);
ret = krb5_copy_keyblock(context, crypto->key.key, &(*key)->key);
if(ret)
return ret;
@@ -640,7 +636,7 @@ krb5_checksum_disable(krb5_context context,
* *
************************************************************/
-struct _krb5_encryption_type *
+KRB5_LIB_FUNCTION struct _krb5_encryption_type * KRB5_LIB_CALL
_krb5_find_enctype(krb5_enctype type)
{
int i;
@@ -666,10 +662,8 @@ krb5_enctype_to_string(krb5_context context,
return KRB5_PROG_ETYPE_NOSUPP;
}
*string = strdup(e->name);
- if(*string == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*string == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -679,11 +673,17 @@ krb5_string_to_enctype(krb5_context context,
krb5_enctype *etype)
{
int i;
- for(i = 0; i < _krb5_num_etypes; i++)
+ for(i = 0; i < _krb5_num_etypes; i++) {
if(strcasecmp(_krb5_etypes[i]->name, string) == 0){
*etype = _krb5_etypes[i]->type;
return 0;
}
+ if(_krb5_etypes[i]->alias != NULL &&
+ strcasecmp(_krb5_etypes[i]->alias, string) == 0){
+ *etype = _krb5_etypes[i]->type;
+ return 0;
+ }
+ }
krb5_set_error_message (context, KRB5_PROG_ETYPE_NOSUPP,
N_("encryption type %s not supported", ""),
string);
@@ -790,7 +790,6 @@ krb5_cksumtype_valid(krb5_context context,
return 0;
}
-
static krb5_boolean
derived_crypto(krb5_context context,
krb5_crypto crypto)
@@ -798,13 +797,6 @@ derived_crypto(krb5_context context,
return (crypto->et->flags & F_DERIVED) != 0;
}
-static krb5_boolean
-special_crypto(krb5_context context,
- krb5_crypto crypto)
-{
- return (crypto->et->flags & F_SPECIAL) != 0;
-}
-
#define CHECKSUMSIZE(C) ((C)->checksumsize)
#define CHECKSUMTYPE(C) ((C)->type)
@@ -830,10 +822,8 @@ encrypt_internal_derived(krb5_context context,
block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
total_sz = block_sz + checksum_sz;
p = calloc(1, total_sz);
- if(p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (p == NULL)
+ return krb5_enomem(context);
q = p;
krb5_generate_random_block(q, et->confoundersize); /* XXX */
@@ -874,6 +864,85 @@ encrypt_internal_derived(krb5_context context,
return ret;
}
+static krb5_error_code
+encrypt_internal_enc_then_cksum(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ const void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t sz, block_sz, checksum_sz, total_sz;
+ Checksum cksum;
+ unsigned char *p, *q, *ivc = NULL;
+ krb5_error_code ret;
+ struct _krb5_key_data *dkey;
+ const struct _krb5_encryption_type *et = crypto->et;
+
+ checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+
+ sz = et->confoundersize + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+ total_sz = block_sz + checksum_sz;
+ p = calloc(1, total_sz);
+ if (p == NULL)
+ return krb5_enomem(context);
+
+ q = p;
+ krb5_generate_random_block(q, et->confoundersize); /* XXX */
+ q += et->confoundersize;
+ memcpy(q, data, len);
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ goto fail;
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ goto fail;
+
+ /* XXX EVP style update API would avoid needing to allocate here */
+ ivc = malloc(et->blocksize + block_sz);
+ if (ivc == NULL) {
+ ret = krb5_enomem(context);
+ goto fail;
+ }
+ if (ivec)
+ memcpy(ivc, ivec, et->blocksize);
+ else
+ memset(ivc, 0, et->blocksize);
+
+ ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec);
+ if (ret)
+ goto fail;
+ memcpy(&ivc[et->blocksize], p, block_sz);
+
+ ret = create_checksum(context,
+ et->keyed_checksum,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ ivc,
+ et->blocksize + block_sz,
+ &cksum);
+ if(ret == 0 && cksum.checksum.length != checksum_sz) {
+ free_Checksum (&cksum);
+ krb5_clear_error_message (context);
+ ret = KRB5_CRYPTO_INTERNAL;
+ }
+ if(ret)
+ goto fail;
+ memcpy(p + block_sz, cksum.checksum.data, cksum.checksum.length);
+ free_Checksum (&cksum);
+ result->data = p;
+ result->length = total_sz;
+ free(ivc);
+ return 0;
+ fail:
+ memset_s(p, total_sz, 0, total_sz);
+ free(p);
+ free(ivc);
+ return ret;
+}
static krb5_error_code
encrypt_internal(krb5_context context,
@@ -894,10 +963,8 @@ encrypt_internal(krb5_context context,
sz = et->confoundersize + checksum_sz + len;
block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
p = calloc(1, block_sz);
- if(p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (p == NULL)
+ return krb5_enomem(context);
q = p;
krb5_generate_random_block(q, et->confoundersize); /* XXX */
@@ -956,10 +1023,8 @@ encrypt_internal_special(krb5_context context,
krb5_error_code ret;
tmp = malloc (sz);
- if (tmp == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (tmp == NULL)
+ return krb5_enomem(context);
p = tmp;
memset (p, 0, cksum_sz);
p += cksum_sz;
@@ -1008,10 +1073,8 @@ decrypt_internal_derived(krb5_context context,
}
p = malloc(len);
- if(len != 0 && p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (len != 0 && p == NULL)
+ return krb5_enomem(context);
memcpy(p, data, len);
len -= checksum_sz;
@@ -1051,8 +1114,91 @@ decrypt_internal_derived(krb5_context context,
result->data = realloc(p, l);
if(result->data == NULL && l != 0) {
free(p);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
+ }
+ result->length = l;
+ return 0;
+}
+
+static krb5_error_code
+decrypt_internal_enc_then_cksum(krb5_context context,
+ krb5_crypto crypto,
+ unsigned usage,
+ void *data,
+ size_t len,
+ krb5_data *result,
+ void *ivec)
+{
+ size_t checksum_sz;
+ Checksum cksum;
+ unsigned char *p;
+ krb5_error_code ret;
+ struct _krb5_key_data *dkey;
+ struct _krb5_encryption_type *et = crypto->et;
+ unsigned long l;
+
+ checksum_sz = CHECKSUMSIZE(et->keyed_checksum);
+ if (len < checksum_sz + et->confoundersize) {
+ krb5_set_error_message(context, KRB5_BAD_MSIZE,
+ N_("Encrypted data shorter then "
+ "checksum + confunder", ""));
+ return KRB5_BAD_MSIZE;
+ }
+
+ if (((len - checksum_sz) % et->padsize) != 0) {
+ krb5_clear_error_message(context);
+ return KRB5_BAD_MSIZE;
+ }
+
+ len -= checksum_sz;
+
+ p = malloc(et->blocksize + len);
+ if (p == NULL)
+ return krb5_enomem(context);
+
+ if (ivec)
+ memcpy(p, ivec, et->blocksize);
+ else
+ memset(p, 0, et->blocksize);
+ memcpy(&p[et->blocksize], data, len);
+
+ cksum.checksum.data = (unsigned char *)data + len;
+ cksum.checksum.length = checksum_sz;
+ cksum.cksumtype = CHECKSUMTYPE(et->keyed_checksum);
+
+ ret = verify_checksum(context,
+ crypto,
+ INTEGRITY_USAGE(usage),
+ p,
+ et->blocksize + len,
+ &cksum);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = _key_schedule(context, dkey);
+ if(ret) {
+ free(p);
+ return ret;
+ }
+ ret = (*et->encrypt)(context, dkey, &p[et->blocksize], len, 0, usage, ivec);
+ if (ret) {
+ free(p);
+ return ret;
+ }
+
+ l = len - et->confoundersize;
+ memmove(p, p + et->blocksize + et->confoundersize, l);
+ result->data = realloc(p, l);
+ if(result->data == NULL && l != 0) {
+ free(p);
+ return krb5_enomem(context);
}
result->length = l;
return 0;
@@ -1085,10 +1231,8 @@ decrypt_internal(krb5_context context,
}
p = malloc(len);
- if(len != 0 && p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (len != 0 && p == NULL)
+ return krb5_enomem(context);
memcpy(p, data, len);
ret = _key_schedule(context, &crypto->key);
@@ -1119,8 +1263,7 @@ decrypt_internal(krb5_context context,
result->data = realloc(p, l);
if(result->data == NULL && l != 0) {
free(p);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
result->length = l;
return 0;
@@ -1153,10 +1296,8 @@ decrypt_internal_special(krb5_context context,
}
p = malloc (len);
- if (p == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (p == NULL)
+ return krb5_enomem(context);
memcpy(p, data, len);
ret = (*et->encrypt)(context, &crypto->key, p, len, FALSE, usage, ivec);
@@ -1169,15 +1310,14 @@ decrypt_internal_special(krb5_context context,
result->data = realloc(p, sz);
if(result->data == NULL && sz != 0) {
free(p);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
result->length = sz;
return 0;
}
static krb5_crypto_iov *
-find_iv(krb5_crypto_iov *data, size_t num_data, unsigned type)
+iov_find(krb5_crypto_iov *data, size_t num_data, unsigned type)
{
size_t i;
for (i = 0; i < num_data; i++)
@@ -1186,6 +1326,154 @@ find_iv(krb5_crypto_iov *data, size_t num_data, unsigned type)
return NULL;
}
+static size_t
+iov_enc_data_len(krb5_crypto_iov *data, int num_data)
+{
+ size_t i, len;
+
+ for (len = 0, i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ len += data[i].data.length;
+ }
+
+ return len;
+}
+
+static size_t
+iov_sign_data_len(krb5_crypto_iov *data, int num_data)
+{
+ size_t i, len;
+
+ for (len = 0, i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
+ data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
+ continue;
+ len += data[i].data.length;
+ }
+
+ return len;
+}
+
+static krb5_error_code
+iov_coalesce(krb5_context context,
+ krb5_data *prefix,
+ krb5_crypto_iov *data,
+ int num_data,
+ krb5_boolean inc_sign_data,
+ krb5_data *out)
+{
+ unsigned char *p, *q;
+ krb5_crypto_iov *hiv, *piv;
+ size_t len;
+ unsigned int i;
+
+ hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+
+ piv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
+
+ len = 0;
+ if (prefix)
+ len += prefix->length;
+ len += hiv->data.length;
+ if (inc_sign_data)
+ len += iov_sign_data_len(data, num_data);
+ else
+ len += iov_enc_data_len(data, num_data);
+ if (piv)
+ len += piv->data.length;
+
+ p = q = malloc(len);
+ if (p == NULL)
+ return krb5_enomem(context);
+
+ if (prefix) {
+ memcpy(q, prefix->data, prefix->length);
+ q += prefix->length;
+ }
+ memcpy(q, hiv->data.data, hiv->data.length);
+ q += hiv->data.length;
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags == KRB5_CRYPTO_TYPE_DATA ||
+ (inc_sign_data && data[i].flags == KRB5_CRYPTO_TYPE_SIGN_ONLY)) {
+ memcpy(q, data[i].data.data, data[i].data.length);
+ q += data[i].data.length;
+ }
+ }
+ if (piv)
+ memset(q, 0, piv->data.length);
+
+ out->length = len;
+ out->data = p;
+
+ return 0;
+}
+
+static krb5_error_code
+iov_uncoalesce(krb5_context context,
+ krb5_data *enc_data,
+ krb5_crypto_iov *data,
+ int num_data)
+{
+ unsigned char *q = enc_data->data;
+ krb5_crypto_iov *hiv, *piv;
+ unsigned int i;
+
+ hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+
+ piv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
+
+ memcpy(hiv->data.data, q, hiv->data.length);
+ q += hiv->data.length;
+
+ for (i = 0; i < num_data; i++) {
+ if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
+ continue;
+ memcpy(data[i].data.data, q, data[i].data.length);
+ q += data[i].data.length;
+ }
+ if (piv)
+ memcpy(piv->data.data, q, piv->data.length);
+
+ return 0;
+}
+
+static krb5_error_code
+iov_pad_validate(const struct _krb5_encryption_type *et,
+ krb5_crypto_iov *data,
+ int num_data,
+ krb5_crypto_iov **ppiv)
+{
+ krb5_crypto_iov *piv;
+ size_t sz, headersz, block_sz, pad_sz, len;
+
+ len = iov_enc_data_len(data, num_data);
+
+ headersz = et->confoundersize;
+
+ sz = headersz + len;
+ block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
+
+ pad_sz = block_sz - sz;
+
+ piv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
+ /* its ok to have no TYPE_PADDING if there is no padding */
+ if (piv == NULL && pad_sz != 0)
+ return KRB5_BAD_MSIZE;
+ if (piv) {
+ if (piv->data.length < pad_sz)
+ return KRB5_BAD_MSIZE;
+ piv->data.length = pad_sz;
+ if (pad_sz)
+ memset(piv->data.data, pad_sz, pad_sz);
+ else
+ piv = NULL;
+ }
+
+ *ppiv = piv;
+ return 0;
+}
+
/**
* Inline encrypt a kerberos message
*
@@ -1218,11 +1506,9 @@ krb5_encrypt_iov_ivec(krb5_context context,
int num_data,
void *ivec)
{
- size_t headersz, trailersz, len;
- int i;
- size_t sz, block_sz, pad_sz;
+ size_t headersz, trailersz;
Checksum cksum;
- unsigned char *p, *q;
+ krb5_data enc_data, sign_data;
krb5_error_code ret;
struct _krb5_key_data *dkey;
const struct _krb5_encryption_type *et = crypto->et;
@@ -1238,147 +1524,126 @@ krb5_encrypt_iov_ivec(krb5_context context,
return KRB5_CRYPTO_INTERNAL;
}
+ krb5_data_zero(&enc_data);
+ krb5_data_zero(&sign_data);
+
headersz = et->confoundersize;
trailersz = CHECKSUMSIZE(et->keyed_checksum);
- for (len = 0, i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
- continue;
- len += data[i].data.length;
- }
-
- sz = headersz + len;
- block_sz = (sz + et->padsize - 1) &~ (et->padsize - 1); /* pad */
-
- pad_sz = block_sz - sz;
-
/* header */
-
- hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
if (hiv == NULL || hiv->data.length != headersz)
return KRB5_BAD_MSIZE;
-
krb5_generate_random_block(hiv->data.data, hiv->data.length);
/* padding */
- piv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_PADDING);
- /* its ok to have no TYPE_PADDING if there is no padding */
- if (piv == NULL && pad_sz != 0)
- return KRB5_BAD_MSIZE;
- if (piv) {
- if (piv->data.length < pad_sz)
- return KRB5_BAD_MSIZE;
- piv->data.length = pad_sz;
- if (pad_sz)
- memset(piv->data.data, pad_sz, pad_sz);
- else
- piv = NULL;
- }
+ ret = iov_pad_validate(et, data, num_data, &piv);
+ if(ret)
+ goto cleanup;
/* trailer */
- tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
- if (tiv == NULL || tiv->data.length != trailersz)
- return KRB5_BAD_MSIZE;
+ tiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (tiv == NULL || tiv->data.length != trailersz) {
+ ret = KRB5_BAD_MSIZE;
+ goto cleanup;
+ }
- /*
- * XXX replace with EVP_Sign? at least make create_checksum an iov
- * function.
- * XXX CTS EVP is broken, can't handle multi buffers :(
- */
+ if (et->flags & F_ENC_THEN_CKSUM) {
+ unsigned char old_ivec[EVP_MAX_IV_LENGTH];
+ krb5_data ivec_data;
- len = block_sz;
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
- continue;
- len += data[i].data.length;
- }
+ ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data);
+ if(ret)
+ goto cleanup;
- p = q = malloc(len);
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ goto cleanup;
- memcpy(q, hiv->data.data, hiv->data.length);
- q += hiv->data.length;
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
- data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
- continue;
- memcpy(q, data[i].data.data, data[i].data.length);
- q += data[i].data.length;
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ goto cleanup;
+
+ heim_assert(et->blocksize <= sizeof(old_ivec),
+ "blocksize too big for ivec buffer");
+
+ if (ivec)
+ memcpy(old_ivec, ivec, et->blocksize);
+ else
+ memset(old_ivec, 0, et->blocksize);
+
+ ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length,
+ 1, usage, ivec);
+ if(ret)
+ goto cleanup;
+
+ ret = iov_uncoalesce(context, &enc_data, data, num_data);
+ if(ret)
+ goto cleanup;
+
+ ivec_data.length = et->blocksize;
+ ivec_data.data = old_ivec;
+
+ ret = iov_coalesce(context, &ivec_data, data, num_data, TRUE, &sign_data);
+ if(ret)
+ goto cleanup;
+ } else {
+ ret = iov_coalesce(context, NULL, data, num_data, TRUE, &sign_data);
+ if(ret)
+ goto cleanup;
}
- if (piv)
- memset(q, 0, piv->data.length);
ret = create_checksum(context,
et->keyed_checksum,
crypto,
INTEGRITY_USAGE(usage),
- p,
- len,
+ sign_data.data,
+ sign_data.length,
&cksum);
- free(p);
if(ret == 0 && cksum.checksum.length != trailersz) {
free_Checksum (&cksum);
krb5_clear_error_message (context);
ret = KRB5_CRYPTO_INTERNAL;
}
if(ret)
- return ret;
+ goto cleanup;
/* save cksum at end */
memcpy(tiv->data.data, cksum.checksum.data, cksum.checksum.length);
free_Checksum (&cksum);
- /* XXX replace with EVP_Cipher */
- p = q = malloc(block_sz);
- if(p == NULL)
- return ENOMEM;
+ if (!(et->flags & F_ENC_THEN_CKSUM)) {
+ ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data);
+ if(ret)
+ goto cleanup;
- memcpy(q, hiv->data.data, hiv->data.length);
- q += hiv->data.length;
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ goto cleanup;
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
- continue;
- memcpy(q, data[i].data.data, data[i].data.length);
- q += data[i].data.length;
- }
- if (piv)
- memset(q, 0, piv->data.length);
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ goto cleanup;
+ ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length,
+ 1, usage, ivec);
+ if(ret)
+ goto cleanup;
- ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
- if(ret) {
- free(p);
- return ret;
- }
- ret = _key_schedule(context, dkey);
- if(ret) {
- free(p);
- return ret;
+ ret = iov_uncoalesce(context, &enc_data, data, num_data);
+ if(ret)
+ goto cleanup;
}
- ret = (*et->encrypt)(context, dkey, p, block_sz, 1, usage, ivec);
- if (ret) {
- free(p);
- return ret;
+cleanup:
+ if (enc_data.data) {
+ memset_s(enc_data.data, enc_data.length, 0, enc_data.length);
+ krb5_data_free(&enc_data);
}
-
- /* now copy data back to buffers */
- q = p;
-
- memcpy(hiv->data.data, q, hiv->data.length);
- q += hiv->data.length;
-
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
- continue;
- memcpy(data[i].data.data, q, data[i].data.length);
- q += data[i].data.length;
+ if (sign_data.data) {
+ memset_s(sign_data.data, sign_data.length, 0, sign_data.length);
+ krb5_data_free(&sign_data);
}
- if (piv)
- memcpy(piv->data.data, q, pad_sz);
-
- free(p);
-
return ret;
}
@@ -1411,10 +1676,8 @@ krb5_decrypt_iov_ivec(krb5_context context,
unsigned int num_data,
void *ivec)
{
- unsigned int i;
- size_t headersz, trailersz, len;
Checksum cksum;
- unsigned char *p, *q;
+ krb5_data enc_data, sign_data;
krb5_error_code ret;
struct _krb5_key_data *dkey;
struct _krb5_encryption_type *et = crypto->et;
@@ -1425,97 +1688,63 @@ krb5_decrypt_iov_ivec(krb5_context context,
return KRB5_CRYPTO_INTERNAL;
}
- headersz = et->confoundersize;
-
- hiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
- if (hiv == NULL || hiv->data.length != headersz)
+ /* header */
+ hiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_HEADER);
+ if (hiv == NULL || hiv->data.length != et->confoundersize)
return KRB5_BAD_MSIZE;
/* trailer */
- trailersz = CHECKSUMSIZE(et->keyed_checksum);
-
- tiv = find_iv(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
- if (tiv->data.length != trailersz)
+ tiv = iov_find(data, num_data, KRB5_CRYPTO_TYPE_TRAILER);
+ if (tiv->data.length != CHECKSUMSIZE(et->keyed_checksum))
return KRB5_BAD_MSIZE;
- /* Find length of data we will decrypt */
-
- len = headersz;
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
- continue;
- len += data[i].data.length;
- }
-
- if ((len % et->padsize) != 0) {
+ /* padding */
+ if ((iov_enc_data_len(data, num_data) % et->padsize) != 0) {
krb5_clear_error_message(context);
return KRB5_BAD_MSIZE;
}
- /* XXX replace with EVP_Cipher */
+ krb5_data_zero(&enc_data);
+ krb5_data_zero(&sign_data);
- p = q = malloc(len);
- if (p == NULL)
- return ENOMEM;
-
- memcpy(q, hiv->data.data, hiv->data.length);
- q += hiv->data.length;
+ if (!(et->flags & F_ENC_THEN_CKSUM)) {
+ ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data);
+ if(ret)
+ goto cleanup;
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
- continue;
- memcpy(q, data[i].data.data, data[i].data.length);
- q += data[i].data.length;
- }
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ goto cleanup;
- ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
- if(ret) {
- free(p);
- return ret;
- }
- ret = _key_schedule(context, dkey);
- if(ret) {
- free(p);
- return ret;
- }
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ goto cleanup;
- ret = (*et->encrypt)(context, dkey, p, len, 0, usage, ivec);
- if (ret) {
- free(p);
- return ret;
- }
+ ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length,
+ 0, usage, ivec);
+ if(ret)
+ goto cleanup;
- /* copy data back to buffers */
- memcpy(hiv->data.data, p, hiv->data.length);
- q = p + hiv->data.length;
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA)
- continue;
- memcpy(data[i].data.data, q, data[i].data.length);
- q += data[i].data.length;
- }
+ ret = iov_uncoalesce(context, &enc_data, data, num_data);
+ if(ret)
+ goto cleanup;
- free(p);
+ ret = iov_coalesce(context, NULL, data, num_data, TRUE, &sign_data);
+ if(ret)
+ goto cleanup;
+ } else {
+ krb5_data ivec_data;
+ static unsigned char zero_ivec[EVP_MAX_IV_LENGTH];
- /* check signature */
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
- continue;
- len += data[i].data.length;
- }
+ heim_assert(et->blocksize <= sizeof(zero_ivec),
+ "blocksize too big for ivec buffer");
- p = q = malloc(len);
- if (p == NULL)
- return ENOMEM;
+ ivec_data.length = et->blocksize;
+ ivec_data.data = ivec ? ivec : zero_ivec;
- memcpy(q, hiv->data.data, hiv->data.length);
- q += hiv->data.length;
- for (i = 0; i < num_data; i++) {
- if (data[i].flags != KRB5_CRYPTO_TYPE_DATA &&
- data[i].flags != KRB5_CRYPTO_TYPE_SIGN_ONLY)
- continue;
- memcpy(q, data[i].data.data, data[i].data.length);
- q += data[i].data.length;
+ ret = iov_coalesce(context, &ivec_data, data, num_data, TRUE, &sign_data);
+ if(ret)
+ goto cleanup;
}
cksum.checksum.data = tiv->data.data;
@@ -1525,10 +1754,44 @@ krb5_decrypt_iov_ivec(krb5_context context,
ret = verify_checksum(context,
crypto,
INTEGRITY_USAGE(usage),
- p,
- len,
+ sign_data.data,
+ sign_data.length,
&cksum);
- free(p);
+ if(ret)
+ goto cleanup;
+
+ if (et->flags & F_ENC_THEN_CKSUM) {
+ ret = iov_coalesce(context, NULL, data, num_data, FALSE, &enc_data);
+ if(ret)
+ goto cleanup;
+
+ ret = _get_derived_key(context, crypto, ENCRYPTION_USAGE(usage), &dkey);
+ if(ret)
+ goto cleanup;
+
+ ret = _key_schedule(context, dkey);
+ if(ret)
+ goto cleanup;
+
+ ret = (*et->encrypt)(context, dkey, enc_data.data, enc_data.length,
+ 0, usage, ivec);
+ if(ret)
+ goto cleanup;
+
+ ret = iov_uncoalesce(context, &enc_data, data, num_data);
+ if(ret)
+ goto cleanup;
+ }
+
+cleanup:
+ if (enc_data.data) {
+ memset_s(enc_data.data, enc_data.length, 0, enc_data.length);
+ krb5_data_free(&enc_data);
+ }
+ if (sign_data.data) {
+ memset_s(sign_data.data, sign_data.length, 0, sign_data.length);
+ krb5_data_free(&sign_data);
+ }
return ret;
}
@@ -1566,7 +1829,7 @@ krb5_create_checksum_iov(krb5_context context,
return KRB5_CRYPTO_INTERNAL;
}
- civ = find_iv(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM);
+ civ = iov_find(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM);
if (civ == NULL)
return KRB5_BAD_MSIZE;
@@ -1645,7 +1908,7 @@ krb5_verify_checksum_iov(krb5_context context,
return KRB5_CRYPTO_INTERNAL;
}
- civ = find_iv(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM);
+ civ = iov_find(data, num_data, KRB5_CRYPTO_TYPE_CHECKSUM);
if (civ == NULL)
return KRB5_BAD_MSIZE;
@@ -1754,14 +2017,27 @@ krb5_encrypt_ivec(krb5_context context,
krb5_data *result,
void *ivec)
{
- if(derived_crypto(context, crypto))
- return encrypt_internal_derived(context, crypto, usage,
+ krb5_error_code ret;
+
+ switch (crypto->et->flags & F_CRYPTO_MASK) {
+ case F_RFC3961_ENC:
+ ret = encrypt_internal_derived(context, crypto, usage,
+ data, len, result, ivec);
+ break;
+ case F_SPECIAL:
+ ret = encrypt_internal_special (context, crypto, usage,
data, len, result, ivec);
- else if (special_crypto(context, crypto))
- return encrypt_internal_special (context, crypto, usage,
- data, len, result, ivec);
- else
- return encrypt_internal(context, crypto, data, len, result, ivec);
+ break;
+ case F_ENC_THEN_CKSUM:
+ ret = encrypt_internal_enc_then_cksum(context, crypto, usage,
+ data, len, result, ivec);
+ break;
+ default:
+ ret = encrypt_internal(context, crypto, data, len, result, ivec);
+ break;
+ }
+
+ return ret;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -1802,14 +2078,27 @@ krb5_decrypt_ivec(krb5_context context,
krb5_data *result,
void *ivec)
{
- if(derived_crypto(context, crypto))
- return decrypt_internal_derived(context, crypto, usage,
- data, len, result, ivec);
- else if (special_crypto (context, crypto))
- return decrypt_internal_special(context, crypto, usage,
- data, len, result, ivec);
- else
- return decrypt_internal(context, crypto, data, len, result, ivec);
+ krb5_error_code ret;
+
+ switch (crypto->et->flags & F_CRYPTO_MASK) {
+ case F_RFC3961_ENC:
+ ret = decrypt_internal_derived(context, crypto, usage,
+ data, len, result, ivec);
+ break;
+ case F_SPECIAL:
+ ret = decrypt_internal_special(context, crypto, usage,
+ data, len, result, ivec);
+ break;
+ case F_ENC_THEN_CKSUM:
+ ret = decrypt_internal_enc_then_cksum(context, crypto, usage,
+ data, len, result, ivec);
+ break;
+ default:
+ ret = decrypt_internal(context, crypto, data, len, result, ivec);
+ break;
+ }
+
+ return ret;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -1839,32 +2128,29 @@ krb5_decrypt_EncryptedData(krb5_context context,
* *
************************************************************/
-krb5_error_code
-_krb5_derive_key(krb5_context context,
- struct _krb5_encryption_type *et,
- struct _krb5_key_data *key,
- const void *constant,
- size_t len)
+static krb5_error_code
+derive_key_rfc3961(krb5_context context,
+ struct _krb5_encryption_type *et,
+ struct _krb5_key_data *key,
+ const void *constant,
+ size_t len)
{
+
unsigned char *k = NULL;
unsigned int nblocks = 0, i;
krb5_error_code ret = 0;
struct _krb5_key_type *kt = et->keytype;
- ret = _key_schedule(context, key);
- if(ret)
- return ret;
if(et->blocksize * 8 < kt->bits || len != et->blocksize) {
nblocks = (kt->bits + et->blocksize * 8 - 1) / (et->blocksize * 8);
k = malloc(nblocks * et->blocksize);
if(k == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
ret = _krb5_n_fold(constant, len, k, et->blocksize);
if (ret) {
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ krb5_enomem(context);
goto out;
}
@@ -1882,8 +2168,7 @@ _krb5_derive_key(krb5_context context,
size_t res_len = (kt->bits + 7) / 8;
if(len != 0 && c == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
memcpy(c, constant, len);
@@ -1891,43 +2176,113 @@ _krb5_derive_key(krb5_context context,
k = malloc(res_len);
if(res_len != 0 && k == NULL) {
free(c);
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
ret = _krb5_n_fold(c, len, k, res_len);
free(c);
if (ret) {
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ krb5_enomem(context);
goto out;
}
}
- /* XXX keytype dependent post-processing */
- switch(kt->type) {
- case ETYPE_OLD_DES3_CBC_SHA1:
+ if (kt->type == KRB5_ENCTYPE_OLD_DES3_CBC_SHA1)
_krb5_DES3_random_to_key(context, key->key, k, nblocks * et->blocksize);
- break;
- case ENCTYPE_AES128_CTS_HMAC_SHA1_96:
- case ENCTYPE_AES256_CTS_HMAC_SHA1_96:
+ else
memcpy(key->key->keyvalue.data, k, key->key->keyvalue.length);
+
+ out:
+ if (k) {
+ memset_s(k, nblocks * et->blocksize, 0, nblocks * et->blocksize);
+ free(k);
+ }
+ return ret;
+}
+
+static krb5_error_code
+derive_key_sp800_hmac(krb5_context context,
+ struct _krb5_encryption_type *et,
+ struct _krb5_key_data *key,
+ const void *constant,
+ size_t len)
+{
+ krb5_error_code ret;
+ struct _krb5_key_type *kt = et->keytype;
+ krb5_data label;
+ const EVP_MD *md = NULL;
+ const unsigned char *c = constant;
+ size_t key_len;
+ krb5_data K1;
+
+ ret = _krb5_aes_sha2_md_for_enctype(context, kt->type, &md);
+ if (ret)
+ return ret;
+
+ /*
+ * PRF usage: not handled here (output cannot be longer)
+ * Integrity usage: truncated hash (half length)
+ * Encryption usage: base key length
+ */
+ if (len == 5 && (c[4] == 0x99 || c[4] == 0x55))
+ key_len = EVP_MD_size(md) / 2;
+ else
+ key_len = kt->size;
+
+ ret = krb5_data_alloc(&K1, key_len);
+ if (ret)
+ return ret;
+
+ label.data = (void *)constant;
+ label.length = len;
+
+ ret = _krb5_SP800_108_HMAC_KDF(context, &key->key->keyvalue,
+ &label, NULL, md, &K1);
+ if (ret == 0) {
+ if (key->key->keyvalue.length > key_len)
+ key->key->keyvalue.length = key_len;
+ memcpy(key->key->keyvalue.data, K1.data, key_len);
+ }
+
+ memset_s(K1.data, K1.length, 0, K1.length);
+ krb5_data_free(&K1);
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_derive_key(krb5_context context,
+ struct _krb5_encryption_type *et,
+ struct _krb5_key_data *key,
+ const void *constant,
+ size_t len)
+{
+ krb5_error_code ret;
+
+ ret = _key_schedule(context, key);
+ if(ret)
+ return ret;
+
+ switch (et->flags & F_KDF_MASK) {
+ case F_RFC3961_KDF:
+ ret = derive_key_rfc3961(context, et, key, constant, len);
+ break;
+ case F_SP800_108_HMAC_KDF:
+ ret = derive_key_sp800_hmac(context, et, key, constant, len);
break;
default:
ret = KRB5_CRYPTO_INTERNAL;
krb5_set_error_message(context, ret,
N_("derive_key() called with unknown keytype (%u)", ""),
- kt->type);
+ et->keytype->type);
break;
}
- out:
+
if (key->schedule) {
free_key_schedule(context, key, et);
key->schedule = NULL;
}
- if (k) {
- memset(k, 0, nblocks * et->blocksize);
- free(k);
- }
+
return ret;
}
@@ -1986,21 +2341,19 @@ _get_derived_key(krb5_context context,
struct _krb5_key_data *d;
unsigned char constant[5];
+ *key = NULL;
for(i = 0; i < crypto->num_key_usage; i++)
if(crypto->key_usage[i].usage == usage) {
*key = &crypto->key_usage[i].key;
return 0;
}
d = _new_derived_key(crypto, usage);
- if(d == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
- krb5_copy_keyblock(context, crypto->key.key, &d->key);
- _krb5_put_int(constant, usage, 5);
- _krb5_derive_key(context, crypto->et, d, constant, sizeof(constant));
+ if (d == NULL)
+ return krb5_enomem(context);
*key = d;
- return 0;
+ krb5_copy_keyblock(context, crypto->key.key, &d->key);
+ _krb5_put_int(constant, usage, sizeof(constant));
+ return _krb5_derive_key(context, crypto->et, d, constant, sizeof(constant));
}
/**
@@ -2029,11 +2382,9 @@ krb5_crypto_init(krb5_context context,
{
krb5_error_code ret;
ALLOC(*crypto, 1);
- if(*crypto == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
- if(etype == ETYPE_NULL)
+ if (*crypto == NULL)
+ return krb5_enomem(context);
+ if(etype == (krb5_enctype)ETYPE_NULL)
etype = key->keytype;
(*crypto)->et = _krb5_find_enctype(etype);
if((*crypto)->et == NULL || ((*crypto)->et->flags & F_DISABLED)) {
@@ -2071,7 +2422,7 @@ free_key_schedule(krb5_context context,
krb5_free_data(context, key->schedule);
}
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_free_key_data(krb5_context context, struct _krb5_key_data *key,
struct _krb5_encryption_type *et)
{
@@ -2280,6 +2631,47 @@ krb5_allow_weak_crypto(krb5_context context,
return 0;
}
+/**
+ * Returns is the encryption is strong or weak
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to probe
+ *
+ * @return Returns true if encryption type is weak or is not supported.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_is_enctype_weak(krb5_context context, krb5_enctype enctype)
+{
+ struct _krb5_encryption_type *et = _krb5_find_enctype(enctype);
+ if(et == NULL || (et->flags & F_WEAK))
+ return TRUE;
+ return FALSE;
+}
+
+/**
+ * Returns whether the encryption type should use randomly generated salts
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to probe
+ *
+ * @return Returns true if generated salts should have random component
+ *
+ * @ingroup krb5_crypto
+ */
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_enctype_requires_random_salt(krb5_context context,
+ krb5_enctype enctype)
+{
+ struct _krb5_encryption_type *et;
+
+ et = _krb5_find_enctype (enctype);
+
+ return et && (et->flags & F_SP800_108_HMAC_KDF);
+}
+
static size_t
wrapped_length (krb5_context context,
krb5_crypto crypto,
@@ -2516,7 +2908,7 @@ krb5_crypto_prfplus(krb5_context context,
krb5_data_free(&input2);
if (ret)
krb5_data_free(output);
- return 0;
+ return ret;
}
/**
@@ -2549,14 +2941,14 @@ krb5_crypto_fx_cf2(krb5_context context,
size_t i, keysize;
memset(res, 0, sizeof(*res));
+ krb5_data_zero(&os1);
+ krb5_data_zero(&os2);
- ret = krb5_enctype_keysize(context, enctype, &keysize);
+ ret = krb5_enctype_keybits(context, enctype, &keysize);
if (ret)
return ret;
+ keysize = (keysize + 7) / 8;
- ret = krb5_data_alloc(&res->keyvalue, keysize);
- if (ret)
- goto out;
ret = krb5_crypto_prfplus(context, crypto1, pepper1, keysize, &os1);
if (ret)
goto out;
@@ -2566,13 +2958,12 @@ krb5_crypto_fx_cf2(krb5_context context,
res->keytype = enctype;
{
- unsigned char *p1 = os1.data, *p2 = os2.data, *p3 = res->keyvalue.data;
+ unsigned char *p1 = os1.data, *p2 = os2.data;
for (i = 0; i < keysize; i++)
- p3[i] = p1[i] ^ p2[i];
+ p1[i] ^= p2[i];
}
+ ret = krb5_random_to_key(context, enctype, os1.data, keysize, res);
out:
- if (ret)
- krb5_data_free(&res->keyvalue);
krb5_data_free(&os1);
krb5_data_free(&os2);
@@ -2613,10 +3004,8 @@ krb5_keytype_to_enctypes (krb5_context context,
}
ret = malloc(n * sizeof(*ret));
- if (ret == NULL && n != 0) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
- }
+ if (ret == NULL && n != 0)
+ return krb5_enomem(context);
n = 0;
for (i = _krb5_num_etypes - 1; i >= 0; --i) {
if (_krb5_etypes[i]->keytype->type == keytype
diff --git a/lib/krb5/crypto.h b/lib/krb5/crypto.h
index 9b95b8f0cbcc..6b0fe8d85aab 100644
--- a/lib/krb5/crypto.h
+++ b/lib/krb5/crypto.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -52,14 +52,22 @@ struct krb5_crypto_data {
#define CRYPTO_ETYPE(C) ((C)->et->type)
/* bits for `flags' below */
-#define F_KEYED 1 /* checksum is keyed */
-#define F_CPROOF 2 /* checksum is collision proof */
-#define F_DERIVED 4 /* uses derived keys */
-#define F_VARIANT 8 /* uses `variant' keys (6.4.3) */
-#define F_PSEUDO 16 /* not a real protocol type */
-#define F_SPECIAL 32 /* backwards */
-#define F_DISABLED 64 /* enctype/checksum disabled */
-#define F_WEAK 128 /* enctype is considered weak */
+#define F_KEYED 0x0001 /* checksum is keyed */
+#define F_CPROOF 0x0002 /* checksum is collision proof */
+#define F_DERIVED 0x0004 /* uses derived keys */
+#define F_VARIANT 0x0008 /* uses `variant' keys (6.4.3) */
+#define F_PSEUDO 0x0010 /* not a real protocol type */
+#define F_DISABLED 0x0020 /* enctype/checksum disabled */
+#define F_WEAK 0x0040 /* enctype is considered weak */
+
+#define F_RFC3961_ENC 0x0100 /* RFC3961 simplified profile */
+#define F_SPECIAL 0x0200 /* backwards */
+#define F_ENC_THEN_CKSUM 0x0400 /* checksum is over encrypted data */
+#define F_CRYPTO_MASK 0x0F00
+
+#define F_RFC3961_KDF 0x1000 /* RFC3961 KDF */
+#define F_SP800_108_HMAC_KDF 0x2000 /* SP800-108 HMAC KDF */
+#define F_KDF_MASK 0xF000
struct salt_type {
krb5_salttype type;
@@ -103,6 +111,7 @@ struct _krb5_checksum_type {
struct _krb5_encryption_type {
krb5_enctype type;
const char *name;
+ const char *alias;
size_t blocksize;
size_t padsize;
size_t confoundersize;
@@ -137,15 +146,19 @@ extern struct _krb5_checksum_type _krb5_checksum_rsa_md5;
extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_des3;
extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes128;
extern struct _krb5_checksum_type _krb5_checksum_hmac_sha1_aes256;
+extern struct _krb5_checksum_type _krb5_checksum_hmac_sha256_128_aes128;
+extern struct _krb5_checksum_type _krb5_checksum_hmac_sha384_192_aes256;
extern struct _krb5_checksum_type _krb5_checksum_hmac_md5;
extern struct _krb5_checksum_type _krb5_checksum_sha1;
+extern struct _krb5_checksum_type _krb5_checksum_sha2;
extern struct _krb5_checksum_type *_krb5_checksum_types[];
extern int _krb5_num_checksums;
/* Salts */
-extern struct salt_type _krb5_AES_salt[];
+extern struct salt_type _krb5_AES_SHA1_salt[];
+extern struct salt_type _krb5_AES_SHA2_salt[];
extern struct salt_type _krb5_arcfour_salt[];
extern struct salt_type _krb5_des_salt[];
extern struct salt_type _krb5_des3_salt[];
@@ -155,6 +168,8 @@ extern struct salt_type _krb5_des3_salt_derived[];
extern struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha1;
extern struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha1;
+extern struct _krb5_encryption_type _krb5_enctype_aes128_cts_hmac_sha256_128;
+extern struct _krb5_encryption_type _krb5_enctype_aes256_cts_hmac_sha384_192;
extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_sha1;
extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_md5;
extern struct _krb5_encryption_type _krb5_enctype_des3_cbc_none;
@@ -172,8 +187,15 @@ extern struct _krb5_encryption_type _krb5_enctype_null;
extern struct _krb5_encryption_type *_krb5_etypes[];
extern int _krb5_num_etypes;
+/* NO_HCRYPTO_POLLUTION is defined in pkinit-ec.c. See commentary there. */
+#ifndef NO_HCRYPTO_POLLUTION
/* Interface to the EVP crypto layer provided by hcrypto */
struct _krb5_evp_schedule {
+ /*
+ * Normally we'd say EVP_CIPHER_CTX here, but! this header gets
+ * included in lib/krb5/pkinit-ec.ck
+ */
EVP_CIPHER_CTX ectx;
EVP_CIPHER_CTX dctx;
};
+#endif
diff --git a/lib/krb5/data.c b/lib/krb5/data.c
index f62a5532ab60..d49685d94d4f 100644
--- a/lib/krb5/data.c
+++ b/lib/krb5/data.c
@@ -62,8 +62,7 @@ krb5_data_zero(krb5_data *p)
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_data_free(krb5_data *p)
{
- if(p->data != NULL)
- free(p->data);
+ free(p->data);
krb5_data_zero(p);
}
@@ -176,10 +175,8 @@ krb5_copy_data(krb5_context context,
{
krb5_error_code ret;
ALLOC(*outdata, 1);
- if(*outdata == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
- }
+ if(*outdata == NULL)
+ return krb5_enomem(context);
ret = der_copy_octet_string(indata, *outdata);
if(ret) {
krb5_clear_error_message (context);
diff --git a/lib/krb5/db_plugin.c b/lib/krb5/db_plugin.c
new file mode 100644
index 000000000000..a46bbc1f0106
--- /dev/null
+++ b/lib/krb5/db_plugin.c
@@ -0,0 +1,31 @@
+/*
+ */
+
+#include "krb5_locl.h"
+#include "db_plugin.h"
+
+/* Default plugin (DB using binary search of sorted text file) follows */
+static heim_base_once_t db_plugins_once = HEIM_BASE_ONCE_INIT;
+
+static krb5_error_code KRB5_LIB_CALL
+db_plugins_plcallback(krb5_context context, const void *plug, void *plugctx,
+ void *userctx)
+{
+ return 0;
+}
+
+static void
+db_plugins_init(void *arg)
+{
+ krb5_context context = arg;
+ (void)_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_DB,
+ KRB5_PLUGIN_DB_VERSION_0, 0, NULL,
+ db_plugins_plcallback);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_load_db_plugins(krb5_context context)
+{
+ heim_base_once_f(&db_plugins_once, context, db_plugins_init);
+}
+
diff --git a/lib/krb5/db_plugin.h b/lib/krb5/db_plugin.h
new file mode 100644
index 000000000000..730c06095558
--- /dev/null
+++ b/lib/krb5/db_plugin.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* $Id$ */
+
+#ifndef HEIMDAL_KRB5_DB_PLUGIN_H
+#define HEIMDAL_KRB5_DB_PLUGIN_H 1
+
+#define KRB5_PLUGIN_DB "krb5_db_plug"
+#define KRB5_PLUGIN_DB_VERSION_0 0
+
+/** @struct krb5plugin_db_ftable_desc
+ *
+ * @brief Description of the krb5 DB plugin facility.
+ *
+ * The krb5_aname_to_lname(3) function's DB rule is pluggable. The
+ * plugin is named KRB5_PLUGIN_DB ("krb5_db_plug"), with a single minor
+ * version, KRB5_PLUGIN_DB_VERSION_0 (0).
+ *
+ * The plugin consists of a data symbol referencing a structure of type
+ * krb5plugin_db_ftable_desc, with three fields:
+ *
+ * @param init Plugin initialization function (see krb5-plugin(7))
+ *
+ * @param minor_version The plugin minor version number (0)
+ *
+ * @param fini Plugin finalization function
+ *
+ * The init entry point is expected to call heim_db_register(). The
+ * fini entry point is expected to do nothing.
+ *
+ * @ingroup krb5_support
+ */
+typedef struct krb5plugin_db_ftable_desc {
+ int minor_version;
+ krb5_error_code (KRB5_LIB_CALL *init)(krb5_context, void **);
+ void (KRB5_LIB_CALL *fini)(void *);
+} krb5plugin_db_ftable;
+
+#endif /* HEIMDAL_KRB5_DB_PLUGIN_H */
+
diff --git a/lib/krb5/dcache.c b/lib/krb5/dcache.c
new file mode 100644
index 000000000000..c89e157de5a3
--- /dev/null
+++ b/lib/krb5/dcache.c
@@ -0,0 +1,699 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+typedef struct krb5_dcache{
+ krb5_ccache fcache;
+ char *dir;
+ char *name;
+} krb5_dcache;
+
+#define DCACHE(X) ((krb5_dcache*)(X)->data.data)
+#define D2FCACHE(X) ((X)->fcache)
+
+static krb5_error_code KRB5_CALLCONV dcc_close(krb5_context, krb5_ccache);
+static krb5_error_code KRB5_CALLCONV dcc_get_default_name(krb5_context, char **);
+
+
+static char *
+primary_create(krb5_dcache *dc)
+{
+ char *primary = NULL;
+
+ asprintf(&primary, "%s/primary", dc->dir);
+ if (primary == NULL)
+ return NULL;
+
+ return primary;
+}
+
+static int
+is_filename_cacheish(const char *name)
+{
+ return strncmp(name, "tkt", 3) == 0;
+
+}
+
+static krb5_error_code
+set_default_cache(krb5_context context, krb5_dcache *dc, const char *residual)
+{
+ char *path = NULL, *primary = NULL;
+ krb5_error_code ret;
+ struct iovec iov[2];
+ size_t len;
+ int fd = -1;
+
+ if (!is_filename_cacheish(residual)) {
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ "name %s is not a cache (doesn't start with tkt)", residual);
+ return KRB5_CC_FORMAT;
+ }
+
+ asprintf(&path, "%s/primary-XXXXXX", dc->dir);
+ if (path == NULL)
+ return krb5_enomem(context);
+
+ fd = mkstemp(path);
+ if (fd < 0) {
+ ret = errno;
+ goto out;
+ }
+ rk_cloexec(fd);
+#ifndef _WIN32
+ if (fchmod(fd, S_IRUSR | S_IWUSR) < 0) {
+ ret = errno;
+ goto out;
+ }
+#endif
+ len = strlen(residual);
+
+ iov[0].iov_base = rk_UNCONST(residual);
+ iov[0].iov_len = len;
+ iov[1].iov_base = "\n";
+ iov[1].iov_len = 1;
+
+ if (writev(fd, iov, sizeof(iov)/sizeof(iov[0])) != len + 1) {
+ ret = errno;
+ goto out;
+ }
+
+ primary = primary_create(dc);
+ if (primary == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+
+ if (rename(path, primary) < 0) {
+ ret = errno;
+ goto out;
+ }
+
+ close(fd);
+ fd = -1;
+
+ ret = 0;
+ out:
+ if (fd >= 0) {
+ (void)unlink(path);
+ close(fd);
+ }
+ if (path)
+ free(path);
+ if (primary)
+ free(primary);
+
+ return ret;
+}
+
+static krb5_error_code
+get_default_cache(krb5_context context, krb5_dcache *dc, char **residual)
+{
+ krb5_error_code ret;
+ char buf[MAXPATHLEN];
+ char *primary;
+ FILE *f;
+
+ *residual = NULL;
+ primary = primary_create(dc);
+ if (primary == NULL)
+ return krb5_enomem(context);
+
+ f = fopen(primary, "r");
+ if (f == NULL) {
+ if (errno == ENOENT) {
+ free(primary);
+ *residual = strdup("tkt");
+ if (*residual == NULL)
+ return krb5_enomem(context);
+ return 0;
+ }
+ ret = errno;
+ krb5_set_error_message(context, ret, "failed to open %s", primary);
+ free(primary);
+ return ret;
+ }
+
+ if (fgets(buf, sizeof(buf), f) == NULL) {
+ ret = ferror(f);
+ fclose(f);
+ krb5_set_error_message(context, ret, "read file %s", primary);
+ free(primary);
+ return ret;
+ }
+ fclose(f);
+
+ buf[strcspn(buf, "\r\n")] = '\0';
+
+ if (!is_filename_cacheish(buf)) {
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ "name in %s is not a cache (doesn't start with tkt)", primary);
+ free(primary);
+ return KRB5_CC_FORMAT;
+ }
+
+ free(primary);
+
+ *residual = strdup(buf);
+ if (*residual == NULL)
+ return krb5_enomem(context);
+
+ return 0;
+}
+
+
+
+static const char* KRB5_CALLCONV
+dcc_get_name(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return dc->name;
+}
+
+
+static krb5_error_code
+verify_directory(krb5_context context, const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) != 0) {
+ if (errno == ENOENT) {
+ /* XXX should use mkdirx_np() */
+ if (rk_mkdir(path, S_IRWXU) == 0)
+ return 0;
+
+ krb5_set_error_message(context, ENOENT,
+ N_("DIR directory %s doesn't exists", ""), path);
+ return ENOENT;
+ } else {
+ int ret = errno;
+ krb5_set_error_message(context, ret,
+ N_("DIR directory %s is bad: %s", ""), path, strerror(ret));
+ return errno;
+ }
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ krb5_set_error_message(context, KRB5_CC_BADNAME,
+ N_("DIR directory %s is not a directory", ""), path);
+ return KRB5_CC_BADNAME;
+ }
+
+ return 0;
+}
+
+static void
+dcc_release(krb5_context context, krb5_dcache *dc)
+{
+ if (dc->fcache)
+ krb5_cc_close(context, dc->fcache);
+ if (dc->dir)
+ free(dc->dir);
+ if (dc->name)
+ free(dc->name);
+ memset(dc, 0, sizeof(*dc));
+ free(dc);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
+{
+ char *filename = NULL;
+ krb5_error_code ret;
+ krb5_dcache *dc;
+ const char *p;
+
+ p = res;
+ do {
+ p = strstr(p, "..");
+ if (p && (p == res || ISPATHSEP(p[-1])) && (ISPATHSEP(p[2]) || p[2] == '\0')) {
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ N_("Path contains a .. component", ""));
+ return KRB5_CC_FORMAT;
+ }
+ if (p)
+ p += 3;
+ } while (p);
+
+ dc = calloc(1, sizeof(*dc));
+ if (dc == NULL) {
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ return KRB5_CC_NOMEM;
+ }
+
+ /* check for explicit component */
+ if (res[0] == ':') {
+ char *q;
+
+ dc->dir = strdup(&res[1]);
+#ifdef _WIN32
+ q = strrchr(dc->dir, '\\');
+ if (q == NULL)
+#endif
+ q = strrchr(dc->dir, '/');
+ if (q) {
+ *q++ = '\0';
+ } else {
+ krb5_set_error_message(context, KRB5_CC_FORMAT, N_("Cache not an absolute path: %s", ""), dc->dir);
+ dcc_release(context, dc);
+ return KRB5_CC_FORMAT;
+ }
+
+ if (!is_filename_cacheish(q)) {
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ N_("Name %s is not a cache (doesn't start with tkt)", ""), q);
+ dcc_release(context, dc);
+ return KRB5_CC_FORMAT;
+ }
+
+ ret = verify_directory(context, dc->dir);
+ if (ret) {
+ dcc_release(context, dc);
+ return ret;
+ }
+
+ dc->name = strdup(res);
+ if (dc->name == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+
+ } else {
+ char *residual;
+ size_t len;
+
+ dc->dir = strdup(res);
+ if (dc->dir == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+
+ len = strlen(dc->dir);
+
+ if (ISPATHSEP(dc->dir[len - 1]))
+ dc->dir[len - 1] = '\0';
+
+ ret = verify_directory(context, dc->dir);
+ if (ret) {
+ dcc_release(context, dc);
+ return ret;
+ }
+
+ ret = get_default_cache(context, dc, &residual);
+ if (ret) {
+ dcc_release(context, dc);
+ return ret;
+ }
+ asprintf(&dc->name, ":%s/%s", dc->dir, residual);
+ free(residual);
+ if (dc->name == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+ }
+
+ asprintf(&filename, "FILE%s", dc->name);
+ if (filename == NULL) {
+ dcc_release(context, dc);
+ return krb5_enomem(context);
+ }
+
+ ret = krb5_cc_resolve(context, filename, &dc->fcache);
+ free(filename);
+ if (ret) {
+ dcc_release(context, dc);
+ return ret;
+ }
+
+
+ (*id)->data.data = dc;
+ (*id)->data.length = sizeof(*dc);
+ return 0;
+}
+
+static char *
+copy_default_dcc_cache(krb5_context context)
+{
+ const char *defname;
+ krb5_error_code ret;
+ char *name = NULL;
+ size_t len;
+
+ len = strlen(krb5_dcc_ops.prefix);
+
+ defname = krb5_cc_default_name(context);
+ if (defname == NULL ||
+ strncmp(defname, krb5_dcc_ops.prefix, len) != 0 ||
+ defname[len] != ':')
+ {
+ ret = dcc_get_default_name(context, &name);
+ if (ret)
+ return NULL;
+
+ return name;
+ } else {
+ return strdup(&defname[len + 1]);
+ }
+}
+
+
+static krb5_error_code KRB5_CALLCONV
+dcc_gen_new(krb5_context context, krb5_ccache *id)
+{
+ krb5_error_code ret;
+ char *name = NULL;
+ krb5_dcache *dc;
+ int fd;
+ size_t len;
+
+ name = copy_default_dcc_cache(context);
+ if (name == NULL) {
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ N_("Can't generate DIR caches unless its the default type", ""));
+ return KRB5_CC_FORMAT;
+ }
+
+ len = strlen(krb5_dcc_ops.prefix);
+ if (strncmp(name, krb5_dcc_ops.prefix, len) == 0 && name[len] == ':')
+ ++len;
+ else
+ len = 0;
+
+ ret = dcc_resolve(context, id, name + len);
+ free(name);
+ name = NULL;
+ if (ret)
+ return ret;
+
+ dc = DCACHE((*id));
+
+ asprintf(&name, ":%s/tktXXXXXX", dc->dir);
+ if (name == NULL) {
+ dcc_close(context, *id);
+ return krb5_enomem(context);
+ }
+
+ fd = mkstemp(&name[1]);
+ if (fd < 0) {
+ dcc_close(context, *id);
+ return krb5_enomem(context);
+ }
+ close(fd);
+
+ free(dc->name);
+ dc->name = name;
+
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_initialize(krb5_context context,
+ krb5_ccache id,
+ krb5_principal primary_principal)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_initialize(context, D2FCACHE(dc), primary_principal);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_close(krb5_context context,
+ krb5_ccache id)
+{
+ dcc_release(context, DCACHE(id));
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_destroy(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_dcache *dc = DCACHE(id);
+ krb5_ccache fcache = D2FCACHE(dc);
+ dc->fcache = NULL;
+ return krb5_cc_destroy(context, fcache);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_store_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_creds *creds)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_store_cred(context, D2FCACHE(dc), creds);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_principal(krb5_context context,
+ krb5_ccache id,
+ krb5_principal *principal)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_get_principal(context, D2FCACHE(dc), principal);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_first (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_start_seq_get(context, D2FCACHE(dc), cursor);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_next (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *creds)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_next_cred(context, D2FCACHE(dc), cursor, creds);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_end_get (krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_end_seq_get(context, D2FCACHE(dc), cursor);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *cred)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_remove_cred(context, D2FCACHE(dc), which, cred);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_set_flags(krb5_context context,
+ krb5_ccache id,
+ krb5_flags flags)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_set_flags(context, D2FCACHE(dc), flags);
+}
+
+static int KRB5_CALLCONV
+dcc_get_version(krb5_context context,
+ krb5_ccache id)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_get_version(context, D2FCACHE(dc));
+}
+
+struct dcache_iter {
+ int first;
+ krb5_dcache *dc;
+};
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
+{
+ struct dcache_iter *iter;
+ krb5_error_code ret;
+ char *name;
+
+ *cursor = NULL;
+ iter = calloc(1, sizeof(*iter));
+ if (iter == NULL)
+ return krb5_enomem(context);
+ iter->first = 1;
+
+ name = copy_default_dcc_cache(context);
+ if (name == NULL) {
+ free(iter);
+ krb5_set_error_message(context, KRB5_CC_FORMAT,
+ N_("Can't generate DIR caches unless its the default type", ""));
+ return KRB5_CC_FORMAT;
+ }
+
+ ret = dcc_resolve(context, NULL, name);
+ free(name);
+ if (ret) {
+ free(iter);
+ return ret;
+ }
+
+ /* XXX We need to opendir() here */
+
+ *cursor = iter;
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
+{
+ struct dcache_iter *iter = cursor;
+
+ if (iter == NULL)
+ return krb5_einval(context, 2);
+
+ if (!iter->first) {
+ krb5_clear_error_message(context);
+ return KRB5_CC_END;
+ }
+
+ /* XXX We need to readdir() here */
+ iter->first = 0;
+
+ return KRB5_CC_END;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
+{
+ struct dcache_iter *iter = cursor;
+
+ if (iter == NULL)
+ return krb5_einval(context, 2);
+
+ /* XXX We need to closedir() here */
+ if (iter->dc)
+ dcc_release(context, iter->dc);
+ free(iter);
+ return 0;
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
+{
+ krb5_dcache *dcfrom = DCACHE(from);
+ krb5_dcache *dcto = DCACHE(to);
+ return krb5_cc_move(context, D2FCACHE(dcfrom), D2FCACHE(dcto));
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_default_name(krb5_context context, char **str)
+{
+ return _krb5_expand_default_cc_name(context,
+ KRB5_DEFAULT_CCNAME_DIR,
+ str);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_set_default(krb5_context context, krb5_ccache id)
+{
+ krb5_dcache *dc = DCACHE(id);
+ const char *name;
+
+ name = krb5_cc_get_name(context, D2FCACHE(dc));
+ if (name == NULL)
+ return ENOENT;
+
+ return set_default_cache(context, dc, name);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_last_change_time(context, D2FCACHE(dc), mtime);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_set_kdc_offset(context, D2FCACHE(dc), kdc_offset);
+}
+
+static krb5_error_code KRB5_CALLCONV
+dcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
+{
+ krb5_dcache *dc = DCACHE(id);
+ return krb5_cc_get_kdc_offset(context, D2FCACHE(dc), kdc_offset);
+}
+
+
+/**
+ * Variable containing the DIR based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
+KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops = {
+ KRB5_CC_OPS_VERSION,
+ "DIR",
+ dcc_get_name,
+ dcc_resolve,
+ dcc_gen_new,
+ dcc_initialize,
+ dcc_destroy,
+ dcc_close,
+ dcc_store_cred,
+ NULL, /* dcc_retrieve */
+ dcc_get_principal,
+ dcc_get_first,
+ dcc_get_next,
+ dcc_end_get,
+ dcc_remove_cred,
+ dcc_set_flags,
+ dcc_get_version,
+ dcc_get_cache_first,
+ dcc_get_cache_next,
+ dcc_end_cache_get,
+ dcc_move,
+ dcc_get_default_name,
+ dcc_set_default,
+ dcc_lastchange,
+ dcc_set_kdc_offset,
+ dcc_get_kdc_offset
+};
diff --git a/lib/krb5/deprecated.c b/lib/krb5/deprecated.c
index 1d44d21b1706..e3c142d889df 100644
--- a/lib/krb5/deprecated.c
+++ b/lib/krb5/deprecated.c
@@ -31,8 +31,14 @@
* SUCH DAMAGE.
*/
+#ifdef __GNUC__
+/* For some GCCs there's no way to shut them up about deprecated functions */
+#define KRB5_DEPRECATED_FUNCTION(x)
+#endif
+
#include "krb5_locl.h"
+
#undef __attribute__
#define __attribute__(x)
@@ -72,16 +78,14 @@ krb5_keytype_to_enctypes_default (krb5_context context,
unsigned int i, n;
krb5_enctype *ret;
- if (keytype != KEYTYPE_DES || context->etypes_des == NULL)
+ if (keytype != (krb5_keytype)KEYTYPE_DES || context->etypes_des == NULL)
return krb5_keytype_to_enctypes (context, keytype, len, val);
for (n = 0; context->etypes_des[n]; ++n)
;
ret = malloc (n * sizeof(*ret));
- if (ret == NULL && n != 0) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ret == NULL && n != 0)
+ return krb5_enomem(context);
for (i = 0; i < n; ++i)
ret[i] = context->etypes_des[i];
*len = n;
@@ -94,13 +98,13 @@ static struct {
const char *name;
krb5_keytype type;
} keys[] = {
- { "null", ENCTYPE_NULL },
- { "des", ETYPE_DES_CBC_CRC },
- { "des3", ETYPE_OLD_DES3_CBC_SHA1 },
- { "aes-128", ETYPE_AES128_CTS_HMAC_SHA1_96 },
- { "aes-256", ETYPE_AES256_CTS_HMAC_SHA1_96 },
- { "arcfour", ETYPE_ARCFOUR_HMAC_MD5 },
- { "arcfour-56", ETYPE_ARCFOUR_HMAC_MD5_56 }
+ { "null", KRB5_ENCTYPE_NULL },
+ { "des", KRB5_ENCTYPE_DES_CBC_CRC },
+ { "des3", KRB5_ENCTYPE_OLD_DES3_CBC_SHA1 },
+ { "aes-128", KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96 },
+ { "aes-256", KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96 },
+ { "arcfour", KRB5_ENCTYPE_ARCFOUR_HMAC_MD5 },
+ { "arcfour-56", KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56 }
};
static int num_keys = sizeof(keys) / sizeof(keys[0]);
@@ -134,11 +138,8 @@ krb5_keytype_to_string(krb5_context context,
return KRB5_PROG_KEYTYPE_NOSUPP;
}
*string = strdup(name);
- if(*string == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*string == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -195,10 +196,8 @@ krb5_password_key_proc (krb5_context context,
char buf[BUFSIZ];
*key = malloc (sizeof (**key));
- if (*key == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
- }
+ if (*key == NULL)
+ return krb5_enomem(context);
if (password == NULL) {
if(UI_UTIL_read_pw_string (buf, sizeof(buf), "Password: ", 0)) {
free (*key);
@@ -471,7 +470,7 @@ krb5_free_error_string(krb5_context context, char *str)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_error_string(krb5_context context, const char *fmt, ...)
- __attribute__((format (printf, 2, 3)))
+ __attribute__ ((__format__ (__printf__, 2, 3)))
KRB5_DEPRECATED_FUNCTION("Use X instead")
{
va_list ap;
@@ -489,7 +488,8 @@ krb5_set_error_string(krb5_context context, const char *fmt, ...)
* Deprecated: use krb5_vset_error_message()
*
* @param context Kerberos context
- * @param msg error message to free
+ * @param fmt error message to free
+ * @param args variable argument list vector
*
* @return Return an error code or 0.
*
@@ -498,7 +498,7 @@ krb5_set_error_string(krb5_context context, const char *fmt, ...)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vset_error_string(krb5_context context, const char *fmt, va_list args)
- __attribute__ ((format (printf, 2, 0)))
+ __attribute__ ((__format__ (__printf__, 2, 0)))
KRB5_DEPRECATED_FUNCTION("Use X instead")
{
krb5_vset_error_message(context, 0, fmt, args);
@@ -606,4 +606,133 @@ krb5_auth_getremoteseqnumber(krb5_context context,
return 0;
}
+/**
+ * Return the error message in context. On error or no error string,
+ * the function returns NULL.
+ *
+ * @param context Kerberos 5 context
+ *
+ * @return an error string, needs to be freed with
+ * krb5_free_error_message(). The functions return NULL on error.
+ *
+ * @ingroup krb5_error
+ */
+
+KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
+krb5_get_error_string(krb5_context context)
+ KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead")
+{
+ char *ret = NULL;
+
+ HEIMDAL_MUTEX_lock(&context->mutex);
+ if (context->error_string)
+ ret = strdup(context->error_string);
+ HEIMDAL_MUTEX_unlock(&context->mutex);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_have_error_string(krb5_context context)
+ KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead")
+{
+ char *str;
+ HEIMDAL_MUTEX_lock(&context->mutex);
+ str = context->error_string;
+ HEIMDAL_MUTEX_unlock(&context->mutex);
+ return str != NULL;
+}
+
+struct send_to_kdc {
+ krb5_send_to_kdc_func func;
+ void *data;
+};
+
+/*
+ * Send the data `send' to one host from `handle` and get back the reply
+ * in `receive'.
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto (krb5_context context,
+ const krb5_data *send_data,
+ krb5_krbhst_handle handle,
+ krb5_data *receive)
+{
+ krb5_error_code ret;
+ krb5_sendto_ctx ctx;
+
+ ret = krb5_sendto_ctx_alloc(context, &ctx);
+ if (ret)
+ return ret;
+ _krb5_sendto_ctx_set_krb5hst(context, ctx, handle);
+
+ ret = krb5_sendto_context(context, ctx, send_data, (char *)_krb5_krbhst_get_realm(handle), receive);
+ krb5_sendto_ctx_free(context, ctx);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_kdc(krb5_context context,
+ const krb5_data *send_data,
+ const krb5_realm *realm,
+ krb5_data *receive)
+{
+ return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_kdc_flags(krb5_context context,
+ const krb5_data *send_data,
+ const krb5_realm *realm,
+ krb5_data *receive,
+ int flags)
+{
+ krb5_error_code ret;
+ krb5_sendto_ctx ctx;
+
+ ret = krb5_sendto_ctx_alloc(context, &ctx);
+ if (ret)
+ return ret;
+ krb5_sendto_ctx_add_flags(ctx, flags);
+ krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL);
+
+ ret = krb5_sendto_context(context, ctx, send_data, *realm, receive);
+ krb5_sendto_ctx_free(context, ctx);
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_send_to_kdc_func(krb5_context context,
+ krb5_send_to_kdc_func func,
+ void *data)
+{
+ free(context->send_to_kdc);
+ if (func == NULL) {
+ context->send_to_kdc = NULL;
+ return 0;
+ }
+
+ context->send_to_kdc = malloc(sizeof(*context->send_to_kdc));
+ if (context->send_to_kdc == NULL) {
+ krb5_set_error_message(context, ENOMEM,
+ N_("malloc: out of memory", ""));
+ return ENOMEM;
+ }
+
+ context->send_to_kdc->func = func;
+ context->send_to_kdc->data = data;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_copy_send_to_kdc_func(krb5_context context, krb5_context to)
+{
+ if (context->send_to_kdc)
+ return krb5_set_send_to_kdc_func(to,
+ context->send_to_kdc->func,
+ context->send_to_kdc->data);
+ else
+ return krb5_set_send_to_kdc_func(to, NULL, NULL);
+}
+
#endif /* HEIMDAL_SMALLER */
diff --git a/lib/krb5/derived-key-test.c b/lib/krb5/derived-key-test.c
index a67c95a54cb6..0e927643f9eb 100644
--- a/lib/krb5/derived-key-test.c
+++ b/lib/krb5/derived-key-test.c
@@ -33,7 +33,7 @@
#include "krb5_locl.h"
#include <err.h>
-enum { MAXSIZE = 24 };
+enum { MAXSIZE = 32 };
static struct testcase {
krb5_enctype enctype;
@@ -72,7 +72,31 @@ static struct testcase {
{ETYPE_DES3_CBC_SHA1, {0x00, 0x00, 0x00, 0x01, 0xaa}, 5,
{0x26, 0xdc, 0xe3, 0x34, 0xb5, 0x45, 0x29, 0x2f, 0x2f, 0xea, 0xb9, 0xa8, 0x70, 0x1a, 0x89, 0xa4, 0xb9, 0x9e, 0xb9, 0x94, 0x2c, 0xec, 0xd0, 0x16},
{0xf4, 0x8f, 0xfd, 0x6e, 0x83, 0xf8, 0x3e, 0x73, 0x54, 0xe6, 0x94, 0xfd, 0x25, 0x2c, 0xf8, 0x3b, 0xfe, 0x58, 0xf7, 0xd5, 0xba, 0x37, 0xec, 0x5d}},
- {0}
+ {ETYPE_AES128_CTS_HMAC_SHA256_128, {0x00, 0x00, 0x00, 0x02, 0x99}, 5,
+ {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C},
+ {0xB3, 0x1A, 0x01, 0x8A, 0x48, 0xF5, 0x47, 0x76, 0xF4, 0x03, 0xE9, 0xA3, 0x96, 0x32, 0x5D, 0xC3}},
+ {ETYPE_AES128_CTS_HMAC_SHA256_128, {0x00, 0x00, 0x00, 0x02, 0xAA}, 5,
+ {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C},
+ {0x9B, 0x19, 0x7D, 0xD1, 0xE8, 0xC5, 0x60, 0x9D, 0x6E, 0x67, 0xC3, 0xE3, 0x7C, 0x62, 0xC7, 0x2E}},
+ {ETYPE_AES128_CTS_HMAC_SHA256_128, {0x00, 0x00, 0x00, 0x02, 0x55}, 5,
+ {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C},
+ {0x9F, 0xDA, 0x0E, 0x56, 0xAB, 0x2D, 0x85, 0xE1, 0x56, 0x9A, 0x68, 0x86, 0x96, 0xC2, 0x6A, 0x6C}},
+ {ETYPE_AES256_CTS_HMAC_SHA384_192, {0x00, 0x00, 0x00, 0x02, 0x99}, 5,
+ {0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98,
+ 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52},
+ {0xEF, 0x57, 0x18, 0xBE, 0x86, 0xCC, 0x84, 0x96, 0x3D, 0x8B, 0xBB, 0x50, 0x31, 0xE9, 0xF5, 0xC4,
+ 0xBA, 0x41, 0xF2, 0x8F, 0xAF, 0x69, 0xE7, 0x3D }},
+ {ETYPE_AES256_CTS_HMAC_SHA384_192, {0x00, 0x00, 0x00, 0x02, 0xAA}, 5,
+ {0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98,
+ 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52},
+ {0x56, 0xAB, 0x22, 0xBE, 0xE6, 0x3D, 0x82, 0xD7, 0xBC, 0x52, 0x27, 0xF6, 0x77, 0x3F, 0x8E, 0xA7,
+ 0xA5, 0xEB, 0x1C, 0x82, 0x51, 0x60, 0xC3, 0x83, 0x12, 0x98, 0x0C, 0x44, 0x2E, 0x5C, 0x7E, 0x49}},
+ {ETYPE_AES256_CTS_HMAC_SHA384_192, {0x00, 0x00, 0x00, 0x02, 0x55}, 5,
+ {0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98,
+ 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52},
+ {0x69, 0xB1, 0x65, 0x14, 0xE3, 0xCD, 0x8E, 0x56, 0xB8, 0x20, 0x10, 0xD5, 0xC7, 0x30, 0x12, 0xB6,
+ 0x22, 0xC4, 0xD0, 0x0F, 0xFC, 0x23, 0xED, 0x1F}},
+ {0, {0}, 0, {0}, {0}}
};
int
@@ -91,8 +115,8 @@ main(int argc, char **argv)
krb5_keyblock key;
krb5_keyblock *dkey;
- key.keytype = KEYTYPE_DES3;
- key.keyvalue.length = MAXSIZE;
+ key.keytype = t->enctype;
+ krb5_enctype_keysize(context, t->enctype, &key.keyvalue.length);
key.keyvalue.data = t->key;
ret = krb5_derive_key(context, &key, t->enctype, t->constant,
@@ -103,7 +127,7 @@ main(int argc, char **argv)
const unsigned char *p = dkey->keyvalue.data;
int i;
- printf ("derive_key failed\n");
+ printf ("derive_key failed (enctype %d)\n", t->enctype);
printf ("should be: ");
for (i = 0; i < dkey->keyvalue.length; ++i)
printf ("%02x", t->res[i]);
diff --git a/lib/krb5/digest.c b/lib/krb5/digest.c
index ef3267b3a766..7be249253bdb 100644
--- a/lib/krb5/digest.c
+++ b/lib/krb5/digest.c
@@ -54,8 +54,7 @@ krb5_digest_alloc(krb5_context context, krb5_digest *digest)
d = calloc(1, sizeof(*d));
if (d == NULL) {
*digest = NULL;
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
*digest = d;
@@ -106,8 +105,7 @@ krb5_digest_set_server_cb(krb5_context context,
free(digest->init.channel);
digest->init.channel = NULL;
}
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -120,10 +118,8 @@ krb5_digest_set_type(krb5_context context,
return EINVAL;
}
digest->init.type = strdup(type);
- if (digest->init.type == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->init.type == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -137,16 +133,13 @@ krb5_digest_set_hostname(krb5_context context,
return EINVAL;
}
digest->init.hostname = malloc(sizeof(*digest->init.hostname));
- if (digest->init.hostname == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->init.hostname == NULL)
+ return krb5_enomem(context);
*digest->init.hostname = strdup(hostname);
if (*digest->init.hostname == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->init.hostname);
digest->init.hostname = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -168,10 +161,8 @@ krb5_digest_set_server_nonce(krb5_context context,
return EINVAL;
}
digest->request.serverNonce = strdup(nonce);
- if (digest->request.serverNonce == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.serverNonce == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -192,10 +183,8 @@ krb5_digest_set_opaque(krb5_context context,
return EINVAL;
}
digest->request.opaque = strdup(opaque);
- if (digest->request.opaque == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.opaque == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -218,16 +207,13 @@ krb5_digest_set_identifier(krb5_context context,
return EINVAL;
}
digest->request.identifier = calloc(1, sizeof(*digest->request.identifier));
- if (digest->request.identifier == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.identifier == NULL)
+ return krb5_enomem(context);
*digest->request.identifier = strdup(id);
if (*digest->request.identifier == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.identifier);
digest->request.identifier = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -473,16 +459,13 @@ krb5_digest_set_client_nonce(krb5_context context,
}
digest->request.clientNonce =
calloc(1, sizeof(*digest->request.clientNonce));
- if (digest->request.clientNonce == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.clientNonce == NULL)
+ return krb5_enomem(context);
*digest->request.clientNonce = strdup(nonce);
if (*digest->request.clientNonce == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.clientNonce);
digest->request.clientNonce = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -498,10 +481,8 @@ krb5_digest_set_digest(krb5_context context,
return EINVAL;
}
digest->request.digest = strdup(dgst);
- if (digest->request.digest == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.digest == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -515,10 +496,8 @@ krb5_digest_set_username(krb5_context context,
return EINVAL;
}
digest->request.username = strdup(username);
- if (digest->request.username == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.username == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -532,16 +511,13 @@ krb5_digest_set_authid(krb5_context context,
return EINVAL;
}
digest->request.authid = malloc(sizeof(*digest->request.authid));
- if (digest->request.authid == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.authid == NULL)
+ return krb5_enomem(context);
*digest->request.authid = strdup(authid);
if (*digest->request.authid == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.authid);
digest->request.authid = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -576,16 +552,13 @@ krb5_digest_set_realm(krb5_context context,
return EINVAL;
}
digest->request.realm = malloc(sizeof(*digest->request.realm));
- if (digest->request.realm == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.realm == NULL)
+ return krb5_enomem(context);
*digest->request.realm = strdup(realm);
if (*digest->request.realm == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.realm);
digest->request.realm = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -601,16 +574,13 @@ krb5_digest_set_method(krb5_context context,
return EINVAL;
}
digest->request.method = malloc(sizeof(*digest->request.method));
- if (digest->request.method == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.method == NULL)
+ return krb5_enomem(context);
*digest->request.method = strdup(method);
if (*digest->request.method == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.method);
digest->request.method = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -625,16 +595,13 @@ krb5_digest_set_uri(krb5_context context,
return EINVAL;
}
digest->request.uri = malloc(sizeof(*digest->request.uri));
- if (digest->request.uri == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.uri == NULL)
+ return krb5_enomem(context);
*digest->request.uri = strdup(uri);
if (*digest->request.uri == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.uri);
digest->request.uri = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -651,16 +618,13 @@ krb5_digest_set_nonceCount(krb5_context context,
}
digest->request.nonceCount =
malloc(sizeof(*digest->request.nonceCount));
- if (digest->request.nonceCount == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.nonceCount == NULL)
+ return krb5_enomem(context);
*digest->request.nonceCount = strdup(nonce_count);
if (*digest->request.nonceCount == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.nonceCount);
digest->request.nonceCount = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -675,16 +639,13 @@ krb5_digest_set_qop(krb5_context context,
return EINVAL;
}
digest->request.qop = malloc(sizeof(*digest->request.qop));
- if (digest->request.qop == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.qop == NULL)
+ return krb5_enomem(context);
*digest->request.qop = strdup(qop);
if (*digest->request.qop == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
free(digest->request.qop);
digest->request.qop = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -695,10 +656,8 @@ krb5_digest_set_responseData(krb5_context context,
const char *response)
{
digest->request.responseData = strdup(response);
- if (digest->request.responseData == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (digest->request.responseData == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -803,8 +762,7 @@ krb5_digest_get_client_binding(krb5_context context,
if (*type == NULL || *binding == NULL) {
free(*type);
free(*binding);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
} else {
*type = NULL;
@@ -842,10 +800,8 @@ krb5_ntlm_alloc(krb5_context context,
krb5_ntlm *ntlm)
{
*ntlm = calloc(1, sizeof(**ntlm));
- if (*ntlm == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*ntlm == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -933,13 +889,13 @@ krb5_ntlm_init_get_flags(krb5_context context,
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_ntlm_init_get_challange(krb5_context context,
+krb5_ntlm_init_get_challenge(krb5_context context,
krb5_ntlm ntlm,
- krb5_data *challange)
+ krb5_data *challenge)
{
krb5_error_code ret;
- ret = der_copy_octet_string(&ntlm->initReply.challange, challange);
+ ret = der_copy_octet_string(&ntlm->initReply.challenge, challenge);
if (ret)
krb5_clear_error_message(context);
@@ -966,10 +922,8 @@ krb5_ntlm_init_get_targetname(krb5_context context,
char **name)
{
*name = strdup(ntlm->initReply.targetname);
- if (*name == NULL) {
- krb5_clear_error_message(context);
- return ENOMEM;
- }
+ if (*name == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -1060,10 +1014,8 @@ krb5_ntlm_req_set_username(krb5_context context,
const char *username)
{
ntlm->request.username = strdup(username);
- if (ntlm->request.username == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ntlm->request.username == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -1073,10 +1025,8 @@ krb5_ntlm_req_set_targetname(krb5_context context,
const char *targetname)
{
ntlm->request.targetname = strdup(targetname);
- if (ntlm->request.targetname == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ntlm->request.targetname == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -1086,10 +1036,8 @@ krb5_ntlm_req_set_lm(krb5_context context,
void *hash, size_t len)
{
ntlm->request.lm.data = malloc(len);
- if (ntlm->request.lm.data == NULL && len != 0) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ntlm->request.lm.data == NULL && len != 0)
+ return krb5_enomem(context);
ntlm->request.lm.length = len;
memcpy(ntlm->request.lm.data, hash, len);
return 0;
@@ -1101,10 +1049,8 @@ krb5_ntlm_req_set_ntlm(krb5_context context,
void *hash, size_t len)
{
ntlm->request.ntlm.data = malloc(len);
- if (ntlm->request.ntlm.data == NULL && len != 0) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ntlm->request.ntlm.data == NULL && len != 0)
+ return krb5_enomem(context);
ntlm->request.ntlm.length = len;
memcpy(ntlm->request.ntlm.data, hash, len);
return 0;
@@ -1116,10 +1062,8 @@ krb5_ntlm_req_set_opaque(krb5_context context,
krb5_data *opaque)
{
ntlm->request.opaque.data = malloc(opaque->length);
- if (ntlm->request.opaque.data == NULL && opaque->length != 0) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ntlm->request.opaque.data == NULL && opaque->length != 0)
+ return krb5_enomem(context);
ntlm->request.opaque.length = opaque->length;
memcpy(ntlm->request.opaque.data, opaque->data, opaque->length);
return 0;
@@ -1131,15 +1075,11 @@ krb5_ntlm_req_set_session(krb5_context context,
void *sessionkey, size_t length)
{
ntlm->request.sessionkey = calloc(1, sizeof(*ntlm->request.sessionkey));
- if (ntlm->request.sessionkey == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ntlm->request.sessionkey == NULL)
+ return krb5_enomem(context);
ntlm->request.sessionkey->data = malloc(length);
- if (ntlm->request.sessionkey->data == NULL && length != 0) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ntlm->request.sessionkey->data == NULL && length != 0)
+ return krb5_enomem(context);
memcpy(ntlm->request.sessionkey->data, sessionkey, length);
ntlm->request.sessionkey->length = length;
return 0;
diff --git a/lib/krb5/dll.c b/lib/krb5/dll.c
new file mode 100644
index 000000000000..80d9fa444030
--- /dev/null
+++ b/lib/krb5/dll.c
@@ -0,0 +1,76 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include<windows.h>
+
+extern void heim_w32_service_thread_detach(void *);
+
+HINSTANCE _krb5_hInstance = NULL;
+
+#if NTDDI_VERSION >= NTDDI_VISTA
+extern BOOL WINAPI
+_hc_w32crypto_DllMain(HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved);
+#endif
+
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+ DWORD fdwReason,
+ LPVOID lpvReserved)
+{
+#if NTDDI_VERSION >= NTDDI_VISTA
+ BOOL ret;
+
+ ret = _hc_w32crypto_DllMain(hinstDLL, fdwReason, lpvReserved);
+ if (!ret)
+ return ret;
+#endif
+
+ switch (fdwReason) {
+ case DLL_PROCESS_ATTACH:
+
+ _krb5_hInstance = hinstDLL;
+ return TRUE;
+
+ case DLL_PROCESS_DETACH:
+ return FALSE;
+
+ case DLL_THREAD_ATTACH:
+ return FALSE;
+
+ case DLL_THREAD_DETACH:
+ heim_w32_service_thread_detach(NULL);
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
diff --git a/lib/krb5/doxygen.c b/lib/krb5/doxygen.c
index fec9a671930a..d3ee52fe9bcc 100644
--- a/lib/krb5/doxygen.c
+++ b/lib/krb5/doxygen.c
@@ -308,8 +308,8 @@
*
* In this case, mutual authentication will be tried. That means that the server
* will authenticate to the client. Using mutual authentication
- * is good since it enables the user to verify that they are talking to the
- * right server (a server that knows the key).
+ * is required to avoid man-in-the-middle attacks, since it enables the user to
+ * verify that they are talking to the right server (a server that knows the key).
*
* If you are using a non-blocking socket you will need to do all work of
* krb5_sendauth() yourself. Basically you need to send over the
diff --git a/lib/hcrypto/dllmain.c b/lib/krb5/enomem.c
index b241c7380bf4..0e67fa8794c2 100644
--- a/lib/hcrypto/dllmain.c
+++ b/lib/krb5/enomem.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995, 1996, 1997 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -31,19 +31,12 @@
* SUCH DAMAGE.
*/
-#include <config.h>
+#include "krb5_locl.h"
-#include <Windows.h>
-
-BOOL WINAPI
-DllMain (HANDLE hInst,
- ULONG reason,
- LPVOID lpReserved)
+krb5_error_code
+krb5_enomem(krb5_context context)
{
- switch(reason) {
- case DLL_PROCESS_ATTACH:
- case DLL_PROCESS_DETACH:
- default:
- return TRUE;
- }
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
}
+
diff --git a/lib/krb5/error_string.c b/lib/krb5/error_string.c
index 1bfbad0bfb05..fa181733d1e9 100644
--- a/lib/krb5/error_string.c
+++ b/lib/krb5/error_string.c
@@ -47,12 +47,12 @@
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_clear_error_message(krb5_context context)
{
- HEIMDAL_MUTEX_lock(context->mutex);
+ HEIMDAL_MUTEX_lock(&context->mutex);
if (context->error_string)
free(context->error_string);
context->error_code = 0;
context->error_string = NULL;
- HEIMDAL_MUTEX_unlock(context->mutex);
+ HEIMDAL_MUTEX_unlock(&context->mutex);
}
/**
@@ -72,7 +72,7 @@ krb5_clear_error_message(krb5_context context)
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_set_error_message(krb5_context context, krb5_error_code ret,
const char *fmt, ...)
- __attribute__ ((format (printf, 3, 4)))
+ __attribute__ ((__format__ (__printf__, 3, 4)))
{
va_list ap;
@@ -98,14 +98,14 @@ krb5_set_error_message(krb5_context context, krb5_error_code ret,
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_vset_error_message (krb5_context context, krb5_error_code ret,
const char *fmt, va_list args)
- __attribute__ ((format (printf, 3, 0)))
+ __attribute__ ((__format__ (__printf__, 3, 0)))
{
int r;
if (context == NULL)
return;
- HEIMDAL_MUTEX_lock(context->mutex);
+ HEIMDAL_MUTEX_lock(&context->mutex);
if (context->error_string) {
free(context->error_string);
context->error_string = NULL;
@@ -114,7 +114,9 @@ krb5_vset_error_message (krb5_context context, krb5_error_code ret,
r = vasprintf(&context->error_string, fmt, args);
if (r < 0)
context->error_string = NULL;
- HEIMDAL_MUTEX_unlock(context->mutex);
+ HEIMDAL_MUTEX_unlock(&context->mutex);
+ if (context->error_string)
+ _krb5_debug(context, 100, "error message: %s: %d", context->error_string, ret);
}
/**
@@ -134,7 +136,7 @@ krb5_vset_error_message (krb5_context context, krb5_error_code ret,
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_prepend_error_message(krb5_context context, krb5_error_code ret,
const char *fmt, ...)
- __attribute__ ((format (printf, 3, 4)))
+ __attribute__ ((__format__ (__printf__, 3, 4)))
{
va_list ap;
@@ -159,20 +161,20 @@ krb5_prepend_error_message(krb5_context context, krb5_error_code ret,
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_vprepend_error_message(krb5_context context, krb5_error_code ret,
const char *fmt, va_list args)
- __attribute__ ((format (printf, 3, 0)))
+ __attribute__ ((__format__ (__printf__, 3, 0)))
{
char *str = NULL, *str2 = NULL;
if (context == NULL)
return;
- HEIMDAL_MUTEX_lock(context->mutex);
+ HEIMDAL_MUTEX_lock(&context->mutex);
if (context->error_code != ret) {
- HEIMDAL_MUTEX_unlock(context->mutex);
+ HEIMDAL_MUTEX_unlock(&context->mutex);
return;
}
if (vasprintf(&str, fmt, args) < 0 || str == NULL) {
- HEIMDAL_MUTEX_unlock(context->mutex);
+ HEIMDAL_MUTEX_unlock(&context->mutex);
return;
}
if (context->error_string) {
@@ -187,42 +189,7 @@ krb5_vprepend_error_message(krb5_context context, krb5_error_code ret,
free(str);
} else
context->error_string = str;
- HEIMDAL_MUTEX_unlock(context->mutex);
-}
-
-
-/**
- * Return the error message in context. On error or no error string,
- * the function returns NULL.
- *
- * @param context Kerberos 5 context
- *
- * @return an error string, needs to be freed with
- * krb5_free_error_message(). The functions return NULL on error.
- *
- * @ingroup krb5_error
- */
-
-KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
-krb5_get_error_string(krb5_context context)
-{
- char *ret = NULL;
-
- HEIMDAL_MUTEX_lock(context->mutex);
- if (context->error_string)
- ret = strdup(context->error_string);
- HEIMDAL_MUTEX_unlock(context->mutex);
- return ret;
-}
-
-KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
-krb5_have_error_string(krb5_context context)
-{
- char *str;
- HEIMDAL_MUTEX_lock(context->mutex);
- str = context->error_string;
- HEIMDAL_MUTEX_unlock(context->mutex);
- return str != NULL;
+ HEIMDAL_MUTEX_unlock(&context->mutex);
}
/**
@@ -259,13 +226,13 @@ krb5_get_error_message(krb5_context context, krb5_error_code code)
*/
if (context)
{
- HEIMDAL_MUTEX_lock(context->mutex);
+ HEIMDAL_MUTEX_lock(&context->mutex);
if (context->error_string &&
(code == context->error_code || context->error_code == 0))
{
str = strdup(context->error_string);
}
- HEIMDAL_MUTEX_unlock(context->mutex);
+ HEIMDAL_MUTEX_unlock(&context->mutex);
if (str)
return str;
@@ -329,7 +296,7 @@ krb5_free_error_message(krb5_context context, const char *msg)
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_get_err_text(krb5_context context, krb5_error_code code)
- KRB5_DEPRECATED_FUNCTION("Use X instead")
+ KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead")
{
const char *p = NULL;
if(context != NULL)
@@ -340,3 +307,4 @@ krb5_get_err_text(krb5_context context, krb5_error_code code)
p = "Unknown error";
return p;
}
+
diff --git a/lib/krb5/expand_hostname.c b/lib/krb5/expand_hostname.c
index 7b638d5f0176..5023d16773e7 100644
--- a/lib/krb5/expand_hostname.c
+++ b/lib/krb5/expand_hostname.c
@@ -39,11 +39,8 @@ copy_hostname(krb5_context context,
char **new_hostname)
{
*new_hostname = strdup (orig_hostname);
- if (*new_hostname == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*new_hostname == NULL)
+ return krb5_enomem(context);
strlwr (*new_hostname);
return 0;
}
@@ -84,13 +81,10 @@ krb5_expand_hostname (krb5_context context,
if (a->ai_canonname != NULL) {
*new_hostname = strdup (a->ai_canonname);
freeaddrinfo (ai);
- if (*new_hostname == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- } else {
+ if (*new_hostname == NULL)
+ return krb5_enomem(context);
+ else
return 0;
- }
}
}
freeaddrinfo (ai);
diff --git a/lib/krb5/expand_path.c b/lib/krb5/expand_path.c
index 4c4898a79ea1..43b8dfeb3a91 100644
--- a/lib/krb5/expand_path.c
+++ b/lib/krb5/expand_path.c
@@ -32,6 +32,8 @@
#include "krb5_locl.h"
+#include <stdarg.h>
+
typedef int PTYPE;
#ifdef _WIN32
@@ -49,7 +51,7 @@ typedef int PTYPE;
* temporary directory until the user profile is loaded. In addition,
* the returned path may or may not exist.
*/
-static int
+static krb5_error_code
_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
TCHAR tpath[MAX_PATH];
@@ -70,11 +72,8 @@ _expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char
*ret = strdup(tpath);
- if (*ret == NULL) {
- if (context)
- krb5_set_error_message(context, ENOMEM, "strdup - Out of memory");
- return ENOMEM;
- }
+ if (*ret == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -89,7 +88,7 @@ extern HINSTANCE _krb5_hInstance;
* "bin" directory is considered to be the directory in which the
* krb5.dll is located.
*/
-static int
+static krb5_error_code
_expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
TCHAR path[MAX_PATH];
@@ -119,7 +118,7 @@ _expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **r
*ret = strdup(path);
if (*ret == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
return 0;
}
@@ -139,7 +138,7 @@ _expand_bin_dir(krb5_context context, PTYPE param, const char *postfix, char **r
* SecurityIdentification level the call will fail.
*
*/
-static int
+static krb5_error_code
_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
int rv = EINVAL;
@@ -237,7 +236,7 @@ _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **re
* Expand a folder identified by a CSIDL
*/
-static int
+static krb5_error_code
_expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **ret)
{
TCHAR path[MAX_PATH];
@@ -255,54 +254,49 @@ _expand_csidl(krb5_context context, PTYPE folder, const char *postfix, char **re
path[len - 1] = '\0';
if (postfix &&
- strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0])) {
- return ENOMEM;
- }
+ strlcat(path, postfix, sizeof(path)/sizeof(path[0])) >= sizeof(path)/sizeof(path[0]))
+ return krb5_enomem(context);
*ret = strdup(path);
- if (*ret == NULL) {
- if (context)
- krb5_set_error_message(context, ENOMEM, "Out of memory");
- return ENOMEM;
- }
+ if (*ret == NULL)
+ return krb5_enomem(context);
return 0;
}
#else
-static int
+static krb5_error_code
_expand_path(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
*ret = strdup(postfix);
- if (*ret == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
- return ENOMEM;
- }
+ if (*ret == NULL)
+ return krb5_enomem(context);
return 0;
}
-static int
+static krb5_error_code
_expand_temp_folder(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
const char *p = NULL;
- if (issuid())
+ if (!issuid())
p = getenv("TEMP");
+
if (p)
*ret = strdup(p);
else
*ret = strdup("/tmp");
if (*ret == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
return 0;
}
-static int
+static krb5_error_code
_expand_userid(krb5_context context, PTYPE param, const char *postfix, char **str)
{
int ret = asprintf(str, "%ld", (unsigned long)getuid());
if (ret < 0 || *str == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
return 0;
}
@@ -310,25 +304,35 @@ _expand_userid(krb5_context context, PTYPE param, const char *postfix, char **st
#endif /* _WIN32 */
/**
+ * Expand an extra token
+ */
+
+static krb5_error_code
+_expand_extra_token(krb5_context context, const char *value, char **ret)
+{
+ *ret = strdup(value);
+ if (*ret == NULL)
+ return krb5_enomem(context);
+ return 0;
+}
+
+/**
* Expand a %{null} token
*
* The expansion of a %{null} token is always the empty string.
*/
-static int
+static krb5_error_code
_expand_null(krb5_context context, PTYPE param, const char *postfix, char **ret)
{
*ret = strdup("");
- if (*ret == NULL) {
- if (context)
- krb5_set_error_message(context, ENOMEM, "Out of memory");
- return ENOMEM;
- }
+ if (*ret == NULL)
+ return krb5_enomem(context);
return 0;
}
-static const struct token {
+static const struct {
const char * tok;
int ftype;
#define FTYPE_CSIDL 0
@@ -370,13 +374,15 @@ static const struct token {
{"null", SPECIAL(_expand_null)}
};
-static int
+static krb5_error_code
_expand_token(krb5_context context,
const char *token,
const char *token_end,
+ char **extra_tokens,
char **ret)
{
size_t i;
+ char **p;
*ret = NULL;
@@ -387,6 +393,11 @@ _expand_token(krb5_context context,
return EINVAL;
}
+ for (p = extra_tokens; p && p[0]; p += 2) {
+ if (strncmp(token+2, p[0], (token_end - token) - 2) == 0)
+ return _expand_extra_token(context, p[1], ret);
+ }
+
for (i = 0; i < sizeof(tokens)/sizeof(tokens[0]); i++) {
if (!strncmp(token+2, tokens[i].tok, (token_end - token) - 2))
return tokens[i].exp_func(context, tokens[i].param,
@@ -398,14 +409,65 @@ _expand_token(krb5_context context,
return EINVAL;
}
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @path_in The path to expand tokens from
+ *
+ * Outputs:
+ *
+ * @ppath_out Path with expanded tokens (caller must free() this)
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_expand_path_tokens(krb5_context context,
const char *path_in,
+ int filepath,
char **ppath_out)
{
+ return _krb5_expand_path_tokensv(context, path_in, filepath, ppath_out, NULL);
+}
+
+static void
+free_extra_tokens(char **extra_tokens)
+{
+ char **p;
+
+ for (p = extra_tokens; p && *p; p++)
+ free(*p);
+ free(extra_tokens);
+}
+
+/**
+ * Internal function to expand tokens in paths.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @path_in The path to expand tokens from
+ * @ppath_out The expanded path
+ * @... Variable number of pairs of strings, the first of each
+ * being a token (e.g., "luser") and the second a string to
+ * replace it with. The list is terminated by a NULL.
+ *
+ * Outputs:
+ *
+ * @ppath_out Path with expanded tokens (caller must free() this)
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_expand_path_tokensv(krb5_context context,
+ const char *path_in,
+ int filepath,
+ char **ppath_out, ...)
+{
char *tok_begin, *tok_end, *append;
+ char **extra_tokens = NULL;
const char *path_left;
+ size_t nargs = 0;
size_t len = 0;
+ va_list ap;
if (path_in == NULL || *path_in == '\0') {
*ppath_out = strdup("");
@@ -414,6 +476,45 @@ _krb5_expand_path_tokens(krb5_context context,
*ppath_out = NULL;
+ va_start(ap, ppath_out);
+ while (va_arg(ap, const char *)) {
+ nargs++;
+ va_arg(ap, const char *);
+ }
+ va_end(ap);
+ nargs *= 2;
+
+ /* Get extra tokens */
+ if (nargs) {
+ size_t i;
+
+ extra_tokens = calloc(nargs + 1, sizeof (*extra_tokens));
+ if (extra_tokens == NULL)
+ return krb5_enomem(context);
+ va_start(ap, ppath_out);
+ for (i = 0; i < nargs; i++) {
+ const char *s = va_arg(ap, const char *); /* token key */
+ if (s == NULL)
+ break;
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i++] == NULL) {
+ va_end(ap);
+ free_extra_tokens(extra_tokens);
+ return krb5_enomem(context);
+ }
+ s = va_arg(ap, const char *); /* token value */
+ if (s == NULL)
+ s = "";
+ extra_tokens[i] = strdup(s);
+ if (extra_tokens[i] == NULL) {
+ va_end(ap);
+ free_extra_tokens(extra_tokens);
+ return krb5_enomem(context);
+ }
+ }
+ va_end(ap);
+ }
+
for (path_left = path_in; path_left && *path_left; ) {
tok_begin = strstr(path_left, "%{");
@@ -431,6 +532,7 @@ _krb5_expand_path_tokens(krb5_context context,
tok_end = strchr(tok_begin, '}');
if (tok_end == NULL) {
+ free_extra_tokens(extra_tokens);
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
@@ -439,7 +541,9 @@ _krb5_expand_path_tokens(krb5_context context,
return EINVAL;
}
- if (_expand_token(context, tok_begin, tok_end, &append)) {
+ if (_expand_token(context, tok_begin, tok_end, extra_tokens,
+ &append)) {
+ free_extra_tokens(extra_tokens);
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
@@ -456,12 +560,11 @@ _krb5_expand_path_tokens(krb5_context context,
if (append == NULL) {
+ free_extra_tokens(extra_tokens);
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
- if (context)
- krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
- return ENOMEM;
+ return krb5_enomem(context);
}
@@ -470,13 +573,12 @@ _krb5_expand_path_tokens(krb5_context context,
char * new_str = realloc(*ppath_out, len + append_len + 1);
if (new_str == NULL) {
+ free_extra_tokens(extra_tokens);
free(append);
if (*ppath_out)
free(*ppath_out);
*ppath_out = NULL;
- if (context)
- krb5_set_error_message(context, ENOMEM, "malloc - out of memory");
- return ENOMEM;
+ return krb5_enomem(context);
}
*ppath_out = new_str;
@@ -488,13 +590,15 @@ _krb5_expand_path_tokens(krb5_context context,
#ifdef _WIN32
/* Also deal with slashes */
- if (*ppath_out) {
+ if (filepath && *ppath_out) {
char * c;
+
for (c = *ppath_out; *c; c++)
if (*c == '/')
*c = '\\';
}
#endif
+ free_extra_tokens(extra_tokens);
return 0;
}
diff --git a/lib/krb5/fast.c b/lib/krb5/fast.c
new file mode 100644
index 000000000000..c30d5442f73a
--- /dev/null
+++ b/lib/krb5/fast.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_fast_cf2(krb5_context context,
+ krb5_keyblock *key1,
+ const char *pepper1,
+ krb5_keyblock *key2,
+ const char *pepper2,
+ krb5_keyblock *armorkey,
+ krb5_crypto *armor_crypto)
+{
+ krb5_crypto crypto1, crypto2;
+ krb5_data pa1, pa2;
+ krb5_error_code ret;
+
+ ret = krb5_crypto_init(context, key1, 0, &crypto1);
+ if (ret)
+ return ret;
+
+ ret = krb5_crypto_init(context, key2, 0, &crypto2);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto1);
+ return ret;
+ }
+
+ pa1.data = rk_UNCONST(pepper1);
+ pa1.length = strlen(pepper1);
+ pa2.data = rk_UNCONST(pepper2);
+ pa2.length = strlen(pepper2);
+
+ ret = krb5_crypto_fx_cf2(context, crypto1, crypto2, &pa1, &pa2,
+ key1->keytype, armorkey);
+ krb5_crypto_destroy(context, crypto1);
+ krb5_crypto_destroy(context, crypto2);
+ if (ret)
+ return ret;
+
+ if (armor_crypto) {
+ ret = krb5_crypto_init(context, armorkey, 0, armor_crypto);
+ if (ret)
+ krb5_free_keyblock_contents(context, armorkey);
+ }
+
+ return ret;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_fast_armor_key(krb5_context context,
+ krb5_keyblock *subkey,
+ krb5_keyblock *sessionkey,
+ krb5_keyblock *armorkey,
+ krb5_crypto *armor_crypto)
+{
+ return _krb5_fast_cf2(context,
+ subkey,
+ "subkeyarmor",
+ sessionkey,
+ "ticketarmor",
+ armorkey,
+ armor_crypto);
+}
diff --git a/lib/krb5/fcache.c b/lib/krb5/fcache.c
index 731f2934146b..ab5d1c137f74 100644
--- a/lib/krb5/fcache.c
+++ b/lib/krb5/fcache.c
@@ -42,6 +42,8 @@ typedef struct krb5_fcache{
struct fcc_cursor {
int fd;
+ off_t cred_start;
+ off_t cred_end;
krb5_storage *sp;
};
@@ -68,7 +70,7 @@ fcc_get_name(krb5_context context,
return FILENAME(id);
}
-int
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
const char *filename)
{
@@ -112,7 +114,7 @@ _krb5_xlock(krb5_context context, int fd, krb5_boolean exclusive,
return ret;
}
-int
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_xunlock(krb5_context context, int fd)
{
int ret;
@@ -223,7 +225,11 @@ scrub_file (int fd)
return errno;
memset(buf, 0, sizeof(buf));
while(pos > 0) {
- ssize_t tmp = write(fd, buf, min((off_t)sizeof(buf), pos));
+ ssize_t tmp;
+ size_t wr = sizeof(buf);
+ if (wr > pos)
+ wr = (size_t)pos;
+ tmp = write(fd, buf, wr);
if (tmp < 0)
return errno;
@@ -244,7 +250,7 @@ scrub_file (int fd)
* hardlink)
*/
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_erase_file(krb5_context context, const char *filename)
{
int fd;
@@ -255,7 +261,7 @@ _krb5_erase_file(krb5_context context, const char *filename)
if (ret < 0)
return errno;
- fd = open(filename, O_RDWR | O_BINARY);
+ fd = open(filename, O_RDWR | O_BINARY | O_CLOEXEC | O_NOFOLLOW);
if(fd < 0) {
if(errno == ENOENT)
return 0;
@@ -269,22 +275,27 @@ _krb5_erase_file(krb5_context context, const char *filename)
return ret;
}
if (unlink(filename) < 0) {
+ ret = errno;
_krb5_xunlock(context, fd);
close (fd);
- return errno;
+ krb5_set_error_message(context, errno,
+ N_("krb5_cc_destroy: unlinking \"%s\": %s", ""),
+ filename, strerror(ret));
+ return ret;
}
- ret = fstat (fd, &sb2);
+ ret = fstat(fd, &sb2);
if (ret < 0) {
+ ret = errno;
_krb5_xunlock(context, fd);
close (fd);
- return errno;
+ return ret;
}
/* check if someone was playing with symlinks */
if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
_krb5_xunlock(context, fd);
- close (fd);
+ close(fd);
return EPERM;
}
@@ -292,18 +303,18 @@ _krb5_erase_file(krb5_context context, const char *filename)
if (sb2.st_nlink != 0) {
_krb5_xunlock(context, fd);
- close (fd);
+ close(fd);
return 0;
}
- ret = scrub_file (fd);
+ ret = scrub_file(fd);
if (ret) {
_krb5_xunlock(context, fd);
close(fd);
return ret;
}
ret = _krb5_xunlock(context, fd);
- close (fd);
+ close(fd);
return ret;
}
@@ -321,27 +332,29 @@ fcc_gen_new(krb5_context context, krb5_ccache *id)
N_("malloc: out of memory", ""));
return KRB5_CC_NOMEM;
}
- ret = asprintf (&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
+ ret = asprintf(&file, "%sXXXXXX", KRB5_DEFAULT_CCFILE_ROOT);
if(ret < 0 || file == NULL) {
free(f);
krb5_set_error_message(context, KRB5_CC_NOMEM,
N_("malloc: out of memory", ""));
return KRB5_CC_NOMEM;
}
- ret = _krb5_expand_path_tokens(context, file, &exp_file);
+ ret = _krb5_expand_path_tokens(context, file, 1, &exp_file);
free(file);
- if (ret)
+ if (ret) {
+ free(f);
return ret;
+ }
file = exp_file;
fd = mkstemp(exp_file);
if(fd < 0) {
- int xret = errno;
- krb5_set_error_message(context, xret, N_("mkstemp %s failed", ""), exp_file);
+ ret = (krb5_error_code)errno;
+ krb5_set_error_message(context, ret, N_("mkstemp %s failed", ""), exp_file);
free(f);
free(exp_file);
- return xret;
+ return ret;
}
close(fd);
f->filename = exp_file;
@@ -379,6 +392,7 @@ storage_set_flags(krb5_context context, krb5_storage *sp, int vno)
static krb5_error_code KRB5_CALLCONV
fcc_open(krb5_context context,
krb5_ccache id,
+ const char *operation,
int *fd_ret,
int flags,
mode_t mode)
@@ -387,24 +401,132 @@ fcc_open(krb5_context context,
(flags | O_RDWR) == flags);
krb5_error_code ret;
const char *filename;
+ struct stat sb1, sb2;
+#ifndef _WIN32
+ struct stat sb3;
+ size_t tries = 3;
+#endif
+ int strict_checking;
int fd;
+ flags |= O_BINARY | O_CLOEXEC | O_NOFOLLOW;
+
+ *fd_ret = -1;
+
if (FCACHE(id) == NULL)
return krb5_einval(context, 2);
filename = FILENAME(id);
+ strict_checking = (flags & O_CREAT) == 0 &&
+ (context->flags & KRB5_CTX_F_FCACHE_STRICT_CHECKING) != 0;
+
+again:
+ memset(&sb1, 0, sizeof(sb1));
+ ret = lstat(filename, &sb1);
+ if (ret == 0) {
+ if (!S_ISREG(sb1.st_mode)) {
+ krb5_set_error_message(context, EPERM,
+ N_("Refuses to open symlinks for caches FILE:%s", ""), filename);
+ return EPERM;
+ }
+ } else if (errno != ENOENT || !(flags & O_CREAT)) {
+ krb5_set_error_message(context, errno, N_("%s lstat(%s)", "file, error"),
+ operation, filename);
+ return errno;
+ }
+
fd = open(filename, flags, mode);
if(fd < 0) {
char buf[128];
ret = errno;
rk_strerror_r(ret, buf, sizeof(buf));
- krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"),
- filename, buf);
+ krb5_set_error_message(context, ret, N_("%s open(%s): %s", "file, error"),
+ operation, filename, buf);
return ret;
}
rk_cloexec(fd);
+ ret = fstat(fd, &sb2);
+ if (ret < 0) {
+ krb5_clear_error_message(context);
+ close(fd);
+ return errno;
+ }
+
+ if (!S_ISREG(sb2.st_mode)) {
+ krb5_set_error_message(context, EPERM, N_("Refuses to open non files caches: FILE:%s", ""), filename);
+ close(fd);
+ return EPERM;
+ }
+
+#ifndef _WIN32
+ if (sb1.st_dev && sb1.st_ino &&
+ (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino)) {
+ /*
+ * Perhaps we raced with a rename(). To complain about
+ * symlinks in that case would cause unnecessary concern, so
+ * we check for that possibility and loop. This has no
+ * TOCTOU problems because we redo the open(). We could also
+ * not do any of this checking if O_NOFOLLOW != 0...
+ */
+ close(fd);
+ ret = lstat(filename, &sb3);
+ if (ret || sb1.st_dev != sb2.st_dev ||
+ sb3.st_dev != sb2.st_dev || sb3.st_ino != sb2.st_ino) {
+ krb5_set_error_message(context, EPERM, N_("Refuses to open possible symlink for caches: FILE:%s", ""), filename);
+ return EPERM;
+ }
+ if (--tries == 0) {
+ krb5_set_error_message(context, EPERM, N_("Raced too many times with renames of FILE:%s", ""), filename);
+ return EPERM;
+ }
+ goto again;
+ }
+#endif
+
+ /*
+ * /tmp (or wherever default ccaches go) might not be on its own
+ * filesystem, or on a filesystem different /etc, say, and even if
+ * it were, suppose a user hard-links another's ccache to her
+ * default ccache, then runs a set-uid program that will user her
+ * default ccache (even if it ignores KRB5CCNAME)...
+ *
+ * Default ccache locations should really be on per-user non-tmp
+ * locations on tmpfs "run" directories. But we don't know here
+ * that this is the case. Thus: no hard-links, no symlinks.
+ */
+ if (sb2.st_nlink != 1) {
+ krb5_set_error_message(context, EPERM, N_("Refuses to open hardlinks for caches FILE:%s", ""), filename);
+ close(fd);
+ return EPERM;
+ }
+
+ if (strict_checking) {
+#ifndef _WIN32
+ /*
+ * XXX WIN32: Needs to have ACL checking code!
+ * st_mode comes out as 100666, and st_uid is no use.
+ */
+ /*
+ * XXX Should probably add options to improve control over this
+ * check. We might want strict checking of everything except
+ * this.
+ */
+ if (sb2.st_uid != geteuid()) {
+ krb5_set_error_message(context, EPERM, N_("Refuses to open cache files not own by myself FILE:%s (owned by %d)", ""), filename, (int)sb2.st_uid);
+ close(fd);
+ return EPERM;
+ }
+ if ((sb2.st_mode & 077) != 0) {
+ krb5_set_error_message(context, EPERM,
+ N_("Refuses to open group/other readable files FILE:%s", ""), filename);
+ close(fd);
+ return EPERM;
+ }
+#endif
+ }
+
if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
close(fd);
return ret;
@@ -427,7 +549,7 @@ fcc_initialize(krb5_context context,
unlink (f->filename);
- ret = fcc_open(context, id, &fd, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ ret = fcc_open(context, id, "initialize", &fd, O_RDWR | O_CREAT | O_EXCL, 0600);
if(ret)
return ret;
{
@@ -465,8 +587,8 @@ fcc_initialize(krb5_context context,
char buf[128];
ret = errno;
rk_strerror_r(ret, buf, sizeof(buf));
- krb5_set_error_message (context, ret, N_("close %s: %s", ""),
- FILENAME(id), buf);
+ krb5_set_error_message(context, ret, N_("close %s: %s", ""),
+ FILENAME(id), buf);
}
return ret;
}
@@ -490,8 +612,7 @@ fcc_destroy(krb5_context context,
if (FCACHE(id) == NULL)
return krb5_einval(context, 2);
- _krb5_erase_file(context, FILENAME(id));
- return 0;
+ return _krb5_erase_file(context, FILENAME(id));
}
static krb5_error_code KRB5_CALLCONV
@@ -502,7 +623,7 @@ fcc_store_cred(krb5_context context,
int ret;
int fd;
- ret = fcc_open(context, id, &fd, O_WRONLY | O_APPEND | O_BINARY | O_CLOEXEC, 0);
+ ret = fcc_open(context, id, "store", &fd, O_WRONLY | O_APPEND, 0);
if(ret)
return ret;
{
@@ -511,11 +632,6 @@ fcc_store_cred(krb5_context context,
sp = krb5_storage_emem();
krb5_storage_set_eof_code(sp, KRB5_CC_END);
storage_set_flags(context, sp, FCACHE(id)->version);
- if (!krb5_config_get_bool_default(context, NULL, TRUE,
- "libdefaults",
- "fcc-mit-ticketflags",
- NULL))
- krb5_storage_set_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER);
ret = krb5_store_creds(sp, creds);
if (ret == 0)
ret = write_storage(context, sp, fd);
@@ -525,31 +641,34 @@ fcc_store_cred(krb5_context context,
if (close(fd) < 0) {
if (ret == 0) {
char buf[128];
- rk_strerror_r(ret, buf, sizeof(buf));
ret = errno;
- krb5_set_error_message (context, ret, N_("close %s: %s", ""),
- FILENAME(id), buf);
+ rk_strerror_r(ret, buf, sizeof(buf));
+ krb5_set_error_message(context, ret, N_("close %s: %s", ""),
+ FILENAME(id), buf);
}
}
return ret;
}
static krb5_error_code
-init_fcc (krb5_context context,
- krb5_ccache id,
- krb5_storage **ret_sp,
- int *ret_fd,
- krb5_deltat *kdc_offset)
+init_fcc(krb5_context context,
+ krb5_ccache id,
+ const char *operation,
+ krb5_storage **ret_sp,
+ int *ret_fd,
+ krb5_deltat *kdc_offset)
{
int fd;
int8_t pvno, tag;
krb5_storage *sp;
krb5_error_code ret;
+ *ret_fd = -1;
+ *ret_sp = NULL;
if (kdc_offset)
*kdc_offset = 0;
- ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ ret = fcc_open(context, id, operation, &fd, O_RDONLY, 0);
if(ret)
return ret;
@@ -561,7 +680,7 @@ init_fcc (krb5_context context,
}
krb5_storage_set_eof_code(sp, KRB5_CC_END);
ret = krb5_ret_int8(sp, &pvno);
- if(ret != 0) {
+ if (ret != 0) {
if(ret == KRB5_CC_END) {
ret = ENOENT;
krb5_set_error_message(context, ret,
@@ -573,7 +692,7 @@ init_fcc (krb5_context context,
FILENAME(id));
goto out;
}
- if(pvno != 5) {
+ if (pvno != 5) {
ret = KRB5_CCACHE_BADVNO;
krb5_set_error_message(context, ret, N_("Bad version number in credential "
"cache file: %s", ""),
@@ -581,7 +700,7 @@ init_fcc (krb5_context context,
goto out;
}
ret = krb5_ret_int8(sp, &tag); /* should not be host byte order */
- if(ret != 0) {
+ if (ret != 0) {
ret = KRB5_CC_FORMAT;
krb5_set_error_message(context, ret, "Error reading tag in "
"cache file: %s", FILENAME(id));
@@ -693,7 +812,7 @@ fcc_get_principal(krb5_context context,
int fd;
krb5_storage *sp;
- ret = init_fcc (context, id, &sp, &fd, NULL);
+ ret = init_fcc (context, id, "get-principal", &sp, &fd, NULL);
if (ret)
return ret;
ret = krb5_ret_principal(sp, principal);
@@ -728,8 +847,8 @@ fcc_get_first (krb5_context context,
}
memset(*cursor, 0, sizeof(struct fcc_cursor));
- ret = init_fcc (context, id, &FCC_CURSOR(*cursor)->sp,
- &FCC_CURSOR(*cursor)->fd, NULL);
+ ret = init_fcc(context, id, "get-frist", &FCC_CURSOR(*cursor)->sp,
+ &FCC_CURSOR(*cursor)->fd, NULL);
if (ret) {
free(*cursor);
*cursor = NULL;
@@ -762,11 +881,16 @@ fcc_get_next (krb5_context context,
if((ret = fcc_lock(context, id, FCC_CURSOR(*cursor)->fd, FALSE)) != 0)
return ret;
+ FCC_CURSOR(*cursor)->cred_start = lseek(FCC_CURSOR(*cursor)->fd,
+ 0, SEEK_CUR);
ret = krb5_ret_creds(FCC_CURSOR(*cursor)->sp, creds);
if (ret)
krb5_clear_error_message(context);
+ FCC_CURSOR(*cursor)->cred_end = lseek(FCC_CURSOR(*cursor)->fd,
+ 0, SEEK_CUR);
+
fcc_unlock(context, FCC_CURSOR(*cursor)->fd);
return ret;
}
@@ -790,72 +914,158 @@ fcc_end_get (krb5_context context,
return 0;
}
-static krb5_error_code KRB5_CALLCONV
-fcc_remove_cred(krb5_context context,
- krb5_ccache id,
- krb5_flags which,
- krb5_creds *cred)
+static void KRB5_CALLCONV
+cred_delete(krb5_context context,
+ krb5_ccache id,
+ krb5_cc_cursor *cursor,
+ krb5_creds *cred)
{
krb5_error_code ret;
- krb5_ccache copy, newfile;
- char *newname = NULL;
- int fd;
+ krb5_storage *sp;
+ krb5_data orig_cred_data;
+ unsigned char *cred_data_in_file = NULL;
+ off_t new_cred_sz;
+ struct stat sb1, sb2;
+ int fd = -1;
+ ssize_t bytes;
+ krb5_const_realm srealm = krb5_principal_get_realm(context, cred->server);
- if (FCACHE(id) == NULL)
- return krb5_einval(context, 2);
+ /* This is best-effort code; if we lose track of errors here it's OK */
+
+ heim_assert(FCC_CURSOR(*cursor)->cred_start < FCC_CURSOR(*cursor)->cred_end,
+ "fcache internal error");
+
+ krb5_data_zero(&orig_cred_data);
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return;
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ storage_set_flags(context, sp, FCACHE(id)->version);
- ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &copy);
+ /* Get a copy of what the cred should look like in the file; see below */
+ ret = krb5_store_creds(sp, cred);
if (ret)
- return ret;
+ goto out;
- ret = krb5_cc_copy_cache(context, id, copy);
- if (ret) {
- krb5_cc_destroy(context, copy);
- return ret;
- }
+ ret = krb5_storage_to_data(sp, &orig_cred_data);
+ if (ret)
+ goto out;
+ krb5_storage_free(sp);
- ret = krb5_cc_remove_cred(context, copy, which, cred);
- if (ret) {
- krb5_cc_destroy(context, copy);
- return ret;
- }
+ cred_data_in_file = malloc(orig_cred_data.length);
+ if (cred_data_in_file == NULL)
+ goto out;
- ret = asprintf(&newname, "FILE:%s.XXXXXX", FILENAME(id));
- if (ret < 0 || newname == NULL) {
- krb5_cc_destroy(context, copy);
- return ENOMEM;
- }
+ /*
+ * Mark the cred expired; krb5_cc_retrieve_cred() callers should use
+ * KRB5_TC_MATCH_TIMES, so this should be good enough...
+ */
+ cred->times.endtime = 0;
- fd = mkstemp(&newname[5]);
- if (fd < 0) {
- ret = errno;
- krb5_cc_destroy(context, copy);
- return ret;
+ /* ...except for config creds because we don't check their endtimes */
+ if (srealm && strcmp(srealm, "X-CACHECONF:") == 0) {
+ ret = krb5_principal_set_realm(context, cred->server, "X-RMED-CONF:");
+ if (ret)
+ goto out;
}
- close(fd);
- ret = krb5_cc_resolve(context, newname, &newfile);
- if (ret) {
- unlink(&newname[5]);
- free(newname);
- krb5_cc_destroy(context, copy);
- return ret;
- }
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ goto out;
+ krb5_storage_set_eof_code(sp, KRB5_CC_END);
+ storage_set_flags(context, sp, FCACHE(id)->version);
- ret = krb5_cc_copy_cache(context, copy, newfile);
- krb5_cc_destroy(context, copy);
- if (ret) {
- free(newname);
- krb5_cc_destroy(context, newfile);
- return ret;
+ ret = krb5_store_creds(sp, cred);
+
+ /* The new cred must be the same size as the old cred */
+ new_cred_sz = krb5_storage_seek(sp, 0, SEEK_END);
+ if (new_cred_sz != orig_cred_data.length || new_cred_sz !=
+ (FCC_CURSOR(*cursor)->cred_end - FCC_CURSOR(*cursor)->cred_start)) {
+ /* XXX This really can't happen. Assert like above? */
+ krb5_set_error_message(context, EINVAL,
+ N_("Credential deletion failed on ccache "
+ "FILE:%s: new credential size did not "
+ "match old credential size", ""),
+ FILENAME(id));
+ goto out;
}
- ret = rk_rename(&newname[5], FILENAME(id));
+ ret = fcc_open(context, id, "remove_cred", &fd, O_RDWR, 0);
if (ret)
- ret = errno;
- free(newname);
- krb5_cc_close(context, newfile);
+ goto out;
+
+ /*
+ * Check that we're updating the same file where we got the
+ * cred's offset, else we'd be corrupting a new ccache.
+ */
+ if (fstat(FCC_CURSOR(*cursor)->fd, &sb1) == -1 ||
+ fstat(fd, &sb2) == -1)
+ goto out;
+ if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino)
+ goto out;
+
+ /*
+ * Make sure what we overwrite is what we expected.
+ *
+ * FIXME: We *really* need the ccache v4 tag for ccache ID. This
+ * check that we're only overwriting something that looks exactly
+ * like what we want to is probably good enough in practice, but
+ * it's not guaranteed to work.
+ */
+ if (lseek(fd, FCC_CURSOR(*cursor)->cred_start, SEEK_SET) == (off_t)-1)
+ goto out;
+ bytes = read(fd, cred_data_in_file, orig_cred_data.length);
+ if (bytes != orig_cred_data.length)
+ goto out;
+ if (memcmp(orig_cred_data.data, cred_data_in_file, bytes) != 0)
+ goto out;
+ if (lseek(fd, FCC_CURSOR(*cursor)->cred_start, SEEK_SET) == (off_t)-1)
+ goto out;
+ ret = write_storage(context, sp, fd);
+out:
+ if (fd > -1) {
+ fcc_unlock(context, fd);
+ if (close(fd) < 0 && ret == 0) {
+ krb5_set_error_message(context, errno, N_("close %s", ""),
+ FILENAME(id));
+ }
+ }
+ krb5_data_free(&orig_cred_data);
+ free(cred_data_in_file);
+ krb5_storage_free(sp);
+ return;
+}
+
+static krb5_error_code KRB5_CALLCONV
+fcc_remove_cred(krb5_context context,
+ krb5_ccache id,
+ krb5_flags which,
+ krb5_creds *mcred)
+{
+ krb5_error_code ret, ret2;
+ krb5_cc_cursor cursor;
+ krb5_creds found_cred;
+
+ if (FCACHE(id) == NULL)
+ return krb5_einval(context, 2);
+ ret = krb5_cc_start_seq_get(context, id, &cursor);
+ if (ret)
+ return ret;
+ while ((ret = krb5_cc_next_cred(context, id, &cursor, &found_cred)) == 0) {
+ if (!krb5_compare_creds(context, which, mcred, &found_cred)) {
+ krb5_free_cred_contents(context, &found_cred);
+ continue;
+ }
+ cred_delete(context, id, &cursor, &found_cred);
+ krb5_free_cred_contents(context, &found_cred);
+ }
+ ret2 = krb5_cc_end_seq_get(context, id, &cursor);
+ if (ret == 0)
+ return ret2;
+ if (ret == KRB5_CC_END)
+ return 0;
return ret;
}
@@ -904,8 +1114,8 @@ fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
{
struct fcache_iter *iter = cursor;
krb5_error_code ret;
- const char *fn;
- char *expandedfn = NULL;
+ const char *fn, *cc_type;
+ krb5_ccache cc;
if (iter == NULL)
return krb5_einval(context, 2);
@@ -916,30 +1126,26 @@ fcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
}
iter->first = 0;
+ /*
+ * Note: do not allow krb5_cc_default_name() to recurse via
+ * krb5_cc_cache_match().
+ * Note that context->default_cc_name will be NULL even though
+ * KRB5CCNAME is set in the environment if
+ * krb5_cc_set_default_name() hasn't
+ */
fn = krb5_cc_default_name(context);
- if (fn == NULL || strncasecmp(fn, "FILE:", 5) != 0) {
- ret = _krb5_expand_default_cc_name(context,
- KRB5_DEFAULT_CCNAME_FILE,
- &expandedfn);
- if (ret)
- return ret;
- fn = expandedfn;
- }
- /* check if file exists, don't return a non existant "next" */
- if (strncasecmp(fn, "FILE:", 5) == 0) {
- struct stat sb;
- ret = stat(fn + 5, &sb);
- if (ret) {
- ret = KRB5_CC_END;
- goto out;
- }
+ ret = krb5_cc_resolve(context, fn, &cc);
+ if (ret != 0)
+ return ret;
+ cc_type = krb5_cc_get_type(context, cc);
+ if (strcmp(cc_type, "FILE") != 0) {
+ krb5_cc_close(context, cc);
+ return KRB5_CC_END;
}
- ret = krb5_cc_resolve(context, fn, id);
- out:
- if (expandedfn)
- free(expandedfn);
- return ret;
+ *id = cc;
+
+ return 0;
}
static krb5_error_code KRB5_CALLCONV
@@ -976,14 +1182,14 @@ fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
int fd1, fd2;
char buf[BUFSIZ];
- ret = fcc_open(context, from, &fd1, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ ret = fcc_open(context, from, "move/from", &fd1, O_RDONLY, 0);
if(ret)
return ret;
unlink(FILENAME(to));
- ret = fcc_open(context, to, &fd2,
- O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
+ ret = fcc_open(context, to, "move/to", &fd2,
+ O_WRONLY | O_CREAT | O_EXCL, 0600);
if(ret)
goto out1;
@@ -1024,7 +1230,7 @@ fcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
{
krb5_storage *sp;
int fd;
- if ((ret = init_fcc (context, to, &sp, &fd, NULL)) == 0) {
+ if ((ret = init_fcc (context, to, "move", &sp, &fd, NULL)) == 0) {
if (sp)
krb5_storage_free(sp);
fcc_unlock(context, fd);
@@ -1052,7 +1258,7 @@ fcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
struct stat sb;
int fd;
- ret = fcc_open(context, id, &fd, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
+ ret = fcc_open(context, id, "lastchange", &fd, O_RDONLY, 0);
if(ret)
return ret;
ret = fstat(fd, &sb);
@@ -1078,7 +1284,7 @@ fcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset
krb5_error_code ret;
krb5_storage *sp = NULL;
int fd;
- ret = init_fcc(context, id, &sp, &fd, kdc_offset);
+ ret = init_fcc(context, id, "get-kdc-offset", &sp, &fd, kdc_offset);
if (sp)
krb5_storage_free(sp);
fcc_unlock(context, fd);
diff --git a/lib/krb5/generate_subkey.c b/lib/krb5/generate_subkey.c
index e09dc2a91685..07047461ee77 100644
--- a/lib/krb5/generate_subkey.c
+++ b/lib/krb5/generate_subkey.c
@@ -55,12 +55,10 @@ krb5_generate_subkey_extended(krb5_context context,
krb5_error_code ret;
ALLOC(*subkey, 1);
- if (*subkey == NULL) {
- krb5_set_error_message(context, ENOMEM,N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*subkey == NULL)
+ return krb5_enomem(context);
- if (etype == ETYPE_NULL)
+ if (etype == (krb5_enctype)ETYPE_NULL)
etype = key->keytype; /* use session key etype */
/* XXX should we use the session key as input to the RF? */
diff --git a/lib/krb5/get_addrs.c b/lib/krb5/get_addrs.c
index 0e2bfcf66f94..824650418869 100644
--- a/lib/krb5/get_addrs.c
+++ b/lib/krb5/get_addrs.c
@@ -64,10 +64,8 @@ gethostname_fallback (krb5_context context, krb5_addresses *res)
}
res->len = 1;
res->val = malloc (sizeof(*res->val));
- if (res->val == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (res->val == NULL)
+ return krb5_enomem(context);
res->val[0].addr_type = hostent->h_addrtype;
res->val[0].address.data = NULL;
res->val[0].address.length = 0;
@@ -130,10 +128,10 @@ find_all_addresses (krb5_context context, krb5_addresses *res, int flags)
/* Allocate storage for them. */
res->val = calloc(num, sizeof(*res->val));
if (res->val == NULL) {
- krb5_free_addresses(context, &ignore_addresses);
+ if (flags & EXTRA_ADDRESSES)
+ krb5_free_addresses(context, &ignore_addresses);
freeifaddrs(ifa0);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
/* Now traverse the list. */
diff --git a/lib/krb5/get_cred.c b/lib/krb5/get_cred.c
index e3bb23a2e9d7..f7318eeb2488 100644
--- a/lib/krb5/get_cred.c
+++ b/lib/krb5/get_cred.c
@@ -105,9 +105,7 @@ set_auth_data (krb5_context context,
ALLOC(req_body->enc_authorization_data, 1);
if (req_body->enc_authorization_data == NULL) {
free (buf);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = krb5_crypto_init(context, subkey, 0, &crypto);
if (ret) {
@@ -159,9 +157,7 @@ init_tgs_req (krb5_context context,
if (in_creds->session.keytype) {
ALLOC_SEQ(&t->req_body.etype, 1);
if(t->req_body.etype.val == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
t->req_body.etype.val[0] = in_creds->session.keytype;
@@ -176,13 +172,15 @@ init_tgs_req (krb5_context context,
goto fail;
t->req_body.addresses = addresses;
t->req_body.kdc_options = flags.b;
+ t->req_body.kdc_options.forwardable = krbtgt->flags.b.forwardable;
+ t->req_body.kdc_options.renewable = krbtgt->flags.b.renewable;
+ t->req_body.kdc_options.proxiable = krbtgt->flags.b.proxiable;
ret = copy_Realm(&in_creds->server->realm, &t->req_body.realm);
if (ret)
goto fail;
ALLOC(t->req_body.sname, 1);
if (t->req_body.sname == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
@@ -193,30 +191,43 @@ init_tgs_req (krb5_context context,
if (ret)
goto fail;
+ if (krbtgt->times.starttime) {
+ ALLOC(t->req_body.from, 1);
+ if(t->req_body.from == NULL){
+ ret = krb5_enomem(context);
+ goto fail;
+ }
+ *t->req_body.from = in_creds->times.starttime;
+ }
+
/* req_body.till should be NULL if there is no endtime specified,
but old MIT code (like DCE secd) doesn't like that */
ALLOC(t->req_body.till, 1);
if(t->req_body.till == NULL){
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
*t->req_body.till = in_creds->times.endtime;
+ if (t->req_body.kdc_options.renewable && krbtgt->times.renew_till) {
+ ALLOC(t->req_body.rtime, 1);
+ if(t->req_body.rtime == NULL){
+ ret = krb5_enomem(context);
+ goto fail;
+ }
+ *t->req_body.rtime = in_creds->times.renew_till;
+ }
+
t->req_body.nonce = nonce;
if(second_ticket){
ALLOC(t->req_body.additional_tickets, 1);
if (t->req_body.additional_tickets == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
ALLOC_SEQ(t->req_body.additional_tickets, 1);
if (t->req_body.additional_tickets->val == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
ret = copy_Ticket(second_ticket, t->req_body.additional_tickets->val);
@@ -225,14 +236,12 @@ init_tgs_req (krb5_context context,
}
ALLOC(t->padata, 1);
if (t->padata == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
ALLOC_SEQ(t->padata, 1 + padata->len);
if (t->padata->val == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
{
@@ -282,7 +291,7 @@ fail:
return ret;
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_krbtgt(krb5_context context,
krb5_ccache id,
krb5_realm realm,
@@ -307,8 +316,13 @@ _krb5_get_krbtgt(krb5_context context,
krb5_free_principal(context, tmp_cred.client);
return ret;
}
+ /*
+ * The forwardable TGT might not be the start TGT, in which case, it is
+ * generally, but not always already cached. Just in case, get it again if
+ * lost.
+ */
ret = krb5_get_credentials(context,
- KRB5_GC_CACHED,
+ 0,
id,
&tmp_cred,
cred);
@@ -556,6 +570,7 @@ get_cred_kdc(krb5_context context,
&krbtgt->addresses,
nonce,
eflags,
+ NULL,
decrypt_tkt_with_subkey,
subkey);
out2:
@@ -642,11 +657,8 @@ krb5_get_kdc_cred(krb5_context context,
krb5_creds *krbtgt;
*out_creds = calloc(1, sizeof(**out_creds));
- if(*out_creds == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(*out_creds == NULL)
+ return krb5_enomem(context);
ret = _krb5_get_krbtgt (context,
id,
in_creds->server->realm,
@@ -695,7 +707,10 @@ find_cred(krb5_context context,
krb5_cc_clear_mcred(&mcreds);
mcreds.server = server;
- ret = krb5_cc_retrieve_cred(context, id, KRB5_TC_DONT_MATCH_REALM,
+ krb5_timeofday(context, &mcreds.times.endtime);
+ ret = krb5_cc_retrieve_cred(context, id,
+ KRB5_TC_DONT_MATCH_REALM |
+ KRB5_TC_MATCH_TIMES,
&mcreds, out_creds);
if(ret == 0)
return 0;
@@ -719,11 +734,8 @@ add_cred(krb5_context context, krb5_creds const *tkt, krb5_creds ***tgts)
for(i = 0; tmp && tmp[i]; i++); /* XXX */
tmp = realloc(tmp, (i+2)*sizeof(*tmp));
- if(tmp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(tmp == NULL)
+ return krb5_enomem(context);
*tgts = tmp;
ret = krb5_copy_creds(context, tkt, &tmp[i]);
tmp[i+1] = NULL;
@@ -742,18 +754,22 @@ get_cred_kdc_capath_worker(krb5_context context,
krb5_creds ***ret_tgts)
{
krb5_error_code ret;
- krb5_creds *tgt, tmp_creds;
+ krb5_creds *tgt = NULL;
+ krb5_creds tmp_creds;
krb5_const_realm client_realm, server_realm;
int ok_as_delegate = 1;
- *out_creds = NULL;
+ *out_creds = calloc(1, sizeof(**out_creds));
+ if (*out_creds == NULL)
+ return krb5_enomem(context);
+
+ memset(&tmp_creds, 0, sizeof(tmp_creds));
client_realm = krb5_principal_get_realm(context, in_creds->client);
server_realm = krb5_principal_get_realm(context, in_creds->server);
- memset(&tmp_creds, 0, sizeof(tmp_creds));
ret = krb5_copy_principal(context, in_creds->client, &tmp_creds.client);
- if(ret)
- return ret;
+ if (ret)
+ goto out;
ret = krb5_make_principal(context,
&tmp_creds.server,
@@ -761,57 +777,66 @@ get_cred_kdc_capath_worker(krb5_context context,
KRB5_TGS_NAME,
server_realm,
NULL);
- if(ret){
- krb5_free_principal(context, tmp_creds.client);
- return ret;
- }
+ if (ret)
+ goto out;
+
{
krb5_creds tgts;
+ /*
+ * If we have krbtgt/server_realm@try_realm cached, use it and we're
+ * done.
+ */
ret = find_cred(context, ccache, tmp_creds.server,
*ret_tgts, &tgts);
- if(ret == 0){
+ if (ret == 0) {
/* only allow implicit ok_as_delegate if the realm is the clients realm */
- if (strcmp(try_realm, client_realm) != 0 || strcmp(try_realm, server_realm) != 0)
+ if (strcmp(try_realm, client_realm) != 0
+ || strcmp(try_realm, server_realm) != 0) {
ok_as_delegate = tgts.flags.b.ok_as_delegate;
+ }
- *out_creds = calloc(1, sizeof(**out_creds));
- if(*out_creds == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
- } else {
- ret = get_cred_kdc_address(context, ccache, flags, NULL,
- in_creds, &tgts,
- impersonate_principal,
- second_ticket,
- *out_creds);
- if (ret) {
- free (*out_creds);
- *out_creds = NULL;
- } else if (ok_as_delegate == 0)
- (*out_creds)->flags.b.ok_as_delegate = 0;
+ ret = get_cred_kdc_address(context, ccache, flags, NULL,
+ in_creds, &tgts,
+ impersonate_principal,
+ second_ticket,
+ *out_creds);
+ krb5_free_cred_contents(context, &tgts);
+ if (ret == 0 &&
+ !krb5_principal_compare(context, in_creds->server,
+ (*out_creds)->server)) {
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
}
- krb5_free_cred_contents(context, &tgts);
- krb5_free_principal(context, tmp_creds.server);
- krb5_free_principal(context, tmp_creds.client);
- return ret;
+ if (ret == 0 && ok_as_delegate == 0)
+ (*out_creds)->flags.b.ok_as_delegate = 0;
+
+ goto out;
}
}
- if(krb5_realm_compare(context, in_creds->client, in_creds->server))
- return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
- /* XXX this can loop forever */
- while(1){
+ if (krb5_realm_compare(context, in_creds->client, in_creds->server)) {
+ ret = not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
+ goto out;
+ }
+
+ /*
+ * XXX This can loop forever, plus we recurse, so we can't just keep a
+ * count here. The count would have to get passed around by reference.
+ *
+ * The KDCs check for transit loops for us, and capath data is finite, so
+ * in fact we'll fall out of this loop at some point. We should do our own
+ * transit loop checking (like get_cred_kdc_referral()), and we should
+ * impose a max number of iterations altogether. But barring malicious or
+ * broken KDCs, this is good enough.
+ */
+ while (1) {
heim_general_string tgt_inst;
ret = get_cred_kdc_capath(context, flags, ccache, &tmp_creds,
NULL, NULL, &tgt, ret_tgts);
- if(ret) {
- krb5_free_principal(context, tmp_creds.server);
- krb5_free_principal(context, tmp_creds.client);
- return ret;
- }
+ if (ret)
+ goto out;
+
/*
* if either of the chain or the ok_as_delegate was stripped
* by the kdc, make sure we strip it too.
@@ -822,46 +847,46 @@ get_cred_kdc_capath_worker(krb5_context context,
}
ret = add_cred(context, tgt, ret_tgts);
- if(ret) {
- krb5_free_principal(context, tmp_creds.server);
- krb5_free_principal(context, tmp_creds.client);
- return ret;
- }
+ if (ret)
+ goto out;
tgt_inst = tgt->server->name.name_string.val[1];
- if(strcmp(tgt_inst, server_realm) == 0)
+ if (strcmp(tgt_inst, server_realm) == 0)
break;
krb5_free_principal(context, tmp_creds.server);
+ tmp_creds.server = NULL;
ret = krb5_make_principal(context, &tmp_creds.server,
tgt_inst, KRB5_TGS_NAME, server_realm, NULL);
- if(ret) {
- krb5_free_principal(context, tmp_creds.server);
- krb5_free_principal(context, tmp_creds.client);
- return ret;
- }
+ if (ret)
+ goto out;
ret = krb5_free_creds(context, tgt);
- if(ret) {
- krb5_free_principal(context, tmp_creds.server);
- krb5_free_principal(context, tmp_creds.client);
- return ret;
- }
+ tgt = NULL;
+ if (ret)
+ goto out;
}
- krb5_free_principal(context, tmp_creds.server);
- krb5_free_principal(context, tmp_creds.client);
- *out_creds = calloc(1, sizeof(**out_creds));
- if(*out_creds == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
- } else {
- ret = get_cred_kdc_address (context, ccache, flags, NULL,
- in_creds, tgt, impersonate_principal,
- second_ticket, *out_creds);
- if (ret) {
- free (*out_creds);
- *out_creds = NULL;
- }
+ ret = get_cred_kdc_address(context, ccache, flags, NULL,
+ in_creds, tgt, impersonate_principal,
+ second_ticket, *out_creds);
+ if (ret == 0 &&
+ !krb5_principal_compare(context, in_creds->server,
+ (*out_creds)->server)) {
+ krb5_free_cred_contents(context, *out_creds);
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
}
- krb5_free_creds(context, tgt);
+ if (ret == 0 && ok_as_delegate == 0)
+ (*out_creds)->flags.b.ok_as_delegate = 0;
+
+out:
+ if (ret) {
+ krb5_free_creds(context, *out_creds);
+ *out_creds = NULL;
+ }
+ if (tmp_creds.server)
+ krb5_free_principal(context, tmp_creds.server);
+ if (tmp_creds.client)
+ krb5_free_principal(context, tmp_creds.client);
+ if (tgt)
+ krb5_free_creds(context, tgt);
return ret;
}
@@ -915,6 +940,13 @@ get_cred_kdc_capath(krb5_context context,
return ret;
}
+/*
+ * Get a service ticket from a KDC by chasing referrals from a start realm.
+ *
+ * All referral TGTs produced in the process are thrown away when we're done.
+ * We don't store them, and we don't allow other search mechanisms (capaths) to
+ * use referral TGTs produced here.
+ */
static krb5_error_code
get_cred_kdc_referral(krb5_context context,
krb5_kdc_flags flags,
@@ -922,14 +954,16 @@ get_cred_kdc_referral(krb5_context context,
krb5_creds *in_creds,
krb5_principal impersonate_principal,
Ticket *second_ticket,
- krb5_creds **out_creds,
- krb5_creds ***ret_tgts)
+ krb5_creds **out_creds)
{
- krb5_const_realm client_realm;
+ krb5_realm start_realm = NULL;
+ krb5_data config_start_realm;
krb5_error_code ret;
krb5_creds tgt, referral, ticket;
+ krb5_creds **referral_tgts = NULL; /* used for loop detection */
int loop = 0;
int ok_as_delegate = 1;
+ size_t i;
if (in_creds->server->name.name_string.len < 2 && !flags.b.canonicalize) {
krb5_set_error_message(context, KRB5KDC_ERR_PATH_NOT_ACCEPTED,
@@ -944,33 +978,49 @@ get_cred_kdc_referral(krb5_context context,
*out_creds = NULL;
- client_realm = krb5_principal_get_realm(context, in_creds->client);
+
+ ret = krb5_cc_get_config(context, ccache, NULL, "start_realm", &config_start_realm);
+ if (ret == 0) {
+ start_realm = strndup(config_start_realm.data, config_start_realm.length);
+ krb5_data_free(&config_start_realm);
+ } else {
+ start_realm = strdup(krb5_principal_get_realm(context, in_creds->client));
+ }
+ if (start_realm == NULL)
+ return krb5_enomem(context);
/* find tgt for the clients base realm */
{
krb5_principal tgtname;
ret = krb5_make_principal(context, &tgtname,
- client_realm,
+ start_realm,
KRB5_TGS_NAME,
- client_realm,
+ start_realm,
NULL);
- if(ret)
+ if (ret) {
+ free(start_realm);
return ret;
+ }
- ret = find_cred(context, ccache, tgtname, *ret_tgts, &tgt);
+ ret = find_cred(context, ccache, tgtname, NULL, &tgt);
krb5_free_principal(context, tgtname);
- if (ret)
+ if (ret) {
+ free(start_realm);
return ret;
+ }
}
referral = *in_creds;
ret = krb5_copy_principal(context, in_creds->server, &referral.server);
if (ret) {
krb5_free_cred_contents(context, &tgt);
+ free(start_realm);
return ret;
}
- ret = krb5_principal_set_realm(context, referral.server, client_realm);
+ ret = krb5_principal_set_realm(context, referral.server, start_realm);
+ free(start_realm);
+ start_realm = NULL;
if (ret) {
krb5_free_cred_contents(context, &tgt);
krb5_free_principal(context, referral.server);
@@ -982,11 +1032,13 @@ get_cred_kdc_referral(krb5_context context,
krb5_creds mcreds;
char *referral_realm;
- /* Use cache if we are not doing impersonation or contrainte deleg */
+ /* Use cache if we are not doing impersonation or contrained deleg */
if (impersonate_principal == NULL || flags.b.constrained_delegation) {
krb5_cc_clear_mcred(&mcreds);
mcreds.server = referral.server;
- ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &ticket);
+ krb5_timeofday(context, &mcreds.times.endtime);
+ ret = krb5_cc_retrieve_cred(context, ccache, KRB5_TC_MATCH_TIMES,
+ &mcreds, &ticket);
} else
ret = EINVAL;
@@ -1015,17 +1067,16 @@ get_cred_kdc_referral(krb5_context context,
referral_realm = ticket.server->name.name_string.val[1];
/* check that there are no referrals loops */
- tickets = *ret_tgts;
+ tickets = referral_tgts;
krb5_cc_clear_mcred(&mcreds);
mcreds.server = ticket.server;
- while(tickets && *tickets){
- if(krb5_compare_creds(context,
+ while (tickets && *tickets){
+ if (krb5_compare_creds(context,
KRB5_TC_DONT_MATCH_REALM,
&mcreds,
- *tickets))
- {
+ *tickets)) {
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
N_("Referral from %s "
"loops back to realm %s", ""),
@@ -1047,7 +1098,9 @@ get_cred_kdc_referral(krb5_context context,
ticket.flags.b.ok_as_delegate = 0;
}
- ret = add_cred(context, &ticket, ret_tgts);
+ _krb5_debug(context, 6, "get_cred_kdc_referral: got referral "
+ "to %s from %s", referral_realm, referral.server->realm);
+ ret = add_cred(context, &ticket, &referral_tgts);
if (ret)
goto out;
@@ -1065,6 +1118,9 @@ get_cred_kdc_referral(krb5_context context,
ret = krb5_copy_creds(context, &ticket, out_creds);
out:
+ for (i = 0; referral_tgts && referral_tgts[i]; i++)
+ krb5_free_creds(context, referral_tgts[i]);
+ free(referral_tgts);
krb5_free_principal(context, referral.server);
krb5_free_cred_contents(context, &tgt);
krb5_free_cred_contents(context, &ticket);
@@ -1077,7 +1133,7 @@ out:
* codebase.
*/
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_cred_kdc_any(krb5_context context,
krb5_kdc_flags flags,
krb5_ccache ccache,
@@ -1091,29 +1147,89 @@ _krb5_get_cred_kdc_any(krb5_context context,
krb5_deltat offset;
ret = krb5_cc_get_kdc_offset(context, ccache, &offset);
- if (ret) {
+ if (ret == 0) {
context->kdc_sec_offset = offset;
context->kdc_usec_offset = 0;
}
- ret = get_cred_kdc_referral(context,
- flags,
- ccache,
- in_creds,
- impersonate_principal,
- second_ticket,
- out_creds,
- ret_tgts);
- if (ret == 0 || flags.b.canonicalize)
- return ret;
- return get_cred_kdc_capath(context,
- flags,
- ccache,
- in_creds,
- impersonate_principal,
- second_ticket,
- out_creds,
- ret_tgts);
+ if (strcmp(in_creds->server->realm, "") != 0) {
+ /*
+ * Non-empty realm? Try capaths first. We might have local
+ * policy (capaths) to honor.
+ */
+ ret = get_cred_kdc_capath(context,
+ flags,
+ ccache,
+ in_creds,
+ impersonate_principal,
+ second_ticket,
+ out_creds,
+ ret_tgts);
+ if (ret == 0)
+ return ret;
+ }
+
+ /* Otherwise try referrals */
+ return get_cred_kdc_referral(context,
+ flags,
+ ccache,
+ in_creds,
+ impersonate_principal,
+ second_ticket,
+ out_creds);
+}
+
+static krb5_error_code
+check_cc(krb5_context context, krb5_flags options, krb5_ccache ccache,
+ krb5_creds *in_creds, krb5_creds *out_creds)
+{
+ krb5_error_code ret;
+ krb5_timestamp now;
+ krb5_times save_times = in_creds->times;
+ NAME_TYPE save_type = in_creds->server->name.name_type;
+
+ krb5_timeofday(context, &now);
+
+ if (!(options & KRB5_GC_EXPIRED_OK) &&
+ in_creds->times.endtime < now) {
+ in_creds->times.renew_till = 0;
+ krb5_timeofday(context, &in_creds->times.endtime);
+ options |= KRB5_TC_MATCH_TIMES;
+ }
+
+ if (save_type == KRB5_NT_SRV_HST_NEEDS_CANON) {
+ /* Avoid name canonicalization in krb5_cc_retrieve_cred() */
+ krb5_principal_set_type(context, in_creds->server, KRB5_NT_SRV_HST);
+ }
+
+ ret = krb5_cc_retrieve_cred(context, ccache,
+ (options &
+ (KRB5_TC_DONT_MATCH_REALM |
+ KRB5_TC_MATCH_KEYTYPE |
+ KRB5_TC_MATCH_TIMES)),
+ in_creds, out_creds);
+
+ in_creds->server->name.name_type = save_type;
+ in_creds->times = save_times;
+ return ret;
+}
+
+static void
+store_cred(krb5_context context, krb5_ccache ccache,
+ krb5_const_principal server_princ, krb5_creds *creds)
+{
+ if (!krb5_principal_compare(context, creds->server, server_princ)) {
+ krb5_principal tmp_princ = creds->server;
+ /*
+ * Store the cred with the pre-canon server princ first so it
+ * can be found quickly in the future.
+ */
+ creds->server = (krb5_principal)server_princ;
+ krb5_cc_store_cred(context, ccache, creds);
+ creds->server = tmp_princ;
+ /* Then store again with the canonicalized server princ */
+ }
+ krb5_cc_store_cred(context, ccache, creds);
}
@@ -1126,64 +1242,72 @@ krb5_get_credentials_with_flags(krb5_context context,
krb5_creds **out_creds)
{
krb5_error_code ret;
+ krb5_name_canon_iterator name_canon_iter = NULL;
+ krb5_name_canon_rule_options rule_opts;
+ krb5_const_principal try_princ = NULL;
+ krb5_principal save_princ = in_creds->server;
krb5_creds **tgts;
krb5_creds *res_creds;
int i;
+ if (_krb5_have_debug(context, 5)) {
+ char *unparsed;
+
+ ret = krb5_unparse_name(context, in_creds->server, &unparsed);
+ if (ret) {
+ _krb5_debug(context, 5, "krb5_get_creds: unable to display "
+ "requested service principal");
+ } else {
+ _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
+ "for %s", unparsed);
+ free(unparsed);
+ }
+ }
+
if (in_creds->session.keytype) {
ret = krb5_enctype_valid(context, in_creds->session.keytype);
if (ret)
return ret;
+ options |= KRB5_TC_MATCH_KEYTYPE;
}
*out_creds = NULL;
res_creds = calloc(1, sizeof(*res_creds));
- if (res_creds == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (res_creds == NULL)
+ return krb5_enomem(context);
- if (in_creds->session.keytype)
- options |= KRB5_TC_MATCH_KEYTYPE;
-
- /*
- * If we got a credential, check if credential is expired before
- * returning it.
- */
- ret = krb5_cc_retrieve_cred(context,
- ccache,
- in_creds->session.keytype ?
- KRB5_TC_MATCH_KEYTYPE : 0,
- in_creds, res_creds);
- /*
- * If we got a credential, check if credential is expired before
- * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
- */
- if (ret == 0) {
- krb5_timestamp timeret;
+ ret = krb5_name_canon_iterator_start(context, in_creds->server,
+ &name_canon_iter);
+ if (ret)
+ return ret;
- /* If expired ok, don't bother checking */
- if(options & KRB5_GC_EXPIRED_OK) {
- *out_creds = res_creds;
- return 0;
- }
+next_rule:
+ krb5_free_cred_contents(context, res_creds);
+ memset(res_creds, 0, sizeof (*res_creds));
+ ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
+ &rule_opts);
+ in_creds->server = rk_UNCONST(try_princ);
+ if (ret)
+ goto out;
- krb5_timeofday(context, &timeret);
- if(res_creds->times.endtime > timeret) {
- *out_creds = res_creds;
- return 0;
- }
- if(options & KRB5_GC_CACHED)
- krb5_cc_remove_cred(context, ccache, 0, res_creds);
+ if (name_canon_iter == NULL) {
+ if (options & KRB5_GC_CACHED)
+ ret = KRB5_CC_NOTFOUND;
+ else
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ goto out;
+ }
+ ret = check_cc(context, options, ccache, in_creds, res_creds);
+ if (ret == 0) {
+ *out_creds = res_creds;
+ res_creds = NULL;
+ goto out;
} else if(ret != KRB5_CC_END) {
- free(res_creds);
- return ret;
+ goto out;
}
- free(res_creds);
- if(options & KRB5_GC_CACHED)
- return not_found(context, in_creds->server, KRB5_CC_NOTFOUND);
+ if (options & KRB5_GC_CACHED)
+ goto next_rule;
if(options & KRB5_GC_USER_USER)
flags.b.enc_tkt_in_skey = 1;
@@ -1193,14 +1317,42 @@ krb5_get_credentials_with_flags(krb5_context context,
tgts = NULL;
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
in_creds, NULL, NULL, out_creds, &tgts);
- for(i = 0; tgts && tgts[i]; i++) {
- krb5_cc_store_cred(context, ccache, tgts[i]);
+ for (i = 0; tgts && tgts[i]; i++) {
+ if ((options & KRB5_GC_NO_STORE) == 0)
+ krb5_cc_store_cred(context, ccache, tgts[i]);
krb5_free_creds(context, tgts[i]);
}
free(tgts);
+
+ /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
+ if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
+ !(rule_opts & KRB5_NCRO_USE_FAST))
+ goto next_rule;
+
if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
- krb5_cc_store_cred(context, ccache, *out_creds);
- return ret;
+ store_cred(context, ccache, in_creds->server, *out_creds);
+
+ if (ret == 0 && _krb5_have_debug(context, 5)) {
+ char *unparsed;
+
+ ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
+ if (ret) {
+ _krb5_debug(context, 5, "krb5_get_creds: unable to display "
+ "service principal");
+ } else {
+ _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
+ unparsed);
+ free(unparsed);
+ }
+ }
+
+out:
+ in_creds->server = save_princ;
+ krb5_free_creds(context, res_creds);
+ krb5_free_name_canon_iterator(context, name_canon_iter);
+ if (ret)
+ return not_found(context, in_creds->server, ret);
+ return 0;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -1228,11 +1380,8 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds_opt_alloc(krb5_context context, krb5_get_creds_opt *opt)
{
*opt = calloc(1, sizeof(**opt));
- if (*opt == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*opt == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -1297,11 +1446,8 @@ krb5_get_creds_opt_set_ticket(krb5_context context,
krb5_error_code ret;
opt->ticket = malloc(sizeof(*ticket));
- if (opt->ticket == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (opt->ticket == NULL)
+ return krb5_enomem(context);
ret = copy_Ticket(ticket, opt->ticket);
if (ret) {
free(opt->ticket);
@@ -1315,7 +1461,6 @@ krb5_get_creds_opt_set_ticket(krb5_context context,
}
-
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_creds(krb5_context context,
krb5_get_creds_opt opt,
@@ -1329,7 +1474,29 @@ krb5_get_creds(krb5_context context,
krb5_error_code ret;
krb5_creds **tgts;
krb5_creds *res_creds;
+ krb5_const_principal try_princ = NULL;
+ krb5_name_canon_iterator name_canon_iter = NULL;
+ krb5_name_canon_rule_options rule_opts;
int i;
+ int type;
+ const char *comp;
+
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.server = rk_UNCONST(inprinc);
+
+ if (_krb5_have_debug(context, 5)) {
+ char *unparsed;
+
+ ret = krb5_unparse_name(context, in_creds.server, &unparsed);
+ if (ret) {
+ _krb5_debug(context, 5, "krb5_get_creds: unable to display "
+ "requested service principal");
+ } else {
+ _krb5_debug(context, 5, "krb5_get_creds: requesting a ticket "
+ "for %s", unparsed);
+ free(unparsed);
+ }
+ }
if (opt && opt->enctype) {
ret = krb5_enctype_valid(context, opt->enctype);
@@ -1337,9 +1504,6 @@ krb5_get_creds(krb5_context context,
return ret;
}
- memset(&in_creds, 0, sizeof(in_creds));
- in_creds.server = rk_UNCONST(inprinc);
-
ret = krb5_cc_get_principal(context, ccache, &in_creds.client);
if (ret)
return ret;
@@ -1354,9 +1518,7 @@ krb5_get_creds(krb5_context context,
res_creds = calloc(1, sizeof(*res_creds));
if (res_creds == NULL) {
krb5_free_principal(context, in_creds.client);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
if (opt && opt->enctype) {
@@ -1364,49 +1526,47 @@ krb5_get_creds(krb5_context context,
options |= KRB5_TC_MATCH_KEYTYPE;
}
- /*
- * If we got a credential, check if credential is expired before
- * returning it.
- */
- ret = krb5_cc_retrieve_cred(context,
- ccache,
- options & KRB5_TC_MATCH_KEYTYPE,
- &in_creds, res_creds);
- /*
- * If we got a credential, check if credential is expired before
- * returning it, but only if KRB5_GC_EXPIRED_OK is not set.
- */
- if (ret == 0) {
- krb5_timestamp timeret;
-
- /* If expired ok, don't bother checking */
- if(options & KRB5_GC_EXPIRED_OK) {
- *out_creds = res_creds;
- krb5_free_principal(context, in_creds.client);
- goto out;
- }
+ ret = krb5_name_canon_iterator_start(context, in_creds.server,
+ &name_canon_iter);
+ if (ret)
+ goto out;
- krb5_timeofday(context, &timeret);
- if(res_creds->times.endtime > timeret) {
- *out_creds = res_creds;
- krb5_free_principal(context, in_creds.client);
- goto out;
- }
- if(options & KRB5_GC_CACHED)
- krb5_cc_remove_cred(context, ccache, 0, res_creds);
+next_rule:
+ ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
+ &rule_opts);
+ in_creds.server = rk_UNCONST(try_princ);
+ if (ret)
+ goto out;
- } else if(ret != KRB5_CC_END) {
- free(res_creds);
- krb5_free_principal(context, in_creds.client);
+ if (name_canon_iter == NULL) {
+ if (options & KRB5_GC_CACHED)
+ ret = KRB5_CC_NOTFOUND;
+ else
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
goto out;
}
- free(res_creds);
- if(options & KRB5_GC_CACHED) {
- krb5_free_principal(context, in_creds.client);
- ret = not_found(context, in_creds.server, KRB5_CC_NOTFOUND);
+
+ ret = check_cc(context, options, ccache, &in_creds, res_creds);
+ if (ret == 0) {
+ *out_creds = res_creds;
+ res_creds = NULL;
+ goto out;
+ } else if (ret != KRB5_CC_END) {
goto out;
}
- if(options & KRB5_GC_USER_USER) {
+ if (options & KRB5_GC_CACHED)
+ goto next_rule;
+
+ type = krb5_principal_get_type(context, try_princ);
+ comp = krb5_principal_get_comp_string(context, try_princ, 0);
+ if ((type == KRB5_NT_SRV_HST || type == KRB5_NT_UNKNOWN) &&
+ comp != NULL && strcmp(comp, "host") == 0)
+ flags.b.canonicalize = 1;
+ if (rule_opts & KRB5_NCRO_NO_REFERRALS)
+ flags.b.canonicalize = 0;
+ else
+ flags.b.canonicalize = (options & KRB5_GC_CANONICALIZE) ? 1 : 0;
+ if (options & KRB5_GC_USER_USER) {
flags.b.enc_tkt_in_skey = 1;
options |= KRB5_GC_NO_STORE;
}
@@ -1418,25 +1578,47 @@ krb5_get_creds(krb5_context context,
flags.b.request_anonymous = 1; /* XXX ARGH confusion */
flags.b.constrained_delegation = 1;
}
- if (options & KRB5_GC_CANONICALIZE)
- flags.b.canonicalize = 1;
tgts = NULL;
ret = _krb5_get_cred_kdc_any(context, flags, ccache,
- &in_creds, opt->self, opt->ticket,
- out_creds, &tgts);
- krb5_free_principal(context, in_creds.client);
- for(i = 0; tgts && tgts[i]; i++) {
- krb5_cc_store_cred(context, ccache, tgts[i]);
+ &in_creds, opt ? opt->self : 0,
+ opt ? opt->ticket : 0, out_creds,
+ &tgts);
+ for (i = 0; tgts && tgts[i]; i++) {
+ if ((options & KRB5_GC_NO_STORE) == 0)
+ krb5_cc_store_cred(context, ccache, tgts[i]);
krb5_free_creds(context, tgts[i]);
}
free(tgts);
- if(ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
- krb5_cc_store_cred(context, ccache, *out_creds);
- out:
- _krb5_debug(context, 5, "krb5_get_creds: ret = %d", ret);
+ /* We don't yet have TGS w/ FAST, so we can't protect KBR-ERRORs */
+ if (ret == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN &&
+ !(rule_opts & KRB5_NCRO_USE_FAST))
+ goto next_rule;
+
+ if (ret == 0 && (options & KRB5_GC_NO_STORE) == 0)
+ store_cred(context, ccache, inprinc, *out_creds);
+
+ if (ret == 0 && _krb5_have_debug(context, 5)) {
+ char *unparsed;
+
+ ret = krb5_unparse_name(context, (*out_creds)->server, &unparsed);
+ if (ret) {
+ _krb5_debug(context, 5, "krb5_get_creds: unable to display "
+ "service principal");
+ } else {
+ _krb5_debug(context, 5, "krb5_get_creds: got a ticket for %s",
+ unparsed);
+ free(unparsed);
+ }
+ }
+out:
+ krb5_free_creds(context, res_creds);
+ krb5_free_principal(context, in_creds.client);
+ krb5_free_name_canon_iterator(context, name_canon_iter);
+ if (ret)
+ return not_found(context, inprinc, ret);
return ret;
}
diff --git a/lib/krb5/get_default_principal.c b/lib/krb5/get_default_principal.c
index 44baa6d1c2cd..e102e5a1fad8 100644
--- a/lib/krb5/get_default_principal.c
+++ b/lib/krb5/get_default_principal.c
@@ -56,7 +56,7 @@ get_env_user(void)
* avoid recursive calls.
*/
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_default_principal_local (krb5_context context,
krb5_principal *princ)
{
@@ -100,7 +100,7 @@ _krb5_get_default_principal_local (krb5_context context,
#define SECURITY_WIN32
#include <security.h>
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_default_principal_local(krb5_context context,
krb5_principal *princ)
{
diff --git a/lib/krb5/get_default_realm.c b/lib/krb5/get_default_realm.c
index 2a4933a62a7d..81a55bb15a62 100644
--- a/lib/krb5/get_default_realm.c
+++ b/lib/krb5/get_default_realm.c
@@ -73,11 +73,8 @@ krb5_get_default_realm(krb5_context context,
}
res = strdup (context->default_realms[0]);
- if (res == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (res == NULL)
+ return krb5_enomem(context);
*realm = res;
return 0;
}
diff --git a/lib/krb5/get_for_creds.c b/lib/krb5/get_for_creds.c
index 979fc9b0ae99..8a00ccec8133 100644
--- a/lib/krb5/get_for_creds.c
+++ b/lib/krb5/get_for_creds.c
@@ -49,8 +49,7 @@ add_addrs(krb5_context context,
tmp = realloc(addr->val, (addr->len + n) * sizeof(*addr->val));
if (tmp == NULL && (addr->len + n) != 0) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
addr->val = tmp;
@@ -266,8 +265,7 @@ krb5_get_forwarded_creds (krb5_context context,
cred.msg_type = krb_cred;
ALLOC_SEQ(&cred.tickets, 1);
if (cred.tickets.val == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out2;
}
ret = decode_Ticket(out_creds->ticket.data,
@@ -279,8 +277,7 @@ krb5_get_forwarded_creds (krb5_context context,
memset (&enc_krb_cred_part, 0, sizeof(enc_krb_cred_part));
ALLOC_SEQ(&enc_krb_cred_part.ticket_info, 1);
if (enc_krb_cred_part.ticket_info.val == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out4;
}
@@ -292,15 +289,13 @@ krb5_get_forwarded_creds (krb5_context context,
ALLOC(enc_krb_cred_part.timestamp, 1);
if (enc_krb_cred_part.timestamp == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out4;
}
*enc_krb_cred_part.timestamp = sec;
ALLOC(enc_krb_cred_part.usec, 1);
if (enc_krb_cred_part.usec == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out4;
}
*enc_krb_cred_part.usec = usec;
@@ -343,9 +338,7 @@ krb5_get_forwarded_creds (krb5_context context,
} else {
ALLOC(enc_krb_cred_part.r_address, 1);
if (enc_krb_cred_part.r_address == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out4;
}
@@ -407,7 +400,7 @@ krb5_get_forwarded_creds (krb5_context context,
*/
if (auth_context->flags & KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED) {
- cred.enc_part.etype = ENCTYPE_NULL;
+ cred.enc_part.etype = KRB5_ENCTYPE_NULL;
cred.enc_part.kvno = NULL;
cred.enc_part.cipher.data = buf;
cred.enc_part.cipher.length = buf_size;
diff --git a/lib/krb5/get_host_realm.c b/lib/krb5/get_host_realm.c
index ed7f54b3d698..955d5462d418 100644
--- a/lib/krb5/get_host_realm.c
+++ b/lib/krb5/get_host_realm.c
@@ -49,8 +49,10 @@
*/
static int
-copy_txt_to_realms (struct rk_resource_record *head,
- krb5_realm **realms)
+copy_txt_to_realms(krb5_context context,
+ const char *domain,
+ struct rk_resource_record *head,
+ krb5_realm **realms)
{
struct rk_resource_record *rr;
unsigned int n, i;
@@ -64,21 +66,36 @@ copy_txt_to_realms (struct rk_resource_record *head,
*realms = malloc ((n + 1) * sizeof(krb5_realm));
if (*realms == NULL)
- return -1;
+ return krb5_enomem(context);;
for (i = 0; i < n + 1; ++i)
(*realms)[i] = NULL;
for (i = 0, rr = head; rr; rr = rr->next) {
if (rr->type == rk_ns_t_txt) {
- char *tmp;
+ char *tmp = NULL;
+ int invalid_tld = 1;
- tmp = strdup(rr->u.txt);
+ /* Check for a gTLD controlled interruption */
+ if (strcmp("Your DNS configuration needs immediate "
+ "attention see https://icann.org/namecollision",
+ rr->u.txt) != 0) {
+ invalid_tld = 0;
+ tmp = strdup(rr->u.txt);
+ }
if (tmp == NULL) {
for (i = 0; i < n; ++i)
free ((*realms)[i]);
free (*realms);
- return -1;
+ if (invalid_tld) {
+ krb5_warnx(context,
+ "Realm lookup failed: "
+ "Domain '%s' needs immediate attention "
+ "see https://icann.org/namecollision",
+ domain);
+ return KRB5_KDC_UNREACH;
+ }
+ return krb5_enomem(context);;
}
(*realms)[i] = tmp;
++i;
@@ -97,7 +114,7 @@ dns_find_realm(krb5_context context,
struct rk_dns_reply *r;
const char **labels;
char **config_labels;
- int i, ret;
+ int i, ret = 0;
config_labels = krb5_config_get_strings(context, NULL, "libdefaults",
"dns_lookup_realm_labels", NULL);
@@ -110,24 +127,26 @@ dns_find_realm(krb5_context context,
for (i = 0; labels[i] != NULL; i++) {
ret = snprintf(dom, sizeof(dom), "%s.%s.", labels[i], domain);
if(ret < 0 || (size_t)ret >= sizeof(dom)) {
- if (config_labels)
- krb5_config_free_strings(config_labels);
- return -1;
+ ret = krb5_enomem(context);
+ goto out;
}
r = rk_dns_lookup(dom, "TXT");
if(r != NULL) {
- ret = copy_txt_to_realms (r->head, realms);
+ ret = copy_txt_to_realms(context, domain, r->head, realms);
rk_dns_free_data(r);
- if(ret == 0) {
- if (config_labels)
- krb5_config_free_strings(config_labels);
- return 0;
- }
+ if(ret == 0)
+ goto out;
}
}
+ krb5_set_error_message(context, KRB5_KDC_UNREACH,
+ "Realm lookup failed: "
+ "No DNS TXT record for %s",
+ domain);
+ ret = KRB5_KDC_UNREACH;
+out:
if (config_labels)
krb5_config_free_strings(config_labels);
- return -1;
+ return ret;
}
/*
@@ -159,55 +178,78 @@ config_find_realm(krb5_context context,
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-_krb5_get_host_realm_int (krb5_context context,
- const char *host,
- krb5_boolean use_dns,
- krb5_realm **realms)
+_krb5_get_host_realm_int(krb5_context context,
+ const char *host,
+ krb5_boolean use_dns,
+ krb5_realm **realms)
{
const char *p, *q;
+ const char *port;
krb5_boolean dns_locate_enable;
+ krb5_error_code ret = 0;
+
+ /* Strip off any trailing ":port" suffix. */
+ port = strchr(host, ':');
+ if (port != NULL) {
+ host = strndup(host, port - host);
+ if (host == NULL)
+ return krb5_enomem(context);
+ }
dns_locate_enable = krb5_config_get_bool_default(context, NULL, TRUE,
- "libdefaults", "dns_lookup_realm", NULL);
+ "libdefaults", "dns_lookup_realm", NULL);
for (p = host; p != NULL; p = strchr (p + 1, '.')) {
- if(config_find_realm(context, p, realms) == 0) {
- if(strcasecmp(*realms[0], "dns_locate") == 0) {
- if(use_dns)
- for (q = host; q != NULL; q = strchr(q + 1, '.'))
- if(dns_find_realm(context, q, realms) == 0)
- return 0;
- continue;
- } else
- return 0;
- }
- else if(use_dns && dns_locate_enable) {
- if(dns_find_realm(context, p, realms) == 0)
- return 0;
- }
+ if (config_find_realm(context, p, realms) == 0) {
+ if (strcasecmp(*realms[0], "dns_locate") != 0)
+ break;
+ krb5_free_host_realm(context, *realms);
+ *realms = NULL;
+ if (!use_dns)
+ continue;
+ for (q = host; q != NULL; q = strchr(q + 1, '.'))
+ if (dns_find_realm(context, q, realms) == 0)
+ break;
+ if (q)
+ break;
+ } else if (use_dns && dns_locate_enable) {
+ if (dns_find_realm(context, p, realms) == 0)
+ break;
+ }
}
- p = strchr(host, '.');
- if(p != NULL) {
- p++;
- *realms = malloc(2 * sizeof(krb5_realm));
- if (*realms == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
- (*realms)[0] = strdup(p);
- if((*realms)[0] == NULL) {
- free(*realms);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
- strupr((*realms)[0]);
- (*realms)[1] = NULL;
- return 0;
+ /*
+ * If 'p' is NULL, we did not find an explicit realm mapping in either the
+ * configuration file or DNS. Try the hostname suffix as a last resort.
+ *
+ * XXX: If we implement a KDC-specific variant of this function just for
+ * referrals, we could check whether we have a cross-realm TGT for the
+ * realm in question, and if not try the parent (loop again).
+ */
+ if (p == NULL) {
+ p = strchr(host, '.');
+ if (p != NULL) {
+ p++;
+ *realms = malloc(2 * sizeof(krb5_realm));
+ if (*realms != NULL &&
+ ((*realms)[0] = strdup(p)) != NULL) {
+ strupr((*realms)[0]);
+ (*realms)[1] = NULL;
+ } else {
+ free(*realms);
+ ret = krb5_enomem(context);
+ }
+ } else {
+ krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
+ N_("unable to find realm of host %s", ""),
+ host);
+ ret = KRB5_ERR_HOST_REALM_UNKNOWN;
+ }
}
- krb5_set_error_message(context, KRB5_ERR_HOST_REALM_UNKNOWN,
- N_("unable to find realm of host %s", ""),
- host);
- return KRB5_ERR_HOST_REALM_UNKNOWN;
+
+ /* If 'port' is not NULL, we have a copy of 'host' to free. */
+ if (port)
+ free((void *)host);
+ return ret;
}
/*
diff --git a/lib/krb5/get_in_tkt.c b/lib/krb5/get_in_tkt.c
index 41618b9616a4..471b900a0b97 100644
--- a/lib/krb5/get_in_tkt.c
+++ b/lib/krb5/get_in_tkt.c
@@ -31,6 +31,8 @@
* SUCH DAMAGE.
*/
+#define KRB5_DEPRECATED_FUNCTION(x)
+
#include "krb5_locl.h"
#ifndef HEIMDAL_SMALLER
@@ -113,14 +115,12 @@ add_padata(krb5_context context,
if (!enctypes) {
enctypes = context->etypes;
netypes = 0;
- for (ep = enctypes; *ep != ETYPE_NULL; ep++)
+ for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
netypes++;
}
pa2 = realloc (md->val, (md->len + netypes) * sizeof(*md->val));
- if (pa2 == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (pa2 == NULL)
+ return krb5_enomem(context);
md->val = pa2;
for (i = 0; i < netypes; ++i) {
@@ -164,14 +164,12 @@ init_as_req (krb5_context context,
a->req_body.kdc_options = opts;
a->req_body.cname = malloc(sizeof(*a->req_body.cname));
if (a->req_body.cname == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
a->req_body.sname = malloc(sizeof(*a->req_body.sname));
if (a->req_body.sname == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
ret = _krb5_principal2principalname (a->req_body.cname, creds->client);
@@ -187,8 +185,7 @@ init_as_req (krb5_context context,
if(creds->times.starttime) {
a->req_body.from = malloc(sizeof(*a->req_body.from));
if (a->req_body.from == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
*a->req_body.from = creds->times.starttime;
@@ -200,8 +197,7 @@ init_as_req (krb5_context context,
if(creds->times.renew_till){
a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
if (a->req_body.rtime == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
*a->req_body.rtime = creds->times.renew_till;
@@ -224,8 +220,7 @@ init_as_req (krb5_context context,
} else {
a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
if (a->req_body.addresses == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
@@ -249,8 +244,7 @@ init_as_req (krb5_context context,
size_t i;
ALLOC(a->padata, 1);
if(a->padata == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
a->padata->val = NULL;
@@ -288,8 +282,7 @@ init_as_req (krb5_context context,
else if (*ptypes == KRB5_PADATA_ENC_TIMESTAMP) {
ALLOC(a->padata, 1);
if (a->padata == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
a->padata->len = 0;
@@ -510,6 +503,7 @@ krb5_get_in_cred(krb5_context context,
NULL,
nonce,
flags,
+ NULL,
decrypt_proc,
decryptarg);
}
diff --git a/lib/krb5/heim_err.et b/lib/krb5/heim_err.et
index c47f77092f13..69039bb4dfe0 100644
--- a/lib/krb5/heim_err.et
+++ b/lib/krb5/heim_err.et
@@ -20,6 +20,7 @@ error_code SERVICE_NOMATCH, "Unacceptable service used"
error_code NOT_SEEKABLE, "File descriptor not seekable"
error_code TOO_BIG, "Offset too large"
error_code BAD_HDBENT_ENCODING, "Invalid HDB entry encoding"
+error_code RANDOM_OFFLINE, "No random source available"
index 64
prefix HEIM_PKINIT
@@ -44,4 +45,9 @@ error_code NONAME, "nodename nor servname provided, or not known"
error_code SERVICE, "servname not supported for ai_socktype"
error_code SOCKTYPE, "ai_socktype not supported"
error_code SYSTEM, "system error returned in errno"
+
+index 192
+prefix HEIM_NET
+error_code CONN_REFUSED, "connection refused"
+
end
diff --git a/lib/krb5/init_creds.c b/lib/krb5/init_creds.c
index 25bef0f34045..58734c43f976 100644
--- a/lib/krb5/init_creds.c
+++ b/lib/krb5/init_creds.c
@@ -60,18 +60,13 @@ krb5_get_init_creds_opt_alloc(krb5_context context,
*opt = NULL;
o = calloc(1, sizeof(*o));
- if (o == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (o == NULL)
+ return krb5_enomem(context);
o->opt_private = calloc(1, sizeof(*o->opt_private));
if (o->opt_private == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
free(o);
- return ENOMEM;
+ return krb5_enomem(context);
}
o->opt_private->refcount = 1;
*opt = o;
@@ -200,6 +195,13 @@ krb5_get_init_creds_opt_set_default_flags(krb5_context context,
#endif
}
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_get_init_creds_opt_set_change_password_prompt(krb5_get_init_creds_opt *opt,
+ int change_password_prompt)
+{
+ opt->flags |= KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT;
+ opt->change_password_prompt = change_password_prompt;
+}
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_init_creds_opt_set_tkt_life(krb5_get_init_creds_opt *opt,
@@ -379,7 +381,7 @@ krb5_get_init_creds_opt_set_process_last_req(krb5_context context,
void *ctx)
{
krb5_error_code ret;
- ret = require_ext_opt(context, opt, "init_creds_opt_set_win2k");
+ ret = require_ext_opt(context, opt, "init_creds_opt_set_process_last_req");
if (ret)
return ret;
@@ -423,10 +425,8 @@ krb5_get_init_creds_opt_get_error(krb5_context context,
KRB5_DEPRECATED_FUNCTION("Use X instead")
{
*error = calloc(1, sizeof(**error));
- if (*error == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*error == NULL)
+ return krb5_enomem(context);
return 0;
}
diff --git a/lib/krb5/init_creds_pw.c b/lib/krb5/init_creds_pw.c
index 37f4147c372a..1eece1760daa 100644
--- a/lib/krb5/init_creds_pw.c
+++ b/lib/krb5/init_creds_pw.c
@@ -34,6 +34,9 @@
*/
#include "krb5_locl.h"
+#ifndef WIN32
+#include <heim-ipc.h>
+#endif /* WIN32 */
typedef struct krb5_get_init_creds_ctx {
KDCOptions flags;
@@ -61,6 +64,10 @@ typedef struct krb5_get_init_creds_ctx {
krb5_pk_init_ctx pk_init_ctx;
int ic_flags;
+ struct {
+ unsigned change_password:1;
+ } runflags;
+
int used_pa_types;
#define USED_PKINIT 1
#define USED_PKINIT_W2K 2
@@ -76,7 +83,25 @@ typedef struct krb5_get_init_creds_ctx {
void *prompter_data;
struct pa_info_data *ppaid;
-
+ struct fast_state {
+ enum PA_FX_FAST_REQUEST_enum type;
+ unsigned int flags;
+#define KRB5_FAST_REPLY_KEY_USE_TO_ENCRYPT_THE_REPLY 1
+#define KRB5_FAST_REPLY_KEY_USE_IN_TRANSACTION 2
+#define KRB5_FAST_KDC_REPLY_KEY_REPLACED 4
+#define KRB5_FAST_REPLY_REPLY_VERIFED 8
+#define KRB5_FAST_STRONG 16
+#define KRB5_FAST_EXPECTED 32 /* in exchange with KDC, fast was discovered */
+#define KRB5_FAST_REQUIRED 64 /* fast required by action of caller */
+#define KRB5_FAST_DISABLED 128
+#define KRB5_FAST_AP_ARMOR_SERVICE 256
+ krb5_keyblock *reply_key;
+ krb5_ccache armor_ccache;
+ krb5_principal armor_service;
+ krb5_crypto armor_crypto;
+ krb5_keyblock armor_key;
+ krb5_keyblock *strengthen_key;
+ } fast_state;
} krb5_get_init_creds_ctx;
@@ -140,6 +165,19 @@ free_init_creds_ctx(krb5_context context, krb5_init_creds_context ctx)
memset(ctx->password, 0, strlen(ctx->password));
free(ctx->password);
}
+ /*
+ * FAST state (we don't close the armor_ccache because we might have
+ * to destroy it, and how would we know? also, the caller should
+ * take care of cleaning up the armor_ccache).
+ */
+ if (ctx->fast_state.armor_service)
+ krb5_free_principal(context, ctx->fast_state.armor_service);
+ if (ctx->fast_state.armor_crypto)
+ krb5_crypto_destroy(context, ctx->fast_state.armor_crypto);
+ if (ctx->fast_state.strengthen_key)
+ krb5_free_keyblock(context, ctx->fast_state.strengthen_key);
+ krb5_free_keyblock_contents(context, &ctx->fast_state.armor_key);
+
krb5_data_free(&ctx->req_buffer);
krb5_free_cred_contents(context, &ctx->cred);
free_METHOD_DATA(&ctx->md);
@@ -194,13 +232,11 @@ init_cred (krb5_context context,
memset (cred, 0, sizeof(*cred));
if (client)
- krb5_copy_principal(context, client, &cred->client);
- else {
- ret = krb5_get_default_principal (context,
- &cred->client);
- if (ret)
- goto out;
- }
+ ret = krb5_copy_principal(context, client, &cred->client);
+ else
+ ret = krb5_get_default_principal(context, &cred->client);
+ if (ret)
+ goto out;
if (start_time)
cred->times.starttime = now + start_time;
@@ -208,12 +244,15 @@ init_cred (krb5_context context,
if (options->flags & KRB5_GET_INIT_CREDS_OPT_TKT_LIFE)
tmp = options->tkt_life;
else
- tmp = 10 * 60 * 60;
+ tmp = KRB5_TKT_LIFETIME_DEFAULT;
cred->times.endtime = now + tmp;
- if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE) &&
- options->renew_life > 0) {
- cred->times.renew_till = now + options->renew_life;
+ if ((options->flags & KRB5_GET_INIT_CREDS_OPT_RENEW_LIFE)) {
+ if (options->renew_life > 0)
+ tmp = options->renew_life;
+ else
+ tmp = KRB5_TKT_RENEW_LIFETIME_DEFAULT;
+ cred->times.renew_till = now + tmp;
}
return 0;
@@ -251,10 +290,10 @@ report_expiration (krb5_context context,
* @param ctx The krb5_init_creds_context check for expiration.
*/
-static krb5_error_code
-process_last_request(krb5_context context,
- krb5_get_init_creds_opt *options,
- krb5_init_creds_context ctx)
+krb5_error_code
+krb5_process_last_request(krb5_context context,
+ krb5_get_init_creds_opt *options,
+ krb5_init_creds_context ctx)
{
krb5_const_realm realm;
LastReq *lr;
@@ -273,12 +312,9 @@ process_last_request(krb5_context context,
if (options && options->opt_private && options->opt_private->lr.func) {
krb5_last_req_entry **lre;
- lre = calloc(lr->len + 1, sizeof(**lre));
- if (lre == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ lre = calloc(lr->len + 1, sizeof(*lre));
+ if (lre == NULL)
+ return krb5_enomem(context);
for (i = 0; i < lr->len; i++) {
lre[i] = calloc(1, sizeof(*lre[i]));
if (lre[i] == NULL)
@@ -311,7 +347,7 @@ process_last_request(krb5_context context,
for (i = 0; i < lr->len; ++i) {
if (lr->val[i].lr_value <= t) {
- switch (abs(lr->val[i].lr_type)) {
+ switch (lr->val[i].lr_type) {
case LR_PW_EXPTIME :
report_expiration(context, ctx->prompter,
ctx->prompter_data,
@@ -326,6 +362,8 @@ process_last_request(krb5_context context,
lr->val[i].lr_value);
reported = TRUE;
break;
+ default:
+ break;
}
}
}
@@ -440,8 +478,7 @@ get_init_creds_common(krb5_context context,
etypes = malloc((options->etype_list_length + 1)
* sizeof(krb5_enctype));
if (etypes == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
memcpy (etypes, options->etype_list,
@@ -453,8 +490,7 @@ get_init_creds_common(krb5_context context,
pre_auth_types = malloc((options->preauth_list_length + 1)
* sizeof(krb5_preauthtype));
if (pre_auth_types == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
memcpy (pre_auth_types, options->preauth_list,
@@ -494,6 +530,8 @@ change_password (krb5_context context,
char *p;
krb5_get_init_creds_opt *options;
+ heim_assert(prompter != NULL, "unexpected NULL prompter");
+
memset (&cpw_cred, 0, sizeof(cpw_cred));
ret = krb5_get_init_creds_opt_alloc(context, &options);
@@ -502,10 +540,15 @@ change_password (krb5_context context,
krb5_get_init_creds_opt_set_tkt_life (options, 60);
krb5_get_init_creds_opt_set_forwardable (options, FALSE);
krb5_get_init_creds_opt_set_proxiable (options, FALSE);
- if (old_options && old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST)
- krb5_get_init_creds_opt_set_preauth_list (options,
- old_options->preauth_list,
- old_options->preauth_list_length);
+ if (old_options &&
+ (old_options->flags & KRB5_GET_INIT_CREDS_OPT_PREAUTH_LIST))
+ krb5_get_init_creds_opt_set_preauth_list(options,
+ old_options->preauth_list,
+ old_options->preauth_list_length);
+ if (old_options &&
+ (old_options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT))
+ krb5_get_init_creds_opt_set_change_password_prompt(options,
+ old_options->change_password_prompt);
krb5_data_zero (&result_code_string);
krb5_data_zero (&result_string);
@@ -626,14 +669,12 @@ init_as_req (krb5_context context,
a->req_body.kdc_options = opts;
a->req_body.cname = malloc(sizeof(*a->req_body.cname));
if (a->req_body.cname == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
a->req_body.sname = malloc(sizeof(*a->req_body.sname));
if (a->req_body.sname == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
@@ -651,21 +692,23 @@ init_as_req (krb5_context context,
if(creds->times.starttime) {
a->req_body.from = malloc(sizeof(*a->req_body.from));
if (a->req_body.from == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
*a->req_body.from = creds->times.starttime;
}
if(creds->times.endtime){
- ALLOC(a->req_body.till, 1);
- *a->req_body.till = creds->times.endtime;
+ if ((ALLOC(a->req_body.till, 1)) != NULL)
+ *a->req_body.till = creds->times.endtime;
+ else {
+ ret = krb5_enomem(context);
+ goto fail;
+ }
}
if(creds->times.renew_till){
a->req_body.rtime = malloc(sizeof(*a->req_body.rtime));
if (a->req_body.rtime == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
*a->req_body.rtime = creds->times.renew_till;
@@ -688,8 +731,7 @@ init_as_req (krb5_context context,
} else {
a->req_body.addresses = malloc(sizeof(*a->req_body.addresses));
if (a->req_body.addresses == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
@@ -859,7 +901,7 @@ pa_pw_or_afs3_salt(krb5_context context,
heim_octet_string *data)
{
krb5_error_code ret;
- if (paid->etype == ENCTYPE_NULL)
+ if (paid->etype == KRB5_ENCTYPE_NULL)
return NULL;
ret = set_paid(paid, context,
paid->etype,
@@ -1002,7 +1044,7 @@ add_enc_ts_padata(krb5_context context,
if (!enctypes) {
enctypes = context->etypes;
netypes = 0;
- for (ep = enctypes; *ep != ETYPE_NULL; ep++)
+ for (ep = enctypes; *ep != (krb5_enctype)ETYPE_NULL; ep++)
netypes++;
}
@@ -1156,10 +1198,9 @@ process_pa_data_to_md(krb5_context context,
krb5_error_code ret;
ALLOC(*out_md, 1);
- if (*out_md == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*out_md == NULL)
+ return krb5_enomem(context);
+
(*out_md)->len = 0;
(*out_md)->val = NULL;
@@ -1203,8 +1244,10 @@ process_pa_data_to_md(krb5_context context,
unsigned flag;
paid = calloc(1, sizeof(*paid));
+ if (paid == NULL)
+ return krb5_enomem(context);
- paid->etype = ENCTYPE_NULL;
+ paid->etype = KRB5_ENCTYPE_NULL;
ppaid = process_pa_info(context, creds->client, a, paid, in_md);
if (ppaid)
@@ -1215,6 +1258,7 @@ process_pa_data_to_md(krb5_context context,
if (ctx->used_pa_types & flag) {
if (ppaid)
free_paid(context, ppaid);
+ free(paid);
krb5_set_error_message(context, KRB5_GET_IN_TKT_LOOP,
"Already tried ENC-TS-%s, looping",
flag == USED_ENC_TS_INFO ? "info" : "guess");
@@ -1237,6 +1281,12 @@ process_pa_data_to_md(krb5_context context,
pa_data_add_pac_request(context, ctx, *out_md);
+ if ((ctx->fast_state.flags & KRB5_FAST_DISABLED) == 0) {
+ ret = krb5_padata_add(context, *out_md, KRB5_PADATA_REQ_ENC_PA_REP, NULL, 0);
+ if (ret)
+ return ret;
+ }
+
if ((*out_md)->len == 0) {
free(*out_md);
*out_md = NULL;
@@ -1357,10 +1407,8 @@ krb5_init_creds_init(krb5_context context,
*rctx = NULL;
ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ctx == NULL)
+ return krb5_enomem(context);
ret = get_init_creds_common(context, client, start_time, options, ctx);
if (ret) {
@@ -1458,10 +1506,8 @@ krb5_init_creds_set_password(krb5_context context,
}
if (password) {
ctx->password = strdup(password);
- if (ctx->password == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ctx->password == NULL)
+ return krb5_enomem(context);
ctx->keyseed = (void *) ctx->password;
} else {
ctx->keyseed = NULL;
@@ -1526,14 +1572,11 @@ krb5_init_creds_set_keytab(krb5_context context,
krb5_enctype *etypes = NULL;
krb5_error_code ret;
size_t netypes = 0;
- int kvno = 0;
+ int kvno = 0, found = 0;
a = malloc(sizeof(*a));
- if (a == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (a == NULL)
+ return krb5_enomem(context);
a->principal = ctx->cred.client;
a->keytab = keytab;
@@ -1559,6 +1602,8 @@ krb5_init_creds_set_keytab(krb5_context context,
if (!krb5_principal_compare(context, entry.principal, ctx->cred.client))
goto next;
+ found = 1;
+
/* check if we ahve this kvno already */
if (entry.vno > kvno) {
/* remove old list of etype */
@@ -1576,8 +1621,11 @@ krb5_init_creds_set_keytab(krb5_context context,
/* add enctype to supported list */
ptr = realloc(etypes, sizeof(etypes[0]) * (netypes + 2));
- if (ptr == NULL)
- goto next;
+ if (ptr == NULL) {
+ free(etypes);
+ ret = krb5_enomem(context);
+ goto out;
+ }
etypes = ptr;
etypes[netypes] = entry.keyblock.keytype;
@@ -1595,7 +1643,13 @@ krb5_init_creds_set_keytab(krb5_context context,
}
out:
- return 0;
+ if (!found) {
+ if (ret == 0)
+ ret = KRB5_KT_NOTFOUND;
+ _krb5_kt_principal_not_found(context, ret, keytab, ctx->cred.client, 0, 0);
+ }
+
+ return ret;
}
static krb5_error_code KRB5_CALLCONV
@@ -1618,6 +1672,483 @@ krb5_init_creds_set_keyblock(krb5_context context,
return 0;
}
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_init_creds_set_fast_ccache(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_ccache fast_ccache)
+{
+ ctx->fast_state.armor_ccache = fast_ccache;
+ ctx->fast_state.flags |= KRB5_FAST_REQUIRED;
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_init_creds_set_fast_ap_armor_service(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_const_principal armor_service)
+{
+ krb5_error_code ret;
+
+ if (ctx->fast_state.armor_service)
+ krb5_free_principal(context, ctx->fast_state.armor_service);
+ if (armor_service) {
+ ret = krb5_copy_principal(context, armor_service, &ctx->fast_state.armor_service);
+ if (ret)
+ return ret;
+ } else {
+ ctx->fast_state.armor_service = NULL;
+ }
+ ctx->fast_state.flags |= KRB5_FAST_REQUIRED | KRB5_FAST_AP_ARMOR_SERVICE;
+ return 0;
+}
+
+/*
+ * FAST
+ */
+
+static krb5_error_code
+check_fast(krb5_context context, struct fast_state *state)
+{
+ if (state->flags & KRB5_FAST_EXPECTED) {
+ krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
+ "Expected FAST, but no FAST "
+ "was in the response from the KDC");
+ return KRB5KRB_AP_ERR_MODIFIED;
+ }
+ return 0;
+}
+
+
+static krb5_error_code
+fast_unwrap_as_rep(krb5_context context, int32_t nonce,
+ krb5_data *chksumdata,
+ struct fast_state *state, AS_REP *rep)
+{
+ PA_FX_FAST_REPLY fxfastrep;
+ KrbFastResponse fastrep;
+ krb5_error_code ret;
+ PA_DATA *pa = NULL;
+ int idx = 0;
+
+ if (state->armor_crypto == NULL || rep->padata == NULL)
+ return check_fast(context, state);
+
+ /* find PA_FX_FAST_REPLY */
+
+ pa = krb5_find_padata(rep->padata->val, rep->padata->len,
+ KRB5_PADATA_FX_FAST, &idx);
+ if (pa == NULL)
+ return check_fast(context, state);
+
+ memset(&fxfastrep, 0, sizeof(fxfastrep));
+ memset(&fastrep, 0, sizeof(fastrep));
+
+ ret = decode_PA_FX_FAST_REPLY(pa->padata_value.data, pa->padata_value.length, &fxfastrep, NULL);
+ if (ret)
+ return ret;
+
+ if (fxfastrep.element == choice_PA_FX_FAST_REPLY_armored_data) {
+ krb5_data data;
+ ret = krb5_decrypt_EncryptedData(context,
+ state->armor_crypto,
+ KRB5_KU_FAST_REP,
+ &fxfastrep.u.armored_data.enc_fast_rep,
+ &data);
+ if (ret)
+ goto out;
+
+ ret = decode_KrbFastResponse(data.data, data.length, &fastrep, NULL);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+
+ } else {
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto out;
+ }
+
+ free_METHOD_DATA(rep->padata);
+ ret = copy_METHOD_DATA(&fastrep.padata, rep->padata);
+ if (ret)
+ goto out;
+
+ if (fastrep.strengthen_key) {
+ if (state->strengthen_key)
+ krb5_free_keyblock(context, state->strengthen_key);
+
+ ret = krb5_copy_keyblock(context, fastrep.strengthen_key, &state->strengthen_key);
+ if (ret)
+ goto out;
+ }
+
+ if (nonce != fastrep.nonce) {
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto out;
+ }
+ if (fastrep.finished) {
+ PrincipalName cname;
+ krb5_realm crealm = NULL;
+
+ if (chksumdata == NULL) {
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ goto out;
+ }
+
+ ret = krb5_verify_checksum(context, state->armor_crypto,
+ KRB5_KU_FAST_FINISHED,
+ chksumdata->data, chksumdata->length,
+ &fastrep.finished->ticket_checksum);
+ if (ret)
+ goto out;
+
+ /* update */
+ ret = copy_Realm(&fastrep.finished->crealm, &crealm);
+ if (ret)
+ goto out;
+ free_Realm(&rep->crealm);
+ rep->crealm = crealm;
+
+ ret = copy_PrincipalName(&fastrep.finished->cname, &cname);
+ if (ret)
+ goto out;
+ free_PrincipalName(&rep->cname);
+ rep->cname = cname;
+
+#if 0 /* store authenticated checksum as kdc-offset */
+ fastrep->finished.timestamp;
+ fastrep->finished.usec = 0;
+#endif
+
+ } else if (chksumdata) {
+ /* expected fastrep.finish but didn't get it */
+ ret = KRB5KDC_ERR_PREAUTH_FAILED;
+ }
+
+ out:
+ free_PA_FX_FAST_REPLY(&fxfastrep);
+
+ return ret;
+}
+
+static krb5_error_code
+fast_unwrap_error(krb5_context context, struct fast_state *state, KRB_ERROR *error)
+{
+ if (state->armor_crypto == NULL)
+ return check_fast(context, state);
+
+ return 0;
+}
+
+krb5_error_code
+_krb5_make_fast_ap_fxarmor(krb5_context context,
+ krb5_ccache armor_ccache,
+ krb5_data *armor_value,
+ krb5_keyblock *armor_key,
+ krb5_crypto *armor_crypto)
+{
+ krb5_auth_context auth_context = NULL;
+ krb5_creds cred, *credp = NULL;
+ krb5_error_code ret;
+ krb5_data empty;
+
+ krb5_data_zero(&empty);
+
+ memset(&cred, 0, sizeof(cred));
+
+ ret = krb5_auth_con_init (context, &auth_context);
+ if (ret)
+ goto out;
+
+ ret = krb5_cc_get_principal(context, armor_ccache, &cred.client);
+ if (ret)
+ goto out;
+
+ ret = krb5_make_principal(context, &cred.server,
+ cred.client->realm,
+ KRB5_TGS_NAME,
+ cred.client->realm,
+ NULL);
+ if (ret) {
+ krb5_free_principal(context, cred.client);
+ goto out;
+ }
+
+ ret = krb5_get_credentials(context, 0, armor_ccache, &cred, &credp);
+ krb5_free_principal(context, cred.server);
+ krb5_free_principal(context, cred.client);
+ if (ret)
+ goto out;
+
+ ret = krb5_auth_con_add_AuthorizationData(context, auth_context, KRB5_PADATA_FX_FAST_ARMOR, &empty);
+ if (ret)
+ goto out;
+
+ ret = krb5_mk_req_extended(context,
+ &auth_context,
+ AP_OPTS_USE_SUBKEY,
+ NULL,
+ credp,
+ armor_value);
+ krb5_free_creds(context, credp);
+ if (ret)
+ goto out;
+
+ ret = _krb5_fast_armor_key(context,
+ auth_context->local_subkey,
+ auth_context->keyblock,
+ armor_key,
+ armor_crypto);
+ if (ret)
+ goto out;
+
+ out:
+ krb5_auth_con_free(context, auth_context);
+ return ret;
+}
+
+#ifndef WIN32
+static heim_base_once_t armor_service_once = HEIM_BASE_ONCE_INIT;
+static heim_ipc armor_service = NULL;
+
+static void
+fast_armor_init_ipc(void *ctx)
+{
+ heim_ipc *ipc = ctx;
+ heim_ipc_init_context("ANY:org.h5l.armor-service", ipc);
+}
+#endif /* WIN32 */
+
+
+static krb5_error_code
+make_fast_ap_fxarmor(krb5_context context,
+ struct fast_state *state,
+ const char *realm,
+ KrbFastArmor **armor)
+{
+ KrbFastArmor *fxarmor = NULL;
+ krb5_error_code ret;
+
+ if (state->armor_crypto)
+ krb5_crypto_destroy(context, state->armor_crypto);
+ krb5_free_keyblock_contents(context, &state->armor_key);
+
+
+ ALLOC(fxarmor, 1);
+ if (fxarmor == NULL)
+ return krb5_enomem(context);
+
+ if (state->flags & KRB5_FAST_AP_ARMOR_SERVICE) {
+#ifdef WIN32
+ krb5_set_error_message(context, ENOTSUP, "Fast armor IPC service not supportted yet on Windows");
+ ret = ENOTSUP;
+ goto out;
+#else /* WIN32 */
+ KERB_ARMOR_SERVICE_REPLY msg;
+ krb5_data request, reply;
+
+ heim_base_once_f(&armor_service_once, &armor_service, fast_armor_init_ipc);
+ if (armor_service == NULL) {
+ krb5_set_error_message(context, ENOENT, "Failed to open fast armor service");
+ ret = ENOENT;
+ goto out;
+ }
+
+ krb5_data_zero(&reply);
+
+ request.data = rk_UNCONST(realm);
+ request.length = strlen(realm);
+
+ ret = heim_ipc_call(armor_service, &request, &reply, NULL);
+ heim_release(send);
+ if (ret) {
+ krb5_set_error_message(context, ret, "Failed to get armor service credential");
+ goto out;
+ }
+
+ ret = decode_KERB_ARMOR_SERVICE_REPLY(reply.data, reply.length, &msg, NULL);
+ krb5_data_free(&reply);
+ if (ret)
+ goto out;
+
+ ret = copy_KrbFastArmor(fxarmor, &msg.armor);
+ if (ret) {
+ free_KERB_ARMOR_SERVICE_REPLY(&msg);
+ goto out;
+ }
+
+ ret = krb5_copy_keyblock_contents(context, &msg.armor_key, &state->armor_key);
+ free_KERB_ARMOR_SERVICE_REPLY(&msg);
+ if (ret)
+ goto out;
+
+ ret = krb5_crypto_init(context, &state->armor_key, 0, &state->armor_crypto);
+ if (ret)
+ goto out;
+#endif /* WIN32 */
+ } else {
+
+ fxarmor->armor_type = 1;
+
+ ret = _krb5_make_fast_ap_fxarmor(context,
+ state->armor_ccache,
+ &fxarmor->armor_value,
+ &state->armor_key,
+ &state->armor_crypto);
+ if (ret)
+ goto out;
+ }
+
+
+ *armor = fxarmor;
+ fxarmor = NULL;
+ out:
+ if (fxarmor) {
+ free_KrbFastArmor(fxarmor);
+ free(fxarmor);
+ }
+ return ret;
+}
+
+static krb5_error_code
+fast_wrap_req(krb5_context context, struct fast_state *state, KDC_REQ *req)
+{
+ KrbFastArmor *fxarmor = NULL;
+ PA_FX_FAST_REQUEST fxreq;
+ krb5_error_code ret;
+ KrbFastReq fastreq;
+ krb5_data data;
+ size_t size;
+
+ if (state->flags & KRB5_FAST_DISABLED) {
+ _krb5_debug(context, 10, "fast disabled, not doing any fast wrapping");
+ return 0;
+ }
+
+ memset(&fxreq, 0, sizeof(fxreq));
+ memset(&fastreq, 0, sizeof(fastreq));
+ krb5_data_zero(&data);
+
+ if (state->armor_crypto == NULL) {
+ if (state->armor_ccache) {
+ /*
+ * Instead of keeping state in FX_COOKIE in the KDC, we
+ * rebuild a new armor key for every request, because this
+ * is what the MIT KDC expect and RFC6113 is vage about
+ * what the behavior should be.
+ */
+ state->type = choice_PA_FX_FAST_REQUEST_armored_data;
+ } else {
+ return check_fast(context, state);
+ }
+ }
+
+ state->flags |= KRB5_FAST_EXPECTED;
+
+ fastreq.fast_options.hide_client_names = 1;
+
+ ret = copy_KDC_REQ_BODY(&req->req_body, &fastreq.req_body);
+ free_KDC_REQ_BODY(&req->req_body);
+
+ req->req_body.realm = strdup(KRB5_ANON_REALM);
+ if ((ALLOC(req->req_body.cname, 1)) != NULL) {
+ req->req_body.cname->name_type = KRB5_NT_WELLKNOWN;
+ if ((ALLOC(req->req_body.cname->name_string.val, 2)) != NULL) {
+ req->req_body.cname->name_string.len = 2;
+ req->req_body.cname->name_string.val[0] = strdup(KRB5_WELLKNOWN_NAME);
+ req->req_body.cname->name_string.val[1] = strdup(KRB5_ANON_NAME);
+ if (req->req_body.cname->name_string.val[0] == NULL ||
+ req->req_body.cname->name_string.val[1] == NULL)
+ ret = krb5_enomem(context);
+ } else
+ ret = krb5_enomem(context);
+ } else
+ ret = krb5_enomem(context);
+ if ((ALLOC(req->req_body.till, 1)) != NULL)
+ *req->req_body.till = 0;
+ else
+ ret = krb5_enomem(context);
+ if (ret)
+ goto out;
+
+ if (req->padata) {
+ ret = copy_METHOD_DATA(req->padata, &fastreq.padata);
+ free_METHOD_DATA(req->padata);
+ } else {
+ if ((ALLOC(req->padata, 1)) == NULL)
+ ret = krb5_enomem(context);
+ }
+ if (ret)
+ goto out;
+
+ ASN1_MALLOC_ENCODE(KrbFastReq, data.data, data.length, &fastreq, &size, ret);
+ if (ret)
+ goto out;
+ heim_assert(data.length == size, "ASN.1 internal error");
+
+ fxreq.element = state->type;
+
+ if (state->type == choice_PA_FX_FAST_REQUEST_armored_data) {
+ size_t len;
+ void *buf;
+
+ ret = make_fast_ap_fxarmor(context, state, fastreq.req_body.realm, &fxreq.u.armored_data.armor);
+ if (ret)
+ goto out;
+
+ heim_assert(state->armor_crypto != NULL, "FAST armor key missing when FAST started");
+
+ ASN1_MALLOC_ENCODE(KDC_REQ_BODY, buf, len, &req->req_body, &size, ret);
+ if (ret)
+ goto out;
+ heim_assert(len == size, "ASN.1 internal error");
+
+ ret = krb5_create_checksum(context, state->armor_crypto,
+ KRB5_KU_FAST_REQ_CHKSUM, 0,
+ buf, len,
+ &fxreq.u.armored_data.req_checksum);
+ free(buf);
+ if (ret)
+ goto out;
+
+ ret = krb5_encrypt_EncryptedData(context, state->armor_crypto,
+ KRB5_KU_FAST_ENC,
+ data.data,
+ data.length,
+ 0,
+ &fxreq.u.armored_data.enc_fast_req);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+
+ } else {
+ krb5_data_free(&data);
+ heim_assert(false, "unknown FAST type, internal error");
+ }
+
+ ASN1_MALLOC_ENCODE(PA_FX_FAST_REQUEST, data.data, data.length, &fxreq, &size, ret);
+ if (ret)
+ goto out;
+ heim_assert(data.length == size, "ASN.1 internal error");
+
+
+ ret = krb5_padata_add(context, req->padata, KRB5_PADATA_FX_FAST, data.data, data.length);
+ if (ret)
+ goto out;
+ krb5_data_zero(&data);
+
+ out:
+ free_PA_FX_FAST_REQUEST(&fxreq);
+ free_KrbFastReq(&fastreq);
+ if (fxarmor) {
+ free_KrbFastArmor(fxarmor);
+ free(fxarmor);
+ }
+ krb5_data_free(&data);
+
+ return ret;
+}
+
+
/**
* The core loop if krb5_get_init_creds() function family. Create the
* packets and have the caller send them off to the KDC.
@@ -1650,6 +2181,7 @@ krb5_init_creds_step(krb5_context context,
krb5_error_code ret;
size_t len = 0;
size_t size;
+ AS_REQ req2;
krb5_data_zero(out);
@@ -1684,8 +2216,27 @@ krb5_init_creds_step(krb5_context context,
ret = decode_AS_REP(in->data, in->length, &rep.kdc_rep, &size);
if (ret == 0) {
- krb5_keyblock *key = NULL;
unsigned eflags = EXTRACT_TICKET_AS_REQ | EXTRACT_TICKET_TIMESYNC;
+ krb5_data data;
+
+ /*
+ * Unwrap AS-REP
+ */
+ ASN1_MALLOC_ENCODE(Ticket, data.data, data.length,
+ &rep.kdc_rep.ticket, &size, ret);
+ if (ret)
+ goto out;
+ heim_assert(data.length == size, "ASN.1 internal error");
+
+ ret = fast_unwrap_as_rep(context, ctx->nonce, &data,
+ &ctx->fast_state, &rep.kdc_rep);
+ krb5_data_free(&data);
+ if (ret)
+ goto out;
+
+ /*
+ * Now check and extract the ticket
+ */
if (ctx->flags.canonicalize) {
eflags |= EXTRACT_TICKET_ALLOW_SERVER_MISMATCH;
@@ -1695,7 +2246,8 @@ krb5_init_creds_step(krb5_context context,
eflags |= EXTRACT_TICKET_ALLOW_CNAME_MISMATCH;
ret = process_pa_data_to_key(context, ctx, &ctx->cred,
- &ctx->as_req, &rep.kdc_rep, hostinfo, &key);
+ &ctx->as_req, &rep.kdc_rep,
+ hostinfo, &ctx->fast_state.reply_key);
if (ret) {
free_AS_REP(&rep.kdc_rep);
goto out;
@@ -1706,21 +2258,22 @@ krb5_init_creds_step(krb5_context context,
ret = _krb5_extract_ticket(context,
&rep,
&ctx->cred,
- key,
+ ctx->fast_state.reply_key,
NULL,
KRB5_KU_AS_REP_ENC_PART,
NULL,
ctx->nonce,
eflags,
+ &ctx->req_buffer,
NULL,
NULL);
- krb5_free_keyblock(context, key);
-
- *flags = 0;
-
if (ret == 0)
ret = copy_EncKDCRepPart(&rep.enc_part, &ctx->enc_part);
+ krb5_free_keyblock(context, ctx->fast_state.reply_key);
+ ctx->fast_state.reply_key = NULL;
+ *flags = 0;
+
free_AS_REP(&rep.kdc_rep);
free_EncASRepPart(&rep.enc_part);
@@ -1741,6 +2294,17 @@ krb5_init_creds_step(krb5_context context,
goto out;
}
+ /*
+ * Unwrap KRB-ERROR
+ */
+ ret = fast_unwrap_error(context, &ctx->fast_state, &ctx->error);
+ if (ret)
+ goto out;
+
+ /*
+ *
+ */
+
ret = krb5_error_from_rd_error(context, &ctx->error, &ctx->cred);
_krb5_debug(context, 5, "krb5_get_init_creds: KRB-ERROR %d", ret);
@@ -1797,14 +2361,79 @@ krb5_init_creds_step(krb5_context context,
ret = krb5_principal_set_realm(context,
ctx->cred.client,
*ctx->error.crealm);
+ if (ret)
+ goto out;
+
+ if (krb5_principal_is_krbtgt(context, ctx->cred.server)) {
+ ret = krb5_init_creds_set_service(context, ctx, NULL);
+ if (ret)
+ goto out;
+ }
+
+ free_AS_REQ(&ctx->as_req);
+ memset(&ctx->as_req, 0, sizeof(ctx->as_req));
ctx->used_pa_types = 0;
+ } else if (ret == KRB5KDC_ERR_KEY_EXP && ctx->runflags.change_password == 0 && ctx->prompter) {
+ char buf2[1024];
+
+ ctx->runflags.change_password = 1;
+
+ ctx->prompter(context, ctx->prompter_data, NULL, N_("Password has expired", ""), 0, NULL);
+
+
+ /* try to avoid recursion */
+ if (ctx->in_tkt_service != NULL && strcmp(ctx->in_tkt_service, "kadmin/changepw") == 0)
+ goto out;
+
+ /* don't try to change password where then where none */
+ if (ctx->prompter == NULL)
+ goto out;
+
+ ret = change_password(context,
+ ctx->cred.client,
+ ctx->password,
+ buf2,
+ sizeof(buf2),
+ ctx->prompter,
+ ctx->prompter_data,
+ NULL);
+ if (ret)
+ goto out;
+
+ krb5_init_creds_set_password(context, ctx, buf2);
+
+ ctx->used_pa_types = 0;
+ ret = 0;
+
+ } else if (ret == KRB5KDC_ERR_PREAUTH_FAILED) {
+
+ if (ctx->fast_state.flags & KRB5_FAST_DISABLED)
+ goto out;
+ if (ctx->fast_state.flags & (KRB5_FAST_REQUIRED | KRB5_FAST_EXPECTED))
+ goto out;
+
+ _krb5_debug(context, 10, "preauth failed with FAST, "
+ "and told by KD or user, trying w/o FAST");
+
+ ctx->fast_state.flags |= KRB5_FAST_DISABLED;
+ ctx->used_pa_types = 0;
+ ret = 0;
}
if (ret)
goto out;
}
}
+ if (ctx->as_req.req_body.cname == NULL) {
+ ret = init_as_req(context, ctx->flags, &ctx->cred,
+ ctx->addrs, ctx->etypes, &ctx->as_req);
+ if (ret) {
+ free_init_creds_ctx(context, ctx);
+ return ret;
+ }
+ }
+
if (ctx->as_req.padata) {
free_METHOD_DATA(ctx->as_req.padata);
free(ctx->as_req.padata);
@@ -1821,11 +2450,23 @@ krb5_init_creds_step(krb5_context context,
if (ret)
goto out;
+ /*
+ * Wrap with FAST
+ */
+ copy_AS_REQ(&ctx->as_req, &req2);
+
+ ret = fast_wrap_req(context, &ctx->fast_state, &req2);
+ if (ret) {
+ free_AS_REQ(&req2);
+ goto out;
+ }
+
krb5_data_free(&ctx->req_buffer);
ASN1_MALLOC_ENCODE(AS_REQ,
ctx->req_buffer.data, ctx->req_buffer.length,
- &ctx->as_req, &len, ret);
+ &req2, &len, ret);
+ free_AS_REQ(&req2);
if (ret)
goto out;
if(len != ctx->req_buffer.length)
@@ -1877,7 +2518,44 @@ krb5_init_creds_get_error(krb5_context context,
ret = copy_KRB_ERROR(&ctx->error, error);
if (ret)
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ krb5_enomem(context);
+
+ return ret;
+}
+
+/**
+ *
+ * @ingroup krb5_credential
+ */
+
+krb5_error_code
+krb5_init_creds_store(krb5_context context,
+ krb5_init_creds_context ctx,
+ krb5_ccache id)
+{
+ krb5_error_code ret;
+
+ if (ctx->cred.client == NULL) {
+ ret = KRB5KDC_ERR_PREAUTH_REQUIRED;
+ krb5_set_error_message(context, ret, "init creds not completed yet");
+ return ret;
+ }
+
+ ret = krb5_cc_initialize(context, id, ctx->cred.client);
+ if (ret)
+ return ret;
+
+ ret = krb5_cc_store_cred(context, id, &ctx->cred);
+ if (ret)
+ return ret;
+
+ if (ctx->cred.flags.b.enc_pa_rep) {
+ krb5_data data = { 3, rk_UNCONST("yes") };
+ ret = krb5_cc_set_config(context, id, ctx->cred.server,
+ "fast_avail", &data);
+ if (ret)
+ return ret;
+ }
return ret;
}
@@ -1968,7 +2646,7 @@ krb5_get_init_creds_password(krb5_context context,
krb5_get_init_creds_opt *options)
{
krb5_init_creds_context ctx;
- char buf[BUFSIZ];
+ char buf[BUFSIZ], buf2[BUFSIZ];
krb5_error_code ret;
int chpw = 0;
@@ -1984,11 +2662,19 @@ krb5_get_init_creds_password(krb5_context context,
if (prompter != NULL && ctx->password == NULL && password == NULL) {
krb5_prompt prompt;
krb5_data password_data;
- char *p, *q;
+ char *p, *q = NULL;
+ int aret;
- krb5_unparse_name (context, client, &p);
- asprintf (&q, "%s's Password: ", p);
+ ret = krb5_unparse_name(context, client, &p);
+ if (ret)
+ goto out;
+
+ aret = asprintf(&q, "%s's Password: ", p);
free (p);
+ if (aret == -1 || q == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
prompt.prompt = q;
password_data.data = buf;
password_data.length = sizeof(buf);
@@ -2016,12 +2702,10 @@ krb5_get_init_creds_password(krb5_context context,
ret = krb5_init_creds_get(context, ctx);
if (ret == 0)
- process_last_request(context, options, ctx);
+ krb5_process_last_request(context, options, ctx);
if (ret == KRB5KDC_ERR_KEY_EXPIRED && chpw == 0) {
- char buf2[1024];
-
/* try to avoid recursion */
if (in_tkt_service != NULL && strcmp(in_tkt_service, "kadmin/changepw") == 0)
goto out;
@@ -2030,16 +2714,21 @@ krb5_get_init_creds_password(krb5_context context,
if (prompter == NULL)
goto out;
+ if ((options->flags & KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT) &&
+ !options->change_password_prompt)
+ goto out;
+
ret = change_password (context,
client,
ctx->password,
buf2,
- sizeof(buf),
+ sizeof(buf2),
prompter,
data,
options);
if (ret)
goto out;
+ password = buf2;
chpw = 1;
krb5_init_creds_free(context, ctx);
goto again;
@@ -2053,6 +2742,7 @@ krb5_get_init_creds_password(krb5_context context,
krb5_init_creds_free(context, ctx);
memset(buf, 0, sizeof(buf));
+ memset(buf2, 0, sizeof(buf2));
return ret;
}
@@ -2091,7 +2781,7 @@ krb5_get_init_creds_keyblock(krb5_context context,
ret = krb5_init_creds_get(context, ctx);
if (ret == 0)
- process_last_request(context, options, ctx);
+ krb5_process_last_request(context, options, ctx);
out:
if (ret == 0)
@@ -2119,10 +2809,22 @@ krb5_get_init_creds_keytab(krb5_context context,
krb5_get_init_creds_opt *options)
{
krb5_init_creds_context ctx;
+ krb5_keytab_entry ktent;
krb5_error_code ret;
+ memset(&ktent, 0, sizeof(ktent));
memset(creds, 0, sizeof(*creds));
+ if (strcmp(client->realm, "") == 0) {
+ /*
+ * Referral realm. We have a keytab, so pick a realm by
+ * matching in the keytab.
+ */
+ ret = krb5_kt_get_entry(context, keytab, client, 0, 0, &ktent);
+ if (ret == 0)
+ client = ktent.principal;
+ }
+
ret = krb5_init_creds_init(context, client, NULL, NULL, start_time, options, &ctx);
if (ret)
goto out;
@@ -2137,9 +2839,10 @@ krb5_get_init_creds_keytab(krb5_context context,
ret = krb5_init_creds_get(context, ctx);
if (ret == 0)
- process_last_request(context, options, ctx);
+ krb5_process_last_request(context, options, ctx);
out:
+ krb5_kt_free_entry(context, &ktent);
if (ret == 0)
krb5_init_creds_get_creds(context, ctx, creds);
diff --git a/lib/krb5/kcm.c b/lib/krb5/kcm.c
index 5a28b5138b5f..557dc5fe419c 100644
--- a/lib/krb5/kcm.c
+++ b/lib/krb5/kcm.c
@@ -553,9 +553,7 @@ kcm_get_first (krb5_context context,
c = calloc(1, sizeof(*c));
if (c == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
return ret;
}
@@ -577,9 +575,7 @@ kcm_get_first (krb5_context context,
if (ptr == NULL) {
free(c->uuids);
free(c);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
c->uuids = ptr;
@@ -788,9 +784,7 @@ kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
c = calloc(1, sizeof(*c));
if (c == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
@@ -819,9 +813,7 @@ kcm_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
ptr = realloc(c->uuids, sizeof(c->uuids[0]) * (c->length + 1));
if (ptr == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
c->uuids = ptr;
@@ -963,6 +955,7 @@ kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops,
krb5_storage *request, *response;
krb5_data response_data;
char *name;
+ int aret;
*str = NULL;
@@ -981,9 +974,9 @@ kcm_get_default_name(krb5_context context, const krb5_cc_ops *ops,
if (ret)
return ret;
- asprintf(str, "%s:%s", ops->prefix, name);
+ aret = asprintf(str, "%s:%s", ops->prefix, name);
free(name);
- if (str == NULL)
+ if (aret == -1 || str == NULL)
return ENOMEM;
return 0;
diff --git a/lib/krb5/kerberos.8 b/lib/krb5/kerberos.8
index 94d49955180b..d54ced53ed8c 100644
--- a/lib/krb5/kerberos.8
+++ b/lib/krb5/kerberos.8
@@ -31,7 +31,7 @@
.\"
.\" $Id$
.\"
-.Dd September 1, 2000
+.Dd Jun 27, 2013
.Dt KERBEROS 8
.Os HEIMDAL
.Sh NAME
@@ -73,7 +73,7 @@ without giving your password.
.Pp
For more information on how Kerberos works, and other general Kerberos
questions see the Kerberos FAQ at
-.Pa http://www.nrl.navy.mil/CCS/people/kenh/kerberos-faq.html .
+.Lk http://www.cmf.nrl.navy.mil/krb/kerberos-faq.html .
.Pp
For setup instructions see the Heimdal Texinfo manual.
.Sh SEE ALSO
@@ -82,7 +82,12 @@ For setup instructions see the Heimdal Texinfo manual.
.Xr kinit 1 ,
.Xr klist 1 ,
.Xr kpasswd 1 ,
-.Xr telnet 1
+.Xr telnet 1 ,
+.Xr krb5 3 ,
+.Xr krb5.conf 5 ,
+.Xr kadmin 1 ,
+.Xr kdc 8 ,
+.Xr ktutil 1
.Sh HISTORY
The Kerberos authentication system was developed in the late 1980's as
part of the Athena Project at the Massachusetts Institute of
diff --git a/lib/krb5/kerberos.cat8 b/lib/krb5/kerberos.cat8
index a6e1fa3fe370..1ea1675a46dd 100644
--- a/lib/krb5/kerberos.cat8
+++ b/lib/krb5/kerberos.cat8
@@ -27,12 +27,13 @@ DDEESSCCRRIIPPTTIIOONN
For more information on how Kerberos works, and other general Kerberos
questions see the Kerberos FAQ at
- _h_t_t_p_:_/_/_w_w_w_._n_r_l_._n_a_v_y_._m_i_l_/_C_C_S_/_p_e_o_p_l_e_/_k_e_n_h_/_k_e_r_b_e_r_o_s_-_f_a_q_._h_t_m_l.
+ hhttttpp::////wwwwww..ccmmff..nnrrll..nnaavvyy..mmiill//kkrrbb//kkeerrbbeerrooss--ffaaqq..hhttmmll.
For setup instructions see the Heimdal Texinfo manual.
SSEEEE AALLSSOO
- ftp(1), kdestroy(1), kinit(1), klist(1), kpasswd(1), telnet(1)
+ ftp(1), kdestroy(1), kinit(1), klist(1), kpasswd(1), telnet(1), krb5(3),
+ krb5.conf(5), kadmin(1), kdc(8), ktutil(1)
HHIISSTTOORRYY
The Kerberos authentication system was developed in the late 1980's as
@@ -54,4 +55,4 @@ HHIISSTTOORRYY
has been in development at the Royal Institute of Technology in Stock-
holm, Sweden, since about 1997.
-HEIMDAL September 1, 2000 HEIMDAL
+HEIMDAL Jun 27, 2013 HEIMDAL
diff --git a/lib/krb5/keyblock.c b/lib/krb5/keyblock.c
index 9ba9c4b290da..abca3ee059f6 100644
--- a/lib/krb5/keyblock.c
+++ b/lib/krb5/keyblock.c
@@ -65,7 +65,7 @@ krb5_free_keyblock_contents(krb5_context context,
if (keyblock->keyvalue.data != NULL)
memset(keyblock->keyvalue.data, 0, keyblock->keyvalue.length);
krb5_data_free (&keyblock->keyvalue);
- keyblock->keytype = ENCTYPE_NULL;
+ keyblock->keytype = KRB5_ENCTYPE_NULL;
}
}
@@ -135,10 +135,8 @@ krb5_copy_keyblock (krb5_context context,
*to = NULL;
k = calloc (1, sizeof(*k));
- if (k == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- return ENOMEM;
- }
+ if (k == NULL)
+ return krb5_enomem(context);
ret = krb5_copy_keyblock_contents (context, inblock, k);
if (ret) {
diff --git a/lib/krb5/keytab.c b/lib/krb5/keytab.c
index 8ca515f2133d..ca37e292a4b3 100644
--- a/lib/krb5/keytab.c
+++ b/lib/krb5/keytab.c
@@ -153,11 +153,8 @@ krb5_kt_register(krb5_context context,
tmp = realloc(context->kt_types,
(context->num_kt_types + 1) * sizeof(*context->kt_types));
- if(tmp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(tmp == NULL)
+ return krb5_enomem(context);
memcpy(&tmp[context->num_kt_types], ops,
sizeof(tmp[context->num_kt_types]));
context->kt_types = tmp;
@@ -173,7 +170,7 @@ keytab_name(const char *name, const char **type, size_t *type_len)
residual = strchr(name, ':');
if (residual == NULL ||
- name[0] == '/'
+ ISPATHSEP(name[0])
#ifdef _WIN32
/* Avoid treating <drive>:<path> as a keytab type
* specification */
@@ -232,10 +229,8 @@ krb5_kt_resolve(krb5_context context,
}
k = malloc (sizeof(*k));
- if (k == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (k == NULL)
+ return krb5_enomem(context);
memcpy(k, &context->kt_types[i], sizeof(*k));
k->data = NULL;
ret = (*k->resolve)(context, residual, k);
@@ -247,6 +242,21 @@ krb5_kt_resolve(krb5_context context,
return ret;
}
+/*
+ * Default ktname from context with possible environment
+ * override
+ */
+static const char *default_ktname(krb5_context context)
+{
+ const char *tmp = NULL;
+
+ if(!issuid())
+ tmp = getenv("KRB5_KTNAME");
+ if(tmp != NULL)
+ return tmp;
+ return context->default_keytab;
+}
+
/**
* copy the name of the default keytab into `name'.
*
@@ -262,7 +272,7 @@ krb5_kt_resolve(krb5_context context,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_default_name(krb5_context context, char *name, size_t namesize)
{
- if (strlcpy (name, context->default_keytab, namesize) >= namesize) {
+ if (strlcpy (name, default_ktname(context), namesize) >= namesize) {
krb5_clear_error_message (context);
return KRB5_CONFIG_NOTENUFSPACE;
}
@@ -284,17 +294,18 @@ krb5_kt_default_name(krb5_context context, char *name, size_t namesize)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize)
{
- const char *kt = NULL;
+ const char *kt;
+
if(context->default_keytab_modify == NULL) {
- if(strncasecmp(context->default_keytab, "ANY:", 4) != 0)
- kt = context->default_keytab;
- else {
- size_t len = strcspn(context->default_keytab + 4, ",");
- if(len >= namesize) {
+ kt = default_ktname(context);
+
+ if (strncasecmp(kt, "ANY:", 4) == 0) {
+ size_t len = strcspn(kt + 4, ",");
+ if (len >= namesize) {
krb5_clear_error_message(context);
return KRB5_CONFIG_NOTENUFSPACE;
}
- strlcpy(name, context->default_keytab + 4, namesize);
+ strlcpy(name, kt + 4, namesize);
name[len] = '\0';
return 0;
}
@@ -321,7 +332,7 @@ krb5_kt_default_modify_name(krb5_context context, char *name, size_t namesize)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_default(krb5_context context, krb5_keytab *id)
{
- return krb5_kt_resolve (context, context->default_keytab, id);
+ return krb5_kt_resolve (context, default_ktname(context), id);
}
/**
@@ -449,9 +460,8 @@ krb5_kt_get_full_name(krb5_context context,
return ret;
if (asprintf(str, "%s:%s", type, name) == -1) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
*str = NULL;
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
@@ -509,7 +519,7 @@ krb5_kt_destroy(krb5_context context,
*/
static krb5_boolean
-compare_aliseses(krb5_context context,
+compare_aliases(krb5_context context,
krb5_keytab_entry *entry,
krb5_const_principal principal)
{
@@ -545,18 +555,24 @@ krb5_kt_compare(krb5_context context,
krb5_kvno vno,
krb5_enctype enctype)
{
- if(principal != NULL &&
- !(krb5_principal_compare(context, entry->principal, principal) ||
- compare_aliseses(context, entry, principal)))
+ /* krb5_principal_compare() does not special-case the referral realm */
+ if (principal != NULL && strcmp(principal->realm, "") == 0 &&
+ !(krb5_principal_compare_any_realm(context, entry->principal, principal) ||
+ compare_aliases(context, entry, principal))) {
+ return FALSE;
+ } else if (principal != NULL && strcmp(principal->realm, "") != 0 &&
+ !(krb5_principal_compare(context, entry->principal, principal) ||
+ compare_aliases(context, entry, principal))) {
return FALSE;
- if(vno && vno != entry->vno)
+ }
+ if (vno && vno != entry->vno)
return FALSE;
- if(enctype && enctype != entry->keyblock.keytype)
+ if (enctype && enctype != entry->keyblock.keytype)
return FALSE;
return TRUE;
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kt_principal_not_found(krb5_context context,
krb5_error_code ret,
krb5_keytab id,
@@ -569,7 +585,8 @@ _krb5_kt_principal_not_found(krb5_context context,
krb5_unparse_name_fixed (context, principal, princ, sizeof(princ));
krb5_kt_get_full_name (context, id, &kt_name);
- krb5_enctype_to_string(context, enctype, &enctype_str);
+ if (enctype)
+ krb5_enctype_to_string(context, enctype, &enctype_str);
if (kvno)
snprintf(kvno_str, sizeof(kvno_str), "(kvno %d)", kvno);
@@ -584,34 +601,18 @@ _krb5_kt_principal_not_found(krb5_context context,
kt_name ? kt_name : "unknown keytab",
enctype_str ? enctype_str : "unknown enctype");
free(kt_name);
- free(enctype_str);
+ if (enctype_str)
+ free(enctype_str);
return ret;
}
-
-/**
- * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
- * from the keytab `id'. Matching is done like krb5_kt_compare().
- *
- * @param context a Keberos context.
- * @param id a keytab.
- * @param principal principal to match, NULL matches all principals.
- * @param kvno key version to match, 0 matches all key version numbers.
- * @param enctype encryption type to match, 0 matches all encryption types.
- * @param entry the returned entry, free with krb5_kt_free_entry().
- *
- * @return Return an error code or 0, see krb5_get_error_message().
- *
- * @ingroup krb5_keytab
- */
-
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_kt_get_entry(krb5_context context,
- krb5_keytab id,
- krb5_const_principal principal,
- krb5_kvno kvno,
- krb5_enctype enctype,
- krb5_keytab_entry *entry)
+static krb5_error_code
+krb5_kt_get_entry_wrapped(krb5_context context,
+ krb5_keytab id,
+ krb5_const_principal principal,
+ krb5_kvno kvno,
+ krb5_enctype enctype,
+ krb5_keytab_entry *entry)
{
krb5_keytab_entry tmp;
krb5_error_code ret;
@@ -655,6 +656,63 @@ krb5_kt_get_entry(krb5_context context,
}
/**
+ * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
+ * from the keytab `id'. Matching is done like krb5_kt_compare().
+ *
+ * @param context a Keberos context.
+ * @param id a keytab.
+ * @param principal principal to match, NULL matches all principals.
+ * @param kvno key version to match, 0 matches all key version numbers.
+ * @param enctype encryption type to match, 0 matches all encryption types.
+ * @param entry the returned entry, free with krb5_kt_free_entry().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_kt_get_entry(krb5_context context,
+ krb5_keytab id,
+ krb5_const_principal principal,
+ krb5_kvno kvno,
+ krb5_enctype enctype,
+ krb5_keytab_entry *entry)
+{
+ krb5_error_code ret;
+ krb5_const_principal try_princ;
+ krb5_name_canon_iterator name_canon_iter;
+
+ if (!principal)
+ return krb5_kt_get_entry_wrapped(context, id, principal, kvno, enctype,
+ entry);
+
+ ret = krb5_name_canon_iterator_start(context, principal, &name_canon_iter);
+ if (ret)
+ return ret;
+
+ do {
+ ret = krb5_name_canon_iterate(context, &name_canon_iter, &try_princ,
+ NULL);
+ if (ret)
+ break;
+ if (try_princ == NULL) {
+ ret = KRB5_KT_NOTFOUND;
+ continue;
+ }
+ ret = krb5_kt_get_entry_wrapped(context, id, try_princ, kvno,
+ enctype, entry);
+ } while (ret == KRB5_KT_NOTFOUND && name_canon_iter);
+
+ if (ret != KRB5_KT_NOTFOUND)
+ krb5_set_error_message(context, ret,
+ N_("Name canon failed while searching keytab",
+ ""));
+ krb5_free_name_canon_iterator(context, name_canon_iter);
+ return ret;
+}
+
+/**
* Copy the contents of `in' into `out'.
*
* @param context a Keberos context.
@@ -860,7 +918,7 @@ krb5_kt_remove_entry(krb5_context context,
* @ingroup krb5_keytab
*/
-KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_have_content(krb5_context context,
krb5_keytab id)
{
diff --git a/lib/krb5/keytab_any.c b/lib/krb5/keytab_any.c
index d5ac4883db10..eea5d64bfb60 100644
--- a/lib/krb5/keytab_any.c
+++ b/lib/krb5/keytab_any.c
@@ -63,15 +63,14 @@ any_resolve(krb5_context context, const char *name, krb5_keytab id)
while (strsep_copy(&name, ",", buf, sizeof(buf)) != -1) {
a = calloc(1, sizeof(*a));
if (a == NULL) {
- ret = ENOMEM;
+ ret = krb5_enomem(context);
goto fail;
}
if (a0 == NULL) {
a0 = a;
a->name = strdup(buf);
if (a->name == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto fail;
}
} else
@@ -131,10 +130,8 @@ any_start_seq_get(krb5_context context,
krb5_error_code ret;
c->data = malloc (sizeof(struct any_cursor_extra_data));
- if(c->data == NULL){
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(c->data == NULL)
+ return krb5_enomem(context);
ed = (struct any_cursor_extra_data *)c->data;
for (ed->a = a; ed->a != NULL; ed->a = ed->a->next) {
ret = krb5_kt_start_seq_get(context, ed->a->kt, &ed->cursor);
@@ -257,5 +254,7 @@ const krb5_kt_ops krb5_any_ops = {
any_next_entry,
any_end_seq_get,
any_add_entry,
- any_remove_entry
+ any_remove_entry,
+ NULL,
+ 0
};
diff --git a/lib/krb5/keytab_file.c b/lib/krb5/keytab_file.c
index ccaf62fcb4c2..14b0f6c5987a 100644
--- a/lib/krb5/keytab_file.c
+++ b/lib/krb5/keytab_file.c
@@ -59,10 +59,8 @@ krb5_kt_ret_data(krb5_context context,
return ret;
data->length = size;
data->data = malloc(size);
- if (data->data == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (data->data == NULL)
+ return krb5_enomem(context);
ret = krb5_storage_read(sp, data->data, size);
if(ret != size)
return (ret < 0)? errno : KRB5_KT_END;
@@ -80,10 +78,8 @@ krb5_kt_ret_string(krb5_context context,
if(ret)
return ret;
*data = malloc(size + 1);
- if (*data == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*data == NULL)
+ return krb5_enomem(context);
ret = krb5_storage_read(sp, *data, size);
(*data)[size] = '\0';
if(ret != size)
@@ -188,11 +184,8 @@ krb5_kt_ret_principal(krb5_context context,
int16_t len;
ALLOC(p, 1);
- if(p == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(p == NULL)
+ return krb5_enomem(context);
ret = krb5_ret_int16(sp, &len);
if(ret) {
@@ -221,8 +214,7 @@ krb5_kt_ret_principal(krb5_context context,
}
p->name.name_string.val = calloc(len, sizeof(*p->name.name_string.val));
if(p->name.name_string.val == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
p->name.name_string.len = len;
@@ -292,15 +284,12 @@ fkt_resolve(krb5_context context, const char *name, krb5_keytab id)
struct fkt_data *d;
d = malloc(sizeof(*d));
- if(d == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(d == NULL)
+ return krb5_enomem(context);
d->filename = strdup(name);
if(d->filename == NULL) {
free(d);
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
d->flags = 0;
id->data = d;
@@ -397,9 +386,7 @@ fkt_start_seq_get_int(krb5_context context,
if (c->sp == NULL) {
_krb5_xunlock(context, c->fd);
close(c->fd);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
ret = krb5_ret_int8(c->sp, &pvno);
@@ -499,6 +486,8 @@ loop:
if(start) *start = pos;
if(end) *end = pos + 4 + len;
out:
+ if (ret)
+ krb5_kt_free_entry(context, entry);
krb5_storage_seek(cursor->sp, pos + 4 + len, SEEK_SET);
return ret;
}
@@ -622,9 +611,7 @@ fkt_add_entry(krb5_context context,
krb5_storage *emem;
emem = krb5_storage_emem();
if(emem == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
ret = krb5_kt_store_principal(context, emem, entry->principal);
@@ -776,7 +763,9 @@ const krb5_kt_ops krb5_fkt_ops = {
fkt_next_entry,
fkt_end_seq_get,
fkt_add_entry,
- fkt_remove_entry
+ fkt_remove_entry,
+ NULL,
+ 0
};
const krb5_kt_ops krb5_wrfkt_ops = {
@@ -790,7 +779,9 @@ const krb5_kt_ops krb5_wrfkt_ops = {
fkt_next_entry,
fkt_end_seq_get,
fkt_add_entry,
- fkt_remove_entry
+ fkt_remove_entry,
+ NULL,
+ 0
};
const krb5_kt_ops krb5_javakt_ops = {
@@ -804,5 +795,7 @@ const krb5_kt_ops krb5_javakt_ops = {
fkt_next_entry,
fkt_end_seq_get,
fkt_add_entry,
- fkt_remove_entry
+ fkt_remove_entry,
+ NULL,
+ 0
};
diff --git a/lib/krb5/keytab_keyfile.c b/lib/krb5/keytab_keyfile.c
index 120083215542..cb865a794c08 100644
--- a/lib/krb5/keytab_keyfile.c
+++ b/lib/krb5/keytab_keyfile.c
@@ -89,11 +89,8 @@ get_cell_and_realm (krb5_context context, struct akf_data *d)
fclose(f);
d->cell = strdup (buf);
- if (d->cell == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (d->cell == NULL)
+ return krb5_enomem(context);
f = fopen (AFS_SERVERMAGICKRBCONF, "r");
if (f != NULL) {
@@ -117,9 +114,7 @@ get_cell_and_realm (krb5_context context, struct akf_data *d)
if (d->realm == NULL) {
free (d->cell);
d->cell = NULL;
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
return 0;
}
@@ -132,13 +127,10 @@ static krb5_error_code KRB5_CALLCONV
akf_resolve(krb5_context context, const char *name, krb5_keytab id)
{
int ret;
- struct akf_data *d = malloc(sizeof (struct akf_data));
+ struct akf_data *d = calloc(1, sizeof (struct akf_data));
- if (d == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (d == NULL)
+ return krb5_enomem(context);
d->num_entries = 0;
ret = get_cell_and_realm (context, d);
@@ -151,9 +143,7 @@ akf_resolve(krb5_context context, const char *name, krb5_keytab id)
free (d->cell);
free (d->realm);
free (d);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
id->data = d;
@@ -271,9 +261,7 @@ akf_next_entry(krb5_context context,
entry->keyblock.keyvalue.data = malloc (8);
if (entry->keyblock.keyvalue.data == NULL) {
krb5_free_principal (context, entry->principal);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- ret = ENOMEM;
+ ret = krb5_enomem(context);
goto out;
}
@@ -348,9 +336,7 @@ akf_add_entry(krb5_context context,
sp = krb5_storage_from_fd(fd);
if(sp == NULL) {
close(fd);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
if (created)
len = 0;
@@ -462,7 +448,9 @@ const krb5_kt_ops krb5_akf_ops = {
akf_next_entry,
akf_end_seq_get,
akf_add_entry,
- NULL /* remove */
+ NULL, /* remove */
+ NULL,
+ 0
};
#endif /* HEIMDAL_SMALLER */
diff --git a/lib/krb5/keytab_memory.c b/lib/krb5/keytab_memory.c
index 0ee684d36382..87953a68e99b 100644
--- a/lib/krb5/keytab_memory.c
+++ b/lib/krb5/keytab_memory.c
@@ -73,17 +73,13 @@ mkt_resolve(krb5_context context, const char *name, krb5_keytab id)
d = calloc(1, sizeof(*d));
if(d == NULL) {
HEIMDAL_MUTEX_unlock(&mkt_mutex);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
d->name = strdup(name);
if (d->name == NULL) {
HEIMDAL_MUTEX_unlock(&mkt_mutex);
free(d);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
d->entries = NULL;
d->num_entries = 0;
@@ -175,11 +171,8 @@ mkt_add_entry(krb5_context context,
struct mkt_data *d = id->data;
krb5_keytab_entry *tmp;
tmp = realloc(d->entries, (d->num_entries + 1) * sizeof(*d->entries));
- if(tmp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (tmp == NULL)
+ return krb5_enomem(context);
d->entries = tmp;
return krb5_kt_copy_entry_contents(context, entry,
&d->entries[d->num_entries++]);
@@ -232,5 +225,7 @@ const krb5_kt_ops krb5_mkt_ops = {
mkt_next_entry,
mkt_end_seq_get,
mkt_add_entry,
- mkt_remove_entry
+ mkt_remove_entry,
+ NULL,
+ 0
};
diff --git a/lib/krb5/krb5-plugin.7 b/lib/krb5/krb5-plugin.7
new file mode 100644
index 000000000000..49204d2f6b46
--- /dev/null
+++ b/lib/krb5/krb5-plugin.7
@@ -0,0 +1,245 @@
+.\" Copyright (c) 1999 - 2005 Kungliga Tekniska Högskolan
+.\" (Royal Institute of Technology, Stockholm, Sweden).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" 3. Neither the name of the Institute nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd December 21, 2011
+.Dt KRB5-PLUGIN 7
+.Os HEIMDAL
+.Sh NAME
+.Nm krb5-plugin
+.Nd plugin interface for Heimdal
+.Sh SYNOPSIS
+.In krb5.h
+.In krb5/an2ln_plugin.h
+.In krb5/ccache_plugin.h
+.In krb5/db_plugin.h
+.In krb5/kuserok_plugin.h
+.In krb5/locate_plugin.h
+.In krb5/send_to_kdc_plugin.h
+.Sh DESCRIPTION
+Heimdal has a plugin interface. Plugins may be statically linked into
+Heimdal and registered via the
+.Xr krb5_plugin_register 3
+function, or they may be dynamically loaded from shared objects present
+in the Heimdal plugins directories.
+.Pp
+Plugins consist of a C struct whose struct name is given in the
+associated header file, such as, for example,
+.Va krb5plugin_kuserok_ftable
+and a pointer to which is either registered via
+.Xr krb5_plugin_register 3
+or found in a shared object via a symbol lookup for the symbol name
+defined in the associated header file (e.g., "kuserok" for the
+plugin for
+.Xr krb5_kuserok 3
+).
+.Pp
+The plugin structs for all plugin types always begin with the same three
+common fields:
+.Bl -enum -compact
+.It
+.Va minor_version
+, an int. Plugin minor versions are defined in each plugin type's
+associated header file.
+.It
+.Va init
+, a pointer to a function with two arguments, a krb5_context and a
+void **, returning a krb5_error_code. This function will be called to
+initialize a plugin-specific context in the form of a void * that will
+be output through the init function's second argument.
+.It
+.Va fini
+, a pointer to a function of one argument, a void *, consisting of the
+plugin's context to be destroyed, and returning void.
+.El
+.Pp
+Each plugin type must add zero or more fields to this struct following
+the above three. Plugins are typically invoked in no particular order
+until one succeeds or fails, or all return a special return value such
+as KRB5_PLUGIN_NO_HANDLE to indicate that the plugin was not applicable.
+Most plugin types obtain deterministic plugin behavior in spite of the
+non-deterministic invocation order by, for example, invoking all plugins
+for each "rule" and passing the rule to each plugin with the expectation
+that just one plugin will match any given rule.
+.Pp
+There is a database plugin system intended for many of the uses of
+databases in Heimdal. The plugin is expected to call
+.Xr heim_db_register 3
+from its
+.Va init
+entry point to register a DB type. The DB plugin's
+.Va fini
+function must do nothing, and the plugin must not provide any other
+entry points.
+.Pp
+The krb5_kuserok plugin adds a single field to its struct: a pointer to
+a function that implements kuserok functionality with the following
+form:
+.Bd -literal -offset indent
+static krb5_error_code
+kuserok(void *plug_ctx, krb5_context context, const char *rule,
+ unsigned int flags, const char *k5login_dir,
+ const char *luser, krb5_const_principal principal,
+ krb5_boolean *result)
+.Ed
+.Pp
+The
+.Va luser
+,
+.Va principal
+and
+.Va result
+arguments are self-explanatory (see
+.Xr krb5_kuserok 3
+). The
+.Va plug_ctx
+argument is the context output by the plugin's init function. The
+.Va rule
+argument is a kuserok rule from the krb5.conf file; each plugin is invoked once
+for each rule until all plugins fail or one succeeds. The
+.Va k5login_dir
+argument provides an alternative k5login file location, if not NULL.
+The
+.Va flags
+argument indicates whether the plugin may call
+.Xr krb5_aname_to_localname 3
+(KUSEROK_ANAME_TO_LNAME_OK), and whether k5login databases are expected to be
+authoritative (KUSEROK_K5LOGIN_IS_AUTHORITATIVE).
+.Pp
+The plugin for
+.Xr krb5_aname_to_localname 3
+is named "an2ln" and has a single extra field for the plugin struct:
+.Bd -literal -offset indent
+typedef krb5_error_code (*set_result_f)(void *, const char *);
+
+static krb5_error_code
+an2ln(void *plug_ctx, krb5_context context, const char *rule,
+ krb5_const_principal aname, set_result_f set_res_f, void *set_res_ctx)
+.Ed
+.Pp
+The arguments for the
+.Va an2ln
+plugin are similar to those of the kuserok plugin, but the result, being
+a string, is set by calling the
+.Va set_res_f
+function argument with the
+.Va set_res_ctx
+and result string as arguments. The
+.Va set_res_f
+function will make a copy of the string.
+.Sh FILES
+.Bl -tag -compact
+.It Pa libdir/plugin/krb5/*
+Shared objects containing plugins for Heimdal.
+.El
+.Sh EXAMPLES
+.Pp
+An example an2ln plugin that maps principals to a constant "nouser"
+follows:
+.Pp
+.Bd -literal -offset indent
+#include <krb5/an2ln_plugin.h>
+
+static krb5_error_code
+nouser_plug_init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
+ return 0;
+}
+
+static void nouser_plug_fini(void *ctx) { }
+
+static krb5_error_code
+nouser_plug_an2ln(void *plug_ctx, krb5_context context,
+ const char *rule,
+ krb5_const_principal aname,
+ set_result_f set_res_f, void *set_res_ctx)
+{
+ krb5_error_code ret;
+
+ if (strcmp(rule, "NOUSER") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ ret = set_res_f(set_res_ctx, "nouser");
+
+ return ret;
+}
+
+krb5plugin_an2ln_ftable an2ln = {
+ KRB5_PLUGIN_AN2LN_VERSION_0,
+ nouser_plug_init,
+ nouser_plug_fini,
+ nouser_plug_an2ln,
+};
+.Ed
+.Pp
+An example kuserok plugin that rejects all requests follows. (Note that
+there exists a built-in plugin with this functionality; see
+.Xr krb5_kuserok 3
+).
+.Pp
+.Bd -literal -offset indent
+#include <krb5/kuserok_plugin.h>
+
+static krb5_error_code
+reject_plug_init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
+ return 0;
+}
+
+static void reject_plug_fini(void *ctx) { }
+
+static krb5_error_code
+reject_plug_kuserok(void *plug_ctx, krb5_context context, const char *rule,
+ unsigned int flags, const char *k5login_dir,
+ const char *luser, krb5_const_principal principal,
+ krb5_boolean *result)
+{
+ if (strcmp(rule, "REJECT") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ *result = FALSE;
+ return 0;
+}
+
+krb5plugin_kuserok_ftable kuserok = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ reject_plug_init,
+ reject_plug_fini,
+ reject_plug_kuserok,
+};
+.Ed
+.Sh SEE ALSO
+.Xr krb5_plugin_register 3
+.Xr krb5_kuserok 3
+.Xr krb5_aname_to_localname 3
diff --git a/lib/krb5/krb5-plugin.cat7 b/lib/krb5/krb5-plugin.cat7
new file mode 100644
index 000000000000..6d8ac426ace7
--- /dev/null
+++ b/lib/krb5/krb5-plugin.cat7
@@ -0,0 +1,168 @@
+
+KRB5-PLUGIN(7) BSD Miscellaneous Information Manual KRB5-PLUGIN(7)
+
+NNAAMMEE
+ kkrrbb55--pplluuggiinn -- plugin interface for Heimdal
+
+SSYYNNOOPPSSIISS
+ ##iinncclluuddee <<kkrrbb55..hh>>
+ ##iinncclluuddee <<kkrrbb55//aann22llnn__pplluuggiinn..hh>>
+ ##iinncclluuddee <<kkrrbb55//ccccaacchhee__pplluuggiinn..hh>>
+ ##iinncclluuddee <<kkrrbb55//ddbb__pplluuggiinn..hh>>
+ ##iinncclluuddee <<kkrrbb55//kkuusseerrookk__pplluuggiinn..hh>>
+ ##iinncclluuddee <<kkrrbb55//llooccaattee__pplluuggiinn..hh>>
+ ##iinncclluuddee <<kkrrbb55//sseenndd__ttoo__kkddcc__pplluuggiinn..hh>>
+
+DDEESSCCRRIIPPTTIIOONN
+ Heimdal has a plugin interface. Plugins may be statically linked into
+ Heimdal and registered via the krb5_plugin_register(3) function, or they
+ may be dynamically loaded from shared objects present in the Heimdal
+ plugins directories.
+
+ Plugins consist of a C struct whose struct name is given in the associ-
+ ated header file, such as, for example, _k_r_b_5_p_l_u_g_i_n___k_u_s_e_r_o_k___f_t_a_b_l_e and a
+ pointer to which is either registered via krb5_plugin_register(3) or
+ found in a shared object via a symbol lookup for the symbol name defined
+ in the associated header file (e.g., "kuserok" for the plugin for
+ krb5_kuserok(3) ).
+
+ The plugin structs for all plugin types always begin with the same three
+ common fields:
+ 1. _m_i_n_o_r___v_e_r_s_i_o_n , an int. Plugin minor versions are defined in each
+ plugin type's associated header file.
+ 2. _i_n_i_t , a pointer to a function with two arguments, a krb5_context
+ and a void **, returning a krb5_error_code. This function will be
+ called to initialize a plugin-specific context in the form of a void
+ * that will be output through the init function's second argument.
+ 3. _f_i_n_i , a pointer to a function of one argument, a void *, consisting
+ of the plugin's context to be destroyed, and returning void.
+
+ Each plugin type must add zero or more fields to this struct following
+ the above three. Plugins are typically invoked in no particular order
+ until one succeeds or fails, or all return a special return value such as
+ KRB5_PLUGIN_NO_HANDLE to indicate that the plugin was not applicable.
+ Most plugin types obtain deterministic plugin behavior in spite of the
+ non-deterministic invocation order by, for example, invoking all plugins
+ for each "rule" and passing the rule to each plugin with the expectation
+ that just one plugin will match any given rule.
+
+ There is a database plugin system intended for many of the uses of data-
+ bases in Heimdal. The plugin is expected to call heim_db_register(3)
+ from its _i_n_i_t entry point to register a DB type. The DB plugin's _f_i_n_i
+ function must do nothing, and the plugin must not provide any other entry
+ points.
+
+ The krb5_kuserok plugin adds a single field to its struct: a pointer to a
+ function that implements kuserok functionality with the following form:
+
+ static krb5_error_code
+ kuserok(void *plug_ctx, krb5_context context, const char *rule,
+ unsigned int flags, const char *k5login_dir,
+ const char *luser, krb5_const_principal principal,
+ krb5_boolean *result)
+
+ The _l_u_s_e_r , _p_r_i_n_c_i_p_a_l and _r_e_s_u_l_t arguments are self-explanatory (see
+ krb5_kuserok(3) ). The _p_l_u_g___c_t_x argument is the context output by the
+ plugin's init function. The _r_u_l_e argument is a kuserok rule from the
+ krb5.conf file; each plugin is invoked once for each rule until all plug-
+ ins fail or one succeeds. The _k_5_l_o_g_i_n___d_i_r argument provides an alterna-
+ tive k5login file location, if not NULL. The _f_l_a_g_s argument indicates
+ whether the plugin may call krb5_aname_to_localname(3)
+ (KUSEROK_ANAME_TO_LNAME_OK), and whether k5login databases are expected
+ to be authoritative (KUSEROK_K5LOGIN_IS_AUTHORITATIVE).
+
+ The plugin for krb5_aname_to_localname(3) is named "an2ln" and has a sin-
+ gle extra field for the plugin struct:
+
+ typedef krb5_error_code (*set_result_f)(void *, const char *);
+
+ static krb5_error_code
+ an2ln(void *plug_ctx, krb5_context context, const char *rule,
+ krb5_const_principal aname, set_result_f set_res_f, void *set_res_ctx)
+
+ The arguments for the _a_n_2_l_n plugin are similar to those of the kuserok
+ plugin, but the result, being a string, is set by calling the _s_e_t___r_e_s___f
+ function argument with the _s_e_t___r_e_s___c_t_x and result string as arguments.
+ The _s_e_t___r_e_s___f function will make a copy of the string.
+
+FFIILLEESS
+ libdir/plugin/krb5/* Shared objects containing plugins for
+ Heimdal.
+
+EEXXAAMMPPLLEESS
+ An example an2ln plugin that maps principals to a constant "nouser" fol-
+ lows:
+
+ #include <krb5/an2ln_plugin.h>
+
+ static krb5_error_code
+ nouser_plug_init(krb5_context context, void **ctx)
+ {
+ *ctx = NULL;
+ return 0;
+ }
+
+ static void nouser_plug_fini(void *ctx) { }
+
+ static krb5_error_code
+ nouser_plug_an2ln(void *plug_ctx, krb5_context context,
+ const char *rule,
+ krb5_const_principal aname,
+ set_result_f set_res_f, void *set_res_ctx)
+ {
+ krb5_error_code ret;
+
+ if (strcmp(rule, "NOUSER") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ ret = set_res_f(set_res_ctx, "nouser");
+
+ return ret;
+ }
+
+ krb5plugin_an2ln_ftable an2ln = {
+ KRB5_PLUGIN_AN2LN_VERSION_0,
+ nouser_plug_init,
+ nouser_plug_fini,
+ nouser_plug_an2ln,
+ };
+
+ An example kuserok plugin that rejects all requests follows. (Note that
+ there exists a built-in plugin with this functionality; see
+ krb5_kuserok(3) ).
+
+ #include <krb5/kuserok_plugin.h>
+
+ static krb5_error_code
+ reject_plug_init(krb5_context context, void **ctx)
+ {
+ *ctx = NULL;
+ return 0;
+ }
+
+ static void reject_plug_fini(void *ctx) { }
+
+ static krb5_error_code
+ reject_plug_kuserok(void *plug_ctx, krb5_context context, const char *rule,
+ unsigned int flags, const char *k5login_dir,
+ const char *luser, krb5_const_principal principal,
+ krb5_boolean *result)
+ {
+ if (strcmp(rule, "REJECT") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ *result = FALSE;
+ return 0;
+ }
+
+ krb5plugin_kuserok_ftable kuserok = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ reject_plug_init,
+ reject_plug_fini,
+ reject_plug_kuserok,
+ };
+
+SSEEEE AALLSSOO
+ krb5_plugin_register(3) krb5_kuserok(3) krb5_aname_to_localname(3)
+
+HEIMDAL December 21, 2011 HEIMDAL
diff --git a/lib/krb5/krb5-private.h b/lib/krb5/krb5-private.h
index 956e00e4aaf0..b511590ccfd4 100644
--- a/lib/krb5/krb5-private.h
+++ b/lib/krb5/krb5-private.h
@@ -9,18 +9,28 @@
#endif
#ifndef KRB5_DEPRECATED_FUNCTION
-#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))
+#ifndef __has_extension
+#define __has_extension(x) 0
+#define KRB5_DEPRECATED_FUNCTIONhas_extension 1
+#endif
+#if __has_extension(attribute_deprecated_with_message)
+#define KRB5_DEPRECATED_FUNCTION(x) __attribute__((__deprecated__(x)))
+#elif defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))
#define KRB5_DEPRECATED_FUNCTION(X) __attribute__((__deprecated__))
#else
#define KRB5_DEPRECATED_FUNCTION(X)
#endif
+#ifdef KRB5_DEPRECATED_FUNCTIONhas_extension
+#undef __has_extension
+#undef KRB5_DEPRECATED_FUNCTIONhas_extension
#endif
+#endif /* KRB5_DEPRECATED_FUNCTION */
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_heim_krb5_ipc_client_clear_target (void);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_heim_krb5_ipc_client_set_target_uid (uid_t /*uid*/);
void
@@ -40,6 +50,15 @@ _krb5_HMAC_MD5_checksum (
Checksum */*result*/);
krb5_error_code
+_krb5_SP800_108_HMAC_KDF (
+ krb5_context /*context*/,
+ const krb5_data */*kdf_K1*/,
+ const krb5_data */*kdf_label*/,
+ const krb5_data */*kdf_context*/,
+ const EVP_MD */*md*/,
+ krb5_data */*kdf_K0*/);
+
+krb5_error_code
_krb5_SP_HMAC_SHA1_checksum (
krb5_context /*context*/,
struct _krb5_key_data */*key*/,
@@ -48,6 +67,12 @@ _krb5_SP_HMAC_SHA1_checksum (
unsigned /*usage*/,
Checksum */*result*/);
+krb5_error_code
+_krb5_aes_sha2_md_for_enctype (
+ krb5_context /*context*/,
+ krb5_enctype /*enctype*/,
+ const EVP_MD **/*md*/);
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_build_authenticator (
krb5_context /*context*/,
@@ -59,6 +84,12 @@ _krb5_build_authenticator (
krb5_key_usage /*usage*/);
krb5_error_code
+_krb5_build_authpack_subjectPK_EC (
+ krb5_context /*context*/,
+ krb5_pk_init_ctx /*ctx*/,
+ AuthPack */*a*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_cc_allocate (
krb5_context /*context*/,
const krb5_cc_ops */*ops*/,
@@ -77,7 +108,7 @@ _krb5_config_get (
int /*type*/,
...);
-krb5_config_section *
+KRB5_LIB_FUNCTION krb5_config_section * KRB5_LIB_CALL
_krb5_config_get_entry (
krb5_config_section **/*parent*/,
const char */*name*/,
@@ -91,7 +122,7 @@ _krb5_config_get_next (
int /*type*/,
...);
-const void *
+KRB5_LIB_FUNCTION const void * KRB5_LIB_CALL
_krb5_config_vget (
krb5_context /*context*/,
const krb5_config_section */*c*/,
@@ -111,10 +142,10 @@ _krb5_copy_send_to_kdc_func (
krb5_context /*context*/,
krb5_context /*to*/);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_crc_init_table (void);
-uint32_t
+KRB5_LIB_FUNCTION uint32_t KRB5_LIB_CALL
_krb5_crc_update (
const char */*p*/,
size_t /*len*/,
@@ -126,12 +157,12 @@ _krb5_debug (
int /*level*/,
const char */*fmt*/,
...)
- __attribute__((format (printf, 3, 4)));
+ __attribute__ ((__format__ (__printf__, 3, 4)));
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_debug_backtrace (krb5_context /*context*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_derive_key (
krb5_context /*context*/,
struct _krb5_encryption_type */*et*/,
@@ -139,7 +170,7 @@ _krb5_derive_key (
const void */*constant*/,
size_t /*len*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_des_checksum (
krb5_context /*context*/,
const EVP_MD */*evp_md*/,
@@ -148,7 +179,7 @@ _krb5_des_checksum (
size_t /*len*/,
Checksum */*cksum*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_des_verify (
krb5_context /*context*/,
const EVP_MD */*evp_md*/,
@@ -157,7 +188,7 @@ _krb5_des_verify (
size_t /*len*/,
Checksum */*C*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_dh_group_ok (
krb5_context /*context*/,
unsigned long /*bits*/,
@@ -167,13 +198,18 @@ _krb5_dh_group_ok (
struct krb5_dh_moduli **/*moduli*/,
char **/*name*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_einval (
krb5_context /*context*/,
const char */*func*/,
unsigned long /*argn*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_enctype_requires_random_salt (
+ krb5_context /*context*/,
+ krb5_enctype /*enctype*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_erase_file (
krb5_context /*context*/,
const char */*filename*/);
@@ -209,7 +245,7 @@ _krb5_evp_schedule (
struct _krb5_key_type */*kt*/,
struct _krb5_key_data */*kd*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_expand_default_cc_name (
krb5_context /*context*/,
const char */*str*/,
@@ -219,9 +255,18 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_expand_path_tokens (
krb5_context /*context*/,
const char */*path_in*/,
+ int /*filepath*/,
char **/*ppath_out*/);
-int
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_expand_path_tokensv (
+ krb5_context /*context*/,
+ const char */*path_in*/,
+ int /*filepath*/,
+ char **/*ppath_out*/,
+ ...);
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_extract_ticket (
krb5_context /*context*/,
krb5_kdc_rep */*rep*/,
@@ -232,28 +277,75 @@ _krb5_extract_ticket (
krb5_addresses */*addrs*/,
unsigned /*nonce*/,
unsigned /*flags*/,
+ krb5_data */*request*/,
krb5_decrypt_proc /*decrypt_proc*/,
krb5_const_pointer /*decryptarg*/);
-struct _krb5_checksum_type *
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_fast_armor_key (
+ krb5_context /*context*/,
+ krb5_keyblock */*subkey*/,
+ krb5_keyblock */*sessionkey*/,
+ krb5_keyblock */*armorkey*/,
+ krb5_crypto */*armor_crypto*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_fast_cf2 (
+ krb5_context /*context*/,
+ krb5_keyblock */*key1*/,
+ const char */*pepper1*/,
+ krb5_keyblock */*key2*/,
+ const char */*pepper2*/,
+ krb5_keyblock */*armorkey*/,
+ krb5_crypto */*armor_crypto*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_find_capath (
+ krb5_context /*context*/,
+ const char */*client_realm*/,
+ const char */*local_realm*/,
+ const char */*server_realm*/,
+ krb5_boolean /*use_hierarchical*/,
+ char ***/*rpath*/,
+ size_t */*npath*/);
+
+KRB5_LIB_FUNCTION struct _krb5_checksum_type * KRB5_LIB_CALL
_krb5_find_checksum (krb5_cksumtype /*type*/);
-struct _krb5_encryption_type *
+KRB5_LIB_FUNCTION struct _krb5_encryption_type * KRB5_LIB_CALL
_krb5_find_enctype (krb5_enctype /*type*/);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_free_capath (
+ krb5_context /*context*/,
+ char **/*capath*/);
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_free_key_data (
krb5_context /*context*/,
struct _krb5_key_data */*key*/,
struct _krb5_encryption_type */*et*/);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_free_krbhst_info (krb5_krbhst_info */*hi*/);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_free_moduli (struct krb5_dh_moduli **/*moduli*/);
-krb5_error_code
+KRB5_LIB_FUNCTION void
+_krb5_free_name_canon_rules (
+ krb5_context /*context*/,
+ krb5_name_canon_rule /*rules*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_get_ad (
+ krb5_context /*context*/,
+ const AuthorizationData */*ad*/,
+ krb5_keyblock */*sessionkey*/,
+ int /*type*/,
+ krb5_data */*data*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_cred_kdc_any (
krb5_context /*context*/,
krb5_kdc_flags /*flags*/,
@@ -264,13 +356,13 @@ _krb5_get_cred_kdc_any (
krb5_creds **/*out_creds*/,
krb5_creds ***/*ret_tgts*/);
-char *
+KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
_krb5_get_default_cc_name_from_registry (krb5_context /*context*/);
-char *
+KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
_krb5_get_default_config_config_files_from_registry (void);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_default_principal_local (
krb5_context /*context*/,
krb5_principal */*princ*/);
@@ -291,19 +383,30 @@ _krb5_get_int (
unsigned long */*value*/,
size_t /*size*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+_krb5_get_int64 (
+ void */*buffer*/,
+ uint64_t */*value*/,
+ size_t /*size*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_get_krbtgt (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_realm /*realm*/,
krb5_creds **/*cred*/);
-krb5_boolean KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_error_code
+_krb5_get_name_canon_rules (
+ krb5_context /*context*/,
+ krb5_name_canon_rule */*rules*/);
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_have_debug (
krb5_context /*context*/,
int /*level*/);
-krb5_boolean
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_homedir_access (krb5_context /*context*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -314,7 +417,7 @@ _krb5_init_etype (
krb5_enctype **/*val*/,
const krb5_enctype */*etypes*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_internal_hmac (
krb5_context /*context*/,
struct _krb5_checksum_type */*cm*/,
@@ -347,7 +450,7 @@ _krb5_kcm_noop (
krb5_context /*context*/,
krb5_ccache /*id*/);
-krb5_error_code KRB5_CALLCONV
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kdc_retry (
krb5_context /*context*/,
krb5_sendto_ctx /*ctx*/,
@@ -355,16 +458,16 @@ _krb5_kdc_retry (
const krb5_data */*reply*/,
int */*action*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_krbhost_info_move (
krb5_context /*context*/,
krb5_krbhst_info */*from*/,
krb5_krbhst_info **/*to*/);
-const char *
+KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
_krb5_krbhst_get_realm (krb5_krbhst_handle /*handle*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_kt_principal_not_found (
krb5_context /*context*/,
krb5_error_code /*ret*/,
@@ -373,16 +476,34 @@ _krb5_kt_principal_not_found (
krb5_enctype /*enctype*/,
int /*kvno*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_kuserok (
+ krb5_context /*context*/,
+ krb5_principal /*principal*/,
+ const char */*luser*/,
+ krb5_boolean /*an2ln_ok*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_load_ccache_plugins (krb5_context /*context*/);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_load_db_plugins (krb5_context /*context*/);
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_load_plugins (
krb5_context /*context*/,
const char */*name*/,
const char **/*paths*/);
krb5_error_code
+_krb5_make_fast_ap_fxarmor (
+ krb5_context /*context*/,
+ krb5_ccache /*armor_ccache*/,
+ krb5_data */*armor_value*/,
+ krb5_keyblock */*armor_key*/,
+ krb5_crypto */*armor_crypto*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_mk_req_internal (
krb5_context /*context*/,
krb5_auth_context */*auth_context*/,
@@ -400,7 +521,7 @@ _krb5_n_fold (
void */*key*/,
size_t /*size*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pac_sign (
krb5_context /*context*/,
krb5_pac /*p*/,
@@ -410,13 +531,13 @@ _krb5_pac_sign (
const krb5_keyblock */*priv_key*/,
krb5_data */*data*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_parse_moduli (
krb5_context /*context*/,
const char */*file*/,
struct krb5_dh_moduli ***/*moduli*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_parse_moduli_line (
krb5_context /*context*/,
const char */*file*/,
@@ -427,7 +548,10 @@ _krb5_parse_moduli_line (
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_pk_cert_free (struct krb5_pk_cert */*cert*/);
-krb5_error_code
+void
+_krb5_pk_eckey_free (void */*eckey*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_kdf (
krb5_context /*context*/,
const struct AlgorithmIdentifier */*ai*/,
@@ -470,7 +594,7 @@ _krb5_pk_mk_padata (
unsigned /*nonce*/,
METHOD_DATA */*md*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pk_octetstring2key (
krb5_context /*context*/,
krb5_enctype /*type*/,
@@ -493,22 +617,25 @@ _krb5_pk_rd_pa_reply (
krb5_keyblock **/*key*/);
krb5_error_code
+_krb5_pk_rd_pa_reply_ecdh_compute_key (
+ krb5_context /*context*/,
+ krb5_pk_init_ctx /*ctx*/,
+ const unsigned char */*in*/,
+ size_t /*in_sz*/,
+ unsigned char **/*out*/,
+ int */*out_sz*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_find (
krb5_context /*context*/,
enum krb5_plugin_type /*type*/,
const char */*name*/,
struct krb5_plugin **/*list*/);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_plugin_free (struct krb5_plugin */*list*/);
-struct krb5_plugin *
-_krb5_plugin_get_next (struct krb5_plugin */*p*/);
-
-void *
-_krb5_plugin_get_symbol (struct krb5_plugin */*p*/);
-
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_run_f (
krb5_context /*context*/,
const char */*module*/,
@@ -516,7 +643,7 @@ _krb5_plugin_run_f (
int /*min_version*/,
int /*flags*/,
void */*userctx*/,
- krb5_error_code (*/*func*/)(krb5_context, const void *, void *, void *));
+ krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_principal2principalname (
@@ -539,7 +666,7 @@ _krb5_principalname2krb5_principal (
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
_krb5_put_int (
void */*buffer*/,
- unsigned long /*value*/,
+ uint64_t /*value*/,
size_t /*size*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -548,41 +675,46 @@ _krb5_s4u2self_to_checksumdata (
const PA_S4U2Self */*self*/,
krb5_data */*data*/);
-int
-_krb5_send_and_recv_tcp (
- krb5_socket_t /*fd*/,
- time_t /*tmout*/,
- const krb5_data */*req*/,
- krb5_data */*rep*/);
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_sendto_ctx_set_krb5hst (
+ krb5_context /*context*/,
+ krb5_sendto_ctx /*ctx*/,
+ krb5_krbhst_handle /*handle*/);
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_sendto_ctx_set_prexmit (
+ krb5_sendto_ctx /*ctx*/,
+ krb5_sendto_prexmit /*prexmit*/,
+ void */*data*/);
-int
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_set_default_cc_name_to_registry (
krb5_context /*context*/,
krb5_ccache /*id*/);
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_unload_plugins (
krb5_context /*context*/,
const char */*name*/);
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_usage2arcfour (
krb5_context /*context*/,
unsigned */*usage*/);
-int
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_xlock (
krb5_context /*context*/,
int /*fd*/,
krb5_boolean /*exclusive*/,
const char */*filename*/);
-void
-_krb5_xor (
- DES_cblock */*key*/,
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_xor8 (
+ unsigned char */*a*/,
const unsigned char */*b*/);
-int
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_xunlock (
krb5_context /*context*/,
int /*fd*/);
diff --git a/lib/krb5/krb5-protos.h b/lib/krb5/krb5-protos.h
index c72e796afe20..a312e070fefb 100644
--- a/lib/krb5/krb5-protos.h
+++ b/lib/krb5/krb5-protos.h
@@ -1,6 +1,7 @@
/* This is a generated file */
#ifndef __krb5_protos_h__
#define __krb5_protos_h__
+#ifndef DOXY
#include <stdarg.h>
@@ -9,12 +10,22 @@
#endif
#ifndef KRB5_DEPRECATED_FUNCTION
-#if defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))
+#ifndef __has_extension
+#define __has_extension(x) 0
+#define KRB5_DEPRECATED_FUNCTIONhas_extension 1
+#endif
+#if __has_extension(attribute_deprecated_with_message)
+#define KRB5_DEPRECATED_FUNCTION(x) __attribute__((__deprecated__(x)))
+#elif defined(__GNUC__) && ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 1 )))
#define KRB5_DEPRECATED_FUNCTION(X) __attribute__((__deprecated__))
#else
#define KRB5_DEPRECATED_FUNCTION(X)
#endif
+#ifdef KRB5_DEPRECATED_FUNCTIONhas_extension
+#undef __has_extension
+#undef KRB5_DEPRECATED_FUNCTIONhas_extension
#endif
+#endif /* KRB5_DEPRECATED_FUNCTION */
#ifdef __cplusplus
@@ -34,6 +45,22 @@ extern "C" {
#endif
#endif
#endif
+/**
+ * Convert the v5 credentials in in_cred to v4-dito in v4creds. This
+ * is done by sending them to the 524 function in the KDC. If
+ * `in_cred' doesn't contain a DES session key, then a new one is
+ * gotten from the KDC and stored in the cred cache `ccache'.
+ *
+ * @param context Kerberos 5 context.
+ * @param in_cred the credential to convert
+ * @param v4creds the converted credential
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_v4compat
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb524_convert_creds_kdc (
krb5_context /*context*/,
@@ -41,6 +68,21 @@ krb524_convert_creds_kdc (
struct credentials */*v4creds*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Convert the v5 credentials in in_cred to v4-dito in v4creds,
+ * check the credential cache ccache before checking with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param ccache credential cache used to check for des-ticket.
+ * @param in_cred the credential to convert
+ * @param v4creds the converted credential
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_v4compat
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb524_convert_creds_kdc_ccache (
krb5_context /*context*/,
@@ -49,20 +91,58 @@ krb524_convert_creds_kdc_ccache (
struct credentials */*v4creds*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure and then abort.
+ *
+ * @param context A Kerberos 5 context
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ... arguments for format string
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_abort (
krb5_context /*context*/,
krb5_error_code /*code*/,
const char */*fmt*/,
...)
- __attribute__ ((noreturn, format (printf, 3, 4)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)));
+
+/**
+ * Log a warning to the log, default stderr, and then abort.
+ *
+ * @param context A Kerberos 5 context
+ * @param fmt printf format string of message to print
+ * @param ... arguments for format string
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_abortx (
krb5_context /*context*/,
const char */*fmt*/,
...)
- __attribute__ ((noreturn, format (printf, 2, 3)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 3)));
+
+/**
+ * krb5_acl_match_file matches ACL format against each line in a file
+ * using krb5_acl_match_string(). Lines starting with # are treated
+ * like comments and ignored.
+ *
+ * @param context Kerberos 5 context.
+ * @param file file with acl listed in the file.
+ * @param format format to match.
+ * @param ... parameter to format string.
+ *
+ * @return Return an error code or 0.
+ *
+ * @sa krb5_acl_match_string
+ * @ingroup krb5_support
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_acl_match_file (
@@ -71,6 +151,52 @@ krb5_acl_match_file (
const char */*format*/,
...);
+/**
+ * krb5_acl_match_string matches ACL format against a string.
+ *
+ * The ACL format has three format specifiers: s, f, and r. Each
+ * specifier will retrieve one argument from the variable arguments
+ * for either matching or storing data. The input string is split up
+ * using " " (space) and "\t" (tab) as a delimiter; multiple and "\t"
+ * in a row are considered to be the same.
+ *
+ * List of format specifiers:
+ * - s Matches a string using strcmp(3) (case sensitive).
+ * - f Matches the string with fnmatch(3). Theflags
+ * argument (the last argument) passed to the fnmatch function is 0.
+ * - r Returns a copy of the string in the char ** passed in; the copy
+ * must be freed with free(3). There is no need to free(3) the
+ * string on error: the function will clean up and set the pointer
+ * to NULL.
+ *
+ * @param context Kerberos 5 context
+ * @param string string to match with
+ * @param format format to match
+ * @param ... parameter to format string
+ *
+ * @return Return an error code or 0.
+ *
+ *
+ * @code
+ * char *s;
+ *
+ * ret = krb5_acl_match_string(context, "foo", "s", "foo");
+ * if (ret)
+ * krb5_errx(context, 1, "acl didn't match");
+ * ret = krb5_acl_match_string(context, "foo foo baz/kaka",
+ * "ss", "foo", &s, "foo/\\*");
+ * if (ret) {
+ * // no need to free(s) on error
+ * assert(s == NULL);
+ * krb5_errx(context, 1, "acl didn't match");
+ * }
+ * free(s);
+ * @endcode
+ *
+ * @sa krb5_acl_match_file
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_acl_match_string (
krb5_context /*context*/,
@@ -78,16 +204,56 @@ krb5_acl_match_string (
const char */*format*/,
...);
+/**
+ * Add a specified list of error messages to the et list in context.
+ * Call func (probably a comerr-generated function) with a pointer to
+ * the current et_list.
+ *
+ * @param context A kerberos context.
+ * @param func The generated com_err et function.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_add_et_list (
krb5_context /*context*/,
void (*/*func*/)(struct et_list **));
+/**
+ * Add extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to add
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_add_extra_addresses (
krb5_context /*context*/,
krb5_addresses */*addresses*/);
+/**
+ * Add extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to ignore
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_add_ignore_addresses (
krb5_context /*context*/,
@@ -109,6 +275,27 @@ krb5_addlog_func (
krb5_log_close_func_t /*close_func*/,
void */*data*/);
+/**
+ * krb5_addr2sockaddr sets the "struct sockaddr sockaddr" from addr
+ * and port. The argument sa_size should initially contain the size of
+ * the sa and after the call, it will contain the actual length of the
+ * address. In case of the sa is too small to fit the whole address,
+ * the up to *sa_size will be stored, and then *sa_size will be set to
+ * the required length.
+ *
+ * @param context a Keberos context
+ * @param addr the address to copy the from
+ * @param sa the struct sockaddr that will be filled in
+ * @param sa_size pointer to length of sa, and after the call, it will
+ * contain the actual length of the address.
+ * @param port set port in sa.
+ *
+ * @return Return an error code or 0. Will return
+ * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_addr2sockaddr (
krb5_context /*context*/,
@@ -117,18 +304,61 @@ krb5_addr2sockaddr (
krb5_socklen_t */*sa_size*/,
int /*port*/);
+/**
+ * krb5_address_compare compares the addresses addr1 and addr2.
+ * Returns TRUE if the two addresses are the same.
+ *
+ * @param context a Keberos context
+ * @param addr1 address to compare
+ * @param addr2 address to compare
+ *
+ * @return Return an TRUE is the address are the same FALSE if not
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_address_compare (
krb5_context /*context*/,
const krb5_address */*addr1*/,
const krb5_address */*addr2*/);
+/**
+ * krb5_address_order compares the addresses addr1 and addr2 so that
+ * it can be used for sorting addresses. If the addresses are the same
+ * address krb5_address_order will return 0. Behavies like memcmp(2).
+ *
+ * @param context a Keberos context
+ * @param addr1 krb5_address to compare
+ * @param addr2 krb5_address to compare
+ *
+ * @return < 0 if address addr1 in "less" then addr2. 0 if addr1 and
+ * addr2 is the same address, > 0 if addr2 is "less" then addr1.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_address_order (
krb5_context /*context*/,
const krb5_address */*addr1*/,
const krb5_address */*addr2*/);
+/**
+ * Calculate the boundary addresses of `inaddr'/`prefixlen' and store
+ * them in `low' and `high'.
+ *
+ * @param context a Keberos context
+ * @param inaddr address in prefixlen that the bondery searched
+ * @param prefixlen width of boundery
+ * @param low lowest address
+ * @param high highest address
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_address_prefixlen_boundary (
krb5_context /*context*/,
@@ -137,17 +367,57 @@ krb5_address_prefixlen_boundary (
krb5_address */*low*/,
krb5_address */*high*/);
+/**
+ * krb5_address_search checks if the address addr is a member of the
+ * address set list addrlist .
+ *
+ * @param context a Keberos context.
+ * @param addr address to search for.
+ * @param addrlist list of addresses to look in for addr.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_address_search (
krb5_context /*context*/,
const krb5_address */*addr*/,
const krb5_addresses */*addrlist*/);
+/**
+ * Enable or disable all weak encryption types
+ *
+ * @param context Kerberos 5 context
+ * @param enable true to enable, false to disable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_allow_weak_crypto (
krb5_context /*context*/,
krb5_boolean /*enable*/);
+/**
+ * Map a principal name to a local username.
+ *
+ * Returns 0 on success, KRB5_NO_LOCALNAME if no mapping was found, or
+ * some Kerberos or system error.
+ *
+ * Inputs:
+ *
+ * @param context A krb5_context
+ * @param aname A principal name
+ * @param lnsize The size of the buffer into which the username will be written
+ * @param lname The buffer into which the username will be written
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_aname_to_localname (
krb5_context /*context*/,
@@ -155,6 +425,23 @@ krb5_aname_to_localname (
size_t /*lnsize*/,
char */*lname*/);
+/**
+ * krb5_anyaddr fills in a "struct sockaddr sa" that can be used to
+ * bind(2) to. The argument sa_size should initially contain the size
+ * of the sa, and after the call, it will contain the actual length
+ * of the address.
+ *
+ * @param context a Keberos context
+ * @param af address family
+ * @param sa sockaddr
+ * @param sa_size lenght of sa.
+ * @param port for to fill into sa.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_anyaddr (
krb5_context /*context*/,
@@ -190,6 +477,19 @@ krb5_appdefault_time (
time_t /*def_val*/,
time_t */*ret_val*/);
+/**
+ * krb5_append_addresses adds the set of addresses in source to
+ * dest. While copying the addresses, duplicates are also sorted out.
+ *
+ * @param context a Keberos context
+ * @param dest destination of copy operation
+ * @param source adresses that are going to be added to dest
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_append_addresses (
krb5_context /*context*/,
@@ -197,17 +497,41 @@ krb5_append_addresses (
const krb5_addresses */*source*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_auth_con_add_AuthorizationData (
+ krb5_context /*context*/,
+ krb5_auth_context /*auth_context*/,
+ int /*type*/,
+ krb5_data */*data*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_addflags (
krb5_context /*context*/,
krb5_auth_context /*auth_context*/,
int32_t /*addflags*/,
int32_t */*flags*/);
+/**
+ * Deallocate an authentication context previously initialized with
+ * krb5_auth_con_init().
+ *
+ * @param context A kerberos context.
+ * @param auth_context The authentication context to be deallocated.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_free (
krb5_context /*context*/,
krb5_auth_context /*auth_context*/);
+/**
+ * Update the authentication context \a auth_context with the local
+ * and remote addresses from socket \a fd, according to \a flags.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_genaddrs (
krb5_context /*context*/,
@@ -300,6 +624,17 @@ krb5_auth_con_getsendsubkey (
krb5_auth_context /*auth_context*/,
krb5_keyblock **/*keyblock*/);
+/**
+ * Allocate and initialize an autentication context.
+ *
+ * @param context A kerberos context.
+ * @param auth_context The authentication context to be initialized.
+ *
+ * Use krb5_auth_con_free() to release the memory when done using the context.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_con_init (
krb5_context /*context*/,
@@ -397,6 +732,12 @@ krb5_auth_con_setuserkey (
krb5_auth_context /*auth_context*/,
krb5_keyblock */*keyblock*/);
+/**
+ * Deprecated: use krb5_auth_con_getremoteseqnumber()
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_auth_getremoteseqnumber (
krb5_context /*context*/,
@@ -413,6 +754,20 @@ krb5_build_ap_req (
krb5_data /*authenticator*/,
krb5_data */*retdata*/);
+/**
+ * Build a principal using vararg style building
+ *
+ * @param context A Kerberos context.
+ * @param principal returned principal
+ * @param rlen length of realm
+ * @param realm realm name
+ * @param ... a list of components ended with NULL.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_build_principal (
krb5_context /*context*/,
@@ -482,6 +837,12 @@ krb5_c_encrypt_length (
size_t /*inputlen*/,
size_t */*length*/);
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_c_enctype_compare (
krb5_context /*context*/,
@@ -565,49 +926,140 @@ krb5_c_verify_checksum (
const krb5_checksum */*cksum*/,
krb5_boolean */*valid*/);
+/**
+ * Destroy the cursor `cursor'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_end_seq_get (
krb5_context /*context*/,
krb5_cc_cache_cursor /*cursor*/);
+/**
+ * Start iterating over all caches of specified type. See also
+ * krb5_cccol_cursor_new().
+
+ * @param context A Kerberos 5 context
+ * @param type optional type to iterate over, if NULL, the default cache is used.
+ * @param cursor cursor should be freed with krb5_cc_cache_end_seq_get().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_get_first (
krb5_context /*context*/,
const char */*type*/,
krb5_cc_cache_cursor */*cursor*/);
+/**
+ * Search for a matching credential cache that have the
+ * `principal' as the default principal. On success, `id' needs to be
+ * freed with krb5_cc_close() or krb5_cc_destroy().
+ *
+ * @param context A Kerberos 5 context
+ * @param client The principal to search for
+ * @param id the returned credential cache
+ *
+ * @return On failure, error code is returned and `id' is set to NULL.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_match (
krb5_context /*context*/,
krb5_principal /*client*/,
krb5_ccache */*id*/);
+/**
+ * Retrieve the next cache pointed to by (`cursor') in `id'
+ * and advance `cursor'.
+ *
+ * @param context A Kerberos 5 context
+ * @param cursor the iterator cursor, returned by krb5_cc_cache_get_first()
+ * @param id next ccache
+ *
+ * @return Return 0 or an error code. Returns KRB5_CC_END when the end
+ * of caches is reached, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_cache_next (
krb5_context /*context*/,
krb5_cc_cache_cursor /*cursor*/,
krb5_ccache */*id*/);
+/**
+ * Clear `mcreds' so it can be used with krb5_cc_retrieve_cred
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_cc_clear_mcred (krb5_creds */*mcred*/);
+/**
+ * Stop using the ccache `id' and free the related resources.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_close (
krb5_context /*context*/,
krb5_ccache /*id*/);
+/**
+ * Just like krb5_cc_copy_match_f(), but copy everything.
+ *
+ * @ingroup @krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_copy_cache (
krb5_context /*context*/,
const krb5_ccache /*from*/,
krb5_ccache /*to*/);
+/**
+ * MIT compat glue
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_copy_creds (
krb5_context /*context*/,
const krb5_ccache /*from*/,
krb5_ccache /*to*/);
+/**
+ * Copy the contents of `from' to `to' if the given match function
+ * return true.
+ *
+ * @param context A Kerberos 5 context.
+ * @param from the cache to copy data from.
+ * @param to the cache to copy data to.
+ * @param match a match function that should return TRUE if cred argument should be copied, if NULL, all credentials are copied.
+ * @param matchctx context passed to match function.
+ * @param matched set to true if there was a credential that matched, may be NULL.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_copy_match_f (
krb5_context /*context*/,
@@ -617,25 +1069,66 @@ krb5_cc_copy_match_f (
void */*matchctx*/,
unsigned int */*matched*/);
+/**
+ * Open the default ccache in `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_default (
krb5_context /*context*/,
krb5_ccache */*id*/);
+/**
+ * Return a pointer to a context static string containing the default
+ * ccache name.
+ *
+ * @return String to the default credential cache name.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_cc_default_name (krb5_context /*context*/);
+/**
+ * Remove the ccache `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_destroy (
krb5_context /*context*/,
krb5_ccache /*id*/);
+/**
+ * Destroy the cursor `cursor'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_end_seq_get (
krb5_context /*context*/,
const krb5_ccache /*id*/,
krb5_cc_cursor */*cursor*/);
+/**
+ * Generate a new ccache of type `ops' in `id'.
+ *
+ * Deprecated: use krb5_cc_new_unique() instead.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_gen_new (
krb5_context /*context*/,
@@ -643,6 +1136,19 @@ krb5_cc_gen_new (
krb5_ccache */*id*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Get some configuration for the credential cache in the cache.
+ *
+ * @param context a Keberos context
+ * @param id the credential cache to store the data for
+ * @param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * @param name name under which the configuraion is stored.
+ * @param data data to fetched, free with krb5_data_free()
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_config (
krb5_context /*context*/,
@@ -651,85 +1157,230 @@ krb5_cc_get_config (
const char */*name*/,
krb5_data */*data*/);
+/**
+ * Get the flags of `id', store them in `flags'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_flags (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_flags */*flags*/);
+/**
+ * Return a friendly name on credential cache. Free the result with krb5_xfree().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_friendly_name (
krb5_context /*context*/,
krb5_ccache /*id*/,
char **/*name*/);
+/**
+ * Return the complete resolvable name the cache
+
+ * @param context a Keberos context
+ * @param id return pointer to a found credential cache
+ * @param str the returned name of a credential cache, free with krb5_xfree()
+ *
+ * @return Returns 0 or an error (and then *str is set to NULL).
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_full_name (
krb5_context /*context*/,
krb5_ccache /*id*/,
char **/*str*/);
+/**
+ * Get the time offset betwen the client and the KDC
+ *
+ * If the backend doesn't support KDC offset, use the context global setting.
+ *
+ * @param context A Kerberos 5 context.
+ * @param id a credential cache
+ * @param offset the offset in seconds
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_kdc_offset (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_deltat */*offset*/);
+/**
+ * Get the lifetime of the initial ticket in the cache
+ *
+ * Get the lifetime of the initial ticket in the cache, if the initial
+ * ticket was not found, the error code KRB5_CC_END is returned.
+ *
+ * @param context A Kerberos 5 context.
+ * @param id a credential cache
+ * @param t the relative lifetime of the initial ticket
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_lifetime (
krb5_context /*context*/,
krb5_ccache /*id*/,
time_t */*t*/);
+/**
+ * Return the name of the ccache `id'
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_cc_get_name (
krb5_context /*context*/,
krb5_ccache /*id*/);
+/**
+ * Return krb5_cc_ops of a the ccache `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL
krb5_cc_get_ops (
krb5_context /*context*/,
krb5_ccache /*id*/);
+/**
+ * Get the cc ops that is registered in `context' to handle the
+ * prefix. prefix can be a complete credential cache name or a
+ * prefix, the function will only use part up to the first colon (:)
+ * if there is one. If prefix the argument is NULL, the default ccache
+ * implemtation is returned.
+ *
+ * @return Returns NULL if ops not found.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION const krb5_cc_ops * KRB5_LIB_CALL
krb5_cc_get_prefix_ops (
krb5_context /*context*/,
const char */*prefix*/);
+/**
+ * Return the principal of `id' in `principal'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_principal (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_principal */*principal*/);
+/**
+ * Return the type of the ccache `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_cc_get_type (
krb5_context /*context*/,
krb5_ccache /*id*/);
+/**
+ * Return the version of `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_get_version (
krb5_context /*context*/,
const krb5_ccache /*id*/);
+/**
+ * Create a new ccache in `id' for `primary_principal'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_initialize (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_principal /*primary_principal*/);
+/**
+ * Return the last time the credential cache was modified.
+ *
+ * @param context A Kerberos 5 context
+ * @param id The credential cache to probe
+ * @param mtime the last modification time, set to 0 on error.
+
+ * @return Return 0 or and error. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_last_change_time (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_timestamp */*mtime*/);
+/**
+ * Move the content from one credential cache to another. The
+ * operation is an atomic switch.
+ *
+ * @param context a Keberos context
+ * @param from the credential cache to move the content from
+ * @param to the credential cache to move the content to
+
+ * @return On sucess, from is freed. On failure, error code is
+ * returned and from and to are both still allocated, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_move (
krb5_context /*context*/,
krb5_ccache /*from*/,
krb5_ccache /*to*/);
+/**
+ * Generates a new unique ccache of `type` in `id'. If `type' is NULL,
+ * the library chooses the default credential cache type. The supplied
+ * `hint' (that can be NULL) is a string that the credential cache
+ * type can use to base the name of the credential on, this is to make
+ * it easier for the user to differentiate the credentials.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_new_unique (
krb5_context /*context*/,
@@ -737,6 +1388,15 @@ krb5_cc_new_unique (
const char */*hint*/,
krb5_ccache */*id*/);
+/**
+ * Retrieve the next cred pointed to by (`id', `cursor') in `creds'
+ * and advance `cursor'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_next_cred (
krb5_context /*context*/,
@@ -744,12 +1404,32 @@ krb5_cc_next_cred (
krb5_cc_cursor */*cursor*/,
krb5_creds */*creds*/);
+/**
+ * Add a new ccache type with operations `ops', overwriting any
+ * existing one if `override'.
+ *
+ * @param context a Keberos context
+ * @param ops type of plugin symbol
+ * @param override flag to select if the registration is to overide
+ * an existing ops with the same name.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_register (
krb5_context /*context*/,
const krb5_cc_ops */*ops*/,
krb5_boolean /*override*/);
+/**
+ * Remove the credential identified by `cred', `which' from `id'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_remove_cred (
krb5_context /*context*/,
@@ -757,12 +1437,43 @@ krb5_cc_remove_cred (
krb5_flags /*which*/,
krb5_creds */*cred*/);
+/**
+ * Find and allocate a ccache in `id' from the specification in `residual'.
+ * If the ccache name doesn't contain any colon, interpret it as a file name.
+ *
+ * @param context a Keberos context.
+ * @param name string name of a credential cache.
+ * @param id return pointer to a found credential cache.
+ *
+ * @return Return 0 or an error code. In case of an error, id is set
+ * to NULL, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_resolve (
krb5_context /*context*/,
const char */*name*/,
krb5_ccache */*id*/);
+/**
+ * Retrieve the credential identified by `mcreds' (and `whichfields')
+ * from `id' in `creds'. 'creds' must be free by the caller using
+ * krb5_free_cred_contents.
+ *
+ * @param context A Kerberos 5 context
+ * @param id a Kerberos 5 credential cache
+ * @param whichfields what fields to use for matching credentials, same
+ * flags as whichfields in krb5_compare_creds()
+ * @param mcreds template credential to use for comparing
+ * @param creds returned credential, free with krb5_free_cred_contents()
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_retrieve_cred (
krb5_context /*context*/,
@@ -771,6 +1482,20 @@ krb5_cc_retrieve_cred (
const krb5_creds */*mcreds*/,
krb5_creds */*creds*/);
+/**
+ * Store some configuration for the credential cache in the cache.
+ * Existing configuration under the same name is over-written.
+ *
+ * @param context a Keberos context
+ * @param id the credential cache to store the data for
+ * @param principal configuration for a specific principal, if
+ * NULL, global for the whole cache.
+ * @param name name under which the configuraion is stored.
+ * @param data data to store, if NULL, configure is removed.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_config (
krb5_context /*context*/,
@@ -779,73 +1504,209 @@ krb5_cc_set_config (
const char */*name*/,
krb5_data */*data*/);
+/**
+ * Set the default cc name for `context' to `name'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_default_name (
krb5_context /*context*/,
const char */*name*/);
+/**
+ * Set the flags of `id' to `flags'.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_flags (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_flags /*flags*/);
+/**
+ * Set the friendly name on credential cache.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_friendly_name (
krb5_context /*context*/,
krb5_ccache /*id*/,
const char */*name*/);
+/**
+ * Set the time offset betwen the client and the KDC
+ *
+ * If the backend doesn't support KDC offset, use the context global setting.
+ *
+ * @param context A Kerberos 5 context.
+ * @param id a credential cache
+ * @param offset the offset in seconds
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_set_kdc_offset (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_deltat /*offset*/);
+/**
+ * Start iterating over `id', `cursor' is initialized to the
+ * beginning. Caller must free the cursor with krb5_cc_end_seq_get().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_start_seq_get (
krb5_context /*context*/,
const krb5_ccache /*id*/,
krb5_cc_cursor */*cursor*/);
+/**
+ * Store `creds' in the ccache `id'.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_store_cred (
krb5_context /*context*/,
krb5_ccache /*id*/,
krb5_creds */*creds*/);
+/**
+ * Return true if the default credential cache support switch
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_cc_support_switch (
krb5_context /*context*/,
const char */*type*/);
+/**
+ * Switch the default default credential cache for a specific
+ * credcache type (and name for some implementations).
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cc_switch (
krb5_context /*context*/,
krb5_ccache /*id*/);
+/**
+ * End an iteration and free all resources, can be done before end is reached.
+ *
+ * @param context A Kerberos 5 context
+ * @param cursor the iteration cursor to be freed.
+ *
+ * @return Return 0 or and error, KRB5_CC_END is returned at the end
+ * of iteration. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_cursor_free (
krb5_context /*context*/,
krb5_cccol_cursor */*cursor*/);
+/**
+ * Get a new cache interation cursor that will interate over all
+ * credentials caches independent of type.
+ *
+ * @param context a Keberos context
+ * @param cursor passed into krb5_cccol_cursor_next() and free with krb5_cccol_cursor_free().
+ *
+ * @return Returns 0 or and error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_cursor_new (
krb5_context /*context*/,
krb5_cccol_cursor */*cursor*/);
+/**
+ * Get next credential cache from the iteration.
+ *
+ * @param context A Kerberos 5 context
+ * @param cursor the iteration cursor
+ * @param cache the returned cursor, pointer is set to NULL on failure
+ * and a cache on success. The returned cache needs to be freed
+ * with krb5_cc_close() or destroyed with krb5_cc_destroy().
+ * MIT Kerberos behavies slightly diffrent and sets cache to NULL
+ * when all caches are iterated over and return 0.
+ *
+ * @return Return 0 or and error, KRB5_CC_END is returned at the end
+ * of iteration. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_cursor_next (
krb5_context /*context*/,
krb5_cccol_cursor /*cursor*/,
krb5_ccache */*cache*/);
+/**
+ * Return the last modfication time for a cache collection. The query
+ * can be limited to a specific cache type. If the function return 0
+ * and mtime is 0, there was no credentials in the caches.
+ *
+ * @param context A Kerberos 5 context
+ * @param type The credential cache to probe, if NULL, all type are traversed.
+ * @param mtime the last modification time, set to 0 on error.
+
+ * @return Return 0 or and error. See krb5_get_error_message().
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cccol_last_change_time (
krb5_context /*context*/,
const char */*type*/,
krb5_timestamp */*mtime*/);
+/**
+ * Deprecated: krb5_change_password() is deprecated, use krb5_set_password().
+ *
+ * @param context a Keberos context
+ * @param creds
+ * @param newpw
+ * @param result_code
+ * @param result_code_string
+ * @param result_string
+ *
+ * @return On sucess password is changed.
+
+ * @ingroup @krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_change_password (
krb5_context /*context*/,
@@ -898,6 +1759,18 @@ krb5_checksumsize (
krb5_cksumtype /*type*/,
size_t */*size*/);
+/**
+ * Return the coresponding encryption type for a checksum type.
+ *
+ * @param context Kerberos context
+ * @param ctype The checksum type to get the result enctype for
+ * @param etype The returned encryption, when the matching etype is
+ * not found, etype is set to ETYPE_NULL.
+ *
+ * @return Return an error code for an failure or 0 on success.
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_cksumtype_to_enctype (
krb5_context /*context*/,
@@ -909,9 +1782,27 @@ krb5_cksumtype_valid (
krb5_context /*context*/,
krb5_cksumtype /*ctype*/);
+/**
+ * Clears the error message from the Kerberos 5 context.
+ *
+ * @param context The Kerberos 5 context to clear
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_clear_error_message (krb5_context /*context*/);
+/**
+ * Clear the error message returned by krb5_get_error_string().
+ *
+ * Deprecated: use krb5_clear_error_message()
+ *
+ * @param context Kerberos context
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_clear_error_string (krb5_context /*context*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
@@ -921,6 +1812,32 @@ krb5_closelog (
krb5_context /*context*/,
krb5_log_facility */*fac*/);
+/**
+ * Return TRUE if `mcreds' and `creds' are equal (`whichfields'
+ * determines what equal means).
+ *
+ *
+ * The following flags, set in whichfields affects the comparison:
+ * - KRB5_TC_MATCH_SRV_NAMEONLY Consider all realms equal when comparing the service principal.
+ * - KRB5_TC_MATCH_KEYTYPE Compare enctypes.
+ * - KRB5_TC_MATCH_FLAGS_EXACT Make sure that the ticket flags are identical.
+ * - KRB5_TC_MATCH_FLAGS Make sure that all ticket flags set in mcreds are also present in creds .
+ * - KRB5_TC_MATCH_TIMES_EXACT Compares the ticket times exactly.
+ * - KRB5_TC_MATCH_TIMES Compares only the expiration times of the creds.
+ * - KRB5_TC_MATCH_AUTHDATA Compares the authdata fields.
+ * - KRB5_TC_MATCH_2ND_TKT Compares the second tickets (used by user-to-user authentication).
+ * - KRB5_TC_MATCH_IS_SKEY Compares the existance of the second ticket.
+ *
+ * @param context Kerberos 5 context.
+ * @param whichfields which fields to compare.
+ * @param mcreds cred to compare with.
+ * @param creds cred to compare with.
+ *
+ * @return return TRUE if mcred and creds are equal, FALSE if not.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_compare_creds (
krb5_context /*context*/,
@@ -928,20 +1845,74 @@ krb5_compare_creds (
const krb5_creds * /*mcreds*/,
const krb5_creds * /*creds*/);
+/**
+ * Free configuration file section, the result of
+ * krb5_config_parse_file() and krb5_config_parse_file_multi().
+ *
+ * @param context A Kerberos 5 context
+ * @param s the configuration section to free
+ *
+ * @return returns 0 on successes, otherwise an error code, see
+ * krb5_get_error_message()
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_config_file_free (
krb5_context /*context*/,
krb5_config_section */*s*/);
+/**
+ * Free the resulting strings from krb5_config-get_strings() and
+ * krb5_config_vget_strings().
+ *
+ * @param strings strings to free
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_config_free_strings (char **/*strings*/);
+/**
+ * Like krb5_config_get_bool() but with a va_list list of
+ * configuration selection.
+ *
+ * Configuration value to a boolean value, where yes/true and any
+ * non-zero number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_config_get_bool (
krb5_context /*context*/,
const krb5_config_section */*c*/,
...);
+/**
+ * krb5_config_get_bool_default() will convert the configuration
+ * option value to a boolean value, where yes/true and any non-zero
+ * number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_config_get_bool_default (
krb5_context /*context*/,
@@ -962,18 +1933,60 @@ krb5_config_get_int_default (
int /*def_value*/,
...);
+/**
+ * Get a list of configuration binding list for more processing
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return NULL if configuration list is not found, a list otherwise
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
krb5_config_get_list (
krb5_context /*context*/,
const krb5_config_section */*c*/,
...);
+/**
+ * Returns a "const char *" to a string in the configuration database.
+ * The string may not be valid after a reload of the configuration
+ * database so a caller should make a local copy if it needs to keep
+ * the string.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return NULL if configuration string not found, a string otherwise
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_config_get_string (
krb5_context /*context*/,
const krb5_config_section */*c*/,
...);
+/**
+ * Like krb5_config_get_string(), but instead of returning NULL,
+ * instead return a default value.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return a configuration string
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_config_get_string_default (
krb5_context /*context*/,
@@ -981,18 +1994,57 @@ krb5_config_get_string_default (
const char */*def_value*/,
...);
+/**
+ * Get a list of configuration strings, free the result with
+ * krb5_config_free_strings().
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION char** KRB5_LIB_CALL
krb5_config_get_strings (
krb5_context /*context*/,
const krb5_config_section */*c*/,
...);
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return parsed the time or -1 on error
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_config_get_time (
krb5_context /*context*/,
const krb5_config_section */*c*/,
...);
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param ... a list of names, terminated with NULL.
+ *
+ * @return parsed the time (or def_value on parse error)
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_config_get_time_default (
krb5_context /*context*/,
@@ -1000,18 +2052,43 @@ krb5_config_get_time_default (
int /*def_value*/,
...);
+/**
+ * If the fname starts with "~/" parse configuration file in the
+ * current users home directory. The behavior can be disabled and
+ * enabled by calling krb5_set_home_dir_access().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_config_parse_file (
krb5_context /*context*/,
const char */*fname*/,
krb5_config_section **/*res*/);
+/**
+ * Parse a configuration file and add the result into res. This
+ * interface can be used to parse several configuration files into one
+ * resulting krb5_config_section by calling it repeatably.
+ *
+ * @param context a Kerberos 5 context.
+ * @param fname a file name to a Kerberos configuration file
+ * @param res the returned result, must be free with krb5_free_config_files().
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_config_parse_file_multi (
krb5_context /*context*/,
const char */*fname*/,
krb5_config_section **/*res*/);
+/**
+ * Deprecated: configuration files are not strings
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_config_parse_string_multi (
krb5_context /*context*/,
@@ -1019,12 +2096,44 @@ krb5_config_parse_string_multi (
krb5_config_section **/*res*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * krb5_config_get_bool() will convert the configuration
+ * option value to a boolean value, where yes/true and any non-zero
+ * number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_config_vget_bool (
krb5_context /*context*/,
const krb5_config_section */*c*/,
va_list /*args*/);
+/**
+ * Like krb5_config_get_bool_default() but with a va_list list of
+ * configuration selection.
+ *
+ * Configuration value to a boolean value, where yes/true and any
+ * non-zero number means TRUE and other value is FALSE.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_config_vget_bool_default (
krb5_context /*context*/,
@@ -1045,18 +2154,57 @@ krb5_config_vget_int_default (
int /*def_value*/,
va_list /*args*/);
+/**
+ * Get a list of configuration binding list for more processing
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return NULL if configuration list is not found, a list otherwise
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION const krb5_config_binding * KRB5_LIB_CALL
krb5_config_vget_list (
krb5_context /*context*/,
const krb5_config_section */*c*/,
va_list /*args*/);
+/**
+ * Like krb5_config_get_string(), but uses a va_list instead of ...
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return NULL if configuration string not found, a string otherwise
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_config_vget_string (
krb5_context /*context*/,
const krb5_config_section */*c*/,
va_list /*args*/);
+/**
+ * Like krb5_config_vget_string(), but instead of returning NULL,
+ * instead return a default value.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return a configuration string
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_config_vget_string_default (
krb5_context /*context*/,
@@ -1064,18 +2212,60 @@ krb5_config_vget_string_default (
const char */*def_value*/,
va_list /*args*/);
+/**
+ * Get a list of configuration strings, free the result with
+ * krb5_config_free_strings().
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return TRUE or FALSE
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION char ** KRB5_LIB_CALL
krb5_config_vget_strings (
krb5_context /*context*/,
const krb5_config_section */*c*/,
va_list /*args*/);
+/**
+ * Get the time from the configuration file using a relative time, for example: 1h30s
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param args a va_list of arguments
+ *
+ * @return parsed the time or -1 on error
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_config_vget_time (
krb5_context /*context*/,
const krb5_config_section */*c*/,
va_list /*args*/);
+/**
+ * Get the time from the configuration file using a relative time.
+ *
+ * Like krb5_config_get_time_default() but with a va_list list of
+ * configuration selection.
+ *
+ * @param context A Kerberos 5 context.
+ * @param c a configuration section, or NULL to use the section from context
+ * @param def_value the default value to return if no configuration
+ * found in the database.
+ * @param args a va_list of arguments
+ *
+ * @return parsed the time (or def_value on parse error)
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_config_vget_time_default (
krb5_context /*context*/,
@@ -1083,12 +2273,38 @@ krb5_config_vget_time_default (
int /*def_value*/,
va_list /*args*/);
+/**
+ * krb5_copy_address copies the content of address
+ * inaddr to outaddr.
+ *
+ * @param context a Keberos context
+ * @param inaddr pointer to source address
+ * @param outaddr pointer to destination address
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_address (
krb5_context /*context*/,
const krb5_address */*inaddr*/,
krb5_address */*outaddr*/);
+/**
+ * krb5_copy_addresses copies the content of addresses
+ * inaddr to outaddr.
+ *
+ * @param context a Keberos context
+ * @param inaddr pointer to source addresses
+ * @param outaddr pointer to destination addresses
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_addresses (
krb5_context /*context*/,
@@ -1101,53 +2317,169 @@ krb5_copy_checksum (
const krb5_checksum */*old*/,
krb5_checksum **/*new*/);
+/**
+ * Make a copy for the Kerberos 5 context, the new krb5_context shoud
+ * be freed with krb5_free_context().
+ *
+ * @param context the Kerberos context to copy
+ * @param out the copy of the Kerberos, set to NULL error.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_context (
krb5_context /*context*/,
krb5_context */*out*/);
+/**
+ * Copy krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param incred source credential
+ * @param outcred destination credential, free with krb5_free_creds().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_creds (
krb5_context /*context*/,
const krb5_creds */*incred*/,
krb5_creds **/*outcred*/);
+/**
+ * Copy content of krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param incred source credential
+ * @param c destination credential, free with krb5_free_cred_contents().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_creds_contents (
krb5_context /*context*/,
const krb5_creds */*incred*/,
krb5_creds */*c*/);
+/**
+ * Copy the data into a newly allocated krb5_data.
+ *
+ * @param context Kerberos 5 context.
+ * @param indata the krb5_data data to copy
+ * @param outdata new krb5_date to copy too. Free with krb5_free_data().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_data (
krb5_context /*context*/,
const krb5_data */*indata*/,
krb5_data **/*outdata*/);
+/**
+ * Copy the list of realms from `from' to `to'.
+ *
+ * @param context Kerberos 5 context.
+ * @param from list of realms to copy from.
+ * @param to list of realms to copy to, free list of krb5_free_host_realm().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_host_realm (
krb5_context /*context*/,
const krb5_realm */*from*/,
krb5_realm **/*to*/);
+/**
+ * Copy a keyblock, free the output keyblock with
+ * krb5_free_keyblock().
+ *
+ * @param context a Kerberos 5 context
+ * @param inblock the key to copy
+ * @param to the output key.
+ *
+ * @return 0 on success or a Kerberos 5 error code
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_keyblock (
krb5_context /*context*/,
const krb5_keyblock */*inblock*/,
krb5_keyblock **/*to*/);
+/**
+ * Copy a keyblock, free the output keyblock with
+ * krb5_free_keyblock_contents().
+ *
+ * @param context a Kerberos 5 context
+ * @param inblock the key to copy
+ * @param to the output key.
+ *
+ * @return 0 on success or a Kerberos 5 error code
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_keyblock_contents (
krb5_context /*context*/,
const krb5_keyblock */*inblock*/,
krb5_keyblock */*to*/);
+/**
+ * Copy a principal
+ *
+ * @param context A Kerberos context.
+ * @param inprinc principal to copy
+ * @param outprinc copied principal, free with krb5_free_principal()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_principal (
krb5_context /*context*/,
krb5_const_principal /*inprinc*/,
krb5_principal */*outprinc*/);
+/**
+ * Copy ticket and content
+ *
+ * @param context a Kerberos 5 context
+ * @param from ticket to copy
+ * @param to new copy of ticket, free with krb5_free_ticket()
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_copy_ticket (
krb5_context /*context*/,
@@ -1164,6 +2496,20 @@ krb5_create_checksum (
size_t /*len*/,
Checksum */*result*/);
+/**
+ * Create a Kerberos message checksum.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param type output data
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_create_checksum_iov (
krb5_context /*context*/,
@@ -1173,14 +2519,52 @@ krb5_create_checksum_iov (
unsigned int /*num_data*/,
krb5_cksumtype */*type*/);
+/**
+ * Returns the ticket flags for the credentials in creds.
+ * See also krb5_ticket_get_flags().
+ *
+ * @param creds credential to get ticket flags from
+ *
+ * @return ticket flags
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
krb5_creds_get_ticket_flags (krb5_creds */*creds*/);
+/**
+ * Free a crypto context created by krb5_crypto_init().
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to free
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_crypto_destroy (
krb5_context /*context*/,
krb5_crypto /*crypto*/);
+/**
+ * The FX-CF2 key derivation function, used in FAST and preauth framework.
+ *
+ * @param context Kerberos 5 context
+ * @param crypto1 first key to combine
+ * @param crypto2 second key to combine
+ * @param pepper1 factor to combine with first key to garante uniqueness
+ * @param pepper2 factor to combine with second key to garante uniqueness
+ * @param enctype the encryption type of the resulting key
+ * @param res allocated key, free with krb5_free_keyblock_contents()
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_crypto_fx_cf2 (
krb5_context /*context*/,
@@ -1197,30 +2581,96 @@ krb5_crypto_get_checksum_type (
krb5_crypto /*crypto*/,
krb5_cksumtype */*type*/);
+/**
+ * Return the blocksize used algorithm referenced by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param blocksize the resulting blocksize
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_crypto_getblocksize (
krb5_context /*context*/,
krb5_crypto /*crypto*/,
size_t */*blocksize*/);
+/**
+ * Return the confounder size used by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param confoundersize the returned confounder size
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_crypto_getconfoundersize (
krb5_context /*context*/,
krb5_crypto /*crypto*/,
size_t */*confoundersize*/);
+/**
+ * Return the encryption type used by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param enctype the resulting encryption type
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_crypto_getenctype (
krb5_context /*context*/,
krb5_crypto /*crypto*/,
krb5_enctype */*enctype*/);
+/**
+ * Return the padding size used by the crypto context
+ *
+ * @param context Kerberos context
+ * @param crypto crypto context to query
+ * @param padsize the return padding size
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_crypto_getpadsize (
krb5_context /*context*/,
krb5_crypto /*crypto*/,
size_t */*padsize*/);
+/**
+ * Create a crypto context used for all encryption and signature
+ * operation. The encryption type to use is taken from the key, but
+ * can be overridden with the enctype parameter. This can be useful
+ * for encryptions types which is compatiable (DES for example).
+ *
+ * To free the crypto context, use krb5_crypto_destroy().
+ *
+ * @param context Kerberos context
+ * @param key the key block information with all key data
+ * @param etype the encryption type
+ * @param crypto the resulting crypto context
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_crypto_init (
krb5_context /*context*/,
@@ -1260,35 +2710,113 @@ krb5_crypto_prf_length (
krb5_enctype /*type*/,
size_t */*length*/);
+/**
+ * Allocate data of and krb5_data.
+ *
+ * @param p krb5_data to allocate.
+ * @param len size to allocate.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_data_alloc (
krb5_data */*p*/,
int /*len*/);
+/**
+ * Compare to data.
+ *
+ * @param data1 krb5_data to compare
+ * @param data2 krb5_data to compare
+ *
+ * @return return the same way as memcmp(), useful when sorting.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_data_cmp (
const krb5_data */*data1*/,
const krb5_data */*data2*/);
+/**
+ * Copy the data of len into the krb5_data.
+ *
+ * @param p krb5_data to copy into.
+ * @param data data to copy..
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_data_copy (
krb5_data */*p*/,
const void */*data*/,
size_t /*len*/);
+/**
+ * Compare to data not exposing timing information from the checksum data
+ *
+ * @param data1 krb5_data to compare
+ * @param data2 krb5_data to compare
+ *
+ * @return returns zero for same data, otherwise non zero.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_data_ct_cmp (
const krb5_data */*data1*/,
const krb5_data */*data2*/);
+/**
+ * Free the content of krb5_data structure, its ok to free a zeroed
+ * structure (with memset() or krb5_data_zero()). When done, the
+ * structure will be zeroed. The same function is called
+ * krb5_free_data_contents() in MIT Kerberos.
+ *
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_data_free (krb5_data */*p*/);
+/**
+ * Grow (or shrink) the content of krb5_data to a new size.
+ *
+ * @param p krb5_data to free.
+ * @param len new size.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_data_realloc (
krb5_data */*p*/,
int /*len*/);
+/**
+ * Reset the (potentially uninitalized) krb5_data structure.
+ *
+ * @param p krb5_data to reset.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_data_zero (krb5_data */*p*/);
@@ -1387,6 +2915,27 @@ krb5_decrypt_EncryptedData (
const EncryptedData */*e*/,
krb5_data */*result*/);
+/**
+ * Inline decrypt a Kerberos message.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param ivec initial cbc/cts vector
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ *
+ * 1. KRB5_CRYPTO_TYPE_HEADER
+ * 2. one KRB5_CRYPTO_TYPE_DATA and array [0,...] of KRB5_CRYPTO_TYPE_SIGN_ONLY in
+ * any order, however the receiver have to aware of the
+ * order. KRB5_CRYPTO_TYPE_SIGN_ONLY is commonly used unencrypoted
+ * protocol headers and trailers. The output data will be of same
+ * size as the input data or shorter.
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_decrypt_iov_ivec (
krb5_context /*context*/,
@@ -1477,6 +3026,19 @@ krb5_digest_init_request (
krb5_realm /*realm*/,
krb5_ccache /*ccache*/);
+/**
+ * Get the supported/allowed mechanism for this principal.
+ *
+ * @param context A Keberos context.
+ * @param realm The realm of the KDC.
+ * @param ccache The credential cache to use when talking to the KDC.
+ * @param flags The supported mechanism.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_digest
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_digest_probe (
krb5_context /*context*/,
@@ -1614,6 +3176,17 @@ krb5_domain_x500_encode (
unsigned int /*num_realms*/,
krb5_data */*encoding*/);
+/**
+ * Convert the getaddrinfo() error code to a Kerberos et error code.
+ *
+ * @param eai_errno contains the error code from getaddrinfo().
+ * @param system_error should have the value of errno after the failed getaddrinfo().
+ *
+ * @return Kerberos error code representing the EAI errors.
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_eai_to_heim_errno (
int /*eai_errno*/,
@@ -1710,6 +3283,30 @@ krb5_encrypt_EncryptedData (
int /*kvno*/,
EncryptedData */*result*/);
+/**
+ * Inline encrypt a kerberos message
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param ivec initial cbc/cts vector
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ *
+ * Kerberos encrypted data look like this:
+ *
+ * 1. KRB5_CRYPTO_TYPE_HEADER
+ * 2. array [1,...] KRB5_CRYPTO_TYPE_DATA and array [0,...]
+ * KRB5_CRYPTO_TYPE_SIGN_ONLY in any order, however the receiver
+ * have to aware of the order. KRB5_CRYPTO_TYPE_SIGN_ONLY is
+ * commonly used headers and trailers.
+ * 3. KRB5_CRYPTO_TYPE_PADDING, at least on padsize long if padsize > 1
+ * 4. KRB5_CRYPTO_TYPE_TRAILER
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_encrypt_iov_ivec (
krb5_context /*context*/,
@@ -1729,11 +3326,33 @@ krb5_encrypt_ivec (
krb5_data */*result*/,
void */*ivec*/);
+/**
+ * Disable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to disable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_enctype_disable (
krb5_context /*context*/,
krb5_enctype /*enctype*/);
+/**
+ * Enable encryption type
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to enable
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_enctype_enable (
krb5_context /*context*/,
@@ -1763,11 +3382,27 @@ krb5_enctype_to_string (
krb5_enctype /*etype*/,
char **/*string*/);
+/**
+ * Check if a enctype is valid, return 0 if it is.
+ *
+ * @param context Kerberos context
+ * @param etype enctype to check if its valid or not
+ *
+ * @return Return an error code for an failure or 0 on success (enctype valid).
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_enctype_valid (
krb5_context /*context*/,
krb5_enctype /*etype*/);
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_enctypes_compatible_keys (
krb5_context /*context*/,
@@ -1778,6 +3413,18 @@ krb5_enctypes_compatible_keys (
krb5_error_code
krb5_enomem (krb5_context /*context*/);
+/**
+ * Log a warning to the log, default stderr, include bthe error from
+ * the last failure and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param code error code of the last error
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_err (
krb5_context /*context*/,
@@ -1785,7 +3432,7 @@ krb5_err (
krb5_error_code /*code*/,
const char */*fmt*/,
...)
- __attribute__ ((noreturn, format (printf, 4, 5)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 4, 5)));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_error_from_rd_error (
@@ -1793,13 +3440,38 @@ krb5_error_from_rd_error (
const krb5_error */*error*/,
const krb5_creds */*creds*/);
+/**
+ * Log a warning to the log, default stderr, and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_errx (
krb5_context /*context*/,
int /*eval*/,
const char */*fmt*/,
...)
- __attribute__ ((noreturn, format (printf, 3, 4)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)));
+
+/**
+ * krb5_expand_hostname() tries to make orig_hostname into a more
+ * canonical one in the newly allocated space returned in
+ * new_hostname.
+
+ * @param context a Keberos context
+ * @param orig_hostname hostname to canonicalise.
+ * @param new_hostname output hostname, caller must free hostname with
+ * krb5_xfree().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_support
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_expand_hostname (
@@ -1807,6 +3479,24 @@ krb5_expand_hostname (
const char */*orig_hostname*/,
char **/*new_hostname*/);
+/**
+ * krb5_expand_hostname_realms() expands orig_hostname to a name we
+ * believe to be a hostname in newly allocated space in new_hostname
+ * and return the realms new_hostname is believed to belong to in
+ * realms.
+ *
+ * @param context a Keberos context
+ * @param orig_hostname hostname to canonicalise.
+ * @param new_hostname output hostname, caller must free hostname with
+ * krb5_xfree().
+ * @param realms output possible realms, is an array that is terminated
+ * with NULL. Caller must free with krb5_free_host_realm().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_expand_hostname_realms (
krb5_context /*context*/,
@@ -1829,11 +3519,35 @@ krb5_format_time (
size_t /*len*/,
krb5_boolean /*include_time*/);
+/**
+ * krb5_free_address frees the data stored in the address that is
+ * alloced with any of the krb5_address functions.
+ *
+ * @param context a Keberos context
+ * @param address addresss to be freed.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_address (
krb5_context /*context*/,
krb5_address */*address*/);
+/**
+ * krb5_free_addresses frees the data stored in the address that is
+ * alloced with any of the krb5_address functions.
+ *
+ * @param context a Keberos context
+ * @param addresses addressses to be freed.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_addresses (
krb5_context /*context*/,
@@ -1859,33 +3573,103 @@ krb5_free_checksum_contents (
krb5_context /*context*/,
krb5_checksum */*cksum*/);
+/**
+ * Free a list of configuration files.
+ *
+ * @param filenames list, terminated with a NULL pointer, to be
+ * freed. NULL is an valid argument.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_config_files (char **/*filenames*/);
+/**
+ * Frees the krb5_context allocated by krb5_init_context().
+ *
+ * @param context context to be freed.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_context (krb5_context /*context*/);
+/**
+ * Free content of krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param c krb5_creds to free.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_cred_contents (
krb5_context /*context*/,
krb5_creds */*c*/);
+/**
+ * Free krb5_creds.
+ *
+ * @param context Kerberos 5 context.
+ * @param c krb5_creds to free.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_creds (
krb5_context /*context*/,
krb5_creds */*c*/);
+/**
+ * Deprecated: use krb5_free_cred_contents()
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_creds_contents (
krb5_context /*context*/,
krb5_creds */*c*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Free krb5_data (and its content).
+ *
+ * @param context Kerberos 5 context.
+ * @param p krb5_data to free.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_data (
krb5_context /*context*/,
krb5_data */*p*/);
+/**
+ * Same as krb5_data_free(). MIT compat.
+ *
+ * Deprecated: use krb5_data_free().
+ *
+ * @param context Kerberos 5 context.
+ * @param data krb5_data to free.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_data_contents (
krb5_context /*context*/,
@@ -1907,32 +3691,89 @@ krb5_free_error_contents (
krb5_context /*context*/,
krb5_error */*error*/);
+/**
+ * Free the error message returned by krb5_get_error_message().
+ *
+ * @param context Kerberos context
+ * @param msg error message to free, returned byg
+ * krb5_get_error_message().
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_error_message (
krb5_context /*context*/,
const char */*msg*/);
+/**
+ * Free the error message returned by krb5_get_error_string().
+ *
+ * Deprecated: use krb5_free_error_message()
+ *
+ * @param context Kerberos context
+ * @param str error message to free
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_error_string (
krb5_context /*context*/,
char */*str*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Free all memory allocated by `realmlist'
+ *
+ * @param context A Kerberos 5 context.
+ * @param realmlist realmlist to free, NULL is ok
+ *
+ * @return a Kerberos error code, always 0.
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_host_realm (
krb5_context /*context*/,
krb5_realm */*realmlist*/);
+/**
+ * Variable containing the FILE based credential cache implemention.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_kdc_rep (
krb5_context /*context*/,
krb5_kdc_rep */*rep*/);
+/**
+ * Free a keyblock, also zero out the content of the keyblock, uses
+ * krb5_free_keyblock_contents() to free the content.
+ *
+ * @param context a Kerberos 5 context
+ * @param keyblock keyblock to free, NULL is valid argument
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_keyblock (
krb5_context /*context*/,
krb5_keyblock */*keyblock*/);
+/**
+ * Free a keyblock's content, also zero out the content of the keyblock.
+ *
+ * @param context a Kerberos 5 context
+ * @param keyblock keyblock content to free, NULL is valid argument
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_keyblock_contents (
krb5_context /*context*/,
@@ -1943,6 +3784,28 @@ krb5_free_krbhst (
krb5_context /*context*/,
char **/*hostlist*/);
+/**
+ * Free a name canonicalization rule iterator.
+ */
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_name_canon_iterator (
+ krb5_context /*context*/,
+ krb5_name_canon_iterator /*iter*/);
+
+/**
+ * Frees a Kerberos principal allocated by the library with
+ * krb5_parse_name(), krb5_make_principal() or any other related
+ * principal functions.
+ *
+ * @param context A Kerberos context.
+ * @param p a principal to free.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_principal (
krb5_context /*context*/,
@@ -1953,17 +3816,54 @@ krb5_free_salt (
krb5_context /*context*/,
krb5_salt /*salt*/);
+/**
+ * Free ticket and content
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to free
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_free_ticket (
krb5_context /*context*/,
krb5_ticket */*ticket*/);
+/**
+ * Deprecated: use krb5_xfree().
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_free_unparsed_name (
krb5_context /*context*/,
char */*str*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Forward credentials for client to host hostname , making them
+ * forwardable if forwardable, and returning the blob of data to sent
+ * in out_data. If hostname == NULL, pick it from server.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context the auth context with the key to encrypt the out_data.
+ * @param hostname the host to forward the tickets too.
+ * @param client the client to delegate from.
+ * @param server the server to delegate the credential too.
+ * @param ccache credential cache to use.
+ * @param forwardable make the forwarded ticket forwabledable.
+ * @param out_data the resulting credential.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_fwd_tgt_creds (
krb5_context /*context*/,
@@ -1975,6 +3875,46 @@ krb5_fwd_tgt_creds (
int /*forwardable*/,
krb5_data */*out_data*/);
+/**
+ * Fill buffer buf with len bytes of PRNG randomness that is ok to use
+ * for key generation, padding and public diclosing the randomness w/o
+ * disclosing the randomness source.
+ *
+ * This function can fail, and callers must check the return value.
+ *
+ * @param buf a buffer to fill with randomness
+ * @param len length of memory that buf points to.
+ *
+ * @return return 0 on success or HEIM_ERR_RANDOM_OFFLINE if the
+ * funcation failed to initialize the randomness source.
+ *
+ * @ingroup krb5_crypto
+ */
+
+HEIMDAL_WARN_UNUSED_RESULT_ATTRIBUTE KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_generate_random (
+ void */*buf*/,
+ size_t /*len*/);
+
+/**
+ * Fill buffer buf with len bytes of PRNG randomness that is ok to use
+ * for key generation, padding and public diclosing the randomness w/o
+ * disclosing the randomness source.
+ *
+ * This function can NOT fail, instead it will abort() and program will crash.
+ *
+ * If this function is called after a successful krb5_init_context(),
+ * the chance of it failing is low due to that krb5_init_context()
+ * pulls out some random, and quite commonly the randomness sources
+ * will not fail once it have started to produce good output,
+ * /dev/urandom behavies that way.
+ *
+ * @param buf a buffer to fill with randomness
+ * @param len length of memory that buf points to.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_generate_random_block (
void */*buf*/,
@@ -1992,6 +3932,12 @@ krb5_generate_seq_number (
const krb5_keyblock */*key*/,
uint32_t */*seqno*/);
+/**
+ * Deprecated: use krb5_generate_subkey_extended()
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_generate_subkey (
krb5_context /*context*/,
@@ -1999,6 +3945,19 @@ krb5_generate_subkey (
krb5_keyblock **/*subkey*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Generate subkey, from keyblock
+ *
+ * @param context kerberos context
+ * @param key session key
+ * @param etype encryption type of subkey, if ETYPE_NULL, use key's enctype
+ * @param subkey returned new, free with krb5_free_keyblock().
+ *
+ * @return 0 on success or a Kerberos 5 error code
+ *
+* @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_generate_subkey_extended (
krb5_context /*context*/,
@@ -2016,6 +3975,12 @@ krb5_get_all_server_addrs (
krb5_context /*context*/,
krb5_addresses */*res*/);
+/**
+ * Deprecated: use krb5_get_credentials_with_flags().
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_cred_from_kdc (
krb5_context /*context*/,
@@ -2025,6 +3990,12 @@ krb5_get_cred_from_kdc (
krb5_creds ***/*ret_tgts*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: use krb5_get_credentials_with_flags().
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_cred_from_kdc_opt (
krb5_context /*context*/,
@@ -2100,9 +4071,35 @@ krb5_get_creds_opt_set_ticket (
krb5_get_creds_opt /*opt*/,
const Ticket */*ticket*/);
+/**
+ * Get the global configuration list.
+ *
+ * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_default_config_files (char ***/*pfilenames*/);
+/**
+ * Get the default encryption types that will be use in communcation
+ * with the KDC, clients and servers.
+ *
+ * @param context Kerberos 5 context.
+ * @param pdu_type request type (AS, TGS or none)
+ * @param etypes Encryption types, array terminated with
+ * ETYPE_NULL(0), caller should free array with krb5_xfree():
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_default_in_tkt_etypes (
krb5_context /*context*/,
@@ -2124,33 +4121,135 @@ krb5_get_default_realms (
krb5_context /*context*/,
krb5_realm **/*realms*/);
+/**
+ * Get if the library uses DNS to canonicalize hostnames.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return return non zero if the library uses DNS to canonicalize hostnames.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_get_dns_canonicalize_hostname (krb5_context /*context*/);
+/**
+ * Return the error string for the error code. The caller must not
+ * free the string.
+ *
+ * This function is deprecated since its not threadsafe.
+ *
+ * @param context Kerberos 5 context.
+ * @param code Kerberos error code.
+ *
+ * @return the error message matching code
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_get_err_text (
krb5_context /*context*/,
krb5_error_code /*code*/)
- KRB5_DEPRECATED_FUNCTION("Use X instead");
+ KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead");
+
+/**
+ * Return the error message for `code' in context. On memory
+ * allocation error the function returns NULL.
+ *
+ * @param context Kerberos 5 context
+ * @param code Error code related to the error
+ *
+ * @return an error string, needs to be freed with
+ * krb5_free_error_message(). The functions return NULL on error.
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
krb5_get_error_message (
krb5_context /*context*/,
krb5_error_code /*code*/);
+/**
+ * Return the error message in context. On error or no error string,
+ * the function returns NULL.
+ *
+ * @param context Kerberos 5 context
+ *
+ * @return an error string, needs to be freed with
+ * krb5_free_error_message(). The functions return NULL on error.
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION char * KRB5_LIB_CALL
-krb5_get_error_string (krb5_context /*context*/);
+krb5_get_error_string (krb5_context /*context*/)
+ KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead");
+
+/**
+ * Get extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to set
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_extra_addresses (
krb5_context /*context*/,
krb5_addresses */*addresses*/);
+/**
+ * Get version of fcache that the library should use.
+ *
+ * @param context Kerberos 5 context.
+ * @param version version number.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_fcache_version (
krb5_context /*context*/,
int */*version*/);
+/**
+ * Gets tickets forwarded to hostname. If the tickets that are
+ * forwarded are address-less, the forwarded tickets will also be
+ * address-less.
+ *
+ * If the ticket have any address, hostname will be used for figure
+ * out the address to forward the ticket too. This since this might
+ * use DNS, its insecure and also doesn't represent configured all
+ * addresses of the host. For example, the host might have two
+ * adresses, one IPv4 and one IPv6 address where the later is not
+ * published in DNS. This IPv6 address might be used communications
+ * and thus the resulting ticket useless.
+ *
+ * @param context A kerberos 5 context.
+ * @param auth_context the auth context with the key to encrypt the out_data.
+ * @param ccache credential cache to use
+ * @param flags the flags to control the resulting ticket flags
+ * @param hostname the host to forward the tickets too.
+ * @param in_creds the in client and server ticket names. The client
+ * and server components forwarded to the remote host.
+ * @param out_data the resulting credential.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_forwarded_creds (
krb5_context /*context*/,
@@ -2167,6 +4266,19 @@ krb5_get_host_realm (
const char */*targethost*/,
krb5_realm **/*realms*/);
+/**
+ * Get extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses list addreses ignored
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_ignore_addresses (
krb5_context /*context*/,
@@ -2204,6 +4316,12 @@ krb5_get_in_tkt (
krb5_kdc_rep */*ret_as_reply*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: use krb5_get_init_creds() and friends.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_in_tkt_with_keytab (
krb5_context /*context*/,
@@ -2217,6 +4335,12 @@ krb5_get_in_tkt_with_keytab (
krb5_kdc_rep */*ret_as_reply*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: use krb5_get_init_creds() and friends.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_in_tkt_with_password (
krb5_context /*context*/,
@@ -2230,6 +4354,12 @@ krb5_get_in_tkt_with_password (
krb5_kdc_rep */*ret_as_reply*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: use krb5_get_init_creds() and friends.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_in_tkt_with_skey (
krb5_context /*context*/,
@@ -2243,6 +4373,12 @@ krb5_get_in_tkt_with_skey (
krb5_kdc_rep */*ret_as_reply*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Get new credentials using keyblock.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_keyblock (
krb5_context /*context*/,
@@ -2253,6 +4389,12 @@ krb5_get_init_creds_keyblock (
const char */*in_tkt_service*/,
krb5_get_init_creds_opt */*options*/);
+/**
+ * Get new credentials using keytab.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_keytab (
krb5_context /*context*/,
@@ -2263,16 +4405,36 @@ krb5_get_init_creds_keytab (
const char */*in_tkt_service*/,
krb5_get_init_creds_opt */*options*/);
+/**
+ * Allocate a new krb5_get_init_creds_opt structure, free with
+ * krb5_get_init_creds_opt_free().
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_opt_alloc (
krb5_context /*context*/,
krb5_get_init_creds_opt **/*opt*/);
+/**
+ * Free krb5_get_init_creds_opt structure.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_init_creds_opt_free (
krb5_context /*context*/,
krb5_get_init_creds_opt */*opt*/);
+/**
+ * Deprecated: use the new krb5_init_creds_init() and
+ * krb5_init_creds_get_error().
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_opt_get_error (
krb5_context /*context*/,
@@ -2280,6 +4442,16 @@ krb5_get_init_creds_opt_get_error (
KRB_ERROR **/*error*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: use krb5_get_init_creds_opt_alloc().
+ *
+ * The reason krb5_get_init_creds_opt_init() is deprecated is that
+ * krb5_get_init_creds_opt is a static structure and for ABI reason it
+ * can't grow, ie can't add new functionality.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_init_creds_opt_init (krb5_get_init_creds_opt */*opt*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
@@ -2307,6 +4479,11 @@ krb5_get_init_creds_opt_set_canonicalize (
krb5_boolean /*req*/);
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_get_init_creds_opt_set_change_password_prompt (
+ krb5_get_init_creds_opt */*opt*/,
+ int /*change_password_prompt*/);
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_get_init_creds_opt_set_default_flags (
krb5_context /*context*/,
const char */*appname*/,
@@ -2396,6 +4573,12 @@ krb5_get_init_creds_opt_set_win2k (
krb5_get_init_creds_opt */*opt*/,
krb5_boolean /*req*/);
+/**
+ * Get new credentials using password.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_init_creds_password (
krb5_context /*context*/,
@@ -2418,6 +4601,18 @@ krb5_get_kdc_cred (
krb5_creds */*in_creds*/,
krb5_creds **out_creds );
+/**
+ * Get current offset in time to the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param sec seconds part of offset.
+ * @param usec micro seconds part of offset.
+ *
+ * @return returns zero
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_kdc_sec_offset (
krb5_context /*context*/,
@@ -2448,9 +4643,29 @@ krb5_get_krbhst (
const krb5_realm */*realm*/,
char ***/*hostlist*/);
+/**
+ * Get max time skew allowed.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return timeskew in seconds.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
krb5_get_max_time_skew (krb5_context /*context*/);
+/**
+ * krb5_init_context() will get one random byte to make sure our
+ * random is alive. Assumption is that once the non blocking
+ * source allows us to pull bytes, its all seeded and allows us to
+ * pull more bytes.
+ *
+ * Most Kerberos users calls krb5_init_context(), so this is
+ * useful point where we can do the checking.
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_permitted_enctypes (
krb5_context /*context*/,
@@ -2476,9 +4691,33 @@ krb5_get_server_rcache (
const krb5_data */*piece*/,
krb5_rcache */*id*/);
+/**
+ * Make the kerberos library default to the admin KDC.
+ *
+ * @param context Kerberos 5 context.
+ *
+ * @return boolean flag to telling the context will use admin KDC as the default KDC.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_get_use_admin_kdc (krb5_context /*context*/);
+/**
+ * Validate the newly fetch credential, see also krb5_verify_init_creds().
+ *
+ * @param context a Kerberos 5 context
+ * @param creds the credentials to verify
+ * @param client the client name to match up
+ * @param ccache the credential cache to use
+ * @param service a service name to use, used with
+ * krb5_sname_to_principal() to build a hostname to use to
+ * verify.
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_get_validated_creds (
krb5_context /*context*/,
@@ -2487,6 +4726,14 @@ krb5_get_validated_creds (
krb5_ccache /*ccache*/,
char */*service*/);
+/**
+ * Get the default logging facility.
+ *
+ * @param context A Kerberos 5 context
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_log_facility * KRB5_LIB_CALL
krb5_get_warn_dest (krb5_context /*context*/);
@@ -2503,6 +4750,20 @@ krb5_getportbyname (
const char */*proto*/,
int /*default_port*/);
+/**
+ * krb5_h_addr2addr works like krb5_h_addr2sockaddr with the exception
+ * that it operates on a krb5_address instead of a struct sockaddr.
+ *
+ * @param context a Keberos context
+ * @param af address family
+ * @param haddr host address from struct hostent.
+ * @param addr returned krb5_address.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_h_addr2addr (
krb5_context /*context*/,
@@ -2510,6 +4771,25 @@ krb5_h_addr2addr (
const char */*haddr*/,
krb5_address */*addr*/);
+/**
+ * krb5_h_addr2sockaddr initializes a "struct sockaddr sa" from af and
+ * the "struct hostent" (see gethostbyname(3) ) h_addr_list
+ * component. The argument sa_size should initially contain the size
+ * of the sa, and after the call, it will contain the actual length of
+ * the address.
+ *
+ * @param context a Keberos context
+ * @param af addresses
+ * @param addr address
+ * @param sa returned struct sockaddr
+ * @param sa_size size of sa
+ * @param port port to set in sa.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_h_addr2sockaddr (
krb5_context /*context*/,
@@ -2519,11 +4799,23 @@ krb5_h_addr2sockaddr (
krb5_socklen_t */*sa_size*/,
int /*port*/);
+/**
+ * Convert the gethostname() error code (h_error) to a Kerberos et
+ * error code.
+ *
+ * @param eai_errno contains the error code from gethostname().
+ *
+ * @return Kerberos error code representing the gethostname errors.
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_h_errno_to_heim_errno (int /*eai_errno*/);
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
-krb5_have_error_string (krb5_context /*context*/);
+krb5_have_error_string (krb5_context /*context*/)
+ KRB5_DEPRECATED_FUNCTION("Use krb5_get_error_message instead");
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_hmac (
@@ -2535,31 +4827,102 @@ krb5_hmac (
krb5_keyblock */*key*/,
Checksum */*result*/);
+/**
+ * Initializes the context structure and reads the configuration file
+ * /etc/krb5.conf. The structure should be freed by calling
+ * krb5_free_context() when it is no longer being used.
+ *
+ * @param context pointer to returned context
+ *
+ * @return Returns 0 to indicate success. Otherwise an errno code is
+ * returned. Failure means either that something bad happened during
+ * initialization (typically ENOMEM) or that Kerberos should not be
+ * used ENXIO. If the function returns HEIM_ERR_RANDOM_OFFLINE, the
+ * random source is not available and later Kerberos calls might fail.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_context (krb5_context */*context*/);
+/**
+ * Free the krb5_init_creds_context allocated by krb5_init_creds_init().
+ *
+ * @param context A Kerberos 5 context.
+ * @param ctx The krb5_init_creds_context to free.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_init_creds_free (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/);
+/**
+ * Get new credentials as setup by the krb5_init_creds_context.
+ *
+ * @param context A Kerberos 5 context.
+ * @param ctx The krb5_init_creds_context to process.
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/);
+/**
+ * Extract the newly acquired credentials from krb5_init_creds_context
+ * context.
+ *
+ * @param context A Kerberos 5 context.
+ * @param ctx
+ * @param cred credentials, free with krb5_free_cred_contents().
+ *
+ * @return 0 for sucess or An Kerberos error code, see krb5_get_error_message().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get_creds (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/,
krb5_creds */*cred*/);
+/**
+ * Get the last error from the transaction.
+ *
+ * @return Returns 0 or an error code
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_get_error (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/,
KRB_ERROR */*error*/);
+/**
+ * Start a new context to get a new initial credential.
+ *
+ * @param context A Kerberos 5 context.
+ * @param client The Kerberos principal to get the credential for, if
+ * NULL is given, the default principal is used as determined by
+ * krb5_get_default_principal().
+ * @param prompter
+ * @param prompter_data
+ * @param start_time the time the ticket should start to be valid or 0 for now.
+ * @param options a options structure, can be NULL for default options.
+ * @param rctx A new allocated free with krb5_init_creds_free().
+ *
+ * @return 0 for success or an Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_init (
krb5_context /*context*/,
@@ -2571,29 +4934,98 @@ krb5_init_creds_init (
krb5_init_creds_context */*rctx*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_init_creds_set_fast_ap_armor_service (
+ krb5_context /*context*/,
+ krb5_init_creds_context /*ctx*/,
+ krb5_const_principal /*armor_service*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_init_creds_set_fast_ccache (
+ krb5_context /*context*/,
+ krb5_init_creds_context /*ctx*/,
+ krb5_ccache /*fast_ccache*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_keyblock (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/,
krb5_keyblock */*keyblock*/);
+/**
+ * Set the keytab to use for authentication.
+ *
+ * @param context a Kerberos 5 context.
+ * @param ctx ctx krb5_init_creds_context context.
+ * @param keytab the keytab to read the key from.
+ *
+ * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_keytab (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/,
krb5_keytab /*keytab*/);
+/**
+ * Sets the password that will use for the request.
+ *
+ * @param context a Kerberos 5 context.
+ * @param ctx ctx krb5_init_creds_context context.
+ * @param password the password to use.
+ *
+ * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_password (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/,
const char */*password*/);
+/**
+ * Sets the service that the is requested. This call is only neede for
+ * special initial tickets, by default the a krbtgt is fetched in the default realm.
+ *
+ * @param context a Kerberos 5 context.
+ * @param ctx a krb5_init_creds_context context.
+ * @param service the service given as a string, for example
+ * "kadmind/admin". If NULL, the default krbtgt in the clients
+ * realm is set.
+ *
+ * @return 0 for success, or an Kerberos 5 error code, see krb5_get_error_message().
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_set_service (
krb5_context /*context*/,
krb5_init_creds_context /*ctx*/,
const char */*service*/);
+/**
+ * The core loop if krb5_get_init_creds() function family. Create the
+ * packets and have the caller send them off to the KDC.
+ *
+ * If the caller want all work been done for them, use
+ * krb5_init_creds_get() instead.
+ *
+ * @param context a Kerberos 5 context.
+ * @param ctx ctx krb5_init_creds_context context.
+ * @param in input data from KDC, first round it should be reset by krb5_data_zer().
+ * @param out reply to KDC.
+ * @param hostinfo KDC address info, first round it can be NULL.
+ * @param flags status of the round, if
+ * KRB5_INIT_CREDS_STEP_FLAG_CONTINUE is set, continue one more round.
+ *
+ * @return 0 for success, or an Kerberos 5 error code, see
+ * krb5_get_error_message().
+ *
+ * @ingroup krb5_credential
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_init_creds_step (
krb5_context /*context*/,
@@ -2603,20 +5035,115 @@ krb5_init_creds_step (
krb5_krbhst_info */*hostinfo*/,
unsigned int */*flags*/);
+/**
+ *
+ * @ingroup krb5_credential
+ */
+
+krb5_error_code
+krb5_init_creds_store (
+ krb5_context /*context*/,
+ krb5_init_creds_context /*ctx*/,
+ krb5_ccache /*id*/);
+
+/**
+ * Init the built-in ets in the Kerberos library.
+ *
+ * @param context kerberos context to add the ets too
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_init_ets (krb5_context /*context*/);
+/**
+ @struct krb5plugin_kuserok_ftable_desc *
+ * @brief Description of the krb5_kuserok(3) plugin facility.
+ *
+ * The krb5_kuserok(3) function is pluggable. The plugin is named
+ * KRB5_PLUGIN_KUSEROK ("krb5_plugin_kuserok"), with a single minor
+ * version, KRB5_PLUGIN_KUSEROK_VERSION_0 (0).
+ *
+ * The plugin for krb5_kuserok(3) consists of a data symbol referencing
+ * a structure of type krb5plugin_kuserok_ftable, with four fields:
+ *
+ * @param init Plugin initialization function (see krb5-plugin(7))
+ *
+ * @param minor_version The plugin minor version number (0)
+ *
+ * @param fini Plugin finalization function
+ *
+ * @param kuserok Plugin kuserok function
+ *
+ * The kuserok field is the plugin entry point that performs the
+ * traditional kuserok operation however the plugin desires. It is
+ * invoked in no particular order relative to other kuserok plugins, but
+ * it has a 'rule' argument that indicates which plugin is intended to
+ * act on the rule. The plugin kuserok function must return
+ * KRB5_PLUGIN_NO_HANDLE if the rule is not applicable to it.
+ *
+ * The plugin kuserok function has the following arguments, in this
+ * order:
+ *
+ * -# plug_ctx, the context value output by the plugin's init function
+ * -# context, a krb5_context
+ * -# rule, the kuserok rule being evaluated (from krb5.conf(5))
+ * -# flags
+ * -# k5login_dir, configured location of k5login per-user files if any
+ * -# luser, name of the local user account to which principal is attempting to access.
+ * -# principal, the krb5_principal trying to access the luser account
+ * -# result, a krb5_boolean pointer where the plugin will output its result
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_initlog (
krb5_context /*context*/,
const char */*program*/,
krb5_log_facility **/*fac*/);
+/**
+ * Return TRUE (non zero) if the principal is a configuration
+ * principal (generated part of krb5_cc_set_config()). Returns FALSE
+ * (zero) if not a configuration principal.
+ *
+ * @param context a Keberos context
+ * @param principal principal to check if it a configuration principal
+ *
+ * @ingroup krb5_ccache
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_is_config_principal (
krb5_context /*context*/,
krb5_const_principal /*principal*/);
+/**
+ * Returns is the encryption is strong or weak
+ *
+ * @param context Kerberos 5 context
+ * @param enctype encryption type to probe
+ *
+ * @return Returns true if encryption type is weak or is not supported.
+ *
+ * @ingroup krb5_crypto
+ */
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+krb5_is_enctype_weak (
+ krb5_context /*context*/,
+ krb5_enctype /*enctype*/);
+
+/**
+ * Runtime check if the Kerberos library was complied with thread support.
+ *
+ * @return TRUE if the library was compiled with thread support, FALSE if not.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_is_thread_safe (void);
@@ -2633,12 +5160,40 @@ krb5_kcm_storage_request (
uint16_t /*opcode*/,
krb5_storage **/*storage_p*/);
+/**
+ * Returns the list of Kerberos encryption types sorted in order of
+ * most preferred to least preferred encryption type. Note that some
+ * encryption types might be disabled, so you need to check with
+ * krb5_enctype_valid() before using the encryption type.
+ *
+ * @return list of enctypes, terminated with ETYPE_NULL. Its a static
+ * array completed into the Kerberos library so the content doesn't
+ * need to be freed.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION const krb5_enctype * KRB5_LIB_CALL
krb5_kerberos_enctypes (krb5_context /*context*/);
+/**
+ * Get encryption type of a keyblock.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_enctype KRB5_LIB_CALL
krb5_keyblock_get_enctype (const krb5_keyblock */*block*/);
+/**
+ * Fill in `key' with key data of type `enctype' from `data' of length
+ * `size'. Key should be freed using krb5_free_keyblock_contents().
+ *
+ * @return 0 on success or a Kerberos 5 error code
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_keyblock_init (
krb5_context /*context*/,
@@ -2655,9 +5210,23 @@ krb5_keyblock_key_proc (
krb5_const_pointer /*keyseed*/,
krb5_keyblock **/*key*/);
+/**
+ * Zero out a keyblock
+ *
+ * @param keyblock keyblock to zero out
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_keyblock_zero (krb5_keyblock */*keyblock*/);
+/**
+ * Deprecated: use krb5_get_init_creds() and friends.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_CALLCONV
krb5_keytab_key_proc (
krb5_context /*context*/,
@@ -2667,6 +5236,12 @@ krb5_keytab_key_proc (
krb5_keyblock **/*key*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_keytype_to_enctypes (
krb5_context /*context*/,
@@ -2675,6 +5250,12 @@ krb5_keytype_to_enctypes (
krb5_enctype **/*val*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_keytype_to_enctypes_default (
krb5_context /*context*/,
@@ -2683,6 +5264,13 @@ krb5_keytype_to_enctypes_default (
krb5_enctype **/*val*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes in
+ * most cases, use krb5_enctype_to_string().
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_keytype_to_string (
krb5_context /*context*/,
@@ -2702,6 +5290,16 @@ krb5_krbhst_free (
krb5_context /*context*/,
krb5_krbhst_handle /*handle*/);
+/**
+ * Return an `struct addrinfo *' for a KDC host.
+ *
+ * Returns an the struct addrinfo in in that corresponds to the
+ * information in `host'. free:ing is handled by krb5_krbhst_free, so
+ * the returned ai must not be released.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_krbhst_get_addrinfo (
krb5_context /*context*/,
@@ -2742,16 +5340,62 @@ krb5_krbhst_reset (
krb5_krbhst_handle /*handle*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_set_hostname (
+ krb5_context /*context*/,
+ krb5_krbhst_handle /*handle*/,
+ const char */*hostname*/);
+
+/**
+ * Add the entry in `entry' to the keytab `id'.
+ *
+ * @param context a Keberos context.
+ * @param id a keytab.
+ * @param entry the entry to add
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_add_entry (
krb5_context /*context*/,
krb5_keytab /*id*/,
krb5_keytab_entry */*entry*/);
+/**
+ * Finish using the keytab in `id'. All resources will be released,
+ * even on errors.
+ *
+ * @param context a Keberos context.
+ * @param id keytab to close.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_close (
krb5_context /*context*/,
krb5_keytab /*id*/);
+/**
+ * Compare `entry' against `principal, vno, enctype'.
+ * Any of `principal, vno, enctype' might be 0 which acts as a wildcard.
+ * Return TRUE if they compare the same, FALSE otherwise.
+ *
+ * @param context a Keberos context.
+ * @param entry an entry to match with.
+ * @param principal principal to match, NULL matches all principals.
+ * @param vno key version to match, 0 matches all key version numbers.
+ * @param enctype encryption type to match, 0 matches all encryption types.
+ *
+ * @return Return TRUE or match, FALSE if not matched.
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_kt_compare (
krb5_context /*context*/,
@@ -2760,45 +5404,143 @@ krb5_kt_compare (
krb5_kvno /*vno*/,
krb5_enctype /*enctype*/);
+/**
+ * Copy the contents of `in' into `out'.
+ *
+ * @param context a Keberos context.
+ * @param in the keytab entry to copy.
+ * @param out the copy of the keytab entry, free with krb5_kt_free_entry().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_copy_entry_contents (
krb5_context /*context*/,
const krb5_keytab_entry */*in*/,
krb5_keytab_entry */*out*/);
+/**
+ * Set `id' to the default keytab.
+ *
+ * @param context a Keberos context.
+ * @param id the new default keytab.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_default (
krb5_context /*context*/,
krb5_keytab */*id*/);
+/**
+ * Copy the name of the default modify keytab into `name'.
+ *
+ * @param context a Keberos context.
+ * @param name buffer where the name will be written
+ * @param namesize length of name
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_default_modify_name (
krb5_context /*context*/,
char */*name*/,
size_t /*namesize*/);
+/**
+ * copy the name of the default keytab into `name'.
+ *
+ * @param context a Keberos context.
+ * @param name buffer where the name will be written
+ * @param namesize length of name
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_default_name (
krb5_context /*context*/,
char */*name*/,
size_t /*namesize*/);
+/**
+ * Destroy (remove) the keytab in `id'. All resources will be released,
+ * even on errors, does the equvalment of krb5_kt_close() on the resources.
+ *
+ * @param context a Keberos context.
+ * @param id keytab to destroy.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_destroy (
krb5_context /*context*/,
krb5_keytab /*id*/);
+/**
+ * Release all resources associated with `cursor'.
+ *
+ * @param context a Keberos context.
+ * @param id a keytab.
+ * @param cursor the cursor to free.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_end_seq_get (
krb5_context /*context*/,
krb5_keytab /*id*/,
krb5_kt_cursor */*cursor*/);
+/**
+ * Free the contents of `entry'.
+ *
+ * @param context a Keberos context.
+ * @param entry the entry to free
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_free_entry (
krb5_context /*context*/,
krb5_keytab_entry */*entry*/);
+/**
+ * Retrieve the keytab entry for `principal, kvno, enctype' into `entry'
+ * from the keytab `id'. Matching is done like krb5_kt_compare().
+ *
+ * @param context a Keberos context.
+ * @param id a keytab.
+ * @param principal principal to match, NULL matches all principals.
+ * @param kvno key version to match, 0 matches all key version numbers.
+ * @param enctype encryption type to match, 0 matches all encryption types.
+ * @param entry the returned entry, free with krb5_kt_free_entry().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_get_entry (
krb5_context /*context*/,
@@ -2808,12 +5550,39 @@ krb5_kt_get_entry (
krb5_enctype /*enctype*/,
krb5_keytab_entry */*entry*/);
+/**
+ * Retrieve the full name of the keytab `keytab' and store the name in
+ * `str'.
+ *
+ * @param context a Keberos context.
+ * @param keytab keytab to get name for.
+ * @param str the name of the keytab name, usee krb5_xfree() to free
+ * the string. On error, *str is set to NULL.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_get_full_name (
krb5_context /*context*/,
krb5_keytab /*keytab*/,
char **/*str*/);
+/**
+ * Retrieve the name of the keytab `keytab' into `name', `namesize'
+ *
+ * @param context a Keberos context.
+ * @param keytab the keytab to get the name for.
+ * @param name name buffer.
+ * @param namesize size of name buffer.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_get_name (
krb5_context /*context*/,
@@ -2821,6 +5590,20 @@ krb5_kt_get_name (
char */*name*/,
size_t /*namesize*/);
+/**
+ * Return the type of the `keytab' in the string `prefix of length
+ * `prefixsize'.
+ *
+ * @param context a Keberos context.
+ * @param keytab the keytab to get the prefix for
+ * @param prefix prefix buffer
+ * @param prefixsize length of prefix buffer
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_get_type (
krb5_context /*context*/,
@@ -2828,11 +5611,36 @@ krb5_kt_get_type (
char */*prefix*/,
size_t /*prefixsize*/);
-KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+/**
+ * Return true if the keytab exists and have entries
+ *
+ * @param context a Keberos context.
+ * @param id a keytab.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_have_content (
krb5_context /*context*/,
krb5_keytab /*id*/);
+/**
+ * Get the next entry from keytab, advance the cursor. On last entry
+ * the function will return KRB5_KT_END.
+ *
+ * @param context a Keberos context.
+ * @param id a keytab.
+ * @param entry the returned entry, free with krb5_kt_free_entry().
+ * @param cursor the cursor of the iteration.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_next_entry (
krb5_context /*context*/,
@@ -2840,6 +5648,22 @@ krb5_kt_next_entry (
krb5_keytab_entry */*entry*/,
krb5_kt_cursor */*cursor*/);
+/**
+ * Read the key identified by `(principal, vno, enctype)' from the
+ * keytab in `keyprocarg' (the default if == NULL) into `*key'.
+ *
+ * @param context a Keberos context.
+ * @param keyprocarg
+ * @param principal
+ * @param vno
+ * @param enctype
+ * @param key
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_read_service_key (
krb5_context /*context*/,
@@ -2849,29 +5673,113 @@ krb5_kt_read_service_key (
krb5_enctype /*enctype*/,
krb5_keyblock **/*key*/);
+/**
+ * Register a new keytab backend.
+ *
+ * @param context a Keberos context.
+ * @param ops a backend to register.
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_register (
krb5_context /*context*/,
const krb5_kt_ops */*ops*/);
+/**
+ * Remove an entry from the keytab, matching is done using
+ * krb5_kt_compare().
+
+ * @param context a Keberos context.
+ * @param id a keytab.
+ * @param entry the entry to remove
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_remove_entry (
krb5_context /*context*/,
krb5_keytab /*id*/,
krb5_keytab_entry */*entry*/);
+/**
+ * Resolve the keytab name (of the form `type:residual') in `name'
+ * into a keytab in `id'.
+ *
+ * @param context a Keberos context.
+ * @param name name to resolve
+ * @param id resulting keytab, free with krb5_kt_close().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_resolve (
krb5_context /*context*/,
const char */*name*/,
krb5_keytab */*id*/);
+/**
+ * Set `cursor' to point at the beginning of `id'.
+ *
+ * @param context a Keberos context.
+ * @param id a keytab.
+ * @param cursor a newly allocated cursor, free with krb5_kt_end_seq_get().
+ *
+ * @return Return an error code or 0, see krb5_get_error_message().
+ *
+ * @ingroup krb5_keytab
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_kt_start_seq_get (
krb5_context /*context*/,
krb5_keytab /*id*/,
krb5_kt_cursor */*cursor*/);
+/**
+ * This function takes the name of a local user and checks if
+ * principal is allowed to log in as that user.
+ *
+ * The user may have a ~/.k5login file listing principals that are
+ * allowed to login as that user. If that file does not exist, all
+ * principals with a only one component that is identical to the
+ * username, and a realm considered local, are allowed access.
+ *
+ * The .k5login file must contain one principal per line, be owned by
+ * user and not be writable by group or other (but must be readable by
+ * anyone).
+ *
+ * Note that if the file exists, no implicit access rights are given
+ * to user@@LOCALREALM.
+ *
+ * Optionally, a set of files may be put in ~/.k5login.d (a
+ * directory), in which case they will all be checked in the same
+ * manner as .k5login. The files may be called anything, but files
+ * starting with a hash (#) , or ending with a tilde (~) are
+ * ignored. Subdirectories are not traversed. Note that this directory
+ * may not be checked by other Kerberos implementations.
+ *
+ * If no configuration file exists, match user against local domains,
+ * ie luser@@LOCAL-REALMS-IN-CONFIGURATION-FILES.
+ *
+ * @param context Kerberos 5 context.
+ * @param principal principal to check if allowed to login
+ * @param luser local user id
+ *
+ * @return returns TRUE if access should be granted, FALSE otherwise.
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_kuserok (
krb5_context /*context*/,
@@ -2885,7 +5793,7 @@ krb5_log (
int /*level*/,
const char */*fmt*/,
...)
- __attribute__((format (printf, 4, 5)));
+ __attribute__ ((__format__ (__printf__, 4, 5)));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_log_msg (
@@ -2895,7 +5803,20 @@ krb5_log_msg (
char **/*reply*/,
const char */*fmt*/,
...)
- __attribute__((format (printf, 5, 6)));
+ __attribute__ ((__format__ (__printf__, 5, 6)));
+
+/**
+ * Create an address of type KRB5_ADDRESS_ADDRPORT from (addr, port)
+ *
+ * @param context a Keberos context
+ * @param res built address from addr/port
+ * @param addr address to use
+ * @param port port to use
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_make_addrport (
@@ -2904,6 +5825,19 @@ krb5_make_addrport (
const krb5_address */*addr*/,
int16_t /*port*/);
+/**
+ * Build a principal using vararg style building
+ *
+ * @param context A Kerberos context.
+ * @param principal returned principal
+ * @param realm realm name
+ * @param ... a list of components ended with NULL.
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_make_principal (
krb5_context /*context*/,
@@ -2911,6 +5845,15 @@ krb5_make_principal (
krb5_const_realm /*realm*/,
...);
+/**
+ * krb5_max_sockaddr_size returns the max size of the .Li struct
+ * sockaddr that the Kerberos library will return.
+ *
+ * @return Return an size_t of the maximum struct sockaddr.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION size_t KRB5_LIB_CALL
krb5_max_sockaddr_size (void);
@@ -2927,6 +5870,19 @@ krb5_mk_error (
krb5_data */*reply*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_error_ext (
+ krb5_context /*context*/,
+ krb5_error_code /*error_code*/,
+ const char */*e_text*/,
+ const krb5_data */*e_data*/,
+ const krb5_principal /*server*/,
+ const PrincipalName */*client_name*/,
+ const Realm */*client_realm*/,
+ time_t */*client_time*/,
+ int */*client_usec*/,
+ krb5_data */*reply*/);
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_mk_priv (
krb5_context /*context*/,
krb5_auth_context /*auth_context*/,
@@ -2978,6 +5934,49 @@ krb5_mk_safe (
krb5_data */*outbuf*/,
krb5_replay_data */*outdata*/);
+/**
+ * Iteratively apply name canon rules, outputing a principal and rule
+ * options each time. Iteration completes when the @iter is NULL on
+ * return or when an error is returned. Callers must free the iterator
+ * if they abandon it mid-way.
+ *
+ * @param context Kerberos context
+ * @param iter name canon rule iterator (input/output)
+ * @param try_princ output principal name
+ * @param rule_opts output rule options
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_name_canon_iterate (
+ krb5_context /*context*/,
+ krb5_name_canon_iterator */*iter*/,
+ krb5_const_principal */*try_princ*/,
+ krb5_name_canon_rule_options */*rule_opts*/);
+
+/**
+ * Initialize name canonicalization iterator.
+ *
+ * @param context Kerberos context
+ * @param in_princ principal name to be canonicalized OR
+ * @param iter output iterator object
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_name_canon_iterator_start (
+ krb5_context /*context*/,
+ krb5_const_principal /*in_princ*/,
+ krb5_name_canon_iterator */*iter*/);
+
+/**
+ * Read \a len bytes from socket \a p_fd into buffer \a buf.
+ * Block until \a len bytes are read or until an error.
+ *
+ * @return If successful, the number of bytes read: \a len.
+ * On end-of-file, 0.
+ * On error, less than 0 (if single-threaded, the error can be found
+ * in the errno global variable).
+ */
+
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
krb5_net_read (
krb5_context /*context*/,
@@ -3011,10 +6010,10 @@ krb5_ntlm_free (
krb5_ntlm /*ntlm*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_ntlm_init_get_challange (
+krb5_ntlm_init_get_challenge (
krb5_context /*context*/,
krb5_ntlm /*ntlm*/,
- krb5_data */*challange*/);
+ krb5_data */*challenge*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ntlm_init_get_flags (
@@ -3131,6 +6130,20 @@ krb5_pac_free (
krb5_context /*context*/,
krb5_pac /*pac*/);
+/**
+ * Get the PAC buffer of specific type from the pac.
+ *
+ * @param context Kerberos 5 context.
+ * @param p the pac structure returned by krb5_pac_parse().
+ * @param type type of buffer to get
+ * @param data return data, free with krb5_data_free().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_pac
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_get_buffer (
krb5_context /*context*/,
@@ -3157,6 +6170,22 @@ krb5_pac_parse (
size_t /*len*/,
krb5_pac */*pac*/);
+/**
+ * Verify the PAC.
+ *
+ * @param context Kerberos 5 context.
+ * @param pac the pac structure returned by krb5_pac_parse().
+ * @param authtime The time of the ticket the PAC belongs to.
+ * @param principal the principal to verify.
+ * @param server The service key, most always be given.
+ * @param privsvr The KDC key, may be given.
+
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5_pac
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_pac_verify (
krb5_context /*context*/,
@@ -3174,18 +6203,56 @@ krb5_padata_add (
void */*buf*/,
size_t /*len*/);
+/**
+ * krb5_parse_address returns the resolved hostname in string to the
+ * krb5_addresses addresses .
+ *
+ * @param context a Keberos context
+ * @param string
+ * @param addresses
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_parse_address (
krb5_context /*context*/,
const char */*string*/,
krb5_addresses */*addresses*/);
+/**
+ * Parse a name into a krb5_principal structure
+ *
+ * @param context Kerberos 5 context
+ * @param name name to parse into a Kerberos principal
+ * @param principal returned principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_parse_name (
krb5_context /*context*/,
const char */*name*/,
krb5_principal */*principal*/);
+/**
+ * Parse a name into a krb5_principal structure, flags controls the behavior.
+ *
+ * @param context Kerberos 5 context
+ * @param name name to parse into a Kerberos principal
+ * @param flags flags to control the behavior
+ * @param principal returned principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_parse_name_flags (
krb5_context /*context*/,
@@ -3193,6 +6260,12 @@ krb5_parse_name_flags (
int /*flags*/,
krb5_principal */*principal*/);
+/**
+ * Parse nametype string and return a nametype integer
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_parse_nametype (
krb5_context /*context*/,
@@ -3204,6 +6277,12 @@ krb5_passwd_result_to_string (
krb5_context /*context*/,
int /*result*/);
+/**
+ * Deprecated: use krb5_get_init_creds() and friends.
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_CALLCONV
krb5_password_key_proc (
krb5_context /*context*/,
@@ -3221,6 +6300,18 @@ krb5_pk_enterprise_cert (
krb5_principal */*principal*/,
struct hx509_certs_data **/*res*/);
+/**
+ * Register a plugin symbol name of specific type.
+ * @param context a Keberos context
+ * @param type type of plugin symbol
+ * @param name name of plugin symbol
+ * @param symbol a pointer to the named symbol
+ * @return In case of error a non zero error com_err error is returned
+ * and the Kerberos error string is set.
+ *
+ * @ingroup krb5_support
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_plugin_register (
krb5_context /*context*/,
@@ -3234,18 +6325,50 @@ krb5_prepend_config_files (
char **/*pq*/,
char ***/*ret_pp*/);
+/**
+ * Prepend the filename to the global configuration list.
+ *
+ * @param filelist a filename to add to the default list of filename
+ * @param pfilenames return array of filenames, should be freed with krb5_free_config_files().
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_prepend_config_files_default (
const char */*filelist*/,
char ***/*pfilenames*/);
+/**
+ * Prepend the context full error string for a specific error code.
+ * The error that is stored should be internationalized.
+ *
+ * The if context is NULL, no error string is stored.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param ... printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_prepend_error_message (
krb5_context /*context*/,
krb5_error_code /*ret*/,
const char */*fmt*/,
...)
- __attribute__ ((format (printf, 3, 4)));
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+/**
+ * Deprecated: use krb5_principal_get_realm()
+ *
+ * @ingroup krb5_deprecated
+ */
KRB5_LIB_FUNCTION krb5_realm * KRB5_LIB_CALL
krb5_princ_realm (
@@ -3253,6 +6376,12 @@ krb5_princ_realm (
krb5_principal /*principal*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Deprecated: use krb5_principal_set_realm()
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_princ_set_realm (
krb5_context /*context*/,
@@ -3260,12 +6389,39 @@ krb5_princ_set_realm (
krb5_realm */*realm*/)
KRB5_DEPRECATED_FUNCTION("Use X instead");
+/**
+ * Compares the two principals, including realm of the principals and returns
+ * TRUE if they are the same and FALSE if not.
+ *
+ * @param context Kerberos 5 context
+ * @param princ1 first principal to compare
+ * @param princ2 second principal to compare
+ *
+ * @ingroup krb5_principal
+ * @see krb5_principal_compare_any_realm()
+ * @see krb5_realm_compare()
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_principal_compare (
krb5_context /*context*/,
krb5_const_principal /*princ1*/,
krb5_const_principal /*princ2*/);
+/**
+ * Return TRUE iff princ1 == princ2 (without considering the realm)
+ *
+ * @param context Kerberos 5 context
+ * @param princ1 first principal to compare
+ * @param princ2 second principal to compare
+ *
+ * @return non zero if equal, 0 if not
+ *
+ * @ingroup krb5_principal
+ * @see krb5_principal_compare()
+ * @see krb5_realm_compare()
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_principal_compare_any_realm (
krb5_context /*context*/,
@@ -3278,26 +6434,126 @@ krb5_principal_get_comp_string (
krb5_const_principal /*principal*/,
unsigned int /*component*/);
+/**
+ * Get number of component is principal.
+ *
+ * @param context Kerberos 5 context
+ * @param principal principal to query
+ *
+ * @return number of components in string
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION unsigned int KRB5_LIB_CALL
krb5_principal_get_num_comp (
krb5_context /*context*/,
krb5_const_principal /*principal*/);
+/**
+ * Get the realm of the principal
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to get the realm for
+ *
+ * @return realm of the principal, don't free or use after krb5_principal is freed
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION const char* KRB5_LIB_CALL
krb5_principal_get_realm (
krb5_context /*context*/,
krb5_const_principal /*principal*/);
+/**
+ * Get the type of the principal
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to get the type for
+ *
+ * @return the type of principal
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_principal_get_type (
krb5_context /*context*/,
krb5_const_principal /*principal*/);
+/**
+ * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_gss_hostbased_service (
+ krb5_context /*context*/,
+ krb5_const_principal /*principal*/);
+
+/**
+ * Check if the cname part of the principal is a krbtgt principal
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_principal_is_krbtgt (
krb5_context /*context*/,
krb5_const_principal /*p*/);
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_lkdc (
+ krb5_context /*context*/,
+ krb5_const_principal /*principal*/);
+
+/**
+ * Returns true if name is Kerberos NULL name
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_null (
+ krb5_context /*context*/,
+ krb5_const_principal /*principal*/);
+
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_pku2u (
+ krb5_context /*context*/,
+ krb5_const_principal /*principal*/);
+
+/**
+ * Check if the cname part of the principal is a initial or renewed krbtgt principal
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_root_krbtgt (
+ krb5_context /*context*/,
+ krb5_const_principal /*p*/);
+
+/**
+ * return TRUE iff princ matches pattern
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_principal_match (
krb5_context /*context*/,
@@ -3305,17 +6561,65 @@ krb5_principal_match (
krb5_const_principal /*pattern*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_principal_set_comp_string (
+ krb5_context /*context*/,
+ krb5_principal /*principal*/,
+ unsigned int /*k*/,
+ const char */*component*/);
+
+/**
+ * Set a new realm for a principal, and as a side-effect free the
+ * previous realm.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal set the realm for
+ * @param realm the new realm to set
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_principal_set_realm (
krb5_context /*context*/,
krb5_principal /*principal*/,
krb5_const_realm /*realm*/);
+/**
+ * Set the type of the principal
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to set the type for
+ * @param type the new type
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_principal_set_type (
krb5_context /*context*/,
krb5_principal /*principal*/,
int /*type*/);
+/**
+ * krb5_print_address prints the address in addr to the string string
+ * that have the length len. If ret_len is not NULL, it will be filled
+ * with the length of the string if size were unlimited (not including
+ * the final NUL) .
+ *
+ * @param addr address to be printed
+ * @param str pointer string to print the address into
+ * @param len length that will fit into area pointed to by "str".
+ * @param ret_len return length the str.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_print_address (
const krb5_address */*addr*/,
@@ -3323,6 +6627,12 @@ krb5_print_address (
size_t /*len*/,
size_t */*ret_len*/);
+krb5_error_code
+krb5_process_last_request (
+ krb5_context /*context*/,
+ krb5_get_init_creds_opt */*options*/,
+ krb5_init_creds_context /*ctx*/);
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_program_setup (
krb5_context */*context*/,
@@ -3341,6 +6651,23 @@ krb5_prompter_posix (
int /*num_prompts*/,
krb5_prompt prompts[]);
+/**
+ * Converts the random bytestring to a protocol key according to
+ * Kerberos crypto frame work. It may be assumed that all the bits of
+ * the input string are equally random, even though the entropy
+ * present in the random source may be limited.
+ *
+ * @param context Kerberos 5 context
+ * @param type the enctype resulting key will be of
+ * @param data input random data to convert to a key
+ * @param size size of input random data, at least krb5_enctype_keysize() long
+ * @param key key, output key, free with krb5_free_keyblock_contents()
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_random_to_key (
krb5_context /*context*/,
@@ -3462,6 +6789,23 @@ krb5_rd_rep (
const krb5_data */*inbuf*/,
krb5_ap_rep_enc_part **/*repl*/);
+/**
+ * Process an AP_REQ message.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context authentication context of the peer.
+ * @param inbuf the AP_REQ message, obtained for example with krb5_read_message().
+ * @param server server principal.
+ * @param keytab server keytab.
+ * @param ap_req_options set to the AP_REQ options. See the AP_OPTS_* defines.
+ * @param ticket on success, set to the authenticated client credentials.
+ * Must be deallocated with krb5_free_ticket(). If not
+ * interested, pass a NULL value.
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req (
krb5_context /*context*/,
@@ -3472,6 +6816,30 @@ krb5_rd_req (
krb5_flags */*ap_req_options*/,
krb5_ticket **/*ticket*/);
+/**
+ * The core server function that verify application authentication
+ * requests from clients.
+ *
+ * @param context Keberos 5 context.
+ * @param auth_context the authentication context, can be NULL, then
+ * default values for the authentication context will used.
+ * @param inbuf the (AP-REQ) authentication buffer
+ *
+ * @param server the server to authenticate to. If NULL the function
+ * will try to find any available credential in the keytab
+ * that will verify the reply. The function will prefer the
+ * server specified in the AP-REQ, but if
+ * there is no mach, it will try all keytab entries for a
+ * match. This has serious performance issues for large keytabs.
+ *
+ * @param inctx control the behavior of the function, if NULL, the
+ * default behavior is used.
+ * @param outctx the return outctx, free with krb5_rd_req_out_ctx_free().
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req_ctx (
krb5_context /*context*/,
@@ -3481,6 +6849,19 @@ krb5_rd_req_ctx (
krb5_rd_req_in_ctx /*inctx*/,
krb5_rd_req_out_ctx */*outctx*/);
+/**
+ * Allocate a krb5_rd_req_in_ctx as an input parameter to
+ * krb5_rd_req_ctx(). The caller should free the context with
+ * krb5_rd_req_in_ctx_free() when done with the context.
+ *
+ * @param context Keberos 5 context.
+ * @param ctx in ctx to krb5_rd_req_ctx().
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req_in_ctx_alloc (
krb5_context /*context*/,
@@ -3497,18 +6878,53 @@ krb5_rd_req_in_set_keyblock (
krb5_rd_req_in_ctx /*in*/,
krb5_keyblock */*keyblock*/);
+/**
+ * Set the keytab that krb5_rd_req_ctx() will use.
+ *
+ * @param context Keberos 5 context.
+ * @param in in ctx to krb5_rd_req_ctx().
+ * @param keytab keytab that krb5_rd_req_ctx() will use, only copy the
+ * pointer, so the caller must free they keytab after
+ * krb5_rd_req_in_ctx_free() is called.
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req_in_set_keytab (
krb5_context /*context*/,
krb5_rd_req_in_ctx /*in*/,
krb5_keytab /*keytab*/);
+/**
+ * Set if krb5_rq_red() is going to check the Windows PAC or not
+ *
+ * @param context Keberos 5 context.
+ * @param in krb5_rd_req_in_ctx to check the option on.
+ * @param flag flag to select if to check the pac (TRUE) or not (FALSE).
+ *
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_auth
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req_in_set_pac_check (
krb5_context /*context*/,
krb5_rd_req_in_ctx /*in*/,
krb5_boolean /*flag*/);
+/**
+ * Free the krb5_rd_req_out_ctx.
+ *
+ * @param context Keberos 5 context.
+ * @param ctx krb5_rd_req_out_ctx context to free.
+ *
+ * @ingroup krb5_auth
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_rd_req_out_ctx_free (
krb5_context /*context*/,
@@ -3526,6 +6942,18 @@ krb5_rd_req_out_get_keyblock (
krb5_rd_req_out_ctx /*out*/,
krb5_keyblock **/*keyblock*/);
+/**
+ * Get the principal that was used in the request from the
+ * client. Might not match whats in the ticket if krb5_rd_req_ctx()
+ * searched in the keytab for a matching key.
+ *
+ * @param context a Kerberos 5 context.
+ * @param out a krb5_rd_req_out_ctx from krb5_rd_req_ctx().
+ * @param principal return principal, free with krb5_free_principal().
+ *
+ * @ingroup krb5_auth
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req_out_get_server (
krb5_context /*context*/,
@@ -3576,12 +7004,52 @@ krb5_read_safe_message (
krb5_pointer /*p_fd*/,
krb5_data */*data*/);
+/**
+ * return TRUE iff realm(princ1) == realm(princ2)
+ *
+ * @param context Kerberos 5 context
+ * @param princ1 first principal to compare
+ * @param princ2 second principal to compare
+ *
+ * @ingroup krb5_principal
+ * @see krb5_principal_compare_any_realm()
+ * @see krb5_principal_compare()
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_realm_compare (
krb5_context /*context*/,
krb5_const_principal /*princ1*/,
krb5_const_principal /*princ2*/);
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_realm_is_lkdc (const char */*realm*/);
+
+/**
+ * Perform the server side of the sendauth protocol.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context authentication context of the peer.
+ * @param p_fd socket associated to the connection.
+ * @param appl_version server-specific string.
+ * @param server server principal.
+ * @param flags if KRB5_RECVAUTH_IGNORE_VERSION is set, skip the sendauth version
+ * part of the protocol.
+ * @param keytab server keytab.
+ * @param ticket on success, set to the authenticated client credentials.
+ * Must be deallocated with krb5_free_ticket(). If not
+ * interested, pass a NULL value.
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_recvauth (
krb5_context /*context*/,
@@ -3593,6 +7061,12 @@ krb5_recvauth (
krb5_keytab /*keytab*/,
krb5_ticket **/*ticket*/);
+/**
+ * Perform the server side of the sendauth protocol like krb5_recvauth(), but support
+ * a user-specified callback, \a match_appl_version, to perform the match of the application
+ * version \a match_data.
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_recvauth_match_version (
krb5_context /*context*/,
@@ -3605,61 +7079,212 @@ krb5_recvauth_match_version (
krb5_keytab /*keytab*/,
krb5_ticket **/*ticket*/);
+/**
+ * Read a address block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param adr the address block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_address (
krb5_storage */*sp*/,
krb5_address */*adr*/);
+/**
+ * Read a addresses block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param adr the addresses block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_addrs (
krb5_storage */*sp*/,
krb5_addresses */*adr*/);
+/**
+ * Read a auth data from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param auth the auth data block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_authdata (
krb5_storage */*sp*/,
krb5_authdata */*auth*/);
+/**
+ * Read a credentials block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the credentials block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_creds (
krb5_storage */*sp*/,
krb5_creds */*creds*/);
+/**
+ * Read a tagged credentials block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the credentials block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_creds_tag (
krb5_storage */*sp*/,
krb5_creds */*creds*/);
+/**
+ * Parse a data from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param data the parsed data
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_data (
krb5_storage */*sp*/,
krb5_data */*data*/);
+/**
+ * Read a int16 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_int16 (
krb5_storage */*sp*/,
int16_t */*value*/);
+/**
+ * Read a int32 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_int32 (
krb5_storage */*sp*/,
int32_t */*value*/);
+/**
+ * Read a int64 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_int64 (
+ krb5_storage */*sp*/,
+ int64_t */*value*/);
+
+/**
+ * Read a int8 from storage
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_int8 (
krb5_storage */*sp*/,
int8_t */*value*/);
+/**
+ * Read a keyblock from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the keyblock read from storage, free using krb5_free_keyblock()
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_keyblock (
krb5_storage */*sp*/,
krb5_keyblock */*p*/);
+/**
+ * Parse principal from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param princ the parsed principal
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_principal (
krb5_storage */*sp*/,
krb5_principal */*princ*/);
+/**
+ * Parse a string from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param string the parsed string
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_string (
krb5_storage */*sp*/,
@@ -3670,26 +7295,100 @@ krb5_ret_stringnl (
krb5_storage */*sp*/,
char **/*string*/);
+/**
+ * Parse zero terminated string from the storage.
+ *
+ * @param sp the storage buffer to read from
+ * @param string the parsed string
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_stringz (
krb5_storage */*sp*/,
char **/*string*/);
+/**
+ * Read a times block from the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param times the times block read from storage
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_times (
krb5_storage */*sp*/,
krb5_times */*times*/);
+/**
+ * Read a int16 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_uint16 (
krb5_storage */*sp*/,
uint16_t */*value*/);
+/**
+ * Read a uint32 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_uint32 (
krb5_storage */*sp*/,
uint32_t */*value*/);
+/**
+ * Read a uint64 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_uint64 (
+ krb5_storage */*sp*/,
+ uint64_t */*value*/);
+
+/**
+ * Read a uint8 from storage
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_uint8 (
krb5_storage */*sp*/,
@@ -3702,6 +7401,30 @@ krb5_salttype_to_string (
krb5_salttype /*stype*/,
char **/*string*/);
+/**
+ * Perform the client side of the sendauth protocol.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context Authentication context of the peer.
+ * @param p_fd Socket associated to the connection.
+ * @param appl_version Server-specific string.
+ * @param client Client principal. If NULL, use the credentials in \a ccache.
+ * @param server Server principal.
+ * @param ap_req_options Options for the AP_REQ message. See the AP_OPTS_* defines in krb5.h.
+ * @param in_data FIXME
+ * @param in_creds FIXME
+ * @param ccache Credentials cache. If NULL, use the default credentials cache.
+ * @param ret_error If not NULL, will be set to the error reported by server, if any.
+ * Must be deallocated with krb5_free_error_contents().
+ * @param rep_result If not NULL, will be set to the EncApRepPart of the AP_REP message.
+ * Must be deallocated with krb5_free_ap_rep_enc_part().
+ * @param out_creds FIXME If not NULL, will be set to FIXME. Must be deallocated with
+ * krb5_free_creds().
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sendauth (
krb5_context /*context*/,
@@ -3730,7 +7453,7 @@ krb5_sendto_context (
krb5_context /*context*/,
krb5_sendto_ctx /*ctx*/,
const krb5_data */*send_data*/,
- const krb5_realm /*realm*/,
+ krb5_const_realm /*realm*/,
krb5_data */*receive*/);
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
@@ -3738,6 +7461,23 @@ krb5_sendto_ctx_add_flags (
krb5_sendto_ctx /*ctx*/,
int /*flags*/);
+/**
+ * @section send_to_kdc Locating and sending packets to the KDC
+ *
+ * The send to kdc code is responsible to request the list of KDC from
+ * the locate-kdc subsystem and then send requests to each of them.
+ *
+ * - Each second a new hostname is tried.
+ * - If the hostname have several addresses, the first will be tried
+ * directly then in turn the other will be tried every 3 seconds
+ * (host_timeout).
+ * - UDP requests are tried 3 times, and it tried with a individual timeout of kdc_timeout / 3.
+ * - TCP and HTTP requests are tried 1 time.
+ *
+ * Total wait time shorter then (number of addresses * 3) + kdc_timeout seconds.
+ *
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sendto_ctx_alloc (
krb5_context /*context*/,
@@ -3778,11 +7518,50 @@ krb5_sendto_kdc_flags (
int /*flags*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_set_hostname (
+ krb5_context /*context*/,
+ krb5_sendto_ctx /*ctx*/,
+ const char */*hostname*/);
+
+/**
+ * Reinit the context from a new set of filenames.
+ *
+ * @param context context to add configuration too.
+ * @param filenames array of filenames, end of list is indicated with a NULL filename.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_config_files (
krb5_context /*context*/,
char **/*filenames*/);
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_debug_dest (
+ krb5_context /*context*/,
+ const char */*program*/,
+ const char */*log_spec*/);
+
+/**
+ * Set the default encryption types that will be use in communcation
+ * with the KDC, clients and servers.
+ *
+ * @param context Kerberos 5 context.
+ * @param etypes Encryption types, array terminated with ETYPE_NULL (0).
+ * A value of NULL resets the encryption types to the defaults set in the
+ * configuration file.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_default_in_tkt_etypes (
krb5_context /*context*/,
const krb5_enctype */*etypes*/);
@@ -3792,57 +7571,186 @@ krb5_set_default_realm (
krb5_context /*context*/,
const char */*realm*/);
+/**
+ * Set if the library should use DNS to canonicalize hostnames.
+ *
+ * @param context Kerberos 5 context.
+ * @param flag if its dns canonicalizion is used or not.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_set_dns_canonicalize_hostname (
krb5_context /*context*/,
krb5_boolean /*flag*/);
+/**
+ * Set the context full error string for a specific error code.
+ * The error that is stored should be internationalized.
+ *
+ * The if context is NULL, no error string is stored.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param ... printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_set_error_message (
krb5_context /*context*/,
krb5_error_code /*ret*/,
const char */*fmt*/,
...)
- __attribute__ ((format (printf, 3, 4)));
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+/**
+ * Set the error message returned by krb5_get_error_string().
+ *
+ * Deprecated: use krb5_get_error_message()
+ *
+ * @param context Kerberos context
+ * @param fmt error message to free
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_deprecated
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_error_string (
krb5_context /*context*/,
const char */*fmt*/,
...)
- __attribute__((format (printf, 2, 3))) KRB5_DEPRECATED_FUNCTION("Use X instead");
+ __attribute__ ((__format__ (__printf__, 2, 3))) KRB5_DEPRECATED_FUNCTION("Use X instead");
+
+/**
+ * Set extra address to the address list that the library will add to
+ * the client's address list when communicating with the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to set
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_extra_addresses (
krb5_context /*context*/,
const krb5_addresses */*addresses*/);
+/**
+ * Set version of fcache that the library should use.
+ *
+ * @param context Kerberos 5 context.
+ * @param version version number.
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_fcache_version (
krb5_context /*context*/,
int /*version*/);
+/**
+ * Enable and disable home directory access on either the global state
+ * or the krb5_context state. By calling krb5_set_home_dir_access()
+ * with context set to NULL, the global state is configured otherwise
+ * the state for the krb5_context is modified.
+ *
+ * For home directory access to be allowed, both the global state and
+ * the krb5_context state have to be allowed.
+ *
+ * @param context a Kerberos 5 context or NULL
+ * @param allow allow if TRUE home directory
+ * @return the old value
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_set_home_dir_access (
krb5_context /*context*/,
krb5_boolean /*allow*/);
+/**
+ * Set extra addresses to ignore when fetching addresses from the
+ * underlaying operating system.
+ *
+ * @param context Kerberos 5 context.
+ * @param addresses addreses to ignore
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_ignore_addresses (
krb5_context /*context*/,
const krb5_addresses */*addresses*/);
+/**
+ * Set current offset in time to the KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param sec seconds part of offset.
+ * @param usec micro seconds part of offset.
+ *
+ * @return returns zero
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_kdc_sec_offset (
krb5_context /*context*/,
int32_t /*sec*/,
int32_t /*usec*/);
+/**
+ * Set max time skew allowed.
+ *
+ * @param context Kerberos 5 context.
+ * @param t timeskew in seconds.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_set_max_time_skew (
krb5_context /*context*/,
time_t /*t*/);
+/**
+ * Change password using creds.
+ *
+ * @param context a Keberos context
+ * @param creds The initial kadmin/passwd for the principal or an admin principal
+ * @param newpw The new password to set
+ * @param targprinc if unset, the default principal is used.
+ * @param result_code Result code, KRB5_KPASSWD_SUCCESS is when password is changed.
+ * @param result_code_string binary message from the server, contains
+ * at least the result_code.
+ * @param result_string A message from the kpasswd service or the
+ * library in human printable form. The string is NUL terminated.
+ *
+ * @return On sucess and *result_code is KRB5_KPASSWD_SUCCESS, the password is changed.
+
+ * @ingroup @krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_password (
krb5_context /*context*/,
@@ -3863,6 +7771,20 @@ krb5_set_password_using_ccache (
krb5_data */*result_code_string*/,
krb5_data */*result_string*/);
+/**
+ * Set the absolute time that the caller knows the kdc has so the
+ * kerberos library can calculate the relative diffrence beteen the
+ * KDC time and local system time.
+ *
+ * @param context Keberos 5 context.
+ * @param sec The applications new of "now" in seconds
+ * @param usec The applications new of "now" in micro seconds
+
+ * @return Kerberos 5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_real_time (
krb5_context /*context*/,
@@ -3875,16 +7797,57 @@ krb5_set_send_to_kdc_func (
krb5_send_to_kdc_func /*func*/,
void */*data*/);
+/**
+ * Make the kerberos library default to the admin KDC.
+ *
+ * @param context Kerberos 5 context.
+ * @param flag boolean flag to select if the use the admin KDC or not.
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_set_use_admin_kdc (
krb5_context /*context*/,
krb5_boolean /*flag*/);
+/**
+ * Set the default logging facility.
+ *
+ * @param context A Kerberos 5 context
+ * @param fac Facility to use for logging.
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_set_warn_dest (
krb5_context /*context*/,
krb5_log_facility */*fac*/);
+/**
+ * Create a principal for the given service running on the given
+ * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
+ * according the configured name canonicalization rules, with
+ * canonicalization delayed in some cases. One rule involves DNS, which
+ * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
+ * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
+ *
+ * Canonicalization is immediate (not delayed) only when there is only
+ * one canonicalization rule and that rule indicates that we should do a
+ * host lookup by name (i.e., DNS).
+ *
+ * @param context A Kerberos context.
+ * @param hostname hostname to use
+ * @param sname Service name to use
+ * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
+ * @param ret_princ return principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sname_to_principal (
krb5_context /*context*/,
@@ -3901,12 +7864,39 @@ krb5_sock_to_principal (
int32_t /*type*/,
krb5_principal */*ret_princ*/);
+/**
+ * krb5_sockaddr2address stores a address a "struct sockaddr" sa in
+ * the krb5_address addr.
+ *
+ * @param context a Keberos context
+ * @param sa a struct sockaddr to extract the address from
+ * @param addr an Kerberos 5 address to store the address in.
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sockaddr2address (
krb5_context /*context*/,
const struct sockaddr */*sa*/,
krb5_address */*addr*/);
+/**
+ * krb5_sockaddr2port extracts a port (if possible) from a "struct
+ * sockaddr.
+ *
+ * @param context a Keberos context
+ * @param sa a struct sockaddr to extract the port from
+ * @param port a pointer to an int16_t store the port in.
+ *
+ * @return Return an error code or 0. Will return
+ * KRB5_PROG_ATYPE_NOSUPP in case address type is not supported.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sockaddr2port (
krb5_context /*context*/,
@@ -3916,6 +7906,18 @@ krb5_sockaddr2port (
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_sockaddr_is_loopback (const struct sockaddr */*sa*/);
+/**
+ * krb5_sockaddr_uninteresting returns TRUE for all .Fa sa that the
+ * kerberos library thinks are uninteresting. One example are link
+ * local addresses.
+ *
+ * @param sa pointer to struct sockaddr that might be interesting.
+ *
+ * @return Return a non zero for uninteresting addresses.
+ *
+ * @ingroup krb5_address
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_sockaddr_uninteresting (const struct sockaddr */*sa*/);
@@ -3925,147 +7927,546 @@ krb5_std_usage (
struct getargs */*args*/,
int /*num_args*/);
+/**
+ * Clear the flags on a storage buffer
+ *
+ * @param sp the storage buffer to clear the flags on
+ * @param flags the flags to clear
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_storage_clear_flags (
krb5_storage */*sp*/,
krb5_flags /*flags*/);
+/**
+ * Create a elastic (allocating) memory storage backend. Memory is
+ * allocated on demand. Free returned krb5_storage with
+ * krb5_storage_free().
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_fd()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_socket()
+ */
+
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
krb5_storage_emem (void);
+/**
+ * Free a krb5 storage.
+ *
+ * @param sp the storage to free.
+ *
+ * @return An Kerberos 5 error code.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_storage_free (krb5_storage */*sp*/);
+/**
+ * Create a fixed size memory storage block
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_mem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_fd()
+ */
+
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
krb5_storage_from_data (krb5_data */*data*/);
+/**
+ *
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_emem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_socket()
+ */
+
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
-krb5_storage_from_fd (krb5_socket_t /*fd_in*/);
+krb5_storage_from_fd (int /*fd_in*/);
+
+/**
+ * Create a fixed size memory storage block
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_fd()
+ * @sa krb5_storage_from_socket()
+ */
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
krb5_storage_from_mem (
void */*buf*/,
size_t /*len*/);
+/**
+ * Create a fixed size memory storage block that is read only
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_mem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_fd()
+ */
+
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
krb5_storage_from_readonly_mem (
const void */*buf*/,
size_t /*len*/);
+/**
+ *
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_emem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_fd()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_from_socket (krb5_socket_t /*sock_in*/);
+
+/**
+ * Sync the storage buffer to its backing store. If there is no
+ * backing store this function will return success.
+ *
+ * @param sp the storage buffer to sync
+ *
+ * @return A Kerberos 5 error code
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_storage_fsync (krb5_storage */*sp*/);
+
+/**
+ * Return the current byteorder for the buffer. See krb5_storage_set_byteorder() for the list or byte order contants.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_flags KRB5_LIB_CALL
krb5_storage_get_byteorder (krb5_storage */*sp*/);
+/**
+ * Get the return code that will be used when end of storage is reached.
+ *
+ * @param sp the storage
+ *
+ * @return storage error code
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_storage_get_eof_code (krb5_storage */*sp*/);
+/**
+ * Return true or false depending on if the storage flags is set or
+ * not. NB testing for the flag 0 always return true.
+ *
+ * @param sp the storage buffer to check flags on
+ * @param flags The flags to test for
+ *
+ * @return true if all the flags are set, false if not.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
krb5_storage_is_flags (
krb5_storage */*sp*/,
krb5_flags /*flags*/);
+/**
+ * Read to the storage buffer.
+ *
+ * @param sp the storage buffer to read from
+ * @param buf the buffer to store the data in
+ * @param len the length to read
+ *
+ * @return The length of data read (can be shorter then len), or negative on error.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
krb5_storage_read (
krb5_storage */*sp*/,
void */*buf*/,
size_t /*len*/);
+/**
+ * Seek to a new offset.
+ *
+ * @param sp the storage buffer to seek in.
+ * @param offset the offset to seek
+ * @param whence relateive searching, SEEK_CUR from the current
+ * position, SEEK_END from the end, SEEK_SET absolute from the start.
+ *
+ * @return The new current offset
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION off_t KRB5_LIB_CALL
krb5_storage_seek (
krb5_storage */*sp*/,
off_t /*offset*/,
int /*whence*/);
+/**
+ * Set the new byte order of the storage buffer.
+ *
+ * @param sp the storage buffer to set the byte order for.
+ * @param byteorder the new byte order.
+ *
+ * The byte order are: KRB5_STORAGE_BYTEORDER_BE,
+ * KRB5_STORAGE_BYTEORDER_LE and KRB5_STORAGE_BYTEORDER_HOST.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_storage_set_byteorder (
krb5_storage */*sp*/,
krb5_flags /*byteorder*/);
+/**
+ * Set the return code that will be used when end of storage is reached.
+ *
+ * @param sp the storage
+ * @param code the error code to return on end of storage
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_storage_set_eof_code (
krb5_storage */*sp*/,
int /*code*/);
+/**
+ * Add the flags on a storage buffer by or-ing in the flags to the buffer.
+ *
+ * @param sp the storage buffer to set the flags on
+ * @param flags the flags to set
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_storage_set_flags (
krb5_storage */*sp*/,
krb5_flags /*flags*/);
+/**
+ * Set the max alloc value
+ *
+ * @param sp the storage buffer set the max allow for
+ * @param size maximum size to allocate, use 0 to remove limit
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_storage_set_max_alloc (
krb5_storage */*sp*/,
size_t /*size*/);
+/**
+ * Copy the contnent of storage
+ *
+ * @param sp the storage to copy to a data
+ * @param data the copied data, free with krb5_data_free()
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_storage_to_data (
krb5_storage */*sp*/,
krb5_data */*data*/);
+/**
+ * Truncate the storage buffer in sp to offset.
+ *
+ * @param sp the storage buffer to truncate.
+ * @param offset the offset to truncate too.
+ *
+ * @return An Kerberos 5 error code.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_storage_truncate (
krb5_storage */*sp*/,
off_t /*offset*/);
+/**
+ * Write to the storage buffer.
+ *
+ * @param sp the storage buffer to write to
+ * @param buf the buffer to write to the storage buffer
+ * @param len the length to write
+ *
+ * @return The length of data written (can be shorter then len), or negative on error.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
krb5_storage_write (
krb5_storage */*sp*/,
const void */*buf*/,
size_t /*len*/);
+/**
+ * Write a address block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the address block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_address (
krb5_storage */*sp*/,
krb5_address /*p*/);
+/**
+ * Write a addresses block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the addresses block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_addrs (
krb5_storage */*sp*/,
krb5_addresses /*p*/);
+/**
+ * Write a auth data block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param auth the auth data block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_authdata (
krb5_storage */*sp*/,
krb5_authdata /*auth*/);
+/**
+ * Write a credentials block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the creds block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_creds (
krb5_storage */*sp*/,
krb5_creds */*creds*/);
+/**
+ * Write a tagged credentials block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param creds the creds block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_creds_tag (
krb5_storage */*sp*/,
krb5_creds */*creds*/);
+/**
+ * Store a data to the storage. The data is stored with an int32 as
+ * lenght plus the data (not padded).
+ *
+ * @param sp the storage buffer to write to
+ * @param data the buffer to store.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_data (
krb5_storage */*sp*/,
krb5_data /*data*/);
+/**
+ * Store a int16 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_int16 (
krb5_storage */*sp*/,
int16_t /*value*/);
+/**
+ * Store a int32 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_int32 (
krb5_storage */*sp*/,
int32_t /*value*/);
+/**
+ * Store a int64 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_int64 (
+ krb5_storage */*sp*/,
+ int64_t /*value*/);
+
+/**
+ * Store a int8 to storage.
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_int8 (
krb5_storage */*sp*/,
int8_t /*value*/);
+/**
+ * Store a keyblock to the storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the keyblock to write
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_keyblock (
krb5_storage */*sp*/,
krb5_keyblock /*p*/);
+/**
+ * Write a principal block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param p the principal block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_principal (
krb5_storage */*sp*/,
krb5_const_principal /*p*/);
+/**
+ * Store a string to the buffer. The data is formated as an len:uint32
+ * plus the string itself (not padded).
+ *
+ * @param sp the storage buffer to write to
+ * @param s the string to store.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_string (
krb5_storage */*sp*/,
@@ -4076,26 +8477,101 @@ krb5_store_stringnl (
krb5_storage */*sp*/,
const char */*s*/);
+/**
+ * Store a zero terminated string to the buffer. The data is stored
+ * one character at a time until a NUL is stored.
+ *
+ * @param sp the storage buffer to write to
+ * @param s the string to store.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_stringz (
krb5_storage */*sp*/,
const char */*s*/);
+/**
+ * Write a times block to storage.
+ *
+ * @param sp the storage buffer to write to
+ * @param times the times block to write.
+ *
+ * @return 0 on success, a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_times (
krb5_storage */*sp*/,
krb5_times /*times*/);
+/**
+ * Store a uint16 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_uint16 (
krb5_storage */*sp*/,
uint16_t /*value*/);
+/**
+ * Store a uint32 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_uint32 (
krb5_storage */*sp*/,
uint32_t /*value*/);
+/**
+ * Store a uint64 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_uint64 (
+ krb5_storage */*sp*/,
+ uint64_t /*value*/);
+
+/**
+ * Store a uint8 to storage.
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_store_uint8 (
krb5_storage */*sp*/,
@@ -4170,6 +8646,13 @@ krb5_string_to_key_salt_opaque (
krb5_data /*opaque*/,
krb5_keyblock */*key*/);
+/**
+ * Deprecated: keytypes doesn't exists, they are really enctypes in
+ * most cases, use krb5_string_to_enctype().
+ *
+ * @ingroup krb5_deprecated
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_string_to_keytype (
krb5_context /*context*/,
@@ -4184,6 +8667,19 @@ krb5_string_to_salttype (
const char */*string*/,
krb5_salttype */*salttype*/);
+/**
+ * Extract the authorization data type of type from the ticket. Store
+ * the field in data. This function is to use for kerberos
+ * applications.
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket Kerberos ticket
+ * @param type type to fetch
+ * @param data returned data, free with krb5_data_free()
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ticket_get_authorization_data_type (
krb5_context /*context*/,
@@ -4191,39 +8687,117 @@ krb5_ticket_get_authorization_data_type (
int /*type*/,
krb5_data */*data*/);
+/**
+ * Return client principal in ticket
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to copy
+ * @param client client principal, free with krb5_free_principal()
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ticket_get_client (
krb5_context /*context*/,
const krb5_ticket */*ticket*/,
krb5_principal */*client*/);
+/**
+ * Return end time of ticket
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to copy
+ *
+ * @return end time of ticket
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION time_t KRB5_LIB_CALL
krb5_ticket_get_endtime (
krb5_context /*context*/,
const krb5_ticket */*ticket*/);
+/**
+ * Get the flags from the Kerberos ticket
+ *
+ * @param context Kerberos context
+ * @param ticket Kerberos ticket
+ *
+ * @return ticket flags
+ *
+ * @ingroup krb5_ticket
+ */
+
KRB5_LIB_FUNCTION unsigned long KRB5_LIB_CALL
krb5_ticket_get_flags (
krb5_context /*context*/,
const krb5_ticket */*ticket*/);
+/**
+ * Return server principal in ticket
+ *
+ * @param context a Kerberos 5 context
+ * @param ticket ticket to copy
+ * @param server server principal, free with krb5_free_principal()
+ *
+ * @return Returns 0 to indicate success. Otherwise an kerberos et
+ * error code is returned, see krb5_get_error_message().
+ *
+ * @ingroup krb5
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ticket_get_server (
krb5_context /*context*/,
const krb5_ticket */*ticket*/,
krb5_principal */*server*/);
+/**
+ * If the caller passes in a negative usec, its assumed to be
+ * unknown and the function will use the current time usec.
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_timeofday (
krb5_context /*context*/,
krb5_timestamp */*timeret*/);
+/**
+ * Unparse the Kerberos name into a string
+ *
+ * @param context Kerberos 5 context
+ * @param principal principal to query
+ * @param name resulting string, free with krb5_xfree()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_unparse_name (
krb5_context /*context*/,
krb5_const_principal /*principal*/,
char **/*name*/);
+/**
+ * Unparse the principal name to a fixed buffer
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param name buffer to write name to
+ * @param len length of buffer
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_unparse_name_fixed (
krb5_context /*context*/,
@@ -4231,6 +8805,20 @@ krb5_unparse_name_fixed (
char */*name*/,
size_t /*len*/);
+/**
+ * Unparse the principal name with unparse flags to a fixed buffer.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param flags unparse flags
+ * @param name buffer to write name to
+ * @param len length of buffer
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_unparse_name_fixed_flags (
krb5_context /*context*/,
@@ -4239,6 +8827,20 @@ krb5_unparse_name_fixed_flags (
char */*name*/,
size_t /*len*/);
+/**
+ * Unparse the principal name to a fixed buffer. The realm is skipped
+ * if its a default realm.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param name buffer to write name to
+ * @param len length of buffer
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_unparse_name_fixed_short (
krb5_context /*context*/,
@@ -4246,6 +8848,19 @@ krb5_unparse_name_fixed_short (
char */*name*/,
size_t /*len*/);
+/**
+ * Unparse the Kerberos name into a string
+ *
+ * @param context Kerberos 5 context
+ * @param principal principal to query
+ * @param flags flag to determine the behavior
+ * @param name resulting string, free with krb5_xfree()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_unparse_name_flags (
krb5_context /*context*/,
@@ -4253,6 +8868,19 @@ krb5_unparse_name_flags (
int /*flags*/,
char **/*name*/);
+/**
+ * Unparse the principal name to a allocated buffer. The realm is
+ * skipped if its a default realm.
+ *
+ * @param context A Kerberos context.
+ * @param principal principal to unparse
+ * @param name returned buffer, free with krb5_xfree()
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_unparse_name_short (
krb5_context /*context*/,
@@ -4265,20 +8893,32 @@ krb5_us_timeofday (
krb5_timestamp */*sec*/,
int32_t */*usec*/);
+/**
+ * Log a warning to the log, default stderr, include bthe error from
+ * the last failure and then abort.
+ *
+ * @param context A Kerberos 5 context
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vabort (
krb5_context /*context*/,
krb5_error_code /*code*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__ ((noreturn, format (printf, 3, 0)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vabortx (
krb5_context /*context*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__ ((noreturn, format (printf, 2, 0)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 0)));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verify_ap_req (
@@ -4319,6 +8959,20 @@ krb5_verify_checksum (
size_t /*len*/,
Checksum */*cksum*/);
+/**
+ * Verify a Kerberos message checksum.
+ *
+ * @param context Kerberos context
+ * @param crypto Kerberos crypto context
+ * @param usage Key usage for this buffer
+ * @param data array of buffers to process
+ * @param num_data length of array
+ * @param type return checksum type if not NULL
+ *
+ * @return Return an error code or 0.
+ * @ingroup krb5_crypto
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verify_checksum_iov (
krb5_context /*context*/,
@@ -4406,6 +9060,19 @@ krb5_verify_user_opt (
const char */*password*/,
krb5_verify_opt */*opt*/);
+/**
+ * Log a warning to the log, default stderr, include bthe error from
+ * the last failure and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verr (
krb5_context /*context*/,
@@ -4413,7 +9080,18 @@ krb5_verr (
krb5_error_code /*code*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__ ((noreturn, format (printf, 4, 0)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 4, 0)));
+
+/**
+ * Log a warning to the log, default stderr, and then exit.
+ *
+ * @param context A Kerberos 5 context
+ * @param eval the exit code to exit with
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verrx (
@@ -4421,7 +9099,7 @@ krb5_verrx (
int /*eval*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__ ((noreturn, format (printf, 3, 0)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vlog (
@@ -4430,7 +9108,7 @@ krb5_vlog (
int /*level*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__((format (printf, 4, 0)));
+ __attribute__ ((__format__ (__printf__, 4, 0)));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vlog_msg (
@@ -4440,7 +9118,20 @@ krb5_vlog_msg (
int /*level*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__((format (printf, 5, 0)));
+ __attribute__ ((__format__ (__printf__, 5, 0)));
+
+/**
+ * Prepend the contexts's full error string for a specific error code.
+ *
+ * The if context is NULL, no error string is stored.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param args printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_vprepend_error_message (
@@ -4448,7 +9139,20 @@ krb5_vprepend_error_message (
krb5_error_code /*ret*/,
const char */*fmt*/,
va_list /*args*/)
- __attribute__ ((format (printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
+
+/**
+ * Set the context full error string for a specific error code.
+ *
+ * The if context is NULL, no error string is stored.
+ *
+ * @param context Kerberos 5 context
+ * @param ret The error code
+ * @param fmt Error string for the error code
+ * @param args printf(3) style parameters.
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_vset_error_message (
@@ -4456,14 +9160,41 @@ krb5_vset_error_message (
krb5_error_code /*ret*/,
const char */*fmt*/,
va_list /*args*/)
- __attribute__ ((format (printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
+
+/**
+ * Set the error message returned by krb5_get_error_string(),
+ * deprecated, use krb5_set_error_message().
+ *
+ * Deprecated: use krb5_vset_error_message()
+ *
+ * @param context Kerberos context
+ * @param fmt error message to free
+ * @param args variable argument list vector
+ *
+ * @return Return an error code or 0.
+ *
+ * @ingroup krb5_deprecated
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vset_error_string (
krb5_context /*context*/,
const char */*fmt*/,
va_list /*args*/)
- __attribute__ ((format (printf, 2, 0))) KRB5_DEPRECATED_FUNCTION("Use X instead");
+ __attribute__ ((__format__ (__printf__, 2, 0))) KRB5_DEPRECATED_FUNCTION("Use X instead");
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vwarn (
@@ -4471,14 +9202,35 @@ krb5_vwarn (
krb5_error_code /*code*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__ ((format (printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ * @param ap arguments
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vwarnx (
krb5_context /*context*/,
const char */*fmt*/,
va_list /*ap*/)
- __attribute__ ((format (printf, 2, 0)));
+ __attribute__ ((__format__ (__printf__, 2, 0)));
+
+/**
+ * Log a warning to the log, default stderr, include the error from
+ * the last failure.
+ *
+ * @param context A Kerberos 5 context.
+ * @param code error code of the last error
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_warn (
@@ -4486,14 +9238,23 @@ krb5_warn (
krb5_error_code /*code*/,
const char */*fmt*/,
...)
- __attribute__ ((format (printf, 3, 4)));
+ __attribute__ ((__format__ (__printf__, 3, 4)));
+
+/**
+ * Log a warning to the log, default stderr.
+ *
+ * @param context A Kerberos 5 context.
+ * @param fmt message to print
+ *
+ * @ingroup krb5_error
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_warnx (
krb5_context /*context*/,
const char */*fmt*/,
...)
- __attribute__ ((format (printf, 2, 3)));
+ __attribute__ ((__format__ (__printf__, 2, 3)));
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_write_message (
@@ -4524,4 +9285,5 @@ krb5_xfree (void */*ptr*/);
#undef KRB5_DEPRECATED_FUNCTION
+#endif /* DOXY */
#endif /* __krb5_protos_h__ */
diff --git a/lib/krb5/krb5.conf.5 b/lib/krb5/krb5.conf.5
index 77d7f808ae47..e7a25af6100b 100644
--- a/lib/krb5/krb5.conf.5
+++ b/lib/krb5/krb5.conf.5
@@ -144,7 +144,7 @@ Default realm to use, this is also known as your
The default is the result of
.Fn krb5_get_host_realm "local hostname" .
.It Li allow_weak_crypto = Va boolean
-is weaks crypto algorithms allowed to be used, among others, DES is
+are weak crypto algorithms allowed to be used, among others, DES is
considered weak.
.It Li clockskew = Va time
Maximum time differential (in seconds) allowed when comparing
@@ -152,11 +152,6 @@ times.
Default is 300 seconds (five minutes).
.It Li kdc_timeout = Va time
Maximum time to wait for a reply from the kdc, default is 3 seconds.
-.It Li v4_name_convert
-.It Li v4_instance_resolve
-These are described in the
-.Xr krb5_425_conv_principal 3
-manual page.
.It Li capath = {
.Bl -tag -width "xxx" -offset indent
.It Va destination-realm Li = Va next-hop-realm
@@ -173,9 +168,9 @@ the default credentials cache name.
If you want to change the type only use
.Li default_cc_type .
The string can contain variables that are expanded on runtime.
-Only support variable now is
+The Only supported variable currently is
.Li %{uid}
-that expands to the current user id.
+which expands to the current user id.
.It Li default_etypes = Va etypes ...
A list of default encryption types to use. (Default: all enctypes if
allow_weak_crypto = TRUE, else all enctypes except single DES enctypes.)
@@ -242,24 +237,172 @@ Scan all network interfaces for addresses, as opposed to simply using
the address associated with the system's host name.
.It Li fcache_version = Va int
Use file credential cache format version specified.
-.It Li krb4_get_tickets = Va boolean
-Also get Kerberos 4 tickets in
-.Nm kinit ,
-.Nm login ,
-and other programs.
-This option is also valid in the [realms] section.
.It Li fcc-mit-ticketflags = Va boolean
Use MIT compatible format for file credential cache.
It's the field ticketflags that is stored in reverse bit order for
older than Heimdal 0.7.
Setting this flag to
.Dv TRUE
-make it store the MIT way, this is default for Heimdal 0.7.
+makes it store the MIT way, this is default for Heimdal 0.7.
.It Li check-rd-req-server
-If set to "ignore", the framework will ignore any the server input to
-.Xr krb5_rd_req 3,
+If set to "ignore", the framework will ignore any of the server input to
+.Xr krb5_rd_req 3 ,
this is very useful when the GSS-API server input the
wrong server name into the gss_accept_sec_context call.
+.It Li k5login_directory = Va directory
+Alternative location for user .k5login files. This option is provided
+for compatibility with MIT krb5 configuration files.
+.It Li k5login_authoritative = Va boolean
+If true then if a principal is not found in k5login files then
+.Xr krb5_userok 3
+will not fallback on principal to username mapping. This option is
+provided for compatibility with MIT krb5 configuration files.
+.It Li kuserok = Va rule ...
+Specifies
+.Xr krb5_userok 3
+behavior. If multiple values are given, then
+.Xr krb5_userok 3
+will evaluate them in order until one succeeds or all fail. Rules are
+implemented by plugins, with three built-in plugins
+described below. Default: USER-K5LOGIN SIMPLE DENY.
+.It Li kuserok = Va DENY
+If set and evaluated then
+.Xr krb5_userok 3
+will deny access to the given username no matter what the principal name
+might be.
+.It Li kuserok = Va SIMPLE
+If set and evaluated then
+.Xr krb5_userok 3
+will use principal to username mapping (see auth_to_local below). If
+the principal maps to the requested username then access is allowed.
+.It Li kuserok = Va SYSTEM-K5LOGIN[:directory]
+If set and evaluated then
+.Xr krb5_userok 3
+will use k5login files named after the
+.Va luser
+argument to
+.Xr krb5_userok 3
+in the given directory or in
+.Pa /etc/k5login.d/ .
+K5login files are text files, with each line containing just a principal
+name; principals apearing in a user's k5login file are permitted access
+to the user's account. Note: this rule performs no ownership nor
+permissions checks on k5login files; proper ownership and
+permissions/ACLs are expected due to the k5login location being a
+system location.
+.It Li kuserok = Va USER-K5LOGIN
+If set and evaluated then
+.Xr krb5_userok 3
+will use
+.Pa ~luser/.k5login
+and
+.Pa ~luser/.k5login.d/* .
+User k5login files and directories must be owned by the user and must
+not have world nor group write permissions.
+.It Li aname2lname-text-db = Va filename
+The named file must be a sorted (in increasing order) text file where
+every line consists of an unparsed principal name optionally followed by
+whitespace and a username. The aname2lname function will do a binary
+search on this file, if configured, looking for lines that match the
+given principal name, and if found the given username will be used, or,
+if the username is missing, an error will be returned. If the file
+doesn't exist, or if no matching line is found then other plugins will
+be allowed to run.
+.It Li fcache_strict_checking
+strict checking in FILE credential caches that owner, no symlink and
+permissions is correct.
+.It Li name_canon_rules = Va rules
+One or more service principal name canonicalization rules. Each rule
+consists of one or more tokens separated by colon (':'). Currently
+these rules are used only for hostname canonicalization (usually when
+getting a service ticket, from a ccache or a TGS, but also when
+acquiring GSS initiator credentials from a keytab). These rules can be
+used to implement DNS resolver-like search lists without having to use
+DNS.
+.Pp
+NOTE: Name canonicalization rules are an experimental feature.
+.Pp
+The first token is a rule type, one of:
+.Va as-is,
+.Va qualify, or
+.Va nss.
+.Pp
+Any remaining tokens must be options tokens:
+.Va use_fast
+(use FAST to protect TGS exchanges; currently not supported),
+.Va use_dnssec
+(use DNSSEC to protect hostname lookups; currently not supported),
+.Va ccache_only
+,
+.Va use_referrals,
+.Va no_referrals,
+.Va lookup_realm,
+.Va mindots=N,
+.Va maxdots=N,
+.Va order=N,
+domain=
+.Va domain,
+realm=
+.Va realm,
+match_domain=
+.Va domain,
+and match_realm=
+.Va realm.
+.Pp
+When trying to obtain a service ticket for a host-based service
+principal name, name canonicalization rules are applied to that name in
+the order given, one by one, until one succeds (a service ticket is
+obtained), or all fail. Similarly when acquiring GSS initiator
+credentials from a keytab, and when comparing a non-canonical GSS name
+to a canonical one.
+.Pp
+For each rule the system checks that the hostname has at least
+.Va mindots
+periods (if given) in it, at most
+.Va maxdots
+periods (if given), that the hostname ends in the given
+.Va match_domain
+(if given),
+and that the realm of the principal matches the
+.Va match_realm
+(if given).
+.Pp
+.Va As-is
+rules leave the hostname unmodified but may set a realm.
+.Va Qualify
+rules qualify the hostname with the given
+.Va domain
+and also may set the realm.
+The
+.Va nss
+rule uses the system resolver to lookup the host's canonical name and is
+usually not secure. Note that using the
+.Va nss
+rule type implies having to have principal aliases in the HDB (though
+not necessarily in keytabs).
+.Pp
+The empty realm denotes "ask the client's realm's TGS". The empty realm
+may be set as well as matched.
+.Pp
+The order in which rules are applied is as follows: first all the rules
+with explicit
+.Va order
+then all other rules in the order in which they appear. If any two
+rules have the same explicit
+.Va order ,
+their order of appearance in krb5.conf breaks the tie. Explicitly
+specifying order can be useful where tools read and write the
+configuration file without preserving parameter order.
+.Pp
+Malformed rules are ignored.
+.It Li allow_hierarchical_capaths = Va boolean
+When validating cross-realm transit paths, absent any explicit capath from the
+client realm to the server realm, allow a hierarchical transit path via the
+common ancestor domain of the two realms.
+Defaults to true.
+Note, absent an explicit setting, hierarchical capaths are always used by
+the KDC when generating a referral to a destination with which is no direct
+trust.
.El
.It Li [domain_realm]
This is a list of mappings from DNS domain to Kerberos realm.
@@ -319,17 +462,65 @@ to the database are performed.
Points to the server where all the password changes are performed.
If there is no such entry, the kpasswd port on the admin_server host
will be tried.
-.It Li krb524_server = Va host[:port]
-Points to the server that does 524 conversions.
-If it is not mentioned, the krb524 port on the kdcs will be tried.
-.It Li v4_instance_convert
-.It Li v4_name_convert
-.It Li default_domain
-See
-.Xr krb5_425_conv_principal 3 .
.It Li tgs_require_subkey
a boolan variable that defaults to false.
Old DCE secd (pre 1.1) might need this to be true.
+.It Li auth_to_local_names = {
+.Bl -tag -width "xxx" -offset indent
+.It Va principal_name = Va username
+The given
+.Va principal_name
+will be mapped to the given
+.Va username
+if the
+.Va REALM
+is a default realm.
+.El
+.It Li }
+.It Li auth_to_local = HEIMDAL_DEFAULT
+Use the Heimdal default principal to username mapping.
+Applies to principals from the
+.Va REALM
+if and only if
+.Va REALM
+is a default realm.
+.It Li auth_to_local = DEFAULT
+Use the MIT default principal to username mapping.
+Applies to principals from the
+.Va REALM
+if and only if
+.Va REALM
+is a default realm.
+.It Li auth_to_local = DB:/path/to/db.txt
+Use a binary search of the given DB. The DB must be a flat-text
+file sortedf in the "C" locale, with each record being a line
+(separated by either LF or CRLF) consisting of a principal name
+followed by whitespace followed by a username.
+Applies to principals from the
+.Va REALM
+if and only if
+.Va REALM
+is a default realm.
+.It Li auth_to_local = DB:/path/to/db
+Use the given DB, if there's a plugin for it.
+Applies to principals from the
+.Va REALM
+if and only if
+.Va REALM
+is a default realm.
+.It Li auth_to_local = RULE:...
+Use the given rule, if there's a plugin for it.
+Applies to principals from the
+.Va REALM
+if and only if
+.Va REALM
+is a default realm.
+.It Li auth_to_local = NONE
+No additional principal to username mapping is done. Note that
+.Va auth_to_local_names
+and any preceding
+.Va auth_to_local
+rules have precedence.
.El
.It Li }
.El
@@ -368,8 +559,10 @@ manual page for a list of defined destinations.
.Bl -tag -width "xxx" -offset indent
.It Li database Li = {
.Bl -tag -width "xxx" -offset indent
-.It Li dbname Li = Va DATABASENAME
-Use this database for this realm.
+.It Li dbname Li = Va [DATBASETYPE:]DATABASENAME
+Use this database for this realm. The
+.Va DATABASETYPE
+should be one of 'lmdb', 'db3', 'db1', 'db', 'sqlite', or 'ldap'.
See the info documetation how to configure different database backends.
.It Li realm Li = Va REALM
Specifies the realm that will be stored in this database.
@@ -388,51 +581,58 @@ Use this file for the ACL list of this database.
Use this file as the log of changes performed to the database.
This file is used by
.Nm ipropd-master
-for propagating changes to slaves.
+for propagating changes to slaves. It is also used by
+.Nm kadmind
+and
+.Nm kadmin
+(when used with the
+.Li -l
+option), and by all applications using
+.Nm libkadm5
+with the local backend, for two-phase commit functionality. Slaves also
+use this. Setting this to
+.Nm /dev/null
+disables two-phase commit and incremental propagation. Use
+.Nm iprop-log
+to show the contents of this log file.
+.It Li log-max-size = Pa number
+When the log reaches this size (in bytes), the log will be truncated,
+saving some entries, and keeping the latest version number so as to not
+disrupt incremental propagation. If set to a negative value then
+automatic log truncation will be disabled. Defaults to 52428800 (50MB).
.El
.It Li }
.It Li max-request = Va SIZE
Maximum size of a kdc request.
.It Li require-preauth = Va BOOL
If set pre-authentication is required.
-Since krb4 requests are not pre-authenticated they will be rejected.
.It Li ports = Va "list of ports"
List of ports the kdc should listen to.
.It Li addresses = Va "list of interfaces"
List of addresses the kdc should bind to.
-.It Li enable-kerberos4 = Va BOOL
-Turn on Kerberos 4 support.
-.It Li v4-realm = Va REALM
-To what realm v4 requests should be mapped.
-.It Li enable-524 = Va BOOL
-Should the Kerberos 524 converting facility be turned on.
-Default is the same as
-.Va enable-kerberos4 .
.It Li enable-http = Va BOOL
Should the kdc answer kdc-requests over http.
-.It Li enable-kaserver = Va BOOL
-If this kdc should emulate the AFS kaserver.
.It Li tgt-use-strongest-session-key = Va BOOL
If this is TRUE then the KDC will prefer the strongest key from the
client's AS-REQ or TGS-REQ enctype list for the ticket session key that
is supported by the KDC and the target principal when the target
principal is a krbtgt principal. Else it will prefer the first key from
the client's AS-REQ enctype list that is also supported by the KDC and
-the target principal. Defaults to TRUE.
+the target principal. Defaults to FALSE.
.It Li svc-use-strongest-session-key = Va BOOL
Like tgt-use-strongest-session-key, but applies to the session key
enctype of tickets for services other than krbtgt principals. Defaults
-to TRUE.
+to FALSE.
.It Li preauth-use-strongest-session-key = Va BOOL
If TRUE then select the strongest possible enctype from the client's
AS-REQ for PA-ETYPE-INFO2 (i.e., for password-based pre-authentication).
-Else pick the first supported enctype from the client's AS-REQ. Defaults
-to TRUE.
+Else pick the first supported enctype from the client's AS-REQ. Defaults
+to FALSE.
.It Li use-strongest-server-key = Va BOOL
If TRUE then the KDC picks, for the ticket encrypted part's key, the
first supported enctype from the target service principal's hdb entry's
current keyset. Else the KDC picks the first supported enctype from the
-target service principal's hdb entry's current keyset. Defaults to TRUE.
+target service principal's hdb entry's current keyset. Defaults to TRUE.
.It Li check-ticket-addresses = Va BOOL
Verify the addresses in the tickets used in tgs requests.
.\" XXX
@@ -449,14 +649,6 @@ The time before expiration that the user should be warned that her
password is about to expire.
.It Li logging = Va Logging
What type of logging the kdc should use, see also [logging]/kdc.
-.It Li use_2b = {
-.Bl -tag -width "xxx" -offset indent
-.It Va principal Li = Va BOOL
-boolean value if the 524 daemon should return AFS 2b tokens for
-.Fa principal .
-.It ...
-.El
-.It Li }
.It Li hdb-ldap-structural-object Va structural object
If the LDAP backend is used for storing principals, this is the
structural object that will be used when creating and when reading
@@ -470,11 +662,44 @@ Should the kdc answer digest requests. The default is FALSE.
.It Li digests_allowed = Va list of digests
Specifies the digests the kdc will reply to. The default is
.Li ntlm-v2 .
+.It Li kx509_ca = Va file
+Specifies the PEM credentials for the kx509 certification authority.
+.It Li require_initial_kca_tickets = Va boolean
+Specified whether to require that tickets for the
+.Li kca_service
+service principal be INITIAL.
+This may be set on a per-realm basis as well as globally.
+Defaults to true for the global setting.
+.It Li kx509_include_pkinit_san = Va boolean
+If true then the kx509 client principal's name and realm will be
+included in an
+.Li id-pkinit-san
+certificate extension.
+This can be set on a per-realm basis as well as globally.
+Defaults to true for the global setting.
+.It Li kx509_template = Va file
+Specifies the PEM file with a template for the certificates to be
+issued.
+The following variables can be interpolated in the subject name using
+${variable} syntax:
+.Bl -tag -width "xxx" -offset indent
+.It principal-name
+The full name of the kx509 client principal.
+.It principal-name-without-realm
+The full name of the kx509 client principal, excluding the realm name.
+.It principal-name-realm
+The name of the client principal's realm.
.El
+.El
+The
+.Li kx509 ,
+.Li kx509_template ,
+.Li kx509_include_pkinit_san ,
+and
+.Li require_initial_kca_tickets
+parameters may be set on a per-realm basis as well.
.It Li [kadmin]
.Bl -tag -width "xxx" -offset indent
-.It Li require-preauth = Va BOOL
-If pre-authentication is required to talk to the kadmin server.
.It Li password_lifetime = Va time
If a principal already have its password set for expiration, this is
the time it will be valid for after a change.
@@ -496,17 +721,27 @@ Additional special values of keytypes are:
.It Li v5
The Kerberos 5 salt
.Va pw-salt
-.It Li v4
-The Kerberos 4 salt
-.Va des:pw-salt:
.El
+.It Li default_key_rules = Va {
+.Bl -tag -width "xxx" -offset indent
+.It Va globing-rule Li = Va keytypes...
+a globbing rule to matching a principal, and when true, use the
+keytypes as specified the same format as [kadmin]default_keys .
+.El
+.It Li }
+.It Li prune-key-history = Va BOOL
+When adding keys to the key history, drop keys that are too old to match
+unexpired tickets (based on the principal's maximum ticket lifetime).
+If the KDC keystore is later compromised traffic protected with the
+discarded older keys may remain protected. This also keeps the HDB
+records for principals with key history from growing without bound.
+The default (backwards compatible) value is "false".
.It Li use_v4_salt = Va BOOL
When true, this is the same as
.Pp
.Va default_keys = Va des3:pw-salt Va v4
.Pp
and is only left for backwards compatibility.
-.El
.It Li [password_quality]
Check the Password quality assurance in the info documentation for
more information.
@@ -522,6 +757,7 @@ List of policy names to apply to the password. Builtin policies are
among other minimum-length, character-class, external-check.
.El
.El
+.El
.Sh ENVIRONMENT
.Ev KRB5_CONFIG
points to the configuration file to read.
@@ -534,24 +770,26 @@ configuration file for Kerberos 5.
.Bd -literal -offset indent
[libdefaults]
default_realm = FOO.SE
+ name_canon_rules = as-is:realm=FOO.SE
+ name_canon_rules = qualify:domain=foo.se:realm=FOO.SE
+ name_canon_rules = qualify:domain=bar.se:realm=FOO.SE
+ name_canon_rules = nss
[domain_realm]
.foo.se = FOO.SE
.bar.se = FOO.SE
[realms]
FOO.SE = {
kdc = kerberos.foo.se
- v4_name_convert = {
- rcmd = host
- }
- v4_instance_convert = {
- xyz = xyz.bar.se
- }
default_domain = foo.se
}
[logging]
kdc = FILE:/var/heimdal/kdc.log
kdc = SYSLOG:INFO
default = SYSLOG:INFO:USER
+[kadmin]
+ default_key_rules = {
+ */ppp@* = arcfour-hmac-md5:pw-salt
+ }
.Ed
.Sh DIAGNOSTICS
Since
@@ -569,7 +807,6 @@ are actually used and thus cannot warn about unknown or misspelled
ones.
.Sh SEE ALSO
.Xr kinit 1 ,
-.Xr krb5_425_conv_principal 3 ,
.Xr krb5_openlog 3 ,
.Xr strftime 3 ,
.Xr verify_krb5_conf 8
diff --git a/lib/krb5/krb5.conf.cat5 b/lib/krb5/krb5.conf.cat5
index aa5ab91bc5e4..619fdc3cd25f 100644
--- a/lib/krb5/krb5.conf.cat5
+++ b/lib/krb5/krb5.conf.cat5
@@ -108,7 +108,7 @@ DDEESSCCRRIIPPTTIIOONN
kkrrbb55__ggeett__hhoosstt__rreeaallmm(_l_o_c_a_l _h_o_s_t_n_a_m_e).
allow_weak_crypto = _b_o_o_l_e_a_n
- is weaks crypto algorithms allowed to be used,
+ are weak crypto algorithms allowed to be used,
among others, DES is considered weak.
clockskew = _t_i_m_e
@@ -120,12 +120,6 @@ DDEESSCCRRIIPPTTIIOONN
Maximum time to wait for a reply from the kdc,
default is 3 seconds.
- v4_name_convert
-
- v4_instance_resolve
- These are described in the
- krb5_425_conv_principal(3) manual page.
-
capath = {
_d_e_s_t_i_n_a_t_i_o_n_-_r_e_a_l_m = _n_e_x_t_-_h_o_p_-_r_e_a_l_m
@@ -142,8 +136,8 @@ DDEESSCCRRIIPPTTIIOONN
the default credentials cache name. If you want to
change the type only use default_cc_type. The
string can contain variables that are expanded on
- runtime. Only support variable now is %{uid} that
- expands to the current user id.
+ runtime. The Only supported variable currently is
+ %{uid} which expands to the current user id.
default_etypes = _e_t_y_p_e_s _._._.
A list of default encryption types to use.
@@ -245,24 +239,163 @@ DDEESSCCRRIIPPTTIIOONN
fcache_version = _i_n_t
Use file credential cache format version specified.
- krb4_get_tickets = _b_o_o_l_e_a_n
- Also get Kerberos 4 tickets in kkiinniitt, llooggiinn, and
- other programs. This option is also valid in the
- [realms] section.
-
fcc-mit-ticketflags = _b_o_o_l_e_a_n
Use MIT compatible format for file credential
cache. It's the field ticketflags that is stored
in reverse bit order for older than Heimdal 0.7.
- Setting this flag to TRUE make it store the MIT
+ Setting this flag to TRUE makes it store the MIT
way, this is default for Heimdal 0.7.
check-rd-req-server
If set to "ignore", the framework will ignore any
- the server input to krb5_rd_req(3,) this is very
+ of the server input to krb5_rd_req(3), this is very
useful when the GSS-API server input the wrong
server name into the gss_accept_sec_context call.
+ k5login_directory = _d_i_r_e_c_t_o_r_y
+ Alternative location for user .k5login files. This
+ option is provided for compatibility with MIT krb5
+ configuration files.
+
+ k5login_authoritative = _b_o_o_l_e_a_n
+ If true then if a principal is not found in k5login
+ files then krb5_userok(3) will not fallback on
+ principal to username mapping. This option is pro-
+ vided for compatibility with MIT krb5 configuration
+ files.
+
+ kuserok = _r_u_l_e _._._.
+ Specifies krb5_userok(3) behavior. If multiple
+ values are given, then krb5_userok(3) will evaluate
+ them in order until one succeeds or all fail.
+ Rules are implemented by plugins, with three built-
+ in plugins described below. Default: USER-K5LOGIN
+ SIMPLE DENY.
+
+ kuserok = _D_E_N_Y
+ If set and evaluated then krb5_userok(3) will deny
+ access to the given username no matter what the
+ principal name might be.
+
+ kuserok = _S_I_M_P_L_E
+ If set and evaluated then krb5_userok(3) will use
+ principal to username mapping (see auth_to_local
+ below). If the principal maps to the requested
+ username then access is allowed.
+
+ kuserok = _S_Y_S_T_E_M_-_K_5_L_O_G_I_N_[_:_d_i_r_e_c_t_o_r_y_]
+ If set and evaluated then krb5_userok(3) will use
+ k5login files named after the _l_u_s_e_r argument to
+ krb5_userok(3) in the given directory or in
+ _/_e_t_c_/_k_5_l_o_g_i_n_._d_/. K5login files are text files,
+ with each line containing just a principal name;
+ principals apearing in a user's k5login file are
+ permitted access to the user's account. Note: this
+ rule performs no ownership nor permissions checks
+ on k5login files; proper ownership and permis-
+ sions/ACLs are expected due to the k5login location
+ being a system location.
+
+ kuserok = _U_S_E_R_-_K_5_L_O_G_I_N
+ If set and evaluated then krb5_userok(3) will use
+ _~_l_u_s_e_r_/_._k_5_l_o_g_i_n and _~_l_u_s_e_r_/_._k_5_l_o_g_i_n_._d_/_*. User
+ k5login files and directories must be owned by the
+ user and must not have world nor group write per-
+ missions.
+
+ aname2lname-text-db = _f_i_l_e_n_a_m_e
+ The named file must be a sorted (in increasing
+ order) text file where every line consists of an
+ unparsed principal name optionally followed by
+ whitespace and a username. The aname2lname func-
+ tion will do a binary search on this file, if con-
+ figured, looking for lines that match the given
+ principal name, and if found the given username
+ will be used, or, if the username is missing, an
+ error will be returned. If the file doesn't exist,
+ or if no matching line is found then other plugins
+ will be allowed to run.
+
+ fcache_strict_checking
+ strict checking in FILE credential caches that
+ owner, no symlink and permissions is correct.
+
+ name_canon_rules = _r_u_l_e_s
+ One or more service principal name canonicalization
+ rules. Each rule consists of one or more tokens
+ separated by colon (':'). Currently these rules
+ are used only for hostname canonicalization (usu-
+ ally when getting a service ticket, from a ccache
+ or a TGS, but also when acquiring GSS initiator
+ credentials from a keytab). These rules can be
+ used to implement DNS resolver-like search lists
+ without having to use DNS.
+
+ NOTE: Name canonicalization rules are an experimen-
+ tal feature.
+
+ The first token is a rule type, one of: _a_s_-_i_s_,
+ _q_u_a_l_i_f_y_, _o_r _n_s_s_.
+
+ Any remaining tokens must be options tokens:
+ _u_s_e___f_a_s_t (use FAST to protect TGS exchanges; cur-
+ rently not supported), _u_s_e___d_n_s_s_e_c (use DNSSEC to
+ protect hostname lookups; currently not supported),
+ _c_c_a_c_h_e___o_n_l_y , _u_s_e___r_e_f_e_r_r_a_l_s_, _n_o___r_e_f_e_r_r_a_l_s_,
+ _l_o_o_k_u_p___r_e_a_l_m_, _m_i_n_d_o_t_s_=_N_, _m_a_x_d_o_t_s_=_N_, _o_r_d_e_r_=_N_,
+ domain= _d_o_m_a_i_n_, realm= _r_e_a_l_m_, match_domain= _d_o_m_a_i_n_,
+ and match_realm= _r_e_a_l_m_.
+
+ When trying to obtain a service ticket for a host-
+ based service principal name, name canonicalization
+ rules are applied to that name in the order given,
+ one by one, until one succeds (a service ticket is
+ obtained), or all fail. Similarly when acquiring
+ GSS initiator credentials from a keytab, and when
+ comparing a non-canonical GSS name to a canonical
+ one.
+
+ For each rule the system checks that the hostname
+ has at least _m_i_n_d_o_t_s periods (if given) in it, at
+ most _m_a_x_d_o_t_s periods (if given), that the hostname
+ ends in the given _m_a_t_c_h___d_o_m_a_i_n (if given), and that
+ the realm of the principal matches the _m_a_t_c_h___r_e_a_l_m
+ (if given).
+
+ _A_s_-_i_s rules leave the hostname unmodified but may
+ set a realm. _Q_u_a_l_i_f_y rules qualify the hostname
+ with the given _d_o_m_a_i_n and also may set the realm.
+ The _n_s_s rule uses the system resolver to lookup the
+ host's canonical name and is usually not secure.
+ Note that using the _n_s_s rule type implies having to
+ have principal aliases in the HDB (though not nec-
+ essarily in keytabs).
+
+ The empty realm denotes "ask the client's realm's
+ TGS". The empty realm may be set as well as
+ matched.
+
+ The order in which rules are applied is as follows:
+ first all the rules with explicit _o_r_d_e_r then all
+ other rules in the order in which they appear. If
+ any two rules have the same explicit _o_r_d_e_r, their
+ order of appearance in krb5.conf breaks the tie.
+ Explicitly specifying order can be useful where
+ tools read and write the configuration file without
+ preserving parameter order.
+
+ Malformed rules are ignored.
+
+ allow_hierarchical_capaths = _b_o_o_l_e_a_n
+ When validating cross-realm transit paths, absent
+ any explicit capath from the client realm to the
+ server realm, allow a hierarchical transit path via
+ the common ancestor domain of the two realms.
+ Defaults to true. Note, absent an explicit set-
+ ting, hierarchical capaths are always used by the
+ KDC when generating a referral to a destination
+ with which is no direct trust.
+
[domain_realm]
This is a list of mappings from DNS domain to Kerberos realm.
Each binding in this section looks like:
@@ -310,23 +443,62 @@ DDEESSCCRRIIPPTTIIOONN
no such entry, the kpasswd port on the
admin_server host will be tried.
- krb524_server = _h_o_s_t_[_:_p_o_r_t_]
- Points to the server that does 524 con-
- versions. If it is not mentioned, the
- krb524 port on the kdcs will be tried.
-
- v4_instance_convert
-
- v4_name_convert
-
- default_domain
- See krb5_425_conv_principal(3).
-
tgs_require_subkey
a boolan variable that defaults to
false. Old DCE secd (pre 1.1) might
need this to be true.
+ auth_to_local_names = {
+
+ _p_r_i_n_c_i_p_a_l___n_a_m_e _= _u_s_e_r_n_a_m_e
+ The given _p_r_i_n_c_i_p_a_l___n_a_m_e will
+ be mapped to the given
+ _u_s_e_r_n_a_m_e if the _R_E_A_L_M is a
+ default realm.
+
+ }
+
+ auth_to_local = HEIMDAL_DEFAULT
+ Use the Heimdal default principal to
+ username mapping. Applies to principals
+ from the _R_E_A_L_M if and only if _R_E_A_L_M is a
+ default realm.
+
+ auth_to_local = DEFAULT
+ Use the MIT default principal to user-
+ name mapping. Applies to principals
+ from the _R_E_A_L_M if and only if _R_E_A_L_M is a
+ default realm.
+
+ auth_to_local = DB:/path/to/db.txt
+ Use a binary search of the given DB.
+ The DB must be a flat-text file sortedf
+ in the "C" locale, with each record
+ being a line (separated by either LF or
+ CRLF) consisting of a principal name
+ followed by whitespace followed by a
+ username. Applies to principals from
+ the _R_E_A_L_M if and only if _R_E_A_L_M is a
+ default realm.
+
+ auth_to_local = DB:/path/to/db
+ Use the given DB, if there's a plugin
+ for it. Applies to principals from the
+ _R_E_A_L_M if and only if _R_E_A_L_M is a default
+ realm.
+
+ auth_to_local = RULE:...
+ Use the given rule, if there's a plugin
+ for it. Applies to principals from the
+ _R_E_A_L_M if and only if _R_E_A_L_M is a default
+ realm.
+
+ auth_to_local = NONE
+ No additional principal to username map-
+ ping is done. Note that
+ _a_u_t_h___t_o___l_o_c_a_l___n_a_m_e_s and any preceding
+ _a_u_t_h___t_o___l_o_c_a_l rules have precedence.
+
}
[capaths]
@@ -359,10 +531,12 @@ DDEESSCCRRIIPPTTIIOONN
database = {
- dbname = _D_A_T_A_B_A_S_E_N_A_M_E
- Use this database for this realm. See
- the info documetation how to configure
- different database backends.
+ dbname = _[_D_A_T_B_A_S_E_T_Y_P_E_:_]_D_A_T_A_B_A_S_E_N_A_M_E
+ Use this database for this realm. The
+ _D_A_T_A_B_A_S_E_T_Y_P_E should be one of 'lmdb',
+ 'db3', 'db1', 'db', 'sqlite', or 'ldap'.
+ See the info documetation how to config-
+ ure different database backends.
realm = _R_E_A_L_M
Specifies the realm that will be stored
@@ -384,7 +558,25 @@ DDEESSCCRRIIPPTTIIOONN
Use this file as the log of changes per-
formed to the database. This file is
used by iipprrooppdd--mmaasstteerr for propagating
- changes to slaves.
+ changes to slaves. It is also used by
+ kkaaddmmiinndd and kkaaddmmiinn (when used with the
+ -l option), and by all applications
+ using lliibbkkaaddmm55 with the local backend,
+ for two-phase commit functionality.
+ Slaves also use this. Setting this to
+ //ddeevv//nnuullll disables two-phase commit and
+ incremental propagation. Use iipprroopp--lloogg
+ to show the contents of this log file.
+
+ log-max-size = _n_u_m_b_e_r
+ When the log reaches this size (in
+ bytes), the log will be truncated, sav-
+ ing some entries, and keeping the latest
+ version number so as to not disrupt
+ incremental propagation. If set to a
+ negative value then automatic log trun-
+ cation will be disabled. Defaults to
+ 52428800 (50MB).
}
@@ -392,9 +584,7 @@ DDEESSCCRRIIPPTTIIOONN
Maximum size of a kdc request.
require-preauth = _B_O_O_L
- If set pre-authentication is required. Since krb4
- requests are not pre-authenticated they will be
- rejected.
+ If set pre-authentication is required.
ports = _l_i_s_t _o_f _p_o_r_t_s
List of ports the kdc should listen to.
@@ -402,23 +592,9 @@ DDEESSCCRRIIPPTTIIOONN
addresses = _l_i_s_t _o_f _i_n_t_e_r_f_a_c_e_s
List of addresses the kdc should bind to.
- enable-kerberos4 = _B_O_O_L
- Turn on Kerberos 4 support.
-
- v4-realm = _R_E_A_L_M
- To what realm v4 requests should be mapped.
-
- enable-524 = _B_O_O_L
- Should the Kerberos 524 converting facility be
- turned on. Default is the same as
- _e_n_a_b_l_e_-_k_e_r_b_e_r_o_s_4.
-
enable-http = _B_O_O_L
Should the kdc answer kdc-requests over http.
- enable-kaserver = _B_O_O_L
- If this kdc should emulate the AFS kaserver.
-
tgt-use-strongest-session-key = _B_O_O_L
If this is TRUE then the KDC will prefer the
strongest key from the client's AS-REQ or TGS-REQ
@@ -427,19 +603,19 @@ DDEESSCCRRIIPPTTIIOONN
the target principal is a krbtgt principal. Else
it will prefer the first key from the client's AS-
REQ enctype list that is also supported by the KDC
- and the target principal. Defaults to TRUE.
+ and the target principal. Defaults to FALSE.
svc-use-strongest-session-key = _B_O_O_L
Like tgt-use-strongest-session-key, but applies to
the session key enctype of tickets for services
- other than krbtgt principals. Defaults to TRUE.
+ other than krbtgt principals. Defaults to FALSE.
preauth-use-strongest-session-key = _B_O_O_L
If TRUE then select the strongest possible enctype
from the client's AS-REQ for PA-ETYPE-INFO2 (i.e.,
for password-based pre-authentication). Else pick
the first supported enctype from the client's AS-
- REQ. Defaults to TRUE.
+ REQ. Defaults to FALSE.
use-strongest-server-key = _B_O_O_L
If TRUE then the KDC picks, for the ticket
@@ -447,7 +623,7 @@ DDEESSCCRRIIPPTTIIOONN
from the target service principal's hdb entry's
current keyset. Else the KDC picks the first sup-
ported enctype from the target service principal's
- hdb entry's current keyset. Defaults to TRUE.
+ hdb entry's current keyset. Defaults to TRUE.
check-ticket-addresses = _B_O_O_L
Verify the addresses in the tickets used in tgs
@@ -472,16 +648,6 @@ DDEESSCCRRIIPPTTIIOONN
What type of logging the kdc should use, see also
[logging]/kdc.
- use_2b = {
-
- _p_r_i_n_c_i_p_a_l = _B_O_O_L
- boolean value if the 524 daemon should
- return AFS 2b tokens for _p_r_i_n_c_i_p_a_l.
-
- ...
-
- }
-
hdb-ldap-structural-object _s_t_r_u_c_t_u_r_a_l _o_b_j_e_c_t
If the LDAP backend is used for storing principals,
this is the structural object that will be used
@@ -501,11 +667,45 @@ DDEESSCCRRIIPPTTIIOONN
Specifies the digests the kdc will reply to. The
default is ntlm-v2.
- [kadmin]
+ kx509_ca = _f_i_l_e
+ Specifies the PEM credentials for the kx509 certi-
+ fication authority.
+
+ require_initial_kca_tickets = _b_o_o_l_e_a_n
+ Specified whether to require that tickets for the
+ kca_service service principal be INITIAL. This may
+ be set on a per-realm basis as well as globally.
+ Defaults to true for the global setting.
+
+ kx509_include_pkinit_san = _b_o_o_l_e_a_n
+ If true then the kx509 client principal's name and
+ realm will be included in an id-pkinit-san certifi-
+ cate extension. This can be set on a per-realm
+ basis as well as globally. Defaults to true for
+ the global setting.
+
+ kx509_template = _f_i_l_e
+ Specifies the PEM file with a template for the cer-
+ tificates to be issued. The following variables
+ can be interpolated in the subject name using
+ ${variable} syntax:
+
+ principal-name
+ The full name of the kx509 client prin-
+ cipal.
+
+ principal-name-without-realm
+ The full name of the kx509 client prin-
+ cipal, excluding the realm name.
+
+ principal-name-realm
+ The name of the client principal's
+ realm.
+ The kx509, kx509_template, kx509_include_pkinit_san, and
+ require_initial_kca_tickets parameters may be set on a per-
+ realm basis as well.
- require-preauth = _B_O_O_L
- If pre-authentication is required to talk to the
- kadmin server.
+ [kadmin]
password_lifetime = _t_i_m_e
If a principal already have its password set for
@@ -526,7 +726,25 @@ DDEESSCCRRIIPPTTIIOONN
v5 The Kerberos 5 salt _p_w_-_s_a_l_t
- v4 The Kerberos 4 salt _d_e_s_:_p_w_-_s_a_l_t_:
+ default_key_rules = _{
+
+ _g_l_o_b_i_n_g_-_r_u_l_e = _k_e_y_t_y_p_e_s_._._.
+ a globbing rule to matching a principal,
+ and when true, use the keytypes as spec-
+ ified the same format as [kad-
+ min]default_keys .
+
+ }
+
+ prune-key-history = _B_O_O_L
+ When adding keys to the key history, drop keys that
+ are too old to match unexpired tickets (based on
+ the principal's maximum ticket lifetime). If the
+ KDC keystore is later compromised traffic protected
+ with the discarded older keys may remain protected.
+ This also keeps the HDB records for principals with
+ key history from growing without bound. The
+ default (backwards compatible) value is "false".
use_v4_salt = _B_O_O_L
When true, this is the same as
@@ -535,26 +753,27 @@ DDEESSCCRRIIPPTTIIOONN
and is only left for backwards compatibility.
- [password_quality]
- Check the Password quality assurance in the info documentation
- for more information.
+ [password_quality]
+ Check the Password quality assurance in the info
+ documentation for more information.
- check_library = _l_i_b_r_a_r_y_-_n_a_m_e
- Library name that contains the password check_func-
- tion
+ check_library = _l_i_b_r_a_r_y_-_n_a_m_e
+ Library name that contains the password
+ check_function
- check_function = _f_u_n_c_t_i_o_n_-_n_a_m_e
- Function name for checking passwords in
- check_library
+ check_function = _f_u_n_c_t_i_o_n_-_n_a_m_e
+ Function name for checking passwords in
+ check_library
- policy_libraries = _l_i_b_r_a_r_y_1 _._._. _l_i_b_r_a_r_y_N
- List of libraries that can do password policy
- checks
+ policy_libraries = _l_i_b_r_a_r_y_1 _._._. _l_i_b_r_a_r_y_N
+ List of libraries that can do password
+ policy checks
- policies = _p_o_l_i_c_y_1 _._._. _p_o_l_i_c_y_N
- List of policy names to apply to the password.
- Builtin policies are among other minimum-length,
- character-class, external-check.
+ policies = _p_o_l_i_c_y_1 _._._. _p_o_l_i_c_y_N
+ List of policy names to apply to the
+ password. Builtin policies are among
+ other minimum-length, character-class,
+ external-check.
EENNVVIIRROONNMMEENNTT
KRB5_CONFIG points to the configuration file to read.
@@ -565,24 +784,26 @@ FFIILLEESS
EEXXAAMMPPLLEESS
[libdefaults]
default_realm = FOO.SE
+ name_canon_rules = as-is:realm=FOO.SE
+ name_canon_rules = qualify:domain=foo.se:realm=FOO.SE
+ name_canon_rules = qualify:domain=bar.se:realm=FOO.SE
+ name_canon_rules = nss
[domain_realm]
.foo.se = FOO.SE
.bar.se = FOO.SE
[realms]
FOO.SE = {
kdc = kerberos.foo.se
- v4_name_convert = {
- rcmd = host
- }
- v4_instance_convert = {
- xyz = xyz.bar.se
- }
default_domain = foo.se
}
[logging]
kdc = FILE:/var/heimdal/kdc.log
kdc = SYSLOG:INFO
default = SYSLOG:INFO:USER
+ [kadmin]
+ default_key_rules = {
+ */ppp@* = arcfour-hmac-md5:pw-salt
+ }
DDIIAAGGNNOOSSTTIICCSS
Since kkrrbb55..ccoonnff is read and parsed by the krb5 library, there is not a
@@ -594,7 +815,6 @@ DDIIAAGGNNOOSSTTIICCSS
unknown or misspelled ones.
SSEEEE AALLSSOO
- kinit(1), krb5_425_conv_principal(3), krb5_openlog(3), strftime(3),
- verify_krb5_conf(8)
+ kinit(1), krb5_openlog(3), strftime(3), verify_krb5_conf(8)
HEIMDAL May 4, 2005 HEIMDAL
diff --git a/lib/krb5/krb5.h b/lib/krb5/krb5.h
index 2d555ea0604f..19c89f21ae84 100644
--- a/lib/krb5/krb5.h
+++ b/lib/krb5/krb5.h
@@ -150,6 +150,8 @@ enum {
ETYPE_DES3_CBC_SHA1 = KRB5_ENCTYPE_DES3_CBC_SHA1,
ETYPE_AES128_CTS_HMAC_SHA1_96 = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA1_96,
ETYPE_AES256_CTS_HMAC_SHA1_96 = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA256_128 = KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ ETYPE_AES256_CTS_HMAC_SHA384_192 = KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192,
ETYPE_ARCFOUR_HMAC_MD5 = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5,
ETYPE_ARCFOUR_HMAC_MD5_56 = KRB5_ENCTYPE_ARCFOUR_HMAC_MD5_56,
ETYPE_ENCTYPE_PK_CROSS = KRB5_ENCTYPE_ENCTYPE_PK_CROSS,
@@ -274,14 +276,28 @@ typedef enum krb5_key_usage {
/* Encryption type of the kdc session contribution in pk-init */
KRB5_KU_AS_REQ = 56,
/* Checksum of over the AS-REQ send by the KDC in PA-REQ-ENC-PA-REP */
+ KRB5_KU_FAST_REQ_CHKSUM = 50,
+ /* FAST armor checksum */
+ KRB5_KU_FAST_ENC = 51,
+ /* FAST armor encryption */
+ KRB5_KU_FAST_REP = 52,
+ /* FAST armor reply */
+ KRB5_KU_FAST_FINISHED = 53,
+ /* FAST finished checksum */
+ KRB5_KU_ENC_CHALLENGE_CLIENT = 54,
+ /* fast challenge from client */
+ KRB5_KU_ENC_CHALLENGE_KDC = 55,
+ /* fast challenge from kdc */
KRB5_KU_DIGEST_ENCRYPT = -18,
/* Encryption key usage used in the digest encryption field */
KRB5_KU_DIGEST_OPAQUE = -19,
/* Checksum key usage used in the digest opaque field */
KRB5_KU_KRB5SIGNEDPATH = -21,
/* Checksum key usage on KRB5SignedPath */
- KRB5_KU_CANONICALIZED_NAMES = -23
+ KRB5_KU_CANONICALIZED_NAMES = -23,
/* Checksum key usage on PA-CANONICALIZED */
+ KRB5_KU_H5L_COOKIE = -25
+ /* encrypted foo */
} krb5_key_usage;
typedef krb5_key_usage krb5_keyusage;
@@ -367,6 +383,7 @@ typedef struct krb5_cccol_cursor_data *krb5_cccol_cursor;
typedef struct krb5_ccache_data {
const struct krb5_cc_ops *ops;
krb5_data data;
+ int initialized; /* if non-zero: krb5_cc_initialize() called, now empty */
}krb5_ccache_data;
typedef struct krb5_ccache_data *krb5_ccache;
@@ -641,6 +658,8 @@ typedef struct krb5_auth_context_data {
krb5_keytype keytype; /* ¿requested key type ? */
krb5_cksumtype cksumtype; /* ¡requested checksum type! */
+
+ AuthorizationData *auth_data;
}krb5_auth_context_data, *krb5_auth_context;
@@ -671,8 +690,17 @@ typedef EncAPRepPart krb5_ap_rep_enc_part;
#define KRB5_TGS_NAME ("krbtgt")
#define KRB5_WELLKNOWN_NAME ("WELLKNOWN")
#define KRB5_ANON_NAME ("ANONYMOUS")
+#define KRB5_ANON_REALM ("WELLKNOWN:ANONYMOUS")
+#define KRB5_WELLKNOWN_ORG_H5L_REALM ("WELLKNOWN:ORG.H5L")
#define KRB5_DIGEST_NAME ("digest")
+
+#define KRB5_PKU2U_REALM_NAME ("WELLKNOWN:PKU2U")
+#define KRB5_LKDC_REALM_NAME ("WELLKNOWN:COM.APPLE.LKDC")
+
+#define KRB5_GSS_HOSTBASED_SERVICE_NAME ("WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE")
+#define KRB5_GSS_REFERALS_REALM_NAME ("WELLKNOWN:ORG.H5L.REFERALS-REALM")
+
typedef enum {
KRB5_PROMPT_TYPE_PASSWORD = 0x1,
KRB5_PROMPT_TYPE_NEW_PASSWORD = 0x2,
@@ -720,6 +748,7 @@ struct _krb5_get_init_creds_opt {
int forwardable;
int proxiable;
int anonymous;
+ int change_password_prompt;
krb5_enctype *etype_list;
int etype_list_length;
krb5_addresses *address_list;
@@ -743,6 +772,7 @@ typedef struct _krb5_get_init_creds_opt krb5_get_init_creds_opt;
#define KRB5_GET_INIT_CREDS_OPT_SALT 0x0080 /* no supported */
#define KRB5_GET_INIT_CREDS_OPT_ANONYMOUS 0x0100
#define KRB5_GET_INIT_CREDS_OPT_DISABLE_TRANSITED_CHECK 0x0200
+#define KRB5_GET_INIT_CREDS_OPT_CHANGE_PASSWORD_PROMPT 0x0400
/* krb5_init_creds_step flags argument */
#define KRB5_INIT_CREDS_STEP_FLAG_CONTINUE 0x0001
@@ -806,6 +836,7 @@ enum {
KRB5_KRBHST_FLAGS_LARGE_MSG = 2
};
+typedef krb5_error_code (*krb5_sendto_prexmit)(krb5_context, int, void *, int, krb5_data *);
typedef krb5_error_code
(KRB5_CALLCONV * krb5_send_to_kdc_func)(krb5_context, void *, krb5_krbhst_info *, time_t,
const krb5_data *, krb5_data *);
@@ -814,7 +845,9 @@ typedef krb5_error_code
enum {
KRB5_PRINCIPAL_PARSE_NO_REALM = 1, /**< Require that there are no realm */
KRB5_PRINCIPAL_PARSE_REQUIRE_REALM = 2, /**< Require a realm present */
- KRB5_PRINCIPAL_PARSE_ENTERPRISE = 4 /**< Parse as a NT-ENTERPRISE name */
+ KRB5_PRINCIPAL_PARSE_ENTERPRISE = 4, /**< Parse as a NT-ENTERPRISE name */
+ KRB5_PRINCIPAL_PARSE_IGNORE_REALM = 8, /**< Ignore realm if present */
+ KRB5_PRINCIPAL_PARSE_NO_DEF_REALM = 16 /**< Don't default the realm */
};
/** flags for krb5_unparse_name_flags */
@@ -827,8 +860,13 @@ enum {
typedef struct krb5_sendto_ctx_data *krb5_sendto_ctx;
#define KRB5_SENDTO_DONE 0
-#define KRB5_SENDTO_RESTART 1
+#define KRB5_SENDTO_RESET 1
#define KRB5_SENDTO_CONTINUE 2
+#define KRB5_SENDTO_TIMEOUT 3
+#define KRB5_SENDTO_INITIAL 4
+#define KRB5_SENDTO_FILTER 5
+#define KRB5_SENDTO_FAILED 6
+#define KRB5_SENDTO_KRBHST 7
typedef krb5_error_code
(KRB5_CALLCONV * krb5_sendto_ctx_func)(krb5_context, krb5_sendto_ctx, void *,
@@ -840,6 +878,8 @@ enum krb5_plugin_type {
PLUGIN_TYPE_FUNC
};
+#define KRB5_PLUGIN_INVOKE_ALL 1
+
struct credentials; /* this is to keep the compiler happy */
struct getargs;
struct sockaddr;
@@ -878,6 +918,28 @@ typedef struct {
typedef krb5_error_code
(KRB5_CALLCONV * krb5_gic_process_last_req)(krb5_context, krb5_last_req_entry **, void *);
+typedef struct {
+ krb5_enctype ks_enctype;
+ krb5int32 ks_salttype;
+}krb5_key_salt_tuple;
+
+/*
+ * Name canonicalization rule options
+ */
+
+typedef enum krb5_name_canon_rule_options {
+ KRB5_NCRO_GC_ONLY = 1 << 0,
+ KRB5_NCRO_USE_REFERRALS = 1 << 1,
+ KRB5_NCRO_NO_REFERRALS = 1 << 2,
+ KRB5_NCRO_USE_FAST = 1 << 3,
+ KRB5_NCRO_USE_DNSSEC = 1 << 4,
+ KRB5_NCRO_LOOKUP_REALM = 1 << 5
+} krb5_name_canon_rule_options;
+
+typedef struct krb5_name_canon_rule_data *krb5_name_canon_rule;
+typedef const struct krb5_name_canon_rule_data *krb5_const_name_canon_rule;
+typedef struct krb5_name_canon_iterator_data *krb5_name_canon_iterator;
+
/*
*
*/
@@ -893,6 +955,7 @@ extern KRB5_LIB_VARIABLE const char *krb5_defkeyname;
extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_acc_ops;
+extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_dcc_ops;
extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_fcc_ops;
extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_mcc_ops;
extern KRB5_LIB_VARIABLE const krb5_cc_ops krb5_kcm_ops;
@@ -911,6 +974,7 @@ extern KRB5_LIB_VARIABLE const char *krb5_cc_type_file;
extern KRB5_LIB_VARIABLE const char *krb5_cc_type_memory;
extern KRB5_LIB_VARIABLE const char *krb5_cc_type_kcm;
extern KRB5_LIB_VARIABLE const char *krb5_cc_type_scc;
+extern KRB5_LIB_VARIABLE const char *krb5_cc_type_dcc;
#endif /* __KRB5_H__ */
diff --git a/lib/krb5/krb5_auth_context.3 b/lib/krb5/krb5_auth_context.3
index ec7f8b31cf85..b627e480e7f3 100644
--- a/lib/krb5/krb5_auth_context.3
+++ b/lib/krb5/krb5_auth_context.3
@@ -224,7 +224,7 @@ will force
and
.Fn krb5_fwd_tgt_creds
to create unencrypted )
-.Dv ENCTYPE_NULL )
+.Dv KRB5_ENCTYPE_NULL )
credentials.
This is for use with old MIT server and JAVA based servers as
they can't handle encrypted
diff --git a/lib/krb5/krb5_auth_context.cat3 b/lib/krb5/krb5_auth_context.cat3
index f74bb606c11c..85458a8f287f 100644
--- a/lib/krb5/krb5_auth_context.cat3
+++ b/lib/krb5/krb5_auth_context.cat3
@@ -125,9 +125,9 @@ DDEESSCCRRIIPPTTIIOONN
KRB5_AUTH_CONTEXT_CLEAR_FORWARDED_CRED
will force kkrrbb55__ggeett__ffoorrwwaarrddeedd__ccrreeddss() and kkrrbb55__ffwwdd__ttggtt__ccrreeddss() to
- create unencrypted ) ENCTYPE_NULL) credentials. This is for use
- with old MIT server and JAVA based servers as they can't handle
- encrypted KRB-CRED. Note that sending such KRB-CRED is clear
+ create unencrypted ) KRB5_ENCTYPE_NULL) credentials. This is for
+ use with old MIT server and JAVA based servers as they can't han-
+ dle encrypted KRB-CRED. Note that sending such KRB-CRED is clear
exposes crypto keys and tickets and is insecure, make sure the
packet is encrypted in the protocol. krb5_rd_cred(3),
krb5_rd_priv(3), krb5_rd_safe(3), krb5_mk_priv(3) and
diff --git a/lib/krb5/krb5_c_make_checksum.3 b/lib/krb5/krb5_c_make_checksum.3
index 77be59adbe76..66402d4ca394 100644
--- a/lib/krb5/krb5_c_make_checksum.3
+++ b/lib/krb5/krb5_c_make_checksum.3
@@ -168,7 +168,7 @@ The functions starting with krb5_c are compat functions with MIT kerberos.
The
.Li krb5_enc_data
structure holds and encrypted data.
-There are two public accessable members of
+There are two public accessible members of
.Li krb5_enc_data .
.Li enctype
that holds the encryption type of the data encrypted and
diff --git a/lib/krb5/krb5_c_make_checksum.cat3 b/lib/krb5/krb5_c_make_checksum.cat3
index ac19146733bd..4d6c31a58973 100644
--- a/lib/krb5/krb5_c_make_checksum.cat3
+++ b/lib/krb5/krb5_c_make_checksum.cat3
@@ -84,7 +84,7 @@ DDEESSCCRRIIPPTTIIOONN
beros.
The krb5_enc_data structure holds and encrypted data. There are two pub-
- lic accessable members of krb5_enc_data. enctype that holds the encryp-
+ lic accessible members of krb5_enc_data. enctype that holds the encryp-
tion type of the data encrypted and ciphertext that is a _k_r_b_5___d_a_t_a that
might contain the encrypted data.
diff --git a/lib/krb5/krb5_digest.3 b/lib/krb5/krb5_digest.3
index a4ee75631a6b..11d4db4603c8 100644
--- a/lib/krb5/krb5_digest.3
+++ b/lib/krb5/krb5_digest.3
@@ -64,7 +64,7 @@
.Nm krb5_digest_get_tickets ,
.Nm krb5_digest_get_client_binding ,
.Nm krb5_digest_get_a1_hash
-.Nd remote digest (HTTP-DIGEST, SASL, CHAP) suppport
+.Nd remote digest (HTTP-DIGEST, SASL, CHAP) support
.Sh LIBRARY
Kerberos 5 Library (libkrb5, -lkrb5)
.Sh SYNOPSIS
diff --git a/lib/krb5/krb5_digest.cat3 b/lib/krb5/krb5_digest.cat3
index 905d334ea67e..3d3c53b528b3 100644
--- a/lib/krb5/krb5_digest.cat3
+++ b/lib/krb5/krb5_digest.cat3
@@ -15,7 +15,7 @@ NNAAMMEE
kkrrbb55__ddiiggeesstt__sseett__qqoopp, kkrrbb55__ddiiggeesstt__rreeqquueesstt, kkrrbb55__ddiiggeesstt__ggeett__rreessppoonnsseeDDaattaa,
kkrrbb55__ddiiggeesstt__ggeett__rrsspp, kkrrbb55__ddiiggeesstt__ggeett__ttiicckkeettss,
kkrrbb55__ddiiggeesstt__ggeett__cclliieenntt__bbiinnddiinngg, kkrrbb55__ddiiggeesstt__ggeett__aa11__hhaasshh -- remote digest
- (HTTP-DIGEST, SASL, CHAP) suppport
+ (HTTP-DIGEST, SASL, CHAP) support
LLIIBBRRAARRYY
Kerberos 5 Library (libkrb5, -lkrb5)
diff --git a/lib/krb5/krb5_err.et b/lib/krb5/krb5_err.et
index 098e04b95928..f660fbae7829 100644
--- a/lib/krb5/krb5_err.et
+++ b/lib/krb5/krb5_err.et
@@ -112,7 +112,12 @@ index 94
error_code INVALID_HASH_ALG, "Invalid OTP digest algorithm"
error_code INVALID_ITERATION_COUNT, "Invalid OTP iteration count"
-# 97-127 are reserved
+# 97-99 are reserved
+
+index 100
+error_code NO_ACCEPTABLE_KDF, "No acceptable KDF offered"
+
+# 101-127 are reserved
index 128
prefix
diff --git a/lib/krb5/krb5_get_creds.3 b/lib/krb5/krb5_get_creds.3
index e0ab8a91db6f..35e861eebd51 100644
--- a/lib/krb5/krb5_get_creds.3
+++ b/lib/krb5/krb5_get_creds.3
@@ -133,7 +133,7 @@ accessor functions.
and
.Fn krb5_get_creds_opt_set_options
adds and sets options to the
-.Fi krb5_get_creds_opt
+.Li krb5_get_creds_opt
structure .
The possible options to set are
.Bl -tag -width "KRB5_GC_USER_USER" -compact
diff --git a/lib/krb5/krb5_get_creds.cat3 b/lib/krb5/krb5_get_creds.cat3
index 9aef83e6fbb7..7e9f2683a556 100644
--- a/lib/krb5/krb5_get_creds.cat3
+++ b/lib/krb5/krb5_get_creds.cat3
@@ -62,7 +62,8 @@ DDEESSCCRRIIPPTTIIOONN
functions.
kkrrbb55__ggeett__ccrreeddss__oopptt__aadddd__ooppttiioonnss() and kkrrbb55__ggeett__ccrreeddss__oopptt__sseett__ooppttiioonnss()
- adds and sets options to the structure . The possible options to set are
+ adds and sets options to the krb5_get_creds_opt structure . The possible
+ options to set are
KRB5_GC_CACHED Only check the _c_c_a_c_h_e, don't got out on network to
fetch credential.
KRB5_GC_USER_USER request a user to user ticket. This options doesn't
diff --git a/lib/krb5/krb5_get_init_creds.3 b/lib/krb5/krb5_get_init_creds.3
index 764efb47e441..11257b9d9428 100644
--- a/lib/krb5/krb5_get_init_creds.3
+++ b/lib/krb5/krb5_get_init_creds.3
@@ -97,6 +97,11 @@ Kerberos 5 Library (libkrb5, -lkrb5)
.Fa "int anonymous"
.Fc
.Ft void
+.Fo krb5_get_init_creds_opt_set_change_password_prompt
+.Fa "krb5_get_init_creds_opt *opt"
+.Fa "int change_password_prompt"
+.Fc
+.Ft void
.Fo krb5_get_init_creds_opt_set_default_flags
.Fa "krb5_context context"
.Fa "const char *appname"
@@ -386,7 +391,7 @@ sets the salt that is going to be used in the request.
sets requested ticket lifetime.
.Pp
.Fn krb5_get_init_creds_opt_set_canonicalize
-requests that the KDC canonicalize the client pricipal if possible.
+requests that the KDC canonicalize the client principal if possible.
.Pp
.Fn krb5_get_init_creds_opt_set_win2k
turns on compatibility with Windows 2000.
diff --git a/lib/krb5/krb5_get_init_creds.cat3 b/lib/krb5/krb5_get_init_creds.cat3
index f727ab77ca60..52b3468a1852 100644
--- a/lib/krb5/krb5_get_init_creds.cat3
+++ b/lib/krb5/krb5_get_init_creds.cat3
@@ -53,6 +53,10 @@ SSYYNNOOPPSSIISS
_i_n_t _a_n_o_n_y_m_o_u_s);
_v_o_i_d
+ kkrrbb55__ggeett__iinniitt__ccrreeddss__oopptt__sseett__cchhaannggee__ppaasssswwoorrdd__pprroommpptt(_k_r_b_5___g_e_t___i_n_i_t___c_r_e_d_s___o_p_t _*_o_p_t,
+ _i_n_t _c_h_a_n_g_e___p_a_s_s_w_o_r_d___p_r_o_m_p_t);
+
+ _v_o_i_d
kkrrbb55__ggeett__iinniitt__ccrreeddss__oopptt__sseett__ddeeffaauulltt__ffllaaggss(_k_r_b_5___c_o_n_t_e_x_t _c_o_n_t_e_x_t,
_c_o_n_s_t _c_h_a_r _*_a_p_p_n_a_m_e, _k_r_b_5___c_o_n_s_t___r_e_a_l_m _r_e_a_l_m,
_k_r_b_5___g_e_t___i_n_i_t___c_r_e_d_s___o_p_t _*_o_p_t);
@@ -234,7 +238,7 @@ DDEESSCCRRIIPPTTIIOONN
kkrrbb55__ggeett__iinniitt__ccrreeddss__oopptt__sseett__ttkktt__lliiffee() sets requested ticket lifetime.
kkrrbb55__ggeett__iinniitt__ccrreeddss__oopptt__sseett__ccaannoonniiccaalliizzee() requests that the KDC canoni-
- calize the client pricipal if possible.
+ calize the client principal if possible.
kkrrbb55__ggeett__iinniitt__ccrreeddss__oopptt__sseett__wwiinn22kk() turns on compatibility with Windows
2000.
diff --git a/lib/krb5/krb5_locl.h b/lib/krb5/krb5_locl.h
index d0c68927ffbd..4d524ce1bb98 100644
--- a/lib/krb5/krb5_locl.h
+++ b/lib/krb5/krb5_locl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997-2006 Kungliga Tekniska Högskolan
+ * Copyright (c) 1997-2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -39,13 +39,13 @@
#define __KRB5_LOCL_H__
#include <config.h>
+#include <roken.h>
-#include <errno.h>
#include <ctype.h>
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <limits.h>
+
+#ifdef HAVE_POLL_H
+#include <sys/poll.h>
+#endif
#include <krb5-types.h>
@@ -55,12 +55,6 @@
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif
-#ifdef HAVE_FCNTL_H
-#include <fcntl.h>
-#endif
#if defined(HAVE_SYS_IOCTL_H) && SunOS != 40
#include <sys/ioctl.h>
@@ -72,44 +66,11 @@
#include <pwd.h>
#endif
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#include <time.h>
-#ifdef HAVE_SYS_TIME_H
-#include <sys/time.h>
-#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
-#ifdef HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETINET_IN6_H
-#include <netinet/in6.h>
-#endif
-#ifdef HAVE_NETINET6_IN6_H
-#include <netinet6/in6.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
#ifdef _AIX
-struct ether_addr;
struct mbuf;
-struct sockaddr_dl;
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_ARPA_NAMESER_H
-#include <arpa/nameser.h>
-#endif
-#ifdef HAVE_SYS_UIO_H
-#include <sys/uio.h>
#endif
#ifdef HAVE_SYS_FILIO_H
#include <sys/filio.h>
@@ -144,17 +105,24 @@ struct sockaddr_dl;
#include <door.h>
#endif
-#include <roken.h>
#include <parse_time.h>
#include <base64.h>
#include <wind.h>
+/*
+ * We use OpenSSL for EC, but to do this we need to disable cross-references
+ * between OpenSSL and hcrypto bn.h and such. Source files that use OpenSSL EC
+ * must define HEIM_NO_CRYPTO_HDRS before including this file.
+ */
#define HC_DEPRECATED_CRYPTO
+#ifndef HEIM_NO_CRYPTO_HDRS
#include "crypto-headers.h"
+#endif
#include <krb5_asn1.h>
+#include <pkinit_asn1.h>
struct send_to_kdc;
@@ -250,6 +218,7 @@ typedef uint32_t krb5_enctype_set;
typedef struct krb5_context_data {
krb5_enctype *etypes;
+ krb5_enctype *cfg_etypes;
krb5_enctype *etypes_des;/* deprecated */
krb5_enctype *as_etypes;
krb5_enctype *tgs_etypes;
@@ -257,6 +226,7 @@ typedef struct krb5_context_data {
char **default_realms;
time_t max_skew;
time_t kdc_timeout;
+ time_t host_timeout;
unsigned max_retries;
int32_t kdc_sec_offset;
int32_t kdc_usec_offset;
@@ -287,24 +257,31 @@ typedef struct krb5_context_data {
char *default_cc_name;
char *default_cc_name_env;
int default_cc_name_set;
- void *mutex; /* protects error_string/error_buf */
+ HEIMDAL_MUTEX mutex; /* protects error_string */
int large_msg_size;
+ int max_msg_size;
+ int tgs_negative_timeout; /* timeout for TGS negative cache */
int flags;
#define KRB5_CTX_F_DNS_CANONICALIZE_HOSTNAME 1
#define KRB5_CTX_F_CHECK_PAC 2
#define KRB5_CTX_F_HOMEDIR_ACCESS 4
#define KRB5_CTX_F_SOCKETS_INITIALIZED 8
#define KRB5_CTX_F_RD_REQ_IGNORE 16
+#define KRB5_CTX_F_FCACHE_STRICT_CHECKING 32
struct send_to_kdc *send_to_kdc;
#ifdef PKINIT
hx509_context hx509ctx;
#endif
+ unsigned int num_kdc_requests;
+ krb5_name_canon_rule name_canon_rules;
} krb5_context_data;
#ifndef KRB5_USE_PATH_TOKENS
#define KRB5_DEFAULT_CCNAME_FILE "FILE:/tmp/krb5cc_%{uid}"
+#define KRB5_DEFAULT_CCNAME_DIR "DIR:/tmp/krb5cc_%{uid}_dir/"
#else
#define KRB5_DEFAULT_CCNAME_FILE "FILE:%{TEMP}/krb5cc_%{uid}"
+#define KRB5_DEFAULT_CCNAME_DIR "DIR:%{TEMP}/krb5cc_%{uid}_dir/"
#endif
#define KRB5_DEFAULT_CCNAME_API "API:"
#define KRB5_DEFAULT_CCNAME_KCM_KCM "KCM:%{uid}"
@@ -336,6 +313,22 @@ typedef struct krb5_context_data {
#define KRB5_FORWARDABLE_DEFAULT TRUE
#endif
+#ifndef KRB5_CONFIGURATION_CHANGE_NOTIFY_NAME
+#define KRB5_CONFIGURATION_CHANGE_NOTIFY_NAME "org.h5l.Kerberos.configuration-changed"
+#endif
+
+#ifndef KRB5_FALLBACK_DEFAULT
+#define KRB5_FALLBACK_DEFAULT TRUE
+#endif
+
+#ifndef KRB5_TKT_LIFETIME_DEFAULT
+# define KRB5_TKT_LIFETIME_DEFAULT 15778800 /* seconds */
+#endif
+
+#ifndef KRB5_TKT_RENEW_LIFETIME_DEFAULT
+# define KRB5_TKT_RENEW_LIFETIME_DEFAULT 15778800 /* seconds */
+#endif
+
#ifdef PKINIT
struct krb5_pk_identity {
@@ -354,6 +347,34 @@ enum krb5_pk_type {
PKINIT_27 = 2
};
+enum keyex_enum { USE_RSA, USE_DH, USE_ECDH };
+
+struct krb5_pk_init_ctx_data {
+ struct krb5_pk_identity *id;
+ enum keyex_enum keyex;
+ union {
+ DH *dh;
+ void *eckey;
+ } u;
+ krb5_data *clientDHNonce;
+ struct krb5_dh_moduli **m;
+ hx509_peer_info peer;
+ enum krb5_pk_type type;
+ unsigned int require_binding:1;
+ unsigned int require_eku:1;
+ unsigned int require_krbtgt_otherName:1;
+ unsigned int require_hostname_match:1;
+ unsigned int trustedCertifiers:1;
+ unsigned int anonymous:1;
+};
+
#endif /* PKINIT */
+#define ISTILDE(x) (x == '~')
+#ifdef _WIN32
+# define ISPATHSEP(x) (x == '/' || x =='\\')
+#else
+# define ISPATHSEP(x) (x == '/')
+#endif
+
#endif /* __KRB5_LOCL_H__ */
diff --git a/lib/krb5/krb5_parse_name.3 b/lib/krb5/krb5_parse_name.3
index eb4a2d28c562..85acc72923e5 100644
--- a/lib/krb5/krb5_parse_name.3
+++ b/lib/krb5/krb5_parse_name.3
@@ -61,7 +61,6 @@ quoting it with a backslash
.Pq Dq \e .
A realm should not contain slashes or colons.
.Sh SEE ALSO
-.Xr krb5_425_conv_principal 3 ,
.Xr krb5_build_principal 3 ,
.Xr krb5_free_principal 3 ,
.Xr krb5_sname_to_principal 3 ,
diff --git a/lib/krb5/krb5_parse_name.cat3 b/lib/krb5/krb5_parse_name.cat3
index 47021d33cc2d..f142b9e1f824 100644
--- a/lib/krb5/krb5_parse_name.cat3
+++ b/lib/krb5/krb5_parse_name.cat3
@@ -25,7 +25,7 @@ DDEESSCCRRIIPPTTIIOONN
backslash (``\''). A realm should not contain slashes or colons.
SSEEEE AALLSSOO
- krb5_425_conv_principal(3), krb5_build_principal(3),
- krb5_free_principal(3), krb5_sname_to_principal(3), krb5_unparse_name(3)
+ krb5_build_principal(3), krb5_free_principal(3),
+ krb5_sname_to_principal(3), krb5_unparse_name(3)
HEIMDAL May 1, 2006 HEIMDAL
diff --git a/lib/krb5/krb5_principal.3 b/lib/krb5/krb5_principal.3
index 2998130a80e3..c8d2fec1d7ba 100644
--- a/lib/krb5/krb5_principal.3
+++ b/lib/krb5/krb5_principal.3
@@ -362,7 +362,6 @@ On failure the function returns an error code and set the error
string.
.\" .Sh EXAMPLES
.Sh SEE ALSO
-.Xr krb5_425_conv_principal 3 ,
.Xr krb5_config 3 ,
.Xr krb5.conf 5
.Sh BUGS
diff --git a/lib/krb5/krb5_principal.cat3 b/lib/krb5/krb5_principal.cat3
index 33ab1505da43..98f7aa30f849 100644
--- a/lib/krb5/krb5_principal.cat3
+++ b/lib/krb5/krb5_principal.cat3
@@ -250,7 +250,7 @@ DDEESSCCRRIIPPTTIIOONN
string.
SSEEEE AALLSSOO
- krb5_425_conv_principal(3), krb5_config(3), krb5.conf(5)
+ krb5_config(3), krb5.conf(5)
BBUUGGSS
You can not have a NUL in a component in some of the variable argument
diff --git a/lib/krb5/krb5_set_default_realm.3 b/lib/krb5/krb5_set_default_realm.3
index b1ab61f05f3c..867da30cf346 100644
--- a/lib/krb5/krb5_set_default_realm.3
+++ b/lib/krb5/krb5_set_default_realm.3
@@ -95,7 +95,7 @@ frees all memory allocated by
.Fn krb5_get_default_realm
returns the first default realm for this host.
The realm returned should be freed with
-.Fn free .
+.Fn krb5_xfree .
.Pp
.Fn krb5_get_default_realms
returns a
diff --git a/lib/krb5/krb5_set_default_realm.cat3 b/lib/krb5/krb5_set_default_realm.cat3
index 0100a0654f7d..ff645cc106f0 100644
--- a/lib/krb5/krb5_set_default_realm.cat3
+++ b/lib/krb5/krb5_set_default_realm.cat3
@@ -39,7 +39,7 @@ DDEESSCCRRIIPPTTIIOONN
kkrrbb55__ffrreeee__hhoosstt__rreeaallmm() frees all memory allocated by _r_e_a_l_m_l_i_s_t.
kkrrbb55__ggeett__ddeeffaauulltt__rreeaallmm() returns the first default realm for this host.
- The realm returned should be freed with ffrreeee().
+ The realm returned should be freed with kkrrbb55__xxffrreeee().
kkrrbb55__ggeett__ddeeffaauulltt__rreeaallmmss() returns a NULL terminated list of default
realms for this context. Realms returned by kkrrbb55__ggeett__ddeeffaauulltt__rreeaallmmss()
diff --git a/lib/krb5/krb5_timeofday.3 b/lib/krb5/krb5_timeofday.3
index b065ade1bdd5..f491c6bd9ee5 100644
--- a/lib/krb5/krb5_timeofday.3
+++ b/lib/krb5/krb5_timeofday.3
@@ -33,7 +33,7 @@
.\"
.\" $Id$
.\"
-.Dd Sepember 16, 2006
+.Dd September 16, 2006
.Dt KRB5_TIMEOFDAY 3
.Os HEIMDAL
.Sh NAME
diff --git a/lib/krb5/krb5_timeofday.cat3 b/lib/krb5/krb5_timeofday.cat3
index 6bffbd449866..7778956aab6c 100644
--- a/lib/krb5/krb5_timeofday.cat3
+++ b/lib/krb5/krb5_timeofday.cat3
@@ -52,4 +52,4 @@ DDEESSCCRRIIPPTTIIOONN
SSEEEE AALLSSOO
gettimeofday(2), krb5(3)
-HEIMDAL Sepember 16, 2006 HEIMDAL
+HEIMDAL September 16, 2006 HEIMDAL
diff --git a/lib/krb5/krbhst.c b/lib/krb5/krbhst.c
index 3242cdb99956..f5351288398c 100644
--- a/lib/krb5/krbhst.c
+++ b/lib/krb5/krbhst.c
@@ -3,6 +3,8 @@
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -47,6 +49,14 @@ string_to_proto(const char *string)
return -1;
}
+static int
+is_invalid_tld_srv_target(const char *target)
+{
+ return (strncmp("your-dns-needs-immediate-attention.",
+ target, 35) == 0
+ && strchr(&target[35], '.') == NULL);
+}
+
/*
* set `res' and `count' to the result of looking up SRV RR in DNS for
* `proto', `proto', `realm' using `dns_type'.
@@ -99,26 +109,38 @@ srv_find_realm(krb5_context context, krb5_krbhst_info ***res, int *count,
*res = malloc(num_srv * sizeof(**res));
if(*res == NULL) {
rk_dns_free_data(r);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
rk_dns_srv_order(r);
for(num_srv = 0, rr = r->head; rr; rr = rr->next)
if(rr->type == rk_ns_t_srv) {
- krb5_krbhst_info *hi;
- size_t len = strlen(rr->u.srv->target);
-
- hi = calloc(1, sizeof(*hi) + len);
+ krb5_krbhst_info *hi = NULL;
+ size_t len;
+ int invalid_tld = 1;
+
+ /* Test for top-level domain controlled interruptions */
+ if (!is_invalid_tld_srv_target(rr->u.srv->target)) {
+ invalid_tld = 0;
+ len = strlen(rr->u.srv->target);
+ hi = calloc(1, sizeof(*hi) + len);
+ }
if(hi == NULL) {
rk_dns_free_data(r);
while(--num_srv >= 0)
free((*res)[num_srv]);
free(*res);
*res = NULL;
- return ENOMEM;
+ if (invalid_tld) {
+ krb5_warnx(context,
+ "Domain lookup failed: "
+ "Realm %s needs immediate attention "
+ "see https://icann.org/namecollision",
+ realm);
+ return KRB5_KDC_UNREACH;
+ }
+ return krb5_enomem(context);
}
(*res)[num_srv++] = hi;
@@ -153,9 +175,11 @@ struct krb5_krbhst_data {
#define KD_CONFIG_EXISTS 32
#define KD_LARGE_MSG 64
#define KD_PLUGIN 128
+#define KD_HOSTNAMES 256
krb5_error_code (*get_next)(krb5_context, struct krb5_krbhst_data *,
krb5_krbhst_info**);
+ char *hostname;
unsigned int fallback_count;
struct krb5_krbhst_info *hosts, **index, **end;
@@ -179,11 +203,17 @@ krbhst_get_default_proto(struct krb5_krbhst_data *kd)
return KRB5_KRBHST_UDP;
}
+static int
+krbhst_get_default_port(struct krb5_krbhst_data *kd)
+{
+ return kd->def_port;
+}
+
/*
*
*/
-const char *
+KRB5_LIB_FUNCTION const char * KRB5_LIB_CALL
_krb5_krbhst_get_realm(krb5_krbhst_handle handle)
{
return handle->realm;
@@ -218,6 +248,7 @@ parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
hi->proto = KRB5_KRBHST_TCP;
p += 4;
} else if(strncmp(p, "udp/", 4) == 0) {
+ hi->proto = KRB5_KRBHST_UDP;
p += 4;
}
@@ -253,7 +284,7 @@ parse_hostspec(krb5_context context, struct krb5_krbhst_data *kd,
return hi;
}
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_free_krbhst_info(krb5_krbhst_info *hi)
{
if (hi->ai != NULL)
@@ -261,7 +292,7 @@ _krb5_free_krbhst_info(krb5_krbhst_info *hi)
free(hi);
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_krbhost_info_move(krb5_context context,
krb5_krbhst_info *from,
krb5_krbhst_info **to)
@@ -269,11 +300,8 @@ _krb5_krbhost_info_move(krb5_context context,
size_t hostnamelen = strlen(from->hostname);
/* trailing NUL is included in structure */
*to = calloc(1, sizeof(**to) + hostnamelen);
- if(*to == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*to == NULL)
+ return krb5_enomem(context);
(*to)->proto = from->proto;
(*to)->port = from->port;
@@ -310,7 +338,7 @@ append_host_string(krb5_context context, struct krb5_krbhst_data *kd,
hi = parse_hostspec(context, kd, host, def_port, port);
if(hi == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
append_host_hostinfo(kd, hi);
return 0;
@@ -375,39 +403,11 @@ krb5_krbhst_get_addrinfo(krb5_context context, krb5_krbhst_info *host,
if (host->ai == NULL) {
struct addrinfo hints;
char portstr[NI_MAXSERV];
- char *hostname = host->hostname;
snprintf (portstr, sizeof(portstr), "%d", host->port);
make_hints(&hints, host->proto);
- /**
- * First try this as an IP address, this allows us to add a
- * dot at the end to stop using the search domains.
- */
-
- hints.ai_flags |= AI_NUMERICHOST | AI_NUMERICSERV;
-
ret = getaddrinfo(host->hostname, portstr, &hints, &host->ai);
- if (ret == 0)
- goto out;
-
- /**
- * If the hostname contains a dot, assumes it's a FQDN and
- * don't use search domains since that might be painfully slow
- * when machine is disconnected from that network.
- */
-
- hints.ai_flags &= ~(AI_NUMERICHOST);
-
- if (strchr(hostname, '.') && hostname[strlen(hostname) - 1] != '.') {
- ret = asprintf(&hostname, "%s.", host->hostname);
- if (ret < 0 || hostname == NULL)
- return ENOMEM;
- }
-
- ret = getaddrinfo(hostname, portstr, &hints, &host->ai);
- if (hostname != host->hostname)
- free(hostname);
if (ret) {
ret = krb5_eai_to_heim_errno(ret, errno);
goto out;
@@ -438,6 +438,9 @@ srv_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
krb5_krbhst_info **res;
int count, i;
+ if (krb5_realm_is_lkdc(kd->realm))
+ return;
+
ret = srv_find_realm(context, &res, &count, kd->realm, "SRV", proto, service,
kd->port);
_krb5_debug(context, 2, "searching DNS for realm %s %s.%s -> %d",
@@ -492,14 +495,23 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
struct addrinfo hints;
char portstr[NI_MAXSERV];
+ ret = krb5_config_get_bool_default(context, NULL, KRB5_FALLBACK_DEFAULT,
+ "libdefaults", "use_fallback", NULL);
+ if (!ret) {
+ kd->flags |= KD_FALLBACK;
+ return 0;
+ }
+
_krb5_debug(context, 2, "fallback lookup %d for realm %s (service %s)",
kd->fallback_count, kd->realm, serv_string);
/*
* Don't try forever in case the DNS server keep returning us
* entries (like wildcard entries or the .nu TLD)
+ *
+ * Also don't try LKDC realms since fallback wont work on them at all.
*/
- if(kd->fallback_count >= 5) {
+ if(kd->fallback_count >= 5 || krb5_realm_is_lkdc(kd->realm)) {
kd->flags |= KD_FALLBACK;
return 0;
}
@@ -511,7 +523,7 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
serv_string, kd->fallback_count, kd->realm);
if (ret < 0 || host == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
make_hints(&hints, proto);
snprintf(portstr, sizeof(portstr), "%d", port);
@@ -522,12 +534,26 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
kd->flags |= KD_FALLBACK;
} else {
struct krb5_krbhst_info *hi;
- size_t hostlen = strlen(host);
+ size_t hostlen;
+
+ /* Check for ICANN gTLD Name Collision address (127.0.53.53) */
+ if (ai->ai_family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
+ if (sin->sin_addr.s_addr == htonl(0x7f003535)) {
+ krb5_warnx(context,
+ "Fallback lookup failed: "
+ "Realm %s needs immediate attention "
+ "see https://icann.org/namecollision",
+ kd->realm);
+ return KRB5_KDC_UNREACH;
+ }
+ }
+ hostlen = strlen(host);
hi = calloc(1, sizeof(*hi) + hostlen);
if(hi == NULL) {
free(host);
- return ENOMEM;
+ return krb5_enomem(context);
}
hi->proto = proto;
@@ -547,24 +573,18 @@ fallback_get_hosts(krb5_context context, struct krb5_krbhst_data *kd,
*/
static krb5_error_code
-add_locate(void *ctx, int type, struct sockaddr *addr)
+add_plugin_host(struct krb5_krbhst_data *kd,
+ const char *host,
+ const char *port,
+ int portnum,
+ int proto)
{
struct krb5_krbhst_info *hi;
- struct krb5_krbhst_data *kd = ctx;
- char host[NI_MAXHOST], port[NI_MAXSERV];
struct addrinfo hints, *ai;
- socklen_t socklen;
size_t hostlen;
int ret;
- socklen = socket_sockaddr_size(addr);
-
- ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
- NI_NUMERICHOST|NI_NUMERICSERV);
- if (ret != 0)
- return 0;
-
- make_hints(&hints, krbhst_get_default_proto(kd));
+ make_hints(&hints, proto);
ret = getaddrinfo(host, port, &hints, &ai);
if (ret)
return 0;
@@ -572,11 +592,13 @@ add_locate(void *ctx, int type, struct sockaddr *addr)
hostlen = strlen(host);
hi = calloc(1, sizeof(*hi) + hostlen);
- if(hi == NULL)
+ if (hi == NULL) {
+ freeaddrinfo(ai);
return ENOMEM;
+ }
- hi->proto = krbhst_get_default_proto(kd);
- hi->port = hi->def_port = socket_get_port(addr);
+ hi->proto = proto;
+ hi->port = hi->def_port = portnum;
hi->ai = ai;
memmove(hi->hostname, host, hostlen);
hi->hostname[hostlen] = '\0';
@@ -585,44 +607,103 @@ add_locate(void *ctx, int type, struct sockaddr *addr)
return 0;
}
+static krb5_error_code
+add_locate(void *ctx, int type, struct sockaddr *addr)
+{
+ struct krb5_krbhst_data *kd = ctx;
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ socklen_t socklen;
+ krb5_error_code ret;
+ int proto, portnum;
+
+ socklen = socket_sockaddr_size(addr);
+ portnum = socket_get_port(addr);
+
+ ret = getnameinfo(addr, socklen, host, sizeof(host), port, sizeof(port),
+ NI_NUMERICHOST|NI_NUMERICSERV);
+ if (ret != 0)
+ return 0;
+
+ if (kd->port)
+ snprintf(port, sizeof(port), "%d", kd->port);
+ else if (atoi(port) == 0)
+ snprintf(port, sizeof(port), "%d", krbhst_get_default_port(kd));
+
+ proto = krbhst_get_default_proto(kd);
+
+ ret = add_plugin_host(kd, host, port, portnum, proto);
+ if (ret)
+ return ret;
+
+ /*
+ * This is really kind of broken and should be solved a different
+ * way, some sites block UDP, and we don't, in the general case,
+ * fall back to TCP, that should also be done. But since that
+ * should require us to invert the whole "find kdc" stack, let put
+ * this in for now.
+ */
+
+ if (proto == KRB5_KRBHST_UDP) {
+ ret = add_plugin_host(kd, host, port, portnum, KRB5_KRBHST_TCP);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+struct plctx {
+ enum locate_service_type type;
+ struct krb5_krbhst_data *kd;
+ unsigned long flags;
+};
+
+static KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+plcallback(krb5_context context,
+ const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_service_locate_ftable *locate = plug;
+ struct plctx *plctx = userctx;
+
+ if (locate->minor_version >= KRB5_PLUGIN_LOCATE_VERSION_2)
+ return locate->lookup(plugctx, plctx->flags, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd);
+
+ if (plctx->flags & KRB5_PLF_ALLOW_HOMEDIR)
+ return locate->old_lookup(plugctx, plctx->type, plctx->kd->realm, 0, 0, add_locate, plctx->kd);
+
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
static void
plugin_get_hosts(krb5_context context,
struct krb5_krbhst_data *kd,
enum locate_service_type type)
{
- struct krb5_plugin *list = NULL, *e;
- krb5_error_code ret;
+ struct plctx ctx = { type, kd, 0 };
- ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA,
- KRB5_PLUGIN_LOCATE, &list);
- if(ret != 0 || list == NULL)
- return;
+ if (_krb5_homedir_access(context))
+ ctx.flags |= KRB5_PLF_ALLOW_HOMEDIR;
- for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
- krb5plugin_service_locate_ftable *service;
- void *ctx;
-
- service = _krb5_plugin_get_symbol(e);
- if (service->minor_version != 0)
- continue;
-
- (*service->init)(context, &ctx);
- ret = (*service->lookup)(ctx, type, kd->realm, 0, 0, add_locate, kd);
- (*service->fini)(ctx);
- if (ret && ret != KRB5_PLUGIN_NO_HANDLE) {
- krb5_set_error_message(context, ret,
- N_("Locate plugin failed to lookup realm %s: %d", ""),
- kd->realm, ret);
- break;
- } else if (ret == 0) {
- _krb5_debug(context, 2, "plugin found result for realm %s", kd->realm);
- kd->flags |= KD_CONFIG_EXISTS;
- }
+ _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_LOCATE,
+ KRB5_PLUGIN_LOCATE_VERSION_0,
+ 0, &ctx, plcallback);
+}
- }
- _krb5_plugin_free(list);
+/*
+ *
+ */
+
+static void
+hostnames_get_hosts(krb5_context context,
+ struct krb5_krbhst_data *kd,
+ const char *type)
+{
+ kd->flags |= KD_HOSTNAMES;
+ if (kd->hostname)
+ append_host_string(context, kd, kd->hostname, kd->def_port, kd->port);
}
+
/*
*
*/
@@ -634,6 +715,12 @@ kdc_get_next(krb5_context context,
{
krb5_error_code ret;
+ if ((kd->flags & KD_HOSTNAMES) == 0) {
+ hostnames_get_hosts(context, kd, "kdc");
+ if(get_next(kd, host))
+ return 0;
+ }
+
if ((kd->flags & KD_PLUGIN) == 0) {
plugin_get_hosts(context, kd, locate_service_kdc);
kd->flags |= KD_PLUGIN;
@@ -806,60 +893,20 @@ kpasswd_get_next(krb5_context context,
return KRB5_KDC_UNREACH;
}
-static krb5_error_code
-krb524_get_next(krb5_context context,
- struct krb5_krbhst_data *kd,
- krb5_krbhst_info **host)
+static void
+krbhost_dealloc(void *ptr)
{
- if ((kd->flags & KD_PLUGIN) == 0) {
- plugin_get_hosts(context, kd, locate_service_krb524);
- kd->flags |= KD_PLUGIN;
- if(get_next(kd, host))
- return 0;
- }
-
- if((kd->flags & KD_CONFIG) == 0) {
- config_get_hosts(context, kd, "krb524_server");
- if(get_next(kd, host))
- return 0;
- kd->flags |= KD_CONFIG;
- }
-
- if (kd->flags & KD_CONFIG_EXISTS) {
- _krb5_debug(context, 1,
- "Configuration exists for realm %s, wont go to DNS",
- kd->realm);
- return KRB5_KDC_UNREACH;
- }
-
- if(context->srv_lookup) {
- if((kd->flags & KD_SRV_UDP) == 0) {
- srv_get_hosts(context, kd, "udp", "krb524");
- kd->flags |= KD_SRV_UDP;
- if(get_next(kd, host))
- return 0;
- }
-
- if((kd->flags & KD_SRV_TCP) == 0) {
- srv_get_hosts(context, kd, "tcp", "krb524");
- kd->flags |= KD_SRV_TCP;
- if(get_next(kd, host))
- return 0;
- }
- }
-
- /* no matches -> try kdc */
+ struct krb5_krbhst_data *handle = (struct krb5_krbhst_data *)ptr;
+ krb5_krbhst_info *h, *next;
- if (krbhst_empty(kd)) {
- kd->flags = 0;
- kd->port = kd->def_port;
- kd->get_next = kdc_get_next;
- return (*kd->get_next)(context, kd, host);
+ for (h = handle->hosts; h != NULL; h = next) {
+ next = h->next;
+ _krb5_free_krbhst_info(h);
}
+ if (handle->hostname)
+ free(handle->hostname);
- _krb5_debug(context, 0, "No kpasswd entries found for realm %s", kd->realm);
-
- return KRB5_KDC_UNREACH;
+ free(handle->realm);
}
static struct krb5_krbhst_data*
@@ -870,11 +917,11 @@ common_init(krb5_context context,
{
struct krb5_krbhst_data *kd;
- if((kd = calloc(1, sizeof(*kd))) == NULL)
+ if ((kd = heim_alloc(sizeof(*kd), "krbhst-context", krbhost_dealloc)) == NULL)
return NULL;
if((kd->realm = strdup(realm)) == NULL) {
- free(kd);
+ heim_release(kd);
return NULL;
}
@@ -917,6 +964,8 @@ krb5_krbhst_init_flags(krb5_context context,
int def_port;
const char *service;
+ *handle = NULL;
+
switch(type) {
case KRB5_KRBHST_KDC:
next = kdc_get_next;
@@ -935,11 +984,6 @@ krb5_krbhst_init_flags(krb5_context context,
KPASSWD_PORT));
service = "change_password";
break;
- case KRB5_KRBHST_KRB524:
- next = krb524_get_next;
- def_port = ntohs(krb5_getportbyname (context, "krb524", "udp", 4444));
- service = "524";
- break;
default:
krb5_set_error_message(context, ENOTTY,
N_("unknown krbhst type (%u)", ""), type);
@@ -987,6 +1031,22 @@ krb5_krbhst_next_as_string(krb5_context context,
return krb5_krbhst_format_string(context, host, hostname, hostlen);
}
+/*
+ *
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_krbhst_set_hostname(krb5_context context,
+ krb5_krbhst_handle handle,
+ const char *hostname)
+{
+ if (handle->hostname)
+ free(handle->hostname);
+ handle->hostname = strdup(hostname);
+ if (handle->hostname == NULL)
+ return ENOMEM;
+ return 0;
+}
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
@@ -997,20 +1057,11 @@ krb5_krbhst_reset(krb5_context context, krb5_krbhst_handle handle)
KRB5_LIB_FUNCTION void KRB5_LIB_CALL
krb5_krbhst_free(krb5_context context, krb5_krbhst_handle handle)
{
- krb5_krbhst_info *h, *next;
-
- if (handle == NULL)
- return;
-
- for (h = handle->hosts; h != NULL; h = next) {
- next = h->next;
- _krb5_free_krbhst_info(h);
- }
-
- free(handle->realm);
- free(handle);
+ heim_release(handle);
}
+#ifndef HEIMDAL_SMALLER
+
/* backwards compatibility ahead */
static krb5_error_code
@@ -1037,7 +1088,7 @@ gethostlist(krb5_context context, const char *realm,
*hostlist = calloc(nhost + 1, sizeof(**hostlist));
if(*hostlist == NULL) {
krb5_krbhst_free(context, handle);
- return ENOMEM;
+ return krb5_enomem(context);
}
krb5_krbhst_reset(context, handle);
@@ -1047,7 +1098,7 @@ gethostlist(krb5_context context, const char *realm,
if(((*hostlist)[nhost++] = strdup(host)) == NULL) {
krb5_free_krbhst(context, *hostlist);
krb5_krbhst_free(context, handle);
- return ENOMEM;
+ return krb5_enomem(context);
}
}
(*hostlist)[nhost] = NULL;
@@ -1091,7 +1142,6 @@ krb5_get_krb524hst (krb5_context context,
return gethostlist(context, *realm, KRB5_KRBHST_KRB524, hostlist);
}
-
/*
* return an malloced list of KDC's for `realm' in `hostlist'
*/
@@ -1119,3 +1169,5 @@ krb5_free_krbhst (krb5_context context,
free (hostlist);
return 0;
}
+
+#endif /* HEIMDAL_SMALLER */
diff --git a/lib/krb5/kuserok.c b/lib/krb5/kuserok.c
index 2fe4e490aa41..492d0c6b858e 100644
--- a/lib/krb5/kuserok.c
+++ b/lib/krb5/kuserok.c
@@ -32,9 +32,230 @@
*/
#include "krb5_locl.h"
+#include "kuserok_plugin.h"
#include <dirent.h>
-#ifndef _WIN32
+#ifndef SYSTEM_K5LOGIN_DIR
+/*
+ * System k5login location. File namess in this directory are expected
+ * to be usernames and to contain a list of principals allowed to login
+ * as the user named the same as the file.
+ */
+#define SYSTEM_K5LOGIN_DIR SYSCONFDIR "/k5login.d"
+#endif
+
+/* Plugin framework bits */
+
+struct plctx {
+ const char *rule;
+ const char *k5login_dir;
+ const char *luser;
+ krb5_const_principal principal;
+ unsigned int flags;
+ krb5_boolean result;
+};
+
+static krb5_error_code KRB5_LIB_CALL
+plcallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_kuserok_ftable *locate = plug;
+ struct plctx *plctx = userctx;
+
+ return locate->kuserok(plugctx, context, plctx->rule, plctx->flags,
+ plctx->k5login_dir, plctx->luser, plctx->principal,
+ &plctx->result);
+}
+
+static krb5_error_code plugin_reg_ret;
+static krb5plugin_kuserok_ftable kuserok_simple_plug;
+static krb5plugin_kuserok_ftable kuserok_sys_k5login_plug;
+static krb5plugin_kuserok_ftable kuserok_user_k5login_plug;
+static krb5plugin_kuserok_ftable kuserok_deny_plug;
+
+static void
+reg_def_plugins_once(void *ctx)
+{
+ krb5_error_code ret;
+ krb5_context context = ctx;
+
+ plugin_reg_ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK,
+ &kuserok_simple_plug);
+ ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK, &kuserok_sys_k5login_plug);
+ if (!plugin_reg_ret)
+ plugin_reg_ret = ret;
+ ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK, &kuserok_user_k5login_plug);
+ if (!plugin_reg_ret)
+ plugin_reg_ret = ret;
+ ret = krb5_plugin_register(context, PLUGIN_TYPE_DATA,
+ KRB5_PLUGIN_KUSEROK, &kuserok_deny_plug);
+ if (!plugin_reg_ret)
+ plugin_reg_ret = ret;
+}
+
+/**
+ * This function is designed to be portable for Win32 and POSIX. The
+ * design does lead to multiple getpwnam_r() calls, but this is probably
+ * not a big deal.
+ *
+ * Inputs:
+ *
+ * @param context A krb5_context
+ * @param filename Name of item to introspection
+ * @param is_system_location TRUE if the dir/file are system locations or
+ * FALSE if they are user home directory locations
+ * @param dir Directory (optional)
+ * @param dirlstat A pointer to struct stat for the directory (optional)
+ * @param file File (optional)
+ * @param owner Name of user that is expected to own the file
+ */
+
+static krb5_error_code
+check_owner_dir(krb5_context context,
+ const char *filename,
+ krb5_boolean is_system_location,
+ DIR *dir,
+ struct stat *dirlstat,
+ const char *owner)
+{
+#ifdef _WIN32
+ /*
+ * XXX Implement this!
+ *
+ * The thing to do is to call _get_osfhandle() on fileno(file) and
+ * dirfd(dir) to get HANDLEs to the same, then call
+ * GetSecurityInfo() on those HANDLEs to get the security descriptor
+ * (SD), then check the owner and DACL. Checking the DACL sounds
+ * like a lot of work (what, derive a mode from the ACL the way
+ * NFSv4 servers do?). Checking the owner means doing an LSARPC
+ * lookup at least (to get the user's SID).
+ */
+ if (is_system_location || owner == NULL)
+ return 0;
+ krb5_set_error_message(context, EACCES,
+ "User k5login files not supported on Windows");
+ return EACCES;
+#else
+ struct passwd pw, *pwd = NULL;
+ char pwbuf[2048];
+ struct stat st;
+
+ heim_assert(owner != NULL, "no directory owner ?");
+
+ if (rk_getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) {
+ krb5_set_error_message(context, errno,
+ "User unknown %s (getpwnam_r())", owner);
+ return EACCES;
+ }
+ if (pwd == NULL) {
+ krb5_set_error_message(context, EACCES, "no user %s", owner);
+ return EACCES;
+ }
+
+ if (fstat(dirfd(dir), &st) == -1) {
+ krb5_set_error_message(context, EACCES,
+ "fstat(%s) of k5login.d failed",
+ filename);
+ return EACCES;
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ krb5_set_error_message(context, ENOTDIR, "%s not a directory",
+ filename);
+ return ENOTDIR;
+ }
+ if (st.st_dev != dirlstat->st_dev || st.st_ino != dirlstat->st_ino) {
+ krb5_set_error_message(context, EACCES,
+ "%s was renamed during kuserok "
+ "operation", filename);
+ return EACCES;
+ }
+ if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
+ krb5_set_error_message(context, EACCES,
+ "%s has world and/or group write "
+ "permissions", filename);
+ return EACCES;
+ }
+ if (pwd->pw_uid != st.st_uid && st.st_uid != 0) {
+ krb5_set_error_message(context, EACCES,
+ "%s not owned by the user (%s) or root",
+ filename, owner);
+ return EACCES;
+ }
+
+ return 0;
+#endif
+}
+
+static krb5_error_code
+check_owner_file(krb5_context context,
+ const char *filename,
+ FILE *file, const char *owner)
+{
+#ifdef _WIN32
+ /*
+ * XXX Implement this!
+ *
+ * The thing to do is to call _get_osfhandle() on fileno(file) and
+ * dirfd(dir) to get HANDLEs to the same, then call
+ * GetSecurityInfo() on those HANDLEs to get the security descriptor
+ * (SD), then check the owner and DACL. Checking the DACL sounds
+ * like a lot of work (what, derive a mode from the ACL the way
+ * NFSv4 servers do?). Checking the owner means doing an LSARPC
+ * lookup at least (to get the user's SID).
+ */
+ if (owner == NULL)
+ return 0;
+
+ krb5_set_error_message(context, EACCES,
+ "User k5login files not supported on Windows");
+ return EACCES;
+#else
+ struct passwd pw, *pwd = NULL;
+ char pwbuf[2048];
+ struct stat st;
+
+ if (owner == NULL)
+ return 0;
+
+ if (rk_getpwnam_r(owner, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) {
+ krb5_set_error_message(context, errno,
+ "User unknown %s (getpwnam_r())", owner);
+ return EACCES;
+ }
+ if (pwd == NULL) {
+ krb5_set_error_message(context, EACCES, "no user %s", owner);
+ return EACCES;
+ }
+
+ if (fstat(fileno(file), &st) == -1) {
+ krb5_set_error_message(context, EACCES, "fstat(%s) of k5login failed",
+ filename);
+ return EACCES;
+ }
+ if (S_ISDIR(st.st_mode)) {
+ krb5_set_error_message(context, EISDIR, "k5login: %s is a directory",
+ filename);
+ return EISDIR;
+ }
+ if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
+ krb5_set_error_message(context, EISDIR,
+ "k5login %s has world and/or group write "
+ "permissions", filename);
+ return EACCES;
+ }
+ if (pwd->pw_uid != st.st_uid && st.st_uid != 0) {
+ krb5_set_error_message(context, EACCES,
+ "k5login %s not owned by the user or root",
+ filename);
+ return EACCES;
+ }
+
+ return 0;
+#endif
+}
+
/* see if principal is mentioned in the filename access file, return
TRUE (in result) if so, FALSE otherwise */
@@ -42,160 +263,152 @@
static krb5_error_code
check_one_file(krb5_context context,
const char *filename,
- struct passwd *pwd,
- krb5_principal principal,
+ const char *owner,
+ krb5_boolean is_system_location,
+ krb5_const_principal principal,
krb5_boolean *result)
{
FILE *f;
char buf[BUFSIZ];
krb5_error_code ret;
- struct stat st;
*result = FALSE;
- f = fopen (filename, "r");
+ f = fopen(filename, "r");
if (f == NULL)
return errno;
rk_cloexec_file(f);
- /* check type and mode of file */
- if (fstat(fileno(f), &st) != 0) {
- fclose (f);
- return errno;
- }
- if (S_ISDIR(st.st_mode)) {
- fclose (f);
- return EISDIR;
- }
- if (st.st_uid != pwd->pw_uid && st.st_uid != 0) {
- fclose (f);
- return EACCES;
- }
- if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
- fclose (f);
- return EACCES;
- }
+ ret = check_owner_file(context, filename, f, owner);
+ if (ret)
+ goto out;
- while (fgets (buf, sizeof(buf), f) != NULL) {
+ while (fgets(buf, sizeof(buf), f) != NULL) {
krb5_principal tmp;
char *newline = buf + strcspn(buf, "\n");
- if(*newline != '\n') {
+ if (*newline != '\n') {
int c;
c = fgetc(f);
- if(c != EOF) {
- while(c != EOF && c != '\n')
+ if (c != EOF) {
+ while (c != EOF && c != '\n')
c = fgetc(f);
/* line was too long, so ignore it */
continue;
}
}
*newline = '\0';
- ret = krb5_parse_name (context, buf, &tmp);
+ ret = krb5_parse_name(context, buf, &tmp);
if (ret)
continue;
- *result = krb5_principal_compare (context, principal, tmp);
- krb5_free_principal (context, tmp);
+ *result = krb5_principal_compare(context, principal, tmp);
+ krb5_free_principal(context, tmp);
if (*result) {
fclose (f);
return 0;
}
}
- fclose (f);
+
+out:
+ fclose(f);
return 0;
}
static krb5_error_code
check_directory(krb5_context context,
const char *dirname,
- struct passwd *pwd,
- krb5_principal principal,
+ const char *owner,
+ krb5_boolean is_system_location,
+ krb5_const_principal principal,
krb5_boolean *result)
{
DIR *d;
struct dirent *dent;
char filename[MAXPATHLEN];
+ size_t len;
krb5_error_code ret = 0;
struct stat st;
*result = FALSE;
- if(lstat(dirname, &st) < 0)
+ if (lstat(dirname, &st) < 0)
return errno;
- if (!S_ISDIR(st.st_mode))
+ if (!S_ISDIR(st.st_mode)) {
+ krb5_set_error_message(context, ENOTDIR, "k5login.d not a directory");
return ENOTDIR;
+ }
- if (st.st_uid != pwd->pw_uid && st.st_uid != 0)
- return EACCES;
- if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0)
- return EACCES;
-
- if((d = opendir(dirname)) == NULL)
+ if ((d = opendir(dirname)) == NULL) {
+ krb5_set_error_message(context, ENOTDIR, "Could not open k5login.d");
return errno;
-
- {
- int fd;
- struct stat st2;
-
- fd = dirfd(d);
- if(fstat(fd, &st2) < 0) {
- closedir(d);
- return errno;
- }
- if(st.st_dev != st2.st_dev || st.st_ino != st2.st_ino) {
- closedir(d);
- return EACCES;
- }
}
- while((dent = readdir(d)) != NULL) {
- if(strcmp(dent->d_name, ".") == 0 ||
+ ret = check_owner_dir(context, dirname, is_system_location, d, &st, owner);
+ if (ret)
+ goto out;
+
+ while ((dent = readdir(d)) != NULL) {
+ /*
+ * XXX: Should we also skip files whose names start with "."?
+ * Vim ".filename.swp" files are also good candidates to skip.
+ * Once we ignore "#*" and "*~", it is not clear what other
+ * heuristics to apply.
+ */
+ if (strcmp(dent->d_name, ".") == 0 ||
strcmp(dent->d_name, "..") == 0 ||
dent->d_name[0] == '#' || /* emacs autosave */
dent->d_name[strlen(dent->d_name) - 1] == '~') /* emacs backup */
continue;
- snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name);
- ret = check_one_file(context, filename, pwd, principal, result);
- if(ret == 0 && *result == TRUE)
- break;
+ len = snprintf(filename, sizeof(filename), "%s/%s", dirname, dent->d_name);
+ /* Skip too-long filenames that got truncated by snprintf() */
+ if (len < sizeof(filename)) {
+ ret = check_one_file(context, filename, owner, is_system_location,
+ principal, result);
+ if (ret == 0 && *result == TRUE)
+ break;
+ }
ret = 0; /* don't propagate errors upstream */
}
+
+out:
closedir(d);
return ret;
}
-#endif /* !_WIN32 */
-
-static krb5_boolean
-match_local_principals(krb5_context context,
- krb5_principal principal,
- const char *luser)
+static krb5_error_code
+check_an2ln(krb5_context context,
+ krb5_const_principal principal,
+ const char *luser,
+ krb5_boolean *result)
{
krb5_error_code ret;
- krb5_realm *realms, *r;
- krb5_boolean result = FALSE;
+ char *lname;
+#if 0
+ /* XXX Should we make this an option? */
/* multi-component principals can never match */
- if(krb5_principal_get_comp_string(context, principal, 1) != NULL)
- return FALSE;
+ if (krb5_principal_get_comp_string(context, principal, 1) != NULL) {
+ *result = FALSE;
+ return 0;
+ }
+#endif
- ret = krb5_get_default_realms (context, &realms);
+ lname = malloc(strlen(luser) + 1);
+ if (lname == NULL)
+ return krb5_enomem(context);
+ ret = krb5_aname_to_localname(context, principal, strlen(luser)+1, lname);
if (ret)
- return FALSE;
+ goto out;
+ if (strcmp(lname, luser) == 0)
+ *result = TRUE;
+ else
+ *result = FALSE;
+
+out:
+ free(lname);
+ return 0;
- for (r = realms; *r != NULL; ++r) {
- if(strcmp(krb5_principal_get_realm(context, principal),
- *r) != 0)
- continue;
- if(strcmp(krb5_principal_get_comp_string(context, principal, 0),
- luser) == 0) {
- result = TRUE;
- break;
- }
- }
- krb5_free_host_realm (context, realms);
- return result;
}
/**
@@ -204,8 +417,8 @@ match_local_principals(krb5_context context,
*
* The user may have a ~/.k5login file listing principals that are
* allowed to login as that user. If that file does not exist, all
- * principals with a first component identical to the username, and a
- * realm considered local, are allowed access.
+ * principals with a only one component that is identical to the
+ * username, and a realm considered local, are allowed access.
*
* The .k5login file must contain one principal per line, be owned by
* user and not be writable by group or other (but must be readable by
@@ -234,70 +447,297 @@ match_local_principals(krb5_context context,
*/
KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
-krb5_kuserok (krb5_context context,
+krb5_kuserok(krb5_context context,
+ krb5_principal principal,
+ const char *luser)
+{
+ return _krb5_kuserok(context, principal, luser, TRUE);
+}
+
+
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
+_krb5_kuserok(krb5_context context,
krb5_principal principal,
- const char *luser)
+ const char *luser,
+ krb5_boolean an2ln_ok)
{
-#ifndef _WIN32
- char *buf;
- size_t buflen;
- struct passwd *pwd = NULL;
- char *profile_dir = NULL;
+ static heim_base_once_t reg_def_plugins = HEIM_BASE_ONCE_INIT;
krb5_error_code ret;
- krb5_boolean result = FALSE;
+ struct plctx ctx;
+ char **rules;
+
+ /*
+ * XXX we should have a struct with a krb5_context field and a
+ * krb5_error_code fied and pass the address of that as the ctx
+ * argument of heim_base_once_f(). For now we use a static to
+ * communicate failures. Actually, we ignore failures anyways,
+ * since we can't return them.
+ */
+ heim_base_once_f(&reg_def_plugins, context, reg_def_plugins_once);
+
+ ctx.flags = 0;
+ ctx.luser = luser;
+ ctx.principal = principal;
+ ctx.result = FALSE;
+
+ ctx.k5login_dir = krb5_config_get_string(context, NULL, "libdefaults",
+ "k5login_directory", NULL);
+
+ if (an2ln_ok)
+ ctx.flags |= KUSEROK_ANAME_TO_LNAME_OK;
+
+ if (krb5_config_get_bool_default(context, NULL, FALSE, "libdefaults",
+ "k5login_authoritative", NULL))
+ ctx.flags |= KUSEROK_K5LOGIN_IS_AUTHORITATIVE;
+
+ if ((ctx.flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE) && plugin_reg_ret)
+ return plugin_reg_ret; /* fail safe */
+
+ rules = krb5_config_get_strings(context, NULL, "libdefaults",
+ "kuserok", NULL);
+ if (rules == NULL) {
+ /* Default: check ~/.k5login */
+ ctx.rule = "USER-K5LOGIN";
+
+ ret = plcallback(context, &kuserok_user_k5login_plug, NULL, &ctx);
+ if (ret == 0)
+ goto out;
+
+ ctx.rule = "SIMPLE";
+ ret = plcallback(context, &kuserok_simple_plug, NULL, &ctx);
+ if (ret == 0)
+ goto out;
+
+ ctx.result = FALSE;
+ } else {
+ size_t n;
+
+ for (n = 0; rules[n]; n++) {
+ ctx.rule = rules[n];
+
+ ret = _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_KUSEROK,
+ KRB5_PLUGIN_KUSEROK_VERSION_0, 0,
+ &ctx, plcallback);
+ if (ret != KRB5_PLUGIN_NO_HANDLE)
+ goto out;
+ }
+ }
- krb5_boolean found_file = FALSE;
+out:
+ krb5_config_free_strings(rules);
-#ifdef POSIX_GETPWNAM_R
- char pwbuf[2048];
- struct passwd pw;
+ return ctx.result;
+}
+
+/*
+ * Simple kuserok: check that the lname for the aname matches luser.
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_simple_plug_f(void *plug_ctx, krb5_context context, const char *rule,
+ unsigned int flags, const char *k5login_dir,
+ const char *luser, krb5_const_principal principal,
+ krb5_boolean *result)
+{
+ krb5_error_code ret;
+
+ if (strcmp(rule, "SIMPLE") != 0 || (flags & KUSEROK_ANAME_TO_LNAME_OK) == 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ ret = check_an2ln(context, principal, luser, result);
+ if (ret == 0 && *result == FALSE)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ return 0;
+}
+
+/*
+ * Check k5login files in a system location, rather than in home
+ * directories.
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_sys_k5login_plug_f(void *plug_ctx, krb5_context context,
+ const char *rule, unsigned int flags,
+ const char *k5login_dir, const char *luser,
+ krb5_const_principal principal, krb5_boolean *result)
+{
+ char filename[MAXPATHLEN];
+ size_t len;
+ const char *profile_dir = NULL;
+ krb5_error_code ret;
+
+ *result = FALSE;
+
+ if (strcmp(rule, "SYSTEM-K5LOGIN") != 0 &&
+ strncmp(rule, "SYSTEM-K5LOGIN:", strlen("SYSTEM-K5LOGIN:")) != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
- if(getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0)
- return FALSE;
+ profile_dir = strchr(rule, ':');
+ if (profile_dir == NULL)
+ profile_dir = k5login_dir ? k5login_dir : SYSTEM_K5LOGIN_DIR;
+ else
+ profile_dir++;
+
+ len = snprintf(filename, sizeof(filename), "%s/%s", profile_dir, luser);
+ if (len < sizeof(filename)) {
+ ret = check_one_file(context, filename, NULL, TRUE, principal, result);
+
+ if (ret == 0 &&
+ ((flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE) || *result == TRUE))
+ return 0;
+ }
+
+ *result = FALSE;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/*
+ * Check ~luser/.k5login and/or ~/luser/.k5login.d
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_user_k5login_plug_f(void *plug_ctx, krb5_context context,
+ const char *rule, unsigned int flags,
+ const char *k5login_dir, const char *luser,
+ krb5_const_principal principal,
+ krb5_boolean *result)
+{
+#ifdef _WIN32
+ return KRB5_PLUGIN_NO_HANDLE;
#else
- pwd = getpwnam (luser);
-#endif
- if (pwd == NULL)
- return FALSE;
- profile_dir = pwd->pw_dir;
+ char *path;
+ char *path_exp;
+ const char *profile_dir = NULL;
+ krb5_error_code ret;
+ krb5_boolean found_file = FALSE;
+ struct passwd pw, *pwd = NULL;
+ char pwbuf[2048];
-#define KLOGIN "/.k5login"
- buflen = strlen(profile_dir) + sizeof(KLOGIN) + 2; /* 2 for .d */
- buf = malloc(buflen);
- if(buf == NULL)
- return FALSE;
- /* check user's ~/.k5login */
- strlcpy(buf, profile_dir, buflen);
- strlcat(buf, KLOGIN, buflen);
- ret = check_one_file(context, buf, pwd, principal, &result);
+ if (strcmp(rule, "USER-K5LOGIN") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
- if(ret == 0 && result == TRUE) {
- free(buf);
- return TRUE;
+ profile_dir = k5login_dir;
+ if (profile_dir == NULL) {
+ /* Don't deadlock with gssd or anything of the sort */
+ if (!_krb5_homedir_access(context))
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ if (getpwnam_r(luser, &pw, pwbuf, sizeof(pwbuf), &pwd) != 0) {
+ krb5_set_error_message(context, errno, "User unknown (getpwnam_r())");
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ if (pwd == NULL) {
+ krb5_set_error_message(context, errno, "User unknown (getpwnam())");
+ return KRB5_PLUGIN_NO_HANDLE;
+ }
+ profile_dir = pwd->pw_dir;
}
- if(ret != ENOENT)
- found_file = TRUE;
+#define KLOGIN "/.k5login"
- strlcat(buf, ".d", buflen);
- ret = check_directory(context, buf, pwd, principal, &result);
- free(buf);
- if(ret == 0 && result == TRUE)
- return TRUE;
+ if (asprintf(&path, "%s/.k5login.d", profile_dir) == -1)
+ return krb5_enomem(context);
- if(ret != ENOENT && ret != ENOTDIR)
+ ret = _krb5_expand_path_tokensv(context, path, 1, &path_exp,
+ "luser", luser, NULL);
+ free(path);
+ if (ret)
+ return ret;
+ path = path_exp;
+
+ /* check user's ~/.k5login */
+ path[strlen(path) - strlen(".d")] = '\0';
+ ret = check_one_file(context, path, luser, FALSE, principal, result);
+
+ /*
+ * A match in ~/.k5login is sufficient. A non-match, falls through to the
+ * .k5login.d code below.
+ */
+ if (ret == 0 && *result == TRUE) {
+ free(path);
+ return 0;
+ }
+ if (ret != ENOENT)
found_file = TRUE;
- /* finally if no files exist, allow all principals matching
- <localuser>@<LOCALREALM> */
- if(found_file == FALSE)
- return match_local_principals(context, principal, luser);
+ /*
+ * A match in ~/.k5login.d/somefile is sufficient. A non-match, falls
+ * through to the code below that handles negative results.
+ *
+ * XXX: put back the .d; clever|hackish? you decide
+ */
+ path[strlen(path)] = '.';
+ ret = check_directory(context, path, luser, FALSE, principal, result);
+ free(path);
+ if (ret == 0 && *result == TRUE)
+ return 0;
+ if (ret != ENOENT && ret != ENOTDIR)
+ found_file = TRUE;
- return FALSE;
-#else
- /* The .k5login file may be on a remote profile and we don't have
- access to the profile until we have a token handle for the
- user's credentials. */
- return match_local_principals(context, principal, luser);
+ /*
+ * When either ~/.k5login or ~/.k5login.d/ exists, but neither matches
+ * and we're authoritative, we're done. Otherwise, give other plugins
+ * a chance.
+ */
+ *result = FALSE;
+ if (found_file && (flags & KUSEROK_K5LOGIN_IS_AUTHORITATIVE))
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
#endif
}
+
+static krb5_error_code KRB5_LIB_CALL
+kuserok_deny_plug_f(void *plug_ctx, krb5_context context, const char *rule,
+ unsigned int flags, const char *k5login_dir,
+ const char *luser, krb5_const_principal principal,
+ krb5_boolean *result)
+{
+ if (strcmp(rule, "DENY") != 0)
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ *result = FALSE;
+ return 0;
+}
+
+static krb5_error_code KRB5_LIB_CALL
+kuser_ok_null_plugin_init(krb5_context context, void **ctx)
+{
+ *ctx = NULL;
+ return 0;
+}
+
+static void KRB5_LIB_CALL
+kuser_ok_null_plugin_fini(void *ctx)
+{
+ return;
+}
+
+static krb5plugin_kuserok_ftable kuserok_simple_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_simple_plug_f,
+};
+
+static krb5plugin_kuserok_ftable kuserok_sys_k5login_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_sys_k5login_plug_f,
+};
+
+static krb5plugin_kuserok_ftable kuserok_user_k5login_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_user_k5login_plug_f,
+};
+
+static krb5plugin_kuserok_ftable kuserok_deny_plug = {
+ KRB5_PLUGIN_KUSEROK_VERSION_0,
+ kuser_ok_null_plugin_init,
+ kuser_ok_null_plugin_fini,
+ kuserok_deny_plug_f,
+};
+
diff --git a/lib/krb5/kuserok_plugin.h b/lib/krb5/kuserok_plugin.h
new file mode 100644
index 000000000000..b45071d18e73
--- /dev/null
+++ b/lib/krb5/kuserok_plugin.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#ifndef HEIMDAL_KRB5_KUSEROK_PLUGIN_H
+#define HEIMDAL_KRB5_KUSEROK_PLUGIN_H 1
+
+#define KRB5_PLUGIN_KUSEROK "krb5_plugin_kuserok"
+#define KRB5_PLUGIN_KUSEROK_VERSION_0 0
+
+/** @struct krb5plugin_kuserok_ftable_desc
+ *
+ * @brief Description of the krb5_kuserok(3) plugin facility.
+ *
+ * The krb5_kuserok(3) function is pluggable. The plugin is named
+ * KRB5_PLUGIN_KUSEROK ("krb5_plugin_kuserok"), with a single minor
+ * version, KRB5_PLUGIN_KUSEROK_VERSION_0 (0).
+ *
+ * The plugin for krb5_kuserok(3) consists of a data symbol referencing
+ * a structure of type krb5plugin_kuserok_ftable, with four fields:
+ *
+ * @param init Plugin initialization function (see krb5-plugin(7))
+ *
+ * @param minor_version The plugin minor version number (0)
+ *
+ * @param fini Plugin finalization function
+ *
+ * @param kuserok Plugin kuserok function
+ *
+ * The kuserok field is the plugin entry point that performs the
+ * traditional kuserok operation however the plugin desires. It is
+ * invoked in no particular order relative to other kuserok plugins, but
+ * it has a 'rule' argument that indicates which plugin is intended to
+ * act on the rule. The plugin kuserok function must return
+ * KRB5_PLUGIN_NO_HANDLE if the rule is not applicable to it.
+ *
+ * The plugin kuserok function has the following arguments, in this
+ * order:
+ *
+ * -# plug_ctx, the context value output by the plugin's init function
+ * -# context, a krb5_context
+ * -# rule, the kuserok rule being evaluated (from krb5.conf(5))
+ * -# flags
+ * -# k5login_dir, configured location of k5login per-user files if any
+ * -# luser, name of the local user account to which principal is attempting to access.
+ * -# principal, the krb5_principal trying to access the luser account
+ * -# result, a krb5_boolean pointer where the plugin will output its result
+ *
+ * @ingroup krb5_support
+ */
+typedef struct krb5plugin_kuserok_ftable_desc {
+ int minor_version;
+ krb5_error_code (KRB5_LIB_CALL *init)(krb5_context, void **);
+ void (KRB5_LIB_CALL *fini)(void *);
+ krb5_error_code (KRB5_LIB_CALL *kuserok)(void *, krb5_context, const char *,
+ unsigned int, const char *, const char *,
+ krb5_const_principal,
+ krb5_boolean *);
+} krb5plugin_kuserok_ftable;
+
+#define KUSEROK_ANAME_TO_LNAME_OK 1
+#define KUSEROK_K5LOGIN_IS_AUTHORITATIVE 2
+
+#endif /* HEIMDAL_KRB5_KUSEROK_PLUGIN_H */
diff --git a/lib/krb5/libkrb5-exports.def.in b/lib/krb5/libkrb5-exports.def.in
new file mode 100644
index 000000000000..01f9f78c3b7c
--- /dev/null
+++ b/lib/krb5/libkrb5-exports.def.in
@@ -0,0 +1,804 @@
+EXPORTS
+ krb524_convert_creds_kdc
+ krb524_convert_creds_kdc_ccache
+ krb5_abort
+ krb5_abortx
+ krb5_acl_match_file
+ krb5_acl_match_string
+ krb5_add_et_list
+ krb5_add_extra_addresses
+ krb5_add_ignore_addresses
+ krb5_addlog_dest
+ krb5_addlog_func
+ krb5_addr2sockaddr
+ krb5_address_compare
+ krb5_address_order
+ krb5_address_prefixlen_boundary
+ krb5_address_search
+ krb5_allow_weak_crypto
+ krb5_aname_to_localname
+ krb5_anyaddr
+ krb5_appdefault_boolean
+ krb5_appdefault_string
+ krb5_appdefault_time
+ krb5_append_addresses
+ krb5_auth_con_addflags
+ krb5_auth_con_free
+ krb5_auth_con_genaddrs
+ krb5_auth_con_generatelocalsubkey
+ krb5_auth_con_getaddrs
+ krb5_auth_con_getauthenticator
+ krb5_auth_con_getcksumtype
+ krb5_auth_con_getflags
+ krb5_auth_con_getkey
+ krb5_auth_con_getkeytype
+ krb5_auth_con_getlocalseqnumber
+ krb5_auth_con_getlocalsubkey
+ krb5_auth_con_getrcache
+ krb5_auth_con_getremoteseqnumber
+ krb5_auth_con_getremotesubkey
+ krb5_auth_con_init
+ krb5_auth_con_removeflags
+ krb5_auth_con_setaddrs
+ krb5_auth_con_setaddrs_from_fd
+ krb5_auth_con_setcksumtype
+ krb5_auth_con_setflags
+ krb5_auth_con_setkey
+ krb5_auth_con_setkeytype
+ krb5_auth_con_setlocalseqnumber
+ krb5_auth_con_setlocalsubkey
+ krb5_auth_con_setrcache
+ krb5_auth_con_setremoteseqnumber
+ krb5_auth_con_setremotesubkey
+ krb5_auth_con_setuserkey
+ krb5_auth_getremoteseqnumber
+ krb5_build_ap_req
+ krb5_build_principal
+ krb5_build_principal_ext
+ krb5_build_principal_va
+ krb5_build_principal_va_ext
+ krb5_c_block_size
+ krb5_c_checksum_length
+ krb5_c_decrypt
+ krb5_c_encrypt
+ krb5_c_encrypt_length
+ krb5_c_enctype_compare
+ krb5_c_get_checksum
+ krb5_c_is_coll_proof_cksum
+ krb5_c_is_keyed_cksum
+ krb5_c_keylengths
+ krb5_c_make_checksum
+ krb5_c_make_random_key
+ krb5_c_prf
+ krb5_c_prf_length
+ krb5_c_random_make_octets ;!
+ krb5_c_set_checksum
+ krb5_c_valid_cksumtype
+ krb5_c_valid_enctype
+ krb5_c_verify_checksum
+ krb5_cc_cache_end_seq_get
+ krb5_cc_cache_get_first
+ krb5_cc_cache_match
+ krb5_cc_cache_next
+ krb5_cc_clear_mcred
+ krb5_cc_close
+ krb5_cc_copy_cache
+ krb5_cc_copy_creds ;!
+ krb5_cc_copy_match_f
+ krb5_cc_default
+ krb5_cc_default_name
+ krb5_cc_destroy
+ krb5_cc_end_seq_get
+ krb5_cc_gen_new
+ krb5_cc_get_config
+ krb5_cc_get_friendly_name
+ krb5_cc_get_full_name
+ krb5_cc_get_kdc_offset
+ krb5_cc_get_lifetime
+ krb5_cc_get_name
+ krb5_cc_get_ops
+ krb5_cc_get_prefix_ops
+ krb5_cc_get_principal
+ krb5_cc_get_type
+ krb5_cc_get_version
+ krb5_cc_initialize
+ krb5_cc_last_change_time
+ krb5_cc_move
+ krb5_cc_new_unique
+ krb5_cc_next_cred
+;! krb5_cc_next_cred_match
+ krb5_cc_register
+ krb5_cc_remove_cred
+ krb5_cc_resolve
+ krb5_cc_retrieve_cred
+ krb5_cc_set_config
+ krb5_cc_set_default_name
+ krb5_cc_set_flags
+ krb5_cc_set_kdc_offset
+ krb5_cc_start_seq_get
+ krb5_cc_store_cred
+ krb5_cc_support_switch
+ krb5_cc_switch
+ krb5_cc_set_friendly_name
+ krb5_change_password
+ krb5_check_transited
+ krb5_check_transited_realms
+ krb5_checksum_disable
+ krb5_checksum_free
+ krb5_checksum_is_collision_proof
+ krb5_checksum_is_keyed
+ krb5_checksumsize
+ krb5_cksumtype_to_enctype
+ krb5_cksumtype_valid
+ krb5_clear_error_string
+ krb5_clear_error_message
+ krb5_closelog
+ krb5_compare_creds
+ krb5_config_file_free
+ krb5_config_free_strings
+; _krb5_config_get
+ krb5_config_get_bool
+ krb5_config_get_bool_default
+ krb5_config_get_int
+ krb5_config_get_int_default
+ krb5_config_get_list
+; _krb5_config_get_next
+ krb5_config_get_string
+ krb5_config_get_string_default
+ krb5_config_get_strings
+ krb5_config_get_time
+ krb5_config_get_time_default
+ krb5_config_parse_file
+ krb5_config_parse_file_multi
+ krb5_config_parse_string_multi
+; _krb5_config_vget
+ krb5_config_vget_bool
+ krb5_config_vget_bool_default
+ krb5_config_vget_int
+ krb5_config_vget_int_default
+ krb5_config_vget_list
+; _krb5_config_vget_next
+ krb5_config_vget_string
+ krb5_config_vget_string_default
+ krb5_config_vget_strings
+ krb5_config_vget_time
+ krb5_config_vget_time_default
+ krb5_copy_address
+ krb5_copy_addresses
+ krb5_copy_checksum
+ krb5_copy_context
+ krb5_copy_creds
+ krb5_copy_creds_contents
+ krb5_copy_data
+ krb5_copy_host_realm
+ krb5_copy_keyblock
+ krb5_copy_keyblock_contents
+ krb5_copy_principal
+ krb5_copy_ticket
+ krb5_create_checksum
+ krb5_create_checksum_iov
+ krb5_crypto_destroy
+ krb5_crypto_fx_cf2
+ krb5_crypto_get_checksum_type
+ krb5_crypto_getblocksize
+ krb5_crypto_getconfoundersize
+ krb5_crypto_getenctype
+ krb5_crypto_getpadsize
+ krb5_crypto_init
+ krb5_crypto_overhead
+ krb5_crypto_prf
+ krb5_crypto_prf_length
+ krb5_crypto_length
+ krb5_crypto_length_iov
+ krb5_decrypt_iov_ivec
+ krb5_encrypt_iov_ivec
+ krb5_data_alloc
+ krb5_data_cmp
+ krb5_data_copy
+ krb5_data_ct_cmp
+ krb5_data_free
+ krb5_data_realloc
+ krb5_data_zero
+ krb5_decode_Authenticator
+ krb5_decode_ETYPE_INFO2
+ krb5_decode_ETYPE_INFO
+ krb5_decode_EncAPRepPart
+ krb5_decode_EncASRepPart
+ krb5_decode_EncKrbCredPart
+ krb5_decode_EncTGSRepPart
+ krb5_decode_EncTicketPart
+ krb5_decode_ap_req
+ krb5_decrypt
+ krb5_decrypt_EncryptedData
+ krb5_decrypt_ivec
+ krb5_decrypt_ticket
+ krb5_derive_key
+ krb5_digest_alloc
+ krb5_digest_free
+ krb5_digest_get_client_binding
+ krb5_digest_get_identifier
+ krb5_digest_get_opaque
+ krb5_digest_get_rsp
+ krb5_digest_get_server_nonce
+ krb5_digest_get_session_key
+ krb5_digest_get_tickets
+ krb5_digest_init_request
+ krb5_digest_probe
+ krb5_digest_rep_get_status
+ krb5_digest_request
+ krb5_digest_set_authentication_user
+ krb5_digest_set_authid
+ krb5_digest_set_client_nonce
+ krb5_digest_set_digest
+ krb5_digest_set_hostname
+ krb5_digest_set_identifier
+ krb5_digest_set_method
+ krb5_digest_set_nonceCount
+ krb5_digest_set_opaque
+ krb5_digest_set_qop
+ krb5_digest_set_realm
+ krb5_digest_set_responseData
+ krb5_digest_set_server_cb
+ krb5_digest_set_server_nonce
+ krb5_digest_set_type
+ krb5_digest_set_uri
+ krb5_digest_set_username
+ krb5_domain_x500_decode
+ krb5_domain_x500_encode
+ krb5_eai_to_heim_errno
+ krb5_encode_Authenticator
+ krb5_encode_ETYPE_INFO2
+ krb5_encode_ETYPE_INFO
+ krb5_encode_EncAPRepPart
+ krb5_encode_EncASRepPart
+ krb5_encode_EncKrbCredPart
+ krb5_encode_EncTGSRepPart
+ krb5_encode_EncTicketPart
+ krb5_encrypt
+ krb5_encrypt_EncryptedData
+ krb5_encrypt_ivec
+ krb5_enctype_enable
+ krb5_enctype_disable
+ krb5_enctype_keybits
+ krb5_enctype_keysize
+ krb5_enctype_to_keytype
+ krb5_enctype_to_string
+ krb5_enctype_valid
+ krb5_enctypes_compatible_keys
+ krb5_enomem
+ krb5_err
+ krb5_error_from_rd_error
+ krb5_errx
+ krb5_expand_hostname
+ krb5_expand_hostname_realms
+ krb5_find_padata
+ krb5_format_time
+ krb5_free_address
+ krb5_free_addresses
+ krb5_free_ap_rep_enc_part
+ krb5_free_authenticator
+ krb5_free_checksum
+ krb5_free_checksum_contents
+ krb5_free_config_files
+ krb5_free_context
+ krb5_free_cred_contents
+ krb5_free_creds
+ krb5_free_creds_contents
+ krb5_free_data
+ krb5_free_data_contents
+ krb5_free_default_realm
+ krb5_free_error
+ krb5_free_error_contents
+ krb5_free_error_string
+ krb5_free_error_message
+ krb5_free_host_realm
+ krb5_free_kdc_rep
+ krb5_free_keyblock
+ krb5_free_keyblock_contents
+ krb5_free_krbhst
+ krb5_free_principal
+ krb5_free_salt
+ krb5_free_ticket
+ krb5_free_unparsed_name
+ krb5_fwd_tgt_creds
+ krb5_generate_random_block
+ krb5_generate_random_keyblock
+ krb5_generate_seq_number
+ krb5_generate_subkey
+ krb5_generate_subkey_extended
+ krb5_get_all_client_addrs
+ krb5_get_all_server_addrs
+ krb5_get_cred_from_kdc
+ krb5_get_cred_from_kdc_opt
+ krb5_get_credentials
+ krb5_get_credentials_with_flags
+ krb5_get_creds
+ krb5_get_creds_opt_add_options
+ krb5_get_creds_opt_alloc
+ krb5_get_creds_opt_free
+ krb5_get_creds_opt_set_enctype
+ krb5_get_creds_opt_set_impersonate
+ krb5_get_creds_opt_set_options
+ krb5_get_creds_opt_set_ticket
+ krb5_get_default_config_files
+ krb5_get_default_in_tkt_etypes
+ krb5_get_default_principal
+ krb5_get_default_realm
+ krb5_get_default_realms
+ krb5_get_dns_canonicalize_hostname
+ krb5_get_err_text
+ krb5_get_error_message
+ krb5_get_error_string
+ krb5_get_extra_addresses
+ krb5_get_fcache_version
+ krb5_get_forwarded_creds
+ krb5_get_host_realm
+ krb5_get_ignore_addresses
+ krb5_get_in_cred
+; krb5_cccol_last_change_time
+ krb5_get_in_tkt
+ krb5_get_in_tkt_with_keytab
+ krb5_get_in_tkt_with_password
+ krb5_get_in_tkt_with_skey
+;! krb5_get_init_creds
+ krb5_get_init_creds_keyblock
+ krb5_get_init_creds_keytab
+ krb5_get_init_creds_opt_alloc
+ krb5_get_init_creds_opt_free
+ krb5_get_init_creds_opt_get_error
+ krb5_get_init_creds_opt_init
+ krb5_get_init_creds_opt_set_address_list
+ krb5_get_init_creds_opt_set_addressless
+ krb5_get_init_creds_opt_set_anonymous
+ krb5_get_init_creds_opt_set_change_password_prompt
+ krb5_get_init_creds_opt_set_canonicalize
+ krb5_get_init_creds_opt_set_default_flags
+ krb5_get_init_creds_opt_set_etype_list
+ krb5_get_init_creds_opt_set_forwardable
+ krb5_get_init_creds_opt_set_pa_password
+ krb5_get_init_creds_opt_set_pac_request
+ krb5_get_init_creds_opt_set_pkinit
+ krb5_get_init_creds_opt_set_preauth_list
+ krb5_get_init_creds_opt_set_process_last_req
+ krb5_get_init_creds_opt_set_proxiable
+ krb5_get_init_creds_opt_set_renew_life
+ krb5_get_init_creds_opt_set_salt
+ krb5_get_init_creds_opt_set_tkt_life
+ krb5_get_init_creds_opt_set_win2k
+ krb5_get_init_creds_password
+ krb5_get_kdc_cred
+ krb5_get_kdc_sec_offset
+ krb5_get_krb524hst
+ krb5_get_krb_admin_hst
+ krb5_get_krb_changepw_hst
+ krb5_get_krbhst
+ krb5_get_max_time_skew
+ krb5_get_pw_salt
+ krb5_get_renewed_creds
+ krb5_get_server_rcache
+ krb5_get_use_admin_kdc
+ krb5_get_validated_creds ;!
+ krb5_get_warn_dest
+ krb5_get_wrapped_length
+ krb5_getportbyname
+ krb5_h_addr2addr
+ krb5_h_addr2sockaddr
+ krb5_h_errno_to_heim_errno
+ krb5_have_error_string
+ krb5_hmac
+ krb5_init_context
+ krb5_init_ets
+ krb5_initlog
+ krb5_is_config_principal
+ krb5_is_enctype_weak
+ krb5_is_thread_safe
+#ifdef HAVE_KCM
+ krb5_kcm_call
+ krb5_kcm_storage_request
+#endif
+ krb5_kerberos_enctypes
+ krb5_keyblock_get_enctype
+ krb5_keyblock_init
+ krb5_keyblock_key_proc
+ krb5_keyblock_zero
+ krb5_keytab_key_proc
+ krb5_keytype_to_enctypes
+ krb5_keytype_to_enctypes_default
+ krb5_keytype_to_string
+ krb5_krbhst_format_string
+ krb5_krbhst_free
+ krb5_krbhst_get_addrinfo
+ krb5_krbhst_init
+ krb5_krbhst_init_flags
+ krb5_krbhst_next
+ krb5_krbhst_next_as_string
+ krb5_krbhst_reset
+ krb5_kt_add_entry
+ krb5_kt_close
+ krb5_kt_compare
+ krb5_kt_copy_entry_contents
+ krb5_kt_default
+ krb5_kt_default_modify_name
+ krb5_kt_default_name
+ krb5_kt_destroy
+ krb5_kt_end_seq_get
+ krb5_kt_free_entry
+ krb5_kt_get_entry
+ krb5_kt_get_full_name
+ krb5_kt_get_name
+ krb5_kt_get_type
+ krb5_kt_have_content
+ krb5_kt_next_entry
+ krb5_kt_read_service_key
+ krb5_kt_register
+ krb5_kt_remove_entry
+ krb5_kt_resolve
+ krb5_kt_start_seq_get
+ krb5_kuserok
+ krb5_log
+ krb5_log_msg
+ krb5_make_addrport
+ krb5_make_principal
+ krb5_max_sockaddr_size
+ krb5_mk_error
+ krb5_mk_error_ext
+ krb5_mk_priv
+ krb5_mk_rep
+ krb5_mk_req
+ krb5_mk_req_exact
+ krb5_mk_req_extended
+ krb5_mk_safe
+ krb5_net_read
+ krb5_net_write
+ krb5_net_write_block
+ krb5_ntlm_alloc
+ krb5_ntlm_free
+ krb5_ntlm_init_get_challenge
+ krb5_ntlm_init_get_flags
+ krb5_ntlm_init_get_opaque
+ krb5_ntlm_init_get_targetinfo
+ krb5_ntlm_init_get_targetname
+ krb5_ntlm_init_request
+ krb5_ntlm_rep_get_sessionkey
+ krb5_ntlm_rep_get_status
+ krb5_ntlm_req_set_flags
+ krb5_ntlm_req_set_lm
+ krb5_ntlm_req_set_ntlm
+ krb5_ntlm_req_set_opaque
+ krb5_ntlm_req_set_session
+ krb5_ntlm_req_set_targetname
+ krb5_ntlm_req_set_username
+ krb5_ntlm_request
+ krb5_openlog
+ krb5_pac_add_buffer
+ krb5_pac_free
+ krb5_pac_get_buffer
+ krb5_pac_get_types
+ krb5_pac_init
+ krb5_pac_parse
+ krb5_pac_verify
+ krb5_padata_add
+ krb5_parse_address
+ krb5_parse_name
+ krb5_parse_name_flags
+ krb5_parse_nametype
+ krb5_passwd_result_to_string
+ krb5_password_key_proc
+ krb5_get_permitted_enctypes
+ krb5_plugin_register
+ krb5_prepend_config_files
+ krb5_prepend_config_files_default
+ krb5_prepend_error_message
+ krb5_princ_realm
+ krb5_princ_set_realm
+ krb5_principal_compare
+ krb5_principal_compare_any_realm
+ krb5_principal_get_comp_string
+ krb5_principal_get_num_comp
+ krb5_principal_get_realm
+ krb5_principal_get_type
+ krb5_principal_is_krbtgt
+ krb5_principal_match
+ krb5_principal_set_comp_string
+ krb5_principal_set_realm
+ krb5_principal_set_type
+ krb5_print_address
+ krb5_program_setup
+ krb5_prompter_posix
+ krb5_random_to_key
+ krb5_rc_close
+ krb5_rc_default
+ krb5_rc_default_name
+ krb5_rc_default_type
+ krb5_rc_destroy
+ krb5_rc_expunge
+ krb5_rc_get_lifespan
+ krb5_rc_get_name
+ krb5_rc_get_type
+ krb5_rc_initialize
+ krb5_rc_recover
+ krb5_rc_resolve
+ krb5_rc_resolve_full
+ krb5_rc_resolve_type
+ krb5_rc_store
+ krb5_rd_cred2
+ krb5_rd_cred
+ krb5_rd_error
+ krb5_rd_priv
+ krb5_rd_rep
+ krb5_rd_req
+ krb5_rd_req_ctx
+ krb5_rd_req_in_ctx_alloc
+ krb5_rd_req_in_ctx_free
+ krb5_rd_req_in_set_keyblock
+ krb5_rd_req_in_set_keytab
+ krb5_rd_req_in_set_pac_check
+ krb5_rd_req_out_ctx_free
+ krb5_rd_req_out_get_ap_req_options
+ krb5_rd_req_out_get_keyblock
+ krb5_rd_req_out_get_ticket
+ krb5_rd_req_with_keyblock
+ krb5_rd_safe
+ krb5_read_message
+ krb5_read_priv_message
+ krb5_read_safe_message
+ krb5_realm_compare
+ krb5_recvauth
+ krb5_recvauth_match_version
+ krb5_ret_address
+ krb5_ret_addrs
+ krb5_ret_authdata
+ krb5_ret_creds
+ krb5_ret_creds_tag
+ krb5_ret_data
+ krb5_ret_int16
+ krb5_ret_int32
+ krb5_ret_int64
+ krb5_ret_int8
+ krb5_ret_keyblock
+ krb5_ret_principal
+ krb5_ret_string
+ krb5_ret_stringnl
+ krb5_ret_stringz
+ krb5_ret_times
+ krb5_ret_uint16
+ krb5_ret_uint32
+ krb5_ret_uint64
+ krb5_ret_uint8
+ krb5_salttype_to_string
+ krb5_sendauth
+ krb5_sendto
+ krb5_sendto_context
+ krb5_sendto_ctx_add_flags
+ krb5_sendto_ctx_alloc
+ krb5_sendto_ctx_free
+ krb5_sendto_ctx_get_flags
+ krb5_sendto_ctx_set_func
+ krb5_sendto_ctx_set_type
+ krb5_sendto_kdc
+ krb5_sendto_kdc_flags
+ krb5_set_config_files
+ krb5_set_debug_dest
+ krb5_set_default_in_tkt_etypes
+ krb5_set_default_realm
+ krb5_set_dns_canonicalize_hostname
+ krb5_set_error_message
+ krb5_set_error_string
+ krb5_set_extra_addresses
+ krb5_set_fcache_version
+ krb5_set_home_dir_access
+ krb5_set_ignore_addresses
+ krb5_set_kdc_sec_offset
+ krb5_set_max_time_skew
+ krb5_set_password
+ krb5_set_password_using_ccache
+ krb5_set_real_time
+ krb5_set_send_to_kdc_func
+ krb5_set_use_admin_kdc
+ krb5_set_warn_dest
+ krb5_sname_to_principal
+ krb5_sock_to_principal
+ krb5_sockaddr2address
+ krb5_sockaddr2port
+ krb5_sockaddr_uninteresting
+ krb5_std_usage
+ krb5_storage_clear_flags
+ krb5_storage_emem
+ krb5_storage_free
+ krb5_storage_from_data
+ krb5_storage_from_fd
+ krb5_storage_from_mem
+ krb5_storage_from_readonly_mem
+ krb5_storage_from_socket
+ krb5_storage_fsync
+ krb5_storage_get_byteorder
+ krb5_storage_get_eof_code
+ krb5_storage_is_flags
+ krb5_storage_read
+ krb5_storage_seek
+ krb5_storage_set_byteorder
+ krb5_storage_set_eof_code
+ krb5_storage_set_flags
+ krb5_storage_set_max_alloc
+ krb5_storage_to_data
+ krb5_storage_truncate
+ krb5_storage_write
+ krb5_store_address
+ krb5_store_addrs
+ krb5_store_authdata
+ krb5_store_creds
+ krb5_store_creds_tag
+ krb5_store_data
+ krb5_store_int16
+ krb5_store_int32
+ krb5_store_int64
+ krb5_store_int8
+ krb5_store_keyblock
+ krb5_store_principal
+ krb5_store_string
+ krb5_store_stringnl
+ krb5_store_stringz
+ krb5_store_times
+ krb5_store_uint16
+ krb5_store_uint32
+ krb5_store_uint64
+ krb5_store_uint8
+ krb5_string_to_deltat
+ krb5_string_to_enctype
+ krb5_string_to_key
+ krb5_string_to_key_data
+ krb5_string_to_key_data_salt
+ krb5_string_to_key_data_salt_opaque
+ krb5_string_to_key_derived
+ krb5_string_to_key_salt
+ krb5_string_to_key_salt_opaque
+ krb5_string_to_keytype
+ krb5_string_to_salttype
+ krb5_ticket_get_authorization_data_type
+ krb5_ticket_get_client
+ krb5_ticket_get_endtime
+ krb5_ticket_get_server
+ krb5_timeofday
+ krb5_unparse_name
+ krb5_unparse_name_fixed
+ krb5_unparse_name_fixed_flags
+ krb5_unparse_name_fixed_short
+ krb5_unparse_name_flags
+ krb5_unparse_name_short
+ krb5_us_timeofday
+ krb5_vabort
+ krb5_vabortx
+ krb5_verify_ap_req2
+ krb5_verify_ap_req
+ krb5_verify_authenticator_checksum
+ krb5_verify_checksum
+ krb5_verify_checksum_iov
+ krb5_verify_init_creds
+ krb5_verify_init_creds_opt_init
+ krb5_verify_init_creds_opt_set_ap_req_nofail
+ krb5_verify_opt_alloc
+ krb5_verify_opt_free
+ krb5_verify_opt_init
+ krb5_verify_opt_set_ccache
+ krb5_verify_opt_set_flags
+ krb5_verify_opt_set_keytab
+ krb5_verify_opt_set_secure
+ krb5_verify_opt_set_service
+ krb5_verify_user
+ krb5_verify_user_lrealm
+ krb5_verify_user_opt
+ krb5_verr
+ krb5_verrx
+ krb5_vlog
+ krb5_vlog_msg
+ krb5_vprepend_error_message
+ krb5_vset_error_message
+ krb5_vset_error_string
+ krb5_vwarn
+ krb5_vwarnx
+ krb5_warn
+ krb5_warnx
+ krb5_write_message
+ krb5_write_priv_message
+ krb5_write_safe_message
+ krb5_xfree
+ krb5_cccol_last_change_time
+ krb5_cccol_cursor_new
+ krb5_cccol_cursor_next
+ krb5_cccol_cursor_free
+
+ ; com_err error tables
+ initialize_krb5_error_table_r
+ initialize_krb5_error_table
+ initialize_krb_error_table_r
+ initialize_krb_error_table
+ initialize_heim_error_table_r
+ initialize_heim_error_table
+ initialize_k524_error_table_r
+ initialize_k524_error_table
+
+ ; variables
+ krb5_mcc_ops DATA
+ krb5_acc_ops DATA
+ krb5_fcc_ops DATA
+#ifdef HAVE_SCC
+ krb5_scc_ops DATA
+#endif
+#ifdef HAVE_KCM
+ krb5_kcm_ops DATA
+#endif
+ krb5_wrfkt_ops DATA
+ krb5_mkt_ops DATA
+ krb5_akf_ops DATA
+ krb5_any_ops DATA
+ heimdal_version DATA
+ heimdal_long_version DATA
+ krb5_config_file DATA
+ krb5_defkeyname DATA
+ krb5_cc_type_api DATA
+ krb5_cc_type_file DATA
+ krb5_cc_type_memory DATA
+ krb5_cc_type_kcm DATA
+ krb5_cc_type_scc DATA
+
+ ; Shared with GSSAPI krb5
+ _krb5_crc_init_table
+ _krb5_crc_update
+ _krb5_get_krbtgt
+ _krb5_build_authenticator
+
+ ; Shared with libkdc
+ _krb5_AES_SHA1_string_to_default_iterator
+ _krb5_AES_SHA2_string_to_default_iterator
+ _krb5_dh_group_ok
+ _krb5_get_host_realm_int
+ _krb5_get_int
+ _krb5_get_int64
+ _krb5_pac_sign
+ _krb5_parse_moduli
+ _krb5_pk_kdf
+ _krb5_pk_load_id
+ _krb5_pk_mk_ContentInfo
+ _krb5_pk_octetstring2key
+ _krb5_plugin_run_f
+ _krb5_enctype_requires_random_salt
+ _krb5_principal2principalname
+ _krb5_principalname2krb5_principal
+ _krb5_put_int
+ _krb5_s4u2self_to_checksumdata
+ _krb5_expand_path_tokens ;!
+
+ ; kinit helper
+ krb5_get_init_creds_opt_set_pkinit_user_certs
+ krb5_pk_enterprise_cert
+
+ krb5_auth_con_getsendsubkey
+ krb5_init_creds_free
+ krb5_init_creds_get
+ krb5_init_creds_get_creds
+ krb5_init_creds_get_error
+ krb5_init_creds_init
+ krb5_init_creds_set_fast_ccache
+ krb5_init_creds_set_keytab
+ krb5_init_creds_set_password
+ krb5_init_creds_set_service
+ krb5_init_creds_store
+ krb5_process_last_request
+
+ ; testing
+;! _krb5_aes_cts_encrypt
+ _krb5_n_fold
+ _krb5_expand_default_cc_name
+
+ ; FAST
+ _krb5_fast_cf2
+ _krb5_fast_armor_key
+
+ ; Recent additions
+ krb5_cc_type_dcc;
+ krb5_dcc_ops;
+ _krb5_plugin_find;
+ _krb5_plugin_free;
+ _krb5_expand_path_tokensv;
+ _krb5_find_capath;
+ _krb5_free_capath;
diff --git a/lib/krb5/locate_plugin.h b/lib/krb5/locate_plugin.h
index b1b1f0ef230e..5a9c7bcb77ee 100644
--- a/lib/krb5/locate_plugin.h
+++ b/lib/krb5/locate_plugin.h
@@ -3,6 +3,8 @@
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
+ * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -37,6 +39,10 @@
#define HEIMDAL_KRB5_LOCATE_PLUGIN_H 1
#define KRB5_PLUGIN_LOCATE "service_locator"
+#define KRB5_PLUGIN_LOCATE_VERSION 1
+#define KRB5_PLUGIN_LOCATE_VERSION_0 0
+#define KRB5_PLUGIN_LOCATE_VERSION_1 1
+#define KRB5_PLUGIN_LOCATE_VERSION_2 2
enum locate_service_type {
locate_service_kdc = 1,
@@ -47,7 +53,15 @@ enum locate_service_type {
};
typedef krb5_error_code
-(*krb5plugin_service_locate_lookup) (void *, enum locate_service_type,
+(*krb5plugin_service_locate_lookup) (void *, unsigned long, enum locate_service_type,
+ const char *, int, int,
+ int (*)(void *,int,struct sockaddr *),
+ void *);
+
+#define KRB5_PLF_ALLOW_HOMEDIR 1
+
+typedef krb5_error_code
+(*krb5plugin_service_locate_lookup_old) (void *, enum locate_service_type,
const char *, int, int,
int (*)(void *,int,struct sockaddr *),
void *);
@@ -57,7 +71,8 @@ typedef struct krb5plugin_service_locate_ftable {
int minor_version;
krb5_error_code (*init)(krb5_context, void **);
void (*fini)(void *);
- krb5plugin_service_locate_lookup lookup;
+ krb5plugin_service_locate_lookup_old old_lookup;
+ krb5plugin_service_locate_lookup lookup; /* version 2 */
} krb5plugin_service_locate_ftable;
#endif /* HEIMDAL_KRB5_LOCATE_PLUGIN_H */
diff --git a/lib/krb5/log.c b/lib/krb5/log.c
index 4b289afd807d..2d66ae4c9090 100644
--- a/lib/krb5/log.c
+++ b/lib/krb5/log.c
@@ -121,17 +121,12 @@ krb5_initlog(krb5_context context,
krb5_log_facility **fac)
{
krb5_log_facility *f = calloc(1, sizeof(*f));
- if(f == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (f == NULL)
+ return krb5_enomem(context);
f->program = strdup(program);
if(f->program == NULL){
free(f);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
*fac = f;
return 0;
@@ -147,11 +142,8 @@ krb5_addlog_func(krb5_context context,
void *data)
{
struct facility *fp = log_realloc(fac);
- if(fp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (fp == NULL)
+ return krb5_enomem(context);
fp->min = min;
fp->max = max;
fp->log_func = log_func;
@@ -190,11 +182,8 @@ open_syslog(krb5_context context,
struct _heimdal_syslog_data *sd = malloc(sizeof(*sd));
int i;
- if(sd == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (sd == NULL)
+ return krb5_enomem(context);
i = find_value(sev, syslogvals);
if(i == -1)
i = LOG_ERR;
@@ -213,6 +202,7 @@ struct file_data{
const char *mode;
FILE *fd;
int keep_open;
+ int freefilename;
};
static void KRB5_CALLCONV
@@ -247,23 +237,27 @@ close_file(void *data)
struct file_data *f = data;
if(f->keep_open && f->filename)
fclose(f->fd);
+ if (f->filename && f->freefilename)
+ free((char *)f->filename);
free(data);
}
static krb5_error_code
open_file(krb5_context context, krb5_log_facility *fac, int min, int max,
- const char *filename, const char *mode, FILE *f, int keep_open)
+ const char *filename, const char *mode, FILE *f, int keep_open,
+ int freefilename)
{
struct file_data *fd = malloc(sizeof(*fd));
- if(fd == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ if (fd == NULL) {
+ if (freefilename && filename)
+ free((char *)filename);
+ return krb5_enomem(context);
}
fd->filename = filename;
fd->mode = mode;
fd->fd = f;
fd->keep_open = keep_open;
+ fd->freefilename = freefilename;
return krb5_addlog_func(context, fac, min, max, log_file, close_file, fd);
}
@@ -277,10 +271,13 @@ krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
int min = 0, max = -1, n;
char c;
const char *p = orig;
+#ifdef _WIN32
+ const char *q;
+#endif
n = sscanf(p, "%d%c%d/", &min, &c, &max);
if(n == 2){
- if(c == '/') {
+ if(ISPATHSEP(c)) {
if(min < 0){
max = -min;
min = 0;
@@ -290,6 +287,12 @@ krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
}
}
if(n){
+#ifdef _WIN32
+ q = strrchr(p, '\\');
+ if (q != NULL)
+ p = q;
+ else
+#endif
p = strchr(p, '/');
if(p == NULL) {
krb5_set_error_message(context, HEIM_ERR_LOG_PARSE,
@@ -299,26 +302,23 @@ krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
p++;
}
if(strcmp(p, "STDERR") == 0){
- ret = open_file(context, f, min, max, NULL, NULL, stderr, 1);
+ ret = open_file(context, f, min, max, NULL, NULL, stderr, 1, 0);
}else if(strcmp(p, "CONSOLE") == 0){
- ret = open_file(context, f, min, max, "/dev/console", "w", NULL, 0);
+ ret = open_file(context, f, min, max, "/dev/console", "w", NULL, 0, 0);
}else if(strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')){
char *fn;
FILE *file = NULL;
int keep_open = 0;
fn = strdup(p + 5);
- if(fn == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (fn == NULL)
+ return krb5_enomem(context);
if(p[4] == '='){
int i = open(fn, O_WRONLY | O_CREAT |
O_TRUNC | O_APPEND, 0666);
if(i < 0) {
ret = errno;
krb5_set_error_message(context, ret,
- N_("open(%s) logile: %s", ""), fn,
+ N_("open(%s) logfile: %s", ""), fn,
strerror(ret));
free(fn);
return ret;
@@ -336,9 +336,9 @@ krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
}
keep_open = 1;
}
- ret = open_file(context, f, min, max, fn, "a", file, keep_open);
+ ret = open_file(context, f, min, max, fn, "a", file, keep_open, 1);
}else if(strncmp(p, "DEVICE", 6) == 0 && (p[6] == ':' || p[6] == '=')){
- ret = open_file(context, f, min, max, strdup(p + 7), "w", NULL, 0);
+ ret = open_file(context, f, min, max, strdup(p + 7), "w", NULL, 0, 1);
}else if(strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')){
char severity[128] = "";
char facility[128] = "";
@@ -411,7 +411,7 @@ krb5_vlog_msg(krb5_context context,
int level,
const char *fmt,
va_list ap)
- __attribute__((format (printf, 5, 0)))
+ __attribute__ ((__format__ (__printf__, 5, 0)))
{
char *msg = NULL;
@@ -449,7 +449,7 @@ krb5_vlog(krb5_context context,
int level,
const char *fmt,
va_list ap)
- __attribute__((format (printf, 4, 0)))
+ __attribute__ ((__format__ (__printf__, 4, 0)))
{
return krb5_vlog_msg(context, fac, NULL, level, fmt, ap);
}
@@ -461,7 +461,7 @@ krb5_log_msg(krb5_context context,
char **reply,
const char *fmt,
...)
- __attribute__((format (printf, 5, 6)))
+ __attribute__ ((__format__ (__printf__, 5, 6)))
{
va_list ap;
krb5_error_code ret;
@@ -479,7 +479,7 @@ krb5_log(krb5_context context,
int level,
const char *fmt,
...)
- __attribute__((format (printf, 4, 5)))
+ __attribute__ ((__format__ (__printf__, 4, 5)))
{
va_list ap;
krb5_error_code ret;
@@ -495,7 +495,7 @@ _krb5_debug(krb5_context context,
int level,
const char *fmt,
...)
- __attribute__((format (printf, 3, 4)))
+ __attribute__ ((__format__ (__printf__, 3, 4)))
{
va_list ap;
@@ -507,10 +507,28 @@ _krb5_debug(krb5_context context,
va_end(ap);
}
-krb5_boolean KRB5_LIB_FUNCTION
+KRB5_LIB_FUNCTION krb5_boolean KRB5_LIB_CALL
_krb5_have_debug(krb5_context context, int level)
{
if (context == NULL || context->debug_dest == NULL)
return 0 ;
return 1;
}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_set_debug_dest(krb5_context context, const char *program,
+ const char *log_spec)
+{
+ krb5_error_code ret;
+
+ if (context->debug_dest == NULL) {
+ ret = krb5_initlog(context, program, &context->debug_dest);
+ if (ret)
+ return ret;
+ }
+
+ ret = krb5_addlog_dest(context, context->debug_dest, log_spec);
+ if (ret)
+ return ret;
+ return 0;
+}
diff --git a/lib/krb5/mcache.c b/lib/krb5/mcache.c
index e4b90c17e7b2..474cb3a2bd2e 100644
--- a/lib/krb5/mcache.c
+++ b/lib/krb5/mcache.c
@@ -47,6 +47,7 @@ typedef struct krb5_mcache {
struct krb5_mcache *next;
time_t mtime;
krb5_deltat kdc_offset;
+ HEIMDAL_MUTEX mutex;
} krb5_mcache;
static HEIMDAL_MUTEX mcc_mutex = HEIMDAL_MUTEX_INITIALIZER;
@@ -99,6 +100,7 @@ mcc_alloc(const char *name)
m->mtime = time(NULL);
m->kdc_offset = 0;
m->next = mcc_head;
+ HEIMDAL_MUTEX_init(&(m->mutex));
mcc_head = m;
HEIMDAL_MUTEX_unlock(&mcc_mutex);
return m;
@@ -116,10 +118,12 @@ mcc_resolve(krb5_context context, krb5_ccache *id, const char *res)
HEIMDAL_MUTEX_unlock(&mcc_mutex);
if (m != NULL) {
- m->refcnt++;
- (*id)->data.data = m;
- (*id)->data.length = sizeof(*m);
- return 0;
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ m->refcnt++;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ (*id)->data.data = m;
+ (*id)->data.length = sizeof(*m);
+ return 0;
}
m = mcc_alloc(res);
@@ -155,29 +159,73 @@ mcc_gen_new(krb5_context context, krb5_ccache *id)
return 0;
}
+static void KRB5_CALLCONV
+mcc_destroy_internal(krb5_context context,
+ krb5_mcache *m)
+{
+ struct link *l;
+
+ if (m->primary_principal != NULL) {
+ krb5_free_principal (context, m->primary_principal);
+ m->primary_principal = NULL;
+ }
+ m->dead = 1;
+
+ l = m->creds;
+ while (l != NULL) {
+ struct link *old;
+
+ krb5_free_cred_contents (context, &l->cred);
+ old = l;
+ l = l->next;
+ free (old);
+ }
+
+ m->creds = NULL;
+ return;
+}
+
static krb5_error_code KRB5_CALLCONV
mcc_initialize(krb5_context context,
krb5_ccache id,
krb5_principal primary_principal)
{
krb5_mcache *m = MCACHE(id);
+ krb5_error_code ret = 0;
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ heim_assert(m->refcnt != 0, "resurection released mcache");
+ /*
+ * It's important to destroy any existing
+ * creds here, that matches the baheviour
+ * of all other backends and also the
+ * MEMORY: backend in MIT.
+ */
+ mcc_destroy_internal(context, m);
m->dead = 0;
+ m->kdc_offset = 0;
m->mtime = time(NULL);
- return krb5_copy_principal (context,
- primary_principal,
- &m->primary_principal);
+ ret = krb5_copy_principal (context,
+ primary_principal,
+ &m->primary_principal);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ret;
}
static int
mcc_close_internal(krb5_mcache *m)
{
- if (--m->refcnt != 0)
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ heim_assert(m->refcnt != 0, "closed dead cache mcache");
+ if (--m->refcnt != 0) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
-
+ }
if (MISDEAD(m)) {
free (m->name);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 1;
}
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
@@ -185,8 +233,12 @@ static krb5_error_code KRB5_CALLCONV
mcc_close(krb5_context context,
krb5_ccache id)
{
- if (mcc_close_internal(MCACHE(id)))
+ krb5_mcache *m = MCACHE(id);
+
+ if (mcc_close_internal(MCACHE(id))) {
+ HEIMDAL_MUTEX_destroy(&(m->mutex));
krb5_data_free(&id->data);
+ }
return 0;
}
@@ -195,10 +247,13 @@ mcc_destroy(krb5_context context,
krb5_ccache id)
{
krb5_mcache **n, *m = MCACHE(id);
- struct link *l;
+ HEIMDAL_MUTEX_lock(&(m->mutex));
if (m->refcnt == 0)
- krb5_abortx(context, "mcc_destroy: refcnt already 0");
+ {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ krb5_abortx(context, "mcc_destroy: refcnt already 0");
+ }
if (!MISDEAD(m)) {
/* if this is an active mcache, remove it from the linked
@@ -211,23 +266,9 @@ mcc_destroy(krb5_context context,
}
}
HEIMDAL_MUTEX_unlock(&mcc_mutex);
- if (m->primary_principal != NULL) {
- krb5_free_principal (context, m->primary_principal);
- m->primary_principal = NULL;
- }
- m->dead = 1;
-
- l = m->creds;
- while (l != NULL) {
- struct link *old;
-
- krb5_free_cred_contents (context, &l->cred);
- old = l;
- l = l->next;
- free (old);
- }
- m->creds = NULL;
+ mcc_destroy_internal(context, m);
}
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
@@ -240,25 +281,32 @@ mcc_store_cred(krb5_context context,
krb5_error_code ret;
struct link *l;
+ HEIMDAL_MUTEX_lock(&(m->mutex));
if (MISDEAD(m))
- return ENOENT;
+ {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ENOENT;
+ }
l = malloc (sizeof(*l));
if (l == NULL) {
- krb5_set_error_message(context, KRB5_CC_NOMEM,
- N_("malloc: out of memory", ""));
- return KRB5_CC_NOMEM;
+ krb5_set_error_message(context, KRB5_CC_NOMEM,
+ N_("malloc: out of memory", ""));
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return KRB5_CC_NOMEM;
}
l->next = m->creds;
m->creds = l;
memset (&l->cred, 0, sizeof(l->cred));
ret = krb5_copy_creds_contents (context, creds, &l->cred);
if (ret) {
- m->creds = l->next;
- free (l);
- return ret;
+ m->creds = l->next;
+ free (l);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ret;
}
m->mtime = time(NULL);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
@@ -268,25 +316,35 @@ mcc_get_principal(krb5_context context,
krb5_principal *principal)
{
krb5_mcache *m = MCACHE(id);
+ krb5_error_code ret = 0;
- if (MISDEAD(m) || m->primary_principal == NULL)
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (MISDEAD(m) || m->primary_principal == NULL) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return ENOENT;
- return krb5_copy_principal (context,
- m->primary_principal,
- principal);
+ }
+ ret = krb5_copy_principal (context,
+ m->primary_principal,
+ principal);
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
+ return ret;
}
static krb5_error_code KRB5_CALLCONV
mcc_get_first (krb5_context context,
- krb5_ccache id,
- krb5_cc_cursor *cursor)
+ krb5_ccache id,
+ krb5_cc_cursor *cursor)
{
krb5_mcache *m = MCACHE(id);
- if (MISDEAD(m))
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (MISDEAD(m)) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return ENOENT;
-
+ }
*cursor = m->creds;
+
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
@@ -299,8 +357,12 @@ mcc_get_next (krb5_context context,
krb5_mcache *m = MCACHE(id);
struct link *l;
- if (MISDEAD(m))
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ if (MISDEAD(m)) {
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return ENOENT;
+ }
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
l = *cursor;
if (l != NULL) {
@@ -328,6 +390,9 @@ mcc_remove_cred(krb5_context context,
{
krb5_mcache *m = MCACHE(id);
struct link **q, *p;
+
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+
for(q = &m->creds, p = *q; p; p = *q) {
if(krb5_compare_creds(context, which, mcreds, &p->cred)) {
*q = p->next;
@@ -337,6 +402,7 @@ mcc_remove_cred(krb5_context context,
} else
q = &p->next;
}
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
@@ -358,16 +424,16 @@ mcc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
struct mcache_iter *iter;
iter = calloc(1, sizeof(*iter));
- if (iter == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (iter == NULL)
+ return krb5_enomem(context);
HEIMDAL_MUTEX_lock(&mcc_mutex);
iter->cache = mcc_head;
- if (iter->cache)
+ if (iter->cache) {
+ HEIMDAL_MUTEX_lock(&(iter->cache->mutex));
iter->cache->refcnt++;
+ HEIMDAL_MUTEX_unlock(&(iter->cache->mutex));
+ }
HEIMDAL_MUTEX_unlock(&mcc_mutex);
*cursor = iter;
@@ -387,7 +453,12 @@ mcc_get_cache_next(krb5_context context, krb5_cc_cursor cursor, krb5_ccache *id)
HEIMDAL_MUTEX_lock(&mcc_mutex);
m = iter->cache;
if (m->next)
- m->next->refcnt++;
+ {
+ HEIMDAL_MUTEX_lock(&(m->next->mutex));
+ m->next->refcnt++;
+ HEIMDAL_MUTEX_unlock(&(m->next->mutex));
+ }
+
iter->cache = m->next;
HEIMDAL_MUTEX_unlock(&mcc_mutex);
@@ -407,7 +478,7 @@ mcc_end_cache_get(krb5_context context, krb5_cc_cursor cursor)
struct mcache_iter *iter = cursor;
if (iter->cache)
- mcc_close_internal(iter->cache);
+ mcc_close_internal(iter->cache);
iter->cache = NULL;
free(iter);
return 0;
@@ -431,6 +502,8 @@ mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
}
}
+ HEIMDAL_MUTEX_lock(&(mfrom->mutex));
+ HEIMDAL_MUTEX_lock(&(mto->mutex));
/* swap creds */
creds = mto->creds;
mto->creds = mfrom->creds;
@@ -442,6 +515,8 @@ mcc_move(krb5_context context, krb5_ccache from, krb5_ccache to)
mto->mtime = mfrom->mtime = time(NULL);
+ HEIMDAL_MUTEX_unlock(&(mfrom->mutex));
+ HEIMDAL_MUTEX_unlock(&(mto->mutex));
HEIMDAL_MUTEX_unlock(&mcc_mutex);
mcc_destroy(context, from);
@@ -452,18 +527,18 @@ static krb5_error_code KRB5_CALLCONV
mcc_default_name(krb5_context context, char **str)
{
*str = strdup("MEMORY:");
- if (*str == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*str == NULL)
+ return krb5_enomem(context);
return 0;
}
static krb5_error_code KRB5_CALLCONV
mcc_lastchange(krb5_context context, krb5_ccache id, krb5_timestamp *mtime)
{
- *mtime = MCACHE(id)->mtime;
+ krb5_mcache *m = MCACHE(id);
+ HEIMDAL_MUTEX_lock(&(m->mutex));
+ *mtime = m->mtime;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
@@ -471,7 +546,9 @@ static krb5_error_code KRB5_CALLCONV
mcc_set_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat kdc_offset)
{
krb5_mcache *m = MCACHE(id);
+ HEIMDAL_MUTEX_lock(&(m->mutex));
m->kdc_offset = kdc_offset;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
@@ -479,7 +556,9 @@ static krb5_error_code KRB5_CALLCONV
mcc_get_kdc_offset(krb5_context context, krb5_ccache id, krb5_deltat *kdc_offset)
{
krb5_mcache *m = MCACHE(id);
+ HEIMDAL_MUTEX_lock(&(m->mutex));
*kdc_offset = m->kdc_offset;
+ HEIMDAL_MUTEX_unlock(&(m->mutex));
return 0;
}
diff --git a/lib/krb5/misc.c b/lib/krb5/misc.c
index ac6720c4e992..baf165c00a6e 100644
--- a/lib/krb5/misc.c
+++ b/lib/krb5/misc.c
@@ -48,55 +48,38 @@ _krb5_s4u2self_to_checksumdata(krb5_context context,
size_t i;
sp = krb5_storage_emem();
- if (sp == NULL) {
- krb5_clear_error_message(context);
- return ENOMEM;
- }
+ if (sp == NULL)
+ return krb5_enomem(context);
krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
ret = krb5_store_int32(sp, self->name.name_type);
- if (ret)
- goto out;
+ if (ret) {
+ krb5_clear_error_message(context);
+ return ret;
+ }
for (i = 0; i < self->name.name_string.len; i++) {
size = strlen(self->name.name_string.val[i]);
ssize = krb5_storage_write(sp, self->name.name_string.val[i], size);
- if (ssize != (krb5_ssize_t)size) {
- ret = ENOMEM;
- goto out;
- }
+ if (ssize != (krb5_ssize_t)size)
+ return krb5_enomem(context);
}
size = strlen(self->realm);
ssize = krb5_storage_write(sp, self->realm, size);
- if (ssize != (krb5_ssize_t)size) {
- ret = ENOMEM;
- goto out;
- }
+ if (ssize != (krb5_ssize_t)size)
+ return krb5_enomem(context);
size = strlen(self->auth);
ssize = krb5_storage_write(sp, self->auth, size);
- if (ssize != (krb5_ssize_t)size) {
- ret = ENOMEM;
- goto out;
- }
+ if (ssize != (krb5_ssize_t)size)
+ return krb5_enomem(context);
ret = krb5_storage_to_data(sp, data);
krb5_storage_free(sp);
return ret;
-
-out:
- krb5_clear_error_message(context);
- return ret;
-}
-
-krb5_error_code
-krb5_enomem(krb5_context context)
-{
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
}
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_debug_backtrace(krb5_context context)
{
-#if defined(HAVE_BACKTRACE) && !defined(HEIMDAL_SMALLER)
+#if defined(HAVE_BACKTRACE) && defined(HAVE_BACKTRACE_SYMBOLS) && !defined(HEIMDAL_SMALLER)
void *stack[128];
char **strs = NULL;
int i, frames = backtrace(stack, sizeof(stack) / sizeof(stack[0]));
@@ -110,7 +93,7 @@ _krb5_debug_backtrace(krb5_context context)
#endif
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_einval(krb5_context context, const char *func, unsigned long argn)
{
#ifndef HEIMDAL_SMALLER
diff --git a/lib/krb5/mit_glue.c b/lib/krb5/mit_glue.c
index 16c230a11f96..deee242c1e8c 100644
--- a/lib/krb5/mit_glue.c
+++ b/lib/krb5/mit_glue.c
@@ -96,7 +96,7 @@ krb5_c_get_checksum(krb5_context context, const krb5_checksum *cksum,
if (data) {
*data = malloc(sizeof(**data));
if (*data == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
ret = der_copy_octet_string(&cksum->checksum, *data);
if (ret) {
@@ -167,7 +167,7 @@ krb5_copy_checksum (krb5_context context,
{
*new = malloc(sizeof(**new));
if (*new == NULL)
- return ENOMEM;
+ return krb5_enomem(context);
return copy_Checksum(old, *new);
}
@@ -378,7 +378,8 @@ krb5_c_prf(krb5_context context,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_c_random_make_octets(krb5_context context, krb5_data * data)
{
- return krb5_generate_random_keyblock(context, data->length, data->data);
+ krb5_generate_random_block(data->data, data->length);
+ return 0;
}
/**
diff --git a/lib/krb5/mk_error.c b/lib/krb5/mk_error.c
index 5fee1d6bed63..7f0be713e04f 100644
--- a/lib/krb5/mk_error.c
+++ b/lib/krb5/mk_error.c
@@ -34,15 +34,16 @@
#include "krb5_locl.h"
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_mk_error(krb5_context context,
- krb5_error_code error_code,
- const char *e_text,
- const krb5_data *e_data,
- const krb5_principal client,
- const krb5_principal server,
- time_t *client_time,
- int *client_usec,
- krb5_data *reply)
+krb5_mk_error_ext(krb5_context context,
+ krb5_error_code error_code,
+ const char *e_text,
+ const krb5_data *e_data,
+ const krb5_principal server,
+ const PrincipalName *client_name,
+ const Realm *client_realm,
+ time_t *client_time,
+ int *client_usec,
+ krb5_data *reply)
{
const char *e_text2 = NULL;
KRB_ERROR msg;
@@ -78,10 +79,8 @@ krb5_mk_error(krb5_context context,
static char unspec[] = "<unspecified realm>";
msg.realm = unspec;
}
- if(client){
- msg.crealm = &client->realm;
- msg.cname = &client->name;
- }
+ msg.crealm = rk_UNCONST(client_realm);
+ msg.cname = rk_UNCONST(client_name);
ASN1_MALLOC_ENCODE(KRB_ERROR, reply->data, reply->length, &msg, &len, ret);
if (e_text2)
@@ -92,3 +91,27 @@ krb5_mk_error(krb5_context context,
krb5_abortx(context, "internal error in ASN.1 encoder");
return 0;
}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_mk_error(krb5_context context,
+ krb5_error_code error_code,
+ const char *e_text,
+ const krb5_data *e_data,
+ const krb5_principal client,
+ const krb5_principal server,
+ time_t *client_time,
+ int *client_usec,
+ krb5_data *reply)
+{
+ const PrincipalName *client_name = NULL;
+ const Realm *client_realm = NULL;
+
+ if (client) {
+ client_realm = &client->realm;
+ client_name = &client->name;
+ }
+
+ return krb5_mk_error_ext(context, error_code, e_text, e_data,
+ server, client_name, client_realm,
+ client_time, client_usec, reply);
+}
diff --git a/lib/krb5/mk_priv.c b/lib/krb5/mk_priv.c
index dede6d2fa4dc..e3b2f9128ba7 100644
--- a/lib/krb5/mk_priv.c
+++ b/lib/krb5/mk_priv.c
@@ -135,10 +135,8 @@ krb5_mk_priv(krb5_context context,
ret = krb5_data_copy(outbuf, buf + buf_size - len, len);
if (ret) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
free(buf);
- return ENOMEM;
+ return krb5_enomem(context);
}
free (buf);
if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_SEQUENCE)
diff --git a/lib/krb5/mk_rep.c b/lib/krb5/mk_rep.c
index 84c315291c1a..bd24c6ec6e19 100644
--- a/lib/krb5/mk_rep.c
+++ b/lib/krb5/mk_rep.c
@@ -67,9 +67,7 @@ krb5_mk_rep(krb5_context context,
&body.subkey);
if (ret) {
free_EncAPRepPart(&body);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
} else
body.subkey = NULL;
@@ -80,9 +78,8 @@ krb5_mk_rep(krb5_context context,
&auth_context->local_seqnumber);
ALLOC(body.seq_number, 1);
if (body.seq_number == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
free_EncAPRepPart(&body);
- return ENOMEM;
+ return krb5_enomem(context);
}
*(body.seq_number) = auth_context->local_seqnumber;
} else
diff --git a/lib/krb5/mk_req_ext.c b/lib/krb5/mk_req_ext.c
index af68e4e19530..18b16a9bf2b0 100644
--- a/lib/krb5/mk_req_ext.c
+++ b/lib/krb5/mk_req_ext.c
@@ -33,7 +33,7 @@
#include "krb5_locl.h"
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_mk_req_internal(krb5_context context,
krb5_auth_context *auth_context,
const krb5_flags ap_req_options,
diff --git a/lib/krb5/n-fold-test.c b/lib/krb5/n-fold-test.c
index 452964522be8..2938b44b3ae8 100644
--- a/lib/krb5/n-fold-test.c
+++ b/lib/krb5/n-fold-test.c
@@ -87,7 +87,7 @@ static struct testcase {
0x08, 0xa5, 0x08, 0x41, 0x22, 0x9a, 0xd7, 0x98, 0xfa, 0xb9, 0x54,
0x0c, 0x1b}
},
- {NULL, 0}
+ {NULL, 0, {0}}
};
int
diff --git a/lib/krb5/n-fold.c b/lib/krb5/n-fold.c
index 2e6092c5ca80..ba3150d423b8 100644
--- a/lib/krb5/n-fold.c
+++ b/lib/krb5/n-fold.c
@@ -32,68 +32,79 @@
#include "krb5_locl.h"
-static krb5_error_code
-rr13(unsigned char *buf, size_t len)
+static void
+rr13(uint8_t *dst1, uint8_t *dst2, uint8_t *src, size_t len)
{
- unsigned char *tmp;
int bytes = (len + 7) / 8;
int i;
- if(len == 0)
- return 0;
- {
- const int bits = 13 % len;
- const int lbit = len % 8;
-
- tmp = malloc(bytes);
- if (tmp == NULL)
- return ENOMEM;
- memcpy(tmp, buf, bytes);
- if(lbit) {
- /* pad final byte with inital bits */
- tmp[bytes - 1] &= 0xff << (8 - lbit);
- for(i = lbit; i < 8; i += len)
- tmp[bytes - 1] |= buf[0] >> i;
- }
- for(i = 0; i < bytes; i++) {
- int bb;
- int b1, s1, b2, s2;
- /* calculate first bit position of this byte */
- bb = 8 * i - bits;
- while(bb < 0)
- bb += len;
- /* byte offset and shift count */
- b1 = bb / 8;
- s1 = bb % 8;
-
- if(bb + 8 > bytes * 8)
- /* watch for wraparound */
- s2 = (len + 8 - s1) % 8;
- else
- s2 = 8 - s1;
- b2 = (b1 + 1) % bytes;
- buf[i] = (tmp[b1] << s1) | (tmp[b2] >> s2);
- }
- free(tmp);
+ const int bits = 13 % len;
+
+ for (i = 0; i < bytes; i++) {
+ int bb;
+ int b1, s1, b2, s2;
+ /* calculate first bit position of this byte */
+ bb = 8 * i - bits;
+ while(bb < 0)
+ bb += len;
+ /* byte offset and shift count */
+ b1 = bb / 8;
+ s1 = bb % 8;
+
+ if (bb + 8 > bytes * 8)
+ /* watch for wraparound */
+ s2 = (len + 8 - s1) % 8;
+ else
+ s2 = 8 - s1;
+ b2 = (b1 + 1) % bytes;
+ dst1[i] = (src[b1] << s1) | (src[b2] >> s2);
+ dst2[i] = dst1[i];
}
- return 0;
+
+ return;
}
-/* Add `b' to `a', both being one's complement numbers. */
+/*
+ * Add `b' to `a', both being one's complement numbers.
+ * This function assumes that inputs *a, *b are aligned
+ * to 4 bytes.
+ */
static void
-add1(unsigned char *a, unsigned char *b, size_t len)
+add1(uint8_t *a, uint8_t *b, size_t len)
{
int i;
int carry = 0;
- for(i = len - 1; i >= 0; i--){
- int x = a[i] + b[i] + carry;
+ uint32_t x;
+ uint32_t left, right;
+
+ for (i = len - 1; (i+1) % 4; i--) {
+ x = a[i] + b[i] + carry;
carry = x > 0xff;
a[i] = x & 0xff;
}
- for(i = len - 1; carry && i >= 0; i--){
- int x = a[i] + carry;
+
+ for (i = len / 4 - 1; i >= 0; i--) {
+ left = ntohl(((uint32_t *)a)[i]);
+ right = ntohl(((uint32_t *)b)[i]);
+ x = left + right + carry;
+ carry = x < left || x < right;
+ ((uint32_t *)a)[i] = x;
+ }
+
+ for (i = len - 1; (i+1) % 4; i--) {
+ x = a[i] + carry;
carry = x > 0xff;
a[i] = x & 0xff;
}
+
+ for (i = len / 4 - 1; carry && i >= 0; i--) {
+ left = ((uint32_t *)a)[i];
+ x = left + carry;
+ carry = x < left;
+ ((uint32_t *)a)[i] = x;
+ }
+
+ for (i = len / 4 - 1; i >=0; i--)
+ ((uint32_t *)a)[i] = htonl(((uint32_t *)a)[i]);
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -101,25 +112,25 @@ _krb5_n_fold(const void *str, size_t len, void *key, size_t size)
{
/* if len < size we need at most N * len bytes, ie < 2 * size;
if len > size we need at most 2 * len */
- krb5_error_code ret = 0;
size_t maxlen = 2 * max(size, len);
size_t l = 0;
- unsigned char *tmp = malloc(maxlen);
- unsigned char *buf = malloc(len);
+ uint8_t *tmp;
+ uint8_t *tmpbuf;
+ uint8_t *buf1;
+ uint8_t *buf2;
- if (tmp == NULL || buf == NULL) {
- ret = ENOMEM;
- goto out;
- }
+ tmp = malloc(maxlen + 2 * len);
+ if (tmp == NULL)
+ return ENOMEM;
+
+ buf1 = tmp + maxlen;
+ buf2 = tmp + maxlen + len;
- memcpy(buf, str, len);
memset(key, 0, size);
+ memcpy(buf1, str, len);
+ memcpy(tmp, buf1, len);
do {
- memcpy(tmp + l, buf, len);
l += len;
- ret = rr13(buf, len * 8);
- if (ret)
- goto out;
while(l >= size) {
add1(key, tmp, size);
l -= size;
@@ -127,15 +138,13 @@ _krb5_n_fold(const void *str, size_t len, void *key, size_t size)
break;
memmove(tmp, tmp + size, l);
}
+ rr13(tmp + l, buf2, buf1, len * 8);
+ tmpbuf = buf1;
+ buf1 = buf2;
+ buf2 = tmpbuf;
} while(l != 0);
-out:
- if (buf) {
- memset(buf, 0, len);
- free(buf);
- }
- if (tmp) {
- memset(tmp, 0, maxlen);
- free(tmp);
- }
- return ret;
+
+ memset(tmp, 0, maxlen + 2 * len);
+ free(tmp);
+ return 0;
}
diff --git a/lib/krb5/net_read.c b/lib/krb5/net_read.c
index f6d781c27c0b..70b18007e13d 100644
--- a/lib/krb5/net_read.c
+++ b/lib/krb5/net_read.c
@@ -33,6 +33,15 @@
#include "krb5_locl.h"
+/**
+ * Read \a len bytes from socket \a p_fd into buffer \a buf.
+ * Block until \a len bytes are read or until an error.
+ *
+ * @return If successful, the number of bytes read: \a len.
+ * On end-of-file, 0.
+ * On error, less than 0 (if single-threaded, the error can be found
+ * in the errno global variable).
+ */
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
krb5_net_read (krb5_context context,
void *p_fd,
diff --git a/lib/krb5/pac.c b/lib/krb5/pac.c
index 91f68d5e00e7..c26201be9cd7 100644
--- a/lib/krb5/pac.c
+++ b/lib/krb5/pac.c
@@ -405,7 +405,7 @@ krb5_pac_get_types(krb5_context context,
{
size_t i;
- *types = calloc(p->pac->numbuffers, sizeof(*types));
+ *types = calloc(p->pac->numbuffers, sizeof(**types));
if (*types == NULL) {
*len = 0;
return krb5_enomem(context);
@@ -549,6 +549,8 @@ create_checksum(krb5_context context,
if (cksumtype == (uint32_t)CKSUMTYPE_HMAC_MD5) {
ret = HMAC_MD5_any_checksum(context, key, data, datalen,
KRB5_KU_OTHER_CKSUM, &cksum);
+ if (ret)
+ return ret;
} else {
ret = krb5_crypto_init(context, key, 0, &crypto);
if (ret)
@@ -595,11 +597,12 @@ verify_logonname(krb5_context context,
krb5_const_principal principal)
{
krb5_error_code ret;
- krb5_principal p2;
uint32_t time1, time2;
krb5_storage *sp;
uint16_t len;
- char *s;
+ char *s = NULL;
+ char *principal_string = NULL;
+ char *logon_string = NULL;
sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo,
logon_name->buffersize);
@@ -615,7 +618,13 @@ verify_logonname(krb5_context context,
uint64_t t1, t2;
t1 = unix2nttime(authtime);
t2 = ((uint64_t)time2 << 32) | time1;
- if (t1 != t2) {
+ /*
+ * When neither the ticket nor the PAC set an explicit authtime,
+ * both times are zero, but relative to different time scales.
+ * So we must compare "not set" values without converting to a
+ * common time reference.
+ */
+ if (t1 != t2 && (t2 != 0 && authtime != 0)) {
krb5_storage_free(sp);
krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch");
return EINVAL;
@@ -664,29 +673,36 @@ verify_logonname(krb5_context context,
return ret;
}
u8len += 1; /* Add space for NUL */
- s = malloc(u8len);
- if (s == NULL) {
+ logon_string = malloc(u8len);
+ if (logon_string == NULL) {
free(ucs2);
return krb5_enomem(context);
}
- ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len);
+ ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len);
free(ucs2);
if (ret) {
- free(s);
+ free(logon_string);
krb5_set_error_message(context, ret, "Failed to convert to UTF-8");
return ret;
}
}
- ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2);
- free(s);
- if (ret)
+ ret = krb5_unparse_name_flags(context, principal,
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM |
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY,
+ &principal_string);
+ if (ret) {
+ free(logon_string);
return ret;
+ }
- if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) {
+ ret = strcmp(logon_string, principal_string);
+ if (ret != 0) {
ret = EINVAL;
- krb5_set_error_message(context, ret, "PAC logon name mismatch");
+ krb5_set_error_message(context, ret, "PAC logon name [%s] mismatch principal name [%s]",
+ logon_string, principal_string);
}
- krb5_free_principal(context, p2);
+ free(logon_string);
+ free(principal_string);
return ret;
out:
return ret;
@@ -722,7 +738,9 @@ build_logon_name(krb5_context context,
CHECK(ret, krb5_store_uint32(sp, t >> 32), out);
ret = krb5_unparse_name_flags(context, principal,
- KRB5_PRINCIPAL_UNPARSE_NO_REALM, &s);
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM |
+ KRB5_PRINCIPAL_UNPARSE_DISPLAY,
+ &s);
if (ret)
goto out;
@@ -733,8 +751,8 @@ build_logon_name(krb5_context context,
ret = wind_utf8ucs2_length(s, &ucs2_len);
if (ret) {
+ krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
free(s);
- krb5_set_error_message(context, ret, "Failed to count length of UTF-8 string");
return ret;
}
@@ -745,16 +763,17 @@ build_logon_name(krb5_context context,
}
ret = wind_utf8ucs2(s, ucs2, &ucs2_len);
- free(s);
if (ret) {
free(ucs2);
- krb5_set_error_message(context, ret, "Failed to convert string to UCS-2");
+ krb5_set_error_message(context, ret, "Principal %s is not valid UTF-8", s);
+ free(s);
return ret;
- }
+ } else
+ free(s);
s2_len = (ucs2_len + 1) * 2;
s2 = malloc(s2_len);
- if (ucs2 == NULL) {
+ if (s2 == NULL) {
free(ucs2);
return krb5_enomem(context);
}
@@ -849,15 +868,14 @@ krb5_pac_verify(krb5_context context,
{
krb5_data *copy;
+ if (pac->server_checksum->buffersize < 4 ||
+ pac->privsvr_checksum->buffersize < 4)
+ return EINVAL;
+
ret = krb5_copy_data(context, &pac->data, &copy);
if (ret)
return ret;
- if (pac->server_checksum->buffersize < 4)
- return EINVAL;
- if (pac->privsvr_checksum->buffersize < 4)
- return EINVAL;
-
memset((char *)copy->data + pac->server_checksum->offset_lo + 4,
0,
pac->server_checksum->buffersize - 4);
@@ -948,7 +966,7 @@ pac_checksum(krb5_context context,
return 0;
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_pac_sign(krb5_context context,
krb5_pac p,
time_t authtime,
@@ -969,6 +987,40 @@ _krb5_pac_sign(krb5_context context,
krb5_data_zero(&logon);
+ for (i = 0; i < p->pac->numbuffers; i++) {
+ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) {
+ if (p->server_checksum == NULL) {
+ p->server_checksum = &p->pac->buffers[i];
+ }
+ if (p->server_checksum != &p->pac->buffers[i]) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC have two server checksums", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) {
+ if (p->privsvr_checksum == NULL) {
+ p->privsvr_checksum = &p->pac->buffers[i];
+ }
+ if (p->privsvr_checksum != &p->pac->buffers[i]) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC have two KDC checksums", ""));
+ goto out;
+ }
+ } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) {
+ if (p->logon_name == NULL) {
+ p->logon_name = &p->pac->buffers[i];
+ }
+ if (p->logon_name != &p->pac->buffers[i]) {
+ ret = EINVAL;
+ krb5_set_error_message(context, ret,
+ N_("PAC have two logon names", ""));
+ goto out;
+ }
+ }
+ }
+
if (p->logon_name == NULL)
num++;
if (p->server_checksum == NULL)
diff --git a/lib/krb5/padata.c b/lib/krb5/padata.c
index babe22cb3876..71c9a7f14772 100644
--- a/lib/krb5/padata.c
+++ b/lib/krb5/padata.c
@@ -49,11 +49,8 @@ krb5_padata_add(krb5_context context, METHOD_DATA *md,
PA_DATA *pa;
pa = realloc (md->val, (md->len + 1) * sizeof(*md->val));
- if (pa == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (pa == NULL)
+ return krb5_enomem(context);
md->val = pa;
pa[md->len].padata_type = type;
diff --git a/lib/krb5/pcache.c b/lib/krb5/pcache.c
index 23d5389a607d..3a9949dc2777 100644
--- a/lib/krb5/pcache.c
+++ b/lib/krb5/pcache.c
@@ -36,31 +36,35 @@
#endif
#include <assert.h>
-krb5_error_code
-_krb5_load_ccache_plugins(krb5_context context)
+/*
+ * cc_plugin_register_to_context is executed once per krb5_init_context().
+ * Its job is to register the plugin's krb5_cc_ops structure with the
+ * krb5_context.
+ */
+
+static krb5_error_code KRB5_LIB_CALL
+cc_plugin_register_to_context(krb5_context context, const void *plug, void *plugctx, void *userctx)
{
- struct krb5_plugin * plist = NULL;
- struct krb5_plugin *p;
- krb5_error_code code;
+ krb5_cc_ops *ccops = (krb5_cc_ops *)plugctx;
+ krb5_error_code ret;
+
+ if (ccops == NULL || ccops->version < KRB5_CC_OPS_VERSION)
+ return KRB5_PLUGIN_NO_HANDLE;
- code = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_CCACHE,
- &plist);
- if (code)
- return code;
+ ret = krb5_cc_register(context, ccops, TRUE);
+ if (ret != 0)
+ *((krb5_error_code *)userctx) = ret;
- for (p = plist; p != NULL; p = _krb5_plugin_get_next(p)) {
- krb5_cc_ops * ccops;
- krb5_error_code c_load;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
- ccops = _krb5_plugin_get_symbol(p);
- if (ccops != NULL && ccops->version == KRB5_CC_OPS_VERSION) {
- c_load = krb5_cc_register(context, ccops, TRUE);
- if (c_load != 0)
- code = c_load;
- }
- }
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_load_ccache_plugins(krb5_context context)
+{
+ krb5_error_code userctx = 0;
- _krb5_plugin_free(plist);
+ (void)_krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_CCACHE,
+ 0, 0, &userctx, cc_plugin_register_to_context);
- return code;
+ return userctx;
}
diff --git a/lib/krb5/pkinit-ec.c b/lib/krb5/pkinit-ec.c
new file mode 100644
index 000000000000..33bc62c8dcef
--- /dev/null
+++ b/lib/krb5/pkinit-ec.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2016 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <roken.h>
+
+#ifdef PKINIT
+
+/*
+ * As with the other *-ec.c files in Heimdal, this is a bit of a hack.
+ *
+ * The idea is to use OpenSSL for EC because hcrypto doesn't have the
+ * required functionality at this time. To do this we segregate
+ * EC-using code into separate source files and then we arrange for them
+ * to get the OpenSSL headers and not the conflicting hcrypto ones.
+ *
+ * Because of auto-generated *-private.h headers, we end up needing to
+ * make sure various types are defined before we include them, thus the
+ * strange header include order here.
+ */
+
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+#include <openssl/ec.h>
+#include <openssl/ecdh.h>
+#include <openssl/evp.h>
+#include <openssl/bn.h>
+#define HEIM_NO_CRYPTO_HDRS
+#endif
+
+/*
+ * NO_HCRYPTO_POLLUTION -> don't refer to hcrypto type/function names
+ * that we don't need in this file and which would clash with OpenSSL's
+ * in ways that are difficult to address in cleaner ways.
+ *
+ * In the medium- to long-term what we should do is move all PK in
+ * Heimdal to the newer EVP interfaces for PK and then nothing outside
+ * lib/hcrypto should ever have to include OpenSSL headers, and -more
+ * specifically- the only thing that should ever have to include OpenSSL
+ * headers is the OpenSSL backend to hcrypto.
+ */
+#define NO_HCRYPTO_POLLUTION
+
+#include "krb5_locl.h"
+#include <hcrypto/des.h>
+#include <cms_asn1.h>
+#include <pkcs8_asn1.h>
+#include <pkcs9_asn1.h>
+#include <pkcs12_asn1.h>
+#include <pkinit_asn1.h>
+#include <asn1_err.h>
+
+#include <der.h>
+
+krb5_error_code
+_krb5_build_authpack_subjectPK_EC(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ AuthPack *a)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ krb5_error_code ret;
+ ECParameters ecp;
+ unsigned char *p;
+ size_t size;
+ int xlen;
+
+ /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
+
+ ecp.element = choice_ECParameters_namedCurve;
+ ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
+ &ecp.u.namedCurve);
+ if (ret)
+ return ret;
+
+ ALLOC(a->clientPublicValue->algorithm.parameters, 1);
+ if (a->clientPublicValue->algorithm.parameters == NULL) {
+ free_ECParameters(&ecp);
+ return krb5_enomem(context);
+ }
+ ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
+ free_ECParameters(&ecp);
+ if (ret)
+ return ret;
+ if ((int)size != xlen)
+ krb5_abortx(context, "asn1 internal error");
+
+ a->clientPublicValue->algorithm.parameters->data = p;
+ a->clientPublicValue->algorithm.parameters->length = size;
+
+ /* copy in public key */
+
+ ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
+ &a->clientPublicValue->algorithm.algorithm);
+ if (ret)
+ return ret;
+
+ ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ if (ctx->u.eckey == NULL)
+ return krb5_enomem(context);
+
+ ret = EC_KEY_generate_key(ctx->u.eckey);
+ if (ret != 1)
+ return EINVAL;
+
+ xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
+ if (xlen <= 0)
+ return EINVAL;
+
+ p = malloc(xlen);
+ if (p == NULL)
+ return krb5_enomem(context);
+
+ a->clientPublicValue->subjectPublicKey.data = p;
+
+ xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
+ if (xlen <= 0) {
+ a->clientPublicValue->subjectPublicKey.data = NULL;
+ free(p);
+ return EINVAL;
+ }
+
+ a->clientPublicValue->subjectPublicKey.length = xlen * 8;
+
+ return 0;
+
+ /* XXX verify that this is right with RFC3279 */
+#else
+ krb5_set_error_message(context, ENOTSUP,
+ N_("PKINIT: ECDH not supported", ""));
+ return ENOTSUP;
+#endif
+}
+
+krb5_error_code
+_krb5_pk_rd_pa_reply_ecdh_compute_key(krb5_context context,
+ krb5_pk_init_ctx ctx,
+ const unsigned char *in,
+ size_t in_sz,
+ unsigned char **out,
+ int *out_sz)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ krb5_error_code ret = 0;
+ int dh_gen_keylen;
+
+ const EC_GROUP *group;
+ EC_KEY *public = NULL;
+
+ group = EC_KEY_get0_group(ctx->u.eckey);
+
+ public = EC_KEY_new();
+ if (public == NULL)
+ return krb5_enomem(context);
+ if (EC_KEY_set_group(public, group) != 1) {
+ EC_KEY_free(public);
+ return krb5_enomem(context);
+ }
+
+ if (o2i_ECPublicKey(&public, &in, in_sz) == NULL) {
+ EC_KEY_free(public);
+ ret = KRB5KRB_ERR_GENERIC;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Can't parse ECDH public key", ""));
+ return ret;
+ }
+
+ *out_sz = (EC_GROUP_get_degree(group) + 7) / 8;
+ if (*out_sz < 0)
+ return EOVERFLOW;
+ *out = malloc(*out_sz);
+ if (*out == NULL) {
+ EC_KEY_free(public);
+ return krb5_enomem(context);
+ }
+ dh_gen_keylen = ECDH_compute_key(*out, *out_sz,
+ EC_KEY_get0_public_key(public),
+ ctx->u.eckey, NULL);
+ EC_KEY_free(public);
+ if (dh_gen_keylen <= 0) {
+ ret = KRB5KRB_ERR_GENERIC;
+ dh_gen_keylen = 0;
+ krb5_set_error_message(context, ret,
+ N_("PKINIT: Can't compute ECDH public key", ""));
+ free(*out);
+ *out = NULL;
+ *out_sz = 0;
+ }
+ *out_sz = dh_gen_keylen;
+
+ return ret;
+#else
+ krb5_set_error_message(context, ENOTSUP,
+ N_("PKINIT: ECDH not supported", ""));
+ return ENOTSUP;
+#endif
+}
+
+void
+_krb5_pk_eckey_free(void *eckey)
+{
+#ifdef HAVE_HCRYPTO_W_OPENSSL
+ EC_KEY_free(eckey);
+#endif
+}
+
+#else
+
+static char lib_krb5_pkinit_ec_c = '\0';
+
+#endif
diff --git a/lib/krb5/pkinit.c b/lib/krb5/pkinit.c
index 1103a17807be..0adb65e7afa1 100644
--- a/lib/krb5/pkinit.c
+++ b/lib/krb5/pkinit.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003 - 2007 Kungliga Tekniska Högskolan
+ * Copyright (c) 2003 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -58,34 +58,13 @@ struct krb5_pk_cert {
hx509_cert cert;
};
-struct krb5_pk_init_ctx_data {
- struct krb5_pk_identity *id;
- enum { USE_RSA, USE_DH, USE_ECDH } keyex;
- union {
- DH *dh;
-#ifdef HAVE_OPENSSL
- EC_KEY *eckey;
-#endif
- } u;
- krb5_data *clientDHNonce;
- struct krb5_dh_moduli **m;
- hx509_peer_info peer;
- enum krb5_pk_type type;
- unsigned int require_binding:1;
- unsigned int require_eku:1;
- unsigned int require_krbtgt_otherName:1;
- unsigned int require_hostname_match:1;
- unsigned int trustedCertifiers:1;
- unsigned int anonymous:1;
-};
-
static void
pk_copy_error(krb5_context context,
hx509_context hx509ctx,
int hxret,
const char *fmt,
...)
- __attribute__ ((format (printf, 4, 5)));
+ __attribute__ ((__format__ (__printf__, 4, 5)));
/*
*
@@ -183,10 +162,10 @@ find_cert(krb5_context context, struct krb5_pk_identity *id,
hx509_query *q, hx509_cert *cert)
{
struct certfind cf[4] = {
- { "MobileMe EKU" },
- { "PKINIT EKU" },
- { "MS EKU" },
- { "any (or no)" }
+ { "MobileMe EKU", NULL },
+ { "PKINIT EKU", NULL },
+ { "MS EKU", NULL },
+ { "any (or no)", NULL }
};
int ret = HX509_CERT_NOT_FOUND;
size_t i, start = 1;
@@ -328,8 +307,10 @@ cert2epi(hx509_context context, void *ctx, hx509_cert c)
id.issuerAndSerialNumber->length,
&iasn, &size, ret);
free_IssuerAndSerialNumber(&iasn);
- if (ret)
+ if (ret) {
+ free_ExternalPrincipalIdentifier(&id);
return ret;
+ }
if (id.issuerAndSerialNumber->length != size)
abort();
}
@@ -399,9 +380,7 @@ build_auth_pack(krb5_context context,
ALLOC(a->pkAuthenticator.paChecksum, 1);
if (a->pkAuthenticator.paChecksum == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = krb5_data_copy(a->pkAuthenticator.paChecksum,
@@ -436,11 +415,8 @@ build_auth_pack(krb5_context context,
return ret;
ctx->u.dh = DH_new();
- if (ctx->u.dh == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ctx->u.dh == NULL)
+ return krb5_enomem(context);
ret = select_dh_group(context, ctx->u.dh, dh_min_bits, ctx->m);
if (ret)
@@ -497,7 +473,12 @@ build_auth_pack(krb5_context context,
free_DomainParameters(&dp);
return ret;
}
- ret = BN_to_integer(context, dh->q, &dp.q);
+ dp.q = calloc(1, sizeof(*dp.q));
+ if (dp.q == NULL) {
+ free_DomainParameters(&dp);
+ return ENOMEM;
+ }
+ ret = BN_to_integer(context, dh->q, dp.q);
if (ret) {
free_DomainParameters(&dp);
return ret;
@@ -533,74 +514,14 @@ build_auth_pack(krb5_context context,
return ret;
if (size != dhbuf.length)
krb5_abortx(context, "asn1 internal error");
+ a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
+ a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
} else if (ctx->keyex == USE_ECDH) {
-#ifdef HAVE_OPENSSL
- ECParameters ecp;
- unsigned char *p;
- int xlen;
-
- /* copy in public key, XXX find the best curve that the server support or use the clients curve if possible */
-
- ecp.element = choice_ECParameters_namedCurve;
- ret = der_copy_oid(&asn1_oid_id_ec_group_secp256r1,
- &ecp.u.namedCurve);
- if (ret)
- return ret;
-
- ALLOC(a->clientPublicValue->algorithm.parameters, 1);
- if (a->clientPublicValue->algorithm.parameters == NULL) {
- free_ECParameters(&ecp);
- return ENOMEM;
- }
- ASN1_MALLOC_ENCODE(ECParameters, p, xlen, &ecp, &size, ret);
- free_ECParameters(&ecp);
- if (ret)
- return ret;
- if ((int)size != xlen)
- krb5_abortx(context, "asn1 internal error");
-
- a->clientPublicValue->algorithm.parameters->data = p;
- a->clientPublicValue->algorithm.parameters->length = size;
-
- /* copy in public key */
-
- ret = der_copy_oid(&asn1_oid_id_ecPublicKey,
- &a->clientPublicValue->algorithm.algorithm);
- if (ret)
- return ret;
-
- ctx->u.eckey = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
- if (ctx->u.eckey == NULL)
- return ENOMEM;
-
- ret = EC_KEY_generate_key(ctx->u.eckey);
- if (ret != 1)
- return EINVAL;
-
- /* encode onto dhkey */
-
- xlen = i2o_ECPublicKey(ctx->u.eckey, NULL);
- if (xlen <= 0)
- abort();
-
- dhbuf.data = malloc(xlen);
- if (dhbuf.data == NULL)
- abort();
- dhbuf.length = xlen;
- p = dhbuf.data;
-
- xlen = i2o_ECPublicKey(ctx->u.eckey, &p);
- if (xlen <= 0)
- abort();
-
- /* XXX verify that this is right with RFC3279 */
-#else
- return EINVAL;
-#endif
+ ret = _krb5_build_authpack_subjectPK_EC(context, ctx, a);
+ if (ret)
+ return ret;
} else
krb5_abortx(context, "internal error");
- a->clientPublicValue->subjectPublicKey.length = dhbuf.length * 8;
- a->clientPublicValue->subjectPublicKey.data = dhbuf.data;
}
{
@@ -763,9 +684,7 @@ pk_mk_padata(krb5_context context,
req.trustedCertifiers = calloc(1, sizeof(*req.trustedCertifiers));
if (req.trustedCertifiers == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
free_PA_PK_AS_REQ(&req);
goto out;
}
@@ -980,9 +899,7 @@ get_reply_key_win(krb5_context context,
*key = malloc (sizeof (**key));
if (*key == NULL) {
free_ReplyKeyPack_Win2k(&key_pack);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = copy_EncryptionKey(&key_pack.replyKey, *key);
@@ -1045,9 +962,7 @@ get_reply_key(krb5_context context,
*key = malloc (sizeof (**key));
if (*key == NULL) {
free_ReplyKeyPack(&key_pack);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = copy_EncryptionKey(&key_pack.replyKey, *key);
@@ -1084,6 +999,7 @@ pk_verify_host(krb5_context context,
if (ctx->require_krbtgt_otherName) {
hx509_octet_string_list list;
size_t i;
+ int matched = 0;
ret = hx509_cert_find_subjectAltName_otherName(context->hx509ctx,
host->cert,
@@ -1098,7 +1014,14 @@ pk_verify_host(krb5_context context,
return ret;
}
- for (i = 0; i < list.len; i++) {
+ /*
+ * subjectAltNames are multi-valued, and a single KDC may serve
+ * multiple realms. The SAN validation here must accept
+ * the KDC's cert if *any* of the SANs match the expected KDC.
+ * It is OK for *some* of the SANs to not match, provided at least
+ * one does.
+ */
+ for (i = 0; matched == 0 && i < list.len; i++) {
KRB5PrincipalName r;
ret = decode_KRB5PrincipalName(list.val[i].data,
@@ -1114,22 +1037,22 @@ pk_verify_host(krb5_context context,
break;
}
- if (r.principalName.name_string.len != 2 ||
- strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) != 0 ||
- strcmp(r.principalName.name_string.val[1], realm) != 0 ||
- strcmp(r.realm, realm) != 0)
- {
- ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
- krb5_set_error_message(context, ret,
- N_("KDC have wrong realm name in "
- "the certificate", ""));
- }
+ if (r.principalName.name_string.len == 2 &&
+ strcmp(r.principalName.name_string.val[0], KRB5_TGS_NAME) == 0
+ && strcmp(r.principalName.name_string.val[1], realm) == 0
+ && strcmp(r.realm, realm) == 0)
+ matched = 1;
free_KRB5PrincipalName(&r);
- if (ret)
- break;
}
hx509_free_octet_string_list(&list);
+ if (matched == 0) {
+ ret = KRB5_KDC_ERR_INVALID_CERTIFICATE;
+ /* XXX: Lost in translation... */
+ krb5_set_error_message(context, ret,
+ N_("KDC have wrong realm name in "
+ "the certificate", ""));
+ }
}
if (ret)
return ret;
@@ -1210,8 +1133,10 @@ pk_rd_pa_reply_enckey(krb5_context context,
ret = der_put_length_and_tag (ptr + ph - 1, ph, content.length,
ASN1_C_UNIV, CONS, UT_Sequence, &l);
- if (ret)
+ if (ret) {
+ free(ptr);
return ret;
+ }
free(content.data);
content.data = ptr;
content.length += ph;
@@ -1422,8 +1347,7 @@ pk_rd_pa_reply_dh(krb5_context context,
dh_gen_key = malloc(size);
if (dh_gen_key == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
@@ -1442,53 +1366,11 @@ pk_rd_pa_reply_dh(krb5_context context,
}
} else {
-#ifdef HAVE_OPENSSL
- const EC_GROUP *group;
- EC_KEY *public = NULL;
-
- group = EC_KEY_get0_group(ctx->u.eckey);
-
- public = EC_KEY_new();
- if (public == NULL) {
- ret = ENOMEM;
- goto out;
- }
- if (EC_KEY_set_group(public, group) != 1) {
- EC_KEY_free(public);
- ret = ENOMEM;
- goto out;
- }
-
- if (o2i_ECPublicKey(&public, &p, size) == NULL) {
- EC_KEY_free(public);
- ret = KRB5KRB_ERR_GENERIC;
- krb5_set_error_message(context, ret,
- N_("PKINIT: Can't parse ECDH public key", ""));
- goto out;
- }
-
- size = (EC_GROUP_get_degree(group) + 7) / 8;
- dh_gen_key = malloc(size);
- if (dh_gen_key == NULL) {
- EC_KEY_free(public);
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
- goto out;
- }
- dh_gen_keylen = ECDH_compute_key(dh_gen_key, size,
- EC_KEY_get0_public_key(public), ctx->u.eckey, NULL);
- EC_KEY_free(public);
- if (dh_gen_keylen == -1) {
- ret = KRB5KRB_ERR_GENERIC;
- dh_gen_keylen = 0;
- krb5_set_error_message(context, ret,
- N_("PKINIT: Can't compute ECDH public key", ""));
- goto out;
- }
-#else
- ret = EINVAL;
-#endif
+ ret = _krb5_pk_rd_pa_reply_ecdh_compute_key(context, ctx, p,
+ size, &dh_gen_key,
+ &dh_gen_keylen);
+ if (ret)
+ goto out;
}
if (dh_gen_keylen <= 0) {
@@ -1501,9 +1383,7 @@ pk_rd_pa_reply_dh(krb5_context context,
*key = malloc (sizeof (**key));
if (*key == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
@@ -1855,11 +1735,8 @@ _krb5_pk_load_id(krb5_context context,
/* load cert */
id = calloc(1, sizeof(*id));
- if (id == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (id == NULL)
+ return krb5_enomem(context);
if (user_id) {
hx509_lock lock;
@@ -2028,7 +1905,7 @@ parse_integer(krb5_context context, char **p, const char *file, int lineno,
return 0;
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_parse_moduli_line(krb5_context context,
const char *file,
int lineno,
@@ -2042,11 +1919,8 @@ _krb5_parse_moduli_line(krb5_context context,
*m = NULL;
m1 = calloc(1, sizeof(*m1));
- if (m1 == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (m1 == NULL)
+ return krb5_enomem(context);
while (isspace((unsigned char)*p))
p++;
@@ -2065,8 +1939,7 @@ _krb5_parse_moduli_line(krb5_context context,
}
m1->name = strdup(p1);
if (m1->name == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memeory", ""));
+ ret = krb5_enomem(context);
goto out;
}
@@ -2108,7 +1981,7 @@ _krb5_parse_moduli_line(krb5_context context,
return ret;
}
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_free_moduli(struct krb5_dh_moduli **moduli)
{
int i;
@@ -2176,7 +2049,7 @@ static const char *default_moduli_rfc3526_MODP_group14 =
"EF15E5FB" "4AAC0B8C" "1CCAA4BE" "754AB572" "8AE9130C" "4C7D0288"
"0AB9472D" "45565534" "7FFFFFFF" "FFFFFFFF";
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_parse_moduli(krb5_context context, const char *file,
struct krb5_dh_moduli ***moduli)
{
@@ -2190,11 +2063,8 @@ _krb5_parse_moduli(krb5_context context, const char *file,
*moduli = NULL;
m = calloc(1, sizeof(m[0]) * 3);
- if (m == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (m == NULL)
+ return krb5_enomem(context);
strlcpy(buf, default_moduli_rfc3526_MODP_group14, sizeof(buf));
ret = _krb5_parse_moduli_line(context, "builtin", 1, buf, &m[0]);
@@ -2220,7 +2090,7 @@ _krb5_parse_moduli(krb5_context context, const char *file,
{
char * exp_file;
- if (_krb5_expand_path_tokens(context, file, &exp_file) == 0) {
+ if (_krb5_expand_path_tokens(context, file, 1, &exp_file) == 0) {
f = fopen(exp_file, "r");
krb5_xfree(exp_file);
} else {
@@ -2246,9 +2116,7 @@ _krb5_parse_moduli(krb5_context context, const char *file,
m2 = realloc(m, (n + 2) * sizeof(m[0]));
if (m2 == NULL) {
_krb5_free_moduli(m);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
m = m2;
@@ -2270,7 +2138,7 @@ _krb5_parse_moduli(krb5_context context, const char *file,
return 0;
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_dh_group_ok(krb5_context context, unsigned long bits,
heim_integer *p, heim_integer *g, heim_integer *q,
struct krb5_dh_moduli **moduli,
@@ -2324,10 +2192,8 @@ _krb5_get_init_creds_opt_free_pkinit(krb5_get_init_creds_opt *opt)
case USE_RSA:
break;
case USE_ECDH:
-#ifdef HAVE_OPENSSL
if (ctx->u.eckey)
- EC_KEY_free(ctx->u.eckey);
-#endif
+ _krb5_pk_eckey_free(ctx->u.eckey);
break;
}
if (ctx->id) {
@@ -2376,11 +2242,8 @@ krb5_get_init_creds_opt_set_pkinit(krb5_context context,
opt->opt_private->pk_init_ctx =
calloc(1, sizeof(*opt->opt_private->pk_init_ctx));
- if (opt->opt_private->pk_init_ctx == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (opt->opt_private->pk_init_ctx == NULL)
+ return krb5_enomem(context);
opt->opt_private->pk_init_ctx->require_binding = 0;
opt->opt_private->pk_init_ctx->require_eku = 1;
opt->opt_private->pk_init_ctx->require_krbtgt_otherName = 1;
diff --git a/lib/krb5/plugin.c b/lib/krb5/plugin.c
index 9303b6c615b7..03f64000f239 100644
--- a/lib/krb5/plugin.c
+++ b/lib/krb5/plugin.c
@@ -61,67 +61,6 @@ struct plugin {
static HEIMDAL_MUTEX plugin_mutex = HEIMDAL_MUTEX_INITIALIZER;
static struct plugin *registered = NULL;
-static int plugins_needs_scan = 1;
-
-static const char *sysplugin_dirs[] = {
- LIBDIR "/plugin/krb5",
-#ifdef __APPLE__
- "/System/Library/KerberosPlugins/KerberosFrameworkPlugins",
-#endif
- NULL
-};
-
-/*
- *
- */
-
-void *
-_krb5_plugin_get_symbol(struct krb5_plugin *p)
-{
- return p->symbol;
-}
-
-struct krb5_plugin *
-_krb5_plugin_get_next(struct krb5_plugin *p)
-{
- return p->next;
-}
-
-/*
- *
- */
-
-#ifdef HAVE_DLOPEN
-
-static krb5_error_code
-loadlib(krb5_context context, char *path)
-{
- struct plugin *e;
-
- e = calloc(1, sizeof(*e));
- if (e == NULL) {
- krb5_set_error_message(context, ENOMEM, "malloc: out of memory");
- free(path);
- return ENOMEM;
- }
-
-#ifndef RTLD_LAZY
-#define RTLD_LAZY 0
-#endif
-#ifndef RTLD_LOCAL
-#define RTLD_LOCAL 0
-#endif
- e->type = DSO;
- /* ignore error from dlopen, and just keep it as negative cache entry */
- e->u.dso.dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
- e->u.dso.path = path;
-
- e->next = registered;
- registered = e;
-
- return 0;
-}
-#endif /* HAVE_DLOPEN */
/**
* Register a plugin symbol name of specific type.
@@ -179,128 +118,6 @@ krb5_plugin_register(krb5_context context,
return 0;
}
-static int
-is_valid_plugin_filename(const char * n)
-{
- if (n[0] == '.' && (n[1] == '\0' || (n[1] == '.' && n[2] == '\0')))
- return 0;
-
-#ifdef _WIN32
- /* On Windows, we only attempt to load .dll files as plug-ins. */
- {
- const char * ext;
-
- ext = strrchr(n, '.');
- if (ext == NULL)
- return 0;
-
- return !stricmp(ext, ".dll");
- }
-#else
- return 1;
-#endif
-}
-
-static void
-trim_trailing_slash(char * path)
-{
- size_t l;
-
- l = strlen(path);
- while (l > 0 && (path[l - 1] == '/'
-#ifdef BACKSLASH_PATH_DELIM
- || path[l - 1] == '\\'
-#endif
- )) {
- path[--l] = '\0';
- }
-}
-
-static krb5_error_code
-load_plugins(krb5_context context)
-{
- struct plugin *e;
- krb5_error_code ret;
- char **dirs = NULL, **di;
- struct dirent *entry;
- char *path;
- DIR *d = NULL;
-
- if (!plugins_needs_scan)
- return 0;
- plugins_needs_scan = 0;
-
-#ifdef HAVE_DLOPEN
-
- dirs = krb5_config_get_strings(context, NULL, "libdefaults",
- "plugin_dir", NULL);
- if (dirs == NULL)
- dirs = rk_UNCONST(sysplugin_dirs);
-
- for (di = dirs; *di != NULL; di++) {
- char * dir = *di;
-
-#ifdef KRB5_USE_PATH_TOKENS
- if (_krb5_expand_path_tokens(context, *di, &dir))
- goto next_dir;
-#endif
-
- trim_trailing_slash(dir);
-
- d = opendir(dir);
-
- if (d == NULL)
- goto next_dir;
-
- rk_cloexec_dir(d);
-
- while ((entry = readdir(d)) != NULL) {
- char *n = entry->d_name;
-
- /* skip . and .. */
- if (!is_valid_plugin_filename(n))
- continue;
-
- path = NULL;
- ret = 0;
-#ifdef __APPLE__
- { /* support loading bundles on MacOS */
- size_t len = strlen(n);
- if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
- ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dir, n, (int)(len - 7), n);
- }
-#endif
- if (ret < 0 || path == NULL)
- ret = asprintf(&path, "%s/%s", dir, n);
-
- if (ret < 0 || path == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, "malloc: out of memory");
- return ret;
- }
-
- /* check if already tried */
- for (e = registered; e != NULL; e = e->next)
- if (e->type == DSO && strcmp(e->u.dso.path, path) == 0)
- break;
- if (e) {
- free(path);
- } else {
- loadlib(context, path); /* store or frees path */
- }
- }
- closedir(d);
-
- next_dir:
- if (dir != *di)
- free(dir);
- }
- if (dirs != rk_UNCONST(sysplugin_dirs))
- krb5_config_free_strings(dirs);
-#endif /* HAVE_DLOPEN */
- return 0;
-}
-
static krb5_error_code
add_symbol(krb5_context context, struct krb5_plugin **list, void *symbol)
{
@@ -317,7 +134,7 @@ add_symbol(krb5_context context, struct krb5_plugin **list, void *symbol)
return 0;
}
-krb5_error_code
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_find(krb5_context context,
enum krb5_plugin_type type,
const char *name,
@@ -330,8 +147,6 @@ _krb5_plugin_find(krb5_context context,
HEIMDAL_MUTEX_lock(&plugin_mutex);
- load_plugins(context);
-
for (ret = 0, e = registered; e != NULL; e = e->next) {
switch(e->type) {
case DSO: {
@@ -366,7 +181,7 @@ _krb5_plugin_find(krb5_context context,
return 0;
}
-void
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_plugin_free(struct krb5_plugin *list)
{
struct krb5_plugin *next;
@@ -404,8 +219,57 @@ plug_dealloc(void *ptr)
dlclose(p->dsohandle);
}
+static char *
+resolve_origin(const char *di)
+{
+#ifdef HAVE_DLADDR
+ Dl_info dl_info;
+ const char *dname;
+ char *path, *p;
+#endif
+
+ if (strncmp(di, "$ORIGIN/", sizeof("$ORIGIN/") - 1) &&
+ strcmp(di, "$ORIGIN"))
+ return strdup(di);
+
+#ifndef HAVE_DLADDR
+ return strdup(LIBDIR "/plugin/krb5");
+#else /* !HAVE_DLADDR */
+ di += sizeof("$ORIGIN") - 1;
+
+ if (dladdr(_krb5_load_plugins, &dl_info) == 0)
+ return strdup(LIBDIR "/plugin/krb5");
+
+ dname = dl_info.dli_fname;
+#ifdef _WIN32
+ p = strrchr(dname, '\\');
+ if (p == NULL)
+#endif
+ p = strrchr(dname, '/');
+ if (p) {
+ if (asprintf(&path, "%.*s%s", (int) (p - dname), dname, di) == -1)
+ return NULL;
+ } else {
+ if (asprintf(&path, "%s%s", dname, di) == -1)
+ return NULL;
+ }
+
+ return path;
+#endif /* !HAVE_DLADDR */
+}
+
-void
+/**
+ * Load plugins (new system) for the given module @name (typically
+ * "krb5") from the given directory @paths.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @name Name of plugin module (typically "krb5")
+ * @paths Array of directory paths where to look
+ */
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_load_plugins(krb5_context context, const char *name, const char **paths)
{
#ifdef HAVE_DLOPEN
@@ -414,7 +278,16 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
struct dirent *entry;
krb5_error_code ret;
const char **di;
+ char *dirname = NULL;
DIR *d;
+#ifdef _WIN32
+ const char * plugin_prefix;
+ size_t plugin_prefix_len;
+
+ if (asprintf(&plugin_prefix, "plugin_%s_", name) == -1)
+ return;
+ plugin_prefix_len = (plugin_prefix ? strlen(plugin_prefix) : 0);
+#endif
HEIMDAL_MUTEX_lock(&plugin_mutex);
@@ -434,12 +307,16 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
heim_release(s);
return;
}
- heim_dict_add_value(modules, s, module);
+ heim_dict_set_value(modules, s, module);
}
heim_release(s);
for (di = paths; *di != NULL; di++) {
- d = opendir(*di);
+ free(dirname);
+ dirname = resolve_origin(*di);
+ if (dirname == NULL)
+ continue;
+ d = opendir(dirname);
if (d == NULL)
continue;
rk_cloexec_dir(d);
@@ -455,15 +332,35 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
continue;
ret = 0;
+#ifdef _WIN32
+ /*
+ * On Windows, plugins must be loaded from the same directory as
+ * heimdal.dll (typically the assembly directory) and must have
+ * the name form "plugin_<module>_<name>.dll".
+ */
+ {
+ char *ext;
+
+ if (strnicmp(n, plugin_prefix, plugin_prefix_len))
+ continue;
+ ext = strrchr(n, '.');
+ if (ext == NULL || stricmp(ext, ".dll"))
+ continue;
+
+ ret = asprintf(&path, "%s\\%s", dirname, n);
+ if (ret < 0 || path == NULL)
+ continue;
+ }
+#endif
#ifdef __APPLE__
{ /* support loading bundles on MacOS */
size_t len = strlen(n);
if (len > 7 && strcmp(&n[len - 7], ".bundle") == 0)
- ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", *di, n, (int)(len - 7), n);
+ ret = asprintf(&path, "%s/%s/Contents/MacOS/%.*s", dirname, n, (int)(len - 7), n);
}
#endif
if (ret < 0 || path == NULL)
- ret = asprintf(&path, "%s/%s", *di, n);
+ ret = asprintf(&path, "%s/%s", dirname, n);
if (ret < 0 || path == NULL)
continue;
@@ -481,24 +378,32 @@ _krb5_load_plugins(krb5_context context, const char *name, const char **paths)
if (p)
p->dsohandle = dlopen(path, RTLD_LOCAL|RTLD_LAZY);
- if (p->dsohandle) {
+ if (p && p->dsohandle) {
p->path = heim_retain(spath);
p->names = heim_dict_create(11);
- heim_dict_add_value(module, spath, p);
+ heim_dict_set_value(module, spath, p);
}
}
+ heim_release(p);
heim_release(spath);
- heim_release(p);
free(path);
}
closedir(d);
}
- heim_release(module);
+ free(dirname);
HEIMDAL_MUTEX_unlock(&plugin_mutex);
+ heim_release(module);
+#ifdef _WIN32
+ if (plugin_prefix)
+ free(plugin_prefix);
+#endif
#endif /* HAVE_DLOPEN */
}
-void
+/**
+ * Unload plugins (new system)
+ */
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
_krb5_unload_plugins(krb5_context context, const char *name)
{
HEIMDAL_MUTEX_lock(&plugin_mutex);
@@ -537,14 +442,15 @@ struct iter_ctx {
heim_string_t n;
const char *name;
int min_version;
+ int flags;
heim_array_t result;
- krb5_error_code (*func)(krb5_context, const void *, void *, void *);
+ krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *);
void *userctx;
krb5_error_code ret;
};
static void
-search_modules(void *ctx, heim_object_t key, heim_object_t value)
+search_modules(heim_object_t key, heim_object_t value, void *ctx)
{
struct iter_ctx *s = ctx;
struct plugin2 *p = value;
@@ -565,19 +471,18 @@ search_modules(void *ctx, heim_object_t key, heim_object_t value)
if (ret)
cpm = pl->dataptr = NULL;
}
- heim_dict_add_value(p->names, s->n, pl);
+ heim_dict_set_value(p->names, s->n, pl);
} else {
cpm = pl->dataptr;
}
if (cpm && cpm->version >= s->min_version)
heim_array_append_value(s->result, pl);
-
heim_release(pl);
}
static void
-eval_results(heim_object_t value, void *ctx)
+eval_results(heim_object_t value, void *ctx, int *stop)
{
struct plug *pl = value;
struct iter_ctx *s = ctx;
@@ -586,50 +491,116 @@ eval_results(heim_object_t value, void *ctx)
return;
s->ret = s->func(s->context, pl->dataptr, pl->ctx, s->userctx);
+ if (s->ret != KRB5_PLUGIN_NO_HANDLE
+ && !(s->flags & KRB5_PLUGIN_INVOKE_ALL))
+ *stop = 1;
}
-krb5_error_code
+/**
+ * Run plugins for the given @module (e.g., "krb5") and @name (e.g.,
+ * "kuserok"). Specifically, the @func is invoked once per-plugin with
+ * four arguments: the @context, the plugin symbol value (a pointer to a
+ * struct whose first three fields are the same as struct common_plugin_method),
+ * a context value produced by the plugin's init method, and @userctx.
+ *
+ * @func should unpack arguments for a plugin function and invoke it
+ * with arguments taken from @userctx. @func should save plugin
+ * outputs, if any, in @userctx.
+ *
+ * All loaded and registered plugins are invoked via @func until @func
+ * returns something other than KRB5_PLUGIN_NO_HANDLE. Plugins that
+ * have nothing to do for the given arguments should return
+ * KRB5_PLUGIN_NO_HANDLE.
+ *
+ * Inputs:
+ *
+ * @context A krb5_context
+ * @module Name of module (typically "krb5")
+ * @name Name of pluggable interface (e.g., "kuserok")
+ * @min_version Lowest acceptable plugin minor version number
+ * @flags Flags (none defined at this time)
+ * @userctx Callback data for the callback function @func
+ * @func A callback function, invoked once per-plugin
+ *
+ * Outputs: None, other than the return value and such outputs as are
+ * gathered by @func.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
_krb5_plugin_run_f(krb5_context context,
const char *module,
const char *name,
int min_version,
int flags,
void *userctx,
- krb5_error_code (*func)(krb5_context, const void *, void *, void *))
+ krb5_error_code (KRB5_LIB_CALL *func)(krb5_context, const void *, void *, void *))
{
heim_string_t m = heim_string_create(module);
heim_dict_t dict;
+ void *plug_ctx;
+ struct common_plugin_method *cpm;
struct iter_ctx s;
+ struct krb5_plugin *registered_plugins = NULL;
+ struct krb5_plugin *p;
- HEIMDAL_MUTEX_lock(&plugin_mutex);
+ /* Get registered plugins */
+ (void) _krb5_plugin_find(context, SYMBOL, name, &registered_plugins);
- dict = heim_dict_copy_value(modules, m);
- heim_release(m);
- if (dict == NULL) {
- HEIMDAL_MUTEX_unlock(&plugin_mutex);
- return KRB5_PLUGIN_NO_HANDLE;
- }
+ HEIMDAL_MUTEX_lock(&plugin_mutex);
s.context = context;
s.name = name;
s.n = heim_string_create(name);
+ s.flags = flags;
s.min_version = min_version;
s.result = heim_array_create();
s.func = func;
s.userctx = userctx;
+ s.ret = KRB5_PLUGIN_NO_HANDLE;
- heim_dict_iterate_f(dict, search_modules, &s);
+ /* Get loaded plugins */
+ dict = heim_dict_copy_value(modules, m);
+ heim_release(m);
- heim_release(dict);
+ /* Add loaded plugins to s.result array */
+ if (dict)
+ heim_dict_iterate_f(dict, &s, search_modules);
+ /* We don't need to hold plugin_mutex during plugin invocation */
HEIMDAL_MUTEX_unlock(&plugin_mutex);
- s.ret = KRB5_PLUGIN_NO_HANDLE;
+ /* Invoke registered plugins (old system) */
+ for (p = registered_plugins; p; p = p->next) {
+ /*
+ * XXX This is the wrong way to handle registered plugins, as we
+ * call init/fini on each invocation! We do this because we
+ * have nowhere in the struct plugin registered list to store
+ * the context allocated by the plugin's init function. (But at
+ * least we do call init/fini!)
+ *
+ * What we should do is adapt the old plugin system to the new
+ * one and change how we register plugins so that we use the new
+ * struct plug to keep track of their context structures, that
+ * way we can init once, invoke many times, then fini.
+ */
+ cpm = (struct common_plugin_method *)p->symbol;
+ s.ret = cpm->init(context, &plug_ctx);
+ if (s.ret)
+ continue;
+ s.ret = s.func(s.context, p->symbol, plug_ctx, s.userctx);
+ cpm->fini(plug_ctx);
+ if (s.ret != KRB5_PLUGIN_NO_HANDLE &&
+ !(flags & KRB5_PLUGIN_INVOKE_ALL))
+ break;
+ }
+ _krb5_plugin_free(registered_plugins);
- heim_array_iterate_f(s.result, eval_results, &s);
+ /* Invoke loaded plugins (new system) */
+ if (s.ret == KRB5_PLUGIN_NO_HANDLE)
+ heim_array_iterate_f(s.result, &s, eval_results);
heim_release(s.result);
heim_release(s.n);
+ heim_release(dict);
return s.ret;
}
diff --git a/lib/krb5/principal.c b/lib/krb5/principal.c
index a10d2d079803..143dd6414b1e 100644
--- a/lib/krb5/principal.c
+++ b/lib/krb5/principal.c
@@ -35,8 +35,8 @@
* @page krb5_principal_intro The principal handing functions.
*
* A Kerberos principal is a email address looking string that
- * contains to parts separeted by a @. The later part is the kerbero
- * realm the principal belongs to and the former is a list of 0 or
+ * contains two parts separated by @. The second part is the kerberos
+ * realm the principal belongs to and the first is a list of 0 or
* more components. For example
* @verbatim
lha@SU.SE
@@ -63,6 +63,28 @@ host/admin@H5L.ORG
#define princ_ncomp(P, N) ((P)->name.name_string.val[(N)])
#define princ_realm(P) ((P)->realm)
+static krb5_error_code
+set_default_princ_type(krb5_principal p, NAME_TYPE defnt)
+{
+ if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), KRB5_TGS_NAME) == 0)
+ princ_type(p) = KRB5_NT_SRV_INST;
+ else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "host") == 0)
+ princ_type(p) = KRB5_NT_SRV_HST;
+ else if (princ_num_comp(p) > 1 && strcmp(princ_ncomp(p, 0), "kca_service") == 0)
+ princ_type(p) = KRB5_NT_SRV_HST;
+ else if (princ_num_comp(p) == 2 &&
+ strcmp(princ_ncomp(p, 0), KRB5_WELLKNOWN_NAME) == 0)
+ princ_type(p) = KRB5_NT_WELLKNOWN;
+ else if (princ_num_comp(p) == 1 && strchr(princ_ncomp(p, 0), '@') != NULL)
+ princ_type(p) = KRB5_NT_SMTP_NAME;
+ else
+ princ_type(p) = defnt;
+ return 0;
+}
+
+static krb5_error_code append_component(krb5_context, krb5_principal,
+ const char *, size_t);
+
/**
* Frees a Kerberos principal allocated by the library with
* krb5_parse_name(), krb5_make_principal() or any other related
@@ -203,99 +225,93 @@ krb5_parse_name_flags(krb5_context context,
char c;
int got_realm = 0;
int first_at = 1;
- int enterprise = (flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE);
+ int no_realm = flags & KRB5_PRINCIPAL_PARSE_NO_REALM;
+ int require_realm = flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
+ int enterprise = flags & KRB5_PRINCIPAL_PARSE_ENTERPRISE;
+ int ignore_realm = flags & KRB5_PRINCIPAL_PARSE_IGNORE_REALM;
+ int no_def_realm = flags & KRB5_PRINCIPAL_PARSE_NO_DEF_REALM;
*principal = NULL;
-#define RFLAGS (KRB5_PRINCIPAL_PARSE_NO_REALM|KRB5_PRINCIPAL_PARSE_REQUIRE_REALM)
-
- if ((flags & RFLAGS) == RFLAGS) {
+ if (no_realm && require_realm) {
krb5_set_error_message(context, KRB5_ERR_NO_SERVICE,
N_("Can't require both realm and "
"no realm at the same time", ""));
return KRB5_ERR_NO_SERVICE;
}
-#undef RFLAGS
/* count number of component,
* enterprise names only have one component
*/
ncomp = 1;
if (!enterprise) {
- for(p = name; *p; p++){
- if(*p=='\\'){
- if(!p[1]) {
+ for (p = name; *p; p++) {
+ if (*p=='\\') {
+ if (!p[1]) {
krb5_set_error_message(context, KRB5_PARSE_MALFORMED,
N_("trailing \\ in principal name", ""));
return KRB5_PARSE_MALFORMED;
}
p++;
- } else if(*p == '/')
+ } else if (*p == '/')
ncomp++;
- else if(*p == '@')
+ else if (*p == '@')
break;
}
}
comp = calloc(ncomp, sizeof(*comp));
- if (comp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (comp == NULL)
+ return krb5_enomem(context);
n = 0;
p = start = q = s = strdup(name);
if (start == NULL) {
- free (comp);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ free(comp);
+ return krb5_enomem(context);
}
- while(*p){
+ while (*p) {
c = *p++;
- if(c == '\\'){
+ if (c == '\\') {
c = *p++;
- if(c == 'n')
+ if (c == 'n')
c = '\n';
- else if(c == 't')
+ else if (c == 't')
c = '\t';
- else if(c == 'b')
+ else if (c == 'b')
c = '\b';
- else if(c == '0')
+ else if (c == '0')
c = '\0';
- else if(c == '\0') {
+ else if (c == '\0') {
ret = KRB5_PARSE_MALFORMED;
krb5_set_error_message(context, ret,
N_("trailing \\ in principal name", ""));
goto exit;
}
- }else if(enterprise && first_at) {
+ } else if (enterprise && first_at) {
if (c == '@')
first_at = 0;
- }else if((c == '/' && !enterprise) || c == '@'){
- if(got_realm){
+ } else if ((c == '/' && !enterprise) || c == '@') {
+ if (got_realm) {
ret = KRB5_PARSE_MALFORMED;
krb5_set_error_message(context, ret,
N_("part after realm in principal name", ""));
goto exit;
- }else{
+ } else {
comp[n] = malloc(q - start + 1);
if (comp[n] == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto exit;
}
memcpy(comp[n], start, q - start);
comp[n][q - start] = 0;
n++;
}
- if(c == '@')
+ if (c == '@')
got_realm = 1;
start = q;
continue;
}
- if(got_realm && (c == '/' || c == '\0')) {
+ if (got_realm && (c == '/' || c == '\0')) {
ret = KRB5_PARSE_MALFORMED;
krb5_set_error_message(context, ret,
N_("part after realm in principal name", ""));
@@ -303,71 +319,67 @@ krb5_parse_name_flags(krb5_context context,
}
*q++ = c;
}
- if(got_realm){
- if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
+ if (got_realm) {
+ if (no_realm) {
ret = KRB5_PARSE_MALFORMED;
krb5_set_error_message(context, ret,
N_("realm found in 'short' principal "
"expected to be without one", ""));
goto exit;
}
- realm = malloc(q - start + 1);
- if (realm == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
- goto exit;
+ if (!ignore_realm) {
+ realm = malloc(q - start + 1);
+ if (realm == NULL) {
+ ret = krb5_enomem(context);
+ goto exit;
+ }
+ memcpy(realm, start, q - start);
+ realm[q - start] = 0;
}
- memcpy(realm, start, q - start);
- realm[q - start] = 0;
- }else{
- if (flags & KRB5_PRINCIPAL_PARSE_REQUIRE_REALM) {
+ } else {
+ if (require_realm) {
ret = KRB5_PARSE_MALFORMED;
krb5_set_error_message(context, ret,
N_("realm NOT found in principal "
"expected to be with one", ""));
goto exit;
- } else if (flags & KRB5_PRINCIPAL_PARSE_NO_REALM) {
+ } else if (no_realm || no_def_realm) {
realm = NULL;
} else {
- ret = krb5_get_default_realm (context, &realm);
+ ret = krb5_get_default_realm(context, &realm);
if (ret)
goto exit;
}
comp[n] = malloc(q - start + 1);
if (comp[n] == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto exit;
}
memcpy(comp[n], start, q - start);
comp[n][q - start] = 0;
n++;
}
- *principal = malloc(sizeof(**principal));
+ *principal = calloc(1, sizeof(**principal));
if (*principal == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto exit;
}
- if (enterprise)
- (*principal)->name.name_type = KRB5_NT_ENTERPRISE_PRINCIPAL;
- else
- (*principal)->name.name_type = KRB5_NT_PRINCIPAL;
(*principal)->name.name_string.val = comp;
princ_num_comp(*principal) = n;
(*principal)->realm = realm;
+ if (enterprise)
+ princ_type(*principal) = KRB5_NT_ENTERPRISE_PRINCIPAL;
+ else
+ set_default_princ_type(*principal, KRB5_NT_PRINCIPAL);
free(s);
return 0;
exit:
- while(n>0){
+ while (n>0) {
free(comp[--n]);
}
free(comp);
- free(realm);
+ krb5_free_default_realm(context, realm);
free(s);
return ret;
}
@@ -394,7 +406,6 @@ krb5_parse_name(krb5_context context,
static const char quotable_chars[] = " \n\t\b\\/@";
static const char replace_chars[] = " ntb\\/@";
-static const char nq_chars[] = " \\/@";
#define add_char(BASE, INDEX, LEN, C) do { if((INDEX) < (LEN)) (BASE)[(INDEX)++] = (C); }while(0);
@@ -457,7 +468,7 @@ unparse_name_fixed(krb5_context context,
return ret;
if(strcmp(princ_realm(principal), r) != 0)
short_form = 0;
- free(r);
+ krb5_free_default_realm(context, r);
}
if(!short_form && !no_realm) {
add_char(name, idx, len, '@');
@@ -571,11 +582,8 @@ unparse_name(krb5_context context,
}
len++; /* '\0' */
*name = malloc(len);
- if(*name == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(*name == NULL)
+ return krb5_enomem(context);
ret = unparse_name_fixed(context, principal, *name, len, flags);
if(ret) {
free(*name);
@@ -668,12 +676,29 @@ krb5_principal_set_realm(krb5_context context,
if (princ_realm(principal))
free(princ_realm(principal));
- princ_realm(principal) = strdup(realm);
- if (princ_realm(principal) == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (realm == NULL)
+ princ_realm(principal) = NULL;
+ else if ((princ_realm(principal) = strdup(realm)) == NULL)
+ return krb5_enomem(context);
+ return 0;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_principal_set_comp_string(krb5_context context,
+ krb5_principal principal,
+ unsigned int k,
+ const char *component)
+{
+ char *s;
+ size_t i;
+
+ for (i = princ_num_comp(principal); i <= k; i++)
+ append_component(context, principal, "", 0);
+ s = strdup(component);
+ if (s == NULL)
+ return krb5_enomem(context);
+ free(princ_ncomp(principal, k));
+ princ_ncomp(principal, k) = s;
return 0;
}
@@ -721,6 +746,7 @@ krb5_build_principal(krb5_context context,
* @ingroup krb5_principal
*/
+/* coverity[+alloc : arg-*1] */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_make_principal(krb5_context context,
krb5_principal *principal,
@@ -740,7 +766,7 @@ krb5_make_principal(krb5_context context,
ret = krb5_build_principal_va(context, principal, strlen(realm), realm, ap);
va_end(ap);
if(r)
- free(r);
+ krb5_free_default_realm(context, r);
return ret;
}
@@ -753,48 +779,50 @@ append_component(krb5_context context, krb5_principal p,
size_t len = princ_num_comp(p);
tmp = realloc(princ_comp(p), (len + 1) * sizeof(*tmp));
- if(tmp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if(tmp == NULL)
+ return krb5_enomem(context);
princ_comp(p) = tmp;
princ_ncomp(p, len) = malloc(comp_len + 1);
- if (princ_ncomp(p, len) == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (princ_ncomp(p, len) == NULL)
+ return krb5_enomem(context);
memcpy (princ_ncomp(p, len), comp, comp_len);
princ_ncomp(p, len)[comp_len] = '\0';
princ_num_comp(p)++;
return 0;
}
-static void
+static krb5_error_code
va_ext_princ(krb5_context context, krb5_principal p, va_list ap)
{
- while(1){
+ krb5_error_code ret = 0;
+
+ while (1){
const char *s;
int len;
- len = va_arg(ap, int);
- if(len == 0)
+
+ if ((len = va_arg(ap, int)) == 0)
break;
s = va_arg(ap, const char*);
- append_component(context, p, s, len);
+ if ((ret = append_component(context, p, s, len)) != 0)
+ break;
}
+ return ret;
}
-static void
+static krb5_error_code
va_princ(krb5_context context, krb5_principal p, va_list ap)
{
- while(1){
+ krb5_error_code ret = 0;
+
+ while (1){
const char *s;
- s = va_arg(ap, const char*);
- if(s == NULL)
+
+ if ((s = va_arg(ap, const char*)) == NULL)
+ break;
+ if ((ret = append_component(context, p, s, strlen(s))) != 0)
break;
- append_component(context, p, s, strlen(s));
}
+ return ret;
}
static krb5_error_code
@@ -802,30 +830,30 @@ build_principal(krb5_context context,
krb5_principal *principal,
int rlen,
krb5_const_realm realm,
- void (*func)(krb5_context, krb5_principal, va_list),
+ krb5_error_code (*func)(krb5_context, krb5_principal, va_list),
va_list ap)
{
+ krb5_error_code ret;
krb5_principal p;
+ *principal = NULL;
p = calloc(1, sizeof(*p));
- if (p == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
- princ_type(p) = KRB5_NT_PRINCIPAL;
+ if (p == NULL)
+ return krb5_enomem(context);
princ_realm(p) = strdup(realm);
- if(p->realm == NULL){
+ if (p->realm == NULL) {
free(p);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
- (*func)(context, p, ap);
- *principal = p;
- return 0;
+ ret = func(context, p, ap);
+ if (ret == 0) {
+ *principal = p;
+ set_default_princ_type(p, KRB5_NT_PRINCIPAL);
+ } else
+ krb5_free_principal(context, p);
+ return ret;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
@@ -883,16 +911,11 @@ krb5_copy_principal(krb5_context context,
krb5_principal *outprinc)
{
krb5_principal p = malloc(sizeof(*p));
- if (p == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (p == NULL)
+ return krb5_enomem(context);
if(copy_Principal(inprinc, p)) {
free(p);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
*outprinc = p;
return 0;
@@ -965,7 +988,7 @@ krb5_principal_compare(krb5_context context,
krb5_const_principal princ1,
krb5_const_principal princ2)
{
- if(!krb5_realm_compare(context, princ1, princ2))
+ if (!krb5_realm_compare(context, princ1, princ2))
return FALSE;
return krb5_principal_compare_any_realm(context, princ1, princ2);
}
@@ -1013,32 +1036,21 @@ krb5_principal_match(krb5_context context,
return TRUE;
}
-/**
- * Create a principal for the service running on hostname. If
- * KRB5_NT_SRV_HST is used, the hostname is canonization using DNS (or
- * some other service), this is potentially insecure.
- *
- * @param context A Kerberos context.
- * @param hostname hostname to use
- * @param sname Service name to use
- * @param type name type of pricipal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
- * @param ret_princ return principal, free with krb5_free_principal().
- *
- * @return An krb5 error code, see krb5_get_error_message().
- *
- * @ingroup krb5_principal
+/*
+ * This is the original krb5_sname_to_principal(), renamed to be a
+ * helper of the new one.
*/
-
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_sname_to_principal (krb5_context context,
- const char *hostname,
- const char *sname,
- int32_t type,
- krb5_principal *ret_princ)
+static krb5_error_code
+krb5_sname_to_principal_old(krb5_context context,
+ const char *realm,
+ const char *hostname,
+ const char *sname,
+ int32_t type,
+ krb5_principal *ret_princ)
{
krb5_error_code ret;
char localhost[MAXHOSTNAMELEN];
- char **realms, *host = NULL;
+ char **realms = NULL, *host = NULL;
if(type != KRB5_NT_SRV_HST && type != KRB5_NT_UNKNOWN) {
krb5_set_error_message(context, KRB5_SNAME_UNSUPP_NAMETYPE,
@@ -1060,23 +1072,30 @@ krb5_sname_to_principal (krb5_context context,
if(sname == NULL)
sname = "host";
if(type == KRB5_NT_SRV_HST) {
- ret = krb5_expand_hostname_realms (context, hostname,
- &host, &realms);
+ if (realm)
+ ret = krb5_expand_hostname(context, hostname, &host);
+ else
+ ret = krb5_expand_hostname_realms(context, hostname,
+ &host, &realms);
if (ret)
return ret;
strlwr(host);
hostname = host;
- } else {
+ if (!realm)
+ realm = realms[0];
+ } else if (!realm) {
ret = krb5_get_host_realm(context, hostname, &realms);
if(ret)
return ret;
+ realm = realms[0];
}
- ret = krb5_make_principal(context, ret_princ, realms[0], sname,
+ ret = krb5_make_principal(context, ret_princ, realm, sname,
hostname, NULL);
if(host)
free(host);
- krb5_free_host_realm(context, realms);
+ if (realms)
+ krb5_free_host_realm(context, realms);
return ret;
}
@@ -1093,9 +1112,12 @@ static const struct {
{ "X500_PRINCIPAL", KRB5_NT_X500_PRINCIPAL },
{ "SMTP_NAME", KRB5_NT_SMTP_NAME },
{ "ENTERPRISE_PRINCIPAL", KRB5_NT_ENTERPRISE_PRINCIPAL },
+ { "WELLKNOWN", KRB5_NT_WELLKNOWN },
+ { "SRV_HST_DOMAIN", KRB5_NT_SRV_HST_DOMAIN },
{ "ENT_PRINCIPAL_AND_ID", KRB5_NT_ENT_PRINCIPAL_AND_ID },
{ "MS_PRINCIPAL", KRB5_NT_MS_PRINCIPAL },
{ "MS_PRINCIPAL_AND_ID", KRB5_NT_MS_PRINCIPAL_AND_ID },
+ { "SRV_HST_NEEDS_CANON", KRB5_NT_SRV_HST_NEEDS_CANON },
{ NULL, 0 }
};
@@ -1122,6 +1144,64 @@ krb5_parse_nametype(krb5_context context, const char *str, int32_t *nametype)
}
/**
+ * Returns true if name is Kerberos NULL name
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_null(krb5_context context, krb5_const_principal principal)
+{
+ if (principal->name.name_type == KRB5_NT_WELLKNOWN &&
+ principal->name.name_string.len == 2 &&
+ strcmp(principal->name.name_string.val[0], "WELLKNOWN") == 0 &&
+ strcmp(principal->name.name_string.val[1], "NULL") == 0)
+ return TRUE;
+ return FALSE;
+}
+
+const char _krb5_wellknown_lkdc[] = "WELLKNOWN:COM.APPLE.LKDC";
+static const char lkdc_prefix[] = "LKDC:";
+
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_realm_is_lkdc(const char *realm)
+{
+
+ return strncmp(realm, lkdc_prefix, sizeof(lkdc_prefix)-1) == 0 ||
+ strncmp(realm, _krb5_wellknown_lkdc, sizeof(_krb5_wellknown_lkdc) - 1) == 0;
+}
+
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_lkdc(krb5_context context, krb5_const_principal principal)
+{
+ return krb5_realm_is_lkdc(principal->realm);
+}
+
+/**
+ * Returns true if name is Kerberos an LKDC realm
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_pku2u(krb5_context context, krb5_const_principal principal)
+{
+ return strcmp(principal->realm, KRB5_PKU2U_REALM_NAME) == 0;
+}
+
+/**
* Check if the cname part of the principal is a krbtgt principal
*
* @ingroup krb5_principal
@@ -1132,5 +1212,873 @@ krb5_principal_is_krbtgt(krb5_context context, krb5_const_principal p)
{
return p->name.name_string.len == 2 &&
strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0;
+}
+
+/**
+ * Returns true iff name is an WELLKNOWN:ORG.H5L.HOSTBASED-SERVICE
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_gss_hostbased_service(krb5_context context,
+ krb5_const_principal principal)
+{
+ if (principal == NULL)
+ return FALSE;
+ if (principal->name.name_string.len != 2)
+ return FALSE;
+ if (strcmp(principal->name.name_string.val[1], KRB5_GSS_HOSTBASED_SERVICE_NAME) != 0)
+ return FALSE;
+ return TRUE;
+}
+
+/**
+ * Check if the cname part of the principal is a initial or renewed krbtgt principal
+ *
+ * @ingroup krb5_principal
+ */
+
+krb5_boolean KRB5_LIB_FUNCTION
+krb5_principal_is_root_krbtgt(krb5_context context, krb5_const_principal p)
+{
+ return p->name.name_string.len == 2 &&
+ strcmp(p->name.name_string.val[0], KRB5_TGS_NAME) == 0 &&
+ strcmp(p->name.name_string.val[1], p->realm) == 0;
+}
+
+static int
+tolower_ascii(int c)
+{
+ if (c >= 'A' || c <= 'Z')
+ return 'a' + (c - 'A');
+ return c;
+}
+
+typedef enum krb5_name_canon_rule_type {
+ KRB5_NCRT_BOGUS = 0,
+ KRB5_NCRT_AS_IS,
+ KRB5_NCRT_QUALIFY,
+ KRB5_NCRT_NSS
+} krb5_name_canon_rule_type;
+
+#ifdef UINT8_MAX
+#define MAXDOTS UINT8_MAX
+#else
+#define MAXDOTS (255U)
+#endif
+#ifdef UINT16_MAX
+#define MAXORDER UINT16_MAX
+#else
+#define MAXORDER (65535U)
+#endif
+
+struct krb5_name_canon_rule_data {
+ krb5_name_canon_rule_type type;
+ krb5_name_canon_rule_options options;
+ uint8_t mindots; /* match this many dots or more */
+ uint8_t maxdots; /* match no more than this many dots */
+ uint16_t explicit_order; /* given order */
+ uint16_t order; /* actual order */
+ char *match_domain; /* match this stem */
+ char *match_realm; /* match this realm */
+ char *domain; /* qualify with this domain */
+ char *realm; /* qualify with this realm */
+};
+
+/**
+ * Create a principal for the given service running on the given
+ * hostname. If KRB5_NT_SRV_HST is used, the hostname is canonicalized
+ * according the configured name canonicalization rules, with
+ * canonicalization delayed in some cases. One rule involves DNS, which
+ * is insecure unless DNSSEC is used, but we don't use DNSSEC-capable
+ * resolver APIs here, so that if DNSSEC is used we wouldn't know it.
+ *
+ * Canonicalization is immediate (not delayed) only when there is only
+ * one canonicalization rule and that rule indicates that we should do a
+ * host lookup by name (i.e., DNS).
+ *
+ * @param context A Kerberos context.
+ * @param hostname hostname to use
+ * @param sname Service name to use
+ * @param type name type of principal, use KRB5_NT_SRV_HST or KRB5_NT_UNKNOWN.
+ * @param ret_princ return principal, free with krb5_free_principal().
+ *
+ * @return An krb5 error code, see krb5_get_error_message().
+ *
+ * @ingroup krb5_principal
+ */
+
+/* coverity[+alloc : arg-*4] */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sname_to_principal(krb5_context context,
+ const char *hostname,
+ const char *sname,
+ int32_t type,
+ krb5_principal *ret_princ)
+{
+ char *realm, *remote_host;
+ krb5_error_code ret;
+ register char *cp;
+ char localname[MAXHOSTNAMELEN];
+
+ *ret_princ = NULL;
+
+ if ((type != KRB5_NT_UNKNOWN) &&
+ (type != KRB5_NT_SRV_HST))
+ return KRB5_SNAME_UNSUPP_NAMETYPE;
+
+ /* if hostname is NULL, use local hostname */
+ if (hostname == NULL) {
+ if (gethostname(localname, MAXHOSTNAMELEN))
+ return errno;
+ hostname = localname;
+ }
+
+ /* if sname is NULL, use "host" */
+ if (sname == NULL)
+ sname = "host";
+
+ remote_host = strdup(hostname);
+ if (remote_host == NULL)
+ return krb5_enomem(context);
+
+ if (type == KRB5_NT_SRV_HST) {
+ krb5_name_canon_rule rules;
+
+ /* Lower-case the hostname, because that's the convention */
+ for (cp = remote_host; *cp; cp++)
+ if (isupper((int) (*cp)))
+ *cp = tolower((int) (*cp));
+
+ /*
+ * If there is only one name canon rule and it says to
+ * canonicalize the old way, do that now, as we used to.
+ */
+ ret = _krb5_get_name_canon_rules(context, &rules);
+ if (ret) {
+ _krb5_debug(context, 5, "Failed to get name canon rules: ret = %d",
+ ret);
+ free(remote_host);
+ return ret;
+ }
+ if (rules[0].type == KRB5_NCRT_NSS &&
+ rules[1].type == KRB5_NCRT_BOGUS) {
+ _krb5_debug(context, 5, "Using nss for name canon immediately");
+ ret = krb5_sname_to_principal_old(context, rules[0].realm,
+ remote_host, sname,
+ KRB5_NT_SRV_HST, ret_princ);
+ free(remote_host);
+ return ret;
+ }
+ }
+
+ /* Remove trailing dots */
+ if (remote_host[0]) {
+ for (cp = remote_host + strlen(remote_host)-1;
+ *cp == '.' && cp > remote_host;
+ cp--) {
+ *cp = '\0';
+ }
+ }
+ realm = ""; /* "Referral realm" */
+
+ ret = krb5_build_principal(context, ret_princ, strlen(realm),
+ realm, sname, remote_host,
+ (char *)0);
+
+ if (ret == 0 && type == KRB5_NT_SRV_HST) {
+ /*
+ * Hostname canonicalization is done elsewhere (in
+ * krb5_get_credentials() and krb5_kt_get_entry()).
+ *
+ * We overload the name type to indicate to those functions that
+ * this principal name requires canonicalization.
+ *
+ * We can't use the empty realm to denote the need to
+ * canonicalize the hostname too: it would mean that users who
+ * want to assert knowledge of a service's realm must also know
+ * the canonical hostname, but in practice they don't.
+ */
+ (*ret_princ)->name.name_type = KRB5_NT_SRV_HST_NEEDS_CANON;
+
+ _krb5_debug(context, 5, "Building a delayed canon principal for %s/%s@",
+ sname, remote_host);
+ }
+
+ free(remote_host);
+ return ret;
+}
+
+static void
+tolower_str(char *s)
+{
+ for (; *s != '\0'; s++) {
+ if (isupper(*s))
+ *s = tolower_ascii(*s);
+ }
+}
+
+static krb5_error_code
+rule_parse_token(krb5_context context, krb5_name_canon_rule rule,
+ const char *tok)
+{
+ long int n;
+ int needs_type = rule->type == KRB5_NCRT_BOGUS;
+
+ /*
+ * Rules consist of a sequence of tokens, some of which indicate
+ * what type of rule the rule is, and some of which set rule options
+ * or ancilliary data. Last rule type token wins.
+ */
+
+ /* Rule type tokens: */
+ if (needs_type && strcmp(tok, "as-is") == 0) {
+ rule->type = KRB5_NCRT_AS_IS;
+ } else if (needs_type && strcmp(tok, "qualify") == 0) {
+ rule->type = KRB5_NCRT_QUALIFY;
+ } else if (needs_type && strcmp(tok, "nss") == 0) {
+ rule->type = KRB5_NCRT_NSS;
+ /* Rule options: */
+ } else if (strcmp(tok, "use_fast") == 0) {
+ rule->options |= KRB5_NCRO_USE_FAST;
+ } else if (strcmp(tok, "use_dnssec") == 0) {
+ rule->options |= KRB5_NCRO_USE_DNSSEC;
+ } else if (strcmp(tok, "ccache_only") == 0) {
+ rule->options |= KRB5_NCRO_GC_ONLY;
+ } else if (strcmp(tok, "no_referrals") == 0) {
+ rule->options |= KRB5_NCRO_NO_REFERRALS;
+ } else if (strcmp(tok, "use_referrals") == 0) {
+ rule->options &= ~KRB5_NCRO_NO_REFERRALS;
+ if (rule->realm == NULL) {
+ rule->realm = strdup("");
+ if (rule->realm == NULL)
+ return krb5_enomem(context);
+ }
+ } else if (strcmp(tok, "lookup_realm") == 0) {
+ rule->options |= KRB5_NCRO_LOOKUP_REALM;
+ free(rule->realm);
+ rule->realm = NULL;
+ /* Rule ancilliary data: */
+ } else if (strncmp(tok, "domain=", strlen("domain=")) == 0) {
+ free(rule->domain);
+ rule->domain = strdup(tok + strlen("domain="));
+ if (rule->domain == NULL)
+ return krb5_enomem(context);
+ tolower_str(rule->domain);
+ } else if (strncmp(tok, "realm=", strlen("realm=")) == 0) {
+ free(rule->realm);
+ rule->realm = strdup(tok + strlen("realm="));
+ if (rule->realm == NULL)
+ return krb5_enomem(context);
+ } else if (strncmp(tok, "match_domain=", strlen("match_domain=")) == 0) {
+ free(rule->match_domain);
+ rule->match_domain = strdup(tok + strlen("match_domain="));
+ if (rule->match_domain == NULL)
+ return krb5_enomem(context);
+ tolower_str(rule->match_domain);
+ } else if (strncmp(tok, "match_realm=", strlen("match_realm=")) == 0) {
+ free(rule->match_realm);
+ rule->match_realm = strdup(tok + strlen("match_realm="));
+ if (rule->match_realm == NULL)
+ return krb5_enomem(context);
+ } else if (strncmp(tok, "mindots=", strlen("mindots=")) == 0) {
+ errno = 0;
+ n = strtol(tok + strlen("mindots="), NULL, 10);
+ if (errno == 0 && n > 0 && n <= MAXDOTS)
+ rule->mindots = n;
+ } else if (strncmp(tok, "maxdots=", strlen("maxdots=")) == 0) {
+ errno = 0;
+ n = strtol(tok + strlen("maxdots="), NULL, 10);
+ if (errno == 0 && n > 0 && n <= MAXDOTS)
+ rule->maxdots = n;
+ } else if (strncmp(tok, "order=", strlen("order=")) == 0) {
+ errno = 0;
+ n = strtol(tok + strlen("order="), NULL, 10);
+ if (errno == 0 && n > 0 && n <= MAXORDER)
+ rule->explicit_order = n;
+ } else {
+ _krb5_debug(context, 5,
+ "Unrecognized name canonicalization rule token %s", tok);
+ return EINVAL;
+ }
+ return 0;
+}
+
+static int
+rule_cmp(const void *a, const void *b)
+{
+ krb5_const_name_canon_rule left = a;
+ krb5_const_name_canon_rule right = b;
+
+ if (left->type == KRB5_NCRT_BOGUS &&
+ right->type == KRB5_NCRT_BOGUS)
+ return 0;
+ if (left->type == KRB5_NCRT_BOGUS)
+ return 1;
+ if (right->type == KRB5_NCRT_BOGUS)
+ return -1;
+ if (left->explicit_order < right->explicit_order)
+ return -1;
+ if (left->explicit_order > right->explicit_order)
+ return 1;
+ return left->order - right->order;
+}
+
+static krb5_error_code
+parse_name_canon_rules(krb5_context context, char **rulestrs,
+ krb5_name_canon_rule *rules)
+{
+ krb5_error_code ret;
+ char *tok;
+ char *cp;
+ char **cpp;
+ size_t n;
+ size_t i, k;
+ int do_sort = 0;
+ krb5_name_canon_rule r;
+
+ *rules = NULL;
+
+ for (n =0, cpp = rulestrs; cpp != NULL && *cpp != NULL; cpp++)
+ n++;
+
+ n += 2; /* Always at least one rule; two for the default case */
+
+ if ((r = calloc(n, sizeof (*r))) == NULL)
+ return krb5_enomem(context);
+
+ for (k = 0; k < n; k++) {
+ r[k].type = KRB5_NCRT_BOGUS;
+ r[k].match_domain = NULL;
+ r[k].match_realm = NULL;
+ r[k].domain = NULL;
+ r[k].realm = NULL;
+ }
+
+ for (i = 0, k = 0; i < n && rulestrs != NULL && rulestrs[i] != NULL; i++) {
+ cp = rulestrs[i];
+ r[k].explicit_order = MAXORDER; /* mark order, see below */
+ r[k].maxdots = MAXDOTS;
+ r[k].order = k; /* default order */
+
+ /* Tokenize and parse value */
+ do {
+ tok = cp;
+ cp = strchr(cp, ':'); /* XXX use strtok_r() */
+ if (cp)
+ *cp++ = '\0'; /* delimit token */
+ ret = rule_parse_token(context, &r[k], tok);
+ if (ret == EINVAL) {
+ r[k].type = KRB5_NCRT_BOGUS;
+ break;
+ }
+ if (ret) {
+ _krb5_free_name_canon_rules(context, r);
+ return ret;
+ }
+ } while (cp && *cp);
+ if (r[k].explicit_order != MAXORDER)
+ do_sort = 1;
+
+ /* Validate parsed rule */
+ if (r[k].type == KRB5_NCRT_BOGUS ||
+ (r[k].type == KRB5_NCRT_QUALIFY && !r[k].domain) ||
+ (r[k].type == KRB5_NCRT_NSS && r[k].domain)) {
+ /* Invalid rule; mark it so and clean up */
+ r[k].type = KRB5_NCRT_BOGUS;
+ free(r[k].match_domain);
+ free(r[k].match_realm);
+ free(r[k].domain);
+ free(r[k].realm);
+ r[k].realm = NULL;
+ r[k].domain = NULL;
+ r[k].match_domain = NULL;
+ r[k].match_realm = NULL;
+ _krb5_debug(context, 5,
+ "Ignoring invalid name canonicalization rule %lu",
+ (unsigned long)i);
+ continue;
+ }
+ k++; /* good rule */
+ }
+
+ if (do_sort) {
+ /*
+ * Note that we make make this a stable sort by using appareance
+ * and explicit order.
+ */
+ qsort(r, n, sizeof(r[0]), rule_cmp);
+ }
+
+ if (r[0].type == KRB5_NCRT_BOGUS) {
+ /* No rules, or no valid rules */
+ r[0].type = KRB5_NCRT_NSS;
+ }
+
+ *rules = r;
+ return 0; /* We don't communicate bad rule errors here */
+}
+
+/*
+ * This exists only because the hostname canonicalization behavior in Heimdal
+ * (and other implementations of Kerberos) has been to use getaddrinfo(),
+ * unsafe though it is, for ages. We can't fix it in one day.
+ */
+static void
+make_rules_safe(krb5_context context, krb5_name_canon_rule rules)
+{
+ /*
+ * If the only rule were to use the name service (getaddrinfo()) then we're
+ * bound to fail. We could try to convert that rule to an as-is rule, but
+ * when we do get a validating resolver we'd be unhappy that we did such a
+ * conversion. Better let the user get failures and make them think about
+ * their naming rules.
+ */
+ if (rules == NULL)
+ return;
+ for (; rules[0].type != KRB5_NCRT_BOGUS; rules++) {
+ if (rules->type == KRB5_NCRT_NSS)
+ rules->options |= KRB5_NCRO_USE_DNSSEC;
+ else
+ rules->options |= KRB5_NCRO_USE_FAST;
+ }
+}
+
+/**
+ * This function returns an array of host-based service name
+ * canonicalization rules. The array of rules is organized as a list.
+ * See the definition of krb5_name_canon_rule.
+ *
+ * @param context A Kerberos context.
+ * @param rules Output location for array of rules.
+ */
+KRB5_LIB_FUNCTION krb5_error_code
+_krb5_get_name_canon_rules(krb5_context context, krb5_name_canon_rule *rules)
+{
+ krb5_error_code ret;
+ char **values = NULL;
+
+ *rules = context->name_canon_rules;
+ if (*rules != NULL)
+ return 0;
+
+ values = krb5_config_get_strings(context, NULL,
+ "libdefaults", "name_canon_rules", NULL);
+ ret = parse_name_canon_rules(context, values, rules);
+ krb5_config_free_strings(values);
+ if (ret)
+ return ret;
+
+ if (krb5_config_get_bool_default(context, NULL, FALSE,
+ "libdefaults", "safe_name_canon", NULL))
+ make_rules_safe(context, *rules);
+
+ heim_assert(rules != NULL && (*rules)[0].type != KRB5_NCRT_BOGUS,
+ "internal error in parsing principal name "
+ "canonicalization rules");
+
+ /* Memoize */
+ context->name_canon_rules = *rules;
+
+ return 0;
+}
+
+static krb5_error_code
+get_host_realm(krb5_context context, const char *hostname, char **realm)
+{
+ krb5_error_code ret;
+ char **hrealms = NULL;
+
+ *realm = NULL;
+ ret = krb5_get_host_realm(context, hostname, &hrealms);
+ if (ret)
+ return ret;
+ if (hrealms == NULL)
+ return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
+ if (hrealms[0] == NULL) {
+ krb5_free_host_realm(context, hrealms);
+ return KRB5_ERR_HOST_REALM_UNKNOWN; /* krb5_set_error() already done */
+ }
+ *realm = strdup(hrealms[0]);
+ krb5_free_host_realm(context, hrealms);
+ if (*realm == NULL)
+ return krb5_enomem(context);
+ return 0;
+}
+
+static int
+is_domain_suffix(const char *domain, const char *suffix)
+{
+ size_t dlen = strlen(domain);
+ size_t slen = strlen(suffix);
+
+ if (dlen < slen + 2)
+ return 0;
+
+ if (strcasecmp(domain + (dlen - slen), suffix) != 0)
+ return 0;
+
+ if (domain[(dlen - slen) - 1] != '.')
+ return 0;
+ return 1;
+}
+
+/*
+ * Applies a name canonicalization rule to a principal.
+ *
+ * Returns zero and no out_princ if the rule does not match.
+ * Returns zero and an out_princ if the rule does match.
+ */
+static krb5_error_code
+apply_name_canon_rule(krb5_context context, krb5_name_canon_rule rules,
+ size_t rule_idx, krb5_const_principal in_princ,
+ krb5_principal *out_princ,
+ krb5_name_canon_rule_options *rule_opts)
+{
+ krb5_name_canon_rule rule = &rules[rule_idx];
+ krb5_error_code ret;
+ unsigned int ndots = 0;
+ krb5_principal nss = NULL;
+ const char *sname = NULL;
+ const char *orig_hostname = NULL;
+ const char *new_hostname = NULL;
+ const char *new_realm = NULL;
+ const char *port = "";
+ const char *cp;
+ char *hostname_sans_port = NULL;
+ char *hostname_with_port = NULL;
+ char *tmp_hostname = NULL;
+ char *tmp_realm = NULL;
+
+ *out_princ = NULL; /* Signal no match */
+
+ if (rule_opts != NULL)
+ *rule_opts = rule->options;
+
+ if (rule->type == KRB5_NCRT_BOGUS)
+ return 0; /* rule doesn't apply */
+
+ sname = krb5_principal_get_comp_string(context, in_princ, 0);
+ orig_hostname = krb5_principal_get_comp_string(context, in_princ, 1);
+
+ /*
+ * Some apps want to use the very non-standard svc/hostname:port@REALM
+ * form. We do our best to support that here :(
+ */
+ port = strchr(orig_hostname, ':');
+ if (port != NULL) {
+ hostname_sans_port = strndup(orig_hostname, port - orig_hostname);
+ if (hostname_sans_port == NULL)
+ return krb5_enomem(context);
+ orig_hostname = hostname_sans_port;
+ }
+
+ _krb5_debug(context, 5, N_("Applying a name rule (type %d) to %s", ""),
+ rule->type, orig_hostname);
+
+ if (rule->mindots > 0 || rule->maxdots > 0) {
+ for (cp = strchr(orig_hostname, '.'); cp && *cp; cp = strchr(cp + 1, '.'))
+ ndots++;
+ }
+ if (rule->mindots > 0 && ndots < rule->mindots)
+ return 0;
+ if (ndots > rule->maxdots)
+ return 0;
+
+ if (rule->match_domain != NULL &&
+ !is_domain_suffix(orig_hostname, rule->match_domain))
+ return 0;
+
+ if (rule->match_realm != NULL &&
+ strcmp(rule->match_realm, in_princ->realm) != 0)
+ return 0;
+
+ new_realm = rule->realm;
+ switch (rule->type) {
+ case KRB5_NCRT_AS_IS:
+ break;
+
+ case KRB5_NCRT_QUALIFY:
+ heim_assert(rule->domain != NULL,
+ "missing domain for qualify name canon rule");
+ if (asprintf(&tmp_hostname, "%s.%s", orig_hostname,
+ rule->domain) == -1 || tmp_hostname == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ new_hostname = tmp_hostname;
+ break;
+
+ case KRB5_NCRT_NSS:
+ if ((rule->options & KRB5_NCRO_USE_DNSSEC)) {
+ ret = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN;
+ krb5_set_error_message(context, ret,
+ "Secure hostname resolution not supported");
+ goto out;
+ }
+ _krb5_debug(context, 5, "Using name service lookups");
+ ret = krb5_sname_to_principal_old(context, rule->realm,
+ orig_hostname, sname,
+ KRB5_NT_SRV_HST,
+ &nss);
+ if (rules[rule_idx + 1].type != KRB5_NCRT_BOGUS &&
+ (ret == KRB5_ERR_BAD_HOSTNAME ||
+ ret == KRB5_ERR_HOST_REALM_UNKNOWN)) {
+ /*
+ * Bad hostname / realm unknown -> rule inapplicable if
+ * there's more rules. If it's the last rule then we want
+ * to return all errors from krb5_sname_to_principal_old()
+ * here.
+ */
+ ret = 0;
+ goto out;
+ }
+ if (ret)
+ goto out;
+
+ new_hostname = krb5_principal_get_comp_string(context, nss, 1);
+ new_realm = krb5_principal_get_realm(context, nss);
+ break;
+
+ default:
+ /* Can't happen */
+ ret = 0;
+ goto out;
+ }
+
+ /*
+ * This rule applies.
+ *
+ * Copy in_princ and mutate the copy per the matched rule.
+ *
+ * This way we apply to principals with two or more components, such as
+ * domain-based names.
+ */
+ ret = krb5_copy_principal(context, in_princ, out_princ);
+ if (ret)
+ goto out;
+
+ if (new_realm == NULL && (rule->options & KRB5_NCRO_LOOKUP_REALM) != 0) {
+ ret = get_host_realm(context, new_hostname, &tmp_realm);
+ if (ret)
+ goto out;
+ new_realm = tmp_realm;
+ }
+
+ /* If we stripped off a :port, add it back in */
+ if (port != NULL && new_hostname != NULL) {
+ if (asprintf(&hostname_with_port, "%s%s", new_hostname, port) == -1 ||
+ hostname_with_port == NULL) {
+ ret = krb5_enomem(context);
+ goto out;
+ }
+ new_hostname = hostname_with_port;
+ }
+
+ if (new_realm != NULL)
+ krb5_principal_set_realm(context, *out_princ, new_realm);
+ if (new_hostname != NULL)
+ krb5_principal_set_comp_string(context, *out_princ, 1, new_hostname);
+ if (princ_type(*out_princ) == KRB5_NT_SRV_HST_NEEDS_CANON)
+ princ_type(*out_princ) = KRB5_NT_SRV_HST;
+
+ /* Trace rule application */
+ {
+ krb5_error_code ret2;
+ char *unparsed;
+
+ ret2 = krb5_unparse_name(context, *out_princ, &unparsed);
+ if (ret2) {
+ _krb5_debug(context, 5,
+ N_("Couldn't unparse canonicalized princicpal (%d)",
+ ""),
+ ret);
+ } else {
+ _krb5_debug(context, 5,
+ N_("Name canon rule application yields %s", ""),
+ unparsed);
+ free(unparsed);
+ }
+ }
+
+out:
+ free(hostname_sans_port);
+ free(hostname_with_port);
+ free(tmp_hostname);
+ free(tmp_realm);
+ krb5_free_principal(context, nss);
+ if (ret)
+ krb5_set_error_message(context, ret,
+ N_("Name canon rule application failed", ""));
+ return ret;
+}
+
+/**
+ * Free name canonicalization rules
+ */
+KRB5_LIB_FUNCTION void
+_krb5_free_name_canon_rules(krb5_context context, krb5_name_canon_rule rules)
+{
+ size_t k;
+
+ if (rules == NULL)
+ return;
+
+ for (k = 0; rules[k].type != KRB5_NCRT_BOGUS; k++) {
+ free(rules[k].match_domain);
+ free(rules[k].match_realm);
+ free(rules[k].domain);
+ free(rules[k].realm);
+ }
+ free(rules);
+}
+
+struct krb5_name_canon_iterator_data {
+ krb5_name_canon_rule rules;
+ krb5_const_principal in_princ; /* given princ */
+ krb5_const_principal out_princ; /* princ to be output */
+ krb5_principal tmp_princ; /* to be freed */
+ int is_trivial; /* no canon to be done */
+ int done; /* no more rules to be applied */
+ size_t cursor; /* current/next rule */
+};
+
+/**
+ * Initialize name canonicalization iterator.
+ *
+ * @param context Kerberos context
+ * @param in_princ principal name to be canonicalized OR
+ * @param iter output iterator object
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_name_canon_iterator_start(krb5_context context,
+ krb5_const_principal in_princ,
+ krb5_name_canon_iterator *iter)
+{
+ krb5_error_code ret;
+ krb5_name_canon_iterator state;
+
+ *iter = NULL;
+
+ state = calloc(1, sizeof (*state));
+ if (state == NULL)
+ return krb5_enomem(context);
+ state->in_princ = in_princ;
+
+ if (princ_type(state->in_princ) == KRB5_NT_SRV_HST_NEEDS_CANON) {
+ ret = _krb5_get_name_canon_rules(context, &state->rules);
+ if (ret)
+ goto out;
+ } else {
+ /* Name needs no canon -> trivial iterator: in_princ is canonical */
+ state->is_trivial = 1;
+ }
+
+ *iter = state;
+ return 0;
+
+out:
+ krb5_free_name_canon_iterator(context, state);
+ return krb5_enomem(context);
+}
+
+/*
+ * Helper for name canon iteration.
+ */
+static krb5_error_code
+name_canon_iterate(krb5_context context,
+ krb5_name_canon_iterator *iter,
+ krb5_name_canon_rule_options *rule_opts)
+{
+ krb5_error_code ret;
+ krb5_name_canon_iterator state = *iter;
+
+ if (rule_opts)
+ *rule_opts = 0;
+
+ if (state == NULL)
+ return 0;
+
+ if (state->done) {
+ krb5_free_name_canon_iterator(context, state);
+ *iter = NULL;
+ return 0;
+ }
+
+ if (state->is_trivial && !state->done) {
+ state->out_princ = state->in_princ;
+ state->done = 1;
+ return 0;
+ }
+
+ heim_assert(state->rules != NULL &&
+ state->rules[state->cursor].type != KRB5_NCRT_BOGUS,
+ "Internal error during name canonicalization");
+
+ do {
+ krb5_free_principal(context, state->tmp_princ);
+ ret = apply_name_canon_rule(context, state->rules, state->cursor,
+ state->in_princ, &state->tmp_princ, rule_opts);
+ if (ret) {
+ krb5_free_name_canon_iterator(context, state);
+ *iter = NULL;
+ return ret;
+ }
+ state->cursor++;
+ } while (state->tmp_princ == NULL &&
+ state->rules[state->cursor].type != KRB5_NCRT_BOGUS);
+
+ if (state->rules[state->cursor].type == KRB5_NCRT_BOGUS)
+ state->done = 1;
+
+ state->out_princ = state->tmp_princ;
+ if (state->tmp_princ == NULL) {
+ krb5_free_name_canon_iterator(context, state);
+ *iter = NULL;
+ return 0;
+ }
+ return 0;
+}
+
+/**
+ * Iteratively apply name canon rules, outputing a principal and rule
+ * options each time. Iteration completes when the @iter is NULL on
+ * return or when an error is returned. Callers must free the iterator
+ * if they abandon it mid-way.
+ *
+ * @param context Kerberos context
+ * @param iter name canon rule iterator (input/output)
+ * @param try_princ output principal name
+ * @param rule_opts output rule options
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_name_canon_iterate(krb5_context context,
+ krb5_name_canon_iterator *iter,
+ krb5_const_principal *try_princ,
+ krb5_name_canon_rule_options *rule_opts)
+{
+ krb5_error_code ret;
+
+ *try_princ = NULL;
+
+ ret = name_canon_iterate(context, iter, rule_opts);
+ if (*iter)
+ *try_princ = (*iter)->out_princ;
+ return ret;
+}
+
+/**
+ * Free a name canonicalization rule iterator.
+ */
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_free_name_canon_iterator(krb5_context context,
+ krb5_name_canon_iterator iter)
+{
+ if (iter == NULL)
+ return;
+ if (iter->tmp_princ)
+ krb5_free_principal(context, iter->tmp_princ);
+ free(iter);
}
diff --git a/lib/krb5/pseudo-random-test.c b/lib/krb5/pseudo-random-test.c
new file mode 100644
index 000000000000..4acf8082fbca
--- /dev/null
+++ b/lib/krb5/pseudo-random-test.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2001 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "krb5_locl.h"
+#include <err.h>
+
+enum { MAXSIZE = 48 };
+
+static struct testcase {
+ krb5_enctype enctype;
+ unsigned char constant[MAXSIZE];
+ size_t constant_len;
+ unsigned char key[MAXSIZE];
+ unsigned char res[MAXSIZE];
+} tests[] = {
+ {ETYPE_AES128_CTS_HMAC_SHA256_128, "test", 4,
+ {0x37, 0x05, 0xD9, 0x60, 0x80, 0xC1, 0x77, 0x28, 0xA0, 0xE8, 0x00, 0xEA, 0xB6, 0xE0, 0xD2, 0x3C},
+ {0x9D, 0x18, 0x86, 0x16, 0xF6, 0x38, 0x52, 0xFE, 0x86, 0x91, 0x5B, 0xB8, 0x40, 0xB4, 0xA8, 0x86,
+ 0xFF, 0x3E, 0x6B, 0xB0, 0xF8, 0x19, 0xB4, 0x9B, 0x89, 0x33, 0x93, 0xD3, 0x93, 0x85, 0x42, 0x95}},
+ {ETYPE_AES256_CTS_HMAC_SHA384_192, "test", 4,
+ {0x6D, 0x40, 0x4D, 0x37, 0xFA, 0xF7, 0x9F, 0x9D, 0xF0, 0xD3, 0x35, 0x68, 0xD3, 0x20, 0x66, 0x98,
+ 0x00, 0xEB, 0x48, 0x36, 0x47, 0x2E, 0xA8, 0xA0, 0x26, 0xD1, 0x6B, 0x71, 0x82, 0x46, 0x0C, 0x52},
+ {0x98, 0x01, 0xF6, 0x9A, 0x36, 0x8C, 0x2B, 0xF6, 0x75, 0xE5, 0x95, 0x21, 0xE1, 0x77, 0xD9, 0xA0,
+ 0x7F, 0x67, 0xEF, 0xE1, 0xCF, 0xDE, 0x8D, 0x3C, 0x8D, 0x6F, 0x6A, 0x02, 0x56, 0xE3, 0xB1, 0x7D,
+ 0xB3, 0xC1, 0xB6, 0x2A, 0xD1, 0xB8, 0x55, 0x33, 0x60, 0xD1, 0x73, 0x67, 0xEB, 0x15, 0x14, 0xD2}},
+ {0, {0}, 0, {0}, {0}}
+};
+
+int
+main(int argc, char **argv)
+{
+ struct testcase *t;
+ krb5_context context;
+ krb5_error_code ret;
+ int val = 0;
+
+ ret = krb5_init_context (&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ for (t = tests; t->enctype != 0; ++t) {
+ krb5_keyblock key;
+ krb5_crypto crypto;
+ krb5_data constant, prf;
+
+ krb5_data_zero(&prf);
+
+ key.keytype = t->enctype;
+ krb5_enctype_keysize(context, t->enctype, &key.keyvalue.length);
+ key.keyvalue.data = t->key;
+
+ ret = krb5_crypto_init(context, &key, 0, &crypto);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_crypto_init");
+
+ constant.data = t->constant;
+ constant.length = t->constant_len;
+
+ ret = krb5_crypto_prf(context, crypto, &constant, &prf);
+ if (ret)
+ krb5_err (context, 1, ret, "krb5_crypto_prf");
+
+ if (memcmp(prf.data, t->res, prf.length) != 0) {
+ const unsigned char *p = prf.data;
+ int i;
+
+ printf ("PRF failed (enctype %d)\n", t->enctype);
+ printf ("should be: ");
+ for (i = 0; i < prf.length; ++i)
+ printf ("%02x", t->res[i]);
+ printf ("\nresult was: ");
+ for (i = 0; i < prf.length; ++i)
+ printf ("%02x", p[i]);
+ printf ("\n");
+ val = 1;
+ }
+ krb5_data_free(&prf);
+ krb5_crypto_destroy(context, crypto);
+ }
+ krb5_free_context(context);
+
+ return val;
+}
diff --git a/lib/krb5/rd_cred.c b/lib/krb5/rd_cred.c
index c08547112b27..b9b18ee99846 100644
--- a/lib/krb5/rd_cred.c
+++ b/lib/krb5/rd_cred.c
@@ -96,7 +96,7 @@ krb5_rd_cred(krb5_context context,
goto out;
}
- if (cred.enc_part.etype == ETYPE_NULL) {
+ if (cred.enc_part.etype == (krb5_enctype)ETYPE_NULL) {
/* DK: MIT GSS-API Compatibility */
enc_krb_cred_part_data.length = cred.enc_part.cipher.length;
enc_krb_cred_part_data.data = cred.enc_part.cipher.data;
@@ -222,7 +222,7 @@ krb5_rd_cred(krb5_context context,
if (enc_krb_cred_part.timestamp == NULL ||
enc_krb_cred_part.usec == NULL ||
- abs(*enc_krb_cred_part.timestamp - sec)
+ labs(*enc_krb_cred_part.timestamp - sec)
> context->max_skew) {
krb5_clear_error_message (context);
ret = KRB5KRB_AP_ERR_SKEW;
@@ -249,9 +249,7 @@ krb5_rd_cred(krb5_context context,
sizeof(**ret_creds));
if (*ret_creds == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
@@ -261,9 +259,7 @@ krb5_rd_cred(krb5_context context,
creds = calloc(1, sizeof(*creds));
if(creds == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret,
- N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
diff --git a/lib/krb5/rd_priv.c b/lib/krb5/rd_priv.c
index 8a46195b694f..3e49dd8b4634 100644
--- a/lib/krb5/rd_priv.c
+++ b/lib/krb5/rd_priv.c
@@ -136,7 +136,7 @@ krb5_rd_priv(krb5_context context,
krb5_timeofday (context, &sec);
if (part.timestamp == NULL ||
part.usec == NULL ||
- abs(*part.timestamp - sec) > context->max_skew) {
+ labs(*part.timestamp - sec) > context->max_skew) {
krb5_clear_error_message (context);
ret = KRB5KRB_AP_ERR_SKEW;
goto failure_part;
diff --git a/lib/krb5/rd_rep.c b/lib/krb5/rd_rep.c
index 391d81c191b8..9968db35af21 100644
--- a/lib/krb5/rd_rep.c
+++ b/lib/krb5/rd_rep.c
@@ -45,6 +45,7 @@ krb5_rd_rep(krb5_context context,
krb5_data data;
krb5_crypto crypto;
+ *repl = NULL;
krb5_data_zero (&data);
ret = decode_AP_REP(inbuf->data, inbuf->length, &ap_rep, &len);
@@ -64,35 +65,32 @@ krb5_rd_rep(krb5_context context,
ret = krb5_crypto_init(context, auth_context->keyblock, 0, &crypto);
if (ret)
goto out;
- ret = krb5_decrypt_EncryptedData (context,
- crypto,
- KRB5_KU_AP_REQ_ENC_PART,
- &ap_rep.enc_part,
- &data);
+ ret = krb5_decrypt_EncryptedData(context,
+ crypto,
+ KRB5_KU_AP_REQ_ENC_PART,
+ &ap_rep.enc_part,
+ &data);
krb5_crypto_destroy(context, crypto);
if (ret)
goto out;
*repl = malloc(sizeof(**repl));
if (*repl == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
ret = decode_EncAPRepPart(data.data, data.length, *repl, &len);
if (ret) {
krb5_set_error_message(context, ret, N_("Failed to decode EncAPRepPart", ""));
- return ret;
+ goto out;
}
if (auth_context->flags & KRB5_AUTH_CONTEXT_DO_TIME) {
if ((*repl)->ctime != auth_context->authenticator->ctime ||
(*repl)->cusec != auth_context->authenticator->cusec)
{
- krb5_free_ap_rep_enc_part(context, *repl);
- *repl = NULL;
ret = KRB5KRB_AP_ERR_MUT_FAIL;
- krb5_clear_error_message (context);
+ krb5_clear_error_message(context);
goto out;
}
}
@@ -103,8 +101,12 @@ krb5_rd_rep(krb5_context context,
krb5_auth_con_setremotesubkey(context, auth_context, (*repl)->subkey);
out:
- krb5_data_free (&data);
- free_AP_REP (&ap_rep);
+ if (ret) {
+ krb5_free_ap_rep_enc_part(context, *repl);
+ *repl = NULL;
+ }
+ krb5_data_free(&data);
+ free_AP_REP(&ap_rep);
return ret;
}
diff --git a/lib/krb5/rd_req.c b/lib/krb5/rd_req.c
index 21daeb596b55..fbced144e723 100644
--- a/lib/krb5/rd_req.c
+++ b/lib/krb5/rd_req.c
@@ -173,50 +173,17 @@ find_etypelist(krb5_context context,
EtypeList *etypes)
{
krb5_error_code ret;
- krb5_authdata *ad;
- krb5_authdata adIfRelevant;
- unsigned i;
-
- memset(&adIfRelevant, 0, sizeof(adIfRelevant));
-
- etypes->len = 0;
- etypes->val = NULL;
-
- ad = auth_context->authenticator->authorization_data;
- if (ad == NULL)
- return 0;
-
- for (i = 0; i < ad->len; i++) {
- if (ad->val[i].ad_type == KRB5_AUTHDATA_IF_RELEVANT) {
- ret = decode_AD_IF_RELEVANT(ad->val[i].ad_data.data,
- ad->val[i].ad_data.length,
- &adIfRelevant,
- NULL);
- if (ret)
- return ret;
-
- if (adIfRelevant.len == 1 &&
- adIfRelevant.val[0].ad_type ==
- KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION) {
- break;
- }
- free_AD_IF_RELEVANT(&adIfRelevant);
- adIfRelevant.len = 0;
- }
- }
-
- if (adIfRelevant.len == 0)
- return 0;
-
- ret = decode_EtypeList(adIfRelevant.val[0].ad_data.data,
- adIfRelevant.val[0].ad_data.length,
- etypes,
- NULL);
+ krb5_data data;
+
+ ret = _krb5_get_ad(context, auth_context->authenticator->authorization_data, NULL, KRB5_AUTHDATA_GSS_API_ETYPE_NEGOTIATION, &data);
if (ret)
- krb5_clear_error_message(context);
-
- free_AD_IF_RELEVANT(&adIfRelevant);
-
+ return 0;
+
+ ret = decode_EtypeList(data.data, data.length, etypes, NULL);
+ krb5_data_free(&data);
+ if (ret)
+ krb5_clear_error_message(context);
+
return ret;
}
@@ -276,33 +243,26 @@ krb5_verify_authenticator_checksum(krb5_context context,
size_t len)
{
krb5_error_code ret;
- krb5_keyblock *key;
+ krb5_keyblock *key = NULL;
krb5_authenticator authenticator;
krb5_crypto crypto;
- ret = krb5_auth_con_getauthenticator (context,
- ac,
- &authenticator);
- if(ret)
+ ret = krb5_auth_con_getauthenticator(context, ac, &authenticator);
+ if (ret)
return ret;
- if(authenticator->cksum == NULL) {
- krb5_free_authenticator(context, &authenticator);
- return -17;
+ if (authenticator->cksum == NULL) {
+ ret = -17;
+ goto out;
}
ret = krb5_auth_con_getkey(context, ac, &key);
- if(ret) {
- krb5_free_authenticator(context, &authenticator);
- return ret;
- }
+ if (ret)
+ goto out;
ret = krb5_crypto_init(context, key, 0, &crypto);
- if(ret)
+ if (ret)
goto out;
- ret = krb5_verify_checksum (context,
- crypto,
- KRB5_KU_AP_REQ_AUTH_CKSUM,
- data,
- len,
- authenticator->cksum);
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_AP_REQ_AUTH_CKSUM,
+ data, len, authenticator->cksum);
krb5_crypto_destroy(context, crypto);
out:
krb5_free_authenticator(context, &authenticator);
@@ -348,6 +308,8 @@ krb5_verify_ap_req2(krb5_context context,
krb5_error_code ret;
EtypeList etypes;
+ memset(&etypes, 0, sizeof(etypes));
+
if (ticket)
*ticket = NULL;
@@ -361,8 +323,7 @@ krb5_verify_ap_req2(krb5_context context,
t = calloc(1, sizeof(*t));
if (t == NULL) {
- ret = ENOMEM;
- krb5_clear_error_message (context);
+ ret = krb5_enomem(context);
goto out;
}
@@ -441,7 +402,7 @@ krb5_verify_ap_req2(krb5_context context,
krb5_timeofday (context, &now);
- if (abs(ac->authenticator->ctime - now) > context->max_skew) {
+ if (labs(ac->authenticator->ctime - now) > context->max_skew) {
ret = KRB5KRB_AP_ERR_SKEW;
krb5_clear_error_message (context);
goto out;
@@ -484,7 +445,7 @@ krb5_verify_ap_req2(krb5_context context,
if (ap_req_options) {
*ap_req_options = 0;
- if (ac->keytype != ETYPE_NULL)
+ if (ac->keytype != (krb5_enctype)ETYPE_NULL)
*ap_req_options |= AP_OPTS_USE_SUBKEY;
if (ap_req->ap_options.use_session_key)
*ap_req_options |= AP_OPTS_USE_SESSION_KEY;
@@ -504,6 +465,7 @@ krb5_verify_ap_req2(krb5_context context,
free_EtypeList(&etypes);
return 0;
out:
+ free_EtypeList(&etypes);
if (t)
krb5_free_ticket (context, t);
if (auth_context == NULL || *auth_context == NULL)
@@ -545,11 +507,8 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req_in_ctx_alloc(krb5_context context, krb5_rd_req_in_ctx *ctx)
{
*ctx = calloc(1, sizeof(**ctx));
- if (*ctx == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*ctx == NULL)
+ return krb5_enomem(context);
(*ctx)->check_pac = (context->flags & KRB5_CTX_F_CHECK_PAC) ? 1 : 0;
return 0;
}
@@ -680,10 +639,22 @@ krb5_rd_req_out_ctx_free(krb5_context context, krb5_rd_req_out_ctx ctx)
free(ctx);
}
-/*
+/**
+ * Process an AP_REQ message.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context authentication context of the peer.
+ * @param inbuf the AP_REQ message, obtained for example with krb5_read_message().
+ * @param server server principal.
+ * @param keytab server keytab.
+ * @param ap_req_options set to the AP_REQ options. See the AP_OPTS_* defines.
+ * @param ticket on success, set to the authenticated client credentials.
+ * Must be deallocated with krb5_free_ticket(). If not
+ * interested, pass a NULL value.
*
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
*/
-
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_rd_req(krb5_context context,
krb5_auth_context *auth_context,
@@ -822,12 +793,12 @@ out:
* default values for the authentication context will used.
* @param inbuf the (AP-REQ) authentication buffer
*
- * @param server the server with authenticate as, if NULL the function
+ * @param server the server to authenticate to. If NULL the function
* will try to find any available credential in the keytab
* that will verify the reply. The function will prefer the
- * server the server client specified in the AP-REQ, but if
+ * server specified in the AP-REQ, but if
* there is no mach, it will try all keytab entries for a
- * match. This have serious performance issues for larger keytabs.
+ * match. This has serious performance issues for large keytabs.
*
* @param inctx control the behavior of the function, if NULL, the
* default behavior is used.
@@ -854,11 +825,8 @@ krb5_rd_req_ctx(krb5_context context,
*outctx = NULL;
o = calloc(1, sizeof(*o));
- if (o == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (o == NULL)
+ return krb5_enomem(context);
if (*auth_context == NULL) {
ret = krb5_auth_con_init(context, auth_context);
@@ -870,7 +838,7 @@ krb5_rd_req_ctx(krb5_context context,
if(ret)
goto out;
- /* Save that principal that was in the request */
+ /* Save the principal that was in the request */
ret = _krb5_principalname2krb5_principal(context,
&o->server,
ap_req.ticket.sname,
@@ -982,7 +950,7 @@ krb5_rd_req_ctx(krb5_context context,
_krb5_kt_principal_not_found(context, ret, id, o->server,
ap_req.ticket.enc_part.etype,
kvno);
- goto out;
+ break;
}
if (entry.keyblock.keytype != ap_req.ticket.enc_part.etype) {
@@ -1015,13 +983,13 @@ krb5_rd_req_ctx(krb5_context context,
&o->keyblock);
if (ret) {
krb5_kt_free_entry (context, &entry);
- goto out;
+ break;
}
ret = krb5_copy_principal(context, entry.principal, &p);
if (ret) {
krb5_kt_free_entry (context, &entry);
- goto out;
+ break;
}
krb5_free_principal(context, o->ticket->server);
o->ticket->server = p;
@@ -1031,6 +999,8 @@ krb5_rd_req_ctx(krb5_context context,
done = 1;
}
krb5_kt_end_seq_get (context, id, &cursor);
+ if (ret)
+ goto out;
}
/* If there is a PAC, verify its server signature */
diff --git a/lib/krb5/rd_safe.c b/lib/krb5/rd_safe.c
index e62906b1f723..cfe1e9829c9c 100644
--- a/lib/krb5/rd_safe.c
+++ b/lib/krb5/rd_safe.c
@@ -159,7 +159,7 @@ krb5_rd_safe(krb5_context context,
if (safe.safe_body.timestamp == NULL ||
safe.safe_body.usec == NULL ||
- abs(*safe.safe_body.timestamp - sec) > context->max_skew) {
+ labs(*safe.safe_body.timestamp - sec) > context->max_skew) {
ret = KRB5KRB_AP_ERR_SKEW;
krb5_clear_error_message (context);
goto failure;
@@ -191,8 +191,7 @@ krb5_rd_safe(krb5_context context,
outbuf->length = safe.safe_body.user_data.length;
outbuf->data = malloc(outbuf->length);
if (outbuf->data == NULL && outbuf->length != 0) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
krb5_data_zero(outbuf);
goto failure;
}
diff --git a/lib/krb5/recvauth.c b/lib/krb5/recvauth.c
index 78e98a10fc1b..785c4c9c5f1b 100644
--- a/lib/krb5/recvauth.c
+++ b/lib/krb5/recvauth.c
@@ -43,6 +43,24 @@ match_exact(const void *data, const char *appl_version)
return strcmp(data, appl_version) == 0;
}
+/**
+ * Perform the server side of the sendauth protocol.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context authentication context of the peer.
+ * @param p_fd socket associated to the connection.
+ * @param appl_version server-specific string.
+ * @param server server principal.
+ * @param flags if KRB5_RECVAUTH_IGNORE_VERSION is set, skip the sendauth version
+ * part of the protocol.
+ * @param keytab server keytab.
+ * @param ticket on success, set to the authenticated client credentials.
+ * Must be deallocated with krb5_free_ticket(). If not
+ * interested, pass a NULL value.
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_recvauth(krb5_context context,
krb5_auth_context *auth_context,
@@ -59,6 +77,11 @@ krb5_recvauth(krb5_context context,
keytab, ticket);
}
+/**
+ * Perform the server side of the sendauth protocol like krb5_recvauth(), but support
+ * a user-specified callback, \a match_appl_version, to perform the match of the application
+ * version \a match_data.
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_recvauth_match_version(krb5_context context,
krb5_auth_context *auth_context,
@@ -97,10 +120,13 @@ krb5_recvauth_match_version(krb5_context context,
if (ret)
return ret;
+ /*
+ * Expect SENDAUTH protocol version.
+ */
if(!(flags & KRB5_RECVAUTH_IGNORE_VERSION)) {
n = krb5_net_read (context, p_fd, &len, 4);
if (n < 0) {
- ret = errno;
+ ret = errno ? errno : EINVAL;
krb5_set_error_message(context, ret, "read: %s", strerror(ret));
return ret;
}
@@ -120,9 +146,12 @@ krb5_recvauth_match_version(krb5_context context,
}
}
+ /*
+ * Expect application protocol version.
+ */
n = krb5_net_read (context, p_fd, &len, 4);
if (n < 0) {
- ret = errno;
+ ret = errno ? errno : EINVAL;
krb5_set_error_message(context, ret, "read: %s", strerror(ret));
return ret;
}
@@ -135,29 +164,38 @@ krb5_recvauth_match_version(krb5_context context,
if (her_appl_version == NULL) {
repl = 2;
krb5_net_write (context, p_fd, &repl, 1);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
if (krb5_net_read (context, p_fd, her_appl_version, len) != len
|| !(*match_appl_version)(match_data, her_appl_version)) {
repl = 2;
krb5_net_write (context, p_fd, &repl, 1);
krb5_set_error_message(context, KRB5_SENDAUTH_BADAPPLVERS,
- N_("wrong sendauth version (%s)", ""),
+ N_("wrong sendauth application version (%s)", ""),
her_appl_version);
free (her_appl_version);
return KRB5_SENDAUTH_BADAPPLVERS;
}
free (her_appl_version);
+ /*
+ * Send OK.
+ */
repl = 0;
if (krb5_net_write (context, p_fd, &repl, 1) != 1) {
- ret = errno;
+ ret = errno ? errno : EINVAL;
krb5_set_error_message(context, ret, "write: %s", strerror(ret));
return ret;
}
+ /*
+ * Until here, the fields in the message were in cleartext and unauthenticated.
+ * From now on, Kerberos kicks in.
+ */
+
+ /*
+ * Expect AP_REQ.
+ */
krb5_data_zero (&data);
ret = krb5_read_message (context, p_fd, &data);
if (ret)
@@ -191,15 +229,21 @@ krb5_recvauth_match_version(krb5_context context,
return ret;
}
+ /*
+ * Send OK.
+ */
len = 0;
if (krb5_net_write (context, p_fd, &len, 4) != 4) {
- ret = errno;
+ ret = errno ? errno : EINVAL;
krb5_set_error_message(context, ret, "write: %s", strerror(ret));
krb5_free_ticket(context, *ticket);
*ticket = NULL;
return ret;
}
+ /*
+ * If client requires mutual authentication, send AP_REP.
+ */
if (ap_options & AP_OPTS_MUTUAL_REQUIRED) {
ret = krb5_mk_rep (context, *auth_context, &data);
if (ret) {
diff --git a/lib/krb5/replay.c b/lib/krb5/replay.c
index 965dd44437d9..6257311676f6 100644
--- a/lib/krb5/replay.c
+++ b/lib/krb5/replay.c
@@ -139,6 +139,7 @@ krb5_rc_initialize(krb5_context context,
krb5_set_error_message(context, ret, "open(%s): %s", id->name, buf);
return ret;
}
+ memset(&tmp, 0, sizeof(tmp));
tmp.stamp = auth_lifespan;
fwrite(&tmp, 1, sizeof(tmp), f);
fclose(f);
@@ -205,6 +206,7 @@ krb5_rc_store(krb5_context context,
time_t t;
FILE *f;
int ret;
+ size_t count;
ent.stamp = time(NULL);
checksum_authenticator(rep, ent.data);
@@ -217,7 +219,9 @@ krb5_rc_store(krb5_context context,
return ret;
}
rk_cloexec_file(f);
- fread(&tmp, sizeof(ent), 1, f);
+ count = fread(&tmp, sizeof(ent), 1, f);
+ if(count != 1)
+ return KRB5_RC_IO_UNKNOWN;
t = ent.stamp - tmp.stamp;
while(fread(&tmp, sizeof(ent), 1, f)){
if(tmp.stamp < t)
@@ -301,11 +305,8 @@ krb5_get_server_rcache(krb5_context context,
char *tmp = malloc(4 * piece->length + 1);
char *name;
- if(tmp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (tmp == NULL)
+ return krb5_enomem(context);
strvisx(tmp, piece->data, piece->length, VIS_WHITE | VIS_OCTAL);
#ifdef HAVE_GETEUID
ret = asprintf(&name, "FILE:rc_%s_%u", tmp, (unsigned)geteuid());
@@ -313,11 +314,8 @@ krb5_get_server_rcache(krb5_context context,
ret = asprintf(&name, "FILE:rc_%s", tmp);
#endif
free(tmp);
- if(ret < 0 || name == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ret < 0 || name == NULL)
+ return krb5_enomem(context);
ret = krb5_rc_resolve_full(context, &rcache, name);
free(name);
diff --git a/lib/krb5/salt-aes.c b/lib/krb5/salt-aes-sha1.c
index 32dafd68cb88..edd066df404c 100644
--- a/lib/krb5/salt-aes.c
+++ b/lib/krb5/salt-aes-sha1.c
@@ -33,15 +33,15 @@
#include "krb5_locl.h"
-int _krb5_AES_string_to_default_iterator = 4096;
+int _krb5_AES_SHA1_string_to_default_iterator = 4096;
static krb5_error_code
-AES_string_to_key(krb5_context context,
- krb5_enctype enctype,
- krb5_data password,
- krb5_salt salt,
- krb5_data opaque,
- krb5_keyblock *key)
+AES_SHA1_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
{
krb5_error_code ret;
uint32_t iter;
@@ -49,7 +49,7 @@ AES_string_to_key(krb5_context context,
struct _krb5_key_data kd;
if (opaque.length == 0)
- iter = _krb5_AES_string_to_default_iterator;
+ iter = _krb5_AES_SHA1_string_to_default_iterator;
else if (opaque.length == 4) {
unsigned long v;
_krb5_get_int(opaque.data, &v, 4);
@@ -63,10 +63,8 @@ AES_string_to_key(krb5_context context,
kd.schedule = NULL;
ALLOC(kd.key, 1);
- if(kd.key == NULL) {
- krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (kd.key == NULL)
+ return krb5_enomem(context);
kd.key->keytype = enctype;
ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
if (ret) {
@@ -74,10 +72,11 @@ AES_string_to_key(krb5_context context,
return ret;
}
- ret = PKCS5_PBKDF2_HMAC_SHA1(password.data, password.length,
- salt.saltvalue.data, salt.saltvalue.length,
- iter,
- et->keytype->size, kd.key->keyvalue.data);
+ ret = PKCS5_PBKDF2_HMAC(password.data, password.length,
+ salt.saltvalue.data, salt.saltvalue.length,
+ iter,
+ EVP_sha1(),
+ et->keytype->size, kd.key->keyvalue.data);
if (ret != 1) {
_krb5_free_key_data(context, &kd, et);
krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
@@ -93,11 +92,11 @@ AES_string_to_key(krb5_context context,
return ret;
}
-struct salt_type _krb5_AES_salt[] = {
+struct salt_type _krb5_AES_SHA1_salt[] = {
{
KRB5_PW_SALT,
"pw-salt",
- AES_string_to_key
+ AES_SHA1_string_to_key
},
- { 0 }
+ { 0, NULL, NULL }
};
diff --git a/lib/krb5/salt-aes-sha2.c b/lib/krb5/salt-aes-sha2.c
new file mode 100644
index 000000000000..bfd726c34c19
--- /dev/null
+++ b/lib/krb5/salt-aes-sha2.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+
+int _krb5_AES_SHA2_string_to_default_iterator = 32768;
+
+static krb5_error_code
+AES_SHA2_string_to_key(krb5_context context,
+ krb5_enctype enctype,
+ krb5_data password,
+ krb5_salt salt,
+ krb5_data opaque,
+ krb5_keyblock *key)
+{
+ krb5_error_code ret;
+ uint32_t iter;
+ struct _krb5_encryption_type *et = NULL;
+ struct _krb5_key_data kd;
+ krb5_data saltp;
+ size_t enctypesz;
+ const EVP_MD *md = NULL;
+
+ krb5_data_zero(&saltp);
+ kd.key = NULL;
+ kd.schedule = NULL;
+
+ if (opaque.length == 0) {
+ iter = _krb5_AES_SHA2_string_to_default_iterator;
+ } else if (opaque.length == 4) {
+ unsigned long v;
+ _krb5_get_int(opaque.data, &v, 4);
+ iter = ((uint32_t)v);
+ } else {
+ ret = KRB5_PROG_KEYTYPE_NOSUPP; /* XXX */
+ goto cleanup;
+ }
+
+ et = _krb5_find_enctype(enctype);
+ if (et == NULL) {
+ ret = KRB5_PROG_KEYTYPE_NOSUPP;
+ goto cleanup;
+ }
+
+ kd.schedule = NULL;
+ ALLOC(kd.key, 1);
+ if (kd.key == NULL) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ kd.key->keytype = enctype;
+ ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
+ if (ret) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+
+ enctypesz = strlen(et->name) + 1;
+ ret = krb5_data_alloc(&saltp, enctypesz + salt.saltvalue.length);
+ if (ret) {
+ ret = krb5_enomem(context);
+ goto cleanup;
+ }
+ memcpy(saltp.data, et->name, enctypesz);
+ memcpy((unsigned char *)saltp.data + enctypesz,
+ salt.saltvalue.data, salt.saltvalue.length);
+
+ ret = _krb5_aes_sha2_md_for_enctype(context, enctype, &md);
+ if (ret)
+ goto cleanup;
+
+ ret = PKCS5_PBKDF2_HMAC(password.data, password.length,
+ saltp.data, saltp.length,
+ iter, md,
+ et->keytype->size, kd.key->keyvalue.data);
+ if (ret != 1) {
+ krb5_set_error_message(context, KRB5_PROG_KEYTYPE_NOSUPP,
+ "Error calculating s2k");
+ ret = KRB5_PROG_KEYTYPE_NOSUPP;
+ goto cleanup;
+ }
+
+ ret = _krb5_derive_key(context, et, &kd, "kerberos", strlen("kerberos"));
+ if (ret)
+ goto cleanup;
+
+ ret = krb5_copy_keyblock_contents(context, kd.key, key);
+ if (ret)
+ goto cleanup;
+
+cleanup:
+ krb5_data_free(&saltp);
+ _krb5_free_key_data(context, &kd, et);
+
+ return ret;
+}
+
+struct salt_type _krb5_AES_SHA2_salt[] = {
+ {
+ KRB5_PW_SALT,
+ "pw-salt",
+ AES_SHA2_string_to_key
+ },
+ { 0, NULL, NULL }
+};
diff --git a/lib/krb5/salt-arcfour.c b/lib/krb5/salt-arcfour.c
index ab5e51270c8e..38aaa25024e6 100644
--- a/lib/krb5/salt-arcfour.c
+++ b/lib/krb5/salt-arcfour.c
@@ -48,8 +48,7 @@ ARCFOUR_string_to_key(krb5_context context,
m = EVP_MD_CTX_create();
if (m == NULL) {
- ret = ENOMEM;
- krb5_set_error_message(context, ret, N_("malloc: out of memory", ""));
+ ret = krb5_enomem(context);
goto out;
}
@@ -58,22 +57,20 @@ ARCFOUR_string_to_key(krb5_context context,
ret = wind_utf8ucs2_length(password.data, &len);
if (ret) {
krb5_set_error_message (context, ret,
- N_("Password not an UCS2 string", ""));
+ N_("Password is not valid UTF-8", ""));
goto out;
}
s = malloc (len * sizeof(s[0]));
if (len != 0 && s == NULL) {
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- ret = ENOMEM;
+ ret = krb5_enomem(context);
goto out;
}
ret = wind_utf8ucs2(password.data, s, &len);
if (ret) {
krb5_set_error_message (context, ret,
- N_("Password not an UCS2 string", ""));
+ N_("Password is not valid UTF-8", ""));
goto out;
}
@@ -89,7 +86,7 @@ ARCFOUR_string_to_key(krb5_context context,
key->keytype = enctype;
ret = krb5_data_alloc (&key->keyvalue, 16);
if (ret) {
- krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ krb5_enomem(context);
goto out;
}
EVP_DigestFinal_ex (m, key->keyvalue.data, NULL);
@@ -108,5 +105,5 @@ struct salt_type _krb5_arcfour_salt[] = {
"pw-salt",
ARCFOUR_string_to_key
},
- { 0 }
+ { 0, NULL, NULL }
};
diff --git a/lib/krb5/salt-des.c b/lib/krb5/salt-des.c
index 56b285f72ed0..afcff675b24f 100644
--- a/lib/krb5/salt-des.c
+++ b/lib/krb5/salt-des.c
@@ -191,10 +191,8 @@ krb5_DES_string_to_key(krb5_context context,
len = password.length + salt.saltvalue.length;
s = malloc(len);
- if(len > 0 && s == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (len > 0 && s == NULL)
+ return krb5_enomem(context);
memcpy(s, password.data, password.length);
memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
DES_string_to_key_int(s, len, &tmp);
@@ -219,6 +217,6 @@ struct salt_type _krb5_des_salt[] = {
DES_AFS3_string_to_key
},
#endif
- { 0 }
+ { 0, NULL, NULL }
};
#endif
diff --git a/lib/krb5/salt-des3.c b/lib/krb5/salt-des3.c
index 79140a274f9e..342bba0d0f6b 100644
--- a/lib/krb5/salt-des3.c
+++ b/lib/krb5/salt-des3.c
@@ -50,10 +50,8 @@ DES3_string_to_key(krb5_context context,
len = password.length + salt.saltvalue.length;
str = malloc(len);
- if(len != 0 && str == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (len != 0 && str == NULL)
+ return krb5_enomem(context);
memcpy(str, password.data, password.length);
memcpy(str + password.length, salt.saltvalue.data, salt.saltvalue.length);
{
@@ -73,7 +71,7 @@ DES3_string_to_key(krb5_context context,
memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
DES_set_odd_parity(keys + i);
if(DES_is_weak_key(keys + i))
- _krb5_xor(keys + i, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ _krb5_xor8(*(keys + i), (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
DES_set_key_unchecked(keys + i, &s[i]);
}
memset(&ivec, 0, sizeof(ivec));
@@ -86,7 +84,7 @@ DES3_string_to_key(krb5_context context,
memcpy(keys + i, tmp + i * 8, sizeof(keys[i]));
DES_set_odd_parity(keys + i);
if(DES_is_weak_key(keys + i))
- _krb5_xor(keys + i, (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
+ _krb5_xor8(*(keys + i), (const unsigned char*)"\0\0\0\0\0\0\0\xf0");
}
memset(tmp, 0, sizeof(tmp));
}
@@ -112,10 +110,8 @@ DES3_string_to_key_derived(krb5_context context,
char *s;
s = malloc(len);
- if(len != 0 && s == NULL) {
- krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (len != 0 && s == NULL)
+ return krb5_enomem(context);
memcpy(s, password.data, password.length);
memcpy(s + password.length, salt.saltvalue.data, salt.saltvalue.length);
ret = krb5_string_to_key_derived(context,
@@ -136,7 +132,7 @@ struct salt_type _krb5_des3_salt[] = {
"pw-salt",
DES3_string_to_key
},
- { 0 }
+ { 0, NULL, NULL }
};
#endif
@@ -146,5 +142,5 @@ struct salt_type _krb5_des3_salt_derived[] = {
"pw-salt",
DES3_string_to_key_derived
},
- { 0 }
+ { 0, NULL, NULL }
};
diff --git a/lib/krb5/salt.c b/lib/krb5/salt.c
index 5e4c8a1c8572..a3e850fcb8cf 100644
--- a/lib/krb5/salt.c
+++ b/lib/krb5/salt.c
@@ -43,6 +43,7 @@ krb5_salttype_to_string (krb5_context context,
struct _krb5_encryption_type *e;
struct salt_type *st;
+ *string = NULL;
e = _krb5_find_enctype (etype);
if (e == NULL) {
krb5_set_error_message(context, KRB5_PROG_ETYPE_NOSUPP,
@@ -53,11 +54,8 @@ krb5_salttype_to_string (krb5_context context,
for (st = e->keytype->string_to_key; st && st->type; st++) {
if (st->type == stype) {
*string = strdup (st->name);
- if (*string == NULL) {
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*string == NULL)
+ return krb5_enomem(context);
return 0;
}
}
@@ -263,11 +261,8 @@ krb5_string_to_key_derived(krb5_context context,
keylen = et->keytype->bits / 8;
ALLOC(kd.key, 1);
- if(kd.key == NULL) {
- krb5_set_error_message (context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (kd.key == NULL)
+ return krb5_enomem(context);
ret = krb5_data_alloc(&kd.key->keyvalue, et->keytype->size);
if(ret) {
free(kd.key);
@@ -277,13 +272,12 @@ krb5_string_to_key_derived(krb5_context context,
tmp = malloc (keylen);
if(tmp == NULL) {
krb5_free_keyblock(context, kd.key);
- krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = _krb5_n_fold(str, len, tmp, keylen);
if (ret) {
free(tmp);
- krb5_set_error_message (context, ENOMEM, N_("malloc: out of memory", ""));
+ krb5_enomem(context);
return ret;
}
kd.schedule = NULL;
diff --git a/lib/krb5/scache.c b/lib/krb5/scache.c
index 5c422c6a4491..61a9b4fa1445 100644
--- a/lib/krb5/scache.c
+++ b/lib/krb5/scache.c
@@ -309,7 +309,6 @@ scc_alloc(krb5_context context, const char *name)
char *file;
if (*name == '\0') {
- krb5_error_code ret;
ret = get_def_name(context, &s->name);
if (ret)
s->name = strdup(SCACHE_DEF_NAME);
@@ -657,12 +656,10 @@ encode_creds(krb5_context context, krb5_creds *creds, krb5_data *data)
krb5_error_code ret;
krb5_storage *sp;
+ krb5_data_zero(data);
sp = krb5_storage_emem();
- if (sp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (sp == NULL)
+ return krb5_enomem(context);
ret = krb5_store_creds(sp, creds);
if (ret) {
@@ -688,11 +685,8 @@ decode_creds(krb5_context context, const void *data, size_t length,
krb5_storage *sp;
sp = krb5_storage_from_readonly_mem(data, length);
- if (sp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (sp == NULL)
+ return krb5_enomem(context);
ret = krb5_ret_creds(sp, creds);
krb5_storage_free(sp);
@@ -882,11 +876,8 @@ scc_get_first (krb5_context context,
*cursor = NULL;
ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ctx == NULL)
+ return krb5_enomem(context);
ret = make_database(context, s);
if (ret) {
@@ -905,19 +896,15 @@ scc_get_first (krb5_context context,
ret = asprintf(&name, "credIteration%pPid%d",
ctx, (int)getpid());
if (ret < 0 || name == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
free(ctx);
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
if (ret < 0 || ctx->drop == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
free(name);
free(ctx);
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = asprintf(&str, "CREATE TEMPORARY TABLE %s "
@@ -927,7 +914,7 @@ scc_get_first (krb5_context context,
free(ctx->drop);
free(name);
free(ctx);
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = exec_stmt(context, s->db, str, KRB5_CC_IO);
@@ -1161,11 +1148,8 @@ scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
*cursor = NULL;
ctx = calloc(1, sizeof(*ctx));
- if (ctx == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ctx == NULL)
+ return krb5_enomem(context);
ret = default_db(context, &ctx->db);
if (ctx->db == NULL) {
@@ -1176,33 +1160,27 @@ scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
ret = asprintf(&name, "cacheIteration%pPid%d",
ctx, (int)getpid());
if (ret < 0 || name == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
sqlite3_close(ctx->db);
free(ctx);
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = asprintf(&ctx->drop, "DROP TABLE %s", name);
if (ret < 0 || ctx->drop == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
sqlite3_close(ctx->db);
free(name);
free(ctx);
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches",
name);
if (ret < 0 || str == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
- return ENOMEM;
+ return krb5_enomem(context);
}
ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO);
@@ -1217,15 +1195,15 @@ scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor)
}
ret = asprintf(&str, "SELECT name FROM %s", name);
- free(name);
if (ret < 0 || str == NULL) {
exec_stmt(context, ctx->db, ctx->drop, 0);
sqlite3_close(ctx->db);
free(name);
free(ctx->drop);
free(ctx);
- return ENOMEM;
+ return krb5_enomem(context);
}
+ free(name);
ret = prepare_stmt(context, ctx->db, &ctx->stmt, str);
free(str);
@@ -1373,11 +1351,8 @@ scc_get_default_name(krb5_context context, char **str)
ret = asprintf(str, "SCC:%s", name);
free(name);
- if (ret < 0 || *str == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (ret < 0 || *str == NULL)
+ return krb5_enomem(context);
return 0;
}
@@ -1445,7 +1420,10 @@ KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops = {
scc_end_cache_get,
scc_move,
scc_get_default_name,
- scc_set_default
+ scc_set_default,
+ NULL,
+ NULL,
+ NULL
};
#endif
diff --git a/lib/krb5/send_to_kdc.c b/lib/krb5/send_to_kdc.c
index edf1d33c9d1c..066b849a7576 100644
--- a/lib/krb5/send_to_kdc.c
+++ b/lib/krb5/send_to_kdc.c
@@ -3,6 +3,8 @@
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
+ * Portions Copyright (c) 2010 - 2013 Apple Inc. All rights reserved.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -34,566 +36,1066 @@
#include "krb5_locl.h"
#include "send_to_kdc_plugin.h"
-struct send_to_kdc {
- krb5_send_to_kdc_func func;
- void *data;
-};
-
-/*
- * send the data in `req' on the socket `fd' (which is datagram iff udp)
- * waiting `tmout' for a reply and returning the reply in `rep'.
- * iff limit read up to this many bytes
- * returns 0 and data in `rep' if succesful, otherwise -1
+/**
+ * @section send_to_kdc Locating and sending packets to the KDC
+ *
+ * The send to kdc code is responsible to request the list of KDC from
+ * the locate-kdc subsystem and then send requests to each of them.
+ *
+ * - Each second a new hostname is tried.
+ * - If the hostname have several addresses, the first will be tried
+ * directly then in turn the other will be tried every 3 seconds
+ * (host_timeout).
+ * - UDP requests are tried 3 times, and it tried with a individual timeout of kdc_timeout / 3.
+ * - TCP and HTTP requests are tried 1 time.
+ *
+ * Total wait time shorter then (number of addresses * 3) + kdc_timeout seconds.
+ *
*/
static int
-recv_loop (krb5_socket_t fd,
- time_t tmout,
- int udp,
- size_t limit,
- krb5_data *rep)
+init_port(const char *s, int fallback)
{
- fd_set fdset;
- struct timeval timeout;
- int ret;
- int nbytes;
+ int tmp;
-#ifndef NO_LIMIT_FD_SETSIZE
- if (fd >= FD_SETSIZE) {
- return -1;
- }
-#endif
+ if (s && sscanf(s, "%d", &tmp) == 1)
+ return htons(tmp);
+ return fallback;
+}
+
+struct send_via_plugin_s {
+ krb5_const_realm realm;
+ krb5_krbhst_info *hi;
+ time_t timeout;
+ const krb5_data *send_data;
+ krb5_data *receive;
+};
+
- krb5_data_zero(rep);
- do {
- FD_ZERO(&fdset);
- FD_SET(fd, &fdset);
- timeout.tv_sec = tmout;
- timeout.tv_usec = 0;
- ret = select (fd + 1, &fdset, NULL, NULL, &timeout);
- if (ret < 0) {
- if (errno == EINTR)
- continue;
- return -1;
- } else if (ret == 0) {
- return 0;
- } else {
- void *tmp;
-
- if (rk_SOCK_IOCTL (fd, FIONREAD, &nbytes) < 0) {
- krb5_data_free (rep);
- return -1;
- }
- if(nbytes <= 0)
- return 0;
-
- if (limit)
- nbytes = min((size_t)nbytes, limit - rep->length);
-
- tmp = realloc (rep->data, rep->length + nbytes);
- if (tmp == NULL) {
- krb5_data_free (rep);
- return -1;
- }
- rep->data = tmp;
- ret = recv (fd, (char*)tmp + rep->length, nbytes, 0);
- if (ret < 0) {
- krb5_data_free (rep);
- return -1;
- }
- rep->length += ret;
- }
- } while(!udp && (limit == 0 || rep->length < limit));
- return 0;
+static krb5_error_code KRB5_LIB_CALL
+kdccallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
+ struct send_via_plugin_s *ctx = userctx;
+
+ if (service->send_to_kdc == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+ return service->send_to_kdc(context, plugctx, ctx->hi, ctx->timeout,
+ ctx->send_data, ctx->receive);
}
-/*
- * Send kerberos requests and receive a reply on a udp or any other kind
- * of a datagram socket. See `recv_loop'.
- */
+static krb5_error_code KRB5_LIB_CALL
+realmcallback(krb5_context context, const void *plug, void *plugctx, void *userctx)
+{
+ const krb5plugin_send_to_kdc_ftable *service = (const krb5plugin_send_to_kdc_ftable *)plug;
+ struct send_via_plugin_s *ctx = userctx;
-static int
-send_and_recv_udp(krb5_socket_t fd,
- time_t tmout,
- const krb5_data *req,
- krb5_data *rep)
+ if (service->send_to_realm == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+ return service->send_to_realm(context, plugctx, ctx->realm, ctx->timeout,
+ ctx->send_data, ctx->receive);
+}
+
+static krb5_error_code
+kdc_via_plugin(krb5_context context,
+ krb5_krbhst_info *hi,
+ time_t timeout,
+ const krb5_data *send_data,
+ krb5_data *receive)
{
- if (send (fd, req->data, req->length, 0) < 0)
- return -1;
+ struct send_via_plugin_s userctx;
+
+ userctx.realm = NULL;
+ userctx.hi = hi;
+ userctx.timeout = timeout;
+ userctx.send_data = send_data;
+ userctx.receive = receive;
- return recv_loop(fd, tmout, 1, 0, rep);
+ return _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_SEND_TO_KDC,
+ KRB5_PLUGIN_SEND_TO_KDC_VERSION_0, 0,
+ &userctx, kdccallback);
}
-/*
- * `send_and_recv' for a TCP (or any other stream) socket.
- * Since there are no record limits on a stream socket the protocol here
- * is to prepend the request with 4 bytes of its length and the reply
- * is similarly encoded.
- */
+static krb5_error_code
+realm_via_plugin(krb5_context context,
+ krb5_const_realm realm,
+ time_t timeout,
+ const krb5_data *send_data,
+ krb5_data *receive)
+{
+ struct send_via_plugin_s userctx;
-static int
-send_and_recv_tcp(krb5_socket_t fd,
- time_t tmout,
- const krb5_data *req,
- krb5_data *rep)
+ userctx.realm = realm;
+ userctx.hi = NULL;
+ userctx.timeout = timeout;
+ userctx.send_data = send_data;
+ userctx.receive = receive;
+
+ return _krb5_plugin_run_f(context, "krb5", KRB5_PLUGIN_SEND_TO_KDC,
+ KRB5_PLUGIN_SEND_TO_KDC_VERSION_2, 0,
+ &userctx, realmcallback);
+}
+
+struct krb5_sendto_ctx_data {
+ int flags;
+ int type;
+ krb5_sendto_ctx_func func;
+ void *data;
+ char *hostname;
+ krb5_krbhst_handle krbhst;
+
+ /* context2 */
+ const krb5_data *send_data;
+ krb5_data response;
+ heim_array_t hosts;
+ int stateflags;
+#define KRBHST_COMPLETED 1
+
+ /* prexmit */
+ krb5_sendto_prexmit prexmit_func;
+ void *prexmit_ctx;
+
+ /* stats */
+ struct {
+ struct timeval start_time;
+ struct timeval name_resolution;
+ struct timeval krbhst;
+ unsigned long sent_packets;
+ unsigned long num_hosts;
+ } stats;
+ unsigned int stid;
+};
+
+static void
+dealloc_sendto_ctx(void *ptr)
{
- unsigned char len[4];
- unsigned long rep_len;
- krb5_data len_data;
+ krb5_sendto_ctx ctx = (krb5_sendto_ctx)ptr;
+ if (ctx->hostname)
+ free(ctx->hostname);
+ heim_release(ctx->hosts);
+ heim_release(ctx->krbhst);
+}
- _krb5_put_int(len, req->length, 4);
- if(net_write (fd, len, sizeof(len)) < 0)
- return -1;
- if(net_write (fd, req->data, req->length) < 0)
- return -1;
- if (recv_loop (fd, tmout, 0, 4, &len_data) < 0)
- return -1;
- if (len_data.length != 4) {
- krb5_data_free (&len_data);
- return -1;
- }
- _krb5_get_int(len_data.data, &rep_len, 4);
- krb5_data_free (&len_data);
- if (recv_loop (fd, tmout, 0, rep_len, rep) < 0)
- return -1;
- if(rep->length != rep_len) {
- krb5_data_free (rep);
- return -1;
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx)
+{
+ *ctx = heim_alloc(sizeof(**ctx), "sendto-context", dealloc_sendto_ctx);
+ if (*ctx == NULL)
+ return krb5_enomem(context);
+ (*ctx)->hosts = heim_array_create();
+
+ return 0;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags)
+{
+ ctx->flags |= flags;
+}
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx)
+{
+ return ctx->flags;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type)
+{
+ ctx->type = type;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx,
+ krb5_sendto_ctx_func func,
+ void *data)
+{
+ ctx->func = func;
+ ctx->data = data;
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_sendto_ctx_set_prexmit(krb5_sendto_ctx ctx,
+ krb5_sendto_prexmit prexmit,
+ void *data)
+{
+ ctx->prexmit_func = prexmit;
+ ctx->prexmit_ctx = data;
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_sendto_set_hostname(krb5_context context,
+ krb5_sendto_ctx ctx,
+ const char *hostname)
+{
+ if (ctx->hostname == NULL)
+ free(ctx->hostname);
+ ctx->hostname = strdup(hostname);
+ if (ctx->hostname == NULL) {
+ krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", ""));
+ return ENOMEM;
}
return 0;
}
-int
-_krb5_send_and_recv_tcp(krb5_socket_t fd,
- time_t tmout,
- const krb5_data *req,
- krb5_data *rep)
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_sendto_ctx_set_krb5hst(krb5_context context,
+ krb5_sendto_ctx ctx,
+ krb5_krbhst_handle handle)
+{
+ heim_release(ctx->krbhst);
+ ctx->krbhst = heim_retain(handle);
+}
+
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx)
{
- return send_and_recv_tcp(fd, tmout, req, rep);
+ heim_release(ctx);
+}
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
+ const krb5_data *reply, int *action)
+{
+ krb5_error_code ret;
+ KRB_ERROR error;
+
+ if(krb5_rd_error(context, reply, &error))
+ return 0;
+
+ ret = krb5_error_from_rd_error(context, &error, NULL);
+ krb5_free_error_contents(context, &error);
+
+ switch(ret) {
+ case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
+ if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
+ break;
+ krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
+ *action = KRB5_SENDTO_RESET;
+ break;
+ }
+ case KRB5KDC_ERR_SVC_UNAVAILABLE:
+ *action = KRB5_SENDTO_CONTINUE;
+ break;
+ }
+ return 0;
}
/*
- * `send_and_recv' tailored for the HTTP protocol.
+ *
*/
-static int
-send_and_recv_http(krb5_socket_t fd,
- time_t tmout,
- const char *prefix,
- const krb5_data *req,
- krb5_data *rep)
-{
- char *request = NULL;
- char *str;
+struct host;
+
+struct host_fun {
+ krb5_error_code (*prepare)(krb5_context, struct host *, const krb5_data *);
+ krb5_error_code (*send_fn)(krb5_context, struct host *);
+ krb5_error_code (*recv_fn)(krb5_context, struct host *, krb5_data *);
+ int ntries;
+};
+
+struct host {
+ enum host_state { CONNECT, CONNECTING, CONNECTED, WAITING_REPLY, DEAD } state;
+ krb5_krbhst_info *hi;
+ struct addrinfo *ai;
+ rk_socket_t fd;
+ struct host_fun *fun;
+ unsigned int tries;
+ time_t timeout;
+ krb5_data data;
+ unsigned int tid;
+};
+
+static void
+debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
+ __attribute__ ((__format__ (__printf__, 4, 5)));
+
+static void
+debug_host(krb5_context context, int level, struct host *host, const char *fmt, ...)
+{
+ const char *proto = "unknown";
+ char name[NI_MAXHOST], port[NI_MAXSERV];
+ char *text = NULL;
+ va_list ap;
int ret;
- int len = base64_encode(req->data, req->length, &str);
- if(len < 0)
- return -1;
- ret = asprintf(&request, "GET %s%s HTTP/1.0\r\n\r\n", prefix, str);
- free(str);
- if (ret < 0 || request == NULL)
+ if (!_krb5_have_debug(context, 5))
+ return;
+
+ va_start(ap, fmt);
+ ret = vasprintf(&text, fmt, ap);
+ va_end(ap);
+ if (ret == -1 || text == NULL)
+ return;
+
+ if (host->hi->proto == KRB5_KRBHST_HTTP)
+ proto = "http";
+ else if (host->hi->proto == KRB5_KRBHST_TCP)
+ proto = "tcp";
+ else if (host->hi->proto == KRB5_KRBHST_UDP)
+ proto = "udp";
+
+ if (getnameinfo(host->ai->ai_addr, host->ai->ai_addrlen,
+ name, sizeof(name), port, sizeof(port), NI_NUMERICHOST) != 0)
+ name[0] = '\0';
+
+ _krb5_debug(context, level, "%s: %s %s:%s (%s) tid: %08x", text,
+ proto, name, port, host->hi->hostname, host->tid);
+ free(text);
+}
+
+
+static void
+deallocate_host(void *ptr)
+{
+ struct host *host = ptr;
+ if (!rk_IS_BAD_SOCKET(host->fd))
+ rk_closesocket(host->fd);
+ krb5_data_free(&host->data);
+ host->ai = NULL;
+}
+
+static void
+host_dead(krb5_context context, struct host *host, const char *msg)
+{
+ debug_host(context, 5, host, "%s", msg);
+ rk_closesocket(host->fd);
+ host->fd = rk_INVALID_SOCKET;
+ host->state = DEAD;
+}
+
+static krb5_error_code
+send_stream(krb5_context context, struct host *host)
+{
+ ssize_t len;
+
+ len = krb5_net_write(context, &host->fd, host->data.data, host->data.length);
+
+ if (len < 0)
+ return errno;
+ else if (len < host->data.length) {
+ host->data.length -= len;
+ memmove(host->data.data, ((uint8_t *)host->data.data) + len, host->data.length - len);
return -1;
- ret = net_write (fd, request, strlen(request));
- free (request);
- if (ret < 0)
+ } else {
+ krb5_data_free(&host->data);
+ return 0;
+ }
+}
+
+static krb5_error_code
+recv_stream(krb5_context context, struct host *host)
+{
+ krb5_error_code ret;
+ size_t oldlen;
+ ssize_t sret;
+ int nbytes;
+
+ if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
+ return HEIM_NET_CONN_REFUSED;
+
+ if (context->max_msg_size - host->data.length < nbytes) {
+ krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
+ N_("TCP message from KDC too large %d", ""),
+ (int)(host->data.length + nbytes));
+ return KRB5KRB_ERR_FIELD_TOOLONG;
+ }
+
+ oldlen = host->data.length;
+
+ ret = krb5_data_realloc(&host->data, oldlen + nbytes + 1 /* NUL */);
+ if (ret)
return ret;
- ret = recv_loop(fd, tmout, 0, 0, rep);
- if(ret)
+
+ sret = krb5_net_read(context, &host->fd, ((uint8_t *)host->data.data) + oldlen, nbytes);
+ if (sret <= 0) {
+ ret = errno;
return ret;
- {
- unsigned long rep_len;
- char *s, *p;
-
- s = realloc(rep->data, rep->length + 1);
- if (s == NULL) {
- krb5_data_free (rep);
- return -1;
- }
- s[rep->length] = 0;
- p = strstr(s, "\r\n\r\n");
- if(p == NULL) {
- krb5_data_zero(rep);
- free(s);
- return -1;
- }
- p += 4;
- rep->data = s;
- rep->length -= p - s;
- if(rep->length < 4) { /* remove length */
- krb5_data_zero(rep);
- free(s);
- return -1;
- }
- rep->length -= 4;
- _krb5_get_int(p, &rep_len, 4);
- if (rep_len != rep->length) {
- krb5_data_zero(rep);
- free(s);
- return -1;
- }
- memmove(rep->data, p + 4, rep->length);
}
+ host->data.length = oldlen + sret;
+ /* zero terminate for http transport */
+ ((uint8_t *)host->data.data)[host->data.length] = '\0';
+
return 0;
}
-static int
-init_port(const char *s, int fallback)
+/*
+ *
+ */
+
+static void
+host_next_timeout(krb5_context context, struct host *host)
{
- if (s) {
- int tmp;
+ host->timeout = context->kdc_timeout / host->fun->ntries;
+ if (host->timeout == 0)
+ host->timeout = 1;
- sscanf (s, "%d", &tmp);
- return htons(tmp);
- } else
- return fallback;
+ host->timeout += time(NULL);
}
/*
- * Return 0 if succesful, otherwise 1
+ * connected host
*/
-static int
-send_via_proxy (krb5_context context,
- const krb5_krbhst_info *hi,
- const krb5_data *send_data,
- krb5_data *receive)
-{
- char *proxy2 = strdup(context->http_proxy);
- char *proxy = proxy2;
- char *prefix = NULL;
- char *colon;
- struct addrinfo hints;
- struct addrinfo *ai, *a;
- int ret;
- krb5_socket_t s = rk_INVALID_SOCKET;
- char portstr[NI_MAXSERV];
+static void
+host_connected(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
+{
+ krb5_error_code ret;
- if (proxy == NULL)
- return ENOMEM;
- if (strncmp (proxy, "http://", 7) == 0)
- proxy += 7;
-
- colon = strchr(proxy, ':');
- if(colon != NULL)
- *colon++ = '\0';
- memset (&hints, 0, sizeof(hints));
- hints.ai_family = PF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- snprintf (portstr, sizeof(portstr), "%d",
- ntohs(init_port (colon, htons(80))));
- ret = getaddrinfo (proxy, portstr, &hints, &ai);
- free (proxy2);
+ host->state = CONNECTED;
+ /*
+ * Now prepare data to send to host
+ */
+ if (ctx->prexmit_func) {
+ krb5_data data;
+
+ krb5_data_zero(&data);
+
+ ret = ctx->prexmit_func(context, host->hi->proto,
+ ctx->prexmit_ctx, host->fd, &data);
+ if (ret == 0) {
+ if (data.length == 0) {
+ host_dead(context, host, "prexmit function didn't send data");
+ return;
+ }
+ ret = host->fun->prepare(context, host, &data);
+ krb5_data_free(&data);
+ }
+
+ } else {
+ ret = host->fun->prepare(context, host, ctx->send_data);
+ }
if (ret)
- return krb5_eai_to_heim_errno(ret, errno);
+ debug_host(context, 5, host, "failed to prexmit/prepare");
+}
- for (a = ai; a != NULL; a = a->ai_next) {
- s = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
- if (s < 0)
- continue;
- rk_cloexec(s);
- if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
- rk_closesocket (s);
- continue;
+/*
+ * connect host
+ */
+
+static void
+host_connect(krb5_context context, krb5_sendto_ctx ctx, struct host *host)
+{
+ krb5_krbhst_info *hi = host->hi;
+ struct addrinfo *ai = host->ai;
+
+ debug_host(context, 5, host, "connecting to host");
+
+ if (connect(host->fd, ai->ai_addr, ai->ai_addrlen) < 0) {
+#ifdef HAVE_WINSOCK
+ if (WSAGetLastError() == WSAEWOULDBLOCK)
+ errno = EINPROGRESS;
+#endif /* HAVE_WINSOCK */
+ if (errno == EINPROGRESS && (hi->proto == KRB5_KRBHST_HTTP || hi->proto == KRB5_KRBHST_TCP)) {
+ debug_host(context, 5, host, "connecting to %d", host->fd);
+ host->state = CONNECTING;
+ } else {
+ host_dead(context, host, "failed to connect");
}
- break;
+ } else {
+ host_connected(context, ctx, host);
}
- if (a == NULL) {
- freeaddrinfo (ai);
- return 1;
- }
- freeaddrinfo (ai);
- ret = asprintf(&prefix, "http://%s/", hi->hostname);
- if(ret < 0 || prefix == NULL) {
- close(s);
- return 1;
- }
- ret = send_and_recv_http(s, context->kdc_timeout,
- prefix, send_data, receive);
- rk_closesocket (s);
- free(prefix);
- if(ret == 0 && receive->length != 0)
- return 0;
- return 1;
+ host_next_timeout(context, host);
}
+/*
+ * HTTP transport
+ */
+
static krb5_error_code
-send_via_plugin(krb5_context context,
- krb5_krbhst_info *hi,
- time_t timeout,
- const krb5_data *send_data,
- krb5_data *receive)
+prepare_http(krb5_context context, struct host *host, const krb5_data *data)
{
- struct krb5_plugin *list = NULL, *e;
+ char *str = NULL, *request = NULL;
krb5_error_code ret;
+ int len;
- ret = _krb5_plugin_find(context, PLUGIN_TYPE_DATA, KRB5_PLUGIN_SEND_TO_KDC, &list);
- if(ret != 0 || list == NULL)
- return KRB5_PLUGIN_NO_HANDLE;
+ heim_assert(host->data.length == 0, "prepare_http called twice");
- for (e = list; e != NULL; e = _krb5_plugin_get_next(e)) {
- krb5plugin_send_to_kdc_ftable *service;
- void *ctx;
+ len = rk_base64_encode(data->data, data->length, &str);
+ if(len < 0)
+ return ENOMEM;
- service = _krb5_plugin_get_symbol(e);
- if (service->minor_version != 0)
- continue;
+ if (context->http_proxy)
+ ret = asprintf(&request, "GET http://%s/%s HTTP/1.0\r\n\r\n", host->hi->hostname, str);
+ else
+ ret = asprintf(&request, "GET /%s HTTP/1.0\r\n\r\n", str);
+ free(str);
+ if(ret < 0 || request == NULL)
+ return ENOMEM;
+
+ host->data.data = request;
+ host->data.length = strlen(request);
- (*service->init)(context, &ctx);
- ret = (*service->send_to_kdc)(context, ctx, hi,
- timeout, send_data, receive);
- (*service->fini)(ctx);
- if (ret == 0)
- break;
- if (ret != KRB5_PLUGIN_NO_HANDLE) {
- krb5_set_error_message(context, ret,
- N_("Plugin send_to_kdc failed to "
- "lookup with error: %d", ""), ret);
- break;
- }
+ return 0;
+}
+
+static krb5_error_code
+recv_http(krb5_context context, struct host *host, krb5_data *data)
+{
+ krb5_error_code ret;
+ unsigned long rep_len;
+ size_t len;
+ char *p;
+
+ /*
+ * recv_stream returns a NUL terminated stream
+ */
+
+ ret = recv_stream(context, host);
+ if (ret)
+ return ret;
+
+ p = strstr(host->data.data, "\r\n\r\n");
+ if (p == NULL)
+ return -1;
+ p += 4;
+
+ len = host->data.length - (p - (char *)host->data.data);
+ if (len < 4)
+ return -1;
+
+ _krb5_get_int(p, &rep_len, 4);
+ if (len < rep_len)
+ return -1;
+
+ p += 4;
+
+ memmove(host->data.data, p, rep_len);
+ host->data.length = rep_len;
+
+ *data = host->data;
+ krb5_data_zero(&host->data);
+
+ return 0;
+}
+
+/*
+ * TCP transport
+ */
+
+static krb5_error_code
+prepare_tcp(krb5_context context, struct host *host, const krb5_data *data)
+{
+ krb5_error_code ret;
+ krb5_storage *sp;
+
+ heim_assert(host->data.length == 0, "prepare_tcp called twice");
+
+ sp = krb5_storage_emem();
+ if (sp == NULL)
+ return ENOMEM;
+
+ ret = krb5_store_data(sp, *data);
+ if (ret) {
+ krb5_storage_free(sp);
+ return ret;
}
- _krb5_plugin_free(list);
- return KRB5_PLUGIN_NO_HANDLE;
+ ret = krb5_storage_to_data(sp, &host->data);
+ krb5_storage_free(sp);
+
+ return ret;
}
+static krb5_error_code
+recv_tcp(krb5_context context, struct host *host, krb5_data *data)
+{
+ krb5_error_code ret;
+ unsigned long pktlen;
+
+ ret = recv_stream(context, host);
+ if (ret)
+ return ret;
+
+ if (host->data.length < 4)
+ return -1;
+
+ _krb5_get_int(host->data.data, &pktlen, 4);
+
+ if (pktlen > host->data.length - 4)
+ return -1;
+
+ memmove(host->data.data, ((uint8_t *)host->data.data) + 4, host->data.length - 4);
+ host->data.length -= 4;
+
+ *data = host->data;
+ krb5_data_zero(&host->data);
+
+ return 0;
+}
/*
- * Send the data `send' to one host from `handle` and get back the reply
- * in `receive'.
+ * UDP transport
*/
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_sendto (krb5_context context,
- const krb5_data *send_data,
- krb5_krbhst_handle handle,
- krb5_data *receive)
-{
- krb5_error_code ret;
- krb5_socket_t fd;
- size_t i;
-
- krb5_data_zero(receive);
-
- for (i = 0; i < context->max_retries; ++i) {
- krb5_krbhst_info *hi;
-
- while (krb5_krbhst_next(context, handle, &hi) == 0) {
- struct addrinfo *ai, *a;
-
- _krb5_debug(context, 2,
- "trying to communicate with host %s in realm %s",
- hi->hostname, _krb5_krbhst_get_realm(handle));
-
- if (context->send_to_kdc) {
- struct send_to_kdc *s = context->send_to_kdc;
-
- ret = (*s->func)(context, s->data, hi,
- context->kdc_timeout, send_data, receive);
- if (ret == 0 && receive->length != 0)
- goto out;
- continue;
- }
-
- ret = send_via_plugin(context, hi, context->kdc_timeout,
- send_data, receive);
- if (ret == 0 && receive->length != 0)
- goto out;
- else if (ret != KRB5_PLUGIN_NO_HANDLE)
- continue;
-
- if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
- if (send_via_proxy (context, hi, send_data, receive) == 0) {
- ret = 0;
- goto out;
- }
- continue;
- }
-
- ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
- if (ret)
- continue;
-
- for (a = ai; a != NULL; a = a->ai_next) {
- fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
- if (rk_IS_BAD_SOCKET(fd))
- continue;
- rk_cloexec(fd);
- if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) {
- rk_closesocket (fd);
- continue;
- }
- switch (hi->proto) {
- case KRB5_KRBHST_HTTP :
- ret = send_and_recv_http(fd, context->kdc_timeout,
- "", send_data, receive);
- break;
- case KRB5_KRBHST_TCP :
- ret = send_and_recv_tcp (fd, context->kdc_timeout,
- send_data, receive);
- break;
- case KRB5_KRBHST_UDP :
- ret = send_and_recv_udp (fd, context->kdc_timeout,
- send_data, receive);
- break;
- }
- rk_closesocket (fd);
- if(ret == 0 && receive->length != 0)
- goto out;
- }
- }
- krb5_krbhst_reset(context, handle);
- }
- krb5_clear_error_message (context);
- ret = KRB5_KDC_UNREACH;
-out:
- _krb5_debug(context, 2,
- "result of trying to talk to realm %s = %d",
- _krb5_krbhst_get_realm(handle), ret);
- return ret;
+static krb5_error_code
+prepare_udp(krb5_context context, struct host *host, const krb5_data *data)
+{
+ return krb5_data_copy(&host->data, data->data, data->length);
}
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_sendto_kdc(krb5_context context,
- const krb5_data *send_data,
- const krb5_realm *realm,
- krb5_data *receive)
+static krb5_error_code
+send_udp(krb5_context context, struct host *host)
{
- return krb5_sendto_kdc_flags(context, send_data, realm, receive, 0);
+ if (send(host->fd, host->data.data, host->data.length, 0) < 0)
+ return errno;
+ return 0;
}
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_sendto_kdc_flags(krb5_context context,
- const krb5_data *send_data,
- const krb5_realm *realm,
- krb5_data *receive,
- int flags)
+static krb5_error_code
+recv_udp(krb5_context context, struct host *host, krb5_data *data)
{
krb5_error_code ret;
- krb5_sendto_ctx ctx;
+ int nbytes;
+
- ret = krb5_sendto_ctx_alloc(context, &ctx);
+ if (rk_SOCK_IOCTL(host->fd, FIONREAD, &nbytes) != 0 || nbytes <= 0)
+ return HEIM_NET_CONN_REFUSED;
+
+ if (context->max_msg_size < nbytes) {
+ krb5_set_error_message(context, KRB5KRB_ERR_FIELD_TOOLONG,
+ N_("UDP message from KDC too large %d", ""),
+ (int)nbytes);
+ return KRB5KRB_ERR_FIELD_TOOLONG;
+ }
+
+ ret = krb5_data_alloc(data, nbytes);
if (ret)
return ret;
- krb5_sendto_ctx_add_flags(ctx, flags);
- krb5_sendto_ctx_set_func(ctx, _krb5_kdc_retry, NULL);
- ret = krb5_sendto_context(context, ctx, send_data, *realm, receive);
- krb5_sendto_ctx_free(context, ctx);
- return ret;
+ ret = recv(host->fd, data->data, data->length, 0);
+ if (ret < 0) {
+ ret = errno;
+ krb5_data_free(data);
+ return ret;
+ }
+ data->length = ret;
+
+ return 0;
}
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_set_send_to_kdc_func(krb5_context context,
- krb5_send_to_kdc_func func,
- void *data)
+static struct host_fun http_fun = {
+ prepare_http,
+ send_stream,
+ recv_http,
+ 1
+};
+static struct host_fun tcp_fun = {
+ prepare_tcp,
+ send_stream,
+ recv_tcp,
+ 1
+};
+static struct host_fun udp_fun = {
+ prepare_udp,
+ send_udp,
+ recv_udp,
+ 3
+};
+
+
+/*
+ * Host state machine
+ */
+
+static int
+eval_host_state(krb5_context context,
+ krb5_sendto_ctx ctx,
+ struct host *host,
+ int readable, int writeable)
{
- free(context->send_to_kdc);
- if (func == NULL) {
- context->send_to_kdc = NULL;
+ krb5_error_code ret;
+
+ if (host->state == CONNECT) {
+ /* check if its this host time to connect */
+ if (host->timeout < time(NULL))
+ host_connect(context, ctx, host);
return 0;
}
- context->send_to_kdc = malloc(sizeof(*context->send_to_kdc));
- if (context->send_to_kdc == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ if (host->state == CONNECTING && writeable)
+ host_connected(context, ctx, host);
+
+ if (readable) {
+
+ debug_host(context, 5, host, "reading packet");
+
+ ret = host->fun->recv_fn(context, host, &ctx->response);
+ if (ret == -1) {
+ /* not done yet */
+ } else if (ret == 0) {
+ /* if recv_foo function returns 0, we have a complete reply */
+ debug_host(context, 5, host, "host completed");
+ return 1;
+ } else {
+ host_dead(context, host, "host disconnected");
+ }
+ }
+
+ /* check if there is anything to send, state might DEAD after read */
+ if (writeable && host->state == CONNECTED) {
+
+ ctx->stats.sent_packets++;
+
+ debug_host(context, 5, host, "writing packet");
+
+ ret = host->fun->send_fn(context, host);
+ if (ret == -1) {
+ /* not done yet */
+ } else if (ret) {
+ host_dead(context, host, "host dead, write failed");
+ } else
+ host->state = WAITING_REPLY;
}
- context->send_to_kdc->func = func;
- context->send_to_kdc->data = data;
return 0;
}
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-_krb5_copy_send_to_kdc_func(krb5_context context, krb5_context to)
+/*
+ *
+ */
+
+static krb5_error_code
+submit_request(krb5_context context, krb5_sendto_ctx ctx, krb5_krbhst_info *hi)
{
- if (context->send_to_kdc)
- return krb5_set_send_to_kdc_func(to,
- context->send_to_kdc->func,
- context->send_to_kdc->data);
- else
- return krb5_set_send_to_kdc_func(to, NULL, NULL);
-}
+ unsigned long submitted_host = 0;
+ krb5_boolean freeai = FALSE;
+ struct timeval nrstart, nrstop;
+ krb5_error_code ret;
+ struct addrinfo *ai = NULL, *a;
+ struct host *host;
+ ret = kdc_via_plugin(context, hi, context->kdc_timeout,
+ ctx->send_data, &ctx->response);
+ if (ret == 0) {
+ return 0;
+ } else if (ret != KRB5_PLUGIN_NO_HANDLE) {
+ _krb5_debug(context, 5, "send via plugin failed %s: %d",
+ hi->hostname, ret);
+ return ret;
+ }
+ /*
+ * If we have a proxy, let use the address of the proxy instead of
+ * the KDC and let the proxy deal with the resolving of the KDC.
+ */
+
+ gettimeofday(&nrstart, NULL);
+
+ if (hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
+ char *proxy2 = strdup(context->http_proxy);
+ char *el, *proxy = proxy2;
+ struct addrinfo hints;
+ char portstr[NI_MAXSERV];
+ unsigned short nport;
+
+ if (proxy == NULL)
+ return ENOMEM;
+ if (strncmp(proxy, "http://", 7) == 0)
+ proxy += 7;
+
+ /* check for url terminating slash */
+ el = strchr(proxy, '/');
+ if (el != NULL)
+ *el = '\0';
+
+ /* check for port in hostname, used below as port */
+ el = strchr(proxy, ':');
+ if(el != NULL)
+ *el++ = '\0';
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+ /* On some systems ntohs(foo(..., htons(...))) causes shadowing */
+ nport = init_port(el, htons(80));
+ snprintf(portstr, sizeof(portstr), "%d", ntohs(nport));
+
+ ret = getaddrinfo(proxy, portstr, &hints, &ai);
+ free(proxy2);
+ if (ret)
+ return krb5_eai_to_heim_errno(ret, errno);
+
+ freeai = TRUE;
-struct krb5_sendto_ctx_data {
- int flags;
- int type;
- krb5_sendto_ctx_func func;
- void *data;
-};
+ } else {
+ ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
+ if (ret)
+ return ret;
+ }
-KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_sendto_ctx_alloc(krb5_context context, krb5_sendto_ctx *ctx)
-{
- *ctx = calloc(1, sizeof(**ctx));
- if (*ctx == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ /* add up times */
+ gettimeofday(&nrstop, NULL);
+ timevalsub(&nrstop, &nrstart);
+ timevaladd(&ctx->stats.name_resolution, &nrstop);
+
+ ctx->stats.num_hosts++;
+
+ for (a = ai; a != NULL; a = a->ai_next) {
+ rk_socket_t fd;
+
+ fd = socket(a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
+ if (rk_IS_BAD_SOCKET(fd))
+ continue;
+ rk_cloexec(fd);
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ if (fd >= FD_SETSIZE) {
+ _krb5_debug(context, 0, "fd too large for select");
+ rk_closesocket(fd);
+ continue;
+ }
+#endif
+ socket_set_nonblocking(fd, 1);
+
+ host = heim_alloc(sizeof(*host), "sendto-host", deallocate_host);
+ if (host == NULL) {
+ if (freeai)
+ freeaddrinfo(ai);
+ rk_closesocket(fd);
+ return ENOMEM;
+ }
+ host->hi = hi;
+ host->fd = fd;
+ host->ai = a;
+ /* next version of stid */
+ host->tid = ctx->stid = (ctx->stid & 0xffff0000) | ((ctx->stid & 0xffff) + 1);
+
+ host->state = CONNECT;
+
+ switch (host->hi->proto) {
+ case KRB5_KRBHST_HTTP :
+ host->fun = &http_fun;
+ break;
+ case KRB5_KRBHST_TCP :
+ host->fun = &tcp_fun;
+ break;
+ case KRB5_KRBHST_UDP :
+ host->fun = &udp_fun;
+ break;
+ default:
+ heim_abort("undefined http transport protocol: %d", (int)host->hi->proto);
+ }
+
+ host->tries = host->fun->ntries;
+
+ /*
+ * Connect directly next host, wait a host_timeout for each next address
+ */
+ if (submitted_host == 0)
+ host_connect(context, ctx, host);
+ else {
+ debug_host(context, 5, host,
+ "Queuing host in future (in %ds), its the %lu address on the same name",
+ (int)(context->host_timeout * submitted_host), submitted_host + 1);
+ host->timeout = time(NULL) + (submitted_host * context->host_timeout);
+ }
+
+ heim_array_append_value(ctx->hosts, host);
+
+ heim_release(host);
+
+ submitted_host++;
}
+
+ if (freeai)
+ freeaddrinfo(ai);
+
+ if (!submitted_host)
+ return KRB5_KDC_UNREACH;
+
return 0;
}
-KRB5_LIB_FUNCTION void KRB5_LIB_CALL
-krb5_sendto_ctx_add_flags(krb5_sendto_ctx ctx, int flags)
+struct wait_ctx {
+ krb5_context context;
+ krb5_sendto_ctx ctx;
+ fd_set rfds;
+ fd_set wfds;
+ unsigned max_fd;
+ int got_reply;
+ time_t timenow;
+};
+
+static void
+wait_setup(heim_object_t obj, void *iter_ctx, int *stop)
{
- ctx->flags |= flags;
+ struct wait_ctx *wait_ctx = iter_ctx;
+ struct host *h = (struct host *)obj;
+
+ /* skip dead hosts */
+ if (h->state == DEAD)
+ return;
+
+ if (h->state == CONNECT) {
+ if (h->timeout < wait_ctx->timenow)
+ host_connect(wait_ctx->context, wait_ctx->ctx, h);
+ return;
+ }
+
+ /* if host timed out, dec tries and (retry or kill host) */
+ if (h->timeout < wait_ctx->timenow) {
+ heim_assert(h->tries != 0, "tries should not reach 0");
+ h->tries--;
+ if (h->tries == 0) {
+ host_dead(wait_ctx->context, h, "host timed out");
+ return;
+ } else {
+ debug_host(wait_ctx->context, 5, h, "retrying sending to");
+ host_next_timeout(wait_ctx->context, h);
+ host_connected(wait_ctx->context, wait_ctx->ctx, h);
+ }
+ }
+
+#ifndef NO_LIMIT_FD_SETSIZE
+ heim_assert(h->fd < FD_SETSIZE, "fd too large");
+#endif
+ switch (h->state) {
+ case WAITING_REPLY:
+ FD_SET(h->fd, &wait_ctx->rfds);
+ break;
+ case CONNECTING:
+ case CONNECTED:
+ FD_SET(h->fd, &wait_ctx->rfds);
+ FD_SET(h->fd, &wait_ctx->wfds);
+ break;
+ default:
+ heim_abort("invalid sendto host state");
+ }
+ if (h->fd > wait_ctx->max_fd)
+ wait_ctx->max_fd = h->fd;
}
-KRB5_LIB_FUNCTION int KRB5_LIB_CALL
-krb5_sendto_ctx_get_flags(krb5_sendto_ctx ctx)
+static int
+wait_filter_dead(heim_object_t obj, void *ctx)
{
- return ctx->flags;
+ struct host *h = (struct host *)obj;
+ return (int)((h->state == DEAD) ? true : false);
}
-KRB5_LIB_FUNCTION void KRB5_LIB_CALL
-krb5_sendto_ctx_set_type(krb5_sendto_ctx ctx, int type)
+static void
+wait_process(heim_object_t obj, void *ctx, int *stop)
{
- ctx->type = type;
-}
+ struct wait_ctx *wait_ctx = ctx;
+ struct host *h = (struct host *)obj;
+ int readable, writeable;
+ heim_assert(h->state != DEAD, "dead host resurected");
+#ifndef NO_LIMIT_FD_SETSIZE
+ heim_assert(h->fd < FD_SETSIZE, "fd too large");
+#endif
+ readable = FD_ISSET(h->fd, &wait_ctx->rfds);
+ writeable = FD_ISSET(h->fd, &wait_ctx->wfds);
-KRB5_LIB_FUNCTION void KRB5_LIB_CALL
-krb5_sendto_ctx_set_func(krb5_sendto_ctx ctx,
- krb5_sendto_ctx_func func,
- void *data)
+ if (readable || writeable || h->state == CONNECT)
+ wait_ctx->got_reply |= eval_host_state(wait_ctx->context, wait_ctx->ctx, h, readable, writeable);
+
+ /* if there is already a reply, just fall though the array */
+ if (wait_ctx->got_reply)
+ *stop = 1;
+}
+
+static krb5_error_code
+wait_response(krb5_context context, int *action, krb5_sendto_ctx ctx)
{
- ctx->func = func;
- ctx->data = data;
+ struct wait_ctx wait_ctx;
+ struct timeval tv;
+ int ret;
+
+ wait_ctx.context = context;
+ wait_ctx.ctx = ctx;
+ FD_ZERO(&wait_ctx.rfds);
+ FD_ZERO(&wait_ctx.wfds);
+ wait_ctx.max_fd = 0;
+
+ /* oh, we have a reply, it must be a plugin that got it for us */
+ if (ctx->response.length) {
+ *action = KRB5_SENDTO_FILTER;
+ return 0;
+ }
+
+ wait_ctx.timenow = time(NULL);
+
+ heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_setup);
+ heim_array_filter_f(ctx->hosts, &wait_ctx, wait_filter_dead);
+
+ if (heim_array_get_length(ctx->hosts) == 0) {
+ if (ctx->stateflags & KRBHST_COMPLETED) {
+ _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
+ "trying to pulling more hosts");
+ *action = KRB5_SENDTO_FAILED;
+ } else {
+ _krb5_debug(context, 5, "no more hosts to send/recv packets to/from "
+ "and no more hosts -> failure");
+ *action = KRB5_SENDTO_TIMEOUT;
+ }
+ return 0;
+ }
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ ret = select(wait_ctx.max_fd + 1, &wait_ctx.rfds, &wait_ctx.wfds, NULL, &tv);
+ if (ret < 0)
+ return errno;
+ if (ret == 0) {
+ *action = KRB5_SENDTO_TIMEOUT;
+ return 0;
+ }
+
+ wait_ctx.got_reply = 0;
+ heim_array_iterate_f(ctx->hosts, &wait_ctx, wait_process);
+ if (wait_ctx.got_reply)
+ *action = KRB5_SENDTO_FILTER;
+ else
+ *action = KRB5_SENDTO_CONTINUE;
+
+ return 0;
}
-KRB5_LIB_FUNCTION void KRB5_LIB_CALL
-krb5_sendto_ctx_free(krb5_context context, krb5_sendto_ctx ctx)
+static void
+reset_context(krb5_context context, krb5_sendto_ctx ctx)
{
- memset(ctx, 0, sizeof(*ctx));
- free(ctx);
+ krb5_data_free(&ctx->response);
+ heim_release(ctx->hosts);
+ ctx->hosts = heim_array_create();
+ ctx->stateflags = 0;
}
+
+/*
+ *
+ */
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sendto_context(krb5_context context,
krb5_sendto_ctx ctx,
const krb5_data *send_data,
- const krb5_realm realm,
+ krb5_const_realm realm,
krb5_data *receive)
{
- krb5_error_code ret;
+ krb5_error_code ret = 0;
krb5_krbhst_handle handle = NULL;
+ struct timeval nrstart, nrstop, stop_time;
int type, freectx = 0;
int action;
+ int numreset = 0;
krb5_data_zero(receive);
-
+
if (ctx == NULL) {
- freectx = 1;
ret = krb5_sendto_ctx_alloc(context, &ctx);
if (ret)
- return ret;
+ goto out;
+ freectx = 1;
}
+ ctx->stid = (context->num_kdc_requests++) << 16;
+
+ memset(&ctx->stats, 0, sizeof(ctx->stats));
+ gettimeofday(&ctx->stats.start_time, NULL);
+
type = ctx->type;
if (type == 0) {
if ((ctx->flags & KRB5_KRBHST_FLAGS_MASTER) || context->use_admin_kdc)
@@ -602,76 +1104,165 @@ krb5_sendto_context(krb5_context context,
type = KRB5_KRBHST_KDC;
}
+ ctx->send_data = send_data;
+
if ((int)send_data->length > context->large_msg_size)
ctx->flags |= KRB5_KRBHST_FLAGS_LARGE_MSG;
/* loop until we get back a appropriate response */
- do {
- action = KRB5_SENDTO_DONE;
+ action = KRB5_SENDTO_INITIAL;
- krb5_data_free(receive);
+ while (action != KRB5_SENDTO_DONE && action != KRB5_SENDTO_FAILED) {
+ krb5_krbhst_info *hi;
- if (handle == NULL) {
- ret = krb5_krbhst_init_flags(context, realm, type,
- ctx->flags, &handle);
- if (ret) {
- if (freectx)
- krb5_sendto_ctx_free(context, ctx);
- return ret;
+ switch (action) {
+ case KRB5_SENDTO_INITIAL:
+ ret = realm_via_plugin(context, realm, context->kdc_timeout,
+ send_data, &ctx->response);
+ if (ret == 0 || ret != KRB5_PLUGIN_NO_HANDLE) {
+ action = KRB5_SENDTO_DONE;
+ break;
+ }
+ action = KRB5_SENDTO_KRBHST;
+ /* FALLTHOUGH */
+ case KRB5_SENDTO_KRBHST:
+ if (ctx->krbhst == NULL) {
+ ret = krb5_krbhst_init_flags(context, realm, type,
+ ctx->flags, &handle);
+ if (ret)
+ goto out;
+
+ if (ctx->hostname) {
+ ret = krb5_krbhst_set_hostname(context, handle, ctx->hostname);
+ if (ret)
+ goto out;
+ }
+
+ } else {
+ handle = heim_retain(ctx->krbhst);
+ }
+ action = KRB5_SENDTO_TIMEOUT;
+ /* FALLTHOUGH */
+ case KRB5_SENDTO_TIMEOUT:
+
+ /*
+ * If we completed, just got to next step
+ */
+
+ if (ctx->stateflags & KRBHST_COMPLETED) {
+ action = KRB5_SENDTO_CONTINUE;
+ break;
+ }
+
+ /*
+ * Pull out next host, if there is no more, close the
+ * handle and mark as completed.
+ *
+ * Collect time spent in krbhst (dns, plugin, etc)
+ */
+
+
+ gettimeofday(&nrstart, NULL);
+
+ ret = krb5_krbhst_next(context, handle, &hi);
+
+ gettimeofday(&nrstop, NULL);
+ timevalsub(&nrstop, &nrstart);
+ timevaladd(&ctx->stats.krbhst, &nrstop);
+
+ action = KRB5_SENDTO_CONTINUE;
+ if (ret == 0) {
+ _krb5_debug(context, 5, "submissing new requests to new host");
+ if (submit_request(context, ctx, hi) != 0)
+ action = KRB5_SENDTO_TIMEOUT;
+ } else {
+ _krb5_debug(context, 5, "out of hosts, waiting for replies");
+ ctx->stateflags |= KRBHST_COMPLETED;
}
- }
- ret = krb5_sendto(context, send_data, handle, receive);
- if (ret)
break;
- if (ctx->func) {
- ret = (*ctx->func)(context, ctx, ctx->data, receive, &action);
+ case KRB5_SENDTO_CONTINUE:
+
+ ret = wait_response(context, &action, ctx);
if (ret)
- break;
- }
- if (action != KRB5_SENDTO_CONTINUE) {
- krb5_krbhst_free(context, handle);
- handle = NULL;
+ goto out;
+
+ break;
+ case KRB5_SENDTO_RESET:
+ /* start over */
+ _krb5_debug(context, 5,
+ "krb5_sendto trying over again (reset): %d",
+ numreset);
+ reset_context(context, ctx);
+ if (handle) {
+ krb5_krbhst_free(context, handle);
+ handle = NULL;
+ }
+ numreset++;
+ if (numreset >= 3)
+ action = KRB5_SENDTO_FAILED;
+ else
+ action = KRB5_SENDTO_KRBHST;
+
+ break;
+ case KRB5_SENDTO_FILTER:
+ /* default to next state, the filter function might modify this */
+ action = KRB5_SENDTO_DONE;
+
+ if (ctx->func) {
+ ret = (*ctx->func)(context, ctx, ctx->data,
+ &ctx->response, &action);
+ if (ret)
+ goto out;
+ }
+ break;
+ case KRB5_SENDTO_FAILED:
+ ret = KRB5_KDC_UNREACH;
+ break;
+ case KRB5_SENDTO_DONE:
+ ret = 0;
+ break;
+ default:
+ heim_abort("invalid krb5_sendto_context state");
}
- } while (action != KRB5_SENDTO_DONE);
- if (handle)
- krb5_krbhst_free(context, handle);
- if (ret == KRB5_KDC_UNREACH)
+ }
+
+out:
+ gettimeofday(&stop_time, NULL);
+ timevalsub(&stop_time, &ctx->stats.start_time);
+ if (ret == 0 && ctx->response.length) {
+ *receive = ctx->response;
+ krb5_data_zero(&ctx->response);
+ } else {
+ krb5_data_free(&ctx->response);
+ krb5_clear_error_message (context);
+ ret = KRB5_KDC_UNREACH;
krb5_set_error_message(context, ret,
N_("unable to reach any KDC in realm %s", ""),
realm);
- if (ret)
- krb5_data_free(receive);
- if (freectx)
- krb5_sendto_ctx_free(context, ctx);
- return ret;
-}
+ }
-krb5_error_code KRB5_CALLCONV
-_krb5_kdc_retry(krb5_context context, krb5_sendto_ctx ctx, void *data,
- const krb5_data *reply, int *action)
-{
- krb5_error_code ret;
- KRB_ERROR error;
+ _krb5_debug(context, 1,
+ "%s %s done: %d hosts: %lu packets: %lu"
+ " wc: %lld.%06lu nr: %lld.%06lu kh: %lld.%06lu tid: %08x",
+ __func__, realm, ret,
+ ctx->stats.num_hosts, ctx->stats.sent_packets,
+ (long long)stop_time.tv_sec,
+ (unsigned long)stop_time.tv_usec,
+ (long long)ctx->stats.name_resolution.tv_sec,
+ (unsigned long)ctx->stats.name_resolution.tv_usec,
+ (long long)ctx->stats.krbhst.tv_sec,
+ (unsigned long)ctx->stats.krbhst.tv_usec, ctx->stid);
- if(krb5_rd_error(context, reply, &error))
- return 0;
- ret = krb5_error_from_rd_error(context, &error, NULL);
- krb5_free_error_contents(context, &error);
+ if (freectx)
+ krb5_sendto_ctx_free(context, ctx);
+ else
+ reset_context(context, ctx);
- switch(ret) {
- case KRB5KRB_ERR_RESPONSE_TOO_BIG: {
- if (krb5_sendto_ctx_get_flags(ctx) & KRB5_KRBHST_FLAGS_LARGE_MSG)
- break;
- krb5_sendto_ctx_add_flags(ctx, KRB5_KRBHST_FLAGS_LARGE_MSG);
- *action = KRB5_SENDTO_RESTART;
- break;
- }
- case KRB5KDC_ERR_SVC_UNAVAILABLE:
- *action = KRB5_SENDTO_CONTINUE;
- break;
- }
- return 0;
+ if (handle)
+ krb5_krbhst_free(context, handle);
+
+ return ret;
}
diff --git a/lib/krb5/send_to_kdc_plugin.h b/lib/krb5/send_to_kdc_plugin.h
index c729a1286bae..11712b2747ac 100644
--- a/lib/krb5/send_to_kdc_plugin.h
+++ b/lib/krb5/send_to_kdc_plugin.h
@@ -40,6 +40,10 @@
#define KRB5_PLUGIN_SEND_TO_KDC "send_to_kdc"
+#define KRB5_PLUGIN_SEND_TO_KDC_VERSION_0 0
+#define KRB5_PLUGIN_SEND_TO_KDC_VERSION_2 2
+#define KRB5_PLUGIN_SEND_TO_KDC_VERSION KRB5_PLUGIN_SEND_TO_KDC_VERSION_2
+
typedef krb5_error_code
(*krb5plugin_send_to_kdc_func)(krb5_context,
void *,
@@ -47,12 +51,21 @@ typedef krb5_error_code
time_t timeout,
const krb5_data *,
krb5_data *);
+typedef krb5_error_code
+(*krb5plugin_send_to_realm_func)(krb5_context,
+ void *,
+ krb5_const_realm,
+ time_t timeout,
+ const krb5_data *,
+ krb5_data *);
+
typedef struct krb5plugin_send_to_kdc_ftable {
int minor_version;
krb5_error_code (*init)(krb5_context, void **);
void (*fini)(void *);
krb5plugin_send_to_kdc_func send_to_kdc;
+ krb5plugin_send_to_realm_func send_to_realm; /* added in version 2 */
} krb5plugin_send_to_kdc_ftable;
#endif /* HEIMDAL_KRB5_SEND_TO_KDC_PLUGIN_H */
diff --git a/lib/krb5/sendauth.c b/lib/krb5/sendauth.c
index d334d34497ab..5011c2680892 100644
--- a/lib/krb5/sendauth.c
+++ b/lib/krb5/sendauth.c
@@ -60,6 +60,29 @@
* }
*/
+/**
+ * Perform the client side of the sendauth protocol.
+ *
+ * @param context Kerberos 5 context.
+ * @param auth_context Authentication context of the peer.
+ * @param p_fd Socket associated to the connection.
+ * @param appl_version Server-specific string.
+ * @param client Client principal. If NULL, use the credentials in \a ccache.
+ * @param server Server principal.
+ * @param ap_req_options Options for the AP_REQ message. See the AP_OPTS_* defines in krb5.h.
+ * @param in_data FIXME
+ * @param in_creds FIXME
+ * @param ccache Credentials cache. If NULL, use the default credentials cache.
+ * @param ret_error If not NULL, will be set to the error reported by server, if any.
+ * Must be deallocated with krb5_free_error_contents().
+ * @param rep_result If not NULL, will be set to the EncApRepPart of the AP_REP message.
+ * Must be deallocated with krb5_free_ap_rep_enc_part().
+ * @param out_creds FIXME If not NULL, will be set to FIXME. Must be deallocated with
+ * krb5_free_creds().
+ *
+ * @return 0 to indicate success. Otherwise a Kerberos error code is
+ * returned, see krb5_get_error_message().
+ */
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sendauth(krb5_context context,
krb5_auth_context *auth_context,
diff --git a/lib/krb5/set_default_realm.c b/lib/krb5/set_default_realm.c
index ddce677c1ac8..b3450c40b327 100644
--- a/lib/krb5/set_default_realm.c
+++ b/lib/krb5/set_default_realm.c
@@ -43,17 +43,12 @@ string_to_list (krb5_context context, const char *s, krb5_realm **list)
{
*list = malloc (2 * sizeof(**list));
- if (*list == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (*list == NULL)
+ return krb5_enomem(context);
(*list)[0] = strdup (s);
if ((*list)[0] == NULL) {
free (*list);
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
+ return krb5_enomem(context);
}
(*list)[1] = NULL;
return 0;
diff --git a/lib/krb5/sp800-108-kdf.c b/lib/krb5/sp800-108-kdf.c
new file mode 100755
index 000000000000..37e06dec3e84
--- /dev/null
+++ b/lib/krb5/sp800-108-kdf.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2015, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "krb5_locl.h"
+
+/*
+ * SP800-108 KDF
+ */
+
+/**
+ * As described in SP800-108 5.1 (for HMAC)
+ *
+ * @param context Kerberos 5 context
+ * @param kdf_K1 Base key material.
+ * @param kdf_label A string that identifies the purpose for the derived key.
+ * @param kdf_context A binary string containing parties, nonce, etc.
+ * @param md Message digest function to use for PRF.
+ * @param kdf_K0 Derived key data.
+ *
+ * @return Return an error code for an failure or 0 on success.
+ * @ingroup krb5_crypto
+ */
+krb5_error_code
+_krb5_SP800_108_HMAC_KDF(krb5_context context,
+ const krb5_data *kdf_K1,
+ const krb5_data *kdf_label,
+ const krb5_data *kdf_context,
+ const EVP_MD *md,
+ krb5_data *kdf_K0)
+{
+ HMAC_CTX c;
+ unsigned char *p = kdf_K0->data;
+ size_t i, n, left = kdf_K0->length;
+ unsigned char hmac[EVP_MAX_MD_SIZE];
+ unsigned int h = EVP_MD_size(md);
+ const size_t L = kdf_K0->length;
+
+ heim_assert(md != NULL, "SP800-108 KDF internal error");
+
+ HMAC_CTX_init(&c);
+
+ n = L / h;
+
+ for (i = 0; i <= n; i++) {
+ unsigned char tmp[4];
+ size_t len;
+
+ HMAC_Init_ex(&c, kdf_K1->data, kdf_K1->length, md, NULL);
+
+ _krb5_put_int(tmp, i + 1, 4);
+ HMAC_Update(&c, tmp, 4);
+ HMAC_Update(&c, kdf_label->data, kdf_label->length);
+ HMAC_Update(&c, (unsigned char *)"", 1);
+ if (kdf_context)
+ HMAC_Update(&c, kdf_context->data, kdf_context->length);
+ _krb5_put_int(tmp, L * 8, 4);
+ HMAC_Update(&c, tmp, 4);
+
+ HMAC_Final(&c, hmac, &h);
+ len = h > left ? left : h;
+ memcpy(p, hmac, len);
+ p += len;
+ left -= len;
+ }
+
+ HMAC_CTX_cleanup(&c);
+
+ return 0;
+}
diff --git a/lib/krb5/store-int.c b/lib/krb5/store-int.c
index d57762971816..542b99abc089 100644
--- a/lib/krb5/store-int.c
+++ b/lib/krb5/store-int.c
@@ -34,7 +34,7 @@
#include "krb5_locl.h"
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
-_krb5_put_int(void *buffer, unsigned long value, size_t size)
+_krb5_put_int(void *buffer, uint64_t value, size_t size)
{
unsigned char *p = buffer;
int i;
@@ -46,7 +46,7 @@ _krb5_put_int(void *buffer, unsigned long value, size_t size)
}
KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
-_krb5_get_int(void *buffer, unsigned long *value, size_t size)
+_krb5_get_int64(void *buffer, uint64_t *value, size_t size)
{
unsigned char *p = buffer;
unsigned long v = 0;
@@ -56,3 +56,12 @@ _krb5_get_int(void *buffer, unsigned long *value, size_t size)
*value = v;
return size;
}
+
+KRB5_LIB_FUNCTION krb5_ssize_t KRB5_LIB_CALL
+_krb5_get_int(void *buffer, unsigned long *value, size_t size)
+{
+ uint64_t v64;
+ krb5_ssize_t bytes = _krb5_get_int64(buffer, &v64, size);
+ *value = v64;
+ return bytes;
+}
diff --git a/lib/krb5/store-int.h b/lib/krb5/store-int.h
index 877ccc008dc9..2762d96fddce 100644
--- a/lib/krb5/store-int.h
+++ b/lib/krb5/store-int.h
@@ -40,6 +40,7 @@ struct krb5_storage_data {
ssize_t (*store)(struct krb5_storage_data*, const void*, size_t);
off_t (*seek)(struct krb5_storage_data*, off_t, int);
int (*trunc)(struct krb5_storage_data*, off_t);
+ int (*fsync)(struct krb5_storage_data*);
void (*free)(struct krb5_storage_data*);
krb5_flags flags;
int eof_code;
diff --git a/lib/krb5/store.c b/lib/krb5/store.c
index 3aeb8d628144..c7355f6861bc 100644
--- a/lib/krb5/store.c
+++ b/lib/krb5/store.c
@@ -191,6 +191,25 @@ krb5_storage_truncate(krb5_storage *sp, off_t offset)
}
/**
+ * Sync the storage buffer to its backing store. If there is no
+ * backing store this function will return success.
+ *
+ * @param sp the storage buffer to sync
+ *
+ * @return A Kerberos 5 error code
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
+krb5_storage_fsync(krb5_storage *sp)
+{
+ if (sp->fsync != NULL)
+ return sp->fsync(sp);
+ return 0;
+}
+
+/**
* Read to the storage buffer.
*
* @param sp the storage buffer to read from
@@ -270,6 +289,8 @@ krb5_storage_get_eof_code(krb5_storage *sp)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_storage_free(krb5_storage *sp)
{
+ if (sp == NULL)
+ return 0;
if(sp->free)
(*sp->free)(sp);
free(sp->data);
@@ -316,13 +337,13 @@ krb5_storage_to_data(krb5_storage *sp, krb5_data *data)
static krb5_error_code
krb5_store_int(krb5_storage *sp,
- int32_t value,
+ int64_t value,
size_t len)
{
int ret;
- unsigned char v[16];
+ unsigned char v[8];
- if(len > sizeof(v))
+ if (len > sizeof(v))
return EINVAL;
_krb5_put_int(v, value, len);
ret = sp->store(sp, v, len);
@@ -357,6 +378,33 @@ krb5_store_int32(krb5_storage *sp,
}
/**
+ * Store a int64 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_int64(krb5_storage *sp,
+ int64_t value)
+{
+ if (BYTEORDER_IS_HOST(sp))
+#ifdef WORDS_BIGENDIAN
+ ;
+#else
+ value = bswap64(value); /* There's no ntohll() */
+#endif
+ else if (BYTEORDER_IS_LE(sp))
+ value = bswap64(value);
+ return krb5_store_int(sp, value, 8);
+}
+
+/**
* Store a uint32 to storage, byte order is controlled by the settings
* on the storage, see krb5_storage_set_byteorder().
*
@@ -375,25 +423,101 @@ krb5_store_uint32(krb5_storage *sp,
return krb5_store_int32(sp, (int32_t)value);
}
+/**
+ * Store a uint64 to storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value to store
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_store_uint64(krb5_storage *sp,
+ uint64_t value)
+{
+ return krb5_store_int64(sp, (int64_t)value);
+}
+
static krb5_error_code
krb5_ret_int(krb5_storage *sp,
- int32_t *value,
+ int64_t *value,
size_t len)
{
int ret;
- unsigned char v[4];
- unsigned long w;
+ unsigned char v[8];
+ uint64_t w;
+ *value = 0; /* quiets warnings */
ret = sp->fetch(sp, v, len);
if (ret < 0)
return errno;
if ((size_t)ret != len)
return sp->eof_code;
- _krb5_get_int(v, &w, len);
+ _krb5_get_int64(v, &w, len);
*value = w;
return 0;
}
/**
+ * Read a int64 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_int64(krb5_storage *sp,
+ int64_t *value)
+{
+ krb5_error_code ret = krb5_ret_int(sp, value, 8);
+ if(ret)
+ return ret;
+ if(BYTEORDER_IS_HOST(sp))
+#ifdef WORDS_BIGENDIAN
+ ;
+#else
+ *value = bswap64(*value); /* There's no ntohll() */
+#endif
+ else if(BYTEORDER_IS_LE(sp))
+ *value = bswap64(*value);
+ return 0;
+}
+
+/**
+ * Read a uint64 from storage, byte order is controlled by the settings
+ * on the storage, see krb5_storage_set_byteorder().
+ *
+ * @param sp the storage to write too
+ * @param value the value read from the buffer
+ *
+ * @return 0 for success, or a Kerberos 5 error code on failure.
+ *
+ * @ingroup krb5_storage
+ */
+
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+krb5_ret_uint64(krb5_storage *sp,
+ uint64_t *value)
+{
+ krb5_error_code ret;
+ int64_t v;
+
+ ret = krb5_ret_int64(sp, &v);
+ if (ret == 0)
+ *value = (uint64_t)v;
+
+ return ret;
+}
+
+/**
* Read a int32 from storage, byte order is controlled by the settings
* on the storage, see krb5_storage_set_byteorder().
*
@@ -409,12 +533,15 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_int32(krb5_storage *sp,
int32_t *value)
{
- krb5_error_code ret = krb5_ret_int(sp, value, 4);
- if(ret)
+ int64_t v;
+
+ krb5_error_code ret = krb5_ret_int(sp, &v, 4);
+ if (ret)
return ret;
- if(BYTEORDER_IS_HOST(sp))
+ *value = v;
+ if (BYTEORDER_IS_HOST(sp))
*value = htonl(*value);
- else if(BYTEORDER_IS_LE(sp))
+ else if (BYTEORDER_IS_LE(sp))
*value = bswap32(*value);
return 0;
}
@@ -432,8 +559,7 @@ krb5_ret_int32(krb5_storage *sp,
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
-krb5_ret_uint32(krb5_storage *sp,
- uint32_t *value)
+krb5_ret_uint32(krb5_storage *sp, uint32_t *value)
{
krb5_error_code ret;
int32_t v;
@@ -503,7 +629,7 @@ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_ret_int16(krb5_storage *sp,
int16_t *value)
{
- int32_t v;
+ int64_t v;
int ret;
ret = krb5_ret_int(sp, &v, 2);
if(ret)
@@ -688,8 +814,10 @@ krb5_ret_data(krb5_storage *sp,
return ret;
if (size) {
ret = sp->fetch(sp, data->data, size);
- if(ret != size)
+ if(ret != size) {
+ krb5_data_free(data);
return (ret < 0)? errno : sp->eof_code;
+ }
}
return 0;
}
@@ -792,12 +920,15 @@ krb5_ret_stringz(krb5_storage *sp,
ssize_t ret;
while((ret = sp->fetch(sp, &c, 1)) == 1){
+ krb5_error_code eret;
char *tmp;
len++;
- ret = size_too_large(sp, len);
- if (ret)
- break;
+ eret = size_too_large(sp, len);
+ if (eret) {
+ free(s);
+ return eret;
+ }
tmp = realloc (s, len);
if (tmp == NULL) {
free (s);
@@ -852,6 +983,7 @@ krb5_ret_stringnl(krb5_storage *sp,
ssize_t ret;
while((ret = sp->fetch(sp, &c, 1)) == 1){
+ krb5_error_code eret;
char *tmp;
if (c == '\r') {
@@ -864,9 +996,11 @@ krb5_ret_stringnl(krb5_storage *sp,
}
len++;
- ret = size_too_large(sp, len);
- if (ret)
- break;
+ eret = size_too_large(sp, len);
+ if (eret) {
+ free(s);
+ return eret;
+ }
tmp = realloc (s, len);
if (tmp == NULL) {
free (s);
@@ -1320,14 +1454,9 @@ krb5_store_creds(krb5_storage *sp, krb5_creds *creds)
ret = krb5_store_int8(sp, creds->second_ticket.length != 0); /* is_skey */
if(ret)
return ret;
-
- if(krb5_storage_is_flags(sp, KRB5_STORAGE_CREDS_FLAGS_WRONG_BITORDER))
- ret = krb5_store_int32(sp, creds->flags.i);
- else
- ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
+ ret = krb5_store_int32(sp, bitswap32(TicketFlags2int(creds->flags.b)));
if(ret)
return ret;
-
ret = krb5_store_addrs(sp, creds->addresses);
if(ret)
return ret;
@@ -1372,23 +1501,7 @@ krb5_ret_creds(krb5_storage *sp, krb5_creds *creds)
if(ret) goto cleanup;
ret = krb5_ret_int32 (sp, &dummy32);
if(ret) goto cleanup;
- /*
- * Runtime detect the what is the higher bits of the bitfield. If
- * any of the higher bits are set in the input data, it's either a
- * new ticket flag (and this code need to be removed), or it's a
- * MIT cache (or new Heimdal cache), lets change it to our current
- * format.
- */
- {
- uint32_t mask = 0xffff0000;
- creds->flags.i = 0;
- creds->flags.b.anonymous = 1;
- if (creds->flags.i & mask)
- mask = ~mask;
- if (dummy32 & mask)
- dummy32 = bitswap32(dummy32);
- }
- creds->flags.i = dummy32;
+ creds->flags.b = int2TicketFlags(bitswap32(dummy32));
ret = krb5_ret_addrs (sp, &creds->addresses);
if(ret) goto cleanup;
ret = krb5_ret_authdata (sp, &creds->authdata);
@@ -1547,23 +1660,7 @@ krb5_ret_creds_tag(krb5_storage *sp,
if(ret) goto cleanup;
ret = krb5_ret_int32 (sp, &dummy32);
if(ret) goto cleanup;
- /*
- * Runtime detect the what is the higher bits of the bitfield. If
- * any of the higher bits are set in the input data, it's either a
- * new ticket flag (and this code need to be removed), or it's a
- * MIT cache (or new Heimdal cache), lets change it to our current
- * format.
- */
- {
- uint32_t mask = 0xffff0000;
- creds->flags.i = 0;
- creds->flags.b.anonymous = 1;
- if (creds->flags.i & mask)
- mask = ~mask;
- if (dummy32 & mask)
- dummy32 = bitswap32(dummy32);
- }
- creds->flags.i = dummy32;
+ creds->flags.b = int2TicketFlags(bitswap32(dummy32));
if (header & SC_ADDRESSES) {
ret = krb5_ret_addrs (sp, &creds->addresses);
if(ret) goto cleanup;
diff --git a/lib/krb5/store_emem.c b/lib/krb5/store_emem.c
index 7f91b0848627..6d95bcf525a8 100644
--- a/lib/krb5/store_emem.c
+++ b/lib/krb5/store_emem.c
@@ -156,6 +156,7 @@ emem_free(krb5_storage *sp)
* @sa krb5_storage_from_readonly_mem()
* @sa krb5_storage_from_fd()
* @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_socket()
*/
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
@@ -189,6 +190,7 @@ krb5_storage_emem(void)
sp->store = emem_store;
sp->seek = emem_seek;
sp->trunc = emem_trunc;
+ sp->fsync = NULL;
sp->free = emem_free;
sp->max_alloc = UINT_MAX/8;
return sp;
diff --git a/lib/krb5/store_fd.c b/lib/krb5/store_fd.c
index 2b72dea3a3fc..ead18be67aa2 100644
--- a/lib/krb5/store_fd.c
+++ b/lib/krb5/store_fd.c
@@ -43,13 +43,47 @@ typedef struct fd_storage {
static ssize_t
fd_fetch(krb5_storage * sp, void *data, size_t size)
{
- return net_read(FD(sp), data, size);
+ char *cbuf = (char *)data;
+ ssize_t count;
+ size_t rem = size;
+
+ /* similar pattern to net_read() to support pipes */
+ while (rem > 0) {
+ count = read (FD(sp), cbuf, rem);
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ } else if (count == 0) {
+ return count;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ return size;
}
static ssize_t
fd_store(krb5_storage * sp, const void *data, size_t size)
{
- return net_write(FD(sp), data, size);
+ const char *cbuf = (const char *)data;
+ ssize_t count;
+ size_t rem = size;
+
+ /* similar pattern to net_write() to support pipes */
+ while (rem > 0) {
+ count = write(FD(sp), cbuf, rem);
+ if (count < 0) {
+ if (errno == EINTR)
+ continue;
+ else
+ return count;
+ }
+ cbuf += count;
+ rem -= count;
+ }
+ return size;
}
static off_t
@@ -66,10 +100,20 @@ fd_trunc(krb5_storage * sp, off_t offset)
return 0;
}
+static int
+fd_sync(krb5_storage * sp)
+{
+ if (fsync(FD(sp)) == -1)
+ return errno;
+ return 0;
+}
+
static void
fd_free(krb5_storage * sp)
{
- close(FD(sp));
+ int save_errno = errno;
+ if (close(FD(sp)) == 0)
+ errno = save_errno;
}
/**
@@ -83,41 +127,48 @@ fd_free(krb5_storage * sp)
* @sa krb5_storage_from_mem()
* @sa krb5_storage_from_readonly_mem()
* @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_socket()
*/
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
-krb5_storage_from_fd(krb5_socket_t fd_in)
+krb5_storage_from_fd(int fd_in)
{
krb5_storage *sp;
+ int saved_errno;
int fd;
-#ifdef SOCKET_IS_NOT_AN_FD
#ifdef _MSC_VER
- if (_get_osfhandle(fd_in) != -1) {
- fd = dup(fd_in);
- } else {
- fd = _open_osfhandle(fd_in, 0);
- }
+ /*
+ * This function used to try to pass the input to
+ * _get_osfhandle() to test if the value is a HANDLE
+ * but this doesn't work because doing so throws an
+ * exception that will result in Watson being triggered
+ * to file a Windows Error Report.
+ */
+ fd = _dup(fd_in);
#else
-#error Dont know how to deal with fd that may or may not be a socket.
-#endif
-#else /* SOCKET_IS_NOT_AN_FD */
fd = dup(fd_in);
#endif
if (fd < 0)
return NULL;
+ errno = ENOMEM;
sp = malloc(sizeof(krb5_storage));
if (sp == NULL) {
+ saved_errno = errno;
close(fd);
+ errno = saved_errno;
return NULL;
}
+ errno = ENOMEM;
sp->data = malloc(sizeof(fd_storage));
if (sp->data == NULL) {
+ saved_errno = errno;
close(fd);
free(sp);
+ errno = saved_errno;
return NULL;
}
sp->flags = 0;
@@ -127,6 +178,7 @@ krb5_storage_from_fd(krb5_socket_t fd_in)
sp->store = fd_store;
sp->seek = fd_seek;
sp->trunc = fd_trunc;
+ sp->fsync = fd_sync;
sp->free = fd_free;
sp->max_alloc = UINT_MAX/8;
return sp;
diff --git a/lib/krb5/store_mem.c b/lib/krb5/store_mem.c
index e674a95dbad0..ff2a570ca62c 100644
--- a/lib/krb5/store_mem.c
+++ b/lib/krb5/store_mem.c
@@ -120,6 +120,7 @@ mem_no_trunc(krb5_storage *sp, off_t offset)
* @sa krb5_storage_from_readonly_mem()
* @sa krb5_storage_from_data()
* @sa krb5_storage_from_fd()
+ * @sa krb5_storage_from_socket()
*/
KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
@@ -144,6 +145,7 @@ krb5_storage_from_mem(void *buf, size_t len)
sp->store = mem_store;
sp->seek = mem_seek;
sp->trunc = mem_trunc;
+ sp->fsync = NULL;
sp->free = NULL;
sp->max_alloc = UINT_MAX/8;
return sp;
@@ -203,6 +205,7 @@ krb5_storage_from_readonly_mem(const void *buf, size_t len)
sp->store = mem_no_store;
sp->seek = mem_seek;
sp->trunc = mem_no_trunc;
+ sp->fsync = NULL;
sp->free = NULL;
sp->max_alloc = UINT_MAX/8;
return sp;
diff --git a/lib/krb5/store_sock.c b/lib/krb5/store_sock.c
new file mode 100644
index 000000000000..f7dc2256d729
--- /dev/null
+++ b/lib/krb5/store_sock.c
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1997 - 2004 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "krb5_locl.h"
+#include "store-int.h"
+
+#ifdef _WIN32
+#include <winsock2.h>
+#endif
+
+typedef struct socket_storage {
+ krb5_socket_t sock;
+} socket_storage;
+
+#define SOCK(S) (((socket_storage*)(S)->data)->sock)
+
+static ssize_t
+socket_fetch(krb5_storage * sp, void *data, size_t size)
+{
+ return net_read(SOCK(sp), data, size);
+}
+
+static ssize_t
+socket_store(krb5_storage * sp, const void *data, size_t size)
+{
+ return net_write(SOCK(sp), data, size);
+}
+
+static off_t
+socket_seek(krb5_storage * sp, off_t offset, int whence)
+{
+ return lseek(SOCK(sp), offset, whence);
+}
+
+static int
+socket_trunc(krb5_storage * sp, off_t offset)
+{
+ if (ftruncate(SOCK(sp), offset) == -1)
+ return errno;
+ return 0;
+}
+
+static int
+socket_sync(krb5_storage * sp)
+{
+ if (fsync(SOCK(sp)) == -1)
+ return errno;
+ return 0;
+}
+
+static void
+socket_free(krb5_storage * sp)
+{
+ int save_errno = errno;
+ if (rk_IS_SOCKET_ERROR(rk_closesocket(SOCK(sp))))
+ errno = rk_SOCK_ERRNO;
+ else
+ errno = save_errno;
+}
+
+/**
+ *
+ *
+ * @return A krb5_storage on success, or NULL on out of memory error.
+ *
+ * @ingroup krb5_storage
+ *
+ * @sa krb5_storage_emem()
+ * @sa krb5_storage_from_mem()
+ * @sa krb5_storage_from_readonly_mem()
+ * @sa krb5_storage_from_data()
+ * @sa krb5_storage_from_fd()
+ */
+
+KRB5_LIB_FUNCTION krb5_storage * KRB5_LIB_CALL
+krb5_storage_from_socket(krb5_socket_t sock_in)
+{
+ krb5_storage *sp;
+ int saved_errno;
+ krb5_socket_t sock;
+
+#ifdef _WIN32
+ WSAPROTOCOL_INFO info;
+
+ if (WSADuplicateSocket(sock_in, GetCurrentProcessId(), &info) == 0)
+ {
+
+ sock = WSASocket( FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ &info, 0, 0);
+ }
+#else
+ sock = dup(sock_in);
+#endif
+
+ if (sock == rk_INVALID_SOCKET)
+ return NULL;
+
+ errno = ENOMEM;
+ sp = malloc(sizeof(krb5_storage));
+ if (sp == NULL) {
+ saved_errno = errno;
+ rk_closesocket(sock);
+ errno = saved_errno;
+ return NULL;
+ }
+
+ errno = ENOMEM;
+ sp->data = malloc(sizeof(socket_storage));
+ if (sp->data == NULL) {
+ saved_errno = errno;
+ rk_closesocket(sock);
+ free(sp);
+ errno = saved_errno;
+ return NULL;
+ }
+ sp->flags = 0;
+ sp->eof_code = HEIM_ERR_EOF;
+ SOCK(sp) = sock;
+ sp->fetch = socket_fetch;
+ sp->store = socket_store;
+ sp->seek = socket_seek;
+ sp->trunc = socket_trunc;
+ sp->fsync = socket_sync;
+ sp->free = socket_free;
+ sp->max_alloc = UINT_MAX/8;
+ return sp;
+}
diff --git a/lib/krb5/string-to-key-test.c b/lib/krb5/string-to-key-test.c
index cb7081b9e0f4..898857591e0c 100644
--- a/lib/krb5/string-to-key-test.c
+++ b/lib/krb5/string-to-key-test.c
@@ -86,7 +86,7 @@ static struct testcase {
{0x6d, 0x2f, 0xcd, 0xf2, 0xd6, 0xfb, 0xbc, 0x3d, 0xdc, 0xad, 0xb5, 0xda, 0x57, 0x10, 0xa2, 0x34, 0x89, 0xb0, 0xd3, 0xb6, 0x9d, 0x5d, 0x9d, 0x4a}},
{"Juri\xc5\xa1i\xc4\x87@ATHENA.MIT.EDU", "\xc3\x9f", ETYPE_DES3_CBC_SHA1,
{0x16, 0xd5, 0xa4, 0x0e, 0x1c, 0xe3, 0xba, 0xcb, 0x61, 0xb9, 0xdc, 0xe0, 0x04, 0x70, 0x32, 0x4c, 0x83, 0x19, 0x73, 0xa7, 0xb9, 0x52, 0xfe, 0xb0}},
- {NULL}
+ {NULL, NULL, 0, {0}}
};
int
diff --git a/lib/krb5/test_alname.c b/lib/krb5/test_alname.c
index 21e4bff8d82c..120143e51e7b 100644
--- a/lib/krb5/test_alname.c
+++ b/lib/krb5/test_alname.c
@@ -34,13 +34,33 @@
#include <getarg.h>
#include <err.h>
+char localname[1024];
+static size_t lname_size = sizeof (localname);
+static int lname_size_arg = 0;
+static int simple_flag = 0;
+static int verbose_flag = 0;
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"lname-size", 0, arg_integer, &lname_size_arg,
+ "set localname size (0 means use default, must be 0..1023)", "integer" },
+ {"simple", 0, arg_flag, &simple_flag, /* Used for scripting */
+ "map the given principal and print the resulting localname", NULL },
+ {"verbose", 0, arg_flag, &verbose_flag,
+ "print the actual principal name as well as the localname", NULL },
+ {"version", 0, arg_flag, &version_flag,
+ "print version", NULL },
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
static void
test_alname(krb5_context context, krb5_const_realm realm,
const char *user, const char *inst,
const char *localuser, int ok)
{
krb5_principal p;
- char localname[1024];
krb5_error_code ret;
char *princ;
@@ -52,14 +72,16 @@ test_alname(krb5_context context, krb5_const_realm realm,
if (ret)
krb5_err(context, 1, ret, "krb5_unparse_name");
- ret = krb5_aname_to_localname(context, p, sizeof(localname), localname);
+ ret = krb5_aname_to_localname(context, p, lname_size, localname);
krb5_free_principal(context, p);
- free(princ);
if (ret) {
- if (!ok)
+ if (!ok) {
+ free(princ);
return;
+ }
krb5_err(context, 1, ret, "krb5_aname_to_localname: %s -> %s",
princ, localuser);
+ free(princ);
}
if (strcmp(localname, localuser) != 0) {
@@ -74,16 +96,6 @@ test_alname(krb5_context context, krb5_const_realm realm,
}
-static int version_flag = 0;
-static int help_flag = 0;
-
-static struct getargs args[] = {
- {"version", 0, arg_flag, &version_flag,
- "print version", NULL },
- {"help", 0, arg_flag, &help_flag,
- NULL, NULL }
-};
-
static void
usage (int ret)
{
@@ -119,15 +131,68 @@ main(int argc, char **argv)
argc -= optidx;
argv += optidx;
- if (argc != 1)
- errx(1, "first argument should be a local user that in root .k5login");
-
- user = argv[0];
-
ret = krb5_init_context(&context);
if (ret)
errx (1, "krb5_init_context failed: %d", ret);
+ if (simple_flag) {
+ krb5_principal princ;
+ char *unparsed;
+ int status = 0;
+
+ /* Map then print the result and exit */
+ if (argc != 1)
+ errx(1, "One argument is required and it must be a principal name");
+
+ ret = krb5_parse_name(context, argv[0], &princ);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_build_principal");
+
+ ret = krb5_unparse_name(context, princ, &unparsed);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_unparse_name");
+
+ if (lname_size_arg > 0 && lname_size_arg < 1024)
+ lname_size = lname_size_arg;
+ else if (lname_size_arg != 0)
+ errx(1, "local name size must be between 0 and 1023 (inclusive)");
+
+ ret = krb5_aname_to_localname(context, princ, lname_size, localname);
+ if (ret == KRB5_NO_LOCALNAME) {
+ if (verbose_flag)
+ fprintf(stderr, "No mapping obtained for %s\n", unparsed);
+ exit(1);
+ }
+ switch (ret) {
+ case KRB5_PLUGIN_NO_HANDLE:
+ fprintf(stderr, "Error: KRB5_PLUGIN_NO_HANDLE leaked!\n");
+ status = 2;
+ break;
+ case KRB5_CONFIG_NOTENUFSPACE:
+ fprintf(stderr, "Error: lname-size (%lu) too small\n",
+ (long unsigned)lname_size);
+ status = 3;
+ break;
+ case 0:
+ if (verbose_flag)
+ printf("%s ", unparsed);
+ printf("%s\n", localname);
+ break;
+ default:
+ krb5_err(context, 4, ret, "krb5_aname_to_localname");
+ break;
+ }
+ free(unparsed);
+ krb5_free_principal(context, princ);
+ krb5_free_context(context);
+ exit(status);
+ }
+
+ if (argc != 1)
+ errx(1, "first argument should be a local user that is in root .k5login");
+
+ user = argv[0];
+
ret = krb5_get_default_realm(context, &realm);
if (ret)
krb5_err(context, 1, ret, "krb5_get_default_realm");
diff --git a/lib/krb5/test_canon.c b/lib/krb5/test_canon.c
new file mode 100755
index 000000000000..8389c02c685e
--- /dev/null
+++ b/lib/krb5/test_canon.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "krb5_locl.h"
+#include <err.h>
+#include <getarg.h>
+
+#if 0
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <krb5.h>
+#endif
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code retval;
+ krb5_context context;
+ krb5_principal princ = NULL;
+ krb5_principal me = NULL;
+ krb5_principal cmp_to_princ = NULL;
+ krb5_ccache cc = NULL;
+ krb5_creds *out_creds = NULL;
+ krb5_keytab kt = NULL;
+ krb5_keytab_entry ktent;
+ krb5_creds in_creds;
+ char *hostname = NULL;
+ char *unparsed = NULL;
+ char *unparsed_canon = NULL;
+ char *during;
+ char *cmp_to = NULL;;
+ int do_kt = 0;
+ int do_get_creds = 0;
+ int opt;
+ int ret = 1;
+
+ memset(&ktent, 0, sizeof(ktent));
+
+ while ((opt = getopt(argc, argv, "hgkc:")) != -1) {
+ switch (opt) {
+ case 'g':
+ do_get_creds++;
+ break;
+ case 'k':
+ do_kt++;
+ break;
+ case 'c':
+ cmp_to = optarg;
+ break;
+ case 'h':
+ default:
+ fprintf(stderr, "Usage: %s [-g] [-k] [-c compare-to-principal] "
+ "[principal]\n", argv[0]);
+ return 1;
+ }
+ }
+
+ if (!do_get_creds && !do_kt && !cmp_to)
+ do_get_creds++;
+
+ if (optind < argc)
+ hostname = argv[optind];
+
+ during = "init_context";
+ retval = krb5_init_context(&context);
+ if (retval) goto err;
+
+ during = "sn2p";
+ retval = krb5_sname_to_principal(context, hostname, "host", KRB5_NT_SRV_HST, &princ);
+ if (retval) goto err;
+
+ during = "unparse of sname2princ";
+ retval = krb5_unparse_name(context, princ, &unparsed);
+ if (retval) goto err;
+ printf("krb5_sname_to_principal() output: %s\n", unparsed);
+
+ if (cmp_to) {
+ krb5_boolean eq;
+
+ during = "parsing principal name for comparison compare";
+ retval = krb5_parse_name(context, cmp_to, &cmp_to_princ);
+ if (retval) goto err;
+
+ eq = krb5_principal_compare(context, princ, cmp_to_princ);
+ printf("%s %s %s\n", unparsed, eq ? "==" : "!=", cmp_to);
+ }
+
+ if (do_get_creds) {
+ during = "ccdefault";
+ retval = krb5_cc_default(context, &cc);
+ if (retval) goto err;
+
+ during = "ccprinc";
+ retval = krb5_cc_get_principal(context, cc, &me);
+ if (retval) goto err;
+
+ memset(&in_creds, 0, sizeof(in_creds));
+ in_creds.client = me;
+ in_creds.server = princ;
+
+ during = "getcreds";
+ retval = krb5_get_credentials(context, 0, cc, &in_creds, &out_creds);
+ if (retval) goto err;
+
+ during = "unparsing principal name canonicalized by krb5_get_credentials()";
+ retval = krb5_unparse_name(context, in_creds.server, &unparsed_canon);
+ if (retval) goto err;
+ printf("Principal name as canonicalized by krb5_get_credentials() is %s\n", unparsed_canon);
+ }
+
+ if (do_kt) {
+ during = "getting keytab";
+ retval = krb5_kt_default(context, &kt);
+ if (retval) goto err;
+
+ during = "getting keytab ktent";
+ retval = krb5_kt_get_entry(context, kt, princ, 0, 0, &ktent);
+ if (retval) goto err;
+
+ during = "unparsing principal name canonicalized by krb5_kt_get_entry()";
+ retval = krb5_unparse_name(context, ktent.principal, &unparsed_canon);
+ if (retval) goto err;
+ printf("Principal name as canonicalized by krb5_kt_get_entry() is %s\n", unparsed_canon);
+ }
+
+ ret = 0;
+
+err:
+ krb5_free_principal(context, princ);
+ krb5_free_principal(context, me);
+ krb5_free_principal(context, cmp_to_princ);
+ krb5_xfree(unparsed);
+ krb5_xfree(unparsed_canon);
+ if (do_get_creds) {
+ krb5_free_creds(context, out_creds);
+ (void) krb5_cc_close(context, cc);
+ }
+ krb5_kt_free_entry(context, &ktent);
+ if (kt)
+ krb5_kt_close(context, kt);
+ krb5_free_context(context);
+ if (ret)
+ fprintf(stderr, "Failed while doing %s (%d)\n", during, retval);
+ return (ret);
+}
+
diff --git a/lib/krb5/test_cc.c b/lib/krb5/test_cc.c
index 911fba52405c..fa7ee37dde6b 100644
--- a/lib/krb5/test_cc.c
+++ b/lib/krb5/test_cc.c
@@ -57,7 +57,7 @@ test_default_name(krb5_context context)
p1 = estrdup(p);
ret = krb5_cc_set_default_name(context, NULL);
- if (p == NULL)
+ if (ret)
krb5_errx (context, 1, "krb5_cc_set_default_name failed");
p = krb5_cc_default_name(context);
@@ -69,7 +69,7 @@ test_default_name(krb5_context context)
krb5_errx (context, 1, "krb5_cc_default_name no longer same");
ret = krb5_cc_set_default_name(context, test_cc_name);
- if (p == NULL)
+ if (ret)
krb5_errx (context, 1, "krb5_cc_set_default_name 1 failed");
p = krb5_cc_default_name(context);
@@ -293,31 +293,31 @@ struct {
} cc_names[] = {
{ "foo", 0, "foo" },
{ "foo%}", 0, "foo%}" },
- { "%{uid}", 0 },
+ { "%{uid}", 0, NULL },
{ "foo%{null}", 0, "foo" },
{ "foo%{null}bar", 0, "foobar" },
- { "%{", 1 },
- { "%{foo %{", 1 },
- { "%{{", 1 },
- { "%{{}", 1 },
- { "%{nulll}", 1 },
- { "%{does not exist}", 1 },
- { "%{}", 1 },
+ { "%{", 1, NULL },
+ { "%{foo %{", 1, NULL },
+ { "%{{", 1, NULL },
+ { "%{{}", 1, NULL },
+ { "%{nulll}", 1, NULL },
+ { "%{does not exist}", 1, NULL },
+ { "%{}", 1, NULL },
#ifdef KRB5_USE_PATH_TOKENS
- { "%{APPDATA}", 0 },
- { "%{COMMON_APPDATA}", 0},
- { "%{LOCAL_APPDATA}", 0},
- { "%{SYSTEM}", 0},
- { "%{WINDOWS}", 0},
- { "%{TEMP}", 0},
- { "%{USERID}", 0},
- { "%{uid}", 0},
- { "%{USERCONFIG}", 0},
- { "%{COMMONCONFIG}", 0},
- { "%{LIBDIR}", 0},
- { "%{BINDIR}", 0},
- { "%{LIBEXEC}", 0},
- { "%{SBINDIR}", 0},
+ { "%{APPDATA}", 0, NULL },
+ { "%{COMMON_APPDATA}", 0, NULL},
+ { "%{LOCAL_APPDATA}", 0, NULL},
+ { "%{SYSTEM}", 0, NULL},
+ { "%{WINDOWS}", 0, NULL},
+ { "%{TEMP}", 0, NULL},
+ { "%{USERID}", 0, NULL},
+ { "%{uid}", 0, NULL},
+ { "%{USERCONFIG}", 0, NULL},
+ { "%{COMMONCONFIG}", 0, NULL},
+ { "%{LIBDIR}", 0, NULL},
+ { "%{BINDIR}", 0, NULL},
+ { "%{LIBEXEC}", 0, NULL},
+ { "%{SBINDIR}", 0, NULL},
#endif
};
@@ -549,14 +549,15 @@ test_prefix_ops(krb5_context context, const char *name, const krb5_cc_ops *ops)
}
static void
-test_cc_config(krb5_context context)
+test_cc_config(krb5_context context, const char *cc_type,
+ const char *cc_name, size_t count)
{
krb5_error_code ret;
krb5_principal p;
krb5_ccache id;
unsigned int i;
- ret = krb5_cc_new_unique(context, "MEMORY", "bar", &id);
+ ret = krb5_cc_new_unique(context, cc_type, cc_name, &id);
if (ret)
krb5_err(context, 1, ret, "krb5_cc_new_unique");
@@ -568,7 +569,7 @@ test_cc_config(krb5_context context)
if (ret)
krb5_err(context, 1, ret, "krb5_cc_initialize");
- for (i = 0; i < 1000; i++) {
+ for (i = 0; i < count; i++) {
krb5_data data, data2;
const char *name = "foo";
krb5_principal p1 = NULL;
@@ -579,6 +580,10 @@ test_cc_config(krb5_context context)
data.data = rk_UNCONST(name);
data.length = strlen(name);
+ /*
+ * Because of how krb5_cc_set_config() this will also test
+ * krb5_cc_remove_cred().
+ */
ret = krb5_cc_set_config(context, id, p1, "FriendlyName", &data);
if (ret)
krb5_errx(context, 1, "krb5_cc_set_config: add");
@@ -586,8 +591,16 @@ test_cc_config(krb5_context context)
ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
if (ret)
krb5_errx(context, 1, "krb5_cc_get_config: first");
+
+ if (data.length != data2.length ||
+ memcmp(data.data, data2.data, data.length) != 0)
+ krb5_errx(context, 1, "krb5_cc_get_config: did not fetch what was set");
+
krb5_data_free(&data2);
+ data.data = rk_UNCONST("bar");
+ data.length = strlen("bar");
+
ret = krb5_cc_set_config(context, id, p1, "FriendlyName", &data);
if (ret)
krb5_errx(context, 1, "krb5_cc_set_config: add -second");
@@ -595,6 +608,11 @@ test_cc_config(krb5_context context)
ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
if (ret)
krb5_errx(context, 1, "krb5_cc_get_config: second");
+
+ if (data.length != data2.length ||
+ memcmp(data.data, data2.data, data.length) != 0)
+ krb5_errx(context, 1, "krb5_cc_get_config: replace failed");
+
krb5_data_free(&data2);
ret = krb5_cc_set_config(context, id, p1, "FriendlyName", NULL);
@@ -604,6 +622,9 @@ test_cc_config(krb5_context context)
ret = krb5_cc_get_config(context, id, p1, "FriendlyName", &data2);
if (ret == 0)
krb5_errx(context, 1, "krb5_cc_get_config: non-existant");
+
+ if (data2.length)
+ krb5_errx(context, 1, "krb5_cc_get_config: delete failed");
}
krb5_cc_destroy(context, id);
@@ -669,6 +690,7 @@ main(int argc, char **argv)
test_init_vs_destroy(context, krb5_cc_type_api);
#endif
test_init_vs_destroy(context, krb5_cc_type_scc);
+ test_init_vs_destroy(context, krb5_cc_type_dcc);
test_mcc_default();
test_def_cc_name(context);
@@ -694,6 +716,10 @@ main(int argc, char **argv)
test_cache_iter(context, krb5_cc_type_api, 0);
test_cache_iter(context, krb5_cc_type_scc, 0);
test_cache_iter(context, krb5_cc_type_scc, 1);
+#if 0
+ test_cache_iter(context, krb5_cc_type_dcc, 0);
+ test_cache_iter(context, krb5_cc_type_dcc, 1);
+#endif
test_copy(context, krb5_cc_type_file, krb5_cc_type_file);
test_copy(context, krb5_cc_type_memory, krb5_cc_type_memory);
@@ -703,6 +729,11 @@ main(int argc, char **argv)
test_copy(context, krb5_cc_type_file, krb5_cc_type_scc);
test_copy(context, krb5_cc_type_scc, krb5_cc_type_memory);
test_copy(context, krb5_cc_type_memory, krb5_cc_type_scc);
+#if 0
+ test_copy(context, krb5_cc_type_dcc, krb5_cc_type_memory);
+ test_copy(context, krb5_cc_type_dcc, krb5_cc_type_file);
+ test_copy(context, krb5_cc_type_dcc, krb5_cc_type_scc);
+#endif
test_move(context, krb5_cc_type_file);
test_move(context, krb5_cc_type_memory);
@@ -710,6 +741,9 @@ main(int argc, char **argv)
test_move(context, krb5_cc_type_kcm);
#endif
test_move(context, krb5_cc_type_scc);
+#if 0
+ test_move(context, krb5_cc_type_dcc);
+#endif
test_prefix_ops(context, "FILE:/tmp/foo", &krb5_fcc_ops);
test_prefix_ops(context, "FILE", &krb5_fcc_ops);
@@ -720,11 +754,16 @@ main(int argc, char **argv)
test_prefix_ops(context, "SCC:", &krb5_scc_ops);
test_prefix_ops(context, "SCC:foo", &krb5_scc_ops);
#endif
+#if 0
+ test_prefix_ops(context, "DIR:", &krb5_dcc_ops);
+ test_prefix_ops(context, "DIR:tkt1", &krb5_dcc_ops);
+#endif
krb5_cc_destroy(context, id1);
krb5_cc_destroy(context, id2);
- test_cc_config(context);
+ test_cc_config(context, "MEMORY", "bar", 1000); /* 1000 because fast */
+ test_cc_config(context, "FILE", "/tmp/foocc", 30); /* 30 because slower */
krb5_free_context(context);
diff --git a/lib/krb5/test_crypto.c b/lib/krb5/test_crypto.c
index 972af0c5f47b..34a709ada12a 100644
--- a/lib/krb5/test_crypto.c
+++ b/lib/krb5/test_crypto.c
@@ -158,11 +158,15 @@ main(int argc, char **argv)
krb5_salt salt;
krb5_enctype enctypes[] = {
+#if 0
ETYPE_DES_CBC_CRC,
ETYPE_DES3_CBC_SHA1,
ETYPE_ARCFOUR_HMAC_MD5,
+#endif
ETYPE_AES128_CTS_HMAC_SHA1_96,
- ETYPE_AES256_CTS_HMAC_SHA1_96
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA256_128,
+ ETYPE_AES256_CTS_HMAC_SHA384_192
};
setprogname(argv[0]);
diff --git a/lib/krb5/test_crypto_wrapping.c b/lib/krb5/test_crypto_wrapping.c
index 81905a8eadd7..aff4e8d508a1 100644
--- a/lib/krb5/test_crypto_wrapping.c
+++ b/lib/krb5/test_crypto_wrapping.c
@@ -131,7 +131,9 @@ main(int argc, char **argv)
ETYPE_DES3_CBC_SHA1,
ETYPE_ARCFOUR_HMAC_MD5,
ETYPE_AES128_CTS_HMAC_SHA1_96,
- ETYPE_AES256_CTS_HMAC_SHA1_96
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ KRB5_ENCTYPE_AES128_CTS_HMAC_SHA256_128,
+ KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192
};
setprogname(argv[0]);
diff --git a/lib/krb5/test_expand_toks.c b/lib/krb5/test_expand_toks.c
new file mode 100644
index 000000000000..7f3d79f0d112
--- /dev/null
+++ b/lib/krb5/test_expand_toks.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of KTH nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#include "krb5_locl.h"
+#include <getarg.h>
+#include <err.h>
+
+static int version_flag = 0;
+static int help_flag = 0;
+
+static struct getargs args[] = {
+ {"help", 0, arg_flag, &help_flag,
+ NULL, NULL }
+};
+
+static void
+usage (int ret)
+{
+ arg_printusage (args,
+ sizeof(args)/sizeof(*args),
+ NULL,
+ "");
+ exit (ret);
+}
+
+int
+main(int argc, char **argv)
+{
+ krb5_context context;
+ krb5_error_code ret;
+ int optidx = 0;
+ char *expanded;
+
+ setprogname(argv[0]);
+
+ if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ usage(1);
+
+ if (help_flag)
+ usage (0);
+
+ if (version_flag){
+ print_version(NULL);
+ exit(0);
+ }
+
+ argc -= optidx;
+ argv += optidx;
+
+ ret = krb5_init_context(&context);
+ if (ret)
+ errx (1, "krb5_init_context failed: %d", ret);
+
+ ret = _krb5_expand_path_tokensv(context, "/tmp/%{foo}/%{bar}%{baz}/x", 0,
+ &expanded,
+ "foo", "abc",
+ "bar", "dce",
+ "baz", "fgh",
+ NULL);
+ if (ret)
+ krb5_err(context, ret, 1, "Token expansion failed");
+
+#ifdef _WIN32
+#define EXPANDED_SHOULD_BE "\\tmp\\abc\\dcefgh\\x"
+#else
+#define EXPANDED_SHOULD_BE "/tmp/abc/dcefgh/x"
+#endif
+
+ if (strcmp(expanded, EXPANDED_SHOULD_BE))
+ krb5_errx(context, 1, "Token expansion incorrect");
+
+ krb5_free_context(context);
+
+ return 0;
+}
diff --git a/lib/krb5/test_fx.c b/lib/krb5/test_fx.c
index 10aa2aebdc73..68f00dc63f6b 100644
--- a/lib/krb5/test_fx.c
+++ b/lib/krb5/test_fx.c
@@ -82,7 +82,30 @@ struct {
"\x88\xbd\xb2\xa9\xf\x3e\x52\x5a\xb0\x5f\x68\xc5\x43\x9a\x4d\x5e"
"\x9c\x2b\xfd\x2b\x02\x24\xde\x39\xb5\x82\xf4\xbb\x05\xfe\x2\x2e",
32
- }
+ },
+ {
+ "key1", "a", ETYPE_ARCFOUR_HMAC_MD5,
+ "key2", "b", ETYPE_ARCFOUR_HMAC_MD5,
+ ETYPE_ARCFOUR_HMAC_MD5,
+ "\x24\xd7\xf6\xb6\xba\xe4\xe5\xc0\x0d\x20\x82\xc5\xeb\xab\x36\x72",
+ 16
+ },
+ /* We don't yet have a PRF for 1DES in Heimdal */
+ {
+ "key1", "a", ETYPE_DES_CBC_CRC,
+ "key2", "b", ETYPE_DES_CBC_CRC,
+ ETYPE_DES_CBC_CRC,
+ "\x43\xba\xe3\x73\x8c\x94\x67\xe6",
+ 8
+ },
+ {
+ "key1", "a", ETYPE_DES3_CBC_SHA1,
+ "key2", "b", ETYPE_DES3_CBC_SHA1,
+ ETYPE_DES3_CBC_SHA1,
+ "\xe5\x8f\x9e\xb6\x43\x86\x2c\x13\xad\x38\xe5\x29\x31\x34\x62\xa7"
+ "\xf7\x3e\x62\x83\x4f\xe5\x4a\x01",
+ 24
+ },
};
@@ -95,6 +118,11 @@ test_cf2(krb5_context context)
krb5_keyblock k1, k2, k3;
krb5_crypto c1, c2;
unsigned int i;
+ unsigned int errors = 0;
+
+ ret = krb5_allow_weak_crypto(context, 1);
+ if (ret)
+ krb5_err(context, 1, ret, "krb5_allow_weak_crypto");
for (i = 0; i < sizeof(cf2)/sizeof(cf2[0]); i++) {
pw.data = cf2[i].p1;
@@ -140,14 +168,26 @@ test_cf2(krb5_context context)
p2.length = strlen(cf2[i].pepper2);
ret = krb5_crypto_fx_cf2(context, c1, c2, &p1, &p2, cf2[i].e3, &k3);
- if (ret)
+ if (ret == KRB5_PROG_ETYPE_NOSUPP) {
+ krb5_warn(context, ret, "KRB-FX-CF2 not supported for enctype %d",
+ cf2[i].e1);
+ continue;
+ } else if (ret) {
krb5_err(context, 1, ret, "krb5_crypto_fx_cf2");
+ }
- if (k3.keytype != cf2[i].e3)
- krb5_errx(context, 1, "length not right");
+ if (k3.keytype != cf2[i].e3) {
+ errors++;
+ krb5_warnx(context, "length not right for enctype %d", cf2[i].e3);
+ continue;
+ }
if (k3.keyvalue.length != cf2[i].len ||
- memcmp(k3.keyvalue.data, cf2[i].key, cf2[i].len) != 0)
- krb5_errx(context, 1, "key not same");
+ memcmp(k3.keyvalue.data, cf2[i].key, cf2[i].len) != 0) {
+ errors++;
+ krb5_warnx(context, "key not same for enctypes %d %d %d",
+ cf2[i].e1, cf2[i].e2, cf2[i].e3);
+ continue;
+ }
krb5_crypto_destroy(context, c1);
krb5_crypto_destroy(context, c2);
@@ -156,6 +196,9 @@ test_cf2(krb5_context context)
krb5_free_keyblock_contents(context, &k2);
krb5_free_keyblock_contents(context, &k3);
}
+
+ if (errors)
+ krb5_errx(context, 1, "%u KRB-FX-CF2 vectors failed", errors);
}
static int version_flag = 0;
diff --git a/lib/krb5/test_kuserok.c b/lib/krb5/test_kuserok.c
index 5beb1b75732f..7319ef71c9dd 100644
--- a/lib/krb5/test_kuserok.c
+++ b/lib/krb5/test_kuserok.c
@@ -96,9 +96,14 @@ main(int argc, char **argv)
ret = krb5_kuserok(context, principal, argv[1]);
+ krb5_free_principal(context, principal);
krb5_free_context(context);
printf("%s is %sallowed to login as %s\n", p, ret ? "" : "NOT ", argv[1]);
+ free(p);
- return 0;
+ if (ret)
+ return 0;
+
+ return 1;
}
diff --git a/lib/krb5/test_pknistkdf.c b/lib/krb5/test_pknistkdf.c
index ca78e3c04d34..f97dddf38a25 100644
--- a/lib/krb5/test_pknistkdf.c
+++ b/lib/krb5/test_pknistkdf.c
@@ -229,7 +229,7 @@ test_dh2key(krb5_context context, int i, struct testcase *c)
AlgorithmIdentifier ai;
size_t size;
- memset(&ticket, 0, sizeof(&ticket));
+ memset(&ticket, 0, sizeof(ticket));
ai.algorithm = *c->oid;
ai.parameters = NULL;
@@ -244,6 +244,14 @@ test_dh2key(krb5_context context, int i, struct testcase *c)
ret = krb5_parse_name(context, c->server, &server);
if (ret)
krb5_err(context, 1, ret, "parse_name: %s", c->server);
+ /*
+ * Making krb5_build_principal*() set a reasonable default principal
+ * name type broke the test vectors here. Rather than regenerate
+ * the vectors, and to prove that this was the issue, we coerce the
+ * name types back to their original.
+ */
+ krb5_principal_set_type(context, client, KRB5_NT_PRINCIPAL);
+ krb5_principal_set_type(context, server, KRB5_NT_PRINCIPAL);
if (verbose_flag) {
char *str;
diff --git a/lib/krb5/test_plugin.c b/lib/krb5/test_plugin.c
index 4235442b999e..ed6a9e7f1568 100644
--- a/lib/krb5/test_plugin.c
+++ b/lib/krb5/test_plugin.c
@@ -77,7 +77,8 @@ krb5plugin_service_locate_ftable resolve = {
0,
resolve_init,
resolve_fini,
- resolve_lookup
+ resolve_lookup,
+ NULL
};
@@ -113,11 +114,13 @@ main(int argc, char **argv)
while(krb5_krbhst_next_as_string(context, handle, host, sizeof(host)) == 0){
found++;
- if (strcmp(host, "127.0.0.2") != 0)
+ if (!found && strcmp(host, "127.0.0.2") != 0 && strcmp(host, "tcp/127.0.0.2") != 0)
krb5_errx(context, 1, "wrong address: %s", host);
}
if (!found)
krb5_errx(context, 1, "failed to find host");
+ if (found < 2)
+ krb5_errx(context, 1, "did not get the two expected results");
krb5_krbhst_free(context, handle);
diff --git a/lib/krb5/test_rfc3961.c b/lib/krb5/test_rfc3961.c
index 972af0c5f47b..f86b8bb3a446 100644
--- a/lib/krb5/test_rfc3961.c
+++ b/lib/krb5/test_rfc3961.c
@@ -162,7 +162,9 @@ main(int argc, char **argv)
ETYPE_DES3_CBC_SHA1,
ETYPE_ARCFOUR_HMAC_MD5,
ETYPE_AES128_CTS_HMAC_SHA1_96,
- ETYPE_AES256_CTS_HMAC_SHA1_96
+ ETYPE_AES256_CTS_HMAC_SHA1_96,
+ ETYPE_AES128_CTS_HMAC_SHA256_128,
+ ETYPE_AES256_CTS_HMAC_SHA384_192
};
setprogname(argv[0]);
diff --git a/lib/krb5/test_set_kvno0.c b/lib/krb5/test_set_kvno0.c
new file mode 100644
index 000000000000..526c240f1c4b
--- /dev/null
+++ b/lib/krb5/test_set_kvno0.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2011, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "krb5_locl.h"
+#include <err.h>
+#include <getarg.h>
+
+#if 0
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <krb5.h>
+#endif
+
+int
+main(int argc, char **argv)
+{
+ krb5_error_code ret;
+ krb5_context context;
+ krb5_ccache src_cc = NULL;
+ krb5_ccache dst_cc = NULL;
+ krb5_cc_cursor cursor;
+ krb5_principal me = NULL;
+ krb5_creds cred;
+ const char *during;
+ Ticket t;
+ size_t len;
+ int make_kvno_absent = 0;
+ int opt;
+
+ memset(&cred, 0, sizeof (cred));
+ during = "init_context";
+ ret = krb5_init_context(&context);
+ if (ret) goto err;
+
+ while ((opt = getopt(argc, argv, "c:n")) != -1) {
+ switch (opt) {
+ case 'c':
+ during = "cc_resolve of source ccache";
+ ret = krb5_cc_resolve(context, optarg, &src_cc);
+ if (ret) goto err;
+ break;
+ case 'n':
+ make_kvno_absent++;
+ break;
+ case 'h':
+ default:
+ fprintf(stderr, "Usage: %s [-n] [-c ccache]\n"
+ "\tThis utility edits a ccache, setting all ticket\n"
+ "\tenc_part kvnos to zero or absent (if -n is set).\n",
+ argv[0]);
+ return 1;
+ }
+ }
+
+ if (!src_cc) {
+ during = "cc_default";
+ ret = krb5_cc_default(context, &src_cc);
+ if (ret) goto err;
+ }
+
+ during = "cc_get_principal";
+ ret = krb5_cc_get_principal(context, src_cc, &me);
+ if (ret) goto err;
+
+ if (optind != argc) {
+ fprintf(stderr, "Usage: %s [-n] [-c ccache]\n"
+ "\tThis utility edits a ccache, setting all ticket\n"
+ "\tenc_part kvnos to zero or absent (if -n is set).\n",
+ argv[0]);
+ return 1;
+ }
+
+ during = "cc_new_unique of temporary ccache";
+ ret = krb5_cc_new_unique(context, krb5_cc_get_type(context, src_cc),
+ NULL, &dst_cc);
+
+ during = "cc_initialize of temporary ccache";
+ ret = krb5_cc_initialize(context, dst_cc, me);
+ if (ret) goto err;
+
+ during = "cc_start_seq_get";
+ ret = krb5_cc_start_seq_get(context, src_cc, &cursor);
+ if (ret) goto err;
+
+ while ((ret = krb5_cc_next_cred(context, src_cc, &cursor, &cred)) == 0) {
+ krb5_data data;
+
+ during = "decode_Ticket";
+ memset(&t, 0, sizeof (t));
+ ret = decode_Ticket(cred.ticket.data, cred.ticket.length, &t, &len);
+ if (ret == ASN1_MISSING_FIELD)
+ continue;
+ if (ret) goto err;
+ if (t.enc_part.kvno) {
+ *t.enc_part.kvno = 0;
+ if (make_kvno_absent) {
+ free(t.enc_part.kvno);
+ t.enc_part.kvno = NULL;
+ }
+ /*
+ * The new Ticket has to need less or same space as before, so
+ * we reuse cred->icket.data.
+ */
+ during = "encode_Ticket";
+ ASN1_MALLOC_ENCODE(Ticket, data.data, data.length, &t, &len, ret);
+ if (ret) {
+ free_Ticket(&t);
+ goto err;
+ }
+ krb5_data_free(&cred.ticket);
+ cred.ticket = data;
+ }
+ free_Ticket(&t);
+ during = "cc_store_cred";
+ ret = krb5_cc_store_cred(context, dst_cc, &cred);
+ if (ret) goto err;
+ krb5_free_cred_contents(context, &cred);
+ memset(&cred, 0, sizeof (cred));
+ }
+ during = "cc_next_cred";
+ if (ret != KRB5_CC_END) goto err;
+
+ during = "cc_end_seq_get";
+ ret = krb5_cc_end_seq_get(context, src_cc, &cursor);
+ if (ret) goto err;
+
+ during = "cc_move";
+ ret = krb5_cc_move(context, dst_cc, src_cc);
+ if (ret) goto err;
+ dst_cc = NULL;
+
+ during = "cc_switch";
+ ret = krb5_cc_switch(context, src_cc);
+ if (ret) goto err;
+
+err:
+ (void) krb5_free_principal(context, me);
+ if (src_cc)
+ (void) krb5_cc_close(context, src_cc);
+ if (dst_cc)
+ (void) krb5_cc_destroy(context, dst_cc);
+ if (ret) {
+ fprintf(stderr, "Failed while doing %s (%d)\n", during, ret);
+ ret = 1;
+ }
+ return (ret);
+}
+
diff --git a/lib/krb5/test_store.c b/lib/krb5/test_store.c
index 6b930775c0cc..5fac75cd1991 100644
--- a/lib/krb5/test_store.c
+++ b/lib/krb5/test_store.c
@@ -44,12 +44,12 @@ test_int8(krb5_context context, krb5_storage *sp)
krb5_storage_truncate(sp, 0);
- for (i = 0; i < sizeof(val[0])/sizeof(val); i++) {
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
ret = krb5_store_int8(sp, val[i]);
if (ret)
krb5_err(context, 1, ret, "krb5_store_int8");
- krb5_storage_seek(sp, 0, SEEK_SET);
+ krb5_storage_seek(sp, i, SEEK_SET);
ret = krb5_ret_int8(sp, &v);
if (ret)
krb5_err(context, 1, ret, "krb5_ret_int8");
@@ -69,12 +69,12 @@ test_int16(krb5_context context, krb5_storage *sp)
krb5_storage_truncate(sp, 0);
- for (i = 0; i < sizeof(val[0])/sizeof(val); i++) {
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
ret = krb5_store_int16(sp, val[i]);
if (ret)
krb5_err(context, 1, ret, "krb5_store_int16");
- krb5_storage_seek(sp, 0, SEEK_SET);
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
ret = krb5_ret_int16(sp, &v);
if (ret)
krb5_err(context, 1, ret, "krb5_ret_int16");
@@ -94,12 +94,12 @@ test_int32(krb5_context context, krb5_storage *sp)
krb5_storage_truncate(sp, 0);
- for (i = 0; i < sizeof(val[0])/sizeof(val); i++) {
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
ret = krb5_store_int32(sp, val[i]);
if (ret)
krb5_err(context, 1, ret, "krb5_store_int32");
- krb5_storage_seek(sp, 0, SEEK_SET);
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
ret = krb5_ret_int32(sp, &v);
if (ret)
krb5_err(context, 1, ret, "krb5_ret_int32");
@@ -119,12 +119,12 @@ test_uint8(krb5_context context, krb5_storage *sp)
krb5_storage_truncate(sp, 0);
- for (i = 0; i < sizeof(val[0])/sizeof(val); i++) {
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
ret = krb5_store_uint8(sp, val[i]);
if (ret)
krb5_err(context, 1, ret, "krb5_store_uint8");
- krb5_storage_seek(sp, 0, SEEK_SET);
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
ret = krb5_ret_uint8(sp, &v);
if (ret)
krb5_err(context, 1, ret, "krb5_ret_uint8");
@@ -144,12 +144,12 @@ test_uint16(krb5_context context, krb5_storage *sp)
krb5_storage_truncate(sp, 0);
- for (i = 0; i < sizeof(val[0])/sizeof(val); i++) {
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
ret = krb5_store_uint16(sp, val[i]);
if (ret)
krb5_err(context, 1, ret, "krb5_store_uint16");
- krb5_storage_seek(sp, 0, SEEK_SET);
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
ret = krb5_ret_uint16(sp, &v);
if (ret)
krb5_err(context, 1, ret, "krb5_ret_uint16");
@@ -169,12 +169,12 @@ test_uint32(krb5_context context, krb5_storage *sp)
krb5_storage_truncate(sp, 0);
- for (i = 0; i < sizeof(val[0])/sizeof(val); i++) {
+ for (i = 0; i < sizeof(val)/sizeof(val[0]); i++) {
ret = krb5_store_uint32(sp, val[i]);
if (ret)
krb5_err(context, 1, ret, "krb5_store_uint32");
- krb5_storage_seek(sp, 0, SEEK_SET);
+ krb5_storage_seek(sp, i * sizeof (v), SEEK_SET);
ret = krb5_ret_uint32(sp, &v);
if (ret)
krb5_err(context, 1, ret, "krb5_ret_uint32");
@@ -220,12 +220,12 @@ test_truncate(krb5_context context, krb5_storage *sp, int fd)
static void
check_too_large(krb5_context context, krb5_storage *sp)
{
- uint32_t too_big_sizes[] = { INT_MAX, INT_MAX / 2, INT_MAX / 4, INT_MAX / 8 + 1};
+ uint32_t too_big_sizes[] = { UINT_MAX, UINT_MAX / 2, UINT_MAX / 4, UINT_MAX / 8 + 1};
krb5_error_code ret;
krb5_data data;
size_t n;
- for (n = 0; n < sizeof(too_big_sizes) / sizeof(too_big_sizes); n++) {
+ for (n = 0; n < sizeof(too_big_sizes) / sizeof(too_big_sizes[0]); n++) {
krb5_storage_truncate(sp, 0);
krb5_store_uint32(sp, too_big_sizes[n]);
krb5_storage_seek(sp, 0, SEEK_SET);
diff --git a/lib/krb5/test_time.c b/lib/krb5/test_time.c
index 5f6625c90906..36c78088bfec 100644
--- a/lib/krb5/test_time.c
+++ b/lib/krb5/test_time.c
@@ -54,11 +54,11 @@ check_set_time(krb5_context context)
if (ret)
krb5_err(context, 1, ret, "krb5_us_timeofday");
- diff2 = abs(sec - tv.tv_sec);
+ diff2 = labs(sec - tv.tv_sec);
if (diff2 < 9 || diff > 11)
- krb5_errx(context, 1, "set time error: diff: %d",
- abs(sec - tv.tv_sec));
+ krb5_errx(context, 1, "set time error: diff: %ld",
+ labs(sec - tv.tv_sec));
}
diff --git a/lib/krb5/ticket.c b/lib/krb5/ticket.c
index 5b6eabe2b0e0..b8d81c6ad53a 100644
--- a/lib/krb5/ticket.c
+++ b/lib/krb5/ticket.c
@@ -81,11 +81,8 @@ krb5_copy_ticket(krb5_context context,
*to = NULL;
tmp = malloc(sizeof(*tmp));
- if(tmp == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if (tmp == NULL)
+ return krb5_enomem(context);
if((ret = copy_EncTicketPart(&from->ticket, &tmp->ticket))){
free(tmp);
return ret;
@@ -325,6 +322,37 @@ out:
return ret;
}
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_get_ad(krb5_context context,
+ const AuthorizationData *ad,
+ krb5_keyblock *sessionkey,
+ int type,
+ krb5_data *data)
+{
+ krb5_boolean found = FALSE;
+ krb5_error_code ret;
+
+ krb5_data_zero(data);
+
+ if (ad == NULL) {
+ krb5_set_error_message(context, ENOENT,
+ N_("No authorization data", ""));
+ return ENOENT; /* XXX */
+ }
+
+ ret = find_type_in_ad(context, type, data, &found, TRUE, sessionkey, ad, 0);
+ if (ret)
+ return ret;
+ if (!found) {
+ krb5_set_error_message(context, ENOENT,
+ N_("Have no authorization data of type %d", ""),
+ type);
+ return ENOENT; /* XXX */
+ }
+ return 0;
+}
+
+
/**
* Extract the authorization data type of type from the ticket. Store
* the field in data. This function is to use for kerberos
@@ -511,88 +539,9 @@ check_client_referral(krb5_context context,
krb5_const_principal mapped,
krb5_keyblock const * key)
{
- krb5_error_code ret;
- PA_ClientCanonicalized canon;
- krb5_crypto crypto;
- krb5_data data;
- PA_DATA *pa;
- size_t len;
- int i = 0;
-
- if (rep->kdc_rep.padata == NULL)
- goto noreferral;
-
- pa = krb5_find_padata(rep->kdc_rep.padata->val,
- rep->kdc_rep.padata->len,
- KRB5_PADATA_CLIENT_CANONICALIZED, &i);
- if (pa == NULL)
- goto noreferral;
-
- ret = decode_PA_ClientCanonicalized(pa->padata_value.data,
- pa->padata_value.length,
- &canon, &len);
- if (ret) {
- krb5_set_error_message(context, ret,
- N_("Failed to decode ClientCanonicalized "
- "from realm %s", ""), requested->realm);
- return ret;
- }
-
- ASN1_MALLOC_ENCODE(PA_ClientCanonicalizedNames, data.data, data.length,
- &canon.names, &len, ret);
- if (ret) {
- free_PA_ClientCanonicalized(&canon);
- return ret;
- }
- if (data.length != len)
- krb5_abortx(context, "internal asn.1 error");
-
- ret = krb5_crypto_init(context, key, 0, &crypto);
- if (ret) {
- free(data.data);
- free_PA_ClientCanonicalized(&canon);
- return ret;
- }
-
- ret = krb5_verify_checksum(context, crypto, KRB5_KU_CANONICALIZED_NAMES,
- data.data, data.length,
- &canon.canon_checksum);
- krb5_crypto_destroy(context, crypto);
- free(data.data);
- if (ret) {
- krb5_set_error_message(context, ret,
- N_("Failed to verify client canonicalized "
- "data from realm %s", ""),
- requested->realm);
- free_PA_ClientCanonicalized(&canon);
- return ret;
- }
-
- if (!_krb5_principal_compare_PrincipalName(context,
- requested,
- &canon.names.requested_name))
+ if (krb5_principal_compare(context, requested, mapped) == FALSE &&
+ !rep->enc_part.flags.enc_pa_rep)
{
- free_PA_ClientCanonicalized(&canon);
- krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
- N_("Requested name doesn't match"
- " in client referral", ""));
- return KRB5_PRINC_NOMATCH;
- }
- if (!_krb5_principal_compare_PrincipalName(context,
- mapped,
- &canon.names.mapped_name))
- {
- free_PA_ClientCanonicalized(&canon);
- krb5_set_error_message(context, KRB5_PRINC_NOMATCH,
- N_("Mapped name doesn't match"
- " in client referral", ""));
- return KRB5_PRINC_NOMATCH;
- }
-
- return 0;
-
-noreferral:
- if (krb5_principal_compare(context, requested, mapped) == FALSE) {
krb5_set_error_message(context, KRB5KRB_AP_ERR_MODIFIED,
N_("Not same client principal returned "
"as requested", ""));
@@ -646,7 +595,7 @@ decrypt_tkt (krb5_context context,
return 0;
}
-int
+KRB5_LIB_FUNCTION int KRB5_LIB_CALL
_krb5_extract_ticket(krb5_context context,
krb5_kdc_rep *rep,
krb5_creds *creds,
@@ -656,6 +605,7 @@ _krb5_extract_ticket(krb5_context context,
krb5_addresses *addrs,
unsigned nonce,
unsigned flags,
+ krb5_data *request,
krb5_decrypt_proc decrypt_proc,
krb5_const_pointer decryptarg)
{
@@ -674,6 +624,48 @@ _krb5_extract_ticket(krb5_context context,
if (ret)
goto out;
+ if (rep->enc_part.flags.enc_pa_rep && request) {
+ krb5_crypto crypto = NULL;
+ Checksum cksum;
+ PA_DATA *pa = NULL;
+ int idx = 0;
+
+ _krb5_debug(context, 5, "processing enc-ap-rep");
+
+ if (rep->enc_part.encrypted_pa_data == NULL ||
+ (pa = krb5_find_padata(rep->enc_part.encrypted_pa_data->val,
+ rep->enc_part.encrypted_pa_data->len,
+ KRB5_PADATA_REQ_ENC_PA_REP,
+ &idx)) == NULL)
+ {
+ _krb5_debug(context, 5, "KRB5_PADATA_REQ_ENC_PA_REP missing");
+ ret = KRB5KRB_AP_ERR_MODIFIED;
+ goto out;
+ }
+
+ ret = krb5_crypto_init(context, key, 0, &crypto);
+ if (ret)
+ goto out;
+
+ ret = decode_Checksum(pa->padata_value.data,
+ pa->padata_value.length,
+ &cksum, NULL);
+ if (ret) {
+ krb5_crypto_destroy(context, crypto);
+ goto out;
+ }
+
+ ret = krb5_verify_checksum(context, crypto,
+ KRB5_KU_AS_REQ,
+ request->data, request->length,
+ &cksum);
+ krb5_crypto_destroy(context, crypto);
+ free_Checksum(&cksum);
+ _krb5_debug(context, 5, "enc-ap-rep: %svalid", (ret == 0) ? "" : "in");
+ if (ret)
+ goto out;
+ }
+
/* save session key */
creds->session.keyvalue.length = 0;
@@ -688,10 +680,10 @@ _krb5_extract_ticket(krb5_context context,
}
/* compare client and save */
- ret = _krb5_principalname2krb5_principal (context,
- &tmp_principal,
- rep->kdc_rep.cname,
- rep->kdc_rep.crealm);
+ ret = _krb5_principalname2krb5_principal(context,
+ &tmp_principal,
+ rep->kdc_rep.cname,
+ rep->kdc_rep.crealm);
if (ret)
goto out;
@@ -776,12 +768,12 @@ _krb5_extract_ticket(krb5_context context,
tmp_time = rep->enc_part.authtime;
if (creds->times.starttime == 0
- && abs(tmp_time - sec_now) > context->max_skew) {
+ && labs(tmp_time - sec_now) > context->max_skew) {
ret = KRB5KRB_AP_ERR_SKEW;
krb5_set_error_message (context, ret,
- N_("time skew (%d) larger than max (%d)", ""),
- abs(tmp_time - sec_now),
- (int)context->max_skew);
+ N_("time skew (%ld) larger than max (%ld)", ""),
+ labs(tmp_time - sec_now),
+ (long)context->max_skew);
goto out;
}
diff --git a/lib/krb5/transited.c b/lib/krb5/transited.c
index 5e21987bca91..35c00e65add4 100644
--- a/lib/krb5/transited.c
+++ b/lib/krb5/transited.c
@@ -398,6 +398,208 @@ krb5_domain_x500_encode(char **realms, unsigned int num_realms,
return 0;
}
+KRB5_LIB_FUNCTION void KRB5_LIB_CALL
+_krb5_free_capath(krb5_context context, char **capath)
+{
+ char **s;
+
+ for (s = capath; s && *s; ++s)
+ free(*s);
+ free(capath);
+}
+
+struct hier_iter {
+ const char *local_realm;
+ const char *server_realm;
+ const char *lr; /* Pointer into tail of local realm */
+ const char *sr; /* Pointer into tail of server realm */
+ size_t llen; /* Length of local_realm */
+ size_t slen; /* Length of server_realm */
+ size_t len; /* Length of common suffix */
+ size_t num; /* Path element count */
+};
+
+/*
+ * Step up from local_realm to common suffix, or else down to server_realm.
+ */
+static const char *
+hier_next(struct hier_iter *state)
+{
+ const char *lr = state->lr;
+ const char *sr = state->sr;
+ const char *lsuffix = state->local_realm + state->llen - state->len;
+ const char *server_realm = state->server_realm;
+
+ if (lr != NULL) {
+ while (lr < lsuffix)
+ if (*lr++ == '.')
+ return state->lr = lr;
+ state->lr = NULL;
+ }
+ if (sr != NULL) {
+ while (--sr >= server_realm)
+ if (sr == server_realm || sr[-1] == '.')
+ return state->sr = sr;
+ state->sr = NULL;
+ }
+ return NULL;
+}
+
+static void
+hier_init(struct hier_iter *state, const char *local_realm, const char *server_realm)
+{
+ size_t llen;
+ size_t slen;
+ size_t len = 0;
+ const char *lr;
+ const char *sr;
+
+ state->local_realm = local_realm;
+ state->server_realm = server_realm;
+ state->llen = llen = strlen(local_realm);
+ state->slen = slen = strlen(server_realm);
+ state->len = 0;
+ state->num = 0;
+
+ if (slen == 0 || llen == 0)
+ return;
+
+ /* Find first difference from the back */
+ for (lr = local_realm + llen, sr = server_realm + slen;
+ lr != local_realm && sr != server_realm;
+ --lr, --sr) {
+ if (lr[-1] != sr[-1])
+ break;
+ if (lr[-1] == '.')
+ len = llen - (lr - local_realm);
+ }
+
+ /* Nothing in common? */
+ if (*lr == '\0')
+ return;
+
+ /* Everything in common? */
+ if (llen == slen && lr == local_realm)
+ return;
+
+ /* Is one realm is a suffix of the other? */
+ if ((llen < slen && lr == local_realm && sr[-1] == '.') ||
+ (llen > slen && sr == server_realm && lr[-1] == '.'))
+ len = llen - (lr - local_realm);
+
+ state->len = len;
+ /* `lr` starts at local realm and walks up the tree to common suffix */
+ state->lr = local_realm;
+ /* `sr` starts at common suffix in server realm and walks down the tree */
+ state->sr = server_realm + slen - len;
+
+ /* Count elements and reset */
+ while (hier_next(state) != NULL)
+ ++state->num;
+ state->lr = local_realm;
+ state->sr = server_realm + slen - len;
+}
+
+/*
+ * Find a referral path from client_realm to server_realm via local_realm.
+ * Either via [capaths] or hierarchicaly.
+ */
+KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
+_krb5_find_capath(krb5_context context,
+ const char *client_realm,
+ const char *local_realm,
+ const char *server_realm,
+ krb5_boolean use_hierarchical,
+ char ***rpath,
+ size_t *npath)
+{
+ char **confpath;
+ char **capath;
+ struct hier_iter hier_state;
+ char **rp;
+ const char *r;
+
+ *rpath = NULL;
+ *npath = 0;
+
+ confpath = krb5_config_get_strings(context, NULL, "capaths",
+ client_realm, server_realm, NULL);
+ if (confpath == NULL)
+ confpath = krb5_config_get_strings(context, NULL, "capaths",
+ local_realm, server_realm, NULL);
+ /*
+ * With a [capaths] setting from the client to the server we look for our
+ * own realm in the list. If our own realm is not present, we return the
+ * full list. Otherwise, we return our realm's successors, or possibly
+ * NULL. Ignoring a [capaths] settings risks loops plus would violate
+ * explicit policy and the principle of least surpise.
+ */
+ if (confpath != NULL) {
+ char **start = confpath;
+ size_t i;
+ size_t n;
+
+ for (rp = start; *rp; rp++)
+ if (strcmp(*rp, local_realm) == 0)
+ start = rp+1;
+ n = rp - start;
+
+ if (n == 0) {
+ krb5_config_free_strings(confpath);
+ return 0;
+ }
+
+ capath = calloc(n + 1, sizeof(*capath));
+ if (capath == NULL) {
+ krb5_config_free_strings(confpath);
+ return krb5_enomem(context);
+ }
+
+ for (i = 0, rp = start; *rp; rp++) {
+ if ((capath[i++] = strdup(*rp)) == NULL) {
+ _krb5_free_capath(context, capath);
+ krb5_config_free_strings(confpath);
+ return krb5_enomem(context);
+ }
+ }
+ krb5_config_free_strings(confpath);
+ capath[i] = NULL;
+ *rpath = capath;
+ *npath = n;
+ return 0;
+ }
+
+ /* The use_hierarchical flag makes hierarchical path lookup unconditional */
+ if (! use_hierarchical &&
+ ! krb5_config_get_bool_default(context, NULL, TRUE, "libdefaults",
+ "allow_hierarchical_capaths", NULL))
+ return 0;
+
+ /*
+ * When validating transit paths, local_realm == client_realm. Otherwise,
+ * with hierarchical referrals, they may differ, and we may be building a
+ * path forward from our own realm!
+ */
+ hier_init(&hier_state, local_realm, server_realm);
+ if (hier_state.num == 0)
+ return 0;
+
+ rp = capath = calloc(hier_state.num + 1, sizeof(*capath));
+ if (capath == NULL)
+ return krb5_enomem(context);
+ while ((r = hier_next(&hier_state)) != NULL) {
+ if ((*rp++ = strdup(r)) == NULL) {
+ _krb5_free_capath(context, capath);
+ return krb5_enomem(context);
+ }
+ }
+
+ *rp = NULL;
+ *rpath = capath;
+ *npath = hier_state.num;
+ return 0;
+}
+
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_check_transited(krb5_context context,
krb5_const_realm client_realm,
@@ -406,35 +608,36 @@ krb5_check_transited(krb5_context context,
unsigned int num_realms,
int *bad_realm)
{
- char **tr_realms;
- char **p;
- size_t i;
-
- if(num_realms == 0)
- return 0;
-
- tr_realms = krb5_config_get_strings(context, NULL,
- "capaths",
- client_realm,
- server_realm,
- NULL);
- for(i = 0; i < num_realms; i++) {
- for(p = tr_realms; p && *p; p++) {
- if(strcmp(*p, realms[i]) == 0)
+ krb5_error_code ret = 0;
+ char **capath = NULL;
+ size_t num_capath = 0;
+ size_t i = 0;
+ size_t j = 0;
+
+ /* In transit checks hierarchical capaths are optional */
+ ret = _krb5_find_capath(context, client_realm, client_realm, server_realm,
+ FALSE, &capath, &num_capath);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < num_realms; i++) {
+ for (j = 0; j < num_capath; ++j) {
+ if (strcmp(realms[i], capath[j]) == 0)
break;
}
- if(p == NULL || *p == NULL) {
- krb5_config_free_strings(tr_realms);
+ if (j == num_capath) {
+ _krb5_free_capath(context, capath);
krb5_set_error_message (context, KRB5KRB_AP_ERR_ILL_CR_TKT,
N_("no transit allowed "
- "through realm %s", ""),
- realms[i]);
- if(bad_realm)
+ "through realm %s from %s to %s", ""),
+ realms[i], client_realm, server_realm);
+ if (bad_realm)
*bad_realm = i;
return KRB5KRB_AP_ERR_ILL_CR_TKT;
}
}
- krb5_config_free_strings(tr_realms);
+
+ _krb5_free_capath(context, capath);
return 0;
}
@@ -487,4 +690,3 @@ main(int argc, char **argv)
return 0;
}
#endif
-
diff --git a/lib/krb5/verify_init.c b/lib/krb5/verify_init.c
index 2e77b7e59d9c..128502fdc1e5 100644
--- a/lib/krb5/verify_init.c
+++ b/lib/krb5/verify_init.c
@@ -55,16 +55,19 @@ static krb5_boolean
fail_verify_is_ok (krb5_context context,
krb5_verify_init_creds_opt *options)
{
- if ((options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL
- && options->ap_req_nofail != 0)
- || krb5_config_get_bool (context,
- NULL,
- "libdefaults",
- "verify_ap_req_nofail",
- NULL))
+
+ if (options && (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)
+ && options->ap_req_nofail != 0)
+ return FALSE;
+
+ if (krb5_config_get_bool(context,
+ NULL,
+ "libdefaults",
+ "verify_ap_req_nofail",
+ NULL))
return FALSE;
- else
- return TRUE;
+
+ return TRUE;
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
diff --git a/lib/krb5/verify_krb5_conf.c b/lib/krb5/verify_krb5_conf.c
index 647a311a2ca6..0db8807a4a12 100644
--- a/lib/krb5/verify_krb5_conf.c
+++ b/lib/krb5/verify_krb5_conf.c
@@ -163,22 +163,22 @@ check_host(krb5_context context, const char *path, char *data)
/* XXX data could be a list of hosts that this code can't handle */
/* XXX copied from krbhst.c */
- if(strncmp(p, "http://", 7) == 0){
+ if (strncmp(p, "http://", 7) == 0){
p += 7;
hints.ai_socktype = SOCK_STREAM;
strlcpy(service, "http", sizeof(service));
defport = 80;
- } else if(strncmp(p, "http/", 5) == 0) {
+ } else if (strncmp(p, "http/", 5) == 0) {
p += 5;
hints.ai_socktype = SOCK_STREAM;
strlcpy(service, "http", sizeof(service));
defport = 80;
- }else if(strncmp(p, "tcp/", 4) == 0){
+ } else if (strncmp(p, "tcp/", 4) == 0){
p += 4;
hints.ai_socktype = SOCK_STREAM;
strlcpy(service, "kerberos", sizeof(service));
defport = 88;
- } else if(strncmp(p, "udp/", 4) == 0) {
+ } else if (strncmp(p, "udp/", 4) == 0) {
p += 4;
hints.ai_socktype = SOCK_DGRAM;
strlcpy(service, "kerberos", sizeof(service));
@@ -188,14 +188,14 @@ check_host(krb5_context context, const char *path, char *data)
strlcpy(service, "kerberos", sizeof(service));
defport = 88;
}
- if(strsep_copy(&p, ":", hostname, sizeof(hostname)) < 0) {
+ if (strsep_copy(&p, ":", hostname, sizeof(hostname)) < 0) {
return 1;
}
hostname[strcspn(hostname, "/")] = '\0';
- if(p != NULL) {
+ if (p != NULL) {
char *end;
int tmp = strtol(p, &end, 0);
- if(end == p) {
+ if (end == p) {
krb5_warnx(context, "%s: failed to parse port number in %s",
path, data);
return 1;
@@ -204,14 +204,15 @@ check_host(krb5_context context, const char *path, char *data)
snprintf(service, sizeof(service), "%u", defport);
}
ret = getaddrinfo(hostname, service, &hints, &ai);
- if(ret == EAI_SERVICE && !isdigit((unsigned char)service[0])) {
+ if (ret == EAI_SERVICE && !isdigit((unsigned char)service[0])) {
snprintf(service, sizeof(service), "%u", defport);
ret = getaddrinfo(hostname, service, &hints, &ai);
}
- if(ret != 0) {
+ if (ret != 0) {
krb5_warnx(context, "%s: %s (%s)", path, gai_strerror(ret), hostname);
return 1;
}
+ freeaddrinfo(ai);
return 0;
}
@@ -289,10 +290,13 @@ check_log(krb5_context context, const char *path, char *data)
int min = 0, max = -1, n;
char c;
const char *p = data;
+#ifdef _WIN32
+ const char *q;
+#endif
n = sscanf(p, "%d%c%d/", &min, &c, &max);
if(n == 2){
- if(c == '/') {
+ if(ISPATHSEP(c)) {
if(min < 0){
max = -min;
min = 0;
@@ -302,6 +306,12 @@ check_log(krb5_context context, const char *path, char *data)
}
}
if(n){
+#ifdef _WIN32
+ q = strrchr(p, '\\');
+ if (q != NULL)
+ p = q;
+ else
+#endif
p = strchr(p, '/');
if(p == NULL) {
krb5_warnx(context, "%s: failed to parse \"%s\"", path, data);
@@ -327,7 +337,7 @@ check_log(krb5_context context, const char *path, char *data)
strlcpy(severity, "ERR", sizeof(severity));
if(*facility == '\0')
strlcpy(facility, "AUTH", sizeof(facility));
- if(find_value(severity, syslogvals) == -1) {
+ if(find_value(facility, syslogvals) == -1) {
krb5_warnx(context, "%s: unknown syslog facility \"%s\"",
path, facility);
ret++;
@@ -353,227 +363,302 @@ struct entry {
};
struct entry all_strings[] = {
- { "", krb5_config_string, NULL },
- { NULL }
+ { "", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry all_boolean[] = {
- { "", krb5_config_string, check_boolean },
- { NULL }
+ { "", krb5_config_string, check_boolean, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry v4_name_convert_entries[] = {
- { "host", krb5_config_list, all_strings },
- { "plain", krb5_config_list, all_strings },
- { NULL }
+ { "host", krb5_config_list, all_strings, 0 },
+ { "plain", krb5_config_list, all_strings, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry libdefaults_entries[] = {
- { "accept_null_addresses", krb5_config_string, check_boolean },
- { "allow_weak_crypto", krb5_config_string, check_boolean },
+ { "accept_null_addresses", krb5_config_string, check_boolean, 0 },
+ { "allow_weak_crypto", krb5_config_string, check_boolean, 0 },
{ "capath", krb5_config_list, all_strings, 1 },
- { "check_pac", krb5_config_string, check_boolean },
- { "clockskew", krb5_config_string, check_time },
- { "date_format", krb5_config_string, NULL },
- { "default_cc_name", krb5_config_string, NULL },
- { "default_etypes", krb5_config_string, NULL },
- { "default_etypes_des", krb5_config_string, NULL },
- { "default_keytab_modify_name", krb5_config_string, NULL },
- { "default_keytab_name", krb5_config_string, NULL },
- { "default_realm", krb5_config_string, NULL },
- { "dns_canonize_hostname", krb5_config_string, check_boolean },
- { "dns_proxy", krb5_config_string, NULL },
- { "dns_lookup_kdc", krb5_config_string, check_boolean },
- { "dns_lookup_realm", krb5_config_string, check_boolean },
- { "dns_lookup_realm_labels", krb5_config_string, NULL },
- { "egd_socket", krb5_config_string, NULL },
- { "encrypt", krb5_config_string, check_boolean },
- { "extra_addresses", krb5_config_string, NULL },
- { "fcache_version", krb5_config_string, check_numeric },
- { "fcc-mit-ticketflags", krb5_config_string, check_boolean },
- { "forward", krb5_config_string, check_boolean },
- { "forwardable", krb5_config_string, check_boolean },
- { "http_proxy", krb5_config_string, check_host /* XXX */ },
- { "ignore_addresses", krb5_config_string, NULL },
- { "kdc_timeout", krb5_config_string, check_time },
- { "kdc_timesync", krb5_config_string, check_boolean },
- { "log_utc", krb5_config_string, check_boolean },
- { "maxretries", krb5_config_string, check_numeric },
- { "scan_interfaces", krb5_config_string, check_boolean },
- { "srv_lookup", krb5_config_string, check_boolean },
- { "srv_try_txt", krb5_config_string, check_boolean },
- { "ticket_lifetime", krb5_config_string, check_time },
- { "time_format", krb5_config_string, NULL },
- { "transited_realms_reject", krb5_config_string, NULL },
- { "no-addresses", krb5_config_string, check_boolean },
- { "v4_instance_resolve", krb5_config_string, check_boolean },
- { "v4_name_convert", krb5_config_list, v4_name_convert_entries },
- { "verify_ap_req_nofail", krb5_config_string, check_boolean },
- { "max_retries", krb5_config_string, check_time },
- { "renew_lifetime", krb5_config_string, check_time },
- { "proxiable", krb5_config_string, check_boolean },
- { "warn_pwexpire", krb5_config_string, check_time },
+ { "ccapi_library", krb5_config_string, NULL, 0 },
+ { "check_pac", krb5_config_string, check_boolean, 0 },
+ { "check-rd-req-server", krb5_config_string, check_boolean, 0 },
+ { "clockskew", krb5_config_string, check_time, 0 },
+ { "date_format", krb5_config_string, NULL, 0 },
+ { "default_as_etypes", krb5_config_string, NULL, 0 },
+ { "default_cc_name", krb5_config_string, NULL, 0 },
+ { "default_cc_type", krb5_config_string, NULL, 0 },
+ { "default_etypes", krb5_config_string, NULL, 0 },
+ { "default_etypes_des", krb5_config_string, NULL, 0 },
+ { "default_keytab_modify_name", krb5_config_string, NULL, 0 },
+ { "default_keytab_name", krb5_config_string, NULL, 0 },
+ { "default_keytab_modify_name", krb5_config_string, NULL, 0 },
+ { "default_realm", krb5_config_string, NULL, 0 },
+ { "default_tgs_etypes", krb5_config_string, NULL, 0 },
+ { "dns_canonize_hostname", krb5_config_string, check_boolean, 0 },
+ { "dns_proxy", krb5_config_string, NULL, 0 },
+ { "dns_lookup_kdc", krb5_config_string, check_boolean, 0 },
+ { "dns_lookup_realm", krb5_config_string, check_boolean, 0 },
+ { "dns_lookup_realm_labels", krb5_config_string, NULL, 0 },
+ { "egd_socket", krb5_config_string, NULL, 0 },
+ { "encrypt", krb5_config_string, check_boolean, 0 },
+ { "extra_addresses", krb5_config_string, NULL, 0 },
+ { "fcache_version", krb5_config_string, check_numeric, 0 },
+ { "fcache_strict_checking", krb5_config_string, check_boolean, 0 },
+ { "fcc-mit-ticketflags", krb5_config_string, check_boolean, 0 },
+ { "forward", krb5_config_string, check_boolean, 0 },
+ { "forwardable", krb5_config_string, check_boolean, 0 },
+ { "allow_hierarchical_capaths", krb5_config_string, check_boolean, 0 },
+ { "host_timeout", krb5_config_string, check_time, 0 },
+ { "http_proxy", krb5_config_string, check_host /* XXX */, 0 },
+ { "ignore_addresses", krb5_config_string, NULL, 0 },
+ { "k5login_authoritative", krb5_config_string, check_boolean, 0 },
+ { "k5login_directory", krb5_config_string, NULL, 0 },
+ { "kdc_timeout", krb5_config_string, check_time, 0 },
+ { "kdc_timesync", krb5_config_string, check_boolean, 0 },
+ { "kuserok", krb5_config_string, NULL, 0 },
+ { "large_message_size", krb5_config_string, check_numeric, 0 },
+ { "log_utc", krb5_config_string, check_boolean, 0 },
+ { "max_retries", krb5_config_string, check_numeric, 0 },
+ { "maximum_message_size", krb5_config_string, check_numeric, 0 },
+ { "moduli", krb5_config_string, NULL, 0 },
+ { "name_canon_rules", krb5_config_string, NULL, 0 },
+ { "no-addresses", krb5_config_string, check_boolean, 0 },
+ { "pkinit_dh_min_bits", krb5_config_string, NULL, 0 },
+ { "proxiable", krb5_config_string, check_boolean, 0 },
+ { "renew_lifetime", krb5_config_string, check_time, 0 },
+ { "scan_interfaces", krb5_config_string, check_boolean, 0 },
+ { "srv_lookup", krb5_config_string, check_boolean, 0 },
+ { "srv_try_txt", krb5_config_string, check_boolean, 0 },
+ { "ticket_lifetime", krb5_config_string, check_time, 0 },
+ { "time_format", krb5_config_string, NULL, 0 },
+ { "transited_realms_reject", krb5_config_string, NULL, 0 },
+ { "use_fallback", krb5_config_string, check_boolean, 0 },
+ { "v4_instance_resolve", krb5_config_string, check_boolean, 0 },
+ { "v4_name_convert", krb5_config_list, v4_name_convert_entries, 0 },
+ { "verify_ap_req_nofail", krb5_config_string, check_boolean, 0 },
+ { "warn_pwexpire", krb5_config_string, check_time, 0 },
+
/* MIT stuff */
- { "permitted_enctypes", krb5_config_string, mit_entry },
- { "default_tgs_enctypes", krb5_config_string, mit_entry },
- { "default_tkt_enctypes", krb5_config_string, mit_entry },
- { NULL }
+ { "permitted_enctypes", krb5_config_string, mit_entry, 0 },
+ { "default_tgs_enctypes", krb5_config_string, mit_entry, 0 },
+ { "default_tkt_enctypes", krb5_config_string, mit_entry, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry appdefaults_entries[] = {
- { "afslog", krb5_config_string, check_boolean },
- { "afs-use-524", krb5_config_string, check_524 },
- { "encrypt", krb5_config_string, check_boolean },
- { "forward", krb5_config_string, check_boolean },
- { "forwardable", krb5_config_string, check_boolean },
- { "proxiable", krb5_config_string, check_boolean },
- { "ticket_lifetime", krb5_config_string, check_time },
- { "renew_lifetime", krb5_config_string, check_time },
- { "no-addresses", krb5_config_string, check_boolean },
- { "krb4_get_tickets", krb5_config_string, check_boolean },
- { "pkinit_anchors", krb5_config_string, NULL },
- { "pkinit_win2k", krb5_config_string, NULL },
- { "pkinit_win2k_require_binding", krb5_config_string, NULL },
- { "pkinit_require_eku", krb5_config_string, NULL },
- { "pkinit_require_krbtgt_otherName", krb5_config_string, NULL },
- { "pkinit_require_hostname_match", krb5_config_string, NULL },
+ { "afslog", krb5_config_string, check_boolean, 0 },
+ { "afs-use-524", krb5_config_string, check_524, 0 },
#if 0
- { "anonymous", krb5_config_string, check_boolean },
+ { "anonymous", krb5_config_string, check_boolean, 0 },
#endif
- { "", krb5_config_list, appdefaults_entries },
- { NULL }
+ { "encrypt", krb5_config_string, check_boolean, 0 },
+ { "forward", krb5_config_string, check_boolean, 0 },
+ { "forwardable", krb5_config_string, check_boolean, 0 },
+ { "krb4_get_tickets", krb5_config_string, check_boolean, 0 },
+ { "proxiable", krb5_config_string, check_boolean, 0 },
+ { "renew_lifetime", krb5_config_string, check_time, 0 },
+ { "no-addresses", krb5_config_string, check_boolean, 0 },
+ { "pkinit_anchors", krb5_config_string, NULL, 0 },
+ { "pkinit_pool", krb5_config_string, NULL, 0 },
+ { "pkinit_require_eku", krb5_config_string, NULL, 0 },
+ { "pkinit_require_hostname_match", krb5_config_string, NULL, 0 },
+ { "pkinit_require_krbtgt_otherName", krb5_config_string, NULL, 0 },
+ { "pkinit_revoke", krb5_config_string, NULL, 0 },
+ { "pkinit_trustedCertifiers", krb5_config_string, check_boolean, 0 },
+ { "pkinit_win2k", krb5_config_string, NULL, 0 },
+ { "pkinit_win2k_require_binding", krb5_config_string, NULL, 0 },
+ { "ticket_lifetime", krb5_config_string, check_time, 0 },
+ { "", krb5_config_list, appdefaults_entries, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry realms_entries[] = {
- { "forwardable", krb5_config_string, check_boolean },
- { "proxiable", krb5_config_string, check_boolean },
- { "ticket_lifetime", krb5_config_string, check_time },
- { "renew_lifetime", krb5_config_string, check_time },
- { "warn_pwexpire", krb5_config_string, check_time },
- { "kdc", krb5_config_string, check_host },
- { "admin_server", krb5_config_string, check_host },
- { "kpasswd_server", krb5_config_string, check_host },
- { "krb524_server", krb5_config_string, check_host },
- { "v4_name_convert", krb5_config_list, v4_name_convert_entries },
- { "v4_instance_convert", krb5_config_list, all_strings },
- { "v4_domains", krb5_config_string, NULL },
- { "default_domain", krb5_config_string, NULL },
- { "win2k_pkinit", krb5_config_string, NULL },
+ { "admin_server", krb5_config_string, check_host, 0 },
+ { "auth_to_local", krb5_config_string, NULL, 0 },
+ { "auth_to_local_names", krb5_config_string, NULL, 0 },
+ { "default_domain", krb5_config_string, NULL, 0 },
+ { "forwardable", krb5_config_string, check_boolean, 0 },
+ { "allow_hierarchical_capaths", krb5_config_string, check_boolean, 0 },
+ { "kdc", krb5_config_string, check_host, 0 },
+ { "kpasswd_server", krb5_config_string, check_host, 0 },
+ { "krb524_server", krb5_config_string, check_host, 0 },
+ { "kx509_ca", krb5_config_string, NULL, 0 },
+ { "kx509_include_pkinit_san", krb5_config_string, check_boolean, 0 },
+ { "name_canon_rules", krb5_config_string, NULL, 0 },
+ { "no-addresses", krb5_config_string, check_boolean, 0 },
+ { "pkinit_anchors", krb5_config_string, NULL, 0 },
+ { "pkinit_require_eku", krb5_config_string, NULL, 0 },
+ { "pkinit_require_hostname_match", krb5_config_string, NULL, 0 },
+ { "pkinit_require_krbtgt_otherName", krb5_config_string, NULL, 0 },
+ { "pkinit_trustedCertifiers", krb5_config_string, check_boolean, 0 },
+ { "pkinit_win2k", krb5_config_string, NULL, 0 },
+ { "pkinit_win2k_require_binding", krb5_config_string, NULL, 0 },
+ { "proxiable", krb5_config_string, check_boolean, 0 },
+ { "renew_lifetime", krb5_config_string, check_time, 0 },
+ { "require_initial_kca_tickets", krb5_config_string, check_boolean, 0 },
+ { "ticket_lifetime", krb5_config_string, check_time, 0 },
+ { "v4_domains", krb5_config_string, NULL, 0 },
+ { "v4_instance_convert", krb5_config_list, all_strings, 0 },
+ { "v4_name_convert", krb5_config_list, v4_name_convert_entries, 0 },
+ { "warn_pwexpire", krb5_config_string, check_time, 0 },
+ { "win2k_pkinit", krb5_config_string, NULL, 0 },
+
/* MIT stuff */
- { "admin_keytab", krb5_config_string, mit_entry },
- { "acl_file", krb5_config_string, mit_entry },
- { "dict_file", krb5_config_string, mit_entry },
- { "kadmind_port", krb5_config_string, mit_entry },
- { "kpasswd_port", krb5_config_string, mit_entry },
- { "master_key_name", krb5_config_string, mit_entry },
- { "master_key_type", krb5_config_string, mit_entry },
- { "key_stash_file", krb5_config_string, mit_entry },
- { "max_life", krb5_config_string, mit_entry },
- { "max_renewable_life", krb5_config_string, mit_entry },
- { "default_principal_expiration", krb5_config_string, mit_entry },
- { "default_principal_flags", krb5_config_string, mit_entry },
- { "supported_enctypes", krb5_config_string, mit_entry },
- { "database_name", krb5_config_string, mit_entry },
- { NULL }
+ { "admin_keytab", krb5_config_string, mit_entry, 0 },
+ { "acl_file", krb5_config_string, mit_entry, 0 },
+ { "database_name", krb5_config_string, mit_entry, 0 },
+ { "default_principal_expiration", krb5_config_string, mit_entry, 0 },
+ { "default_principal_flags", krb5_config_string, mit_entry, 0 },
+ { "dict_file", krb5_config_string, mit_entry, 0 },
+ { "kadmind_port", krb5_config_string, mit_entry, 0 },
+ { "kpasswd_port", krb5_config_string, mit_entry, 0 },
+ { "master_kdc", krb5_config_string, mit_entry, 0 },
+ { "master_key_name", krb5_config_string, mit_entry, 0 },
+ { "master_key_type", krb5_config_string, mit_entry, 0 },
+ { "key_stash_file", krb5_config_string, mit_entry, 0 },
+ { "max_life", krb5_config_string, mit_entry, 0 },
+ { "max_renewable_life", krb5_config_string, mit_entry, 0 },
+ { "supported_enctypes", krb5_config_string, mit_entry, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry realms_foobar[] = {
- { "", krb5_config_list, realms_entries },
- { NULL }
+ { "", krb5_config_list, realms_entries, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry kdc_database_entries[] = {
- { "realm", krb5_config_string, NULL },
- { "dbname", krb5_config_string, NULL },
- { "mkey_file", krb5_config_string, NULL },
- { "acl_file", krb5_config_string, NULL },
- { "log_file", krb5_config_string, NULL },
- { NULL }
+ { "acl_file", krb5_config_string, NULL, 0 },
+ { "dbname", krb5_config_string, NULL, 0 },
+ { "log_file", krb5_config_string, NULL, 0 },
+ { "mkey_file", krb5_config_string, NULL, 0 },
+ { "realm", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry kdc_entries[] = {
- { "database", krb5_config_list, kdc_database_entries },
- { "key-file", krb5_config_string, NULL },
- { "logging", krb5_config_string, check_log },
- { "max-request", krb5_config_string, check_bytes },
- { "require-preauth", krb5_config_string, check_boolean },
- { "ports", krb5_config_string, NULL },
- { "addresses", krb5_config_string, NULL },
- { "enable-kerberos4", krb5_config_string, check_boolean },
- { "enable-524", krb5_config_string, check_boolean },
- { "enable-http", krb5_config_string, check_boolean },
- { "check-ticket-addresses", krb5_config_string, check_boolean },
- { "allow-null-ticket-addresses", krb5_config_string, check_boolean },
- { "allow-anonymous", krb5_config_string, check_boolean },
- { "v4_realm", krb5_config_string, NULL },
+ { "addresses", krb5_config_string, NULL, 0 },
+ { "allow-anonymous", krb5_config_string, check_boolean, 0 },
+ { "allow-null-ticket-addresses", krb5_config_string, check_boolean, 0 },
+ { "check-ticket-addresses", krb5_config_string, check_boolean, 0 },
+ { "database", krb5_config_list, kdc_database_entries, 0 },
+ { "detach", krb5_config_string, check_boolean, 0 },
+ { "digests_allowed", krb5_config_string, NULL, 0 },
+ { "disable-des", krb5_config_string, check_boolean, 0 },
+ { "enable-524", krb5_config_string, check_boolean, 0 },
+ { "enable-digest", krb5_config_string, check_boolean, 0 },
{ "enable-kaserver", krb5_config_string, check_boolean, 1 },
- { "encode_as_rep_as_tgs_rep", krb5_config_string, check_boolean },
- { "kdc_warn_pwexpire", krb5_config_string, check_time },
- { "use_2b", krb5_config_list, NULL },
- { "enable-pkinit", krb5_config_string, check_boolean },
- { "pkinit_identity", krb5_config_string, NULL },
- { "pkinit_anchors", krb5_config_string, NULL },
- { "pkinit_pool", krb5_config_string, NULL },
- { "pkinit_revoke", krb5_config_string, NULL },
- { "pkinit_kdc_ocsp", krb5_config_string, NULL },
- { "pkinit_principal_in_certificate", krb5_config_string, NULL },
- { "pkinit_dh_min_bits", krb5_config_string, NULL },
- { "pkinit_allow_proxy_certificate", krb5_config_string, NULL },
- { "hdb-ldap-create-base", krb5_config_string, NULL },
- { "v4-realm", krb5_config_string, NULL },
- { NULL }
+ { "enable-kerberos4", krb5_config_string, check_boolean, 1 },
+ { "enable-kx509", krb5_config_string, check_boolean, 0 },
+ { "enable-http", krb5_config_string, check_boolean, 0 },
+ { "enable-pkinit", krb5_config_string, check_boolean, 0 },
+ { "encode_as_rep_as_tgs_rep", krb5_config_string, check_boolean, 0 },
+ { "enforce-transited-policy", krb5_config_string, NULL, 1 },
+ { "hdb-ldap-create-base", krb5_config_string, NULL, 0 },
+ { "iprop-acl", krb5_config_string, NULL, 0 },
+ { "iprop-stats", krb5_config_string, NULL, 0 },
+ { "kdc-request-log", krb5_config_string, NULL, 0 },
+ { "kdc_warn_pwexpire", krb5_config_string, check_time, 0 },
+ { "key-file", krb5_config_string, NULL, 0 },
+ { "kx509_ca", krb5_config_string, NULL, 0 },
+ { "kx509_include_pkinit_san", krb5_config_string, check_boolean, 0 },
+ { "kx509_template", krb5_config_string, NULL, 0 },
+ { "logging", krb5_config_string, check_log, 0 },
+ { "max-kdc-datagram-reply-length", krb5_config_string, check_bytes, 0 },
+ { "max-request", krb5_config_string, check_bytes, 0 },
+ { "pkinit_allow_proxy_certificate", krb5_config_string, check_boolean, 0 },
+ { "pkinit_anchors", krb5_config_string, NULL, 0 },
+ { "pkinit_dh_min_bits", krb5_config_string, check_numeric, 0 },
+ { "pkinit_identity", krb5_config_string, NULL, 0 },
+ { "pkinit_kdc_friendly_name", krb5_config_string, NULL, 0 },
+ { "pkinit_kdc_ocsp", krb5_config_string, NULL, 0 },
+ { "pkinit_mappings_file", krb5_config_string, NULL, 0 },
+ { "pkinit_pool", krb5_config_string, NULL, 0 },
+ { "pkinit_principal_in_certificate", krb5_config_string, check_boolean, 0 },
+ { "pkinit_revoke", krb5_config_string, NULL, 0 },
+ { "pkinit_win2k_require_binding", krb5_config_string, check_boolean, 0 },
+ { "ports", krb5_config_string, NULL, 0 },
+ { "preauth-use-strongest-session-key", krb5_config_string, check_boolean, 0 },
+ { "require_initial_kca_tickets", krb5_config_string, check_boolean, 0 },
+ { "require-preauth", krb5_config_string, check_boolean, 0 },
+ { "svc-use-strongest-session-key", krb5_config_string, check_boolean, 0 },
+ { "tgt-use-strongest-session-key", krb5_config_string, check_boolean, 0 },
+ { "transited-policy", krb5_config_string, NULL, 0 },
+ { "use_2b", krb5_config_list, NULL, 0 },
+ { "use-strongest-server-key", krb5_config_string, check_boolean, 0 },
+ { "v4_realm", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry kadmin_entries[] = {
- { "password_lifetime", krb5_config_string, check_time },
- { "default_keys", krb5_config_string, NULL },
- { "use_v4_salt", krb5_config_string, NULL },
- { "require-preauth", krb5_config_string, check_boolean },
- { NULL }
+ { "allow_self_change_password", krb5_config_string, check_boolean, 0 },
+ { "default_keys", krb5_config_string, NULL, 0 },
+ { "password_lifetime", krb5_config_string, check_time, 0 },
+ { "require-preauth", krb5_config_string, check_boolean, 0 },
+ { "save-password", krb5_config_string, check_boolean, 0 },
+ { "use_v4_salt", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry log_strings[] = {
- { "", krb5_config_string, check_log },
- { NULL }
+ { "", krb5_config_string, check_log, 0 },
+ { NULL, 0, NULL, 0 }
};
/* MIT stuff */
struct entry kdcdefaults_entries[] = {
- { "kdc_ports", krb5_config_string, mit_entry },
- { "v4_mode", krb5_config_string, mit_entry },
- { NULL }
+ { "kdc_ports", krb5_config_string, mit_entry, 0 },
+ { "v4_mode", krb5_config_string, mit_entry, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry capaths_entries[] = {
- { "", krb5_config_list, all_strings },
- { NULL }
+ { "", krb5_config_list, all_strings, 0 },
+ { NULL, 0, NULL, 0 }
+};
+
+struct entry kcm_entries[] = {
+ { "detach", krb5_config_string, check_boolean, 0 },
+ { "disallow-getting-krbtgt", krb5_config_string, check_boolean, 0 },
+ { "logging", krb5_config_string, NULL, 0 },
+ { "max-request", krb5_config_string, NULL, 0 },
+ { "system_ccache", krb5_config_string, NULL, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry password_quality_entries[] = {
- { "policies", krb5_config_string, NULL },
- { "external_program", krb5_config_string, NULL },
- { "min_classes", krb5_config_string, check_numeric },
- { "min_length", krb5_config_string, check_numeric },
- { "", krb5_config_list, all_strings },
- { NULL }
+ { "check_function", krb5_config_string, NULL, 0 },
+ { "check_library", krb5_config_string, NULL, 0 },
+ { "external_program", krb5_config_string, NULL, 0 },
+ { "min_classes", krb5_config_string, check_numeric, 0 },
+ { "min_length", krb5_config_string, check_numeric, 0 },
+ { "policies", krb5_config_string, NULL, 0 },
+ { "policy_libraries", krb5_config_string, NULL, 0 },
+ { "", krb5_config_list, all_strings, 0 },
+ { NULL, 0, NULL, 0 }
};
struct entry toplevel_sections[] = {
- { "libdefaults" , krb5_config_list, libdefaults_entries },
- { "realms", krb5_config_list, realms_foobar },
- { "domain_realm", krb5_config_list, all_strings },
- { "logging", krb5_config_list, log_strings },
- { "kdc", krb5_config_list, kdc_entries },
- { "kadmin", krb5_config_list, kadmin_entries },
- { "appdefaults", krb5_config_list, appdefaults_entries },
- { "gssapi", krb5_config_list, NULL },
- { "capaths", krb5_config_list, capaths_entries },
- { "password_quality", krb5_config_list, password_quality_entries },
+ { "appdefaults", krb5_config_list, appdefaults_entries, 0 },
+ { "capaths", krb5_config_list, capaths_entries, 0 },
+ { "domain_realm", krb5_config_list, all_strings, 0 },
+ { "gssapi", krb5_config_list, NULL, 0 },
+ { "kadmin", krb5_config_list, kadmin_entries, 0 },
+ { "kcm", krb5_config_list, kcm_entries, 0 },
+ { "kdc", krb5_config_list, kdc_entries, 0 },
+ { "libdefaults" , krb5_config_list, libdefaults_entries, 0 },
+ { "logging", krb5_config_list, log_strings, 0 },
+ { "password_quality", krb5_config_list, password_quality_entries, 0 },
+ { "realms", krb5_config_list, realms_foobar, 0 },
+
/* MIT stuff */
- { "kdcdefaults", krb5_config_list, kdcdefaults_entries },
- { NULL }
+ { "kdcdefaults", krb5_config_list, kdcdefaults_entries, 0 },
+ { NULL, 0, NULL, 0 }
};
diff --git a/lib/krb5/verify_user.c b/lib/krb5/verify_user.c
index 01562ef562a3..663196b29b15 100644
--- a/lib/krb5/verify_user.c
+++ b/lib/krb5/verify_user.c
@@ -100,11 +100,8 @@ KRB5_LIB_FUNCTION int KRB5_LIB_CALL
krb5_verify_opt_alloc(krb5_context context, krb5_verify_opt **opt)
{
*opt = calloc(1, sizeof(**opt));
- if ((*opt) == NULL) {
- krb5_set_error_message(context, ENOMEM,
- N_("malloc: out of memory", ""));
- return ENOMEM;
- }
+ if ((*opt) == NULL)
+ return krb5_enomem(context);
krb5_verify_opt_init(*opt);
return 0;
}
diff --git a/lib/krb5/version-script.map b/lib/krb5/version-script.map
index 818e6e071cbd..6c49368a8a14 100644
--- a/lib/krb5/version-script.map
+++ b/lib/krb5/version-script.map
@@ -2,10 +2,6 @@ HEIMDAL_KRB5_2.0 {
global:
krb524_convert_creds_kdc;
krb524_convert_creds_kdc_ccache;
- krb5_425_conv_principal;
- krb5_425_conv_principal_ext2;
- krb5_425_conv_principal_ext;
- krb5_524_conv_principal;
krb5_abort;
krb5_abortx;
krb5_acl_match_file;
@@ -42,6 +38,7 @@ HEIMDAL_KRB5_2.0 {
krb5_auth_con_getrcache;
krb5_auth_con_getremoteseqnumber;
krb5_auth_con_getremotesubkey;
+ krb5_auth_con_getsendsubkey;
krb5_auth_con_init;
krb5_auth_con_removeflags;
krb5_auth_con_setaddrs;
@@ -349,6 +346,7 @@ HEIMDAL_KRB5_2.0 {
krb5_get_init_creds_opt_set_address_list;
krb5_get_init_creds_opt_set_addressless;
krb5_get_init_creds_opt_set_anonymous;
+ krb5_get_init_creds_opt_set_change_password_prompt;
krb5_get_init_creds_opt_set_canonicalize;
krb5_get_init_creds_opt_set_default_flags;
krb5_get_init_creds_opt_set_etype_list;
@@ -387,6 +385,7 @@ HEIMDAL_KRB5_2.0 {
krb5_init_ets;
krb5_initlog;
krb5_is_config_principal;
+ krb5_is_enctype_weak;
krb5_is_thread_safe;
krb5_kcm_call;
krb5_kcm_storage_request;
@@ -435,6 +434,7 @@ HEIMDAL_KRB5_2.0 {
krb5_make_principal;
krb5_max_sockaddr_size;
krb5_mk_error;
+ krb5_mk_error_ext;
krb5_mk_priv;
krb5_mk_rep;
krb5_mk_req;
@@ -446,7 +446,7 @@ HEIMDAL_KRB5_2.0 {
krb5_net_write_block;
krb5_ntlm_alloc;
krb5_ntlm_free;
- krb5_ntlm_init_get_challange;
+ krb5_ntlm_init_get_challenge;
krb5_ntlm_init_get_flags;
krb5_ntlm_init_get_opaque;
krb5_ntlm_init_get_targetinfo;
@@ -491,6 +491,7 @@ HEIMDAL_KRB5_2.0 {
krb5_principal_get_realm;
krb5_principal_get_type;
krb5_principal_match;
+ krb5_principal_set_comp_string;
krb5_principal_set_realm;
krb5_principal_set_type;
krb5_principal_is_krbtgt;
@@ -545,6 +546,7 @@ HEIMDAL_KRB5_2.0 {
krb5_ret_data;
krb5_ret_int16;
krb5_ret_int32;
+ krb5_ret_int64;
krb5_ret_int8;
krb5_ret_keyblock;
krb5_ret_principal;
@@ -554,6 +556,7 @@ HEIMDAL_KRB5_2.0 {
krb5_ret_times;
krb5_ret_uint16;
krb5_ret_uint32;
+ krb5_ret_uint64;
krb5_ret_uint8;
krb5_salttype_to_string;
krb5_sendauth;
@@ -568,6 +571,7 @@ HEIMDAL_KRB5_2.0 {
krb5_sendto_kdc;
krb5_sendto_kdc_flags;
krb5_set_config_files;
+ krb5_set_debug_dest;
krb5_set_default_in_tkt_etypes;
krb5_set_default_realm;
krb5_set_dns_canonicalize_hostname;
@@ -598,6 +602,8 @@ HEIMDAL_KRB5_2.0 {
krb5_storage_from_fd;
krb5_storage_from_mem;
krb5_storage_from_readonly_mem;
+ krb5_storage_from_socket;
+ krb5_storage_fsync;
krb5_storage_get_byteorder;
krb5_storage_get_eof_code;
krb5_storage_is_flags;
@@ -618,6 +624,7 @@ HEIMDAL_KRB5_2.0 {
krb5_store_data;
krb5_store_int16;
krb5_store_int32;
+ krb5_store_int64;
krb5_store_int8;
krb5_store_keyblock;
krb5_store_principal;
@@ -627,6 +634,7 @@ HEIMDAL_KRB5_2.0 {
krb5_store_times;
krb5_store_uint16;
krb5_store_uint32;
+ krb5_store_uint64;
krb5_store_uint8;
krb5_string_to_deltat;
krb5_string_to_enctype;
@@ -702,6 +710,7 @@ HEIMDAL_KRB5_2.0 {
initialize_k524_error_table;
# variables
+ krb5_dcc_ops;
krb5_mcc_ops;
krb5_acc_ops;
krb5_fcc_ops;
@@ -716,36 +725,29 @@ HEIMDAL_KRB5_2.0 {
krb5_config_file;
krb5_defkeyname;
krb5_cc_type_api;
+ krb5_cc_type_dcc;
krb5_cc_type_file;
krb5_cc_type_memory;
krb5_cc_type_kcm;
krb5_cc_type_scc;
+ # shared with HDB
+ _krb5_plugin_run_f;
+ _krb5_enctype_requires_random_salt;
+
# Shared with GSSAPI krb5
_krb5_crc_init_table;
_krb5_crc_update;
_krb5_get_krbtgt;
_krb5_build_authenticator;
- # V4 compat glue
- _krb5_krb_tf_setup;
- _krb5_krb_dest_tkt;
- _krb5_krb_life_to_time;
- _krb5_krb_decomp_ticket;
- _krb5_krb_decomp_ticket;
- _krb5_krb_create_ticket;
- _krb5_krb_create_ciph;
- _krb5_krb_create_auth_reply;
- _krb5_krb_rd_req;
- _krb5_krb_free_auth_data;
- _krb5_krb_time_to_life;
- _krb5_krb_cr_err_reply;
-
# Shared with libkdc
- _krb5_AES_string_to_default_iterator;
+ _krb5_AES_SHA1_string_to_default_iterator;
+ _krb5_AES_SHA2_string_to_default_iterator;
_krb5_dh_group_ok;
_krb5_get_host_realm_int;
_krb5_get_int;
+ _krb5_get_int64;
_krb5_pac_sign;
_krb5_parse_moduli;
_krb5_pk_kdf;
@@ -754,8 +756,7 @@ HEIMDAL_KRB5_2.0 {
_krb5_pk_octetstring2key;
_krb5_plugin_find;
_krb5_plugin_free;
- _krb5_plugin_get_next;
- _krb5_plugin_get_symbol;
+ _krb5_plugin_run_f;
_krb5_principal2principalname;
_krb5_principalname2krb5_principal;
_krb5_put_int;
@@ -764,11 +765,32 @@ HEIMDAL_KRB5_2.0 {
# kinit helper
krb5_get_init_creds_opt_set_pkinit_user_certs;
krb5_pk_enterprise_cert;
+ krb5_process_last_request;
+ krb5_init_creds_init;
+ krb5_init_creds_set_service;
+ krb5_init_creds_set_fast_ccache;
+ krb5_init_creds_set_keytab;
+ krb5_init_creds_get;
+ krb5_init_creds_get_creds;
+ krb5_init_creds_get_error;
+ krb5_init_creds_set_password;
+ krb5_init_creds_store;
+ krb5_init_creds_free;
# testing
_krb5_aes_cts_encrypt;
_krb5_n_fold;
_krb5_expand_default_cc_name;
+ _krb5_expand_path_tokensv;
+
+ # FAST
+ _krb5_fast_cf2;
+ _krb5_fast_armor_key;
+
+ # TGS
+ _krb5_find_capath;
+ _krb5_free_capath;
+
local:
*;
};
diff --git a/lib/krb5/warn.c b/lib/krb5/warn.c
index cb3be76fccff..8269aff7416e 100644
--- a/lib/krb5/warn.c
+++ b/lib/krb5/warn.c
@@ -36,7 +36,7 @@
static krb5_error_code _warnerr(krb5_context context, int do_errtext,
krb5_error_code code, int level, const char *fmt, va_list ap)
- __attribute__((__format__(__printf__, 5, 0)));
+ __attribute__ ((__format__ (__printf__, 5, 0)));
static krb5_error_code
_warnerr(krb5_context context, int do_errtext,
@@ -86,6 +86,12 @@ _warnerr(krb5_context context, int do_errtext,
ret = _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
va_end(ap);
+#define FUNC_NORET(ETEXT, CODE, LEVEL) \
+ va_list ap; \
+ va_start(ap, fmt); \
+ (void) _warnerr(context, ETEXT, CODE, LEVEL, fmt, ap); \
+ va_end(ap);
+
#undef __attribute__
#define __attribute__(X)
@@ -104,7 +110,7 @@ _warnerr(krb5_context context, int do_errtext,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vwarn(krb5_context context, krb5_error_code code,
const char *fmt, va_list ap)
- __attribute__ ((format (printf, 3, 0)))
+ __attribute__ ((__format__ (__printf__, 3, 0)))
{
return _warnerr(context, 1, code, 1, fmt, ap);
}
@@ -122,7 +128,7 @@ krb5_vwarn(krb5_context context, krb5_error_code code,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_warn(krb5_context context, krb5_error_code code, const char *fmt, ...)
- __attribute__ ((format (printf, 3, 4)))
+ __attribute__ ((__format__ (__printf__, 3, 4)))
{
FUNC(1, code, 1);
return ret;
@@ -140,7 +146,7 @@ krb5_warn(krb5_context context, krb5_error_code code, const char *fmt, ...)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vwarnx(krb5_context context, const char *fmt, va_list ap)
- __attribute__ ((format (printf, 2, 0)))
+ __attribute__ ((__format__ (__printf__, 2, 0)))
{
return _warnerr(context, 0, 0, 1, fmt, ap);
}
@@ -156,7 +162,7 @@ krb5_vwarnx(krb5_context context, const char *fmt, va_list ap)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_warnx(krb5_context context, const char *fmt, ...)
- __attribute__ ((format (printf, 2, 3)))
+ __attribute__ ((__format__ (__printf__, 2, 3)))
{
FUNC(0, 0, 1);
return ret;
@@ -178,7 +184,7 @@ krb5_warnx(krb5_context context, const char *fmt, ...)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verr(krb5_context context, int eval, krb5_error_code code,
const char *fmt, va_list ap)
- __attribute__ ((noreturn, format (printf, 4, 0)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 4, 0)))
{
_warnerr(context, 1, code, 0, fmt, ap);
exit(eval);
@@ -200,9 +206,9 @@ krb5_verr(krb5_context context, int eval, krb5_error_code code,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_err(krb5_context context, int eval, krb5_error_code code,
const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 4, 5)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 4, 5)))
{
- FUNC(1, code, 0);
+ FUNC_NORET(1, code, 0);
exit(eval);
UNREACHABLE(return 0);
}
@@ -220,7 +226,7 @@ krb5_err(krb5_context context, int eval, krb5_error_code code,
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_verrx(krb5_context context, int eval, const char *fmt, va_list ap)
- __attribute__ ((noreturn, format (printf, 3, 0)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)))
{
_warnerr(context, 0, 0, 0, fmt, ap);
exit(eval);
@@ -239,9 +245,9 @@ krb5_verrx(krb5_context context, int eval, const char *fmt, va_list ap)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_errx(krb5_context context, int eval, const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 3, 4)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)))
{
- FUNC(0, 0, 0);
+ FUNC_NORET(0, 0, 0);
exit(eval);
UNREACHABLE(return 0);
}
@@ -261,7 +267,7 @@ krb5_errx(krb5_context context, int eval, const char *fmt, ...)
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vabort(krb5_context context, krb5_error_code code,
const char *fmt, va_list ap)
- __attribute__ ((noreturn, format (printf, 3, 0)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 0)))
{
_warnerr(context, 1, code, 0, fmt, ap);
abort();
@@ -275,22 +281,23 @@ krb5_vabort(krb5_context context, krb5_error_code code,
* @param context A Kerberos 5 context
* @param code error code of the last error
* @param fmt message to print
+ * @param ... arguments for format string
*
* @ingroup krb5_error
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_abort(krb5_context context, krb5_error_code code, const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 3, 4)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 3, 4)))
{
- FUNC(1, code, 0);
+ FUNC_NORET(1, code, 0);
abort();
UNREACHABLE(return 0);
}
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_vabortx(krb5_context context, const char *fmt, va_list ap)
- __attribute__ ((noreturn, format (printf, 2, 0)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 0)))
{
_warnerr(context, 0, 0, 0, fmt, ap);
abort();
@@ -301,17 +308,17 @@ krb5_vabortx(krb5_context context, const char *fmt, va_list ap)
* Log a warning to the log, default stderr, and then abort.
*
* @param context A Kerberos 5 context
- * @param code error code of the last error
- * @param fmt message to print
+ * @param fmt printf format string of message to print
+ * @param ... arguments for format string
*
* @ingroup krb5_error
*/
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_abortx(krb5_context context, const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 2, 3)))
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 3)))
{
- FUNC(0, 0, 0);
+ FUNC_NORET(0, 0, 0);
abort();
UNREACHABLE(return 0);
}
diff --git a/lib/libedit/Makefile.in b/lib/libedit/Makefile.in
index 2af565df6a5b..ea0c8c9d94c7 100644
--- a/lib/libedit/Makefile.in
+++ b/lib/libedit/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -15,6 +14,61 @@
@SET_MAKE@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -34,51 +88,89 @@ POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = .
-DIST_COMMON = $(am__configure_deps) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(srcdir)/config.h.in \
- $(top_srcdir)/configure COPYING ChangeLog INSTALL THANKS \
- config.guess config.sub depcomp install-sh ltmain.sh missing
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
+ $(am__configure_deps) $(am__DIST_COMMON)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
-AM_V_GEN = $(am__v_GEN_$(V))
-am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
-am__v_GEN_0 = @echo " GEN " $@;
-AM_V_at = $(am__v_at_$(V))
-am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
+am__v_at_1 =
SOURCES =
DIST_SOURCES =
-RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \
- html-recursive info-recursive install-data-recursive \
- install-dvi-recursive install-exec-recursive \
- install-html-recursive install-info-recursive \
- install-pdf-recursive install-ps-recursive install-recursive \
- installcheck-recursive installdirs-recursive pdf-recursive \
- ps-recursive uninstall-recursive
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
-AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \
- $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \
- distdir dist dist-all distcheck
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ cscope distdir dist dist-all distcheck
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \
+ $(LISP)config.h.in
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+CSCOPE = cscope
DIST_SUBDIRS = $(SUBDIRS)
+am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in COPYING \
+ ChangeLog INSTALL THANKS compile config.guess config.sub \
+ install-sh ltmain.sh missing
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
am__remove_distdir = \
- { test ! -d "$(distdir)" \
- || { find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
- && rm -fr "$(distdir)"; }; }
+ if test -d "$(distdir)"; then \
+ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
+ && rm -rf "$(distdir)" \
+ || { sleep 5 && rm -rf "$(distdir)"; }; \
+ else :; fi
+am__post_remove_distdir = $(am__remove_distdir)
am__relativize = \
dir0=`pwd`; \
sed_first='s,^\([^/]*\)/.*$$,\1,'; \
@@ -106,10 +198,12 @@ am__relativize = \
reldir="$$dir2"
DIST_ARCHIVES = $(distdir).tar.gz
GZIP_ENV = --best
+DIST_TARGETS = dist-gzip
distuninstallcheck_listfiles = find . -type f -print
+am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
+ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
distcleancheck_listfiles = find . -type f -print
ACLOCAL = @ACLOCAL@
-ALLOCA = @ALLOCA@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
@@ -148,6 +242,7 @@ LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
LT_VERSION = @LT_VERSION@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
@@ -234,7 +329,7 @@ all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive
.SUFFIXES:
-am--refresh:
+am--refresh: Makefile
@:
$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
@@ -249,7 +344,6 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -270,10 +364,8 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
config.h: stamp-h1
- @if test ! -f $@; then \
- rm -f stamp-h1; \
- $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
- else :; fi
+ @test -f $@ || rm -f stamp-h1
+ @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
@rm -f stamp-h1
@@ -296,22 +388,25 @@ distclean-libtool:
-rm -f libtool config.lt
# This directory's subdirectories are mostly independent; you can cd
-# into them and run `make' without going through this Makefile.
-# To change the values of `make' variables: instead of editing Makefiles,
-# (1) if the variable is set in `config.status', edit `config.status'
-# (which will cause the Makefiles to be regenerated when you run `make');
-# (2) otherwise, pass the desired values on the `make' command line.
-$(RECURSIVE_TARGETS):
- @fail= failcom='exit 1'; \
- for f in x $$MAKEFLAGS; do \
- case $$f in \
- *=* | --[!k]*);; \
- *k*) failcom='fail=yes';; \
- esac; \
- done; \
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
dot_seen=no; \
target=`echo $@ | sed s/-recursive//`; \
- list='$(SUBDIRS)'; for subdir in $$list; do \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
echo "Making $$target in $$subdir"; \
if test "$$subdir" = "."; then \
dot_seen=yes; \
@@ -326,57 +421,12 @@ $(RECURSIVE_TARGETS):
$(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
fi; test -z "$$fail"
-$(RECURSIVE_CLEAN_TARGETS):
- @fail= failcom='exit 1'; \
- for f in x $$MAKEFLAGS; do \
- case $$f in \
- *=* | --[!k]*);; \
- *k*) failcom='fail=yes';; \
- esac; \
- done; \
- dot_seen=no; \
- case "$@" in \
- distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
- *) list='$(SUBDIRS)' ;; \
- esac; \
- rev=''; for subdir in $$list; do \
- if test "$$subdir" = "."; then :; else \
- rev="$$subdir $$rev"; \
- fi; \
- done; \
- rev="$$rev ."; \
- target=`echo $@ | sed s/-recursive//`; \
- for subdir in $$rev; do \
- echo "Making $$target in $$subdir"; \
- if test "$$subdir" = "."; then \
- local_target="$$target-am"; \
- else \
- local_target="$$target"; \
- fi; \
- ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
- || eval $$failcom; \
- done && test -z "$$fail"
-tags-recursive:
- list='$(SUBDIRS)'; for subdir in $$list; do \
- test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \
- done
-ctags-recursive:
- list='$(SUBDIRS)'; for subdir in $$list; do \
- test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \
- done
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
@@ -392,12 +442,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
fi; \
done; \
- list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -409,15 +454,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: ctags-recursive $(HEADERS) $(SOURCES) config.h.in $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) config.h.in $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -426,9 +467,31 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscope: cscope.files
+ test ! -s cscope.files \
+ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS)
+clean-cscope:
+ -rm -f cscope.files
+cscope.files: clean-cscope cscopelist
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+ -rm -f cscope.out cscope.in.out cscope.po.out cscope.files
distdir: $(DISTFILES)
$(am__remove_distdir)
@@ -464,13 +527,10 @@ distdir: $(DISTFILES)
done
@list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
if test "$$subdir" = .; then :; else \
- test -d "$(distdir)/$$subdir" \
- || $(MKDIR_P) "$(distdir)/$$subdir" \
- || exit 1; \
- fi; \
- done
- @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
- if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
$(am__relativize); \
new_distdir=$$reldir; \
@@ -498,37 +558,43 @@ distdir: $(DISTFILES)
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|| chmod -R a+r "$(distdir)"
dist-gzip: distdir
- tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
- $(am__remove_distdir)
+ tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz
+ $(am__post_remove_distdir)
dist-bzip2: distdir
- tardir=$(distdir) && $(am__tar) | bzip2 -9 -c >$(distdir).tar.bz2
- $(am__remove_distdir)
+ tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
+ $(am__post_remove_distdir)
-dist-lzma: distdir
- tardir=$(distdir) && $(am__tar) | lzma -9 -c >$(distdir).tar.lzma
- $(am__remove_distdir)
+dist-lzip: distdir
+ tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
+ $(am__post_remove_distdir)
dist-xz: distdir
- tardir=$(distdir) && $(am__tar) | xz -c >$(distdir).tar.xz
- $(am__remove_distdir)
+ tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz
+ $(am__post_remove_distdir)
dist-tarZ: distdir
+ @echo WARNING: "Support for distribution archives compressed with" \
+ "legacy program 'compress' is deprecated." >&2
+ @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z
- $(am__remove_distdir)
+ $(am__post_remove_distdir)
dist-shar: distdir
- shar $(distdir) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).shar.gz
- $(am__remove_distdir)
+ @echo WARNING: "Support for shar distribution archives is" \
+ "deprecated." >&2
+ @echo WARNING: "It will be removed altogether in Automake 2.0" >&2
+ shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz
+ $(am__post_remove_distdir)
dist-zip: distdir
-rm -f $(distdir).zip
zip -rq $(distdir).zip $(distdir)
- $(am__remove_distdir)
+ $(am__post_remove_distdir)
-dist dist-all: distdir
- tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
- $(am__remove_distdir)
+dist dist-all:
+ $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:'
+ $(am__post_remove_distdir)
# This target untars the dist file and tries a VPATH configuration. Then
# it guarantees that the distribution is self-contained by making another
@@ -536,31 +602,33 @@ dist dist-all: distdir
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
- GZIP=$(GZIP_ENV) gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
+ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
- *.tar.lzma*) \
- lzma -dc $(distdir).tar.lzma | $(am__untar) ;;\
+ *.tar.lz*) \
+ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
*.tar.xz*) \
xz -dc $(distdir).tar.xz | $(am__untar) ;;\
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
- GZIP=$(GZIP_ENV) gzip -dc $(distdir).shar.gz | unshar ;;\
+ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
esac
- chmod -R a-w $(distdir); chmod a+w $(distdir)
- mkdir $(distdir)/_build
- mkdir $(distdir)/_inst
+ chmod -R a-w $(distdir)
+ chmod u+w $(distdir)
+ mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst
chmod a-w $(distdir)
test -d $(distdir)/_build || exit 0; \
dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \
&& dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \
&& am__cwd=`pwd` \
- && $(am__cd) $(distdir)/_build \
- && ../configure --srcdir=.. --prefix="$$dc_install_base" \
+ && $(am__cd) $(distdir)/_build/sub \
+ && ../../configure \
+ $(AM_DISTCHECK_CONFIGURE_FLAGS) \
$(DISTCHECK_CONFIGURE_FLAGS) \
+ --srcdir=../.. --prefix="$$dc_install_base" \
&& $(MAKE) $(AM_MAKEFLAGS) \
&& $(MAKE) $(AM_MAKEFLAGS) dvi \
&& $(MAKE) $(AM_MAKEFLAGS) check \
@@ -583,13 +651,21 @@ distcheck: dist
&& $(MAKE) $(AM_MAKEFLAGS) distcleancheck \
&& cd "$$am__cwd" \
|| exit 1
- $(am__remove_distdir)
+ $(am__post_remove_distdir)
@(echo "$(distdir) archives ready for distribution: "; \
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
distuninstallcheck:
- @$(am__cd) '$(distuninstallcheck_dir)' \
- && test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
+ @test -n '$(distuninstallcheck_dir)' || { \
+ echo 'ERROR: trying to run $@ with an empty' \
+ '$$(distuninstallcheck_dir)' >&2; \
+ exit 1; \
+ }; \
+ $(am__cd) '$(distuninstallcheck_dir)' || { \
+ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \
+ exit 1; \
+ }; \
+ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \
|| { echo "ERROR: files left after uninstall:" ; \
if test -n "$(DESTDIR)"; then \
echo " (check DESTDIR support)"; \
@@ -620,10 +696,15 @@ install-am: all-am
installcheck: installcheck-recursive
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -705,24 +786,26 @@ ps-am:
uninstall-am:
-.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) all \
- ctags-recursive install-am install-strip tags-recursive
-
-.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \
- all all-am am--refresh check check-am clean clean-generic \
- clean-libtool ctags ctags-recursive dist dist-all dist-bzip2 \
- dist-gzip dist-lzma dist-shar dist-tarZ dist-xz dist-zip \
- distcheck distclean distclean-generic distclean-hdr \
- distclean-libtool distclean-tags distcleancheck distdir \
- distuninstallcheck dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am install-dvi \
- install-dvi-am install-exec install-exec-am install-html \
- install-html-am install-info install-info-am install-man \
- install-pdf install-pdf-am install-ps install-ps-am \
- install-strip installcheck installcheck-am installdirs \
- installdirs-am maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
- ps ps-am tags tags-recursive uninstall uninstall-am
+.MAKE: $(am__recursive_targets) all install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
+ am--refresh check check-am clean clean-cscope clean-generic \
+ clean-libtool cscope cscopelist-am ctags ctags-am dist \
+ dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
+ dist-xz dist-zip distcheck distclean distclean-generic \
+ distclean-hdr distclean-libtool distclean-tags distcleancheck \
+ distdir distuninstallcheck dvi dvi-am html html-am info \
+ info-am install install-am install-data install-data-am \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-html install-html-am install-info install-info-am \
+ install-man install-pdf install-pdf-am install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs installdirs-am maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
+ uninstall-am
+
+.PRECIOUS: Makefile
#EXTRA_DIST = libedit.pc.in
diff --git a/lib/libedit/acinclude.m4 b/lib/libedit/acinclude.m4
index 4a581894bc7c..51b4ddb2c334 100644
--- a/lib/libedit/acinclude.m4
+++ b/lib/libedit/acinclude.m4
@@ -64,22 +64,3 @@ AC_DEFUN([EL_GETPW_R_DRAFT],
AC_MSG_RESULT(yes)],
[AC_MSG_RESULT(no)])
])
-
-
-dnl
-dnl use option --enable-widec to turn on use of wide-character support
-dnl
-AC_DEFUN([EL_ENABLE_WIDEC],
-[
- AC_MSG_CHECKING(if you want wide-character code)
- AC_ARG_ENABLE(widec,
- [ --enable-widec compile with wide-char/UTF-8 code],
- [with_widec=$enableval],
- [with_widec=no])
- AC_MSG_RESULT($with_widec)
- if test "$with_widec" = yes ; then
- AC_DEFINE(WIDECHAR, 1, [Define to 1 if you want wide-character code])
- fi
- AM_CONDITIONAL([WIDECHAR], [test "$with_widec" = yes])
-])
-
diff --git a/lib/libedit/aclocal.m4 b/lib/libedit/aclocal.m4
index 22f6418d3936..5954fb8cd506 100644
--- a/lib/libedit/aclocal.m4
+++ b/lib/libedit/aclocal.m4
@@ -1,7 +1,7 @@
-# generated automatically by aclocal 1.11.1 -*- Autoconf -*-
+# generated automatically by aclocal 1.15.1 -*- Autoconf -*-
+
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -11,19 +11,18 @@
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
+m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])])
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
-m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],,
-[m4_warning([this file was generated for autoconf 2.65.
+m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],,
+[m4_warning([this file was generated for autoconf 2.69.
You have another version of autoconf. It may work, but is not guaranteed to.
If you have problems, you may need to regenerate the build system entirely.
-To do so, use the procedure documented by the package, typically `autoreconf'.])])
+To do so, use the procedure documented by the package, typically 'autoreconf'.])])
# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*-
#
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
-# 2006, 2007, 2008, 2009, 2010 Free Software Foundation,
-# Inc.
+# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc.
# Written by Gordon Matzigkeit, 1996
#
# This file is free software; the Free Software Foundation gives
@@ -31,36 +30,30 @@ To do so, use the procedure documented by the package, typically `autoreconf'.])
# modifications, as long as this notice is preserved.
m4_define([_LT_COPYING], [dnl
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
-# 2006, 2007, 2008, 2009, 2010 Free Software Foundation,
-# Inc.
-# Written by Gordon Matzigkeit, 1996
-#
-# This file is part of GNU Libtool.
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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 of of the License, or
+# (at your option) any later version.
#
-# GNU Libtool 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 of
-# the License, or (at your option) any later version.
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
#
-# As a special exception to the GNU General Public License,
-# if you distribute this file as part of a program or library that
-# is built using GNU Libtool, you may include this file under the
-# same distribution terms that you use for the rest of that program.
-#
-# GNU Libtool is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
-# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
-# obtained by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
])
-# serial 57 LT_INIT
+# serial 58 LT_INIT
# LT_PREREQ(VERSION)
@@ -88,7 +81,7 @@ esac
# LT_INIT([OPTIONS])
# ------------------
AC_DEFUN([LT_INIT],
-[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT
+[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK
AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
AC_BEFORE([$0], [LT_LANG])dnl
AC_BEFORE([$0], [LT_OUTPUT])dnl
@@ -112,7 +105,7 @@ dnl Parse OPTIONS
_LT_SET_OPTIONS([$0], [$1])
# This can be used to rebuild libtool when needed
-LIBTOOL_DEPS="$ltmain"
+LIBTOOL_DEPS=$ltmain
# Always use our own libtool.
LIBTOOL='$(SHELL) $(top_builddir)/libtool'
@@ -132,26 +125,43 @@ dnl AC_DEFUN([AC_PROG_LIBTOOL], [])
dnl AC_DEFUN([AM_PROG_LIBTOOL], [])
+# _LT_PREPARE_CC_BASENAME
+# -----------------------
+m4_defun([_LT_PREPARE_CC_BASENAME], [
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in @S|@*""; do
+ case $cc_temp in
+ compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
+ distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+])# _LT_PREPARE_CC_BASENAME
+
+
# _LT_CC_BASENAME(CC)
# -------------------
-# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME,
+# but that macro is also expanded into generated libtool script, which
+# arranges for $SED and $ECHO to be set by different means.
m4_defun([_LT_CC_BASENAME],
-[for cc_temp in $1""; do
- case $cc_temp in
- compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;;
- distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;;
- \-*) ;;
- *) break;;
- esac
-done
-cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+[m4_require([_LT_PREPARE_CC_BASENAME])dnl
+AC_REQUIRE([_LT_DECL_SED])dnl
+AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+func_cc_basename $1
+cc_basename=$func_cc_basename_result
])
# _LT_FILEUTILS_DEFAULTS
# ----------------------
# It is okay to use these file commands and assume they have been set
-# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'.
+# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'.
m4_defun([_LT_FILEUTILS_DEFAULTS],
[: ${CP="cp -f"}
: ${MV="mv -f"}
@@ -167,6 +177,8 @@ AC_REQUIRE([AC_CANONICAL_BUILD])dnl
AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl
AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl
+_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl
+dnl
_LT_DECL([], [host_alias], [0], [The host system])dnl
_LT_DECL([], [host], [0])dnl
_LT_DECL([], [host_os], [0])dnl
@@ -196,15 +208,16 @@ m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl
m4_require([_LT_CMD_OLD_ARCHIVE])dnl
m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl
m4_require([_LT_WITH_SYSROOT])dnl
+m4_require([_LT_CMD_TRUNCATE])dnl
_LT_CONFIG_LIBTOOL_INIT([
-# See if we are running on zsh, and set the options which allow our
+# See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes INIT.
-if test -n "\${ZSH_VERSION+set}" ; then
+if test -n "\${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
])
-if test -n "${ZSH_VERSION+set}" ; then
+if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
@@ -217,7 +230,7 @@ aix3*)
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
- if test "X${COLLECT_NAMES+set}" != Xset; then
+ if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
@@ -228,14 +241,14 @@ esac
ofile=libtool
can_build_shared=yes
-# All known linkers require a `.a' archive for static linking (except MSVC,
+# All known linkers require a '.a' archive for static linking (except MSVC,
# which needs '.lib').
libext=a
-with_gnu_ld="$lt_cv_prog_gnu_ld"
+with_gnu_ld=$lt_cv_prog_gnu_ld
-old_CC="$CC"
-old_CFLAGS="$CFLAGS"
+old_CC=$CC
+old_CFLAGS=$CFLAGS
# Set sane defaults for various variables
test -z "$CC" && CC=cc
@@ -288,21 +301,21 @@ no_glob_subst='s/\*/\\\*/g'
# _LT_PROG_LTMAIN
# ---------------
-# Note that this code is called both from `configure', and `config.status'
+# Note that this code is called both from 'configure', and 'config.status'
# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably,
-# `config.status' has no value for ac_aux_dir unless we are using Automake,
+# 'config.status' has no value for ac_aux_dir unless we are using Automake,
# so we pass a copy along to make sure it has a sensible value anyway.
m4_defun([_LT_PROG_LTMAIN],
[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl
_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir'])
-ltmain="$ac_aux_dir/ltmain.sh"
+ltmain=$ac_aux_dir/ltmain.sh
])# _LT_PROG_LTMAIN
# So that we can recreate a full libtool script including additional
# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS
-# in macros and then make a single call at the end using the `libtool'
+# in macros and then make a single call at the end using the 'libtool'
# label.
@@ -434,8 +447,8 @@ m4_define([_lt_decl_all_varnames],
# _LT_CONFIG_STATUS_DECLARE([VARNAME])
# ------------------------------------
-# Quote a variable value, and forward it to `config.status' so that its
-# declaration there will have the same value as in `configure'. VARNAME
+# Quote a variable value, and forward it to 'config.status' so that its
+# declaration there will have the same value as in 'configure'. VARNAME
# must have a single quote delimited value for this to work.
m4_define([_LT_CONFIG_STATUS_DECLARE],
[$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`'])
@@ -459,7 +472,7 @@ m4_defun([_LT_CONFIG_STATUS_DECLARATIONS],
# Output comment and list of tags supported by the script
m4_defun([_LT_LIBTOOL_TAGS],
[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl
-available_tags="_LT_TAGS"dnl
+available_tags='_LT_TAGS'dnl
])
@@ -487,7 +500,7 @@ m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl
# _LT_LIBTOOL_CONFIG_VARS
# -----------------------
# Produce commented declarations of non-tagged libtool config variables
-# suitable for insertion in the LIBTOOL CONFIG section of the `libtool'
+# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool'
# script. Tagged libtool config variables (even for the LIBTOOL CONFIG
# section) are produced by _LT_LIBTOOL_TAG_VARS.
m4_defun([_LT_LIBTOOL_CONFIG_VARS],
@@ -513,8 +526,8 @@ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])])
# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of
# variables for single and double quote escaping we saved from calls
# to _LT_DECL, we can put quote escaped variables declarations
-# into `config.status', and then the shell code to quote escape them in
-# for loops in `config.status'. Finally, any additional code accumulated
+# into 'config.status', and then the shell code to quote escape them in
+# for loops in 'config.status'. Finally, any additional code accumulated
# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded.
m4_defun([_LT_CONFIG_COMMANDS],
[AC_PROVIDE_IFELSE([LT_OUTPUT],
@@ -560,7 +573,7 @@ for var in lt_decl_all_varnames([[ \
]], lt_decl_quote_varnames); do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[[\\\\\\\`\\"\\\$]]*)
- eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
@@ -573,7 +586,7 @@ for var in lt_decl_all_varnames([[ \
]], lt_decl_dquote_varnames); do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[[\\\\\\\`\\"\\\$]]*)
- eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
@@ -589,7 +602,7 @@ _LT_OUTPUT_LIBTOOL_INIT
# Generate a child script FILE with all initialization necessary to
# reuse the environment learned by the parent script, and make the
# file executable. If COMMENT is supplied, it is inserted after the
-# `#!' sequence but before initialization text begins. After this
+# '#!' sequence but before initialization text begins. After this
# macro, additional text can be appended to FILE to form the body of
# the child script. The macro ends with non-zero status if the
# file could not be fully written (such as if the disk is full).
@@ -611,7 +624,7 @@ AS_SHELL_SANITIZE
_AS_PREPARE
exec AS_MESSAGE_FD>&1
_ASEOF
-test $lt_write_fail = 0 && chmod +x $1[]dnl
+test 0 = "$lt_write_fail" && chmod +x $1[]dnl
m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT
# LT_OUTPUT
@@ -634,7 +647,7 @@ exec AS_MESSAGE_LOG_FD>>config.log
} >&AS_MESSAGE_LOG_FD
lt_cl_help="\
-\`$as_me' creates a local libtool stub from the current configuration,
+'$as_me' creates a local libtool stub from the current configuration,
for use in further configure time tests before the real libtool is
generated.
@@ -652,11 +665,11 @@ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl
m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION])
configured by $[0], generated by m4_PACKAGE_STRING.
-Copyright (C) 2010 Free Software Foundation, Inc.
+Copyright (C) 2011 Free Software Foundation, Inc.
This config.lt script is free software; the Free Software Foundation
gives unlimited permision to copy, distribute and modify it."
-while test $[#] != 0
+while test 0 != $[#]
do
case $[1] in
--version | --v* | -V )
@@ -669,10 +682,10 @@ do
lt_cl_silent=: ;;
-*) AC_MSG_ERROR([unrecognized option: $[1]
-Try \`$[0] --help' for more information.]) ;;
+Try '$[0] --help' for more information.]) ;;
*) AC_MSG_ERROR([unrecognized argument: $[1]
-Try \`$[0] --help' for more information.]) ;;
+Try '$[0] --help' for more information.]) ;;
esac
shift
done
@@ -698,7 +711,7 @@ chmod +x "$CONFIG_LT"
# open by configure. Here we exec the FD to /dev/null, effectively closing
# config.log, so it can be properly (re)opened and appended to by config.lt.
lt_cl_success=:
-test "$silent" = yes &&
+test yes = "$silent" &&
lt_config_lt_args="$lt_config_lt_args --quiet"
exec AS_MESSAGE_LOG_FD>/dev/null
$SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false
@@ -718,27 +731,31 @@ m4_defun([_LT_CONFIG],
_LT_CONFIG_SAVE_COMMANDS([
m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl
m4_if(_LT_TAG, [C], [
- # See if we are running on zsh, and set the options which allow our
+ # See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes.
- if test -n "${ZSH_VERSION+set}" ; then
+ if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
- cfgfile="${ofile}T"
+ cfgfile=${ofile}T
trap "$RM \"$cfgfile\"; exit 1" 1 2 15
$RM "$cfgfile"
cat <<_LT_EOF >> "$cfgfile"
#! $SHELL
-
-# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
-# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Generated automatically by $as_me ($PACKAGE) $VERSION
# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
# NOTE: Changes made to this file will be lost: look at ltmain.sh.
-#
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
_LT_COPYING
_LT_LIBTOOL_TAGS
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
+
# ### BEGIN LIBTOOL CONFIG
_LT_LIBTOOL_CONFIG_VARS
_LT_LIBTOOL_TAG_VARS
@@ -746,13 +763,24 @@ _LT_LIBTOOL_TAG_VARS
_LT_EOF
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_PREPARE_MUNGE_PATH_LIST
+_LT_PREPARE_CC_BASENAME
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
case $host_os in
aix3*)
cat <<\_LT_EOF >> "$cfgfile"
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
-if test "X${COLLECT_NAMES+set}" != Xset; then
+if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
@@ -769,8 +797,6 @@ _LT_EOF
sed '$q' "$ltmain" >> "$cfgfile" \
|| (rm -f "$cfgfile"; exit 1)
- _LT_PROG_REPLACE_SHELLFNS
-
mv -f "$cfgfile" "$ofile" ||
(rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
chmod +x "$ofile"
@@ -788,7 +814,6 @@ _LT_EOF
[m4_if([$1], [], [
PACKAGE='$PACKAGE'
VERSION='$VERSION'
- TIMESTAMP='$TIMESTAMP'
RM='$RM'
ofile='$ofile'], [])
])dnl /_LT_CONFIG_SAVE_COMMANDS
@@ -816,6 +841,7 @@ AC_DEFUN([LT_LANG],
m4_case([$1],
[C], [_LT_LANG(C)],
[C++], [_LT_LANG(CXX)],
+ [Go], [_LT_LANG(GO)],
[Java], [_LT_LANG(GCJ)],
[Fortran 77], [_LT_LANG(F77)],
[Fortran], [_LT_LANG(FC)],
@@ -837,6 +863,29 @@ m4_defun([_LT_LANG],
])# _LT_LANG
+m4_ifndef([AC_PROG_GO], [
+# NOTE: This macro has been submitted for inclusion into #
+# GNU Autoconf as AC_PROG_GO. When it is available in #
+# a released version of Autoconf we should remove this #
+# macro and use it instead. #
+m4_defun([AC_PROG_GO],
+[AC_LANG_PUSH(Go)dnl
+AC_ARG_VAR([GOC], [Go compiler command])dnl
+AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl
+_AC_ARG_VAR_LDFLAGS()dnl
+AC_CHECK_TOOL(GOC, gccgo)
+if test -z "$GOC"; then
+ if test -n "$ac_tool_prefix"; then
+ AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo])
+ fi
+fi
+if test -z "$GOC"; then
+ AC_CHECK_PROG(GOC, gccgo, gccgo, false)
+fi
+])#m4_defun
+])#m4_ifndef
+
+
# _LT_LANG_DEFAULT_CONFIG
# -----------------------
m4_defun([_LT_LANG_DEFAULT_CONFIG],
@@ -867,6 +916,10 @@ AC_PROVIDE_IFELSE([AC_PROG_GCJ],
m4_ifdef([LT_PROG_GCJ],
[m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])])
+AC_PROVIDE_IFELSE([AC_PROG_GO],
+ [LT_LANG(GO)],
+ [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])])
+
AC_PROVIDE_IFELSE([LT_PROG_RC],
[LT_LANG(RC)],
[m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])])
@@ -957,7 +1010,7 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod],
[lt_cv_apple_cc_single_mod=no
- if test -z "${LT_MULTI_MODULE}"; then
+ if test -z "$LT_MULTI_MODULE"; then
# By default we will add the -single_module flag. You can override
# by either setting the environment variable LT_MULTI_MODULE
# non-empty at configure time, or by adding -multi_module to the
@@ -969,7 +1022,13 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
-dynamiclib -Wl,-single_module conftest.c 2>conftest.err
_lt_result=$?
- if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
lt_cv_apple_cc_single_mod=yes
else
cat conftest.err >&AS_MESSAGE_LOG_FD
@@ -977,6 +1036,7 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
rm -rf libconftest.dylib*
rm -f conftest.*
fi])
+
AC_CACHE_CHECK([for -exported_symbols_list linker flag],
[lt_cv_ld_exported_symbols_list],
[lt_cv_ld_exported_symbols_list=no
@@ -986,8 +1046,9 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[
AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])],
[lt_cv_ld_exported_symbols_list=yes],
[lt_cv_ld_exported_symbols_list=no])
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
])
+
AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load],
[lt_cv_ld_force_load=no
cat > conftest.c << _LT_EOF
@@ -1005,7 +1066,9 @@ _LT_EOF
echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD
$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
_lt_result=$?
- if test -f conftest && test ! -s conftest.err && test $_lt_result = 0 && $GREP forced_load conftest 2>&1 >/dev/null; then
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&AS_MESSAGE_LOG_FD
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
lt_cv_ld_force_load=yes
else
cat conftest.err >&AS_MESSAGE_LOG_FD
@@ -1015,32 +1078,32 @@ _LT_EOF
])
case $host_os in
rhapsody* | darwin1.[[012]])
- _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
darwin1.*)
- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
darwin*) # darwin 5.x on
# if running on 10.5 or later, the deployment target defaults
# to the OS version, if on x86, and 10.4, the deployment
# target defaults to 10.4. Don't you love it?
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
10.0,*86*-darwin8*|10.0,*-darwin[[91]]*)
- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
- 10.[[012]]*)
- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[[012]][[,.]]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
10.*)
- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
esac
- if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
_lt_dar_single_mod='$single_module'
fi
- if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
- _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
else
- _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
fi
- if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
_lt_dsymutil='~$DSYMUTIL $lib || :'
else
_lt_dsymutil=
@@ -1050,8 +1113,8 @@ _LT_EOF
])
-# _LT_DARWIN_LINKER_FEATURES
-# --------------------------
+# _LT_DARWIN_LINKER_FEATURES([TAG])
+# ---------------------------------
# Checks for linker and compiler features on darwin
m4_defun([_LT_DARWIN_LINKER_FEATURES],
[
@@ -1060,27 +1123,29 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES],
_LT_TAGVAR(hardcode_direct, $1)=no
_LT_TAGVAR(hardcode_automatic, $1)=yes
_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
- if test "$lt_cv_ld_force_load" = "yes"; then
- _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ if test yes = "$lt_cv_ld_force_load"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes],
+ [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes])
else
_LT_TAGVAR(whole_archive_flag_spec, $1)=''
fi
_LT_TAGVAR(link_all_deplibs, $1)=yes
- _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined"
+ _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined
case $cc_basename in
- ifort*) _lt_dar_can_shared=yes ;;
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
*) _lt_dar_can_shared=$GCC ;;
esac
- if test "$_lt_dar_can_shared" = "yes"; then
+ if test yes = "$_lt_dar_can_shared"; then
output_verbose_link_cmd=func_echo_all
- _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
- _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
- _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
- _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
m4_if([$1], [CXX],
-[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then
- _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}"
- _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}"
+[ if test yes != "$lt_cv_apple_cc_single_mod"; then
+ _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil"
fi
],[])
else
@@ -1100,7 +1165,7 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES],
# Allow to override them for all tags through lt_cv_aix_libpath.
m4_defun([_LT_SYS_MODULE_PATH_AIX],
[m4_require([_LT_DECL_SED])dnl
-if test "${lt_cv_aix_libpath+set}" = set; then
+if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])],
@@ -1118,7 +1183,7 @@ else
_LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"`
fi],[])
if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then
- _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib"
+ _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib
fi
])
aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])
@@ -1138,8 +1203,8 @@ m4_define([_LT_SHELL_INIT],
# -----------------------
# Find how we can fake an echo command that does not interpret backslash.
# In particular, with Autoconf 2.60 or later we add some code to the start
-# of the generated configure script which will find a shell with a builtin
-# printf (which we can use as an echo command).
+# of the generated configure script that will find a shell with a builtin
+# printf (that we can use as an echo command).
m4_defun([_LT_PROG_ECHO_BACKSLASH],
[ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO
@@ -1167,10 +1232,10 @@ fi
# Invoke $ECHO with all args, space-separated.
func_echo_all ()
{
- $ECHO "$*"
+ $ECHO "$*"
}
-case "$ECHO" in
+case $ECHO in
printf*) AC_MSG_RESULT([printf]) ;;
print*) AC_MSG_RESULT([print -r]) ;;
*) AC_MSG_RESULT([cat]) ;;
@@ -1196,16 +1261,17 @@ _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes])
AC_DEFUN([_LT_WITH_SYSROOT],
[AC_MSG_CHECKING([for sysroot])
AC_ARG_WITH([sysroot],
-[ --with-sysroot[=DIR] Search for dependent libraries within DIR
- (or the compiler's sysroot if not specified).],
+[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@],
+ [Search for dependent libraries within DIR (or the compiler's sysroot
+ if not specified).])],
[], [with_sysroot=no])
dnl lt_sysroot will always be passed unquoted. We quote it here
dnl in case the user passed a directory name.
lt_sysroot=
-case ${with_sysroot} in #(
+case $with_sysroot in #(
yes)
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
lt_sysroot=`$CC --print-sysroot 2>/dev/null`
fi
;; #(
@@ -1215,14 +1281,14 @@ case ${with_sysroot} in #(
no|'')
;; #(
*)
- AC_MSG_RESULT([${with_sysroot}])
+ AC_MSG_RESULT([$with_sysroot])
AC_MSG_ERROR([The sysroot must be an absolute path.])
;;
esac
AC_MSG_RESULT([${lt_sysroot:-no}])
_LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl
-[dependent libraries, and in which our libraries should be installed.])])
+[dependent libraries, and where our libraries should be installed.])])
# _LT_ENABLE_LOCK
# ---------------
@@ -1230,31 +1296,33 @@ m4_defun([_LT_ENABLE_LOCK],
[AC_ARG_ENABLE([libtool-lock],
[AS_HELP_STRING([--disable-libtool-lock],
[avoid locking (might break parallel builds)])])
-test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
# Some flags need to be propagated to the compiler or linker for good
# libtool support.
case $host in
ia64-*-hpux*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
echo 'int i;' > conftest.$ac_ext
if AC_TRY_EVAL(ac_compile); then
case `/usr/bin/file conftest.$ac_objext` in
*ELF-32*)
- HPUX_IA64_MODE="32"
+ HPUX_IA64_MODE=32
;;
*ELF-64*)
- HPUX_IA64_MODE="64"
+ HPUX_IA64_MODE=64
;;
esac
fi
rm -rf conftest*
;;
*-*-irix6*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
if AC_TRY_EVAL(ac_compile); then
- if test "$lt_cv_prog_gnu_ld" = yes; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
case `/usr/bin/file conftest.$ac_objext` in
*32-bit*)
LD="${LD-ld} -melf32bsmip"
@@ -1283,9 +1351,46 @@ ia64-*-hpux*)
rm -rf conftest*
;;
-x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext
+ if AC_TRY_EVAL(ac_compile); then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
echo 'int i;' > conftest.$ac_ext
if AC_TRY_EVAL(ac_compile); then
case `/usr/bin/file conftest.o` in
@@ -1295,9 +1400,19 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
LD="${LD-ld} -m elf_i386_fbsd"
;;
x86_64-*linux*)
- LD="${LD-ld} -m elf_i386"
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
;;
- ppc64-*linux*|powerpc64-*linux*)
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
+ ;;
+ powerpc64-*linux*)
LD="${LD-ld} -m elf32ppclinux"
;;
s390x-*linux*)
@@ -1316,7 +1431,10 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
x86_64-*linux*)
LD="${LD-ld} -m elf_x86_64"
;;
- ppc*-*linux*|powerpc*-*linux*)
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
LD="${LD-ld} -m elf64ppc"
;;
s390*-*linux*|s390*-*tpf*)
@@ -1334,25 +1452,39 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
*-*-sco3.2v5*)
# On SCO OpenServer 5, we need -belf to get full-featured binaries.
- SAVE_CFLAGS="$CFLAGS"
+ SAVE_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -belf"
AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf,
[AC_LANG_PUSH(C)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no])
AC_LANG_POP])
- if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ if test yes != "$lt_cv_cc_needs_belf"; then
# this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
- CFLAGS="$SAVE_CFLAGS"
+ CFLAGS=$SAVE_CFLAGS
fi
;;
-sparc*-*solaris*)
- # Find out which ABI we are using.
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
echo 'int i;' > conftest.$ac_ext
if AC_TRY_EVAL(ac_compile); then
case `/usr/bin/file conftest.o` in
*64-bit*)
case $lt_cv_prog_gnu_ld in
- yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
*)
if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
LD="${LD-ld} -64"
@@ -1366,7 +1498,7 @@ sparc*-*solaris*)
;;
esac
-need_locks="$enable_libtool_lock"
+need_locks=$enable_libtool_lock
])# _LT_ENABLE_LOCK
@@ -1385,11 +1517,11 @@ AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
[echo conftest.$ac_objext > conftest.lst
lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD'
AC_TRY_EVAL([lt_ar_try])
- if test "$ac_status" -eq 0; then
+ if test 0 -eq "$ac_status"; then
# Ensure the archiver fails upon bogus file names.
rm -f conftest.$ac_objext libconftest.a
AC_TRY_EVAL([lt_ar_try])
- if test "$ac_status" -ne 0; then
+ if test 0 -ne "$ac_status"; then
lt_cv_ar_at_file=@
fi
fi
@@ -1397,7 +1529,7 @@ AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file],
])
])
-if test "x$lt_cv_ar_at_file" = xno; then
+if test no = "$lt_cv_ar_at_file"; then
archiver_list_spec=
else
archiver_list_spec=$lt_cv_ar_at_file
@@ -1428,14 +1560,14 @@ old_postuninstall_cmds=
if test -n "$RANLIB"; then
case $host_os in
- openbsd*)
- old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
;;
*)
- old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
;;
esac
- old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
fi
case $host_os in
@@ -1464,7 +1596,7 @@ AC_CACHE_CHECK([$1], [$2],
[$2=no
m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4])
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
- lt_compiler_flag="$3"
+ lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
@@ -1491,7 +1623,7 @@ AC_CACHE_CHECK([$1], [$2],
$RM conftest*
])
-if test x"[$]$2" = xyes; then
+if test yes = "[$]$2"; then
m4_if([$5], , :, [$5])
else
m4_if([$6], , :, [$6])
@@ -1513,7 +1645,7 @@ AC_DEFUN([_LT_LINKER_OPTION],
m4_require([_LT_DECL_SED])dnl
AC_CACHE_CHECK([$1], [$2],
[$2=no
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $3"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
@@ -1532,10 +1664,10 @@ AC_CACHE_CHECK([$1], [$2],
fi
fi
$RM -r conftest*
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
])
-if test x"[$]$2" = xyes; then
+if test yes = "[$]$2"; then
m4_if([$4], , :, [$4])
else
m4_if([$5], , :, [$5])
@@ -1556,7 +1688,7 @@ AC_DEFUN([LT_CMD_MAX_LEN],
AC_MSG_CHECKING([the maximum length of command line arguments])
AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
i=0
- teststring="ABCD"
+ teststring=ABCD
case $build_os in
msdosdjgpp*)
@@ -1596,7 +1728,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
lt_cv_sys_max_cmd_len=8192;
;;
- netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
# This has been around since 386BSD, at least. Likely further.
if test -x /sbin/sysctl; then
lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
@@ -1615,6 +1747,11 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
lt_cv_sys_max_cmd_len=196608
;;
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
osf*)
# Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
# due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
@@ -1641,22 +1778,23 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
;;
*)
lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
- if test -n "$lt_cv_sys_max_cmd_len"; then
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
else
# Make teststring a little bigger before we do anything with it.
# a 1K string should be a reasonable start.
- for i in 1 2 3 4 5 6 7 8 ; do
+ for i in 1 2 3 4 5 6 7 8; do
teststring=$teststring$teststring
done
SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
# If test is not a shell built-in, we'll probably end up computing a
# maximum length that is only half of the actual maximum length, but
# we can't tell.
- while { test "X"`func_fallback_echo "$teststring$teststring" 2>/dev/null` \
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
= "X$teststring$teststring"; } >/dev/null 2>&1 &&
- test $i != 17 # 1/2 MB should be enough
+ test 17 != "$i" # 1/2 MB should be enough
do
i=`expr $i + 1`
teststring=$teststring$teststring
@@ -1672,7 +1810,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl
;;
esac
])
-if test -n $lt_cv_sys_max_cmd_len ; then
+if test -n "$lt_cv_sys_max_cmd_len"; then
AC_MSG_RESULT($lt_cv_sys_max_cmd_len)
else
AC_MSG_RESULT(none)
@@ -1700,7 +1838,7 @@ m4_defun([_LT_HEADER_DLFCN],
# ----------------------------------------------------------------
m4_defun([_LT_TRY_DLOPEN_SELF],
[m4_require([_LT_HEADER_DLFCN])dnl
-if test "$cross_compiling" = yes; then :
+if test yes = "$cross_compiling"; then :
[$4]
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
@@ -1747,9 +1885,9 @@ else
# endif
#endif
-/* When -fvisbility=hidden is used, assume the code has been annotated
+/* When -fvisibility=hidden is used, assume the code has been annotated
correspondingly for the symbols needed. */
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
int fnord () __attribute__((visibility("default")));
#endif
@@ -1775,7 +1913,7 @@ int main ()
return status;
}]
_LT_EOF
- if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then
+ if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then
(./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null
lt_status=$?
case x$lt_status in
@@ -1796,7 +1934,7 @@ rm -fr conftest*
# ------------------
AC_DEFUN([LT_SYS_DLOPEN_SELF],
[m4_require([_LT_HEADER_DLFCN])dnl
-if test "x$enable_dlopen" != xyes; then
+if test yes != "$enable_dlopen"; then
enable_dlopen=unknown
enable_dlopen_self=unknown
enable_dlopen_self_static=unknown
@@ -1806,44 +1944,52 @@ else
case $host_os in
beos*)
- lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen=load_add_on
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
;;
mingw* | pw32* | cegcc*)
- lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen=LoadLibrary
lt_cv_dlopen_libs=
;;
cygwin*)
- lt_cv_dlopen="dlopen"
+ lt_cv_dlopen=dlopen
lt_cv_dlopen_libs=
;;
darwin*)
- # if libdl is installed we need to link against it
+ # if libdl is installed we need to link against it
AC_CHECK_LIB([dl], [dlopen],
- [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[
- lt_cv_dlopen="dyld"
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[
+ lt_cv_dlopen=dyld
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
])
;;
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
*)
AC_CHECK_FUNC([shl_load],
- [lt_cv_dlopen="shl_load"],
+ [lt_cv_dlopen=shl_load],
[AC_CHECK_LIB([dld], [shl_load],
- [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"],
+ [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld],
[AC_CHECK_FUNC([dlopen],
- [lt_cv_dlopen="dlopen"],
+ [lt_cv_dlopen=dlopen],
[AC_CHECK_LIB([dl], [dlopen],
- [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],
[AC_CHECK_LIB([svld], [dlopen],
- [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"],
+ [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld],
[AC_CHECK_LIB([dld], [dld_link],
- [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"])
+ [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld])
])
])
])
@@ -1852,21 +1998,21 @@ else
;;
esac
- if test "x$lt_cv_dlopen" != xno; then
- enable_dlopen=yes
- else
+ if test no = "$lt_cv_dlopen"; then
enable_dlopen=no
+ else
+ enable_dlopen=yes
fi
case $lt_cv_dlopen in
dlopen)
- save_CPPFLAGS="$CPPFLAGS"
- test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
- save_LIBS="$LIBS"
+ save_LIBS=$LIBS
LIBS="$lt_cv_dlopen_libs $LIBS"
AC_CACHE_CHECK([whether a program can dlopen itself],
@@ -1876,7 +2022,7 @@ else
lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross)
])
- if test "x$lt_cv_dlopen_self" = xyes; then
+ if test yes = "$lt_cv_dlopen_self"; then
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
AC_CACHE_CHECK([whether a statically linked program can dlopen itself],
lt_cv_dlopen_self_static, [dnl
@@ -1886,9 +2032,9 @@ else
])
fi
- CPPFLAGS="$save_CPPFLAGS"
- LDFLAGS="$save_LDFLAGS"
- LIBS="$save_LIBS"
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
;;
esac
@@ -1980,8 +2126,8 @@ m4_defun([_LT_COMPILER_FILE_LOCKS],
m4_require([_LT_FILEUTILS_DEFAULTS])dnl
_LT_COMPILER_C_O([$1])
-hard_links="nottested"
-if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then
+hard_links=nottested
+if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then
# do not overwrite the value of need_locks provided by the user
AC_MSG_CHECKING([if we can lock with hard links])
hard_links=yes
@@ -1991,8 +2137,8 @@ if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" !=
ln conftest.a conftest.b 2>&5 || hard_links=no
ln conftest.a conftest.b 2>/dev/null && hard_links=no
AC_MSG_RESULT([$hard_links])
- if test "$hard_links" = no; then
- AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe])
+ if test no = "$hard_links"; then
+ AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe])
need_locks=warn
fi
else
@@ -2019,8 +2165,8 @@ objdir=$lt_cv_objdir
_LT_DECL([], [objdir], [0],
[The name of the directory that contains temporary libtool files])dnl
m4_pattern_allow([LT_OBJDIR])dnl
-AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/",
- [Define to the sub-directory in which libtool stores uninstalled libraries.])
+AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/",
+ [Define to the sub-directory where libtool stores uninstalled libraries.])
])# _LT_CHECK_OBJDIR
@@ -2032,15 +2178,15 @@ m4_defun([_LT_LINKER_HARDCODE_LIBPATH],
_LT_TAGVAR(hardcode_action, $1)=
if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" ||
test -n "$_LT_TAGVAR(runpath_var, $1)" ||
- test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then
+ test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then
# We can hardcode non-existent directories.
- if test "$_LT_TAGVAR(hardcode_direct, $1)" != no &&
+ if test no != "$_LT_TAGVAR(hardcode_direct, $1)" &&
# If the only mechanism to avoid hardcoding is shlibpath_var, we
# have to relink, otherwise we might link with an installed library
# when we should be linking with a yet-to-be-installed one
- ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no &&
- test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" &&
+ test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then
# Linking always hardcodes the temporary library directory.
_LT_TAGVAR(hardcode_action, $1)=relink
else
@@ -2054,12 +2200,12 @@ else
fi
AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)])
-if test "$_LT_TAGVAR(hardcode_action, $1)" = relink ||
- test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then
+if test relink = "$_LT_TAGVAR(hardcode_action, $1)" ||
+ test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then
# Fast installation is not supported
enable_fast_install=no
-elif test "$shlibpath_overrides_runpath" = yes ||
- test "$enable_shared" = no; then
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
# Fast installation is not necessary
enable_fast_install=needless
fi
@@ -2083,7 +2229,7 @@ else
# FIXME - insert some real tests, host_os isn't really good enough
case $host_os in
darwin*)
- if test -n "$STRIP" ; then
+ if test -n "$STRIP"; then
striplib="$STRIP -x"
old_striplib="$STRIP -S"
AC_MSG_RESULT([yes])
@@ -2101,6 +2247,47 @@ _LT_DECL([], [striplib], [1])
])# _LT_CMD_STRIPLIB
+# _LT_PREPARE_MUNGE_PATH_LIST
+# ---------------------------
+# Make sure func_munge_path_list() is defined correctly.
+m4_defun([_LT_PREPARE_MUNGE_PATH_LIST],
+[[# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x@S|@2 in
+ x)
+ ;;
+ *:)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\"
+ ;;
+ x:*)
+ eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\"
+ ;;
+ *)
+ eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+]])# _LT_PREPARE_PATH_LIST
+
+
# _LT_SYS_DYNAMIC_LINKER([TAG])
# -----------------------------
# PORTME Fill in your ld.so characteristics
@@ -2111,17 +2298,18 @@ m4_require([_LT_FILEUTILS_DEFAULTS])dnl
m4_require([_LT_DECL_OBJDUMP])dnl
m4_require([_LT_DECL_SED])dnl
m4_require([_LT_CHECK_SHELL_FEATURES])dnl
+m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl
AC_MSG_CHECKING([dynamic linker characteristics])
m4_if([$1],
[], [
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
case $host_os in
- darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
- *) lt_awk_arg="/^libraries:/" ;;
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
esac
case $host_os in
- mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;;
- *) lt_sed_strip_eq="s,=/,/,g" ;;
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
esac
lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
case $lt_search_path_spec in
@@ -2137,28 +2325,35 @@ if test "$GCC" = yes; then
;;
esac
# Ok, now we have the path, separated by spaces, we can step through it
- # and add multilib dir if necessary.
+ # and add multilib dir if necessary...
lt_tmp_lt_search_path_spec=
- lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
for lt_sys_path in $lt_search_path_spec; do
- if test -d "$lt_sys_path/$lt_multi_os_dir"; then
- lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
- else
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
test -d "$lt_sys_path" && \
lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
fi
done
lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
-BEGIN {RS=" "; FS="/|\n";} {
- lt_foo="";
- lt_count=0;
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
for (lt_i = NF; lt_i > 0; lt_i--) {
if ($lt_i != "" && $lt_i != ".") {
if ($lt_i == "..") {
lt_count++;
} else {
if (lt_count == 0) {
- lt_foo="/" $lt_i lt_foo;
+ lt_foo = "/" $lt_i lt_foo;
} else {
lt_count--;
}
@@ -2172,7 +2367,7 @@ BEGIN {RS=" "; FS="/|\n";} {
# for these hosts.
case $host_os in
mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
- $SED 's,/\([[A-Za-z]]:\),\1,g'` ;;
+ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;;
esac
sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
else
@@ -2181,7 +2376,7 @@ fi])
library_names_spec=
libname_spec='lib$name'
soname_spec=
-shrext_cmds=".so"
+shrext_cmds=.so
postinstall_cmds=
postuninstall_cmds=
finish_cmds=
@@ -2198,56 +2393,109 @@ hardcode_into_libs=no
# flags to be left without arguments
need_version=unknown
+AC_ARG_VAR([LT_SYS_LIBRARY_PATH],
+[User-defined run-time library search path.])
+
case $host_os in
aix3*)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
shlibpath_var=LIBPATH
# AIX 3 has no versioning support, so we append a major version to the name.
- soname_spec='${libname}${release}${shared_ext}$major'
+ soname_spec='$libname$release$shared_ext$major'
;;
aix[[4-9]]*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
hardcode_into_libs=yes
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 supports IA64
- library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
else
# With GCC up to 2.95.x, collect2 would create an import file
# for dependence libraries. The import file would start with
- # the line `#! .'. This would cause the generated library to
- # depend on `.', always an invalid library. This was fixed in
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
# development snapshots of GCC prior to 3.0.
case $host_os in
aix4 | aix4.[[01]] | aix4.[[01]].*)
if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
echo ' yes '
- echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
:
else
can_build_shared=no
fi
;;
esac
- # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
# soname into executable. Probably we can add versioning support to
# collect2, so additional links can be useful in future.
- if test "$aix_use_runtimelinking" = yes; then
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
# If using run time linking (on AIX 4.2 or later) use lib<name>.so
# instead of lib<name>.a to let people know that these are not
# typical AIX shared libraries.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a[(]lib.so.V[)]'
# We preserve .a as extension for shared libraries through AIX4.2
# and later when we are not doing run time linking.
- library_names_spec='${libname}${release}.a $libname.a'
- soname_spec='${libname}${release}${shared_ext}$major'
- fi
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
shlibpath_var=LIBPATH
fi
;;
@@ -2257,27 +2505,27 @@ amigaos*)
powerpc)
# Since July 2007 AmigaOS4 officially supports .so libraries.
# When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
;;
m68k)
library_names_spec='$libname.ixlibrary $libname.a'
# Create ${libname}_ixlibrary.a entries in /sys/libs.
- finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
;;
esac
;;
beos*)
- library_names_spec='${libname}${shared_ext}'
+ library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"
shlibpath_var=LIBRARY_PATH
;;
bsdi[[45]]*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
@@ -2289,7 +2537,7 @@ bsdi[[45]]*)
cygwin* | mingw* | pw32* | cegcc*)
version_type=windows
- shrext_cmds=".dll"
+ shrext_cmds=.dll
need_version=no
need_lib_prefix=no
@@ -2298,8 +2546,8 @@ cygwin* | mingw* | pw32* | cegcc*)
# gcc
library_names_spec='$libname.dll.a'
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname~
@@ -2315,17 +2563,17 @@ cygwin* | mingw* | pw32* | cegcc*)
case $host_os in
cygwin*)
# Cygwin DLLs use 'cyg' prefix rather than 'lib'
- soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
m4_if([$1], [],[
sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"])
;;
mingw* | cegcc*)
# MinGW DLLs use traditional 'lib' prefix
- soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
;;
pw32*)
# pw32 DLLs use 'pw' prefix rather than 'lib'
- library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
;;
esac
dynamic_linker='Win32 ld.exe'
@@ -2334,8 +2582,8 @@ m4_if([$1], [],[
*,cl*)
# Native MSVC
libname_spec='$name'
- soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}'
- library_names_spec='${libname}.dll.lib'
+ soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
case $build_os in
mingw*)
@@ -2362,7 +2610,7 @@ m4_if([$1], [],[
sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
;;
*)
- sys_lib_search_path_spec="$LIB"
+ sys_lib_search_path_spec=$LIB
if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then
# It is most probably a Windows format PATH.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
@@ -2375,8 +2623,8 @@ m4_if([$1], [],[
esac
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname'
@@ -2389,7 +2637,7 @@ m4_if([$1], [],[
*)
# Assume MSVC wrapper
- library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib'
dynamic_linker='Win32 ld.exe'
;;
esac
@@ -2402,8 +2650,8 @@ darwin* | rhapsody*)
version_type=darwin
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
- soname_spec='${libname}${release}${major}$shared_ext'
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
shlibpath_overrides_runpath=yes
shlibpath_var=DYLD_LIBRARY_PATH
shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
@@ -2413,18 +2661,14 @@ m4_if([$1], [],[
;;
dgux*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
-freebsd1*)
- dynamic_linker=no
- ;;
-
freebsd* | dragonfly*)
# DragonFly does not have aout. When/if they implement a new
# versioning mechanism, adjust this.
@@ -2432,25 +2676,26 @@ freebsd* | dragonfly*)
objformat=`/usr/bin/objformat`
else
case $host_os in
- freebsd[[123]]*) objformat=aout ;;
+ freebsd[[23]].*) objformat=aout ;;
*) objformat=elf ;;
esac
fi
version_type=freebsd-$objformat
case $version_type in
freebsd-elf*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
need_version=no
need_lib_prefix=no
;;
freebsd-*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
need_version=yes
;;
esac
shlibpath_var=LD_LIBRARY_PATH
case $host_os in
- freebsd2*)
+ freebsd2.*)
shlibpath_overrides_runpath=yes
;;
freebsd3.[[01]]* | freebsdelf3.[[01]]*)
@@ -2469,25 +2714,15 @@ freebsd* | dragonfly*)
esac
;;
-gnu*)
- version_type=linux
- need_lib_prefix=no
- need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- shlibpath_var=LD_LIBRARY_PATH
- hardcode_into_libs=yes
- ;;
-
haiku*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
dynamic_linker="$host_os runtime_loader"
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LIBRARY_PATH
- shlibpath_overrides_runpath=yes
+ shlibpath_overrides_runpath=no
sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
hardcode_into_libs=yes
;;
@@ -2505,14 +2740,15 @@ hpux9* | hpux10* | hpux11*)
dynamic_linker="$host_os dld.so"
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- if test "X$HPUX_IA64_MODE" = X32; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
else
sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
fi
- sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
hppa*64*)
shrext_cmds='.sl'
@@ -2520,8 +2756,8 @@ hpux9* | hpux10* | hpux11*)
dynamic_linker="$host_os dld.sl"
shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
@@ -2530,8 +2766,8 @@ hpux9* | hpux10* | hpux11*)
dynamic_linker="$host_os dld.sl"
shlibpath_var=SHLIB_PATH
shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
;;
esac
# HP-UX runs *really* slowly unless shared libraries are mode 555, ...
@@ -2541,11 +2777,11 @@ hpux9* | hpux10* | hpux11*)
;;
interix[[3-9]]*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
@@ -2556,16 +2792,16 @@ irix5* | irix6* | nonstopux*)
case $host_os in
nonstopux*) version_type=nonstopux ;;
*)
- if test "$lt_cv_prog_gnu_ld" = yes; then
- version_type=linux
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
else
version_type=irix
fi ;;
esac
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
case $host_os in
irix5* | nonstopux*)
libsuff= shlibsuff=
@@ -2584,8 +2820,8 @@ irix5* | irix6* | nonstopux*)
esac
shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
shlibpath_overrides_runpath=no
- sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
- sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
hardcode_into_libs=yes
;;
@@ -2594,13 +2830,33 @@ linux*oldld* | linux*aout* | linux*coff*)
dynamic_linker=no
;;
-# This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
- version_type=linux
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
@@ -2625,7 +2881,12 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu)
# before this can be enabled.
hardcode_into_libs=yes
- # Append ld.so.conf contents to the search path
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
if test -f /etc/ld.so.conf; then
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
@@ -2645,12 +2906,12 @@ netbsd*)
need_lib_prefix=no
need_version=no
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
dynamic_linker='NetBSD (a.out) ld.so'
else
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='NetBSD ld.elf_so'
fi
shlibpath_var=LD_LIBRARY_PATH
@@ -2659,8 +2920,8 @@ netbsd*)
;;
newsos6)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
;;
@@ -2669,58 +2930,68 @@ newsos6)
version_type=qnx
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='ldqnx.so'
;;
-openbsd*)
+openbsd* | bitrig*)
version_type=sunos
- sys_lib_dlsearch_path_spec="/usr/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib
need_lib_prefix=no
- # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
- case $host_os in
- openbsd3.3 | openbsd3.3.*) need_version=yes ;;
- *) need_version=no ;;
- esac
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
- finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
- shlibpath_var=LD_LIBRARY_PATH
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
- case $host_os in
- openbsd2.[[89]] | openbsd2.[[89]].*)
- shlibpath_overrides_runpath=no
- ;;
- *)
- shlibpath_overrides_runpath=yes
- ;;
- esac
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
else
- shlibpath_overrides_runpath=yes
+ need_version=yes
fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
;;
os2*)
libname_spec='$name'
- shrext_cmds=".dll"
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
need_lib_prefix=no
- library_names_spec='$libname${shared_ext} $libname.a'
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
dynamic_linker='OS/2 ld.exe'
- shlibpath_var=LIBPATH
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
;;
osf3* | osf4* | osf5*)
version_type=osf
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
- sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
rdos*)
@@ -2728,11 +2999,11 @@ rdos*)
;;
solaris*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
@@ -2742,20 +3013,20 @@ solaris*)
sunos4*)
version_type=sunos
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
need_lib_prefix=no
fi
need_version=yes
;;
sysv4 | sysv4.3*)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
case $host_vendor in
sni)
@@ -2776,24 +3047,24 @@ sysv4 | sysv4.3*)
;;
sysv4*MP*)
- if test -d /usr/nec ;then
- version_type=linux
- library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
- soname_spec='$libname${shared_ext}.$major'
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
shlibpath_var=LD_LIBRARY_PATH
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
- version_type=freebsd-elf
+ version_type=sco
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
else
sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
@@ -2808,19 +3079,19 @@ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
tpf*)
# TPF is a cross-target only. Preferred cross-host = GNU/Linux.
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
uts4*)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
@@ -2829,20 +3100,30 @@ uts4*)
;;
esac
AC_MSG_RESULT([$dynamic_linker])
-test "$dynamic_linker" = no && can_build_shared=no
+test no = "$dynamic_linker" && can_build_shared=no
variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
fi
-if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
- sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
fi
-if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
- sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
fi
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
_LT_DECL([], [variables_saved_for_relink], [1],
[Variables whose values should be saved in libtool wrapper scripts and
restored at link time])
@@ -2875,39 +3156,41 @@ _LT_DECL([], [hardcode_into_libs], [0],
[Whether we should hardcode library paths into libraries])
_LT_DECL([], [sys_lib_search_path_spec], [2],
[Compile-time system search path for libraries])
-_LT_DECL([], [sys_lib_dlsearch_path_spec], [2],
- [Run-time system search path for libraries])
+_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2],
+ [Detected run-time system search path for libraries])
+_LT_DECL([], [configure_time_lt_sys_library_path], [2],
+ [Explicit LT_SYS_LIBRARY_PATH set during ./configure time])
])# _LT_SYS_DYNAMIC_LINKER
# _LT_PATH_TOOL_PREFIX(TOOL)
# --------------------------
-# find a file program which can recognize shared library
+# find a file program that can recognize shared library
AC_DEFUN([_LT_PATH_TOOL_PREFIX],
[m4_require([_LT_DECL_EGREP])dnl
AC_MSG_CHECKING([for $1])
AC_CACHE_VAL(lt_cv_path_MAGIC_CMD,
[case $MAGIC_CMD in
[[\\/*] | ?:[\\/]*])
- lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
*)
- lt_save_MAGIC_CMD="$MAGIC_CMD"
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
dnl $ac_dummy forces splitting on constant user-supplied paths.
dnl POSIX.2 word splitting is done only on the output of word expansions,
dnl not every word. This closes a longstanding sh security hole.
ac_dummy="m4_if([$2], , $PATH, [$2])"
for ac_dir in $ac_dummy; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- if test -f $ac_dir/$1; then
- lt_cv_path_MAGIC_CMD="$ac_dir/$1"
+ if test -f "$ac_dir/$1"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"$1"
if test -n "$file_magic_test_file"; then
case $deplibs_check_method in
"file_magic "*)
file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
- MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
$EGREP "$file_magic_regex" > /dev/null; then
:
@@ -2930,11 +3213,11 @@ _LT_EOF
break
fi
done
- IFS="$lt_save_ifs"
- MAGIC_CMD="$lt_save_MAGIC_CMD"
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
;;
esac])
-MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if test -n "$MAGIC_CMD"; then
AC_MSG_RESULT($MAGIC_CMD)
else
@@ -2952,7 +3235,7 @@ dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], [])
# _LT_PATH_MAGIC
# --------------
-# find a file program which can recognize a shared library
+# find a file program that can recognize a shared library
m4_defun([_LT_PATH_MAGIC],
[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH)
if test -z "$lt_cv_path_MAGIC_CMD"; then
@@ -2979,16 +3262,16 @@ m4_require([_LT_PROG_ECHO_BACKSLASH])dnl
AC_ARG_WITH([gnu-ld],
[AS_HELP_STRING([--with-gnu-ld],
[assume the C compiler uses GNU ld @<:@default=no@:>@])],
- [test "$withval" = no || with_gnu_ld=yes],
+ [test no = "$withval" || with_gnu_ld=yes],
[with_gnu_ld=no])dnl
ac_prog=ld
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
# Check if gcc -print-prog-name=ld gives a path.
AC_MSG_CHECKING([for ld used by $CC])
case $host in
*-*-mingw*)
- # gcc leaves a trailing carriage return which upsets mingw
+ # gcc leaves a trailing carriage return, which upsets mingw
ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
*)
ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
@@ -3002,7 +3285,7 @@ if test "$GCC" = yes; then
while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
done
- test -z "$LD" && LD="$ac_prog"
+ test -z "$LD" && LD=$ac_prog
;;
"")
# If it fails, then pretend we aren't using GCC.
@@ -3013,37 +3296,37 @@ if test "$GCC" = yes; then
with_gnu_ld=unknown
;;
esac
-elif test "$with_gnu_ld" = yes; then
+elif test yes = "$with_gnu_ld"; then
AC_MSG_CHECKING([for GNU ld])
else
AC_MSG_CHECKING([for non-GNU ld])
fi
AC_CACHE_VAL(lt_cv_path_LD,
[if test -z "$LD"; then
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
- lt_cv_path_LD="$ac_dir/$ac_prog"
+ lt_cv_path_LD=$ac_dir/$ac_prog
# Check to see if the program is GNU ld. I'd rather use --version,
# but apparently some variants of GNU ld only accept -v.
# Break only if it was the GNU/non-GNU ld that we prefer.
case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
- test "$with_gnu_ld" != no && break
+ test no != "$with_gnu_ld" && break
;;
*)
- test "$with_gnu_ld" != yes && break
+ test yes != "$with_gnu_ld" && break
;;
esac
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
else
- lt_cv_path_LD="$LD" # Let the user override the test with a path.
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
fi])
-LD="$lt_cv_path_LD"
+LD=$lt_cv_path_LD
if test -n "$LD"; then
AC_MSG_RESULT($LD)
else
@@ -3097,13 +3380,13 @@ esac
reload_cmds='$LD$reload_flag -o $output$reload_objs'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
- if test "$GCC" != yes; then
+ if test yes != "$GCC"; then
reload_cmds=false
fi
;;
darwin*)
- if test "$GCC" = yes; then
- reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
else
reload_cmds='$LD$reload_flag -o $output$reload_objs'
fi
@@ -3114,6 +3397,43 @@ _LT_TAGDECL([], [reload_cmds], [2])dnl
])# _LT_CMD_RELOAD
+# _LT_PATH_DD
+# -----------
+# find a working dd
+m4_defun([_LT_PATH_DD],
+[AC_CACHE_CHECK([for a working dd], [ac_cv_path_lt_DD],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd],
+[if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi])
+rm -f conftest.i conftest2.i conftest.out])
+])# _LT_PATH_DD
+
+
+# _LT_CMD_TRUNCATE
+# ----------------
+# find command to truncate a binary pipe
+m4_defun([_LT_CMD_TRUNCATE],
+[m4_require([_LT_PATH_DD])
+AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin],
+[printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"])
+_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1],
+ [Command to truncate a binary pipe])
+])# _LT_CMD_TRUNCATE
+
+
# _LT_CHECK_MAGIC_METHOD
# ----------------------
# how to check for library dependencies
@@ -3129,13 +3449,13 @@ lt_cv_deplibs_check_method='unknown'
# Need to set the preceding variable on all platforms that support
# interlibrary dependencies.
# 'none' -- dependencies not supported.
-# `unknown' -- same as none, but documents that we really don't know.
+# 'unknown' -- same as none, but documents that we really don't know.
# 'pass_all' -- all dependencies passed with no checks.
# 'test_compile' -- check by making test program.
# 'file_magic [[regex]]' -- check by looking for files in library path
-# which responds to the $file_magic_cmd with a given extended regex.
-# If you have `file' or equivalent on your system and you're not sure
-# whether `pass_all' will *always* work, you probably want this one.
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
case $host_os in
aix[[4-9]]*)
@@ -3162,8 +3482,7 @@ mingw* | pw32*)
# Base MSYS/MinGW do not provide the 'file' command needed by
# func_win32_libid shell function, so use a weaker test based on 'objdump',
# unless we find 'file', for example because we are cross-compiling.
- # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
- if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+ if ( file / ) >/dev/null 2>&1; then
lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
lt_cv_file_magic_cmd='func_win32_libid'
else
@@ -3199,10 +3518,6 @@ freebsd* | dragonfly*)
fi
;;
-gnu*)
- lt_cv_deplibs_check_method=pass_all
- ;;
-
haiku*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -3240,8 +3555,8 @@ irix5* | irix6* | nonstopux*)
lt_cv_deplibs_check_method=pass_all
;;
-# This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -3263,8 +3578,8 @@ newos6*)
lt_cv_deplibs_check_method=pass_all
;;
-openbsd*)
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$'
else
lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$'
@@ -3317,6 +3632,9 @@ sysv4 | sysv4.3*)
tpf*)
lt_cv_deplibs_check_method=pass_all
;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
esac
])
@@ -3357,33 +3675,38 @@ AC_DEFUN([LT_PATH_NM],
AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM,
[if test -n "$NM"; then
# Let the user override the test.
- lt_cv_path_NM="$NM"
+ lt_cv_path_NM=$NM
else
- lt_nm_to_check="${ac_tool_prefix}nm"
+ lt_nm_to_check=${ac_tool_prefix}nm
if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
lt_nm_to_check="$lt_nm_to_check nm"
fi
for lt_tmp_nm in $lt_nm_to_check; do
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- tmp_nm="$ac_dir/$lt_tmp_nm"
- if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
# Check to see if the nm accepts a BSD-compat flag.
- # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
# nm: unknown option "B" ignored
# Tru64's nm complains that /dev/null is an invalid object file
- case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
- */dev/null* | *'Invalid file or object type'*)
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
lt_cv_path_NM="$tmp_nm -B"
- break
+ break 2
;;
*)
case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
*/dev/null*)
lt_cv_path_NM="$tmp_nm -p"
- break
+ break 2
;;
*)
lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
@@ -3394,21 +3717,21 @@ else
esac
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
done
: ${lt_cv_path_NM=no}
fi])
-if test "$lt_cv_path_NM" != "no"; then
- NM="$lt_cv_path_NM"
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
else
# Didn't find any BSD compatible name lister, look for dumpbin.
if test -n "$DUMPBIN"; then :
# Let the user override the test.
else
AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :)
- case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
*COFF*)
- DUMPBIN="$DUMPBIN -symbols"
+ DUMPBIN="$DUMPBIN -symbols -headers"
;;
*)
DUMPBIN=:
@@ -3416,8 +3739,8 @@ else
esac
fi
AC_SUBST([DUMPBIN])
- if test "$DUMPBIN" != ":"; then
- NM="$DUMPBIN"
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
fi
fi
test -z "$NM" && NM=nm
@@ -3463,8 +3786,8 @@ lt_cv_sharedlib_from_linklib_cmd,
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
- # two different shell functions defined in ltmain.sh
- # decide which to use based on capabilities of $DLLTOOL
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
case `$DLLTOOL --help 2>&1` in
*--identify-strict*)
lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
@@ -3476,7 +3799,7 @@ cygwin* | mingw* | pw32* | cegcc*)
;;
*)
# fallback: assume linklib IS sharedlib
- lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
;;
esac
])
@@ -3503,13 +3826,28 @@ AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool
lt_cv_path_mainfest_tool=yes
fi
rm -f conftest*])
-if test "x$lt_cv_path_mainfest_tool" != xyes; then
+if test yes != "$lt_cv_path_mainfest_tool"; then
MANIFEST_TOOL=:
fi
_LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl
])# _LT_PATH_MANIFEST_TOOL
+# _LT_DLL_DEF_P([FILE])
+# ---------------------
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with func_dll_def_p in the libtool script
+AC_DEFUN([_LT_DLL_DEF_P],
+[dnl
+ test DEF = "`$SED -n dnl
+ -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace
+ -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments
+ -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl
+ -e q dnl Only consider the first "real" line
+ $1`" dnl
+])# _LT_DLL_DEF_P
+
+
# LT_LIB_M
# --------
# check for math library
@@ -3521,11 +3859,11 @@ case $host in
# These system don't have libm, or don't need it
;;
*-ncr-sysv4.3*)
- AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw")
+ AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw)
AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm")
;;
*)
- AC_CHECK_LIB(m, cos, LIBM="-lm")
+ AC_CHECK_LIB(m, cos, LIBM=-lm)
;;
esac
AC_SUBST([LIBM])
@@ -3544,7 +3882,7 @@ m4_defun([_LT_COMPILER_NO_RTTI],
_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
case $cc_basename in
nvcc*)
_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;;
@@ -3596,7 +3934,7 @@ cygwin* | mingw* | pw32* | cegcc*)
symcode='[[ABCDGISTW]]'
;;
hpux*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
symcode='[[ABCDEGRST]]'
fi
;;
@@ -3629,14 +3967,44 @@ case `$NM -V 2>&1` in
symcode='[[ABCDGIRSTW]]' ;;
esac
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
# Transform an extracted symbol line into a proper C declaration.
# Some systems (esp. on ia64) link data and code symbols differently,
# so use this general approach.
-lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
# Transform an extracted symbol line into symbol name and symbol address
-lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'"
-lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
# Handle CRLF in mingw tool chain
opt_cr=
@@ -3654,20 +4022,24 @@ for ac_symprfx in "" "_"; do
# Write the raw and C identifiers.
if test "$lt_cv_nm_interface" = "MS dumpbin"; then
- # Fake it for dumpbin and say T for any non-static function
- # and D for any global variable.
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
# Also find C++ and __fastcall symbols from MSVC++,
# which start with @ or ?.
lt_cv_sys_global_symbol_pipe="$AWK ['"\
" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
" \$ 0!~/External *\|/{next};"\
" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
" {if(hide[section]) next};"\
-" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
-" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
-" s[1]~/^[@?]/{print s[1], s[1]; next};"\
-" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
" ' prfx=^$ac_symprfx]"
else
lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
@@ -3707,11 +4079,11 @@ _LT_EOF
if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
cat <<_LT_EOF > conftest.$ac_ext
/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
-/* DATA imports from DLLs on WIN32 con't be const, because runtime
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
relocations are performed -- see ld's documentation on pseudo-relocs. */
# define LT@&t@_DLSYM_CONST
-#elif defined(__osf__)
+#elif defined __osf__
/* This system does not cope well with relocations in const data. */
# define LT@&t@_DLSYM_CONST
#else
@@ -3737,7 +4109,7 @@ lt__PROGRAM__LTX_preloaded_symbols[[]] =
{
{ "@PROGRAM@", (void *) 0 },
_LT_EOF
- $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
cat <<\_LT_EOF >> conftest.$ac_ext
{0, (void *) 0}
};
@@ -3757,9 +4129,9 @@ _LT_EOF
mv conftest.$ac_objext conftstm.$ac_objext
lt_globsym_save_LIBS=$LIBS
lt_globsym_save_CFLAGS=$CFLAGS
- LIBS="conftstm.$ac_objext"
+ LIBS=conftstm.$ac_objext
CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)"
- if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then
+ if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then
pipe_works=yes
fi
LIBS=$lt_globsym_save_LIBS
@@ -3780,7 +4152,7 @@ _LT_EOF
rm -rf conftest* conftst*
# Do not use the global_symbol_pipe unless it works.
- if test "$pipe_works" = yes; then
+ if test yes = "$pipe_works"; then
break
else
lt_cv_sys_global_symbol_pipe=
@@ -3807,12 +4179,16 @@ _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1],
[Take the output of nm and produce a listing of raw symbols and C names])
_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1],
[Transform the output of nm in a proper C declaration])
+_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1],
+ [Transform the output of nm into a list of symbols to manually relocate])
_LT_DECL([global_symbol_to_c_name_address],
[lt_cv_sys_global_symbol_to_c_name_address], [1],
[Transform the output of nm in a C name address pair])
_LT_DECL([global_symbol_to_c_name_address_lib_prefix],
[lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1],
[Transform the output of nm in a C name address pair when lib prefix is needed])
+_LT_DECL([nm_interface], [lt_cv_nm_interface], [1],
+ [The name lister interface])
_LT_DECL([], [nm_file_list_spec], [1],
[Specify filename containing input files for $NM])
]) # _LT_CMD_GLOBAL_SYMBOLS
@@ -3828,17 +4204,18 @@ _LT_TAGVAR(lt_prog_compiler_static, $1)=
m4_if([$1], [CXX], [
# C++ specific cases for pic, static, wl, etc.
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
case $host_os in
aix*)
# All AIX code is PIC.
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
;;
amigaos*)
@@ -3849,8 +4226,8 @@ m4_if([$1], [CXX], [
;;
m68k)
# FIXME: we need at least 68020 code to build shared libraries, but
- # adding the `-m68020' flag to GCC prevents building anything better,
- # like `-m68040'.
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
;;
esac
@@ -3866,6 +4243,11 @@ m4_if([$1], [CXX], [
# (--disable-auto-import) libraries
m4_if([$1], [GCJ], [],
[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
;;
darwin* | rhapsody*)
# PIC is the default on this platform
@@ -3915,7 +4297,7 @@ m4_if([$1], [CXX], [
case $host_os in
aix[[4-9]]*)
# All AIX code is PIC.
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
else
@@ -3956,14 +4338,14 @@ m4_if([$1], [CXX], [
case $cc_basename in
CC*)
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
- _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
- if test "$host_cpu" != ia64; then
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
+ if test ia64 != "$host_cpu"; then
_LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z'
fi
;;
aCC*)
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
- _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
case $host_cpu in
hppa*64*|ia64*)
# +Z the default
@@ -3992,7 +4374,7 @@ m4_if([$1], [CXX], [
;;
esac
;;
- linux* | k*bsd*-gnu | kopensolaris*-gnu)
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
KCC*)
# KAI C++ Compiler
@@ -4000,7 +4382,7 @@ m4_if([$1], [CXX], [
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
;;
ecpc* )
- # old Intel C++ for x86_64 which still supported -KPIC.
+ # old Intel C++ for x86_64, which still supported -KPIC.
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
@@ -4145,17 +4527,18 @@ m4_if([$1], [CXX], [
fi
],
[
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
case $host_os in
aix*)
# All AIX code is PIC.
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
fi
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
;;
amigaos*)
@@ -4166,8 +4549,8 @@ m4_if([$1], [CXX], [
;;
m68k)
# FIXME: we need at least 68020 code to build shared libraries, but
- # adding the `-m68020' flag to GCC prevents building anything better,
- # like `-m68040'.
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4'
;;
esac
@@ -4184,6 +4567,11 @@ m4_if([$1], [CXX], [
# (--disable-auto-import) libraries
m4_if([$1], [GCJ], [],
[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
;;
darwin* | rhapsody*)
@@ -4244,7 +4632,9 @@ m4_if([$1], [CXX], [
case $cc_basename in
nvcc*) # Cuda Compiler Driver 2.2
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker '
- _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Xcompiler -fPIC'
+ if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)"
+ fi
;;
esac
else
@@ -4252,7 +4642,7 @@ m4_if([$1], [CXX], [
case $host_os in
aix*)
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
else
@@ -4260,11 +4650,30 @@ m4_if([$1], [CXX], [
fi
;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
+ esac
+ ;;
+
mingw* | cygwin* | pw32* | os2* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
m4_if([$1], [GCJ], [],
[_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT'])
+ case $host_os in
+ os2*)
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static'
+ ;;
+ esac
;;
hpux9* | hpux10* | hpux11*)
@@ -4280,7 +4689,7 @@ m4_if([$1], [CXX], [
;;
esac
# Is there a better lt_prog_compiler_static that works with the bundled CC?
- _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive'
;;
irix5* | irix6* | nonstopux*)
@@ -4289,9 +4698,9 @@ m4_if([$1], [CXX], [
_LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared'
;;
- linux* | k*bsd*-gnu | kopensolaris*-gnu)
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
- # old Intel for x86_64 which still supported -KPIC.
+ # old Intel for x86_64, which still supported -KPIC.
ecc*)
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
@@ -4316,6 +4725,12 @@ m4_if([$1], [CXX], [
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group compilers (*not* the Pentium gcc compiler,
# which looks to be a dead project)
@@ -4336,18 +4751,33 @@ m4_if([$1], [CXX], [
;;
*)
case `$CC -V 2>&1 | sed 5q` in
- *Sun\ F* | *Sun*Fortran*)
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*)
# Sun Fortran 8.3 passes all unrecognized flags to the linker
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
_LT_TAGVAR(lt_prog_compiler_wl, $1)=''
;;
+ *Sun\ F* | *Sun*Fortran*)
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld '
+ ;;
*Sun\ C*)
# Sun C 5.9
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
_LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
;;
+ *Intel*\ [[CF]]*Compiler*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-static'
+ ;;
+ *Portland\ Group*)
+ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,'
+ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic'
+ _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
+ ;;
esac
;;
esac
@@ -4398,7 +4828,7 @@ m4_if([$1], [CXX], [
;;
sysv4*MP*)
- if test -d /usr/nec ;then
+ if test -d /usr/nec; then
_LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic'
_LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic'
fi
@@ -4427,7 +4857,7 @@ m4_if([$1], [CXX], [
fi
])
case $host_os in
- # For platforms which do not support PIC, -DPIC is meaningless:
+ # For platforms that do not support PIC, -DPIC is meaningless:
*djgpp*)
_LT_TAGVAR(lt_prog_compiler_pic, $1)=
;;
@@ -4493,21 +4923,27 @@ m4_if([$1], [CXX], [
case $host_os in
aix[[4-9]]*)
# If we're using GNU nm, then we don't want the "-C" option.
- # -C means demangle to AIX nm, but means don't demangle with GNU nm
- # Also, AIX nm treats weak defined symbols like other global defined
- # symbols, whereas GNU nm marks them as "W".
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
- _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
else
- _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
fi
;;
pw32*)
- _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds"
+ _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds
;;
cygwin* | mingw* | cegcc*)
case $cc_basename in
- cl*) ;;
+ cl*)
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
+ ;;
*)
_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols'
_LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
@@ -4532,7 +4968,6 @@ m4_if([$1], [CXX], [
_LT_TAGVAR(hardcode_direct, $1)=no
_LT_TAGVAR(hardcode_direct_absolute, $1)=no
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
- _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
_LT_TAGVAR(hardcode_libdir_separator, $1)=
_LT_TAGVAR(hardcode_minus_L, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
@@ -4548,9 +4983,9 @@ m4_if([$1], [CXX], [
# included in the symbol list
_LT_TAGVAR(include_expsyms, $1)=
# exclude_expsyms can be an extended regexp of symbols to exclude
- # it will be wrapped by ` (' and `)$', so one must not match beginning or
- # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
- # as well as any symbol that contains `d'.
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
_LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*']
# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
# platforms (ab)use it in PIC code, but their linkers get confused if
@@ -4566,7 +5001,7 @@ dnl Note also adjust exclude_expsyms for C++ above.
# FIXME: the MSVC++ port hasn't been tested in a loooong time
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
- if test "$GCC" != yes; then
+ if test yes != "$GCC"; then
with_gnu_ld=no
fi
;;
@@ -4574,7 +5009,7 @@ dnl Note also adjust exclude_expsyms for C++ above.
# we just hope/assume this is gcc and not c89 (= MSVC++)
with_gnu_ld=yes
;;
- openbsd*)
+ openbsd* | bitrig*)
with_gnu_ld=no
;;
esac
@@ -4584,7 +5019,7 @@ dnl Note also adjust exclude_expsyms for C++ above.
# On some targets, GNU ld is compatible enough with the native linker
# that we're better off using the native interface for both.
lt_use_gnu_ld_interface=no
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
case $host_os in
aix*)
# The AIX port of GNU ld has always aspired to compatibility
@@ -4606,24 +5041,24 @@ dnl Note also adjust exclude_expsyms for C++ above.
esac
fi
- if test "$lt_use_gnu_ld_interface" = yes; then
+ if test yes = "$lt_use_gnu_ld_interface"; then
# If archive_cmds runs LD, not CC, wlarc should be empty
- wlarc='${wl}'
+ wlarc='$wl'
# Set some defaults for GNU ld with shared library support. These
# are reset later if shared libraries are not supported. Putting them
# here allows them to be overridden if necessary.
runpath_var=LD_RUN_PATH
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
# ancient GNU ld didn't support --whole-archive et. al.
if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
- _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
else
_LT_TAGVAR(whole_archive_flag_spec, $1)=
fi
supports_anon_versioning=no
- case `$LD -v 2>&1` in
+ case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in
*GNU\ gold*) supports_anon_versioning=yes ;;
*\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11
*\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
@@ -4636,7 +5071,7 @@ dnl Note also adjust exclude_expsyms for C++ above.
case $host_os in
aix[[3-9]]*)
# On AIX/PPC, the GNU linker is very broken
- if test "$host_cpu" != ia64; then
+ if test ia64 != "$host_cpu"; then
_LT_TAGVAR(ld_shlibs, $1)=no
cat <<_LT_EOF 1>&2
@@ -4655,7 +5090,7 @@ _LT_EOF
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
_LT_TAGVAR(archive_expsym_cmds, $1)=''
;;
m68k)
@@ -4671,7 +5106,7 @@ _LT_EOF
_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
# support --undefined. This deserves some investigation. FIXME
- _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
else
_LT_TAGVAR(ld_shlibs, $1)=no
fi
@@ -4681,7 +5116,7 @@ _LT_EOF
# _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
# as there is no search path for DLLs.
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
_LT_TAGVAR(always_export_symbols, $1)=no
_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
@@ -4689,61 +5124,89 @@ _LT_EOF
_LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname']
if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
- # If the export-symbols file already is a .def file (1st line
- # is EXPORTS), use it as is; otherwise, prepend...
- _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- cp $export_symbols $output_objdir/$soname.def;
- else
- echo EXPORTS > $output_objdir/$soname.def;
- cat $export_symbols >> $output_objdir/$soname.def;
- fi~
- $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
else
_LT_TAGVAR(ld_shlibs, $1)=no
fi
;;
haiku*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
_LT_TAGVAR(link_all_deplibs, $1)=yes
;;
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
interix[[3-9]]*)
_LT_TAGVAR(hardcode_direct, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
# Instead, shared libraries are loaded at an image base (0x10000000 by
# default) and relocated if they conflict, which is a slow very memory
# consuming and fragmenting process. To avoid this, we pick a random,
# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
# time. Moving up from 0x10000000 also allows more sbrk(2) space.
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
tmp_diet=no
- if test "$host_os" = linux-dietlibc; then
+ if test linux-dietlibc = "$host_os"; then
case $cc_basename in
diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
esac
fi
if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
- && test "$tmp_diet" = no
+ && test no = "$tmp_diet"
then
tmp_addflag=' $pic_flag'
tmp_sharedflag='-shared'
case $cc_basename,$host_cpu in
pgcc*) # Portland Group C compiler
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag'
;;
pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group f77 and f90 compilers
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag -Mnomain' ;;
ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
tmp_addflag=' -i_dynamic' ;;
@@ -4754,43 +5217,47 @@ _LT_EOF
lf95*) # Lahey Fortran 8.1
_LT_TAGVAR(whole_archive_flag_spec, $1)=
tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below)
tmp_sharedflag='-qmkshrobj'
tmp_addflag= ;;
nvcc*) # Cuda Compiler Driver 2.2
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
_LT_TAGVAR(compiler_needs_object, $1)=yes
;;
esac
case `$CC -V 2>&1 | sed 5q` in
*Sun\ C*) # Sun C 5.9
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
_LT_TAGVAR(compiler_needs_object, $1)=yes
tmp_sharedflag='-G' ;;
*Sun\ F*) # Sun Fortran 8.3
tmp_sharedflag='-G' ;;
esac
- _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ if test yes = "$supports_anon_versioning"; then
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
fi
case $cc_basename in
+ tcc*)
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic'
+ ;;
xlf* | bgf* | bgxlf* | mpixlf*)
# IBM XL Fortran 10.1 on PPC cannot create shared libs itself
_LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
- _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
_LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ if test yes = "$supports_anon_versioning"; then
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
fi
;;
esac
@@ -4804,8 +5271,8 @@ _LT_EOF
_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
wlarc=
else
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
fi
;;
@@ -4823,8 +5290,8 @@ _LT_EOF
_LT_EOF
elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
_LT_TAGVAR(ld_shlibs, $1)=no
fi
@@ -4836,7 +5303,7 @@ _LT_EOF
_LT_TAGVAR(ld_shlibs, $1)=no
cat <<_LT_EOF 1>&2
-*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
*** reliably create shared libraries on SCO systems. Therefore, libtool
*** is disabling shared libraries support. We urge you to upgrade GNU
*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
@@ -4851,9 +5318,9 @@ _LT_EOF
# DT_RUNPATH tag from executables and libraries. But doing so
# requires that you compile everything twice, which is a pain.
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
_LT_TAGVAR(ld_shlibs, $1)=no
fi
@@ -4870,15 +5337,15 @@ _LT_EOF
*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
_LT_TAGVAR(ld_shlibs, $1)=no
fi
;;
esac
- if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then
+ if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then
runpath_var=
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
_LT_TAGVAR(export_dynamic_flag_spec, $1)=
@@ -4894,7 +5361,7 @@ _LT_EOF
# Note: this linker hardcodes the directories in LIBPATH if there
# are no directories specified by -L.
_LT_TAGVAR(hardcode_minus_L, $1)=yes
- if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
# Neither direct hardcoding nor static linking is supported with a
# broken collect2.
_LT_TAGVAR(hardcode_direct, $1)=unsupported
@@ -4902,34 +5369,57 @@ _LT_EOF
;;
aix[[4-9]]*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# On IA64, the linker does run time linking by default, so we don't
# have to do anything special.
aix_use_runtimelinking=no
exp_sym_flag='-Bexport'
- no_entry_flag=""
+ no_entry_flag=
else
# If we're using GNU nm, then we don't want the "-C" option.
- # -C means demangle to AIX nm, but means don't demangle with GNU nm
- # Also, AIX nm treats weak defined symbols like other global
- # defined symbols, whereas GNU nm marks them as "W".
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
- _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
else
- _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
fi
aix_use_runtimelinking=no
# Test if we are trying to use run time linking or normal
# AIX style linking. If -brtl is somewhere in LDFLAGS, we
- # need to do runtime linking.
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
for ld_flag in $LDFLAGS; do
- if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
aix_use_runtimelinking=yes
break
fi
done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
;;
esac
@@ -4948,13 +5438,21 @@ _LT_EOF
_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
_LT_TAGVAR(hardcode_libdir_separator, $1)=':'
_LT_TAGVAR(link_all_deplibs, $1)=yes
- _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
case $host_os in aix4.[[012]]|aix4.[[012]].*)
# We only want to do this on AIX 4.2 and lower, the check
# below for broken collect2 doesn't work under 4.3+
- collect2name=`${CC} -print-prog-name=collect2`
+ collect2name=`$CC -print-prog-name=collect2`
if test -f "$collect2name" &&
strings "$collect2name" | $GREP resolve_lib_name >/dev/null
then
@@ -4973,61 +5471,80 @@ _LT_EOF
;;
esac
shared_flag='-shared'
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag="$shared_flag "'${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
else
# not using gcc
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
# chokes on -Wl,-G. The following line is correct:
shared_flag='-G'
else
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag='${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
else
- shared_flag='${wl}-bM:SRE'
+ shared_flag='$wl-bM:SRE'
fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
fi
fi
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
# It seems that -bexpall does not export symbols beginning with
# underscore (_), so it is better to generate a list of symbols to export.
_LT_TAGVAR(always_export_symbols, $1)=yes
- if test "$aix_use_runtimelinking" = yes; then
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
# Warning - without using the other runtime loading flags (-brtl),
# -berok will link without error, but may produce a broken library.
_LT_TAGVAR(allow_undefined_flag, $1)='-berok'
# Determine the default libpath from the value encoded in an
# empty executable.
_LT_SYS_MODULE_PATH_AIX([$1])
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
else
- if test "$host_cpu" = ia64; then
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
_LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
- _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
else
# Determine the default libpath from the value encoded in an
# empty executable.
_LT_SYS_MODULE_PATH_AIX([$1])
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
# Warning - without using the other run time loading flags,
# -berok will link without error, but may produce a broken library.
- _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
- _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
- if test "$with_gnu_ld" = yes; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
# We only use this code for GNU lds that support --whole-archive.
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
else
# Exported symbols can be pulled into shared objects from archives
_LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
fi
_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
- # This is similar to how AIX traditionally builds its shared libraries.
- _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
fi
fi
;;
@@ -5036,7 +5553,7 @@ _LT_EOF
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
_LT_TAGVAR(archive_expsym_cmds, $1)=''
;;
m68k)
@@ -5066,35 +5583,37 @@ _LT_EOF
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
- _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
- _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
- else
- sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
- fi~
- $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
- linknames='
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
# The linker will not automatically build a static lib if we build a DLL.
# _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
_LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols'
# Don't use ranlib
_LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
_LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
- lt_tool_outputfile="@TOOL_OUTPUT@"~
- case $lt_outputfile in
- *.exe|*.EXE) ;;
- *)
- lt_outputfile="$lt_outputfile.exe"
- lt_tool_outputfile="$lt_tool_outputfile.exe"
- ;;
- esac~
- if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
- $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
- $RM "$lt_outputfile.manifest";
- fi'
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
;;
*)
# Assume MSVC wrapper
@@ -5103,7 +5622,7 @@ _LT_EOF
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
_LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
# The linker will automatically build a .lib file if we build a DLL.
@@ -5125,10 +5644,6 @@ _LT_EOF
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
;;
- freebsd1*)
- _LT_TAGVAR(ld_shlibs, $1)=no
- ;;
-
# FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
# support. Future versions do this automatically, but an explicit c++rt0.o
# does not break anything, and helps significantly (at the cost of a little
@@ -5141,7 +5656,7 @@ _LT_EOF
;;
# Unfortunately, older versions of FreeBSD 2 do not have this feature.
- freebsd2*)
+ freebsd2.*)
_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
_LT_TAGVAR(hardcode_direct, $1)=yes
_LT_TAGVAR(hardcode_minus_L, $1)=yes
@@ -5157,34 +5672,33 @@ _LT_EOF
;;
hpux9*)
- if test "$GCC" = yes; then
- _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
else
- _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
fi
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
_LT_TAGVAR(hardcode_direct, $1)=yes
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
_LT_TAGVAR(hardcode_minus_L, $1)=yes
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
;;
hpux10*)
- if test "$GCC" = yes && test "$with_gnu_ld" = no; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
else
_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
fi
- if test "$with_gnu_ld" = no; then
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
- _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir'
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
_LT_TAGVAR(hardcode_direct, $1)=yes
_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
_LT_TAGVAR(hardcode_minus_L, $1)=yes
@@ -5192,25 +5706,25 @@ _LT_EOF
;;
hpux11*)
- if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+ if test yes,no = "$GCC,$with_gnu_ld"; then
case $host_cpu in
hppa*64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
else
case $host_cpu in
hppa*64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
m4_if($1, [], [
@@ -5218,14 +5732,14 @@ _LT_EOF
# (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
_LT_LINKER_OPTION([if $CC understands -b],
_LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b],
- [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'],
[_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])],
- [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
+ [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'])
;;
esac
fi
- if test "$with_gnu_ld" = no; then
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
case $host_cpu in
@@ -5236,7 +5750,7 @@ _LT_EOF
*)
_LT_TAGVAR(hardcode_direct, $1)=yes
_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
@@ -5247,16 +5761,16 @@ _LT_EOF
;;
irix5* | irix6* | nonstopux*)
- if test "$GCC" = yes; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
# Try to use the -exported_symbol ld option, if it does not
# work, assume that -exports_file does not work either and
# implicitly export all symbols.
# This should be the same for all languages, so no per-tag cache variable.
AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol],
[lt_cv_irix_exported_symbol],
- [save_LDFLAGS="$LDFLAGS"
- LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ [save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
AC_LINK_IFELSE(
[AC_LANG_SOURCE(
[AC_LANG_CASE([C], [[int foo (void) { return 0; }]],
@@ -5269,21 +5783,31 @@ _LT_EOF
end]])])],
[lt_cv_irix_exported_symbol=yes],
[lt_cv_irix_exported_symbol=no])
- LDFLAGS="$save_LDFLAGS"])
- if test "$lt_cv_irix_exported_symbol" = yes; then
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+ LDFLAGS=$save_LDFLAGS])
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
fi
else
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
fi
_LT_TAGVAR(archive_cmds_need_lc, $1)='no'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
_LT_TAGVAR(inherit_rpath, $1)=yes
_LT_TAGVAR(link_all_deplibs, $1)=yes
;;
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ _LT_TAGVAR(ld_shlibs, $1)=yes
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
_LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
@@ -5298,7 +5822,7 @@ _LT_EOF
newsos6)
_LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
_LT_TAGVAR(hardcode_direct, $1)=yes
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
;;
@@ -5306,27 +5830,19 @@ _LT_EOF
*nto* | *qnx*)
;;
- openbsd*)
+ openbsd* | bitrig*)
if test -f /usr/libexec/ld.so; then
_LT_TAGVAR(hardcode_direct, $1)=yes
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
else
- case $host_os in
- openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*)
- _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
- ;;
- *)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
- ;;
- esac
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
fi
else
_LT_TAGVAR(ld_shlibs, $1)=no
@@ -5337,33 +5853,53 @@ _LT_EOF
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
_LT_TAGVAR(hardcode_minus_L, $1)=yes
_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
- _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
- _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
;;
osf3*)
- if test "$GCC" = yes; then
- _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
else
_LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
fi
_LT_TAGVAR(archive_cmds_need_lc, $1)='no'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
;;
osf4* | osf5*) # as osf3* with the addition of -msym flag
- if test "$GCC" = yes; then
- _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
else
_LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
_LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
- $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
# Both c and cxx compiler support -rpath directly
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
@@ -5374,24 +5910,24 @@ _LT_EOF
solaris*)
_LT_TAGVAR(no_undefined_flag, $1)=' -z defs'
- if test "$GCC" = yes; then
- wlarc='${wl}'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
else
case `$CC -V 2>&1` in
*"Compilers 5.0"*)
wlarc=''
- _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
;;
*)
- wlarc='${wl}'
- _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ wlarc='$wl'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
;;
esac
fi
@@ -5401,11 +5937,11 @@ _LT_EOF
solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
*)
# The compiler driver will combine and reorder linker options,
- # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
# but is careful enough not to reorder.
# Supported since Solaris 2.6 (maybe 2.5.1?)
- if test "$GCC" = yes; then
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
else
_LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
fi
@@ -5415,10 +5951,10 @@ _LT_EOF
;;
sunos4*)
- if test "x$host_vendor" = xsequent; then
+ if test sequent = "$host_vendor"; then
# Use $CC to link under sequent, because it throws in some extra .o
# files that make .init and .fini sections work.
- _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
else
_LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
fi
@@ -5467,43 +6003,43 @@ _LT_EOF
;;
sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
- _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
_LT_TAGVAR(archive_cmds_need_lc, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
runpath_var='LD_RUN_PATH'
- if test "$GCC" = yes; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
- _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
sysv5* | sco3.2v5* | sco5v6*)
- # Note: We can NOT use -z defs as we might desire, because we do not
+ # Note: We CANNOT use -z defs as we might desire, because we do not
# link with -lc, and that would cause any symbols used from libc to
# always be unresolved, which means just about no library would
# ever link correctly. If we're not using GNU ld we use -z text
# though, which does catch some bad symbols but isn't as heavy-handed
# as -z defs.
- _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
- _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
_LT_TAGVAR(archive_cmds_need_lc, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=':'
_LT_TAGVAR(link_all_deplibs, $1)=yes
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
runpath_var='LD_RUN_PATH'
- if test "$GCC" = yes; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
- _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
@@ -5518,17 +6054,17 @@ _LT_EOF
;;
esac
- if test x$host_vendor = xsni; then
+ if test sni = "$host_vendor"; then
case $host in
sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym'
;;
esac
fi
fi
])
AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
-test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld
@@ -5545,7 +6081,7 @@ x|xyes)
# Assume -lc should be added
_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
- if test "$enable_shared" = yes && test "$GCC" = yes; then
+ if test yes,yes = "$GCC,$enable_shared"; then
case $_LT_TAGVAR(archive_cmds, $1) in
*'~'*)
# FIXME: we may have to deal with multi-command sequences.
@@ -5622,18 +6158,15 @@ _LT_TAGDECL([], [no_undefined_flag], [1],
_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1],
[Flag to hardcode $libdir into a binary during linking.
This must work even if $libdir does not exist])
-_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1],
- [[If ld is used when linking, flag to hardcode $libdir into a binary
- during linking. This must work even if $libdir does not exist]])
_LT_TAGDECL([], [hardcode_libdir_separator], [1],
[Whether we need a single "-rpath" flag with a separated argument])
_LT_TAGDECL([], [hardcode_direct], [0],
- [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
DIR into the resulting binary])
_LT_TAGDECL([], [hardcode_direct_absolute], [0],
- [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes
+ [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes
DIR into the resulting binary and the resulting library dependency is
- "absolute", i.e impossible to change by setting ${shlibpath_var} if the
+ "absolute", i.e impossible to change by setting $shlibpath_var if the
library is relocated])
_LT_TAGDECL([], [hardcode_minus_L], [0],
[Set to "yes" if using the -LDIR flag during linking hardcodes DIR
@@ -5674,10 +6207,10 @@ dnl [Compiler flag to generate thread safe objects])
# ------------------------
# Ensure that the configuration variables for a C compiler are suitably
# defined. These variables are subsequently used by _LT_CONFIG to write
-# the compiler configuration to `libtool'.
+# the compiler configuration to 'libtool'.
m4_defun([_LT_LANG_C_CONFIG],
[m4_require([_LT_DECL_EGREP])dnl
-lt_save_CC="$CC"
+lt_save_CC=$CC
AC_LANG_PUSH(C)
# Source file extension for C test sources.
@@ -5713,18 +6246,18 @@ if test -n "$compiler"; then
LT_SYS_DLOPEN_SELF
_LT_CMD_STRIPLIB
- # Report which library types will actually be built
+ # Report what library types will actually be built
AC_MSG_CHECKING([if libtool supports shared libraries])
AC_MSG_RESULT([$can_build_shared])
AC_MSG_CHECKING([whether to build shared libraries])
- test "$can_build_shared" = "no" && enable_shared=no
+ test no = "$can_build_shared" && enable_shared=no
# On AIX, shared libraries and static libraries use the same namespace, and
# are all built from PIC.
case $host_os in
aix3*)
- test "$enable_shared" = yes && enable_static=no
+ test yes = "$enable_shared" && enable_static=no
if test -n "$RANLIB"; then
archive_cmds="$archive_cmds~\$RANLIB \$lib"
postinstall_cmds='$RANLIB $lib'
@@ -5732,8 +6265,12 @@ if test -n "$compiler"; then
;;
aix[[4-9]]*)
- if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
- test "$enable_shared" = yes && enable_static=no
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
fi
;;
esac
@@ -5741,13 +6278,13 @@ if test -n "$compiler"; then
AC_MSG_CHECKING([whether to build static libraries])
# Make sure either enable_shared or enable_static is yes.
- test "$enable_shared" = yes || enable_static=yes
+ test yes = "$enable_shared" || enable_static=yes
AC_MSG_RESULT([$enable_static])
_LT_CONFIG($1)
fi
AC_LANG_POP
-CC="$lt_save_CC"
+CC=$lt_save_CC
])# _LT_LANG_C_CONFIG
@@ -5755,14 +6292,14 @@ CC="$lt_save_CC"
# --------------------------
# Ensure that the configuration variables for a C++ compiler are suitably
# defined. These variables are subsequently used by _LT_CONFIG to write
-# the compiler configuration to `libtool'.
+# the compiler configuration to 'libtool'.
m4_defun([_LT_LANG_CXX_CONFIG],
[m4_require([_LT_FILEUTILS_DEFAULTS])dnl
m4_require([_LT_DECL_EGREP])dnl
m4_require([_LT_PATH_MANIFEST_TOOL])dnl
-if test -n "$CXX" && ( test "X$CXX" != "Xno" &&
- ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) ||
- (test "X$CXX" != "Xg++"))) ; then
+if test -n "$CXX" && ( test no != "$CXX" &&
+ ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) ||
+ (test g++ != "$CXX"))); then
AC_PROG_CXXCPP
else
_lt_caught_CXX_error=yes
@@ -5778,7 +6315,6 @@ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
_LT_TAGVAR(hardcode_direct, $1)=no
_LT_TAGVAR(hardcode_direct_absolute, $1)=no
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
_LT_TAGVAR(hardcode_libdir_separator, $1)=
_LT_TAGVAR(hardcode_minus_L, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported
@@ -5805,7 +6341,7 @@ _LT_TAGVAR(objext, $1)=$objext
# the CXX compiler isn't working. Some variables (like enable_shared)
# are currently assumed to apply to all compilers on this platform,
# and will be corrupted by setting them based on a non-working compiler.
-if test "$_lt_caught_CXX_error" != yes; then
+if test yes != "$_lt_caught_CXX_error"; then
# Code to be used in simple compile tests
lt_simple_compile_test_code="int some_variable = 0;"
@@ -5847,35 +6383,35 @@ if test "$_lt_caught_CXX_error" != yes; then
if test -n "$compiler"; then
# We don't want -fno-exception when compiling C++ code, so set the
# no_builtin_flag separately
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin'
else
_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=
fi
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
# Set up default GNU C++ configuration
LT_PATH_LD
# Check if GNU C++ uses GNU ld as the underlying linker, since the
# archiving commands below assume that GNU ld is being used.
- if test "$with_gnu_ld" = yes; then
- _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ if test yes = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
# If archive_cmds runs LD, not CC, wlarc should be empty
# XXX I think wlarc can be eliminated in ltcf-cxx, but I need to
# investigate it a little bit more. (MM)
- wlarc='${wl}'
+ wlarc='$wl'
# ancient GNU ld didn't support --whole-archive et. al.
if eval "`$CC -print-prog-name=ld` --help 2>&1" |
$GREP 'no-whole-archive' > /dev/null; then
- _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
else
_LT_TAGVAR(whole_archive_flag_spec, $1)=
fi
@@ -5911,18 +6447,30 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(ld_shlibs, $1)=no
;;
aix[[4-9]]*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# On IA64, the linker does run time linking by default, so we don't
# have to do anything special.
aix_use_runtimelinking=no
exp_sym_flag='-Bexport'
- no_entry_flag=""
+ no_entry_flag=
else
aix_use_runtimelinking=no
# Test if we are trying to use run time linking or normal
# AIX style linking. If -brtl is somewhere in LDFLAGS, we
- # need to do runtime linking.
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*)
for ld_flag in $LDFLAGS; do
case $ld_flag in
@@ -5932,6 +6480,13 @@ if test "$_lt_caught_CXX_error" != yes; then
;;
esac
done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
;;
esac
@@ -5950,13 +6505,21 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
_LT_TAGVAR(hardcode_libdir_separator, $1)=':'
_LT_TAGVAR(link_all_deplibs, $1)=yes
- _LT_TAGVAR(file_list_spec, $1)='${wl}-f,'
+ _LT_TAGVAR(file_list_spec, $1)='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ _LT_TAGVAR(hardcode_direct, $1)=no
+ _LT_TAGVAR(hardcode_direct_absolute, $1)=no
+ ;;
+ esac
- if test "$GXX" = yes; then
+ if test yes = "$GXX"; then
case $host_os in aix4.[[012]]|aix4.[[012]].*)
# We only want to do this on AIX 4.2 and lower, the check
# below for broken collect2 doesn't work under 4.3+
- collect2name=`${CC} -print-prog-name=collect2`
+ collect2name=`$CC -print-prog-name=collect2`
if test -f "$collect2name" &&
strings "$collect2name" | $GREP resolve_lib_name >/dev/null
then
@@ -5974,64 +6537,84 @@ if test "$_lt_caught_CXX_error" != yes; then
fi
esac
shared_flag='-shared'
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag="$shared_flag "'${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag=$shared_flag' $wl-G'
fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
else
# not using gcc
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
# chokes on -Wl,-G. The following line is correct:
shared_flag='-G'
else
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag='${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
else
- shared_flag='${wl}-bM:SRE'
+ shared_flag='$wl-bM:SRE'
fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
fi
fi
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall'
# It seems that -bexpall does not export symbols beginning with
# underscore (_), so it is better to generate a list of symbols to
# export.
_LT_TAGVAR(always_export_symbols, $1)=yes
- if test "$aix_use_runtimelinking" = yes; then
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
# Warning - without using the other runtime loading flags (-brtl),
# -berok will link without error, but may produce a broken library.
- _LT_TAGVAR(allow_undefined_flag, $1)='-berok'
+ # The "-G" linker flag allows undefined symbols.
+ _LT_TAGVAR(no_undefined_flag, $1)='-bernotok'
# Determine the default libpath from the value encoded in an empty
# executable.
_LT_SYS_MODULE_PATH_AIX([$1])
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
else
- if test "$host_cpu" = ia64; then
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib'
+ if test ia64 = "$host_cpu"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib'
_LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs"
- _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
else
# Determine the default libpath from the value encoded in an
# empty executable.
_LT_SYS_MODULE_PATH_AIX([$1])
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath"
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath"
# Warning - without using the other run time loading flags,
# -berok will link without error, but may produce a broken library.
- _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok'
- _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok'
- if test "$with_gnu_ld" = yes; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
# We only use this code for GNU lds that support --whole-archive.
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
else
# Exported symbols can be pulled into shared objects from archives
_LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience'
fi
_LT_TAGVAR(archive_cmds_need_lc, $1)=yes
- # This is similar to how AIX traditionally builds its shared
- # libraries.
- _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared
+ # libraries. Need -bnortl late, we may have -brtl in LDFLAGS.
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d'
fi
fi
;;
@@ -6041,7 +6624,7 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
# support --undefined. This deserves some investigation. FIXME
- _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
else
_LT_TAGVAR(ld_shlibs, $1)=no
fi
@@ -6069,57 +6652,58 @@ if test "$_lt_caught_CXX_error" != yes; then
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
- _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
- _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
- else
- $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
- fi~
- $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
- linknames='
+ _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
# The linker will not automatically build a static lib if we build a DLL.
# _LT_TAGVAR(old_archive_from_new_cmds, $1)='true'
_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
# Don't use ranlib
_LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib'
_LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~
- lt_tool_outputfile="@TOOL_OUTPUT@"~
- case $lt_outputfile in
- *.exe|*.EXE) ;;
- *)
- lt_outputfile="$lt_outputfile.exe"
- lt_tool_outputfile="$lt_tool_outputfile.exe"
- ;;
- esac~
- func_to_tool_file "$lt_outputfile"~
- if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
- $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
- $RM "$lt_outputfile.manifest";
- fi'
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ func_to_tool_file "$lt_outputfile"~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
;;
*)
# g++
# _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless,
# as there is no search path for DLLs.
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols'
_LT_TAGVAR(allow_undefined_flag, $1)=unsupported
_LT_TAGVAR(always_export_symbols, $1)=no
_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
- # If the export-symbols file already is a .def file (1st line
- # is EXPORTS), use it as is; otherwise, prepend...
- _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- cp $export_symbols $output_objdir/$soname.def;
- else
- echo EXPORTS > $output_objdir/$soname.def;
- cat $export_symbols >> $output_objdir/$soname.def;
- fi~
- $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
else
_LT_TAGVAR(ld_shlibs, $1)=no
fi
@@ -6130,6 +6714,34 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_DARWIN_LINKER_FEATURES($1)
;;
+ os2*)
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir'
+ _LT_TAGVAR(hardcode_minus_L, $1)=yes
+ _LT_TAGVAR(allow_undefined_flag, $1)=unsupported
+ shrext_cmds=.dll
+ _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes
+ ;;
+
dgux*)
case $cc_basename in
ec++*)
@@ -6148,7 +6760,7 @@ if test "$_lt_caught_CXX_error" != yes; then
esac
;;
- freebsd[[12]]*)
+ freebsd2.*)
# C++ shared libraries reported to be fairly broken before
# switch to ELF
_LT_TAGVAR(ld_shlibs, $1)=no
@@ -6164,18 +6776,15 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(ld_shlibs, $1)=yes
;;
- gnu*)
- ;;
-
haiku*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
_LT_TAGVAR(link_all_deplibs, $1)=yes
;;
hpux9*)
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
_LT_TAGVAR(hardcode_direct, $1)=yes
_LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH,
# but as the default
@@ -6187,7 +6796,7 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(ld_shlibs, $1)=no
;;
aCC*)
- _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
@@ -6196,11 +6805,11 @@ if test "$_lt_caught_CXX_error" != yes; then
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
;;
*)
- if test "$GXX" = yes; then
- _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ if test yes = "$GXX"; then
+ _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
else
# FIXME: insert proper C++ library support
_LT_TAGVAR(ld_shlibs, $1)=no
@@ -6210,15 +6819,15 @@ if test "$_lt_caught_CXX_error" != yes; then
;;
hpux10*|hpux11*)
- if test $with_gnu_ld = no; then
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir'
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
case $host_cpu in
hppa*64*|ia64*)
;;
*)
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
;;
esac
fi
@@ -6244,13 +6853,13 @@ if test "$_lt_caught_CXX_error" != yes; then
aCC*)
case $host_cpu in
hppa*64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
ia64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
esac
# Commands to make compiler produce verbose output that lists
@@ -6261,20 +6870,20 @@ if test "$_lt_caught_CXX_error" != yes; then
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
;;
*)
- if test "$GXX" = yes; then
- if test $with_gnu_ld = no; then
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
case $host_cpu in
hppa*64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
ia64*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
;;
esac
fi
@@ -6289,22 +6898,22 @@ if test "$_lt_caught_CXX_error" != yes; then
interix[[3-9]]*)
_LT_TAGVAR(hardcode_direct, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
# Instead, shared libraries are loaded at an image base (0x10000000 by
# default) and relocated if they conflict, which is a slow very memory
# consuming and fragmenting process. To avoid this, we pick a random,
# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
# time. Moving up from 0x10000000 also allows more sbrk(2) space.
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
irix5* | irix6*)
case $cc_basename in
CC*)
# SGI C++
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
# Archives containing C++ object files must be created using
# "CC -ar", where "CC" is the IRIX C++ compiler. This is
@@ -6313,22 +6922,22 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs'
;;
*)
- if test "$GXX" = yes; then
- if test "$with_gnu_ld" = no; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GXX"; then
+ if test no = "$with_gnu_ld"; then
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
else
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib'
fi
fi
_LT_TAGVAR(link_all_deplibs, $1)=yes
;;
esac
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
_LT_TAGVAR(inherit_rpath, $1)=yes
;;
- linux* | k*bsd*-gnu | kopensolaris*-gnu)
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
KCC*)
# Kuck and Associates, Inc. (KAI) C++ Compiler
@@ -6336,8 +6945,8 @@ if test "$_lt_caught_CXX_error" != yes; then
# KCC will only create a shared library if the output file
# ends with ".so" (or ".sl" for HP-UX), so rename the library
# to its proper name (with version) after linking.
- _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib'
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
@@ -6346,10 +6955,10 @@ if test "$_lt_caught_CXX_error" != yes; then
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
# Archives containing C++ object files must be created using
# "CC -Bstatic", where "CC" is the KAI C++ compiler.
@@ -6363,59 +6972,59 @@ if test "$_lt_caught_CXX_error" != yes; then
# earlier do not add the objects themselves.
case `$CC -V 2>&1` in
*"Version 7."*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
*) # Version 8.0 or newer
tmp_idyn=
case $host_cpu in
ia64*) tmp_idyn=' -i_dynamic';;
esac
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
esac
_LT_TAGVAR(archive_cmds_need_lc, $1)=no
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive'
;;
pgCC* | pgcpp*)
# Portland Group C++ compiler
case `$CC -V` in
*pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*)
_LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
- compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~
+ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"'
_LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
- $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
- $RANLIB $oldlib'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~
+ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~
+ $RANLIB $oldlib'
_LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
_LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~
- rm -rf $tpldir~
- $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
- $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ rm -rf $tpldir~
+ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~
+ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
*) # Version 6 and above use weak symbols
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
;;
esac
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
;;
cxx*)
# Compaq C++
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols'
runpath_var=LD_RUN_PATH
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
@@ -6429,18 +7038,18 @@ if test "$_lt_caught_CXX_error" != yes; then
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed'
;;
xl* | mpixl* | bgxl*)
# IBM XL 8.0 on PPC, with GNU ld
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic'
- _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ if test yes = "$supports_anon_versioning"; then
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
fi
;;
*)
@@ -6448,10 +7057,10 @@ if test "$_lt_caught_CXX_error" != yes; then
*Sun\ C*)
# Sun C++ 5.9
_LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
- _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols'
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
_LT_TAGVAR(compiler_needs_object, $1)=yes
# Not sure whether something based on
@@ -6509,22 +7118,17 @@ if test "$_lt_caught_CXX_error" != yes; then
_LT_TAGVAR(ld_shlibs, $1)=yes
;;
- openbsd2*)
- # C++ shared libraries are fairly broken
- _LT_TAGVAR(ld_shlibs, $1)=no
- ;;
-
- openbsd*)
+ openbsd* | bitrig*)
if test -f /usr/libexec/ld.so; then
_LT_TAGVAR(hardcode_direct, $1)=yes
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
_LT_TAGVAR(hardcode_direct_absolute, $1)=yes
_LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
- if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib'
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E'
- _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
+ if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
fi
output_verbose_link_cmd=func_echo_all
else
@@ -6540,9 +7144,9 @@ if test "$_lt_caught_CXX_error" != yes; then
# KCC will only create a shared library if the output file
# ends with ".so" (or ".sl" for HP-UX), so rename the library
# to its proper name (with version) after linking.
- _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
+ _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
# Archives containing C++ object files must be created using
@@ -6560,17 +7164,17 @@ if test "$_lt_caught_CXX_error" != yes; then
cxx*)
case $host in
osf3*)
- _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
;;
*)
_LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*'
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
_LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~
- echo "-hidden">> $lib.exp~
- $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~
- $RM $lib.exp'
+ echo "-hidden">> $lib.exp~
+ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~
+ $RM $lib.exp'
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir'
;;
esac
@@ -6585,21 +7189,21 @@ if test "$_lt_caught_CXX_error" != yes; then
# explicitly linking system object files so we need to strip them
# from the output so that they don't get included in the library
# dependencies.
- output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
+ output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"'
;;
*)
- if test "$GXX" = yes && test "$with_gnu_ld" = no; then
- _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*'
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*'
case $host in
osf3*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
;;
*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
;;
esac
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=:
# Commands to make compiler produce verbose output that lists
@@ -6645,9 +7249,9 @@ if test "$_lt_caught_CXX_error" != yes; then
# Sun C++ 4.2, 5.x and Centerline C++
_LT_TAGVAR(archive_cmds_need_lc,$1)=yes
_LT_TAGVAR(no_undefined_flag, $1)=' -zdefs'
- _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags'
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir'
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
@@ -6655,7 +7259,7 @@ if test "$_lt_caught_CXX_error" != yes; then
solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
*)
# The compiler driver will combine and reorder linker options,
- # but understands `-z linker_flag'.
+ # but understands '-z linker_flag'.
# Supported since Solaris 2.6 (maybe 2.5.1?)
_LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract'
;;
@@ -6672,30 +7276,30 @@ if test "$_lt_caught_CXX_error" != yes; then
;;
gcx*)
# Green Hills C++ Compiler
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
# The C++ compiler must be used to create the archive.
_LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs'
;;
*)
# GNU C++ compiler with Solaris linker
- if test "$GXX" = yes && test "$with_gnu_ld" = no; then
- _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs'
+ if test yes,no = "$GXX,$with_gnu_ld"; then
+ _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs'
if $CC --version | $GREP -v '^2\.7' > /dev/null; then
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
# linking a shared library.
output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
else
- # g++ 2.7 appears to require `-G' NOT `-shared' on this
+ # g++ 2.7 appears to require '-G' NOT '-shared' on this
# platform.
- _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib'
_LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
+ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp'
# Commands to make compiler produce verbose output that lists
# what "hidden" libraries, object files and flags are used when
@@ -6703,11 +7307,11 @@ if test "$_lt_caught_CXX_error" != yes; then
output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"'
fi
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir'
case $host_os in
solaris2.[[0-5]] | solaris2.[[0-5]].*) ;;
*)
- _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
;;
esac
fi
@@ -6716,52 +7320,52 @@ if test "$_lt_caught_CXX_error" != yes; then
;;
sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*)
- _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
_LT_TAGVAR(archive_cmds_need_lc, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
runpath_var='LD_RUN_PATH'
case $cc_basename in
CC*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
;;
sysv5* | sco3.2v5* | sco5v6*)
- # Note: We can NOT use -z defs as we might desire, because we do not
+ # Note: We CANNOT use -z defs as we might desire, because we do not
# link with -lc, and that would cause any symbols used from libc to
# always be unresolved, which means just about no library would
# ever link correctly. If we're not using GNU ld we use -z text
# though, which does catch some bad symbols but isn't as heavy-handed
# as -z defs.
- _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text'
- _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs'
+ _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text'
+ _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs'
_LT_TAGVAR(archive_cmds_need_lc, $1)=no
_LT_TAGVAR(hardcode_shlibpath_var, $1)=no
- _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir'
+ _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir'
_LT_TAGVAR(hardcode_libdir_separator, $1)=':'
_LT_TAGVAR(link_all_deplibs, $1)=yes
- _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport'
+ _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport'
runpath_var='LD_RUN_PATH'
case $cc_basename in
CC*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
_LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~
- '"$_LT_TAGVAR(old_archive_cmds, $1)"
+ '"$_LT_TAGVAR(old_archive_cmds, $1)"
_LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~
- '"$_LT_TAGVAR(reload_cmds, $1)"
+ '"$_LT_TAGVAR(reload_cmds, $1)"
;;
*)
- _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
;;
@@ -6792,10 +7396,10 @@ if test "$_lt_caught_CXX_error" != yes; then
esac
AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)])
- test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no
+ test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no
- _LT_TAGVAR(GCC, $1)="$GXX"
- _LT_TAGVAR(LD, $1)="$LD"
+ _LT_TAGVAR(GCC, $1)=$GXX
+ _LT_TAGVAR(LD, $1)=$LD
## CAVEAT EMPTOR:
## There is no encapsulation within the following macros, do not change
@@ -6822,7 +7426,7 @@ if test "$_lt_caught_CXX_error" != yes; then
lt_cv_path_LD=$lt_save_path_LD
lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld
lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld
-fi # test "$_lt_caught_CXX_error" != yes
+fi # test yes != "$_lt_caught_CXX_error"
AC_LANG_POP
])# _LT_LANG_CXX_CONFIG
@@ -6844,13 +7448,14 @@ AC_REQUIRE([_LT_DECL_SED])
AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])
func_stripname_cnf ()
{
- case ${2} in
- .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
- *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
+ case @S|@2 in
+ .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;;
+ *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;;
esac
} # func_stripname_cnf
])# _LT_FUNC_STRIPNAME_CNF
+
# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME])
# ---------------------------------
# Figure out "hidden" library dependencies from verbose
@@ -6909,12 +7514,18 @@ public class foo {
}
};
_LT_EOF
+], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF
+package foo
+func foo() {
+}
+_LT_EOF
])
_lt_libdeps_save_CFLAGS=$CFLAGS
case "$CC $CFLAGS " in #(
*\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;;
*\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;;
+*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;;
esac
dnl Parse the compiler output and extract the necessary
@@ -6928,13 +7539,13 @@ if AC_TRY_EVAL(ac_compile); then
pre_test_object_deps_done=no
for p in `eval "$output_verbose_link_cmd"`; do
- case ${prev}${p} in
+ case $prev$p in
-L* | -R* | -l*)
# Some compilers place space between "-{L,R}" and the path.
# Remove the space.
- if test $p = "-L" ||
- test $p = "-R"; then
+ if test x-L = "$p" ||
+ test x-R = "$p"; then
prev=$p
continue
fi
@@ -6950,16 +7561,16 @@ if AC_TRY_EVAL(ac_compile); then
case $p in
=*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;;
esac
- if test "$pre_test_object_deps_done" = no; then
- case ${prev} in
+ if test no = "$pre_test_object_deps_done"; then
+ case $prev in
-L | -R)
# Internal compiler library paths should come after those
# provided the user. The postdeps already come after the
# user supplied libs so there is no need to process them.
if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then
- _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}"
+ _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p
else
- _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}"
+ _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p"
fi
;;
# The "-l" case would never come before the object being
@@ -6967,9 +7578,9 @@ if AC_TRY_EVAL(ac_compile); then
esac
else
if test -z "$_LT_TAGVAR(postdeps, $1)"; then
- _LT_TAGVAR(postdeps, $1)="${prev}${p}"
+ _LT_TAGVAR(postdeps, $1)=$prev$p
else
- _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}"
+ _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p"
fi
fi
prev=
@@ -6984,15 +7595,15 @@ if AC_TRY_EVAL(ac_compile); then
continue
fi
- if test "$pre_test_object_deps_done" = no; then
+ if test no = "$pre_test_object_deps_done"; then
if test -z "$_LT_TAGVAR(predep_objects, $1)"; then
- _LT_TAGVAR(predep_objects, $1)="$p"
+ _LT_TAGVAR(predep_objects, $1)=$p
else
_LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p"
fi
else
if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then
- _LT_TAGVAR(postdep_objects, $1)="$p"
+ _LT_TAGVAR(postdep_objects, $1)=$p
else
_LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p"
fi
@@ -7023,51 +7634,6 @@ interix[[3-9]]*)
_LT_TAGVAR(postdep_objects,$1)=
_LT_TAGVAR(postdeps,$1)=
;;
-
-linux*)
- case `$CC -V 2>&1 | sed 5q` in
- *Sun\ C*)
- # Sun C++ 5.9
-
- # The more standards-conforming stlport4 library is
- # incompatible with the Cstd library. Avoid specifying
- # it if it's in CXXFLAGS. Ignore libCrun as
- # -library=stlport4 depends on it.
- case " $CXX $CXXFLAGS " in
- *" -library=stlport4 "*)
- solaris_use_stlport4=yes
- ;;
- esac
-
- if test "$solaris_use_stlport4" != yes; then
- _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
- fi
- ;;
- esac
- ;;
-
-solaris*)
- case $cc_basename in
- CC* | sunCC*)
- # The more standards-conforming stlport4 library is
- # incompatible with the Cstd library. Avoid specifying
- # it if it's in CXXFLAGS. Ignore libCrun as
- # -library=stlport4 depends on it.
- case " $CXX $CXXFLAGS " in
- *" -library=stlport4 "*)
- solaris_use_stlport4=yes
- ;;
- esac
-
- # Adding this requires a known-good setup of shared libraries for
- # Sun compiler versions before 5.6, else PIC objects from an old
- # archive will be linked into the output, leading to subtle bugs.
- if test "$solaris_use_stlport4" != yes; then
- _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun'
- fi
- ;;
- esac
- ;;
esac
])
@@ -7076,7 +7642,7 @@ case " $_LT_TAGVAR(postdeps, $1) " in
esac
_LT_TAGVAR(compiler_lib_search_dirs, $1)=
if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then
- _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'`
+ _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'`
fi
_LT_TAGDECL([], [compiler_lib_search_dirs], [1],
[The directories searched by this compiler when creating a shared library])
@@ -7096,10 +7662,10 @@ _LT_TAGDECL([], [compiler_lib_search_path], [1],
# --------------------------
# Ensure that the configuration variables for a Fortran 77 compiler are
# suitably defined. These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
+# to write the compiler configuration to 'libtool'.
m4_defun([_LT_LANG_F77_CONFIG],
[AC_LANG_PUSH(Fortran 77)
-if test -z "$F77" || test "X$F77" = "Xno"; then
+if test -z "$F77" || test no = "$F77"; then
_lt_disable_F77=yes
fi
@@ -7111,7 +7677,6 @@ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
_LT_TAGVAR(hardcode_direct, $1)=no
_LT_TAGVAR(hardcode_direct_absolute, $1)=no
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
_LT_TAGVAR(hardcode_libdir_separator, $1)=
_LT_TAGVAR(hardcode_minus_L, $1)=no
_LT_TAGVAR(hardcode_automatic, $1)=no
@@ -7137,7 +7702,7 @@ _LT_TAGVAR(objext, $1)=$objext
# the F77 compiler isn't working. Some variables (like enable_shared)
# are currently assumed to apply to all compilers on this platform,
# and will be corrupted by setting them based on a non-working compiler.
-if test "$_lt_disable_F77" != yes; then
+if test yes != "$_lt_disable_F77"; then
# Code to be used in simple compile tests
lt_simple_compile_test_code="\
subroutine t
@@ -7159,7 +7724,7 @@ if test "$_lt_disable_F77" != yes; then
_LT_LINKER_BOILERPLATE
# Allow CC to be a program name with arguments.
- lt_save_CC="$CC"
+ lt_save_CC=$CC
lt_save_GCC=$GCC
lt_save_CFLAGS=$CFLAGS
CC=${F77-"f77"}
@@ -7173,21 +7738,25 @@ if test "$_lt_disable_F77" != yes; then
AC_MSG_RESULT([$can_build_shared])
AC_MSG_CHECKING([whether to build shared libraries])
- test "$can_build_shared" = "no" && enable_shared=no
+ test no = "$can_build_shared" && enable_shared=no
# On AIX, shared libraries and static libraries use the same namespace, and
# are all built from PIC.
case $host_os in
aix3*)
- test "$enable_shared" = yes && enable_static=no
+ test yes = "$enable_shared" && enable_static=no
if test -n "$RANLIB"; then
archive_cmds="$archive_cmds~\$RANLIB \$lib"
postinstall_cmds='$RANLIB $lib'
fi
;;
aix[[4-9]]*)
- if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
- test "$enable_shared" = yes && enable_static=no
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
fi
;;
esac
@@ -7195,11 +7764,11 @@ if test "$_lt_disable_F77" != yes; then
AC_MSG_CHECKING([whether to build static libraries])
# Make sure either enable_shared or enable_static is yes.
- test "$enable_shared" = yes || enable_static=yes
+ test yes = "$enable_shared" || enable_static=yes
AC_MSG_RESULT([$enable_static])
- _LT_TAGVAR(GCC, $1)="$G77"
- _LT_TAGVAR(LD, $1)="$LD"
+ _LT_TAGVAR(GCC, $1)=$G77
+ _LT_TAGVAR(LD, $1)=$LD
## CAVEAT EMPTOR:
## There is no encapsulation within the following macros, do not change
@@ -7216,9 +7785,9 @@ if test "$_lt_disable_F77" != yes; then
fi # test -n "$compiler"
GCC=$lt_save_GCC
- CC="$lt_save_CC"
- CFLAGS="$lt_save_CFLAGS"
-fi # test "$_lt_disable_F77" != yes
+ CC=$lt_save_CC
+ CFLAGS=$lt_save_CFLAGS
+fi # test yes != "$_lt_disable_F77"
AC_LANG_POP
])# _LT_LANG_F77_CONFIG
@@ -7228,11 +7797,11 @@ AC_LANG_POP
# -------------------------
# Ensure that the configuration variables for a Fortran compiler are
# suitably defined. These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
+# to write the compiler configuration to 'libtool'.
m4_defun([_LT_LANG_FC_CONFIG],
[AC_LANG_PUSH(Fortran)
-if test -z "$FC" || test "X$FC" = "Xno"; then
+if test -z "$FC" || test no = "$FC"; then
_lt_disable_FC=yes
fi
@@ -7244,7 +7813,6 @@ _LT_TAGVAR(export_dynamic_flag_spec, $1)=
_LT_TAGVAR(hardcode_direct, $1)=no
_LT_TAGVAR(hardcode_direct_absolute, $1)=no
_LT_TAGVAR(hardcode_libdir_flag_spec, $1)=
-_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)=
_LT_TAGVAR(hardcode_libdir_separator, $1)=
_LT_TAGVAR(hardcode_minus_L, $1)=no
_LT_TAGVAR(hardcode_automatic, $1)=no
@@ -7270,7 +7838,7 @@ _LT_TAGVAR(objext, $1)=$objext
# the FC compiler isn't working. Some variables (like enable_shared)
# are currently assumed to apply to all compilers on this platform,
# and will be corrupted by setting them based on a non-working compiler.
-if test "$_lt_disable_FC" != yes; then
+if test yes != "$_lt_disable_FC"; then
# Code to be used in simple compile tests
lt_simple_compile_test_code="\
subroutine t
@@ -7292,7 +7860,7 @@ if test "$_lt_disable_FC" != yes; then
_LT_LINKER_BOILERPLATE
# Allow CC to be a program name with arguments.
- lt_save_CC="$CC"
+ lt_save_CC=$CC
lt_save_GCC=$GCC
lt_save_CFLAGS=$CFLAGS
CC=${FC-"f95"}
@@ -7308,21 +7876,25 @@ if test "$_lt_disable_FC" != yes; then
AC_MSG_RESULT([$can_build_shared])
AC_MSG_CHECKING([whether to build shared libraries])
- test "$can_build_shared" = "no" && enable_shared=no
+ test no = "$can_build_shared" && enable_shared=no
# On AIX, shared libraries and static libraries use the same namespace, and
# are all built from PIC.
case $host_os in
aix3*)
- test "$enable_shared" = yes && enable_static=no
+ test yes = "$enable_shared" && enable_static=no
if test -n "$RANLIB"; then
archive_cmds="$archive_cmds~\$RANLIB \$lib"
postinstall_cmds='$RANLIB $lib'
fi
;;
aix[[4-9]]*)
- if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
- test "$enable_shared" = yes && enable_static=no
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
fi
;;
esac
@@ -7330,11 +7902,11 @@ if test "$_lt_disable_FC" != yes; then
AC_MSG_CHECKING([whether to build static libraries])
# Make sure either enable_shared or enable_static is yes.
- test "$enable_shared" = yes || enable_static=yes
+ test yes = "$enable_shared" || enable_static=yes
AC_MSG_RESULT([$enable_static])
- _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu"
- _LT_TAGVAR(LD, $1)="$LD"
+ _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu
+ _LT_TAGVAR(LD, $1)=$LD
## CAVEAT EMPTOR:
## There is no encapsulation within the following macros, do not change
@@ -7354,7 +7926,7 @@ if test "$_lt_disable_FC" != yes; then
GCC=$lt_save_GCC
CC=$lt_save_CC
CFLAGS=$lt_save_CFLAGS
-fi # test "$_lt_disable_FC" != yes
+fi # test yes != "$_lt_disable_FC"
AC_LANG_POP
])# _LT_LANG_FC_CONFIG
@@ -7364,7 +7936,7 @@ AC_LANG_POP
# --------------------------
# Ensure that the configuration variables for the GNU Java Compiler compiler
# are suitably defined. These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
+# to write the compiler configuration to 'libtool'.
m4_defun([_LT_LANG_GCJ_CONFIG],
[AC_REQUIRE([LT_PROG_GCJ])dnl
AC_LANG_SAVE
@@ -7398,7 +7970,7 @@ CC=${GCJ-"gcj"}
CFLAGS=$GCJFLAGS
compiler=$CC
_LT_TAGVAR(compiler, $1)=$CC
-_LT_TAGVAR(LD, $1)="$LD"
+_LT_TAGVAR(LD, $1)=$LD
_LT_CC_BASENAME([$compiler])
# GCJ did not exist at the time GCC didn't implicitly link libc in.
@@ -7427,11 +7999,78 @@ CFLAGS=$lt_save_CFLAGS
])# _LT_LANG_GCJ_CONFIG
+# _LT_LANG_GO_CONFIG([TAG])
+# --------------------------
+# Ensure that the configuration variables for the GNU Go compiler
+# are suitably defined. These variables are subsequently used by _LT_CONFIG
+# to write the compiler configuration to 'libtool'.
+m4_defun([_LT_LANG_GO_CONFIG],
+[AC_REQUIRE([LT_PROG_GO])dnl
+AC_LANG_SAVE
+
+# Source file extension for Go test sources.
+ac_ext=go
+
+# Object file extension for compiled Go test sources.
+objext=o
+_LT_TAGVAR(objext, $1)=$objext
+
+# Code to be used in simple compile tests
+lt_simple_compile_test_code="package main; func main() { }"
+
+# Code to be used in simple link tests
+lt_simple_link_test_code='package main; func main() { }'
+
+# ltmain only uses $CC for tagged configurations so make sure $CC is set.
+_LT_TAG_COMPILER
+
+# save warnings/boilerplate of simple test code
+_LT_COMPILER_BOILERPLATE
+_LT_LINKER_BOILERPLATE
+
+# Allow CC to be a program name with arguments.
+lt_save_CC=$CC
+lt_save_CFLAGS=$CFLAGS
+lt_save_GCC=$GCC
+GCC=yes
+CC=${GOC-"gccgo"}
+CFLAGS=$GOFLAGS
+compiler=$CC
+_LT_TAGVAR(compiler, $1)=$CC
+_LT_TAGVAR(LD, $1)=$LD
+_LT_CC_BASENAME([$compiler])
+
+# Go did not exist at the time GCC didn't implicitly link libc in.
+_LT_TAGVAR(archive_cmds_need_lc, $1)=no
+
+_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds
+_LT_TAGVAR(reload_flag, $1)=$reload_flag
+_LT_TAGVAR(reload_cmds, $1)=$reload_cmds
+
+if test -n "$compiler"; then
+ _LT_COMPILER_NO_RTTI($1)
+ _LT_COMPILER_PIC($1)
+ _LT_COMPILER_C_O($1)
+ _LT_COMPILER_FILE_LOCKS($1)
+ _LT_LINKER_SHLIBS($1)
+ _LT_LINKER_HARDCODE_LIBPATH($1)
+
+ _LT_CONFIG($1)
+fi
+
+AC_LANG_RESTORE
+
+GCC=$lt_save_GCC
+CC=$lt_save_CC
+CFLAGS=$lt_save_CFLAGS
+])# _LT_LANG_GO_CONFIG
+
+
# _LT_LANG_RC_CONFIG([TAG])
# -------------------------
# Ensure that the configuration variables for the Windows resource compiler
# are suitably defined. These variables are subsequently used by _LT_CONFIG
-# to write the compiler configuration to `libtool'.
+# to write the compiler configuration to 'libtool'.
m4_defun([_LT_LANG_RC_CONFIG],
[AC_REQUIRE([LT_PROG_RC])dnl
AC_LANG_SAVE
@@ -7447,7 +8086,7 @@ _LT_TAGVAR(objext, $1)=$objext
lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }'
# Code to be used in simple link tests
-lt_simple_link_test_code="$lt_simple_compile_test_code"
+lt_simple_link_test_code=$lt_simple_compile_test_code
# ltmain only uses $CC for tagged configurations so make sure $CC is set.
_LT_TAG_COMPILER
@@ -7457,7 +8096,7 @@ _LT_COMPILER_BOILERPLATE
_LT_LINKER_BOILERPLATE
# Allow CC to be a program name with arguments.
-lt_save_CC="$CC"
+lt_save_CC=$CC
lt_save_CFLAGS=$CFLAGS
lt_save_GCC=$GCC
GCC=
@@ -7486,7 +8125,7 @@ AC_DEFUN([LT_PROG_GCJ],
[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ],
[m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ],
[AC_CHECK_TOOL(GCJ, gcj,)
- test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2"
+ test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2"
AC_SUBST(GCJFLAGS)])])[]dnl
])
@@ -7496,6 +8135,13 @@ dnl aclocal-1.4 backwards compatibility:
dnl AC_DEFUN([LT_AC_PROG_GCJ], [])
+# LT_PROG_GO
+# ----------
+AC_DEFUN([LT_PROG_GO],
+[AC_CHECK_TOOL(GOC, gccgo,)
+])
+
+
# LT_PROG_RC
# ----------
AC_DEFUN([LT_PROG_RC],
@@ -7588,7 +8234,7 @@ lt_ac_count=0
# Add /usr/xpg4/bin/sed as it is typically found on Solaris
# along with /bin/sed that truncates output.
for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
- test ! -f $lt_ac_sed && continue
+ test ! -f "$lt_ac_sed" && continue
cat /dev/null > conftest.in
lt_ac_count=0
echo $ECHO_N "0123456789$ECHO_C" >conftest.in
@@ -7605,9 +8251,9 @@ for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do
$lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break
cmp -s conftest.out conftest.nl || break
# 10000 chars as input seems more than enough
- test $lt_ac_count -gt 10 && break
+ test 10 -lt "$lt_ac_count" && break
lt_ac_count=`expr $lt_ac_count + 1`
- if test $lt_ac_count -gt $lt_ac_max; then
+ if test "$lt_ac_count" -gt "$lt_ac_max"; then
lt_ac_max=$lt_ac_count
lt_cv_path_SED=$lt_ac_sed
fi
@@ -7631,27 +8277,7 @@ dnl AC_DEFUN([LT_AC_PROG_SED], [])
# Find out whether the shell is Bourne or XSI compatible,
# or has some other useful features.
m4_defun([_LT_CHECK_SHELL_FEATURES],
-[AC_MSG_CHECKING([whether the shell understands some XSI constructs])
-# Try some XSI features
-xsi_shell=no
-( _lt_dummy="a/b/c"
- test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
- = c,a/b,b/c, \
- && eval 'test $(( 1 + 1 )) -eq 2 \
- && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
- && xsi_shell=yes
-AC_MSG_RESULT([$xsi_shell])
-_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell'])
-
-AC_MSG_CHECKING([whether the shell understands "+="])
-lt_shell_append=no
-( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \
- >/dev/null 2>&1 \
- && lt_shell_append=yes
-AC_MSG_RESULT([$lt_shell_append])
-_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append'])
-
-if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
+[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
lt_unset=unset
else
lt_unset=false
@@ -7675,102 +8301,9 @@ _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl
])# _LT_CHECK_SHELL_FEATURES
-# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY)
-# ------------------------------------------------------
-# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and
-# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY.
-m4_defun([_LT_PROG_FUNCTION_REPLACE],
-[dnl {
-sed -e '/^$1 ()$/,/^} # $1 /c\
-$1 ()\
-{\
-m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1])
-} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-])
-
-
-# _LT_PROG_REPLACE_SHELLFNS
-# -------------------------
-# Replace existing portable implementations of several shell functions with
-# equivalent extended shell implementations where those features are available..
-m4_defun([_LT_PROG_REPLACE_SHELLFNS],
-[if test x"$xsi_shell" = xyes; then
- _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl
- case ${1} in
- */*) func_dirname_result="${1%/*}${2}" ;;
- * ) func_dirname_result="${3}" ;;
- esac])
-
- _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl
- func_basename_result="${1##*/}"])
-
- _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl
- case ${1} in
- */*) func_dirname_result="${1%/*}${2}" ;;
- * ) func_dirname_result="${3}" ;;
- esac
- func_basename_result="${1##*/}"])
-
- _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl
- # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
- # positional parameters, so assign one to ordinary parameter first.
- func_stripname_result=${3}
- func_stripname_result=${func_stripname_result#"${1}"}
- func_stripname_result=${func_stripname_result%"${2}"}])
-
- _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl
- func_split_long_opt_name=${1%%=*}
- func_split_long_opt_arg=${1#*=}])
-
- _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl
- func_split_short_opt_arg=${1#??}
- func_split_short_opt_name=${1%"$func_split_short_opt_arg"}])
-
- _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl
- case ${1} in
- *.lo) func_lo2o_result=${1%.lo}.${objext} ;;
- *) func_lo2o_result=${1} ;;
- esac])
-
- _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo])
-
- _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))])
-
- _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}])
-fi
-
-if test x"$lt_shell_append" = xyes; then
- _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"])
-
- _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl
- func_quote_for_eval "${2}"
-dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \
- eval "${1}+=\\\\ \\$func_quote_for_eval_result"])
-
- # Save a `func_append' function call where possible by direct use of '+='
- sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
- test 0 -eq $? || _lt_function_replace_fail=:
-else
- # Save a `func_append' function call even when '+=' is not available
- sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
- test 0 -eq $? || _lt_function_replace_fail=:
-fi
-
-if test x"$_lt_function_replace_fail" = x":"; then
- AC_MSG_WARN([Unable to substitute extended shell functions in $ofile])
-fi
-])
-
# _LT_PATH_CONVERSION_FUNCTIONS
# -----------------------------
-# Determine which file name conversion functions should be used by
+# Determine what file name conversion functions should be used by
# func_to_host_file (and, implicitly, by func_to_host_path). These are needed
# for certain cross-compile configurations and native mingw.
m4_defun([_LT_PATH_CONVERSION_FUNCTIONS],
@@ -7837,15 +8370,15 @@ _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd],
# Helper functions for option handling. -*- Autoconf -*-
#
-# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software
+# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
# unlimited permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
-# serial 7 ltoptions.m4
+# serial 8 ltoptions.m4
# This is to help aclocal find these macros, as it can't see m4_define.
AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])])
@@ -7866,7 +8399,7 @@ m4_define([_LT_SET_OPTION],
[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl
m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]),
_LT_MANGLE_DEFUN([$1], [$2]),
- [m4_warning([Unknown $1 option `$2'])])[]dnl
+ [m4_warning([Unknown $1 option '$2'])])[]dnl
])
@@ -7912,13 +8445,15 @@ m4_if([$1],[LT_INIT],[
dnl
dnl If no reference was made to various pairs of opposing options, then
dnl we run the default mode handler for the pair. For example, if neither
- dnl `shared' nor `disable-shared' was passed, we enable building of shared
+ dnl 'shared' nor 'disable-shared' was passed, we enable building of shared
dnl archives by default:
_LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED])
_LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC])
_LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC])
_LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install],
- [_LT_ENABLE_FAST_INSTALL])
+ [_LT_ENABLE_FAST_INSTALL])
+ _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4],
+ [_LT_WITH_AIX_SONAME([aix])])
])
])# _LT_SET_OPTIONS
@@ -7946,7 +8481,7 @@ AU_DEFUN([AC_LIBTOOL_DLOPEN],
[_LT_SET_OPTION([LT_INIT], [dlopen])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
-put the `dlopen' option into LT_INIT's first parameter.])
+put the 'dlopen' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
@@ -7982,7 +8517,7 @@ AU_DEFUN([AC_LIBTOOL_WIN32_DLL],
_LT_SET_OPTION([LT_INIT], [win32-dll])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
-put the `win32-dll' option into LT_INIT's first parameter.])
+put the 'win32-dll' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
@@ -7991,9 +8526,9 @@ dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], [])
# _LT_ENABLE_SHARED([DEFAULT])
# ----------------------------
-# implement the --enable-shared flag, and supports the `shared' and
-# `disable-shared' LT_INIT options.
-# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+# implement the --enable-shared flag, and supports the 'shared' and
+# 'disable-shared' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_SHARED],
[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([shared],
@@ -8006,14 +8541,14 @@ AC_ARG_ENABLE([shared],
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac],
[enable_shared=]_LT_ENABLE_SHARED_DEFAULT)
@@ -8045,9 +8580,9 @@ dnl AC_DEFUN([AM_DISABLE_SHARED], [])
# _LT_ENABLE_STATIC([DEFAULT])
# ----------------------------
-# implement the --enable-static flag, and support the `static' and
-# `disable-static' LT_INIT options.
-# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+# implement the --enable-static flag, and support the 'static' and
+# 'disable-static' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_STATIC],
[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([static],
@@ -8060,14 +8595,14 @@ AC_ARG_ENABLE([static],
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac],
[enable_static=]_LT_ENABLE_STATIC_DEFAULT)
@@ -8099,9 +8634,9 @@ dnl AC_DEFUN([AM_DISABLE_STATIC], [])
# _LT_ENABLE_FAST_INSTALL([DEFAULT])
# ----------------------------------
-# implement the --enable-fast-install flag, and support the `fast-install'
-# and `disable-fast-install' LT_INIT options.
-# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'.
+# implement the --enable-fast-install flag, and support the 'fast-install'
+# and 'disable-fast-install' LT_INIT options.
+# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'.
m4_define([_LT_ENABLE_FAST_INSTALL],
[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl
AC_ARG_ENABLE([fast-install],
@@ -8114,14 +8649,14 @@ AC_ARG_ENABLE([fast-install],
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac],
[enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT)
@@ -8138,14 +8673,14 @@ AU_DEFUN([AC_ENABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
-the `fast-install' option into LT_INIT's first parameter.])
+the 'fast-install' option into LT_INIT's first parameter.])
])
AU_DEFUN([AC_DISABLE_FAST_INSTALL],
[_LT_SET_OPTION([LT_INIT], [disable-fast-install])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you put
-the `disable-fast-install' option into LT_INIT's first parameter.])
+the 'disable-fast-install' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
@@ -8153,19 +8688,85 @@ dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], [])
dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], [])
+# _LT_WITH_AIX_SONAME([DEFAULT])
+# ----------------------------------
+# implement the --with-aix-soname flag, and support the `aix-soname=aix'
+# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT
+# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'.
+m4_define([_LT_WITH_AIX_SONAME],
+[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl
+shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[[5-9]]*,yes)
+ AC_MSG_CHECKING([which variant of shared library versioning to provide])
+ AC_ARG_WITH([aix-soname],
+ [AS_HELP_STRING([--with-aix-soname=aix|svr4|both],
+ [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])],
+ [case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ AC_MSG_ERROR([Unknown argument to --with-aix-soname])
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname],
+ [AC_CACHE_VAL([lt_cv_with_aix_soname],
+ [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT)
+ with_aix_soname=$lt_cv_with_aix_soname])
+ AC_MSG_RESULT([$with_aix_soname])
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+_LT_DECL([], [shared_archive_member_spec], [0],
+ [Shared archive member basename, for filename based shared library versioning on AIX])dnl
+])# _LT_WITH_AIX_SONAME
+
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])])
+LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])])
+
+
# _LT_WITH_PIC([MODE])
# --------------------
-# implement the --with-pic flag, and support the `pic-only' and `no-pic'
+# implement the --with-pic flag, and support the 'pic-only' and 'no-pic'
# LT_INIT options.
-# MODE is either `yes' or `no'. If omitted, it defaults to `both'.
+# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'.
m4_define([_LT_WITH_PIC],
[AC_ARG_WITH([pic],
- [AS_HELP_STRING([--with-pic],
+ [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@],
[try to use only PIC/non-PIC objects @<:@default=use both@:>@])],
- [pic_mode="$withval"],
- [pic_mode=default])
-
-test -z "$pic_mode" && pic_mode=m4_default([$1], [default])
+ [lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac],
+ [pic_mode=m4_default([$1], [default])])
_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl
])# _LT_WITH_PIC
@@ -8178,7 +8779,7 @@ AU_DEFUN([AC_LIBTOOL_PICMODE],
[_LT_SET_OPTION([LT_INIT], [pic-only])
AC_DIAGNOSE([obsolete],
[$0: Remove this warning and the call to _LT_SET_OPTION when you
-put the `pic-only' option into LT_INIT's first parameter.])
+put the 'pic-only' option into LT_INIT's first parameter.])
])
dnl aclocal-1.4 backwards compatibility:
@@ -8201,7 +8802,8 @@ LT_OPTION_DEFINE([LTDL_INIT], [convenience],
# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*-
#
-# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software
+# Foundation, Inc.
# Written by Gary V. Vaughan, 2004
#
# This file is free software; the Free Software Foundation gives
@@ -8234,7 +8836,7 @@ m4_define([_lt_join],
# ------------
# Manipulate m4 lists.
# These macros are necessary as long as will still need to support
-# Autoconf-2.59 which quotes differently.
+# Autoconf-2.59, which quotes differently.
m4_define([lt_car], [[$1]])
m4_define([lt_cdr],
[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])],
@@ -8245,7 +8847,7 @@ m4_define([lt_unquote], $1)
# lt_append(MACRO-NAME, STRING, [SEPARATOR])
# ------------------------------------------
-# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'.
+# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'.
# Note that neither SEPARATOR nor STRING are expanded; they are appended
# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked).
# No SEPARATOR is output if MACRO-NAME was previously undefined (different
@@ -8325,7 +8927,7 @@ m4_define([lt_dict_filter],
# ltversion.m4 -- version numbers -*- Autoconf -*-
#
-# Copyright (C) 2004 Free Software Foundation, Inc.
+# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc.
# Written by Scott James Remnant, 2004
#
# This file is free software; the Free Software Foundation gives
@@ -8334,22 +8936,23 @@ m4_define([lt_dict_filter],
# @configure_input@
-# serial 3293 ltversion.m4
+# serial 4179 ltversion.m4
# This file is part of GNU Libtool
-m4_define([LT_PACKAGE_VERSION], [2.4])
-m4_define([LT_PACKAGE_REVISION], [1.3293])
+m4_define([LT_PACKAGE_VERSION], [2.4.6])
+m4_define([LT_PACKAGE_REVISION], [2.4.6])
AC_DEFUN([LTVERSION_VERSION],
-[macro_version='2.4'
-macro_revision='1.3293'
+[macro_version='2.4.6'
+macro_revision='2.4.6'
_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?])
_LT_DECL(, macro_revision, 0)
])
# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*-
#
-# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc.
+# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software
+# Foundation, Inc.
# Written by Scott James Remnant, 2004.
#
# This file is free software; the Free Software Foundation gives
@@ -8360,7 +8963,7 @@ _LT_DECL(, macro_revision, 0)
# These exist entirely to fool aclocal when bootstrapping libtool.
#
-# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN)
+# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN),
# which have later been changed to m4_define as they aren't part of the
# exported API, or moved to Autoconf or Automake where they belong.
#
@@ -8374,7 +8977,7 @@ _LT_DECL(, macro_revision, 0)
# included after everything else. This provides aclocal with the
# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything
# because those macros already exist, or will be overwritten later.
-# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
+# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6.
#
# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here.
# Yes, that means every name once taken will need to remain here until
@@ -8446,7 +9049,7 @@ m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])])
m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])])
m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
-# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2002-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -8458,10 +9061,10 @@ m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])])
# generated from the m4 files accompanying Automake X.Y.
# (This private macro should not be called outside this file.)
AC_DEFUN([AM_AUTOMAKE_VERSION],
-[am__api_version='1.11'
+[am__api_version='1.15'
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
dnl require some minimum version. Point them to the right macro.
-m4_if([$1], [1.11.1], [],
+m4_if([$1], [1.15.1], [],
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
])
@@ -8477,22 +9080,22 @@ m4_define([_AM_AUTOCONF_VERSION], [])
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
# This function is AC_REQUIREd by AM_INIT_AUTOMAKE.
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
-[AM_AUTOMAKE_VERSION([1.11.1])dnl
+[AM_AUTOMAKE_VERSION([1.15.1])dnl
m4_ifndef([AC_AUTOCONF_VERSION],
[m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl
_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
-# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets
-# $ac_aux_dir to `$srcdir/foo'. In other projects, it is set to
-# `$srcdir', `$srcdir/..', or `$srcdir/../..'.
+# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to
+# '$srcdir', '$srcdir/..', or '$srcdir/../..'.
#
# Of course, Automake must honor this variable whenever it calls a
# tool from the auxiliary directory. The problem is that $srcdir (and
@@ -8511,7 +9114,7 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
#
# The reason of the latter failure is that $top_srcdir and $ac_aux_dir
# are both prefixed by $srcdir. In an in-source build this is usually
-# harmless because $srcdir is `.', but things will broke when you
+# harmless because $srcdir is '.', but things will broke when you
# start a VPATH build or use an absolute $srcdir.
#
# So we could use something similar to $top_srcdir/$ac_aux_dir/missing,
@@ -8529,30 +9132,26 @@ _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))])
# configured tree to be moved without reconfiguration.
AC_DEFUN([AM_AUX_DIR_EXPAND],
-[dnl Rely on autoconf to set up CDPATH properly.
-AC_PREREQ([2.50])dnl
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
+[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
])
# AM_CONDITIONAL -*- Autoconf -*-
-# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008
-# Free Software Foundation, Inc.
+# Copyright (C) 1997-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 9
-
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
# -------------------------------------
# Define a conditional.
AC_DEFUN([AM_CONDITIONAL],
-[AC_PREREQ(2.52)dnl
- ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
- [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
+[AC_PREREQ([2.52])dnl
+ m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
+ [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
AC_SUBST([$1_TRUE])dnl
AC_SUBST([$1_FALSE])dnl
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
@@ -8571,16 +9170,14 @@ AC_CONFIG_COMMANDS_PRE(
Usually this means the macro was only invoked conditionally.]])
fi])])
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009
-# Free Software Foundation, Inc.
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 10
-# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
+# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be
# written in clear, in which case automake, when reading aclocal.m4,
# will think it sees a *use*, and therefore will trigger all it's
# C support machinery. Also note that it means that autoscan, seeing
@@ -8590,7 +9187,7 @@ fi])])
# _AM_DEPENDENCIES(NAME)
# ----------------------
# See how the compiler implements dependency checking.
-# NAME is "CC", "CXX", "GCJ", or "OBJC".
+# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC".
# We try a few techniques and use that to set a single cache variable.
#
# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was
@@ -8603,12 +9200,13 @@ AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl
AC_REQUIRE([AM_MAKE_INCLUDE])dnl
AC_REQUIRE([AM_DEP_TRACK])dnl
-ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
- [$1], CXX, [depcc="$CXX" am_compiler_list=],
- [$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
- [$1], UPC, [depcc="$UPC" am_compiler_list=],
- [$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
- [depcc="$$1" am_compiler_list=])
+m4_if([$1], [CC], [depcc="$CC" am_compiler_list=],
+ [$1], [CXX], [depcc="$CXX" am_compiler_list=],
+ [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
+ [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'],
+ [$1], [UPC], [depcc="$UPC" am_compiler_list=],
+ [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
+ [depcc="$$1" am_compiler_list=])
AC_CACHE_CHECK([dependency style of $depcc],
[am_cv_$1_dependencies_compiler_type],
@@ -8616,8 +9214,9 @@ AC_CACHE_CHECK([dependency style of $depcc],
# We make a subdir and do the tests there. Otherwise we can end up
# making bogus files that we don't know about and never remove. For
# instance it was reported that on HP-UX the gcc test will end up
- # making a dummy file named `D' -- because `-MD' means `put the output
- # in D'.
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
mkdir conftest.dir
# Copy depcomp to subdir because otherwise we won't find it if we're
# using a relative directory.
@@ -8656,16 +9255,16 @@ AC_CACHE_CHECK([dependency style of $depcc],
: > sub/conftest.c
for i in 1 2 3 4 5 6; do
echo '#include "conftst'$i'.h"' >> sub/conftest.c
- # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
- # Solaris 8's {/usr,}/bin/sh.
- touch sub/conftst$i.h
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
done
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
- # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
# mode. It turns out that the SunPro C++ compiler does not properly
- # handle `-M -o', and we need to detect this. Also, some Intel
- # versions had trouble with output in subdirs
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
am__obj=sub/conftest.${OBJEXT-o}
am__minus_obj="-o $am__obj"
case $depmode in
@@ -8674,16 +9273,16 @@ AC_CACHE_CHECK([dependency style of $depcc],
test "$am__universal" = false || continue
;;
nosideeffect)
- # after this tag, mechanisms are not by side-effect, so they'll
- # only be used when explicitly requested
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
if test "x$enable_dependency_tracking" = xyes; then
continue
else
break
fi
;;
- msvisualcpp | msvcmsys)
- # This compiler won't grok `-c -o', but also, the minuso test has
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
# not run yet. These depmodes are late enough in the game, and
# so weak that their functioning should not be impacted.
am__obj=conftest.${OBJEXT-o}
@@ -8731,7 +9330,7 @@ AM_CONDITIONAL([am__fastdep$1], [
# AM_SET_DEPDIR
# -------------
# Choose a directory name for dependency files.
-# This macro is AC_REQUIREd in _AM_DEPENDENCIES
+# This macro is AC_REQUIREd in _AM_DEPENDENCIES.
AC_DEFUN([AM_SET_DEPDIR],
[AC_REQUIRE([AM_SET_LEADING_DOT])dnl
AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
@@ -8741,34 +9340,39 @@ AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl
# AM_DEP_TRACK
# ------------
AC_DEFUN([AM_DEP_TRACK],
-[AC_ARG_ENABLE(dependency-tracking,
-[ --disable-dependency-tracking speeds up one-time build
- --enable-dependency-tracking do not reject slow dependency extractors])
+[AC_ARG_ENABLE([dependency-tracking], [dnl
+AS_HELP_STRING(
+ [--enable-dependency-tracking],
+ [do not reject slow dependency extractors])
+AS_HELP_STRING(
+ [--disable-dependency-tracking],
+ [speeds up one-time build])])
if test "x$enable_dependency_tracking" != xno; then
am_depcomp="$ac_aux_dir/depcomp"
AMDEPBACKSLASH='\'
+ am__nodep='_no'
fi
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
AC_SUBST([AMDEPBACKSLASH])dnl
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
+AC_SUBST([am__nodep])dnl
+_AM_SUBST_NOTMAKE([am__nodep])dnl
])
# Generate code to set up dependency tracking. -*- Autoconf -*-
-# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
-# Free Software Foundation, Inc.
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-#serial 5
# _AM_OUTPUT_DEPENDENCY_COMMANDS
# ------------------------------
AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
[{
- # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
case $CONFIG_FILES in
@@ -8781,7 +9385,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
- # We used to match only the files named `Makefile.in', but
+ # We used to match only the files named 'Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
@@ -8793,21 +9397,19 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
- # from the Makefile without running `make'.
+ # from the Makefile without running 'make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
- test -z "am__include" && continue
+ test -z "$am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
- # When using ansi2knr, U may be empty or an underscore; expand it
- U=`sed -n 's/^U = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`AS_DIRNAME(["$file"])`
@@ -8825,7 +9427,7 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
# This macro should only be invoked once -- use via AC_REQUIRE.
#
# This code is only required when automatic dependency tracking
-# is enabled. FIXME. This creates each `.P' file that we will
+# is enabled. FIXME. This creates each '.P' file that we will
# need in order to bootstrap the dependency handling code.
AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
[AC_CONFIG_COMMANDS([depfiles],
@@ -8835,18 +9437,21 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# Do all the work for Automake. -*- Autoconf -*-
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
-# 2005, 2006, 2008, 2009 Free Software Foundation, Inc.
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 16
-
# This macro actually does too much. Some checks are only needed if
# your package does certain things. But this isn't really a big deal.
+dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O.
+m4_define([AC_PROG_CC],
+m4_defn([AC_PROG_CC])
+[_AM_PROG_CC_C_O
+])
+
# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE])
# AM_INIT_AUTOMAKE([OPTIONS])
# -----------------------------------------------
@@ -8859,7 +9464,7 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
# arguments mandatory, and then we can depend on a new Autoconf
# release and drop the old call support.
AC_DEFUN([AM_INIT_AUTOMAKE],
-[AC_PREREQ([2.62])dnl
+[AC_PREREQ([2.65])dnl
dnl Autoconf wants to disallow AM_ names. We explicitly allow
dnl the ones we care about.
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
@@ -8888,33 +9493,42 @@ AC_SUBST([CYGPATH_W])
# Define the identity of the package.
dnl Distinguish between old-style and new-style calls.
m4_ifval([$2],
-[m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
+[AC_DIAGNOSE([obsolete],
+ [$0: two- and three-arguments forms are deprecated.])
+m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl
AC_SUBST([PACKAGE], [$1])dnl
AC_SUBST([VERSION], [$2])],
[_AM_SET_OPTIONS([$1])dnl
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
-m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
+m4_if(
+ m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]),
+ [ok:ok],,
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
_AM_IF_OPTION([no-define],,
-[AC_DEFINE_UNQUOTED(PACKAGE, "$PACKAGE", [Name of package])
- AC_DEFINE_UNQUOTED(VERSION, "$VERSION", [Version number of package])])dnl
+[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package])
+ AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl
# Some tools Automake needs.
AC_REQUIRE([AM_SANITY_CHECK])dnl
AC_REQUIRE([AC_ARG_PROGRAM])dnl
-AM_MISSING_PROG(ACLOCAL, aclocal-${am__api_version})
-AM_MISSING_PROG(AUTOCONF, autoconf)
-AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version})
-AM_MISSING_PROG(AUTOHEADER, autoheader)
-AM_MISSING_PROG(MAKEINFO, makeinfo)
+AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}])
+AM_MISSING_PROG([AUTOCONF], [autoconf])
+AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}])
+AM_MISSING_PROG([AUTOHEADER], [autoheader])
+AM_MISSING_PROG([MAKEINFO], [makeinfo])
AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl
-AC_REQUIRE([AM_PROG_MKDIR_P])dnl
-# We need awk for the "check" target. The system "awk" is bad on
-# some platforms.
+AC_REQUIRE([AC_PROG_MKDIR_P])dnl
+# For better backward compatibility. To be removed once Automake 1.9.x
+# dies out for good. For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+AC_SUBST([mkdir_p], ['$(MKDIR_P)'])
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
AC_REQUIRE([AC_PROG_AWK])dnl
AC_REQUIRE([AC_PROG_MAKE_SET])dnl
AC_REQUIRE([AM_SET_LEADING_DOT])dnl
@@ -8923,34 +9537,82 @@ _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])],
[_AM_PROG_TAR([v7])])])
_AM_IF_OPTION([no-dependencies],,
[AC_PROVIDE_IFELSE([AC_PROG_CC],
- [_AM_DEPENDENCIES(CC)],
- [define([AC_PROG_CC],
- defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl
+ [_AM_DEPENDENCIES([CC])],
+ [m4_define([AC_PROG_CC],
+ m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_CXX],
- [_AM_DEPENDENCIES(CXX)],
- [define([AC_PROG_CXX],
- defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
+ [_AM_DEPENDENCIES([CXX])],
+ [m4_define([AC_PROG_CXX],
+ m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
- [_AM_DEPENDENCIES(OBJC)],
- [define([AC_PROG_OBJC],
- defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
+ [_AM_DEPENDENCIES([OBJC])],
+ [m4_define([AC_PROG_OBJC],
+ m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl
+AC_PROVIDE_IFELSE([AC_PROG_OBJCXX],
+ [_AM_DEPENDENCIES([OBJCXX])],
+ [m4_define([AC_PROG_OBJCXX],
+ m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl
])
-_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl
-dnl The `parallel-tests' driver may need to know about EXEEXT, so add the
-dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro
-dnl is hooked onto _AC_COMPILER_EXEEXT early, see below.
+AC_REQUIRE([AM_SILENT_RULES])dnl
+dnl The testsuite driver may need to know about EXEEXT, so add the
+dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This
+dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below.
AC_CONFIG_COMMANDS_PRE(dnl
[m4_provide_if([_AM_COMPILER_EXEEXT],
[AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl
+
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes. So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+ cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present. This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message. This
+can help us improve future automake versions.
+
+END
+ if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+ echo 'Configuration will proceed anyway, since you have set the' >&2
+ echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+ echo >&2
+ else
+ cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+ AC_MSG_ERROR([Your 'rm' program is bad, sorry.])
+ fi
+fi
+dnl The trailing newline in this macro's definition is deliberate, for
+dnl backward compatibility and to allow trailing 'dnl'-style comments
+dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841.
])
-dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
+dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not
dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further
dnl mangled by Autoconf and run in a shell conditional statement.
m4_define([_AC_COMPILER_EXEEXT],
m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])])
-
# When config.status generates a header, we must update the stamp-h file.
# This file resides in the same directory as the config header
# that is generated. The stamp files are numbered to have different names.
@@ -8972,7 +9634,7 @@ for _am_header in $config_headers :; do
done
echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count])
-# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -8983,7 +9645,7 @@ echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_co
# Define $install_sh.
AC_DEFUN([AM_PROG_INSTALL_SH],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -8991,16 +9653,14 @@ if test x"${install_sh}" != xset; then
install_sh="\${SHELL} $am_aux_dir/install-sh"
esac
fi
-AC_SUBST(install_sh)])
+AC_SUBST([install_sh])])
-# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+# Copyright (C) 2003-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 2
-
# Check whether the underlying file-system supports filenames
# with a leading dot. For instance MS-DOS doesn't.
AC_DEFUN([AM_SET_LEADING_DOT],
@@ -9016,14 +9676,12 @@ AC_SUBST([am__leading_dot])])
# Check to see how 'make' treats includes. -*- Autoconf -*-
-# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 4
-
# AM_MAKE_INCLUDE()
# -----------------
# Check to see how make treats includes.
@@ -9041,7 +9699,7 @@ am__quote=
_am_result=none
# First try GNU make style include.
echo "include confinc" > confmf
-# Ignore all kinds of additional output from `make'.
+# Ignore all kinds of additional output from 'make'.
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=include
@@ -9068,15 +9726,12 @@ rm -f confinc confmf
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
-# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008
-# Free Software Foundation, Inc.
+# Copyright (C) 1997-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 6
-
# AM_MISSING_PROG(NAME, PROGRAM)
# ------------------------------
AC_DEFUN([AM_MISSING_PROG],
@@ -9084,11 +9739,10 @@ AC_DEFUN([AM_MISSING_PROG],
$1=${$1-"${am_missing_run}$2"}
AC_SUBST($1)])
-
# AM_MISSING_HAS_RUN
# ------------------
-# Define MISSING if not defined so far and test if it supports --run.
-# If it does, set am_missing_run to use it, otherwise, to nothing.
+# Define MISSING if not defined so far and test if it is modern enough.
+# If it is, set am_missing_run to use it, otherwise, to nothing.
AC_DEFUN([AM_MISSING_HAS_RUN],
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
AC_REQUIRE_AUX_FILE([missing])dnl
@@ -9101,63 +9755,35 @@ if test x"${MISSING+set}" != xset; then
esac
fi
# Use eval to expand $SHELL
-if eval "$MISSING --run true"; then
- am_missing_run="$MISSING --run "
+if eval "$MISSING --is-lightweight"; then
+ am_missing_run="$MISSING "
else
am_missing_run=
- AC_MSG_WARN([`missing' script is too old or missing])
+ AC_MSG_WARN(['missing' script is too old or missing])
fi
])
-# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
-#
-# This file is free software; the Free Software Foundation
-# gives unlimited permission to copy and/or distribute it,
-# with or without modifications, as long as this notice is preserved.
-
-# AM_PROG_MKDIR_P
-# ---------------
-# Check for `mkdir -p'.
-AC_DEFUN([AM_PROG_MKDIR_P],
-[AC_PREREQ([2.60])dnl
-AC_REQUIRE([AC_PROG_MKDIR_P])dnl
-dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
-dnl while keeping a definition of mkdir_p for backward compatibility.
-dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
-dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
-dnl Makefile.ins that do not define MKDIR_P, so we do our own
-dnl adjustment using top_builddir (which is defined more often than
-dnl MKDIR_P).
-AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
-case $mkdir_p in
- [[\\/$]]* | ?:[[\\/]]*) ;;
- */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
-esac
-])
-
# Helper functions for option handling. -*- Autoconf -*-
-# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 4
-
# _AM_MANGLE_OPTION(NAME)
# -----------------------
AC_DEFUN([_AM_MANGLE_OPTION],
[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])])
# _AM_SET_OPTION(NAME)
-# ------------------------------
+# --------------------
# Set option NAME. Presently that only means defining a flag for this option.
AC_DEFUN([_AM_SET_OPTION],
-[m4_define(_AM_MANGLE_OPTION([$1]), 1)])
+[m4_define(_AM_MANGLE_OPTION([$1]), [1])])
# _AM_SET_OPTIONS(OPTIONS)
-# ----------------------------------
+# ------------------------
# OPTIONS is a space-separated list of Automake options.
AC_DEFUN([_AM_SET_OPTIONS],
[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])])
@@ -9168,24 +9794,82 @@ AC_DEFUN([_AM_SET_OPTIONS],
AC_DEFUN([_AM_IF_OPTION],
[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])])
-# Check to make sure that the build environment is sane. -*- Autoconf -*-
+# Copyright (C) 1999-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# _AM_PROG_CC_C_O
+# ---------------
+# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC
+# to automatically call this.
+AC_DEFUN([_AM_PROG_CC_C_O],
+[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
+AC_REQUIRE_AUX_FILE([compile])dnl
+AC_LANG_PUSH([C])dnl
+AC_CACHE_CHECK(
+ [whether $CC understands -c and -o together],
+ [am_cv_prog_cc_c_o],
+ [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])])
+ # Make sure it works both with $CC and with simple cc.
+ # Following AC_PROG_CC_C_O, we do the test twice because some
+ # compilers refuse to overwrite an existing .o file with -o,
+ # though they will create one.
+ am_cv_prog_cc_c_o=yes
+ for am_i in 1 2; do
+ if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \
+ && test -f conftest2.$ac_objext; then
+ : OK
+ else
+ am_cv_prog_cc_c_o=no
+ break
+ fi
+ done
+ rm -f core conftest*
+ unset am_i])
+if test "$am_cv_prog_cc_c_o" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+AC_LANG_POP([C])])
+
+# For backward compatibility.
+AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])])
-# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008
-# Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 5
+# AM_RUN_LOG(COMMAND)
+# -------------------
+# Run COMMAND, save the exit status in ac_status, and log it.
+# (This has been adapted from Autoconf's _AC_RUN_LOG macro.)
+AC_DEFUN([AM_RUN_LOG],
+[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD
+ ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD
+ (exit $ac_status); }])
+
+# Check to make sure that the build environment is sane. -*- Autoconf -*-
+
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
# AM_SANITY_CHECK
# ---------------
AC_DEFUN([AM_SANITY_CHECK],
[AC_MSG_CHECKING([whether build environment is sane])
-# Just in case
-sleep 1
-echo timestamp > conftest.file
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
@@ -9196,32 +9880,40 @@ case `pwd` in
esac
case $srcdir in
*[[\\\"\#\$\&\'\`$am_lf\ \ ]]*)
- AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);;
+ AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);;
esac
-# Do `set' in a subshell so we don't clobber the current shell's
+# Do 'set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
- set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
- if test "$[*]" = "X"; then
- # -L didn't work.
- set X `ls -t "$srcdir/configure" conftest.file`
- fi
- rm -f conftest.file
- if test "$[*]" != "X $srcdir/configure conftest.file" \
- && test "$[*]" != "X conftest.file $srcdir/configure"; then
-
- # If neither matched, then we have a broken ls. This can happen
- # if, for instance, CONFIG_SHELL is bash and it inherits a
- # broken ls alias from the environment. This has actually
- # happened. Such a system could not be considered "sane".
- AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
-alias in your environment])
- fi
-
+ am_has_slept=no
+ for am_try in 1 2; do
+ echo "timestamp, slept: $am_has_slept" > conftest.file
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$[*]" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ if test "$[*]" != "X $srcdir/configure conftest.file" \
+ && test "$[*]" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken
+ alias in your environment])
+ fi
+ if test "$[2]" = conftest.file || test $am_try -eq 2; then
+ break
+ fi
+ # Just in case.
+ sleep 1
+ am_has_slept=yes
+ done
test "$[2]" = conftest.file
)
then
@@ -9231,36 +9923,85 @@ else
AC_MSG_ERROR([newly created file is older than distributed files!
Check your system clock])
fi
-AC_MSG_RESULT(yes)])
+AC_MSG_RESULT([yes])
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+ ( sleep 1 ) &
+ am_sleep_pid=$!
+fi
+AC_CONFIG_COMMANDS_PRE(
+ [AC_MSG_CHECKING([that generated files are newer than configure])
+ if test -n "$am_sleep_pid"; then
+ # Hide warnings about reused PIDs.
+ wait $am_sleep_pid 2>/dev/null
+ fi
+ AC_MSG_RESULT([done])])
+rm -f conftest.file
+])
-# Copyright (C) 2009 Free Software Foundation, Inc.
+# Copyright (C) 2009-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 1
-
# AM_SILENT_RULES([DEFAULT])
# --------------------------
# Enable less verbose build rules; with the default set to DEFAULT
-# (`yes' being less verbose, `no' or empty being verbose).
+# ("yes" being less verbose, "no" or empty being verbose).
AC_DEFUN([AM_SILENT_RULES],
-[AC_ARG_ENABLE([silent-rules],
-[ --enable-silent-rules less verbose build output (undo: `make V=1')
- --disable-silent-rules verbose build output (undo: `make V=0')])
-case $enable_silent_rules in
-yes) AM_DEFAULT_VERBOSITY=0;;
-no) AM_DEFAULT_VERBOSITY=1;;
-*) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
+[AC_ARG_ENABLE([silent-rules], [dnl
+AS_HELP_STRING(
+ [--enable-silent-rules],
+ [less verbose build output (undo: "make V=1")])
+AS_HELP_STRING(
+ [--disable-silent-rules],
+ [verbose build output (undo: "make V=0")])dnl
+])
+case $enable_silent_rules in @%:@ (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);;
esac
+dnl
+dnl A few 'make' implementations (e.g., NonStop OS and NextStep)
+dnl do not support nested variable expansions.
+dnl See automake bug#9928 and bug#10237.
+am_make=${MAKE-make}
+AC_CACHE_CHECK([whether $am_make supports nested variables],
+ [am_cv_make_support_nested_variables],
+ [if AS_ECHO([['TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi])
+if test $am_cv_make_support_nested_variables = yes; then
+ dnl Using '$V' instead of '$(V)' breaks IRIX make.
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AC_SUBST([AM_V])dnl
+AM_SUBST_NOTMAKE([AM_V])dnl
+AC_SUBST([AM_DEFAULT_V])dnl
+AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl
AC_SUBST([AM_DEFAULT_VERBOSITY])dnl
AM_BACKSLASH='\'
AC_SUBST([AM_BACKSLASH])dnl
_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
])
-# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc.
+# Copyright (C) 2001-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -9268,34 +10009,32 @@ _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl
# AM_PROG_INSTALL_STRIP
# ---------------------
-# One issue with vendor `install' (even GNU) is that you can't
+# One issue with vendor 'install' (even GNU) is that you can't
# specify the program used to strip binaries. This is especially
# annoying in cross-compiling environments, where the build's strip
# is unlikely to handle the host's binaries.
# Fortunately install-sh will honor a STRIPPROG variable, so we
-# always use install-sh in `make install-strip', and initialize
+# always use install-sh in "make install-strip", and initialize
# STRIPPROG with the value of the STRIP variable (set by the user).
AC_DEFUN([AM_PROG_INSTALL_STRIP],
[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl
-# Installed binaries are usually stripped using `strip' when the user
-# run `make install-strip'. However `strip' might not be the right
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip". However 'strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
-# will honor the `STRIP' environment variable to overrule this program.
-dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
+# will honor the 'STRIP' environment variable to overrule this program.
+dnl Don't test for $cross_compiling = yes, because it might be 'maybe'.
if test "$cross_compiling" != no; then
AC_CHECK_TOOL([STRIP], [strip], :)
fi
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
AC_SUBST([INSTALL_STRIP_PROGRAM])])
-# Copyright (C) 2006, 2008 Free Software Foundation, Inc.
+# Copyright (C) 2006-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 2
-
# _AM_SUBST_NOTMAKE(VARIABLE)
# ---------------------------
# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in.
@@ -9303,24 +10042,22 @@ AC_SUBST([INSTALL_STRIP_PROGRAM])])
AC_DEFUN([_AM_SUBST_NOTMAKE])
# AM_SUBST_NOTMAKE(VARIABLE)
-# ---------------------------
+# --------------------------
# Public sister of _AM_SUBST_NOTMAKE.
AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Check how to create a tarball. -*- Autoconf -*-
-# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+# Copyright (C) 2004-2017 Free Software Foundation, Inc.
#
# This file is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
-# serial 2
-
# _AM_PROG_TAR(FORMAT)
# --------------------
# Check how to create a tarball in format FORMAT.
-# FORMAT should be one of `v7', `ustar', or `pax'.
+# FORMAT should be one of 'v7', 'ustar', or 'pax'.
#
# Substitute a variable $(am__tar) that is a command
# writing to stdout a FORMAT-tarball containing the directory
@@ -9330,75 +10067,114 @@ AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)])
# Substitute a variable $(am__untar) that extract such
# a tarball read from stdin.
# $(am__untar) < result.tar
+#
AC_DEFUN([_AM_PROG_TAR],
-[# Always define AMTAR for backward compatibility.
-AM_MISSING_PROG([AMTAR], [tar])
-m4_if([$1], [v7],
- [am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'],
- [m4_case([$1], [ustar],, [pax],,
- [m4_fatal([Unknown tar format])])
-AC_MSG_CHECKING([how to create a $1 tar archive])
-# Loop over all known methods to create a tar archive until one works.
+[# Always define AMTAR for backward compatibility. Yes, it's still used
+# in the wild :-( We should find a proper way to deprecate it ...
+AC_SUBST([AMTAR], ['$${TAR-tar}'])
+
+# We'll loop over all known methods to create a tar archive until one works.
_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none'
-_am_tools=${am_cv_prog_tar_$1-$_am_tools}
-# Do not fold the above two line into one, because Tru64 sh and
-# Solaris sh will not grok spaces in the rhs of `-'.
-for _am_tool in $_am_tools
-do
- case $_am_tool in
- gnutar)
- for _am_tar in tar gnutar gtar;
- do
- AM_RUN_LOG([$_am_tar --version]) && break
- done
- am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
- am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
- am__untar="$_am_tar -xf -"
- ;;
- plaintar)
- # Must skip GNU tar: if it does not support --format= it doesn't create
- # ustar tarball either.
- (tar --version) >/dev/null 2>&1 && continue
- am__tar='tar chf - "$$tardir"'
- am__tar_='tar chf - "$tardir"'
- am__untar='tar xf -'
- ;;
- pax)
- am__tar='pax -L -x $1 -w "$$tardir"'
- am__tar_='pax -L -x $1 -w "$tardir"'
- am__untar='pax -r'
- ;;
- cpio)
- am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
- am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
- am__untar='cpio -i -H $1 -d'
- ;;
- none)
- am__tar=false
- am__tar_=false
- am__untar=false
- ;;
- esac
- # If the value was cached, stop now. We just wanted to have am__tar
- # and am__untar set.
- test -n "${am_cv_prog_tar_$1}" && break
+m4_if([$1], [v7],
+ [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'],
+
+ [m4_case([$1],
+ [ustar],
+ [# The POSIX 1988 'ustar' format is defined with fixed-size fields.
+ # There is notably a 21 bits limit for the UID and the GID. In fact,
+ # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343
+ # and bug#13588).
+ am_max_uid=2097151 # 2^21 - 1
+ am_max_gid=$am_max_uid
+ # The $UID and $GID variables are not portable, so we need to resort
+ # to the POSIX-mandated id(1) utility. Errors in the 'id' calls
+ # below are definitely unexpected, so allow the users to see them
+ # (that is, avoid stderr redirection).
+ am_uid=`id -u || echo unknown`
+ am_gid=`id -g || echo unknown`
+ AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format])
+ if test $am_uid -le $am_max_uid; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ _am_tools=none
+ fi
+ AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format])
+ if test $am_gid -le $am_max_gid; then
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ _am_tools=none
+ fi],
+
+ [pax],
+ [],
- # tar/untar a dummy directory, and stop if the command works
- rm -rf conftest.dir
- mkdir conftest.dir
- echo GrepMe > conftest.dir/file
- AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ [m4_fatal([Unknown tar format])])
+
+ AC_MSG_CHECKING([how to create a $1 tar archive])
+
+ # Go ahead even if we have the value already cached. We do so because we
+ # need to set the values for the 'am__tar' and 'am__untar' variables.
+ _am_tools=${am_cv_prog_tar_$1-$_am_tools}
+
+ for _am_tool in $_am_tools; do
+ case $_am_tool in
+ gnutar)
+ for _am_tar in tar gnutar gtar; do
+ AM_RUN_LOG([$_am_tar --version]) && break
+ done
+ am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"'
+ am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"'
+ am__untar="$_am_tar -xf -"
+ ;;
+ plaintar)
+ # Must skip GNU tar: if it does not support --format= it doesn't create
+ # ustar tarball either.
+ (tar --version) >/dev/null 2>&1 && continue
+ am__tar='tar chf - "$$tardir"'
+ am__tar_='tar chf - "$tardir"'
+ am__untar='tar xf -'
+ ;;
+ pax)
+ am__tar='pax -L -x $1 -w "$$tardir"'
+ am__tar_='pax -L -x $1 -w "$tardir"'
+ am__untar='pax -r'
+ ;;
+ cpio)
+ am__tar='find "$$tardir" -print | cpio -o -H $1 -L'
+ am__tar_='find "$tardir" -print | cpio -o -H $1 -L'
+ am__untar='cpio -i -H $1 -d'
+ ;;
+ none)
+ am__tar=false
+ am__tar_=false
+ am__untar=false
+ ;;
+ esac
+
+ # If the value was cached, stop now. We just wanted to have am__tar
+ # and am__untar set.
+ test -n "${am_cv_prog_tar_$1}" && break
+
+ # tar/untar a dummy directory, and stop if the command works.
+ rm -rf conftest.dir
+ mkdir conftest.dir
+ echo GrepMe > conftest.dir/file
+ AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar])
+ rm -rf conftest.dir
+ if test -s conftest.tar; then
+ AM_RUN_LOG([$am__untar <conftest.tar])
+ AM_RUN_LOG([cat conftest.dir/file])
+ grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
+ fi
+ done
rm -rf conftest.dir
- if test -s conftest.tar; then
- AM_RUN_LOG([$am__untar <conftest.tar])
- grep GrepMe conftest.dir/file >/dev/null 2>&1 && break
- fi
-done
-rm -rf conftest.dir
-AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
-AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+ AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool])
+ AC_MSG_RESULT([$am_cv_prog_tar_$1])])
+
AC_SUBST([am__tar])
AC_SUBST([am__untar])
]) # _AM_PROG_TAR
diff --git a/lib/libedit/compile b/lib/libedit/compile
new file mode 100755
index 000000000000..2ab71e4ea13e
--- /dev/null
+++ b/lib/libedit/compile
@@ -0,0 +1,348 @@
+#! /bin/sh
+# Wrapper for compilers which do not understand '-c -o'.
+
+scriptversion=2016-01-11.22; # UTC
+
+# Copyright (C) 1999-2017 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
+ if test -f "$dir/lib$lib.a"; then
+ found=yes
+ lib=$dir/lib$lib.a
+ 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 | \
+ icl | *[/\\]icl | icl.exe | *[/\\]icl.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: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/lib/libedit/config.guess b/lib/libedit/config.guess
index dc84c68ef798..2193702b12af 100755
--- a/lib/libedit/config.guess
+++ b/lib/libedit/config.guess
@@ -1,14 +1,12 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-# Free Software Foundation, Inc.
+# Copyright 1992-2017 Free Software Foundation, Inc.
-timestamp='2009-11-20'
+timestamp='2017-05-27'
# This file 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 of the License, or
+# 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
@@ -17,26 +15,22 @@ timestamp='2009-11-20'
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# 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 Per Bothner. Please send patches (context
-# diff format) to <config-patches@gnu.org> and include a ChangeLog
-# entry.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
#
-# This script attempts to guess a canonical system name similar to
-# config.sub. If it succeeds, it prints the system name on stdout, and
-# exits with 0. Otherwise, it exits with 1.
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
me=`echo "$0" | sed -e 's,.*/,,'`
@@ -56,8 +50,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+Copyright 1992-2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -139,12 +132,33 @@ UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+case "${UNAME_SYSTEM}" in
+Linux|GNU|GNU/*)
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ LIBC=gnu
+
+ eval $set_cc_for_build
+ cat <<-EOF > $dummy.c
+ #include <features.h>
+ #if defined(__UCLIBC__)
+ LIBC=uclibc
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
+ #else
+ LIBC=gnu
+ #endif
+ EOF
+ eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+ ;;
+esac
+
# Note: order is significant - the case branches are not exclusive.
case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
*:NetBSD:*:*)
# NetBSD (nbsd) targets should (where applicable) match one or
- # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
# *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
# switched to ELF, *-*-netbsd* would select the old
# object file format. This provides both forward
@@ -154,19 +168,29 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# Note: NetBSD doesn't particularly care about the vendor
# portion of the name. We always set it to "unknown".
sysctl="sysctl -n hw.machine_arch"
- UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \
- /usr/sbin/$sysctl 2>/dev/null || echo unknown)`
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ /sbin/$sysctl 2>/dev/null || \
+ /usr/sbin/$sysctl 2>/dev/null || \
+ echo unknown)`
case "${UNAME_MACHINE_ARCH}" in
armeb) machine=armeb-unknown ;;
arm*) machine=arm-unknown ;;
sh3el) machine=shl-unknown ;;
sh3eb) machine=sh-unknown ;;
sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine=${arch}${endian}-unknown
+ ;;
*) machine=${UNAME_MACHINE_ARCH}-unknown ;;
esac
# The Operating System including object format, if it has switched
- # to ELF recently, or will in the future.
+ # to ELF recently (or will in the future) and ABI.
case "${UNAME_MACHINE_ARCH}" in
+ earm*)
+ os=netbsdelf
+ ;;
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
eval $set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
@@ -180,7 +204,14 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
fi
;;
*)
- os=netbsd
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case "${UNAME_MACHINE_ARCH}" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
;;
esac
# The OS release
@@ -193,18 +224,26 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
release='-gnu'
;;
*)
- release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'`
+ release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
;;
esac
# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
# contains redundant information, the shorter form:
# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
- echo "${machine}-${os}${release}"
+ echo "${machine}-${os}${release}${abi}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
exit ;;
*:OpenBSD:*:*)
UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE}
exit ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE}
+ exit ;;
*:ekkoBSD:*:*)
echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE}
exit ;;
@@ -217,13 +256,16 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
*:MirBSD:*:*)
echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
exit ;;
+ *:Sortix:*:*)
+ echo ${UNAME_MACHINE}-unknown-sortix
+ exit ;;
alpha:OSF1:*:*)
case $UNAME_RELEASE in
*4.0)
UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
;;
esac
# According to Compaq, /usr/sbin/psrinfo has been available on
@@ -233,43 +275,46 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
case "$ALPHA_CPU_TYPE" in
"EV4 (21064)")
- UNAME_MACHINE="alpha" ;;
+ UNAME_MACHINE=alpha ;;
"EV4.5 (21064)")
- UNAME_MACHINE="alpha" ;;
+ UNAME_MACHINE=alpha ;;
"LCA4 (21066/21068)")
- UNAME_MACHINE="alpha" ;;
+ UNAME_MACHINE=alpha ;;
"EV5 (21164)")
- UNAME_MACHINE="alphaev5" ;;
+ UNAME_MACHINE=alphaev5 ;;
"EV5.6 (21164A)")
- UNAME_MACHINE="alphaev56" ;;
+ UNAME_MACHINE=alphaev56 ;;
"EV5.6 (21164PC)")
- UNAME_MACHINE="alphapca56" ;;
+ UNAME_MACHINE=alphapca56 ;;
"EV5.7 (21164PC)")
- UNAME_MACHINE="alphapca57" ;;
+ UNAME_MACHINE=alphapca57 ;;
"EV6 (21264)")
- UNAME_MACHINE="alphaev6" ;;
+ UNAME_MACHINE=alphaev6 ;;
"EV6.7 (21264A)")
- UNAME_MACHINE="alphaev67" ;;
+ UNAME_MACHINE=alphaev67 ;;
"EV6.8CB (21264C)")
- UNAME_MACHINE="alphaev68" ;;
+ UNAME_MACHINE=alphaev68 ;;
"EV6.8AL (21264B)")
- UNAME_MACHINE="alphaev68" ;;
+ UNAME_MACHINE=alphaev68 ;;
"EV6.8CX (21264D)")
- UNAME_MACHINE="alphaev68" ;;
+ UNAME_MACHINE=alphaev68 ;;
"EV6.9A (21264/EV69A)")
- UNAME_MACHINE="alphaev69" ;;
+ UNAME_MACHINE=alphaev69 ;;
"EV7 (21364)")
- UNAME_MACHINE="alphaev7" ;;
+ UNAME_MACHINE=alphaev7 ;;
"EV7.9 (21364A)")
- UNAME_MACHINE="alphaev79" ;;
+ UNAME_MACHINE=alphaev79 ;;
esac
# A Pn.n version is a patched version.
# A Vn.n version is a released version.
# A Tn.n version is a released field test version.
# A Xn.n version is an unreleased experimental baselevel.
# 1.2 uses "1.2" for uname -r.
- echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
- exit ;;
+ echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
Alpha\ *:Windows_NT*:*)
# How do we know it's Interix rather than the generic POSIX subsystem?
# Should we change UNAME_MACHINE based on the output of uname instead
@@ -295,12 +340,12 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo s390-ibm-zvmoe
exit ;;
*:OS400:*:*)
- echo powerpc-ibm-os400
+ echo powerpc-ibm-os400
exit ;;
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
echo arm-acorn-riscix${UNAME_RELEASE}
exit ;;
- arm:riscos:*:*|arm:RISCOS:*:*)
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
exit ;;
SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
@@ -338,16 +383,16 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
exit ;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
eval $set_cc_for_build
- SUN_ARCH="i386"
+ SUN_ARCH=i386
# If there is a compiler, see if it is configured for 64-bit objects.
# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
# This test works for both compilers.
- if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
- (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
grep IS_64BIT_ARCH >/dev/null
then
- SUN_ARCH="x86_64"
+ SUN_ARCH=x86_64
fi
fi
echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
@@ -372,7 +417,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
exit ;;
sun*:*:4.2BSD:*)
UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
- test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3
+ test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3
case "`/bin/arch`" in
sun3)
echo m68k-sun-sunos${UNAME_RELEASE}
@@ -394,23 +439,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# MiNT. But MiNT is downward compatible to TOS, so this should
# be no problem.
atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
echo m68k-atari-mint${UNAME_RELEASE}
- exit ;;
+ exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint${UNAME_RELEASE}
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-milan-mint${UNAME_RELEASE}
+ exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-hades-mint${UNAME_RELEASE}
+ exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
- exit ;;
+ echo m68k-unknown-mint${UNAME_RELEASE}
+ exit ;;
m68k:machten:*:*)
echo m68k-apple-machten${UNAME_RELEASE}
exit ;;
@@ -480,8 +525,8 @@ EOF
echo m88k-motorola-sysv3
exit ;;
AViiON:dgux:*:*)
- # DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
then
if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
@@ -494,7 +539,7 @@ EOF
else
echo i586-dg-dgux${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
echo m88k-dolphin-sysv3
exit ;;
@@ -551,15 +596,16 @@ EOF
echo rs6000-ibm-aix3.2
fi
exit ;;
- *:AIX:*:[456])
+ *:AIX:*:[4567])
IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then
IBM_ARCH=rs6000
else
IBM_ARCH=powerpc
fi
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
+ if [ -x /usr/bin/lslpp ] ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
else
IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
fi
@@ -594,58 +640,58 @@ EOF
9000/[678][0-9][0-9])
if [ -x /usr/bin/getconf ]; then
sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
- sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
- case "${sc_cpu_version}" in
- 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0
- 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1
- 532) # CPU_PA_RISC2_0
- case "${sc_kernel_bits}" in
- 32) HP_ARCH="hppa2.0n" ;;
- 64) HP_ARCH="hppa2.0w" ;;
- '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20
- esac ;;
- esac
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case "${sc_cpu_version}" in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "${sc_kernel_bits}" in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
fi
if [ "${HP_ARCH}" = "" ]; then
eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ sed 's/^ //' << EOF >$dummy.c
- #define _HPUX_SOURCE
- #include <stdlib.h>
- #include <unistd.h>
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
- int main ()
- {
- #if defined(_SC_KERNEL_BITS)
- long bits = sysconf(_SC_KERNEL_BITS);
- #endif
- long cpu = sysconf (_SC_CPU_VERSION);
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
- switch (cpu)
- {
- case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
- case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
- case CPU_PA_RISC2_0:
- #if defined(_SC_KERNEL_BITS)
- switch (bits)
- {
- case 64: puts ("hppa2.0w"); break;
- case 32: puts ("hppa2.0n"); break;
- default: puts ("hppa2.0"); break;
- } break;
- #else /* !defined(_SC_KERNEL_BITS) */
- puts ("hppa2.0"); break;
- #endif
- default: puts ("hppa1.0"); break;
- }
- exit (0);
- }
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
EOF
- (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;;
esac
- if [ ${HP_ARCH} = "hppa2.0w" ]
+ if [ ${HP_ARCH} = hppa2.0w ]
then
eval $set_cc_for_build
@@ -658,12 +704,12 @@ EOF
# $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
# => hppa64-hp-hpux11.23
- if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) |
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
grep -q __LP64__
then
- HP_ARCH="hppa2.0w"
+ HP_ARCH=hppa2.0w
else
- HP_ARCH="hppa64"
+ HP_ARCH=hppa64
fi
fi
echo ${HP_ARCH}-hp-hpux${HPUX_REV}
@@ -730,22 +776,22 @@ EOF
exit ;;
C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
echo c1-convex-bsd
- exit ;;
+ exit ;;
C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
if getsysinfo -f scalar_acc
then echo c32-convex-bsd
else echo c2-convex-bsd
fi
- exit ;;
+ exit ;;
C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
echo c34-convex-bsd
- exit ;;
+ exit ;;
C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
echo c38-convex-bsd
- exit ;;
+ exit ;;
C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
echo c4-convex-bsd
- exit ;;
+ exit ;;
CRAY*Y-MP:*:*:*)
echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
@@ -768,15 +814,15 @@ EOF
echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
exit ;;
F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
- FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'`
- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
- FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
- echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
- exit ;;
+ FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'`
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
5000:UNIX_System_V:4.*:*)
- FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'`
- FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'`
- echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
exit ;;
i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE}
@@ -788,30 +834,36 @@ EOF
echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
exit ;;
*:FreeBSD:*:*)
- case ${UNAME_MACHINE} in
- pc98)
- echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case ${UNAME_PROCESSOR} in
amd64)
- echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
- *)
- echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
esac
+ echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
exit ;;
i*:CYGWIN*:*)
echo ${UNAME_MACHINE}-pc-cygwin
exit ;;
+ *:MINGW64*:*)
+ echo ${UNAME_MACHINE}-pc-mingw64
+ exit ;;
*:MINGW*:*)
echo ${UNAME_MACHINE}-pc-mingw32
exit ;;
+ *:MSYS*:*)
+ echo ${UNAME_MACHINE}-pc-msys
+ exit ;;
i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ # uname -m includes "-pc" on this system.
+ echo ${UNAME_MACHINE}-mingw32
exit ;;
i*:PW*:*)
echo ${UNAME_MACHINE}-pc-pw32
exit ;;
*:Interix*:*)
- case ${UNAME_MACHINE} in
+ case ${UNAME_MACHINE} in
x86)
echo i586-pc-interix${UNAME_RELEASE}
exit ;;
@@ -848,15 +900,22 @@ EOF
exit ;;
*:GNU:*:*)
# the GNU system
- echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
+ echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'`
exit ;;
*:GNU/*:*:*)
# other systems with GNU libc and userland
- echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu
+ echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC}
exit ;;
i*86:Minix:*:*)
echo ${UNAME_MACHINE}-pc-minix
exit ;;
+ aarch64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
alpha:Linux:*:*)
case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
EV5) UNAME_MACHINE=alphaev5 ;;
@@ -866,52 +925,62 @@ EOF
EV6) UNAME_MACHINE=alphaev6 ;;
EV67) UNAME_MACHINE=alphaev67 ;;
EV68*) UNAME_MACHINE=alphaev68 ;;
- esac
+ esac
objdump --private-headers /bin/sh | grep -q ld.so.1
- if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi
- echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC}
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ arc:Linux:*:* | arceb:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
arm*:Linux:*:*)
eval $set_cc_for_build
if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ARM_EABI__
then
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
else
- echo ${UNAME_MACHINE}-unknown-linux-gnueabi
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi
+ else
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
+ fi
fi
exit ;;
avr32*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
cris:Linux:*:*)
- echo cris-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-${LIBC}
exit ;;
crisv32:Linux:*:*)
- echo crisv32-axis-linux-gnu
+ echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+ exit ;;
+ e2k:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
frv:Linux:*:*)
- echo frv-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ hexagon:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
i*86:Linux:*:*)
- LIBC=gnu
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
- #ifdef __dietlibc__
- LIBC=dietlibc
- #endif
-EOF
- eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC'`
- echo "${UNAME_MACHINE}-pc-linux-${LIBC}"
+ echo ${UNAME_MACHINE}-pc-linux-${LIBC}
exit ;;
ia64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ k1om:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
m32r*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
m68*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
mips:Linux:*:* | mips64:Linux:*:*)
eval $set_cc_for_build
@@ -930,51 +999,69 @@ EOF
#endif
EOF
eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; }
+ test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
;;
- or32:Linux:*:*)
- echo or32-unknown-linux-gnu
+ mips64el:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-${LIBC}
+ exit ;;
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
padre:Linux:*:*)
- echo sparc-unknown-linux-gnu
+ echo sparc-unknown-linux-${LIBC}
exit ;;
parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-gnu
+ echo hppa64-unknown-linux-${LIBC}
exit ;;
parisc:Linux:*:* | hppa:Linux:*:*)
# Look for CPU level
case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
- PA7*) echo hppa1.1-unknown-linux-gnu ;;
- PA8*) echo hppa2.0-unknown-linux-gnu ;;
- *) echo hppa-unknown-linux-gnu ;;
+ PA7*) echo hppa1.1-unknown-linux-${LIBC} ;;
+ PA8*) echo hppa2.0-unknown-linux-${LIBC} ;;
+ *) echo hppa-unknown-linux-${LIBC} ;;
esac
exit ;;
ppc64:Linux:*:*)
- echo powerpc64-unknown-linux-gnu
+ echo powerpc64-unknown-linux-${LIBC}
exit ;;
ppc:Linux:*:*)
- echo powerpc-unknown-linux-gnu
+ echo powerpc-unknown-linux-${LIBC}
+ exit ;;
+ ppc64le:Linux:*:*)
+ echo powerpc64le-unknown-linux-${LIBC}
+ exit ;;
+ ppcle:Linux:*:*)
+ echo powerpcle-unknown-linux-${LIBC}
+ exit ;;
+ riscv32:Linux:*:* | riscv64:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
s390:Linux:*:* | s390x:Linux:*:*)
- echo ${UNAME_MACHINE}-ibm-linux
+ echo ${UNAME_MACHINE}-ibm-linux-${LIBC}
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
sh*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
sparc:Linux:*:* | sparc64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ exit ;;
+ tile*:Linux:*:*)
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
vax:Linux:*:*)
- echo ${UNAME_MACHINE}-dec-linux-gnu
+ echo ${UNAME_MACHINE}-dec-linux-${LIBC}
exit ;;
x86_64:Linux:*:*)
- echo x86_64-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-pc-linux-${LIBC}
exit ;;
xtensa*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-gnu
+ echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
exit ;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
@@ -983,11 +1070,11 @@ EOF
echo i386-sequent-sysv4
exit ;;
i*86:UNIX_SV:4.2MP:2.*)
- # Unixware is an offshoot of SVR4, but it has its own version
- # number series starting with 2...
- # I am not positive that other SVR4 systems won't match this,
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
# I just have to hope. -- rms.
- # Use sysv4.2uw... so that sysv4* matches it.
+ # Use sysv4.2uw... so that sysv4* matches it.
echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION}
exit ;;
i*86:OS/2:*:*)
@@ -1019,7 +1106,7 @@ EOF
fi
exit ;;
i*86:*:5:[678]*)
- # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
case `/bin/uname -X | grep "^Machine"` in
*486*) UNAME_MACHINE=i486 ;;
*Pentium) UNAME_MACHINE=i586 ;;
@@ -1047,13 +1134,13 @@ EOF
exit ;;
pc:*:*:*)
# Left here for compatibility:
- # uname -m prints for DJGPP always 'pc', but it prints nothing about
- # the processor, so we play safe by assuming i586.
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
# Note: whatever this is, it MUST be the same as what config.sub
- # prints for the "djgpp" host, or else GDB configury will decide that
+ # prints for the "djgpp" host, or else GDB configure will decide that
# this is a cross-build.
echo i586-pc-msdosdjgpp
- exit ;;
+ exit ;;
Intel:Mach:3*:*)
echo i386-pc-mach3
exit ;;
@@ -1088,8 +1175,8 @@ EOF
/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
&& { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;;
3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
- /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
- && { echo i486-ncr-sysv4; exit; } ;;
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
NCR*:*:4.2:* | MPRAS*:*:4.2:*)
OS_REL='.3'
test -r /etc/.relid \
@@ -1132,10 +1219,10 @@ EOF
echo ns32k-sni-sysv
fi
exit ;;
- PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
- # says <Richard.M.Bartel@ccMail.Census.GOV>
- echo i586-unisys-sysv4
- exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
*:UNIX_System_V:4*:FTX*)
# From Gerald Hewes <hewes@openmarket.com>.
# How about differentiating between stratus architectures? -djm
@@ -1161,11 +1248,11 @@ EOF
exit ;;
R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
if [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ echo mips-nec-sysv${UNAME_RELEASE}
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv${UNAME_RELEASE}
fi
- exit ;;
+ exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
echo powerpc-be-beos
exit ;;
@@ -1178,6 +1265,9 @@ EOF
BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
echo i586-pc-haiku
exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
SX-4:SUPER-UX:*:*)
echo sx4-nec-superux${UNAME_RELEASE}
exit ;;
@@ -1196,6 +1286,9 @@ EOF
SX-8R:SUPER-UX:*:*)
echo sx8r-nec-superux${UNAME_RELEASE}
exit ;;
+ SX-ACE:SUPER-UX:*:*)
+ echo sxace-nec-superux${UNAME_RELEASE}
+ exit ;;
Power*:Rhapsody:*:*)
echo powerpc-apple-rhapsody${UNAME_RELEASE}
exit ;;
@@ -1204,24 +1297,43 @@ EOF
exit ;;
*:Darwin:*:*)
UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
- case $UNAME_PROCESSOR in
- i386)
- eval $set_cc_for_build
- if [ "$CC_FOR_BUILD" != 'no_compiler_found' ]; then
- if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
- (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \
- grep IS_64BIT_ARCH >/dev/null
- then
- UNAME_PROCESSOR="x86_64"
- fi
- fi ;;
- unknown) UNAME_PROCESSOR=powerpc ;;
- esac
+ eval $set_cc_for_build
+ if test "$UNAME_PROCESSOR" = unknown ; then
+ UNAME_PROCESSOR=powerpc
+ fi
+ if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
+ if [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # Avoid executing cc on OS X 10.9, as it ships with a stub
+ # that puts up a graphical alert prompting to install
+ # developer tools. Any system running Mac OS X 10.7 or
+ # later (Darwin 11 and later) is required to have a 64-bit
+ # processor. This is not true of the ARM version of Darwin
+ # that Apple uses in portable devices.
+ UNAME_PROCESSOR=x86_64
+ fi
echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
exit ;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
UNAME_PROCESSOR=`uname -p`
- if test "$UNAME_PROCESSOR" = "x86"; then
+ if test "$UNAME_PROCESSOR" = x86; then
UNAME_PROCESSOR=i386
UNAME_MACHINE=pc
fi
@@ -1230,12 +1342,18 @@ EOF
*:QNX:*:4*)
echo i386-pc-qnx
exit ;;
- NSE-?:NONSTOP_KERNEL:*:*)
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk${UNAME_RELEASE}
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
echo nse-tandem-nsk${UNAME_RELEASE}
exit ;;
- NSR-?:NONSTOP_KERNEL:*:*)
+ NSR-*:NONSTOP_KERNEL:*:*)
echo nsr-tandem-nsk${UNAME_RELEASE}
exit ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk${UNAME_RELEASE}
+ exit ;;
*:NonStop-UX:*:*)
echo mips-compaq-nonstopux
exit ;;
@@ -1249,7 +1367,7 @@ EOF
# "uname -m" is not consistent, so use $cputype instead. 386
# is converted to i386 for consistency with other x86
# operating systems.
- if test "$cputype" = "386"; then
+ if test "$cputype" = 386; then
UNAME_MACHINE=i386
else
UNAME_MACHINE="$cputype"
@@ -1275,13 +1393,13 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ echo mips-sei-seiux${UNAME_RELEASE}
exit ;;
*:DragonFly:*:*)
echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
exit ;;
*:*VMS:*:*)
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
case "${UNAME_MACHINE}" in
A*) echo alpha-dec-vms ; exit ;;
I*) echo ia64-dec-vms ; exit ;;
@@ -1291,7 +1409,7 @@ EOF
echo i386-pc-xenix
exit ;;
i*86:skyos:*:*)
- echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//'
+ echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'`
exit ;;
i*86:rdos:*:*)
echo ${UNAME_MACHINE}-pc-rdos
@@ -1299,174 +1417,28 @@ EOF
i*86:AROS:*:*)
echo ${UNAME_MACHINE}-pc-aros
exit ;;
-esac
-
-#echo '(No uname command or uname output not recognized.)' 1>&2
-#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2
-
-eval $set_cc_for_build
-cat >$dummy.c <<EOF
-#ifdef _SEQUENT_
-# include <sys/types.h>
-# include <sys/utsname.h>
-#endif
-main ()
-{
-#if defined (sony)
-#if defined (MIPSEB)
- /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
- I don't know.... */
- printf ("mips-sony-bsd\n"); exit (0);
-#else
-#include <sys/param.h>
- printf ("m68k-sony-newsos%s\n",
-#ifdef NEWSOS4
- "4"
-#else
- ""
-#endif
- ); exit (0);
-#endif
-#endif
-
-#if defined (__arm) && defined (__acorn) && defined (__unix)
- printf ("arm-acorn-riscix\n"); exit (0);
-#endif
-
-#if defined (hp300) && !defined (hpux)
- printf ("m68k-hp-bsd\n"); exit (0);
-#endif
-
-#if defined (NeXT)
-#if !defined (__ARCHITECTURE__)
-#define __ARCHITECTURE__ "m68k"
-#endif
- int version;
- version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
- if (version < 4)
- printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
- else
- printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
- exit (0);
-#endif
-
-#if defined (MULTIMAX) || defined (n16)
-#if defined (UMAXV)
- printf ("ns32k-encore-sysv\n"); exit (0);
-#else
-#if defined (CMU)
- printf ("ns32k-encore-mach\n"); exit (0);
-#else
- printf ("ns32k-encore-bsd\n"); exit (0);
-#endif
-#endif
-#endif
-
-#if defined (__386BSD__)
- printf ("i386-pc-bsd\n"); exit (0);
-#endif
-
-#if defined (sequent)
-#if defined (i386)
- printf ("i386-sequent-dynix\n"); exit (0);
-#endif
-#if defined (ns32000)
- printf ("ns32k-sequent-dynix\n"); exit (0);
-#endif
-#endif
-
-#if defined (_SEQUENT_)
- struct utsname un;
-
- uname(&un);
-
- if (strncmp(un.version, "V2", 2) == 0) {
- printf ("i386-sequent-ptx2\n"); exit (0);
- }
- if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
- printf ("i386-sequent-ptx1\n"); exit (0);
- }
- printf ("i386-sequent-ptx\n"); exit (0);
-
-#endif
-
-#if defined (vax)
-# if !defined (ultrix)
-# include <sys/param.h>
-# if defined (BSD)
-# if BSD == 43
- printf ("vax-dec-bsd4.3\n"); exit (0);
-# else
-# if BSD == 199006
- printf ("vax-dec-bsd4.3reno\n"); exit (0);
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# endif
-# else
- printf ("vax-dec-bsd\n"); exit (0);
-# endif
-# else
- printf ("vax-dec-ultrix\n"); exit (0);
-# endif
-#endif
-
-#if defined (alliant) && defined (i860)
- printf ("i860-alliant-bsd\n"); exit (0);
-#endif
-
- exit (1);
-}
-EOF
-
-$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` &&
- { echo "$SYSTEM_NAME"; exit; }
-
-# Apollos put the system type in the environment.
-
-test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; }
-
-# Convex versions that predate uname can use getsysinfo(1)
-
-if [ -x /usr/convex/getsysinfo ]
-then
- case `getsysinfo -f cpu_type` in
- c1*)
- echo c1-convex-bsd
- exit ;;
- c2*)
- if getsysinfo -f scalar_acc
- then echo c32-convex-bsd
- else echo c2-convex-bsd
- fi
+ x86_64:VMkernel:*:*)
+ echo ${UNAME_MACHINE}-unknown-esx
exit ;;
- c34*)
- echo c34-convex-bsd
- exit ;;
- c38*)
- echo c38-convex-bsd
+ amd64:Isilon\ OneFS:*:*)
+ echo x86_64-unknown-onefs
exit ;;
- c4*)
- echo c4-convex-bsd
- exit ;;
- esac
-fi
+esac
cat >&2 <<EOF
$0: unable to guess system type
-This script, last modified $timestamp, has failed to recognize
-the operating system you are using. It is advised that you
-download the most up to date version of the config scripts from
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite
+config.guess and config.sub with the latest versions from:
- http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess;hb=HEAD
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
and
- http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+ http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
-If the version you run ($0) is already up to date, please
-send the following data and any information you think might be
-pertinent to <config-patches@gnu.org> in order to provide the needed
-information to handle your system.
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
config.guess timestamp = $timestamp
diff --git a/lib/libedit/config.h.in b/lib/libedit/config.h.in
index df59ac31796d..ba283cc39f52 100644
--- a/lib/libedit/config.h.in
+++ b/lib/libedit/config.h.in
@@ -1,23 +1,5 @@
/* config.h.in. Generated from configure.ac by autoheader. */
-/* Define to 1 if the `closedir' function returns void instead of `int'. */
-#undef CLOSEDIR_VOID
-
-/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP
- systems. This function is required for `alloca.c' support on those systems.
- */
-#undef CRAY_STACKSEG_END
-
-/* Define to 1 if using `alloca.c'. */
-#undef C_ALLOCA
-
-/* Define to 1 if you have `alloca', as a function or macro. */
-#undef HAVE_ALLOCA
-
-/* Define to 1 if you have <alloca.h> and it should be used (not on Ultrix).
- */
-#undef HAVE_ALLOCA_H
-
/* Define to 1 if you have the <curses.h> header file. */
#undef HAVE_CURSES_H
@@ -28,15 +10,15 @@
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
-/* Define to 1 if you have the `endpwent' function. */
-#undef HAVE_ENDPWENT
-
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the `fork' function. */
#undef HAVE_FORK
+/* Define to 1 if you have the `getline' function. */
+#undef HAVE_GETLINE
+
/* Define to 1 if you have getpwnam_r and getpwuid_r that are draft POSIX.1
versions. */
#undef HAVE_GETPW_R_DRAFT
@@ -63,29 +45,17 @@
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
-/* Define to 1 if you have the <malloc.h> header file. */
-#undef HAVE_MALLOC_H
-
-/* Define to 1 if you have the `memchr' function. */
-#undef HAVE_MEMCHR
-
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
-/* Define to 1 if you have the `memset' function. */
-#undef HAVE_MEMSET
-
/* Define to 1 if you have the <ncurses.h> header file. */
#undef HAVE_NCURSES_H
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
-/* Define to 1 if you have the `regcomp' function. */
-#undef HAVE_REGCOMP
-
-/* Define to 1 if you have the `re_comp' function. */
-#undef HAVE_RE_COMP
+/* Define to 1 if if your system has SIZE_MAX */
+#undef HAVE_SIZE_MAX
/* Define to 1 if `stat' has the bug that it succeeds when given the
zero-length file name argument. */
@@ -97,35 +67,14 @@
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
-/* Define to 1 if you have the `strcasecmp' function. */
-#undef HAVE_STRCASECMP
-
-/* Define to 1 if you have the `strchr' function. */
-#undef HAVE_STRCHR
-
-/* Define to 1 if you have the `strcspn' function. */
-#undef HAVE_STRCSPN
-
-/* Define to 1 if you have the `strdup' function. */
-#undef HAVE_STRDUP
-
-/* Define to 1 if you have the `strerror' function. */
-#undef HAVE_STRERROR
-
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
-/* Define to 1 if you have the `strrchr' function. */
-#undef HAVE_STRRCHR
-
-/* Define to 1 if you have the `strstr' function. */
-#undef HAVE_STRSTR
-
-/* Define to 1 if you have the `strtol' function. */
-#undef HAVE_STRTOL
+/* Define to 1 if struct dirent has member d_namlen */
+#undef HAVE_STRUCT_DIRENT_D_NAMLEN
/* Define to 1 if you have the <sys/cdefs.h> header file. */
#undef HAVE_SYS_CDEFS_H
@@ -181,8 +130,7 @@
slash. */
#undef LSTAT_FOLLOWS_SLASHED_SYMLINK
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
- */
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Name of package */
@@ -209,14 +157,6 @@
/* Define as the return type of signal handlers (`int' or `void'). */
#undef RETSIGTYPE
-/* If using the C implementation of alloca, define if you know the
- direction of stack growth for your system; otherwise it will be
- automatically deduced at runtime.
- STACK_DIRECTION > 0 => grows toward higher addresses
- STACK_DIRECTION < 0 => grows toward lower addresses
- STACK_DIRECTION = 0 => direction of growth unknown */
-#undef STACK_DIRECTION
-
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
@@ -245,9 +185,6 @@
/* Version number of package */
#undef VERSION
-/* Define to 1 if you want wide-character code */
-#undef WIDECHAR
-
/* Define to 1 if on MINIX. */
#undef _MINIX
diff --git a/lib/libedit/config.sub b/lib/libedit/config.sub
index 2a55a50751c1..40ea5dfe1152 100755
--- a/lib/libedit/config.sub
+++ b/lib/libedit/config.sub
@@ -1,38 +1,31 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-# 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
-# Free Software Foundation, Inc.
+# Copyright 1992-2017 Free Software Foundation, Inc.
-timestamp='2009-11-20'
+timestamp='2017-04-02'
-# This file is (in principle) common to ALL GNU software.
-# The presence of a machine in this file suggests that SOME GNU software
-# can handle that machine. It does not imply ALL GNU software can.
-#
-# This file 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 of the License, or
+# This file 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.
+# 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, write to the Free Software
-# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA
-# 02110-1301, USA.
+# 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.
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
-# Please send patches to <config-patches@gnu.org>. Submit a context
-# diff and a properly formatted GNU ChangeLog entry.
+# Please send patches to <config-patches@gnu.org>.
#
# Configuration subroutine to validate and canonicalize a configuration type.
# Supply the specified configuration type as an argument.
@@ -40,7 +33,7 @@ timestamp='2009-11-20'
# Otherwise, we print the canonical config type on stdout and succeed.
# You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub;hb=HEAD
+# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
# This file is supposed to be the same for all GNU packages
# and recognize all the CPU types, system types and aliases
@@ -60,8 +53,7 @@ timestamp='2009-11-20'
me=`echo "$0" | sed -e 's,.*/,,'`
usage="\
-Usage: $0 [OPTION] CPU-MFR-OPSYS
- $0 [OPTION] ALIAS
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
Canonicalize a configuration name.
@@ -75,8 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
-Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+Copyright 1992-2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -123,13 +114,18 @@ esac
# Here we must recognize all the valid KERNEL-OS combinations.
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
case $maybe_os in
- nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \
- uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \
- kopensolaris*-gnu* | \
+ nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
+ linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
+ knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
+ kopensolaris*-gnu* | cloudabi*-eabi* | \
storm-chaos* | os2-emx* | rtmk-nova*)
os=-$maybe_os
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
;;
+ android-linux)
+ os=-linux-android
+ basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
+ ;;
*)
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
if [ $basic_machine != $1 ]
@@ -152,12 +148,12 @@ case $os in
-convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\
-c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \
-harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \
- -apple | -axis | -knuth | -cray | -microblaze)
+ -apple | -axis | -knuth | -cray | -microblaze*)
os=
basic_machine=$1
;;
- -bluegene*)
- os=-cnk
+ -bluegene*)
+ os=-cnk
;;
-sim | -cisco | -oki | -wec | -winbond)
os=
@@ -173,10 +169,10 @@ case $os in
os=-chorusos
basic_machine=$1
;;
- -chorusrdb)
- os=-chorusrdb
+ -chorusrdb)
+ os=-chorusrdb
basic_machine=$1
- ;;
+ ;;
-hiux*)
os=-hiuxwe2
;;
@@ -221,6 +217,12 @@ case $os in
-isc*)
basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
;;
+ -lynx*178)
+ os=-lynxos178
+ ;;
+ -lynx*5)
+ os=-lynxos5
+ ;;
-lynx*)
os=-lynxos
;;
@@ -245,20 +247,29 @@ case $basic_machine in
# Some are omitted here because they have special meanings below.
1750a | 580 \
| a29k \
+ | aarch64 | aarch64_be \
| alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
| alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
| am33_2.0 \
- | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \
+ | arc | arceb \
+ | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
+ | avr | avr32 \
+ | ba \
+ | be32 | be64 \
| bfin \
- | c4x | clipper \
+ | c4x | c8051 | clipper \
| d10v | d30v | dlx | dsp16xx \
- | fido | fr30 | frv \
+ | e2k | epiphany \
+ | fido | fr30 | frv | ft32 \
| h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
- | i370 | i860 | i960 | ia64 \
+ | hexagon \
+ | i370 | i860 | i960 | ia16 | ia64 \
| ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
| lm32 \
| m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | mcore | mep | metag \
+ | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
| mips | mipsbe | mipseb | mipsel | mipsle \
| mips16 \
| mips64 | mips64el \
@@ -272,38 +283,57 @@ case $basic_machine in
| mips64vr5900 | mips64vr5900el \
| mipsisa32 | mipsisa32el \
| mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
| mipsisa64 | mipsisa64el \
| mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
| mipsisa64sb1 | mipsisa64sb1el \
| mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
| mipstx39 | mipstx39el \
| mn10200 | mn10300 \
| moxie \
| mt \
| msp430 \
- | nios | nios2 \
+ | nds32 | nds32le | nds32be \
+ | nios | nios2 | nios2eb | nios2el \
| ns16k | ns32k \
- | or32 \
+ | open8 | or1k | or1knd | or32 \
| pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \
+ | powerpc | powerpc64 | powerpc64le | powerpcle \
+ | pru \
| pyramid \
- | rx \
+ | riscv32 | riscv64 \
+ | rl78 | rx \
| score \
- | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
+ | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
| sh64 | sh64le \
| sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
| sparcv8 | sparcv9 | sparcv9b | sparcv9v \
- | spu | strongarm \
- | tahoe | thumb | tic4x | tic80 | tron \
+ | spu \
+ | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
| ubicom32 \
- | v850 | v850e \
+ | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
+ | visium \
+ | wasm32 \
| we32k \
- | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \
+ | x86 | xc16x | xstormy16 | xtensa \
| z8k | z80)
basic_machine=$basic_machine-unknown
;;
- m6811 | m68hc11 | m6812 | m68hc12 | picochip)
- # Motorola 68HC11/12.
+ c54x)
+ basic_machine=tic54x-unknown
+ ;;
+ c55x)
+ basic_machine=tic55x-unknown
+ ;;
+ c6x)
+ basic_machine=tic6x-unknown
+ ;;
+ leon|leon[3-9])
+ basic_machine=sparc-$basic_machine
+ ;;
+ m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
basic_machine=$basic_machine-unknown
os=-none
;;
@@ -313,6 +343,21 @@ case $basic_machine in
basic_machine=mt-unknown
;;
+ strongarm | thumb | xscale)
+ basic_machine=arm-unknown
+ ;;
+ xgate)
+ basic_machine=$basic_machine-unknown
+ os=-none
+ ;;
+ xscaleeb)
+ basic_machine=armeb-unknown
+ ;;
+
+ xscaleel)
+ basic_machine=armel-unknown
+ ;;
+
# We use `pc' rather than `unknown'
# because (1) that's what they normally are, and
# (2) the word "unknown" tends to confuse beginning users.
@@ -327,25 +372,32 @@ case $basic_machine in
# Recognize the basic CPU types with company name.
580-* \
| a29k-* \
+ | aarch64-* | aarch64_be-* \
| alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
| alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
- | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \
+ | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
| arm-* | armbe-* | armle-* | armeb-* | armv*-* \
| avr-* | avr32-* \
+ | ba-* \
+ | be32-* | be64-* \
| bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \
- | clipper-* | craynv-* | cydra-* \
+ | c[123]* | c30-* | [cjt]90-* | c4x-* \
+ | c8051-* | clipper-* | craynv-* | cydra-* \
| d10v-* | d30v-* | dlx-* \
- | elxsi-* \
+ | e2k-* | elxsi-* \
| f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
| h8300-* | h8500-* \
| hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
- | i*86-* | i860-* | i960-* | ia64-* \
+ | hexagon-* \
+ | i*86-* | i860-* | i960-* | ia16-* | ia64-* \
| ip2k-* | iq2000-* \
+ | k1om-* \
+ | le32-* | le64-* \
| lm32-* \
| m32c-* | m32r-* | m32rle-* \
| m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* | metag-* | microblaze-* \
+ | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
+ | microblaze-* | microblazeel-* \
| mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
| mips16-* \
| mips64-* | mips64el-* \
@@ -359,33 +411,45 @@ case $basic_machine in
| mips64vr5900-* | mips64vr5900el-* \
| mipsisa32-* | mipsisa32el-* \
| mipsisa32r2-* | mipsisa32r2el-* \
+ | mipsisa32r6-* | mipsisa32r6el-* \
| mipsisa64-* | mipsisa64el-* \
| mipsisa64r2-* | mipsisa64r2el-* \
+ | mipsisa64r6-* | mipsisa64r6el-* \
| mipsisa64sb1-* | mipsisa64sb1el-* \
| mipsisa64sr71k-* | mipsisa64sr71kel-* \
+ | mipsr5900-* | mipsr5900el-* \
| mipstx39-* | mipstx39el-* \
| mmix-* \
| mt-* \
| msp430-* \
- | nios-* | nios2-* \
+ | nds32-* | nds32le-* | nds32be-* \
+ | nios-* | nios2-* | nios2eb-* | nios2el-* \
| none-* | np1-* | ns16k-* | ns32k-* \
+ | open8-* \
+ | or1k*-* \
| orion-* \
| pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \
+ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
+ | pru-* \
| pyramid-* \
- | romp-* | rs6000-* | rx-* \
+ | riscv32-* | riscv64-* \
+ | rl78-* | romp-* | rs6000-* | rx-* \
| sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
| shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
| sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
| sparclite-* \
- | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \
- | tahoe-* | thumb-* \
- | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* | tile-* \
+ | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
+ | tahoe-* \
+ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
+ | tile*-* \
| tron-* \
| ubicom32-* \
- | v850-* | v850e-* | vax-* \
+ | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
+ | vax-* \
+ | visium-* \
+ | wasm32-* \
| we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \
+ | x86-* | x86_64-* | xc16x-* | xps100-* \
| xstormy16-* | xtensa*-* \
| ymp-* \
| z8k-* | z80-*)
@@ -410,7 +474,7 @@ case $basic_machine in
basic_machine=a29k-amd
os=-udi
;;
- abacus)
+ abacus)
basic_machine=abacus-unknown
;;
adobe68k)
@@ -460,6 +524,9 @@ case $basic_machine in
basic_machine=i386-pc
os=-aros
;;
+ asmjs)
+ basic_machine=asmjs-unknown
+ ;;
aux)
basic_machine=m68k-apple
os=-aux
@@ -480,11 +547,20 @@ case $basic_machine in
basic_machine=powerpc-ibm
os=-cnk
;;
+ c54x-*)
+ basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c55x-*)
+ basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
+ c6x-*)
+ basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
c90)
basic_machine=c90-cray
os=-unicos
;;
- cegcc)
+ cegcc)
basic_machine=arm-unknown
os=-cegcc
;;
@@ -516,7 +592,7 @@ case $basic_machine in
basic_machine=craynv-cray
os=-unicosmp
;;
- cr16)
+ cr16 | cr16-*)
basic_machine=cr16-unknown
os=-elf
;;
@@ -571,6 +647,14 @@ case $basic_machine in
basic_machine=m68k-bull
os=-sysv3
;;
+ e500v[12])
+ basic_machine=powerpc-unknown
+ os=$os"spe"
+ ;;
+ e500v[12]-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ os=$os"spe"
+ ;;
ebmon29k)
basic_machine=a29k-amd
os=-ebmon
@@ -674,7 +758,6 @@ case $basic_machine in
i370-ibm* | ibm*)
basic_machine=i370-ibm
;;
-# I'm not sure what "Sysv32" means. Should this be sysv3.2?
i*86v32)
basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
os=-sysv32
@@ -713,6 +796,9 @@ case $basic_machine in
basic_machine=m68k-isi
os=-sysv
;;
+ leon-*|leon[3-9]-*)
+ basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
+ ;;
m68knommu)
basic_machine=m68k-unknown
os=-linux
@@ -732,11 +818,15 @@ case $basic_machine in
basic_machine=ns32k-utek
os=-sysv
;;
- microblaze)
+ microblaze*)
basic_machine=microblaze-xilinx
;;
+ mingw64)
+ basic_machine=x86_64-pc
+ os=-mingw64
+ ;;
mingw32)
- basic_machine=i386-pc
+ basic_machine=i686-pc
os=-mingw32
;;
mingw32ce)
@@ -764,6 +854,10 @@ case $basic_machine in
basic_machine=powerpc-unknown
os=-morphos
;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ os=-moxiebox
+ ;;
msdos)
basic_machine=i386-pc
os=-msdos
@@ -771,10 +865,18 @@ case $basic_machine in
ms1-*)
basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
;;
+ msys)
+ basic_machine=i686-pc
+ os=-msys
+ ;;
mvs)
basic_machine=i370-ibm
os=-mvs
;;
+ nacl)
+ basic_machine=le32-unknown
+ os=-nacl
+ ;;
ncr3000)
basic_machine=i486-ncr
os=-sysv4
@@ -839,9 +941,18 @@ case $basic_machine in
np1)
basic_machine=np1-gould
;;
+ neo-tandem)
+ basic_machine=neo-tandem
+ ;;
+ nse-tandem)
+ basic_machine=nse-tandem
+ ;;
nsr-tandem)
basic_machine=nsr-tandem
;;
+ nsx-tandem)
+ basic_machine=nsx-tandem
+ ;;
op50n-* | op60c-*)
basic_machine=hppa1.1-oki
os=-proelf
@@ -921,11 +1032,12 @@ case $basic_machine in
;;
power) basic_machine=power-ibm
;;
- ppc) basic_machine=powerpc-unknown
+ ppc | ppcbe) basic_machine=powerpc-unknown
;;
- ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ppc-* | ppcbe-*)
+ basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
- ppcle | powerpclittle | ppc-le | powerpc-little)
+ ppcle | powerpclittle)
basic_machine=powerpcle-unknown
;;
ppcle-* | powerpclittle-*)
@@ -935,7 +1047,7 @@ case $basic_machine in
;;
ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
;;
- ppc64le | powerpc64little | ppc64-le | powerpc64-little)
+ ppc64le | powerpc64little)
basic_machine=powerpc64le-unknown
;;
ppc64le-* | powerpc64little-*)
@@ -948,7 +1060,11 @@ case $basic_machine in
basic_machine=i586-unknown
os=-pw32
;;
- rdos)
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ os=-rdos
+ ;;
+ rdos32)
basic_machine=i386-pc
os=-rdos
;;
@@ -1017,6 +1133,9 @@ case $basic_machine in
basic_machine=i860-stratus
os=-sysv4
;;
+ strongarm-* | thumb-*)
+ basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ ;;
sun2)
basic_machine=m68000-sun
;;
@@ -1073,20 +1192,8 @@ case $basic_machine in
basic_machine=t90-cray
os=-unicos
;;
- tic54x | c54x*)
- basic_machine=tic54x-unknown
- os=-coff
- ;;
- tic55x | c55x*)
- basic_machine=tic55x-unknown
- os=-coff
- ;;
- tic6x | c6x*)
- basic_machine=tic6x-unknown
- os=-coff
- ;;
tile*)
- basic_machine=tile-unknown
+ basic_machine=$basic_machine-unknown
os=-linux-gnu
;;
tx39)
@@ -1141,6 +1248,9 @@ case $basic_machine in
basic_machine=a29k-wrs
os=-vxworks
;;
+ wasm32)
+ basic_machine=wasm32-unknown
+ ;;
w65*)
basic_machine=w65-wdc
os=-none
@@ -1156,6 +1266,9 @@ case $basic_machine in
xps | xps100)
basic_machine=xps100-honeywell
;;
+ xscale-* | xscalee[bl]-*)
+ basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ;;
ymp)
basic_machine=ymp-cray
os=-unicos
@@ -1253,11 +1366,11 @@ esac
if [ x"$os" != x"" ]
then
case $os in
- # First match some system type aliases
- # that might get confused with valid system types.
+ # First match some system type aliases
+ # that might get confused with valid system types.
# -solaris* is a basic system type, with this one exception.
- -auroraux)
- os=-auroraux
+ -auroraux)
+ os=-auroraux
;;
-solaris1 | -solaris1.*)
os=`echo $os | sed -e 's|solaris1|sunos4|'`
@@ -1281,28 +1394,30 @@ case $os in
-gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
| -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
| -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
- | -sym* | -kopensolaris* \
+ | -sym* | -kopensolaris* | -plan9* \
| -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* | -aros* \
+ | -aos* | -aros* | -cloudabi* | -sortix* \
| -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
| -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
| -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -openbsd* | -solidbsd* \
+ | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
| -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
| -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
| -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
- | -chorusos* | -chorusrdb* | -cegcc* \
- | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
- | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \
- | -uxpv* | -beos* | -mpeix* | -udk* \
+ | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \
+ | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
+ | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
+ | -linux-newlib* | -linux-musl* | -linux-uclibc* \
+ | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
| -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
| -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
| -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
| -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
| -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es*)
+ | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
+ | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*)
# Remember, each alternative MUST END IN *, to match a version number.
;;
-qnx*)
@@ -1341,7 +1456,7 @@ case $os in
-opened*)
os=-openedition
;;
- -os400*)
+ -os400*)
os=-os400
;;
-wince*)
@@ -1390,7 +1505,7 @@ case $os in
-sinix*)
os=-sysv4
;;
- -tpf*)
+ -tpf*)
os=-tpf
;;
-triton*)
@@ -1426,15 +1541,16 @@ case $os in
-aros*)
os=-aros
;;
- -kaos*)
- os=-kaos
- ;;
-zvmoe)
os=-zvmoe
;;
-dicos*)
os=-dicos
;;
+ -nacl*)
+ ;;
+ -ios)
+ ;;
-none)
;;
*)
@@ -1457,10 +1573,10 @@ else
# system, and we'll never get to this point.
case $basic_machine in
- score-*)
+ score-*)
os=-elf
;;
- spu-*)
+ spu-*)
os=-elf
;;
*-acorn)
@@ -1472,8 +1588,23 @@ case $basic_machine in
arm*-semi)
os=-aout
;;
- c4x-* | tic4x-*)
- os=-coff
+ c4x-* | tic4x-*)
+ os=-coff
+ ;;
+ c8051-*)
+ os=-elf
+ ;;
+ hexagon-*)
+ os=-elf
+ ;;
+ tic54x-*)
+ os=-coff
+ ;;
+ tic55x-*)
+ os=-coff
+ ;;
+ tic6x-*)
+ os=-coff
;;
# This must come before the *-dec entry.
pdp10-*)
@@ -1493,14 +1624,11 @@ case $basic_machine in
;;
m68000-sun)
os=-sunos3
- # This also exists in the configure program, but was not the
- # default.
- # os=-sunos4
;;
m68*-cisco)
os=-aout
;;
- mep-*)
+ mep-*)
os=-elf
;;
mips*-cisco)
@@ -1518,6 +1646,9 @@ case $basic_machine in
sparc-* | *-sun)
os=-sunos4.1.1
;;
+ pru-*)
+ os=-elf
+ ;;
*-be)
os=-beos
;;
@@ -1527,7 +1658,7 @@ case $basic_machine in
*-ibm)
os=-aix
;;
- *-knuth)
+ *-knuth)
os=-mmixware
;;
*-wec)
diff --git a/lib/libedit/configure b/lib/libedit/configure
index c069ba40fe8b..4bc18e334016 100755
--- a/lib/libedit/configure
+++ b/lib/libedit/configure
@@ -1,11 +1,9 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.65 for libedit 3.0.
+# Generated by GNU Autoconf 2.69 for libedit 3.1.
#
#
-# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,
-# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
#
#
# This configure script is free software; the Free Software Foundation
@@ -89,6 +87,7 @@ fi
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -133,6 +132,31 @@ export LANGUAGE
# CDPATH.
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+# Use a proper internal environment variable to ensure we don't fall
+ # into an infinite loop, continuously re-executing ourselves.
+ if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then
+ _as_can_reexec=no; export _as_can_reexec;
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+as_fn_exit 255
+ fi
+ # We don't want this to propagate to other subprocesses.
+ { _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
@@ -166,7 +190,8 @@ if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
else
exitcode=1; echo positional parameters were not saved.
fi
-test x\$exitcode = x0 || exit 1"
+test x\$exitcode = x0 || exit 1
+test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
@@ -219,14 +244,25 @@ IFS=$as_save_IFS
if test "x$CONFIG_SHELL" != x; then :
- # We cannot yet assume a decent shell, so we have to provide a
- # neutralization value for shells without unset; and this also
- # works around shells that cannot unset nonexistent variables.
- BASH_ENV=/dev/null
- ENV=/dev/null
- (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
- export CONFIG_SHELL
- exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}
+ export CONFIG_SHELL
+ # We cannot yet assume a decent shell, so we have to provide a
+# neutralization value for shells without unset; and this also
+# works around shells that cannot unset nonexistent variables.
+# Preserve -v and -x to the replacement shell.
+BASH_ENV=/dev/null
+ENV=/dev/null
+(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV
+case $- in # ((((
+ *v*x* | *x*v* ) as_opts=-vx ;;
+ *v* ) as_opts=-v ;;
+ *x* ) as_opts=-x ;;
+ * ) as_opts= ;;
+esac
+exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
+# Admittedly, this is quite paranoid, since all the known shells bail
+# out after a failed `exec'.
+$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+exit 255
fi
if test x$as_have_required = xno; then :
@@ -324,10 +360,18 @@ $as_echo X"$as_dir" |
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
- } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
@@ -364,19 +408,19 @@ else
fi # as_fn_arith
-# as_fn_error ERROR [LINENO LOG_FD]
-# ---------------------------------
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
-# script with status $?, using 1 if that was 0.
+# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
- as_status=$?; test $as_status -eq 0 && as_status=1
- if test "$3"; then
- as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $1" >&2
+ $as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
@@ -449,6 +493,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
chmod +x "$as_me.lineno" ||
{ $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+ # If we had to re-execute with $CONFIG_SHELL, we're ensured to have
+ # already done that, so ensure we don't try to do so again and fall
+ # in an infinite loop. This has already happened in practice.
+ _as_can_reexec=no; export _as_can_reexec
# Don't try to exec as it changes $[0], causing all sort of problems
# (the dirname of $[0] is not the place where we might find the
# original and so on. Autoconf is especially sensitive to this).
@@ -483,16 +531,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -504,28 +552,8 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -540,7 +568,7 @@ test -n "$DJDIR" || exec 7<&0 </dev/null
exec 6>&1
# Name of the host.
-# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,
+# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status,
# so uname gets run too.
ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`
@@ -558,13 +586,13 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='libedit'
-PACKAGE_TARNAME='libedit-20120111'
-PACKAGE_VERSION='3.0'
-PACKAGE_STRING='libedit 3.0'
+PACKAGE_TARNAME='libedit-20171208'
+PACKAGE_VERSION='3.1'
+PACKAGE_STRING='libedit 3.1'
PACKAGE_BUGREPORT=''
PACKAGE_URL=''
-ac_unique_file="src/strlcat.c"
+ac_unique_file="src/el.c"
# Factoring default headers for most tests.
ac_includes_default="\
#include <stdio.h>
@@ -605,14 +633,10 @@ ac_subst_vars='am__EXEEXT_FALSE
am__EXEEXT_TRUE
LTLIBOBJS
LIBOBJS
-ALLOCA
-WIDECHAR_FALSE
-WIDECHAR_TRUE
MANTYPE
NROFF
-AM_BACKSLASH
-AM_DEFAULT_VERBOSITY
LT_VERSION
+LT_SYS_LIBRARY_PATH
OTOOL64
OTOOL
LIPO
@@ -640,9 +664,14 @@ build_vendor
build_cpu
build
LIBTOOL
+AM_BACKSLASH
+AM_DEFAULT_VERBOSITY
+AM_DEFAULT_V
+AM_V
am__fastdepCC_FALSE
am__fastdepCC_TRUE
CCDEPMODE
+am__nodep
AMDEPBACKSLASH
AMDEP_FALSE
AMDEP_TRUE
@@ -724,15 +753,15 @@ ac_subst_files=''
ac_user_opts='
enable_option_checking
enable_dependency_tracking
+enable_silent_rules
enable_shared
enable_static
with_pic
enable_fast_install
+with_aix_soname
with_gnu_ld
with_sysroot
enable_libtool_lock
-enable_silent_rules
-enable_widec
'
ac_precious_vars='build_alias
host_alias
@@ -742,7 +771,8 @@ CFLAGS
LDFLAGS
LIBS
CPPFLAGS
-CPP'
+CPP
+LT_SYS_LIBRARY_PATH'
# Initialize some variables set by options.
@@ -805,8 +835,9 @@ do
fi
case $ac_option in
- *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
- *) ac_optarg=yes ;;
+ *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;
+ *=) ac_optarg= ;;
+ *) ac_optarg=yes ;;
esac
# Accept the important Cygnus configure options, so we can diagnose typos.
@@ -851,7 +882,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid feature name: $ac_useropt"
+ as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -877,7 +908,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid feature name: $ac_useropt"
+ as_fn_error $? "invalid feature name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1081,7 +1112,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid package name: $ac_useropt"
+ as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1097,7 +1128,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error "invalid package name: $ac_useropt"
+ as_fn_error $? "invalid package name: $ac_useropt"
ac_useropt_orig=$ac_useropt
ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1127,8 +1158,8 @@ do
| --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
x_libraries=$ac_optarg ;;
- -*) as_fn_error "unrecognized option: \`$ac_option'
-Try \`$0 --help' for more information."
+ -*) as_fn_error $? "unrecognized option: \`$ac_option'
+Try \`$0 --help' for more information"
;;
*=*)
@@ -1136,7 +1167,7 @@ Try \`$0 --help' for more information."
# Reject names that are not valid shell variable names.
case $ac_envvar in #(
'' | [0-9]* | *[!_$as_cr_alnum]* )
- as_fn_error "invalid variable name: \`$ac_envvar'" ;;
+ as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
esac
eval $ac_envvar=\$ac_optarg
export $ac_envvar ;;
@@ -1146,7 +1177,7 @@ Try \`$0 --help' for more information."
$as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
$as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
- : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}
+ : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
;;
esac
@@ -1154,13 +1185,13 @@ done
if test -n "$ac_prev"; then
ac_option=--`echo $ac_prev | sed 's/_/-/g'`
- as_fn_error "missing argument to $ac_option"
+ as_fn_error $? "missing argument to $ac_option"
fi
if test -n "$ac_unrecognized_opts"; then
case $enable_option_checking in
no) ;;
- fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;;
+ fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
*) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
esac
fi
@@ -1183,7 +1214,7 @@ do
[\\/$]* | ?:[\\/]* ) continue;;
NONE | '' ) case $ac_var in *prefix ) continue;; esac;;
esac
- as_fn_error "expected an absolute directory name for --$ac_var: $ac_val"
+ as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
done
# There might be people who depend on the old broken behavior: `$host'
@@ -1197,8 +1228,6 @@ target=$target_alias
if test "x$host_alias" != x; then
if test "x$build_alias" = x; then
cross_compiling=maybe
- $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.
- If a cross compiler is detected then cross compile mode will be used." >&2
elif test "x$build_alias" != "x$host_alias"; then
cross_compiling=yes
fi
@@ -1213,9 +1242,9 @@ test "$silent" = yes && exec 6>/dev/null
ac_pwd=`pwd` && test -n "$ac_pwd" &&
ac_ls_di=`ls -di .` &&
ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||
- as_fn_error "working directory cannot be determined"
+ as_fn_error $? "working directory cannot be determined"
test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||
- as_fn_error "pwd does not report name of working directory"
+ as_fn_error $? "pwd does not report name of working directory"
# Find the source files, if location was not specified.
@@ -1254,11 +1283,11 @@ else
fi
if test ! -r "$srcdir/$ac_unique_file"; then
test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
- as_fn_error "cannot find sources ($ac_unique_file) in $srcdir"
+ as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
fi
ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
ac_abs_confdir=`(
- cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg"
+ cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
pwd)`
# When building in place, set srcdir=.
if test "$ac_abs_confdir" = "$ac_pwd"; then
@@ -1284,7 +1313,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures libedit 3.0 to adapt to many kinds of systems.
+\`configure' configures libedit 3.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1298,7 +1327,7 @@ Configuration:
--help=short display options specific to this package
--help=recursive display the short help of all the included packages
-V, --version display version information and exit
- -q, --quiet, --silent do not print \`checking...' messages
+ -q, --quiet, --silent do not print \`checking ...' messages
--cache-file=FILE cache test results in FILE [disabled]
-C, --config-cache alias for \`--cache-file=config.cache'
-n, --no-create do not create output files
@@ -1333,7 +1362,7 @@ Fine tuning of the installation directories:
--localedir=DIR locale-dependent data [DATAROOTDIR/locale]
--mandir=DIR man documentation [DATAROOTDIR/man]
--docdir=DIR documentation root
- [DATAROOTDIR/doc/libedit-20120111]
+ [DATAROOTDIR/doc/libedit-20171208]
--htmldir=DIR html documentation [DOCDIR]
--dvidir=DIR dvi documentation [DOCDIR]
--pdfdir=DIR pdf documentation [DOCDIR]
@@ -1355,7 +1384,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of libedit 3.0:";;
+ short | recursive ) echo "Configuration of libedit 3.1:";;
esac
cat <<\_ACEOF
@@ -1363,25 +1392,29 @@ Optional Features:
--disable-option-checking ignore unrecognized --enable/--with options
--disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
- --disable-dependency-tracking speeds up one-time build
- --enable-dependency-tracking do not reject slow dependency extractors
+ --enable-dependency-tracking
+ do not reject slow dependency extractors
+ --disable-dependency-tracking
+ speeds up one-time build
+ --enable-silent-rules less verbose build output (undo: "make V=1")
+ --disable-silent-rules verbose build output (undo: "make V=0")
--enable-shared[=PKGS] build shared libraries [default=yes]
--enable-static[=PKGS] build static libraries [default=yes]
--enable-fast-install[=PKGS]
optimize for fast installation [default=yes]
--disable-libtool-lock avoid locking (might break parallel builds)
- --enable-silent-rules less verbose build output (undo: `make V=1')
- --disable-silent-rules verbose build output (undo: `make V=0')
- --enable-widec compile with wide-char/UTF-8 code
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
--without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
- --with-pic try to use only PIC/non-PIC objects [default=use
+ --with-pic[=PKGS] try to use only PIC/non-PIC objects [default=use
both]
+ --with-aix-soname=aix|svr4|both
+ shared library versioning (aka "SONAME") variant to
+ provide on AIX, [default=aix].
--with-gnu-ld assume the C compiler uses GNU ld [default=no]
- --with-sysroot=DIR Search for dependent libraries within DIR
- (or the compiler's sysroot if not specified).
+ --with-sysroot[=DIR] Search for dependent libraries within DIR (or the
+ compiler's sysroot if not specified).
Some influential environment variables:
CC C compiler command
@@ -1392,6 +1425,8 @@ Some influential environment variables:
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
CPP C preprocessor
+ LT_SYS_LIBRARY_PATH
+ User-defined run-time library search path.
Use these variables to override the choices made by `configure' or to help
it to find libraries and programs with nonstandard names/locations.
@@ -1459,10 +1494,10 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-libedit configure 3.0
-generated by GNU Autoconf 2.65
+libedit configure 3.1
+generated by GNU Autoconf 2.69
-Copyright (C) 2009 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
@@ -1506,7 +1541,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_compile
@@ -1532,7 +1567,7 @@ $as_echo "$ac_try_echo"; } >&5
mv -f conftest.er1 conftest.err
fi
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } >/dev/null && {
+ test $ac_status = 0; } > conftest.i && {
test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
test ! -s conftest.err
}; then :
@@ -1543,7 +1578,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_cpp
@@ -1556,10 +1591,10 @@ fi
ac_fn_c_check_header_mongrel ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+ if eval \${$3+:} false; then :
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
fi
eval ac_res=\$$3
@@ -1595,7 +1630,7 @@ if ac_fn_c_try_cpp "$LINENO"; then :
else
ac_header_preproc=no
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
$as_echo "$ac_header_preproc" >&6; }
@@ -1622,7 +1657,7 @@ $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
eval "$3=\$ac_header_compiler"
@@ -1631,7 +1666,7 @@ eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
fi
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_mongrel
@@ -1672,7 +1707,7 @@ sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=$ac_status
fi
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_run
@@ -1686,7 +1721,7 @@ ac_fn_c_check_header_compile ()
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -1704,7 +1739,7 @@ fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
@@ -1735,7 +1770,7 @@ $as_echo "$ac_try_echo"; } >&5
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
- $as_test_x conftest$ac_exeext
+ test -x conftest$ac_exeext
}; then :
ac_retval=0
else
@@ -1749,7 +1784,7 @@ fi
# interfere with the next link command; also delete a directory that is
# left behind by Apple's compiler. We do this before executing the actions.
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
} # ac_fn_c_try_link
@@ -1762,7 +1797,7 @@ ac_fn_c_check_func ()
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -1817,7 +1852,7 @@ fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
@@ -1830,7 +1865,7 @@ ac_fn_c_check_type ()
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
$as_echo_n "checking for $2... " >&6; }
-if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$3+:} false; then :
$as_echo_n "(cached) " >&6
else
eval "$3=no"
@@ -1871,15 +1906,72 @@ fi
eval ac_res=\$$3
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
- eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;}
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_type
+
+# ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES
+# ----------------------------------------------------
+# Tries to find if the field MEMBER exists in type AGGR, after including
+# INCLUDES, setting cache variable VAR accordingly.
+ac_fn_c_check_member ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5
+$as_echo_n "checking for $2.$3... " >&6; }
+if eval \${$4+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$4=yes"
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$5
+int
+main ()
+{
+static $2 ac_aggr;
+if (sizeof ac_aggr.$3)
+return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ eval "$4=yes"
+else
+ eval "$4=no"
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+eval ac_res=\$$4
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_check_member
cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by libedit $as_me 3.0, which was
-generated by GNU Autoconf 2.65. Invocation command line was
+It was created by libedit $as_me 3.1, which was
+generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -1989,11 +2081,9 @@ trap 'exit_status=$?
{
echo
- cat <<\_ASBOX
-## ---------------- ##
+ $as_echo "## ---------------- ##
## Cache variables. ##
-## ---------------- ##
-_ASBOX
+## ---------------- ##"
echo
# The following way of writing the cache mishandles newlines in values,
(
@@ -2027,11 +2117,9 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
)
echo
- cat <<\_ASBOX
-## ----------------- ##
+ $as_echo "## ----------------- ##
## Output variables. ##
-## ----------------- ##
-_ASBOX
+## ----------------- ##"
echo
for ac_var in $ac_subst_vars
do
@@ -2044,11 +2132,9 @@ _ASBOX
echo
if test -n "$ac_subst_files"; then
- cat <<\_ASBOX
-## ------------------- ##
+ $as_echo "## ------------------- ##
## File substitutions. ##
-## ------------------- ##
-_ASBOX
+## ------------------- ##"
echo
for ac_var in $ac_subst_files
do
@@ -2062,11 +2148,9 @@ _ASBOX
fi
if test -s confdefs.h; then
- cat <<\_ASBOX
-## ----------- ##
+ $as_echo "## ----------- ##
## confdefs.h. ##
-## ----------- ##
-_ASBOX
+## ----------- ##"
echo
cat confdefs.h
echo
@@ -2121,7 +2205,12 @@ _ACEOF
ac_site_file1=NONE
ac_site_file2=NONE
if test -n "$CONFIG_SITE"; then
- ac_site_file1=$CONFIG_SITE
+ # We do not want a PATH search for config.site.
+ case $CONFIG_SITE in #((
+ -*) ac_site_file1=./$CONFIG_SITE;;
+ */*) ac_site_file1=$CONFIG_SITE;;
+ *) ac_site_file1=./$CONFIG_SITE;;
+ esac
elif test "x$prefix" != xNONE; then
ac_site_file1=$prefix/share/config.site
ac_site_file2=$prefix/etc/config.site
@@ -2136,7 +2225,11 @@ do
{ $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
$as_echo "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5
- . "$ac_site_file"
+ . "$ac_site_file" \
+ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
+$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+as_fn_error $? "failed to load site script $ac_site_file
+See \`config.log' for more details" "$LINENO" 5; }
fi
done
@@ -2212,7 +2305,7 @@ if $ac_cache_corrupted; then
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
{ $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
- as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
+ as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5
fi
## -------------------- ##
## Main body of script. ##
@@ -2230,6 +2323,38 @@ ac_config_headers="$ac_config_headers config.h"
# features of Posix that are extensions to C (define _GNU_SOURCE)
+ac_aux_dir=
+for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
+ if test -f "$ac_dir/install-sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f "$ac_dir/install.sh"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ elif test -f "$ac_dir/shtool"; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/shtool install -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
+fi
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
+ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
+ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
+
+
+# Expand $ac_aux_dir to an absolute path.
+am_aux_dir=`cd "$ac_aux_dir" && pwd`
+
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -2240,7 +2365,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2252,7 +2377,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2280,7 +2405,7 @@ if test -z "$ac_cv_prog_CC"; then
set dummy gcc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
@@ -2292,7 +2417,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2333,7 +2458,7 @@ if test -z "$CC"; then
set dummy ${ac_tool_prefix}cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2345,7 +2470,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2373,7 +2498,7 @@ if test -z "$CC"; then
set dummy cc; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2386,7 +2511,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
ac_prog_rejected=yes
continue
@@ -2432,7 +2557,7 @@ if test -z "$CC"; then
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_CC+set}" = set; then :
+if ${ac_cv_prog_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$CC"; then
@@ -2444,7 +2569,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2476,7 +2601,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_CC+set}" = set; then :
+if ${ac_cv_prog_ac_ct_CC+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_CC"; then
@@ -2488,7 +2613,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -2530,8 +2655,8 @@ fi
test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "no acceptable C compiler found in \$PATH
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "no acceptable C compiler found in \$PATH
+See \`config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
@@ -2645,9 +2770,8 @@ sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-{ as_fn_set_status 77
-as_fn_error "C compiler cannot create executables
-See \`config.log' for more details." "$LINENO" 5; }; }
+as_fn_error 77 "C compiler cannot create executables
+See \`config.log' for more details" "$LINENO" 5; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
@@ -2689,8 +2813,8 @@ done
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "cannot compute suffix of executables: cannot compile and link
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "cannot compute suffix of executables: cannot compile and link
+See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
@@ -2747,9 +2871,9 @@ $as_echo "$ac_try_echo"; } >&5
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "cannot run C compiled programs.
+as_fn_error $? "cannot run C compiled programs.
If you meant to cross compile, use \`--host'.
-See \`config.log' for more details." "$LINENO" 5; }
+See \`config.log' for more details" "$LINENO" 5; }
fi
fi
fi
@@ -2760,7 +2884,7 @@ rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
ac_clean_files=$ac_clean_files_save
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
$as_echo_n "checking for suffix of object files... " >&6; }
-if test "${ac_cv_objext+set}" = set; then :
+if ${ac_cv_objext+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -2800,8 +2924,8 @@ sed 's/^/| /' conftest.$ac_ext >&5
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "cannot compute suffix of object files: cannot compile
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "cannot compute suffix of object files: cannot compile
+See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
@@ -2811,7 +2935,7 @@ OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if test "${ac_cv_c_compiler_gnu+set}" = set; then :
+if ${ac_cv_c_compiler_gnu+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -2848,7 +2972,7 @@ ac_test_CFLAGS=${CFLAGS+set}
ac_save_CFLAGS=$CFLAGS
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if test "${ac_cv_prog_cc_g+set}" = set; then :
+if ${ac_cv_prog_cc_g+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_save_c_werror_flag=$ac_c_werror_flag
@@ -2926,7 +3050,7 @@ else
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if test "${ac_cv_prog_cc_c89+set}" = set; then :
+if ${ac_cv_prog_cc_c89+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c89=no
@@ -2935,8 +3059,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdarg.h>
#include <stdio.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+struct stat;
/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
struct buf { int x; };
FILE * (*rcsopen) (struct buf *, struct stat *, int);
@@ -3021,6 +3144,65 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5
+$as_echo_n "checking whether $CC understands -c and -o together... " >&6; }
+if ${am_cv_prog_cc_c_o+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main ()
+{
+
+ ;
+ return 0;
+}
+_ACEOF
+ # Make sure it works both with $CC and with simple cc.
+ # Following AC_PROG_CC_C_O, we do the test twice because some
+ # compilers refuse to overwrite an existing .o file with -o,
+ # though they will create one.
+ am_cv_prog_cc_c_o=yes
+ for am_i in 1 2; do
+ if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5
+ ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5
+ ac_status=$?
+ echo "$as_me:$LINENO: \$? = $ac_status" >&5
+ (exit $ac_status); } \
+ && test -f conftest2.$ac_objext; then
+ : OK
+ else
+ am_cv_prog_cc_c_o=no
+ break
+ fi
+ done
+ rm -f core conftest*
+ unset am_i
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5
+$as_echo "$am_cv_prog_cc_c_o" >&6; }
+if test "$am_cv_prog_cc_c_o" != yes; then
+ # Losing compiler, so override with the script.
+ # FIXME: It is wrong to rewrite CC.
+ # But if we don't then we get into trouble of one sort or another.
+ # A longer-term fix would be to have automake use am__CC in this case,
+ # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)"
+ CC="$am_aux_dir/compile $CC"
+fi
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
@@ -3034,7 +3216,7 @@ if test -n "$CPP" && test -d "$CPP"; then
CPP=
fi
if test -z "$CPP"; then
- if test "${ac_cv_prog_CPP+set}" = set; then :
+ if ${ac_cv_prog_CPP+:} false; then :
$as_echo_n "(cached) " >&6
else
# Double quotes because CPP needs to be expanded
@@ -3064,7 +3246,7 @@ else
# Broken: fails on valid input.
continue
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
@@ -3080,11 +3262,11 @@ else
ac_preproc_ok=:
break
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
break
fi
@@ -3123,7 +3305,7 @@ else
# Broken: fails on valid input.
continue
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
# OK, works on sane cases. Now check whether nonexistent headers
# can be detected and how.
@@ -3139,18 +3321,18 @@ else
ac_preproc_ok=:
break
fi
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.err conftest.i conftest.$ac_ext
done
# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.err conftest.$ac_ext
+rm -f conftest.i conftest.err conftest.$ac_ext
if $ac_preproc_ok; then :
else
{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error "C preprocessor \"$CPP\" fails sanity check
-See \`config.log' for more details." "$LINENO" 5; }
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See \`config.log' for more details" "$LINENO" 5; }
fi
ac_ext=c
@@ -3162,7 +3344,7 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
-if test "${ac_cv_path_GREP+set}" = set; then :
+if ${ac_cv_path_GREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$GREP"; then
@@ -3176,7 +3358,7 @@ do
for ac_prog in grep ggrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue
+ as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
# Check for GNU $ac_path_GREP
case `"$ac_path_GREP" --version 2>&1` in
@@ -3211,7 +3393,7 @@ esac
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_GREP"; then
- as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_GREP=$GREP
@@ -3225,7 +3407,7 @@ $as_echo "$ac_cv_path_GREP" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
$as_echo_n "checking for egrep... " >&6; }
-if test "${ac_cv_path_EGREP+set}" = set; then :
+if ${ac_cv_path_EGREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
@@ -3242,7 +3424,7 @@ do
for ac_prog in egrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue
+ as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
# Check for GNU $ac_path_EGREP
case `"$ac_path_EGREP" --version 2>&1` in
@@ -3277,7 +3459,7 @@ esac
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_EGREP"; then
- as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_EGREP=$EGREP
@@ -3292,7 +3474,7 @@ $as_echo "$ac_cv_path_EGREP" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
$as_echo_n "checking for ANSI C header files... " >&6; }
-if test "${ac_cv_header_stdc+set}" = set; then :
+if ${ac_cv_header_stdc+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -3409,8 +3591,7 @@ do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
"
-eval as_val=\$$as_ac_Header
- if test "x$as_val" = x""yes; then :
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
@@ -3422,7 +3603,7 @@ done
ac_fn_c_check_header_mongrel "$LINENO" "minix/config.h" "ac_cv_header_minix_config_h" "$ac_includes_default"
-if test "x$ac_cv_header_minix_config_h" = x""yes; then :
+if test "x$ac_cv_header_minix_config_h" = xyes; then :
MINIX=yes
else
MINIX=
@@ -3444,14 +3625,14 @@ $as_echo "#define _MINIX 1" >>confdefs.h
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether it is safe to define __EXTENSIONS__" >&5
$as_echo_n "checking whether it is safe to define __EXTENSIONS__... " >&6; }
-if test "${ac_cv_safe_to_define___extensions__+set}" = set; then :
+if ${ac_cv_safe_to_define___extensions__+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-# define __EXTENSIONS__ 1
- $ac_includes_default
+# define __EXTENSIONS__ 1
+ $ac_includes_default
int
main ()
{
@@ -3482,30 +3663,7 @@ $as_echo "$ac_cv_safe_to_define___extensions__" >&6; }
-am__api_version='1.11'
-
-ac_aux_dir=
-for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do
- for ac_t in install-sh install.sh shtool; do
- if test -f "$ac_dir/$ac_t"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/$ac_t -c"
- break 2
- fi
- done
-done
-if test -z "$ac_aux_dir"; then
- as_fn_error "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5
-fi
-
-# These three variables are undocumented and unsupported,
-# and are intended to be withdrawn in a future Autoconf release.
-# They can cause serious problems if a builder's source tree is in a directory
-# whose full name contains unusual characters.
-ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
-ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
-ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
-
+am__api_version='1.15'
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
@@ -3524,7 +3682,7 @@ ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
$as_echo_n "checking for a BSD-compatible install... " >&6; }
if test -z "$INSTALL"; then
-if test "${ac_cv_path_install+set}" = set; then :
+if ${ac_cv_path_install+:} false; then :
$as_echo_n "(cached) " >&6
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3544,7 +3702,7 @@ case $as_dir/ in #((
# by default.
for ac_prog in ginstall scoinst install; do
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
if test $ac_prog = install &&
grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# AIX install. It has an incompatible calling convention.
@@ -3602,56 +3760,71 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5
$as_echo_n "checking whether build environment is sane... " >&6; }
-# Just in case
-sleep 1
-echo timestamp > conftest.file
# Reject unsafe characters in $srcdir or the absolute working directory
# name. Accept space and tab only in the latter.
am_lf='
'
case `pwd` in
*[\\\"\#\$\&\'\`$am_lf]*)
- as_fn_error "unsafe absolute working directory name" "$LINENO" 5;;
+ as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;;
esac
case $srcdir in
*[\\\"\#\$\&\'\`$am_lf\ \ ]*)
- as_fn_error "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;;
+ as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;;
esac
-# Do `set' in a subshell so we don't clobber the current shell's
+# Do 'set' in a subshell so we don't clobber the current shell's
# arguments. Must try -L first in case configure is actually a
# symlink; some systems play weird games with the mod time of symlinks
# (eg FreeBSD returns the mod time of the symlink's containing
# directory).
if (
- set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
- if test "$*" = "X"; then
- # -L didn't work.
- set X `ls -t "$srcdir/configure" conftest.file`
- fi
- rm -f conftest.file
- if test "$*" != "X $srcdir/configure conftest.file" \
- && test "$*" != "X conftest.file $srcdir/configure"; then
-
- # If neither matched, then we have a broken ls. This can happen
- # if, for instance, CONFIG_SHELL is bash and it inherits a
- # broken ls alias from the environment. This has actually
- # happened. Such a system could not be considered "sane".
- as_fn_error "ls -t appears to fail. Make sure there is not a broken
-alias in your environment" "$LINENO" 5
- fi
-
+ am_has_slept=no
+ for am_try in 1 2; do
+ echo "timestamp, slept: $am_has_slept" > conftest.file
+ set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null`
+ if test "$*" = "X"; then
+ # -L didn't work.
+ set X `ls -t "$srcdir/configure" conftest.file`
+ fi
+ if test "$*" != "X $srcdir/configure conftest.file" \
+ && test "$*" != "X conftest.file $srcdir/configure"; then
+
+ # If neither matched, then we have a broken ls. This can happen
+ # if, for instance, CONFIG_SHELL is bash and it inherits a
+ # broken ls alias from the environment. This has actually
+ # happened. Such a system could not be considered "sane".
+ as_fn_error $? "ls -t appears to fail. Make sure there is not a broken
+ alias in your environment" "$LINENO" 5
+ fi
+ if test "$2" = conftest.file || test $am_try -eq 2; then
+ break
+ fi
+ # Just in case.
+ sleep 1
+ am_has_slept=yes
+ done
test "$2" = conftest.file
)
then
# Ok.
:
else
- as_fn_error "newly created file is older than distributed files!
+ as_fn_error $? "newly created file is older than distributed files!
Check your system clock" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
$as_echo "yes" >&6; }
+# If we didn't sleep, we still need to ensure time stamps of config.status and
+# generated files are strictly newer.
+am_sleep_pid=
+if grep 'slept: no' conftest.file >/dev/null 2>&1; then
+ ( sleep 1 ) &
+ am_sleep_pid=$!
+fi
+
+rm -f conftest.file
+
test "$program_prefix" != NONE &&
program_transform_name="s&^&$program_prefix&;$program_transform_name"
# Use a double $ so make ignores it.
@@ -3662,9 +3835,6 @@ test "$program_suffix" != NONE &&
ac_script='s/[\\$]/&&/g;s/;s,x,x,$//'
program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"`
-# expand $ac_aux_dir to an absolute path
-am_aux_dir=`cd $ac_aux_dir && pwd`
-
if test x"${MISSING+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
@@ -3674,15 +3844,15 @@ if test x"${MISSING+set}" != xset; then
esac
fi
# Use eval to expand $SHELL
-if eval "$MISSING --run true"; then
- am_missing_run="$MISSING --run "
+if eval "$MISSING --is-lightweight"; then
+ am_missing_run="$MISSING "
else
am_missing_run=
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5
-$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;}
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5
+$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;}
fi
-if test x"${install_sh}" != xset; then
+if test x"${install_sh+set}" != xset; then
case $am_aux_dir in
*\ * | *\ *)
install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;;
@@ -3691,17 +3861,17 @@ if test x"${install_sh}" != xset; then
esac
fi
-# Installed binaries are usually stripped using `strip' when the user
-# run `make install-strip'. However `strip' might not be the right
+# Installed binaries are usually stripped using 'strip' when the user
+# run "make install-strip". However 'strip' might not be the right
# tool to use in cross-compilation environments, therefore Automake
-# will honor the `STRIP' environment variable to overrule this program.
+# will honor the 'STRIP' environment variable to overrule this program.
if test "$cross_compiling" != no; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args.
set dummy ${ac_tool_prefix}strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_STRIP+set}" = set; then :
+if ${ac_cv_prog_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$STRIP"; then
@@ -3713,7 +3883,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_STRIP="${ac_tool_prefix}strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3741,7 +3911,7 @@ if test -z "$ac_cv_prog_STRIP"; then
set dummy strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then :
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_STRIP"; then
@@ -3753,7 +3923,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_STRIP="strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3794,7 +3964,7 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5
$as_echo_n "checking for a thread-safe mkdir -p... " >&6; }
if test -z "$MKDIR_P"; then
- if test "${ac_cv_path_mkdir+set}" = set; then :
+ if ${ac_cv_path_mkdir+:} false; then :
$as_echo_n "(cached) " >&6
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3804,7 +3974,7 @@ do
test -z "$as_dir" && as_dir=.
for ac_prog in mkdir gmkdir; do
for ac_exec_ext in '' $ac_executable_extensions; do
- { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue
+ as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue
case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #(
'mkdir (GNU coreutils) '* | \
'mkdir (coreutils) '* | \
@@ -3833,19 +4003,13 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5
$as_echo "$MKDIR_P" >&6; }
-mkdir_p="$MKDIR_P"
-case $mkdir_p in
- [\\/$]* | ?:[\\/]*) ;;
- */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
-esac
-
for ac_prog in gawk mawk nawk awk
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_AWK+set}" = set; then :
+if ${ac_cv_prog_AWK+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AWK"; then
@@ -3857,7 +4021,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AWK="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -3885,7 +4049,7 @@ done
$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; }
set x ${MAKE-make}
ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'`
-if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then :
$as_echo_n "(cached) " >&6
else
cat >conftest.make <<\_ACEOF
@@ -3893,7 +4057,7 @@ SHELL = /bin/sh
all:
@echo '@@@%%%=$(MAKE)=@@@%%%'
_ACEOF
-# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+# GNU make sometimes prints "make[1]: Entering ...", which would confuse us.
case `${MAKE-make} -f conftest.make 2>/dev/null` in
*@@@%%%=?*=@@@%%%*)
eval ac_cv_prog_make_${ac_make}_set=yes;;
@@ -3940,7 +4104,7 @@ am__quote=
_am_result=none
# First try GNU make style include.
echo "include confinc" > confmf
-# Ignore all kinds of additional output from `make'.
+# Ignore all kinds of additional output from 'make'.
case `$am_make -s -f confmf 2> /dev/null` in #(
*the\ am__doit\ target*)
am__include=include
@@ -3973,6 +4137,7 @@ fi
if test "x$enable_dependency_tracking" != xno; then
am_depcomp="$ac_aux_dir/depcomp"
AMDEPBACKSLASH='\'
+ am__nodep='_no'
fi
if test "x$enable_dependency_tracking" != xno; then
AMDEP_TRUE=
@@ -3983,13 +4148,52 @@ else
fi
+# Check whether --enable-silent-rules was given.
+if test "${enable_silent_rules+set}" = set; then :
+ enableval=$enable_silent_rules;
+fi
+
+case $enable_silent_rules in # (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=1;;
+esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
+AM_BACKSLASH='\'
+
if test "`cd $srcdir && pwd`" != "`pwd`"; then
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
# is not polluted with repeated "-I."
am__isrc=' -I$(srcdir)'
# test to see if srcdir already configured
if test -f $srcdir/config.status; then
- as_fn_error "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
+ as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5
fi
fi
@@ -4004,8 +4208,8 @@ fi
# Define the identity of the package.
- PACKAGE='libedit-20120111'
- VERSION='3.0'
+ PACKAGE='libedit-20171208'
+ VERSION='3.1'
cat >>confdefs.h <<_ACEOF
@@ -4033,13 +4237,24 @@ AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"}
MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"}
-# We need awk for the "check" target. The system "awk" is bad on
-# some platforms.
-# Always define AMTAR for backward compatibility.
+# For better backward compatibility. To be removed once Automake 1.9.x
+# dies out for good. For more background, see:
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00001.html>
+# <http://lists.gnu.org/archive/html/automake/2012-07/msg00014.html>
+mkdir_p='$(MKDIR_P)'
+
+# We need awk for the "check" target (and possibly the TAP driver). The
+# system "awk" is bad on some platforms.
+# Always define AMTAR for backward compatibility. Yes, it's still used
+# in the wild :-( We should find a proper way to deprecate it ...
+AMTAR='$${TAR-tar}'
+
-AMTAR=${AMTAR-"${am_missing_run}tar"}
+# We'll loop over all known methods to create a tar archive until one works.
+_am_tools='gnutar pax cpio none'
+
+am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'
-am__tar='${AMTAR} chof - "$$tardir"'; am__untar='${AMTAR} xf -'
@@ -4048,15 +4263,16 @@ depcc="$CC" am_compiler_list=
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5
$as_echo_n "checking dependency style of $depcc... " >&6; }
-if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then :
+if ${am_cv_CC_dependencies_compiler_type+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then
# We make a subdir and do the tests there. Otherwise we can end up
# making bogus files that we don't know about and never remove. For
# instance it was reported that on HP-UX the gcc test will end up
- # making a dummy file named `D' -- because `-MD' means `put the output
- # in D'.
+ # making a dummy file named 'D' -- because '-MD' means "put the output
+ # in D".
+ rm -rf conftest.dir
mkdir conftest.dir
# Copy depcomp to subdir because otherwise we won't find it if we're
# using a relative directory.
@@ -4090,16 +4306,16 @@ else
: > sub/conftest.c
for i in 1 2 3 4 5 6; do
echo '#include "conftst'$i'.h"' >> sub/conftest.c
- # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with
- # Solaris 8's {/usr,}/bin/sh.
- touch sub/conftst$i.h
+ # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with
+ # Solaris 10 /bin/sh.
+ echo '/* dummy */' > sub/conftst$i.h
done
echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf
- # We check with `-c' and `-o' for the sake of the "dashmstdout"
+ # We check with '-c' and '-o' for the sake of the "dashmstdout"
# mode. It turns out that the SunPro C++ compiler does not properly
- # handle `-M -o', and we need to detect this. Also, some Intel
- # versions had trouble with output in subdirs
+ # handle '-M -o', and we need to detect this. Also, some Intel
+ # versions had trouble with output in subdirs.
am__obj=sub/conftest.${OBJEXT-o}
am__minus_obj="-o $am__obj"
case $depmode in
@@ -4108,16 +4324,16 @@ else
test "$am__universal" = false || continue
;;
nosideeffect)
- # after this tag, mechanisms are not by side-effect, so they'll
- # only be used when explicitly requested
+ # After this tag, mechanisms are not by side-effect, so they'll
+ # only be used when explicitly requested.
if test "x$enable_dependency_tracking" = xyes; then
continue
else
break
fi
;;
- msvisualcpp | msvcmsys)
- # This compiler won't grok `-c -o', but also, the minuso test has
+ msvc7 | msvc7msys | msvisualcpp | msvcmsys)
+ # This compiler won't grok '-c -o', but also, the minuso test has
# not run yet. These depmodes are late enough in the game, and
# so weak that their functioning should not be impacted.
am__obj=conftest.${OBJEXT-o}
@@ -4172,6 +4388,48 @@ fi
+# POSIX will say in a future version that running "rm -f" with no argument
+# is OK; and we want to be able to make that assumption in our Makefile
+# recipes. So use an aggressive probe to check that the usage we want is
+# actually supported "in the wild" to an acceptable degree.
+# See automake bug#10828.
+# To make any issue more visible, cause the running configure to be aborted
+# by default if the 'rm' program in use doesn't match our expectations; the
+# user can still override this though.
+if rm -f && rm -fr && rm -rf; then : OK; else
+ cat >&2 <<'END'
+Oops!
+
+Your 'rm' program seems unable to run without file operands specified
+on the command line, even when the '-f' option is present. This is contrary
+to the behaviour of most rm programs out there, and not conforming with
+the upcoming POSIX standard: <http://austingroupbugs.net/view.php?id=542>
+
+Please tell bug-automake@gnu.org about your system, including the value
+of your $PATH and any error possibly output before this message. This
+can help us improve future automake versions.
+
+END
+ if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then
+ echo 'Configuration will proceed anyway, since you have set the' >&2
+ echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2
+ echo >&2
+ else
+ cat >&2 <<'END'
+Aborting the configuration process, to ensure you take notice of the issue.
+
+You can download and install GNU coreutils to get an 'rm' implementation
+that behaves properly: <http://www.gnu.org/software/coreutils/>.
+
+If you want to complete the configuration process using your problematic
+'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM
+to "yes", and re-run configure.
+
+END
+ as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5
+ fi
+fi
+
case `pwd` in
*\ * | *\ *)
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5
@@ -4180,8 +4438,8 @@ esac
-macro_version='2.4'
-macro_revision='1.3293'
+macro_version='2.4.6'
+macro_revision='2.4.6'
@@ -4195,31 +4453,31 @@ macro_revision='1.3293'
-ltmain="$ac_aux_dir/ltmain.sh"
+ltmain=$ac_aux_dir/ltmain.sh
# Make sure we can run config.sub.
$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
- as_fn_error "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
+ as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
$as_echo_n "checking build system type... " >&6; }
-if test "${ac_cv_build+set}" = set; then :
+if ${ac_cv_build+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_build_alias=$build_alias
test "x$ac_build_alias" = x &&
ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
test "x$ac_build_alias" = x &&
- as_fn_error "cannot guess build type; you must specify one" "$LINENO" 5
+ as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` ||
- as_fn_error "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
$as_echo "$ac_cv_build" >&6; }
case $ac_cv_build in
*-*-*) ;;
-*) as_fn_error "invalid value of canonical build" "$LINENO" 5;;
+*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
esac
build=$ac_cv_build
ac_save_IFS=$IFS; IFS='-'
@@ -4237,14 +4495,14 @@ case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
$as_echo_n "checking host system type... " >&6; }
-if test "${ac_cv_host+set}" = set; then :
+if ${ac_cv_host+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "x$host_alias" = x; then
ac_cv_host=$ac_cv_build
else
ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` ||
- as_fn_error "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
+ as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5
fi
fi
@@ -4252,7 +4510,7 @@ fi
$as_echo "$ac_cv_host" >&6; }
case $ac_cv_host in
*-*-*) ;;
-*) as_fn_error "invalid value of canonical host" "$LINENO" 5;;
+*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
esac
host=$ac_cv_host
ac_save_IFS=$IFS; IFS='-'
@@ -4315,7 +4573,7 @@ func_echo_all ()
$ECHO ""
}
-case "$ECHO" in
+case $ECHO in
printf*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: printf" >&5
$as_echo "printf" >&6; } ;;
print*) { $as_echo "$as_me:${as_lineno-$LINENO}: result: print -r" >&5
@@ -4339,7 +4597,7 @@ esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5
$as_echo_n "checking for a sed that does not truncate output... " >&6; }
-if test "${ac_cv_path_SED+set}" = set; then :
+if ${ac_cv_path_SED+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
@@ -4359,7 +4617,7 @@ do
for ac_prog in sed gsed; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_SED="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue
+ as_fn_executable_p "$ac_path_SED" || continue
# Check for GNU ac_path_SED and select it if it is found.
# Check for GNU $ac_path_SED
case `"$ac_path_SED" --version 2>&1` in
@@ -4394,7 +4652,7 @@ esac
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_SED"; then
- as_fn_error "no acceptable sed could be found in \$PATH" "$LINENO" 5
+ as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5
fi
else
ac_cv_path_SED=$SED
@@ -4421,7 +4679,7 @@ Xsed="$SED -e 1s/^X//"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
$as_echo_n "checking for fgrep... " >&6; }
-if test "${ac_cv_path_FGREP+set}" = set; then :
+if ${ac_cv_path_FGREP+:} false; then :
$as_echo_n "(cached) " >&6
else
if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
@@ -4438,7 +4696,7 @@ do
for ac_prog in fgrep; do
for ac_exec_ext in '' $ac_executable_extensions; do
ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext"
- { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue
+ as_fn_executable_p "$ac_path_FGREP" || continue
# Check for GNU ac_path_FGREP and select it if it is found.
# Check for GNU $ac_path_FGREP
case `"$ac_path_FGREP" --version 2>&1` in
@@ -4473,7 +4731,7 @@ esac
done
IFS=$as_save_IFS
if test -z "$ac_cv_path_FGREP"; then
- as_fn_error "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ as_fn_error $? "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
fi
else
ac_cv_path_FGREP=$FGREP
@@ -4508,19 +4766,19 @@ test -z "$GREP" && GREP=grep
# Check whether --with-gnu-ld was given.
if test "${with_gnu_ld+set}" = set; then :
- withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes
+ withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
else
with_gnu_ld=no
fi
ac_prog=ld
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
# Check if gcc -print-prog-name=ld gives a path.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5
$as_echo_n "checking for ld used by $CC... " >&6; }
case $host in
*-*-mingw*)
- # gcc leaves a trailing carriage return which upsets mingw
+ # gcc leaves a trailing carriage return, which upsets mingw
ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;;
*)
ac_prog=`($CC -print-prog-name=ld) 2>&5` ;;
@@ -4534,7 +4792,7 @@ $as_echo_n "checking for ld used by $CC... " >&6; }
while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do
ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"`
done
- test -z "$LD" && LD="$ac_prog"
+ test -z "$LD" && LD=$ac_prog
;;
"")
# If it fails, then pretend we aren't using GCC.
@@ -4545,43 +4803,43 @@ $as_echo_n "checking for ld used by $CC... " >&6; }
with_gnu_ld=unknown
;;
esac
-elif test "$with_gnu_ld" = yes; then
+elif test yes = "$with_gnu_ld"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5
$as_echo_n "checking for GNU ld... " >&6; }
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5
$as_echo_n "checking for non-GNU ld... " >&6; }
fi
-if test "${lt_cv_path_LD+set}" = set; then :
+if ${lt_cv_path_LD+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -z "$LD"; then
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then
- lt_cv_path_LD="$ac_dir/$ac_prog"
+ lt_cv_path_LD=$ac_dir/$ac_prog
# Check to see if the program is GNU ld. I'd rather use --version,
# but apparently some variants of GNU ld only accept -v.
# Break only if it was the GNU/non-GNU ld that we prefer.
case `"$lt_cv_path_LD" -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
- test "$with_gnu_ld" != no && break
+ test no != "$with_gnu_ld" && break
;;
*)
- test "$with_gnu_ld" != yes && break
+ test yes != "$with_gnu_ld" && break
;;
esac
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
else
- lt_cv_path_LD="$LD" # Let the user override the test with a path.
+ lt_cv_path_LD=$LD # Let the user override the test with a path.
fi
fi
-LD="$lt_cv_path_LD"
+LD=$lt_cv_path_LD
if test -n "$LD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
$as_echo "$LD" >&6; }
@@ -4589,10 +4847,10 @@ else
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
$as_echo "no" >&6; }
fi
-test -z "$LD" && as_fn_error "no acceptable ld found in \$PATH" "$LINENO" 5
+test -z "$LD" && as_fn_error $? "no acceptable ld found in \$PATH" "$LINENO" 5
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5
$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; }
-if test "${lt_cv_prog_gnu_ld+set}" = set; then :
+if ${lt_cv_prog_gnu_ld+:} false; then :
$as_echo_n "(cached) " >&6
else
# I'd rather use --version here, but apparently some GNU lds only accept -v.
@@ -4619,38 +4877,43 @@ with_gnu_ld=$lt_cv_prog_gnu_ld
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5
$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
-if test "${lt_cv_path_NM+set}" = set; then :
+if ${lt_cv_path_NM+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$NM"; then
# Let the user override the test.
- lt_cv_path_NM="$NM"
+ lt_cv_path_NM=$NM
else
- lt_nm_to_check="${ac_tool_prefix}nm"
+ lt_nm_to_check=${ac_tool_prefix}nm
if test -n "$ac_tool_prefix" && test "$build" = "$host"; then
lt_nm_to_check="$lt_nm_to_check nm"
fi
for lt_tmp_nm in $lt_nm_to_check; do
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- tmp_nm="$ac_dir/$lt_tmp_nm"
- if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then
+ tmp_nm=$ac_dir/$lt_tmp_nm
+ if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then
# Check to see if the nm accepts a BSD-compat flag.
- # Adding the `sed 1q' prevents false positives on HP-UX, which says:
+ # Adding the 'sed 1q' prevents false positives on HP-UX, which says:
# nm: unknown option "B" ignored
# Tru64's nm complains that /dev/null is an invalid object file
- case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in
- */dev/null* | *'Invalid file or object type'*)
+ # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty
+ case $build_os in
+ mingw*) lt_bad_file=conftest.nm/nofile ;;
+ *) lt_bad_file=/dev/null ;;
+ esac
+ case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in
+ *$lt_bad_file* | *'Invalid file or object type'*)
lt_cv_path_NM="$tmp_nm -B"
- break
+ break 2
;;
*)
case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in
*/dev/null*)
lt_cv_path_NM="$tmp_nm -p"
- break
+ break 2
;;
*)
lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but
@@ -4661,15 +4924,15 @@ else
esac
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
done
: ${lt_cv_path_NM=no}
fi
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
$as_echo "$lt_cv_path_NM" >&6; }
-if test "$lt_cv_path_NM" != "no"; then
- NM="$lt_cv_path_NM"
+if test no != "$lt_cv_path_NM"; then
+ NM=$lt_cv_path_NM
else
# Didn't find any BSD compatible name lister, look for dumpbin.
if test -n "$DUMPBIN"; then :
@@ -4682,7 +4945,7 @@ else
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_DUMPBIN+set}" = set; then :
+if ${ac_cv_prog_DUMPBIN+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DUMPBIN"; then
@@ -4694,7 +4957,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4726,7 +4989,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_DUMPBIN+set}" = set; then :
+if ${ac_cv_prog_ac_ct_DUMPBIN+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DUMPBIN"; then
@@ -4738,7 +5001,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DUMPBIN="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -4775,9 +5038,9 @@ esac
fi
fi
- case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in
+ case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in
*COFF*)
- DUMPBIN="$DUMPBIN -symbols"
+ DUMPBIN="$DUMPBIN -symbols -headers"
;;
*)
DUMPBIN=:
@@ -4785,8 +5048,8 @@ fi
esac
fi
- if test "$DUMPBIN" != ":"; then
- NM="$DUMPBIN"
+ if test : != "$DUMPBIN"; then
+ NM=$DUMPBIN
fi
fi
test -z "$NM" && NM=nm
@@ -4798,7 +5061,7 @@ test -z "$NM" && NM=nm
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5
$as_echo_n "checking the name lister ($NM) interface... " >&6; }
-if test "${lt_cv_nm_interface+set}" = set; then :
+if ${lt_cv_nm_interface+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_nm_interface="BSD nm"
@@ -4833,11 +5096,11 @@ fi
# find the maximum length of command line arguments
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5
$as_echo_n "checking the maximum length of command line arguments... " >&6; }
-if test "${lt_cv_sys_max_cmd_len+set}" = set; then :
+if ${lt_cv_sys_max_cmd_len+:} false; then :
$as_echo_n "(cached) " >&6
else
i=0
- teststring="ABCD"
+ teststring=ABCD
case $build_os in
msdosdjgpp*)
@@ -4877,7 +5140,7 @@ else
lt_cv_sys_max_cmd_len=8192;
;;
- netbsd* | freebsd* | openbsd* | darwin* | dragonfly*)
+ bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*)
# This has been around since 386BSD, at least. Likely further.
if test -x /sbin/sysctl; then
lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax`
@@ -4896,6 +5159,11 @@ else
lt_cv_sys_max_cmd_len=196608
;;
+ os2*)
+ # The test takes a long time on OS/2.
+ lt_cv_sys_max_cmd_len=8192
+ ;;
+
osf*)
# Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure
# due to this test when exec_disable_arg_limit is 1 on Tru64. It is not
@@ -4922,22 +5190,23 @@ else
;;
*)
lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null`
- if test -n "$lt_cv_sys_max_cmd_len"; then
+ if test -n "$lt_cv_sys_max_cmd_len" && \
+ test undefined != "$lt_cv_sys_max_cmd_len"; then
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4`
lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3`
else
# Make teststring a little bigger before we do anything with it.
# a 1K string should be a reasonable start.
- for i in 1 2 3 4 5 6 7 8 ; do
+ for i in 1 2 3 4 5 6 7 8; do
teststring=$teststring$teststring
done
SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}}
# If test is not a shell built-in, we'll probably end up computing a
# maximum length that is only half of the actual maximum length, but
# we can't tell.
- while { test "X"`func_fallback_echo "$teststring$teststring" 2>/dev/null` \
+ while { test X`env echo "$teststring$teststring" 2>/dev/null` \
= "X$teststring$teststring"; } >/dev/null 2>&1 &&
- test $i != 17 # 1/2 MB should be enough
+ test 17 != "$i" # 1/2 MB should be enough
do
i=`expr $i + 1`
teststring=$teststring$teststring
@@ -4955,7 +5224,7 @@ else
fi
-if test -n $lt_cv_sys_max_cmd_len ; then
+if test -n "$lt_cv_sys_max_cmd_len"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5
$as_echo "$lt_cv_sys_max_cmd_len" >&6; }
else
@@ -4973,30 +5242,6 @@ max_cmd_len=$lt_cv_sys_max_cmd_len
: ${MV="mv -f"}
: ${RM="rm -f"}
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5
-$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; }
-# Try some XSI features
-xsi_shell=no
-( _lt_dummy="a/b/c"
- test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \
- = c,a/b,b/c, \
- && eval 'test $(( 1 + 1 )) -eq 2 \
- && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \
- && xsi_shell=yes
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5
-$as_echo "$xsi_shell" >&6; }
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5
-$as_echo_n "checking whether the shell understands \"+=\"... " >&6; }
-lt_shell_append=no
-( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \
- >/dev/null 2>&1 \
- && lt_shell_append=yes
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5
-$as_echo "$lt_shell_append" >&6; }
-
-
if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then
lt_unset=unset
else
@@ -5030,7 +5275,7 @@ esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to $host format" >&5
$as_echo_n "checking how to convert $build file names to $host format... " >&6; }
-if test "${lt_cv_to_host_file_cmd+set}" = set; then :
+if ${lt_cv_to_host_file_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
case $host in
@@ -5077,7 +5322,7 @@ $as_echo "$lt_cv_to_host_file_cmd" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to convert $build file names to toolchain format" >&5
$as_echo_n "checking how to convert $build file names to toolchain format... " >&6; }
-if test "${lt_cv_to_tool_file_cmd+set}" = set; then :
+if ${lt_cv_to_tool_file_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
#assume ordinary cross tools, or native build.
@@ -5104,7 +5349,7 @@ $as_echo "$lt_cv_to_tool_file_cmd" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5
$as_echo_n "checking for $LD option to reload object files... " >&6; }
-if test "${lt_cv_ld_reload_flag+set}" = set; then :
+if ${lt_cv_ld_reload_flag+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_reload_flag='-r'
@@ -5119,13 +5364,13 @@ esac
reload_cmds='$LD$reload_flag -o $output$reload_objs'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
- if test "$GCC" != yes; then
+ if test yes != "$GCC"; then
reload_cmds=false
fi
;;
darwin*)
- if test "$GCC" = yes; then
- reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs'
+ if test yes = "$GCC"; then
+ reload_cmds='$LTCC $LTCFLAGS -nostdlib $wl-r -o $output$reload_objs'
else
reload_cmds='$LD$reload_flag -o $output$reload_objs'
fi
@@ -5145,7 +5390,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}objdump; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_OBJDUMP+set}" = set; then :
+if ${ac_cv_prog_OBJDUMP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OBJDUMP"; then
@@ -5157,7 +5402,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5185,7 +5430,7 @@ if test -z "$ac_cv_prog_OBJDUMP"; then
set dummy objdump; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then :
+if ${ac_cv_prog_ac_ct_OBJDUMP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OBJDUMP"; then
@@ -5197,7 +5442,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OBJDUMP="objdump"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5244,7 +5489,7 @@ test -z "$OBJDUMP" && OBJDUMP=objdump
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5
$as_echo_n "checking how to recognize dependent libraries... " >&6; }
-if test "${lt_cv_deplibs_check_method+set}" = set; then :
+if ${lt_cv_deplibs_check_method+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_file_magic_cmd='$MAGIC_CMD'
@@ -5253,13 +5498,13 @@ lt_cv_deplibs_check_method='unknown'
# Need to set the preceding variable on all platforms that support
# interlibrary dependencies.
# 'none' -- dependencies not supported.
-# `unknown' -- same as none, but documents that we really don't know.
+# 'unknown' -- same as none, but documents that we really don't know.
# 'pass_all' -- all dependencies passed with no checks.
# 'test_compile' -- check by making test program.
# 'file_magic [[regex]]' -- check by looking for files in library path
-# which responds to the $file_magic_cmd with a given extended regex.
-# If you have `file' or equivalent on your system and you're not sure
-# whether `pass_all' will *always* work, you probably want this one.
+# that responds to the $file_magic_cmd with a given extended regex.
+# If you have 'file' or equivalent on your system and you're not sure
+# whether 'pass_all' will *always* work, you probably want this one.
case $host_os in
aix[4-9]*)
@@ -5286,8 +5531,7 @@ mingw* | pw32*)
# Base MSYS/MinGW do not provide the 'file' command needed by
# func_win32_libid shell function, so use a weaker test based on 'objdump',
# unless we find 'file', for example because we are cross-compiling.
- # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin.
- if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then
+ if ( file / ) >/dev/null 2>&1; then
lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL'
lt_cv_file_magic_cmd='func_win32_libid'
else
@@ -5323,10 +5567,6 @@ freebsd* | dragonfly*)
fi
;;
-gnu*)
- lt_cv_deplibs_check_method=pass_all
- ;;
-
haiku*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -5364,8 +5604,8 @@ irix5* | irix6* | nonstopux*)
lt_cv_deplibs_check_method=pass_all
;;
-# This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
lt_cv_deplibs_check_method=pass_all
;;
@@ -5387,8 +5627,8 @@ newos6*)
lt_cv_deplibs_check_method=pass_all
;;
-openbsd*)
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+openbsd* | bitrig*)
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$'
else
lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$'
@@ -5441,6 +5681,9 @@ sysv4 | sysv4.3*)
tpf*)
lt_cv_deplibs_check_method=pass_all
;;
+os2*)
+ lt_cv_deplibs_check_method=pass_all
+ ;;
esac
fi
@@ -5491,7 +5734,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}dlltool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_DLLTOOL+set}" = set; then :
+if ${ac_cv_prog_DLLTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DLLTOOL"; then
@@ -5503,7 +5746,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5531,7 +5774,7 @@ if test -z "$ac_cv_prog_DLLTOOL"; then
set dummy dlltool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_DLLTOOL+set}" = set; then :
+if ${ac_cv_prog_ac_ct_DLLTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DLLTOOL"; then
@@ -5543,7 +5786,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DLLTOOL="dlltool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5591,15 +5834,15 @@ test -z "$DLLTOOL" && DLLTOOL=dlltool
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to associate runtime and link libraries" >&5
$as_echo_n "checking how to associate runtime and link libraries... " >&6; }
-if test "${lt_cv_sharedlib_from_linklib_cmd+set}" = set; then :
+if ${lt_cv_sharedlib_from_linklib_cmd+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_sharedlib_from_linklib_cmd='unknown'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
- # two different shell functions defined in ltmain.sh
- # decide which to use based on capabilities of $DLLTOOL
+ # two different shell functions defined in ltmain.sh;
+ # decide which one to use based on capabilities of $DLLTOOL
case `$DLLTOOL --help 2>&1` in
*--identify-strict*)
lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib
@@ -5611,7 +5854,7 @@ cygwin* | mingw* | pw32* | cegcc*)
;;
*)
# fallback: assume linklib IS sharedlib
- lt_cv_sharedlib_from_linklib_cmd="$ECHO"
+ lt_cv_sharedlib_from_linklib_cmd=$ECHO
;;
esac
@@ -5634,7 +5877,7 @@ if test -n "$ac_tool_prefix"; then
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_AR+set}" = set; then :
+if ${ac_cv_prog_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AR"; then
@@ -5646,7 +5889,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AR="$ac_tool_prefix$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5678,7 +5921,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_AR+set}" = set; then :
+if ${ac_cv_prog_ac_ct_AR+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_AR"; then
@@ -5690,7 +5933,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_AR="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5742,7 +5985,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for archiver @FILE support" >&5
$as_echo_n "checking for archiver @FILE support... " >&6; }
-if test "${lt_cv_ar_at_file+set}" = set; then :
+if ${lt_cv_ar_at_file+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ar_at_file=no
@@ -5765,7 +6008,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
- if test "$ac_status" -eq 0; then
+ if test 0 -eq "$ac_status"; then
# Ensure the archiver fails upon bogus file names.
rm -f conftest.$ac_objext libconftest.a
{ { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$lt_ar_try\""; } >&5
@@ -5773,7 +6016,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
- if test "$ac_status" -ne 0; then
+ if test 0 -ne "$ac_status"; then
lt_cv_ar_at_file=@
fi
fi
@@ -5786,7 +6029,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
$as_echo "$lt_cv_ar_at_file" >&6; }
-if test "x$lt_cv_ar_at_file" = xno; then
+if test no = "$lt_cv_ar_at_file"; then
archiver_list_spec=
else
archiver_list_spec=$lt_cv_ar_at_file
@@ -5803,7 +6046,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_STRIP+set}" = set; then :
+if ${ac_cv_prog_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$STRIP"; then
@@ -5815,7 +6058,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_STRIP="${ac_tool_prefix}strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5843,7 +6086,7 @@ if test -z "$ac_cv_prog_STRIP"; then
set dummy strip; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then :
+if ${ac_cv_prog_ac_ct_STRIP+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_STRIP"; then
@@ -5855,7 +6098,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_STRIP="strip"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5902,7 +6145,7 @@ if test -n "$ac_tool_prefix"; then
set dummy ${ac_tool_prefix}ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_RANLIB+set}" = set; then :
+if ${ac_cv_prog_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$RANLIB"; then
@@ -5914,7 +6157,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -5942,7 +6185,7 @@ if test -z "$ac_cv_prog_RANLIB"; then
set dummy ranlib; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then :
+if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_RANLIB"; then
@@ -5954,7 +6197,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_RANLIB="ranlib"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6003,14 +6246,14 @@ old_postuninstall_cmds=
if test -n "$RANLIB"; then
case $host_os in
- openbsd*)
- old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib"
+ bitrig* | openbsd*)
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib"
;;
*)
- old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib"
+ old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib"
;;
esac
- old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib"
+ old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib"
fi
case $host_os in
@@ -6071,7 +6314,7 @@ compiler=$CC
# Check for command to grab the raw symbol name followed by C symbol from nm.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5
$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; }
-if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then :
+if ${lt_cv_sys_global_symbol_pipe+:} false; then :
$as_echo_n "(cached) " >&6
else
@@ -6093,7 +6336,7 @@ cygwin* | mingw* | pw32* | cegcc*)
symcode='[ABCDGISTW]'
;;
hpux*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
symcode='[ABCDEGRST]'
fi
;;
@@ -6126,14 +6369,44 @@ case `$NM -V 2>&1` in
symcode='[ABCDGIRSTW]' ;;
esac
+if test "$lt_cv_nm_interface" = "MS dumpbin"; then
+ # Gets list of data symbols to import.
+ lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'"
+ # Adjust the below global symbol transforms to fixup imported variables.
+ lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'"
+ lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'"
+ lt_c_name_lib_hook="\
+ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\
+ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'"
+else
+ # Disable hooks by default.
+ lt_cv_sys_global_symbol_to_import=
+ lt_cdecl_hook=
+ lt_c_name_hook=
+ lt_c_name_lib_hook=
+fi
+
# Transform an extracted symbol line into a proper C declaration.
# Some systems (esp. on ia64) link data and code symbols differently,
# so use this general approach.
-lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'"
+lt_cv_sys_global_symbol_to_cdecl="sed -n"\
+$lt_cdecl_hook\
+" -e 's/^T .* \(.*\)$/extern int \1();/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'"
# Transform an extracted symbol line into symbol name and symbol address
-lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'"
-lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\)[ ]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'"
+lt_cv_sys_global_symbol_to_c_name_address="sed -n"\
+$lt_c_name_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'"
+
+# Transform an extracted symbol line into symbol name with lib prefix and
+# symbol address.
+lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\
+$lt_c_name_lib_hook\
+" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\
+" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\
+" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'"
# Handle CRLF in mingw tool chain
opt_cr=
@@ -6151,20 +6424,24 @@ for ac_symprfx in "" "_"; do
# Write the raw and C identifiers.
if test "$lt_cv_nm_interface" = "MS dumpbin"; then
- # Fake it for dumpbin and say T for any non-static function
- # and D for any global variable.
+ # Fake it for dumpbin and say T for any non-static function,
+ # D for any global variable and I for any imported variable.
# Also find C++ and __fastcall symbols from MSVC++,
# which start with @ or ?.
lt_cv_sys_global_symbol_pipe="$AWK '"\
" {last_section=section; section=\$ 3};"\
+" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\
" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\
+" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\
+" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\
+" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\
" \$ 0!~/External *\|/{next};"\
" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\
" {if(hide[section]) next};"\
-" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\
-" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\
-" s[1]~/^[@?]/{print s[1], s[1]; next};"\
-" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\
+" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\
+" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\
+" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\
+" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\
" ' prfx=^$ac_symprfx"
else
lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'"
@@ -6212,11 +6489,11 @@ _LT_EOF
if $GREP ' nm_test_func$' "$nlist" >/dev/null; then
cat <<_LT_EOF > conftest.$ac_ext
/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
-/* DATA imports from DLLs on WIN32 con't be const, because runtime
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
relocations are performed -- see ld's documentation on pseudo-relocs. */
# define LT_DLSYM_CONST
-#elif defined(__osf__)
+#elif defined __osf__
/* This system does not cope well with relocations in const data. */
# define LT_DLSYM_CONST
#else
@@ -6242,7 +6519,7 @@ lt__PROGRAM__LTX_preloaded_symbols[] =
{
{ "@PROGRAM@", (void *) 0 },
_LT_EOF
- $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
+ $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext
cat <<\_LT_EOF >> conftest.$ac_ext
{0, (void *) 0}
};
@@ -6262,13 +6539,13 @@ _LT_EOF
mv conftest.$ac_objext conftstm.$ac_objext
lt_globsym_save_LIBS=$LIBS
lt_globsym_save_CFLAGS=$CFLAGS
- LIBS="conftstm.$ac_objext"
+ LIBS=conftstm.$ac_objext
CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag"
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } && test -s conftest${ac_exeext}; then
+ test $ac_status = 0; } && test -s conftest$ac_exeext; then
pipe_works=yes
fi
LIBS=$lt_globsym_save_LIBS
@@ -6289,7 +6566,7 @@ _LT_EOF
rm -rf conftest* conftst*
# Do not use the global_symbol_pipe unless it works.
- if test "$pipe_works" = yes; then
+ if test yes = "$pipe_works"; then
break
else
lt_cv_sys_global_symbol_pipe=
@@ -6342,6 +6619,16 @@ fi
+
+
+
+
+
+
+
+
+
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sysroot" >&5
$as_echo_n "checking for sysroot... " >&6; }
@@ -6354,9 +6641,9 @@ fi
lt_sysroot=
-case ${with_sysroot} in #(
+case $with_sysroot in #(
yes)
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
lt_sysroot=`$CC --print-sysroot 2>/dev/null`
fi
;; #(
@@ -6366,9 +6653,9 @@ case ${with_sysroot} in #(
no|'')
;; #(
*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${with_sysroot}" >&5
-$as_echo "${with_sysroot}" >&6; }
- as_fn_error "The sysroot must be an absolute path." "$LINENO" 5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_sysroot" >&5
+$as_echo "$with_sysroot" >&6; }
+ as_fn_error $? "The sysroot must be an absolute path." "$LINENO" 5
;;
esac
@@ -6379,18 +6666,99 @@ $as_echo "${lt_sysroot:-no}" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a working dd" >&5
+$as_echo_n "checking for a working dd... " >&6; }
+if ${ac_cv_path_lt_DD+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+: ${lt_DD:=$DD}
+if test -z "$lt_DD"; then
+ ac_path_lt_DD_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_prog in dd; do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_lt_DD="$as_dir/$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_lt_DD" || continue
+if "$ac_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=:
+fi
+ $ac_path_lt_DD_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_lt_DD"; then
+ :
+ fi
+else
+ ac_cv_path_lt_DD=$lt_DD
+fi
+
+rm -f conftest.i conftest2.i conftest.out
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
+$as_echo "$ac_cv_path_lt_DD" >&6; }
+
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to truncate binary pipes" >&5
+$as_echo_n "checking how to truncate binary pipes... " >&6; }
+if ${lt_cv_truncate_bin+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ printf 0123456789abcdef0123456789abcdef >conftest.i
+cat conftest.i conftest.i >conftest2.i
+lt_cv_truncate_bin=
+if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
+ cmp -s conftest.i conftest.out \
+ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
+fi
+rm -f conftest.i conftest2.i conftest.out
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
+$as_echo "$lt_cv_truncate_bin" >&6; }
+
+
+
+
+
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
# Check whether --enable-libtool-lock was given.
if test "${enable_libtool_lock+set}" = set; then :
enableval=$enable_libtool_lock;
fi
-test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes
+test no = "$enable_libtool_lock" || enable_libtool_lock=yes
# Some flags need to be propagated to the compiler or linker for good
# libtool support.
case $host in
ia64-*-hpux*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set mode
+ # options accordingly.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
@@ -6399,24 +6767,25 @@ ia64-*-hpux*)
test $ac_status = 0; }; then
case `/usr/bin/file conftest.$ac_objext` in
*ELF-32*)
- HPUX_IA64_MODE="32"
+ HPUX_IA64_MODE=32
;;
*ELF-64*)
- HPUX_IA64_MODE="64"
+ HPUX_IA64_MODE=64
;;
esac
fi
rm -rf conftest*
;;
*-*-irix6*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
echo '#line '$LINENO' "configure"' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; then
- if test "$lt_cv_prog_gnu_ld" = yes; then
+ if test yes = "$lt_cv_prog_gnu_ld"; then
case `/usr/bin/file conftest.$ac_objext` in
*32-bit*)
LD="${LD-ld} -melf32bsmip"
@@ -6445,9 +6814,50 @@ ia64-*-hpux*)
rm -rf conftest*
;;
-x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \
+mips64*-*linux*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
+ echo '#line '$LINENO' "configure"' > conftest.$ac_ext
+ if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
+ (eval $ac_compile) 2>&5
+ ac_status=$?
+ $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; then
+ emul=elf
+ case `/usr/bin/file conftest.$ac_objext` in
+ *32-bit*)
+ emul="${emul}32"
+ ;;
+ *64-bit*)
+ emul="${emul}64"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *MSB*)
+ emul="${emul}btsmip"
+ ;;
+ *LSB*)
+ emul="${emul}ltsmip"
+ ;;
+ esac
+ case `/usr/bin/file conftest.$ac_objext` in
+ *N32*)
+ emul="${emul}n32"
+ ;;
+ esac
+ LD="${LD-ld} -m $emul"
+ fi
+ rm -rf conftest*
+ ;;
+
+x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \
s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
- # Find out which ABI we are using.
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly. Note that the listed cases only cover the
+ # situations where additional linker options are needed (such as when
+ # doing 32-bit compilation for a host where ld defaults to 64-bit, or
+ # vice versa); the common cases where no linker options are needed do
+ # not appear in the list.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
@@ -6461,9 +6871,19 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
LD="${LD-ld} -m elf_i386_fbsd"
;;
x86_64-*linux*)
- LD="${LD-ld} -m elf_i386"
+ case `/usr/bin/file conftest.o` in
+ *x86-64*)
+ LD="${LD-ld} -m elf32_x86_64"
+ ;;
+ *)
+ LD="${LD-ld} -m elf_i386"
+ ;;
+ esac
+ ;;
+ powerpc64le-*linux*)
+ LD="${LD-ld} -m elf32lppclinux"
;;
- ppc64-*linux*|powerpc64-*linux*)
+ powerpc64-*linux*)
LD="${LD-ld} -m elf32ppclinux"
;;
s390x-*linux*)
@@ -6482,7 +6902,10 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
x86_64-*linux*)
LD="${LD-ld} -m elf_x86_64"
;;
- ppc*-*linux*|powerpc*-*linux*)
+ powerpcle-*linux*)
+ LD="${LD-ld} -m elf64lppc"
+ ;;
+ powerpc-*linux*)
LD="${LD-ld} -m elf64ppc"
;;
s390*-*linux*|s390*-*tpf*)
@@ -6500,11 +6923,11 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*)
*-*-sco3.2v5*)
# On SCO OpenServer 5, we need -belf to get full-featured binaries.
- SAVE_CFLAGS="$CFLAGS"
+ SAVE_CFLAGS=$CFLAGS
CFLAGS="$CFLAGS -belf"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5
$as_echo_n "checking whether the C compiler needs -belf... " >&6; }
-if test "${lt_cv_cc_needs_belf+set}" = set; then :
+if ${lt_cv_cc_needs_belf+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_ext=c
@@ -6540,13 +6963,14 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
$as_echo "$lt_cv_cc_needs_belf" >&6; }
- if test x"$lt_cv_cc_needs_belf" != x"yes"; then
+ if test yes != "$lt_cv_cc_needs_belf"; then
# this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf
- CFLAGS="$SAVE_CFLAGS"
+ CFLAGS=$SAVE_CFLAGS
fi
;;
-sparc*-*solaris*)
- # Find out which ABI we are using.
+*-*solaris*)
+ # Find out what ABI is being produced by ac_compile, and set linker
+ # options accordingly.
echo 'int i;' > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
(eval $ac_compile) 2>&5
@@ -6556,7 +6980,20 @@ sparc*-*solaris*)
case `/usr/bin/file conftest.o` in
*64-bit*)
case $lt_cv_prog_gnu_ld in
- yes*) LD="${LD-ld} -m elf64_sparc" ;;
+ yes*)
+ case $host in
+ i?86-*-solaris*|x86_64-*-solaris*)
+ LD="${LD-ld} -m elf_x86_64"
+ ;;
+ sparc*-*-solaris*)
+ LD="${LD-ld} -m elf64_sparc"
+ ;;
+ esac
+ # GNU ld 2.21 introduced _sol2 emulations. Use them if available.
+ if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then
+ LD=${LD-ld}_sol2
+ fi
+ ;;
*)
if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then
LD="${LD-ld} -64"
@@ -6570,14 +7007,14 @@ sparc*-*solaris*)
;;
esac
-need_locks="$enable_libtool_lock"
+need_locks=$enable_libtool_lock
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}mt", so it can be a program name with args.
set dummy ${ac_tool_prefix}mt; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_MANIFEST_TOOL+set}" = set; then :
+if ${ac_cv_prog_MANIFEST_TOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$MANIFEST_TOOL"; then
@@ -6589,7 +7026,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_MANIFEST_TOOL="${ac_tool_prefix}mt"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6617,7 +7054,7 @@ if test -z "$ac_cv_prog_MANIFEST_TOOL"; then
set dummy mt; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_MANIFEST_TOOL+set}" = set; then :
+if ${ac_cv_prog_ac_ct_MANIFEST_TOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_MANIFEST_TOOL"; then
@@ -6629,7 +7066,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_MANIFEST_TOOL="mt"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6667,7 +7104,7 @@ fi
test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $MANIFEST_TOOL is a manifest tool" >&5
$as_echo_n "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
-if test "${lt_cv_path_mainfest_tool+set}" = set; then :
+if ${lt_cv_path_mainfest_tool+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_path_mainfest_tool=no
@@ -6681,7 +7118,7 @@ else
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
$as_echo "$lt_cv_path_mainfest_tool" >&6; }
-if test "x$lt_cv_path_mainfest_tool" != xyes; then
+if test yes != "$lt_cv_path_mainfest_tool"; then
MANIFEST_TOOL=:
fi
@@ -6697,7 +7134,7 @@ fi
set dummy ${ac_tool_prefix}dsymutil; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_DSYMUTIL+set}" = set; then :
+if ${ac_cv_prog_DSYMUTIL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$DSYMUTIL"; then
@@ -6709,7 +7146,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6737,7 +7174,7 @@ if test -z "$ac_cv_prog_DSYMUTIL"; then
set dummy dsymutil; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_DSYMUTIL+set}" = set; then :
+if ${ac_cv_prog_ac_ct_DSYMUTIL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_DSYMUTIL"; then
@@ -6749,7 +7186,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_DSYMUTIL="dsymutil"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6789,7 +7226,7 @@ fi
set dummy ${ac_tool_prefix}nmedit; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_NMEDIT+set}" = set; then :
+if ${ac_cv_prog_NMEDIT+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$NMEDIT"; then
@@ -6801,7 +7238,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6829,7 +7266,7 @@ if test -z "$ac_cv_prog_NMEDIT"; then
set dummy nmedit; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_NMEDIT+set}" = set; then :
+if ${ac_cv_prog_ac_ct_NMEDIT+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_NMEDIT"; then
@@ -6841,7 +7278,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_NMEDIT="nmedit"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6881,7 +7318,7 @@ fi
set dummy ${ac_tool_prefix}lipo; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_LIPO+set}" = set; then :
+if ${ac_cv_prog_LIPO+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$LIPO"; then
@@ -6893,7 +7330,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_LIPO="${ac_tool_prefix}lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6921,7 +7358,7 @@ if test -z "$ac_cv_prog_LIPO"; then
set dummy lipo; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then :
+if ${ac_cv_prog_ac_ct_LIPO+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_LIPO"; then
@@ -6933,7 +7370,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_LIPO="lipo"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -6973,7 +7410,7 @@ fi
set dummy ${ac_tool_prefix}otool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_OTOOL+set}" = set; then :
+if ${ac_cv_prog_OTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OTOOL"; then
@@ -6985,7 +7422,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL="${ac_tool_prefix}otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -7013,7 +7450,7 @@ if test -z "$ac_cv_prog_OTOOL"; then
set dummy otool; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_OTOOL+set}" = set; then :
+if ${ac_cv_prog_ac_ct_OTOOL+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OTOOL"; then
@@ -7025,7 +7462,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL="otool"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -7065,7 +7502,7 @@ fi
set dummy ${ac_tool_prefix}otool64; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_OTOOL64+set}" = set; then :
+if ${ac_cv_prog_OTOOL64+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$OTOOL64"; then
@@ -7077,7 +7514,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -7105,7 +7542,7 @@ if test -z "$ac_cv_prog_OTOOL64"; then
set dummy otool64; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_ac_ct_OTOOL64+set}" = set; then :
+if ${ac_cv_prog_ac_ct_OTOOL64+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$ac_ct_OTOOL64"; then
@@ -7117,7 +7554,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_OTOOL64="otool64"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -7180,11 +7617,11 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5
$as_echo_n "checking for -single_module linker flag... " >&6; }
-if test "${lt_cv_apple_cc_single_mod+set}" = set; then :
+if ${lt_cv_apple_cc_single_mod+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_apple_cc_single_mod=no
- if test -z "${LT_MULTI_MODULE}"; then
+ if test -z "$LT_MULTI_MODULE"; then
# By default we will add the -single_module flag. You can override
# by either setting the environment variable LT_MULTI_MODULE
# non-empty at configure time, or by adding -multi_module to the
@@ -7196,7 +7633,13 @@ else
$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \
-dynamiclib -Wl,-single_module conftest.c 2>conftest.err
_lt_result=$?
- if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then
+ # If there is a non-empty error log, and "single_module"
+ # appears in it, assume the flag caused a linker warning
+ if test -s conftest.err && $GREP single_module conftest.err; then
+ cat conftest.err >&5
+ # Otherwise, if the output was created with a 0 exit code from
+ # the compiler, it worked.
+ elif test -f libconftest.dylib && test 0 = "$_lt_result"; then
lt_cv_apple_cc_single_mod=yes
else
cat conftest.err >&5
@@ -7207,9 +7650,10 @@ else
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
$as_echo "$lt_cv_apple_cc_single_mod" >&6; }
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5
$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; }
-if test "${lt_cv_ld_exported_symbols_list+set}" = set; then :
+if ${lt_cv_ld_exported_symbols_list+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_exported_symbols_list=no
@@ -7234,14 +7678,15 @@ else
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
$as_echo "$lt_cv_ld_exported_symbols_list" >&6; }
+
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for -force_load linker flag" >&5
$as_echo_n "checking for -force_load linker flag... " >&6; }
-if test "${lt_cv_ld_force_load+set}" = set; then :
+if ${lt_cv_ld_force_load+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_ld_force_load=no
@@ -7260,7 +7705,9 @@ _LT_EOF
echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&5
$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err
_lt_result=$?
- if test -f conftest && test ! -s conftest.err && test $_lt_result = 0 && $GREP forced_load conftest 2>&1 >/dev/null; then
+ if test -s conftest.err && $GREP force_load conftest.err; then
+ cat conftest.err >&5
+ elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then
lt_cv_ld_force_load=yes
else
cat conftest.err >&5
@@ -7273,32 +7720,32 @@ fi
$as_echo "$lt_cv_ld_force_load" >&6; }
case $host_os in
rhapsody* | darwin1.[012])
- _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
darwin1.*)
- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
darwin*) # darwin 5.x on
# if running on 10.5 or later, the deployment target defaults
# to the OS version, if on x86, and 10.4, the deployment
# target defaults to 10.4. Don't you love it?
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
10.0,*86*-darwin8*|10.0,*-darwin[91]*)
- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
- 10.[012]*)
- _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
+ 10.[012][,.]*)
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
10.*)
- _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
esac
- if test "$lt_cv_apple_cc_single_mod" = "yes"; then
+ if test yes = "$lt_cv_apple_cc_single_mod"; then
_lt_dar_single_mod='$single_module'
fi
- if test "$lt_cv_ld_exported_symbols_list" = "yes"; then
- _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym'
+ if test yes = "$lt_cv_ld_exported_symbols_list"; then
+ _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym'
else
- _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}'
+ _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib'
fi
- if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then
+ if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then
_lt_dsymutil='~$DSYMUTIL $lib || :'
else
_lt_dsymutil=
@@ -7306,11 +7753,46 @@ $as_echo "$lt_cv_ld_force_load" >&6; }
;;
esac
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
for ac_header in dlfcn.h
do :
ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default
"
-if test "x$ac_cv_header_dlfcn_h" = x""yes; then :
+if test "x$ac_cv_header_dlfcn_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_DLFCN_H 1
_ACEOF
@@ -7342,14 +7824,14 @@ if test "${enable_shared+set}" = set; then :
*)
enable_shared=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_shared=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac
else
@@ -7373,14 +7855,14 @@ if test "${enable_static+set}" = set; then :
*)
enable_static=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_static=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac
else
@@ -7398,14 +7880,27 @@ fi
# Check whether --with-pic was given.
if test "${with_pic+set}" = set; then :
- withval=$with_pic; pic_mode="$withval"
+ withval=$with_pic; lt_p=${PACKAGE-default}
+ case $withval in
+ yes|no) pic_mode=$withval ;;
+ *)
+ pic_mode=default
+ # Look at the argument we got. We use all the common list separators.
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
+ for lt_pkg in $withval; do
+ IFS=$lt_save_ifs
+ if test "X$lt_pkg" = "X$lt_p"; then
+ pic_mode=yes
+ fi
+ done
+ IFS=$lt_save_ifs
+ ;;
+ esac
else
pic_mode=default
fi
-test -z "$pic_mode" && pic_mode=default
-
@@ -7421,14 +7916,14 @@ if test "${enable_fast_install+set}" = set; then :
*)
enable_fast_install=no
# Look at the argument we got. We use all the common list separators.
- lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR,"
+ lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR,
for pkg in $enableval; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
if test "X$pkg" = "X$p"; then
enable_fast_install=yes
fi
done
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
;;
esac
else
@@ -7442,11 +7937,63 @@ fi
+ shared_archive_member_spec=
+case $host,$enable_shared in
+power*-*-aix[5-9]*,yes)
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking which variant of shared library versioning to provide" >&5
+$as_echo_n "checking which variant of shared library versioning to provide... " >&6; }
+
+# Check whether --with-aix-soname was given.
+if test "${with_aix_soname+set}" = set; then :
+ withval=$with_aix_soname; case $withval in
+ aix|svr4|both)
+ ;;
+ *)
+ as_fn_error $? "Unknown argument to --with-aix-soname" "$LINENO" 5
+ ;;
+ esac
+ lt_cv_with_aix_soname=$with_aix_soname
+else
+ if ${lt_cv_with_aix_soname+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ lt_cv_with_aix_soname=aix
+fi
+
+ with_aix_soname=$lt_cv_with_aix_soname
+fi
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5
+$as_echo "$with_aix_soname" >&6; }
+ if test aix != "$with_aix_soname"; then
+ # For the AIX way of multilib, we name the shared archive member
+ # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o',
+ # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File.
+ # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag,
+ # the AIX toolchain works better with OBJECT_MODE set (default 32).
+ if test 64 = "${OBJECT_MODE-32}"; then
+ shared_archive_member_spec=shr_64
+ else
+ shared_archive_member_spec=shr
+ fi
+ fi
+ ;;
+*)
+ with_aix_soname=aix
+ ;;
+esac
+
+
+
+
+
+
+
# This can be used to rebuild libtool when needed
-LIBTOOL_DEPS="$ltmain"
+LIBTOOL_DEPS=$ltmain
# Always use our own libtool.
LIBTOOL='$(SHELL) $(top_builddir)/libtool'
@@ -7476,6 +8023,10 @@ LIBTOOL='$(SHELL) $(top_builddir)/libtool'
+
+
+
+
test -z "$LN_S" && LN_S="ln -s"
@@ -7491,13 +8042,13 @@ test -z "$LN_S" && LN_S="ln -s"
-if test -n "${ZSH_VERSION+set}" ; then
+if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5
$as_echo_n "checking for objdir... " >&6; }
-if test "${lt_cv_objdir+set}" = set; then :
+if ${lt_cv_objdir+:} false; then :
$as_echo_n "(cached) " >&6
else
rm -f .libs 2>/dev/null
@@ -7530,7 +8081,7 @@ aix3*)
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
- if test "X${COLLECT_NAMES+set}" != Xset; then
+ if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
@@ -7541,14 +8092,14 @@ esac
ofile=libtool
can_build_shared=yes
-# All known linkers require a `.a' archive for static linking (except MSVC,
+# All known linkers require a '.a' archive for static linking (except MSVC,
# which needs '.lib').
libext=a
-with_gnu_ld="$lt_cv_prog_gnu_ld"
+with_gnu_ld=$lt_cv_prog_gnu_ld
-old_CC="$CC"
-old_CFLAGS="$CFLAGS"
+old_CC=$CC
+old_CFLAGS=$CFLAGS
# Set sane defaults for various variables
test -z "$CC" && CC=cc
@@ -7557,15 +8108,8 @@ test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS
test -z "$LD" && LD=ld
test -z "$ac_objext" && ac_objext=o
-for cc_temp in $compiler""; do
- case $cc_temp in
- compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
- distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
- \-*) ;;
- *) break;;
- esac
-done
-cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+func_cc_basename $compiler
+cc_basename=$func_cc_basename_result
# Only perform the check for file, if the check method requires it
@@ -7575,27 +8119,27 @@ file_magic*)
if test "$file_magic_cmd" = '$MAGIC_CMD'; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5
$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; }
-if test "${lt_cv_path_MAGIC_CMD+set}" = set; then :
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
- lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
*)
- lt_save_MAGIC_CMD="$MAGIC_CMD"
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
for ac_dir in $ac_dummy; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- if test -f $ac_dir/${ac_tool_prefix}file; then
- lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file"
+ if test -f "$ac_dir/${ac_tool_prefix}file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"${ac_tool_prefix}file"
if test -n "$file_magic_test_file"; then
case $deplibs_check_method in
"file_magic "*)
file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
- MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
$EGREP "$file_magic_regex" > /dev/null; then
:
@@ -7618,13 +8162,13 @@ _LT_EOF
break
fi
done
- IFS="$lt_save_ifs"
- MAGIC_CMD="$lt_save_MAGIC_CMD"
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
;;
esac
fi
-MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if test -n "$MAGIC_CMD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
$as_echo "$MAGIC_CMD" >&6; }
@@ -7641,27 +8185,27 @@ if test -z "$lt_cv_path_MAGIC_CMD"; then
if test -n "$ac_tool_prefix"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5
$as_echo_n "checking for file... " >&6; }
-if test "${lt_cv_path_MAGIC_CMD+set}" = set; then :
+if ${lt_cv_path_MAGIC_CMD+:} false; then :
$as_echo_n "(cached) " >&6
else
case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
- lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path.
+ lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
*)
- lt_save_MAGIC_CMD="$MAGIC_CMD"
- lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR
+ lt_save_MAGIC_CMD=$MAGIC_CMD
+ lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
ac_dummy="/usr/bin$PATH_SEPARATOR$PATH"
for ac_dir in $ac_dummy; do
- IFS="$lt_save_ifs"
+ IFS=$lt_save_ifs
test -z "$ac_dir" && ac_dir=.
- if test -f $ac_dir/file; then
- lt_cv_path_MAGIC_CMD="$ac_dir/file"
+ if test -f "$ac_dir/file"; then
+ lt_cv_path_MAGIC_CMD=$ac_dir/"file"
if test -n "$file_magic_test_file"; then
case $deplibs_check_method in
"file_magic "*)
file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"`
- MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+ MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null |
$EGREP "$file_magic_regex" > /dev/null; then
:
@@ -7684,13 +8228,13 @@ _LT_EOF
break
fi
done
- IFS="$lt_save_ifs"
- MAGIC_CMD="$lt_save_MAGIC_CMD"
+ IFS=$lt_save_ifs
+ MAGIC_CMD=$lt_save_MAGIC_CMD
;;
esac
fi
-MAGIC_CMD="$lt_cv_path_MAGIC_CMD"
+MAGIC_CMD=$lt_cv_path_MAGIC_CMD
if test -n "$MAGIC_CMD"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5
$as_echo "$MAGIC_CMD" >&6; }
@@ -7711,7 +8255,7 @@ esac
# Use C for the default configuration in the libtool script
-lt_save_CC="$CC"
+lt_save_CC=$CC
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -7769,7 +8313,7 @@ if test -n "$compiler"; then
lt_prog_compiler_no_builtin_flag=
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
case $cc_basename in
nvcc*)
lt_prog_compiler_no_builtin_flag=' -Xcompiler -fno-builtin' ;;
@@ -7779,13 +8323,13 @@ if test "$GCC" = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5
$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
-if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then :
+if ${lt_cv_prog_compiler_rtti_exceptions+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_rtti_exceptions=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
- lt_compiler_flag="-fno-rtti -fno-exceptions"
+ lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
@@ -7815,7 +8359,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
-if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then
+if test yes = "$lt_cv_prog_compiler_rtti_exceptions"; then
lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions"
else
:
@@ -7833,17 +8377,18 @@ lt_prog_compiler_pic=
lt_prog_compiler_static=
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_static='-static'
case $host_os in
aix*)
# All AIX code is PIC.
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static='-Bstatic'
fi
+ lt_prog_compiler_pic='-fPIC'
;;
amigaos*)
@@ -7854,8 +8399,8 @@ lt_prog_compiler_static=
;;
m68k)
# FIXME: we need at least 68020 code to build shared libraries, but
- # adding the `-m68020' flag to GCC prevents building anything better,
- # like `-m68040'.
+ # adding the '-m68020' flag to GCC prevents building anything better,
+ # like '-m68040'.
lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4'
;;
esac
@@ -7871,6 +8416,11 @@ lt_prog_compiler_static=
# Although the cygwin gcc ignores -fPIC, still need this for old-style
# (--disable-auto-import) libraries
lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
;;
darwin* | rhapsody*)
@@ -7931,7 +8481,9 @@ lt_prog_compiler_static=
case $cc_basename in
nvcc*) # Cuda Compiler Driver 2.2
lt_prog_compiler_wl='-Xlinker '
- lt_prog_compiler_pic='-Xcompiler -fPIC'
+ if test -n "$lt_prog_compiler_pic"; then
+ lt_prog_compiler_pic="-Xcompiler $lt_prog_compiler_pic"
+ fi
;;
esac
else
@@ -7939,7 +8491,7 @@ lt_prog_compiler_static=
case $host_os in
aix*)
lt_prog_compiler_wl='-Wl,'
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 now supports IA64 processor
lt_prog_compiler_static='-Bstatic'
else
@@ -7947,10 +8499,29 @@ lt_prog_compiler_static=
fi
;;
+ darwin* | rhapsody*)
+ # PIC is the default on this platform
+ # Common symbols not allowed in MH_DYLIB files
+ lt_prog_compiler_pic='-fno-common'
+ case $cc_basename in
+ nagfor*)
+ # NAG Fortran compiler
+ lt_prog_compiler_wl='-Wl,-Wl,,'
+ lt_prog_compiler_pic='-PIC'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
+ esac
+ ;;
+
mingw* | cygwin* | pw32* | os2* | cegcc*)
# This hack is so that the source file can tell whether it is being
# built for inclusion in a dll (and should export symbols for example).
lt_prog_compiler_pic='-DDLL_EXPORT'
+ case $host_os in
+ os2*)
+ lt_prog_compiler_static='$wl-static'
+ ;;
+ esac
;;
hpux9* | hpux10* | hpux11*)
@@ -7966,7 +8537,7 @@ lt_prog_compiler_static=
;;
esac
# Is there a better lt_prog_compiler_static that works with the bundled CC?
- lt_prog_compiler_static='${wl}-a ${wl}archive'
+ lt_prog_compiler_static='$wl-a ${wl}archive'
;;
irix5* | irix6* | nonstopux*)
@@ -7975,9 +8546,9 @@ lt_prog_compiler_static=
lt_prog_compiler_static='-non_shared'
;;
- linux* | k*bsd*-gnu | kopensolaris*-gnu)
+ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
case $cc_basename in
- # old Intel for x86_64 which still supported -KPIC.
+ # old Intel for x86_64, which still supported -KPIC.
ecc*)
lt_prog_compiler_wl='-Wl,'
lt_prog_compiler_pic='-KPIC'
@@ -8002,6 +8573,12 @@ lt_prog_compiler_static=
lt_prog_compiler_pic='-PIC'
lt_prog_compiler_static='-Bstatic'
;;
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group compilers (*not* the Pentium gcc compiler,
# which looks to be a dead project)
@@ -8022,18 +8599,33 @@ lt_prog_compiler_static=
;;
*)
case `$CC -V 2>&1 | sed 5q` in
- *Sun\ F* | *Sun*Fortran*)
+ *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [1-7].* | *Sun*Fortran*\ 8.[0-3]*)
# Sun Fortran 8.3 passes all unrecognized flags to the linker
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl=''
;;
+ *Sun\ F* | *Sun*Fortran*)
+ lt_prog_compiler_pic='-KPIC'
+ lt_prog_compiler_static='-Bstatic'
+ lt_prog_compiler_wl='-Qoption ld '
+ ;;
*Sun\ C*)
# Sun C 5.9
lt_prog_compiler_pic='-KPIC'
lt_prog_compiler_static='-Bstatic'
lt_prog_compiler_wl='-Wl,'
;;
+ *Intel*\ [CF]*Compiler*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fPIC'
+ lt_prog_compiler_static='-static'
+ ;;
+ *Portland\ Group*)
+ lt_prog_compiler_wl='-Wl,'
+ lt_prog_compiler_pic='-fpic'
+ lt_prog_compiler_static='-Bstatic'
+ ;;
esac
;;
esac
@@ -8084,7 +8676,7 @@ lt_prog_compiler_static=
;;
sysv4*MP*)
- if test -d /usr/nec ;then
+ if test -d /usr/nec; then
lt_prog_compiler_pic='-Kconform_pic'
lt_prog_compiler_static='-Bstatic'
fi
@@ -8113,7 +8705,7 @@ lt_prog_compiler_static=
fi
case $host_os in
- # For platforms which do not support PIC, -DPIC is meaningless:
+ # For platforms that do not support PIC, -DPIC is meaningless:
*djgpp*)
lt_prog_compiler_pic=
;;
@@ -8124,7 +8716,7 @@ esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5
$as_echo_n "checking for $compiler option to produce PIC... " >&6; }
-if test "${lt_cv_prog_compiler_pic+set}" = set; then :
+if ${lt_cv_prog_compiler_pic+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
@@ -8139,13 +8731,13 @@ lt_prog_compiler_pic=$lt_cv_prog_compiler_pic
if test -n "$lt_prog_compiler_pic"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5
$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; }
-if test "${lt_cv_prog_compiler_pic_works+set}" = set; then :
+if ${lt_cv_prog_compiler_pic_works+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_pic_works=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
- lt_compiler_flag="$lt_prog_compiler_pic -DPIC"
+ lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment
# Insert the option either (1) after the last *FLAGS variable, or
# (2) before a word containing "conftest.", or (3) at the end.
# Note that $ac_compile itself does not contain backslashes and begins
@@ -8175,7 +8767,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
$as_echo "$lt_cv_prog_compiler_pic_works" >&6; }
-if test x"$lt_cv_prog_compiler_pic_works" = xyes; then
+if test yes = "$lt_cv_prog_compiler_pic_works"; then
case $lt_prog_compiler_pic in
"" | " "*) ;;
*) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;;
@@ -8203,11 +8795,11 @@ fi
wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5
$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; }
-if test "${lt_cv_prog_compiler_static_works+set}" = set; then :
+if ${lt_cv_prog_compiler_static_works+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_static_works=no
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
@@ -8226,13 +8818,13 @@ else
fi
fi
$RM -r conftest*
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
$as_echo "$lt_cv_prog_compiler_static_works" >&6; }
-if test x"$lt_cv_prog_compiler_static_works" = xyes; then
+if test yes = "$lt_cv_prog_compiler_static_works"; then
:
else
lt_prog_compiler_static=
@@ -8246,7 +8838,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
-if test "${lt_cv_prog_compiler_c_o+set}" = set; then :
+if ${lt_cv_prog_compiler_c_o+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o=no
@@ -8301,7 +8893,7 @@ $as_echo "$lt_cv_prog_compiler_c_o" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5
$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
-if test "${lt_cv_prog_compiler_c_o+set}" = set; then :
+if ${lt_cv_prog_compiler_c_o+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler_c_o=no
@@ -8352,8 +8944,8 @@ $as_echo "$lt_cv_prog_compiler_c_o" >&6; }
-hard_links="nottested"
-if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then
+hard_links=nottested
+if test no = "$lt_cv_prog_compiler_c_o" && test no != "$need_locks"; then
# do not overwrite the value of need_locks provided by the user
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5
$as_echo_n "checking if we can lock with hard links... " >&6; }
@@ -8365,9 +8957,9 @@ $as_echo_n "checking if we can lock with hard links... " >&6; }
ln conftest.a conftest.b 2>/dev/null && hard_links=no
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5
$as_echo "$hard_links" >&6; }
- if test "$hard_links" = no; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5
-$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;}
+ if test no = "$hard_links"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&5
+$as_echo "$as_me: WARNING: '$CC' does not support '-c -o', so 'make -j' may be unsafe" >&2;}
need_locks=warn
fi
else
@@ -8395,7 +8987,6 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
hardcode_direct=no
hardcode_direct_absolute=no
hardcode_libdir_flag_spec=
- hardcode_libdir_flag_spec_ld=
hardcode_libdir_separator=
hardcode_minus_L=no
hardcode_shlibpath_var=unsupported
@@ -8411,9 +9002,9 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
# included in the symbol list
include_expsyms=
# exclude_expsyms can be an extended regexp of symbols to exclude
- # it will be wrapped by ` (' and `)$', so one must not match beginning or
- # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc',
- # as well as any symbol that contains `d'.
+ # it will be wrapped by ' (' and ')$', so one must not match beginning or
+ # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc',
+ # as well as any symbol that contains 'd'.
exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'
# Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out
# platforms (ab)use it in PIC code, but their linkers get confused if
@@ -8428,7 +9019,7 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
# FIXME: the MSVC++ port hasn't been tested in a loooong time
# When not using gcc, we currently assume that we are using
# Microsoft Visual C++.
- if test "$GCC" != yes; then
+ if test yes != "$GCC"; then
with_gnu_ld=no
fi
;;
@@ -8436,7 +9027,7 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
# we just hope/assume this is gcc and not c89 (= MSVC++)
with_gnu_ld=yes
;;
- openbsd*)
+ openbsd* | bitrig*)
with_gnu_ld=no
;;
esac
@@ -8446,7 +9037,7 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
# On some targets, GNU ld is compatible enough with the native linker
# that we're better off using the native interface for both.
lt_use_gnu_ld_interface=no
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
case $host_os in
aix*)
# The AIX port of GNU ld has always aspired to compatibility
@@ -8468,24 +9059,24 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
esac
fi
- if test "$lt_use_gnu_ld_interface" = yes; then
+ if test yes = "$lt_use_gnu_ld_interface"; then
# If archive_cmds runs LD, not CC, wlarc should be empty
- wlarc='${wl}'
+ wlarc='$wl'
# Set some defaults for GNU ld with shared library support. These
# are reset later if shared libraries are not supported. Putting them
# here allows them to be overridden if necessary.
runpath_var=LD_RUN_PATH
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
- export_dynamic_flag_spec='${wl}--export-dynamic'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ export_dynamic_flag_spec='$wl--export-dynamic'
# ancient GNU ld didn't support --whole-archive et. al.
if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then
- whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive'
+ whole_archive_flag_spec=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive'
else
whole_archive_flag_spec=
fi
supports_anon_versioning=no
- case `$LD -v 2>&1` in
+ case `$LD -v | $SED -e 's/(^)\+)\s\+//' 2>&1` in
*GNU\ gold*) supports_anon_versioning=yes ;;
*\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11
*\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ...
@@ -8498,7 +9089,7 @@ $as_echo_n "checking whether the $compiler linker ($LD) supports shared librarie
case $host_os in
aix[3-9]*)
# On AIX/PPC, the GNU linker is very broken
- if test "$host_cpu" != ia64; then
+ if test ia64 != "$host_cpu"; then
ld_shlibs=no
cat <<_LT_EOF 1>&2
@@ -8517,7 +9108,7 @@ _LT_EOF
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds=''
;;
m68k)
@@ -8533,7 +9124,7 @@ _LT_EOF
allow_undefined_flag=unsupported
# Joseph Beckenbach <jrb3@best.com> says some releases of gcc
# support --undefined. This deserves some investigation. FIXME
- archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
else
ld_shlibs=no
fi
@@ -8543,7 +9134,7 @@ _LT_EOF
# _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless,
# as there is no search path for DLLs.
hardcode_libdir_flag_spec='-L$libdir'
- export_dynamic_flag_spec='${wl}--export-all-symbols'
+ export_dynamic_flag_spec='$wl--export-all-symbols'
allow_undefined_flag=unsupported
always_export_symbols=no
enable_shared_with_static_runtimes=yes
@@ -8551,61 +9142,89 @@ _LT_EOF
exclude_expsyms='[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'
if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
- # If the export-symbols file already is a .def file (1st line
- # is EXPORTS), use it as is; otherwise, prepend...
- archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- cp $export_symbols $output_objdir/$soname.def;
- else
- echo EXPORTS > $output_objdir/$soname.def;
- cat $export_symbols >> $output_objdir/$soname.def;
- fi~
- $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
+ # If the export-symbols file already is a .def file, use it as
+ # is; otherwise, prepend EXPORTS...
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp $export_symbols $output_objdir/$soname.def;
+ else
+ echo EXPORTS > $output_objdir/$soname.def;
+ cat $export_symbols >> $output_objdir/$soname.def;
+ fi~
+ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib'
else
ld_shlibs=no
fi
;;
haiku*)
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
link_all_deplibs=yes
;;
+ os2*)
+ hardcode_libdir_flag_spec='-L$libdir'
+ hardcode_minus_L=yes
+ allow_undefined_flag=unsupported
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
+ ;;
+
interix[3-9]*)
hardcode_direct=no
hardcode_shlibpath_var=no
- hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
- export_dynamic_flag_spec='${wl}-E'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
# Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc.
# Instead, shared libraries are loaded at an image base (0x10000000 by
# default) and relocated if they conflict, which is a slow very memory
# consuming and fragmenting process. To avoid this, we pick a random,
# 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link
# time. Moving up from 0x10000000 also allows more sbrk(2) space.
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
- archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
+ archive_expsym_cmds='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib'
;;
gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu)
tmp_diet=no
- if test "$host_os" = linux-dietlibc; then
+ if test linux-dietlibc = "$host_os"; then
case $cc_basename in
diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn)
esac
fi
if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \
- && test "$tmp_diet" = no
+ && test no = "$tmp_diet"
then
tmp_addflag=' $pic_flag'
tmp_sharedflag='-shared'
case $cc_basename,$host_cpu in
pgcc*) # Portland Group C compiler
- whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag'
;;
pgf77* | pgf90* | pgf95* | pgfortran*)
# Portland Group f77 and f90 compilers
- whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
tmp_addflag=' $pic_flag -Mnomain' ;;
ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64
tmp_addflag=' -i_dynamic' ;;
@@ -8616,43 +9235,47 @@ _LT_EOF
lf95*) # Lahey Fortran 8.1
whole_archive_flag_spec=
tmp_sharedflag='--shared' ;;
+ nagfor*) # NAGFOR 5.3
+ tmp_sharedflag='-Wl,-shared' ;;
xl[cC]* | bgxl[cC]* | mpixl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below)
tmp_sharedflag='-qmkshrobj'
tmp_addflag= ;;
nvcc*) # Cuda Compiler Driver 2.2
- whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
compiler_needs_object=yes
;;
esac
case `$CC -V 2>&1 | sed 5q` in
*Sun\ C*) # Sun C 5.9
- whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive'
compiler_needs_object=yes
tmp_sharedflag='-G' ;;
*Sun\ F*) # Sun Fortran 8.3
tmp_sharedflag='-G' ;;
esac
- archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ if test yes = "$supports_anon_versioning"; then
archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib'
fi
case $cc_basename in
+ tcc*)
+ export_dynamic_flag_spec='-rdynamic'
+ ;;
xlf* | bgf* | bgxlf* | mpixlf*)
# IBM XL Fortran 10.1 on PPC cannot create shared libs itself
whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive'
- hardcode_libdir_flag_spec=
- hardcode_libdir_flag_spec_ld='-rpath $libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib'
- if test "x$supports_anon_versioning" = xyes; then
+ if test yes = "$supports_anon_versioning"; then
archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~
- cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
- echo "local: *; };" >> $output_objdir/$libname.ver~
- $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
+ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~
+ echo "local: *; };" >> $output_objdir/$libname.ver~
+ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib'
fi
;;
esac
@@ -8666,8 +9289,8 @@ _LT_EOF
archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib'
wlarc=
else
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
fi
;;
@@ -8685,8 +9308,8 @@ _LT_EOF
_LT_EOF
elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
@@ -8698,7 +9321,7 @@ _LT_EOF
ld_shlibs=no
cat <<_LT_EOF 1>&2
-*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not
+*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot
*** reliably create shared libraries on SCO systems. Therefore, libtool
*** is disabling shared libraries support. We urge you to upgrade GNU
*** binutils to release 2.16.91.0.3 or newer. Another option is to modify
@@ -8713,9 +9336,9 @@ _LT_EOF
# DT_RUNPATH tag from executables and libraries. But doing so
# requires that you compile everything twice, which is a pain.
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
@@ -8732,15 +9355,15 @@ _LT_EOF
*)
if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib'
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib'
else
ld_shlibs=no
fi
;;
esac
- if test "$ld_shlibs" = no; then
+ if test no = "$ld_shlibs"; then
runpath_var=
hardcode_libdir_flag_spec=
export_dynamic_flag_spec=
@@ -8756,7 +9379,7 @@ _LT_EOF
# Note: this linker hardcodes the directories in LIBPATH if there
# are no directories specified by -L.
hardcode_minus_L=yes
- if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then
+ if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then
# Neither direct hardcoding nor static linking is supported with a
# broken collect2.
hardcode_direct=unsupported
@@ -8764,34 +9387,57 @@ _LT_EOF
;;
aix[4-9]*)
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# On IA64, the linker does run time linking by default, so we don't
# have to do anything special.
aix_use_runtimelinking=no
exp_sym_flag='-Bexport'
- no_entry_flag=""
+ no_entry_flag=
else
# If we're using GNU nm, then we don't want the "-C" option.
- # -C means demangle to AIX nm, but means don't demangle with GNU nm
- # Also, AIX nm treats weak defined symbols like other global
- # defined symbols, whereas GNU nm marks them as "W".
+ # -C means demangle to GNU nm, but means don't demangle to AIX nm.
+ # Without the "-l" option, or with the "-B" option, AIX nm treats
+ # weak defined symbols like other global defined symbols, whereas
+ # GNU nm marks them as "W".
+ # While the 'weak' keyword is ignored in the Export File, we need
+ # it in the Import File for the 'aix-soname' feature, so we have
+ # to replace the "-B" option with "-P" for AIX nm.
if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then
- export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && (substr(\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols'
else
- export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols'
+ export_symbols_cmds='`func_echo_all $NM | $SED -e '\''s/B\([^B]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && (substr(\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols'
fi
aix_use_runtimelinking=no
# Test if we are trying to use run time linking or normal
# AIX style linking. If -brtl is somewhere in LDFLAGS, we
- # need to do runtime linking.
+ # have runtime linking enabled, and use it for executables.
+ # For shared libraries, we enable/disable runtime linking
+ # depending on the kind of the shared library created -
+ # when "with_aix_soname,aix_use_runtimelinking" is:
+ # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables
+ # "aix,yes" lib.so shared, rtl:yes, for executables
+ # lib.a static archive
+ # "both,no" lib.so.V(shr.o) shared, rtl:yes
+ # lib.a(lib.so.V) shared, rtl:no, for executables
+ # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a(lib.so.V) shared, rtl:no
+ # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables
+ # lib.a static archive
case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*)
for ld_flag in $LDFLAGS; do
- if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+ if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then
aix_use_runtimelinking=yes
break
fi
done
+ if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then
+ # With aix-soname=svr4, we create the lib.so.V shared archives only,
+ # so we don't have lib.a shared libs to link our executables.
+ # We have to force runtime linking in this case.
+ aix_use_runtimelinking=yes
+ LDFLAGS="$LDFLAGS -Wl,-brtl"
+ fi
;;
esac
@@ -8810,13 +9456,21 @@ _LT_EOF
hardcode_direct_absolute=yes
hardcode_libdir_separator=':'
link_all_deplibs=yes
- file_list_spec='${wl}-f,'
+ file_list_spec='$wl-f,'
+ case $with_aix_soname,$aix_use_runtimelinking in
+ aix,*) ;; # traditional, no import file
+ svr4,* | *,yes) # use import file
+ # The Import File defines what to hardcode.
+ hardcode_direct=no
+ hardcode_direct_absolute=no
+ ;;
+ esac
- if test "$GCC" = yes; then
+ if test yes = "$GCC"; then
case $host_os in aix4.[012]|aix4.[012].*)
# We only want to do this on AIX 4.2 and lower, the check
# below for broken collect2 doesn't work under 4.3+
- collect2name=`${CC} -print-prog-name=collect2`
+ collect2name=`$CC -print-prog-name=collect2`
if test -f "$collect2name" &&
strings "$collect2name" | $GREP resolve_lib_name >/dev/null
then
@@ -8835,38 +9489,45 @@ _LT_EOF
;;
esac
shared_flag='-shared'
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag="$shared_flag "'${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag="$shared_flag "'$wl-G'
fi
+ # Need to ensure runtime linking is disabled for the traditional
+ # shared library, or the linker may eventually find shared libraries
+ # /with/ Import File - we do not want to mix them.
+ shared_flag_aix='-shared'
+ shared_flag_svr4='-shared $wl-G'
else
# not using gcc
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release
# chokes on -Wl,-G. The following line is correct:
shared_flag='-G'
else
- if test "$aix_use_runtimelinking" = yes; then
- shared_flag='${wl}-G'
+ if test yes = "$aix_use_runtimelinking"; then
+ shared_flag='$wl-G'
else
- shared_flag='${wl}-bM:SRE'
+ shared_flag='$wl-bM:SRE'
fi
+ shared_flag_aix='$wl-bM:SRE'
+ shared_flag_svr4='$wl-G'
fi
fi
- export_dynamic_flag_spec='${wl}-bexpall'
+ export_dynamic_flag_spec='$wl-bexpall'
# It seems that -bexpall does not export symbols beginning with
# underscore (_), so it is better to generate a list of symbols to export.
always_export_symbols=yes
- if test "$aix_use_runtimelinking" = yes; then
+ if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then
# Warning - without using the other runtime loading flags (-brtl),
# -berok will link without error, but may produce a broken library.
allow_undefined_flag='-berok'
# Determine the default libpath from the value encoded in an
# empty executable.
- if test "${lt_cv_aix_libpath+set}" = set; then
+ if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
- if test "${lt_cv_aix_libpath_+set}" = set; then :
+ if ${lt_cv_aix_libpath_+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -8898,7 +9559,7 @@ fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath_"; then
- lt_cv_aix_libpath_="/usr/lib:/lib"
+ lt_cv_aix_libpath_=/usr/lib:/lib
fi
fi
@@ -8906,20 +9567,20 @@ fi
aix_libpath=$lt_cv_aix_libpath_
fi
- hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
- archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag"
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
+ archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag
else
- if test "$host_cpu" = ia64; then
- hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+ if test ia64 = "$host_cpu"; then
+ hardcode_libdir_flag_spec='$wl-R $libdir:/usr/lib:/lib'
allow_undefined_flag="-z nodefs"
- archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols"
+ archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols"
else
# Determine the default libpath from the value encoded in an
# empty executable.
- if test "${lt_cv_aix_libpath+set}" = set; then
+ if test set = "${lt_cv_aix_libpath+set}"; then
aix_libpath=$lt_cv_aix_libpath
else
- if test "${lt_cv_aix_libpath_+set}" = set; then :
+ if ${lt_cv_aix_libpath_+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -8951,7 +9612,7 @@ fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
if test -z "$lt_cv_aix_libpath_"; then
- lt_cv_aix_libpath_="/usr/lib:/lib"
+ lt_cv_aix_libpath_=/usr/lib:/lib
fi
fi
@@ -8959,21 +9620,33 @@ fi
aix_libpath=$lt_cv_aix_libpath_
fi
- hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+ hardcode_libdir_flag_spec='$wl-blibpath:$libdir:'"$aix_libpath"
# Warning - without using the other run time loading flags,
# -berok will link without error, but may produce a broken library.
- no_undefined_flag=' ${wl}-bernotok'
- allow_undefined_flag=' ${wl}-berok'
- if test "$with_gnu_ld" = yes; then
+ no_undefined_flag=' $wl-bernotok'
+ allow_undefined_flag=' $wl-berok'
+ if test yes = "$with_gnu_ld"; then
# We only use this code for GNU lds that support --whole-archive.
- whole_archive_flag_spec='${wl}--whole-archive$convenience ${wl}--no-whole-archive'
+ whole_archive_flag_spec='$wl--whole-archive$convenience $wl--no-whole-archive'
else
# Exported symbols can be pulled into shared objects from archives
whole_archive_flag_spec='$convenience'
fi
archive_cmds_need_lc=yes
- # This is similar to how AIX traditionally builds its shared libraries.
- archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname'
+ archive_expsym_cmds='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d'
+ # -brtl affects multiple linker settings, -berok does not and is overridden later
+ compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([, ]\\)%-berok\\1%g"`'
+ if test svr4 != "$with_aix_soname"; then
+ # This is similar to how AIX traditionally builds its shared libraries.
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname'
+ fi
+ if test aix != "$with_aix_soname"; then
+ archive_expsym_cmds="$archive_expsym_cmds"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp'
+ else
+ # used by -dlpreopen to get the symbols
+ archive_expsym_cmds="$archive_expsym_cmds"'~$MV $output_objdir/$realname.d/$soname $output_objdir'
+ fi
+ archive_expsym_cmds="$archive_expsym_cmds"'~$RM -r $output_objdir/$realname.d'
fi
fi
;;
@@ -8982,7 +9655,7 @@ fi
case $host_cpu in
powerpc)
# see comment about AmigaOS4 .so support
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib'
archive_expsym_cmds=''
;;
m68k)
@@ -9012,35 +9685,37 @@ fi
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
- archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames='
- archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then
- sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp;
- else
- sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp;
- fi~
- $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
- linknames='
+ archive_cmds='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames='
+ archive_expsym_cmds='if test DEF = "`$SED -n -e '\''s/^[ ]*//'\'' -e '\''/^\(;.*\)*$/d'\'' -e '\''s/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p'\'' -e q $export_symbols`" ; then
+ cp "$export_symbols" "$output_objdir/$soname.def";
+ echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp";
+ else
+ $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp;
+ fi~
+ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~
+ linknames='
# The linker will not automatically build a static lib if we build a DLL.
# _LT_TAGVAR(old_archive_from_new_cmds, )='true'
enable_shared_with_static_runtimes=yes
+ exclude_expsyms='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*'
export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1,DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols'
# Don't use ranlib
old_postinstall_cmds='chmod 644 $oldlib'
postlink_cmds='lt_outputfile="@OUTPUT@"~
- lt_tool_outputfile="@TOOL_OUTPUT@"~
- case $lt_outputfile in
- *.exe|*.EXE) ;;
- *)
- lt_outputfile="$lt_outputfile.exe"
- lt_tool_outputfile="$lt_tool_outputfile.exe"
- ;;
- esac~
- if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then
- $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
- $RM "$lt_outputfile.manifest";
- fi'
+ lt_tool_outputfile="@TOOL_OUTPUT@"~
+ case $lt_outputfile in
+ *.exe|*.EXE) ;;
+ *)
+ lt_outputfile=$lt_outputfile.exe
+ lt_tool_outputfile=$lt_tool_outputfile.exe
+ ;;
+ esac~
+ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then
+ $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1;
+ $RM "$lt_outputfile.manifest";
+ fi'
;;
*)
# Assume MSVC wrapper
@@ -9049,7 +9724,7 @@ fi
# Tell ltmain to make .lib files, not .a files.
libext=lib
# Tell ltmain to make .dll files, not .so files.
- shrext_cmds=".dll"
+ shrext_cmds=.dll
# FIXME: Setting linknames here is a bad hack.
archive_cmds='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames='
# The linker will automatically build a .lib file if we build a DLL.
@@ -9068,23 +9743,24 @@ fi
hardcode_direct=no
hardcode_automatic=yes
hardcode_shlibpath_var=unsupported
- if test "$lt_cv_ld_force_load" = "yes"; then
- whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+ if test yes = "$lt_cv_ld_force_load"; then
+ whole_archive_flag_spec='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`'
+
else
whole_archive_flag_spec=''
fi
link_all_deplibs=yes
- allow_undefined_flag="$_lt_dar_allow_undefined"
+ allow_undefined_flag=$_lt_dar_allow_undefined
case $cc_basename in
- ifort*) _lt_dar_can_shared=yes ;;
+ ifort*|nagfor*) _lt_dar_can_shared=yes ;;
*) _lt_dar_can_shared=$GCC ;;
esac
- if test "$_lt_dar_can_shared" = "yes"; then
+ if test yes = "$_lt_dar_can_shared"; then
output_verbose_link_cmd=func_echo_all
- archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}"
- module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}"
- archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}"
- module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}"
+ archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil"
+ module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil"
+ archive_expsym_cmds="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil"
+ module_expsym_cmds="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil"
else
ld_shlibs=no
@@ -9098,10 +9774,6 @@ fi
hardcode_shlibpath_var=no
;;
- freebsd1*)
- ld_shlibs=no
- ;;
-
# FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor
# support. Future versions do this automatically, but an explicit c++rt0.o
# does not break anything, and helps significantly (at the cost of a little
@@ -9114,7 +9786,7 @@ fi
;;
# Unfortunately, older versions of FreeBSD 2 do not have this feature.
- freebsd2*)
+ freebsd2.*)
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes
hardcode_minus_L=yes
@@ -9130,34 +9802,33 @@ fi
;;
hpux9*)
- if test "$GCC" = yes; then
- archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ if test yes = "$GCC"; then
+ archive_cmds='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
else
- archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib'
+ archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib'
fi
- hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
- export_dynamic_flag_spec='${wl}-E'
+ export_dynamic_flag_spec='$wl-E'
;;
hpux10*)
- if test "$GCC" = yes && test "$with_gnu_ld" = no; then
- archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes,no = "$GCC,$with_gnu_ld"; then
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
fi
- if test "$with_gnu_ld" = no; then
- hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
- hardcode_libdir_flag_spec_ld='+b $libdir'
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
hardcode_direct=yes
hardcode_direct_absolute=yes
- export_dynamic_flag_spec='${wl}-E'
+ export_dynamic_flag_spec='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
hardcode_minus_L=yes
@@ -9165,25 +9836,25 @@ fi
;;
hpux11*)
- if test "$GCC" = yes && test "$with_gnu_ld" = no; then
+ if test yes,no = "$GCC,$with_gnu_ld"; then
case $host_cpu in
hppa*64*)
- archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
- archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
- archive_cmds='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
;;
esac
else
case $host_cpu in
hppa*64*)
- archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
;;
ia64*)
- archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags'
;;
*)
@@ -9191,11 +9862,11 @@ fi
# (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does)
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $CC understands -b" >&5
$as_echo_n "checking if $CC understands -b... " >&6; }
-if test "${lt_cv_prog_compiler__b+set}" = set; then :
+if ${lt_cv_prog_compiler__b+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_prog_compiler__b=no
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -b"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then
@@ -9214,14 +9885,14 @@ else
fi
fi
$RM -r conftest*
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
$as_echo "$lt_cv_prog_compiler__b" >&6; }
-if test x"$lt_cv_prog_compiler__b" = xyes; then
- archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
+if test yes = "$lt_cv_prog_compiler__b"; then
+ archive_cmds='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'
fi
@@ -9229,8 +9900,8 @@ fi
;;
esac
fi
- if test "$with_gnu_ld" = no; then
- hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+ if test no = "$with_gnu_ld"; then
+ hardcode_libdir_flag_spec='$wl+b $wl$libdir'
hardcode_libdir_separator=:
case $host_cpu in
@@ -9241,7 +9912,7 @@ fi
*)
hardcode_direct=yes
hardcode_direct_absolute=yes
- export_dynamic_flag_spec='${wl}-E'
+ export_dynamic_flag_spec='$wl-E'
# hardcode_minus_L: Not really in the search PATH,
# but as the default location of the library.
@@ -9252,19 +9923,19 @@ fi
;;
irix5* | irix6* | nonstopux*)
- if test "$GCC" = yes; then
- archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
# Try to use the -exported_symbol ld option, if it does not
# work, assume that -exports_file does not work either and
# implicitly export all symbols.
# This should be the same for all languages, so no per-tag cache variable.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $host_os linker accepts -exported_symbol" >&5
$as_echo_n "checking whether the $host_os linker accepts -exported_symbol... " >&6; }
-if test "${lt_cv_irix_exported_symbol+set}" = set; then :
+if ${lt_cv_irix_exported_symbol+:} false; then :
$as_echo_n "(cached) " >&6
else
- save_LDFLAGS="$LDFLAGS"
- LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null"
+ save_LDFLAGS=$LDFLAGS
+ LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int foo (void) { return 0; }
@@ -9276,24 +9947,34 @@ else
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
- LDFLAGS="$save_LDFLAGS"
+ LDFLAGS=$save_LDFLAGS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
$as_echo "$lt_cv_irix_exported_symbol" >&6; }
- if test "$lt_cv_irix_exported_symbol" = yes; then
- archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib'
+ if test yes = "$lt_cv_irix_exported_symbol"; then
+ archive_expsym_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib'
fi
else
- archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
- archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib'
+ archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
+ archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib'
fi
archive_cmds_need_lc='no'
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
inherit_rpath=yes
link_all_deplibs=yes
;;
+ linux*)
+ case $cc_basename in
+ tcc*)
+ # Fabrice Bellard et al's Tiny C Compiler
+ ld_shlibs=yes
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ ;;
+ esac
+ ;;
+
netbsd*)
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out
@@ -9308,7 +9989,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
newsos6)
archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags'
hardcode_direct=yes
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
hardcode_shlibpath_var=no
;;
@@ -9316,27 +9997,19 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
*nto* | *qnx*)
;;
- openbsd*)
+ openbsd* | bitrig*)
if test -f /usr/libexec/ld.so; then
hardcode_direct=yes
hardcode_shlibpath_var=no
hardcode_direct_absolute=yes
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols'
- hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
- export_dynamic_flag_spec='${wl}-E'
+ archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
+ export_dynamic_flag_spec='$wl-E'
else
- case $host_os in
- openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
- archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags'
- hardcode_libdir_flag_spec='-R$libdir'
- ;;
- *)
- archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
- hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
- ;;
- esac
+ archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags'
+ hardcode_libdir_flag_spec='$wl-rpath,$libdir'
fi
else
ld_shlibs=no
@@ -9347,33 +10020,53 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
hardcode_libdir_flag_spec='-L$libdir'
hardcode_minus_L=yes
allow_undefined_flag=unsupported
- archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def'
- old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def'
+ shrext_cmds=.dll
+ archive_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ archive_expsym_cmds='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~
+ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~
+ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~
+ $ECHO EXPORTS >> $output_objdir/$libname.def~
+ prefix_cmds="$SED"~
+ if test EXPORTS = "`$SED 1q $export_symbols`"; then
+ prefix_cmds="$prefix_cmds -e 1d";
+ fi~
+ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~
+ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~
+ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~
+ emximp -o $lib $output_objdir/$libname.def'
+ old_archive_From_new_cmds='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def'
+ enable_shared_with_static_runtimes=yes
;;
osf3*)
- if test "$GCC" = yes; then
- allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
- archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
else
allow_undefined_flag=' -expect_unresolved \*'
- archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
fi
archive_cmds_need_lc='no'
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
hardcode_libdir_separator=:
;;
osf4* | osf5*) # as osf3* with the addition of -msym flag
- if test "$GCC" = yes; then
- allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*'
- archive_cmds='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib'
- hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+ if test yes = "$GCC"; then
+ allow_undefined_flag=' $wl-expect_unresolved $wl\*'
+ archive_cmds='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib'
+ hardcode_libdir_flag_spec='$wl-rpath $wl$libdir'
else
allow_undefined_flag=' -expect_unresolved \*'
- archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib'
+ archive_cmds='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib'
archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~
- $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp'
+ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp'
# Both c and cxx compiler support -rpath directly
hardcode_libdir_flag_spec='-rpath $libdir'
@@ -9384,24 +10077,24 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
solaris*)
no_undefined_flag=' -z defs'
- if test "$GCC" = yes; then
- wlarc='${wl}'
- archive_cmds='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ wlarc='$wl'
+ archive_cmds='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
else
case `$CC -V 2>&1` in
*"Compilers 5.0"*)
wlarc=''
- archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags'
+ archive_cmds='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
+ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp'
;;
*)
- wlarc='${wl}'
- archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ wlarc='$wl'
+ archive_cmds='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags'
archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~
- $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
+ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp'
;;
esac
fi
@@ -9411,11 +10104,11 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
solaris2.[0-5] | solaris2.[0-5].*) ;;
*)
# The compiler driver will combine and reorder linker options,
- # but understands `-z linker_flag'. GCC discards it without `$wl',
+ # but understands '-z linker_flag'. GCC discards it without '$wl',
# but is careful enough not to reorder.
# Supported since Solaris 2.6 (maybe 2.5.1?)
- if test "$GCC" = yes; then
- whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract'
+ if test yes = "$GCC"; then
+ whole_archive_flag_spec='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract'
else
whole_archive_flag_spec='-z allextract$convenience -z defaultextract'
fi
@@ -9425,10 +10118,10 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
;;
sunos4*)
- if test "x$host_vendor" = xsequent; then
+ if test sequent = "$host_vendor"; then
# Use $CC to link under sequent, because it throws in some extra .o
# files that make .init and .fini sections work.
- archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags'
else
archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags'
fi
@@ -9477,43 +10170,43 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
;;
sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
- no_undefined_flag='${wl}-z,text'
+ no_undefined_flag='$wl-z,text'
archive_cmds_need_lc=no
hardcode_shlibpath_var=no
runpath_var='LD_RUN_PATH'
- if test "$GCC" = yes; then
- archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
- archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
sysv5* | sco3.2v5* | sco5v6*)
- # Note: We can NOT use -z defs as we might desire, because we do not
+ # Note: We CANNOT use -z defs as we might desire, because we do not
# link with -lc, and that would cause any symbols used from libc to
# always be unresolved, which means just about no library would
# ever link correctly. If we're not using GNU ld we use -z text
# though, which does catch some bad symbols but isn't as heavy-handed
# as -z defs.
- no_undefined_flag='${wl}-z,text'
- allow_undefined_flag='${wl}-z,nodefs'
+ no_undefined_flag='$wl-z,text'
+ allow_undefined_flag='$wl-z,nodefs'
archive_cmds_need_lc=no
hardcode_shlibpath_var=no
- hardcode_libdir_flag_spec='${wl}-R,$libdir'
+ hardcode_libdir_flag_spec='$wl-R,$libdir'
hardcode_libdir_separator=':'
link_all_deplibs=yes
- export_dynamic_flag_spec='${wl}-Bexport'
+ export_dynamic_flag_spec='$wl-Bexport'
runpath_var='LD_RUN_PATH'
- if test "$GCC" = yes; then
- archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ if test yes = "$GCC"; then
+ archive_cmds='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
else
- archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
- archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_cmds='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
+ archive_expsym_cmds='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags'
fi
;;
@@ -9528,10 +10221,10 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
;;
esac
- if test x$host_vendor = xsni; then
+ if test sni = "$host_vendor"; then
case $host in
sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*)
- export_dynamic_flag_spec='${wl}-Blargedynsym'
+ export_dynamic_flag_spec='$wl-Blargedynsym'
;;
esac
fi
@@ -9539,7 +10232,7 @@ $as_echo "$lt_cv_irix_exported_symbol" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5
$as_echo "$ld_shlibs" >&6; }
-test "$ld_shlibs" = no && can_build_shared=no
+test no = "$ld_shlibs" && can_build_shared=no
with_gnu_ld=$with_gnu_ld
@@ -9565,7 +10258,7 @@ x|xyes)
# Assume -lc should be added
archive_cmds_need_lc=yes
- if test "$enable_shared" = yes && test "$GCC" = yes; then
+ if test yes,yes = "$GCC,$enable_shared"; then
case $archive_cmds in
*'~'*)
# FIXME: we may have to deal with multi-command sequences.
@@ -9576,7 +10269,7 @@ x|xyes)
# to ld, don't add -lc before -lgcc.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5
$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; }
-if test "${lt_cv_archive_cmds_need_lc+set}" = set; then :
+if ${lt_cv_archive_cmds_need_lc+:} false; then :
$as_echo_n "(cached) " >&6
else
$RM conftest*
@@ -9777,22 +10470,17 @@ esac
-
-
-
-
-
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5
$as_echo_n "checking dynamic linker characteristics... " >&6; }
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
case $host_os in
- darwin*) lt_awk_arg="/^libraries:/,/LR/" ;;
- *) lt_awk_arg="/^libraries:/" ;;
+ darwin*) lt_awk_arg='/^libraries:/,/LR/' ;;
+ *) lt_awk_arg='/^libraries:/' ;;
esac
case $host_os in
- mingw* | cegcc*) lt_sed_strip_eq="s,=\([A-Za-z]:\),\1,g" ;;
- *) lt_sed_strip_eq="s,=/,/,g" ;;
+ mingw* | cegcc*) lt_sed_strip_eq='s|=\([A-Za-z]:\)|\1|g' ;;
+ *) lt_sed_strip_eq='s|=/|/|g' ;;
esac
lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq`
case $lt_search_path_spec in
@@ -9808,28 +10496,35 @@ if test "$GCC" = yes; then
;;
esac
# Ok, now we have the path, separated by spaces, we can step through it
- # and add multilib dir if necessary.
+ # and add multilib dir if necessary...
lt_tmp_lt_search_path_spec=
- lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null`
+ # ...but if some path component already ends with the multilib dir we assume
+ # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer).
+ case "$lt_multi_os_dir; $lt_search_path_spec " in
+ "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*)
+ lt_multi_os_dir=
+ ;;
+ esac
for lt_sys_path in $lt_search_path_spec; do
- if test -d "$lt_sys_path/$lt_multi_os_dir"; then
- lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir"
- else
+ if test -d "$lt_sys_path$lt_multi_os_dir"; then
+ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir"
+ elif test -n "$lt_multi_os_dir"; then
test -d "$lt_sys_path" && \
lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path"
fi
done
lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk '
-BEGIN {RS=" "; FS="/|\n";} {
- lt_foo="";
- lt_count=0;
+BEGIN {RS = " "; FS = "/|\n";} {
+ lt_foo = "";
+ lt_count = 0;
for (lt_i = NF; lt_i > 0; lt_i--) {
if ($lt_i != "" && $lt_i != ".") {
if ($lt_i == "..") {
lt_count++;
} else {
if (lt_count == 0) {
- lt_foo="/" $lt_i lt_foo;
+ lt_foo = "/" $lt_i lt_foo;
} else {
lt_count--;
}
@@ -9843,7 +10538,7 @@ BEGIN {RS=" "; FS="/|\n";} {
# for these hosts.
case $host_os in
mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\
- $SED 's,/\([A-Za-z]:\),\1,g'` ;;
+ $SED 's|/\([A-Za-z]:\)|\1|g'` ;;
esac
sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP`
else
@@ -9852,7 +10547,7 @@ fi
library_names_spec=
libname_spec='lib$name'
soname_spec=
-shrext_cmds=".so"
+shrext_cmds=.so
postinstall_cmds=
postuninstall_cmds=
finish_cmds=
@@ -9869,56 +10564,108 @@ hardcode_into_libs=no
# flags to be left without arguments
need_version=unknown
+
+
case $host_os in
aix3*)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname.a'
shlibpath_var=LIBPATH
# AIX 3 has no versioning support, so we append a major version to the name.
- soname_spec='${libname}${release}${shared_ext}$major'
+ soname_spec='$libname$release$shared_ext$major'
;;
aix[4-9]*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
hardcode_into_libs=yes
- if test "$host_cpu" = ia64; then
+ if test ia64 = "$host_cpu"; then
# AIX 5 supports IA64
- library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
else
# With GCC up to 2.95.x, collect2 would create an import file
# for dependence libraries. The import file would start with
- # the line `#! .'. This would cause the generated library to
- # depend on `.', always an invalid library. This was fixed in
+ # the line '#! .'. This would cause the generated library to
+ # depend on '.', always an invalid library. This was fixed in
# development snapshots of GCC prior to 3.0.
case $host_os in
aix4 | aix4.[01] | aix4.[01].*)
if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)'
echo ' yes '
- echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then
+ echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then
:
else
can_build_shared=no
fi
;;
esac
- # AIX (on Power*) has no versioning support, so currently we can not hardcode correct
+ # Using Import Files as archive members, it is possible to support
+ # filename-based versioning of shared library archives on AIX. While
+ # this would work for both with and without runtime linking, it will
+ # prevent static linking of such archives. So we do filename-based
+ # shared library versioning with .so extension only, which is used
+ # when both runtime linking and shared linking is enabled.
+ # Unfortunately, runtime linking may impact performance, so we do
+ # not want this to be the default eventually. Also, we use the
+ # versioned .so libs for executables only if there is the -brtl
+ # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only.
+ # To allow for filename-based versioning support, we need to create
+ # libNAME.so.V as an archive file, containing:
+ # *) an Import File, referring to the versioned filename of the
+ # archive as well as the shared archive member, telling the
+ # bitwidth (32 or 64) of that shared object, and providing the
+ # list of exported symbols of that shared object, eventually
+ # decorated with the 'weak' keyword
+ # *) the shared object with the F_LOADONLY flag set, to really avoid
+ # it being seen by the linker.
+ # At run time we better use the real file rather than another symlink,
+ # but for link time we create the symlink libNAME.so -> libNAME.so.V
+
+ case $with_aix_soname,$aix_use_runtimelinking in
+ # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct
# soname into executable. Probably we can add versioning support to
# collect2, so additional links can be useful in future.
- if test "$aix_use_runtimelinking" = yes; then
+ aix,yes) # traditional libtool
+ dynamic_linker='AIX unversionable lib.so'
# If using run time linking (on AIX 4.2 or later) use lib<name>.so
# instead of lib<name>.a to let people know that these are not
# typical AIX shared libraries.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- else
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ ;;
+ aix,no) # traditional AIX only
+ dynamic_linker='AIX lib.a(lib.so.V)'
# We preserve .a as extension for shared libraries through AIX4.2
# and later when we are not doing run time linking.
- library_names_spec='${libname}${release}.a $libname.a'
- soname_spec='${libname}${release}${shared_ext}$major'
- fi
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ ;;
+ svr4,*) # full svr4 only
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,yes) # both, prefer svr4
+ dynamic_linker="AIX lib.so.V($shared_archive_member_spec.o), lib.a(lib.so.V)"
+ library_names_spec='$libname$release$shared_ext$major $libname$shared_ext'
+ # unpreferred sharedlib libNAME.a needs extra handling
+ postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"'
+ postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"'
+ # We do not specify a path in Import Files, so LIBPATH fires.
+ shlibpath_overrides_runpath=yes
+ ;;
+ *,no) # both, prefer aix
+ dynamic_linker="AIX lib.a(lib.so.V), lib.so.V($shared_archive_member_spec.o)"
+ library_names_spec='$libname$release.a $libname.a'
+ soname_spec='$libname$release$shared_ext$major'
+ # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling
+ postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)'
+ postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"'
+ ;;
+ esac
shlibpath_var=LIBPATH
fi
;;
@@ -9928,27 +10675,27 @@ amigaos*)
powerpc)
# Since July 2007 AmigaOS4 officially supports .so libraries.
# When compiling the executable, add -use-dynld -Lsobjs: to the compileline.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
;;
m68k)
library_names_spec='$libname.ixlibrary $libname.a'
# Create ${libname}_ixlibrary.a entries in /sys/libs.
- finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
+ finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done'
;;
esac
;;
beos*)
- library_names_spec='${libname}${shared_ext}'
+ library_names_spec='$libname$shared_ext'
dynamic_linker="$host_os ld.so"
shlibpath_var=LIBRARY_PATH
;;
bsdi[45]*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib"
@@ -9960,7 +10707,7 @@ bsdi[45]*)
cygwin* | mingw* | pw32* | cegcc*)
version_type=windows
- shrext_cmds=".dll"
+ shrext_cmds=.dll
need_version=no
need_lib_prefix=no
@@ -9969,8 +10716,8 @@ cygwin* | mingw* | pw32* | cegcc*)
# gcc
library_names_spec='$libname.dll.a'
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname~
@@ -9986,17 +10733,17 @@ cygwin* | mingw* | pw32* | cegcc*)
case $host_os in
cygwin*)
# Cygwin DLLs use 'cyg' prefix rather than 'lib'
- soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"
;;
mingw* | cegcc*)
# MinGW DLLs use traditional 'lib' prefix
- soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
pw32*)
# pw32 DLLs use 'pw' prefix rather than 'lib'
- library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
+ library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
;;
esac
dynamic_linker='Win32 ld.exe'
@@ -10005,8 +10752,8 @@ cygwin* | mingw* | pw32* | cegcc*)
*,cl*)
# Native MSVC
libname_spec='$name'
- soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}'
- library_names_spec='${libname}.dll.lib'
+ soname_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext'
+ library_names_spec='$libname.dll.lib'
case $build_os in
mingw*)
@@ -10033,7 +10780,7 @@ cygwin* | mingw* | pw32* | cegcc*)
sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"`
;;
*)
- sys_lib_search_path_spec="$LIB"
+ sys_lib_search_path_spec=$LIB
if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then
# It is most probably a Windows format PATH.
sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'`
@@ -10046,8 +10793,8 @@ cygwin* | mingw* | pw32* | cegcc*)
esac
# DLL is installed to $(libdir)/../bin by postinstall_cmds
- postinstall_cmds='base_file=`basename \${file}`~
- dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~
dldir=$destdir/`dirname \$dlpath`~
test -d \$dldir || mkdir -p \$dldir~
$install_prog $dir/$dlname \$dldir/$dlname'
@@ -10060,7 +10807,7 @@ cygwin* | mingw* | pw32* | cegcc*)
*)
# Assume MSVC wrapper
- library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib'
+ library_names_spec='$libname`echo $release | $SED -e 's/[.]/-/g'`$versuffix$shared_ext $libname.lib'
dynamic_linker='Win32 ld.exe'
;;
esac
@@ -10073,8 +10820,8 @@ darwin* | rhapsody*)
version_type=darwin
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext'
- soname_spec='${libname}${release}${major}$shared_ext'
+ library_names_spec='$libname$release$major$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$major$shared_ext'
shlibpath_overrides_runpath=yes
shlibpath_var=DYLD_LIBRARY_PATH
shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`'
@@ -10084,18 +10831,14 @@ darwin* | rhapsody*)
;;
dgux*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
-freebsd1*)
- dynamic_linker=no
- ;;
-
freebsd* | dragonfly*)
# DragonFly does not have aout. When/if they implement a new
# versioning mechanism, adjust this.
@@ -10103,25 +10846,26 @@ freebsd* | dragonfly*)
objformat=`/usr/bin/objformat`
else
case $host_os in
- freebsd[123]*) objformat=aout ;;
+ freebsd[23].*) objformat=aout ;;
*) objformat=elf ;;
esac
fi
version_type=freebsd-$objformat
case $version_type in
freebsd-elf*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
need_version=no
need_lib_prefix=no
;;
freebsd-*)
- library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
need_version=yes
;;
esac
shlibpath_var=LD_LIBRARY_PATH
case $host_os in
- freebsd2*)
+ freebsd2.*)
shlibpath_overrides_runpath=yes
;;
freebsd3.[01]* | freebsdelf3.[01]*)
@@ -10140,25 +10884,15 @@ freebsd* | dragonfly*)
esac
;;
-gnu*)
- version_type=linux
- need_lib_prefix=no
- need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- shlibpath_var=LD_LIBRARY_PATH
- hardcode_into_libs=yes
- ;;
-
haiku*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
dynamic_linker="$host_os runtime_loader"
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LIBRARY_PATH
- shlibpath_overrides_runpath=yes
+ shlibpath_overrides_runpath=no
sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib'
hardcode_into_libs=yes
;;
@@ -10176,14 +10910,15 @@ hpux9* | hpux10* | hpux11*)
dynamic_linker="$host_os dld.so"
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
- if test "X$HPUX_IA64_MODE" = X32; then
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
+ if test 32 = "$HPUX_IA64_MODE"; then
sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux32
else
sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64"
+ sys_lib_dlsearch_path_spec=/usr/lib/hpux64
fi
- sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
hppa*64*)
shrext_cmds='.sl'
@@ -10191,8 +10926,8 @@ hpux9* | hpux10* | hpux11*)
dynamic_linker="$host_os dld.sl"
shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH
shlibpath_overrides_runpath=yes # Unless +noenvvar is specified.
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64"
sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
@@ -10201,8 +10936,8 @@ hpux9* | hpux10* | hpux11*)
dynamic_linker="$host_os dld.sl"
shlibpath_var=SHLIB_PATH
shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
;;
esac
# HP-UX runs *really* slowly unless shared libraries are mode 555, ...
@@ -10212,11 +10947,11 @@ hpux9* | hpux10* | hpux11*)
;;
interix[3-9]*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
@@ -10227,16 +10962,16 @@ irix5* | irix6* | nonstopux*)
case $host_os in
nonstopux*) version_type=nonstopux ;;
*)
- if test "$lt_cv_prog_gnu_ld" = yes; then
- version_type=linux
+ if test yes = "$lt_cv_prog_gnu_ld"; then
+ version_type=linux # correct to gnu/linux during the next big refactor
else
version_type=irix
fi ;;
esac
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext'
case $host_os in
irix5* | nonstopux*)
libsuff= shlibsuff=
@@ -10255,8 +10990,8 @@ irix5* | irix6* | nonstopux*)
esac
shlibpath_var=LD_LIBRARY${shlibsuff}_PATH
shlibpath_overrides_runpath=no
- sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}"
- sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}"
+ sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff"
+ sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff"
hardcode_into_libs=yes
;;
@@ -10265,19 +11000,39 @@ linux*oldld* | linux*aout* | linux*coff*)
dynamic_linker=no
;;
-# This must be Linux ELF.
-linux* | k*bsd*-gnu | kopensolaris*-gnu)
- version_type=linux
+linux*android*)
+ version_type=none # Android doesn't support versioned libraries.
+ need_lib_prefix=no
+ need_version=no
+ library_names_spec='$libname$release$shared_ext'
+ soname_spec='$libname$release$shared_ext'
+ finish_cmds=
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
+
+ # This implies no fast_install, which is unacceptable.
+ # Some rework will be needed to allow for fast_install
+ # before this can be enabled.
+ hardcode_into_libs=yes
+
+ dynamic_linker='Android linker'
+ # Don't embed -rpath directories since the linker doesn't support them.
+ hardcode_libdir_flag_spec='-L$libdir'
+ ;;
+
+# This must be glibc/ELF.
+linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
# Some binutils ld are patched to set DT_RUNPATH
- if test "${lt_cv_shlibpath_overrides_runpath+set}" = set; then :
+ if ${lt_cv_shlibpath_overrides_runpath+:} false; then :
$as_echo_n "(cached) " >&6
else
lt_cv_shlibpath_overrides_runpath=no
@@ -10315,7 +11070,12 @@ fi
# before this can be enabled.
hardcode_into_libs=yes
- # Append ld.so.conf contents to the search path
+ # Ideally, we could use ldconfig to report *all* directores which are
+ # searched for libraries, however this is still not possible. Aside from not
+ # being certain /sbin/ldconfig is available, command
+ # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64,
+ # even though it is searched at run-time. Try to do the best guess by
+ # appending ld.so.conf contents (and includes) to the search path.
if test -f /etc/ld.so.conf; then
lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '`
sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra"
@@ -10335,12 +11095,12 @@ netbsd*)
need_lib_prefix=no
need_version=no
if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
dynamic_linker='NetBSD (a.out) ld.so'
else
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
dynamic_linker='NetBSD ld.elf_so'
fi
shlibpath_var=LD_LIBRARY_PATH
@@ -10349,8 +11109,8 @@ netbsd*)
;;
newsos6)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
;;
@@ -10359,58 +11119,68 @@ newsos6)
version_type=qnx
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
dynamic_linker='ldqnx.so'
;;
-openbsd*)
+openbsd* | bitrig*)
version_type=sunos
- sys_lib_dlsearch_path_spec="/usr/lib"
+ sys_lib_dlsearch_path_spec=/usr/lib
need_lib_prefix=no
- # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs.
- case $host_os in
- openbsd3.3 | openbsd3.3.*) need_version=yes ;;
- *) need_version=no ;;
- esac
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
- finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
- shlibpath_var=LD_LIBRARY_PATH
- if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
- case $host_os in
- openbsd2.[89] | openbsd2.[89].*)
- shlibpath_overrides_runpath=no
- ;;
- *)
- shlibpath_overrides_runpath=yes
- ;;
- esac
+ if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then
+ need_version=no
else
- shlibpath_overrides_runpath=yes
+ need_version=yes
fi
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
+ finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir'
+ shlibpath_var=LD_LIBRARY_PATH
+ shlibpath_overrides_runpath=yes
;;
os2*)
libname_spec='$name'
- shrext_cmds=".dll"
+ version_type=windows
+ shrext_cmds=.dll
+ need_version=no
need_lib_prefix=no
- library_names_spec='$libname${shared_ext} $libname.a'
+ # OS/2 can only load a DLL with a base name of 8 characters or less.
+ soname_spec='`test -n "$os2dllname" && libname="$os2dllname";
+ v=$($ECHO $release$versuffix | tr -d .-);
+ n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _);
+ $ECHO $n$v`$shared_ext'
+ library_names_spec='${libname}_dll.$libext'
dynamic_linker='OS/2 ld.exe'
- shlibpath_var=LIBPATH
+ shlibpath_var=BEGINLIBPATH
+ sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
+ postinstall_cmds='base_file=`basename \$file`~
+ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~
+ dldir=$destdir/`dirname \$dlpath`~
+ test -d \$dldir || mkdir -p \$dldir~
+ $install_prog $dir/$dlname \$dldir/$dlname~
+ chmod a+x \$dldir/$dlname~
+ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then
+ eval '\''$striplib \$dldir/$dlname'\'' || exit \$?;
+ fi'
+ postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~
+ dlpath=$dir/\$dldll~
+ $RM \$dlpath'
;;
osf3* | osf4* | osf5*)
version_type=osf
need_lib_prefix=no
need_version=no
- soname_spec='${libname}${release}${shared_ext}$major'
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ soname_spec='$libname$release$shared_ext$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib"
- sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec"
+ sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec
;;
rdos*)
@@ -10418,11 +11188,11 @@ rdos*)
;;
solaris*)
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
@@ -10432,20 +11202,20 @@ solaris*)
sunos4*)
version_type=sunos
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix'
finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
need_lib_prefix=no
fi
need_version=yes
;;
sysv4 | sysv4.3*)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
case $host_vendor in
sni)
@@ -10466,24 +11236,24 @@ sysv4 | sysv4.3*)
;;
sysv4*MP*)
- if test -d /usr/nec ;then
- version_type=linux
- library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}'
- soname_spec='$libname${shared_ext}.$major'
+ if test -d /usr/nec; then
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext'
+ soname_spec='$libname$shared_ext.$major'
shlibpath_var=LD_LIBRARY_PATH
fi
;;
sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
- version_type=freebsd-elf
+ version_type=sco
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=yes
hardcode_into_libs=yes
- if test "$with_gnu_ld" = yes; then
+ if test yes = "$with_gnu_ld"; then
sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib'
else
sys_lib_search_path_spec='/usr/ccs/lib /usr/lib'
@@ -10498,19 +11268,19 @@ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
tpf*)
# TPF is a cross-target only. Preferred cross-host = GNU/Linux.
- version_type=linux
+ version_type=linux # correct to gnu/linux during the next big refactor
need_lib_prefix=no
need_version=no
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
shlibpath_var=LD_LIBRARY_PATH
shlibpath_overrides_runpath=no
hardcode_into_libs=yes
;;
uts4*)
- version_type=linux
- library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}'
- soname_spec='${libname}${release}${shared_ext}$major'
+ version_type=linux # correct to gnu/linux during the next big refactor
+ library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext'
+ soname_spec='$libname$release$shared_ext$major'
shlibpath_var=LD_LIBRARY_PATH
;;
@@ -10520,20 +11290,35 @@ uts4*)
esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5
$as_echo "$dynamic_linker" >&6; }
-test "$dynamic_linker" = no && can_build_shared=no
+test no = "$dynamic_linker" && can_build_shared=no
variables_saved_for_relink="PATH $shlibpath_var $runpath_var"
-if test "$GCC" = yes; then
+if test yes = "$GCC"; then
variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH"
fi
-if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then
- sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec"
+if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then
+ sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec
fi
-if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then
- sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec"
+
+if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then
+ sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec
fi
+# remember unaugmented sys_lib_dlsearch_path content for libtool script decls...
+configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec
+
+# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code
+func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH"
+
+# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool
+configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH
+
+
+
+
+
+
@@ -10630,15 +11415,15 @@ $as_echo_n "checking how to hardcode library paths into programs... " >&6; }
hardcode_action=
if test -n "$hardcode_libdir_flag_spec" ||
test -n "$runpath_var" ||
- test "X$hardcode_automatic" = "Xyes" ; then
+ test yes = "$hardcode_automatic"; then
# We can hardcode non-existent directories.
- if test "$hardcode_direct" != no &&
+ if test no != "$hardcode_direct" &&
# If the only mechanism to avoid hardcoding is shlibpath_var, we
# have to relink, otherwise we might link with an installed library
# when we should be linking with a yet-to-be-installed one
- ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no &&
- test "$hardcode_minus_L" != no; then
+ ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, )" &&
+ test no != "$hardcode_minus_L"; then
# Linking always hardcodes the temporary library directory.
hardcode_action=relink
else
@@ -10653,12 +11438,12 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5
$as_echo "$hardcode_action" >&6; }
-if test "$hardcode_action" = relink ||
- test "$inherit_rpath" = yes; then
+if test relink = "$hardcode_action" ||
+ test yes = "$inherit_rpath"; then
# Fast installation is not supported
enable_fast_install=no
-elif test "$shlibpath_overrides_runpath" = yes ||
- test "$enable_shared" = no; then
+elif test yes = "$shlibpath_overrides_runpath" ||
+ test no = "$enable_shared"; then
# Fast installation is not necessary
enable_fast_install=needless
fi
@@ -10668,7 +11453,7 @@ fi
- if test "x$enable_dlopen" != xyes; then
+ if test yes != "$enable_dlopen"; then
enable_dlopen=unknown
enable_dlopen_self=unknown
enable_dlopen_self_static=unknown
@@ -10678,26 +11463,26 @@ else
case $host_os in
beos*)
- lt_cv_dlopen="load_add_on"
+ lt_cv_dlopen=load_add_on
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
;;
mingw* | pw32* | cegcc*)
- lt_cv_dlopen="LoadLibrary"
+ lt_cv_dlopen=LoadLibrary
lt_cv_dlopen_libs=
;;
cygwin*)
- lt_cv_dlopen="dlopen"
+ lt_cv_dlopen=dlopen
lt_cv_dlopen_libs=
;;
darwin*)
- # if libdl is installed we need to link against it
+ # if libdl is installed we need to link against it
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
-if test "${ac_cv_lib_dl_dlopen+set}" = set; then :
+if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -10731,11 +11516,11 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
-if test "x$ac_cv_lib_dl_dlopen" = x""yes; then :
- lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
else
- lt_cv_dlopen="dyld"
+ lt_cv_dlopen=dyld
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
@@ -10743,14 +11528,22 @@ fi
;;
+ tpf*)
+ # Don't try to run any link tests for TPF. We know it's impossible
+ # because TPF is a cross-compiler, and we know how we open DSOs.
+ lt_cv_dlopen=dlopen
+ lt_cv_dlopen_libs=
+ lt_cv_dlopen_self=no
+ ;;
+
*)
ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load"
-if test "x$ac_cv_func_shl_load" = x""yes; then :
- lt_cv_dlopen="shl_load"
+if test "x$ac_cv_func_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
$as_echo_n "checking for shl_load in -ldld... " >&6; }
-if test "${ac_cv_lib_dld_shl_load+set}" = set; then :
+if ${ac_cv_lib_dld_shl_load+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -10784,16 +11577,16 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
$as_echo "$ac_cv_lib_dld_shl_load" >&6; }
-if test "x$ac_cv_lib_dld_shl_load" = x""yes; then :
- lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"
+if test "x$ac_cv_lib_dld_shl_load" = xyes; then :
+ lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
else
ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
-if test "x$ac_cv_func_dlopen" = x""yes; then :
- lt_cv_dlopen="dlopen"
+if test "x$ac_cv_func_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
$as_echo_n "checking for dlopen in -ldl... " >&6; }
-if test "${ac_cv_lib_dl_dlopen+set}" = set; then :
+if ${ac_cv_lib_dl_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -10827,12 +11620,12 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
$as_echo "$ac_cv_lib_dl_dlopen" >&6; }
-if test "x$ac_cv_lib_dl_dlopen" = x""yes; then :
- lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"
+if test "x$ac_cv_lib_dl_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
$as_echo_n "checking for dlopen in -lsvld... " >&6; }
-if test "${ac_cv_lib_svld_dlopen+set}" = set; then :
+if ${ac_cv_lib_svld_dlopen+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -10866,12 +11659,12 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
$as_echo "$ac_cv_lib_svld_dlopen" >&6; }
-if test "x$ac_cv_lib_svld_dlopen" = x""yes; then :
- lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"
+if test "x$ac_cv_lib_svld_dlopen" = xyes; then :
+ lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
$as_echo_n "checking for dld_link in -ldld... " >&6; }
-if test "${ac_cv_lib_dld_dld_link+set}" = set; then :
+if ${ac_cv_lib_dld_dld_link+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -10905,8 +11698,8 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
$as_echo "$ac_cv_lib_dld_dld_link" >&6; }
-if test "x$ac_cv_lib_dld_dld_link" = x""yes; then :
- lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"
+if test "x$ac_cv_lib_dld_dld_link" = xyes; then :
+ lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
fi
@@ -10927,29 +11720,29 @@ fi
;;
esac
- if test "x$lt_cv_dlopen" != xno; then
- enable_dlopen=yes
- else
+ if test no = "$lt_cv_dlopen"; then
enable_dlopen=no
+ else
+ enable_dlopen=yes
fi
case $lt_cv_dlopen in
dlopen)
- save_CPPFLAGS="$CPPFLAGS"
- test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
+ save_CPPFLAGS=$CPPFLAGS
+ test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H"
- save_LDFLAGS="$LDFLAGS"
+ save_LDFLAGS=$LDFLAGS
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\"
- save_LIBS="$LIBS"
+ save_LIBS=$LIBS
LIBS="$lt_cv_dlopen_libs $LIBS"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5
$as_echo_n "checking whether a program can dlopen itself... " >&6; }
-if test "${lt_cv_dlopen_self+set}" = set; then :
+if ${lt_cv_dlopen_self+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test "$cross_compiling" = yes; then :
+ if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
@@ -10996,9 +11789,9 @@ else
# endif
#endif
-/* When -fvisbility=hidden is used, assume the code has been annotated
+/* When -fvisibility=hidden is used, assume the code has been annotated
correspondingly for the symbols needed. */
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
int fnord () __attribute__((visibility("default")));
#endif
@@ -11028,7 +11821,7 @@ _LT_EOF
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
(./conftest; exit; ) >&5 2>/dev/null
lt_status=$?
case x$lt_status in
@@ -11048,14 +11841,14 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
$as_echo "$lt_cv_dlopen_self" >&6; }
- if test "x$lt_cv_dlopen_self" = xyes; then
+ if test yes = "$lt_cv_dlopen_self"; then
wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5
$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; }
-if test "${lt_cv_dlopen_self_static+set}" = set; then :
+if ${lt_cv_dlopen_self_static+:} false; then :
$as_echo_n "(cached) " >&6
else
- if test "$cross_compiling" = yes; then :
+ if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self_static=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
@@ -11102,9 +11895,9 @@ else
# endif
#endif
-/* When -fvisbility=hidden is used, assume the code has been annotated
+/* When -fvisibility=hidden is used, assume the code has been annotated
correspondingly for the symbols needed. */
-#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
+#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3))
int fnord () __attribute__((visibility("default")));
#endif
@@ -11134,7 +11927,7 @@ _LT_EOF
(eval $ac_link) 2>&5
ac_status=$?
$as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then
+ test $ac_status = 0; } && test -s "conftest$ac_exeext" 2>/dev/null; then
(./conftest; exit; ) >&5 2>/dev/null
lt_status=$?
case x$lt_status in
@@ -11155,9 +11948,9 @@ fi
$as_echo "$lt_cv_dlopen_self_static" >&6; }
fi
- CPPFLAGS="$save_CPPFLAGS"
- LDFLAGS="$save_LDFLAGS"
- LIBS="$save_LIBS"
+ CPPFLAGS=$save_CPPFLAGS
+ LDFLAGS=$save_LDFLAGS
+ LIBS=$save_LIBS
;;
esac
@@ -11201,7 +11994,7 @@ else
# FIXME - insert some real tests, host_os isn't really good enough
case $host_os in
darwin*)
- if test -n "$STRIP" ; then
+ if test -n "$STRIP"; then
striplib="$STRIP -x"
old_striplib="$STRIP -S"
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
@@ -11229,7 +12022,7 @@ fi
- # Report which library types will actually be built
+ # Report what library types will actually be built
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5
$as_echo_n "checking if libtool supports shared libraries... " >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5
@@ -11237,13 +12030,13 @@ $as_echo "$can_build_shared" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5
$as_echo_n "checking whether to build shared libraries... " >&6; }
- test "$can_build_shared" = "no" && enable_shared=no
+ test no = "$can_build_shared" && enable_shared=no
# On AIX, shared libraries and static libraries use the same namespace, and
# are all built from PIC.
case $host_os in
aix3*)
- test "$enable_shared" = yes && enable_static=no
+ test yes = "$enable_shared" && enable_static=no
if test -n "$RANLIB"; then
archive_cmds="$archive_cmds~\$RANLIB \$lib"
postinstall_cmds='$RANLIB $lib'
@@ -11251,8 +12044,12 @@ $as_echo_n "checking whether to build shared libraries... " >&6; }
;;
aix[4-9]*)
- if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then
- test "$enable_shared" = yes && enable_static=no
+ if test ia64 != "$host_cpu"; then
+ case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in
+ yes,aix,yes) ;; # shared object as lib.so file only
+ yes,svr4,*) ;; # shared object as lib.so archive member only
+ yes,*) enable_static=no ;; # shared object in lib.a archive as well
+ esac
fi
;;
esac
@@ -11262,7 +12059,7 @@ $as_echo "$enable_shared" >&6; }
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5
$as_echo_n "checking whether to build static libraries... " >&6; }
# Make sure either enable_shared or enable_static is yes.
- test "$enable_shared" = yes || enable_static=yes
+ test yes = "$enable_shared" || enable_static=yes
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5
$as_echo "$enable_static" >&6; }
@@ -11276,7 +12073,9 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
-CC="$lt_save_CC"
+CC=$lt_save_CC
+
+
@@ -11308,18 +12107,45 @@ if test "${enable_silent_rules+set}" = set; then :
enableval=$enable_silent_rules;
fi
-case $enable_silent_rules in
-yes) AM_DEFAULT_VERBOSITY=0;;
-no) AM_DEFAULT_VERBOSITY=1;;
-*) AM_DEFAULT_VERBOSITY=0;;
+case $enable_silent_rules in # (((
+ yes) AM_DEFAULT_VERBOSITY=0;;
+ no) AM_DEFAULT_VERBOSITY=1;;
+ *) AM_DEFAULT_VERBOSITY=0;;
esac
+am_make=${MAKE-make}
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5
+$as_echo_n "checking whether $am_make supports nested variables... " >&6; }
+if ${am_cv_make_support_nested_variables+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if $as_echo 'TRUE=$(BAR$(V))
+BAR0=false
+BAR1=true
+V=1
+am__doit:
+ @$(TRUE)
+.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then
+ am_cv_make_support_nested_variables=yes
+else
+ am_cv_make_support_nested_variables=no
+fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5
+$as_echo "$am_cv_make_support_nested_variables" >&6; }
+if test $am_cv_make_support_nested_variables = yes; then
+ AM_V='$(V)'
+ AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)'
+else
+ AM_V=$AM_DEFAULT_VERBOSITY
+ AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY
+fi
AM_BACKSLASH='\'
# Checks for programs.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5
$as_echo_n "checking for $CC option to accept ISO C99... " >&6; }
-if test "${ac_cv_prog_cc_c99+set}" = set; then :
+if ${ac_cv_prog_cc_c99+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_cv_prog_cc_c99=no
@@ -11463,7 +12289,7 @@ main ()
return 0;
}
_ACEOF
-for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -xc99=all -qlanglvl=extc99
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99
do
CC="$ac_save_CC $ac_arg"
if ac_fn_c_try_compile "$LINENO"; then :
@@ -11512,7 +12338,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_prog_AWK+set}" = set; then :
+if ${ac_cv_prog_AWK+:} false; then :
$as_echo_n "(cached) " >&6
else
if test -n "$AWK"; then
@@ -11524,7 +12350,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_prog_AWK="$ac_prog"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -11557,7 +12383,7 @@ do
set dummy $ac_prog; ac_word=$2
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
$as_echo_n "checking for $ac_word... " >&6; }
-if test "${ac_cv_path_NROFF+set}" = set; then :
+if ${ac_cv_path_NROFF+:} false; then :
$as_echo_n "(cached) " >&6
else
case $NROFF in
@@ -11571,7 +12397,7 @@ do
IFS=$as_save_IFS
test -z "$as_dir" && as_dir=.
for ac_exec_ext in '' $ac_executable_extensions; do
- if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
ac_cv_path_NROFF="$as_dir/$ac_word$ac_exec_ext"
$as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
break 2
@@ -11606,7 +12432,7 @@ test -n "$NROFF" || NROFF="/bin/false"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -lcurses" >&5
$as_echo_n "checking for tgetent in -lcurses... " >&6; }
-if test "${ac_cv_lib_curses_tgetent+set}" = set; then :
+if ${ac_cv_lib_curses_tgetent+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -11640,7 +12466,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_curses_tgetent" >&5
$as_echo "$ac_cv_lib_curses_tgetent" >&6; }
-if test "x$ac_cv_lib_curses_tgetent" = x""yes; then :
+if test "x$ac_cv_lib_curses_tgetent" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBCURSES 1
_ACEOF
@@ -11650,7 +12476,7 @@ _ACEOF
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for tgetent in -lncurses" >&5
$as_echo_n "checking for tgetent in -lncurses... " >&6; }
-if test "${ac_cv_lib_ncurses_tgetent+set}" = set; then :
+if ${ac_cv_lib_ncurses_tgetent+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_check_lib_save_LIBS=$LIBS
@@ -11684,7 +12510,7 @@ LIBS=$ac_check_lib_save_LIBS
fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_tgetent" >&5
$as_echo "$ac_cv_lib_ncurses_tgetent" >&6; }
-if test "x$ac_cv_lib_ncurses_tgetent" = x""yes; then :
+if test "x$ac_cv_lib_ncurses_tgetent" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_LIBNCURSES 1
_ACEOF
@@ -11692,238 +12518,20 @@ _ACEOF
LIBS="-lncurses $LIBS"
else
- as_fn_error "libcurses or libncurses are required!" "$LINENO" 5
-fi
-
-fi
-
-
-
-### use option --enable-widec to turn on use of wide-character support
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking if you want wide-character code" >&5
-$as_echo_n "checking if you want wide-character code... " >&6; }
- # Check whether --enable-widec was given.
-if test "${enable_widec+set}" = set; then :
- enableval=$enable_widec; with_widec=$enableval
-else
- with_widec=no
+ as_fn_error $? "libcurses or libncurses are required!" "$LINENO" 5
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_widec" >&5
-$as_echo "$with_widec" >&6; }
- if test "$with_widec" = yes ; then
-
-$as_echo "#define WIDECHAR 1" >>confdefs.h
-
- fi
- if test "$with_widec" = yes; then
- WIDECHAR_TRUE=
- WIDECHAR_FALSE='#'
-else
- WIDECHAR_TRUE='#'
- WIDECHAR_FALSE=
fi
# Checks for header files.
-# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
-# for constant arguments. Useless!
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working alloca.h" >&5
-$as_echo_n "checking for working alloca.h... " >&6; }
-if test "${ac_cv_working_alloca_h+set}" = set; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <alloca.h>
-int
-main ()
-{
-char *p = (char *) alloca (2 * sizeof (int));
- if (p) return 0;
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_working_alloca_h=yes
-else
- ac_cv_working_alloca_h=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_working_alloca_h" >&5
-$as_echo "$ac_cv_working_alloca_h" >&6; }
-if test $ac_cv_working_alloca_h = yes; then
-
-$as_echo "#define HAVE_ALLOCA_H 1" >>confdefs.h
-
-fi
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for alloca" >&5
-$as_echo_n "checking for alloca... " >&6; }
-if test "${ac_cv_func_alloca_works+set}" = set; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#ifdef __GNUC__
-# define alloca __builtin_alloca
-#else
-# ifdef _MSC_VER
-# include <malloc.h>
-# define alloca _alloca
-# else
-# ifdef HAVE_ALLOCA_H
-# include <alloca.h>
-# else
-# ifdef _AIX
- #pragma alloca
-# else
-# ifndef alloca /* predefined by HP cc +Olibcalls */
-char *alloca ();
-# endif
-# endif
-# endif
-# endif
-#endif
-
-int
-main ()
-{
-char *p = (char *) alloca (1);
- if (p) return 0;
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
- ac_cv_func_alloca_works=yes
-else
- ac_cv_func_alloca_works=no
-fi
-rm -f core conftest.err conftest.$ac_objext \
- conftest$ac_exeext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_alloca_works" >&5
-$as_echo "$ac_cv_func_alloca_works" >&6; }
-
-if test $ac_cv_func_alloca_works = yes; then
-
-$as_echo "#define HAVE_ALLOCA 1" >>confdefs.h
-
-else
- # The SVR3 libPW and SVR4 libucb both contain incompatible functions
-# that cause trouble. Some versions do not even contain alloca or
-# contain a buggy version. If you still want to use their alloca,
-# use ar to extract alloca.o from them instead of compiling alloca.c.
-
-ALLOCA=\${LIBOBJDIR}alloca.$ac_objext
-
-$as_echo "#define C_ALLOCA 1" >>confdefs.h
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether \`alloca.c' needs Cray hooks" >&5
-$as_echo_n "checking whether \`alloca.c' needs Cray hooks... " >&6; }
-if test "${ac_cv_os_cray+set}" = set; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#if defined CRAY && ! defined CRAY2
-webecray
-#else
-wenotbecray
-#endif
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "webecray" >/dev/null 2>&1; then :
- ac_cv_os_cray=yes
-else
- ac_cv_os_cray=no
-fi
-rm -f conftest*
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_os_cray" >&5
-$as_echo "$ac_cv_os_cray" >&6; }
-if test $ac_cv_os_cray = yes; then
- for ac_func in _getb67 GETB67 getb67; do
- as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
-ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
-eval as_val=\$$as_ac_var
- if test "x$as_val" = x""yes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define CRAY_STACKSEG_END $ac_func
-_ACEOF
-
- break
-fi
-
- done
-fi
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking stack direction for C alloca" >&5
-$as_echo_n "checking stack direction for C alloca... " >&6; }
-if test "${ac_cv_c_stack_direction+set}" = set; then :
- $as_echo_n "(cached) " >&6
-else
- if test "$cross_compiling" = yes; then :
- ac_cv_c_stack_direction=0
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-$ac_includes_default
-int
-find_stack_direction ()
-{
- static char *addr = 0;
- auto char dummy;
- if (addr == 0)
- {
- addr = &dummy;
- return find_stack_direction ();
- }
- else
- return (&dummy > addr) ? 1 : -1;
-}
-
-int
-main ()
-{
- return find_stack_direction () < 0;
-}
-_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
- ac_cv_c_stack_direction=1
-else
- ac_cv_c_stack_direction=-1
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_stack_direction" >&5
-$as_echo "$ac_cv_c_stack_direction" >&6; }
-cat >>confdefs.h <<_ACEOF
-#define STACK_DIRECTION $ac_cv_c_stack_direction
-_ACEOF
-
-
-fi
-
ac_header_dirent=no
for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h; do
as_ac_Header=`$as_echo "ac_cv_header_dirent_$ac_hdr" | $as_tr_sh`
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_hdr that defines DIR" >&5
$as_echo_n "checking for $ac_hdr that defines DIR... " >&6; }
-if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then :
+if eval \${$as_ac_Header+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -11950,8 +12558,7 @@ fi
eval ac_res=\$$as_ac_Header
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
$as_echo "$ac_res" >&6; }
-eval as_val=\$$as_ac_Header
- if test "x$as_val" = x""yes; then :
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_hdr" | $as_tr_cpp` 1
_ACEOF
@@ -11964,7 +12571,7 @@ done
if test $ac_header_dirent = dirent.h; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
$as_echo_n "checking for library containing opendir... " >&6; }
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -11998,11 +12605,11 @@ for ac_lib in '' dir; do
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
- if test "${ac_cv_search_opendir+set}" = set; then :
+ if ${ac_cv_search_opendir+:} false; then :
break
fi
done
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
else
ac_cv_search_opendir=no
@@ -12021,7 +12628,7 @@ fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing opendir" >&5
$as_echo_n "checking for library containing opendir... " >&6; }
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_func_search_save_LIBS=$LIBS
@@ -12055,11 +12662,11 @@ for ac_lib in '' x; do
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext
- if test "${ac_cv_search_opendir+set}" = set; then :
+ if ${ac_cv_search_opendir+:} false; then :
break
fi
done
-if test "${ac_cv_search_opendir+set}" = set; then :
+if ${ac_cv_search_opendir+:} false; then :
else
ac_cv_search_opendir=no
@@ -12079,7 +12686,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
$as_echo_n "checking for ANSI C header files... " >&6; }
-if test "${ac_cv_header_stdc+set}" = set; then :
+if ${ac_cv_header_stdc+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -12191,7 +12798,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
-if test "${ac_cv_header_sys_wait_h+set}" = set; then :
+if ${ac_cv_header_sys_wait_h+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -12230,12 +12837,11 @@ $as_echo "#define HAVE_SYS_WAIT_H 1" >>confdefs.h
fi
-for ac_header in fcntl.h limits.h malloc.h stdlib.h string.h sys/ioctl.h sys/param.h unistd.h curses.h ncurses.h sys/cdefs.h
+for ac_header in fcntl.h limits.h stdint.h stdlib.h string.h sys/ioctl.h sys/param.h unistd.h curses.h ncurses.h sys/cdefs.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
-eval as_val=\$$as_ac_Header
- if test "x$as_val" = x""yes; then :
+if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
@@ -12246,10 +12852,10 @@ done
ac_fn_c_check_header_mongrel "$LINENO" "termios.h" "ac_cv_header_termios_h" "$ac_includes_default"
-if test "x$ac_cv_header_termios_h" = x""yes; then :
+if test "x$ac_cv_header_termios_h" = xyes; then :
else
- as_fn_error "termios.h is required!" "$LINENO" 5
+ as_fn_error $? "termios.h is required!" "$LINENO" 5
fi
@@ -12264,7 +12870,7 @@ do :
#endif
"
-if test "x$ac_cv_header_term_h" = x""yes; then :
+if test "x$ac_cv_header_term_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_TERM_H 1
_ACEOF
@@ -12277,7 +12883,7 @@ done
# Checks for typedefs, structures, and compiler characteristics.
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5
$as_echo_n "checking for an ANSI C-conforming const... " >&6; }
-if test "${ac_cv_c_const+set}" = set; then :
+if ${ac_cv_c_const+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -12286,11 +12892,11 @@ else
int
main ()
{
-/* FIXME: Include the comments suggested by Paul. */
+
#ifndef __cplusplus
- /* Ultrix mips cc rejects this. */
+ /* Ultrix mips cc rejects this sort of thing. */
typedef int charset[2];
- const charset cs;
+ const charset cs = { 0, 0 };
/* SunOS 4.1.1 cc rejects this. */
char const *const *pcpcc;
char **ppc;
@@ -12307,8 +12913,9 @@ main ()
++pcpcc;
ppc = (char**) pcpcc;
pcpcc = (char const *const *) ppc;
- { /* SCO 3.2v4 cc rejects this. */
- char *t;
+ { /* SCO 3.2v4 cc rejects this sort of thing. */
+ char tx;
+ char *t = &tx;
char const *s = 0 ? (char *) 0 : (char const *) 0;
*t++ = 0;
@@ -12324,10 +12931,10 @@ main ()
iptr p = 0;
++p;
}
- { /* AIX XL C 1.02.0.0 rejects this saying
+ { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying
"k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
- struct s { int j; const int *ap[3]; };
- struct s *b; b->j = 5;
+ struct s { int j; const int *ap[3]; } bx;
+ struct s *b = &bx; b->j = 5;
}
{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */
const int foo = 10;
@@ -12356,7 +12963,7 @@ $as_echo "#define const /**/" >>confdefs.h
fi
ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default"
-if test "x$ac_cv_type_pid_t" = x""yes; then :
+if test "x$ac_cv_type_pid_t" = xyes; then :
else
@@ -12367,7 +12974,7 @@ _ACEOF
fi
ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
-if test "x$ac_cv_type_size_t" = x""yes; then :
+if test "x$ac_cv_type_size_t" = xyes; then :
else
@@ -12378,7 +12985,7 @@ _ACEOF
fi
ac_fn_c_check_type "$LINENO" "u_int32_t" "ac_cv_type_u_int32_t" "$ac_includes_default"
-if test "x$ac_cv_type_u_int32_t" = x""yes; then :
+if test "x$ac_cv_type_u_int32_t" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_U_INT32_T 1
@@ -12388,53 +12995,40 @@ _ACEOF
fi
-# Checks for library functions.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether closedir returns void" >&5
-$as_echo_n "checking whether closedir returns void... " >&6; }
-if test "${ac_cv_func_closedir_void+set}" = set; then :
- $as_echo_n "(cached) " >&6
-else
- if test "$cross_compiling" = yes; then :
- ac_cv_func_closedir_void=yes
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-$ac_includes_default
-#include <$ac_header_dirent>
-#ifndef __cplusplus
-int closedir ();
+
+#include <limits.h>
+#include <stddef.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
#endif
int
main ()
{
-return closedir (opendir (".")) != 0;
+size_t x = SIZE_MAX;
;
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
- ac_cv_func_closedir_void=no
+if ac_fn_c_try_compile "$LINENO"; then :
+ have_size_max=yes
else
- ac_cv_func_closedir_void=yes
+ have_size_max=no
fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_closedir_void" >&5
-$as_echo "$ac_cv_func_closedir_void" >&6; }
-if test $ac_cv_func_closedir_void = yes; then
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+if test "$have_size_max" = yes; then
-$as_echo "#define CLOSEDIR_VOID 1" >>confdefs.h
+$as_echo "#define HAVE_SIZE_MAX 1" >>confdefs.h
fi
+# Checks for library functions.
for ac_header in vfork.h
do :
ac_fn_c_check_header_mongrel "$LINENO" "vfork.h" "ac_cv_header_vfork_h" "$ac_includes_default"
-if test "x$ac_cv_header_vfork_h" = x""yes; then :
+if test "x$ac_cv_header_vfork_h" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_VFORK_H 1
_ACEOF
@@ -12447,8 +13041,7 @@ for ac_func in fork vfork
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
-eval as_val=\$$as_ac_var
- if test "x$as_val" = x""yes; then :
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
@@ -12459,7 +13052,7 @@ done
if test "x$ac_cv_func_fork" = xyes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working fork" >&5
$as_echo_n "checking for working fork... " >&6; }
-if test "${ac_cv_func_fork_works+set}" = set; then :
+if ${ac_cv_func_fork_works+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
@@ -12512,7 +13105,7 @@ ac_cv_func_vfork_works=$ac_cv_func_vfork
if test "x$ac_cv_func_vfork" = xyes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for working vfork" >&5
$as_echo_n "checking for working vfork... " >&6; }
-if test "${ac_cv_func_vfork_works+set}" = set; then :
+if ${ac_cv_func_vfork_works+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
@@ -12648,7 +13241,7 @@ fi
if test $ac_cv_c_compiler_gnu = yes; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC needs -traditional" >&5
$as_echo_n "checking whether $CC needs -traditional... " >&6; }
-if test "${ac_cv_prog_gcc_traditional+set}" = set; then :
+if ${ac_cv_prog_gcc_traditional+:} false; then :
$as_echo_n "(cached) " >&6
else
ac_pattern="Autoconf.*'x'"
@@ -12692,7 +13285,7 @@ fi
#AC_FUNC_REALLOC
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5
$as_echo_n "checking return type of signal handlers... " >&6; }
-if test "${ac_cv_type_signal+set}" = set; then :
+if ${ac_cv_type_signal+:} false; then :
$as_echo_n "(cached) " >&6
else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -12725,7 +13318,7 @@ _ACEOF
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5
$as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; }
-if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then :
+if ${ac_cv_func_lstat_dereferences_slashed_symlink+:} false; then :
$as_echo_n "(cached) " >&6
else
rm -f conftest.sym conftest.file
@@ -12787,7 +13380,7 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stat accepts an empty string" >&5
$as_echo_n "checking whether stat accepts an empty string... " >&6; }
-if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then :
+if ${ac_cv_func_stat_empty_string_bug+:} false; then :
$as_echo_n "(cached) " >&6
else
if test "$cross_compiling" = yes; then :
@@ -12831,12 +13424,11 @@ _ACEOF
fi
-for ac_func in endpwent isascii memchr memset re_comp regcomp strcasecmp strchr strcspn strdup strerror strrchr strstr strtol issetugid wcsdup
+for ac_func in getline isascii issetugid wcsdup
do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
-eval as_val=\$$as_ac_var
- if test "x$as_val" = x""yes; then :
+if eval test \"x\$"$as_ac_var"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1
_ACEOF
@@ -12911,6 +13503,17 @@ rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
+ac_fn_c_check_member "$LINENO" "struct dirent" "d_namlen" "ac_cv_member_struct_dirent_d_namlen" "#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+
+"
+if test "x$ac_cv_member_struct_dirent_d_namlen" = xyes; then :
+
+$as_echo "#define HAVE_STRUCT_DIRENT_D_NAMLEN 1" >>confdefs.h
+
+fi
+
@@ -12980,10 +13583,21 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
:end' >>confcache
if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
if test -w "$cache_file"; then
- test "x$cache_file" != "x/dev/null" &&
+ if test "x$cache_file" != "x/dev/null"; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
$as_echo "$as_me: updating cache $cache_file" >&6;}
- cat confcache >$cache_file
+ if test ! -f "$cache_file" || test -h "$cache_file"; then
+ cat confcache >"$cache_file"
+ else
+ case $cache_file in #(
+ */* | ?:*)
+ mv -f confcache "$cache_file"$$ &&
+ mv -f "$cache_file"$$ "$cache_file" ;; #(
+ *)
+ mv -f confcache "$cache_file" ;;
+ esac
+ fi
+ fi
else
{ $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
@@ -12999,6 +13613,7 @@ DEFS=-DHAVE_CONFIG_H
ac_libobjs=
ac_ltlibobjs=
+U=
for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
# 1. Remove the extension, and $U if already installed.
ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
@@ -13013,12 +13628,20 @@ LIBOBJS=$ac_libobjs
LTLIBOBJS=$ac_ltlibobjs
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5
+$as_echo_n "checking that generated files are newer than configure... " >&6; }
+ if test -n "$am_sleep_pid"; then
+ # Hide warnings about reused PIDs.
+ wait $am_sleep_pid 2>/dev/null
+ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5
+$as_echo "done" >&6; }
if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then
- as_fn_error "conditional \"AMDEP\" was never defined.
+ as_fn_error $? "conditional \"AMDEP\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then
- as_fn_error "conditional \"am__fastdepCC\" was never defined.
+ as_fn_error $? "conditional \"am__fastdepCC\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
if test -n "$EXEEXT"; then
@@ -13029,12 +13652,8 @@ else
am__EXEEXT_FALSE=
fi
-if test -z "${WIDECHAR_TRUE}" && test -z "${WIDECHAR_FALSE}"; then
- as_fn_error "conditional \"WIDECHAR\" was never defined.
-Usually this means the macro was only invoked conditionally." "$LINENO" 5
-fi
-: ${CONFIG_STATUS=./config.status}
+: "${CONFIG_STATUS=./config.status}"
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
@@ -13135,6 +13754,7 @@ fi
IFS=" "" $as_nl"
# Find who we are. Look in the path if we contain no directory separator.
+as_myself=
case $0 in #((
*[\\/]* ) as_myself=$0 ;;
*) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -13180,19 +13800,19 @@ export LANGUAGE
(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
-# as_fn_error ERROR [LINENO LOG_FD]
-# ---------------------------------
+# as_fn_error STATUS ERROR [LINENO LOG_FD]
+# ----------------------------------------
# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are
# provided, also output the error to LOG_FD, referencing LINENO. Then exit the
-# script with status $?, using 1 if that was 0.
+# script with STATUS, using 1 if that was 0.
as_fn_error ()
{
- as_status=$?; test $as_status -eq 0 && as_status=1
- if test "$3"; then
- as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3
+ as_status=$1; test $as_status -eq 0 && as_status=1
+ if test "$4"; then
+ as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $1" >&2
+ $as_echo "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
@@ -13330,16 +13950,16 @@ if (echo >conf$$.file) 2>/dev/null; then
# ... but there are two gotchas:
# 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
# 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -p'.
+ # In both cases, we have to default to `cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
as_ln_s=ln
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
else
- as_ln_s='cp -p'
+ as_ln_s='cp -pR'
fi
rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file
rmdir conf$$.dir 2>/dev/null
@@ -13388,7 +14008,7 @@ $as_echo X"$as_dir" |
test -d "$as_dir" && break
done
test -z "$as_dirs" || eval "mkdir $as_dirs"
- } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir"
+ } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir"
} # as_fn_mkdir_p
@@ -13399,28 +14019,16 @@ else
as_mkdir_p=false
fi
-if test -x / >/dev/null 2>&1; then
- as_test_x='test -x'
-else
- if ls -dL / >/dev/null 2>&1; then
- as_ls_L_option=L
- else
- as_ls_L_option=
- fi
- as_test_x='
- eval sh -c '\''
- if test -d "$1"; then
- test -d "$1/.";
- else
- case $1 in #(
- -*)set "./$1";;
- esac;
- case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #((
- ???[sx]*):;;*)false;;esac;fi
- '\'' sh
- '
-fi
-as_executable_p=$as_test_x
+
+# as_fn_executable_p FILE
+# -----------------------
+# Test if FILE is an executable regular file.
+as_fn_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+} # as_fn_executable_p
+as_test_x='test -x'
+as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
@@ -13441,8 +14049,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by libedit $as_me 3.0, which was
-generated by GNU Autoconf 2.65. Invocation command line was
+This file was extended by libedit $as_me 3.1, which was
+generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -13507,11 +14115,11 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-libedit config.status 3.0
-configured by $0, generated by GNU Autoconf 2.65,
+libedit config.status 3.1
+configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
-Copyright (C) 2009 Free Software Foundation, Inc.
+Copyright (C) 2012 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
@@ -13529,11 +14137,16 @@ ac_need_defaults=:
while test $# != 0
do
case $1 in
- --*=*)
+ --*=?*)
ac_option=`expr "X$1" : 'X\([^=]*\)='`
ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`
ac_shift=:
;;
+ --*=)
+ ac_option=`expr "X$1" : 'X\([^=]*\)='`
+ ac_optarg=
+ ac_shift=:
+ ;;
*)
ac_option=$1
ac_optarg=$2
@@ -13555,6 +14168,7 @@ do
$ac_shift
case $ac_optarg in
*\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ '') as_fn_error $? "missing file argument" ;;
esac
as_fn_append CONFIG_FILES " '$ac_optarg'"
ac_need_defaults=false;;
@@ -13567,7 +14181,7 @@ do
ac_need_defaults=false;;
--he | --h)
# Conflict between --help and --header
- as_fn_error "ambiguous option: \`$1'
+ as_fn_error $? "ambiguous option: \`$1'
Try \`$0 --help' for more information.";;
--help | --hel | -h )
$as_echo "$ac_cs_usage"; exit ;;
@@ -13576,7 +14190,7 @@ Try \`$0 --help' for more information.";;
ac_cs_silent=: ;;
# This is an error.
- -*) as_fn_error "unrecognized option: \`$1'
+ -*) as_fn_error $? "unrecognized option: \`$1'
Try \`$0 --help' for more information." ;;
*) as_fn_append ac_config_targets " $1"
@@ -13596,7 +14210,7 @@ fi
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
- set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
+ set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
\$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
@@ -13636,8 +14250,10 @@ enable_shared='`$ECHO "$enable_shared" | $SED "$delay_single_quote_subst"`'
enable_static='`$ECHO "$enable_static" | $SED "$delay_single_quote_subst"`'
pic_mode='`$ECHO "$pic_mode" | $SED "$delay_single_quote_subst"`'
enable_fast_install='`$ECHO "$enable_fast_install" | $SED "$delay_single_quote_subst"`'
+shared_archive_member_spec='`$ECHO "$shared_archive_member_spec" | $SED "$delay_single_quote_subst"`'
SHELL='`$ECHO "$SHELL" | $SED "$delay_single_quote_subst"`'
ECHO='`$ECHO "$ECHO" | $SED "$delay_single_quote_subst"`'
+PATH_SEPARATOR='`$ECHO "$PATH_SEPARATOR" | $SED "$delay_single_quote_subst"`'
host_alias='`$ECHO "$host_alias" | $SED "$delay_single_quote_subst"`'
host='`$ECHO "$host" | $SED "$delay_single_quote_subst"`'
host_os='`$ECHO "$host_os" | $SED "$delay_single_quote_subst"`'
@@ -13684,10 +14300,13 @@ compiler='`$ECHO "$compiler" | $SED "$delay_single_quote_subst"`'
GCC='`$ECHO "$GCC" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_pipe='`$ECHO "$lt_cv_sys_global_symbol_pipe" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_cdecl='`$ECHO "$lt_cv_sys_global_symbol_to_cdecl" | $SED "$delay_single_quote_subst"`'
+lt_cv_sys_global_symbol_to_import='`$ECHO "$lt_cv_sys_global_symbol_to_import" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address" | $SED "$delay_single_quote_subst"`'
lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $SED "$delay_single_quote_subst"`'
+lt_cv_nm_interface='`$ECHO "$lt_cv_nm_interface" | $SED "$delay_single_quote_subst"`'
nm_file_list_spec='`$ECHO "$nm_file_list_spec" | $SED "$delay_single_quote_subst"`'
lt_sysroot='`$ECHO "$lt_sysroot" | $SED "$delay_single_quote_subst"`'
+lt_cv_truncate_bin='`$ECHO "$lt_cv_truncate_bin" | $SED "$delay_single_quote_subst"`'
objdir='`$ECHO "$objdir" | $SED "$delay_single_quote_subst"`'
MAGIC_CMD='`$ECHO "$MAGIC_CMD" | $SED "$delay_single_quote_subst"`'
lt_prog_compiler_no_builtin_flag='`$ECHO "$lt_prog_compiler_no_builtin_flag" | $SED "$delay_single_quote_subst"`'
@@ -13720,7 +14339,6 @@ with_gnu_ld='`$ECHO "$with_gnu_ld" | $SED "$delay_single_quote_subst"`'
allow_undefined_flag='`$ECHO "$allow_undefined_flag" | $SED "$delay_single_quote_subst"`'
no_undefined_flag='`$ECHO "$no_undefined_flag" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_flag_spec='`$ECHO "$hardcode_libdir_flag_spec" | $SED "$delay_single_quote_subst"`'
-hardcode_libdir_flag_spec_ld='`$ECHO "$hardcode_libdir_flag_spec_ld" | $SED "$delay_single_quote_subst"`'
hardcode_libdir_separator='`$ECHO "$hardcode_libdir_separator" | $SED "$delay_single_quote_subst"`'
hardcode_direct='`$ECHO "$hardcode_direct" | $SED "$delay_single_quote_subst"`'
hardcode_direct_absolute='`$ECHO "$hardcode_direct_absolute" | $SED "$delay_single_quote_subst"`'
@@ -13753,7 +14371,8 @@ finish_cmds='`$ECHO "$finish_cmds" | $SED "$delay_single_quote_subst"`'
finish_eval='`$ECHO "$finish_eval" | $SED "$delay_single_quote_subst"`'
hardcode_into_libs='`$ECHO "$hardcode_into_libs" | $SED "$delay_single_quote_subst"`'
sys_lib_search_path_spec='`$ECHO "$sys_lib_search_path_spec" | $SED "$delay_single_quote_subst"`'
-sys_lib_dlsearch_path_spec='`$ECHO "$sys_lib_dlsearch_path_spec" | $SED "$delay_single_quote_subst"`'
+configure_time_dlsearch_path='`$ECHO "$configure_time_dlsearch_path" | $SED "$delay_single_quote_subst"`'
+configure_time_lt_sys_library_path='`$ECHO "$configure_time_lt_sys_library_path" | $SED "$delay_single_quote_subst"`'
hardcode_action='`$ECHO "$hardcode_action" | $SED "$delay_single_quote_subst"`'
enable_dlopen='`$ECHO "$enable_dlopen" | $SED "$delay_single_quote_subst"`'
enable_dlopen_self='`$ECHO "$enable_dlopen_self" | $SED "$delay_single_quote_subst"`'
@@ -13776,6 +14395,7 @@ _LTECHO_EOF'
# Quote evaled strings.
for var in SHELL \
ECHO \
+PATH_SEPARATOR \
SED \
GREP \
EGREP \
@@ -13803,9 +14423,12 @@ CFLAGS \
compiler \
lt_cv_sys_global_symbol_pipe \
lt_cv_sys_global_symbol_to_cdecl \
+lt_cv_sys_global_symbol_to_import \
lt_cv_sys_global_symbol_to_c_name_address \
lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \
+lt_cv_nm_interface \
nm_file_list_spec \
+lt_cv_truncate_bin \
lt_prog_compiler_no_builtin_flag \
lt_prog_compiler_pic \
lt_prog_compiler_wl \
@@ -13826,7 +14449,6 @@ with_gnu_ld \
allow_undefined_flag \
no_undefined_flag \
hardcode_libdir_flag_spec \
-hardcode_libdir_flag_spec_ld \
hardcode_libdir_separator \
exclude_expsyms \
include_expsyms \
@@ -13841,7 +14463,7 @@ old_striplib \
striplib; do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[\\\\\\\`\\"\\\$]*)
- eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\""
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
@@ -13868,10 +14490,11 @@ postinstall_cmds \
postuninstall_cmds \
finish_cmds \
sys_lib_search_path_spec \
-sys_lib_dlsearch_path_spec; do
+configure_time_dlsearch_path \
+configure_time_lt_sys_library_path; do
case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in
*[\\\\\\\`\\"\\\$]*)
- eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\""
+ eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes
;;
*)
eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\""
@@ -13880,19 +14503,16 @@ sys_lib_dlsearch_path_spec; do
done
ac_aux_dir='$ac_aux_dir'
-xsi_shell='$xsi_shell'
-lt_shell_append='$lt_shell_append'
-# See if we are running on zsh, and set the options which allow our
+# See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes INIT.
-if test -n "\${ZSH_VERSION+set}" ; then
+if test -n "\${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
PACKAGE='$PACKAGE'
VERSION='$VERSION'
- TIMESTAMP='$TIMESTAMP'
RM='$RM'
ofile='$ofile'
@@ -13913,7 +14533,7 @@ do
"Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;;
"src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;;
- *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
esac
done
@@ -13936,9 +14556,10 @@ fi
# after its creation but before its name has been assigned to `$tmp'.
$debug ||
{
- tmp=
+ tmp= ac_tmp=
trap 'exit_status=$?
- { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status
+ : "${ac_tmp:=$tmp}"
+ { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status
' 0
trap 'as_fn_exit 1' 1 2 13 15
}
@@ -13946,12 +14567,13 @@ $debug ||
{
tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&
- test -n "$tmp" && test -d "$tmp"
+ test -d "$tmp"
} ||
{
tmp=./conf$$-$RANDOM
(umask 077 && mkdir "$tmp")
-} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5
+} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5
+ac_tmp=$tmp
# Set up the scripts for CONFIG_FILES section.
# No need to generate them if there are no CONFIG_FILES.
@@ -13968,12 +14590,12 @@ if test "x$ac_cr" = x; then
fi
ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null`
if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then
- ac_cs_awk_cr='\r'
+ ac_cs_awk_cr='\\r'
else
ac_cs_awk_cr=$ac_cr
fi
-echo 'BEGIN {' >"$tmp/subs1.awk" &&
+echo 'BEGIN {' >"$ac_tmp/subs1.awk" &&
_ACEOF
@@ -13982,18 +14604,18 @@ _ACEOF
echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' &&
echo "_ACEOF"
} >conf$$subs.sh ||
- as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
-ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'`
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
+ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'`
ac_delim='%!_!# '
for ac_last_try in false false false false false :; do
. ./conf$$subs.sh ||
- as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X`
if test $ac_delim_n = $ac_delim_num; then
break
elif $ac_last_try; then
- as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5
+ as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
@@ -14001,7 +14623,7 @@ done
rm -f conf$$subs.sh
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
-cat >>"\$tmp/subs1.awk" <<\\_ACAWK &&
+cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK &&
_ACEOF
sed -n '
h
@@ -14049,7 +14671,7 @@ t delim
rm -f conf$$subs.awk
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
-cat >>"\$tmp/subs1.awk" <<_ACAWK &&
+cat >>"\$ac_tmp/subs1.awk" <<_ACAWK &&
for (key in S) S_is_set[key] = 1
FS = ""
@@ -14081,21 +14703,29 @@ if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then
sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g"
else
cat
-fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \
- || as_fn_error "could not setup config files machinery" "$LINENO" 5
+fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \
+ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5
_ACEOF
-# VPATH may cause trouble with some makes, so we remove $(srcdir),
-# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and
+# VPATH may cause trouble with some makes, so we remove sole $(srcdir),
+# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and
# trailing colons and then remove the whole line if VPATH becomes empty
# (actually we leave an empty line to preserve line numbers).
if test "x$srcdir" = x.; then
- ac_vpsub='/^[ ]*VPATH[ ]*=/{
-s/:*\$(srcdir):*/:/
-s/:*\${srcdir}:*/:/
-s/:*@srcdir@:*/:/
-s/^\([^=]*=[ ]*\):*/\1/
+ ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{
+h
+s///
+s/^/:/
+s/[ ]*$/:/
+s/:\$(srcdir):/:/g
+s/:\${srcdir}:/:/g
+s/:@srcdir@:/:/g
+s/^:*//
s/:*$//
+x
+s/\(=[ ]*\).*/\1/
+G
+s/\n//
s/^[^=]*=[ ]*$//
}'
fi
@@ -14107,7 +14737,7 @@ fi # test -n "$CONFIG_FILES"
# No need to generate them if there are no CONFIG_HEADERS.
# This happens for instance with `./config.status Makefile'.
if test -n "$CONFIG_HEADERS"; then
-cat >"$tmp/defines.awk" <<\_ACAWK ||
+cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
BEGIN {
_ACEOF
@@ -14119,11 +14749,11 @@ _ACEOF
# handling of long lines.
ac_delim='%!_!# '
for ac_last_try in false false :; do
- ac_t=`sed -n "/$ac_delim/p" confdefs.h`
- if test -z "$ac_t"; then
+ ac_tt=`sed -n "/$ac_delim/p" confdefs.h`
+ if test -z "$ac_tt"; then
break
elif $ac_last_try; then
- as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5
+ as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5
else
ac_delim="$ac_delim!$ac_delim _$ac_delim!! "
fi
@@ -14208,7 +14838,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
_ACAWK
_ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
- as_fn_error "could not setup config headers machinery" "$LINENO" 5
+ as_fn_error $? "could not setup config headers machinery" "$LINENO" 5
fi # test -n "$CONFIG_HEADERS"
@@ -14221,7 +14851,7 @@ do
esac
case $ac_mode$ac_tag in
:[FHL]*:*);;
- :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
:[FH]-) ac_tag=-:-;;
:[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
esac
@@ -14240,7 +14870,7 @@ do
for ac_f
do
case $ac_f in
- -) ac_f="$tmp/stdin";;
+ -) ac_f="$ac_tmp/stdin";;
*) # Look for the file first in the build tree, then in the source tree
# (if the path is not absolute). The absolute path cannot be DOS-style,
# because $ac_f cannot contain `:'.
@@ -14249,7 +14879,7 @@ do
[\\/$]*) false;;
*) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
esac ||
- as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
esac
case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
as_fn_append ac_file_inputs " '$ac_f'"
@@ -14275,8 +14905,8 @@ $as_echo "$as_me: creating $ac_file" >&6;}
esac
case $ac_tag in
- *:-:* | *:-) cat >"$tmp/stdin" \
- || as_fn_error "could not create $ac_file" "$LINENO" 5 ;;
+ *:-:* | *:-) cat >"$ac_tmp/stdin" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;;
esac
;;
esac
@@ -14412,23 +15042,24 @@ s&@INSTALL@&$ac_INSTALL&;t t
s&@MKDIR_P@&$ac_MKDIR_P&;t t
$ac_datarootdir_hack
"
-eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
+eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \
+ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5
test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
- { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&
- { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&
+ { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
+ "$ac_tmp/out"`; test -z "$ac_out"; } &&
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined. Please make sure it is defined." >&5
+which seems to be undefined. Please make sure it is defined" >&5
$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
-which seems to be undefined. Please make sure it is defined." >&2;}
+which seems to be undefined. Please make sure it is defined" >&2;}
- rm -f "$tmp/stdin"
+ rm -f "$ac_tmp/stdin"
case $ac_file in
- -) cat "$tmp/out" && rm -f "$tmp/out";;
- *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";;
+ -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";;
+ *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";;
esac \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
;;
:H)
#
@@ -14437,21 +15068,21 @@ which seems to be undefined. Please make sure it is defined." >&2;}
if test x"$ac_file" != x-; then
{
$as_echo "/* $configure_input */" \
- && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs"
- } >"$tmp/config.h" \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
- if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
+ } >"$ac_tmp/config.h" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
+ if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
$as_echo "$as_me: $ac_file is unchanged" >&6;}
else
rm -f "$ac_file"
- mv "$tmp/config.h" "$ac_file" \
- || as_fn_error "could not create $ac_file" "$LINENO" 5
+ mv "$ac_tmp/config.h" "$ac_file" \
+ || as_fn_error $? "could not create $ac_file" "$LINENO" 5
fi
else
$as_echo "/* $configure_input */" \
- && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \
- || as_fn_error "could not create -" "$LINENO" 5
+ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
+ || as_fn_error $? "could not create -" "$LINENO" 5
fi
# Compute "$ac_file"'s index in $config_headers.
_am_arg="$ac_file"
@@ -14497,7 +15128,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
case $ac_file$ac_mode in
"depfiles":C) test x"$AMDEP_TRUE" != x"" || {
- # Autoconf 2.62 quotes --file arguments for eval, but not when files
+ # Older Autoconf quotes --file arguments for eval, but not when files
# are listed without --file. Let's play safe and only enable the eval
# if we detect the quoting.
case $CONFIG_FILES in
@@ -14510,7 +15141,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;}
# Strip MF so we end up with the name of the file.
mf=`echo "$mf" | sed -e 's/:.*$//'`
# Check whether this is an Automake generated Makefile or not.
- # We used to match only the files named `Makefile.in', but
+ # We used to match only the files named 'Makefile.in', but
# some people rename them; so instead we look at the file content.
# Grep'ing the first line is not enough: some people post-process
# each Makefile.in and add a new line on top of each file to say so.
@@ -14544,21 +15175,19 @@ $as_echo X"$mf" |
continue
fi
# Extract the definition of DEPDIR, am__include, and am__quote
- # from the Makefile without running `make'.
+ # from the Makefile without running 'make'.
DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"`
test -z "$DEPDIR" && continue
am__include=`sed -n 's/^am__include = //p' < "$mf"`
- test -z "am__include" && continue
+ test -z "$am__include" && continue
am__quote=`sed -n 's/^am__quote = //p' < "$mf"`
- # When using ansi2knr, U may be empty or an underscore; expand it
- U=`sed -n 's/^U = //p' < "$mf"`
# Find all dependency output files, they are included files with
# $(DEPDIR) in their names. We invoke sed twice because it is the
# simplest approach to changing $(DEPDIR) to its actual value in the
# expansion.
for file in `sed -n "
s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \
- sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do
+ sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g'`; do
# Make sure the directory exists.
test -f "$dirpart/$file" && continue
fdir=`$as_dirname -- "$file" ||
@@ -14593,55 +15222,53 @@ $as_echo X"$file" |
;;
"libtool":C)
- # See if we are running on zsh, and set the options which allow our
+ # See if we are running on zsh, and set the options that allow our
# commands through without removal of \ escapes.
- if test -n "${ZSH_VERSION+set}" ; then
+ if test -n "${ZSH_VERSION+set}"; then
setopt NO_GLOB_SUBST
fi
- cfgfile="${ofile}T"
+ cfgfile=${ofile}T
trap "$RM \"$cfgfile\"; exit 1" 1 2 15
$RM "$cfgfile"
cat <<_LT_EOF >> "$cfgfile"
#! $SHELL
-
-# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services.
-# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION
+# Generated automatically by $as_me ($PACKAGE) $VERSION
# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
# NOTE: Changes made to this file will be lost: look at ltmain.sh.
+
+# Provide generalized library-building support services.
+# Written by Gordon Matzigkeit, 1996
+
+# Copyright (C) 2014 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# GNU Libtool 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 of of the License, or
+# (at your option) any later version.
#
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005,
-# 2006, 2007, 2008, 2009, 2010 Free Software Foundation,
-# Inc.
-# Written by Gordon Matzigkeit, 1996
-#
-# This file is part of GNU Libtool.
-#
-# GNU Libtool 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 of
-# the License, or (at your option) any later version.
-#
-# As a special exception to the GNU General Public License,
-# if you distribute this file as part of a program or library that
-# is built using GNU Libtool, you may include this file under the
-# same distribution terms that you use for the rest of that program.
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program or library that is built
+# using GNU Libtool, you may include this file under the same
+# distribution terms that you use for the rest of that program.
#
-# GNU Libtool is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# GNU Libtool 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 GNU Libtool; see the file COPYING. If not, a copy
-# can be downloaded from http://www.gnu.org/licenses/gpl.html, or
-# obtained by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
# The names of the tagged configurations supported by this script.
-available_tags=""
+available_tags=''
+
+# Configured defaults for sys_lib_dlsearch_path munging.
+: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"}
# ### BEGIN LIBTOOL CONFIG
@@ -14661,12 +15288,18 @@ pic_mode=$pic_mode
# Whether or not to optimize for fast installation.
fast_install=$enable_fast_install
+# Shared archive member basename,for filename based shared library versioning on AIX.
+shared_archive_member_spec=$shared_archive_member_spec
+
# Shell to use when invoking shell scripts.
SHELL=$lt_SHELL
# An echo program that protects backslashes.
ECHO=$lt_ECHO
+# The PATH separator for the build system.
+PATH_SEPARATOR=$lt_PATH_SEPARATOR
+
# The host system.
host_alias=$host_alias
host=$host
@@ -14775,18 +15408,27 @@ global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe
# Transform the output of nm in a proper C declaration.
global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl
+# Transform the output of nm into a list of symbols to manually relocate.
+global_symbol_to_import=$lt_lt_cv_sys_global_symbol_to_import
+
# Transform the output of nm in a C name address pair.
global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address
# Transform the output of nm in a C name address pair when lib prefix is needed.
global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix
+# The name lister interface.
+nm_interface=$lt_lt_cv_nm_interface
+
# Specify filename containing input files for \$NM.
nm_file_list_spec=$lt_nm_file_list_spec
-# The root where to search for dependent libraries,and in which our libraries should be installed.
+# The root where to search for dependent libraries,and where our libraries should be installed.
lt_sysroot=$lt_sysroot
+# Command to truncate a binary pipe.
+lt_truncate_bin=$lt_lt_cv_truncate_bin
+
# The name of the directory that contains temporary libtool files.
objdir=$objdir
@@ -14877,8 +15519,11 @@ hardcode_into_libs=$hardcode_into_libs
# Compile-time system search path for libraries.
sys_lib_search_path_spec=$lt_sys_lib_search_path_spec
-# Run-time system search path for libraries.
-sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec
+# Detected run-time system search path for libraries.
+sys_lib_dlsearch_path_spec=$lt_configure_time_dlsearch_path
+
+# Explicit LT_SYS_LIBRARY_PATH set during ./configure time.
+configure_time_lt_sys_library_path=$lt_configure_time_lt_sys_library_path
# Whether dlopen is supported.
dlopen_support=$enable_dlopen
@@ -14968,20 +15613,16 @@ no_undefined_flag=$lt_no_undefined_flag
# This must work even if \$libdir does not exist
hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec
-# If ld is used when linking, flag to hardcode \$libdir into a binary
-# during linking. This must work even if \$libdir does not exist.
-hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld
-
# Whether we need a single "-rpath" flag with a separated argument.
hardcode_libdir_separator=$lt_hardcode_libdir_separator
-# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary.
hardcode_direct=$hardcode_direct
-# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes
+# Set to "yes" if using DIR/libNAME\$shared_ext during linking hardcodes
# DIR into the resulting binary and the resulting library dependency is
-# "absolute",i.e impossible to change by setting \${shlibpath_var} if the
+# "absolute",i.e impossible to change by setting \$shlibpath_var if the
# library is relocated.
hardcode_direct_absolute=$hardcode_direct_absolute
@@ -15033,13 +15674,72 @@ hardcode_action=$hardcode_action
_LT_EOF
+ cat <<'_LT_EOF' >> "$cfgfile"
+
+# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE
+
+# func_munge_path_list VARIABLE PATH
+# -----------------------------------
+# VARIABLE is name of variable containing _space_ separated list of
+# directories to be munged by the contents of PATH, which is string
+# having a format:
+# "DIR[:DIR]:"
+# string "DIR[ DIR]" will be prepended to VARIABLE
+# ":DIR[:DIR]"
+# string "DIR[ DIR]" will be appended to VARIABLE
+# "DIRP[:DIRP]::[DIRA:]DIRA"
+# string "DIRP[ DIRP]" will be prepended to VARIABLE and string
+# "DIRA[ DIRA]" will be appended to VARIABLE
+# "DIR[:DIR]"
+# VARIABLE will be replaced by "DIR[ DIR]"
+func_munge_path_list ()
+{
+ case x$2 in
+ x)
+ ;;
+ *:)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'` \$$1\"
+ ;;
+ x:*)
+ eval $1=\"\$$1 `$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ *::*)
+ eval $1=\"\$$1\ `$ECHO $2 | $SED -e 's/.*:://' -e 's/:/ /g'`\"
+ eval $1=\"`$ECHO $2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \$$1\"
+ ;;
+ *)
+ eval $1=\"`$ECHO $2 | $SED 's/:/ /g'`\"
+ ;;
+ esac
+}
+
+
+# Calculate cc_basename. Skip known compiler wrappers and cross-prefix.
+func_cc_basename ()
+{
+ for cc_temp in $*""; do
+ case $cc_temp in
+ compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+ distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+ \-*) ;;
+ *) break;;
+ esac
+ done
+ func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"`
+}
+
+
+# ### END FUNCTIONS SHARED WITH CONFIGURE
+
+_LT_EOF
+
case $host_os in
aix3*)
cat <<\_LT_EOF >> "$cfgfile"
# AIX sometimes has problems with the GCC collect2 program. For some
# reason, if we set the COLLECT_NAMES environment variable, the problems
# vanish in a puff of smoke.
-if test "X${COLLECT_NAMES+set}" != Xset; then
+if test set != "${COLLECT_NAMES+set}"; then
COLLECT_NAMES=
export COLLECT_NAMES
fi
@@ -15048,7 +15748,7 @@ _LT_EOF
esac
-ltmain="$ac_aux_dir/ltmain.sh"
+ltmain=$ac_aux_dir/ltmain.sh
# We use sed instead of cat because bash on DJGPP gets confused if
@@ -15058,165 +15758,6 @@ ltmain="$ac_aux_dir/ltmain.sh"
sed '$q' "$ltmain" >> "$cfgfile" \
|| (rm -f "$cfgfile"; exit 1)
- if test x"$xsi_shell" = xyes; then
- sed -e '/^func_dirname ()$/,/^} # func_dirname /c\
-func_dirname ()\
-{\
-\ case ${1} in\
-\ */*) func_dirname_result="${1%/*}${2}" ;;\
-\ * ) func_dirname_result="${3}" ;;\
-\ esac\
-} # Extended-shell func_dirname implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_basename ()$/,/^} # func_basename /c\
-func_basename ()\
-{\
-\ func_basename_result="${1##*/}"\
-} # Extended-shell func_basename implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_dirname_and_basename ()$/,/^} # func_dirname_and_basename /c\
-func_dirname_and_basename ()\
-{\
-\ case ${1} in\
-\ */*) func_dirname_result="${1%/*}${2}" ;;\
-\ * ) func_dirname_result="${3}" ;;\
-\ esac\
-\ func_basename_result="${1##*/}"\
-} # Extended-shell func_dirname_and_basename implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_stripname ()$/,/^} # func_stripname /c\
-func_stripname ()\
-{\
-\ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are\
-\ # positional parameters, so assign one to ordinary parameter first.\
-\ func_stripname_result=${3}\
-\ func_stripname_result=${func_stripname_result#"${1}"}\
-\ func_stripname_result=${func_stripname_result%"${2}"}\
-} # Extended-shell func_stripname implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_split_long_opt ()$/,/^} # func_split_long_opt /c\
-func_split_long_opt ()\
-{\
-\ func_split_long_opt_name=${1%%=*}\
-\ func_split_long_opt_arg=${1#*=}\
-} # Extended-shell func_split_long_opt implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_split_short_opt ()$/,/^} # func_split_short_opt /c\
-func_split_short_opt ()\
-{\
-\ func_split_short_opt_arg=${1#??}\
-\ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}\
-} # Extended-shell func_split_short_opt implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_lo2o ()$/,/^} # func_lo2o /c\
-func_lo2o ()\
-{\
-\ case ${1} in\
-\ *.lo) func_lo2o_result=${1%.lo}.${objext} ;;\
-\ *) func_lo2o_result=${1} ;;\
-\ esac\
-} # Extended-shell func_lo2o implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_xform ()$/,/^} # func_xform /c\
-func_xform ()\
-{\
- func_xform_result=${1%.*}.lo\
-} # Extended-shell func_xform implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_arith ()$/,/^} # func_arith /c\
-func_arith ()\
-{\
- func_arith_result=$(( $* ))\
-} # Extended-shell func_arith implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_len ()$/,/^} # func_len /c\
-func_len ()\
-{\
- func_len_result=${#1}\
-} # Extended-shell func_len implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-fi
-
-if test x"$lt_shell_append" = xyes; then
- sed -e '/^func_append ()$/,/^} # func_append /c\
-func_append ()\
-{\
- eval "${1}+=\\${2}"\
-} # Extended-shell func_append implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- sed -e '/^func_append_quoted ()$/,/^} # func_append_quoted /c\
-func_append_quoted ()\
-{\
-\ func_quote_for_eval "${2}"\
-\ eval "${1}+=\\\\ \\$func_quote_for_eval_result"\
-} # Extended-shell func_append_quoted implementation' "$cfgfile" > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
-test 0 -eq $? || _lt_function_replace_fail=:
-
-
- # Save a `func_append' function call where possible by direct use of '+='
- sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
- test 0 -eq $? || _lt_function_replace_fail=:
-else
- # Save a `func_append' function call even when '+=' is not available
- sed -e 's%func_append \([a-zA-Z_]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \
- && mv -f "$cfgfile.tmp" "$cfgfile" \
- || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp")
- test 0 -eq $? || _lt_function_replace_fail=:
-fi
-
-if test x"$_lt_function_replace_fail" = x":"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Unable to substitute extended shell functions in $ofile" >&5
-$as_echo "$as_me: WARNING: Unable to substitute extended shell functions in $ofile" >&2;}
-fi
-
-
mv -f "$cfgfile" "$ofile" ||
(rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile")
chmod +x "$ofile"
@@ -15232,7 +15773,7 @@ _ACEOF
ac_clean_files=$ac_clean_files_save
test $ac_write_fail = 0 ||
- as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5
+ as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5
# configure is writing to config.log, and then calls config.status.
@@ -15253,7 +15794,7 @@ if test "$no_create" != yes; then
exec 5>>config.log
# Use ||, not &&, to avoid exiting from the if with $? = 1, which
# would make configure fail if this is the last instruction.
- $ac_cs_success || as_fn_exit $?
+ $ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
diff --git a/lib/libedit/configure.ac b/lib/libedit/configure.ac
index 08591bab1398..d379a4cafc23 100644
--- a/lib/libedit/configure.ac
+++ b/lib/libedit/configure.ac
@@ -11,7 +11,7 @@
AC_PREREQ(2.61)
AC_INIT(libedit, [EL_RELEASE],, libedit-[EL_TIMESTAMP])
-AC_CONFIG_SRCDIR([src/strlcat.c])
+AC_CONFIG_SRCDIR([src/el.c])
AC_CONFIG_HEADER([config.h])
# features of Posix that are extensions to C (define _GNU_SOURCE)
@@ -38,15 +38,11 @@ AC_CHECK_LIB(curses, tgetent,,
[AC_MSG_ERROR([libcurses or libncurses are required!])] )] )
-### use option --enable-widec to turn on use of wide-character support
-EL_ENABLE_WIDEC
-
# Checks for header files.
-AC_FUNC_ALLOCA
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([fcntl.h limits.h malloc.h stdlib.h string.h sys/ioctl.h sys/param.h unistd.h curses.h ncurses.h sys/cdefs.h])
+AC_CHECK_HEADERS([fcntl.h limits.h stdint.h stdlib.h string.h sys/ioctl.h sys/param.h unistd.h curses.h ncurses.h sys/cdefs.h])
AC_CHECK_HEADER([termios.h], [], [AC_MSG_ERROR([termios.h is required!])],[])
@@ -65,8 +61,22 @@ AC_TYPE_PID_T
AC_TYPE_SIZE_T
AC_CHECK_TYPES([u_int32_t])
+AC_TRY_COMPILE([
+#include <limits.h>
+#include <stddef.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+],
+[size_t x = SIZE_MAX;],
+[have_size_max=yes],
+[have_size_max=no])
+if test "$have_size_max" = yes; then
+ AC_DEFINE([HAVE_SIZE_MAX], [1],
+ [Define to 1 if if your system has SIZE_MAX])
+fi
+
# Checks for library functions.
-AC_FUNC_CLOSEDIR_VOID
AC_FUNC_FORK
AC_PROG_GCC_TRADITIONAL
## _AIX is offended by rpl_malloc and rpl_realloc
@@ -74,10 +84,17 @@ AC_PROG_GCC_TRADITIONAL
#AC_FUNC_REALLOC
AC_TYPE_SIGNAL
AC_FUNC_STAT
-AC_CHECK_FUNCS([endpwent isascii memchr memset re_comp regcomp strcasecmp strchr strcspn strdup strerror strrchr strstr strtol issetugid wcsdup])
+AC_CHECK_FUNCS([getline isascii issetugid wcsdup])
EL_GETPW_R_POSIX
EL_GETPW_R_DRAFT
+AC_CHECK_MEMBER(struct dirent.d_namlen,
+AC_DEFINE([HAVE_STRUCT_DIRENT_D_NAMLEN],[1],
+[Define to 1 if struct dirent has member d_namlen]),,
+[#if HAVE_DIRENT_H
+#include <dirent.h>
+#endif
+])
AH_BOTTOM([
#include "sys.h"
diff --git a/lib/libedit/depcomp b/lib/libedit/depcomp
index df8eea7e4ce8..b39f98f9ae9f 100755
--- a/lib/libedit/depcomp
+++ b/lib/libedit/depcomp
@@ -1,10 +1,9 @@
#! /bin/sh
# depcomp - compile a program generating dependencies as side-effects
-scriptversion=2009-04-28.21; # UTC
+scriptversion=2016-01-11.22; # UTC
-# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006, 2007, 2009 Free
-# Software Foundation, Inc.
+# Copyright (C) 1999-2017 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
@@ -28,9 +27,9 @@ scriptversion=2009-04-28.21; # UTC
case $1 in
'')
- echo "$0: No command. Try \`$0 --help' for more information." 1>&2
- exit 1;
- ;;
+ echo "$0: No command. Try '$0 --help' for more information." 1>&2
+ exit 1;
+ ;;
-h | --h*)
cat <<\EOF
Usage: depcomp [--help] [--version] PROGRAM [ARGS]
@@ -40,11 +39,11 @@ as side-effects.
Environment variables:
depmode Dependency tracking mode.
- source Source file read by `PROGRAMS ARGS'.
- object Object file output by `PROGRAMS ARGS'.
+ 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 outputing dependencies.
+ tmpdepfile Temporary file to use when outputting dependencies.
libtool Whether libtool is used (yes/no).
Report bugs to <bug-automake@gnu.org>.
@@ -57,6 +56,66 @@ EOF
;;
esac
+# Get the directory component of the given path, and save it in the
+# global variables '$dir'. Note that this directory component will
+# be either empty or ending with a '/' character. This is deliberate.
+set_dir_from ()
+{
+ case $1 in
+ */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;;
+ *) dir=;;
+ esac
+}
+
+# Get the suffix-stripped basename of the given path, and save it the
+# global variable '$base'.
+set_base_from ()
+{
+ base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'`
+}
+
+# If no dependency file was actually created by the compiler invocation,
+# we still have to create a dummy depfile, to avoid errors with the
+# Makefile "include basename.Plo" scheme.
+make_dummy_depfile ()
+{
+ echo "#dummy" > "$depfile"
+}
+
+# Factor out some common post-processing of the generated depfile.
+# Requires the auxiliary global variable '$tmpdepfile' to be set.
+aix_post_process_depfile ()
+{
+ # If the compiler actually managed to produce a dependency file,
+ # post-process it.
+ if test -f "$tmpdepfile"; then
+ # Each line is of the form 'foo.o: dependency.h'.
+ # Do two passes, one to just change these to
+ # $object: dependency.h
+ # and one to simply output
+ # dependency.h:
+ # which is needed to avoid the deleted-header problem.
+ { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile"
+ sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile"
+ } > "$depfile"
+ rm -f "$tmpdepfile"
+ else
+ make_dummy_depfile
+ fi
+}
+
+# A tabulation character.
+tab=' '
+# A newline character.
+nl='
+'
+# Character ranges might be problematic outside the C locale.
+# These definitions help.
+upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ
+lower=abcdefghijklmnopqrstuvwxyz
+digits=0123456789
+alpha=${upper}${lower}
+
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
@@ -69,6 +128,9 @@ tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`}
rm -f "$tmpdepfile"
+# Avoid interferences from the environment.
+gccflag= dashmflag=
+
# 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
@@ -80,18 +142,32 @@ if test "$depmode" = hp; then
fi
if test "$depmode" = dashXmstdout; then
- # This is just like dashmstdout with a different argument.
- dashmflag=-xM
- depmode=dashmstdout
+ # 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
+ # 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 information.
+ gccflag=-qmakedep=gcc,-MF
+ depmode=gcc
fi
case "$depmode" in
@@ -114,8 +190,7 @@ gcc3)
done
"$@"
stat=$?
- if test $stat -eq 0; then :
- else
+ if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
@@ -123,13 +198,17 @@ gcc3)
;;
gcc)
+## Note that this doesn't just cater to obsosete pre-3.x GCC compilers.
+## but also to in-use compilers like IMB xlc/xlC and the HP C compiler.
+## (see the conditional assignment to $gccflag above).
## 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).
+## -MM, not -M (despite what the docs say). Also, it might not be
+## supported by the other compilers which use the 'gcc' depmode.
## - Using -M directly means running the compiler twice (even worse
## than renaming).
if test -z "$gccflag"; then
@@ -137,31 +216,31 @@ gcc)
fi
"$@" -Wp,"$gccflag$tmpdepfile"
stat=$?
- if test $stat -eq 0; then :
- else
+ if test $stat -ne 0; then
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.
+ # 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.
+## 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 ' ' '
-' < "$tmpdepfile" |
-## Some versions of gcc put a space before the `:'. On the theory
+## 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.
+## 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 '/:$/d' | sed -e 's/$/ :/' >> "$depfile"
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \
+ | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
@@ -179,8 +258,7 @@ sgi)
"$@" -MDupdate "$tmpdepfile"
fi
stat=$?
- if test $stat -eq 0; then :
- else
+ if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
@@ -188,43 +266,41 @@ sgi)
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
+ # the IRIX cc adds comments like '#:fec' to the end of the
# dependency line.
- tr ' ' '
-' < "$tmpdepfile" \
- | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \
- tr '
-' ' ' >> "$depfile"
+ 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 ' ' '
-' < "$tmpdepfile" \
- | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \
- >> "$depfile"
+ 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"
+ make_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
+ # 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$//'`
+ set_dir_from "$object"
+ set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.u
tmpdepfile2=$base.u
@@ -237,9 +313,7 @@ aix)
"$@" -M
fi
stat=$?
-
- if test $stat -eq 0; then :
- else
+ if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
exit $stat
fi
@@ -248,44 +322,100 @@ aix)
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"
- # That's a tab and a space in the [].
- sed -e 's,^.*\.[a-z]*:[ ]*,,' -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"
+ aix_post_process_depfile
+ ;;
+
+tcc)
+ # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26
+ # FIXME: That version still under development at the moment of writing.
+ # Make that this statement remains true also for stable, released
+ # versions.
+ # It will wrap lines (doesn't matter whether long or short) with a
+ # trailing '\', as in:
+ #
+ # foo.o : \
+ # foo.c \
+ # foo.h \
+ #
+ # It will put a trailing '\' even on the last line, and will use leading
+ # spaces rather than leading tabs (at least since its commit 0394caf7
+ # "Emit spaces for -MD").
+ "$@" -MD -MF "$tmpdepfile"
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile"
+ exit $stat
fi
+ rm -f "$depfile"
+ # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'.
+ # We have to change lines of the first kind to '$object: \'.
+ sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile"
+ # And for each line of the second kind, we have to emit a 'dep.h:'
+ # dummy dependency, to avoid the deleted-header problem.
+ sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile"
rm -f "$tmpdepfile"
;;
-icc)
- # Intel's C compiler understands `-MD -MF file'. However on
- # icc -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
+## The order of this option in the case statement is important, since the
+## shell code in configure will try each of these formats in the order
+## listed in this file. A plain '-MD' option would be understood by many
+## compilers, so we must ensure this comes after the gcc and icc options.
+pgcc)
+ # Portland's C compiler understands '-MD'.
+ # Will always output deps to 'file.d' where file is the root name of the
+ # source file under compilation, even if file resides in a subdirectory.
+ # The object file name does not affect the name of the '.d' file.
+ # pgcc 10.2 will output
# foo.o: sub/foo.c sub/foo.h
- # and will wrap long lines using \ :
+ # and will wrap long lines using '\' :
# foo.o: sub/foo.c ... \
# sub/foo.h ... \
# ...
+ set_dir_from "$object"
+ # Use the source, not the object, to determine the base name, since
+ # that's sadly what pgcc will do too.
+ set_base_from "$source"
+ tmpdepfile=$base.d
+
+ # For projects that build the same source file twice into different object
+ # files, the pgcc approach of using the *source* file root name can cause
+ # problems in parallel builds. Use a locking strategy to avoid stomping on
+ # the same $tmpdepfile.
+ lockdir=$base.d-lock
+ trap "
+ echo '$0: caught signal, cleaning up...' >&2
+ rmdir '$lockdir'
+ exit 1
+ " 1 2 13 15
+ numtries=100
+ i=$numtries
+ while test $i -gt 0; do
+ # mkdir is a portable test-and-set.
+ if mkdir "$lockdir" 2>/dev/null; then
+ # This process acquired the lock.
+ "$@" -MD
+ stat=$?
+ # Release the lock.
+ rmdir "$lockdir"
+ break
+ else
+ # If the lock is being held by a different process, wait
+ # until the winning process is done or we timeout.
+ while test -d "$lockdir" && test $i -gt 0; do
+ sleep 1
+ i=`expr $i - 1`
+ done
+ fi
+ i=`expr $i - 1`
+ done
+ trap - 1 2 13 15
+ if test $i -le 0; then
+ echo "$0: failed to acquire lock after $numtries attempts" >&2
+ echo "$0: check lockdir '$lockdir'" >&2
+ exit 1
+ fi
- "$@" -MD -MF "$tmpdepfile"
- stat=$?
- if test $stat -eq 0; then :
- else
+ if test $stat -ne 0; then
rm -f "$tmpdepfile"
exit $stat
fi
@@ -297,8 +427,8 @@ icc)
sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile"
# Some versions of the HPUX 10.20 sed can't process this invocation
# correctly. Breaking it into two sed invocations is a workaround.
- sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" |
- sed -e 's/$/ :/' >> "$depfile"
+ sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \
+ | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
@@ -309,9 +439,8 @@ hp2)
# '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$//'`
+ set_dir_from "$object"
+ set_base_from "$object"
if test "$libtool" = yes; then
tmpdepfile1=$dir$base.d
tmpdepfile2=$dir.libs/$base.d
@@ -322,8 +451,7 @@ hp2)
"$@" +Maked
fi
stat=$?
- if test $stat -eq 0; then :
- else
+ if test $stat -ne 0; then
rm -f "$tmpdepfile1" "$tmpdepfile2"
exit $stat
fi
@@ -333,77 +461,107 @@ hp2)
test -f "$tmpdepfile" && break
done
if test -f "$tmpdepfile"; then
- sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile"
- # Add `dependent.h:' lines.
+ sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile"
+ # Add 'dependent.h:' lines.
sed -ne '2,${
- s/^ *//
- s/ \\*$//
- s/$/:/
- p
- }' "$tmpdepfile" >> "$depfile"
+ s/^ *//
+ s/ \\*$//
+ s/$/:/
+ p
+ }' "$tmpdepfile" >> "$depfile"
else
- echo "#dummy" > "$depfile"
+ make_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"
- # That's a tab and a space in the [].
- sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile"
- else
- echo "#dummy" > "$depfile"
- fi
- rm -f "$tmpdepfile"
- ;;
+ # 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.
+ set_dir_from "$object"
+ set_base_from "$object"
+
+ if test "$libtool" = yes; then
+ # Libtool 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$base.o.d # libtool 1.5
+ tmpdepfile2=$dir.libs/$base.o.d # Likewise.
+ tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504
+ "$@" -Wc,-MD
+ else
+ tmpdepfile1=$dir$base.d
+ tmpdepfile2=$dir$base.d
+ tmpdepfile3=$dir$base.d
+ "$@" -MD
+ fi
+
+ stat=$?
+ if test $stat -ne 0; then
+ rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ exit $stat
+ fi
+
+ for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3"
+ do
+ test -f "$tmpdepfile" && break
+ done
+ # Same post-processing that is required for AIX mode.
+ aix_post_process_depfile
+ ;;
+
+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 -ne 0; then
+ 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"
+ echo >> "$depfile" # make sure the fragment doesn't end with a backslash
+ 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
@@ -422,7 +580,7 @@ dashmstdout)
shift
fi
- # Remove `-o $object'.
+ # Remove '-o $object'.
IFS=" "
for arg
do
@@ -442,18 +600,18 @@ dashmstdout)
done
test -z "$dashmflag" && dashmflag=-M
- # Require at least two characters before searching for `:'
+ # 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.
+ # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise.
"$@" $dashmflag |
- sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile"
+ sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile"
rm -f "$depfile"
cat < "$tmpdepfile" > "$depfile"
- tr ' ' '
-' < "$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"
+ # Some versions of the HPUX 10.20 sed can't process this sed invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ tr ' ' "$nl" < "$tmpdepfile" \
+ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+ | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile"
;;
@@ -503,12 +661,15 @@ makedepend)
touch "$tmpdepfile"
${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@"
rm -f "$depfile"
- cat < "$tmpdepfile" > "$depfile"
- sed '1,2d' "$tmpdepfile" | tr ' ' '
-' | \
-## 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"
+ # 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"
+ # Some versions of the HPUX 10.20 sed can't process the last invocation
+ # correctly. Breaking it into two sed invocations is a workaround.
+ sed '1,2d' "$tmpdepfile" \
+ | tr ' ' "$nl" \
+ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \
+ | sed -e 's/$/ :/' >> "$depfile"
rm -f "$tmpdepfile" "$tmpdepfile".bak
;;
@@ -525,7 +686,7 @@ cpp)
shift
fi
- # Remove `-o $object'.
+ # Remove '-o $object'.
IFS=" "
for arg
do
@@ -544,10 +705,10 @@ cpp)
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"
+ "$@" -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"
@@ -579,23 +740,23 @@ msvisualcpp)
shift
;;
"-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI")
- set fnord "$@"
- shift
- shift
- ;;
+ set fnord "$@"
+ shift
+ shift
+ ;;
*)
- set fnord "$@" "$arg"
- 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:: \1 \\:p' >> "$depfile"
- echo " " >> "$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"
;;
@@ -625,6 +786,6 @@ exit 0
# 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-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
diff --git a/lib/libedit/install-sh b/lib/libedit/install-sh
index 6781b987bdbc..0360b79e7d02 100755
--- a/lib/libedit/install-sh
+++ b/lib/libedit/install-sh
@@ -1,7 +1,7 @@
#!/bin/sh
# install - install a program, script, or datafile
-scriptversion=2009-04-28.21; # UTC
+scriptversion=2016-01-11.22; # UTC
# This originates from X11R5 (mit/util/scripts/install.sh), which was
# later released in X11R6 (xc/config/util/install.sh) with the
@@ -35,25 +35,21 @@ scriptversion=2009-04-28.21; # UTC
# 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
+# '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.
+tab=' '
nl='
'
-IFS=" "" $nl"
+IFS=" $tab$nl"
-# set DOITPROG to echo to test this script
+# 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
+doit_exec=${doit:-exec}
# Put in absolute file names if you don't have them in your path;
# or use environment vars.
@@ -68,17 +64,6 @@ 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.
@@ -97,7 +82,7 @@ dir_arg=
dst_arg=
copy_on_change=false
-no_target_directory=
+is_target_a_directory=possibly
usage="\
Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
@@ -137,42 +122,57 @@ while test $# -ne 0; do
-d) dir_arg=true;;
-g) chgrpcmd="$chgrpprog $2"
- shift;;
+ shift;;
--help) echo "$usage"; exit $?;;
-m) mode=$2
- case $mode in
- *' '* | *' '* | *'
-'* | *'*'* | *'?'* | *'['*)
- echo "$0: invalid mode: $mode" >&2
- exit 1;;
- esac
- shift;;
+ case $mode in
+ *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
-o) chowncmd="$chownprog $2"
- shift;;
+ shift;;
-s) stripcmd=$stripprog;;
- -t) dst_arg=$2
- shift;;
+ -t)
+ is_target_a_directory=always
+ 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;;
+ -T) is_target_a_directory=never;;
--version) echo "$0 $scriptversion"; exit $?;;
- --) shift
- break;;
+ --) shift
+ break;;
- -*) echo "$0: invalid option: $1" >&2
- exit 1;;
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
*) break;;
esac
shift
done
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+ if test -n "$dst_arg"; then
+ echo "$0: target directory not allowed when installing a directory." >&2
+ exit 1
+ fi
+fi
+
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.
@@ -186,6 +186,10 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
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
@@ -194,13 +198,26 @@ if test $# -eq 0; then
echo "$0: no input file specified." >&2
exit 1
fi
- # It's OK to call `install-sh -d' without argument.
+ # 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
- trap '(exit $?); exit' 1 2 13 15
+ if test $# -gt 1 || test "$is_target_a_directory" = always; then
+ if test ! -d "$dst_arg"; then
+ echo "$0: $dst_arg: Is not a directory." >&2
+ exit 1
+ fi
+ fi
+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.
@@ -211,16 +228,16 @@ if test -z "$dir_arg"; then
*[0-7])
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw='% 200'
+ u_plus_rw='% 200'
fi
cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
*)
if test -z "$stripcmd"; then
- u_plus_rw=
+ u_plus_rw=
else
- u_plus_rw=,u+rw
+ u_plus_rw=,u+rw
fi
cp_umask=$mode$u_plus_rw;;
esac
@@ -228,9 +245,9 @@ fi
for src
do
- # Protect names starting with `-'.
+ # Protect names problematic for 'test' and other utilities.
case $src in
- -*) src=./$src;;
+ -* | [=\(\)!]) src=./$src;;
esac
if test -n "$dir_arg"; then
@@ -252,51 +269,20 @@ do
echo "$0: no destination specified." >&2
exit 1
fi
-
dst=$dst_arg
- # Protect names starting with `-'.
- case $dst in
- -*) dst=./$dst;;
- esac
# 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
+ if test "$is_target_a_directory" = never; 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'
- `
-
+ dstdir=`dirname "$dst"`
test -d "$dstdir"
dstdir_status=$?
fi
@@ -307,74 +293,74 @@ do
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;;
+ # 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-writable 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"
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
)
then :
else
@@ -384,53 +370,51 @@ do
# directory the slow way, step by step, checking for races as we go.
case $dstdir in
- /*) prefix='/';;
- -*) prefix='./';;
- *) prefix='';;
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
esac
- eval "$initialize_posix_glob"
-
oIFS=$IFS
IFS=/
- $posix_glob set -f
+ set -f
set fnord $dstdir
shift
- $posix_glob set +f
+ set +f
IFS=$oIFS
prefixes=
for d
do
- test -z "$d" && 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/
+ 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
+ # 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
@@ -465,15 +449,12 @@ do
# 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 &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+ set -f &&
set X $old && old=:$2:$4:$5:$6 &&
set X $new && new=:$2:$4:$5:$6 &&
- $posix_glob set +f &&
-
+ set +f &&
test "$old" = "$new" &&
$cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
then
@@ -486,24 +467,24 @@ do
# 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"
+ # 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
@@ -515,6 +496,6 @@ done
# 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-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
diff --git a/lib/libedit/ltmain.sh b/lib/libedit/ltmain.sh
index 3061e3c5a2f7..0f0a2da3f9dd 100755
--- a/lib/libedit/ltmain.sh
+++ b/lib/libedit/ltmain.sh
@@ -1,9 +1,12 @@
+#! /bin/sh
+## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
+## by inline-source v2014-01-03.01
-# libtool (GNU libtool) 2.4
+# libtool (GNU libtool) 2.4.6
+# Provide generalized library-building support services.
# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
-# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006,
-# 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+# Copyright (C) 1996-2015 Free Software Foundation, Inc.
# This is free software; see the source for copying conditions. There is NO
# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
@@ -23,885 +26,2112 @@
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with GNU Libtool; see the file COPYING. If not, a copy
-# can be downloaded from http://www.gnu.org/licenses/gpl.html,
-# or obtained by writing to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# Usage: $progname [OPTION]... [MODE-ARG]...
-#
-# Provide generalized library-building support services.
-#
-# --config show all configuration variables
-# --debug enable verbose shell tracing
-# -n, --dry-run display commands without modifying any files
-# --features display basic configuration information and exit
-# --mode=MODE use operation mode MODE
-# --preserve-dup-deps don't remove duplicate dependency libraries
-# --quiet, --silent don't print informational messages
-# --no-quiet, --no-silent
-# print informational messages (default)
-# --tag=TAG use configuration variables from tag TAG
-# -v, --verbose print more informational messages than default
-# --no-verbose don't print the extra informational messages
-# --version print version information
-# -h, --help, --help-all print short, long, or detailed help message
-#
-# MODE must be one of the following:
-#
-# clean remove files from the build directory
-# compile compile a source file into a libtool object
-# execute automatically set library path, then run a program
-# finish complete the installation of libtool libraries
-# install install libraries or executables
-# link create a library or an executable
-# uninstall remove libraries from an installed directory
-#
-# MODE-ARGS vary depending on the MODE. When passed as first option,
-# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that.
-# Try `$progname --help --mode=MODE' for a more detailed description of MODE.
-#
-# When reporting a bug, please describe a test case to reproduce it and
-# include the following information:
-#
-# host-triplet: $host
-# shell: $SHELL
-# compiler: $LTCC
-# compiler flags: $LTCFLAGS
-# linker: $LD (gnu? $with_gnu_ld)
-# $progname: (GNU libtool) 2.4
-# automake: $automake_version
-# autoconf: $autoconf_version
-#
-# Report bugs to <bug-libtool@gnu.org>.
-# GNU libtool home page: <http://www.gnu.org/software/libtool/>.
-# General help using GNU software: <http://www.gnu.org/gethelp/>.
PROGRAM=libtool
PACKAGE=libtool
-VERSION=2.4
-TIMESTAMP=""
-package_revision=1.3293
+VERSION=2.4.6
+package_revision=2.4.6
-# Be Bourne compatible
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Run './libtool --help' for help with using this script from the
+# command line.
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# After configure completes, it has a better idea of some of the
+# shell tools we need than the defaults used by the functions shared
+# with bootstrap, so set those here where they can still be over-
+# ridden by the user, but otherwise take precedence.
+
+: ${AUTOCONF="autoconf"}
+: ${AUTOMAKE="automake"}
+
+
+## -------------------------- ##
+## Source external libraries. ##
+## -------------------------- ##
+
+# Much of our low-level functionality needs to be sourced from external
+# libraries, which are installed to $pkgauxdir.
+
+# Set a version string for this script.
+scriptversion=2015-01-20.17; # UTC
+
+# General shell script boiler plate, and helper functions.
+# Written by Gary V. Vaughan, 2004
+
+# Copyright (C) 2004-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# 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.
+
+# As a special exception to the GNU General Public License, if you distribute
+# this file as part of a program or library that is built using GNU Libtool,
+# you may include this file under the same distribution terms that you use
+# for the rest of that program.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNES 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/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# Evaluate this file near the top of your script to gain access to
+# the functions and variables defined here:
+#
+# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh
+#
+# If you need to override any of the default environment variable
+# settings, do that before evaluating this file.
+
+
+## -------------------- ##
+## Shell normalisation. ##
+## -------------------- ##
+
+# Some shells need a little help to be as Bourne compatible as possible.
+# Before doing anything else, make sure all that help has been provided!
+
+DUALCASE=1; export DUALCASE # for MKS sh
+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
emulate sh
NULLCMD=:
- # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which
+ # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
else
- case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac
+ case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac
fi
-BIN_SH=xpg4; export BIN_SH # for Tru64
-DUALCASE=1; export DUALCASE # for MKS sh
-# A function that is used when there is no print builtin or printf.
-func_fallback_echo ()
-{
- eval 'cat <<_LTECHO_EOF
-$1
-_LTECHO_EOF'
-}
-
-# NLS nuisances: We save the old values to restore during execute mode.
-lt_user_locale=
-lt_safe_locale=
-for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
+# NLS nuisances: We save the old values in case they are required later.
+_G_user_locale=
+_G_safe_locale=
+for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES
do
- eval "if test \"\${$lt_var+set}\" = set; then
- save_$lt_var=\$$lt_var
- $lt_var=C
- export $lt_var
- lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\"
- lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\"
+ eval "if test set = \"\${$_G_var+set}\"; then
+ save_$_G_var=\$$_G_var
+ $_G_var=C
+ export $_G_var
+ _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\"
+ _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\"
fi"
done
-LC_ALL=C
-LANGUAGE=C
-export LANGUAGE LC_ALL
-$lt_unset CDPATH
+# CDPATH.
+(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+
+# Make sure IFS has a sensible default
+sp=' '
+nl='
+'
+IFS="$sp $nl"
+
+# There are apparently some retarded systems that use ';' as a PATH separator!
+if test "${PATH_SEPARATOR+set}" != set; then
+ PATH_SEPARATOR=:
+ (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
+ (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
+ PATH_SEPARATOR=';'
+ }
+fi
-# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
-# is ksh but when the shell is invoked as "sh" and the current value of
-# the _XPG environment variable is not equal to 1 (one), the special
-# positional parameter $0, within a function call, is the name of the
-# function.
-progpath="$0"
+## ------------------------- ##
+## Locate command utilities. ##
+## ------------------------- ##
+# func_executable_p FILE
+# ----------------------
+# Check that FILE is an executable regular file.
+func_executable_p ()
+{
+ test -f "$1" && test -x "$1"
+}
+
+
+# func_path_progs PROGS_LIST CHECK_FUNC [PATH]
+# --------------------------------------------
+# Search for either a program that responds to --version with output
+# containing "GNU", or else returned by CHECK_FUNC otherwise, by
+# trying all the directories in PATH with each of the elements of
+# PROGS_LIST.
+#
+# CHECK_FUNC should accept the path to a candidate program, and
+# set $func_check_prog_result if it truncates its output less than
+# $_G_path_prog_max characters.
+func_path_progs ()
+{
+ _G_progs_list=$1
+ _G_check_func=$2
+ _G_PATH=${3-"$PATH"}
+
+ _G_path_prog_max=0
+ _G_path_prog_found=false
+ _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:}
+ for _G_dir in $_G_PATH; do
+ IFS=$_G_save_IFS
+ test -z "$_G_dir" && _G_dir=.
+ for _G_prog_name in $_G_progs_list; do
+ for _exeext in '' .EXE; do
+ _G_path_prog=$_G_dir/$_G_prog_name$_exeext
+ func_executable_p "$_G_path_prog" || continue
+ case `"$_G_path_prog" --version 2>&1` in
+ *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;;
+ *) $_G_check_func $_G_path_prog
+ func_path_progs_result=$func_check_prog_result
+ ;;
+ esac
+ $_G_path_prog_found && break 3
+ done
+ done
+ done
+ IFS=$_G_save_IFS
+ test -z "$func_path_progs_result" && {
+ echo "no acceptable sed could be found in \$PATH" >&2
+ exit 1
+ }
+}
+
+
+# We want to be able to use the functions in this file before configure
+# has figured out where the best binaries are kept, which means we have
+# to search for them ourselves - except when the results are already set
+# where we skip the searches.
+
+# Unless the user overrides by setting SED, search the path for either GNU
+# sed, or the sed that truncates its output the least.
+test -z "$SED" && {
+ _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+ for _G_i in 1 2 3 4 5 6 7; do
+ _G_sed_script=$_G_sed_script$nl$_G_sed_script
+ done
+ echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed
+ _G_sed_script=
+
+ func_check_prog_sed ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo '' >> conftest.nl
+ "$_G_path_prog" -f conftest.sed <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin
+ rm -f conftest.sed
+ SED=$func_path_progs_result
+}
+
+
+# Unless the user overrides by setting GREP, search the path for either GNU
+# grep, or the grep that truncates its output the least.
+test -z "$GREP" && {
+ func_check_prog_grep ()
+ {
+ _G_path_prog=$1
+
+ _G_count=0
+ _G_path_prog_max=0
+ printf 0123456789 >conftest.in
+ while :
+ do
+ cat conftest.in conftest.in >conftest.tmp
+ mv conftest.tmp conftest.in
+ cp conftest.in conftest.nl
+ echo 'GREP' >> conftest.nl
+ "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' <conftest.nl >conftest.out 2>/dev/null || break
+ diff conftest.out conftest.nl >/dev/null 2>&1 || break
+ _G_count=`expr $_G_count + 1`
+ if test "$_G_count" -gt "$_G_path_prog_max"; then
+ # Best one so far, save it but keep looking for a better one
+ func_check_prog_result=$_G_path_prog
+ _G_path_prog_max=$_G_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test 10 -lt "$_G_count" && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out
+ }
+
+ func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin
+ GREP=$func_path_progs_result
+}
+
+
+## ------------------------------- ##
+## User overridable command paths. ##
+## ------------------------------- ##
+
+# All uppercase variable names are used for environment variables. These
+# variables can be overridden by the user before calling a script that
+# uses them if a suitable command of that name is not already available
+# in the command search PATH.
+
: ${CP="cp -f"}
-test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'}
-: ${EGREP="grep -E"}
-: ${FGREP="grep -F"}
-: ${GREP="grep"}
+: ${ECHO="printf %s\n"}
+: ${EGREP="$GREP -E"}
+: ${FGREP="$GREP -F"}
: ${LN_S="ln -s"}
: ${MAKE="make"}
: ${MKDIR="mkdir"}
: ${MV="mv -f"}
: ${RM="rm -f"}
-: ${SED="sed"}
: ${SHELL="${CONFIG_SHELL-/bin/sh}"}
-: ${Xsed="$SED -e 1s/^X//"}
-# Global variables:
-EXIT_SUCCESS=0
-EXIT_FAILURE=1
-EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
-EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
-
-exit_status=$EXIT_SUCCESS
-# Make sure IFS has a sensible default
-lt_nl='
-'
-IFS=" $lt_nl"
+## -------------------- ##
+## Useful sed snippets. ##
+## -------------------- ##
-dirname="s,/[^/]*$,,"
-basename="s,^.*/,,"
+sed_dirname='s|/[^/]*$||'
+sed_basename='s|^.*/||'
-# func_dirname file append nondir_replacement
-# Compute the dirname of FILE. If nonempty, add APPEND to the result,
-# otherwise set result to NONDIR_REPLACEMENT.
-func_dirname ()
-{
- func_dirname_result=`$ECHO "${1}" | $SED "$dirname"`
- if test "X$func_dirname_result" = "X${1}"; then
- func_dirname_result="${3}"
- else
- func_dirname_result="$func_dirname_result${2}"
- fi
-} # func_dirname may be replaced by extended shell implementation
-
-
-# func_basename file
-func_basename ()
-{
- func_basename_result=`$ECHO "${1}" | $SED "$basename"`
-} # func_basename may be replaced by extended shell implementation
+# Sed substitution that helps us do robust quoting. It backslashifies
+# metacharacters that are still active within double-quoted strings.
+sed_quote_subst='s|\([`"$\\]\)|\\\1|g'
+# Same as above, but do not quote variable references.
+sed_double_quote_subst='s/\(["`\\]\)/\\\1/g'
-# func_dirname_and_basename file append nondir_replacement
-# perform func_basename and func_dirname in a single function
-# call:
-# dirname: Compute the dirname of FILE. If nonempty,
-# add APPEND to the result, otherwise set result
-# to NONDIR_REPLACEMENT.
-# value returned in "$func_dirname_result"
-# basename: Compute filename of FILE.
-# value retuned in "$func_basename_result"
-# Implementation must be kept synchronized with func_dirname
-# and func_basename. For efficiency, we do not delegate to
-# those functions but instead duplicate the functionality here.
-func_dirname_and_basename ()
-{
- # Extract subdirectory from the argument.
- func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"`
- if test "X$func_dirname_result" = "X${1}"; then
- func_dirname_result="${3}"
- else
- func_dirname_result="$func_dirname_result${2}"
- fi
- func_basename_result=`$ECHO "${1}" | $SED -e "$basename"`
-} # func_dirname_and_basename may be replaced by extended shell implementation
+# Sed substitution that turns a string into a regex matching for the
+# string literally.
+sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g'
+# Sed substitution that converts a w32 file name or path
+# that contains forward slashes, into one that contains
+# (escaped) backslashes. A very naive implementation.
+sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
+
+# Re-'\' parameter expansions in output of sed_double_quote_subst that
+# were '\'-ed in input to the same. If an odd number of '\' preceded a
+# '$' in input to sed_double_quote_subst, that '$' was protected from
+# expansion. Since each input '\' is now two '\'s, look for any number
+# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'.
+_G_bs='\\'
+_G_bs2='\\\\'
+_G_bs4='\\\\\\\\'
+_G_dollar='\$'
+sed_double_backslash="\
+ s/$_G_bs4/&\\
+/g
+ s/^$_G_bs2$_G_dollar/$_G_bs&/
+ s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g
+ s/\n//g"
-# func_stripname prefix suffix name
-# strip PREFIX and SUFFIX off of NAME.
-# PREFIX and SUFFIX must not contain globbing or regex special
-# characters, hashes, percent signs, but SUFFIX may contain a leading
-# dot (in which case that matches only a dot).
-# func_strip_suffix prefix name
-func_stripname ()
-{
- case ${2} in
- .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;;
- *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;;
- esac
-} # func_stripname may be replaced by extended shell implementation
+## ----------------- ##
+## Global variables. ##
+## ----------------- ##
-# These SED scripts presuppose an absolute path with a trailing slash.
-pathcar='s,^/\([^/]*\).*$,\1,'
-pathcdr='s,^/[^/]*,,'
-removedotparts=':dotsl
- s@/\./@/@g
- t dotsl
- s,/\.$,/,'
-collapseslashes='s@/\{1,\}@/@g'
-finalslash='s,/*$,/,'
+# Except for the global variables explicitly listed below, the following
+# functions in the '^func_' namespace, and the '^require_' namespace
+# variables initialised in the 'Resource management' section, sourcing
+# this file will not pollute your global namespace with anything
+# else. There's no portable way to scope variables in Bourne shell
+# though, so actually running these functions will sometimes place
+# results into a variable named after the function, and often use
+# temporary variables in the '^_G_' namespace. If you are careful to
+# avoid using those namespaces casually in your sourcing script, things
+# should continue to work as you expect. And, of course, you can freely
+# overwrite any of the functions or variables defined here before
+# calling anything to customize them.
-# func_normal_abspath PATH
-# Remove doubled-up and trailing slashes, "." path components,
-# and cancel out any ".." path components in PATH after making
-# it an absolute path.
-# value returned in "$func_normal_abspath_result"
-func_normal_abspath ()
-{
- # Start from root dir and reassemble the path.
- func_normal_abspath_result=
- func_normal_abspath_tpath=$1
- func_normal_abspath_altnamespace=
- case $func_normal_abspath_tpath in
- "")
- # Empty path, that just means $cwd.
- func_stripname '' '/' "`pwd`"
- func_normal_abspath_result=$func_stripname_result
- return
- ;;
- # The next three entries are used to spot a run of precisely
- # two leading slashes without using negated character classes;
- # we take advantage of case's first-match behaviour.
- ///*)
- # Unusual form of absolute path, do nothing.
- ;;
- //*)
- # Not necessarily an ordinary path; POSIX reserves leading '//'
- # and for example Cygwin uses it to access remote file shares
- # over CIFS/SMB, so we conserve a leading double slash if found.
- func_normal_abspath_altnamespace=/
- ;;
- /*)
- # Absolute path, do nothing.
- ;;
- *)
- # Relative path, prepend $cwd.
- func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
- ;;
- esac
- # Cancel out all the simple stuff to save iterations. We also want
- # the path to end with a slash for ease of parsing, so make sure
- # there is one (and only one) here.
- func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
- -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"`
- while :; do
- # Processed it all yet?
- if test "$func_normal_abspath_tpath" = / ; then
- # If we ascended to the root using ".." the result may be empty now.
- if test -z "$func_normal_abspath_result" ; then
- func_normal_abspath_result=/
- fi
- break
- fi
- func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
- -e "$pathcar"`
- func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
- -e "$pathcdr"`
- # Figure out what to do with it
- case $func_normal_abspath_tcomponent in
- "")
- # Trailing empty path component, ignore it.
- ;;
- ..)
- # Parent dir; strip last assembled component from result.
- func_dirname "$func_normal_abspath_result"
- func_normal_abspath_result=$func_dirname_result
- ;;
- *)
- # Actual path component, append it.
- func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent
- ;;
- esac
- done
- # Restore leading double-slash if one was found on entry.
- func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
-}
+EXIT_SUCCESS=0
+EXIT_FAILURE=1
+EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing.
+EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake.
-# func_relative_path SRCDIR DSTDIR
-# generates a relative path from SRCDIR to DSTDIR, with a trailing
-# slash if non-empty, suitable for immediately appending a filename
-# without needing to append a separator.
-# value returned in "$func_relative_path_result"
-func_relative_path ()
-{
- func_relative_path_result=
- func_normal_abspath "$1"
- func_relative_path_tlibdir=$func_normal_abspath_result
- func_normal_abspath "$2"
- func_relative_path_tbindir=$func_normal_abspath_result
-
- # Ascend the tree starting from libdir
- while :; do
- # check if we have found a prefix of bindir
- case $func_relative_path_tbindir in
- $func_relative_path_tlibdir)
- # found an exact match
- func_relative_path_tcancelled=
- break
- ;;
- $func_relative_path_tlibdir*)
- # found a matching prefix
- func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
- func_relative_path_tcancelled=$func_stripname_result
- if test -z "$func_relative_path_result"; then
- func_relative_path_result=.
- fi
- break
- ;;
- *)
- func_dirname $func_relative_path_tlibdir
- func_relative_path_tlibdir=${func_dirname_result}
- if test "x$func_relative_path_tlibdir" = x ; then
- # Have to descend all the way to the root!
- func_relative_path_result=../$func_relative_path_result
- func_relative_path_tcancelled=$func_relative_path_tbindir
- break
- fi
- func_relative_path_result=../$func_relative_path_result
- ;;
- esac
- done
+# Allow overriding, eg assuming that you follow the convention of
+# putting '$debug_cmd' at the start of all your functions, you can get
+# bash to show function call trace with:
+#
+# debug_cmd='eval echo "${FUNCNAME[0]} $*" >&2' bash your-script-name
+debug_cmd=${debug_cmd-":"}
+exit_cmd=:
- # Now calculate path; take care to avoid doubling-up slashes.
- func_stripname '' '/' "$func_relative_path_result"
- func_relative_path_result=$func_stripname_result
- func_stripname '/' '/' "$func_relative_path_tcancelled"
- if test "x$func_stripname_result" != x ; then
- func_relative_path_result=${func_relative_path_result}/${func_stripname_result}
- fi
+# By convention, finish your script with:
+#
+# exit $exit_status
+#
+# so that you can set exit_status to non-zero if you want to indicate
+# something went wrong during execution without actually bailing out at
+# the point of failure.
+exit_status=$EXIT_SUCCESS
- # Normalisation. If bindir is libdir, return empty string,
- # else relative path ending with a slash; either way, target
- # file name can be directly appended.
- if test ! -z "$func_relative_path_result"; then
- func_stripname './' '' "$func_relative_path_result/"
- func_relative_path_result=$func_stripname_result
- fi
-}
+# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh
+# is ksh but when the shell is invoked as "sh" and the current value of
+# the _XPG environment variable is not equal to 1 (one), the special
+# positional parameter $0, within a function call, is the name of the
+# function.
+progpath=$0
-# The name of this program:
-func_dirname_and_basename "$progpath"
-progname=$func_basename_result
+# The name of this program.
+progname=`$ECHO "$progpath" |$SED "$sed_basename"`
-# Make sure we have an absolute path for reexecution:
+# Make sure we have an absolute progpath for reexecution:
case $progpath in
[\\/]*|[A-Za-z]:\\*) ;;
*[\\/]*)
- progdir=$func_dirname_result
+ progdir=`$ECHO "$progpath" |$SED "$sed_dirname"`
progdir=`cd "$progdir" && pwd`
- progpath="$progdir/$progname"
+ progpath=$progdir/$progname
;;
*)
- save_IFS="$IFS"
- IFS=:
+ _G_IFS=$IFS
+ IFS=${PATH_SEPARATOR-:}
for progdir in $PATH; do
- IFS="$save_IFS"
+ IFS=$_G_IFS
test -x "$progdir/$progname" && break
done
- IFS="$save_IFS"
+ IFS=$_G_IFS
test -n "$progdir" || progdir=`pwd`
- progpath="$progdir/$progname"
+ progpath=$progdir/$progname
;;
esac
-# Sed substitution that helps us do robust quoting. It backslashifies
-# metacharacters that are still active within double-quoted strings.
-Xsed="${SED}"' -e 1s/^X//'
-sed_quote_subst='s/\([`"$\\]\)/\\\1/g'
-# Same as above, but do not quote variable references.
-double_quote_subst='s/\(["`\\]\)/\\\1/g'
+## ----------------- ##
+## Standard options. ##
+## ----------------- ##
-# Sed substitution that turns a string into a regex matching for the
-# string literally.
-sed_make_literal_regex='s,[].[^$\\*\/],\\&,g'
-
-# Sed substitution that converts a w32 file name or path
-# which contains forward slashes, into one that contains
-# (escaped) backslashes. A very naive implementation.
-lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g'
-
-# Re-`\' parameter expansions in output of double_quote_subst that were
-# `\'-ed in input to the same. If an odd number of `\' preceded a '$'
-# in input to double_quote_subst, that '$' was protected from expansion.
-# Since each input `\' is now two `\'s, look for any number of runs of
-# four `\'s followed by two `\'s and then a '$'. `\' that '$'.
-bs='\\'
-bs2='\\\\'
-bs4='\\\\\\\\'
-dollar='\$'
-sed_double_backslash="\
- s/$bs4/&\\
-/g
- s/^$bs2$dollar/$bs&/
- s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g
- s/\n//g"
+# The following options affect the operation of the functions defined
+# below, and should be set appropriately depending on run-time para-
+# meters passed on the command line.
-# Standard options:
opt_dry_run=false
-opt_help=false
opt_quiet=false
opt_verbose=false
-opt_warning=:
-# func_echo arg...
-# Echo program name prefixed message, along with the current mode
-# name if it has been set yet.
-func_echo ()
+# Categories 'all' and 'none' are always available. Append any others
+# you will pass as the first argument to func_warning from your own
+# code.
+warning_categories=
+
+# By default, display warnings according to 'opt_warning_types'. Set
+# 'warning_func' to ':' to elide all warnings, or func_fatal_error to
+# treat the next displayed warning as a fatal error.
+warning_func=func_warn_and_continue
+
+# Set to 'all' to display all warnings, 'none' to suppress all
+# warnings, or a space delimited list of some subset of
+# 'warning_categories' to display only the listed warnings.
+opt_warning_types=all
+
+
+## -------------------- ##
+## Resource management. ##
+## -------------------- ##
+
+# This section contains definitions for functions that each ensure a
+# particular resource (a file, or a non-empty configuration variable for
+# example) is available, and if appropriate to extract default values
+# from pertinent package files. Call them using their associated
+# 'require_*' variable to ensure that they are executed, at most, once.
+#
+# It's entirely deliberate that calling these functions can set
+# variables that don't obey the namespace limitations obeyed by the rest
+# of this file, in order that that they be as useful as possible to
+# callers.
+
+
+# require_term_colors
+# -------------------
+# Allow display of bold text on terminals that support it.
+require_term_colors=func_require_term_colors
+func_require_term_colors ()
{
- $ECHO "$progname: ${opt_mode+$opt_mode: }$*"
+ $debug_cmd
+
+ test -t 1 && {
+ # COLORTERM and USE_ANSI_COLORS environment variables take
+ # precedence, because most terminfo databases neglect to describe
+ # whether color sequences are supported.
+ test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"}
+
+ if test 1 = "$USE_ANSI_COLORS"; then
+ # Standard ANSI escape sequences
+ tc_reset=''
+ tc_bold=''; tc_standout=''
+ tc_red=''; tc_green=''
+ tc_blue=''; tc_cyan=''
+ else
+ # Otherwise trust the terminfo database after all.
+ test -n "`tput sgr0 2>/dev/null`" && {
+ tc_reset=`tput sgr0`
+ test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold`
+ tc_standout=$tc_bold
+ test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso`
+ test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1`
+ test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2`
+ test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4`
+ test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5`
+ }
+ fi
+ }
+
+ require_term_colors=:
}
-# func_verbose arg...
-# Echo program name prefixed message in verbose mode only.
-func_verbose ()
+
+## ----------------- ##
+## Function library. ##
+## ----------------- ##
+
+# This section contains a variety of useful functions to call in your
+# scripts. Take note of the portable wrappers for features provided by
+# some modern shells, which will fall back to slower equivalents on
+# less featureful shells.
+
+
+# func_append VAR VALUE
+# ---------------------
+# Append VALUE onto the existing contents of VAR.
+
+ # We should try to minimise forks, especially on Windows where they are
+ # unreasonably slow, so skip the feature probes when bash or zsh are
+ # being used:
+ if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then
+ : ${_G_HAVE_ARITH_OP="yes"}
+ : ${_G_HAVE_XSI_OPS="yes"}
+ # The += operator was introduced in bash 3.1
+ case $BASH_VERSION in
+ [12].* | 3.0 | 3.0*) ;;
+ *)
+ : ${_G_HAVE_PLUSEQ_OP="yes"}
+ ;;
+ esac
+ fi
+
+ # _G_HAVE_PLUSEQ_OP
+ # Can be empty, in which case the shell is probed, "yes" if += is
+ # useable or anything else if it does not work.
+ test -z "$_G_HAVE_PLUSEQ_OP" \
+ && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
+ && _G_HAVE_PLUSEQ_OP=yes
+
+if test yes = "$_G_HAVE_PLUSEQ_OP"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_append ()
+ {
+ $debug_cmd
+
+ eval "$1+=\$2"
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_append ()
+ {
+ $debug_cmd
+
+ eval "$1=\$$1\$2"
+ }
+fi
+
+
+# func_append_quoted VAR VALUE
+# ----------------------------
+# Quote VALUE and append to the end of shell variable VAR, separated
+# by a space.
+if test yes = "$_G_HAVE_PLUSEQ_OP"; then
+ eval 'func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1+=\\ \$func_quote_for_eval_result"
+ }'
+else
+ func_append_quoted ()
+ {
+ $debug_cmd
+
+ func_quote_for_eval "$2"
+ eval "$1=\$$1\\ \$func_quote_for_eval_result"
+ }
+fi
+
+
+# func_append_uniq VAR VALUE
+# --------------------------
+# Append unique VALUE onto the existing contents of VAR, assuming
+# entries are delimited by the first character of VALUE. For example:
+#
+# func_append_uniq options " --another-option option-argument"
+#
+# will only append to $options if " --another-option option-argument "
+# is not already present somewhere in $options already (note spaces at
+# each end implied by leading space in second argument).
+func_append_uniq ()
{
- $opt_verbose && func_echo ${1+"$@"}
+ $debug_cmd
- # A bug in bash halts the script if the last line of a function
- # fails when set -e is in force, so we need another command to
- # work around that:
- :
+ eval _G_current_value='`$ECHO $'$1'`'
+ _G_delim=`expr "$2" : '\(.\)'`
+
+ case $_G_delim$_G_current_value$_G_delim in
+ *"$2$_G_delim"*) ;;
+ *) func_append "$@" ;;
+ esac
}
-# func_echo_all arg...
+
+# func_arith TERM...
+# ------------------
+# Set func_arith_result to the result of evaluating TERMs.
+ test -z "$_G_HAVE_ARITH_OP" \
+ && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \
+ && _G_HAVE_ARITH_OP=yes
+
+if test yes = "$_G_HAVE_ARITH_OP"; then
+ eval 'func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=$(( $* ))
+ }'
+else
+ func_arith ()
+ {
+ $debug_cmd
+
+ func_arith_result=`expr "$@"`
+ }
+fi
+
+
+# func_basename FILE
+# ------------------
+# Set func_basename_result to FILE with everything up to and including
+# the last / stripped.
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ # If this shell supports suffix pattern removal, then use it to avoid
+ # forking. Hide the definitions single quotes in case the shell chokes
+ # on unsupported syntax...
+ _b='func_basename_result=${1##*/}'
+ _d='case $1 in
+ */*) func_dirname_result=${1%/*}$2 ;;
+ * ) func_dirname_result=$3 ;;
+ esac'
+
+else
+ # ...otherwise fall back to using sed.
+ _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`'
+ _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"`
+ if test "X$func_dirname_result" = "X$1"; then
+ func_dirname_result=$3
+ else
+ func_append func_dirname_result "$2"
+ fi'
+fi
+
+eval 'func_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+}'
+
+
+# func_dirname FILE APPEND NONDIR_REPLACEMENT
+# -------------------------------------------
+# Compute the dirname of FILE. If nonempty, add APPEND to the result,
+# otherwise set result to NONDIR_REPLACEMENT.
+eval 'func_dirname ()
+{
+ $debug_cmd
+
+ '"$_d"'
+}'
+
+
+# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT
+# --------------------------------------------------------
+# Perform func_basename and func_dirname in a single function
+# call:
+# dirname: Compute the dirname of FILE. If nonempty,
+# add APPEND to the result, otherwise set result
+# to NONDIR_REPLACEMENT.
+# value returned in "$func_dirname_result"
+# basename: Compute filename of FILE.
+# value retuned in "$func_basename_result"
+# For efficiency, we do not delegate to the functions above but instead
+# duplicate the functionality here.
+eval 'func_dirname_and_basename ()
+{
+ $debug_cmd
+
+ '"$_b"'
+ '"$_d"'
+}'
+
+
+# func_echo ARG...
+# ----------------
+# Echo program name prefixed message.
+func_echo ()
+{
+ $debug_cmd
+
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
+
+
+# func_echo_all ARG...
+# --------------------
# Invoke $ECHO with all args, space-separated.
func_echo_all ()
{
$ECHO "$*"
}
-# func_error arg...
-# Echo program name prefixed message to standard error.
-func_error ()
+
+# func_echo_infix_1 INFIX ARG...
+# ------------------------------
+# Echo program name, followed by INFIX on the first line, with any
+# additional lines not showing INFIX.
+func_echo_infix_1 ()
{
- $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2
+ $debug_cmd
+
+ $require_term_colors
+
+ _G_infix=$1; shift
+ _G_indent=$_G_infix
+ _G_prefix="$progname: $_G_infix: "
+ _G_message=$*
+
+ # Strip color escape sequences before counting printable length
+ for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan"
+ do
+ test -n "$_G_tc" && {
+ _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"`
+ _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"`
+ }
+ done
+ _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes
+
+ func_echo_infix_1_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_infix_1_IFS
+ $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2
+ _G_prefix=$_G_indent
+ done
+ IFS=$func_echo_infix_1_IFS
}
-# func_warning arg...
-# Echo program name prefixed warning message to standard error.
-func_warning ()
+
+# func_error ARG...
+# -----------------
+# Echo program name prefixed message to standard error.
+func_error ()
{
- $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2
+ $debug_cmd
- # bash bug again:
- :
+ $require_term_colors
+
+ func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2
}
-# func_fatal_error arg...
+
+# func_fatal_error ARG...
+# -----------------------
# Echo program name prefixed message to standard error, and exit.
func_fatal_error ()
{
- func_error ${1+"$@"}
- exit $EXIT_FAILURE
-}
+ $debug_cmd
-# func_fatal_help arg...
-# Echo program name prefixed message to standard error, followed by
-# a help hint, and exit.
-func_fatal_help ()
-{
- func_error ${1+"$@"}
- func_fatal_error "$help"
+ func_error "$*"
+ exit $EXIT_FAILURE
}
-help="Try \`$progname --help' for more information." ## default
-# func_grep expression filename
+# func_grep EXPRESSION FILENAME
+# -----------------------------
# Check whether EXPRESSION matches any line of FILENAME, without output.
func_grep ()
{
+ $debug_cmd
+
$GREP "$1" "$2" >/dev/null 2>&1
}
-# func_mkdir_p directory-path
+# func_len STRING
+# ---------------
+# Set func_len_result to the length of STRING. STRING may not
+# start with a hyphen.
+ test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=${#1}
+ }'
+else
+ func_len ()
+ {
+ $debug_cmd
+
+ func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len`
+ }
+fi
+
+
+# func_mkdir_p DIRECTORY-PATH
+# ---------------------------
# Make sure the entire path to DIRECTORY-PATH is available.
func_mkdir_p ()
{
- my_directory_path="$1"
- my_dir_list=
+ $debug_cmd
+
+ _G_directory_path=$1
+ _G_dir_list=
- if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then
+ if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then
- # Protect directory names starting with `-'
- case $my_directory_path in
- -*) my_directory_path="./$my_directory_path" ;;
+ # Protect directory names starting with '-'
+ case $_G_directory_path in
+ -*) _G_directory_path=./$_G_directory_path ;;
esac
# While some portion of DIR does not yet exist...
- while test ! -d "$my_directory_path"; do
+ while test ! -d "$_G_directory_path"; do
# ...make a list in topmost first order. Use a colon delimited
# list incase some portion of path contains whitespace.
- my_dir_list="$my_directory_path:$my_dir_list"
+ _G_dir_list=$_G_directory_path:$_G_dir_list
# If the last portion added has no slash in it, the list is done
- case $my_directory_path in */*) ;; *) break ;; esac
+ case $_G_directory_path in */*) ;; *) break ;; esac
# ...otherwise throw away the child directory and loop
- my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"`
+ _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"`
done
- my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'`
+ _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'`
- save_mkdir_p_IFS="$IFS"; IFS=':'
- for my_dir in $my_dir_list; do
- IFS="$save_mkdir_p_IFS"
- # mkdir can fail with a `File exist' error if two processes
+ func_mkdir_p_IFS=$IFS; IFS=:
+ for _G_dir in $_G_dir_list; do
+ IFS=$func_mkdir_p_IFS
+ # mkdir can fail with a 'File exist' error if two processes
# try to create one of the directories concurrently. Don't
# stop in that case!
- $MKDIR "$my_dir" 2>/dev/null || :
+ $MKDIR "$_G_dir" 2>/dev/null || :
done
- IFS="$save_mkdir_p_IFS"
+ IFS=$func_mkdir_p_IFS
# Bail out if we (or some other process) failed to create a directory.
- test -d "$my_directory_path" || \
- func_fatal_error "Failed to create \`$1'"
+ test -d "$_G_directory_path" || \
+ func_fatal_error "Failed to create '$1'"
fi
}
-# func_mktempdir [string]
+# func_mktempdir [BASENAME]
+# -------------------------
# Make a temporary directory that won't clash with other running
# libtool processes, and avoids race conditions if possible. If
-# given, STRING is the basename for that directory.
+# given, BASENAME is the basename for that directory.
func_mktempdir ()
{
- my_template="${TMPDIR-/tmp}/${1-$progname}"
+ $debug_cmd
- if test "$opt_dry_run" = ":"; then
+ _G_template=${TMPDIR-/tmp}/${1-$progname}
+
+ if test : = "$opt_dry_run"; then
# Return a directory name, but don't create it in dry-run mode
- my_tmpdir="${my_template}-$$"
+ _G_tmpdir=$_G_template-$$
else
# If mktemp works, use that first and foremost
- my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null`
+ _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null`
- if test ! -d "$my_tmpdir"; then
+ if test ! -d "$_G_tmpdir"; then
# Failing that, at least try and use $RANDOM to avoid a race
- my_tmpdir="${my_template}-${RANDOM-0}$$"
+ _G_tmpdir=$_G_template-${RANDOM-0}$$
- save_mktempdir_umask=`umask`
+ func_mktempdir_umask=`umask`
umask 0077
- $MKDIR "$my_tmpdir"
- umask $save_mktempdir_umask
+ $MKDIR "$_G_tmpdir"
+ umask $func_mktempdir_umask
fi
# If we're not in dry-run mode, bomb out on failure
- test -d "$my_tmpdir" || \
- func_fatal_error "cannot create temporary directory \`$my_tmpdir'"
+ test -d "$_G_tmpdir" || \
+ func_fatal_error "cannot create temporary directory '$_G_tmpdir'"
fi
- $ECHO "$my_tmpdir"
+ $ECHO "$_G_tmpdir"
}
-# func_quote_for_eval arg
-# Aesthetically quote ARG to be evaled later.
-# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT
-# is double-quoted, suitable for a subsequent eval, whereas
-# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters
-# which are still active within double quotes backslashified.
-func_quote_for_eval ()
+# func_normal_abspath PATH
+# ------------------------
+# Remove doubled-up and trailing slashes, "." path components,
+# and cancel out any ".." path components in PATH after making
+# it an absolute path.
+func_normal_abspath ()
{
- case $1 in
- *[\\\`\"\$]*)
- func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;;
- *)
- func_quote_for_eval_unquoted_result="$1" ;;
- esac
+ $debug_cmd
- case $func_quote_for_eval_unquoted_result in
- # Double-quote args containing shell metacharacters to delay
- # word splitting, command substitution and and variable
- # expansion for a subsequent eval.
- # Many Bourne shells cannot handle close brackets correctly
- # in scan sets, so we specify it separately.
- *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
- func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\""
+ # These SED scripts presuppose an absolute path with a trailing slash.
+ _G_pathcar='s|^/\([^/]*\).*$|\1|'
+ _G_pathcdr='s|^/[^/]*||'
+ _G_removedotparts=':dotsl
+ s|/\./|/|g
+ t dotsl
+ s|/\.$|/|'
+ _G_collapseslashes='s|/\{1,\}|/|g'
+ _G_finalslash='s|/*$|/|'
+
+ # Start from root dir and reassemble the path.
+ func_normal_abspath_result=
+ func_normal_abspath_tpath=$1
+ func_normal_abspath_altnamespace=
+ case $func_normal_abspath_tpath in
+ "")
+ # Empty path, that just means $cwd.
+ func_stripname '' '/' "`pwd`"
+ func_normal_abspath_result=$func_stripname_result
+ return
+ ;;
+ # The next three entries are used to spot a run of precisely
+ # two leading slashes without using negated character classes;
+ # we take advantage of case's first-match behaviour.
+ ///*)
+ # Unusual form of absolute path, do nothing.
+ ;;
+ //*)
+ # Not necessarily an ordinary path; POSIX reserves leading '//'
+ # and for example Cygwin uses it to access remote file shares
+ # over CIFS/SMB, so we conserve a leading double slash if found.
+ func_normal_abspath_altnamespace=/
+ ;;
+ /*)
+ # Absolute path, do nothing.
;;
*)
- func_quote_for_eval_result="$func_quote_for_eval_unquoted_result"
+ # Relative path, prepend $cwd.
+ func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath
+ ;;
esac
+
+ # Cancel out all the simple stuff to save iterations. We also want
+ # the path to end with a slash for ease of parsing, so make sure
+ # there is one (and only one) here.
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"`
+ while :; do
+ # Processed it all yet?
+ if test / = "$func_normal_abspath_tpath"; then
+ # If we ascended to the root using ".." the result may be empty now.
+ if test -z "$func_normal_abspath_result"; then
+ func_normal_abspath_result=/
+ fi
+ break
+ fi
+ func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcar"`
+ func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \
+ -e "$_G_pathcdr"`
+ # Figure out what to do with it
+ case $func_normal_abspath_tcomponent in
+ "")
+ # Trailing empty path component, ignore it.
+ ;;
+ ..)
+ # Parent dir; strip last assembled component from result.
+ func_dirname "$func_normal_abspath_result"
+ func_normal_abspath_result=$func_dirname_result
+ ;;
+ *)
+ # Actual path component, append it.
+ func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent"
+ ;;
+ esac
+ done
+ # Restore leading double-slash if one was found on entry.
+ func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result
+}
+
+
+# func_notquiet ARG...
+# --------------------
+# Echo program name prefixed message only when not in quiet mode.
+func_notquiet ()
+{
+ $debug_cmd
+
+ $opt_quiet || func_echo ${1+"$@"}
+
+ # A bug in bash halts the script if the last line of a function
+ # fails when set -e is in force, so we need another command to
+ # work around that:
+ :
+}
+
+
+# func_relative_path SRCDIR DSTDIR
+# --------------------------------
+# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR.
+func_relative_path ()
+{
+ $debug_cmd
+
+ func_relative_path_result=
+ func_normal_abspath "$1"
+ func_relative_path_tlibdir=$func_normal_abspath_result
+ func_normal_abspath "$2"
+ func_relative_path_tbindir=$func_normal_abspath_result
+
+ # Ascend the tree starting from libdir
+ while :; do
+ # check if we have found a prefix of bindir
+ case $func_relative_path_tbindir in
+ $func_relative_path_tlibdir)
+ # found an exact match
+ func_relative_path_tcancelled=
+ break
+ ;;
+ $func_relative_path_tlibdir*)
+ # found a matching prefix
+ func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir"
+ func_relative_path_tcancelled=$func_stripname_result
+ if test -z "$func_relative_path_result"; then
+ func_relative_path_result=.
+ fi
+ break
+ ;;
+ *)
+ func_dirname $func_relative_path_tlibdir
+ func_relative_path_tlibdir=$func_dirname_result
+ if test -z "$func_relative_path_tlibdir"; then
+ # Have to descend all the way to the root!
+ func_relative_path_result=../$func_relative_path_result
+ func_relative_path_tcancelled=$func_relative_path_tbindir
+ break
+ fi
+ func_relative_path_result=../$func_relative_path_result
+ ;;
+ esac
+ done
+
+ # Now calculate path; take care to avoid doubling-up slashes.
+ func_stripname '' '/' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ func_stripname '/' '/' "$func_relative_path_tcancelled"
+ if test -n "$func_stripname_result"; then
+ func_append func_relative_path_result "/$func_stripname_result"
+ fi
+
+ # Normalisation. If bindir is libdir, return '.' else relative path.
+ if test -n "$func_relative_path_result"; then
+ func_stripname './' '' "$func_relative_path_result"
+ func_relative_path_result=$func_stripname_result
+ fi
+
+ test -n "$func_relative_path_result" || func_relative_path_result=.
+
+ :
+}
+
+
+# func_quote_for_eval ARG...
+# --------------------------
+# Aesthetically quote ARGs to be evaled later.
+# This function returns two values:
+# i) func_quote_for_eval_result
+# double-quoted, suitable for a subsequent eval
+# ii) func_quote_for_eval_unquoted_result
+# has all characters that are still active within double
+# quotes backslashified.
+func_quote_for_eval ()
+{
+ $debug_cmd
+
+ func_quote_for_eval_unquoted_result=
+ func_quote_for_eval_result=
+ while test 0 -lt $#; do
+ case $1 in
+ *[\\\`\"\$]*)
+ _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
+ *)
+ _G_unquoted_arg=$1 ;;
+ esac
+ if test -n "$func_quote_for_eval_unquoted_result"; then
+ func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
+ else
+ func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
+ fi
+
+ case $_G_unquoted_arg in
+ # Double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and variable expansion
+ # for a subsequent eval.
+ # Many Bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ _G_quoted_arg=\"$_G_unquoted_arg\"
+ ;;
+ *)
+ _G_quoted_arg=$_G_unquoted_arg
+ ;;
+ esac
+
+ if test -n "$func_quote_for_eval_result"; then
+ func_append func_quote_for_eval_result " $_G_quoted_arg"
+ else
+ func_append func_quote_for_eval_result "$_G_quoted_arg"
+ fi
+ shift
+ done
}
-# func_quote_for_expand arg
+# func_quote_for_expand ARG
+# -------------------------
# Aesthetically quote ARG to be evaled later; same as above,
# but do not quote variable references.
func_quote_for_expand ()
{
+ $debug_cmd
+
case $1 in
*[\\\`\"]*)
- my_arg=`$ECHO "$1" | $SED \
- -e "$double_quote_subst" -e "$sed_double_backslash"` ;;
+ _G_arg=`$ECHO "$1" | $SED \
+ -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
*)
- my_arg="$1" ;;
+ _G_arg=$1 ;;
esac
- case $my_arg in
+ case $_G_arg in
# Double-quote args containing shell metacharacters to delay
# word splitting and command substitution for a subsequent eval.
# Many Bourne shells cannot handle close brackets correctly
# in scan sets, so we specify it separately.
*[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
- my_arg="\"$my_arg\""
+ _G_arg=\"$_G_arg\"
;;
esac
- func_quote_for_expand_result="$my_arg"
+ func_quote_for_expand_result=$_G_arg
}
-# func_show_eval cmd [fail_exp]
-# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# func_stripname PREFIX SUFFIX NAME
+# ---------------------------------
+# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result.
+# PREFIX and SUFFIX must not contain globbing or regex special
+# characters, hashes, percent signs, but SUFFIX may contain a leading
+# dot (in which case that matches only a dot).
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_stripname ()
+ {
+ $debug_cmd
+
+ # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are
+ # positional parameters, so assign one to ordinary variable first.
+ func_stripname_result=$3
+ func_stripname_result=${func_stripname_result#"$1"}
+ func_stripname_result=${func_stripname_result%"$2"}
+ }'
+else
+ func_stripname ()
+ {
+ $debug_cmd
+
+ case $2 in
+ .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;;
+ *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;;
+ esac
+ }
+fi
+
+
+# func_show_eval CMD [FAIL_EXP]
+# -----------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
# is given, then evaluate it.
func_show_eval ()
{
- my_cmd="$1"
- my_fail_exp="${2-:}"
+ $debug_cmd
- ${opt_silent-false} || {
- func_quote_for_expand "$my_cmd"
- eval "func_echo $func_quote_for_expand_result"
- }
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
+
+ func_quote_for_expand "$_G_cmd"
+ eval "func_notquiet $func_quote_for_expand_result"
- if ${opt_dry_run-false}; then :; else
- eval "$my_cmd"
- my_status=$?
- if test "$my_status" -eq 0; then :; else
- eval "(exit $my_status); $my_fail_exp"
+ $opt_dry_run || {
+ eval "$_G_cmd"
+ _G_status=$?
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
fi
- fi
+ }
}
-# func_show_eval_locale cmd [fail_exp]
-# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is
+# func_show_eval_locale CMD [FAIL_EXP]
+# ------------------------------------
+# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is
# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP
# is given, then evaluate it. Use the saved locale for evaluation.
func_show_eval_locale ()
{
- my_cmd="$1"
- my_fail_exp="${2-:}"
+ $debug_cmd
+
+ _G_cmd=$1
+ _G_fail_exp=${2-':'}
- ${opt_silent-false} || {
- func_quote_for_expand "$my_cmd"
+ $opt_quiet || {
+ func_quote_for_expand "$_G_cmd"
eval "func_echo $func_quote_for_expand_result"
}
- if ${opt_dry_run-false}; then :; else
- eval "$lt_user_locale
- $my_cmd"
- my_status=$?
- eval "$lt_safe_locale"
- if test "$my_status" -eq 0; then :; else
- eval "(exit $my_status); $my_fail_exp"
+ $opt_dry_run || {
+ eval "$_G_user_locale
+ $_G_cmd"
+ _G_status=$?
+ eval "$_G_safe_locale"
+ if test 0 -ne "$_G_status"; then
+ eval "(exit $_G_status); $_G_fail_exp"
fi
- fi
+ }
}
+
# func_tr_sh
+# ----------
# Turn $1 into a string suitable for a shell variable name.
# Result is stored in $func_tr_sh_result. All characters
# not in the set a-zA-Z0-9_ are replaced with '_'. Further,
# if $1 begins with a digit, a '_' is prepended as well.
func_tr_sh ()
{
- case $1 in
- [0-9]* | *[!a-zA-Z0-9_]*)
- func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'`
- ;;
- * )
- func_tr_sh_result=$1
- ;;
- esac
+ $debug_cmd
+
+ case $1 in
+ [0-9]* | *[!a-zA-Z0-9_]*)
+ func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'`
+ ;;
+ * )
+ func_tr_sh_result=$1
+ ;;
+ esac
}
-# func_version
-# Echo version message to standard output and exit.
-func_version ()
+# func_verbose ARG...
+# -------------------
+# Echo program name prefixed message in verbose mode only.
+func_verbose ()
{
- $opt_debug
+ $debug_cmd
- $SED -n '/(C)/!b go
- :more
- /\./!{
- N
- s/\n# / /
- b more
- }
- :go
- /^# '$PROGRAM' (GNU /,/# warranty; / {
- s/^# //
- s/^# *$//
- s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/
- p
- }' < "$progpath"
- exit $?
+ $opt_verbose && func_echo "$*"
+
+ :
}
-# func_usage
-# Echo short help message to standard output and exit.
-func_usage ()
+
+# func_warn_and_continue ARG...
+# -----------------------------
+# Echo program name prefixed warning message to standard error.
+func_warn_and_continue ()
{
- $opt_debug
+ $debug_cmd
- $SED -n '/^# Usage:/,/^# *.*--help/ {
- s/^# //
- s/^# *$//
- s/\$progname/'$progname'/
- p
- }' < "$progpath"
- echo
- $ECHO "run \`$progname --help | more' for full usage"
- exit $?
+ $require_term_colors
+
+ func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2
+}
+
+
+# func_warning CATEGORY ARG...
+# ----------------------------
+# Echo program name prefixed warning message to standard error. Warning
+# messages can be filtered according to CATEGORY, where this function
+# elides messages where CATEGORY is not listed in the global variable
+# 'opt_warning_types'.
+func_warning ()
+{
+ $debug_cmd
+
+ # CATEGORY must be in the warning_categories list!
+ case " $warning_categories " in
+ *" $1 "*) ;;
+ *) func_internal_error "invalid warning category '$1'" ;;
+ esac
+
+ _G_category=$1
+ shift
+
+ case " $opt_warning_types " in
+ *" $_G_category "*) $warning_func ${1+"$@"} ;;
+ esac
+}
+
+
+# func_sort_ver VER1 VER2
+# -----------------------
+# 'sort -V' is not generally available.
+# Note this deviates from the version comparison in automake
+# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a
+# but this should suffice as we won't be specifying old
+# version formats or redundant trailing .0 in bootstrap.conf.
+# If we did want full compatibility then we should probably
+# use m4_version_compare from autoconf.
+func_sort_ver ()
+{
+ $debug_cmd
+
+ printf '%s\n%s\n' "$1" "$2" \
+ | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n
+}
+
+# func_lt_ver PREV CURR
+# ---------------------
+# Return true if PREV and CURR are in the correct order according to
+# func_sort_ver, otherwise false. Use it like this:
+#
+# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..."
+func_lt_ver ()
+{
+ $debug_cmd
+
+ test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q`
+}
+
+
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
+#! /bin/sh
+
+# Set a version string for this script.
+scriptversion=2014-01-07.03; # UTC
+
+# A portable, pluggable option parser for Bourne shell.
+# Written by Gary V. Vaughan, 2010
+
+# Copyright (C) 2010-2015 Free Software Foundation, Inc.
+# This is free software; see the source for copying conditions. There is NO
+# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# 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/>.
+
+# Please report bugs or propose patches to gary@gnu.org.
+
+
+## ------ ##
+## Usage. ##
+## ------ ##
+
+# This file is a library for parsing options in your shell scripts along
+# with assorted other useful supporting features that you can make use
+# of too.
+#
+# For the simplest scripts you might need only:
+#
+# #!/bin/sh
+# . relative/path/to/funclib.sh
+# . relative/path/to/options-parser
+# scriptversion=1.0
+# func_options ${1+"$@"}
+# eval set dummy "$func_options_result"; shift
+# ...rest of your script...
+#
+# In order for the '--version' option to work, you will need to have a
+# suitably formatted comment like the one at the top of this file
+# starting with '# Written by ' and ending with '# warranty; '.
+#
+# For '-h' and '--help' to work, you will also need a one line
+# description of your script's purpose in a comment directly above the
+# '# Written by ' line, like the one at the top of this file.
+#
+# The default options also support '--debug', which will turn on shell
+# execution tracing (see the comment above debug_cmd below for another
+# use), and '--verbose' and the func_verbose function to allow your script
+# to display verbose messages only when your user has specified
+# '--verbose'.
+#
+# After sourcing this file, you can plug processing for additional
+# options by amending the variables from the 'Configuration' section
+# below, and following the instructions in the 'Option parsing'
+# section further down.
+
+## -------------- ##
+## Configuration. ##
+## -------------- ##
+
+# You should override these variables in your script after sourcing this
+# file so that they reflect the customisations you have added to the
+# option parser.
+
+# The usage line for option parsing errors and the start of '-h' and
+# '--help' output messages. You can embed shell variables for delayed
+# expansion at the time the message is displayed, but you will need to
+# quote other shell meta-characters carefully to prevent them being
+# expanded when the contents are evaled.
+usage='$progpath [OPTION]...'
+
+# Short help message in response to '-h' and '--help'. Add to this or
+# override it after sourcing this library to reflect the full set of
+# options your script accepts.
+usage_message="\
+ --debug enable verbose shell tracing
+ -W, --warnings=CATEGORY
+ report the warnings falling in CATEGORY [all]
+ -v, --verbose verbosely report processing
+ --version print version information and exit
+ -h, --help print short or long help message and exit
+"
+
+# Additional text appended to 'usage_message' in response to '--help'.
+long_help_message="
+Warning categories include:
+ 'all' show all warnings
+ 'none' turn off all the warnings
+ 'error' warnings are treated as fatal errors"
+
+# Help message printed before fatal option parsing errors.
+fatal_help="Try '\$progname --help' for more information."
+
+
+
+## ------------------------- ##
+## Hook function management. ##
+## ------------------------- ##
+
+# This section contains functions for adding, removing, and running hooks
+# to the main code. A hook is just a named list of of function, that can
+# be run in order later on.
+
+# func_hookable FUNC_NAME
+# -----------------------
+# Declare that FUNC_NAME will run hooks added with
+# 'func_add_hook FUNC_NAME ...'.
+func_hookable ()
+{
+ $debug_cmd
+
+ func_append hookable_fns " $1"
}
-# func_help [NOEXIT]
-# Echo long help message to standard output and exit,
-# unless 'noexit' is passed as argument.
+
+# func_add_hook FUNC_NAME HOOK_FUNC
+# ---------------------------------
+# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must
+# first have been declared "hookable" by a call to 'func_hookable'.
+func_add_hook ()
+{
+ $debug_cmd
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not accept hook functions." ;;
+ esac
+
+ eval func_append ${1}_hooks '" $2"'
+}
+
+
+# func_remove_hook FUNC_NAME HOOK_FUNC
+# ------------------------------------
+# Remove HOOK_FUNC from the list of functions called by FUNC_NAME.
+func_remove_hook ()
+{
+ $debug_cmd
+
+ eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`'
+}
+
+
+# func_run_hooks FUNC_NAME [ARG]...
+# ---------------------------------
+# Run all hook functions registered to FUNC_NAME.
+# It is assumed that the list of hook functions contains nothing more
+# than a whitespace-delimited list of legal shell function names, and
+# no effort is wasted trying to catch shell meta-characters or preserve
+# whitespace.
+func_run_hooks ()
+{
+ $debug_cmd
+
+ case " $hookable_fns " in
+ *" $1 "*) ;;
+ *) func_fatal_error "'$1' does not support hook funcions.n" ;;
+ esac
+
+ eval _G_hook_fns=\$$1_hooks; shift
+
+ for _G_hook in $_G_hook_fns; do
+ eval $_G_hook '"$@"'
+
+ # store returned options list back into positional
+ # parameters for next 'cmd' execution.
+ eval _G_hook_result=\$${_G_hook}_result
+ eval set dummy "$_G_hook_result"; shift
+ done
+
+ func_quote_for_eval ${1+"$@"}
+ func_run_hooks_result=$func_quote_for_eval_result
+}
+
+
+
+## --------------- ##
+## Option parsing. ##
+## --------------- ##
+
+# In order to add your own option parsing hooks, you must accept the
+# full positional parameter list in your hook function, remove any
+# options that you action, and then pass back the remaining unprocessed
+# options in '<hooked_function_name>_result', escaped suitably for
+# 'eval'. Like this:
+#
+# my_options_prep ()
+# {
+# $debug_cmd
+#
+# # Extend the existing usage message.
+# usage_message=$usage_message'
+# -s, --silent don'\''t print informational messages
+# '
+#
+# func_quote_for_eval ${1+"$@"}
+# my_options_prep_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_options_prep my_options_prep
+#
+#
+# my_silent_option ()
+# {
+# $debug_cmd
+#
+# # Note that for efficiency, we parse as many options as we can
+# # recognise in a loop before passing the remainder back to the
+# # caller on the first unrecognised argument we encounter.
+# while test $# -gt 0; do
+# opt=$1; shift
+# case $opt in
+# --silent|-s) opt_silent=: ;;
+# # Separate non-argument short options:
+# -s*) func_split_short_opt "$_G_opt"
+# set dummy "$func_split_short_opt_name" \
+# "-$func_split_short_opt_arg" ${1+"$@"}
+# shift
+# ;;
+# *) set dummy "$_G_opt" "$*"; shift; break ;;
+# esac
+# done
+#
+# func_quote_for_eval ${1+"$@"}
+# my_silent_option_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_parse_options my_silent_option
+#
+#
+# my_option_validation ()
+# {
+# $debug_cmd
+#
+# $opt_silent && $opt_verbose && func_fatal_help "\
+# '--silent' and '--verbose' options are mutually exclusive."
+#
+# func_quote_for_eval ${1+"$@"}
+# my_option_validation_result=$func_quote_for_eval_result
+# }
+# func_add_hook func_validate_options my_option_validation
+#
+# You'll alse need to manually amend $usage_message to reflect the extra
+# options you parse. It's preferable to append if you can, so that
+# multiple option parsing hooks can be added safely.
+
+
+# func_options [ARG]...
+# ---------------------
+# All the functions called inside func_options are hookable. See the
+# individual implementations for details.
+func_hookable func_options
+func_options ()
+{
+ $debug_cmd
+
+ func_options_prep ${1+"$@"}
+ eval func_parse_options \
+ ${func_options_prep_result+"$func_options_prep_result"}
+ eval func_validate_options \
+ ${func_parse_options_result+"$func_parse_options_result"}
+
+ eval func_run_hooks func_options \
+ ${func_validate_options_result+"$func_validate_options_result"}
+
+ # save modified positional parameters for caller
+ func_options_result=$func_run_hooks_result
+}
+
+
+# func_options_prep [ARG]...
+# --------------------------
+# All initialisations required before starting the option parse loop.
+# Note that when calling hook functions, we pass through the list of
+# positional parameters. If a hook function modifies that list, and
+# needs to propogate that back to rest of this script, then the complete
+# modified list must be put in 'func_run_hooks_result' before
+# returning.
+func_hookable func_options_prep
+func_options_prep ()
+{
+ $debug_cmd
+
+ # Option defaults:
+ opt_verbose=false
+ opt_warning_types=
+
+ func_run_hooks func_options_prep ${1+"$@"}
+
+ # save modified positional parameters for caller
+ func_options_prep_result=$func_run_hooks_result
+}
+
+
+# func_parse_options [ARG]...
+# ---------------------------
+# The main option parsing loop.
+func_hookable func_parse_options
+func_parse_options ()
+{
+ $debug_cmd
+
+ func_parse_options_result=
+
+ # this just eases exit handling
+ while test $# -gt 0; do
+ # Defer to hook functions for initial option parsing, so they
+ # get priority in the event of reusing an option name.
+ func_run_hooks func_parse_options ${1+"$@"}
+
+ # Adjust func_parse_options positional parameters to match
+ eval set dummy "$func_run_hooks_result"; shift
+
+ # Break out of the loop if we already parsed every option.
+ test $# -gt 0 || break
+
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --debug|-x) debug_cmd='set -x'
+ func_echo "enabling shell trace mode"
+ $debug_cmd
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ set dummy --warnings none ${1+"$@"}
+ shift
+ ;;
+
+ --warnings|--warning|-W)
+ test $# = 0 && func_missing_arg $_G_opt && break
+ case " $warning_categories $1" in
+ *" $1 "*)
+ # trailing space prevents matching last $1 above
+ func_append_uniq opt_warning_types " $1"
+ ;;
+ *all)
+ opt_warning_types=$warning_categories
+ ;;
+ *none)
+ opt_warning_types=none
+ warning_func=:
+ ;;
+ *error)
+ opt_warning_types=$warning_categories
+ warning_func=func_fatal_error
+ ;;
+ *)
+ func_fatal_error \
+ "unsupported warning category: '$1'"
+ ;;
+ esac
+ shift
+ ;;
+
+ --verbose|-v) opt_verbose=: ;;
+ --version) func_version ;;
+ -\?|-h) func_usage ;;
+ --help) func_help ;;
+
+ # Separate optargs to long options (plugins may need this):
+ --*=*) func_split_equals "$_G_opt"
+ set dummy "$func_split_equals_lhs" \
+ "$func_split_equals_rhs" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate optargs to short options:
+ -W*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ # Separate non-argument short options:
+ -\?*|-h*|-v*|-x*)
+ func_split_short_opt "$_G_opt"
+ set dummy "$func_split_short_opt_name" \
+ "-$func_split_short_opt_arg" ${1+"$@"}
+ shift
+ ;;
+
+ --) break ;;
+ -*) func_fatal_help "unrecognised option: '$_G_opt'" ;;
+ *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ esac
+ done
+
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ func_parse_options_result=$func_quote_for_eval_result
+}
+
+
+# func_validate_options [ARG]...
+# ------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+func_hookable func_validate_options
+func_validate_options ()
+{
+ $debug_cmd
+
+ # Display all warnings if -W was not given.
+ test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
+
+ func_run_hooks func_validate_options ${1+"$@"}
+
+ # Bail if the options were screwed!
+ $exit_cmd $EXIT_FAILURE
+
+ # save modified positional parameters for caller
+ func_validate_options_result=$func_run_hooks_result
+}
+
+
+
+## ----------------- ##
+## Helper functions. ##
+## ----------------- ##
+
+# This section contains the helper functions used by the rest of the
+# hookable option parser framework in ascii-betical order.
+
+
+# func_fatal_help ARG...
+# ----------------------
+# Echo program name prefixed message to standard error, followed by
+# a help hint, and exit.
+func_fatal_help ()
+{
+ $debug_cmd
+
+ eval \$ECHO \""Usage: $usage"\"
+ eval \$ECHO \""$fatal_help"\"
+ func_error ${1+"$@"}
+ exit $EXIT_FAILURE
+}
+
+
+# func_help
+# ---------
+# Echo long help message to standard output and exit.
func_help ()
{
- $opt_debug
-
- $SED -n '/^# Usage:/,/# Report bugs to/ {
- :print
- s/^# //
- s/^# *$//
- s*\$progname*'$progname'*
- s*\$host*'"$host"'*
- s*\$SHELL*'"$SHELL"'*
- s*\$LTCC*'"$LTCC"'*
- s*\$LTCFLAGS*'"$LTCFLAGS"'*
- s*\$LD*'"$LD"'*
- s/\$with_gnu_ld/'"$with_gnu_ld"'/
- s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/
- s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/
- p
- d
- }
- /^# .* home page:/b print
- /^# General help using/b print
- ' < "$progpath"
- ret=$?
- if test -z "$1"; then
- exit $ret
- fi
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message"
+ exit 0
}
-# func_missing_arg argname
+
+# func_missing_arg ARGNAME
+# ------------------------
# Echo program name prefixed message to standard error and set global
# exit_cmd.
func_missing_arg ()
{
- $opt_debug
+ $debug_cmd
- func_error "missing argument for $1."
+ func_error "Missing argument for '$1'."
exit_cmd=exit
}
-# func_split_short_opt shortopt
+# func_split_equals STRING
+# ------------------------
+# Set func_split_equals_lhs and func_split_equals_rhs shell variables after
+# splitting STRING at the '=' sign.
+test -z "$_G_HAVE_XSI_OPS" \
+ && (eval 'x=a/b/c;
+ test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
+ && _G_HAVE_XSI_OPS=yes
+
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=${1%%=*}
+ func_split_equals_rhs=${1#*=}
+ test "x$func_split_equals_lhs" = "x$1" \
+ && func_split_equals_rhs=
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_equals ()
+ {
+ $debug_cmd
+
+ func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
+ func_split_equals_rhs=
+ test "x$func_split_equals_lhs" = "x$1" \
+ || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
+ }
+fi #func_split_equals
+
+
+# func_split_short_opt SHORTOPT
+# -----------------------------
# Set func_split_short_opt_name and func_split_short_opt_arg shell
# variables after splitting SHORTOPT after the 2nd character.
-func_split_short_opt ()
+if test yes = "$_G_HAVE_XSI_OPS"
+then
+ # This is an XSI compatible shell, allowing a faster implementation...
+ eval 'func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_arg=${1#??}
+ func_split_short_opt_name=${1%"$func_split_short_opt_arg"}
+ }'
+else
+ # ...otherwise fall back to using expr, which is often a shell builtin.
+ func_split_short_opt ()
+ {
+ $debug_cmd
+
+ func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'`
+ func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
+ }
+fi #func_split_short_opt
+
+
+# func_usage
+# ----------
+# Echo short help message to standard output and exit.
+func_usage ()
{
- my_sed_short_opt='1s/^\(..\).*$/\1/;q'
- my_sed_short_rest='1s/^..\(.*\)$/\1/;q'
+ $debug_cmd
- func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"`
- func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"`
-} # func_split_short_opt may be replaced by extended shell implementation
+ func_usage_message
+ $ECHO "Run '$progname --help |${PAGER-more}' for full usage"
+ exit 0
+}
-# func_split_long_opt longopt
-# Set func_split_long_opt_name and func_split_long_opt_arg shell
-# variables after splitting LONGOPT at the `=' sign.
-func_split_long_opt ()
+# func_usage_message
+# ------------------
+# Echo short help message to standard output.
+func_usage_message ()
{
- my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q'
- my_sed_long_arg='1s/^--[^=]*=//'
+ $debug_cmd
- func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"`
- func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"`
-} # func_split_long_opt may be replaced by extended shell implementation
+ eval \$ECHO \""Usage: $usage"\"
+ echo
+ $SED -n 's|^# ||
+ /^Written by/{
+ x;p;x
+ }
+ h
+ /^Written by/q' < "$progpath"
+ echo
+ eval \$ECHO \""$usage_message"\"
+}
-exit_cmd=:
+# func_version
+# ------------
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $debug_cmd
+ printf '%s\n' "$progname $scriptversion"
+ $SED -n '
+ /(C)/!b go
+ :more
+ /\./!{
+ N
+ s|\n# | |
+ b more
+ }
+ :go
+ /^# Written by /,/# warranty; / {
+ s|^# ||
+ s|^# *$||
+ s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
+ p
+ }
+ /^# Written by / {
+ s|^# ||
+ p
+ }
+ /^warranty; /q' < "$progpath"
+ exit $?
+}
-magic="%%%MAGIC variable%%%"
-magic_exe="%%%MAGIC EXE variable%%%"
+# Local variables:
+# mode: shell-script
+# sh-indentation: 2
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-time-zone: "UTC"
+# End:
-# Global variables.
-nonopt=
-preserve_args=
-lo2o="s/\\.lo\$/.${objext}/"
-o2lo="s/\\.${objext}\$/.lo/"
-extracted_archives=
-extracted_serial=0
+# Set a version string.
+scriptversion='(GNU libtool) 2.4.6'
-# If this variable is set in any of the actions, the command in it
-# will be execed at the end. This prevents here-documents from being
-# left over by shells.
-exec_cmd=
-# func_append var value
-# Append VALUE to the end of shell variable VAR.
-func_append ()
+# func_echo ARG...
+# ----------------
+# Libtool also displays the current mode in messages, so override
+# funclib.sh func_echo with this custom definition.
+func_echo ()
{
- eval "${1}=\$${1}\${2}"
-} # func_append may be replaced by extended shell implementation
+ $debug_cmd
-# func_append_quoted var value
-# Quote VALUE and append to the end of shell variable VAR, separated
-# by a space.
-func_append_quoted ()
-{
- func_quote_for_eval "${2}"
- eval "${1}=\$${1}\\ \$func_quote_for_eval_result"
-} # func_append_quoted may be replaced by extended shell implementation
+ _G_message=$*
+
+ func_echo_IFS=$IFS
+ IFS=$nl
+ for _G_line in $_G_message; do
+ IFS=$func_echo_IFS
+ $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line"
+ done
+ IFS=$func_echo_IFS
+}
-# func_arith arithmetic-term...
-func_arith ()
+# func_warning ARG...
+# -------------------
+# Libtool warnings are not categorized, so override funclib.sh
+# func_warning with this simpler definition.
+func_warning ()
{
- func_arith_result=`expr "${@}"`
-} # func_arith may be replaced by extended shell implementation
+ $debug_cmd
+ $warning_func ${1+"$@"}
+}
-# func_len string
-# STRING may not start with a hyphen.
-func_len ()
-{
- func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len`
-} # func_len may be replaced by extended shell implementation
+## ---------------- ##
+## Options parsing. ##
+## ---------------- ##
+
+# Hook in the functions to make sure our own options are parsed during
+# the option parsing loop.
+
+usage='$progpath [OPTION]... [MODE-ARG]...'
+
+# Short help message in response to '-h'.
+usage_message="Options:
+ --config show all configuration variables
+ --debug enable verbose shell tracing
+ -n, --dry-run display commands without modifying any files
+ --features display basic configuration information and exit
+ --mode=MODE use operation mode MODE
+ --no-warnings equivalent to '-Wnone'
+ --preserve-dup-deps don't remove duplicate dependency libraries
+ --quiet, --silent don't print informational messages
+ --tag=TAG use configuration variables from tag TAG
+ -v, --verbose print more informational messages than default
+ --version print version information
+ -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all]
+ -h, --help, --help-all print short, long, or detailed help message
+"
-# func_lo2o object
-func_lo2o ()
+# Additional text appended to 'usage_message' in response to '--help'.
+func_help ()
{
- func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"`
-} # func_lo2o may be replaced by extended shell implementation
+ $debug_cmd
+
+ func_usage_message
+ $ECHO "$long_help_message
+
+MODE must be one of the following:
+
+ clean remove files from the build directory
+ compile compile a source file into a libtool object
+ execute automatically set library path, then run a program
+ finish complete the installation of libtool libraries
+ install install libraries or executables
+ link create a library or an executable
+ uninstall remove libraries from an installed directory
+
+MODE-ARGS vary depending on the MODE. When passed as first option,
+'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that.
+Try '$progname --help --mode=MODE' for a more detailed description of MODE.
+
+When reporting a bug, please describe a test case to reproduce it and
+include the following information:
+
+ host-triplet: $host
+ shell: $SHELL
+ compiler: $LTCC
+ compiler flags: $LTCFLAGS
+ linker: $LD (gnu? $with_gnu_ld)
+ version: $progname (GNU libtool) 2.4.6
+ automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
+ autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q`
+
+Report bugs to <bug-libtool@gnu.org>.
+GNU libtool home page: <http://www.gnu.org/software/libtool/>.
+General help using GNU software: <http://www.gnu.org/gethelp/>."
+ exit 0
+}
-# func_xform libobj-or-source
-func_xform ()
-{
- func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'`
-} # func_xform may be replaced by extended shell implementation
+# func_lo2o OBJECT-NAME
+# ---------------------
+# Transform OBJECT-NAME from a '.lo' suffix to the platform specific
+# object suffix.
+
+lo2o=s/\\.lo\$/.$objext/
+o2lo=s/\\.$objext\$/.lo/
+if test yes = "$_G_HAVE_XSI_OPS"; then
+ eval 'func_lo2o ()
+ {
+ case $1 in
+ *.lo) func_lo2o_result=${1%.lo}.$objext ;;
+ * ) func_lo2o_result=$1 ;;
+ esac
+ }'
-# func_fatal_configuration arg...
+ # func_xform LIBOBJ-OR-SOURCE
+ # ---------------------------
+ # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise)
+ # suffix to a '.lo' libtool-object suffix.
+ eval 'func_xform ()
+ {
+ func_xform_result=${1%.*}.lo
+ }'
+else
+ # ...otherwise fall back to using sed.
+ func_lo2o ()
+ {
+ func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"`
+ }
+
+ func_xform ()
+ {
+ func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'`
+ }
+fi
+
+
+# func_fatal_configuration ARG...
+# -------------------------------
# Echo program name prefixed message to standard error, followed by
# a configuration failure hint, and exit.
func_fatal_configuration ()
{
- func_error ${1+"$@"}
- func_error "See the $PACKAGE documentation for more information."
- func_fatal_error "Fatal configuration error."
+ func__fatal_error ${1+"$@"} \
+ "See the $PACKAGE documentation for more information." \
+ "Fatal configuration error."
}
# func_config
+# -----------
# Display the configuration for all the tags in this script.
func_config ()
{
@@ -919,17 +2149,19 @@ func_config ()
exit $?
}
+
# func_features
+# -------------
# Display the features supported by this script.
func_features ()
{
echo "host: $host"
- if test "$build_libtool_libs" = yes; then
+ if test yes = "$build_libtool_libs"; then
echo "enable shared libraries"
else
echo "disable shared libraries"
fi
- if test "$build_old_libs" = yes; then
+ if test yes = "$build_old_libs"; then
echo "enable static libraries"
else
echo "disable static libraries"
@@ -938,284 +2170,297 @@ func_features ()
exit $?
}
-# func_enable_tag tagname
+
+# func_enable_tag TAGNAME
+# -----------------------
# Verify that TAGNAME is valid, and either flag an error and exit, or
# enable the TAGNAME tag. We also add TAGNAME to the global $taglist
# variable here.
func_enable_tag ()
{
- # Global variable:
- tagname="$1"
+ # Global variable:
+ tagname=$1
- re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
- re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
- sed_extractcf="/$re_begincf/,/$re_endcf/p"
+ re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$"
+ re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$"
+ sed_extractcf=/$re_begincf/,/$re_endcf/p
- # Validate tagname.
- case $tagname in
- *[!-_A-Za-z0-9,/]*)
- func_fatal_error "invalid tag name: $tagname"
- ;;
- esac
+ # Validate tagname.
+ case $tagname in
+ *[!-_A-Za-z0-9,/]*)
+ func_fatal_error "invalid tag name: $tagname"
+ ;;
+ esac
- # Don't test for the "default" C tag, as we know it's
- # there but not specially marked.
- case $tagname in
- CC) ;;
+ # Don't test for the "default" C tag, as we know it's
+ # there but not specially marked.
+ case $tagname in
+ CC) ;;
*)
- if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
- taglist="$taglist $tagname"
-
- # Evaluate the configuration. Be careful to quote the path
- # and the sed script, to avoid splitting on whitespace, but
- # also don't use non-portable quotes within backquotes within
- # quotes we have to do it in 2 steps:
- extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
- eval "$extractedcf"
- else
- func_error "ignoring unknown tag $tagname"
- fi
- ;;
- esac
+ if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then
+ taglist="$taglist $tagname"
+
+ # Evaluate the configuration. Be careful to quote the path
+ # and the sed script, to avoid splitting on whitespace, but
+ # also don't use non-portable quotes within backquotes within
+ # quotes we have to do it in 2 steps:
+ extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"`
+ eval "$extractedcf"
+ else
+ func_error "ignoring unknown tag $tagname"
+ fi
+ ;;
+ esac
}
+
# func_check_version_match
+# ------------------------
# Ensure that we are using m4 macros, and libtool script from the same
# release of libtool.
func_check_version_match ()
{
- if test "$package_revision" != "$macro_revision"; then
- if test "$VERSION" != "$macro_version"; then
- if test -z "$macro_version"; then
- cat >&2 <<_LT_EOF
+ if test "$package_revision" != "$macro_revision"; then
+ if test "$VERSION" != "$macro_version"; then
+ if test -z "$macro_version"; then
+ cat >&2 <<_LT_EOF
$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
$progname: definition of this LT_INIT comes from an older release.
$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
$progname: and run autoconf again.
_LT_EOF
- else
- cat >&2 <<_LT_EOF
+ else
+ cat >&2 <<_LT_EOF
$progname: Version mismatch error. This is $PACKAGE $VERSION, but the
$progname: definition of this LT_INIT comes from $PACKAGE $macro_version.
$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION
$progname: and run autoconf again.
_LT_EOF
- fi
- else
- cat >&2 <<_LT_EOF
+ fi
+ else
+ cat >&2 <<_LT_EOF
$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision,
$progname: but the definition of this LT_INIT comes from revision $macro_revision.
$progname: You should recreate aclocal.m4 with macros from revision $package_revision
$progname: of $PACKAGE $VERSION and run autoconf again.
_LT_EOF
- fi
+ fi
- exit $EXIT_MISMATCH
- fi
+ exit $EXIT_MISMATCH
+ fi
}
-# Shorthand for --mode=foo, only valid as the first argument
-case $1 in
-clean|clea|cle|cl)
- shift; set dummy --mode clean ${1+"$@"}; shift
- ;;
-compile|compil|compi|comp|com|co|c)
- shift; set dummy --mode compile ${1+"$@"}; shift
- ;;
-execute|execut|execu|exec|exe|ex|e)
- shift; set dummy --mode execute ${1+"$@"}; shift
- ;;
-finish|finis|fini|fin|fi|f)
- shift; set dummy --mode finish ${1+"$@"}; shift
- ;;
-install|instal|insta|inst|ins|in|i)
- shift; set dummy --mode install ${1+"$@"}; shift
- ;;
-link|lin|li|l)
- shift; set dummy --mode link ${1+"$@"}; shift
- ;;
-uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
- shift; set dummy --mode uninstall ${1+"$@"}; shift
- ;;
-esac
+# libtool_options_prep [ARG]...
+# -----------------------------
+# Preparation for options parsed by libtool.
+libtool_options_prep ()
+{
+ $debug_mode
+ # Option defaults:
+ opt_config=false
+ opt_dlopen=
+ opt_dry_run=false
+ opt_help=false
+ opt_mode=
+ opt_preserve_dup_deps=false
+ opt_quiet=false
+ nonopt=
+ preserve_args=
-# Option defaults:
-opt_debug=:
-opt_dry_run=false
-opt_config=false
-opt_preserve_dup_deps=false
-opt_features=false
-opt_finish=false
-opt_help=false
-opt_help_all=false
-opt_silent=:
-opt_verbose=:
-opt_silent=false
-opt_verbose=false
+ # Shorthand for --mode=foo, only valid as the first argument
+ case $1 in
+ clean|clea|cle|cl)
+ shift; set dummy --mode clean ${1+"$@"}; shift
+ ;;
+ compile|compil|compi|comp|com|co|c)
+ shift; set dummy --mode compile ${1+"$@"}; shift
+ ;;
+ execute|execut|execu|exec|exe|ex|e)
+ shift; set dummy --mode execute ${1+"$@"}; shift
+ ;;
+ finish|finis|fini|fin|fi|f)
+ shift; set dummy --mode finish ${1+"$@"}; shift
+ ;;
+ install|instal|insta|inst|ins|in|i)
+ shift; set dummy --mode install ${1+"$@"}; shift
+ ;;
+ link|lin|li|l)
+ shift; set dummy --mode link ${1+"$@"}; shift
+ ;;
+ uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
+ shift; set dummy --mode uninstall ${1+"$@"}; shift
+ ;;
+ esac
+
+ # Pass back the list of options.
+ func_quote_for_eval ${1+"$@"}
+ libtool_options_prep_result=$func_quote_for_eval_result
+}
+func_add_hook func_options_prep libtool_options_prep
-# Parse options once, thoroughly. This comes as soon as possible in the
-# script to make things like `--version' happen as quickly as we can.
+# libtool_parse_options [ARG]...
+# ---------------------------------
+# Provide handling for libtool specific options.
+libtool_parse_options ()
{
- # this just eases exit handling
- while test $# -gt 0; do
- opt="$1"
- shift
- case $opt in
- --debug|-x) opt_debug='set -x'
- func_echo "enabling shell trace mode"
- $opt_debug
- ;;
- --dry-run|--dryrun|-n)
- opt_dry_run=:
- ;;
- --config)
- opt_config=:
-func_config
- ;;
- --dlopen|-dlopen)
- optarg="$1"
- opt_dlopen="${opt_dlopen+$opt_dlopen
-}$optarg"
- shift
- ;;
- --preserve-dup-deps)
- opt_preserve_dup_deps=:
- ;;
- --features)
- opt_features=:
-func_features
- ;;
- --finish)
- opt_finish=:
-set dummy --mode finish ${1+"$@"}; shift
- ;;
- --help)
- opt_help=:
- ;;
- --help-all)
- opt_help_all=:
-opt_help=': help-all'
- ;;
- --mode)
- test $# = 0 && func_missing_arg $opt && break
- optarg="$1"
- opt_mode="$optarg"
-case $optarg in
- # Valid mode arguments:
- clean|compile|execute|finish|install|link|relink|uninstall) ;;
-
- # Catch anything else as an error
- *) func_error "invalid argument for $opt"
- exit_cmd=exit
- break
- ;;
-esac
- shift
- ;;
- --no-silent|--no-quiet)
- opt_silent=false
-func_append preserve_args " $opt"
- ;;
- --no-verbose)
- opt_verbose=false
-func_append preserve_args " $opt"
- ;;
- --silent|--quiet)
- opt_silent=:
-func_append preserve_args " $opt"
- opt_verbose=false
- ;;
- --verbose|-v)
- opt_verbose=:
-func_append preserve_args " $opt"
-opt_silent=false
- ;;
- --tag)
- test $# = 0 && func_missing_arg $opt && break
- optarg="$1"
- opt_tag="$optarg"
-func_append preserve_args " $opt $optarg"
-func_enable_tag "$optarg"
- shift
- ;;
-
- -\?|-h) func_usage ;;
- --help) func_help ;;
- --version) func_version ;;
-
- # Separate optargs to long options:
- --*=*)
- func_split_long_opt "$opt"
- set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"}
- shift
- ;;
-
- # Separate non-argument short options:
- -\?*|-h*|-n*|-v*)
- func_split_short_opt "$opt"
- set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"}
- shift
- ;;
-
- --) break ;;
- -*) func_fatal_help "unrecognized option \`$opt'" ;;
- *) set dummy "$opt" ${1+"$@"}; shift; break ;;
- esac
- done
+ $debug_cmd
- # Validate options:
+ # Perform our own loop to consume as many options as possible in
+ # each iteration.
+ while test $# -gt 0; do
+ _G_opt=$1
+ shift
+ case $_G_opt in
+ --dry-run|--dryrun|-n)
+ opt_dry_run=:
+ ;;
+
+ --config) func_config ;;
+
+ --dlopen|-dlopen)
+ opt_dlopen="${opt_dlopen+$opt_dlopen
+}$1"
+ shift
+ ;;
+
+ --preserve-dup-deps)
+ opt_preserve_dup_deps=: ;;
+
+ --features) func_features ;;
+
+ --finish) set dummy --mode finish ${1+"$@"}; shift ;;
+
+ --help) opt_help=: ;;
+
+ --help-all) opt_help=': help-all' ;;
+
+ --mode) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_mode=$1
+ case $1 in
+ # Valid mode arguments:
+ clean|compile|execute|finish|install|link|relink|uninstall) ;;
+
+ # Catch anything else as an error
+ *) func_error "invalid argument for $_G_opt"
+ exit_cmd=exit
+ break
+ ;;
+ esac
+ shift
+ ;;
+
+ --no-silent|--no-quiet)
+ opt_quiet=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-warnings|--no-warning|--no-warn)
+ opt_warning=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --no-verbose)
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --silent|--quiet)
+ opt_quiet=:
+ opt_verbose=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ --tag) test $# = 0 && func_missing_arg $_G_opt && break
+ opt_tag=$1
+ func_append preserve_args " $_G_opt $1"
+ func_enable_tag "$1"
+ shift
+ ;;
+
+ --verbose|-v) opt_quiet=false
+ opt_verbose=:
+ func_append preserve_args " $_G_opt"
+ ;;
+
+ # An option not handled by this hook function:
+ *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ esac
+ done
- # save first non-option argument
- if test "$#" -gt 0; then
- nonopt="$opt"
- shift
- fi
- # preserve --debug
- test "$opt_debug" = : || func_append preserve_args " --debug"
+ # save modified positional parameters for caller
+ func_quote_for_eval ${1+"$@"}
+ libtool_parse_options_result=$func_quote_for_eval_result
+}
+func_add_hook func_parse_options libtool_parse_options
- case $host in
- *cygwin* | *mingw* | *pw32* | *cegcc*)
- # don't eliminate duplications in $postdeps and $predeps
- opt_duplicate_compiler_generated_deps=:
- ;;
- *)
- opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
- ;;
- esac
- $opt_help || {
- # Sanity checks first:
- func_check_version_match
- if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then
- func_fatal_configuration "not configured to build any kind of library"
+# libtool_validate_options [ARG]...
+# ---------------------------------
+# Perform any sanity checks on option settings and/or unconsumed
+# arguments.
+libtool_validate_options ()
+{
+ # save first non-option argument
+ if test 0 -lt $#; then
+ nonopt=$1
+ shift
fi
- # Darwin sucks
- eval std_shrext=\"$shrext_cmds\"
+ # preserve --debug
+ test : = "$debug_cmd" || func_append preserve_args " --debug"
- # Only execute mode is allowed to have -dlopen flags.
- if test -n "$opt_dlopen" && test "$opt_mode" != execute; then
- func_error "unrecognized option \`-dlopen'"
- $ECHO "$help" 1>&2
- exit $EXIT_FAILURE
- fi
+ case $host in
+ # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
+ # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
+ *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
+ # don't eliminate duplications in $postdeps and $predeps
+ opt_duplicate_compiler_generated_deps=:
+ ;;
+ *)
+ opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps
+ ;;
+ esac
- # Change the help message to a mode-specific one.
- generic_help="$help"
- help="Try \`$progname --help --mode=$opt_mode' for more information."
- }
+ $opt_help || {
+ # Sanity checks first:
+ func_check_version_match
+
+ test yes != "$build_libtool_libs" \
+ && test yes != "$build_old_libs" \
+ && func_fatal_configuration "not configured to build any kind of library"
+ # Darwin sucks
+ eval std_shrext=\"$shrext_cmds\"
- # Bail if the options were screwed
- $exit_cmd $EXIT_FAILURE
+ # Only execute mode is allowed to have -dlopen flags.
+ if test -n "$opt_dlopen" && test execute != "$opt_mode"; then
+ func_error "unrecognized option '-dlopen'"
+ $ECHO "$help" 1>&2
+ exit $EXIT_FAILURE
+ fi
+
+ # Change the help message to a mode-specific one.
+ generic_help=$help
+ help="Try '$progname --help --mode=$opt_mode' for more information."
+ }
+
+ # Pass back the unparsed argument list
+ func_quote_for_eval ${1+"$@"}
+ libtool_validate_options_result=$func_quote_for_eval_result
}
+func_add_hook func_validate_options libtool_validate_options
+
+# Process options as early as possible so that --help and --version
+# can return quickly.
+func_options ${1+"$@"}
+eval set dummy "$func_options_result"; shift
@@ -1223,24 +2468,52 @@ func_enable_tag "$optarg"
## Main. ##
## ----------- ##
+magic='%%%MAGIC variable%%%'
+magic_exe='%%%MAGIC EXE variable%%%'
+
+# Global variables.
+extracted_archives=
+extracted_serial=0
+
+# If this variable is set in any of the actions, the command in it
+# will be execed at the end. This prevents here-documents from being
+# left over by shells.
+exec_cmd=
+
+
+# A function that is used when there is no print builtin or printf.
+func_fallback_echo ()
+{
+ eval 'cat <<_LTECHO_EOF
+$1
+_LTECHO_EOF'
+}
+
+# func_generated_by_libtool
+# True iff stdin has been generated by Libtool. This function is only
+# a basic sanity check; it will hardly flush out determined imposters.
+func_generated_by_libtool_p ()
+{
+ $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+}
+
# func_lalib_p file
-# True iff FILE is a libtool `.la' library or `.lo' object file.
+# True iff FILE is a libtool '.la' library or '.lo' object file.
# This function is only a basic sanity check; it will hardly flush out
# determined imposters.
func_lalib_p ()
{
test -f "$1" &&
- $SED -e 4q "$1" 2>/dev/null \
- | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1
+ $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p
}
# func_lalib_unsafe_p file
-# True iff FILE is a libtool `.la' library or `.lo' object file.
+# True iff FILE is a libtool '.la' library or '.lo' object file.
# This function implements the same check as func_lalib_p without
# resorting to external programs. To this end, it redirects stdin and
# closes it afterwards, without saving the original file descriptor.
# As a safety measure, use it only where a negative result would be
-# fatal anyway. Works if `file' does not exist.
+# fatal anyway. Works if 'file' does not exist.
func_lalib_unsafe_p ()
{
lalib_p=no
@@ -1248,13 +2521,13 @@ func_lalib_unsafe_p ()
for lalib_p_l in 1 2 3 4
do
read lalib_p_line
- case "$lalib_p_line" in
+ case $lalib_p_line in
\#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;;
esac
done
exec 0<&5 5<&-
fi
- test "$lalib_p" = yes
+ test yes = "$lalib_p"
}
# func_ltwrapper_script_p file
@@ -1263,7 +2536,8 @@ func_lalib_unsafe_p ()
# determined imposters.
func_ltwrapper_script_p ()
{
- func_lalib_p "$1"
+ test -f "$1" &&
+ $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p
}
# func_ltwrapper_executable_p file
@@ -1288,7 +2562,7 @@ func_ltwrapper_scriptname ()
{
func_dirname_and_basename "$1" "" "."
func_stripname '' '.exe' "$func_basename_result"
- func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper"
+ func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper
}
# func_ltwrapper_p file
@@ -1307,11 +2581,13 @@ func_ltwrapper_p ()
# FAIL_CMD may read-access the current command in variable CMD!
func_execute_cmds ()
{
- $opt_debug
+ $debug_cmd
+
save_ifs=$IFS; IFS='~'
for cmd in $1; do
- IFS=$save_ifs
+ IFS=$sp$nl
eval cmd=\"$cmd\"
+ IFS=$save_ifs
func_show_eval "$cmd" "${2-:}"
done
IFS=$save_ifs
@@ -1323,10 +2599,11 @@ func_execute_cmds ()
# Note that it is not necessary on cygwin/mingw to append a dot to
# FILE even if both FILE and FILE.exe exist: automatic-append-.exe
# behavior happens only for exec(3), not for open(2)! Also, sourcing
-# `FILE.' does not work on cygwin managed mounts.
+# 'FILE.' does not work on cygwin managed mounts.
func_source ()
{
- $opt_debug
+ $debug_cmd
+
case $1 in
*/* | *\\*) . "$1" ;;
*) . "./$1" ;;
@@ -1353,10 +2630,10 @@ func_resolve_sysroot ()
# store the result into func_replace_sysroot_result.
func_replace_sysroot ()
{
- case "$lt_sysroot:$1" in
+ case $lt_sysroot:$1 in
?*:"$lt_sysroot"*)
func_stripname "$lt_sysroot" '' "$1"
- func_replace_sysroot_result="=$func_stripname_result"
+ func_replace_sysroot_result='='$func_stripname_result
;;
*)
# Including no sysroot.
@@ -1373,7 +2650,8 @@ func_replace_sysroot ()
# arg is usually of the form 'gcc ...'
func_infer_tag ()
{
- $opt_debug
+ $debug_cmd
+
if test -n "$available_tags" && test -z "$tagname"; then
CC_quoted=
for arg in $CC; do
@@ -1392,7 +2670,7 @@ func_infer_tag ()
for z in $available_tags; do
if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then
# Evaluate the configuration.
- eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
+ eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`"
CC_quoted=
for arg in $CC; do
# Double-quote args containing other shell metacharacters.
@@ -1417,7 +2695,7 @@ func_infer_tag ()
# line option must be used.
if test -z "$tagname"; then
func_echo "unable to infer tagged configuration"
- func_fatal_error "specify a tag with \`--tag'"
+ func_fatal_error "specify a tag with '--tag'"
# else
# func_verbose "using $tagname tagged configuration"
fi
@@ -1433,15 +2711,15 @@ func_infer_tag ()
# but don't create it if we're doing a dry run.
func_write_libtool_object ()
{
- write_libobj=${1}
- if test "$build_libtool_libs" = yes; then
- write_lobj=\'${2}\'
+ write_libobj=$1
+ if test yes = "$build_libtool_libs"; then
+ write_lobj=\'$2\'
else
write_lobj=none
fi
- if test "$build_old_libs" = yes; then
- write_oldobj=\'${3}\'
+ if test yes = "$build_old_libs"; then
+ write_oldobj=\'$3\'
else
write_oldobj=none
fi
@@ -1449,7 +2727,7 @@ func_write_libtool_object ()
$opt_dry_run || {
cat >${write_libobj}T <<EOF
# $write_libobj - a libtool object file
-# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
@@ -1461,7 +2739,7 @@ pic_object=$write_lobj
non_pic_object=$write_oldobj
EOF
- $MV "${write_libobj}T" "${write_libobj}"
+ $MV "${write_libobj}T" "$write_libobj"
}
}
@@ -1481,8 +2759,9 @@ EOF
# be empty on error (or when ARG is empty)
func_convert_core_file_wine_to_w32 ()
{
- $opt_debug
- func_convert_core_file_wine_to_w32_result="$1"
+ $debug_cmd
+
+ func_convert_core_file_wine_to_w32_result=$1
if test -n "$1"; then
# Unfortunately, winepath does not exit with a non-zero error code, so we
# are forced to check the contents of stdout. On the other hand, if the
@@ -1490,9 +2769,9 @@ func_convert_core_file_wine_to_w32 ()
# *an error message* to stdout. So we must check for both error code of
# zero AND non-empty stdout, which explains the odd construction:
func_convert_core_file_wine_to_w32_tmp=`winepath -w "$1" 2>/dev/null`
- if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then
+ if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then
func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" |
- $SED -e "$lt_sed_naive_backslashify"`
+ $SED -e "$sed_naive_backslashify"`
else
func_convert_core_file_wine_to_w32_result=
fi
@@ -1513,18 +2792,19 @@ func_convert_core_file_wine_to_w32 ()
# are convertible, then the result may be empty.
func_convert_core_path_wine_to_w32 ()
{
- $opt_debug
+ $debug_cmd
+
# unfortunately, winepath doesn't convert paths, only file names
- func_convert_core_path_wine_to_w32_result=""
+ func_convert_core_path_wine_to_w32_result=
if test -n "$1"; then
oldIFS=$IFS
IFS=:
for func_convert_core_path_wine_to_w32_f in $1; do
IFS=$oldIFS
func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f"
- if test -n "$func_convert_core_file_wine_to_w32_result" ; then
+ if test -n "$func_convert_core_file_wine_to_w32_result"; then
if test -z "$func_convert_core_path_wine_to_w32_result"; then
- func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result"
+ func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result
else
func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result"
fi
@@ -1553,7 +2833,8 @@ func_convert_core_path_wine_to_w32 ()
# environment variable; do not put it in $PATH.
func_cygpath ()
{
- $opt_debug
+ $debug_cmd
+
if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then
func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null`
if test "$?" -ne 0; then
@@ -1562,7 +2843,7 @@ func_cygpath ()
fi
else
func_cygpath_result=
- func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'"
+ func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'"
fi
}
#end: func_cygpath
@@ -1573,10 +2854,11 @@ func_cygpath ()
# result in func_convert_core_msys_to_w32_result.
func_convert_core_msys_to_w32 ()
{
- $opt_debug
+ $debug_cmd
+
# awkward: cmd appends spaces to result
func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null |
- $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"`
+ $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"`
}
#end: func_convert_core_msys_to_w32
@@ -1587,13 +2869,14 @@ func_convert_core_msys_to_w32 ()
# func_to_host_file_result to ARG1).
func_convert_file_check ()
{
- $opt_debug
- if test -z "$2" && test -n "$1" ; then
+ $debug_cmd
+
+ if test -z "$2" && test -n "$1"; then
func_error "Could not determine host file name corresponding to"
- func_error " \`$1'"
+ func_error " '$1'"
func_error "Continuing, but uninstalled executables may not work."
# Fallback:
- func_to_host_file_result="$1"
+ func_to_host_file_result=$1
fi
}
# end func_convert_file_check
@@ -1605,10 +2888,11 @@ func_convert_file_check ()
# func_to_host_file_result to a simplistic fallback value (see below).
func_convert_path_check ()
{
- $opt_debug
+ $debug_cmd
+
if test -z "$4" && test -n "$3"; then
func_error "Could not determine the host path corresponding to"
- func_error " \`$3'"
+ func_error " '$3'"
func_error "Continuing, but uninstalled executables may not work."
# Fallback. This is a deliberately simplistic "conversion" and
# should not be "improved". See libtool.info.
@@ -1617,7 +2901,7 @@ func_convert_path_check ()
func_to_host_path_result=`echo "$3" |
$SED -e "$lt_replace_pathsep_chars"`
else
- func_to_host_path_result="$3"
+ func_to_host_path_result=$3
fi
fi
}
@@ -1629,9 +2913,10 @@ func_convert_path_check ()
# and appending REPL if ORIG matches BACKPAT.
func_convert_path_front_back_pathsep ()
{
- $opt_debug
+ $debug_cmd
+
case $4 in
- $1 ) func_to_host_path_result="$3$func_to_host_path_result"
+ $1 ) func_to_host_path_result=$3$func_to_host_path_result
;;
esac
case $4 in
@@ -1645,7 +2930,7 @@ func_convert_path_front_back_pathsep ()
##################################################
# $build to $host FILE NAME CONVERSION FUNCTIONS #
##################################################
-# invoked via `$to_host_file_cmd ARG'
+# invoked via '$to_host_file_cmd ARG'
#
# In each case, ARG is the path to be converted from $build to $host format.
# Result will be available in $func_to_host_file_result.
@@ -1656,7 +2941,8 @@ func_convert_path_front_back_pathsep ()
# in func_to_host_file_result.
func_to_host_file ()
{
- $opt_debug
+ $debug_cmd
+
$to_host_file_cmd "$1"
}
# end func_to_host_file
@@ -1668,7 +2954,8 @@ func_to_host_file ()
# in (the comma separated) LAZY, no conversion takes place.
func_to_tool_file ()
{
- $opt_debug
+ $debug_cmd
+
case ,$2, in
*,"$to_tool_file_cmd",*)
func_to_tool_file_result=$1
@@ -1686,7 +2973,7 @@ func_to_tool_file ()
# Copy ARG to func_to_host_file_result.
func_convert_file_noop ()
{
- func_to_host_file_result="$1"
+ func_to_host_file_result=$1
}
# end func_convert_file_noop
@@ -1697,11 +2984,12 @@ func_convert_file_noop ()
# func_to_host_file_result.
func_convert_file_msys_to_w32 ()
{
- $opt_debug
- func_to_host_file_result="$1"
+ $debug_cmd
+
+ func_to_host_file_result=$1
if test -n "$1"; then
func_convert_core_msys_to_w32 "$1"
- func_to_host_file_result="$func_convert_core_msys_to_w32_result"
+ func_to_host_file_result=$func_convert_core_msys_to_w32_result
fi
func_convert_file_check "$1" "$func_to_host_file_result"
}
@@ -1713,8 +3001,9 @@ func_convert_file_msys_to_w32 ()
# func_to_host_file_result.
func_convert_file_cygwin_to_w32 ()
{
- $opt_debug
- func_to_host_file_result="$1"
+ $debug_cmd
+
+ func_to_host_file_result=$1
if test -n "$1"; then
# because $build is cygwin, we call "the" cygpath in $PATH; no need to use
# LT_CYGPATH in this case.
@@ -1730,11 +3019,12 @@ func_convert_file_cygwin_to_w32 ()
# and a working winepath. Returns result in func_to_host_file_result.
func_convert_file_nix_to_w32 ()
{
- $opt_debug
- func_to_host_file_result="$1"
+ $debug_cmd
+
+ func_to_host_file_result=$1
if test -n "$1"; then
func_convert_core_file_wine_to_w32 "$1"
- func_to_host_file_result="$func_convert_core_file_wine_to_w32_result"
+ func_to_host_file_result=$func_convert_core_file_wine_to_w32_result
fi
func_convert_file_check "$1" "$func_to_host_file_result"
}
@@ -1746,12 +3036,13 @@ func_convert_file_nix_to_w32 ()
# Returns result in func_to_host_file_result.
func_convert_file_msys_to_cygwin ()
{
- $opt_debug
- func_to_host_file_result="$1"
+ $debug_cmd
+
+ func_to_host_file_result=$1
if test -n "$1"; then
func_convert_core_msys_to_w32 "$1"
func_cygpath -u "$func_convert_core_msys_to_w32_result"
- func_to_host_file_result="$func_cygpath_result"
+ func_to_host_file_result=$func_cygpath_result
fi
func_convert_file_check "$1" "$func_to_host_file_result"
}
@@ -1764,13 +3055,14 @@ func_convert_file_msys_to_cygwin ()
# in func_to_host_file_result.
func_convert_file_nix_to_cygwin ()
{
- $opt_debug
- func_to_host_file_result="$1"
+ $debug_cmd
+
+ func_to_host_file_result=$1
if test -n "$1"; then
# convert from *nix to w32, then use cygpath to convert from w32 to cygwin.
func_convert_core_file_wine_to_w32 "$1"
func_cygpath -u "$func_convert_core_file_wine_to_w32_result"
- func_to_host_file_result="$func_cygpath_result"
+ func_to_host_file_result=$func_cygpath_result
fi
func_convert_file_check "$1" "$func_to_host_file_result"
}
@@ -1780,7 +3072,7 @@ func_convert_file_nix_to_cygwin ()
#############################################
# $build to $host PATH CONVERSION FUNCTIONS #
#############################################
-# invoked via `$to_host_path_cmd ARG'
+# invoked via '$to_host_path_cmd ARG'
#
# In each case, ARG is the path to be converted from $build to $host format.
# The result will be available in $func_to_host_path_result.
@@ -1804,10 +3096,11 @@ func_convert_file_nix_to_cygwin ()
to_host_path_cmd=
func_init_to_host_path_cmd ()
{
- $opt_debug
+ $debug_cmd
+
if test -z "$to_host_path_cmd"; then
func_stripname 'func_convert_file_' '' "$to_host_file_cmd"
- to_host_path_cmd="func_convert_path_${func_stripname_result}"
+ to_host_path_cmd=func_convert_path_$func_stripname_result
fi
}
@@ -1817,7 +3110,8 @@ func_init_to_host_path_cmd ()
# in func_to_host_path_result.
func_to_host_path ()
{
- $opt_debug
+ $debug_cmd
+
func_init_to_host_path_cmd
$to_host_path_cmd "$1"
}
@@ -1828,7 +3122,7 @@ func_to_host_path ()
# Copy ARG to func_to_host_path_result.
func_convert_path_noop ()
{
- func_to_host_path_result="$1"
+ func_to_host_path_result=$1
}
# end func_convert_path_noop
@@ -1839,8 +3133,9 @@ func_convert_path_noop ()
# func_to_host_path_result.
func_convert_path_msys_to_w32 ()
{
- $opt_debug
- func_to_host_path_result="$1"
+ $debug_cmd
+
+ func_to_host_path_result=$1
if test -n "$1"; then
# Remove leading and trailing path separator characters from ARG. MSYS
# behavior is inconsistent here; cygpath turns them into '.;' and ';.';
@@ -1848,7 +3143,7 @@ func_convert_path_msys_to_w32 ()
func_stripname : : "$1"
func_to_host_path_tmp1=$func_stripname_result
func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
- func_to_host_path_result="$func_convert_core_msys_to_w32_result"
+ func_to_host_path_result=$func_convert_core_msys_to_w32_result
func_convert_path_check : ";" \
"$func_to_host_path_tmp1" "$func_to_host_path_result"
func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
@@ -1862,8 +3157,9 @@ func_convert_path_msys_to_w32 ()
# func_to_host_file_result.
func_convert_path_cygwin_to_w32 ()
{
- $opt_debug
- func_to_host_path_result="$1"
+ $debug_cmd
+
+ func_to_host_path_result=$1
if test -n "$1"; then
# See func_convert_path_msys_to_w32:
func_stripname : : "$1"
@@ -1882,14 +3178,15 @@ func_convert_path_cygwin_to_w32 ()
# a working winepath. Returns result in func_to_host_file_result.
func_convert_path_nix_to_w32 ()
{
- $opt_debug
- func_to_host_path_result="$1"
+ $debug_cmd
+
+ func_to_host_path_result=$1
if test -n "$1"; then
# See func_convert_path_msys_to_w32:
func_stripname : : "$1"
func_to_host_path_tmp1=$func_stripname_result
func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
- func_to_host_path_result="$func_convert_core_path_wine_to_w32_result"
+ func_to_host_path_result=$func_convert_core_path_wine_to_w32_result
func_convert_path_check : ";" \
"$func_to_host_path_tmp1" "$func_to_host_path_result"
func_convert_path_front_back_pathsep ":*" "*:" ";" "$1"
@@ -1903,15 +3200,16 @@ func_convert_path_nix_to_w32 ()
# Returns result in func_to_host_file_result.
func_convert_path_msys_to_cygwin ()
{
- $opt_debug
- func_to_host_path_result="$1"
+ $debug_cmd
+
+ func_to_host_path_result=$1
if test -n "$1"; then
# See func_convert_path_msys_to_w32:
func_stripname : : "$1"
func_to_host_path_tmp1=$func_stripname_result
func_convert_core_msys_to_w32 "$func_to_host_path_tmp1"
func_cygpath -u -p "$func_convert_core_msys_to_w32_result"
- func_to_host_path_result="$func_cygpath_result"
+ func_to_host_path_result=$func_cygpath_result
func_convert_path_check : : \
"$func_to_host_path_tmp1" "$func_to_host_path_result"
func_convert_path_front_back_pathsep ":*" "*:" : "$1"
@@ -1926,8 +3224,9 @@ func_convert_path_msys_to_cygwin ()
# func_to_host_file_result.
func_convert_path_nix_to_cygwin ()
{
- $opt_debug
- func_to_host_path_result="$1"
+ $debug_cmd
+
+ func_to_host_path_result=$1
if test -n "$1"; then
# Remove leading and trailing path separator characters from
# ARG. msys behavior is inconsistent here, cygpath turns them
@@ -1936,7 +3235,7 @@ func_convert_path_nix_to_cygwin ()
func_to_host_path_tmp1=$func_stripname_result
func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1"
func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result"
- func_to_host_path_result="$func_cygpath_result"
+ func_to_host_path_result=$func_cygpath_result
func_convert_path_check : : \
"$func_to_host_path_tmp1" "$func_to_host_path_result"
func_convert_path_front_back_pathsep ":*" "*:" : "$1"
@@ -1945,13 +3244,31 @@ func_convert_path_nix_to_cygwin ()
# end func_convert_path_nix_to_cygwin
+# func_dll_def_p FILE
+# True iff FILE is a Windows DLL '.def' file.
+# Keep in sync with _LT_DLL_DEF_P in libtool.m4
+func_dll_def_p ()
+{
+ $debug_cmd
+
+ func_dll_def_p_tmp=`$SED -n \
+ -e 's/^[ ]*//' \
+ -e '/^\(;.*\)*$/d' \
+ -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \
+ -e q \
+ "$1"`
+ test DEF = "$func_dll_def_p_tmp"
+}
+
+
# func_mode_compile arg...
func_mode_compile ()
{
- $opt_debug
+ $debug_cmd
+
# Get the compilation command and the source file.
base_compile=
- srcfile="$nonopt" # always keep a non-empty value in "srcfile"
+ srcfile=$nonopt # always keep a non-empty value in "srcfile"
suppress_opt=yes
suppress_output=
arg_mode=normal
@@ -1964,12 +3281,12 @@ func_mode_compile ()
case $arg_mode in
arg )
# do not "continue". Instead, add this to base_compile
- lastarg="$arg"
+ lastarg=$arg
arg_mode=normal
;;
target )
- libobj="$arg"
+ libobj=$arg
arg_mode=normal
continue
;;
@@ -1979,7 +3296,7 @@ func_mode_compile ()
case $arg in
-o)
test -n "$libobj" && \
- func_fatal_error "you cannot specify \`-o' more than once"
+ func_fatal_error "you cannot specify '-o' more than once"
arg_mode=target
continue
;;
@@ -2008,12 +3325,12 @@ func_mode_compile ()
func_stripname '-Wc,' '' "$arg"
args=$func_stripname_result
lastarg=
- save_ifs="$IFS"; IFS=','
+ save_ifs=$IFS; IFS=,
for arg in $args; do
- IFS="$save_ifs"
+ IFS=$save_ifs
func_append_quoted lastarg "$arg"
done
- IFS="$save_ifs"
+ IFS=$save_ifs
func_stripname ' ' '' "$lastarg"
lastarg=$func_stripname_result
@@ -2026,8 +3343,8 @@ func_mode_compile ()
# Accept the current argument as the source file.
# The previous "srcfile" becomes the current argument.
#
- lastarg="$srcfile"
- srcfile="$arg"
+ lastarg=$srcfile
+ srcfile=$arg
;;
esac # case $arg
;;
@@ -2042,13 +3359,13 @@ func_mode_compile ()
func_fatal_error "you must specify an argument for -Xcompile"
;;
target)
- func_fatal_error "you must specify a target with \`-o'"
+ func_fatal_error "you must specify a target with '-o'"
;;
*)
# Get the name of the library object.
test -z "$libobj" && {
func_basename "$srcfile"
- libobj="$func_basename_result"
+ libobj=$func_basename_result
}
;;
esac
@@ -2059,7 +3376,7 @@ func_mode_compile ()
*.[cCFSifmso] | \
*.ada | *.adb | *.ads | *.asm | \
*.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \
- *.[fF][09]? | *.for | *.java | *.obj | *.sx | *.cu | *.cup)
+ *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup)
func_xform "$libobj"
libobj=$func_xform_result
;;
@@ -2068,7 +3385,7 @@ func_mode_compile ()
case $libobj in
*.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;;
*)
- func_fatal_error "cannot determine name of library object from \`$libobj'"
+ func_fatal_error "cannot determine name of library object from '$libobj'"
;;
esac
@@ -2077,8 +3394,8 @@ func_mode_compile ()
for arg in $later; do
case $arg in
-shared)
- test "$build_libtool_libs" != yes && \
- func_fatal_configuration "can not build a shared library"
+ test yes = "$build_libtool_libs" \
+ || func_fatal_configuration "cannot build a shared library"
build_old_libs=no
continue
;;
@@ -2104,17 +3421,17 @@ func_mode_compile ()
func_quote_for_eval "$libobj"
test "X$libobj" != "X$func_quote_for_eval_result" \
&& $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
- && func_warning "libobj name \`$libobj' may not contain shell special characters."
+ && func_warning "libobj name '$libobj' may not contain shell special characters."
func_dirname_and_basename "$obj" "/" ""
- objname="$func_basename_result"
- xdir="$func_dirname_result"
- lobj=${xdir}$objdir/$objname
+ objname=$func_basename_result
+ xdir=$func_dirname_result
+ lobj=$xdir$objdir/$objname
test -z "$base_compile" && \
func_fatal_help "you must specify a compilation command"
# Delete any leftover library objects.
- if test "$build_old_libs" = yes; then
+ if test yes = "$build_old_libs"; then
removelist="$obj $lobj $libobj ${libobj}T"
else
removelist="$lobj $libobj ${libobj}T"
@@ -2126,16 +3443,16 @@ func_mode_compile ()
pic_mode=default
;;
esac
- if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then
+ if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then
# non-PIC code in shared libraries is not supported
pic_mode=default
fi
# Calculate the filename of the output object if compiler does
# not support -o with -c
- if test "$compiler_c_o" = no; then
- output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext}
- lockfile="$output_obj.lock"
+ if test no = "$compiler_c_o"; then
+ output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext
+ lockfile=$output_obj.lock
else
output_obj=
need_locks=no
@@ -2144,12 +3461,12 @@ func_mode_compile ()
# Lock this critical section if it is needed
# We use this script file to make the link, it avoids creating a new file
- if test "$need_locks" = yes; then
+ if test yes = "$need_locks"; then
until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
func_echo "Waiting for $lockfile to be removed"
sleep 2
done
- elif test "$need_locks" = warn; then
+ elif test warn = "$need_locks"; then
if test -f "$lockfile"; then
$ECHO "\
*** ERROR, $lockfile exists and contains:
@@ -2157,7 +3474,7 @@ func_mode_compile ()
This indicates that another process is trying to use the same
temporary object file, and libtool could not work around it because
-your compiler does not support \`-c' and \`-o' together. If you
+your compiler does not support '-c' and '-o' together. If you
repeat this compilation, it may succeed, by chance, but you had better
avoid parallel builds (make -j) in this platform, or get a better
compiler."
@@ -2179,11 +3496,11 @@ compiler."
qsrcfile=$func_quote_for_eval_result
# Only build a PIC object if we are building libtool libraries.
- if test "$build_libtool_libs" = yes; then
+ if test yes = "$build_libtool_libs"; then
# Without this assignment, base_compile gets emptied.
fbsd_hideous_sh_bug=$base_compile
- if test "$pic_mode" != no; then
+ if test no != "$pic_mode"; then
command="$base_compile $qsrcfile $pic_flag"
else
# Don't build PIC code
@@ -2200,7 +3517,7 @@ compiler."
func_show_eval_locale "$command" \
'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE'
- if test "$need_locks" = warn &&
+ if test warn = "$need_locks" &&
test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
$ECHO "\
*** ERROR, $lockfile contains:
@@ -2211,7 +3528,7 @@ $srcfile
This indicates that another process is trying to use the same
temporary object file, and libtool could not work around it because
-your compiler does not support \`-c' and \`-o' together. If you
+your compiler does not support '-c' and '-o' together. If you
repeat this compilation, it may succeed, by chance, but you had better
avoid parallel builds (make -j) in this platform, or get a better
compiler."
@@ -2227,20 +3544,20 @@ compiler."
fi
# Allow error messages only from the first compilation.
- if test "$suppress_opt" = yes; then
+ if test yes = "$suppress_opt"; then
suppress_output=' >/dev/null 2>&1'
fi
fi
# Only build a position-dependent object if we build old libraries.
- if test "$build_old_libs" = yes; then
- if test "$pic_mode" != yes; then
+ if test yes = "$build_old_libs"; then
+ if test yes != "$pic_mode"; then
# Don't build PIC code
command="$base_compile $qsrcfile$pie_flag"
else
command="$base_compile $qsrcfile $pic_flag"
fi
- if test "$compiler_c_o" = yes; then
+ if test yes = "$compiler_c_o"; then
func_append command " -o $obj"
fi
@@ -2249,7 +3566,7 @@ compiler."
func_show_eval_locale "$command" \
'$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE'
- if test "$need_locks" = warn &&
+ if test warn = "$need_locks" &&
test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then
$ECHO "\
*** ERROR, $lockfile contains:
@@ -2260,7 +3577,7 @@ $srcfile
This indicates that another process is trying to use the same
temporary object file, and libtool could not work around it because
-your compiler does not support \`-c' and \`-o' together. If you
+your compiler does not support '-c' and '-o' together. If you
repeat this compilation, it may succeed, by chance, but you had better
avoid parallel builds (make -j) in this platform, or get a better
compiler."
@@ -2280,7 +3597,7 @@ compiler."
func_write_libtool_object "$libobj" "$objdir/$objname" "$objname"
# Unlock the critical section if it was locked
- if test "$need_locks" != no; then
+ if test no != "$need_locks"; then
removelist=$lockfile
$RM "$lockfile"
fi
@@ -2290,7 +3607,7 @@ compiler."
}
$opt_help || {
- test "$opt_mode" = compile && func_mode_compile ${1+"$@"}
+ test compile = "$opt_mode" && func_mode_compile ${1+"$@"}
}
func_mode_help ()
@@ -2310,7 +3627,7 @@ func_mode_help ()
Remove files from the build directory.
RM is the name of the program to use to delete files associated with each FILE
-(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
to RM.
If FILE is a libtool library, object or program, all the files associated
@@ -2329,16 +3646,16 @@ This mode accepts the following additional options:
-no-suppress do not suppress compiler output for multiple passes
-prefer-pic try to build PIC objects only
-prefer-non-pic try to build non-PIC objects only
- -shared do not build a \`.o' file suitable for static linking
- -static only build a \`.o' file suitable for static linking
+ -shared do not build a '.o' file suitable for static linking
+ -static only build a '.o' file suitable for static linking
-Wc,FLAG pass FLAG directly to the compiler
-COMPILE-COMMAND is a command to be used in creating a \`standard' object file
+COMPILE-COMMAND is a command to be used in creating a 'standard' object file
from the given SOURCEFILE.
The output file name is determined by removing the directory component from
-SOURCEFILE, then substituting the C source code suffix \`.c' with the
-library object suffix, \`.lo'."
+SOURCEFILE, then substituting the C source code suffix '.c' with the
+library object suffix, '.lo'."
;;
execute)
@@ -2351,7 +3668,7 @@ This mode accepts the following additional options:
-dlopen FILE add the directory containing FILE to the library path
-This mode sets the library path environment variable according to \`-dlopen'
+This mode sets the library path environment variable according to '-dlopen'
flags.
If any of the ARGS are libtool executable wrappers, then they are translated
@@ -2370,7 +3687,7 @@ Complete the installation of libtool libraries.
Each LIBDIR is a directory that contains libtool libraries.
The commands that this mode executes may require superuser privileges. Use
-the \`--dry-run' option if you just want to see what would be executed."
+the '--dry-run' option if you just want to see what would be executed."
;;
install)
@@ -2380,7 +3697,7 @@ the \`--dry-run' option if you just want to see what would be executed."
Install executables or libraries.
INSTALL-COMMAND is the installation command. The first component should be
-either the \`install' or \`cp' program.
+either the 'install' or 'cp' program.
The following components of INSTALL-COMMAND are treated specially:
@@ -2406,7 +3723,7 @@ The following components of LINK-COMMAND are treated specially:
-avoid-version do not add a version suffix if possible
-bindir BINDIR specify path to binaries directory (for systems where
libraries must be found in the PATH setting at runtime)
- -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime
+ -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime
-dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols
-export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3)
-export-symbols SYMFILE
@@ -2420,7 +3737,8 @@ The following components of LINK-COMMAND are treated specially:
-no-install link a not-installable executable
-no-undefined declare that a library does not refer to external symbols
-o OUTPUT-FILE create OUTPUT-FILE from the specified objects
- -objectlist FILE Use a list of object files found in FILE to specify objects
+ -objectlist FILE use a list of object files found in FILE to specify objects
+ -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes)
-precious-files-regex REGEX
don't remove output files matching REGEX
-release RELEASE specify package release information
@@ -2440,20 +3758,20 @@ The following components of LINK-COMMAND are treated specially:
-Xlinker FLAG pass linker-specific FLAG directly to the linker
-XCClinker FLAG pass link-specific FLAG to the compiler driver (CC)
-All other options (arguments beginning with \`-') are ignored.
+All other options (arguments beginning with '-') are ignored.
-Every other argument is treated as a filename. Files ending in \`.la' are
+Every other argument is treated as a filename. Files ending in '.la' are
treated as uninstalled libtool libraries, other files are standard or library
object files.
-If the OUTPUT-FILE ends in \`.la', then a libtool library is created,
-only library objects (\`.lo' files) may be specified, and \`-rpath' is
+If the OUTPUT-FILE ends in '.la', then a libtool library is created,
+only library objects ('.lo' files) may be specified, and '-rpath' is
required, except when creating a convenience library.
-If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created
-using \`ar' and \`ranlib', or on Windows using \`lib'.
+If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created
+using 'ar' and 'ranlib', or on Windows using 'lib'.
-If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file
+If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file
is created, otherwise an executable program is created."
;;
@@ -2464,7 +3782,7 @@ is created, otherwise an executable program is created."
Remove libraries from an installation directory.
RM is the name of the program to use to delete files associated with each FILE
-(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed
+(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed
to RM.
If FILE is a libtool library, all the files associated with it are deleted.
@@ -2472,17 +3790,17 @@ Otherwise, only FILE itself is deleted using RM."
;;
*)
- func_fatal_help "invalid operation mode \`$opt_mode'"
+ func_fatal_help "invalid operation mode '$opt_mode'"
;;
esac
echo
- $ECHO "Try \`$progname --help' for more information about other modes."
+ $ECHO "Try '$progname --help' for more information about other modes."
}
# Now that we've collected a possible --mode arg, show help if necessary
if $opt_help; then
- if test "$opt_help" = :; then
+ if test : = "$opt_help"; then
func_mode_help
else
{
@@ -2490,7 +3808,7 @@ if $opt_help; then
for opt_mode in compile link execute install finish uninstall clean; do
func_mode_help
done
- } | sed -n '1p; 2,$s/^Usage:/ or: /p'
+ } | $SED -n '1p; 2,$s/^Usage:/ or: /p'
{
func_help noexit
for opt_mode in compile link execute install finish uninstall clean; do
@@ -2498,7 +3816,7 @@ if $opt_help; then
func_mode_help
done
} |
- sed '1d
+ $SED '1d
/^When reporting/,/^Report/{
H
d
@@ -2515,16 +3833,17 @@ fi
# func_mode_execute arg...
func_mode_execute ()
{
- $opt_debug
+ $debug_cmd
+
# The first argument is the command name.
- cmd="$nonopt"
+ cmd=$nonopt
test -z "$cmd" && \
func_fatal_help "you must specify a COMMAND"
# Handle -dlopen flags immediately.
for file in $opt_dlopen; do
test -f "$file" \
- || func_fatal_help "\`$file' is not a file"
+ || func_fatal_help "'$file' is not a file"
dir=
case $file in
@@ -2534,7 +3853,7 @@ func_mode_execute ()
# Check to see that this really is a libtool archive.
func_lalib_unsafe_p "$file" \
- || func_fatal_help "\`$lib' is not a valid libtool archive"
+ || func_fatal_help "'$lib' is not a valid libtool archive"
# Read the libtool library.
dlname=
@@ -2545,18 +3864,18 @@ func_mode_execute ()
if test -z "$dlname"; then
# Warn if it was a shared library.
test -n "$library_names" && \
- func_warning "\`$file' was not linked with \`-export-dynamic'"
+ func_warning "'$file' was not linked with '-export-dynamic'"
continue
fi
func_dirname "$file" "" "."
- dir="$func_dirname_result"
+ dir=$func_dirname_result
if test -f "$dir/$objdir/$dlname"; then
func_append dir "/$objdir"
else
if test ! -f "$dir/$dlname"; then
- func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'"
+ func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'"
fi
fi
;;
@@ -2564,18 +3883,18 @@ func_mode_execute ()
*.lo)
# Just add the directory containing the .lo file.
func_dirname "$file" "" "."
- dir="$func_dirname_result"
+ dir=$func_dirname_result
;;
*)
- func_warning "\`-dlopen' is ignored for non-libtool libraries and objects"
+ func_warning "'-dlopen' is ignored for non-libtool libraries and objects"
continue
;;
esac
# Get the absolute pathname.
absdir=`cd "$dir" && pwd`
- test -n "$absdir" && dir="$absdir"
+ test -n "$absdir" && dir=$absdir
# Now add the directory to shlibpath_var.
if eval "test -z \"\$$shlibpath_var\""; then
@@ -2587,7 +3906,7 @@ func_mode_execute ()
# This variable tells wrapper scripts just to set shlibpath_var
# rather than running their programs.
- libtool_execute_magic="$magic"
+ libtool_execute_magic=$magic
# Check if any of the arguments is a wrapper script.
args=
@@ -2600,12 +3919,12 @@ func_mode_execute ()
if func_ltwrapper_script_p "$file"; then
func_source "$file"
# Transform arg to wrapped name.
- file="$progdir/$program"
+ file=$progdir/$program
elif func_ltwrapper_executable_p "$file"; then
func_ltwrapper_scriptname "$file"
func_source "$func_ltwrapper_scriptname_result"
# Transform arg to wrapped name.
- file="$progdir/$program"
+ file=$progdir/$program
fi
;;
esac
@@ -2613,7 +3932,15 @@ func_mode_execute ()
func_append_quoted args "$file"
done
- if test "X$opt_dry_run" = Xfalse; then
+ if $opt_dry_run; then
+ # Display what would be done.
+ if test -n "$shlibpath_var"; then
+ eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
+ echo "export $shlibpath_var"
+ fi
+ $ECHO "$cmd$args"
+ exit $EXIT_SUCCESS
+ else
if test -n "$shlibpath_var"; then
# Export the shlibpath_var.
eval "export $shlibpath_var"
@@ -2630,25 +3957,18 @@ func_mode_execute ()
done
# Now prepare to actually exec the command.
- exec_cmd="\$cmd$args"
- else
- # Display what would be done.
- if test -n "$shlibpath_var"; then
- eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\""
- echo "export $shlibpath_var"
- fi
- $ECHO "$cmd$args"
- exit $EXIT_SUCCESS
+ exec_cmd=\$cmd$args
fi
}
-test "$opt_mode" = execute && func_mode_execute ${1+"$@"}
+test execute = "$opt_mode" && func_mode_execute ${1+"$@"}
# func_mode_finish arg...
func_mode_finish ()
{
- $opt_debug
+ $debug_cmd
+
libs=
libdirs=
admincmds=
@@ -2662,11 +3982,11 @@ func_mode_finish ()
if func_lalib_unsafe_p "$opt"; then
func_append libs " $opt"
else
- func_warning "\`$opt' is not a valid libtool archive"
+ func_warning "'$opt' is not a valid libtool archive"
fi
else
- func_fatal_error "invalid argument \`$opt'"
+ func_fatal_error "invalid argument '$opt'"
fi
done
@@ -2681,12 +4001,12 @@ func_mode_finish ()
# Remove sysroot references
if $opt_dry_run; then
for lib in $libs; do
- echo "removing references to $lt_sysroot and \`=' prefixes from $lib"
+ echo "removing references to $lt_sysroot and '=' prefixes from $lib"
done
else
tmpdir=`func_mktempdir`
for lib in $libs; do
- sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
+ $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \
> $tmpdir/tmp-la
mv -f $tmpdir/tmp-la $lib
done
@@ -2711,7 +4031,7 @@ func_mode_finish ()
fi
# Exit here if they wanted silent mode.
- $opt_silent && exit $EXIT_SUCCESS
+ $opt_quiet && exit $EXIT_SUCCESS
if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
echo "----------------------------------------------------------------------"
@@ -2722,27 +4042,27 @@ func_mode_finish ()
echo
echo "If you ever happen to want to link against installed libraries"
echo "in a given directory, LIBDIR, you must either use libtool, and"
- echo "specify the full pathname of the library, or use the \`-LLIBDIR'"
+ echo "specify the full pathname of the library, or use the '-LLIBDIR'"
echo "flag during linking and do at least one of the following:"
if test -n "$shlibpath_var"; then
- echo " - add LIBDIR to the \`$shlibpath_var' environment variable"
+ echo " - add LIBDIR to the '$shlibpath_var' environment variable"
echo " during execution"
fi
if test -n "$runpath_var"; then
- echo " - add LIBDIR to the \`$runpath_var' environment variable"
+ echo " - add LIBDIR to the '$runpath_var' environment variable"
echo " during linking"
fi
if test -n "$hardcode_libdir_flag_spec"; then
libdir=LIBDIR
eval flag=\"$hardcode_libdir_flag_spec\"
- $ECHO " - use the \`$flag' linker flag"
+ $ECHO " - use the '$flag' linker flag"
fi
if test -n "$admincmds"; then
$ECHO " - have your system administrator run these commands:$admincmds"
fi
if test -f /etc/ld.so.conf; then
- echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'"
+ echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'"
fi
echo
@@ -2761,18 +4081,20 @@ func_mode_finish ()
exit $EXIT_SUCCESS
}
-test "$opt_mode" = finish && func_mode_finish ${1+"$@"}
+test finish = "$opt_mode" && func_mode_finish ${1+"$@"}
# func_mode_install arg...
func_mode_install ()
{
- $opt_debug
+ $debug_cmd
+
# There may be an optional sh(1) argument at the beginning of
# install_prog (especially on Windows NT).
- if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh ||
+ if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" ||
# Allow the use of GNU shtool's install command.
- case $nonopt in *shtool*) :;; *) false;; esac; then
+ case $nonopt in *shtool*) :;; *) false;; esac
+ then
# Aesthetically quote it.
func_quote_for_eval "$nonopt"
install_prog="$func_quote_for_eval_result "
@@ -2799,7 +4121,7 @@ func_mode_install ()
opts=
prev=
install_type=
- isdir=no
+ isdir=false
stripme=
no_mode=:
for arg
@@ -2812,7 +4134,7 @@ func_mode_install ()
fi
case $arg in
- -d) isdir=yes ;;
+ -d) isdir=: ;;
-f)
if $install_cp; then :; else
prev=$arg
@@ -2830,7 +4152,7 @@ func_mode_install ()
*)
# If the previous option needed an argument, then skip it.
if test -n "$prev"; then
- if test "x$prev" = x-m && test -n "$install_override_mode"; then
+ if test X-m = "X$prev" && test -n "$install_override_mode"; then
arg2=$install_override_mode
no_mode=false
fi
@@ -2855,7 +4177,7 @@ func_mode_install ()
func_fatal_help "you must specify an install program"
test -n "$prev" && \
- func_fatal_help "the \`$prev' option requires an argument"
+ func_fatal_help "the '$prev' option requires an argument"
if test -n "$install_override_mode" && $no_mode; then
if $install_cp; then :; else
@@ -2877,19 +4199,19 @@ func_mode_install ()
dest=$func_stripname_result
# Check to see that the destination is a directory.
- test -d "$dest" && isdir=yes
- if test "$isdir" = yes; then
- destdir="$dest"
+ test -d "$dest" && isdir=:
+ if $isdir; then
+ destdir=$dest
destname=
else
func_dirname_and_basename "$dest" "" "."
- destdir="$func_dirname_result"
- destname="$func_basename_result"
+ destdir=$func_dirname_result
+ destname=$func_basename_result
# Not a directory, so check to see that there is only one file specified.
set dummy $files; shift
test "$#" -gt 1 && \
- func_fatal_help "\`$dest' is not a directory"
+ func_fatal_help "'$dest' is not a directory"
fi
case $destdir in
[\\/]* | [A-Za-z]:[\\/]*) ;;
@@ -2898,7 +4220,7 @@ func_mode_install ()
case $file in
*.lo) ;;
*)
- func_fatal_help "\`$destdir' must be an absolute directory name"
+ func_fatal_help "'$destdir' must be an absolute directory name"
;;
esac
done
@@ -2907,7 +4229,7 @@ func_mode_install ()
# This variable tells wrapper scripts just to set variables rather
# than running their programs.
- libtool_install_magic="$magic"
+ libtool_install_magic=$magic
staticlibs=
future_libdirs=
@@ -2927,7 +4249,7 @@ func_mode_install ()
# Check to see that this really is a libtool archive.
func_lalib_unsafe_p "$file" \
- || func_fatal_help "\`$file' is not a valid libtool archive"
+ || func_fatal_help "'$file' is not a valid libtool archive"
library_names=
old_library=
@@ -2949,7 +4271,7 @@ func_mode_install ()
fi
func_dirname "$file" "/" ""
- dir="$func_dirname_result"
+ dir=$func_dirname_result
func_append dir "$objdir"
if test -n "$relink_command"; then
@@ -2963,7 +4285,7 @@ func_mode_install ()
# are installed into $libdir/../bin (currently, that works fine)
# but it's something to keep an eye on.
test "$inst_prefix_dir" = "$destdir" && \
- func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir"
+ func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir"
if test -n "$inst_prefix_dir"; then
# Stick the inst_prefix_dir data into the link command.
@@ -2972,29 +4294,36 @@ func_mode_install ()
relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"`
fi
- func_warning "relinking \`$file'"
+ func_warning "relinking '$file'"
func_show_eval "$relink_command" \
- 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"'
+ 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"'
fi
# See the names of the shared library.
set dummy $library_names; shift
if test -n "$1"; then
- realname="$1"
+ realname=$1
shift
- srcname="$realname"
- test -n "$relink_command" && srcname="$realname"T
+ srcname=$realname
+ test -n "$relink_command" && srcname=${realname}T
# Install the shared library and build the symlinks.
func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \
'exit $?'
- tstripme="$stripme"
+ tstripme=$stripme
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
case $realname in
*.dll.a)
- tstripme=""
+ tstripme=
+ ;;
+ esac
+ ;;
+ os2*)
+ case $realname in
+ *_dll.a)
+ tstripme=
;;
esac
;;
@@ -3005,7 +4334,7 @@ func_mode_install ()
if test "$#" -gt 0; then
# Delete the old symlinks, and create new ones.
- # Try `ln -sf' first, because the `ln' binary might depend on
+ # Try 'ln -sf' first, because the 'ln' binary might depend on
# the symlink we replace! Solaris /bin/ln does not understand -f,
# so we also need to try rm && ln -s.
for linkname
@@ -3016,14 +4345,14 @@ func_mode_install ()
fi
# Do each command in the postinstall commands.
- lib="$destdir/$realname"
+ lib=$destdir/$realname
func_execute_cmds "$postinstall_cmds" 'exit $?'
fi
# Install the pseudo-library for information purposes.
func_basename "$file"
- name="$func_basename_result"
- instname="$dir/$name"i
+ name=$func_basename_result
+ instname=$dir/${name}i
func_show_eval "$install_prog $instname $destdir/$name" 'exit $?'
# Maybe install the static library, too.
@@ -3035,11 +4364,11 @@ func_mode_install ()
# Figure out destination file name, if it wasn't already specified.
if test -n "$destname"; then
- destfile="$destdir/$destname"
+ destfile=$destdir/$destname
else
func_basename "$file"
- destfile="$func_basename_result"
- destfile="$destdir/$destfile"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
fi
# Deduce the name of the destination old-style object file.
@@ -3049,11 +4378,11 @@ func_mode_install ()
staticdest=$func_lo2o_result
;;
*.$objext)
- staticdest="$destfile"
+ staticdest=$destfile
destfile=
;;
*)
- func_fatal_help "cannot copy a libtool object to \`$destfile'"
+ func_fatal_help "cannot copy a libtool object to '$destfile'"
;;
esac
@@ -3062,7 +4391,7 @@ func_mode_install ()
func_show_eval "$install_prog $file $destfile" 'exit $?'
# Install the old object if enabled.
- if test "$build_old_libs" = yes; then
+ if test yes = "$build_old_libs"; then
# Deduce the name of the old-style object file.
func_lo2o "$file"
staticobj=$func_lo2o_result
@@ -3074,23 +4403,23 @@ func_mode_install ()
*)
# Figure out destination file name, if it wasn't already specified.
if test -n "$destname"; then
- destfile="$destdir/$destname"
+ destfile=$destdir/$destname
else
func_basename "$file"
- destfile="$func_basename_result"
- destfile="$destdir/$destfile"
+ destfile=$func_basename_result
+ destfile=$destdir/$destfile
fi
# If the file is missing, and there is a .exe on the end, strip it
# because it is most likely a libtool script we actually want to
# install
- stripped_ext=""
+ stripped_ext=
case $file in
*.exe)
if test ! -f "$file"; then
func_stripname '' '.exe' "$file"
file=$func_stripname_result
- stripped_ext=".exe"
+ stripped_ext=.exe
fi
;;
esac
@@ -3118,19 +4447,19 @@ func_mode_install ()
# Check the variables that should have been set.
test -z "$generated_by_libtool_version" && \
- func_fatal_error "invalid libtool wrapper script \`$wrapper'"
+ func_fatal_error "invalid libtool wrapper script '$wrapper'"
- finalize=yes
+ finalize=:
for lib in $notinst_deplibs; do
# Check to see that each library is installed.
libdir=
if test -f "$lib"; then
func_source "$lib"
fi
- libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test
+ libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'`
if test -n "$libdir" && test ! -f "$libfile"; then
- func_warning "\`$lib' has not been installed in \`$libdir'"
- finalize=no
+ func_warning "'$lib' has not been installed in '$libdir'"
+ finalize=false
fi
done
@@ -3138,29 +4467,29 @@ func_mode_install ()
func_source "$wrapper"
outputname=
- if test "$fast_install" = no && test -n "$relink_command"; then
+ if test no = "$fast_install" && test -n "$relink_command"; then
$opt_dry_run || {
- if test "$finalize" = yes; then
+ if $finalize; then
tmpdir=`func_mktempdir`
func_basename "$file$stripped_ext"
- file="$func_basename_result"
- outputname="$tmpdir/$file"
+ file=$func_basename_result
+ outputname=$tmpdir/$file
# Replace the output file specification.
relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
- $opt_silent || {
+ $opt_quiet || {
func_quote_for_expand "$relink_command"
eval "func_echo $func_quote_for_expand_result"
}
if eval "$relink_command"; then :
else
- func_error "error: relink \`$file' with the above command before installing it"
+ func_error "error: relink '$file' with the above command before installing it"
$opt_dry_run || ${RM}r "$tmpdir"
continue
fi
- file="$outputname"
+ file=$outputname
else
- func_warning "cannot relink \`$file'"
+ func_warning "cannot relink '$file'"
fi
}
else
@@ -3197,15 +4526,17 @@ func_mode_install ()
for file in $staticlibs; do
func_basename "$file"
- name="$func_basename_result"
+ name=$func_basename_result
# Set up the ranlib parameters.
- oldlib="$destdir/$name"
+ oldlib=$destdir/$name
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
func_show_eval "$install_prog \$file \$oldlib" 'exit $?'
if test -n "$stripme" && test -n "$old_striplib"; then
- func_show_eval "$old_striplib $oldlib" 'exit $?'
+ func_show_eval "$old_striplib $tool_oldlib" 'exit $?'
fi
# Do each command in the postinstall commands.
@@ -3213,18 +4544,18 @@ func_mode_install ()
done
test -n "$future_libdirs" && \
- func_warning "remember to run \`$progname --finish$future_libdirs'"
+ func_warning "remember to run '$progname --finish$future_libdirs'"
if test -n "$current_libdirs"; then
# Maybe just do a dry run.
$opt_dry_run && current_libdirs=" -n$current_libdirs"
- exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs'
+ exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs'
else
exit $EXIT_SUCCESS
fi
}
-test "$opt_mode" = install && func_mode_install ${1+"$@"}
+test install = "$opt_mode" && func_mode_install ${1+"$@"}
# func_generate_dlsyms outputname originator pic_p
@@ -3232,16 +4563,17 @@ test "$opt_mode" = install && func_mode_install ${1+"$@"}
# a dlpreopen symbol table.
func_generate_dlsyms ()
{
- $opt_debug
- my_outputname="$1"
- my_originator="$2"
- my_pic_p="${3-no}"
- my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'`
+ $debug_cmd
+
+ my_outputname=$1
+ my_originator=$2
+ my_pic_p=${3-false}
+ my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'`
my_dlsyms=
- if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
if test -n "$NM" && test -n "$global_symbol_pipe"; then
- my_dlsyms="${my_outputname}S.c"
+ my_dlsyms=${my_outputname}S.c
else
func_error "not configured to extract global symbols from dlpreopened files"
fi
@@ -3252,7 +4584,7 @@ func_generate_dlsyms ()
"") ;;
*.c)
# Discover the nlist of each of the dlfiles.
- nlist="$output_objdir/${my_outputname}.nm"
+ nlist=$output_objdir/$my_outputname.nm
func_show_eval "$RM $nlist ${nlist}S ${nlist}T"
@@ -3260,34 +4592,36 @@ func_generate_dlsyms ()
func_verbose "creating $output_objdir/$my_dlsyms"
$opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\
-/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */
-/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */
+/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */
+/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */
#ifdef __cplusplus
extern \"C\" {
#endif
-#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
+#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4))
#pragma GCC diagnostic ignored \"-Wstrict-prototypes\"
#endif
/* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */
-#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE)
-/* DATA imports from DLLs on WIN32 con't be const, because runtime
+#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE
+/* DATA imports from DLLs on WIN32 can't be const, because runtime
relocations are performed -- see ld's documentation on pseudo-relocs. */
# define LT_DLSYM_CONST
-#elif defined(__osf__)
+#elif defined __osf__
/* This system does not cope well with relocations in const data. */
# define LT_DLSYM_CONST
#else
# define LT_DLSYM_CONST const
#endif
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
/* External symbol declarations for the compiler. */\
"
- if test "$dlself" = yes; then
- func_verbose "generating symbol list for \`$output'"
+ if test yes = "$dlself"; then
+ func_verbose "generating symbol list for '$output'"
$opt_dry_run || echo ': @PROGRAM@ ' > "$nlist"
@@ -3295,7 +4629,7 @@ extern \"C\" {
progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP`
for progfile in $progfiles; do
func_to_tool_file "$progfile" func_convert_file_msys_to_w32
- func_verbose "extracting global C symbols from \`$func_to_tool_file_result'"
+ func_verbose "extracting global C symbols from '$func_to_tool_file_result'"
$opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'"
done
@@ -3315,10 +4649,10 @@ extern \"C\" {
# Prepare the list of exported symbols
if test -z "$export_symbols"; then
- export_symbols="$output_objdir/$outputname.exp"
+ export_symbols=$output_objdir/$outputname.exp
$opt_dry_run || {
$RM $export_symbols
- eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
+ eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
case $host in
*cygwin* | *mingw* | *cegcc* )
eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
@@ -3328,7 +4662,7 @@ extern \"C\" {
}
else
$opt_dry_run || {
- eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
+ eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"'
eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
eval '$MV "$nlist"T "$nlist"'
case $host in
@@ -3342,22 +4676,22 @@ extern \"C\" {
fi
for dlprefile in $dlprefiles; do
- func_verbose "extracting global C symbols from \`$dlprefile'"
+ func_verbose "extracting global C symbols from '$dlprefile'"
func_basename "$dlprefile"
- name="$func_basename_result"
+ name=$func_basename_result
case $host in
*cygwin* | *mingw* | *cegcc* )
# if an import library, we need to obtain dlname
if func_win32_import_lib_p "$dlprefile"; then
func_tr_sh "$dlprefile"
eval "curr_lafile=\$libfile_$func_tr_sh_result"
- dlprefile_dlbasename=""
+ dlprefile_dlbasename=
if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then
# Use subshell, to avoid clobbering current variable values
dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"`
- if test -n "$dlprefile_dlname" ; then
+ if test -n "$dlprefile_dlname"; then
func_basename "$dlprefile_dlname"
- dlprefile_dlbasename="$func_basename_result"
+ dlprefile_dlbasename=$func_basename_result
else
# no lafile. user explicitly requested -dlpreopen <import library>.
$sharedlib_from_linklib_cmd "$dlprefile"
@@ -3365,7 +4699,7 @@ extern \"C\" {
fi
fi
$opt_dry_run || {
- if test -n "$dlprefile_dlbasename" ; then
+ if test -n "$dlprefile_dlbasename"; then
eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"'
else
func_warning "Could not compute DLL name from $name"
@@ -3421,6 +4755,11 @@ extern \"C\" {
echo '/* NONE */' >> "$output_objdir/$my_dlsyms"
fi
+ func_show_eval '$RM "${nlist}I"'
+ if test -n "$global_symbol_to_import"; then
+ eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I'
+ fi
+
echo >> "$output_objdir/$my_dlsyms" "\
/* The mapping between symbol names and symbols. */
@@ -3429,11 +4768,30 @@ typedef struct {
void *address;
} lt_dlsymlist;
extern LT_DLSYM_CONST lt_dlsymlist
-lt_${my_prefix}_LTX_preloaded_symbols[];
+lt_${my_prefix}_LTX_preloaded_symbols[];\
+"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+static void lt_syminit(void)
+{
+ LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols;
+ for (; symbol->name; ++symbol)
+ {"
+ $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms"
+ echo >> "$output_objdir/$my_dlsyms" "\
+ }
+}"
+ fi
+ echo >> "$output_objdir/$my_dlsyms" "\
LT_DLSYM_CONST lt_dlsymlist
lt_${my_prefix}_LTX_preloaded_symbols[] =
-{\
- { \"$my_originator\", (void *) 0 },"
+{ {\"$my_originator\", (void *) 0},"
+
+ if test -s "$nlist"I; then
+ echo >> "$output_objdir/$my_dlsyms" "\
+ {\"@INIT@\", (void *) &lt_syminit},"
+ fi
case $need_lib_prefix in
no)
@@ -3470,14 +4828,12 @@ static const void *lt_preloaded_setup() {
# linked before any other PIC object. But we must not use
# pic_flag when linking with -static. The problem exists in
# FreeBSD 2.2.6 and is fixed in FreeBSD 3.1.
- *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
+ *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*)
pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;;
*-*-hpux*)
pic_flag_for_symtable=" $pic_flag" ;;
*)
- if test "X$my_pic_p" != Xno; then
- pic_flag_for_symtable=" $pic_flag"
- fi
+ $my_pic_p && pic_flag_for_symtable=" $pic_flag"
;;
esac
;;
@@ -3494,10 +4850,10 @@ static const void *lt_preloaded_setup() {
func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?'
# Clean up the generated files.
- func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"'
+ func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"'
# Transform the symbol file into the correct name.
- symfileobj="$output_objdir/${my_outputname}S.$objext"
+ symfileobj=$output_objdir/${my_outputname}S.$objext
case $host in
*cygwin* | *mingw* | *cegcc* )
if test -f "$output_objdir/$my_outputname.def"; then
@@ -3515,7 +4871,7 @@ static const void *lt_preloaded_setup() {
esac
;;
*)
- func_fatal_error "unknown suffix for \`$my_dlsyms'"
+ func_fatal_error "unknown suffix for '$my_dlsyms'"
;;
esac
else
@@ -3529,6 +4885,32 @@ static const void *lt_preloaded_setup() {
fi
}
+# func_cygming_gnu_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is a GNU/binutils-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_gnu_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
+ test -n "$func_cygming_gnu_implib_tmp"
+}
+
+# func_cygming_ms_implib_p ARG
+# This predicate returns with zero status (TRUE) if
+# ARG is an MS-style import library. Returns
+# with nonzero status (FALSE) otherwise.
+func_cygming_ms_implib_p ()
+{
+ $debug_cmd
+
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
+ test -n "$func_cygming_ms_implib_tmp"
+}
+
# func_win32_libid arg
# return the library type of file 'arg'
#
@@ -3538,8 +4920,9 @@ static const void *lt_preloaded_setup() {
# Despite the name, also deal with 64 bit binaries.
func_win32_libid ()
{
- $opt_debug
- win32_libid_type="unknown"
+ $debug_cmd
+
+ win32_libid_type=unknown
win32_fileres=`file -L $1 2>/dev/null`
case $win32_fileres in
*ar\ archive\ import\ library*) # definitely import
@@ -3549,16 +4932,29 @@ func_win32_libid ()
# Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
$EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
- func_to_tool_file "$1" func_convert_file_msys_to_w32
- win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
- $SED -n -e '
+ case $nm_interface in
+ "MS dumpbin")
+ if func_cygming_ms_implib_p "$1" ||
+ func_cygming_gnu_implib_p "$1"
+ then
+ win32_nmres=import
+ else
+ win32_nmres=
+ fi
+ ;;
+ *)
+ func_to_tool_file "$1" func_convert_file_msys_to_w32
+ win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" |
+ $SED -n -e '
1,100{
/ I /{
- s,.*,import,
+ s|.*|import|
p
q
}
}'`
+ ;;
+ esac
case $win32_nmres in
import*) win32_libid_type="x86 archive import";;
*) win32_libid_type="x86 archive static";;
@@ -3590,7 +4986,8 @@ func_win32_libid ()
# $sharedlib_from_linklib_result
func_cygming_dll_for_implib ()
{
- $opt_debug
+ $debug_cmd
+
sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"`
}
@@ -3607,7 +5004,8 @@ func_cygming_dll_for_implib ()
# specified import library.
func_cygming_dll_for_implib_fallback_core ()
{
- $opt_debug
+ $debug_cmd
+
match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"`
$OBJDUMP -s --section "$1" "$2" 2>/dev/null |
$SED '/^Contents of section '"$match_literal"':/{
@@ -3643,8 +5041,8 @@ func_cygming_dll_for_implib_fallback_core ()
/./p' |
# we now have a list, one entry per line, of the stringified
# contents of the appropriate section of all members of the
- # archive which possess that section. Heuristic: eliminate
- # all those which have a first or second character that is
+ # archive that possess that section. Heuristic: eliminate
+ # all those that have a first or second character that is
# a '.' (that is, objdump's representation of an unprintable
# character.) This should work for all archives with less than
# 0x302f exports -- but will fail for DLLs whose name actually
@@ -3655,30 +5053,6 @@ func_cygming_dll_for_implib_fallback_core ()
$SED -e '/^\./d;/^.\./d;q'
}
-# func_cygming_gnu_implib_p ARG
-# This predicate returns with zero status (TRUE) if
-# ARG is a GNU/binutils-style import library. Returns
-# with nonzero status (FALSE) otherwise.
-func_cygming_gnu_implib_p ()
-{
- $opt_debug
- func_to_tool_file "$1" func_convert_file_msys_to_w32
- func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'`
- test -n "$func_cygming_gnu_implib_tmp"
-}
-
-# func_cygming_ms_implib_p ARG
-# This predicate returns with zero status (TRUE) if
-# ARG is an MS-style import library. Returns
-# with nonzero status (FALSE) otherwise.
-func_cygming_ms_implib_p ()
-{
- $opt_debug
- func_to_tool_file "$1" func_convert_file_msys_to_w32
- func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'`
- test -n "$func_cygming_ms_implib_tmp"
-}
-
# func_cygming_dll_for_implib_fallback ARG
# Platform-specific function to extract the
# name of the DLL associated with the specified
@@ -3692,16 +5066,17 @@ func_cygming_ms_implib_p ()
# $sharedlib_from_linklib_result
func_cygming_dll_for_implib_fallback ()
{
- $opt_debug
- if func_cygming_gnu_implib_p "$1" ; then
+ $debug_cmd
+
+ if func_cygming_gnu_implib_p "$1"; then
# binutils import library
sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"`
- elif func_cygming_ms_implib_p "$1" ; then
+ elif func_cygming_ms_implib_p "$1"; then
# ms-generated import library
sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"`
else
# unknown
- sharedlib_from_linklib_result=""
+ sharedlib_from_linklib_result=
fi
}
@@ -3709,10 +5084,11 @@ func_cygming_dll_for_implib_fallback ()
# func_extract_an_archive dir oldlib
func_extract_an_archive ()
{
- $opt_debug
- f_ex_an_ar_dir="$1"; shift
- f_ex_an_ar_oldlib="$1"
- if test "$lock_old_archive_extraction" = yes; then
+ $debug_cmd
+
+ f_ex_an_ar_dir=$1; shift
+ f_ex_an_ar_oldlib=$1
+ if test yes = "$lock_old_archive_extraction"; then
lockfile=$f_ex_an_ar_oldlib.lock
until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do
func_echo "Waiting for $lockfile to be removed"
@@ -3721,7 +5097,7 @@ func_extract_an_archive ()
fi
func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \
'stat=$?; rm -f "$lockfile"; exit $stat'
- if test "$lock_old_archive_extraction" = yes; then
+ if test yes = "$lock_old_archive_extraction"; then
$opt_dry_run || rm -f "$lockfile"
fi
if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then
@@ -3735,22 +5111,23 @@ func_extract_an_archive ()
# func_extract_archives gentop oldlib ...
func_extract_archives ()
{
- $opt_debug
- my_gentop="$1"; shift
+ $debug_cmd
+
+ my_gentop=$1; shift
my_oldlibs=${1+"$@"}
- my_oldobjs=""
- my_xlib=""
- my_xabs=""
- my_xdir=""
+ my_oldobjs=
+ my_xlib=
+ my_xabs=
+ my_xdir=
for my_xlib in $my_oldlibs; do
# Extract the objects.
case $my_xlib in
- [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;;
+ [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;;
*) my_xabs=`pwd`"/$my_xlib" ;;
esac
func_basename "$my_xlib"
- my_xlib="$func_basename_result"
+ my_xlib=$func_basename_result
my_xlib_u=$my_xlib
while :; do
case " $extracted_archives " in
@@ -3762,7 +5139,7 @@ func_extract_archives ()
esac
done
extracted_archives="$extracted_archives $my_xlib_u"
- my_xdir="$my_gentop/$my_xlib_u"
+ my_xdir=$my_gentop/$my_xlib_u
func_mkdir_p "$my_xdir"
@@ -3775,22 +5152,23 @@ func_extract_archives ()
cd $my_xdir || exit $?
darwin_archive=$my_xabs
darwin_curdir=`pwd`
- darwin_base_archive=`basename "$darwin_archive"`
+ func_basename "$darwin_archive"
+ darwin_base_archive=$func_basename_result
darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true`
if test -n "$darwin_arches"; then
darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'`
darwin_arch=
func_verbose "$darwin_base_archive has multiple architectures $darwin_arches"
- for darwin_arch in $darwin_arches ; do
- func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}"
- $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}"
- cd "unfat-$$/${darwin_base_archive}-${darwin_arch}"
- func_extract_an_archive "`pwd`" "${darwin_base_archive}"
+ for darwin_arch in $darwin_arches; do
+ func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch"
+ $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive"
+ cd "unfat-$$/$darwin_base_archive-$darwin_arch"
+ func_extract_an_archive "`pwd`" "$darwin_base_archive"
cd "$darwin_curdir"
- $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}"
+ $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive"
done # $darwin_arches
## Okay now we've a bunch of thin objects, gotta fatten them up :)
- darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u`
+ darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u`
darwin_file=
darwin_files=
for darwin_file in $darwin_filelist; do
@@ -3812,7 +5190,7 @@ func_extract_archives ()
my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP`
done
- func_extract_archives_result="$my_oldobjs"
+ func_extract_archives_result=$my_oldobjs
}
@@ -3827,7 +5205,7 @@ func_extract_archives ()
#
# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
# variable will take. If 'yes', then the emitted script
-# will assume that the directory in which it is stored is
+# will assume that the directory where it is stored is
# the $objdir directory. This is a cygwin/mingw-specific
# behavior.
func_emit_wrapper ()
@@ -3838,7 +5216,7 @@ func_emit_wrapper ()
#! $SHELL
# $output - temporary wrapper script for $objdir/$outputname
-# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
#
# The $output program cannot be directly executed until all the libtool
# libraries that it depends on are installed.
@@ -3895,9 +5273,9 @@ _LTECHO_EOF'
# Very basic option parsing. These options are (a) specific to
# the libtool wrapper, (b) are identical between the wrapper
-# /script/ and the wrapper /executable/ which is used only on
+# /script/ and the wrapper /executable/ that is used only on
# windows platforms, and (c) all begin with the string "--lt-"
-# (application programs are unlikely to have options which match
+# (application programs are unlikely to have options that match
# this pattern).
#
# There are only two supported options: --lt-debug and
@@ -3930,7 +5308,7 @@ func_parse_lt_options ()
# Print the debug banner immediately:
if test -n \"\$lt_option_debug\"; then
- echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2
+ echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2
fi
}
@@ -3941,7 +5319,7 @@ func_lt_dump_args ()
lt_dump_args_N=1;
for lt_arg
do
- \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\"
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\"
lt_dump_args_N=\`expr \$lt_dump_args_N + 1\`
done
}
@@ -3955,7 +5333,7 @@ func_exec_program_core ()
*-*-mingw | *-*-os2* | *-cegcc*)
$ECHO "\
if test -n \"\$lt_option_debug\"; then
- \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2
func_lt_dump_args \${1+\"\$@\"} 1>&2
fi
exec \"\$progdir\\\\\$program\" \${1+\"\$@\"}
@@ -3965,7 +5343,7 @@ func_exec_program_core ()
*)
$ECHO "\
if test -n \"\$lt_option_debug\"; then
- \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2
+ \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2
func_lt_dump_args \${1+\"\$@\"} 1>&2
fi
exec \"\$progdir/\$program\" \${1+\"\$@\"}
@@ -3982,14 +5360,17 @@ func_exec_program_core ()
# launches target application with the remaining arguments.
func_exec_program ()
{
- for lt_wr_arg
- do
- case \$lt_wr_arg in
- --lt-*) ;;
- *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
- esac
- shift
- done
+ case \" \$* \" in
+ *\\ --lt-*)
+ for lt_wr_arg
+ do
+ case \$lt_wr_arg in
+ --lt-*) ;;
+ *) set x \"\$@\" \"\$lt_wr_arg\"; shift;;
+ esac
+ shift
+ done ;;
+ esac
func_exec_program_core \${1+\"\$@\"}
}
@@ -4037,13 +5418,13 @@ func_exec_program ()
test -n \"\$absdir\" && thisdir=\"\$absdir\"
"
- if test "$fast_install" = yes; then
+ if test yes = "$fast_install"; then
$ECHO "\
program=lt-'$outputname'$exeext
progdir=\"\$thisdir/$objdir\"
if test ! -f \"\$progdir/\$program\" ||
- { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\
+ { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\
test \"X\$file\" != \"X\$progdir/\$program\"; }; then
file=\"\$\$-\$program\"
@@ -4060,7 +5441,7 @@ func_exec_program ()
if test -n \"\$relink_command\"; then
if relink_command_output=\`eval \$relink_command 2>&1\`; then :
else
- $ECHO \"\$relink_command_output\" >&2
+ \$ECHO \"\$relink_command_output\" >&2
$RM \"\$progdir/\$file\"
exit 1
fi
@@ -4095,7 +5476,7 @@ func_exec_program ()
fi
# Export our shlibpath_var if we have one.
- if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
$ECHO "\
# Add our own library path to $shlibpath_var
$shlibpath_var=\"$temp_rpath\$$shlibpath_var\"
@@ -4115,7 +5496,7 @@ func_exec_program ()
fi
else
# The program doesn't exist.
- \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2
+ \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2
\$ECHO \"This script is just a wrapper for \$program.\" 1>&2
\$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2
exit 1
@@ -4134,7 +5515,7 @@ func_emit_cwrapperexe_src ()
cat <<EOF
/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
- Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+ Generated by $PROGRAM (GNU $PACKAGE) $VERSION
The $output program cannot be directly executed until all the libtool
libraries that it depends on are installed.
@@ -4169,47 +5550,45 @@ EOF
#include <fcntl.h>
#include <sys/stat.h>
+#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0)
+
/* declarations of non-ANSI functions */
-#if defined(__MINGW32__)
+#if defined __MINGW32__
# ifdef __STRICT_ANSI__
int _putenv (const char *);
# endif
-#elif defined(__CYGWIN__)
+#elif defined __CYGWIN__
# ifdef __STRICT_ANSI__
char *realpath (const char *, char *);
int putenv (char *);
int setenv (const char *, const char *, int);
# endif
-/* #elif defined (other platforms) ... */
+/* #elif defined other_platform || defined ... */
#endif
/* portability defines, excluding path handling macros */
-#if defined(_MSC_VER)
+#if defined _MSC_VER
# define setmode _setmode
# define stat _stat
# define chmod _chmod
# define getcwd _getcwd
# define putenv _putenv
# define S_IXUSR _S_IEXEC
-# ifndef _INTPTR_T_DEFINED
-# define _INTPTR_T_DEFINED
-# define intptr_t int
-# endif
-#elif defined(__MINGW32__)
+#elif defined __MINGW32__
# define setmode _setmode
# define stat _stat
# define chmod _chmod
# define getcwd _getcwd
# define putenv _putenv
-#elif defined(__CYGWIN__)
+#elif defined __CYGWIN__
# define HAVE_SETENV
# define FOPEN_WB "wb"
-/* #elif defined (other platforms) ... */
+/* #elif defined other platforms ... */
#endif
-#if defined(PATH_MAX)
+#if defined PATH_MAX
# define LT_PATHMAX PATH_MAX
-#elif defined(MAXPATHLEN)
+#elif defined MAXPATHLEN
# define LT_PATHMAX MAXPATHLEN
#else
# define LT_PATHMAX 1024
@@ -4228,8 +5607,8 @@ int setenv (const char *, const char *, int);
# define PATH_SEPARATOR ':'
#endif
-#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
- defined (__OS2__)
+#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \
+ defined __OS2__
# define HAVE_DOS_BASED_FILE_SYSTEM
# define FOPEN_WB "wb"
# ifndef DIR_SEPARATOR_2
@@ -4262,10 +5641,10 @@ int setenv (const char *, const char *, int);
#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type)))
#define XFREE(stale) do { \
- if (stale) { free ((void *) stale); stale = 0; } \
+ if (stale) { free (stale); stale = 0; } \
} while (0)
-#if defined(LT_DEBUGWRAPPER)
+#if defined LT_DEBUGWRAPPER
static int lt_debug = 1;
#else
static int lt_debug = 0;
@@ -4294,11 +5673,16 @@ void lt_dump_script (FILE *f);
EOF
cat <<EOF
-volatile const char * MAGIC_EXE = "$magic_exe";
+#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5)
+# define externally_visible volatile
+#else
+# define externally_visible __attribute__((externally_visible)) volatile
+#endif
+externally_visible const char * MAGIC_EXE = "$magic_exe";
const char * LIB_PATH_VARNAME = "$shlibpath_var";
EOF
- if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
+ if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then
func_to_host_path "$temp_rpath"
cat <<EOF
const char * LIB_PATH_VALUE = "$func_to_host_path_result";
@@ -4322,7 +5706,7 @@ const char * EXE_PATH_VALUE = "";
EOF
fi
- if test "$fast_install" = yes; then
+ if test yes = "$fast_install"; then
cat <<EOF
const char * TARGET_PROGRAM_NAME = "lt-$outputname"; /* hopefully, no .exe */
EOF
@@ -4351,12 +5735,12 @@ main (int argc, char *argv[])
char *actual_cwrapper_name;
char *target_name;
char *lt_argv_zero;
- intptr_t rval = 127;
+ int rval = 127;
int i;
program_name = (char *) xstrdup (base_name (argv[0]));
- newargz = XMALLOC (char *, argc + 1);
+ newargz = XMALLOC (char *, (size_t) argc + 1);
/* very simple arg parsing; don't want to rely on getopt
* also, copy all non cwrapper options to newargz, except
@@ -4365,10 +5749,10 @@ main (int argc, char *argv[])
newargc=0;
for (i = 1; i < argc; i++)
{
- if (strcmp (argv[i], dumpscript_opt) == 0)
+ if (STREQ (argv[i], dumpscript_opt))
{
EOF
- case "$host" in
+ case $host in
*mingw* | *cygwin* )
# make stdout use "unix" line endings
echo " setmode(1,_O_BINARY);"
@@ -4379,12 +5763,12 @@ EOF
lt_dump_script (stdout);
return 0;
}
- if (strcmp (argv[i], debug_opt) == 0)
+ if (STREQ (argv[i], debug_opt))
{
lt_debug = 1;
continue;
}
- if (strcmp (argv[i], ltwrapper_option_prefix) == 0)
+ if (STREQ (argv[i], ltwrapper_option_prefix))
{
/* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
namespace, but it is not one of the ones we know about and
@@ -4407,7 +5791,7 @@ EOF
EOF
cat <<EOF
/* The GNU banner must be the first non-error debug message */
- lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\n");
+ lt_debugprintf (__FILE__, __LINE__, "libtool wrapper (GNU $PACKAGE) $VERSION\n");
EOF
cat <<"EOF"
lt_debugprintf (__FILE__, __LINE__, "(main) argv[0]: %s\n", argv[0]);
@@ -4518,7 +5902,7 @@ EOF
cat <<"EOF"
/* execv doesn't actually work on mingw as expected on unix */
newargz = prepare_spawn (newargz);
- rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
+ rval = (int) _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz);
if (rval == -1)
{
/* failed to start process */
@@ -4563,7 +5947,7 @@ base_name (const char *name)
{
const char *base;
-#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
/* Skip over the disk name in MSDOS pathnames. */
if (isalpha ((unsigned char) name[0]) && name[1] == ':')
name += 2;
@@ -4622,7 +6006,7 @@ find_executable (const char *wrapper)
const char *p_next;
/* static buffer for getcwd */
char tmp[LT_PATHMAX + 1];
- int tmp_len;
+ size_t tmp_len;
char *concat_name;
lt_debugprintf (__FILE__, __LINE__, "(find_executable): %s\n",
@@ -4632,7 +6016,7 @@ find_executable (const char *wrapper)
return NULL;
/* Absolute path? */
-#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':')
{
concat_name = xstrdup (wrapper);
@@ -4650,7 +6034,7 @@ find_executable (const char *wrapper)
return concat_name;
XFREE (concat_name);
}
-#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+#if defined HAVE_DOS_BASED_FILE_SYSTEM
}
#endif
@@ -4673,7 +6057,7 @@ find_executable (const char *wrapper)
for (q = p; *q; q++)
if (IS_PATH_SEPARATOR (*q))
break;
- p_len = q - p;
+ p_len = (size_t) (q - p);
p_next = (*q == '\0' ? q : q + 1);
if (p_len == 0)
{
@@ -4792,7 +6176,7 @@ strendzap (char *str, const char *pat)
if (patlen <= len)
{
str += len - patlen;
- if (strcmp (str, pat) == 0)
+ if (STREQ (str, pat))
*str = '\0';
}
return str;
@@ -4857,7 +6241,7 @@ lt_setenv (const char *name, const char *value)
char *str = xstrdup (value);
setenv (name, str, 1);
#else
- int len = strlen (name) + 1 + strlen (value) + 1;
+ size_t len = strlen (name) + 1 + strlen (value) + 1;
char *str = XMALLOC (char, len);
sprintf (str, "%s=%s", name, value);
if (putenv (str) != EXIT_SUCCESS)
@@ -4874,8 +6258,8 @@ lt_extend_str (const char *orig_value, const char *add, int to_end)
char *new_value;
if (orig_value && *orig_value)
{
- int orig_value_len = strlen (orig_value);
- int add_len = strlen (add);
+ size_t orig_value_len = strlen (orig_value);
+ size_t add_len = strlen (add);
new_value = XMALLOC (char, add_len + orig_value_len + 1);
if (to_end)
{
@@ -4906,10 +6290,10 @@ lt_update_exe_path (const char *name, const char *value)
{
char *new_value = lt_extend_str (getenv (name), value, 0);
/* some systems can't cope with a ':'-terminated path #' */
- int len = strlen (new_value);
- while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
+ size_t len = strlen (new_value);
+ while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1]))
{
- new_value[len-1] = '\0';
+ new_value[--len] = '\0';
}
lt_setenv (name, new_value);
XFREE (new_value);
@@ -5057,9 +6441,15 @@ void lt_dump_script (FILE* f)
{
EOF
func_emit_wrapper yes |
- $SED -e 's/\([\\"]\)/\\\1/g' \
- -e 's/^/ fputs ("/' -e 's/$/\\n", f);/'
-
+ $SED -n -e '
+s/^\(.\{79\}\)\(..*\)/\1\
+\2/
+h
+s/\([\\"]\)/\\\1/g
+s/$/\\n/
+s/\([^\n]*\).*/ fputs ("\1", f);/p
+g
+D'
cat <<"EOF"
}
EOF
@@ -5070,27 +6460,47 @@ EOF
# True if ARG is an import lib, as indicated by $file_magic_cmd
func_win32_import_lib_p ()
{
- $opt_debug
+ $debug_cmd
+
case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in
*import*) : ;;
*) false ;;
esac
}
+# func_suncc_cstd_abi
+# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!!
+# Several compiler flags select an ABI that is incompatible with the
+# Cstd library. Avoid specifying it if any are in CXXFLAGS.
+func_suncc_cstd_abi ()
+{
+ $debug_cmd
+
+ case " $compile_command " in
+ *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*)
+ suncc_use_cstd_abi=no
+ ;;
+ *)
+ suncc_use_cstd_abi=yes
+ ;;
+ esac
+}
+
# func_mode_link arg...
func_mode_link ()
{
- $opt_debug
+ $debug_cmd
+
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
# It is impossible to link a dll without this setting, and
# we shouldn't force the makefile maintainer to figure out
- # which system we are compiling for in order to pass an extra
+ # what system we are compiling for in order to pass an extra
# flag for every libtool invocation.
# allow_undefined=no
# FIXME: Unfortunately, there are problems with the above when trying
- # to make a dll which has undefined symbols, in which case not
+ # to make a dll that has undefined symbols, in which case not
# even a static library is built. For now, we need to specify
# -no-undefined on the libtool link line when we can be certain
# that all symbols are satisfied, otherwise we get a static library.
@@ -5134,10 +6544,11 @@ func_mode_link ()
module=no
no_install=no
objs=
+ os2dllname=
non_pic_objects=
precious_files_regex=
prefer_static_libs=no
- preload=no
+ preload=false
prev=
prevarg=
release=
@@ -5149,7 +6560,7 @@ func_mode_link ()
vinfo=
vinfo_number=no
weak_libs=
- single_module="${wl}-single_module"
+ single_module=$wl-single_module
func_infer_tag $base_compile
# We need to know -static, to get the right output filenames.
@@ -5157,15 +6568,15 @@ func_mode_link ()
do
case $arg in
-shared)
- test "$build_libtool_libs" != yes && \
- func_fatal_configuration "can not build a shared library"
+ test yes != "$build_libtool_libs" \
+ && func_fatal_configuration "cannot build a shared library"
build_old_libs=no
break
;;
-all-static | -static | -static-libtool-libs)
case $arg in
-all-static)
- if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then
+ if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then
func_warning "complete static linking is impossible in this configuration"
fi
if test -n "$link_static_flag"; then
@@ -5198,7 +6609,7 @@ func_mode_link ()
# Go through the arguments, transforming them on the way.
while test "$#" -gt 0; do
- arg="$1"
+ arg=$1
shift
func_quote_for_eval "$arg"
qarg=$func_quote_for_eval_unquoted_result
@@ -5215,21 +6626,21 @@ func_mode_link ()
case $prev in
bindir)
- bindir="$arg"
+ bindir=$arg
prev=
continue
;;
dlfiles|dlprefiles)
- if test "$preload" = no; then
+ $preload || {
# Add the symbol object into the linking commands.
func_append compile_command " @SYMFILE@"
func_append finalize_command " @SYMFILE@"
- preload=yes
- fi
+ preload=:
+ }
case $arg in
*.la | *.lo) ;; # We handle these cases below.
force)
- if test "$dlself" = no; then
+ if test no = "$dlself"; then
dlself=needless
export_dynamic=yes
fi
@@ -5237,9 +6648,9 @@ func_mode_link ()
continue
;;
self)
- if test "$prev" = dlprefiles; then
+ if test dlprefiles = "$prev"; then
dlself=yes
- elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then
+ elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then
dlself=yes
else
dlself=needless
@@ -5249,7 +6660,7 @@ func_mode_link ()
continue
;;
*)
- if test "$prev" = dlfiles; then
+ if test dlfiles = "$prev"; then
func_append dlfiles " $arg"
else
func_append dlprefiles " $arg"
@@ -5260,14 +6671,14 @@ func_mode_link ()
esac
;;
expsyms)
- export_symbols="$arg"
+ export_symbols=$arg
test -f "$arg" \
- || func_fatal_error "symbol file \`$arg' does not exist"
+ || func_fatal_error "symbol file '$arg' does not exist"
prev=
continue
;;
expsyms_regex)
- export_symbols_regex="$arg"
+ export_symbols_regex=$arg
prev=
continue
;;
@@ -5285,7 +6696,13 @@ func_mode_link ()
continue
;;
inst_prefix)
- inst_prefix_dir="$arg"
+ inst_prefix_dir=$arg
+ prev=
+ continue
+ ;;
+ mllvm)
+ # Clang does not use LLVM to link, so we can simply discard any
+ # '-mllvm $arg' options when doing the link step.
prev=
continue
;;
@@ -5309,21 +6726,21 @@ func_mode_link ()
if test -z "$pic_object" ||
test -z "$non_pic_object" ||
- test "$pic_object" = none &&
- test "$non_pic_object" = none; then
- func_fatal_error "cannot find name of object for \`$arg'"
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
fi
# Extract subdirectory from the argument.
func_dirname "$arg" "/" ""
- xdir="$func_dirname_result"
+ xdir=$func_dirname_result
- if test "$pic_object" != none; then
+ if test none != "$pic_object"; then
# Prepend the subdirectory the object is found in.
- pic_object="$xdir$pic_object"
+ pic_object=$xdir$pic_object
- if test "$prev" = dlfiles; then
- if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
func_append dlfiles " $pic_object"
prev=
continue
@@ -5334,7 +6751,7 @@ func_mode_link ()
fi
# CHECK ME: I think I busted this. -Ossama
- if test "$prev" = dlprefiles; then
+ if test dlprefiles = "$prev"; then
# Preload the old-style object.
func_append dlprefiles " $pic_object"
prev=
@@ -5342,23 +6759,23 @@ func_mode_link ()
# A PIC object.
func_append libobjs " $pic_object"
- arg="$pic_object"
+ arg=$pic_object
fi
# Non-PIC object.
- if test "$non_pic_object" != none; then
+ if test none != "$non_pic_object"; then
# Prepend the subdirectory the object is found in.
- non_pic_object="$xdir$non_pic_object"
+ non_pic_object=$xdir$non_pic_object
# A standard non-PIC object
func_append non_pic_objects " $non_pic_object"
- if test -z "$pic_object" || test "$pic_object" = none ; then
- arg="$non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
fi
else
# If the PIC object exists, use it instead.
# $xdir was prepended to $pic_object above.
- non_pic_object="$pic_object"
+ non_pic_object=$pic_object
func_append non_pic_objects " $non_pic_object"
fi
else
@@ -5366,7 +6783,7 @@ func_mode_link ()
if $opt_dry_run; then
# Extract subdirectory from the argument.
func_dirname "$arg" "/" ""
- xdir="$func_dirname_result"
+ xdir=$func_dirname_result
func_lo2o "$arg"
pic_object=$xdir$objdir/$func_lo2o_result
@@ -5374,24 +6791,29 @@ func_mode_link ()
func_append libobjs " $pic_object"
func_append non_pic_objects " $non_pic_object"
else
- func_fatal_error "\`$arg' is not a valid libtool object"
+ func_fatal_error "'$arg' is not a valid libtool object"
fi
fi
done
else
- func_fatal_error "link input file \`$arg' does not exist"
+ func_fatal_error "link input file '$arg' does not exist"
fi
arg=$save_arg
prev=
continue
;;
+ os2dllname)
+ os2dllname=$arg
+ prev=
+ continue
+ ;;
precious_regex)
- precious_files_regex="$arg"
+ precious_files_regex=$arg
prev=
continue
;;
release)
- release="-$arg"
+ release=-$arg
prev=
continue
;;
@@ -5403,7 +6825,7 @@ func_mode_link ()
func_fatal_error "only absolute run-paths are allowed"
;;
esac
- if test "$prev" = rpath; then
+ if test rpath = "$prev"; then
case "$rpath " in
*" $arg "*) ;;
*) func_append rpath " $arg" ;;
@@ -5418,7 +6840,7 @@ func_mode_link ()
continue
;;
shrext)
- shrext_cmds="$arg"
+ shrext_cmds=$arg
prev=
continue
;;
@@ -5458,7 +6880,7 @@ func_mode_link ()
esac
fi # test -n "$prev"
- prevarg="$arg"
+ prevarg=$arg
case $arg in
-all-static)
@@ -5472,7 +6894,7 @@ func_mode_link ()
-allow-undefined)
# FIXME: remove this flag sometime in the future.
- func_fatal_error "\`-allow-undefined' must not be used because it is the default"
+ func_fatal_error "'-allow-undefined' must not be used because it is the default"
;;
-avoid-version)
@@ -5504,7 +6926,7 @@ func_mode_link ()
if test -n "$export_symbols" || test -n "$export_symbols_regex"; then
func_fatal_error "more than one -exported-symbols argument is not allowed"
fi
- if test "X$arg" = "X-export-symbols"; then
+ if test X-export-symbols = "X$arg"; then
prev=expsyms
else
prev=expsyms_regex
@@ -5538,9 +6960,9 @@ func_mode_link ()
func_stripname "-L" '' "$arg"
if test -z "$func_stripname_result"; then
if test "$#" -gt 0; then
- func_fatal_error "require no space between \`-L' and \`$1'"
+ func_fatal_error "require no space between '-L' and '$1'"
else
- func_fatal_error "need path for \`-L' option"
+ func_fatal_error "need path for '-L' option"
fi
fi
func_resolve_sysroot "$func_stripname_result"
@@ -5551,8 +6973,8 @@ func_mode_link ()
*)
absdir=`cd "$dir" && pwd`
test -z "$absdir" && \
- func_fatal_error "cannot determine absolute directory name of \`$dir'"
- dir="$absdir"
+ func_fatal_error "cannot determine absolute directory name of '$dir'"
+ dir=$absdir
;;
esac
case "$deplibs " in
@@ -5587,7 +7009,7 @@ func_mode_link ()
;;
-l*)
- if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then
+ if test X-lc = "X$arg" || test X-lm = "X$arg"; then
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
# These systems don't actually have a C or math library (as such)
@@ -5595,11 +7017,11 @@ func_mode_link ()
;;
*-*-os2*)
# These systems don't actually have a C library (as such)
- test "X$arg" = "X-lc" && continue
+ test X-lc = "X$arg" && continue
;;
- *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
# Do not include libc due to us having libc/libc_r.
- test "X$arg" = "X-lc" && continue
+ test X-lc = "X$arg" && continue
;;
*-*-rhapsody* | *-*-darwin1.[012])
# Rhapsody C and math libraries are in the System framework
@@ -5608,16 +7030,16 @@ func_mode_link ()
;;
*-*-sco3.2v5* | *-*-sco5v6*)
# Causes problems with __ctype
- test "X$arg" = "X-lc" && continue
+ test X-lc = "X$arg" && continue
;;
*-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*)
# Compiler inserts libc in the correct place for threads to work
- test "X$arg" = "X-lc" && continue
+ test X-lc = "X$arg" && continue
;;
esac
- elif test "X$arg" = "X-lc_r"; then
+ elif test X-lc_r = "X$arg"; then
case $host in
- *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
# Do not include libc_r directly, use -pthread flag.
continue
;;
@@ -5627,6 +7049,11 @@ func_mode_link ()
continue
;;
+ -mllvm)
+ prev=mllvm
+ continue
+ ;;
+
-module)
module=yes
continue
@@ -5643,7 +7070,8 @@ func_mode_link ()
continue
;;
- -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
func_append compiler_flags " $arg"
func_append compile_command " $arg"
func_append finalize_command " $arg"
@@ -5655,7 +7083,7 @@ func_mode_link ()
;;
-multi_module)
- single_module="${wl}-multi_module"
+ single_module=$wl-multi_module
continue
;;
@@ -5669,8 +7097,8 @@ func_mode_link ()
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
# The PATH hackery in wrapper scripts is required on Windows
# and Darwin in order for the loader to find any dlls it needs.
- func_warning "\`-no-install' is ignored for $host"
- func_warning "assuming \`-no-fast-install' instead"
+ func_warning "'-no-install' is ignored for $host"
+ func_warning "assuming '-no-fast-install' instead"
fast_install=no
;;
*) no_install=yes ;;
@@ -5688,6 +7116,11 @@ func_mode_link ()
continue
;;
+ -os2dllname)
+ prev=os2dllname
+ continue
+ ;;
+
-o) prev=output ;;
-precious-files-regex)
@@ -5775,14 +7208,14 @@ func_mode_link ()
func_stripname '-Wc,' '' "$arg"
args=$func_stripname_result
arg=
- save_ifs="$IFS"; IFS=','
+ save_ifs=$IFS; IFS=,
for flag in $args; do
- IFS="$save_ifs"
+ IFS=$save_ifs
func_quote_for_eval "$flag"
func_append arg " $func_quote_for_eval_result"
func_append compiler_flags " $func_quote_for_eval_result"
done
- IFS="$save_ifs"
+ IFS=$save_ifs
func_stripname ' ' '' "$arg"
arg=$func_stripname_result
;;
@@ -5791,15 +7224,15 @@ func_mode_link ()
func_stripname '-Wl,' '' "$arg"
args=$func_stripname_result
arg=
- save_ifs="$IFS"; IFS=','
+ save_ifs=$IFS; IFS=,
for flag in $args; do
- IFS="$save_ifs"
+ IFS=$save_ifs
func_quote_for_eval "$flag"
func_append arg " $wl$func_quote_for_eval_result"
func_append compiler_flags " $wl$func_quote_for_eval_result"
func_append linker_flags " $func_quote_for_eval_result"
done
- IFS="$save_ifs"
+ IFS=$save_ifs
func_stripname ' ' '' "$arg"
arg=$func_stripname_result
;;
@@ -5822,7 +7255,7 @@ func_mode_link ()
# -msg_* for osf cc
-msg_*)
func_quote_for_eval "$arg"
- arg="$func_quote_for_eval_result"
+ arg=$func_quote_for_eval_result
;;
# Flags to be passed through unchanged, with rationale:
@@ -5834,25 +7267,46 @@ func_mode_link ()
# -m*, -t[45]*, -txscale* architecture-specific flags for GCC
# -F/path path to uninstalled frameworks, gcc on darwin
# -p, -pg, --coverage, -fprofile-* profiling flags for GCC
+ # -fstack-protector* stack protector flags for GCC
# @file GCC response files
# -tp=* Portland pgcc target processor selection
# --sysroot=* for sysroot support
- # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+ # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
+ # -stdlib=* select c++ std lib with clang
-64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
-t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
- -O*|-flto*|-fwhopr*|-fuse-linker-plugin)
+ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*)
func_quote_for_eval "$arg"
- arg="$func_quote_for_eval_result"
+ arg=$func_quote_for_eval_result
func_append compile_command " $arg"
func_append finalize_command " $arg"
func_append compiler_flags " $arg"
continue
;;
+ -Z*)
+ if test os2 = "`expr $host : '.*\(os2\)'`"; then
+ # OS/2 uses -Zxxx to specify OS/2-specific options
+ compiler_flags="$compiler_flags $arg"
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ case $arg in
+ -Zlinker | -Zstack)
+ prev=xcompiler
+ ;;
+ esac
+ continue
+ else
+ # Otherwise treat like 'Some other compiler flag' below
+ func_quote_for_eval "$arg"
+ arg=$func_quote_for_eval_result
+ fi
+ ;;
+
# Some other compiler flag.
-* | +*)
func_quote_for_eval "$arg"
- arg="$func_quote_for_eval_result"
+ arg=$func_quote_for_eval_result
;;
*.$objext)
@@ -5873,21 +7327,21 @@ func_mode_link ()
if test -z "$pic_object" ||
test -z "$non_pic_object" ||
- test "$pic_object" = none &&
- test "$non_pic_object" = none; then
- func_fatal_error "cannot find name of object for \`$arg'"
+ test none = "$pic_object" &&
+ test none = "$non_pic_object"; then
+ func_fatal_error "cannot find name of object for '$arg'"
fi
# Extract subdirectory from the argument.
func_dirname "$arg" "/" ""
- xdir="$func_dirname_result"
+ xdir=$func_dirname_result
- if test "$pic_object" != none; then
+ test none = "$pic_object" || {
# Prepend the subdirectory the object is found in.
- pic_object="$xdir$pic_object"
+ pic_object=$xdir$pic_object
- if test "$prev" = dlfiles; then
- if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then
+ if test dlfiles = "$prev"; then
+ if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then
func_append dlfiles " $pic_object"
prev=
continue
@@ -5898,7 +7352,7 @@ func_mode_link ()
fi
# CHECK ME: I think I busted this. -Ossama
- if test "$prev" = dlprefiles; then
+ if test dlprefiles = "$prev"; then
# Preload the old-style object.
func_append dlprefiles " $pic_object"
prev=
@@ -5906,23 +7360,23 @@ func_mode_link ()
# A PIC object.
func_append libobjs " $pic_object"
- arg="$pic_object"
- fi
+ arg=$pic_object
+ }
# Non-PIC object.
- if test "$non_pic_object" != none; then
+ if test none != "$non_pic_object"; then
# Prepend the subdirectory the object is found in.
- non_pic_object="$xdir$non_pic_object"
+ non_pic_object=$xdir$non_pic_object
# A standard non-PIC object
func_append non_pic_objects " $non_pic_object"
- if test -z "$pic_object" || test "$pic_object" = none ; then
- arg="$non_pic_object"
+ if test -z "$pic_object" || test none = "$pic_object"; then
+ arg=$non_pic_object
fi
else
# If the PIC object exists, use it instead.
# $xdir was prepended to $pic_object above.
- non_pic_object="$pic_object"
+ non_pic_object=$pic_object
func_append non_pic_objects " $non_pic_object"
fi
else
@@ -5930,7 +7384,7 @@ func_mode_link ()
if $opt_dry_run; then
# Extract subdirectory from the argument.
func_dirname "$arg" "/" ""
- xdir="$func_dirname_result"
+ xdir=$func_dirname_result
func_lo2o "$arg"
pic_object=$xdir$objdir/$func_lo2o_result
@@ -5938,7 +7392,7 @@ func_mode_link ()
func_append libobjs " $pic_object"
func_append non_pic_objects " $non_pic_object"
else
- func_fatal_error "\`$arg' is not a valid libtool object"
+ func_fatal_error "'$arg' is not a valid libtool object"
fi
fi
;;
@@ -5954,11 +7408,11 @@ func_mode_link ()
# A libtool-controlled library.
func_resolve_sysroot "$arg"
- if test "$prev" = dlfiles; then
+ if test dlfiles = "$prev"; then
# This library was specified with -dlopen.
func_append dlfiles " $func_resolve_sysroot_result"
prev=
- elif test "$prev" = dlprefiles; then
+ elif test dlprefiles = "$prev"; then
# The library was specified with -dlpreopen.
func_append dlprefiles " $func_resolve_sysroot_result"
prev=
@@ -5973,7 +7427,7 @@ func_mode_link ()
# Unknown arguments in both finalize_command and compile_command need
# to be aesthetically quoted because they are evaled later.
func_quote_for_eval "$arg"
- arg="$func_quote_for_eval_result"
+ arg=$func_quote_for_eval_result
;;
esac # arg
@@ -5985,9 +7439,9 @@ func_mode_link ()
done # argument parsing loop
test -n "$prev" && \
- func_fatal_help "the \`$prevarg' option requires an argument"
+ func_fatal_help "the '$prevarg' option requires an argument"
- if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then
+ if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then
eval arg=\"$export_dynamic_flag_spec\"
func_append compile_command " $arg"
func_append finalize_command " $arg"
@@ -5996,20 +7450,23 @@ func_mode_link ()
oldlibs=
# calculate the name of the file, without its directory
func_basename "$output"
- outputname="$func_basename_result"
- libobjs_save="$libobjs"
+ outputname=$func_basename_result
+ libobjs_save=$libobjs
if test -n "$shlibpath_var"; then
# get the directories listed in $shlibpath_var
- eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\`
+ eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\`
else
shlib_search_path=
fi
eval sys_lib_search_path=\"$sys_lib_search_path_spec\"
eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\"
+ # Definition is injected by LT_CONFIG during libtool generation.
+ func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH"
+
func_dirname "$output" "/" ""
- output_objdir="$func_dirname_result$objdir"
+ output_objdir=$func_dirname_result$objdir
func_to_tool_file "$output_objdir/"
tool_output_objdir=$func_to_tool_file_result
# Create the object directory.
@@ -6032,7 +7489,7 @@ func_mode_link ()
# Find all interdependent deplibs by searching for libraries
# that are linked more than once (e.g. -la -lb -la)
for deplib in $deplibs; do
- if $opt_preserve_dup_deps ; then
+ if $opt_preserve_dup_deps; then
case "$libs " in
*" $deplib "*) func_append specialdeplibs " $deplib" ;;
esac
@@ -6040,7 +7497,7 @@ func_mode_link ()
func_append libs " $deplib"
done
- if test "$linkmode" = lib; then
+ if test lib = "$linkmode"; then
libs="$predeps $libs $compiler_lib_search_path $postdeps"
# Compute libraries that are listed more than once in $predeps
@@ -6072,7 +7529,7 @@ func_mode_link ()
case $file in
*.la) ;;
*)
- func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file"
+ func_fatal_help "libraries can '-dlopen' only libtool libraries: $file"
;;
esac
done
@@ -6080,7 +7537,7 @@ func_mode_link ()
prog)
compile_deplibs=
finalize_deplibs=
- alldeplibs=no
+ alldeplibs=false
newdlfiles=
newdlprefiles=
passes="conv scan dlopen dlpreopen link"
@@ -6092,29 +7549,29 @@ func_mode_link ()
for pass in $passes; do
# The preopen pass in lib mode reverses $deplibs; put it back here
# so that -L comes before libs that need it for instance...
- if test "$linkmode,$pass" = "lib,link"; then
+ if test lib,link = "$linkmode,$pass"; then
## FIXME: Find the place where the list is rebuilt in the wrong
## order, and fix it there properly
tmp_deplibs=
for deplib in $deplibs; do
tmp_deplibs="$deplib $tmp_deplibs"
done
- deplibs="$tmp_deplibs"
+ deplibs=$tmp_deplibs
fi
- if test "$linkmode,$pass" = "lib,link" ||
- test "$linkmode,$pass" = "prog,scan"; then
- libs="$deplibs"
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass"; then
+ libs=$deplibs
deplibs=
fi
- if test "$linkmode" = prog; then
+ if test prog = "$linkmode"; then
case $pass in
- dlopen) libs="$dlfiles" ;;
- dlpreopen) libs="$dlprefiles" ;;
+ dlopen) libs=$dlfiles ;;
+ dlpreopen) libs=$dlprefiles ;;
link) libs="$deplibs %DEPLIBS% $dependency_libs" ;;
esac
fi
- if test "$linkmode,$pass" = "lib,dlpreopen"; then
+ if test lib,dlpreopen = "$linkmode,$pass"; then
# Collect and forward deplibs of preopened libtool libs
for lib in $dlprefiles; do
# Ignore non-libtool-libs
@@ -6135,25 +7592,26 @@ func_mode_link ()
esac
done
done
- libs="$dlprefiles"
+ libs=$dlprefiles
fi
- if test "$pass" = dlopen; then
+ if test dlopen = "$pass"; then
# Collect dlpreopened libraries
- save_deplibs="$deplibs"
+ save_deplibs=$deplibs
deplibs=
fi
for deplib in $libs; do
lib=
- found=no
+ found=false
case $deplib in
- -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads)
- if test "$linkmode,$pass" = "prog,link"; then
+ -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ if test prog,link = "$linkmode,$pass"; then
compile_deplibs="$deplib $compile_deplibs"
finalize_deplibs="$deplib $finalize_deplibs"
else
func_append compiler_flags " $deplib"
- if test "$linkmode" = lib ; then
+ if test lib = "$linkmode"; then
case "$new_inherited_linker_flags " in
*" $deplib "*) ;;
* ) func_append new_inherited_linker_flags " $deplib" ;;
@@ -6163,13 +7621,13 @@ func_mode_link ()
continue
;;
-l*)
- if test "$linkmode" != lib && test "$linkmode" != prog; then
- func_warning "\`-l' is ignored for archives/objects"
+ if test lib != "$linkmode" && test prog != "$linkmode"; then
+ func_warning "'-l' is ignored for archives/objects"
continue
fi
func_stripname '-l' '' "$deplib"
name=$func_stripname_result
- if test "$linkmode" = lib; then
+ if test lib = "$linkmode"; then
searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path"
else
searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path"
@@ -6177,31 +7635,22 @@ func_mode_link ()
for searchdir in $searchdirs; do
for search_ext in .la $std_shrext .so .a; do
# Search the libtool library
- lib="$searchdir/lib${name}${search_ext}"
+ lib=$searchdir/lib$name$search_ext
if test -f "$lib"; then
- if test "$search_ext" = ".la"; then
- found=yes
+ if test .la = "$search_ext"; then
+ found=:
else
- found=no
+ found=false
fi
break 2
fi
done
done
- if test "$found" != yes; then
- # deplib doesn't seem to be a libtool library
- if test "$linkmode,$pass" = "prog,link"; then
- compile_deplibs="$deplib $compile_deplibs"
- finalize_deplibs="$deplib $finalize_deplibs"
- else
- deplibs="$deplib $deplibs"
- test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
- fi
- continue
- else # deplib is a libtool library
+ if $found; then
+ # deplib is a libtool library
# If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib,
# We need to do some special things here, and not later.
- if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
case " $predeps $postdeps " in
*" $deplib "*)
if func_lalib_p "$lib"; then
@@ -6209,19 +7658,19 @@ func_mode_link ()
old_library=
func_source "$lib"
for l in $old_library $library_names; do
- ll="$l"
+ ll=$l
done
- if test "X$ll" = "X$old_library" ; then # only static version available
- found=no
+ if test "X$ll" = "X$old_library"; then # only static version available
+ found=false
func_dirname "$lib" "" "."
- ladir="$func_dirname_result"
+ ladir=$func_dirname_result
lib=$ladir/$old_library
- if test "$linkmode,$pass" = "prog,link"; then
+ if test prog,link = "$linkmode,$pass"; then
compile_deplibs="$deplib $compile_deplibs"
finalize_deplibs="$deplib $finalize_deplibs"
else
deplibs="$deplib $deplibs"
- test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
fi
continue
fi
@@ -6230,15 +7679,25 @@ func_mode_link ()
*) ;;
esac
fi
+ else
+ # deplib doesn't seem to be a libtool library
+ if test prog,link = "$linkmode,$pass"; then
+ compile_deplibs="$deplib $compile_deplibs"
+ finalize_deplibs="$deplib $finalize_deplibs"
+ else
+ deplibs="$deplib $deplibs"
+ test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs"
+ fi
+ continue
fi
;; # -l
*.ltframework)
- if test "$linkmode,$pass" = "prog,link"; then
+ if test prog,link = "$linkmode,$pass"; then
compile_deplibs="$deplib $compile_deplibs"
finalize_deplibs="$deplib $finalize_deplibs"
else
deplibs="$deplib $deplibs"
- if test "$linkmode" = lib ; then
+ if test lib = "$linkmode"; then
case "$new_inherited_linker_flags " in
*" $deplib "*) ;;
* ) func_append new_inherited_linker_flags " $deplib" ;;
@@ -6251,18 +7710,18 @@ func_mode_link ()
case $linkmode in
lib)
deplibs="$deplib $deplibs"
- test "$pass" = conv && continue
+ test conv = "$pass" && continue
newdependency_libs="$deplib $newdependency_libs"
func_stripname '-L' '' "$deplib"
func_resolve_sysroot "$func_stripname_result"
func_append newlib_search_path " $func_resolve_sysroot_result"
;;
prog)
- if test "$pass" = conv; then
+ if test conv = "$pass"; then
deplibs="$deplib $deplibs"
continue
fi
- if test "$pass" = scan; then
+ if test scan = "$pass"; then
deplibs="$deplib $deplibs"
else
compile_deplibs="$deplib $compile_deplibs"
@@ -6273,13 +7732,13 @@ func_mode_link ()
func_append newlib_search_path " $func_resolve_sysroot_result"
;;
*)
- func_warning "\`-L' is ignored for archives/objects"
+ func_warning "'-L' is ignored for archives/objects"
;;
esac # linkmode
continue
;; # -L
-R*)
- if test "$pass" = link; then
+ if test link = "$pass"; then
func_stripname '-R' '' "$deplib"
func_resolve_sysroot "$func_stripname_result"
dir=$func_resolve_sysroot_result
@@ -6297,7 +7756,7 @@ func_mode_link ()
lib=$func_resolve_sysroot_result
;;
*.$libext)
- if test "$pass" = conv; then
+ if test conv = "$pass"; then
deplibs="$deplib $deplibs"
continue
fi
@@ -6308,21 +7767,26 @@ func_mode_link ()
case " $dlpreconveniencelibs " in
*" $deplib "*) ;;
*)
- valid_a_lib=no
+ valid_a_lib=false
case $deplibs_check_method in
match_pattern*)
set dummy $deplibs_check_method; shift
match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \
| $EGREP "$match_pattern_regex" > /dev/null; then
- valid_a_lib=yes
+ valid_a_lib=:
fi
;;
pass_all)
- valid_a_lib=yes
+ valid_a_lib=:
;;
esac
- if test "$valid_a_lib" != yes; then
+ if $valid_a_lib; then
+ echo
+ $ECHO "*** Warning: Linking the shared library $output against the"
+ $ECHO "*** static library $deplib is not portable!"
+ deplibs="$deplib $deplibs"
+ else
echo
$ECHO "*** Warning: Trying to link with static lib archive $deplib."
echo "*** I have the capability to make that library automatically link in when"
@@ -6330,18 +7794,13 @@ func_mode_link ()
echo "*** shared version of the library, which you do not appear to have"
echo "*** because the file extensions .$libext of this argument makes me believe"
echo "*** that it is just a static archive that I should not use here."
- else
- echo
- $ECHO "*** Warning: Linking the shared library $output against the"
- $ECHO "*** static library $deplib is not portable!"
- deplibs="$deplib $deplibs"
fi
;;
esac
continue
;;
prog)
- if test "$pass" != link; then
+ if test link != "$pass"; then
deplibs="$deplib $deplibs"
else
compile_deplibs="$deplib $compile_deplibs"
@@ -6352,10 +7811,10 @@ func_mode_link ()
esac # linkmode
;; # *.$libext
*.lo | *.$objext)
- if test "$pass" = conv; then
+ if test conv = "$pass"; then
deplibs="$deplib $deplibs"
- elif test "$linkmode" = prog; then
- if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then
+ elif test prog = "$linkmode"; then
+ if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then
# If there is no dlopen support or we're linking statically,
# we need to preload.
func_append newdlprefiles " $deplib"
@@ -6368,22 +7827,20 @@ func_mode_link ()
continue
;;
%DEPLIBS%)
- alldeplibs=yes
+ alldeplibs=:
continue
;;
esac # case $deplib
- if test "$found" = yes || test -f "$lib"; then :
- else
- func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'"
- fi
+ $found || test -f "$lib" \
+ || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'"
# Check to see that this really is a libtool archive.
func_lalib_unsafe_p "$lib" \
- || func_fatal_error "\`$lib' is not a valid libtool archive"
+ || func_fatal_error "'$lib' is not a valid libtool archive"
func_dirname "$lib" "" "."
- ladir="$func_dirname_result"
+ ladir=$func_dirname_result
dlname=
dlopen=
@@ -6413,30 +7870,30 @@ func_mode_link ()
done
fi
dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
- if test "$linkmode,$pass" = "lib,link" ||
- test "$linkmode,$pass" = "prog,scan" ||
- { test "$linkmode" != prog && test "$linkmode" != lib; }; then
+ if test lib,link = "$linkmode,$pass" ||
+ test prog,scan = "$linkmode,$pass" ||
+ { test prog != "$linkmode" && test lib != "$linkmode"; }; then
test -n "$dlopen" && func_append dlfiles " $dlopen"
test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen"
fi
- if test "$pass" = conv; then
+ if test conv = "$pass"; then
# Only check for convenience libraries
deplibs="$lib $deplibs"
if test -z "$libdir"; then
if test -z "$old_library"; then
- func_fatal_error "cannot find name of link library for \`$lib'"
+ func_fatal_error "cannot find name of link library for '$lib'"
fi
# It is a libtool convenience library, so add in its objects.
func_append convenience " $ladir/$objdir/$old_library"
func_append old_convenience " $ladir/$objdir/$old_library"
- elif test "$linkmode" != prog && test "$linkmode" != lib; then
- func_fatal_error "\`$lib' is not a convenience library"
+ elif test prog != "$linkmode" && test lib != "$linkmode"; then
+ func_fatal_error "'$lib' is not a convenience library"
fi
tmp_libs=
for deplib in $dependency_libs; do
deplibs="$deplib $deplibs"
- if $opt_preserve_dup_deps ; then
+ if $opt_preserve_dup_deps; then
case "$tmp_libs " in
*" $deplib "*) func_append specialdeplibs " $deplib" ;;
esac
@@ -6450,26 +7907,26 @@ func_mode_link ()
# Get the name of the library we link against.
linklib=
if test -n "$old_library" &&
- { test "$prefer_static_libs" = yes ||
- test "$prefer_static_libs,$installed" = "built,no"; }; then
+ { test yes = "$prefer_static_libs" ||
+ test built,no = "$prefer_static_libs,$installed"; }; then
linklib=$old_library
else
for l in $old_library $library_names; do
- linklib="$l"
+ linklib=$l
done
fi
if test -z "$linklib"; then
- func_fatal_error "cannot find name of link library for \`$lib'"
+ func_fatal_error "cannot find name of link library for '$lib'"
fi
# This library was specified with -dlopen.
- if test "$pass" = dlopen; then
- if test -z "$libdir"; then
- func_fatal_error "cannot -dlopen a convenience library: \`$lib'"
- fi
+ if test dlopen = "$pass"; then
+ test -z "$libdir" \
+ && func_fatal_error "cannot -dlopen a convenience library: '$lib'"
if test -z "$dlname" ||
- test "$dlopen_support" != yes ||
- test "$build_libtool_libs" = no; then
+ test yes != "$dlopen_support" ||
+ test no = "$build_libtool_libs"
+ then
# If there is no dlname, no dlopen support or we're linking
# statically, we need to preload. We also need to preload any
# dependent libraries so libltdl's deplib preloader doesn't
@@ -6483,40 +7940,40 @@ func_mode_link ()
# We need an absolute path.
case $ladir in
- [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;;
+ [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;;
*)
abs_ladir=`cd "$ladir" && pwd`
if test -z "$abs_ladir"; then
- func_warning "cannot determine absolute directory name of \`$ladir'"
+ func_warning "cannot determine absolute directory name of '$ladir'"
func_warning "passing it literally to the linker, although it might fail"
- abs_ladir="$ladir"
+ abs_ladir=$ladir
fi
;;
esac
func_basename "$lib"
- laname="$func_basename_result"
+ laname=$func_basename_result
# Find the relevant object directory and library name.
- if test "X$installed" = Xyes; then
+ if test yes = "$installed"; then
if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then
- func_warning "library \`$lib' was moved."
- dir="$ladir"
- absdir="$abs_ladir"
- libdir="$abs_ladir"
+ func_warning "library '$lib' was moved."
+ dir=$ladir
+ absdir=$abs_ladir
+ libdir=$abs_ladir
else
- dir="$lt_sysroot$libdir"
- absdir="$lt_sysroot$libdir"
+ dir=$lt_sysroot$libdir
+ absdir=$lt_sysroot$libdir
fi
- test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes
+ test yes = "$hardcode_automatic" && avoidtemprpath=yes
else
if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then
- dir="$ladir"
- absdir="$abs_ladir"
+ dir=$ladir
+ absdir=$abs_ladir
# Remove this search path later
func_append notinst_path " $abs_ladir"
else
- dir="$ladir/$objdir"
- absdir="$abs_ladir/$objdir"
+ dir=$ladir/$objdir
+ absdir=$abs_ladir/$objdir
# Remove this search path later
func_append notinst_path " $abs_ladir"
fi
@@ -6525,11 +7982,11 @@ func_mode_link ()
name=$func_stripname_result
# This library was specified with -dlpreopen.
- if test "$pass" = dlpreopen; then
- if test -z "$libdir" && test "$linkmode" = prog; then
- func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'"
+ if test dlpreopen = "$pass"; then
+ if test -z "$libdir" && test prog = "$linkmode"; then
+ func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'"
fi
- case "$host" in
+ case $host in
# special handling for platforms with PE-DLLs.
*cygwin* | *mingw* | *cegcc* )
# Linker will automatically link against shared library if both
@@ -6573,9 +8030,9 @@ func_mode_link ()
if test -z "$libdir"; then
# Link the convenience library
- if test "$linkmode" = lib; then
+ if test lib = "$linkmode"; then
deplibs="$dir/$old_library $deplibs"
- elif test "$linkmode,$pass" = "prog,link"; then
+ elif test prog,link = "$linkmode,$pass"; then
compile_deplibs="$dir/$old_library $compile_deplibs"
finalize_deplibs="$dir/$old_library $finalize_deplibs"
else
@@ -6585,14 +8042,14 @@ func_mode_link ()
fi
- if test "$linkmode" = prog && test "$pass" != link; then
+ if test prog = "$linkmode" && test link != "$pass"; then
func_append newlib_search_path " $ladir"
deplibs="$lib $deplibs"
- linkalldeplibs=no
- if test "$link_all_deplibs" != no || test -z "$library_names" ||
- test "$build_libtool_libs" = no; then
- linkalldeplibs=yes
+ linkalldeplibs=false
+ if test no != "$link_all_deplibs" || test -z "$library_names" ||
+ test no = "$build_libtool_libs"; then
+ linkalldeplibs=:
fi
tmp_libs=
@@ -6604,14 +8061,14 @@ func_mode_link ()
;;
esac
# Need to link against all dependency_libs?
- if test "$linkalldeplibs" = yes; then
+ if $linkalldeplibs; then
deplibs="$deplib $deplibs"
else
# Need to hardcode shared library paths
# or/and link against static libraries
newdependency_libs="$deplib $newdependency_libs"
fi
- if $opt_preserve_dup_deps ; then
+ if $opt_preserve_dup_deps; then
case "$tmp_libs " in
*" $deplib "*) func_append specialdeplibs " $deplib" ;;
esac
@@ -6621,15 +8078,15 @@ func_mode_link ()
continue
fi # $linkmode = prog...
- if test "$linkmode,$pass" = "prog,link"; then
+ if test prog,link = "$linkmode,$pass"; then
if test -n "$library_names" &&
- { { test "$prefer_static_libs" = no ||
- test "$prefer_static_libs,$installed" = "built,yes"; } ||
+ { { test no = "$prefer_static_libs" ||
+ test built,yes = "$prefer_static_libs,$installed"; } ||
test -z "$old_library"; }; then
# We need to hardcode the library path
- if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then
+ if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then
# Make sure the rpath contains only unique directories.
- case "$temp_rpath:" in
+ case $temp_rpath: in
*"$absdir:"*) ;;
*) func_append temp_rpath "$absdir:" ;;
esac
@@ -6658,9 +8115,9 @@ func_mode_link ()
esac
fi # $linkmode,$pass = prog,link...
- if test "$alldeplibs" = yes &&
- { test "$deplibs_check_method" = pass_all ||
- { test "$build_libtool_libs" = yes &&
+ if $alldeplibs &&
+ { test pass_all = "$deplibs_check_method" ||
+ { test yes = "$build_libtool_libs" &&
test -n "$library_names"; }; }; then
# We only need to search for static libraries
continue
@@ -6669,19 +8126,19 @@ func_mode_link ()
link_static=no # Whether the deplib will be linked statically
use_static_libs=$prefer_static_libs
- if test "$use_static_libs" = built && test "$installed" = yes; then
+ if test built = "$use_static_libs" && test yes = "$installed"; then
use_static_libs=no
fi
if test -n "$library_names" &&
- { test "$use_static_libs" = no || test -z "$old_library"; }; then
+ { test no = "$use_static_libs" || test -z "$old_library"; }; then
case $host in
- *cygwin* | *mingw* | *cegcc*)
+ *cygwin* | *mingw* | *cegcc* | *os2*)
# No point in relinking DLLs because paths are not encoded
func_append notinst_deplibs " $lib"
need_relink=no
;;
*)
- if test "$installed" = no; then
+ if test no = "$installed"; then
func_append notinst_deplibs " $lib"
need_relink=yes
fi
@@ -6691,24 +8148,24 @@ func_mode_link ()
# Warn about portability, can't link against -module's on some
# systems (darwin). Don't bleat about dlopened modules though!
- dlopenmodule=""
+ dlopenmodule=
for dlpremoduletest in $dlprefiles; do
if test "X$dlpremoduletest" = "X$lib"; then
- dlopenmodule="$dlpremoduletest"
+ dlopenmodule=$dlpremoduletest
break
fi
done
- if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then
+ if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then
echo
- if test "$linkmode" = prog; then
+ if test prog = "$linkmode"; then
$ECHO "*** Warning: Linking the executable $output against the loadable module"
else
$ECHO "*** Warning: Linking the shared library $output against the loadable module"
fi
$ECHO "*** $linklib is not portable!"
fi
- if test "$linkmode" = lib &&
- test "$hardcode_into_libs" = yes; then
+ if test lib = "$linkmode" &&
+ test yes = "$hardcode_into_libs"; then
# Hardcode the library path.
# Skip directories that are in the system default run-time
# search path.
@@ -6736,43 +8193,43 @@ func_mode_link ()
# figure out the soname
set dummy $library_names
shift
- realname="$1"
+ realname=$1
shift
libname=`eval "\\$ECHO \"$libname_spec\""`
# use dlname if we got it. it's perfectly good, no?
if test -n "$dlname"; then
- soname="$dlname"
+ soname=$dlname
elif test -n "$soname_spec"; then
# bleh windows
case $host in
- *cygwin* | mingw* | *cegcc*)
+ *cygwin* | mingw* | *cegcc* | *os2*)
func_arith $current - $age
major=$func_arith_result
- versuffix="-$major"
+ versuffix=-$major
;;
esac
eval soname=\"$soname_spec\"
else
- soname="$realname"
+ soname=$realname
fi
# Make a new name for the extract_expsyms_cmds to use
- soroot="$soname"
+ soroot=$soname
func_basename "$soroot"
- soname="$func_basename_result"
+ soname=$func_basename_result
func_stripname 'lib' '.dll' "$soname"
newlib=libimp-$func_stripname_result.a
# If the library has no export list, then create one now
if test -f "$output_objdir/$soname-def"; then :
else
- func_verbose "extracting exported symbol list from \`$soname'"
+ func_verbose "extracting exported symbol list from '$soname'"
func_execute_cmds "$extract_expsyms_cmds" 'exit $?'
fi
# Create $newlib
if test -f "$output_objdir/$newlib"; then :; else
- func_verbose "generating import library for \`$soname'"
+ func_verbose "generating import library for '$soname'"
func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?'
fi
# make sure the library variables are pointing to the new library
@@ -6780,58 +8237,58 @@ func_mode_link ()
linklib=$newlib
fi # test -n "$old_archive_from_expsyms_cmds"
- if test "$linkmode" = prog || test "$opt_mode" != relink; then
+ if test prog = "$linkmode" || test relink != "$opt_mode"; then
add_shlibpath=
add_dir=
add=
lib_linked=yes
case $hardcode_action in
immediate | unsupported)
- if test "$hardcode_direct" = no; then
- add="$dir/$linklib"
+ if test no = "$hardcode_direct"; then
+ add=$dir/$linklib
case $host in
- *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;;
- *-*-sysv4*uw2*) add_dir="-L$dir" ;;
+ *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
+ *-*-sysv4*uw2*) add_dir=-L$dir ;;
*-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
- *-*-unixware7*) add_dir="-L$dir" ;;
+ *-*-unixware7*) add_dir=-L$dir ;;
*-*-darwin* )
- # if the lib is a (non-dlopened) module then we can not
+ # if the lib is a (non-dlopened) module then we cannot
# link against it, someone is ignoring the earlier warnings
if /usr/bin/file -L $add 2> /dev/null |
- $GREP ": [^:]* bundle" >/dev/null ; then
+ $GREP ": [^:]* bundle" >/dev/null; then
if test "X$dlopenmodule" != "X$lib"; then
$ECHO "*** Warning: lib $linklib is a module, not a shared library"
- if test -z "$old_library" ; then
+ if test -z "$old_library"; then
echo
echo "*** And there doesn't seem to be a static archive available"
echo "*** The link will probably fail, sorry"
else
- add="$dir/$old_library"
+ add=$dir/$old_library
fi
elif test -n "$old_library"; then
- add="$dir/$old_library"
+ add=$dir/$old_library
fi
fi
esac
- elif test "$hardcode_minus_L" = no; then
+ elif test no = "$hardcode_minus_L"; then
case $host in
- *-*-sunos*) add_shlibpath="$dir" ;;
+ *-*-sunos*) add_shlibpath=$dir ;;
esac
- add_dir="-L$dir"
- add="-l$name"
- elif test "$hardcode_shlibpath_var" = no; then
- add_shlibpath="$dir"
- add="-l$name"
+ add_dir=-L$dir
+ add=-l$name
+ elif test no = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
else
lib_linked=no
fi
;;
relink)
- if test "$hardcode_direct" = yes &&
- test "$hardcode_direct_absolute" = no; then
- add="$dir/$linklib"
- elif test "$hardcode_minus_L" = yes; then
- add_dir="-L$dir"
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$dir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$absdir
# Try looking first in the location we're being installed to.
if test -n "$inst_prefix_dir"; then
case $libdir in
@@ -6840,10 +8297,10 @@ func_mode_link ()
;;
esac
fi
- add="-l$name"
- elif test "$hardcode_shlibpath_var" = yes; then
- add_shlibpath="$dir"
- add="-l$name"
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
+ add_shlibpath=$dir
+ add=-l$name
else
lib_linked=no
fi
@@ -6851,7 +8308,7 @@ func_mode_link ()
*) lib_linked=no ;;
esac
- if test "$lib_linked" != yes; then
+ if test yes != "$lib_linked"; then
func_fatal_configuration "unsupported hardcode properties"
fi
@@ -6861,15 +8318,15 @@ func_mode_link ()
*) func_append compile_shlibpath "$add_shlibpath:" ;;
esac
fi
- if test "$linkmode" = prog; then
+ if test prog = "$linkmode"; then
test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs"
test -n "$add" && compile_deplibs="$add $compile_deplibs"
else
test -n "$add_dir" && deplibs="$add_dir $deplibs"
test -n "$add" && deplibs="$add $deplibs"
- if test "$hardcode_direct" != yes &&
- test "$hardcode_minus_L" != yes &&
- test "$hardcode_shlibpath_var" = yes; then
+ if test yes != "$hardcode_direct" &&
+ test yes != "$hardcode_minus_L" &&
+ test yes = "$hardcode_shlibpath_var"; then
case :$finalize_shlibpath: in
*":$libdir:"*) ;;
*) func_append finalize_shlibpath "$libdir:" ;;
@@ -6878,33 +8335,33 @@ func_mode_link ()
fi
fi
- if test "$linkmode" = prog || test "$opt_mode" = relink; then
+ if test prog = "$linkmode" || test relink = "$opt_mode"; then
add_shlibpath=
add_dir=
add=
# Finalize command for both is simple: just hardcode it.
- if test "$hardcode_direct" = yes &&
- test "$hardcode_direct_absolute" = no; then
- add="$libdir/$linklib"
- elif test "$hardcode_minus_L" = yes; then
- add_dir="-L$libdir"
- add="-l$name"
- elif test "$hardcode_shlibpath_var" = yes; then
+ if test yes = "$hardcode_direct" &&
+ test no = "$hardcode_direct_absolute"; then
+ add=$libdir/$linklib
+ elif test yes = "$hardcode_minus_L"; then
+ add_dir=-L$libdir
+ add=-l$name
+ elif test yes = "$hardcode_shlibpath_var"; then
case :$finalize_shlibpath: in
*":$libdir:"*) ;;
*) func_append finalize_shlibpath "$libdir:" ;;
esac
- add="-l$name"
- elif test "$hardcode_automatic" = yes; then
+ add=-l$name
+ elif test yes = "$hardcode_automatic"; then
if test -n "$inst_prefix_dir" &&
- test -f "$inst_prefix_dir$libdir/$linklib" ; then
- add="$inst_prefix_dir$libdir/$linklib"
+ test -f "$inst_prefix_dir$libdir/$linklib"; then
+ add=$inst_prefix_dir$libdir/$linklib
else
- add="$libdir/$linklib"
+ add=$libdir/$linklib
fi
else
# We cannot seem to hardcode it, guess we'll fake it.
- add_dir="-L$libdir"
+ add_dir=-L$libdir
# Try looking first in the location we're being installed to.
if test -n "$inst_prefix_dir"; then
case $libdir in
@@ -6913,10 +8370,10 @@ func_mode_link ()
;;
esac
fi
- add="-l$name"
+ add=-l$name
fi
- if test "$linkmode" = prog; then
+ if test prog = "$linkmode"; then
test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs"
test -n "$add" && finalize_deplibs="$add $finalize_deplibs"
else
@@ -6924,43 +8381,43 @@ func_mode_link ()
test -n "$add" && deplibs="$add $deplibs"
fi
fi
- elif test "$linkmode" = prog; then
+ elif test prog = "$linkmode"; then
# Here we assume that one of hardcode_direct or hardcode_minus_L
# is not unsupported. This is valid on all known static and
# shared platforms.
- if test "$hardcode_direct" != unsupported; then
- test -n "$old_library" && linklib="$old_library"
+ if test unsupported != "$hardcode_direct"; then
+ test -n "$old_library" && linklib=$old_library
compile_deplibs="$dir/$linklib $compile_deplibs"
finalize_deplibs="$dir/$linklib $finalize_deplibs"
else
compile_deplibs="-l$name -L$dir $compile_deplibs"
finalize_deplibs="-l$name -L$dir $finalize_deplibs"
fi
- elif test "$build_libtool_libs" = yes; then
+ elif test yes = "$build_libtool_libs"; then
# Not a shared library
- if test "$deplibs_check_method" != pass_all; then
+ if test pass_all != "$deplibs_check_method"; then
# We're trying link a shared library against a static one
# but the system doesn't support it.
# Just print a warning and add the library to dependency_libs so
# that the program can be linked against the static library.
echo
- $ECHO "*** Warning: This system can not link to static lib archive $lib."
+ $ECHO "*** Warning: This system cannot link to static lib archive $lib."
echo "*** I have the capability to make that library automatically link in when"
echo "*** you link to this library. But I can only do this if you have a"
echo "*** shared version of the library, which you do not appear to have."
- if test "$module" = yes; then
+ if test yes = "$module"; then
echo "*** But as you try to build a module library, libtool will still create "
echo "*** a static module, that should work as long as the dlopening application"
echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
if test -z "$global_symbol_pipe"; then
echo
echo "*** However, this would only work if libtool was able to extract symbol"
- echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
echo "*** not find such a program. So, this module is probably useless."
- echo "*** \`nm' from GNU binutils and a full rebuild may help."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
fi
- if test "$build_old_libs" = no; then
+ if test no = "$build_old_libs"; then
build_libtool_libs=module
build_old_libs=yes
else
@@ -6973,11 +8430,11 @@ func_mode_link ()
fi
fi # link shared/static library?
- if test "$linkmode" = lib; then
+ if test lib = "$linkmode"; then
if test -n "$dependency_libs" &&
- { test "$hardcode_into_libs" != yes ||
- test "$build_old_libs" = yes ||
- test "$link_static" = yes; }; then
+ { test yes != "$hardcode_into_libs" ||
+ test yes = "$build_old_libs" ||
+ test yes = "$link_static"; }; then
# Extract -R from dependency_libs
temp_deplibs=
for libdir in $dependency_libs; do
@@ -6991,12 +8448,12 @@ func_mode_link ()
*) func_append temp_deplibs " $libdir";;
esac
done
- dependency_libs="$temp_deplibs"
+ dependency_libs=$temp_deplibs
fi
func_append newlib_search_path " $absdir"
# Link against this library
- test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
+ test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs"
# ... and its dependency_libs
tmp_libs=
for deplib in $dependency_libs; do
@@ -7006,7 +8463,7 @@ func_mode_link ()
func_resolve_sysroot "$func_stripname_result";;
*) func_resolve_sysroot "$deplib" ;;
esac
- if $opt_preserve_dup_deps ; then
+ if $opt_preserve_dup_deps; then
case "$tmp_libs " in
*" $func_resolve_sysroot_result "*)
func_append specialdeplibs " $func_resolve_sysroot_result" ;;
@@ -7015,12 +8472,12 @@ func_mode_link ()
func_append tmp_libs " $func_resolve_sysroot_result"
done
- if test "$link_all_deplibs" != no; then
+ if test no != "$link_all_deplibs"; then
# Add the search paths of all dependency libraries
for deplib in $dependency_libs; do
path=
case $deplib in
- -L*) path="$deplib" ;;
+ -L*) path=$deplib ;;
*.la)
func_resolve_sysroot "$deplib"
deplib=$func_resolve_sysroot_result
@@ -7028,12 +8485,12 @@ func_mode_link ()
dir=$func_dirname_result
# We need an absolute path.
case $dir in
- [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;;
+ [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;;
*)
absdir=`cd "$dir" && pwd`
if test -z "$absdir"; then
- func_warning "cannot determine absolute directory name of \`$dir'"
- absdir="$dir"
+ func_warning "cannot determine absolute directory name of '$dir'"
+ absdir=$dir
fi
;;
esac
@@ -7041,35 +8498,35 @@ func_mode_link ()
case $host in
*-*-darwin*)
depdepl=
- eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
- if test -n "$deplibrary_names" ; then
- for tmp in $deplibrary_names ; do
+ eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib`
+ if test -n "$deplibrary_names"; then
+ for tmp in $deplibrary_names; do
depdepl=$tmp
done
- if test -f "$absdir/$objdir/$depdepl" ; then
- depdepl="$absdir/$objdir/$depdepl"
- darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ if test -f "$absdir/$objdir/$depdepl"; then
+ depdepl=$absdir/$objdir/$depdepl
+ darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
if test -z "$darwin_install_name"; then
- darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
+ darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'`
fi
- func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}"
- func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}"
+ func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl"
+ func_append linker_flags " -dylib_file $darwin_install_name:$depdepl"
path=
fi
fi
;;
*)
- path="-L$absdir/$objdir"
+ path=-L$absdir/$objdir
;;
esac
else
- eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
test -z "$libdir" && \
- func_fatal_error "\`$deplib' is not a valid libtool archive"
+ func_fatal_error "'$deplib' is not a valid libtool archive"
test "$absdir" != "$libdir" && \
- func_warning "\`$deplib' seems to be moved"
+ func_warning "'$deplib' seems to be moved"
- path="-L$absdir"
+ path=-L$absdir
fi
;;
esac
@@ -7081,23 +8538,23 @@ func_mode_link ()
fi # link_all_deplibs != no
fi # linkmode = lib
done # for deplib in $libs
- if test "$pass" = link; then
- if test "$linkmode" = "prog"; then
+ if test link = "$pass"; then
+ if test prog = "$linkmode"; then
compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs"
else
compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'`
fi
fi
- dependency_libs="$newdependency_libs"
- if test "$pass" = dlpreopen; then
+ dependency_libs=$newdependency_libs
+ if test dlpreopen = "$pass"; then
# Link the dlpreopened libraries before other libraries
for deplib in $save_deplibs; do
deplibs="$deplib $deplibs"
done
fi
- if test "$pass" != dlopen; then
- if test "$pass" != conv; then
+ if test dlopen != "$pass"; then
+ test conv = "$pass" || {
# Make sure lib_search_path contains only unique directories.
lib_search_path=
for dir in $newlib_search_path; do
@@ -7107,12 +8564,12 @@ func_mode_link ()
esac
done
newlib_search_path=
- fi
+ }
- if test "$linkmode,$pass" != "prog,link"; then
- vars="deplibs"
- else
+ if test prog,link = "$linkmode,$pass"; then
vars="compile_deplibs finalize_deplibs"
+ else
+ vars=deplibs
fi
for var in $vars dependency_libs; do
# Add libraries to $var in reverse order
@@ -7170,62 +8627,93 @@ func_mode_link ()
eval $var=\"$tmp_libs\"
done # for var
fi
+
+ # Add Sun CC postdeps if required:
+ test CXX = "$tagname" && {
+ case $host_os in
+ linux*)
+ case `$CC -V 2>&1 | sed 5q` in
+ *Sun\ C*) # Sun C++ 5.9
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+
+ solaris*)
+ func_cc_basename "$CC"
+ case $func_cc_basename_result in
+ CC* | sunCC*)
+ func_suncc_cstd_abi
+
+ if test no != "$suncc_use_cstd_abi"; then
+ func_append postdeps ' -library=Cstd -library=Crun'
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ }
+
# Last step: remove runtime libs from dependency_libs
# (they stay in deplibs)
tmp_libs=
- for i in $dependency_libs ; do
+ for i in $dependency_libs; do
case " $predeps $postdeps $compiler_lib_search_path " in
*" $i "*)
- i=""
+ i=
;;
esac
- if test -n "$i" ; then
+ if test -n "$i"; then
func_append tmp_libs " $i"
fi
done
dependency_libs=$tmp_libs
done # for pass
- if test "$linkmode" = prog; then
- dlfiles="$newdlfiles"
+ if test prog = "$linkmode"; then
+ dlfiles=$newdlfiles
fi
- if test "$linkmode" = prog || test "$linkmode" = lib; then
- dlprefiles="$newdlprefiles"
+ if test prog = "$linkmode" || test lib = "$linkmode"; then
+ dlprefiles=$newdlprefiles
fi
case $linkmode in
oldlib)
- if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
- func_warning "\`-dlopen' is ignored for archives"
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for archives"
fi
case " $deplibs" in
*\ -l* | *\ -L*)
- func_warning "\`-l' and \`-L' are ignored for archives" ;;
+ func_warning "'-l' and '-L' are ignored for archives" ;;
esac
test -n "$rpath" && \
- func_warning "\`-rpath' is ignored for archives"
+ func_warning "'-rpath' is ignored for archives"
test -n "$xrpath" && \
- func_warning "\`-R' is ignored for archives"
+ func_warning "'-R' is ignored for archives"
test -n "$vinfo" && \
- func_warning "\`-version-info/-version-number' is ignored for archives"
+ func_warning "'-version-info/-version-number' is ignored for archives"
test -n "$release" && \
- func_warning "\`-release' is ignored for archives"
+ func_warning "'-release' is ignored for archives"
test -n "$export_symbols$export_symbols_regex" && \
- func_warning "\`-export-symbols' is ignored for archives"
+ func_warning "'-export-symbols' is ignored for archives"
# Now set the variables for building old libraries.
build_libtool_libs=no
- oldlibs="$output"
+ oldlibs=$output
func_append objs "$old_deplibs"
;;
lib)
- # Make sure we only generate libraries of the form `libNAME.la'.
+ # Make sure we only generate libraries of the form 'libNAME.la'.
case $outputname in
lib*)
func_stripname 'lib' '.la' "$outputname"
@@ -7234,10 +8722,10 @@ func_mode_link ()
eval libname=\"$libname_spec\"
;;
*)
- test "$module" = no && \
- func_fatal_help "libtool library \`$output' must begin with \`lib'"
+ test no = "$module" \
+ && func_fatal_help "libtool library '$output' must begin with 'lib'"
- if test "$need_lib_prefix" != no; then
+ if test no != "$need_lib_prefix"; then
# Add the "lib" prefix for modules if required
func_stripname '' '.la' "$outputname"
name=$func_stripname_result
@@ -7251,8 +8739,8 @@ func_mode_link ()
esac
if test -n "$objs"; then
- if test "$deplibs_check_method" != pass_all; then
- func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs"
+ if test pass_all != "$deplibs_check_method"; then
+ func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs"
else
echo
$ECHO "*** Warning: Linking the shared library $output against the non-libtool"
@@ -7261,21 +8749,21 @@ func_mode_link ()
fi
fi
- test "$dlself" != no && \
- func_warning "\`-dlopen self' is ignored for libtool libraries"
+ test no = "$dlself" \
+ || func_warning "'-dlopen self' is ignored for libtool libraries"
set dummy $rpath
shift
- test "$#" -gt 1 && \
- func_warning "ignoring multiple \`-rpath's for a libtool library"
+ test 1 -lt "$#" \
+ && func_warning "ignoring multiple '-rpath's for a libtool library"
- install_libdir="$1"
+ install_libdir=$1
oldlibs=
if test -z "$rpath"; then
- if test "$build_libtool_libs" = yes; then
+ if test yes = "$build_libtool_libs"; then
# Building a libtool convenience library.
- # Some compilers have problems with a `.al' extension so
+ # Some compilers have problems with a '.al' extension so
# convenience libraries should have the same extension an
# archive normally would.
oldlibs="$output_objdir/$libname.$libext $oldlibs"
@@ -7284,20 +8772,20 @@ func_mode_link ()
fi
test -n "$vinfo" && \
- func_warning "\`-version-info/-version-number' is ignored for convenience libraries"
+ func_warning "'-version-info/-version-number' is ignored for convenience libraries"
test -n "$release" && \
- func_warning "\`-release' is ignored for convenience libraries"
+ func_warning "'-release' is ignored for convenience libraries"
else
# Parse the version information argument.
- save_ifs="$IFS"; IFS=':'
+ save_ifs=$IFS; IFS=:
set dummy $vinfo 0 0 0
shift
- IFS="$save_ifs"
+ IFS=$save_ifs
test -n "$7" && \
- func_fatal_help "too many parameters to \`-version-info'"
+ func_fatal_help "too many parameters to '-version-info'"
# convert absolute version numbers to libtool ages
# this retains compatibility with .la files and attempts
@@ -7305,41 +8793,42 @@ func_mode_link ()
case $vinfo_number in
yes)
- number_major="$1"
- number_minor="$2"
- number_revision="$3"
+ number_major=$1
+ number_minor=$2
+ number_revision=$3
#
# There are really only two kinds -- those that
# use the current revision as the major version
# and those that subtract age and use age as
# a minor version. But, then there is irix
- # which has an extra 1 added just for fun
+ # that has an extra 1 added just for fun
#
case $version_type in
- darwin|linux|osf|windows|none)
+ # correct linux to gnu/linux during the next big refactor
+ darwin|freebsd-elf|linux|osf|windows|none)
func_arith $number_major + $number_minor
current=$func_arith_result
- age="$number_minor"
- revision="$number_revision"
+ age=$number_minor
+ revision=$number_revision
;;
- freebsd-aout|freebsd-elf|qnx|sunos)
- current="$number_major"
- revision="$number_minor"
- age="0"
+ freebsd-aout|qnx|sunos)
+ current=$number_major
+ revision=$number_minor
+ age=0
;;
irix|nonstopux)
func_arith $number_major + $number_minor
current=$func_arith_result
- age="$number_minor"
- revision="$number_minor"
+ age=$number_minor
+ revision=$number_minor
lt_irix_increment=no
;;
esac
;;
no)
- current="$1"
- revision="$2"
- age="$3"
+ current=$1
+ revision=$2
+ age=$3
;;
esac
@@ -7347,30 +8836,30 @@ func_mode_link ()
case $current in
0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
*)
- func_error "CURRENT \`$current' must be a nonnegative integer"
- func_fatal_error "\`$vinfo' is not valid version information"
+ func_error "CURRENT '$current' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
;;
esac
case $revision in
0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
*)
- func_error "REVISION \`$revision' must be a nonnegative integer"
- func_fatal_error "\`$vinfo' is not valid version information"
+ func_error "REVISION '$revision' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
;;
esac
case $age in
0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;;
*)
- func_error "AGE \`$age' must be a nonnegative integer"
- func_fatal_error "\`$vinfo' is not valid version information"
+ func_error "AGE '$age' must be a nonnegative integer"
+ func_fatal_error "'$vinfo' is not valid version information"
;;
esac
if test "$age" -gt "$current"; then
- func_error "AGE \`$age' is greater than the current interface number \`$current'"
- func_fatal_error "\`$vinfo' is not valid version information"
+ func_error "AGE '$age' is greater than the current interface number '$current'"
+ func_fatal_error "'$vinfo' is not valid version information"
fi
# Calculate the version variables.
@@ -7385,26 +8874,36 @@ func_mode_link ()
# verstring for coding it into the library header
func_arith $current - $age
major=.$func_arith_result
- versuffix="$major.$age.$revision"
+ versuffix=$major.$age.$revision
# Darwin ld doesn't like 0 for these options...
func_arith $current + 1
minor_current=$func_arith_result
- xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision"
+ xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ # On Darwin other compilers
+ case $CC in
+ nagfor*)
+ verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision"
+ ;;
+ *)
+ verstring="-compatibility_version $minor_current -current_version $minor_current.$revision"
+ ;;
+ esac
;;
freebsd-aout)
- major=".$current"
- versuffix=".$current.$revision";
+ major=.$current
+ versuffix=.$current.$revision
;;
freebsd-elf)
- major=".$current"
- versuffix=".$current"
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
;;
irix | nonstopux)
- if test "X$lt_irix_increment" = "Xno"; then
+ if test no = "$lt_irix_increment"; then
func_arith $current - $age
else
func_arith $current - $age + 1
@@ -7415,69 +8914,74 @@ func_mode_link ()
nonstopux) verstring_prefix=nonstopux ;;
*) verstring_prefix=sgi ;;
esac
- verstring="$verstring_prefix$major.$revision"
+ verstring=$verstring_prefix$major.$revision
# Add in all the interfaces that we are compatible with.
loop=$revision
- while test "$loop" -ne 0; do
+ while test 0 -ne "$loop"; do
func_arith $revision - $loop
iface=$func_arith_result
func_arith $loop - 1
loop=$func_arith_result
- verstring="$verstring_prefix$major.$iface:$verstring"
+ verstring=$verstring_prefix$major.$iface:$verstring
done
- # Before this point, $major must not contain `.'.
+ # Before this point, $major must not contain '.'.
major=.$major
- versuffix="$major.$revision"
+ versuffix=$major.$revision
;;
- linux)
+ linux) # correct to gnu/linux during the next big refactor
func_arith $current - $age
major=.$func_arith_result
- versuffix="$major.$age.$revision"
+ versuffix=$major.$age.$revision
;;
osf)
func_arith $current - $age
major=.$func_arith_result
- versuffix=".$current.$age.$revision"
- verstring="$current.$age.$revision"
+ versuffix=.$current.$age.$revision
+ verstring=$current.$age.$revision
# Add in all the interfaces that we are compatible with.
loop=$age
- while test "$loop" -ne 0; do
+ while test 0 -ne "$loop"; do
func_arith $current - $loop
iface=$func_arith_result
func_arith $loop - 1
loop=$func_arith_result
- verstring="$verstring:${iface}.0"
+ verstring=$verstring:$iface.0
done
# Make executables depend on our current version.
- func_append verstring ":${current}.0"
+ func_append verstring ":$current.0"
;;
qnx)
- major=".$current"
- versuffix=".$current"
+ major=.$current
+ versuffix=.$current
+ ;;
+
+ sco)
+ major=.$current
+ versuffix=.$current
;;
sunos)
- major=".$current"
- versuffix=".$current.$revision"
+ major=.$current
+ versuffix=.$current.$revision
;;
windows)
# Use '-' rather than '.', since we only want one
- # extension on DOS 8.3 filesystems.
+ # extension on DOS 8.3 file systems.
func_arith $current - $age
major=$func_arith_result
- versuffix="-$major"
+ versuffix=-$major
;;
*)
- func_fatal_configuration "unknown library version type \`$version_type'"
+ func_fatal_configuration "unknown library version type '$version_type'"
;;
esac
@@ -7491,42 +8995,45 @@ func_mode_link ()
verstring=
;;
*)
- verstring="0.0"
+ verstring=0.0
;;
esac
- if test "$need_version" = no; then
+ if test no = "$need_version"; then
versuffix=
else
- versuffix=".0.0"
+ versuffix=.0.0
fi
fi
# Remove version info from name if versioning should be avoided
- if test "$avoid_version" = yes && test "$need_version" = no; then
+ if test yes,no = "$avoid_version,$need_version"; then
major=
versuffix=
- verstring=""
+ verstring=
fi
# Check to see if the archive will have undefined symbols.
- if test "$allow_undefined" = yes; then
- if test "$allow_undefined_flag" = unsupported; then
- func_warning "undefined symbols not allowed in $host shared libraries"
- build_libtool_libs=no
- build_old_libs=yes
+ if test yes = "$allow_undefined"; then
+ if test unsupported = "$allow_undefined_flag"; then
+ if test yes = "$build_old_libs"; then
+ func_warning "undefined symbols not allowed in $host shared libraries; building static only"
+ build_libtool_libs=no
+ else
+ func_fatal_error "can't build $host shared library unless -no-undefined is specified"
+ fi
fi
else
# Don't allow undefined symbols.
- allow_undefined_flag="$no_undefined_flag"
+ allow_undefined_flag=$no_undefined_flag
fi
fi
- func_generate_dlsyms "$libname" "$libname" "yes"
+ func_generate_dlsyms "$libname" "$libname" :
func_append libobjs " $symfileobj"
- test "X$libobjs" = "X " && libobjs=
+ test " " = "$libobjs" && libobjs=
- if test "$opt_mode" != relink; then
+ if test relink != "$opt_mode"; then
# Remove our outputs, but don't remove object files since they
# may have been created when compiling PIC objects.
removelist=
@@ -7535,8 +9042,8 @@ func_mode_link ()
case $p in
*.$objext | *.gcno)
;;
- $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*)
- if test "X$precious_files_regex" != "X"; then
+ $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*)
+ if test -n "$precious_files_regex"; then
if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1
then
continue
@@ -7552,11 +9059,11 @@ func_mode_link ()
fi
# Now set the variables for building old libraries.
- if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then
+ if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then
func_append oldlibs " $output_objdir/$libname.$libext"
# Transform .lo files to .o files.
- oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP`
+ oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP`
fi
# Eliminate all temporary directories.
@@ -7577,13 +9084,13 @@ func_mode_link ()
*) func_append finalize_rpath " $libdir" ;;
esac
done
- if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then
+ if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then
dependency_libs="$temp_xrpath $dependency_libs"
fi
fi
# Make sure dlfiles contains only unique files that won't be dlpreopened
- old_dlfiles="$dlfiles"
+ old_dlfiles=$dlfiles
dlfiles=
for lib in $old_dlfiles; do
case " $dlprefiles $dlfiles " in
@@ -7593,7 +9100,7 @@ func_mode_link ()
done
# Make sure dlprefiles contains only unique files
- old_dlprefiles="$dlprefiles"
+ old_dlprefiles=$dlprefiles
dlprefiles=
for lib in $old_dlprefiles; do
case "$dlprefiles " in
@@ -7602,7 +9109,7 @@ func_mode_link ()
esac
done
- if test "$build_libtool_libs" = yes; then
+ if test yes = "$build_libtool_libs"; then
if test -n "$rpath"; then
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
@@ -7626,7 +9133,7 @@ func_mode_link ()
;;
*)
# Add libc to deplibs on all other systems if necessary.
- if test "$build_libtool_need_lc" = "yes"; then
+ if test yes = "$build_libtool_need_lc"; then
func_append deplibs " -lc"
fi
;;
@@ -7642,9 +9149,9 @@ func_mode_link ()
# I'm not sure if I'm treating the release correctly. I think
# release should show up in the -l (ie -lgmp5) so we don't want to
# add it in twice. Is that correct?
- release=""
- versuffix=""
- major=""
+ release=
+ versuffix=
+ major=
newdeplibs=
droppeddeps=no
case $deplibs_check_method in
@@ -7673,20 +9180,20 @@ EOF
-l*)
func_stripname -l '' "$i"
name=$func_stripname_result
- if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
case " $predeps $postdeps " in
*" $i "*)
func_append newdeplibs " $i"
- i=""
+ i=
;;
esac
fi
- if test -n "$i" ; then
+ if test -n "$i"; then
libname=`eval "\\$ECHO \"$libname_spec\""`
deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
set dummy $deplib_matches; shift
deplib_match=$1
- if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
func_append newdeplibs " $i"
else
droppeddeps=yes
@@ -7716,20 +9223,20 @@ EOF
$opt_dry_run || $RM conftest
if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
ldd_output=`ldd conftest`
- if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
case " $predeps $postdeps " in
*" $i "*)
func_append newdeplibs " $i"
- i=""
+ i=
;;
esac
fi
- if test -n "$i" ; then
+ if test -n "$i"; then
libname=`eval "\\$ECHO \"$libname_spec\""`
deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
set dummy $deplib_matches; shift
deplib_match=$1
- if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then
+ if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
func_append newdeplibs " $i"
else
droppeddeps=yes
@@ -7766,24 +9273,24 @@ EOF
-l*)
func_stripname -l '' "$a_deplib"
name=$func_stripname_result
- if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
case " $predeps $postdeps " in
*" $a_deplib "*)
func_append newdeplibs " $a_deplib"
- a_deplib=""
+ a_deplib=
;;
esac
fi
- if test -n "$a_deplib" ; then
+ if test -n "$a_deplib"; then
libname=`eval "\\$ECHO \"$libname_spec\""`
if test -n "$file_magic_glob"; then
libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob`
else
libnameglob=$libname
fi
- test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob`
+ test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob`
for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
- if test "$want_nocaseglob" = yes; then
+ if test yes = "$want_nocaseglob"; then
shopt -s nocaseglob
potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null`
$nocaseglob
@@ -7801,25 +9308,25 @@ EOF
# We might still enter an endless loop, since a link
# loop can be closed while we follow links,
# but so what?
- potlib="$potent_lib"
+ potlib=$potent_lib
while test -h "$potlib" 2>/dev/null; do
- potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'`
+ potliblink=`ls -ld $potlib | $SED 's/.* -> //'`
case $potliblink in
- [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";;
- *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";;
+ [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;;
+ *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";;
esac
done
if eval $file_magic_cmd \"\$potlib\" 2>/dev/null |
$SED -e 10q |
$EGREP "$file_magic_regex" > /dev/null; then
func_append newdeplibs " $a_deplib"
- a_deplib=""
+ a_deplib=
break 2
fi
done
done
fi
- if test -n "$a_deplib" ; then
+ if test -n "$a_deplib"; then
droppeddeps=yes
echo
$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
@@ -7827,7 +9334,7 @@ EOF
echo "*** you link to this library. But I can only do this if you have a"
echo "*** shared version of the library, which you do not appear to have"
echo "*** because I did check the linker path looking for a file starting"
- if test -z "$potlib" ; then
+ if test -z "$potlib"; then
$ECHO "*** with $libname but no candidates were found. (...for file magic test)"
else
$ECHO "*** with $libname and none of the candidates passed a file format test"
@@ -7850,30 +9357,30 @@ EOF
-l*)
func_stripname -l '' "$a_deplib"
name=$func_stripname_result
- if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
case " $predeps $postdeps " in
*" $a_deplib "*)
func_append newdeplibs " $a_deplib"
- a_deplib=""
+ a_deplib=
;;
esac
fi
- if test -n "$a_deplib" ; then
+ if test -n "$a_deplib"; then
libname=`eval "\\$ECHO \"$libname_spec\""`
for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do
potential_libs=`ls $i/$libname[.-]* 2>/dev/null`
for potent_lib in $potential_libs; do
- potlib="$potent_lib" # see symlink-check above in file_magic test
+ potlib=$potent_lib # see symlink-check above in file_magic test
if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \
$EGREP "$match_pattern_regex" > /dev/null; then
func_append newdeplibs " $a_deplib"
- a_deplib=""
+ a_deplib=
break 2
fi
done
done
fi
- if test -n "$a_deplib" ; then
+ if test -n "$a_deplib"; then
droppeddeps=yes
echo
$ECHO "*** Warning: linker path does not have real file for library $a_deplib."
@@ -7881,7 +9388,7 @@ EOF
echo "*** you link to this library. But I can only do this if you have a"
echo "*** shared version of the library, which you do not appear to have"
echo "*** because I did check the linker path looking for a file starting"
- if test -z "$potlib" ; then
+ if test -z "$potlib"; then
$ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
else
$ECHO "*** with $libname and none of the candidates passed a file format test"
@@ -7897,18 +9404,18 @@ EOF
done # Gone through all deplibs.
;;
none | unknown | *)
- newdeplibs=""
+ newdeplibs=
tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'`
- if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then
- for i in $predeps $postdeps ; do
+ if test yes = "$allow_libtool_libs_with_static_runtimes"; then
+ for i in $predeps $postdeps; do
# can't use Xsed below, because $i might contain '/'
- tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"`
+ tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"`
done
fi
case $tmp_deplibs in
*[!\ \ ]*)
echo
- if test "X$deplibs_check_method" = "Xnone"; then
+ if test none = "$deplibs_check_method"; then
echo "*** Warning: inter-library dependencies are not supported in this platform."
else
echo "*** Warning: inter-library dependencies are not known to be supported."
@@ -7932,8 +9439,8 @@ EOF
;;
esac
- if test "$droppeddeps" = yes; then
- if test "$module" = yes; then
+ if test yes = "$droppeddeps"; then
+ if test yes = "$module"; then
echo
echo "*** Warning: libtool could not satisfy all declared inter-library"
$ECHO "*** dependencies of module $libname. Therefore, libtool will create"
@@ -7942,12 +9449,12 @@ EOF
if test -z "$global_symbol_pipe"; then
echo
echo "*** However, this would only work if libtool was able to extract symbol"
- echo "*** lists from a program, using \`nm' or equivalent, but libtool could"
+ echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
echo "*** not find such a program. So, this module is probably useless."
- echo "*** \`nm' from GNU binutils and a full rebuild may help."
+ echo "*** 'nm' from GNU binutils and a full rebuild may help."
fi
- if test "$build_old_libs" = no; then
- oldlibs="$output_objdir/$libname.$libext"
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
build_libtool_libs=module
build_old_libs=yes
else
@@ -7958,14 +9465,14 @@ EOF
echo "*** automatically added whenever a program is linked with this library"
echo "*** or is declared to -dlopen it."
- if test "$allow_undefined" = no; then
+ if test no = "$allow_undefined"; then
echo
echo "*** Since this library must not contain undefined symbols,"
echo "*** because either the platform does not support them or"
echo "*** it was explicitly requested with -no-undefined,"
echo "*** libtool will only create a static version of it."
- if test "$build_old_libs" = no; then
- oldlibs="$output_objdir/$libname.$libext"
+ if test no = "$build_old_libs"; then
+ oldlibs=$output_objdir/$libname.$libext
build_libtool_libs=module
build_old_libs=yes
else
@@ -8011,7 +9518,7 @@ EOF
*) func_append new_libs " $deplib" ;;
esac
done
- deplibs="$new_libs"
+ deplibs=$new_libs
# All the library-specific variables (install_libdir is set above).
library_names=
@@ -8019,20 +9526,25 @@ EOF
dlname=
# Test again, we may have decided not to build it any more
- if test "$build_libtool_libs" = yes; then
- if test "$hardcode_into_libs" = yes; then
+ if test yes = "$build_libtool_libs"; then
+ # Remove $wl instances when linking with ld.
+ # FIXME: should test the right _cmds variable.
+ case $archive_cmds in
+ *\$LD\ *) wl= ;;
+ esac
+ if test yes = "$hardcode_into_libs"; then
# Hardcode the library paths
hardcode_libdirs=
dep_rpath=
- rpath="$finalize_rpath"
- test "$opt_mode" != relink && rpath="$compile_rpath$rpath"
+ rpath=$finalize_rpath
+ test relink = "$opt_mode" || rpath=$compile_rpath$rpath
for libdir in $rpath; do
if test -n "$hardcode_libdir_flag_spec"; then
if test -n "$hardcode_libdir_separator"; then
func_replace_sysroot "$libdir"
libdir=$func_replace_sysroot_result
if test -z "$hardcode_libdirs"; then
- hardcode_libdirs="$libdir"
+ hardcode_libdirs=$libdir
else
# Just accumulate the unique libdirs.
case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
@@ -8050,19 +9562,15 @@ EOF
elif test -n "$runpath_var"; then
case "$perm_rpath " in
*" $libdir "*) ;;
- *) func_apped perm_rpath " $libdir" ;;
+ *) func_append perm_rpath " $libdir" ;;
esac
fi
done
# Substitute the hardcoded libdirs into the rpath.
if test -n "$hardcode_libdir_separator" &&
test -n "$hardcode_libdirs"; then
- libdir="$hardcode_libdirs"
- if test -n "$hardcode_libdir_flag_spec_ld"; then
- eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\"
- else
- eval dep_rpath=\"$hardcode_libdir_flag_spec\"
- fi
+ libdir=$hardcode_libdirs
+ eval "dep_rpath=\"$hardcode_libdir_flag_spec\""
fi
if test -n "$runpath_var" && test -n "$perm_rpath"; then
# We should set the runpath_var.
@@ -8075,8 +9583,8 @@ EOF
test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs"
fi
- shlibpath="$finalize_shlibpath"
- test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath"
+ shlibpath=$finalize_shlibpath
+ test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath
if test -n "$shlibpath"; then
eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var"
fi
@@ -8086,19 +9594,19 @@ EOF
eval library_names=\"$library_names_spec\"
set dummy $library_names
shift
- realname="$1"
+ realname=$1
shift
if test -n "$soname_spec"; then
eval soname=\"$soname_spec\"
else
- soname="$realname"
+ soname=$realname
fi
if test -z "$dlname"; then
dlname=$soname
fi
- lib="$output_objdir/$realname"
+ lib=$output_objdir/$realname
linknames=
for link
do
@@ -8112,7 +9620,7 @@ EOF
delfiles=
if test -n "$export_symbols" && test -n "$include_expsyms"; then
$opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp"
- export_symbols="$output_objdir/$libname.uexp"
+ export_symbols=$output_objdir/$libname.uexp
func_append delfiles " $export_symbols"
fi
@@ -8121,31 +9629,31 @@ EOF
cygwin* | mingw* | cegcc*)
if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
# exporting using user supplied symfile
- if test "x`$SED 1q $export_symbols`" != xEXPORTS; then
+ func_dll_def_p "$export_symbols" || {
# and it's NOT already a .def file. Must figure out
# which of the given symbols are data symbols and tag
# them as such. So, trigger use of export_symbols_cmds.
# export_symbols gets reassigned inside the "prepare
# the list of exported symbols" if statement, so the
# include_expsyms logic still works.
- orig_export_symbols="$export_symbols"
+ orig_export_symbols=$export_symbols
export_symbols=
always_export_symbols=yes
- fi
+ }
fi
;;
esac
# Prepare the list of exported symbols
if test -z "$export_symbols"; then
- if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then
- func_verbose "generating symbol list for \`$libname.la'"
- export_symbols="$output_objdir/$libname.exp"
+ if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
$opt_dry_run || $RM $export_symbols
cmds=$export_symbols_cmds
- save_ifs="$IFS"; IFS='~'
+ save_ifs=$IFS; IFS='~'
for cmd1 in $cmds; do
- IFS="$save_ifs"
+ IFS=$save_ifs
# Take the normal branch if the nm_file_list_spec branch
# doesn't work or if tool conversion is not needed.
case $nm_file_list_spec~$to_tool_file_cmd in
@@ -8159,7 +9667,7 @@ EOF
try_normal_branch=no
;;
esac
- if test "$try_normal_branch" = yes \
+ if test yes = "$try_normal_branch" \
&& { test "$len" -lt "$max_cmd_len" \
|| test "$max_cmd_len" -le -1; }
then
@@ -8170,7 +9678,7 @@ EOF
output_la=$func_basename_result
save_libobjs=$libobjs
save_output=$output
- output=${output_objdir}/${output_la}.nm
+ output=$output_objdir/$output_la.nm
func_to_tool_file "$output"
libobjs=$nm_file_list_spec$func_to_tool_file_result
func_append delfiles " $output"
@@ -8193,8 +9701,8 @@ EOF
break
fi
done
- IFS="$save_ifs"
- if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then
+ IFS=$save_ifs
+ if test -n "$export_symbols_regex" && test : != "$skipped_export"; then
func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
func_show_eval '$MV "${export_symbols}T" "$export_symbols"'
fi
@@ -8202,16 +9710,16 @@ EOF
fi
if test -n "$export_symbols" && test -n "$include_expsyms"; then
- tmp_export_symbols="$export_symbols"
- test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
$opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
fi
- if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then
+ if test : != "$skipped_export" && test -n "$orig_export_symbols"; then
# The given exports_symbols file has to be filtered, so filter it.
- func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
# FIXME: $output_objdir/$libname.filter potentially contains lots of
- # 's' commands which not all seds can handle. GNU sed should be fine
+ # 's' commands, which not all seds can handle. GNU sed should be fine
# though. Also, the filter scales superlinearly with the number of
# global variables. join(1) would be nice here, but unfortunately
# isn't a blessed tool.
@@ -8230,11 +9738,11 @@ EOF
;;
esac
done
- deplibs="$tmp_deplibs"
+ deplibs=$tmp_deplibs
if test -n "$convenience"; then
if test -n "$whole_archive_flag_spec" &&
- test "$compiler_needs_object" = yes &&
+ test yes = "$compiler_needs_object" &&
test -z "$libobjs"; then
# extract the archives, so we have objects to list.
# TODO: could optimize this to just extract one archive.
@@ -8245,7 +9753,7 @@ EOF
eval libobjs=\"\$libobjs $whole_archive_flag_spec\"
test "X$libobjs" = "X " && libobjs=
else
- gentop="$output_objdir/${outputname}x"
+ gentop=$output_objdir/${outputname}x
func_append generated " $gentop"
func_extract_archives $gentop $convenience
@@ -8254,18 +9762,18 @@ EOF
fi
fi
- if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then
+ if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then
eval flag=\"$thread_safe_flag_spec\"
func_append linker_flags " $flag"
fi
# Make a backup of the uninstalled library when relinking
- if test "$opt_mode" = relink; then
+ if test relink = "$opt_mode"; then
$opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $?
fi
# Do each of the archive commands.
- if test "$module" = yes && test -n "$module_cmds" ; then
+ if test yes = "$module" && test -n "$module_cmds"; then
if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
eval test_cmds=\"$module_expsym_cmds\"
cmds=$module_expsym_cmds
@@ -8283,7 +9791,7 @@ EOF
fi
fi
- if test "X$skipped_export" != "X:" &&
+ if test : != "$skipped_export" &&
func_len " $test_cmds" &&
len=$func_len_result &&
test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then
@@ -8316,8 +9824,8 @@ EOF
last_robj=
k=1
- if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then
- output=${output_objdir}/${output_la}.lnkscript
+ if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+ output=$output_objdir/$output_la.lnkscript
func_verbose "creating GNU ld script: $output"
echo 'INPUT (' > $output
for obj in $save_libobjs
@@ -8329,14 +9837,14 @@ EOF
func_append delfiles " $output"
func_to_tool_file "$output"
output=$func_to_tool_file_result
- elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then
- output=${output_objdir}/${output_la}.lnk
+ elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+ output=$output_objdir/$output_la.lnk
func_verbose "creating linker input file list: $output"
: > $output
set x $save_libobjs
shift
firstobj=
- if test "$compiler_needs_object" = yes; then
+ if test yes = "$compiler_needs_object"; then
firstobj="$1 "
shift
fi
@@ -8351,7 +9859,7 @@ EOF
else
if test -n "$save_libobjs"; then
func_verbose "creating reloadable object files..."
- output=$output_objdir/$output_la-${k}.$objext
+ output=$output_objdir/$output_la-$k.$objext
eval test_cmds=\"$reload_cmds\"
func_len " $test_cmds"
len0=$func_len_result
@@ -8363,13 +9871,13 @@ EOF
func_len " $obj"
func_arith $len + $func_len_result
len=$func_arith_result
- if test "X$objlist" = X ||
+ if test -z "$objlist" ||
test "$len" -lt "$max_cmd_len"; then
func_append objlist " $obj"
else
# The command $test_cmds is almost too long, add a
# command to the queue.
- if test "$k" -eq 1 ; then
+ if test 1 -eq "$k"; then
# The first file doesn't have a previous command to add.
reload_objs=$objlist
eval concat_cmds=\"$reload_cmds\"
@@ -8379,10 +9887,10 @@ EOF
reload_objs="$objlist $last_robj"
eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\"
fi
- last_robj=$output_objdir/$output_la-${k}.$objext
+ last_robj=$output_objdir/$output_la-$k.$objext
func_arith $k + 1
k=$func_arith_result
- output=$output_objdir/$output_la-${k}.$objext
+ output=$output_objdir/$output_la-$k.$objext
objlist=" $obj"
func_len " $last_robj"
func_arith $len0 + $func_len_result
@@ -8394,9 +9902,9 @@ EOF
# files will link in the last one created.
test -z "$concat_cmds" || concat_cmds=$concat_cmds~
reload_objs="$objlist $last_robj"
- eval concat_cmds=\"\${concat_cmds}$reload_cmds\"
+ eval concat_cmds=\"\$concat_cmds$reload_cmds\"
if test -n "$last_robj"; then
- eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\"
+ eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
fi
func_append delfiles " $output"
@@ -8404,9 +9912,9 @@ EOF
output=
fi
- if ${skipped_export-false}; then
- func_verbose "generating symbol list for \`$libname.la'"
- export_symbols="$output_objdir/$libname.exp"
+ ${skipped_export-false} && {
+ func_verbose "generating symbol list for '$libname.la'"
+ export_symbols=$output_objdir/$libname.exp
$opt_dry_run || $RM $export_symbols
libobjs=$output
# Append the command to create the export file.
@@ -8415,16 +9923,16 @@ EOF
if test -n "$last_robj"; then
eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\"
fi
- fi
+ }
test -n "$save_libobjs" &&
func_verbose "creating a temporary reloadable object file: $output"
# Loop through the commands generated above and execute them.
- save_ifs="$IFS"; IFS='~'
+ save_ifs=$IFS; IFS='~'
for cmd in $concat_cmds; do
- IFS="$save_ifs"
- $opt_silent || {
+ IFS=$save_ifs
+ $opt_quiet || {
func_quote_for_expand "$cmd"
eval "func_echo $func_quote_for_expand_result"
}
@@ -8432,7 +9940,7 @@ EOF
lt_exit=$?
# Restore the uninstalled library and exit
- if test "$opt_mode" = relink; then
+ if test relink = "$opt_mode"; then
( cd "$output_objdir" && \
$RM "${realname}T" && \
$MV "${realname}U" "$realname" )
@@ -8441,7 +9949,7 @@ EOF
exit $lt_exit
}
done
- IFS="$save_ifs"
+ IFS=$save_ifs
if test -n "$export_symbols_regex" && ${skipped_export-false}; then
func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"'
@@ -8449,18 +9957,18 @@ EOF
fi
fi
- if ${skipped_export-false}; then
+ ${skipped_export-false} && {
if test -n "$export_symbols" && test -n "$include_expsyms"; then
- tmp_export_symbols="$export_symbols"
- test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols"
+ tmp_export_symbols=$export_symbols
+ test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols
$opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"'
fi
if test -n "$orig_export_symbols"; then
# The given exports_symbols file has to be filtered, so filter it.
- func_verbose "filter symbol list for \`$libname.la' to tag DATA exports"
+ func_verbose "filter symbol list for '$libname.la' to tag DATA exports"
# FIXME: $output_objdir/$libname.filter potentially contains lots of
- # 's' commands which not all seds can handle. GNU sed should be fine
+ # 's' commands, which not all seds can handle. GNU sed should be fine
# though. Also, the filter scales superlinearly with the number of
# global variables. join(1) would be nice here, but unfortunately
# isn't a blessed tool.
@@ -8469,7 +9977,7 @@ EOF
export_symbols=$output_objdir/$libname.def
$opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols
fi
- fi
+ }
libobjs=$output
# Restore the value of output.
@@ -8483,7 +9991,7 @@ EOF
# value of $libobjs for piecewise linking.
# Do each of the archive commands.
- if test "$module" = yes && test -n "$module_cmds" ; then
+ if test yes = "$module" && test -n "$module_cmds"; then
if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then
cmds=$module_expsym_cmds
else
@@ -8505,7 +10013,7 @@ EOF
# Add any objects from preloaded convenience libraries
if test -n "$dlprefiles"; then
- gentop="$output_objdir/${outputname}x"
+ gentop=$output_objdir/${outputname}x
func_append generated " $gentop"
func_extract_archives $gentop $dlprefiles
@@ -8513,11 +10021,12 @@ EOF
test "X$libobjs" = "X " && libobjs=
fi
- save_ifs="$IFS"; IFS='~'
+ save_ifs=$IFS; IFS='~'
for cmd in $cmds; do
- IFS="$save_ifs"
+ IFS=$sp$nl
eval cmd=\"$cmd\"
- $opt_silent || {
+ IFS=$save_ifs
+ $opt_quiet || {
func_quote_for_expand "$cmd"
eval "func_echo $func_quote_for_expand_result"
}
@@ -8525,7 +10034,7 @@ EOF
lt_exit=$?
# Restore the uninstalled library and exit
- if test "$opt_mode" = relink; then
+ if test relink = "$opt_mode"; then
( cd "$output_objdir" && \
$RM "${realname}T" && \
$MV "${realname}U" "$realname" )
@@ -8534,10 +10043,10 @@ EOF
exit $lt_exit
}
done
- IFS="$save_ifs"
+ IFS=$save_ifs
# Restore the uninstalled library and exit
- if test "$opt_mode" = relink; then
+ if test relink = "$opt_mode"; then
$opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $?
if test -n "$convenience"; then
@@ -8557,39 +10066,39 @@ EOF
done
# If -module or -export-dynamic was specified, set the dlname.
- if test "$module" = yes || test "$export_dynamic" = yes; then
+ if test yes = "$module" || test yes = "$export_dynamic"; then
# On all known operating systems, these are identical.
- dlname="$soname"
+ dlname=$soname
fi
fi
;;
obj)
- if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then
- func_warning "\`-dlopen' is ignored for objects"
+ if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then
+ func_warning "'-dlopen' is ignored for objects"
fi
case " $deplibs" in
*\ -l* | *\ -L*)
- func_warning "\`-l' and \`-L' are ignored for objects" ;;
+ func_warning "'-l' and '-L' are ignored for objects" ;;
esac
test -n "$rpath" && \
- func_warning "\`-rpath' is ignored for objects"
+ func_warning "'-rpath' is ignored for objects"
test -n "$xrpath" && \
- func_warning "\`-R' is ignored for objects"
+ func_warning "'-R' is ignored for objects"
test -n "$vinfo" && \
- func_warning "\`-version-info' is ignored for objects"
+ func_warning "'-version-info' is ignored for objects"
test -n "$release" && \
- func_warning "\`-release' is ignored for objects"
+ func_warning "'-release' is ignored for objects"
case $output in
*.lo)
test -n "$objs$old_deplibs" && \
- func_fatal_error "cannot build library object \`$output' from non-libtool objects"
+ func_fatal_error "cannot build library object '$output' from non-libtool objects"
libobj=$output
func_lo2o "$libobj"
@@ -8597,7 +10106,7 @@ EOF
;;
*)
libobj=
- obj="$output"
+ obj=$output
;;
esac
@@ -8610,17 +10119,19 @@ EOF
# the extraction.
reload_conv_objs=
gentop=
- # reload_cmds runs $LD directly, so let us get rid of
- # -Wl from whole_archive_flag_spec and hope we can get by with
- # turning comma into space..
- wl=
-
+ # if reload_cmds runs $LD directly, get rid of -Wl from
+ # whole_archive_flag_spec and hope we can get by with turning comma
+ # into space.
+ case $reload_cmds in
+ *\$LD[\ \$]*) wl= ;;
+ esac
if test -n "$convenience"; then
if test -n "$whole_archive_flag_spec"; then
eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\"
- reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+ test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'`
+ reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags
else
- gentop="$output_objdir/${obj}x"
+ gentop=$output_objdir/${obj}x
func_append generated " $gentop"
func_extract_archives $gentop $convenience
@@ -8629,12 +10140,12 @@ EOF
fi
# If we're not building shared, we need to use non_pic_objs
- test "$build_libtool_libs" != yes && libobjs="$non_pic_objects"
+ test yes = "$build_libtool_libs" || libobjs=$non_pic_objects
# Create the old-style object.
- reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test
+ reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs
- output="$obj"
+ output=$obj
func_execute_cmds "$reload_cmds" 'exit $?'
# Exit if we aren't doing a library object file.
@@ -8646,7 +10157,7 @@ EOF
exit $EXIT_SUCCESS
fi
- if test "$build_libtool_libs" != yes; then
+ test yes = "$build_libtool_libs" || {
if test -n "$gentop"; then
func_show_eval '${RM}r "$gentop"'
fi
@@ -8656,12 +10167,12 @@ EOF
# $show "echo timestamp > $libobj"
# $opt_dry_run || eval "echo timestamp > $libobj" || exit $?
exit $EXIT_SUCCESS
- fi
+ }
- if test -n "$pic_flag" || test "$pic_mode" != default; then
+ if test -n "$pic_flag" || test default != "$pic_mode"; then
# Only do commands if we really have different PIC objects.
reload_objs="$libobjs $reload_conv_objs"
- output="$libobj"
+ output=$libobj
func_execute_cmds "$reload_cmds" 'exit $?'
fi
@@ -8678,16 +10189,14 @@ EOF
output=$func_stripname_result.exe;;
esac
test -n "$vinfo" && \
- func_warning "\`-version-info' is ignored for programs"
+ func_warning "'-version-info' is ignored for programs"
test -n "$release" && \
- func_warning "\`-release' is ignored for programs"
+ func_warning "'-release' is ignored for programs"
- test "$preload" = yes \
- && test "$dlopen_support" = unknown \
- && test "$dlopen_self" = unknown \
- && test "$dlopen_self_static" = unknown && \
- func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support."
+ $preload \
+ && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \
+ && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support."
case $host in
*-*-rhapsody* | *-*-darwin1.[012])
@@ -8701,11 +10210,11 @@ EOF
*-*-darwin*)
# Don't allow lazy linking, it breaks C++ global constructors
# But is supposedly fixed on 10.4 or later (yay!).
- if test "$tagname" = CXX ; then
+ if test CXX = "$tagname"; then
case ${MACOSX_DEPLOYMENT_TARGET-10.0} in
10.[0123])
- func_append compile_command " ${wl}-bind_at_load"
- func_append finalize_command " ${wl}-bind_at_load"
+ func_append compile_command " $wl-bind_at_load"
+ func_append finalize_command " $wl-bind_at_load"
;;
esac
fi
@@ -8741,7 +10250,7 @@ EOF
*) func_append new_libs " $deplib" ;;
esac
done
- compile_deplibs="$new_libs"
+ compile_deplibs=$new_libs
func_append compile_command " $compile_deplibs"
@@ -8765,7 +10274,7 @@ EOF
if test -n "$hardcode_libdir_flag_spec"; then
if test -n "$hardcode_libdir_separator"; then
if test -z "$hardcode_libdirs"; then
- hardcode_libdirs="$libdir"
+ hardcode_libdirs=$libdir
else
# Just accumulate the unique libdirs.
case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
@@ -8788,7 +10297,7 @@ EOF
fi
case $host in
*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
- testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'`
+ testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
case :$dllsearchpath: in
*":$libdir:"*) ;;
::) dllsearchpath=$libdir;;
@@ -8805,10 +10314,10 @@ EOF
# Substitute the hardcoded libdirs into the rpath.
if test -n "$hardcode_libdir_separator" &&
test -n "$hardcode_libdirs"; then
- libdir="$hardcode_libdirs"
+ libdir=$hardcode_libdirs
eval rpath=\" $hardcode_libdir_flag_spec\"
fi
- compile_rpath="$rpath"
+ compile_rpath=$rpath
rpath=
hardcode_libdirs=
@@ -8816,7 +10325,7 @@ EOF
if test -n "$hardcode_libdir_flag_spec"; then
if test -n "$hardcode_libdir_separator"; then
if test -z "$hardcode_libdirs"; then
- hardcode_libdirs="$libdir"
+ hardcode_libdirs=$libdir
else
# Just accumulate the unique libdirs.
case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in
@@ -8841,45 +10350,43 @@ EOF
# Substitute the hardcoded libdirs into the rpath.
if test -n "$hardcode_libdir_separator" &&
test -n "$hardcode_libdirs"; then
- libdir="$hardcode_libdirs"
+ libdir=$hardcode_libdirs
eval rpath=\" $hardcode_libdir_flag_spec\"
fi
- finalize_rpath="$rpath"
+ finalize_rpath=$rpath
- if test -n "$libobjs" && test "$build_old_libs" = yes; then
+ if test -n "$libobjs" && test yes = "$build_old_libs"; then
# Transform all the library objects into standard objects.
compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP`
fi
- func_generate_dlsyms "$outputname" "@PROGRAM@" "no"
+ func_generate_dlsyms "$outputname" "@PROGRAM@" false
# template prelinking step
if test -n "$prelink_cmds"; then
func_execute_cmds "$prelink_cmds" 'exit $?'
fi
- wrappers_required=yes
+ wrappers_required=:
case $host in
*cegcc* | *mingw32ce*)
# Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
- wrappers_required=no
+ wrappers_required=false
;;
*cygwin* | *mingw* )
- if test "$build_libtool_libs" != yes; then
- wrappers_required=no
- fi
+ test yes = "$build_libtool_libs" || wrappers_required=false
;;
*)
- if test "$need_relink" = no || test "$build_libtool_libs" != yes; then
- wrappers_required=no
+ if test no = "$need_relink" || test yes != "$build_libtool_libs"; then
+ wrappers_required=false
fi
;;
esac
- if test "$wrappers_required" = no; then
+ $wrappers_required || {
# Replace the output file specification.
compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
- link_command="$compile_command$compile_rpath"
+ link_command=$compile_command$compile_rpath
# We have no uninstalled library dependencies, so finalize right now.
exit_status=0
@@ -8892,12 +10399,12 @@ EOF
fi
# Delete the generated files.
- if test -f "$output_objdir/${outputname}S.${objext}"; then
- func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"'
+ if test -f "$output_objdir/${outputname}S.$objext"; then
+ func_show_eval '$RM "$output_objdir/${outputname}S.$objext"'
fi
exit $exit_status
- fi
+ }
if test -n "$compile_shlibpath$finalize_shlibpath"; then
compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command"
@@ -8927,9 +10434,9 @@ EOF
fi
fi
- if test "$no_install" = yes; then
+ if test yes = "$no_install"; then
# We don't need to create a wrapper script.
- link_command="$compile_var$compile_command$compile_rpath"
+ link_command=$compile_var$compile_command$compile_rpath
# Replace the output file specification.
link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'`
# Delete the old output file.
@@ -8946,27 +10453,28 @@ EOF
exit $EXIT_SUCCESS
fi
- if test "$hardcode_action" = relink; then
- # Fast installation is not supported
- link_command="$compile_var$compile_command$compile_rpath"
- relink_command="$finalize_var$finalize_command$finalize_rpath"
+ case $hardcode_action,$fast_install in
+ relink,*)
+ # Fast installation is not supported
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
- func_warning "this platform does not like uninstalled shared libraries"
- func_warning "\`$output' will be relinked during installation"
- else
- if test "$fast_install" != no; then
- link_command="$finalize_var$compile_command$finalize_rpath"
- if test "$fast_install" = yes; then
- relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
- else
- # fast_install is set to needless
- relink_command=
- fi
- else
- link_command="$compile_var$compile_command$compile_rpath"
- relink_command="$finalize_var$finalize_command$finalize_rpath"
- fi
- fi
+ func_warning "this platform does not like uninstalled shared libraries"
+ func_warning "'$output' will be relinked during installation"
+ ;;
+ *,yes)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'`
+ ;;
+ *,no)
+ link_command=$compile_var$compile_command$compile_rpath
+ relink_command=$finalize_var$finalize_command$finalize_rpath
+ ;;
+ *,needless)
+ link_command=$finalize_var$compile_command$finalize_rpath
+ relink_command=
+ ;;
+ esac
# Replace the output file specification.
link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'`
@@ -9023,8 +10531,8 @@ EOF
func_dirname_and_basename "$output" "" "."
output_name=$func_basename_result
output_path=$func_dirname_result
- cwrappersource="$output_path/$objdir/lt-$output_name.c"
- cwrapper="$output_path/$output_name.exe"
+ cwrappersource=$output_path/$objdir/lt-$output_name.c
+ cwrapper=$output_path/$output_name.exe
$RM $cwrappersource $cwrapper
trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15
@@ -9045,7 +10553,7 @@ EOF
trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15
$opt_dry_run || {
# note: this script will not be executed, so do not chmod.
- if test "x$build" = "x$host" ; then
+ if test "x$build" = "x$host"; then
$cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result
else
func_emit_wrapper no > $func_ltwrapper_scriptname_result
@@ -9068,25 +10576,27 @@ EOF
# See if we need to build an old-fashioned archive.
for oldlib in $oldlibs; do
- if test "$build_libtool_libs" = convenience; then
- oldobjs="$libobjs_save $symfileobj"
- addlibs="$convenience"
- build_libtool_libs=no
- else
- if test "$build_libtool_libs" = module; then
- oldobjs="$libobjs_save"
+ case $build_libtool_libs in
+ convenience)
+ oldobjs="$libobjs_save $symfileobj"
+ addlibs=$convenience
build_libtool_libs=no
- else
+ ;;
+ module)
+ oldobjs=$libobjs_save
+ addlibs=$old_convenience
+ build_libtool_libs=no
+ ;;
+ *)
oldobjs="$old_deplibs $non_pic_objects"
- if test "$preload" = yes && test -f "$symfileobj"; then
- func_append oldobjs " $symfileobj"
- fi
- fi
- addlibs="$old_convenience"
- fi
+ $preload && test -f "$symfileobj" \
+ && func_append oldobjs " $symfileobj"
+ addlibs=$old_convenience
+ ;;
+ esac
if test -n "$addlibs"; then
- gentop="$output_objdir/${outputname}x"
+ gentop=$output_objdir/${outputname}x
func_append generated " $gentop"
func_extract_archives $gentop $addlibs
@@ -9094,13 +10604,13 @@ EOF
fi
# Do each command in the archive commands.
- if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then
+ if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then
cmds=$old_archive_from_new_cmds
else
# Add any objects from preloaded convenience libraries
if test -n "$dlprefiles"; then
- gentop="$output_objdir/${outputname}x"
+ gentop=$output_objdir/${outputname}x
func_append generated " $gentop"
func_extract_archives $gentop $dlprefiles
@@ -9121,7 +10631,7 @@ EOF
:
else
echo "copying selected object files to avoid basename conflicts..."
- gentop="$output_objdir/${outputname}x"
+ gentop=$output_objdir/${outputname}x
func_append generated " $gentop"
func_mkdir_p "$gentop"
save_oldobjs=$oldobjs
@@ -9130,7 +10640,7 @@ EOF
for obj in $save_oldobjs
do
func_basename "$obj"
- objbase="$func_basename_result"
+ objbase=$func_basename_result
case " $oldobjs " in
" ") oldobjs=$obj ;;
*[\ /]"$objbase "*)
@@ -9152,6 +10662,8 @@ EOF
esac
done
fi
+ func_to_tool_file "$oldlib" func_convert_file_msys_to_w32
+ tool_oldlib=$func_to_tool_file_result
eval cmds=\"$old_archive_cmds\"
func_len " $cmds"
@@ -9197,18 +10709,18 @@ EOF
else
# the above command should be used before it gets too long
oldobjs=$objlist
- if test "$obj" = "$last_oldobj" ; then
+ if test "$obj" = "$last_oldobj"; then
RANLIB=$save_RANLIB
fi
test -z "$concat_cmds" || concat_cmds=$concat_cmds~
- eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\"
+ eval concat_cmds=\"\$concat_cmds$old_archive_cmds\"
objlist=
len=$len0
fi
done
RANLIB=$save_RANLIB
oldobjs=$objlist
- if test "X$oldobjs" = "X" ; then
+ if test -z "$oldobjs"; then
eval cmds=\"\$concat_cmds\"
else
eval cmds=\"\$concat_cmds~\$old_archive_cmds\"
@@ -9225,7 +10737,7 @@ EOF
case $output in
*.la)
old_library=
- test "$build_old_libs" = yes && old_library="$libname.$libext"
+ test yes = "$build_old_libs" && old_library=$libname.$libext
func_verbose "creating $output"
# Preserve any variables that may affect compiler behavior
@@ -9240,30 +10752,31 @@ EOF
fi
done
# Quote the link command for shipping.
- relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
- if test "$hardcode_automatic" = yes ; then
+ if test yes = "$hardcode_automatic"; then
relink_command=
fi
# Only create the output if not a dry run.
$opt_dry_run || {
for installed in no yes; do
- if test "$installed" = yes; then
+ if test yes = "$installed"; then
if test -z "$install_libdir"; then
break
fi
- output="$output_objdir/$outputname"i
+ output=$output_objdir/${outputname}i
# Replace all uninstalled libtool libraries with the installed ones
newdependency_libs=
for deplib in $dependency_libs; do
case $deplib in
*.la)
func_basename "$deplib"
- name="$func_basename_result"
- eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib`
+ name=$func_basename_result
+ func_resolve_sysroot "$deplib"
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result`
test -z "$libdir" && \
- func_fatal_error "\`$deplib' is not a valid libtool archive"
+ func_fatal_error "'$deplib' is not a valid libtool archive"
func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name"
;;
-L*)
@@ -9279,23 +10792,23 @@ EOF
*) func_append newdependency_libs " $deplib" ;;
esac
done
- dependency_libs="$newdependency_libs"
+ dependency_libs=$newdependency_libs
newdlfiles=
for lib in $dlfiles; do
case $lib in
*.la)
func_basename "$lib"
- name="$func_basename_result"
- eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
test -z "$libdir" && \
- func_fatal_error "\`$lib' is not a valid libtool archive"
+ func_fatal_error "'$lib' is not a valid libtool archive"
func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name"
;;
*) func_append newdlfiles " $lib" ;;
esac
done
- dlfiles="$newdlfiles"
+ dlfiles=$newdlfiles
newdlprefiles=
for lib in $dlprefiles; do
case $lib in
@@ -9305,34 +10818,34 @@ EOF
# didn't already link the preopened objects directly into
# the library:
func_basename "$lib"
- name="$func_basename_result"
- eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
+ name=$func_basename_result
+ eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib`
test -z "$libdir" && \
- func_fatal_error "\`$lib' is not a valid libtool archive"
+ func_fatal_error "'$lib' is not a valid libtool archive"
func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name"
;;
esac
done
- dlprefiles="$newdlprefiles"
+ dlprefiles=$newdlprefiles
else
newdlfiles=
for lib in $dlfiles; do
case $lib in
- [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
*) abs=`pwd`"/$lib" ;;
esac
func_append newdlfiles " $abs"
done
- dlfiles="$newdlfiles"
+ dlfiles=$newdlfiles
newdlprefiles=
for lib in $dlprefiles; do
case $lib in
- [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;;
+ [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;;
*) abs=`pwd`"/$lib" ;;
esac
func_append newdlprefiles " $abs"
done
- dlprefiles="$newdlprefiles"
+ dlprefiles=$newdlprefiles
fi
$RM $output
# place dlname in correct position for cygwin
@@ -9348,10 +10861,9 @@ EOF
case $host,$output,$installed,$module,$dlname in
*cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
# If a -bindir argument was supplied, place the dll there.
- if test "x$bindir" != x ;
- then
+ if test -n "$bindir"; then
func_relative_path "$install_libdir" "$bindir"
- tdlname=$func_relative_path_result$dlname
+ tdlname=$func_relative_path_result/$dlname
else
# Otherwise fall back on heuristic.
tdlname=../bin/$dlname
@@ -9360,7 +10872,7 @@ EOF
esac
$ECHO > $output "\
# $outputname - a libtool library file
-# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION
+# Generated by $PROGRAM (GNU $PACKAGE) $VERSION
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
@@ -9374,7 +10886,7 @@ library_names='$library_names'
# The name of the static archive.
old_library='$old_library'
-# Linker flags that can not go in dependency_libs.
+# Linker flags that cannot go in dependency_libs.
inherited_linker_flags='$new_inherited_linker_flags'
# Libraries that this one depends upon.
@@ -9400,7 +10912,7 @@ dlpreopen='$dlprefiles'
# Directory that this library needs to be installed in:
libdir='$install_libdir'"
- if test "$installed" = no && test "$need_relink" = yes; then
+ if test no,yes = "$installed,$need_relink"; then
$ECHO >> $output "\
relink_command=\"$relink_command\""
fi
@@ -9415,27 +10927,29 @@ relink_command=\"$relink_command\""
exit $EXIT_SUCCESS
}
-{ test "$opt_mode" = link || test "$opt_mode" = relink; } &&
- func_mode_link ${1+"$@"}
+if test link = "$opt_mode" || test relink = "$opt_mode"; then
+ func_mode_link ${1+"$@"}
+fi
# func_mode_uninstall arg...
func_mode_uninstall ()
{
- $opt_debug
- RM="$nonopt"
+ $debug_cmd
+
+ RM=$nonopt
files=
- rmforce=
+ rmforce=false
exit_status=0
# This variable tells wrapper scripts just to set variables rather
# than running their programs.
- libtool_install_magic="$magic"
+ libtool_install_magic=$magic
for arg
do
case $arg in
- -f) func_append RM " $arg"; rmforce=yes ;;
+ -f) func_append RM " $arg"; rmforce=: ;;
-*) func_append RM " $arg" ;;
*) func_append files " $arg" ;;
esac
@@ -9448,18 +10962,18 @@ func_mode_uninstall ()
for file in $files; do
func_dirname "$file" "" "."
- dir="$func_dirname_result"
- if test "X$dir" = X.; then
- odir="$objdir"
+ dir=$func_dirname_result
+ if test . = "$dir"; then
+ odir=$objdir
else
- odir="$dir/$objdir"
+ odir=$dir/$objdir
fi
func_basename "$file"
- name="$func_basename_result"
- test "$opt_mode" = uninstall && odir="$dir"
+ name=$func_basename_result
+ test uninstall = "$opt_mode" && odir=$dir
# Remember odir for removal later, being careful to avoid duplicates
- if test "$opt_mode" = clean; then
+ if test clean = "$opt_mode"; then
case " $rmdirs " in
*" $odir "*) ;;
*) func_append rmdirs " $odir" ;;
@@ -9474,11 +10988,11 @@ func_mode_uninstall ()
elif test -d "$file"; then
exit_status=1
continue
- elif test "$rmforce" = yes; then
+ elif $rmforce; then
continue
fi
- rmfiles="$file"
+ rmfiles=$file
case $name in
*.la)
@@ -9492,7 +11006,7 @@ func_mode_uninstall ()
done
test -n "$old_library" && func_append rmfiles " $odir/$old_library"
- case "$opt_mode" in
+ case $opt_mode in
clean)
case " $library_names " in
*" $dlname "*) ;;
@@ -9503,12 +11017,12 @@ func_mode_uninstall ()
uninstall)
if test -n "$library_names"; then
# Do each command in the postuninstall commands.
- func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1'
fi
if test -n "$old_library"; then
# Do each command in the old_postuninstall commands.
- func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1'
+ func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1'
fi
# FIXME: should reinstall the best remaining shared library.
;;
@@ -9524,21 +11038,19 @@ func_mode_uninstall ()
func_source $dir/$name
# Add PIC object to the list of files to remove.
- if test -n "$pic_object" &&
- test "$pic_object" != none; then
+ if test -n "$pic_object" && test none != "$pic_object"; then
func_append rmfiles " $dir/$pic_object"
fi
# Add non-PIC object to the list of files to remove.
- if test -n "$non_pic_object" &&
- test "$non_pic_object" != none; then
+ if test -n "$non_pic_object" && test none != "$non_pic_object"; then
func_append rmfiles " $dir/$non_pic_object"
fi
fi
;;
*)
- if test "$opt_mode" = clean ; then
+ if test clean = "$opt_mode"; then
noexename=$name
case $file in
*.exe)
@@ -9565,12 +11077,12 @@ func_mode_uninstall ()
# note $name still contains .exe if it was in $file originally
# as does the version of $file that was added into $rmfiles
- func_append rmfiles " $odir/$name $odir/${name}S.${objext}"
- if test "$fast_install" = yes && test -n "$relink_command"; then
+ func_append rmfiles " $odir/$name $odir/${name}S.$objext"
+ if test yes = "$fast_install" && test -n "$relink_command"; then
func_append rmfiles " $odir/lt-$name"
fi
- if test "X$noexename" != "X$name" ; then
- func_append rmfiles " $odir/lt-${noexename}.c"
+ if test "X$noexename" != "X$name"; then
+ func_append rmfiles " $odir/lt-$noexename.c"
fi
fi
fi
@@ -9579,7 +11091,7 @@ func_mode_uninstall ()
func_show_eval "$RM $rmfiles" 'exit_status=1'
done
- # Try to remove the ${objdir}s in the directories where we deleted files
+ # Try to remove the $objdir's in the directories where we deleted files
for dir in $rmdirs; do
if test -d "$dir"; then
func_show_eval "rmdir $dir >/dev/null 2>&1"
@@ -9589,16 +11101,17 @@ func_mode_uninstall ()
exit $exit_status
}
-{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } &&
- func_mode_uninstall ${1+"$@"}
+if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then
+ func_mode_uninstall ${1+"$@"}
+fi
test -z "$opt_mode" && {
- help="$generic_help"
+ help=$generic_help
func_fatal_help "you must specify a MODE"
}
test -z "$exec_cmd" && \
- func_fatal_help "invalid operation mode \`$opt_mode'"
+ func_fatal_help "invalid operation mode '$opt_mode'"
if test -n "$exec_cmd"; then
eval exec "$exec_cmd"
@@ -9609,7 +11122,7 @@ exit $exit_status
# The TAGs below are defined such that we never get into a situation
-# in which we disable both kinds of libraries. Given conflicting
+# where we disable both kinds of libraries. Given conflicting
# choices, we go for a static library, that is the most portable,
# since we can't tell whether shared libraries were disabled because
# the user asked for that or because the platform doesn't support
@@ -9632,5 +11145,3 @@ build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac`
# mode:shell-script
# sh-indentation:2
# End:
-# vi:sw=2
-
diff --git a/lib/libedit/missing b/lib/libedit/missing
index 28055d2ae6f2..c6e379584baf 100755
--- a/lib/libedit/missing
+++ b/lib/libedit/missing
@@ -1,11 +1,10 @@
#! /bin/sh
-# Common stub for a few missing GNU programs while installing.
+# Common wrapper for a few potentially missing GNU programs.
-scriptversion=2009-04-28.21; # UTC
+scriptversion=2016-01-11.22; # UTC
-# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006,
-# 2008, 2009 Free Software Foundation, Inc.
-# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996.
+# Copyright (C) 1996-2017 Free Software Foundation, Inc.
+# Originally written 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
@@ -26,69 +25,40 @@ scriptversion=2009-04-28.21; # UTC
# 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"
+ 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
+case $1 in
-msg="missing on your system"
+ --is-lightweight)
+ # Used by our autoconf macros to check whether the available missing
+ # script is modern enough.
+ exit 0
+ ;;
-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
- ;;
+ --run)
+ # Back-compat with the calling convention used by older automake.
+ shift
+ ;;
-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.
+Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due
+to PROGRAM being missing or too old.
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
- tar try tar, gnutar, gtar, then tar without non-portable flags
- yacc create \`y.tab.[ch]', if possible, from existing .[ch]
+ aclocal autoconf autoheader autom4te automake makeinfo
+ bison yacc flex lex help2man
-Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and
-\`g' are ignored when checking the name.
+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 $?
@@ -100,277 +70,146 @@ Send bug reports to <bug-automake@gnu.org>."
;;
-*)
- echo 1>&2 "$0: Unknown \`$1' option"
- echo 1>&2 "Try \`$0 --help' for more information"
+ 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.
- ;;
-
- tar*)
- if test -n "$run"; then
- echo 1>&2 "ERROR: \`tar' requires --run"
- exit 1
- elif test "x$2" = "x--version" || test "x$2" = "x--help"; then
- exit 1
- fi
- ;;
-
- *)
- 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
- ;;
-
- tar*)
- shift
-
- # We have already tried tar in the generic part.
- # Look for gnutar/gtar before invocation to avoid ugly error
- # messages.
- if (gnutar --version > /dev/null 2>&1); then
- gnutar "$@" && exit 0
- fi
- if (gtar --version > /dev/null 2>&1); then
- gtar "$@" && exit 0
- fi
- firstarg="$1"
- if shift; then
- case $firstarg in
- *o*)
- firstarg=`echo "$firstarg" | sed s/o//`
- tar "$firstarg" "$@" && exit 0
- ;;
- esac
- case $firstarg in
- *h*)
- firstarg=`echo "$firstarg" | sed s/h//`
- tar "$firstarg" "$@" && exit 0
- ;;
- esac
- fi
-
- echo 1>&2 "\
-WARNING: I can't seem to be able to run \`tar' with the given arguments.
- You may want to install GNU tar or Free paxutils, or check the
- command line arguments."
- exit 1
- ;;
-
- *)
- 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
+# Run the given program, remember its exit status.
+"$@"; st=$?
+
+# If it succeeded, we are done.
+test $st -eq 0 && exit 0
+
+# Also exit now if we it failed (or wasn't found), and '--version' was
+# passed; such an option is passed most likely to detect whether the
+# program is present and works.
+case $2 in --version|--help) exit $st;; esac
+
+# Exit code 63 means version mismatch. This often happens when the user
+# tries to use an ancient version of a tool on a file that requires a
+# minimum version.
+if test $st -eq 63; then
+ msg="probably too old"
+elif test $st -eq 127; then
+ # Program was missing.
+ msg="missing on your system"
+else
+ # Program was found and executed, but failed. Give up.
+ exit $st
+fi
-exit 0
+perl_URL=http://www.perl.org/
+flex_URL=http://flex.sourceforge.net/
+gnu_software_URL=http://www.gnu.org/software
+
+program_details ()
+{
+ case $1 in
+ aclocal|automake)
+ echo "The '$1' program is part of the GNU Automake package:"
+ echo "<$gnu_software_URL/automake>"
+ echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/autoconf>"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ autoconf|autom4te|autoheader)
+ echo "The '$1' program is part of the GNU Autoconf package:"
+ echo "<$gnu_software_URL/autoconf/>"
+ echo "It also requires GNU m4 and Perl in order to run:"
+ echo "<$gnu_software_URL/m4/>"
+ echo "<$perl_URL>"
+ ;;
+ esac
+}
+
+give_advice ()
+{
+ # Normalize program name to check for.
+ normalized_program=`echo "$1" | sed '
+ s/^gnu-//; t
+ s/^gnu//; t
+ s/^g//; t'`
+
+ printf '%s\n' "'$1' is $msg."
+
+ configure_deps="'configure.ac' or m4 files included by 'configure.ac'"
+ case $normalized_program in
+ autoconf*)
+ echo "You should only need it if you modified 'configure.ac',"
+ echo "or m4 files included by it."
+ program_details 'autoconf'
+ ;;
+ autoheader*)
+ echo "You should only need it if you modified 'acconfig.h' or"
+ echo "$configure_deps."
+ program_details 'autoheader'
+ ;;
+ automake*)
+ echo "You should only need it if you modified 'Makefile.am' or"
+ echo "$configure_deps."
+ program_details 'automake'
+ ;;
+ aclocal*)
+ echo "You should only need it if you modified 'acinclude.m4' or"
+ echo "$configure_deps."
+ program_details 'aclocal'
+ ;;
+ autom4te*)
+ echo "You might have modified some maintainer files that require"
+ echo "the 'autom4te' program to be rebuilt."
+ program_details 'autom4te'
+ ;;
+ bison*|yacc*)
+ echo "You should only need it if you modified a '.y' file."
+ echo "You may want to install the GNU Bison package:"
+ echo "<$gnu_software_URL/bison/>"
+ ;;
+ lex*|flex*)
+ echo "You should only need it if you modified a '.l' file."
+ echo "You may want to install the Fast Lexical Analyzer package:"
+ echo "<$flex_URL>"
+ ;;
+ help2man*)
+ echo "You should only need it if you modified a dependency" \
+ "of a man page."
+ echo "You may want to install the GNU Help2man package:"
+ echo "<$gnu_software_URL/help2man/>"
+ ;;
+ makeinfo*)
+ echo "You should only need it if you modified a '.texi' file, or"
+ echo "any other file indirectly affecting the aspect of the manual."
+ echo "You might want to install the Texinfo package:"
+ echo "<$gnu_software_URL/texinfo/>"
+ echo "The spurious makeinfo call might also be the consequence of"
+ echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might"
+ echo "want to install GNU make:"
+ echo "<$gnu_software_URL/make/>"
+ ;;
+ *)
+ echo "You might have modified some files without having the proper"
+ echo "tools for further handling them. Check the 'README' file, it"
+ echo "often tells you about the needed prerequisites for installing"
+ echo "this package. You may also peek at any GNU archive site, in"
+ echo "case some other package contains this missing '$1' program."
+ ;;
+ esac
+}
+
+give_advice "$1" | sed -e '1s/^/WARNING: /' \
+ -e '2,$s/^/ /' >&2
+
+# Propagate the correct exit status (expected to be 127 for a program
+# not found, 63 for a program that failed due to version mismatch).
+exit $st
# 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-time-zone: "UTC0"
# time-stamp-end: "; # UTC"
# End:
diff --git a/lib/libedit/src/Makefile.am b/lib/libedit/src/Makefile.am
index 47e8a50265b7..f46c32839b24 100644
--- a/lib/libedit/src/Makefile.am
+++ b/lib/libedit/src/Makefile.am
@@ -1,56 +1,41 @@
-BUILT_SOURCES = vi.h emacs.h common.h fcns.h help.h fcns.c help.c
-if WIDECHAR
-BUILT_SOURCES += tokenizern.c historyn.c
-endif
+BUILT_SOURCES = vi.h emacs.h common.h fcns.h help.h func.h
AHDR= vi.h emacs.h common.h
ASRC= $(srcdir)/vi.c $(srcdir)/emacs.c $(srcdir)/common.c
-vi.h: Makefile $(srcdir)/vi.c
+vi.h: Makefile $(srcdir)/makelist $(srcdir)/vi.c
AWK=$(AWK) sh $(srcdir)/makelist -h $(srcdir)/vi.c > $@
-emacs.h: Makefile $(srcdir)/emacs.c
+emacs.h: Makefile $(srcdir)/makelist $(srcdir)/emacs.c
AWK=$(AWK) sh $(srcdir)/makelist -h $(srcdir)/emacs.c > $@
-common.h: Makefile $(srcdir)/common.c
+common.h: Makefile $(srcdir)/makelist $(srcdir)/common.c
AWK=$(AWK) sh $(srcdir)/makelist -h $(srcdir)/common.c > $@
-fcns.h: Makefile $(AHDR)
+fcns.h: Makefile $(srcdir)/makelist $(AHDR)
AWK=$(AWK) sh $(srcdir)/makelist -fh $(AHDR) > $@
-help.h: Makefile $(ASRC)
- AWK=$(AWK) sh $(srcdir)/makelist -bh $(ASRC) > $@
-
-fcns.c: Makefile $(AHDR)
+func.h: Makefile $(srcdir)/makelist $(AHDR)
AWK=$(AWK) sh $(srcdir)/makelist -fc $(AHDR) > $@
-help.c: Makefile $(ASRC)
- AWK=$(AWK) sh $(srcdir)/makelist -bc $(ASRC) > $@
-
-tokenizern.c: Makefile $(srcdir)/tokenizer.c
- AWK=$(AWK) sh $(srcdir)/makelist -n $(srcdir)/tokenizer.c > $@
-
-historyn.c: Makefile $(srcdir)/history.c
- AWK=$(AWK) sh $(srcdir)/makelist -n $(srcdir)/history.c > $@
+help.h: Makefile $(srcdir)/makelist $(ASRC)
+ AWK=$(AWK) sh $(srcdir)/makelist -bh $(ASRC) > $@
CLEANFILES = $(BUILT_SOURCES)
lib_LTLIBRARIES = libheimedit.la
-libheimedit_la_SOURCES = chared.c common.c el.c emacs.c hist.c key.c map.c chartype.c parse.c \
- prompt.c read.c refresh.c search.c sig.c term.c tty.c vi.c \
- fgetln.c strlcat.c strlcpy.c unvis.c vis.c wcsdup.c tokenizer.c \
- history.c filecomplete.c readline.c chared.h el.h hist.h \
- histedit.h key.h map.h chartype.h parse.h prompt.h read.h refresh.h \
- search.h sig.h sys.h el_term.h tty.h vis.h filecomplete.h \
- editline/readline.h
-if WIDECHAR
-libheimedit_la_SOURCES += eln.c
-endif
+libheimedit_la_SOURCES = \
+ chared.c chartype.c common.c el.c eln.c emacs.c filecomplete.c hist.c \
+ history.c historyn.c keymacro.c map.c parse.c prompt.c read.c readline.c \
+ refresh.c search.c sig.c terminal.c tokenizer.c tokenizern.c tty.c \
+ unvis.c vi.c vis.c wcsdup.c \
+ chared.h chartype.h readline/readline.h el.h filecomplete.h \
+ histedit.h hist.h keymacro.h map.h parse.h prompt.h read.h \
+ refresh.h search.h sig.h sys.h terminal.h tty.h vis.h
EXTRA_DIST = makelist shlib_version
-#nobase_include_HEADERS = histedit.h editline/readline.h
-EXTRA_DIST += histedit.h editline/readline.h
+EXTRA_DIST += histedit.h readline/readline.h
nodist_libheimedit_la_SOURCES = $(BUILT_SOURCES)
diff --git a/lib/libedit/src/Makefile.in b/lib/libedit/src/Makefile.in
index f68759ee7727..db7091d89ef1 100644
--- a/lib/libedit/src/Makefile.in
+++ b/lib/libedit/src/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -16,6 +15,61 @@
@SET_MAKE@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -34,15 +88,13 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-@WIDECHAR_TRUE@am__append_1 = tokenizern.c historyn.c
-@WIDECHAR_TRUE@am__append_2 = eln.c
subdir = src
-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/acinclude.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
@@ -68,35 +120,44 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libheimedit_la_LIBADD =
-am__libheimedit_la_SOURCES_DIST = chared.c common.c el.c emacs.c \
- hist.c key.c map.c chartype.c parse.c prompt.c read.c \
- refresh.c search.c sig.c term.c tty.c vi.c fgetln.c strlcat.c \
- strlcpy.c unvis.c vis.c wcsdup.c tokenizer.c history.c \
- filecomplete.c readline.c chared.h el.h hist.h histedit.h \
- key.h map.h chartype.h parse.h prompt.h read.h refresh.h \
- search.h sig.h sys.h el_term.h tty.h vis.h filecomplete.h \
- editline/readline.h eln.c
-@WIDECHAR_TRUE@am__objects_1 = eln.lo
-am_libheimedit_la_OBJECTS = chared.lo common.lo el.lo emacs.lo hist.lo \
- key.lo map.lo chartype.lo parse.lo prompt.lo read.lo \
- refresh.lo search.lo sig.lo term.lo tty.lo vi.lo fgetln.lo \
- strlcat.lo strlcpy.lo unvis.lo vis.lo wcsdup.lo tokenizer.lo \
- history.lo filecomplete.lo readline.lo $(am__objects_1)
-@WIDECHAR_TRUE@am__objects_2 = tokenizern.lo historyn.lo
-am__objects_3 = fcns.lo help.lo $(am__objects_2)
-nodist_libheimedit_la_OBJECTS = $(am__objects_3)
+am_libheimedit_la_OBJECTS = chared.lo chartype.lo common.lo el.lo \
+ eln.lo emacs.lo filecomplete.lo hist.lo history.lo historyn.lo \
+ keymacro.lo map.lo parse.lo prompt.lo read.lo readline.lo \
+ refresh.lo search.lo sig.lo terminal.lo tokenizer.lo \
+ tokenizern.lo tty.lo unvis.lo vi.lo vis.lo wcsdup.lo
+am__objects_1 =
+nodist_libheimedit_la_OBJECTS = $(am__objects_1)
libheimedit_la_OBJECTS = $(am_libheimedit_la_OBJECTS) \
$(nodist_libheimedit_la_OBJECTS)
-AM_V_lt = $(am__v_lt_$(V))
-am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
am__v_lt_0 = --silent
+am__v_lt_1 =
libheimedit_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
$(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
$(AM_CFLAGS) $(CFLAGS) $(libheimedit_la_LDFLAGS) $(LDFLAGS) -o \
$@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
@@ -107,29 +168,47 @@ LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
$(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
$(AM_CFLAGS) $(CFLAGS)
-AM_V_CC = $(am__v_CC_$(V))
-am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
-am__v_CC_0 = @echo " CC " $@;
-AM_V_at = $(am__v_at_$(V))
-am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
-am__v_at_0 = @
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(AM_LDFLAGS) $(LDFLAGS) -o $@
-AM_V_CCLD = $(am__v_CCLD_$(V))
-am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
-am__v_CCLD_0 = @echo " CCLD " $@;
-AM_V_GEN = $(am__v_GEN_$(V))
-am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
-am__v_GEN_0 = @echo " GEN " $@;
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(libheimedit_la_SOURCES) $(nodist_libheimedit_la_SOURCES)
-DIST_SOURCES = $(am__libheimedit_la_SOURCES_DIST)
+DIST_SOURCES = $(libheimedit_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
-ALLOCA = @ALLOCA@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
@@ -168,6 +247,7 @@ LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
LT_VERSION = @LT_VERSION@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
@@ -246,21 +326,21 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-BUILT_SOURCES = vi.h emacs.h common.h fcns.h help.h fcns.c help.c \
- $(am__append_1)
+BUILT_SOURCES = vi.h emacs.h common.h fcns.h help.h func.h
AHDR = vi.h emacs.h common.h
ASRC = $(srcdir)/vi.c $(srcdir)/emacs.c $(srcdir)/common.c
CLEANFILES = $(BUILT_SOURCES)
lib_LTLIBRARIES = libheimedit.la
-libheimedit_la_SOURCES = chared.c common.c el.c emacs.c hist.c key.c \
- map.c chartype.c parse.c prompt.c read.c refresh.c search.c \
- sig.c term.c tty.c vi.c fgetln.c strlcat.c strlcpy.c unvis.c \
- vis.c wcsdup.c tokenizer.c history.c filecomplete.c readline.c \
- chared.h el.h hist.h histedit.h key.h map.h chartype.h parse.h \
- prompt.h read.h refresh.h search.h sig.h sys.h el_term.h tty.h \
- vis.h filecomplete.h editline/readline.h $(am__append_2)
-#nobase_include_HEADERS = histedit.h editline/readline.h
-EXTRA_DIST = makelist shlib_version histedit.h editline/readline.h
+libheimedit_la_SOURCES = \
+ chared.c chartype.c common.c el.c eln.c emacs.c filecomplete.c hist.c \
+ history.c historyn.c keymacro.c map.c parse.c prompt.c read.c readline.c \
+ refresh.c search.c sig.c terminal.c tokenizer.c tokenizern.c tty.c \
+ unvis.c vi.c vis.c wcsdup.c \
+ chared.h chartype.h readline/readline.h el.h filecomplete.h \
+ histedit.h hist.h keymacro.h map.h parse.h prompt.h read.h \
+ refresh.h search.h sig.h sys.h terminal.h tty.h vis.h
+
+EXTRA_DIST = makelist shlib_version histedit.h readline/readline.h
nodist_libheimedit_la_SOURCES = $(BUILT_SOURCES)
libheimedit_la_LDFLAGS = -no-undefined -version-info $(LT_VERSION)
all: $(BUILT_SOURCES)
@@ -280,7 +360,6 @@ $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --gnu src/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -298,9 +377,9 @@ $(top_srcdir)/configure: $(am__configure_deps)
$(ACLOCAL_M4): $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -308,6 +387,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -323,13 +404,16 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libheimedit.la: $(libheimedit_la_OBJECTS) $(libheimedit_la_DEPENDENCIES)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libheimedit.la: $(libheimedit_la_OBJECTS) $(libheimedit_la_DEPENDENCIES) $(EXTRA_libheimedit_la_DEPENDENCIES)
$(AM_V_CCLD)$(libheimedit_la_LINK) -rpath $(libdir) $(libheimedit_la_OBJECTS) $(libheimedit_la_LIBADD) $(LIBS)
mostlyclean-compile:
@@ -344,14 +428,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/el.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eln.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/emacs.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fcns.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fgetln.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filecomplete.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/help.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hist.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/history.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/historyn.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/key.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keymacro.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/map.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/prompt.Plo@am__quote@
@@ -360,9 +441,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/refresh.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/search.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sig.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcat.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strlcpy.Plo@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/term.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tokenizer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tokenizern.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tty.Plo@am__quote@
@@ -374,26 +453,23 @@ distclean-compile:
.c.o:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -401,26 +477,15 @@ mostlyclean-libtool:
clean-libtool:
-rm -rf .libs _libs
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -432,15 +497,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -449,6 +510,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -502,10 +578,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -592,47 +673,40 @@ uninstall-am: uninstall-libLTLIBRARIES
.MAKE: all check install install-am install-strip
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
- clean-libLTLIBRARIES clean-libtool ctags distclean \
- distclean-compile distclean-generic distclean-libtool \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am install-dvi \
- install-dvi-am install-exec install-exec-am install-html \
- install-html-am install-info install-info-am \
- install-libLTLIBRARIES install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
+.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-libLTLIBRARIES
+ tags tags-am uninstall uninstall-am uninstall-libLTLIBRARIES
+
+.PRECIOUS: Makefile
-vi.h: Makefile $(srcdir)/vi.c
+vi.h: Makefile $(srcdir)/makelist $(srcdir)/vi.c
AWK=$(AWK) sh $(srcdir)/makelist -h $(srcdir)/vi.c > $@
-emacs.h: Makefile $(srcdir)/emacs.c
+emacs.h: Makefile $(srcdir)/makelist $(srcdir)/emacs.c
AWK=$(AWK) sh $(srcdir)/makelist -h $(srcdir)/emacs.c > $@
-common.h: Makefile $(srcdir)/common.c
+common.h: Makefile $(srcdir)/makelist $(srcdir)/common.c
AWK=$(AWK) sh $(srcdir)/makelist -h $(srcdir)/common.c > $@
-fcns.h: Makefile $(AHDR)
+fcns.h: Makefile $(srcdir)/makelist $(AHDR)
AWK=$(AWK) sh $(srcdir)/makelist -fh $(AHDR) > $@
-help.h: Makefile $(ASRC)
- AWK=$(AWK) sh $(srcdir)/makelist -bh $(ASRC) > $@
-
-fcns.c: Makefile $(AHDR)
+func.h: Makefile $(srcdir)/makelist $(AHDR)
AWK=$(AWK) sh $(srcdir)/makelist -fc $(AHDR) > $@
-help.c: Makefile $(ASRC)
- AWK=$(AWK) sh $(srcdir)/makelist -bc $(ASRC) > $@
-
-tokenizern.c: Makefile $(srcdir)/tokenizer.c
- AWK=$(AWK) sh $(srcdir)/makelist -n $(srcdir)/tokenizer.c > $@
-
-historyn.c: Makefile $(srcdir)/history.c
- AWK=$(AWK) sh $(srcdir)/makelist -n $(srcdir)/history.c > $@
+help.h: Makefile $(srcdir)/makelist $(ASRC)
+ AWK=$(AWK) sh $(srcdir)/makelist -bh $(ASRC) > $@
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/lib/libedit/src/chared.c b/lib/libedit/src/chared.c
index ec600a7bd389..9d46ba68feea 100644
--- a/lib/libedit/src/chared.c
+++ b/lib/libedit/src/chared.c
@@ -1,4 +1,4 @@
-/* $NetBSD: chared.c,v 1.29 2010/08/28 15:44:59 christos Exp $ */
+/* $NetBSD: chared.c,v 1.56 2016/05/22 19:44:26 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,17 +37,20 @@
#if 0
static char sccsid[] = "@(#)chared.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: chared.c,v 1.29 2010/08/28 15:44:59 christos Exp $");
+__RCSID("$NetBSD: chared.c,v 1.56 2016/05/22 19:44:26 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
* chared.c: Character editor utilities
*/
+#include <ctype.h>
#include <stdlib.h>
-#include "el.h"
+#include <string.h>
-private void ch__clearmacro (EditLine *);
+#include "el.h"
+#include "common.h"
+#include "fcns.h"
/* value to leave unused in line buffer */
#define EL_LEAVE 2
@@ -55,7 +58,7 @@ private void ch__clearmacro (EditLine *);
/* cv_undo():
* Handle state for the vi undo command
*/
-protected void
+libedit_private void
cv_undo(EditLine *el)
{
c_undo_t *vu = &el->el_chared.c_undo;
@@ -63,8 +66,8 @@ cv_undo(EditLine *el)
size_t size;
/* Save entire line for undo */
- size = el->el_line.lastchar - el->el_line.buffer;
- vu->len = size;
+ size = (size_t)(el->el_line.lastchar - el->el_line.buffer);
+ vu->len = (ssize_t)size;
vu->cursor = (int)(el->el_line.cursor - el->el_line.buffer);
(void)memcpy(vu->buf, el->el_line.buffer, size * sizeof(*vu->buf));
@@ -79,12 +82,12 @@ cv_undo(EditLine *el)
/* cv_yank():
* Save yank/delete data for paste
*/
-protected void
-cv_yank(EditLine *el, const Char *ptr, int size)
+libedit_private void
+cv_yank(EditLine *el, const wchar_t *ptr, int size)
{
c_kill_t *k = &el->el_chared.c_kill;
- (void)memcpy(k->buf, ptr, size * sizeof(*k->buf));
+ (void)memcpy(k->buf, ptr, (size_t)size * sizeof(*k->buf));
k->last = k->buf + size;
}
@@ -92,10 +95,10 @@ cv_yank(EditLine *el, const Char *ptr, int size)
/* c_insert():
* Insert num characters
*/
-protected void
+libedit_private void
c_insert(EditLine *el, int num)
{
- Char *cp;
+ wchar_t *cp;
if (el->el_line.lastchar + num >= el->el_line.limit) {
if (!ch_enlargebufs(el, (size_t)num))
@@ -114,7 +117,7 @@ c_insert(EditLine *el, int num)
/* c_delafter():
* Delete num characters after the cursor
*/
-protected void
+libedit_private void
c_delafter(EditLine *el, int num)
{
@@ -127,7 +130,7 @@ c_delafter(EditLine *el, int num)
}
if (num > 0) {
- Char *cp;
+ wchar_t *cp;
for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
*cp = cp[num];
@@ -140,10 +143,10 @@ c_delafter(EditLine *el, int num)
/* c_delafter1():
* Delete the character after the cursor, do not yank
*/
-protected void
+libedit_private void
c_delafter1(EditLine *el)
{
- Char *cp;
+ wchar_t *cp;
for (cp = el->el_line.cursor; cp <= el->el_line.lastchar; cp++)
*cp = cp[1];
@@ -155,7 +158,7 @@ c_delafter1(EditLine *el)
/* c_delbefore():
* Delete num characters before the cursor
*/
-protected void
+libedit_private void
c_delbefore(EditLine *el, int num)
{
@@ -168,7 +171,7 @@ c_delbefore(EditLine *el, int num)
}
if (num > 0) {
- Char *cp;
+ wchar_t *cp;
for (cp = el->el_line.cursor - num;
cp <= el->el_line.lastchar;
@@ -183,10 +186,10 @@ c_delbefore(EditLine *el, int num)
/* c_delbefore1():
* Delete the character before the cursor, do not yank
*/
-protected void
+libedit_private void
c_delbefore1(EditLine *el)
{
- Char *cp;
+ wchar_t *cp;
for (cp = el->el_line.cursor - 1; cp <= el->el_line.lastchar; cp++)
*cp = cp[1];
@@ -198,22 +201,22 @@ c_delbefore1(EditLine *el)
/* ce__isword():
* Return if p is part of a word according to emacs
*/
-protected int
-ce__isword(Int p)
+libedit_private int
+ce__isword(wint_t p)
{
- return (Isalnum(p) || Strchr(STR("*?_-.[]~="), p) != NULL);
+ return iswalnum(p) || wcschr(L"*?_-.[]~=", p) != NULL;
}
/* cv__isword():
* Return if p is part of a word according to vi
*/
-protected int
-cv__isword(Int p)
+libedit_private int
+cv__isword(wint_t p)
{
- if (Isalnum(p) || p == '_')
+ if (iswalnum(p) || p == L'_')
return 1;
- if (Isgraph(p))
+ if (iswgraph(p))
return 2;
return 0;
}
@@ -222,18 +225,18 @@ cv__isword(Int p)
/* cv__isWord():
* Return if p is part of a big word according to vi
*/
-protected int
-cv__isWord(Int p)
+libedit_private int
+cv__isWord(wint_t p)
{
- return (!Isspace(p));
+ return !iswspace(p);
}
/* c__prev_word():
* Find the previous word
*/
-protected Char *
-c__prev_word(Char *p, Char *low, int n, int (*wtest)(Int))
+libedit_private wchar_t *
+c__prev_word(wchar_t *p, wchar_t *low, int n, int (*wtest)(wint_t))
{
p--;
@@ -249,15 +252,15 @@ c__prev_word(Char *p, Char *low, int n, int (*wtest)(Int))
if (p < low)
p = low;
/* cp now points where we want it */
- return (p);
+ return p;
}
/* c__next_word():
* Find the next word
*/
-protected Char *
-c__next_word(Char *p, Char *high, int n, int (*wtest)(Int))
+libedit_private wchar_t *
+c__next_word(wchar_t *p, wchar_t *high, int n, int (*wtest)(wint_t))
{
while (n--) {
while ((p < high) && !(*wtest)(*p))
@@ -268,14 +271,15 @@ c__next_word(Char *p, Char *high, int n, int (*wtest)(Int))
if (p > high)
p = high;
/* p now points where we want it */
- return (p);
+ return p;
}
/* cv_next_word():
* Find the next word vi style
*/
-protected Char *
-cv_next_word(EditLine *el, Char *p, Char *high, int n, int (*wtest)(Int))
+libedit_private wchar_t *
+cv_next_word(EditLine *el, wchar_t *p, wchar_t *high, int n,
+ int (*wtest)(wint_t))
{
int test;
@@ -288,29 +292,29 @@ cv_next_word(EditLine *el, Char *p, Char *high, int n, int (*wtest)(Int))
* trailing whitespace! This is not what 'w' does..
*/
if (n || el->el_chared.c_vcmd.action != (DELETE|INSERT))
- while ((p < high) && Isspace(*p))
+ while ((p < high) && iswspace(*p))
p++;
}
/* p now points where we want it */
if (p > high)
- return (high);
+ return high;
else
- return (p);
+ return p;
}
/* cv_prev_word():
* Find the previous word vi style
*/
-protected Char *
-cv_prev_word(Char *p, Char *low, int n, int (*wtest)(Int))
+libedit_private wchar_t *
+cv_prev_word(wchar_t *p, wchar_t *low, int n, int (*wtest)(wint_t))
{
int test;
p--;
while (n--) {
- while ((p > low) && Isspace(*p))
+ while ((p > low) && iswspace(*p))
p--;
test = (*wtest)(*p);
while ((p >= low) && (*wtest)(*p) == test)
@@ -320,51 +324,16 @@ cv_prev_word(Char *p, Char *low, int n, int (*wtest)(Int))
/* p now points where we want it */
if (p < low)
- return (low);
+ return low;
else
- return (p);
+ return p;
}
-#ifdef notdef
-/* c__number():
- * Ignore character p points to, return number appearing after that.
- * A '$' by itself means a big number; "$-" is for negative; '^' means 1.
- * Return p pointing to last char used.
- */
-protected Char *
-c__number(
- Char *p, /* character position */
- int *num, /* Return value */
- int dval) /* dval is the number to subtract from like $-3 */
-{
- int i;
- int sign = 1;
-
- if (*++p == '^') {
- *num = 1;
- return (p);
- }
- if (*p == '$') {
- if (*++p != '-') {
- *num = 0x7fffffff; /* Handle $ */
- return (--p);
- }
- sign = -1; /* Handle $- */
- ++p;
- }
- /* XXX: this assumes ASCII compatible digits */
- for (i = 0; Isdigit(*p); i = 10 * i + *p++ - '0')
- continue;
- *num = (sign < 0 ? dval - i : i);
- return (--p);
-}
-#endif
-
/* cv_delfini():
* Finish vi delete action
*/
-protected void
+libedit_private void
cv_delfini(EditLine *el)
{
int size;
@@ -399,40 +368,18 @@ cv_delfini(EditLine *el)
}
-#ifdef notdef
-/* ce__endword():
- * Go to the end of this word according to emacs
- */
-protected Char *
-ce__endword(Char *p, Char *high, int n)
-{
- p++;
-
- while (n--) {
- while ((p < high) && Isspace(*p))
- p++;
- while ((p < high) && !Isspace(*p))
- p++;
- }
-
- p--;
- return (p);
-}
-#endif
-
-
/* cv__endword():
* Go to the end of this word according to vi
*/
-protected Char *
-cv__endword(Char *p, Char *high, int n, int (*wtest)(Int))
+libedit_private wchar_t *
+cv__endword(wchar_t *p, wchar_t *high, int n, int (*wtest)(wint_t))
{
int test;
p++;
while (n--) {
- while ((p < high) && Isspace(*p))
+ while ((p < high) && iswspace(*p))
p++;
test = (*wtest)(*p);
@@ -440,21 +387,19 @@ cv__endword(Char *p, Char *high, int n, int (*wtest)(Int))
p++;
}
p--;
- return (p);
+ return p;
}
/* ch_init():
* Initialize the character editor
*/
-protected int
+libedit_private int
ch_init(EditLine *el)
{
- c_macro_t *ma = &el->el_chared.c_macro;
-
el->el_line.buffer = el_malloc(EL_BUFSIZ *
sizeof(*el->el_line.buffer));
if (el->el_line.buffer == NULL)
- return (-1);
+ return -1;
(void) memset(el->el_line.buffer, 0, EL_BUFSIZ *
sizeof(*el->el_line.buffer));
@@ -465,7 +410,7 @@ ch_init(EditLine *el)
el->el_chared.c_undo.buf = el_malloc(EL_BUFSIZ *
sizeof(*el->el_chared.c_undo.buf));
if (el->el_chared.c_undo.buf == NULL)
- return (-1);
+ return -1;
(void) memset(el->el_chared.c_undo.buf, 0, EL_BUFSIZ *
sizeof(*el->el_chared.c_undo.buf));
el->el_chared.c_undo.len = -1;
@@ -473,7 +418,7 @@ ch_init(EditLine *el)
el->el_chared.c_redo.buf = el_malloc(EL_BUFSIZ *
sizeof(*el->el_chared.c_redo.buf));
if (el->el_chared.c_redo.buf == NULL)
- return (-1);
+ return -1;
el->el_chared.c_redo.pos = el->el_chared.c_redo.buf;
el->el_chared.c_redo.lim = el->el_chared.c_redo.buf + EL_BUFSIZ;
el->el_chared.c_redo.cmd = ED_UNASSIGNED;
@@ -484,13 +429,15 @@ ch_init(EditLine *el)
el->el_chared.c_kill.buf = el_malloc(EL_BUFSIZ *
sizeof(*el->el_chared.c_kill.buf));
if (el->el_chared.c_kill.buf == NULL)
- return (-1);
+ return -1;
(void) memset(el->el_chared.c_kill.buf, 0, EL_BUFSIZ *
sizeof(*el->el_chared.c_kill.buf));
el->el_chared.c_kill.mark = el->el_line.buffer;
el->el_chared.c_kill.last = el->el_chared.c_kill.buf;
el->el_chared.c_resizefun = NULL;
el->el_chared.c_resizearg = NULL;
+ el->el_chared.c_aliasfun = NULL;
+ el->el_chared.c_aliasarg = NULL;
el->el_map.current = el->el_map.key;
@@ -500,19 +447,14 @@ ch_init(EditLine *el)
el->el_state.argument = 1;
el->el_state.lastcmd = ED_UNASSIGNED;
- ma->level = -1;
- ma->offset = 0;
- ma->macro = el_malloc(EL_MAXMACRO * sizeof(*ma->macro));
- if (ma->macro == NULL)
- return (-1);
- return (0);
+ return 0;
}
/* ch_reset():
* Reset the character editor
*/
-protected void
-ch_reset(EditLine *el, int mclear)
+libedit_private void
+ch_reset(EditLine *el)
{
el->el_line.cursor = el->el_line.buffer;
el->el_line.lastchar = el->el_line.buffer;
@@ -534,30 +476,19 @@ ch_reset(EditLine *el, int mclear)
el->el_state.lastcmd = ED_UNASSIGNED;
el->el_history.eventno = 0;
-
- if (mclear)
- ch__clearmacro(el);
-}
-
-private void
-ch__clearmacro(EditLine *el)
-{
- c_macro_t *ma = &el->el_chared.c_macro;
- while (ma->level >= 0)
- el_free((ptr_t)ma->macro[ma->level--]);
}
/* ch_enlargebufs():
* Enlarge line buffer to be able to hold twice as much characters.
* Returns 1 if successful, 0 if not.
*/
-protected int
+libedit_private int
ch_enlargebufs(EditLine *el, size_t addlen)
{
size_t sz, newsz;
- Char *newbuffer, *oldbuf, *oldkbuf;
+ wchar_t *newbuffer, *oldbuf, *oldkbuf;
- sz = el->el_line.limit - el->el_line.buffer + EL_LEAVE;
+ sz = (size_t)(el->el_line.limit - el->el_line.buffer + EL_LEAVE);
newsz = sz * 2;
/*
* If newly required length is longer than current buffer, we need
@@ -589,7 +520,8 @@ ch_enlargebufs(EditLine *el, size_t addlen)
/*
* Reallocate kill buffer.
*/
- newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz * sizeof(*newbuffer));
+ newbuffer = el_realloc(el->el_chared.c_kill.buf, newsz *
+ sizeof(*newbuffer));
if (!newbuffer)
return 0;
@@ -639,53 +571,51 @@ ch_enlargebufs(EditLine *el, size_t addlen)
/* ch_end():
* Free the data structures used by the editor
*/
-protected void
+libedit_private void
ch_end(EditLine *el)
{
- el_free((ptr_t) el->el_line.buffer);
+ el_free(el->el_line.buffer);
el->el_line.buffer = NULL;
el->el_line.limit = NULL;
- el_free((ptr_t) el->el_chared.c_undo.buf);
+ el_free(el->el_chared.c_undo.buf);
el->el_chared.c_undo.buf = NULL;
- el_free((ptr_t) el->el_chared.c_redo.buf);
+ el_free(el->el_chared.c_redo.buf);
el->el_chared.c_redo.buf = NULL;
el->el_chared.c_redo.pos = NULL;
el->el_chared.c_redo.lim = NULL;
el->el_chared.c_redo.cmd = ED_UNASSIGNED;
- el_free((ptr_t) el->el_chared.c_kill.buf);
+ el_free(el->el_chared.c_kill.buf);
el->el_chared.c_kill.buf = NULL;
- ch_reset(el, 1);
- el_free((ptr_t) el->el_chared.c_macro.macro);
- el->el_chared.c_macro.macro = NULL;
+ ch_reset(el);
}
/* el_insertstr():
* Insert string at cursorI
*/
-public int
-FUN(el,insertstr)(EditLine *el, const Char *s)
+int
+el_winsertstr(EditLine *el, const wchar_t *s)
{
size_t len;
- if ((len = Strlen(s)) == 0)
- return (-1);
+ if (s == NULL || (len = wcslen(s)) == 0)
+ return -1;
if (el->el_line.lastchar + len >= el->el_line.limit) {
if (!ch_enlargebufs(el, len))
- return (-1);
+ return -1;
}
c_insert(el, (int)len);
while (*s)
*el->el_line.cursor++ = *s++;
- return (0);
+ return 0;
}
/* el_deletestr():
* Delete num characters before the cursor
*/
-public void
+void
el_deletestr(EditLine *el, int n)
{
if (n <= 0)
@@ -700,19 +630,37 @@ el_deletestr(EditLine *el, int n)
el->el_line.cursor = el->el_line.buffer;
}
+/* el_cursor():
+ * Move the cursor to the left or the right of the current position
+ */
+int
+el_cursor(EditLine *el, int n)
+{
+ if (n == 0)
+ goto out;
+
+ el->el_line.cursor += n;
+
+ if (el->el_line.cursor < el->el_line.buffer)
+ el->el_line.cursor = el->el_line.buffer;
+ if (el->el_line.cursor > el->el_line.lastchar)
+ el->el_line.cursor = el->el_line.lastchar;
+out:
+ return (int)(el->el_line.cursor - el->el_line.buffer);
+}
+
/* c_gets():
* Get a string
*/
-protected int
-c_gets(EditLine *el, Char *buf, const Char *prompt)
+libedit_private int
+c_gets(EditLine *el, wchar_t *buf, const wchar_t *prompt)
{
- Char ch;
ssize_t len;
- Char *cp = el->el_line.buffer;
+ wchar_t *cp = el->el_line.buffer, ch;
if (prompt) {
- len = Strlen(prompt);
- (void)memcpy(cp, prompt, len * sizeof(*cp));
+ len = (ssize_t)wcslen(prompt);
+ (void)memcpy(cp, prompt, (size_t)len * sizeof(*cp));
cp += len;
}
len = 0;
@@ -723,7 +671,7 @@ c_gets(EditLine *el, Char *buf, const Char *prompt)
el->el_line.lastchar = cp + 1;
re_refresh(el);
- if (FUN(el,getc)(el, &ch) != 1) {
+ if (el_wgetc(el, &ch) != 1) {
ed_end_of_file(el, 0);
len = -1;
break;
@@ -731,24 +679,25 @@ c_gets(EditLine *el, Char *buf, const Char *prompt)
switch (ch) {
- case 0010: /* Delete and backspace */
+ case L'\b': /* Delete and backspace */
case 0177:
if (len == 0) {
len = -1;
break;
}
+ len--;
cp--;
continue;
case 0033: /* ESC */
- case '\r': /* Newline */
- case '\n':
+ case L'\r': /* Newline */
+ case L'\n':
buf[len] = ch;
break;
default:
- if (len >= EL_BUFSIZ - 16)
- term_beep(el);
+ if (len >= (ssize_t)(EL_BUFSIZ - 16))
+ terminal_beep(el);
else {
buf[len++] = ch;
*cp++ = ch;
@@ -768,16 +717,16 @@ c_gets(EditLine *el, Char *buf, const Char *prompt)
/* c_hpos():
* Return the current horizontal position of the cursor
*/
-protected int
+libedit_private int
c_hpos(EditLine *el)
{
- Char *ptr;
+ wchar_t *ptr;
/*
* Find how many characters till the beginning of this line.
*/
if (el->el_line.cursor == el->el_line.buffer)
- return (0);
+ return 0;
else {
for (ptr = el->el_line.cursor - 1;
ptr >= el->el_line.buffer && *ptr != '\n';
@@ -787,10 +736,18 @@ c_hpos(EditLine *el)
}
}
-protected int
+libedit_private int
ch_resizefun(EditLine *el, el_zfunc_t f, void *a)
{
el->el_chared.c_resizefun = f;
el->el_chared.c_resizearg = a;
return 0;
}
+
+libedit_private int
+ch_aliasfun(EditLine *el, el_afunc_t f, void *a)
+{
+ el->el_chared.c_aliasfun = f;
+ el->el_chared.c_aliasarg = a;
+ return 0;
+}
diff --git a/lib/libedit/src/chared.h b/lib/libedit/src/chared.h
index 176475ac8f05..39f7d5173a7c 100644
--- a/lib/libedit/src/chared.h
+++ b/lib/libedit/src/chared.h
@@ -1,4 +1,4 @@
-/* $NetBSD: chared.h,v 1.21 2010/08/28 15:44:59 christos Exp $ */
+/* $NetBSD: chared.h,v 1.30 2016/05/22 19:44:26 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,13 +40,6 @@
#ifndef _h_el_chared
#define _h_el_chared
-#include <ctype.h>
-#include <string.h>
-
-#include "histedit.h"
-
-#define EL_MAXMACRO 10
-
/*
* This is an issue of basic "vi" look-and-feel. Defining VI_MOVE works
* like real vi: i.e. the transition from command<->insert modes moves
@@ -59,29 +52,22 @@
*/
#define VI_MOVE
-
-typedef struct c_macro_t {
- int level;
- int offset;
- Char **macro;
-} c_macro_t;
-
/*
* Undo information for vi - no undo in emacs (yet)
*/
typedef struct c_undo_t {
ssize_t len; /* length of saved line */
int cursor; /* position of saved cursor */
- Char *buf; /* full saved text */
+ wchar_t *buf; /* full saved text */
} c_undo_t;
/* redo for vi */
typedef struct c_redo_t {
- Char *buf; /* redo insert key sequence */
- Char *pos;
- Char *lim;
+ wchar_t *buf; /* redo insert key sequence */
+ wchar_t *pos;
+ wchar_t *lim;
el_action_t cmd; /* command to redo */
- Char ch; /* char that invoked it */
+ wchar_t ch; /* char that invoked it */
int count;
int action; /* from cv_action() */
} c_redo_t;
@@ -91,19 +77,20 @@ typedef struct c_redo_t {
*/
typedef struct c_vcmd_t {
int action;
- Char *pos;
+ wchar_t *pos;
} c_vcmd_t;
/*
* Kill buffer for emacs
*/
typedef struct c_kill_t {
- Char *buf;
- Char *last;
- Char *mark;
+ wchar_t *buf;
+ wchar_t *last;
+ wchar_t *mark;
} c_kill_t;
typedef void (*el_zfunc_t)(EditLine *, void *);
+typedef const char *(*el_afunc_t)(void *, const char *);
/*
* Note that we use both data structures because the user can bind
@@ -114,9 +101,10 @@ typedef struct el_chared_t {
c_kill_t c_kill;
c_redo_t c_redo;
c_vcmd_t c_vcmd;
- c_macro_t c_macro;
el_zfunc_t c_resizefun;
+ el_afunc_t c_aliasfun;
void * c_resizearg;
+ void * c_aliasarg;
} el_chared_t;
@@ -136,36 +124,32 @@ typedef struct el_chared_t {
#define MODE_REPLACE 1
#define MODE_REPLACE_1 2
-#include "common.h"
-#include "vi.h"
-#include "emacs.h"
-#include "search.h"
-#include "fcns.h"
-
-
-protected int cv__isword(Int);
-protected int cv__isWord(Int);
-protected void cv_delfini(EditLine *);
-protected Char *cv__endword(Char *, Char *, int, int (*)(Int));
-protected int ce__isword(Int);
-protected void cv_undo(EditLine *);
-protected void cv_yank(EditLine *, const Char *, int);
-protected Char *cv_next_word(EditLine*, Char *, Char *, int, int (*)(Int));
-protected Char *cv_prev_word(Char *, Char *, int, int (*)(Int));
-protected Char *c__next_word(Char *, Char *, int, int (*)(Int));
-protected Char *c__prev_word(Char *, Char *, int, int (*)(Int));
-protected void c_insert(EditLine *, int);
-protected void c_delbefore(EditLine *, int);
-protected void c_delbefore1(EditLine *);
-protected void c_delafter(EditLine *, int);
-protected void c_delafter1(EditLine *);
-protected int c_gets(EditLine *, Char *, const Char *);
-protected int c_hpos(EditLine *);
-
-protected int ch_init(EditLine *);
-protected void ch_reset(EditLine *, int);
-protected int ch_resizefun(EditLine *, el_zfunc_t, void *);
-protected int ch_enlargebufs(EditLine *, size_t);
-protected void ch_end(EditLine *);
+
+libedit_private int cv__isword(wint_t);
+libedit_private int cv__isWord(wint_t);
+libedit_private void cv_delfini(EditLine *);
+libedit_private wchar_t *cv__endword(wchar_t *, wchar_t *, int, int (*)(wint_t));
+libedit_private int ce__isword(wint_t);
+libedit_private void cv_undo(EditLine *);
+libedit_private void cv_yank(EditLine *, const wchar_t *, int);
+libedit_private wchar_t *cv_next_word(EditLine*, wchar_t *, wchar_t *, int,
+ int (*)(wint_t));
+libedit_private wchar_t *cv_prev_word(wchar_t *, wchar_t *, int, int (*)(wint_t));
+libedit_private wchar_t *c__next_word(wchar_t *, wchar_t *, int, int (*)(wint_t));
+libedit_private wchar_t *c__prev_word(wchar_t *, wchar_t *, int, int (*)(wint_t));
+libedit_private void c_insert(EditLine *, int);
+libedit_private void c_delbefore(EditLine *, int);
+libedit_private void c_delbefore1(EditLine *);
+libedit_private void c_delafter(EditLine *, int);
+libedit_private void c_delafter1(EditLine *);
+libedit_private int c_gets(EditLine *, wchar_t *, const wchar_t *);
+libedit_private int c_hpos(EditLine *);
+
+libedit_private int ch_init(EditLine *);
+libedit_private void ch_reset(EditLine *);
+libedit_private int ch_resizefun(EditLine *, el_zfunc_t, void *);
+libedit_private int ch_aliasfun(EditLine *, el_afunc_t, void *);
+libedit_private int ch_enlargebufs(EditLine *, size_t);
+libedit_private void ch_end(EditLine *);
#endif /* _h_el_chared */
diff --git a/lib/libedit/src/chartype.c b/lib/libedit/src/chartype.c
index a0adde6f6087..f350dfd2dedd 100644
--- a/lib/libedit/src/chartype.c
+++ b/lib/libedit/src/chartype.c
@@ -1,4 +1,4 @@
-/* $NetBSD: chartype.c,v 1.4 2010/04/15 00:55:57 christos Exp $ */
+/* $NetBSD: chartype.c,v 1.30 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -12,13 +12,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -38,122 +31,132 @@
*/
#include "config.h"
#if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: chartype.c,v 1.4 2010/04/15 00:55:57 christos Exp $");
+__RCSID("$NetBSD: chartype.c,v 1.30 2016/05/09 21:46:56 christos Exp $");
#endif /* not lint && not SCCSID */
-#include "el.h"
+
+#include <ctype.h>
#include <stdlib.h>
+#include <string.h>
+
+#include "el.h"
-#define CT_BUFSIZ 1024
+#define CT_BUFSIZ ((size_t)1024)
-#ifdef WIDECHAR
-protected void
-ct_conv_buff_resize(ct_buffer_t *conv, size_t mincsize, size_t minwsize)
+static int ct_conv_cbuff_resize(ct_buffer_t *, size_t);
+static int ct_conv_wbuff_resize(ct_buffer_t *, size_t);
+
+static int
+ct_conv_cbuff_resize(ct_buffer_t *conv, size_t csize)
{
void *p;
- if (mincsize > conv->csize) {
- conv->csize = mincsize;
- p = el_realloc(conv->cbuff, conv->csize);
- if (p == NULL) {
- conv->csize = 0;
- el_free(conv->cbuff);
- conv->cbuff = NULL;
- } else
- conv->cbuff = p;
+
+ if (csize <= conv->csize)
+ return 0;
+
+ conv->csize = csize;
+
+ p = el_realloc(conv->cbuff, conv->csize * sizeof(*conv->cbuff));
+ if (p == NULL) {
+ conv->csize = 0;
+ el_free(conv->cbuff);
+ conv->cbuff = NULL;
+ return -1;
}
+ conv->cbuff = p;
+ return 0;
+}
- if (minwsize > conv->wsize) {
- conv->wsize = minwsize;
- p = el_realloc(conv->wbuff, conv->wsize);
- if (p == NULL) {
- conv->wsize = 0;
- el_free(conv->wbuff);
- conv->wbuff = NULL;
- } else
- conv->wbuff = p;
+static int
+ct_conv_wbuff_resize(ct_buffer_t *conv, size_t wsize)
+{
+ void *p;
+
+ if (wsize <= conv->wsize)
+ return 0;
+
+ conv->wsize = wsize;
+
+ p = el_realloc(conv->wbuff, conv->wsize * sizeof(*conv->wbuff));
+ if (p == NULL) {
+ conv->wsize = 0;
+ el_free(conv->wbuff);
+ conv->wbuff = NULL;
+ return -1;
}
+ conv->wbuff = p;
+ return 0;
}
-public char *
-ct_encode_string(const Char *s, ct_buffer_t *conv)
+char *
+ct_encode_string(const wchar_t *s, ct_buffer_t *conv)
{
char *dst;
- ssize_t used = 0;
+ ssize_t used;
if (!s)
return NULL;
- if (!conv->cbuff)
- ct_conv_buff_resize(conv, CT_BUFSIZ, 0);
- if (!conv->cbuff)
- return NULL;
dst = conv->cbuff;
- while (*s) {
- used = ct_encode_char(dst, (int)(conv->csize -
- (dst - conv->cbuff)), *s);
- if (used == -1) { /* failed to encode, need more buffer space */
- used = dst - conv->cbuff;
- ct_conv_buff_resize(conv, conv->csize + CT_BUFSIZ, 0);
- if (!conv->cbuff)
+ for (;;) {
+ used = (ssize_t)(dst - conv->cbuff);
+ if ((conv->csize - (size_t)used) < 5) {
+ if (ct_conv_cbuff_resize(conv,
+ conv->csize + CT_BUFSIZ) == -1)
return NULL;
dst = conv->cbuff + used;
- /* don't increment s here - we want to retry it! */
}
- else
- ++s;
+ if (!*s)
+ break;
+ used = ct_encode_char(dst, (size_t)5, *s);
+ if (used == -1) /* failed to encode, need more buffer space */
+ abort();
+ ++s;
dst += used;
}
- if (dst >= (conv->cbuff + conv->csize)) {
- used = dst - conv->cbuff;
- ct_conv_buff_resize(conv, conv->csize + 1, 0);
- if (!conv->cbuff)
- return NULL;
- dst = conv->cbuff + used;
- }
*dst = '\0';
return conv->cbuff;
}
-public Char *
+wchar_t *
ct_decode_string(const char *s, ct_buffer_t *conv)
{
- size_t len = 0;
+ size_t len;
if (!s)
return NULL;
- if (!conv->wbuff)
- ct_conv_buff_resize(conv, 0, CT_BUFSIZ);
- if (!conv->wbuff)
- return NULL;
- len = ct_mbstowcs(0, s, 0);
- if (len > conv->wsize)
- ct_conv_buff_resize(conv, 0, len + 1);
- if (!conv->wbuff)
+ len = mbstowcs(NULL, s, (size_t)0);
+ if (len == (size_t)-1)
return NULL;
- ct_mbstowcs(conv->wbuff, s, conv->wsize);
+
+ if (conv->wsize < ++len)
+ if (ct_conv_wbuff_resize(conv, len + CT_BUFSIZ) == -1)
+ return NULL;
+
+ mbstowcs(conv->wbuff, s, conv->wsize);
return conv->wbuff;
}
-protected Char **
+libedit_private wchar_t **
ct_decode_argv(int argc, const char *argv[], ct_buffer_t *conv)
{
size_t bufspace;
int i;
- Char *p;
- Char **wargv;
+ wchar_t *p;
+ wchar_t **wargv;
ssize_t bytes;
/* Make sure we have enough space in the conversion buffer to store all
* the argv strings. */
for (i = 0, bufspace = 0; i < argc; ++i)
bufspace += argv[i] ? strlen(argv[i]) + 1 : 0;
- ct_conv_buff_resize(conv, 0, bufspace);
- if (!conv->wsize)
- return NULL;
+ if (conv->wsize < ++bufspace)
+ if (ct_conv_wbuff_resize(conv, bufspace + CT_BUFSIZ) == -1)
+ return NULL;
- wargv = el_malloc(argc * sizeof(*wargv));
+ wargv = el_malloc((size_t)argc * sizeof(*wargv));
for (i = 0, p = conv->wbuff; i < argc; ++i) {
if (!argv[i]) { /* don't pass null pointers to mbstowcs */
@@ -161,14 +164,14 @@ ct_decode_argv(int argc, const char *argv[], ct_buffer_t *conv)
continue;
} else {
wargv[i] = p;
- bytes = mbstowcs(p, argv[i], bufspace);
+ bytes = (ssize_t)mbstowcs(p, argv[i], bufspace);
}
if (bytes == -1) {
el_free(wargv);
return NULL;
} else
bytes++; /* include '\0' in the count */
- bufspace -= bytes;
+ bufspace -= (size_t)bytes;
p += bytes;
}
@@ -176,8 +179,8 @@ ct_decode_argv(int argc, const char *argv[], ct_buffer_t *conv)
}
-protected size_t
-ct_enc_width(Char c)
+libedit_private size_t
+ct_enc_width(wchar_t c)
{
/* UTF-8 encoding specific values */
if (c < 0x80)
@@ -192,74 +195,66 @@ ct_enc_width(Char c)
return 0; /* not a valid codepoint */
}
-protected ssize_t
-ct_encode_char(char *dst, size_t len, Char c)
+libedit_private ssize_t
+ct_encode_char(char *dst, size_t len, wchar_t c)
{
ssize_t l = 0;
if (len < ct_enc_width(c))
return -1;
- l = ct_wctomb(dst, c);
+ l = wctomb(dst, c);
if (l < 0) {
- ct_wctomb_reset;
+ wctomb(NULL, L'\0');
l = 0;
}
return l;
}
-#endif
-protected const Char *
-ct_visual_string(const Char *s)
+libedit_private const wchar_t *
+ct_visual_string(const wchar_t *s, ct_buffer_t *conv)
{
- static Char *buff = NULL;
- static size_t buffsize = 0;
- void *p;
- Char *dst;
- ssize_t used = 0;
+ wchar_t *dst;
+ ssize_t used;
if (!s)
return NULL;
- if (!buff) {
- buffsize = CT_BUFSIZ;
- buff = el_malloc(buffsize * sizeof(*buff));
- }
- dst = buff;
+
+ if (ct_conv_wbuff_resize(conv, CT_BUFSIZ) == -1)
+ return NULL;
+
+ used = 0;
+ dst = conv->wbuff;
while (*s) {
- used = ct_visual_char(dst, buffsize - (dst - buff), *s);
- if (used == -1) { /* failed to encode, need more buffer space */
- used = dst - buff;
- buffsize += CT_BUFSIZ;
- p = el_realloc(buff, buffsize * sizeof(*buff));
- if (p == NULL)
- goto out;
- buff = p;
- dst = buff + used;
- /* don't increment s here - we want to retry it! */
+ used = ct_visual_char(dst,
+ conv->wsize - (size_t)(dst - conv->wbuff), *s);
+ if (used != -1) {
+ ++s;
+ dst += used;
+ continue;
}
- else
- ++s;
- dst += used;
+
+ /* failed to encode, need more buffer space */
+ used = dst - conv->wbuff;
+ if (ct_conv_wbuff_resize(conv, conv->wsize + CT_BUFSIZ) == -1)
+ return NULL;
+ dst = conv->wbuff + used;
}
- if (dst >= (buff + buffsize)) { /* sigh */
- buffsize += 1;
- p = el_realloc(buff, buffsize * sizeof(*buff));
- if (p == NULL)
- goto out;
- buff = p;
- dst = buff + buffsize - 1;
+
+ if (dst >= (conv->wbuff + conv->wsize)) { /* sigh */
+ used = dst - conv->wbuff;
+ if (ct_conv_wbuff_resize(conv, conv->wsize + CT_BUFSIZ) == -1)
+ return NULL;
+ dst = conv->wbuff + used;
}
- *dst = 0;
- return buff;
-out:
- el_free(buff);
- buffsize = 0;
- return NULL;
+
+ *dst = L'\0';
+ return conv->wbuff;
}
-protected int
-ct_visual_width(Char c)
+libedit_private int
+ct_visual_width(wchar_t c)
{
int t = ct_chr_class(c);
switch (t) {
@@ -269,7 +264,6 @@ ct_visual_width(Char c)
return 1; /* Hmm, this really need to be handled outside! */
case CHTYPE_NL:
return 0; /* Should this be 1 instead? */
-#ifdef WIDECHAR
case CHTYPE_PRINT:
return wcwidth(c);
case CHTYPE_NONPRINT:
@@ -277,20 +271,14 @@ ct_visual_width(Char c)
return 8; /* \U+12345 */
else
return 7; /* \U+1234 */
-#else
- case CHTYPE_PRINT:
- return 1;
- case CHTYPE_NONPRINT:
- return 4; /* \123 */
-#endif
default:
return 0; /* should not happen */
}
}
-protected ssize_t
-ct_visual_char(Char *dst, size_t len, Char c)
+libedit_private ssize_t
+ct_visual_char(wchar_t *dst, size_t len, wchar_t c)
{
int t = ct_chr_class(c);
switch (t) {
@@ -315,7 +303,6 @@ ct_visual_char(Char *dst, size_t len, Char c)
* so this is right */
if ((ssize_t)len < ct_visual_width(c))
return -1; /* insufficient space */
-#ifdef WIDECHAR
*dst++ = '\\';
*dst++ = 'U';
*dst++ = '+';
@@ -326,14 +313,7 @@ ct_visual_char(Char *dst, size_t len, Char c)
*dst++ = tohexdigit(((unsigned int) c >> 8) & 0xf);
*dst++ = tohexdigit(((unsigned int) c >> 4) & 0xf);
*dst = tohexdigit(((unsigned int) c ) & 0xf);
- return (c > 0xffff) ? 8 : 7;
-#else
- *dst++ = '\\';
-#define tooctaldigit(v) ((v) + '0')
- *dst++ = tooctaldigit(((unsigned int) c >> 6) & 0x7);
- *dst++ = tooctaldigit(((unsigned int) c >> 3) & 0x7);
- *dst++ = tooctaldigit(((unsigned int) c ) & 0x7);
-#endif
+ return c > 0xffff ? 8 : 7;
/*FALLTHROUGH*/
/* these two should be handled outside this function */
default: /* we should never hit the default */
@@ -344,16 +324,16 @@ ct_visual_char(Char *dst, size_t len, Char c)
-protected int
-ct_chr_class(Char c)
+libedit_private int
+ct_chr_class(wchar_t c)
{
if (c == '\t')
return CHTYPE_TAB;
else if (c == '\n')
return CHTYPE_NL;
- else if (IsASCII(c) && Iscntrl(c))
+ else if (c < 0x100 && iswcntrl(c))
return CHTYPE_ASCIICTL;
- else if (Isprint(c))
+ else if (iswprint(c))
return CHTYPE_PRINT;
else
return CHTYPE_NONPRINT;
diff --git a/lib/libedit/src/chartype.h b/lib/libedit/src/chartype.h
index df65b02c00f9..7f4b8d9d6b9f 100644
--- a/lib/libedit/src/chartype.h
+++ b/lib/libedit/src/chartype.h
@@ -1,4 +1,4 @@
-/* $NetBSD: chartype.h,v 1.7 2010/12/16 17:42:28 wiz Exp $ */
+/* $NetBSD: chartype.h,v 1.34 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -12,13 +12,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -36,15 +29,11 @@
#ifndef _h_chartype_f
#define _h_chartype_f
-
-
-#ifdef WIDECHAR
-
/* Ideally we should also test the value of the define to see if it
* supports non-BMP code points without requiring UTF-16, but nothing
* seems to actually advertise this properly, despite Unicode 3.1 having
* been around since 2001... */
-#if !defined(__NetBSD__) && !defined(__sun) && !(defined(__APPLE__) && defined(__MACH__))
+#if !defined(__NetBSD__) && !defined(__sun) && !(defined(__APPLE__) && defined(__MACH__)) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
#ifndef __STDC_ISO_10646__
/* In many places it is assumed that the first 127 code points are ASCII
* compatible, so ensure wchar_t indeed does ISO 10646 and not some other
@@ -60,175 +49,53 @@
#warning Build environment does not support non-BMP characters
#endif
-#ifndef HAVE_WCSDUP
-wchar_t *wcsdup(const wchar_t *s);
-#endif
-
-#define ct_mbtowc mbtowc
-#define ct_mbtowc_reset mbtowc(0,0,0)
-#define ct_wctomb wctomb
-#define ct_wctomb_reset wctomb(0,0)
-#define ct_wcstombs wcstombs
-#define ct_mbstowcs mbstowcs
-
-#define Char wchar_t
-#define Int wint_t
-#define FUN(prefix,rest) prefix ## _w ## rest
-#define FUNW(type) type ## _w
-#define TYPE(type) type ## W
-#define FSTR "%ls"
-#define STR(x) L ## x
-#define UC(c) c
-#define Isalpha(x) iswalpha(x)
-#define Isalnum(x) iswalnum(x)
-#define Isgraph(x) iswgraph(x)
-#define Isspace(x) iswspace(x)
-#define Isdigit(x) iswdigit(x)
-#define Iscntrl(x) iswcntrl(x)
-#define Isprint(x) iswprint(x)
-
-#define Isupper(x) iswupper(x)
-#define Islower(x) iswlower(x)
-#define Toupper(x) towupper(x)
-#define Tolower(x) towlower(x)
-
-#define IsASCII(x) (x < 0x100)
-
-#define Strlen(x) wcslen(x)
-#define Strchr(s,c) wcschr(s,c)
-#define Strrchr(s,c) wcsrchr(s,c)
-#define Strstr(s,v) wcsstr(s,v)
-#define Strdup(x) wcsdup(x)
-#define Strcpy(d,s) wcscpy(d,s)
-#define Strncpy(d,s,n) wcsncpy(d,s,n)
-#define Strncat(d,s,n) wcsncat(d,s,n)
-
-#define Strcmp(s,v) wcscmp(s,v)
-#define Strncmp(s,v,n) wcsncmp(s,v,n)
-#define Strcspn(s,r) wcscspn(s,r)
-
-#define Strtol(p,e,b) wcstol(p,e,b)
-
-#define Width(c) wcwidth(c)
-
-#else /* NARROW */
-
-#define ct_mbtowc error
-#define ct_mbtowc_reset
-#define ct_wctomb error
-#define ct_wctomb_reset
-#define ct_wcstombs(a, b, c) (strncpy(a, b, c), strlen(a))
-#define ct_mbstowcs(a, b, c) (strncpy(a, b, c), strlen(a))
-
-#define Char char
-#define Int int
-#define FUN(prefix,rest) prefix ## _ ## rest
-#define FUNW(type) type
-#define TYPE(type) type
-#define FSTR "%s"
-#define STR(x) x
-#define UC(c) (unsigned char)(c)
-
-#define Isalpha(x) isalpha((unsigned char)x)
-#define Isalnum(x) isalnum((unsigned char)x)
-#define Isgraph(x) isgraph((unsigned char)x)
-#define Isspace(x) isspace((unsigned char)x)
-#define Isdigit(x) isdigit((unsigned char)x)
-#define Iscntrl(x) iscntrl((unsigned char)x)
-#define Isprint(x) isprint((unsigned char)x)
-
-#define Isupper(x) isupper((unsigned char)x)
-#define Islower(x) islower((unsigned char)x)
-#define Toupper(x) toupper((unsigned char)x)
-#define Tolower(x) tolower((unsigned char)x)
-
-#define IsASCII(x) isascii((unsigned char)x)
-
-#define Strlen(x) strlen(x)
-#define Strchr(s,c) strchr(s,c)
-#define Strrchr(s,c) strrchr(s,c)
-#define Strstr(s,v) strstr(s,v)
-#define Strdup(x) strdup(x)
-#define Strcpy(d,s) strcpy(d,s)
-#define Strncpy(d,s,n) strncpy(d,s,n)
-#define Strncat(d,s,n) strncat(d,s,n)
-
-#define Strcmp(s,v) strcmp(s,v)
-#define Strncmp(s,v,n) strncmp(s,v,n)
-#define Strcspn(s,r) strcspn(s,r)
-
-#define Strtol(p,e,b) strtol(p,e,b)
-
-#define Width(c) 1
-
-#endif
-
-
-#ifdef WIDECHAR
/*
* Conversion buffer
*/
typedef struct ct_buffer_t {
char *cbuff;
size_t csize;
- Char *wbuff;
+ wchar_t *wbuff;
size_t wsize;
} ct_buffer_t;
-#define ct_encode_string __ct_encode_string
/* Encode a wide-character string and return the UTF-8 encoded result. */
-public char *ct_encode_string(const Char *, ct_buffer_t *);
+char *ct_encode_string(const wchar_t *, ct_buffer_t *);
-#define ct_decode_string __ct_decode_string
/* Decode a (multi)?byte string and return the wide-character string result. */
-public Char *ct_decode_string(const char *, ct_buffer_t *);
+wchar_t *ct_decode_string(const char *, ct_buffer_t *);
/* Decode a (multi)?byte argv string array.
* The pointer returned must be free()d when done. */
-protected Char **ct_decode_argv(int, const char *[], ct_buffer_t *);
-
-/* Resizes the conversion buffer(s) if needed. */
-protected void ct_conv_buff_resize(ct_buffer_t *, size_t, size_t);
-protected ssize_t ct_encode_char(char *, size_t, Char);
-protected size_t ct_enc_width(Char);
-
-#define ct_free_argv(s) el_free(s)
-
-#else
-#define ct_encode_string(s, b) (s)
-#define ct_decode_string(s, b) (s)
-#define ct_decode_argv(l, s, b) (s)
-#define ct_conv_buff_resize(b, os, ns)
-#define ct_encode_char(d, l, s) (*d = s, 1)
-#define ct_free_argv(s)
-#endif
+libedit_private wchar_t **ct_decode_argv(int, const char *[], ct_buffer_t *);
-#ifndef NARROWCHAR
-/* Encode a characted into the destination buffer, provided there is sufficent
+/* Encode a character into the destination buffer, provided there is sufficient
* buffer space available. Returns the number of bytes used up (zero if the
* character cannot be encoded, -1 if there was not enough space available). */
+libedit_private ssize_t ct_encode_char(char *, size_t, wchar_t);
+libedit_private size_t ct_enc_width(wchar_t);
-/* The maximum buffer size to hold the most unwieldly visual representation,
+/* The maximum buffer size to hold the most unwieldy visual representation,
* in this case \U+nnnnn. */
-#define VISUAL_WIDTH_MAX 8
+#define VISUAL_WIDTH_MAX ((size_t)8)
/* The terminal is thought of in terms of X columns by Y lines. In the cases
* where a wide character takes up more than one column, the adjacent
* occupied column entries will contain this faux character. */
-#define MB_FILL_CHAR ((Char)-1)
+#define MB_FILL_CHAR ((wchar_t)-1)
/* Visual width of character c, taking into account ^? , \0177 and \U+nnnnn
* style visual expansions. */
-protected int ct_visual_width(Char);
+libedit_private int ct_visual_width(wchar_t);
/* Turn the given character into the appropriate visual format, matching
* the width given by ct_visual_width(). Returns the number of characters used
- * up, or -1 if insufficient space. Buffer length is in count of Char's. */
-protected ssize_t ct_visual_char(Char *, size_t, Char);
+ * up, or -1 if insufficient space. Buffer length is in count of wchar_t's. */
+libedit_private ssize_t ct_visual_char(wchar_t *, size_t, wchar_t);
/* Convert the given string into visual format, using the ct_visual_char()
* function. Uses a static buffer, so not threadsafe. */
-protected const Char *ct_visual_string(const Char *);
+libedit_private const wchar_t *ct_visual_string(const wchar_t *, ct_buffer_t *);
/* printable character, use ct_visual_width() to find out display width */
@@ -242,8 +109,6 @@ protected const Char *ct_visual_string(const Char *);
/* non-printable character */
#define CHTYPE_NONPRINT (-4)
/* classification of character c, as one of the above defines */
-protected int ct_chr_class(Char c);
-#endif
-
+libedit_private int ct_chr_class(wchar_t c);
#endif /* _chartype_f */
diff --git a/lib/libedit/src/common.c b/lib/libedit/src/common.c
index c4d21aa93286..270860510b80 100644
--- a/lib/libedit/src/common.c
+++ b/lib/libedit/src/common.c
@@ -1,4 +1,4 @@
-/* $NetBSD: common.c,v 1.24 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: common.c,v 1.47 2016/05/22 19:44:26 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,27 +37,34 @@
#if 0
static char sccsid[] = "@(#)common.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: common.c,v 1.24 2009/12/30 22:37:40 christos Exp $");
+__RCSID("$NetBSD: common.c,v 1.47 2016/05/22 19:44:26 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
* common.c: Common Editor functions
*/
+#include <ctype.h>
+#include <string.h>
+
#include "el.h"
+#include "common.h"
+#include "fcns.h"
+#include "parse.h"
+#include "vi.h"
/* ed_end_of_file():
* Indicate end of file
* [^D]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_end_of_file(EditLine *el, Int c __attribute__((__unused__)))
+ed_end_of_file(EditLine *el, wint_t c __attribute__((__unused__)))
{
re_goto_bottom(el);
*el->el_line.lastchar = '\0';
- return (CC_EOF);
+ return CC_EOF;
}
@@ -65,13 +72,13 @@ ed_end_of_file(EditLine *el, Int c __attribute__((__unused__)))
* Add character to the line
* Insert a character [bound to all insert keys]
*/
-protected el_action_t
-ed_insert(EditLine *el, Int c)
+libedit_private el_action_t
+ed_insert(EditLine *el, wint_t c)
{
int count = el->el_state.argument;
if (c == '\0')
- return (CC_ERROR);
+ return CC_ERROR;
if (el->el_line.lastchar + el->el_state.argument >=
el->el_line.limit) {
@@ -99,7 +106,7 @@ ed_insert(EditLine *el, Int c)
if (el->el_state.inputmode == MODE_REPLACE_1)
return vi_command_mode(el, 0);
- return (CC_NORM);
+ return CC_NORM;
}
@@ -107,14 +114,14 @@ ed_insert(EditLine *el, Int c)
* Delete from beginning of current word to cursor
* [M-^?] [^W]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_delete_prev_word(EditLine *el, Int c __attribute__((__unused__)))
+ed_delete_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *cp, *p, *kp;
+ wchar_t *cp, *p, *kp;
if (el->el_line.cursor == el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
cp = c__prev_word(el->el_line.cursor, el->el_line.buffer,
el->el_state.argument, ce__isword);
@@ -127,7 +134,7 @@ ed_delete_prev_word(EditLine *el, Int c __attribute__((__unused__)))
el->el_line.cursor = cp;
if (el->el_line.cursor < el->el_line.buffer)
el->el_line.cursor = el->el_line.buffer; /* bounds check */
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -135,14 +142,14 @@ ed_delete_prev_word(EditLine *el, Int c __attribute__((__unused__)))
* Delete character under cursor
* [^D] [x]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_delete_next_char(EditLine *el, Int c)
+ed_delete_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
-#ifdef notdef /* XXX */
+#ifdef DEBUG_EDIT
#define EL el->el_line
- (void) fprintf(el->el_errlfile,
- "\nD(b: %x(%s) c: %x(%s) last: %x(%s) limit: %x(%s)\n",
+ (void) fprintf(el->el_errfile,
+ "\nD(b: %p(%ls) c: %p(%ls) last: %p(%ls) limit: %p(%ls)\n",
EL.buffer, EL.buffer, EL.cursor, EL.cursor, EL.lastchar,
EL.lastchar, EL.limit, EL.limit);
#endif
@@ -152,32 +159,29 @@ ed_delete_next_char(EditLine *el, Int c)
if (el->el_line.cursor == el->el_line.buffer) {
/* if I'm also at the beginning */
#ifdef KSHVI
- return (CC_ERROR);
+ return CC_ERROR;
#else
/* then do an EOF */
- term_writec(el, c);
- return (CC_EOF);
+ terminal_writec(el, c);
+ return CC_EOF;
#endif
} else {
#ifdef KSHVI
el->el_line.cursor--;
#else
- return (CC_ERROR);
+ return CC_ERROR;
#endif
}
- } else {
- if (el->el_line.cursor != el->el_line.buffer)
- el->el_line.cursor--;
- else
- return (CC_ERROR);
- }
+ } else
+ return CC_ERROR;
}
c_delafter(el, el->el_state.argument); /* delete after dot */
- if (el->el_line.cursor >= el->el_line.lastchar &&
+ if (el->el_map.type == MAP_VI &&
+ el->el_line.cursor >= el->el_line.lastchar &&
el->el_line.cursor > el->el_line.buffer)
/* bounds check */
el->el_line.cursor = el->el_line.lastchar - 1;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -185,11 +189,11 @@ ed_delete_next_char(EditLine *el, Int c)
* Cut to the end of line
* [^K] [^K]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_kill_line(EditLine *el, Int c __attribute__((__unused__)))
+ed_kill_line(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *kp, *cp;
+ wchar_t *kp, *cp;
cp = el->el_line.cursor;
kp = el->el_chared.c_kill.buf;
@@ -198,7 +202,7 @@ ed_kill_line(EditLine *el, Int c __attribute__((__unused__)))
el->el_chared.c_kill.last = kp;
/* zap! -- delete to end */
el->el_line.lastchar = el->el_line.cursor;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -206,22 +210,22 @@ ed_kill_line(EditLine *el, Int c __attribute__((__unused__)))
* Move cursor to the end of line
* [^E] [^E]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_move_to_end(EditLine *el, Int c __attribute__((__unused__)))
+ed_move_to_end(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_line.cursor = el->el_line.lastchar;
if (el->el_map.type == MAP_VI) {
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
#ifdef VI_MOVE
el->el_line.cursor--;
#endif
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -229,23 +233,23 @@ ed_move_to_end(EditLine *el, Int c __attribute__((__unused__)))
* Move cursor to the beginning of line
* [^A] [^A]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_move_to_beg(EditLine *el, Int c __attribute__((__unused__)))
+ed_move_to_beg(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_line.cursor = el->el_line.buffer;
if (el->el_map.type == MAP_VI) {
/* We want FIRST non space character */
- while (Isspace(*el->el_line.cursor))
+ while (iswspace(*el->el_line.cursor))
el->el_line.cursor++;
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -253,13 +257,13 @@ ed_move_to_beg(EditLine *el, Int c __attribute__((__unused__)))
* Exchange the character to the left of the cursor with the one under it
* [^T] [^T]
*/
-protected el_action_t
-ed_transpose_chars(EditLine *el, Int c)
+libedit_private el_action_t
+ed_transpose_chars(EditLine *el, wint_t c)
{
if (el->el_line.cursor < el->el_line.lastchar) {
if (el->el_line.lastchar <= &el->el_line.buffer[1])
- return (CC_ERROR);
+ return CC_ERROR;
else
el->el_line.cursor++;
}
@@ -268,9 +272,9 @@ ed_transpose_chars(EditLine *el, Int c)
c = el->el_line.cursor[-2];
el->el_line.cursor[-2] = el->el_line.cursor[-1];
el->el_line.cursor[-1] = c;
- return (CC_REFRESH);
+ return CC_REFRESH;
} else
- return (CC_ERROR);
+ return CC_ERROR;
}
@@ -278,17 +282,17 @@ ed_transpose_chars(EditLine *el, Int c)
* Move to the right one character
* [^F] [^F]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_next_char(EditLine *el, Int c __attribute__((__unused__)))
+ed_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *lim = el->el_line.lastchar;
+ wchar_t *lim = el->el_line.lastchar;
if (el->el_line.cursor >= lim ||
(el->el_line.cursor == lim - 1 &&
el->el_map.type == MAP_VI &&
el->el_chared.c_vcmd.action == NOP))
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor += el->el_state.argument;
if (el->el_line.cursor > lim)
@@ -297,9 +301,9 @@ ed_next_char(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_map.type == MAP_VI)
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -307,13 +311,13 @@ ed_next_char(EditLine *el, Int c __attribute__((__unused__)))
* Move to the beginning of the current word
* [M-b] [b]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_prev_word(EditLine *el, Int c __attribute__((__unused__)))
+ed_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor == el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = c__prev_word(el->el_line.cursor,
el->el_line.buffer,
@@ -323,9 +327,9 @@ ed_prev_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_map.type == MAP_VI)
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -333,9 +337,9 @@ ed_prev_word(EditLine *el, Int c __attribute__((__unused__)))
* Move to the left one character
* [^B] [^B]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_prev_char(EditLine *el, Int c __attribute__((__unused__)))
+ed_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor > el->el_line.buffer) {
@@ -346,11 +350,11 @@ ed_prev_char(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_map.type == MAP_VI)
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
} else
- return (CC_ERROR);
+ return CC_ERROR;
}
@@ -358,32 +362,30 @@ ed_prev_char(EditLine *el, Int c __attribute__((__unused__)))
* Add the next character typed verbatim
* [^V] [^V]
*/
-protected el_action_t
-ed_quoted_insert(EditLine *el, Int c)
+libedit_private el_action_t
+ed_quoted_insert(EditLine *el, wint_t c)
{
int num;
- Char tc;
tty_quotemode(el);
- num = FUN(el,getc)(el, &tc);
- c = tc;
+ num = el_wgetc(el, &c);
tty_noquotemode(el);
if (num == 1)
- return (ed_insert(el, c));
+ return ed_insert(el, c);
else
- return (ed_end_of_file(el, 0));
+ return ed_end_of_file(el, 0);
}
/* ed_digit():
* Adds to argument or enters a digit
*/
-protected el_action_t
-ed_digit(EditLine *el, Int c)
+libedit_private el_action_t
+ed_digit(EditLine *el, wint_t c)
{
- if (!Isdigit(c))
- return (CC_ERROR);
+ if (!iswdigit(c))
+ return CC_ERROR;
if (el->el_state.doingarg) {
/* if doing an arg, add this in... */
@@ -391,11 +393,11 @@ ed_digit(EditLine *el, Int c)
el->el_state.argument = c - '0';
else {
if (el->el_state.argument > 1000000)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_state.argument =
(el->el_state.argument * 10) + (c - '0');
}
- return (CC_ARGHACK);
+ return CC_ARGHACK;
}
return ed_insert(el, c);
@@ -406,23 +408,23 @@ ed_digit(EditLine *el, Int c)
* Digit that starts argument
* For ESC-n
*/
-protected el_action_t
-ed_argument_digit(EditLine *el, Int c)
+libedit_private el_action_t
+ed_argument_digit(EditLine *el, wint_t c)
{
- if (!Isdigit(c))
- return (CC_ERROR);
+ if (!iswdigit(c))
+ return CC_ERROR;
if (el->el_state.doingarg) {
if (el->el_state.argument > 1000000)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_state.argument = (el->el_state.argument * 10) +
(c - '0');
} else { /* else starting an argument */
el->el_state.argument = c - '0';
el->el_state.doingarg = 1;
}
- return (CC_ARGHACK);
+ return CC_ARGHACK;
}
@@ -430,114 +432,27 @@ ed_argument_digit(EditLine *el, Int c)
* Indicates unbound character
* Bound to keys that are not assigned
*/
-protected el_action_t
-/*ARGSUSED*/
-ed_unassigned(EditLine *el, Int c __attribute__((__unused__)))
-{
-
- return (CC_ERROR);
-}
-
-
-/**
- ** TTY key handling.
- **/
-
-/* ed_tty_sigint():
- * Tty interrupt character
- * [^C]
- */
-protected el_action_t
-/*ARGSUSED*/
-ed_tty_sigint(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
-{
-
- return (CC_NORM);
-}
-
-
-/* ed_tty_dsusp():
- * Tty delayed suspend character
- * [^Y]
- */
-protected el_action_t
-/*ARGSUSED*/
-ed_tty_dsusp(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
-{
-
- return (CC_NORM);
-}
-
-
-/* ed_tty_flush_output():
- * Tty flush output characters
- * [^O]
- */
-protected el_action_t
-/*ARGSUSED*/
-ed_tty_flush_output(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
-{
-
- return (CC_NORM);
-}
-
-
-/* ed_tty_sigquit():
- * Tty quit character
- * [^\]
- */
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_tty_sigquit(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
+ed_unassigned(EditLine *el __attribute__((__unused__)),
+ wint_t c __attribute__((__unused__)))
{
- return (CC_NORM);
+ return CC_ERROR;
}
-/* ed_tty_sigtstp():
- * Tty suspend character
- * [^Z]
+/* ed_ignore():
+ * Input characters that have no effect
+ * [^C ^O ^Q ^S ^Z ^\ ^]] [^C ^O ^Q ^S ^\]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_tty_sigtstp(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
+ed_ignore(EditLine *el __attribute__((__unused__)),
+ wint_t c __attribute__((__unused__)))
{
- return (CC_NORM);
-}
-
-
-/* ed_tty_stop_output():
- * Tty disallow output characters
- * [^S]
- */
-protected el_action_t
-/*ARGSUSED*/
-ed_tty_stop_output(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
-{
-
- return (CC_NORM);
-}
-
-
-/* ed_tty_start_output():
- * Tty allow output characters
- * [^Q]
- */
-protected el_action_t
-/*ARGSUSED*/
-ed_tty_start_output(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
-{
-
- return (CC_NORM);
+ return CC_NORM;
}
@@ -545,15 +460,15 @@ ed_tty_start_output(EditLine *el __attribute__((__unused__)),
* Execute command
* [^J]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_newline(EditLine *el, Int c __attribute__((__unused__)))
+ed_newline(EditLine *el, wint_t c __attribute__((__unused__)))
{
re_goto_bottom(el);
*el->el_line.lastchar++ = '\n';
*el->el_line.lastchar = '\0';
- return (CC_NEWLINE);
+ return CC_NEWLINE;
}
@@ -561,19 +476,19 @@ ed_newline(EditLine *el, Int c __attribute__((__unused__)))
* Delete the character to the left of the cursor
* [^?]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
+ed_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor <= el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
c_delbefore(el, el->el_state.argument);
el->el_line.cursor -= el->el_state.argument;
if (el->el_line.cursor < el->el_line.buffer)
el->el_line.cursor = el->el_line.buffer;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -581,14 +496,14 @@ ed_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
* Clear screen leaving current line at the top
* [^L]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_clear_screen(EditLine *el, Int c __attribute__((__unused__)))
+ed_clear_screen(EditLine *el, wint_t c __attribute__((__unused__)))
{
- term_clear_screen(el); /* clear the whole real screen */
+ terminal_clear_screen(el); /* clear the whole real screen */
re_clear_display(el); /* reset everything */
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -596,13 +511,13 @@ ed_clear_screen(EditLine *el, Int c __attribute__((__unused__)))
* Redisplay everything
* ^R
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
ed_redisplay(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
+ wint_t c __attribute__((__unused__)))
{
- return (CC_REDISPLAY);
+ return CC_REDISPLAY;
}
@@ -610,13 +525,13 @@ ed_redisplay(EditLine *el __attribute__((__unused__)),
* Erase current line and start from scratch
* [^G]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_start_over(EditLine *el, Int c __attribute__((__unused__)))
+ed_start_over(EditLine *el, wint_t c __attribute__((__unused__)))
{
- ch_reset(el, 0);
- return (CC_REFRESH);
+ ch_reset(el);
+ return CC_REFRESH;
}
@@ -624,13 +539,13 @@ ed_start_over(EditLine *el, Int c __attribute__((__unused__)))
* First character in a bound sequence
* Placeholder for external keys
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
ed_sequence_lead_in(EditLine *el __attribute__((__unused__)),
- Int c __attribute__((__unused__)))
+ wint_t c __attribute__((__unused__)))
{
- return (CC_NORM);
+ return CC_NORM;
}
@@ -638,9 +553,9 @@ ed_sequence_lead_in(EditLine *el __attribute__((__unused__)),
* Move to the previous history line
* [^P] [k]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_prev_history(EditLine *el, Int c __attribute__((__unused__)))
+ed_prev_history(EditLine *el, wint_t c __attribute__((__unused__)))
{
char beep = 0;
int sv_event = el->el_history.eventno;
@@ -650,7 +565,7 @@ ed_prev_history(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_history.eventno == 0) { /* save the current buffer
* away */
- (void) Strncpy(el->el_history.buf, el->el_line.buffer,
+ (void) wcsncpy(el->el_history.buf, el->el_line.buffer,
EL_BUFSIZ);
el->el_history.last = el->el_history.buf +
(el->el_line.lastchar - el->el_line.buffer);
@@ -660,7 +575,6 @@ ed_prev_history(EditLine *el, Int c __attribute__((__unused__)))
if (hist_get(el) == CC_ERROR) {
if (el->el_map.type == MAP_VI) {
el->el_history.eventno = sv_event;
-
}
beep = 1;
/* el->el_history.eventno was fixed by first call */
@@ -676,9 +590,9 @@ ed_prev_history(EditLine *el, Int c __attribute__((__unused__)))
* Move to the next history line
* [^N] [j]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_next_history(EditLine *el, Int c __attribute__((__unused__)))
+ed_next_history(EditLine *el, wint_t c __attribute__((__unused__)))
{
el_action_t beep = CC_REFRESH, rval;
@@ -703,13 +617,13 @@ ed_next_history(EditLine *el, Int c __attribute__((__unused__)))
* Search previous in history for a line matching the current
* next search history [M-P] [K]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_search_prev_history(EditLine *el, Int c __attribute__((__unused__)))
+ed_search_prev_history(EditLine *el, wint_t c __attribute__((__unused__)))
{
- const Char *hp;
+ const wchar_t *hp;
int h;
- bool_t found = 0;
+ int found = 0;
el->el_chared.c_vcmd.action = NOP;
el->el_chared.c_undo.len = -1;
@@ -720,20 +634,20 @@ ed_search_prev_history(EditLine *el, Int c __attribute__((__unused__)))
"e_prev_search_hist(): eventno < 0;\n");
#endif
el->el_history.eventno = 0;
- return (CC_ERROR);
+ return CC_ERROR;
}
if (el->el_history.eventno == 0) {
- (void) Strncpy(el->el_history.buf, el->el_line.buffer,
+ (void) wcsncpy(el->el_history.buf, el->el_line.buffer,
EL_BUFSIZ);
el->el_history.last = el->el_history.buf +
(el->el_line.lastchar - el->el_line.buffer);
}
if (el->el_history.ref == NULL)
- return (CC_ERROR);
+ return CC_ERROR;
hp = HIST_FIRST(el);
if (hp == NULL)
- return (CC_ERROR);
+ return CC_ERROR;
c_setpat(el); /* Set search pattern !! */
@@ -744,11 +658,11 @@ ed_search_prev_history(EditLine *el, Int c __attribute__((__unused__)))
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
#endif
- if ((Strncmp(hp, el->el_line.buffer, (size_t)
+ if ((wcsncmp(hp, el->el_line.buffer, (size_t)
(el->el_line.lastchar - el->el_line.buffer)) ||
hp[el->el_line.lastchar - el->el_line.buffer]) &&
c_hmatch(el, hp)) {
- found++;
+ found = 1;
break;
}
h++;
@@ -759,11 +673,11 @@ ed_search_prev_history(EditLine *el, Int c __attribute__((__unused__)))
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "not found\n");
#endif
- return (CC_ERROR);
+ return CC_ERROR;
}
el->el_history.eventno = h;
- return (hist_get(el));
+ return hist_get(el);
}
@@ -771,27 +685,27 @@ ed_search_prev_history(EditLine *el, Int c __attribute__((__unused__)))
* Search next in history for a line matching the current
* [M-N] [J]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_search_next_history(EditLine *el, Int c __attribute__((__unused__)))
+ed_search_next_history(EditLine *el, wint_t c __attribute__((__unused__)))
{
- const Char *hp;
+ const wchar_t *hp;
int h;
- bool_t found = 0;
+ int found = 0;
el->el_chared.c_vcmd.action = NOP;
el->el_chared.c_undo.len = -1;
*el->el_line.lastchar = '\0'; /* just in case */
if (el->el_history.eventno == 0)
- return (CC_ERROR);
+ return CC_ERROR;
if (el->el_history.ref == NULL)
- return (CC_ERROR);
+ return CC_ERROR;
hp = HIST_FIRST(el);
if (hp == NULL)
- return (CC_ERROR);
+ return CC_ERROR;
c_setpat(el); /* Set search pattern !! */
@@ -799,7 +713,7 @@ ed_search_next_history(EditLine *el, Int c __attribute__((__unused__)))
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "Comparing with \"%s\"\n", hp);
#endif
- if ((Strncmp(hp, el->el_line.buffer, (size_t)
+ if ((wcsncmp(hp, el->el_line.buffer, (size_t)
(el->el_line.lastchar - el->el_line.buffer)) ||
hp[el->el_line.lastchar - el->el_line.buffer]) &&
c_hmatch(el, hp))
@@ -812,12 +726,12 @@ ed_search_next_history(EditLine *el, Int c __attribute__((__unused__)))
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "not found\n");
#endif
- return (CC_ERROR);
+ return CC_ERROR;
}
}
el->el_history.eventno = found;
- return (hist_get(el));
+ return hist_get(el);
}
@@ -825,11 +739,11 @@ ed_search_next_history(EditLine *el, Int c __attribute__((__unused__)))
* Move up one line
* Could be [k] [^p]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_prev_line(EditLine *el, Int c __attribute__((__unused__)))
+ed_prev_line(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *ptr;
+ wchar_t *ptr;
int nchars = c_hpos(el);
/*
@@ -843,7 +757,7 @@ ed_prev_line(EditLine *el, Int c __attribute__((__unused__)))
break;
if (el->el_state.argument > 0)
- return (CC_ERROR);
+ return CC_ERROR;
/*
* Move to the beginning of the line
@@ -860,7 +774,7 @@ ed_prev_line(EditLine *el, Int c __attribute__((__unused__)))
continue;
el->el_line.cursor = ptr;
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -868,11 +782,11 @@ ed_prev_line(EditLine *el, Int c __attribute__((__unused__)))
* Move down one line
* Could be [j] [^n]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_next_line(EditLine *el, Int c __attribute__((__unused__)))
+ed_next_line(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *ptr;
+ wchar_t *ptr;
int nchars = c_hpos(el);
/*
@@ -883,7 +797,7 @@ ed_next_line(EditLine *el, Int c __attribute__((__unused__)))
break;
if (el->el_state.argument > 0)
- return (CC_ERROR);
+ return CC_ERROR;
/*
* Move to the character requested
@@ -894,7 +808,7 @@ ed_next_line(EditLine *el, Int c __attribute__((__unused__)))
continue;
el->el_line.cursor = ptr;
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -902,18 +816,18 @@ ed_next_line(EditLine *el, Int c __attribute__((__unused__)))
* Editline extended command
* [M-X] [:]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-ed_command(EditLine *el, Int c __attribute__((__unused__)))
+ed_command(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char tmpbuf[EL_BUFSIZ];
+ wchar_t tmpbuf[EL_BUFSIZ];
int tmplen;
- tmplen = c_gets(el, tmpbuf, STR("\n: "));
- term__putc(el, '\n');
+ tmplen = c_gets(el, tmpbuf, L"\n: ");
+ terminal__putc(el, '\n');
if (tmplen < 0 || (tmpbuf[tmplen] = 0, parse_line(el, tmpbuf)) == -1)
- term_beep(el);
+ terminal_beep(el);
el->el_map.current = el->el_map.key;
re_clear_display(el);
diff --git a/lib/libedit/src/el.c b/lib/libedit/src/el.c
index 168734cec6f7..acde929427ca 100644
--- a/lib/libedit/src/el.c
+++ b/lib/libedit/src/el.c
@@ -1,4 +1,4 @@
-/* $NetBSD: el.c,v 1.61 2011/01/27 23:11:40 christos Exp $ */
+/* $NetBSD: el.c,v 1.92 2016/05/22 19:44:26 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,37 +37,44 @@
#if 0
static char sccsid[] = "@(#)el.c 8.2 (Berkeley) 1/3/94";
#else
-__RCSID("$NetBSD: el.c,v 1.61 2011/01/27 23:11:40 christos Exp $");
+__RCSID("$NetBSD: el.c,v 1.92 2016/05/22 19:44:26 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
-#ifndef MAXPATHLEN
-#define MAXPATHLEN 4096
-#endif
-
/*
* el.c: EditLine interface functions
*/
#include <sys/types.h>
#include <sys/param.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdarg.h>
#include <ctype.h>
-#include <locale.h>
#include <langinfo.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
#include "el.h"
+#include "parse.h"
+#include "read.h"
/* el_init():
* Initialize editline and set default parameters.
*/
-public EditLine *
+EditLine *
el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
{
- EditLine *el = (EditLine *) el_malloc(sizeof(EditLine));
+ return el_init_fd(prog, fin, fout, ferr, fileno(fin), fileno(fout),
+ fileno(ferr));
+}
+
+EditLine *
+el_init_fd(const char *prog, FILE *fin, FILE *fout, FILE *ferr,
+ int fdin, int fdout, int fderr)
+{
+ EditLine *el = el_malloc(sizeof(*el));
if (el == NULL)
- return (NULL);
+ return NULL;
memset(el, 0, sizeof(EditLine));
@@ -75,11 +82,11 @@ el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
el->el_outfile = fout;
el->el_errfile = ferr;
- el->el_infd = fileno(fin);
- el->el_outfd = fileno(fout);
- el->el_errfd = fileno(ferr);
+ el->el_infd = fdin;
+ el->el_outfd = fdout;
+ el->el_errfd = fderr;
- el->el_prog = Strdup(ct_decode_string(prog, &el->el_scratch));
+ el->el_prog = wcsdup(ct_decode_string(prog, &el->el_scratch));
if (el->el_prog == NULL) {
el_free(el);
return NULL;
@@ -89,19 +96,17 @@ el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
* Initialize all the modules. Order is important!!!
*/
el->el_flags = 0;
-#ifdef WIDECHAR
if (setlocale(LC_CTYPE, NULL) != NULL){
if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0)
el->el_flags |= CHARSET_IS_UTF8;
}
-#endif
- if (term_init(el) == -1) {
+ if (terminal_init(el) == -1) {
el_free(el->el_prog);
el_free(el);
return NULL;
}
- (void) key_init(el);
+ (void) keymacro_init(el);
(void) map_init(el);
if (tty_init(el) == -1)
el->el_flags |= NO_TTY;
@@ -110,16 +115,18 @@ el_init(const char *prog, FILE *fin, FILE *fout, FILE *ferr)
(void) hist_init(el);
(void) prompt_init(el);
(void) sig_init(el);
- (void) read_init(el);
-
- return (el);
+ if (read_init(el) == -1) {
+ el_end(el);
+ return NULL;
+ }
+ return el;
}
/* el_end():
* Clean up.
*/
-public void
+void
el_end(EditLine *el)
{
@@ -128,50 +135,52 @@ el_end(EditLine *el)
el_reset(el);
- term_end(el);
- key_end(el);
+ terminal_end(el);
+ keymacro_end(el);
map_end(el);
- tty_end(el);
+ if (!(el->el_flags & NO_TTY))
+ tty_end(el);
ch_end(el);
+ read_end(el->el_read);
search_end(el);
hist_end(el);
prompt_end(el);
sig_end(el);
- el_free((ptr_t) el->el_prog);
- el_free((ptr_t) el);
-#ifdef WIDECHAR
- el_free((ptr_t) el->el_scratch.cbuff);
- el_free((ptr_t) el->el_scratch.wbuff);
- el_free((ptr_t) el->el_lgcyconv.cbuff);
- el_free((ptr_t) el->el_lgcyconv.wbuff);
-#endif
+ el_free(el->el_prog);
+ el_free(el->el_visual.cbuff);
+ el_free(el->el_visual.wbuff);
+ el_free(el->el_scratch.cbuff);
+ el_free(el->el_scratch.wbuff);
+ el_free(el->el_lgcyconv.cbuff);
+ el_free(el->el_lgcyconv.wbuff);
+ el_free(el);
}
/* el_reset():
* Reset the tty and the parser
*/
-public void
+void
el_reset(EditLine *el)
{
tty_cookedmode(el);
- ch_reset(el, 0); /* XXX: Do we want that? */
+ ch_reset(el); /* XXX: Do we want that? */
}
/* el_set():
* set the editline parameters
*/
-public int
-FUN(el,set)(EditLine *el, int op, ...)
+int
+el_wset(EditLine *el, int op, ...)
{
va_list ap;
int rv = 0;
if (el == NULL)
- return (-1);
+ return -1;
va_start(ap, op);
switch (op) {
@@ -190,21 +199,28 @@ FUN(el,set)(EditLine *el, int op, ...)
break;
}
+ case EL_ALIAS_TEXT: {
+ el_afunc_t p = va_arg(ap, el_afunc_t);
+ void *arg = va_arg(ap, void *);
+ rv = ch_aliasfun(el, p, arg);
+ break;
+ }
+
case EL_PROMPT_ESC:
case EL_RPROMPT_ESC: {
el_pfunc_t p = va_arg(ap, el_pfunc_t);
int c = va_arg(ap, int);
- rv = prompt_set(el, p, c, op, 1);
+ rv = prompt_set(el, p, (wchar_t)c, op, 1);
break;
}
case EL_TERMINAL:
- rv = term_set(el, va_arg(ap, char *));
+ rv = terminal_set(el, va_arg(ap, char *));
break;
case EL_EDITOR:
- rv = map_set_editor(el, va_arg(ap, Char *));
+ rv = map_set_editor(el, va_arg(ap, wchar_t *));
break;
case EL_SIGNAL:
@@ -220,36 +236,36 @@ FUN(el,set)(EditLine *el, int op, ...)
case EL_ECHOTC:
case EL_SETTY:
{
- const Char *argv[20];
+ const wchar_t *argv[20];
int i;
- for (i = 1; i < 20; i++)
- if ((argv[i] = va_arg(ap, Char *)) == NULL)
+ for (i = 1; i < (int)__arraycount(argv); i++)
+ if ((argv[i] = va_arg(ap, wchar_t *)) == NULL)
break;
switch (op) {
case EL_BIND:
- argv[0] = STR("bind");
+ argv[0] = L"bind";
rv = map_bind(el, i, argv);
break;
case EL_TELLTC:
- argv[0] = STR("telltc");
- rv = term_telltc(el, i, argv);
+ argv[0] = L"telltc";
+ rv = terminal_telltc(el, i, argv);
break;
case EL_SETTC:
- argv[0] = STR("settc");
- rv = term_settc(el, i, argv);
+ argv[0] = L"settc";
+ rv = terminal_settc(el, i, argv);
break;
case EL_ECHOTC:
- argv[0] = STR("echotc");
- rv = term_echotc(el, i, argv);
+ argv[0] = L"echotc";
+ rv = terminal_echotc(el, i, argv);
break;
case EL_SETTY:
- argv[0] = STR("setty");
+ argv[0] = L"setty";
rv = tty_stty(el, i, argv);
break;
@@ -263,8 +279,8 @@ FUN(el,set)(EditLine *el, int op, ...)
case EL_ADDFN:
{
- Char *name = va_arg(ap, Char *);
- Char *help = va_arg(ap, Char *);
+ wchar_t *name = va_arg(ap, wchar_t *);
+ wchar_t *help = va_arg(ap, wchar_t *);
el_func_t func = va_arg(ap, el_func_t);
rv = map_addfunc(el, name, help, func);
@@ -274,7 +290,7 @@ FUN(el,set)(EditLine *el, int op, ...)
case EL_HIST:
{
hist_fun_t func = va_arg(ap, hist_fun_t);
- ptr_t ptr = va_arg(ap, ptr_t);
+ void *ptr = va_arg(ap, void *);
rv = hist_set(el, func, ptr);
if (!(el->el_flags & CHARSET_IS_UTF8))
@@ -293,8 +309,7 @@ FUN(el,set)(EditLine *el, int op, ...)
case EL_GETCFN:
{
el_rfunc_t rc = va_arg(ap, el_rfunc_t);
- rv = el_read_setfn(el, rc);
- el->el_flags &= ~NARROW_READ;
+ rv = el_read_setfn(el->el_read, rc);
break;
}
@@ -355,7 +370,7 @@ FUN(el,set)(EditLine *el, int op, ...)
case EL_REFRESH:
re_clear_display(el);
re_refresh(el);
- term__flush(el);
+ terminal__flush(el);
break;
default:
@@ -364,15 +379,15 @@ FUN(el,set)(EditLine *el, int op, ...)
}
va_end(ap);
- return (rv);
+ return rv;
}
/* el_get():
* retrieve the editline parameters
*/
-public int
-FUN(el,get)(EditLine *el, int op, ...)
+int
+el_wget(EditLine *el, int op, ...)
{
va_list ap;
int rv;
@@ -392,14 +407,14 @@ FUN(el,get)(EditLine *el, int op, ...)
case EL_PROMPT_ESC:
case EL_RPROMPT_ESC: {
el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
- Char *c = va_arg(ap, Char *);
+ wchar_t *c = va_arg(ap, wchar_t *);
rv = prompt_get(el, p, c, op);
break;
}
case EL_EDITOR:
- rv = map_get_editor(el, va_arg(ap, const Char **));
+ rv = map_get_editor(el, va_arg(ap, const wchar_t **));
break;
case EL_SIGNAL:
@@ -413,7 +428,7 @@ FUN(el,get)(EditLine *el, int op, ...)
break;
case EL_TERMINAL:
- term_get(el, va_arg(ap, const char **));
+ terminal_get(el, va_arg(ap, const char **));
rv = 0;
break;
@@ -423,26 +438,17 @@ FUN(el,get)(EditLine *el, int op, ...)
char *argv[20];
int i;
- for (i = 1; i < (int)(sizeof(argv) / sizeof(argv[0])); i++)
+ for (i = 1; i < (int)__arraycount(argv); i++)
if ((argv[i] = va_arg(ap, char *)) == NULL)
break;
- switch (op) {
- case EL_GETTC:
- argv[0] = name;
- rv = term_gettc(el, i, argv);
- break;
-
- default:
- rv = -1;
- EL_ABORT((el->el_errfile, "Bad op %d\n", op));
- break;
- }
+ argv[0] = name;
+ rv = terminal_gettc(el, i, argv);
break;
}
case EL_GETCFN:
- *va_arg(ap, el_rfunc_t *) = el_read_getfn(el);
+ *va_arg(ap, el_rfunc_t *) = el_read_getfn(el->el_read);
rv = 0;
break;
@@ -452,7 +458,7 @@ FUN(el,get)(EditLine *el, int op, ...)
break;
case EL_UNBUFFERED:
- *va_arg(ap, int *) = (!(el->el_flags & UNBUFFERED));
+ *va_arg(ap, int *) = (el->el_flags & UNBUFFERED) != 0;
rv = 0;
break;
@@ -486,81 +492,97 @@ FUN(el,get)(EditLine *el, int op, ...)
}
va_end(ap);
- return (rv);
+ return rv;
}
/* el_line():
* Return editing info
*/
-public const TYPE(LineInfo) *
-FUN(el,line)(EditLine *el)
+const LineInfoW *
+el_wline(EditLine *el)
{
- return (const TYPE(LineInfo) *) (void *) &el->el_line;
+ return (const LineInfoW *)(void *)&el->el_line;
}
/* el_source():
* Source a file
*/
-public int
+int
el_source(EditLine *el, const char *fname)
{
FILE *fp;
size_t len;
+ ssize_t slen;
char *ptr;
- char path[MAXPATHLEN];
- const Char *dptr;
+ char *path = NULL;
+ const wchar_t *dptr;
+ int error = 0;
fp = NULL;
if (fname == NULL) {
+#ifdef HAVE_ISSETUGID
static const char elpath[] = "/.editrc";
+ size_t plen = sizeof(elpath);
-#ifdef HAVE_ISSETUGID
if (issetugid())
- return (-1);
-#endif
+ return -1;
if ((ptr = getenv("HOME")) == NULL)
- return (-1);
- if (strlcpy(path, ptr, sizeof(path)) >= sizeof(path))
- return (-1);
- if (strlcat(path, elpath, sizeof(path)) >= sizeof(path))
- return (-1);
+ return -1;
+ plen += strlen(ptr);
+ if ((path = el_malloc(plen * sizeof(*path))) == NULL)
+ return -1;
+ (void)snprintf(path, plen, "%s%s", ptr, elpath);
fname = path;
+#else
+ /*
+ * If issetugid() is missing, always return an error, in order
+ * to keep from inadvertently opening up the user to a security
+ * hole.
+ */
+ return -1;
+#endif
}
if (fp == NULL)
fp = fopen(fname, "r");
- if (fp == NULL)
- return (-1);
+ if (fp == NULL) {
+ el_free(path);
+ return -1;
+ }
+
+ ptr = NULL;
+ len = 0;
+ while ((slen = getline(&ptr, &len, fp)) != -1) {
+ if (*ptr == '\n')
+ continue; /* Empty line. */
+ if (slen > 0 && ptr[--slen] == '\n')
+ ptr[slen] = '\0';
- while ((ptr = fgetln(fp, &len)) != NULL) {
dptr = ct_decode_string(ptr, &el->el_scratch);
if (!dptr)
continue;
- if (len > 0 && dptr[len - 1] == '\n')
- --len;
-
/* loop until first non-space char or EOL */
- while (*dptr != '\0' && Isspace(*dptr))
+ while (*dptr != '\0' && iswspace(*dptr))
dptr++;
if (*dptr == '#')
continue; /* ignore, this is a comment line */
- if (parse_line(el, dptr) == -1) {
- (void) fclose(fp);
- return (-1);
- }
+ if ((error = parse_line(el, dptr)) == -1)
+ break;
}
+ free(ptr);
+ el_free(path);
(void) fclose(fp);
- return (0);
+ return error;
}
/* el_resize():
* Called from program when terminal is resized
*/
-public void
+void
el_resize(EditLine *el)
{
int lins, cols;
@@ -571,8 +593,8 @@ el_resize(EditLine *el)
(void) sigprocmask(SIG_BLOCK, &nset, &oset);
/* get the correct window size */
- if (term_get_size(el, &lins, &cols))
- term_change_size(el, lins, cols);
+ if (terminal_get_size(el, &lins, &cols))
+ terminal_change_size(el, lins, cols);
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
}
@@ -581,38 +603,38 @@ el_resize(EditLine *el)
/* el_beep():
* Called from the program to beep
*/
-public void
+void
el_beep(EditLine *el)
{
- term_beep(el);
+ terminal_beep(el);
}
/* el_editmode()
* Set the state of EDIT_DISABLED from the `edit' command.
*/
-protected int
+libedit_private int
/*ARGSUSED*/
-el_editmode(EditLine *el, int argc, const Char **argv)
+el_editmode(EditLine *el, int argc, const wchar_t **argv)
{
- const Char *how;
+ const wchar_t *how;
if (argv == NULL || argc != 2 || argv[1] == NULL)
- return (-1);
+ return -1;
how = argv[1];
- if (Strcmp(how, STR("on")) == 0) {
+ if (wcscmp(how, L"on") == 0) {
el->el_flags &= ~EDIT_DISABLED;
tty_rawmode(el);
- } else if (Strcmp(how, STR("off")) == 0) {
+ } else if (wcscmp(how, L"off") == 0) {
tty_cookedmode(el);
el->el_flags |= EDIT_DISABLED;
}
else {
- (void) fprintf(el->el_errfile, "edit: Bad value `" FSTR "'.\n",
+ (void) fprintf(el->el_errfile, "edit: Bad value `%ls'.\n",
how);
- return (-1);
+ return -1;
}
- return (0);
+ return 0;
}
diff --git a/lib/libedit/src/el.h b/lib/libedit/src/el.h
index da07b3061a3c..c281bc01b4d2 100644
--- a/lib/libedit/src/el.h
+++ b/lib/libedit/src/el.h
@@ -1,4 +1,4 @@
-/* $NetBSD: el.h,v 1.22 2011/01/27 23:11:40 christos Exp $ */
+/* $NetBSD: el.h,v 1.41 2016/05/24 15:00:45 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -48,21 +48,15 @@
#include "histedit.h"
#include "chartype.h"
-#include <stdio.h>
-#include <sys/types.h>
-#define EL_BUFSIZ 1024 /* Maximum line size */
+#define EL_BUFSIZ ((size_t)1024) /* Maximum line size */
#define HANDLE_SIGNALS 0x01
#define NO_TTY 0x02
#define EDIT_DISABLED 0x04
#define UNBUFFERED 0x08
#define CHARSET_IS_UTF8 0x10
-#define IGNORE_EXTCHARS 0x20 /* Ignore characters read > 0xff */
#define NARROW_HISTORY 0x40
-#define NARROW_READ 0x80
-
-typedef int bool_t; /* True or not */
typedef unsigned char el_action_t; /* Index to command array */
@@ -72,10 +66,10 @@ typedef struct coord_t { /* Position on the screen */
} coord_t;
typedef struct el_line_t {
- Char *buffer; /* Input line */
- Char *cursor; /* Cursor position */
- Char *lastchar; /* Last character */
- const Char *limit; /* Max position */
+ wchar_t *buffer; /* Input line */
+ wchar_t *cursor; /* Cursor position */
+ wchar_t *lastchar; /* Last character */
+ const wchar_t *limit; /* Max position */
} el_line_t;
/*
@@ -87,8 +81,8 @@ typedef struct el_state_t {
int argument; /* Numeric argument */
int metanext; /* Is the next char a meta char */
el_action_t lastcmd; /* Previous command */
- el_action_t thiscmd; /* this command */
- Char thisch; /* char that generated it */
+ el_action_t thiscmd; /* this command */
+ wchar_t thisch; /* char that generated it */
} el_state_t;
/*
@@ -100,21 +94,19 @@ typedef struct el_state_t {
#include "tty.h"
#include "prompt.h"
-#include "key.h"
-#include "el_term.h"
+#include "keymacro.h"
+#include "terminal.h"
#include "refresh.h"
#include "chared.h"
-#include "common.h"
#include "search.h"
#include "hist.h"
#include "map.h"
-#include "parse.h"
#include "sig.h"
-#include "help.h"
-#include "read.h"
+
+struct el_read_t;
struct editline {
- Char *el_prog; /* the program name */
+ wchar_t *el_prog; /* the program name */
FILE *el_infile; /* Stdio stuff */
FILE *el_outfile; /* Stdio stuff */
FILE *el_errfile; /* Stdio stuff */
@@ -122,33 +114,31 @@ struct editline {
int el_outfd; /* Output file descriptor */
int el_errfd; /* Error file descriptor */
int el_flags; /* Various flags. */
- int el_errno; /* Local copy of errno */
coord_t el_cursor; /* Cursor location */
- Char **el_display; /* Real screen image = what is there */
- Char **el_vdisplay; /* Virtual screen image = what we see */
+ wchar_t **el_display; /* Real screen image = what is there */
+ wchar_t **el_vdisplay; /* Virtual screen image = what we see */
void *el_data; /* Client data */
el_line_t el_line; /* The current line information */
el_state_t el_state; /* Current editor state */
- el_term_t el_term; /* Terminal dependent stuff */
+ el_terminal_t el_terminal; /* Terminal dependent stuff */
el_tty_t el_tty; /* Tty dependent stuff */
el_refresh_t el_refresh; /* Refresh stuff */
el_prompt_t el_prompt; /* Prompt stuff */
el_prompt_t el_rprompt; /* Prompt stuff */
el_chared_t el_chared; /* Characted editor stuff */
el_map_t el_map; /* Key mapping stuff */
- el_key_t el_key; /* Key binding stuff */
+ el_keymacro_t el_keymacro; /* Key binding stuff */
el_history_t el_history; /* History stuff */
el_search_t el_search; /* Search stuff */
el_signal_t el_signal; /* Signal handling stuff */
- el_read_t el_read; /* Character reading stuff */
-#ifdef WIDECHAR
+ struct el_read_t *el_read; /* Character reading stuff */
+ ct_buffer_t el_visual; /* Buffer for displayable str */
ct_buffer_t el_scratch; /* Scratch conversion buffer */
ct_buffer_t el_lgcyconv; /* Buffer for legacy wrappers */
LineInfo el_lgcylinfo; /* Legacy LineInfo buffer */
-#endif
};
-protected int el_editmode(EditLine *, int, const Char **);
+libedit_private int el_editmode(EditLine *, int, const wchar_t **);
#ifdef DEBUG
#define EL_ABORT(a) do { \
diff --git a/lib/libedit/src/eln.c b/lib/libedit/src/eln.c
index 04e992fbad2b..aa0a5b565dd7 100644
--- a/lib/libedit/src/eln.c
+++ b/lib/libedit/src/eln.c
@@ -1,4 +1,4 @@
-/* $NetBSD: eln.c,v 1.9 2010/11/04 13:53:12 christos Exp $ */
+/* $NetBSD: eln.c,v 1.34 2016/05/09 21:37:34 christos Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -12,13 +12,6 @@
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the NetBSD
- * Foundation, Inc. and its contributors.
- * 4. Neither the name of The NetBSD Foundation nor the names of its
- * contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
@@ -34,35 +27,38 @@
*/
#include "config.h"
#if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: eln.c,v 1.9 2010/11/04 13:53:12 christos Exp $");
+__RCSID("$NetBSD: eln.c,v 1.34 2016/05/09 21:37:34 christos Exp $");
#endif /* not lint && not SCCSID */
-#include "histedit.h"
-#include "el.h"
-#include "read.h"
+#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
-public int
+#include "el.h"
+
+int
el_getc(EditLine *el, char *cp)
{
int num_read;
wchar_t wc = 0;
- if (!(el->el_flags & CHARSET_IS_UTF8))
- el->el_flags |= IGNORE_EXTCHARS;
- num_read = el_wgetc (el, &wc);
- if (!(el->el_flags & CHARSET_IS_UTF8))
- el->el_flags &= ~IGNORE_EXTCHARS;
-
- if (num_read > 0)
- *cp = (unsigned char)wc;
- return num_read;
+ num_read = el_wgetc(el, &wc);
+ *cp = '\0';
+ if (num_read <= 0)
+ return num_read;
+ num_read = wctob(wc);
+ if (num_read == EOF) {
+ errno = ERANGE;
+ return -1;
+ } else {
+ *cp = (char)num_read;
+ return 1;
+ }
}
-public void
+void
el_push(EditLine *el, const char *str)
{
/* Using multibyte->wide string decoding works fine under single-byte
@@ -71,36 +67,41 @@ el_push(EditLine *el, const char *str)
}
-public const char *
+const char *
el_gets(EditLine *el, int *nread)
{
const wchar_t *tmp;
- el->el_flags |= IGNORE_EXTCHARS;
tmp = el_wgets(el, nread);
- el->el_flags &= ~IGNORE_EXTCHARS;
+ if (tmp != NULL) {
+ int i;
+ size_t nwread = 0;
+
+ for (i = 0; i < *nread; i++)
+ nwread += ct_enc_width(tmp[i]);
+ *nread = (int)nwread;
+ }
return ct_encode_string(tmp, &el->el_lgcyconv);
}
-public int
+int
el_parse(EditLine *el, int argc, const char *argv[])
{
int ret;
const wchar_t **wargv;
- wargv = (const wchar_t **)
- ct_decode_argv(argc, argv, &el->el_lgcyconv);
+ wargv = (void *)ct_decode_argv(argc, argv, &el->el_lgcyconv);
if (!wargv)
return -1;
ret = el_wparse(el, argc, wargv);
- ct_free_argv(wargv);
+ el_free(wargv);
return ret;
}
-public int
+int
el_set(EditLine *el, int op, ...)
{
va_list ap;
@@ -125,6 +126,22 @@ el_set(EditLine *el, int op, ...)
break;
}
+ case EL_ALIAS_TEXT: {
+ el_afunc_t p = va_arg(ap, el_afunc_t);
+ void *arg = va_arg(ap, void *);
+ ret = ch_aliasfun(el, p, arg);
+ break;
+ }
+
+ case EL_PROMPT_ESC:
+ case EL_RPROMPT_ESC: {
+ el_pfunc_t p = va_arg(ap, el_pfunc_t);
+ int c = va_arg(ap, int);
+
+ ret = prompt_set(el, p, c, op, 0);
+ break;
+ }
+
case EL_TERMINAL: /* const char * */
ret = el_wset(el, op, va_arg(ap, char *));
break;
@@ -149,12 +166,11 @@ el_set(EditLine *el, int op, ...)
const char *argv[20];
int i;
const wchar_t **wargv;
- for (i = 1; i < (int)__arraycount(argv); ++i)
- if ((argv[i] = va_arg(ap, char *)) == NULL)
+ for (i = 1; i < (int)__arraycount(argv) - 1; ++i)
+ if ((argv[i] = va_arg(ap, const char *)) == NULL)
break;
- argv[0] = NULL;
- wargv = (const wchar_t **)
- ct_decode_argv(i, argv, &el->el_lgcyconv);
+ argv[0] = argv[i] = NULL;
+ wargv = (void *)ct_decode_argv(i + 1, argv, &el->el_lgcyconv);
if (!wargv) {
ret = -1;
goto out;
@@ -166,29 +182,29 @@ el_set(EditLine *el, int op, ...)
*/
switch (op) {
case EL_BIND:
- wargv[0] = STR("bind");
+ wargv[0] = L"bind";
ret = map_bind(el, i, wargv);
break;
case EL_TELLTC:
- wargv[0] = STR("telltc");
- ret = term_telltc(el, i, wargv);
+ wargv[0] = L"telltc";
+ ret = terminal_telltc(el, i, wargv);
break;
case EL_SETTC:
- wargv[0] = STR("settc");
- ret = term_settc(el, i, wargv);
+ wargv[0] = L"settc";
+ ret = terminal_settc(el, i, wargv);
break;
case EL_ECHOTC:
- wargv[0] = STR("echotc");
- ret = term_echotc(el, i, wargv);
+ wargv[0] = L"echotc";
+ ret = terminal_echotc(el, i, wargv);
break;
case EL_SETTY:
- wargv[0] = STR("setty");
+ wargv[0] = L"setty";
ret = tty_stty(el, i, wargv);
break;
default:
ret = -1;
}
- ct_free_argv(wargv);
+ el_free(wargv);
break;
}
@@ -207,40 +223,42 @@ el_set(EditLine *el, int op, ...)
ret = -1;
goto out;
}
- // XXX: The two strdup's leak
- ret = map_addfunc(el, Strdup(wargv[0]), Strdup(wargv[1]),
+ /* XXX: The two strdup's leak */
+ ret = map_addfunc(el, wcsdup(wargv[0]), wcsdup(wargv[1]),
func);
- ct_free_argv(wargv);
+ el_free(wargv);
break;
}
case EL_HIST: { /* hist_fun_t, const char * */
hist_fun_t fun = va_arg(ap, hist_fun_t);
- ptr_t ptr = va_arg(ap, ptr_t);
+ void *ptr = va_arg(ap, void *);
ret = hist_set(el, fun, ptr);
el->el_flags |= NARROW_HISTORY;
break;
}
- /* XXX: do we need to change el_rfunc_t? */
+
case EL_GETCFN: /* el_rfunc_t */
ret = el_wset(el, op, va_arg(ap, el_rfunc_t));
- el->el_flags |= NARROW_READ;
break;
+
case EL_CLIENTDATA: /* void * */
ret = el_wset(el, op, va_arg(ap, void *));
break;
+
case EL_SETFP: { /* int, FILE * */
int what = va_arg(ap, int);
FILE *fp = va_arg(ap, FILE *);
ret = el_wset(el, op, what, fp);
break;
}
- case EL_PROMPT_ESC: /* el_pfunc_t, char */
- case EL_RPROMPT_ESC: {
- el_pfunc_t p = va_arg(ap, el_pfunc_t);
- char c = va_arg(ap, int);
- ret = prompt_set(el, p, c, op, 0);
+
+ case EL_REFRESH:
+ re_clear_display(el);
+ re_refresh(el);
+ terminal__flush(el);
+ ret = 0;
break;
- }
+
default:
ret = -1;
break;
@@ -252,7 +270,7 @@ out:
}
-public int
+int
el_get(EditLine *el, int op, ...)
{
va_list ap;
@@ -275,9 +293,9 @@ el_get(EditLine *el, int op, ...)
case EL_RPROMPT_ESC: {
el_pfunc_t *p = va_arg(ap, el_pfunc_t *);
char *c = va_arg(ap, char *);
- wchar_t wc;
+ wchar_t wc = 0;
ret = prompt_get(el, p, &wc, op);
- *c = (unsigned char)wc;
+ *c = (char)wc;
break;
}
@@ -310,11 +328,10 @@ el_get(EditLine *el, int op, ...)
if ((argv[i] = va_arg(ap, char *)) == NULL)
break;
argv[0] = gettc;
- ret = term_gettc(el, i, argv);
+ ret = terminal_gettc(el, i, argv);
break;
}
- /* XXX: do we need to change el_rfunc_t? */
case EL_GETCFN: /* el_rfunc_t */
ret = el_wget(el, op, va_arg(ap, el_rfunc_t *));
break;
@@ -346,7 +363,7 @@ el_line(EditLine *el)
const LineInfoW *winfo = el_wline(el);
LineInfo *info = &el->el_lgcylinfo;
size_t offset;
- const Char *p;
+ const wchar_t *p;
info->buffer = ct_encode_string(winfo->buffer, &el->el_lgcyconv);
diff --git a/lib/libedit/src/emacs.c b/lib/libedit/src/emacs.c
index 8fb6f4b50ae0..0636c28b26fc 100644
--- a/lib/libedit/src/emacs.c
+++ b/lib/libedit/src/emacs.c
@@ -1,4 +1,4 @@
-/* $NetBSD: emacs.c,v 1.23 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,37 +37,41 @@
#if 0
static char sccsid[] = "@(#)emacs.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: emacs.c,v 1.23 2009/12/30 22:37:40 christos Exp $");
+__RCSID("$NetBSD: emacs.c,v 1.36 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
* emacs.c: Emacs functions
*/
+#include <ctype.h>
+
#include "el.h"
+#include "emacs.h"
+#include "fcns.h"
/* em_delete_or_list():
* Delete character under cursor or list completions if at end of line
* [^D]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_delete_or_list(EditLine *el, Int c)
+em_delete_or_list(EditLine *el, wint_t c)
{
if (el->el_line.cursor == el->el_line.lastchar) {
/* if I'm at the end */
if (el->el_line.cursor == el->el_line.buffer) {
/* and the beginning */
- term_writec(el, c); /* then do an EOF */
- return (CC_EOF);
+ terminal_writec(el, c); /* then do an EOF */
+ return CC_EOF;
} else {
/*
* Here we could list completions, but it is an
* error right now
*/
- term_beep(el);
- return (CC_ERROR);
+ terminal_beep(el);
+ return CC_ERROR;
}
} else {
if (el->el_state.doingarg)
@@ -77,7 +81,7 @@ em_delete_or_list(EditLine *el, Int c)
if (el->el_line.cursor > el->el_line.lastchar)
el->el_line.cursor = el->el_line.lastchar;
/* bounds check */
- return (CC_REFRESH);
+ return CC_REFRESH;
}
}
@@ -86,14 +90,14 @@ em_delete_or_list(EditLine *el, Int c)
* Cut from cursor to end of current word
* [M-d]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_delete_next_word(EditLine *el, Int c __attribute__((__unused__)))
+em_delete_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *cp, *p, *kp;
+ wchar_t *cp, *p, *kp;
if (el->el_line.cursor == el->el_line.lastchar)
- return (CC_ERROR);
+ return CC_ERROR;
cp = c__next_word(el->el_line.cursor, el->el_line.lastchar,
el->el_state.argument, ce__isword);
@@ -107,7 +111,7 @@ em_delete_next_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_line.cursor > el->el_line.lastchar)
el->el_line.cursor = el->el_line.lastchar;
/* bounds check */
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -115,19 +119,19 @@ em_delete_next_word(EditLine *el, Int c __attribute__((__unused__)))
* Paste cut buffer at cursor position
* [^Y]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_yank(EditLine *el, Int c __attribute__((__unused__)))
+em_yank(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *kp, *cp;
+ wchar_t *kp, *cp;
if (el->el_chared.c_kill.last == el->el_chared.c_kill.buf)
- return (CC_NORM);
+ return CC_NORM;
if (el->el_line.lastchar +
(el->el_chared.c_kill.last - el->el_chared.c_kill.buf) >=
el->el_line.limit)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_chared.c_kill.mark = el->el_line.cursor;
cp = el->el_line.cursor;
@@ -143,7 +147,7 @@ em_yank(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_state.argument == 1)
el->el_line.cursor = cp;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -151,11 +155,11 @@ em_yank(EditLine *el, Int c __attribute__((__unused__)))
* Cut the entire line and save in cut buffer
* [^U]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_kill_line(EditLine *el, Int c __attribute__((__unused__)))
+em_kill_line(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *kp, *cp;
+ wchar_t *kp, *cp;
cp = el->el_line.buffer;
kp = el->el_chared.c_kill.buf;
@@ -165,7 +169,7 @@ em_kill_line(EditLine *el, Int c __attribute__((__unused__)))
/* zap! -- delete all of it */
el->el_line.lastchar = el->el_line.buffer;
el->el_line.cursor = el->el_line.buffer;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -173,14 +177,14 @@ em_kill_line(EditLine *el, Int c __attribute__((__unused__)))
* Cut area between mark and cursor and save in cut buffer
* [^W]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_kill_region(EditLine *el, Int c __attribute__((__unused__)))
+em_kill_region(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *kp, *cp;
+ wchar_t *kp, *cp;
if (!el->el_chared.c_kill.mark)
- return (CC_ERROR);
+ return CC_ERROR;
if (el->el_chared.c_kill.mark > el->el_line.cursor) {
cp = el->el_line.cursor;
@@ -198,7 +202,7 @@ em_kill_region(EditLine *el, Int c __attribute__((__unused__)))
c_delbefore(el, (int)(cp - el->el_chared.c_kill.mark));
el->el_line.cursor = el->el_chared.c_kill.mark;
}
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -206,14 +210,14 @@ em_kill_region(EditLine *el, Int c __attribute__((__unused__)))
* Copy area between mark and cursor to cut buffer
* [M-W]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_copy_region(EditLine *el, Int c __attribute__((__unused__)))
+em_copy_region(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *kp, *cp;
+ wchar_t *kp, *cp;
if (!el->el_chared.c_kill.mark)
- return (CC_ERROR);
+ return CC_ERROR;
if (el->el_chared.c_kill.mark > el->el_line.cursor) {
cp = el->el_line.cursor;
@@ -228,7 +232,7 @@ em_copy_region(EditLine *el, Int c __attribute__((__unused__)))
*kp++ = *cp++; /* copy it */
el->el_chared.c_kill.last = kp;
}
- return (CC_NORM);
+ return CC_NORM;
}
@@ -236,8 +240,8 @@ em_copy_region(EditLine *el, Int c __attribute__((__unused__)))
* Exchange the two characters before the cursor
* Gosling emacs transpose chars [^T]
*/
-protected el_action_t
-em_gosmacs_transpose(EditLine *el, Int c)
+libedit_private el_action_t
+em_gosmacs_transpose(EditLine *el, wint_t c)
{
if (el->el_line.cursor > &el->el_line.buffer[1]) {
@@ -245,9 +249,9 @@ em_gosmacs_transpose(EditLine *el, Int c)
c = el->el_line.cursor[-2];
el->el_line.cursor[-2] = el->el_line.cursor[-1];
el->el_line.cursor[-1] = c;
- return (CC_REFRESH);
+ return CC_REFRESH;
} else
- return (CC_ERROR);
+ return CC_ERROR;
}
@@ -255,12 +259,12 @@ em_gosmacs_transpose(EditLine *el, Int c)
* Move next to end of current word
* [M-f]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_next_word(EditLine *el, Int c __attribute__((__unused__)))
+em_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor == el->el_line.lastchar)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = c__next_word(el->el_line.cursor,
el->el_line.lastchar,
@@ -270,9 +274,9 @@ em_next_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_map.type == MAP_VI)
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -280,23 +284,23 @@ em_next_word(EditLine *el, Int c __attribute__((__unused__)))
* Uppercase the characters from cursor to end of current word
* [M-u]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_upper_case(EditLine *el, Int c __attribute__((__unused__)))
+em_upper_case(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *cp, *ep;
+ wchar_t *cp, *ep;
ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
el->el_state.argument, ce__isword);
for (cp = el->el_line.cursor; cp < ep; cp++)
- if (Islower(*cp))
- *cp = Toupper(*cp);
+ if (iswlower(*cp))
+ *cp = towupper(*cp);
el->el_line.cursor = ep;
if (el->el_line.cursor > el->el_line.lastchar)
el->el_line.cursor = el->el_line.lastchar;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -304,31 +308,31 @@ em_upper_case(EditLine *el, Int c __attribute__((__unused__)))
* Capitalize the characters from cursor to end of current word
* [M-c]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_capitol_case(EditLine *el, Int c __attribute__((__unused__)))
+em_capitol_case(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *cp, *ep;
+ wchar_t *cp, *ep;
ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
el->el_state.argument, ce__isword);
for (cp = el->el_line.cursor; cp < ep; cp++) {
- if (Isalpha(*cp)) {
- if (Islower(*cp))
- *cp = Toupper(*cp);
+ if (iswalpha(*cp)) {
+ if (iswlower(*cp))
+ *cp = towupper(*cp);
cp++;
break;
}
}
for (; cp < ep; cp++)
- if (Isupper(*cp))
- *cp = Tolower(*cp);
+ if (iswupper(*cp))
+ *cp = towlower(*cp);
el->el_line.cursor = ep;
if (el->el_line.cursor > el->el_line.lastchar)
el->el_line.cursor = el->el_line.lastchar;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -336,23 +340,23 @@ em_capitol_case(EditLine *el, Int c __attribute__((__unused__)))
* Lowercase the characters from cursor to end of current word
* [M-l]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_lower_case(EditLine *el, Int c __attribute__((__unused__)))
+em_lower_case(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *cp, *ep;
+ wchar_t *cp, *ep;
ep = c__next_word(el->el_line.cursor, el->el_line.lastchar,
el->el_state.argument, ce__isword);
for (cp = el->el_line.cursor; cp < ep; cp++)
- if (Isupper(*cp))
- *cp = Tolower(*cp);
+ if (iswupper(*cp))
+ *cp = towlower(*cp);
el->el_line.cursor = ep;
if (el->el_line.cursor > el->el_line.lastchar)
el->el_line.cursor = el->el_line.lastchar;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -360,13 +364,13 @@ em_lower_case(EditLine *el, Int c __attribute__((__unused__)))
* Set the mark at cursor
* [^@]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_set_mark(EditLine *el, Int c __attribute__((__unused__)))
+em_set_mark(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_chared.c_kill.mark = el->el_line.cursor;
- return (CC_NORM);
+ return CC_NORM;
}
@@ -374,16 +378,16 @@ em_set_mark(EditLine *el, Int c __attribute__((__unused__)))
* Exchange the cursor and mark
* [^X^X]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_exchange_mark(EditLine *el, Int c __attribute__((__unused__)))
+em_exchange_mark(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *cp;
+ wchar_t *cp;
cp = el->el_line.cursor;
el->el_line.cursor = el->el_chared.c_kill.mark;
el->el_chared.c_kill.mark = cp;
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -391,16 +395,16 @@ em_exchange_mark(EditLine *el, Int c __attribute__((__unused__)))
* Universal argument (argument times 4)
* [^U]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_universal_argument(EditLine *el, Int c __attribute__((__unused__)))
+em_universal_argument(EditLine *el, wint_t c __attribute__((__unused__)))
{ /* multiply current argument by 4 */
if (el->el_state.argument > 1000000)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_state.doingarg = 1;
el->el_state.argument *= 4;
- return (CC_ARGHACK);
+ return CC_ARGHACK;
}
@@ -408,41 +412,41 @@ em_universal_argument(EditLine *el, Int c __attribute__((__unused__)))
* Add 8th bit to next character typed
* [<ESC>]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_meta_next(EditLine *el, Int c __attribute__((__unused__)))
+em_meta_next(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_state.metanext = 1;
- return (CC_ARGHACK);
+ return CC_ARGHACK;
}
/* em_toggle_overwrite():
* Switch from insert to overwrite mode or vice versa
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_toggle_overwrite(EditLine *el, Int c __attribute__((__unused__)))
+em_toggle_overwrite(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_state.inputmode = (el->el_state.inputmode == MODE_INSERT) ?
MODE_REPLACE : MODE_INSERT;
- return (CC_NORM);
+ return CC_NORM;
}
/* em_copy_prev_word():
* Copy current word to cursor
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_copy_prev_word(EditLine *el, Int c __attribute__((__unused__)))
+em_copy_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *cp, *oldc, *dp;
+ wchar_t *cp, *oldc, *dp;
if (el->el_line.cursor == el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
oldc = el->el_line.cursor;
/* does a bounds check */
@@ -455,33 +459,33 @@ em_copy_prev_word(EditLine *el, Int c __attribute__((__unused__)))
el->el_line.cursor = dp;/* put cursor at end */
- return (CC_REFRESH);
+ return CC_REFRESH;
}
/* em_inc_search_next():
* Emacs incremental next search
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_inc_search_next(EditLine *el, Int c __attribute__((__unused__)))
+em_inc_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_search.patlen = 0;
- return (ce_inc_search(el, ED_SEARCH_NEXT_HISTORY));
+ return ce_inc_search(el, ED_SEARCH_NEXT_HISTORY);
}
/* em_inc_search_prev():
* Emacs incremental reverse search
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_inc_search_prev(EditLine *el, Int c __attribute__((__unused__)))
+em_inc_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_search.patlen = 0;
- return (ce_inc_search(el, ED_SEARCH_PREV_HISTORY));
+ return ce_inc_search(el, ED_SEARCH_PREV_HISTORY);
}
@@ -489,13 +493,13 @@ em_inc_search_prev(EditLine *el, Int c __attribute__((__unused__)))
* Delete the character to the left of the cursor
* [^?]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-em_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
+em_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor <= el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
if (el->el_state.doingarg)
c_delbefore(el, el->el_state.argument);
@@ -504,5 +508,5 @@ em_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
el->el_line.cursor -= el->el_state.argument;
if (el->el_line.cursor < el->el_line.buffer)
el->el_line.cursor = el->el_line.buffer;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
diff --git a/lib/libedit/src/fgetln.c b/lib/libedit/src/fgetln.c
deleted file mode 100644
index 6c4f320c12ca..000000000000
--- a/lib/libedit/src/fgetln.c
+++ /dev/null
@@ -1,107 +0,0 @@
-/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */
-
-/*-
- * Copyright (c) 1998 The NetBSD Foundation, Inc.
- * All rights reserved.
- *
- * This code is derived from software contributed to The NetBSD Foundation
- * by Christos Zoulas.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
- * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifdef HAVE_NBTOOL_CONFIG_H
-#include "nbtool_config.h"
-#endif
-
-#if !HAVE_FGETLN
-#include "config.h"
-#include <stdlib.h>
-#ifndef HAVE_NBTOOL_CONFIG_H
-/* These headers are required, but included from nbtool_config.h */
-#include <stdio.h>
-#include <unistd.h>
-#include <errno.h>
-#include <string.h>
-#endif
-
-char *
-fgetln(FILE *fp, size_t *len)
-{
- static char *buf = NULL;
- static size_t bufsiz = 0;
- char *ptr;
-
-
- if (buf == NULL) {
- bufsiz = BUFSIZ;
- if ((buf = malloc(bufsiz)) == NULL)
- return NULL;
- }
-
- if (fgets(buf, bufsiz, fp) == NULL)
- return NULL;
-
- *len = 0;
- while ((ptr = strchr(&buf[*len], '\n')) == NULL) {
- size_t nbufsiz = bufsiz + BUFSIZ;
- char *nbuf = realloc(buf, nbufsiz);
-
- if (nbuf == NULL) {
- int oerrno = errno;
- free(buf);
- errno = oerrno;
- buf = NULL;
- return NULL;
- } else
- buf = nbuf;
-
- if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) {
- buf[bufsiz] = '\0';
- *len = strlen(buf);
- return buf;
- }
-
- *len = bufsiz;
- bufsiz = nbufsiz;
- }
-
- *len = (ptr - buf) + 1;
- return buf;
-}
-
-#endif
-
-#ifdef TEST
-int
-main(int argc, char *argv[])
-{
- char *p;
- size_t len;
-
- while ((p = fgetln(stdin, &len)) != NULL) {
- (void)printf("%zu %s", len, p);
- free(p);
- }
- return 0;
-}
-#endif
diff --git a/lib/libedit/src/filecomplete.c b/lib/libedit/src/filecomplete.c
index 1dcf75bd992b..d1051f087e4f 100644
--- a/lib/libedit/src/filecomplete.c
+++ b/lib/libedit/src/filecomplete.c
@@ -1,4 +1,4 @@
-/* $NetBSD: filecomplete.c,v 1.23 2010/12/06 00:05:38 dholland Exp $ */
+/* $NetBSD: filecomplete.c,v 1.44 2016/10/31 17:46:32 abhinav Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -29,52 +29,27 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-/* AIX requires this to be the first thing in the file. */
-#if defined (_AIX) && !defined (__GNUC__)
- #pragma alloca
-#endif
-
#include "config.h"
-
-#ifdef __GNUC__
-# undef alloca
-# define alloca(n) __builtin_alloca (n)
-#else
-# ifdef HAVE_ALLOCA_H
-# include <alloca.h>
-# else
-# ifndef _AIX
-extern char *alloca ();
-# endif
-# endif
-#endif
-
#if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: filecomplete.c,v 1.23 2010/12/06 00:05:38 dholland Exp $");
+__RCSID("$NetBSD: filecomplete.c,v 1.44 2016/10/31 17:46:32 abhinav Exp $");
#endif /* not lint && not SCCSID */
#include <sys/types.h>
#include <sys/stat.h>
-#include <stdio.h>
#include <dirent.h>
-#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
#include <pwd.h>
-#include <ctype.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-#include <limits.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <vis.h>
#include "el.h"
-#include "fcns.h" /* for EL_NUM_FCNS */
-#include "histedit.h"
#include "filecomplete.h"
-static const Char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@',
- '$', '>', '<', '=', ';', '|', '&', '{', '(', '\0' };
-
+static const wchar_t break_chars[] = L" \t\n\"\\'`@$><=;|&{(";
/********************************/
/* completion functions */
@@ -84,18 +59,21 @@ static const Char break_chars[] = { ' ', '\t', '\n', '"', '\\', '\'', '`', '@',
* if ``user'' isn't valid user name or ``txt'' doesn't start
* w/ '~', returns pointer to strdup()ed copy of ``txt''
*
- * it's callers's responsibility to free() returned string
+ * it's the caller's responsibility to free() the returned string
*/
char *
fn_tilde_expand(const char *txt)
{
- struct passwd pwres, *pass;
+#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
+ struct passwd pwres;
+ char pwbuf[1024];
+#endif
+ struct passwd *pass;
char *temp;
size_t len = 0;
- char pwbuf[1024];
if (txt[0] != '~')
- return (strdup(txt));
+ return strdup(txt);
temp = strchr(txt + 1, '/');
if (temp == NULL) {
@@ -103,8 +81,9 @@ fn_tilde_expand(const char *txt)
if (temp == NULL)
return NULL;
} else {
- len = temp - txt + 1; /* text until string after slash */
- temp = malloc(len);
+ /* text until string after slash */
+ len = (size_t)(temp - txt + 1);
+ temp = el_malloc(len * sizeof(*temp));
if (temp == NULL)
return NULL;
(void)strncpy(temp, txt + 1, len - 2);
@@ -112,12 +91,13 @@ fn_tilde_expand(const char *txt)
}
if (temp[0] == 0) {
#ifdef HAVE_GETPW_R_POSIX
- if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf), &pass) != 0)
+ if (getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf),
+ &pass) != 0)
pass = NULL;
#elif HAVE_GETPW_R_DRAFT
pass = getpwuid_r(getuid(), &pwres, pwbuf, sizeof(pwbuf));
#else
- pass = getpwuid(getuid());
+ pass = getpwuid(getuid());
#endif
} else {
#ifdef HAVE_GETPW_R_POSIX
@@ -129,20 +109,21 @@ fn_tilde_expand(const char *txt)
pass = getpwnam(temp);
#endif
}
- free(temp); /* value no more needed */
+ el_free(temp); /* value no more needed */
if (pass == NULL)
- return (strdup(txt));
+ return strdup(txt);
/* update pointer txt to point at string immedially following */
/* first slash */
txt += len;
- temp = malloc(strlen(pass->pw_dir) + 1 + strlen(txt) + 1);
+ len = strlen(pass->pw_dir) + 1 + strlen(txt) + 1;
+ temp = el_malloc(len * sizeof(*temp));
if (temp == NULL)
return NULL;
- (void)sprintf(temp, "%s/%s", pass->pw_dir, txt);
+ (void)snprintf(temp, len, "%s/%s", pass->pw_dir, txt);
- return (temp);
+ return temp;
}
@@ -151,7 +132,7 @@ fn_tilde_expand(const char *txt)
* such file can be found
* value of ``state'' is ignored
*
- * it's caller's responsibility to free returned string
+ * it's the caller's responsibility to free the returned string
*/
char *
fn_filename_completion_function(const char *text, int state)
@@ -168,19 +149,21 @@ fn_filename_completion_function(const char *text, int state)
if (temp) {
char *nptr;
temp++;
- nptr = realloc(filename, strlen(temp) + 1);
+ nptr = el_realloc(filename, (strlen(temp) + 1) *
+ sizeof(*nptr));
if (nptr == NULL) {
- free(filename);
+ el_free(filename);
filename = NULL;
return NULL;
}
filename = nptr;
(void)strcpy(filename, temp);
- len = temp - text; /* including last slash */
+ len = (size_t)(temp - text); /* including last slash */
- nptr = realloc(dirname, len + 1);
+ nptr = el_realloc(dirname, (len + 1) *
+ sizeof(*nptr));
if (nptr == NULL) {
- free(dirname);
+ el_free(dirname);
dirname = NULL;
return NULL;
}
@@ -188,7 +171,7 @@ fn_filename_completion_function(const char *text, int state)
(void)strncpy(dirname, text, len);
dirname[len] = '\0';
} else {
- free(filename);
+ el_free(filename);
if (*text == 0)
filename = NULL;
else {
@@ -196,7 +179,7 @@ fn_filename_completion_function(const char *text, int state)
if (filename == NULL)
return NULL;
}
- free(dirname);
+ el_free(dirname);
dirname = NULL;
}
@@ -207,7 +190,7 @@ fn_filename_completion_function(const char *text, int state)
/* support for ``~user'' syntax */
- free(dirpath);
+ el_free(dirpath);
dirpath = NULL;
if (dirname == NULL) {
if ((dirname = strdup("")) == NULL)
@@ -223,7 +206,7 @@ fn_filename_completion_function(const char *text, int state)
dir = opendir(dirpath);
if (!dir)
- return (NULL); /* cannot open the directory */
+ return NULL; /* cannot open the directory */
/* will be used in cycle */
filename_len = filename ? strlen(filename) : 0;
@@ -240,8 +223,11 @@ fn_filename_completion_function(const char *text, int state)
/* otherwise, get first entry where first */
/* filename_len characters are equal */
if (entry->d_name[0] == filename[0]
- /* Some dirents have d_namlen, but it is not portable. */
+#if HAVE_STRUCT_DIRENT_D_NAMLEN
+ && entry->d_namlen >= filename_len
+#else
&& strlen(entry->d_name) >= filename_len
+#endif
&& strncmp(entry->d_name, filename,
filename_len) == 0)
break;
@@ -249,20 +235,24 @@ fn_filename_completion_function(const char *text, int state)
if (entry) { /* match found */
- /* Some dirents have d_namlen, but it is not portable. */
+#if HAVE_STRUCT_DIRENT_D_NAMLEN
+ len = entry->d_namlen;
+#else
len = strlen(entry->d_name);
+#endif
- temp = malloc(strlen(dirname) + len + 1);
+ len = strlen(dirname) + len + 1;
+ temp = el_malloc(len * sizeof(*temp));
if (temp == NULL)
return NULL;
- (void)sprintf(temp, "%s%s", dirname, entry->d_name);
+ (void)snprintf(temp, len, "%s%s", dirname, entry->d_name);
} else {
(void)closedir(dir);
dir = NULL;
temp = NULL;
}
- return (temp);
+ return temp;
}
@@ -279,7 +269,7 @@ append_char_function(const char *name)
rs = "/";
out:
if (expname)
- free(expname);
+ el_free(expname);
return rs;
}
/*
@@ -302,10 +292,10 @@ completion_matches(const char *text, char *(*genfunc)(const char *, int))
char **nmatch_list;
while (matches + 3 >= match_list_len)
match_list_len <<= 1;
- nmatch_list = realloc(match_list,
- match_list_len * sizeof(char *));
+ nmatch_list = el_realloc(match_list,
+ match_list_len * sizeof(*nmatch_list));
if (nmatch_list == NULL) {
- free(match_list);
+ el_free(match_list);
return NULL;
}
match_list = nmatch_list;
@@ -328,9 +318,9 @@ completion_matches(const char *text, char *(*genfunc)(const char *, int))
max_equal = i;
}
- retstr = malloc(max_equal + 1);
+ retstr = el_malloc((max_equal + 1) * sizeof(*retstr));
if (retstr == NULL) {
- free(match_list);
+ el_free(match_list);
return NULL;
}
(void)strncpy(retstr, match_list[1], max_equal);
@@ -338,9 +328,9 @@ completion_matches(const char *text, char *(*genfunc)(const char *, int))
match_list[0] = retstr;
/* add NULL as last pointer to the array */
- match_list[matches + 1] = (char *) NULL;
+ match_list[matches + 1] = NULL;
- return (match_list);
+ return match_list;
}
/*
@@ -367,7 +357,7 @@ void
fn_display_match_list (EditLine *el, char **matches, size_t num, size_t width)
{
size_t line, lines, col, cols, thisguy;
- int screenwidth = el->el_term.t_size.h;
+ int screenwidth = el->el_terminal.t_size.h;
/* Ignore matches[0]. Avoid 1-based array logic below. */
matches++;
@@ -377,7 +367,7 @@ fn_display_match_list (EditLine *el, char **matches, size_t num, size_t width)
* Find out how many entries can be put on one line; count
* with one space between strings the same way it's printed.
*/
- cols = screenwidth / (width + 1);
+ cols = (size_t)screenwidth / (width + 1);
if (cols == 0)
cols = 1;
@@ -418,14 +408,14 @@ int
fn_complete(EditLine *el,
char *(*complet_func)(const char *, int),
char **(*attempted_completion_function)(const char *, int, int),
- const Char *word_break, const Char *special_prefixes,
+ const wchar_t *word_break, const wchar_t *special_prefixes,
const char *(*app_func)(const char *), size_t query_items,
int *completion_type, int *over, int *point, int *end)
{
- const TYPE(LineInfo) *li;
- Char *temp;
+ const LineInfoW *li;
+ wchar_t *temp;
char **matches;
- const Char *ctemp;
+ const wchar_t *ctemp;
size_t len;
int what_to_do = '\t';
int retval = CC_NORM;
@@ -443,38 +433,36 @@ fn_complete(EditLine *el,
app_func = append_char_function;
/* We now look backwards for the start of a filename/variable word */
- li = FUN(el,line)(el);
+ li = el_wline(el);
ctemp = li->cursor;
while (ctemp > li->buffer
- && !Strchr(word_break, ctemp[-1])
- && (!special_prefixes || !Strchr(special_prefixes, ctemp[-1]) ) )
+ && !wcschr(word_break, ctemp[-1])
+ && (!special_prefixes || !wcschr(special_prefixes, ctemp[-1]) ) )
ctemp--;
- len = li->cursor - ctemp;
-#if defined(__SSP__) || defined(__SSP_ALL__)
- temp = malloc(sizeof(*temp) * (len + 1));
-#else
- temp = alloca(sizeof(*temp) * (len + 1));
-#endif
- (void)Strncpy(temp, ctemp, len);
+ len = (size_t)(li->cursor - ctemp);
+ temp = el_malloc((len + 1) * sizeof(*temp));
+ (void)wcsncpy(temp, ctemp, len);
temp[len] = '\0';
/* these can be used by function called in completion_matches() */
/* or (*attempted_completion_function)() */
- if (point != 0)
+ if (point != NULL)
*point = (int)(li->cursor - li->buffer);
if (end != NULL)
*end = (int)(li->lastchar - li->buffer);
if (attempted_completion_function) {
int cur_off = (int)(li->cursor - li->buffer);
- matches = (*attempted_completion_function) (ct_encode_string(temp, &el->el_scratch),
- (int)(cur_off - len), cur_off);
+ matches = (*attempted_completion_function)(
+ ct_encode_string(temp, &el->el_scratch),
+ cur_off - (int)len, cur_off);
} else
- matches = 0;
+ matches = NULL;
if (!attempted_completion_function ||
(over != NULL && !*over && !matches))
- matches = completion_matches(ct_encode_string(temp, &el->el_scratch), complet_func);
+ matches = completion_matches(
+ ct_encode_string(temp, &el->el_scratch), complet_func);
if (over != NULL)
*over = 0;
@@ -490,24 +478,22 @@ fn_complete(EditLine *el,
*/
if (matches[0][0] != '\0') {
el_deletestr(el, (int) len);
- FUN(el,insertstr)(el,
+ el_winsertstr(el,
ct_decode_string(matches[0], &el->el_scratch));
}
- if (what_to_do == '?')
- goto display_matches;
- if (matches[2] == NULL && strcmp(matches[0], matches[1]) == 0) {
+ if (matches[2] == NULL &&
+ (matches[1] == NULL || strcmp(matches[0], matches[1]) == 0)) {
/*
* We found exact match. Add a space after
* it, unless we do filename completion and the
* object is a directory.
*/
- FUN(el,insertstr)(el,
+ el_winsertstr(el,
ct_decode_string((*app_func)(matches[0]),
&el->el_scratch));
- } else if (what_to_do == '!') {
- display_matches:
+ } else if (what_to_do == '!' || what_to_do == '?') {
/*
* More than one match and requested to list possible
* matches.
@@ -519,7 +505,7 @@ fn_complete(EditLine *el,
maxlen = match_len;
}
/* matches[1] through matches[i-1] are available */
- matches_num = i - 1;
+ matches_num = (size_t)(i - 1);
/* newline to get on next line from command line */
(void)fprintf(el->el_outfile, "\n");
@@ -566,13 +552,11 @@ fn_complete(EditLine *el,
/* free elements of array and the array itself */
for (i = 0; matches[i]; i++)
- free(matches[i]);
- free(matches);
+ el_free(matches[i]);
+ el_free(matches);
matches = NULL;
}
-#if defined(__SSP__) || defined(__SSP_ALL__)
- free(temp);
-#endif
+ el_free(temp);
return retval;
}
@@ -584,6 +568,6 @@ unsigned char
_el_fn_complete(EditLine *el, int ch __attribute__((__unused__)))
{
return (unsigned char)fn_complete(el, NULL, NULL,
- break_chars, NULL, NULL, 100,
+ break_chars, NULL, NULL, (size_t)100,
NULL, NULL, NULL, NULL);
}
diff --git a/lib/libedit/src/filecomplete.h b/lib/libedit/src/filecomplete.h
index 971e6e059391..7e93b9e36520 100644
--- a/lib/libedit/src/filecomplete.h
+++ b/lib/libedit/src/filecomplete.h
@@ -1,4 +1,4 @@
-/* $NetBSD: filecomplete.h,v 1.9 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: filecomplete.h,v 1.10 2016/04/11 00:50:13 christos Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -34,7 +34,7 @@
int fn_complete(EditLine *,
char *(*)(const char *, int),
char **(*)(const char *, int, int),
- const Char *, const Char *, const char *(*)(const char *), size_t,
+ const wchar_t *, const wchar_t *, const char *(*)(const char *), size_t,
int *, int *, int *, int *);
void fn_display_match_list(EditLine *, char **, size_t, size_t);
diff --git a/lib/libedit/src/hist.c b/lib/libedit/src/hist.c
index e12bfb9b9b4a..4f01ade243ef 100644
--- a/lib/libedit/src/hist.c
+++ b/lib/libedit/src/hist.c
@@ -1,4 +1,4 @@
-/* $NetBSD: hist.c,v 1.17 2009/12/30 23:54:52 christos Exp $ */
+/* $NetBSD: hist.c,v 1.30 2016/11/07 15:30:18 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)hist.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: hist.c,v 1.17 2009/12/30 23:54:52 christos Exp $");
+__RCSID("$NetBSD: hist.c,v 1.30 2016/11/07 15:30:18 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -45,12 +45,15 @@ __RCSID("$NetBSD: hist.c,v 1.17 2009/12/30 23:54:52 christos Exp $");
* hist.c: History access functions
*/
#include <stdlib.h>
+#include <string.h>
+#include <vis.h>
+
#include "el.h"
/* hist_init():
* Initialization function.
*/
-protected int
+libedit_private int
hist_init(EditLine *el)
{
@@ -59,20 +62,20 @@ hist_init(EditLine *el)
el->el_history.buf = el_malloc(EL_BUFSIZ * sizeof(*el->el_history.buf));
el->el_history.sz = EL_BUFSIZ;
if (el->el_history.buf == NULL)
- return (-1);
+ return -1;
el->el_history.last = el->el_history.buf;
- return (0);
+ return 0;
}
/* hist_end():
* clean up history;
*/
-protected void
+libedit_private void
hist_end(EditLine *el)
{
- el_free((ptr_t) el->el_history.buf);
+ el_free(el->el_history.buf);
el->el_history.buf = NULL;
}
@@ -80,13 +83,13 @@ hist_end(EditLine *el)
/* hist_set():
* Set new history interface
*/
-protected int
-hist_set(EditLine *el, hist_fun_t fun, ptr_t ptr)
+libedit_private int
+hist_set(EditLine *el, hist_fun_t fun, void *ptr)
{
el->el_history.ref = ptr;
el->el_history.fun = fun;
- return (0);
+ return 0;
}
@@ -94,14 +97,14 @@ hist_set(EditLine *el, hist_fun_t fun, ptr_t ptr)
* Get a history line and update it in the buffer.
* eventno tells us the event to get.
*/
-protected el_action_t
+libedit_private el_action_t
hist_get(EditLine *el)
{
- const Char *hp;
+ const wchar_t *hp;
int h;
if (el->el_history.eventno == 0) { /* if really the current line */
- (void) Strncpy(el->el_line.buffer, el->el_history.buf,
+ (void) wcsncpy(el->el_line.buffer, el->el_history.buf,
el->el_history.sz);
el->el_line.lastchar = el->el_line.buffer +
(el->el_history.last - el->el_history.buf);
@@ -113,25 +116,25 @@ hist_get(EditLine *el)
#endif /* KSHVI */
el->el_line.cursor = el->el_line.lastchar;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
if (el->el_history.ref == NULL)
- return (CC_ERROR);
+ return CC_ERROR;
hp = HIST_FIRST(el);
if (hp == NULL)
- return (CC_ERROR);
+ return CC_ERROR;
for (h = 1; h < el->el_history.eventno; h++)
if ((hp = HIST_NEXT(el)) == NULL) {
el->el_history.eventno = h;
- return (CC_ERROR);
+ return CC_ERROR;
}
- (void) Strncpy(el->el_line.buffer, hp,
+ (void) wcsncpy(el->el_line.buffer, hp,
(size_t)(el->el_line.limit - el->el_line.buffer));
el->el_line.buffer[el->el_line.limit - el->el_line.buffer - 1] = '\0';
- el->el_line.lastchar = el->el_line.buffer + Strlen(el->el_line.buffer);
+ el->el_line.lastchar = el->el_line.buffer + wcslen(el->el_line.buffer);
if (el->el_line.lastchar > el->el_line.buffer
&& el->el_line.lastchar[-1] == '\n')
@@ -146,42 +149,63 @@ hist_get(EditLine *el)
#endif /* KSHVI */
el->el_line.cursor = el->el_line.lastchar;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
/* hist_command()
* process a history command
*/
-protected int
-hist_command(EditLine *el, int argc, const Char **argv)
+libedit_private int
+hist_command(EditLine *el, int argc, const wchar_t **argv)
{
- const Char *str;
+ const wchar_t *str;
int num;
- HistEvent ev;
+ HistEventW ev;
if (el->el_history.ref == NULL)
- return (-1);
+ return -1;
- if (argc == 1 || Strcmp(argv[1], STR("list")) == 0) {
+ if (argc == 1 || wcscmp(argv[1], L"list") == 0) {
+ size_t maxlen = 0;
+ char *buf = NULL;
+ int hno = 1;
/* List history entries */
- for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el))
- (void) fprintf(el->el_outfile, "%d %s",
- el->el_history.ev.num, ct_encode_string(str, &el->el_scratch));
- return (0);
+ for (str = HIST_LAST(el); str != NULL; str = HIST_PREV(el)) {
+ char *ptr =
+ ct_encode_string(str, &el->el_scratch);
+ size_t len = strlen(ptr);
+ if (len > 0 && ptr[len - 1] == '\n')
+ ptr[--len] = '\0';
+ len = len * 4 + 1;
+ if (len >= maxlen) {
+ maxlen = len + 1024;
+ char *nbuf = el_realloc(buf, maxlen);
+ if (nbuf == NULL) {
+ el_free(buf);
+ return -1;
+ }
+ buf = nbuf;
+ }
+ strvis(buf, ptr, VIS_NL);
+ (void) fprintf(el->el_outfile, "%d\t%s\n",
+ hno++, buf);
+ }
+ el_free(buf);
+ return 0;
}
if (argc != 3)
- return (-1);
+ return -1;
- num = (int)Strtol(argv[2], NULL, 0);
+ num = (int)wcstol(argv[2], NULL, 0);
- if (Strcmp(argv[1], STR("size")) == 0)
- return history(el->el_history.ref, &ev, H_SETSIZE, num);
+ if (wcscmp(argv[1], L"size") == 0)
+ return history_w(el->el_history.ref, &ev, H_SETSIZE, num);
- if (Strcmp(argv[1], STR("unique")) == 0)
- return history(el->el_history.ref, &ev, H_SETUNIQUE, num);
+ if (wcscmp(argv[1], L"unique") == 0)
+ return history_w(el->el_history.ref, &ev, H_SETUNIQUE, num);
return -1;
}
@@ -190,11 +214,11 @@ hist_command(EditLine *el, int argc, const Char **argv)
* Enlarge history buffer to specified value. Called from el_enlargebufs().
* Return 0 for failure, 1 for success.
*/
-protected int
+libedit_private int
/*ARGSUSED*/
hist_enlargebuf(EditLine *el, size_t oldsz, size_t newsz)
{
- Char *newbuf;
+ wchar_t *newbuf;
newbuf = el_realloc(el->el_history.buf, newsz * sizeof(*newbuf));
if (!newbuf)
@@ -210,9 +234,8 @@ hist_enlargebuf(EditLine *el, size_t oldsz, size_t newsz)
return 1;
}
-#ifdef WIDECHAR
-protected wchar_t *
-hist_convert(EditLine *el, int fn, ptr_t arg)
+libedit_private wchar_t *
+hist_convert(EditLine *el, int fn, void *arg)
{
HistEventW ev;
if ((*(el)->el_history.fun)((el)->el_history.ref, &ev, fn, arg) == -1)
@@ -220,4 +243,3 @@ hist_convert(EditLine *el, int fn, ptr_t arg)
return ct_decode_string((const char *)(const void *)ev.str,
&el->el_scratch);
}
-#endif
diff --git a/lib/libedit/src/hist.h b/lib/libedit/src/hist.h
index 8c3af40bf868..c58c5bfed81e 100644
--- a/lib/libedit/src/hist.h
+++ b/lib/libedit/src/hist.h
@@ -1,4 +1,4 @@
-/* $NetBSD: hist.h,v 1.12 2009/12/30 23:54:52 christos Exp $ */
+/* $NetBSD: hist.h,v 1.22 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,31 +40,24 @@
#ifndef _h_el_hist
#define _h_el_hist
-#include "histedit.h"
-
-typedef int (*hist_fun_t)(ptr_t, TYPE(HistEvent) *, int, ...);
+typedef int (*hist_fun_t)(void *, HistEventW *, int, ...);
typedef struct el_history_t {
- Char *buf; /* The history buffer */
- size_t sz; /* Size of history buffer */
- Char *last; /* The last character */
+ wchar_t *buf; /* The history buffer */
+ size_t sz; /* Size of history buffer */
+ wchar_t *last; /* The last character */
int eventno; /* Event we are looking for */
- ptr_t ref; /* Argument for history fcns */
+ void *ref; /* Argument for history fcns */
hist_fun_t fun; /* Event access */
- TYPE(HistEvent) ev; /* Event cookie */
+ HistEventW ev; /* Event cookie */
} el_history_t;
#define HIST_FUN_INTERNAL(el, fn, arg) \
((((*(el)->el_history.fun) ((el)->el_history.ref, &(el)->el_history.ev, \
fn, arg)) == -1) ? NULL : (el)->el_history.ev.str)
-#ifdef WIDECHAR
#define HIST_FUN(el, fn, arg) \
(((el)->el_flags & NARROW_HISTORY) ? hist_convert(el, fn, arg) : \
HIST_FUN_INTERNAL(el, fn, arg))
-#else
-#define HIST_FUN(el, fn, arg) HIST_FUN_INTERNAL(el, fn, arg)
-#endif
-
#define HIST_NEXT(el) HIST_FUN(el, H_NEXT, NULL)
#define HIST_FIRST(el) HIST_FUN(el, H_FIRST, NULL)
@@ -73,15 +66,14 @@ typedef struct el_history_t {
#define HIST_SET(el, num) HIST_FUN(el, H_SET, num)
#define HIST_LOAD(el, fname) HIST_FUN(el, H_LOAD fname)
#define HIST_SAVE(el, fname) HIST_FUN(el, H_SAVE fname)
+#define HIST_SAVE_FP(el, fp) HIST_FUN(el, H_SAVE_FP fp)
-protected int hist_init(EditLine *);
-protected void hist_end(EditLine *);
-protected el_action_t hist_get(EditLine *);
-protected int hist_set(EditLine *, hist_fun_t, ptr_t);
-protected int hist_command(EditLine *, int, const Char **);
-protected int hist_enlargebuf(EditLine *, size_t, size_t);
-#ifdef WIDECHAR
-protected wchar_t *hist_convert(EditLine *, int, ptr_t);
-#endif
+libedit_private int hist_init(EditLine *);
+libedit_private void hist_end(EditLine *);
+libedit_private el_action_t hist_get(EditLine *);
+libedit_private int hist_set(EditLine *, hist_fun_t, void *);
+libedit_private int hist_command(EditLine *, int, const wchar_t **);
+libedit_private int hist_enlargebuf(EditLine *, size_t, size_t);
+libedit_private wchar_t *hist_convert(EditLine *, int, void *);
#endif /* _h_el_hist */
diff --git a/lib/libedit/src/histedit.h b/lib/libedit/src/histedit.h
index 5009feec62c0..9ea71899d892 100644
--- a/lib/libedit/src/histedit.h
+++ b/lib/libedit/src/histedit.h
@@ -1,4 +1,4 @@
-/* $NetBSD: histedit.h,v 1.47 2010/08/28 15:44:59 christos Exp $ */
+/* $NetBSD: histedit.h,v 1.56 2016/04/19 19:50:53 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -43,8 +43,6 @@
#define LIBEDIT_MAJOR 2
#define LIBEDIT_MINOR 11
-#include <stdint.h>
-
#include <sys/types.h>
#include <stdio.h>
@@ -86,6 +84,8 @@ typedef struct lineinfo {
* Initialization, cleanup, and resetting
*/
EditLine *el_init(const char *, FILE *, FILE *, FILE *);
+EditLine *el_init_fd(const char *, FILE *, FILE *, FILE *,
+ int, int, int);
void el_end(EditLine *);
void el_reset(EditLine *);
@@ -141,8 +141,8 @@ unsigned char _el_fn_complete(EditLine *, int);
#define EL_ECHOTC 7 /* , const Char *, ..., NULL); set */
#define EL_SETTY 8 /* , const Char *, ..., NULL); set */
#define EL_ADDFN 9 /* , const Char *, const Char, set */
- /* el_func_t); */
-#define EL_HIST 10 /* , hist_fun_t, const ptr_t); set */
+ /* el_func_t); */
+#define EL_HIST 10 /* , hist_fun_t, const void *); set */
#define EL_EDITMODE 11 /* , int); set/get */
#define EL_RPROMPT 12 /* , prompt_func); set/get */
#define EL_GETCFN 13 /* , el_rfunc_t); set/get */
@@ -156,6 +156,7 @@ unsigned char _el_fn_complete(EditLine *, int);
#define EL_PROMPT_ESC 21 /* , prompt_func, Char); set/get */
#define EL_RPROMPT_ESC 22 /* , prompt_func, Char); set/get */
#define EL_RESIZE 23 /* , el_zfunc_t, void *); set */
+#define EL_ALIAS_TEXT 24 /* , el_afunc_t, void *); set */
#define EL_BUILTIN_GETCFN (NULL)
@@ -224,6 +225,7 @@ int history(History *, HistEvent *, int, ...);
#define H_NEXT_EVDATA 23 /* , const int, histdata_t *); */
#define H_DELDATA 24 /* , int, histdata_t *);*/
#define H_REPLACE 25 /* , const char *, histdata_t); */
+#define H_SAVE_FP 26 /* , FILE *); */
@@ -247,21 +249,10 @@ int tok_str(Tokenizer *, const char *,
/*
* Begin Wide Character Support
*/
-#ifdef __linux__
-/* Apparently we need _GNU_SOURCE defined to get access to wcsdup on Linux */
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#endif
-
#include <wchar.h>
#include <wctype.h>
/*
- * Wide character versions
- */
-
-/*
* ==== Editing ====
*/
typedef struct lineinfow {
@@ -270,6 +261,8 @@ typedef struct lineinfow {
const wchar_t *lastchar;
} LineInfoW;
+typedef int (*el_rfunc_t)(EditLine *, wchar_t *);
+
const wchar_t *el_wgets(EditLine *, int *);
int el_wgetc(EditLine *, wchar_t *);
void el_wpush(EditLine *, const wchar_t *);
@@ -279,6 +272,7 @@ int el_wparse(EditLine *, int, const wchar_t **);
int el_wset(EditLine *, int, ...);
int el_wget(EditLine *, int, ...);
+int el_cursor(EditLine *, int);
const LineInfoW *el_wline(EditLine *);
int el_winsertstr(EditLine *, const wchar_t *);
#define el_wdeletestr el_deletestr
diff --git a/lib/libedit/src/history.c b/lib/libedit/src/history.c
index 3ec16a77bc02..41d1014ca24d 100644
--- a/lib/libedit/src/history.c
+++ b/lib/libedit/src/history.c
@@ -1,4 +1,4 @@
-/* $NetBSD: history.c,v 1.38 2011/01/16 03:05:51 christos Exp $ */
+/* $NetBSD: history.c,v 1.57 2016/04/11 18:56:31 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -32,39 +32,72 @@
* SUCH DAMAGE.
*/
-#ifndef NARROWCHAR
#include "config.h"
-#endif
-
#if !defined(lint) && !defined(SCCSID)
#if 0
static char sccsid[] = "@(#)history.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: history.c,v 1.38 2011/01/16 03:05:51 christos Exp $");
+__RCSID("$NetBSD: history.c,v 1.57 2016/04/11 18:56:31 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
* hist.c: TYPE(History) access functions
*/
-#include <string.h>
-#include <stdlib.h>
+#include <sys/stat.h>
#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
#include <vis.h>
-#include <sys/stat.h>
static const char hist_cookie[] = "_HiStOrY_V2_\n";
#include "histedit.h"
+
+
+#ifdef NARROWCHAR
+
+#define Char char
+#define FUN(prefix, rest) prefix ## _ ## rest
+#define FUNW(type) type
+#define TYPE(type) type
+#define STR(x) x
+
+#define Strlen(s) strlen(s)
+#define Strdup(s) strdup(s)
+#define Strcmp(d, s) strcmp(d, s)
+#define Strncmp(d, s, n) strncmp(d, s, n)
+#define Strncpy(d, s, n) strncpy(d, s, n)
+#define Strncat(d, s, n) strncat(d, s, n)
+#define ct_decode_string(s, b) (s)
+#define ct_encode_string(s, b) (s)
+
+#else
#include "chartype.h"
-typedef int (*history_gfun_t)(ptr_t, TYPE(HistEvent) *);
-typedef int (*history_efun_t)(ptr_t, TYPE(HistEvent) *, const Char *);
-typedef void (*history_vfun_t)(ptr_t, TYPE(HistEvent) *);
-typedef int (*history_sfun_t)(ptr_t, TYPE(HistEvent) *, const int);
+#define Char wchar_t
+#define FUN(prefix, rest) prefix ## _w ## rest
+#define FUNW(type) type ## _w
+#define TYPE(type) type ## W
+#define STR(x) L ## x
+
+#define Strlen(s) wcslen(s)
+#define Strdup(s) wcsdup(s)
+#define Strcmp(d, s) wcscmp(d, s)
+#define Strncmp(d, s, n) wcsncmp(d, s, n)
+#define Strncpy(d, s, n) wcsncpy(d, s, n)
+#define Strncat(d, s, n) wcsncat(d, s, n)
+
+#endif
+
+
+typedef int (*history_gfun_t)(void *, TYPE(HistEvent) *);
+typedef int (*history_efun_t)(void *, TYPE(HistEvent) *, const Char *);
+typedef void (*history_vfun_t)(void *, TYPE(HistEvent) *);
+typedef int (*history_sfun_t)(void *, TYPE(HistEvent) *, const int);
struct TYPE(history) {
- ptr_t h_ref; /* Argument for history fcns */
+ void *h_ref; /* Argument for history fcns */
int h_ent; /* Last entry point for history */
history_gfun_t h_first; /* Get the first element */
history_gfun_t h_next; /* Get the next element */
@@ -100,18 +133,20 @@ typedef struct {
} HistEventPrivate;
-
-private int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
-private int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
-private int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
-private int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
-private int history_set_fun(TYPE(History) *, TYPE(History) *);
-private int history_load(TYPE(History) *, const char *);
-private int history_save(TYPE(History) *, const char *);
-private int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
-private int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
-private int history_next_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
-private int history_prev_string(TYPE(History) *, TYPE(HistEvent) *, const Char *);
+static int history_setsize(TYPE(History) *, TYPE(HistEvent) *, int);
+static int history_getsize(TYPE(History) *, TYPE(HistEvent) *);
+static int history_setunique(TYPE(History) *, TYPE(HistEvent) *, int);
+static int history_getunique(TYPE(History) *, TYPE(HistEvent) *);
+static int history_set_fun(TYPE(History) *, TYPE(History) *);
+static int history_load(TYPE(History) *, const char *);
+static int history_save(TYPE(History) *, const char *);
+static int history_save_fp(TYPE(History) *, FILE *);
+static int history_prev_event(TYPE(History) *, TYPE(HistEvent) *, int);
+static int history_next_event(TYPE(History) *, TYPE(HistEvent) *, int);
+static int history_next_string(TYPE(History) *, TYPE(HistEvent) *,
+ const Char *);
+static int history_prev_string(TYPE(History) *, TYPE(HistEvent) *,
+ const Char *);
/***********************************************************************/
@@ -136,23 +171,23 @@ typedef struct history_t {
#define H_UNIQUE 1 /* Store only unique elements */
} history_t;
-private int history_def_next(ptr_t, TYPE(HistEvent) *);
-private int history_def_first(ptr_t, TYPE(HistEvent) *);
-private int history_def_prev(ptr_t, TYPE(HistEvent) *);
-private int history_def_last(ptr_t, TYPE(HistEvent) *);
-private int history_def_curr(ptr_t, TYPE(HistEvent) *);
-private int history_def_set(ptr_t, TYPE(HistEvent) *, const int);
-private void history_def_clear(ptr_t, TYPE(HistEvent) *);
-private int history_def_enter(ptr_t, TYPE(HistEvent) *, const Char *);
-private int history_def_add(ptr_t, TYPE(HistEvent) *, const Char *);
-private int history_def_del(ptr_t, TYPE(HistEvent) *, const int);
+static int history_def_next(void *, TYPE(HistEvent) *);
+static int history_def_first(void *, TYPE(HistEvent) *);
+static int history_def_prev(void *, TYPE(HistEvent) *);
+static int history_def_last(void *, TYPE(HistEvent) *);
+static int history_def_curr(void *, TYPE(HistEvent) *);
+static int history_def_set(void *, TYPE(HistEvent) *, const int);
+static void history_def_clear(void *, TYPE(HistEvent) *);
+static int history_def_enter(void *, TYPE(HistEvent) *, const Char *);
+static int history_def_add(void *, TYPE(HistEvent) *, const Char *);
+static int history_def_del(void *, TYPE(HistEvent) *, const int);
-private int history_def_init(ptr_t *, TYPE(HistEvent) *, int);
-private int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
-private void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
+static int history_def_init(void **, TYPE(HistEvent) *, int);
+static int history_def_insert(history_t *, TYPE(HistEvent) *, const Char *);
+static void history_def_delete(history_t *, TYPE(HistEvent) *, hentry_t *);
-private int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
-private int history_set_nth(ptr_t, TYPE(HistEvent) *, int);
+static int history_deldata_nth(history_t *, TYPE(HistEvent) *, int, void **);
+static int history_set_nth(void *, TYPE(HistEvent) *, int);
#define history_def_setsize(p, num)(void) (((history_t *)p)->max = (num))
#define history_def_getsize(p) (((history_t *)p)->cur)
@@ -209,8 +244,8 @@ static const Char *const he_errlist[] = {
/* history_def_first():
* Default function to return the first event in the history.
*/
-private int
-history_def_first(ptr_t p, TYPE(HistEvent) *ev)
+static int
+history_def_first(void *p, TYPE(HistEvent) *ev)
{
history_t *h = (history_t *) p;
@@ -219,18 +254,18 @@ history_def_first(ptr_t p, TYPE(HistEvent) *ev)
*ev = h->cursor->ev;
else {
he_seterrev(ev, _HE_FIRST_NOTFOUND);
- return (-1);
+ return -1;
}
- return (0);
+ return 0;
}
/* history_def_last():
* Default function to return the last event in the history.
*/
-private int
-history_def_last(ptr_t p, TYPE(HistEvent) *ev)
+static int
+history_def_last(void *p, TYPE(HistEvent) *ev)
{
history_t *h = (history_t *) p;
@@ -239,69 +274,69 @@ history_def_last(ptr_t p, TYPE(HistEvent) *ev)
*ev = h->cursor->ev;
else {
he_seterrev(ev, _HE_LAST_NOTFOUND);
- return (-1);
+ return -1;
}
- return (0);
+ return 0;
}
/* history_def_next():
* Default function to return the next event in the history.
*/
-private int
-history_def_next(ptr_t p, TYPE(HistEvent) *ev)
+static int
+history_def_next(void *p, TYPE(HistEvent) *ev)
{
history_t *h = (history_t *) p;
if (h->cursor == &h->list) {
he_seterrev(ev, _HE_EMPTY_LIST);
- return (-1);
+ return -1;
}
if (h->cursor->next == &h->list) {
he_seterrev(ev, _HE_END_REACHED);
- return (-1);
+ return -1;
}
h->cursor = h->cursor->next;
*ev = h->cursor->ev;
- return (0);
+ return 0;
}
/* history_def_prev():
* Default function to return the previous event in the history.
*/
-private int
-history_def_prev(ptr_t p, TYPE(HistEvent) *ev)
+static int
+history_def_prev(void *p, TYPE(HistEvent) *ev)
{
history_t *h = (history_t *) p;
if (h->cursor == &h->list) {
he_seterrev(ev,
(h->cur > 0) ? _HE_END_REACHED : _HE_EMPTY_LIST);
- return (-1);
+ return -1;
}
if (h->cursor->prev == &h->list) {
he_seterrev(ev, _HE_START_REACHED);
- return (-1);
+ return -1;
}
h->cursor = h->cursor->prev;
*ev = h->cursor->ev;
- return (0);
+ return 0;
}
/* history_def_curr():
* Default function to return the current event in the history.
*/
-private int
-history_def_curr(ptr_t p, TYPE(HistEvent) *ev)
+static int
+history_def_curr(void *p, TYPE(HistEvent) *ev)
{
history_t *h = (history_t *) p;
@@ -310,10 +345,10 @@ history_def_curr(ptr_t p, TYPE(HistEvent) *ev)
else {
he_seterrev(ev,
(h->cur > 0) ? _HE_CURR_INVALID : _HE_EMPTY_LIST);
- return (-1);
+ return -1;
}
- return (0);
+ return 0;
}
@@ -321,14 +356,14 @@ history_def_curr(ptr_t p, TYPE(HistEvent) *ev)
* Default function to set the current event in the history to the
* given one.
*/
-private int
-history_def_set(ptr_t p, TYPE(HistEvent) *ev, const int n)
+static int
+history_def_set(void *p, TYPE(HistEvent) *ev, const int n)
{
history_t *h = (history_t *) p;
if (h->cur == 0) {
he_seterrev(ev, _HE_EMPTY_LIST);
- return (-1);
+ return -1;
}
if (h->cursor == &h->list || h->cursor->ev.num != n) {
for (h->cursor = h->list.next; h->cursor != &h->list;
@@ -338,9 +373,9 @@ history_def_set(ptr_t p, TYPE(HistEvent) *ev, const int n)
}
if (h->cursor == &h->list) {
he_seterrev(ev, _HE_NOT_FOUND);
- return (-1);
+ return -1;
}
- return (0);
+ return 0;
}
@@ -348,14 +383,14 @@ history_def_set(ptr_t p, TYPE(HistEvent) *ev, const int n)
* Default function to set the current event in the history to the
* n-th one.
*/
-private int
-history_set_nth(ptr_t p, TYPE(HistEvent) *ev, int n)
+static int
+history_set_nth(void *p, TYPE(HistEvent) *ev, int n)
{
history_t *h = (history_t *) p;
if (h->cur == 0) {
he_seterrev(ev, _HE_EMPTY_LIST);
- return (-1);
+ return -1;
}
for (h->cursor = h->list.prev; h->cursor != &h->list;
h->cursor = h->cursor->prev)
@@ -363,17 +398,17 @@ history_set_nth(ptr_t p, TYPE(HistEvent) *ev, int n)
break;
if (h->cursor == &h->list) {
he_seterrev(ev, _HE_NOT_FOUND);
- return (-1);
+ return -1;
}
- return (0);
+ return 0;
}
/* history_def_add():
* Append string to element
*/
-private int
-history_def_add(ptr_t p, TYPE(HistEvent) *ev, const Char *str)
+static int
+history_def_add(void *p, TYPE(HistEvent) *ev, const Char *str)
{
history_t *h = (history_t *) p;
size_t len;
@@ -381,38 +416,38 @@ history_def_add(ptr_t p, TYPE(HistEvent) *ev, const Char *str)
HistEventPrivate *evp = (void *)&h->cursor->ev;
if (h->cursor == &h->list)
- return (history_def_enter(p, ev, str));
+ return history_def_enter(p, ev, str);
len = Strlen(evp->str) + Strlen(str) + 1;
s = h_malloc(len * sizeof(*s));
if (s == NULL) {
he_seterrev(ev, _HE_MALLOC_FAILED);
- return (-1);
+ return -1;
}
(void) Strncpy(s, h->cursor->ev.str, len);
s[len - 1] = '\0';
(void) Strncat(s, str, len - Strlen(s) - 1);
- h_free((ptr_t)evp->str);
+ h_free(evp->str);
evp->str = s;
*ev = h->cursor->ev;
- return (0);
+ return 0;
}
-private int
+static int
history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
int num, void **data)
{
if (history_set_nth(h, ev, num) != 0)
- return (-1);
+ return -1;
/* magic value to skip delete (just set to n-th history) */
if (data == (void **)-1)
- return (0);
+ return 0;
ev->str = Strdup(h->cursor->ev.str);
ev->num = h->cursor->ev.num;
if (data)
*data = h->cursor->data;
history_def_delete(h, ev, h->cursor);
- return (0);
+ return 0;
}
@@ -420,17 +455,17 @@ history_deldata_nth(history_t *h, TYPE(HistEvent) *ev,
* Delete element hp of the h list
*/
/* ARGSUSED */
-private int
-history_def_del(ptr_t p, TYPE(HistEvent) *ev __attribute__((__unused__)),
+static int
+history_def_del(void *p, TYPE(HistEvent) *ev __attribute__((__unused__)),
const int num)
{
history_t *h = (history_t *) p;
if (history_def_set(h, ev, num) != 0)
- return (-1);
+ return -1;
ev->str = Strdup(h->cursor->ev.str);
ev->num = h->cursor->ev.num;
history_def_delete(h, ev, h->cursor);
- return (0);
+ return 0;
}
@@ -438,7 +473,7 @@ history_def_del(ptr_t p, TYPE(HistEvent) *ev __attribute__((__unused__)),
* Delete element hp of the h list
*/
/* ARGSUSED */
-private void
+static void
history_def_delete(history_t *h,
TYPE(HistEvent) *ev __attribute__((__unused__)), hentry_t *hp)
{
@@ -452,7 +487,7 @@ history_def_delete(history_t *h,
}
hp->prev->next = hp->next;
hp->next->prev = hp->prev;
- h_free((ptr_t) evp->str);
+ h_free(evp->str);
h_free(hp);
h->cur--;
}
@@ -461,47 +496,49 @@ history_def_delete(history_t *h,
/* history_def_insert():
* Insert element with string str in the h list
*/
-private int
+static int
history_def_insert(history_t *h, TYPE(HistEvent) *ev, const Char *str)
{
+ hentry_t *c;
- h->cursor = (hentry_t *) h_malloc(sizeof(hentry_t));
- if (h->cursor == NULL)
+ c = h_malloc(sizeof(*c));
+ if (c == NULL)
goto oomem;
- if ((h->cursor->ev.str = h_strdup(str)) == NULL) {
- h_free((ptr_t)h->cursor);
+ if ((c->ev.str = h_strdup(str)) == NULL) {
+ h_free(c);
goto oomem;
}
- h->cursor->data = NULL;
- h->cursor->ev.num = ++h->eventid;
- h->cursor->next = h->list.next;
- h->cursor->prev = &h->list;
- h->list.next->prev = h->cursor;
- h->list.next = h->cursor;
+ c->data = NULL;
+ c->ev.num = ++h->eventid;
+ c->next = h->list.next;
+ c->prev = &h->list;
+ h->list.next->prev = c;
+ h->list.next = c;
h->cur++;
+ h->cursor = c;
- *ev = h->cursor->ev;
- return (0);
+ *ev = c->ev;
+ return 0;
oomem:
he_seterrev(ev, _HE_MALLOC_FAILED);
- return (-1);
+ return -1;
}
/* history_def_enter():
* Default function to enter an item in the history
*/
-private int
-history_def_enter(ptr_t p, TYPE(HistEvent) *ev, const Char *str)
+static int
+history_def_enter(void *p, TYPE(HistEvent) *ev, const Char *str)
{
history_t *h = (history_t *) p;
if ((h->flags & H_UNIQUE) != 0 && h->list.next != &h->list &&
Strcmp(h->list.next->ev.str, str) == 0)
- return (0);
+ return 0;
if (history_def_insert(h, ev, str) == -1)
- return (-1); /* error, keep error message */
+ return -1; /* error, keep error message */
/*
* Always keep at least one entry.
@@ -510,7 +547,7 @@ history_def_enter(ptr_t p, TYPE(HistEvent) *ev, const Char *str)
while (h->cur > h->max && h->cur > 0)
history_def_delete(h, ev, h->list.prev);
- return (1);
+ return 1;
}
@@ -518,10 +555,10 @@ history_def_enter(ptr_t p, TYPE(HistEvent) *ev, const Char *str)
* Default history initialization function
*/
/* ARGSUSED */
-private int
-history_def_init(ptr_t *p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
+static int
+history_def_init(void **p, TYPE(HistEvent) *ev __attribute__((__unused__)), int n)
{
- history_t *h = (history_t *) h_malloc(sizeof(history_t));
+ history_t *h = (history_t *) h_malloc(sizeof(*h));
if (h == NULL)
return -1;
@@ -535,7 +572,7 @@ history_def_init(ptr_t *p, TYPE(HistEvent) *ev __attribute__((__unused__)), int
h->list.ev.num = 0;
h->cursor = &h->list;
h->flags = 0;
- *p = (ptr_t) h;
+ *p = h;
return 0;
}
@@ -543,13 +580,14 @@ history_def_init(ptr_t *p, TYPE(HistEvent) *ev __attribute__((__unused__)), int
/* history_def_clear():
* Default history cleanup function
*/
-private void
-history_def_clear(ptr_t p, TYPE(HistEvent) *ev)
+static void
+history_def_clear(void *p, TYPE(HistEvent) *ev)
{
history_t *h = (history_t *) p;
while (h->list.prev != &h->list)
history_def_delete(h, ev, h->list.prev);
+ h->cursor = &h->list;
h->eventid = 0;
h->cur = 0;
}
@@ -562,16 +600,16 @@ history_def_clear(ptr_t p, TYPE(HistEvent) *ev)
/* history_init():
* Initialization function.
*/
-public TYPE(History) *
+TYPE(History) *
FUN(history,init)(void)
{
TYPE(HistEvent) ev;
- TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(TYPE(History)));
+ TYPE(History) *h = (TYPE(History) *) h_malloc(sizeof(*h));
if (h == NULL)
return NULL;
if (history_def_init(&h->h_ref, &ev, 0) == -1) {
- h_free((ptr_t)h);
+ h_free(h);
return NULL;
}
h->h_ent = -1;
@@ -586,14 +624,14 @@ FUN(history,init)(void)
h->h_add = history_def_add;
h->h_del = history_def_del;
- return (h);
+ return h;
}
/* history_end():
* clean up history;
*/
-public void
+void
FUN(history,end)(TYPE(History) *h)
{
TYPE(HistEvent) ev;
@@ -609,77 +647,77 @@ FUN(history,end)(TYPE(History) *h)
/* history_setsize():
* Set history number of events
*/
-private int
+static int
history_setsize(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
{
if (h->h_next != history_def_next) {
he_seterrev(ev, _HE_NOT_ALLOWED);
- return (-1);
+ return -1;
}
if (num < 0) {
he_seterrev(ev, _HE_BAD_PARAM);
- return (-1);
+ return -1;
}
history_def_setsize(h->h_ref, num);
- return (0);
+ return 0;
}
/* history_getsize():
* Get number of events currently in history
*/
-private int
+static int
history_getsize(TYPE(History) *h, TYPE(HistEvent) *ev)
{
if (h->h_next != history_def_next) {
he_seterrev(ev, _HE_NOT_ALLOWED);
- return (-1);
+ return -1;
}
ev->num = history_def_getsize(h->h_ref);
if (ev->num < -1) {
he_seterrev(ev, _HE_SIZE_NEGATIVE);
- return (-1);
+ return -1;
}
- return (0);
+ return 0;
}
/* history_setunique():
* Set if adjacent equal events should not be entered in history.
*/
-private int
+static int
history_setunique(TYPE(History) *h, TYPE(HistEvent) *ev, int uni)
{
if (h->h_next != history_def_next) {
he_seterrev(ev, _HE_NOT_ALLOWED);
- return (-1);
+ return -1;
}
history_def_setunique(h->h_ref, uni);
- return (0);
+ return 0;
}
/* history_getunique():
* Get if adjacent equal events should not be entered in history.
*/
-private int
+static int
history_getunique(TYPE(History) *h, TYPE(HistEvent) *ev)
{
if (h->h_next != history_def_next) {
he_seterrev(ev, _HE_NOT_ALLOWED);
- return (-1);
+ return -1;
}
ev->num = history_def_getunique(h->h_ref);
- return (0);
+ return 0;
}
/* history_set_fun():
* Set history functions
*/
-private int
+static int
history_set_fun(TYPE(History) *h, TYPE(History) *nh)
{
TYPE(HistEvent) ev;
@@ -689,7 +727,8 @@ history_set_fun(TYPE(History) *h, TYPE(History) *nh)
nh->h_enter == NULL || nh->h_add == NULL || nh->h_clear == NULL ||
nh->h_del == NULL || nh->h_ref == NULL) {
if (h->h_next != history_def_next) {
- history_def_init(&h->h_ref, &ev, 0);
+ if (history_def_init(&h->h_ref, &ev, 0) == -1)
+ return -1;
h->h_first = history_def_first;
h->h_next = history_def_next;
h->h_last = history_def_last;
@@ -701,7 +740,7 @@ history_set_fun(TYPE(History) *h, TYPE(History) *nh)
h->h_add = history_def_add;
h->h_del = history_def_del;
}
- return (-1);
+ return -1;
}
if (h->h_next == history_def_next)
history_def_clear(h->h_ref, &ev);
@@ -718,50 +757,49 @@ history_set_fun(TYPE(History) *h, TYPE(History) *nh)
h->h_add = nh->h_add;
h->h_del = nh->h_del;
- return (0);
+ return 0;
}
/* history_load():
* TYPE(History) load function
*/
-private int
+static int
history_load(TYPE(History) *h, const char *fname)
{
FILE *fp;
char *line;
- size_t sz, max_size;
+ size_t llen;
+ ssize_t sz;
+ size_t max_size;
char *ptr;
int i = -1;
TYPE(HistEvent) ev;
-#ifdef WIDECHAR
+#ifndef NARROWCHAR
static ct_buffer_t conv;
#endif
if ((fp = fopen(fname, "r")) == NULL)
- return (i);
+ return i;
- if ((line = fgetln(fp, &sz)) == NULL)
+ line = NULL;
+ llen = 0;
+ if ((sz = getline(&line, &llen, fp)) == -1)
goto done;
- if (strncmp(line, hist_cookie, sz) != 0)
+ if (strncmp(line, hist_cookie, (size_t)sz) != 0)
goto done;
- ptr = h_malloc(max_size = 1024);
+ ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
if (ptr == NULL)
goto done;
- for (i = 0; (line = fgetln(fp, &sz)) != NULL; i++) {
- char c = line[sz];
-
- if (sz != 0 && line[sz - 1] == '\n')
+ for (i = 0; (sz = getline(&line, &llen, fp)) != -1; i++) {
+ if (sz > 0 && line[sz - 1] == '\n')
line[--sz] = '\0';
- else
- line[sz] = '\0';
-
- if (max_size < sz) {
+ if (max_size < (size_t)sz) {
char *nptr;
- max_size = (sz + 1024) & ~1023;
- nptr = h_realloc(ptr, max_size);
+ max_size = ((size_t)sz + 1024) & (size_t)~1023;
+ nptr = h_realloc(ptr, max_size * sizeof(*ptr));
if (nptr == NULL) {
i = -1;
goto oomem;
@@ -769,88 +807,104 @@ history_load(TYPE(History) *h, const char *fname)
ptr = nptr;
}
(void) strunvis(ptr, line);
- line[sz] = c;
if (HENTER(h, &ev, ct_decode_string(ptr, &conv)) == -1) {
i = -1;
goto oomem;
}
}
oomem:
- h_free((ptr_t)ptr);
+ h_free(ptr);
done:
+ free(line);
(void) fclose(fp);
- return (i);
+ return i;
}
-/* history_save():
+/* history_save_fp():
* TYPE(History) save function
*/
-private int
-history_save(TYPE(History) *h, const char *fname)
+static int
+history_save_fp(TYPE(History) *h, FILE *fp)
{
- FILE *fp;
TYPE(HistEvent) ev;
int i = -1, retval;
size_t len, max_size;
char *ptr;
-#ifdef WIDECHAR
+ const char *str;
+#ifndef NARROWCHAR
static ct_buffer_t conv;
#endif
- if ((fp = fopen(fname, "w")) == NULL)
- return (-1);
-
if (fchmod(fileno(fp), S_IRUSR|S_IWUSR) == -1)
goto done;
if (fputs(hist_cookie, fp) == EOF)
goto done;
- ptr = h_malloc(max_size = 1024);
+ ptr = h_malloc((max_size = 1024) * sizeof(*ptr));
if (ptr == NULL)
goto done;
for (i = 0, retval = HLAST(h, &ev);
retval != -1;
retval = HPREV(h, &ev), i++) {
- len = Strlen(ev.str) * 4;
- if (len >= max_size) {
+ str = ct_encode_string(ev.str, &conv);
+ len = strlen(str) * 4 + 1;
+ if (len > max_size) {
char *nptr;
- max_size = (len + 1024) & ~1023;
- nptr = h_realloc(ptr, max_size);
+ max_size = (len + 1024) & (size_t)~1023;
+ nptr = h_realloc(ptr, max_size * sizeof(*ptr));
if (nptr == NULL) {
i = -1;
goto oomem;
}
ptr = nptr;
}
- (void) strvis(ptr, ct_encode_string(ev.str, &conv), VIS_WHITE);
+ (void) strvis(ptr, str, VIS_WHITE);
(void) fprintf(fp, "%s\n", ptr);
}
oomem:
- h_free((ptr_t)ptr);
+ h_free(ptr);
done:
- (void) fclose(fp);
- return (i);
+ return i;
+}
+
+
+/* history_save():
+ * History save function
+ */
+static int
+history_save(TYPE(History) *h, const char *fname)
+{
+ FILE *fp;
+ int i;
+
+ if ((fp = fopen(fname, "w")) == NULL)
+ return -1;
+
+ i = history_save_fp(h, fp);
+
+ (void) fclose(fp);
+ return i;
}
/* history_prev_event():
* Find the previous event, with number given
*/
-private int
+static int
history_prev_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
{
int retval;
for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
if (ev->num == num)
- return (0);
+ return 0;
he_seterrev(ev, _HE_NOT_FOUND);
- return (-1);
+ return -1;
}
-private int
+static int
history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
{
int retval;
@@ -859,35 +913,35 @@ history_next_evdata(TYPE(History) *h, TYPE(HistEvent) *ev, int num, void **d)
if (ev->num == num) {
if (d)
*d = ((history_t *)h->h_ref)->cursor->data;
- return (0);
+ return 0;
}
he_seterrev(ev, _HE_NOT_FOUND);
- return (-1);
+ return -1;
}
/* history_next_event():
* Find the next event, with number given
*/
-private int
+static int
history_next_event(TYPE(History) *h, TYPE(HistEvent) *ev, int num)
{
int retval;
for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
if (ev->num == num)
- return (0);
+ return 0;
he_seterrev(ev, _HE_NOT_FOUND);
- return (-1);
+ return -1;
}
/* history_prev_string():
* Find the previous event beginning with string
*/
-private int
+static int
history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
{
size_t len = Strlen(str);
@@ -895,17 +949,17 @@ history_prev_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
for (retval = HCURR(h, ev); retval != -1; retval = HNEXT(h, ev))
if (Strncmp(str, ev->str, len) == 0)
- return (0);
+ return 0;
he_seterrev(ev, _HE_NOT_FOUND);
- return (-1);
+ return -1;
}
/* history_next_string():
* Find the next event beginning with string
*/
-private int
+static int
history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
{
size_t len = Strlen(str);
@@ -913,10 +967,10 @@ history_next_string(TYPE(History) *h, TYPE(HistEvent) *ev, const Char *str)
for (retval = HCURR(h, ev); retval != -1; retval = HPREV(h, ev))
if (Strncmp(str, ev->str, len) == 0)
- return (0);
+ return 0;
he_seterrev(ev, _HE_NOT_FOUND);
- return (-1);
+ return -1;
}
@@ -1013,6 +1067,12 @@ FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
he_seterrev(ev, _HE_HIST_WRITE);
break;
+ case H_SAVE_FP:
+ retval = history_save_fp(h, va_arg(va, FILE *));
+ if (retval == -1)
+ he_seterrev(ev, _HE_HIST_WRITE);
+ break;
+
case H_PREV_EVENT:
retval = history_prev_event(h, ev, va_arg(va, int));
break;
@@ -1033,7 +1093,7 @@ FUNW(history)(TYPE(History) *h, TYPE(HistEvent) *ev, int fun, ...)
{
TYPE(History) hf;
- hf.h_ref = va_arg(va, ptr_t);
+ hf.h_ref = va_arg(va, void *);
h->h_ent = -1;
hf.h_first = va_arg(va, history_gfun_t);
hf.h_next = va_arg(va, history_gfun_t);
diff --git a/lib/libedit/src/historyn.c b/lib/libedit/src/historyn.c
new file mode 100644
index 000000000000..59130dea3451
--- /dev/null
+++ b/lib/libedit/src/historyn.c
@@ -0,0 +1,3 @@
+#include "config.h"
+#define NARROWCHAR
+#include "history.c"
diff --git a/lib/libedit/src/key.c b/lib/libedit/src/keymacro.c
index daf679fd5bb0..13d208930e6b 100644
--- a/lib/libedit/src/key.c
+++ b/lib/libedit/src/keymacro.c
@@ -1,4 +1,4 @@
-/* $NetBSD: key.c,v 1.23 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: keymacro.c,v 1.23 2016/05/24 15:00:45 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,286 +37,289 @@
#if 0
static char sccsid[] = "@(#)key.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: key.c,v 1.23 2009/12/30 22:37:40 christos Exp $");
+__RCSID("$NetBSD: keymacro.c,v 1.23 2016/05/24 15:00:45 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
- * key.c: This module contains the procedures for maintaining
- * the extended-key map.
+ * keymacro.c: This module contains the procedures for maintaining
+ * the extended-key map.
*
* An extended-key (key) is a sequence of keystrokes introduced
* with a sequence introducer and consisting of an arbitrary
- * number of characters. This module maintains a map (the el->el_key.map)
+ * number of characters. This module maintains a map (the
+ * el->el_keymacro.map)
* to convert these extended-key sequences into input strs
- * (XK_STR), editor functions (XK_CMD), or unix commands (XK_EXE).
+ * (XK_STR) or editor functions (XK_CMD).
*
* Warning:
* If key is a substr of some other keys, then the longer
* keys are lost!! That is, if the keys "abcd" and "abcef"
- * are in el->el_key.map, adding the key "abc" will cause the first two
- * definitions to be lost.
+ * are in el->el_keymacro.map, adding the key "abc" will cause
+ * the first two definitions to be lost.
*
* Restrictions:
* -------------
* 1) It is not possible to have one key that is a
* substr of another.
*/
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
#include "el.h"
+#include "fcns.h"
/*
- * The Nodes of the el->el_key.map. The el->el_key.map is a linked list
- * of these node elements
+ * The Nodes of the el->el_keymacro.map. The el->el_keymacro.map is a
+ * linked list of these node elements
*/
-struct key_node_t {
- Char ch; /* single character of key */
- int type; /* node type */
- key_value_t val; /* command code or pointer to str, */
- /* if this is a leaf */
- struct key_node_t *next; /* ptr to next char of this key */
- struct key_node_t *sibling; /* ptr to another key with same prefix*/
+struct keymacro_node_t {
+ wchar_t ch; /* single character of key */
+ int type; /* node type */
+ keymacro_value_t val; /* command code or pointer to str, */
+ /* if this is a leaf */
+ struct keymacro_node_t *next; /* ptr to next char of this key */
+ struct keymacro_node_t *sibling;/* ptr to another key with same prefix*/
};
-private int node_trav(EditLine *, key_node_t *, Char *,
- key_value_t *);
-private int node__try(EditLine *, key_node_t *, const Char *,
- key_value_t *, int);
-private key_node_t *node__get(Int);
-private void node__free(key_node_t *);
-private void node__put(EditLine *, key_node_t *);
-private int node__delete(EditLine *, key_node_t **, const Char *);
-private int node_lookup(EditLine *, const Char *, key_node_t *,
- size_t);
-private int node_enum(EditLine *, key_node_t *, size_t);
+static int node_trav(EditLine *, keymacro_node_t *, wchar_t *,
+ keymacro_value_t *);
+static int node__try(EditLine *, keymacro_node_t *,
+ const wchar_t *, keymacro_value_t *, int);
+static keymacro_node_t *node__get(wint_t);
+static void node__free(keymacro_node_t *);
+static void node__put(EditLine *, keymacro_node_t *);
+static int node__delete(EditLine *, keymacro_node_t **,
+ const wchar_t *);
+static int node_lookup(EditLine *, const wchar_t *,
+ keymacro_node_t *, size_t);
+static int node_enum(EditLine *, keymacro_node_t *, size_t);
#define KEY_BUFSIZ EL_BUFSIZ
-/* key_init():
+/* keymacro_init():
* Initialize the key maps
*/
-protected int
-key_init(EditLine *el)
+libedit_private int
+keymacro_init(EditLine *el)
{
- el->el_key.buf = el_malloc(KEY_BUFSIZ * sizeof(*el->el_key.buf));
- if (el->el_key.buf == NULL)
- return (-1);
- el->el_key.map = NULL;
- key_reset(el);
- return (0);
+ el->el_keymacro.buf = el_malloc(KEY_BUFSIZ *
+ sizeof(*el->el_keymacro.buf));
+ if (el->el_keymacro.buf == NULL)
+ return -1;
+ el->el_keymacro.map = NULL;
+ keymacro_reset(el);
+ return 0;
}
-/* key_end():
+/* keymacro_end():
* Free the key maps
*/
-protected void
-key_end(EditLine *el)
+libedit_private void
+keymacro_end(EditLine *el)
{
- el_free((ptr_t) el->el_key.buf);
- el->el_key.buf = NULL;
- node__free(el->el_key.map);
+ el_free(el->el_keymacro.buf);
+ el->el_keymacro.buf = NULL;
+ node__free(el->el_keymacro.map);
}
-/* key_map_cmd():
+/* keymacro_map_cmd():
* Associate cmd with a key value
*/
-protected key_value_t *
-key_map_cmd(EditLine *el, int cmd)
+libedit_private keymacro_value_t *
+keymacro_map_cmd(EditLine *el, int cmd)
{
- el->el_key.val.cmd = (el_action_t) cmd;
- return (&el->el_key.val);
+ el->el_keymacro.val.cmd = (el_action_t) cmd;
+ return &el->el_keymacro.val;
}
-/* key_map_str():
+/* keymacro_map_str():
* Associate str with a key value
*/
-protected key_value_t *
-key_map_str(EditLine *el, Char *str)
+libedit_private keymacro_value_t *
+keymacro_map_str(EditLine *el, wchar_t *str)
{
- el->el_key.val.str = str;
- return (&el->el_key.val);
+ el->el_keymacro.val.str = str;
+ return &el->el_keymacro.val;
}
-/* key_reset():
- * Takes all nodes on el->el_key.map and puts them on free list. Then
- * initializes el->el_key.map with arrow keys
+/* keymacro_reset():
+ * Takes all nodes on el->el_keymacro.map and puts them on free list.
+ * Then initializes el->el_keymacro.map with arrow keys
* [Always bind the ansi arrow keys?]
*/
-protected void
-key_reset(EditLine *el)
+libedit_private void
+keymacro_reset(EditLine *el)
{
- node__put(el, el->el_key.map);
- el->el_key.map = NULL;
+ node__put(el, el->el_keymacro.map);
+ el->el_keymacro.map = NULL;
return;
}
-/* key_get():
- * Calls the recursive function with entry point el->el_key.map
+/* keymacro_get():
+ * Calls the recursive function with entry point el->el_keymacro.map
* Looks up *ch in map and then reads characters until a
* complete match is found or a mismatch occurs. Returns the
- * type of the match found (XK_STR, XK_CMD, or XK_EXE).
+ * type of the match found (XK_STR or XK_CMD).
* Returns NULL in val.str and XK_STR for no match.
+ * Returns XK_NOD for end of file or read error.
* The last character read is returned in *ch.
*/
-protected int
-key_get(EditLine *el, Char *ch, key_value_t *val)
+libedit_private int
+keymacro_get(EditLine *el, wchar_t *ch, keymacro_value_t *val)
{
- return (node_trav(el, el->el_key.map, ch, val));
+ return node_trav(el, el->el_keymacro.map, ch, val);
}
-/* key_add():
- * Adds key to the el->el_key.map and associates the value in val with it.
- * If key is already is in el->el_key.map, the new code is applied to the
- * existing key. Ntype specifies if code is a command, an
- * out str or a unix command.
+/* keymacro_add():
+ * Adds key to the el->el_keymacro.map and associates the value in
+ * val with it. If key is already is in el->el_keymacro.map, the new
+ * code is applied to the existing key. Ntype specifies if code is a
+ * command, an out str or a unix command.
*/
-protected void
-key_add(EditLine *el, const Char *key, key_value_t *val, int ntype)
+libedit_private void
+keymacro_add(EditLine *el, const wchar_t *key, keymacro_value_t *val,
+ int ntype)
{
if (key[0] == '\0') {
(void) fprintf(el->el_errfile,
- "key_add: Null extended-key not allowed.\n");
+ "keymacro_add: Null extended-key not allowed.\n");
return;
}
if (ntype == XK_CMD && val->cmd == ED_SEQUENCE_LEAD_IN) {
(void) fprintf(el->el_errfile,
- "key_add: sequence-lead-in command not allowed\n");
+ "keymacro_add: sequence-lead-in command not allowed\n");
return;
}
- if (el->el_key.map == NULL)
+ if (el->el_keymacro.map == NULL)
/* tree is initially empty. Set up new node to match key[0] */
- el->el_key.map = node__get(key[0]);
+ el->el_keymacro.map = node__get(key[0]);
/* it is properly initialized */
- /* Now recurse through el->el_key.map */
- (void) node__try(el, el->el_key.map, key, val, ntype);
+ /* Now recurse through el->el_keymacro.map */
+ (void) node__try(el, el->el_keymacro.map, key, val, ntype);
return;
}
-/* key_clear():
+/* keymacro_clear():
*
*/
-protected void
-key_clear(EditLine *el, el_action_t *map, const Char *in)
+libedit_private void
+keymacro_clear(EditLine *el, el_action_t *map, const wchar_t *in)
{
-#ifdef WIDECHAR
if (*in > N_KEYS) /* can't be in the map */
return;
-#endif
if ((map[(unsigned char)*in] == ED_SEQUENCE_LEAD_IN) &&
((map == el->el_map.key &&
el->el_map.alt[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN) ||
(map == el->el_map.alt &&
el->el_map.key[(unsigned char)*in] != ED_SEQUENCE_LEAD_IN)))
- (void) key_delete(el, in);
+ (void) keymacro_delete(el, in);
}
-/* key_delete():
+/* keymacro_delete():
* Delete the key and all longer keys staring with key, if
* they exists.
*/
-protected int
-key_delete(EditLine *el, const Char *key)
+libedit_private int
+keymacro_delete(EditLine *el, const wchar_t *key)
{
if (key[0] == '\0') {
(void) fprintf(el->el_errfile,
- "key_delete: Null extended-key not allowed.\n");
- return (-1);
+ "keymacro_delete: Null extended-key not allowed.\n");
+ return -1;
}
- if (el->el_key.map == NULL)
- return (0);
+ if (el->el_keymacro.map == NULL)
+ return 0;
- (void) node__delete(el, &el->el_key.map, key);
- return (0);
+ (void) node__delete(el, &el->el_keymacro.map, key);
+ return 0;
}
-/* key_print():
+/* keymacro_print():
* Print the binding associated with key key.
- * Print entire el->el_key.map if null
+ * Print entire el->el_keymacro.map if null
*/
-protected void
-key_print(EditLine *el, const Char *key)
+libedit_private void
+keymacro_print(EditLine *el, const wchar_t *key)
{
- /* do nothing if el->el_key.map is empty and null key specified */
- if (el->el_key.map == NULL && *key == 0)
+ /* do nothing if el->el_keymacro.map is empty and null key specified */
+ if (el->el_keymacro.map == NULL && *key == 0)
return;
- el->el_key.buf[0] = '"';
- if (node_lookup(el, key, el->el_key.map, 1) <= -1)
+ el->el_keymacro.buf[0] = '"';
+ if (node_lookup(el, key, el->el_keymacro.map, (size_t)1) <= -1)
/* key is not bound */
- (void) fprintf(el->el_errfile, "Unbound extended key \"" FSTR "\"\n",
- key);
+ (void) fprintf(el->el_errfile, "Unbound extended key \"%ls"
+ "\"\n", key);
return;
}
/* node_trav():
* recursively traverses node in tree until match or mismatch is
- * found. May read in more characters.
+ * found. May read in more characters.
*/
-private int
-node_trav(EditLine *el, key_node_t *ptr, Char *ch, key_value_t *val)
+static int
+node_trav(EditLine *el, keymacro_node_t *ptr, wchar_t *ch,
+ keymacro_value_t *val)
{
if (ptr->ch == *ch) {
/* match found */
if (ptr->next) {
/* key not complete so get next char */
- if (FUN(el,getc)(el, ch) != 1) {/* if EOF or error */
- val->cmd = ED_END_OF_FILE;
- return (XK_CMD);
- /* PWP: Pretend we just read an end-of-file */
- }
- return (node_trav(el, ptr->next, ch, val));
+ if (el_wgetc(el, ch) != 1)
+ return XK_NOD;
+ return node_trav(el, ptr->next, ch, val);
} else {
*val = ptr->val;
if (ptr->type != XK_CMD)
*ch = '\0';
- return (ptr->type);
+ return ptr->type;
}
} else {
/* no match found here */
if (ptr->sibling) {
/* try next sibling */
- return (node_trav(el, ptr->sibling, ch, val));
+ return node_trav(el, ptr->sibling, ch, val);
} else {
/* no next sibling -- mismatch */
val->str = NULL;
- return (XK_STR);
+ return XK_STR;
}
}
}
/* node__try():
- * Find a node that matches *str or allocate a new one
+ * Find a node that matches *str or allocate a new one
*/
-private int
-node__try(EditLine *el, key_node_t *ptr, const Char *str, key_value_t *val, int ntype)
+static int
+node__try(EditLine *el, keymacro_node_t *ptr, const wchar_t *str,
+ keymacro_value_t *val, int ntype)
{
if (ptr->ch != *str) {
- key_node_t *xm;
+ keymacro_node_t *xm;
for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
if (xm->sibling->ch == *str)
@@ -337,9 +340,8 @@ node__try(EditLine *el, key_node_t *ptr, const Char *str, key_value_t *val, int
case XK_NOD:
break;
case XK_STR:
- case XK_EXE:
if (ptr->val.str)
- el_free((ptr_t) ptr->val.str);
+ el_free(ptr->val.str);
break;
default:
EL_ABORT((el->el_errfile, "Bad XK_ type %d\n",
@@ -352,8 +354,7 @@ node__try(EditLine *el, key_node_t *ptr, const Char *str, key_value_t *val, int
ptr->val = *val;
break;
case XK_STR:
- case XK_EXE:
- if ((ptr->val.str = Strdup(val->str)) == NULL)
+ if ((ptr->val.str = wcsdup(val->str)) == NULL)
return -1;
break;
default:
@@ -366,29 +367,29 @@ node__try(EditLine *el, key_node_t *ptr, const Char *str, key_value_t *val, int
ptr->next = node__get(*str); /* setup new node */
(void) node__try(el, ptr->next, str, val, ntype);
}
- return (0);
+ return 0;
}
/* node__delete():
* Delete node that matches str
*/
-private int
-node__delete(EditLine *el, key_node_t **inptr, const Char *str)
+static int
+node__delete(EditLine *el, keymacro_node_t **inptr, const wchar_t *str)
{
- key_node_t *ptr;
- key_node_t *prev_ptr = NULL;
+ keymacro_node_t *ptr;
+ keymacro_node_t *prev_ptr = NULL;
ptr = *inptr;
if (ptr->ch != *str) {
- key_node_t *xm;
+ keymacro_node_t *xm;
for (xm = ptr; xm->sibling != NULL; xm = xm->sibling)
if (xm->sibling->ch == *str)
break;
if (xm->sibling == NULL)
- return (0);
+ return 0;
prev_ptr = xm;
ptr = xm->sibling;
}
@@ -400,20 +401,20 @@ node__delete(EditLine *el, key_node_t **inptr, const Char *str)
prev_ptr->sibling = ptr->sibling;
ptr->sibling = NULL;
node__put(el, ptr);
- return (1);
+ return 1;
} else if (ptr->next != NULL &&
node__delete(el, &ptr->next, str) == 1) {
if (ptr->next != NULL)
- return (0);
+ return 0;
if (prev_ptr == NULL)
*inptr = ptr->sibling;
else
prev_ptr->sibling = ptr->sibling;
ptr->sibling = NULL;
node__put(el, ptr);
- return (1);
+ return 1;
} else {
- return (0);
+ return 0;
}
}
@@ -421,8 +422,8 @@ node__delete(EditLine *el, key_node_t **inptr, const Char *str)
/* node__put():
* Puts a tree of nodes onto free list using free(3).
*/
-private void
-node__put(EditLine *el, key_node_t *ptr)
+static void
+node__put(EditLine *el, keymacro_node_t *ptr)
{
if (ptr == NULL)
return;
@@ -437,28 +438,27 @@ node__put(EditLine *el, key_node_t *ptr)
case XK_CMD:
case XK_NOD:
break;
- case XK_EXE:
case XK_STR:
if (ptr->val.str != NULL)
- el_free((ptr_t) ptr->val.str);
+ el_free(ptr->val.str);
break;
default:
EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ptr->type));
break;
}
- el_free((ptr_t) ptr);
+ el_free(ptr);
}
/* node__get():
- * Returns pointer to a key_node_t for ch.
+ * Returns pointer to a keymacro_node_t for ch.
*/
-private key_node_t *
-node__get(Int ch)
+static keymacro_node_t *
+node__get(wint_t ch)
{
- key_node_t *ptr;
+ keymacro_node_t *ptr;
- ptr = (key_node_t *) el_malloc((size_t) sizeof(key_node_t));
+ ptr = el_malloc(sizeof(*ptr));
if (ptr == NULL)
return NULL;
ptr->ch = ch;
@@ -466,57 +466,59 @@ node__get(Int ch)
ptr->val.str = NULL;
ptr->next = NULL;
ptr->sibling = NULL;
- return (ptr);
+ return ptr;
}
-private void
-node__free(key_node_t *k)
+static void
+node__free(keymacro_node_t *k)
{
if (k == NULL)
return;
node__free(k->sibling);
node__free(k->next);
- el_free((ptr_t) k);
+ el_free(k);
}
/* node_lookup():
* look for the str starting at node ptr.
* Print if last node
*/
-private int
-node_lookup(EditLine *el, const Char *str, key_node_t *ptr, size_t cnt)
+static int
+node_lookup(EditLine *el, const wchar_t *str, keymacro_node_t *ptr,
+ size_t cnt)
{
ssize_t used;
if (ptr == NULL)
- return (-1); /* cannot have null ptr */
+ return -1; /* cannot have null ptr */
if (!str || *str == 0) {
/* no more chars in str. node_enum from here. */
(void) node_enum(el, ptr, cnt);
- return (0);
+ return 0;
} else {
- /* If match put this char into el->el_key.buf. Recurse */
+ /* If match put this char into el->el_keymacro.buf. Recurse */
if (ptr->ch == *str) {
/* match found */
- used = ct_visual_char(el->el_key.buf + cnt,
+ used = ct_visual_char(el->el_keymacro.buf + cnt,
KEY_BUFSIZ - cnt, ptr->ch);
if (used == -1)
- return (-1); /* ran out of buffer space */
+ return -1; /* ran out of buffer space */
if (ptr->next != NULL)
/* not yet at leaf */
return (node_lookup(el, str + 1, ptr->next,
- used + cnt));
+ (size_t)used + cnt));
else {
/* next node is null so key should be complete */
if (str[1] == 0) {
- el->el_key.buf[cnt + used ] = '"';
- el->el_key.buf[cnt + used + 1] = '\0';
- key_kprint(el, el->el_key.buf,
+ size_t px = cnt + (size_t)used;
+ el->el_keymacro.buf[px] = '"';
+ el->el_keymacro.buf[px + 1] = '\0';
+ keymacro_kprint(el, el->el_keymacro.buf,
&ptr->val, ptr->type);
- return (0);
+ return 0;
} else
- return (-1);
+ return -1;
/* mismatch -- str still has chars */
}
} else {
@@ -525,7 +527,7 @@ node_lookup(EditLine *el, const Char *str, key_node_t *ptr, size_t cnt)
return (node_lookup(el, str, ptr->sibling,
cnt));
else
- return (-1);
+ return -1;
}
}
}
@@ -534,49 +536,52 @@ node_lookup(EditLine *el, const Char *str, key_node_t *ptr, size_t cnt)
/* node_enum():
* Traverse the node printing the characters it is bound in buffer
*/
-private int
-node_enum(EditLine *el, key_node_t *ptr, size_t cnt)
+static int
+node_enum(EditLine *el, keymacro_node_t *ptr, size_t cnt)
{
ssize_t used;
if (cnt >= KEY_BUFSIZ - 5) { /* buffer too small */
- el->el_key.buf[++cnt] = '"';
- el->el_key.buf[++cnt] = '\0';
+ el->el_keymacro.buf[++cnt] = '"';
+ el->el_keymacro.buf[++cnt] = '\0';
(void) fprintf(el->el_errfile,
"Some extended keys too long for internal print buffer");
- (void) fprintf(el->el_errfile, " \"" FSTR "...\"\n", el->el_key.buf);
- return (0);
+ (void) fprintf(el->el_errfile, " \"%ls...\"\n",
+ el->el_keymacro.buf);
+ return 0;
}
if (ptr == NULL) {
#ifdef DEBUG_EDIT
(void) fprintf(el->el_errfile,
"node_enum: BUG!! Null ptr passed\n!");
#endif
- return (-1);
+ return -1;
}
/* put this char at end of str */
- used = ct_visual_char(el->el_key.buf + cnt, KEY_BUFSIZ - cnt, ptr->ch);
+ used = ct_visual_char(el->el_keymacro.buf + cnt, KEY_BUFSIZ - cnt,
+ ptr->ch);
if (ptr->next == NULL) {
/* print this key and function */
- el->el_key.buf[cnt + used ] = '"';
- el->el_key.buf[cnt + used + 1] = '\0';
- key_kprint(el, el->el_key.buf, &ptr->val, ptr->type);
+ el->el_keymacro.buf[cnt + (size_t)used ] = '"';
+ el->el_keymacro.buf[cnt + (size_t)used + 1] = '\0';
+ keymacro_kprint(el, el->el_keymacro.buf, &ptr->val, ptr->type);
} else
- (void) node_enum(el, ptr->next, cnt + used);
+ (void) node_enum(el, ptr->next, cnt + (size_t)used);
/* go to sibling if there is one */
if (ptr->sibling)
(void) node_enum(el, ptr->sibling, cnt);
- return (0);
+ return 0;
}
-/* key_kprint():
+/* keymacro_kprint():
* Print the specified key and its associated
* function specified by val
*/
-protected void
-key_kprint(EditLine *el, const Char *key, key_value_t *val, int ntype)
+libedit_private void
+keymacro_kprint(EditLine *el, const wchar_t *key, keymacro_value_t *val,
+ int ntype)
{
el_bindings_t *fp;
char unparsbuf[EL_BUFSIZ];
@@ -585,8 +590,7 @@ key_kprint(EditLine *el, const Char *key, key_value_t *val, int ntype)
if (val != NULL)
switch (ntype) {
case XK_STR:
- case XK_EXE:
- (void) key__decode_str(val->str, unparsbuf,
+ (void) keymacro__decode_str(val->str, unparsbuf,
sizeof(unparsbuf),
ntype == XK_STR ? "\"\"" : "[]");
(void) fprintf(el->el_outfile, fmt,
@@ -595,7 +599,7 @@ key_kprint(EditLine *el, const Char *key, key_value_t *val, int ntype)
case XK_CMD:
for (fp = el->el_map.help; fp->name; fp++)
if (val->cmd == fp->func) {
- ct_wcstombs(unparsbuf, fp->name, sizeof(unparsbuf));
+ wcstombs(unparsbuf, fp->name, sizeof(unparsbuf));
unparsbuf[sizeof(unparsbuf) -1] = '\0';
(void) fprintf(el->el_outfile, fmt,
ct_encode_string(key, &el->el_scratch), unparsbuf);
@@ -623,14 +627,15 @@ key_kprint(EditLine *el, const Char *key, key_value_t *val, int ntype)
*b++ = c; \
else \
b++
-/* key__decode_str():
+/* keymacro__decode_str():
* Make a printable version of the ey
*/
-protected size_t
-key__decode_str(const Char *str, char *buf, size_t len, const char *sep)
+libedit_private size_t
+keymacro__decode_str(const wchar_t *str, char *buf, size_t len,
+ const char *sep)
{
char *b = buf, *eb = b + len;
- const Char *p;
+ const wchar_t *p;
b = buf;
if (sep[0] != '\0') {
@@ -642,8 +647,8 @@ key__decode_str(const Char *str, char *buf, size_t len, const char *sep)
goto add_endsep;
}
for (p = str; *p != 0; p++) {
- Char dbuf[VISUAL_WIDTH_MAX];
- Char *p2 = dbuf;
+ wchar_t dbuf[VISUAL_WIDTH_MAX];
+ wchar_t *p2 = dbuf;
ssize_t l = ct_visual_char(dbuf, VISUAL_WIDTH_MAX, *p);
while (l-- > 0) {
ssize_t n = ct_encode_char(b, (size_t)(eb - b), *p2++);
@@ -662,4 +667,3 @@ add_endsep:
buf[len - 1] = '\0';
return (size_t)(b - buf);
}
-
diff --git a/lib/libedit/src/key.h b/lib/libedit/src/keymacro.h
index 8f067a0647c8..0653bbe3f287 100644
--- a/lib/libedit/src/key.h
+++ b/lib/libedit/src/keymacro.h
@@ -1,4 +1,4 @@
-/* $NetBSD: key.h,v 1.13 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: keymacro.h,v 1.6 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -35,46 +35,42 @@
*/
/*
- * el.key.h: Key macro header
+ * el.keymacro.h: Key macro header
*/
-#ifndef _h_el_key
-#define _h_el_key
+#ifndef _h_el_keymacro
+#define _h_el_keymacro
-typedef union key_value_t {
+typedef union keymacro_value_t {
el_action_t cmd; /* If it is a command the # */
- Char *str; /* If it is a string... */
-} key_value_t;
+ wchar_t *str; /* If it is a string... */
+} keymacro_value_t;
-typedef struct key_node_t key_node_t;
+typedef struct keymacro_node_t keymacro_node_t;
-typedef struct el_key_t {
- Char *buf; /* Key print buffer */
- key_node_t *map; /* Key map */
- key_value_t val; /* Local conversion buffer */
-} el_key_t;
+typedef struct el_keymacro_t {
+ wchar_t *buf; /* Key print buffer */
+ keymacro_node_t *map; /* Key map */
+ keymacro_value_t val; /* Local conversion buffer */
+} el_keymacro_t;
#define XK_CMD 0
#define XK_STR 1
#define XK_NOD 2
-#define XK_EXE 3
-#undef key_end
-#undef key_clear
-#undef key_print
-
-protected int key_init(EditLine *);
-protected void key_end(EditLine *);
-protected key_value_t *key_map_cmd(EditLine *, int);
-protected key_value_t *key_map_str(EditLine *, Char *);
-protected void key_reset(EditLine *);
-protected int key_get(EditLine *, Char *, key_value_t *);
-protected void key_add(EditLine *, const Char *, key_value_t *, int);
-protected void key_clear(EditLine *, el_action_t *, const Char *);
-protected int key_delete(EditLine *, const Char *);
-protected void key_print(EditLine *, const Char *);
-protected void key_kprint(EditLine *, const Char *, key_value_t *,
- int);
-protected size_t key__decode_str(const Char *, char *, size_t,
+libedit_private int keymacro_init(EditLine *);
+libedit_private void keymacro_end(EditLine *);
+libedit_private keymacro_value_t *keymacro_map_cmd(EditLine *, int);
+libedit_private keymacro_value_t *keymacro_map_str(EditLine *, wchar_t *);
+libedit_private void keymacro_reset(EditLine *);
+libedit_private int keymacro_get(EditLine *, wchar_t *, keymacro_value_t *);
+libedit_private void keymacro_add(EditLine *, const wchar_t *,
+ keymacro_value_t *, int);
+libedit_private void keymacro_clear(EditLine *, el_action_t *, const wchar_t *);
+libedit_private int keymacro_delete(EditLine *, const wchar_t *);
+libedit_private void keymacro_print(EditLine *, const wchar_t *);
+libedit_private void keymacro_kprint(EditLine *, const wchar_t *,
+ keymacro_value_t *, int);
+libedit_private size_t keymacro__decode_str(const wchar_t *, char *, size_t,
const char *);
-#endif /* _h_el_key */
+#endif /* _h_el_keymacro */
diff --git a/lib/libedit/src/makelist b/lib/libedit/src/makelist
index 662bc0704b4a..c8f927651ab3 100644
--- a/lib/libedit/src/makelist
+++ b/lib/libedit/src/makelist
@@ -1,5 +1,5 @@
#!/bin/sh -
-# $NetBSD: makelist,v 1.16 2010/04/18 21:17:05 christos Exp $
+# $NetBSD: makelist,v 1.29 2016/05/09 21:46:56 christos Exp $
#
# Copyright (c) 1992, 1993
# The Regents of the University of California. All rights reserved.
@@ -35,7 +35,8 @@
# makelist.sh: Automatically generate header files...
-USAGE="Usage: $0 -n|-h|-e|-fc|-fh|-bc|-bh|-m <filenames>"
+AWK=awk
+USAGE="Usage: $0 -h|-fc|-fh|-bh <filenames>"
if [ "x$1" = "x" ]
then
@@ -50,17 +51,6 @@ FILES="$@"
case $FLAG in
-# generate foo.h file from foo.c
-#
--n)
- cat << _EOF
-#include "config.h"
-#undef WIDECHAR
-#define NARROWCHAR
-#include "${FILES}"
-_EOF
- ;;
-
-h)
set - `echo $FILES | sed -e 's/\\./_/g'`
hdr="_h_`basename $1`"
@@ -77,7 +67,8 @@ _EOF
# XXX: need a space between name and prototype so that -fc and -fh
# parsing is much easier
#
- printf("protected el_action_t\t%s (EditLine *, Int);\n", name);
+ printf("libedit_private el_action_t\t%s (EditLine *, wint_t);\n",
+ name);
}
}
END {
@@ -85,15 +76,13 @@ _EOF
}'
;;
-# generate help.c from various .c files
+# generate help.h from various .c files
#
--bc)
+-bh)
cat $FILES | $AWK '
BEGIN {
printf("/* Automatically generated file, do not edit */\n");
- printf("#include \"config.h\"\n#include \"el.h\"\n");
- printf("#include \"chartype.h\"\n");
- printf("private const struct el_bindings_t el_func_help[] = {\n");
+ printf("static const struct el_bindings_t el_func_help[] = {\n");
low = "abcdefghijklmnopqrstuvwxyz_";
high = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_";
for (i = 1; i <= length(low); i++)
@@ -113,46 +102,31 @@ _EOF
fname = fname s;
}
- printf(" { %-30.30s %-30.30s\n","STR(\"" fname "\"),", uname ",");
+ printf(" { %-30.30s %-30.30s\n","L\"" fname "\",", uname ",");
ok = 1;
}
}
/^ \*/ {
if (ok) {
- printf(" STR(\"");
+ printf(" L\"");
for (i = 2; i < NF; i++)
printf("%s ", $i);
- printf("%s\") },\n", $i);
+ printf("%s\" },\n", $i);
ok = 0;
}
}
END {
printf("};\n");
- printf("\nprotected const el_bindings_t* help__get()");
- printf("{ return el_func_help; }\n");
}'
;;
-# generate help.h from various .c files
-#
--bh)
- $AWK '
- BEGIN {
- printf("/* Automatically generated file, do not edit */\n");
- printf("#ifndef _h_help_c\n#define _h_help_c\n");
- printf("protected const el_bindings_t *help__get(void);\n");
- printf("#endif /* _h_help_c */\n");
- }' /dev/null
- ;;
-
# generate fcns.h from various .h files
#
-fh)
cat $FILES | $AWK '/el_action_t/ { print $3 }' | \
- sort | tr '[a-z]' '[A-Z]' | $AWK '
+ sort | tr '[:lower:]' '[:upper:]' | $AWK '
BEGIN {
printf("/* Automatically generated file, do not edit */\n");
- printf("#ifndef _h_fcns_c\n#define _h_fcns_c\n");
count = 0;
}
{
@@ -160,21 +134,16 @@ _EOF
}
END {
printf("#define\t%-30.30s\t%3d\n", "EL_NUM_FCNS", count);
-
- printf("typedef el_action_t (*el_func_t)(EditLine *, Int);");
- printf("\nprotected const el_func_t* func__get(void);\n");
- printf("#endif /* _h_fcns_c */\n");
}'
;;
-# generate fcns.c from various .h files
+# generate func.h from various .h files
#
-fc)
cat $FILES | $AWK '/el_action_t/ { print $3 }' | sort | $AWK '
BEGIN {
printf("/* Automatically generated file, do not edit */\n");
- printf("#include \"config.h\"\n#include \"el.h\"\n");
- printf("private const el_func_t el_func[] = {");
+ printf("static const el_func_t el_func[] = {");
maxlen = 80;
needn = 1;
len = 0;
@@ -194,59 +163,6 @@ _EOF
}
END {
printf("\n};\n");
- printf("\nprotected const el_func_t* func__get() { return el_func; }\n");
- }'
- ;;
-
-# generate editline.c from various .c files
-#
--e)
- echo "$FILES" | tr ' ' '\012' | $AWK '
- BEGIN {
- printf("/* Automatically generated file, do not edit */\n");
- printf("#define protected static\n");
- printf("#define SCCSID\n");
- }
- {
- printf("#include \"%s\"\n", $1);
- }'
- ;;
-
-# generate man page fragment from various .c files
-#
--m)
- cat $FILES | $AWK '
- BEGIN {
- printf(".\\\" Section automatically generated with makelist\n");
- printf(".Bl -tag -width 4n\n");
- }
- /\(\):/ {
- pr = substr($2, 1, 2);
- if (pr == "vi" || pr == "em" || pr == "ed") {
- name = substr($2, 1, length($2) - 3);
- fname = "";
- for (i = 1; i <= length(name); i++) {
- s = substr(name, i, 1);
- if (s == "_")
- s = "-";
- fname = fname s;
- }
-
- printf(".It Ic %s\n", fname);
- ok = 1;
- }
- }
- /^ \*/ {
- if (ok) {
- for (i = 2; i < NF; i++)
- printf("%s ", $i);
- printf("%s.\n", $i);
- ok = 0;
- }
- }
- END {
- printf(".El\n");
- printf(".\\\" End of section automatically generated with makelist\n");
}'
;;
diff --git a/lib/libedit/src/map.c b/lib/libedit/src/map.c
index e622060faff1..f72d2724a81e 100644
--- a/lib/libedit/src/map.c
+++ b/lib/libedit/src/map.c
@@ -1,4 +1,4 @@
-/* $NetBSD: map.c,v 1.25 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: map.c,v 1.51 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,30 +37,40 @@
#if 0
static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: map.c,v 1.25 2009/12/30 22:37:40 christos Exp $");
+__RCSID("$NetBSD: map.c,v 1.51 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
* map.c: Editor function definitions
*/
+#include <ctype.h>
#include <stdlib.h>
-#include "el.h"
+#include <string.h>
-private void map_print_key(EditLine *, el_action_t *, const Char *);
-private void map_print_some_keys(EditLine *, el_action_t *, Int, Int);
-private void map_print_all_keys(EditLine *);
-private void map_init_nls(EditLine *);
-private void map_init_meta(EditLine *);
+#include "el.h"
+#include "common.h"
+#include "emacs.h"
+#include "vi.h"
+#include "fcns.h"
+#include "func.h"
+#include "help.h"
+#include "parse.h"
+
+static void map_print_key(EditLine *, el_action_t *, const wchar_t *);
+static void map_print_some_keys(EditLine *, el_action_t *, wint_t, wint_t);
+static void map_print_all_keys(EditLine *);
+static void map_init_nls(EditLine *);
+static void map_init_meta(EditLine *);
/* keymap tables ; should be N_KEYS*sizeof(KEYCMD) bytes long */
-private const el_action_t el_map_emacs[] = {
+static const el_action_t el_map_emacs[] = {
/* 0 */ EM_SET_MARK, /* ^@ */
/* 1 */ ED_MOVE_TO_BEG, /* ^A */
/* 2 */ ED_PREV_CHAR, /* ^B */
- /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 3 */ ED_IGNORE, /* ^C */
/* 4 */ EM_DELETE_OR_LIST, /* ^D */
/* 5 */ ED_MOVE_TO_END, /* ^E */
/* 6 */ ED_NEXT_CHAR, /* ^F */
@@ -72,21 +82,21 @@ private const el_action_t el_map_emacs[] = {
/* 12 */ ED_CLEAR_SCREEN, /* ^L */
/* 13 */ ED_NEWLINE, /* ^M */
/* 14 */ ED_NEXT_HISTORY, /* ^N */
- /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 15 */ ED_IGNORE, /* ^O */
/* 16 */ ED_PREV_HISTORY, /* ^P */
- /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 17 */ ED_IGNORE, /* ^Q */
/* 18 */ ED_REDISPLAY, /* ^R */
- /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 19 */ ED_IGNORE, /* ^S */
/* 20 */ ED_TRANSPOSE_CHARS, /* ^T */
/* 21 */ EM_KILL_LINE, /* ^U */
/* 22 */ ED_QUOTED_INSERT, /* ^V */
/* 23 */ EM_KILL_REGION, /* ^W */
/* 24 */ ED_SEQUENCE_LEAD_IN, /* ^X */
/* 25 */ EM_YANK, /* ^Y */
- /* 26 */ ED_TTY_SIGTSTP, /* ^Z */
+ /* 26 */ ED_IGNORE, /* ^Z */
/* 27 */ EM_META_NEXT, /* ^[ */
- /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
- /* 29 */ ED_TTY_DSUSP, /* ^] */
+ /* 28 */ ED_IGNORE, /* ^\ */
+ /* 29 */ ED_IGNORE, /* ^] */
/* 30 */ ED_UNASSIGNED, /* ^^ */
/* 31 */ ED_UNASSIGNED, /* ^_ */
/* 32 */ ED_INSERT, /* SPACE */
@@ -323,7 +333,7 @@ private const el_action_t el_map_emacs[] = {
* insert mode characters are in the normal keymap, and command mode
* in the extended keymap.
*/
-private const el_action_t el_map_vi_insert[] = {
+static const el_action_t el_map_vi_insert[] = {
#ifdef KSHVI
/* 0 */ ED_UNASSIGNED, /* ^@ */
/* 1 */ ED_INSERT, /* ^A */
@@ -342,9 +352,9 @@ private const el_action_t el_map_vi_insert[] = {
/* 14 */ ED_INSERT, /* ^N */
/* 15 */ ED_INSERT, /* ^O */
/* 16 */ ED_INSERT, /* ^P */
- /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 17 */ ED_IGNORE, /* ^Q */
/* 18 */ ED_INSERT, /* ^R */
- /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 19 */ ED_IGNORE, /* ^S */
/* 20 */ ED_INSERT, /* ^T */
/* 21 */ VI_KILL_LINE_PREV, /* ^U */
/* 22 */ ED_QUOTED_INSERT, /* ^V */
@@ -354,7 +364,7 @@ private const el_action_t el_map_vi_insert[] = {
/* 25 */ ED_INSERT, /* ^Y */
/* 26 */ ED_INSERT, /* ^Z */
/* 27 */ VI_COMMAND_MODE, /* ^[ */ /* [ Esc ] key */
- /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 28 */ ED_IGNORE, /* ^\ */
/* 29 */ ED_INSERT, /* ^] */
/* 30 */ ED_INSERT, /* ^^ */
/* 31 */ ED_INSERT, /* ^_ */
@@ -368,7 +378,7 @@ private const el_action_t el_map_vi_insert[] = {
/* 0 */ ED_UNASSIGNED, /* ^@ */
/* 1 */ ED_MOVE_TO_BEG, /* ^A */
/* 2 */ ED_PREV_CHAR, /* ^B */
- /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 3 */ ED_IGNORE, /* ^C */
/* 4 */ VI_LIST_OR_EOF, /* ^D */
/* 5 */ ED_MOVE_TO_END, /* ^E */
/* 6 */ ED_NEXT_CHAR, /* ^F */
@@ -380,20 +390,20 @@ private const el_action_t el_map_vi_insert[] = {
/* 12 */ ED_CLEAR_SCREEN, /* ^L */
/* 13 */ ED_NEWLINE, /* ^M */
/* 14 */ ED_NEXT_HISTORY, /* ^N */
- /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 15 */ ED_IGNORE, /* ^O */
/* 16 */ ED_PREV_HISTORY, /* ^P */
- /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 17 */ ED_IGNORE, /* ^Q */
/* 18 */ ED_REDISPLAY, /* ^R */
- /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 19 */ ED_IGNORE, /* ^S */
/* 20 */ ED_TRANSPOSE_CHARS, /* ^T */
/* 21 */ VI_KILL_LINE_PREV, /* ^U */
/* 22 */ ED_QUOTED_INSERT, /* ^V */
/* 23 */ ED_DELETE_PREV_WORD, /* ^W */
/* 24 */ ED_UNASSIGNED, /* ^X */
- /* 25 */ ED_TTY_DSUSP, /* ^Y */
- /* 26 */ ED_TTY_SIGTSTP, /* ^Z */
+ /* 25 */ ED_IGNORE, /* ^Y */
+ /* 26 */ ED_IGNORE, /* ^Z */
/* 27 */ VI_COMMAND_MODE, /* ^[ */
- /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 28 */ ED_IGNORE, /* ^\ */
/* 29 */ ED_UNASSIGNED, /* ^] */
/* 30 */ ED_UNASSIGNED, /* ^^ */
/* 31 */ ED_UNASSIGNED, /* ^_ */
@@ -624,11 +634,11 @@ private const el_action_t el_map_vi_insert[] = {
/* 255 */ ED_INSERT /* M-^? */
};
-private const el_action_t el_map_vi_command[] = {
+static const el_action_t el_map_vi_command[] = {
/* 0 */ ED_UNASSIGNED, /* ^@ */
/* 1 */ ED_MOVE_TO_BEG, /* ^A */
/* 2 */ ED_UNASSIGNED, /* ^B */
- /* 3 */ ED_TTY_SIGINT, /* ^C */
+ /* 3 */ ED_IGNORE, /* ^C */
/* 4 */ ED_UNASSIGNED, /* ^D */
/* 5 */ ED_MOVE_TO_END, /* ^E */
/* 6 */ ED_UNASSIGNED, /* ^F */
@@ -640,11 +650,11 @@ private const el_action_t el_map_vi_command[] = {
/* 12 */ ED_CLEAR_SCREEN, /* ^L */
/* 13 */ ED_NEWLINE, /* ^M */
/* 14 */ ED_NEXT_HISTORY, /* ^N */
- /* 15 */ ED_TTY_FLUSH_OUTPUT, /* ^O */
+ /* 15 */ ED_IGNORE, /* ^O */
/* 16 */ ED_PREV_HISTORY, /* ^P */
- /* 17 */ ED_TTY_START_OUTPUT, /* ^Q */
+ /* 17 */ ED_IGNORE, /* ^Q */
/* 18 */ ED_REDISPLAY, /* ^R */
- /* 19 */ ED_TTY_STOP_OUTPUT, /* ^S */
+ /* 19 */ ED_IGNORE, /* ^S */
/* 20 */ ED_UNASSIGNED, /* ^T */
/* 21 */ VI_KILL_LINE_PREV, /* ^U */
/* 22 */ ED_UNASSIGNED, /* ^V */
@@ -653,7 +663,7 @@ private const el_action_t el_map_vi_command[] = {
/* 25 */ ED_UNASSIGNED, /* ^Y */
/* 26 */ ED_UNASSIGNED, /* ^Z */
/* 27 */ EM_META_NEXT, /* ^[ */
- /* 28 */ ED_TTY_SIGQUIT, /* ^\ */
+ /* 28 */ ED_IGNORE, /* ^\ */
/* 29 */ ED_UNASSIGNED, /* ^] */
/* 30 */ ED_UNASSIGNED, /* ^^ */
/* 31 */ ED_UNASSIGNED, /* ^_ */
@@ -887,7 +897,7 @@ private const el_action_t el_map_vi_command[] = {
/* map_init():
* Initialize and allocate the maps
*/
-protected int
+libedit_private int
map_init(EditLine *el)
{
@@ -903,26 +913,25 @@ map_init(EditLine *el)
EL_ABORT((el->errfile, "Vi insert map incorrect\n"));
#endif
- el->el_map.alt = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS);
+ el->el_map.alt = el_malloc(sizeof(*el->el_map.alt) * N_KEYS);
if (el->el_map.alt == NULL)
- return (-1);
- el->el_map.key = (el_action_t *)el_malloc(sizeof(el_action_t) * N_KEYS);
+ return -1;
+ el->el_map.key = el_malloc(sizeof(*el->el_map.key) * N_KEYS);
if (el->el_map.key == NULL)
- return (-1);
+ return -1;
el->el_map.emacs = el_map_emacs;
el->el_map.vic = el_map_vi_command;
el->el_map.vii = el_map_vi_insert;
- el->el_map.help = (el_bindings_t *) el_malloc(sizeof(el_bindings_t) *
- EL_NUM_FCNS);
+ el->el_map.help = el_malloc(sizeof(*el->el_map.help) * EL_NUM_FCNS);
if (el->el_map.help == NULL)
- return (-1);
- (void) memcpy(el->el_map.help, help__get(),
- sizeof(el_bindings_t) * EL_NUM_FCNS);
- el->el_map.func = (el_func_t *)el_malloc(sizeof(el_func_t) *
- EL_NUM_FCNS);
+ return -1;
+ (void) memcpy(el->el_map.help, el_func_help,
+ sizeof(*el->el_map.help) * EL_NUM_FCNS);
+ el->el_map.func = el_malloc(sizeof(*el->el_map.func) * EL_NUM_FCNS);
if (el->el_map.func == NULL)
- return (-1);
- memcpy(el->el_map.func, func__get(), sizeof(el_func_t) * EL_NUM_FCNS);
+ return -1;
+ memcpy(el->el_map.func, el_func, sizeof(*el->el_map.func)
+ * EL_NUM_FCNS);
el->el_map.nfunc = EL_NUM_FCNS;
#ifdef VIDEFAULT
@@ -930,27 +939,27 @@ map_init(EditLine *el)
#else
map_init_emacs(el);
#endif /* VIDEFAULT */
- return (0);
+ return 0;
}
/* map_end():
* Free the space taken by the editor maps
*/
-protected void
+libedit_private void
map_end(EditLine *el)
{
- el_free((ptr_t) el->el_map.alt);
+ el_free(el->el_map.alt);
el->el_map.alt = NULL;
- el_free((ptr_t) el->el_map.key);
+ el_free(el->el_map.key);
el->el_map.key = NULL;
el->el_map.emacs = NULL;
el->el_map.vic = NULL;
el->el_map.vii = NULL;
- el_free((ptr_t) el->el_map.help);
+ el_free(el->el_map.help);
el->el_map.help = NULL;
- el_free((ptr_t) el->el_map.func);
+ el_free(el->el_map.func);
el->el_map.func = NULL;
}
@@ -958,7 +967,7 @@ map_end(EditLine *el)
/* map_init_nls():
* Find all the printable keys and bind them to self insert
*/
-private void
+static void
map_init_nls(EditLine *el)
{
int i;
@@ -966,7 +975,7 @@ map_init_nls(EditLine *el)
el_action_t *map = el->el_map.key;
for (i = 0200; i <= 0377; i++)
- if (Isprint(i))
+ if (iswprint(i))
map[i] = ED_INSERT;
}
@@ -974,10 +983,10 @@ map_init_nls(EditLine *el)
/* map_init_meta():
* Bind all the meta keys to the appropriate ESC-<key> sequence
*/
-private void
+static void
map_init_meta(EditLine *el)
{
- Char buf[3];
+ wchar_t buf[3];
int i;
el_action_t *map = el->el_map.key;
el_action_t *alt = el->el_map.alt;
@@ -995,7 +1004,7 @@ map_init_meta(EditLine *el)
} else
map = alt;
}
- buf[0] = (Char) i;
+ buf[0] = (wchar_t)i;
buf[2] = 0;
for (i = 0200; i <= 0377; i++)
switch (map[i]) {
@@ -1005,7 +1014,7 @@ map_init_meta(EditLine *el)
break;
default:
buf[1] = i & 0177;
- key_add(el, buf, key_map_cmd(el, (int) map[i]), XK_CMD);
+ keymacro_add(el, buf, keymacro_map_cmd(el, (int) map[i]), XK_CMD);
break;
}
map[(int) buf[0]] = ED_SEQUENCE_LEAD_IN;
@@ -1015,7 +1024,7 @@ map_init_meta(EditLine *el)
/* map_init_vi():
* Initialize the vi bindings
*/
-protected void
+libedit_private void
map_init_vi(EditLine *el)
{
int i;
@@ -1027,7 +1036,7 @@ map_init_vi(EditLine *el)
el->el_map.type = MAP_VI;
el->el_map.current = el->el_map.key;
- key_reset(el);
+ keymacro_reset(el);
for (i = 0; i < N_KEYS; i++) {
key[i] = vii[i];
@@ -1038,25 +1047,25 @@ map_init_vi(EditLine *el)
map_init_nls(el);
tty_bind_char(el, 1);
- term_bind_arrow(el);
+ terminal_bind_arrow(el);
}
/* map_init_emacs():
* Initialize the emacs bindings
*/
-protected void
+libedit_private void
map_init_emacs(EditLine *el)
{
int i;
- Char buf[3];
+ wchar_t buf[3];
el_action_t *key = el->el_map.key;
el_action_t *alt = el->el_map.alt;
const el_action_t *emacs = el->el_map.emacs;
el->el_map.type = MAP_EMACS;
el->el_map.current = el->el_map.key;
- key_reset(el);
+ keymacro_reset(el);
for (i = 0; i < N_KEYS; i++) {
key[i] = emacs[i];
@@ -1069,84 +1078,84 @@ map_init_emacs(EditLine *el)
buf[0] = CONTROL('X');
buf[1] = CONTROL('X');
buf[2] = 0;
- key_add(el, buf, key_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD);
+ keymacro_add(el, buf, keymacro_map_cmd(el, EM_EXCHANGE_MARK), XK_CMD);
tty_bind_char(el, 1);
- term_bind_arrow(el);
+ terminal_bind_arrow(el);
}
/* map_set_editor():
* Set the editor
*/
-protected int
-map_set_editor(EditLine *el, Char *editor)
+libedit_private int
+map_set_editor(EditLine *el, wchar_t *editor)
{
- if (Strcmp(editor, STR("emacs")) == 0) {
+ if (wcscmp(editor, L"emacs") == 0) {
map_init_emacs(el);
- return (0);
+ return 0;
}
- if (Strcmp(editor, STR("vi")) == 0) {
+ if (wcscmp(editor, L"vi") == 0) {
map_init_vi(el);
- return (0);
+ return 0;
}
- return (-1);
+ return -1;
}
/* map_get_editor():
* Retrieve the editor
*/
-protected int
-map_get_editor(EditLine *el, const Char **editor)
+libedit_private int
+map_get_editor(EditLine *el, const wchar_t **editor)
{
if (editor == NULL)
- return (-1);
+ return -1;
switch (el->el_map.type) {
case MAP_EMACS:
- *editor = STR("emacs");
- return (0);
+ *editor = L"emacs";
+ return 0;
case MAP_VI:
- *editor = STR("vi");
- return (0);
+ *editor = L"vi";
+ return 0;
}
- return (-1);
+ return -1;
}
/* map_print_key():
* Print the function description for 1 key
*/
-private void
-map_print_key(EditLine *el, el_action_t *map, const Char *in)
+static void
+map_print_key(EditLine *el, el_action_t *map, const wchar_t *in)
{
char outbuf[EL_BUFSIZ];
el_bindings_t *bp, *ep;
if (in[0] == '\0' || in[1] == '\0') {
- (void) key__decode_str(in, outbuf, sizeof(outbuf), "");
+ (void) keymacro__decode_str(in, outbuf, sizeof(outbuf), "");
ep = &el->el_map.help[el->el_map.nfunc];
for (bp = el->el_map.help; bp < ep; bp++)
if (bp->func == map[(unsigned char) *in]) {
(void) fprintf(el->el_outfile,
- "%s\t->\t" FSTR "\n", outbuf, bp->name);
+ "%s\t->\t%ls\n", outbuf, bp->name);
return;
}
} else
- key_print(el, in);
+ keymacro_print(el, in);
}
/* map_print_some_keys():
* Print keys from first to last
*/
-private void
-map_print_some_keys(EditLine *el, el_action_t *map, Int first, Int last)
+static void
+map_print_some_keys(EditLine *el, el_action_t *map, wint_t first, wint_t last)
{
el_bindings_t *bp, *ep;
- Char firstbuf[2], lastbuf[2];
+ wchar_t firstbuf[2], lastbuf[2];
char unparsbuf[EL_BUFSIZ], extrabuf[EL_BUFSIZ];
firstbuf[0] = first;
@@ -1155,7 +1164,7 @@ map_print_some_keys(EditLine *el, el_action_t *map, Int first, Int last)
lastbuf[1] = 0;
if (map[first] == ED_UNASSIGNED) {
if (first == last) {
- (void) key__decode_str(firstbuf, unparsbuf,
+ (void) keymacro__decode_str(firstbuf, unparsbuf,
sizeof(unparsbuf), STRQQ);
(void) fprintf(el->el_outfile,
"%-15s-> is undefined\n", unparsbuf);
@@ -1166,17 +1175,17 @@ map_print_some_keys(EditLine *el, el_action_t *map, Int first, Int last)
for (bp = el->el_map.help; bp < ep; bp++) {
if (bp->func == map[first]) {
if (first == last) {
- (void) key__decode_str(firstbuf, unparsbuf,
+ (void) keymacro__decode_str(firstbuf, unparsbuf,
sizeof(unparsbuf), STRQQ);
- (void) fprintf(el->el_outfile, "%-15s-> " FSTR "\n",
+ (void) fprintf(el->el_outfile, "%-15s-> %ls\n",
unparsbuf, bp->name);
} else {
- (void) key__decode_str(firstbuf, unparsbuf,
+ (void) keymacro__decode_str(firstbuf, unparsbuf,
sizeof(unparsbuf), STRQQ);
- (void) key__decode_str(lastbuf, extrabuf,
+ (void) keymacro__decode_str(lastbuf, extrabuf,
sizeof(extrabuf), STRQQ);
(void) fprintf(el->el_outfile,
- "%-4s to %-7s-> " FSTR "\n",
+ "%-4s to %-7s-> %ls\n",
unparsbuf, extrabuf, bp->name);
}
return;
@@ -1184,14 +1193,14 @@ map_print_some_keys(EditLine *el, el_action_t *map, Int first, Int last)
}
#ifdef MAP_DEBUG
if (map == el->el_map.key) {
- (void) key__decode_str(firstbuf, unparsbuf,
+ (void) keymacro__decode_str(firstbuf, unparsbuf,
sizeof(unparsbuf), STRQQ);
(void) fprintf(el->el_outfile,
"BUG!!! %s isn't bound to anything.\n", unparsbuf);
(void) fprintf(el->el_outfile, "el->el_map.key[%d] == %d\n",
first, el->el_map.key[first]);
} else {
- (void) key__decode_str(firstbuf, unparsbuf,
+ (void) keymacro__decode_str(firstbuf, unparsbuf,
sizeof(unparsbuf), STRQQ);
(void) fprintf(el->el_outfile,
"BUG!!! %s isn't bound to anything.\n", unparsbuf);
@@ -1206,7 +1215,7 @@ map_print_some_keys(EditLine *el, el_action_t *map, Int first, Int last)
/* map_print_all_keys():
* Print the function description for all keys.
*/
-private void
+static void
map_print_all_keys(EditLine *el)
{
int prev, i;
@@ -1232,31 +1241,31 @@ map_print_all_keys(EditLine *el)
map_print_some_keys(el, el->el_map.alt, prev, i - 1);
(void) fprintf(el->el_outfile, "Multi-character bindings\n");
- key_print(el, STR(""));
+ keymacro_print(el, L"");
(void) fprintf(el->el_outfile, "Arrow key bindings\n");
- term_print_arrow(el, STR(""));
+ terminal_print_arrow(el, L"");
}
/* map_bind():
* Add/remove/change bindings
*/
-protected int
-map_bind(EditLine *el, int argc, const Char **argv)
+libedit_private int
+map_bind(EditLine *el, int argc, const wchar_t **argv)
{
el_action_t *map;
int ntype, rem;
- const Char *p;
- Char inbuf[EL_BUFSIZ];
- Char outbuf[EL_BUFSIZ];
- const Char *in = NULL;
- Char *out = NULL;
+ const wchar_t *p;
+ wchar_t inbuf[EL_BUFSIZ];
+ wchar_t outbuf[EL_BUFSIZ];
+ const wchar_t *in = NULL;
+ wchar_t *out;
el_bindings_t *bp, *ep;
int cmd;
int key;
if (argv == NULL)
- return (-1);
+ return -1;
map = el->el_map.key;
ntype = XK_CMD;
@@ -1271,11 +1280,6 @@ map_bind(EditLine *el, int argc, const Char **argv)
case 's':
ntype = XK_STR;
break;
-#ifdef notyet
- case 'c':
- ntype = XK_EXE;
- break;
-#endif
case 'k':
key = 1;
break;
@@ -1286,136 +1290,138 @@ map_bind(EditLine *el, int argc, const Char **argv)
case 'v':
map_init_vi(el);
- return (0);
+ return 0;
case 'e':
map_init_emacs(el);
- return (0);
+ return 0;
case 'l':
ep = &el->el_map.help[el->el_map.nfunc];
for (bp = el->el_map.help; bp < ep; bp++)
(void) fprintf(el->el_outfile,
- "" FSTR "\n\t" FSTR "\n",
+ "%ls\n\t%ls\n",
bp->name, bp->description);
- return (0);
+ return 0;
default:
(void) fprintf(el->el_errfile,
- "" FSTR ": Invalid switch `%c'.\n",
- argv[0], p[1]);
+ "%ls: Invalid switch `%lc'.\n",
+ argv[0], (wint_t)p[1]);
}
else
break;
if (argv[argc] == NULL) {
map_print_all_keys(el);
- return (0);
+ return 0;
}
if (key)
in = argv[argc++];
else if ((in = parse__string(inbuf, argv[argc++])) == NULL) {
(void) fprintf(el->el_errfile,
- "" FSTR ": Invalid \\ or ^ in instring.\n",
+ "%ls: Invalid \\ or ^ in instring.\n",
argv[0]);
- return (-1);
+ return -1;
}
if (rem) {
if (key) {
- (void) term_clear_arrow(el, in);
- return (-1);
+ (void) terminal_clear_arrow(el, in);
+ return -1;
}
if (in[1])
- (void) key_delete(el, in);
+ (void) keymacro_delete(el, in);
else if (map[(unsigned char) *in] == ED_SEQUENCE_LEAD_IN)
- (void) key_delete(el, in);
+ (void) keymacro_delete(el, in);
else
map[(unsigned char) *in] = ED_UNASSIGNED;
- return (0);
+ return 0;
}
if (argv[argc] == NULL) {
if (key)
- term_print_arrow(el, in);
+ terminal_print_arrow(el, in);
else
map_print_key(el, map, in);
- return (0);
+ return 0;
}
#ifdef notyet
if (argv[argc + 1] != NULL) {
- bindkey_usage();
- return (-1);
+ bindkeymacro_usage();
+ return -1;
}
#endif
switch (ntype) {
case XK_STR:
- case XK_EXE:
if ((out = parse__string(outbuf, argv[argc])) == NULL) {
(void) fprintf(el->el_errfile,
- "" FSTR ": Invalid \\ or ^ in outstring.\n", argv[0]);
- return (-1);
+ "%ls: Invalid \\ or ^ in outstring.\n", argv[0]);
+ return -1;
}
if (key)
- term_set_arrow(el, in, key_map_str(el, out), ntype);
+ terminal_set_arrow(el, in, keymacro_map_str(el, out), ntype);
else
- key_add(el, in, key_map_str(el, out), ntype);
+ keymacro_add(el, in, keymacro_map_str(el, out), ntype);
map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN;
break;
case XK_CMD:
if ((cmd = parse_cmd(el, argv[argc])) == -1) {
(void) fprintf(el->el_errfile,
- "" FSTR ": Invalid command `" FSTR "'.\n",
+ "%ls: Invalid command `%ls'.\n",
argv[0], argv[argc]);
- return (-1);
+ return -1;
}
if (key)
- term_set_arrow(el, in, key_map_str(el, out), ntype);
+ terminal_set_arrow(el, in, keymacro_map_cmd(el, cmd), ntype);
else {
if (in[1]) {
- key_add(el, in, key_map_cmd(el, cmd), ntype);
+ keymacro_add(el, in, keymacro_map_cmd(el, cmd), ntype);
map[(unsigned char) *in] = ED_SEQUENCE_LEAD_IN;
} else {
- key_clear(el, map, in);
- map[(unsigned char) *in] = cmd;
+ keymacro_clear(el, map, in);
+ map[(unsigned char) *in] = (el_action_t)cmd;
}
}
break;
+ /* coverity[dead_error_begin] */
default:
EL_ABORT((el->el_errfile, "Bad XK_ type %d\n", ntype));
break;
}
- return (0);
+ return 0;
}
/* map_addfunc():
* add a user defined function
*/
-protected int
-map_addfunc(EditLine *el, const Char *name, const Char *help, el_func_t func)
+libedit_private int
+map_addfunc(EditLine *el, const wchar_t *name, const wchar_t *help,
+ el_func_t func)
{
void *p;
- int nf = el->el_map.nfunc + 1;
+ size_t nf = el->el_map.nfunc + 1;
if (name == NULL || help == NULL || func == NULL)
- return (-1);
+ return -1;
- if ((p = el_realloc(el->el_map.func, nf * sizeof(el_func_t))) == NULL)
- return (-1);
- el->el_map.func = (el_func_t *) p;
- if ((p = el_realloc(el->el_map.help, nf * sizeof(el_bindings_t)))
+ if ((p = el_realloc(el->el_map.func, nf *
+ sizeof(*el->el_map.func))) == NULL)
+ return -1;
+ el->el_map.func = p;
+ if ((p = el_realloc(el->el_map.help, nf * sizeof(*el->el_map.help)))
== NULL)
- return (-1);
- el->el_map.help = (el_bindings_t *) p;
+ return -1;
+ el->el_map.help = p;
- nf = el->el_map.nfunc;
+ nf = (size_t)el->el_map.nfunc;
el->el_map.func[nf] = func;
el->el_map.help[nf].name = name;
- el->el_map.help[nf].func = nf;
+ el->el_map.help[nf].func = (int)nf;
el->el_map.help[nf].description = help;
el->el_map.nfunc++;
- return (0);
+ return 0;
}
diff --git a/lib/libedit/src/map.h b/lib/libedit/src/map.h
index 8e0c7e4eaa11..b4e4e2899a1d 100644
--- a/lib/libedit/src/map.h
+++ b/lib/libedit/src/map.h
@@ -1,4 +1,4 @@
-/* $NetBSD: map.h,v 1.9 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: map.h,v 1.13 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,13 +40,14 @@
#ifndef _h_el_map
#define _h_el_map
+typedef el_action_t (*el_func_t)(EditLine *, wint_t);
+
typedef struct el_bindings_t { /* for the "bind" shell command */
- const Char *name; /* function name for bind command */
+ const wchar_t *name; /* function name for bind command */
int func; /* function numeric value */
- const Char *description; /* description of function */
+ const wchar_t *description; /* description of function */
} el_bindings_t;
-
typedef struct el_map_t {
el_action_t *alt; /* The current alternate key map */
el_action_t *key; /* The current normal key map */
@@ -57,7 +58,7 @@ typedef struct el_map_t {
int type; /* Emacs or vi */
el_bindings_t *help; /* The help for the editor functions */
el_func_t *func; /* List of available functions */
- int nfunc; /* The number of functions/help items */
+ size_t nfunc; /* The number of functions/help items */
} el_map_t;
#define MAP_EMACS 0
@@ -65,13 +66,14 @@ typedef struct el_map_t {
#define N_KEYS 256
-protected int map_bind(EditLine *, int, const Char **);
-protected int map_init(EditLine *);
-protected void map_end(EditLine *);
-protected void map_init_vi(EditLine *);
-protected void map_init_emacs(EditLine *);
-protected int map_set_editor(EditLine *, Char *);
-protected int map_get_editor(EditLine *, const Char **);
-protected int map_addfunc(EditLine *, const Char *, const Char *, el_func_t);
+libedit_private int map_bind(EditLine *, int, const wchar_t **);
+libedit_private int map_init(EditLine *);
+libedit_private void map_end(EditLine *);
+libedit_private void map_init_vi(EditLine *);
+libedit_private void map_init_emacs(EditLine *);
+libedit_private int map_set_editor(EditLine *, wchar_t *);
+libedit_private int map_get_editor(EditLine *, const wchar_t **);
+libedit_private int map_addfunc(EditLine *, const wchar_t *, const wchar_t *,
+ el_func_t);
#endif /* _h_el_map */
diff --git a/lib/libedit/src/parse.c b/lib/libedit/src/parse.c
index 9983fa750372..fdd0d847a79d 100644
--- a/lib/libedit/src/parse.c
+++ b/lib/libedit/src/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.23 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: parse.c,v 1.40 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: parse.c,v 1.23 2009/12/30 22:37:40 christos Exp $");
+__RCSID("$NetBSD: parse.c,v 1.40 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -54,80 +54,83 @@ __RCSID("$NetBSD: parse.c,v 1.23 2009/12/30 22:37:40 christos Exp $");
* settc
* setty
*/
-#include "el.h"
#include <stdlib.h>
+#include <string.h>
+
+#include "el.h"
+#include "parse.h"
-private const struct {
- const Char *name;
- int (*func)(EditLine *, int, const Char **);
+static const struct {
+ const wchar_t *name;
+ int (*func)(EditLine *, int, const wchar_t **);
} cmds[] = {
- { STR("bind"), map_bind },
- { STR("echotc"), term_echotc },
- { STR("edit"), el_editmode },
- { STR("history"), hist_command },
- { STR("telltc"), term_telltc },
- { STR("settc"), term_settc },
- { STR("setty"), tty_stty },
- { NULL, NULL }
+ { L"bind", map_bind },
+ { L"echotc", terminal_echotc },
+ { L"edit", el_editmode },
+ { L"history", hist_command },
+ { L"telltc", terminal_telltc },
+ { L"settc", terminal_settc },
+ { L"setty", tty_stty },
+ { NULL, NULL }
};
/* parse_line():
* Parse a line and dispatch it
*/
-protected int
-parse_line(EditLine *el, const Char *line)
+libedit_private int
+parse_line(EditLine *el, const wchar_t *line)
{
- const Char **argv;
+ const wchar_t **argv;
int argc;
- TYPE(Tokenizer) *tok;
+ TokenizerW *tok;
- tok = FUN(tok,init)(NULL);
- FUN(tok,str)(tok, line, &argc, &argv);
- argc = FUN(el,parse)(el, argc, argv);
- FUN(tok,end)(tok);
- return (argc);
+ tok = tok_winit(NULL);
+ tok_wstr(tok, line, &argc, &argv);
+ argc = el_wparse(el, argc, argv);
+ tok_wend(tok);
+ return argc;
}
/* el_parse():
* Command dispatcher
*/
-public int
-FUN(el,parse)(EditLine *el, int argc, const Char *argv[])
+int
+el_wparse(EditLine *el, int argc, const wchar_t *argv[])
{
- const Char *ptr;
+ const wchar_t *ptr;
int i;
if (argc < 1)
- return (-1);
- ptr = Strchr(argv[0], ':');
+ return -1;
+ ptr = wcschr(argv[0], L':');
if (ptr != NULL) {
- Char *tprog;
+ wchar_t *tprog;
size_t l;
if (ptr == argv[0])
- return (0);
- l = ptr - argv[0] - 1;
+ return 0;
+ l = (size_t)(ptr - argv[0] - 1);
tprog = el_malloc((l + 1) * sizeof(*tprog));
if (tprog == NULL)
- return (0);
- (void) Strncpy(tprog, argv[0], l);
+ return 0;
+ (void) wcsncpy(tprog, argv[0], l);
tprog[l] = '\0';
ptr++;
- l = el_match(el->el_prog, tprog);
+ l = (size_t)el_match(el->el_prog, tprog);
el_free(tprog);
if (!l)
- return (0);
+ return 0;
} else
ptr = argv[0];
for (i = 0; cmds[i].name != NULL; i++)
- if (Strcmp(cmds[i].name, ptr) == 0) {
+ if (wcscmp(cmds[i].name, ptr) == 0) {
i = (*cmds[i].func) (el, argc, argv);
- return (-i);
+ return -i;
}
- return (-1);
+ return -1;
}
@@ -135,16 +138,16 @@ FUN(el,parse)(EditLine *el, int argc, const Char *argv[])
* Parse a string of the form ^<char> \<odigit> \<char> \U+xxxx and return
* the appropriate character or -1 if the escape is not valid
*/
-protected int
-parse__escape(const Char **ptr)
+libedit_private int
+parse__escape(const wchar_t **ptr)
{
- const Char *p;
- Int c;
+ const wchar_t *p;
+ wint_t c;
p = *ptr;
if (p[1] == 0)
- return (-1);
+ return -1;
if (*p == '\\') {
p++;
@@ -173,28 +176,28 @@ parse__escape(const Char **ptr)
case 'e':
c = '\033'; /* Escape */
break;
- case 'U': /* Unicode \U+xxxx or \U+xxxxx format */
- {
- int i;
- const Char hex[] = STR("0123456789ABCDEF");
- const Char *h;
- ++p;
- if (*p++ != '+')
- return (-1);
+ case 'U': /* Unicode \U+xxxx or \U+xxxxx format */
+ {
+ int i;
+ const wchar_t hex[] = L"0123456789ABCDEF";
+ const wchar_t *h;
+ ++p;
+ if (*p++ != '+')
+ return -1;
c = 0;
- for (i = 0; i < 5; ++i) {
- h = Strchr(hex, *p++);
- if (!h && i < 4)
- return (-1);
- else if (h)
- c = (c << 4) | ((int)(h - hex));
- else
- --p;
- }
- if (c > 0x10FFFF) /* outside valid character range */
- return -1;
- break;
- }
+ for (i = 0; i < 5; ++i) {
+ h = wcschr(hex, *p++);
+ if (!h && i < 4)
+ return -1;
+ else if (h)
+ c = (c << 4) | ((int)(h - hex));
+ else
+ --p;
+ }
+ if (c > 0x10FFFF) /* outside valid character range */
+ return -1;
+ break;
+ }
case '0':
case '1':
case '2':
@@ -214,8 +217,8 @@ parse__escape(const Char **ptr)
}
c = (c << 3) | (ch - '0');
}
- if ((c & 0xffffff00) != 0)
- return (-1);
+ if ((c & (wint_t)0xffffff00) != (wint_t)0)
+ return -1;
--p;
break;
}
@@ -229,29 +232,29 @@ parse__escape(const Char **ptr)
} else
c = *p;
*ptr = ++p;
- return (c);
+ return c;
}
/* parse__string():
* Parse the escapes from in and put the raw string out
*/
-protected Char *
-parse__string(Char *out, const Char *in)
+libedit_private wchar_t *
+parse__string(wchar_t *out, const wchar_t *in)
{
- Char *rv = out;
+ wchar_t *rv = out;
int n;
for (;;)
switch (*in) {
case '\0':
*out = '\0';
- return (rv);
+ return rv;
case '\\':
case '^':
if ((n = parse__escape(&in)) == -1)
- return (NULL);
- *out++ = n;
+ return NULL;
+ *out++ = (wchar_t)n;
break;
case 'M':
@@ -273,13 +276,14 @@ parse__string(Char *out, const Char *in)
* Return the command number for the command string given
* or -1 if one is not found
*/
-protected int
-parse_cmd(EditLine *el, const Char *cmd)
+libedit_private int
+parse_cmd(EditLine *el, const wchar_t *cmd)
{
- el_bindings_t *b;
+ el_bindings_t *b = el->el_map.help;
+ size_t i;
- for (b = el->el_map.help; b->name != NULL; b++)
- if (Strcmp(b->name, cmd) == 0)
- return (b->func);
- return (-1);
+ for (i = 0; i < el->el_map.nfunc; i++)
+ if (wcscmp(b[i].name, cmd) == 0)
+ return b[i].func;
+ return -1;
}
diff --git a/lib/libedit/src/parse.h b/lib/libedit/src/parse.h
index ec04051bc27e..fe8eb4733521 100644
--- a/lib/libedit/src/parse.h
+++ b/lib/libedit/src/parse.h
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.h,v 1.7 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: parse.h,v 1.9 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,9 +40,9 @@
#ifndef _h_el_parse
#define _h_el_parse
-protected int parse_line(EditLine *, const Char *);
-protected int parse__escape(const Char **);
-protected Char *parse__string(Char *, const Char *);
-protected int parse_cmd(EditLine *, const Char *);
+libedit_private int parse_line(EditLine *, const wchar_t *);
+libedit_private int parse__escape(const wchar_t **);
+libedit_private wchar_t *parse__string(wchar_t *, const wchar_t *);
+libedit_private int parse_cmd(EditLine *, const wchar_t *);
#endif /* _h_el_parse */
diff --git a/lib/libedit/src/prompt.c b/lib/libedit/src/prompt.c
index acfb4372e4a1..a3d9915ec0e9 100644
--- a/lib/libedit/src/prompt.c
+++ b/lib/libedit/src/prompt.c
@@ -1,4 +1,4 @@
-/* $NetBSD: prompt.c,v 1.18 2009/12/31 15:58:26 christos Exp $ */
+/* $NetBSD: prompt.c,v 1.26 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)prompt.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: prompt.c,v 1.18 2009/12/31 15:58:26 christos Exp $");
+__RCSID("$NetBSD: prompt.c,v 1.26 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -47,43 +47,43 @@ __RCSID("$NetBSD: prompt.c,v 1.18 2009/12/31 15:58:26 christos Exp $");
#include <stdio.h>
#include "el.h"
-private Char *prompt_default(EditLine *);
-private Char *prompt_default_r(EditLine *);
+static wchar_t *prompt_default(EditLine *);
+static wchar_t *prompt_default_r(EditLine *);
/* prompt_default():
* Just a default prompt, in case the user did not provide one
*/
-private Char *
+static wchar_t *
/*ARGSUSED*/
prompt_default(EditLine *el __attribute__((__unused__)))
{
- static Char a[3] = {'?', ' ', '\0'};
+ static wchar_t a[3] = L"? ";
- return (a);
+ return a;
}
/* prompt_default_r():
* Just a default rprompt, in case the user did not provide one
*/
-private Char *
+static wchar_t *
/*ARGSUSED*/
prompt_default_r(EditLine *el __attribute__((__unused__)))
{
- static Char a[1] = {'\0'};
+ static wchar_t a[1] = L"";
- return (a);
+ return a;
}
/* prompt_print():
* Print the prompt and update the prompt position.
*/
-protected void
+libedit_private void
prompt_print(EditLine *el, int op)
{
el_prompt_t *elp;
- Char *p;
+ wchar_t *p;
int ignore = 0;
if (op == EL_PROMPT)
@@ -103,7 +103,7 @@ prompt_print(EditLine *el, int op)
continue;
}
if (ignore)
- term__putc(el, *p);
+ terminal__putc(el, *p);
else
re_putc(el, *p, 1);
}
@@ -116,7 +116,7 @@ prompt_print(EditLine *el, int op)
/* prompt_init():
* Initialize the prompt stuff
*/
-protected int
+libedit_private int
prompt_init(EditLine *el)
{
@@ -135,7 +135,7 @@ prompt_init(EditLine *el)
/* prompt_end():
* Clean up the prompt stuff
*/
-protected void
+libedit_private void
/*ARGSUSED*/
prompt_end(EditLine *el __attribute__((__unused__)))
{
@@ -145,8 +145,8 @@ prompt_end(EditLine *el __attribute__((__unused__)))
/* prompt_set():
* Install a prompt printing function
*/
-protected int
-prompt_set(EditLine *el, el_pfunc_t prf, Char c, int op, int wide)
+libedit_private int
+prompt_set(EditLine *el, el_pfunc_t prf, wchar_t c, int op, int wide)
{
el_prompt_t *p;
@@ -177,8 +177,8 @@ prompt_set(EditLine *el, el_pfunc_t prf, Char c, int op, int wide)
/* prompt_get():
* Retrieve the prompt printing function
*/
-protected int
-prompt_get(EditLine *el, el_pfunc_t *prf, Char *c, int op)
+libedit_private int
+prompt_get(EditLine *el, el_pfunc_t *prf, wchar_t *c, int op)
{
el_prompt_t *p;
diff --git a/lib/libedit/src/prompt.h b/lib/libedit/src/prompt.h
index cde7462859ba..2931428dbb35 100644
--- a/lib/libedit/src/prompt.h
+++ b/lib/libedit/src/prompt.h
@@ -1,4 +1,4 @@
-/* $NetBSD: prompt.h,v 1.10 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: prompt.h,v 1.15 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,21 +40,19 @@
#ifndef _h_el_prompt
#define _h_el_prompt
-#include "histedit.h"
-
-typedef Char *(*el_pfunc_t)(EditLine *);
+typedef wchar_t *(*el_pfunc_t)(EditLine *);
typedef struct el_prompt_t {
el_pfunc_t p_func; /* Function to return the prompt */
coord_t p_pos; /* position in the line after prompt */
- Char p_ignore; /* character to start/end literal */
+ wchar_t p_ignore; /* character to start/end literal */
int p_wide;
} el_prompt_t;
-protected void prompt_print(EditLine *, int);
-protected int prompt_set(EditLine *, el_pfunc_t, Char, int, int);
-protected int prompt_get(EditLine *, el_pfunc_t *, Char *, int);
-protected int prompt_init(EditLine *);
-protected void prompt_end(EditLine *);
+libedit_private void prompt_print(EditLine *, int);
+libedit_private int prompt_set(EditLine *, el_pfunc_t, wchar_t, int, int);
+libedit_private int prompt_get(EditLine *, el_pfunc_t *, wchar_t *, int);
+libedit_private int prompt_init(EditLine *);
+libedit_private void prompt_end(EditLine *);
#endif /* _h_el_prompt */
diff --git a/lib/libedit/src/read.c b/lib/libedit/src/read.c
index 094977f72671..cfab8206eeca 100644
--- a/lib/libedit/src/read.c
+++ b/lib/libedit/src/read.c
@@ -1,4 +1,4 @@
-/* $NetBSD: read.c,v 1.58 2011/02/18 20:53:05 christos Exp $ */
+/* $NetBSD: read.c,v 1.101 2016/05/25 13:01:11 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,49 +37,90 @@
#if 0
static char sccsid[] = "@(#)read.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: read.c,v 1.58 2011/02/18 20:53:05 christos Exp $");
+__RCSID("$NetBSD: read.c,v 1.101 2016/05/25 13:01:11 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
- * read.c: Clean this junk up! This is horrible code.
- * Terminal read functions
+ * read.c: Terminal read functions
*/
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
-#include <unistd.h>
-#include <stdlib.h>
#include <limits.h>
-#include "el.h"
-
-#define OKCMD -1 /* must be -1! */
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
-private int read__fixio(int, int);
-private int read_preread(EditLine *);
-private int read_char(EditLine *, Char *);
-private int read_getcmd(EditLine *, el_action_t *, Char *);
-private void read_pop(c_macro_t *);
+#include "el.h"
+#include "fcns.h"
+#include "read.h"
+
+#define EL_MAXMACRO 10
+
+struct macros {
+ wchar_t **macro;
+ int level;
+ int offset;
+};
+
+struct el_read_t {
+ struct macros macros;
+ el_rfunc_t read_char; /* Function to read a character. */
+ int read_errno;
+};
+
+static int read__fixio(int, int);
+static int read_char(EditLine *, wchar_t *);
+static int read_getcmd(EditLine *, el_action_t *, wchar_t *);
+static void read_clearmacros(struct macros *);
+static void read_pop(struct macros *);
+static const wchar_t *noedit_wgets(EditLine *, int *);
/* read_init():
* Initialize the read stuff
*/
-protected int
+libedit_private int
read_init(EditLine *el)
{
+ struct macros *ma;
+
+ if ((el->el_read = el_malloc(sizeof(*el->el_read))) == NULL)
+ return -1;
+
+ ma = &el->el_read->macros;
+ if ((ma->macro = el_malloc(EL_MAXMACRO *
+ sizeof(*ma->macro))) == NULL) {
+ free(el->el_read);
+ return -1;
+ }
+ ma->level = -1;
+ ma->offset = 0;
+
/* builtin read_char */
- el->el_read.read_char = read_char;
+ el->el_read->read_char = read_char;
return 0;
}
+/* el_read_end():
+ * Free the data structures used by the read stuff.
+ */
+libedit_private void
+read_end(struct el_read_t *el_read)
+{
+ read_clearmacros(&el_read->macros);
+ el_free(el_read->macros.macro);
+ el_read->macros.macro = NULL;
+}
/* el_read_setfn():
* Set the read char function to the one provided.
* If it is set to EL_BUILTIN_GETCFN, then reset to the builtin one.
*/
-protected int
-el_read_setfn(EditLine *el, el_rfunc_t rc)
+libedit_private int
+el_read_setfn(struct el_read_t *el_read, el_rfunc_t rc)
{
- el->el_read.read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc;
+ el_read->read_char = (rc == EL_BUILTIN_GETCFN) ? read_char : rc;
return 0;
}
@@ -88,42 +129,19 @@ el_read_setfn(EditLine *el, el_rfunc_t rc)
* return the current read char function, or EL_BUILTIN_GETCFN
* if it is the default one
*/
-protected el_rfunc_t
-el_read_getfn(EditLine *el)
+libedit_private el_rfunc_t
+el_read_getfn(struct el_read_t *el_read)
{
- return (el->el_read.read_char == read_char) ?
- EL_BUILTIN_GETCFN : el->el_read.read_char;
+ return el_read->read_char == read_char ?
+ EL_BUILTIN_GETCFN : el_read->read_char;
}
-#ifndef MIN
-#define MIN(A,B) ((A) < (B) ? (A) : (B))
-#endif
-
-#ifdef DEBUG_EDIT
-private void
-read_debug(EditLine *el)
-{
-
- if (el->el_line.cursor > el->el_line.lastchar)
- (void) fprintf(el->el_errfile, "cursor > lastchar\r\n");
- if (el->el_line.cursor < el->el_line.buffer)
- (void) fprintf(el->el_errfile, "cursor < buffer\r\n");
- if (el->el_line.cursor > el->el_line.limit)
- (void) fprintf(el->el_errfile, "cursor > limit\r\n");
- if (el->el_line.lastchar > el->el_line.limit)
- (void) fprintf(el->el_errfile, "lastchar > limit\r\n");
- if (el->el_line.limit != &el->el_line.buffer[EL_BUFSIZ - 2])
- (void) fprintf(el->el_errfile, "limit != &buffer[EL_BUFSIZ-2]\r\n");
-}
-#endif /* DEBUG_EDIT */
-
-
/* read__fixio():
* Try to recover from a read error
*/
/* ARGSUSED */
-private int
+static int
read__fixio(int fd __attribute__((__unused__)), int e)
{
@@ -133,7 +151,7 @@ read__fixio(int fd __attribute__((__unused__)), int e)
#ifdef EWOULDBLOCK
case EWOULDBLOCK:
#ifndef TRY_AGAIN
-#define TRY_AGAIN
+#define TRY_AGAIN
#endif
#endif /* EWOULDBLOCK */
@@ -141,7 +159,7 @@ read__fixio(int fd __attribute__((__unused__)), int e)
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
case EAGAIN:
#ifndef TRY_AGAIN
-#define TRY_AGAIN
+#define TRY_AGAIN
#endif
#endif /* EWOULDBLOCK && EWOULDBLOCK != EAGAIN */
#endif /* POSIX && EAGAIN */
@@ -150,10 +168,10 @@ read__fixio(int fd __attribute__((__unused__)), int e)
#ifdef TRY_AGAIN
#if defined(F_SETFL) && defined(O_NDELAY)
if ((e = fcntl(fd, F_GETFL, 0)) == -1)
- return (-1);
+ return -1;
if (fcntl(fd, F_SETFL, e & ~O_NDELAY) == -1)
- return (-1);
+ return -1;
else
e = 1;
#endif /* F_SETFL && O_NDELAY */
@@ -162,95 +180,62 @@ read__fixio(int fd __attribute__((__unused__)), int e)
{
int zero = 0;
- if (ioctl(fd, FIONBIO, (ioctl_t) & zero) == -1)
- return (-1);
+ if (ioctl(fd, FIONBIO, &zero) == -1)
+ return -1;
else
e = 1;
}
#endif /* FIONBIO */
#endif /* TRY_AGAIN */
- return (e ? 0 : -1);
+ return e ? 0 : -1;
case EINTR:
- return (0);
+ return 0;
default:
- return (-1);
- }
-}
-
-
-/* read_preread():
- * Try to read the stuff in the input queue;
- */
-private int
-read_preread(EditLine *el)
-{
- int chrs = 0;
-
- if (el->el_tty.t_mode == ED_IO)
- return (0);
-
-#ifndef WIDECHAR
-/* FIONREAD attempts to buffer up multiple bytes, and to make that work
- * properly with partial wide/UTF-8 characters would need some careful work. */
-#ifdef FIONREAD
- (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
- if (chrs > 0) {
- char buf[EL_BUFSIZ];
-
- chrs = read(el->el_infd, buf,
- (size_t) MIN(chrs, EL_BUFSIZ - 1));
- if (chrs > 0) {
- buf[chrs] = '\0';
- el_push(el, buf);
- }
+ return -1;
}
-#endif /* FIONREAD */
-#endif
- return (chrs > 0);
}
/* el_push():
* Push a macro
*/
-public void
-FUN(el,push)(EditLine *el, const Char *str)
+void
+el_wpush(EditLine *el, const wchar_t *str)
{
- c_macro_t *ma = &el->el_chared.c_macro;
+ struct macros *ma = &el->el_read->macros;
if (str != NULL && ma->level + 1 < EL_MAXMACRO) {
ma->level++;
- if ((ma->macro[ma->level] = Strdup(str)) != NULL)
+ if ((ma->macro[ma->level] = wcsdup(str)) != NULL)
return;
ma->level--;
}
- term_beep(el);
- term__flush(el);
+ terminal_beep(el);
+ terminal__flush(el);
}
/* read_getcmd():
- * Return next command from the input stream.
+ * Get next command from the input stream,
+ * return 0 on success or -1 on EOF or error.
* Character values > 255 are not looked up in the map, but inserted.
*/
-private int
-read_getcmd(EditLine *el, el_action_t *cmdnum, Char *ch)
+static int
+read_getcmd(EditLine *el, el_action_t *cmdnum, wchar_t *ch)
{
+ static const wchar_t meta = (wchar_t)0x80;
el_action_t cmd;
int num;
- el->el_errno = 0;
do {
- if ((num = FUN(el,getc)(el, ch)) != 1) {/* if EOF or error */
- el->el_errno = num == 0 ? 0 : errno;
- return (num);
- }
+ if ((num = el_wgetc(el, ch)) != 1)
+ return -1;
#ifdef KANJI
- if ((*ch & 0200)) {
+ if ((*ch & meta)) {
el->el_state.metanext = 0;
cmd = CcViMap[' '];
break;
@@ -259,71 +244,52 @@ read_getcmd(EditLine *el, el_action_t *cmdnum, Char *ch)
if (el->el_state.metanext) {
el->el_state.metanext = 0;
- *ch |= 0200;
+ *ch |= meta;
}
-#ifdef WIDECHAR
- if (*ch >= N_KEYS)
- cmd = ED_INSERT;
+ if (*ch >= N_KEYS)
+ cmd = ED_INSERT;
else
-#endif
- cmd = el->el_map.current[(unsigned char) *ch];
+ cmd = el->el_map.current[(unsigned char) *ch];
if (cmd == ED_SEQUENCE_LEAD_IN) {
- key_value_t val;
- switch (key_get(el, ch, &val)) {
+ keymacro_value_t val;
+ switch (keymacro_get(el, ch, &val)) {
case XK_CMD:
cmd = val.cmd;
break;
case XK_STR:
- FUN(el,push)(el, val.str);
+ el_wpush(el, val.str);
break;
-#ifdef notyet
- case XK_EXE:
- /* XXX: In the future to run a user function */
- RunCommand(val.str);
- break;
-#endif
+ case XK_NOD:
+ return -1;
default:
EL_ABORT((el->el_errfile, "Bad XK_ type \n"));
break;
}
}
- if (el->el_map.alt == NULL)
- el->el_map.current = el->el_map.key;
} while (cmd == ED_SEQUENCE_LEAD_IN);
*cmdnum = cmd;
- return (OKCMD);
-}
-
-#ifdef WIDECHAR
-/* utf8_islead():
- * Test whether a byte is a leading byte of a UTF-8 sequence.
- */
-private int
-utf8_islead(unsigned char c)
-{
- return (c < 0x80) || /* single byte char */
- (c >= 0xc2 && c <= 0xf4); /* start of multibyte sequence */
+ return 0;
}
-#endif
/* read_char():
* Read a character from the tty.
*/
-private int
-read_char(EditLine *el, Char *cp)
+static int
+read_char(EditLine *el, wchar_t *cp)
{
ssize_t num_read;
int tried = 0;
- char cbuf[MB_LEN_MAX];
- int cbp = 0;
- int bytes = 0;
+ char cbuf[MB_LEN_MAX];
+ size_t cbp = 0;
+ int save_errno = errno;
again:
el->el_signal->sig_no = 0;
- while ((num_read = read(el->el_infd, cbuf + cbp, 1)) == -1) {
+ while ((num_read = read(el->el_infd, cbuf + cbp, (size_t)1)) == -1) {
+ int e = errno;
switch (el->el_signal->sig_no) {
case SIGCONT:
- el_set(el, EL_REFRESH);
+ el_wset(el, EL_REFRESH);
/*FALLTHROUGH*/
case SIGWINCH:
sig_set(el);
@@ -331,44 +297,69 @@ read_char(EditLine *el, Char *cp)
default:
break;
}
- if (!tried && read__fixio(el->el_infd, errno) == 0)
+ if (!tried && read__fixio(el->el_infd, e) == 0) {
+ errno = save_errno;
tried = 1;
- else {
- *cp = '\0';
- return (-1);
+ } else {
+ errno = e;
+ *cp = L'\0';
+ return -1;
}
}
-#ifdef WIDECHAR
- if (el->el_flags & CHARSET_IS_UTF8) {
- if (!utf8_islead((unsigned char)cbuf[0]))
- goto again; /* discard the byte we read and try again */
+ /* Test for EOF */
+ if (num_read == 0) {
+ *cp = L'\0';
+ return 0;
+ }
+
+ for (;;) {
+ mbstate_t mbs;
+
++cbp;
- if ((bytes = ct_mbtowc(cp, cbuf, cbp)) == -1) {
- ct_mbtowc_reset;
- if (cbp >= MB_LEN_MAX) { /* "shouldn't happen" */
- *cp = '\0';
- return (-1);
+ /* This only works because UTF8 is stateless. */
+ memset(&mbs, 0, sizeof(mbs));
+ switch (mbrtowc(cp, cbuf, cbp, &mbs)) {
+ case (size_t)-1:
+ if (cbp > 1) {
+ /*
+ * Invalid sequence, discard all bytes
+ * except the last one.
+ */
+ cbuf[0] = cbuf[cbp - 1];
+ cbp = 0;
+ break;
+ } else {
+ /* Invalid byte, discard it. */
+ cbp = 0;
+ goto again;
}
+ case (size_t)-2:
+ /*
+ * We don't support other multibyte charsets.
+ * The second condition shouldn't happen
+ * and is here merely for additional safety.
+ */
+ if ((el->el_flags & CHARSET_IS_UTF8) == 0 ||
+ cbp >= MB_LEN_MAX) {
+ errno = EILSEQ;
+ *cp = L'\0';
+ return -1;
+ }
+ /* Incomplete sequence, read another byte. */
goto again;
+ default:
+ /* Valid character, process it. */
+ return 1;
}
- } else /* we don't support other multibyte charsets */
-#endif
- *cp = (unsigned char)cbuf[0];
-
- if ((el->el_flags & IGNORE_EXTCHARS) && bytes > 1) {
- cbp = 0; /* skip this character */
- goto again;
}
-
- return (int)num_read;
}
/* read_pop():
* Pop a macro from the stack
*/
-private void
-read_pop(c_macro_t *ma)
+static void
+read_pop(struct macros *ma)
{
int i;
@@ -379,22 +370,25 @@ read_pop(c_macro_t *ma)
ma->offset = 0;
}
-/* el_getc():
- * Read a character
+static void
+read_clearmacros(struct macros *ma)
+{
+ while (ma->level >= 0)
+ el_free(ma->macro[ma->level--]);
+ ma->offset = 0;
+}
+
+/* el_wgetc():
+ * Read a wide character
*/
-public int
-FUN(el,getc)(EditLine *el, Char *cp)
+int
+el_wgetc(EditLine *el, wchar_t *cp)
{
+ struct macros *ma = &el->el_read->macros;
int num_read;
- c_macro_t *ma = &el->el_chared.c_macro;
- term__flush(el);
+ terminal__flush(el);
for (;;) {
- if (ma->level < 0) {
- if (!read_preread(el))
- break;
- }
-
if (ma->level < 0)
break;
@@ -410,30 +404,26 @@ FUN(el,getc)(EditLine *el, Char *cp)
read_pop(ma);
}
- return (1);
+ return 1;
}
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile, "Turning raw mode on\n");
-#endif /* DEBUG_READ */
if (tty_rawmode(el) < 0)/* make sure the tty is set up correctly */
- return (0);
-
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile, "Reading a character\n");
-#endif /* DEBUG_READ */
- num_read = (*el->el_read.read_char)(el, cp);
-#ifdef WIDECHAR
- if (el->el_flags & NARROW_READ)
- *cp = *(char *)(void *)cp;
-#endif
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile, "Got it %c\n", *cp);
-#endif /* DEBUG_READ */
- return (num_read);
+ return 0;
+
+ num_read = (*el->el_read->read_char)(el, cp);
+
+ /*
+ * Remember the original reason of a read failure
+ * such that el_wgets() can restore it after doing
+ * various cleanup operation that might change errno.
+ */
+ if (num_read < 0)
+ el->el_read->read_errno = errno;
+
+ return num_read;
}
-protected void
+libedit_private void
read_prepare(EditLine *el)
{
if (el->el_flags & HANDLE_SIGNALS)
@@ -447,14 +437,14 @@ read_prepare(EditLine *el)
we have the wrong size. */
el_resize(el);
re_clear_display(el); /* reset the display stuff */
- ch_reset(el, 0);
+ ch_reset(el);
re_refresh(el); /* print the prompt */
if (el->el_flags & UNBUFFERED)
- term__flush(el);
+ terminal__flush(el);
}
-protected void
+libedit_private void
read_finish(EditLine *el)
{
if ((el->el_flags & UNBUFFERED) == 0)
@@ -463,61 +453,59 @@ read_finish(EditLine *el)
sig_clr(el);
}
-public const Char *
-FUN(el,gets)(EditLine *el, int *nread)
+static const wchar_t *
+noedit_wgets(EditLine *el, int *nread)
+{
+ el_line_t *lp = &el->el_line;
+ int num;
+
+ while ((num = (*el->el_read->read_char)(el, lp->lastchar)) == 1) {
+ if (lp->lastchar + 1 >= lp->limit &&
+ !ch_enlargebufs(el, (size_t)2))
+ break;
+ lp->lastchar++;
+ if (el->el_flags & UNBUFFERED ||
+ lp->lastchar[-1] == '\r' ||
+ lp->lastchar[-1] == '\n')
+ break;
+ }
+ if (num == -1 && errno == EINTR)
+ lp->lastchar = lp->buffer;
+ lp->cursor = lp->lastchar;
+ *lp->lastchar = '\0';
+ *nread = (int)(lp->lastchar - lp->buffer);
+ return *nread ? lp->buffer : NULL;
+}
+
+const wchar_t *
+el_wgets(EditLine *el, int *nread)
{
int retval;
el_action_t cmdnum = 0;
int num; /* how many chars we have read at NL */
- Char ch, *cp;
- int crlf = 0;
+ wchar_t ch;
int nrb;
-#ifdef FIONREAD
- c_macro_t *ma = &el->el_chared.c_macro;
-#endif /* FIONREAD */
if (nread == NULL)
nread = &nrb;
*nread = 0;
+ el->el_read->read_errno = 0;
if (el->el_flags & NO_TTY) {
- size_t idx;
-
- cp = el->el_line.buffer;
- while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
- /* make sure there is space for next character */
- if (cp + 1 >= el->el_line.limit) {
- idx = (cp - el->el_line.buffer);
- if (!ch_enlargebufs(el, 2))
- break;
- cp = &el->el_line.buffer[idx];
- }
- cp++;
- if (el->el_flags & UNBUFFERED)
- break;
- if (cp[-1] == '\r' || cp[-1] == '\n')
- break;
- }
- if (num == -1) {
- if (errno == EINTR)
- cp = el->el_line.buffer;
- el->el_errno = errno;
- }
-
- goto noedit;
+ el->el_line.lastchar = el->el_line.buffer;
+ return noedit_wgets(el, nread);
}
-
#ifdef FIONREAD
- if (el->el_tty.t_mode == EX_IO && ma->level < 0) {
- long chrs = 0;
+ if (el->el_tty.t_mode == EX_IO && el->el_read->macros.level < 0) {
+ int chrs = 0;
- (void) ioctl(el->el_infd, FIONREAD, (ioctl_t) & chrs);
+ (void) ioctl(el->el_infd, FIONREAD, &chrs);
if (chrs == 0) {
if (tty_rawmode(el) < 0) {
errno = 0;
*nread = 0;
- return (NULL);
+ return NULL;
}
}
}
@@ -527,81 +515,19 @@ FUN(el,gets)(EditLine *el, int *nread)
read_prepare(el);
if (el->el_flags & EDIT_DISABLED) {
- size_t idx;
-
if ((el->el_flags & UNBUFFERED) == 0)
- cp = el->el_line.buffer;
- else
- cp = el->el_line.lastchar;
-
- term__flush(el);
-
- while ((num = (*el->el_read.read_char)(el, cp)) == 1) {
- /* make sure there is space next character */
- if (cp + 1 >= el->el_line.limit) {
- idx = (cp - el->el_line.buffer);
- if (!ch_enlargebufs(el, 2))
- break;
- cp = &el->el_line.buffer[idx];
- }
- cp++;
- crlf = cp[-1] == '\r' || cp[-1] == '\n';
- if (el->el_flags & UNBUFFERED)
- break;
- if (crlf)
- break;
- }
-
- if (num == -1) {
- if (errno == EINTR)
- cp = el->el_line.buffer;
- el->el_errno = errno;
- }
-
- goto noedit;
+ el->el_line.lastchar = el->el_line.buffer;
+ terminal__flush(el);
+ return noedit_wgets(el, nread);
}
- for (num = OKCMD; num == OKCMD;) { /* while still editing this
- * line */
-#ifdef DEBUG_EDIT
- read_debug(el);
-#endif /* DEBUG_EDIT */
+ for (num = -1; num == -1;) { /* while still editing this line */
/* if EOF or error */
- if ((num = read_getcmd(el, &cmdnum, &ch)) != OKCMD) {
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "Returning from el_gets %d\n", num);
-#endif /* DEBUG_READ */
+ if (read_getcmd(el, &cmdnum, &ch) == -1)
break;
- }
- if (el->el_errno == EINTR) {
- el->el_line.buffer[0] = '\0';
- el->el_line.lastchar =
- el->el_line.cursor = el->el_line.buffer;
- break;
- }
- if ((unsigned int)cmdnum >= (unsigned int)el->el_map.nfunc) { /* BUG CHECK command */
-#ifdef DEBUG_EDIT
- (void) fprintf(el->el_errfile,
- "ERROR: illegal command from key 0%o\r\n", ch);
-#endif /* DEBUG_EDIT */
+ if ((size_t)cmdnum >= el->el_map.nfunc) /* BUG CHECK command */
continue; /* try again */
- }
/* now do the real command */
-#ifdef DEBUG_READ
- {
- el_bindings_t *b;
- for (b = el->el_map.help; b->name; b++)
- if (b->func == cmdnum)
- break;
- if (b->name)
- (void) fprintf(el->el_errfile,
- "Executing %s\n", b->name);
- else
- (void) fprintf(el->el_errfile,
- "Error command = %d\n", cmdnum);
- }
-#endif /* DEBUG_READ */
/* vi redo needs these way down the levels... */
el->el_state.thiscmd = cmdnum;
el->el_state.thisch = ch;
@@ -610,16 +536,12 @@ FUN(el,gets)(EditLine *el, int *nread)
el->el_chared.c_redo.pos < el->el_chared.c_redo.lim) {
if (cmdnum == VI_DELETE_PREV_CHAR &&
el->el_chared.c_redo.pos != el->el_chared.c_redo.buf
- && Isprint(el->el_chared.c_redo.pos[-1]))
+ && iswprint(el->el_chared.c_redo.pos[-1]))
el->el_chared.c_redo.pos--;
else
*el->el_chared.c_redo.pos++ = ch;
}
retval = (*el->el_map.func[cmdnum]) (el, ch);
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "Returned state %d\n", retval );
-#endif /* DEBUG_READ */
/* save the last command here */
el->el_state.lastcmd = cmdnum;
@@ -641,7 +563,7 @@ FUN(el,gets)(EditLine *el, int *nread)
case CC_REFRESH_BEEP:
re_refresh(el);
- term_beep(el);
+ terminal_beep(el);
break;
case CC_NORM: /* normal char */
@@ -666,24 +588,17 @@ FUN(el,gets)(EditLine *el, int *nread)
break;
case CC_FATAL: /* fatal error, reset to known state */
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "*** editor fatal ERROR ***\r\n\n");
-#endif /* DEBUG_READ */
/* put (real) cursor in a known place */
re_clear_display(el); /* reset the display stuff */
- ch_reset(el, 1); /* reset the input pointers */
- re_refresh(el); /* print the prompt again */
+ ch_reset(el); /* reset the input pointers */
+ read_clearmacros(&el->el_read->macros);
+ re_refresh(el); /* print the prompt again */
break;
case CC_ERROR:
default: /* functions we don't know about */
-#ifdef DEBUG_READ
- (void) fprintf(el->el_errfile,
- "*** editor ERROR ***\r\n\n");
-#endif /* DEBUG_READ */
- term_beep(el);
- term__flush(el);
+ terminal_beep(el);
+ terminal__flush(el);
break;
}
el->el_state.argument = 1;
@@ -693,24 +608,19 @@ FUN(el,gets)(EditLine *el, int *nread)
break;
}
- term__flush(el); /* flush any buffered output */
+ terminal__flush(el); /* flush any buffered output */
/* make sure the tty is set up correctly */
if ((el->el_flags & UNBUFFERED) == 0) {
read_finish(el);
*nread = num != -1 ? num : 0;
- } else {
+ } else
*nread = (int)(el->el_line.lastchar - el->el_line.buffer);
- }
- goto done;
-noedit:
- el->el_line.cursor = el->el_line.lastchar = cp;
- *cp = '\0';
- *nread = (int)(el->el_line.cursor - el->el_line.buffer);
-done:
+
if (*nread == 0) {
if (num == -1) {
*nread = -1;
- errno = el->el_errno;
+ if (el->el_read->read_errno)
+ errno = el->el_read->read_errno;
}
return NULL;
} else
diff --git a/lib/libedit/src/read.h b/lib/libedit/src/read.h
index 1ded7c1cf2a0..1acf5d67637e 100644
--- a/lib/libedit/src/read.h
+++ b/lib/libedit/src/read.h
@@ -1,4 +1,4 @@
-/* $NetBSD: read.h,v 1.7 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: read.h,v 1.12 2016/05/22 19:44:26 christos Exp $ */
/*-
* Copyright (c) 2001 The NetBSD Foundation, Inc.
@@ -35,16 +35,11 @@
#ifndef _h_el_read
#define _h_el_read
-typedef int (*el_rfunc_t)(EditLine *, Char *);
-
-typedef struct el_read_t {
- el_rfunc_t read_char; /* Function to read a character */
-} el_read_t;
-
-protected int read_init(EditLine *);
-protected void read_prepare(EditLine *);
-protected void read_finish(EditLine *);
-protected int el_read_setfn(EditLine *, el_rfunc_t);
-protected el_rfunc_t el_read_getfn(EditLine *);
+libedit_private int read_init(EditLine *);
+libedit_private void read_end(struct el_read_t *);
+libedit_private void read_prepare(EditLine *);
+libedit_private void read_finish(EditLine *);
+libedit_private int el_read_setfn(struct el_read_t *, el_rfunc_t);
+libedit_private el_rfunc_t el_read_getfn(struct el_read_t *);
#endif /* _h_el_read */
diff --git a/lib/libedit/src/readline.c b/lib/libedit/src/readline.c
index aeb6c46f41e5..f4e0a39e3925 100644
--- a/lib/libedit/src/readline.c
+++ b/lib/libedit/src/readline.c
@@ -1,4 +1,4 @@
-/* $NetBSD: readline.c,v 1.92 2010/09/16 20:08:51 christos Exp $ */
+/* $NetBSD: readline.c,v 1.139 2016/10/28 18:32:26 christos Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -31,33 +31,31 @@
#include "config.h"
#if !defined(lint) && !defined(SCCSID)
-__RCSID("$NetBSD: readline.c,v 1.92 2010/09/16 20:08:51 christos Exp $");
+__RCSID("$NetBSD: readline.c,v 1.139 2016/10/28 18:32:26 christos Exp $");
#endif /* not lint && not SCCSID */
+#include "config.h"
+
#include <sys/types.h>
#include <sys/stat.h>
-#include <stdio.h>
-#include <dirent.h>
-#include <string.h>
-#include <pwd.h>
#include <ctype.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <limits.h>
+#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
#include <vis.h>
-#include "editline/readline.h"
+
+#include "readline/readline.h"
#include "el.h"
-#include "fcns.h" /* for EL_NUM_FCNS */
-#include "histedit.h"
+#include "fcns.h"
#include "filecomplete.h"
-#if !defined(SIZE_T_MAX)
-# define SIZE_T_MAX (size_t)(-1)
-#endif
-
void rl_prep_terminal(int);
void rl_deprep_terminal(void);
@@ -81,15 +79,24 @@ FILE *rl_outstream = NULL;
int rl_point = 0;
int rl_end = 0;
char *rl_line_buffer = NULL;
-VCPFunction *rl_linefunc = NULL;
+rl_vcpfunc_t *rl_linefunc = NULL;
int rl_done = 0;
VFunction *rl_event_hook = NULL;
KEYMAP_ENTRY_ARRAY emacs_standard_keymap,
emacs_meta_keymap,
emacs_ctlx_keymap;
+/*
+ * The following is not implemented; we always catch signals in the
+ * libedit fashion: set handlers on entry to el_gets() and clear them
+ * on the way out. This simplistic approach works for most cases; if
+ * it does not work for your application, please let us know.
+ */
+int rl_catch_signals = 1;
+int rl_catch_sigwinch = 1;
int history_base = 1; /* probably never subject to change */
int history_length = 0;
+int history_offset = 0;
int max_input_history = 0;
char history_expansion_char = '!';
char history_subst_char = '^';
@@ -102,8 +109,9 @@ int rl_attempted_completion_over = 0;
char *rl_basic_word_break_characters = break_chars;
char *rl_completer_word_break_characters = NULL;
char *rl_completer_quote_characters = NULL;
-Function *rl_completion_entry_function = NULL;
-CPPFunction *rl_attempted_completion_function = NULL;
+rl_compentry_func_t *rl_completion_entry_function = NULL;
+char *(*rl_completion_word_break_hook)(void) = NULL;
+rl_completion_func_t *rl_attempted_completion_function = NULL;
Function *rl_pre_input_hook = NULL;
Function *rl_startup1_hook = NULL;
int (*rl_getc_function)(FILE *) = NULL;
@@ -111,7 +119,6 @@ char *rl_terminal_name = NULL;
int rl_already_prompted = 0;
int rl_filename_completion_desired = 0;
int rl_ignore_completion_duplicates = 0;
-int rl_catch_signals = 1;
int readline_echoing_p = 1;
int _rl_print_completions_horizontally = 0;
VFunction *rl_redisplay_function = NULL;
@@ -155,48 +162,29 @@ int rl_completion_append_character = ' ';
static History *h = NULL;
static EditLine *e = NULL;
-static Function *map[256];
+static rl_command_func_t *map[256];
static jmp_buf topbuf;
/* internal functions */
static unsigned char _el_rl_complete(EditLine *, int);
static unsigned char _el_rl_tstp(EditLine *, int);
static char *_get_prompt(EditLine *);
-static int _getc_function(EditLine *, char *);
-static HIST_ENTRY *_move_history(int);
+static int _getc_function(EditLine *, wchar_t *);
static int _history_expand_command(const char *, size_t, size_t,
char **);
static char *_rl_compat_sub(const char *, const char *,
const char *, int);
-static int _rl_event_read_char(EditLine *, char *);
+static int _rl_event_read_char(EditLine *, wchar_t *);
static void _rl_update_pos(void);
+static HIST_ENTRY rl_he;
/* ARGSUSED */
static char *
_get_prompt(EditLine *el __attribute__((__unused__)))
{
rl_already_prompted = 1;
- return (rl_prompt);
-}
-
-
-/*
- * generic function for moving around history
- */
-static HIST_ENTRY *
-_move_history(int op)
-{
- HistEvent ev;
- static HIST_ENTRY rl_he;
-
- if (history(h, &ev, op) != 0)
- return (HIST_ENTRY *) NULL;
-
- rl_he.line = ev.str;
- rl_he.data = NULL;
-
- return (&rl_he);
+ return rl_prompt;
}
@@ -205,14 +193,14 @@ _move_history(int op)
*/
static int
/*ARGSUSED*/
-_getc_function(EditLine *el, char *c)
+_getc_function(EditLine *el __attribute__((__unused__)), wchar_t *c)
{
int i;
- i = (*rl_getc_function)(NULL);
+ i = (*rl_getc_function)(rl_instream);
if (i == -1)
return 0;
- *c = i;
+ *c = (wchar_t)i;
return 1;
}
@@ -224,23 +212,27 @@ _resize_fun(EditLine *el, void *a)
li = el_line(el);
/* a cheesy way to get rid of const cast. */
- *ap = memchr(li->buffer, *li->buffer, 1);
+ *ap = memchr(li->buffer, *li->buffer, (size_t)1);
}
-static const char _dothistory[] = "/.history";
-
static const char *
_default_history_file(void)
{
struct passwd *p;
- static char path[PATH_MAX];
+ static char *path;
+ size_t len;
- if (*path)
+ if (path)
return path;
+
if ((p = getpwuid(getuid())) == NULL)
return NULL;
- strlcpy(path, p->pw_dir, PATH_MAX);
- strlcat(path, _dothistory, PATH_MAX);
+
+ len = strlen(p->pw_dir) + sizeof("/.history");
+ if ((path = malloc(len)) == NULL)
+ return NULL;
+
+ (void)snprintf(path, len, "%s/.history", p->pw_dir);
return path;
}
@@ -261,7 +253,7 @@ rl_set_prompt(const char *prompt)
if (rl_prompt != NULL && strcmp(rl_prompt, prompt) == 0)
return 0;
if (rl_prompt)
- free(rl_prompt);
+ el_free(rl_prompt);
rl_prompt = strdup(prompt);
if (rl_prompt == NULL)
return -1;
@@ -305,7 +297,7 @@ rl_initialize(void)
h = history_init();
if (!e || !h)
- return (-1);
+ return -1;
history(h, &ev, H_SETSIZE, INT_MAX); /* unlimited */
history_length = 0;
@@ -329,7 +321,7 @@ rl_initialize(void)
el_set(e, EL_SIGNAL, rl_catch_signals);
/* set default mode to "emacs"-style and read setting afterwards */
- /* so this can be overriden */
+ /* so this can be overridden */
el_set(e, EL_EDITOR, "emacs");
if (rl_terminal_name != NULL)
el_set(e, EL_TERMINAL, rl_terminal_name);
@@ -353,6 +345,37 @@ rl_initialize(void)
_el_rl_tstp);
el_set(e, EL_BIND, "^Z", "rl_tstp", NULL);
+ /*
+ * Set some readline compatible key-bindings.
+ */
+ el_set(e, EL_BIND, "^R", "em-inc-search-prev", NULL);
+
+ /*
+ * Allow the use of Home/End keys.
+ */
+ el_set(e, EL_BIND, "\\e[1~", "ed-move-to-beg", NULL);
+ el_set(e, EL_BIND, "\\e[4~", "ed-move-to-end", NULL);
+ el_set(e, EL_BIND, "\\e[7~", "ed-move-to-beg", NULL);
+ el_set(e, EL_BIND, "\\e[8~", "ed-move-to-end", NULL);
+ el_set(e, EL_BIND, "\\e[H", "ed-move-to-beg", NULL);
+ el_set(e, EL_BIND, "\\e[F", "ed-move-to-end", NULL);
+
+ /*
+ * Allow the use of the Delete/Insert keys.
+ */
+ el_set(e, EL_BIND, "\\e[3~", "ed-delete-next-char", NULL);
+ el_set(e, EL_BIND, "\\e[2~", "ed-quoted-insert", NULL);
+
+ /*
+ * Ctrl-left-arrow and Ctrl-right-arrow for word moving.
+ */
+ el_set(e, EL_BIND, "\\e[1;5C", "em-next-word", NULL);
+ el_set(e, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL);
+ el_set(e, EL_BIND, "\\e[5C", "em-next-word", NULL);
+ el_set(e, EL_BIND, "\\e[5D", "ed-prev-word", NULL);
+ el_set(e, EL_BIND, "\\e\\e[C", "em-next-word", NULL);
+ el_set(e, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL);
+
/* read settings from configuration file */
el_source(e, NULL);
@@ -366,7 +389,7 @@ rl_initialize(void)
if (rl_startup_hook)
(*rl_startup_hook)(NULL, 0);
- return (0);
+ return 0;
}
@@ -444,6 +467,7 @@ using_history(void)
{
if (h == NULL || e == NULL)
rl_initialize();
+ history_offset = history_length;
}
@@ -475,7 +499,7 @@ _rl_compat_sub(const char *str, const char *what, const char *with,
} else
s++;
}
- r = result = malloc(len + 1);
+ r = result = el_malloc((len + 1) * sizeof(*r));
if (result == NULL)
return NULL;
s = str;
@@ -486,13 +510,13 @@ _rl_compat_sub(const char *str, const char *what, const char *with,
s += what_len;
if (!globally) {
(void)strcpy(r, s);
- return(result);
+ return result;
}
} else
*r++ = *s++;
}
*r = '\0';
- return(result);
+ return result;
}
static char *last_search_pat; /* last !?pat[?] search pattern */
@@ -509,12 +533,12 @@ get_history_event(const char *cmd, int *cindex, int qchar)
idx = *cindex;
if (cmd[idx++] != history_expansion_char)
- return(NULL);
+ return NULL;
/* find out which event to take */
if (cmd[idx] == history_expansion_char || cmd[idx] == '\0') {
if (history(h, &ev, H_FIRST) != 0)
- return(NULL);
+ return NULL;
*cindex = cmd[idx]? (idx + 1):idx;
return ev.str;
}
@@ -525,7 +549,7 @@ get_history_event(const char *cmd, int *cindex, int qchar)
}
if ('0' <= cmd[idx] && cmd[idx] <= '9') {
- HIST_ENTRY *rl_he;
+ HIST_ENTRY *he;
num = 0;
while (cmd[idx] && '0' <= cmd[idx] && cmd[idx] <= '9') {
@@ -535,11 +559,11 @@ get_history_event(const char *cmd, int *cindex, int qchar)
if (sign)
num = history_length - num + 1;
- if (!(rl_he = history_get(num)))
- return(NULL);
+ if (!(he = history_get(num)))
+ return NULL;
*cindex = idx;
- return(rl_he->line);
+ return he->line;
}
sub = 0;
if (cmd[idx] == '?') {
@@ -557,15 +581,15 @@ get_history_event(const char *cmd, int *cindex, int qchar)
break;
idx++;
}
- len = idx - begin;
+ len = (size_t)idx - (size_t)begin;
if (sub && cmd[idx] == '?')
idx++;
if (sub && len == 0 && last_search_pat && *last_search_pat)
pat = last_search_pat;
else if (len == 0)
- return(NULL);
+ return NULL;
else {
- if ((pat = malloc(len + 1)) == NULL)
+ if ((pat = el_malloc((len + 1) * sizeof(*pat))) == NULL)
return NULL;
(void)strncpy(pat, cmd + begin, len);
pat[len] = '\0';
@@ -573,15 +597,15 @@ get_history_event(const char *cmd, int *cindex, int qchar)
if (history(h, &ev, H_CURR) != 0) {
if (pat != last_search_pat)
- free(pat);
- return (NULL);
+ el_free(pat);
+ return NULL;
}
num = ev.num;
if (sub) {
if (pat != last_search_pat) {
if (last_search_pat)
- free(last_search_pat);
+ el_free(last_search_pat);
last_search_pat = pat;
}
ret = history_search(pat, -1);
@@ -593,21 +617,21 @@ get_history_event(const char *cmd, int *cindex, int qchar)
history(h, &ev, H_FIRST);
(void)fprintf(rl_outstream, "%s: Event not found\n", pat);
if (pat != last_search_pat)
- free(pat);
- return(NULL);
+ el_free(pat);
+ return NULL;
}
if (sub && len) {
if (last_search_match && last_search_match != pat)
- free(last_search_match);
+ el_free(last_search_match);
last_search_match = pat;
}
if (pat != last_search_pat)
- free(pat);
+ el_free(pat);
if (history(h, &ev, H_CURR) != 0)
- return(NULL);
+ return NULL;
*cindex = idx;
rptr = ev.str;
@@ -625,7 +649,7 @@ get_history_event(const char *cmd, int *cindex, int qchar)
* returns 0 if data was not modified, 1 if it was and 2 if the string
* should be only printed and not executed; in case of error,
* returns -1 and *result points to NULL
- * it's callers responsibility to free() string returned in *result
+ * it's the caller's responsibility to free() the string returned in *result
*/
static int
_history_expand_command(const char *command, size_t offs, size_t cmdlen,
@@ -659,7 +683,8 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
} else {
if (command[offs + 1] == '#') {
/* use command so far */
- if ((aptr = malloc(offs + 1)) == NULL)
+ if ((aptr = el_malloc((offs + 1) * sizeof(*aptr)))
+ == NULL)
return -1;
(void)strncpy(aptr, command, offs);
aptr[offs] = '\0';
@@ -670,19 +695,19 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
qchar = (offs > 0 && command[offs - 1] == '"')? '"':0;
ptr = get_history_event(command + offs, &idx, qchar);
}
- has_mods = command[offs + idx] == ':';
+ has_mods = command[offs + (size_t)idx] == ':';
}
if (ptr == NULL && aptr == NULL)
- return(-1);
+ return -1;
if (!has_mods) {
*result = strdup(aptr ? aptr : ptr);
if (aptr)
- free(aptr);
+ el_free(aptr);
if (*result == NULL)
return -1;
- return(1);
+ return 1;
}
cmd = command + offs + idx + 1;
@@ -727,18 +752,18 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
(void)fprintf(rl_outstream, "%s: Bad word specifier",
command + offs + idx);
if (aptr)
- free(aptr);
- return(-1);
+ el_free(aptr);
+ return -1;
}
} else
tmp = strdup(aptr? aptr:ptr);
if (aptr)
- free(aptr);
+ el_free(aptr);
if (*cmd == '\0' || ((size_t)(cmd - (command + offs)) >= cmdlen)) {
*result = tmp;
- return(1);
+ return 1;
}
for (; *cmd; cmd++) {
@@ -750,7 +775,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
} else if (*cmd == 't') { /* remove leading path */
if ((aptr = strrchr(tmp, '/')) != NULL) {
aptr = strdup(aptr + 1);
- free(tmp);
+ el_free(tmp);
tmp = aptr;
}
} else if (*cmd == 'r') { /* remove trailing suffix */
@@ -759,7 +784,7 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
} else if (*cmd == 'e') { /* remove all but suffix */
if ((aptr = strrchr(tmp, '.')) != NULL) {
aptr = strdup(aptr);
- free(tmp);
+ el_free(tmp);
tmp = aptr;
}
} else if (*cmd == 'p') /* print only */
@@ -776,10 +801,10 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
else if (*cmd == 's') {
delim = *(++cmd), cmd++;
size = 16;
- what = realloc(from, size);
+ what = el_realloc(from, size * sizeof(*what));
if (what == NULL) {
- free(from);
- free(tmp);
+ el_free(from);
+ el_free(tmp);
return 0;
}
len = 0;
@@ -788,11 +813,12 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
cmd++;
if (len >= size) {
char *nwhat;
- nwhat = realloc(what,
- (size <<= 1));
+ nwhat = el_realloc(what,
+ (size <<= 1) *
+ sizeof(*nwhat));
if (nwhat == NULL) {
- free(what);
- free(tmp);
+ el_free(what);
+ el_free(tmp);
return 0;
}
what = nwhat;
@@ -802,17 +828,17 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
what[len] = '\0';
from = what;
if (*what == '\0') {
- free(what);
+ el_free(what);
if (search) {
from = strdup(search);
if (from == NULL) {
- free(tmp);
+ el_free(tmp);
return 0;
}
} else {
from = NULL;
- free(tmp);
- return (-1);
+ el_free(tmp);
+ return -1;
}
}
cmd++; /* shift after delim */
@@ -820,10 +846,10 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
continue;
size = 16;
- with = realloc(to, size);
+ with = el_realloc(to, size * sizeof(*with));
if (with == NULL) {
- free(to);
- free(tmp);
+ el_free(to);
+ el_free(tmp);
return -1;
}
len = 0;
@@ -832,10 +858,11 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
if (len + from_len + 1 >= size) {
char *nwith;
size += from_len + 1;
- nwith = realloc(with, size);
+ nwith = el_realloc(with,
+ size * sizeof(*nwith));
if (nwith == NULL) {
- free(with);
- free(tmp);
+ el_free(with);
+ el_free(tmp);
return -1;
}
with = nwith;
@@ -858,14 +885,14 @@ _history_expand_command(const char *command, size_t offs, size_t cmdlen,
aptr = _rl_compat_sub(tmp, from, to, g_on);
if (aptr) {
- free(tmp);
+ el_free(tmp);
tmp = aptr;
}
g_on = 0;
}
}
*result = tmp;
- return (p_on? 2:1);
+ return p_on? 2:1;
}
@@ -884,13 +911,13 @@ history_expand(char *str, char **output)
if (history_expansion_char == 0) {
*output = strdup(str);
- return(0);
+ return 0;
}
*output = NULL;
if (str[0] == history_subst_char) {
/* ^foo^foo2^ is equivalent to !!:s^foo^foo2^ */
- *output = malloc(strlen(str) + 4 + 1);
+ *output = el_malloc((strlen(str) + 4 + 1) * sizeof(**output));
if (*output == NULL)
return 0;
(*output)[0] = (*output)[1] = history_expansion_char;
@@ -907,11 +934,12 @@ history_expand(char *str, char **output)
#define ADD_STRING(what, len, fr) \
{ \
if (idx + len + 1 > size) { \
- char *nresult = realloc(result, (size += len + 1));\
+ char *nresult = el_realloc(result, \
+ (size += len + 1) * sizeof(*nresult)); \
if (nresult == NULL) { \
- free(*output); \
+ el_free(*output); \
if (/*CONSTCOND*/fr) \
- free(tmp); \
+ el_free(tmp); \
return 0; \
} \
result = nresult; \
@@ -935,7 +963,8 @@ loop:
for (; str[j]; j++) {
if (str[j] == '\\' &&
str[j + 1] == history_expansion_char) {
- (void)strcpy(&str[j], &str[j + 1]);
+ len = strlen(&str[j + 1]) + 1;
+ memmove(&str[j], &str[j + 1], len);
continue;
}
if (!loop_again) {
@@ -978,7 +1007,7 @@ loop:
ADD_STRING(tmp, len, 1);
}
if (tmp) {
- free(tmp);
+ el_free(tmp);
tmp = NULL;
}
i = j;
@@ -995,10 +1024,10 @@ loop:
ret = -1;
#endif
}
- free(*output);
+ el_free(*output);
*output = result;
- return (ret);
+ return ret;
}
/*
@@ -1033,14 +1062,14 @@ history_arg_extract(int start, int end, const char *str)
(size_t)end > max || start > end)
goto out;
- for (i = start, len = 0; i <= (size_t)end; i++)
+ for (i = (size_t)start, len = 0; i <= (size_t)end; i++)
len += strlen(arr[i]) + 1;
len++;
- result = malloc(len);
+ result = el_malloc(len * sizeof(*result));
if (result == NULL)
goto out;
- for (i = start, len = 0; i <= (size_t)end; i++) {
+ for (i = (size_t)start, len = 0; i <= (size_t)end; i++) {
(void)strcpy(result + len, arr[i]);
len += strlen(arr[i]);
if (i < (size_t)end)
@@ -1050,8 +1079,8 @@ history_arg_extract(int start, int end, const char *str)
out:
for (i = 0; arr[i]; i++)
- free(arr[i]);
- free(arr);
+ el_free(arr[i]);
+ el_free(arr);
return result;
}
@@ -1090,19 +1119,19 @@ history_tokenize(const char *str)
if (idx + 2 >= size) {
char **nresult;
size <<= 1;
- nresult = realloc(result, size * sizeof(char *));
+ nresult = el_realloc(result, (size_t)size * sizeof(*nresult));
if (nresult == NULL) {
- free(result);
+ el_free(result);
return NULL;
}
result = nresult;
}
- len = i - start;
- temp = malloc(len + 1);
+ len = (size_t)i - (size_t)start;
+ temp = el_malloc((size_t)(len + 1) * sizeof(*temp));
if (temp == NULL) {
for (i = 0; i < idx; i++)
- free(result[i]);
- free(result);
+ el_free(result[i]);
+ el_free(result);
return NULL;
}
(void)strncpy(temp, &str[start], len);
@@ -1112,7 +1141,7 @@ history_tokenize(const char *str)
if (str[i])
i++;
}
- return (result);
+ return result;
}
@@ -1123,12 +1152,22 @@ void
stifle_history(int max)
{
HistEvent ev;
+ HIST_ENTRY *he;
if (h == NULL || e == NULL)
rl_initialize();
- if (history(h, &ev, H_SETSIZE, max) == 0)
+ if (history(h, &ev, H_SETSIZE, max) == 0) {
max_input_history = max;
+ if (history_length > max)
+ history_base = history_length - max;
+ while (history_length > max) {
+ he = remove_history(0);
+ el_free(he->data);
+ el_free((void *)(unsigned long)he->line);
+ el_free(he);
+ }
+ }
}
@@ -1144,7 +1183,7 @@ unstifle_history(void)
history(h, &ev, H_SETSIZE, INT_MAX);
omax = max_input_history;
max_input_history = INT_MAX;
- return (omax); /* some value _must_ be returned */
+ return omax; /* some value _must_ be returned */
}
@@ -1153,7 +1192,7 @@ history_is_stifled(void)
{
/* cannot return true answer */
- return (max_input_history != INT_MAX);
+ return max_input_history != INT_MAX;
}
static const char _history_tmp_template[] = "/tmp/.historyXXXXXX";
@@ -1188,7 +1227,7 @@ history_truncate_file (const char *filename, int nlines)
}
for(;;) {
- if (fread(buf, sizeof(buf), 1, fp) != 1) {
+ if (fread(buf, sizeof(buf), (size_t)1, fp) != 1) {
if (ferror(fp)) {
ret = errno;
break;
@@ -1198,7 +1237,7 @@ history_truncate_file (const char *filename, int nlines)
ret = errno;
break;
}
- left = fread(buf, 1, sizeof(buf), fp);
+ left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), fp);
if (ferror(fp)) {
ret = errno;
break;
@@ -1206,14 +1245,15 @@ history_truncate_file (const char *filename, int nlines)
if (left == 0) {
count--;
left = sizeof(buf);
- } else if (fwrite(buf, (size_t)left, 1, tp) != 1) {
+ } else if (fwrite(buf, (size_t)left, (size_t)1, tp)
+ != 1) {
ret = errno;
break;
}
fflush(tp);
break;
}
- if (fwrite(buf, sizeof(buf), 1, tp) != 1) {
+ if (fwrite(buf, sizeof(buf), (size_t)1, tp) != 1) {
ret = errno;
break;
}
@@ -1243,7 +1283,7 @@ history_truncate_file (const char *filename, int nlines)
ret = errno;
break;
}
- if (fread(buf, sizeof(buf), 1, tp) != 1) {
+ if (fread(buf, sizeof(buf), (size_t)1, tp) != 1) {
if (ferror(tp)) {
ret = errno;
break;
@@ -1257,7 +1297,7 @@ history_truncate_file (const char *filename, int nlines)
if (ret || nlines > 0)
goto out3;
- if (fseeko(fp, 0, SEEK_SET) == (off_t)-1) {
+ if (fseeko(fp, (off_t)0, SEEK_SET) == (off_t)-1) {
ret = errno;
goto out3;
}
@@ -1269,12 +1309,12 @@ history_truncate_file (const char *filename, int nlines)
}
for(;;) {
- if ((left = fread(buf, 1, sizeof(buf), tp)) == 0) {
+ if ((left = (ssize_t)fread(buf, (size_t)1, sizeof(buf), tp)) == 0) {
if (ferror(fp))
ret = errno;
break;
}
- if (fwrite(buf, (size_t)left, 1, fp) != 1) {
+ if (fwrite(buf, (size_t)left, (size_t)1, fp) != 1) {
ret = errno;
break;
}
@@ -1305,8 +1345,8 @@ read_history(const char *filename)
rl_initialize();
if (filename == NULL && (filename = _default_history_file()) == NULL)
return errno;
- return (history(h, &ev, H_LOAD, filename) == -1 ?
- (errno ? errno : EINVAL) : 0);
+ return history(h, &ev, H_LOAD, filename) == -1 ?
+ (errno ? errno : EINVAL) : 0;
}
@@ -1322,8 +1362,8 @@ write_history(const char *filename)
rl_initialize();
if (filename == NULL && (filename = _default_history_file()) == NULL)
return errno;
- return (history(h, &ev, H_SAVE, filename) == -1 ?
- (errno ? errno : EINVAL) : 0);
+ return history(h, &ev, H_SAVE, filename) == -1 ?
+ (errno ? errno : EINVAL) : 0;
}
@@ -1342,25 +1382,37 @@ history_get(int num)
if (h == NULL || e == NULL)
rl_initialize();
+ if (num < history_base)
+ return NULL;
+
/* save current position */
if (history(h, &ev, H_CURR) != 0)
- return (NULL);
+ return NULL;
curr_num = ev.num;
- /* start from the oldest */
- if (history(h, &ev, H_LAST) != 0)
- return (NULL); /* error */
-
- /* look forwards for event matching specified offset */
- if (history(h, &ev, H_NEXT_EVDATA, num, &she.data))
- return (NULL);
+ /*
+ * use H_DELDATA to set to nth history (without delete) by passing
+ * (void **)-1 -- as in history_set_pos
+ */
+ if (history(h, &ev, H_DELDATA, num - history_base, (void **)-1) != 0)
+ goto out;
+ /* get current entry */
+ if (history(h, &ev, H_CURR) != 0)
+ goto out;
+ if (history(h, &ev, H_NEXT_EVDATA, ev.num, &she.data) != 0)
+ goto out;
she.line = ev.str;
/* restore pointer to where it was */
(void)history(h, &ev, H_SET, curr_num);
- return (&she);
+ return &she;
+
+out:
+ /* restore pointer to where it was */
+ (void)history(h, &ev, H_SET, curr_num);
+ return NULL;
}
@@ -1375,11 +1427,15 @@ add_history(const char *line)
if (h == NULL || e == NULL)
rl_initialize();
- (void)history(h, &ev, H_ENTER, line);
- if (history(h, &ev, H_GETSIZE) == 0)
- history_length = ev.num;
+ if (history(h, &ev, H_ENTER, line) == -1)
+ return 0;
- return (!(history_length > 0)); /* return 0 if all is okay */
+ (void)history(h, &ev, H_GETSIZE);
+ if (ev.num == history_length)
+ history_base++;
+ else
+ history_length = ev.num;
+ return 0;
}
@@ -1395,11 +1451,11 @@ remove_history(int num)
if (h == NULL || e == NULL)
rl_initialize();
- if ((he = malloc(sizeof(*he))) == NULL)
+ if ((he = el_malloc(sizeof(*he))) == NULL)
return NULL;
if (history(h, &ev, H_DELDATA, num, &he->data) != 0) {
- free(he);
+ el_free(he);
return NULL;
}
@@ -1433,7 +1489,7 @@ replace_history_entry(int num, const char *line, histdata_t data)
if (history(h, &ev, H_LAST) != 0)
return NULL; /* error */
- if ((he = malloc(sizeof(*he))) == NULL)
+ if ((he = el_malloc(sizeof(*he))) == NULL)
return NULL;
/* look forwards for event matching specified offset */
@@ -1453,7 +1509,7 @@ replace_history_entry(int num, const char *line, histdata_t data)
return he;
out:
- free(he);
+ el_free(he);
return NULL;
}
@@ -1465,8 +1521,11 @@ clear_history(void)
{
HistEvent ev;
+ if (h == NULL || e == NULL)
+ rl_initialize();
+
(void)history(h, &ev, H_CLEAR);
- history_length = 0;
+ history_offset = history_length = 0;
}
@@ -1476,21 +1535,42 @@ clear_history(void)
int
where_history(void)
{
+ return history_offset;
+}
+
+static HIST_ENTRY **_history_listp;
+static HIST_ENTRY *_history_list;
+
+HIST_ENTRY **
+history_list(void)
+{
HistEvent ev;
- int curr_num, off;
+ HIST_ENTRY **nlp, *nl;
+ int i;
- if (history(h, &ev, H_CURR) != 0)
- return (0);
- curr_num = ev.num;
+ if (history(h, &ev, H_LAST) != 0)
+ return NULL;
- (void)history(h, &ev, H_FIRST);
- off = 1;
- while (ev.num != curr_num && history(h, &ev, H_NEXT) == 0)
- off++;
+ if ((nlp = el_realloc(_history_listp,
+ (size_t)history_length * sizeof(*nlp))) == NULL)
+ return NULL;
+ _history_listp = nlp;
- return (off);
-}
+ if ((nl = el_realloc(_history_list,
+ (size_t)history_length * sizeof(*nl))) == NULL)
+ return NULL;
+ _history_list = nl;
+ i = 0;
+ do {
+ _history_listp[i] = &_history_list[i];
+ _history_list[i].line = ev.str;
+ _history_list[i].data = NULL;
+ if (i++ == history_length)
+ abort();
+ } while (history(h, &ev, H_PREV) == 0);
+ return _history_listp;
+}
/*
* returns current history event or NULL if there is no such event
@@ -1498,8 +1578,14 @@ where_history(void)
HIST_ENTRY *
current_history(void)
{
+ HistEvent ev;
+
+ if (history(h, &ev, H_PREV_EVENT, history_offset + 1) != 0)
+ return NULL;
- return (_move_history(H_CURR));
+ rl_he.line = ev.str;
+ rl_he.data = NULL;
+ return &rl_he;
}
@@ -1514,7 +1600,7 @@ history_total_bytes(void)
size_t size;
if (history(h, &ev, H_CURR) != 0)
- return (-1);
+ return -1;
curr_num = ev.num;
(void)history(h, &ev, H_FIRST);
@@ -1526,7 +1612,7 @@ history_total_bytes(void)
/* get to the same position as before */
history(h, &ev, H_PREV_EVENT, curr_num);
- return (int)(size);
+ return (int)size;
}
@@ -1536,35 +1622,31 @@ history_total_bytes(void)
int
history_set_pos(int pos)
{
- HistEvent ev;
- int curr_num;
-
if (pos >= history_length || pos < 0)
- return (-1);
-
- (void)history(h, &ev, H_CURR);
- curr_num = ev.num;
+ return 0;
- /*
- * use H_DELDATA to set to nth history (without delete) by passing
- * (void **)-1
- */
- if (history(h, &ev, H_DELDATA, pos, (void **)-1)) {
- (void)history(h, &ev, H_SET, curr_num);
- return(-1);
- }
- return (0);
+ history_offset = pos;
+ return 1;
}
/*
* returns previous event in history and shifts pointer accordingly
+ * Note that readline and editline define directions in opposite ways.
*/
HIST_ENTRY *
previous_history(void)
{
+ HistEvent ev;
+
+ if (history_offset == 0)
+ return NULL;
- return (_move_history(H_PREV));
+ if (history(h, &ev, H_LAST) != 0)
+ return NULL;
+
+ history_offset--;
+ return current_history();
}
@@ -1574,8 +1656,16 @@ previous_history(void)
HIST_ENTRY *
next_history(void)
{
+ HistEvent ev;
+
+ if (history_offset >= history_length)
+ return NULL;
+
+ if (history(h, &ev, H_LAST) != 0)
+ return NULL;
- return (_move_history(H_NEXT));
+ history_offset++;
+ return current_history();
}
@@ -1590,17 +1680,17 @@ history_search(const char *str, int direction)
int curr_num;
if (history(h, &ev, H_CURR) != 0)
- return (-1);
+ return -1;
curr_num = ev.num;
for (;;) {
if ((strp = strstr(ev.str, str)) != NULL)
- return (int) (strp - ev.str);
+ return (int)(strp - ev.str);
if (history(h, &ev, direction < 0 ? H_NEXT:H_PREV) != 0)
break;
}
(void)history(h, &ev, H_SET, curr_num);
- return (-1);
+ return -1;
}
@@ -1633,15 +1723,15 @@ history_search_pos(const char *str,
pos = (pos > 0) ? 1 : -1;
if (history(h, &ev, H_CURR) != 0)
- return (-1);
+ return -1;
curr_num = ev.num;
- if (history_set_pos(off) != 0 || history(h, &ev, H_CURR) != 0)
- return (-1);
+ if (!history_set_pos(off) || history(h, &ev, H_CURR) != 0)
+ return -1;
for (;;) {
if (strstr(ev.str, str))
- return (off);
+ return off;
if (history(h, &ev, (pos < 0) ? H_PREV : H_NEXT) != 0)
break;
}
@@ -1650,7 +1740,7 @@ history_search_pos(const char *str,
(void)history(h, &ev,
pos < 0 ? H_NEXT_EVENT : H_PREV_EVENT, curr_num);
- return (-1);
+ return -1;
}
@@ -1673,16 +1763,20 @@ filename_completion_function(const char *name, int state)
* a completion generator for usernames; returns _first_ username
* which starts with supplied text
* text contains a partial username preceded by random character
- * (usually '~'); state is ignored
- * it's callers responsibility to free returned value
+ * (usually '~'); state resets search from start (??? should we do that anyway)
+ * it's the caller's responsibility to free the returned value
*/
char *
username_completion_function(const char *text, int state)
{
- struct passwd *pwd;
+#if defined(HAVE_GETPW_R_POSIX) || defined(HAVE_GETPW_R_DRAFT)
+ struct passwd pwres;
+ char pwbuf[1024];
+#endif
+ struct passwd *pass = NULL;
if (text[0] == '\0')
- return (NULL);
+ return NULL;
if (*text == '~')
text++;
@@ -1690,15 +1784,17 @@ username_completion_function(const char *text, int state)
if (state == 0)
setpwent();
- while ((pwd = getpwent())
- && pwd != NULL && text[0] == pwd->pw_name[0]
- && strcmp(text, pwd->pw_name) == 0);
+ while (
+ (pass = getpwent()) != NULL
+ && text[0] == pass->pw_name[0]
+ && strcmp(text, pass->pw_name) == 0)
+ continue;
- if (pwd == NULL) {
+ if (pass == NULL) {
endpwent();
return NULL;
}
- return strdup(pwd->pw_name);
+ return strdup(pass->pw_name);
}
@@ -1731,7 +1827,7 @@ _rl_completion_append_character_function(const char *dummy
__attribute__((__unused__)))
{
static char buf[2];
- buf[0] = rl_completion_append_character;
+ buf[0] = (char)rl_completion_append_character;
buf[1] = '\0';
return buf;
}
@@ -1744,9 +1840,8 @@ _rl_completion_append_character_function(const char *dummy
int
rl_complete(int ignore __attribute__((__unused__)), int invoking_key)
{
-#ifdef WIDECHAR
static ct_buffer_t wbreak_conv, sprefix_conv;
-#endif
+ char *breakchars;
if (h == NULL || e == NULL)
rl_initialize();
@@ -1756,15 +1851,22 @@ rl_complete(int ignore __attribute__((__unused__)), int invoking_key)
arr[0] = (char)invoking_key;
arr[1] = '\0';
el_insertstr(e, arr);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
+ if (rl_completion_word_break_hook != NULL)
+ breakchars = (*rl_completion_word_break_hook)();
+ else
+ breakchars = rl_basic_word_break_characters;
+
+ _rl_update_pos();
+
/* Just look at how many global variables modify this operation! */
return fn_complete(e,
- (CPFunction *)rl_completion_entry_function,
+ (rl_compentry_func_t *)rl_completion_entry_function,
rl_attempted_completion_function,
ct_decode_string(rl_basic_word_break_characters, &wbreak_conv),
- ct_decode_string(rl_special_prefixes, &sprefix_conv),
+ ct_decode_string(breakchars, &sprefix_conv),
_rl_completion_append_character_function,
(size_t)rl_completion_query_items,
&rl_completion_type, &rl_attempted_completion_over,
@@ -1801,7 +1903,7 @@ rl_bind_key(int c, rl_command_func_t *func)
e->el_map.key[c] = ED_INSERT;
retval = 0;
}
- return (retval);
+ return retval;
}
@@ -1817,7 +1919,7 @@ rl_read_key(void)
if (e == NULL || h == NULL)
rl_initialize();
- return (el_getc(e, fooarr));
+ return el_getc(e, fooarr);
}
@@ -1847,32 +1949,33 @@ rl_insert(int count, int c)
rl_initialize();
/* XXX - int -> char conversion can lose on multichars */
- arr[0] = c;
+ arr[0] = (char)c;
arr[1] = '\0';
for (; count > 0; count--)
el_push(e, arr);
- return (0);
+ return 0;
}
int
rl_insert_text(const char *text)
{
if (!text || *text == 0)
- return (0);
+ return 0;
if (h == NULL || e == NULL)
rl_initialize();
if (el_insertstr(e, text) < 0)
- return (0);
+ return 0;
return (int)strlen(text);
}
/*ARGSUSED*/
int
-rl_newline(int count, int c)
+rl_newline(int count __attribute__((__unused__)),
+ int c __attribute__((__unused__)))
{
/*
* Readline-4.0 appears to ignore the args.
@@ -1882,14 +1985,14 @@ rl_newline(int count, int c)
/*ARGSUSED*/
static unsigned char
-rl_bind_wrapper(EditLine *el, unsigned char c)
+rl_bind_wrapper(EditLine *el __attribute__((__unused__)), unsigned char c)
{
if (map[c] == NULL)
return CC_ERROR;
_rl_update_pos();
- (*map[c])(NULL, c);
+ (*map[c])(1, c);
/* If rl_done was set by the above call, deal with it here */
if (rl_done)
@@ -1899,7 +2002,7 @@ rl_bind_wrapper(EditLine *el, unsigned char c)
}
int
-rl_add_defun(const char *name, Function *fun, int c)
+rl_add_defun(const char *name, rl_command_func_t *fun, int c)
{
char dest[8];
if ((size_t)c >= sizeof(map) / sizeof(map[0]) || c < 0)
@@ -1907,12 +2010,12 @@ rl_add_defun(const char *name, Function *fun, int c)
map[(unsigned char)c] = fun;
el_set(e, EL_ADDFN, name, name, rl_bind_wrapper);
vis(dest, c, VIS_WHITE|VIS_NOSLASH, 0);
- el_set(e, EL_BIND, dest, name);
+ el_set(e, EL_BIND, dest, name, NULL);
return 0;
}
void
-rl_callback_read_char()
+rl_callback_read_char(void)
{
int count = 0, done = 0;
const char *buf = el_gets(e, &count);
@@ -1933,12 +2036,12 @@ rl_callback_read_char()
} else
wbuf = NULL;
(*(void (*)(const char *))rl_linefunc)(wbuf);
- //el_set(e, EL_UNBUFFERED, 1);
+ el_set(e, EL_UNBUFFERED, 1);
}
}
void
-rl_callback_handler_install(const char *prompt, VCPFunction *linefunc)
+rl_callback_handler_install(const char *prompt, rl_vcpfunc_t *linefunc)
{
if (e == NULL) {
rl_initialize();
@@ -1959,7 +2062,7 @@ void
rl_redisplay(void)
{
char a[2];
- a[0] = e->el_tty.t_c[TS_IO][C_REPRINT];
+ a[0] = (char)e->el_tty.t_c[TS_IO][C_REPRINT];
a[1] = '\0';
el_push(e, a);
}
@@ -1968,7 +2071,7 @@ int
rl_get_previous_history(int count, int key)
{
char a[2];
- a[0] = key;
+ a[0] = (char)key;
a[1] = '\0';
while (count--)
el_push(e, a);
@@ -1977,7 +2080,7 @@ rl_get_previous_history(int count, int key)
void
/*ARGSUSED*/
-rl_prep_terminal(int meta_flag)
+rl_prep_terminal(int meta_flag __attribute__((__unused__)))
{
el_set(e, EL_PREP_TERM, 1);
}
@@ -1991,7 +2094,7 @@ rl_deprep_terminal(void)
int
rl_read_init_file(const char *s)
{
- return(el_source(e, s));
+ return el_source(e, s);
}
int
@@ -2005,7 +2108,7 @@ rl_parse_and_bind(const char *line)
tok_str(tok, line, &argc, &argv);
argc = el_parse(e, argc, argv);
tok_end(tok);
- return (argc ? 1 : 0);
+ return argc ? 1 : 0;
}
int
@@ -2015,7 +2118,7 @@ rl_variable_bind(const char *var, const char *value)
* The proper return value is undocument, but this is what the
* readline source seems to do.
*/
- return ((el_set(e, EL_BIND, "", var, value) == -1) ? 1 : 0);
+ return el_set(e, EL_BIND, "", var, value, NULL) == -1 ? 1 : 0;
}
void
@@ -2023,41 +2126,43 @@ rl_stuff_char(int c)
{
char buf[2];
- buf[0] = c;
+ buf[0] = (char)c;
buf[1] = '\0';
el_insertstr(e, buf);
}
static int
-_rl_event_read_char(EditLine *el, char *cp)
+_rl_event_read_char(EditLine *el, wchar_t *wc)
{
+ char ch;
int n;
ssize_t num_read = 0;
- *cp = '\0';
+ ch = '\0';
+ *wc = L'\0';
while (rl_event_hook) {
(*rl_event_hook)();
#if defined(FIONREAD)
if (ioctl(el->el_infd, FIONREAD, &n) < 0)
- return(-1);
+ return -1;
if (n)
- num_read = read(el->el_infd, cp, 1);
+ num_read = read(el->el_infd, &ch, (size_t)1);
else
num_read = 0;
#elif defined(F_SETFL) && defined(O_NDELAY)
if ((n = fcntl(el->el_infd, F_GETFL, 0)) < 0)
- return(-1);
+ return -1;
if (fcntl(el->el_infd, F_SETFL, n|O_NDELAY) < 0)
- return(-1);
- num_read = read(el->el_infd, cp, 1);
+ return -1;
+ num_read = read(el->el_infd, &ch, 1);
if (fcntl(el->el_infd, F_SETFL, n))
- return(-1);
+ return -1;
#else
/* not non-blocking, but what you gonna do? */
- num_read = read(el->el_infd, cp, 1);
- return(-1);
+ num_read = read(el->el_infd, &ch, 1);
+ return -1;
#endif
if (num_read < 0 && errno == EAGAIN)
@@ -2068,6 +2173,7 @@ _rl_event_read_char(EditLine *el, char *cp)
}
if (!rl_event_hook)
el_set(el, EL_GETCFN, EL_BUILTIN_GETCFN);
+ *wc = (wchar_t)ch;
return (int)num_read;
}
@@ -2084,9 +2190,9 @@ void
rl_get_screen_size(int *rows, int *cols)
{
if (rows)
- el_get(e, EL_GETTC, "li", rows);
+ el_get(e, EL_GETTC, "li", rows, (void *)0);
if (cols)
- el_get(e, EL_GETTC, "co", cols);
+ el_get(e, EL_GETTC, "co", cols, (void *)0);
}
void
@@ -2094,9 +2200,9 @@ rl_set_screen_size(int rows, int cols)
{
char buf[64];
(void)snprintf(buf, sizeof(buf), "%d", rows);
- el_set(e, EL_SETTC, "li", buf);
+ el_set(e, EL_SETTC, "li", buf, NULL);
(void)snprintf(buf, sizeof(buf), "%d", cols);
- el_set(e, EL_SETTC, "co", buf);
+ el_set(e, EL_SETTC, "co", buf, NULL);
}
char **
@@ -2107,7 +2213,7 @@ rl_completion_matches(const char *str, rl_compentry_func_t *fun)
len = 1;
max = 10;
- if ((list = malloc(max * sizeof(*list))) == NULL)
+ if ((list = el_malloc(max * sizeof(*list))) == NULL)
return NULL;
while ((match = (*fun)(str, (int)(len - 1))) != NULL) {
@@ -2115,7 +2221,7 @@ rl_completion_matches(const char *str, rl_compentry_func_t *fun)
if (len == max) {
char **nl;
max += 10;
- if ((nl = realloc(list, max * sizeof(*nl))) == NULL)
+ if ((nl = el_realloc(list, max * sizeof(*nl))) == NULL)
goto out;
list = nl;
}
@@ -2130,7 +2236,7 @@ rl_completion_matches(const char *str, rl_compentry_func_t *fun)
}
qsort(&list[1], len - 1, sizeof(*list),
(int (*)(const void *, const void *)) strcmp);
- min = SIZE_T_MAX;
+ min = SIZE_MAX;
for (i = 1, a = list[i]; i < len - 1; i++, a = b) {
b = list[i + 1];
for (j = 0; a[j] && a[j] == b[j]; j++)
@@ -2142,7 +2248,7 @@ rl_completion_matches(const char *str, rl_compentry_func_t *fun)
if ((list[0] = strdup(str)) == NULL)
goto out;
} else {
- if ((list[0] = malloc(min + 1)) == NULL)
+ if ((list[0] = el_malloc((min + 1) * sizeof(*list[0]))) == NULL)
goto out;
(void)memcpy(list[0], list[1], min);
list[0][min] = '\0';
@@ -2150,7 +2256,7 @@ rl_completion_matches(const char *str, rl_compentry_func_t *fun)
return list;
out:
- free(list);
+ el_free(list);
return NULL;
}
@@ -2185,15 +2291,16 @@ history_get_history_state(void)
{
HISTORY_STATE *hs;
- if ((hs = malloc(sizeof(HISTORY_STATE))) == NULL)
- return (NULL);
+ if ((hs = el_malloc(sizeof(*hs))) == NULL)
+ return NULL;
hs->length = history_length;
- return (hs);
+ return hs;
}
int
/*ARGSUSED*/
-rl_kill_text(int from, int to)
+rl_kill_text(int from __attribute__((__unused__)),
+ int to __attribute__((__unused__)))
{
return 0;
}
@@ -2212,20 +2319,25 @@ rl_get_keymap(void)
void
/*ARGSUSED*/
-rl_set_keymap(Keymap k)
+rl_set_keymap(Keymap k __attribute__((__unused__)))
{
}
int
/*ARGSUSED*/
-rl_generic_bind(int type, const char * keyseq, const char * data, Keymap k)
+rl_generic_bind(int type __attribute__((__unused__)),
+ const char * keyseq __attribute__((__unused__)),
+ const char * data __attribute__((__unused__)),
+ Keymap k __attribute__((__unused__)))
{
return 0;
}
int
/*ARGSUSED*/
-rl_bind_key_in_map(int key, rl_command_func_t *fun, Keymap k)
+rl_bind_key_in_map(int key __attribute__((__unused__)),
+ rl_command_func_t *fun __attribute__((__unused__)),
+ Keymap k __attribute__((__unused__)))
{
return 0;
}
@@ -2241,3 +2353,15 @@ rl_on_new_line(void)
{
return 0;
}
+
+void
+rl_free_line_state(void)
+{
+}
+
+int
+/*ARGSUSED*/
+rl_set_keyboard_input_timeout(int u __attribute__((__unused__)))
+{
+ return 0;
+}
diff --git a/lib/libedit/src/editline/readline.h b/lib/libedit/src/readline/readline.h
index b36d405e0ab3..79ae115aae4a 100644
--- a/lib/libedit/src/editline/readline.h
+++ b/lib/libedit/src/readline/readline.h
@@ -1,4 +1,4 @@
-/* $NetBSD: readline.h,v 1.32 2010/09/16 20:08:52 christos Exp $ */
+/* $NetBSD: readline.h,v 1.41 2016/10/28 18:32:35 christos Exp $ */
/*-
* Copyright (c) 1997 The NetBSD Foundation, Inc.
@@ -39,9 +39,8 @@
/* typedefs */
typedef int Function(const char *, int);
typedef void VFunction(void);
-typedef void VCPFunction(char *);
-typedef char *CPFunction(const char *, int);
-typedef char **CPPFunction(const char *, int, int);
+typedef void rl_vcpfunc_t(char *);
+typedef char **rl_completion_func_t(const char *, int, int);
typedef char *rl_compentry_func_t(const char *, int);
typedef int rl_command_func_t(int, int);
@@ -54,7 +53,7 @@ typedef void *histdata_t;
typedef struct _hist_entry {
const char *line;
- histdata_t data;
+ histdata_t data;
} HIST_ENTRY;
typedef struct _keymap_entry {
@@ -75,7 +74,7 @@ typedef KEYMAP_ENTRY *Keymap;
#ifndef CTRL
#include <sys/ioctl.h>
-#if !defined(__sun) && !defined(__hpux) && !defined(_AIX)
+#if !defined(__sun) && !defined(__hpux) && !defined(_AIX) && !defined(_CYGWIN_)
#include <sys/ttydefaults.h>
#endif
#ifndef CTRL
@@ -88,7 +87,7 @@ typedef KEYMAP_ENTRY *Keymap;
#define RUBOUT 0x7f
#define ABORT_CHAR CTRL('G')
-#define RL_READLINE_VERSION 0x0402
+#define RL_READLINE_VERSION 0x0402
#define RL_PROMPT_START_IGNORE '\1'
#define RL_PROMPT_END_IGNORE '\2'
@@ -97,7 +96,7 @@ typedef KEYMAP_ENTRY *Keymap;
extern "C" {
#endif
extern const char *rl_library_version;
-extern int rl_readline_version;
+extern int rl_readline_version;
extern char *rl_readline_name;
extern FILE *rl_instream;
extern FILE *rl_outstream;
@@ -108,8 +107,9 @@ extern int max_input_history;
extern char *rl_basic_word_break_characters;
extern char *rl_completer_word_break_characters;
extern char *rl_completer_quote_characters;
-extern Function *rl_completion_entry_function;
-extern CPPFunction *rl_attempted_completion_function;
+extern rl_compentry_func_t *rl_completion_entry_function;
+extern char *(*rl_completion_word_break_hook)(void);
+extern rl_completion_func_t *rl_attempted_completion_function;
extern int rl_attempted_completion_over;
extern int rl_completion_type;
extern int rl_completion_query_items;
@@ -121,9 +121,12 @@ extern Function *rl_startup_hook;
extern char *rl_terminal_name;
extern int rl_already_prompted;
extern char *rl_prompt;
+extern int rl_done;
/*
* The following is not implemented
*/
+extern int rl_catch_signals;
+extern int rl_catch_sigwinch;
extern KEYMAP_ENTRY_ARRAY emacs_standard_keymap,
emacs_meta_keymap,
emacs_ctlx_keymap;
@@ -156,6 +159,7 @@ int history_total_bytes(void);
int history_set_pos(int);
HIST_ENTRY *previous_history(void);
HIST_ENTRY *next_history(void);
+HIST_ENTRY **history_list(void);
int history_search(const char *, int);
int history_search_prefix(const char *, int);
int history_search_pos(const char *, int, int);
@@ -172,7 +176,7 @@ char *filename_completion_function(const char *, int);
char *username_completion_function(const char *, int);
int rl_complete(int, int);
int rl_read_key(void);
-char **completion_matches(const char *, CPFunction *);
+char **completion_matches(const char *, rl_compentry_func_t *);
void rl_display_match_list(char **, int, int);
int rl_insert(int, int);
@@ -181,7 +185,7 @@ void rl_reset_terminal(const char *);
int rl_bind_key(int, rl_command_func_t *);
int rl_newline(int, int);
void rl_callback_read_char(void);
-void rl_callback_handler_install(const char *, VCPFunction *);
+void rl_callback_handler_install(const char *, rl_vcpfunc_t *);
void rl_callback_handler_remove(void);
void rl_redisplay(void);
int rl_get_previous_history(int, int);
@@ -191,14 +195,14 @@ int rl_read_init_file(const char *);
int rl_parse_and_bind(const char *);
int rl_variable_bind(const char *, const char *);
void rl_stuff_char(int);
-int rl_add_defun(const char *, Function *, int);
+int rl_add_defun(const char *, rl_command_func_t *, int);
HISTORY_STATE *history_get_history_state(void);
void rl_get_screen_size(int *, int *);
void rl_set_screen_size(int, int);
-char *rl_filename_completion_function (const char *, int);
+char *rl_filename_completion_function (const char *, int);
int _rl_abort_internal(void);
int _rl_qsort_string_compare(char **, char **);
-char **rl_completion_matches(const char *, rl_compentry_func_t *);
+char **rl_completion_matches(const char *, rl_compentry_func_t *);
void rl_forced_update_display(void);
int rl_set_prompt(const char *);
int rl_on_new_line(void);
@@ -214,6 +218,8 @@ int rl_generic_bind(int, const char *, const char *, Keymap);
int rl_bind_key_in_map(int, rl_command_func_t *, Keymap);
void rl_cleanup_after_signal(void);
void rl_free_line_state(void);
+int rl_set_keyboard_input_timeout(int);
+
#ifdef __cplusplus
}
#endif
diff --git a/lib/libedit/src/refresh.c b/lib/libedit/src/refresh.c
index 7f35ce1536bb..e7775b0e4d48 100644
--- a/lib/libedit/src/refresh.c
+++ b/lib/libedit/src/refresh.c
@@ -1,4 +1,4 @@
-/* $NetBSD: refresh.c,v 1.35 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: refresh.c,v 1.51 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)refresh.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: refresh.c,v 1.35 2009/12/30 22:37:40 christos Exp $");
+__RCSID("$NetBSD: refresh.c,v 1.51 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -45,26 +45,25 @@ __RCSID("$NetBSD: refresh.c,v 1.35 2009/12/30 22:37:40 christos Exp $");
* refresh.c: Lower level screen refreshing functions
*/
#include <stdio.h>
-#include <ctype.h>
-#include <unistd.h>
#include <string.h>
+#include <unistd.h>
#include "el.h"
-private void re_nextline(EditLine *);
-private void re_addc(EditLine *, Int);
-private void re_update_line(EditLine *, Char *, Char *, int);
-private void re_insert (EditLine *, Char *, int, int, Char *, int);
-private void re_delete(EditLine *, Char *, int, int, int);
-private void re_fastputc(EditLine *, Int);
-private void re_clear_eol(EditLine *, int, int, int);
-private void re__strncopy(Char *, Char *, size_t);
-private void re__copy_and_pad(Char *, const Char *, size_t);
+static void re_nextline(EditLine *);
+static void re_addc(EditLine *, wint_t);
+static void re_update_line(EditLine *, wchar_t *, wchar_t *, int);
+static void re_insert (EditLine *, wchar_t *, int, int, wchar_t *, int);
+static void re_delete(EditLine *, wchar_t *, int, int, int);
+static void re_fastputc(EditLine *, wint_t);
+static void re_clear_eol(EditLine *, int, int, int);
+static void re__strncopy(wchar_t *, wchar_t *, size_t);
+static void re__copy_and_pad(wchar_t *, const wchar_t *, size_t);
#ifdef DEBUG_REFRESH
-private void re_printstr(EditLine *, const char *, char *, char *);
+static void re_printstr(EditLine *, const char *, wchar_t *, wchar_t *);
#define __F el->el_errfile
-#define ELRE_ASSERT(a, b, c) do \
+#define ELRE_ASSERT(a, b, c) do \
if (/*CONSTCOND*/ a) { \
(void) fprintf b; \
c; \
@@ -75,8 +74,8 @@ private void re_printstr(EditLine *, const char *, char *, char *);
/* re_printstr():
* Print a string on the debugging pty
*/
-private void
-re_printstr(EditLine *el, const char *str, char *f, char *t)
+static void
+re_printstr(EditLine *el, const char *str, wchar_t *f, wchar_t *t)
{
ELRE_DEBUG(1, (__F, "%s:\"", str));
@@ -92,7 +91,7 @@ re_printstr(EditLine *el, const char *str, char *f, char *t)
/* re_nextline():
* Move to the next line or scroll
*/
-private void
+static void
re_nextline(EditLine *el)
{
el->el_refresh.r_cursor.h = 0; /* reset it. */
@@ -103,9 +102,9 @@ re_nextline(EditLine *el)
* We do this via pointer shuffling - it's safe in this case
* and we avoid memcpy().
*/
- if (el->el_refresh.r_cursor.v + 1 >= el->el_term.t_size.v) {
- int i, lins = el->el_term.t_size.v;
- Char *firstline = el->el_vdisplay[0];
+ if (el->el_refresh.r_cursor.v + 1 >= el->el_terminal.t_size.v) {
+ int i, lins = el->el_terminal.t_size.v;
+ wchar_t *firstline = el->el_vdisplay[0];
for(i = 1; i < lins; i++)
el->el_vdisplay[i - 1] = el->el_vdisplay[i];
@@ -115,19 +114,19 @@ re_nextline(EditLine *el)
} else
el->el_refresh.r_cursor.v++;
- ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_term.t_size.v,
+ ELRE_ASSERT(el->el_refresh.r_cursor.v >= el->el_terminal.t_size.v,
(__F, "\r\nre_putc: overflow! r_cursor.v == %d > %d\r\n",
- el->el_refresh.r_cursor.v, el->el_term.t_size.v),
+ el->el_refresh.r_cursor.v, el->el_terminal.t_size.v),
abort());
}
/* re_addc():
* Draw c, expanding tabs, control chars etc.
*/
-private void
-re_addc(EditLine *el, Int c)
+static void
+re_addc(EditLine *el, wint_t c)
{
- switch (ct_chr_class((Char)c)) {
+ switch (ct_chr_class(c)) {
case CHTYPE_TAB: /* expand the tab */
for (;;) {
re_putc(el, ' ', 1);
@@ -146,9 +145,9 @@ re_addc(EditLine *el, Int c)
re_putc(el, c, 1);
break;
default: {
- Char visbuf[VISUAL_WIDTH_MAX];
+ wchar_t visbuf[VISUAL_WIDTH_MAX];
ssize_t i, n =
- ct_visual_char(visbuf, VISUAL_WIDTH_MAX, (Char)c);
+ ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
for (i = 0; n-- > 0; ++i)
re_putc(el, visbuf[i], 1);
break;
@@ -160,13 +159,15 @@ re_addc(EditLine *el, Int c)
/* re_putc():
* Draw the character given
*/
-protected void
-re_putc(EditLine *el, Int c, int shift)
+libedit_private void
+re_putc(EditLine *el, wint_t c, int shift)
{
- int i, w = Width(c);
- ELRE_DEBUG(1, (__F, "printing %5x '%c'\r\n", c, c));
+ int i, w = wcwidth(c);
+ ELRE_DEBUG(1, (__F, "printing %5x '%lc'\r\n", c, c));
+ if (w == -1)
+ w = 0;
- while (shift && (el->el_refresh.r_cursor.h + w > el->el_term.t_size.h))
+ while (shift && (el->el_refresh.r_cursor.h + w > el->el_terminal.t_size.h))
re_putc(el, ' ', 1);
el->el_vdisplay[el->el_refresh.r_cursor.v]
@@ -181,9 +182,9 @@ re_putc(EditLine *el, Int c, int shift)
return;
el->el_refresh.r_cursor.h += w; /* advance to next place */
- if (el->el_refresh.r_cursor.h >= el->el_term.t_size.h) {
+ if (el->el_refresh.r_cursor.h >= el->el_terminal.t_size.h) {
/* assure end of line */
- el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_term.t_size.h]
+ el->el_vdisplay[el->el_refresh.r_cursor.v][el->el_terminal.t_size.h]
= '\0';
re_nextline(el);
}
@@ -192,21 +193,21 @@ re_putc(EditLine *el, Int c, int shift)
/* re_refresh():
* draws the new virtual screen image from the current input
- * line, then goes line-by-line changing the real image to the new
+ * line, then goes line-by-line changing the real image to the new
* virtual image. The routine to re-draw a line can be replaced
* easily in hopes of a smarter one being placed there.
*/
-protected void
+libedit_private void
re_refresh(EditLine *el)
{
int i, rhdiff;
- Char *cp, *st;
+ wchar_t *cp, *st;
coord_t cur;
#ifdef notyet
size_t termsz;
#endif
- ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%s:\r\n",
+ ELRE_DEBUG(1, (__F, "el->el_line.buffer = :%ls:\r\n",
el->el_line.buffer));
/* reset the Drawing cursor */
@@ -235,7 +236,7 @@ re_refresh(EditLine *el)
/* draw the current input buffer */
#if notyet
- termsz = el->el_term.t_size.h * el->el_term.t_size.v;
+ termsz = el->el_terminal.t_size.h * el->el_terminal.t_size.v;
if (el->el_line.lastchar - el->el_line.buffer > termsz) {
/*
* If line is longer than terminal, process only part
@@ -244,21 +245,21 @@ re_refresh(EditLine *el)
size_t rem = (el->el_line.lastchar-el->el_line.buffer)%termsz;
st = el->el_line.lastchar - rem
- - (termsz - (((rem / el->el_term.t_size.v) - 1)
- * el->el_term.t_size.v));
+ - (termsz - (((rem / el->el_terminal.t_size.v) - 1)
+ * el->el_terminal.t_size.v));
} else
#endif
st = el->el_line.buffer;
for (cp = st; cp < el->el_line.lastchar; cp++) {
if (cp == el->el_line.cursor) {
- int w = Width(*cp);
+ int w = wcwidth(*cp);
/* save for later */
cur.h = el->el_refresh.r_cursor.h;
cur.v = el->el_refresh.r_cursor.v;
/* handle being at a linebroken doublewidth char */
if (w > 1 && el->el_refresh.r_cursor.h + w >
- el->el_term.t_size.h) {
+ el->el_terminal.t_size.h) {
cur.h = 0;
cur.v++;
}
@@ -270,7 +271,7 @@ re_refresh(EditLine *el)
cur.h = el->el_refresh.r_cursor.h;
cur.v = el->el_refresh.r_cursor.v;
}
- rhdiff = el->el_term.t_size.h - el->el_refresh.r_cursor.h -
+ rhdiff = el->el_terminal.t_size.h - el->el_refresh.r_cursor.h -
el->el_rprompt.p_pos.h;
if (el->el_rprompt.p_pos.h && !el->el_rprompt.p_pos.v &&
!el->el_refresh.r_cursor.v && rhdiff > 1) {
@@ -293,8 +294,9 @@ re_refresh(EditLine *el)
ELRE_DEBUG(1, (__F,
"term.h=%d vcur.h=%d vcur.v=%d vdisplay[0]=\r\n:%80.80s:\r\n",
- el->el_term.t_size.h, el->el_refresh.r_cursor.h,
- el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0])));
+ el->el_terminal.t_size.h, el->el_refresh.r_cursor.h,
+ el->el_refresh.r_cursor.v, ct_encode_string(el->el_vdisplay[0],
+ &el->el_scratch)));
ELRE_DEBUG(1, (__F, "updating %d lines.\r\n", el->el_refresh.r_newcv));
for (i = 0; i <= el->el_refresh.r_newcv; i++) {
@@ -309,7 +311,7 @@ re_refresh(EditLine *el)
* leftover stuff.
*/
re__copy_and_pad(el->el_display[i], el->el_vdisplay[i],
- (size_t) el->el_term.t_size.h);
+ (size_t) el->el_terminal.t_size.h);
}
ELRE_DEBUG(1, (__F,
"\r\nel->el_refresh.r_cursor.v=%d,el->el_refresh.r_oldcv=%d i=%d\r\n",
@@ -317,12 +319,12 @@ re_refresh(EditLine *el)
if (el->el_refresh.r_oldcv > el->el_refresh.r_newcv)
for (; i <= el->el_refresh.r_oldcv; i++) {
- term_move_to_line(el, i);
- term_move_to_char(el, 0);
- /* This Strlen should be safe even with MB_FILL_CHARs */
- term_clear_EOL(el, (int) Strlen(el->el_display[i]));
+ terminal_move_to_line(el, i);
+ terminal_move_to_char(el, 0);
+ /* This wcslen should be safe even with MB_FILL_CHARs */
+ terminal_clear_EOL(el, (int) wcslen(el->el_display[i]));
#ifdef DEBUG_REFRESH
- term_overwrite(el, "C\b", (size_t)2);
+ terminal_overwrite(el, L"C\b", 2);
#endif /* DEBUG_REFRESH */
el->el_display[i][0] = '\0';
}
@@ -332,22 +334,22 @@ re_refresh(EditLine *el)
"\r\ncursor.h = %d, cursor.v = %d, cur.h = %d, cur.v = %d\r\n",
el->el_refresh.r_cursor.h, el->el_refresh.r_cursor.v,
cur.h, cur.v));
- term_move_to_line(el, cur.v); /* go to where the cursor is */
- term_move_to_char(el, cur.h);
+ terminal_move_to_line(el, cur.v); /* go to where the cursor is */
+ terminal_move_to_char(el, cur.h);
}
/* re_goto_bottom():
* used to go to last used screen line
*/
-protected void
+libedit_private void
re_goto_bottom(EditLine *el)
{
- term_move_to_line(el, el->el_refresh.r_oldcv);
- term__putc(el, '\n');
+ terminal_move_to_line(el, el->el_refresh.r_oldcv);
+ terminal__putc(el, '\n');
re_clear_display(el);
- term__flush(el);
+ terminal__flush(el);
}
@@ -355,12 +357,12 @@ re_goto_bottom(EditLine *el)
* insert num characters of s into d (in front of the character)
* at dat, maximum length of d is dlen
*/
-private void
+static void
/*ARGSUSED*/
re_insert(EditLine *el __attribute__((__unused__)),
- Char *d, int dat, int dlen, Char *s, int num)
+ wchar_t *d, int dat, int dlen, wchar_t *s, int num)
{
- Char *a, *b;
+ wchar_t *a, *b;
if (num <= 0)
return;
@@ -369,8 +371,9 @@ re_insert(EditLine *el __attribute__((__unused__)),
ELRE_DEBUG(1,
(__F, "re_insert() starting: %d at %d max %d, d == \"%s\"\n",
- num, dat, dlen, ct_encode_string(d)));
- ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s)));
+ num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
+ &el->el_scratch)));
/* open up the space for num chars */
if (num > 0) {
@@ -383,8 +386,9 @@ re_insert(EditLine *el __attribute__((__unused__)),
ELRE_DEBUG(1, (__F,
"re_insert() after insert: %d at %d max %d, d == \"%s\"\n",
- num, dat, dlen, ct_encode_string(d)));
- ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s)));
+ num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
+ ELRE_DEBUG(1, (__F, "s == \"%s\"\n", ct_encode_string(s,
+ &el->el_scratch)));
/* copy the characters */
for (a = d + dat; (a < d + dlen) && (num > 0); num--)
@@ -404,12 +408,12 @@ re_insert(EditLine *el __attribute__((__unused__)),
/* re_delete():
* delete num characters d at dat, maximum length of d is dlen
*/
-private void
+static void
/*ARGSUSED*/
re_delete(EditLine *el __attribute__((__unused__)),
- Char *d, int dat, int dlen, int num)
+ wchar_t *d, int dat, int dlen, int num)
{
- Char *a, *b;
+ wchar_t *a, *b;
if (num <= 0)
return;
@@ -419,7 +423,7 @@ re_delete(EditLine *el __attribute__((__unused__)),
}
ELRE_DEBUG(1,
(__F, "re_delete() starting: %d at %d max %d, d == \"%s\"\n",
- num, dat, dlen, ct_encode_string(d)));
+ num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
/* open up the space for num chars */
if (num > 0) {
@@ -431,15 +435,15 @@ re_delete(EditLine *el __attribute__((__unused__)),
}
ELRE_DEBUG(1,
(__F, "re_delete() after delete: %d at %d max %d, d == \"%s\"\n",
- num, dat, dlen, ct_encode_string(d)));
+ num, dat, dlen, ct_encode_string(d, &el->el_scratch)));
}
/* re__strncopy():
* Like strncpy without padding.
*/
-private void
-re__strncopy(Char *a, Char *b, size_t n)
+static void
+re__strncopy(wchar_t *a, wchar_t *b, size_t n)
{
while (n-- && *b)
@@ -451,9 +455,9 @@ re__strncopy(Char *a, Char *b, size_t n)
* in order to make sure that we have cleared the previous contents of
* the line. fx and sx is the number of characters inserted or deleted
* in the first or second diff, diff is the difference between the
- * number of characters between the new and old line.
+ * number of characters between the new and old line.
*/
-private void
+static void
re_clear_eol(EditLine *el, int fx, int sx, int diff)
{
@@ -470,7 +474,7 @@ re_clear_eol(EditLine *el, int fx, int sx, int diff)
diff = sx;
ELRE_DEBUG(1, (__F, "re_clear_eol %d\n", diff));
- term_clear_EOL(el, diff);
+ terminal_clear_EOL(el, diff);
}
/*****************************************************************
@@ -497,12 +501,12 @@ new: eddie> Oh, my little buggy says to me, as lurgid as
*/
#define MIN_END_KEEP 4
-private void
-re_update_line(EditLine *el, Char *old, Char *new, int i)
+static void
+re_update_line(EditLine *el, wchar_t *old, wchar_t *new, int i)
{
- Char *o, *n, *p, c;
- Char *ofd, *ols, *oe, *nfd, *nls, *ne;
- Char *osb, *ose, *nsb, *nse;
+ wchar_t *o, *n, *p, c;
+ wchar_t *ofd, *ols, *oe, *nfd, *nls, *ne;
+ wchar_t *osb, *ose, *nsb, *nse;
int fx, sx;
size_t len;
@@ -559,7 +563,7 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
nls = ++n;
/*
- * find same begining and same end
+ * find same beginning and same end
*/
osb = ols;
nsb = nls;
@@ -689,9 +693,9 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
sx = (int)((nls - nse) - (ols - ose));
ELRE_DEBUG(1, (__F, "fx %d, sx %d\n", fx, sx));
- ELRE_DEBUG(1, (__F, "ofd %d, osb %d, ose %d, ols %d, oe %d\n",
+ ELRE_DEBUG(1, (__F, "ofd %td, osb %td, ose %td, ols %td, oe %td\n",
ofd - old, osb - old, ose - old, ols - old, oe - old));
- ELRE_DEBUG(1, (__F, "nfd %d, nsb %d, nse %d, nls %d, ne %d\n",
+ ELRE_DEBUG(1, (__F, "nfd %td, nsb %td, nse %td, nls %td, ne %td\n",
nfd - new, nsb - new, nse - new, nls - new, ne - new));
ELRE_DEBUG(1, (__F,
"xxx-xxx:\"00000000001111111111222222222233333333334\"\r\n"));
@@ -717,7 +721,7 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
* don't have to change the line, we don't move to it. el_cursor.h to
* first diff char
*/
- term_move_to_line(el, i);
+ terminal_move_to_line(el, i);
/*
* at this point we have something like this:
@@ -741,7 +745,7 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
* if we have a net insert on the first difference, AND inserting the
* net amount ((nsb-nfd) - (osb-ofd)) won't push the last useful
* character (which is ne if nls != ne, otherwise is nse) off the edge
- * of the screen (el->el_term.t_size.h) else we do the deletes first
+ * of the screen (el->el_terminal.t_size.h) else we do the deletes first
* so that we keep everything we need to.
*/
@@ -763,13 +767,13 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
* No insert or delete
*/
if ((nsb != nfd) && fx > 0 &&
- ((p - old) + fx <= el->el_term.t_size.h)) {
+ ((p - old) + fx <= el->el_terminal.t_size.h)) {
ELRE_DEBUG(1,
- (__F, "first diff insert at %d...\r\n", nfd - new));
+ (__F, "first diff insert at %td...\r\n", nfd - new));
/*
* Move to the first char to insert, where the first diff is.
*/
- term_move_to_char(el, (int)(nfd - new));
+ terminal_move_to_char(el, (int)(nfd - new));
/*
* Check if we have stuff to keep at end
*/
@@ -781,21 +785,21 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
if (fx > 0) {
ELRE_DEBUG(!EL_CAN_INSERT, (__F,
"ERROR: cannot insert in early first diff\n"));
- term_insertwrite(el, nfd, fx);
+ terminal_insertwrite(el, nfd, fx);
re_insert(el, old, (int)(ofd - old),
- el->el_term.t_size.h, nfd, fx);
+ el->el_terminal.t_size.h, nfd, fx);
}
/*
* write (nsb-nfd) - fx chars of new starting at
* (nfd + fx)
*/
len = (size_t) ((nsb - nfd) - fx);
- term_overwrite(el, (nfd + fx), len);
+ terminal_overwrite(el, (nfd + fx), len);
re__strncopy(ofd + fx, nfd + fx, len);
} else {
ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
len = (size_t)(nsb - nfd);
- term_overwrite(el, nfd, len);
+ terminal_overwrite(el, nfd, len);
re__strncopy(ofd, nfd, len);
/*
* Done
@@ -804,11 +808,11 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
}
} else if (fx < 0) {
ELRE_DEBUG(1,
- (__F, "first diff delete at %d...\r\n", ofd - old));
+ (__F, "first diff delete at %td...\r\n", ofd - old));
/*
* move to the first char to delete where the first diff is
*/
- term_move_to_char(el, (int)(ofd - old));
+ terminal_move_to_char(el, (int)(ofd - old));
/*
* Check if we have stuff to save
*/
@@ -821,15 +825,15 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
if (fx < 0) {
ELRE_DEBUG(!EL_CAN_DELETE, (__F,
"ERROR: cannot delete in first diff\n"));
- term_deletechars(el, -fx);
+ terminal_deletechars(el, -fx);
re_delete(el, old, (int)(ofd - old),
- el->el_term.t_size.h, -fx);
+ el->el_terminal.t_size.h, -fx);
}
/*
* write (nsb-nfd) chars of new starting at nfd
*/
len = (size_t) (nsb - nfd);
- term_overwrite(el, nfd, len);
+ terminal_overwrite(el, nfd, len);
re__strncopy(ofd, nfd, len);
} else {
@@ -838,7 +842,7 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
/*
* write (nsb-nfd) chars of new starting at nfd
*/
- term_overwrite(el, nfd, (size_t)(nsb - nfd));
+ terminal_overwrite(el, nfd, (size_t)(nsb - nfd));
re_clear_eol(el, fx, sx,
(int)((oe - old) - (ne - new)));
/*
@@ -849,9 +853,9 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
} else
fx = 0;
- if (sx < 0 && (ose - old) + fx < el->el_term.t_size.h) {
+ if (sx < 0 && (ose - old) + fx < el->el_terminal.t_size.h) {
ELRE_DEBUG(1, (__F,
- "second diff delete at %d...\r\n", (ose - old) + fx));
+ "second diff delete at %td...\r\n", (ose - old) + fx));
/*
* Check if we have stuff to delete
*/
@@ -859,7 +863,7 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
* fx is the number of characters inserted (+) or deleted (-)
*/
- term_move_to_char(el, (int)((ose - old) + fx));
+ terminal_move_to_char(el, (int)((ose - old) + fx));
/*
* Check if we have stuff to save
*/
@@ -871,16 +875,16 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
if (sx < 0) {
ELRE_DEBUG(!EL_CAN_DELETE, (__F,
"ERROR: cannot delete in second diff\n"));
- term_deletechars(el, -sx);
+ terminal_deletechars(el, -sx);
}
/*
* write (nls-nse) chars of new starting at nse
*/
- term_overwrite(el, nse, (size_t)(nls - nse));
+ terminal_overwrite(el, nse, (size_t)(nls - nse));
} else {
ELRE_DEBUG(1, (__F,
"but with nothing left to save\r\n"));
- term_overwrite(el, nse, (size_t)(nls - nse));
+ terminal_overwrite(el, nse, (size_t)(nls - nse));
re_clear_eol(el, fx, sx,
(int)((oe - old) - (ne - new)));
}
@@ -889,10 +893,10 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
* if we have a first insert AND WE HAVEN'T ALREADY DONE IT...
*/
if ((nsb != nfd) && (osb - ofd) <= (nsb - nfd) && (fx == 0)) {
- ELRE_DEBUG(1, (__F, "late first diff insert at %d...\r\n",
+ ELRE_DEBUG(1, (__F, "late first diff insert at %td...\r\n",
nfd - new));
- term_move_to_char(el, (int)(nfd - new));
+ terminal_move_to_char(el, (int)(nfd - new));
/*
* Check if we have stuff to keep at the end
*/
@@ -910,21 +914,21 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
*/
ELRE_DEBUG(!EL_CAN_INSERT, (__F,
"ERROR: cannot insert in late first diff\n"));
- term_insertwrite(el, nfd, fx);
+ terminal_insertwrite(el, nfd, fx);
re_insert(el, old, (int)(ofd - old),
- el->el_term.t_size.h, nfd, fx);
+ el->el_terminal.t_size.h, nfd, fx);
}
/*
* write (nsb-nfd) - fx chars of new starting at
* (nfd + fx)
*/
len = (size_t) ((nsb - nfd) - fx);
- term_overwrite(el, (nfd + fx), len);
+ terminal_overwrite(el, (nfd + fx), len);
re__strncopy(ofd + fx, nfd + fx, len);
} else {
ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
len = (size_t) (nsb - nfd);
- term_overwrite(el, nfd, len);
+ terminal_overwrite(el, nfd, len);
re__strncopy(ofd, nfd, len);
}
}
@@ -934,24 +938,24 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
if (sx >= 0) {
ELRE_DEBUG(1, (__F,
"second diff insert at %d...\r\n", (int)(nse - new)));
- term_move_to_char(el, (int)(nse - new));
+ terminal_move_to_char(el, (int)(nse - new));
if (ols != oe) {
ELRE_DEBUG(1, (__F, "with stuff to keep at end\r\n"));
if (sx > 0) {
/* insert sx chars of new starting at nse */
ELRE_DEBUG(!EL_CAN_INSERT, (__F,
"ERROR: cannot insert in second diff\n"));
- term_insertwrite(el, nse, sx);
+ terminal_insertwrite(el, nse, sx);
}
/*
* write (nls-nse) - sx chars of new starting at
* (nse + sx)
*/
- term_overwrite(el, (nse + sx),
+ terminal_overwrite(el, (nse + sx),
(size_t)((nls - nse) - sx));
} else {
ELRE_DEBUG(1, (__F, "without anything to save\r\n"));
- term_overwrite(el, nse, (size_t)(nls - nse));
+ terminal_overwrite(el, nse, (size_t)(nls - nse));
/*
* No need to do a clear-to-end here because we were
@@ -967,8 +971,8 @@ re_update_line(EditLine *el, Char *old, Char *new, int i)
/* re__copy_and_pad():
* Copy string and pad with spaces
*/
-private void
-re__copy_and_pad(Char *dst, const Char *src, size_t width)
+static void
+re__copy_and_pad(wchar_t *dst, const wchar_t *src, size_t width)
{
size_t i;
@@ -988,10 +992,10 @@ re__copy_and_pad(Char *dst, const Char *src, size_t width)
/* re_refresh_cursor():
* Move to the new cursor position
*/
-protected void
+libedit_private void
re_refresh_cursor(EditLine *el)
{
- Char *cp;
+ wchar_t *cp;
int h, v, th, w;
if (el->el_line.cursor >= el->el_line.lastchar) {
@@ -1005,7 +1009,7 @@ re_refresh_cursor(EditLine *el)
/* first we must find where the cursor is... */
h = el->el_prompt.p_pos.h;
v = el->el_prompt.p_pos.v;
- th = el->el_term.t_size.h; /* optimize for speed */
+ th = el->el_terminal.t_size.h; /* optimize for speed */
/* do input buffer to el->el_line.cursor */
for (cp = el->el_line.buffer; cp < el->el_line.cursor; cp++) {
@@ -1019,7 +1023,7 @@ re_refresh_cursor(EditLine *el)
continue;
break;
default:
- w = Width(*cp);
+ w = wcwidth(*cp);
if (w > 1 && h + w > th) { /* won't fit on line */
h = 0;
v++;
@@ -1035,36 +1039,36 @@ re_refresh_cursor(EditLine *el)
}
/* if we have a next character, and it's a doublewidth one, we need to
* check whether we need to linebreak for it to fit */
- if (cp < el->el_line.lastchar && (w = Width(*cp)) > 1)
+ if (cp < el->el_line.lastchar && (w = wcwidth(*cp)) > 1)
if (h + w > th) {
h = 0;
v++;
}
/* now go there */
- term_move_to_line(el, v);
- term_move_to_char(el, h);
- term__flush(el);
+ terminal_move_to_line(el, v);
+ terminal_move_to_char(el, h);
+ terminal__flush(el);
}
/* re_fastputc():
* Add a character fast.
*/
-private void
-re_fastputc(EditLine *el, Int c)
+static void
+re_fastputc(EditLine *el, wint_t c)
{
- int w = Width((Char)c);
- while (w > 1 && el->el_cursor.h + w > el->el_term.t_size.h)
+ int w = wcwidth(c);
+ while (w > 1 && el->el_cursor.h + w > el->el_terminal.t_size.h)
re_fastputc(el, ' ');
- term__putc(el, c);
+ terminal__putc(el, c);
el->el_display[el->el_cursor.v][el->el_cursor.h++] = c;
while (--w > 0)
el->el_display[el->el_cursor.v][el->el_cursor.h++]
= MB_FILL_CHAR;
- if (el->el_cursor.h >= el->el_term.t_size.h) {
+ if (el->el_cursor.h >= el->el_terminal.t_size.h) {
/* if we must overflow */
el->el_cursor.h = 0;
@@ -1074,14 +1078,14 @@ re_fastputc(EditLine *el, Int c)
* We do this via pointer shuffling - it's safe in this case
* and we avoid memcpy().
*/
- if (el->el_cursor.v + 1 >= el->el_term.t_size.v) {
- int i, lins = el->el_term.t_size.v;
- Char *firstline = el->el_display[0];
+ if (el->el_cursor.v + 1 >= el->el_terminal.t_size.v) {
+ int i, lins = el->el_terminal.t_size.v;
+ wchar_t *firstline = el->el_display[0];
for(i = 1; i < lins; i++)
el->el_display[i - 1] = el->el_display[i];
- re__copy_and_pad(firstline, STR(""), 0);
+ re__copy_and_pad(firstline, L"", (size_t)0);
el->el_display[i - 1] = firstline;
} else {
el->el_cursor.v++;
@@ -1089,12 +1093,12 @@ re_fastputc(EditLine *el, Int c)
}
if (EL_HAS_AUTO_MARGINS) {
if (EL_HAS_MAGIC_MARGINS) {
- term__putc(el, ' ');
- term__putc(el, '\b');
+ terminal__putc(el, ' ');
+ terminal__putc(el, '\b');
}
} else {
- term__putc(el, '\r');
- term__putc(el, '\n');
+ terminal__putc(el, '\r');
+ terminal__putc(el, '\n');
}
}
}
@@ -1104,10 +1108,10 @@ re_fastputc(EditLine *el, Int c)
* we added just one char, handle it fast.
* Assumes that screen cursor == real cursor
*/
-protected void
+libedit_private void
re_fastaddc(EditLine *el)
{
- Char c;
+ wchar_t c;
int rhdiff;
c = el->el_line.cursor[-1];
@@ -1116,7 +1120,7 @@ re_fastaddc(EditLine *el)
re_refresh(el); /* too hard to handle */
return;
}
- rhdiff = el->el_term.t_size.h - el->el_cursor.h -
+ rhdiff = el->el_terminal.t_size.h - el->el_cursor.h -
el->el_rprompt.p_pos.h;
if (el->el_rprompt.p_pos.h && rhdiff < 3) {
re_refresh(el); /* clear out rprompt if less than 1 char gap */
@@ -1131,29 +1135,29 @@ re_fastaddc(EditLine *el)
break;
case CHTYPE_ASCIICTL:
case CHTYPE_NONPRINT: {
- Char visbuf[VISUAL_WIDTH_MAX];
+ wchar_t visbuf[VISUAL_WIDTH_MAX];
ssize_t i, n =
- ct_visual_char(visbuf, VISUAL_WIDTH_MAX, (Char)c);
+ ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
for (i = 0; n-- > 0; ++i)
re_fastputc(el, visbuf[i]);
break;
}
}
- term__flush(el);
+ terminal__flush(el);
}
/* re_clear_display():
* clear the screen buffers so that new new prompt starts fresh.
*/
-protected void
+libedit_private void
re_clear_display(EditLine *el)
{
int i;
el->el_cursor.v = 0;
el->el_cursor.h = 0;
- for (i = 0; i < el->el_term.t_size.v; i++)
+ for (i = 0; i < el->el_terminal.t_size.v; i++)
el->el_display[i][0] = '\0';
el->el_refresh.r_oldcv = 0;
}
@@ -1162,7 +1166,7 @@ re_clear_display(EditLine *el)
/* re_clear_lines():
* Make sure all lines are *really* blank
*/
-protected void
+libedit_private void
re_clear_lines(EditLine *el)
{
@@ -1170,14 +1174,14 @@ re_clear_lines(EditLine *el)
int i;
for (i = el->el_refresh.r_oldcv; i >= 0; i--) {
/* for each line on the screen */
- term_move_to_line(el, i);
- term_move_to_char(el, 0);
- term_clear_EOL(el, el->el_term.t_size.h);
+ terminal_move_to_line(el, i);
+ terminal_move_to_char(el, 0);
+ terminal_clear_EOL(el, el->el_terminal.t_size.h);
}
} else {
- term_move_to_line(el, el->el_refresh.r_oldcv);
+ terminal_move_to_line(el, el->el_refresh.r_oldcv);
/* go to last line */
- term__putc(el, '\r'); /* go to BOL */
- term__putc(el, '\n'); /* go to new line */
+ terminal__putc(el, '\r'); /* go to BOL */
+ terminal__putc(el, '\n'); /* go to new line */
}
}
diff --git a/lib/libedit/src/refresh.h b/lib/libedit/src/refresh.h
index f80be4635456..621c5691ec1c 100644
--- a/lib/libedit/src/refresh.h
+++ b/lib/libedit/src/refresh.h
@@ -1,4 +1,4 @@
-/* $NetBSD: refresh.h,v 1.6 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: refresh.h,v 1.10 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,20 +40,18 @@
#ifndef _h_el_refresh
#define _h_el_refresh
-#include "histedit.h"
-
typedef struct {
coord_t r_cursor; /* Refresh cursor position */
int r_oldcv; /* Vertical locations */
int r_newcv;
} el_refresh_t;
-protected void re_putc(EditLine *, Int, int);
-protected void re_clear_lines(EditLine *);
-protected void re_clear_display(EditLine *);
-protected void re_refresh(EditLine *);
-protected void re_refresh_cursor(EditLine *);
-protected void re_fastaddc(EditLine *);
-protected void re_goto_bottom(EditLine *);
+libedit_private void re_putc(EditLine *, wint_t, int);
+libedit_private void re_clear_lines(EditLine *);
+libedit_private void re_clear_display(EditLine *);
+libedit_private void re_refresh(EditLine *);
+libedit_private void re_refresh_cursor(EditLine *);
+libedit_private void re_fastaddc(EditLine *);
+libedit_private void re_goto_bottom(EditLine *);
#endif /* _h_el_refresh */
diff --git a/lib/libedit/src/search.c b/lib/libedit/src/search.c
index 59bbd1add0be..5226cf5fa6a3 100644
--- a/lib/libedit/src/search.c
+++ b/lib/libedit/src/search.c
@@ -1,4 +1,4 @@
-/* $NetBSD: search.c,v 1.24 2010/04/15 00:57:33 christos Exp $ */
+/* $NetBSD: search.c,v 1.47 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)search.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: search.c,v 1.24 2010/04/15 00:57:33 christos Exp $");
+__RCSID("$NetBSD: search.c,v 1.47 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -45,13 +45,16 @@ __RCSID("$NetBSD: search.c,v 1.24 2010/04/15 00:57:33 christos Exp $");
* search.c: History and character search functions
*/
#include <stdlib.h>
-#include <sys/types.h>
+#include <string.h>
#if defined(REGEX)
#include <regex.h>
#elif defined(REGEXP)
#include <regexp.h>
#endif
+
#include "el.h"
+#include "common.h"
+#include "fcns.h"
/*
* Adjust cursor in vi mode to include the character under it
@@ -63,31 +66,32 @@ __RCSID("$NetBSD: search.c,v 1.24 2010/04/15 00:57:33 christos Exp $");
/* search_init():
* Initialize the search stuff
*/
-protected int
+libedit_private int
search_init(EditLine *el)
{
el->el_search.patbuf = el_malloc(EL_BUFSIZ *
sizeof(*el->el_search.patbuf));
if (el->el_search.patbuf == NULL)
- return (-1);
+ return -1;
+ el->el_search.patbuf[0] = L'\0';
el->el_search.patlen = 0;
el->el_search.patdir = -1;
- el->el_search.chacha = '\0';
+ el->el_search.chacha = L'\0';
el->el_search.chadir = CHAR_FWD;
el->el_search.chatflg = 0;
- return (0);
+ return 0;
}
/* search_end():
* Initialize the search stuff
*/
-protected void
+libedit_private void
search_end(EditLine *el)
{
- el_free((ptr_t) el->el_search.patbuf);
+ el_free(el->el_search.patbuf);
el->el_search.patbuf = NULL;
}
@@ -96,7 +100,7 @@ search_end(EditLine *el)
/* regerror():
* Handle regular expression errors
*/
-public void
+void
/*ARGSUSED*/
regerror(const char *msg)
{
@@ -107,12 +111,10 @@ regerror(const char *msg)
/* el_match():
* Return if string matches pattern
*/
-protected int
-el_match(const Char *str, const Char *pat)
+libedit_private int
+el_match(const wchar_t *str, const wchar_t *pat)
{
-#ifdef WIDECHAR
static ct_buffer_t conv;
-#endif
#if defined (REGEX)
regex_t re;
int rv;
@@ -124,30 +126,31 @@ el_match(const Char *str, const Char *pat)
extern int re_exec(const char *);
#endif
- if (Strstr(str, pat) != 0)
- return (1);
+ if (wcsstr(str, pat) != 0)
+ return 1;
#if defined(REGEX)
if (regcomp(&re, ct_encode_string(pat, &conv), 0) == 0) {
- rv = regexec(&re, ct_encode_string(str, &conv), 0, NULL, 0) == 0;
+ rv = regexec(&re, ct_encode_string(str, &conv), (size_t)0, NULL,
+ 0) == 0;
regfree(&re);
} else {
rv = 0;
}
- return (rv);
+ return rv;
#elif defined(REGEXP)
if ((re = regcomp(ct_encode_string(pat, &conv))) != NULL) {
rv = regexec(re, ct_encode_string(str, &conv));
- free((ptr_t) re);
+ el_free(re);
} else {
rv = 0;
}
- return (rv);
+ return rv;
#else
if (re_comp(ct_encode_string(pat, &conv)) != NULL)
- return (0);
+ return 0;
else
- return (re_exec(ct_encode_string(str, &conv)) == 1);
+ return re_exec(ct_encode_string(str, &conv)) == 1;
#endif
}
@@ -155,35 +158,36 @@ el_match(const Char *str, const Char *pat)
/* c_hmatch():
* return True if the pattern matches the prefix
*/
-protected int
-c_hmatch(EditLine *el, const Char *str)
+libedit_private int
+c_hmatch(EditLine *el, const wchar_t *str)
{
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "match `%s' with `%s'\n",
el->el_search.patbuf, str);
#endif /* SDEBUG */
- return (el_match(str, el->el_search.patbuf));
+ return el_match(str, el->el_search.patbuf);
}
/* c_setpat():
* Set the history seatch pattern
*/
-protected void
+libedit_private void
c_setpat(EditLine *el)
{
if (el->el_state.lastcmd != ED_SEARCH_PREV_HISTORY &&
el->el_state.lastcmd != ED_SEARCH_NEXT_HISTORY) {
- el->el_search.patlen = EL_CURSOR(el) - el->el_line.buffer;
+ el->el_search.patlen =
+ (size_t)(EL_CURSOR(el) - el->el_line.buffer);
if (el->el_search.patlen >= EL_BUFSIZ)
el->el_search.patlen = EL_BUFSIZ - 1;
if (el->el_search.patlen != 0) {
- (void) Strncpy(el->el_search.patbuf, el->el_line.buffer,
+ (void) wcsncpy(el->el_search.patbuf, el->el_line.buffer,
el->el_search.patlen);
el->el_search.patbuf[el->el_search.patlen] = '\0';
} else
- el->el_search.patlen = Strlen(el->el_search.patbuf);
+ el->el_search.patlen = wcslen(el->el_search.patbuf);
}
#ifdef SDEBUG
(void) fprintf(el->el_errfile, "\neventno = %d\n",
@@ -201,15 +205,14 @@ c_setpat(EditLine *el)
/* ce_inc_search():
* Emacs incremental search
*/
-protected el_action_t
+libedit_private el_action_t
ce_inc_search(EditLine *el, int dir)
{
- static const Char STRfwd[] = {'f', 'w', 'd', '\0'},
- STRbck[] = {'b', 'c', 'k', '\0'};
- static Char pchar = ':';/* ':' = normal, '?' = failed */
- static Char endcmd[2] = {'\0', '\0'};
- Char ch, *ocursor = el->el_line.cursor, oldpchar = pchar;
- const Char *cp;
+ static const wchar_t STRfwd[] = L"fwd", STRbck[] = L"bck";
+ static wchar_t pchar = L':'; /* ':' = normal, '?' = failed */
+ static wchar_t endcmd[2] = {'\0', '\0'};
+ wchar_t *ocursor = el->el_line.cursor, oldpchar = pchar, ch;
+ const wchar_t *cp;
el_action_t ret = CC_NORM;
@@ -221,7 +224,7 @@ ce_inc_search(EditLine *el, int dir)
if (el->el_line.lastchar + sizeof(STRfwd) /
sizeof(*el->el_line.lastchar) + 2 +
el->el_search.patlen >= el->el_line.limit)
- return (CC_ERROR);
+ return CC_ERROR;
for (;;) {
@@ -248,14 +251,14 @@ ce_inc_search(EditLine *el, int dir)
*el->el_line.lastchar = '\0';
re_refresh(el);
- if (FUN(el,getc)(el, &ch) != 1)
- return (ed_end_of_file(el, 0));
+ if (el_wgetc(el, &ch) != 1)
+ return ed_end_of_file(el, 0);
switch (el->el_map.current[(unsigned char) ch]) {
case ED_INSERT:
case ED_DIGIT:
if (el->el_search.patlen >= EL_BUFSIZ - LEN)
- term_beep(el);
+ terminal_beep(el);
else {
el->el_search.patbuf[el->el_search.patlen++] =
ch;
@@ -280,7 +283,7 @@ ce_inc_search(EditLine *el, int dir)
if (el->el_search.patlen > LEN)
done++;
else
- term_beep(el);
+ terminal_beep(el);
break;
default:
@@ -304,7 +307,7 @@ ce_inc_search(EditLine *el, int dir)
*el->el_line.cursor != '\n') {
if (el->el_search.patlen >=
EL_BUFSIZ - LEN) {
- term_beep(el);
+ terminal_beep(el);
break;
}
el->el_search.patbuf[el->el_search.patlen++] =
@@ -317,14 +320,14 @@ ce_inc_search(EditLine *el, int dir)
re_refresh(el);
break;
} else if (isglob(*cp)) {
- term_beep(el);
+ terminal_beep(el);
break;
}
break;
default: /* Terminate and execute cmd */
endcmd[0] = ch;
- FUN(el,push)(el, endcmd);
+ el_wpush(el, endcmd);
/* FALLTHROUGH */
case 0033: /* ESC: Terminate */
@@ -344,14 +347,14 @@ ce_inc_search(EditLine *el, int dir)
/* Can't search if unmatched '[' */
for (cp = &el->el_search.patbuf[el->el_search.patlen-1],
- ch = ']';
+ ch = L']';
cp >= &el->el_search.patbuf[LEN];
cp--)
if (*cp == '[' || *cp == ']') {
ch = *cp;
break;
}
- if (el->el_search.patlen > LEN && ch != '[') {
+ if (el->el_search.patlen > LEN && ch != L'[') {
if (redo && newdir == dir) {
if (pchar == '?') { /* wrap around */
el->el_history.eventno =
@@ -386,9 +389,10 @@ ce_inc_search(EditLine *el, int dir)
/* avoid c_setpat */
el->el_state.lastcmd =
(el_action_t) newdir;
- ret = newdir == ED_SEARCH_PREV_HISTORY ?
+ ret = (el_action_t)
+ (newdir == ED_SEARCH_PREV_HISTORY ?
ed_search_prev_history(el, 0) :
- ed_search_next_history(el, 0);
+ ed_search_next_history(el, 0));
if (ret != CC_ERROR) {
el->el_line.cursor = newdir ==
ED_SEARCH_PREV_HISTORY ?
@@ -402,13 +406,13 @@ ce_inc_search(EditLine *el, int dir)
el->el_search.patbuf[el->el_search.patlen] =
'\0';
if (ret == CC_ERROR) {
- term_beep(el);
+ terminal_beep(el);
if (el->el_history.eventno !=
ohisteventno) {
el->el_history.eventno =
ohisteventno;
if (hist_get(el) == CC_ERROR)
- return (CC_ERROR);
+ return CC_ERROR;
}
el->el_line.cursor = ocursor;
pchar = '?';
@@ -433,14 +437,14 @@ ce_inc_search(EditLine *el, int dir)
if (el->el_history.eventno != ohisteventno) {
el->el_history.eventno = ohisteventno;
if (hist_get(el) == CC_ERROR)
- return (CC_ERROR);
+ return CC_ERROR;
}
el->el_line.cursor = ocursor;
if (ret == CC_ERROR)
re_refresh(el);
}
if (done || ret != CC_NORM)
- return (ret);
+ return ret;
}
}
@@ -448,12 +452,12 @@ ce_inc_search(EditLine *el, int dir)
/* cv_search():
* Vi search.
*/
-protected el_action_t
+libedit_private el_action_t
cv_search(EditLine *el, int dir)
{
- Char ch;
- Char tmpbuf[EL_BUFSIZ];
- int tmplen;
+ wchar_t ch;
+ wchar_t tmpbuf[EL_BUFSIZ];
+ ssize_t tmplen;
#ifdef ANCHOR
tmpbuf[0] = '.';
@@ -464,7 +468,7 @@ cv_search(EditLine *el, int dir)
el->el_search.patdir = dir;
tmplen = c_gets(el, &tmpbuf[LEN],
- dir == ED_SEARCH_PREV_HISTORY ? STR("\n/") : STR("\n?") );
+ dir == ED_SEARCH_PREV_HISTORY ? L"\n/" : L"\n?" );
if (tmplen == -1)
return CC_REFRESH;
@@ -478,16 +482,16 @@ cv_search(EditLine *el, int dir)
*/
if (el->el_search.patlen == 0) {
re_refresh(el);
- return (CC_ERROR);
+ return CC_ERROR;
}
#ifdef ANCHOR
if (el->el_search.patbuf[0] != '.' &&
el->el_search.patbuf[0] != '*') {
- (void) Strncpy(tmpbuf, el->el_search.patbuf,
+ (void) wcsncpy(tmpbuf, el->el_search.patbuf,
sizeof(tmpbuf) / sizeof(*tmpbuf) - 1);
el->el_search.patbuf[0] = '.';
el->el_search.patbuf[1] = '*';
- (void) Strncpy(&el->el_search.patbuf[2], tmpbuf,
+ (void) wcsncpy(&el->el_search.patbuf[2], tmpbuf,
EL_BUFSIZ - 3);
el->el_search.patlen++;
el->el_search.patbuf[el->el_search.patlen++] = '.';
@@ -501,33 +505,33 @@ cv_search(EditLine *el, int dir)
tmpbuf[tmplen++] = '*';
#endif
tmpbuf[tmplen] = '\0';
- (void) Strncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1);
- el->el_search.patlen = tmplen;
+ (void) wcsncpy(el->el_search.patbuf, tmpbuf, EL_BUFSIZ - 1);
+ el->el_search.patlen = (size_t)tmplen;
}
el->el_state.lastcmd = (el_action_t) dir; /* avoid c_setpat */
el->el_line.cursor = el->el_line.lastchar = el->el_line.buffer;
if ((dir == ED_SEARCH_PREV_HISTORY ? ed_search_prev_history(el, 0) :
ed_search_next_history(el, 0)) == CC_ERROR) {
re_refresh(el);
- return (CC_ERROR);
+ return CC_ERROR;
}
if (ch == 0033) {
re_refresh(el);
return ed_newline(el, 0);
}
- return (CC_REFRESH);
+ return CC_REFRESH;
}
/* ce_search_line():
* Look for a pattern inside a line
*/
-protected el_action_t
+libedit_private el_action_t
ce_search_line(EditLine *el, int dir)
{
- Char *cp = el->el_line.cursor;
- Char *pattern = el->el_search.patbuf;
- Char oc, *ocp;
+ wchar_t *cp = el->el_line.cursor;
+ wchar_t *pattern = el->el_search.patbuf;
+ wchar_t oc, *ocp;
#ifdef ANCHOR
ocp = &pattern[1];
oc = *ocp;
@@ -542,21 +546,21 @@ ce_search_line(EditLine *el, int dir)
if (el_match(cp, ocp)) {
*ocp = oc;
el->el_line.cursor = cp;
- return (CC_NORM);
+ return CC_NORM;
}
}
*ocp = oc;
- return (CC_ERROR);
+ return CC_ERROR;
} else {
for (; *cp != '\0' && cp < el->el_line.limit; cp++) {
if (el_match(cp, ocp)) {
*ocp = oc;
el->el_line.cursor = cp;
- return (CC_NORM);
+ return CC_NORM;
}
}
*ocp = oc;
- return (CC_ERROR);
+ return CC_ERROR;
}
}
@@ -564,8 +568,8 @@ ce_search_line(EditLine *el, int dir)
/* cv_repeat_srch():
* Vi repeat search
*/
-protected el_action_t
-cv_repeat_srch(EditLine *el, Int c)
+libedit_private el_action_t
+cv_repeat_srch(EditLine *el, wint_t c)
{
#ifdef SDEBUG
@@ -578,11 +582,11 @@ cv_repeat_srch(EditLine *el, Int c)
switch (c) {
case ED_SEARCH_NEXT_HISTORY:
- return (ed_search_next_history(el, 0));
+ return ed_search_next_history(el, 0);
case ED_SEARCH_PREV_HISTORY:
- return (ed_search_prev_history(el, 0));
+ return ed_search_prev_history(el, 0);
default:
- return (CC_ERROR);
+ return CC_ERROR;
}
}
@@ -590,36 +594,34 @@ cv_repeat_srch(EditLine *el, Int c)
/* cv_csearch():
* Vi character search
*/
-protected el_action_t
-cv_csearch(EditLine *el, int direction, Int ch, int count, int tflag)
+libedit_private el_action_t
+cv_csearch(EditLine *el, int direction, wint_t ch, int count, int tflag)
{
- Char *cp;
+ wchar_t *cp;
if (ch == 0)
return CC_ERROR;
- if (ch == -1) {
- Char c;
- if (FUN(el,getc)(el, &c) != 1)
+ if (ch == (wint_t)-1) {
+ if (el_wgetc(el, &ch) != 1)
return ed_end_of_file(el, 0);
- ch = c;
}
/* Save for ';' and ',' commands */
el->el_search.chacha = ch;
el->el_search.chadir = direction;
- el->el_search.chatflg = tflag;
+ el->el_search.chatflg = (char)tflag;
cp = el->el_line.cursor;
while (count--) {
- if (*cp == ch)
+ if ((wint_t)*cp == ch)
cp += direction;
for (;;cp += direction) {
if (cp >= el->el_line.lastchar)
return CC_ERROR;
if (cp < el->el_line.buffer)
return CC_ERROR;
- if (*cp == ch)
+ if ((wint_t)*cp == ch)
break;
}
}
diff --git a/lib/libedit/src/search.h b/lib/libedit/src/search.h
index d9f27e561854..4ca39c4c00e9 100644
--- a/lib/libedit/src/search.h
+++ b/lib/libedit/src/search.h
@@ -1,4 +1,4 @@
-/* $NetBSD: search.h,v 1.9 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: search.h,v 1.14 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,27 +40,25 @@
#ifndef _h_el_search
#define _h_el_search
-#include "histedit.h"
-
typedef struct el_search_t {
- Char *patbuf; /* The pattern buffer */
+ wchar_t *patbuf; /* The pattern buffer */
size_t patlen; /* Length of the pattern buffer */
int patdir; /* Direction of the last search */
int chadir; /* Character search direction */
- Char chacha; /* Character we are looking for */
+ wchar_t chacha; /* Character we are looking for */
char chatflg; /* 0 if f, 1 if t */
} el_search_t;
-protected int el_match(const Char *, const Char *);
-protected int search_init(EditLine *);
-protected void search_end(EditLine *);
-protected int c_hmatch(EditLine *, const Char *);
-protected void c_setpat(EditLine *);
-protected el_action_t ce_inc_search(EditLine *, int);
-protected el_action_t cv_search(EditLine *, int);
-protected el_action_t ce_search_line(EditLine *, int);
-protected el_action_t cv_repeat_srch(EditLine *, Int);
-protected el_action_t cv_csearch(EditLine *, int, Int, int, int);
+libedit_private int el_match(const wchar_t *, const wchar_t *);
+libedit_private int search_init(EditLine *);
+libedit_private void search_end(EditLine *);
+libedit_private int c_hmatch(EditLine *, const wchar_t *);
+libedit_private void c_setpat(EditLine *);
+libedit_private el_action_t ce_inc_search(EditLine *, int);
+libedit_private el_action_t cv_search(EditLine *, int);
+libedit_private el_action_t ce_search_line(EditLine *, int);
+libedit_private el_action_t cv_repeat_srch(EditLine *, wint_t);
+libedit_private el_action_t cv_csearch(EditLine *, int, wint_t, int, int);
#endif /* _h_el_search */
diff --git a/lib/libedit/src/shlib_version b/lib/libedit/src/shlib_version
index 778486cf30d9..303609d268aa 100644
--- a/lib/libedit/src/shlib_version
+++ b/lib/libedit/src/shlib_version
@@ -1,5 +1,5 @@
-# $NetBSD: shlib_version,v 1.18 2009/01/11 03:07:48 christos Exp $
+# $NetBSD: shlib_version,v 1.19 2013/01/22 20:23:21 christos Exp $
# Remember to update distrib/sets/lists/base/shl.* when changing
#
major=3
-minor=0
+minor=1
diff --git a/lib/libedit/src/sig.c b/lib/libedit/src/sig.c
index 1ea319faa063..83742a3d6588 100644
--- a/lib/libedit/src/sig.c
+++ b/lib/libedit/src/sig.c
@@ -1,4 +1,4 @@
-/* $NetBSD: sig.c,v 1.15 2009/02/19 15:20:22 christos Exp $ */
+/* $NetBSD: sig.c,v 1.26 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)sig.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: sig.c,v 1.15 2009/02/19 15:20:22 christos Exp $");
+__RCSID("$NetBSD: sig.c,v 1.26 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -46,31 +46,35 @@ __RCSID("$NetBSD: sig.c,v 1.15 2009/02/19 15:20:22 christos Exp $");
* our policy is to trap all signals, set a good state
* and pass the ball to our caller.
*/
-#include "el.h"
+#include <errno.h>
#include <stdlib.h>
-private EditLine *sel = NULL;
+#include "el.h"
+#include "common.h"
+
+static EditLine *sel = NULL;
-private const int sighdl[] = {
+static const int sighdl[] = {
#define _DO(a) (a),
ALLSIGS
#undef _DO
- 1
};
-private void sig_handler(int);
+static void sig_handler(int);
/* sig_handler():
* This is the handler called for all signals
* XXX: we cannot pass any data so we just store the old editline
* state in a private variable
*/
-private void
+static void
sig_handler(int signo)
{
- int i;
+ int i, save_errno;
sigset_t nset, oset;
+ save_errno = errno;
(void) sigemptyset(&nset);
(void) sigaddset(&nset, signo);
(void) sigprocmask(SIG_BLOCK, &nset, &oset);
@@ -82,7 +86,7 @@ sig_handler(int signo)
tty_rawmode(sel);
if (ed_redisplay(sel, 0) == CC_REFRESH)
re_refresh(sel);
- term__flush(sel);
+ terminal__flush(sel);
break;
case SIGWINCH:
@@ -104,13 +108,14 @@ sig_handler(int signo)
sigemptyset(&sel->el_signal->sig_action[i].sa_mask);
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
(void) kill(0, signo);
+ errno = save_errno;
}
/* sig_init():
* Initialize all signal stuff
*/
-protected int
+libedit_private int
sig_init(EditLine *el)
{
size_t i;
@@ -142,11 +147,11 @@ sig_init(EditLine *el)
/* sig_end():
* Clear all signal stuff
*/
-protected void
+libedit_private void
sig_end(EditLine *el)
{
- el_free((ptr_t) el->el_signal);
+ el_free(el->el_signal);
el->el_signal = NULL;
}
@@ -154,7 +159,7 @@ sig_end(EditLine *el)
/* sig_set():
* set all the signal handlers
*/
-protected void
+libedit_private void
sig_set(EditLine *el)
{
size_t i;
@@ -181,7 +186,7 @@ sig_set(EditLine *el)
/* sig_clr():
* clear all the signal handlers
*/
-protected void
+libedit_private void
sig_clr(EditLine *el)
{
size_t i;
diff --git a/lib/libedit/src/sig.h b/lib/libedit/src/sig.h
index c957cfdf5a72..5ee453fb6121 100644
--- a/lib/libedit/src/sig.h
+++ b/lib/libedit/src/sig.h
@@ -1,4 +1,4 @@
-/* $NetBSD: sig.h,v 1.8 2009/02/19 15:20:22 christos Exp $ */
+/* $NetBSD: sig.h,v 1.11 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -42,8 +42,6 @@
#include <signal.h>
-#include "histedit.h"
-
/*
* Define here all the signals we are going to handle
* The _DO macro is used to iterate in the source code
@@ -64,9 +62,9 @@ typedef struct {
volatile sig_atomic_t sig_no;
} *el_signal_t;
-protected void sig_end(EditLine*);
-protected int sig_init(EditLine*);
-protected void sig_set(EditLine*);
-protected void sig_clr(EditLine*);
+libedit_private void sig_end(EditLine*);
+libedit_private int sig_init(EditLine*);
+libedit_private void sig_set(EditLine*);
+libedit_private void sig_clr(EditLine*);
#endif /* _h_el_sig */
diff --git a/lib/libedit/src/strlcat.c b/lib/libedit/src/strlcat.c
deleted file mode 100644
index 1a92fc5fa9ad..000000000000
--- a/lib/libedit/src/strlcat.c
+++ /dev/null
@@ -1,74 +0,0 @@
-/* $NetBSD: strlcat.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */
-/* $OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp $ */
-
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
- * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "config.h"
-
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: strlcat.c,v 1.3 2007/06/04 18:19:27 christos Exp $");
-#endif /* LIBC_SCCS and not lint */
-
-#include <sys/types.h>
-#include <assert.h>
-#include <string.h>
-
-#ifdef _LIBC
-# ifdef __weak_alias
-__weak_alias(strlcat, _strlcat)
-# endif
-#endif
-
-#if !HAVE_STRLCAT
-/*
- * Appends src to string dst of size siz (unlike strncat, siz is the
- * full size of dst, not space left). At most siz-1 characters
- * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
- * Returns strlen(src) + MIN(siz, strlen(initial dst)).
- * If retval >= siz, truncation occurred.
- */
-size_t
-strlcat(char *dst, const char *src, size_t siz)
-{
- char *d = dst;
- const char *s = src;
- size_t n = siz;
- size_t dlen;
-
- _DIAGASSERT(dst != NULL);
- _DIAGASSERT(src != NULL);
-
- /* Find the end of dst and adjust bytes left but don't go past end */
- while (n-- != 0 && *d != '\0')
- d++;
- dlen = d - dst;
- n = siz - dlen;
-
- if (n == 0)
- return(dlen + strlen(s));
- while (*s != '\0') {
- if (n != 1) {
- *d++ = *s;
- n--;
- }
- s++;
- }
- *d = '\0';
-
- return(dlen + (s - src)); /* count does not include NUL */
-}
-#endif
diff --git a/lib/libedit/src/strlcpy.c b/lib/libedit/src/strlcpy.c
deleted file mode 100644
index 8b3dfad211fa..000000000000
--- a/lib/libedit/src/strlcpy.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/* $NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $ */
-/* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */
-
-/*
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
- * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
- * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "config.h"
-
-#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: strlcpy.c,v 1.3 2007/06/04 18:19:27 christos Exp $");
-#endif /* LIBC_SCCS and not lint */
-
-#include <sys/types.h>
-#include <assert.h>
-#include <string.h>
-
-#ifdef _LIBC
-# ifdef __weak_alias
-__weak_alias(strlcpy, _strlcpy)
-# endif
-#endif
-
-#if !HAVE_STRLCPY
-/*
- * Copy src to string dst of size siz. At most siz-1 characters
- * will be copied. Always NUL terminates (unless siz == 0).
- * Returns strlen(src); if retval >= siz, truncation occurred.
- */
-size_t
-strlcpy(char *dst, const char *src, size_t siz)
-{
- char *d = dst;
- const char *s = src;
- size_t n = siz;
-
- _DIAGASSERT(dst != NULL);
- _DIAGASSERT(src != NULL);
-
- /* Copy as many bytes as will fit */
- if (n != 0 && --n != 0) {
- do {
- if ((*d++ = *s++) == 0)
- break;
- } while (--n != 0);
- }
-
- /* Not enough room in dst, add NUL and traverse rest of src */
- if (n == 0) {
- if (siz != 0)
- *d = '\0'; /* NUL-terminate dst */
- while (*s++)
- ;
- }
-
- return(s - src - 1); /* count does not include NUL */
-}
-#endif
diff --git a/lib/libedit/src/sys.h b/lib/libedit/src/sys.h
index 87c90df4e4a2..1321c209697c 100644
--- a/lib/libedit/src/sys.h
+++ b/lib/libedit/src/sys.h
@@ -1,4 +1,4 @@
-/* $NetBSD: sys.h,v 1.13 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: sys.h,v 1.27 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -44,12 +44,12 @@
#include <sys/cdefs.h>
#endif
-#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8)
-# define __attribute__(A)
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
#endif
-#ifndef _DIAGASSERT
-# define _DIAGASSERT(x)
+#if !defined(__attribute__) && (defined(__cplusplus) || !defined(__GNUC__) || __GNUC__ == 2 && __GNUC_MINOR__ < 8)
+# define __attribute__(A)
#endif
#ifndef __BEGIN_DECLS
@@ -62,37 +62,13 @@
# endif
#endif
-#ifndef public
-# define public /* Externally visible functions/variables */
-#endif
-
-#ifndef private
-# define private static /* Always hidden internals */
-#endif
-
-#ifndef protected
-# define protected /* Redefined from elsewhere to "static" */
- /* When we want to hide everything */
-#endif
+/* If your compiler does not support this, define it to be empty. */
+#define libedit_private __attribute__((__visibility__("hidden")))
#ifndef __arraycount
# define __arraycount(a) (sizeof(a) / sizeof(*(a)))
#endif
-#ifndef HAVE_U_INT32_T
-typedef unsigned int u_int32_t;
-#endif
-
-#ifndef _PTR_T
-# define _PTR_T
-typedef void *ptr_t;
-#endif
-
-#ifndef _IOCTL_T
-# define _IOCTL_T
-typedef void *ioctl_t;
-#endif
-
#include <stdio.h>
#ifndef HAVE_STRLCAT
@@ -105,57 +81,28 @@ size_t strlcat(char *dst, const char *src, size_t size);
size_t strlcpy(char *dst, const char *src, size_t size);
#endif
-#ifndef HAVE_FGETLN
-#define fgetln libedit_fgetln
-char *fgetln(FILE *fp, size_t *len);
+#ifndef HAVE_GETLINE
+#define getline libedit_getline
+ssize_t getline(char **line, size_t *len, FILE *fp);
#endif
-#define REGEX /* Use POSIX.2 regular expression functions */
-#undef REGEXP /* Use UNIX V8 regular expression functions */
+#ifndef _DIAGASSERT
+#define _DIAGASSERT(x)
+#endif
-#if defined(__sun)
-extern int tgetent(char *, const char *);
-extern int tgetflag(char *);
-extern int tgetnum(char *);
-extern int tputs(const char *, int, int (*)(int));
-extern char* tgoto(const char*, int, int);
-extern char* tgetstr(char*, char**);
+#ifndef __RCSID
+#define __RCSID(x)
#endif
-#ifdef notdef
-# undef REGEX
-# undef REGEXP
-# include <malloc.h>
-# ifdef __GNUC__
-/*
- * Broken hdrs.
- */
-extern int tgetent(const char *bp, char *name);
-extern int tgetflag(const char *id);
-extern int tgetnum(const char *id);
-extern char *tgetstr(const char *id, char **area);
-extern char *tgoto(const char *cap, int col, int row);
-extern int tputs(const char *str, int affcnt, int (*putc)(int));
-extern char *getenv(const char *);
-extern int fprintf(FILE *, const char *, ...);
-extern int sigsetmask(int);
-extern int sigblock(int);
-extern int fputc(int, FILE *);
-extern int fgetc(FILE *);
-extern int fflush(FILE *);
-extern int tolower(int);
-extern int toupper(int);
-extern int errno, sys_nerr;
-extern char *sys_errlist[];
-extern void perror(const char *);
-# include <string.h>
-# define strerror(e) sys_errlist[e]
-# endif
-# ifdef SABER
-extern ptr_t memcpy(ptr_t, const ptr_t, size_t);
-extern ptr_t memset(ptr_t, int, size_t);
-# endif
-extern char *fgetline(FILE *, int *);
+#ifndef HAVE_U_INT32_T
+typedef unsigned int u_int32_t;
+#endif
+
+#ifndef HAVE_SIZE_MAX
+#define SIZE_MAX ((size_t)-1)
#endif
+#define REGEX /* Use POSIX.2 regular expression functions */
+#undef REGEXP /* Use UNIX V8 regular expression functions */
+
#endif /* _h_sys */
diff --git a/lib/libedit/src/term.c b/lib/libedit/src/terminal.c
index 3a1b5e739ed0..26838e813d65 100644
--- a/lib/libedit/src/term.c
+++ b/lib/libedit/src/terminal.c
@@ -1,4 +1,4 @@
-/* $NetBSD: term.c,v 1.57 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: terminal.c,v 1.32 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,21 +37,23 @@
#if 0
static char sccsid[] = "@(#)term.c 8.2 (Berkeley) 4/30/95";
#else
-__RCSID("$NetBSD: term.c,v 1.57 2009/12/30 22:37:40 christos Exp $");
+__RCSID("$NetBSD: terminal.c,v 1.32 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
- * term.c: Editor/termcap-curses interface
- * We have to declare a static variable here, since the
- * termcap putchar routine does not take an argument!
+ * terminal.c: Editor/termcap-curses interface
+ * We have to declare a static variable here, since the
+ * termcap putchar routine does not take an argument!
*/
-#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <limits.h>
#include <signal.h>
-#include <string.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
-#include <limits.h>
#ifdef HAVE_TERMCAP_H
#include <termcap.h>
#endif
@@ -60,19 +62,18 @@ __RCSID("$NetBSD: term.c,v 1.57 2009/12/30 22:37:40 christos Exp $");
#elif HAVE_NCURSES_H
#include <ncurses.h>
#endif
-/* Solaris's term.h does horrid things. */
-#if defined(HAVE_TERM_H) && !defined(__sun)
+/* Solaris's term.h does horrid things. */
+#if defined(HAVE_TERM_H) && !defined(__sun) && !defined(HAVE_TERMCAP_H)
#include <term.h>
#endif
-#include <sys/types.h>
-#include <sys/ioctl.h>
#ifdef _REENTRANT
#include <pthread.h>
#endif
#include "el.h"
+#include "fcns.h"
/*
* IMPORTANT NOTE: these routines are allowed to look at the current screen
@@ -81,86 +82,14 @@ __RCSID("$NetBSD: term.c,v 1.57 2009/12/30 22:37:40 christos Exp $");
* assumption...
*/
-#define TC_BUFSIZE 2048
+#define TC_BUFSIZE ((size_t)2048)
-#define GoodStr(a) (el->el_term.t_str[a] != NULL && \
- el->el_term.t_str[a][0] != '\0')
-#define Str(a) el->el_term.t_str[a]
-#define Val(a) el->el_term.t_val[a]
+#define GoodStr(a) (el->el_terminal.t_str[a] != NULL && \
+ el->el_terminal.t_str[a][0] != '\0')
+#define Str(a) el->el_terminal.t_str[a]
+#define Val(a) el->el_terminal.t_val[a]
-#ifdef notdef
-private const struct {
- const char *b_name;
- int b_rate;
-} baud_rate[] = {
-#ifdef B0
- { "0", B0 },
-#endif
-#ifdef B50
- { "50", B50 },
-#endif
-#ifdef B75
- { "75", B75 },
-#endif
-#ifdef B110
- { "110", B110 },
-#endif
-#ifdef B134
- { "134", B134 },
-#endif
-#ifdef B150
- { "150", B150 },
-#endif
-#ifdef B200
- { "200", B200 },
-#endif
-#ifdef B300
- { "300", B300 },
-#endif
-#ifdef B600
- { "600", B600 },
-#endif
-#ifdef B900
- { "900", B900 },
-#endif
-#ifdef B1200
- { "1200", B1200 },
-#endif
-#ifdef B1800
- { "1800", B1800 },
-#endif
-#ifdef B2400
- { "2400", B2400 },
-#endif
-#ifdef B3600
- { "3600", B3600 },
-#endif
-#ifdef B4800
- { "4800", B4800 },
-#endif
-#ifdef B7200
- { "7200", B7200 },
-#endif
-#ifdef B9600
- { "9600", B9600 },
-#endif
-#ifdef EXTA
- { "19200", EXTA },
-#endif
-#ifdef B19200
- { "19200", B19200 },
-#endif
-#ifdef EXTB
- { "38400", EXTB },
-#endif
-#ifdef B38400
- { "38400", B38400 },
-#endif
- { NULL, 0 }
-};
-#endif
-
-private const struct termcapstr {
+static const struct termcapstr {
const char *name;
const char *long_name;
} tstr[] = {
@@ -240,11 +169,13 @@ private const struct termcapstr {
{ "kh", "send cursor home" },
#define T_at7 37
{ "@7", "send cursor end" },
-#define T_str 38
+#define T_kD 38
+ { "kD", "send cursor delete" },
+#define T_str 39
{ NULL, NULL }
};
-private const struct termcapval {
+static const struct termcapval {
const char *name;
const char *long_name;
} tval[] = {
@@ -269,27 +200,28 @@ private const struct termcapval {
};
/* do two or more of the attributes use me */
-private void term_setflags(EditLine *);
-private int term_rebuffer_display(EditLine *);
-private void term_free_display(EditLine *);
-private int term_alloc_display(EditLine *);
-private void term_alloc(EditLine *, const struct termcapstr *, const char *);
-private void term_init_arrow(EditLine *);
-private void term_reset_arrow(EditLine *);
-private int term_putc(int);
-private void term_tputs(EditLine *, const char *, int);
+static void terminal_setflags(EditLine *);
+static int terminal_rebuffer_display(EditLine *);
+static void terminal_free_display(EditLine *);
+static int terminal_alloc_display(EditLine *);
+static void terminal_alloc(EditLine *, const struct termcapstr *,
+ const char *);
+static void terminal_init_arrow(EditLine *);
+static void terminal_reset_arrow(EditLine *);
+static int terminal_putc(int);
+static void terminal_tputs(EditLine *, const char *, int);
#ifdef _REENTRANT
-private pthread_mutex_t term_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t terminal_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
-private FILE *term_outfile = NULL;
+static FILE *terminal_outfile = NULL;
-/* term_setflags():
+/* terminal_setflags():
* Set the terminal capability flags
*/
-private void
-term_setflags(EditLine *el)
+static void
+terminal_setflags(EditLine *el)
{
EL_FLAGS = 0;
if (el->el_tty.t_tabs)
@@ -330,69 +262,91 @@ term_setflags(EditLine *el)
#endif /* DEBUG_SCREEN */
}
-/* term_init():
+/* terminal_init():
* Initialize the terminal stuff
*/
-protected int
-term_init(EditLine *el)
+libedit_private int
+terminal_init(EditLine *el)
{
- el->el_term.t_buf = (char *) el_malloc(TC_BUFSIZE);
- if (el->el_term.t_buf == NULL)
- return (-1);
- el->el_term.t_cap = (char *) el_malloc(TC_BUFSIZE);
- if (el->el_term.t_cap == NULL)
- return (-1);
- el->el_term.t_fkey = (fkey_t *) el_malloc(A_K_NKEYS * sizeof(fkey_t));
- if (el->el_term.t_fkey == NULL)
- return (-1);
- el->el_term.t_loc = 0;
- el->el_term.t_str = (char **) el_malloc(T_str * sizeof(char *));
- if (el->el_term.t_str == NULL)
- return (-1);
- (void) memset(el->el_term.t_str, 0, T_str * sizeof(char *));
- el->el_term.t_val = (int *) el_malloc(T_val * sizeof(int));
- if (el->el_term.t_val == NULL)
- return (-1);
- (void) memset(el->el_term.t_val, 0, T_val * sizeof(int));
- (void) term_set(el, NULL);
- term_init_arrow(el);
- return (0);
+ el->el_terminal.t_buf = el_malloc(TC_BUFSIZE *
+ sizeof(*el->el_terminal.t_buf));
+ if (el->el_terminal.t_buf == NULL)
+ goto fail1;
+ el->el_terminal.t_cap = el_malloc(TC_BUFSIZE *
+ sizeof(*el->el_terminal.t_cap));
+ if (el->el_terminal.t_cap == NULL)
+ goto fail2;
+ el->el_terminal.t_fkey = el_malloc(A_K_NKEYS *
+ sizeof(*el->el_terminal.t_fkey));
+ if (el->el_terminal.t_fkey == NULL)
+ goto fail3;
+ el->el_terminal.t_loc = 0;
+ el->el_terminal.t_str = el_malloc(T_str *
+ sizeof(*el->el_terminal.t_str));
+ if (el->el_terminal.t_str == NULL)
+ goto fail4;
+ (void) memset(el->el_terminal.t_str, 0, T_str *
+ sizeof(*el->el_terminal.t_str));
+ el->el_terminal.t_val = el_malloc(T_val *
+ sizeof(*el->el_terminal.t_val));
+ if (el->el_terminal.t_val == NULL)
+ goto fail5;
+ (void) memset(el->el_terminal.t_val, 0, T_val *
+ sizeof(*el->el_terminal.t_val));
+ (void) terminal_set(el, NULL);
+ terminal_init_arrow(el);
+ return 0;
+fail5:
+ free(el->el_terminal.t_str);
+ el->el_terminal.t_str = NULL;
+fail4:
+ free(el->el_terminal.t_fkey);
+ el->el_terminal.t_fkey = NULL;
+fail3:
+ free(el->el_terminal.t_cap);
+ el->el_terminal.t_cap = NULL;
+fail2:
+ free(el->el_terminal.t_buf);
+ el->el_terminal.t_buf = NULL;
+fail1:
+ return -1;
}
-/* term_end():
+/* terminal_end():
* Clean up the terminal stuff
*/
-protected void
-term_end(EditLine *el)
+libedit_private void
+terminal_end(EditLine *el)
{
- el_free((ptr_t) el->el_term.t_buf);
- el->el_term.t_buf = NULL;
- el_free((ptr_t) el->el_term.t_cap);
- el->el_term.t_cap = NULL;
- el->el_term.t_loc = 0;
- el_free((ptr_t) el->el_term.t_str);
- el->el_term.t_str = NULL;
- el_free((ptr_t) el->el_term.t_val);
- el->el_term.t_val = NULL;
- el_free((ptr_t) el->el_term.t_fkey);
- el->el_term.t_fkey = NULL;
- term_free_display(el);
+ el_free(el->el_terminal.t_buf);
+ el->el_terminal.t_buf = NULL;
+ el_free(el->el_terminal.t_cap);
+ el->el_terminal.t_cap = NULL;
+ el->el_terminal.t_loc = 0;
+ el_free(el->el_terminal.t_str);
+ el->el_terminal.t_str = NULL;
+ el_free(el->el_terminal.t_val);
+ el->el_terminal.t_val = NULL;
+ el_free(el->el_terminal.t_fkey);
+ el->el_terminal.t_fkey = NULL;
+ terminal_free_display(el);
}
-/* term_alloc():
+/* terminal_alloc():
* Maintain a string pool for termcap strings
*/
-private void
-term_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
+static void
+terminal_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
{
char termbuf[TC_BUFSIZE];
size_t tlen, clen;
- char **tlist = el->el_term.t_str;
+ char **tlist = el->el_terminal.t_str;
char **tmp, **str = &tlist[t - tstr];
+ (void) memset(termbuf, 0, sizeof(termbuf));
if (cap == NULL || *cap == '\0') {
*str = NULL;
return;
@@ -412,11 +366,11 @@ term_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
/*
* New string is longer; see if we have enough space to append
*/
- if (el->el_term.t_loc + 3 < TC_BUFSIZE) {
+ if (el->el_terminal.t_loc + 3 < TC_BUFSIZE) {
/* XXX strcpy is safe */
- (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc],
- cap);
- el->el_term.t_loc += (int)clen + 1; /* one for \0 */
+ (void) strcpy(*str = &el->el_terminal.t_buf[
+ el->el_terminal.t_loc], cap);
+ el->el_terminal.t_loc += clen + 1; /* one for \0 */
return;
}
/*
@@ -425,131 +379,135 @@ term_alloc(EditLine *el, const struct termcapstr *t, const char *cap)
*/
tlen = 0;
for (tmp = tlist; tmp < &tlist[T_str]; tmp++)
- if (*tmp != NULL && *tmp != '\0' && *tmp != *str) {
+ if (*tmp != NULL && **tmp != '\0' && *tmp != *str) {
char *ptr;
for (ptr = *tmp; *ptr != '\0'; termbuf[tlen++] = *ptr++)
continue;
termbuf[tlen++] = '\0';
}
- memcpy(el->el_term.t_buf, termbuf, TC_BUFSIZE);
- el->el_term.t_loc = (int)tlen;
- if (el->el_term.t_loc + 3 >= TC_BUFSIZE) {
+ memcpy(el->el_terminal.t_buf, termbuf, TC_BUFSIZE);
+ el->el_terminal.t_loc = tlen;
+ if (el->el_terminal.t_loc + 3 >= TC_BUFSIZE) {
(void) fprintf(el->el_errfile,
"Out of termcap string space.\n");
return;
}
/* XXX strcpy is safe */
- (void) strcpy(*str = &el->el_term.t_buf[el->el_term.t_loc], cap);
- el->el_term.t_loc += (int)clen + 1; /* one for \0 */
+ (void) strcpy(*str = &el->el_terminal.t_buf[el->el_terminal.t_loc],
+ cap);
+ el->el_terminal.t_loc += (size_t)clen + 1; /* one for \0 */
return;
}
-/* term_rebuffer_display():
+/* terminal_rebuffer_display():
* Rebuffer the display after the screen changed size
*/
-private int
-term_rebuffer_display(EditLine *el)
+static int
+terminal_rebuffer_display(EditLine *el)
{
- coord_t *c = &el->el_term.t_size;
+ coord_t *c = &el->el_terminal.t_size;
- term_free_display(el);
+ terminal_free_display(el);
c->h = Val(T_co);
c->v = Val(T_li);
- if (term_alloc_display(el) == -1)
- return (-1);
- return (0);
+ if (terminal_alloc_display(el) == -1)
+ return -1;
+ return 0;
}
-/* term_alloc_display():
+/* terminal_alloc_display():
* Allocate a new display.
*/
-private int
-term_alloc_display(EditLine *el)
+static int
+terminal_alloc_display(EditLine *el)
{
int i;
- Char **b;
- coord_t *c = &el->el_term.t_size;
+ wchar_t **b;
+ coord_t *c = &el->el_terminal.t_size;
- b = el_malloc(sizeof(*b) * (c->v + 1));
+ b = el_malloc(sizeof(*b) * (size_t)(c->v + 1));
if (b == NULL)
- return (-1);
+ goto done;
for (i = 0; i < c->v; i++) {
- b[i] = el_malloc(sizeof(**b) * (c->h + 1));
+ b[i] = el_malloc(sizeof(**b) * (size_t)(c->h + 1));
if (b[i] == NULL) {
while (--i >= 0)
- el_free((ptr_t) b[i]);
- el_free((ptr_t) b);
- return (-1);
+ el_free(b[i]);
+ el_free(b);
+ goto done;
}
}
b[c->v] = NULL;
el->el_display = b;
- b = el_malloc(sizeof(*b) * (c->v + 1));
+ b = el_malloc(sizeof(*b) * (size_t)(c->v + 1));
if (b == NULL)
- return (-1);
+ goto done;
for (i = 0; i < c->v; i++) {
- b[i] = el_malloc(sizeof(**b) * (c->h + 1));
+ b[i] = el_malloc(sizeof(**b) * (size_t)(c->h + 1));
if (b[i] == NULL) {
while (--i >= 0)
- el_free((ptr_t) b[i]);
- el_free((ptr_t) b);
- return (-1);
+ el_free(b[i]);
+ el_free(b);
+ goto done;
}
}
b[c->v] = NULL;
el->el_vdisplay = b;
- return (0);
+ return 0;
+done:
+ terminal_free_display(el);
+ return -1;
}
-/* term_free_display():
+/* terminal_free_display():
* Free the display buffers
*/
-private void
-term_free_display(EditLine *el)
+static void
+terminal_free_display(EditLine *el)
{
- Char **b;
- Char **bufp;
+ wchar_t **b;
+ wchar_t **bufp;
b = el->el_display;
el->el_display = NULL;
if (b != NULL) {
for (bufp = b; *bufp != NULL; bufp++)
- el_free((ptr_t) *bufp);
- el_free((ptr_t) b);
+ el_free(*bufp);
+ el_free(b);
}
b = el->el_vdisplay;
el->el_vdisplay = NULL;
if (b != NULL) {
for (bufp = b; *bufp != NULL; bufp++)
- el_free((ptr_t) *bufp);
- el_free((ptr_t) b);
+ el_free(*bufp);
+ el_free(b);
}
}
-/* term_move_to_line():
+/* terminal_move_to_line():
* move to line <where> (first line == 0)
- * as efficiently as possible
+ * as efficiently as possible
*/
-protected void
-term_move_to_line(EditLine *el, int where)
+libedit_private void
+terminal_move_to_line(EditLine *el, int where)
{
int del;
if (where == el->el_cursor.v)
return;
- if (where > el->el_term.t_size.v) {
+ if (where > el->el_terminal.t_size.v) {
#ifdef DEBUG_SCREEN
(void) fprintf(el->el_errfile,
- "term_move_to_line: where is ridiculous: %d\r\n", where);
+ "%s: where is ridiculous: %d\r\n", __func__, where);
#endif /* DEBUG_SCREEN */
return;
}
@@ -557,30 +515,29 @@ term_move_to_line(EditLine *el, int where)
while (del > 0) {
if (EL_HAS_AUTO_MARGINS &&
el->el_display[el->el_cursor.v][0] != '\0') {
- size_t h = el->el_term.t_size.h - 1;
-#ifdef WIDECHAR
+ size_t h = (size_t)
+ (el->el_terminal.t_size.h - 1);
for (; h > 0 &&
el->el_display[el->el_cursor.v][h] ==
MB_FILL_CHAR;
h--)
continue;
-#endif
/* move without newline */
- term_move_to_char(el, (int)h);
- term_overwrite(el, &el->el_display
+ terminal_move_to_char(el, (int)h);
+ terminal_overwrite(el, &el->el_display
[el->el_cursor.v][el->el_cursor.h],
- (size_t)(el->el_term.t_size.h -
+ (size_t)(el->el_terminal.t_size.h -
el->el_cursor.h));
/* updates Cursor */
del--;
} else {
if ((del > 1) && GoodStr(T_DO)) {
- term_tputs(el, tgoto(Str(T_DO), del,
+ terminal_tputs(el, tgoto(Str(T_DO), del,
del), del);
del = 0;
} else {
for (; del > 0; del--)
- term__putc(el, '\n');
+ terminal__putc(el, '\n');
/* because the \n will become \r\n */
el->el_cursor.h = 0;
}
@@ -588,22 +545,22 @@ term_move_to_line(EditLine *el, int where)
}
} else { /* del < 0 */
if (GoodStr(T_UP) && (-del > 1 || !GoodStr(T_up)))
- term_tputs(el, tgoto(Str(T_UP), -del, -del), -del);
+ terminal_tputs(el, tgoto(Str(T_UP), -del, -del), -del);
else {
if (GoodStr(T_up))
for (; del < 0; del++)
- term_tputs(el, Str(T_up), 1);
+ terminal_tputs(el, Str(T_up), 1);
}
}
el->el_cursor.v = where;/* now where is here */
}
-/* term_move_to_char():
+/* terminal_move_to_char():
* Move to the character position specified
*/
-protected void
-term_move_to_char(EditLine *el, int where)
+libedit_private void
+terminal_move_to_char(EditLine *el, int where)
{
int del, i;
@@ -611,15 +568,15 @@ mc_again:
if (where == el->el_cursor.h)
return;
- if (where > el->el_term.t_size.h) {
+ if (where > el->el_terminal.t_size.h) {
#ifdef DEBUG_SCREEN
(void) fprintf(el->el_errfile,
- "term_move_to_char: where is riduculous: %d\r\n", where);
+ "%s: where is ridiculous: %d\r\n", __func__, where);
#endif /* DEBUG_SCREEN */
return;
}
if (!where) { /* if where is first column */
- term__putc(el, '\r'); /* do a CR */
+ terminal__putc(el, '\r'); /* do a CR */
el->el_cursor.h = 0;
return;
}
@@ -627,28 +584,28 @@ mc_again:
if ((del < -4 || del > 4) && GoodStr(T_ch))
/* go there directly */
- term_tputs(el, tgoto(Str(T_ch), where, where), where);
+ terminal_tputs(el, tgoto(Str(T_ch), where, where), where);
else {
if (del > 0) { /* moving forward */
if ((del > 4) && GoodStr(T_RI))
- term_tputs(el, tgoto(Str(T_RI), del, del), del);
+ terminal_tputs(el, tgoto(Str(T_RI), del, del),
+ del);
else {
/* if I can do tabs, use them */
if (EL_CAN_TAB) {
if ((el->el_cursor.h & 0370) !=
(where & ~0x7)
-#ifdef WIDECHAR
&& (el->el_display[
el->el_cursor.v][where & 0370] !=
MB_FILL_CHAR)
-#endif
) {
/* if not within tab stop */
for (i =
(el->el_cursor.h & 0370);
i < (where & ~0x7);
i += 8)
- term__putc(el, '\t');
+ terminal__putc(el,
+ '\t');
/* then tab over */
el->el_cursor.h = where & ~0x7;
}
@@ -658,17 +615,17 @@ mc_again:
* chars, so we do.
*/
/*
- * NOTE THAT term_overwrite() WILL CHANGE
+ * NOTE THAT terminal_overwrite() WILL CHANGE
* el->el_cursor.h!!!
*/
- term_overwrite(el, &el->el_display[
+ terminal_overwrite(el, &el->el_display[
el->el_cursor.v][el->el_cursor.h],
(size_t)(where - el->el_cursor.h));
}
} else { /* del < 0 := moving backward */
if ((-del > 4) && GoodStr(T_LE))
- term_tputs(el, tgoto(Str(T_LE), -del, -del),
+ terminal_tputs(el, tgoto(Str(T_LE), -del, -del),
-del);
else { /* can't go directly there */
/*
@@ -680,12 +637,12 @@ mc_again:
(((unsigned int) where >> 3) +
(where & 07)))
: (-del > where)) {
- term__putc(el, '\r'); /* do a CR */
+ terminal__putc(el, '\r');/* do a CR */
el->el_cursor.h = 0;
goto mc_again; /* and try again */
}
for (i = 0; i < -del; i++)
- term__putc(el, '\b');
+ terminal__putc(el, '\b');
}
}
}
@@ -693,62 +650,60 @@ mc_again:
}
-/* term_overwrite():
+/* terminal_overwrite():
* Overstrike num characters
* Assumes MB_FILL_CHARs are present to keep the column count correct
*/
-protected void
-term_overwrite(EditLine *el, const Char *cp, size_t n)
+libedit_private void
+terminal_overwrite(EditLine *el, const wchar_t *cp, size_t n)
{
if (n == 0)
return;
- if (n > (size_t)el->el_term.t_size.h) {
+ if (n > (size_t)el->el_terminal.t_size.h) {
#ifdef DEBUG_SCREEN
(void) fprintf(el->el_errfile,
- "term_overwrite: n is riduculous: %d\r\n", n);
+ "%s: n is ridiculous: %zu\r\n", __func__, n);
#endif /* DEBUG_SCREEN */
return;
}
do {
- /* term__putc() ignores any MB_FILL_CHARs */
- term__putc(el, *cp++);
+ /* terminal__putc() ignores any MB_FILL_CHARs */
+ terminal__putc(el, *cp++);
el->el_cursor.h++;
} while (--n);
- if (el->el_cursor.h >= el->el_term.t_size.h) { /* wrap? */
+ if (el->el_cursor.h >= el->el_terminal.t_size.h) { /* wrap? */
if (EL_HAS_AUTO_MARGINS) { /* yes */
el->el_cursor.h = 0;
el->el_cursor.v++;
if (EL_HAS_MAGIC_MARGINS) {
/* force the wrap to avoid the "magic"
* situation */
- Char c;
+ wchar_t c;
if ((c = el->el_display[el->el_cursor.v]
[el->el_cursor.h]) != '\0') {
- term_overwrite(el, &c, 1);
-#ifdef WIDECHAR
+ terminal_overwrite(el, &c, (size_t)1);
while (el->el_display[el->el_cursor.v]
[el->el_cursor.h] == MB_FILL_CHAR)
el->el_cursor.h++;
-#endif
} else {
- term__putc(el, ' ');
+ terminal__putc(el, ' ');
el->el_cursor.h = 1;
}
}
} else /* no wrap, but cursor stays on screen */
- el->el_cursor.h = el->el_term.t_size.h - 1;
+ el->el_cursor.h = el->el_terminal.t_size.h - 1;
}
}
-/* term_deletechars():
+/* terminal_deletechars():
* Delete num characters
*/
-protected void
-term_deletechars(EditLine *el, int num)
+libedit_private void
+terminal_deletechars(EditLine *el, int num)
{
if (num <= 0)
return;
@@ -759,38 +714,38 @@ term_deletechars(EditLine *el, int num)
#endif /* DEBUG_EDIT */
return;
}
- if (num > el->el_term.t_size.h) {
+ if (num > el->el_terminal.t_size.h) {
#ifdef DEBUG_SCREEN
(void) fprintf(el->el_errfile,
- "term_deletechars: num is riduculous: %d\r\n", num);
+ "%s: num is ridiculous: %d\r\n", __func__, num);
#endif /* DEBUG_SCREEN */
return;
}
if (GoodStr(T_DC)) /* if I have multiple delete */
if ((num > 1) || !GoodStr(T_dc)) { /* if dc would be more
* expen. */
- term_tputs(el, tgoto(Str(T_DC), num, num), num);
+ terminal_tputs(el, tgoto(Str(T_DC), num, num), num);
return;
}
if (GoodStr(T_dm)) /* if I have delete mode */
- term_tputs(el, Str(T_dm), 1);
+ terminal_tputs(el, Str(T_dm), 1);
if (GoodStr(T_dc)) /* else do one at a time */
while (num--)
- term_tputs(el, Str(T_dc), 1);
+ terminal_tputs(el, Str(T_dc), 1);
if (GoodStr(T_ed)) /* if I have delete mode */
- term_tputs(el, Str(T_ed), 1);
+ terminal_tputs(el, Str(T_ed), 1);
}
-/* term_insertwrite():
+/* terminal_insertwrite():
* Puts terminal in insert character mode or inserts num
* characters in the line
* Assumes MB_FILL_CHARs are present to keep column count correct
*/
-protected void
-term_insertwrite(EditLine *el, Char *cp, int num)
+libedit_private void
+terminal_insertwrite(EditLine *el, wchar_t *cp, int num)
{
if (num <= 0)
return;
@@ -800,130 +755,116 @@ term_insertwrite(EditLine *el, Char *cp, int num)
#endif /* DEBUG_EDIT */
return;
}
- if (num > el->el_term.t_size.h) {
+ if (num > el->el_terminal.t_size.h) {
#ifdef DEBUG_SCREEN
(void) fprintf(el->el_errfile,
- "StartInsert: num is riduculous: %d\r\n", num);
+ "%s: num is ridiculous: %d\r\n", __func__, num);
#endif /* DEBUG_SCREEN */
return;
}
if (GoodStr(T_IC)) /* if I have multiple insert */
if ((num > 1) || !GoodStr(T_ic)) {
/* if ic would be more expensive */
- term_tputs(el, tgoto(Str(T_IC), num, num), num);
- term_overwrite(el, cp, (size_t)num);
+ terminal_tputs(el, tgoto(Str(T_IC), num, num), num);
+ terminal_overwrite(el, cp, (size_t)num);
/* this updates el_cursor.h */
return;
}
if (GoodStr(T_im) && GoodStr(T_ei)) { /* if I have insert mode */
- term_tputs(el, Str(T_im), 1);
+ terminal_tputs(el, Str(T_im), 1);
el->el_cursor.h += num;
do
- term__putc(el, *cp++);
+ terminal__putc(el, *cp++);
while (--num);
if (GoodStr(T_ip)) /* have to make num chars insert */
- term_tputs(el, Str(T_ip), 1);
+ terminal_tputs(el, Str(T_ip), 1);
- term_tputs(el, Str(T_ei), 1);
+ terminal_tputs(el, Str(T_ei), 1);
return;
}
do {
if (GoodStr(T_ic)) /* have to make num chars insert */
- term_tputs(el, Str(T_ic), 1);
+ terminal_tputs(el, Str(T_ic), 1);
- term__putc(el, *cp++);
+ terminal__putc(el, *cp++);
el->el_cursor.h++;
if (GoodStr(T_ip)) /* have to make num chars insert */
- term_tputs(el, Str(T_ip), 1);
+ terminal_tputs(el, Str(T_ip), 1);
/* pad the inserted char */
} while (--num);
}
-/* term_clear_EOL():
+/* terminal_clear_EOL():
* clear to end of line. There are num characters to clear
*/
-protected void
-term_clear_EOL(EditLine *el, int num)
+libedit_private void
+terminal_clear_EOL(EditLine *el, int num)
{
int i;
if (EL_CAN_CEOL && GoodStr(T_ce))
- term_tputs(el, Str(T_ce), 1);
+ terminal_tputs(el, Str(T_ce), 1);
else {
for (i = 0; i < num; i++)
- term__putc(el, ' ');
+ terminal__putc(el, ' ');
el->el_cursor.h += num; /* have written num spaces */
}
}
-/* term_clear_screen():
+/* terminal_clear_screen():
* Clear the screen
*/
-protected void
-term_clear_screen(EditLine *el)
+libedit_private void
+terminal_clear_screen(EditLine *el)
{ /* clear the whole screen and home */
if (GoodStr(T_cl))
/* send the clear screen code */
- term_tputs(el, Str(T_cl), Val(T_li));
+ terminal_tputs(el, Str(T_cl), Val(T_li));
else if (GoodStr(T_ho) && GoodStr(T_cd)) {
- term_tputs(el, Str(T_ho), Val(T_li)); /* home */
+ terminal_tputs(el, Str(T_ho), Val(T_li)); /* home */
/* clear to bottom of screen */
- term_tputs(el, Str(T_cd), Val(T_li));
+ terminal_tputs(el, Str(T_cd), Val(T_li));
} else {
- term__putc(el, '\r');
- term__putc(el, '\n');
+ terminal__putc(el, '\r');
+ terminal__putc(el, '\n');
}
}
-/* term_beep():
+/* terminal_beep():
* Beep the way the terminal wants us
*/
-protected void
-term_beep(EditLine *el)
+libedit_private void
+terminal_beep(EditLine *el)
{
if (GoodStr(T_bl))
/* what termcap says we should use */
- term_tputs(el, Str(T_bl), 1);
+ terminal_tputs(el, Str(T_bl), 1);
else
- term__putc(el, '\007'); /* an ASCII bell; ^G */
+ terminal__putc(el, '\007'); /* an ASCII bell; ^G */
}
-#ifdef notdef
-/* term_clear_to_bottom():
- * Clear to the bottom of the screen
- */
-protected void
-term_clear_to_bottom(EditLine *el)
-{
- if (GoodStr(T_cd))
- term_tputs(el, Str(T_cd), Val(T_li));
- else if (GoodStr(T_ce))
- term_tputs(el, Str(T_ce), Val(T_li));
-}
-#endif
-
-protected void
-term_get(EditLine *el, const char **term)
+libedit_private void
+terminal_get(EditLine *el, const char **term)
{
- *term = el->el_term.t_name;
+ *term = el->el_terminal.t_name;
}
-/* term_set():
+/* terminal_set():
* Read in the terminal capabilities from the requested terminal
*/
-protected int
-term_set(EditLine *el, const char *term)
+libedit_private int
+terminal_set(EditLine *el, const char *term)
{
int i;
char buf[TC_BUFSIZE];
@@ -948,9 +889,9 @@ term_set(EditLine *el, const char *term)
if (strcmp(term, "emacs") == 0)
el->el_flags |= EDIT_DISABLED;
- memset(el->el_term.t_cap, 0, TC_BUFSIZE);
+ (void) memset(el->el_terminal.t_cap, 0, TC_BUFSIZE);
- i = tgetent(el->el_term.t_cap, term);
+ i = tgetent(el->el_terminal.t_cap, term);
if (i <= 0) {
if (i == -1)
@@ -965,7 +906,7 @@ term_set(EditLine *el, const char *term)
Val(T_pt) = Val(T_km) = Val(T_li) = 0;
Val(T_xt) = Val(T_MT);
for (t = tstr; t->name != NULL; t++)
- term_alloc(el, t, NULL);
+ terminal_alloc(el, t, NULL);
} else {
/* auto/magic margins */
Val(T_am) = tgetflag("am");
@@ -981,7 +922,7 @@ term_set(EditLine *el, const char *term)
Val(T_li) = tgetnum("li");
for (t = tstr; t->name != NULL; t++) {
/* XXX: some systems' tgetstr needs non const */
- term_alloc(el, t, tgetstr(strchr(t->name, *t->name),
+ terminal_alloc(el, t, tgetstr(strchr(t->name, *t->name),
&area));
}
}
@@ -991,28 +932,28 @@ term_set(EditLine *el, const char *term)
if (Val(T_li) < 1)
Val(T_li) = 24;
- el->el_term.t_size.v = Val(T_co);
- el->el_term.t_size.h = Val(T_li);
+ el->el_terminal.t_size.v = Val(T_co);
+ el->el_terminal.t_size.h = Val(T_li);
- term_setflags(el);
+ terminal_setflags(el);
/* get the correct window size */
- (void) term_get_size(el, &lins, &cols);
- if (term_change_size(el, lins, cols) == -1)
- return (-1);
+ (void) terminal_get_size(el, &lins, &cols);
+ if (terminal_change_size(el, lins, cols) == -1)
+ return -1;
(void) sigprocmask(SIG_SETMASK, &oset, NULL);
- term_bind_arrow(el);
- el->el_term.t_name = term;
- return (i <= 0 ? -1 : 0);
+ terminal_bind_arrow(el);
+ el->el_terminal.t_name = term;
+ return i <= 0 ? -1 : 0;
}
-/* term_get_size():
+/* terminal_get_size():
* Return the new window size in lines and cols, and
* true if the size was changed.
*/
-protected int
-term_get_size(EditLine *el, int *lins, int *cols)
+libedit_private int
+terminal_get_size(EditLine *el, int *lins, int *cols)
{
*cols = Val(T_co);
@@ -1021,7 +962,7 @@ term_get_size(EditLine *el, int *lins, int *cols)
#ifdef TIOCGWINSZ
{
struct winsize ws;
- if (ioctl(el->el_infd, TIOCGWINSZ, (ioctl_t) & ws) != -1) {
+ if (ioctl(el->el_infd, TIOCGWINSZ, &ws) != -1) {
if (ws.ws_col)
*cols = ws.ws_col;
if (ws.ws_row)
@@ -1032,7 +973,7 @@ term_get_size(EditLine *el, int *lins, int *cols)
#ifdef TIOCGSIZE
{
struct ttysize ts;
- if (ioctl(el->el_infd, TIOCGSIZE, (ioctl_t) & ts) != -1) {
+ if (ioctl(el->el_infd, TIOCGSIZE, &ts) != -1) {
if (ts.ts_cols)
*cols = ts.ts_cols;
if (ts.ts_lines)
@@ -1040,15 +981,15 @@ term_get_size(EditLine *el, int *lins, int *cols)
}
}
#endif
- return (Val(T_co) != *cols || Val(T_li) != *lins);
+ return Val(T_co) != *cols || Val(T_li) != *lins;
}
-/* term_change_size():
+/* terminal_change_size():
* Change the size of the terminal
*/
-protected int
-term_change_size(EditLine *el, int lins, int cols)
+libedit_private int
+terminal_change_size(EditLine *el, int lins, int cols)
{
/*
* Just in case
@@ -1057,184 +998,190 @@ term_change_size(EditLine *el, int lins, int cols)
Val(T_li) = (lins < 1) ? 24 : lins;
/* re-make display buffers */
- if (term_rebuffer_display(el) == -1)
- return (-1);
+ if (terminal_rebuffer_display(el) == -1)
+ return -1;
re_clear_display(el);
- return (0);
+ return 0;
}
-/* term_init_arrow():
+/* terminal_init_arrow():
* Initialize the arrow key bindings from termcap
*/
-private void
-term_init_arrow(EditLine *el)
+static void
+terminal_init_arrow(EditLine *el)
{
- fkey_t *arrow = el->el_term.t_fkey;
+ funckey_t *arrow = el->el_terminal.t_fkey;
- arrow[A_K_DN].name = STR("down");
+ arrow[A_K_DN].name = L"down";
arrow[A_K_DN].key = T_kd;
arrow[A_K_DN].fun.cmd = ED_NEXT_HISTORY;
arrow[A_K_DN].type = XK_CMD;
- arrow[A_K_UP].name = STR("up");
+ arrow[A_K_UP].name = L"up";
arrow[A_K_UP].key = T_ku;
arrow[A_K_UP].fun.cmd = ED_PREV_HISTORY;
arrow[A_K_UP].type = XK_CMD;
- arrow[A_K_LT].name = STR("left");
+ arrow[A_K_LT].name = L"left";
arrow[A_K_LT].key = T_kl;
arrow[A_K_LT].fun.cmd = ED_PREV_CHAR;
arrow[A_K_LT].type = XK_CMD;
- arrow[A_K_RT].name = STR("right");
+ arrow[A_K_RT].name = L"right";
arrow[A_K_RT].key = T_kr;
arrow[A_K_RT].fun.cmd = ED_NEXT_CHAR;
arrow[A_K_RT].type = XK_CMD;
- arrow[A_K_HO].name = STR("home");
+ arrow[A_K_HO].name = L"home";
arrow[A_K_HO].key = T_kh;
arrow[A_K_HO].fun.cmd = ED_MOVE_TO_BEG;
arrow[A_K_HO].type = XK_CMD;
- arrow[A_K_EN].name = STR("end");
+ arrow[A_K_EN].name = L"end";
arrow[A_K_EN].key = T_at7;
arrow[A_K_EN].fun.cmd = ED_MOVE_TO_END;
arrow[A_K_EN].type = XK_CMD;
+
+ arrow[A_K_DE].name = L"delete";
+ arrow[A_K_DE].key = T_kD;
+ arrow[A_K_DE].fun.cmd = ED_DELETE_NEXT_CHAR;
+ arrow[A_K_DE].type = XK_CMD;
}
-/* term_reset_arrow():
+/* terminal_reset_arrow():
* Reset arrow key bindings
*/
-private void
-term_reset_arrow(EditLine *el)
+static void
+terminal_reset_arrow(EditLine *el)
{
- fkey_t *arrow = el->el_term.t_fkey;
- static const Char strA[] = {033, '[', 'A', '\0'};
- static const Char strB[] = {033, '[', 'B', '\0'};
- static const Char strC[] = {033, '[', 'C', '\0'};
- static const Char strD[] = {033, '[', 'D', '\0'};
- static const Char strH[] = {033, '[', 'H', '\0'};
- static const Char strF[] = {033, '[', 'F', '\0'};
- static const Char stOA[] = {033, 'O', 'A', '\0'};
- static const Char stOB[] = {033, 'O', 'B', '\0'};
- static const Char stOC[] = {033, 'O', 'C', '\0'};
- static const Char stOD[] = {033, 'O', 'D', '\0'};
- static const Char stOH[] = {033, 'O', 'H', '\0'};
- static const Char stOF[] = {033, 'O', 'F', '\0'};
-
- key_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
- key_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
- key_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
- key_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
- key_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
- key_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
- key_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
- key_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
- key_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
- key_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
- key_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
- key_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
-
- if (el->el_map.type == MAP_VI) {
- key_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
- key_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
- key_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
- key_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
- key_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
- key_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
- key_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
- key_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
- key_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
- key_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
- key_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
- key_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
- }
+ funckey_t *arrow = el->el_terminal.t_fkey;
+ static const wchar_t strA[] = L"\033[A";
+ static const wchar_t strB[] = L"\033[B";
+ static const wchar_t strC[] = L"\033[C";
+ static const wchar_t strD[] = L"\033[D";
+ static const wchar_t strH[] = L"\033[H";
+ static const wchar_t strF[] = L"\033[F";
+ static const wchar_t stOA[] = L"\033OA";
+ static const wchar_t stOB[] = L"\033OB";
+ static const wchar_t stOC[] = L"\033OC";
+ static const wchar_t stOD[] = L"\033OD";
+ static const wchar_t stOH[] = L"\033OH";
+ static const wchar_t stOF[] = L"\033OF";
+
+ keymacro_add(el, strA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ keymacro_add(el, strB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ keymacro_add(el, strC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ keymacro_add(el, strD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ keymacro_add(el, strH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ keymacro_add(el, strF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ keymacro_add(el, stOA, &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ keymacro_add(el, stOB, &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ keymacro_add(el, stOC, &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ keymacro_add(el, stOD, &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ keymacro_add(el, stOH, &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ keymacro_add(el, stOF, &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+
+ if (el->el_map.type != MAP_VI)
+ return;
+ keymacro_add(el, &strA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ keymacro_add(el, &strB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ keymacro_add(el, &strC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ keymacro_add(el, &strD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ keymacro_add(el, &strH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ keymacro_add(el, &strF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
+ keymacro_add(el, &stOA[1], &arrow[A_K_UP].fun, arrow[A_K_UP].type);
+ keymacro_add(el, &stOB[1], &arrow[A_K_DN].fun, arrow[A_K_DN].type);
+ keymacro_add(el, &stOC[1], &arrow[A_K_RT].fun, arrow[A_K_RT].type);
+ keymacro_add(el, &stOD[1], &arrow[A_K_LT].fun, arrow[A_K_LT].type);
+ keymacro_add(el, &stOH[1], &arrow[A_K_HO].fun, arrow[A_K_HO].type);
+ keymacro_add(el, &stOF[1], &arrow[A_K_EN].fun, arrow[A_K_EN].type);
}
-/* term_set_arrow():
+/* terminal_set_arrow():
* Set an arrow key binding
*/
-protected int
-term_set_arrow(EditLine *el, const Char *name, key_value_t *fun, int type)
+libedit_private int
+terminal_set_arrow(EditLine *el, const wchar_t *name, keymacro_value_t *fun,
+ int type)
{
- fkey_t *arrow = el->el_term.t_fkey;
+ funckey_t *arrow = el->el_terminal.t_fkey;
int i;
for (i = 0; i < A_K_NKEYS; i++)
- if (Strcmp(name, arrow[i].name) == 0) {
+ if (wcscmp(name, arrow[i].name) == 0) {
arrow[i].fun = *fun;
arrow[i].type = type;
- return (0);
+ return 0;
}
- return (-1);
+ return -1;
}
-/* term_clear_arrow():
+/* terminal_clear_arrow():
* Clear an arrow key binding
*/
-protected int
-term_clear_arrow(EditLine *el, const Char *name)
+libedit_private int
+terminal_clear_arrow(EditLine *el, const wchar_t *name)
{
- fkey_t *arrow = el->el_term.t_fkey;
+ funckey_t *arrow = el->el_terminal.t_fkey;
int i;
for (i = 0; i < A_K_NKEYS; i++)
- if (Strcmp(name, arrow[i].name) == 0) {
+ if (wcscmp(name, arrow[i].name) == 0) {
arrow[i].type = XK_NOD;
- return (0);
+ return 0;
}
- return (-1);
+ return -1;
}
-/* term_print_arrow():
+/* terminal_print_arrow():
* Print the arrow key bindings
*/
-protected void
-term_print_arrow(EditLine *el, const Char *name)
+libedit_private void
+terminal_print_arrow(EditLine *el, const wchar_t *name)
{
int i;
- fkey_t *arrow = el->el_term.t_fkey;
+ funckey_t *arrow = el->el_terminal.t_fkey;
for (i = 0; i < A_K_NKEYS; i++)
- if (*name == '\0' || Strcmp(name, arrow[i].name) == 0)
+ if (*name == '\0' || wcscmp(name, arrow[i].name) == 0)
if (arrow[i].type != XK_NOD)
- key_kprint(el, arrow[i].name, &arrow[i].fun,
- arrow[i].type);
+ keymacro_kprint(el, arrow[i].name,
+ &arrow[i].fun, arrow[i].type);
}
-/* term_bind_arrow():
+/* terminal_bind_arrow():
* Bind the arrow keys
*/
-protected void
-term_bind_arrow(EditLine *el)
+libedit_private void
+terminal_bind_arrow(EditLine *el)
{
el_action_t *map;
const el_action_t *dmap;
int i, j;
char *p;
- fkey_t *arrow = el->el_term.t_fkey;
+ funckey_t *arrow = el->el_terminal.t_fkey;
/* Check if the components needed are initialized */
- if (el->el_term.t_buf == NULL || el->el_map.key == NULL)
+ if (el->el_terminal.t_buf == NULL || el->el_map.key == NULL)
return;
map = el->el_map.type == MAP_VI ? el->el_map.alt : el->el_map.key;
dmap = el->el_map.type == MAP_VI ? el->el_map.vic : el->el_map.emacs;
- term_reset_arrow(el);
+ terminal_reset_arrow(el);
for (i = 0; i < A_K_NKEYS; i++) {
- Char wt_str[VISUAL_WIDTH_MAX];
- Char *px;
+ wchar_t wt_str[VISUAL_WIDTH_MAX];
+ wchar_t *px;
size_t n;
- p = el->el_term.t_str[arrow[i].key];
+ p = el->el_terminal.t_str[arrow[i].key];
if (!p || !*p)
continue;
for (n = 0; n < VISUAL_WIDTH_MAX && p[n]; ++n)
@@ -1254,97 +1201,99 @@ term_bind_arrow(EditLine *el)
* unassigned key.
*/
if (arrow[i].type == XK_NOD)
- key_clear(el, map, px);
+ keymacro_clear(el, map, px);
else {
if (p[1] && (dmap[j] == map[j] ||
map[j] == ED_SEQUENCE_LEAD_IN)) {
- key_add(el, px, &arrow[i].fun,
+ keymacro_add(el, px, &arrow[i].fun,
arrow[i].type);
map[j] = ED_SEQUENCE_LEAD_IN;
} else if (map[j] == ED_UNASSIGNED) {
- key_clear(el, map, px);
+ keymacro_clear(el, map, px);
if (arrow[i].type == XK_CMD)
map[j] = arrow[i].fun.cmd;
else
- key_add(el, px, &arrow[i].fun,
+ keymacro_add(el, px, &arrow[i].fun,
arrow[i].type);
}
}
}
}
-/* term_putc():
+/* terminal_putc():
* Add a character
*/
-private int
-term_putc(int c)
+static int
+terminal_putc(int c)
{
- if (term_outfile == NULL)
+ if (terminal_outfile == NULL)
return -1;
- return fputc(c, term_outfile);
+ return fputc(c, terminal_outfile);
}
-private void
-term_tputs(EditLine *el, const char *cap, int affcnt)
+static void
+terminal_tputs(EditLine *el, const char *cap, int affcnt)
{
#ifdef _REENTRANT
- pthread_mutex_lock(&term_mutex);
+ pthread_mutex_lock(&terminal_mutex);
#endif
- term_outfile = el->el_outfile;
- (void)tputs(cap, affcnt, term_putc);
+ terminal_outfile = el->el_outfile;
+ (void)tputs(cap, affcnt, terminal_putc);
#ifdef _REENTRANT
- pthread_mutex_unlock(&term_mutex);
+ pthread_mutex_unlock(&terminal_mutex);
#endif
}
-/* term__putc():
+/* terminal__putc():
* Add a character
*/
-protected int
-term__putc(EditLine *el, Int c)
+libedit_private int
+terminal__putc(EditLine *el, wint_t c)
{
char buf[MB_LEN_MAX +1];
ssize_t i;
- if (c == MB_FILL_CHAR)
+ if (c == (wint_t)MB_FILL_CHAR)
return 0;
- i = ct_encode_char(buf, MB_LEN_MAX, c);
+ i = ct_encode_char(buf, (size_t)MB_LEN_MAX, c);
if (i <= 0)
return (int)i;
buf[i] = '\0';
return fputs(buf, el->el_outfile);
}
-/* term__flush():
+/* terminal__flush():
* Flush output
*/
-protected void
-term__flush(EditLine *el)
+libedit_private void
+terminal__flush(EditLine *el)
{
(void) fflush(el->el_outfile);
}
-/* term_writec():
+/* terminal_writec():
* Write the given character out, in a human readable form
*/
-protected void
-term_writec(EditLine *el, Int c)
+libedit_private void
+terminal_writec(EditLine *el, wint_t c)
{
- Char visbuf[VISUAL_WIDTH_MAX +1];
+ wchar_t visbuf[VISUAL_WIDTH_MAX +1];
ssize_t vcnt = ct_visual_char(visbuf, VISUAL_WIDTH_MAX, c);
+ if (vcnt < 0)
+ vcnt = 0;
visbuf[vcnt] = '\0';
- term_overwrite(el, visbuf, (size_t)vcnt);
- term__flush(el);
+ terminal_overwrite(el, visbuf, (size_t)vcnt);
+ terminal__flush(el);
}
-/* term_telltc():
+/* terminal_telltc():
* Print the current termcap characteristics
*/
-protected int
+libedit_private int
/*ARGSUSED*/
-term_telltc(EditLine *el, int argc __attribute__((__unused__)),
- const Char **argv __attribute__((__unused__)))
+terminal_telltc(EditLine *el, int argc __attribute__((__unused__)),
+ const wchar_t **argv __attribute__((__unused__)))
{
const struct termcapstr *t;
char **ts;
@@ -1363,12 +1312,12 @@ term_telltc(EditLine *el, int argc __attribute__((__unused__)),
(void) fprintf(el->el_outfile, "\tIt %s magic margins\n",
EL_HAS_MAGIC_MARGINS ? "has" : "does not have");
- for (t = tstr, ts = el->el_term.t_str; t->name != NULL; t++, ts++) {
+ for (t = tstr, ts = el->el_terminal.t_str; t->name != NULL; t++, ts++) {
const char *ub;
if (*ts && **ts) {
ub = ct_encode_string(ct_visual_string(
- ct_decode_string(*ts, &el->el_scratch)),
- &el->el_scratch);
+ ct_decode_string(*ts, &el->el_scratch),
+ &el->el_visual), &el->el_scratch);
} else {
ub = "(empty)";
}
@@ -1376,17 +1325,17 @@ term_telltc(EditLine *el, int argc __attribute__((__unused__)),
t->long_name, t->name, ub);
}
(void) fputc('\n', el->el_outfile);
- return (0);
+ return 0;
}
-/* term_settc():
+/* terminal_settc():
* Change the current terminal characteristics
*/
-protected int
+libedit_private int
/*ARGSUSED*/
-term_settc(EditLine *el, int argc __attribute__((__unused__)),
- const Char **argv)
+terminal_settc(EditLine *el, int argc __attribute__((__unused__)),
+ const wchar_t **argv)
{
const struct termcapstr *ts;
const struct termcapval *tv;
@@ -1408,8 +1357,8 @@ term_settc(EditLine *el, int argc __attribute__((__unused__)),
break;
if (ts->name != NULL) {
- term_alloc(el, ts, how);
- term_setflags(el);
+ terminal_alloc(el, ts, how);
+ terminal_setflags(el);
return 0;
}
/*
@@ -1425,16 +1374,16 @@ term_settc(EditLine *el, int argc __attribute__((__unused__)),
if (tv == &tval[T_pt] || tv == &tval[T_km] ||
tv == &tval[T_am] || tv == &tval[T_xn]) {
if (strcmp(how, "yes") == 0)
- el->el_term.t_val[tv - tval] = 1;
+ el->el_terminal.t_val[tv - tval] = 1;
else if (strcmp(how, "no") == 0)
- el->el_term.t_val[tv - tval] = 0;
+ el->el_terminal.t_val[tv - tval] = 0;
else {
(void) fprintf(el->el_errfile,
- "" FSTR ": Bad value `%s'.\n", argv[0], how);
+ "%ls: Bad value `%s'.\n", argv[0], how);
return -1;
}
- term_setflags(el);
- if (term_change_size(el, Val(T_li), Val(T_co)) == -1)
+ terminal_setflags(el);
+ if (terminal_change_size(el, Val(T_li), Val(T_co)) == -1)
return -1;
return 0;
} else {
@@ -1444,14 +1393,14 @@ term_settc(EditLine *el, int argc __attribute__((__unused__)),
i = strtol(how, &ep, 10);
if (*ep != '\0') {
(void) fprintf(el->el_errfile,
- "" FSTR ": Bad value `%s'.\n", argv[0], how);
+ "%ls: Bad value `%s'.\n", argv[0], how);
return -1;
}
- el->el_term.t_val[tv - tval] = (int) i;
- el->el_term.t_size.v = Val(T_co);
- el->el_term.t_size.h = Val(T_li);
+ el->el_terminal.t_val[tv - tval] = (int) i;
+ el->el_terminal.t_size.v = Val(T_co);
+ el->el_terminal.t_size.h = Val(T_li);
if (tv == &tval[T_co] || tv == &tval[T_li])
- if (term_change_size(el, Val(T_li), Val(T_co))
+ if (terminal_change_size(el, Val(T_li), Val(T_co))
== -1)
return -1;
return 0;
@@ -1459,12 +1408,12 @@ term_settc(EditLine *el, int argc __attribute__((__unused__)),
}
-/* term_gettc():
+/* terminal_gettc():
* Get the current terminal characteristics
*/
-protected int
+libedit_private int
/*ARGSUSED*/
-term_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
+terminal_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
{
const struct termcapstr *ts;
const struct termcapval *tv;
@@ -1472,7 +1421,7 @@ term_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
void *how;
if (argv == NULL || argv[1] == NULL || argv[2] == NULL)
- return (-1);
+ return -1;
what = argv[1];
how = argv[2];
@@ -1485,7 +1434,7 @@ term_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
break;
if (ts->name != NULL) {
- *(char **)how = el->el_term.t_str[ts - tstr];
+ *(char **)how = el->el_terminal.t_str[ts - tstr];
return 0;
}
/*
@@ -1502,27 +1451,27 @@ term_gettc(EditLine *el, int argc __attribute__((__unused__)), char **argv)
tv == &tval[T_am] || tv == &tval[T_xn]) {
static char yes[] = "yes";
static char no[] = "no";
- if (el->el_term.t_val[tv - tval])
+ if (el->el_terminal.t_val[tv - tval])
*(char **)how = yes;
else
*(char **)how = no;
return 0;
} else {
- *(int *)how = el->el_term.t_val[tv - tval];
+ *(int *)how = el->el_terminal.t_val[tv - tval];
return 0;
}
}
-/* term_echotc():
+/* terminal_echotc():
* Print the termcap string out with variable substitution
*/
-protected int
+libedit_private int
/*ARGSUSED*/
-term_echotc(EditLine *el, int argc __attribute__((__unused__)),
- const Char **argv)
+terminal_echotc(EditLine *el, int argc __attribute__((__unused__)),
+ const wchar_t **argv)
{
char *cap, *scap;
- Char *ep;
+ wchar_t *ep;
int arg_need, arg_cols, arg_rows;
int verbose = 0, silent = 0;
char *area;
@@ -1534,7 +1483,7 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
area = buf;
if (argv == NULL || argv[1] == NULL)
- return (-1);
+ return -1;
argv++;
if (argv[0][0] == '-') {
@@ -1552,43 +1501,31 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
argv++;
}
if (!*argv || *argv[0] == '\0')
- return (0);
- if (Strcmp(*argv, STR("tabs")) == 0) {
+ return 0;
+ if (wcscmp(*argv, L"tabs") == 0) {
(void) fprintf(el->el_outfile, fmts, EL_CAN_TAB ? "yes" : "no");
- return (0);
- } else if (Strcmp(*argv, STR("meta")) == 0) {
+ return 0;
+ } else if (wcscmp(*argv, L"meta") == 0) {
(void) fprintf(el->el_outfile, fmts, Val(T_km) ? "yes" : "no");
- return (0);
- } else if (Strcmp(*argv, STR("xn")) == 0) {
+ return 0;
+ } else if (wcscmp(*argv, L"xn") == 0) {
(void) fprintf(el->el_outfile, fmts, EL_HAS_MAGIC_MARGINS ?
"yes" : "no");
- return (0);
- } else if (Strcmp(*argv, STR("am")) == 0) {
+ return 0;
+ } else if (wcscmp(*argv, L"am") == 0) {
(void) fprintf(el->el_outfile, fmts, EL_HAS_AUTO_MARGINS ?
"yes" : "no");
- return (0);
- } else if (Strcmp(*argv, STR("baud")) == 0) {
-#ifdef notdef
- int i;
-
- for (i = 0; baud_rate[i].b_name != NULL; i++)
- if (el->el_tty.t_speed == baud_rate[i].b_rate) {
- (void) fprintf(el->el_outfile, fmts,
- baud_rate[i].b_name);
- return (0);
- }
- (void) fprintf(el->el_outfile, fmtd, 0);
-#else
+ return 0;
+ } else if (wcscmp(*argv, L"baud") == 0) {
(void) fprintf(el->el_outfile, fmtd, (int)el->el_tty.t_speed);
-#endif
- return (0);
- } else if (Strcmp(*argv, STR("rows")) == 0 ||
- Strcmp(*argv, STR("lines")) == 0) {
+ return 0;
+ } else if (wcscmp(*argv, L"rows") == 0 ||
+ wcscmp(*argv, L"lines") == 0) {
(void) fprintf(el->el_outfile, fmtd, Val(T_li));
- return (0);
- } else if (Strcmp(*argv, STR("cols")) == 0) {
+ return 0;
+ } else if (wcscmp(*argv, L"cols") == 0) {
(void) fprintf(el->el_outfile, fmtd, Val(T_co));
- return (0);
+ return 0;
}
/*
* Try to use our local definition first
@@ -1597,7 +1534,7 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
for (t = tstr; t->name != NULL; t++)
if (strcmp(t->name,
ct_encode_string(*argv, &el->el_scratch)) == 0) {
- scap = el->el_term.t_str[t - tstr];
+ scap = el->el_terminal.t_str[t - tstr];
break;
}
if (t->name == NULL) {
@@ -1607,9 +1544,9 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
if (!scap || scap[0] == '\0') {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Termcap parameter `" FSTR "' not found.\n",
+ "echotc: Termcap parameter `%ls' not found.\n",
*argv);
- return (-1);
+ return -1;
}
/*
* Count home many values we need for this capability.
@@ -1650,11 +1587,11 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
if (*argv && *argv[0]) {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Warning: Extra argument `" FSTR "'.\n",
+ "echotc: Warning: Extra argument `%ls'.\n",
*argv);
- return (-1);
+ return -1;
}
- term_tputs(el, scap, 1);
+ terminal_tputs(el, scap, 1);
break;
case 1:
argv++;
@@ -1662,27 +1599,27 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
if (!silent)
(void) fprintf(el->el_errfile,
"echotc: Warning: Missing argument.\n");
- return (-1);
+ return -1;
}
arg_cols = 0;
- i = Strtol(*argv, &ep, 10);
+ i = wcstol(*argv, &ep, 10);
if (*ep != '\0' || i < 0) {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Bad value `" FSTR "' for rows.\n",
+ "echotc: Bad value `%ls' for rows.\n",
*argv);
- return (-1);
+ return -1;
}
arg_rows = (int) i;
argv++;
if (*argv && *argv[0]) {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Warning: Extra argument `" FSTR "'.\n",
- *argv);
- return (-1);
+ "echotc: Warning: Extra argument `%ls"
+ "'.\n", *argv);
+ return -1;
}
- term_tputs(el, tgoto(scap, arg_cols, arg_rows), 1);
+ terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), 1);
break;
default:
/* This is wrong, but I will ignore it... */
@@ -1697,15 +1634,15 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
if (!silent)
(void) fprintf(el->el_errfile,
"echotc: Warning: Missing argument.\n");
- return (-1);
+ return -1;
}
- i = Strtol(*argv, &ep, 10);
+ i = wcstol(*argv, &ep, 10);
if (*ep != '\0' || i < 0) {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Bad value `" FSTR "' for cols.\n",
+ "echotc: Bad value `%ls' for cols.\n",
*argv);
- return (-1);
+ return -1;
}
arg_cols = (int) i;
argv++;
@@ -1713,33 +1650,33 @@ term_echotc(EditLine *el, int argc __attribute__((__unused__)),
if (!silent)
(void) fprintf(el->el_errfile,
"echotc: Warning: Missing argument.\n");
- return (-1);
+ return -1;
}
- i = Strtol(*argv, &ep, 10);
+ i = wcstol(*argv, &ep, 10);
if (*ep != '\0' || i < 0) {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Bad value `" FSTR "' for rows.\n",
+ "echotc: Bad value `%ls' for rows.\n",
*argv);
- return (-1);
+ return -1;
}
arg_rows = (int) i;
if (*ep != '\0') {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Bad value `" FSTR "'.\n", *argv);
- return (-1);
+ "echotc: Bad value `%ls'.\n", *argv);
+ return -1;
}
argv++;
if (*argv && *argv[0]) {
if (!silent)
(void) fprintf(el->el_errfile,
- "echotc: Warning: Extra argument `" FSTR "'.\n",
- *argv);
- return (-1);
+ "echotc: Warning: Extra argument `%ls"
+ "'.\n", *argv);
+ return -1;
}
- term_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows);
+ terminal_tputs(el, tgoto(scap, arg_cols, arg_rows), arg_rows);
break;
}
- return (0);
+ return 0;
}
diff --git a/lib/libedit/src/el_term.h b/lib/libedit/src/terminal.h
index c8572075f528..ae61beb1ecfc 100644
--- a/lib/libedit/src/el_term.h
+++ b/lib/libedit/src/terminal.h
@@ -1,4 +1,4 @@
-/* $NetBSD: term.h,v 1.21 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: terminal.h,v 1.9 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,17 +37,15 @@
/*
* el.term.h: Termcap header
*/
-#ifndef _h_el_term
-#define _h_el_term
-
-#include "histedit.h"
+#ifndef _h_el_terminal
+#define _h_el_terminal
typedef struct { /* Symbolic function key bindings */
- const Char *name; /* name of the key */
+ const wchar_t *name; /* name of the key */
int key; /* Index in termcap table */
- key_value_t fun; /* Function bound to it */
+ keymacro_value_t fun; /* Function bound to it */
int type; /* Type of function */
-} fkey_t;
+} funckey_t;
typedef struct {
const char *t_name; /* the terminal name */
@@ -63,12 +61,12 @@ typedef struct {
#define TERM_HAS_AUTO_MARGINS 0x080 /* Has auto margins */
#define TERM_HAS_MAGIC_MARGINS 0x100 /* Has magic margins */
char *t_buf; /* Termcap buffer */
- int t_loc; /* location used */
+ size_t t_loc; /* location used */
char **t_str; /* termcap strings */
int *t_val; /* termcap values */
char *t_cap; /* Termcap buffer */
- fkey_t *t_fkey; /* Array of keys */
-} el_term_t;
+ funckey_t *t_fkey; /* Array of keys */
+} el_terminal_t;
/*
* fKey indexes
@@ -79,47 +77,40 @@ typedef struct {
#define A_K_RT 3
#define A_K_HO 4
#define A_K_EN 5
-#define A_K_NKEYS 6
-
-#ifdef __sun
-extern int tgetent(char *, const char *);
-extern int tgetflag(char *);
-extern int tgetnum(char *);
-extern int tputs(const char *, int, int (*)(int));
-extern char* tgoto(const char*, int, int);
-extern char* tgetstr(char*, char**);
-#endif
+#define A_K_DE 6
+#define A_K_NKEYS 7
-protected void term_move_to_line(EditLine *, int);
-protected void term_move_to_char(EditLine *, int);
-protected void term_clear_EOL(EditLine *, int);
-protected void term_overwrite(EditLine *, const Char *, size_t);
-protected void term_insertwrite(EditLine *, Char *, int);
-protected void term_deletechars(EditLine *, int);
-protected void term_clear_screen(EditLine *);
-protected void term_beep(EditLine *);
-protected int term_change_size(EditLine *, int, int);
-protected int term_get_size(EditLine *, int *, int *);
-protected int term_init(EditLine *);
-protected void term_bind_arrow(EditLine *);
-protected void term_print_arrow(EditLine *, const Char *);
-protected int term_clear_arrow(EditLine *, const Char *);
-protected int term_set_arrow(EditLine *, const Char *, key_value_t *, int);
-protected void term_end(EditLine *);
-protected void term_get(EditLine *, const char **);
-protected int term_set(EditLine *, const char *);
-protected int term_settc(EditLine *, int, const Char **);
-protected int term_gettc(EditLine *, int, char **);
-protected int term_telltc(EditLine *, int, const Char **);
-protected int term_echotc(EditLine *, int, const Char **);
-protected void term_writec(EditLine *, Int);
-protected int term__putc(EditLine *, Int);
-protected void term__flush(EditLine *);
+libedit_private void terminal_move_to_line(EditLine *, int);
+libedit_private void terminal_move_to_char(EditLine *, int);
+libedit_private void terminal_clear_EOL(EditLine *, int);
+libedit_private void terminal_overwrite(EditLine *, const wchar_t *, size_t);
+libedit_private void terminal_insertwrite(EditLine *, wchar_t *, int);
+libedit_private void terminal_deletechars(EditLine *, int);
+libedit_private void terminal_clear_screen(EditLine *);
+libedit_private void terminal_beep(EditLine *);
+libedit_private int terminal_change_size(EditLine *, int, int);
+libedit_private int terminal_get_size(EditLine *, int *, int *);
+libedit_private int terminal_init(EditLine *);
+libedit_private void terminal_bind_arrow(EditLine *);
+libedit_private void terminal_print_arrow(EditLine *, const wchar_t *);
+libedit_private int terminal_clear_arrow(EditLine *, const wchar_t *);
+libedit_private int terminal_set_arrow(EditLine *, const wchar_t *,
+ keymacro_value_t *, int);
+libedit_private void terminal_end(EditLine *);
+libedit_private void terminal_get(EditLine *, const char **);
+libedit_private int terminal_set(EditLine *, const char *);
+libedit_private int terminal_settc(EditLine *, int, const wchar_t **);
+libedit_private int terminal_gettc(EditLine *, int, char **);
+libedit_private int terminal_telltc(EditLine *, int, const wchar_t **);
+libedit_private int terminal_echotc(EditLine *, int, const wchar_t **);
+libedit_private void terminal_writec(EditLine *, wint_t);
+libedit_private int terminal__putc(EditLine *, wint_t);
+libedit_private void terminal__flush(EditLine *);
/*
* Easy access macros
*/
-#define EL_FLAGS (el)->el_term.t_flags
+#define EL_FLAGS (el)->el_terminal.t_flags
#define EL_CAN_INSERT (EL_FLAGS & TERM_CAN_INSERT)
#define EL_CAN_DELETE (EL_FLAGS & TERM_CAN_DELETE)
@@ -131,4 +122,4 @@ protected void term__flush(EditLine *);
#define EL_HAS_AUTO_MARGINS (EL_FLAGS & TERM_HAS_AUTO_MARGINS)
#define EL_HAS_MAGIC_MARGINS (EL_FLAGS & TERM_HAS_MAGIC_MARGINS)
-#endif /* _h_el_term */
+#endif /* _h_el_terminal */
diff --git a/lib/libedit/src/tokenizer.c b/lib/libedit/src/tokenizer.c
index b1d5278d26cf..18532240dbb9 100644
--- a/lib/libedit/src/tokenizer.c
+++ b/lib/libedit/src/tokenizer.c
@@ -1,4 +1,4 @@
-/* $NetBSD: tokenizer.c,v 1.18 2010/01/03 18:27:10 christos Exp $ */
+/* $NetBSD: tokenizer.c,v 1.28 2016/04/11 18:56:31 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -32,15 +32,12 @@
* SUCH DAMAGE.
*/
-#ifndef NARROWCHAR
#include "config.h"
-#endif
-
#if !defined(lint) && !defined(SCCSID)
#if 0
static char sccsid[] = "@(#)tokenizer.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: tokenizer.c,v 1.18 2010/01/03 18:27:10 christos Exp $");
+__RCSID("$NetBSD: tokenizer.c,v 1.28 2016/04/11 18:56:31 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -48,10 +45,10 @@ __RCSID("$NetBSD: tokenizer.c,v 1.18 2010/01/03 18:27:10 christos Exp $");
/*
* tokenize.c: Bourne shell like tokenizer
*/
-#include <string.h>
#include <stdlib.h>
+#include <string.h>
+
#include "histedit.h"
-#include "chartype.h"
typedef enum {
Q_none, Q_single, Q_double, Q_one, Q_doubleone
@@ -68,12 +65,26 @@ typedef enum {
#define tok_malloc(a) malloc(a)
#define tok_free(a) free(a)
#define tok_realloc(a, b) realloc(a, b)
-#define tok_strdup(a) Strdup(a)
+#ifdef NARROWCHAR
+#define Char char
+#define FUN(prefix, rest) prefix ## _ ## rest
+#define TYPE(type) type
+#define STR(x) x
+#define Strchr(s, c) strchr(s, c)
+#define tok_strdup(s) strdup(s)
+#else
+#define Char wchar_t
+#define FUN(prefix, rest) prefix ## _w ## rest
+#define TYPE(type) type ## W
+#define STR(x) L ## x
+#define Strchr(s, c) wcschr(s, c)
+#define tok_strdup(s) wcsdup(s)
+#endif
struct TYPE(tokenizer) {
Char *ifs; /* In field separator */
- int argc, amax; /* Current and maximum number of args */
+ size_t argc, amax; /* Current and maximum number of args */
Char **argv; /* Argument list */
Char *wptr, *wmax; /* Space and limit on the word buffer */
Char *wstart; /* Beginning of next word */
@@ -83,13 +94,13 @@ struct TYPE(tokenizer) {
};
-private void FUN(tok,finish)(TYPE(Tokenizer) *);
+static void FUN(tok,finish)(TYPE(Tokenizer) *);
/* FUN(tok,finish)():
* Finish a word in the tokenizer.
*/
-private void
+static void
FUN(tok,finish)(TYPE(Tokenizer) *tok)
{
@@ -106,32 +117,32 @@ FUN(tok,finish)(TYPE(Tokenizer) *tok)
/* FUN(tok,init)():
* Initialize the tokenizer
*/
-public TYPE(Tokenizer) *
+TYPE(Tokenizer) *
FUN(tok,init)(const Char *ifs)
{
- TYPE(Tokenizer) *tok = tok_malloc(sizeof(TYPE(Tokenizer)));
+ TYPE(Tokenizer) *tok = tok_malloc(sizeof(*tok));
if (tok == NULL)
return NULL;
tok->ifs = tok_strdup(ifs ? ifs : IFS);
if (tok->ifs == NULL) {
- tok_free((ptr_t)tok);
+ tok_free(tok);
return NULL;
}
tok->argc = 0;
tok->amax = AINCR;
tok->argv = tok_malloc(sizeof(*tok->argv) * tok->amax);
if (tok->argv == NULL) {
- tok_free((ptr_t)tok->ifs);
- tok_free((ptr_t)tok);
+ tok_free(tok->ifs);
+ tok_free(tok);
return NULL;
}
tok->argv[0] = NULL;
tok->wspace = tok_malloc(WINCR * sizeof(*tok->wspace));
if (tok->wspace == NULL) {
- tok_free((ptr_t)tok->argv);
- tok_free((ptr_t)tok->ifs);
- tok_free((ptr_t)tok);
+ tok_free(tok->argv);
+ tok_free(tok->ifs);
+ tok_free(tok);
return NULL;
}
tok->wmax = tok->wspace + WINCR;
@@ -140,14 +151,14 @@ FUN(tok,init)(const Char *ifs)
tok->flags = 0;
tok->quote = Q_none;
- return (tok);
+ return tok;
}
/* FUN(tok,reset)():
* Reset the tokenizer
*/
-public void
+void
FUN(tok,reset)(TYPE(Tokenizer) *tok)
{
@@ -162,14 +173,14 @@ FUN(tok,reset)(TYPE(Tokenizer) *tok)
/* FUN(tok,end)():
* Clean up
*/
-public void
+void
FUN(tok,end)(TYPE(Tokenizer) *tok)
{
- tok_free((ptr_t) tok->ifs);
- tok_free((ptr_t) tok->wspace);
- tok_free((ptr_t) tok->argv);
- tok_free((ptr_t) tok);
+ tok_free(tok->ifs);
+ tok_free(tok->wspace);
+ tok_free(tok->argv);
+ tok_free(tok);
}
@@ -191,7 +202,7 @@ FUN(tok,end)(TYPE(Tokenizer) *tok)
* cursorc if !NULL, argv element containing cursor
* cursorv if !NULL, offset in argv[cursorc] of cursor
*/
-public int
+int
FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
int *argc, const Char ***argv, int *cursorc, int *cursoro)
{
@@ -204,7 +215,7 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
if (ptr >= line->lastchar)
ptr = STR("");
if (ptr == line->cursor) {
- cc = tok->argc;
+ cc = (int)tok->argc;
co = (int)(tok->wptr - tok->wstart);
}
switch (*ptr) {
@@ -236,7 +247,7 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
break;
default:
- return (-1);
+ return -1;
}
break;
@@ -267,7 +278,7 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
break;
default:
- return (-1);
+ return -1;
}
break;
@@ -298,7 +309,7 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
break;
default:
- return (-1);
+ return -1;
}
break;
@@ -324,7 +335,7 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
break;
default:
- return (0);
+ return 0;
}
break;
@@ -334,15 +345,15 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
/* Finish word and return */
if (tok->flags & TOK_EAT) {
tok->flags &= ~TOK_EAT;
- return (3);
+ return 3;
}
goto tok_line_outok;
case Q_single:
- return (1);
+ return 1;
case Q_double:
- return (2);
+ return 2;
case Q_doubleone:
tok->quote = Q_double;
@@ -355,7 +366,7 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
break;
default:
- return (-1);
+ return -1;
}
break;
@@ -387,21 +398,21 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
break;
default:
- return (-1);
+ return -1;
}
break;
}
if (tok->wptr >= tok->wmax - 4) {
- size_t size = tok->wmax - tok->wspace + WINCR;
+ size_t size = (size_t)(tok->wmax - tok->wspace + WINCR);
Char *s = tok_realloc(tok->wspace,
size * sizeof(*s));
if (s == NULL)
- return (-1);
+ return -1;
if (s != tok->wspace) {
- int i;
+ size_t i;
for (i = 0; i < tok->argc; i++) {
tok->argv[i] =
(tok->argv[i] - tok->wspace) + s;
@@ -416,14 +427,16 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
Char **p;
tok->amax += AINCR;
p = tok_realloc(tok->argv, tok->amax * sizeof(*p));
- if (p == NULL)
- return (-1);
+ if (p == NULL) {
+ tok->amax -= AINCR;
+ return -1;
+ }
tok->argv = p;
}
}
tok_line_outok:
if (cc == -1 && co == -1) {
- cc = tok->argc;
+ cc = (int)tok->argc;
co = (int)(tok->wptr - tok->wstart);
}
if (cursorc != NULL)
@@ -432,15 +445,15 @@ FUN(tok,line)(TYPE(Tokenizer) *tok, const TYPE(LineInfo) *line,
*cursoro = co;
FUN(tok,finish)(tok);
*argv = (const Char **)tok->argv;
- *argc = tok->argc;
- return (0);
+ *argc = (int)tok->argc;
+ return 0;
}
/* FUN(tok,str)():
* Simpler version of tok_line, taking a NUL terminated line
* and splitting into words, ignoring cursor state.
*/
-public int
+int
FUN(tok,str)(TYPE(Tokenizer) *tok, const Char *line, int *argc,
const Char ***argv)
{
@@ -449,5 +462,5 @@ FUN(tok,str)(TYPE(Tokenizer) *tok, const Char *line, int *argc,
memset(&li, 0, sizeof(li));
li.buffer = line;
li.cursor = li.lastchar = Strchr(line, '\0');
- return (FUN(tok,line)(tok, &li, argc, argv, NULL, NULL));
+ return FUN(tok,line)(tok, &li, argc, argv, NULL, NULL);
}
diff --git a/lib/libedit/src/tokenizern.c b/lib/libedit/src/tokenizern.c
new file mode 100644
index 000000000000..5846b60643c5
--- /dev/null
+++ b/lib/libedit/src/tokenizern.c
@@ -0,0 +1,3 @@
+#include "config.h"
+#define NARROWCHAR
+#include "tokenizer.c"
diff --git a/lib/libedit/src/tty.c b/lib/libedit/src/tty.c
index 94d23de23599..f524da47341f 100644
--- a/lib/libedit/src/tty.c
+++ b/lib/libedit/src/tty.c
@@ -1,4 +1,4 @@
-/* $NetBSD: tty.c,v 1.35 2011/01/28 03:41:52 christos Exp $ */
+/* $NetBSD: tty.c,v 1.65 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)tty.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: tty.c,v 1.35 2011/01/28 03:41:52 christos Exp $");
+__RCSID("$NetBSD: tty.c,v 1.65 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
@@ -46,10 +46,14 @@ __RCSID("$NetBSD: tty.c,v 1.35 2011/01/28 03:41:52 christos Exp $");
*/
#include <assert.h>
#include <errno.h>
-#include <unistd.h> /* for isatty */
+#include <stdlib.h> /* for abort */
+#include <string.h>
#include <strings.h> /* for ffs */
+#include <unistd.h> /* for isatty */
+
#include "el.h"
-#include "tty.h"
+#include "fcns.h"
+#include "parse.h"
typedef struct ttymodes_t {
const char *m_name;
@@ -58,12 +62,12 @@ typedef struct ttymodes_t {
} ttymodes_t;
typedef struct ttymap_t {
- Int nch, och; /* Internal and termio rep of chars */
+ wint_t nch, och; /* Internal and termio rep of chars */
el_action_t bind[3]; /* emacs, vi, and vi-cmd */
} ttymap_t;
-private const ttyperm_t ttyperm = {
+static const ttyperm_t ttyperm = {
{
{"iflag:", ICRNL, (INLCR | IGNCR)},
{"oflag:", (OPOST | ONLCR), ONLRET},
@@ -91,7 +95,7 @@ private const ttyperm_t ttyperm = {
}
};
-private const ttychar_t ttychar = {
+static const ttychar_t ttychar = {
{
CINTR, CQUIT, CERASE, CKILL,
CEOF, CEOL, CEOL2, CSWTCH,
@@ -121,7 +125,7 @@ private const ttychar_t ttychar = {
}
};
-private const ttymap_t tty_map[] = {
+static const ttymap_t tty_map[] = {
#ifdef VERASE
{C_ERASE, VERASE,
{EM_DELETE_PREV_CHAR, VI_DELETE_PREV_CHAR, ED_PREV_CHAR}},
@@ -154,11 +158,11 @@ private const ttymap_t tty_map[] = {
{C_LNEXT, VLNEXT,
{ED_QUOTED_INSERT, ED_QUOTED_INSERT, ED_UNASSIGNED}},
#endif /* VLNEXT */
- {-1, -1,
+ {(wint_t)-1, (wint_t)-1,
{ED_UNASSIGNED, ED_UNASSIGNED, ED_UNASSIGNED}}
};
-private const ttymodes_t ttymodes[] = {
+static const ttymodes_t ttymodes[] = {
#ifdef IGNBRK
{"ignbrk", IGNBRK, MD_INP},
#endif /* IGNBRK */
@@ -452,20 +456,21 @@ private const ttymodes_t ttymodes[] = {
#define tty__geteightbit(td) (((td)->c_cflag & CSIZE) == CS8)
#define tty__cooked_mode(td) ((td)->c_lflag & ICANON)
-private int tty_getty(EditLine *, struct termios *);
-private int tty_setty(EditLine *, int, const struct termios *);
-private int tty__getcharindex(int);
-private void tty__getchar(struct termios *, unsigned char *);
-private void tty__setchar(struct termios *, unsigned char *);
-private speed_t tty__getspeed(struct termios *);
-private int tty_setup(EditLine *);
+static int tty_getty(EditLine *, struct termios *);
+static int tty_setty(EditLine *, int, const struct termios *);
+static int tty__getcharindex(int);
+static void tty__getchar(struct termios *, unsigned char *);
+static void tty__setchar(struct termios *, unsigned char *);
+static speed_t tty__getspeed(struct termios *);
+static int tty_setup(EditLine *);
+static void tty_setup_flags(EditLine *, struct termios *, int);
#define t_qu t_ts
/* tty_getty():
* Wrapper for tcgetattr to handle EINTR
*/
-private int
+static int
tty_getty(EditLine *el, struct termios *t)
{
int rv;
@@ -477,7 +482,7 @@ tty_getty(EditLine *el, struct termios *t)
/* tty_setty():
* Wrapper for tcsetattr to handle EINTR
*/
-private int
+static int
tty_setty(EditLine *el, int action, const struct termios *t)
{
int rv;
@@ -489,45 +494,38 @@ tty_setty(EditLine *el, int action, const struct termios *t)
/* tty_setup():
* Get the tty parameters and initialize the editing state
*/
-private int
+static int
tty_setup(EditLine *el)
{
int rst = 1;
if (el->el_flags & EDIT_DISABLED)
- return (0);
+ return 0;
+
+ if (el->el_tty.t_initialized)
+ return -1;
if (!isatty(el->el_outfd)) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile,
- "tty_setup: isatty: %s\n", strerror(errno));
+ (void) fprintf(el->el_errfile, "%s: isatty: %s\n", __func__,
+ strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
- if (tty_getty(el, &el->el_tty.t_ed) == -1) {
+ if (tty_getty(el, &el->el_tty.t_or) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile,
- "tty_setup: tty_getty: %s\n", strerror(errno));
+ (void) fprintf(el->el_errfile, "%s: tty_getty: %s\n", __func__,
+ strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
- el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed;
+ el->el_tty.t_ts = el->el_tty.t_ex = el->el_tty.t_ed = el->el_tty.t_or;
el->el_tty.t_speed = tty__getspeed(&el->el_tty.t_ex);
el->el_tty.t_tabs = tty__gettabs(&el->el_tty.t_ex);
el->el_tty.t_eight = tty__geteightbit(&el->el_tty.t_ex);
- el->el_tty.t_ex.c_iflag &= ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask;
- el->el_tty.t_ex.c_iflag |= el->el_tty.t_t[EX_IO][MD_INP].t_setmask;
-
- el->el_tty.t_ex.c_oflag &= ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask;
- el->el_tty.t_ex.c_oflag |= el->el_tty.t_t[EX_IO][MD_OUT].t_setmask;
-
- el->el_tty.t_ex.c_cflag &= ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask;
- el->el_tty.t_ex.c_cflag |= el->el_tty.t_t[EX_IO][MD_CTL].t_setmask;
-
- el->el_tty.t_ex.c_lflag &= ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask;
- el->el_tty.t_ex.c_lflag |= el->el_tty.t_t[EX_IO][MD_LIN].t_setmask;
+ tty_setup_flags(el, &el->el_tty.t_ex, EX_IO);
/*
* Reset the tty chars to reasonable defaults
@@ -555,76 +553,73 @@ tty_setup(EditLine *el)
tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile,
- "tty_setup: tty_setty: %s\n",
- strerror(errno));
+ (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n",
+ __func__, strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
}
-#ifdef notdef
- else
- tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
-#endif
-
- el->el_tty.t_ed.c_iflag &= ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
- el->el_tty.t_ed.c_iflag |= el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
-
- el->el_tty.t_ed.c_oflag &= ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
- el->el_tty.t_ed.c_oflag |= el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
- el->el_tty.t_ed.c_cflag &= ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
- el->el_tty.t_ed.c_cflag |= el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
-
- el->el_tty.t_ed.c_lflag &= ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
- el->el_tty.t_ed.c_lflag |= el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
+ tty_setup_flags(el, &el->el_tty.t_ed, ED_IO);
tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
tty_bind_char(el, 1);
- return (0);
+ el->el_tty.t_initialized = 1;
+ return 0;
}
-protected int
+libedit_private int
tty_init(EditLine *el)
{
el->el_tty.t_mode = EX_IO;
el->el_tty.t_vdisable = _POSIX_VDISABLE;
+ el->el_tty.t_initialized = 0;
(void) memcpy(el->el_tty.t_t, ttyperm, sizeof(ttyperm_t));
(void) memcpy(el->el_tty.t_c, ttychar, sizeof(ttychar_t));
- return (tty_setup(el));
+ return tty_setup(el);
}
/* tty_end():
* Restore the tty to its original settings
*/
-protected void
+libedit_private void
/*ARGSUSED*/
-tty_end(EditLine *el __attribute__((__unused__)))
+tty_end(EditLine *el)
{
+ if (el->el_flags & EDIT_DISABLED)
+ return;
+
+ if (!el->el_tty.t_initialized)
+ return;
- /* XXX: Maybe reset to an initial state? */
+ if (tty_setty(el, TCSAFLUSH, &el->el_tty.t_or) == -1) {
+#ifdef DEBUG_TTY
+ (void) fprintf(el->el_errfile,
+ "%s: tty_setty: %s\n", __func__, strerror(errno));
+#endif /* DEBUG_TTY */
+ }
}
/* tty__getspeed():
* Get the tty speed
*/
-private speed_t
+static speed_t
tty__getspeed(struct termios *td)
{
speed_t spd;
if ((spd = cfgetispeed(td)) == 0)
spd = cfgetospeed(td);
- return (spd);
+ return spd;
}
/* tty__getspeed():
* Return the index of the asked char in the c_cc array
*/
-private int
+static int
tty__getcharindex(int i)
{
switch (i) {
@@ -732,7 +727,7 @@ tty__getcharindex(int i)
/* tty__getchar():
* Get the tty characters
*/
-private void
+static void
tty__getchar(struct termios *td, unsigned char *s)
{
@@ -814,7 +809,7 @@ tty__getchar(struct termios *td, unsigned char *s)
/* tty__setchar():
* Set the tty characters
*/
-private void
+static void
tty__setchar(struct termios *td, unsigned char *s)
{
@@ -896,13 +891,13 @@ tty__setchar(struct termios *td, unsigned char *s)
/* tty_bind_char():
* Rebind the editline functions
*/
-protected void
+libedit_private void
tty_bind_char(EditLine *el, int force)
{
unsigned char *t_n = el->el_tty.t_c[ED_IO];
unsigned char *t_o = el->el_tty.t_ed.c_cc;
- Char new[2], old[2];
+ wchar_t new[2], old[2];
const ttymap_t *tp;
el_action_t *map, *alt;
const el_action_t *dmap, *dalt;
@@ -918,46 +913,100 @@ tty_bind_char(EditLine *el, int force)
dalt = NULL;
}
- for (tp = tty_map; tp->nch != -1; tp++) {
- new[0] = t_n[tp->nch];
- old[0] = t_o[tp->och];
+ for (tp = tty_map; tp->nch != (wint_t)-1; tp++) {
+ new[0] = (wchar_t)t_n[tp->nch];
+ old[0] = (wchar_t)t_o[tp->och];
if (new[0] == old[0] && !force)
continue;
/* Put the old default binding back, and set the new binding */
- key_clear(el, map, old);
- map[UC(old[0])] = dmap[UC(old[0])];
- key_clear(el, map, new);
+ keymacro_clear(el, map, old);
+ map[(unsigned char)old[0]] = dmap[(unsigned char)old[0]];
+ keymacro_clear(el, map, new);
/* MAP_VI == 1, MAP_EMACS == 0... */
- map[UC(new[0])] = tp->bind[el->el_map.type];
+ map[(unsigned char)new[0]] = tp->bind[el->el_map.type];
if (dalt) {
- key_clear(el, alt, old);
- alt[UC(old[0])] = dalt[UC(old[0])];
- key_clear(el, alt, new);
- alt[UC(new[0])] = tp->bind[el->el_map.type + 1];
+ keymacro_clear(el, alt, old);
+ alt[(unsigned char)old[0]] =
+ dalt[(unsigned char)old[0]];
+ keymacro_clear(el, alt, new);
+ alt[(unsigned char)new[0]] =
+ tp->bind[el->el_map.type + 1];
}
}
}
+static tcflag_t *
+tty__get_flag(struct termios *t, int kind) {
+ switch (kind) {
+ case MD_INP:
+ return &t->c_iflag;
+ case MD_OUT:
+ return &t->c_oflag;
+ case MD_CTL:
+ return &t->c_cflag;
+ case MD_LIN:
+ return &t->c_lflag;
+ default:
+ abort();
+ /*NOTREACHED*/
+ }
+}
+
+
+static tcflag_t
+tty_update_flag(EditLine *el, tcflag_t f, int mode, int kind)
+{
+ f &= ~el->el_tty.t_t[mode][kind].t_clrmask;
+ f |= el->el_tty.t_t[mode][kind].t_setmask;
+ return f;
+}
+
+
+static void
+tty_update_flags(EditLine *el, int kind)
+{
+ tcflag_t *tt, *ed, *ex;
+ tt = tty__get_flag(&el->el_tty.t_ts, kind);
+ ed = tty__get_flag(&el->el_tty.t_ed, kind);
+ ex = tty__get_flag(&el->el_tty.t_ex, kind);
+
+ if (*tt != *ex && (kind != MD_CTL || *tt != *ed)) {
+ *ed = tty_update_flag(el, *tt, ED_IO, kind);
+ *ex = tty_update_flag(el, *tt, EX_IO, kind);
+ }
+}
+
+
+static void
+tty_update_char(EditLine *el, int mode, int c) {
+ if (!((el->el_tty.t_t[mode][MD_CHAR].t_setmask & C_SH(c)))
+ && (el->el_tty.t_c[TS_IO][c] != el->el_tty.t_c[EX_IO][c]))
+ el->el_tty.t_c[mode][c] = el->el_tty.t_c[TS_IO][c];
+ if (el->el_tty.t_t[mode][MD_CHAR].t_clrmask & C_SH(c))
+ el->el_tty.t_c[mode][c] = el->el_tty.t_vdisable;
+}
+
+
/* tty_rawmode():
- * Set terminal into 1 character at a time mode.
+ * Set terminal into 1 character at a time mode.
*/
-protected int
+libedit_private int
tty_rawmode(EditLine *el)
{
if (el->el_tty.t_mode == ED_IO || el->el_tty.t_mode == QU_IO)
- return (0);
+ return 0;
if (el->el_flags & EDIT_DISABLED)
- return (0);
+ return 0;
if (tty_getty(el, &el->el_tty.t_ts) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile, "tty_rawmode: tty_getty: %s\n",
+ (void) fprintf(el->el_errfile, "%s: tty_getty: %s\n", __func__,
strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
/*
* We always keep up with the eight bit setting and the speed of the
@@ -974,225 +1023,145 @@ tty_rawmode(EditLine *el)
(void) cfsetospeed(&el->el_tty.t_ed, el->el_tty.t_speed);
}
if (tty__cooked_mode(&el->el_tty.t_ts)) {
- if (el->el_tty.t_ts.c_cflag != el->el_tty.t_ex.c_cflag) {
- el->el_tty.t_ex.c_cflag =
- el->el_tty.t_ts.c_cflag;
- el->el_tty.t_ex.c_cflag &=
- ~el->el_tty.t_t[EX_IO][MD_CTL].t_clrmask;
- el->el_tty.t_ex.c_cflag |=
- el->el_tty.t_t[EX_IO][MD_CTL].t_setmask;
-
- el->el_tty.t_ed.c_cflag =
- el->el_tty.t_ts.c_cflag;
- el->el_tty.t_ed.c_cflag &=
- ~el->el_tty.t_t[ED_IO][MD_CTL].t_clrmask;
- el->el_tty.t_ed.c_cflag |=
- el->el_tty.t_t[ED_IO][MD_CTL].t_setmask;
- }
- if ((el->el_tty.t_ts.c_lflag != el->el_tty.t_ex.c_lflag) &&
- (el->el_tty.t_ts.c_lflag != el->el_tty.t_ed.c_lflag)) {
- el->el_tty.t_ex.c_lflag =
- el->el_tty.t_ts.c_lflag;
- el->el_tty.t_ex.c_lflag &=
- ~el->el_tty.t_t[EX_IO][MD_LIN].t_clrmask;
- el->el_tty.t_ex.c_lflag |=
- el->el_tty.t_t[EX_IO][MD_LIN].t_setmask;
-
- el->el_tty.t_ed.c_lflag =
- el->el_tty.t_ts.c_lflag;
- el->el_tty.t_ed.c_lflag &=
- ~el->el_tty.t_t[ED_IO][MD_LIN].t_clrmask;
- el->el_tty.t_ed.c_lflag |=
- el->el_tty.t_t[ED_IO][MD_LIN].t_setmask;
- }
- if ((el->el_tty.t_ts.c_iflag != el->el_tty.t_ex.c_iflag) &&
- (el->el_tty.t_ts.c_iflag != el->el_tty.t_ed.c_iflag)) {
- el->el_tty.t_ex.c_iflag =
- el->el_tty.t_ts.c_iflag;
- el->el_tty.t_ex.c_iflag &=
- ~el->el_tty.t_t[EX_IO][MD_INP].t_clrmask;
- el->el_tty.t_ex.c_iflag |=
- el->el_tty.t_t[EX_IO][MD_INP].t_setmask;
-
- el->el_tty.t_ed.c_iflag =
- el->el_tty.t_ts.c_iflag;
- el->el_tty.t_ed.c_iflag &=
- ~el->el_tty.t_t[ED_IO][MD_INP].t_clrmask;
- el->el_tty.t_ed.c_iflag |=
- el->el_tty.t_t[ED_IO][MD_INP].t_setmask;
- }
- if ((el->el_tty.t_ts.c_oflag != el->el_tty.t_ex.c_oflag) &&
- (el->el_tty.t_ts.c_oflag != el->el_tty.t_ed.c_oflag)) {
- el->el_tty.t_ex.c_oflag =
- el->el_tty.t_ts.c_oflag;
- el->el_tty.t_ex.c_oflag &=
- ~el->el_tty.t_t[EX_IO][MD_OUT].t_clrmask;
- el->el_tty.t_ex.c_oflag |=
- el->el_tty.t_t[EX_IO][MD_OUT].t_setmask;
-
- el->el_tty.t_ed.c_oflag =
- el->el_tty.t_ts.c_oflag;
- el->el_tty.t_ed.c_oflag &=
- ~el->el_tty.t_t[ED_IO][MD_OUT].t_clrmask;
- el->el_tty.t_ed.c_oflag |=
- el->el_tty.t_t[ED_IO][MD_OUT].t_setmask;
- }
+ int i;
+
+ for (i = MD_INP; i <= MD_LIN; i++)
+ tty_update_flags(el, i);
+
if (tty__gettabs(&el->el_tty.t_ex) == 0)
el->el_tty.t_tabs = 0;
else
el->el_tty.t_tabs = EL_CAN_TAB ? 1 : 0;
- {
- int i;
+ tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
+ /*
+ * Check if the user made any changes.
+ * If he did, then propagate the changes to the
+ * edit and execute data structures.
+ */
+ for (i = 0; i < C_NCC; i++)
+ if (el->el_tty.t_c[TS_IO][i] !=
+ el->el_tty.t_c[EX_IO][i])
+ break;
- tty__getchar(&el->el_tty.t_ts, el->el_tty.t_c[TS_IO]);
+ if (i != C_NCC) {
/*
- * Check if the user made any changes.
- * If he did, then propagate the changes to the
- * edit and execute data structures.
- */
+ * Propagate changes only to the unlibedit_private
+ * chars that have been modified just now.
+ */
for (i = 0; i < C_NCC; i++)
- if (el->el_tty.t_c[TS_IO][i] !=
- el->el_tty.t_c[EX_IO][i])
- break;
-
- if (i != C_NCC) {
- /*
- * Propagate changes only to the unprotected
- * chars that have been modified just now.
- */
- for (i = 0; i < C_NCC; i++) {
- if (!((el->el_tty.t_t[ED_IO][MD_CHAR].t_setmask & C_SH(i)))
- && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
- el->el_tty.t_c[ED_IO][i] = el->el_tty.t_c[TS_IO][i];
- if (el->el_tty.t_t[ED_IO][MD_CHAR].t_clrmask & C_SH(i))
- el->el_tty.t_c[ED_IO][i] = el->el_tty.t_vdisable;
- }
- tty_bind_char(el, 0);
- tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
-
- for (i = 0; i < C_NCC; i++) {
- if (!((el->el_tty.t_t[EX_IO][MD_CHAR].t_setmask & C_SH(i)))
- && (el->el_tty.t_c[TS_IO][i] != el->el_tty.t_c[EX_IO][i]))
- el->el_tty.t_c[EX_IO][i] = el->el_tty.t_c[TS_IO][i];
- if (el->el_tty.t_t[EX_IO][MD_CHAR].t_clrmask & C_SH(i))
- el->el_tty.t_c[EX_IO][i] = el->el_tty.t_vdisable;
- }
- tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
- }
+ tty_update_char(el, ED_IO, i);
+
+ tty_bind_char(el, 0);
+ tty__setchar(&el->el_tty.t_ed, el->el_tty.t_c[ED_IO]);
+
+ for (i = 0; i < C_NCC; i++)
+ tty_update_char(el, EX_IO, i);
+
+ tty__setchar(&el->el_tty.t_ex, el->el_tty.t_c[EX_IO]);
}
}
if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile, "tty_rawmode: tty_setty: %s\n",
+ (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__,
strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
el->el_tty.t_mode = ED_IO;
- return (0);
+ return 0;
}
/* tty_cookedmode():
* Set the tty back to normal mode
*/
-protected int
+libedit_private int
tty_cookedmode(EditLine *el)
{ /* set tty in normal setup */
if (el->el_tty.t_mode == EX_IO)
- return (0);
+ return 0;
if (el->el_flags & EDIT_DISABLED)
- return (0);
+ return 0;
if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ex) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile,
- "tty_cookedmode: tty_setty: %s\n",
+ (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__,
strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
el->el_tty.t_mode = EX_IO;
- return (0);
+ return 0;
}
/* tty_quotemode():
* Turn on quote mode
*/
-protected int
+libedit_private int
tty_quotemode(EditLine *el)
{
if (el->el_tty.t_mode == QU_IO)
- return (0);
+ return 0;
el->el_tty.t_qu = el->el_tty.t_ed;
- el->el_tty.t_qu.c_iflag &= ~el->el_tty.t_t[QU_IO][MD_INP].t_clrmask;
- el->el_tty.t_qu.c_iflag |= el->el_tty.t_t[QU_IO][MD_INP].t_setmask;
-
- el->el_tty.t_qu.c_oflag &= ~el->el_tty.t_t[QU_IO][MD_OUT].t_clrmask;
- el->el_tty.t_qu.c_oflag |= el->el_tty.t_t[QU_IO][MD_OUT].t_setmask;
-
- el->el_tty.t_qu.c_cflag &= ~el->el_tty.t_t[QU_IO][MD_CTL].t_clrmask;
- el->el_tty.t_qu.c_cflag |= el->el_tty.t_t[QU_IO][MD_CTL].t_setmask;
-
- el->el_tty.t_qu.c_lflag &= ~el->el_tty.t_t[QU_IO][MD_LIN].t_clrmask;
- el->el_tty.t_qu.c_lflag |= el->el_tty.t_t[QU_IO][MD_LIN].t_setmask;
+ tty_setup_flags(el, &el->el_tty.t_qu, QU_IO);
if (tty_setty(el, TCSADRAIN, &el->el_tty.t_qu) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile, "QuoteModeOn: tty_setty: %s\n",
+ (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__,
strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
el->el_tty.t_mode = QU_IO;
- return (0);
+ return 0;
}
/* tty_noquotemode():
* Turn off quote mode
*/
-protected int
+libedit_private int
tty_noquotemode(EditLine *el)
{
if (el->el_tty.t_mode != QU_IO)
- return (0);
+ return 0;
if (tty_setty(el, TCSADRAIN, &el->el_tty.t_ed) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile, "QuoteModeOff: tty_setty: %s\n",
+ (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n", __func__,
strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
el->el_tty.t_mode = ED_IO;
- return (0);
+ return 0;
}
/* tty_stty():
* Stty builtin
*/
-protected int
+libedit_private int
/*ARGSUSED*/
-tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
+tty_stty(EditLine *el, int argc __attribute__((__unused__)),
+ const wchar_t **argv)
{
const ttymodes_t *m;
char x;
int aflag = 0;
- const Char *s, *d;
+ const wchar_t *s, *d;
char name[EL_BUFSIZ];
struct termios *tios = &el->el_tty.t_ex;
int z = EX_IO;
if (argv == NULL)
- return (-1);
+ return -1;
strncpy(name, ct_encode_string(*argv++, &el->el_scratch), sizeof(name));
name[sizeof(name) - 1] = '\0';
@@ -1219,9 +1188,9 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
break;
default:
(void) fprintf(el->el_errfile,
- "%s: Unknown switch `%c'.\n",
- name, argv[0][1]);
- return (-1);
+ "%s: Unknown switch `%lc'.\n",
+ name, (wint_t)argv[0][1]);
+ return -1;
}
if (!argv || !*argv) {
@@ -1239,8 +1208,9 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
if (i != -1) {
x = (el->el_tty.t_t[z][i].t_setmask & m->m_value)
? '+' : '\0';
- x = (el->el_tty.t_t[z][i].t_clrmask & m->m_value)
- ? '-' : x;
+
+ if (el->el_tty.t_t[z][i].t_clrmask & m->m_value)
+ x = '-';
} else {
x = '\0';
}
@@ -1249,7 +1219,8 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
cu = strlen(m->m_name) + (x != '\0') + 1;
- if (len + cu >= (size_t)el->el_term.t_size.h) {
+ if (len + cu >=
+ (size_t)el->el_terminal.t_size.h) {
(void) fprintf(el->el_outfile, "\n%*s",
(int)st, "");
len = st + cu;
@@ -1265,31 +1236,33 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
}
}
(void) fprintf(el->el_outfile, "\n");
- return (0);
+ return 0;
}
while (argv && (s = *argv++)) {
- const Char *p;
+ const wchar_t *p;
switch (*s) {
case '+':
case '-':
- x = *s++;
+ x = (char)*s++;
break;
default:
x = '\0';
break;
}
d = s;
- p = Strchr(s, '=');
+ p = wcschr(s, L'=');
for (m = ttymodes; m->m_name; m++)
- if ((p ? strncmp(m->m_name, ct_encode_string(d, &el->el_scratch), (size_t)(p - d)) :
- strcmp(m->m_name, ct_encode_string(d, &el->el_scratch))) == 0 &&
+ if ((p ? strncmp(m->m_name, ct_encode_string(d,
+ &el->el_scratch), (size_t)(p - d)) :
+ strcmp(m->m_name, ct_encode_string(d,
+ &el->el_scratch))) == 0 &&
(p == NULL || m->m_type == MD_CHAR))
break;
if (!m->m_name) {
(void) fprintf(el->el_errfile,
- "%s: Invalid argument `" FSTR "'.\n", name, d);
- return (-1);
+ "%s: Invalid argument `%ls'.\n", name, d);
+ return -1;
}
if (p) {
int c = ffs((int)m->m_value);
@@ -1299,7 +1272,7 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
c--;
c = tty__getcharindex(c);
assert(c != -1);
- tios->c_cc[c] = v;
+ tios->c_cc[c] = (cc_t)v;
continue;
}
switch (x) {
@@ -1318,17 +1291,18 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
}
}
+ tty_setup_flags(el, tios, z);
if (el->el_tty.t_mode == z) {
if (tty_setty(el, TCSADRAIN, tios) == -1) {
#ifdef DEBUG_TTY
- (void) fprintf(el->el_errfile,
- "tty_stty: tty_setty: %s\n", strerror(errno));
+ (void) fprintf(el->el_errfile, "%s: tty_setty: %s\n",
+ __func__, strerror(errno));
#endif /* DEBUG_TTY */
- return (-1);
+ return -1;
}
}
- return (0);
+ return 0;
}
@@ -1336,7 +1310,7 @@ tty_stty(EditLine *el, int argc __attribute__((__unused__)), const Char **argv)
/* tty_printchar():
* DEbugging routine to print the tty characters
*/
-private void
+static void
tty_printchar(EditLine *el, unsigned char *s)
{
ttyperm_t *m;
@@ -1355,3 +1329,14 @@ tty_printchar(EditLine *el, unsigned char *s)
(void) fprintf(el->el_errfile, "\n");
}
#endif /* notyet */
+
+
+static void
+tty_setup_flags(EditLine *el, struct termios *tios, int mode)
+{
+ int kind;
+ for (kind = MD_INP; kind <= MD_LIN; kind++) {
+ tcflag_t *f = tty__get_flag(tios, kind);
+ *f = tty_update_flag(el, *f, mode, kind);
+ }
+}
diff --git a/lib/libedit/src/tty.h b/lib/libedit/src/tty.h
index f4b4d42a5eee..2603e1ad2d6d 100644
--- a/lib/libedit/src/tty.h
+++ b/lib/libedit/src/tty.h
@@ -1,4 +1,4 @@
-/* $NetBSD: tty.h,v 1.12 2009/12/30 22:37:40 christos Exp $ */
+/* $NetBSD: tty.h,v 1.21 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -40,8 +40,6 @@
#ifndef _h_el_tty
#define _h_el_tty
-#include "sys.h"
-#include "histedit.h"
#include <termios.h>
#include <unistd.h>
@@ -431,7 +429,7 @@
#define C_MIN 23
#define C_TIME 24
#define C_NCC 25
-#define C_SH(A) (1 << (A))
+#define C_SH(A) ((unsigned int)(1 << (A)))
/*
* Terminal dependend data structures
@@ -442,6 +440,7 @@
#define QU_IO 2 /* used only for quoted chars */
#define NN_IO 3 /* The number of entries */
+/* Don't re-order */
#define MD_INP 0
#define MD_OUT 1
#define MD_CTL 2
@@ -457,24 +456,25 @@ typedef struct {
typedef unsigned char ttychar_t[NN_IO][C_NCC];
-protected int tty_init(EditLine *);
-protected void tty_end(EditLine *);
-protected int tty_stty(EditLine *, int, const Char **);
-protected int tty_rawmode(EditLine *);
-protected int tty_cookedmode(EditLine *);
-protected int tty_quotemode(EditLine *);
-protected int tty_noquotemode(EditLine *);
-protected void tty_bind_char(EditLine *, int);
+libedit_private int tty_init(EditLine *);
+libedit_private void tty_end(EditLine *);
+libedit_private int tty_stty(EditLine *, int, const wchar_t **);
+libedit_private int tty_rawmode(EditLine *);
+libedit_private int tty_cookedmode(EditLine *);
+libedit_private int tty_quotemode(EditLine *);
+libedit_private int tty_noquotemode(EditLine *);
+libedit_private void tty_bind_char(EditLine *, int);
typedef struct {
ttyperm_t t_t;
ttychar_t t_c;
- struct termios t_ex, t_ed, t_ts;
+ struct termios t_or, t_ex, t_ed, t_ts;
int t_tabs;
int t_eight;
speed_t t_speed;
- int t_mode;
+ unsigned char t_mode;
unsigned char t_vdisable;
+ unsigned char t_initialized;
} el_tty_t;
diff --git a/lib/libedit/src/unvis.c b/lib/libedit/src/unvis.c
index c20a67f4c29d..719f86a79964 100644
--- a/lib/libedit/src/unvis.c
+++ b/lib/libedit/src/unvis.c
@@ -1,4 +1,4 @@
-/* $NetBSD: unvis.c,v 1.32 2010/11/27 21:22:11 christos Exp $ */
+/* $NetBSD: unvis.c,v 1.44 2014/09/26 15:43:36 roy Exp $ */
/*-
* Copyright (c) 1989, 1993
@@ -34,22 +34,18 @@
#if 0
static char sccsid[] = "@(#)unvis.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: unvis.c,v 1.32 2010/11/27 21:22:11 christos Exp $");
+__RCSID("$NetBSD: unvis.c,v 1.44 2014/09/26 15:43:36 roy Exp $");
#endif
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
-#include <stdint.h>
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
+#include <errno.h>
#include <vis.h>
-#ifdef __weak_alias
-__weak_alias(strunvis,_strunvis)
-#endif
-
#if !HAVE_VIS
/*
* decode driven by state machine
@@ -61,24 +57,25 @@ __weak_alias(strunvis,_strunvis)
#define S_CTRL 4 /* control char started (^) */
#define S_OCTAL2 5 /* octal digit 2 */
#define S_OCTAL3 6 /* octal digit 3 */
-#define S_HEX1 7 /* http hex digit */
-#define S_HEX2 8 /* http hex digit 2 */
-#define S_MIME1 9 /* mime hex digit 1 */
-#define S_MIME2 10 /* mime hex digit 2 */
-#define S_EATCRNL 11 /* mime eating CRNL */
-#define S_AMP 12 /* seen & */
-#define S_NUMBER 13 /* collecting number */
-#define S_STRING 14 /* collecting string */
+#define S_HEX 7 /* mandatory hex digit */
+#define S_HEX1 8 /* http hex digit */
+#define S_HEX2 9 /* http hex digit 2 */
+#define S_MIME1 10 /* mime hex digit 1 */
+#define S_MIME2 11 /* mime hex digit 2 */
+#define S_EATCRNL 12 /* mime eating CRNL */
+#define S_AMP 13 /* seen & */
+#define S_NUMBER 14 /* collecting number */
+#define S_STRING 15 /* collecting string */
#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
-#define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
-#define XTOD(c) (isdigit(c) ? (c - '0') : ((c - 'A') + 10))
+#define xtod(c) (isdigit(c) ? (c - '0') : ((tolower(c) - 'a') + 10))
+#define XTOD(c) (isdigit(c) ? (c - '0') : ((c - 'A') + 10))
/*
* RFC 1866
*/
static const struct nv {
- const char *name;
+ char name[7];
uint8_t value;
} nv[] = {
{ "AElig", 198 }, /* capital AE diphthong (ligature) */
@@ -295,6 +292,9 @@ unvis(char *cp, int c, int *astate, int flag)
*cp = '\033';
*astate = SS(0, S_GROUND);
return UNVIS_VALID;
+ case 'x':
+ *astate = SS(0, S_HEX);
+ return UNVIS_NOCHAR;
case '\n':
/*
* hidden newline
@@ -307,6 +307,12 @@ unvis(char *cp, int c, int *astate, int flag)
*/
*astate = SS(0, S_GROUND);
return UNVIS_NOCHAR;
+ default:
+ if (isgraph(c)) {
+ *cp = c;
+ *astate = SS(0, S_GROUND);
+ return UNVIS_VALID;
+ }
}
goto bad;
@@ -358,6 +364,10 @@ unvis(char *cp, int c, int *astate, int flag)
*/
return UNVIS_VALIDPUSH;
+ case S_HEX:
+ if (!isxdigit(uc))
+ goto bad;
+ /*FALLTHROUGH*/
case S_HEX1:
if (isxdigit(uc)) {
*cp = xtod(uc);
@@ -436,7 +446,7 @@ unvis(char *cp, int c, int *astate, int flag)
break;
}
- if (*cp == __arraycount(nv))
+ if (ia == __arraycount(nv))
goto bad;
if (uc != 0) {
@@ -468,47 +478,76 @@ unvis(char *cp, int c, int *astate, int flag)
}
/*
- * strunvis - decode src into dst
+ * strnunvisx - decode src into dst
*
* Number of chars decoded into dst is returned, -1 on error.
* Dst is null terminated.
*/
int
-strunvisx(char *dst, const char *src, int flag)
+strnunvisx(char *dst, size_t dlen, const char *src, int flag)
{
char c;
- char *start = dst;
+ char t = '\0', *start = dst;
int state = 0;
_DIAGASSERT(src != NULL);
_DIAGASSERT(dst != NULL);
+#define CHECKSPACE() \
+ do { \
+ if (dlen-- == 0) { \
+ errno = ENOSPC; \
+ return -1; \
+ } \
+ } while (/*CONSTCOND*/0)
while ((c = *src++) != '\0') {
again:
- switch (unvis(dst, c, &state, flag)) {
+ switch (unvis(&t, c, &state, flag)) {
case UNVIS_VALID:
- dst++;
+ CHECKSPACE();
+ *dst++ = t;
break;
case UNVIS_VALIDPUSH:
- dst++;
+ CHECKSPACE();
+ *dst++ = t;
goto again;
case 0:
case UNVIS_NOCHAR:
break;
+ case UNVIS_SYNBAD:
+ errno = EINVAL;
+ return -1;
default:
- return (-1);
+ _DIAGASSERT(/*CONSTCOND*/0);
+ errno = EINVAL;
+ return -1;
}
}
- if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
- dst++;
+ if (unvis(&t, c, &state, UNVIS_END) == UNVIS_VALID) {
+ CHECKSPACE();
+ *dst++ = t;
+ }
+ CHECKSPACE();
*dst = '\0';
return (int)(dst - start);
}
int
+strunvisx(char *dst, const char *src, int flag)
+{
+ return strnunvisx(dst, (size_t)~0, src, flag);
+}
+
+int
strunvis(char *dst, const char *src)
{
- return strunvisx(dst, src, 0);
+ return strnunvisx(dst, (size_t)~0, src, 0);
+}
+
+int
+strnunvis(char *dst, size_t dlen, const char *src)
+{
+ return strnunvisx(dst, dlen, src, 0);
}
#endif
diff --git a/lib/libedit/src/vi.c b/lib/libedit/src/vi.c
index fb8ee720362c..0c37bfb9b2ec 100644
--- a/lib/libedit/src/vi.c
+++ b/lib/libedit/src/vi.c
@@ -1,4 +1,4 @@
-/* $NetBSD: vi.c,v 1.34 2011/02/22 05:45:08 joerg Exp $ */
+/* $NetBSD: vi.c,v 1.62 2016/05/09 21:46:56 christos Exp $ */
/*-
* Copyright (c) 1992, 1993
@@ -33,37 +33,43 @@
*/
#include "config.h"
-#include <stdlib.h>
-#include <unistd.h>
-#include <limits.h>
-#include <sys/wait.h>
-
#if !defined(lint) && !defined(SCCSID)
#if 0
static char sccsid[] = "@(#)vi.c 8.1 (Berkeley) 6/4/93";
#else
-__RCSID("$NetBSD: vi.c,v 1.34 2011/02/22 05:45:08 joerg Exp $");
+__RCSID("$NetBSD: vi.c,v 1.62 2016/05/09 21:46:56 christos Exp $");
#endif
#endif /* not lint && not SCCSID */
/*
* vi.c: Vi mode commands.
*/
+#include <sys/wait.h>
+#include <ctype.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
#include "el.h"
+#include "common.h"
+#include "emacs.h"
+#include "fcns.h"
+#include "vi.h"
-private el_action_t cv_action(EditLine *, Int);
-private el_action_t cv_paste(EditLine *, Int);
+static el_action_t cv_action(EditLine *, wint_t);
+static el_action_t cv_paste(EditLine *, wint_t);
/* cv_action():
* Handle vi actions.
*/
-private el_action_t
-cv_action(EditLine *el, Int c)
+static el_action_t
+cv_action(EditLine *el, wint_t c)
{
if (el->el_chared.c_vcmd.action != NOP) {
/* 'cc', 'dd' and (possibly) friends */
- if (c != el->el_chared.c_vcmd.action)
+ if (c != (wint_t)el->el_chared.c_vcmd.action)
return CC_ERROR;
if (!(c & YANK))
@@ -79,26 +85,27 @@ cv_action(EditLine *el, Int c)
if (c & INSERT)
el->el_map.current = el->el_map.key;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
el->el_chared.c_vcmd.pos = el->el_line.cursor;
el->el_chared.c_vcmd.action = c;
- return (CC_ARGHACK);
+ return CC_ARGHACK;
}
/* cv_paste():
* Paste previous deletion before or after the cursor
*/
-private el_action_t
-cv_paste(EditLine *el, Int c)
+static el_action_t
+cv_paste(EditLine *el, wint_t c)
{
c_kill_t *k = &el->el_chared.c_kill;
size_t len = (size_t)(k->last - k->buf);
if (k->buf == NULL || len == 0)
- return (CC_ERROR);
+ return CC_ERROR;
#ifdef DEBUG_PASTE
- (void) fprintf(el->el_errfile, "Paste: \"%.*s\"\n", (int)len, k->buf);
+ (void) fprintf(el->el_errfile, "Paste: \"%.*ls\"\n", (int)len,
+ k->buf);
#endif
cv_undo(el);
@@ -108,11 +115,11 @@ cv_paste(EditLine *el, Int c)
c_insert(el, (int)len);
if (el->el_line.cursor + len > el->el_line.lastchar)
- return (CC_ERROR);
+ return CC_ERROR;
(void) memcpy(el->el_line.cursor, k->buf, len *
sizeof(*el->el_line.cursor));
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -120,12 +127,12 @@ cv_paste(EditLine *el, Int c)
* Vi paste previous deletion to the right of the cursor
* [p]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_paste_next(EditLine *el, Int c __attribute__((__unused__)))
+vi_paste_next(EditLine *el, wint_t c __attribute__((__unused__)))
{
- return (cv_paste(el, 0));
+ return cv_paste(el, 0);
}
@@ -133,12 +140,12 @@ vi_paste_next(EditLine *el, Int c __attribute__((__unused__)))
* Vi paste previous deletion to the left of the cursor
* [P]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_paste_prev(EditLine *el, Int c __attribute__((__unused__)))
+vi_paste_prev(EditLine *el, wint_t c __attribute__((__unused__)))
{
- return (cv_paste(el, 1));
+ return cv_paste(el, 1);
}
@@ -146,13 +153,13 @@ vi_paste_prev(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the previous space delimited word
* [B]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_prev_big_word(EditLine *el, Int c __attribute__((__unused__)))
+vi_prev_big_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor == el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = cv_prev_word(el->el_line.cursor,
el->el_line.buffer,
@@ -161,9 +168,9 @@ vi_prev_big_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -171,13 +178,13 @@ vi_prev_big_word(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the previous word
* [b]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_prev_word(EditLine *el, Int c __attribute__((__unused__)))
+vi_prev_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor == el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = cv_prev_word(el->el_line.cursor,
el->el_line.buffer,
@@ -186,9 +193,9 @@ vi_prev_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -196,13 +203,13 @@ vi_prev_word(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the next space delimited word
* [W]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_next_big_word(EditLine *el, Int c __attribute__((__unused__)))
+vi_next_big_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor >= el->el_line.lastchar - 1)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
el->el_line.lastchar, el->el_state.argument, cv__isWord);
@@ -210,9 +217,9 @@ vi_next_big_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_map.type == MAP_VI)
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -220,13 +227,13 @@ vi_next_big_word(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the next word
* [w]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_next_word(EditLine *el, Int c __attribute__((__unused__)))
+vi_next_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor >= el->el_line.lastchar - 1)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = cv_next_word(el, el->el_line.cursor,
el->el_line.lastchar, el->el_state.argument, cv__isword);
@@ -234,9 +241,9 @@ vi_next_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_map.type == MAP_VI)
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -244,21 +251,21 @@ vi_next_word(EditLine *el, Int c __attribute__((__unused__)))
* Vi change case of character under the cursor and advance one character
* [~]
*/
-protected el_action_t
-vi_change_case(EditLine *el, Int c)
+libedit_private el_action_t
+vi_change_case(EditLine *el, wint_t c)
{
int i;
if (el->el_line.cursor >= el->el_line.lastchar)
- return (CC_ERROR);
+ return CC_ERROR;
cv_undo(el);
for (i = 0; i < el->el_state.argument; i++) {
c = *el->el_line.cursor;
- if (Isupper(c))
- *el->el_line.cursor = Tolower(c);
- else if (Islower(c))
- *el->el_line.cursor = Toupper(c);
+ if (iswupper(c))
+ *el->el_line.cursor = towlower(c);
+ else if (iswlower(c))
+ *el->el_line.cursor = towupper(c);
if (++el->el_line.cursor >= el->el_line.lastchar) {
el->el_line.cursor--;
@@ -275,16 +282,16 @@ vi_change_case(EditLine *el, Int c)
* Vi change prefix command
* [c]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_change_meta(EditLine *el, Int c __attribute__((__unused__)))
+vi_change_meta(EditLine *el, wint_t c __attribute__((__unused__)))
{
/*
* Delete with insert == change: first we delete and then we leave in
* insert mode.
*/
- return (cv_action(el, DELETE | INSERT));
+ return cv_action(el, DELETE | INSERT);
}
@@ -292,15 +299,15 @@ vi_change_meta(EditLine *el, Int c __attribute__((__unused__)))
* Vi enter insert mode at the beginning of line
* [I]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_insert_at_bol(EditLine *el, Int c __attribute__((__unused__)))
+vi_insert_at_bol(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_line.cursor = el->el_line.buffer;
cv_undo(el);
el->el_map.current = el->el_map.key;
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -308,9 +315,9 @@ vi_insert_at_bol(EditLine *el, Int c __attribute__((__unused__)))
* Vi replace character under the cursor with the next character typed
* [r]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_replace_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_replace_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor >= el->el_line.lastchar)
@@ -319,7 +326,7 @@ vi_replace_char(EditLine *el, Int c __attribute__((__unused__)))
el->el_map.current = el->el_map.key;
el->el_state.inputmode = MODE_REPLACE_1;
cv_undo(el);
- return (CC_ARGHACK);
+ return CC_ARGHACK;
}
@@ -327,15 +334,15 @@ vi_replace_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi enter replace mode
* [R]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_replace_mode(EditLine *el, Int c __attribute__((__unused__)))
+vi_replace_mode(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_map.current = el->el_map.key;
el->el_state.inputmode = MODE_REPLACE;
cv_undo(el);
- return (CC_NORM);
+ return CC_NORM;
}
@@ -343,14 +350,14 @@ vi_replace_mode(EditLine *el, Int c __attribute__((__unused__)))
* Vi replace character under the cursor and enter insert mode
* [s]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_substitute_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_substitute_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
c_delafter(el, el->el_state.argument);
el->el_map.current = el->el_map.key;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -358,9 +365,9 @@ vi_substitute_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi substitute entire line
* [S]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_substitute_line(EditLine *el, Int c __attribute__((__unused__)))
+vi_substitute_line(EditLine *el, wint_t c __attribute__((__unused__)))
{
cv_undo(el);
@@ -368,7 +375,7 @@ vi_substitute_line(EditLine *el, Int c __attribute__((__unused__)))
(int)(el->el_line.lastchar - el->el_line.buffer));
(void) em_kill_line(el, 0);
el->el_map.current = el->el_map.key;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -376,9 +383,9 @@ vi_substitute_line(EditLine *el, Int c __attribute__((__unused__)))
* Vi change to end of line
* [C]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_change_to_eol(EditLine *el, Int c __attribute__((__unused__)))
+vi_change_to_eol(EditLine *el, wint_t c __attribute__((__unused__)))
{
cv_undo(el);
@@ -386,7 +393,7 @@ vi_change_to_eol(EditLine *el, Int c __attribute__((__unused__)))
(int)(el->el_line.lastchar - el->el_line.cursor));
(void) ed_kill_line(el, 0);
el->el_map.current = el->el_map.key;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -394,14 +401,14 @@ vi_change_to_eol(EditLine *el, Int c __attribute__((__unused__)))
* Vi enter insert mode
* [i]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_insert(EditLine *el, Int c __attribute__((__unused__)))
+vi_insert(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_map.current = el->el_map.key;
cv_undo(el);
- return (CC_NORM);
+ return CC_NORM;
}
@@ -409,9 +416,9 @@ vi_insert(EditLine *el, Int c __attribute__((__unused__)))
* Vi enter insert mode after the cursor
* [a]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_add(EditLine *el, Int c __attribute__((__unused__)))
+vi_add(EditLine *el, wint_t c __attribute__((__unused__)))
{
int ret;
@@ -426,7 +433,7 @@ vi_add(EditLine *el, Int c __attribute__((__unused__)))
cv_undo(el);
- return (ret);
+ return (el_action_t)ret;
}
@@ -434,15 +441,15 @@ vi_add(EditLine *el, Int c __attribute__((__unused__)))
* Vi enter insert mode at end of line
* [A]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_add_at_eol(EditLine *el, Int c __attribute__((__unused__)))
+vi_add_at_eol(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_map.current = el->el_map.key;
el->el_line.cursor = el->el_line.lastchar;
cv_undo(el);
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -450,12 +457,12 @@ vi_add_at_eol(EditLine *el, Int c __attribute__((__unused__)))
* Vi delete prefix command
* [d]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_delete_meta(EditLine *el, Int c __attribute__((__unused__)))
+vi_delete_meta(EditLine *el, wint_t c __attribute__((__unused__)))
{
- return (cv_action(el, DELETE));
+ return cv_action(el, DELETE);
}
@@ -463,13 +470,13 @@ vi_delete_meta(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the end of the current space delimited word
* [E]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_end_big_word(EditLine *el, Int c)
+vi_end_big_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor == el->el_line.lastchar)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = cv__endword(el->el_line.cursor,
el->el_line.lastchar, el->el_state.argument, cv__isWord);
@@ -477,9 +484,9 @@ vi_end_big_word(EditLine *el, Int c)
if (el->el_chared.c_vcmd.action != NOP) {
el->el_line.cursor++;
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -487,13 +494,13 @@ vi_end_big_word(EditLine *el, Int c)
* Vi move to the end of the current word
* [e]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_end_word(EditLine *el, Int c __attribute__((__unused__)))
+vi_end_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor == el->el_line.lastchar)
- return (CC_ERROR);
+ return CC_ERROR;
el->el_line.cursor = cv__endword(el->el_line.cursor,
el->el_line.lastchar, el->el_state.argument, cv__isword);
@@ -501,9 +508,9 @@ vi_end_word(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_chared.c_vcmd.action != NOP) {
el->el_line.cursor++;
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -511,9 +518,9 @@ vi_end_word(EditLine *el, Int c __attribute__((__unused__)))
* Vi undo last change
* [u]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_undo(EditLine *el, Int c __attribute__((__unused__)))
+vi_undo(EditLine *el, wint_t c __attribute__((__unused__)))
{
c_undo_t un = el->el_chared.c_undo;
@@ -530,7 +537,7 @@ vi_undo(EditLine *el, Int c __attribute__((__unused__)))
el->el_line.cursor = un.buf + un.cursor;
el->el_line.lastchar = un.buf + un.len;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -538,9 +545,9 @@ vi_undo(EditLine *el, Int c __attribute__((__unused__)))
* Vi enter command mode (use alternative key bindings)
* [<ESC>]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_command_mode(EditLine *el, Int c __attribute__((__unused__)))
+vi_command_mode(EditLine *el, wint_t c __attribute__((__unused__)))
{
/* [Esc] cancels pending action */
@@ -555,7 +562,7 @@ vi_command_mode(EditLine *el, Int c __attribute__((__unused__)))
if (el->el_line.cursor > el->el_line.buffer)
el->el_line.cursor--;
#endif
- return (CC_CURSOR);
+ return CC_CURSOR;
}
@@ -563,8 +570,8 @@ vi_command_mode(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the beginning of line
* [0]
*/
-protected el_action_t
-vi_zero(EditLine *el, Int c)
+libedit_private el_action_t
+vi_zero(EditLine *el, wint_t c)
{
if (el->el_state.doingarg)
@@ -573,27 +580,27 @@ vi_zero(EditLine *el, Int c)
el->el_line.cursor = el->el_line.buffer;
if (el->el_chared.c_vcmd.action != NOP) {
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
/* vi_delete_prev_char():
- * Vi move to previous character (backspace)
+ * Vi move to previous character (backspace)
* [^H] in insert mode only
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_delete_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_line.cursor <= el->el_line.buffer)
- return (CC_ERROR);
+ return CC_ERROR;
c_delbefore1(el);
el->el_line.cursor--;
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -601,34 +608,34 @@ vi_delete_prev_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi list choices for completion or indicate end of file if empty line
* [^D]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_list_or_eof(EditLine *el, Int c)
+vi_list_or_eof(EditLine *el, wint_t c)
{
if (el->el_line.cursor == el->el_line.lastchar) {
if (el->el_line.cursor == el->el_line.buffer) {
- term_writec(el, c); /* then do a EOF */
- return (CC_EOF);
+ terminal_writec(el, c); /* then do a EOF */
+ return CC_EOF;
} else {
/*
* Here we could list completions, but it is an
* error right now
*/
- term_beep(el);
- return (CC_ERROR);
+ terminal_beep(el);
+ return CC_ERROR;
}
} else {
#ifdef notyet
re_goto_bottom(el);
*el->el_line.lastchar = '\0'; /* just in case */
- return (CC_LIST_CHOICES);
+ return CC_LIST_CHOICES;
#else
/*
* Just complain for now.
*/
- term_beep(el);
- return (CC_ERROR);
+ terminal_beep(el);
+ return CC_ERROR;
#endif
}
}
@@ -638,11 +645,11 @@ vi_list_or_eof(EditLine *el, Int c)
* Vi cut from beginning of line to cursor
* [^U]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_kill_line_prev(EditLine *el, Int c __attribute__((__unused__)))
+vi_kill_line_prev(EditLine *el, wint_t c __attribute__((__unused__)))
{
- Char *kp, *cp;
+ wchar_t *kp, *cp;
cp = el->el_line.buffer;
kp = el->el_chared.c_kill.buf;
@@ -651,7 +658,7 @@ vi_kill_line_prev(EditLine *el, Int c __attribute__((__unused__)))
el->el_chared.c_kill.last = kp;
c_delbefore(el, (int)(el->el_line.cursor - el->el_line.buffer));
el->el_line.cursor = el->el_line.buffer; /* zap! */
- return (CC_REFRESH);
+ return CC_REFRESH;
}
@@ -659,12 +666,12 @@ vi_kill_line_prev(EditLine *el, Int c __attribute__((__unused__)))
* Vi search history previous
* [?]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_search_prev(EditLine *el, Int c __attribute__((__unused__)))
+vi_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
{
- return (cv_search(el, ED_SEARCH_PREV_HISTORY));
+ return cv_search(el, ED_SEARCH_PREV_HISTORY);
}
@@ -672,12 +679,12 @@ vi_search_prev(EditLine *el, Int c __attribute__((__unused__)))
* Vi search history next
* [/]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_search_next(EditLine *el, Int c __attribute__((__unused__)))
+vi_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
{
- return (cv_search(el, ED_SEARCH_NEXT_HISTORY));
+ return cv_search(el, ED_SEARCH_NEXT_HISTORY);
}
@@ -685,15 +692,15 @@ vi_search_next(EditLine *el, Int c __attribute__((__unused__)))
* Vi repeat current search in the same search direction
* [n]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_repeat_search_next(EditLine *el, Int c __attribute__((__unused__)))
+vi_repeat_search_next(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_search.patlen == 0)
- return (CC_ERROR);
+ return CC_ERROR;
else
- return (cv_repeat_srch(el, el->el_search.patdir));
+ return cv_repeat_srch(el, el->el_search.patdir);
}
@@ -702,12 +709,12 @@ vi_repeat_search_next(EditLine *el, Int c __attribute__((__unused__)))
* [N]
*/
/*ARGSUSED*/
-protected el_action_t
-vi_repeat_search_prev(EditLine *el, Int c __attribute__((__unused__)))
+libedit_private el_action_t
+vi_repeat_search_prev(EditLine *el, wint_t c __attribute__((__unused__)))
{
if (el->el_search.patlen == 0)
- return (CC_ERROR);
+ return CC_ERROR;
else
return (cv_repeat_srch(el,
el->el_search.patdir == ED_SEARCH_PREV_HISTORY ?
@@ -719,9 +726,9 @@ vi_repeat_search_prev(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the character specified next
* [f]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_next_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 0);
}
@@ -731,9 +738,9 @@ vi_next_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi move to the character specified previous
* [F]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_prev_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 0);
}
@@ -743,9 +750,9 @@ vi_prev_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi move up to the character specified next
* [t]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_to_next_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_to_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
return cv_csearch(el, CHAR_FWD, -1, el->el_state.argument, 1);
}
@@ -755,9 +762,9 @@ vi_to_next_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi move up to the character specified previous
* [T]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_to_prev_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_to_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
return cv_csearch(el, CHAR_BACK, -1, el->el_state.argument, 1);
}
@@ -767,9 +774,9 @@ vi_to_prev_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi repeat current character search in the same search direction
* [;]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_repeat_next_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_repeat_next_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
return cv_csearch(el, el->el_search.chadir, el->el_search.chacha,
@@ -781,9 +788,9 @@ vi_repeat_next_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi repeat current character search in the opposite search direction
* [,]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_repeat_prev_char(EditLine *el, Int c __attribute__((__unused__)))
+vi_repeat_prev_char(EditLine *el, wint_t c __attribute__((__unused__)))
{
el_action_t r;
int dir = el->el_search.chadir;
@@ -799,22 +806,22 @@ vi_repeat_prev_char(EditLine *el, Int c __attribute__((__unused__)))
* Vi go to matching () {} or []
* [%]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_match(EditLine *el, Int c)
+vi_match(EditLine *el, wint_t c __attribute__((__unused__)))
{
- const Char match_chars[] = STR("()[]{}");
- Char *cp;
+ const wchar_t match_chars[] = L"()[]{}";
+ wchar_t *cp;
size_t delta, i, count;
- Char o_ch, c_ch;
+ wchar_t o_ch, c_ch;
*el->el_line.lastchar = '\0'; /* just in case */
- i = Strcspn(el->el_line.cursor, match_chars);
+ i = wcscspn(el->el_line.cursor, match_chars);
o_ch = el->el_line.cursor[i];
if (o_ch == 0)
return CC_ERROR;
- delta = Strchr(match_chars, o_ch) - match_chars;
+ delta = (size_t)(wcschr(match_chars, o_ch) - match_chars);
c_ch = match_chars[delta ^ 1];
count = 1;
delta = 1 - (delta & 1) * 2;
@@ -837,18 +844,18 @@ vi_match(EditLine *el, Int c)
if (delta > 0)
el->el_line.cursor++;
cv_delfini(el);
- return (CC_REFRESH);
+ return CC_REFRESH;
}
- return (CC_CURSOR);
+ return CC_CURSOR;
}
/* vi_undo_line():
* Vi undo all changes to line
* [U]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_undo_line(EditLine *el, Int c)
+vi_undo_line(EditLine *el, wint_t c __attribute__((__unused__)))
{
cv_undo(el);
@@ -860,9 +867,9 @@ vi_undo_line(EditLine *el, Int c)
* [|]
* NB netbsd vi goes to screen column 'n', posix says nth character
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_to_column(EditLine *el, Int c)
+vi_to_column(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_line.cursor = el->el_line.buffer;
@@ -874,9 +881,9 @@ vi_to_column(EditLine *el, Int c)
* Vi yank to end of line
* [Y]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_yank_end(EditLine *el, Int c)
+vi_yank_end(EditLine *el, wint_t c __attribute__((__unused__)))
{
cv_yank(el, el->el_line.cursor,
@@ -888,9 +895,9 @@ vi_yank_end(EditLine *el, Int c)
* Vi yank
* [y]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_yank(EditLine *el, Int c)
+vi_yank(EditLine *el, wint_t c __attribute__((__unused__)))
{
return cv_action(el, YANK);
@@ -900,9 +907,9 @@ vi_yank(EditLine *el, Int c)
* Vi comment out current command
* [#]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_comment_out(EditLine *el, Int c)
+vi_comment_out(EditLine *el, wint_t c __attribute__((__unused__)))
{
el->el_line.cursor = el->el_line.buffer;
@@ -918,51 +925,42 @@ vi_comment_out(EditLine *el, Int c)
* NB: posix implies that we should enter insert mode, however
* this is against historical precedent...
*/
-#undef __weak_reference /* __weak_reference is different on freebsd */
-#ifdef __weak_reference
-__weakref_visible char *my_get_alias_text(const char *)
- __weak_reference(get_alias_text);
-#endif
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_alias(EditLine *el, Int c)
+vi_alias(EditLine *el, wint_t c __attribute__((__unused__)))
{
-#ifdef __weak_reference
char alias_name[3];
- char *alias_text;
+ const char *alias_text;
- if (my_get_alias_text == 0) {
+ if (el->el_chared.c_aliasfun == NULL)
return CC_ERROR;
- }
alias_name[0] = '_';
alias_name[2] = 0;
if (el_getc(el, &alias_name[1]) != 1)
return CC_ERROR;
- alias_text = my_get_alias_text(alias_name);
+ alias_text = (*el->el_chared.c_aliasfun)(el->el_chared.c_aliasarg,
+ alias_name);
if (alias_text != NULL)
- FUN(el,push)(el, ct_decode_string(alias_text, &el->el_scratch));
+ el_wpush(el, ct_decode_string(alias_text, &el->el_scratch));
return CC_NORM;
-#else
- return CC_ERROR;
-#endif
}
/* vi_to_history_line():
* Vi go to specified history file line.
* [G]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_to_history_line(EditLine *el, Int c)
+vi_to_history_line(EditLine *el, wint_t c __attribute__((__unused__)))
{
int sv_event_no = el->el_history.eventno;
el_action_t rval;
if (el->el_history.eventno == 0) {
- (void) Strncpy(el->el_history.buf, el->el_line.buffer,
+ (void) wcsncpy(el->el_history.buf, el->el_line.buffer,
EL_BUFSIZ);
el->el_history.last = el->el_history.buf +
(el->el_line.lastchar - el->el_line.buffer);
@@ -998,18 +996,18 @@ vi_to_history_line(EditLine *el, Int c)
* Vi edit history line with vi
* [v]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_histedit(EditLine *el, Int c)
+vi_histedit(EditLine *el, wint_t c __attribute__((__unused__)))
{
int fd;
pid_t pid;
ssize_t st;
int status;
char tempfile[] = "/tmp/histedit.XXXXXXXXXX";
- char *cp;
+ char *cp = NULL;
size_t len;
- Char *line;
+ wchar_t *line = NULL;
if (el->el_state.doingarg) {
if (vi_to_history_line(el, 0) == CC_ERROR)
@@ -1021,32 +1019,23 @@ vi_histedit(EditLine *el, Int c)
return CC_ERROR;
len = (size_t)(el->el_line.lastchar - el->el_line.buffer);
#define TMP_BUFSIZ (EL_BUFSIZ * MB_LEN_MAX)
- cp = el_malloc(TMP_BUFSIZ);
- if (cp == NULL) {
- unlink(tempfile);
- close(fd);
- return CC_ERROR;
- }
- line = el_malloc(len * sizeof(*line));
- if (line == NULL) {
- el_free((ptr_t)cp);
- return CC_ERROR;
- }
- Strncpy(line, el->el_line.buffer, len);
+ cp = el_malloc(TMP_BUFSIZ * sizeof(*cp));
+ if (cp == NULL)
+ goto error;
+ line = el_malloc(len * sizeof(*line) + 1);
+ if (line == NULL)
+ goto error;
+ wcsncpy(line, el->el_line.buffer, len);
line[len] = '\0';
- ct_wcstombs(cp, line, TMP_BUFSIZ - 1);
+ wcstombs(cp, line, TMP_BUFSIZ - 1);
cp[TMP_BUFSIZ - 1] = '\0';
len = strlen(cp);
write(fd, cp, len);
- write(fd, "\n", 1);
+ write(fd, "\n", (size_t)1);
pid = fork();
switch (pid) {
case -1:
- close(fd);
- unlink(tempfile);
- el_free(cp);
- el_free(line);
- return CC_ERROR;
+ goto error;
case 0:
close(fd);
execlp("vi", "vi", tempfile, (char *)NULL);
@@ -1056,12 +1045,12 @@ vi_histedit(EditLine *el, Int c)
while (waitpid(pid, &status, 0) != pid)
continue;
lseek(fd, (off_t)0, SEEK_SET);
- st = read(fd, cp, TMP_BUFSIZ);
+ st = read(fd, cp, TMP_BUFSIZ - 1);
if (st > 0) {
- len = (size_t)(el->el_line.lastchar -
- el->el_line.buffer);
- len = ct_mbstowcs(el->el_line.buffer, cp, len);
- if (len > 0 && el->el_line.buffer[len -1] == '\n')
+ cp[st] = '\0';
+ len = (size_t)(el->el_line.limit - el->el_line.buffer);
+ len = mbstowcs(el->el_line.buffer, cp, len);
+ if (len > 0 && el->el_line.buffer[len - 1] == '\n')
--len;
}
else
@@ -1077,6 +1066,12 @@ vi_histedit(EditLine *el, Int c)
unlink(tempfile);
/* return CC_REFRESH; */
return ed_newline(el, 0);
+error:
+ el_free(line);
+ el_free(cp);
+ close(fd);
+ unlink(tempfile);
+ return CC_ERROR;
}
/* vi_history_word():
@@ -1085,33 +1080,33 @@ vi_histedit(EditLine *el, Int c)
* Who knows where this one came from!
* '_' in vi means 'entire current line', so 'cc' is a synonym for 'c_'
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_history_word(EditLine *el, Int c)
+vi_history_word(EditLine *el, wint_t c __attribute__((__unused__)))
{
- const Char *wp = HIST_FIRST(el);
- const Char *wep, *wsp;
+ const wchar_t *wp = HIST_FIRST(el);
+ const wchar_t *wep, *wsp;
int len;
- Char *cp;
- const Char *lim;
+ wchar_t *cp;
+ const wchar_t *lim;
if (wp == NULL)
return CC_ERROR;
- wep = wsp = 0;
+ wep = wsp = NULL;
do {
- while (Isspace(*wp))
+ while (iswspace(*wp))
wp++;
if (*wp == 0)
break;
wsp = wp;
- while (*wp && !Isspace(*wp))
+ while (*wp && !iswspace(*wp))
wp++;
wep = wp;
} while ((!el->el_state.doingarg || --el->el_state.argument > 0)
&& *wp != 0);
- if (wsp == 0 || (el->el_state.doingarg && el->el_state.argument != 0))
+ if (wsp == NULL || (el->el_state.doingarg && el->el_state.argument != 0))
return CC_ERROR;
cv_undo(el);
@@ -1135,9 +1130,9 @@ vi_history_word(EditLine *el, Int c)
* Vi redo last non-motion command
* [.]
*/
-protected el_action_t
+libedit_private el_action_t
/*ARGSUSED*/
-vi_redo(EditLine *el, Int c)
+vi_redo(EditLine *el, wint_t c __attribute__((__unused__)))
{
c_redo_t *r = &el->el_chared.c_redo;
@@ -1153,10 +1148,10 @@ vi_redo(EditLine *el, Int c)
/* sanity */
r->pos = r->lim - 1;
r->pos[0] = 0;
- FUN(el,push)(el, r->buf);
+ el_wpush(el, r->buf);
}
el->el_state.thiscmd = r->cmd;
el->el_state.thisch = r->ch;
- return (*el->el_map.func[r->cmd])(el, r->ch);
+ return (*el->el_map.func[r->cmd])(el, r->ch);
}
diff --git a/lib/libedit/src/vis.c b/lib/libedit/src/vis.c
index 2641c4a9f675..1071803c4c7f 100644
--- a/lib/libedit/src/vis.c
+++ b/lib/libedit/src/vis.c
@@ -1,4 +1,4 @@
-/* $NetBSD: vis.c,v 1.41 2009/11/23 10:08:47 plunky Exp $ */
+/* $NetBSD: vis.c,v 1.71 2016/01/14 20:41:23 christos Exp $ */
/*-
* Copyright (c) 1989, 1993
@@ -56,25 +56,19 @@
*/
#include "config.h"
-
#if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: vis.c,v 1.41 2009/11/23 10:08:47 plunky Exp $");
+__RCSID("$NetBSD: vis.c,v 1.71 2016/01/14 20:41:23 christos Exp $");
#endif /* LIBC_SCCS and not lint */
#include <sys/types.h>
+#include <sys/param.h>
#include <assert.h>
#include <vis.h>
+#include <errno.h>
#include <stdlib.h>
-
-#ifdef __weak_alias
-__weak_alias(strsvis,_strsvis)
-__weak_alias(strsvisx,_strsvisx)
-__weak_alias(strvis,_strvis)
-__weak_alias(strvisx,_strvisx)
-__weak_alias(svis,_svis)
-__weak_alias(vis,_vis)
-#endif
+#include <wchar.h>
+#include <wctype.h>
#if !HAVE_VIS || !HAVE_SVIS
#include <ctype.h>
@@ -82,54 +76,69 @@ __weak_alias(vis,_vis)
#include <stdio.h>
#include <string.h>
-static char *do_svis(char *, int, int, int, const char *);
+/*
+ * The reason for going through the trouble to deal with character encodings
+ * in vis(3), is that we use this to safe encode output of commands. This
+ * safe encoding varies depending on the character set. For example if we
+ * display ps output in French, we don't want to display French characters
+ * as M-foo.
+ */
+
+static wchar_t *do_svis(wchar_t *, wint_t, int, wint_t, const wchar_t *);
#undef BELL
-#define BELL '\a'
-
-#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7')
-#define iswhite(c) (c == ' ' || c == '\t' || c == '\n')
-#define issafe(c) (c == '\b' || c == BELL || c == '\r')
-#define xtoa(c) "0123456789abcdef"[c]
-#define XTOA(c) "0123456789ABCDEF"[c]
-
-#define MAXEXTRAS 5
-
-#define MAKEEXTRALIST(flag, extra, orig_str) \
-do { \
- const char *orig = orig_str; \
- const char *o = orig; \
- char *e; \
- while (*o++) \
- continue; \
- extra = malloc((size_t)((o - orig) + MAXEXTRAS)); \
- if (!extra) break; \
- for (o = orig, e = extra; (*e++ = *o++) != '\0';) \
- continue; \
- e--; \
- if (flag & VIS_SP) *e++ = ' '; \
- if (flag & VIS_TAB) *e++ = '\t'; \
- if (flag & VIS_NL) *e++ = '\n'; \
- if ((flag & VIS_NOSLASH) == 0) *e++ = '\\'; \
- *e = '\0'; \
-} while (/*CONSTCOND*/0)
+#define BELL L'\a'
+
+#if defined(LC_C_LOCALE)
+#define iscgraph(c) isgraph_l(c, LC_C_LOCALE)
+#else
+/* Keep it simple for now, no locale stuff */
+#define iscgraph(c) isgraph(c)
+#ifdef notyet
+#include <locale.h>
+static int
+iscgraph(int c) {
+ int rv;
+ char *ol;
+
+ ol = setlocale(LC_CTYPE, "C");
+ rv = isgraph(c);
+ if (ol)
+ setlocale(LC_CTYPE, ol);
+ return rv;
+}
+#endif
+#endif
+
+#define ISGRAPH(flags, c) \
+ (((flags) & VIS_NOLOCALE) ? iscgraph(c) : iswgraph(c))
+
+#define iswoctal(c) (((u_char)(c)) >= L'0' && ((u_char)(c)) <= L'7')
+#define iswwhite(c) (c == L' ' || c == L'\t' || c == L'\n')
+#define iswsafe(c) (c == L'\b' || c == BELL || c == L'\r')
+#define xtoa(c) L"0123456789abcdef"[c]
+#define XTOA(c) L"0123456789ABCDEF"[c]
+
+#define MAXEXTRAS 30
+
+static const wchar_t char_shell[] = L"'`\";&<>()|{}]\\$!^~";
+static const wchar_t char_glob[] = L"*?[#";
/*
* This is do_hvis, for HTTP style (RFC 1808)
*/
-static char *
-do_hvis(char *dst, int c, int flag, int nextc, const char *extra)
+static wchar_t *
+do_hvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
{
-
- if ((isascii(c) && isalnum(c))
+ if (iswalnum(c)
/* safe */
- || c == '$' || c == '-' || c == '_' || c == '.' || c == '+'
+ || c == L'$' || c == L'-' || c == L'_' || c == L'.' || c == L'+'
/* extra */
- || c == '!' || c == '*' || c == '\'' || c == '(' || c == ')'
- || c == ',') {
- dst = do_svis(dst, c, flag, nextc, extra);
- } else {
- *dst++ = '%';
+ || c == L'!' || c == L'*' || c == L'\'' || c == L'(' || c == L')'
+ || c == L',')
+ dst = do_svis(dst, c, flags, nextc, extra);
+ else {
+ *dst++ = L'%';
*dst++ = xtoa(((unsigned int)c >> 4) & 0xf);
*dst++ = xtoa((unsigned int)c & 0xf);
}
@@ -141,214 +150,488 @@ do_hvis(char *dst, int c, int flag, int nextc, const char *extra)
* This is do_mvis, for Quoted-Printable MIME (RFC 2045)
* NB: No handling of long lines or CRLF.
*/
-static char *
-do_mvis(char *dst, int c, int flag, int nextc, const char *extra)
+static wchar_t *
+do_mvis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
{
- if ((c != '\n') &&
+ if ((c != L'\n') &&
/* Space at the end of the line */
- ((isspace(c) && (nextc == '\r' || nextc == '\n')) ||
+ ((iswspace(c) && (nextc == L'\r' || nextc == L'\n')) ||
/* Out of range */
- (!isspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
+ (!iswspace(c) && (c < 33 || (c > 60 && c < 62) || c > 126)) ||
/* Specific char to be escaped */
- strchr("#$@[\\]^`{|}~", c) != NULL)) {
- *dst++ = '=';
+ wcschr(L"#$@[\\]^`{|}~", c) != NULL)) {
+ *dst++ = L'=';
*dst++ = XTOA(((unsigned int)c >> 4) & 0xf);
*dst++ = XTOA((unsigned int)c & 0xf);
- } else {
- dst = do_svis(dst, c, flag, nextc, extra);
- }
+ } else
+ dst = do_svis(dst, c, flags, nextc, extra);
return dst;
}
/*
- * This is do_vis, the central code of vis.
- * dst: Pointer to the destination buffer
- * c: Character to encode
- * flag: Flag word
- * nextc: The character following 'c'
- * extra: Pointer to the list of extra characters to be
- * backslash-protected.
+ * Output single byte of multibyte character.
*/
-static char *
-do_svis(char *dst, int c, int flag, int nextc, const char *extra)
+static wchar_t *
+do_mbyte(wchar_t *dst, wint_t c, int flags, wint_t nextc, int iswextra)
{
- int isextra;
- isextra = strchr(extra, c) != NULL;
- if (!isextra && isascii(c) && (isgraph(c) || iswhite(c) ||
- ((flag & VIS_SAFE) && issafe(c)))) {
- *dst++ = c;
- return dst;
- }
- if (flag & VIS_CSTYLE) {
+ if (flags & VIS_CSTYLE) {
switch (c) {
- case '\n':
- *dst++ = '\\'; *dst++ = 'n';
+ case L'\n':
+ *dst++ = L'\\'; *dst++ = L'n';
return dst;
- case '\r':
- *dst++ = '\\'; *dst++ = 'r';
+ case L'\r':
+ *dst++ = L'\\'; *dst++ = L'r';
return dst;
- case '\b':
- *dst++ = '\\'; *dst++ = 'b';
+ case L'\b':
+ *dst++ = L'\\'; *dst++ = L'b';
return dst;
case BELL:
- *dst++ = '\\'; *dst++ = 'a';
+ *dst++ = L'\\'; *dst++ = L'a';
return dst;
- case '\v':
- *dst++ = '\\'; *dst++ = 'v';
+ case L'\v':
+ *dst++ = L'\\'; *dst++ = L'v';
return dst;
- case '\t':
- *dst++ = '\\'; *dst++ = 't';
+ case L'\t':
+ *dst++ = L'\\'; *dst++ = L't';
return dst;
- case '\f':
- *dst++ = '\\'; *dst++ = 'f';
+ case L'\f':
+ *dst++ = L'\\'; *dst++ = L'f';
return dst;
- case ' ':
- *dst++ = '\\'; *dst++ = 's';
+ case L' ':
+ *dst++ = L'\\'; *dst++ = L's';
return dst;
- case '\0':
- *dst++ = '\\'; *dst++ = '0';
- if (isoctal(nextc)) {
- *dst++ = '0';
- *dst++ = '0';
+ case L'\0':
+ *dst++ = L'\\'; *dst++ = L'0';
+ if (iswoctal(nextc)) {
+ *dst++ = L'0';
+ *dst++ = L'0';
}
return dst;
+ /* We cannot encode these characters in VIS_CSTYLE
+ * because they special meaning */
+ case L'n':
+ case L'r':
+ case L'b':
+ case L'a':
+ case L'v':
+ case L't':
+ case L'f':
+ case L's':
+ case L'0':
+ case L'M':
+ case L'^':
+ case L'$': /* vis(1) -l */
+ break;
default:
- if (isgraph(c)) {
- *dst++ = '\\'; *dst++ = c;
+ if (ISGRAPH(flags, c) && !iswoctal(c)) {
+ *dst++ = L'\\';
+ *dst++ = c;
return dst;
}
}
}
- if (isextra || ((c & 0177) == ' ') || (flag & VIS_OCTAL)) {
- *dst++ = '\\';
- *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + '0';
- *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + '0';
- *dst++ = (c & 07) + '0';
+ if (iswextra || ((c & 0177) == L' ') || (flags & VIS_OCTAL)) {
+ *dst++ = L'\\';
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 6) & 03) + L'0';
+ *dst++ = (u_char)(((u_int32_t)(u_char)c >> 3) & 07) + L'0';
+ *dst++ = (c & 07) + L'0';
} else {
- if ((flag & VIS_NOSLASH) == 0) *dst++ = '\\';
+ if ((flags & VIS_NOSLASH) == 0)
+ *dst++ = L'\\';
+
if (c & 0200) {
- c &= 0177; *dst++ = 'M';
+ c &= 0177;
+ *dst++ = L'M';
}
- if (iscntrl(c)) {
- *dst++ = '^';
+
+ if (iswcntrl(c)) {
+ *dst++ = L'^';
if (c == 0177)
- *dst++ = '?';
+ *dst++ = L'?';
else
- *dst++ = c + '@';
+ *dst++ = c + L'@';
} else {
- *dst++ = '-'; *dst++ = c;
+ *dst++ = L'-';
+ *dst++ = c;
}
}
+
return dst;
}
-typedef char *(*visfun_t)(char *, int, int, int, const char *);
+/*
+ * This is do_vis, the central code of vis.
+ * dst: Pointer to the destination buffer
+ * c: Character to encode
+ * flags: Flags word
+ * nextc: The character following 'c'
+ * extra: Pointer to the list of extra characters to be
+ * backslash-protected.
+ */
+static wchar_t *
+do_svis(wchar_t *dst, wint_t c, int flags, wint_t nextc, const wchar_t *extra)
+{
+ int iswextra, i, shft;
+ uint64_t bmsk, wmsk;
+
+ iswextra = wcschr(extra, c) != NULL;
+ if (!iswextra && (ISGRAPH(flags, c) || iswwhite(c) ||
+ ((flags & VIS_SAFE) && iswsafe(c)))) {
+ *dst++ = c;
+ return dst;
+ }
+
+ /* See comment in istrsenvisx() output loop, below. */
+ wmsk = 0;
+ for (i = sizeof(wmsk) - 1; i >= 0; i--) {
+ shft = i * NBBY;
+ bmsk = (uint64_t)0xffLL << shft;
+ wmsk |= bmsk;
+ if ((c & wmsk) || i == 0)
+ dst = do_mbyte(dst, (wint_t)(
+ (uint64_t)(c & bmsk) >> shft),
+ flags, nextc, iswextra);
+ }
+
+ return dst;
+}
+
+typedef wchar_t *(*visfun_t)(wchar_t *, wint_t, int, wint_t, const wchar_t *);
/*
* Return the appropriate encoding function depending on the flags given.
*/
static visfun_t
-getvisfun(int flag)
+getvisfun(int flags)
{
- if (flag & VIS_HTTPSTYLE)
+ if (flags & VIS_HTTPSTYLE)
return do_hvis;
- if (flag & VIS_MIMESTYLE)
+ if (flags & VIS_MIMESTYLE)
return do_mvis;
return do_svis;
}
/*
- * svis - visually encode characters, also encoding the characters
- * pointed to by `extra'
+ * Expand list of extra characters to not visually encode.
*/
-char *
-svis(char *dst, int c, int flag, int nextc, const char *extra)
+static wchar_t *
+makeextralist(int flags, const char *src)
{
- char *nextra = NULL;
- visfun_t f;
+ wchar_t *dst, *d;
+ size_t len;
+ const wchar_t *s;
+
+ len = strlen(src);
+ if ((dst = calloc(len + MAXEXTRAS, sizeof(*dst))) == NULL)
+ return NULL;
+
+ if ((flags & VIS_NOLOCALE) || mbstowcs(dst, src, len) == (size_t)-1) {
+ size_t i;
+ for (i = 0; i < len; i++)
+ dst[i] = (wchar_t)(u_char)src[i];
+ d = dst + len;
+ } else
+ d = dst + wcslen(dst);
+
+ if (flags & VIS_GLOB)
+ for (s = char_glob; *s; *d++ = *s++)
+ continue;
+
+ if (flags & VIS_SHELL)
+ for (s = char_shell; *s; *d++ = *s++)
+ continue;
+
+ if (flags & VIS_SP) *d++ = L' ';
+ if (flags & VIS_TAB) *d++ = L'\t';
+ if (flags & VIS_NL) *d++ = L'\n';
+ if ((flags & VIS_NOSLASH) == 0) *d++ = L'\\';
+ *d = L'\0';
- _DIAGASSERT(dst != NULL);
- _DIAGASSERT(extra != NULL);
- MAKEEXTRALIST(flag, nextra, extra);
- if (!nextra) {
- *dst = '\0'; /* can't create nextra, return "" */
- return dst;
- }
- f = getvisfun(flag);
- dst = (*f)(dst, c, flag, nextc, nextra);
- free(nextra);
- *dst = '\0';
return dst;
}
-
/*
- * strsvis, strsvisx - visually encode characters from src into dst
- *
- * Extra is a pointer to a \0-terminated list of characters to
- * be encoded, too. These functions are useful e. g. to
- * encode strings in such a way so that they are not interpreted
- * by a shell.
- *
- * Dst must be 4 times the size of src to account for possible
- * expansion. The length of dst, not including the trailing NULL,
- * is returned.
- *
- * Strsvisx encodes exactly len bytes from src into dst.
- * This is useful for encoding a block of data.
+ * istrsenvisx()
+ * The main internal function.
+ * All user-visible functions call this one.
*/
-int
-strsvis(char *dst, const char *csrc, int flag, const char *extra)
+static int
+istrsenvisx(char **mbdstp, size_t *dlen, const char *mbsrc, size_t mblength,
+ int flags, const char *mbextra, int *cerr_ptr)
{
- int c;
- char *start;
- char *nextra = NULL;
- const unsigned char *src = (const unsigned char *)csrc;
+ wchar_t *dst, *src, *pdst, *psrc, *start, *extra;
+ size_t len, olen;
+ uint64_t bmsk, wmsk;
+ wint_t c;
visfun_t f;
-
- _DIAGASSERT(dst != NULL);
- _DIAGASSERT(src != NULL);
- _DIAGASSERT(extra != NULL);
- MAKEEXTRALIST(flag, nextra, extra);
- if (!nextra) {
- *dst = '\0'; /* can't create nextra, return "" */
- return 0;
+ int clen = 0, cerr, error = -1, i, shft;
+ char *mbdst, *mdst;
+ ssize_t mbslength, maxolen;
+
+ _DIAGASSERT(mbdstp != NULL);
+ _DIAGASSERT(mbsrc != NULL || mblength == 0);
+ _DIAGASSERT(mbextra != NULL);
+
+ /*
+ * Input (mbsrc) is a char string considered to be multibyte
+ * characters. The input loop will read this string pulling
+ * one character, possibly multiple bytes, from mbsrc and
+ * converting each to wchar_t in src.
+ *
+ * The vis conversion will be done using the wide char
+ * wchar_t string.
+ *
+ * This will then be converted back to a multibyte string to
+ * return to the caller.
+ */
+
+ /* Allocate space for the wide char strings */
+ psrc = pdst = extra = NULL;
+ mdst = NULL;
+ if ((psrc = calloc(mblength + 1, sizeof(*psrc))) == NULL)
+ return -1;
+ if ((pdst = calloc((4 * mblength) + 1, sizeof(*pdst))) == NULL)
+ goto out;
+ if (*mbdstp == NULL) {
+ if ((mdst = calloc((4 * mblength) + 1, sizeof(*mdst))) == NULL)
+ goto out;
+ *mbdstp = mdst;
}
- f = getvisfun(flag);
- for (start = dst; (c = *src++) != '\0'; /* empty */)
- dst = (*f)(dst, c, flag, *src, nextra);
- free(nextra);
- *dst = '\0';
- return (int)(dst - start);
-}
+ mbdst = *mbdstp;
+ dst = pdst;
+ src = psrc;
-int
-strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
-{
- unsigned char c;
- char *start;
- char *nextra = NULL;
- const unsigned char *src = (const unsigned char *)csrc;
- visfun_t f;
+ if (flags & VIS_NOLOCALE) {
+ /* Do one byte at a time conversion */
+ cerr = 1;
+ } else {
+ /* Use caller's multibyte conversion error flag. */
+ cerr = cerr_ptr ? *cerr_ptr : 0;
+ }
- _DIAGASSERT(dst != NULL);
- _DIAGASSERT(src != NULL);
- _DIAGASSERT(extra != NULL);
- MAKEEXTRALIST(flag, nextra, extra);
- if (! nextra) {
- *dst = '\0'; /* can't create nextra, return "" */
- return 0;
+ /*
+ * Input loop.
+ * Handle up to mblength characters (not bytes). We do not
+ * stop at NULs because we may be processing a block of data
+ * that includes NULs.
+ */
+ mbslength = (ssize_t)mblength;
+ /*
+ * When inputing a single character, must also read in the
+ * next character for nextc, the look-ahead character.
+ */
+ if (mbslength == 1)
+ mbslength++;
+ while (mbslength > 0) {
+ /* Convert one multibyte character to wchar_t. */
+ if (!cerr)
+ clen = mbtowc(src, mbsrc, MB_LEN_MAX);
+ if (cerr || clen < 0) {
+ /* Conversion error, process as a byte instead. */
+ *src = (wint_t)(u_char)*mbsrc;
+ clen = 1;
+ cerr = 1;
+ }
+ if (clen == 0)
+ /*
+ * NUL in input gives 0 return value. process
+ * as single NUL byte and keep going.
+ */
+ clen = 1;
+ /* Advance buffer character pointer. */
+ src++;
+ /* Advance input pointer by number of bytes read. */
+ mbsrc += clen;
+ /* Decrement input byte count. */
+ mbslength -= clen;
+ }
+ len = src - psrc;
+ src = psrc;
+ /*
+ * In the single character input case, we will have actually
+ * processed two characters, c and nextc. Reset len back to
+ * just a single character.
+ */
+ if (mblength < len)
+ len = mblength;
+
+ /* Convert extra argument to list of characters for this mode. */
+ extra = makeextralist(flags, mbextra);
+ if (!extra) {
+ if (dlen && *dlen == 0) {
+ errno = ENOSPC;
+ goto out;
+ }
+ *mbdst = '\0'; /* can't create extra, return "" */
+ error = 0;
+ goto out;
}
- f = getvisfun(flag);
+ /* Look up which processing function to call. */
+ f = getvisfun(flags);
+
+ /*
+ * Main processing loop.
+ * Call do_Xvis processing function one character at a time
+ * with next character available for look-ahead.
+ */
for (start = dst; len > 0; len--) {
c = *src++;
- dst = (*f)(dst, c, flag, len > 1 ? *src : '\0', nextra);
+ dst = (*f)(dst, c, flags, len >= 1 ? *src : L'\0', extra);
+ if (dst == NULL) {
+ errno = ENOSPC;
+ goto out;
+ }
}
- free(nextra);
- *dst = '\0';
- return (int)(dst - start);
+
+ /* Terminate the string in the buffer. */
+ *dst = L'\0';
+
+ /*
+ * Output loop.
+ * Convert wchar_t string back to multibyte output string.
+ * If we have hit a multi-byte conversion error on input,
+ * output byte-by-byte here. Else use wctomb().
+ */
+ len = wcslen(start);
+ maxolen = dlen ? *dlen : (wcslen(start) * MB_LEN_MAX + 1);
+ olen = 0;
+ for (dst = start; len > 0; len--) {
+ if (!cerr)
+ clen = wctomb(mbdst, *dst);
+ if (cerr || clen < 0) {
+ /*
+ * Conversion error, process as a byte(s) instead.
+ * Examine each byte and higher-order bytes for
+ * data. E.g.,
+ * 0x000000000000a264 -> a2 64
+ * 0x000000001f00a264 -> 1f 00 a2 64
+ */
+ clen = 0;
+ wmsk = 0;
+ for (i = sizeof(wmsk) - 1; i >= 0; i--) {
+ shft = i * NBBY;
+ bmsk = (uint64_t)0xffLL << shft;
+ wmsk |= bmsk;
+ if ((*dst & wmsk) || i == 0)
+ mbdst[clen++] = (char)(
+ (uint64_t)(*dst & bmsk) >>
+ shft);
+ }
+ cerr = 1;
+ }
+ /* If this character would exceed our output limit, stop. */
+ if (olen + clen > (size_t)maxolen)
+ break;
+ /* Advance output pointer by number of bytes written. */
+ mbdst += clen;
+ /* Advance buffer character pointer. */
+ dst++;
+ /* Incrment output character count. */
+ olen += clen;
+ }
+
+ /* Terminate the output string. */
+ *mbdst = '\0';
+
+ if (flags & VIS_NOLOCALE) {
+ /* Pass conversion error flag out. */
+ if (cerr_ptr)
+ *cerr_ptr = cerr;
+ }
+
+ free(extra);
+ free(pdst);
+ free(psrc);
+
+ return (int)olen;
+out:
+ free(extra);
+ free(pdst);
+ free(psrc);
+ free(mdst);
+ return error;
+}
+
+static int
+istrsenvisxl(char **mbdstp, size_t *dlen, const char *mbsrc,
+ int flags, const char *mbextra, int *cerr_ptr)
+{
+ return istrsenvisx(mbdstp, dlen, mbsrc,
+ mbsrc != NULL ? strlen(mbsrc) : 0, flags, mbextra, cerr_ptr);
+}
+
+#endif
+
+#if !HAVE_SVIS
+/*
+ * The "svis" variants all take an "extra" arg that is a pointer
+ * to a NUL-terminated list of characters to be encoded, too.
+ * These functions are useful e. g. to encode strings in such a
+ * way so that they are not interpreted by a shell.
+ */
+
+char *
+svis(char *mbdst, int c, int flags, int nextc, const char *mbextra)
+{
+ char cc[2];
+ int ret;
+
+ cc[0] = c;
+ cc[1] = nextc;
+
+ ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, mbextra, NULL);
+ if (ret < 0)
+ return NULL;
+ return mbdst + ret;
+}
+
+char *
+snvis(char *mbdst, size_t dlen, int c, int flags, int nextc, const char *mbextra)
+{
+ char cc[2];
+ int ret;
+
+ cc[0] = c;
+ cc[1] = nextc;
+
+ ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, mbextra, NULL);
+ if (ret < 0)
+ return NULL;
+ return mbdst + ret;
+}
+
+int
+strsvis(char *mbdst, const char *mbsrc, int flags, const char *mbextra)
+{
+ return istrsenvisxl(&mbdst, NULL, mbsrc, flags, mbextra, NULL);
+}
+
+int
+strsnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags, const char *mbextra)
+{
+ return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, mbextra, NULL);
+}
+
+int
+strsvisx(char *mbdst, const char *mbsrc, size_t len, int flags, const char *mbextra)
+{
+ return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, mbextra, NULL);
+}
+
+int
+strsnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
+ const char *mbextra)
+{
+ return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, NULL);
+}
+
+int
+strsenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
+ const char *mbextra, int *cerr_ptr)
+{
+ return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, mbextra, cerr_ptr);
}
#endif
@@ -357,67 +640,89 @@ strsvisx(char *dst, const char *csrc, size_t len, int flag, const char *extra)
* vis - visually encode characters
*/
char *
-vis(char *dst, int c, int flag, int nextc)
+vis(char *mbdst, int c, int flags, int nextc)
{
- char *extra = NULL;
- unsigned char uc = (unsigned char)c;
- visfun_t f;
+ char cc[2];
+ int ret;
- _DIAGASSERT(dst != NULL);
+ cc[0] = c;
+ cc[1] = nextc;
- MAKEEXTRALIST(flag, extra, "");
- if (! extra) {
- *dst = '\0'; /* can't create extra, return "" */
- return dst;
- }
- f = getvisfun(flag);
- dst = (*f)(dst, uc, flag, nextc, extra);
- free(extra);
- *dst = '\0';
- return dst;
+ ret = istrsenvisx(&mbdst, NULL, cc, 1, flags, "", NULL);
+ if (ret < 0)
+ return NULL;
+ return mbdst + ret;
}
+char *
+nvis(char *mbdst, size_t dlen, int c, int flags, int nextc)
+{
+ char cc[2];
+ int ret;
+
+ cc[0] = c;
+ cc[1] = nextc;
+
+ ret = istrsenvisx(&mbdst, &dlen, cc, 1, flags, "", NULL);
+ if (ret < 0)
+ return NULL;
+ return mbdst + ret;
+}
/*
- * strvis, strvisx - visually encode characters from src into dst
+ * strvis - visually encode characters from src into dst
*
* Dst must be 4 times the size of src to account for possible
* expansion. The length of dst, not including the trailing NULL,
* is returned.
- *
- * Strvisx encodes exactly len bytes from src into dst.
- * This is useful for encoding a block of data.
*/
+
int
-strvis(char *dst, const char *src, int flag)
+strvis(char *mbdst, const char *mbsrc, int flags)
{
- char *extra = NULL;
- int rv;
+ return istrsenvisxl(&mbdst, NULL, mbsrc, flags, "", NULL);
+}
- MAKEEXTRALIST(flag, extra, "");
- if (!extra) {
- *dst = '\0'; /* can't create extra, return "" */
- return 0;
- }
- rv = strsvis(dst, src, flag, extra);
- free(extra);
- return rv;
+int
+strnvis(char *mbdst, size_t dlen, const char *mbsrc, int flags)
+{
+ return istrsenvisxl(&mbdst, &dlen, mbsrc, flags, "", NULL);
}
+int
+stravis(char **mbdstp, const char *mbsrc, int flags)
+{
+ *mbdstp = NULL;
+ return istrsenvisxl(mbdstp, NULL, mbsrc, flags, "", NULL);
+}
+
+/*
+ * strvisx - visually encode characters from src into dst
+ *
+ * Dst must be 4 times the size of src to account for possible
+ * expansion. The length of dst, not including the trailing NULL,
+ * is returned.
+ *
+ * Strvisx encodes exactly len characters from src into dst.
+ * This is useful for encoding a block of data.
+ */
int
-strvisx(char *dst, const char *src, size_t len, int flag)
+strvisx(char *mbdst, const char *mbsrc, size_t len, int flags)
{
- char *extra = NULL;
- int rv;
+ return istrsenvisx(&mbdst, NULL, mbsrc, len, flags, "", NULL);
+}
- MAKEEXTRALIST(flag, extra, "");
- if (!extra) {
- *dst = '\0'; /* can't create extra, return "" */
- return 0;
- }
- rv = strsvisx(dst, src, len, flag, extra);
- free(extra);
- return rv;
+int
+strnvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags)
+{
+ return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", NULL);
+}
+
+int
+strenvisx(char *mbdst, size_t dlen, const char *mbsrc, size_t len, int flags,
+ int *cerr_ptr)
+{
+ return istrsenvisx(&mbdst, &dlen, mbsrc, len, flags, "", cerr_ptr);
}
#endif
diff --git a/lib/libedit/src/vis.h b/lib/libedit/src/vis.h
index 23fc0d8e6d01..a1b23c920ec0 100644
--- a/lib/libedit/src/vis.h
+++ b/lib/libedit/src/vis.h
@@ -1,4 +1,4 @@
-/* $NetBSD: vis.h,v 1.18 2010/11/27 19:44:54 christos Exp $ */
+/* $NetBSD: vis.h,v 1.24 2016/01/14 20:42:14 christos Exp $ */
/*-
* Copyright (c) 1990, 1993
@@ -34,31 +34,38 @@
#ifndef _VIS_H_
#define _VIS_H_
+#include <sys/types.h>
+
/*
* to select alternate encoding format
*/
-#define VIS_OCTAL 0x001 /* use octal \ddd format */
-#define VIS_CSTYLE 0x002 /* use \[nrft0..] where appropiate */
+#define VIS_OCTAL 0x0001 /* use octal \ddd format */
+#define VIS_CSTYLE 0x0002 /* use \[nrft0..] where appropiate */
/*
* to alter set of characters encoded (default is to encode all
* non-graphic except space, tab, and newline).
*/
-#define VIS_SP 0x004 /* also encode space */
-#define VIS_TAB 0x008 /* also encode tab */
-#define VIS_NL 0x010 /* also encode newline */
+#define VIS_SP 0x0004 /* also encode space */
+#define VIS_TAB 0x0008 /* also encode tab */
+#define VIS_NL 0x0010 /* also encode newline */
#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
-#define VIS_SAFE 0x020 /* only encode "unsafe" characters */
+#define VIS_SAFE 0x0020 /* only encode "unsafe" characters */
/*
* other
*/
-#define VIS_NOSLASH 0x040 /* inhibit printing '\' */
-#define VIS_HTTP1808 0x080 /* http-style escape % hex hex */
-#define VIS_HTTPSTYLE 0x080 /* http-style escape % hex hex */
-#define VIS_MIMESTYLE 0x100 /* mime-style escape = HEX HEX */
-#define VIS_HTTP1866 0x200 /* http-style &#num; or &string; */
-#define VIS_NOESCAPE 0x400 /* don't decode `\' */
+#define VIS_NOSLASH 0x0040 /* inhibit printing '\' */
+#define VIS_HTTP1808 0x0080 /* http-style escape % hex hex */
+#define VIS_HTTPSTYLE 0x0080 /* http-style escape % hex hex */
+#define VIS_MIMESTYLE 0x0100 /* mime-style escape = HEX HEX */
+#define VIS_HTTP1866 0x0200 /* http-style &#num; or &string; */
+#define VIS_NOESCAPE 0x0400 /* don't decode `\' */
+#define _VIS_END 0x0800 /* for unvis */
+#define VIS_GLOB 0x1000 /* encode glob(3) magic characters */
+#define VIS_SHELL 0x2000 /* encode shell special characters [not glob] */
+#define VIS_META (VIS_WHITE | VIS_GLOB | VIS_SHELL)
+#define VIS_NOLOCALE 0x4000 /* encode using the C locale */
/*
* unvis return codes
@@ -72,20 +79,40 @@
/*
* unvis flags
*/
-#define UNVIS_END 1 /* no more characters */
+#define UNVIS_END _VIS_END /* no more characters */
+
+#include <sys/cdefs.h>
__BEGIN_DECLS
char *vis(char *, int, int, int);
+char *nvis(char *, size_t, int, int, int);
+
char *svis(char *, int, int, int, const char *);
+char *snvis(char *, size_t, int, int, int, const char *);
+
int strvis(char *, const char *, int);
+int stravis(char **, const char *, int);
+int strnvis(char *, size_t, const char *, int);
+
int strsvis(char *, const char *, int, const char *);
+int strsnvis(char *, size_t, const char *, int, const char *);
+
int strvisx(char *, const char *, size_t, int);
+int strnvisx(char *, size_t, const char *, size_t, int);
+int strenvisx(char *, size_t, const char *, size_t, int, int *);
+
int strsvisx(char *, const char *, size_t, int, const char *);
+int strsnvisx(char *, size_t, const char *, size_t, int, const char *);
+int strsenvisx(char *, size_t, const char *, size_t , int, const char *,
+ int *);
+
int strunvis(char *, const char *);
+int strnunvis(char *, size_t, const char *);
+
int strunvisx(char *, const char *, int);
-#ifndef __LIBC12_SOURCE__
+int strnunvisx(char *, size_t, const char *, int);
+
int unvis(char *, int, int *, int);
-#endif
__END_DECLS
#endif /* !_VIS_H_ */
diff --git a/lib/ntlm/Makefile.am b/lib/ntlm/Makefile.am
index 8b36bbc14e48..d33efd2b679a 100644
--- a/lib/ntlm/Makefile.am
+++ b/lib/ntlm/Makefile.am
@@ -2,11 +2,9 @@
include $(top_srcdir)/Makefile.am.common
-AM_CPPFLAGS += $(INCLUDE_hcrypto)
-
lib_LTLIBRARIES = libheimntlm.la
-dist_include_HEADERS = heimntlm.h heimntlm-protos.h
+dist_include_HEADERS = heimntlm.h $(srcdir)/heimntlm-protos.h
nodist_include_HEADERS = ntlm_err.h
@@ -23,10 +21,11 @@ $(libheimntlm_la_OBJECTS): $(srcdir)/version-script.map
libheimntlm_la_LIBADD = \
../krb5/libkrb5.la \
+ $(top_builddir)/lib/wind/libwind.la \
$(LIB_hcrypto) \
$(LIBADD_roken)
-$(srcdir)/heimntlm-protos.h:
+$(srcdir)/heimntlm-protos.h: $(dist_libheimntlm_la_SOURCES)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -o heimntlm-protos.h $(dist_libheimntlm_la_SOURCES) || rm -f heimntlm-protos.h
$(libheimntlm_la_OBJECTS): $(srcdir)/heimntlm-protos.h ntlm_err.h
diff --git a/lib/ntlm/Makefile.in b/lib/ntlm/Makefile.in
index f033c982259f..28cebe5ca621 100644
--- a/lib/ntlm/Makefile.in
+++ b/lib/ntlm/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -23,6 +22,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -41,9 +95,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(dist_include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
TESTS = test_ntlm$(EXEEXT)
check_PROGRAMS = test_ntlm$(EXEEXT)
@@ -62,8 +113,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -76,6 +126,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -95,6 +146,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -120,48 +173,284 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)" \
"$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
-libheimntlm_la_DEPENDENCIES = ../krb5/libkrb5.la $(am__DEPENDENCIES_1) \
+libheimntlm_la_DEPENDENCIES = ../krb5/libkrb5.la \
+ $(top_builddir)/lib/wind/libwind.la $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1)
dist_libheimntlm_la_OBJECTS = ntlm.lo
nodist_libheimntlm_la_OBJECTS = ntlm_err.lo
libheimntlm_la_OBJECTS = $(dist_libheimntlm_la_OBJECTS) \
$(nodist_libheimntlm_la_OBJECTS)
-libheimntlm_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
- $(libheimntlm_la_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libheimntlm_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libheimntlm_la_LDFLAGS) $(LDFLAGS) -o \
+ $@
test_ntlm_SOURCES = test_ntlm.c
test_ntlm_OBJECTS = test_ntlm.$(OBJEXT)
test_ntlm_LDADD = $(LDADD)
test_ntlm_DEPENDENCIES = libheimntlm.la $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(dist_libheimntlm_la_SOURCES) \
$(nodist_libheimntlm_la_SOURCES) test_ntlm.c
DIST_SOURCES = $(dist_libheimntlm_la_SOURCES) test_ntlm.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(dist_include_HEADERS) $(nodist_include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -180,12 +469,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -195,17 +484,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -224,12 +513,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -238,6 +524,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -254,10 +541,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -265,6 +550,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -279,12 +565,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -301,6 +590,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -315,13 +605,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -345,6 +629,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -380,37 +666,46 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
-AM_CPPFLAGS = $(INCLUDES_roken) $(INCLUDE_hcrypto)
+AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
lib_LTLIBRARIES = libheimntlm.la
-dist_include_HEADERS = heimntlm.h heimntlm-protos.h
+dist_include_HEADERS = heimntlm.h $(srcdir)/heimntlm-protos.h
nodist_include_HEADERS = ntlm_err.h
dist_libheimntlm_la_SOURCES = ntlm.c heimntlm.h
nodist_libheimntlm_la_SOURCES = ntlm_err.c
libheimntlm_la_LDFLAGS = -version-info 1:0:1 $(am__append_1)
libheimntlm_la_LIBADD = \
../krb5/libkrb5.la \
+ $(top_builddir)/lib/wind/libwind.la \
$(LIB_hcrypto) \
$(LIBADD_roken)
@@ -428,7 +723,7 @@ CLEANFILES = \
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -441,7 +736,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/ntlm/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/ntlm/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -450,6 +744,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -459,9 +754,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -469,6 +764,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -484,14 +781,17 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libheimntlm.la: $(libheimntlm_la_OBJECTS) $(libheimntlm_la_DEPENDENCIES)
- $(libheimntlm_la_LINK) -rpath $(libdir) $(libheimntlm_la_OBJECTS) $(libheimntlm_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libheimntlm.la: $(libheimntlm_la_OBJECTS) $(libheimntlm_la_DEPENDENCIES) $(EXTRA_libheimntlm_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libheimntlm_la_LINK) -rpath $(libdir) $(libheimntlm_la_OBJECTS) $(libheimntlm_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -501,9 +801,10 @@ clean-checkPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-test_ntlm$(EXEEXT): $(test_ntlm_OBJECTS) $(test_ntlm_DEPENDENCIES)
+
+test_ntlm$(EXEEXT): $(test_ntlm_OBJECTS) $(test_ntlm_DEPENDENCIES) $(EXTRA_test_ntlm_DEPENDENCIES)
@rm -f test_ntlm$(EXEEXT)
- $(LINK) $(test_ntlm_OBJECTS) $(test_ntlm_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_ntlm_OBJECTS) $(test_ntlm_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -516,25 +817,25 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_ntlm.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -543,8 +844,11 @@ clean-libtool:
-rm -rf .libs _libs
install-dist_includeHEADERS: $(dist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -558,13 +862,14 @@ uninstall-dist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_includeHEADERS: $(nodist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -578,30 +883,17 @@ uninstall-nodist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -613,15 +905,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -630,101 +918,187 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test_ntlm.log: test_ntlm$(EXEEXT)
+ @p='test_ntlm$(EXEEXT)'; \
+ b='test_ntlm'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -778,11 +1152,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -825,9 +1207,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -870,43 +1251,57 @@ uninstall-am: uninstall-dist_includeHEADERS uninstall-libLTLIBRARIES \
uninstall-nodist_includeHEADERS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-checkPROGRAMS clean-generic \
- clean-libLTLIBRARIES clean-libtool ctags dist-hook distclean \
- distclean-compile distclean-generic distclean-libtool \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am \
- install-data-hook install-dist_includeHEADERS install-dvi \
- install-dvi-am install-exec install-exec-am install-exec-hook \
- install-html install-html-am install-info install-info-am \
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool cscopelist-am ctags \
+ ctags-am dist-hook distclean distclean-compile \
+ distclean-generic distclean-libtool distclean-tags distdir dvi \
+ dvi-am html html-am info info-am install install-am \
+ install-data install-data-am install-data-hook \
+ install-dist_includeHEADERS install-dvi install-dvi-am \
+ install-exec install-exec-am install-exec-local install-html \
+ install-html-am install-info install-info-am \
install-libLTLIBRARIES install-man \
install-nodist_includeHEADERS install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-dist_includeHEADERS \
- uninstall-hook uninstall-libLTLIBRARIES \
- uninstall-nodist_includeHEADERS
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-dist_includeHEADERS uninstall-hook \
+ uninstall-libLTLIBRARIES uninstall-nodist_includeHEADERS
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-exec-hook: install-suid-programs
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -914,7 +1309,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -971,6 +1366,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1013,6 +1410,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1026,13 +1436,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -1064,7 +1474,7 @@ distdir-in-tree: $(DISTFILES) $(INFO_DEPS)
done
$(libheimntlm_la_OBJECTS): $(srcdir)/version-script.map
-$(srcdir)/heimntlm-protos.h:
+$(srcdir)/heimntlm-protos.h: $(dist_libheimntlm_la_SOURCES)
cd $(srcdir) && perl ../../cf/make-proto.pl -q -P comment -o heimntlm-protos.h $(dist_libheimntlm_la_SOURCES) || rm -f heimntlm-protos.h
$(libheimntlm_la_OBJECTS): $(srcdir)/heimntlm-protos.h ntlm_err.h
diff --git a/lib/ntlm/NTMakefile b/lib/ntlm/NTMakefile
index 40ed64449572..527fc891266d 100644
--- a/lib/ntlm/NTMakefile
+++ b/lib/ntlm/NTMakefile
@@ -75,7 +75,7 @@ test-binaries: $(OBJ)\test_ntlm.exe
test-run:
cd $(OBJ)
- test_ntlm.exe
+ -test_ntlm.exe
cd $(SRCDIR)
$(OBJ)\test_ntlm.exe: $(OBJ)\test_ntlm.obj $(LIBHEIMNTLM) $(LIBHEIMDAL) $(LIBVERS) $(LIBROKEN)
diff --git a/lib/ntlm/heimntlm-protos.h b/lib/ntlm/heimntlm-protos.h
index 5f56536ffc3b..b44dbc312527 100644
--- a/lib/ntlm/heimntlm-protos.h
+++ b/lib/ntlm/heimntlm-protos.h
@@ -1,6 +1,7 @@
/* This is a generated file */
#ifndef __heimntlm_protos_h__
#define __heimntlm_protos_h__
+#ifndef DOXY
#include <stdarg.h>
@@ -8,6 +9,20 @@
extern "C" {
#endif
+/**
+ * Generates an NTLMv1 session random with assosited session master key.
+ *
+ * @param key the ntlm v1 key
+ * @param len length of key
+ * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
+ * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_build_ntlm1_master (
void */*key*/,
@@ -15,6 +30,21 @@ heim_ntlm_build_ntlm1_master (
struct ntlm_buf */*session*/,
struct ntlm_buf */*master*/);
+/**
+ * Generates an NTLMv2 session random with associated session master key.
+ *
+ * @param key the NTLMv2 key
+ * @param len length of key
+ * @param blob the NTLMv2 "blob"
+ * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
+ * @param master calculated session master key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_build_ntlm2_master (
void */*key*/,
@@ -23,6 +53,23 @@ heim_ntlm_build_ntlm2_master (
struct ntlm_buf */*session*/,
struct ntlm_buf */*master*/);
+/**
+ * Calculate LMv2 response
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param serverchallenge challenge as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_calculate_lm2 (
const void */*key*/,
@@ -33,6 +80,20 @@ heim_ntlm_calculate_lm2 (
unsigned char ntlmv2[16],
struct ntlm_buf */*answer*/);
+/**
+ * Calculate NTLMv1 response hash
+ *
+ * @param key the ntlm v1 key
+ * @param len length of key
+ * @param challenge sent by the server
+ * @param answer calculated answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_calculate_ntlm1 (
void */*key*/,
@@ -40,6 +101,24 @@ heim_ntlm_calculate_ntlm1 (
unsigned char challenge[8],
struct ntlm_buf */*answer*/);
+/**
+ * Calculate NTLMv2 response
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param serverchallenge challenge as sent by the server in the type2 message.
+ * @param infotarget infotarget as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_calculate_ntlm2 (
const void */*key*/,
@@ -51,6 +130,10 @@ heim_ntlm_calculate_ntlm2 (
unsigned char ntlmv2[16],
struct ntlm_buf */*answer*/);
+/**
+ * Third check with empty domain.
+ */
+
int
heim_ntlm_calculate_ntlm2_sess (
const unsigned char clnt_nonce[8],
@@ -65,6 +148,19 @@ heim_ntlm_calculate_ntlm2_sess_hash (
const unsigned char svr_chal[8],
unsigned char verifier[8]);
+/**
+ * Decodes an NTLM targetinfo message
+ *
+ * @param data input data buffer with the encode NTLM targetinfo message
+ * @param ucs2 if the strings should be encoded with ucs2 (selected by flag in message).
+ * @param ti the decoded target info, should be freed with heim_ntlm_free_targetinfo().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_decode_targetinfo (
const struct ntlm_buf */*data*/,
@@ -95,42 +191,150 @@ heim_ntlm_derive_ntlm2_sess (
const unsigned char svr_chal[8],
unsigned char derivedkey[16]);
+/**
+ * Encodes a ntlm_targetinfo message.
+ *
+ * @param ti the ntlm_targetinfo message to encode.
+ * @param ucs2 ignored
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_encode_targetinfo (
const struct ntlm_targetinfo */*ti*/,
int /*ucs2*/,
struct ntlm_buf */*data*/);
+/**
+ * Encodes an ntlm_type1 message.
+ *
+ * @param type1 the ntlm_type1 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_encode_type1 (
const struct ntlm_type1 */*type1*/,
struct ntlm_buf */*data*/);
+/**
+ * Encodes an ntlm_type2 message.
+ *
+ * @param type2 the ntlm_type2 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_encode_type2 (
const struct ntlm_type2 */*type2*/,
struct ntlm_buf */*data*/);
+/**
+ * Encodes an ntlm_type3 message.
+ *
+ * @param type3 the ntlm_type3 message to encode.
+ * @param data is the return buffer with the encoded message, should be
+ * @param[out] mic_offset offset of message integrity code
+ * freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_encode_type3 (
const struct ntlm_type3 */*type3*/,
- struct ntlm_buf */*data*/);
+ struct ntlm_buf */*data*/,
+ size_t */*mic_offset*/);
+
+/**
+ * heim_ntlm_free_buf frees the ntlm buffer
+ *
+ * @param p buffer to be freed
+ *
+ * @ingroup ntlm_core
+ */
void
heim_ntlm_free_buf (struct ntlm_buf */*p*/);
+/**
+ * Frees the ntlm_targetinfo message
+ *
+ * @param ti targetinfo to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
void
heim_ntlm_free_targetinfo (struct ntlm_targetinfo */*ti*/);
+/**
+ * Frees the ntlm_type1 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
void
heim_ntlm_free_type1 (struct ntlm_type1 */*data*/);
+/**
+ * Frees the ntlm_type2 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
void
heim_ntlm_free_type2 (struct ntlm_type2 */*data*/);
+/**
+ * Frees the ntlm_type3 message
+ *
+ * @param data message to be freed
+ *
+ * @ingroup ntlm_core
+ */
+
void
heim_ntlm_free_type3 (struct ntlm_type3 */*data*/);
+/**
+ * Given a key and encrypted session, unwrap the session key
+ *
+ * @param baseKey the sessionBaseKey
+ * @param encryptedSession encrypted session, type3.session field.
+ * @param session generated session nonce, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_keyex_unwrap (
struct ntlm_buf */*baseKey*/,
@@ -143,19 +347,60 @@ heim_ntlm_keyex_wrap (
struct ntlm_buf */*session*/,
struct ntlm_buf */*encryptedSession*/);
+/**
+ * Calculate the NTLM key, the password is assumed to be in UTF8.
+ *
+ * @param password password to calcute the key for.
+ * @param key calcuted key, should be freed with heim_ntlm_free_buf().
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_nt_key (
const char */*password*/,
struct ntlm_buf */*key*/);
+/**
+ * Generates an NTLMv2 session key.
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param upper_case_target upper case the target, should not be used only for legacy systems
+ * @param ntlmv2 the ntlmv2 session key
+ *
+ * @return 0 on success, or an error code on failure.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_ntlmv2_key (
const void */*key*/,
size_t /*len*/,
const char */*username*/,
const char */*target*/,
+ int /*upper_case_target*/,
unsigned char ntlmv2[16]);
+time_t
+heim_ntlm_ts2unixtime (uint64_t /*t*/);
+
+uint64_t
+heim_ntlm_unix2ts_time (time_t /*unix_time*/);
+
+/**
+ @defgroup ntlm_core Heimdal NTLM library *
+ * The NTLM core functions implement the string2key generation
+ * function, message encode and decode function, and the hash function
+ * functions.
+ */
+
size_t
heim_ntlm_unparse_flags (
uint32_t /*flags*/,
@@ -175,6 +420,25 @@ heim_ntlm_v2_base_session (
struct ntlm_buf */*ntlmResponse*/,
struct ntlm_buf */*session*/);
+/**
+ * Verify NTLMv2 response.
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param now the time now (0 if the library should pick it up itself)
+ * @param serverchallenge challenge as sent by the server in the type2 message.
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ * @param infotarget infotarget as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
int
heim_ntlm_verify_ntlm2 (
const void */*key*/,
@@ -191,4 +455,5 @@ heim_ntlm_verify_ntlm2 (
}
#endif
+#endif /* DOXY */
#endif /* __heimntlm_protos_h__ */
diff --git a/lib/ntlm/heimntlm.h b/lib/ntlm/heimntlm.h
index 22e2142df88f..6b24649df849 100644
--- a/lib/ntlm/heimntlm.h
+++ b/lib/ntlm/heimntlm.h
@@ -70,8 +70,8 @@ struct ntlm_buf {
#define NTLM_TARGET_DOMAIN 0x00010000
#define NTLM_TARGET_SERVER 0x00020000
-#define NTLM_TARGET_SHARE 0x00040000
-#define NTLM_NEG_NTLM2_SESSION 0x00080000
+#define NTLM_TARGET_SHARE 0x00040000 /* mbz */
+#define NTLM_NEG_NTLM2_SESSION 0x00080000 /* EXTENDED_SESSIONSECURITY */
#define NTLM_NEG_NTLM2 0x00080000
#define NTLM_NEG_IDENTIFY 0x00100000
@@ -95,7 +95,9 @@ struct ntlm_buf {
* heim_ntlm_free_targetinfo().
*/
+/* avflags */
#define NTLM_TI_AV_FLAG_GUEST 0x00000001
+#define NTLM_TI_AV_FLAG_MIC 0x00000002
struct ntlm_targetinfo {
char *servername; /**< */
@@ -104,6 +106,9 @@ struct ntlm_targetinfo {
char *dnsservername; /**< */
char *dnstreename; /**< */
uint32_t avflags; /**< */
+ char *targetname;
+ struct ntlm_buf channel_bindings;
+ uint64_t timestamp;
};
/**
@@ -149,8 +154,12 @@ struct ntlm_type3 {
struct ntlm_buf sessionkey; /**< */
char *ws; /**< */
uint32_t os[2]; /**< */
+ size_t mic_offset;
+ uint8_t mic[16];
};
+extern time_t heim_ntlm_time_skew;
+
#include <ntlm_err.h>
#include <heimntlm-protos.h>
diff --git a/lib/ntlm/ntlm.c b/lib/ntlm/ntlm.c
index 7aafc8c0aa7a..7ec0181ff419 100644
--- a/lib/ntlm/ntlm.c
+++ b/lib/ntlm/ntlm.c
@@ -44,9 +44,12 @@
#include <limits.h>
#include <roken.h>
+
+#include <wind.h>
#include <parse_units.h>
#include <krb5.h>
+
#define HC_DEPRECATED_CRYPTO
#include "krb5-types.h"
@@ -103,6 +106,8 @@ struct sec_buffer {
static const unsigned char ntlmsigature[8] = "NTLMSSP\x00";
+time_t heim_ntlm_time_skew = 300;
+
/*
*
*/
@@ -116,6 +121,25 @@ static const unsigned char ntlmsigature[8] = "NTLMSSP\x00";
} \
} while(/*CONSTCOND*/0)
+#define CHECK_SIZE(f, e) \
+ do { \
+ ssize_t sret = f; \
+ if (sret != (ssize_t)(e)) { \
+ ret = HNTLM_ERR_DECODE; \
+ goto out; \
+ } \
+ } while(/*CONSTCOND*/0)
+
+#define CHECK_OFFSET(f, e) \
+ do { \
+ off_t sret = f; \
+ if (sret != (e)) { \
+ ret = HNTLM_ERR_DECODE; \
+ goto out; \
+ } \
+ } while(/*CONSTCOND*/0)
+
+
static struct units ntlm_flag_units[] = {
#define ntlm_flag(x) { #x, NTLM_##x }
ntlm_flag(ENC_56),
@@ -182,36 +206,68 @@ heim_ntlm_free_buf(struct ntlm_buf *p)
static int
ascii2ucs2le(const char *string, int up, struct ntlm_buf *buf)
{
- unsigned char *p;
- size_t len, i;
+ uint16_t *data;
+ size_t len, n;
+ uint8_t *p;
+ int ret;
- len = strlen(string);
- if (len / 2 > UINT_MAX)
+ ret = wind_utf8ucs2_length(string, &len);
+ if (ret)
+ return ret;
+ if (len > UINT_MAX / sizeof(data[0]))
return ERANGE;
+ data = malloc(len * sizeof(data[0]));
+ if (data == NULL)
+ return ENOMEM;
+
+ ret = wind_utf8ucs2(string, data, &len);
+ if (ret) {
+ free(data);
+ return ret;
+ }
+
+ if (len == 0) {
+ free(data);
+ buf->data = NULL;
+ buf->length = 0;
+ return 0;
+ }
+
+ /* uppercase string, only handle ascii right now */
+ if (up) {
+ for (n = 0; n < len ; n++) {
+ if (data[n] < 128)
+ data[n] = toupper((int)data[n]);
+ }
+ }
+
buf->length = len * 2;
- buf->data = malloc(buf->length);
+ p = buf->data = malloc(buf->length);
if (buf->data == NULL && len != 0) {
+ free(data);
heim_ntlm_free_buf(buf);
return ENOMEM;
}
- p = buf->data;
- for (i = 0; i < len; i++) {
- unsigned char t = (unsigned char)string[i];
- if (t & 0x80) {
- heim_ntlm_free_buf(buf);
- return EINVAL;
- }
- if (up)
- t = toupper(t);
- p[(i * 2) + 0] = t;
- p[(i * 2) + 1] = 0;
+ for (n = 0; n < len ; n++) {
+ p[(n * 2) + 0] = (data[n] ) & 0xff;
+ p[(n * 2) + 1] = (data[n] >> 8) & 0xff;
}
+ memset(data, 0, sizeof(data[0]) * len);
+ free(data);
+
return 0;
}
/*
+ * Sizes in bytes
+ */
+
+#define SIZE_SEC_BUFFER (2+2+4)
+#define SIZE_OS_VERSION (8)
+
+/*
*
*/
@@ -242,13 +298,20 @@ out:
* wire, but using utf8 in memory.
*/
-static krb5_error_code
+static size_t
len_string(int ucs2, const char *s)
{
- size_t len = strlen(s);
- if (ucs2)
- len *= 2;
- return len;
+ if (ucs2) {
+ size_t len;
+ int ret;
+
+ ret = wind_utf8ucs2_length(s, &len);
+ if (ret == 0)
+ return len * 2;
+ return strlen(s) * 5 * 2;
+ } else {
+ return strlen(s);
+ }
}
/*
@@ -259,28 +322,50 @@ static krb5_error_code
ret_string(krb5_storage *sp, int ucs2, size_t len, char **s)
{
krb5_error_code ret;
+ uint16_t *data = NULL;
*s = malloc(len + 1);
if (*s == NULL)
return ENOMEM;
- CHECK(krb5_storage_read(sp, *s, len), len);
+ CHECK_SIZE(krb5_storage_read(sp, *s, len), len);
(*s)[len] = '\0';
if (ucs2) {
- size_t i;
- for (i = 0; i < len / 2; i++) {
- (*s)[i] = (*s)[i * 2];
- if ((*s)[i * 2 + 1]) {
- free(*s);
- *s = NULL;
- return EINVAL;
- }
+ unsigned int flags = WIND_RW_LE;
+ size_t utf16len = len / 2;
+ size_t utf8len;
+
+ data = malloc(utf16len * sizeof(data[0]));
+ if (data == NULL) {
+ free(*s); *s = NULL;
+ ret = ENOMEM;
+ goto out;
+ }
+
+ ret = wind_ucs2read(*s, len, &flags, data, &utf16len);
+ free(*s); *s = NULL;
+ if (ret) {
+ goto out;
}
- (*s)[i] = '\0';
+
+ CHECK(wind_ucs2utf8_length(data, utf16len, &utf8len), 0);
+
+ utf8len += 1;
+
+ *s = malloc(utf8len);
+ if (s == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+
+ CHECK(wind_ucs2utf8(data, utf16len, *s, &utf8len), 0);
}
ret = 0;
out:
+ if (data)
+ free(data);
+
return ret;
}
@@ -290,10 +375,10 @@ static krb5_error_code
ret_sec_string(krb5_storage *sp, int ucs2, struct sec_buffer *desc, char **s)
{
krb5_error_code ret = 0;
- CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
+ CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
CHECK(ret_string(sp, ucs2, desc->length, s), 0);
out:
- return ret;
+ return ret;
}
static krb5_error_code
@@ -311,7 +396,7 @@ put_string(krb5_storage *sp, int ucs2, const char *s)
buf.length = strlen(s);
}
- CHECK(krb5_storage_write(sp, buf.data, buf.length), buf.length);
+ CHECK_SIZE(krb5_storage_write(sp, buf.data, buf.length), buf.length);
if (ucs2)
heim_ntlm_free_buf(&buf);
ret = 0;
@@ -330,8 +415,8 @@ ret_buf(krb5_storage *sp, struct sec_buffer *desc, struct ntlm_buf *buf)
buf->data = malloc(desc->length);
buf->length = desc->length;
- CHECK(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
- CHECK(krb5_storage_read(sp, buf->data, buf->length), buf->length);
+ CHECK_OFFSET(krb5_storage_seek(sp, desc->offset, SEEK_SET), desc->offset);
+ CHECK_SIZE(krb5_storage_read(sp, buf->data, buf->length), buf->length);
ret = 0;
out:
return ret;
@@ -341,7 +426,7 @@ static krb5_error_code
put_buf(krb5_storage *sp, const struct ntlm_buf *buf)
{
krb5_error_code ret;
- CHECK(krb5_storage_write(sp, buf->data, buf->length), buf->length);
+ CHECK_SIZE(krb5_storage_write(sp, buf->data, buf->length), buf->length);
ret = 0;
out:
return ret;
@@ -363,6 +448,8 @@ heim_ntlm_free_targetinfo(struct ntlm_targetinfo *ti)
free(ti->dnsdomainname);
free(ti->dnsservername);
free(ti->dnstreename);
+ free(ti->targetname);
+ heim_ntlm_free_buf(&ti->channel_bindings);
memset(ti, 0, sizeof(*ti));
}
@@ -423,6 +510,20 @@ heim_ntlm_encode_targetinfo(const struct ntlm_targetinfo *ti,
CHECK(krb5_store_uint16(out, 4), 0);
CHECK(krb5_store_uint32(out, ti->avflags), 0);
}
+ if (ti->timestamp) {
+ CHECK(krb5_store_uint16(out, 7), 0);
+ CHECK(krb5_store_uint16(out, 8), 0);
+ CHECK(krb5_store_uint32(out, ti->timestamp & 0xffffffff), 0);
+ CHECK(krb5_store_uint32(out, (ti->timestamp >> 32) & 0xffffffff), 0);
+ }
+ if (ti->targetname) {
+ CHECK(encode_ti_string(out, 9, ucs2, ti->targetname), 0);
+ }
+ if (ti->channel_bindings.length) {
+ CHECK(krb5_store_uint16(out, 10), 0);
+ CHECK(krb5_store_uint16(out, ti->channel_bindings.length), 0);
+ CHECK_SIZE(krb5_storage_write(out, ti->channel_bindings.data, ti->channel_bindings.length), ti->channel_bindings.length);
+ }
/* end tag */
CHECK(krb5_store_int16(out, 0), 0);
@@ -497,6 +598,26 @@ heim_ntlm_decode_targetinfo(const struct ntlm_buf *data,
case 6:
CHECK(krb5_ret_uint32(in, &ti->avflags), 0);
break;
+ case 7: {
+ uint32_t tmp;
+ CHECK(krb5_ret_uint32(in, &tmp), 0);
+ ti->timestamp = tmp;
+ CHECK(krb5_ret_uint32(in, &tmp), 0);
+ ti->timestamp |= ((uint64_t)tmp) << 32;
+ break;
+ }
+ case 9:
+ CHECK(ret_string(in, 1, len, &ti->targetname), 0);
+ break;
+ case 10:
+ ti->channel_bindings.data = malloc(len);
+ if (ti->channel_bindings.data == NULL) {
+ ret = ENOMEM;
+ goto out;
+ }
+ ti->channel_bindings.length = len;
+ CHECK_SIZE(krb5_storage_read(in, ti->channel_bindings.data, len), len);
+ break;
default:
krb5_storage_seek(in, len, SEEK_CUR);
break;
@@ -508,6 +629,21 @@ heim_ntlm_decode_targetinfo(const struct ntlm_buf *data,
return ret;
}
+static krb5_error_code
+encode_os_version(krb5_storage *out)
+{
+ krb5_error_code ret;
+ CHECK(krb5_store_uint8(out, 0x06), 0);
+ CHECK(krb5_store_uint8(out, 0x01), 0);
+ CHECK(krb5_store_uint16(out, 0x1db0), 0);
+ CHECK(krb5_store_uint8(out, 0x0f), 0); /* ntlm version 15 */
+ CHECK(krb5_store_uint8(out, 0x00), 0);
+ CHECK(krb5_store_uint8(out, 0x00), 0);
+ CHECK(krb5_store_uint8(out, 0x00), 0);
+ out:
+ return ret;
+}
+
/**
* Frees the ntlm_type1 message
*
@@ -534,6 +670,7 @@ heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data)
uint32_t type;
struct sec_buffer domain, hostname;
krb5_storage *in;
+ int ucs2;
memset(data, 0, sizeof(*data));
@@ -544,25 +681,30 @@ heim_ntlm_decode_type1(const struct ntlm_buf *buf, struct ntlm_type1 *data)
}
krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
- CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
CHECK(krb5_ret_uint32(in, &type), 0);
CHECK(type, 1);
CHECK(krb5_ret_uint32(in, &data->flags), 0);
- if (data->flags & NTLM_OEM_SUPPLIED_DOMAIN)
- CHECK(ret_sec_buffer(in, &domain), 0);
- if (data->flags & NTLM_OEM_SUPPLIED_WORKSTATION)
- CHECK(ret_sec_buffer(in, &hostname), 0);
-#if 0
- if (domain.offset > 32) {
+
+ ucs2 = !!(data->flags & NTLM_NEG_UNICODE);
+
+ /*
+ * domain and hostname are unconditionally encoded regardless of
+ * NTLMSSP_NEGOTIATE_OEM_{HOSTNAME,WORKSTATION}_SUPPLIED flag
+ */
+ CHECK(ret_sec_buffer(in, &domain), 0);
+ CHECK(ret_sec_buffer(in, &hostname), 0);
+
+ if (data->flags & NTLM_NEG_VERSION) {
CHECK(krb5_ret_uint32(in, &data->os[0]), 0);
CHECK(krb5_ret_uint32(in, &data->os[1]), 0);
}
-#endif
+
if (data->flags & NTLM_OEM_SUPPLIED_DOMAIN)
- CHECK(ret_sec_string(in, 0, &domain, &data->domain), 0);
+ CHECK(ret_sec_string(in, ucs2, &domain, &data->domain), 0);
if (data->flags & NTLM_OEM_SUPPLIED_WORKSTATION)
- CHECK(ret_sec_string(in, 0, &hostname, &data->hostname), 0);
+ CHECK(ret_sec_string(in, ucs2, &hostname, &data->hostname), 0);
out:
if (in)
@@ -593,35 +735,41 @@ heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data)
struct sec_buffer domain, hostname;
krb5_storage *out;
uint32_t base, flags;
+ int ucs2 = 0;
flags = type1->flags;
base = 16;
+ if (flags & NTLM_NEG_UNICODE)
+ ucs2 = 1;
+
if (type1->domain) {
- base += 8;
+ base += SIZE_SEC_BUFFER;
flags |= NTLM_OEM_SUPPLIED_DOMAIN;
}
if (type1->hostname) {
- base += 8;
+ base += SIZE_SEC_BUFFER;
flags |= NTLM_OEM_SUPPLIED_WORKSTATION;
}
- if (type1->os[0])
- base += 8;
+ if (flags & NTLM_NEG_VERSION)
+ base += SIZE_OS_VERSION; /* os */
- domain.offset = base;
if (type1->domain) {
- domain.length = len_string(0, type1->domain);
+ domain.offset = base;
+ domain.length = len_string(ucs2, type1->domain);
domain.allocated = domain.length;
} else {
+ domain.offset = 0;
domain.length = 0;
domain.allocated = 0;
}
- hostname.offset = domain.allocated + domain.offset;
if (type1->hostname) {
- hostname.length = len_string(0, type1->hostname);
+ hostname.offset = domain.allocated + domain.offset;
+ hostname.length = len_string(ucs2, type1->hostname);
hostname.allocated = hostname.length;
} else {
+ hostname.offset = 0;
hostname.length = 0;
hostname.allocated = 0;
}
@@ -631,21 +779,21 @@ heim_ntlm_encode_type1(const struct ntlm_type1 *type1, struct ntlm_buf *data)
return ENOMEM;
krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
- CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
sizeof(ntlmsigature));
CHECK(krb5_store_uint32(out, 1), 0);
CHECK(krb5_store_uint32(out, flags), 0);
CHECK(store_sec_buffer(out, &domain), 0);
CHECK(store_sec_buffer(out, &hostname), 0);
-#if 0
- CHECK(krb5_store_uint32(out, type1->os[0]), 0);
- CHECK(krb5_store_uint32(out, type1->os[1]), 0);
-#endif
+
+ if (flags & NTLM_NEG_VERSION) {
+ CHECK(encode_os_version(out), 0);
+ }
if (type1->domain)
- CHECK(put_string(out, 0, type1->domain), 0);
+ CHECK(put_string(out, ucs2, type1->domain), 0);
if (type1->hostname)
- CHECK(put_string(out, 0, type1->hostname), 0);
+ CHECK(put_string(out, ucs2, type1->hostname), 0);
{
krb5_data d;
@@ -695,7 +843,7 @@ heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2)
}
krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
- CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
CHECK(krb5_ret_uint32(in, &type), 0);
CHECK(type, 2);
@@ -704,7 +852,7 @@ heim_ntlm_decode_type2(const struct ntlm_buf *buf, struct ntlm_type2 *type2)
CHECK(krb5_ret_uint32(in, &type2->flags), 0);
if (type2->flags & NTLM_NEG_UNICODE)
ucs2 = 1;
- CHECK(krb5_storage_read(in, type2->challenge, sizeof(type2->challenge)),
+ CHECK_SIZE(krb5_storage_read(in, type2->challenge, sizeof(type2->challenge)),
sizeof(type2->challenge));
CHECK(krb5_ret_uint32(in, &ctx[0]), 0); /* context */
CHECK(krb5_ret_uint32(in, &ctx[1]), 0);
@@ -753,7 +901,7 @@ heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data)
base = 48;
if (type2->flags & NTLM_NEG_VERSION)
- base += 8;
+ base += SIZE_OS_VERSION;
if (type2->flags & NTLM_NEG_UNICODE)
ucs2 = 1;
@@ -771,23 +919,22 @@ heim_ntlm_encode_type2(const struct ntlm_type2 *type2, struct ntlm_buf *data)
return ENOMEM;
krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
- CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
sizeof(ntlmsigature));
CHECK(krb5_store_uint32(out, 2), 0);
CHECK(store_sec_buffer(out, &targetname), 0);
CHECK(krb5_store_uint32(out, type2->flags), 0);
- CHECK(krb5_storage_write(out, type2->challenge, sizeof(type2->challenge)),
+ CHECK_SIZE(krb5_storage_write(out, type2->challenge, sizeof(type2->challenge)),
sizeof(type2->challenge));
CHECK(krb5_store_uint32(out, 0), 0); /* context */
CHECK(krb5_store_uint32(out, 0), 0);
CHECK(store_sec_buffer(out, &targetinfo), 0);
/* os version */
if (type2->flags & NTLM_NEG_VERSION) {
- CHECK(krb5_store_uint32(out, type2->os[0]), 0);
- CHECK(krb5_store_uint32(out, type2->os[1]), 0);
+ CHECK(encode_os_version(out), 0);
}
CHECK(put_string(out, ucs2, type2->targetname), 0);
- CHECK(krb5_storage_write(out, type2->targetinfo.data,
+ CHECK_SIZE(krb5_storage_write(out, type2->targetinfo.data,
type2->targetinfo.length),
type2->targetinfo.length);
@@ -841,7 +988,7 @@ heim_ntlm_decode_type3(const struct ntlm_buf *buf,
uint32_t type;
krb5_storage *in;
struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
- uint32_t min_offset = 72;
+ uint32_t min_offset = 0xffffffff;
memset(type3, 0, sizeof(*type3));
memset(&sessionkey, 0, sizeof(sessionkey));
@@ -853,7 +1000,7 @@ heim_ntlm_decode_type3(const struct ntlm_buf *buf,
}
krb5_storage_set_byteorder(in, KRB5_STORAGE_BYTEORDER_LE);
- CHECK(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
+ CHECK_SIZE(krb5_storage_read(in, sig, sizeof(sig)), sizeof(sig));
CHECK(memcmp(ntlmsigature, sig, sizeof(ntlmsigature)), 0);
CHECK(krb5_ret_uint32(in, &type), 0);
CHECK(type, 3);
@@ -864,24 +1011,27 @@ heim_ntlm_decode_type3(const struct ntlm_buf *buf,
if (ntlm.allocated)
min_offset = min(min_offset, ntlm.offset);
CHECK(ret_sec_buffer(in, &target), 0);
- if (target.allocated)
- min_offset = min(min_offset, target.offset);
+ min_offset = min(min_offset, target.offset);
CHECK(ret_sec_buffer(in, &username), 0);
- if (username.allocated)
- min_offset = min(min_offset, username.offset);
+ min_offset = min(min_offset, username.offset);
CHECK(ret_sec_buffer(in, &ws), 0);
if (ws.allocated)
min_offset = min(min_offset, ws.offset);
- if (min_offset > 52) {
+ if (min_offset >= 52) {
CHECK(ret_sec_buffer(in, &sessionkey), 0);
- min_offset = max(min_offset, sessionkey.offset);
+ min_offset = min(min_offset, sessionkey.offset);
CHECK(krb5_ret_uint32(in, &type3->flags), 0);
}
- if (min_offset > 52 + 8 + 4 + 8) {
+ if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION) {
CHECK(krb5_ret_uint32(in, &type3->os[0]), 0);
CHECK(krb5_ret_uint32(in, &type3->os[1]), 0);
}
+ if (min_offset >= 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION + 16) {
+ type3->mic_offset = 52 + SIZE_SEC_BUFFER + 4 + SIZE_OS_VERSION;
+ CHECK_SIZE(krb5_storage_read(in, type3->mic, sizeof(type3->mic)), sizeof(type3->mic));
+ } else
+ type3->mic_offset = 0;
CHECK(ret_buf(in, &lm, &type3->lm), 0);
CHECK(ret_buf(in, &ntlm, &type3->ntlm), 0);
CHECK(ret_sec_string(in, ucs2, &target, &type3->targetname), 0);
@@ -904,6 +1054,7 @@ out:
*
* @param type3 the ntlm_type3 message to encode.
* @param data is the return buffer with the encoded message, should be
+ * @param[out] mic_offset offset of message integrity code
* freed with heim_ntlm_free_buf().
*
* @return In case of success 0 is return, an errors, a errno in what
@@ -913,7 +1064,7 @@ out:
*/
int
-heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data)
+heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data, size_t *mic_offset)
{
struct sec_buffer lm, ntlm, target, username, sessionkey, ws;
krb5_error_code ret;
@@ -932,9 +1083,12 @@ heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data)
base += 8; /* sessionkey sec buf */
base += 4; /* flags */
+ if (type3->flags & NTLM_NEG_VERSION)
+ base += SIZE_OS_VERSION; /* os flags */
- if (type3->os[0]) {
- base += 8;
+ if (mic_offset) {
+ *mic_offset = base;
+ base += 16;
}
if (type3->flags & NTLM_NEG_UNICODE)
@@ -969,7 +1123,7 @@ heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data)
return ENOMEM;
krb5_storage_set_byteorder(out, KRB5_STORAGE_BYTEORDER_LE);
- CHECK(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
+ CHECK_SIZE(krb5_storage_write(out, ntlmsigature, sizeof(ntlmsigature)),
sizeof(ntlmsigature));
CHECK(krb5_store_uint32(out, 3), 0);
@@ -981,10 +1135,15 @@ heim_ntlm_encode_type3(const struct ntlm_type3 *type3, struct ntlm_buf *data)
CHECK(store_sec_buffer(out, &sessionkey), 0);
CHECK(krb5_store_uint32(out, type3->flags), 0);
-#if 0
- CHECK(krb5_store_uint32(out, 0), 0); /* os0 */
- CHECK(krb5_store_uint32(out, 0), 0); /* os1 */
-#endif
+ /* os version */
+ if (type3->flags & NTLM_NEG_VERSION) {
+ CHECK(encode_os_version(out), 0);
+ }
+
+ if (mic_offset) {
+ static const uint8_t buf[16] = { 0 };
+ CHECK_SIZE(krb5_storage_write(out, buf, sizeof(buf)), sizeof(buf));
+ }
CHECK(put_string(out, ucs2, type3->targetname), 0);
CHECK(put_string(out, ucs2, type3->username), 0);
@@ -1055,10 +1214,10 @@ heim_ntlm_nt_key(const char *password, struct ntlm_buf *key)
EVP_MD_CTX *m;
int ret;
- key->data = malloc(MD5_DIGEST_LENGTH);
+ key->data = malloc(MD4_DIGEST_LENGTH);
if (key->data == NULL)
return ENOMEM;
- key->length = MD5_DIGEST_LENGTH;
+ key->length = MD4_DIGEST_LENGTH;
ret = ascii2ucs2le(password, 0, &buf);
if (ret) {
@@ -1133,7 +1292,7 @@ heim_ntlm_v1_base_session(void *key, size_t len,
session->length = 0;
return ENOMEM;
}
-
+
m = EVP_MD_CTX_create();
if (m == NULL) {
heim_ntlm_free_buf(session);
@@ -1182,6 +1341,9 @@ heim_ntlm_keyex_wrap(struct ntlm_buf *base_session,
EVP_CIPHER_CTX c;
int ret;
+ if (base_session->length != MD4_DIGEST_LENGTH)
+ return HNTLM_ERR_INVALID_LENGTH;
+
session->length = MD4_DIGEST_LENGTH;
session->data = malloc(session->length);
if (session->data == NULL) {
@@ -1222,8 +1384,6 @@ heim_ntlm_keyex_wrap(struct ntlm_buf *base_session,
}
-
-
/**
* Generates an NTLMv1 session random with assosited session master key.
*
@@ -1313,6 +1473,8 @@ heim_ntlm_keyex_unwrap(struct ntlm_buf *baseKey,
memset(session, 0, sizeof(*session));
+ if (encryptedSession->length != MD4_DIGEST_LENGTH)
+ return HNTLM_ERR_INVALID_LENGTH;
if (baseKey->length != MD4_DIGEST_LENGTH)
return HNTLM_ERR_INVALID_LENGTH;
@@ -1344,6 +1506,7 @@ heim_ntlm_keyex_unwrap(struct ntlm_buf *baseKey,
* @param len length of key
* @param username name of the user, as sent in the message, assumed to be in UTF8.
* @param target the name of the target, assumed to be in UTF8.
+ * @param upper_case_target upper case the target, should not be used only for legacy systems
* @param ntlmv2 the ntlmv2 session key
*
* @return 0 on success, or an error code on failure.
@@ -1355,6 +1518,7 @@ int
heim_ntlm_ntlmv2_key(const void *key, size_t len,
const char *username,
const char *target,
+ int upper_case_target,
unsigned char ntlmv2[16])
{
int ret;
@@ -1371,8 +1535,8 @@ heim_ntlm_ntlmv2_key(const void *key, size_t len,
goto out;
HMAC_Update(&c, buf.data, buf.length);
free(buf.data);
- /* uppercase target and turn into ucs2-le */
- ret = ascii2ucs2le(target, 1, &buf);
+ /* turn target into ucs2-le */
+ ret = ascii2ucs2le(target, upper_case_target, &buf);
if (ret)
goto out;
HMAC_Update(&c, buf.data, buf.length);
@@ -1381,6 +1545,7 @@ heim_ntlm_ntlmv2_key(const void *key, size_t len,
HMAC_Final(&c, ntlmv2, &hmaclen);
out:
HMAC_CTX_cleanup(&c);
+ memset(&c, 0, sizeof(c));
return ret;
}
@@ -1391,16 +1556,16 @@ heim_ntlm_ntlmv2_key(const void *key, size_t len,
#define NTTIME_EPOCH 0x019DB1DED53E8000LL
-static uint64_t
-unix2nttime(time_t unix_time)
+uint64_t
+heim_ntlm_unix2ts_time(time_t unix_time)
{
long long wt;
wt = unix_time * (uint64_t)10000000 + (uint64_t)NTTIME_EPOCH;
return wt;
}
-static time_t
-nt2unixtime(uint64_t t)
+time_t
+heim_ntlm_ts2unixtime(uint64_t t)
{
t = ((t - (uint64_t)NTTIME_EPOCH) / (uint64_t)10000000);
if (t > (((uint64_t)(time_t)(~(uint64_t)0)) >> 1))
@@ -1440,7 +1605,7 @@ heim_ntlm_calculate_lm2(const void *key, size_t len,
/* calculate ntlmv2 key */
- heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
+ heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2);
answer->data = malloc(24);
if (answer->data == NULL)
@@ -1450,7 +1615,7 @@ heim_ntlm_calculate_lm2(const void *key, size_t len,
heim_ntlm_derive_ntlm2_sess(ntlmv2, clientchallenge, 8,
serverchallenge, answer->data);
- memcpy(((uint8_t *)answer->data) + 16, clientchallenge, 8);
+ memcpy(((unsigned char *)answer->data) + 16, clientchallenge, 8);
return 0;
}
@@ -1490,14 +1655,14 @@ heim_ntlm_calculate_ntlm2(const void *key, size_t len,
unsigned char clientchallenge[8];
uint64_t t;
- t = unix2nttime(time(NULL));
+ t = heim_ntlm_unix2ts_time(time(NULL));
if (RAND_bytes(clientchallenge, sizeof(clientchallenge)) != 1)
return HNTLM_ERR_RAND;
/* calculate ntlmv2 key */
- heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
+ heim_ntlm_ntlmv2_key(key, len, username, target, 0, ntlmv2);
/* calculate and build ntlmv2 answer */
@@ -1512,12 +1677,19 @@ heim_ntlm_calculate_ntlm2(const void *key, size_t len,
CHECK(krb5_store_uint32(sp, t & 0xffffffff), 0);
CHECK(krb5_store_uint32(sp, t >> 32), 0);
- CHECK(krb5_storage_write(sp, clientchallenge, 8), 8);
+ CHECK_SIZE(krb5_storage_write(sp, clientchallenge, 8), 8);
- CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */
- CHECK(krb5_storage_write(sp, infotarget->data, infotarget->length),
+ CHECK(krb5_store_uint32(sp, 0), 0); /* Z(4) */
+ CHECK_SIZE(krb5_storage_write(sp, infotarget->data, infotarget->length),
infotarget->length);
- CHECK(krb5_store_uint32(sp, 0), 0); /* unknown but zero will work */
+
+ /*
+ * These last 4 bytes(Z(4)) are not documented by MicroSoft and
+ * SnowLeopard doesn't send them, Lion expected them to be there,
+ * so we have to continue to send them. That is ok, since everyone
+ * else (except Snow) seems to do that too.
+ */
+ CHECK(krb5_store_uint32(sp, 0), 0); /* Z(4) */
CHECK(krb5_storage_to_data(sp, &data), 0);
krb5_storage_free(sp);
@@ -1531,8 +1703,8 @@ heim_ntlm_calculate_ntlm2(const void *key, size_t len,
return ENOMEM;
}
- CHECK(krb5_storage_write(sp, ntlmv2answer, 16), 16);
- CHECK(krb5_storage_write(sp, data.data, data.length), data.length);
+ CHECK_SIZE(krb5_storage_write(sp, ntlmv2answer, 16), 16);
+ CHECK_SIZE(krb5_storage_write(sp, data.data, data.length), data.length);
krb5_data_free(&data);
CHECK(krb5_storage_to_data(sp, &data), 0);
@@ -1551,43 +1723,25 @@ out:
static const int authtimediff = 3600 * 2; /* 2 hours */
-/**
- * Verify NTLMv2 response.
- *
- * @param key the ntlm key
- * @param len length of key
- * @param username name of the user, as sent in the message, assumed to be in UTF8.
- * @param target the name of the target, assumed to be in UTF8.
- * @param now the time now (0 if the library should pick it up itself)
- * @param serverchallenge challenge as sent by the server in the type2 message.
- * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
- * @param infotarget infotarget as sent by the server in the type2 message.
- * @param ntlmv2 calculated session key
- *
- * @return In case of success 0 is return, an errors, a errno in what
- * went wrong.
- *
- * @ingroup ntlm_core
- */
-
-int
-heim_ntlm_verify_ntlm2(const void *key, size_t len,
- const char *username,
- const char *target,
- time_t now,
- const unsigned char serverchallenge[8],
- const struct ntlm_buf *answer,
- struct ntlm_buf *infotarget,
- unsigned char ntlmv2[16])
+static int
+verify_ntlm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ int upper_case_target,
+ time_t now,
+ const unsigned char serverchallenge[8],
+ const struct ntlm_buf *answer,
+ struct ntlm_buf *infotarget,
+ unsigned char ntlmv2[16])
{
krb5_error_code ret;
unsigned char clientanswer[16];
unsigned char clientnonce[8];
unsigned char serveranswer[16];
krb5_storage *sp;
+ uint64_t t;
time_t authtime;
uint32_t temp;
- uint64_t t;
infotarget->length = 0;
infotarget->data = NULL;
@@ -1600,7 +1754,7 @@ heim_ntlm_verify_ntlm2(const void *key, size_t len,
/* calculate ntlmv2 key */
- heim_ntlm_ntlmv2_key(key, len, username, target, ntlmv2);
+ heim_ntlm_ntlmv2_key(key, len, username, target, upper_case_target, ntlmv2);
/* calculate and build ntlmv2 answer */
@@ -1609,7 +1763,7 @@ heim_ntlm_verify_ntlm2(const void *key, size_t len,
return ENOMEM;
krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE);
- CHECK(krb5_storage_read(sp, clientanswer, 16), 16);
+ CHECK_SIZE(krb5_storage_read(sp, clientanswer, 16), 16);
CHECK(krb5_ret_uint32(sp, &temp), 0);
CHECK(temp, 0x00000101);
@@ -1621,28 +1775,32 @@ heim_ntlm_verify_ntlm2(const void *key, size_t len,
CHECK(krb5_ret_uint32(sp, &temp), 0);
t |= ((uint64_t)temp)<< 32;
- authtime = nt2unixtime(t);
+ authtime = heim_ntlm_ts2unixtime(t);
- if (abs((int)(authtime - now)) > authtimediff) {
+ if (labs((int)(authtime - now)) > authtimediff) {
ret = HNTLM_ERR_TIME_SKEW;
goto out;
}
/* client challenge */
- CHECK(krb5_storage_read(sp, clientnonce, 8), 8);
+ CHECK_SIZE(krb5_storage_read(sp, clientnonce, 8), 8);
- CHECK(krb5_ret_uint32(sp, &temp), 0); /* unknown */
+ CHECK(krb5_ret_uint32(sp, &temp), 0); /* Z(4) */
- /* should really unparse the infotarget, but lets pick up everything */
- infotarget->length = answer->length - krb5_storage_seek(sp, 0, SEEK_CUR);
+ /* let pick up targetinfo */
+ infotarget->length = answer->length - (size_t)krb5_storage_seek(sp, 0, SEEK_CUR);
+ if (infotarget->length < 4) {
+ ret = HNTLM_ERR_INVALID_LENGTH;
+ goto out;
+ }
infotarget->data = malloc(infotarget->length);
if (infotarget->data == NULL) {
ret = ENOMEM;
goto out;
}
- CHECK(krb5_storage_read(sp, infotarget->data, infotarget->length),
+ CHECK_SIZE(krb5_storage_read(sp, infotarget->data, infotarget->length),
infotarget->length);
- /* XXX remove the unknown ?? */
+
krb5_storage_free(sp);
sp = NULL;
@@ -1669,6 +1827,60 @@ out:
return ret;
}
+/**
+ * Verify NTLMv2 response.
+ *
+ * @param key the ntlm key
+ * @param len length of key
+ * @param username name of the user, as sent in the message, assumed to be in UTF8.
+ * @param target the name of the target, assumed to be in UTF8.
+ * @param now the time now (0 if the library should pick it up itself)
+ * @param serverchallenge challenge as sent by the server in the type2 message.
+ * @param answer ntlm response answer, should be freed with heim_ntlm_free_buf().
+ * @param infotarget infotarget as sent by the server in the type2 message.
+ * @param ntlmv2 calculated session key
+ *
+ * @return In case of success 0 is return, an errors, a errno in what
+ * went wrong.
+ *
+ * @ingroup ntlm_core
+ */
+
+int
+heim_ntlm_verify_ntlm2(const void *key, size_t len,
+ const char *username,
+ const char *target,
+ time_t now,
+ const unsigned char serverchallenge[8],
+ const struct ntlm_buf *answer,
+ struct ntlm_buf *infotarget,
+ unsigned char ntlmv2[16])
+{
+ int ret;
+
+ /**
+ * First check with the domain as the client passed it to the function.
+ */
+
+ ret = verify_ntlm2(key, len, username, target, 0, now,
+ serverchallenge, answer, infotarget, ntlmv2);
+
+ /**
+ * Second check with domain uppercased.
+ */
+
+ if (ret)
+ ret = verify_ntlm2(key, len, username, target, 1, now,
+ serverchallenge, answer, infotarget, ntlmv2);
+
+ /**
+ * Third check with empty domain.
+ */
+ if (ret)
+ ret = verify_ntlm2(key, len, username, "", 0, now,
+ serverchallenge, answer, infotarget, ntlmv2);
+ return ret;
+}
/*
* Calculate the NTLM2 Session Response
@@ -1799,5 +2011,5 @@ heim_ntlm_derive_ntlm2_sess(const unsigned char sessionkey[16],
HMAC_Update(&c, clnt_nonce, clnt_nonce_length);
HMAC_Final(&c, derivedkey, &hmaclen);
HMAC_CTX_cleanup(&c);
+ memset(&c, 0, sizeof(c));
}
-
diff --git a/lib/ntlm/ntlm_err.et b/lib/ntlm/ntlm_err.et
index 0fd6e00a21c7..fd39aa31fb2d 100644
--- a/lib/ntlm/ntlm_err.et
+++ b/lib/ntlm/ntlm_err.et
@@ -14,11 +14,47 @@ error_code RAND, "Random generator failed"
error_code AUTH, "NTLM authentication failed"
error_code TIME_SKEW, "Client time skewed to server"
error_code OEM, "Client set OEM string"
-error_code MISSING_NAME_SEPARATOR, "missing @ or \ in name"
+error_code MISSING_NAME_SEPARATOR, "missing @ or \\\\ in name"
error_code MISSING_BUFFER, "missing expected buffer"
error_code INVALID_APOP, "Invalid APOP response"
error_code INVALID_CRAM_MD5, "Invalid CRAM-MD5 response"
error_code INVALID_DIGEST_MD5, "Invalid DIGEST-MD5 response"
error_code INVALID_DIGEST_MD5_RSPAUTH, "Invalid DIGEST-MD5 rspauth"
+error_code INVALID_CHANNEL_BINDINGS, "Invalid channel bindings"
+error_code INVALID_MIC, "Invalid MIC"
+error_code INVALID_SESSIONKEY, "Invalid session key"
+
+
+#
+# NTLM/GSS error codes
+#
+index 64
+error_code NOT_CONFIGURED, "NTLM not configured"
+
+error_code INVALID_CHALLANGE, "Invalid client challenge"
+error_code INVALID_LMv1_RESPONSE, "Invalid client LMv1 response"
+error_code INVALID_NT_RESPONSE, "Invalid client NT response"
+error_code INVALID_LMv2_RESPONSE, "Invalid client LMv2 response"
+error_code INVALID_NTv1_RESPONSE, "Invalid client NTv1 response"
+error_code INVALID_NTv2_RESPONSE, "Invalid client NTv2 response"
+error_code INVALID_NTv1_ANSWER, "Invalid client NTv1 answer"
+error_code INVALID_NTv2_ANSWER, "Invalid client NTv2 answer"
+error_code INVALID_SESSION_KEY, "Invalid session key"
+
+error_code INVALID_NO_GUEST, "Invalid guest login request"
+
+error_code NO_NETR_CONFIGURED, "No NETR configured"
+
+
+#
+# Scram errors
+#
+prefix HSCRAM_ERR
+index 128
+
+error_code INVALID_MESSAGE, "Invalid SCRAM message"
+error_code INVALID_PROOF, "Invalid SCRAM proof"
+error_code INVALID_ROLE, "Invalid SCRAM role"
+
end
diff --git a/lib/ntlm/test_ntlm.c b/lib/ntlm/test_ntlm.c
index 5a995adae48b..e7d723727e58 100644
--- a/lib/ntlm/test_ntlm.c
+++ b/lib/ntlm/test_ntlm.c
@@ -41,11 +41,14 @@
#include <krb5-types.h> /* or <inttypes.h> */
#include <heimntlm.h>
+static int dumpdata_flag;
+
static int
test_parse(void)
{
const char *user = "foo",
*domain = "mydomain",
+ *hostname = "myhostname",
*password = "digestpassword",
*target = "DOMAIN";
struct ntlm_type1 type1;
@@ -56,9 +59,9 @@ test_parse(void)
memset(&type1, 0, sizeof(type1));
- type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_TARGET|NTLM_NEG_NTLM;
+ type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_TARGET|NTLM_NEG_NTLM|NTLM_NEG_VERSION;
type1.domain = rk_UNCONST(domain);
- type1.hostname = NULL;
+ type1.hostname = rk_UNCONST(hostname);
type1.os[0] = 0;
type1.os[1] = 0;
@@ -68,11 +71,20 @@ test_parse(void)
memset(&type1, 0, sizeof(type1));
+ if (dumpdata_flag)
+ rk_dumpdata("ntlm-type1", data.data, data.length);
+
ret = heim_ntlm_decode_type1(&data, &type1);
free(data.data);
if (ret)
errx(1, "heim_ntlm_encode_type1");
+ if (strcmp(type1.domain, domain) != 0)
+ errx(1, "parser got domain wrong: %s", type1.domain);
+
+ if (strcmp(type1.hostname, hostname) != 0)
+ errx(1, "parser got hostname wrong: %s", type1.hostname);
+
heim_ntlm_free_type1(&type1);
/*
@@ -95,6 +107,9 @@ test_parse(void)
memset(&type2, 0, sizeof(type2));
+ if (dumpdata_flag)
+ rk_dumpdata("ntlm-type2", data.data, data.length);
+
ret = heim_ntlm_decode_type2(&data, &type2);
free(data.data);
if (ret)
@@ -123,7 +138,7 @@ test_parse(void)
free(key.data);
}
- ret = heim_ntlm_encode_type3(&type3, &data);
+ ret = heim_ntlm_encode_type3(&type3, &data, NULL);
if (ret)
errx(1, "heim_ntlm_encode_type3");
@@ -131,6 +146,9 @@ test_parse(void)
memset(&type3, 0, sizeof(type3));
+ if (dumpdata_flag)
+ rk_dumpdata("ntlm-type3", data.data, data.length);
+
ret = heim_ntlm_decode_type3(&data, 1, &type3);
free(data.data);
if (ret)
@@ -289,17 +307,106 @@ test_ntlm2_session_resp(void)
}
static int
+test_ntlmv2(void)
+{
+ unsigned char type3[413] =
+ "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00"
+ "\x80\x00\x00\x00\x9e\x00\x9e\x00\x98\x00\x00\x00\x14\x00\x14\x00"
+ "\x48\x00\x00\x00\x10\x00\x10\x00\x5c\x00\x00\x00\x14\x00\x14\x00"
+ "\x6c\x00\x00\x00\x00\x00\x00\x00\x36\x01\x00\x00\x05\x82\x88\xa2"
+ "\x05\x01\x28\x0a\x00\x00\x00\x0f\x43\x00\x4f\x00\x4c\x00\x4c\x00"
+ "\x45\x00\x59\x00\x2d\x00\x58\x00\x50\x00\x34\x00\x54\x00\x45\x00"
+ "\x53\x00\x54\x00\x55\x00\x53\x00\x45\x00\x52\x00\x43\x00\x4f\x00"
+ "\x4c\x00\x4c\x00\x45\x00\x59\x00\x2d\x00\x58\x00\x50\x00\x34\x00"
+ "\x2f\x96\xec\x0a\xf7\x9f\x2e\x24\xba\x09\x48\x10\xa5\x22\xd4\xe1"
+ "\x16\x6a\xca\x58\x74\x9a\xc1\x4f\x54\x6f\xee\x40\x96\xce\x43\x6e"
+ "\xdf\x99\x20\x71\x6c\x9a\xda\x2a\x01\x01\x00\x00\x00\x00\x00\x00"
+ "\x8d\xc0\x57\xc9\x79\x5e\xcb\x01\x16\x6a\xca\x58\x74\x9a\xc1\x4f"
+ "\x00\x00\x00\x00\x02\x00\x14\x00\x4e\x00\x55\x00\x54\x00\x43\x00"
+ "\x52\x00\x41\x00\x43\x00\x4b\x00\x45\x00\x52\x00\x01\x00\x14\x00"
+ "\x4e\x00\x55\x00\x54\x00\x43\x00\x52\x00\x41\x00\x43\x00\x4b\x00"
+ "\x45\x00\x52\x00\x04\x00\x12\x00\x61\x00\x70\x00\x70\x00\x6c\x00"
+ "\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x03\x00\x20\x00\x68\x00"
+ "\x75\x00\x6d\x00\x6d\x00\x65\x00\x6c\x00\x2e\x00\x61\x00\x70\x00"
+ "\x70\x00\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64\x00\x6f"
+ "\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32\x00\x20"
+ "\x00\x53\x00\x65\x00\x72\x00\x76\x00\x69\x00\x63\x00\x65\x00\x20"
+ "\x00\x50\x00\x61\x00\x63\x00\x6b\x00\x20\x00\x33\x00\x20\x00\x32"
+ "\x00\x36\x00\x30\x00\x30\x00\x00\x00\x57\x00\x69\x00\x6e\x00\x64"
+ "\x00\x6f\x00\x77\x00\x73\x00\x20\x00\x32\x00\x30\x00\x30\x00\x32"
+ "\x00\x20\x00\x35\x00\x2e\x00\x31\x00\x00\x00\x00\x00";
+ const unsigned char challenge[8] =
+ "\xe4\x9c\x6a\x12\xe1\xbd\xde\x6a";
+ unsigned char sessionkey[16];
+
+ const char key[16] = "\xD1\x83\x98\x3E\xAE\xA7\xBE\x99\x59\xC8\xF4\xC1\x98\xED\x0E\x68";
+
+ struct ntlm_buf data;
+ struct ntlm_type3 t3;
+ int ret;
+
+ struct ntlm_targetinfo ti;
+
+ unsigned char timsg[114] =
+ "\002\000\024\000N\000U\000T\000C\000R\000A\000C\000K\000E\000R\000\001\000\024\000N\000U\000T\000C\000R\000A\000C\000K\000E\000R\000\004\000\022\000a\000p\000p\000l\000e\000.\000c\000o\000m\000\003\000 \000h\000u\000m\000m\000e\000l\000.\000a\000p\000p\000l\000e\000.\000c\000o\000m\000\000\000\000\000\000\000\000";
+
+
+ data.data = type3;
+ data.length = sizeof(type3);
+
+ ret = heim_ntlm_decode_type3(&data, 1, &t3);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type3");
+
+ memset(&ti, 0, sizeof(ti));
+
+ data.data = timsg;
+ data.length = sizeof(timsg);
+
+ ret = heim_ntlm_decode_targetinfo(&data, 1, &ti);
+ if (ret)
+ return ret;
+
+ ret = heim_ntlm_verify_ntlm2(key, sizeof(key),
+ t3.username,
+ t3.targetname,
+ 1285615547,
+ challenge,
+ &t3.ntlm,
+ &data,
+ sessionkey);
+ if (ret)
+ errx(1, "verify_ntlmv2");
+
+ if (sizeof(timsg) != data.length || memcmp(timsg, data.data, sizeof(timsg)) != 0)
+ errx(1, "target info wrong: %d != %d",
+ (int)sizeof(timsg), (int)data.length);
+
+ heim_ntlm_free_type3(&t3);
+ heim_ntlm_free_targetinfo(&ti);
+
+ return 0;
+}
+
+static int
test_targetinfo(void)
{
struct ntlm_targetinfo ti;
struct ntlm_buf buf;
const char *dnsservername = "dnsservername";
+ const char *targetname = "targetname";
+ const char z16[16] = { 0 };
int ret;
memset(&ti, 0, sizeof(ti));
ti.dnsservername = rk_UNCONST(dnsservername);
ti.avflags = 1;
+ ti.targetname = rk_UNCONST(targetname);
+ ti.channel_bindings.data = rk_UNCONST(z16);
+ ti.channel_bindings.length = sizeof(z16);
+
ret = heim_ntlm_encode_targetinfo(&ti, 1, &buf);
if (ret)
return ret;
@@ -315,12 +422,133 @@ test_targetinfo(void)
errx(1, "ti.dnshostname != %s", dnsservername);
if (ti.avflags != 1)
errx(1, "ti.avflags != 1");
+ if (ti.targetname == NULL ||
+ strcmp(ti.targetname, targetname) != 0)
+ errx(1, "ti.targetname != %s", targetname);
+
+ if (ti.channel_bindings.length != sizeof(z16) ||
+ memcmp(ti.channel_bindings.data, z16, sizeof(z16)) != 0)
+ errx(1, "ti.channel_bindings != Z(16)");
heim_ntlm_free_targetinfo(&ti);
return 0;
}
+static int
+test_string2key(void)
+{
+ const char *pw = "山田";
+ struct ntlm_buf buf;
+
+ unsigned char key[16] = {
+ 0xc6, 0x5d, 0xc7, 0x61, 0xa1, 0x34, 0x17, 0xa1,
+ 0x17, 0x08, 0x9c, 0x1b, 0xb0, 0x0d, 0x0f, 0x19
+ };
+
+ if (heim_ntlm_nt_key(pw, &buf) != 0)
+ errx(1, "heim_ntlmv_nt_key(jp)");
+
+ if (buf.length != 16 || memcmp(buf.data, key, 16) != 0)
+ errx(1, "compare failed");
+
+ heim_ntlm_free_buf(&buf);
+
+ return 0;
+}
+
+static int
+test_jp(void)
+{
+ char buf2[220] =
+ "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00\x06\x00\x06\x00"
+ "\x38\x00\x00\x00\x05\x02\x89\x62\x62\x94\xb1\xf3\x56\x80\xb0\xf9"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x9e\x00\x9e\x00\x3e\x00\x00\x00"
+ "\x06\x01\xb0\x1d\x00\x00\x00\x0f\x43\x00\x4f\x00\x53\x00\x02\x00"
+ "\x06\x00\x43\x00\x4f\x00\x53\x00\x01\x00\x12\x00\x43\x00\x4f\x00"
+ "\x53\x00\x57\x00\x49\x00\x4e\x00\x37\x00\x4a\x00\x50\x00\x04\x00"
+ "\x1a\x00\x63\x00\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00"
+ "\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x03\x00\x2e\x00"
+ "\x63\x00\x6f\x00\x73\x00\x77\x00\x69\x00\x6e\x00\x37\x00\x6a\x00"
+ "\x70\x00\x2e\x00\x63\x00\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00"
+ "\x70\x00\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x05\x00"
+ "\x1a\x00\x63\x00\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00"
+ "\x6c\x00\x65\x00\x2e\x00\x63\x00\x6f\x00\x6d\x00\x07\x00\x08\x00"
+ "\x94\x51\xf0\xbd\xdc\x61\xcb\x01\x00\x00\x00\x00";
+
+ char buf3[362] =
+ "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00"
+ "\x74\x00\x00\x00\xce\x00\xce\x00\x8c\x00\x00\x00\x1a\x00\x1a\x00"
+ "\x40\x00\x00\x00\x04\x00\x04\x00\x5a\x00\x00\x00\x16\x00\x16\x00"
+ "\x5e\x00\x00\x00\x10\x00\x10\x00\x5a\x01\x00\x00\x05\x02\x89\x62"
+ "\x31\x00\x37\x00\x2e\x00\x32\x00\x30\x00\x31\x00\x2e\x00\x35\x00"
+ "\x37\x00\x2e\x00\x31\x00\x32\x00\x31\x00\x71\x5c\x30\x75\x77\x00"
+ "\x6f\x00\x72\x00\x6b\x00\x73\x00\x74\x00\x61\x00\x74\x00\x69\x00"
+ "\x6f\x00\x6e\x00\xab\xad\xeb\x72\x01\xd4\x5f\xdf\x59\x07\x5f\xa9"
+ "\xfd\x54\x98\x2d\xfa\x17\xbb\xf1\x3c\x8f\xf5\x20\xe6\x8f\xd7\x0a"
+ "\xc9\x19\x3e\x94\x61\x31\xdb\x0f\x55\xe8\xe2\x53\x01\x01\x00\x00"
+ "\x00\x00\x00\x00\x00\x06\x3e\x30\xe4\x61\xcb\x01\x71\x98\x10\x6b"
+ "\x4c\x82\xec\xb3\x00\x00\x00\x00\x02\x00\x06\x00\x43\x00\x4f\x00"
+ "\x53\x00\x01\x00\x12\x00\x43\x00\x4f\x00\x53\x00\x57\x00\x49\x00"
+ "\x4e\x00\x37\x00\x4a\x00\x50\x00\x04\x00\x1a\x00\x63\x00\x6f\x00"
+ "\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00\x6c\x00\x65\x00\x2e\x00"
+ "\x63\x00\x6f\x00\x6d\x00\x03\x00\x2e\x00\x63\x00\x6f\x00\x73\x00"
+ "\x77\x00\x69\x00\x6e\x00\x37\x00\x6a\x00\x70\x00\x2e\x00\x63\x00"
+ "\x6f\x00\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00\x6c\x00\x65\x00"
+ "\x2e\x00\x63\x00\x6f\x00\x6d\x00\x05\x00\x1a\x00\x63\x00\x6f\x00"
+ "\x73\x00\x2e\x00\x61\x00\x70\x00\x70\x00\x6c\x00\x65\x00\x2e\x00"
+ "\x63\x00\x6f\x00\x6d\x00\x07\x00\x08\x00\xab\xec\xcc\x30\xe4\x61"
+ "\xcb\x01\x00\x00\x00\x00\x00\x00\x00\x00\xbc\x2e\xba\x3f\xd1\xb1"
+ "\xa7\x70\x00\x9d\x55\xa0\x59\x74\x2b\x78";
+
+
+ struct ntlm_type2 type2;
+ struct ntlm_type3 type3;
+ struct ntlm_buf data;
+ int ret;
+
+ data.length = sizeof(buf2);
+ data.data = buf2;
+
+ memset(&type2, 0, sizeof(type2));
+
+ ret = heim_ntlm_decode_type2(&data, &type2);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2(jp): %d", ret);
+
+ data.data = NULL;
+ data.length = 0;
+
+ ret = heim_ntlm_encode_type2(&type2, &data);
+ if (ret)
+ errx(1, "heim_ntlm_encode_type2(jp): %d", ret);
+
+ heim_ntlm_free_type2(&type2);
+ heim_ntlm_free_buf(&data);
+
+ data.length = sizeof(buf3);
+ data.data = buf3;
+
+ memset(&type3, 0, sizeof(type3));
+
+ ret = heim_ntlm_decode_type3(&data, 1, &type3);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2(jp): %d", ret);
+
+ data.data = NULL;
+ data.length = 0;
+
+ ret = heim_ntlm_encode_type3(&type3, &data, NULL);
+ if (ret)
+ errx(1, "heim_ntlm_decode_type2(jp): %d", ret);
+
+ heim_ntlm_free_type3(&type3);
+ heim_ntlm_free_buf(&data);
+
+ return 0;
+}
+
+
static int verbose_flag = 0;
static int version_flag = 0;
static int help_flag = 0;
@@ -342,11 +570,11 @@ usage (int ret)
int
main(int argc, char **argv)
{
- int ret = 0, optind = 0;
+ int ret = 0, optidx = 0;
setprogname(argv[0]);
- if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optind))
+ if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
if (help_flag)
@@ -357,24 +585,33 @@ main(int argc, char **argv)
exit(0);
}
- argc -= optind;
- argv += optind;
-
if (verbose_flag)
printf("test_parse\n");
+ ret |= test_parse();
- ret += test_parse();
if (verbose_flag)
printf("test_keys\n");
+ ret |= test_keys();
- ret += test_keys();
if (verbose_flag)
printf("test_ntlm2_session_resp\n");
- ret += test_ntlm2_session_resp();
+ ret |= test_ntlm2_session_resp();
if (verbose_flag)
printf("test_targetinfo\n");
- ret += test_targetinfo();
+ ret |= test_targetinfo();
+
+ if (verbose_flag)
+ printf("test_ntlmv2\n");
+ ret |= test_ntlmv2();
+
+ if (verbose_flag)
+ printf("test_string2key\n");
+ ret |= test_string2key();
+
+ if (verbose_flag)
+ printf("test_jp\n");
+ ret |= test_jp();
return ret;
}
diff --git a/lib/otp/Makefile.am b/lib/otp/Makefile.am
index 9a54e538418b..018e4ef32f7d 100644
--- a/lib/otp/Makefile.am
+++ b/lib/otp/Makefile.am
@@ -2,7 +2,10 @@
include $(top_srcdir)/Makefile.am.common
-AM_CPPFLAGS += $(INCLUDE_hcrypto) $(ROKEN_RENAME)
+AM_CPPFLAGS += $(ROKEN_RENAME)
+if HAVE_DBHEADER
+AM_CPPFLAGS += -I$(DBHEADER)
+endif
noinst_PROGRAMS = otptest
@@ -16,16 +19,16 @@ lib_LTLIBRARIES = libotp.la
libotp_la_LDFLAGS = -version-info 1:5:1
libotp_la_LIBADD = $(LIB_hcrypto) $(LIB_roken)
-if HAVE_DB1
+if HAVE_DB3
ndbm_wrap = ndbm_wrap.c ndbm_wrap.h
-libotp_la_LIBADD += $(LIB_db_create)
+libotp_la_LIBADD += $(DB3LIB)
else
-if HAVE_DB3
+if HAVE_DB1
ndbm_wrap = ndbm_wrap.c ndbm_wrap.h
-libotp_la_LIBADD += $(LIB_db_create)
+libotp_la_LIBADD += $(DB1LIB)
else
ndbm_wrap =
-libotp_la_LIBADD += $(LIB_NDBM)
+libotp_la_LIBADD += $(NDBMLIB)
endif
endif
diff --git a/lib/otp/Makefile.in b/lib/otp/Makefile.in
index dabdcf799cf8..839c51add3f7 100644
--- a/lib/otp/Makefile.in
+++ b/lib/otp/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,15 +96,13 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
+@HAVE_DBHEADER_TRUE@am__append_1 = -I$(DBHEADER)
noinst_PROGRAMS = otptest$(EXEEXT)
check_PROGRAMS = otptest$(EXEEXT)
-@HAVE_DB1_TRUE@am__append_1 = $(LIB_db_create)
-@HAVE_DB1_FALSE@@HAVE_DB3_TRUE@am__append_2 = $(LIB_db_create)
-@HAVE_DB1_FALSE@@HAVE_DB3_FALSE@am__append_3 = $(LIB_NDBM)
-@versionscript_TRUE@am__append_4 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
+@HAVE_DB3_TRUE@am__append_2 = $(DB3LIB)
+@HAVE_DB1_TRUE@@HAVE_DB3_FALSE@am__append_3 = $(DB1LIB)
+@HAVE_DB1_FALSE@@HAVE_DB3_FALSE@am__append_4 = $(NDBMLIB)
+@versionscript_TRUE@am__append_5 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
subdir = lib/otp
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -66,8 +118,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -80,6 +131,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -99,6 +151,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -124,54 +178,113 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
-@HAVE_DB1_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
-@HAVE_DB1_FALSE@@HAVE_DB3_TRUE@am__DEPENDENCIES_3 = \
-@HAVE_DB1_FALSE@@HAVE_DB3_TRUE@ $(am__DEPENDENCIES_1)
+@HAVE_DB3_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+@HAVE_DB1_TRUE@@HAVE_DB3_FALSE@am__DEPENDENCIES_3 = \
+@HAVE_DB1_TRUE@@HAVE_DB3_FALSE@ $(am__DEPENDENCIES_1)
@HAVE_DB1_FALSE@@HAVE_DB3_FALSE@am__DEPENDENCIES_4 = \
@HAVE_DB1_FALSE@@HAVE_DB3_FALSE@ $(am__DEPENDENCIES_1)
dist_libotp_la_OBJECTS = otp.lo otp_challenge.lo otp_db.lo otp_md.lo \
otp_parse.lo otp_print.lo otp_verify.lo
-@HAVE_DB1_FALSE@@HAVE_DB3_TRUE@am__objects_1 = ndbm_wrap.lo
-@HAVE_DB1_TRUE@am__objects_1 = ndbm_wrap.lo
+@HAVE_DB1_TRUE@@HAVE_DB3_FALSE@am__objects_1 = ndbm_wrap.lo
+@HAVE_DB3_TRUE@am__objects_1 = ndbm_wrap.lo
@do_roken_rename_TRUE@am__objects_2 = snprintf.lo strcasecmp.lo \
@do_roken_rename_TRUE@ strncasecmp.lo strlwr.lo strlcpy.lo \
@do_roken_rename_TRUE@ strlcat.lo
nodist_libotp_la_OBJECTS = $(am__objects_1) $(am__objects_2)
libotp_la_OBJECTS = $(dist_libotp_la_OBJECTS) \
$(nodist_libotp_la_OBJECTS)
-libotp_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libotp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libotp_la_LDFLAGS) $(LDFLAGS) -o $@
PROGRAMS = $(noinst_PROGRAMS)
otptest_SOURCES = otptest.c
otptest_OBJECTS = otptest.$(OBJEXT)
otptest_DEPENDENCIES = libotp.la
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(dist_libotp_la_SOURCES) $(nodist_libotp_la_SOURCES) \
otptest.c
DIST_SOURCES = $(dist_libotp_la_SOURCES) otptest.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -190,12 +303,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -205,17 +318,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -234,12 +347,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -248,6 +358,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -264,10 +375,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -275,6 +384,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -289,12 +399,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -311,6 +424,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -325,13 +439,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -355,6 +463,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -390,38 +500,46 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
-AM_CPPFLAGS = $(INCLUDES_roken) $(INCLUDE_hcrypto) $(ROKEN_RENAME)
+AM_CPPFLAGS = $(INCLUDES_roken) $(ROKEN_RENAME) $(am__append_1)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
otptest_LDADD = libotp.la
include_HEADERS = otp.h
lib_LTLIBRARIES = libotp.la
-libotp_la_LDFLAGS = -version-info 1:5:1 $(am__append_4)
-libotp_la_LIBADD = $(LIB_hcrypto) $(LIB_roken) $(am__append_1) \
- $(am__append_2) $(am__append_3)
+libotp_la_LDFLAGS = -version-info 1:5:1 $(am__append_5)
+libotp_la_LIBADD = $(LIB_hcrypto) $(LIB_roken) $(am__append_2) \
+ $(am__append_3) $(am__append_4)
@HAVE_DB1_FALSE@@HAVE_DB3_FALSE@ndbm_wrap =
-@HAVE_DB1_FALSE@@HAVE_DB3_TRUE@ndbm_wrap = ndbm_wrap.c ndbm_wrap.h
-@HAVE_DB1_TRUE@ndbm_wrap = ndbm_wrap.c ndbm_wrap.h
+@HAVE_DB1_TRUE@@HAVE_DB3_FALSE@ndbm_wrap = ndbm_wrap.c ndbm_wrap.h
+@HAVE_DB3_TRUE@ndbm_wrap = ndbm_wrap.c ndbm_wrap.h
dist_libotp_la_SOURCES = \
otp.c \
otp_challenge.c \
@@ -451,7 +569,7 @@ EXTRA_DIST = NTMakefile version-script.map
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -464,7 +582,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/otp/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/otp/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -473,6 +590,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -482,9 +600,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -492,6 +610,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -507,14 +627,17 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libotp.la: $(libotp_la_OBJECTS) $(libotp_la_DEPENDENCIES)
- $(libotp_la_LINK) -rpath $(libdir) $(libotp_la_OBJECTS) $(libotp_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libotp.la: $(libotp_la_OBJECTS) $(libotp_la_DEPENDENCIES) $(EXTRA_libotp_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libotp_la_LINK) -rpath $(libdir) $(libotp_la_OBJECTS) $(libotp_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -533,9 +656,10 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-otptest$(EXEEXT): $(otptest_OBJECTS) $(otptest_DEPENDENCIES)
+
+otptest$(EXEEXT): $(otptest_OBJECTS) $(otptest_DEPENDENCIES) $(EXTRA_otptest_DEPENDENCIES)
@rm -f otptest$(EXEEXT)
- $(LINK) $(otptest_OBJECTS) $(otptest_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(otptest_OBJECTS) $(otptest_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -560,25 +684,25 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strncasecmp.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -587,8 +711,11 @@ clean-libtool:
-rm -rf .libs _libs
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -602,30 +729,17 @@ uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -637,15 +751,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -654,6 +764,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -710,10 +835,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -756,9 +886,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -800,41 +929,55 @@ ps-am:
uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
- clean-libtool clean-noinstPROGRAMS ctags dist-hook distclean \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool clean-noinstPROGRAMS \
+ cscopelist-am ctags ctags-am dist-hook distclean \
distclean-compile distclean-generic distclean-libtool \
distclean-tags distdir dvi dvi-am html html-am info info-am \
install install-am install-data install-data-am \
install-data-hook install-dvi install-dvi-am install-exec \
- install-exec-am install-exec-hook install-html install-html-am \
- install-includeHEADERS install-info install-info-am \
- install-libLTLIBRARIES install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
+ install-exec-am install-exec-local install-html \
+ install-html-am install-includeHEADERS install-info \
+ install-info-am install-libLTLIBRARIES install-man install-pdf \
+ install-pdf-am install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-hook \
+ tags tags-am uninstall uninstall-am uninstall-hook \
uninstall-includeHEADERS uninstall-libLTLIBRARIES
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
-install-exec-hook: install-suid-programs
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -842,7 +985,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -899,6 +1042,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -941,6 +1086,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -954,13 +1112,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/otp/otptest.c b/lib/otp/otptest.c
index 869f8742155c..dbf213168091 100644
--- a/lib/otp/otptest.c
+++ b/lib/otp/otptest.c
@@ -107,7 +107,7 @@ test (void)
{"sha", "OTP's are good", "correct", 0, "d51f3e99bf8e6f0b", "RUST WELT KICK FELL TAIL FRAU"},
{"sha", "OTP's are good", "correct", 1, "82aeb52d943774e4", "FLIT DOSE ALSO MEW DRUM DEFY"},
{"sha", "OTP's are good", "correct", 99, "4f296a74fe1567ec", "AURA ALOE HURL WING BERG WAIT"},
- {NULL}
+ {NULL, NULL, NULL, 0, NULL, NULL}
};
struct test *t;
diff --git a/lib/roken/Makefile.am b/lib/roken/Makefile.am
index e723288107c7..1a1593949b79 100644
--- a/lib/roken/Makefile.am
+++ b/lib/roken/Makefile.am
@@ -20,14 +20,18 @@ if HAVE_DBHEADER
AM_CPPFLAGS += -I$(DBHEADER)
endif
-noinst_PROGRAMS = snprintf-test resolve-test rkpty
+noinst_PROGRAMS = snprintf-test resolve-test rkpty test-detach
+
+CHECK_LOCAL = snprintf-test resolve-test rkpty make-roken
check_PROGRAMS = \
base64-test \
getaddrinfo-test \
getifaddrs-test \
+ getxxyyy-test \
hex-test \
test-readenv \
+ resolve-test \
parse_bytes-test \
parse_reply-test \
parse_time-test \
@@ -49,6 +53,8 @@ parse_reply_test_CFLAGS = -DTEST_RESOLVE
test_readenv_SOURCES = test-readenv.c test-mem.c
+test_detach_SOURCES = test-detach.c
+
rkpty_LDADD = $(LIB_openpty) $(LDADD)
parse_time_test_SOURCES = parse_time-test.c test-mem.c
@@ -63,6 +69,9 @@ tsearch_test_SOURCES = tsearch-test.c
tsearch_test_LDADD = libtest.la $(LDADD)
tsearch_test_CFLAGS = -DTEST_TSEARCH
+getxxyyy_test_SOURCES = getxxyyy.c
+getxxyyy_test_CFLAGS = -DTEST_GETXXYYY
+
resolve_test_SOURCES = resolve-test.c
libroken_la_SOURCES = \
@@ -71,6 +80,7 @@ libroken_la_SOURCES = \
concat.c \
cloexec.c \
ct.c \
+ detach.c \
doxygen.c \
dumpdata.c \
environment.c \
@@ -83,6 +93,7 @@ libroken_la_SOURCES = \
getarg.c \
getnameinfo_verified.c \
getprogname.c \
+ getxxyyy.c \
h_errno.c \
hex.c \
hostent_find_fqdn.c \
@@ -90,6 +101,7 @@ libroken_la_SOURCES = \
k_getpwnam.c \
k_getpwuid.c \
mini_inetd.c \
+ mkdir.c \
net_read.c \
net_write.c \
parse_bytes.c \
@@ -128,7 +140,7 @@ EXTRA_libroken_la_SOURCES = \
search.hin \
vis.hin
-libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt)
+libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt) $(LIB_pidfile)
$(LTLIBOBJS) $(libroken_la_OBJECTS): roken.h $(XHEADERS)
@@ -233,7 +245,23 @@ EXTRA_DIST = \
NTMakefile \
roken.awk roken.h.in \
$(man_MANS) \
- test-mem.h \
+ dirent.c \
+ dirent.hin \
+ dirent-test.c \
+ dlfcn.hin \
+ dlfcn_w32.c \
+ getifaddrs_w32.c \
ndbm_wrap.c \
ndbm_wrap.h \
+ rename.c \
+ simple_exec_w32.c \
+ sleep.c \
+ sockstartup_w32.c \
+ stdbool.hin \
+ stdint.hin \
+ syslogc.c \
+ syslog.hin \
+ test-mem.h \
+ test-mini_inetd.c \
+ win32_alloc.c \
version-script.map
diff --git a/lib/roken/Makefile.in b/lib/roken/Makefile.in
index 9c05b08a404b..41af2c3d40ac 100644
--- a/lib/roken/Makefile.in
+++ b/lib/roken/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,34 +96,16 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(am__dist_include_HEADERS_DIST) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog chown.c \
- closefrom.c copyhostent.c daemon.c ecalloc.c emalloc.c \
- erealloc.c err.c errx.c estrdup.c fchown.c flock.c fnmatch.c \
- freeaddrinfo.c freehostent.c gai_strerror.c getaddrinfo.c \
- getcap.c getcwd.c getdtablesize.c getegid.c geteuid.c getgid.c \
- gethostname.c getifaddrs.c getipnodebyaddr.c getipnodebyname.c \
- getnameinfo.c getopt.c gettimeofday.c getuid.c getusershell.c \
- glob.c hstrerror.c inet_aton.c inet_ntop.c inet_pton.c \
- initgroups.c innetgr.c install-sh iruserok.c localtime_r.c \
- lstat.c memmove.c missing mkinstalldirs mkstemp.c putenv.c \
- rcmd.c readv.c recvmsg.c sendmsg.c setegid.c setenv.c \
- seteuid.c strcasecmp.c strdup.c strerror.c strftime.c \
- strlcat.c strlcpy.c strlwr.c strncasecmp.c strndup.c strnlen.c \
- strptime.c strsep.c strsep_copy.c strtok_r.c strupr.c swab.c \
- timegm.c tsearch.c unsetenv.c verr.c verrx.c vsyslog.c vwarn.c \
- vwarnx.c warn.c warnx.c writev.c
@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
@HAVE_DBHEADER_TRUE@am__append_2 = -I$(DBHEADER)
noinst_PROGRAMS = snprintf-test$(EXEEXT) resolve-test$(EXEEXT) \
- rkpty$(EXEEXT) $(am__EXEEXT_1)
+ rkpty$(EXEEXT) test-detach$(EXEEXT) $(am__EXEEXT_1)
check_PROGRAMS = base64-test$(EXEEXT) getaddrinfo-test$(EXEEXT) \
- getifaddrs-test$(EXEEXT) hex-test$(EXEEXT) \
- test-readenv$(EXEEXT) parse_bytes-test$(EXEEXT) \
- parse_reply-test$(EXEEXT) parse_time-test$(EXEEXT) \
- snprintf-test$(EXEEXT) strpftime-test$(EXEEXT) \
- tsearch-test$(EXEEXT)
+ getifaddrs-test$(EXEEXT) getxxyyy-test$(EXEEXT) \
+ hex-test$(EXEEXT) test-readenv$(EXEEXT) resolve-test$(EXEEXT) \
+ parse_bytes-test$(EXEEXT) parse_reply-test$(EXEEXT) \
+ parse_time-test$(EXEEXT) snprintf-test$(EXEEXT) \
+ strpftime-test$(EXEEXT) tsearch-test$(EXEEXT)
@have_socket_wrapper_TRUE@am__append_3 = socket_wrapper.c socket_wrapper.h
@have_socket_wrapper_TRUE@am__append_4 = socket_wrapper.h
@@ -91,8 +127,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -105,6 +140,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -124,6 +160,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__dist_include_HEADERS_DIST) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -149,39 +187,47 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man3dir)" \
"$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)" \
"$(DESTDIR)$(rokenincludedir)"
LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
am__DEPENDENCIES_1 =
-libroken_la_DEPENDENCIES = @LTLIBOBJS@ $(am__DEPENDENCIES_1)
+libroken_la_DEPENDENCIES = @LTLIBOBJS@ $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1)
am__libroken_la_SOURCES_DIST = base64.c bswap.c concat.c cloexec.c \
- ct.c doxygen.c dumpdata.c environment.c eread.c esetenv.c \
- ewrite.c getaddrinfo_hostspec.c get_default_username.c \
- get_window_size.c getarg.c getnameinfo_verified.c \
- getprogname.c h_errno.c hex.c hostent_find_fqdn.c issuid.c \
- k_getpwnam.c k_getpwuid.c mini_inetd.c net_read.c net_write.c \
- parse_bytes.c parse_time.c parse_units.c qsort.c rand.c \
- realloc.c resolve.c roken_gethostby.c rtbl.c rtbl.h \
- setprogname.c signal.c simple_exec.c snprintf.c socket.c \
- strcollect.c strerror_r.c strpool.c timeval.c tm2time.c \
- unvis.c verify.c vis.c warnerr.c write_pid.c xfree.c xdbm.h \
- socket_wrapper.c socket_wrapper.h
+ ct.c detach.c doxygen.c dumpdata.c environment.c eread.c \
+ esetenv.c ewrite.c getaddrinfo_hostspec.c \
+ get_default_username.c get_window_size.c getarg.c \
+ getnameinfo_verified.c getprogname.c getxxyyy.c h_errno.c \
+ hex.c hostent_find_fqdn.c issuid.c k_getpwnam.c k_getpwuid.c \
+ mini_inetd.c mkdir.c net_read.c net_write.c parse_bytes.c \
+ parse_time.c parse_units.c qsort.c rand.c realloc.c resolve.c \
+ roken_gethostby.c rtbl.c rtbl.h setprogname.c signal.c \
+ simple_exec.c snprintf.c socket.c strcollect.c strerror_r.c \
+ strpool.c timeval.c tm2time.c unvis.c verify.c vis.c warnerr.c \
+ write_pid.c xfree.c xdbm.h socket_wrapper.c socket_wrapper.h
@have_socket_wrapper_TRUE@am__objects_1 = \
@have_socket_wrapper_TRUE@ libroken_la-socket_wrapper.lo
am_libroken_la_OBJECTS = libroken_la-base64.lo libroken_la-bswap.lo \
libroken_la-concat.lo libroken_la-cloexec.lo libroken_la-ct.lo \
- libroken_la-doxygen.lo libroken_la-dumpdata.lo \
- libroken_la-environment.lo libroken_la-eread.lo \
- libroken_la-esetenv.lo libroken_la-ewrite.lo \
- libroken_la-getaddrinfo_hostspec.lo \
+ libroken_la-detach.lo libroken_la-doxygen.lo \
+ libroken_la-dumpdata.lo libroken_la-environment.lo \
+ libroken_la-eread.lo libroken_la-esetenv.lo \
+ libroken_la-ewrite.lo libroken_la-getaddrinfo_hostspec.lo \
libroken_la-get_default_username.lo \
libroken_la-get_window_size.lo libroken_la-getarg.lo \
libroken_la-getnameinfo_verified.lo libroken_la-getprogname.lo \
- libroken_la-h_errno.lo libroken_la-hex.lo \
- libroken_la-hostent_find_fqdn.lo libroken_la-issuid.lo \
- libroken_la-k_getpwnam.lo libroken_la-k_getpwuid.lo \
- libroken_la-mini_inetd.lo libroken_la-net_read.lo \
+ libroken_la-getxxyyy.lo libroken_la-h_errno.lo \
+ libroken_la-hex.lo libroken_la-hostent_find_fqdn.lo \
+ libroken_la-issuid.lo libroken_la-k_getpwnam.lo \
+ libroken_la-k_getpwuid.lo libroken_la-mini_inetd.lo \
+ libroken_la-mkdir.lo libroken_la-net_read.lo \
libroken_la-net_write.lo libroken_la-parse_bytes.lo \
libroken_la-parse_time.lo libroken_la-parse_units.lo \
libroken_la-qsort.lo libroken_la-rand.lo \
@@ -196,14 +242,18 @@ am_libroken_la_OBJECTS = libroken_la-base64.lo libroken_la-bswap.lo \
libroken_la-warnerr.lo libroken_la-write_pid.lo \
libroken_la-xfree.lo $(am__objects_1)
libroken_la_OBJECTS = $(am_libroken_la_OBJECTS)
-libroken_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libroken_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libroken_la_LDFLAGS) $(LDFLAGS) -o $@
libtest_la_LIBADD =
am_libtest_la_OBJECTS = libtest_la-strftime.lo libtest_la-strptime.lo \
libtest_la-snprintf.lo libtest_la-tsearch.lo
libtest_la_OBJECTS = $(am_libtest_la_OBJECTS)
-libtest_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+libtest_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(libtest_la_CFLAGS) \
$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
@CROSS_COMPILE_FALSE@am__EXEEXT_1 = make-roken$(EXEEXT)
@@ -220,6 +270,13 @@ getifaddrs_test_SOURCES = getifaddrs-test.c
getifaddrs_test_OBJECTS = getifaddrs-test.$(OBJEXT)
getifaddrs_test_LDADD = $(LDADD)
getifaddrs_test_DEPENDENCIES = libroken.la
+am_getxxyyy_test_OBJECTS = getxxyyy_test-getxxyyy.$(OBJEXT)
+getxxyyy_test_OBJECTS = $(am_getxxyyy_test_OBJECTS)
+getxxyyy_test_LDADD = $(LDADD)
+getxxyyy_test_DEPENDENCIES = libroken.la
+getxxyyy_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(getxxyyy_test_CFLAGS) \
+ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
hex_test_SOURCES = hex-test.c
hex_test_OBJECTS = hex-test.$(OBJEXT)
hex_test_LDADD = $(LDADD)
@@ -237,9 +294,10 @@ am_parse_reply_test_OBJECTS = \
parse_reply_test_OBJECTS = $(am_parse_reply_test_OBJECTS)
parse_reply_test_LDADD = $(LDADD)
parse_reply_test_DEPENDENCIES = libroken.la
-parse_reply_test_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(parse_reply_test_CFLAGS) \
- $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+parse_reply_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(parse_reply_test_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \
+ -o $@
am_parse_time_test_OBJECTS = parse_time-test.$(OBJEXT) \
test-mem.$(OBJEXT)
parse_time_test_OBJECTS = $(am_parse_time_test_OBJECTS)
@@ -255,15 +313,20 @@ rkpty_DEPENDENCIES = $(am__DEPENDENCIES_1) $(LDADD)
am_snprintf_test_OBJECTS = snprintf_test-snprintf-test.$(OBJEXT)
snprintf_test_OBJECTS = $(am_snprintf_test_OBJECTS)
snprintf_test_DEPENDENCIES = libtest.la $(LDADD)
-snprintf_test_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+snprintf_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(snprintf_test_CFLAGS) \
$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
am_strpftime_test_OBJECTS = strpftime_test-strpftime-test.$(OBJEXT)
strpftime_test_OBJECTS = $(am_strpftime_test_OBJECTS)
strpftime_test_DEPENDENCIES = libtest.la $(LDADD)
-strpftime_test_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
- $(LIBTOOLFLAGS) --mode=link $(CCLD) $(strpftime_test_CFLAGS) \
- $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+strpftime_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(strpftime_test_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o \
+ $@
+am_test_detach_OBJECTS = test-detach.$(OBJEXT)
+test_detach_OBJECTS = $(am_test_detach_OBJECTS)
+test_detach_LDADD = $(LDADD)
+test_detach_DEPENDENCIES = libroken.la
am_test_readenv_OBJECTS = test-readenv.$(OBJEXT) test-mem.$(OBJEXT)
test_readenv_OBJECTS = $(am_test_readenv_OBJECTS)
test_readenv_LDADD = $(LDADD)
@@ -271,35 +334,63 @@ test_readenv_DEPENDENCIES = libroken.la
am_tsearch_test_OBJECTS = tsearch_test-tsearch-test.$(OBJEXT)
tsearch_test_OBJECTS = $(am_tsearch_test_OBJECTS)
tsearch_test_DEPENDENCIES = libtest.la $(LDADD)
-tsearch_test_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+tsearch_test_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(tsearch_test_CFLAGS) \
$(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(libroken_la_SOURCES) $(EXTRA_libroken_la_SOURCES) \
$(libtest_la_SOURCES) base64-test.c getaddrinfo-test.c \
- getifaddrs-test.c hex-test.c $(nodist_make_roken_SOURCES) \
- parse_bytes-test.c $(parse_reply_test_SOURCES) \
- $(parse_time_test_SOURCES) $(resolve_test_SOURCES) rkpty.c \
- $(snprintf_test_SOURCES) $(strpftime_test_SOURCES) \
+ getifaddrs-test.c $(getxxyyy_test_SOURCES) hex-test.c \
+ $(nodist_make_roken_SOURCES) parse_bytes-test.c \
+ $(parse_reply_test_SOURCES) $(parse_time_test_SOURCES) \
+ $(resolve_test_SOURCES) rkpty.c $(snprintf_test_SOURCES) \
+ $(strpftime_test_SOURCES) $(test_detach_SOURCES) \
$(test_readenv_SOURCES) $(tsearch_test_SOURCES)
DIST_SOURCES = $(am__libroken_la_SOURCES_DIST) \
$(EXTRA_libroken_la_SOURCES) $(libtest_la_SOURCES) \
- base64-test.c getaddrinfo-test.c getifaddrs-test.c hex-test.c \
- parse_bytes-test.c $(parse_reply_test_SOURCES) \
- $(parse_time_test_SOURCES) $(resolve_test_SOURCES) rkpty.c \
- $(snprintf_test_SOURCES) $(strpftime_test_SOURCES) \
+ base64-test.c getaddrinfo-test.c getifaddrs-test.c \
+ $(getxxyyy_test_SOURCES) hex-test.c parse_bytes-test.c \
+ $(parse_reply_test_SOURCES) $(parse_time_test_SOURCES) \
+ $(resolve_test_SOURCES) rkpty.c $(snprintf_test_SOURCES) \
+ $(strpftime_test_SOURCES) $(test_detach_SOURCES) \
$(test_readenv_SOURCES) $(tsearch_test_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
man3dir = $(mandir)/man3
MANS = $(man_MANS)
am__dist_include_HEADERS_DIST = base64.h getarg.h hex.h parse_bytes.h \
@@ -307,15 +398,229 @@ am__dist_include_HEADERS_DIST = base64.h getarg.h hex.h parse_bytes.h \
xdbm.h socket_wrapper.h
HEADERS = $(dist_include_HEADERS) $(nodist_include_HEADERS) \
$(nodist_rokeninclude_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver ChangeLog chown.c closefrom.c \
+ copyhostent.c daemon.c ecalloc.c emalloc.c erealloc.c err.c \
+ errx.c estrdup.c fchown.c flock.c fnmatch.c freeaddrinfo.c \
+ freehostent.c gai_strerror.c getaddrinfo.c getcap.c getcwd.c \
+ getdtablesize.c getegid.c geteuid.c getgid.c gethostname.c \
+ getifaddrs.c getipnodebyaddr.c getipnodebyname.c getnameinfo.c \
+ getopt.c gettimeofday.c getuid.c getusershell.c glob.c \
+ hstrerror.c inet_aton.c inet_ntop.c inet_pton.c initgroups.c \
+ innetgr.c install-sh iruserok.c localtime_r.c lstat.c \
+ memmove.c memset_s.c missing mkinstalldirs mkstemp.c putenv.c \
+ rcmd.c readv.c recvmsg.c sendmsg.c setegid.c setenv.c \
+ seteuid.c strcasecmp.c strdup.c strerror.c strftime.c \
+ strlcat.c strlcpy.c strlwr.c strncasecmp.c strndup.c strnlen.c \
+ strptime.c strsep.c strsep_copy.c strtok_r.c strtoll.c \
+ strtoull.c strupr.c swab.c timegm.c tsearch.c unsetenv.c \
+ verr.c verrx.c vsyslog.c vwarn.c vwarnx.c warn.c warnx.c \
+ writev.c
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -334,12 +639,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -349,17 +654,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -378,12 +683,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -392,6 +694,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -408,10 +711,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -419,6 +720,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -433,12 +735,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -455,6 +760,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -469,13 +775,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -499,6 +799,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -534,7 +836,8 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .hin
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8 .hin
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken) $(libroken_la_CPPFLAGS) \
$(am__append_2)
@@ -542,28 +845,36 @@ AM_CPPFLAGS = $(INCLUDES_roken) $(libroken_la_CPPFLAGS) \
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
ACLOCAL_AMFLAGS = -I ../../cf
CLEANFILES = roken.h make-roken.c $(XHEADERS) err.h fnmatch.h glob.h \
ifaddrs.h search.h vis.h
lib_LTLIBRARIES = libroken.la
libroken_la_LDFLAGS = -version-info 19:0:1 $(am__append_1)
libroken_la_CPPFLAGS = -DBUILD_ROKEN_LIB
+CHECK_LOCAL = snprintf-test resolve-test rkpty make-roken
TESTS = $(check_PROGRAMS)
LDADD = libroken.la
make_roken_LDADD =
@@ -573,6 +884,7 @@ libtest_la_CFLAGS = -DTEST_SNPRINTF -DTEST_STRPFTIME
parse_reply_test_SOURCES = parse_reply-test.c resolve.c
parse_reply_test_CFLAGS = -DTEST_RESOLVE
test_readenv_SOURCES = test-readenv.c test-mem.c
+test_detach_SOURCES = test-detach.c
rkpty_LDADD = $(LIB_openpty) $(LDADD)
parse_time_test_SOURCES = parse_time-test.c test-mem.c
strpftime_test_SOURCES = strpftime-test.c strpftime-test.h
@@ -584,19 +896,21 @@ snprintf_test_CFLAGS = -DTEST_SNPRINTF
tsearch_test_SOURCES = tsearch-test.c
tsearch_test_LDADD = libtest.la $(LDADD)
tsearch_test_CFLAGS = -DTEST_TSEARCH
+getxxyyy_test_SOURCES = getxxyyy.c
+getxxyyy_test_CFLAGS = -DTEST_GETXXYYY
resolve_test_SOURCES = resolve-test.c
libroken_la_SOURCES = base64.c bswap.c concat.c cloexec.c ct.c \
- doxygen.c dumpdata.c environment.c eread.c esetenv.c ewrite.c \
- getaddrinfo_hostspec.c get_default_username.c \
+ detach.c doxygen.c dumpdata.c environment.c eread.c esetenv.c \
+ ewrite.c getaddrinfo_hostspec.c get_default_username.c \
get_window_size.c getarg.c getnameinfo_verified.c \
- getprogname.c h_errno.c hex.c hostent_find_fqdn.c issuid.c \
- k_getpwnam.c k_getpwuid.c mini_inetd.c net_read.c net_write.c \
- parse_bytes.c parse_time.c parse_units.c qsort.c rand.c \
- realloc.c resolve.c roken_gethostby.c rtbl.c rtbl.h \
- setprogname.c signal.c simple_exec.c snprintf.c socket.c \
- strcollect.c strerror_r.c strpool.c timeval.c tm2time.c \
- unvis.c verify.c vis.c warnerr.c write_pid.c xfree.c xdbm.h \
- $(am__append_3)
+ getprogname.c getxxyyy.c h_errno.c hex.c hostent_find_fqdn.c \
+ issuid.c k_getpwnam.c k_getpwuid.c mini_inetd.c mkdir.c \
+ net_read.c net_write.c parse_bytes.c parse_time.c \
+ parse_units.c qsort.c rand.c realloc.c resolve.c \
+ roken_gethostby.c rtbl.c rtbl.h setprogname.c signal.c \
+ simple_exec.c snprintf.c socket.c strcollect.c strerror_r.c \
+ strpool.c timeval.c tm2time.c unvis.c verify.c vis.c warnerr.c \
+ write_pid.c xfree.c xdbm.h $(am__append_3)
EXTRA_libroken_la_SOURCES = \
err.hin \
glob.hin \
@@ -605,7 +919,7 @@ EXTRA_libroken_la_SOURCES = \
search.hin \
vis.hin
-libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt)
+libroken_la_LIBADD = @LTLIBOBJS@ $(LIB_crypt) $(LIB_pidfile)
BUILT_SOURCES = roken.h $(am__append_6)
@have_err_h_FALSE@err_h = err.h
@have_err_h_TRUE@err_h =
@@ -633,16 +947,32 @@ EXTRA_DIST = \
NTMakefile \
roken.awk roken.h.in \
$(man_MANS) \
- test-mem.h \
+ dirent.c \
+ dirent.hin \
+ dirent-test.c \
+ dlfcn.hin \
+ dlfcn_w32.c \
+ getifaddrs_w32.c \
ndbm_wrap.c \
ndbm_wrap.h \
+ rename.c \
+ simple_exec_w32.c \
+ sleep.c \
+ sockstartup_w32.c \
+ stdbool.hin \
+ stdint.hin \
+ syslogc.c \
+ syslog.hin \
+ test-mem.h \
+ test-mini_inetd.c \
+ win32_alloc.c \
version-script.map
all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .hin .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .hin .c .lo .log .o .obj .test .test$(EXEEXT) .trs
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -655,7 +985,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/roken/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/roken/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -664,6 +993,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -673,9 +1003,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -683,6 +1013,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -698,25 +1030,31 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
- @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libroken.la: $(libroken_la_OBJECTS) $(libroken_la_DEPENDENCIES)
- $(libroken_la_LINK) -rpath $(libdir) $(libroken_la_OBJECTS) $(libroken_la_LIBADD) $(LIBS)
-libtest.la: $(libtest_la_OBJECTS) $(libtest_la_DEPENDENCIES)
- $(libtest_la_LINK) $(libtest_la_OBJECTS) $(libtest_la_LIBADD) $(LIBS)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libroken.la: $(libroken_la_OBJECTS) $(libroken_la_DEPENDENCIES) $(EXTRA_libroken_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libroken_la_LINK) -rpath $(libdir) $(libroken_la_OBJECTS) $(libroken_la_LIBADD) $(LIBS)
+
+libtest.la: $(libtest_la_OBJECTS) $(libtest_la_DEPENDENCIES) $(EXTRA_libtest_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libtest_la_LINK) $(libtest_la_OBJECTS) $(libtest_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -735,48 +1073,70 @@ clean-noinstPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-base64-test$(EXEEXT): $(base64_test_OBJECTS) $(base64_test_DEPENDENCIES)
+
+base64-test$(EXEEXT): $(base64_test_OBJECTS) $(base64_test_DEPENDENCIES) $(EXTRA_base64_test_DEPENDENCIES)
@rm -f base64-test$(EXEEXT)
- $(LINK) $(base64_test_OBJECTS) $(base64_test_LDADD) $(LIBS)
-getaddrinfo-test$(EXEEXT): $(getaddrinfo_test_OBJECTS) $(getaddrinfo_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(base64_test_OBJECTS) $(base64_test_LDADD) $(LIBS)
+
+getaddrinfo-test$(EXEEXT): $(getaddrinfo_test_OBJECTS) $(getaddrinfo_test_DEPENDENCIES) $(EXTRA_getaddrinfo_test_DEPENDENCIES)
@rm -f getaddrinfo-test$(EXEEXT)
- $(LINK) $(getaddrinfo_test_OBJECTS) $(getaddrinfo_test_LDADD) $(LIBS)
-getifaddrs-test$(EXEEXT): $(getifaddrs_test_OBJECTS) $(getifaddrs_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(getaddrinfo_test_OBJECTS) $(getaddrinfo_test_LDADD) $(LIBS)
+
+getifaddrs-test$(EXEEXT): $(getifaddrs_test_OBJECTS) $(getifaddrs_test_DEPENDENCIES) $(EXTRA_getifaddrs_test_DEPENDENCIES)
@rm -f getifaddrs-test$(EXEEXT)
- $(LINK) $(getifaddrs_test_OBJECTS) $(getifaddrs_test_LDADD) $(LIBS)
-hex-test$(EXEEXT): $(hex_test_OBJECTS) $(hex_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(getifaddrs_test_OBJECTS) $(getifaddrs_test_LDADD) $(LIBS)
+
+getxxyyy-test$(EXEEXT): $(getxxyyy_test_OBJECTS) $(getxxyyy_test_DEPENDENCIES) $(EXTRA_getxxyyy_test_DEPENDENCIES)
+ @rm -f getxxyyy-test$(EXEEXT)
+ $(AM_V_CCLD)$(getxxyyy_test_LINK) $(getxxyyy_test_OBJECTS) $(getxxyyy_test_LDADD) $(LIBS)
+
+hex-test$(EXEEXT): $(hex_test_OBJECTS) $(hex_test_DEPENDENCIES) $(EXTRA_hex_test_DEPENDENCIES)
@rm -f hex-test$(EXEEXT)
- $(LINK) $(hex_test_OBJECTS) $(hex_test_LDADD) $(LIBS)
-make-roken$(EXEEXT): $(make_roken_OBJECTS) $(make_roken_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(hex_test_OBJECTS) $(hex_test_LDADD) $(LIBS)
+
+make-roken$(EXEEXT): $(make_roken_OBJECTS) $(make_roken_DEPENDENCIES) $(EXTRA_make_roken_DEPENDENCIES)
@rm -f make-roken$(EXEEXT)
- $(LINK) $(make_roken_OBJECTS) $(make_roken_LDADD) $(LIBS)
-parse_bytes-test$(EXEEXT): $(parse_bytes_test_OBJECTS) $(parse_bytes_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(make_roken_OBJECTS) $(make_roken_LDADD) $(LIBS)
+
+parse_bytes-test$(EXEEXT): $(parse_bytes_test_OBJECTS) $(parse_bytes_test_DEPENDENCIES) $(EXTRA_parse_bytes_test_DEPENDENCIES)
@rm -f parse_bytes-test$(EXEEXT)
- $(LINK) $(parse_bytes_test_OBJECTS) $(parse_bytes_test_LDADD) $(LIBS)
-parse_reply-test$(EXEEXT): $(parse_reply_test_OBJECTS) $(parse_reply_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(parse_bytes_test_OBJECTS) $(parse_bytes_test_LDADD) $(LIBS)
+
+parse_reply-test$(EXEEXT): $(parse_reply_test_OBJECTS) $(parse_reply_test_DEPENDENCIES) $(EXTRA_parse_reply_test_DEPENDENCIES)
@rm -f parse_reply-test$(EXEEXT)
- $(parse_reply_test_LINK) $(parse_reply_test_OBJECTS) $(parse_reply_test_LDADD) $(LIBS)
-parse_time-test$(EXEEXT): $(parse_time_test_OBJECTS) $(parse_time_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(parse_reply_test_LINK) $(parse_reply_test_OBJECTS) $(parse_reply_test_LDADD) $(LIBS)
+
+parse_time-test$(EXEEXT): $(parse_time_test_OBJECTS) $(parse_time_test_DEPENDENCIES) $(EXTRA_parse_time_test_DEPENDENCIES)
@rm -f parse_time-test$(EXEEXT)
- $(LINK) $(parse_time_test_OBJECTS) $(parse_time_test_LDADD) $(LIBS)
-resolve-test$(EXEEXT): $(resolve_test_OBJECTS) $(resolve_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(parse_time_test_OBJECTS) $(parse_time_test_LDADD) $(LIBS)
+
+resolve-test$(EXEEXT): $(resolve_test_OBJECTS) $(resolve_test_DEPENDENCIES) $(EXTRA_resolve_test_DEPENDENCIES)
@rm -f resolve-test$(EXEEXT)
- $(LINK) $(resolve_test_OBJECTS) $(resolve_test_LDADD) $(LIBS)
-rkpty$(EXEEXT): $(rkpty_OBJECTS) $(rkpty_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(resolve_test_OBJECTS) $(resolve_test_LDADD) $(LIBS)
+
+rkpty$(EXEEXT): $(rkpty_OBJECTS) $(rkpty_DEPENDENCIES) $(EXTRA_rkpty_DEPENDENCIES)
@rm -f rkpty$(EXEEXT)
- $(LINK) $(rkpty_OBJECTS) $(rkpty_LDADD) $(LIBS)
-snprintf-test$(EXEEXT): $(snprintf_test_OBJECTS) $(snprintf_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(rkpty_OBJECTS) $(rkpty_LDADD) $(LIBS)
+
+snprintf-test$(EXEEXT): $(snprintf_test_OBJECTS) $(snprintf_test_DEPENDENCIES) $(EXTRA_snprintf_test_DEPENDENCIES)
@rm -f snprintf-test$(EXEEXT)
- $(snprintf_test_LINK) $(snprintf_test_OBJECTS) $(snprintf_test_LDADD) $(LIBS)
-strpftime-test$(EXEEXT): $(strpftime_test_OBJECTS) $(strpftime_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(snprintf_test_LINK) $(snprintf_test_OBJECTS) $(snprintf_test_LDADD) $(LIBS)
+
+strpftime-test$(EXEEXT): $(strpftime_test_OBJECTS) $(strpftime_test_DEPENDENCIES) $(EXTRA_strpftime_test_DEPENDENCIES)
@rm -f strpftime-test$(EXEEXT)
- $(strpftime_test_LINK) $(strpftime_test_OBJECTS) $(strpftime_test_LDADD) $(LIBS)
-test-readenv$(EXEEXT): $(test_readenv_OBJECTS) $(test_readenv_DEPENDENCIES)
+ $(AM_V_CCLD)$(strpftime_test_LINK) $(strpftime_test_OBJECTS) $(strpftime_test_LDADD) $(LIBS)
+
+test-detach$(EXEEXT): $(test_detach_OBJECTS) $(test_detach_DEPENDENCIES) $(EXTRA_test_detach_DEPENDENCIES)
+ @rm -f test-detach$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_detach_OBJECTS) $(test_detach_LDADD) $(LIBS)
+
+test-readenv$(EXEEXT): $(test_readenv_OBJECTS) $(test_readenv_DEPENDENCIES) $(EXTRA_test_readenv_DEPENDENCIES)
@rm -f test-readenv$(EXEEXT)
- $(LINK) $(test_readenv_OBJECTS) $(test_readenv_LDADD) $(LIBS)
-tsearch-test$(EXEEXT): $(tsearch_test_OBJECTS) $(tsearch_test_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_readenv_OBJECTS) $(test_readenv_LDADD) $(LIBS)
+
+tsearch-test$(EXEEXT): $(tsearch_test_OBJECTS) $(tsearch_test_DEPENDENCIES) $(EXTRA_tsearch_test_DEPENDENCIES)
@rm -f tsearch-test$(EXEEXT)
- $(tsearch_test_LINK) $(tsearch_test_OBJECTS) $(tsearch_test_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(tsearch_test_LINK) $(tsearch_test_OBJECTS) $(tsearch_test_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -827,6 +1187,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/localtime_r.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/lstat.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memmove.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/memset_s.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/mkstemp.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/putenv.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/rcmd.Plo@am__quote@
@@ -850,6 +1211,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strsep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strsep_copy.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strtok_r.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strtoll.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strtoull.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strupr.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/swab.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/timegm.Plo@am__quote@
@@ -866,12 +1229,14 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/base64-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getaddrinfo-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getifaddrs-test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getxxyyy_test-getxxyyy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hex-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-base64.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-bswap.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-cloexec.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-concat.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-ct.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-detach.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-doxygen.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-dumpdata.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-environment.Plo@am__quote@
@@ -884,6 +1249,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-getarg.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-getnameinfo_verified.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-getprogname.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-getxxyyy.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-h_errno.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-hex.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-hostent_find_fqdn.Plo@am__quote@
@@ -891,6 +1257,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-k_getpwnam.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-k_getpwuid.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-mini_inetd.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-mkdir.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-net_read.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-net_write.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libroken_la-parse_bytes.Plo@am__quote@
@@ -932,492 +1299,528 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rkpty.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snprintf_test-snprintf-test.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strpftime_test-strpftime-test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-detach.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-mem.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test-readenv.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsearch_test-tsearch-test.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
libroken_la-base64.lo: base64.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-base64.lo -MD -MP -MF $(DEPDIR)/libroken_la-base64.Tpo -c -o libroken_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-base64.Tpo $(DEPDIR)/libroken_la-base64.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='base64.c' object='libroken_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-base64.lo -MD -MP -MF $(DEPDIR)/libroken_la-base64.Tpo -c -o libroken_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-base64.Tpo $(DEPDIR)/libroken_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libroken_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
libroken_la-bswap.lo: bswap.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-bswap.lo -MD -MP -MF $(DEPDIR)/libroken_la-bswap.Tpo -c -o libroken_la-bswap.lo `test -f 'bswap.c' || echo '$(srcdir)/'`bswap.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-bswap.Tpo $(DEPDIR)/libroken_la-bswap.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='bswap.c' object='libroken_la-bswap.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-bswap.lo -MD -MP -MF $(DEPDIR)/libroken_la-bswap.Tpo -c -o libroken_la-bswap.lo `test -f 'bswap.c' || echo '$(srcdir)/'`bswap.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-bswap.Tpo $(DEPDIR)/libroken_la-bswap.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bswap.c' object='libroken_la-bswap.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-bswap.lo `test -f 'bswap.c' || echo '$(srcdir)/'`bswap.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-bswap.lo `test -f 'bswap.c' || echo '$(srcdir)/'`bswap.c
libroken_la-concat.lo: concat.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-concat.lo -MD -MP -MF $(DEPDIR)/libroken_la-concat.Tpo -c -o libroken_la-concat.lo `test -f 'concat.c' || echo '$(srcdir)/'`concat.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-concat.Tpo $(DEPDIR)/libroken_la-concat.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='concat.c' object='libroken_la-concat.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-concat.lo -MD -MP -MF $(DEPDIR)/libroken_la-concat.Tpo -c -o libroken_la-concat.lo `test -f 'concat.c' || echo '$(srcdir)/'`concat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-concat.Tpo $(DEPDIR)/libroken_la-concat.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='concat.c' object='libroken_la-concat.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-concat.lo `test -f 'concat.c' || echo '$(srcdir)/'`concat.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-concat.lo `test -f 'concat.c' || echo '$(srcdir)/'`concat.c
libroken_la-cloexec.lo: cloexec.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-cloexec.lo -MD -MP -MF $(DEPDIR)/libroken_la-cloexec.Tpo -c -o libroken_la-cloexec.lo `test -f 'cloexec.c' || echo '$(srcdir)/'`cloexec.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-cloexec.Tpo $(DEPDIR)/libroken_la-cloexec.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='cloexec.c' object='libroken_la-cloexec.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-cloexec.lo -MD -MP -MF $(DEPDIR)/libroken_la-cloexec.Tpo -c -o libroken_la-cloexec.lo `test -f 'cloexec.c' || echo '$(srcdir)/'`cloexec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-cloexec.Tpo $(DEPDIR)/libroken_la-cloexec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cloexec.c' object='libroken_la-cloexec.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-cloexec.lo `test -f 'cloexec.c' || echo '$(srcdir)/'`cloexec.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-cloexec.lo `test -f 'cloexec.c' || echo '$(srcdir)/'`cloexec.c
libroken_la-ct.lo: ct.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-ct.lo -MD -MP -MF $(DEPDIR)/libroken_la-ct.Tpo -c -o libroken_la-ct.lo `test -f 'ct.c' || echo '$(srcdir)/'`ct.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-ct.Tpo $(DEPDIR)/libroken_la-ct.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ct.c' object='libroken_la-ct.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-ct.lo -MD -MP -MF $(DEPDIR)/libroken_la-ct.Tpo -c -o libroken_la-ct.lo `test -f 'ct.c' || echo '$(srcdir)/'`ct.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-ct.Tpo $(DEPDIR)/libroken_la-ct.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ct.c' object='libroken_la-ct.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-ct.lo `test -f 'ct.c' || echo '$(srcdir)/'`ct.c
+
+libroken_la-detach.lo: detach.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-detach.lo -MD -MP -MF $(DEPDIR)/libroken_la-detach.Tpo -c -o libroken_la-detach.lo `test -f 'detach.c' || echo '$(srcdir)/'`detach.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-detach.Tpo $(DEPDIR)/libroken_la-detach.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='detach.c' object='libroken_la-detach.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-ct.lo `test -f 'ct.c' || echo '$(srcdir)/'`ct.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-detach.lo `test -f 'detach.c' || echo '$(srcdir)/'`detach.c
libroken_la-doxygen.lo: doxygen.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-doxygen.lo -MD -MP -MF $(DEPDIR)/libroken_la-doxygen.Tpo -c -o libroken_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-doxygen.Tpo $(DEPDIR)/libroken_la-doxygen.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='doxygen.c' object='libroken_la-doxygen.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-doxygen.lo -MD -MP -MF $(DEPDIR)/libroken_la-doxygen.Tpo -c -o libroken_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-doxygen.Tpo $(DEPDIR)/libroken_la-doxygen.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='doxygen.c' object='libroken_la-doxygen.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-doxygen.lo `test -f 'doxygen.c' || echo '$(srcdir)/'`doxygen.c
libroken_la-dumpdata.lo: dumpdata.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-dumpdata.lo -MD -MP -MF $(DEPDIR)/libroken_la-dumpdata.Tpo -c -o libroken_la-dumpdata.lo `test -f 'dumpdata.c' || echo '$(srcdir)/'`dumpdata.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-dumpdata.Tpo $(DEPDIR)/libroken_la-dumpdata.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dumpdata.c' object='libroken_la-dumpdata.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-dumpdata.lo -MD -MP -MF $(DEPDIR)/libroken_la-dumpdata.Tpo -c -o libroken_la-dumpdata.lo `test -f 'dumpdata.c' || echo '$(srcdir)/'`dumpdata.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-dumpdata.Tpo $(DEPDIR)/libroken_la-dumpdata.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dumpdata.c' object='libroken_la-dumpdata.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-dumpdata.lo `test -f 'dumpdata.c' || echo '$(srcdir)/'`dumpdata.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-dumpdata.lo `test -f 'dumpdata.c' || echo '$(srcdir)/'`dumpdata.c
libroken_la-environment.lo: environment.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-environment.lo -MD -MP -MF $(DEPDIR)/libroken_la-environment.Tpo -c -o libroken_la-environment.lo `test -f 'environment.c' || echo '$(srcdir)/'`environment.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-environment.Tpo $(DEPDIR)/libroken_la-environment.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='environment.c' object='libroken_la-environment.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-environment.lo -MD -MP -MF $(DEPDIR)/libroken_la-environment.Tpo -c -o libroken_la-environment.lo `test -f 'environment.c' || echo '$(srcdir)/'`environment.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-environment.Tpo $(DEPDIR)/libroken_la-environment.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='environment.c' object='libroken_la-environment.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-environment.lo `test -f 'environment.c' || echo '$(srcdir)/'`environment.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-environment.lo `test -f 'environment.c' || echo '$(srcdir)/'`environment.c
libroken_la-eread.lo: eread.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-eread.lo -MD -MP -MF $(DEPDIR)/libroken_la-eread.Tpo -c -o libroken_la-eread.lo `test -f 'eread.c' || echo '$(srcdir)/'`eread.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-eread.Tpo $(DEPDIR)/libroken_la-eread.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='eread.c' object='libroken_la-eread.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-eread.lo -MD -MP -MF $(DEPDIR)/libroken_la-eread.Tpo -c -o libroken_la-eread.lo `test -f 'eread.c' || echo '$(srcdir)/'`eread.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-eread.Tpo $(DEPDIR)/libroken_la-eread.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eread.c' object='libroken_la-eread.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-eread.lo `test -f 'eread.c' || echo '$(srcdir)/'`eread.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-eread.lo `test -f 'eread.c' || echo '$(srcdir)/'`eread.c
libroken_la-esetenv.lo: esetenv.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-esetenv.lo -MD -MP -MF $(DEPDIR)/libroken_la-esetenv.Tpo -c -o libroken_la-esetenv.lo `test -f 'esetenv.c' || echo '$(srcdir)/'`esetenv.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-esetenv.Tpo $(DEPDIR)/libroken_la-esetenv.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='esetenv.c' object='libroken_la-esetenv.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-esetenv.lo -MD -MP -MF $(DEPDIR)/libroken_la-esetenv.Tpo -c -o libroken_la-esetenv.lo `test -f 'esetenv.c' || echo '$(srcdir)/'`esetenv.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-esetenv.Tpo $(DEPDIR)/libroken_la-esetenv.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='esetenv.c' object='libroken_la-esetenv.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-esetenv.lo `test -f 'esetenv.c' || echo '$(srcdir)/'`esetenv.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-esetenv.lo `test -f 'esetenv.c' || echo '$(srcdir)/'`esetenv.c
libroken_la-ewrite.lo: ewrite.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-ewrite.lo -MD -MP -MF $(DEPDIR)/libroken_la-ewrite.Tpo -c -o libroken_la-ewrite.lo `test -f 'ewrite.c' || echo '$(srcdir)/'`ewrite.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-ewrite.Tpo $(DEPDIR)/libroken_la-ewrite.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='ewrite.c' object='libroken_la-ewrite.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-ewrite.lo -MD -MP -MF $(DEPDIR)/libroken_la-ewrite.Tpo -c -o libroken_la-ewrite.lo `test -f 'ewrite.c' || echo '$(srcdir)/'`ewrite.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-ewrite.Tpo $(DEPDIR)/libroken_la-ewrite.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ewrite.c' object='libroken_la-ewrite.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-ewrite.lo `test -f 'ewrite.c' || echo '$(srcdir)/'`ewrite.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-ewrite.lo `test -f 'ewrite.c' || echo '$(srcdir)/'`ewrite.c
libroken_la-getaddrinfo_hostspec.lo: getaddrinfo_hostspec.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getaddrinfo_hostspec.lo -MD -MP -MF $(DEPDIR)/libroken_la-getaddrinfo_hostspec.Tpo -c -o libroken_la-getaddrinfo_hostspec.lo `test -f 'getaddrinfo_hostspec.c' || echo '$(srcdir)/'`getaddrinfo_hostspec.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-getaddrinfo_hostspec.Tpo $(DEPDIR)/libroken_la-getaddrinfo_hostspec.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='getaddrinfo_hostspec.c' object='libroken_la-getaddrinfo_hostspec.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getaddrinfo_hostspec.lo -MD -MP -MF $(DEPDIR)/libroken_la-getaddrinfo_hostspec.Tpo -c -o libroken_la-getaddrinfo_hostspec.lo `test -f 'getaddrinfo_hostspec.c' || echo '$(srcdir)/'`getaddrinfo_hostspec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-getaddrinfo_hostspec.Tpo $(DEPDIR)/libroken_la-getaddrinfo_hostspec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getaddrinfo_hostspec.c' object='libroken_la-getaddrinfo_hostspec.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getaddrinfo_hostspec.lo `test -f 'getaddrinfo_hostspec.c' || echo '$(srcdir)/'`getaddrinfo_hostspec.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getaddrinfo_hostspec.lo `test -f 'getaddrinfo_hostspec.c' || echo '$(srcdir)/'`getaddrinfo_hostspec.c
libroken_la-get_default_username.lo: get_default_username.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-get_default_username.lo -MD -MP -MF $(DEPDIR)/libroken_la-get_default_username.Tpo -c -o libroken_la-get_default_username.lo `test -f 'get_default_username.c' || echo '$(srcdir)/'`get_default_username.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-get_default_username.Tpo $(DEPDIR)/libroken_la-get_default_username.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_default_username.c' object='libroken_la-get_default_username.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-get_default_username.lo -MD -MP -MF $(DEPDIR)/libroken_la-get_default_username.Tpo -c -o libroken_la-get_default_username.lo `test -f 'get_default_username.c' || echo '$(srcdir)/'`get_default_username.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-get_default_username.Tpo $(DEPDIR)/libroken_la-get_default_username.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_default_username.c' object='libroken_la-get_default_username.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-get_default_username.lo `test -f 'get_default_username.c' || echo '$(srcdir)/'`get_default_username.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-get_default_username.lo `test -f 'get_default_username.c' || echo '$(srcdir)/'`get_default_username.c
libroken_la-get_window_size.lo: get_window_size.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-get_window_size.lo -MD -MP -MF $(DEPDIR)/libroken_la-get_window_size.Tpo -c -o libroken_la-get_window_size.lo `test -f 'get_window_size.c' || echo '$(srcdir)/'`get_window_size.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-get_window_size.Tpo $(DEPDIR)/libroken_la-get_window_size.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='get_window_size.c' object='libroken_la-get_window_size.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-get_window_size.lo -MD -MP -MF $(DEPDIR)/libroken_la-get_window_size.Tpo -c -o libroken_la-get_window_size.lo `test -f 'get_window_size.c' || echo '$(srcdir)/'`get_window_size.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-get_window_size.Tpo $(DEPDIR)/libroken_la-get_window_size.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='get_window_size.c' object='libroken_la-get_window_size.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-get_window_size.lo `test -f 'get_window_size.c' || echo '$(srcdir)/'`get_window_size.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-get_window_size.lo `test -f 'get_window_size.c' || echo '$(srcdir)/'`get_window_size.c
libroken_la-getarg.lo: getarg.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getarg.lo -MD -MP -MF $(DEPDIR)/libroken_la-getarg.Tpo -c -o libroken_la-getarg.lo `test -f 'getarg.c' || echo '$(srcdir)/'`getarg.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-getarg.Tpo $(DEPDIR)/libroken_la-getarg.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='getarg.c' object='libroken_la-getarg.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getarg.lo -MD -MP -MF $(DEPDIR)/libroken_la-getarg.Tpo -c -o libroken_la-getarg.lo `test -f 'getarg.c' || echo '$(srcdir)/'`getarg.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-getarg.Tpo $(DEPDIR)/libroken_la-getarg.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getarg.c' object='libroken_la-getarg.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getarg.lo `test -f 'getarg.c' || echo '$(srcdir)/'`getarg.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getarg.lo `test -f 'getarg.c' || echo '$(srcdir)/'`getarg.c
libroken_la-getnameinfo_verified.lo: getnameinfo_verified.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getnameinfo_verified.lo -MD -MP -MF $(DEPDIR)/libroken_la-getnameinfo_verified.Tpo -c -o libroken_la-getnameinfo_verified.lo `test -f 'getnameinfo_verified.c' || echo '$(srcdir)/'`getnameinfo_verified.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-getnameinfo_verified.Tpo $(DEPDIR)/libroken_la-getnameinfo_verified.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='getnameinfo_verified.c' object='libroken_la-getnameinfo_verified.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getnameinfo_verified.lo -MD -MP -MF $(DEPDIR)/libroken_la-getnameinfo_verified.Tpo -c -o libroken_la-getnameinfo_verified.lo `test -f 'getnameinfo_verified.c' || echo '$(srcdir)/'`getnameinfo_verified.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-getnameinfo_verified.Tpo $(DEPDIR)/libroken_la-getnameinfo_verified.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getnameinfo_verified.c' object='libroken_la-getnameinfo_verified.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getnameinfo_verified.lo `test -f 'getnameinfo_verified.c' || echo '$(srcdir)/'`getnameinfo_verified.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getnameinfo_verified.lo `test -f 'getnameinfo_verified.c' || echo '$(srcdir)/'`getnameinfo_verified.c
libroken_la-getprogname.lo: getprogname.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getprogname.lo -MD -MP -MF $(DEPDIR)/libroken_la-getprogname.Tpo -c -o libroken_la-getprogname.lo `test -f 'getprogname.c' || echo '$(srcdir)/'`getprogname.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-getprogname.Tpo $(DEPDIR)/libroken_la-getprogname.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='getprogname.c' object='libroken_la-getprogname.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getprogname.lo -MD -MP -MF $(DEPDIR)/libroken_la-getprogname.Tpo -c -o libroken_la-getprogname.lo `test -f 'getprogname.c' || echo '$(srcdir)/'`getprogname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-getprogname.Tpo $(DEPDIR)/libroken_la-getprogname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getprogname.c' object='libroken_la-getprogname.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getprogname.lo `test -f 'getprogname.c' || echo '$(srcdir)/'`getprogname.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getprogname.lo `test -f 'getprogname.c' || echo '$(srcdir)/'`getprogname.c
+
+libroken_la-getxxyyy.lo: getxxyyy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-getxxyyy.lo -MD -MP -MF $(DEPDIR)/libroken_la-getxxyyy.Tpo -c -o libroken_la-getxxyyy.lo `test -f 'getxxyyy.c' || echo '$(srcdir)/'`getxxyyy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-getxxyyy.Tpo $(DEPDIR)/libroken_la-getxxyyy.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getxxyyy.c' object='libroken_la-getxxyyy.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-getxxyyy.lo `test -f 'getxxyyy.c' || echo '$(srcdir)/'`getxxyyy.c
libroken_la-h_errno.lo: h_errno.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-h_errno.lo -MD -MP -MF $(DEPDIR)/libroken_la-h_errno.Tpo -c -o libroken_la-h_errno.lo `test -f 'h_errno.c' || echo '$(srcdir)/'`h_errno.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-h_errno.Tpo $(DEPDIR)/libroken_la-h_errno.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='h_errno.c' object='libroken_la-h_errno.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-h_errno.lo -MD -MP -MF $(DEPDIR)/libroken_la-h_errno.Tpo -c -o libroken_la-h_errno.lo `test -f 'h_errno.c' || echo '$(srcdir)/'`h_errno.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-h_errno.Tpo $(DEPDIR)/libroken_la-h_errno.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='h_errno.c' object='libroken_la-h_errno.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-h_errno.lo `test -f 'h_errno.c' || echo '$(srcdir)/'`h_errno.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-h_errno.lo `test -f 'h_errno.c' || echo '$(srcdir)/'`h_errno.c
libroken_la-hex.lo: hex.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-hex.lo -MD -MP -MF $(DEPDIR)/libroken_la-hex.Tpo -c -o libroken_la-hex.lo `test -f 'hex.c' || echo '$(srcdir)/'`hex.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-hex.Tpo $(DEPDIR)/libroken_la-hex.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hex.c' object='libroken_la-hex.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-hex.lo -MD -MP -MF $(DEPDIR)/libroken_la-hex.Tpo -c -o libroken_la-hex.lo `test -f 'hex.c' || echo '$(srcdir)/'`hex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-hex.Tpo $(DEPDIR)/libroken_la-hex.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hex.c' object='libroken_la-hex.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-hex.lo `test -f 'hex.c' || echo '$(srcdir)/'`hex.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-hex.lo `test -f 'hex.c' || echo '$(srcdir)/'`hex.c
libroken_la-hostent_find_fqdn.lo: hostent_find_fqdn.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-hostent_find_fqdn.lo -MD -MP -MF $(DEPDIR)/libroken_la-hostent_find_fqdn.Tpo -c -o libroken_la-hostent_find_fqdn.lo `test -f 'hostent_find_fqdn.c' || echo '$(srcdir)/'`hostent_find_fqdn.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-hostent_find_fqdn.Tpo $(DEPDIR)/libroken_la-hostent_find_fqdn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hostent_find_fqdn.c' object='libroken_la-hostent_find_fqdn.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-hostent_find_fqdn.lo -MD -MP -MF $(DEPDIR)/libroken_la-hostent_find_fqdn.Tpo -c -o libroken_la-hostent_find_fqdn.lo `test -f 'hostent_find_fqdn.c' || echo '$(srcdir)/'`hostent_find_fqdn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-hostent_find_fqdn.Tpo $(DEPDIR)/libroken_la-hostent_find_fqdn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostent_find_fqdn.c' object='libroken_la-hostent_find_fqdn.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-hostent_find_fqdn.lo `test -f 'hostent_find_fqdn.c' || echo '$(srcdir)/'`hostent_find_fqdn.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-hostent_find_fqdn.lo `test -f 'hostent_find_fqdn.c' || echo '$(srcdir)/'`hostent_find_fqdn.c
libroken_la-issuid.lo: issuid.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-issuid.lo -MD -MP -MF $(DEPDIR)/libroken_la-issuid.Tpo -c -o libroken_la-issuid.lo `test -f 'issuid.c' || echo '$(srcdir)/'`issuid.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-issuid.Tpo $(DEPDIR)/libroken_la-issuid.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='issuid.c' object='libroken_la-issuid.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-issuid.lo -MD -MP -MF $(DEPDIR)/libroken_la-issuid.Tpo -c -o libroken_la-issuid.lo `test -f 'issuid.c' || echo '$(srcdir)/'`issuid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-issuid.Tpo $(DEPDIR)/libroken_la-issuid.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='issuid.c' object='libroken_la-issuid.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-issuid.lo `test -f 'issuid.c' || echo '$(srcdir)/'`issuid.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-issuid.lo `test -f 'issuid.c' || echo '$(srcdir)/'`issuid.c
libroken_la-k_getpwnam.lo: k_getpwnam.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-k_getpwnam.lo -MD -MP -MF $(DEPDIR)/libroken_la-k_getpwnam.Tpo -c -o libroken_la-k_getpwnam.lo `test -f 'k_getpwnam.c' || echo '$(srcdir)/'`k_getpwnam.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-k_getpwnam.Tpo $(DEPDIR)/libroken_la-k_getpwnam.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='k_getpwnam.c' object='libroken_la-k_getpwnam.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-k_getpwnam.lo -MD -MP -MF $(DEPDIR)/libroken_la-k_getpwnam.Tpo -c -o libroken_la-k_getpwnam.lo `test -f 'k_getpwnam.c' || echo '$(srcdir)/'`k_getpwnam.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-k_getpwnam.Tpo $(DEPDIR)/libroken_la-k_getpwnam.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='k_getpwnam.c' object='libroken_la-k_getpwnam.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-k_getpwnam.lo `test -f 'k_getpwnam.c' || echo '$(srcdir)/'`k_getpwnam.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-k_getpwnam.lo `test -f 'k_getpwnam.c' || echo '$(srcdir)/'`k_getpwnam.c
libroken_la-k_getpwuid.lo: k_getpwuid.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-k_getpwuid.lo -MD -MP -MF $(DEPDIR)/libroken_la-k_getpwuid.Tpo -c -o libroken_la-k_getpwuid.lo `test -f 'k_getpwuid.c' || echo '$(srcdir)/'`k_getpwuid.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-k_getpwuid.Tpo $(DEPDIR)/libroken_la-k_getpwuid.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='k_getpwuid.c' object='libroken_la-k_getpwuid.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-k_getpwuid.lo -MD -MP -MF $(DEPDIR)/libroken_la-k_getpwuid.Tpo -c -o libroken_la-k_getpwuid.lo `test -f 'k_getpwuid.c' || echo '$(srcdir)/'`k_getpwuid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-k_getpwuid.Tpo $(DEPDIR)/libroken_la-k_getpwuid.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='k_getpwuid.c' object='libroken_la-k_getpwuid.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-k_getpwuid.lo `test -f 'k_getpwuid.c' || echo '$(srcdir)/'`k_getpwuid.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-k_getpwuid.lo `test -f 'k_getpwuid.c' || echo '$(srcdir)/'`k_getpwuid.c
libroken_la-mini_inetd.lo: mini_inetd.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-mini_inetd.lo -MD -MP -MF $(DEPDIR)/libroken_la-mini_inetd.Tpo -c -o libroken_la-mini_inetd.lo `test -f 'mini_inetd.c' || echo '$(srcdir)/'`mini_inetd.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-mini_inetd.Tpo $(DEPDIR)/libroken_la-mini_inetd.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mini_inetd.c' object='libroken_la-mini_inetd.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-mini_inetd.lo -MD -MP -MF $(DEPDIR)/libroken_la-mini_inetd.Tpo -c -o libroken_la-mini_inetd.lo `test -f 'mini_inetd.c' || echo '$(srcdir)/'`mini_inetd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-mini_inetd.Tpo $(DEPDIR)/libroken_la-mini_inetd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mini_inetd.c' object='libroken_la-mini_inetd.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-mini_inetd.lo `test -f 'mini_inetd.c' || echo '$(srcdir)/'`mini_inetd.c
+
+libroken_la-mkdir.lo: mkdir.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-mkdir.lo -MD -MP -MF $(DEPDIR)/libroken_la-mkdir.Tpo -c -o libroken_la-mkdir.lo `test -f 'mkdir.c' || echo '$(srcdir)/'`mkdir.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-mkdir.Tpo $(DEPDIR)/libroken_la-mkdir.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mkdir.c' object='libroken_la-mkdir.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-mini_inetd.lo `test -f 'mini_inetd.c' || echo '$(srcdir)/'`mini_inetd.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-mkdir.lo `test -f 'mkdir.c' || echo '$(srcdir)/'`mkdir.c
libroken_la-net_read.lo: net_read.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-net_read.lo -MD -MP -MF $(DEPDIR)/libroken_la-net_read.Tpo -c -o libroken_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-net_read.Tpo $(DEPDIR)/libroken_la-net_read.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='net_read.c' object='libroken_la-net_read.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-net_read.lo -MD -MP -MF $(DEPDIR)/libroken_la-net_read.Tpo -c -o libroken_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-net_read.Tpo $(DEPDIR)/libroken_la-net_read.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net_read.c' object='libroken_la-net_read.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-net_read.lo `test -f 'net_read.c' || echo '$(srcdir)/'`net_read.c
libroken_la-net_write.lo: net_write.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-net_write.lo -MD -MP -MF $(DEPDIR)/libroken_la-net_write.Tpo -c -o libroken_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-net_write.Tpo $(DEPDIR)/libroken_la-net_write.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='net_write.c' object='libroken_la-net_write.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-net_write.lo -MD -MP -MF $(DEPDIR)/libroken_la-net_write.Tpo -c -o libroken_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-net_write.Tpo $(DEPDIR)/libroken_la-net_write.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='net_write.c' object='libroken_la-net_write.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-net_write.lo `test -f 'net_write.c' || echo '$(srcdir)/'`net_write.c
libroken_la-parse_bytes.lo: parse_bytes.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-parse_bytes.lo -MD -MP -MF $(DEPDIR)/libroken_la-parse_bytes.Tpo -c -o libroken_la-parse_bytes.lo `test -f 'parse_bytes.c' || echo '$(srcdir)/'`parse_bytes.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-parse_bytes.Tpo $(DEPDIR)/libroken_la-parse_bytes.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='parse_bytes.c' object='libroken_la-parse_bytes.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-parse_bytes.lo -MD -MP -MF $(DEPDIR)/libroken_la-parse_bytes.Tpo -c -o libroken_la-parse_bytes.lo `test -f 'parse_bytes.c' || echo '$(srcdir)/'`parse_bytes.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-parse_bytes.Tpo $(DEPDIR)/libroken_la-parse_bytes.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse_bytes.c' object='libroken_la-parse_bytes.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-parse_bytes.lo `test -f 'parse_bytes.c' || echo '$(srcdir)/'`parse_bytes.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-parse_bytes.lo `test -f 'parse_bytes.c' || echo '$(srcdir)/'`parse_bytes.c
libroken_la-parse_time.lo: parse_time.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-parse_time.lo -MD -MP -MF $(DEPDIR)/libroken_la-parse_time.Tpo -c -o libroken_la-parse_time.lo `test -f 'parse_time.c' || echo '$(srcdir)/'`parse_time.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-parse_time.Tpo $(DEPDIR)/libroken_la-parse_time.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='parse_time.c' object='libroken_la-parse_time.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-parse_time.lo -MD -MP -MF $(DEPDIR)/libroken_la-parse_time.Tpo -c -o libroken_la-parse_time.lo `test -f 'parse_time.c' || echo '$(srcdir)/'`parse_time.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-parse_time.Tpo $(DEPDIR)/libroken_la-parse_time.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse_time.c' object='libroken_la-parse_time.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-parse_time.lo `test -f 'parse_time.c' || echo '$(srcdir)/'`parse_time.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-parse_time.lo `test -f 'parse_time.c' || echo '$(srcdir)/'`parse_time.c
libroken_la-parse_units.lo: parse_units.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-parse_units.lo -MD -MP -MF $(DEPDIR)/libroken_la-parse_units.Tpo -c -o libroken_la-parse_units.lo `test -f 'parse_units.c' || echo '$(srcdir)/'`parse_units.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-parse_units.Tpo $(DEPDIR)/libroken_la-parse_units.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='parse_units.c' object='libroken_la-parse_units.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-parse_units.lo -MD -MP -MF $(DEPDIR)/libroken_la-parse_units.Tpo -c -o libroken_la-parse_units.lo `test -f 'parse_units.c' || echo '$(srcdir)/'`parse_units.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-parse_units.Tpo $(DEPDIR)/libroken_la-parse_units.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse_units.c' object='libroken_la-parse_units.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-parse_units.lo `test -f 'parse_units.c' || echo '$(srcdir)/'`parse_units.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-parse_units.lo `test -f 'parse_units.c' || echo '$(srcdir)/'`parse_units.c
libroken_la-qsort.lo: qsort.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-qsort.lo -MD -MP -MF $(DEPDIR)/libroken_la-qsort.Tpo -c -o libroken_la-qsort.lo `test -f 'qsort.c' || echo '$(srcdir)/'`qsort.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-qsort.Tpo $(DEPDIR)/libroken_la-qsort.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='qsort.c' object='libroken_la-qsort.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-qsort.lo -MD -MP -MF $(DEPDIR)/libroken_la-qsort.Tpo -c -o libroken_la-qsort.lo `test -f 'qsort.c' || echo '$(srcdir)/'`qsort.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-qsort.Tpo $(DEPDIR)/libroken_la-qsort.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='qsort.c' object='libroken_la-qsort.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-qsort.lo `test -f 'qsort.c' || echo '$(srcdir)/'`qsort.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-qsort.lo `test -f 'qsort.c' || echo '$(srcdir)/'`qsort.c
libroken_la-rand.lo: rand.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-rand.lo -MD -MP -MF $(DEPDIR)/libroken_la-rand.Tpo -c -o libroken_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-rand.Tpo $(DEPDIR)/libroken_la-rand.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rand.c' object='libroken_la-rand.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-rand.lo -MD -MP -MF $(DEPDIR)/libroken_la-rand.Tpo -c -o libroken_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-rand.Tpo $(DEPDIR)/libroken_la-rand.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rand.c' object='libroken_la-rand.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-rand.lo `test -f 'rand.c' || echo '$(srcdir)/'`rand.c
libroken_la-realloc.lo: realloc.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-realloc.lo -MD -MP -MF $(DEPDIR)/libroken_la-realloc.Tpo -c -o libroken_la-realloc.lo `test -f 'realloc.c' || echo '$(srcdir)/'`realloc.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-realloc.Tpo $(DEPDIR)/libroken_la-realloc.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='realloc.c' object='libroken_la-realloc.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-realloc.lo -MD -MP -MF $(DEPDIR)/libroken_la-realloc.Tpo -c -o libroken_la-realloc.lo `test -f 'realloc.c' || echo '$(srcdir)/'`realloc.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-realloc.Tpo $(DEPDIR)/libroken_la-realloc.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='realloc.c' object='libroken_la-realloc.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-realloc.lo `test -f 'realloc.c' || echo '$(srcdir)/'`realloc.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-realloc.lo `test -f 'realloc.c' || echo '$(srcdir)/'`realloc.c
libroken_la-resolve.lo: resolve.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-resolve.lo -MD -MP -MF $(DEPDIR)/libroken_la-resolve.Tpo -c -o libroken_la-resolve.lo `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-resolve.Tpo $(DEPDIR)/libroken_la-resolve.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='resolve.c' object='libroken_la-resolve.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-resolve.lo -MD -MP -MF $(DEPDIR)/libroken_la-resolve.Tpo -c -o libroken_la-resolve.lo `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-resolve.Tpo $(DEPDIR)/libroken_la-resolve.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolve.c' object='libroken_la-resolve.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-resolve.lo `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-resolve.lo `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
libroken_la-roken_gethostby.lo: roken_gethostby.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-roken_gethostby.lo -MD -MP -MF $(DEPDIR)/libroken_la-roken_gethostby.Tpo -c -o libroken_la-roken_gethostby.lo `test -f 'roken_gethostby.c' || echo '$(srcdir)/'`roken_gethostby.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-roken_gethostby.Tpo $(DEPDIR)/libroken_la-roken_gethostby.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='roken_gethostby.c' object='libroken_la-roken_gethostby.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-roken_gethostby.lo -MD -MP -MF $(DEPDIR)/libroken_la-roken_gethostby.Tpo -c -o libroken_la-roken_gethostby.lo `test -f 'roken_gethostby.c' || echo '$(srcdir)/'`roken_gethostby.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-roken_gethostby.Tpo $(DEPDIR)/libroken_la-roken_gethostby.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='roken_gethostby.c' object='libroken_la-roken_gethostby.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-roken_gethostby.lo `test -f 'roken_gethostby.c' || echo '$(srcdir)/'`roken_gethostby.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-roken_gethostby.lo `test -f 'roken_gethostby.c' || echo '$(srcdir)/'`roken_gethostby.c
libroken_la-rtbl.lo: rtbl.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-rtbl.lo -MD -MP -MF $(DEPDIR)/libroken_la-rtbl.Tpo -c -o libroken_la-rtbl.lo `test -f 'rtbl.c' || echo '$(srcdir)/'`rtbl.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-rtbl.Tpo $(DEPDIR)/libroken_la-rtbl.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='rtbl.c' object='libroken_la-rtbl.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-rtbl.lo -MD -MP -MF $(DEPDIR)/libroken_la-rtbl.Tpo -c -o libroken_la-rtbl.lo `test -f 'rtbl.c' || echo '$(srcdir)/'`rtbl.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-rtbl.Tpo $(DEPDIR)/libroken_la-rtbl.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='rtbl.c' object='libroken_la-rtbl.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-rtbl.lo `test -f 'rtbl.c' || echo '$(srcdir)/'`rtbl.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-rtbl.lo `test -f 'rtbl.c' || echo '$(srcdir)/'`rtbl.c
libroken_la-setprogname.lo: setprogname.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-setprogname.lo -MD -MP -MF $(DEPDIR)/libroken_la-setprogname.Tpo -c -o libroken_la-setprogname.lo `test -f 'setprogname.c' || echo '$(srcdir)/'`setprogname.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-setprogname.Tpo $(DEPDIR)/libroken_la-setprogname.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='setprogname.c' object='libroken_la-setprogname.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-setprogname.lo -MD -MP -MF $(DEPDIR)/libroken_la-setprogname.Tpo -c -o libroken_la-setprogname.lo `test -f 'setprogname.c' || echo '$(srcdir)/'`setprogname.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-setprogname.Tpo $(DEPDIR)/libroken_la-setprogname.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='setprogname.c' object='libroken_la-setprogname.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-setprogname.lo `test -f 'setprogname.c' || echo '$(srcdir)/'`setprogname.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-setprogname.lo `test -f 'setprogname.c' || echo '$(srcdir)/'`setprogname.c
libroken_la-signal.lo: signal.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-signal.lo -MD -MP -MF $(DEPDIR)/libroken_la-signal.Tpo -c -o libroken_la-signal.lo `test -f 'signal.c' || echo '$(srcdir)/'`signal.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-signal.Tpo $(DEPDIR)/libroken_la-signal.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='signal.c' object='libroken_la-signal.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-signal.lo -MD -MP -MF $(DEPDIR)/libroken_la-signal.Tpo -c -o libroken_la-signal.lo `test -f 'signal.c' || echo '$(srcdir)/'`signal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-signal.Tpo $(DEPDIR)/libroken_la-signal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='signal.c' object='libroken_la-signal.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-signal.lo `test -f 'signal.c' || echo '$(srcdir)/'`signal.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-signal.lo `test -f 'signal.c' || echo '$(srcdir)/'`signal.c
libroken_la-simple_exec.lo: simple_exec.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-simple_exec.lo -MD -MP -MF $(DEPDIR)/libroken_la-simple_exec.Tpo -c -o libroken_la-simple_exec.lo `test -f 'simple_exec.c' || echo '$(srcdir)/'`simple_exec.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-simple_exec.Tpo $(DEPDIR)/libroken_la-simple_exec.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='simple_exec.c' object='libroken_la-simple_exec.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-simple_exec.lo -MD -MP -MF $(DEPDIR)/libroken_la-simple_exec.Tpo -c -o libroken_la-simple_exec.lo `test -f 'simple_exec.c' || echo '$(srcdir)/'`simple_exec.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-simple_exec.Tpo $(DEPDIR)/libroken_la-simple_exec.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='simple_exec.c' object='libroken_la-simple_exec.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-simple_exec.lo `test -f 'simple_exec.c' || echo '$(srcdir)/'`simple_exec.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-simple_exec.lo `test -f 'simple_exec.c' || echo '$(srcdir)/'`simple_exec.c
libroken_la-snprintf.lo: snprintf.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-snprintf.lo -MD -MP -MF $(DEPDIR)/libroken_la-snprintf.Tpo -c -o libroken_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-snprintf.Tpo $(DEPDIR)/libroken_la-snprintf.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='snprintf.c' object='libroken_la-snprintf.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-snprintf.lo -MD -MP -MF $(DEPDIR)/libroken_la-snprintf.Tpo -c -o libroken_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-snprintf.Tpo $(DEPDIR)/libroken_la-snprintf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snprintf.c' object='libroken_la-snprintf.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
libroken_la-socket.lo: socket.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-socket.lo -MD -MP -MF $(DEPDIR)/libroken_la-socket.Tpo -c -o libroken_la-socket.lo `test -f 'socket.c' || echo '$(srcdir)/'`socket.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-socket.Tpo $(DEPDIR)/libroken_la-socket.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='socket.c' object='libroken_la-socket.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-socket.lo -MD -MP -MF $(DEPDIR)/libroken_la-socket.Tpo -c -o libroken_la-socket.lo `test -f 'socket.c' || echo '$(srcdir)/'`socket.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-socket.Tpo $(DEPDIR)/libroken_la-socket.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socket.c' object='libroken_la-socket.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-socket.lo `test -f 'socket.c' || echo '$(srcdir)/'`socket.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-socket.lo `test -f 'socket.c' || echo '$(srcdir)/'`socket.c
libroken_la-strcollect.lo: strcollect.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-strcollect.lo -MD -MP -MF $(DEPDIR)/libroken_la-strcollect.Tpo -c -o libroken_la-strcollect.lo `test -f 'strcollect.c' || echo '$(srcdir)/'`strcollect.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-strcollect.Tpo $(DEPDIR)/libroken_la-strcollect.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strcollect.c' object='libroken_la-strcollect.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-strcollect.lo -MD -MP -MF $(DEPDIR)/libroken_la-strcollect.Tpo -c -o libroken_la-strcollect.lo `test -f 'strcollect.c' || echo '$(srcdir)/'`strcollect.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-strcollect.Tpo $(DEPDIR)/libroken_la-strcollect.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strcollect.c' object='libroken_la-strcollect.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-strcollect.lo `test -f 'strcollect.c' || echo '$(srcdir)/'`strcollect.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-strcollect.lo `test -f 'strcollect.c' || echo '$(srcdir)/'`strcollect.c
libroken_la-strerror_r.lo: strerror_r.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-strerror_r.lo -MD -MP -MF $(DEPDIR)/libroken_la-strerror_r.Tpo -c -o libroken_la-strerror_r.lo `test -f 'strerror_r.c' || echo '$(srcdir)/'`strerror_r.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-strerror_r.Tpo $(DEPDIR)/libroken_la-strerror_r.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strerror_r.c' object='libroken_la-strerror_r.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-strerror_r.lo -MD -MP -MF $(DEPDIR)/libroken_la-strerror_r.Tpo -c -o libroken_la-strerror_r.lo `test -f 'strerror_r.c' || echo '$(srcdir)/'`strerror_r.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-strerror_r.Tpo $(DEPDIR)/libroken_la-strerror_r.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strerror_r.c' object='libroken_la-strerror_r.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-strerror_r.lo `test -f 'strerror_r.c' || echo '$(srcdir)/'`strerror_r.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-strerror_r.lo `test -f 'strerror_r.c' || echo '$(srcdir)/'`strerror_r.c
libroken_la-strpool.lo: strpool.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-strpool.lo -MD -MP -MF $(DEPDIR)/libroken_la-strpool.Tpo -c -o libroken_la-strpool.lo `test -f 'strpool.c' || echo '$(srcdir)/'`strpool.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-strpool.Tpo $(DEPDIR)/libroken_la-strpool.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strpool.c' object='libroken_la-strpool.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-strpool.lo -MD -MP -MF $(DEPDIR)/libroken_la-strpool.Tpo -c -o libroken_la-strpool.lo `test -f 'strpool.c' || echo '$(srcdir)/'`strpool.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-strpool.Tpo $(DEPDIR)/libroken_la-strpool.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strpool.c' object='libroken_la-strpool.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-strpool.lo `test -f 'strpool.c' || echo '$(srcdir)/'`strpool.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-strpool.lo `test -f 'strpool.c' || echo '$(srcdir)/'`strpool.c
libroken_la-timeval.lo: timeval.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-timeval.lo -MD -MP -MF $(DEPDIR)/libroken_la-timeval.Tpo -c -o libroken_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-timeval.Tpo $(DEPDIR)/libroken_la-timeval.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='timeval.c' object='libroken_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-timeval.lo -MD -MP -MF $(DEPDIR)/libroken_la-timeval.Tpo -c -o libroken_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-timeval.Tpo $(DEPDIR)/libroken_la-timeval.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libroken_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
libroken_la-tm2time.lo: tm2time.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-tm2time.lo -MD -MP -MF $(DEPDIR)/libroken_la-tm2time.Tpo -c -o libroken_la-tm2time.lo `test -f 'tm2time.c' || echo '$(srcdir)/'`tm2time.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-tm2time.Tpo $(DEPDIR)/libroken_la-tm2time.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tm2time.c' object='libroken_la-tm2time.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-tm2time.lo -MD -MP -MF $(DEPDIR)/libroken_la-tm2time.Tpo -c -o libroken_la-tm2time.lo `test -f 'tm2time.c' || echo '$(srcdir)/'`tm2time.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-tm2time.Tpo $(DEPDIR)/libroken_la-tm2time.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tm2time.c' object='libroken_la-tm2time.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-tm2time.lo `test -f 'tm2time.c' || echo '$(srcdir)/'`tm2time.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-tm2time.lo `test -f 'tm2time.c' || echo '$(srcdir)/'`tm2time.c
libroken_la-unvis.lo: unvis.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-unvis.lo -MD -MP -MF $(DEPDIR)/libroken_la-unvis.Tpo -c -o libroken_la-unvis.lo `test -f 'unvis.c' || echo '$(srcdir)/'`unvis.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-unvis.Tpo $(DEPDIR)/libroken_la-unvis.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='unvis.c' object='libroken_la-unvis.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-unvis.lo -MD -MP -MF $(DEPDIR)/libroken_la-unvis.Tpo -c -o libroken_la-unvis.lo `test -f 'unvis.c' || echo '$(srcdir)/'`unvis.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-unvis.Tpo $(DEPDIR)/libroken_la-unvis.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='unvis.c' object='libroken_la-unvis.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-unvis.lo `test -f 'unvis.c' || echo '$(srcdir)/'`unvis.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-unvis.lo `test -f 'unvis.c' || echo '$(srcdir)/'`unvis.c
libroken_la-verify.lo: verify.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-verify.lo -MD -MP -MF $(DEPDIR)/libroken_la-verify.Tpo -c -o libroken_la-verify.lo `test -f 'verify.c' || echo '$(srcdir)/'`verify.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-verify.Tpo $(DEPDIR)/libroken_la-verify.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='verify.c' object='libroken_la-verify.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-verify.lo -MD -MP -MF $(DEPDIR)/libroken_la-verify.Tpo -c -o libroken_la-verify.lo `test -f 'verify.c' || echo '$(srcdir)/'`verify.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-verify.Tpo $(DEPDIR)/libroken_la-verify.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='verify.c' object='libroken_la-verify.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-verify.lo `test -f 'verify.c' || echo '$(srcdir)/'`verify.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-verify.lo `test -f 'verify.c' || echo '$(srcdir)/'`verify.c
libroken_la-vis.lo: vis.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-vis.lo -MD -MP -MF $(DEPDIR)/libroken_la-vis.Tpo -c -o libroken_la-vis.lo `test -f 'vis.c' || echo '$(srcdir)/'`vis.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-vis.Tpo $(DEPDIR)/libroken_la-vis.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='vis.c' object='libroken_la-vis.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-vis.lo -MD -MP -MF $(DEPDIR)/libroken_la-vis.Tpo -c -o libroken_la-vis.lo `test -f 'vis.c' || echo '$(srcdir)/'`vis.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-vis.Tpo $(DEPDIR)/libroken_la-vis.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vis.c' object='libroken_la-vis.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-vis.lo `test -f 'vis.c' || echo '$(srcdir)/'`vis.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-vis.lo `test -f 'vis.c' || echo '$(srcdir)/'`vis.c
libroken_la-warnerr.lo: warnerr.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-warnerr.lo -MD -MP -MF $(DEPDIR)/libroken_la-warnerr.Tpo -c -o libroken_la-warnerr.lo `test -f 'warnerr.c' || echo '$(srcdir)/'`warnerr.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-warnerr.Tpo $(DEPDIR)/libroken_la-warnerr.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='warnerr.c' object='libroken_la-warnerr.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-warnerr.lo -MD -MP -MF $(DEPDIR)/libroken_la-warnerr.Tpo -c -o libroken_la-warnerr.lo `test -f 'warnerr.c' || echo '$(srcdir)/'`warnerr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-warnerr.Tpo $(DEPDIR)/libroken_la-warnerr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnerr.c' object='libroken_la-warnerr.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-warnerr.lo `test -f 'warnerr.c' || echo '$(srcdir)/'`warnerr.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-warnerr.lo `test -f 'warnerr.c' || echo '$(srcdir)/'`warnerr.c
libroken_la-write_pid.lo: write_pid.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-write_pid.lo -MD -MP -MF $(DEPDIR)/libroken_la-write_pid.Tpo -c -o libroken_la-write_pid.lo `test -f 'write_pid.c' || echo '$(srcdir)/'`write_pid.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-write_pid.Tpo $(DEPDIR)/libroken_la-write_pid.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='write_pid.c' object='libroken_la-write_pid.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-write_pid.lo -MD -MP -MF $(DEPDIR)/libroken_la-write_pid.Tpo -c -o libroken_la-write_pid.lo `test -f 'write_pid.c' || echo '$(srcdir)/'`write_pid.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-write_pid.Tpo $(DEPDIR)/libroken_la-write_pid.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='write_pid.c' object='libroken_la-write_pid.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-write_pid.lo `test -f 'write_pid.c' || echo '$(srcdir)/'`write_pid.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-write_pid.lo `test -f 'write_pid.c' || echo '$(srcdir)/'`write_pid.c
libroken_la-xfree.lo: xfree.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-xfree.lo -MD -MP -MF $(DEPDIR)/libroken_la-xfree.Tpo -c -o libroken_la-xfree.lo `test -f 'xfree.c' || echo '$(srcdir)/'`xfree.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-xfree.Tpo $(DEPDIR)/libroken_la-xfree.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='xfree.c' object='libroken_la-xfree.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-xfree.lo -MD -MP -MF $(DEPDIR)/libroken_la-xfree.Tpo -c -o libroken_la-xfree.lo `test -f 'xfree.c' || echo '$(srcdir)/'`xfree.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-xfree.Tpo $(DEPDIR)/libroken_la-xfree.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xfree.c' object='libroken_la-xfree.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-xfree.lo `test -f 'xfree.c' || echo '$(srcdir)/'`xfree.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-xfree.lo `test -f 'xfree.c' || echo '$(srcdir)/'`xfree.c
libroken_la-socket_wrapper.lo: socket_wrapper.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-socket_wrapper.lo -MD -MP -MF $(DEPDIR)/libroken_la-socket_wrapper.Tpo -c -o libroken_la-socket_wrapper.lo `test -f 'socket_wrapper.c' || echo '$(srcdir)/'`socket_wrapper.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libroken_la-socket_wrapper.Tpo $(DEPDIR)/libroken_la-socket_wrapper.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='socket_wrapper.c' object='libroken_la-socket_wrapper.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libroken_la-socket_wrapper.lo -MD -MP -MF $(DEPDIR)/libroken_la-socket_wrapper.Tpo -c -o libroken_la-socket_wrapper.lo `test -f 'socket_wrapper.c' || echo '$(srcdir)/'`socket_wrapper.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libroken_la-socket_wrapper.Tpo $(DEPDIR)/libroken_la-socket_wrapper.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socket_wrapper.c' object='libroken_la-socket_wrapper.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-socket_wrapper.lo `test -f 'socket_wrapper.c' || echo '$(srcdir)/'`socket_wrapper.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libroken_la_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libroken_la-socket_wrapper.lo `test -f 'socket_wrapper.c' || echo '$(srcdir)/'`socket_wrapper.c
libtest_la-strftime.lo: strftime.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-strftime.lo -MD -MP -MF $(DEPDIR)/libtest_la-strftime.Tpo -c -o libtest_la-strftime.lo `test -f 'strftime.c' || echo '$(srcdir)/'`strftime.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_la-strftime.Tpo $(DEPDIR)/libtest_la-strftime.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strftime.c' object='libtest_la-strftime.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-strftime.lo -MD -MP -MF $(DEPDIR)/libtest_la-strftime.Tpo -c -o libtest_la-strftime.lo `test -f 'strftime.c' || echo '$(srcdir)/'`strftime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtest_la-strftime.Tpo $(DEPDIR)/libtest_la-strftime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strftime.c' object='libtest_la-strftime.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-strftime.lo `test -f 'strftime.c' || echo '$(srcdir)/'`strftime.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-strftime.lo `test -f 'strftime.c' || echo '$(srcdir)/'`strftime.c
libtest_la-strptime.lo: strptime.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-strptime.lo -MD -MP -MF $(DEPDIR)/libtest_la-strptime.Tpo -c -o libtest_la-strptime.lo `test -f 'strptime.c' || echo '$(srcdir)/'`strptime.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_la-strptime.Tpo $(DEPDIR)/libtest_la-strptime.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strptime.c' object='libtest_la-strptime.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-strptime.lo -MD -MP -MF $(DEPDIR)/libtest_la-strptime.Tpo -c -o libtest_la-strptime.lo `test -f 'strptime.c' || echo '$(srcdir)/'`strptime.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtest_la-strptime.Tpo $(DEPDIR)/libtest_la-strptime.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strptime.c' object='libtest_la-strptime.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-strptime.lo `test -f 'strptime.c' || echo '$(srcdir)/'`strptime.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-strptime.lo `test -f 'strptime.c' || echo '$(srcdir)/'`strptime.c
libtest_la-snprintf.lo: snprintf.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-snprintf.lo -MD -MP -MF $(DEPDIR)/libtest_la-snprintf.Tpo -c -o libtest_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_la-snprintf.Tpo $(DEPDIR)/libtest_la-snprintf.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='snprintf.c' object='libtest_la-snprintf.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-snprintf.lo -MD -MP -MF $(DEPDIR)/libtest_la-snprintf.Tpo -c -o libtest_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtest_la-snprintf.Tpo $(DEPDIR)/libtest_la-snprintf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snprintf.c' object='libtest_la-snprintf.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-snprintf.lo `test -f 'snprintf.c' || echo '$(srcdir)/'`snprintf.c
libtest_la-tsearch.lo: tsearch.c
-@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-tsearch.lo -MD -MP -MF $(DEPDIR)/libtest_la-tsearch.Tpo -c -o libtest_la-tsearch.lo `test -f 'tsearch.c' || echo '$(srcdir)/'`tsearch.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libtest_la-tsearch.Tpo $(DEPDIR)/libtest_la-tsearch.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tsearch.c' object='libtest_la-tsearch.lo' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -MT libtest_la-tsearch.lo -MD -MP -MF $(DEPDIR)/libtest_la-tsearch.Tpo -c -o libtest_la-tsearch.lo `test -f 'tsearch.c' || echo '$(srcdir)/'`tsearch.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libtest_la-tsearch.Tpo $(DEPDIR)/libtest_la-tsearch.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tsearch.c' object='libtest_la-tsearch.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-tsearch.lo `test -f 'tsearch.c' || echo '$(srcdir)/'`tsearch.c
+
+getxxyyy_test-getxxyyy.o: getxxyyy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getxxyyy_test_CFLAGS) $(CFLAGS) -MT getxxyyy_test-getxxyyy.o -MD -MP -MF $(DEPDIR)/getxxyyy_test-getxxyyy.Tpo -c -o getxxyyy_test-getxxyyy.o `test -f 'getxxyyy.c' || echo '$(srcdir)/'`getxxyyy.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getxxyyy_test-getxxyyy.Tpo $(DEPDIR)/getxxyyy_test-getxxyyy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getxxyyy.c' object='getxxyyy_test-getxxyyy.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libtest_la_CFLAGS) $(CFLAGS) -c -o libtest_la-tsearch.lo `test -f 'tsearch.c' || echo '$(srcdir)/'`tsearch.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getxxyyy_test_CFLAGS) $(CFLAGS) -c -o getxxyyy_test-getxxyyy.o `test -f 'getxxyyy.c' || echo '$(srcdir)/'`getxxyyy.c
+
+getxxyyy_test-getxxyyy.obj: getxxyyy.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getxxyyy_test_CFLAGS) $(CFLAGS) -MT getxxyyy_test-getxxyyy.obj -MD -MP -MF $(DEPDIR)/getxxyyy_test-getxxyyy.Tpo -c -o getxxyyy_test-getxxyyy.obj `if test -f 'getxxyyy.c'; then $(CYGPATH_W) 'getxxyyy.c'; else $(CYGPATH_W) '$(srcdir)/getxxyyy.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getxxyyy_test-getxxyyy.Tpo $(DEPDIR)/getxxyyy_test-getxxyyy.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getxxyyy.c' object='getxxyyy_test-getxxyyy.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getxxyyy_test_CFLAGS) $(CFLAGS) -c -o getxxyyy_test-getxxyyy.obj `if test -f 'getxxyyy.c'; then $(CYGPATH_W) 'getxxyyy.c'; else $(CYGPATH_W) '$(srcdir)/getxxyyy.c'; fi`
parse_reply_test-parse_reply-test.o: parse_reply-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-parse_reply-test.o -MD -MP -MF $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo -c -o parse_reply_test-parse_reply-test.o `test -f 'parse_reply-test.c' || echo '$(srcdir)/'`parse_reply-test.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo $(DEPDIR)/parse_reply_test-parse_reply-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='parse_reply-test.c' object='parse_reply_test-parse_reply-test.o' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-parse_reply-test.o -MD -MP -MF $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo -c -o parse_reply_test-parse_reply-test.o `test -f 'parse_reply-test.c' || echo '$(srcdir)/'`parse_reply-test.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo $(DEPDIR)/parse_reply_test-parse_reply-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse_reply-test.c' object='parse_reply_test-parse_reply-test.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-parse_reply-test.o `test -f 'parse_reply-test.c' || echo '$(srcdir)/'`parse_reply-test.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-parse_reply-test.o `test -f 'parse_reply-test.c' || echo '$(srcdir)/'`parse_reply-test.c
parse_reply_test-parse_reply-test.obj: parse_reply-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-parse_reply-test.obj -MD -MP -MF $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo -c -o parse_reply_test-parse_reply-test.obj `if test -f 'parse_reply-test.c'; then $(CYGPATH_W) 'parse_reply-test.c'; else $(CYGPATH_W) '$(srcdir)/parse_reply-test.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo $(DEPDIR)/parse_reply_test-parse_reply-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='parse_reply-test.c' object='parse_reply_test-parse_reply-test.obj' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-parse_reply-test.obj -MD -MP -MF $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo -c -o parse_reply_test-parse_reply-test.obj `if test -f 'parse_reply-test.c'; then $(CYGPATH_W) 'parse_reply-test.c'; else $(CYGPATH_W) '$(srcdir)/parse_reply-test.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/parse_reply_test-parse_reply-test.Tpo $(DEPDIR)/parse_reply_test-parse_reply-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='parse_reply-test.c' object='parse_reply_test-parse_reply-test.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-parse_reply-test.obj `if test -f 'parse_reply-test.c'; then $(CYGPATH_W) 'parse_reply-test.c'; else $(CYGPATH_W) '$(srcdir)/parse_reply-test.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-parse_reply-test.obj `if test -f 'parse_reply-test.c'; then $(CYGPATH_W) 'parse_reply-test.c'; else $(CYGPATH_W) '$(srcdir)/parse_reply-test.c'; fi`
parse_reply_test-resolve.o: resolve.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-resolve.o -MD -MP -MF $(DEPDIR)/parse_reply_test-resolve.Tpo -c -o parse_reply_test-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parse_reply_test-resolve.Tpo $(DEPDIR)/parse_reply_test-resolve.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='resolve.c' object='parse_reply_test-resolve.o' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-resolve.o -MD -MP -MF $(DEPDIR)/parse_reply_test-resolve.Tpo -c -o parse_reply_test-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/parse_reply_test-resolve.Tpo $(DEPDIR)/parse_reply_test-resolve.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolve.c' object='parse_reply_test-resolve.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-resolve.o `test -f 'resolve.c' || echo '$(srcdir)/'`resolve.c
parse_reply_test-resolve.obj: resolve.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-resolve.obj -MD -MP -MF $(DEPDIR)/parse_reply_test-resolve.Tpo -c -o parse_reply_test-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/parse_reply_test-resolve.Tpo $(DEPDIR)/parse_reply_test-resolve.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='resolve.c' object='parse_reply_test-resolve.obj' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -MT parse_reply_test-resolve.obj -MD -MP -MF $(DEPDIR)/parse_reply_test-resolve.Tpo -c -o parse_reply_test-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/parse_reply_test-resolve.Tpo $(DEPDIR)/parse_reply_test-resolve.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='resolve.c' object='parse_reply_test-resolve.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(parse_reply_test_CFLAGS) $(CFLAGS) -c -o parse_reply_test-resolve.obj `if test -f 'resolve.c'; then $(CYGPATH_W) 'resolve.c'; else $(CYGPATH_W) '$(srcdir)/resolve.c'; fi`
snprintf_test-snprintf-test.o: snprintf-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -MT snprintf_test-snprintf-test.o -MD -MP -MF $(DEPDIR)/snprintf_test-snprintf-test.Tpo -c -o snprintf_test-snprintf-test.o `test -f 'snprintf-test.c' || echo '$(srcdir)/'`snprintf-test.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/snprintf_test-snprintf-test.Tpo $(DEPDIR)/snprintf_test-snprintf-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='snprintf-test.c' object='snprintf_test-snprintf-test.o' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -MT snprintf_test-snprintf-test.o -MD -MP -MF $(DEPDIR)/snprintf_test-snprintf-test.Tpo -c -o snprintf_test-snprintf-test.o `test -f 'snprintf-test.c' || echo '$(srcdir)/'`snprintf-test.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snprintf_test-snprintf-test.Tpo $(DEPDIR)/snprintf_test-snprintf-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snprintf-test.c' object='snprintf_test-snprintf-test.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -c -o snprintf_test-snprintf-test.o `test -f 'snprintf-test.c' || echo '$(srcdir)/'`snprintf-test.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -c -o snprintf_test-snprintf-test.o `test -f 'snprintf-test.c' || echo '$(srcdir)/'`snprintf-test.c
snprintf_test-snprintf-test.obj: snprintf-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -MT snprintf_test-snprintf-test.obj -MD -MP -MF $(DEPDIR)/snprintf_test-snprintf-test.Tpo -c -o snprintf_test-snprintf-test.obj `if test -f 'snprintf-test.c'; then $(CYGPATH_W) 'snprintf-test.c'; else $(CYGPATH_W) '$(srcdir)/snprintf-test.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/snprintf_test-snprintf-test.Tpo $(DEPDIR)/snprintf_test-snprintf-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='snprintf-test.c' object='snprintf_test-snprintf-test.obj' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -MT snprintf_test-snprintf-test.obj -MD -MP -MF $(DEPDIR)/snprintf_test-snprintf-test.Tpo -c -o snprintf_test-snprintf-test.obj `if test -f 'snprintf-test.c'; then $(CYGPATH_W) 'snprintf-test.c'; else $(CYGPATH_W) '$(srcdir)/snprintf-test.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snprintf_test-snprintf-test.Tpo $(DEPDIR)/snprintf_test-snprintf-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snprintf-test.c' object='snprintf_test-snprintf-test.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -c -o snprintf_test-snprintf-test.obj `if test -f 'snprintf-test.c'; then $(CYGPATH_W) 'snprintf-test.c'; else $(CYGPATH_W) '$(srcdir)/snprintf-test.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snprintf_test_CFLAGS) $(CFLAGS) -c -o snprintf_test-snprintf-test.obj `if test -f 'snprintf-test.c'; then $(CYGPATH_W) 'snprintf-test.c'; else $(CYGPATH_W) '$(srcdir)/snprintf-test.c'; fi`
strpftime_test-strpftime-test.o: strpftime-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -MT strpftime_test-strpftime-test.o -MD -MP -MF $(DEPDIR)/strpftime_test-strpftime-test.Tpo -c -o strpftime_test-strpftime-test.o `test -f 'strpftime-test.c' || echo '$(srcdir)/'`strpftime-test.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/strpftime_test-strpftime-test.Tpo $(DEPDIR)/strpftime_test-strpftime-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strpftime-test.c' object='strpftime_test-strpftime-test.o' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -MT strpftime_test-strpftime-test.o -MD -MP -MF $(DEPDIR)/strpftime_test-strpftime-test.Tpo -c -o strpftime_test-strpftime-test.o `test -f 'strpftime-test.c' || echo '$(srcdir)/'`strpftime-test.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/strpftime_test-strpftime-test.Tpo $(DEPDIR)/strpftime_test-strpftime-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strpftime-test.c' object='strpftime_test-strpftime-test.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -c -o strpftime_test-strpftime-test.o `test -f 'strpftime-test.c' || echo '$(srcdir)/'`strpftime-test.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -c -o strpftime_test-strpftime-test.o `test -f 'strpftime-test.c' || echo '$(srcdir)/'`strpftime-test.c
strpftime_test-strpftime-test.obj: strpftime-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -MT strpftime_test-strpftime-test.obj -MD -MP -MF $(DEPDIR)/strpftime_test-strpftime-test.Tpo -c -o strpftime_test-strpftime-test.obj `if test -f 'strpftime-test.c'; then $(CYGPATH_W) 'strpftime-test.c'; else $(CYGPATH_W) '$(srcdir)/strpftime-test.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/strpftime_test-strpftime-test.Tpo $(DEPDIR)/strpftime_test-strpftime-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='strpftime-test.c' object='strpftime_test-strpftime-test.obj' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -MT strpftime_test-strpftime-test.obj -MD -MP -MF $(DEPDIR)/strpftime_test-strpftime-test.Tpo -c -o strpftime_test-strpftime-test.obj `if test -f 'strpftime-test.c'; then $(CYGPATH_W) 'strpftime-test.c'; else $(CYGPATH_W) '$(srcdir)/strpftime-test.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/strpftime_test-strpftime-test.Tpo $(DEPDIR)/strpftime_test-strpftime-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strpftime-test.c' object='strpftime_test-strpftime-test.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -c -o strpftime_test-strpftime-test.obj `if test -f 'strpftime-test.c'; then $(CYGPATH_W) 'strpftime-test.c'; else $(CYGPATH_W) '$(srcdir)/strpftime-test.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(strpftime_test_CFLAGS) $(CFLAGS) -c -o strpftime_test-strpftime-test.obj `if test -f 'strpftime-test.c'; then $(CYGPATH_W) 'strpftime-test.c'; else $(CYGPATH_W) '$(srcdir)/strpftime-test.c'; fi`
tsearch_test-tsearch-test.o: tsearch-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -MT tsearch_test-tsearch-test.o -MD -MP -MF $(DEPDIR)/tsearch_test-tsearch-test.Tpo -c -o tsearch_test-tsearch-test.o `test -f 'tsearch-test.c' || echo '$(srcdir)/'`tsearch-test.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tsearch_test-tsearch-test.Tpo $(DEPDIR)/tsearch_test-tsearch-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tsearch-test.c' object='tsearch_test-tsearch-test.o' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -MT tsearch_test-tsearch-test.o -MD -MP -MF $(DEPDIR)/tsearch_test-tsearch-test.Tpo -c -o tsearch_test-tsearch-test.o `test -f 'tsearch-test.c' || echo '$(srcdir)/'`tsearch-test.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tsearch_test-tsearch-test.Tpo $(DEPDIR)/tsearch_test-tsearch-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tsearch-test.c' object='tsearch_test-tsearch-test.o' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -c -o tsearch_test-tsearch-test.o `test -f 'tsearch-test.c' || echo '$(srcdir)/'`tsearch-test.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -c -o tsearch_test-tsearch-test.o `test -f 'tsearch-test.c' || echo '$(srcdir)/'`tsearch-test.c
tsearch_test-tsearch-test.obj: tsearch-test.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -MT tsearch_test-tsearch-test.obj -MD -MP -MF $(DEPDIR)/tsearch_test-tsearch-test.Tpo -c -o tsearch_test-tsearch-test.obj `if test -f 'tsearch-test.c'; then $(CYGPATH_W) 'tsearch-test.c'; else $(CYGPATH_W) '$(srcdir)/tsearch-test.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/tsearch_test-tsearch-test.Tpo $(DEPDIR)/tsearch_test-tsearch-test.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='tsearch-test.c' object='tsearch_test-tsearch-test.obj' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -MT tsearch_test-tsearch-test.obj -MD -MP -MF $(DEPDIR)/tsearch_test-tsearch-test.Tpo -c -o tsearch_test-tsearch-test.obj `if test -f 'tsearch-test.c'; then $(CYGPATH_W) 'tsearch-test.c'; else $(CYGPATH_W) '$(srcdir)/tsearch-test.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tsearch_test-tsearch-test.Tpo $(DEPDIR)/tsearch_test-tsearch-test.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='tsearch-test.c' object='tsearch_test-tsearch-test.obj' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -c -o tsearch_test-tsearch-test.obj `if test -f 'tsearch-test.c'; then $(CYGPATH_W) 'tsearch-test.c'; else $(CYGPATH_W) '$(srcdir)/tsearch-test.c'; fi`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(tsearch_test_CFLAGS) $(CFLAGS) -c -o tsearch_test-tsearch-test.obj `if test -f 'tsearch-test.c'; then $(CYGPATH_W) 'tsearch-test.c'; else $(CYGPATH_W) '$(srcdir)/tsearch-test.c'; fi`
mostlyclean-libtool:
-rm -f *.lo
@@ -1426,11 +1829,18 @@ clean-libtool:
-rm -rf .libs _libs
install-man3: $(man_MANS)
@$(NORMAL_INSTALL)
- test -z "$(man3dir)" || $(MKDIR_P) "$(DESTDIR)$(man3dir)"
- @list=''; test -n "$(man3dir)" || exit 0; \
- { for i in $$list; do echo "$$i"; done; \
- l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \
- sed -n '/\.3[a-z]*$$/p'; \
+ @list1=''; \
+ list2='$(man_MANS)'; \
+ test -n "$(man3dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man3dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man3dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.3[a-z]*$$/p'; \
+ fi; \
} | while read p; do \
if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; echo "$$p"; \
@@ -1459,13 +1869,14 @@ uninstall-man3:
sed -n '/\.3[a-z]*$$/p'; \
} | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^3][0-9a-z]*$$,3,;x' \
-e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
- test -z "$$files" || { \
- echo " ( cd '$(DESTDIR)$(man3dir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(man3dir)" && rm -f $$files; }
+ dir='$(DESTDIR)$(man3dir)'; $(am__uninstall_files_from_dir)
install-dist_includeHEADERS: $(dist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1479,13 +1890,14 @@ uninstall-dist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_includeHEADERS: $(nodist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1499,13 +1911,14 @@ uninstall-nodist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_rokenincludeHEADERS: $(nodist_rokeninclude_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(rokenincludedir)" || $(MKDIR_P) "$(DESTDIR)$(rokenincludedir)"
@list='$(nodist_rokeninclude_HEADERS)'; test -n "$(rokenincludedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(rokenincludedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(rokenincludedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -1519,30 +1932,17 @@ uninstall-nodist_rokenincludeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_rokeninclude_HEADERS)'; test -n "$(rokenincludedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(rokenincludedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(rokenincludedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(rokenincludedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -1554,15 +1954,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -1571,116 +1967,273 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+base64-test.log: base64-test$(EXEEXT)
+ @p='base64-test$(EXEEXT)'; \
+ b='base64-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+getaddrinfo-test.log: getaddrinfo-test$(EXEEXT)
+ @p='getaddrinfo-test$(EXEEXT)'; \
+ b='getaddrinfo-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+getifaddrs-test.log: getifaddrs-test$(EXEEXT)
+ @p='getifaddrs-test$(EXEEXT)'; \
+ b='getifaddrs-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+getxxyyy-test.log: getxxyyy-test$(EXEEXT)
+ @p='getxxyyy-test$(EXEEXT)'; \
+ b='getxxyyy-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+hex-test.log: hex-test$(EXEEXT)
+ @p='hex-test$(EXEEXT)'; \
+ b='hex-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-readenv.log: test-readenv$(EXEEXT)
+ @p='test-readenv$(EXEEXT)'; \
+ b='test-readenv'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+resolve-test.log: resolve-test$(EXEEXT)
+ @p='resolve-test$(EXEEXT)'; \
+ b='resolve-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+parse_bytes-test.log: parse_bytes-test$(EXEEXT)
+ @p='parse_bytes-test$(EXEEXT)'; \
+ b='parse_bytes-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+parse_reply-test.log: parse_reply-test$(EXEEXT)
+ @p='parse_reply-test$(EXEEXT)'; \
+ b='parse_reply-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+parse_time-test.log: parse_time-test$(EXEEXT)
+ @p='parse_time-test$(EXEEXT)'; \
+ b='parse_time-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+snprintf-test.log: snprintf-test$(EXEEXT)
+ @p='snprintf-test$(EXEEXT)'; \
+ b='snprintf-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+strpftime-test.log: strpftime-test$(EXEEXT)
+ @p='strpftime-test$(EXEEXT)'; \
+ b='strpftime-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+tsearch-test.log: tsearch-test$(EXEEXT)
+ @p='tsearch-test$(EXEEXT)'; \
+ b='tsearch-test'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
- @list='$(MANS)'; if test -n "$$list"; then \
- list=`for p in $$list; do \
- if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
- if test -f "$$d$$p"; then echo "$$d$$p"; else :; fi; done`; \
- if test -n "$$list" && \
- grep 'ab help2man is required to generate this page' $$list >/dev/null; then \
- echo "error: found man pages containing the \`missing help2man' replacement text:" >&2; \
- grep -l 'ab help2man is required to generate this page' $$list | sed 's/^/ /' >&2; \
- echo " to fix them, install help2man, remove and regenerate the man pages;" >&2; \
- echo " typically \`make maintainer-clean' will remove them" >&2; \
- exit 1; \
- else :; fi; \
- else :; fi
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -1735,11 +2288,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -1785,9 +2346,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -1834,45 +2394,60 @@ uninstall-am: uninstall-dist_includeHEADERS uninstall-libLTLIBRARIES \
uninstall-man: uninstall-man3
.MAKE: all check check-am install install-am install-data-am \
- install-exec-am install-strip uninstall-am
+ install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-checkPROGRAMS clean-generic \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-checkPROGRAMS clean-generic \
clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \
- clean-noinstPROGRAMS ctags dist-hook distclean \
- distclean-compile distclean-generic distclean-libtool \
- distclean-tags distdir dvi dvi-am html html-am info info-am \
- install install-am install-data install-data-am \
- install-data-hook install-dist_includeHEADERS install-dvi \
- install-dvi-am install-exec install-exec-am install-exec-hook \
- install-html install-html-am install-info install-info-am \
- install-libLTLIBRARIES install-man install-man3 \
- install-nodist_includeHEADERS \
+ clean-noinstPROGRAMS cscopelist-am ctags ctags-am dist-hook \
+ distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-data-hook install-dist_includeHEADERS \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-exec-local install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES install-man \
+ install-man3 install-nodist_includeHEADERS \
install-nodist_rokenincludeHEADERS install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-dist_includeHEADERS \
- uninstall-hook uninstall-libLTLIBRARIES uninstall-man \
- uninstall-man3 uninstall-nodist_includeHEADERS \
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-dist_includeHEADERS uninstall-hook \
+ uninstall-libLTLIBRARIES uninstall-man uninstall-man3 \
+ uninstall-nodist_includeHEADERS \
uninstall-nodist_rokenincludeHEADERS
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
-install-exec-hook: install-suid-programs
+install-exec-local: install-suid-programs
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -1880,7 +2455,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -1937,6 +2512,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1979,6 +2556,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1992,13 +2582,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/roken/NTMakefile b/lib/roken/NTMakefile
index fab0a0cc0fd7..57ef7506f81f 100644
--- a/lib/roken/NTMakefile
+++ b/lib/roken/NTMakefile
@@ -39,6 +39,7 @@ libroken_la_OBJS = \
$(OBJ)\concat.obj \
$(OBJ)\cloexec.obj \
$(OBJ)\ct.obj \
+ $(OBJ)\detach.obj \
$(OBJ)\dirent.obj \
$(OBJ)\dlfcn_w32.obj \
$(OBJ)\dumpdata.obj \
@@ -69,6 +70,8 @@ libroken_la_OBJS = \
$(OBJ)\issuid.obj \
$(OBJ)\localtime_r.obj \
$(OBJ)\lstat.obj \
+ $(OBJ)\memset_s.obj \
+ $(OBJ)\mkdir.obj \
$(OBJ)\mini_inetd.obj \
$(OBJ)\mkstemp.obj \
$(OBJ)\net_read.obj \
@@ -94,11 +97,14 @@ libroken_la_OBJS = \
$(OBJ)\strerror_r.obj \
$(OBJ)\strlcat.obj \
$(OBJ)\strlcpy.obj \
+ $(OBJ)\strndup.obj \
$(OBJ)\strpool.obj \
$(OBJ)\strptime.obj \
$(OBJ)\strsep.obj \
$(OBJ)\strsep_copy.obj \
$(OBJ)\strtok_r.obj \
+ $(OBJ)\strtoll.obj \
+ $(OBJ)\strtoull.obj \
$(OBJ)\syslogc.obj \
$(OBJ)\timegm.obj \
$(OBJ)\timeval.obj \
@@ -113,6 +119,8 @@ libroken_la_OBJS = \
$(OBJ)\warn.obj \
$(OBJ)\warnerr.obj \
$(OBJ)\warnx.obj \
+ $(OBJ)\win32_alloc.obj \
+ $(OBJ)\writev.obj \
$(OBJ)\xfree.obj
!if $(NMAKE_WINVER) < 0x0600
@@ -160,6 +168,9 @@ INCFILES = \
$(INCDIR)\stdbool.h \
$(INCDIR)\syslog.h \
$(INCDIR)\vis.h \
+!ifndef HAVE_STDINT_H
+ $(INCDIR)\stdint.h \
+!endif
$(INCDIR)\xdbm.h
clean::
@@ -179,6 +190,7 @@ TEST_PROGS = \
$(OBJ)\getaddrinfo-test.exe \
$(OBJ)\getifaddrs-test.exe \
$(OBJ)\hex-test.exe \
+ $(OBJ)\test-detach.exe \
$(OBJ)\test-readenv.exe \
$(OBJ)\parse_bytes-test.exe \
$(OBJ)\parse_reply-test.exe \
@@ -230,7 +242,7 @@ $(OBJ)\snprintf-test.exe: $(OBJ)\snprintf-test.obj $(OBJ)\libtest.lib $(LIBROKEN
$(OBJ)\resolve-test.exe: $(OBJ)\resolve-test.obj $(LIBROKEN)
$(EXECONLINK) DnsAPI.lib
-$(OBJ)\base64-test.exe: $(OBJ)\base64-test.obj $(OBJ)\base64.obj
+$(OBJ)\base64-test.exe: $(OBJ)\base64-test.obj $(OBJ)\base64.obj $(LIBROKEN)
$(EXECONLINK)
$(OBJ)\getaddrinfo-test.exe: $(OBJ)\getaddrinfo-test.obj $(LIBROKEN)
@@ -242,6 +254,9 @@ $(OBJ)\hex-test.exe: $(OBJ)\hex-test.obj $(LIBROKEN)
$(OBJ)\parse_bytes-test.exe: $(OBJ)\parse_bytes-test.obj $(LIBROKEN)
$(EXECONLINK)
+$(OBJ)\test-detach.exe: $(OBJ)\test-detach.obj $(OBJ)\detach.obj $(LIBROKEN)
+ $(EXECONLINK)
+
$(OBJ)\dirent-test.exe: $(OBJ)\dirent-test.obj $(LIBROKEN)
$(EXECONLINK)
@@ -255,19 +270,19 @@ test-binaries: $(TEST_PROGS) $(TMP_PROGS)
test-run:
cd $(OBJ)
- test-mini_inetd.exe
- dirent-test.exe
- base64-test.exe
- getaddrinfo-test.exe
- getifaddrs-test.exe
- hex-test.exe
- test-readenv.exe
- parse_bytes-test.exe
+ -test-mini_inetd.exe
+ -dirent-test.exe
+ -base64-test.exe
+ -getaddrinfo-test.exe
+ -getifaddrs-test.exe
+ -hex-test.exe
+ -test-readenv.exe
+ -parse_bytes-test.exe
# Need to rewrite this test:
-# parse_reply-test.exe
- parse_time-test.exe
- snprintf-test.exe
- strpftime-test.exe
+# -parse_reply-test.exe
+ -parse_time-test.exe
+ -snprintf-test.exe
+ -strpftime-test.exe
cd $(SRCDIR)
test:: test-binaries test-run
diff --git a/lib/roken/base64-test.c b/lib/roken/base64-test.c
index e9a2835e85e1..86cccbb1d4f9 100644
--- a/lib/roken/base64-test.c
+++ b/lib/roken/base64-test.c
@@ -53,12 +53,12 @@ main(int argc, char **argv)
{ "4444", 4, "NDQ0NA==" },
{ "55555", 5, "NTU1NTU=" },
{ "abc:def", 7, "YWJjOmRlZg==" },
- { NULL }
+ { NULL, 0, NULL }
};
for(t = tests; t->data; t++) {
char *str;
int len;
- len = base64_encode(t->data, t->len, &str);
+ len = rk_base64_encode(t->data, t->len, &str);
if(strcmp(str, t->result) != 0) {
fprintf(stderr, "failed test %d: %s != %s\n", numtest,
str, t->result);
@@ -66,7 +66,7 @@ main(int argc, char **argv)
}
free(str);
str = strdup(t->result);
- len = base64_decode(t->result, str);
+ len = rk_base64_decode(t->result, str);
if(len != t->len) {
fprintf(stderr, "failed test %d: len %lu != %lu\n", numtest,
(unsigned long)len, (unsigned long)t->len);
@@ -81,12 +81,12 @@ main(int argc, char **argv)
{
char str[32];
- if(base64_decode("M=M=", str) != -1) {
+ if(rk_base64_decode("M=M=", str) != -1) {
fprintf(stderr, "failed test %d: successful decode of `M=M='\n",
numtest++);
numerr++;
}
- if(base64_decode("MQ===", str) != -1) {
+ if(rk_base64_decode("MQ===", str) != -1) {
fprintf(stderr, "failed test %d: successful decode of `MQ==='\n",
numtest++);
numerr++;
diff --git a/lib/roken/base64.c b/lib/roken/base64.c
index 394e9841c78d..6ee4899e5331 100644
--- a/lib/roken/base64.c
+++ b/lib/roken/base64.c
@@ -52,7 +52,7 @@ pos(char c)
}
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
-base64_encode(const void *data, int size, char **str)
+rk_base64_encode(const void *data, int size, char **str)
{
char *s, *p;
int i;
@@ -121,7 +121,7 @@ token_decode(const char *token)
}
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
-base64_decode(const char *str, void *data)
+rk_base64_decode(const char *str, void *data)
{
const char *p;
unsigned char *q;
diff --git a/lib/roken/base64.h b/lib/roken/base64.h
index dfae4c13b324..734a6b5d9ebb 100644
--- a/lib/roken/base64.h
+++ b/lib/roken/base64.h
@@ -47,9 +47,9 @@
#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
-base64_encode(const void *, int, char **);
+rk_base64_encode(const void *, int, char **);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
-base64_decode(const char *, void *);
+rk_base64_decode(const char *, void *);
#endif
diff --git a/lib/roken/bswap.c b/lib/roken/bswap.c
index 7f8c1c22b1b7..b0c4248da11e 100644
--- a/lib/roken/bswap.c
+++ b/lib/roken/bswap.c
@@ -34,6 +34,23 @@
#include <config.h>
#include "roken.h"
+#ifndef HAVE_BSWAP64
+
+ROKEN_LIB_FUNCTION uint64_t ROKEN_LIB_CALL
+bswap64 (uint64_t val)
+{
+ return
+ (val & 0xffULL) << 56 |
+ (val & 0xff00ULL) << 40 |
+ (val & 0xff0000ULL) << 24 |
+ (val & 0xff000000ULL) << 8 |
+ (val & 0xff00000000ULL) >> 8 |
+ (val & 0xff0000000000ULL) >> 24 |
+ (val & 0xff000000000000ULL) >> 40 |
+ (val & 0xff00000000000000ULL) >> 56 ;
+}
+#endif
+
#ifndef HAVE_BSWAP32
ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
diff --git a/lib/roken/cloexec.c b/lib/roken/cloexec.c
index 2d1fe033f2d1..59ffa50ae1eb 100644
--- a/lib/roken/cloexec.c
+++ b/lib/roken/cloexec.c
@@ -64,3 +64,12 @@ rk_cloexec_dir(DIR * d)
rk_cloexec(dirfd(d));
#endif
}
+
+void ROKEN_LIB_FUNCTION
+rk_cloexec_socket(rk_socket_t s)
+{
+#ifndef _WIN32
+ rk_cloexec((int)s);
+#endif
+}
+
diff --git a/lib/roken/ct.c b/lib/roken/ct.c
index 0778c2d47499..79f312d8d79e 100644
--- a/lib/roken/ct.c
+++ b/lib/roken/ct.c
@@ -42,6 +42,12 @@
* ct_memcmp() can't be used to order memory regions like memcmp(),
* for example, use ct_memcmp() with qsort().
*
+ * We use volatile to avoid optimizations where the compiler and/or
+ * linker turn this ct_memcmp() into a plain memcmp(). The pointers
+ * themselves are also marked volatile (not just the memory pointed at)
+ * because in some GCC versions there is a bug which can be worked
+ * around by doing this.
+ *
* @param p1 memory region 1 to compare
* @param p2 memory region 2 to compare
* @param len length of memory
@@ -52,9 +58,21 @@
*/
int
-ct_memcmp(const void *p1, const void *p2, size_t len)
+ct_memcmp(const volatile void * volatile p1,
+ const volatile void * volatile p2,
+ size_t len)
{
- const unsigned char *s1 = p1, *s2 = p2;
+ /*
+ * There's no need for s1 and s2 to be volatile; only p1 and p2 have
+ * to be in order to work around GCC bugs.
+ *
+ * However, s1 and s2 do have to point to volatile, as we don't know
+ * if the object was originally defined as volatile, and if it was
+ * then we'd get undefined behavior here if s1/s2 were declared to
+ * point to non-volatile memory.
+ */
+ const volatile unsigned char *s1 = p1;
+ const volatile unsigned char *s2 = p2;
size_t i;
int r = 0;
diff --git a/lib/roken/daemon.c b/lib/roken/daemon.c
index 591a9a9532fb..6128bebe3875 100644
--- a/lib/roken/daemon.c
+++ b/lib/roken/daemon.c
@@ -72,7 +72,7 @@ daemon(int nochdir, int noclose)
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
if (fd > 2)
- close (fd);
+ (void) close(fd);
}
return (0);
}
diff --git a/lib/roken/detach.c b/lib/roken/detach.c
new file mode 100644
index 000000000000..7d0f24d32ad8
--- /dev/null
+++ b/lib/roken/detach.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (c) 2015
+ * Cryptonector LLC. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Cryptonector LLC may not be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef WIN32
+#include <io.h>
+#include <stdlib.h>
+#else
+#include <unistd.h>
+#endif
+#include "roken.h"
+
+#ifdef WIN32
+#define dup2 _dup2
+#endif
+
+static int pipefds[2] = {-1, -1};
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+roken_detach_prep(int argc, char **argv, char *special_arg)
+{
+ pid_t child;
+ char buf[1];
+ ssize_t bytes;
+ int status;
+
+ pipefds[0] = -1;
+ pipefds[1] = -1;
+
+#ifdef WIN32
+ if (_pipe(pipefds, 4, O_BINARY) == -1)
+ err(1, "failed to setup to detach daemon (_pipe failed)");
+#else
+ if (pipe(pipefds) == -1)
+ err(1, "failed to setup to detach daemon (pipe failed)");
+#endif
+
+#ifndef WIN32
+ fflush(stdout);
+ child = fork();
+#else
+ {
+ intptr_t child_handle;
+ int write_side;
+ size_t i;
+ char *fildes;
+ char **new_argv;
+
+ new_argv = calloc(argc + 2, sizeof(*new_argv));
+ if (new_argv == NULL)
+ err(1, "Out of memory");
+
+ write_side = _dup(pipefds[1]); /* The new fd will be inherited */
+ if (write_side == -1)
+ err(1, "Out of memory");
+
+ if (asprintf(&fildes, "%d", write_side) == -1 ||
+ fildes == NULL)
+ err(1, "failed to setup to detach daemon (_dup failed)");
+
+ new_argv[0] = argv[0];
+ new_argv[1] = special_arg;
+ new_argv[2] = fildes;
+ for (i = 1; argv[i] != NULL; i++)
+ new_argv[i + 1] = argv[i];
+ new_argv[argc + 2] = NULL;
+
+ _flushall();
+ child_handle = spawnvp(_P_NOWAIT, argv[0], new_argv);
+ if (child_handle == -1)
+ child = (pid_t)-1;
+ else
+ child = GetProcessId((HANDLE)child_handle);
+ }
+#endif
+ if (child == (pid_t)-1)
+ err(1, "failed to setup to fork daemon (fork failed)");
+
+#ifndef WIN32
+ if (child == 0) {
+ int fd;
+
+ (void) close(pipefds[0]);
+ pipefds[0] = -1;
+ /*
+ * Keep stdout/stderr for now so output and errors prior to
+ * detach_finish() can be seen by the user.
+ */
+ fd = open(_PATH_DEVNULL, O_RDWR, 0);
+ if (fd == -1)
+ err(1, "failed to open /dev/null");
+ (void) dup2(fd, STDIN_FILENO);
+ if (fd > STDERR_FILENO)
+ (void) close(fd);
+ return;
+ }
+#endif
+
+ (void) close(pipefds[1]);
+ pipefds[1] = -1;
+ do {
+ bytes = read(pipefds[0], buf, sizeof(buf));
+ } while (bytes == -1 && errno == EINTR);
+ (void) close(pipefds[0]);
+ pipefds[0] = -1;
+ if (bytes == -1) {
+ /*
+ * No need to wait for the process. We've killed it. If it
+ * doesn't want to exit, we'd have to wait potentially forever,
+ * but we want to indicate failure to the user as soon as
+ * possible. A wait with timeout would end the same way
+ * (attempting to kill the process).
+ */
+ err(1, "failed to setup daemon child (read from child pipe)");
+ }
+ if (bytes == 0) {
+ warnx("daemon child preparation failed, waiting for child");
+ status = wait_for_process(child);
+ if (SE_IS_ERROR(status) || SE_PROCSTATUS(status) != 0)
+ errx(SE_PROCSTATUS(status),
+ "daemon child preparation failed (child exited)");
+ }
+ _exit(0);
+}
+
+#ifdef WIN32
+#ifdef dup2
+#undef dup2
+#endif
+#define dup2 _dup2
+#endif
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+roken_detach_finish(const char *dir, int daemon_child_fd)
+{
+ char buf[1] = "";
+ ssize_t bytes;
+ int fd;
+
+ rk_pidfile(NULL);
+ if (pipefds[1] == -1 && daemon_child_fd != -1)
+ pipefds[1] = daemon_child_fd;
+ if (pipefds[0] != -1)
+ (void) close(pipefds[0]);
+ if (pipefds[1] == -1)
+ return;
+
+#ifdef HAVE_SETSID
+ if (setsid() == -1)
+ err(1, "failed to detach from tty");
+#endif
+
+#ifndef WIN32
+ /*
+ * Hopefully we've written any pidfiles by now, if they had to be in
+ * the current directory...
+ *
+ * The daemons do re-open logs and so on, therefore this chdir()
+ * call needs to be optional for testing.
+ */
+ if (dir != NULL && chdir(dir) == -1)
+ err(1, "failed to chdir to /");
+#endif
+
+ do {
+ bytes = write(pipefds[1], buf, sizeof(buf));
+ } while (bytes == -1 && errno == EINTR);
+ if (bytes == -1)
+ err(1, "failed to signal parent while detaching");
+ (void) close(pipefds[1]);
+ if (bytes != sizeof(buf))
+ errx(1, "failed to signal parent while detaching");
+
+ fd = open(_PATH_DEVNULL, O_RDWR, 0);
+ if (fd == -1)
+ err(1, "failed to open /dev/null");
+ /*
+ * Maybe we should check that our output got written, if redirected
+ * to a file. File utils normally do this.
+ */
+ (void) dup2(fd, STDOUT_FILENO);
+ (void) dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void) close(fd);
+}
diff --git a/lib/roken/dirent-test.c b/lib/roken/dirent-test.c
new file mode 100644
index 000000000000..2ac3827fb12f
--- /dev/null
+++ b/lib/roken/dirent-test.c
@@ -0,0 +1,278 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <direct.h>
+#include <errno.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <string.h>
+#include "dirent.h"
+
+/* Note that we create a known directory structure in a subdirectory
+ of the current directory to run our tests. */
+
+#define TESTDIR "dirent-test-dir"
+
+const char * dir_entries[] = {
+ "A",
+ "B",
+ "C",
+ "CAA",
+ "CAAA",
+ "CABBBB",
+ "CAABBB.txt",
+ "A filename with spaces"
+};
+
+const char * entries_begin_with_C[] = {
+ "C",
+ "CAA",
+ "CAAA",
+ "CABBBB",
+ "CAABBB.txt"
+};
+
+const char * entries_end_with_A[] = {
+ "A",
+ "CAA",
+ "CAAA"
+};
+
+const int n_dir_entries = sizeof(dir_entries)/sizeof(dir_entries[0]);
+
+int teardown_test(void);
+
+void fail_test(const char * reason, ...)
+{
+ va_list args;
+
+ va_start(args, reason);
+ vfprintf(stderr, reason, args);
+ va_end(args);
+
+ fprintf(stderr, " : errno = %d (%s)\n", errno, strerror(errno));
+ teardown_test();
+ abort();
+}
+
+void fail_test_nf(const char * format, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "FAIL:");
+
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+
+ fprintf(stderr, " : errno = %d (%s)\n", errno, strerror(errno));
+}
+
+int touch(const char * filename)
+{
+ int fd;
+
+ fd = _open(filename, _O_CREAT, _S_IREAD| _S_IWRITE);
+
+ if (fd == -1)
+ return -1;
+
+ return _close(fd);
+}
+
+int setup_test(void)
+{
+ int i;
+
+ fprintf(stderr, "Creating test directory %s ...\n", TESTDIR);
+
+ if (_mkdir(TESTDIR))
+ fail_test("Can't create test directory \"" TESTDIR "\"");
+
+ if (_chdir(TESTDIR))
+ fail_test("Can't change to test directory");
+
+ for (i=0; i < n_dir_entries; i++) {
+ if (touch(dir_entries[i]))
+ fail_test("Can't create test file '%s'", dir_entries[i]);
+ }
+
+ fprintf(stderr, "Done with test setup.\n");
+
+ return 0;
+}
+
+int teardown_test(void)
+{
+ char dirname[_MAX_PATH];
+ size_t len;
+ int i;
+
+ printf ("Begin cleanup...\n");
+
+ if (_getcwd(dirname, sizeof(dirname)/sizeof(char)) != NULL &&
+
+ (len = strlen(dirname)) > sizeof(TESTDIR)/sizeof(char) &&
+
+ !strcmp(dirname + len + 1 - sizeof(TESTDIR)/sizeof(char), TESTDIR)) {
+
+ /* fallthrough */
+
+ } else {
+ /* did we create the directory? */
+
+ if (!_rmdir( TESTDIR )) {
+ fprintf(stderr, "Removed test directory\n");
+ return 0;
+ } else {
+ if (errno == ENOTEMPTY) {
+ if (_chdir(TESTDIR)) {
+ fprintf(stderr, "Can't change to test directory. Aborting cleanup.\n");
+ return -1;
+ } else {
+ /* fallthrough */
+ }
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ fprintf(stderr, "Cleaning up test directory %s ...\n", TESTDIR);
+
+ for (i=0; i < n_dir_entries; i++) {
+ if (_unlink(dir_entries[i])) {
+ /* if the test setup failed, we expect this to happen for
+ at least some files */
+ }
+ }
+
+ if (_chdir("..")) {
+ fprintf(stderr, "Can't escape test directory. Giving in.\n");
+ return -1;
+ }
+
+ if (_rmdir( TESTDIR )) {
+ fprintf(stderr, "Can't remove test directory.\n");
+ return -1;
+ }
+
+ printf("Cleaned up test directory\n");
+ return 0;
+}
+
+int check_list(const char * filespec, const char ** list, int n, int expect_dot_and_dotdot)
+{
+ DIR * d;
+ struct dirent * e;
+ int n_found = 0;
+ int i;
+ int rv = 0;
+ int retry = 1;
+
+ d = opendir(filespec);
+ if (d == NULL) {
+ fail_test_nf("opendir failed for [%s]", filespec);
+ return -1;
+ }
+
+ printf("Checking filespec [%s]... ", filespec);
+
+ retry:
+ while ((e = readdir(d)) != NULL) {
+ n_found ++;
+
+ if (expect_dot_and_dotdot &&
+ (!strcmp(e->d_name, ".") ||
+ !strcmp(e->d_name, "..")))
+ continue;
+
+ for (i=0; i < n; i++) {
+ if (!strcmp(list[i], e->d_name))
+ break;
+ }
+
+ if (i == n) {
+ fail_test_nf("Found unexpected entry [%s]", e->d_name);
+ rv = -1;
+ }
+ }
+
+ if (n_found != n) {
+ fail_test_nf("Unexpected number of entries [%d]. Expected %d", n_found, n);
+ rv = -1;
+ }
+
+ if (retry) {
+ retry = 0;
+ n_found = 0;
+
+ rewinddir(d);
+ goto retry;
+ }
+
+ if (closedir(d)) {
+ fail_test_nf("closedir() failed");
+ }
+
+ printf("done\n");
+
+ return rv;
+}
+
+int run_tests()
+{
+ /* assumes that the test directory has been set up and we have
+ changed into the test directory. */
+
+ check_list("*", dir_entries, n_dir_entries + 2, 1);
+ check_list("*.*", dir_entries, n_dir_entries + 2, 1);
+ check_list("C*", entries_begin_with_C, sizeof(entries_begin_with_C)/sizeof(entries_begin_with_C[0]), 0);
+ check_list("*A", entries_end_with_A, sizeof(entries_end_with_A)/sizeof(entries_end_with_A[0]), 0);
+
+ return 0;
+}
+
+int main(int argc, char ** argv)
+{
+ if (setup_test())
+ return 1;
+
+ run_tests();
+
+ teardown_test();
+
+ return 0;
+}
diff --git a/lib/roken/dirent.c b/lib/roken/dirent.c
new file mode 100644
index 000000000000..36f1bc9ddb0e
--- /dev/null
+++ b/lib/roken/dirent.c
@@ -0,0 +1,235 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include<config.h>
+
+#include <stdlib.h>
+#include <io.h>
+#include <string.h>
+#include <errno.h>
+#include "dirent.h"
+
+#ifndef _WIN32
+#error Only implemented for Win32
+#endif
+
+struct _dirent_dirinfo {
+ int magic;
+ long n_entries;
+ long nc_entries;
+ long cursor;
+ struct dirent **entries;
+};
+#define DIRINFO_MAGIC 0xf8c0639d
+#define IS_DP(p) ((p) && ((DIR *)(p))->magic == DIRINFO_MAGIC)
+
+#define INITIAL_ENTRIES 16
+
+/**
+ * Create a filespec for use with _findfirst() using a path spec
+ *
+ * If the last component of the path spec contains wildcards, we let
+ * it be. If the last component doesn't end with a slash, we add one.
+ */
+static const char *
+filespec_from_dir_path(const char * path, char * buffer, size_t cch_buffer)
+{
+ char *comp, *t;
+ size_t pos;
+ int found_sep = 0;
+
+ if (strcpy_s(buffer, cch_buffer, path) != 0)
+ return NULL;
+
+ comp = strrchr(buffer, '\\');
+ if (comp == NULL)
+ comp = buffer;
+ else
+ found_sep = 1;
+
+ t = strrchr(comp, '/');
+ if (t != NULL) {
+ comp = t;
+ found_sep = 1;
+ }
+
+ if (found_sep)
+ comp++;
+
+ pos = strcspn(comp, "*?");
+ if (comp[pos] != '\0')
+ return buffer;
+
+ /* We don't append a slash if pos == 0 because that changes the
+ * meaning:
+ *
+ * "*.*" is all files in the current directory.
+ * "\*.*" is all files in the root directory of the current drive.
+ */
+ if (pos > 0 && comp[pos - 1] != '\\' &&
+ comp[pos - 1] != '/') {
+ strcat_s(comp, cch_buffer - (comp - buffer), "\\");
+ }
+
+ strcat_s(comp, cch_buffer - (comp - buffer), "*.*");
+
+ return buffer;
+}
+
+ROKEN_LIB_FUNCTION DIR * ROKEN_LIB_CALL
+opendir(const char * path)
+{
+ DIR * dp;
+ struct _finddata_t fd;
+ intptr_t fd_handle;
+ const char *filespec;
+ char path_buffer[1024]="";
+
+ memset(&fd, 0, sizeof(fd));
+
+ filespec = filespec_from_dir_path(path, path_buffer, sizeof(path_buffer)/sizeof(char));
+ if (filespec == NULL)
+ return NULL;
+
+ fd_handle = _findfirst(filespec, &fd);
+
+ if (fd_handle == -1)
+ return NULL;
+
+ dp = malloc(sizeof(*dp));
+ if (dp == NULL)
+ goto done;
+
+ memset(dp, 0, sizeof(*dp));
+ dp->magic = DIRINFO_MAGIC;
+ dp->cursor = 0;
+ dp->n_entries = 0;
+ dp->nc_entries = INITIAL_ENTRIES;
+ dp->entries = calloc(dp->nc_entries, sizeof(dp->entries[0]));
+
+ if (dp->entries == NULL) {
+ closedir(dp);
+ dp = NULL;
+ goto done;
+ }
+
+ do {
+ size_t len = strlen(fd.name);
+ struct dirent * e;
+
+ if (dp->n_entries == dp->nc_entries) {
+ struct dirent ** ne;
+
+ dp->nc_entries *= 2;
+ ne = realloc(dp->entries, sizeof(dp->entries[0]) * dp->nc_entries);
+
+ if (ne == NULL) {
+ closedir(dp);
+ dp = NULL;
+ goto done;
+ }
+
+ dp->entries = ne;
+ }
+
+ e = malloc(sizeof(*e) + len * sizeof(char));
+ if (e == NULL) {
+ closedir(dp);
+ dp = NULL;
+ goto done;
+ }
+
+ e->d_ino = 0; /* no inodes :( */
+ strcpy_s(e->d_name, len + 1, fd.name);
+
+ dp->entries[dp->n_entries++] = e;
+
+ } while (_findnext(fd_handle, &fd) == 0);
+
+ done:
+ if (fd_handle != -1)
+ _findclose(fd_handle);
+
+ return dp;
+}
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+closedir(DIR * dp)
+{
+ if (!IS_DP(dp))
+ return EINVAL;
+
+ if (dp->entries) {
+ long i;
+
+ for (i=0; i < dp->n_entries; i++) {
+ free(dp->entries[i]);
+ }
+
+ free(dp->entries);
+ }
+
+ free(dp);
+
+ return 0;
+}
+
+ROKEN_LIB_FUNCTION struct dirent * ROKEN_LIB_CALL
+readdir(DIR * dp)
+{
+ if (!IS_DP(dp) ||
+ dp->cursor < 0 ||
+ dp->cursor >= dp->n_entries)
+
+ return NULL;
+
+ return dp->entries[dp->cursor++];
+}
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+rewinddir(DIR * dp)
+{
+ if (IS_DP(dp))
+ dp->cursor = 0;
+}
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+seekdir(DIR * dp, long offset)
+{
+ if (IS_DP(dp) && offset >= 0 && offset < dp->n_entries)
+ dp->cursor = offset;
+}
+
+ROKEN_LIB_FUNCTION long ROKEN_LIB_CALL
+telldir(DIR * dp)
+{
+ return dp->cursor;
+}
diff --git a/lib/roken/dirent.hin b/lib/roken/dirent.hin
new file mode 100644
index 000000000000..c9a86c63f5e5
--- /dev/null
+++ b/lib/roken/dirent.hin
@@ -0,0 +1,66 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#ifndef __DIRENT_H__
+#define __DIRENT_H__
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION
+#define ROKEN_LIB_CALL __cdecl
+#else
+#define ROKEN_LIB_FUNCTION
+#define ROKEN_LIB_CALL
+#endif
+#endif
+
+#include<sys/types.h>
+
+struct dirent {
+ ino_t d_ino;
+ char d_name[1];
+};
+
+typedef struct _dirent_dirinfo DIR;
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL closedir(DIR *);
+
+ROKEN_LIB_FUNCTION DIR * ROKEN_LIB_CALL opendir(const char *);
+
+ROKEN_LIB_FUNCTION struct dirent * ROKEN_LIB_CALL readdir(DIR *);
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rewinddir(DIR *);
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL seekdir(DIR *, long);
+
+ROKEN_LIB_FUNCTION long ROKEN_LIB_CALL telldir(DIR *);
+
+#endif
diff --git a/lib/roken/dlfcn.hin b/lib/roken/dlfcn.hin
new file mode 100644
index 000000000000..cc93bf366e1e
--- /dev/null
+++ b/lib/roken/dlfcn.hin
@@ -0,0 +1,92 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#ifndef __dlfcn_h__
+#define __dlfcn_h__
+
+#include <windows.h>
+
+#ifndef ROKEN_LIB_FUNCTION
+#ifdef _WIN32
+#define ROKEN_LIB_FUNCTION
+#define ROKEN_LIB_CALL __cdecl
+#else
+#define ROKEN_LIB_FUNCTION
+#define ROKEN_LIB_CALL
+#endif
+#endif
+
+#ifdef _WIN32
+typedef int (__stdcall *DLSYM_RET_TYPE)();
+#else
+#define DLSYM_RET_TYPE void *
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Implementation based on
+ http://www.opengroup.org/onlinepubs/009695399/basedefs/dlfcn.h.html */
+
+#define RTLD_LAZY (1<<0)
+
+#define RTLD_NOW (1<<1)
+
+#define RTLD_GLOBAL (1<<2)
+
+#define RTLD_LOCAL (1<<3)
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+dlclose(void *);
+
+ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
+dlerror(void);
+
+ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
+dlopen(const char *, int);
+
+ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
+dlsym(void *, const char *);
+
+typedef struct Dl_info {
+ char *dli_fname;
+ char _dli_buf[MAX_PATH + 2];
+} Dl_info, Dl_info_t;
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+dladdr(void *addr, Dl_info *dli);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* __dlfcn_h__ */
diff --git a/lib/roken/dlfcn_w32.c b/lib/roken/dlfcn_w32.c
new file mode 100644
index 000000000000..96cea138241e
--- /dev/null
+++ b/lib/roken/dlfcn_w32.c
@@ -0,0 +1,185 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <config.h>
+#include <windows.h>
+#include <dlfcn.h>
+#include <strsafe.h>
+
+#define ERR_STR_LEN 256
+
+static volatile LONG dlfcn_tls = TLS_OUT_OF_INDEXES;
+
+static DWORD get_tl_error_slot(void)
+{
+ if (dlfcn_tls == TLS_OUT_OF_INDEXES) {
+ DWORD slot = TlsAlloc();
+ DWORD old_slot;
+
+ if (slot == TLS_OUT_OF_INDEXES)
+ return dlfcn_tls;
+
+ if ((old_slot = InterlockedCompareExchange(&dlfcn_tls, slot,
+ TLS_OUT_OF_INDEXES)) !=
+ TLS_OUT_OF_INDEXES) {
+
+ /* Lost a race */
+ TlsFree(slot);
+ return old_slot;
+ } else {
+ return slot;
+ }
+ }
+
+ return dlfcn_tls;
+}
+
+static void set_error(const char * e)
+{
+ char * s;
+ char * old_s;
+ size_t len;
+
+ DWORD slot = get_tl_error_slot();
+
+ if (slot == TLS_OUT_OF_INDEXES)
+ return;
+
+ len = strlen(e) * sizeof(char) + sizeof(char);
+ s = LocalAlloc(LMEM_FIXED, len);
+ if (s == NULL)
+ return;
+
+ old_s = (char *) TlsGetValue(slot);
+ TlsSetValue(slot, (LPVOID) s);
+
+ if (old_s != NULL)
+ LocalFree(old_s);
+}
+
+static void set_error_from_last(void) {
+ DWORD slot = get_tl_error_slot();
+ char * s = NULL;
+ char * old_s;
+
+ if (slot == TLS_OUT_OF_INDEXES)
+ return;
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ 0, GetLastError(), 0,
+ (LPTSTR) &s, 0,
+ NULL);
+ if (s == NULL)
+ return;
+
+ old_s = (char *) TlsGetValue(slot);
+ TlsSetValue(slot, (LPVOID) s);
+
+ if (old_s != NULL)
+ LocalFree(old_s);
+}
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+dlclose(void * vhm)
+{
+ BOOL brv;
+
+ brv = FreeLibrary((HMODULE) vhm);
+ if (!brv) {
+ set_error_from_last();
+ }
+ return !brv;
+}
+
+ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
+dlerror(void)
+{
+ DWORD slot = get_tl_error_slot();
+
+ if (slot == TLS_OUT_OF_INDEXES)
+ return NULL;
+
+ return (char *) TlsGetValue(slot);
+}
+
+ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
+dlopen(const char *fn, int flags)
+{
+ HMODULE hm;
+ UINT old_error_mode;
+
+ /* We don't support dlopen(0, ...) on Windows.*/
+ if ( fn == NULL ) {
+ set_error("Not implemented");
+ return NULL;
+ }
+
+ old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS);
+
+ hm = LoadLibraryEx(fn, 0, LOAD_WITH_ALTERED_SEARCH_PATH);
+
+ if (hm == NULL) {
+ set_error_from_last();
+ }
+
+ SetErrorMode(old_error_mode);
+
+ return (void *) hm;
+}
+
+ROKEN_LIB_FUNCTION DLSYM_RET_TYPE ROKEN_LIB_CALL
+dlsym(void * vhm, const char * func_name)
+{
+ HMODULE hm = (HMODULE) vhm;
+
+ return (DLSYM_RET_TYPE)(ULONG_PTR)GetProcAddress(hm, func_name);
+}
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+dladdr(void *addr, Dl_info *dli)
+{
+ HMODULE hm;
+ DWORD nsize;
+
+ memset(dli, 0, sizeof(*dli));
+ dli->dli_fname = dli->_dli_buf;
+
+ if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ (LPCTSTR)(ULONG_PTR)addr, &hm))
+ return 0;
+
+ nsize = GetModuleFileName(hm, dli->_dli_buf, sizeof(dli->_dli_buf));
+ dli->_dli_buf[sizeof(dli->_dli_buf) - 1] = '\0';
+ if (nsize >= sizeof(dli->_dli_buf))
+ return 0; /* truncated? can't be... */
+ return 1;
+}
diff --git a/lib/roken/dumpdata.c b/lib/roken/dumpdata.c
index 844360187f87..252744e28e2f 100644
--- a/lib/roken/dumpdata.c
+++ b/lib/roken/dumpdata.c
@@ -81,14 +81,16 @@ rk_undumpdata(const char *filename, void **buf, size_t *size)
sret = net_read(fd, *buf, *size);
if (sret < 0)
ret = errno;
- else if (sret != (ssize_t)*size) {
+ else if (sret != (ssize_t)*size)
ret = EINVAL;
- free(*buf);
- *buf = NULL;
- } else
+ else
ret = 0;
- out:
+ out:
+ if (ret) {
+ free(*buf);
+ *buf = NULL;
+ }
close(fd);
return ret;
}
diff --git a/lib/roken/err.hin b/lib/roken/err.hin
index 96fe5cf8515f..c5343c49adea 100644
--- a/lib/roken/err.hin
+++ b/lib/roken/err.hin
@@ -58,33 +58,33 @@
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
verr(int eval, const char *fmt, va_list ap)
- __attribute__ ((noreturn, format (printf, 2, 0)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 0)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
err(int eval, const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 2, 3)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 3)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
verrx(int eval, const char *fmt, va_list ap)
- __attribute__ ((noreturn, format (printf, 2, 0)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 0)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
errx(int eval, const char *fmt, ...)
- __attribute__ ((noreturn, format (printf, 2, 3)));
+ __attribute__ ((__noreturn__, __format__ (__printf__, 2, 3)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
vwarn(const char *fmt, va_list ap)
- __attribute__ ((format (printf, 1, 0)));
+ __attribute__ ((__format__ (__printf__, 1, 0)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
warn(const char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__ ((__format__ (__printf__, 1, 2)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
vwarnx(const char *fmt, va_list ap)
- __attribute__ ((format (printf, 1, 0)));
+ __attribute__ ((__format__ (__printf__, 1, 0)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
warnx(const char *fmt, ...)
- __attribute__ ((format (printf, 1, 2)));
+ __attribute__ ((__format__ (__printf__, 1, 2)));
#endif /* __ERR_H__ */
diff --git a/lib/roken/getarg.3 b/lib/roken/getarg.3
index dda6e7dbf3d3..0d01d8f33f5f 100644
--- a/lib/roken/getarg.3
+++ b/lib/roken/getarg.3
@@ -334,7 +334,7 @@ the options come in the correct order.
.Pp
Options with multiple arguments should be handled better.
.Pp
-Should be integreated with SL.
+Should be integrated with SL.
.Pp
It's very confusing that the struct you pass in is called getargS.
.Sh SEE ALSO
diff --git a/lib/roken/getarg.c b/lib/roken/getarg.c
index d6a504868959..76f3c808a2a3 100644
--- a/lib/roken/getarg.c
+++ b/lib/roken/getarg.c
@@ -595,7 +595,7 @@ struct getargs args[] = {
int main(int argc, char **argv)
{
int goptind = 0;
- while(getarg(args, 5, argc, argv, &goptind))
+ while (getarg(args, 5, argc, argv, &goptind))
printf("Bad arg: %s\n", argv[goptind]);
printf("flag1 = %d\n", flag1);
printf("flag2 = %d\n", flag2);
diff --git a/lib/roken/getarg.cat3 b/lib/roken/getarg.cat3
index d9eddffd8bfc..6e08f7e4843e 100644
--- a/lib/roken/getarg.cat3
+++ b/lib/roken/getarg.cat3
@@ -221,7 +221,7 @@ BBUUGGSS
Options with multiple arguments should be handled better.
- Should be integreated with SL.
+ Should be integrated with SL.
It's very confusing that the struct you pass in is called getargS.
diff --git a/lib/roken/getcap.c b/lib/roken/getcap.c
index 42f8dc07d80d..a8dd94bef9a9 100644
--- a/lib/roken/getcap.c
+++ b/lib/roken/getcap.c
@@ -52,9 +52,6 @@
#include <unistd.h>
#define BFRAG 1024
-#if 0
-#define BSIZE 1024
-#endif
#define ESC ('[' & 037) /* ASCII ESC */
#define MAX_RECURSION 32 /* maximum getent recursion */
#define SFRAG 100 /* cgetstr mallocs in SFRAG chunks */
@@ -67,15 +64,6 @@ static size_t topreclen; /* toprec length */
static char *toprec; /* Additional record specified by cgetset() */
static int gottoprec; /* Flag indicating retrieval of toprecord */
-#if 0 /*
- * Don't use db support unless it's build into libc but we don't
- * check for that now, so just disable the code.
- */
-#if defined(HAVE_DBOPEN) && defined(HAVE_DB_H)
-#define USE_DB
-#endif
-#endif
-
#ifdef USE_DB
static int cdbget (DB *, char **, const char *);
#endif
@@ -88,10 +76,6 @@ ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL cgetcap(char *buf, const char *cap, int
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetent(char **buf, char **db_array, const char *name);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetmatch(const char *buf, const char *name);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetclose(void);
-#if 0
-int cgetfirst(char **buf, char **db_array);
-int cgetnext(char **bp, char **db_array);
-#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetstr(char *buf, const char *cap, char **str);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetustr(char *buf, const char *cap, char **str);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL cgetnum(char *buf, const char *cap, long *num);
@@ -250,12 +234,12 @@ getent(char **cap, size_t *len, char **db_array, int fd,
* Check if we have a top record from cgetset().
*/
if (depth == 0 && toprec != NULL && cgetmatch(toprec, name) == 0) {
- size_t len = topreclen + BFRAG;
- if ((record = malloc (len)) == NULL) {
+ size_t tmplen = topreclen + BFRAG;
+ if ((record = malloc (tmplen)) == NULL) {
errno = ENOMEM;
return (-2);
}
- (void)strlcpy(record, toprec, len);
+ (void)strlcpy(record, toprec, tmplen);
db_p = db_array;
rp = record + topreclen + 1;
r_end = rp + BFRAG;
@@ -296,6 +280,7 @@ getent(char **cap, size_t *len, char **db_array, int fd,
!= NULL) {
free(record);
retval = cdbget(capdbp, &record, name);
+ /* record is no longer for us to free here */
if (retval < 0) {
/* no record available */
(void)capdbp->close(capdbp);
@@ -416,17 +401,20 @@ getent(char **cap, size_t *len, char **db_array, int fd,
*/
if (rp >= r_end) {
u_int pos;
+ char *tmp;
size_t newsize;
pos = rp - record;
newsize = r_end - record + BFRAG;
- record = realloc(record, newsize);
- if (record == NULL) {
+ tmp = realloc(record, newsize);
+ if (tmp == NULL) {
errno = ENOMEM;
if (myfd)
(void)close(fd);
+ free(record);
return (-2);
}
+ record = tmp;
r_end = record + newsize;
rp = record + pos;
}
@@ -464,8 +452,10 @@ getent(char **cap, size_t *len, char **db_array, int fd,
break;
}
- if (!foundit)
+ if (!foundit) {
+ free(record);
return (-1);
+ }
/*
* Got the capability record, but now we have to expand all tc=name
@@ -557,19 +547,22 @@ getent(char **cap, size_t *len, char **db_array, int fd,
if (diff >= r_end - rp) {
u_int pos, tcpos, tcposend;
size_t newsize;
+ char *tmp;
pos = rp - record;
newsize = r_end - record + diff + BFRAG;
tcpos = tcstart - record;
tcposend = tcend - record;
- record = realloc(record, newsize);
- if (record == NULL) {
+ tmp = realloc(record, newsize);
+ if (tmp == NULL) {
errno = ENOMEM;
if (myfd)
(void)close(fd);
free(icap);
+ free(record);
return (-2);
}
+ record = tmp;
r_end = record + newsize;
rp = record + pos;
tcstart = record + tcpos;
@@ -600,12 +593,15 @@ getent(char **cap, size_t *len, char **db_array, int fd,
if (myfd)
(void)close(fd);
*len = rp - record - 1; /* don't count NUL */
- if (r_end > rp)
- if ((record =
- realloc(record, (size_t)(rp - record))) == NULL) {
+ if (r_end > rp) {
+ char *tmp = realloc(record, (size_t)(rp - record));
+ if (tmp == NULL) {
errno = ENOMEM;
+ free(record);
return (-2);
}
+ record = tmp;
+ }
*cap = record;
if (tc_not_resolved)
@@ -687,15 +683,6 @@ cgetmatch(const char *buf, const char *name)
}
}
-#if 0
-int
-cgetfirst(char **buf, char **db_array)
-{
- (void)cgetclose();
- return (cgetnext(buf, db_array));
-}
-#endif
-
static FILE *pfp;
static int slash;
static char **dbp;
@@ -713,131 +700,6 @@ cgetclose(void)
return(0);
}
-#if 0
-/*
- * Cgetnext() gets either the first or next entry in the logical database
- * specified by db_array. It returns 0 upon completion of the database, 1
- * upon returning an entry with more remaining, and -1 if an error occurs.
- */
-int
-cgetnext(char **bp, char **db_array)
-{
- size_t len;
- int status, done;
- char *cp, *line, *rp, *np, buf[BSIZE], nbuf[BSIZE];
- size_t dummy;
-
- if (dbp == NULL)
- dbp = db_array;
-
- if (pfp == NULL && (pfp = fopen(*dbp, "r")) == NULL) {
- (void)cgetclose();
- return (-1);
- }
- for(;;) {
- if (toprec && !gottoprec) {
- gottoprec = 1;
- line = toprec;
- } else {
- line = fgetln(pfp, &len);
- if (line == NULL && pfp) {
- if (ferror(pfp)) {
- (void)cgetclose();
- return (-1);
- } else {
- (void)fclose(pfp);
- pfp = NULL;
- if (*++dbp == NULL) {
- (void)cgetclose();
- return (0);
- } else if ((pfp =
- fopen(*dbp, "r")) == NULL) {
- (void)cgetclose();
- return (-1);
- } else
- continue;
- }
- } else
- line[len - 1] = '\0';
- if (len == 1) {
- slash = 0;
- continue;
- }
- if (isspace((unsigned char)*line) ||
- *line == ':' || *line == '#' || slash) {
- if (line[len - 2] == '\\')
- slash = 1;
- else
- slash = 0;
- continue;
- }
- if (line[len - 2] == '\\')
- slash = 1;
- else
- slash = 0;
- }
-
-
- /*
- * Line points to a name line.
- */
- done = 0;
- np = nbuf;
- for (;;) {
- for (cp = line; *cp != '\0'; cp++) {
- if (*cp == ':') {
- *np++ = ':';
- done = 1;
- break;
- }
- if (*cp == '\\')
- break;
- *np++ = *cp;
- }
- if (done) {
- *np = '\0';
- break;
- } else { /* name field extends beyond the line */
- line = fgetln(pfp, &len);
- if (line == NULL && pfp) {
- if (ferror(pfp)) {
- (void)cgetclose();
- return (-1);
- }
- (void)fclose(pfp);
- pfp = NULL;
- *np = '\0';
- break;
- } else
- line[len - 1] = '\0';
- }
- }
- rp = buf;
- for(cp = nbuf; *cp != '\0'; cp++)
- if (*cp == '|' || *cp == ':')
- break;
- else
- *rp++ = *cp;
-
- *rp = '\0';
- /*
- * XXX
- * Last argument of getent here should be nbuf if we want true
- * sequential access in the case of duplicates.
- * With NULL, getent will return the first entry found
- * rather than the duplicate entry record. This is a
- * matter of semantics that should be resolved.
- */
- status = getent(bp, &dummy, db_array, -1, buf, 0, NULL);
- if (status == -2 || status == -3)
- (void)cgetclose();
-
- return (status + 1);
- }
- /* NOTREACHED */
-}
-#endif
-
/*
* Cgetstr retrieves the value of the string capability cap from the
* capability record pointed to by buf. A pointer to a decoded, NUL
@@ -1036,9 +898,14 @@ cgetustr(char *buf, const char *cap, char **str)
/*
* Give back any extra memory and return value and success.
*/
- if (m_room != 0)
- if ((mem = realloc(mem, (size_t)(mp - mem))) == NULL)
+ if (m_room != 0) {
+ char *tmp = realloc(mem, (size_t)(mp - mem));
+ if (tmp == NULL) {
+ free(mem);
return (-2);
+ }
+ mem = tmp;
+ }
*str = mem;
return (len);
}
diff --git a/lib/roken/getifaddrs_w32.c b/lib/roken/getifaddrs_w32.c
new file mode 100644
index 000000000000..beb29b397a34
--- /dev/null
+++ b/lib/roken/getifaddrs_w32.c
@@ -0,0 +1,161 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include<config.h>
+
+#include <roken.h>
+
+#include <ifaddrs.h>
+
+#ifndef _WIN32
+#error This is a Windows specific implementation.
+#endif
+
+static struct sockaddr *
+dupaddr(const sockaddr_gen * src)
+{
+ sockaddr_gen * d = malloc(sizeof(*d));
+
+ if (d) {
+ memcpy(d, src, sizeof(*d));
+ }
+
+ return (struct sockaddr *) d;
+}
+
+int ROKEN_LIB_FUNCTION
+rk_getifaddrs(struct ifaddrs **ifpp)
+{
+ SOCKET s = INVALID_SOCKET;
+ size_t il_len = 8192;
+ int ret = -1;
+ INTERFACE_INFO *il = NULL;
+
+ *ifpp = NULL;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == INVALID_SOCKET)
+ return -1;
+
+ for (;;) {
+ DWORD cbret = 0;
+
+ il = malloc(il_len);
+ if (!il)
+ break;
+
+ ZeroMemory(il, il_len);
+
+ if (WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0,
+ (LPVOID) il, (DWORD) il_len, &cbret,
+ NULL, NULL) == 0) {
+ il_len = cbret;
+ break;
+ }
+
+ free (il);
+ il = NULL;
+
+ if (WSAGetLastError() == WSAEFAULT && cbret > il_len) {
+ il_len = cbret;
+ } else {
+ break;
+ }
+ }
+
+ if (!il)
+ goto _exit;
+
+ /* il is an array of INTERFACE_INFO structures. il_len has the
+ actual size of the buffer. The number of elements is
+ il_len/sizeof(INTERFACE_INFO) */
+
+ {
+ size_t n = il_len / sizeof(INTERFACE_INFO);
+ size_t i;
+
+ for (i = 0; i < n; i++ ) {
+ struct ifaddrs *ifp;
+
+ ifp = malloc(sizeof(*ifp));
+ if (ifp == NULL)
+ break;
+
+ ZeroMemory(ifp, sizeof(*ifp));
+
+ ifp->ifa_next = NULL;
+ ifp->ifa_name = NULL;
+ ifp->ifa_flags = il[i].iiFlags;
+ ifp->ifa_addr = dupaddr(&il[i].iiAddress);
+ ifp->ifa_netmask = dupaddr(&il[i].iiNetmask);
+ ifp->ifa_broadaddr = dupaddr(&il[i].iiBroadcastAddress);
+ ifp->ifa_data = NULL;
+
+ *ifpp = ifp;
+ ifpp = &ifp->ifa_next;
+ }
+
+ if (i == n)
+ ret = 0;
+ }
+
+ _exit:
+
+ if (s != INVALID_SOCKET)
+ closesocket(s);
+
+ if (il)
+ free (il);
+
+ return ret;
+}
+
+void ROKEN_LIB_FUNCTION
+rk_freeifaddrs(struct ifaddrs *ifp)
+{
+ struct ifaddrs *p, *q;
+
+ for(p = ifp; p; ) {
+ if (p->ifa_name)
+ free(p->ifa_name);
+ if(p->ifa_addr)
+ free(p->ifa_addr);
+ if(p->ifa_dstaddr)
+ free(p->ifa_dstaddr);
+ if(p->ifa_netmask)
+ free(p->ifa_netmask);
+ if(p->ifa_data)
+ free(p->ifa_data);
+ q = p;
+ p = p->ifa_next;
+ free(q);
+ }
+}
diff --git a/lib/roken/getxxyyy.c b/lib/roken/getxxyyy.c
new file mode 100644
index 000000000000..5beed69df6de
--- /dev/null
+++ b/lib/roken/getxxyyy.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2011 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include "roken.h"
+
+#ifdef TEST_GETXXYYY
+#undef rk_getpwnam_r
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+rk_getpwnam_r(const char *, struct passwd *, char *, size_t, struct passwd **);
+#endif
+
+#if !defined(POSIX_GETPWNAM_R) || defined(TEST_GETXXYYY)
+
+/*
+ * At least limit the race between threads
+ */
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+rk_getpwnam_r(const char *name, struct passwd *pwd, char *buffer,
+ size_t bufsize, struct passwd **result)
+{
+ struct passwd *p;
+ size_t slen, n = 0;
+
+ *result = NULL;
+
+ p = getpwnam(name);
+ if(p == NULL)
+ return (errno = ENOENT);
+
+ memset(pwd, 0, sizeof(*pwd));
+
+#define APPEND(el) \
+do { \
+ slen = strlen(p->el) + 1; \
+ if (slen > bufsize) return (errno = ENOMEM); \
+ memcpy(buffer, p->el, slen); \
+ pwd->el = buffer; \
+ buffer += slen; \
+ bufsize -= slen; \
+} while(0)
+
+ APPEND(pw_name);
+ if (p->pw_passwd)
+ APPEND(pw_name);
+ pwd->pw_uid = p->pw_uid;
+ pwd->pw_gid = p->pw_gid;
+ APPEND(pw_gecos);
+ APPEND(pw_dir);
+ APPEND(pw_shell);
+
+ *result = pwd;
+
+ return 0;
+}
+
+#endif /* POSIX_GETPWNAM_R */
+
+#ifdef TEST_GETXXYYY
+
+#include <err.h>
+
+int verbose_flag = 0;
+
+static void
+print_result(struct passwd *p)
+{
+ if (!verbose_flag)
+ return;
+ printf("%s\n", p->pw_name);
+ printf("%d\n", (int)p->pw_uid);
+ printf("%s\n", p->pw_shell);
+ printf("%s\n", p->pw_dir);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct passwd pwd, *result;
+ char buf[1024];
+ int ret;
+ const char *user;
+
+ user = getenv("USER");
+ if (!user)
+ user = "root";
+
+ ret = rk_getpwnam_r(user, &pwd, buf, sizeof(buf), &result);
+ if (ret)
+ errx(1, "rk_getpwnam_r");
+ print_result(result);
+
+ ret = rk_getpwnam_r(user, &pwd, buf, 1, &result);
+ if (ret == 0)
+ errx(1, "rk_getpwnam_r too small buf");
+
+ ret = rk_getpwnam_r("no-user-here-promise", &pwd, buf, sizeof(buf), &result);
+ if (ret == 0)
+ errx(1, "rk_getpwnam_r no user");
+
+ return 0;
+}
+
+#endif
diff --git a/lib/roken/glob.c b/lib/roken/glob.c
index 5a7ed65af809..03bc2a4dd613 100644
--- a/lib/roken/glob.c
+++ b/lib/roken/glob.c
@@ -404,7 +404,7 @@ static int
glob0(const Char *pattern, glob_t *pglob)
{
const Char *qpatnext;
- int c, err, oldpathc;
+ int c, ret, oldpathc;
Char *bufnext, patbuf[MaxPathLen+1];
size_t limit = 0;
@@ -464,8 +464,8 @@ glob0(const Char *pattern, glob_t *pglob)
qprintf("glob0:", patbuf);
#endif
- if ((err = glob1(patbuf, pglob, &limit)) != 0)
- return(err);
+ if ((ret = glob1(patbuf, pglob, &limit)) != 0)
+ return(ret);
/*
* If there was no match we are going to append the pattern
@@ -572,7 +572,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pattern, Char *restpattern,
{
struct dirent *dp;
DIR *dirp;
- int err;
+ int ret;
char buf[MaxPathLen];
/*
@@ -597,7 +597,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pattern, Char *restpattern,
return(0);
}
- err = 0;
+ ret = 0;
/* Search directory for matching names. */
if (pglob->gl_flags & GLOB_ALTDIRFUNC)
@@ -618,8 +618,8 @@ glob3(Char *pathbuf, Char *pathend, Char *pattern, Char *restpattern,
*pathend = CHAR_EOS;
continue;
}
- err = glob2(pathbuf, --dc, restpattern, pglob, limit);
- if (err)
+ ret = glob2(pathbuf, --dc, restpattern, pglob, limit);
+ if (ret)
break;
}
@@ -627,7 +627,7 @@ glob3(Char *pathbuf, Char *pathend, Char *pattern, Char *restpattern,
(*pglob->gl_closedir)(dirp);
else
closedir(dirp);
- return(err);
+ return(ret);
}
diff --git a/lib/roken/hex-test.c b/lib/roken/hex-test.c
index 9a3d10f2870b..a81422e1f4a5 100644
--- a/lib/roken/hex-test.c
+++ b/lib/roken/hex-test.c
@@ -55,7 +55,7 @@ main(int argc, char **argv)
{ "abcdef", 6, "616263646566" },
{ "abcdefg", 7, "61626364656667" },
{ "=", 1, "3D" },
- { NULL }
+ { NULL, 0, NULL }
};
for(t = tests; t->data; t++) {
char *str;
diff --git a/lib/roken/issuid.c b/lib/roken/issuid.c
index ea0db803e2e5..7a77b3834461 100644
--- a/lib/roken/issuid.c
+++ b/lib/roken/issuid.c
@@ -33,24 +33,183 @@
#include <config.h>
+#ifdef HAVE_SYS_AUXV_H
+#include <sys/auxv.h>
+#endif
+
+#include <errno.h>
+
#include "roken.h"
+/* NetBSD calls AT_UID AT_RUID. Everyone else calls it AT_UID. */
+#if defined(AT_EUID) && defined(AT_RUID) && !defined(AT_UID)
+#define AT_UID AT_RUID
+#endif
+#if defined(AT_EGID) && defined(AT_RGID) && !defined(AT_GID)
+#define AT_GID AT_RGID
+#endif
+
+#ifdef __GLIBC__
+#ifdef __GLIBC_PREREQ
+#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) __GLIBC_PREREQ(maj, min)
+#else
+#define HAVE_GLIBC_API_VERSION_SUPPORT(maj, min) \
+ ((__GLIBC << 16) + GLIBC_MINOR >= ((maj) << 16) + (min))
+#endif
+
+#if HAVE_GLIBC_API_VERSION_SUPPORT(2, 19)
+#define GETAUXVAL_SETS_ERRNO
+#endif
+#endif
+
+#ifdef HAVE_GETAUXVAL
+static unsigned long
+rk_getauxval(unsigned long type)
+{
+ errno = 0;
+#ifdef GETAUXVAL_SETS_ERRNO
+ return getauxval(type);
+#else
+ unsigned long ret = getauxval(type);
+
+ if (ret == 0)
+ errno = ENOENT;
+ return ret;
+#endif
+}
+#define USE_RK_GETAUXVAL
+#endif
+
+/**
+ * Returns non-zero if the caller's process started as set-uid or
+ * set-gid (and therefore the environment cannot be trusted).
+ *
+ * @return Non-zero if the environment is not trusted.
+ */
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
issuid(void)
{
+ /*
+ * We want to use issetugid(), but issetugid() is not the same on
+ * all OSes.
+ *
+ * On Illumos derivatives, OpenBSD, and Solaris issetugid() returns
+ * true IFF the program exec()ed was set-uid or set-gid.
+ *
+ * On NetBSD and FreeBSD issetugid() returns true if the program
+ * exec()ed was set-uid or set-gid, or if the process has switched
+ * UIDs/GIDs or otherwise changed privileges or is a descendant of
+ * such a process and has not exec()ed since.
+ *
+ * What we want here is to know only if the program exec()ed was
+ * set-uid or set-gid, so we can decide whether to trust the
+ * enviroment variables. We don't care if this was a process that
+ * started as root and later changed UIDs/privs whatever: since it
+ * started out as privileged, it inherited an environment from a
+ * privileged pre-exec self, and so on, so the environment is
+ * trusted.
+ *
+ * Therefore the FreeBSD/NetBSD issetugid() does us no good.
+ *
+ * Linux, meanwhile, has no issetugid() (at least glibc doesn't
+ * anyways).
+ *
+ * Systems that support ELF put an "auxilliary vector" on the stack
+ * prior to starting the RTLD, and this vector includes (optionally)
+ * information about the process' EUID, RUID, EGID, RGID, and so on
+ * at the time of exec(), which we can use to construct proper
+ * issetugid() functionality.
+ *
+ * Where available, we use the ELF auxilliary vector as a fallback
+ * if issetugid() is not available.
+ *
+ * All of this is as of late March 2015, and might become stale in
+ * the future.
+ */
+
+#ifdef USE_RK_GETAUXVAL
+ /* If we have getauxval(), use that */
+
+#if (defined(AT_EUID) && defined(AT_UID) || (defined(AT_EGID) && defined(AT_GID)))
+ int seen = 0;
+#endif
+
+#if defined(AT_EUID) && defined(AT_UID)
+ {
+ unsigned long euid;
+ unsigned long uid;
+
+ euid = rk_getauxval(AT_EUID);
+ if (errno == 0)
+ seen |= 1;
+ uid = rk_getauxval(AT_UID);
+ if (errno == 0)
+ seen |= 2;
+ if (euid != uid)
+ return 1;
+ }
+#endif
+#if defined(AT_EGID) && defined(AT_GID)
+ {
+ unsigned long egid;
+ unsigned long gid;
+
+ egid = rk_getauxval(AT_EGID);
+ if (errno == 0)
+ seen |= 4;
+ gid = rk_getauxval(AT_GID);
+ if (errno == 0)
+ seen |= 8;
+ if (egid != gid)
+ return 2;
+ }
+#endif
+#ifdef AT_SECURE
+ /* AT_SECURE is set if the program was set-id. */
+ if (rk_getauxval(AT_SECURE) != 0)
+ return 1;
+#endif
+
+#if (defined(AT_EUID) && defined(AT_UID) || (defined(AT_EGID) && defined(AT_GID)))
+ if (seen == 15)
+ return 0;
+#endif
+
+ /* rk_getauxval() does set errno */
+ if (errno == 0)
+ return 0;
+ /*
+ * Fall through if we have getauxval() but we didn't have (or don't
+ * know if we don't have) the aux entries that we needed.
+ */
+#endif /* USE_RK_GETAUXVAL */
+
#if defined(HAVE_ISSETUGID)
+ /*
+ * If we have issetugid(), use it.
+ *
+ * We may lose on some BSDs. This manifests as, for example,
+ * gss_store_cred() not honoring KRB5CCNAME.
+ */
return issetugid();
-#else /* !HAVE_ISSETUGID */
+#endif /* USE_RK_GETAUXVAL */
+
+ /*
+ * Paranoia: for extra safety we ought to default to returning 1.
+ * But who knows what that might break where users link statically
+ * and use a.out, say. Also, on Windows we should always return 0.
+ *
+ * For now we stick to returning zero by default.
+ */
#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
- if(getuid() != geteuid())
+ if (getuid() != geteuid())
return 1;
#endif
#if defined(HAVE_GETGID) && defined(HAVE_GETEGID)
- if(getgid() != getegid())
+ if (getgid() != getegid())
return 2;
#endif
return 0;
-#endif /* HAVE_ISSETUGID */
}
diff --git a/lib/heimdal/dllmain.c b/lib/roken/memset_s.c
index 3f4d9b3ef0ba..207fe19f5e20 100644
--- a/lib/heimdal/dllmain.c
+++ b/lib/roken/memset_s.c
@@ -1,5 +1,5 @@
/***********************************************************************
- * Copyright (c) 2009, Secure Endpoints Inc.
+ * Copyright (c) 2015, Your File System Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,23 @@
*
**********************************************************************/
-#include<windows.h>
+#include <config.h>
+#include "roken.h"
-BOOL WINAPI
-DllMain(__in HINSTANCE hinstDLL,
- __in DWORD fdwReason,
- __in LPVOID lpvReserved)
+int ROKEN_LIB_FUNCTION
+memset_s(void *s, size_t smax, int c, size_t n)
{
- return TRUE;
+ volatile unsigned char *p = s;
+
+#ifdef _WIN32
+ if (c == 0) {
+ SecureZeroMemory(s, n);
+ return 0;
+ }
+#endif
+
+ while (n--)
+ *p++ = c;
+
+ return 0;
}
diff --git a/lib/roken/mkdir.c b/lib/roken/mkdir.c
new file mode 100644
index 000000000000..918ff65c610f
--- /dev/null
+++ b/lib/roken/mkdir.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+
+#include "roken.h"
+#ifdef MKDIR_DOES_NOT_HAVE_MODE
+ #undef mkdir
+#else
+ #undef rk_mkdir
+#endif
+
+int ROKEN_LIB_FUNCTION
+rk_mkdir(const char *pathname, mode_t mode)
+{
+#ifndef MKDIR_DOES_NOT_HAVE_MODE
+ return mkdir(pathname, mode);
+#else
+ /* Windows does not provide the ability to set access permissions */
+ return _mkdir(pathname);
+#endif
+}
diff --git a/lib/roken/ndbm_wrap.c b/lib/roken/ndbm_wrap.c
index 73a3726d0f02..f7b87f1203e9 100644
--- a/lib/roken/ndbm_wrap.c
+++ b/lib/roken/ndbm_wrap.c
@@ -111,20 +111,20 @@ static datum
dbm_get (DB *db, int flags)
{
DBT key, value;
- datum datum;
+ datum d;
#ifdef HAVE_DB3
if(cursor == NULL)
db->cursor(db, NULL, &cursor, 0);
if(cursor->c_get(cursor, &key, &value, flags) != 0) {
- datum.dptr = NULL;
- datum.dsize = 0;
+ d.dptr = NULL;
+ d.dsize = 0;
} else
- DBT2DATUM(&value, &datum);
+ DBT2DATUM(&value, &d);
#else
db->seq(db, &key, &value, flags);
- DBT2DATUM(&value, &datum);
+ DBT2DATUM(&value, &d);
#endif
- return datum;
+ return d;
}
#ifndef DB_FIRST
diff --git a/lib/roken/parse_bytes-test.c b/lib/roken/parse_bytes-test.c
index 3c42cb1827cb..87f2fa8984aa 100644
--- a/lib/roken/parse_bytes-test.c
+++ b/lib/roken/parse_bytes-test.c
@@ -63,7 +63,6 @@ main(int argc, char **argv)
for (i = 0; i < sizeof(tests)/sizeof(tests[0]); ++i) {
char buf[256];
int val = parse_bytes (tests[i].str, tests[i].def_unit);
- int len;
if (val != tests[i].val) {
printf ("parse_bytes (%s, %s) = %d != %d\n",
@@ -73,7 +72,7 @@ main(int argc, char **argv)
++ret;
}
if (tests[i].canonicalp) {
- len = unparse_bytes (tests[i].val, buf, sizeof(buf));
+ (void) unparse_bytes (tests[i].val, buf, sizeof(buf));
if (strcmp (tests[i].str, buf) != 0) {
printf ("unparse_bytes (%d) = \"%s\" != \"%s\"\n",
tests[i].val, buf, tests[i].str);
diff --git a/lib/roken/parse_time-test.c b/lib/roken/parse_time-test.c
index 851764d28e56..a2dfb22f9805 100644
--- a/lib/roken/parse_time-test.c
+++ b/lib/roken/parse_time-test.c
@@ -49,7 +49,8 @@ static struct testcase {
{ 8, 60, "1 minute" },
{ 6, 3600, "1 hour" },
{ 15, 3601, "1 hour 1 second" },
- { 16, 3602, "1 hour 2 seconds" }
+ { 16, 3602, "1 hour 2 seconds" },
+ { 9, 300, "5 minutes" },
};
int
diff --git a/lib/roken/parse_time.3 b/lib/roken/parse_time.3
index da326cb3bc21..7881eab5bb19 100644
--- a/lib/roken/parse_time.3
+++ b/lib/roken/parse_time.3
@@ -28,9 +28,9 @@
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
-.\" $Id$
+.\" $Id: parse_time.3,v 1.3 2013/06/17 18:57:45 robert Exp $
.\"
-.Dd October 31, 2004
+.Dd November 17, 2013
.Dt PARSE_TIME 3
.Os HEIMDAL
.Sh NAME
@@ -54,28 +54,28 @@ The roken library (libroken, -lroken)
.Sh DESCRIPTION
The
.Fn parse_time
-function converts a the period of time specified in
+function converts the period of time specified
into a number of seconds.
The
.Fa timespec
can be any number of
.Aq number unit
pairs separated by comma and whitespace. The number can be
-negative. Number without explicit units are taken as being
+negative. Numbers without explicit units are taken as being
.Fa def_unit .
.Pp
The
.Fn unparse_time
and
.Fn unparse_time_approx
-does the opposite of
+do the opposite of
.Fn parse_time ,
that is they take a number of seconds and express that as human
-readable string.
+readable strings.
.Fa unparse_time
produces an exact time, while
.Fa unparse_time_approx
-restricts the result to only include one units.
+restricts the result to include only one unit.
.Pp
.Fn print_time_table
prints a descriptive list of available units on the passed file
diff --git a/lib/roken/parse_time.cat3 b/lib/roken/parse_time.cat3
index c2c2e6125ffd..ca1c51ff4916 100644
--- a/lib/roken/parse_time.cat3
+++ b/lib/roken/parse_time.cat3
@@ -24,15 +24,15 @@ SSYYNNOOPPSSIISS
uunnppaarrssee__ttiimmee__aapppprrooxx(_i_n_t _s_e_c_o_n_d_s, _c_h_a_r _*_b_u_f, _s_i_z_e___t _l_e_n);
DDEESSCCRRIIPPTTIIOONN
- The ppaarrssee__ttiimmee() function converts a the period of time specified in into
- a number of seconds. The _t_i_m_e_s_p_e_c can be any number of <number unit>
- pairs separated by comma and whitespace. The number can be negative. Num-
- ber without explicit units are taken as being _d_e_f___u_n_i_t.
+ The ppaarrssee__ttiimmee() function converts the period of time specified into a
+ number of seconds. The _t_i_m_e_s_p_e_c can be any number of <number unit> pairs
+ separated by comma and whitespace. The number can be negative. Numbers
+ without explicit units are taken as being _d_e_f___u_n_i_t.
- The uunnppaarrssee__ttiimmee() and uunnppaarrssee__ttiimmee__aapppprrooxx() does the opposite of
+ The uunnppaarrssee__ttiimmee() and uunnppaarrssee__ttiimmee__aapppprrooxx() do the opposite of
ppaarrssee__ttiimmee(), that is they take a number of seconds and express that as
- human readable string. _u_n_p_a_r_s_e___t_i_m_e produces an exact time, while
- _u_n_p_a_r_s_e___t_i_m_e___a_p_p_r_o_x restricts the result to only include one units.
+ human readable strings. _u_n_p_a_r_s_e___t_i_m_e produces an exact time, while
+ _u_n_p_a_r_s_e___t_i_m_e___a_p_p_r_o_x restricts the result to include only one unit.
pprriinntt__ttiimmee__ttaabbllee() prints a descriptive list of available units on the
passed file descriptor.
@@ -109,4 +109,4 @@ BBUUGGSS
hack for English plural forms. If these functions are ever localised,
this scheme will have to change.
-HEIMDAL October 31, 2004 HEIMDAL
+HEIMDAL November 17, 2013 HEIMDAL
diff --git a/lib/roken/rand.c b/lib/roken/rand.c
index ef92c2052b78..03239d7dc244 100644
--- a/lib/roken/rand.c
+++ b/lib/roken/rand.c
@@ -30,9 +30,14 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
+#include <config.h>
#include "roken.h"
+#ifdef HAVE_WIN32_RAND_S
+static int hasRand_s = 1;
+#endif
+
void ROKEN_LIB_FUNCTION
rk_random_init(void)
{
@@ -43,6 +48,32 @@ rk_random_init(void)
#elif defined(HAVE_RANDOM)
srandom(time(NULL));
#else
+# ifdef HAVE_WIN32_RAND_S
+ OSVERSIONINFO osInfo;
+
+ osInfo.dwOSVersionInfoSize = sizeof(osInfo);
+ hasRand_s =
+ (GetVersionEx(&osInfo)
+ && ((osInfo.dwMajorVersion > 5) ||
+ (osInfo.dwMajorVersion == 5) && (osInfo.dwMinorVersion >= 1)));
+# endif
srand (time(NULL));
#endif
}
+
+#ifdef HAVE_WIN32_RAND_S
+unsigned int ROKEN_LIB_FUNCTION
+rk_random(void)
+{
+ if (hasRand_s) {
+ unsigned int n;
+ int code;
+
+ code = rand_s(&n);
+ if (code == 0)
+ return n;
+ }
+
+ return rand();
+}
+#endif
diff --git a/lib/roken/realloc.c b/lib/roken/realloc.c
index 8cbc0d63e06d..fefa90d62e62 100644
--- a/lib/roken/realloc.c
+++ b/lib/roken/realloc.c
@@ -36,6 +36,7 @@
#include <stdlib.h>
#include "roken.h"
+#undef realloc
ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
rk_realloc(void *ptr, size_t size)
diff --git a/lib/roken/rename.c b/lib/roken/rename.c
new file mode 100644
index 000000000000..d7348d30a4fe
--- /dev/null
+++ b/lib/roken/rename.c
@@ -0,0 +1,49 @@
+/***********************************************************************
+ * Copyright (c) 2010, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <config.h>
+#include "roken.h"
+
+/* rename() for platforms where the native implementation doesn't
+ * unlink newname. */
+int rk_rename(const char * oldname, const char * newname)
+{
+ int ret;
+
+ ret = rename(oldname, newname);
+ if (ret != 0 && (errno == EEXIST || errno == EACCES)) {
+ ret = unlink(newname);
+ if (ret == 0)
+ ret = rename(oldname, newname);
+ }
+
+ return ret;
+}
diff --git a/lib/roken/resolve-test.c b/lib/roken/resolve-test.c
index 711d6e896e74..25cc98aafe3a 100644
--- a/lib/roken/resolve-test.c
+++ b/lib/roken/resolve-test.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1995 - 2004 Kungliga Tekniska Högskolan
+ * Copyright (c) 1995 - 2016 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
@@ -36,6 +36,7 @@
#include "roken.h"
#include "getarg.h"
+#include <assert.h>
#ifdef HAVE_ARPA_NAMESER_H
#include <arpa/nameser.h>
#endif
@@ -44,11 +45,15 @@
#endif
#include "resolve.h"
+static int srv_rr_order = 1;
static int loop_integer = 1;
static int version_flag = 0;
static int help_flag = 0;
static struct getargs args[] = {
+ {"srv-rr-order", 0,
+ arg_negative_flag, &srv_rr_order,
+ "do not test SRV RR ordering", NULL },
{"loop", 0, arg_integer, &loop_integer,
"loop resolving", NULL },
{"version", 0, arg_flag, &version_flag,
@@ -67,6 +72,107 @@ usage (int ret)
exit (ret);
}
+#define NUMRRS 16
+
+static
+int
+test_rk_dns_srv_order(size_t run)
+{
+ struct rk_dns_reply reply;
+ struct rk_resource_record rrs[NUMRRS];
+ struct rk_resource_record *rr;
+ struct rk_srv_record srvs[NUMRRS];
+ size_t i, prio0;
+ int fail = 0;
+
+ (void) memset(&reply, 0, sizeof(reply));
+ (void) memset(srvs, 0, sizeof(srvs));
+ (void) memset(rrs, 0, sizeof(rrs));
+
+ /* Test with two equal weight zero SRV records */
+ rrs[0].type = rk_ns_t_srv;
+ rrs[0].u.srv = &srvs[0];
+ rrs[0].next = &rrs[1];
+ srvs[0].priority = 10;
+ srvs[0].weight = 0;
+
+ rrs[1].type = rk_ns_t_srv;
+ rrs[1].u.srv = &srvs[1];
+ rrs[1].next = NULL;
+ srvs[1].priority = 10;
+ srvs[1].weight = 0;
+ reply.head = &rrs[0];
+
+ rk_dns_srv_order(&reply);
+ assert(reply.head != NULL);
+ printf("%p %p\n", &rrs[0], rrs[0].next);
+
+ /*
+ * Test four priority groups with priority 1--5 and weigths 0--3 in the
+ * first two groups, and 1--4 in the last two groups. Test multiple zero
+ * weights, by further coercing the weight to zero if <= run/2.
+ */
+ for (i = 0; i < NUMRRS; i++) {
+ rrs[i].type = rk_ns_t_srv;
+ rrs[i].u.srv = &srvs[i];
+ srvs[i].priority = 1 + i / 4;
+ srvs[i].weight = i % 4 + i / 8;
+ if (srvs[i].weight <= run/2)
+ srvs[i].weight = 0;
+ }
+ /* Shuffle the RRs */
+ for (i = 0; i < NUMRRS - 1; i++) {
+ struct rk_resource_record tmp;
+ size_t j = rk_random() % (NUMRRS - i);
+
+ if (j > 0) {
+ tmp = rrs[i+j];
+ rrs[i+j] = rrs[i];
+ rrs[i] = tmp;
+ }
+ }
+ for (i = 0; i < NUMRRS; i++)
+ rrs[i].next = &rrs[i + 1];
+ rrs[i - 1].next = NULL;
+ reply.head = &rrs[0];
+
+ for (i = 0, rr = reply.head; i < NUMRRS; i++) {
+ if (rr == NULL)
+ break;
+ printf("SRV RR order run %lu input: prio %lu weight %lu\n",
+ (unsigned long)run, (unsigned long)rr->u.srv->priority,
+ (unsigned long)rr->u.srv->weight);
+ rr = rr->next;
+ }
+
+ rk_dns_srv_order(&reply);
+ assert(reply.head != NULL);
+
+ /*
+ * After sorting, ensure monotone priority ordering with jumps by 1 at
+ * group boundaries.
+ */
+ prio0 = 0;
+ for (i = 0, rr = reply.head; i < NUMRRS; i++) {
+ if (rr == NULL)
+ break;
+ if (rr->u.srv->priority < prio0 ||
+ (rr->u.srv->priority != prio0 &&
+ (i % 4 != 0 || rr->u.srv->priority > prio0 + 1))) {
+ printf("SRV RR order run %lu failed\n", run);
+ fail = 1;
+ }
+ prio0 = rr->u.srv->priority;
+ printf("SRV RR order run %lu output: prio %lu weight %lu\n",
+ (unsigned long)run, (unsigned long)rr->u.srv->priority,
+ (unsigned long)rr->u.srv->weight);
+ rr = rr->next;
+ }
+ assert(i == NUMRRS);
+
+ return fail;
+}
+
int
main(int argc, char **argv)
{
@@ -75,14 +181,15 @@ main(int argc, char **argv)
int optidx = 0, i, exit_code = 0;
setprogname (argv[0]);
+ rk_random_init();
- if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
+ if (getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx))
usage(1);
if (help_flag)
usage (0);
- if(version_flag){
+ if (version_flag) {
printf("some version\n");
exit(0);
}
@@ -90,23 +197,38 @@ main(int argc, char **argv)
argc -= optidx;
argv += optidx;
- if (argc != 2)
+ if (argc != 2 && argc != 0 && !srv_rr_order)
usage(1);
+ if (srv_rr_order) {
+ exit_code += test_rk_dns_srv_order(0);
+ exit_code += test_rk_dns_srv_order(1);
+ exit_code += test_rk_dns_srv_order(2);
+ exit_code += test_rk_dns_srv_order(3);
+ exit_code += test_rk_dns_srv_order(4);
+ exit_code += test_rk_dns_srv_order(5);
+ }
+
+ if (srv_rr_order && argc == 0)
+ exit(exit_code ? 1 : 0);
+
+ if (argc != 2)
+ usage(1);
+
for (i = 0; i < loop_integer; i++) {
r = rk_dns_lookup(argv[0], argv[1]);
- if(r == NULL){
+ if (r == NULL) {
printf("No reply.\n");
exit_code = 1;
break;
}
- if(r->q.type == rk_ns_t_srv)
+ if (r->q.type == rk_ns_t_srv)
rk_dns_srv_order(r);
- for(rr = r->head; rr;rr=rr->next){
+ for (rr = r->head; rr;rr=rr->next) {
printf("%-30s %-5s %-6d ", rr->domain, rk_dns_type_to_string(rr->type), rr->ttl);
- switch(rr->type){
+ switch (rr->type) {
case rk_ns_t_ns:
case rk_ns_t_cname:
case rk_ns_t_ptr:
@@ -116,11 +238,11 @@ main(int argc, char **argv)
printf("%s\n", inet_ntoa(*rr->u.a));
break;
case rk_ns_t_mx:
- case rk_ns_t_afsdb:{
+ case rk_ns_t_afsdb: {
printf("%d %s\n", rr->u.mx->preference, rr->u.mx->domain);
break;
}
- case rk_ns_t_srv:{
+ case rk_ns_t_srv: {
struct rk_srv_record *srv = rr->u.srv;
printf("%d %d %d %s\n", srv->priority, srv->weight,
srv->port, srv->target);
@@ -130,45 +252,47 @@ main(int argc, char **argv)
printf("%s\n", rr->u.txt);
break;
}
- case rk_ns_t_sig : {
+ case rk_ns_t_sig: {
struct rk_sig_record *sig = rr->u.sig;
const char *type_string = rk_dns_type_to_string (sig->type);
- printf ("type %u (%s), algorithm %u, labels %u, orig_ttl %u, sig_expiration %u, sig_inception %u, key_tag %u, signer %s\n",
- sig->type, type_string ? type_string : "",
- sig->algorithm, sig->labels, sig->orig_ttl,
- sig->sig_expiration, sig->sig_inception, sig->key_tag,
- sig->signer);
+ printf("type %u (%s), algorithm %u, labels %u, orig_ttl %u, "
+ "sig_expiration %u, sig_inception %u, key_tag %u, "
+ "signer %s\n",
+ sig->type, type_string ? type_string : "",
+ sig->algorithm, sig->labels, sig->orig_ttl,
+ sig->sig_expiration, sig->sig_inception, sig->key_tag,
+ sig->signer);
break;
}
- case rk_ns_t_key : {
+ case rk_ns_t_key: {
struct rk_key_record *key = rr->u.key;
- printf ("flags %u, protocol %u, algorithm %u\n",
- key->flags, key->protocol, key->algorithm);
+ printf("flags %u, protocol %u, algorithm %u\n",
+ key->flags, key->protocol, key->algorithm);
break;
}
- case rk_ns_t_sshfp : {
+ case rk_ns_t_sshfp: {
struct rk_sshfp_record *sshfp = rr->u.sshfp;
- size_t i;
+ size_t j;
printf ("alg %u type %u length %lu data ", sshfp->algorithm,
sshfp->type, (unsigned long)sshfp->sshfp_len);
- for (i = 0; i < sshfp->sshfp_len; i++)
- printf("%02X", sshfp->sshfp_data[i]);
+ for (j = 0; j < sshfp->sshfp_len; j++)
+ printf("%02X", sshfp->sshfp_data[j]);
printf("\n");
break;
}
- case rk_ns_t_ds : {
+ case rk_ns_t_ds: {
struct rk_ds_record *ds = rr->u.ds;
- size_t i;
+ size_t j;
- printf ("key tag %u alg %u type %u length %lu data ",
- ds->key_tag, ds->algorithm, ds->digest_type,
- (unsigned long)ds->digest_len);
- for (i = 0; i < ds->digest_len; i++)
- printf("%02X", ds->digest_data[i]);
+ printf("key tag %u alg %u type %u length %lu data ",
+ ds->key_tag, ds->algorithm, ds->digest_type,
+ (unsigned long)ds->digest_len);
+ for (j = 0; j < ds->digest_len; j++)
+ printf("%02X", ds->digest_data[j]);
printf("\n");
break;
@@ -181,5 +305,5 @@ main(int argc, char **argv)
rk_dns_free_data(r);
}
- return exit_code;
+ return exit_code ? 1 : 0;
}
diff --git a/lib/roken/resolve.c b/lib/roken/resolve.c
index 2eeaaf344018..98be0cb87f8a 100644
--- a/lib/roken/resolve.c
+++ b/lib/roken/resolve.c
@@ -659,40 +659,59 @@ rk_dns_srv_order(struct rk_dns_reply *r)
headp = &r->head;
- for(ss = srvs; ss < srvs + num_srv; ) {
- int sum, rnd, count;
+ for (ss = srvs; ss < srvs + num_srv; ) {
+ int sum, zeros, rnd, count; /* zeros -> weight scaling */
struct rk_resource_record **ee, **tt;
- /* find the last record with the same priority and count the
- sum of all weights */
- for(sum = 0, tt = ss; tt < srvs + num_srv; tt++) {
+
+ /*
+ * find the last record with the same priority and count the sum of all
+ * weights
+ */
+ for (sum = 0, zeros = 0, tt = ss; tt < srvs + num_srv; tt++) {
assert(*tt != NULL);
if((*tt)->u.srv->priority != (*ss)->u.srv->priority)
break;
sum += (*tt)->u.srv->weight;
+ if ((*tt)->u.srv->weight == 0)
+ zeros++;
}
+ /* make sure scale (`zeros') is > 0 then scale out */
+ sum += zeros ? 1 : zeros++;
+ sum *= zeros;
ee = tt;
- /* ss is now the first record of this priority and ee is the
- first of the next */
- while(ss < ee) {
- rnd = rk_random() % (sum + 1);
- for(count = 0, tt = ss; ; tt++) {
- if(*tt == NULL)
- continue;
- count += (*tt)->u.srv->weight;
- if(count >= rnd)
+
+ /*
+ * ss is now the first record of this priority and ee is the first of
+ * the next or the first past the end of srvs
+ */
+ while (ss < ee) {
+ rnd = rk_random() % sum + 1;
+ for (count = 0, tt = ss; tt < ee; tt++) {
+ if (*tt == NULL)
+ continue; /* this one's already been picked */
+ if ((*tt)->u.srv->weight == 0)
+ count++;
+ else
+ count += (*tt)->u.srv->weight * zeros;
+ if (count >= rnd)
break;
}
-
assert(tt < ee);
- /* insert the selected record at the tail (of the head) of
- the list */
+ /* push the selected record */
(*tt)->next = *headp;
*headp = *tt;
headp = &(*tt)->next;
- sum -= (*tt)->u.srv->weight;
+ /*
+ * reduce the sum so the next iteration is sure to reach the random
+ * total after examining all the remaining records.
+ */
+ if ((*tt)->u.srv->weight == 0)
+ sum--;
+ else
+ sum -= (*tt)->u.srv->weight * zeros;
*tt = NULL;
- while(ss < ee && *ss == NULL)
+ while (ss < ee && *ss == NULL)
ss++;
}
}
diff --git a/lib/roken/rkpty.c b/lib/roken/rkpty.c
index f2c62f23f39c..619b7b6391a5 100644
--- a/lib/roken/rkpty.c
+++ b/lib/roken/rkpty.c
@@ -99,7 +99,7 @@ open_pty(void)
printf("implement open_pty\n");
exit(77);
#endif
-#if defined(HAVE_OPENPTY) || defined(__linux) || defined(__osf__) /* XXX */
+#if defined(HAVE_OPENPTY) || defined(__osf__) /* XXX */
if(openpty(&master, &slave, line, 0, 0) == 0)
return;
#endif /* HAVE_OPENPTY .... */
@@ -233,7 +233,7 @@ eval_parent(pid_t pid)
c->str, c->lineno);
else if (alarmset)
errx(1, "got a signal %d waiting for %s (line %u)",
- alarmset, c->str, c->lineno);
+ (int)alarmset, c->str, c->lineno);
if (sret <= 0)
errx(1, "end command while waiting for %s (line %u)",
c->str, c->lineno);
@@ -305,9 +305,9 @@ eval_parent(pid_t pid)
static struct getargs args[] = {
{ "timeout", 't', arg_integer, &timeout, "timout", "seconds" },
- { "verbose", 'v', arg_counter, &verbose, "verbose debugging" },
- { "version", 0, arg_flag, &version_flag, "print version" },
- { "help", 0, arg_flag, &help_flag, NULL }
+ { "verbose", 'v', arg_counter, &verbose, "verbose debugging", NULL },
+ { "version", 0, arg_flag, &version_flag, "print version", NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
static void
diff --git a/lib/roken/roken-common.h b/lib/roken/roken-common.h
index a819d510d031..4766538a0d38 100644
--- a/lib/roken/roken-common.h
+++ b/lib/roken/roken-common.h
@@ -122,7 +122,15 @@
#define O_ACCMODE 003
#endif
-#ifndef _WIN32
+#ifndef O_NOFOLLOW
+#define O_NOFOLLOW 0
+#endif
+
+#ifdef _WIN32
+
+#define _PATH_DEVNULL "\\\\.\\NUL"
+
+#else
#ifndef _PATH_DEV
#define _PATH_DEV "/dev/"
@@ -151,7 +159,11 @@
#endif /* !_WIN32 */
#ifndef PATH_MAX
+#ifdef MAX_PATH
#define PATH_MAX MAX_PATH
+#else
+#define PATH_MAX 4096
+#endif
#endif
#ifndef RETSIGTYPE
@@ -401,6 +413,10 @@ socket_set_debug (rk_socket_t);
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
socket_set_tos (rk_socket_t, int);
+#define socket_set_nonblocking rk_socket_set_nonblocking
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+socket_set_nonblocking(rk_socket_t, int);
+
#define socket_set_reuseaddr rk_socket_set_reuseaddr
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
socket_set_reuseaddr (rk_socket_t, int);
@@ -452,7 +468,7 @@ free_environment(char **);
#define warnerr rk_warnerr
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_warnerr(int doerrno, const char *fmt, va_list ap)
- __attribute__ ((format (printf, 2, 0)));
+ __attribute__ ((__format__ (__printf__, 2, 0)));
ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
rk_realloc(void *, size_t);
@@ -464,7 +480,7 @@ rk_strpoolcollect(struct rk_strpool *);
ROKEN_LIB_FUNCTION struct rk_strpool * ROKEN_LIB_CALL
rk_strpoolprintf(struct rk_strpool *, const char *, ...)
- __attribute__ ((format (printf, 2, 3)));
+ __attribute__ ((__format__ (__printf__, 2, 3)));
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_strpoolfree(struct rk_strpool *);
@@ -487,12 +503,20 @@ rk_cloexec_file(FILE *);
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
rk_cloexec_dir(DIR *);
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+rk_cloexec_socket(rk_socket_t);
+
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
-ct_memcmp(const void *, const void *, size_t);
+ct_memcmp(const volatile void * volatile,
+ const volatile void * volatile,
+ size_t);
void ROKEN_LIB_FUNCTION
rk_random_init(void);
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+rk_mkdir(const char *, mode_t);
+
ROKEN_CPP_END
#endif /* __ROKEN_COMMON_H__ */
diff --git a/lib/roken/roken.awk b/lib/roken/roken.awk
index 47ae1960a4cb..47313c843af2 100644
--- a/lib/roken/roken.awk
+++ b/lib/roken/roken.awk
@@ -9,6 +9,9 @@ BEGIN {
print "#ifdef HAVE_SYS_SOCKET_H"
print "#include <sys/socket.h>"
print "#endif"
+ print "#ifdef HAVE_ERRNO_H"
+ print "#include <errno.h>"
+ print "#endif"
print ""
print "int main(int argc, char **argv)"
print "{"
diff --git a/lib/roken/roken.h.in b/lib/roken/roken.h.in
index ab8c8741ab09..1a9db635686f 100644
--- a/lib/roken/roken.h.in
+++ b/lib/roken/roken.h.in
@@ -3,22 +3,22 @@
* Copyright (c) 1995-2005 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
- *
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
- *
+ *
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
- *
+ *
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
- *
+ *
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
@@ -32,6 +32,12 @@
* SUCH DAMAGE.
*/
+#if defined(_WIN32) && _MSC_VER >= 1400
+/* _CRT_RAND_S must be defined before including stdlib.h */
+# define _CRT_RAND_S
+# define HAVE_WIN32_RAND_S 1
+#endif
+
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -39,6 +45,7 @@
#include <stdint.h>
#endif
#include <string.h>
+#include <limits.h>
#include <signal.h>
#ifndef ROKEN_LIB_FUNCTION
@@ -65,10 +72,8 @@
#include <ws2tcpip.h>
/*
- * error codes for inet_ntop/inet_pton
+ * error codes for inet_ntop/inet_pton
*/
-#define EAFNOSUPPORT WSAEAFNOSUPPORT
-
typedef SOCKET rk_socket_t;
#define rk_closesocket(x) closesocket(x)
@@ -79,23 +84,6 @@ typedef SOCKET rk_socket_t;
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_SOCK_IOCTL(SOCKET s, long cmd, int * argp);
-/* Microsoft VC 2010 POSIX definitions */
-#ifndef ENOTSOCK
-#define ENOTSOCK 128
-#endif
-#ifndef ENOTSUP
-#define ENOTSUP 129
-#endif
-#ifndef EOVERFLOW
-#define EOVERFLOW 132
-#endif
-#ifndef ETIMEDOUT
-#define ETIMEDOUT 138
-#endif
-#ifndef EWOULDBLOCK
-#define EWOULDBLOCK 140
-#endif
-
#define rk_SOCK_INIT() rk_WSAStartup()
#define rk_SOCK_EXIT() rk_WSACleanup()
@@ -116,13 +104,17 @@ typedef int rk_socket_t;
#define rk_SOCK_INIT() 0
#define rk_SOCK_EXIT() do { } while(0)
-#endif
+#endif /* WinSock */
#ifndef IN_LOOPBACKNET
#define IN_LOOPBACKNET 127
#endif
#ifdef _MSC_VER
+#ifndef HAVE_STDINT_H
+#include <intsafe.h>
+#endif
+
/* Declarations for Microsoft Visual C runtime on Windows */
#include<process.h>
@@ -196,6 +188,9 @@ struct sockaddr_dl;
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
#ifdef HAVE_SYS_BITYPES_H
#include <sys/bitypes.h>
#endif
@@ -274,6 +269,10 @@ struct sockaddr_dl;
#include <dirent.h>
#endif
+#ifdef HAVE_DIRECT_H
+#include <direct.h>
+#endif
+
#ifdef BACKSLASH_PATH_DELIM
#define rk_PATH_DELIM '\\'
#endif
@@ -367,6 +366,49 @@ rk_vsnprintf (char *str, size_t sz, const char *format, va_list args);
S_ISBLK(m)
*/
+/* The following symbolic constants are provided for rk_mkdir mode */
+
+#define S_IRWXU 00700 /* user (file owner) has read, write and execute permission */
+#define S_IRUSR 00400 /* user has read permission */
+#define S_IWUSR 00200 /* user has write permission */
+#define S_IXUSR 00100 /* user has execute permission */
+#define S_IRWXG 00070 /* group has read, write and execute permission */
+#define S_IRGRP 00040 /* group has read permission */
+#define S_IWGRP 00020 /* group has write permission */
+#define S_IXGRP 00010 /* group has execute permission */
+#define S_IRWXO 00007 /* others have read, write and execute permission */
+#define S_IROTH 00004 /* others have read permission */
+#define S_IWOTH 00002 /* others have write permission */
+#define S_IXOTH 00001 /* others have execute permission */
+
+#if !defined(ROKEN_NO_DEFINE_ALLOCATORS)
+/* Ensure that a common memory allocator is used by all */
+#define calloc rk_calloc
+#define free rk_free
+#define malloc rk_malloc
+#define realloc rk_realloc
+#define strdup rk_strdup
+#define wcsdup rk_wcsdup
+#endif
+
+ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
+rk_calloc(size_t, size_t);
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+rk_free(void *);
+
+ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
+rk_malloc(size_t);
+
+ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
+rk_realloc(void *, size_t);
+
+ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
+rk_strdup(const char *);
+
+ROKEN_LIB_FUNCTION unsigned short * ROKEN_LIB_CALL
+rk_wcsdup(const unsigned short *);
+
#endif /* _MSC_VER */
#ifdef HAVE_WINSOCK
@@ -429,16 +471,16 @@ ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL endusershell(void);
#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rk_snprintf (char *, size_t, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
+ __attribute__ ((__format__ (__printf__, 3, 4)));
#endif
#if !defined(HAVE_VSNPRINTF) || defined(NEED_VSNPRINTF_PROTO)
#ifndef HAVE_VSNPRINTF
#define vsnprintf rk_vsnprintf
#endif
-ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rk_vsnprintf (char *, size_t, const char *, va_list)
- __attribute__((format (printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
#endif
#if !defined(HAVE_ASPRINTF) || defined(NEED_ASPRINTF_PROTO)
@@ -447,7 +489,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rk_asprintf (char **, const char *, ...)
- __attribute__ ((format (printf, 2, 3)));
+ __attribute__ ((__format__ (__printf__, 2, 3)));
#endif
#if !defined(HAVE_VASPRINTF) || defined(NEED_VASPRINTF_PROTO)
@@ -456,7 +498,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rk_vasprintf (char **, const char *, va_list)
- __attribute__((format (printf, 2, 0)));
+ __attribute__ ((__format__ (__printf__, 2, 0)));
#endif
#if !defined(HAVE_ASNPRINTF) || defined(NEED_ASNPRINTF_PROTO)
@@ -465,7 +507,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rk_asnprintf (char **, size_t, const char *, ...)
- __attribute__ ((format (printf, 3, 4)));
+ __attribute__ ((__format__ (__printf__, 3, 4)));
#endif
#if !defined(HAVE_VASNPRINTF) || defined(NEED_VASNPRINTF_PROTO)
@@ -474,7 +516,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
vasnprintf (char **, size_t, const char *, va_list)
- __attribute__((format (printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
#endif
#ifndef HAVE_STRDUP
@@ -601,6 +643,13 @@ ROKEN_LIB_FUNCTION struct passwd * ROKEN_LIB_CALL k_getpwnam (const char *);
ROKEN_LIB_FUNCTION struct passwd * ROKEN_LIB_CALL k_getpwuid (uid_t);
#endif
+#ifdef POSIX_GETPWNAM_R
+#define rk_getpwnam_r(_n, _pw, _b, _sz, _pwd) getpwnam_r(_n, _pw, _b, _sz, _pwd)
+#else
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+rk_getpwnam_r(const char *, struct passwd *, char *, size_t, struct passwd **);
+#endif
+
ROKEN_LIB_FUNCTION const char * ROKEN_LIB_CALL get_default_username (void);
#ifndef HAVE_SETEUID
@@ -648,6 +697,13 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL rk_rename(const char *, const char *);
#define rk_rename(__rk_rn_from,__rk_rn_to) rename(__rk_rn_from,__rk_rn_to)
#endif
+#ifdef MKDIR_DOES_NOT_HAVE_MODE
+#define mkdir rk_mkdir
+#else
+#define rk_mkdir(__rk_rn_name, __rk_rn_mode) mkdir(__rk_rn_name,__rk_rn_mode)
+#endif
+
+
#if !defined(HAVE_DAEMON) || defined(NEED_DAEMON_PROTO)
#ifndef HAVE_DAEMON
#define daemon rk_daemon
@@ -677,7 +733,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL innetgr(const char*, const char*,
#ifndef HAVE_IRUSEROK
#define iruserok rk_iruserok
-ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL iruserok(unsigned, int,
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL iruserok(unsigned, int,
const char *, const char *);
#endif
@@ -700,13 +756,15 @@ ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL
readv(int, const struct iovec *, int);
#endif
-#ifndef HAVE_PIDFILE
#ifdef NO_PIDFILES
-#define pidfile(x) ((void) 0)
+#define rk_pidfile(x) ((void) 0)
#else
-#define pidfile rk_pidfile
-ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL pidfile (const char*);
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL rk_pidfile (const char*);
#endif
+
+#ifndef HAVE_BSWAP64
+#define bswap64 rk_bswap64
+ROKEN_LIB_FUNCTION uint64_t ROKEN_LIB_CALL bswap64(uint64_t);
#endif
#ifndef HAVE_BSWAP32
@@ -760,6 +818,9 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL roken_vconcat (char *, size_t, va_list);
ROKEN_LIB_FUNCTION size_t ROKEN_LIB_CALL
roken_vmconcat (char **, size_t, va_list);
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL roken_detach_prep(int, char **, char *);
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL roken_detach_finish(const char *, int);
+
ROKEN_LIB_FUNCTION ssize_t ROKEN_LIB_CALL
net_write (rk_socket_t, const void *, size_t);
@@ -923,6 +984,9 @@ gai_strerror(int);
ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
sleep(unsigned int seconds);
+ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
+usleep(unsigned int useconds);
+
#endif
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
@@ -932,7 +996,7 @@ getnameinfo_verified(const struct sockaddr *, socklen_t,
int);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
-roken_getaddrinfo_hostspec(const char *, int, struct addrinfo **);
+roken_getaddrinfo_hostspec(const char *, int, struct addrinfo **);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
roken_getaddrinfo_hostspec2(const char *, int, int, struct addrinfo **);
@@ -979,7 +1043,7 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
roken_gethostby_setup(const char*, const char*);
ROKEN_LIB_FUNCTION struct hostent* ROKEN_LIB_CALL
roken_gethostbyname(const char*);
-ROKEN_LIB_FUNCTION struct hostent* ROKEN_LIB_CALL
+ROKEN_LIB_FUNCTION struct hostent* ROKEN_LIB_CALL
roken_gethostbyaddr(const void*, size_t, int);
#ifdef GETSERVBYNAME_PROTO_COMPATIBLE
@@ -1026,6 +1090,22 @@ ROKEN_LIB_FUNCTION struct tm * ROKEN_LIB_CALL
localtime_r(const time_t *, struct tm *);
#endif
+#if !defined(HAVE_STRTOLL) || defined(NEED_STRTOLL_PROTO)
+#ifndef HAVE_STRTOLL
+#define strtoll rk_strtoll
+#endif
+ROKEN_LIB_FUNCTION long long ROKEN_LIB_CALL
+strtoll(const char * nptr, char ** endptr, int base);
+#endif
+
+#if !defined(HAVE_STRTOULL) || defined(NEED_STRTOULL_PROTO)
+#ifndef HAVE_STRTOULL
+#define strtoull rk_strtoull
+#endif
+ROKEN_LIB_FUNCTION unsigned long long ROKEN_LIB_CALL
+strtoull(const char * nptr, char ** endptr, int base);
+#endif
+
#if !defined(HAVE_STRSVIS) || defined(NEED_STRSVIS_PROTO)
#ifndef HAVE_STRSVIS
#define strsvis rk_strsvis
@@ -1108,12 +1188,23 @@ void
rk_qsort(void *, size_t, size_t, int (*)(const void *, const void *));
#endif
+#ifndef HAVE_MEMSET_S
+#define memset_s rk_memset_s
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL memset_s(void *s, size_t smax,
+ int c, size_t n);
+#endif
+
#if defined(HAVE_ARC4RANDOM)
-#define rk_random() arc4random()
+# define rk_random() arc4random()
#elif defined(HAVE_RANDOM)
-#define rk_random() random()
+# define rk_random() random()
#else
-#define rk_random() rand()
+# ifdef HAVE_WIN32_RAND_S
+ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
+rk_random(void);
+# else
+# define rk_random() rand()
+# endif
#endif
#ifndef HAVE_TDELETE
@@ -1135,6 +1226,33 @@ rk_qsort(void *, size_t, size_t, int (*)(const void *, const void *));
int ROKEN_LIB_FUNCTION rk_socket(int, int, int);
#endif
+/* Microsoft VC 2010 POSIX definitions */
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT 102
+#endif
+#ifndef EINPROGRESS
+#define EINPROGRESS 112
+#endif
+#ifndef ELOOP
+#define ELOOP 114
+#endif
+#ifndef ENOTSOCK
+#define ENOTSOCK 128
+#endif
+#ifndef ENOTSUP
+#define ENOTSUP 129
+#endif
+#ifndef EOVERFLOW
+#define EOVERFLOW 132
+#endif
+#ifndef ETIMEDOUT
+#define ETIMEDOUT 138
+#endif
+#ifndef EWOULDBLOCK
+#define EWOULDBLOCK 140
+#endif
+
+
#ifdef SOCKET_WRAPPER_REPLACE
#include <socket_wrapper.h>
#endif
diff --git a/lib/roken/roken_gethostby.c b/lib/roken/roken_gethostby.c
index 1bb560d3baf4..a2dda7e05a51 100644
--- a/lib/roken/roken_gethostby.c
+++ b/lib/roken/roken_gethostby.c
@@ -77,7 +77,8 @@ setup_int(const char *proxy_host, short proxy_port,
if(make_address(dns_host, &dns_addr.sin_addr) != 0)
return -1;
dns_addr.sin_port = htons(dns_port);
- asprintf(&dns_req, "%s", dns_path);
+ if (asprintf(&dns_req, "%s", dns_path) < 0)
+ return -1;
}
dns_addr.sin_family = AF_INET;
return 0;
diff --git a/lib/roken/rtbl.c b/lib/roken/rtbl.c
index fe0fde662b86..efcf55e58898 100644
--- a/lib/roken/rtbl.c
+++ b/lib/roken/rtbl.c
@@ -34,6 +34,7 @@
#include <config.h>
#include "roken.h"
+#include <ctype.h>
#include "rtbl.h"
struct column_entry {
@@ -168,7 +169,7 @@ rtbl_new_row(rtbl_t table)
if(table->columns[c]->num_rows == max_rows)
continue;
tmp = realloc(table->columns[c]->rows,
- max_rows * sizeof(table->columns[c]->rows));
+ max_rows * sizeof(table->columns[c]->rows[0]));
if(tmp == NULL)
return ENOMEM;
table->columns[c]->rows = tmp;
@@ -188,7 +189,7 @@ column_compute_width (rtbl_t table, struct column_data *column)
if(table->flags & RTBL_HEADER_STYLE_NONE)
column->width = 0;
else
- column->width = strlen (column->header);
+ column->width = (int)strlen (column->header);
for (i = 0; i < column->num_rows; i++)
column->width = max (column->width, (int) strlen (column->rows[i].data));
}
@@ -362,6 +363,18 @@ rtbl_add_column_entryv (rtbl_t table, const char *column, const char *fmt, ...)
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_format (rtbl_t table, FILE * f)
{
+ char *str = rtbl_format_str(table);
+ if (str == NULL)
+ return ENOMEM;
+ fprintf(f, "%s", str);
+ free(str);
+ return 0;
+}
+
+static char *
+rtbl_format_pretty(rtbl_t table)
+{
+ struct rk_strpool *p = NULL;
size_t i, j;
for (i = 0; i < table->num_columns; i++)
@@ -371,16 +384,18 @@ rtbl_format (rtbl_t table, FILE * f)
struct column_data *c = table->columns[i];
if(table->column_separator != NULL && i > 0)
- fprintf (f, "%s", table->column_separator);
- fprintf (f, "%s", get_column_prefix (table, c));
- if(i == table->num_columns - 1 && c->suffix == NULL)
+ p = rk_strpoolprintf(p, "%s", table->column_separator);
+ p = rk_strpoolprintf(p, "%s", get_column_prefix (table, c));
+ if (c == NULL) {
+ /* do nothing if no column */
+ } else if(i == table->num_columns - 1 && c->suffix == NULL)
/* last column, so no need to pad with spaces */
- fprintf (f, "%-*s", 0, c->header);
+ p = rk_strpoolprintf(p, "%-*s", 0, c->header);
else
- fprintf (f, "%-*s", (int)c->width, c->header);
- fprintf (f, "%s", get_column_suffix (table, c));
+ p = rk_strpoolprintf(p, "%-*s", (int)c->width, c->header);
+ p = rk_strpoolprintf(p, "%s", get_column_suffix (table, c));
}
- fprintf (f, "\n");
+ p = rk_strpoolprintf(p, "\n");
}
for (j = 0;; j++) {
@@ -403,7 +418,7 @@ rtbl_format (rtbl_t table, FILE * f)
struct column_data *c = table->columns[i];
if(table->column_separator != NULL && i > 0)
- fprintf (f, "%s", table->column_separator);
+ p = rk_strpoolprintf(p, "%s", table->column_separator);
w = c->width;
@@ -414,16 +429,72 @@ rtbl_format (rtbl_t table, FILE * f)
else
w = -w;
}
- fprintf (f, "%s", get_column_prefix (table, c));
+ p = rk_strpoolprintf(p, "%s", get_column_prefix (table, c));
if (c->num_rows <= j)
- fprintf (f, "%*s", w, "");
+ p = rk_strpoolprintf(p, "%*s", w, "");
else
- fprintf (f, "%*s", w, c->rows[j].data);
- fprintf (f, "%s", get_column_suffix (table, c));
+ p = rk_strpoolprintf(p, "%*s", w, c->rows[j].data);
+ p = rk_strpoolprintf(p, "%s", get_column_suffix (table, c));
}
- fprintf (f, "\n");
+ p = rk_strpoolprintf(p, "\n");
}
- return 0;
+
+ return rk_strpoolcollect(p);
+}
+
+static char *
+rtbl_format_json(rtbl_t table)
+{
+ struct rk_strpool *p = NULL;
+ size_t i, j;
+ int comma;
+
+ p = rk_strpoolprintf(p, "[");
+ for (j = 0;; j++) {
+ int flag = 0;
+
+ /* are there any more rows left? */
+ for (i = 0; flag == 0 && i < table->num_columns; ++i) {
+ struct column_data *c = table->columns[i];
+
+ if (c->num_rows > j) {
+ ++flag;
+ break;
+ }
+ }
+ if (flag == 0)
+ break;
+
+ p = rk_strpoolprintf(p, "%s{", j > 0 ? "," : "");
+
+ comma = 0;
+ for (i = 0; i < table->num_columns; i++) {
+ struct column_data *c = table->columns[i];
+
+ if (c->num_rows > j) {
+ char *header = c->header;
+ while (isspace((int)header[0])) /* trim off prefixed whitespace */
+ header++;
+ p = rk_strpoolprintf(p, "%s\"%s\" : \"%s\"",
+ comma ? "," : "", header,
+ c->rows[j].data);
+ comma = 1;
+ }
+ }
+ p = rk_strpoolprintf(p, "}");
+ }
+ p = rk_strpoolprintf(p, "]");
+
+ return rk_strpoolcollect(p);
+}
+
+ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
+rtbl_format_str (rtbl_t table)
+{
+ if (table->flags & RTBL_JSON)
+ return rtbl_format_json(table);
+
+ return rtbl_format_pretty(table);
}
#ifdef TEST
diff --git a/lib/roken/rtbl.h b/lib/roken/rtbl.h
index 549d3a8aa415..274f75c123f4 100644
--- a/lib/roken/rtbl.h
+++ b/lib/roken/rtbl.h
@@ -61,6 +61,7 @@ typedef struct rtbl_data *rtbl_t;
/* flags */
#define RTBL_HEADER_STYLE_NONE 1
+#define RTBL_JSON 2
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column (rtbl_t, const char*, unsigned int);
@@ -71,14 +72,14 @@ rtbl_add_column_by_id (rtbl_t, unsigned int, const char*, unsigned int);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entryv_by_id (rtbl_t table, unsigned int id,
const char *fmt, ...)
- __attribute__ ((format (printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entry (rtbl_t, const char*, const char*);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entryv (rtbl_t, const char*, const char*, ...)
- __attribute__ ((format (printf, 3, 0)));
+ __attribute__ ((__format__ (__printf__, 3, 0)));
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_add_column_entry_by_id (rtbl_t, unsigned int, const char*);
@@ -92,6 +93,9 @@ rtbl_destroy (rtbl_t);
ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
rtbl_format (rtbl_t, FILE*);
+ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
+rtbl_format_str (rtbl_t);
+
ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
rtbl_get_flags (rtbl_t);
diff --git a/lib/roken/simple_exec.c b/lib/roken/simple_exec.c
index 97679d7e4175..552e121b0d22 100644
--- a/lib/roken/simple_exec.c
+++ b/lib/roken/simple_exec.c
@@ -144,17 +144,31 @@ ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
const char *file, ...)
{
- int in_fd[2], out_fd[2], err_fd[2];
+ int in_fd[2] = {-1, -1};
+ int out_fd[2] = {-1, -1};
+ int err_fd[2] = {-1, -1};
pid_t pid;
va_list ap;
char **argv;
+ int ret = 0;
if(stdin_fd != NULL)
- pipe(in_fd);
- if(stdout_fd != NULL)
- pipe(out_fd);
- if(stderr_fd != NULL)
- pipe(err_fd);
+ ret = pipe(in_fd);
+ if(ret != -1 && stdout_fd != NULL)
+ ret = pipe(out_fd);
+ if(ret != -1 && stderr_fd != NULL)
+ ret = pipe(err_fd);
+
+ if (ret == -1) {
+ close(in_fd[0]);
+ close(in_fd[1]);
+ close(out_fd[0]);
+ close(out_fd[1]);
+ close(err_fd[0]);
+ close(err_fd[1]);
+ return SE_E_UNSPECIFIED;
+ }
+
pid = fork();
switch(pid) {
case 0:
diff --git a/lib/roken/simple_exec_w32.c b/lib/roken/simple_exec_w32.c
new file mode 100644
index 000000000000..264cdb2e9d24
--- /dev/null
+++ b/lib/roken/simple_exec_w32.c
@@ -0,0 +1,431 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <config.h>
+
+#include <roken.h>
+#include <strsafe.h>
+
+#ifndef _WIN32
+#error This implementation is Windows specific.
+#endif
+
+/**
+ * wait_for_process_timed waits for a process to terminate or until a
+ * specified timeout occurs.
+ *
+ * @param[in] pid Process id for the monitored process
+
+ * @param[in] func Timeout callback function. When the wait times out,
+ * the callback function is called. The possible return values
+ * from the callback function are:
+ *
+ * - ((time_t) -2) Exit loop without killing child and return SE_E_EXECTIMEOUT.
+ * - ((time_t) -1) Kill child with SIGTERM and wait for child to exit.
+ * - 0 Don't timeout again
+ * - n Seconds to next timeout
+ *
+ * @param[in] ptr Optional parameter for func()
+ *
+ * @param[in] timeout Seconds to first timeout.
+ *
+ * @retval SE_E_UNSPECIFIED Unspecified system error
+ * @retval SE_E_FORKFAILED Fork failure (not applicable for _WIN32 targets)
+ * @retval SE_E_WAITPIDFAILED waitpid errors
+ * @retval SE_E_EXECTIMEOUT exec timeout
+ * @retval 0 <= Return value from subprocess
+ * @retval SE_E_NOTFOUND The program coudln't be found
+ * @retval 128- The signal that killed the subprocess +128.
+ */
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+wait_for_process_timed(pid_t pid, time_t (*func)(void *),
+ void *ptr, time_t timeout)
+{
+ HANDLE hProcess;
+ DWORD wrv = 0;
+ DWORD dtimeout;
+ int rv = 0;
+
+ hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
+
+ if (hProcess == NULL) {
+ return SE_E_WAITPIDFAILED;
+ }
+
+ dtimeout = (DWORD) ((timeout == 0)? INFINITE: timeout * 1000);
+
+ do {
+ wrv = WaitForSingleObject(hProcess, dtimeout);
+
+ if (wrv == WAIT_OBJECT_0) {
+
+ DWORD prv = 0;
+
+ GetExitCodeProcess(hProcess, &prv);
+ rv = (int) prv;
+ break;
+
+ } else if (wrv == WAIT_TIMEOUT) {
+
+ if (func == NULL)
+ continue;
+
+ timeout = (*func)(ptr);
+
+ if (timeout == (time_t)-1) {
+
+ if (TerminateProcess(hProcess, 128 + 9)) {
+ dtimeout = INFINITE;
+ continue;
+ }
+ rv = SE_E_UNSPECIFIED;
+ break;
+
+ } else if (timeout == (time_t) -2) {
+
+ rv = SE_E_EXECTIMEOUT;
+ break;
+
+ } else {
+
+ dtimeout = (DWORD) ((timeout == 0)? INFINITE: timeout * 1000);
+ continue;
+
+ }
+
+ } else {
+
+ rv = SE_E_UNSPECIFIED;
+ break;
+
+ }
+
+ } while(TRUE);
+
+ CloseHandle(hProcess);
+
+ return rv;
+}
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+wait_for_process(pid_t pid)
+{
+ return wait_for_process_timed(pid, NULL, NULL, 0);
+}
+
+static char *
+collect_commandline(const char * fn, va_list * ap)
+{
+ size_t len = 0;
+ size_t alloc_len = 0;
+ const char * s;
+ char * cmd = NULL;
+
+ for (s = fn; s; s = (char *) va_arg(*ap, char *)) {
+ size_t cmp_len;
+ int need_quote = FALSE;
+
+ if (FAILED(StringCchLength(s, MAX_PATH, &cmp_len))) {
+ if (cmd)
+ free(cmd);
+ return NULL;
+ }
+
+ if (cmp_len == 0)
+ continue;
+
+ if (strchr(s, ' ') && /* need to quote any component that
+ has embedded spaces, but not if
+ they are already quoted. */
+ s[0] != '"' &&
+ s[cmp_len - 1] != '"') {
+ need_quote = TRUE;
+ cmp_len += 2 * sizeof(char);
+ }
+
+ if (s != fn)
+ cmp_len += 1 * sizeof(char);
+
+ if (alloc_len < len + cmp_len + 1) {
+ char * nc;
+
+ alloc_len += ((len + cmp_len - alloc_len) / MAX_PATH + 1) * MAX_PATH;
+ nc = (char *) realloc(cmd, alloc_len * sizeof(char));
+ if (nc == NULL) {
+ if (cmd)
+ free(cmd);
+ return NULL;
+ }
+ cmd = nc;
+ }
+
+ if (cmd == NULL)
+ return NULL;
+
+ if (s != fn)
+ cmd[len++] = ' ';
+
+ if (need_quote) {
+ StringCchPrintf(cmd + len, alloc_len - len, "\"%s\"", s);
+ } else {
+ StringCchCopy(cmd + len, alloc_len - len, s);
+ }
+
+ len += cmp_len;
+ }
+
+ return cmd;
+}
+
+ROKEN_LIB_FUNCTION pid_t ROKEN_LIB_CALL
+pipe_execv(FILE **stdin_fd, FILE **stdout_fd, FILE **stderr_fd,
+ const char *file, ...)
+{
+ HANDLE hOut_r = NULL;
+ HANDLE hOut_w = NULL;
+ HANDLE hIn_r = NULL;
+ HANDLE hIn_w = NULL;
+ HANDLE hErr_r = NULL;
+ HANDLE hErr_w = NULL;
+
+ SECURITY_ATTRIBUTES sa;
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+
+ char * commandline = NULL;
+
+ pid_t rv = (pid_t) -1;
+
+ {
+ va_list ap;
+
+ va_start(ap, file);
+ commandline = collect_commandline(file, &ap);
+
+ if (commandline == NULL)
+ return rv;
+ }
+
+ ZeroMemory(&si, sizeof(si));
+ ZeroMemory(&pi, sizeof(pi));
+ ZeroMemory(&sa, sizeof(sa));
+
+ pi.hProcess = NULL;
+ pi.hThread = NULL;
+
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ sa.lpSecurityDescriptor = NULL;
+
+ if ((stdout_fd && !CreatePipe(&hOut_r, &hOut_w, &sa, 0 /* Use default */)) ||
+
+ (stdin_fd && !CreatePipe(&hIn_r, &hIn_w, &sa, 0)) ||
+
+ (stderr_fd && !CreatePipe(&hErr_r, &hErr_w, &sa, 0)) ||
+
+ (!stdout_fd && (hOut_w = CreateFile("CON", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) ||
+
+ (!stdin_fd && (hIn_r = CreateFile("CON",GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) ||
+
+ (!stderr_fd && (hErr_w = CreateFile("CON", GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE))
+
+ goto _exit;
+
+ /* We don't want the child processes inheriting these */
+ if (hOut_r)
+ SetHandleInformation(hOut_r, HANDLE_FLAG_INHERIT, FALSE);
+
+ if (hIn_w)
+ SetHandleInformation(hIn_w, HANDLE_FLAG_INHERIT, FALSE);
+
+ if (hErr_r)
+ SetHandleInformation(hErr_r, HANDLE_FLAG_INHERIT, FALSE);
+
+ si.cb = sizeof(si);
+ si.lpReserved = NULL;
+ si.lpDesktop = NULL;
+ si.lpTitle = NULL;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = hIn_r;
+ si.hStdOutput = hOut_w;
+ si.hStdError = hErr_w;
+
+ if (!CreateProcess(file, commandline, NULL, NULL,
+ TRUE, /* bInheritHandles */
+ CREATE_NO_WINDOW, /* dwCreationFlags */
+ NULL, /* lpEnvironment */
+ NULL, /* lpCurrentDirectory */
+ &si,
+ &pi)) {
+
+ rv = (pid_t) (GetLastError() == ERROR_FILE_NOT_FOUND)? 127 : -1;
+ goto _exit;
+ }
+
+ if (stdin_fd) {
+ *stdin_fd = _fdopen(_open_osfhandle((intptr_t) hIn_w, 0), "wb");
+ if (*stdin_fd)
+ hIn_w = NULL;
+ }
+
+ if (stdout_fd) {
+ *stdout_fd = _fdopen(_open_osfhandle((intptr_t) hOut_r, _O_RDONLY), "rb");
+ if (*stdout_fd)
+ hOut_r = NULL;
+ }
+
+ if (stderr_fd) {
+ *stderr_fd = _fdopen(_open_osfhandle((intptr_t) hErr_r, _O_RDONLY), "rb");
+ if (*stderr_fd)
+ hErr_r = NULL;
+ }
+
+ rv = (pid_t) pi.dwProcessId;
+
+ _exit:
+
+ if (pi.hProcess) CloseHandle(pi.hProcess);
+
+ if (pi.hThread) CloseHandle(pi.hThread);
+
+ if (hIn_r) CloseHandle(hIn_r);
+
+ if (hIn_w) CloseHandle(hIn_w);
+
+ if (hOut_r) CloseHandle(hOut_r);
+
+ if (hOut_w) CloseHandle(hOut_w);
+
+ if (hErr_r) CloseHandle(hErr_r);
+
+ if (hErr_w) CloseHandle(hErr_w);
+
+ return rv;
+}
+
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+simple_execvp_timed(const char *file, char *const args[],
+ time_t (*func)(void *), void *ptr, time_t timeout)
+{
+ intptr_t hp;
+ int rv;
+
+ hp = spawnvp(_P_NOWAIT, file, args);
+
+ if (hp == -1)
+ return (errno == ENOENT)? 127: 126;
+ else if (hp == 0)
+ return 0;
+
+ rv = wait_for_process_timed(GetProcessId((HANDLE) hp), func, ptr, timeout);
+
+ CloseHandle((HANDLE) hp);
+
+ return rv;
+}
+
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+simple_execvp(const char *file, char *const args[])
+{
+ return simple_execvp_timed(file, args, NULL, NULL, 0);
+}
+
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+simple_execve_timed(const char *file, char *const args[], char *const envp[],
+ time_t (*func)(void *), void *ptr, time_t timeout)
+{
+ intptr_t hp;
+ int rv;
+
+ hp = spawnve(_P_NOWAIT, file, args, envp);
+
+ if (hp == -1)
+ return (errno == ENOENT)? 127: 126;
+ else if (hp == 0)
+ return 0;
+
+ rv = wait_for_process_timed(GetProcessId((HANDLE) hp), func, ptr, timeout);
+
+ CloseHandle((HANDLE) hp);
+
+ return rv;
+}
+
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+simple_execve(const char *file, char *const args[], char *const envp[])
+{
+ return simple_execve_timed(file, args, envp, NULL, NULL, 0);
+}
+
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+simple_execlp(const char *file, ...)
+{
+ va_list ap;
+ char **argv;
+ int ret;
+
+ va_start(ap, file);
+ argv = vstrcollect(&ap);
+ va_end(ap);
+ if(argv == NULL)
+ return SE_E_UNSPECIFIED;
+ ret = simple_execvp(file, argv);
+ free(argv);
+ return ret;
+}
+
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+simple_execle(const char *file, ... /* ,char *const envp[] */)
+{
+ va_list ap;
+ char **argv;
+ char *const* envp;
+ int ret;
+
+ va_start(ap, file);
+ argv = vstrcollect(&ap);
+ envp = va_arg(ap, char **);
+ va_end(ap);
+ if(argv == NULL)
+ return SE_E_UNSPECIFIED;
+ ret = simple_execve(file, argv, envp);
+ free(argv);
+ return ret;
+}
diff --git a/lib/roken/sleep.c b/lib/roken/sleep.c
new file mode 100644
index 000000000000..a2599b90ebf0
--- /dev/null
+++ b/lib/roken/sleep.c
@@ -0,0 +1,55 @@
+/***********************************************************************
+ * Copyright (c) 2009, 2014, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <config.h>
+
+#include <roken.h>
+
+#ifndef _WIN32
+#error Only implemented on Windows
+#endif
+
+ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
+sleep(unsigned int seconds)
+{
+ if (SleepEx(1000 * (DWORD) seconds, FALSE) != 0)
+ return -1;
+ return 0;
+}
+
+/* We can only sleep in millisecond increments */
+ROKEN_LIB_FUNCTION unsigned int ROKEN_LIB_CALL
+usleep(unsigned int useconds)
+{
+ if (SleepEx((DWORD)(useconds / 1000), FALSE) != 0)
+ return -1;
+ return 0;
+}
diff --git a/lib/roken/snprintf-test.c b/lib/roken/snprintf-test.c
index adfcb713f614..e51402d288da 100644
--- a/lib/roken/snprintf-test.c
+++ b/lib/roken/snprintf-test.c
@@ -34,6 +34,9 @@
#include "roken.h"
#include <limits.h>
+extern int rk_snprintf(char *, size_t, const char *, ...);
+extern int rk_vsnprintf(char *, size_t, const char *, va_list);
+
static int
try (const char *format, ...)
{
@@ -132,9 +135,10 @@ cmp_with_sprintf_long_long (void)
{
int tot = 0;
long long long_long_values[] = {
- ((long long)LONG_MIN) -1, LONG_MIN, -17, -1,
- 0,
- 1, 17, 4711, 65535, LONG_MAX, ((long long)LONG_MAX) + 1};
+ ((long long)LONG_MIN) - (sizeof(long long) > sizeof(long)),
+ LONG_MIN, -17, -1, 0, 1, 17, 4711, 65535, LONG_MAX,
+ ((long long)LONG_MAX) + (sizeof(long long) > sizeof(long))
+ };
int i;
for (i = 0; i < sizeof(long_long_values) / sizeof(long_long_values[0]); ++i) {
diff --git a/lib/roken/snprintf.c b/lib/roken/snprintf.c
index 88e996c671f1..1aae4cd1ff7b 100644
--- a/lib/roken/snprintf.c
+++ b/lib/roken/snprintf.c
@@ -117,6 +117,10 @@ typedef unsigned long u_longest;
typedef long longest;
#endif
+#ifndef HAVE_UINTPTR_T
+typedef u_longest uintptr_t;
+#endif
+
static size_t
@@ -272,7 +276,7 @@ append_string (struct snprintf_state *state,
len += pad(state, width, ' ');
if (prec != -1) {
- while (*arg && prec--) {
+ while (prec-- && *arg) {
(*state->append_char) (state, *arg++);
++len;
}
@@ -498,7 +502,7 @@ xyzprintf (struct snprintf_state *state, const char *char_format, va_list ap)
break;
}
case 'p' : {
- u_longest arg = (u_longest)va_arg(ap, void*);
+ uintptr_t arg = (uintptr_t)va_arg(ap, void*);
len += append_number (state, arg, 0x10, "0123456789ABCDEF",
width, prec, flags, 0);
diff --git a/lib/roken/socket.c b/lib/roken/socket.c
index 017d6252ea16..0a8612557bce 100644
--- a/lib/roken/socket.c
+++ b/lib/roken/socket.c
@@ -260,6 +260,28 @@ socket_set_tos (rk_socket_t sock, int tos)
}
/*
+ * Set the non-blocking-ness of the socket.
+ */
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+socket_set_nonblocking(rk_socket_t sock, int nonblock)
+{
+#if defined(O_NONBLOCK)
+ int flags = fcntl(sock, F_GETFL, 0);
+ if (flags == -1)
+ return;
+ if (nonblock)
+ flags |= O_NONBLOCK;
+ else
+ flags &= ~O_NONBLOCK;
+ fcntl(sock, F_SETFL, flags);
+#elif defined(FIOBIO)
+ int flags = !!nonblock;
+ return ioctl(sock, FIOBIO, &flags);
+#endif
+}
+
+/*
* set the reuse of addresses on `sock' to `val'.
*/
diff --git a/lib/roken/sockstartup_w32.c b/lib/roken/sockstartup_w32.c
new file mode 100644
index 000000000000..35eb3ae96f16
--- /dev/null
+++ b/lib/roken/sockstartup_w32.c
@@ -0,0 +1,75 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include<config.h>
+
+#include <roken.h>
+
+#ifndef _WIN32
+#error Only implemented for Windows
+#endif
+
+volatile LONG _startup_count = 0;
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+rk_WSAStartup(void)
+{
+ WSADATA wsad;
+
+ if (!WSAStartup( MAKEWORD(2, 2), &wsad )) {
+ if (wsad.wVersion != MAKEWORD(2, 2)) {
+ /* huh? We can't use 2.2? */
+ WSACleanup();
+ return -1;
+ }
+
+ InterlockedIncrement(&_startup_count);
+ return 0;
+ }
+
+ return -1;
+}
+
+
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+rk_WSACleanup(void)
+{
+ LONG l;
+
+ if ((l = InterlockedDecrement(&_startup_count)) < 0) {
+ l = InterlockedIncrement(&_startup_count) - 1;
+ }
+
+ if (l >= 0) {
+ return WSACleanup();
+ }
+ return -1;
+}
diff --git a/lib/roken/stdbool.hin b/lib/roken/stdbool.hin
new file mode 100644
index 000000000000..cdaee5b5d6cd
--- /dev/null
+++ b/lib/roken/stdbool.hin
@@ -0,0 +1,13 @@
+#ifndef _STDBOOL_H
+#define _STDBOOL_H
+
+#ifndef __GNUC__
+typedef int _Bool;
+#endif
+
+#define bool _Bool
+#define true 1
+#define false 0
+#define __bool_true_false_are_defined 1
+
+#endif /* _STDBOOL_H */
diff --git a/lib/roken/stdint.hin b/lib/roken/stdint.hin
new file mode 100644
index 000000000000..4a8387a4e65c
--- /dev/null
+++ b/lib/roken/stdint.hin
@@ -0,0 +1,15 @@
+#ifndef _STDINT_H
+#define _STDINT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef long long int64_t;
+typedef unsigned long long uint64_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _STDINT_H */
diff --git a/lib/roken/strtoll.c b/lib/roken/strtoll.c
new file mode 100644
index 000000000000..1be8761313a3
--- /dev/null
+++ b/lib/roken/strtoll.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ * Portions of this software were developed by David Chisnall
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "roken.h"
+
+/* #include <sys/cdefs.h> */
+
+#include <limits.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+/*
+ * Convert a string to a long long integer.
+ *
+ * Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+ROKEN_LIB_FUNCTION long long ROKEN_LIB_CALL
+strtoll(const char * nptr, char ** endptr, int base)
+{
+ const char *s;
+ unsigned long long acc;
+ char c;
+ unsigned long long cutoff;
+ int neg, any, cutlim;
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ s = nptr;
+ do {
+ c = *s++;
+ } while (isspace((unsigned char)c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X') &&
+ ((s[1] >= '0' && s[1] <= '9') ||
+ (s[1] >= 'A' && s[1] <= 'F') ||
+ (s[1] >= 'a' && s[1] <= 'f'))) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ acc = any = 0;
+ if (base < 2 || base > 36)
+ goto noconv;
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for quads is
+ * [-9223372036854775808..9223372036854775807] and the input base
+ * is 10, cutoff will be set to 922337203685477580 and cutlim to
+ * either 7 (neg==0) or 8 (neg==1), meaning that if we have
+ * accumulated a value > 922337203685477580, or equal but the
+ * next digit is > 7 (or 8), the number is too big, and we will
+ * return a range error.
+ *
+ * Set 'any' if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = neg ? (unsigned long long)-(LLONG_MIN + LLONG_MAX) + LLONG_MAX
+ : LLONG_MAX;
+ cutlim = cutoff % base;
+ cutoff /= base;
+ for ( ; ; c = *s++) {
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'A' && c <= 'Z')
+ c -= 'A' - 10;
+ else if (c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = neg ? LLONG_MIN : LLONG_MAX;
+ errno = ERANGE;
+ } else if (!any) {
+noconv:
+ errno = EINVAL;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
+
diff --git a/lib/roken/strtoull.c b/lib/roken/strtoull.c
new file mode 100644
index 000000000000..cdf59448f1a1
--- /dev/null
+++ b/lib/roken/strtoull.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 2011 The FreeBSD Foundation
+ * All rights reserved.
+ * Portions of this software were developed by David Chisnall
+ * under sponsorship from the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "roken.h"
+
+/* #include <sys/cdefs.h> */
+
+#include <limits.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdint.h>
+
+/*
+ * Convert a string to an unsigned long long integer.
+ *
+ * Assumes that the upper and lower case
+ * alphabets and digits are each contiguous.
+ */
+ROKEN_LIB_FUNCTION unsigned long long ROKEN_LIB_CALL
+strtoull(const char * nptr, char ** endptr, int base)
+{
+ const char *s;
+ unsigned long long acc;
+ char c;
+ unsigned long long cutoff;
+ int neg, any, cutlim;
+
+ /*
+ * See strtoq for comments as to the logic used.
+ */
+ s = nptr;
+ do {
+ c = *s++;
+ } while (isspace((unsigned char)c));
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X') &&
+ ((s[1] >= '0' && s[1] <= '9') ||
+ (s[1] >= 'A' && s[1] <= 'F') ||
+ (s[1] >= 'a' && s[1] <= 'f'))) {
+ c = s[1];
+ s += 2;
+ base = 16;
+ }
+ if (base == 0)
+ base = c == '0' ? 8 : 10;
+ acc = any = 0;
+ if (base < 2 || base > 36)
+ goto noconv;
+
+ cutoff = ULLONG_MAX / base;
+ cutlim = ULLONG_MAX % base;
+ for ( ; ; c = *s++) {
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'A' && c <= 'Z')
+ c -= 'A' - 10;
+ else if (c >= 'a' && c <= 'z')
+ c -= 'a' - 10;
+ else
+ break;
+ if (c >= base)
+ break;
+ if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim))
+ any = -1;
+ else {
+ any = 1;
+ acc *= base;
+ acc += c;
+ }
+ }
+ if (any < 0) {
+ acc = ULLONG_MAX;
+ errno = ERANGE;
+ } else if (!any) {
+noconv:
+ errno = EINVAL;
+ } else if (neg)
+ acc = -acc;
+ if (endptr != NULL)
+ *endptr = (char *)(any ? s - 1 : nptr);
+ return (acc);
+}
+
diff --git a/lib/roken/syslog.hin b/lib/roken/syslog.hin
new file mode 100644
index 000000000000..f68dd1134bce
--- /dev/null
+++ b/lib/roken/syslog.hin
@@ -0,0 +1,248 @@
+/*
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)syslog.h 8.1 (Berkeley) 6/2/93
+ */
+
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#ifndef _SYS_SYSLOG_H
+#define _SYS_SYSLOG_H 1
+
+#include <stdarg.h>
+
+/*
+ * priorities/facilities are encoded into a single 32-bit quantity, where the
+ * bottom 3 bits are the priority (0-7) and the top 28 bits are the facility
+ * (0-big number). Both the priorities and the facilities map roughly
+ * one-to-one to strings in the syslogd(8) source code. This mapping is
+ * included in this file.
+ *
+ * priorities (these are ordered)
+ */
+#define LOG_EMERG 0 /* system is unusable */
+#define LOG_ALERT 1 /* action must be taken immediately */
+#define LOG_CRIT 2 /* critical conditions */
+#define LOG_ERR 3 /* error conditions */
+#define LOG_WARNING 4 /* warning conditions */
+#define LOG_NOTICE 5 /* normal but significant condition */
+#define LOG_INFO 6 /* informational */
+#define LOG_DEBUG 7 /* debug-level messages */
+
+#define LOG_PRIMASK 0x07 /* mask to extract priority part (internal) */
+ /* extract priority */
+#define LOG_PRI(p) ((p) & LOG_PRIMASK)
+#define LOG_MAKEPRI(fac, pri) (((fac) << 3) | (pri))
+
+#ifdef SYSLOG_NAMES
+#define INTERNAL_NOPRI 0x10 /* the "no priority" priority */
+ /* mark "facility" */
+#define INTERNAL_MARK LOG_MAKEPRI(LOG_NFACILITIES, 0)
+typedef struct _code {
+ char *c_name;
+ int c_val;
+} CODE;
+
+CODE prioritynames[] =
+ {
+ { "alert", LOG_ALERT },
+ { "crit", LOG_CRIT },
+ { "debug", LOG_DEBUG },
+ { "emerg", LOG_EMERG },
+ { "err", LOG_ERR },
+ { "error", LOG_ERR }, /* DEPRECATED */
+ { "info", LOG_INFO },
+ { "none", INTERNAL_NOPRI }, /* INTERNAL */
+ { "notice", LOG_NOTICE },
+ { "panic", LOG_EMERG }, /* DEPRECATED */
+ { "warn", LOG_WARNING }, /* DEPRECATED */
+ { "warning", LOG_WARNING },
+ { NULL, -1 }
+ };
+#endif
+
+/* facility codes */
+#define LOG_KERN (0<<3) /* kernel messages */
+#define LOG_USER (1<<3) /* random user-level messages */
+#define LOG_MAIL (2<<3) /* mail system */
+#define LOG_DAEMON (3<<3) /* system daemons */
+#define LOG_AUTH (4<<3) /* security/authorization messages */
+#define LOG_SYSLOG (5<<3) /* messages generated internally by syslogd */
+#define LOG_LPR (6<<3) /* line printer subsystem */
+#define LOG_NEWS (7<<3) /* network news subsystem */
+#define LOG_UUCP (8<<3) /* UUCP subsystem */
+#define LOG_CRON (9<<3) /* clock daemon */
+#define LOG_AUTHPRIV (10<<3) /* security/authorization messages (private) */
+#define LOG_FTP (11<<3) /* ftp daemon */
+
+ /* other codes through 15 reserved for system use */
+#define LOG_LOCAL0 (16<<3) /* reserved for local use */
+#define LOG_LOCAL1 (17<<3) /* reserved for local use */
+#define LOG_LOCAL2 (18<<3) /* reserved for local use */
+#define LOG_LOCAL3 (19<<3) /* reserved for local use */
+#define LOG_LOCAL4 (20<<3) /* reserved for local use */
+#define LOG_LOCAL5 (21<<3) /* reserved for local use */
+#define LOG_LOCAL6 (22<<3) /* reserved for local use */
+#define LOG_LOCAL7 (23<<3) /* reserved for local use */
+
+#define LOG_NFACILITIES 24 /* current number of facilities */
+#define LOG_FACMASK 0x03f8 /* mask to extract facility part */
+ /* facility of pri */
+#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3)
+
+#ifdef SYSLOG_NAMES
+CODE facilitynames[] =
+ {
+ { "auth", LOG_AUTH },
+ { "authpriv", LOG_AUTHPRIV },
+ { "cron", LOG_CRON },
+ { "daemon", LOG_DAEMON },
+ { "ftp", LOG_FTP },
+ { "kern", LOG_KERN },
+ { "lpr", LOG_LPR },
+ { "mail", LOG_MAIL },
+ { "mark", INTERNAL_MARK }, /* INTERNAL */
+ { "news", LOG_NEWS },
+ { "security", LOG_AUTH }, /* DEPRECATED */
+ { "syslog", LOG_SYSLOG },
+ { "user", LOG_USER },
+ { "uucp", LOG_UUCP },
+ { "local0", LOG_LOCAL0 },
+ { "local1", LOG_LOCAL1 },
+ { "local2", LOG_LOCAL2 },
+ { "local3", LOG_LOCAL3 },
+ { "local4", LOG_LOCAL4 },
+ { "local5", LOG_LOCAL5 },
+ { "local6", LOG_LOCAL6 },
+ { "local7", LOG_LOCAL7 },
+ { NULL, -1 }
+ };
+#endif
+
+/*
+ * arguments to setlogmask.
+ */
+#define LOG_MASK(pri) (1 << (pri)) /* mask for one priority */
+#define LOG_UPTO(pri) ((1 << ((pri)+1)) - 1) /* all priorities through pri */
+
+/*
+ * Option flags for openlog.
+ *
+ * LOG_ODELAY no longer does anything.
+ * LOG_NDELAY is the inverse of what it used to be.
+ */
+#define LOG_PID 0x01 /* log the pid with each message */
+#define LOG_CONS 0x02 /* log on the console if errors in sending */
+#define LOG_ODELAY 0x04 /* delay open until first syslog() (default) */
+#define LOG_NDELAY 0x08 /* don't delay open */
+#define LOG_NOWAIT 0x10 /* don't wait for console forks: DEPRECATED */
+#define LOG_PERROR 0x20 /* log to stderr as well */
+
+#define SYSLOG_PORT 514
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Close desriptor used to write to system logger. */
+extern void closelog (void);
+
+/* Open connection to system logger. */
+extern void openlog (char *__ident, int __option, int __facility);
+
+/* Set the log mask level. */
+extern int setlogmask (int __mask);
+
+/* Generate a log message using FMT string and option arguments. */
+extern void syslog (int __pri, char *__fmt, ...);
+
+/* Generate a log message using FMT and using arguments pointed to by AP. */
+extern void vsyslog (int __pri, char *__fmt, va_list __ap);
+
+#ifdef _WIN32
+ /* Windows specific.
+
+ init_syslog() *must* be called before calling any of the above
+ functions. exit_syslog() will be scheduled using atexit().
+ However, it is not an error and encouraged to call
+ exit_syslog() before the application exits.
+
+ During operation, the application is free to call exit_syslog()
+ followed by init_syslog() to re-initialize the library. i.e. if
+ a different syslog host is to be used.
+
+ */
+
+ /* Initializes the syslog library and sets the syslog host. The
+ hostname parameter is of the form "<hostname>[:<port>]". The
+ <port> may be a numeric port or it may be a name of a service.
+ If the <port> is specified using a service name, it will be
+ looked up using getservbyname().
+
+ On failure, the hostname and port will be set to "localhost"
+ and SYSLOG_PORT respectively.
+ */
+ extern void init_syslog(const char * hostname);
+
+ extern void exit_syslog(void);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* syslog.h */
diff --git a/lib/roken/syslogc.c b/lib/roken/syslogc.c
new file mode 100644
index 000000000000..f4fb539a634f
--- /dev/null
+++ b/lib/roken/syslogc.c
@@ -0,0 +1,339 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+/*
+ * Based on code by Alexander Yaworsky
+ */
+
+#include <config.h>
+
+#include <roken.h>
+
+#define SYSLOG_DGRAM_SIZE 1024
+
+static BOOL syslog_opened = FALSE;
+
+static int syslog_mask = 0xFF;
+static char syslog_ident[ 128 ] = "";
+static int syslog_facility = LOG_USER;
+static char syslog_procid_str[ 20 ];
+
+static SOCKADDR_IN syslog_hostaddr;
+static SOCKET syslog_socket = INVALID_SOCKET;
+static char local_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ];
+
+static char syslog_hostname[ MAX_COMPUTERNAME_LENGTH + 1 ] = "localhost";
+static unsigned short syslog_port = SYSLOG_PORT;
+
+static int datagramm_size;
+
+volatile BOOL initialized = FALSE;
+BOOL wsa_initialized = FALSE;
+CRITICAL_SECTION cs_syslog;
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+init_syslog(const char * hostname)
+{
+ WSADATA wsd;
+ char * service;
+
+ if ( initialized )
+ return;
+
+ if( WSAStartup( MAKEWORD( 2, 2 ), &wsd ) ) {
+ fprintf(stderr, "Can't initialize WinSock\n");
+ /* we let the rest of the initialization code go through,
+ although none of the syslog calls would succeed. */
+ } else {
+ wsa_initialized = TRUE;
+ }
+
+ if (hostname)
+ strcpy_s(syslog_hostname, sizeof(syslog_hostname), hostname);
+ else
+ strcpy_s(syslog_hostname, sizeof(syslog_hostname), "");
+
+ service = strchr(syslog_hostname, ':');
+
+ if (service) {
+ int tp;
+
+ *service++ = '\0';
+
+ if ((tp = atoi(service)) <= 0) {
+ struct servent * se;
+
+ se = getservbyname(service, "udp");
+
+ syslog_port = (se == NULL)? SYSLOG_PORT: se->s_port;
+ } else {
+ syslog_port = (unsigned short) tp;
+ }
+ } else {
+ syslog_port = SYSLOG_PORT;
+ }
+
+ InitializeCriticalSection(&cs_syslog);
+ initialized = TRUE;
+
+ atexit(exit_syslog);
+}
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+exit_syslog(void)
+{
+ if ( !initialized )
+ return;
+
+ closelog();
+
+ if ( wsa_initialized )
+ WSACleanup();
+
+ DeleteCriticalSection(&cs_syslog);
+ initialized = FALSE;
+}
+
+static void init_logger_addr()
+{
+ struct hostent * phe = NULL;
+
+ memset( &syslog_hostaddr, 0, sizeof(SOCKADDR_IN) );
+ syslog_hostaddr.sin_family = AF_INET;
+
+ if (syslog_hostname[0] == '\0')
+ goto use_default;
+
+ phe = gethostbyname( syslog_hostname );
+ if( !phe )
+ goto use_default;
+
+ memcpy( &syslog_hostaddr.sin_addr.s_addr, phe->h_addr, phe->h_length );
+
+ syslog_hostaddr.sin_port = htons( syslog_port );
+ return;
+
+use_default:
+ syslog_hostaddr.sin_addr.S_un.S_addr = htonl( 0x7F000001 );
+ syslog_hostaddr.sin_port = htons( SYSLOG_PORT );
+}
+
+/******************************************************************************
+ * closelog
+ *
+ * Close desriptor used to write to system logger.
+ */
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+closelog()
+{
+ if ( !initialized )
+ return;
+
+ EnterCriticalSection(&cs_syslog);
+ if( syslog_opened ) {
+ closesocket( syslog_socket );
+ syslog_socket = INVALID_SOCKET;
+ syslog_opened = FALSE;
+ }
+ LeaveCriticalSection(&cs_syslog);
+}
+
+/******************************************************************************
+ * openlog
+ *
+ * Open connection to system logger.
+ */
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+openlog( char* ident, int option, int facility )
+{
+ BOOL failed = FALSE;
+ SOCKADDR_IN sa_local;
+ DWORD n;
+ int size;
+
+ if ( !initialized )
+ return;
+
+ EnterCriticalSection(&cs_syslog);
+
+ if( syslog_opened )
+ goto done;
+
+ failed = TRUE;
+
+ syslog_facility = facility? facility : LOG_USER;
+
+ if( option & LOG_PID )
+ sprintf_s( syslog_procid_str, sizeof(syslog_procid_str), "[%lu]", GetCurrentProcessId() );
+ else
+ syslog_procid_str[0] = '\0';
+
+ /* FIXME: handle other options */
+
+ n = sizeof(local_hostname);
+ if( !GetComputerName( local_hostname, &n ) )
+ goto done;
+
+ syslog_socket = INVALID_SOCKET;
+
+ init_logger_addr();
+
+ for( n = 0;; n++ )
+ {
+ syslog_socket = socket( AF_INET, SOCK_DGRAM, 0 );
+ if( INVALID_SOCKET == syslog_socket )
+ goto done;
+
+ memset( &sa_local, 0, sizeof(SOCKADDR_IN) );
+ sa_local.sin_family = AF_INET;
+ if( bind( syslog_socket, (SOCKADDR*) &sa_local, sizeof(SOCKADDR_IN) ) == 0 )
+ break;
+ rk_closesocket( syslog_socket );
+ syslog_socket = INVALID_SOCKET;
+ if( n == 100 )
+ goto done;
+ Sleep(0);
+ }
+
+ /* get size of datagramm */
+ size = sizeof(datagramm_size);
+ if( getsockopt( syslog_socket, SOL_SOCKET, SO_MAX_MSG_SIZE, (char*) &datagramm_size, &size ) )
+ goto done;
+ if( datagramm_size - strlen(local_hostname) - (ident? strlen(ident) : 0) < 64 )
+ goto done;
+ if( datagramm_size > SYSLOG_DGRAM_SIZE )
+ datagramm_size = SYSLOG_DGRAM_SIZE;
+
+ if (ident)
+ strcpy_s(syslog_ident, sizeof(syslog_ident), ident);
+
+ syslog_facility = (facility ? facility : LOG_USER);
+ failed = FALSE;
+
+ done:
+ if( failed ) {
+ if( syslog_socket != INVALID_SOCKET )
+ rk_closesocket( syslog_socket );
+ }
+ syslog_opened = !failed;
+
+ LeaveCriticalSection(&cs_syslog);
+}
+
+/******************************************************************************
+ * setlogmask
+ *
+ * Set the log mask level.
+ */
+ROKEN_LIB_FUNCTION int ROKEN_LIB_CALL
+setlogmask( int mask )
+{
+ int ret;
+
+ if ( !initialized )
+ return 0;
+
+ EnterCriticalSection(&cs_syslog);
+
+ ret = syslog_mask;
+ if( mask )
+ syslog_mask = mask;
+
+ LeaveCriticalSection(&cs_syslog);
+
+ return ret;
+}
+
+/******************************************************************************
+ * syslog
+ *
+ * Generate a log message using FMT string and option arguments.
+ */
+ROKEN_LIB_FUNCTION void
+syslog( int pri, char* fmt, ... )
+{
+ va_list ap;
+
+ va_start( ap, fmt );
+ vsyslog( pri, fmt, ap );
+ va_end( ap );
+}
+
+/******************************************************************************
+ * vsyslog
+ *
+ * Generate a log message using FMT and using arguments pointed to by AP.
+ */
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+vsyslog( int pri, char* fmt, va_list ap )
+{
+ static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+ char datagramm[ SYSLOG_DGRAM_SIZE ];
+ SYSTEMTIME stm;
+ int len;
+ char *p;
+
+ if ( !initialized )
+ return;
+
+ EnterCriticalSection(&cs_syslog);
+
+ if( !(LOG_MASK( LOG_PRI( pri )) & syslog_mask) )
+ goto done;
+
+ openlog( NULL, 0, pri & LOG_FACMASK );
+ if( !syslog_opened )
+ goto done;
+
+ if( !(pri & LOG_FACMASK) )
+ pri |= syslog_facility;
+
+ GetLocalTime( &stm );
+ len = sprintf_s( datagramm, sizeof(datagramm),
+ "<%d>%s %2d %02d:%02d:%02d %s %s%s: ",
+ pri,
+ month[ stm.wMonth - 1 ], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond,
+ local_hostname, syslog_ident, syslog_procid_str );
+ vsprintf_s( datagramm + len, datagramm_size - len, fmt, ap );
+ p = strchr( datagramm, '\n' );
+ if( p )
+ *p = 0;
+ p = strchr( datagramm, '\r' );
+ if( p )
+ *p = 0;
+
+ sendto( syslog_socket, datagramm, strlen(datagramm), 0, (SOCKADDR*) &syslog_hostaddr, sizeof(SOCKADDR_IN) );
+
+ done:
+ LeaveCriticalSection(&cs_syslog);
+}
+
diff --git a/lib/roken/test-detach.c b/lib/roken/test-detach.c
new file mode 100644
index 000000000000..09434d8ab9e5
--- /dev/null
+++ b/lib/roken/test-detach.c
@@ -0,0 +1,82 @@
+/***********************************************************************
+ * Copyright (c) 2015, Cryptonector LLC
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#ifdef WIN32
+#include <process.h>
+#ifdef getpid
+#undef getpid
+#endif
+#define getpid _getpid
+#else
+#include <unistd.h>
+#endif
+#include "roken.h"
+
+int main(int argc, char **argv)
+{
+ char *ends;
+ long n;
+ int fd = -1;
+
+ if (argc > 1) {
+ if (argc != 3)
+ errx(1, "Usage: test-detach [--daemon-child fd]");
+ fprintf(stderr, "Child started (argv[1] = %s, argv[2] = %s)!\n", argv[1], argv[2]);
+ errno = 0;
+ n = strtol(argv[2], &ends, 10);
+ fd = n;
+ if (errno != 0)
+ err(1, "Usage: test-detach [--daemon-child fd]");
+ if (n < 0 || ends == NULL || *ends != '\0' || n != fd)
+ errx(1, "Usage: test-detach [--daemon-child fd]");
+ } else {
+ fprintf(stderr, "Parent started as %ld\n", (long)getpid());
+ roken_detach_prep(argc, argv, "--daemon-child");
+ }
+ fprintf(stderr, "Now should be the child: %ld\n", (long)getpid());
+ roken_detach_finish(NULL, fd);
+ /*
+ * These printfs will not appear: stderr will have been replaced
+ * with /dev/null.
+ */
+ fprintf(stderr, "Now should be the child: %ld, wrote to parent\n", (long)getpid());
+ sleep(5);
+ fprintf(stderr, "Daemon child done\n");
+ return 0;
+}
diff --git a/lib/roken/test-mem.c b/lib/roken/test-mem.c
index 2ce961e06340..5ae10172aea2 100644
--- a/lib/roken/test-mem.c
+++ b/lib/roken/test-mem.c
@@ -70,13 +70,16 @@ static RETSIGTYPE
segv_handler(int sig)
{
int fd;
+ ssize_t ret;
char msg[] = "SIGSEGV i current test: ";
fd = open("/dev/stdout", O_WRONLY, 0600);
if (fd >= 0) {
- (void)write(fd, msg, sizeof(msg) - 1);
- (void)write(fd, testname, strlen(testname));
- (void)write(fd, "\n", 1);
+ ret = write(fd, msg, sizeof(msg) - 1);
+ if (ret != -1)
+ ret = write(fd, testname, strlen(testname));
+ if (ret != -1)
+ ret = write(fd, "\n", 1);
close(fd);
}
_exit(1);
diff --git a/lib/roken/test-mini_inetd.c b/lib/roken/test-mini_inetd.c
new file mode 100644
index 000000000000..5afdf74ed8c9
--- /dev/null
+++ b/lib/roken/test-mini_inetd.c
@@ -0,0 +1,369 @@
+/***********************************************************************
+ * Copyright (c) 2009, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#include <config.h>
+#include <roken.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define PORT 8013
+#define PORT_S "8013"
+
+char * prog = "Master";
+int is_client = 0;
+
+static int
+get_address(int flags, struct addrinfo ** ret)
+{
+ struct addrinfo ai;
+ int rv;
+
+ memset(&ai, 0, sizeof(ai));
+
+ ai.ai_flags = flags | AI_NUMERICHOST;
+ ai.ai_family = AF_INET;
+ ai.ai_socktype = SOCK_STREAM;
+ ai.ai_protocol = PF_UNSPEC;
+
+ rv = getaddrinfo("127.0.0.1", PORT_S, &ai, ret);
+ if (rv)
+ warnx("getaddrinfo: %s", gai_strerror(rv));
+ return rv;
+}
+
+static int
+get_connected_socket(rk_socket_t * s_ret)
+{
+ struct addrinfo * ai = NULL;
+ int rv = 0;
+ rk_socket_t s = rk_INVALID_SOCKET;
+
+ rv = get_address(0, &ai);
+ if (rv)
+ return rv;
+
+ s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (rk_IS_BAD_SOCKET(s)) {
+ rv = 1;
+ goto done;
+ }
+
+ rv = connect(s, ai->ai_addr, ai->ai_addrlen);
+ if (rk_IS_SOCKET_ERROR(rv))
+ goto done;
+
+ *s_ret = s;
+ s = rk_INVALID_SOCKET;
+ rv = 0;
+
+ done:
+ if (!rk_IS_BAD_SOCKET(s))
+ rk_closesocket(s);
+
+ if (ai)
+ freeaddrinfo(ai);
+
+ return (rv) ? rk_SOCK_ERRNO : 0;
+}
+
+const char * test_strings[] = {
+ "Hello",
+ "01234566789012345689012345678901234567890123456789",
+ "Another test",
+ "exit"
+};
+
+static int
+test_simple_echo_client(void)
+{
+ rk_socket_t s = rk_INVALID_SOCKET;
+ int rv;
+ char buf[81];
+ int i;
+
+ fprintf(stderr, "[%s] Getting connected socket...", getprogname());
+ rv = get_connected_socket(&s);
+ if (rv) {
+ fprintf(stderr, "\n[%s] get_connected_socket() failed (%s)\n",
+ getprogname(), strerror(rk_SOCK_ERRNO));
+ return 1;
+ }
+
+ fprintf(stderr, "[%s] done\n", getprogname());
+
+ for (i=0; i < sizeof(test_strings)/sizeof(test_strings[0]); i++) {
+ rv = send(s, test_strings[i], strlen(test_strings[i]), 0);
+ if (rk_IS_SOCKET_ERROR(rv)) {
+ fprintf(stderr, "[%s] send() failure (%s)\n",
+ getprogname(), strerror(rk_SOCK_ERRNO));
+ rk_closesocket(s);
+ return 1;
+ }
+
+ rv = recv(s, buf, sizeof(buf), 0);
+ if (rk_IS_SOCKET_ERROR(rv)) {
+ fprintf (stderr, "[%s] recv() failure (%s)\n",
+ getprogname(), strerror(rk_SOCK_ERRNO));
+ rk_closesocket(s);
+ return 1;
+ }
+
+ if (rv == 0) {
+ fprintf (stderr, "[%s] No data received\n", prog);
+ rk_closesocket(s);
+ return 1;
+ }
+
+ if (rv != strlen(test_strings[i])) {
+ fprintf (stderr, "[%s] Data length mismatch %d != %d\n", prog, rv, strlen(test_strings[i]));
+ rk_closesocket(s);
+ return 1;
+ }
+ }
+
+ fprintf (stderr, "[%s] Done\n", prog);
+ rk_closesocket(s);
+ return 0;
+}
+
+static int
+test_simple_echo_socket(void)
+{
+ fprintf (stderr, "[%s] Process ID %d\n", prog, GetCurrentProcessId());
+ fprintf (stderr, "[%s] Starting echo test with sockets\n", prog);
+
+ if (is_client) {
+ return test_simple_echo_client();
+ } else {
+
+ rk_socket_t s = rk_INVALID_SOCKET;
+
+ fprintf (stderr, "[%s] Listening for connections...\n", prog);
+ mini_inetd(htons(PORT), &s);
+ if (rk_IS_BAD_SOCKET(s)) {
+ fprintf (stderr, "[%s] Connect failed (%s)\n",
+ getprogname(), strerror(rk_SOCK_ERRNO));
+ } else {
+ fprintf (stderr, "[%s] Connected\n", prog);
+ }
+
+ {
+ char buf[81];
+ int rv, srv;
+
+ while ((rv = recv(s, buf, sizeof(buf), 0)) != 0 && !rk_IS_SOCKET_ERROR(rv)) {
+ buf[rv] = 0;
+ fprintf(stderr, "[%s] Received [%s]\n", prog, buf);
+
+ /* simple echo */
+ srv = send(s, buf, rv, 0);
+ if (srv != rv) {
+ if (rk_IS_SOCKET_ERROR(srv))
+ fprintf(stderr, "[%s] send() error [%s]\n",
+ getprogname(), strerror(rk_SOCK_ERRNO));
+ else
+ fprintf(stderr, "[%s] send() size mismatch %d != %d",
+ getprogname(), srv, rv);
+ }
+
+ if (!strcmp(buf, "exit")) {
+ fprintf(stderr, "[%s] Exiting...\n", prog);
+ shutdown(s, SD_SEND);
+ rk_closesocket(s);
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "[%s] recv() failed (%s)\n",
+ getprogname(),
+ strerror(rk_SOCK_ERRNO));
+ }
+
+ rk_closesocket(s);
+ }
+
+ return 1;
+}
+
+static int
+test_simple_echo(void)
+{
+ fprintf (stderr, "[%s] Starting echo test\n", prog);
+
+ if (is_client) {
+
+ return test_simple_echo_client();
+
+ } else {
+
+ fprintf (stderr, "[%s] Listening for connections...\n", prog);
+ mini_inetd(htons(PORT), NULL);
+ fprintf (stderr, "[%s] Connected\n", prog);
+
+ {
+ char buf[81];
+ while (gets(buf)) {
+ fprintf(stderr, "[%s] Received [%s]\n", prog, buf);
+
+ if (!strcmp(buf, "exit"))
+ return 0;
+
+ /* simple echo */
+ puts(buf);
+ }
+
+ fprintf(stderr, "[%s] gets() failed (%s)\n", prog, _strerror("gets"));
+ }
+ }
+
+ return 1;
+}
+
+static int
+do_client(void)
+{
+ int rv = 0;
+
+ rk_SOCK_INIT();
+
+ prog = "Client";
+ is_client = 1;
+
+ fprintf(stderr, "Starting client...\n");
+
+ rv = test_simple_echo_socket();
+
+ rk_SOCK_EXIT();
+
+ return rv;
+}
+
+static int
+do_server(void)
+{
+ int rv = 0;
+
+ rk_SOCK_INIT();
+
+ prog = "Server";
+
+ fprintf(stderr, "Starting server...\n");
+
+ rv = test_simple_echo_socket();
+
+ rk_SOCK_EXIT();
+
+ return rv;
+}
+
+static time_t
+wait_callback(void *p)
+{
+ return (time_t)-1;
+}
+
+static int
+do_test(char * path)
+{
+ intptr_t p_server;
+ intptr_t p_client;
+ int client_rv;
+ int server_rv;
+
+ p_server = _spawnl(_P_NOWAIT, path, path, "--server", NULL);
+ if (p_server <= 0) {
+ fprintf(stderr, "%s: %s", path, _strerror("Can't start server process"));
+ return 1;
+ }
+#ifdef _WIN32
+ /* On Windows, the _spawn*() functions return a process handle on
+ success. We need a process ID for use with
+ wait_for_process_timed(). */
+
+ p_server = GetProcessId((HANDLE) p_server);
+#endif
+ fprintf(stderr, "Created server process ID %d\n", p_server);
+
+ p_client = _spawnl(_P_NOWAIT, path, path, "--client", NULL);
+ if (p_client <= 0) {
+ fprintf(stderr, "%s: %s", path, _strerror("Can't start client process"));
+ fprintf(stderr, "Waiting for server process to terminate ...");
+ wait_for_process_timed(p_server, wait_callback, NULL, 5);
+ fprintf(stderr, "DONE\n");
+ return 1;
+ }
+#ifdef _WIN32
+ p_client = GetProcessId((HANDLE) p_client);
+#endif
+ fprintf(stderr, "Created client process ID %d\n", p_client);
+
+ fprintf(stderr, "Waiting for client process to terminate ...");
+ client_rv = wait_for_process_timed(p_client, wait_callback, NULL, 5);
+ if (SE_IS_ERROR(client_rv)) {
+ fprintf(stderr, "\nwait_for_process_timed() failed for client. rv=%d\n", client_rv);
+ } else {
+ fprintf(stderr, "DONE\n");
+ }
+
+ fprintf(stderr, "Waiting for server process to terminate ...");
+ server_rv = wait_for_process_timed(p_server, wait_callback, NULL, 5);
+ if (SE_IS_ERROR(server_rv)) {
+ fprintf(stderr, "\nwait_for_process_timed() failed for server. rv=%d\n", server_rv);
+ } else {
+ fprintf(stderr, "DONE\n");
+ }
+
+ if (client_rv == 0 && server_rv == 0) {
+ fprintf(stderr, "PASS\n");
+ return 0;
+ } else {
+ fprintf(stderr, "FAIL: Client rv=%d, Server rv=%d\n", client_rv, server_rv);
+ return 1;
+ }
+}
+
+int main(int argc, char ** argv)
+{
+ setprogname(argv[0]);
+
+ if (argc == 2 && strcmp(argv[1], "--client") == 0)
+ return do_client();
+ else if (argc == 2 && strcmp(argv[1], "--server") == 0)
+ return do_server();
+ else if (argc == 1)
+ return do_test(argv[0]);
+ else {
+ printf ("%s: Test mini_inetd() function. Run with no arguments to start test\n",
+ argv[0]);
+ return 1;
+ }
+}
diff --git a/lib/roken/tsearch.c b/lib/roken/tsearch.c
index 65328d367df7..0820bb62c0f8 100644
--- a/lib/roken/tsearch.c
+++ b/lib/roken/tsearch.c
@@ -117,14 +117,13 @@ rk_tdelete(const void * vkey, void ** vrootp,
int (*compar)(const void *, const void *))
{
node_t **rootp = (node_t **)vrootp;
- node_t *p, *q, *r;
+ node_t *q, *r;
int cmp;
- if (rootp == NULL || (p = *rootp) == NULL)
+ if (rootp == NULL || *rootp == NULL)
return NULL;
while ((cmp = (*compar)(vkey, (*rootp)->key)) != 0) {
- p = *rootp;
rootp = (cmp < 0) ?
&(*rootp)->llink : /* follow llink branch */
&(*rootp)->rlink; /* follow rlink branch */
@@ -148,7 +147,7 @@ rk_tdelete(const void * vkey, void ** vrootp,
}
free(*rootp); /* D4: Free node */
*rootp = q; /* link parent to new node */
- return p;
+ return *rootp;
}
/*
diff --git a/lib/roken/version-script.map b/lib/roken/version-script.map
index 9229a373cd7d..ccd5925d24af 100644
--- a/lib/roken/version-script.map
+++ b/lib/roken/version-script.map
@@ -2,8 +2,6 @@ HEIMDAL_ROKEN_1.0 {
global:
arg_printusage;
arg_printusage_i18n;
- base64_decode;
- base64_encode;
cgetcap;
cgetclose;
cgetmatch;
@@ -36,13 +34,17 @@ HEIMDAL_ROKEN_1.0 {
print_units_table;
rk_asnprintf;
rk_asprintf;
+ rk_base64_decode;
+ rk_base64_encode;
rk_bswap16;
rk_bswap32;
+ rk_bswap64;
rk_cgetent;
rk_cgetstr;
rk_cloexec;
rk_cloexec_file;
rk_cloexec_dir;
+ rk_cloexec_socket;
rk_closefrom;
rk_copyhostent;
rk_dns_free_data;
@@ -79,6 +81,8 @@ HEIMDAL_ROKEN_1.0 {
rk_inet_ntop;
rk_inet_pton;
rk_localtime_r;
+ rk_memset_s;
+ rk_mkdir;
rk_mkstemp;
rk_pid_file_delete;
rk_pid_file_write;
@@ -105,6 +109,7 @@ HEIMDAL_ROKEN_1.0 {
rk_socket_set_any;
rk_socket_set_debug;
rk_socket_set_ipv6only;
+ rk_socket_set_nonblocking;
rk_socket_set_port;
rk_socket_set_portrange;
rk_socket_set_reuseaddr;
@@ -123,17 +128,14 @@ HEIMDAL_ROKEN_1.0 {
rk_strptime;
rk_strsep_copy;
rk_strsvis;
- rk_strsvis;
rk_strsvisx;
- rk_strunvis;
+ rk_strtoll;
+ rk_strtoull;
rk_strunvis;
rk_strunvisx;
rk_strupr;
rk_strvis;
- rk_strvis;
rk_strvisx;
- rk_strvisx;
- rk_svis;
rk_svis;
rk_timegm;
rk_timevaladd;
@@ -148,7 +150,6 @@ HEIMDAL_ROKEN_1.0 {
rk_vasnprintf;
rk_vasprintf;
rk_vis;
- rk_vis;
rk_vsnprintf;
rk_vstrcollect;
rk_wait_for_process;
@@ -156,6 +157,8 @@ HEIMDAL_ROKEN_1.0 {
rk_warnerr;
rk_xfree;
roken_concat;
+ roken_detach_prep;
+ roken_detach_finish;
roken_getaddrinfo_hostspec2;
roken_getaddrinfo_hostspec;
roken_gethostby_setup;
diff --git a/lib/roken/win32_alloc.c b/lib/roken/win32_alloc.c
new file mode 100644
index 000000000000..40fe0f4fed13
--- /dev/null
+++ b/lib/roken/win32_alloc.c
@@ -0,0 +1,76 @@
+/***********************************************************************
+ * Copyright (c) 2012, Secure Endpoints Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+#include <config.h>
+#include "roken.h"
+#undef calloc
+#undef malloc
+#undef free
+#undef strdup
+#undef wcsdup
+
+/*
+ * Windows executables and dlls suffer when memory is
+ * allocated with one allocator and deallocated with
+ * another because each allocator is backed by a separate
+ * heap. Reduce the exposure by ensuring that all
+ * binaries that are built using roken will build against
+ * same allocator.
+ */
+
+ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
+rk_calloc(size_t elements, size_t size)
+{
+ return calloc( elements, size);
+}
+
+ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
+rk_free(void * ptr)
+{
+ free( ptr);
+}
+
+ROKEN_LIB_FUNCTION void * ROKEN_LIB_CALL
+rk_malloc(size_t size)
+{
+ return malloc( size);
+}
+
+ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
+rk_strdup(const char *str)
+{
+ return strdup( str);
+}
+
+ROKEN_LIB_FUNCTION unsigned short * ROKEN_LIB_CALL
+rk_wcsdup(const unsigned short *str)
+{
+ return wcsdup( str);
+}
diff --git a/lib/roken/write_pid.c b/lib/roken/write_pid.c
index 5059369847af..25ee686c30dd 100644
--- a/lib/roken/write_pid.c
+++ b/lib/roken/write_pid.c
@@ -35,56 +35,84 @@
#include "roken.h"
+#ifdef HAVE_UTIL_H
+#include <util.h>
+#endif
+
ROKEN_LIB_FUNCTION char * ROKEN_LIB_CALL
-pid_file_write (const char *progname)
+pid_file_write(const char *progname)
{
+ const char *pidfile_dir = NULL;
char *ret = NULL;
FILE *fp;
- if (asprintf (&ret, "%s%s.pid", _PATH_VARRUN, progname) < 0 || ret == NULL)
+ /*
+ * Maybe we could have a version of this function (and pidfile())
+ * where we get a directory from the caller. That would allow us to
+ * have command-line options for the daemons for this.
+ *
+ * For now we use an environment variable.
+ */
+ if (!issuid())
+ pidfile_dir = getenv("HEIM_PIDFILE_DIR");
+ if (pidfile_dir == NULL)
+ pidfile_dir = _PATH_VARRUN;
+
+ if (asprintf(&ret, "%s%s.pid", pidfile_dir, progname) < 0 || ret == NULL)
return NULL;
- fp = fopen (ret, "w");
+ fp = fopen(ret, "w");
if (fp == NULL) {
- free (ret);
+ free(ret);
return NULL;
}
- fprintf (fp, "%u", (unsigned)getpid());
- fclose (fp);
+ fprintf(fp, "%lu\n", (unsigned long)getpid());
+ fclose(fp);
return ret;
}
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
-pid_file_delete (char **filename)
+pid_file_delete(char **filename)
{
if (*filename != NULL) {
- unlink (*filename);
- free (*filename);
+ unlink(*filename);
+ free(*filename);
*filename = NULL;
}
}
-#ifndef HAVE_PIDFILE
static char *pidfile_path;
+static pid_t pidfile_pid;
static void
pidfile_cleanup(void)
{
- if(pidfile_path != NULL)
+ if (pidfile_path != NULL && pidfile_pid == getpid())
pid_file_delete(&pidfile_path);
}
ROKEN_LIB_FUNCTION void ROKEN_LIB_CALL
-pidfile(const char *basename)
+rk_pidfile(const char *bname)
{
- if(pidfile_path != NULL)
+ /*
+ * If the OS has a pidfile(), call that, but still call
+ * pid_file_write(). Even if both want to write the same file,
+ * writing it twice will still work.
+ */
+#ifdef HAVE_PIDFILE
+ pidfile(bname);
+#endif
+
+ if (pidfile_path != NULL)
return;
- if(basename == NULL)
- basename = getprogname();
- pidfile_path = pid_file_write(basename);
+ if (bname == NULL)
+ bname = getprogname();
+ pidfile_path = pid_file_write(bname);
+ pidfile_pid = getpid();
#if defined(HAVE_ATEXIT)
- atexit(pidfile_cleanup);
+ if (pidfile_path != NULL)
+ atexit(pidfile_cleanup);
#elif defined(HAVE_ON_EXIT)
- on_exit(pidfile_cleanup);
+ if (pidfile_path != NULL)
+ on_exit(pidfile_cleanup);
#endif
}
-#endif
diff --git a/lib/sl/Makefile.am b/lib/sl/Makefile.am
index c49353f8847b..e7be219fcd62 100644
--- a/lib/sl/Makefile.am
+++ b/lib/sl/Makefile.am
@@ -3,7 +3,7 @@
include $(top_srcdir)/Makefile.am.common
if do_roken_rename
-ES = strtok_r.c snprintf.c strdup.c strupr.c getprogname.c
+ES = strtok_r.c snprintf.c strdup.c strupr.c
endif
AM_CPPFLAGS += $(ROKEN_RENAME)
@@ -29,7 +29,7 @@ libexec_heimdal_PROGRAMS = slc
slc_SOURCES = slc-gram.y slc-lex.l slc.h
-CLEANFILES = snprintf.c strtok_r.c strdup.c strupr.c getprogname.c slc-lex.c
+CLEANFILES = snprintf.c strtok_r.c strdup.c strupr.c slc-lex.c
LDADD = libsl.la $(LIB_roken)
@@ -43,8 +43,6 @@ strdup.c:
$(LN_S) $(srcdir)/../roken/strdup.c .
strupr.c:
$(LN_S) $(srcdir)/../roken/strupr.c .
-getprogname.c:
- $(LN_S) $(srcdir)/../roken/getprogname.c .
slc-lex.c: slc-gram.h
diff --git a/lib/sl/Makefile.in b/lib/sl/Makefile.in
index cb44b91af156..3310ef2bf145 100644
--- a/lib/sl/Makefile.in
+++ b/lib/sl/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,10 +96,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog slc-gram.c \
- slc-gram.h slc-lex.c
TESTS = test_sl$(EXEEXT)
check_PROGRAMS = $(am__EXEEXT_1)
libexec_heimdal_PROGRAMS = slc$(EXEEXT)
@@ -64,8 +114,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -78,6 +127,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -97,6 +147,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -122,18 +174,28 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" \
"$(DESTDIR)$(libexec_heimdaldir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
libsl_la_DEPENDENCIES =
dist_libsl_la_OBJECTS = sl.lo
@do_roken_rename_TRUE@am__objects_1 = strtok_r.lo snprintf.lo \
-@do_roken_rename_TRUE@ strdup.lo strupr.lo getprogname.lo
+@do_roken_rename_TRUE@ strdup.lo strupr.lo
nodist_libsl_la_OBJECTS = $(am__objects_1)
libsl_la_OBJECTS = $(dist_libsl_la_OBJECTS) $(nodist_libsl_la_OBJECTS)
-libsl_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(libsl_la_LDFLAGS) \
- $(LDFLAGS) -o $@
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libsl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(libsl_la_LDFLAGS) $(LDFLAGS) -o $@
am__EXEEXT_1 = test_sl$(EXEEXT)
PROGRAMS = $(libexec_heimdal_PROGRAMS)
am_slc_OBJECTS = slc-gram.$(OBJEXT) slc-lex.$(OBJEXT)
@@ -145,40 +207,275 @@ test_sl_SOURCES = test_sl.c
test_sl_OBJECTS = test_sl.$(OBJEXT)
test_sl_LDADD = $(LDADD)
test_sl_DEPENDENCIES = libsl.la $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
-LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
-LTLEXCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(LEX) $(LFLAGS) $(AM_LFLAGS)
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo " LEX " $@;
+am__v_LEX_1 =
YLWRAP = $(top_srcdir)/ylwrap
@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
-YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
-LTYACCCOMPILE = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(YACC) $(YFLAGS) $(AM_YFLAGS)
+am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
+ -e s/c++$$/h++/ -e s/c$$/h/
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo " YACC " $@;
+am__v_YACC_1 =
SOURCES = $(dist_libsl_la_SOURCES) $(nodist_libsl_la_SOURCES) \
$(slc_SOURCES) test_sl.c
DIST_SOURCES = $(dist_libsl_la_SOURCES) $(slc_SOURCES) test_sl.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver $(top_srcdir)/ylwrap ChangeLog \
+ slc-gram.c slc-gram.h slc-lex.c
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -197,12 +494,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -212,17 +509,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -241,12 +538,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -255,6 +549,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -271,10 +566,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -282,6 +575,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -296,12 +590,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -318,6 +615,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -332,13 +630,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = -d
abs_builddir = @abs_builddir@
@@ -362,6 +654,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -397,30 +691,38 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken) $(ROKEN_RENAME)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
-@do_roken_rename_TRUE@ES = strtok_r.c snprintf.c strdup.c strupr.c getprogname.c
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
+@do_roken_rename_TRUE@ES = strtok_r.c snprintf.c strdup.c strupr.c
include_HEADERS = sl.h
lib_LTLIBRARIES = libsl.la
libsl_la_LDFLAGS = -version-info 2:1:2
@@ -428,14 +730,14 @@ libsl_la_LIBADD = @LIB_readline@
dist_libsl_la_SOURCES = sl_locl.h sl.c roken_rename.h
nodist_libsl_la_SOURCES = $(ES)
slc_SOURCES = slc-gram.y slc-lex.l slc.h
-CLEANFILES = snprintf.c strtok_r.c strdup.c strupr.c getprogname.c slc-lex.c
+CLEANFILES = snprintf.c strtok_r.c strdup.c strupr.c slc-lex.c
LDADD = libsl.la $(LIB_roken)
slc_LDADD = $(LEXLIB) $(LDADD)
EXTRA_DIST = NTMakefile
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .l .lo .o .obj .y
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .l .lo .log .o .obj .test .test$(EXEEXT) .trs .y
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -448,7 +750,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/sl/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/sl/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -457,6 +758,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -466,9 +768,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -476,6 +778,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -491,14 +795,17 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libsl.la: $(libsl_la_OBJECTS) $(libsl_la_DEPENDENCIES)
- $(libsl_la_LINK) -rpath $(libdir) $(libsl_la_OBJECTS) $(libsl_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libsl.la: $(libsl_la_OBJECTS) $(libsl_la_DEPENDENCIES) $(EXTRA_libsl_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libsl_la_LINK) -rpath $(libdir) $(libsl_la_OBJECTS) $(libsl_la_LIBADD) $(LIBS)
clean-checkPROGRAMS:
@list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
@@ -510,14 +817,19 @@ clean-checkPROGRAMS:
rm -f $$list
install-libexec_heimdalPROGRAMS: $(libexec_heimdal_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(libexec_heimdaldir)" || $(MKDIR_P) "$(DESTDIR)$(libexec_heimdaldir)"
@list='$(libexec_heimdal_PROGRAMS)'; test -n "$(libexec_heimdaldir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexec_heimdaldir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexec_heimdaldir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -538,7 +850,8 @@ uninstall-libexec_heimdalPROGRAMS:
@list='$(libexec_heimdal_PROGRAMS)'; test -n "$(libexec_heimdaldir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(libexec_heimdaldir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(libexec_heimdaldir)" && rm -f $$files
@@ -552,16 +865,16 @@ clean-libexec_heimdalPROGRAMS:
echo " rm -f" $$list; \
rm -f $$list
slc-gram.h: slc-gram.c
- @if test ! -f $@; then \
- rm -f slc-gram.c; \
- $(MAKE) $(AM_MAKEFLAGS) slc-gram.c; \
- else :; fi
-slc$(EXEEXT): $(slc_OBJECTS) $(slc_DEPENDENCIES)
+ @if test ! -f $@; then rm -f slc-gram.c; else :; fi
+ @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) slc-gram.c; else :; fi
+
+slc$(EXEEXT): $(slc_OBJECTS) $(slc_DEPENDENCIES) $(EXTRA_slc_DEPENDENCIES)
@rm -f slc$(EXEEXT)
- $(LINK) $(slc_OBJECTS) $(slc_LDADD) $(LIBS)
-test_sl$(EXEEXT): $(test_sl_OBJECTS) $(test_sl_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(slc_OBJECTS) $(slc_LDADD) $(LIBS)
+
+test_sl$(EXEEXT): $(test_sl_OBJECTS) $(test_sl_DEPENDENCIES) $(EXTRA_test_sl_DEPENDENCIES)
@rm -f test_sl$(EXEEXT)
- $(LINK) $(test_sl_OBJECTS) $(test_sl_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_sl_OBJECTS) $(test_sl_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -569,7 +882,6 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getprogname.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sl.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slc-gram.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/slc-lex.Po@am__quote@
@@ -580,31 +892,31 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_sl.Po@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
.l.c:
- $(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
.y.c:
- $(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h $*.h y.output $*.output -- $(YACCCOMPILE)
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
mostlyclean-libtool:
-rm -f *.lo
@@ -613,8 +925,11 @@ clean-libtool:
-rm -rf .libs _libs
install-includeHEADERS: $(include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -628,30 +943,17 @@ uninstall-includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -663,15 +965,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -680,101 +978,187 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test_sl.log: test_sl$(EXEEXT)
+ @p='test_sl$(EXEEXT)'; \
+ b='test_sl'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -828,11 +1212,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -877,10 +1269,9 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES \
+install-exec-am: install-exec-local install-libLTLIBRARIES \
install-libexec_heimdalPROGRAMS
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+
install-html: install-html-am
install-html-am:
@@ -923,43 +1314,57 @@ uninstall-am: uninstall-includeHEADERS uninstall-libLTLIBRARIES \
uninstall-libexec_heimdalPROGRAMS
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-checkPROGRAMS clean-generic \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-checkPROGRAMS clean-generic \
clean-libLTLIBRARIES clean-libexec_heimdalPROGRAMS \
- clean-libtool ctags dist-hook distclean distclean-compile \
- distclean-generic distclean-libtool distclean-tags distdir dvi \
- dvi-am html html-am info info-am install install-am \
- install-data install-data-am install-data-hook install-dvi \
- install-dvi-am install-exec install-exec-am install-exec-hook \
- install-html install-html-am install-includeHEADERS \
- install-info install-info-am install-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-hook install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-local install-html \
+ install-html-am install-includeHEADERS install-info \
+ install-info-am install-libLTLIBRARIES \
install-libexec_heimdalPROGRAMS install-man install-pdf \
install-pdf-am install-ps install-ps-am install-strip \
installcheck installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-hook \
+ recheck tags tags-am uninstall uninstall-am uninstall-hook \
uninstall-includeHEADERS uninstall-libLTLIBRARIES \
uninstall-libexec_heimdalPROGRAMS
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
+
+install-exec-local: install-suid-programs
+
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
-install-exec-hook: install-suid-programs
+all-local: codesign-all
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -967,7 +1372,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -1024,6 +1429,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1066,6 +1473,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1079,13 +1499,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
@@ -1124,8 +1544,6 @@ strdup.c:
$(LN_S) $(srcdir)/../roken/strdup.c .
strupr.c:
$(LN_S) $(srcdir)/../roken/strupr.c .
-getprogname.c:
- $(LN_S) $(srcdir)/../roken/getprogname.c .
slc-lex.c: slc-gram.h
diff --git a/lib/sl/NTMakefile b/lib/sl/NTMakefile
index 285c5d7f1296..ac398c7e44b5 100644
--- a/lib/sl/NTMakefile
+++ b/lib/sl/NTMakefile
@@ -68,7 +68,7 @@ test-binaries: $(OBJ)\test_sl.exe
test-run:
cd $(OBJ)
- test_sl.exe
+ -test_sl.exe
cd $(SRC)\$(RELDIR)
test:: test-binaries test-run
diff --git a/lib/sl/sl.c b/lib/sl/sl.c
index 30f69436dd2e..f6f4bc52ea3d 100644
--- a/lib/sl/sl.c
+++ b/lib/sl/sl.c
@@ -53,8 +53,11 @@ mandoc_template(SL_cmd *cmds,
t = time(NULL);
strftime(timestr, sizeof(timestr), "%b %d, %Y", localtime(&t));
printf(".Dd %s\n", timestr);
- p = strrchr(getprogname(), '/');
- if(p) p++; else p = getprogname();
+#ifdef HAVE_GETPROGNAME
+ p = getprogname();
+#else
+ p = "unknown-application";
+#endif
strncpy(cmd, p, sizeof(cmd));
cmd[sizeof(cmd)-1] = '\0';
strupr(cmd);
@@ -327,7 +330,7 @@ sl_command_loop(SL_cmd *cmds, const char *prompt, void **data)
if (argc >= 1) {
ret = sl_command(cmds, argc, argv);
if(ret == -1) {
- printf ("Unrecognized command: %s\n", argv[0]);
+ sl_did_you_mean(cmds, argv[0]);
ret = 0;
}
}
@@ -393,3 +396,102 @@ sl_slc_help (SL_cmd *cmds, int argc, char **argv)
}
}
}
+
+/* OptimalStringAlignmentDistance */
+
+static int
+osad(const char *s1, const char *s2)
+{
+ size_t l1 = strlen(s1), l2 = strlen(s2), i, j;
+ int *row0, *row1, *row2, *tmp, cost;
+
+ row0 = calloc(sizeof(int), l2 + 1);
+ row1 = calloc(sizeof(int), l2 + 1);
+ row2 = calloc(sizeof(int), l2 + 1);
+
+ for (j = 0; j < l2 + 1; j++)
+ row1[j] = j;
+
+ for (i = 0; i < l1; i++) {
+
+ row2[0] = i + 1;
+
+ for (j = 0; j < l2; j++) {
+
+ row2[j + 1] = row1[j] + (s1[i] != s2[j]); /* substitute */
+
+ if (row2[j + 1] > row1[j + 1] + 1) /* delete */
+ row2[j + 1] = row1[j + 1] + 1;
+ if (row2[j + 1] > row2[j] + 1) /* insert */
+ row2[j + 1] = row2[j] + 1;
+ if (j > 0 && i > 0 && s1[i - 1] != s2[j - 1] && s1[i - 1] == s2[j] && s1[i] == s2[j - 1] && row2[j + 1] < row0[j - 1]) /* transposition */
+ row2[j + 1] = row0[j - 1] + 1;
+ }
+
+ tmp = row0;
+ row0 = row1;
+ row1 = row2;
+ row2 = tmp;
+ }
+
+ cost = row1[l2];
+
+ free(row0);
+ free(row1);
+ free(row2);
+
+ return cost;
+}
+
+/**
+ * Will propose a list of command that are almost matching the command
+ * used, if there is no matching, will ask the user to use "help".
+ *
+ * @param cmds command array to use for matching
+ * @param match the command that didn't exists
+ */
+
+void
+sl_did_you_mean(SL_cmd *cmds, const char *match)
+{
+ int *metrics, best_match = INT_MAX;
+ SL_cmd *c;
+ size_t n;
+
+ for (n = 0, c = cmds; c->name; c++, n++)
+ ;
+ metrics = calloc(n, sizeof(metrics[0]));
+ if (metrics == NULL)
+ return;
+
+ for (n = 0; cmds[n].name; n++) {
+ metrics[n] = osad(match, cmds[n].name);
+ if (metrics[n] < best_match)
+ best_match = metrics[n];
+ }
+ if (best_match == INT_MAX) {
+ free(metrics);
+ fprintf(stderr, "What kind of command is %s", match);
+ return;
+ }
+
+ /* if match distance is low, propose that for the user */
+ if (best_match < 7) {
+
+ fprintf(stderr, "error: %s is not a known command, did you mean ?\n", match);
+ for (n = 0; cmds[n].name; n++) {
+ if (metrics[n] == best_match) {
+ fprintf(stderr, "\t%s\n", cmds[n].name);
+ }
+ }
+ fprintf(stderr, "\n");
+
+ } else {
+
+ fprintf(stderr, "error: %s is not a command, use \"help\" for more list of commands.\n", match);
+ }
+
+ free(metrics);
+
+ return;
+}
diff --git a/lib/sl/sl.h b/lib/sl/sl.h
index 09225b0a537c..affdb6e1abf9 100644
--- a/lib/sl/sl.h
+++ b/lib/sl/sl.h
@@ -61,6 +61,8 @@ int sl_make_argv(char*, int*, char***);
void sl_apropos (SL_cmd *cmd, const char *topic);
SL_cmd *sl_match (SL_cmd *cmds, char *cmd, int exactp);
void sl_slc_help (SL_cmd *cmds, int argc, char **argv);
+void sl_did_you_mean(SL_cmd *cmds, const char *match);
+
#ifdef __cplusplus
}
diff --git a/lib/sl/slc-gram.c b/lib/sl/slc-gram.c
index e44c7110bbd2..7c9262f79986 100644
--- a/lib/sl/slc-gram.c
+++ b/lib/sl/slc-gram.c
@@ -1838,6 +1838,7 @@ gen_command(struct assignment *as)
cprint(1, " { ");
fprintf(cfile, "\"%s\", ", a->u.value);
fprintf(cfile, "%s_wrap, ", f);
+ free(f);
b = find(as, "argument");
if(b)
fprintf(cfile, "\"%s %s\", ", a->u.value, b->u.value);
@@ -1851,7 +1852,7 @@ gen_command(struct assignment *as)
fprintf(cfile, " },\n");
for(a = a->next; a != NULL; a = a->next)
if(strcmp(a->name, "name") == 0)
- cprint(1, " { \"%s\" },\n", a->u.value);
+ cprint(1, " { \"%s\", NULL, NULL, NULL },\n", a->u.value);
cprint(0, "\n");
}
@@ -1870,6 +1871,7 @@ make_name(struct assignment *as)
struct assignment *lopt;
struct assignment *type;
char *s;
+ int ret;
lopt = find(as, "long");
if(lopt == NULL)
@@ -1879,9 +1881,11 @@ make_name(struct assignment *as)
type = find(as, "type");
if(strcmp(type->u.value, "-flag") == 0)
- asprintf(&s, "%s_flag", lopt->u.value);
+ ret = asprintf(&s, "%s_flag", lopt->u.value);
else
- asprintf(&s, "%s_%s", lopt->u.value, type->u.value);
+ ret = asprintf(&s, "%s_%s", lopt->u.value, type->u.value);
+ if (ret == -1)
+ return NULL;
gen_name(s);
return s;
}
@@ -1956,7 +1960,7 @@ struct type_handler {
defval_neg_flag,
NULL
},
- { NULL }
+ { NULL, NULL, NULL, NULL, NULL }
};
static struct type_handler *find_handler(struct assignment *type)
@@ -2220,7 +2224,7 @@ gen(struct assignment *as)
cprint(0, "SL_cmd commands[] = {\n");
for(a = as; a != NULL; a = a->next)
gen_command(a->u.assignment);
- cprint(1, "{ NULL }\n");
+ cprint(1, "{ NULL, NULL, NULL, NULL }\n");
cprint(0, "};\n");
hprint(0, "extern SL_cmd commands[];\n");
@@ -2229,8 +2233,8 @@ gen(struct assignment *as)
int version_flag;
int help_flag;
struct getargs args[] = {
- { "version", 0, arg_flag, &version_flag },
- { "help", 0, arg_flag, &help_flag }
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/sl/slc-gram.y b/lib/sl/slc-gram.y
index 530b1a344776..31ec6bc24de4 100644
--- a/lib/sl/slc-gram.y
+++ b/lib/sl/slc-gram.y
@@ -328,6 +328,7 @@ gen_command(struct assignment *as)
cprint(1, " { ");
fprintf(cfile, "\"%s\", ", a->u.value);
fprintf(cfile, "%s_wrap, ", f);
+ free(f);
b = find(as, "argument");
if(b)
fprintf(cfile, "\"%s %s\", ", a->u.value, b->u.value);
@@ -341,7 +342,7 @@ gen_command(struct assignment *as)
fprintf(cfile, " },\n");
for(a = a->next; a != NULL; a = a->next)
if(strcmp(a->name, "name") == 0)
- cprint(1, " { \"%s\" },\n", a->u.value);
+ cprint(1, " { \"%s\", NULL, NULL, NULL },\n", a->u.value);
cprint(0, "\n");
}
@@ -360,6 +361,7 @@ make_name(struct assignment *as)
struct assignment *lopt;
struct assignment *type;
char *s;
+ int ret;
lopt = find(as, "long");
if(lopt == NULL)
@@ -369,9 +371,11 @@ make_name(struct assignment *as)
type = find(as, "type");
if(strcmp(type->u.value, "-flag") == 0)
- asprintf(&s, "%s_flag", lopt->u.value);
+ ret = asprintf(&s, "%s_flag", lopt->u.value);
else
- asprintf(&s, "%s_%s", lopt->u.value, type->u.value);
+ ret = asprintf(&s, "%s_%s", lopt->u.value, type->u.value);
+ if (ret == -1)
+ return NULL;
gen_name(s);
return s;
}
@@ -446,7 +450,7 @@ struct type_handler {
defval_neg_flag,
NULL
},
- { NULL }
+ { NULL, NULL, NULL, NULL, NULL }
};
static struct type_handler *find_handler(struct assignment *type)
@@ -710,7 +714,7 @@ gen(struct assignment *as)
cprint(0, "SL_cmd commands[] = {\n");
for(a = as; a != NULL; a = a->next)
gen_command(a->u.assignment);
- cprint(1, "{ NULL }\n");
+ cprint(1, "{ NULL, NULL, NULL, NULL }\n");
cprint(0, "};\n");
hprint(0, "extern SL_cmd commands[];\n");
@@ -719,8 +723,8 @@ gen(struct assignment *as)
int version_flag;
int help_flag;
struct getargs args[] = {
- { "version", 0, arg_flag, &version_flag },
- { "help", 0, arg_flag, &help_flag }
+ { "version", 0, arg_flag, &version_flag, NULL, NULL },
+ { "help", 0, arg_flag, &help_flag, NULL, NULL }
};
int num_args = sizeof(args) / sizeof(args[0]);
diff --git a/lib/sl/slc-lex.c b/lib/sl/slc-lex.c
index 7bba1531a13d..4afc6df11d1d 100644
--- a/lib/sl/slc-lex.c
+++ b/lib/sl/slc-lex.c
@@ -46,6 +46,7 @@ typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
+typedef uint64_t flex_uint64_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
@@ -354,7 +355,7 @@ static void yy_fatal_error (yyconst char msg[] );
*/
#define YY_DO_BEFORE_ACTION \
(yytext_ptr) = yy_bp; \
- yyleng = (size_t) (yy_cp - yy_bp); \
+ yyleng = (yy_size_t) (yy_cp - yy_bp); \
(yy_hold_char) = *yy_cp; \
*yy_cp = '\0'; \
(yy_c_buf_p) = yy_cp;
@@ -505,7 +506,7 @@ static char * handle_string(void);
#undef ECHO
-#line 509 "slc-lex.c"
+#line 510 "slc-lex.c"
#define INITIAL 0
@@ -687,7 +688,7 @@ YY_DECL
#line 58 "slc-lex.l"
-#line 691 "slc-lex.c"
+#line 692 "slc-lex.c"
if ( !(yy_init) )
{
@@ -809,7 +810,7 @@ YY_RULE_SETUP
#line 68 "slc-lex.l"
ECHO;
YY_BREAK
-#line 813 "slc-lex.c"
+#line 814 "slc-lex.c"
case YY_STATE_EOF(INITIAL):
yyterminate();
diff --git a/lib/sqlite/Makefile.am b/lib/sqlite/Makefile.am
index 2ca8bfd64af6..bd039692225f 100644
--- a/lib/sqlite/Makefile.am
+++ b/lib/sqlite/Makefile.am
@@ -2,6 +2,10 @@
include $(top_srcdir)/Makefile.am.common
+if ENABLE_PTHREAD_SUPPORT
+AM_CPPFLAGS += -DSQLITE_THREADSAFE=1
+endif
+
lib_LTLIBRARIES = libheimsqlite.la
noinst_HEADERS = sqlite3.h sqlite3ext.h
diff --git a/lib/sqlite/Makefile.in b/lib/sqlite/Makefile.in
index e5df2eb7f2db..cb43aca20595 100644
--- a/lib/sqlite/Makefile.in
+++ b/lib/sqlite/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -23,6 +22,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -41,9 +95,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common
+@ENABLE_PTHREAD_SUPPORT_TRUE@am__append_1 = -DSQLITE_THREADSAFE=1
subdir = lib/sqlite
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -59,8 +111,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -73,6 +124,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -92,6 +144,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(noinst_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -117,34 +171,93 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__DEPENDENCIES_1 =
libheimsqlite_la_DEPENDENCIES = $(am__DEPENDENCIES_1)
am_libheimsqlite_la_OBJECTS = sqlite3.lo
libheimsqlite_la_OBJECTS = $(am_libheimsqlite_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(libheimsqlite_la_SOURCES)
DIST_SOURCES = $(libheimsqlite_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(noinst_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ README
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -163,12 +276,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -178,17 +291,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -207,12 +320,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -221,6 +331,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -237,10 +348,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -248,6 +357,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -262,12 +372,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -284,6 +397,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -298,13 +412,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -328,6 +436,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -363,29 +473,37 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
-AM_CPPFLAGS = $(INCLUDES_roken)
+AM_CPPFLAGS = $(INCLUDES_roken) $(am__append_1)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
lib_LTLIBRARIES = libheimsqlite.la
noinst_HEADERS = sqlite3.h sqlite3ext.h
libheimsqlite_la_SOURCES = sqlite3.c
@@ -394,7 +512,7 @@ EXTRA_DIST = NTMakefile
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -407,7 +525,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/sqlite/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/sqlite/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -416,6 +533,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -425,9 +543,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -435,6 +553,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -450,14 +570,17 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libheimsqlite.la: $(libheimsqlite_la_OBJECTS) $(libheimsqlite_la_DEPENDENCIES)
- $(LINK) -rpath $(libdir) $(libheimsqlite_la_OBJECTS) $(libheimsqlite_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libheimsqlite.la: $(libheimsqlite_la_OBJECTS) $(libheimsqlite_la_DEPENDENCIES) $(EXTRA_libheimsqlite_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) -rpath $(libdir) $(libheimsqlite_la_OBJECTS) $(libheimsqlite_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -468,25 +591,25 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sqlite3.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -494,26 +617,15 @@ mostlyclean-libtool:
clean-libtool:
-rm -rf .libs _libs
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -525,15 +637,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -542,6 +650,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -597,10 +720,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -642,9 +770,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -686,40 +813,54 @@ ps-am:
uninstall-am: uninstall-libLTLIBRARIES
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-generic clean-libLTLIBRARIES clean-libtool ctags \
- dist-hook distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-data \
- install-data-am install-data-hook install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am \
+ install-data-hook install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-local install-html \
install-html-am install-info install-info-am \
install-libLTLIBRARIES install-man install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-hook \
+ tags tags-am uninstall uninstall-am uninstall-hook \
uninstall-libLTLIBRARIES
+.PRECIOUS: Makefile
+
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
-install-exec-hook: install-suid-programs
+install-exec-local: install-suid-programs
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -727,7 +868,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -784,6 +925,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -826,6 +969,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -839,13 +995,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/sqlite/README b/lib/sqlite/README
new file mode 100644
index 000000000000..f147d8a7519f
--- /dev/null
+++ b/lib/sqlite/README
@@ -0,0 +1,25 @@
+
+This directory contains a copy of the SQLite3 amalgamation.
+
+In order to help update this copy to newer versions of SQLite3, please
+make NO changes to it that are not absolutely necessary:
+
+ - add no features that can't be added as loadable extensions or avoided
+ altogether
+ - fix no bugs that can't be worked around
+ - define macros via the Makefile as much as possible (-D...) rather
+ than via changes to the source
+ - fix absolutely no whitespace, formatting, or spelling errors
+ - fix no compiler warnings (the upstream community mostly doesn't care
+ to fix them) unless you're absolutely convinced that the warning(s)
+ represent a serious bug(s) that is relevant to Heimdal
+
+If you must make changes to this copy, DO:
+
+ - have a commit just for those changes and no other changes outside
+ this part of the tree
+
+ - provide enough information in the commit message that someone else
+ (or even yourself) can figure out whether and how to re-apply your
+ change years later when updating to a newer version of SQLite3
+
diff --git a/lib/sqlite/sqlite3.c b/lib/sqlite/sqlite3.c
index d04fa3801ebe..4a87906e1215 100644
--- a/lib/sqlite/sqlite3.c
+++ b/lib/sqlite/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.7.8. By combining all the individual C code files into this
+** version 3.15.1. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -9,7 +9,7 @@
**
** This file is all you need to compile SQLite. To use SQLite in other
** programs, you need this file and the "sqlite3.h" header file that defines
-** the programming interface to the SQLite library. (If you do not have
+** the programming interface to the SQLite library. (If you do not have
** the "sqlite3.h" header file at hand, you will find a copy embedded within
** the text of this file. Search for "Begin file sqlite3.h" to find the start
** of the embedded sqlite3.h header file.) Additional code files may be needed
@@ -22,9 +22,6 @@
#ifndef SQLITE_PRIVATE
# define SQLITE_PRIVATE static
#endif
-#ifndef SQLITE_API
-# define SQLITE_API
-#endif
/************** Begin file sqliteInt.h ***************************************/
/*
** 2001 September 15
@@ -40,515 +37,225 @@
** Internal interface definitions for SQLite.
**
*/
-#ifndef _SQLITEINT_H_
-#define _SQLITEINT_H_
+#ifndef SQLITEINT_H
+#define SQLITEINT_H
-/*
-** These #defines should enable >2GB file support on POSIX if the
-** underlying operating system supports it. If the OS lacks
-** large file support, or if the OS is windows, these should be no-ops.
+/* Special Comments:
**
-** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any
-** system #includes. Hence, this block of code must be the very first
-** code in all source files.
+** Some comments have special meaning to the tools that measure test
+** coverage:
**
-** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
-** on the compiler command line. This is necessary if you are compiling
-** on a recent machine (ex: Red Hat 7.2) but you want your code to work
-** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2
-** without this option, LFS is enable. But LFS does not exist in the kernel
-** in Red Hat 6.0, so the code won't work. Hence, for maximum binary
-** portability you should omit LFS.
+** NO_TEST - The branches on this line are not
+** measured by branch coverage. This is
+** used on lines of code that actually
+** implement parts of coverage testing.
**
-** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later.
-*/
-#ifndef SQLITE_DISABLE_LFS
-# define _LARGE_FILE 1
-# ifndef _FILE_OFFSET_BITS
-# define _FILE_OFFSET_BITS 64
-# endif
-# define _LARGEFILE_SOURCE 1
-#endif
-
-/*
-** Include the configuration header output by 'configure' if we're using the
-** autoconf-based build
-*/
-#ifdef _HAVE_SQLITE_CONFIG_H
-#include "config.h"
-#endif
-
-/************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/
-/************** Begin file sqliteLimit.h *************************************/
-/*
-** 2007 May 7
+** OPTIMIZATION-IF-TRUE - This branch is allowed to alway be false
+** and the correct answer is still obtained,
+** though perhaps more slowly.
**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
+** OPTIMIZATION-IF-FALSE - This branch is allowed to alway be true
+** and the correct answer is still obtained,
+** though perhaps more slowly.
**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
+** PREVENTS-HARMLESS-OVERREAD - This branch prevents a buffer overread
+** that would be harmless and undetectable
+** if it did occur.
**
-*************************************************************************
-**
-** This file defines various limits of what SQLite can process.
+** In all cases, the special comment must be enclosed in the usual
+** slash-asterisk...asterisk-slash comment marks, with no spaces between the
+** asterisks and the comment text.
*/
/*
-** The maximum length of a TEXT or BLOB in bytes. This also
-** limits the size of a row in a table or index.
-**
-** The hard limit is the ability of a 32-bit signed integer
-** to count the size: 2^31-1 or 2147483647.
+** Make sure the Tcl calling convention macro is defined. This macro is
+** only used by test code and Tcl integration code.
*/
-#ifndef SQLITE_MAX_LENGTH
-# define SQLITE_MAX_LENGTH 1000000000
+#ifndef SQLITE_TCLAPI
+# define SQLITE_TCLAPI
#endif
/*
-** This is the maximum number of
-**
-** * Columns in a table
-** * Columns in an index
-** * Columns in a view
-** * Terms in the SET clause of an UPDATE statement
-** * Terms in the result set of a SELECT statement
-** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
-** * Terms in the VALUES clause of an INSERT statement
-**
-** The hard upper limit here is 32676. Most database people will
-** tell you that in a well-normalized database, you usually should
-** not have more than a dozen or so columns in any table. And if
-** that is the case, there is no point in having more than a few
-** dozen values in any of the other situations described above.
+** Make sure that rand_s() is available on Windows systems with MSVC 2005
+** or higher.
*/
-#ifndef SQLITE_MAX_COLUMN
-# define SQLITE_MAX_COLUMN 2000
+#if defined(_MSC_VER) && _MSC_VER>=1400
+# define _CRT_RAND_S
#endif
/*
-** The maximum length of a single SQL statement in bytes.
-**
-** It used to be the case that setting this value to zero would
-** turn the limit off. That is no longer true. It is not possible
-** to turn this limit off.
+** Include the header file used to customize the compiler options for MSVC.
+** This should be done first so that it can successfully prevent spurious
+** compiler warnings due to subsequent content in this file and other files
+** that are included by this file.
*/
-#ifndef SQLITE_MAX_SQL_LENGTH
-# define SQLITE_MAX_SQL_LENGTH 1000000000
-#endif
-
+/************** Include msvc.h in the middle of sqliteInt.h ******************/
+/************** Begin file msvc.h ********************************************/
/*
-** The maximum depth of an expression tree. This is limited to
-** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
-** want to place more severe limits on the complexity of an
-** expression.
+** 2015 January 12
**
-** A value of 0 used to mean that the limit was not enforced.
-** But that is no longer true. The limit is now strictly enforced
-** at all times.
-*/
-#ifndef SQLITE_MAX_EXPR_DEPTH
-# define SQLITE_MAX_EXPR_DEPTH 1000
-#endif
-
-/*
-** The maximum number of terms in a compound SELECT statement.
-** The code generator for compound SELECT statements does one
-** level of recursion for each term. A stack overflow can result
-** if the number of terms is too large. In practice, most SQL
-** never has more than 3 or 4 terms. Use a value of 0 to disable
-** any limit on the number of terms in a compount SELECT.
-*/
-#ifndef SQLITE_MAX_COMPOUND_SELECT
-# define SQLITE_MAX_COMPOUND_SELECT 500
-#endif
-
-/*
-** The maximum number of opcodes in a VDBE program.
-** Not currently enforced.
-*/
-#ifndef SQLITE_MAX_VDBE_OP
-# define SQLITE_MAX_VDBE_OP 25000
-#endif
-
-/*
-** The maximum number of arguments to an SQL function.
-*/
-#ifndef SQLITE_MAX_FUNCTION_ARG
-# define SQLITE_MAX_FUNCTION_ARG 127
-#endif
-
-/*
-** The maximum number of in-memory pages to use for the main database
-** table and for temporary tables. The SQLITE_DEFAULT_CACHE_SIZE
-*/
-#ifndef SQLITE_DEFAULT_CACHE_SIZE
-# define SQLITE_DEFAULT_CACHE_SIZE 2000
-#endif
-#ifndef SQLITE_DEFAULT_TEMP_CACHE_SIZE
-# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500
-#endif
-
-/*
-** The default number of frames to accumulate in the log file before
-** checkpointing the database in WAL mode.
-*/
-#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
-# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000
-#endif
-
-/*
-** The maximum number of attached databases. This must be between 0
-** and 62. The upper bound on 62 is because a 64-bit integer bitmap
-** is used internally to track attached databases.
-*/
-#ifndef SQLITE_MAX_ATTACHED
-# define SQLITE_MAX_ATTACHED 10
-#endif
-
-
-/*
-** The maximum value of a ?nnn wildcard that the parser will accept.
-*/
-#ifndef SQLITE_MAX_VARIABLE_NUMBER
-# define SQLITE_MAX_VARIABLE_NUMBER 999
-#endif
-
-/* Maximum page size. The upper bound on this value is 65536. This a limit
-** imposed by the use of 16-bit offsets within each page.
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
**
-** Earlier versions of SQLite allowed the user to change this value at
-** compile time. This is no longer permitted, on the grounds that it creates
-** a library that is technically incompatible with an SQLite library
-** compiled with a different limit. If a process operating on a database
-** with a page-size of 65536 bytes crashes, then an instance of SQLite
-** compiled with the default page-size limit will not be able to rollback
-** the aborted transaction. This could lead to database corruption.
-*/
-#ifdef SQLITE_MAX_PAGE_SIZE
-# undef SQLITE_MAX_PAGE_SIZE
-#endif
-#define SQLITE_MAX_PAGE_SIZE 65536
-
-
-/*
-** The default size of a database page.
-*/
-#ifndef SQLITE_DEFAULT_PAGE_SIZE
-# define SQLITE_DEFAULT_PAGE_SIZE 1024
-#endif
-#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
-# undef SQLITE_DEFAULT_PAGE_SIZE
-# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
-#endif
-
-/*
-** Ordinarily, if no value is explicitly provided, SQLite creates databases
-** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain
-** device characteristics (sector-size and atomic write() support),
-** SQLite may choose a larger value. This constant is the maximum value
-** SQLite will choose on its own.
-*/
-#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE
-# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192
-#endif
-#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
-# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
-# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
-#endif
-
-
-/*
-** Maximum number of pages in one database file.
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
**
-** This is really just the default value for the max_page_count pragma.
-** This value can be lowered (or raised) at run-time using that the
-** max_page_count macro.
-*/
-#ifndef SQLITE_MAX_PAGE_COUNT
-# define SQLITE_MAX_PAGE_COUNT 1073741823
-#endif
-
-/*
-** Maximum length (in bytes) of the pattern in a LIKE or GLOB
-** operator.
-*/
-#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
-# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
-#endif
-
-/*
-** Maximum depth of recursion for triggers.
+******************************************************************************
**
-** A value of 1 means that a trigger program will not be able to itself
-** fire any triggers. A value of 0 means that no trigger programs at all
-** may be executed.
-*/
-#ifndef SQLITE_MAX_TRIGGER_DEPTH
-# define SQLITE_MAX_TRIGGER_DEPTH 1000
-#endif
-
-/************** End of sqliteLimit.h *****************************************/
+** This file contains code that is specific to MSVC.
+*/
+#ifndef SQLITE_MSVC_H
+#define SQLITE_MSVC_H
+
+#if defined(_MSC_VER)
+#pragma warning(disable : 4054)
+#pragma warning(disable : 4055)
+#pragma warning(disable : 4100)
+#pragma warning(disable : 4127)
+#pragma warning(disable : 4130)
+#pragma warning(disable : 4152)
+#pragma warning(disable : 4189)
+#pragma warning(disable : 4206)
+#pragma warning(disable : 4210)
+#pragma warning(disable : 4232)
+#pragma warning(disable : 4244)
+#pragma warning(disable : 4305)
+#pragma warning(disable : 4306)
+#pragma warning(disable : 4702)
+#pragma warning(disable : 4706)
+#endif /* defined(_MSC_VER) */
+
+#endif /* SQLITE_MSVC_H */
+
+/************** End of msvc.h ************************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
-/* Disable nuisance warnings on Borland compilers */
-#if defined(__BORLANDC__)
-#pragma warn -rch /* unreachable code */
-#pragma warn -ccc /* Condition is always true or false */
-#pragma warn -aus /* Assigned value is never used */
-#pragma warn -csu /* Comparing signed and unsigned */
-#pragma warn -spa /* Suspicious pointer arithmetic */
-#endif
-
-/* Needed for various definitions... */
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE
-#endif
-
-/*
-** Include standard header files as necessary
-*/
-#ifdef HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#ifdef HAVE_INTTYPES_H
-#include <inttypes.h>
-#endif
-
/*
-** The number of samples of an index that SQLite takes in order to
-** construct a histogram of the table content when running ANALYZE
-** and with SQLITE_ENABLE_STAT2
+** Special setup for VxWorks
*/
-#define SQLITE_INDEX_SAMPLES 10
-
+/************** Include vxworks.h in the middle of sqliteInt.h ***************/
+/************** Begin file vxworks.h *****************************************/
/*
-** The following macros are used to cast pointers to integers and
-** integers to pointers. The way you do this varies from one compiler
-** to the next, so we have developed the following set of #if statements
-** to generate appropriate macros for a wide range of compilers.
+** 2015-03-02
**
-** The correct "ANSI" way to do this is to use the intptr_t type.
-** Unfortunately, that typedef is not available on all compilers, or
-** if it is available, it requires an #include of specific headers
-** that vary from one machine to the next.
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
**
-** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
-** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
-** So we have to define the macros in different ways depending on the
-** compiler.
-*/
-#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
-# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
-#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
-# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
-# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
-#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
-# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
-#else /* Generates a warning - but it always works */
-# define SQLITE_INT_TO_PTR(X) ((void*)(X))
-# define SQLITE_PTR_TO_INT(X) ((int)(X))
-#endif
-
-/*
-** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
-** 0 means mutexes are permanently disable and the library is never
-** threadsafe. 1 means the library is serialized which is the highest
-** level of threadsafety. 2 means the libary is multithreaded - multiple
-** threads can use SQLite as long as no two threads try to use the same
-** database connection at the same time.
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
**
-** Older versions of SQLite used an optional THREADSAFE macro.
-** We support that for legacy.
+******************************************************************************
+**
+** This file contains code that is specific to Wind River's VxWorks
*/
-#if !defined(SQLITE_THREADSAFE)
-#if defined(THREADSAFE)
-# define SQLITE_THREADSAFE THREADSAFE
-#else
-# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */
-#endif
-#endif
-
-/*
-** The SQLITE_DEFAULT_MEMSTATUS macro must be defined as either 0 or 1.
-** It determines whether or not the features related to
-** SQLITE_CONFIG_MEMSTATUS are available by default or not. This value can
-** be overridden at runtime using the sqlite3_config() API.
+#if defined(__RTP__) || defined(_WRS_KERNEL)
+/* This is VxWorks. Set up things specially for that OS
*/
-#if !defined(SQLITE_DEFAULT_MEMSTATUS)
-# define SQLITE_DEFAULT_MEMSTATUS 1
-#endif
+#include <vxWorks.h>
+#include <pthread.h> /* amalgamator: dontcache */
+#define OS_VXWORKS 1
+#define SQLITE_OS_OTHER 0
+#define SQLITE_HOMEGROWN_RECURSIVE_MUTEX 1
+#define SQLITE_OMIT_LOAD_EXTENSION 1
+#define SQLITE_ENABLE_LOCKING_STYLE 0
+#define HAVE_UTIME 1
+#else
+/* This is not VxWorks. */
+#define OS_VXWORKS 0
+#define HAVE_FCHOWN 1
+#define HAVE_READLINK 1
+#define HAVE_LSTAT 1
+#endif /* defined(_WRS_KERNEL) */
+
+/************** End of vxworks.h *********************************************/
+/************** Continuing where we left off in sqliteInt.h ******************/
/*
-** Exactly one of the following macros must be defined in order to
-** specify which memory allocation subsystem to use.
+** These #defines should enable >2GB file support on POSIX if the
+** underlying operating system supports it. If the OS lacks
+** large file support, or if the OS is windows, these should be no-ops.
**
-** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
-** SQLITE_WIN32_MALLOC // Use Win32 native heap API
-** SQLITE_MEMDEBUG // Debugging version of system malloc()
+** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any
+** system #includes. Hence, this block of code must be the very first
+** code in all source files.
**
-** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
-** assert() macro is enabled, each call into the Win32 native heap subsystem
-** will cause HeapValidate to be called. If heap validation should fail, an
-** assertion will be triggered.
+** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
+** on the compiler command line. This is necessary if you are compiling
+** on a recent machine (ex: Red Hat 7.2) but you want your code to work
+** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2
+** without this option, LFS is enable. But LFS does not exist in the kernel
+** in Red Hat 6.0, so the code won't work. Hence, for maximum binary
+** portability you should omit LFS.
**
-** (Historical note: There used to be several other options, but we've
-** pared it down to just these two.)
+** The previous paragraph was written in 2005. (This paragraph is written
+** on 2008-11-28.) These days, all Linux kernels support large files, so
+** you should probably leave LFS enabled. But some embedded platforms might
+** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful.
**
-** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
-** the default.
-*/
-#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)>1
-# error "At most one of the following compile-time configuration options\
- is allows: SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG"
-#endif
-#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)==0
-# define SQLITE_SYSTEM_MALLOC 1
-#endif
-
-/*
-** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the
-** sizes of memory allocations below this value where possible.
+** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later.
*/
-#if !defined(SQLITE_MALLOC_SOFT_LIMIT)
-# define SQLITE_MALLOC_SOFT_LIMIT 1024
+#ifndef SQLITE_DISABLE_LFS
+# define _LARGE_FILE 1
+# ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+# endif
+# define _LARGEFILE_SOURCE 1
#endif
-/*
-** We need to define _XOPEN_SOURCE as follows in order to enable
-** recursive mutexes on most Unix systems. But Mac OS X is different.
-** The _XOPEN_SOURCE define causes problems for Mac OS X we are told,
-** so it is omitted there. See ticket #2673.
-**
-** Later we learn that _XOPEN_SOURCE is poorly or incorrectly
-** implemented on some systems. So we avoid defining it at all
-** if it is already defined or if it is unneeded because we are
-** not doing a threadsafe build. Ticket #2681.
-**
-** See also ticket #2741.
-*/
-#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__) && SQLITE_THREADSAFE
-#ifdef __sun
-# define _XOPEN_SOURCE 600
+/* What version of GCC is being used. 0 means GCC is not being used */
+#ifdef __GNUC__
+# define GCC_VERSION (__GNUC__*1000000+__GNUC_MINOR__*1000+__GNUC_PATCHLEVEL__)
#else
-# define _XOPEN_SOURCE 500 /* Needed to enable pthread recursive mutexes */
-#endif
-#endif
-
-/*
-** The TCL headers are only needed when compiling the TCL bindings.
-*/
-#if defined(SQLITE_TCL) || defined(TCLSH)
-# include <tcl.h>
-#endif
-
-/*
-** Many people are failing to set -DNDEBUG=1 when compiling SQLite.
-** Setting NDEBUG makes the code smaller and run faster. So the following
-** lines are added to automatically set NDEBUG unless the -DSQLITE_DEBUG=1
-** option is set. Thus NDEBUG becomes an opt-in rather than an opt-out
-** feature.
-*/
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
-# define NDEBUG 1
+# define GCC_VERSION 0
#endif
-/*
-** The testcase() macro is used to aid in coverage testing. When
-** doing coverage testing, the condition inside the argument to
-** testcase() must be evaluated both true and false in order to
-** get full branch coverage. The testcase() macro is inserted
-** to help ensure adequate test coverage in places where simple
-** condition/decision coverage is inadequate. For example, testcase()
-** can be used to make sure boundary values are tested. For
-** bitmask tests, testcase() can be used to make sure each bit
-** is significant and used at least once. On switch statements
-** where multiple cases go to the same block of code, testcase()
-** can insure that all cases are evaluated.
-**
-*/
-#ifdef SQLITE_COVERAGE_TEST
-SQLITE_PRIVATE void sqlite3Coverage(int);
-# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); }
-#else
-# define testcase(X)
+/* Needed for various definitions... */
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+# define _GNU_SOURCE
#endif
-/*
-** The TESTONLY macro is used to enclose variable declarations or
-** other bits of code that are needed to support the arguments
-** within testcase() and assert() macros.
-*/
-#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST)
-# define TESTONLY(X) X
-#else
-# define TESTONLY(X)
+#if defined(__OpenBSD__) && !defined(_BSD_SOURCE)
+# define _BSD_SOURCE
#endif
/*
-** Sometimes we need a small amount of code such as a variable initialization
-** to setup for a later assert() statement. We do not want this code to
-** appear when assert() is disabled. The following macro is therefore
-** used to contain that setup code. The "VVA" acronym stands for
-** "Verification, Validation, and Accreditation". In other words, the
-** code within VVA_ONLY() will only run during verification processes.
+** For MinGW, check to see if we can include the header file containing its
+** version information, among other things. Normally, this internal MinGW
+** header file would [only] be included automatically by other MinGW header
+** files; however, the contained version information is now required by this
+** header file to work around binary compatibility issues (see below) and
+** this is the only known way to reliably obtain it. This entire #if block
+** would be completely unnecessary if there was any other way of detecting
+** MinGW via their preprocessor (e.g. if they customized their GCC to define
+** some MinGW-specific macros). When compiling for MinGW, either the
+** _HAVE_MINGW_H or _HAVE__MINGW_H (note the extra underscore) macro must be
+** defined; otherwise, detection of conditions specific to MinGW will be
+** disabled.
*/
-#ifndef NDEBUG
-# define VVA_ONLY(X) X
-#else
-# define VVA_ONLY(X)
+#if defined(_HAVE_MINGW_H)
+# include "mingw.h"
+#elif defined(_HAVE__MINGW_H)
+# include "_mingw.h"
#endif
/*
-** The ALWAYS and NEVER macros surround boolean expressions which
-** are intended to always be true or false, respectively. Such
-** expressions could be omitted from the code completely. But they
-** are included in a few cases in order to enhance the resilience
-** of SQLite to unexpected behavior - to make the code "self-healing"
-** or "ductile" rather than being "brittle" and crashing at the first
-** hint of unplanned behavior.
-**
-** In other words, ALWAYS and NEVER are added for defensive code.
-**
-** When doing coverage testing ALWAYS and NEVER are hard-coded to
-** be true and false so that the unreachable code then specify will
-** not be counted as untested code.
+** For MinGW version 4.x (and higher), check to see if the _USE_32BIT_TIME_T
+** define is required to maintain binary compatibility with the MSVC runtime
+** library in use (e.g. for Windows XP).
*/
-#if defined(SQLITE_COVERAGE_TEST)
-# define ALWAYS(X) (1)
-# define NEVER(X) (0)
-#elif !defined(NDEBUG)
-# define ALWAYS(X) ((X)?1:(assert(0),0))
-# define NEVER(X) ((X)?(assert(0),1):0)
-#else
-# define ALWAYS(X) (X)
-# define NEVER(X) (X)
+#if !defined(_USE_32BIT_TIME_T) && !defined(_USE_64BIT_TIME_T) && \
+ defined(_WIN32) && !defined(_WIN64) && \
+ defined(__MINGW_MAJOR_VERSION) && __MINGW_MAJOR_VERSION >= 4 && \
+ defined(__MSVCRT__)
+# define _USE_32BIT_TIME_T
#endif
-/*
-** Return true (non-zero) if the input is a integer that is too large
-** to fit in 32-bits. This macro is used inside of various testcase()
-** macros to verify that we have tested SQLite for large-file support.
-*/
-#define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0)
-
-/*
-** The macro unlikely() is a hint that surrounds a boolean
-** expression that is usually false. Macro likely() surrounds
-** a boolean expression that is usually true. GCC is able to
-** use these hints to generate better code, sometimes.
+/* The public SQLite interface. The _FILE_OFFSET_BITS macro must appear
+** first in QNX. Also, the _USE_32BIT_TIME_T macro must appear first for
+** MinGW.
*/
-#if defined(__GNUC__) && 0
-# define likely(X) __builtin_expect((X),1)
-# define unlikely(X) __builtin_expect((X),0)
-#else
-# define likely(X) !!(X)
-# define unlikely(X) !!(X)
-#endif
-
/************** Include sqlite3.h in the middle of sqliteInt.h ***************/
/************** Begin file sqlite3.h *****************************************/
/*
@@ -576,15 +283,15 @@ SQLITE_PRIVATE void sqlite3Coverage(int);
**
** The official C-language API documentation for SQLite is derived
** from comments in this file. This file is the authoritative source
-** on how SQLite interfaces are suppose to operate.
+** on how SQLite interfaces are supposed to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
*/
-#ifndef _SQLITE3_H_
-#define _SQLITE3_H_
+#ifndef SQLITE3_H
+#define SQLITE3_H
#include <stdarg.h> /* Needed for the definition of va_list */
/*
@@ -596,21 +303,34 @@ extern "C" {
/*
-** Add the ability to override 'extern'
+** Provide the ability to override linkage features of the interface.
*/
#ifndef SQLITE_EXTERN
# define SQLITE_EXTERN extern
#endif
-
#ifndef SQLITE_API
# define SQLITE_API
#endif
-
+#ifndef SQLITE_CDECL
+# define SQLITE_CDECL
+#endif
+#ifndef SQLITE_APICALL
+# define SQLITE_APICALL
+#endif
+#ifndef SQLITE_STDCALL
+# define SQLITE_STDCALL SQLITE_APICALL
+#endif
+#ifndef SQLITE_CALLBACK
+# define SQLITE_CALLBACK
+#endif
+#ifndef SQLITE_SYSAPI
+# define SQLITE_SYSAPI
+#endif
/*
** These no-op macros are used in front of interfaces to mark those
** interfaces as either deprecated or experimental. New applications
-** should not use deprecated interfaces - they are support for backwards
+** should not use deprecated interfaces - they are supported for backwards
** compatibility only. Application writers should be aware that
** experimental interfaces are subject to change in point releases.
**
@@ -648,7 +368,8 @@ extern "C" {
** be held constant and Z will be incremented or else Y will be incremented
** and Z will be reset to zero.
**
-** Since version 3.6.18, SQLite source code has been stored in the
+** Since [version 3.6.18] ([dateof:3.6.18]),
+** SQLite source code has been stored in the
** <a href="http://www.fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
** a string which identifies a particular check-in of SQLite
@@ -660,9 +381,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.8"
-#define SQLITE_VERSION_NUMBER 3007008
-#define SQLITE_SOURCE_ID "2011-09-19 14:49:19 3e0da808d2f5b4d12046e05980ca04578f581177"
+#define SQLITE_VERSION "3.15.1"
+#define SQLITE_VERSION_NUMBER 3015001
+#define SQLITE_SOURCE_ID "2016-11-04 12:08:49 1136863c76576110e710dd5d69ab6bf347c65e36"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -673,7 +394,7 @@ extern "C" {
** but are associated with the library instead of the header file. ^(Cautious
** programmers might include assert() statements in their application to
** verify that values returned by these interfaces match the macros in
-** the header, and thus insure that the application is
+** the header, and thus ensure that the application is
** compiled with matching library and header files.
**
** <blockquote><pre>
@@ -730,7 +451,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N);
** CAPI3REF: Test To See If The Library Is Threadsafe
**
** ^The sqlite3_threadsafe() function returns zero if and only if
-** SQLite was compiled mutexing code omitted due to the
+** SQLite was compiled with mutexing code omitted due to the
** [SQLITE_THREADSAFE] compile-time option being set to 0.
**
** SQLite can be compiled with or without mutexes. When
@@ -754,7 +475,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N);
** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but
** can be fully or partially disabled using a call to [sqlite3_config()]
** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD],
-** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the
+** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the
** sqlite3_threadsafe() function shows only the compile-time setting of
** thread safety, not any run-time changes to that setting made by
** sqlite3_config(). In other words, the return value from sqlite3_threadsafe()
@@ -772,7 +493,8 @@ SQLITE_API int sqlite3_threadsafe(void);
** the opaque structure named "sqlite3". It is useful to think of an sqlite3
** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
-** is its destructor. There are many other interfaces (such as
+** and [sqlite3_close_v2()] are its destructors. There are many other
+** interfaces (such as
** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
** [sqlite3_busy_timeout()] to name but three) that are methods on an
** sqlite3 object.
@@ -818,29 +540,48 @@ typedef sqlite_uint64 sqlite3_uint64;
/*
** CAPI3REF: Closing A Database Connection
-**
-** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
-** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
-** successfully destroyed and all associated resources are deallocated.
-**
-** Applications must [sqlite3_finalize | finalize] all [prepared statements]
-** and [sqlite3_blob_close | close] all [BLOB handles] associated with
-** the [sqlite3] object prior to attempting to close the object. ^If
-** sqlite3_close() is called on a [database connection] that still has
-** outstanding [prepared statements] or [BLOB handles], then it returns
-** SQLITE_BUSY.
-**
-** ^If [sqlite3_close()] is invoked while a transaction is open,
+** DESTRUCTOR: sqlite3
+**
+** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
+** for the [sqlite3] object.
+** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
+** the [sqlite3] object is successfully destroyed and all associated
+** resources are deallocated.
+**
+** ^If the database connection is associated with unfinalized prepared
+** statements or unfinished sqlite3_backup objects then sqlite3_close()
+** will leave the database connection open and return [SQLITE_BUSY].
+** ^If sqlite3_close_v2() is called with unfinalized prepared statements
+** and/or unfinished sqlite3_backups, then the database connection becomes
+** an unusable "zombie" which will automatically be deallocated when the
+** last prepared statement is finalized or the last sqlite3_backup is
+** finished. The sqlite3_close_v2() interface is intended for use with
+** host languages that are garbage collected, and where the order in which
+** destructors are called is arbitrary.
+**
+** Applications should [sqlite3_finalize | finalize] all [prepared statements],
+** [sqlite3_blob_close | close] all [BLOB handles], and
+** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
+** with the [sqlite3] object prior to attempting to close the object. ^If
+** sqlite3_close_v2() is called on a [database connection] that still has
+** outstanding [prepared statements], [BLOB handles], and/or
+** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
+** of resources is deferred until all [prepared statements], [BLOB handles],
+** and [sqlite3_backup] objects are also destroyed.
+**
+** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
**
-** The C parameter to [sqlite3_close(C)] must be either a NULL
+** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
+** must be either a NULL
** pointer or an [sqlite3] object pointer obtained
** from [sqlite3_open()], [sqlite3_open16()], or
** [sqlite3_open_v2()], and not previously closed.
-** ^Calling sqlite3_close() with a NULL pointer argument is a
-** harmless no-op.
+** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer
+** argument is a harmless no-op.
*/
-SQLITE_API int sqlite3_close(sqlite3 *);
+SQLITE_API int sqlite3_close(sqlite3*);
+SQLITE_API int sqlite3_close_v2(sqlite3*);
/*
** The type for a callback function.
@@ -851,6 +592,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
/*
** CAPI3REF: One-Step Query Execution Interface
+** METHOD: sqlite3
**
** The sqlite3_exec() interface is a convenience wrapper around
** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()],
@@ -875,7 +617,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** from [sqlite3_malloc()] and passed back through the 5th parameter.
** To avoid memory leaks, the application should invoke [sqlite3_free()]
** on error message strings returned through the 5th parameter of
-** of sqlite3_exec() after the error message string is no longer needed.
+** sqlite3_exec() after the error message string is no longer needed.
** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors
** occur, then sqlite3_exec() sets the pointer in its 5th parameter to
** NULL before returning.
@@ -902,9 +644,9 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** Restrictions:
**
** <ul>
-** <li> The application must insure that the 1st parameter to sqlite3_exec()
+** <li> The application must ensure that the 1st parameter to sqlite3_exec()
** is a valid and open [database connection].
-** <li> The application must not close [database connection] specified by
+** <li> The application must not close the [database connection] specified by
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
@@ -920,16 +662,14 @@ SQLITE_API int sqlite3_exec(
/*
** CAPI3REF: Result Codes
-** KEYWORDS: SQLITE_OK {error code} {error codes}
-** KEYWORDS: {result code} {result codes}
+** KEYWORDS: {result code definitions}
**
** Many SQLite functions return an integer result code from the set shown
-** here in order to indicates success or failure.
+** here in order to indicate success or failure.
**
** New error codes may be added in future versions of SQLite.
**
-** See also: [SQLITE_IOERR_READ | extended result codes],
-** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].
+** See also: [extended result code definitions]
*/
#define SQLITE_OK 0 /* Successful result */
/* beginning-of-error-codes */
@@ -959,32 +699,28 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_FORMAT 24 /* Auxiliary database format error */
#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
#define SQLITE_NOTADB 26 /* File opened that is not a database file */
+#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */
+#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
/* end-of-error-codes */
/*
** CAPI3REF: Extended Result Codes
-** KEYWORDS: {extended error code} {extended error codes}
-** KEYWORDS: {extended result code} {extended result codes}
+** KEYWORDS: {extended result code definitions}
**
-** In its default configuration, SQLite API routines return one of 26 integer
-** [SQLITE_OK | result codes]. However, experience has shown that many of
+** In its default configuration, SQLite API routines return one of 30 integer
+** [result codes]. However, experience has shown that many of
** these result codes are too coarse-grained. They do not provide as
** much information about problems as programmers might like. In an effort to
-** address this, newer versions of SQLite (version 3.3.8 and later) include
+** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8]
+** and later) include
** support for additional result codes that provide more detailed information
-** about errors. The extended result codes are enabled or disabled
+** about errors. These [extended result codes] are enabled or disabled
** on a per database connection basis using the
-** [sqlite3_extended_result_codes()] API.
-**
-** Some of the available extended result codes are listed here.
-** One may expect the number of extended result codes will be expand
-** over time. Software that uses extended result codes should expect
-** to see new result codes in future releases of SQLite.
-**
-** The SQLITE_OK result code will never be extended. It will always
-** be exactly zero.
+** [sqlite3_extended_result_codes()] API. Or, the extended code for
+** the most recent error can be obtained using
+** [sqlite3_extended_errcode()].
*/
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
@@ -1008,12 +744,40 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8))
#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8))
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
+#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
+#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
+#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
+#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
+#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
+#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
+#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
+#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
+#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
+#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
+#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
+#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
+#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
+#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
+#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
+#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8))
+#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8))
+#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8))
+#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8))
+#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
+#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
+#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
+#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
+#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
+#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
+#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
+#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
@@ -1029,6 +793,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */
#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */
#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */
+#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */
#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */
#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */
@@ -1048,7 +813,7 @@ SQLITE_API int sqlite3_exec(
** CAPI3REF: Device Characteristics
**
** The xDeviceCharacteristics method of the [sqlite3_io_methods]
-** object returns an integer which is a vector of the these
+** object returns an integer which is a vector of these
** bit values expressing I/O characteristics of the mass storage
** device that holds the file that the [sqlite3_io_methods]
** refers to.
@@ -1062,7 +827,15 @@ SQLITE_API int sqlite3_exec(
** first then the size of the file is extended, never the other
** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
** information is written to disk in the same order as calls
-** to xWrite().
+** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that
+** after reboot following a crash or power loss, the only bytes in a
+** file that were written at the application level might have changed
+** and that adjacent bytes, even bytes within the same sector are
+** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
+** flag indicate that a file cannot be deleted when open. The
+** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
+** read-only media and cannot be changed even by processes with
+** elevated privileges.
*/
#define SQLITE_IOCAP_ATOMIC 0x00000001
#define SQLITE_IOCAP_ATOMIC512 0x00000002
@@ -1076,6 +849,8 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
+#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
+#define SQLITE_IOCAP_IMMUTABLE 0x00002000
/*
** CAPI3REF: File Locking Levels
@@ -1182,7 +957,7 @@ struct sqlite3_file {
** locking strategy (for example to use dot-file locks), to inquire
** about the status of a lock, or to break stale locks. The SQLite
** core reserves all opcodes less than 100 for its own use.
-** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available.
+** A [file control opcodes | list of opcodes] less than 100 is available.
** Applications that define a custom xFileControl method should use opcodes
** greater than 100 to avoid conflicts. VFS implementations should
** return [SQLITE_NOTFOUND] for file control opcodes that they do not
@@ -1247,24 +1022,31 @@ struct sqlite3_io_methods {
void (*xShmBarrier)(sqlite3_file*);
int (*xShmUnmap)(sqlite3_file*, int deleteFlag);
/* Methods above are valid for version 2 */
+ int (*xFetch)(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
+ int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p);
+ /* Methods above are valid for version 3 */
/* Additional methods may be added in future releases */
};
/*
** CAPI3REF: Standard File Control Opcodes
+** KEYWORDS: {file control opcodes} {file control opcode}
**
** These integer constants are opcodes for the xFileControl method
** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()]
** interface.
**
+** <ul>
+** <li>[[SQLITE_FCNTL_LOCKSTATE]]
** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This
** opcode causes the xFileControl method to write the current state of
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
** into an integer that the pArg argument points to. This capability
-** is used during testing and only needs to be supported when SQLITE_TEST
-** is defined.
+** is used during testing and is only available when the SQLITE_TEST
+** compile-time option is used.
**
+** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
** layer a hint of how large the database file will grow to be during the
** current transaction. This hint is not guaranteed to be accurate but it
@@ -1272,6 +1054,7 @@ struct sqlite3_io_methods {
** file space based on this hint in order to help writes to the database
** file run faster.
**
+** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
** extends and truncates the database file in chunks of a size specified
** by the user. The fourth argument to [sqlite3_file_control()] should
@@ -1280,29 +1063,51 @@ struct sqlite3_io_methods {
** chunks (say 1MB at a time), may reduce file-system fragmentation and
** improve performance on some systems.
**
+** <li>[[SQLITE_FCNTL_FILE_POINTER]]
** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with a particular database
-** connection. See the [sqlite3_file_control()] documentation for
-** additional information.
-**
-** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by
-** SQLite and sent to all VFSes in place of a call to the xSync method
-** when the database connection has [PRAGMA synchronous] set to OFF.)^
-** Some specialized VFSes need this signal in order to operate correctly
-** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most
-** VFSes do not need this signal and should silently ignore this opcode.
-** Applications should not call [sqlite3_file_control()] with this
-** opcode as doing so may disrupt the operation of the specialized VFSes
-** that do require it.
-**
+** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER].
+**
+** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]]
+** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer
+** to the [sqlite3_file] object associated with the journal file (either
+** the [rollback journal] or the [write-ahead log]) for a particular database
+** connection. See also [SQLITE_FCNTL_FILE_POINTER].
+**
+** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
+** No longer in use.
+**
+** <li>[[SQLITE_FCNTL_SYNC]]
+** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
+** sent to the VFS immediately before the xSync method is invoked on a
+** database file descriptor. Or, if the xSync method is not invoked
+** because the user has configured SQLite with
+** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
+** of the xSync method. In most cases, the pointer argument passed with
+** this file-control is NULL. However, if the database file is being synced
+** as part of a multi-database commit, the argument points to a nul-terminated
+** string containing the transactions master-journal file name. VFSes that
+** do not need this signal should silently ignore this opcode. Applications
+** should not call [sqlite3_file_control()] with this opcode as doing so may
+** disrupt the operation of the specialized VFSes that do require it.
+**
+** <li>[[SQLITE_FCNTL_COMMIT_PHASETWO]]
+** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite
+** and sent to the VFS after a transaction has been committed immediately
+** but before the database is unlocked. VFSes that do not need this signal
+** should silently ignore this opcode. Applications should not call
+** [sqlite3_file_control()] with this opcode as doing so may disrupt the
+** operation of the specialized VFSes that do require it.
+**
+** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
** retry counts and intervals for certain disk I/O operations for the
-** windows [VFS] in order to work to provide robustness against
+** windows [VFS] in order to provide robustness in the presence of
** anti-virus programs. By default, the windows VFS will retry file read,
-** file write, and file delete opertions up to 10 times, with a delay
+** file write, and file delete operations up to 10 times, with a delay
** of 25 milliseconds before the first retry and with the delay increasing
** by an additional 25 milliseconds with each subsequent retry. This
-** opcode allows those to values (10 retries and 25 milliseconds of delay)
+** opcode allows these two values (10 retries and 25 milliseconds of delay)
** to be adjusted. The values are changed for all database connections
** within the same process. The argument is a pointer to an array of two
** integers where the first integer i the new retry count and the second
@@ -1311,8 +1116,9 @@ struct sqlite3_io_methods {
** into the array entry, allowing the current retry settings to be
** interrogated. The zDbName parameter is ignored.
**
+** <li>[[SQLITE_FCNTL_PERSIST_WAL]]
** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
-** persistent [WAL | Write AHead Log] setting. By default, the auxiliary
+** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary
** write ahead log and shared memory files used for transaction control
** are automatically deleted when the latest connection to the database
** closes. Setting persistent WAL mode causes those files to persist after
@@ -1324,18 +1130,177 @@ struct sqlite3_io_methods {
** That integer is 0 to disable persistent WAL mode or 1 to enable persistent
** WAL mode. If the integer is -1, then it is overwritten with the current
** WAL persistence setting.
-**
+**
+** <li>[[SQLITE_FCNTL_POWERSAFE_OVERWRITE]]
+** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
+** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting
+** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the
+** xDeviceCharacteristics methods. The fourth parameter to
+** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
+** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage
+** mode. If the integer is -1, then it is overwritten with the current
+** zero-damage mode setting.
+**
+** <li>[[SQLITE_FCNTL_OVERWRITE]]
+** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
+** a write transaction to indicate that, unless it is rolled back for some
+** reason, the entire database file will be overwritten by the current
+** transaction. This is used by VACUUM operations.
+**
+** <li>[[SQLITE_FCNTL_VFSNAME]]
+** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
+** all [VFSes] in the VFS stack. The names are of all VFS shims and the
+** final bottom-level VFS are written into memory obtained from
+** [sqlite3_malloc()] and the result is stored in the char* variable
+** that the fourth parameter of [sqlite3_file_control()] points to.
+** The caller is responsible for freeing the memory when done. As with
+** all file-control actions, there is no guarantee that this will actually
+** do anything. Callers should initialize the char* variable to a NULL
+** pointer in case this file-control is not implemented. This file-control
+** is intended for diagnostic use only.
+**
+** <li>[[SQLITE_FCNTL_VFS_POINTER]]
+** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
+** [VFSes] currently in use. ^(The argument X in
+** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
+** of type "[sqlite3_vfs] **". This opcodes will set *X
+** to a pointer to the top-level VFS.)^
+** ^When there are multiple VFS shims in the stack, this opcode finds the
+** upper-most shim only.
+**
+** <li>[[SQLITE_FCNTL_PRAGMA]]
+** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
+** file control is sent to the open [sqlite3_file] object corresponding
+** to the database file to which the pragma statement refers. ^The argument
+** to the [SQLITE_FCNTL_PRAGMA] file control is an array of
+** pointers to strings (char**) in which the second element of the array
+** is the name of the pragma and the third element is the argument to the
+** pragma or NULL if the pragma has no argument. ^The handler for an
+** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element
+** of the char** argument point to a string obtained from [sqlite3_mprintf()]
+** or the equivalent and that string will become the result of the pragma or
+** the error message if the pragma fails. ^If the
+** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal
+** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA]
+** file control returns [SQLITE_OK], then the parser assumes that the
+** VFS has handled the PRAGMA itself and the parser generates a no-op
+** prepared statement if result string is NULL, or that returns a copy
+** of the result string if the string is non-NULL.
+** ^If the [SQLITE_FCNTL_PRAGMA] file control returns
+** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means
+** that the VFS encountered an error while handling the [PRAGMA] and the
+** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
+** file control occurs at the beginning of pragma statement analysis and so
+** it is able to override built-in [PRAGMA] statements.
+**
+** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
+** ^The [SQLITE_FCNTL_BUSYHANDLER]
+** file-control may be invoked by SQLite on the database file handle
+** shortly after it is opened in order to provide a custom VFS with access
+** to the connections busy-handler callback. The argument is of type (void **)
+** - an array of two (void *) values. The first (void *) actually points
+** to a function of type (int (*)(void *)). In order to invoke the connections
+** busy-handler, this function should be invoked with the second (void *) in
+** the array as the only argument. If it returns non-zero, then the operation
+** should be retried. If it returns zero, the custom VFS should abandon the
+** current operation.
+**
+** <li>[[SQLITE_FCNTL_TEMPFILENAME]]
+** ^Application can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control
+** to have SQLite generate a
+** temporary filename using the same algorithm that is followed to generate
+** temporary filenames for TEMP tables and other internal uses. The
+** argument should be a char** which will be filled with the filename
+** written into memory obtained from [sqlite3_malloc()]. The caller should
+** invoke [sqlite3_free()] on the result to avoid a memory leak.
+**
+** <li>[[SQLITE_FCNTL_MMAP_SIZE]]
+** The [SQLITE_FCNTL_MMAP_SIZE] file control is used to query or set the
+** maximum number of bytes that will be used for memory-mapped I/O.
+** The argument is a pointer to a value of type sqlite3_int64 that
+** is an advisory maximum number of bytes in the file to memory map. The
+** pointer is overwritten with the old value. The limit is not changed if
+** the value originally pointed to is negative, and so the current limit
+** can be queried by passing in a pointer to a negative number. This
+** file-control is used internally to implement [PRAGMA mmap_size].
+**
+** <li>[[SQLITE_FCNTL_TRACE]]
+** The [SQLITE_FCNTL_TRACE] file control provides advisory information
+** to the VFS about what the higher layers of the SQLite stack are doing.
+** This file control is used by some VFS activity tracing [shims].
+** The argument is a zero-terminated string. Higher layers in the
+** SQLite stack may generate instances of this file control if
+** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled.
+**
+** <li>[[SQLITE_FCNTL_HAS_MOVED]]
+** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a
+** pointer to an integer and it writes a boolean into that integer depending
+** on whether or not the file has been renamed, moved, or deleted since it
+** was first opened.
+**
+** <li>[[SQLITE_FCNTL_WIN32_GET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_GET_HANDLE] opcode can be used to obtain the
+** underlying native file handle associated with a file handle. This file
+** control interprets its argument as a pointer to a native file handle and
+** writes the resulting value there.
+**
+** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
+** opcode causes the xFileControl method to swap the file handle with the one
+** pointed to by the pArg argument. This capability is used during testing
+** and only needs to be supported when SQLITE_TEST is defined.
+**
+** <li>[[SQLITE_FCNTL_WAL_BLOCK]]
+** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might
+** be advantageous to block on the next WAL lock if the lock is not immediately
+** available. The WAL subsystem issues this signal during rare
+** circumstances in order to fix a problem with priority inversion.
+** Applications should <em>not</em> use this file-control.
+**
+** <li>[[SQLITE_FCNTL_ZIPVFS]]
+** The [SQLITE_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other
+** VFS should return SQLITE_NOTFOUND for this opcode.
+**
+** <li>[[SQLITE_FCNTL_RBU]]
+** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
+** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
+** this opcode.
+** </ul>
*/
-#define SQLITE_FCNTL_LOCKSTATE 1
-#define SQLITE_GET_LOCKPROXYFILE 2
-#define SQLITE_SET_LOCKPROXYFILE 3
-#define SQLITE_LAST_ERRNO 4
-#define SQLITE_FCNTL_SIZE_HINT 5
-#define SQLITE_FCNTL_CHUNK_SIZE 6
-#define SQLITE_FCNTL_FILE_POINTER 7
-#define SQLITE_FCNTL_SYNC_OMITTED 8
-#define SQLITE_FCNTL_WIN32_AV_RETRY 9
-#define SQLITE_FCNTL_PERSIST_WAL 10
+#define SQLITE_FCNTL_LOCKSTATE 1
+#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2
+#define SQLITE_FCNTL_SET_LOCKPROXYFILE 3
+#define SQLITE_FCNTL_LAST_ERRNO 4
+#define SQLITE_FCNTL_SIZE_HINT 5
+#define SQLITE_FCNTL_CHUNK_SIZE 6
+#define SQLITE_FCNTL_FILE_POINTER 7
+#define SQLITE_FCNTL_SYNC_OMITTED 8
+#define SQLITE_FCNTL_WIN32_AV_RETRY 9
+#define SQLITE_FCNTL_PERSIST_WAL 10
+#define SQLITE_FCNTL_OVERWRITE 11
+#define SQLITE_FCNTL_VFSNAME 12
+#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
+#define SQLITE_FCNTL_PRAGMA 14
+#define SQLITE_FCNTL_BUSYHANDLER 15
+#define SQLITE_FCNTL_TEMPFILENAME 16
+#define SQLITE_FCNTL_MMAP_SIZE 18
+#define SQLITE_FCNTL_TRACE 19
+#define SQLITE_FCNTL_HAS_MOVED 20
+#define SQLITE_FCNTL_SYNC 21
+#define SQLITE_FCNTL_COMMIT_PHASETWO 22
+#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
+#define SQLITE_FCNTL_WAL_BLOCK 24
+#define SQLITE_FCNTL_ZIPVFS 25
+#define SQLITE_FCNTL_RBU 26
+#define SQLITE_FCNTL_VFS_POINTER 27
+#define SQLITE_FCNTL_JOURNAL_POINTER 28
+#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
+
+/* deprecated names */
+#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
+#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
+#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
+
/*
** CAPI3REF: Mutex Handle
@@ -1350,6 +1315,16 @@ struct sqlite3_io_methods {
typedef struct sqlite3_mutex sqlite3_mutex;
/*
+** CAPI3REF: Loadable Extension Thunk
+**
+** A pointer to the opaque sqlite3_api_routines structure is passed as
+** the third parameter to entry points of [loadable extensions]. This
+** structure must be typedefed in order to work around compiler warnings
+** on some platforms.
+*/
+typedef struct sqlite3_api_routines sqlite3_api_routines;
+
+/*
** CAPI3REF: OS Interface Object
**
** An instance of the sqlite3_vfs object defines the interface between
@@ -1390,7 +1365,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
** from xFullPathname() with an optional suffix added.
** ^If a suffix is added to the zFilename parameter, it will
** consist of a single "-" character followed by no more than
-** 10 alphanumeric and/or "-" characters.
+** 11 alphanumeric and/or "-" characters.
** ^SQLite further guarantees that
** the string will be valid and unchanged until xClose() is
** called. Because of the previous sentence,
@@ -1542,7 +1517,7 @@ struct sqlite3_vfs {
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
- ** New fields may be appended in figure versions. The iVersion
+ ** New fields may be appended in future versions. The iVersion
** value will increment whenever this happens.
*/
};
@@ -1587,7 +1562,7 @@ struct sqlite3_vfs {
** </ul>
**
** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as
-** was given no the corresponding lock.
+** was given on the corresponding lock.
**
** The xShmLock method can transition between unlocked and SHARED or
** between unlocked and EXCLUSIVE. It cannot transition between SHARED
@@ -1698,9 +1673,11 @@ SQLITE_API int sqlite3_os_end(void);
** applications and so this routine is usually not necessary. It is
** provided to support rare applications with unusual needs.
**
-** The sqlite3_config() interface is not threadsafe. The application
-** must insure that no other SQLite interfaces are invoked by other
-** threads while sqlite3_config() is running. Furthermore, sqlite3_config()
+** <b>The sqlite3_config() interface is not threadsafe. The application
+** must ensure that no other SQLite interfaces are invoked by other
+** threads while sqlite3_config() is running.</b>
+**
+** The sqlite3_config() interface
** may only be invoked prior to library initialization using
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
@@ -1722,6 +1699,7 @@ SQLITE_API int sqlite3_config(int, ...);
/*
** CAPI3REF: Configure database connections
+** METHOD: sqlite3
**
** The sqlite3_db_config() interface is used to make configuration
** changes to a [database connection]. The interface is similar to
@@ -1780,7 +1758,7 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
** that causes the corresponding memory allocation to fail.
**
-** The xInit method initializes the memory allocator. (For example,
+** The xInit method initializes the memory allocator. For example,
** it might allocate any require mutexes or initialize internal data
** structures. The xShutdown method is invoked (indirectly) by
** [sqlite3_shutdown()] and should deallocate any resources acquired
@@ -1870,31 +1848,33 @@ struct sqlite3_mem_methods {
** SQLITE_CONFIG_SERIALIZED configuration option.</dd>
**
** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mem_methods] structure. The argument specifies
+** <dd> ^(The SQLITE_CONFIG_MALLOC option takes a single argument which is
+** a pointer to an instance of the [sqlite3_mem_methods] structure.
+** The argument specifies
** alternative low-level memory allocation routines to be used in place of
** the memory allocation routines built into SQLite.)^ ^SQLite makes
** its own private copy of the content of the [sqlite3_mem_methods] structure
** before the [sqlite3_config()] call returns.</dd>
**
** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods]
+** <dd> ^(The SQLITE_CONFIG_GETMALLOC option takes a single argument which
+** is a pointer to an instance of the [sqlite3_mem_methods] structure.
+** The [sqlite3_mem_methods]
** structure is filled with the currently defined memory allocation routines.)^
** This option can be used to overload the default memory allocation
** routines with a wrapper that simulations memory allocation failure or
** tracks memory usage, for example. </dd>
**
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
-** <dd> ^This option takes single argument of type int, interpreted as a
-** boolean, which enables or disables the collection of memory allocation
-** statistics. ^(When memory allocation statistics are disabled, the
-** following SQLite interfaces become non-operational:
+** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int,
+** interpreted as a boolean, which enables or disables the collection of
+** memory allocation statistics. ^(When memory allocation statistics are
+** disabled, the following SQLite interfaces become non-operational:
** <ul>
** <li> [sqlite3_memory_used()]
** <li> [sqlite3_memory_highwater()]
** <li> [sqlite3_soft_heap_limit64()]
-** <li> [sqlite3_status()]
+** <li> [sqlite3_status64()]
** </ul>)^
** ^Memory allocation statistics are enabled by default unless SQLite is
** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory
@@ -1902,65 +1882,84 @@ struct sqlite3_mem_methods {
** </dd>
**
** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>
-** <dd> ^This option specifies a static memory buffer that SQLite can use for
-** scratch memory. There are three arguments: A pointer an 8-byte
+** <dd> ^The SQLITE_CONFIG_SCRATCH option specifies a static memory buffer
+** that SQLite can use for scratch memory. ^(There are three arguments
+** to SQLITE_CONFIG_SCRATCH: A pointer an 8-byte
** aligned memory buffer from which the scratch allocations will be
** drawn, the size of each scratch allocation (sz),
-** and the maximum number of scratch allocations (N). The sz
-** argument must be a multiple of 16.
+** and the maximum number of scratch allocations (N).)^
** The first argument must be a pointer to an 8-byte aligned buffer
** of at least sz*N bytes of memory.
-** ^SQLite will use no more than two scratch buffers per thread. So
-** N should be set to twice the expected maximum number of threads.
-** ^SQLite will never require a scratch buffer that is more than 6
-** times the database page size. ^If SQLite needs needs additional
+** ^SQLite will not use more than one scratch buffers per thread.
+** ^SQLite will never request a scratch buffer that is more than 6
+** times the database page size.
+** ^If SQLite needs needs additional
** scratch memory beyond what is provided by this configuration option, then
-** [sqlite3_malloc()] will be used to obtain the memory needed.</dd>
+** [sqlite3_malloc()] will be used to obtain the memory needed.<p>
+** ^When the application provides any amount of scratch memory using
+** SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary large
+** [sqlite3_malloc|heap allocations].
+** This can help [Robson proof|prevent memory allocation failures] due to heap
+** fragmentation in low-memory embedded systems.
+** </dd>
**
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
-** <dd> ^This option specifies a static memory buffer that SQLite can use for
-** the database page cache with the default page cache implementation.
-** This configuration should not be used if an application-define page
-** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option.
-** There are three arguments to this option: A pointer to 8-byte aligned
-** memory, the size of each page buffer (sz), and the number of pages (N).
+** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool
+** that SQLite can use for the database page cache with the default page
+** cache implementation.
+** This configuration option is a no-op if an application-define page
+** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2].
+** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to
+** 8-byte aligned memory (pMem), the size of each page cache line (sz),
+** and the number of cache lines (N).
** The sz argument should be the size of the largest database page
-** (a power of two between 512 and 32768) plus a little extra for each
-** page header. ^The page header size is 20 to 40 bytes depending on
-** the host architecture. ^It is harmless, apart from the wasted memory,
-** to make sz a little too large. The first
-** argument should point to an allocation of at least sz*N bytes of memory.
-** ^SQLite will use the memory provided by the first argument to satisfy its
-** memory needs for the first N pages that it adds to cache. ^If additional
-** page cache memory is needed beyond what is provided by this option, then
-** SQLite goes to [sqlite3_malloc()] for the additional storage space.
-** The pointer in the first argument must
-** be aligned to an 8-byte boundary or subsequent behavior of SQLite
-** will be undefined.</dd>
+** (a power of two between 512 and 65536) plus some extra bytes for each
+** page header. ^The number of extra bytes needed by the page header
+** can be determined using [SQLITE_CONFIG_PCACHE_HDRSZ].
+** ^It is harmless, apart from the wasted memory,
+** for the sz parameter to be larger than necessary. The pMem
+** argument must be either a NULL pointer or a pointer to an 8-byte
+** aligned block of memory of at least sz*N bytes, otherwise
+** subsequent behavior is undefined.
+** ^When pMem is not NULL, SQLite will strive to use the memory provided
+** to satisfy page cache needs, falling back to [sqlite3_malloc()] if
+** a page cache line is larger than sz bytes or if all of the pMem buffer
+** is exhausted.
+** ^If pMem is NULL and N is non-zero, then each database connection
+** does an initial bulk allocation for page cache memory
+** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
+** of -1024*N bytes if N is negative, . ^If additional
+** page cache memory is needed beyond what is provided by the initial
+** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
+** additional cache line. </dd>
**
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
-** <dd> ^This option specifies a static memory buffer that SQLite will use
-** for all of its dynamic memory allocation needs beyond those provided
-** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].
-** There are three arguments: An 8-byte aligned pointer to the memory,
+** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
+** that SQLite will use for all of its dynamic memory allocation needs
+** beyond those provided for by [SQLITE_CONFIG_SCRATCH] and
+** [SQLITE_CONFIG_PAGECACHE].
+** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled
+** with either [SQLITE_ENABLE_MEMSYS3] or [SQLITE_ENABLE_MEMSYS5] and returns
+** [SQLITE_ERROR] if invoked otherwise.
+** ^There are three arguments to SQLITE_CONFIG_HEAP:
+** An 8-byte aligned pointer to the memory,
** the number of bytes in the memory buffer, and the minimum allocation size.
** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts
** to using its default memory allocator (the system malloc() implementation),
** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the
-** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
-** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
+** memory pointer is not NULL then the alternative memory
** allocator is engaged to handle all of SQLites memory allocation needs.
** The first pointer (the memory pointer) must be aligned to an 8-byte
** boundary or subsequent behavior of SQLite will be undefined.
-** The minimum allocation size is capped at 2^12. Reasonable values
-** for the minimum allocation size are 2^5 through 2^8.</dd>
+** The minimum allocation size is capped at 2**12. Reasonable values
+** for the minimum allocation size are 2**5 through 2**8.</dd>
**
** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mutex_methods] structure. The argument specifies
-** alternative low-level mutex routines to be used in place
-** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the
-** content of the [sqlite3_mutex_methods] structure before the call to
+** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a
+** pointer to an instance of the [sqlite3_mutex_methods] structure.
+** The argument specifies alternative low-level mutex routines to be used
+** in place the mutex routines built into SQLite.)^ ^SQLite makes a copy of
+** the content of the [sqlite3_mutex_methods] structure before the call to
** [sqlite3_config()] returns. ^If SQLite is compiled with
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
** the entire mutexing subsystem is omitted from the build and hence calls to
@@ -1968,8 +1967,8 @@ struct sqlite3_mem_methods {
** return [SQLITE_ERROR].</dd>
**
** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mutex_methods] structure. The
+** <dd> ^(The SQLITE_CONFIG_GETMUTEX option takes a single argument which
+** is a pointer to an instance of the [sqlite3_mutex_methods] structure. The
** [sqlite3_mutex_methods]
** structure is filled with the currently defined mutex routines.)^
** This option can be used to overload the default mutex allocation
@@ -1981,28 +1980,30 @@ struct sqlite3_mem_methods {
** return [SQLITE_ERROR].</dd>
**
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
-** <dd> ^(This option takes two arguments that determine the default
-** memory allocation for the lookaside memory allocator on each
-** [database connection]. The first argument is the
+** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
+** the default size of lookaside memory on each [database connection].
+** The first argument is the
** size of each lookaside buffer slot and the second is the number of
-** slots allocated to each database connection.)^ ^(This option sets the
-** <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
-** verb to [sqlite3_db_config()] can be used to change the lookaside
+** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
+** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
+** option to [sqlite3_db_config()] can be used to change the lookaside
** configuration on individual connections.)^ </dd>
**
-** [[SQLITE_CONFIG_PCACHE]] <dt>SQLITE_CONFIG_PCACHE</dt>
-** <dd> ^(This option takes a single argument which is a pointer to
-** an [sqlite3_pcache_methods] object. This object specifies the interface
-** to a custom page cache implementation.)^ ^SQLite makes a copy of the
-** object and uses it for page cache memory allocations.</dd>
+** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
+** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
+** a pointer to an [sqlite3_pcache_methods2] object. This object specifies
+** the interface to a custom page cache implementation.)^
+** ^SQLite makes a copy of the [sqlite3_pcache_methods2] object.</dd>
**
-** [[SQLITE_CONFIG_GETPCACHE]] <dt>SQLITE_CONFIG_GETPCACHE</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** [sqlite3_pcache_methods] object. SQLite copies of the current
-** page cache implementation into that object.)^ </dd>
+** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
+** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which
+** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies of
+** the current page cache implementation into that object.)^ </dd>
**
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
-** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
+** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite
+** global [error log].
+** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
** function with a call signature of void(*)(void*,int,const char*),
** and a pointer to void. ^If the function pointer is not NULL, it is
** invoked by [sqlite3_log()] to process each logging event. ^If the
@@ -2020,17 +2021,108 @@ struct sqlite3_mem_methods {
** function must be threadsafe. </dd>
**
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
-** <dd> This option takes a single argument of type int. If non-zero, then
-** URI handling is globally enabled. If the parameter is zero, then URI handling
-** is globally disabled. If URI handling is globally enabled, all filenames
-** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
+** <dd>^(The SQLITE_CONFIG_URI option takes a single argument of type int.
+** If non-zero, then URI handling is globally enabled. If the parameter is zero,
+** then URI handling is globally disabled.)^ ^If URI handling is globally
+** enabled, all filenames passed to [sqlite3_open()], [sqlite3_open_v2()],
+** [sqlite3_open16()] or
** specified as part of [ATTACH] commands are interpreted as URIs, regardless
** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
-** connection is opened. If it is globally disabled, filenames are
+** connection is opened. ^If it is globally disabled, filenames are
** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
-** database connection is opened. By default, URI handling is globally
+** database connection is opened. ^(By default, URI handling is globally
** disabled. The default value may be changed by compiling with the
-** [SQLITE_USE_URI] symbol defined.
+** [SQLITE_USE_URI] symbol defined.)^
+**
+** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
+** <dd>^The SQLITE_CONFIG_COVERING_INDEX_SCAN option takes a single integer
+** argument which is interpreted as a boolean in order to enable or disable
+** the use of covering indices for full table scans in the query optimizer.
+** ^The default setting is determined
+** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
+** if that compile-time option is omitted.
+** The ability to disable the use of covering indices for full table scans
+** is because some incorrectly coded legacy applications might malfunction
+** when the optimization is enabled. Providing the ability to
+** disable the optimization allows the older, buggy application code to work
+** without change even with newer versions of SQLite.
+**
+** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]]
+** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE
+** <dd> These options are obsolete and should not be used by new code.
+** They are retained for backwards compatibility but are now no-ops.
+** </dd>
+**
+** [[SQLITE_CONFIG_SQLLOG]]
+** <dt>SQLITE_CONFIG_SQLLOG
+** <dd>This option is only available if sqlite is compiled with the
+** [SQLITE_ENABLE_SQLLOG] pre-processor macro defined. The first argument should
+** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int).
+** The second should be of type (void*). The callback is invoked by the library
+** in three separate circumstances, identified by the value passed as the
+** fourth parameter. If the fourth parameter is 0, then the database connection
+** passed as the second argument has just been opened. The third argument
+** points to a buffer containing the name of the main database file. If the
+** fourth parameter is 1, then the SQL statement that the third parameter
+** points to has just been executed. Or, if the fourth parameter is 2, then
+** the connection being passed as the second parameter is being closed. The
+** third parameter is passed NULL In this case. An example of using this
+** configuration option can be seen in the "test_sqllog.c" source file in
+** the canonical SQLite source tree.</dd>
+**
+** [[SQLITE_CONFIG_MMAP_SIZE]]
+** <dt>SQLITE_CONFIG_MMAP_SIZE
+** <dd>^SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
+** that are the default mmap size limit (the default setting for
+** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
+** ^The default setting can be overridden by each database connection using
+** either the [PRAGMA mmap_size] command, or by using the
+** [SQLITE_FCNTL_MMAP_SIZE] file control. ^(The maximum allowed mmap size
+** will be silently truncated if necessary so that it does not exceed the
+** compile-time maximum mmap size set by the
+** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^
+** ^If either argument to this option is negative, then that argument is
+** changed to its compile-time default.
+**
+** [[SQLITE_CONFIG_WIN32_HEAPSIZE]]
+** <dt>SQLITE_CONFIG_WIN32_HEAPSIZE
+** <dd>^The SQLITE_CONFIG_WIN32_HEAPSIZE option is only available if SQLite is
+** compiled for Windows with the [SQLITE_WIN32_MALLOC] pre-processor macro
+** defined. ^SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value
+** that specifies the maximum size of the created heap.
+**
+** [[SQLITE_CONFIG_PCACHE_HDRSZ]]
+** <dt>SQLITE_CONFIG_PCACHE_HDRSZ
+** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which
+** is a pointer to an integer and writes into that integer the number of extra
+** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE].
+** The amount of extra space required can change depending on the compiler,
+** target platform, and SQLite version.
+**
+** [[SQLITE_CONFIG_PMASZ]]
+** <dt>SQLITE_CONFIG_PMASZ
+** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which
+** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded
+** sorter to that integer. The default minimum PMA Size is set by the
+** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched
+** to help with sort operations when multithreaded sorting
+** is enabled (using the [PRAGMA threads] command) and the amount of content
+** to be sorted exceeds the page size times the minimum of the
+** [PRAGMA cache_size] setting and this value.
+**
+** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
+** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
+** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
+** becomes the [statement journal] spill-to-disk threshold.
+** [Statement journals] are held in memory until their size (in bytes)
+** exceeds this threshold, at which point they are written to disk.
+** Or if the threshold is -1, statement journals are always held
+** exclusively in memory.
+** Since many statement journals never become large, setting the spill
+** threshold to a value such as 64KiB can greatly reduce the amount of
+** I/O required to support statement rollback.
+** The default value for this setting is controlled by the
+** [SQLITE_STMTJRNL_SPILL] compile-time option.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -2046,10 +2138,19 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
-#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */
-#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */
+#define SQLITE_CONFIG_PCACHE 14 /* no-op */
+#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
#define SQLITE_CONFIG_URI 17 /* int */
+#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
+#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
+#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
+#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
+#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
+#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
+#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -2107,15 +2208,58 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
+** <dd> ^This option is used to enable or disable the two-argument
+** version of the [fts3_tokenizer()] function which is part of the
+** [FTS3] full-text search engine extension.
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable fts3_tokenizer() or
+** positive to enable fts3_tokenizer() or negative to leave the setting
+** unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the new setting is not reported back. </dd>
+**
+** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
+** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
+** interface independently of the [load_extension()] SQL function.
+** The [sqlite3_enable_load_extension()] API enables or disables both the
+** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
+** There should be two additional arguments.
+** When the first argument to this interface is 1, then only the C-API is
+** enabled and the SQL function remains disabled. If the first argument to
+** this interface is 0, then both the C-API and the SQL function are disabled.
+** If the first argument is -1, then no changes are made to state of either the
+** C-API or the SQL function.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
+** is disabled or enabled following this call. The second parameter may
+** be a NULL pointer, in which case the new setting is not reported back.
+** </dd>
+**
+** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
+** <dd> ^This option is used to change the name of the "main" database
+** schema. ^The sole argument is a pointer to a constant UTF8 string
+** which will become the new schema name in place of "main". ^SQLite
+** does not make a copy of the new main schema name string, so the application
+** must ensure that the argument passed into this DBCONFIG option is unchanged
+** until after the database connection closes.
+** </dd>
+**
** </dl>
*/
-#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
-#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
-#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
+#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
+#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
+** METHOD: sqlite3
**
** ^The sqlite3_extended_result_codes() routine enables or disables the
** [extended result codes] feature of SQLite. ^The extended result
@@ -2125,20 +2269,23 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff);
/*
** CAPI3REF: Last Insert Rowid
+** METHOD: sqlite3
**
-** ^Each entry in an SQLite table has a unique 64-bit signed
+** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables)
+** has a unique 64-bit signed
** integer key called the [ROWID | "rowid"]. ^The rowid is always available
** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
** names are not also used by explicitly declared columns. ^If
** the table has a column of type [INTEGER PRIMARY KEY] then that column
** is another alias for the rowid.
**
-** ^This routine returns the [rowid] of the most recent
-** successful [INSERT] into the database from the [database connection]
-** in the first argument. ^As of SQLite version 3.7.7, this routines
-** records the last insert rowid of both ordinary tables and [virtual tables].
-** ^If no successful [INSERT]s
-** have ever occurred on that database connection, zero is returned.
+** ^The sqlite3_last_insert_rowid(D) interface returns the [rowid] of the
+** most recent successful [INSERT] into a rowid table or [virtual table]
+** on database connection D.
+** ^Inserts into [WITHOUT ROWID] tables are not recorded.
+** ^If no successful [INSERT]s into rowid tables
+** have ever occurred on the database connection D,
+** then sqlite3_last_insert_rowid(D) returns zero.
**
** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
** method, then this routine will return the [rowid] of the inserted
@@ -2174,48 +2321,47 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
/*
** CAPI3REF: Count The Number Of Rows Modified
+** METHOD: sqlite3
**
-** ^This function returns the number of database rows that were changed
-** or inserted or deleted by the most recently completed SQL statement
-** on the [database connection] specified by the first parameter.
-** ^(Only changes that are directly specified by the [INSERT], [UPDATE],
-** or [DELETE] statement are counted. Auxiliary changes caused by
-** triggers or [foreign key actions] are not counted.)^ Use the
-** [sqlite3_total_changes()] function to find the total number of changes
-** including changes caused by triggers and foreign key actions.
-**
-** ^Changes to a view that are simulated by an [INSTEAD OF trigger]
-** are not counted. Only real table changes are counted.
-**
-** ^(A "row change" is a change to a single row of a single table
-** caused by an INSERT, DELETE, or UPDATE statement. Rows that
-** are changed as side effects of [REPLACE] constraint resolution,
-** rollback, ABORT processing, [DROP TABLE], or by any other
-** mechanisms do not count as direct row changes.)^
-**
-** A "trigger context" is a scope of execution that begins and
-** ends with the script of a [CREATE TRIGGER | trigger].
-** Most SQL statements are
-** evaluated outside of any trigger. This is the "top level"
-** trigger context. If a trigger fires from the top level, a
-** new trigger context is entered for the duration of that one
-** trigger. Subtriggers create subcontexts for their duration.
-**
-** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does
-** not create a new trigger context.
-**
-** ^This function returns the number of direct row changes in the
-** most recent INSERT, UPDATE, or DELETE statement within the same
-** trigger context.
-**
-** ^Thus, when called from the top level, this function returns the
-** number of changes in the most recent INSERT, UPDATE, or DELETE
-** that also occurred at the top level. ^(Within the body of a trigger,
-** the sqlite3_changes() interface can be called to find the number of
-** changes in the most recently completed INSERT, UPDATE, or DELETE
-** statement within the body of the same trigger.
-** However, the number returned does not include changes
-** caused by subtriggers since those have their own context.)^
+** ^This function returns the number of rows modified, inserted or
+** deleted by the most recently completed INSERT, UPDATE or DELETE
+** statement on the database connection specified by the only parameter.
+** ^Executing any other type of SQL statement does not modify the value
+** returned by this function.
+**
+** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
+** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
+** [foreign key actions] or [REPLACE] constraint resolution are not counted.
+**
+** Changes to a view that are intercepted by
+** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value
+** returned by sqlite3_changes() immediately after an INSERT, UPDATE or
+** DELETE statement run on a view is always zero. Only changes made to real
+** tables are counted.
+**
+** Things are more complicated if the sqlite3_changes() function is
+** executed while a trigger program is running. This may happen if the
+** program uses the [changes() SQL function], or if some other callback
+** function invokes sqlite3_changes() directly. Essentially:
+**
+** <ul>
+** <li> ^(Before entering a trigger program the value returned by
+** sqlite3_changes() function is saved. After the trigger program
+** has finished, the original value is restored.)^
+**
+** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE
+** statement sets the value returned by sqlite3_changes()
+** upon completion as normal. Of course, this value will not include
+** any changes performed by sub-triggers, as the sqlite3_changes()
+** value will be saved and restored after each sub-trigger has run.)^
+** </ul>
+**
+** ^This means that if the changes() SQL function (or similar) is used
+** by the first INSERT, UPDATE or DELETE statement within a trigger, it
+** returns the value as set when the calling statement began executing.
+** ^If it is used by the second or subsequent such statement within a trigger
+** program, the value returned reflects the number of rows modified by the
+** previous INSERT, UPDATE or DELETE statement within the same trigger.
**
** See also the [sqlite3_total_changes()] interface, the
** [count_changes pragma], and the [changes() SQL function].
@@ -2228,21 +2374,19 @@ SQLITE_API int sqlite3_changes(sqlite3*);
/*
** CAPI3REF: Total Number Of Rows Modified
+** METHOD: sqlite3
**
-** ^This function returns the number of row changes caused by [INSERT],
-** [UPDATE] or [DELETE] statements since the [database connection] was opened.
-** ^(The count returned by sqlite3_total_changes() includes all changes
-** from all [CREATE TRIGGER | trigger] contexts and changes made by
-** [foreign key actions]. However,
-** the count does not include changes used to implement [REPLACE] constraints,
-** do rollbacks or ABORT processing, or [DROP TABLE] processing. The
-** count does not include rows of views that fire an [INSTEAD OF trigger],
-** though if the INSTEAD OF trigger makes changes of its own, those changes
-** are counted.)^
-** ^The sqlite3_total_changes() function counts the changes as soon as
-** the statement that makes them is completed (when the statement handle
-** is passed to [sqlite3_reset()] or [sqlite3_finalize()]).
-**
+** ^This function returns the total number of rows inserted, modified or
+** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
+** since the database connection was opened, including those executed as
+** part of trigger programs. ^Executing any other type of SQL statement
+** does not affect the value returned by sqlite3_total_changes().
+**
+** ^Changes made as part of [foreign key actions] are included in the
+** count, but those made as part of REPLACE constraint resolution are
+** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
+** are not counted.
+**
** See also the [sqlite3_changes()] interface, the
** [count_changes pragma], and the [total_changes() SQL function].
**
@@ -2254,6 +2398,7 @@ SQLITE_API int sqlite3_total_changes(sqlite3*);
/*
** CAPI3REF: Interrupt A Long-Running Query
+** METHOD: sqlite3
**
** ^This function causes any pending database operation to abort and
** return at its earliest opportunity. This routine is typically
@@ -2329,28 +2474,36 @@ SQLITE_API int sqlite3_complete16(const void *sql);
/*
** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors
-**
-** ^This routine sets a callback function that might be invoked whenever
-** an attempt is made to open a database table that another thread
-** or process has locked.
-**
-** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]
+** KEYWORDS: {busy-handler callback} {busy handler}
+** METHOD: sqlite3
+**
+** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X
+** that might be invoked with argument P whenever
+** an attempt is made to access a database table associated with
+** [database connection] D when another thread
+** or process has the table locked.
+** The sqlite3_busy_handler() interface is used to implement
+** [sqlite3_busy_timeout()] and [PRAGMA busy_timeout].
+**
+** ^If the busy callback is NULL, then [SQLITE_BUSY]
** is returned immediately upon encountering the lock. ^If the busy callback
** is not NULL, then the callback might be invoked with two arguments.
**
** ^The first argument to the busy handler is a copy of the void* pointer which
** is the third argument to sqlite3_busy_handler(). ^The second argument to
** the busy handler callback is the number of times that the busy handler has
-** been invoked for this locking event. ^If the
+** been invoked previously for the same locking event. ^If the
** busy callback returns 0, then no additional attempts are made to
-** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned.
+** access the database and [SQLITE_BUSY] is returned
+** to the application.
** ^If the callback returns non-zero, then another attempt
-** is made to open the database for reading and the cycle repeats.
+** is made to access the database and the cycle repeats.
**
** The presence of a busy handler does not guarantee that it will be invoked
** when there is lock contention. ^If SQLite determines that invoking the busy
** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY]
-** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler.
+** to the application instead of invoking the
+** busy handler.
** Consider a scenario where one process is holding a read lock that
** it is trying to promote to a reserved lock and
** a second process is holding a reserved lock that it is trying
@@ -2364,57 +2517,48 @@ SQLITE_API int sqlite3_complete16(const void *sql);
**
** ^The default busy callback is NULL.
**
-** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED]
-** when SQLite is in the middle of a large transaction where all the
-** changes will not fit into the in-memory cache. SQLite will
-** already hold a RESERVED lock on the database file, but it needs
-** to promote this lock to EXCLUSIVE so that it can spill cache
-** pages into the database file without harm to concurrent
-** readers. ^If it is unable to promote the lock, then the in-memory
-** cache will be left in an inconsistent state and so the error
-** code is promoted from the relatively benign [SQLITE_BUSY] to
-** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion
-** forces an automatic rollback of the changes. See the
-** <a href="/cvstrac/wiki?p=CorruptionFollowingBusyError">
-** CorruptionFollowingBusyError</a> wiki page for a discussion of why
-** this is important.
-**
** ^(There can only be a single busy handler defined for each
** [database connection]. Setting a new busy handler clears any
** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()]
-** will also set or clear the busy handler.
+** or evaluating [PRAGMA busy_timeout=N] will change the
+** busy handler and thus clear any previously set busy handler.
**
** The busy callback should not take any actions which modify the
-** database connection that invoked the busy handler. Any such actions
+** database connection that invoked the busy handler. In other words,
+** the busy handler is not reentrant. Any such actions
** result in undefined behavior.
**
** A busy handler must not close the database connection
** or [prepared statement] that invoked the busy handler.
*/
-SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
+SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
/*
** CAPI3REF: Set A Busy Timeout
+** METHOD: sqlite3
**
** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps
** for a specified amount of time when a table is locked. ^The handler
** will sleep multiple times until at least "ms" milliseconds of sleeping
** have accumulated. ^After at least "ms" milliseconds of sleeping,
** the handler returns 0 which causes [sqlite3_step()] to return
-** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED].
+** [SQLITE_BUSY].
**
** ^Calling this routine with an argument less than or equal to zero
** turns off all busy handlers.
**
** ^(There can only be a single busy handler for a particular
-** [database connection] any any given moment. If another busy handler
+** [database connection] at any given moment. If another busy handler
** was defined (using [sqlite3_busy_handler()]) prior to calling
** this routine, that other busy handler is cleared.)^
+**
+** See also: [PRAGMA busy_timeout]
*/
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
/*
** CAPI3REF: Convenience Routines For Running Queries
+** METHOD: sqlite3
**
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
@@ -2500,6 +2644,10 @@ SQLITE_API void sqlite3_free_table(char **result);
**
** These routines are work-alikes of the "printf()" family of functions
** from the standard C library.
+** These routines understand most of the common K&R formatting options,
+** plus some additional non-standard formats, detailed below.
+** Note that some of the more obscure formatting options from recent
+** C-library standards are omitted from this implementation.
**
** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
** results into memory obtained from [sqlite3_malloc()].
@@ -2532,9 +2680,9 @@ SQLITE_API void sqlite3_free_table(char **result);
** These routines all implement some additional formatting
** options that are useful for constructing SQL statements.
** All of the usual printf() formatting options apply. In addition, there
-** is are "%q", "%Q", and "%z" options.
+** is are "%q", "%Q", "%w" and "%z" options.
**
-** ^(The %q option works like %s in that it substitutes a null-terminated
+** ^(The %q option works like %s in that it substitutes a nul-terminated
** string from the argument list. But %q also doubles every '\'' character.
** %q is designed for use inside a string literal.)^ By doubling each '\''
** character it escapes that character and allows it to be inserted into
@@ -2585,6 +2733,12 @@ SQLITE_API void sqlite3_free_table(char **result);
** The code above will render a correct SQL statement in the zSQL
** variable even if the zText variable is a NULL pointer.
**
+** ^(The "%w" formatting option is like "%q" except that it expects to
+** be contained within double-quotes instead of single quotes, and it
+** escapes the double-quote character instead of the single-quote
+** character.)^ The "%w" formatting option is intended for safely inserting
+** table and column names into a constructed SQL statement.
+**
** ^(The "%z" formatting option works like "%s" but with the
** addition that after the string has been read and copied into
** the result, [sqlite3_free()] is called on the input string.)^
@@ -2609,6 +2763,10 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns
** a NULL pointer.
**
+** ^The sqlite3_malloc64(N) routine works just like
+** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead
+** of a signed 32-bit integer.
+**
** ^Calling sqlite3_free() with a pointer previously returned
** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
** that it might be reused. ^The sqlite3_free() routine is
@@ -2620,24 +2778,38 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** might result if sqlite3_free() is called with a non-NULL pointer that
** was not obtained from sqlite3_malloc() or sqlite3_realloc().
**
-** ^(The sqlite3_realloc() interface attempts to resize a
-** prior memory allocation to be at least N bytes, where N is the
-** second parameter. The memory allocation to be resized is the first
-** parameter.)^ ^ If the first parameter to sqlite3_realloc()
+** ^The sqlite3_realloc(X,N) interface attempts to resize a
+** prior memory allocation X to be at least N bytes.
+** ^If the X parameter to sqlite3_realloc(X,N)
** is a NULL pointer then its behavior is identical to calling
-** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().
-** ^If the second parameter to sqlite3_realloc() is zero or
+** sqlite3_malloc(N).
+** ^If the N parameter to sqlite3_realloc(X,N) is zero or
** negative then the behavior is exactly the same as calling
-** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().
-** ^sqlite3_realloc() returns a pointer to a memory allocation
-** of at least N bytes in size or NULL if sufficient memory is unavailable.
+** sqlite3_free(X).
+** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation
+** of at least N bytes in size or NULL if insufficient memory is available.
** ^If M is the size of the prior allocation, then min(N,M) bytes
** of the prior allocation are copied into the beginning of buffer returned
-** by sqlite3_realloc() and the prior allocation is freed.
-** ^If sqlite3_realloc() returns NULL, then the prior allocation
-** is not freed.
-**
-** ^The memory returned by sqlite3_malloc() and sqlite3_realloc()
+** by sqlite3_realloc(X,N) and the prior allocation is freed.
+** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the
+** prior allocation is not freed.
+**
+** ^The sqlite3_realloc64(X,N) interfaces works the same as
+** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead
+** of a 32-bit signed integer.
+**
+** ^If X is a memory allocation previously obtained from sqlite3_malloc(),
+** sqlite3_malloc64(), sqlite3_realloc(), or sqlite3_realloc64(), then
+** sqlite3_msize(X) returns the size of that memory allocation in bytes.
+** ^The value returned by sqlite3_msize(X) might be larger than the number
+** of bytes requested when X was allocated. ^If X is a NULL pointer then
+** sqlite3_msize(X) returns zero. If X points to something that is not
+** the beginning of memory allocation, or if it points to a formerly
+** valid memory allocation that has now been freed, then the behavior
+** of sqlite3_msize(X) is undefined and possibly harmful.
+**
+** ^The memory returned by sqlite3_malloc(), sqlite3_realloc(),
+** sqlite3_malloc64(), and sqlite3_realloc64()
** is always aligned to at least an 8 byte boundary, or to a
** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time
** option is used.
@@ -2647,12 +2819,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** implementation of these routines to be omitted. That capability
** is no longer provided. Only built-in memory allocators can be used.
**
-** The Windows OS interface layer calls
+** Prior to SQLite version 3.7.10, the Windows OS interface layer called
** the system malloc() and free() directly when converting
** filenames between the UTF-8 encoding used by SQLite
** and whatever filename encoding is used by the particular Windows
-** installation. Memory allocation errors are detected, but
-** they are reported back as [SQLITE_CANTOPEN] or
+** installation. Memory allocation errors were detected, but
+** they were reported back as [SQLITE_CANTOPEN] or
** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
**
** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
@@ -2665,8 +2837,11 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** [sqlite3_free()] or [sqlite3_realloc()].
*/
SQLITE_API void *sqlite3_malloc(int);
+SQLITE_API void *sqlite3_malloc64(sqlite3_uint64);
SQLITE_API void *sqlite3_realloc(void*, int);
+SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64);
SQLITE_API void sqlite3_free(void*);
+SQLITE_API sqlite3_uint64 sqlite3_msize(void*);
/*
** CAPI3REF: Memory Allocator Statistics
@@ -2704,11 +2879,14 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
** applications to access the same PRNG for other purposes.
**
** ^A call to this routine stores N bytes of randomness into buffer P.
-**
-** ^The first time this routine is invoked (either internally or by
-** the application) the PRNG is seeded using randomness obtained
-** from the xRandomness method of the default [sqlite3_vfs] object.
-** ^On all subsequent invocations, the pseudo-randomness is generated
+** ^The P parameter can be a NULL pointer.
+**
+** ^If this routine has not been previously called or if the previous
+** call had N less than one or a NULL pointer for P, then the PRNG is
+** seeded using randomness obtained from the xRandomness method of
+** the default [sqlite3_vfs] object.
+** ^If the previous call to this routine had an N of 1 or more and a
+** non-NULL P then the pseudo-randomness is generated
** internally and without recourse to the [sqlite3_vfs] xRandomness
** method.
*/
@@ -2716,6 +2894,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P);
/*
** CAPI3REF: Compile-Time Authorization Callbacks
+** METHOD: sqlite3
**
** ^This routine registers an authorizer callback with a particular
** [database connection], supplied in the first argument.
@@ -2809,8 +2988,8 @@ SQLITE_API int sqlite3_set_authorizer(
** [sqlite3_set_authorizer | authorizer documentation] for additional
** information.
**
-** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]
-** from the [sqlite3_vtab_on_conflict()] interface.
+** Note that SQLITE_IGNORE is also used as a [conflict resolution mode]
+** returned from the [sqlite3_vtab_on_conflict()] interface.
*/
#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
@@ -2868,9 +3047,14 @@ SQLITE_API int sqlite3_set_authorizer(
#define SQLITE_FUNCTION 31 /* NULL Function Name */
#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */
#define SQLITE_COPY 0 /* No longer used */
+#define SQLITE_RECURSIVE 33 /* NULL NULL */
/*
** CAPI3REF: Tracing And Profiling Functions
+** METHOD: sqlite3
+**
+** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
+** instead of the routines described here.
**
** These routines register callback functions that can be used for
** tracing and profiling the execution of SQL statements.
@@ -2883,6 +3067,9 @@ SQLITE_API int sqlite3_set_authorizer(
** as each triggered subprogram is entered. The callbacks for triggers
** contain a UTF-8 SQL comment that identifies the trigger.)^
**
+** The [SQLITE_TRACE_SIZE_LIMIT] compile-time option can be used to limit
+** the length of [bound parameter] expansion in the output of sqlite3_trace().
+**
** ^The callback function registered by sqlite3_profile() is invoked
** as each SQL statement finishes. ^The profile callback contains
** the original statement text and an estimate of wall-clock time
@@ -2894,12 +3081,107 @@ SQLITE_API int sqlite3_set_authorizer(
** sqlite3_profile() function is considered experimental and is
** subject to change in future versions of SQLite.
*/
-SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
-SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
+SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
+ void(*xTrace)(void*,const char*), void*);
+SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
/*
+** CAPI3REF: SQL Trace Event Codes
+** KEYWORDS: SQLITE_TRACE
+**
+** These constants identify classes of events that can be monitored
+** using the [sqlite3_trace_v2()] tracing logic. The third argument
+** to [sqlite3_trace_v2()] is an OR-ed combination of one or more of
+** the following constants. ^The first argument to the trace callback
+** is one of the following constants.
+**
+** New tracing constants may be added in future releases.
+**
+** ^A trace callback has four arguments: xCallback(T,C,P,X).
+** ^The T argument is one of the integer type codes above.
+** ^The C argument is a copy of the context pointer passed in as the
+** fourth argument to [sqlite3_trace_v2()].
+** The P and X arguments are pointers whose meanings depend on T.
+**
+** <dl>
+** [[SQLITE_TRACE_STMT]] <dt>SQLITE_TRACE_STMT</dt>
+** <dd>^An SQLITE_TRACE_STMT callback is invoked when a prepared statement
+** first begins running and possibly at other times during the
+** execution of the prepared statement, such as at the start of each
+** trigger subprogram. ^The P argument is a pointer to the
+** [prepared statement]. ^The X argument is a pointer to a string which
+** is the unexpanded SQL text of the prepared statement or an SQL comment
+** that indicates the invocation of a trigger. ^The callback can compute
+** the same text that would have been returned by the legacy [sqlite3_trace()]
+** interface by using the X argument when X begins with "--" and invoking
+** [sqlite3_expanded_sql(P)] otherwise.
+**
+** [[SQLITE_TRACE_PROFILE]] <dt>SQLITE_TRACE_PROFILE</dt>
+** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
+** information as is provided by the [sqlite3_profile()] callback.
+** ^The P argument is a pointer to the [prepared statement] and the
+** X argument points to a 64-bit integer which is the estimated of
+** the number of nanosecond that the prepared statement took to run.
+** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
+**
+** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
+** <dd>^An SQLITE_TRACE_ROW callback is invoked whenever a prepared
+** statement generates a single row of result.
+** ^The P argument is a pointer to the [prepared statement] and the
+** X argument is unused.
+**
+** [[SQLITE_TRACE_CLOSE]] <dt>SQLITE_TRACE_CLOSE</dt>
+** <dd>^An SQLITE_TRACE_CLOSE callback is invoked when a database
+** connection closes.
+** ^The P argument is a pointer to the [database connection] object
+** and the X argument is unused.
+** </dl>
+*/
+#define SQLITE_TRACE_STMT 0x01
+#define SQLITE_TRACE_PROFILE 0x02
+#define SQLITE_TRACE_ROW 0x04
+#define SQLITE_TRACE_CLOSE 0x08
+
+/*
+** CAPI3REF: SQL Trace Hook
+** METHOD: sqlite3
+**
+** ^The sqlite3_trace_v2(D,M,X,P) interface registers a trace callback
+** function X against [database connection] D, using property mask M
+** and context pointer P. ^If the X callback is
+** NULL or if the M mask is zero, then tracing is disabled. The
+** M argument should be the bitwise OR-ed combination of
+** zero or more [SQLITE_TRACE] constants.
+**
+** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides
+** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2().
+**
+** ^The X callback is invoked whenever any of the events identified by
+** mask M occur. ^The integer return value from the callback is currently
+** ignored, though this may change in future releases. Callback
+** implementations should return zero to ensure future compatibility.
+**
+** ^A trace callback is invoked with four arguments: callback(T,C,P,X).
+** ^The T argument is one of the [SQLITE_TRACE]
+** constants to indicate why the callback was invoked.
+** ^The C argument is a copy of the context pointer.
+** The P and X arguments are pointers whose meanings depend on T.
+**
+** The sqlite3_trace_v2() interface is intended to replace the legacy
+** interfaces [sqlite3_trace()] and [sqlite3_profile()], both of which
+** are deprecated.
+*/
+SQLITE_API int sqlite3_trace_v2(
+ sqlite3*,
+ unsigned uMask,
+ int(*xCallback)(unsigned,void*,void*,void*),
+ void *pCtx
+);
+
+/*
** CAPI3REF: Query Progress Callbacks
+** METHOD: sqlite3
**
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
** function X to be invoked periodically during long running calls to
@@ -2908,9 +3190,10 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
** interface is to keep a GUI updated during a large query.
**
** ^The parameter P is passed through as the only parameter to the
-** callback function X. ^The parameter N is the number of
+** callback function X. ^The parameter N is the approximate number of
** [virtual machine instructions] that are evaluated between successive
-** invocations of the callback X.
+** invocations of the callback X. ^If N is less than one then the progress
+** handler is disabled.
**
** ^Only a single progress handler may be defined at one time per
** [database connection]; setting a new progress handler cancels the
@@ -2932,6 +3215,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
/*
** CAPI3REF: Opening A New Database Connection
+** CONSTRUCTOR: sqlite3
**
** ^These routines open an SQLite database file as specified by the
** filename argument. ^The filename argument is interpreted as UTF-8 for
@@ -2946,9 +3230,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** an English language description of the error following a failure of any
** of the sqlite3_open() routines.
**
-** ^The default encoding for the database will be UTF-8 if
-** sqlite3_open() or sqlite3_open_v2() is called and
-** UTF-16 in the native byte order if sqlite3_open16() is used.
+** ^The default encoding will be UTF-8 for databases created using
+** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases
+** created using sqlite3_open16() will be UTF-16 in the native byte order.
**
** Whether or not an error occurs when it is opened, resources
** associated with the [database connection] handle should be released by
@@ -3036,13 +3320,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** then it is interpreted as an absolute path. ^If the path does not begin
** with a '/' (meaning that the authority section is omitted from the URI)
** then the path is interpreted as a relative path.
-** ^On windows, the first component of an absolute path
-** is a drive specification (e.g. "C:").
+** ^(On windows, the first component of an absolute path
+** is a drive specification (e.g. "C:").)^
**
** [[core URI query parameters]]
** The query component of a URI may contain parameters that are interpreted
** either by SQLite itself, or by a [VFS | custom VFS implementation].
-** SQLite interprets the following three query parameters:
+** SQLite and its built-in [VFSes] interpret the
+** following query parameters:
**
** <ul>
** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of
@@ -3053,18 +3338,20 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** present, then the VFS specified by the option takes precedence over
** the value passed as the fourth parameter to sqlite3_open_v2().
**
-** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or
-** "rwc". Attempting to set it to any other value is an error)^.
+** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
+** "rwc", or "memory". Attempting to set it to any other value is
+** an error)^.
** ^If "ro" is specified, then the database is opened for read-only
** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
-** third argument to sqlite3_prepare_v2(). ^If the mode option is set to
+** third argument to sqlite3_open_v2(). ^If the mode option is set to
** "rw", then the database is opened for read-write (but not create)
** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
** been set. ^Value "rwc" is equivalent to setting both
-** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is
-** used, it is an error to specify a value for the mode parameter that is
-** less restrictive than that specified by the flags passed as the third
-** parameter.
+** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is
+** set to "memory" then a pure [in-memory database] that never reads
+** or writes from disk is used. ^It is an error to specify a value for
+** the mode parameter that is less restrictive than that specified by
+** the flags passed in the third parameter to sqlite3_open_v2().
**
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
** "private". ^Setting it to "shared" is equivalent to setting the
@@ -3072,8 +3359,30 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
-** a URI filename, its value overrides any behaviour requested by setting
+** a URI filename, its value overrides any behavior requested by setting
** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
+**
+** <li> <b>psow</b>: ^The psow parameter indicates whether or not the
+** [powersafe overwrite] property does or does not apply to the
+** storage media on which the database file resides.
+**
+** <li> <b>nolock</b>: ^The nolock parameter is a boolean query parameter
+** which if set disables file locking in rollback journal modes. This
+** is useful for accessing a database on a filesystem that does not
+** support locking. Caution: Database corruption might result if two
+** or more processes write to the same database and any one of those
+** processes uses nolock=1.
+**
+** <li> <b>immutable</b>: ^The immutable parameter is a boolean query
+** parameter that indicates that the database file is stored on
+** read-only media. ^When immutable is set, SQLite assumes that the
+** database file cannot be changed, even by a process with higher
+** privilege, and so the database is opened read-only and all locking
+** and change detection is disabled. Caution: Setting the immutable
+** property on a database file that does in fact change can result
+** in incorrect query results and/or [SQLITE_CORRUPT] errors.
+** See also: [SQLITE_IOCAP_IMMUTABLE].
+**
** </ul>
**
** ^Specifying an unknown parameter in the query component of a URI is not an
@@ -3103,8 +3412,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** Open file "data.db" in the current directory for read-only access.
** Regardless of whether or not shared-cache mode is enabled by
** default, use a private cache.
-** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td>
-** Open file "/home/fred/data.db". Use the special VFS "unix-nolock".
+** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
+** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
+** that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
** An error. "readonly" is not a valid option for the "mode" parameter.
** </table>
@@ -3123,6 +3433,12 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** codepage is currently defined. Filenames containing international
** characters must be converted to UTF-8 prior to passing them into
** sqlite3_open() or sqlite3_open_v2().
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various
+** features that require the use of temporary files may fail.
+**
+** See also: [sqlite3_temp_directory]
*/
SQLITE_API int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
@@ -3142,31 +3458,58 @@ SQLITE_API int sqlite3_open_v2(
/*
** CAPI3REF: Obtain Values For URI Parameters
**
-** This is a utility routine, useful to VFS implementations, that checks
+** These are utility routines, useful to VFS implementations, that check
** to see if a database file was a URI that contained a specific query
-** parameter, and if so obtains the value of the query parameter.
-**
-** The zFilename argument is the filename pointer passed into the xOpen()
-** method of a VFS implementation. The zParam argument is the name of the
-** query parameter we seek. This routine returns the value of the zParam
-** parameter if it exists. If the parameter does not exist, this routine
-** returns a NULL pointer.
-**
-** If the zFilename argument to this function is not a pointer that SQLite
-** passed into the xOpen VFS method, then the behavior of this routine
-** is undefined and probably undesirable.
+** parameter, and if so obtains the value of that query parameter.
+**
+** If F is the database filename pointer passed into the xOpen() method of
+** a VFS implementation when the flags parameter to xOpen() has one or
+** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and
+** P is the name of the query parameter, then
+** sqlite3_uri_parameter(F,P) returns the value of the P
+** parameter if it exists or a NULL pointer if P does not appear as a
+** query parameter on F. If P is a query parameter of F
+** has no explicit value, then sqlite3_uri_parameter(F,P) returns
+** a pointer to an empty string.
+**
+** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean
+** parameter and returns true (1) or false (0) according to the value
+** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the
+** value of query parameter P is one of "yes", "true", or "on" in any
+** case or if the value begins with a non-zero number. The
+** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of
+** query parameter P is one of "no", "false", or "off" in any case or
+** if the value begins with a numeric zero. If P is not a query
+** parameter on F or if the value of P is does not match any of the
+** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0).
+**
+** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
+** 64-bit signed integer and returns that integer, or D if P does not
+** exist. If the value of P is something other than an integer, then
+** zero is returned.
+**
+** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and
+** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and
+** is not a database file pathname pointer that SQLite passed into the xOpen
+** VFS method, then the behavior of this routine is undefined and probably
+** undesirable.
*/
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
+SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
+SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
/*
** CAPI3REF: Error Codes And Messages
-**
-** ^The sqlite3_errcode() interface returns the numeric [result code] or
-** [extended result code] for the most recent failed sqlite3_* API call
-** associated with a [database connection]. If a prior API call failed
-** but the most recent API call succeeded, the return value from
-** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode()
+** METHOD: sqlite3
+**
+** ^If the most recent sqlite3_* API call associated with
+** [database connection] D failed, then the sqlite3_errcode(D) interface
+** returns the numeric [result code] or [extended result code] for that
+** API call.
+** If the most recent API call was successful,
+** then the return value from sqlite3_errcode() is undefined.
+** ^The sqlite3_extended_errcode()
** interface is the same except that it always returns the
** [extended result code] even when extended result codes are
** disabled.
@@ -3178,6 +3521,11 @@ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
+** ^The sqlite3_errstr() interface returns the English-language text
+** that describes the [result code], as UTF-8.
+** ^(Memory to hold the error message string is managed internally
+** and must not be freed by the application)^.
+**
** When the serialized [threading mode] is in use, it might be the
** case that a second error occurs on a separate thread in between
** the time of the first error and the call to these interfaces.
@@ -3196,35 +3544,37 @@ SQLITE_API int sqlite3_errcode(sqlite3 *db);
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
+SQLITE_API const char *sqlite3_errstr(int);
/*
-** CAPI3REF: SQL Statement Object
+** CAPI3REF: Prepared Statement Object
** KEYWORDS: {prepared statement} {prepared statements}
**
-** An instance of this object represents a single SQL statement.
-** This object is variously known as a "prepared statement" or a
-** "compiled SQL statement" or simply as a "statement".
+** An instance of this object represents a single SQL statement that
+** has been compiled into binary form and is ready to be evaluated.
**
-** The life of a statement object goes something like this:
+** Think of each SQL statement as a separate computer program. The
+** original SQL text is source code. A prepared statement object
+** is the compiled object code. All SQL must be converted into a
+** prepared statement before it can be run.
+**
+** The life-cycle of a prepared statement object usually goes like this:
**
** <ol>
-** <li> Create the object using [sqlite3_prepare_v2()] or a related
-** function.
-** <li> Bind values to [host parameters] using the sqlite3_bind_*()
+** <li> Create the prepared statement object using [sqlite3_prepare_v2()].
+** <li> Bind values to [parameters] using the sqlite3_bind_*()
** interfaces.
** <li> Run the SQL by calling [sqlite3_step()] one or more times.
-** <li> Reset the statement using [sqlite3_reset()] then go back
+** <li> Reset the prepared statement using [sqlite3_reset()] then go back
** to step 2. Do this zero or more times.
** <li> Destroy the object using [sqlite3_finalize()].
** </ol>
-**
-** Refer to documentation on individual methods above for additional
-** information.
*/
typedef struct sqlite3_stmt sqlite3_stmt;
/*
** CAPI3REF: Run-time Limits
+** METHOD: sqlite3
**
** ^(This interface allows the size of various constructs to be limited
** on a connection by connection basis. The first parameter is the
@@ -3314,6 +3664,10 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
**
** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
** <dd>The maximum depth of recursion for triggers.</dd>)^
+**
+** [[SQLITE_LIMIT_WORKER_THREADS]] ^(<dt>SQLITE_LIMIT_WORKER_THREADS</dt>
+** <dd>The maximum number of auxiliary worker threads that a single
+** [prepared statement] may start.</dd>)^
** </dl>
*/
#define SQLITE_LIMIT_LENGTH 0
@@ -3327,10 +3681,13 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8
#define SQLITE_LIMIT_VARIABLE_NUMBER 9
#define SQLITE_LIMIT_TRIGGER_DEPTH 10
+#define SQLITE_LIMIT_WORKER_THREADS 11
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
+** METHOD: sqlite3
+** CONSTRUCTOR: sqlite3_stmt
**
** To execute an SQL query, it must first be compiled into a byte-code
** program using one of these routines.
@@ -3344,15 +3701,14 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2()
** use UTF-16.
**
-** ^If the nByte argument is less than zero, then zSql is read up to the
-** first zero terminator. ^If nByte is non-negative, then it is the maximum
-** number of bytes read from zSql. ^When nByte is non-negative, the
-** zSql string ends at either the first '\000' or '\u0000' character or
-** the nByte-th byte, whichever comes first. If the caller knows
-** that the supplied string is nul-terminated, then there is a small
-** performance advantage to be gained by passing an nByte parameter that
-** is equal to the number of bytes in the input string <i>including</i>
-** the nul-terminator bytes.
+** ^If the nByte argument is negative, then zSql is read up to the
+** first zero terminator. ^If nByte is positive, then it is the
+** number of bytes read from zSql. ^If nByte is zero, then no prepared
+** statement is generated.
+** If the caller knows that the supplied string is nul-terminated, then
+** there is a small performance advantage to passing an nByte parameter that
+** is the number of bytes in the input string <i>including</i>
+** the nul-terminator.
**
** ^If pzTail is not NULL then *pzTail is made to point to the first byte
** past the end of the first SQL statement in zSql. These routines only
@@ -3382,7 +3738,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** <li>
** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
** always used to do, [sqlite3_step()] will automatically recompile the SQL
-** statement and try to run it again.
+** statement and try to run it again. As many as [SQLITE_MAX_SCHEMA_RETRY]
+** retries will occur before sqlite3_step() gives up and returns an error.
** </li>
**
** <li>
@@ -3403,8 +3760,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled.
-** the
+** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
** </li>
** </ol>
*/
@@ -3439,15 +3795,41 @@ SQLITE_API int sqlite3_prepare16_v2(
/*
** CAPI3REF: Retrieving Statement SQL
-**
-** ^This interface can be used to retrieve a saved copy of the original
-** SQL text used to create a [prepared statement] if that statement was
-** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8
+** SQL text used to create [prepared statement] P if P was
+** created by either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
+** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8
+** string containing the SQL text of prepared statement P with
+** [bound parameters] expanded.
+**
+** ^(For example, if a prepared statement is created using the SQL
+** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345
+** and parameter :xyz is unbound, then sqlite3_sql() will return
+** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql()
+** will return "SELECT 2345,NULL".)^
+**
+** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
+** is available to hold the result, or if the result would exceed the
+** the maximum string length determined by the [SQLITE_LIMIT_LENGTH].
+**
+** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
+** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
+** option causes sqlite3_expanded_sql() to always return NULL.
+**
+** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
+** automatically freed when the prepared statement is finalized.
+** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
+** is obtained from [sqlite3_malloc()] and must be free by the application
+** by passing it to [sqlite3_free()].
*/
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
+SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
** and only if the [prepared statement] X makes no direct changes to
@@ -3478,6 +3860,27 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
+** CAPI3REF: Determine If A Prepared Statement Has Been Reset
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
+** [prepared statement] S has been stepped at least once using
+** [sqlite3_step(S)] but has neither run to completion (returned
+** [SQLITE_DONE] from [sqlite3_step(S)]) nor
+** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
+** interface returns false if S is a NULL pointer. If S is not a
+** NULL pointer and is not a pointer to a valid [prepared statement]
+** object, then the behavior is undefined and probably undesirable.
+**
+** This interface can be used in combination [sqlite3_next_stmt()]
+** to locate all prepared statements associated with a database
+** connection that are in need of being reset. This can be used,
+** for example, in diagnostic routines to search for prepared
+** statements that are holding a transaction open.
+*/
+SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
+
+/*
** CAPI3REF: Dynamically Typed Value Object
** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}
**
@@ -3490,7 +3893,9 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
** Some interfaces require a protected sqlite3_value. Other interfaces
** will accept either a protected or an unprotected sqlite3_value.
** Every interface that accepts sqlite3_value arguments specifies
-** whether or not it requires a protected sqlite3_value.
+** whether or not it requires a protected sqlite3_value. The
+** [sqlite3_value_dup()] interface can be used to construct a new
+** protected sqlite3_value from an unprotected sqlite3_value.
**
** The terms "protected" and "unprotected" refer to whether or not
** a mutex is held. An internal mutex is held for a protected
@@ -3534,6 +3939,7 @@ typedef struct sqlite3_context sqlite3_context;
** CAPI3REF: Binding Values To Prepared Statements
** KEYWORDS: {host parameter} {host parameters} {host parameter name}
** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}
+** METHOD: sqlite3_stmt
**
** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants,
** literals may be replaced by a [parameter] that matches one of following
@@ -3567,18 +3973,31 @@ typedef struct sqlite3_context sqlite3_context;
** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
**
** ^The third argument is the value to bind to the parameter.
+** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
+** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
+** is ignored and the end result is the same as sqlite3_bind_null().
**
** ^(In those routines that have a fourth argument, its value is the
** number of bytes in the parameter. To be clear: the value is the
** number of <u>bytes</u> in the value, not the number of characters.)^
-** ^If the fourth parameter is negative, the length of the string is
+** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
+** is negative, then the length of the string is
** the number of bytes up to the first zero terminator.
-**
-** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
-** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
+** If the fourth parameter to sqlite3_bind_blob() is negative, then
+** the behavior is undefined.
+** If a non-negative fourth parameter is provided to sqlite3_bind_text()
+** or sqlite3_bind_text16() or sqlite3_bind_text64() then
+** that parameter must be the byte offset
+** where the NUL terminator would occur assuming the string were NUL
+** terminated. If any NUL characters occur at byte offsets less than
+** the value of the fourth parameter then the resulting string value will
+** contain embedded NULs. The result of expressions involving strings
+** with embedded NULs is undefined.
+**
+** ^The fifth argument to the BLOB and string binding interfaces
+** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called
-** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(),
-** sqlite3_bind_text(), or sqlite3_bind_text16() fails.
+** to dispose of the BLOB or string even if the call to bind API fails.
** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed.
@@ -3586,6 +4005,14 @@ typedef struct sqlite3_context sqlite3_context;
** SQLite makes its own private copy of the data immediately, before
** the sqlite3_bind_*() routine returns.
**
+** ^The sixth argument to sqlite3_bind_text64() must be one of
+** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
+** to specify the encoding of the text in the third parameter. If
+** the sixth argument to sqlite3_bind_text64() is not one of the
+** allowed values shown above, or if the text encoding is different
+** from the encoding specified by the sixth parameter, then the behavior
+** is undefined.
+**
** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that
** is filled with zeroes. ^A zeroblob uses a fixed amount of memory
** (just an integer to hold its size) while it is being processed.
@@ -3606,6 +4033,9 @@ typedef struct sqlite3_context sqlite3_context;
**
** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an
** [error code] if anything goes wrong.
+** ^[SQLITE_TOOBIG] might be returned if the size of a string or BLOB
+** exceeds limits imposed by [sqlite3_limit]([SQLITE_LIMIT_LENGTH]) or
+** [SQLITE_MAX_LENGTH].
** ^[SQLITE_RANGE] is returned if the parameter
** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails.
**
@@ -3613,17 +4043,23 @@ typedef struct sqlite3_context sqlite3_context;
** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].
*/
SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,
+ void(*)(void*));
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);
SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);
-SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
+SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
+SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,
+ void(*)(void*), unsigned char encoding);
SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
+SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
/*
** CAPI3REF: Number Of SQL Parameters
+** METHOD: sqlite3_stmt
**
** ^This routine can be used to find the number of [SQL parameters]
** in a [prepared statement]. SQL parameters are tokens of the
@@ -3644,6 +4080,7 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*);
/*
** CAPI3REF: Name Of A Host Parameter
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_bind_parameter_name(P,N) interface returns
** the name of the N-th [SQL parameter] in the [prepared statement] P.
@@ -3671,6 +4108,7 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
/*
** CAPI3REF: Index Of A Parameter With A Given Name
+** METHOD: sqlite3_stmt
**
** ^Return the index of an SQL parameter given its name. ^The
** index value returned is suitable for use as the second
@@ -3681,12 +4119,13 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
**
** See also: [sqlite3_bind_blob|sqlite3_bind()],
** [sqlite3_bind_parameter_count()], and
-** [sqlite3_bind_parameter_index()].
+** [sqlite3_bind_parameter_name()].
*/
SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
/*
** CAPI3REF: Reset All Bindings On A Prepared Statement
+** METHOD: sqlite3_stmt
**
** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset
** the [sqlite3_bind_blob | bindings] on a [prepared statement].
@@ -3696,6 +4135,7 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);
/*
** CAPI3REF: Number Of Columns In A Result Set
+** METHOD: sqlite3_stmt
**
** ^Return the number of columns in the result set returned by the
** [prepared statement]. ^This routine returns 0 if pStmt is an SQL
@@ -3707,6 +4147,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Column Names In A Result Set
+** METHOD: sqlite3_stmt
**
** ^These routines return the name assigned to a particular column
** in the result set of a [SELECT] statement. ^The sqlite3_column_name()
@@ -3736,6 +4177,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);
/*
** CAPI3REF: Source Of Data In A Query Result
+** METHOD: sqlite3_stmt
**
** ^These routines provide a means to determine the database, table, and
** table column that is the origin of a particular result column in
@@ -3788,6 +4230,7 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
/*
** CAPI3REF: Declared Datatype Of A Query Result
+** METHOD: sqlite3_stmt
**
** ^(The first parameter is a [prepared statement].
** If this statement is a [SELECT] statement and the Nth column of the
@@ -3820,6 +4263,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
/*
** CAPI3REF: Evaluate An SQL Statement
+** METHOD: sqlite3_stmt
**
** After a [prepared statement] has been prepared using either
** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy
@@ -3876,7 +4320,8 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
-** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
+** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
@@ -3899,6 +4344,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*);
/*
** CAPI3REF: Number of columns in a result set
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_data_count(P) interface returns the number of columns in the
** current row of the result set of [prepared statement] P.
@@ -3906,6 +4352,12 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*);
** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
** interfaces) then sqlite3_data_count(P) returns 0.
** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
+** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
+** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P)
+** will return non-zero if previous call to [sqlite3_step](P) returned
+** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum]
+** where it always returns zero since each step of that multi-step
+** pragma returns 0 columns of data.
**
** See also: [sqlite3_column_count()]
*/
@@ -3946,8 +4398,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Result Values From A Query
** KEYWORDS: {column access functions}
-**
-** These routines form the "result set" interface.
+** METHOD: sqlite3_stmt
**
** ^These routines return information about a single column of the current
** result row of a query. ^In every case the first argument is a pointer
@@ -4005,16 +4456,17 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** bytes in the string, not the number of characters.
**
** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(),
-** even empty strings, are always zero terminated. ^The return
+** even empty strings, are always zero-terminated. ^The return
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
**
-** ^The object returned by [sqlite3_column_value()] is an
-** [unprotected sqlite3_value] object. An unprotected sqlite3_value object
-** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].
+** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
+** [unprotected sqlite3_value] object. In a multithreaded environment,
+** an unprotected sqlite3_value object may only be used safely with
+** [sqlite3_bind_value()] and [sqlite3_result_value()].
** If the [unprotected sqlite3_value] object returned by
** [sqlite3_column_value()] is used in any other way, including calls
** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
-** or [sqlite3_value_bytes()], then the behavior is undefined.
+** or [sqlite3_value_bytes()], the behavior is not threadsafe.
**
** These routines attempt to convert the value where appropriate. ^For
** example, if the internal representation is FLOAT and a text result
@@ -4028,29 +4480,23 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
**
** <tr><td> NULL <td> INTEGER <td> Result is 0
** <tr><td> NULL <td> FLOAT <td> Result is 0.0
-** <tr><td> NULL <td> TEXT <td> Result is NULL pointer
-** <tr><td> NULL <td> BLOB <td> Result is NULL pointer
+** <tr><td> NULL <td> TEXT <td> Result is a NULL pointer
+** <tr><td> NULL <td> BLOB <td> Result is a NULL pointer
** <tr><td> INTEGER <td> FLOAT <td> Convert from integer to float
** <tr><td> INTEGER <td> TEXT <td> ASCII rendering of the integer
** <tr><td> INTEGER <td> BLOB <td> Same as INTEGER->TEXT
-** <tr><td> FLOAT <td> INTEGER <td> Convert from float to integer
+** <tr><td> FLOAT <td> INTEGER <td> [CAST] to INTEGER
** <tr><td> FLOAT <td> TEXT <td> ASCII rendering of the float
-** <tr><td> FLOAT <td> BLOB <td> Same as FLOAT->TEXT
-** <tr><td> TEXT <td> INTEGER <td> Use atoi()
-** <tr><td> TEXT <td> FLOAT <td> Use atof()
+** <tr><td> FLOAT <td> BLOB <td> [CAST] to BLOB
+** <tr><td> TEXT <td> INTEGER <td> [CAST] to INTEGER
+** <tr><td> TEXT <td> FLOAT <td> [CAST] to REAL
** <tr><td> TEXT <td> BLOB <td> No change
-** <tr><td> BLOB <td> INTEGER <td> Convert to TEXT then use atoi()
-** <tr><td> BLOB <td> FLOAT <td> Convert to TEXT then use atof()
+** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER
+** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
** </table>
** </blockquote>)^
**
-** The table above makes reference to standard C library functions atoi()
-** and atof(). SQLite does not really use these functions. It has its
-** own equivalent internal routines. The atoi() and atof() names are
-** used in the table for brevity and because they are familiar to most
-** C programmers.
-**
** Note that when type conversions occur, pointers returned by prior
** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
** sqlite3_column_text16() may be invalidated.
@@ -4075,7 +4521,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** of conversion are done in place when it is possible, but sometimes they
** are not possible and in those cases prior pointers are invalidated.
**
-** The safest and easiest to remember policy is to invoke these routines
+** The safest policy is to invoke these routines
** in one of the following ways:
**
** <ul>
@@ -4095,8 +4541,8 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** ^The pointers returned are valid until a type conversion occurs as
** described above, or until [sqlite3_step()] or [sqlite3_reset()] or
** [sqlite3_finalize()] is called. ^The memory space used to hold strings
-** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned
-** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
+** and BLOBs is freed automatically. Do <em>not</em> pass the pointers returned
+** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
** [sqlite3_free()].
**
** ^(If a memory allocation error occurs during the evaluation of any
@@ -4118,6 +4564,7 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
/*
** CAPI3REF: Destroy A Prepared Statement Object
+** DESTRUCTOR: sqlite3_stmt
**
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
** ^If the most recent evaluation of the statement encountered no errors
@@ -4145,6 +4592,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Reset A Prepared Statement Object
+** METHOD: sqlite3_stmt
**
** The sqlite3_reset() function is called to reset a [prepared statement]
** object back to its initial state, ready to be re-executed.
@@ -4174,6 +4622,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** KEYWORDS: {function creation routines}
** KEYWORDS: {application-defined SQL function}
** KEYWORDS: {application-defined SQL functions}
+** METHOD: sqlite3
**
** ^These functions (collectively known as "function creation routines")
** are used to add SQL functions or aggregates or to redefine the behavior
@@ -4205,15 +4654,24 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
**
** ^The fourth parameter, eTextRep, specifies what
** [SQLITE_UTF8 | text encoding] this SQL function prefers for
-** its parameters. Every SQL function implementation must be able to work
-** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
-** more efficient with one encoding than another. ^An application may
-** invoke sqlite3_create_function() or sqlite3_create_function16() multiple
-** times with the same function but with different values of eTextRep.
+** its parameters. The application should set this parameter to
+** [SQLITE_UTF16LE] if the function implementation invokes
+** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the
+** implementation invokes [sqlite3_value_text16be()] on an input, or
+** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8]
+** otherwise. ^The same SQL function may be registered multiple times using
+** different preferred text encodings, with different implementations for
+** each encoding.
** ^When multiple implementations of the same function are available, SQLite
** will pick the one that involves the least amount of data conversion.
-** If there is only a single implementation which does not care what text
-** encoding is used, then the fourth argument should be [SQLITE_ANY].
+**
+** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC]
+** to signal that the function will always return the same result given
+** the same inputs within a single SQL statement. Most SQL functions are
+** deterministic. The built-in [random()] SQL function is an example of a
+** function that is not deterministic. The SQLite query planner is able to
+** perform additional optimizations on deterministic functions, so use
+** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
@@ -4295,22 +4753,32 @@ SQLITE_API int sqlite3_create_function_v2(
** These constant define integer codes that represent the various
** text encodings supported by SQLite.
*/
-#define SQLITE_UTF8 1
-#define SQLITE_UTF16LE 2
-#define SQLITE_UTF16BE 3
+#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */
+#define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */
+#define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */
#define SQLITE_UTF16 4 /* Use native byte order */
-#define SQLITE_ANY 5 /* sqlite3_create_function only */
+#define SQLITE_ANY 5 /* Deprecated */
#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
/*
+** CAPI3REF: Function Flags
+**
+** These constants may be ORed together with the
+** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
+** to [sqlite3_create_function()], [sqlite3_create_function16()], or
+** [sqlite3_create_function_v2()].
+*/
+#define SQLITE_DETERMINISTIC 0x800
+
+/*
** CAPI3REF: Deprecated Functions
** DEPRECATED
**
** These functions are [deprecated]. In order to maintain
** backwards compatibility with older code, these functions continue
** to be supported. However, new applications should avoid
-** the use of these functions. To help encourage people to avoid
-** using these functions, we are not going to tell you what they do.
+** the use of these functions. To encourage programmers to avoid
+** these functions, we will not explain what they do.
*/
#ifndef SQLITE_OMIT_DEPRECATED
SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*);
@@ -4318,15 +4786,17 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);
SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);
SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
-SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
+SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
+ void*,sqlite3_int64);
#endif
/*
-** CAPI3REF: Obtaining SQL Function Parameter Values
+** CAPI3REF: Obtaining SQL Values
+** METHOD: sqlite3_value
**
** The C-language implementation of SQL functions and aggregates uses
** this set of interface routines to access the parameter values on
-** the function or aggregate.
+** the function or aggregate.
**
** The xFunc (for scalar functions) or xStep (for aggregates) parameters
** to [sqlite3_create_function()] and [sqlite3_create_function16()]
@@ -4341,7 +4811,7 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** object results in undefined behavior.
**
** ^These routines work just like the corresponding [column access functions]
-** except that these routines take a single [protected sqlite3_value] object
+** except that these routines take a single [protected sqlite3_value] object
** pointer instead of a [sqlite3_stmt*] pointer and an integer column number.
**
** ^The sqlite3_value_text16() interface extracts a UTF-16 string
@@ -4380,7 +4850,41 @@ SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
/*
+** CAPI3REF: Finding The Subtype Of SQL Values
+** METHOD: sqlite3_value
+**
+** The sqlite3_value_subtype(V) function returns the subtype for
+** an [application-defined SQL function] argument V. The subtype
+** information can be used to pass a limited amount of context from
+** one SQL function to another. Use the [sqlite3_result_subtype()]
+** routine to set the subtype for the return value of an SQL function.
+**
+** SQLite makes no use of subtype itself. It merely passes the subtype
+** from the result of one [application-defined SQL function] into the
+** input of another.
+*/
+SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
+
+/*
+** CAPI3REF: Copy And Free SQL Values
+** METHOD: sqlite3_value
+**
+** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value]
+** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
+** is a [protected sqlite3_value] object even if the input is not.
+** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
+** memory allocation fails.
+**
+** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
+** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
+** then sqlite3_value_free(V) is a harmless no-op.
+*/
+SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value*);
+SQLITE_API void sqlite3_value_free(sqlite3_value*);
+
+/*
** CAPI3REF: Obtain Aggregate Function Context
+** METHOD: sqlite3_context
**
** Implementations of aggregate SQL functions use this
** routine to allocate memory for storing their state.
@@ -4398,14 +4902,17 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
** In those cases, sqlite3_aggregate_context() might be called for the
** first time from within xFinal().)^
**
-** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is
-** less than or equal to zero or if a memory allocate error occurs.
+** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
+** when first called if N is less than or equal to zero or if a memory
+** allocate error occurs.
**
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
** determined by the N parameter on first successful call. Changing the
** value of N in subsequent call to sqlite3_aggregate_context() within
** the same aggregate function instance will not resize the memory
-** allocation.)^
+** allocation.)^ Within the xFinal callback, it is customary to set
+** N=0 in calls to sqlite3_aggregate_context(C,N) so that no
+** pointless memory allocations occur.
**
** ^SQLite automatically frees the memory allocated by
** sqlite3_aggregate_context() when the aggregate query concludes.
@@ -4422,6 +4929,7 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
/*
** CAPI3REF: User Data For Functions
+** METHOD: sqlite3_context
**
** ^The sqlite3_user_data() interface returns a copy of
** the pointer that was the pUserData parameter (the 5th parameter)
@@ -4436,6 +4944,7 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context*);
/*
** CAPI3REF: Database Connection For Functions
+** METHOD: sqlite3_context
**
** ^The sqlite3_context_db_handle() interface returns a copy of
** the pointer to the [database connection] (the 1st parameter)
@@ -4447,42 +4956,52 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
/*
** CAPI3REF: Function Auxiliary Data
+** METHOD: sqlite3_context
**
-** The following two functions may be used by scalar SQL functions to
+** These functions may be used by (non-aggregate) SQL functions to
** associate metadata with argument values. If the same value is passed to
** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. This may
-** be used, for example, to add a regular-expression matching scalar
-** function. The compiled version of the regular expression is stored as
-** metadata associated with the SQL value passed as the regular expression
-** pattern. The compiled regular expression can be reused on multiple
-** invocations of the same function so that the original pattern string
-** does not need to be recompiled on each invocation.
+** some circumstances the associated metadata may be preserved. An example
+** of where this might be useful is in a regular-expression matching
+** function. The compiled version of the regular expression can be stored as
+** metadata associated with the pattern string.
+** Then as long as the pattern string remains the same,
+** the compiled regular expression can be reused on multiple
+** invocations of the same function.
**
** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata
** associated by the sqlite3_set_auxdata() function with the Nth argument
-** value to the application-defined function. ^If no metadata has been ever
-** been set for the Nth argument of the function, or if the corresponding
-** function parameter has changed since the meta-data was set,
-** then sqlite3_get_auxdata() returns a NULL pointer.
-**
-** ^The sqlite3_set_auxdata() interface saves the metadata
-** pointed to by its 3rd parameter as the metadata for the N-th
-** argument of the application-defined function. Subsequent
-** calls to sqlite3_get_auxdata() might return this data, if it has
-** not been destroyed.
-** ^If it is not NULL, SQLite will invoke the destructor
-** function given by the 4th parameter to sqlite3_set_auxdata() on
-** the metadata when the corresponding function parameter changes
-** or when the SQL statement completes, whichever comes first.
-**
-** SQLite is free to call the destructor and drop metadata on any
-** parameter of any function at any time. ^The only guarantee is that
-** the destructor will be called before the metadata is dropped.
+** value to the application-defined function. ^If there is no metadata
+** associated with the function argument, this sqlite3_get_auxdata() interface
+** returns a NULL pointer.
+**
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
+** argument of the application-defined function. ^Subsequent
+** calls to sqlite3_get_auxdata(C,N) return P from the most recent
+** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
+** NULL if the metadata has been discarded.
+** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
+** SQLite will invoke the destructor function X with parameter P exactly
+** once, when the metadata is discarded.
+** SQLite is free to discard the metadata at any time, including: <ul>
+** <li> ^(when the corresponding function parameter changes)^, or
+** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
+** SQL statement)^, or
+** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
+** parameter)^, or
+** <li> ^(during the original sqlite3_set_auxdata() call when a memory
+** allocation error occurs.)^ </ul>
+**
+** Note the last bullet in particular. The destructor X in
+** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
+** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
+** should be called near the end of the function implementation and the
+** function implementation should not make any use of P after
+** sqlite3_set_auxdata() has been called.
**
** ^(In practice, metadata is preserved between function calls for
-** expressions that are constant at compile time. This includes literal
-** values and [parameters].)^
+** function parameters that are compile-time constants, including literal
+** values and [parameters] and expressions composed from the same.)^
**
** These routines must be called from the same thread in which
** the SQL function is running.
@@ -4503,7 +5022,7 @@ SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(voi
** the content before returning.
**
** The typedef is necessary to work around problems in certain
-** C++ compilers. See ticket #2191.
+** C++ compilers.
*/
typedef void (*sqlite3_destructor_type)(void*);
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
@@ -4511,6 +5030,7 @@ typedef void (*sqlite3_destructor_type)(void*);
/*
** CAPI3REF: Setting The Result Of An SQL Function
+** METHOD: sqlite3_context
**
** These routines are used by the xFunc or xFinal callbacks that
** implement SQL functions and aggregates. See
@@ -4526,9 +5046,9 @@ typedef void (*sqlite3_destructor_type)(void*);
** to by the second parameter and which is N bytes long where N is the
** third parameter.
**
-** ^The sqlite3_result_zeroblob() interfaces set the result of
-** the application-defined function to be a BLOB containing all zero
-** bytes and N bytes in size, where N is the value of the 2nd parameter.
+** ^The sqlite3_result_zeroblob(C,N) and sqlite3_result_zeroblob64(C,N)
+** interfaces set the result of the application-defined function to be
+** a BLOB containing all zero bytes and N bytes in size.
**
** ^The sqlite3_result_double() interface sets the result from
** an application-defined function to be a floating point value specified
@@ -4556,11 +5076,11 @@ typedef void (*sqlite3_destructor_type)(void*);
** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error()
** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.
**
-** ^The sqlite3_result_toobig() interface causes SQLite to throw an error
-** indicating that a string or BLOB is too long to represent.
+** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an
+** error indicating that a string or BLOB is too long to represent.
**
-** ^The sqlite3_result_nomem() interface causes SQLite to throw an error
-** indicating that a memory allocation failed.
+** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an
+** error indicating that a memory allocation failed.
**
** ^The sqlite3_result_int() interface sets the return value
** of the application-defined function to be the 32-bit signed integer
@@ -4577,6 +5097,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** set the return value of the application-defined function to be
** a text string which is represented as UTF-8, UTF-16 native byte order,
** UTF-16 little endian, or UTF-16 big endian, respectively.
+** ^The sqlite3_result_text64() interface sets the return value of an
+** application-defined function to be a text string in an encoding
+** specified by the fifth (and last) parameter, which must be one
+** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
@@ -4585,7 +5109,12 @@ typedef void (*sqlite3_destructor_type)(void*);
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined
-** function result.
+** function result. If the 3rd parameter is non-negative, then it
+** must be the byte offset into the string where the NUL terminator would
+** appear if the string where NUL terminated. If any NUL characters occur
+** in the string at a byte offset that is less than the value of the 3rd
+** parameter, then the resulting string will contain embedded NULs and the
+** result of expressions operating on strings with embedded NULs is undefined.
** ^If the 4th parameter to the sqlite3_result_text* interfaces
** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that
** function as the destructor on the text or BLOB result when it has
@@ -4601,7 +5130,7 @@ typedef void (*sqlite3_destructor_type)(void*);
** from [sqlite3_malloc()] before it returns.
**
** ^The sqlite3_result_value() interface sets the result of
-** the application-defined function to be a copy the
+** the application-defined function to be a copy of the
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
** so that the [sqlite3_value] specified in the parameter may change or
@@ -4615,6 +5144,8 @@ typedef void (*sqlite3_destructor_type)(void*);
** the [sqlite3_context] pointer, the results are undefined.
*/
SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
+SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*,
+ sqlite3_uint64,void(*)(void*));
SQLITE_API void sqlite3_result_double(sqlite3_context*, double);
SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int);
SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int);
@@ -4625,14 +5156,33 @@ SQLITE_API void sqlite3_result_int(sqlite3_context*, int);
SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
SQLITE_API void sqlite3_result_null(sqlite3_context*);
SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
+SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64,
+ void(*)(void*), unsigned char encoding);
SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n);
+SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
+
+
+/*
+** CAPI3REF: Setting The Subtype Of An SQL Function
+** METHOD: sqlite3_context
+**
+** The sqlite3_result_subtype(C,T) function causes the subtype of
+** the result from the [application-defined SQL function] with
+** [sqlite3_context] C to be the value T. Only the lower 8 bits
+** of the subtype T are preserved in current versions of SQLite;
+** higher order bits are discarded.
+** The number of subtype bytes preserved by SQLite might increase
+** in future releases of SQLite.
+*/
+SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
/*
** CAPI3REF: Define New Collating Sequences
+** METHOD: sqlite3
**
** ^These functions add, remove, or modify a [collation] associated
** with the [database connection] specified as the first argument.
@@ -4735,6 +5285,7 @@ SQLITE_API int sqlite3_create_collation16(
/*
** CAPI3REF: Collation Needed Callbacks
+** METHOD: sqlite3
**
** ^To avoid having to register all collation sequences before a database
** can be used, a single callback function may be registered with the
@@ -4782,6 +5333,11 @@ SQLITE_API int sqlite3_key(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The key */
);
+SQLITE_API int sqlite3_key_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The key */
+);
/*
** Change the key on an open database. If the current database is not
@@ -4795,6 +5351,11 @@ SQLITE_API int sqlite3_rekey(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The new key */
);
+SQLITE_API int sqlite3_rekey_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The new key */
+);
/*
** Specify the activation key for a SEE database. Unless
@@ -4844,6 +5405,13 @@ SQLITE_API int sqlite3_sleep(int);
** is a NULL pointer, then SQLite performs a search for an appropriate
** temporary file directory.
**
+** Applications are strongly discouraged from using this global variable.
+** It is required to set a temporary folder on Windows Runtime (WinRT).
+** But for all other platforms, it is highly recommended that applications
+** neither read nor write this variable. This global variable is a relic
+** that exists for backwards compatibility of legacy applications and should
+** be avoided in new projects.
+**
** It is not safe to read or modify this variable in more than one
** thread at a time. It is not safe to read or modify this variable
** if a [database connection] is being used at the same time in a separate
@@ -4862,12 +5430,70 @@ SQLITE_API int sqlite3_sleep(int);
** Hence, if this variable is modified directly, either it should be
** made NULL or made to point to memory obtained from [sqlite3_malloc]
** or else the use of the [temp_store_directory pragma] should be avoided.
+** Except when requested by the [temp_store_directory pragma], SQLite
+** does not free the memory that sqlite3_temp_directory points to. If
+** the application wants that memory to be freed, it must do
+** so itself, taking care to only do so after all [database connection]
+** objects have been destroyed.
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various
+** features that require the use of temporary files may fail. Here is an
+** example of how to do this using C++ with the Windows Runtime:
+**
+** <blockquote><pre>
+** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
+** &nbsp; TemporaryFolder->Path->Data();
+** char zPathBuf&#91;MAX_PATH + 1&#93;;
+** memset(zPathBuf, 0, sizeof(zPathBuf));
+** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
+** &nbsp; NULL, NULL);
+** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
+** </pre></blockquote>
*/
SQLITE_API char *sqlite3_temp_directory;
/*
+** CAPI3REF: Name Of The Folder Holding Database Files
+**
+** ^(If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all database files
+** specified with a relative pathname and created or accessed by
+** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed
+** to be relative to that directory.)^ ^If this variable is a NULL
+** pointer, then SQLite assumes that all database files specified
+** with a relative pathname are relative to the current directory
+** for the process. Only the windows VFS makes use of this global
+** variable; it is ignored by the unix VFS.
+**
+** Changing the value of this variable while a database connection is
+** open can result in a corrupt database.
+**
+** It is not safe to read or modify this variable in more than one
+** thread at a time. It is not safe to read or modify this variable
+** if a [database connection] is being used at the same time in a separate
+** thread.
+** It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been called and that this variable remain unchanged
+** thereafter.
+**
+** ^The [data_store_directory pragma] may modify this variable and cause
+** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
+** the [data_store_directory pragma] always assumes that any string
+** that this variable points to is held in memory obtained from
+** [sqlite3_malloc] and the pragma may attempt to free that memory
+** using [sqlite3_free].
+** Hence, if this variable is modified directly, either it should be
+** made NULL or made to point to memory obtained from [sqlite3_malloc]
+** or else the use of the [data_store_directory pragma] should be avoided.
+*/
+SQLITE_API char *sqlite3_data_directory;
+
+/*
** CAPI3REF: Test For Auto-Commit Mode
** KEYWORDS: {autocommit mode}
+** METHOD: sqlite3
**
** ^The sqlite3_get_autocommit() interface returns non-zero or
** zero if the given database connection is or is not in autocommit mode,
@@ -4890,6 +5516,7 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
/*
** CAPI3REF: Find The Database Handle Of A Prepared Statement
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_db_handle interface returns the [database connection] handle
** to which a [prepared statement] belongs. ^The [database connection]
@@ -4901,7 +5528,35 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
/*
+** CAPI3REF: Return The Filename For A Database Connection
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename
+** associated with database N of connection D. ^The main database file
+** has the name "main". If there is no attached database N on the database
+** connection D, or if database N is a temporary or in-memory database, then
+** a NULL pointer is returned.
+**
+** ^The filename returned by this function is the output of the
+** xFullPathname method of the [VFS]. ^In other words, the filename
+** will be an absolute pathname, even if the filename used
+** to open the database originally was a URI or relative pathname.
+*/
+SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
+
+/*
+** CAPI3REF: Determine if a database is read-only
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N
+** of connection D is read-only, 0 if it is read/write, or -1 if N is not
+** the name of a database on connection D.
+*/
+SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
+
+/*
** CAPI3REF: Find the next prepared statement
+** METHOD: sqlite3
**
** ^This interface returns a pointer to the next [prepared statement] after
** pStmt associated with the [database connection] pDb. ^If pStmt is NULL
@@ -4917,6 +5572,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
/*
** CAPI3REF: Commit And Rollback Notification Callbacks
+** METHOD: sqlite3
**
** ^The sqlite3_commit_hook() interface registers a callback
** function to be invoked whenever a transaction is [COMMIT | committed].
@@ -4935,13 +5591,15 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
** on the same [database connection] D, or NULL for
** the first call for each function on D.
**
+** The commit and rollback hook callbacks are not reentrant.
** The callback implementation must not do anything that will modify
** the database connection that invoked the callback. Any actions
** to modify the database connection must be deferred until after the
** completion of the [sqlite3_step()] call that triggered the commit
** or rollback hook in the first place.
-** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
-** database connections for the meaning of "modify" in this paragraph.
+** Note that running any other SQL statements, including SELECT statements,
+** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify
+** the database connections for the meaning of "modify" in this paragraph.
**
** ^Registering a NULL function disables the callback.
**
@@ -4964,15 +5622,17 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
/*
** CAPI3REF: Data Change Notification Callbacks
+** METHOD: sqlite3
**
** ^The sqlite3_update_hook() interface registers a callback function
** with the [database connection] identified by the first argument
-** to be invoked whenever a row is updated, inserted or deleted.
+** to be invoked whenever a row is updated, inserted or deleted in
+** a [rowid table].
** ^Any callback set by a previous call to this function
** for the same database connection is overridden.
**
** ^The second argument is a pointer to the function to invoke when a
-** row is updated, inserted or deleted.
+** row is updated, inserted or deleted in a rowid table.
** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
@@ -4985,6 +5645,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
**
** ^(The update hook is not invoked when internal system tables are
** modified (i.e. sqlite_master and sqlite_sequence).)^
+** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
**
** ^In the current implementation, the update hook
** is not invoked when duplication rows are deleted because of an
@@ -5005,8 +5666,8 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** on the same [database connection] D, or NULL for
** the first call on D.
**
-** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()]
-** interfaces.
+** See also the [sqlite3_commit_hook()], [sqlite3_rollback_hook()],
+** and [sqlite3_preupdate_hook()] interfaces.
*/
SQLITE_API void *sqlite3_update_hook(
sqlite3*,
@@ -5016,7 +5677,6 @@ SQLITE_API void *sqlite3_update_hook(
/*
** CAPI3REF: Enable Or Disable Shared Pager Cache
-** KEYWORDS: {shared cache}
**
** ^(This routine enables or disables the sharing of the database cache
** and schema data structures between [database connection | connections]
@@ -5024,7 +5684,8 @@ SQLITE_API void *sqlite3_update_hook(
** and disabled if the argument is false.)^
**
** ^Cache sharing is enabled and disabled for an entire process.
-** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
+** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
+** In prior versions of SQLite,
** sharing was enabled or disabled for each thread separately.
**
** ^(The cache sharing mode set by this interface effects all subsequent
@@ -5039,6 +5700,14 @@ SQLITE_API void *sqlite3_update_hook(
** future releases of SQLite. Applications that care about shared
** cache setting should set it explicitly.
**
+** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0
+** and will always return SQLITE_MISUSE. On those systems,
+** shared cache mode should be enabled per-database connection via
+** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE].
+**
+** This interface is threadsafe on processors where writing a
+** 32-bit integer is atomic.
+**
** See Also: [SQLite Shared-Cache Mode]
*/
SQLITE_API int sqlite3_enable_shared_cache(int);
@@ -5054,10 +5723,26 @@ SQLITE_API int sqlite3_enable_shared_cache(int);
** which might be more or less than the amount requested.
** ^The sqlite3_release_memory() routine is a no-op returning zero
** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT].
+**
+** See also: [sqlite3_db_release_memory()]
*/
SQLITE_API int sqlite3_release_memory(int);
/*
+** CAPI3REF: Free Memory Used By A Database Connection
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap
+** memory as possible from database connection D. Unlike the
+** [sqlite3_release_memory()] interface, this interface is in effect even
+** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
+** omitted.
+**
+** See also: [sqlite3_release_memory()]
+*/
+SQLITE_API int sqlite3_db_release_memory(sqlite3*);
+
+/*
** CAPI3REF: Impose A Limit On Heap Size
**
** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the
@@ -5071,7 +5756,8 @@ SQLITE_API int sqlite3_release_memory(int);
** is advisory only.
**
** ^The return value from sqlite3_soft_heap_limit64() is the size of
-** the soft heap limit prior to the call. ^If the argument N is negative
+** the soft heap limit prior to the call, or negative in the case of an
+** error. ^If the argument N is negative
** then no change is made to the soft heap limit. Hence, the current
** size of the soft heap limit can be determined by invoking
** sqlite3_soft_heap_limit64() with a negative argument.
@@ -5087,13 +5773,14 @@ SQLITE_API int sqlite3_release_memory(int);
** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and
** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.
** <li> An alternative page cache implementation is specified using
-** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...).
+** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...).
** <li> The page cache allocates from its own memory pool supplied
** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than
** from the heap.
** </ul>)^
**
-** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
+** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
+** the soft heap limit is enforced
** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
** the soft heap limit is enforced on every memory allocation. Without
@@ -5122,21 +5809,29 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
/*
** CAPI3REF: Extract Metadata About A Column Of A Table
-**
-** ^This routine returns metadata about a specific column of a specific
-** database table accessible using the [database connection] handle
-** passed as the first function argument.
+** METHOD: sqlite3
+**
+** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns
+** information about column C of table T in database D
+** on [database connection] X.)^ ^The sqlite3_table_column_metadata()
+** interface returns SQLITE_OK and fills in the non-NULL pointers in
+** the final five arguments with appropriate values if the specified
+** column exists. ^The sqlite3_table_column_metadata() interface returns
+** SQLITE_ERROR and if the specified column does not exist.
+** ^If the column-name parameter to sqlite3_table_column_metadata() is a
+** NULL pointer, then this routine simply checks for the existence of the
+** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it
+** does not.
**
** ^The column is identified by the second, third and fourth parameters to
-** this function. ^The second parameter is either the name of the database
+** this function. ^(The second parameter is either the name of the database
** (i.e. "main", "temp", or an attached database) containing the specified
-** table or NULL. ^If it is NULL, then all attached databases are searched
+** table or NULL.)^ ^If it is NULL, then all attached databases are searched
** for the table using the same algorithm used by the database engine to
** resolve unqualified table references.
**
** ^The third and fourth parameters to this function are the table and column
-** name of the desired column, respectively. Neither of these parameters
-** may be NULL.
+** name of the desired column, respectively.
**
** ^Metadata is returned by writing to the memory locations passed as the 5th
** and subsequent parameters to this function. ^Any of these arguments may be
@@ -5155,16 +5850,17 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
** </blockquote>)^
**
** ^The memory pointed to by the character pointers returned for the
-** declaration type and collation sequence is valid only until the next
+** declaration type and collation sequence is valid until the next
** call to any SQLite API function.
**
** ^If the specified table is actually a view, an [error code] is returned.
**
-** ^If the specified column is "rowid", "oid" or "_rowid_" and an
+** ^If the specified column is "rowid", "oid" or "_rowid_" and the table
+** is not a [WITHOUT ROWID] table and an
** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output
** parameters are set for the explicitly declared column. ^(If there is no
-** explicitly declared [INTEGER PRIMARY KEY] column, then the output
-** parameters are set as follows:
+** [INTEGER PRIMARY KEY] column, then the outputs
+** for the [rowid] are set as follows:
**
** <pre>
** data type: "INTEGER"
@@ -5174,13 +5870,9 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
** auto increment: 0
** </pre>)^
**
-** ^(This function may load one or more schemas from database files. If an
-** error occurs during this process, or if the requested table or column
-** cannot be found, an [error code] is returned and an error message left
-** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^
-**
-** ^This API is only available if the library was compiled with the
-** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.
+** ^This function causes all database schemas to be read from disk and
+** parsed, if that has not already been done, and returns an error if
+** any errors are encountered while loading the schema.
*/
SQLITE_API int sqlite3_table_column_metadata(
sqlite3 *db, /* Connection handle */
@@ -5196,15 +5888,25 @@ SQLITE_API int sqlite3_table_column_metadata(
/*
** CAPI3REF: Load An Extension
+** METHOD: sqlite3
**
** ^This interface loads an SQLite extension library from the named file.
**
** ^The sqlite3_load_extension() interface attempts to load an
-** SQLite extension library contained in the file zFile.
+** [SQLite extension] library contained in the file zFile. If
+** the file cannot be loaded directly, attempts are made to load
+** with various operating-system specific extensions added.
+** So for example, if "samplelib" cannot be loaded, then names like
+** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might
+** be tried also.
**
** ^The entry point is zProc.
-** ^zProc may be 0, in which case the name of the entry point
-** defaults to "sqlite3_extension_init".
+** ^(zProc may be 0, in which case SQLite will try to come up with an
+** entry point name on its own. It first tries "sqlite3_extension_init".
+** If that does not work, it constructs a name "sqlite3_X_init" where the
+** X is consists of the lower-case equivalent of all ASCII alphabetic
+** characters in the filename from the last "/" to the first following
+** "." and omitting any initial "lib".)^
** ^The sqlite3_load_extension() interface returns
** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
** ^If an error occurs and pzErrMsg is not 0, then the
@@ -5214,9 +5916,18 @@ SQLITE_API int sqlite3_table_column_metadata(
** should free this memory by calling [sqlite3_free()].
**
** ^Extension loading must be enabled using
-** [sqlite3_enable_load_extension()] prior to calling this API,
+** [sqlite3_enable_load_extension()] or
+** [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],1,NULL)
+** prior to calling this API,
** otherwise an error will be returned.
**
+** <b>Security warning:</b> It is recommended that the
+** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this
+** interface. The use of the [sqlite3_enable_load_extension()] interface
+** should be avoided. This will keep the SQL function [load_extension()]
+** disabled and prevent SQL injections from giving attackers
+** access to extension loading capabilities.
+**
** See also the [load_extension() SQL function].
*/
SQLITE_API int sqlite3_load_extension(
@@ -5228,16 +5939,28 @@ SQLITE_API int sqlite3_load_extension(
/*
** CAPI3REF: Enable Or Disable Extension Loading
+** METHOD: sqlite3
**
** ^So as not to open security holes in older applications that are
-** unprepared to deal with extension loading, and as a means of disabling
-** extension loading while evaluating user-entered SQL, the following API
+** unprepared to deal with [extension loading], and as a means of disabling
+** [extension loading] while evaluating user-entered SQL, the following API
** is provided to turn the [sqlite3_load_extension()] mechanism on and off.
**
-** ^Extension loading is off by default. See ticket #1863.
+** ^Extension loading is off by default.
** ^Call the sqlite3_enable_load_extension() routine with onoff==1
** to turn extension loading on and call it with onoff==0 to turn
** it back off again.
+**
+** ^This interface enables or disables both the C-API
+** [sqlite3_load_extension()] and the SQL function [load_extension()].
+** ^(Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..)
+** to enable or disable only the C-API.)^
+**
+** <b>Security warning:</b> It is recommended that extension loading
+** be disabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
+** rather than this interface, so the [load_extension()] SQL function
+** remains disabled. This will prevent SQL injections from giving attackers
+** access to extension loading capabilities.
*/
SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
@@ -5246,12 +5969,12 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
**
** ^This interface causes the xEntryPoint() function to be invoked for
** each new [database connection] that is created. The idea here is that
-** xEntryPoint() is the entry point for a statically linked SQLite extension
+** xEntryPoint() is the entry point for a statically linked [SQLite extension]
** that is to be automatically loaded into all new database connections.
**
** ^(Even though the function prototype shows that xEntryPoint() takes
** no arguments and returns void, SQLite invokes xEntryPoint() with three
-** arguments and expects and integer result as if the signature of the
+** arguments and expects an integer result as if the signature of the
** entry point where as follows:
**
** <blockquote><pre>
@@ -5274,9 +5997,22 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
** on the list of automatic extensions is a harmless no-op. ^No entry point
** will be called more than once for each database connection that is opened.
**
-** See also: [sqlite3_reset_auto_extension()].
+** See also: [sqlite3_reset_auto_extension()]
+** and [sqlite3_cancel_auto_extension()]
+*/
+SQLITE_API int sqlite3_auto_extension(void(*xEntryPoint)(void));
+
+/*
+** CAPI3REF: Cancel Automatic Extension Loading
+**
+** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the
+** initialization routine X that was registered using a prior call to
+** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)]
+** routine returns 1 if initialization routine X was successfully
+** unregistered and it returns 0 if X was not on the list of initialization
+** routines.
*/
-SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void));
+SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
/*
** CAPI3REF: Reset Automatic Extension Loading
@@ -5386,6 +6122,17 @@ struct sqlite3_module {
** ^Information about the ORDER BY clause is stored in aOrderBy[].
** ^Each term of aOrderBy records a column of the ORDER BY clause.
**
+** The colUsed field indicates which columns of the virtual table may be
+** required by the current scan. Virtual table columns are numbered from
+** zero in the order in which they appear within the CREATE TABLE statement
+** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62),
+** the corresponding bit is set within the colUsed mask if the column may be
+** required by SQLite. If the table has at least 64 columns and any column
+** to the right of the first 63 is required, then bit 63 of colUsed is also
+** set. In other words, column iCol may be required if the expression
+** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
+** non-zero.
+**
** The [xBestIndex] method must fill aConstraintUsage[] with information
** about what parameters to pass to xFilter. ^If argvIndex>0 then
** the right-hand side of the corresponding aConstraint[] is evaluated
@@ -5402,16 +6149,48 @@ struct sqlite3_module {
** the correct order to satisfy the ORDER BY clause so that no separate
** sorting step is required.
**
-** ^The estimatedCost value is an estimate of the cost of doing the
-** particular lookup. A full scan of a table with N entries should have
-** a cost of N. A binary search of a table of N entries should have a
-** cost of approximately log(N).
+** ^The estimatedCost value is an estimate of the cost of a particular
+** strategy. A cost of N indicates that the cost of the strategy is similar
+** to a linear scan of an SQLite table with N rows. A cost of log(N)
+** indicates that the expense of the operation is similar to that of a
+** binary search on a unique indexed field of an SQLite table with N rows.
+**
+** ^The estimatedRows value is an estimate of the number of rows that
+** will be returned by the strategy.
+**
+** The xBestIndex method may optionally populate the idxFlags field with a
+** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
+** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
+** assumes that the strategy may visit at most one row.
+**
+** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
+** SQLite also assumes that if a call to the xUpdate() method is made as
+** part of the same statement to delete or update a virtual table row and the
+** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback
+** any database changes. In other words, if the xUpdate() returns
+** SQLITE_CONSTRAINT, the database contents must be exactly as they were
+** before xUpdate was called. By contrast, if SQLITE_INDEX_SCAN_UNIQUE is not
+** set and xUpdate returns SQLITE_CONSTRAINT, any database changes made by
+** the xUpdate method are automatically rolled back by SQLite.
+**
+** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
+** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
+** If a virtual table extension is
+** used with an SQLite version earlier than 3.8.2, the results of attempting
+** to read or write the estimatedRows field are undefined (but are likely
+** to included crashing the application). The estimatedRows field should
+** therefore only be used if [sqlite3_libversion_number()] returns a
+** value greater than or equal to 3008002. Similarly, the idxFlags field
+** was added for [version 3.9.0] ([dateof:3.9.0]).
+** It may therefore only be used if
+** sqlite3_libversion_number() returns a value greater than or equal to
+** 3009000.
*/
struct sqlite3_index_info {
/* Inputs */
int nConstraint; /* Number of entries in aConstraint */
struct sqlite3_index_constraint {
- int iColumn; /* Column on left-hand side of constraint */
+ int iColumn; /* Column constrained. -1 for ROWID */
unsigned char op; /* Constraint operator */
unsigned char usable; /* True if this constraint is usable */
int iTermOffset; /* Used internally - xBestIndex should ignore */
@@ -5430,10 +6209,21 @@ struct sqlite3_index_info {
char *idxStr; /* String, possibly obtained from sqlite3_malloc */
int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
int orderByConsumed; /* True if output is already ordered */
- double estimatedCost; /* Estimated cost of using this index */
+ double estimatedCost; /* Estimated cost of using this index */
+ /* Fields below are only available in SQLite 3.8.2 and later */
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ /* Fields below are only available in SQLite 3.9.0 and later */
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ /* Fields below are only available in SQLite 3.10.0 and later */
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
};
/*
+** CAPI3REF: Virtual Table Scan Flags
+*/
+#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
** These macros defined the allowed values for the
@@ -5441,15 +6231,19 @@ struct sqlite3_index_info {
** an operator that is part of a constraint term in the wHERE clause of
** a query that uses a [virtual table].
*/
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
-#define SQLITE_INDEX_CONSTRAINT_GT 4
-#define SQLITE_INDEX_CONSTRAINT_LE 8
-#define SQLITE_INDEX_CONSTRAINT_LT 16
-#define SQLITE_INDEX_CONSTRAINT_GE 32
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
+#define SQLITE_INDEX_CONSTRAINT_GT 4
+#define SQLITE_INDEX_CONSTRAINT_LE 8
+#define SQLITE_INDEX_CONSTRAINT_LT 16
+#define SQLITE_INDEX_CONSTRAINT_GE 32
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_LIKE 65
+#define SQLITE_INDEX_CONSTRAINT_GLOB 66
+#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
/*
** CAPI3REF: Register A Virtual Table Implementation
+** METHOD: sqlite3
**
** ^These routines are used to register a new [virtual table module] name.
** ^Module names must be registered before
@@ -5507,7 +6301,7 @@ SQLITE_API int sqlite3_create_module_v2(
*/
struct sqlite3_vtab {
const sqlite3_module *pModule; /* The module for this virtual table */
- int nRef; /* NO LONGER USED */
+ int nRef; /* Number of open cursors */
char *zErrMsg; /* Error message from sqlite3_mprintf() */
/* Virtual table implementations will typically add additional fields */
};
@@ -5546,6 +6340,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
/*
** CAPI3REF: Overload A Function For A Virtual Table
+** METHOD: sqlite3
**
** ^(Virtual tables can provide alternative implementations of functions
** using the [xFindFunction] method of the [virtual table module].
@@ -5588,6 +6383,8 @@ typedef struct sqlite3_blob sqlite3_blob;
/*
** CAPI3REF: Open A BLOB For Incremental I/O
+** METHOD: sqlite3
+** CONSTRUCTOR: sqlite3_blob
**
** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located
** in row iRow, column zColumn, table zTable in database zDb;
@@ -5597,26 +6394,42 @@ typedef struct sqlite3_blob sqlite3_blob;
** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
** </pre>)^
**
+** ^(Parameter zDb is not the filename that contains the database, but
+** rather the symbolic name of the database. For attached databases, this is
+** the name that appears after the AS keyword in the [ATTACH] statement.
+** For the main database file, the database name is "main". For TEMP
+** tables, the database name is "temp".)^
+**
** ^If the flags parameter is non-zero, then the BLOB is opened for read
-** and write access. ^If it is zero, the BLOB is opened for read access.
-** ^It is not possible to open a column that is part of an index or primary
-** key for writing. ^If [foreign key constraints] are enabled, it is
-** not possible to open a column that is part of a [child key] for writing.
-**
-** ^Note that the database name is not the filename that contains
-** the database but rather the symbolic name of the database that
-** appears after the AS keyword when the database is connected using [ATTACH].
-** ^For the main database file, the database name is "main".
-** ^For TEMP tables, the database name is "temp".
-**
-** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written
-** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set
-** to be a null pointer.)^
-** ^This function sets the [database connection] error code and message
-** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related
-** functions. ^Note that the *ppBlob variable is always initialized in a
-** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob
-** regardless of the success or failure of this routine.
+** and write access. ^If the flags parameter is zero, the BLOB is opened for
+** read-only access.
+**
+** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored
+** in *ppBlob. Otherwise an [error code] is returned and, unless the error
+** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
+** the API is not misused, it is always safe to call [sqlite3_blob_close()]
+** on *ppBlob after this function it returns.
+**
+** This function fails with SQLITE_ERROR if any of the following are true:
+** <ul>
+** <li> ^(Database zDb does not exist)^,
+** <li> ^(Table zTable does not exist within database zDb)^,
+** <li> ^(Table zTable is a WITHOUT ROWID table)^,
+** <li> ^(Column zColumn does not exist)^,
+** <li> ^(Row iRow is not present in the table)^,
+** <li> ^(The specified column of row iRow contains a value that is not
+** a TEXT or BLOB value)^,
+** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
+** constraint and the blob is being opened for read/write access)^,
+** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
+** column zColumn is part of a [child key] definition and the blob is
+** being opened for read/write access)^.
+** </ul>
+**
+** ^Unless it returns SQLITE_MISUSE, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
+**
**
** ^(If the row that a BLOB handle points to is modified by an
** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
@@ -5635,9 +6448,8 @@ typedef struct sqlite3_blob sqlite3_blob;
** blob.
**
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
-** and the built-in [zeroblob] SQL function can be used, if desired,
-** to create an empty, zero-filled blob in which to read or write using
-** this interface.
+** and the built-in [zeroblob] SQL function may be used to create a
+** zero-filled blob to read or write using the incremental-blob interface.
**
** To avoid a resource leak, every open [BLOB handle] should eventually
** be released by a call to [sqlite3_blob_close()].
@@ -5654,6 +6466,7 @@ SQLITE_API int sqlite3_blob_open(
/*
** CAPI3REF: Move a BLOB Handle to a New Row
+** METHOD: sqlite3_blob
**
** ^This function is used to move an existing blob handle so that it points
** to a different row of the same database table. ^The new row is identified
@@ -5674,34 +6487,34 @@ SQLITE_API int sqlite3_blob_open(
**
** ^This function sets the database handle error code and message.
*/
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
+SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
/*
** CAPI3REF: Close A BLOB Handle
+** DESTRUCTOR: sqlite3_blob
**
-** ^Closes an open [BLOB handle].
-**
-** ^Closing a BLOB shall cause the current transaction to commit
-** if there are no other BLOBs, no pending prepared statements, and the
-** database connection is in [autocommit mode].
-** ^If any writes were made to the BLOB, they might be held in cache
-** until the close operation if they will fit.
+** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed
+** unconditionally. Even if this routine returns an error code, the
+** handle is still closed.)^
**
-** ^(Closing the BLOB often forces the changes
-** out to disk and so if any I/O errors occur, they will likely occur
-** at the time when the BLOB is closed. Any errors that occur during
-** closing are reported as a non-zero return value.)^
+** ^If the blob handle being closed was opened for read-write access, and if
+** the database is in auto-commit mode and there are no other open read-write
+** blob handles or active write statements, the current transaction is
+** committed. ^If an error occurs while committing the transaction, an error
+** code is returned and the transaction rolled back.
**
-** ^(The BLOB is closed unconditionally. Even if this routine returns
-** an error code, the BLOB is still closed.)^
-**
-** ^Calling this routine with a null pointer (such as would be returned
-** by a failed call to [sqlite3_blob_open()]) is a harmless no-op.
+** Calling this function with an argument that is not a NULL pointer or an
+** open blob handle results in undefined behaviour. ^Calling this routine
+** with a null pointer (such as would be returned by a failed call to
+** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
+** is passed a valid open blob handle, the values returned by the
+** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning.
*/
SQLITE_API int sqlite3_blob_close(sqlite3_blob *);
/*
** CAPI3REF: Return The Size Of An Open BLOB
+** METHOD: sqlite3_blob
**
** ^Returns the size in bytes of the BLOB accessible via the
** successfully opened [BLOB handle] in its only argument. ^The
@@ -5717,6 +6530,7 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *);
/*
** CAPI3REF: Read Data From A BLOB Incrementally
+** METHOD: sqlite3_blob
**
** ^(This function is used to read data from an open [BLOB handle] into a
** caller-supplied buffer. N bytes of data are copied into buffer Z
@@ -5745,22 +6559,29 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
/*
** CAPI3REF: Write Data Into A BLOB Incrementally
+** METHOD: sqlite3_blob
+**
+** ^(This function is used to write data into an open [BLOB handle] from a
+** caller-supplied buffer. N bytes of data are copied from the buffer Z
+** into the open BLOB, starting at offset iOffset.)^
**
-** ^This function is used to write data into an open [BLOB handle] from a
-** caller-supplied buffer. ^N bytes of data are copied from the buffer Z
-** into the open BLOB, starting at offset iOffset.
+** ^(On success, sqlite3_blob_write() returns SQLITE_OK.
+** Otherwise, an [error code] or an [extended error code] is returned.)^
+** ^Unless SQLITE_MISUSE is returned, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
**
** ^If the [BLOB handle] passed as the first argument was not opened for
** writing (the flags parameter to [sqlite3_blob_open()] was zero),
** this function returns [SQLITE_READONLY].
**
-** ^This function may only modify the contents of the BLOB; it is
+** This function may only modify the contents of the BLOB; it is
** not possible to increase the size of a BLOB using this API.
** ^If offset iOffset is less than N bytes from the end of the BLOB,
-** [SQLITE_ERROR] is returned and no data is written. ^If N is
-** less than zero [SQLITE_ERROR] is returned and no data is written.
-** The size of the BLOB (and hence the maximum value of N+iOffset)
-** can be determined using the [sqlite3_blob_bytes()] interface.
+** [SQLITE_ERROR] is returned and no data is written. The size of the
+** BLOB (and hence the maximum value of N+iOffset) can be determined
+** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less
+** than zero [SQLITE_ERROR] is returned and no data is written.
**
** ^An attempt to write to an expired [BLOB handle] fails with an
** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred
@@ -5769,9 +6590,6 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
** have been overwritten by the statement that expired the BLOB handle
** or by other independent statements.
**
-** ^(On success, sqlite3_blob_write() returns SQLITE_OK.
-** Otherwise, an [error code] or an [extended error code] is returned.)^
-**
** This routine only works on a [BLOB handle] which has been created
** by a prior successful call to [sqlite3_blob_open()] and which has not
** been closed by [sqlite3_blob_close()]. Passing any other pointer in
@@ -5824,46 +6642,51 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** The SQLite source code contains multiple implementations
** of these mutex routines. An appropriate implementation
-** is selected automatically at compile-time. ^(The following
+** is selected automatically at compile-time. The following
** implementations are available in the SQLite core:
**
** <ul>
-** <li> SQLITE_MUTEX_OS2
-** <li> SQLITE_MUTEX_PTHREAD
+** <li> SQLITE_MUTEX_PTHREADS
** <li> SQLITE_MUTEX_W32
** <li> SQLITE_MUTEX_NOOP
-** </ul>)^
+** </ul>
**
-** ^The SQLITE_MUTEX_NOOP implementation is a set of routines
+** The SQLITE_MUTEX_NOOP implementation is a set of routines
** that does no real locking and is appropriate for use in
-** a single-threaded application. ^The SQLITE_MUTEX_OS2,
-** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations
-** are appropriate for use on OS/2, Unix, and Windows.
+** a single-threaded application. The SQLITE_MUTEX_PTHREADS and
+** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix
+** and Windows.
**
-** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
+** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
** implementation is included with the library. In this case the
** application must supply a custom mutex implementation using the
** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function
** before calling sqlite3_initialize() or any other public sqlite3_
-** function that calls sqlite3_initialize().)^
+** function that calls sqlite3_initialize().
**
** ^The sqlite3_mutex_alloc() routine allocates a new
-** mutex and returns a pointer to it. ^If it returns NULL
-** that means that a mutex could not be allocated. ^SQLite
-** will unwind its stack and return an error. ^(The argument
-** to sqlite3_mutex_alloc() is one of these integer constants:
+** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc()
+** routine returns NULL if it is unable to allocate the requested
+** mutex. The argument to sqlite3_mutex_alloc() must one of these
+** integer constants:
**
** <ul>
** <li> SQLITE_MUTEX_FAST
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
-** </ul>)^
+** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
+** </ul>
**
** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
** cause sqlite3_mutex_alloc() to create
@@ -5871,14 +6694,14 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
** The mutex implementation does not need to make a distinction
** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
-** not want to. ^SQLite will only request a recursive mutex in
-** cases where it really needs one. ^If a faster non-recursive mutex
+** not want to. SQLite will only request a recursive mutex in
+** cases where it really needs one. If a faster non-recursive mutex
** implementation is available on the host platform, the mutex subsystem
** might return such a mutex in response to SQLITE_MUTEX_FAST.
**
** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other
** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return
-** a pointer to a static preexisting mutex. ^Six static mutexes are
+** a pointer to a static preexisting mutex. ^Nine static mutexes are
** used by the current version of SQLite. Future versions of SQLite
** may add additional static mutexes. Static mutexes are for internal
** use by SQLite only. Applications that use SQLite mutexes should
@@ -5887,16 +6710,13 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. ^But for the static
+** returns a different mutex on every call. ^For the static
** mutex types, the same mutex is returned on every call that has
** the same type number.
**
** ^The sqlite3_mutex_free() routine deallocates a previously
-** allocated dynamic mutex. ^SQLite is careful to deallocate every
-** dynamic mutex that it allocates. The dynamic mutexes must not be in
-** use when they are deallocated. Attempting to deallocate a static
-** mutex results in undefined behavior. ^SQLite never deallocates
-** a static mutex.
+** allocated dynamic mutex. Attempting to deallocate a static
+** mutex results in undefined behavior.
**
** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
** to enter a mutex. ^If another thread is already within the mutex,
@@ -5904,23 +6724,21 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK]
** upon successful entry. ^(Mutexes created using
** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread.
-** In such cases the,
+** In such cases, the
** mutex must be exited an equal number of times before another thread
-** can enter.)^ ^(If the same thread tries to enter any other
-** kind of mutex more than once, the behavior is undefined.
-** SQLite will never exhibit
-** such behavior in its own use of mutexes.)^
+** can enter.)^ If the same thread tries to enter any mutex other
+** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined.
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^
+** will always return SQLITE_BUSY. The SQLite core only ever uses
+** sqlite3_mutex_try() as an optimization so this is acceptable
+** behavior.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
-** previously entered by the same thread. ^(The behavior
+** previously entered by the same thread. The behavior
** is undefined if the mutex is not currently entered by the
-** calling thread or is not currently allocated. SQLite will
-** never do either.)^
+** calling thread or is not currently allocated.
**
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
** sqlite3_mutex_leave() is a NULL pointer, then all three routines
@@ -5941,9 +6759,9 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*);
** used to allocate and use mutexes.
**
** Usually, the default mutex implementations provided by SQLite are
-** sufficient, however the user has the option of substituting a custom
+** sufficient, however the application has the option of substituting a custom
** implementation for specialized deployments or systems for which SQLite
-** does not provide a suitable implementation. In this case, the user
+** does not provide a suitable implementation. In this case, the application
** creates and populates an instance of this structure to pass
** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option.
** Additionally, an instance of this structure can be used as an
@@ -5984,13 +6802,13 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*);
** (i.e. it is acceptable to provide an implementation that segfaults if
** it is passed a NULL pointer).
**
-** The xMutexInit() method must be threadsafe. ^It must be harmless to
+** The xMutexInit() method must be threadsafe. It must be harmless to
** invoke xMutexInit() multiple times within the same process and without
** intervening calls to xMutexEnd(). Second and subsequent calls to
** xMutexInit() must be no-ops.
**
-** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
-** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
+** xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
+** and its associates). Similarly, xMutexAlloc() must not use SQLite memory
** allocation for a static mutex. ^However xMutexAlloc() may use SQLite
** memory allocation for a fast or recursive mutex.
**
@@ -6016,29 +6834,29 @@ struct sqlite3_mutex_methods {
** CAPI3REF: Mutex Verification Routines
**
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines
-** are intended for use inside assert() statements. ^The SQLite core
+** are intended for use inside assert() statements. The SQLite core
** never uses these routines except inside an assert() and applications
-** are advised to follow the lead of the core. ^The SQLite core only
+** are advised to follow the lead of the core. The SQLite core only
** provides implementations for these routines when it is compiled
-** with the SQLITE_DEBUG flag. ^External mutex implementations
+** with the SQLITE_DEBUG flag. External mutex implementations
** are only required to provide these routines if SQLITE_DEBUG is
** defined and if NDEBUG is not defined.
**
-** ^These routines should return true if the mutex in their argument
+** These routines should return true if the mutex in their argument
** is held or not held, respectively, by the calling thread.
**
-** ^The implementation is not required to provided versions of these
+** The implementation is not required to provide versions of these
** routines that actually work. If the implementation does not provide working
** versions of these routines, it should at least provide stubs that always
** return true so that one does not get spurious assertion failures.
**
-** ^If the argument to sqlite3_mutex_held() is a NULL pointer then
+** If the argument to sqlite3_mutex_held() is a NULL pointer then
** the routine should return 1. This seems counter-intuitive since
** clearly the mutex cannot be held if it does not exist. But
** the reason the mutex does not exist is because the build is not
** using mutexes. And we do not want the assert() containing the
** call to sqlite3_mutex_held() to fail, so a non-zero return is
-** the appropriate thing to do. ^The sqlite3_mutex_notheld()
+** the appropriate thing to do. The sqlite3_mutex_notheld()
** interface should also return 1 when given a NULL pointer.
*/
#ifndef NDEBUG
@@ -6062,13 +6880,20 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
-#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
+#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
+#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */
+#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */
+#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */
+#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */
+#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */
+#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */
/*
** CAPI3REF: Retrieve the mutex for a database connection
+** METHOD: sqlite3
**
** ^This interface returns a pointer the [sqlite3_mutex] object that
** serializes access to the [database connection] given in the argument
@@ -6080,6 +6905,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
/*
** CAPI3REF: Low-Level Control Of Database Files
+** METHOD: sqlite3
**
** ^The [sqlite3_file_control()] interface makes a direct call to the
** xFileControl method for the [sqlite3_io_methods] object associated
@@ -6155,15 +6981,22 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_RESERVE 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16
-#define SQLITE_TESTCTRL_PGHDRSZ 17
-#define SQLITE_TESTCTRL_SCRATCHMALLOC 18
-#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19
-#define SQLITE_TESTCTRL_LAST 19
+#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
+#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
+#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
+#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
+#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
+#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
+#define SQLITE_TESTCTRL_BYTEORDER 22
+#define SQLITE_TESTCTRL_ISINIT 23
+#define SQLITE_TESTCTRL_SORTER_MMAP 24
+#define SQLITE_TESTCTRL_IMPOSTER 25
+#define SQLITE_TESTCTRL_LAST 25
/*
** CAPI3REF: SQLite Runtime Status
**
-** ^This interface is used to retrieve runtime status information
+** ^These interfaces are used to retrieve runtime status information
** about the performance of SQLite, and optionally to reset various
** highwater marks. ^The first argument is an integer code for
** the specific parameter to measure. ^(Recognized integer codes
@@ -6177,19 +7010,22 @@ SQLITE_API int sqlite3_test_control(int op, ...);
** ^(Other parameters record only the highwater mark and not the current
** value. For these latter parameters nothing is written into *pCurrent.)^
**
-** ^The sqlite3_status() routine returns SQLITE_OK on success and a
-** non-zero [error code] on failure.
+** ^The sqlite3_status() and sqlite3_status64() routines return
+** SQLITE_OK on success and a non-zero [error code] on failure.
**
-** This routine is threadsafe but is not atomic. This routine can be
-** called while other threads are running the same or different SQLite
-** interfaces. However the values returned in *pCurrent and
-** *pHighwater reflect the status of SQLite at different points in time
-** and it is possible that another thread might change the parameter
-** in between the times when *pCurrent and *pHighwater are written.
+** If either the current value or the highwater mark is too large to
+** be represented by a 32-bit integer, then the values returned by
+** sqlite3_status() are undefined.
**
** See also: [sqlite3_db_status()]
*/
SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+SQLITE_API int sqlite3_status64(
+ int op,
+ sqlite3_int64 *pCurrent,
+ sqlite3_int64 *pHighwater,
+ int resetFlag
+);
/*
@@ -6268,7 +7104,8 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
-** <dd>This parameter records the deepest parser stack. It is only
+** <dd>The *pHighwater parameter records the deepest parser stack.
+** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
**
@@ -6287,6 +7124,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF
/*
** CAPI3REF: Database Connection Status
+** METHOD: sqlite3
**
** ^This interface is used to retrieve runtime status information
** about a single [database connection]. ^The first argument is the
@@ -6349,12 +7187,24 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** the current value is always zero.)^
**
** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
-** <dd>This parameter returns the approximate number of of bytes of heap
+** <dd>This parameter returns the approximate number of bytes of heap
** memory used by all pager caches associated with the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
**
+** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
+** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt>
+** <dd>This parameter is similar to DBSTATUS_CACHE_USED, except that if a
+** pager cache is shared between two or more connections the bytes of heap
+** memory used by that pager cache is divided evenly between the attached
+** connections.)^ In other words, if none of the pager caches associated
+** with the database connection are shared, this request returns the same
+** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are
+** shared, the value returned by this call will be smaller than that returned
+** by DBSTATUS_CACHE_USED. ^The highwater mark associated with
+** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0.
+**
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
-** <dd>This parameter returns the approximate number of of bytes of heap
+** <dd>This parameter returns the approximate number of bytes of heap
** memory used to store the schema for all databases associated
** with the connection - main, temp, and any [ATTACH]-ed databases.)^
** ^The full amount of memory used by the schemas is reported, even if the
@@ -6363,11 +7213,40 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
**
** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
-** <dd>This parameter returns the approximate number of of bytes of heap
+** <dd>This parameter returns the approximate number of bytes of heap
** and lookaside memory used by all prepared statements associated with
** the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.
** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
+** <dd>This parameter returns the number of pager cache hits that have
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
+** is always 0.
+** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
+** <dd>This parameter returns the number of pager cache misses that have
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
+** is always 0.
+** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(<dt>SQLITE_DBSTATUS_CACHE_WRITE</dt>
+** <dd>This parameter returns the number of dirty cache entries that have
+** been written to disk. Specifically, the number of pages written to the
+** wal file in wal mode databases, or the number of pages written to the
+** database file in rollback mode databases. Any pages written as part of
+** transaction rollback or database recovery operations are not included.
+** If an IO or other error occurs while writing a page to disk, the effect
+** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
+** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
+** </dd>
+**
+** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
+** <dd>This parameter returns zero for the current value if and only if
+** all foreign key constraints (deferred or immediate) have been
+** resolved.)^ ^The highwater mark is always 0.
+** </dd>
** </dl>
*/
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
@@ -6377,11 +7256,17 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6
-#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */
+#define SQLITE_DBSTATUS_CACHE_HIT 7
+#define SQLITE_DBSTATUS_CACHE_MISS 8
+#define SQLITE_DBSTATUS_CACHE_WRITE 9
+#define SQLITE_DBSTATUS_DEFERRED_FKS 10
+#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
+#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */
/*
** CAPI3REF: Prepared Statement Status
+** METHOD: sqlite3_stmt
**
** ^(Each prepared statement maintains various
** [SQLITE_STMTSTATUS counters] that measure the number
@@ -6432,11 +7317,20 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** improvement performance by adding permanent indices that do not
** need to be reinitialized each time the statement is run.</dd>
**
+** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt>
+** <dd>^This is the number of virtual machine operations executed
+** by the prepared statement if that number is less than or equal
+** to 2147483647. The number of virtual machine operations can be
+** used as a proxy for the total work done by the prepared statement.
+** If the number of virtual machine operations exceeds 2147483647
+** then the value returned by this statement status code is undefined.
+** </dd>
** </dl>
*/
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
#define SQLITE_STMTSTATUS_SORT 2
#define SQLITE_STMTSTATUS_AUTOINDEX 3
+#define SQLITE_STMTSTATUS_VM_STEP 4
/*
** CAPI3REF: Custom Page Cache Object
@@ -6447,17 +7341,33 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** sqlite3_pcache object except by holding and passing pointers
** to the object.
**
-** See [sqlite3_pcache_methods] for additional information.
+** See [sqlite3_pcache_methods2] for additional information.
*/
typedef struct sqlite3_pcache sqlite3_pcache;
/*
+** CAPI3REF: Custom Page Cache Object
+**
+** The sqlite3_pcache_page object represents a single page in the
+** page cache. The page cache will allocate instances of this
+** object. Various methods of the page cache use pointers to instances
+** of this object as parameters or as their return value.
+**
+** See [sqlite3_pcache_methods2] for additional information.
+*/
+typedef struct sqlite3_pcache_page sqlite3_pcache_page;
+struct sqlite3_pcache_page {
+ void *pBuf; /* The content of the page */
+ void *pExtra; /* Extra information associated with the page */
+};
+
+/*
** CAPI3REF: Application Defined Page Cache.
** KEYWORDS: {page cache}
**
-** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can
+** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can
** register an alternative page cache implementation by passing in an
-** instance of the sqlite3_pcache_methods structure.)^
+** instance of the sqlite3_pcache_methods2 structure.)^
** In many applications, most of the heap memory allocated by
** SQLite is used for the page cache.
** By implementing a
@@ -6471,7 +7381,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** extreme measure that is only needed by the most demanding applications.
** The built-in page cache is recommended for most uses.
**
-** ^(The contents of the sqlite3_pcache_methods structure are copied to an
+** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an
** internal buffer by SQLite within the call to [sqlite3_config]. Hence
** the application may discard the parameter after the call to
** [sqlite3_config()] returns.)^
@@ -6480,7 +7390,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** ^(The xInit() method is called once for each effective
** call to [sqlite3_initialize()])^
** (usually only once during the lifetime of the process). ^(The xInit()
-** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^
+** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^
** The intent of the xInit() method is to set up global data structures
** required by the custom page cache implementation.
** ^(If the xInit() method is NULL, then the
@@ -6507,17 +7417,15 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** SQLite will typically create one cache instance for each open database file,
** though this is not guaranteed. ^The
** first parameter, szPage, is the size in bytes of the pages that must
-** be allocated by the cache. ^szPage will not be a power of two. ^szPage
-** will the page size of the database file that is to be cached plus an
-** increment (here called "R") of less than 250. SQLite will use the
-** extra R bytes on each page to store metadata about the underlying
-** database page on disk. The value of R depends
+** be allocated by the cache. ^szPage will always a power of two. ^The
+** second parameter szExtra is a number of bytes of extra storage
+** associated with each page cache entry. ^The szExtra parameter will
+** a number less than 250. SQLite will use the
+** extra szExtra bytes on each page to store metadata about the underlying
+** database page on disk. The value passed into szExtra depends
** on the SQLite version, the target platform, and how SQLite was compiled.
-** ^(R is constant for a particular build of SQLite. Except, there are two
-** distinct values of R when SQLite is compiled with the proprietary
-** ZIPVFS extension.)^ ^The second argument to
-** xCreate(), bPurgeable, is true if the cache being created will
-** be used to cache database pages of a file stored on disk, or
+** ^The third argument to xCreate(), bPurgeable, is true if the cache being
+** created will be used to cache database pages of a file stored on disk, or
** false if it is used for an in-memory database. The cache implementation
** does not have to do anything special based with the value of bPurgeable;
** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will
@@ -6541,11 +7449,16 @@ typedef struct sqlite3_pcache sqlite3_pcache;
**
** [[the xFetch() page cache methods]]
** The xFetch() method locates a page in the cache and returns a pointer to
-** the page, or a NULL pointer.
-** A "page", in this context, means a buffer of szPage bytes aligned at an
-** 8-byte boundary. The page to be fetched is determined by the key. ^The
-** minimum key value is 1. After it has been retrieved using xFetch, the page
-** is considered to be "pinned".
+** an sqlite3_pcache_page object associated with that page, or a NULL pointer.
+** The pBuf element of the returned sqlite3_pcache_page object will be a
+** pointer to a buffer of szPage bytes used to store the content of a
+** single database page. The pExtra element of sqlite3_pcache_page will be
+** a pointer to the szExtra bytes of extra storage that SQLite has requested
+** for each entry in the page cache.
+**
+** The page to be fetched is determined by the key. ^The minimum key value
+** is 1. After it has been retrieved using xFetch, the page is considered
+** to be "pinned".
**
** If the requested page is already in the page cache, then the page cache
** implementation must return a pointer to the page buffer with its content
@@ -6554,7 +7467,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** parameter to help it determined what action to take:
**
** <table border=1 width=85% align=center>
-** <tr><th> createFlag <th> Behaviour when page is not already in cache
+** <tr><th> createFlag <th> Behavior when page is not already in cache
** <tr><td> 0 <td> Do not allocate a new page. Return NULL.
** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so.
** Otherwise return NULL.
@@ -6598,8 +7511,37 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** ^The xDestroy() method is used to delete a cache allocated by xCreate().
** All resources associated with the specified cache should be freed. ^After
** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*]
-** handle invalid, and will not use it with any other sqlite3_pcache_methods
+** handle invalid, and will not use it with any other sqlite3_pcache_methods2
** functions.
+**
+** [[the xShrink() page cache method]]
+** ^SQLite invokes the xShrink() method when it wants the page cache to
+** free up as much of heap memory as possible. The page cache implementation
+** is not obligated to free any memory, but well-behaved implementations should
+** do their best.
+*/
+typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;
+struct sqlite3_pcache_methods2 {
+ int iVersion;
+ void *pArg;
+ int (*xInit)(void*);
+ void (*xShutdown)(void*);
+ sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable);
+ void (*xCachesize)(sqlite3_pcache*, int nCachesize);
+ int (*xPagecount)(sqlite3_pcache*);
+ sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
+ void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
+ void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
+ unsigned oldKey, unsigned newKey);
+ void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
+ void (*xDestroy)(sqlite3_pcache*);
+ void (*xShrink)(sqlite3_pcache*);
+};
+
+/*
+** This is the obsolete pcache_methods object that has now been replaced
+** by sqlite3_pcache_methods2. This object is not used by SQLite. It is
+** retained in the header file for backwards compatibility only.
*/
typedef struct sqlite3_pcache_methods sqlite3_pcache_methods;
struct sqlite3_pcache_methods {
@@ -6616,6 +7558,7 @@ struct sqlite3_pcache_methods {
void (*xDestroy)(sqlite3_pcache*);
};
+
/*
** CAPI3REF: Online Backup Object
**
@@ -6672,6 +7615,10 @@ typedef struct sqlite3_backup sqlite3_backup;
** must be different or else sqlite3_backup_init(D,N,S,M) will fail with
** an error.
**
+** ^A call to sqlite3_backup_init() will fail, returning NULL, if
+** there is already a read or read-write transaction open on the
+** destination database.
+**
** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is
** returned and an error code and error message are stored in the
** destination [database connection] D.
@@ -6764,20 +7711,20 @@ typedef struct sqlite3_backup sqlite3_backup;
** is not a permanent error and does not affect the return value of
** sqlite3_backup_finish().
**
-** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]]
+** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]]
** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b>
**
-** ^Each call to sqlite3_backup_step() sets two values inside
-** the [sqlite3_backup] object: the number of pages still to be backed
-** up and the total number of pages in the source database file.
-** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
-** retrieve these two values, respectively.
-**
-** ^The values returned by these functions are only updated by
-** sqlite3_backup_step(). ^If the source database is modified during a backup
-** operation, then the values are not updated to account for any extra
-** pages that need to be updated or the size of the source database file
-** changing.
+** ^The sqlite3_backup_remaining() routine returns the number of pages still
+** to be backed up at the conclusion of the most recent sqlite3_backup_step().
+** ^The sqlite3_backup_pagecount() routine returns the total number of pages
+** in the source database at the conclusion of the most recent
+** sqlite3_backup_step().
+** ^(The values returned by these functions are only updated by
+** sqlite3_backup_step(). If the source database is modified in a way that
+** changes the size of the source database or the number of pages remaining,
+** those changes are not reflected in the output of sqlite3_backup_pagecount()
+** and sqlite3_backup_remaining() until after the next
+** sqlite3_backup_step().)^
**
** <b>Concurrent Usage of Database Handles</b>
**
@@ -6823,6 +7770,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
/*
** CAPI3REF: Unlock Notification
+** METHOD: sqlite3
**
** ^When running in shared-cache mode, a database operation may fail with
** an [SQLITE_LOCKED] error if the required locks on the shared-cache or
@@ -6945,17 +7893,58 @@ SQLITE_API int sqlite3_unlock_notify(
/*
** CAPI3REF: String Comparison
**
-** ^The [sqlite3_strnicmp()] API allows applications and extensions to
-** compare the contents of two buffers containing UTF-8 strings in a
-** case-independent fashion, using the same definition of case independence
-** that SQLite uses internally when comparing identifiers.
+** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications
+** and extensions to compare the contents of two buffers containing UTF-8
+** strings in a case-independent fashion, using the same definition of "case
+** independence" that SQLite uses internally when comparing identifiers.
*/
+SQLITE_API int sqlite3_stricmp(const char *, const char *);
SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
/*
+** CAPI3REF: String Globbing
+*
+** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if
+** string X matches the [GLOB] pattern P.
+** ^The definition of [GLOB] pattern matching used in
+** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the
+** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function
+** is case sensitive.
+**
+** Note that this routine returns zero on a match and non-zero if the strings
+** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
+**
+** See also: [sqlite3_strlike()].
+*/
+SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr);
+
+/*
+** CAPI3REF: String LIKE Matching
+*
+** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if
+** string X matches the [LIKE] pattern P with escape character E.
+** ^The definition of [LIKE] pattern matching used in
+** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E"
+** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without
+** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0.
+** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case
+** insensitive - equivalent upper and lower case ASCII characters match
+** one another.
+**
+** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though
+** only ASCII characters are case folded.
+**
+** Note that this routine returns zero on a match and non-zero if the strings
+** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
+**
+** See also: [sqlite3_strglob()].
+*/
+SQLITE_API int sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc);
+
+/*
** CAPI3REF: Error Logging Interface
**
-** ^The [sqlite3_log()] interface writes a message into the error log
+** ^The [sqlite3_log()] interface writes a message into the [error log]
** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].
** ^If logging is enabled, the zFormat string and subsequent arguments are
** used with [sqlite3_snprintf()] to generate the final output string.
@@ -6977,14 +7966,13 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
/*
** CAPI3REF: Write-Ahead Log Commit Hook
+** METHOD: sqlite3
**
** ^The [sqlite3_wal_hook()] function is used to register a callback that
-** will be invoked each time a database connection commits data to a
-** [write-ahead log] (i.e. whenever a transaction is committed in
-** [journal_mode | journal_mode=WAL mode]).
+** is invoked each time data is committed to a database in wal mode.
**
-** ^The callback is invoked by SQLite after the commit has taken place and
-** the associated write-lock on the database released, so the implementation
+** ^(The callback is invoked by SQLite after the commit has taken place and
+** the associated write-lock on the database released)^, so the implementation
** may read, write or [checkpoint] the database as required.
**
** ^The first parameter passed to the callback function when it is invoked
@@ -7008,7 +7996,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
** previously registered write-ahead log callback. ^Note that the
** [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
-** those overwrite any prior [sqlite3_wal_hook()] settings.
+** overwrite any prior [sqlite3_wal_hook()] settings.
*/
SQLITE_API void *sqlite3_wal_hook(
sqlite3*,
@@ -7018,6 +8006,7 @@ SQLITE_API void *sqlite3_wal_hook(
/*
** CAPI3REF: Configure an auto-checkpoint
+** METHOD: sqlite3
**
** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
** [sqlite3_wal_hook()] that causes any database on [database connection] D
@@ -7035,6 +8024,9 @@ SQLITE_API void *sqlite3_wal_hook(
** ^The [wal_autocheckpoint pragma] can be used to invoke this interface
** from SQL.
**
+** ^Checkpoints initiated by this mechanism are
+** [sqlite3_wal_checkpoint_v2|PASSIVE].
+**
** ^Every new [database connection] defaults to having the auto-checkpoint
** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
** pages. The use of this interface
@@ -7045,91 +8037,117 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
/*
** CAPI3REF: Checkpoint a database
+** METHOD: sqlite3
**
-** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X
-** on [database connection] D to be [checkpointed]. ^If X is NULL or an
-** empty string, then a checkpoint is run on all databases of
-** connection D. ^If the database connection D is not in
-** [WAL | write-ahead log mode] then this interface is a harmless no-op.
+** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to
+** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^
**
-** ^The [wal_checkpoint pragma] can be used to invoke this interface
-** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the
-** [wal_autocheckpoint pragma] can be used to cause this interface to be
-** run whenever the WAL reaches a certain size threshold.
+** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the
+** [write-ahead log] for database X on [database connection] D to be
+** transferred into the database file and for the write-ahead log to
+** be reset. See the [checkpointing] documentation for addition
+** information.
**
-** See also: [sqlite3_wal_checkpoint_v2()]
+** This interface used to be the only way to cause a checkpoint to
+** occur. But then the newer and more powerful [sqlite3_wal_checkpoint_v2()]
+** interface was added. This interface is retained for backwards
+** compatibility and as a convenience for applications that need to manually
+** start a callback but which do not need the full power (and corresponding
+** complication) of [sqlite3_wal_checkpoint_v2()].
*/
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
/*
** CAPI3REF: Checkpoint a database
+** METHOD: sqlite3
**
-** Run a checkpoint operation on WAL database zDb attached to database
-** handle db. The specific operation is determined by the value of the
-** eMode parameter:
+** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint
+** operation on database X of [database connection] D in mode M. Status
+** information is written back into integers pointed to by L and C.)^
+** ^(The M parameter must be a valid [checkpoint mode]:)^
**
** <dl>
** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>
-** Checkpoint as many frames as possible without waiting for any database
-** readers or writers to finish. Sync the db file if all frames in the log
-** are checkpointed. This mode is the same as calling
-** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked.
+** ^Checkpoint as many frames as possible without waiting for any database
+** readers or writers to finish, then sync the database file if all frames
+** in the log were checkpointed. ^The [busy-handler callback]
+** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode.
+** ^On the other hand, passive mode might leave the checkpoint unfinished
+** if there are concurrent readers or writers.
**
** <dt>SQLITE_CHECKPOINT_FULL<dd>
-** This mode blocks (calls the busy-handler callback) until there is no
+** ^This mode blocks (it invokes the
+** [sqlite3_busy_handler|busy-handler callback]) until there is no
** database writer and all readers are reading from the most recent database
-** snapshot. It then checkpoints all frames in the log file and syncs the
-** database file. This call blocks database writers while it is running,
-** but not database readers.
+** snapshot. ^It then checkpoints all frames in the log file and syncs the
+** database file. ^This mode blocks new database writers while it is pending,
+** but new database readers are allowed to continue unimpeded.
**
** <dt>SQLITE_CHECKPOINT_RESTART<dd>
-** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after
-** checkpointing the log file it blocks (calls the busy-handler callback)
-** until all readers are reading from the database file only. This ensures
-** that the next client to write to the database file restarts the log file
-** from the beginning. This call blocks database writers while it is running,
-** but not database readers.
+** ^This mode works the same way as SQLITE_CHECKPOINT_FULL with the addition
+** that after checkpointing the log file it blocks (calls the
+** [busy-handler callback])
+** until all readers are reading from the database file only. ^This ensures
+** that the next writer will restart the log file from the beginning.
+** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new
+** database writer attempts while it is pending, but does not impede readers.
+**
+** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
+** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
+** addition that it also truncates the log file to zero bytes just prior
+** to a successful return.
** </dl>
**
-** If pnLog is not NULL, then *pnLog is set to the total number of frames in
-** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to
-** the total number of checkpointed frames (including any that were already
-** checkpointed when this function is called). *pnLog and *pnCkpt may be
-** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK.
-** If no values are available because of an error, they are both set to -1
-** before returning to communicate this to the caller.
-**
-** All calls obtain an exclusive "checkpoint" lock on the database file. If
+** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
+** the log file or to -1 if the checkpoint could not run because
+** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
+** NULL,then *pnCkpt is set to the total number of checkpointed frames in the
+** log file (including any that were already checkpointed before the function
+** was called) or to -1 if the checkpoint could not run due to an error or
+** because the database is not in WAL mode. ^Note that upon successful
+** completion of an SQLITE_CHECKPOINT_TRUNCATE, the log file will have been
+** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero.
+**
+** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If
** any other process is running a checkpoint operation at the same time, the
-** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a
+** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a
** busy-handler configured, it will not be invoked in this case.
**
-** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive
-** "writer" lock on the database file. If the writer lock cannot be obtained
-** immediately, and a busy-handler is configured, it is invoked and the writer
-** lock retried until either the busy-handler returns 0 or the lock is
-** successfully obtained. The busy-handler is also invoked while waiting for
-** database readers as described above. If the busy-handler returns 0 before
+** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the
+** exclusive "writer" lock on the database file. ^If the writer lock cannot be
+** obtained immediately, and a busy-handler is configured, it is invoked and
+** the writer lock retried until either the busy-handler returns 0 or the lock
+** is successfully obtained. ^The busy-handler is also invoked while waiting for
+** database readers as described above. ^If the busy-handler returns 0 before
** the writer lock is obtained or while waiting for database readers, the
** checkpoint operation proceeds from that point in the same way as
** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
-** without blocking any further. SQLITE_BUSY is returned in this case.
+** without blocking any further. ^SQLITE_BUSY is returned in this case.
**
-** If parameter zDb is NULL or points to a zero length string, then the
-** specified operation is attempted on all WAL databases. In this case the
-** values written to output parameters *pnLog and *pnCkpt are undefined. If
+** ^If parameter zDb is NULL or points to a zero length string, then the
+** specified operation is attempted on all WAL databases [attached] to
+** [database connection] db. In this case the
+** values written to output parameters *pnLog and *pnCkpt are undefined. ^If
** an SQLITE_BUSY error is encountered when processing one or more of the
** attached WAL databases, the operation is still attempted on any remaining
-** attached databases and SQLITE_BUSY is returned to the caller. If any other
+** attached databases and SQLITE_BUSY is returned at the end. ^If any other
** error occurs while processing an attached database, processing is abandoned
-** and the error code returned to the caller immediately. If no error
+** and the error code is returned to the caller immediately. ^If no error
** (SQLITE_BUSY or otherwise) is encountered while processing the attached
** databases, SQLITE_OK is returned.
**
-** If database zDb is the name of an attached database that is not in WAL
-** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If
+** ^If database zDb is the name of an attached database that is not in WAL
+** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. ^If
** zDb is not NULL (or a zero length string) and is not the name of any
** attached database, SQLITE_ERROR is returned to the caller.
+**
+** ^Unless it returns SQLITE_MISUSE,
+** the sqlite3_wal_checkpoint_v2() interface
+** sets the error information that is queried by
+** [sqlite3_errcode()] and [sqlite3_errmsg()].
+**
+** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface
+** from SQL.
*/
SQLITE_API int sqlite3_wal_checkpoint_v2(
sqlite3 *db, /* Database handle */
@@ -7140,16 +8158,18 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
);
/*
-** CAPI3REF: Checkpoint operation parameters
+** CAPI3REF: Checkpoint Mode Values
+** KEYWORDS: {checkpoint mode}
**
-** These constants can be used as the 3rd parameter to
-** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()]
-** documentation for additional information about the meaning and use of
-** each of these values.
+** These constants define all valid values for the "checkpoint mode" passed
+** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
+** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
+** meaning of each of these checkpoint modes.
*/
-#define SQLITE_CHECKPOINT_PASSIVE 0
-#define SQLITE_CHECKPOINT_FULL 1
-#define SQLITE_CHECKPOINT_RESTART 2
+#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
+#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
+#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
** CAPI3REF: Virtual Table Interface Configuration
@@ -7222,6 +8242,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
/*
** CAPI3REF: Conflict resolution modes
+** KEYWORDS: {conflict resolution mode}
**
** These constants are returned by [sqlite3_vtab_on_conflict()] to
** inform a [virtual table] implementation what the [ON CONFLICT] mode
@@ -7237,7 +8258,380 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
/* #define SQLITE_ABORT 4 // Also an error code */
#define SQLITE_REPLACE 5
+/*
+** CAPI3REF: Prepared Statement Scan Status Opcodes
+** KEYWORDS: {scanstatus options}
+**
+** The following constants can be used for the T parameter to the
+** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a
+** different metric for sqlite3_stmt_scanstatus() to return.
+**
+** When the value returned to V is a string, space to hold that string is
+** managed by the prepared statement S and will be automatically freed when
+** S is finalized.
+**
+** <dl>
+** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
+** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be
+** set to the total number of times that the X-th loop has run.</dd>
+**
+** [[SQLITE_SCANSTAT_NVISIT]] <dt>SQLITE_SCANSTAT_NVISIT</dt>
+** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be set
+** to the total number of rows examined by all iterations of the X-th loop.</dd>
+**
+** [[SQLITE_SCANSTAT_EST]] <dt>SQLITE_SCANSTAT_EST</dt>
+** <dd>^The "double" variable pointed to by the T parameter will be set to the
+** query planner's estimate for the average number of rows output from each
+** iteration of the X-th loop. If the query planner's estimates was accurate,
+** then this value will approximate the quotient NVISIT/NLOOP and the
+** product of this value for all prior loops with the same SELECTID will
+** be the NLOOP value for the current loop.
+**
+** [[SQLITE_SCANSTAT_NAME]] <dt>SQLITE_SCANSTAT_NAME</dt>
+** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** to a zero-terminated UTF-8 string containing the name of the index or table
+** used for the X-th loop.
+**
+** [[SQLITE_SCANSTAT_EXPLAIN]] <dt>SQLITE_SCANSTAT_EXPLAIN</dt>
+** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
+** description for the X-th loop.
+**
+** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt>
+** <dd>^The "int" variable pointed to by the T parameter will be set to the
+** "select-id" for the X-th loop. The select-id identifies which query or
+** subquery the loop is part of. The main query has a select-id of zero.
+** The select-id is the same value as is output in the first column
+** of an [EXPLAIN QUERY PLAN] query.
+** </dl>
+*/
+#define SQLITE_SCANSTAT_NLOOP 0
+#define SQLITE_SCANSTAT_NVISIT 1
+#define SQLITE_SCANSTAT_EST 2
+#define SQLITE_SCANSTAT_NAME 3
+#define SQLITE_SCANSTAT_EXPLAIN 4
+#define SQLITE_SCANSTAT_SELECTID 5
+
+/*
+** CAPI3REF: Prepared Statement Scan Status
+** METHOD: sqlite3_stmt
+**
+** This interface returns information about the predicted and measured
+** performance for pStmt. Advanced applications can use this
+** interface to compare the predicted and the measured performance and
+** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
+**
+** Since this interface is expected to be rarely used, it is only
+** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS]
+** compile-time option.
+**
+** The "iScanStatusOp" parameter determines which status information to return.
+** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
+** of this interface is undefined.
+** ^The requested measurement is written into a variable pointed to by
+** the "pOut" parameter.
+** Parameter "idx" identifies the specific loop to retrieve statistics for.
+** Loops are numbered starting from zero. ^If idx is out of range - less than
+** zero or greater than or equal to the total number of loops used to implement
+** the statement - a non-zero value is returned and the variable that pOut
+** points to is unchanged.
+**
+** ^Statistics might not be available for all loops in all statements. ^In cases
+** where there exist loops with no available statistics, this function behaves
+** as if the loop did not exist - it returns non-zero and leave the variable
+** that pOut points to unchanged.
+**
+** See also: [sqlite3_stmt_scanstatus_reset()]
+*/
+SQLITE_API int sqlite3_stmt_scanstatus(
+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */
+ int idx, /* Index of loop to report on */
+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
+ void *pOut /* Result written here */
+);
+
+/*
+** CAPI3REF: Zero Scan-Status Counters
+** METHOD: sqlite3_stmt
+**
+** ^Zero all [sqlite3_stmt_scanstatus()] related event counters.
+**
+** This API is only available if the library is built with pre-processor
+** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined.
+*/
+SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Flush caches to disk mid-transaction
+**
+** ^If a write-transaction is open on [database connection] D when the
+** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
+** pages in the pager-cache that are not currently in use are written out
+** to disk. A dirty page may be in use if a database cursor created by an
+** active SQL statement is reading from it, or if it is page 1 of a database
+** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)]
+** interface flushes caches for all schemas - "main", "temp", and
+** any [attached] databases.
+**
+** ^If this function needs to obtain extra database locks before dirty pages
+** can be flushed to disk, it does so. ^If those locks cannot be obtained
+** immediately and there is a busy-handler callback configured, it is invoked
+** in the usual manner. ^If the required lock still cannot be obtained, then
+** the database is skipped and an attempt made to flush any dirty pages
+** belonging to the next (if any) database. ^If any databases are skipped
+** because locks cannot be obtained, but no other error occurs, this
+** function returns SQLITE_BUSY.
+**
+** ^If any other error occurs while flushing dirty pages to disk (for
+** example an IO error or out-of-memory condition), then processing is
+** abandoned and an SQLite [error code] is returned to the caller immediately.
+**
+** ^Otherwise, if no error occurs, [sqlite3_db_cacheflush()] returns SQLITE_OK.
+**
+** ^This function does not set the database handle error code or message
+** returned by the [sqlite3_errcode()] and [sqlite3_errmsg()] functions.
+*/
+SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
+
+/*
+** CAPI3REF: The pre-update hook.
+**
+** ^These interfaces are only available if SQLite is compiled using the
+** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option.
+**
+** ^The [sqlite3_preupdate_hook()] interface registers a callback function
+** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation
+** on a [rowid table].
+** ^At most one preupdate hook may be registered at a time on a single
+** [database connection]; each call to [sqlite3_preupdate_hook()] overrides
+** the previous setting.
+** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()]
+** with a NULL pointer as the second parameter.
+** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as
+** the first parameter to callbacks.
+**
+** ^The preupdate hook only fires for changes to [rowid tables]; the preupdate
+** hook is not invoked for changes to [virtual tables] or [WITHOUT ROWID]
+** tables.
+**
+** ^The second parameter to the preupdate callback is a pointer to
+** the [database connection] that registered the preupdate hook.
+** ^The third parameter to the preupdate callback is one of the constants
+** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to identify the
+** kind of update operation that is about to occur.
+** ^(The fourth parameter to the preupdate callback is the name of the
+** database within the database connection that is being modified. This
+** will be "main" for the main database or "temp" for TEMP tables or
+** the name given after the AS keyword in the [ATTACH] statement for attached
+** databases.)^
+** ^The fifth parameter to the preupdate callback is the name of the
+** table that is being modified.
+** ^The sixth parameter to the preupdate callback is the initial [rowid] of the
+** row being changes for SQLITE_UPDATE and SQLITE_DELETE changes and is
+** undefined for SQLITE_INSERT changes.
+** ^The seventh parameter to the preupdate callback is the final [rowid] of
+** the row being changed for SQLITE_UPDATE and SQLITE_INSERT changes and is
+** undefined for SQLITE_DELETE changes.
+**
+** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
+** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
+** provide additional information about a preupdate event. These routines
+** may only be called from within a preupdate callback. Invoking any of
+** these routines from outside of a preupdate callback or with a
+** [database connection] pointer that is different from the one supplied
+** to the preupdate callback results in undefined and probably undesirable
+** behavior.
+**
+** ^The [sqlite3_preupdate_count(D)] interface returns the number of columns
+** in the row that is being inserted, updated, or deleted.
+**
+** ^The [sqlite3_preupdate_old(D,N,P)] interface writes into P a pointer to
+** a [protected sqlite3_value] that contains the value of the Nth column of
+** the table row before it is updated. The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within SQLITE_UPDATE and SQLITE_DELETE
+** preupdate callbacks; if it is used by an SQLITE_INSERT callback then the
+** behavior is undefined. The [sqlite3_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [sqlite3_preupdate_new(D,N,P)] interface writes into P a pointer to
+** a [protected sqlite3_value] that contains the value of the Nth column of
+** the table row after it is updated. The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within SQLITE_INSERT and SQLITE_UPDATE
+** preupdate callbacks; if it is used by an SQLITE_DELETE callback then the
+** behavior is undefined. The [sqlite3_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate
+** callback was invoked as a result of a direct insert, update, or delete
+** operation; or 1 for inserts, updates, or deletes invoked by top-level
+** triggers; or 2 for changes resulting from triggers called by top-level
+** triggers; and so forth.
+**
+** See also: [sqlite3_update_hook()]
+*/
+SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
+ sqlite3 *db,
+ void(*xPreUpdate)(
+ void *pCtx, /* Copy of third arg to preupdate_hook() */
+ sqlite3 *db, /* Database handle */
+ int op, /* SQLITE_UPDATE, DELETE or INSERT */
+ char const *zDb, /* Database name */
+ char const *zName, /* Table name */
+ sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
+ sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
+ ),
+ void*
+);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_depth(sqlite3 *);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
+/*
+** CAPI3REF: Low-level system error code
+**
+** ^Attempt to return the underlying operating system error code or error
+** number that caused the most recent I/O error or failure to open a file.
+** The return value is OS-dependent. For example, on unix systems, after
+** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be
+** called to get back the underlying "errno" that caused the problem, such
+** as ENOSPC, EAUTH, EISDIR, and so forth.
+*/
+SQLITE_API int sqlite3_system_errno(sqlite3*);
+
+/*
+** CAPI3REF: Database Snapshot
+** KEYWORDS: {snapshot}
+** EXPERIMENTAL
+**
+** An instance of the snapshot object records the state of a [WAL mode]
+** database for some specific point in history.
+**
+** In [WAL mode], multiple [database connections] that are open on the
+** same database file can each be reading a different historical version
+** of the database file. When a [database connection] begins a read
+** transaction, that connection sees an unchanging copy of the database
+** as it existed for the point in time when the transaction first started.
+** Subsequent changes to the database from other connections are not seen
+** by the reader until a new read transaction is started.
+**
+** The sqlite3_snapshot object records state information about an historical
+** version of the database file so that it is possible to later open a new read
+** transaction that sees that historical version of the database rather than
+** the most recent version.
+**
+** The constructor for this object is [sqlite3_snapshot_get()]. The
+** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
+** to an historical snapshot (if possible). The destructor for
+** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
+*/
+typedef struct sqlite3_snapshot sqlite3_snapshot;
+
+/*
+** CAPI3REF: Record A Database Snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
+** new [sqlite3_snapshot] object that records the current state of
+** schema S in database connection D. ^On success, the
+** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
+** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
+** ^If schema S of [database connection] D is not a [WAL mode] database
+** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
+** leaves the *P value unchanged and returns an appropriate [error code].
+**
+** The [sqlite3_snapshot] object returned from a successful call to
+** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
+** to avoid a memory leak.
+**
+** The [sqlite3_snapshot_get()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
+ sqlite3 *db,
+ const char *zSchema,
+ sqlite3_snapshot **ppSnapshot
+);
+
+/*
+** CAPI3REF: Start a read transaction on an historical snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a
+** read transaction for schema S of
+** [database connection] D such that the read transaction
+** refers to historical [snapshot] P, rather than the most
+** recent change to the database.
+** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
+** or an appropriate [error code] if it fails.
+**
+** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
+** the first operation following the [BEGIN] that takes the schema S
+** out of [autocommit mode].
+** ^In other words, schema S must not currently be in
+** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the
+** database connection D must be out of [autocommit mode].
+** ^A [snapshot] will fail to open if it has been overwritten by a
+** [checkpoint].
+** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
+** database connection D does not know that the database file for
+** schema S is in [WAL mode]. A database connection might not know
+** that the database file is in [WAL mode] if there has been no prior
+** I/O on that database connection, or if the database entered [WAL mode]
+** after the most recent I/O on the database connection.)^
+** (Hint: Run "[PRAGMA application_id]" against a newly opened
+** database connection in order to make it ready to use snapshots.)
+**
+** The [sqlite3_snapshot_open()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
+ sqlite3 *db,
+ const char *zSchema,
+ sqlite3_snapshot *pSnapshot
+);
+
+/*
+** CAPI3REF: Destroy a snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P.
+** The application must eventually free every [sqlite3_snapshot] object
+** using this routine to avoid a memory leak.
+**
+** The [sqlite3_snapshot_free()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
+
+/*
+** CAPI3REF: Compare the ages of two snapshot handles.
+** EXPERIMENTAL
+**
+** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
+** of two valid snapshot handles.
+**
+** If the two snapshot handles are not associated with the same database
+** file, the result of the comparison is undefined.
+**
+** Additionally, the result of the comparison is only valid if both of the
+** snapshot handles were obtained by calling sqlite3_snapshot_get() since the
+** last time the wal file was deleted. The wal file is deleted when the
+** database is changed back to rollback mode or when the number of database
+** clients drops to zero. If either snapshot handle was obtained before the
+** wal file was last deleted, the value returned by this function
+** is undefined.
+**
+** Otherwise, this API returns a negative value if P1 refers to an older
+** snapshot than P2, zero if the two handles refer to the same database
+** snapshot, and a positive value if P1 is a newer snapshot than P2.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
+ sqlite3_snapshot *p1,
+ sqlite3_snapshot *p2
+);
/*
** Undo the hack that converts floating point types to integer for
@@ -7250,8 +8644,9 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
#if 0
} /* End of the 'extern "C"' block */
#endif
-#endif
+#endif /* SQLITE3_H */
+/******** Begin file sqlite3rtree.h *********/
/*
** 2010 August 30
**
@@ -7274,6 +8669,16 @@ extern "C" {
#endif
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
+typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
+
+/* The double-precision datatype used by RTree depends on the
+** SQLITE_RTREE_INT_ONLY compile-time option.
+*/
+#ifdef SQLITE_RTREE_INT_ONLY
+ typedef sqlite3_int64 sqlite3_rtree_dbl;
+#else
+ typedef double sqlite3_rtree_dbl;
+#endif
/*
** Register a geometry callback named zGeom that can be used as part of an
@@ -7284,7 +8689,7 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
SQLITE_API int sqlite3_rtree_geometry_callback(
sqlite3 *db,
const char *zGeom,
- int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes),
+ int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*),
void *pContext
);
@@ -7296,11 +8701,62 @@ SQLITE_API int sqlite3_rtree_geometry_callback(
struct sqlite3_rtree_geometry {
void *pContext; /* Copy of pContext passed to s_r_g_c() */
int nParam; /* Size of array aParam[] */
- double *aParam; /* Parameters passed to SQL geom function */
+ sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */
void *pUser; /* Callback implementation user data */
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
};
+/*
+** Register a 2nd-generation geometry callback named zScore that can be
+** used as part of an R-Tree geometry query as follows:
+**
+** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
+*/
+SQLITE_API int sqlite3_rtree_query_callback(
+ sqlite3 *db,
+ const char *zQueryFunc,
+ int (*xQueryFunc)(sqlite3_rtree_query_info*),
+ void *pContext,
+ void (*xDestructor)(void*)
+);
+
+
+/*
+** A pointer to a structure of the following type is passed as the
+** argument to scored geometry callback registered using
+** sqlite3_rtree_query_callback().
+**
+** Note that the first 5 fields of this structure are identical to
+** sqlite3_rtree_geometry. This structure is a subclass of
+** sqlite3_rtree_geometry.
+*/
+struct sqlite3_rtree_query_info {
+ void *pContext; /* pContext from when function registered */
+ int nParam; /* Number of function parameters */
+ sqlite3_rtree_dbl *aParam; /* value of function parameters */
+ void *pUser; /* callback can use this, if desired */
+ void (*xDelUser)(void*); /* function to free pUser */
+ sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */
+ unsigned int *anQueue; /* Number of pending entries in the queue */
+ int nCoord; /* Number of coordinates */
+ int iLevel; /* Level of current node or entry */
+ int mxLevel; /* The largest iLevel value in the tree */
+ sqlite3_int64 iRowid; /* Rowid for current entry */
+ sqlite3_rtree_dbl rParentScore; /* Score of parent node */
+ int eParentWithin; /* Visibility of parent node */
+ int eWithin; /* OUT: Visiblity */
+ sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
+ /* The following fields are only available in 3.8.11 and later */
+ sqlite3_value **apSqlParam; /* Original SQL values of parameters */
+};
+
+/*
+** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin.
+*/
+#define NOT_WITHIN 0 /* Object completely outside of query region */
+#define PARTLY_WITHIN 1 /* Object partially overlaps query region */
+#define FULLY_WITHIN 2 /* Object fully contained within query region */
+
#if 0
} /* end of the 'extern "C"' block */
@@ -7308,9 +8764,2428 @@ struct sqlite3_rtree_geometry {
#endif /* ifndef _SQLITE3RTREE_H_ */
+/******** End of sqlite3rtree.h *********/
+/******** Begin file sqlite3session.h *********/
+
+#if !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION)
+#define __SQLITESESSION_H_ 1
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#if 0
+extern "C" {
+#endif
+
+
+/*
+** CAPI3REF: Session Object Handle
+*/
+typedef struct sqlite3_session sqlite3_session;
+
+/*
+** CAPI3REF: Changeset Iterator Handle
+*/
+typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
+
+/*
+** CAPI3REF: Create A New Session Object
+**
+** Create a new session object attached to database handle db. If successful,
+** a pointer to the new object is written to *ppSession and SQLITE_OK is
+** returned. If an error occurs, *ppSession is set to NULL and an SQLite
+** error code (e.g. SQLITE_NOMEM) is returned.
+**
+** It is possible to create multiple session objects attached to a single
+** database handle.
+**
+** Session objects created using this function should be deleted using the
+** [sqlite3session_delete()] function before the database handle that they
+** are attached to is itself closed. If the database handle is closed before
+** the session object is deleted, then the results of calling any session
+** module function, including [sqlite3session_delete()] on the session object
+** are undefined.
+**
+** Because the session module uses the [sqlite3_preupdate_hook()] API, it
+** is not possible for an application to register a pre-update hook on a
+** database handle that has one or more session objects attached. Nor is
+** it possible to create a session object attached to a database handle for
+** which a pre-update hook is already defined. The results of attempting
+** either of these things are undefined.
+**
+** The session object will be used to create changesets for tables in
+** database zDb, where zDb is either "main", or "temp", or the name of an
+** attached database. It is not an error if database zDb is not attached
+** to the database when the session object is created.
+*/
+int sqlite3session_create(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of db (e.g. "main") */
+ sqlite3_session **ppSession /* OUT: New session object */
+);
+
+/*
+** CAPI3REF: Delete A Session Object
+**
+** Delete a session object previously allocated using
+** [sqlite3session_create()]. Once a session object has been deleted, the
+** results of attempting to use pSession with any other session module
+** function are undefined.
+**
+** Session objects must be deleted before the database handle to which they
+** are attached is closed. Refer to the documentation for
+** [sqlite3session_create()] for details.
+*/
+void sqlite3session_delete(sqlite3_session *pSession);
+
+
+/*
+** CAPI3REF: Enable Or Disable A Session Object
+**
+** Enable or disable the recording of changes by a session object. When
+** enabled, a session object records changes made to the database. When
+** disabled - it does not. A newly created session object is enabled.
+** Refer to the documentation for [sqlite3session_changeset()] for further
+** details regarding how enabling and disabling a session object affects
+** the eventual changesets.
+**
+** Passing zero to this function disables the session. Passing a value
+** greater than zero enables it. Passing a value less than zero is a
+** no-op, and may be used to query the current state of the session.
+**
+** The return value indicates the final state of the session object: 0 if
+** the session is disabled, or 1 if it is enabled.
+*/
+int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
+
+/*
+** CAPI3REF: Set Or Clear the Indirect Change Flag
+**
+** Each change recorded by a session object is marked as either direct or
+** indirect. A change is marked as indirect if either:
+**
+** <ul>
+** <li> The session object "indirect" flag is set when the change is
+** made, or
+** <li> The change is made by an SQL trigger or foreign key action
+** instead of directly as a result of a users SQL statement.
+** </ul>
+**
+** If a single row is affected by more than one operation within a session,
+** then the change is considered indirect if all operations meet the criteria
+** for an indirect change above, or direct otherwise.
+**
+** This function is used to set, clear or query the session object indirect
+** flag. If the second argument passed to this function is zero, then the
+** indirect flag is cleared. If it is greater than zero, the indirect flag
+** is set. Passing a value less than zero does not modify the current value
+** of the indirect flag, and may be used to query the current state of the
+** indirect flag for the specified session object.
+**
+** The return value indicates the final state of the indirect flag: 0 if
+** it is clear, or 1 if it is set.
+*/
+int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);
+
+/*
+** CAPI3REF: Attach A Table To A Session Object
+**
+** If argument zTab is not NULL, then it is the name of a table to attach
+** to the session object passed as the first argument. All subsequent changes
+** made to the table while the session object is enabled will be recorded. See
+** documentation for [sqlite3session_changeset()] for further details.
+**
+** Or, if argument zTab is NULL, then changes are recorded for all tables
+** in the database. If additional tables are added to the database (by
+** executing "CREATE TABLE" statements) after this call is made, changes for
+** the new tables are also recorded.
+**
+** Changes can only be recorded for tables that have a PRIMARY KEY explicitly
+** defined as part of their CREATE TABLE statement. It does not matter if the
+** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY
+** KEY may consist of a single column, or may be a composite key.
+**
+** It is not an error if the named table does not exist in the database. Nor
+** is it an error if the named table does not have a PRIMARY KEY. However,
+** no changes will be recorded in either of these scenarios.
+**
+** Changes are not recorded for individual rows that have NULL values stored
+** in one or more of their PRIMARY KEY columns.
+**
+** SQLITE_OK is returned if the call completes without error. Or, if an error
+** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
+*/
+int sqlite3session_attach(
+ sqlite3_session *pSession, /* Session object */
+ const char *zTab /* Table name */
+);
+
+/*
+** CAPI3REF: Set a table filter on a Session Object.
+**
+** The second argument (xFilter) is the "filter callback". For changes to rows
+** in tables that are not attached to the Session object, the filter is called
+** to determine whether changes to the table's rows should be tracked or not.
+** If xFilter returns 0, changes is not tracked. Note that once a table is
+** attached, xFilter will not be called again.
+*/
+void sqlite3session_table_filter(
+ sqlite3_session *pSession, /* Session object */
+ int(*xFilter)(
+ void *pCtx, /* Copy of third arg to _filter_table() */
+ const char *zTab /* Table name */
+ ),
+ void *pCtx /* First argument passed to xFilter */
+);
+
+/*
+** CAPI3REF: Generate A Changeset From A Session Object
+**
+** Obtain a changeset containing changes to the tables attached to the
+** session object passed as the first argument. If successful,
+** set *ppChangeset to point to a buffer containing the changeset
+** and *pnChangeset to the size of the changeset in bytes before returning
+** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to
+** zero and return an SQLite error code.
+**
+** A changeset consists of zero or more INSERT, UPDATE and/or DELETE changes,
+** each representing a change to a single row of an attached table. An INSERT
+** change contains the values of each field of a new database row. A DELETE
+** contains the original values of each field of a deleted database row. An
+** UPDATE change contains the original values of each field of an updated
+** database row along with the updated values for each updated non-primary-key
+** column. It is not possible for an UPDATE change to represent a change that
+** modifies the values of primary key columns. If such a change is made, it
+** is represented in a changeset as a DELETE followed by an INSERT.
+**
+** Changes are not recorded for rows that have NULL values stored in one or
+** more of their PRIMARY KEY columns. If such a row is inserted or deleted,
+** no corresponding change is present in the changesets returned by this
+** function. If an existing row with one or more NULL values stored in
+** PRIMARY KEY columns is updated so that all PRIMARY KEY columns are non-NULL,
+** only an INSERT is appears in the changeset. Similarly, if an existing row
+** with non-NULL PRIMARY KEY values is updated so that one or more of its
+** PRIMARY KEY columns are set to NULL, the resulting changeset contains a
+** DELETE change only.
+**
+** The contents of a changeset may be traversed using an iterator created
+** using the [sqlite3changeset_start()] API. A changeset may be applied to
+** a database with a compatible schema using the [sqlite3changeset_apply()]
+** API.
+**
+** Within a changeset generated by this function, all changes related to a
+** single table are grouped together. In other words, when iterating through
+** a changeset or when applying a changeset to a database, all changes related
+** to a single table are processed before moving on to the next table. Tables
+** are sorted in the same order in which they were attached (or auto-attached)
+** to the sqlite3_session object. The order in which the changes related to
+** a single table are stored is undefined.
+**
+** Following a successful call to this function, it is the responsibility of
+** the caller to eventually free the buffer that *ppChangeset points to using
+** [sqlite3_free()].
+**
+** <h3>Changeset Generation</h3>
+**
+** Once a table has been attached to a session object, the session object
+** records the primary key values of all new rows inserted into the table.
+** It also records the original primary key and other column values of any
+** deleted or updated rows. For each unique primary key value, data is only
+** recorded once - the first time a row with said primary key is inserted,
+** updated or deleted in the lifetime of the session.
+**
+** There is one exception to the previous paragraph: when a row is inserted,
+** updated or deleted, if one or more of its primary key columns contain a
+** NULL value, no record of the change is made.
+**
+** The session object therefore accumulates two types of records - those
+** that consist of primary key values only (created when the user inserts
+** a new record) and those that consist of the primary key values and the
+** original values of other table columns (created when the users deletes
+** or updates a record).
+**
+** When this function is called, the requested changeset is created using
+** both the accumulated records and the current contents of the database
+** file. Specifically:
+**
+** <ul>
+** <li> For each record generated by an insert, the database is queried
+** for a row with a matching primary key. If one is found, an INSERT
+** change is added to the changeset. If no such row is found, no change
+** is added to the changeset.
+**
+** <li> For each record generated by an update or delete, the database is
+** queried for a row with a matching primary key. If such a row is
+** found and one or more of the non-primary key fields have been
+** modified from their original values, an UPDATE change is added to
+** the changeset. Or, if no such row is found in the table, a DELETE
+** change is added to the changeset. If there is a row with a matching
+** primary key in the database, but all fields contain their original
+** values, no change is added to the changeset.
+** </ul>
+**
+** This means, amongst other things, that if a row is inserted and then later
+** deleted while a session object is active, neither the insert nor the delete
+** will be present in the changeset. Or if a row is deleted and then later a
+** row with the same primary key values inserted while a session object is
+** active, the resulting changeset will contain an UPDATE change instead of
+** a DELETE and an INSERT.
+**
+** When a session object is disabled (see the [sqlite3session_enable()] API),
+** it does not accumulate records when rows are inserted, updated or deleted.
+** This may appear to have some counter-intuitive effects if a single row
+** is written to more than once during a session. For example, if a row
+** is inserted while a session object is enabled, then later deleted while
+** the same session object is disabled, no INSERT record will appear in the
+** changeset, even though the delete took place while the session was disabled.
+** Or, if one field of a row is updated while a session is disabled, and
+** another field of the same row is updated while the session is enabled, the
+** resulting changeset will contain an UPDATE change that updates both fields.
+*/
+int sqlite3session_changeset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppChangeset /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Load The Difference Between Tables Into A Session
+**
+** If it is not already attached to the session object passed as the first
+** argument, this function attaches table zTbl in the same manner as the
+** [sqlite3session_attach()] function. If zTbl does not exist, or if it
+** does not have a primary key, this function is a no-op (but does not return
+** an error).
+**
+** Argument zFromDb must be the name of a database ("main", "temp" etc.)
+** attached to the same database handle as the session object that contains
+** a table compatible with the table attached to the session by this function.
+** A table is considered compatible if it:
+**
+** <ul>
+** <li> Has the same name,
+** <li> Has the same set of columns declared in the same order, and
+** <li> Has the same PRIMARY KEY definition.
+** </ul>
+**
+** If the tables are not compatible, SQLITE_SCHEMA is returned. If the tables
+** are compatible but do not have any PRIMARY KEY columns, it is not an error
+** but no changes are added to the session object. As with other session
+** APIs, tables without PRIMARY KEYs are simply ignored.
+**
+** This function adds a set of changes to the session object that could be
+** used to update the table in database zFrom (call this the "from-table")
+** so that its content is the same as the table attached to the session
+** object (call this the "to-table"). Specifically:
+**
+** <ul>
+** <li> For each row (primary key) that exists in the to-table but not in
+** the from-table, an INSERT record is added to the session object.
+**
+** <li> For each row (primary key) that exists in the to-table but not in
+** the from-table, a DELETE record is added to the session object.
+**
+** <li> For each row (primary key) that exists in both tables, but features
+** different in each, an UPDATE record is added to the session.
+** </ul>
+**
+** To clarify, if this function is called and then a changeset constructed
+** using [sqlite3session_changeset()], then after applying that changeset to
+** database zFrom the contents of the two compatible tables would be
+** identical.
+**
+** It an error if database zFrom does not exist or does not contain the
+** required compatible table.
+**
+** If the operation successful, SQLITE_OK is returned. Otherwise, an SQLite
+** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
+** may be set to point to a buffer containing an English language error
+** message. It is the responsibility of the caller to free this buffer using
+** sqlite3_free().
+*/
+int sqlite3session_diff(
+ sqlite3_session *pSession,
+ const char *zFromDb,
+ const char *zTbl,
+ char **pzErrMsg
+);
+
+
+/*
+** CAPI3REF: Generate A Patchset From A Session Object
+**
+** The differences between a patchset and a changeset are that:
+**
+** <ul>
+** <li> DELETE records consist of the primary key fields only. The
+** original values of other fields are omitted.
+** <li> The original values of any modified fields are omitted from
+** UPDATE records.
+** </ul>
+**
+** A patchset blob may be used with up to date versions of all
+** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(),
+** which returns SQLITE_CORRUPT if it is passed a patchset. Similarly,
+** attempting to use a patchset blob with old versions of the
+** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error.
+**
+** Because the non-primary key "old.*" fields are omitted, no
+** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset
+** is passed to the sqlite3changeset_apply() API. Other conflict types work
+** in the same way as for changesets.
+**
+** Changes within a patchset are ordered in the same way as for changesets
+** generated by the sqlite3session_changeset() function (i.e. all changes for
+** a single table are grouped together, tables appear in the order in which
+** they were attached to the session object).
+*/
+int sqlite3session_patchset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppPatchset /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Test if a changeset has recorded any changes.
+**
+** Return non-zero if no changes to attached tables have been recorded by
+** the session object passed as the first argument. Otherwise, if one or
+** more changes have been recorded, return zero.
+**
+** Even if this function returns zero, it is possible that calling
+** [sqlite3session_changeset()] on the session handle may still return a
+** changeset that contains no changes. This can happen when a row in
+** an attached table is modified and then later on the original values
+** are restored. However, if this function returns non-zero, then it is
+** guaranteed that a call to sqlite3session_changeset() will return a
+** changeset containing zero changes.
+*/
+int sqlite3session_isempty(sqlite3_session *pSession);
+
+/*
+** CAPI3REF: Create An Iterator To Traverse A Changeset
+**
+** Create an iterator used to iterate through the contents of a changeset.
+** If successful, *pp is set to point to the iterator handle and SQLITE_OK
+** is returned. Otherwise, if an error occurs, *pp is set to zero and an
+** SQLite error code is returned.
+**
+** The following functions can be used to advance and query a changeset
+** iterator created by this function:
+**
+** <ul>
+** <li> [sqlite3changeset_next()]
+** <li> [sqlite3changeset_op()]
+** <li> [sqlite3changeset_new()]
+** <li> [sqlite3changeset_old()]
+** </ul>
+**
+** It is the responsibility of the caller to eventually destroy the iterator
+** by passing it to [sqlite3changeset_finalize()]. The buffer containing the
+** changeset (pChangeset) must remain valid until after the iterator is
+** destroyed.
+**
+** Assuming the changeset blob was created by one of the
+** [sqlite3session_changeset()], [sqlite3changeset_concat()] or
+** [sqlite3changeset_invert()] functions, all changes within the changeset
+** that apply to a single table are grouped together. This means that when
+** an application iterates through a changeset using an iterator created by
+** this function, all changes that relate to a single table are visited
+** consecutively. There is no chance that the iterator will visit a change
+** the applies to table X, then one for table Y, and then later on visit
+** another change for table X.
+*/
+int sqlite3changeset_start(
+ sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
+ int nChangeset, /* Size of changeset blob in bytes */
+ void *pChangeset /* Pointer to blob containing changeset */
+);
+
+
+/*
+** CAPI3REF: Advance A Changeset Iterator
+**
+** This function may only be used with iterators created by function
+** [sqlite3changeset_start()]. If it is called on an iterator passed to
+** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE
+** is returned and the call has no effect.
+**
+** Immediately after an iterator is created by sqlite3changeset_start(), it
+** does not point to any change in the changeset. Assuming the changeset
+** is not empty, the first call to this function advances the iterator to
+** point to the first change in the changeset. Each subsequent call advances
+** the iterator to point to the next change in the changeset (if any). If
+** no error occurs and the iterator points to a valid change after a call
+** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned.
+** Otherwise, if all changes in the changeset have already been visited,
+** SQLITE_DONE is returned.
+**
+** If an error occurs, an SQLite error code is returned. Possible error
+** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or
+** SQLITE_NOMEM.
+*/
+int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Obtain The Current Operation From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
+** is not the case, this function returns [SQLITE_MISUSE].
+**
+** If argument pzTab is not NULL, then *pzTab is set to point to a
+** nul-terminated utf-8 encoded string containing the name of the table
+** affected by the current change. The buffer remains valid until either
+** sqlite3changeset_next() is called on the iterator or until the
+** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
+** set to the number of columns in the table affected by the change. If
+** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change
+** is an indirect change, or false (0) otherwise. See the documentation for
+** [sqlite3session_indirect()] for a description of direct and indirect
+** changes. Finally, if pOp is not NULL, then *pOp is set to one of
+** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
+** type of change that the iterator currently points to.
+**
+** If no error occurs, SQLITE_OK is returned. If an error does occur, an
+** SQLite error code is returned. The values of the output variables may not
+** be trusted in this case.
+*/
+int sqlite3changeset_op(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ const char **pzTab, /* OUT: Pointer to table name */
+ int *pnCol, /* OUT: Number of columns in table */
+ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
+ int *pbIndirect /* OUT: True for an 'indirect' change */
+);
+
+/*
+** CAPI3REF: Obtain The Primary Key Definition Of A Table
+**
+** For each modified table, a changeset includes the following:
+**
+** <ul>
+** <li> The number of columns in the table, and
+** <li> Which of those columns make up the tables PRIMARY KEY.
+** </ul>
+**
+** This function is used to find which columns comprise the PRIMARY KEY of
+** the table modified by the change that iterator pIter currently points to.
+** If successful, *pabPK is set to point to an array of nCol entries, where
+** nCol is the number of columns in the table. Elements of *pabPK are set to
+** 0x01 if the corresponding column is part of the tables primary key, or
+** 0x00 if it is not.
+**
+** If argument pnCol is not NULL, then *pnCol is set to the number of columns
+** in the table.
+**
+** If this function is called when the iterator does not point to a valid
+** entry, SQLITE_MISUSE is returned and the output variables zeroed. Otherwise,
+** SQLITE_OK is returned and the output variables populated as described
+** above.
+*/
+int sqlite3changeset_pk(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */
+ int *pnCol /* OUT: Number of entries in output array */
+);
+
+/*
+** CAPI3REF: Obtain old.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [SQLITE_DELETE] or [SQLITE_UPDATE]. Otherwise,
+** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the vector of
+** original row values stored as part of the UPDATE or DELETE change and
+** returns SQLITE_OK. The name of the function comes from the fact that this
+** is similar to the "old.*" columns available to update or delete triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_old(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain new.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [SQLITE_UPDATE] or [SQLITE_INSERT]. Otherwise,
+** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the vector of
+** new row values stored as part of the UPDATE or INSERT change and
+** returns SQLITE_OK. If the change is an UPDATE and does not include
+** a new value for the requested column, *ppValue is set to NULL and
+** SQLITE_OK returned. The name of the function comes from the fact that
+** this is similar to the "new.*" columns available to update or delete
+** triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_new(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator
+**
+** This function should only be used with iterator objects passed to a
+** conflict-handler callback by [sqlite3changeset_apply()] with either
+** [SQLITE_CHANGESET_DATA] or [SQLITE_CHANGESET_CONFLICT]. If this function
+** is called on any other iterator, [SQLITE_MISUSE] is returned and *ppValue
+** is set to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the
+** "conflicting row" associated with the current conflict-handler callback
+** and returns SQLITE_OK.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_conflict(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: Value from conflicting row */
+);
+
+/*
+** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations
+**
+** This function may only be called with an iterator passed to an
+** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
+** it sets the output variable to the total number of known foreign key
+** violations in the destination database and returns SQLITE_OK.
+**
+** In all other cases this function returns SQLITE_MISUSE.
+*/
+int sqlite3changeset_fk_conflicts(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int *pnOut /* OUT: Number of FK violations */
+);
+
+
+/*
+** CAPI3REF: Finalize A Changeset Iterator
+**
+** This function is used to finalize an iterator allocated with
+** [sqlite3changeset_start()].
+**
+** This function should only be called on iterators created using the
+** [sqlite3changeset_start()] function. If an application calls this
+** function with an iterator passed to a conflict-handler by
+** [sqlite3changeset_apply()], [SQLITE_MISUSE] is immediately returned and the
+** call has no effect.
+**
+** If an error was encountered within a call to an sqlite3changeset_xxx()
+** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an
+** [SQLITE_NOMEM] in [sqlite3changeset_new()]) then an error code corresponding
+** to that error is returned by this function. Otherwise, SQLITE_OK is
+** returned. This is to allow the following pattern (pseudo-code):
+**
+** sqlite3changeset_start();
+** while( SQLITE_ROW==sqlite3changeset_next() ){
+** // Do something with change.
+** }
+** rc = sqlite3changeset_finalize();
+** if( rc!=SQLITE_OK ){
+** // An error has occurred
+** }
+*/
+int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Invert A Changeset
+**
+** This function is used to "invert" a changeset object. Applying an inverted
+** changeset to a database reverses the effects of applying the uninverted
+** changeset. Specifically:
+**
+** <ul>
+** <li> Each DELETE change is changed to an INSERT, and
+** <li> Each INSERT change is changed to a DELETE, and
+** <li> For each UPDATE change, the old.* and new.* values are exchanged.
+** </ul>
+**
+** This function does not change the order in which changes appear within
+** the changeset. It merely reverses the sense of each individual change.
+**
+** If successful, a pointer to a buffer containing the inverted changeset
+** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and
+** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are
+** zeroed and an SQLite error code returned.
+**
+** It is the responsibility of the caller to eventually call sqlite3_free()
+** on the *ppOut pointer to free the buffer allocation following a successful
+** call to this function.
+**
+** WARNING/TODO: This function currently assumes that the input is a valid
+** changeset. If it is not, the results are undefined.
+*/
+int sqlite3changeset_invert(
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+/*
+** CAPI3REF: Concatenate Two Changeset Objects
+**
+** This function is used to concatenate two changesets, A and B, into a
+** single changeset. The result is a changeset equivalent to applying
+** changeset A followed by changeset B.
+**
+** This function combines the two input changesets using an
+** sqlite3_changegroup object. Calling it produces similar results as the
+** following code fragment:
+**
+** sqlite3_changegroup *pGrp;
+** rc = sqlite3_changegroup_new(&pGrp);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
+** if( rc==SQLITE_OK ){
+** rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
+** }else{
+** *ppOut = 0;
+** *pnOut = 0;
+** }
+**
+** Refer to the sqlite3_changegroup documentation below for details.
+*/
+int sqlite3changeset_concat(
+ int nA, /* Number of bytes in buffer pA */
+ void *pA, /* Pointer to buffer containing changeset A */
+ int nB, /* Number of bytes in buffer pB */
+ void *pB, /* Pointer to buffer containing changeset B */
+ int *pnOut, /* OUT: Number of bytes in output changeset */
+ void **ppOut /* OUT: Buffer containing output changeset */
+);
+
+
+/*
+** CAPI3REF: Changegroup Handle
+*/
+typedef struct sqlite3_changegroup sqlite3_changegroup;
+
+/*
+** CAPI3REF: Create A New Changegroup Object
+**
+** An sqlite3_changegroup object is used to combine two or more changesets
+** (or patchsets) into a single changeset (or patchset). A single changegroup
+** object may combine changesets or patchsets, but not both. The output is
+** always in the same format as the input.
+**
+** If successful, this function returns SQLITE_OK and populates (*pp) with
+** a pointer to a new sqlite3_changegroup object before returning. The caller
+** should eventually free the returned object using a call to
+** sqlite3changegroup_delete(). If an error occurs, an SQLite error code
+** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL.
+**
+** The usual usage pattern for an sqlite3_changegroup object is as follows:
+**
+** <ul>
+** <li> It is created using a call to sqlite3changegroup_new().
+**
+** <li> Zero or more changesets (or patchsets) are added to the object
+** by calling sqlite3changegroup_add().
+**
+** <li> The result of combining all input changesets together is obtained
+** by the application via a call to sqlite3changegroup_output().
+**
+** <li> The object is deleted using a call to sqlite3changegroup_delete().
+** </ul>
+**
+** Any number of calls to add() and output() may be made between the calls to
+** new() and delete(), and in any order.
+**
+** As well as the regular sqlite3changegroup_add() and
+** sqlite3changegroup_output() functions, also available are the streaming
+** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm().
+*/
+int sqlite3changegroup_new(sqlite3_changegroup **pp);
+
+/*
+** CAPI3REF: Add A Changeset To A Changegroup
+**
+** Add all changes within the changeset (or patchset) in buffer pData (size
+** nData bytes) to the changegroup.
+**
+** If the buffer contains a patchset, then all prior calls to this function
+** on the same changegroup object must also have specified patchsets. Or, if
+** the buffer contains a changeset, so must have the earlier calls to this
+** function. Otherwise, SQLITE_ERROR is returned and no changes are added
+** to the changegroup.
+**
+** Rows within the changeset and changegroup are identified by the values in
+** their PRIMARY KEY columns. A change in the changeset is considered to
+** apply to the same row as a change already present in the changegroup if
+** the two rows have the same primary key.
+**
+** Changes to rows that do not already appear in the changegroup are
+** simply copied into it. Or, if both the new changeset and the changegroup
+** contain changes that apply to a single row, the final contents of the
+** changegroup depends on the type of each change, as follows:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+** <tr><th style="white-space:pre">Existing Change </th>
+** <th style="white-space:pre">New Change </th>
+** <th>Output Change
+** <tr><td>INSERT <td>INSERT <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>INSERT <td>UPDATE <td>
+** The INSERT change remains in the changegroup. The values in the
+** INSERT change are modified as if the row was inserted by the
+** existing change and then updated according to the new change.
+** <tr><td>INSERT <td>DELETE <td>
+** The existing INSERT is removed from the changegroup. The DELETE is
+** not added.
+** <tr><td>UPDATE <td>INSERT <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>UPDATE <td>UPDATE <td>
+** The existing UPDATE remains within the changegroup. It is amended
+** so that the accompanying values are as if the row was updated once
+** by the existing change and then again by the new change.
+** <tr><td>UPDATE <td>DELETE <td>
+** The existing UPDATE is replaced by the new DELETE within the
+** changegroup.
+** <tr><td>DELETE <td>INSERT <td>
+** If one or more of the column values in the row inserted by the
+** new change differ from those in the row deleted by the existing
+** change, the existing DELETE is replaced by an UPDATE within the
+** changegroup. Otherwise, if the inserted row is exactly the same
+** as the deleted row, the existing DELETE is simply discarded.
+** <tr><td>DELETE <td>UPDATE <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>DELETE <td>DELETE <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** </table>
+**
+** If the new changeset contains changes to a table that is already present
+** in the changegroup, then the number of columns and the position of the
+** primary key columns for the table must be consistent. If this is not the
+** case, this function fails with SQLITE_SCHEMA. If the input changeset
+** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
+** returned. Or, if an out-of-memory condition occurs during processing, this
+** function returns SQLITE_NOMEM. In all cases, if an error occurs the
+** final contents of the changegroup is undefined.
+**
+** If no error occurs, SQLITE_OK is returned.
+*/
+int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
+
+/*
+** CAPI3REF: Obtain A Composite Changeset From A Changegroup
+**
+** Obtain a buffer containing a changeset (or patchset) representing the
+** current contents of the changegroup. If the inputs to the changegroup
+** were themselves changesets, the output is a changeset. Or, if the
+** inputs were patchsets, the output is also a patchset.
+**
+** As with the output of the sqlite3session_changeset() and
+** sqlite3session_patchset() functions, all changes related to a single
+** table are grouped together in the output of this function. Tables appear
+** in the same order as for the very first changeset added to the changegroup.
+** If the second or subsequent changesets added to the changegroup contain
+** changes for tables that do not appear in the first changeset, they are
+** appended onto the end of the output changeset, again in the order in
+** which they are first encountered.
+**
+** If an error occurs, an SQLite error code is returned and the output
+** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK
+** is returned and the output variables are set to the size of and a
+** pointer to the output buffer, respectively. In this case it is the
+** responsibility of the caller to eventually free the buffer using a
+** call to sqlite3_free().
+*/
+int sqlite3changegroup_output(
+ sqlite3_changegroup*,
+ int *pnData, /* OUT: Size of output buffer in bytes */
+ void **ppData /* OUT: Pointer to output buffer */
+);
+
+/*
+** CAPI3REF: Delete A Changegroup Object
+*/
+void sqlite3changegroup_delete(sqlite3_changegroup*);
+
+/*
+** CAPI3REF: Apply A Changeset To A Database
+**
+** Apply a changeset to a database. This function attempts to update the
+** "main" database attached to handle db with the changes found in the
+** changeset passed via the second and third arguments.
+**
+** The fourth argument (xFilter) passed to this function is the "filter
+** callback". If it is not NULL, then for each table affected by at least one
+** change in the changeset, the filter callback is invoked with
+** the table name as the second argument, and a copy of the context pointer
+** passed as the sixth argument to this function as the first. If the "filter
+** callback" returns zero, then no attempt is made to apply any changes to
+** the table. Otherwise, if the return value is non-zero or the xFilter
+** argument to this function is NULL, all changes related to the table are
+** attempted.
+**
+** For each table that is not excluded by the filter callback, this function
+** tests that the target database contains a compatible table. A table is
+** considered compatible if all of the following are true:
+**
+** <ul>
+** <li> The table has the same name as the name recorded in the
+** changeset, and
+** <li> The table has the same number of columns as recorded in the
+** changeset, and
+** <li> The table has primary key columns in the same position as
+** recorded in the changeset.
+** </ul>
+**
+** If there is no compatible table, it is not an error, but none of the
+** changes associated with the table are applied. A warning message is issued
+** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most
+** one such warning is issued for each table in the changeset.
+**
+** For each change for which there is a compatible table, an attempt is made
+** to modify the table contents according to the UPDATE, INSERT or DELETE
+** change. If a change cannot be applied cleanly, the conflict handler
+** function passed as the fifth argument to sqlite3changeset_apply() may be
+** invoked. A description of exactly when the conflict handler is invoked for
+** each type of change is below.
+**
+** Unlike the xFilter argument, xConflict may not be passed NULL. The results
+** of passing anything other than a valid function pointer as the xConflict
+** argument are undefined.
+**
+** Each time the conflict handler function is invoked, it must return one
+** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
+** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned
+** if the second argument passed to the conflict handler is either
+** SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler
+** returns an illegal value, any changes already made are rolled back and
+** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different
+** actions are taken by sqlite3changeset_apply() depending on the value
+** returned by each invocation of the conflict-handler function. Refer to
+** the documentation for the three
+** [SQLITE_CHANGESET_OMIT|available return values] for details.
+**
+** <dl>
+** <dt>DELETE Changes<dd>
+** For each DELETE change, this function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
+** the changeset the row is deleted from the target database.
+**
+** If a row with matching primary key values is found, but one or more of
+** the non-primary key fields contains a value different from the original
+** row value stored in the changeset, the conflict-handler function is
+** invoked with [SQLITE_CHANGESET_DATA] as the second argument.
+**
+** If no row with matching primary key values is found in the database,
+** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
+** passed as the second argument.
+**
+** If the DELETE operation is attempted, but SQLite returns SQLITE_CONSTRAINT
+** (which can only happen if a foreign key constraint is violated), the
+** conflict-handler function is invoked with [SQLITE_CHANGESET_CONSTRAINT]
+** passed as the second argument. This includes the case where the DELETE
+** operation is attempted because an earlier call to the conflict handler
+** function returned [SQLITE_CHANGESET_REPLACE].
+**
+** <dt>INSERT Changes<dd>
+** For each INSERT change, an attempt is made to insert the new row into
+** the database.
+**
+** If the attempt to insert the row fails because the database already
+** contains a row with the same primary key values, the conflict handler
+** function is invoked with the second argument set to
+** [SQLITE_CHANGESET_CONFLICT].
+**
+** If the attempt to insert the row fails because of some other constraint
+** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is
+** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT].
+** This includes the case where the INSERT operation is re-attempted because
+** an earlier call to the conflict handler function returned
+** [SQLITE_CHANGESET_REPLACE].
+**
+** <dt>UPDATE Changes<dd>
+** For each UPDATE change, this function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
+** the changeset the row is updated within the target database.
+**
+** If a row with matching primary key values is found, but one or more of
+** the non-primary key fields contains a value different from an original
+** row value stored in the changeset, the conflict-handler function is
+** invoked with [SQLITE_CHANGESET_DATA] as the second argument. Since
+** UPDATE changes only contain values for non-primary key fields that are
+** to be modified, only those fields need to match the original values to
+** avoid the SQLITE_CHANGESET_DATA conflict-handler callback.
+**
+** If no row with matching primary key values is found in the database,
+** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
+** passed as the second argument.
+**
+** If the UPDATE operation is attempted, but SQLite returns
+** SQLITE_CONSTRAINT, the conflict-handler function is invoked with
+** [SQLITE_CHANGESET_CONSTRAINT] passed as the second argument.
+** This includes the case where the UPDATE operation is attempted after
+** an earlier call to the conflict handler function returned
+** [SQLITE_CHANGESET_REPLACE].
+** </dl>
+**
+** It is safe to execute SQL statements, including those that write to the
+** table that the callback related to, from within the xConflict callback.
+** This can be used to further customize the applications conflict
+** resolution strategy.
+**
+** All changes made by this function are enclosed in a savepoint transaction.
+** If any other error (aside from a constraint failure when attempting to
+** write to the target database) occurs, then the savepoint transaction is
+** rolled back, restoring the target database to its original state, and an
+** SQLite error code returned.
+*/
+int sqlite3changeset_apply(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+);
+
+/*
+** CAPI3REF: Constants Passed To The Conflict Handler
+**
+** Values that may be passed as the second argument to a conflict-handler.
+**
+** <dl>
+** <dt>SQLITE_CHANGESET_DATA<dd>
+** The conflict handler is invoked with CHANGESET_DATA as the second argument
+** when processing a DELETE or UPDATE change if a row with the required
+** PRIMARY KEY fields is present in the database, but one or more other
+** (non primary-key) fields modified by the update do not contain the
+** expected "before" values.
+**
+** The conflicting row, in this case, is the database row with the matching
+** primary key.
+**
+** <dt>SQLITE_CHANGESET_NOTFOUND<dd>
+** The conflict handler is invoked with CHANGESET_NOTFOUND as the second
+** argument when processing a DELETE or UPDATE change if a row with the
+** required PRIMARY KEY fields is not present in the database.
+**
+** There is no conflicting row in this case. The results of invoking the
+** sqlite3changeset_conflict() API are undefined.
+**
+** <dt>SQLITE_CHANGESET_CONFLICT<dd>
+** CHANGESET_CONFLICT is passed as the second argument to the conflict
+** handler while processing an INSERT change if the operation would result
+** in duplicate primary key values.
+**
+** The conflicting row in this case is the database row with the matching
+** primary key.
+**
+** <dt>SQLITE_CHANGESET_FOREIGN_KEY<dd>
+** If foreign key handling is enabled, and applying a changeset leaves the
+** database in a state containing foreign key violations, the conflict
+** handler is invoked with CHANGESET_FOREIGN_KEY as the second argument
+** exactly once before the changeset is committed. If the conflict handler
+** returns CHANGESET_OMIT, the changes, including those that caused the
+** foreign key constraint violation, are committed. Or, if it returns
+** CHANGESET_ABORT, the changeset is rolled back.
+**
+** No current or conflicting row information is provided. The only function
+** it is possible to call on the supplied sqlite3_changeset_iter handle
+** is sqlite3changeset_fk_conflicts().
+**
+** <dt>SQLITE_CHANGESET_CONSTRAINT<dd>
+** If any other constraint violation occurs while applying a change (i.e.
+** a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is
+** invoked with CHANGESET_CONSTRAINT as the second argument.
+**
+** There is no conflicting row in this case. The results of invoking the
+** sqlite3changeset_conflict() API are undefined.
+**
+** </dl>
+*/
+#define SQLITE_CHANGESET_DATA 1
+#define SQLITE_CHANGESET_NOTFOUND 2
+#define SQLITE_CHANGESET_CONFLICT 3
+#define SQLITE_CHANGESET_CONSTRAINT 4
+#define SQLITE_CHANGESET_FOREIGN_KEY 5
+
+/*
+** CAPI3REF: Constants Returned By The Conflict Handler
+**
+** A conflict handler callback must return one of the following three values.
+**
+** <dl>
+** <dt>SQLITE_CHANGESET_OMIT<dd>
+** If a conflict handler returns this value no special action is taken. The
+** change that caused the conflict is not applied. The session module
+** continues to the next change in the changeset.
+**
+** <dt>SQLITE_CHANGESET_REPLACE<dd>
+** This value may only be returned if the second argument to the conflict
+** handler was SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If this
+** is not the case, any changes applied so far are rolled back and the
+** call to sqlite3changeset_apply() returns SQLITE_MISUSE.
+**
+** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_DATA conflict
+** handler, then the conflicting row is either updated or deleted, depending
+** on the type of change.
+**
+** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_CONFLICT conflict
+** handler, then the conflicting row is removed from the database and a
+** second attempt to apply the change is made. If this second attempt fails,
+** the original row is restored to the database before continuing.
+**
+** <dt>SQLITE_CHANGESET_ABORT<dd>
+** If this value is returned, any changes applied so far are rolled back
+** and the call to sqlite3changeset_apply() returns SQLITE_ABORT.
+** </dl>
+*/
+#define SQLITE_CHANGESET_OMIT 0
+#define SQLITE_CHANGESET_REPLACE 1
+#define SQLITE_CHANGESET_ABORT 2
+
+/*
+** CAPI3REF: Streaming Versions of API functions.
+**
+** The six streaming API xxx_strm() functions serve similar purposes to the
+** corresponding non-streaming API functions:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+** <tr><th>Streaming function<th>Non-streaming equivalent</th>
+** <tr><td>sqlite3changeset_apply_str<td>[sqlite3changeset_apply]
+** <tr><td>sqlite3changeset_concat_str<td>[sqlite3changeset_concat]
+** <tr><td>sqlite3changeset_invert_str<td>[sqlite3changeset_invert]
+** <tr><td>sqlite3changeset_start_str<td>[sqlite3changeset_start]
+** <tr><td>sqlite3session_changeset_str<td>[sqlite3session_changeset]
+** <tr><td>sqlite3session_patchset_str<td>[sqlite3session_patchset]
+** </table>
+**
+** Non-streaming functions that accept changesets (or patchsets) as input
+** require that the entire changeset be stored in a single buffer in memory.
+** Similarly, those that return a changeset or patchset do so by returning
+** a pointer to a single large buffer allocated using sqlite3_malloc().
+** Normally this is convenient. However, if an application running in a
+** low-memory environment is required to handle very large changesets, the
+** large contiguous memory allocations required can become onerous.
+**
+** In order to avoid this problem, instead of a single large buffer, input
+** is passed to a streaming API functions by way of a callback function that
+** the sessions module invokes to incrementally request input data as it is
+** required. In all cases, a pair of API function parameters such as
+**
+** <pre>
+** &nbsp; int nChangeset,
+** &nbsp; void *pChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** &nbsp; int (*xInput)(void *pIn, void *pData, int *pnData),
+** &nbsp; void *pIn,
+** </pre>
+**
+** Each time the xInput callback is invoked by the sessions module, the first
+** argument passed is a copy of the supplied pIn context pointer. The second
+** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
+** error occurs the xInput method should copy up to (*pnData) bytes of data
+** into the buffer and set (*pnData) to the actual number of bytes copied
+** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
+** should be set to zero to indicate this. Or, if an error occurs, an SQLite
+** error code should be returned. In all cases, if an xInput callback returns
+** an error, all processing is abandoned and the streaming API function
+** returns a copy of the error code to the caller.
+**
+** In the case of sqlite3changeset_start_strm(), the xInput callback may be
+** invoked by the sessions module at any point during the lifetime of the
+** iterator. If such an xInput callback returns an error, the iterator enters
+** an error state, whereby all subsequent calls to iterator functions
+** immediately fail with the same error code as returned by xInput.
+**
+** Similarly, streaming API functions that return changesets (or patchsets)
+** return them in chunks by way of a callback function instead of via a
+** pointer to a single large buffer. In this case, a pair of parameters such
+** as:
+**
+** <pre>
+** &nbsp; int *pnChangeset,
+** &nbsp; void **ppChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** &nbsp; int (*xOutput)(void *pOut, const void *pData, int nData),
+** &nbsp; void *pOut
+** </pre>
+**
+** The xOutput callback is invoked zero or more times to return data to
+** the application. The first parameter passed to each call is a copy of the
+** pOut pointer supplied by the application. The second parameter, pData,
+** points to a buffer nData bytes in size containing the chunk of output
+** data being returned. If the xOutput callback successfully processes the
+** supplied data, it should return SQLITE_OK to indicate success. Otherwise,
+** it should return some other SQLite error code. In this case processing
+** is immediately abandoned and the streaming API function returns a copy
+** of the xOutput error code to the application.
+**
+** The sessions module never invokes an xOutput callback with the third
+** parameter set to a value less than or equal to zero. Other than this,
+** no guarantees are made as to the size of the chunks of data returned.
+*/
+int sqlite3changeset_apply_strm(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+);
+int sqlite3changeset_concat_strm(
+ int (*xInputA)(void *pIn, void *pData, int *pnData),
+ void *pInA,
+ int (*xInputB)(void *pIn, void *pData, int *pnData),
+ void *pInB,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_invert_strm(
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_start_strm(
+ sqlite3_changeset_iter **pp,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3session_changeset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3session_patchset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changegroup_add_strm(sqlite3_changegroup*,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3changegroup_output_strm(sqlite3_changegroup*,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#if 0
+}
+#endif
+
+#endif /* !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) */
+
+/******** End of sqlite3session.h *********/
+/******** Begin file fts5.h *********/
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** FTS5 may be extended with:
+**
+** * custom tokenizers, and
+** * custom auxiliary functions.
+*/
+
+
+#ifndef _FTS5_H
+#define _FTS5_H
+
+
+#if 0
+extern "C" {
+#endif
+
+/*************************************************************************
+** CUSTOM AUXILIARY FUNCTIONS
+**
+** Virtual table implementations may overload SQL functions by implementing
+** the sqlite3_module.xFindFunction() method.
+*/
+
+typedef struct Fts5ExtensionApi Fts5ExtensionApi;
+typedef struct Fts5Context Fts5Context;
+typedef struct Fts5PhraseIter Fts5PhraseIter;
+
+typedef void (*fts5_extension_function)(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+);
+
+struct Fts5PhraseIter {
+ const unsigned char *a;
+ const unsigned char *b;
+};
+
+/*
+** EXTENSION API FUNCTIONS
+**
+** xUserData(pFts):
+** Return a copy of the context pointer the extension function was
+** registered with.
+**
+** xColumnTotalSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the FTS5 table. Or, if iCol is
+** non-negative but less than the number of columns in the table, return
+** the total number of tokens in column iCol, considering all rows in
+** the FTS5 table.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** xColumnCount(pFts):
+** Return the number of columns in the table.
+**
+** xColumnSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the current row. Or, if iCol is
+** non-negative but less than the number of columns in the table, set
+** *pnToken to the number of tokens in column iCol of the current row.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** This function may be quite inefficient if used with an FTS5 table
+** created with the "columnsize=0" option.
+**
+** xColumnText:
+** This function attempts to retrieve the text of column iCol of the
+** current document. If successful, (*pz) is set to point to a buffer
+** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
+** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
+** if an error occurs, an SQLite error code is returned and the final values
+** of (*pz) and (*pn) are undefined.
+**
+** xPhraseCount:
+** Returns the number of phrases in the current query expression.
+**
+** xPhraseSize:
+** Returns the number of tokens in phrase iPhrase of the query. Phrases
+** are numbered starting from zero.
+**
+** xInstCount:
+** Set *pnInst to the total number of occurrences of all phrases within
+** the query within the current row. Return SQLITE_OK if successful, or
+** an error code (i.e. SQLITE_NOMEM) if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always returns 0.
+**
+** xInst:
+** Query for the details of phrase match iIdx within the current row.
+** Phrase matches are numbered starting from zero, so the iIdx argument
+** should be greater than or equal to zero and smaller than the value
+** output by xInstCount().
+**
+** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** to the column in which it occurs and *piOff the token offset of the
+** first token of the phrase. The exception is if the table was created
+** with the offsets=0 option specified. In this case *piOff is always
+** set to -1.
+**
+** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
+** if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
+**
+** xRowid:
+** Returns the rowid of the current row.
+**
+** xTokenize:
+** Tokenize text using the tokenizer belonging to the FTS5 table.
+**
+** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback):
+** This API function is used to query the FTS table for phrase iPhrase
+** of the current query. Specifically, a query equivalent to:
+**
+** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
+**
+** with $p set to a phrase equivalent to the phrase iPhrase of the
+** current query is executed. Any column filter that applies to
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
+** function may be used to access the properties of each matched row.
+** Invoking Api.xUserData() returns a copy of the pointer passed as
+** the third argument to pUserData.
+**
+** If the callback function returns any value other than SQLITE_OK, the
+** query is abandoned and the xQueryPhrase function returns immediately.
+** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
+** Otherwise, the error code is propagated upwards.
+**
+** If the query runs to completion without incident, SQLITE_OK is returned.
+** Or, if some error occurs before the query completes or is aborted by
+** the callback, an SQLite error code is returned.
+**
+**
+** xSetAuxdata(pFts5, pAux, xDelete)
+**
+** Save the pointer passed as the second argument as the extension functions
+** "auxiliary data". The pointer may then be retrieved by the current or any
+** future invocation of the same fts5 extension function made as part of
+** of the same MATCH query using the xGetAuxdata() API.
+**
+** Each extension function is allocated a single auxiliary data slot for
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
+** single auxiliary data context.
+**
+** If there is already an auxiliary data pointer when this function is
+** invoked, then it is replaced by the new pointer. If an xDelete callback
+** was specified along with the original pointer, it is invoked at this
+** point.
+**
+** The xDelete callback, if one is specified, is also invoked on the
+** auxiliary data pointer after the FTS5 query has finished.
+**
+** If an error (e.g. an OOM condition) occurs within this function, an
+** the auxiliary data is set to NULL and an error code returned. If the
+** xDelete parameter was not NULL, it is invoked on the auxiliary data
+** pointer before returning.
+**
+**
+** xGetAuxdata(pFts5, bClear)
+**
+** Returns the current auxiliary data pointer for the fts5 extension
+** function. See the xSetAuxdata() method for details.
+**
+** If the bClear argument is non-zero, then the auxiliary data is cleared
+** (set to NULL) before this function returns. In this case the xDelete,
+** if any, is not invoked.
+**
+**
+** xRowCount(pFts5, pnRow)
+**
+** This function is used to retrieve the total number of rows in the table.
+** In other words, the same value that would be returned by:
+**
+** SELECT count(*) FROM ftstable;
+**
+** xPhraseFirst()
+** This function is used, along with type Fts5PhraseIter and the xPhraseNext
+** method, to iterate through all instances of a single query phrase within
+** the current row. This is the same information as is accessible via the
+** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
+** to use, this API may be faster under some circumstances. To iterate
+** through instances of phrase iPhrase, use the following code:
+**
+** Fts5PhraseIter iter;
+** int iCol, iOff;
+** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
+** iCol>=0;
+** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
+** ){
+** // An instance of phrase iPhrase at offset iOff of column iCol
+** }
+**
+** The Fts5PhraseIter structure is defined above. Applications should not
+** modify this structure directly - it should only be used as shown above
+** with the xPhraseFirst() and xPhraseNext() API methods (and by
+** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always iterates
+** through an empty set (all calls to xPhraseFirst() set iCol to -1).
+**
+** xPhraseNext()
+** See xPhraseFirst above.
+**
+** xPhraseFirstColumn()
+** This function and xPhraseNextColumn() are similar to the xPhraseFirst()
+** and xPhraseNext() APIs described above. The difference is that instead
+** of iterating through all instances of a phrase in the current row, these
+** APIs are used to iterate through the set of columns in the current row
+** that contain one or more instances of a specified phrase. For example:
+**
+** Fts5PhraseIter iter;
+** int iCol;
+** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
+** iCol>=0;
+** pApi->xPhraseNextColumn(pFts, &iter, &iCol)
+** ){
+** // Column iCol contains at least one instance of phrase iPhrase
+** }
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
+** xPhraseFirstColumn() set iCol to -1).
+**
+** The information accessed using this API and its companion
+** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
+** (or xInst/xInstCount). The chief advantage of this API is that it is
+** significantly more efficient than those alternatives when used with
+** "detail=column" tables.
+**
+** xPhraseNextColumn()
+** See xPhraseFirstColumn above.
+*/
+struct Fts5ExtensionApi {
+ int iVersion; /* Currently always set to 3 */
+
+ void *(*xUserData)(Fts5Context*);
+
+ int (*xColumnCount)(Fts5Context*);
+ int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
+ int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
+
+ int (*xTokenize)(Fts5Context*,
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+ );
+
+ int (*xPhraseCount)(Fts5Context*);
+ int (*xPhraseSize)(Fts5Context*, int iPhrase);
+
+ int (*xInstCount)(Fts5Context*, int *pnInst);
+ int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
+
+ sqlite3_int64 (*xRowid)(Fts5Context*);
+ int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
+ int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
+
+ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
+ int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
+ );
+ int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
+ void *(*xGetAuxdata)(Fts5Context*, int bClear);
+
+ int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
+ void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
+
+ int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
+ void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+};
+
+/*
+** CUSTOM AUXILIARY FUNCTIONS
+*************************************************************************/
+
+/*************************************************************************
+** CUSTOM TOKENIZERS
+**
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
+** following structure. All structure methods must be defined, setting
+** any member of the fts5_tokenizer struct to NULL leads to undefined
+** behaviour. The structure methods are expected to function as follows:
+**
+** xCreate:
+** This function is used to allocate and initialize a tokenizer instance.
+** A tokenizer instance is required to actually tokenize text.
+**
+** The first argument passed to this function is a copy of the (void*)
+** pointer provided by the application when the fts5_tokenizer object
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** The second and third arguments are an array of nul-terminated strings
+** containing the tokenizer arguments, if any, specified following the
+** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
+** to create the FTS5 table.
+**
+** The final argument is an output variable. If successful, (*ppOut)
+** should be set to point to the new tokenizer handle and SQLITE_OK
+** returned. If an error occurs, some value other than SQLITE_OK should
+** be returned. In this case, fts5 assumes that the final value of *ppOut
+** is undefined.
+**
+** xDelete:
+** This function is invoked to delete a tokenizer handle previously
+** allocated using xCreate(). Fts5 guarantees that this function will
+** be invoked exactly once for each successful call to xCreate().
+**
+** xTokenize:
+** This function is expected to tokenize the nText byte string indicated
+** by argument pText. pText may or may not be nul-terminated. The first
+** argument passed to this function is a pointer to an Fts5Tokenizer object
+** returned by an earlier call to xCreate().
+**
+** The second argument indicates the reason that FTS5 is requesting
+** tokenization of the supplied text. This is always one of the following
+** four values:
+**
+** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
+** or removed from the FTS table. The tokenizer is being invoked to
+** determine the set of tokens to add to (or delete from) the
+** FTS index.
+**
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
+** a bareword or quoted string specified as part of the query.
+**
+** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
+** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
+** followed by a "*" character, indicating that the last token
+** returned by the tokenizer will be treated as a token prefix.
+**
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** satisfy an fts5_api.xTokenize() request made by an auxiliary
+** function. Or an fts5_api.xColumnSize() request made by the same
+** on a columnsize=0 database.
+** </ul>
+**
+** For each token in the input string, the supplied callback xToken() must
+** be invoked. The first argument to it should be a copy of the pointer
+** passed as the second argument to xTokenize(). The third and fourth
+** arguments are a pointer to a buffer containing the token text, and the
+** size of the token in bytes. The 4th and 5th arguments are the byte offsets
+** of the first byte of and first byte immediately following the text from
+** which the token is derived within the input.
+**
+** The second argument passed to the xToken() callback ("tflags") should
+** normally be set to 0. The exception is if the tokenizer supports
+** synonyms. In this case see the discussion below for details.
+**
+** FTS5 assumes the xToken() callback is invoked for each token in the
+** order that they occur within the input text.
+**
+** If an xToken() callback returns any value other than SQLITE_OK, then
+** the tokenization should be abandoned and the xTokenize() method should
+** immediately return a copy of the xToken() return value. Or, if the
+** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally,
+** if an error occurs with the xTokenize() implementation itself, it
+** may abandon the tokenization and return any error code other than
+** SQLITE_OK or SQLITE_DONE.
+**
+** SYNONYM SUPPORT
+**
+** Custom tokenizers may also support synonyms. Consider a case in which a
+** user wishes to query for a phrase such as "first place". Using the
+** built-in tokenizers, the FTS5 query 'first + place' will match instances
+** of "first place" within the document set, but not alternative forms
+** such as "1st place". In some applications, it would be better to match
+** all instances of "first place" or "1st place" regardless of which form
+** the user specified in the MATCH query text.
+**
+** There are several ways to approach this in FTS5:
+**
+** <ol><li> By mapping all synonyms to a single token. In this case, the
+** In the above example, this means that the tokenizer returns the
+** same token for inputs "first" and "1st". Say that token is in
+** fact "first", so that when the user inserts the document "I won
+** 1st place" entries are added to the index for tokens "i", "won",
+** "first" and "place". If the user then queries for '1st + place',
+** the tokenizer substitutes "first" for "1st" and the query works
+** as expected.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** In this case, when tokenizing query text, the tokenizer may
+** provide multiple synonyms for a single term within the document.
+** FTS5 then queries the index for each synonym individually. For
+** example, faced with the query:
+**
+** <codeblock>
+** ... MATCH 'first place'</codeblock>
+**
+** the tokenizer offers both "1st" and "first" as synonyms for the
+** first token in the MATCH query and FTS5 effectively runs a query
+** similar to:
+**
+** <codeblock>
+** ... MATCH '(first OR 1st) place'</codeblock>
+**
+** except that, for the purposes of auxiliary functions, the query
+** still appears to contain just two phrases - "(first OR 1st)"
+** being treated as a single phrase.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** Using this method, when tokenizing document text, the tokenizer
+** provides multiple synonyms for each token. So that when a
+** document such as "I won first place" is tokenized, entries are
+** added to the FTS index for "i", "won", "first", "1st" and
+** "place".
+**
+** This way, even if the tokenizer does not provide synonyms
+** when tokenizing query text (it should not - to do would be
+** inefficient), it doesn't matter if the user queries for
+** 'first + place' or '1st + place', as there are entires in the
+** FTS index corresponding to both forms of the first token.
+** </ol>
+**
+** Whether it is parsing document or query text, any call to xToken that
+** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
+** is considered to supply a synonym for the previous token. For example,
+** when parsing the document "I won first place", a tokenizer that supports
+** synonyms would call xToken() 5 times, as follows:
+**
+** <codeblock>
+** xToken(pCtx, 0, "i", 1, 0, 1);
+** xToken(pCtx, 0, "won", 3, 2, 5);
+** xToken(pCtx, 0, "first", 5, 6, 11);
+** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11);
+** xToken(pCtx, 0, "place", 5, 12, 17);
+**</codeblock>
+**
+** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
+** xToken() is called. Multiple synonyms may be specified for a single token
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** There is no limit to the number of synonyms that may be provided for a
+** single token.
+**
+** In many cases, method (1) above is the best approach. It does not add
+** extra data to the FTS index or require FTS5 to query for multiple terms,
+** so it is efficient in terms of disk space and query speed. However, it
+** does not support prefix queries very well. If, as suggested above, the
+** token "first" is subsituted for "1st" by the tokenizer, then the query:
+**
+** <codeblock>
+** ... MATCH '1s*'</codeblock>
+**
+** will not match documents that contain the token "1st" (as the tokenizer
+** will probably not map "1s" to any prefix of "first").
+**
+** For full prefix support, method (3) may be preferred. In this case,
+** because the index contains entries for both "first" and "1st", prefix
+** queries such as 'fi*' or '1s*' will match correctly. However, because
+** extra entries are added to the FTS index, this method uses more space
+** within the database.
+**
+** Method (2) offers a midpoint between (1) and (3). Using this method,
+** a query such as '1s*' will match documents that contain the literal
+** token "1st", but not "first" (assuming the tokenizer is not able to
+** provide synonyms for prefixes). However, a non-prefix query like '1st'
+** will match against "1st" and "first". This method does not require
+** extra disk space, as no extra entries are added to the FTS index.
+** On the other hand, it may require more CPU cycles to run MATCH queries,
+** as separate queries of the FTS index are required for each synonym.
+**
+** When using methods (2) or (3), it is important that the tokenizer only
+** provide synonyms when tokenizing document text (method (2)) or query
+** text (method (3)), not both. Doing so will not cause any errors, but is
+** inefficient.
+*/
+typedef struct Fts5Tokenizer Fts5Tokenizer;
+typedef struct fts5_tokenizer fts5_tokenizer;
+struct fts5_tokenizer {
+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
+ void (*xDelete)(Fts5Tokenizer*);
+ int (*xTokenize)(Fts5Tokenizer*,
+ void *pCtx,
+ int flags, /* Mask of FTS5_TOKENIZE_* flags */
+ const char *pText, int nText,
+ int (*xToken)(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Byte offset of token within input text */
+ int iEnd /* Byte offset of end of token within input text */
+ )
+ );
+};
+
+/* Flags that may be passed as the third argument to xTokenize() */
+#define FTS5_TOKENIZE_QUERY 0x0001
+#define FTS5_TOKENIZE_PREFIX 0x0002
+#define FTS5_TOKENIZE_DOCUMENT 0x0004
+#define FTS5_TOKENIZE_AUX 0x0008
+
+/* Flags that may be passed by the tokenizer implementation back to FTS5
+** as the third argument to the supplied xToken callback. */
+#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
+
+/*
+** END OF CUSTOM TOKENIZERS
+*************************************************************************/
+
+/*************************************************************************
+** FTS5 EXTENSION REGISTRATION API
+*/
+typedef struct fts5_api fts5_api;
+struct fts5_api {
+ int iVersion; /* Currently always set to 2 */
+
+ /* Create a new tokenizer */
+ int (*xCreateTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_tokenizer *pTokenizer,
+ void (*xDestroy)(void*)
+ );
+
+ /* Find an existing tokenizer */
+ int (*xFindTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void **ppContext,
+ fts5_tokenizer *pTokenizer
+ );
+
+ /* Create a new auxiliary function */
+ int (*xCreateFunction)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_extension_function xFunction,
+ void (*xDestroy)(void*)
+ );
+};
+
+/*
+** END OF REGISTRATION API
+*************************************************************************/
+
+#if 0
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _FTS5_H */
+
+/******** End of fts5.h *********/
/************** End of sqlite3.h *********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
+
+/*
+** Include the configuration header output by 'configure' if we're using the
+** autoconf-based build
+*/
+#ifdef _HAVE_SQLITE_CONFIG_H
+#include "config.h"
+#endif
+
+/************** Include sqliteLimit.h in the middle of sqliteInt.h ***********/
+/************** Begin file sqliteLimit.h *************************************/
+/*
+** 2007 May 7
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file defines various limits of what SQLite can process.
+*/
+
+/*
+** The maximum length of a TEXT or BLOB in bytes. This also
+** limits the size of a row in a table or index.
+**
+** The hard limit is the ability of a 32-bit signed integer
+** to count the size: 2^31-1 or 2147483647.
+*/
+#ifndef SQLITE_MAX_LENGTH
+# define SQLITE_MAX_LENGTH 1000000000
+#endif
+
+/*
+** This is the maximum number of
+**
+** * Columns in a table
+** * Columns in an index
+** * Columns in a view
+** * Terms in the SET clause of an UPDATE statement
+** * Terms in the result set of a SELECT statement
+** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
+** * Terms in the VALUES clause of an INSERT statement
+**
+** The hard upper limit here is 32676. Most database people will
+** tell you that in a well-normalized database, you usually should
+** not have more than a dozen or so columns in any table. And if
+** that is the case, there is no point in having more than a few
+** dozen values in any of the other situations described above.
+*/
+#ifndef SQLITE_MAX_COLUMN
+# define SQLITE_MAX_COLUMN 2000
+#endif
+
+/*
+** The maximum length of a single SQL statement in bytes.
+**
+** It used to be the case that setting this value to zero would
+** turn the limit off. That is no longer true. It is not possible
+** to turn this limit off.
+*/
+#ifndef SQLITE_MAX_SQL_LENGTH
+# define SQLITE_MAX_SQL_LENGTH 1000000000
+#endif
+
+/*
+** The maximum depth of an expression tree. This is limited to
+** some extent by SQLITE_MAX_SQL_LENGTH. But sometime you might
+** want to place more severe limits on the complexity of an
+** expression.
+**
+** A value of 0 used to mean that the limit was not enforced.
+** But that is no longer true. The limit is now strictly enforced
+** at all times.
+*/
+#ifndef SQLITE_MAX_EXPR_DEPTH
+# define SQLITE_MAX_EXPR_DEPTH 1000
+#endif
+
+/*
+** The maximum number of terms in a compound SELECT statement.
+** The code generator for compound SELECT statements does one
+** level of recursion for each term. A stack overflow can result
+** if the number of terms is too large. In practice, most SQL
+** never has more than 3 or 4 terms. Use a value of 0 to disable
+** any limit on the number of terms in a compount SELECT.
+*/
+#ifndef SQLITE_MAX_COMPOUND_SELECT
+# define SQLITE_MAX_COMPOUND_SELECT 500
+#endif
+
+/*
+** The maximum number of opcodes in a VDBE program.
+** Not currently enforced.
+*/
+#ifndef SQLITE_MAX_VDBE_OP
+# define SQLITE_MAX_VDBE_OP 25000
+#endif
+
+/*
+** The maximum number of arguments to an SQL function.
+*/
+#ifndef SQLITE_MAX_FUNCTION_ARG
+# define SQLITE_MAX_FUNCTION_ARG 127
+#endif
+
+/*
+** The suggested maximum number of in-memory pages to use for
+** the main database table and for temporary tables.
+**
+** IMPLEMENTATION-OF: R-30185-15359 The default suggested cache size is -2000,
+** which means the cache size is limited to 2048000 bytes of memory.
+** IMPLEMENTATION-OF: R-48205-43578 The default suggested cache size can be
+** altered using the SQLITE_DEFAULT_CACHE_SIZE compile-time options.
+*/
+#ifndef SQLITE_DEFAULT_CACHE_SIZE
+# define SQLITE_DEFAULT_CACHE_SIZE -2000
+#endif
+
+/*
+** The default number of frames to accumulate in the log file before
+** checkpointing the database in WAL mode.
+*/
+#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
+# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000
+#endif
+
+/*
+** The maximum number of attached databases. This must be between 0
+** and 125. The upper bound of 125 is because the attached databases are
+** counted using a signed 8-bit integer which has a maximum value of 127
+** and we have to allow 2 extra counts for the "main" and "temp" databases.
+*/
+#ifndef SQLITE_MAX_ATTACHED
+# define SQLITE_MAX_ATTACHED 10
+#endif
+
+
+/*
+** The maximum value of a ?nnn wildcard that the parser will accept.
+*/
+#ifndef SQLITE_MAX_VARIABLE_NUMBER
+# define SQLITE_MAX_VARIABLE_NUMBER 999
+#endif
+
+/* Maximum page size. The upper bound on this value is 65536. This a limit
+** imposed by the use of 16-bit offsets within each page.
+**
+** Earlier versions of SQLite allowed the user to change this value at
+** compile time. This is no longer permitted, on the grounds that it creates
+** a library that is technically incompatible with an SQLite library
+** compiled with a different limit. If a process operating on a database
+** with a page-size of 65536 bytes crashes, then an instance of SQLite
+** compiled with the default page-size limit will not be able to rollback
+** the aborted transaction. This could lead to database corruption.
+*/
+#ifdef SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_MAX_PAGE_SIZE
+#endif
+#define SQLITE_MAX_PAGE_SIZE 65536
+
+
+/*
+** The default size of a database page.
+*/
+#ifndef SQLITE_DEFAULT_PAGE_SIZE
+# define SQLITE_DEFAULT_PAGE_SIZE 4096
+#endif
+#if SQLITE_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_DEFAULT_PAGE_SIZE
+# define SQLITE_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
+#endif
+
+/*
+** Ordinarily, if no value is explicitly provided, SQLite creates databases
+** with page size SQLITE_DEFAULT_PAGE_SIZE. However, based on certain
+** device characteristics (sector-size and atomic write() support),
+** SQLite may choose a larger value. This constant is the maximum value
+** SQLite will choose on its own.
+*/
+#ifndef SQLITE_MAX_DEFAULT_PAGE_SIZE
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE 8192
+#endif
+#if SQLITE_MAX_DEFAULT_PAGE_SIZE>SQLITE_MAX_PAGE_SIZE
+# undef SQLITE_MAX_DEFAULT_PAGE_SIZE
+# define SQLITE_MAX_DEFAULT_PAGE_SIZE SQLITE_MAX_PAGE_SIZE
+#endif
+
+
+/*
+** Maximum number of pages in one database file.
+**
+** This is really just the default value for the max_page_count pragma.
+** This value can be lowered (or raised) at run-time using that the
+** max_page_count macro.
+*/
+#ifndef SQLITE_MAX_PAGE_COUNT
+# define SQLITE_MAX_PAGE_COUNT 1073741823
+#endif
+
+/*
+** Maximum length (in bytes) of the pattern in a LIKE or GLOB
+** operator.
+*/
+#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
+# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000
+#endif
+
+/*
+** Maximum depth of recursion for triggers.
+**
+** A value of 1 means that a trigger program will not be able to itself
+** fire any triggers. A value of 0 means that no trigger programs at all
+** may be executed.
+*/
+#ifndef SQLITE_MAX_TRIGGER_DEPTH
+# define SQLITE_MAX_TRIGGER_DEPTH 1000
+#endif
+
+/************** End of sqliteLimit.h *****************************************/
+/************** Continuing where we left off in sqliteInt.h ******************/
+
+/* Disable nuisance warnings on Borland compilers */
+#if defined(__BORLANDC__)
+#pragma warn -rch /* unreachable code */
+#pragma warn -ccc /* Condition is always true or false */
+#pragma warn -aus /* Assigned value is never used */
+#pragma warn -csu /* Comparing signed and unsigned */
+#pragma warn -spa /* Suspicious pointer arithmetic */
+#endif
+
+/*
+** Include standard header files as necessary
+*/
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+/*
+** The following macros are used to cast pointers to integers and
+** integers to pointers. The way you do this varies from one compiler
+** to the next, so we have developed the following set of #if statements
+** to generate appropriate macros for a wide range of compilers.
+**
+** The correct "ANSI" way to do this is to use the intptr_t type.
+** Unfortunately, that typedef is not available on all compilers, or
+** if it is available, it requires an #include of specific headers
+** that vary from one machine to the next.
+**
+** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
+** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
+** So we have to define the macros in different ways depending on the
+** compiler.
+*/
+#if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */
+# define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(__PTRDIFF_TYPE__)(X))
+#elif !defined(__GNUC__) /* Works for compilers other than LLVM */
+# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X])
+# define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0))
+#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */
+# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(intptr_t)(X))
+#else /* Generates a warning - but it always works */
+# define SQLITE_INT_TO_PTR(X) ((void*)(X))
+# define SQLITE_PTR_TO_INT(X) ((int)(X))
+#endif
+
+/*
+** A macro to hint to the compiler that a function should not be
+** inlined.
+*/
+#if defined(__GNUC__)
+# define SQLITE_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER) && _MSC_VER>=1310
+# define SQLITE_NOINLINE __declspec(noinline)
+#else
+# define SQLITE_NOINLINE
+#endif
+
+/*
+** Make sure that the compiler intrinsics we desire are enabled when
+** compiling with an appropriate version of MSVC unless prevented by
+** the SQLITE_DISABLE_INTRINSIC define.
+*/
+#if !defined(SQLITE_DISABLE_INTRINSIC)
+# if defined(_MSC_VER) && _MSC_VER>=1400
+# if !defined(_WIN32_WCE)
+# include <intrin.h>
+# pragma intrinsic(_byteswap_ushort)
+# pragma intrinsic(_byteswap_ulong)
+# pragma intrinsic(_ReadWriteBarrier)
+# else
+# include <cmnintrin.h>
+# endif
+# endif
+#endif
+
+/*
+** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
+** 0 means mutexes are permanently disable and the library is never
+** threadsafe. 1 means the library is serialized which is the highest
+** level of threadsafety. 2 means the library is multithreaded - multiple
+** threads can use SQLite as long as no two threads try to use the same
+** database connection at the same time.
+**
+** Older versions of SQLite used an optional THREADSAFE macro.
+** We support that for legacy.
+*/
+#if !defined(SQLITE_THREADSAFE)
+# if defined(THREADSAFE)
+# define SQLITE_THREADSAFE THREADSAFE
+# else
+# define SQLITE_THREADSAFE 1 /* IMP: R-07272-22309 */
+# endif
+#endif
+
+/*
+** Powersafe overwrite is on by default. But can be turned off using
+** the -DSQLITE_POWERSAFE_OVERWRITE=0 command-line option.
+*/
+#ifndef SQLITE_POWERSAFE_OVERWRITE
+# define SQLITE_POWERSAFE_OVERWRITE 1
+#endif
+
+/*
+** EVIDENCE-OF: R-25715-37072 Memory allocation statistics are enabled by
+** default unless SQLite is compiled with SQLITE_DEFAULT_MEMSTATUS=0 in
+** which case memory allocation statistics are disabled by default.
+*/
+#if !defined(SQLITE_DEFAULT_MEMSTATUS)
+# define SQLITE_DEFAULT_MEMSTATUS 1
+#endif
+
+/*
+** Exactly one of the following macros must be defined in order to
+** specify which memory allocation subsystem to use.
+**
+** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
+** SQLITE_WIN32_MALLOC // Use Win32 native heap API
+** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails
+** SQLITE_MEMDEBUG // Debugging version of system malloc()
+**
+** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
+** assert() macro is enabled, each call into the Win32 native heap subsystem
+** will cause HeapValidate to be called. If heap validation should fail, an
+** assertion will be triggered.
+**
+** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
+** the default.
+*/
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)>1
+# error "Two or more of the following compile-time configuration options\
+ are defined but at most one is allowed:\
+ SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\
+ SQLITE_ZERO_MALLOC"
+#endif
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)==0
+# define SQLITE_SYSTEM_MALLOC 1
+#endif
+
+/*
+** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the
+** sizes of memory allocations below this value where possible.
+*/
+#if !defined(SQLITE_MALLOC_SOFT_LIMIT)
+# define SQLITE_MALLOC_SOFT_LIMIT 1024
+#endif
+
+/*
+** We need to define _XOPEN_SOURCE as follows in order to enable
+** recursive mutexes on most Unix systems and fchmod() on OpenBSD.
+** But _XOPEN_SOURCE define causes problems for Mac OS X, so omit
+** it.
+*/
+#if !defined(_XOPEN_SOURCE) && !defined(__DARWIN__) && !defined(__APPLE__)
+# define _XOPEN_SOURCE 600
+#endif
+
+/*
+** NDEBUG and SQLITE_DEBUG are opposites. It should always be true that
+** defined(NDEBUG)==!defined(SQLITE_DEBUG). If this is not currently true,
+** make it true by defining or undefining NDEBUG.
+**
+** Setting NDEBUG makes the code smaller and faster by disabling the
+** assert() statements in the code. So we want the default action
+** to be for NDEBUG to be set and NDEBUG to be undefined only if SQLITE_DEBUG
+** is set. Thus NDEBUG becomes an opt-in rather than an opt-out
+** feature.
+*/
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
+#endif
+#if defined(NDEBUG) && defined(SQLITE_DEBUG)
+# undef NDEBUG
+#endif
+
+/*
+** Enable SQLITE_ENABLE_EXPLAIN_COMMENTS if SQLITE_DEBUG is turned on.
+*/
+#if !defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) && defined(SQLITE_DEBUG)
+# define SQLITE_ENABLE_EXPLAIN_COMMENTS 1
+#endif
+
+/*
+** The testcase() macro is used to aid in coverage testing. When
+** doing coverage testing, the condition inside the argument to
+** testcase() must be evaluated both true and false in order to
+** get full branch coverage. The testcase() macro is inserted
+** to help ensure adequate test coverage in places where simple
+** condition/decision coverage is inadequate. For example, testcase()
+** can be used to make sure boundary values are tested. For
+** bitmask tests, testcase() can be used to make sure each bit
+** is significant and used at least once. On switch statements
+** where multiple cases go to the same block of code, testcase()
+** can insure that all cases are evaluated.
+**
+*/
+#ifdef SQLITE_COVERAGE_TEST
+SQLITE_PRIVATE void sqlite3Coverage(int);
+# define testcase(X) if( X ){ sqlite3Coverage(__LINE__); }
+#else
+# define testcase(X)
+#endif
+
+/*
+** The TESTONLY macro is used to enclose variable declarations or
+** other bits of code that are needed to support the arguments
+** within testcase() and assert() macros.
+*/
+#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST)
+# define TESTONLY(X) X
+#else
+# define TESTONLY(X)
+#endif
+
+/*
+** Sometimes we need a small amount of code such as a variable initialization
+** to setup for a later assert() statement. We do not want this code to
+** appear when assert() is disabled. The following macro is therefore
+** used to contain that setup code. The "VVA" acronym stands for
+** "Verification, Validation, and Accreditation". In other words, the
+** code within VVA_ONLY() will only run during verification processes.
+*/
+#ifndef NDEBUG
+# define VVA_ONLY(X) X
+#else
+# define VVA_ONLY(X)
+#endif
+
+/*
+** The ALWAYS and NEVER macros surround boolean expressions which
+** are intended to always be true or false, respectively. Such
+** expressions could be omitted from the code completely. But they
+** are included in a few cases in order to enhance the resilience
+** of SQLite to unexpected behavior - to make the code "self-healing"
+** or "ductile" rather than being "brittle" and crashing at the first
+** hint of unplanned behavior.
+**
+** In other words, ALWAYS and NEVER are added for defensive code.
+**
+** When doing coverage testing ALWAYS and NEVER are hard-coded to
+** be true and false so that the unreachable code they specify will
+** not be counted as untested code.
+*/
+#if defined(SQLITE_COVERAGE_TEST) || defined(SQLITE_MUTATION_TEST)
+# define ALWAYS(X) (1)
+# define NEVER(X) (0)
+#elif !defined(NDEBUG)
+# define ALWAYS(X) ((X)?1:(assert(0),0))
+# define NEVER(X) ((X)?(assert(0),1):0)
+#else
+# define ALWAYS(X) (X)
+# define NEVER(X) (X)
+#endif
+
+/*
+** Some malloc failures are only possible if SQLITE_TEST_REALLOC_STRESS is
+** defined. We need to defend against those failures when testing with
+** SQLITE_TEST_REALLOC_STRESS, but we don't want the unreachable branches
+** during a normal build. The following macro can be used to disable tests
+** that are always false except when SQLITE_TEST_REALLOC_STRESS is set.
+*/
+#if defined(SQLITE_TEST_REALLOC_STRESS)
+# define ONLY_IF_REALLOC_STRESS(X) (X)
+#elif !defined(NDEBUG)
+# define ONLY_IF_REALLOC_STRESS(X) ((X)?(assert(0),1):0)
+#else
+# define ONLY_IF_REALLOC_STRESS(X) (0)
+#endif
+
+/*
+** Declarations used for tracing the operating system interfaces.
+*/
+#if defined(SQLITE_FORCE_OS_TRACE) || defined(SQLITE_TEST) || \
+ (defined(SQLITE_DEBUG) && SQLITE_OS_WIN)
+ extern int sqlite3OSTrace;
+# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
+# define SQLITE_HAVE_OS_TRACE
+#else
+# define OSTRACE(X)
+# undef SQLITE_HAVE_OS_TRACE
+#endif
+
+/*
+** Is the sqlite3ErrName() function needed in the build? Currently,
+** it is needed by "mutex_w32.c" (when debugging), "os_win.c" (when
+** OSTRACE is enabled), and by several "test*.c" files (which are
+** compiled using SQLITE_TEST).
+*/
+#if defined(SQLITE_HAVE_OS_TRACE) || defined(SQLITE_TEST) || \
+ (defined(SQLITE_DEBUG) && SQLITE_OS_WIN)
+# define SQLITE_NEED_ERR_NAME
+#else
+# undef SQLITE_NEED_ERR_NAME
+#endif
+
+/*
+** SQLITE_ENABLE_EXPLAIN_COMMENTS is incompatible with SQLITE_OMIT_EXPLAIN
+*/
+#ifdef SQLITE_OMIT_EXPLAIN
+# undef SQLITE_ENABLE_EXPLAIN_COMMENTS
+#endif
+
+/*
+** Return true (non-zero) if the input is an integer that is too large
+** to fit in 32-bits. This macro is used inside of various testcase()
+** macros to verify that we have tested SQLite for large-file support.
+*/
+#define IS_BIG_INT(X) (((X)&~(i64)0xffffffff)!=0)
+
+/*
+** The macro unlikely() is a hint that surrounds a boolean
+** expression that is usually false. Macro likely() surrounds
+** a boolean expression that is usually true. These hints could,
+** in theory, be used by the compiler to generate better code, but
+** currently they are just comments for human readers.
+*/
+#define likely(X) (X)
+#define unlikely(X) (X)
+
/************** Include hash.h in the middle of sqliteInt.h ******************/
/************** Begin file hash.h ********************************************/
/*
@@ -7324,11 +11199,11 @@ struct sqlite3_rtree_geometry {
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This is the header file for the generic hash-table implemenation
+** This is the header file for the generic hash-table implementation
** used in SQLite.
*/
-#ifndef _SQLITE_HASH_H_
-#define _SQLITE_HASH_H_
+#ifndef SQLITE_HASH_H
+#define SQLITE_HASH_H
/* Forward declarations of structures. */
typedef struct Hash Hash;
@@ -7374,15 +11249,15 @@ struct Hash {
struct HashElem {
HashElem *next, *prev; /* Next and previous elements in the table */
void *data; /* Data associated with this element */
- const char *pKey; int nKey; /* Key associated with this element */
+ const char *pKey; /* Key associated with this element */
};
/*
** Access routines. To delete, insert a NULL pointer.
*/
SQLITE_PRIVATE void sqlite3HashInit(Hash*);
-SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const char *pKey, int nKey, void *pData);
-SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const char *pKey, int nKey);
+SQLITE_PRIVATE void *sqlite3HashInsert(Hash*, const char *pKey, void *pData);
+SQLITE_PRIVATE void *sqlite3HashFind(const Hash*, const char *pKey);
SQLITE_PRIVATE void sqlite3HashClear(Hash*);
/*
@@ -7408,169 +11283,182 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
*/
/* #define sqliteHashCount(H) ((H)->count) // NOT USED */
-#endif /* _SQLITE_HASH_H_ */
+#endif /* SQLITE_HASH_H */
/************** End of hash.h ************************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include parse.h in the middle of sqliteInt.h *****************/
/************** Begin file parse.h *******************************************/
-#define TK_SEMI 1
-#define TK_EXPLAIN 2
-#define TK_QUERY 3
-#define TK_PLAN 4
-#define TK_BEGIN 5
-#define TK_TRANSACTION 6
-#define TK_DEFERRED 7
-#define TK_IMMEDIATE 8
-#define TK_EXCLUSIVE 9
-#define TK_COMMIT 10
-#define TK_END 11
-#define TK_ROLLBACK 12
-#define TK_SAVEPOINT 13
-#define TK_RELEASE 14
-#define TK_TO 15
-#define TK_TABLE 16
-#define TK_CREATE 17
-#define TK_IF 18
-#define TK_NOT 19
-#define TK_EXISTS 20
-#define TK_TEMP 21
-#define TK_LP 22
-#define TK_RP 23
-#define TK_AS 24
-#define TK_COMMA 25
-#define TK_ID 26
-#define TK_INDEXED 27
-#define TK_ABORT 28
-#define TK_ACTION 29
-#define TK_AFTER 30
-#define TK_ANALYZE 31
-#define TK_ASC 32
-#define TK_ATTACH 33
-#define TK_BEFORE 34
-#define TK_BY 35
-#define TK_CASCADE 36
-#define TK_CAST 37
-#define TK_COLUMNKW 38
-#define TK_CONFLICT 39
-#define TK_DATABASE 40
-#define TK_DESC 41
-#define TK_DETACH 42
-#define TK_EACH 43
-#define TK_FAIL 44
-#define TK_FOR 45
-#define TK_IGNORE 46
-#define TK_INITIALLY 47
-#define TK_INSTEAD 48
-#define TK_LIKE_KW 49
-#define TK_MATCH 50
-#define TK_NO 51
-#define TK_KEY 52
-#define TK_OF 53
-#define TK_OFFSET 54
-#define TK_PRAGMA 55
-#define TK_RAISE 56
-#define TK_REPLACE 57
-#define TK_RESTRICT 58
-#define TK_ROW 59
-#define TK_TRIGGER 60
-#define TK_VACUUM 61
-#define TK_VIEW 62
-#define TK_VIRTUAL 63
-#define TK_REINDEX 64
-#define TK_RENAME 65
-#define TK_CTIME_KW 66
-#define TK_ANY 67
-#define TK_OR 68
-#define TK_AND 69
-#define TK_IS 70
-#define TK_BETWEEN 71
-#define TK_IN 72
-#define TK_ISNULL 73
-#define TK_NOTNULL 74
-#define TK_NE 75
-#define TK_EQ 76
-#define TK_GT 77
-#define TK_LE 78
-#define TK_LT 79
-#define TK_GE 80
-#define TK_ESCAPE 81
-#define TK_BITAND 82
-#define TK_BITOR 83
-#define TK_LSHIFT 84
-#define TK_RSHIFT 85
-#define TK_PLUS 86
-#define TK_MINUS 87
-#define TK_STAR 88
-#define TK_SLASH 89
-#define TK_REM 90
-#define TK_CONCAT 91
-#define TK_COLLATE 92
-#define TK_BITNOT 93
-#define TK_STRING 94
-#define TK_JOIN_KW 95
-#define TK_CONSTRAINT 96
-#define TK_DEFAULT 97
-#define TK_NULL 98
-#define TK_PRIMARY 99
-#define TK_UNIQUE 100
-#define TK_CHECK 101
-#define TK_REFERENCES 102
-#define TK_AUTOINCR 103
-#define TK_ON 104
-#define TK_INSERT 105
-#define TK_DELETE 106
-#define TK_UPDATE 107
-#define TK_SET 108
-#define TK_DEFERRABLE 109
-#define TK_FOREIGN 110
-#define TK_DROP 111
-#define TK_UNION 112
-#define TK_ALL 113
-#define TK_EXCEPT 114
-#define TK_INTERSECT 115
-#define TK_SELECT 116
-#define TK_DISTINCT 117
-#define TK_DOT 118
-#define TK_FROM 119
-#define TK_JOIN 120
-#define TK_USING 121
-#define TK_ORDER 122
-#define TK_GROUP 123
-#define TK_HAVING 124
-#define TK_LIMIT 125
-#define TK_WHERE 126
-#define TK_INTO 127
-#define TK_VALUES 128
-#define TK_INTEGER 129
-#define TK_FLOAT 130
-#define TK_BLOB 131
-#define TK_REGISTER 132
-#define TK_VARIABLE 133
-#define TK_CASE 134
-#define TK_WHEN 135
-#define TK_THEN 136
-#define TK_ELSE 137
-#define TK_INDEX 138
-#define TK_ALTER 139
-#define TK_ADD 140
-#define TK_TO_TEXT 141
-#define TK_TO_BLOB 142
-#define TK_TO_NUMERIC 143
-#define TK_TO_INT 144
-#define TK_TO_REAL 145
-#define TK_ISNOT 146
-#define TK_END_OF_FILE 147
-#define TK_ILLEGAL 148
-#define TK_SPACE 149
+#define TK_SEMI 1
+#define TK_EXPLAIN 2
+#define TK_QUERY 3
+#define TK_PLAN 4
+#define TK_BEGIN 5
+#define TK_TRANSACTION 6
+#define TK_DEFERRED 7
+#define TK_IMMEDIATE 8
+#define TK_EXCLUSIVE 9
+#define TK_COMMIT 10
+#define TK_END 11
+#define TK_ROLLBACK 12
+#define TK_SAVEPOINT 13
+#define TK_RELEASE 14
+#define TK_TO 15
+#define TK_TABLE 16
+#define TK_CREATE 17
+#define TK_IF 18
+#define TK_NOT 19
+#define TK_EXISTS 20
+#define TK_TEMP 21
+#define TK_LP 22
+#define TK_RP 23
+#define TK_AS 24
+#define TK_WITHOUT 25
+#define TK_COMMA 26
+#define TK_OR 27
+#define TK_AND 28
+#define TK_IS 29
+#define TK_MATCH 30
+#define TK_LIKE_KW 31
+#define TK_BETWEEN 32
+#define TK_IN 33
+#define TK_ISNULL 34
+#define TK_NOTNULL 35
+#define TK_NE 36
+#define TK_EQ 37
+#define TK_GT 38
+#define TK_LE 39
+#define TK_LT 40
+#define TK_GE 41
+#define TK_ESCAPE 42
+#define TK_BITAND 43
+#define TK_BITOR 44
+#define TK_LSHIFT 45
+#define TK_RSHIFT 46
+#define TK_PLUS 47
+#define TK_MINUS 48
+#define TK_STAR 49
+#define TK_SLASH 50
+#define TK_REM 51
+#define TK_CONCAT 52
+#define TK_COLLATE 53
+#define TK_BITNOT 54
+#define TK_ID 55
+#define TK_INDEXED 56
+#define TK_ABORT 57
+#define TK_ACTION 58
+#define TK_AFTER 59
+#define TK_ANALYZE 60
+#define TK_ASC 61
+#define TK_ATTACH 62
+#define TK_BEFORE 63
+#define TK_BY 64
+#define TK_CASCADE 65
+#define TK_CAST 66
+#define TK_COLUMNKW 67
+#define TK_CONFLICT 68
+#define TK_DATABASE 69
+#define TK_DESC 70
+#define TK_DETACH 71
+#define TK_EACH 72
+#define TK_FAIL 73
+#define TK_FOR 74
+#define TK_IGNORE 75
+#define TK_INITIALLY 76
+#define TK_INSTEAD 77
+#define TK_NO 78
+#define TK_KEY 79
+#define TK_OF 80
+#define TK_OFFSET 81
+#define TK_PRAGMA 82
+#define TK_RAISE 83
+#define TK_RECURSIVE 84
+#define TK_REPLACE 85
+#define TK_RESTRICT 86
+#define TK_ROW 87
+#define TK_TRIGGER 88
+#define TK_VACUUM 89
+#define TK_VIEW 90
+#define TK_VIRTUAL 91
+#define TK_WITH 92
+#define TK_REINDEX 93
+#define TK_RENAME 94
+#define TK_CTIME_KW 95
+#define TK_ANY 96
+#define TK_STRING 97
+#define TK_JOIN_KW 98
+#define TK_CONSTRAINT 99
+#define TK_DEFAULT 100
+#define TK_NULL 101
+#define TK_PRIMARY 102
+#define TK_UNIQUE 103
+#define TK_CHECK 104
+#define TK_REFERENCES 105
+#define TK_AUTOINCR 106
+#define TK_ON 107
+#define TK_INSERT 108
+#define TK_DELETE 109
+#define TK_UPDATE 110
+#define TK_SET 111
+#define TK_DEFERRABLE 112
+#define TK_FOREIGN 113
+#define TK_DROP 114
+#define TK_UNION 115
+#define TK_ALL 116
+#define TK_EXCEPT 117
+#define TK_INTERSECT 118
+#define TK_SELECT 119
+#define TK_VALUES 120
+#define TK_DISTINCT 121
+#define TK_DOT 122
+#define TK_FROM 123
+#define TK_JOIN 124
+#define TK_USING 125
+#define TK_ORDER 126
+#define TK_GROUP 127
+#define TK_HAVING 128
+#define TK_LIMIT 129
+#define TK_WHERE 130
+#define TK_INTO 131
+#define TK_FLOAT 132
+#define TK_BLOB 133
+#define TK_INTEGER 134
+#define TK_VARIABLE 135
+#define TK_CASE 136
+#define TK_WHEN 137
+#define TK_THEN 138
+#define TK_ELSE 139
+#define TK_INDEX 140
+#define TK_ALTER 141
+#define TK_ADD 142
+#define TK_TO_TEXT 143
+#define TK_TO_BLOB 144
+#define TK_TO_NUMERIC 145
+#define TK_TO_INT 146
+#define TK_TO_REAL 147
+#define TK_ISNOT 148
+#define TK_END_OF_FILE 149
#define TK_UNCLOSED_STRING 150
#define TK_FUNCTION 151
#define TK_COLUMN 152
#define TK_AGG_FUNCTION 153
#define TK_AGG_COLUMN 154
-#define TK_CONST_FUNC 155
-#define TK_UMINUS 156
-#define TK_UPLUS 157
+#define TK_UMINUS 155
+#define TK_UPLUS 156
+#define TK_REGISTER 157
+#define TK_VECTOR 158
+#define TK_SELECT_COLUMN 159
+#define TK_ASTERISK 160
+#define TK_SPAN 161
+#define TK_SPACE 162
+#define TK_ILLEGAL 163
+
+/* The token codes above must all fit in 8 bits */
+#define TKFLG_MASK 0xff
+
+/* Flags that can be added to a token code when it is not
+** being stored in a u8: */
+#define TKFLG_DONTFOLD 0x100 /* Omit constant folding optimizations */
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -7602,7 +11490,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
/*
** OMIT_TEMPDB is set to 1 if SQLITE_OMIT_TEMPDB is defined, or 0
-** afterward. Having this macro allows us to cause the C compiler
+** afterward. Having this macro allows us to cause the C compiler
** to omit code used by TEMP tables without messy #ifndef statements.
*/
#ifdef SQLITE_OMIT_TEMPDB
@@ -7619,7 +11507,7 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
*/
#define SQLITE_MAX_FILE_FORMAT 4
#ifndef SQLITE_DEFAULT_FILE_FORMAT
-# define SQLITE_DEFAULT_FILE_FORMAT 1
+# define SQLITE_DEFAULT_FILE_FORMAT 4
#endif
/*
@@ -7636,6 +11524,37 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
*/
#ifndef SQLITE_TEMP_STORE
# define SQLITE_TEMP_STORE 1
+# define SQLITE_TEMP_STORE_xc 1 /* Exclude from ctime.c */
+#endif
+
+/*
+** If no value has been provided for SQLITE_MAX_WORKER_THREADS, or if
+** SQLITE_TEMP_STORE is set to 3 (never use temporary files), set it
+** to zero.
+*/
+#if SQLITE_TEMP_STORE==3 || SQLITE_THREADSAFE==0
+# undef SQLITE_MAX_WORKER_THREADS
+# define SQLITE_MAX_WORKER_THREADS 0
+#endif
+#ifndef SQLITE_MAX_WORKER_THREADS
+# define SQLITE_MAX_WORKER_THREADS 8
+#endif
+#ifndef SQLITE_DEFAULT_WORKER_THREADS
+# define SQLITE_DEFAULT_WORKER_THREADS 0
+#endif
+#if SQLITE_DEFAULT_WORKER_THREADS>SQLITE_MAX_WORKER_THREADS
+# undef SQLITE_MAX_WORKER_THREADS
+# define SQLITE_MAX_WORKER_THREADS SQLITE_DEFAULT_WORKER_THREADS
+#endif
+
+/*
+** The default initial allocation for the pagecache when using separate
+** pagecaches for each database connection. A positive number is the
+** number of pages. A negative number N translations means that a buffer
+** of -1024*N bytes is allocated and used for as many pages as it will hold.
+*/
+#ifndef SQLITE_DEFAULT_PCACHE_INITSZ
+# define SQLITE_DEFAULT_PCACHE_INITSZ 100
#endif
/*
@@ -7647,6 +11566,21 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#endif
/*
+** Macros to compute minimum and maximum of two numbers.
+*/
+#ifndef MIN
+# define MIN(A,B) ((A)<(B)?(A):(B))
+#endif
+#ifndef MAX
+# define MAX(A,B) ((A)>(B)?(A):(B))
+#endif
+
+/*
+** Swap two objects of type TYPE.
+*/
+#define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
+
+/*
** Check to see if this machine uses EBCDIC. (Yes, believe it or
** not, there are still machines out there that use EBCDIC.)
*/
@@ -7718,23 +11652,112 @@ typedef INT8_TYPE i8; /* 1-byte signed integer */
#define SQLITE_MAX_U32 ((((u64)1)<<32)-1)
/*
-** Macros to determine whether the machine is big or little endian,
-** evaluated at runtime.
+** The datatype used to store estimates of the number of rows in a
+** table or index. This is an unsigned integer type. For 99.9% of
+** the world, a 32-bit integer is sufficient. But a 64-bit integer
+** can be used at compile-time if desired.
*/
-#ifdef SQLITE_AMALGAMATION
-SQLITE_PRIVATE const int sqlite3one = 1;
+#ifdef SQLITE_64BIT_STATS
+ typedef u64 tRowcnt; /* 64-bit only if requested at compile-time */
+#else
+ typedef u32 tRowcnt; /* 32-bit is the default */
+#endif
+
+/*
+** Estimated quantities used for query planning are stored as 16-bit
+** logarithms. For quantity X, the value stored is 10*log2(X). This
+** gives a possible range of values of approximately 1.0e986 to 1e-986.
+** But the allowed values are "grainy". Not every value is representable.
+** For example, quantities 16 and 17 are both represented by a LogEst
+** of 40. However, since LogEst quantities are suppose to be estimates,
+** not exact values, this imprecision is not a problem.
+**
+** "LogEst" is short for "Logarithmic Estimate".
+**
+** Examples:
+** 1 -> 0 20 -> 43 10000 -> 132
+** 2 -> 10 25 -> 46 25000 -> 146
+** 3 -> 16 100 -> 66 1000000 -> 199
+** 4 -> 20 1000 -> 99 1048576 -> 200
+** 10 -> 33 1024 -> 100 4294967296 -> 320
+**
+** The LogEst can be negative to indicate fractional values.
+** Examples:
+**
+** 0.5 -> -10 0.1 -> -33 0.0625 -> -40
+*/
+typedef INT16_TYPE LogEst;
+
+/*
+** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer
+*/
+#ifndef SQLITE_PTRSIZE
+# if defined(__SIZEOF_POINTER__)
+# define SQLITE_PTRSIZE __SIZEOF_POINTER__
+# elif defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+ defined(_M_ARM) || defined(__arm__) || defined(__x86)
+# define SQLITE_PTRSIZE 4
+# else
+# define SQLITE_PTRSIZE 8
+# endif
+#endif
+
+/* The uptr type is an unsigned integer large enough to hold a pointer
+*/
+#if defined(HAVE_STDINT_H)
+ typedef uintptr_t uptr;
+#elif SQLITE_PTRSIZE==4
+ typedef u32 uptr;
#else
-SQLITE_PRIVATE const int sqlite3one;
+ typedef u64 uptr;
#endif
-#if defined(i386) || defined(__i386__) || defined(_M_IX86)\
- || defined(__x86_64) || defined(__x86_64__)
+
+/*
+** The SQLITE_WITHIN(P,S,E) macro checks to see if pointer P points to
+** something between S (inclusive) and E (exclusive).
+**
+** In other words, S is a buffer and E is a pointer to the first byte after
+** the end of buffer S. This macro returns true if P points to something
+** contained within the buffer S.
+*/
+#define SQLITE_WITHIN(P,S,E) (((uptr)(P)>=(uptr)(S))&&((uptr)(P)<(uptr)(E)))
+
+
+/*
+** Macros to determine whether the machine is big or little endian,
+** and whether or not that determination is run-time or compile-time.
+**
+** For best performance, an attempt is made to guess at the byte-order
+** using C-preprocessor macros. If that is unsuccessful, or if
+** -DSQLITE_RUNTIME_BYTEORDER=1 is set, then byte-order is determined
+** at run-time.
+*/
+#if (defined(i386) || defined(__i386__) || defined(_M_IX86) || \
+ defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \
+ defined(_M_AMD64) || defined(_M_ARM) || defined(__x86) || \
+ defined(__arm__)) && !defined(SQLITE_RUNTIME_BYTEORDER)
+# define SQLITE_BYTEORDER 1234
# define SQLITE_BIGENDIAN 0
# define SQLITE_LITTLEENDIAN 1
# define SQLITE_UTF16NATIVE SQLITE_UTF16LE
-#else
+#endif
+#if (defined(sparc) || defined(__ppc__)) \
+ && !defined(SQLITE_RUNTIME_BYTEORDER)
+# define SQLITE_BYTEORDER 4321
+# define SQLITE_BIGENDIAN 1
+# define SQLITE_LITTLEENDIAN 0
+# define SQLITE_UTF16NATIVE SQLITE_UTF16BE
+#endif
+#if !defined(SQLITE_BYTEORDER)
+# ifdef SQLITE_AMALGAMATION
+ const int sqlite3one = 1;
+# else
+ extern const int sqlite3one;
+# endif
+# define SQLITE_BYTEORDER 0 /* 0 means "unknown at compile-time" */
# define SQLITE_BIGENDIAN (*(char *)(&sqlite3one)==0)
# define SQLITE_LITTLEENDIAN (*(char *)(&sqlite3one)==1)
-# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE)
+# define SQLITE_UTF16NATIVE (SQLITE_BIGENDIAN?SQLITE_UTF16BE:SQLITE_UTF16LE)
#endif
/*
@@ -7745,7 +11768,7 @@ SQLITE_PRIVATE const int sqlite3one;
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
-/*
+/*
** Round up a number to the next larger multiple of 8. This is used
** to force 8-byte alignment on 64-bit architectures.
*/
@@ -7762,7 +11785,7 @@ SQLITE_PRIVATE const int sqlite3one;
** all alignment restrictions correct.
**
** Except, if SQLITE_4_BYTE_ALIGNED_MALLOC is defined, then the
-** underlying malloc() implemention might return us 4-byte aligned
+** underlying malloc() implementation might return us 4-byte aligned
** pointers. In that case, only verify 4-byte alignment.
*/
#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
@@ -7771,10 +11794,75 @@ SQLITE_PRIVATE const int sqlite3one;
# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0)
#endif
+/*
+** Disable MMAP on platforms where it is known to not work
+*/
+#if defined(__OpenBSD__) || defined(__QNXNTO__)
+# undef SQLITE_MAX_MMAP_SIZE
+# define SQLITE_MAX_MMAP_SIZE 0
+#endif
+
+/*
+** Default maximum size of memory used by memory-mapped I/O in the VFS
+*/
+#ifdef __APPLE__
+# include <TargetConditionals.h>
+#endif
+#ifndef SQLITE_MAX_MMAP_SIZE
+# if defined(__linux__) \
+ || defined(_WIN32) \
+ || (defined(__APPLE__) && defined(__MACH__)) \
+ || defined(__sun) \
+ || defined(__FreeBSD__) \
+ || defined(__DragonFly__)
+# define SQLITE_MAX_MMAP_SIZE 0x7fff0000 /* 2147418112 */
+# else
+# define SQLITE_MAX_MMAP_SIZE 0
+# endif
+# define SQLITE_MAX_MMAP_SIZE_xc 1 /* exclude from ctime.c */
+#endif
+
+/*
+** The default MMAP_SIZE is zero on all platforms. Or, even if a larger
+** default MMAP_SIZE is specified at compile-time, make sure that it does
+** not exceed the maximum mmap size.
+*/
+#ifndef SQLITE_DEFAULT_MMAP_SIZE
+# define SQLITE_DEFAULT_MMAP_SIZE 0
+# define SQLITE_DEFAULT_MMAP_SIZE_xc 1 /* Exclude from ctime.c */
+#endif
+#if SQLITE_DEFAULT_MMAP_SIZE>SQLITE_MAX_MMAP_SIZE
+# undef SQLITE_DEFAULT_MMAP_SIZE
+# define SQLITE_DEFAULT_MMAP_SIZE SQLITE_MAX_MMAP_SIZE
+#endif
+
+/*
+** Only one of SQLITE_ENABLE_STAT3 or SQLITE_ENABLE_STAT4 can be defined.
+** Priority is given to SQLITE_ENABLE_STAT4. If either are defined, also
+** define SQLITE_ENABLE_STAT3_OR_STAT4
+*/
+#ifdef SQLITE_ENABLE_STAT4
+# undef SQLITE_ENABLE_STAT3
+# define SQLITE_ENABLE_STAT3_OR_STAT4 1
+#elif SQLITE_ENABLE_STAT3
+# define SQLITE_ENABLE_STAT3_OR_STAT4 1
+#elif SQLITE_ENABLE_STAT3_OR_STAT4
+# undef SQLITE_ENABLE_STAT3_OR_STAT4
+#endif
+
+/*
+** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not
+** the Select query generator tracing logic is turned on.
+*/
+#if defined(SQLITE_DEBUG) || defined(SQLITE_ENABLE_SELECTTRACE)
+# define SELECTTRACE_ENABLED 1
+#else
+# define SELECTTRACE_ENABLED 0
+#endif
/*
** An instance of the following structure is used to store the busy-handler
-** callback for a given sqlite handle.
+** callback for a given sqlite handle.
**
** The sqlite.busyHandler member of the sqlite struct contains the busy
** callback for the database handle. Each pager opened via the sqlite
@@ -7813,10 +11901,19 @@ struct BusyHandler {
#define ArraySize(X) ((int)(sizeof(X)/sizeof(X[0])))
/*
+** Determine if the argument is a power of two
+*/
+#define IsPowerOfTwo(X) (((X)&((X)-1))==0)
+
+/*
** The following value as a destructor means to use sqlite3DbFree().
-** This is an internal extension to SQLITE_STATIC and SQLITE_TRANSIENT.
+** The sqlite3DbFree() routine requires two parameters instead of the
+** one parameter that destructors normally want. So we have to introduce
+** this magic value that the code knows to handle differently. Any
+** pointer will work here as long as it is distinct from SQLITE_STATIC
+** and SQLITE_TRANSIENT.
*/
-#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3DbFree)
+#define SQLITE_DYNAMIC ((sqlite3_destructor_type)sqlite3MallocSize)
/*
** When SQLITE_OMIT_WSD is defined, it means that the target platform does
@@ -7836,19 +11933,19 @@ struct BusyHandler {
#define SQLITE_WSD const
#define GLOBAL(t,v) (*(t*)sqlite3_wsd_find((void*)&(v), sizeof(v)))
#define sqlite3GlobalConfig GLOBAL(struct Sqlite3Config, sqlite3Config)
-SQLITE_API int sqlite3_wsd_init(int N, int J);
-SQLITE_API void *sqlite3_wsd_find(void *K, int L);
+SQLITE_API int sqlite3_wsd_init(int N, int J);
+SQLITE_API void *sqlite3_wsd_find(void *K, int L);
#else
- #define SQLITE_WSD
+ #define SQLITE_WSD
#define GLOBAL(t,v) v
#define sqlite3GlobalConfig sqlite3Config
#endif
/*
** The following macros are used to suppress compiler warnings and to
-** make it clear to human readers when a function parameter is deliberately
+** make it clear to human readers when a function parameter is deliberately
** left unused within the body of a function. This usually happens when
-** a function is called via a function pointer. For example the
+** a function is called via a function pointer. For example the
** implementation of an SQL aggregate step callback may not use the
** parameter indicating the number of arguments passed to the aggregate,
** if it knows that this is enforced elsewhere.
@@ -7891,14 +11988,19 @@ typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
+typedef struct PreUpdate PreUpdate;
+typedef struct PrintfArguments PrintfArguments;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
+typedef struct SQLiteThread SQLiteThread;
+typedef struct SelectDest SelectDest;
typedef struct SrcList SrcList;
typedef struct StrAccum StrAccum;
typedef struct Table Table;
typedef struct TableLock TableLock;
typedef struct Token Token;
+typedef struct TreeView TreeView;
typedef struct Trigger Trigger;
typedef struct TriggerPrg TriggerPrg;
typedef struct TriggerStep TriggerStep;
@@ -7906,12 +12008,11 @@ typedef struct UnpackedRecord UnpackedRecord;
typedef struct VTable VTable;
typedef struct VtabCtx VtabCtx;
typedef struct Walker Walker;
-typedef struct WherePlan WherePlan;
typedef struct WhereInfo WhereInfo;
-typedef struct WhereLevel WhereLevel;
+typedef struct With With;
/*
-** Defer sourcing vdbe.h and btree.h until after the "u8" and
+** Defer sourcing vdbe.h and btree.h until after the "u8" and
** "BusyHandler" typedefs. vdbe.h also requires a few of the opaque
** pointer types (i.e. FuncDef) defined above.
*/
@@ -7932,13 +12033,13 @@ typedef struct WhereLevel WhereLevel;
** subsystem. See comments in the source code for a detailed description
** of what each interface routine does.
*/
-#ifndef _BTREE_H_
-#define _BTREE_H_
+#ifndef SQLITE_BTREE_H
+#define SQLITE_BTREE_H
/* TODO: This definition is just included so other modules compile. It
** needs to be revisited.
*/
-#define SQLITE_N_BTREE_META 10
+#define SQLITE_N_BTREE_META 16
/*
** If defined as non-zero, auto-vacuum is enabled by default. Otherwise
@@ -7958,6 +12059,7 @@ typedef struct WhereLevel WhereLevel;
typedef struct Btree Btree;
typedef struct BtCursor BtCursor;
typedef struct BtShared BtShared;
+typedef struct BtreePayload BtreePayload;
SQLITE_PRIVATE int sqlite3BtreeOpen(
@@ -7976,28 +12078,31 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
** pager.h.
*/
#define BTREE_OMIT_JOURNAL 1 /* Do not create or use a rollback journal */
-#define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */
-#define BTREE_MEMORY 4 /* This is an in-memory DB */
-#define BTREE_SINGLE 8 /* The file contains at most 1 b-tree */
-#define BTREE_UNORDERED 16 /* Use of a hash implementation is OK */
+#define BTREE_MEMORY 2 /* This is an in-memory DB */
+#define BTREE_SINGLE 4 /* The file contains at most 1 b-tree */
+#define BTREE_UNORDERED 8 /* Use of a hash implementation is OK */
SQLITE_PRIVATE int sqlite3BtreeClose(Btree*);
SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree*,int);
-SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(Btree*,int,int,int);
-SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree*);
+SQLITE_PRIVATE int sqlite3BtreeSetSpillSize(Btree*,int);
+#if SQLITE_MAX_MMAP_SIZE>0
+SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree*,sqlite3_int64);
+#endif
+SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(Btree*,unsigned);
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
-SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree*);
+SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*);
+SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int);
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int);
SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*);
-SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*);
+SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int);
SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int);
SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags);
SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*);
@@ -8005,7 +12110,9 @@ SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*);
SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*);
SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree);
+#ifndef SQLITE_OMIT_SHARED_CACHE
SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock);
+#endif
SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int);
SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *);
@@ -8029,11 +12136,14 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *);
SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*);
SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*);
-SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int);
+SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor*);
+SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree*, int, int);
SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
+SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
+
/*
** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta
** should be one of the following values. The integer values are assigned
@@ -8045,6 +12155,11 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
** For example, the free-page-count field is located at byte offset 36 of
** the database file header. The incr-vacuum-flag field is located at
** byte offset 64 (== 36+4*7).
+**
+** The BTREE_DATA_VERSION value is not really a value stored in the header.
+** It is a read-only number computed by the pager. But we merge it with
+** the header value access routines since its access pattern is the same.
+** Call it a "virtual meta value".
*/
#define BTREE_FREE_PAGE_COUNT 0
#define BTREE_SCHEMA_VERSION 1
@@ -8054,6 +12169,79 @@ SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
#define BTREE_TEXT_ENCODING 5
#define BTREE_USER_VERSION 6
#define BTREE_INCR_VACUUM 7
+#define BTREE_APPLICATION_ID 8
+#define BTREE_DATA_VERSION 15 /* A virtual meta-value */
+
+/*
+** Kinds of hints that can be passed into the sqlite3BtreeCursorHint()
+** interface.
+**
+** BTREE_HINT_RANGE (arguments: Expr*, Mem*)
+**
+** The first argument is an Expr* (which is guaranteed to be constant for
+** the lifetime of the cursor) that defines constraints on which rows
+** might be fetched with this cursor. The Expr* tree may contain
+** TK_REGISTER nodes that refer to values stored in the array of registers
+** passed as the second parameter. In other words, if Expr.op==TK_REGISTER
+** then the value of the node is the value in Mem[pExpr.iTable]. Any
+** TK_COLUMN node in the expression tree refers to the Expr.iColumn-th
+** column of the b-tree of the cursor. The Expr tree will not contain
+** any function calls nor subqueries nor references to b-trees other than
+** the cursor being hinted.
+**
+** The design of the _RANGE hint is aid b-tree implementations that try
+** to prefetch content from remote machines - to provide those
+** implementations with limits on what needs to be prefetched and thereby
+** reduce network bandwidth.
+**
+** Note that BTREE_HINT_FLAGS with BTREE_BULKLOAD is the only hint used by
+** standard SQLite. The other hints are provided for extentions that use
+** the SQLite parser and code generator but substitute their own storage
+** engine.
+*/
+#define BTREE_HINT_RANGE 0 /* Range constraints on queries */
+
+/*
+** Values that may be OR'd together to form the argument to the
+** BTREE_HINT_FLAGS hint for sqlite3BtreeCursorHint():
+**
+** The BTREE_BULKLOAD flag is set on index cursors when the index is going
+** to be filled with content that is already in sorted order.
+**
+** The BTREE_SEEK_EQ flag is set on cursors that will get OP_SeekGE or
+** OP_SeekLE opcodes for a range search, but where the range of entries
+** selected will all have the same key. In other words, the cursor will
+** be used only for equality key searches.
+**
+*/
+#define BTREE_BULKLOAD 0x00000001 /* Used to full index in sorted order */
+#define BTREE_SEEK_EQ 0x00000002 /* EQ seeks only - no range seeks */
+
+/*
+** Flags passed as the third argument to sqlite3BtreeCursor().
+**
+** For read-only cursors the wrFlag argument is always zero. For read-write
+** cursors it may be set to either (BTREE_WRCSR|BTREE_FORDELETE) or just
+** (BTREE_WRCSR). If the BTREE_FORDELETE bit is set, then the cursor will
+** only be used by SQLite for the following:
+**
+** * to seek to and then delete specific entries, and/or
+**
+** * to read values that will be used to create keys that other
+** BTREE_FORDELETE cursors will seek to and delete.
+**
+** The BTREE_FORDELETE flag is an optimization hint. It is not used by
+** by this, the native b-tree engine of SQLite, but it is available to
+** alternative storage engines that might be substituted in place of this
+** b-tree system. For alternative storage engines in which a delete of
+** the main table row automatically deletes corresponding index rows,
+** the FORDELETE flag hint allows those alternative storage engines to
+** skip a lot of work. Namely: FORDELETE cursors may treat all SEEK
+** and DELETE operations as no-ops, and any READ operation against a
+** FORDELETE cursor may return a null row: 0x01 0x00.
+*/
+#define BTREE_WRCSR 0x00000004 /* read-write cursor */
+#define BTREE_FORDELETE 0x00000008 /* Cursor is for seek/delete only */
SQLITE_PRIVATE int sqlite3BtreeCursor(
Btree*, /* BTree containing table to open */
@@ -8064,6 +12252,10 @@ SQLITE_PRIVATE int sqlite3BtreeCursor(
);
SQLITE_PRIVATE int sqlite3BtreeCursorSize(void);
SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*);
+SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor*, unsigned);
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor*, int, ...);
+#endif
SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
@@ -8073,33 +12265,65 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
int bias,
int *pRes
);
-SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*, int*);
-SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*);
-SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const void *pKey, i64 nKey,
- const void *pData, int nData,
- int nZero, int bias, int seekResult);
+SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor*);
+SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*);
+SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);
+
+/* Allowed flags for the 2nd argument to sqlite3BtreeDelete() */
+#define BTREE_SAVEPOSITION 0x02 /* Leave cursor pointing at NEXT or PREV */
+#define BTREE_AUXDELETE 0x04 /* not the primary delete operation */
+
+/* An instance of the BtreePayload object describes the content of a single
+** entry in either an index or table btree.
+**
+** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
+** an arbitrary key and no data. These btrees have pKey,nKey set to their
+** key and pData,nData,nZero set to zero.
+**
+** Table btrees (used for rowid tables) contain an integer rowid used as
+** the key and passed in the nKey field. The pKey field is zero.
+** pData,nData hold the content of the new entry. nZero extra zero bytes
+** are appended to the end of the content when constructing the entry.
+**
+** This object is used to pass information into sqlite3BtreeInsert(). The
+** same information used to be passed as five separate parameters. But placing
+** the information into this object helps to keep the interface more
+** organized and understandable, and it also helps the resulting code to
+** run a little faster by using fewer registers for parameter passing.
+*/
+struct BtreePayload {
+ const void *pKey; /* Key content for indexes. NULL for tables */
+ sqlite3_int64 nKey; /* Size of pKey for indexes. PRIMARY KEY for tabs */
+ const void *pData; /* Data for tables. NULL for indexes */
+ int nData; /* Size of pData. 0 if none. */
+ int nZero; /* Extra zero data appended after pData,nData */
+};
+
+SQLITE_PRIVATE int sqlite3BtreeInsert(BtCursor*, const BtreePayload *pPayload,
+ int bias, int seekResult);
SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor*, int *pRes);
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes);
SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes);
SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes);
-SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize);
+SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*);
-SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor*, int *pAmt);
-SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor*, int *pAmt);
-SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor*, u32 *pSize);
+SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor*, u32 *pAmt);
+SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor*);
SQLITE_PRIVATE int sqlite3BtreeData(BtCursor*, u32 offset, u32 amt, void*);
-SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor*, sqlite3_int64);
-SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor*);
SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(Btree*, int *aRoot, int nRoot, int, int*);
SQLITE_PRIVATE struct Pager *sqlite3BtreePager(Btree*);
+#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
-SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *);
+SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *);
+#endif
SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *);
-
SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
+SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor*, unsigned int mask);
+SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *pBt);
+SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void);
#ifndef NDEBUG
SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
@@ -8126,15 +12350,19 @@ SQLITE_PRIVATE int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#ifndef SQLITE_OMIT_SHARED_CACHE
SQLITE_PRIVATE void sqlite3BtreeEnter(Btree*);
SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3*);
+SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*);
+SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*);
+SQLITE_PRIVATE int sqlite3BtreeConnectionCount(Btree*);
#else
# define sqlite3BtreeEnter(X)
# define sqlite3BtreeEnterAll(X)
+# define sqlite3BtreeSharable(X) 0
+# define sqlite3BtreeEnterCursor(X)
+# define sqlite3BtreeConnectionCount(X) 1
#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE
-SQLITE_PRIVATE int sqlite3BtreeSharable(Btree*);
SQLITE_PRIVATE void sqlite3BtreeLeave(Btree*);
-SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor*);
SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor*);
SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3*);
#ifndef NDEBUG
@@ -8145,9 +12373,7 @@ SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*);
#endif
#else
-# define sqlite3BtreeSharable(X) 0
# define sqlite3BtreeLeave(X)
-# define sqlite3BtreeEnterCursor(X)
# define sqlite3BtreeLeaveCursor(X)
# define sqlite3BtreeLeaveAll(X)
@@ -8157,7 +12383,7 @@ SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*);
#endif
-#endif /* _BTREE_H_ */
+#endif /* SQLITE_BTREE_H */
/************** End of btree.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -8180,8 +12406,8 @@ SQLITE_PRIVATE int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*);
** or VDBE. The VDBE implements an abstract machine that runs a
** simple program to access and modify the underlying database.
*/
-#ifndef _SQLITE_VDBE_H_
-#define _SQLITE_VDBE_H_
+#ifndef SQLITE_VDBE_H
+#define SQLITE_VDBE_H
/* #include <stdio.h> */
/*
@@ -8195,7 +12421,6 @@ typedef struct Vdbe Vdbe;
** The names of the following types declared in vdbeInt.h are required
** for the VdbeOp definition.
*/
-typedef struct VdbeFunc VdbeFunc;
typedef struct Mem Mem;
typedef struct SubProgram SubProgram;
@@ -8207,34 +12432,41 @@ typedef struct SubProgram SubProgram;
struct VdbeOp {
u8 opcode; /* What operation to perform */
signed char p4type; /* One of the P4_xxx constants for p4 */
- u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */
+ u8 notUsed1;
u8 p5; /* Fifth parameter is an unsigned character */
int p1; /* First operand */
int p2; /* Second parameter (often the jump destination) */
int p3; /* The third parameter */
- union { /* fourth parameter */
+ union p4union { /* fourth parameter */
int i; /* Integer value if p4type==P4_INT32 */
void *p; /* Generic pointer */
char *z; /* Pointer to data for string (char array) types */
i64 *pI64; /* Used when p4type is P4_INT64 */
double *pReal; /* Used when p4type is P4_REAL */
FuncDef *pFunc; /* Used when p4type is P4_FUNCDEF */
- VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */
+ sqlite3_context *pCtx; /* Used when p4type is P4_FUNCCTX */
CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */
Mem *pMem; /* Used when p4type is P4_MEM */
VTable *pVtab; /* Used when p4type is P4_VTAB */
KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */
int *ai; /* Used when p4type is P4_INTARRAY */
SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */
+ Table *pTab; /* Used when p4type is P4_TABLE */
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ Expr *pExpr; /* Used when p4type is P4_EXPR */
+#endif
int (*xAdvance)(BtCursor *, int *);
} p4;
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
char *zComment; /* Comment to improve readability */
#endif
#ifdef VDBE_PROFILE
- int cnt; /* Number of times this instruction was executed */
+ u32 cnt; /* Number of times this instruction was executed */
u64 cycles; /* Total time spent executing this instruction */
#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ int iSrcLine; /* Source-code line that generated this opcode */
+#endif
};
typedef struct VdbeOp VdbeOp;
@@ -8272,7 +12504,7 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_COLLSEQ (-4) /* P4 is a pointer to a CollSeq structure */
#define P4_FUNCDEF (-5) /* P4 is a pointer to a FuncDef structure */
#define P4_KEYINFO (-6) /* P4 is a pointer to a KeyInfo structure */
-#define P4_VDBEFUNC (-7) /* P4 is a pointer to a VdbeFunc structure */
+#define P4_EXPR (-7) /* P4 is a pointer to an Expr tree */
#define P4_MEM (-8) /* P4 is a pointer to a Mem* structure */
#define P4_TRANSIENT 0 /* P4 is a pointer to a transient string */
#define P4_VTAB (-10) /* P4 is a pointer to an sqlite3_vtab structure */
@@ -8283,16 +12515,14 @@ typedef struct VdbeOpList VdbeOpList;
#define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */
#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */
#define P4_ADVANCE (-19) /* P4 is a pointer to BtreeNext() or BtreePrev() */
+#define P4_TABLE (-20) /* P4 is a pointer to a Table structure */
+#define P4_FUNCCTX (-21) /* P4 is a pointer to an sqlite3_context object */
-/* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure
-** is made. That copy is freed when the Vdbe is finalized. But if the
-** argument is P4_KEYINFO_HANDOFF, the passed in pointer is used. It still
-** gets freed when the Vdbe is finalized so it still should be obtained
-** from a single sqliteMalloc(). But no copy is made and the calling
-** function should *not* try to free the KeyInfo.
-*/
-#define P4_KEYINFO_HANDOFF (-16)
-#define P4_KEYINFO_STATIC (-17)
+/* Error message codes for OP_Halt */
+#define P5_ConstraintNotNull 1
+#define P5_ConstraintUnique 2
+#define P5_ConstraintCheck 3
+#define P5_ConstraintFK 4
/*
** The Vdbe.aColName array contains 5n Mem structures, where n is the
@@ -8328,190 +12558,211 @@ typedef struct VdbeOpList VdbeOpList;
/************** Include opcodes.h in the middle of vdbe.h ********************/
/************** Begin file opcodes.h *****************************************/
/* Automatically generated. Do not edit */
-/* See the mkopcodeh.awk script for details */
-#define OP_Goto 1
-#define OP_Gosub 2
-#define OP_Return 3
-#define OP_Yield 4
-#define OP_HaltIfNull 5
-#define OP_Halt 6
-#define OP_Integer 7
-#define OP_Int64 8
-#define OP_Real 130 /* same as TK_FLOAT */
-#define OP_String8 94 /* same as TK_STRING */
-#define OP_String 9
-#define OP_Null 10
-#define OP_Blob 11
-#define OP_Variable 12
-#define OP_Move 13
-#define OP_Copy 14
-#define OP_SCopy 15
-#define OP_ResultRow 16
-#define OP_Concat 91 /* same as TK_CONCAT */
-#define OP_Add 86 /* same as TK_PLUS */
-#define OP_Subtract 87 /* same as TK_MINUS */
-#define OP_Multiply 88 /* same as TK_STAR */
-#define OP_Divide 89 /* same as TK_SLASH */
-#define OP_Remainder 90 /* same as TK_REM */
-#define OP_CollSeq 17
-#define OP_Function 18
-#define OP_BitAnd 82 /* same as TK_BITAND */
-#define OP_BitOr 83 /* same as TK_BITOR */
-#define OP_ShiftLeft 84 /* same as TK_LSHIFT */
-#define OP_ShiftRight 85 /* same as TK_RSHIFT */
-#define OP_AddImm 20
-#define OP_MustBeInt 21
-#define OP_RealAffinity 22
-#define OP_ToText 141 /* same as TK_TO_TEXT */
-#define OP_ToBlob 142 /* same as TK_TO_BLOB */
-#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/
-#define OP_ToInt 144 /* same as TK_TO_INT */
-#define OP_ToReal 145 /* same as TK_TO_REAL */
-#define OP_Eq 76 /* same as TK_EQ */
-#define OP_Ne 75 /* same as TK_NE */
-#define OP_Lt 79 /* same as TK_LT */
-#define OP_Le 78 /* same as TK_LE */
-#define OP_Gt 77 /* same as TK_GT */
-#define OP_Ge 80 /* same as TK_GE */
-#define OP_Permutation 23
-#define OP_Compare 24
-#define OP_Jump 25
-#define OP_And 69 /* same as TK_AND */
-#define OP_Or 68 /* same as TK_OR */
-#define OP_Not 19 /* same as TK_NOT */
-#define OP_BitNot 93 /* same as TK_BITNOT */
-#define OP_Once 26
-#define OP_If 27
-#define OP_IfNot 28
-#define OP_IsNull 73 /* same as TK_ISNULL */
-#define OP_NotNull 74 /* same as TK_NOTNULL */
-#define OP_Column 29
-#define OP_Affinity 30
-#define OP_MakeRecord 31
-#define OP_Count 32
-#define OP_Savepoint 33
-#define OP_AutoCommit 34
-#define OP_Transaction 35
-#define OP_ReadCookie 36
-#define OP_SetCookie 37
-#define OP_VerifyCookie 38
-#define OP_OpenRead 39
-#define OP_OpenWrite 40
-#define OP_OpenAutoindex 41
-#define OP_OpenEphemeral 42
-#define OP_SorterOpen 43
-#define OP_OpenPseudo 44
-#define OP_Close 45
-#define OP_SeekLt 46
-#define OP_SeekLe 47
-#define OP_SeekGe 48
-#define OP_SeekGt 49
-#define OP_Seek 50
-#define OP_NotFound 51
-#define OP_Found 52
-#define OP_IsUnique 53
-#define OP_NotExists 54
-#define OP_Sequence 55
-#define OP_NewRowid 56
-#define OP_Insert 57
-#define OP_InsertInt 58
-#define OP_Delete 59
-#define OP_ResetCount 60
-#define OP_SorterCompare 61
-#define OP_SorterData 62
-#define OP_RowKey 63
-#define OP_RowData 64
-#define OP_Rowid 65
-#define OP_NullRow 66
-#define OP_Last 67
-#define OP_SorterSort 70
-#define OP_Sort 71
-#define OP_Rewind 72
-#define OP_SorterNext 81
-#define OP_Prev 92
-#define OP_Next 95
-#define OP_SorterInsert 96
-#define OP_IdxInsert 97
-#define OP_IdxDelete 98
-#define OP_IdxRowid 99
-#define OP_IdxLT 100
-#define OP_IdxGE 101
-#define OP_Destroy 102
-#define OP_Clear 103
-#define OP_CreateIndex 104
-#define OP_CreateTable 105
-#define OP_ParseSchema 106
-#define OP_LoadAnalysis 107
-#define OP_DropTable 108
-#define OP_DropIndex 109
-#define OP_DropTrigger 110
-#define OP_IntegrityCk 111
-#define OP_RowSetAdd 112
-#define OP_RowSetRead 113
-#define OP_RowSetTest 114
-#define OP_Program 115
-#define OP_Param 116
-#define OP_FkCounter 117
-#define OP_FkIfZero 118
-#define OP_MemMax 119
-#define OP_IfPos 120
-#define OP_IfNeg 121
-#define OP_IfZero 122
-#define OP_AggStep 123
-#define OP_AggFinal 124
-#define OP_Checkpoint 125
-#define OP_JournalMode 126
-#define OP_Vacuum 127
-#define OP_IncrVacuum 128
-#define OP_Expire 129
-#define OP_TableLock 131
-#define OP_VBegin 132
-#define OP_VCreate 133
-#define OP_VDestroy 134
-#define OP_VOpen 135
-#define OP_VFilter 136
-#define OP_VColumn 137
-#define OP_VNext 138
-#define OP_VRename 139
-#define OP_VUpdate 140
-#define OP_Pagecount 146
-#define OP_MaxPgcnt 147
-#define OP_Trace 148
-#define OP_Noop 149
-#define OP_Explain 150
-
+/* See the tool/mkopcodeh.tcl script for details */
+#define OP_Savepoint 0
+#define OP_AutoCommit 1
+#define OP_Transaction 2
+#define OP_SorterNext 3
+#define OP_PrevIfOpen 4
+#define OP_NextIfOpen 5
+#define OP_Prev 6
+#define OP_Next 7
+#define OP_Checkpoint 8
+#define OP_JournalMode 9
+#define OP_Vacuum 10
+#define OP_VFilter 11 /* synopsis: iplan=r[P3] zplan='P4' */
+#define OP_VUpdate 12 /* synopsis: data=r[P3@P2] */
+#define OP_Goto 13
+#define OP_Gosub 14
+#define OP_InitCoroutine 15
+#define OP_Yield 16
+#define OP_MustBeInt 17
+#define OP_Jump 18
+#define OP_Not 19 /* same as TK_NOT, synopsis: r[P2]= !r[P1] */
+#define OP_Once 20
+#define OP_If 21
+#define OP_IfNot 22
+#define OP_SeekLT 23 /* synopsis: key=r[P3@P4] */
+#define OP_SeekLE 24 /* synopsis: key=r[P3@P4] */
+#define OP_SeekGE 25 /* synopsis: key=r[P3@P4] */
+#define OP_SeekGT 26 /* synopsis: key=r[P3@P4] */
+#define OP_Or 27 /* same as TK_OR, synopsis: r[P3]=(r[P1] || r[P2]) */
+#define OP_And 28 /* same as TK_AND, synopsis: r[P3]=(r[P1] && r[P2]) */
+#define OP_NoConflict 29 /* synopsis: key=r[P3@P4] */
+#define OP_NotFound 30 /* synopsis: key=r[P3@P4] */
+#define OP_Found 31 /* synopsis: key=r[P3@P4] */
+#define OP_SeekRowid 32 /* synopsis: intkey=r[P3] */
+#define OP_NotExists 33 /* synopsis: intkey=r[P3] */
+#define OP_IsNull 34 /* same as TK_ISNULL, synopsis: if r[P1]==NULL goto P2 */
+#define OP_NotNull 35 /* same as TK_NOTNULL, synopsis: if r[P1]!=NULL goto P2 */
+#define OP_Ne 36 /* same as TK_NE, synopsis: IF r[P3]!=r[P1] */
+#define OP_Eq 37 /* same as TK_EQ, synopsis: IF r[P3]==r[P1] */
+#define OP_Gt 38 /* same as TK_GT, synopsis: IF r[P3]>r[P1] */
+#define OP_Le 39 /* same as TK_LE, synopsis: IF r[P3]<=r[P1] */
+#define OP_Lt 40 /* same as TK_LT, synopsis: IF r[P3]<r[P1] */
+#define OP_Ge 41 /* same as TK_GE, synopsis: IF r[P3]>=r[P1] */
+#define OP_ElseNotEq 42 /* same as TK_ESCAPE */
+#define OP_BitAnd 43 /* same as TK_BITAND, synopsis: r[P3]=r[P1]&r[P2] */
+#define OP_BitOr 44 /* same as TK_BITOR, synopsis: r[P3]=r[P1]|r[P2] */
+#define OP_ShiftLeft 45 /* same as TK_LSHIFT, synopsis: r[P3]=r[P2]<<r[P1] */
+#define OP_ShiftRight 46 /* same as TK_RSHIFT, synopsis: r[P3]=r[P2]>>r[P1] */
+#define OP_Add 47 /* same as TK_PLUS, synopsis: r[P3]=r[P1]+r[P2] */
+#define OP_Subtract 48 /* same as TK_MINUS, synopsis: r[P3]=r[P2]-r[P1] */
+#define OP_Multiply 49 /* same as TK_STAR, synopsis: r[P3]=r[P1]*r[P2] */
+#define OP_Divide 50 /* same as TK_SLASH, synopsis: r[P3]=r[P2]/r[P1] */
+#define OP_Remainder 51 /* same as TK_REM, synopsis: r[P3]=r[P2]%r[P1] */
+#define OP_Concat 52 /* same as TK_CONCAT, synopsis: r[P3]=r[P2]+r[P1] */
+#define OP_Last 53
+#define OP_BitNot 54 /* same as TK_BITNOT, synopsis: r[P1]= ~r[P1] */
+#define OP_SorterSort 55
+#define OP_Sort 56
+#define OP_Rewind 57
+#define OP_IdxLE 58 /* synopsis: key=r[P3@P4] */
+#define OP_IdxGT 59 /* synopsis: key=r[P3@P4] */
+#define OP_IdxLT 60 /* synopsis: key=r[P3@P4] */
+#define OP_IdxGE 61 /* synopsis: key=r[P3@P4] */
+#define OP_RowSetRead 62 /* synopsis: r[P3]=rowset(P1) */
+#define OP_RowSetTest 63 /* synopsis: if r[P3] in rowset(P1) goto P2 */
+#define OP_Program 64
+#define OP_FkIfZero 65 /* synopsis: if fkctr[P1]==0 goto P2 */
+#define OP_IfPos 66 /* synopsis: if r[P1]>0 then r[P1]-=P3, goto P2 */
+#define OP_IfNotZero 67 /* synopsis: if r[P1]!=0 then r[P1]-=P3, goto P2 */
+#define OP_DecrJumpZero 68 /* synopsis: if (--r[P1])==0 goto P2 */
+#define OP_IncrVacuum 69
+#define OP_VNext 70
+#define OP_Init 71 /* synopsis: Start at P2 */
+#define OP_Return 72
+#define OP_EndCoroutine 73
+#define OP_HaltIfNull 74 /* synopsis: if r[P3]=null halt */
+#define OP_Halt 75
+#define OP_Integer 76 /* synopsis: r[P2]=P1 */
+#define OP_Int64 77 /* synopsis: r[P2]=P4 */
+#define OP_String 78 /* synopsis: r[P2]='P4' (len=P1) */
+#define OP_Null 79 /* synopsis: r[P2..P3]=NULL */
+#define OP_SoftNull 80 /* synopsis: r[P1]=NULL */
+#define OP_Blob 81 /* synopsis: r[P2]=P4 (len=P1) */
+#define OP_Variable 82 /* synopsis: r[P2]=parameter(P1,P4) */
+#define OP_Move 83 /* synopsis: r[P2@P3]=r[P1@P3] */
+#define OP_Copy 84 /* synopsis: r[P2@P3+1]=r[P1@P3+1] */
+#define OP_SCopy 85 /* synopsis: r[P2]=r[P1] */
+#define OP_IntCopy 86 /* synopsis: r[P2]=r[P1] */
+#define OP_ResultRow 87 /* synopsis: output=r[P1@P2] */
+#define OP_CollSeq 88
+#define OP_Function0 89 /* synopsis: r[P3]=func(r[P2@P5]) */
+#define OP_Function 90 /* synopsis: r[P3]=func(r[P2@P5]) */
+#define OP_AddImm 91 /* synopsis: r[P1]=r[P1]+P2 */
+#define OP_RealAffinity 92
+#define OP_Cast 93 /* synopsis: affinity(r[P1]) */
+#define OP_Permutation 94
+#define OP_Compare 95 /* synopsis: r[P1@P3] <-> r[P2@P3] */
+#define OP_Column 96 /* synopsis: r[P3]=PX */
+#define OP_String8 97 /* same as TK_STRING, synopsis: r[P2]='P4' */
+#define OP_Affinity 98 /* synopsis: affinity(r[P1@P2]) */
+#define OP_MakeRecord 99 /* synopsis: r[P3]=mkrec(r[P1@P2]) */
+#define OP_Count 100 /* synopsis: r[P2]=count() */
+#define OP_ReadCookie 101
+#define OP_SetCookie 102
+#define OP_ReopenIdx 103 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenRead 104 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenWrite 105 /* synopsis: root=P2 iDb=P3 */
+#define OP_OpenAutoindex 106 /* synopsis: nColumn=P2 */
+#define OP_OpenEphemeral 107 /* synopsis: nColumn=P2 */
+#define OP_SorterOpen 108
+#define OP_SequenceTest 109 /* synopsis: if( cursor[P1].ctr++ ) pc = P2 */
+#define OP_OpenPseudo 110 /* synopsis: P3 columns in r[P2] */
+#define OP_Close 111
+#define OP_ColumnsUsed 112
+#define OP_Sequence 113 /* synopsis: r[P2]=cursor[P1].ctr++ */
+#define OP_NewRowid 114 /* synopsis: r[P2]=rowid */
+#define OP_Insert 115 /* synopsis: intkey=r[P3] data=r[P2] */
+#define OP_InsertInt 116 /* synopsis: intkey=P3 data=r[P2] */
+#define OP_Delete 117
+#define OP_ResetCount 118
+#define OP_SorterCompare 119 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
+#define OP_SorterData 120 /* synopsis: r[P2]=data */
+#define OP_RowKey 121 /* synopsis: r[P2]=key */
+#define OP_RowData 122 /* synopsis: r[P2]=data */
+#define OP_Rowid 123 /* synopsis: r[P2]=rowid */
+#define OP_NullRow 124
+#define OP_SorterInsert 125
+#define OP_IdxInsert 126 /* synopsis: key=r[P2] */
+#define OP_IdxDelete 127 /* synopsis: key=r[P2@P3] */
+#define OP_Seek 128 /* synopsis: Move P3 to P1.rowid */
+#define OP_IdxRowid 129 /* synopsis: r[P2]=rowid */
+#define OP_Destroy 130
+#define OP_Clear 131
+#define OP_Real 132 /* same as TK_FLOAT, synopsis: r[P2]=P4 */
+#define OP_ResetSorter 133
+#define OP_CreateIndex 134 /* synopsis: r[P2]=root iDb=P1 */
+#define OP_CreateTable 135 /* synopsis: r[P2]=root iDb=P1 */
+#define OP_ParseSchema 136
+#define OP_LoadAnalysis 137
+#define OP_DropTable 138
+#define OP_DropIndex 139
+#define OP_DropTrigger 140
+#define OP_IntegrityCk 141
+#define OP_RowSetAdd 142 /* synopsis: rowset(P1)=r[P2] */
+#define OP_Param 143
+#define OP_FkCounter 144 /* synopsis: fkctr[P1]+=P2 */
+#define OP_MemMax 145 /* synopsis: r[P1]=max(r[P1],r[P2]) */
+#define OP_OffsetLimit 146 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
+#define OP_AggStep0 147 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggStep 148 /* synopsis: accum=r[P3] step(r[P2@P5]) */
+#define OP_AggFinal 149 /* synopsis: accum=r[P1] N=P2 */
+#define OP_Expire 150
+#define OP_TableLock 151 /* synopsis: iDb=P1 root=P2 write=P3 */
+#define OP_VBegin 152
+#define OP_VCreate 153
+#define OP_VDestroy 154
+#define OP_VOpen 155
+#define OP_VColumn 156 /* synopsis: r[P3]=vcolumn(P2) */
+#define OP_VRename 157
+#define OP_Pagecount 158
+#define OP_MaxPgcnt 159
+#define OP_CursorHint 160
+#define OP_Noop 161
+#define OP_Explain 162
/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
** are encoded into bitvectors as follows:
*/
-#define OPFLG_JUMP 0x0001 /* jump: P2 holds jmp target */
-#define OPFLG_OUT2_PRERELEASE 0x0002 /* out2-prerelease: */
-#define OPFLG_IN1 0x0004 /* in1: P1 is an input */
-#define OPFLG_IN2 0x0008 /* in2: P2 is an input */
-#define OPFLG_IN3 0x0010 /* in3: P3 is an input */
-#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */
-#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */
+#define OPFLG_JUMP 0x01 /* jump: P2 holds jmp target */
+#define OPFLG_IN1 0x02 /* in1: P1 is an input */
+#define OPFLG_IN2 0x04 /* in2: P2 is an input */
+#define OPFLG_IN3 0x08 /* in3: P3 is an input */
+#define OPFLG_OUT2 0x10 /* out2: P2 is an output */
+#define OPFLG_OUT3 0x20 /* out3: P3 is an output */
#define OPFLG_INITIALIZER {\
-/* 0 */ 0x00, 0x01, 0x05, 0x04, 0x04, 0x10, 0x00, 0x02,\
-/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x24, 0x24,\
-/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\
-/* 24 */ 0x00, 0x01, 0x05, 0x05, 0x05, 0x00, 0x00, 0x00,\
-/* 32 */ 0x02, 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00,\
-/* 40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x11,\
-/* 48 */ 0x11, 0x11, 0x08, 0x11, 0x11, 0x11, 0x11, 0x02,\
-/* 56 */ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 64 */ 0x00, 0x02, 0x00, 0x01, 0x4c, 0x4c, 0x01, 0x01,\
-/* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\
-/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\
-/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x01, 0x24, 0x02, 0x01,\
-/* 96 */ 0x08, 0x08, 0x00, 0x02, 0x01, 0x01, 0x02, 0x00,\
-/* 104 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 112 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\
-/* 120 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02, 0x00,\
-/* 128 */ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,\
-/* 136 */ 0x01, 0x00, 0x01, 0x00, 0x00, 0x04, 0x04, 0x04,\
-/* 144 */ 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,}
+/* 0 */ 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01,\
+/* 8 */ 0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01,\
+/* 16 */ 0x03, 0x03, 0x01, 0x12, 0x01, 0x03, 0x03, 0x09,\
+/* 24 */ 0x09, 0x09, 0x09, 0x26, 0x26, 0x09, 0x09, 0x09,\
+/* 32 */ 0x09, 0x09, 0x03, 0x03, 0x0b, 0x0b, 0x0b, 0x0b,\
+/* 40 */ 0x0b, 0x0b, 0x01, 0x26, 0x26, 0x26, 0x26, 0x26,\
+/* 48 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x01, 0x12, 0x01,\
+/* 56 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x23, 0x0b,\
+/* 64 */ 0x01, 0x01, 0x03, 0x03, 0x03, 0x01, 0x01, 0x01,\
+/* 72 */ 0x02, 0x02, 0x08, 0x00, 0x10, 0x10, 0x10, 0x10,\
+/* 80 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00,\
+/* 88 */ 0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00,\
+/* 96 */ 0x00, 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
+/* 104 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 112 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 120 */ 0x00, 0x00, 0x00, 0x10, 0x00, 0x04, 0x04, 0x00,\
+/* 128 */ 0x00, 0x10, 0x10, 0x00, 0x10, 0x00, 0x10, 0x10,\
+/* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10,\
+/* 144 */ 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,\
+/* 152 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,\
+/* 160 */ 0x00, 0x00, 0x00,}
+
+/* The sqlite3P2Values() routine is able to run faster if it knows
+** the value of the largest JUMP opcode. The smaller the maximum
+** JUMP opcode the better, so the mkopcodeh.tcl script that
+** generated this include file strives to group all JUMP opcodes
+** together near the beginning of the list.
+*/
+#define SQLITE_MX_JUMP_OPCODE 71 /* Maximum JUMP opcode */
/************** End of opcodes.h *********************************************/
/************** Continuing where we left off in vdbe.h ***********************/
@@ -8520,35 +12771,48 @@ typedef struct VdbeOpList VdbeOpList;
** Prototypes for the VDBE interface. See comments on the implementation
** for a description of what each of these routines does.
*/
-SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3*);
+SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*);
SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe*,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp1(Vdbe*,int,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int);
+SQLITE_PRIVATE int sqlite3VdbeGoto(Vdbe*,int);
+SQLITE_PRIVATE int sqlite3VdbeLoadString(Vdbe*,int,const char*);
+SQLITE_PRIVATE void sqlite3VdbeMultiLoad(Vdbe*,int,const char*,...);
SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int);
+SQLITE_PRIVATE int sqlite3VdbeAddOp4Dup8(Vdbe*,int,int,int,int,const u8*,int);
SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int);
-SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp);
+SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe*,int);
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
+SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N);
+#else
+# define sqlite3VdbeVerifyNoMallocRequired(A,B)
+#endif
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, u32 addr, int P2);
SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe*, u32 addr, int P3);
SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe*, u8 P5);
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe*, int addr);
-SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe*, int addr);
+SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe*, int addr);
+SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe*, u8 op);
SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe*, int addr, const char *zP4, int N);
+SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse*, Index*);
SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe*, int);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int);
SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe*);
+SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*);
-SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3*,Vdbe*);
+SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3*,Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,Parse*);
SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int);
SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int);
-SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*);
#endif
SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe*);
@@ -8560,33 +12824,93 @@ SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int);
SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*);
SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*);
-SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8);
+SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8);
SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int);
#ifndef SQLITE_OMIT_TRACE
SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*);
#endif
+SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,UnpackedRecord*);
SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*);
+SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int);
SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo *, char *, int, char **);
+typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
+SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);
+
#ifndef SQLITE_OMIT_TRIGGER
SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
#endif
-
-#ifndef NDEBUG
+/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
+** each VDBE opcode.
+**
+** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
+** comments in VDBE programs that show key decision points in the code
+** generator.
+*/
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe*, const char*, ...);
# define VdbeComment(X) sqlite3VdbeComment X
SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
# define VdbeNoopComment(X) sqlite3VdbeNoopComment X
+# ifdef SQLITE_ENABLE_MODULE_COMMENTS
+# define VdbeModuleComment(X) sqlite3VdbeNoopComment X
+# else
+# define VdbeModuleComment(X)
+# endif
#else
# define VdbeComment(X)
# define VdbeNoopComment(X)
+# define VdbeModuleComment(X)
+#endif
+
+/*
+** The VdbeCoverage macros are used to set a coverage testing point
+** for VDBE branch instructions. The coverage testing points are line
+** numbers in the sqlite3.c source file. VDBE branch coverage testing
+** only works with an amalagmation build. That's ok since a VDBE branch
+** coverage build designed for testing the test suite only. No application
+** should ever ship with VDBE branch coverage measuring turned on.
+**
+** VdbeCoverage(v) // Mark the previously coded instruction
+** // as a branch
+**
+** VdbeCoverageIf(v, conditional) // Mark previous if conditional true
+**
+** VdbeCoverageAlwaysTaken(v) // Previous branch is always taken
+**
+** VdbeCoverageNeverTaken(v) // Previous branch is never taken
+**
+** Every VDBE branch operation must be tagged with one of the macros above.
+** If not, then when "make test" is run with -DSQLITE_VDBE_COVERAGE and
+** -DSQLITE_DEBUG then an ALWAYS() will fail in the vdbeTakeBranch()
+** routine in vdbe.c, alerting the developer to the missed tag.
+*/
+#ifdef SQLITE_VDBE_COVERAGE
+SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe*,int);
+# define VdbeCoverage(v) sqlite3VdbeSetLineNumber(v,__LINE__)
+# define VdbeCoverageIf(v,x) if(x)sqlite3VdbeSetLineNumber(v,__LINE__)
+# define VdbeCoverageAlwaysTaken(v) sqlite3VdbeSetLineNumber(v,2);
+# define VdbeCoverageNeverTaken(v) sqlite3VdbeSetLineNumber(v,1);
+# define VDBE_OFFSET_LINENO(x) (__LINE__+x)
+#else
+# define VdbeCoverage(v)
+# define VdbeCoverageIf(v,x)
+# define VdbeCoverageAlwaysTaken(v)
+# define VdbeCoverageNeverTaken(v)
+# define VDBE_OFFSET_LINENO(x) 0
#endif
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+SQLITE_PRIVATE void sqlite3VdbeScanStatus(Vdbe*, int, int, int, LogEst, const char*);
+#else
+# define sqlite3VdbeScanStatus(a,b,c,d,e)
#endif
+#endif /* SQLITE_VDBE_H */
+
/************** End of vdbe.h ************************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
/************** Include pager.h in the middle of sqliteInt.h *****************/
@@ -8607,8 +12931,8 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe*, const char*, ...);
** at a time and provides a journal for rollback.
*/
-#ifndef _PAGER_H_
-#define _PAGER_H_
+#ifndef SQLITE_PAGER_H
+#define SQLITE_PAGER_H
/*
** Default maximum size for persistent journal files. A negative
@@ -8651,8 +12975,7 @@ typedef struct PgHdr DbPage;
** NOTE: These values must match the corresponding BTREE_ values in btree.h.
*/
#define PAGER_OMIT_JOURNAL 0x0001 /* Do not use a rollback journal */
-#define PAGER_NO_READLOCK 0x0002 /* Omit readlocks on readonly files */
-#define PAGER_MEMORY 0x0004 /* In-memory database */
+#define PAGER_MEMORY 0x0002 /* In-memory database */
/*
** Valid values for the second argument to sqlite3PagerLockingMode().
@@ -8662,7 +12985,11 @@ typedef struct PgHdr DbPage;
#define PAGER_LOCKINGMODE_EXCLUSIVE 1
/*
-** Numeric constants that encode the journalmode.
+** Numeric constants that encode the journalmode.
+**
+** The numeric values encoded here (other than PAGER_JOURNALMODE_QUERY)
+** are exposed in the API via the "PRAGMA journal_mode" command and
+** therefore cannot be changed without a compatibility break.
*/
#define PAGER_JOURNALMODE_QUERY (-1) /* Query the value of journalmode */
#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
@@ -8673,6 +13000,30 @@ typedef struct PgHdr DbPage;
#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
/*
+** Flags that make up the mask passed to sqlite3PagerGet().
+*/
+#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
+#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */
+
+/*
+** Flags for sqlite3PagerSetFlags()
+**
+** Value constraints (enforced via assert()):
+** PAGER_FULLFSYNC == SQLITE_FullFSync
+** PAGER_CKPT_FULLFSYNC == SQLITE_CkptFullFSync
+** PAGER_CACHE_SPILL == SQLITE_CacheSpill
+*/
+#define PAGER_SYNCHRONOUS_OFF 0x01 /* PRAGMA synchronous=OFF */
+#define PAGER_SYNCHRONOUS_NORMAL 0x02 /* PRAGMA synchronous=NORMAL */
+#define PAGER_SYNCHRONOUS_FULL 0x03 /* PRAGMA synchronous=FULL */
+#define PAGER_SYNCHRONOUS_EXTRA 0x04 /* PRAGMA synchronous=EXTRA */
+#define PAGER_SYNCHRONOUS_MASK 0x07 /* Mask for four values above */
+#define PAGER_FULLFSYNC 0x08 /* PRAGMA fullfsync=ON */
+#define PAGER_CKPT_FULLFSYNC 0x10 /* PRAGMA checkpoint_fullfsync=ON */
+#define PAGER_CACHESPILL 0x20 /* PRAGMA cache_spill=ON */
+#define PAGER_FLAGS_MASK 0x38 /* All above except SYNCHRONOUS */
+
+/*
** The remainder of this file contains the declarations of the functions
** that make up the Pager sub-system API. See source code comments for
** a detailed description of each routine.
@@ -8694,22 +13045,29 @@ SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*);
/* Functions used to configure a Pager object. */
SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *);
SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u32*, int);
+#ifdef SQLITE_HAS_CODEC
+SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager*,Pager*);
+#endif
SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int);
SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int);
-SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(Pager*,int,int,int);
+SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager*, int);
+SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *, sqlite3_int64);
+SQLITE_PRIVATE void sqlite3PagerShrink(Pager*);
+SQLITE_PRIVATE void sqlite3PagerSetFlags(Pager*,unsigned);
SQLITE_PRIVATE int sqlite3PagerLockingMode(Pager *, int);
SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *, int);
SQLITE_PRIVATE int sqlite3PagerGetJournalMode(Pager*);
SQLITE_PRIVATE int sqlite3PagerOkToChangeJournalMode(Pager*);
SQLITE_PRIVATE i64 sqlite3PagerJournalSizeLimit(Pager *, i64);
SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager*);
+SQLITE_PRIVATE int sqlite3PagerFlush(Pager*);
/* Functions used to obtain and release page references. */
-SQLITE_PRIVATE int sqlite3PagerAcquire(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
-#define sqlite3PagerGet(A,B,C) sqlite3PagerAcquire(A,B,C,0)
+SQLITE_PRIVATE int sqlite3PagerGet(Pager *pPager, Pgno pgno, DbPage **ppPage, int clrFlag);
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno);
SQLITE_PRIVATE void sqlite3PagerRef(DbPage*);
SQLITE_PRIVATE void sqlite3PagerUnref(DbPage*);
+SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage*);
/* Operations on page references. */
SQLITE_PRIVATE int sqlite3PagerWrite(DbPage*);
@@ -8724,34 +13082,55 @@ SQLITE_PRIVATE void sqlite3PagerPagecount(Pager*, int*);
SQLITE_PRIVATE int sqlite3PagerBegin(Pager*, int exFlag, int);
SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, int);
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager*);
-SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster);
SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager*);
SQLITE_PRIVATE int sqlite3PagerRollback(Pager*);
SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager);
-SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*);
-SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
-SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
-SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
-SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager);
+#ifndef SQLITE_OMIT_WAL
+SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int, int*, int*);
+SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
+SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager);
+SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager);
+# ifdef SQLITE_ENABLE_SNAPSHOT
+SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
+# endif
+#else
+# define sqlite3PagerUseWal(x) 0
+#endif
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager);
+#endif
/* Functions used to query pager state and configuration. */
SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*);
-SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
+SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager*);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3PagerRefcount(Pager*);
+#endif
SQLITE_PRIVATE int sqlite3PagerMemUsed(Pager*);
-SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*);
-SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager*);
+SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager*, int);
+SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*);
SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*);
+SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
-SQLITE_PRIVATE int sqlite3PagerNosync(Pager*);
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
+SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
+SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
/* Functions used to truncate the database file. */
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
+SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);
+
#if defined(SQLITE_HAS_CODEC) && !defined(SQLITE_OMIT_WAL)
SQLITE_PRIVATE void *sqlite3PagerCodec(DbPage *);
#endif
@@ -8771,7 +13150,7 @@ SQLITE_PRIVATE void sqlite3PagerRefdump(Pager*);
# define enable_simulated_io_errors()
#endif
-#endif /* _PAGER_H_ */
+#endif /* SQLITE_PAGER_H */
/************** End of pager.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -8802,11 +13181,12 @@ typedef struct PCache PCache;
** structure.
*/
struct PgHdr {
- void *pData; /* Content of this page */
+ sqlite3_pcache_page *pPage; /* Pcache object page handle */
+ void *pData; /* Page data */
void *pExtra; /* Extra content */
- PgHdr *pDirty; /* Transient list of dirty pages */
- Pgno pgno; /* Page number for this page */
+ PgHdr *pDirty; /* Transient list of dirty sorted by pgno */
Pager *pPager; /* The pager this page is part of */
+ Pgno pgno; /* Page number for this page */
#ifdef SQLITE_CHECK_PAGES
u32 pageHash; /* Hash of page content */
#endif
@@ -8824,12 +13204,15 @@ struct PgHdr {
};
/* Bit values for PgHdr.flags */
-#define PGHDR_DIRTY 0x002 /* Page has changed */
-#define PGHDR_NEED_SYNC 0x004 /* Fsync the rollback journal before
- ** writing this page to the database */
-#define PGHDR_NEED_READ 0x008 /* Content is unread */
-#define PGHDR_REUSE_UNLIKELY 0x010 /* A hint that reuse is unlikely */
-#define PGHDR_DONT_WRITE 0x020 /* Do not write content to disk */
+#define PGHDR_CLEAN 0x001 /* Page not on the PCache.pDirty list */
+#define PGHDR_DIRTY 0x002 /* Page is on the PCache.pDirty list */
+#define PGHDR_WRITEABLE 0x004 /* Journaled and ready to modify */
+#define PGHDR_NEED_SYNC 0x008 /* Fsync the rollback journal before
+ ** writing this page to the database */
+#define PGHDR_DONT_WRITE 0x010 /* Do not write content to disk */
+#define PGHDR_MMAP 0x020 /* This is an mmap page object */
+
+#define PGHDR_WAL_APPEND 0x040 /* Appended to wal file */
/* Initialize and shutdown the page cache subsystem */
SQLITE_PRIVATE int sqlite3PcacheInitialize(void);
@@ -8844,7 +13227,7 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *, int sz, int n);
** Under memory stress, invoke xStress to try to make pages clean.
** Only clean and unpinned pages can be reclaimed.
*/
-SQLITE_PRIVATE void sqlite3PcacheOpen(
+SQLITE_PRIVATE int sqlite3PcacheOpen(
int szPage, /* Size of every page */
int szExtra, /* Extra space associated with each page */
int bPurgeable, /* True if pages are on backing store */
@@ -8854,7 +13237,7 @@ SQLITE_PRIVATE void sqlite3PcacheOpen(
);
/* Modify the page-size after the cache has been created. */
-SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *, int);
+SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *, int);
/* Return the size in bytes of a PCache object. Used to preallocate
** storage space.
@@ -8864,13 +13247,16 @@ SQLITE_PRIVATE int sqlite3PcacheSize(void);
/* One release per successful fetch. Page is pinned until released.
** Reference counted.
*/
-SQLITE_PRIVATE int sqlite3PcacheFetch(PCache*, Pgno, int createFlag, PgHdr**);
+SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(PCache*, Pgno, int createFlag);
+SQLITE_PRIVATE int sqlite3PcacheFetchStress(PCache*, Pgno, sqlite3_pcache_page**);
+SQLITE_PRIVATE PgHdr *sqlite3PcacheFetchFinish(PCache*, Pgno, sqlite3_pcache_page *pPage);
SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr*);
SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr*); /* Remove page from cache */
SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr*); /* Make sure page is marked dirty */
SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr*); /* Mark a single page as clean */
SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache*); /* Mark all dirty list pages as clean */
+SQLITE_PRIVATE void sqlite3PcacheClearWritable(PCache*);
/* Change a page number. Used by incr-vacuum. */
SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr*, Pgno);
@@ -8909,6 +13295,11 @@ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*);
SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *));
#endif
+#if defined(SQLITE_DEBUG)
+/* Check invariants on a PgHdr object */
+SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr*);
+#endif
+
/* Set and get the suggested cache-size for the specified pager-cache.
**
** If no global maximum is configured, then the system attempts to limit
@@ -8920,6 +13311,16 @@ SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *, int);
SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *);
#endif
+/* Set or get the suggested spill-size for the specified pager-cache.
+**
+** The spill-size is the minimum number of pages in cache before the cache
+** will attempt to spill dirty pages by calling xStress.
+*/
+SQLITE_PRIVATE int sqlite3PcacheSetSpillsize(PCache *, int);
+
+/* Free up as much memory as possible from the page cache */
+SQLITE_PRIVATE void sqlite3PcacheShrink(PCache*);
+
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
/* Try to return memory used by the pcache module to the main memory heap */
SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int);
@@ -8931,11 +13332,17 @@ SQLITE_PRIVATE void sqlite3PcacheStats(int*,int*,int*,int*);
SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
+/* Return the header size */
+SQLITE_PRIVATE int sqlite3HeaderSizePcache(void);
+SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void);
+
+/* Number of dirty pages as a percentage of the configured cache size */
+SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache*);
+
#endif /* _PCACHE_H_ */
/************** End of pcache.h **********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
-
/************** Include os.h in the middle of sqliteInt.h ********************/
/************** Begin file os.h **********************************************/
/*
@@ -8961,84 +13368,71 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
#define _SQLITE_OS_H_
/*
-** Figure out if we are dealing with Unix, Windows, or some other
-** operating system. After the following block of preprocess macros,
-** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
-** will defined to either 1 or 0. One of the four will be 1. The other
-** three will be 0.
+** Attempt to automatically detect the operating system and setup the
+** necessary pre-processor macros for it.
+*/
+/************** Include os_setup.h in the middle of os.h *********************/
+/************** Begin file os_setup.h ****************************************/
+/*
+** 2013 November 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains pre-processor directives related to operating system
+** detection and/or setup.
+*/
+#ifndef SQLITE_OS_SETUP_H
+#define SQLITE_OS_SETUP_H
+
+/*
+** Figure out if we are dealing with Unix, Windows, or some other operating
+** system.
+**
+** After the following block of preprocess macros, all of SQLITE_OS_UNIX,
+** SQLITE_OS_WIN, and SQLITE_OS_OTHER will defined to either 1 or 0. One of
+** the three will be 1. The other two will be 0.
*/
#if defined(SQLITE_OS_OTHER)
-# if SQLITE_OS_OTHER==1
-# undef SQLITE_OS_UNIX
-# define SQLITE_OS_UNIX 0
-# undef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# undef SQLITE_OS_OS2
-# define SQLITE_OS_OS2 0
-# else
-# undef SQLITE_OS_OTHER
-# endif
+# if SQLITE_OS_OTHER==1
+# undef SQLITE_OS_UNIX
+# define SQLITE_OS_UNIX 0
+# undef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+# else
+# undef SQLITE_OS_OTHER
+# endif
#endif
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
-# define SQLITE_OS_OTHER 0
-# ifndef SQLITE_OS_WIN
-# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
-# define SQLITE_OS_WIN 1
-# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 0
-# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__)
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 1
-# else
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 1
-# define SQLITE_OS_OS2 0
+# define SQLITE_OS_OTHER 0
+# ifndef SQLITE_OS_WIN
+# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
+ defined(__MINGW32__) || defined(__BORLANDC__)
+# define SQLITE_OS_WIN 1
+# define SQLITE_OS_UNIX 0
+# else
+# define SQLITE_OS_WIN 0
+# define SQLITE_OS_UNIX 1
+# endif
+# else
+# define SQLITE_OS_UNIX 0
# endif
-# else
-# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 0
-# endif
#else
-# ifndef SQLITE_OS_WIN
-# define SQLITE_OS_WIN 0
-# endif
-#endif
-
-/*
-** Determine if we are dealing with WindowsCE - which has a much
-** reduced API.
-*/
-#if defined(_WIN32_WCE)
-# define SQLITE_OS_WINCE 1
-#else
-# define SQLITE_OS_WINCE 0
+# ifndef SQLITE_OS_WIN
+# define SQLITE_OS_WIN 0
+# endif
#endif
+#endif /* SQLITE_OS_SETUP_H */
-/*
-** Define the maximum size of a temporary filename
-*/
-#if SQLITE_OS_WIN
-# include <windows.h>
-# define SQLITE_TEMPNAME_SIZE (MAX_PATH+50)
-#elif SQLITE_OS_OS2
-# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY)
-# include <os2safe.h> /* has to be included before os2.h for linking to work */
-# endif
-# define INCL_DOSDATETIME
-# define INCL_DOSFILEMGR
-# define INCL_DOSERRORS
-# define INCL_DOSMISC
-# define INCL_DOSPROCESS
-# define INCL_DOSMODULEMGR
-# define INCL_DOSSEMAPHORES
-# include <os2.h>
-# include <uconv.h>
-# define SQLITE_TEMPNAME_SIZE (CCHMAXPATHCOMP)
-#else
-# define SQLITE_TEMPNAME_SIZE 200
-#endif
+/************** End of os_setup.h ********************************************/
+/************** Continuing where we left off in os.h *************************/
/* If the SET_FULLSYNC macro is not defined above, then make it
** a no-op
@@ -9051,7 +13445,7 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
** The default size of a disk sector
*/
#ifndef SQLITE_DEFAULT_SECTOR_SIZE
-# define SQLITE_DEFAULT_SECTOR_SIZE 512
+# define SQLITE_DEFAULT_SECTOR_SIZE 4096
#endif
/*
@@ -9134,7 +13528,7 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void);
** shared locks begins at SHARED_FIRST.
**
** The same locking strategy and
-** byte ranges are used for Unix. This leaves open the possiblity of having
+** byte ranges are used for Unix. This leaves open the possibility of having
** clients on win95, winNT, and unix all talking to the same shared file
** and all locking correctly. To do so would require that samba (or whatever
** tool is being used for file sharing) implements locks correctly between
@@ -9174,7 +13568,7 @@ SQLITE_PRIVATE int sqlite3OsInit(void);
/*
** Functions for accessing sqlite3_file methods
*/
-SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file*);
+SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file*);
SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file*, void*, int amt, i64 offset);
SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file*, const void*, int amt, i64 offset);
SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file*, i64 size);
@@ -9184,6 +13578,7 @@ SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file*, int);
SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file*, int);
SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut);
SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file*,int,void*);
+SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file*,int,void*);
#define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0
SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id);
SQLITE_PRIVATE int sqlite3OsDeviceCharacteristics(sqlite3_file *id);
@@ -9191,6 +13586,9 @@ SQLITE_PRIVATE int sqlite3OsShmMap(sqlite3_file *,int,int,int,void volatile **);
SQLITE_PRIVATE int sqlite3OsShmLock(sqlite3_file *id, int, int, int);
SQLITE_PRIVATE void sqlite3OsShmBarrier(sqlite3_file *id);
SQLITE_PRIVATE int sqlite3OsShmUnmap(sqlite3_file *id, int);
+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64, int, void **);
+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *, i64, void *);
+
/*
** Functions for accessing sqlite3_vfs methods
@@ -9207,6 +13605,7 @@ SQLITE_PRIVATE void sqlite3OsDlClose(sqlite3_vfs *, void *);
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *, int, char *);
SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *, int);
+SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs*);
SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
/*
@@ -9214,7 +13613,7 @@ SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *, sqlite3_int64*);
** sqlite3_malloc() to obtain space for the file-handle structure.
*/
SQLITE_PRIVATE int sqlite3OsOpenMalloc(sqlite3_vfs *, const char *, sqlite3_file **, int,int*);
-SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
+SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *);
#endif /* _SQLITE_OS_H_ */
@@ -9249,7 +13648,7 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
** Figure out what version of the code to use. The choices are
**
** SQLITE_MUTEX_OMIT No mutex logic. Not even stubs. The
-** mutexes implemention cannot be overridden
+** mutexes implementation cannot be overridden
** at start-time.
**
** SQLITE_MUTEX_NOOP For single-threaded applications. No
@@ -9260,8 +13659,6 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix.
**
** SQLITE_MUTEX_W32 For multi-threaded applications on Win32.
-**
-** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2.
*/
#if !SQLITE_THREADSAFE
# define SQLITE_MUTEX_OMIT
@@ -9271,8 +13668,6 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
# define SQLITE_MUTEX_PTHREADS
# elif SQLITE_OS_WIN
# define SQLITE_MUTEX_W32
-# elif SQLITE_OS_OS2
-# define SQLITE_MUTEX_OS2
# else
# define SQLITE_MUTEX_NOOP
# endif
@@ -9284,19 +13679,52 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
*/
#define sqlite3_mutex_alloc(X) ((sqlite3_mutex*)8)
#define sqlite3_mutex_free(X)
-#define sqlite3_mutex_enter(X)
+#define sqlite3_mutex_enter(X)
#define sqlite3_mutex_try(X) SQLITE_OK
-#define sqlite3_mutex_leave(X)
+#define sqlite3_mutex_leave(X)
#define sqlite3_mutex_held(X) ((void)(X),1)
#define sqlite3_mutex_notheld(X) ((void)(X),1)
#define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8)
#define sqlite3MutexInit() SQLITE_OK
#define sqlite3MutexEnd()
+#define MUTEX_LOGIC(X)
+#else
+#define MUTEX_LOGIC(X) X
#endif /* defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
+/* The SQLITE_EXTRA_DURABLE compile-time option used to set the default
+** synchronous setting to EXTRA. It is no longer supported.
+*/
+#ifdef SQLITE_EXTRA_DURABLE
+# warning Use SQLITE_DEFAULT_SYNCHRONOUS=3 instead of SQLITE_EXTRA_DURABLE
+# define SQLITE_DEFAULT_SYNCHRONOUS 3
+#endif
+
+/*
+** Default synchronous levels.
+**
+** Note that (for historcal reasons) the PAGER_SYNCHRONOUS_* macros differ
+** from the SQLITE_DEFAULT_SYNCHRONOUS value by 1.
+**
+** PAGER_SYNCHRONOUS DEFAULT_SYNCHRONOUS
+** OFF 1 0
+** NORMAL 2 1
+** FULL 3 2
+** EXTRA 4 3
+**
+** The "PRAGMA synchronous" statement also uses the zero-based numbers.
+** In other words, the zero-based numbers are used for all external interfaces
+** and the one-based values are used internally.
+*/
+#ifndef SQLITE_DEFAULT_SYNCHRONOUS
+# define SQLITE_DEFAULT_SYNCHRONOUS (PAGER_SYNCHRONOUS_FULL-1)
+#endif
+#ifndef SQLITE_DEFAULT_WAL_SYNCHRONOUS
+# define SQLITE_DEFAULT_WAL_SYNCHRONOUS SQLITE_DEFAULT_SYNCHRONOUS
+#endif
/*
** Each database file to be accessed by the system is an instance
@@ -9306,10 +13734,10 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *);
** databases may be attached.
*/
struct Db {
- char *zName; /* Name of this database */
+ char *zDbSName; /* Name of this database. (schema name, not filename) */
Btree *pBt; /* The B*Tree structure for this database file */
- u8 inTrans; /* 0: not writable. 1: Transaction. 2: Checkpoint */
u8 safety_level; /* How aggressive at syncing data to disk */
+ u8 bSyncSet; /* True if "PRAGMA synchronous=N" has been run */
Schema *pSchema; /* Pointer to database schema (possibly shared) */
};
@@ -9320,7 +13748,7 @@ struct Db {
** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing.
** In shared cache mode, a single Schema object can be shared by multiple
** Btrees that refer to the same underlying BtShared object.
-**
+**
** Schema objects are automatically deallocated when the last Btree that
** references them is destroyed. The TEMP Schema is manually freed by
** sqlite3_close().
@@ -9340,18 +13768,18 @@ struct Schema {
Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */
u8 file_format; /* Schema format version for this file */
u8 enc; /* Text encoding used by this database */
- u16 flags; /* Flags associated with this schema */
+ u16 schemaFlags; /* Flags associated with this schema */
int cache_size; /* Number of pages to use in the cache */
};
/*
-** These macros can be used to test, set, or clear bits in the
+** These macros can be used to test, set, or clear bits in the
** Db.pSchema->flags field.
*/
-#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))==(P))
-#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->flags&(P))!=0)
-#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->flags|=(P)
-#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->flags&=~(P)
+#define DbHasProperty(D,I,P) (((D)->aDb[I].pSchema->schemaFlags&(P))==(P))
+#define DbHasAnyProperty(D,I,P) (((D)->aDb[I].pSchema->schemaFlags&(P))!=0)
+#define DbSetProperty(D,I,P) (D)->aDb[I].pSchema->schemaFlags|=(P)
+#define DbClearProperty(D,I,P) (D)->aDb[I].pSchema->schemaFlags&=~(P)
/*
** Allowed values for the DB.pSchema->flags field.
@@ -9371,7 +13799,7 @@ struct Schema {
** The number of different kinds of things that can be limited
** using the sqlite3_limit() interface.
*/
-#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1)
+#define SQLITE_N_LIMIT (SQLITE_LIMIT_WORKER_THREADS+1)
/*
** Lookaside malloc is a set of fixed-size buffers that can be used
@@ -9394,8 +13822,8 @@ struct Schema {
** lookaside allocations are not used to construct the schema objects.
*/
struct Lookaside {
+ u32 bDisable; /* Only operate the lookaside when zero */
u16 sz; /* Size of each buffer in bytes */
- u8 bEnabled; /* False to disable new lookaside allocations */
u8 bMalloced; /* True if pStart obtained from sqlite3_malloc() */
int nOut; /* Number of buffers currently checked out */
int mxOut; /* Highwater mark for nOut */
@@ -9409,87 +13837,131 @@ struct LookasideSlot {
};
/*
-** A hash table for function definitions.
+** A hash table for built-in function definitions. (Application-defined
+** functions use a regular table table from hash.h.)
**
** Hash each FuncDef structure into one of the FuncDefHash.a[] slots.
-** Collisions are on the FuncDef.pHash chain.
+** Collisions are on the FuncDef.u.pHash chain.
*/
+#define SQLITE_FUNC_HASH_SZ 23
struct FuncDefHash {
- FuncDef *a[23]; /* Hash table for functions */
+ FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
+};
+
+#ifdef SQLITE_USER_AUTHENTICATION
+/*
+** Information held in the "sqlite3" database connection object and used
+** to manage user authentication.
+*/
+typedef struct sqlite3_userauth sqlite3_userauth;
+struct sqlite3_userauth {
+ u8 authLevel; /* Current authentication level */
+ int nAuthPW; /* Size of the zAuthPW in bytes */
+ char *zAuthPW; /* Password used to authenticate */
+ char *zAuthUser; /* User name used to authenticate */
};
+/* Allowed values for sqlite3_userauth.authLevel */
+#define UAUTH_Unknown 0 /* Authentication not yet checked */
+#define UAUTH_Fail 1 /* User authentication failed */
+#define UAUTH_User 2 /* Authenticated as a normal user */
+#define UAUTH_Admin 3 /* Authenticated as an administrator */
+
+/* Functions used only by user authorization logic */
+SQLITE_PRIVATE int sqlite3UserAuthTable(const char*);
+SQLITE_PRIVATE int sqlite3UserAuthCheckLogin(sqlite3*,const char*,u8*);
+SQLITE_PRIVATE void sqlite3UserAuthInit(sqlite3*);
+SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**);
+
+#endif /* SQLITE_USER_AUTHENTICATION */
+
+/*
+** typedef for the authorization callback function.
+*/
+#ifdef SQLITE_USER_AUTHENTICATION
+ typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
+ const char*, const char*);
+#else
+ typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
+ const char*);
+#endif
+
+#ifndef SQLITE_OMIT_DEPRECATED
+/* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing
+** in the style of sqlite3_trace()
+*/
+#define SQLITE_TRACE_LEGACY 0x80
+#else
+#define SQLITE_TRACE_LEGACY 0
+#endif /* SQLITE_OMIT_DEPRECATED */
+
+
/*
** Each database connection is an instance of the following structure.
-**
-** The sqlite.lastRowid records the last insert rowid generated by an
-** insert statement. Inserts on views do not affect its value. Each
-** trigger has its own context, so that lastRowid can be updated inside
-** triggers as usual. The previous value will be restored once the trigger
-** exits. Upon entering a before or instead of trigger, lastRowid is no
-** longer (since after version 2.8.12) reset to -1.
-**
-** The sqlite.nChange does not count changes within triggers and keeps no
-** context. It is reset at start of sqlite3_exec.
-** The sqlite.lsChange represents the number of changes made by the last
-** insert, update, or delete statement. It remains constant throughout the
-** length of a statement and is then updated by OP_SetCounts. It keeps a
-** context stack just like lastRowid so that the count of changes
-** within a trigger is not seen outside the trigger. Changes to views do not
-** affect the value of lsChange.
-** The sqlite.csChange keeps track of the number of current changes (since
-** the last statement) and is used to update sqlite_lsChange.
-**
-** The member variables sqlite.errCode, sqlite.zErrMsg and sqlite.zErrMsg16
-** store the most recent error code and, if applicable, string. The
-** internal function sqlite3Error() is used to set these variables
-** consistently.
*/
struct sqlite3 {
sqlite3_vfs *pVfs; /* OS Interface */
- int nDb; /* Number of backends currently in use */
+ struct Vdbe *pVdbe; /* List of active virtual machines */
+ CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
+ sqlite3_mutex *mutex; /* Connection mutex */
Db *aDb; /* All backends */
+ int nDb; /* Number of backends currently in use */
int flags; /* Miscellaneous flags. See below */
+ i64 lastRowid; /* ROWID of most recent insert (see above) */
+ i64 szMmap; /* Default mmap_size setting */
unsigned int openFlags; /* Flags passed to sqlite3_vfs.xOpen() */
int errCode; /* Most recent error code (SQLITE_*) */
int errMask; /* & result codes with this before returning */
+ int iSysErrno; /* Errno value from last system error */
+ u16 dbOptFlags; /* Flags to enable/disable optimizations */
+ u8 enc; /* Text encoding */
u8 autoCommit; /* The auto-commit flag. */
u8 temp_store; /* 1: file 2: memory 0: default */
u8 mallocFailed; /* True if we have seen a malloc failure */
+ u8 bBenignMalloc; /* Do not require OOMs if true */
u8 dfltLockMode; /* Default locking-mode for attached dbs */
signed char nextAutovac; /* Autovac setting after VACUUM if >=0 */
u8 suppressErr; /* Do not issue error messages if true */
u8 vtabOnConflict; /* Value to return for s3_vtab_on_conflict() */
+ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
+ u8 mTrace; /* zero or more SQLITE_TRACE flags */
int nextPagesize; /* Pagesize after VACUUM if >0 */
- int nTable; /* Number of tables in the database */
- CollSeq *pDfltColl; /* The default collating sequence (BINARY) */
- i64 lastRowid; /* ROWID of most recent insert (see above) */
u32 magic; /* Magic number for detect library misuse */
int nChange; /* Value returned by sqlite3_changes() */
int nTotalChange; /* Value returned by sqlite3_total_changes() */
- sqlite3_mutex *mutex; /* Connection mutex */
int aLimit[SQLITE_N_LIMIT]; /* Limits */
+ int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
struct sqlite3InitInfo { /* Information used during initialization */
- int iDb; /* When back is being initialized */
int newTnum; /* Rootpage of table being initialized */
+ u8 iDb; /* Which db file is being initialized */
u8 busy; /* TRUE if currently initializing */
u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */
+ u8 imposterTable; /* Building an imposter table */
} init;
+ int nVdbeActive; /* Number of VDBEs currently running */
+ int nVdbeRead; /* Number of active VDBEs that read or write */
+ int nVdbeWrite; /* Number of active VDBEs that read and write */
+ int nVdbeExec; /* Number of nested calls to VdbeExec() */
+ int nVDestroy; /* Number of active OP_VDestroy operations */
int nExtension; /* Number of loaded extensions */
void **aExtension; /* Array of shared library handles */
- struct Vdbe *pVdbe; /* List of active virtual machines */
- int activeVdbeCnt; /* Number of VDBEs currently executing */
- int writeVdbeCnt; /* Number of active VDBEs that are writing */
- int vdbeExecCnt; /* Number of nested calls to VdbeExec() */
- void (*xTrace)(void*,const char*); /* Trace function */
+ int (*xTrace)(u32,void*,void*,void*); /* Trace function */
void *pTraceArg; /* Argument to the trace function */
void (*xProfile)(void*,const char*,u64); /* Profiling function */
void *pProfileArg; /* Argument to profile function */
- void *pCommitArg; /* Argument to xCommitCallback() */
+ void *pCommitArg; /* Argument to xCommitCallback() */
int (*xCommitCallback)(void*); /* Invoked at every commit. */
- void *pRollbackArg; /* Argument to xRollbackCallback() */
+ void *pRollbackArg; /* Argument to xRollbackCallback() */
void (*xRollbackCallback)(void*); /* Invoked at every commit. */
void *pUpdateArg;
void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ void *pPreUpdateArg; /* First argument to xPreUpdateCallback */
+ void (*xPreUpdateCallback)( /* Registered using sqlite3_preupdate_hook() */
+ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64
+ );
+ PreUpdate *pPreUpdate; /* Context for active pre-update callback */
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifndef SQLITE_OMIT_WAL
int (*xWalCallback)(void *, sqlite3 *, const char *, int);
void *pWalArg;
@@ -9498,45 +13970,41 @@ struct sqlite3 {
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
void *pCollNeededArg;
sqlite3_value *pErr; /* Most recent error message */
- char *zErrMsg; /* Most recent error message (UTF-8 encoded) */
- char *zErrMsg16; /* Most recent error message (UTF-16 encoded) */
union {
volatile int isInterrupted; /* True if sqlite3_interrupt has been called */
double notUsed1; /* Spacer */
} u1;
Lookaside lookaside; /* Lookaside malloc configuration */
#ifndef SQLITE_OMIT_AUTHORIZATION
- int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
- /* Access authorization function */
+ sqlite3_xauth xAuth; /* Access authorization function */
void *pAuthArg; /* 1st argument to the access auth function */
#endif
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
int (*xProgress)(void *); /* The progress callback */
void *pProgressArg; /* Argument to the progress callback */
- int nProgressOps; /* Number of opcodes for progress callback */
+ unsigned nProgressOps; /* Number of opcodes for progress callback */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
+ int nVTrans; /* Allocated size of aVTrans */
Hash aModule; /* populated by sqlite3_create_module() */
VtabCtx *pVtabCtx; /* Context for active vtab connect/create */
VTable **aVTrans; /* Virtual tables with open transactions */
- int nVTrans; /* Allocated size of aVTrans */
VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */
#endif
- FuncDefHash aFunc; /* Hash table of connection functions */
+ Hash aFunc; /* Hash table of connection functions */
Hash aCollSeq; /* All collating sequences */
BusyHandler busyHandler; /* Busy callback */
- int busyTimeout; /* Busy handler timeout, in msec */
Db aDbStatic[2]; /* Static space for the 2 default backends */
Savepoint *pSavepoint; /* List of active savepoints */
+ int busyTimeout; /* Busy handler timeout, in msec */
int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */
- u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */
i64 nDeferredCons; /* Net deferred constraints this transaction. */
+ i64 nDeferredImmCons; /* Net deferred immediate constraints */
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
-
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
- /* The following variables are all protected by the STATIC_MASTER
- ** mutex, not by sqlite3.mutex. They are used by code in notify.c.
+ /* The following variables are all protected by the STATIC_MASTER
+ ** mutex, not by sqlite3.mutex. They are used by code in notify.c.
**
** When X.pUnlockConnection==Y, that means that X is waiting for Y to
** unlock so that it can proceed.
@@ -9551,59 +14019,97 @@ struct sqlite3 {
void (*xUnlockNotify)(void **, int); /* Unlock notify callback */
sqlite3 *pNextBlocked; /* Next in list of all blocked connections */
#endif
+#ifdef SQLITE_USER_AUTHENTICATION
+ sqlite3_userauth auth; /* User authentication information */
+#endif
};
/*
** A macro to discover the encoding of a database.
*/
-#define ENC(db) ((db)->aDb[0].pSchema->enc)
+#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc)
+#define ENC(db) ((db)->enc)
/*
** Possible values for the sqlite3.flags.
-*/
-#define SQLITE_VdbeTrace 0x00000100 /* True to trace VDBE execution */
-#define SQLITE_InternChanges 0x00000200 /* Uncommitted Hash table changes */
-#define SQLITE_FullColNames 0x00000400 /* Show full column names on SELECT */
-#define SQLITE_ShortColNames 0x00000800 /* Show short columns names */
-#define SQLITE_CountRows 0x00001000 /* Count rows changed by INSERT, */
+**
+** Value constraints (enforced via assert()):
+** SQLITE_FullFSync == PAGER_FULLFSYNC
+** SQLITE_CkptFullFSync == PAGER_CKPT_FULLFSYNC
+** SQLITE_CacheSpill == PAGER_CACHE_SPILL
+*/
+#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */
+#define SQLITE_InternChanges 0x00000002 /* Uncommitted Hash table changes */
+#define SQLITE_FullColNames 0x00000004 /* Show full column names on SELECT */
+#define SQLITE_FullFSync 0x00000008 /* Use full fsync on the backend */
+#define SQLITE_CkptFullFSync 0x00000010 /* Use full fsync for checkpoint */
+#define SQLITE_CacheSpill 0x00000020 /* OK to spill pager cache */
+#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */
+#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */
/* DELETE, or UPDATE and return */
/* the count using a callback. */
-#define SQLITE_NullCallback 0x00002000 /* Invoke the callback once if the */
+#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */
/* result set is empty */
-#define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */
-#define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */
-#define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */
-#define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when
- ** accessing read-only databases */
-#define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */
-#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */
-#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */
-#define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */
-#define SQLITE_CkptFullFSync 0x00400000 /* Use full fsync for checkpoint */
-#define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */
-#define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */
-#define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */
-#define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */
-#define SQLITE_AutoIndex 0x08000000 /* Enable automatic indexes */
-#define SQLITE_PreferBuiltin 0x10000000 /* Preference to built-in funcs */
-#define SQLITE_LoadExtension 0x20000000 /* Enable load_extension */
-#define SQLITE_EnableTrigger 0x40000000 /* True to enable triggers */
-
-/*
-** Bits of the sqlite3.flags field that are used by the
-** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface.
-** These must be the low-order bits of the flags field.
-*/
-#define SQLITE_QueryFlattener 0x01 /* Disable query flattening */
-#define SQLITE_ColumnCache 0x02 /* Disable the column cache */
-#define SQLITE_IndexSort 0x04 /* Disable indexes for sorting */
-#define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */
-#define SQLITE_IndexCover 0x10 /* Disable index covering table */
-#define SQLITE_GroupByOrder 0x20 /* Disable GROUPBY cover of ORDERBY */
-#define SQLITE_FactorOutConst 0x40 /* Disable factoring out constants */
-#define SQLITE_IdxRealAsInt 0x80 /* Store REAL as INT in indices */
-#define SQLITE_DistinctOpt 0x80 /* DISTINCT using indexes */
-#define SQLITE_OptMask 0xff /* Mask of all disablable opts */
+#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */
+#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */
+#define SQLITE_WriteSchema 0x00000800 /* OK to update SQLITE_MASTER */
+#define SQLITE_VdbeAddopTrace 0x00001000 /* Trace sqlite3VdbeAddOp() calls */
+#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */
+#define SQLITE_ReadUncommitted 0x0004000 /* For shared-cache mode */
+#define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */
+#define SQLITE_RecoveryMode 0x00010000 /* Ignore schema errors */
+#define SQLITE_ReverseOrder 0x00020000 /* Reverse unordered SELECTs */
+#define SQLITE_RecTriggers 0x00040000 /* Enable recursive triggers */
+#define SQLITE_ForeignKeys 0x00080000 /* Enforce foreign key constraints */
+#define SQLITE_AutoIndex 0x00100000 /* Enable automatic indexes */
+#define SQLITE_PreferBuiltin 0x00200000 /* Preference to built-in funcs */
+#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */
+#define SQLITE_LoadExtFunc 0x00800000 /* Enable load_extension() SQL func */
+#define SQLITE_EnableTrigger 0x01000000 /* True to enable triggers */
+#define SQLITE_DeferFKs 0x02000000 /* Defer all FK constraints */
+#define SQLITE_QueryOnly 0x04000000 /* Disable database changes */
+#define SQLITE_VdbeEQP 0x08000000 /* Debug EXPLAIN QUERY PLAN */
+#define SQLITE_Vacuum 0x10000000 /* Currently in a VACUUM */
+#define SQLITE_CellSizeCk 0x20000000 /* Check btree cell sizes on load */
+#define SQLITE_Fts3Tokenizer 0x40000000 /* Enable fts3_tokenizer(2) */
+
+
+/*
+** Bits of the sqlite3.dbOptFlags field that are used by the
+** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
+** selectively disable various optimizations.
+*/
+#define SQLITE_QueryFlattener 0x0001 /* Query flattening */
+#define SQLITE_ColumnCache 0x0002 /* Column cache */
+#define SQLITE_GroupByOrder 0x0004 /* GROUPBY cover of ORDERBY */
+#define SQLITE_FactorOutConst 0x0008 /* Constant factoring */
+/* not used 0x0010 // Was: SQLITE_IdxRealAsInt */
+#define SQLITE_DistinctOpt 0x0020 /* DISTINCT using indexes */
+#define SQLITE_CoverIdxScan 0x0040 /* Covering index scans */
+#define SQLITE_OrderByIdxJoin 0x0080 /* ORDER BY of joins via index */
+#define SQLITE_SubqCoroutine 0x0100 /* Evaluate subqueries as coroutines */
+#define SQLITE_Transitive 0x0200 /* Transitive constraints */
+#define SQLITE_OmitNoopJoin 0x0400 /* Omit unused tables in joins */
+#define SQLITE_Stat34 0x0800 /* Use STAT3 or STAT4 data */
+#define SQLITE_CursorHints 0x2000 /* Add OP_CursorHint opcodes */
+#define SQLITE_AllOpts 0xffff /* All optimizations */
+
+/*
+** Macros for testing whether or not optimizations are enabled or disabled.
+*/
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+#define OptimizationDisabled(db, mask) (((db)->dbOptFlags&(mask))!=0)
+#define OptimizationEnabled(db, mask) (((db)->dbOptFlags&(mask))==0)
+#else
+#define OptimizationDisabled(db, mask) 0
+#define OptimizationEnabled(db, mask) 1
+#endif
+
+/*
+** Return true if it OK to factor constant expressions into the initialization
+** code. The argument is a Parse object for the code generator.
+*/
+#define ConstFactorOk(P) ((P)->okConstFactor)
/*
** Possible values for the sqlite.magic field.
@@ -9615,32 +14121,37 @@ struct sqlite3 {
#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
+#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */
/*
** Each SQL function is defined by an instance of the following
-** structure. A pointer to this structure is stored in the sqlite.aFunc
-** hash table. When multiple functions have the same name, the hash table
-** points to a linked list of these structures.
+** structure. For global built-in functions (ex: substr(), max(), count())
+** a pointer to this structure is held in the sqlite3BuiltinFunctions object.
+** For per-connection application-defined functions, a pointer to this
+** structure is held in the db->aHash hash table.
+**
+** The u.pHash field is used by the global built-ins. The u.pDestructor
+** field is used by per-connection app-def functions.
*/
struct FuncDef {
- i16 nArg; /* Number of arguments. -1 means unlimited */
- u8 iPrefEnc; /* Preferred text encoding (SQLITE_UTF8, 16LE, 16BE) */
- u8 flags; /* Some combination of SQLITE_FUNC_* */
+ i8 nArg; /* Number of arguments. -1 means unlimited */
+ u16 funcFlags; /* Some combination of SQLITE_FUNC_* */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**); /* Regular function */
- void (*xStep)(sqlite3_context*,int,sqlite3_value**); /* Aggregate step */
- void (*xFinalize)(sqlite3_context*); /* Aggregate finalizer */
- char *zName; /* SQL name of the function. */
- FuncDef *pHash; /* Next with a different name but the same hash */
- FuncDestructor *pDestructor; /* Reference counted destructor function */
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
+ void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
+ const char *zName; /* SQL name of the function. */
+ union {
+ FuncDef *pHash; /* Next with a different name but the same hash */
+ FuncDestructor *pDestructor; /* Reference counted destructor function */
+ } u;
};
/*
** This structure encapsulates a user-function destructor callback (as
** configured using create_function_v2()) and a reference counter. When
** create_function_v2() is called to create a function with a destructor,
-** a single object of this type is allocated. FuncDestructor.nRef is set to
+** a single object of this type is allocated. FuncDestructor.nRef is set to
** the number of FuncDef objects created (either 1 or 3, depending on whether
** or not the specified encoding is SQLITE_ANY). The FuncDef.pDestructor
** member of each of the new FuncDef objects is set to point to the allocated
@@ -9657,27 +14168,53 @@ struct FuncDestructor {
};
/*
-** Possible values for FuncDef.flags
-*/
-#define SQLITE_FUNC_LIKE 0x01 /* Candidate for the LIKE optimization */
-#define SQLITE_FUNC_CASE 0x02 /* Case-sensitive LIKE-type function */
-#define SQLITE_FUNC_EPHEM 0x04 /* Ephemeral. Delete with VDBE */
-#define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */
-#define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */
-#define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */
-#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */
+** Possible values for FuncDef.flags. Note that the _LENGTH and _TYPEOF
+** values must correspond to OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG. And
+** SQLITE_FUNC_CONSTANT must be the same as SQLITE_DETERMINISTIC. There
+** are assert() statements in the code to verify this.
+**
+** Value constraints (enforced via assert()):
+** SQLITE_FUNC_MINMAX == NC_MinMaxAgg == SF_MinMaxAgg
+** SQLITE_FUNC_LENGTH == OPFLAG_LENGTHARG
+** SQLITE_FUNC_TYPEOF == OPFLAG_TYPEOFARG
+** SQLITE_FUNC_CONSTANT == SQLITE_DETERMINISTIC from the API
+** SQLITE_FUNC_ENCMASK depends on SQLITE_UTF* macros in the API
+*/
+#define SQLITE_FUNC_ENCMASK 0x0003 /* SQLITE_UTF8, SQLITE_UTF16BE or UTF16LE */
+#define SQLITE_FUNC_LIKE 0x0004 /* Candidate for the LIKE optimization */
+#define SQLITE_FUNC_CASE 0x0008 /* Case-sensitive LIKE-type function */
+#define SQLITE_FUNC_EPHEM 0x0010 /* Ephemeral. Delete with VDBE */
+#define SQLITE_FUNC_NEEDCOLL 0x0020 /* sqlite3GetFuncCollSeq() might be called*/
+#define SQLITE_FUNC_LENGTH 0x0040 /* Built-in length() function */
+#define SQLITE_FUNC_TYPEOF 0x0080 /* Built-in typeof() function */
+#define SQLITE_FUNC_COUNT 0x0100 /* Built-in count(*) aggregate */
+#define SQLITE_FUNC_COALESCE 0x0200 /* Built-in coalesce() or ifnull() */
+#define SQLITE_FUNC_UNLIKELY 0x0400 /* Built-in unlikely() function */
+#define SQLITE_FUNC_CONSTANT 0x0800 /* Constant inputs give a constant output */
+#define SQLITE_FUNC_MINMAX 0x1000 /* True for min() and max() aggregates */
+#define SQLITE_FUNC_SLOCHNG 0x2000 /* "Slow Change". Value constant during a
+ ** single query - might change over time */
/*
** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are
** used to create the initializers for the FuncDef structures.
**
** FUNCTION(zName, nArg, iArg, bNC, xFunc)
-** Used to create a scalar function definition of a function zName
+** Used to create a scalar function definition of a function zName
** implemented by C function xFunc that accepts nArg arguments. The
** value passed as iArg is cast to a (void*) and made available
-** as the user-data (sqlite3_user_data()) for the function. If
+** as the user-data (sqlite3_user_data()) for the function. If
** argument bNC is true, then the SQLITE_FUNC_NEEDCOLL flag is set.
**
+** VFUNCTION(zName, nArg, iArg, bNC, xFunc)
+** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag.
+**
+** DFUNCTION(zName, nArg, iArg, bNC, xFunc)
+** Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and
+** adds the SQLITE_FUNC_SLOCHNG flag. Used for date & time functions
+** and functions like sqlite_version() that can change, but not during
+** a single query.
+**
** AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
** Used to create an aggregate function definition implemented by
** the C functions xStep and xFinal. The first four parameters
@@ -9685,24 +14222,37 @@ struct FuncDestructor {
** FUNCTION().
**
** LIKEFUNC(zName, nArg, pArg, flags)
-** Used to create a scalar function definition of a function zName
-** that accepts nArg arguments and is implemented by a call to C
+** Used to create a scalar function definition of a function zName
+** that accepts nArg arguments and is implemented by a call to C
** function likeFunc. Argument pArg is cast to a (void *) and made
** available as the function user-data (sqlite3_user_data()). The
** FuncDef.flags variable is set to the value passed as the flags
** parameter.
*/
#define FUNCTION(zName, nArg, iArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
- SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, #zName, 0, 0}
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
+#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
+ {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
+#define DFUNCTION(zName, nArg, iArg, bNC, xFunc) \
+ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
+#define FUNCTION2(zName, nArg, iArg, bNC, xFunc, extraFlags) \
+ {nArg,SQLITE_FUNC_CONSTANT|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL)|extraFlags,\
+ SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, #zName, {0} }
#define STR_FUNCTION(zName, nArg, pArg, bNC, xFunc) \
- {nArg, SQLITE_UTF8, bNC*SQLITE_FUNC_NEEDCOLL, \
- pArg, 0, xFunc, 0, 0, #zName, 0, 0}
+ {nArg, SQLITE_FUNC_SLOCHNG|SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
+ pArg, 0, xFunc, 0, #zName, }
#define LIKEFUNC(zName, nArg, arg, flags) \
- {nArg, SQLITE_UTF8, flags, (void *)arg, 0, likeFunc, 0, 0, #zName, 0, 0}
+ {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8|flags, \
+ (void *)arg, 0, likeFunc, 0, #zName, {0} }
#define AGGREGATE(zName, nArg, arg, nc, xStep, xFinal) \
- {nArg, SQLITE_UTF8, nc*SQLITE_FUNC_NEEDCOLL, \
- SQLITE_INT_TO_PTR(arg), 0, 0, xStep,xFinal,#zName,0,0}
+ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL), \
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
+#define AGGREGATE2(zName, nArg, arg, nc, xStep, xFinal, extraFlags) \
+ {nArg, SQLITE_UTF8|(nc*SQLITE_FUNC_NEEDCOLL)|extraFlags, \
+ SQLITE_INT_TO_PTR(arg), 0, xStep,xFinal,#zName, {0}}
/*
** All current savepoints are stored in a linked list starting at
@@ -9713,6 +14263,7 @@ struct FuncDestructor {
struct Savepoint {
char *zName; /* Savepoint name (nul-terminated) */
i64 nDeferredCons; /* Number of deferred fk violations */
+ i64 nDeferredImmCons; /* Number of deferred imm fk. */
Savepoint *pNext; /* Parent savepoint (if any) */
};
@@ -9735,6 +14286,7 @@ struct Module {
const char *zName; /* Name passed to create_module() */
void *pAux; /* pAux passed to create_module() */
void (*xDestroy)(void *); /* Module destructor function */
+ Table *pEpoTab; /* Eponymous table for this module */
};
/*
@@ -9742,115 +14294,104 @@ struct Module {
** of this structure.
*/
struct Column {
- char *zName; /* Name of this column */
+ char *zName; /* Name of this column, \000, then the type */
Expr *pDflt; /* Default value of this column */
- char *zDflt; /* Original text of the default value */
- char *zType; /* Data type for this column */
char *zColl; /* Collating sequence. If NULL, use the default */
- u8 notNull; /* True if there is a NOT NULL constraint */
- u8 isPrimKey; /* True if this column is part of the PRIMARY KEY */
+ u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
char affinity; /* One of the SQLITE_AFF_... values */
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- u8 isHidden; /* True if this column is 'hidden' */
-#endif
+ u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */
+ u8 colFlags; /* Boolean properties. See COLFLAG_ defines below */
};
+/* Allowed values for Column.colFlags:
+*/
+#define COLFLAG_PRIMKEY 0x0001 /* Column is part of the primary key */
+#define COLFLAG_HIDDEN 0x0002 /* A hidden column in a virtual table */
+#define COLFLAG_HASTYPE 0x0004 /* Type name follows column name */
+
/*
** A "Collating Sequence" is defined by an instance of the following
** structure. Conceptually, a collating sequence consists of a name and
** a comparison routine that defines the order of that sequence.
**
-** There may two separate implementations of the collation function, one
-** that processes text in UTF-8 encoding (CollSeq.xCmp) and another that
-** processes text encoded in UTF-16 (CollSeq.xCmp16), using the machine
-** native byte order. When a collation sequence is invoked, SQLite selects
-** the version that will require the least expensive encoding
-** translations, if any.
-**
-** The CollSeq.pUser member variable is an extra parameter that passed in
-** as the first argument to the UTF-8 comparison function, xCmp.
-** CollSeq.pUser16 is the equivalent for the UTF-16 comparison function,
-** xCmp16.
-**
-** If both CollSeq.xCmp and CollSeq.xCmp16 are NULL, it means that the
+** If CollSeq.xCmp is NULL, it means that the
** collating sequence is undefined. Indices built on an undefined
** collating sequence may not be read or written.
*/
struct CollSeq {
char *zName; /* Name of the collating sequence, UTF-8 encoded */
u8 enc; /* Text encoding handled by xCmp() */
- u8 type; /* One of the SQLITE_COLL_... values below */
void *pUser; /* First argument to xCmp() */
int (*xCmp)(void*,int, const void*, int, const void*);
void (*xDel)(void*); /* Destructor for pUser */
};
/*
-** Allowed values of CollSeq.type:
-*/
-#define SQLITE_COLL_BINARY 1 /* The default memcmp() collating sequence */
-#define SQLITE_COLL_NOCASE 2 /* The built-in NOCASE collating sequence */
-#define SQLITE_COLL_REVERSE 3 /* The built-in REVERSE collating sequence */
-#define SQLITE_COLL_USER 0 /* Any other user-defined collating sequence */
-
-/*
** A sort order can be either ASC or DESC.
*/
#define SQLITE_SO_ASC 0 /* Sort in ascending order */
#define SQLITE_SO_DESC 1 /* Sort in ascending order */
+#define SQLITE_SO_UNDEFINED -1 /* No sort order specified */
/*
** Column affinity types.
**
** These used to have mnemonic name like 'i' for SQLITE_AFF_INTEGER and
** 't' for SQLITE_AFF_TEXT. But we can save a little space and improve
-** the speed a little by numbering the values consecutively.
+** the speed a little by numbering the values consecutively.
**
-** But rather than start with 0 or 1, we begin with 'a'. That way,
+** But rather than start with 0 or 1, we begin with 'A'. That way,
** when multiple affinity types are concatenated into a string and
** used as the P4 operand, they will be more readable.
**
** Note also that the numeric types are grouped together so that testing
-** for a numeric type is a single comparison.
+** for a numeric type is a single comparison. And the BLOB type is first.
*/
-#define SQLITE_AFF_TEXT 'a'
-#define SQLITE_AFF_NONE 'b'
-#define SQLITE_AFF_NUMERIC 'c'
-#define SQLITE_AFF_INTEGER 'd'
-#define SQLITE_AFF_REAL 'e'
+#define SQLITE_AFF_BLOB 'A'
+#define SQLITE_AFF_TEXT 'B'
+#define SQLITE_AFF_NUMERIC 'C'
+#define SQLITE_AFF_INTEGER 'D'
+#define SQLITE_AFF_REAL 'E'
#define sqlite3IsNumericAffinity(X) ((X)>=SQLITE_AFF_NUMERIC)
/*
** The SQLITE_AFF_MASK values masks off the significant bits of an
-** affinity value.
+** affinity value.
*/
-#define SQLITE_AFF_MASK 0x67
+#define SQLITE_AFF_MASK 0x47
/*
** Additional bit values that can be ORed with an affinity without
** changing the affinity.
+**
+** The SQLITE_NOTNULL flag is a combination of NULLEQ and JUMPIFNULL.
+** It causes an assert() to fire if either operand to a comparison
+** operator is NULL. It is added to certain comparison operators to
+** prove that the operands are always NOT NULL.
*/
-#define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */
-#define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */
+#define SQLITE_KEEPNULL 0x08 /* Used by vector == or <> */
+#define SQLITE_JUMPIFNULL 0x10 /* jumps if either operand is NULL */
+#define SQLITE_STOREP2 0x20 /* Store result in reg[P2] rather than jump */
#define SQLITE_NULLEQ 0x80 /* NULL=NULL */
+#define SQLITE_NOTNULL 0x90 /* Assert that operands are never NULL */
/*
** An object of this type is created for each virtual table present in
-** the database schema.
+** the database schema.
**
** If the database schema is shared, then there is one instance of this
** structure for each database connection (sqlite3*) that uses the shared
** schema. This is because each database connection requires its own unique
-** instance of the sqlite3_vtab* handle used to access the virtual table
-** implementation. sqlite3_vtab* handles can not be shared between
-** database connections, even when the rest of the in-memory database
+** instance of the sqlite3_vtab* handle used to access the virtual table
+** implementation. sqlite3_vtab* handles can not be shared between
+** database connections, even when the rest of the in-memory database
** schema is shared, as the implementation often stores the database
** connection handle passed to it via the xConnect() or xCreate() method
** during initialization internally. This database connection handle may
-** then be used by the virtual table implementation to access real tables
-** within the database. So that they appear as part of the callers
-** transaction, these accesses need to be made via the same database
+** then be used by the virtual table implementation to access real tables
+** within the database. So that they appear as part of the callers
+** transaction, these accesses need to be made via the same database
** connection as that used to execute SQL operations on the virtual table.
**
** All VTable objects that correspond to a single table in a shared
@@ -9862,19 +14403,19 @@ struct CollSeq {
** sqlite3_vtab* handle in the compiled query.
**
** When an in-memory Table object is deleted (for example when the
-** schema is being reloaded for some reason), the VTable objects are not
-** deleted and the sqlite3_vtab* handles are not xDisconnect()ed
+** schema is being reloaded for some reason), the VTable objects are not
+** deleted and the sqlite3_vtab* handles are not xDisconnect()ed
** immediately. Instead, they are moved from the Table.pVTable list to
** another linked list headed by the sqlite3.pDisconnect member of the
-** corresponding sqlite3 structure. They are then deleted/xDisconnected
+** corresponding sqlite3 structure. They are then deleted/xDisconnected
** next time a statement is prepared using said sqlite3*. This is done
** to avoid deadlock issues involving multiple sqlite3.mutex mutexes.
** Refer to comments above function sqlite3VtabUnlockList() for an
** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect
** list without holding the corresponding sqlite3.mutex mutex.
**
-** The memory for objects of this type is always allocated by
-** sqlite3DbMalloc(), using the connection handle stored in VTable.db as
+** The memory for objects of this type is always allocated by
+** sqlite3DbMalloc(), using the connection handle stored in VTable.db as
** the first argument.
*/
struct VTable {
@@ -9888,59 +14429,36 @@ struct VTable {
};
/*
-** Each SQL table is represented in memory by an instance of the
-** following structure.
-**
-** Table.zName is the name of the table. The case of the original
-** CREATE TABLE statement is stored, but case is not significant for
-** comparisons.
-**
-** Table.nCol is the number of columns in this table. Table.aCol is a
-** pointer to an array of Column structures, one for each column.
-**
-** If the table has an INTEGER PRIMARY KEY, then Table.iPKey is the index of
-** the column that is that key. Otherwise Table.iPKey is negative. Note
-** that the datatype of the PRIMARY KEY must be INTEGER for this field to
-** be set. An INTEGER PRIMARY KEY is used as the rowid for each row of
-** the table. If a table has no INTEGER PRIMARY KEY, then a random rowid
-** is generated for each row of the table. TF_HasPrimaryKey is set if
-** the table has any PRIMARY KEY, INTEGER or otherwise.
-**
-** Table.tnum is the page number for the root BTree page of the table in the
-** database file. If Table.iDb is the index of the database table backend
-** in sqlite.aDb[]. 0 is for the main database and 1 is for the file that
-** holds temporary tables and indices. If TF_Ephemeral is set
-** then the table is stored in a file that is automatically deleted
-** when the VDBE cursor to the table is closed. In this case Table.tnum
-** refers VDBE cursor number that holds the table open, not to the root
-** page number. Transient tables are used to hold the results of a
-** sub-query that appears instead of a real table name in the FROM clause
-** of a SELECT statement.
+** The schema for each SQL table and view is represented in memory
+** by an instance of the following structure.
*/
struct Table {
char *zName; /* Name of the table or view */
- int iPKey; /* If not negative, use aCol[iPKey] as the primary key */
- int nCol; /* Number of columns in this table */
Column *aCol; /* Information about each column */
Index *pIndex; /* List of SQL indexes on this table. */
- int tnum; /* Root BTree node for this table (see note above) */
- unsigned nRowEst; /* Estimated rows in table - from sqlite_stat1 table */
Select *pSelect; /* NULL for tables. Points to definition if a view. */
- u16 nRef; /* Number of pointers to this Table */
- u8 tabFlags; /* Mask of TF_* values */
- u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
FKey *pFKey; /* Linked list of all foreign keys in this table */
char *zColAff; /* String defining the affinity of each column */
-#ifndef SQLITE_OMIT_CHECK
- Expr *pCheck; /* The AND of all CHECK constraints */
+ ExprList *pCheck; /* All CHECK constraints */
+ /* ... also used as column name list in a VIEW */
+ int tnum; /* Root BTree page for this table */
+ i16 iPKey; /* If not negative, use aCol[iPKey] as the rowid */
+ i16 nCol; /* Number of columns in this table */
+ u16 nRef; /* Number of pointers to this Table */
+ LogEst nRowLogEst; /* Estimated rows in table - from sqlite_stat1 table */
+ LogEst szTabRow; /* Estimated size of each table row in bytes */
+#ifdef SQLITE_ENABLE_COSTMULT
+ LogEst costMult; /* Cost multiplier for using this table */
#endif
+ u8 tabFlags; /* Mask of TF_* values */
+ u8 keyConf; /* What to do in case of uniqueness conflict on iPKey */
#ifndef SQLITE_OMIT_ALTERTABLE
int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
- VTable *pVTable; /* List of VTable objects. */
int nModuleArg; /* Number of arguments to the module */
- char **azModuleArg; /* Text of all module args. [0] is module name */
+ char **azModuleArg; /* 0: module 1: schema 2: vtab name 3...: args */
+ VTable *pVTable; /* List of VTable objects. */
#endif
Trigger *pTrigger; /* List of triggers stored in pSchema */
Schema *pSchema; /* Schema that contains this table */
@@ -9948,15 +14466,22 @@ struct Table {
};
/*
-** Allowed values for Tabe.tabFlags.
+** Allowed values for Table.tabFlags.
+**
+** TF_OOOHidden applies to tables or view that have hidden columns that are
+** followed by non-hidden columns. Example: "CREATE VIRTUAL TABLE x USING
+** vtab1(a HIDDEN, b);". Since "b" is a non-hidden column but "a" is hidden,
+** the TF_OOOHidden attribute would apply in this case. Such tables require
+** special handling during INSERT processing.
*/
#define TF_Readonly 0x01 /* Read-only system table */
#define TF_Ephemeral 0x02 /* An ephemeral table */
#define TF_HasPrimaryKey 0x04 /* Table has a primary key */
#define TF_Autoincrement 0x08 /* Integer primary key is autoincrement */
#define TF_Virtual 0x10 /* Is a virtual table */
-#define TF_NeedMetadata 0x20 /* aCol[].zType and aCol[].pColl missing */
-
+#define TF_WithoutRowid 0x20 /* No rowid. PRIMARY KEY is the key */
+#define TF_NoVisibleRowid 0x40 /* No user-visible "rowid" column */
+#define TF_OOOHidden 0x80 /* Out-of-Order hidden columns */
/*
@@ -9966,13 +14491,33 @@ struct Table {
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
# define IsVirtual(X) (((X)->tabFlags & TF_Virtual)!=0)
-# define IsHiddenColumn(X) ((X)->isHidden)
#else
# define IsVirtual(X) 0
-# define IsHiddenColumn(X) 0
#endif
/*
+** Macros to determine if a column is hidden. IsOrdinaryHiddenColumn()
+** only works for non-virtual tables (ordinary tables and views) and is
+** always false unless SQLITE_ENABLE_HIDDEN_COLUMNS is defined. The
+** IsHiddenColumn() macro is general purpose.
+*/
+#if defined(SQLITE_ENABLE_HIDDEN_COLUMNS)
+# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
+# define IsOrdinaryHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
+#elif !defined(SQLITE_OMIT_VIRTUALTABLE)
+# define IsHiddenColumn(X) (((X)->colFlags & COLFLAG_HIDDEN)!=0)
+# define IsOrdinaryHiddenColumn(X) 0
+#else
+# define IsHiddenColumn(X) 0
+# define IsOrdinaryHiddenColumn(X) 0
+#endif
+
+
+/* Does the table have a rowid */
+#define HasRowid(X) (((X)->tabFlags & TF_WithoutRowid)==0)
+#define VisibleRowid(X) (((X)->tabFlags & TF_NoVisibleRowid)==0)
+
+/*
** Each foreign key constraint is an instance of the following structure.
**
** A foreign key is associated with two tables. The "from" table is
@@ -9986,26 +14531,35 @@ struct Table {
** );
**
** For foreign key "fk1", the from-table is "ex1" and the to-table is "ex2".
+** Equivalent names:
+**
+** from-table == child-table
+** to-table == parent-table
**
** Each REFERENCES clause generates an instance of the following structure
** which is attached to the from-table. The to-table need not exist when
** the from-table is created. The existence of the to-table is not checked.
+**
+** The list of all parents for child Table X is held at X.pFKey.
+**
+** A list of all children for a table named Z (which might not even exist)
+** is held in Schema.fkeyHash with a hash key of Z.
*/
struct FKey {
Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */
- FKey *pNextFrom; /* Next foreign key in pFrom */
+ FKey *pNextFrom; /* Next FKey with the same in pFrom. Next parent of pFrom */
char *zTo; /* Name of table that the key points to (aka: Parent) */
- FKey *pNextTo; /* Next foreign key on table named zTo */
- FKey *pPrevTo; /* Previous foreign key on table named zTo */
+ FKey *pNextTo; /* Next with the same zTo. Next child of zTo. */
+ FKey *pPrevTo; /* Previous with the same zTo */
int nCol; /* Number of columns in this key */
/* EV: R-30323-21917 */
- u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
- u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
- Trigger *apTrigger[2]; /* Triggers for aAction[] actions */
- struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
- int iFrom; /* Index of column in pFrom */
- char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */
- } aCol[1]; /* One entry for each of nCol column s */
+ u8 isDeferred; /* True if constraint checking is deferred till COMMIT */
+ u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
+ Trigger *apTrigger[2];/* Triggers for aAction[] actions */
+ struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
+ int iFrom; /* Index of column in pFrom */
+ char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
+ } aCol[1]; /* One entry for each of nCol columns */
};
/*
@@ -10029,7 +14583,7 @@ struct FKey {
** key is set to NULL. CASCADE means that a DELETE or UPDATE of the
** referenced table row is propagated into the row that holds the
** foreign key.
-**
+**
** The following symbolic values are used to record which type
** of action to take.
*/
@@ -10045,26 +14599,31 @@ struct FKey {
#define OE_SetDflt 8 /* Set the foreign key value to its default */
#define OE_Cascade 9 /* Cascade the changes */
-#define OE_Default 99 /* Do whatever the default action is */
+#define OE_Default 10 /* Do whatever the default action is */
/*
** An instance of the following structure is passed as the first
-** argument to sqlite3VdbeKeyCompare and is used to control the
+** argument to sqlite3VdbeKeyCompare and is used to control the
** comparison of the two index keys.
+**
+** Note that aSortOrder[] and aColl[] have nField+1 slots. There
+** are nField slots for the columns of an index then one extra slot
+** for the rowid at the end.
*/
struct KeyInfo {
- sqlite3 *db; /* The database connection */
+ u32 nRef; /* Number of references to this KeyInfo object */
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
- u16 nField; /* Number of entries in aColl[] */
- u8 *aSortOrder; /* Sort order for each column. May be NULL */
+ u16 nField; /* Number of key columns in the index */
+ u16 nXField; /* Number of columns beyond the key columns */
+ sqlite3 *db; /* The database connection */
+ u8 *aSortOrder; /* Sort order for each column. */
CollSeq *aColl[1]; /* Collating sequence for each term of the key */
};
/*
-** An instance of the following structure holds information about a
-** single index record that has already been parsed out into individual
-** values.
+** This object holds a record which has been parsed out into individual
+** fields, for the purposes of doing a comparison.
**
** A record is an object that contains one or more fields of data.
** Records are used to store the content of a table row and to store
@@ -10072,26 +14631,42 @@ struct KeyInfo {
** the OP_MakeRecord opcode of the VDBE and is disassembled by the
** OP_Column opcode.
**
-** This structure holds a record that has already been disassembled
-** into its constituent fields.
+** An instance of this object serves as a "key" for doing a search on
+** an index b+tree. The goal of the search is to find the entry that
+** is closed to the key described by this object. This object might hold
+** just a prefix of the key. The number of fields is given by
+** pKeyInfo->nField.
+**
+** The r1 and r2 fields are the values to return if this key is less than
+** or greater than a key in the btree, respectively. These are normally
+** -1 and +1 respectively, but might be inverted to +1 and -1 if the b-tree
+** is in DESC order.
+**
+** The key comparison functions actually return default_rc when they find
+** an equals comparison. default_rc can be -1, 0, or +1. If there are
+** multiple entries in the b-tree with the same key (when only looking
+** at the first pKeyInfo->nFields,) then default_rc can be set to -1 to
+** cause the search to find the last match, or +1 to cause the search to
+** find the first match.
+**
+** The key comparison functions will set eqSeen to true if they ever
+** get and equal results when comparing this structure to a b-tree record.
+** When default_rc!=0, the search might end up on the record immediately
+** before the first match or immediately after the last match. The
+** eqSeen field will indicate whether or not an exact match exists in the
+** b-tree.
*/
struct UnpackedRecord {
KeyInfo *pKeyInfo; /* Collation and sort-order information */
- u16 nField; /* Number of entries in apMem[] */
- u16 flags; /* Boolean settings. UNPACKED_... below */
- i64 rowid; /* Used by UNPACKED_PREFIX_SEARCH */
Mem *aMem; /* Values */
+ u16 nField; /* Number of entries in apMem[] */
+ i8 default_rc; /* Comparison result if keys are equal */
+ u8 errCode; /* Error detected by xRecordCompare (CORRUPT or NOMEM) */
+ i8 r1; /* Value to return if (lhs > rhs) */
+ i8 r2; /* Value to return if (rhs < lhs) */
+ u8 eqSeen; /* True if an equality comparison has been seen */
};
-/*
-** Allowed values of UnpackedRecord.flags
-*/
-#define UNPACKED_NEED_FREE 0x0001 /* Memory is from sqlite3Malloc() */
-#define UNPACKED_NEED_DESTROY 0x0002 /* apMem[]s should all be destroyed */
-#define UNPACKED_IGNORE_ROWID 0x0004 /* Ignore trailing rowid on key1 */
-#define UNPACKED_INCRKEY 0x0008 /* Make this key an epsilon larger */
-#define UNPACKED_PREFIX_MATCH 0x0010 /* A prefix match is considered OK */
-#define UNPACKED_PREFIX_SEARCH 0x0020 /* A prefix match is considered OK */
/*
** Each SQL index is represented in memory by an
@@ -10107,7 +14682,7 @@ struct UnpackedRecord {
** In the Table structure describing Ex1, nCol==3 because there are
** three columns in the table. In the Index structure describing
** Ex2, nColumn==2 since 2 of the 3 columns of Ex1 are indexed.
-** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the
+** The value of aiColumn is {2, 0}. aiColumn[0]==2 because the
** first column to be indexed (c3) has an index of 2 in Ex1.aCol[].
** The second column to be indexed (c1) has an index of 0 in
** Ex1.aCol[], hence Ex2.aiColumn[1]==0.
@@ -10115,39 +14690,81 @@ struct UnpackedRecord {
** The Index.onError field determines whether or not the indexed columns
** must be unique and what to do if they are not. When Index.onError=OE_None,
** it means this is not a unique index. Otherwise it is a unique index
-** and the value of Index.onError indicate the which conflict resolution
+** and the value of Index.onError indicate the which conflict resolution
** algorithm to employ whenever an attempt is made to insert a non-unique
** element.
+**
+** While parsing a CREATE TABLE or CREATE INDEX statement in order to
+** generate VDBE code (as opposed to parsing one read from an sqlite_master
+** table as part of parsing an existing database schema), transient instances
+** of this structure may be created. In this case the Index.tnum variable is
+** used to store the address of a VDBE instruction, not a database page
+** number (it cannot - the database page is not allocated until the VDBE
+** program is executed). See convertToWithoutRowidTable() for details.
*/
struct Index {
- char *zName; /* Name of this index */
- int nColumn; /* Number of columns in the table used by this index */
- int *aiColumn; /* Which columns are used by this index. 1st is 0 */
- unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */
- Table *pTable; /* The SQL table being indexed */
- int tnum; /* Page containing root of this index in database file */
- u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
- u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */
- u8 bUnordered; /* Use this index for == or IN queries only */
- char *zColAff; /* String defining the affinity of each column */
- Index *pNext; /* The next index associated with the same table */
- Schema *pSchema; /* Schema containing this index */
- u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */
- char **azColl; /* Array of collation sequence names for index */
- IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */
+ char *zName; /* Name of this index */
+ i16 *aiColumn; /* Which columns are used by this index. 1st is 0 */
+ LogEst *aiRowLogEst; /* From ANALYZE: Est. rows selected by each column */
+ Table *pTable; /* The SQL table being indexed */
+ char *zColAff; /* String defining the affinity of each column */
+ Index *pNext; /* The next index associated with the same table */
+ Schema *pSchema; /* Schema containing this index */
+ u8 *aSortOrder; /* for each column: True==DESC, False==ASC */
+ const char **azColl; /* Array of collation sequence names for index */
+ Expr *pPartIdxWhere; /* WHERE clause for partial indices */
+ ExprList *aColExpr; /* Column expressions */
+ int tnum; /* DB Page containing root of this index */
+ LogEst szIdxRow; /* Estimated average row size in bytes */
+ u16 nKeyCol; /* Number of columns forming the key */
+ u16 nColumn; /* Number of columns stored in the index */
+ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
+ unsigned idxType:2; /* 1==UNIQUE, 2==PRIMARY KEY, 0==CREATE INDEX */
+ unsigned bUnordered:1; /* Use this index for == or IN queries only */
+ unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
+ unsigned isResized:1; /* True if resizeIndexObject() has been called */
+ unsigned isCovering:1; /* True if this is a covering index */
+ unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int nSample; /* Number of elements in aSample[] */
+ int nSampleCol; /* Size of IndexSample.anEq[] and so on */
+ tRowcnt *aAvgEq; /* Average nEq values for keys not in aSample */
+ IndexSample *aSample; /* Samples of the left-most key */
+ tRowcnt *aiRowEst; /* Non-logarithmic stat1 data for this index */
+ tRowcnt nRowEst0; /* Non-logarithmic number of rows in the index */
+#endif
};
/*
-** Each sample stored in the sqlite_stat2 table is represented in memory
-** using a structure of this type.
+** Allowed values for Index.idxType
+*/
+#define SQLITE_IDXTYPE_APPDEF 0 /* Created using CREATE INDEX */
+#define SQLITE_IDXTYPE_UNIQUE 1 /* Implements a UNIQUE constraint */
+#define SQLITE_IDXTYPE_PRIMARYKEY 2 /* Is the PRIMARY KEY for the table */
+
+/* Return true if index X is a PRIMARY KEY index */
+#define IsPrimaryKeyIndex(X) ((X)->idxType==SQLITE_IDXTYPE_PRIMARYKEY)
+
+/* Return true if index X is a UNIQUE index */
+#define IsUniqueIndex(X) ((X)->onError!=OE_None)
+
+/* The Index.aiColumn[] values are normally positive integer. But
+** there are some negative values that have special meaning:
+*/
+#define XN_ROWID (-1) /* Indexed column is the rowid */
+#define XN_EXPR (-2) /* Indexed column is an expression */
+
+/*
+** Each sample stored in the sqlite_stat3 table is represented in memory
+** using a structure of this type. See documentation at the top of the
+** analyze.c source file for additional information.
*/
struct IndexSample {
- union {
- char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */
- double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */
- } u;
- u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */
- u8 nByte; /* Size in byte of text or blob. */
+ void *p; /* Pointer to sampled record */
+ int n; /* Size of record in bytes */
+ tRowcnt *anEq; /* Est. number of rows where the key equals this sample */
+ tRowcnt *anLt; /* Est. number of rows where key is less than this sample */
+ tRowcnt *anDLt; /* Est. number of distinct keys less than this sample */
};
/*
@@ -10183,8 +14800,9 @@ struct AggInfo {
** than the source table */
int sortingIdx; /* Cursor number of the sorting index */
int sortingIdxPTab; /* Cursor number of pseudo-table */
- ExprList *pGroupBy; /* The group by clause */
int nSortingColumn; /* Number of columns in the sorting index */
+ int mnReg, mxReg; /* Range of registers allocated for aCol and aFunc */
+ ExprList *pGroupBy; /* The group by clause */
struct AggInfo_col { /* For each column used in source tables */
Table *pTab; /* Source table */
int iTable; /* Cursor number of the source table */
@@ -10194,7 +14812,6 @@ struct AggInfo {
Expr *pExpr; /* The original expression */
} *aCol;
int nColumn; /* Number of used entries in aCol[] */
- int nColumnAlloc; /* Number of slots allocated for aCol[] */
int nAccumulator; /* Number of columns that show through to the output.
** Additional columns are used only as parameters to
** aggregate functions */
@@ -10205,7 +14822,6 @@ struct AggInfo {
int iDistinct; /* Ephemeral table used to enforce DISTINCT */
} *aFunc;
int nFunc; /* Number of entries in aFunc[] */
- int nFuncAlloc; /* Number of slots allocated for aFunc[] */
};
/*
@@ -10234,9 +14850,9 @@ typedef int ynVar;
** to represent the greater-than-or-equal-to operator in the expression
** tree.
**
-** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
+** If the expression is an SQL literal (TK_INTEGER, TK_FLOAT, TK_BLOB,
** or TK_STRING), then Expr.token contains the text of the SQL literal. If
-** the expression is a variable (TK_VARIABLE), then Expr.token contains the
+** the expression is a variable (TK_VARIABLE), then Expr.token contains the
** variable name. Finally, if the expression is an SQL function (TK_FUNCTION),
** then Expr.token contains the name of the function.
**
@@ -10247,7 +14863,7 @@ typedef int ynVar;
** a CASE expression or an IN expression of the form "<lhs> IN (<y>, <z>...)".
** Expr.x.pSelect is used if the expression is a sub-select or an expression of
** the form "<lhs> IN (SELECT ...)". If the EP_xIsSelect bit is set in the
-** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is
+** Expr.flags mask, then Expr.x.pSelect is valid. Otherwise, Expr.x.pList is
** valid.
**
** An expression of the form ID or ID.ID refers to a column in a table.
@@ -10258,8 +14874,8 @@ typedef int ynVar;
** value is also stored in the Expr.iAgg column in the aggregate so that
** it can be accessed after all aggregates are computed.
**
-** If the expression is an unbound variable marker (a question mark
-** character '?' in the original SQL) then the Expr.iTable holds the index
+** If the expression is an unbound variable marker (a question mark
+** character '?' in the original SQL) then the Expr.iTable holds the index
** number for that variable.
**
** If the expression is a subquery then Expr.iColumn holds an integer
@@ -10290,7 +14906,7 @@ typedef int ynVar;
struct Expr {
u8 op; /* Operation performed by this node */
char affinity; /* The affinity of the column or 0 if not a column */
- u16 flags; /* Various flags. EP_* See below */
+ u32 flags; /* Various flags. EP_* See below */
union {
char *zToken; /* Token value. Zero terminated and dequoted */
int iValue; /* Non-negative integer value if EP_IntValue */
@@ -10298,88 +14914,96 @@ struct Expr {
/* If the EP_TokenOnly flag is set in the Expr.flags mask, then no
** space is allocated for the fields below this point. An attempt to
- ** access them will result in a segfault or malfunction.
+ ** access them will result in a segfault or malfunction.
*********************************************************************/
Expr *pLeft; /* Left subnode */
Expr *pRight; /* Right subnode */
union {
- ExprList *pList; /* Function arguments or in "<expr> IN (<expr-list)" */
- Select *pSelect; /* Used for sub-selects and "<expr> IN (<select>)" */
+ ExprList *pList; /* op = IN, EXISTS, SELECT, CASE, FUNCTION, BETWEEN */
+ Select *pSelect; /* EP_xIsSelect and op = IN, EXISTS, SELECT */
} x;
- CollSeq *pColl; /* The collation type of the column or 0 */
/* If the EP_Reduced flag is set in the Expr.flags mask, then no
** space is allocated for the fields below this point. An attempt to
** access them will result in a segfault or malfunction.
*********************************************************************/
+#if SQLITE_MAX_EXPR_DEPTH>0
+ int nHeight; /* Height of the tree headed by this node */
+#endif
int iTable; /* TK_COLUMN: cursor number of table holding column
** TK_REGISTER: register number
- ** TK_TRIGGER: 1 -> new, 0 -> old */
+ ** TK_TRIGGER: 1 -> new, 0 -> old
+ ** EP_Unlikely: 134217728 times likelihood
+ ** TK_SELECT: 1st register of result vector */
ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid.
- ** TK_VARIABLE: variable number (always >= 1). */
+ ** TK_VARIABLE: variable number (always >= 1).
+ ** TK_SELECT_COLUMN: column of the result vector */
i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */
- u8 flags2; /* Second set of flags. EP2_... */
- u8 op2; /* If a TK_REGISTER, the original value of Expr.op */
+ u8 op2; /* TK_REGISTER: original value of Expr.op
+ ** TK_COLUMN: the value of p5 for OP_Column
+ ** TK_AGG_FUNCTION: nesting depth */
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
Table *pTab; /* Table for TK_COLUMN expressions. */
-#if SQLITE_MAX_EXPR_DEPTH>0
- int nHeight; /* Height of the tree headed by this node */
-#endif
};
/*
** The following are the meanings of bits in the Expr.flags field.
*/
-#define EP_FromJoin 0x0001 /* Originated in ON or USING clause of a join */
-#define EP_Agg 0x0002 /* Contains one or more aggregate functions */
-#define EP_Resolved 0x0004 /* IDs have been resolved to COLUMNs */
-#define EP_Error 0x0008 /* Expression contains one or more errors */
-#define EP_Distinct 0x0010 /* Aggregate function with DISTINCT keyword */
-#define EP_VarSelect 0x0020 /* pSelect is correlated, not constant */
-#define EP_DblQuoted 0x0040 /* token.z was originally in "..." */
-#define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */
-#define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */
-#define EP_FixedDest 0x0200 /* Result needed in a specific register */
-#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */
-#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */
-
-#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */
-#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
-#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */
-
-/*
-** The following are the meanings of bits in the Expr.flags2 field.
+#define EP_FromJoin 0x000001 /* Originates in ON/USING clause of outer join */
+#define EP_Agg 0x000002 /* Contains one or more aggregate functions */
+#define EP_Resolved 0x000004 /* IDs have been resolved to COLUMNs */
+#define EP_Error 0x000008 /* Expression contains one or more errors */
+#define EP_Distinct 0x000010 /* Aggregate function with DISTINCT keyword */
+#define EP_VarSelect 0x000020 /* pSelect is correlated, not constant */
+#define EP_DblQuoted 0x000040 /* token.z was originally in "..." */
+#define EP_InfixFunc 0x000080 /* True for an infix function: LIKE, GLOB, etc */
+#define EP_Collate 0x000100 /* Tree contains a TK_COLLATE operator */
+#define EP_Generic 0x000200 /* Ignore COLLATE or affinity on this tree */
+#define EP_IntValue 0x000400 /* Integer value contained in u.iValue */
+#define EP_xIsSelect 0x000800 /* x.pSelect is valid (otherwise x.pList is) */
+#define EP_Skip 0x001000 /* COLLATE, AS, or UNLIKELY */
+#define EP_Reduced 0x002000 /* Expr struct EXPR_REDUCEDSIZE bytes only */
+#define EP_TokenOnly 0x004000 /* Expr struct EXPR_TOKENONLYSIZE bytes only */
+#define EP_Static 0x008000 /* Held in memory not obtained from malloc() */
+#define EP_MemToken 0x010000 /* Need to sqlite3DbFree() Expr.zToken */
+#define EP_NoReduce 0x020000 /* Cannot EXPRDUP_REDUCE this Expr */
+#define EP_Unlikely 0x040000 /* unlikely() or likelihood() function */
+#define EP_ConstFunc 0x080000 /* A SQLITE_FUNC_CONSTANT or _SLOCHNG function */
+#define EP_CanBeNull 0x100000 /* Can be null despite NOT NULL constraint */
+#define EP_Subquery 0x200000 /* Tree contains a TK_SELECT operator */
+#define EP_Alias 0x400000 /* Is an alias for a result set column */
+#define EP_Leaf 0x800000 /* Expr.pLeft, .pRight, .u.pSelect all NULL */
+
+/*
+** Combinations of two or more EP_* flags
+*/
+#define EP_Propagate (EP_Collate|EP_Subquery) /* Propagate these bits up tree */
+
+/*
+** These macros can be used to test, set, or clear bits in the
+** Expr.flags field.
*/
-#define EP2_MallocedToken 0x0001 /* Need to sqlite3DbFree() Expr.zToken */
-#define EP2_Irreducible 0x0002 /* Cannot EXPRDUP_REDUCE this Expr */
+#define ExprHasProperty(E,P) (((E)->flags&(P))!=0)
+#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P))
+#define ExprSetProperty(E,P) (E)->flags|=(P)
+#define ExprClearProperty(E,P) (E)->flags&=~(P)
-/*
-** The pseudo-routine sqlite3ExprSetIrreducible sets the EP2_Irreducible
-** flag on an expression structure. This flag is used for VV&A only. The
-** routine is implemented as a macro that only works when in debugging mode,
-** so as not to burden production code.
+/* The ExprSetVVAProperty() macro is used for Verification, Validation,
+** and Accreditation only. It works like ExprSetProperty() during VVA
+** processes but is a no-op for delivery.
*/
#ifdef SQLITE_DEBUG
-# define ExprSetIrreducible(X) (X)->flags2 |= EP2_Irreducible
+# define ExprSetVVAProperty(E,P) (E)->flags|=(P)
#else
-# define ExprSetIrreducible(X)
+# define ExprSetVVAProperty(E,P)
#endif
/*
-** These macros can be used to test, set, or clear bits in the
-** Expr.flags field.
-*/
-#define ExprHasProperty(E,P) (((E)->flags&(P))==(P))
-#define ExprHasAnyProperty(E,P) (((E)->flags&(P))!=0)
-#define ExprSetProperty(E,P) (E)->flags|=(P)
-#define ExprClearProperty(E,P) (E)->flags&=~(P)
-
-/*
-** Macros to determine the number of bytes required by a normal Expr
-** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
+** Macros to determine the number of bytes required by a normal Expr
+** struct, an Expr struct with the EP_Reduced flag set in Expr.flags
** and an Expr struct with the EP_TokenOnly flag set.
*/
#define EXPR_FULLSIZE sizeof(Expr) /* Full size */
@@ -10387,7 +15011,7 @@ struct Expr {
#define EXPR_TOKENONLYSIZE offsetof(Expr,pLeft) /* Fewer features */
/*
-** Flags passed to the sqlite3ExprDup() function. See the header comment
+** Flags passed to the sqlite3ExprDup() function. See the header comment
** above sqlite3ExprDup() for details.
*/
#define EXPRDUP_REDUCE 0x0001 /* Used reduced-size Expr nodes */
@@ -10399,20 +15023,33 @@ struct Expr {
** list of "ID = expr" items in an UPDATE. A list of expressions can
** also be used as the argument to a function, in which case the a.zName
** field is not used.
+**
+** By default the Expr.zSpan field holds a human-readable description of
+** the expression that is used in the generation of error messages and
+** column labels. In this case, Expr.zSpan is typically the text of a
+** column expression as it exists in a SELECT statement. However, if
+** the bSpanIsTab flag is set, then zSpan is overloaded to mean the name
+** of the result column in the form: DATABASE.TABLE.COLUMN. This later
+** form is used for name resolution with nested FROM clauses.
*/
struct ExprList {
int nExpr; /* Number of expressions on the list */
- int nAlloc; /* Number of entries allocated below */
- int iECursor; /* VDBE Cursor associated with this ExprList */
- struct ExprList_item {
- Expr *pExpr; /* The list of expressions */
- char *zName; /* Token associated with this expression */
- char *zSpan; /* Original text of the expression */
- u8 sortOrder; /* 1 for DESC or 0 for ASC */
- u8 done; /* A flag to indicate when processing is finished */
- u16 iCol; /* For ORDER BY, column number in result set */
- u16 iAlias; /* Index into Parse.aAlias[] for zName */
- } *a; /* One entry for each expression */
+ struct ExprList_item { /* For each expression in the list */
+ Expr *pExpr; /* The list of expressions */
+ char *zName; /* Token associated with this expression */
+ char *zSpan; /* Original text of the expression */
+ u8 sortOrder; /* 1 for DESC or 0 for ASC */
+ unsigned done :1; /* A flag to indicate when processing is finished */
+ unsigned bSpanIsTab :1; /* zSpan holds DB.TABLE.COLUMN */
+ unsigned reusable :1; /* Constant expression is reusable */
+ union {
+ struct {
+ u16 iOrderByCol; /* For ORDER BY, column number in result set */
+ u16 iAlias; /* Index into Parse.aAlias[] for zName */
+ } x;
+ int iConstExprReg; /* Register in which Expr value is cached */
+ } u;
+ } *a; /* Alloc a power of two greater or equal to nExpr */
};
/*
@@ -10447,7 +15084,6 @@ struct IdList {
int idx; /* Index in some Table.aCol[] of a column named zName */
} *a;
int nId; /* Number of identifiers on the list */
- int nAlloc; /* Number of entries allocated for a[] below */
};
/*
@@ -10457,7 +15093,11 @@ struct IdList {
** tables in a join to 32 instead of 64. But it also reduces the size
** of the library by 738 bytes on ix86.
*/
-typedef u64 Bitmask;
+#ifdef SQLITE_BITMASK_TYPE
+ typedef SQLITE_BITMASK_TYPE Bitmask;
+#else
+ typedef u64 Bitmask;
+#endif
/*
** The number of bits in a Bitmask. "BMS" means "BitMask Size".
@@ -10465,6 +15105,13 @@ typedef u64 Bitmask;
#define BMS ((int)(sizeof(Bitmask)*8))
/*
+** A bit in a Bitmask
+*/
+#define MASKBIT(n) (((Bitmask)1)<<(n))
+#define MASKBIT32(n) (((unsigned int)1)<<(n))
+#define ALLBITS ((Bitmask)-1)
+
+/*
** The following structure describes the FROM clause of a SELECT statement.
** Each table or subquery in the FROM clause is a separate element of
** the SrcList.a[] array.
@@ -10484,9 +15131,10 @@ typedef u64 Bitmask;
** contains more than 63 columns and the 64-th or later column is used.
*/
struct SrcList {
- i16 nSrc; /* Number of tables or subqueries in the FROM clause */
- i16 nAlloc; /* Number of entries allocated in a[] below */
+ int nSrc; /* Number of tables or subqueries in the FROM clause */
+ u32 nAlloc; /* Number of entries allocated in a[] below */
struct SrcList_item {
+ Schema *pSchema; /* Schema to which this item is fixed */
char *zDatabase; /* Name of database holding this table */
char *zName; /* Name of the table */
char *zAlias; /* The "B" part of a "A AS B" phrase. zName is the "A" */
@@ -10494,9 +15142,16 @@ struct SrcList {
Select *pSelect; /* A SELECT statement used in place of a table name */
int addrFillSub; /* Address of subroutine to manifest a subquery */
int regReturn; /* Register holding return address of addrFillSub */
- u8 jointype; /* Type of join between this able and the previous */
- u8 notIndexed; /* True if there is a NOT INDEXED clause */
- u8 isCorrelated; /* True if sub-query is correlated */
+ int regResult; /* Registers holding results of a co-routine */
+ struct {
+ u8 jointype; /* Type of join between this table and the previous */
+ unsigned notIndexed :1; /* True if there is a NOT INDEXED clause */
+ unsigned isIndexedBy :1; /* True if there is an INDEXED BY clause */
+ unsigned isTabFunc :1; /* True if table-valued-function syntax */
+ unsigned isCorrelated :1; /* True if sub-query is correlated */
+ unsigned viaCoroutine :1; /* Implemented as a co-routine */
+ unsigned isRecursive :1; /* True for recursive reference in WITH */
+ } fg;
#ifndef SQLITE_OMIT_EXPLAIN
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
#endif
@@ -10504,8 +15159,11 @@ struct SrcList {
Expr *pOn; /* The ON clause of a join */
IdList *pUsing; /* The USING clause of a join */
Bitmask colUsed; /* Bit N (1<<N) set if column N of pTab is used */
- char *zIndex; /* Identifier from "INDEXED BY <zIndex>" clause */
- Index *pIndex; /* Index structure corresponding to zIndex, if any */
+ union {
+ char *zIndexedBy; /* Identifier from "INDEXED BY <zIndex>" clause */
+ ExprList *pFuncArg; /* Arguments to table-valued-function */
+ } u1;
+ Index *pIBIndex; /* Index structure corresponding to u1.zIndexedBy */
} a[1]; /* One entry for each identifier on the list */
};
@@ -10522,114 +15180,37 @@ struct SrcList {
/*
-** A WherePlan object holds information that describes a lookup
-** strategy.
-**
-** This object is intended to be opaque outside of the where.c module.
-** It is included here only so that that compiler will know how big it
-** is. None of the fields in this object should be used outside of
-** the where.c module.
-**
-** Within the union, pIdx is only used when wsFlags&WHERE_INDEXED is true.
-** pTerm is only used when wsFlags&WHERE_MULTI_OR is true. And pVtabIdx
-** is only used when wsFlags&WHERE_VIRTUALTABLE is true. It is never the
-** case that more than one of these conditions is true.
-*/
-struct WherePlan {
- u32 wsFlags; /* WHERE_* flags that describe the strategy */
- u32 nEq; /* Number of == constraints */
- double nRow; /* Estimated number of rows (for EQP) */
- union {
- Index *pIdx; /* Index when WHERE_INDEXED is true */
- struct WhereTerm *pTerm; /* WHERE clause term for OR-search */
- sqlite3_index_info *pVtabIdx; /* Virtual table index to use */
- } u;
-};
-
-/*
-** For each nested loop in a WHERE clause implementation, the WhereInfo
-** structure contains a single instance of this structure. This structure
-** is intended to be private the the where.c module and should not be
-** access or modified by other modules.
-**
-** The pIdxInfo field is used to help pick the best index on a
-** virtual table. The pIdxInfo pointer contains indexing
-** information for the i-th table in the FROM clause before reordering.
-** All the pIdxInfo pointers are freed by whereInfoFree() in where.c.
-** All other information in the i-th WhereLevel object for the i-th table
-** after FROM clause ordering.
-*/
-struct WhereLevel {
- WherePlan plan; /* query plan for this element of the FROM clause */
- int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
- int iTabCur; /* The VDBE cursor used to access the table */
- int iIdxCur; /* The VDBE cursor used to access pIdx */
- int addrBrk; /* Jump here to break out of the loop */
- int addrNxt; /* Jump here to start the next IN combination */
- int addrCont; /* Jump here to continue with the next loop cycle */
- int addrFirst; /* First instruction of interior of the loop */
- u8 iFrom; /* Which entry in the FROM clause */
- u8 op, p5; /* Opcode and P5 of the opcode that ends the loop */
- int p1, p2; /* Operands of the opcode used to ends the loop */
- union { /* Information that depends on plan.wsFlags */
- struct {
- int nIn; /* Number of entries in aInLoop[] */
- struct InLoop {
- int iCur; /* The VDBE cursor used by this IN operator */
- int addrInTop; /* Top of the IN loop */
- } *aInLoop; /* Information about each nested IN operator */
- } in; /* Used when plan.wsFlags&WHERE_IN_ABLE */
- } u;
-
- /* The following field is really not part of the current level. But
- ** we need a place to cache virtual table index information for each
- ** virtual table in the FROM clause and the WhereLevel structure is
- ** a convenient place since there is one WhereLevel for each FROM clause
- ** element.
- */
- sqlite3_index_info *pIdxInfo; /* Index info for n-th source table */
-};
-
-/*
** Flags appropriate for the wctrlFlags parameter of sqlite3WhereBegin()
** and the WhereInfo.wctrlFlags member.
+**
+** Value constraints (enforced via assert()):
+** WHERE_USE_LIMIT == SF_FixedLimit
*/
#define WHERE_ORDERBY_NORMAL 0x0000 /* No-op */
#define WHERE_ORDERBY_MIN 0x0001 /* ORDER BY processing for min() func */
#define WHERE_ORDERBY_MAX 0x0002 /* ORDER BY processing for max() func */
#define WHERE_ONEPASS_DESIRED 0x0004 /* Want to do one-pass UPDATE/DELETE */
-#define WHERE_DUPLICATES_OK 0x0008 /* Ok to return a row more than once */
-#define WHERE_OMIT_OPEN 0x0010 /* Table cursors are already open */
-#define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */
-#define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */
-#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */
-
-/*
-** The WHERE clause processing routine has two halves. The
-** first part does the start of the WHERE loop and the second
-** half does the tail of the WHERE loop. An instance of
-** this structure is returned by the first half and passed
-** into the second half to give some continuity.
-*/
-struct WhereInfo {
- Parse *pParse; /* Parsing and code generating context */
- u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
- u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */
- u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
- u8 eDistinct;
- SrcList *pTabList; /* List of tables in the join */
- int iTop; /* The very beginning of the WHERE loop */
- int iContinue; /* Jump here to continue with next record */
- int iBreak; /* Jump here to break out of the loop */
- int nLevel; /* Number of nested loop */
- struct WhereClause *pWC; /* Decomposition of the WHERE clause */
- double savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
- double nRowOut; /* Estimated number of output rows */
- WhereLevel a[1]; /* Information about each nest loop in WHERE */
-};
-
-#define WHERE_DISTINCT_UNIQUE 1
-#define WHERE_DISTINCT_ORDERED 2
+#define WHERE_ONEPASS_MULTIROW 0x0008 /* ONEPASS is ok with multiple rows */
+#define WHERE_DUPLICATES_OK 0x0010 /* Ok to return a row more than once */
+#define WHERE_OR_SUBCLAUSE 0x0020 /* Processing a sub-WHERE as part of
+ ** the OR optimization */
+#define WHERE_GROUPBY 0x0040 /* pOrderBy is really a GROUP BY */
+#define WHERE_DISTINCTBY 0x0080 /* pOrderby is really a DISTINCT clause */
+#define WHERE_WANT_DISTINCT 0x0100 /* All output needs to be distinct */
+#define WHERE_SORTBYGROUP 0x0200 /* Support sqlite3WhereIsSorted() */
+#define WHERE_SEEK_TABLE 0x0400 /* Do not defer seeks on main table */
+#define WHERE_ORDERBY_LIMIT 0x0800 /* ORDERBY+LIMIT on the inner loop */
+ /* 0x1000 not currently used */
+ /* 0x2000 not currently used */
+#define WHERE_USE_LIMIT 0x4000 /* Use the LIMIT in cost estimates */
+ /* 0x8000 not currently used */
+
+/* Allowed return values from sqlite3WhereIsDistinct()
+*/
+#define WHERE_DISTINCT_NOOP 0 /* DISTINCT keyword not used */
+#define WHERE_DISTINCT_UNIQUE 1 /* No duplicates */
+#define WHERE_DISTINCT_ORDERED 2 /* All duplicates are adjacent */
+#define WHERE_DISTINCT_UNORDERED 3 /* Duplicates are scattered */
/*
** A NameContext defines a context in which to resolve table and column
@@ -10640,12 +15221,12 @@ struct WhereInfo {
** pEList corresponds to the result set of a SELECT and is NULL for
** other statements.
**
-** NameContexts can be nested. When resolving names, the inner-most
+** NameContexts can be nested. When resolving names, the inner-most
** context is searched first. If no match is found, the next outer
** context is checked. If there is still no match, the next context
** is checked. This process continues until either a match is found
** or all contexts are check. When a match is found, the nRef member of
-** the context containing the match is incremented.
+** the context containing the match is incremented.
**
** Each subquery gets a new NameContext. The pNext field points to the
** NameContext in the parent query. Thus the process of scanning the
@@ -10655,18 +15236,32 @@ struct WhereInfo {
struct NameContext {
Parse *pParse; /* The parser */
SrcList *pSrcList; /* One or more tables used to resolve names */
- ExprList *pEList; /* Optional list of named expressions */
- int nRef; /* Number of names resolved by this context */
- int nErr; /* Number of errors encountered while resolving names */
- u8 allowAgg; /* Aggregate functions allowed here */
- u8 hasAgg; /* True if aggregates are seen */
- u8 isCheck; /* True if resolving names in a CHECK constraint */
- int nDepth; /* Depth of subquery recursion. 1 for no recursion */
+ ExprList *pEList; /* Optional list of result-set columns */
AggInfo *pAggInfo; /* Information about aggregates at this level */
NameContext *pNext; /* Next outer name context. NULL for outermost */
+ int nRef; /* Number of names resolved by this context */
+ int nErr; /* Number of errors encountered while resolving names */
+ u16 ncFlags; /* Zero or more NC_* flags defined below */
};
/*
+** Allowed values for the NameContext, ncFlags field.
+**
+** Value constraints (all checked via assert()):
+** NC_HasAgg == SF_HasAgg
+** NC_MinMaxAgg == SF_MinMaxAgg == SQLITE_FUNC_MINMAX
+**
+*/
+#define NC_AllowAgg 0x0001 /* Aggregate functions are allowed here */
+#define NC_PartIdx 0x0002 /* True if resolving a partial index WHERE */
+#define NC_IsCheck 0x0004 /* True if resolving names in a CHECK constraint */
+#define NC_InAggFunc 0x0008 /* True if analyzing arguments to an agg func */
+#define NC_HasAgg 0x0010 /* One or more aggregate functions seen */
+#define NC_IdxExpr 0x0020 /* True if resolving columns of CREATE INDEX */
+#define NC_VarSelect 0x0040 /* A correlated subquery has been seen */
+#define NC_MinMaxAgg 0x1000 /* min/max aggregates seen. See note above */
+
+/*
** An instance of the following structure contains all information
** needed to generate code for a single SELECT statement.
**
@@ -10683,14 +15278,19 @@ struct NameContext {
** as the OP_OpenEphm instruction is coded because not
** enough information about the compound query is known at that point.
** The KeyInfo for addrOpenTran[0] and [1] contains collating sequences
-** for the result set. The KeyInfo for addrOpenTran[2] contains collating
+** for the result set. The KeyInfo for addrOpenEphm[2] contains collating
** sequences for the ORDER BY clause.
*/
struct Select {
ExprList *pEList; /* The fields of the result */
u8 op; /* One of: TK_UNION TK_ALL TK_INTERSECT TK_EXCEPT */
- char affinity; /* MakeRecord with this affinity for SRT_Set */
- u16 selFlags; /* Various SF_* values */
+ LogEst nSelectRow; /* Estimated number of result rows */
+ u32 selFlags; /* Various SF_* values */
+ int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
+#if SELECTTRACE_ENABLED
+ char zSelName[12]; /* Symbolic name of this SELECT use for debugging */
+#endif
+ int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
SrcList *pSrc; /* The FROM clause */
Expr *pWhere; /* The WHERE clause */
ExprList *pGroupBy; /* The GROUP BY clause */
@@ -10698,65 +15298,142 @@ struct Select {
ExprList *pOrderBy; /* The ORDER BY clause */
Select *pPrior; /* Prior select in a compound select statement */
Select *pNext; /* Next select to the left in a compound */
- Select *pRightmost; /* Right-most select in a compound select statement */
Expr *pLimit; /* LIMIT expression. NULL means not used. */
Expr *pOffset; /* OFFSET expression. NULL means not used. */
- int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
- int addrOpenEphm[3]; /* OP_OpenEphem opcodes related to this select */
- double nSelectRow; /* Estimated number of result rows */
+ With *pWith; /* WITH clause attached to this select. Or NULL. */
};
/*
** Allowed values for Select.selFlags. The "SF" prefix stands for
** "Select Flag".
-*/
-#define SF_Distinct 0x0001 /* Output should be DISTINCT */
-#define SF_Resolved 0x0002 /* Identifiers have been resolved */
-#define SF_Aggregate 0x0004 /* Contains aggregate functions */
-#define SF_UsesEphemeral 0x0008 /* Uses the OpenEphemeral opcode */
-#define SF_Expanded 0x0010 /* sqlite3SelectExpand() called on this */
-#define SF_HasTypeInfo 0x0020 /* FROM subqueries have Table metadata */
-#define SF_UseSorter 0x0040 /* Sort using a sorter */
-
-
-/*
-** The results of a select can be distributed in several ways. The
-** "SRT" prefix means "SELECT Result Type".
+**
+** Value constraints (all checked via assert())
+** SF_HasAgg == NC_HasAgg
+** SF_MinMaxAgg == NC_MinMaxAgg == SQLITE_FUNC_MINMAX
+** SF_FixedLimit == WHERE_USE_LIMIT
+*/
+#define SF_Distinct 0x00001 /* Output should be DISTINCT */
+#define SF_All 0x00002 /* Includes the ALL keyword */
+#define SF_Resolved 0x00004 /* Identifiers have been resolved */
+#define SF_Aggregate 0x00008 /* Contains agg functions or a GROUP BY */
+#define SF_HasAgg 0x00010 /* Contains aggregate functions */
+#define SF_UsesEphemeral 0x00020 /* Uses the OpenEphemeral opcode */
+#define SF_Expanded 0x00040 /* sqlite3SelectExpand() called on this */
+#define SF_HasTypeInfo 0x00080 /* FROM subqueries have Table metadata */
+#define SF_Compound 0x00100 /* Part of a compound query */
+#define SF_Values 0x00200 /* Synthesized from VALUES clause */
+#define SF_MultiValue 0x00400 /* Single VALUES term with multiple rows */
+#define SF_NestedFrom 0x00800 /* Part of a parenthesized FROM clause */
+#define SF_MinMaxAgg 0x01000 /* Aggregate containing min() or max() */
+#define SF_Recursive 0x02000 /* The recursive part of a recursive CTE */
+#define SF_FixedLimit 0x04000 /* nSelectRow set by a constant LIMIT */
+#define SF_MaybeConvert 0x08000 /* Need convertCompoundSelectToSubquery() */
+#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
+#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
+
+
+/*
+** The results of a SELECT can be distributed in several ways, as defined
+** by one of the following macros. The "SRT" prefix means "SELECT Result
+** Type".
+**
+** SRT_Union Store results as a key in a temporary index
+** identified by pDest->iSDParm.
+**
+** SRT_Except Remove results from the temporary index pDest->iSDParm.
+**
+** SRT_Exists Store a 1 in memory cell pDest->iSDParm if the result
+** set is not empty.
+**
+** SRT_Discard Throw the results away. This is used by SELECT
+** statements within triggers whose only purpose is
+** the side-effects of functions.
+**
+** All of the above are free to ignore their ORDER BY clause. Those that
+** follow must honor the ORDER BY clause.
+**
+** SRT_Output Generate a row of output (using the OP_ResultRow
+** opcode) for each row in the result set.
+**
+** SRT_Mem Only valid if the result is a single column.
+** Store the first column of the first result row
+** in register pDest->iSDParm then abandon the rest
+** of the query. This destination implies "LIMIT 1".
+**
+** SRT_Set The result must be a single column. Store each
+** row of result as the key in table pDest->iSDParm.
+** Apply the affinity pDest->affSdst before storing
+** results. Used to implement "IN (SELECT ...)".
+**
+** SRT_EphemTab Create an temporary table pDest->iSDParm and store
+** the result there. The cursor is left open after
+** returning. This is like SRT_Table except that
+** this destination uses OP_OpenEphemeral to create
+** the table first.
+**
+** SRT_Coroutine Generate a co-routine that returns a new row of
+** results each time it is invoked. The entry point
+** of the co-routine is stored in register pDest->iSDParm
+** and the result row is stored in pDest->nDest registers
+** starting with pDest->iSdst.
+**
+** SRT_Table Store results in temporary table pDest->iSDParm.
+** SRT_Fifo This is like SRT_EphemTab except that the table
+** is assumed to already be open. SRT_Fifo has
+** the additional property of being able to ignore
+** the ORDER BY clause.
+**
+** SRT_DistFifo Store results in a temporary table pDest->iSDParm.
+** But also use temporary table pDest->iSDParm+1 as
+** a record of all prior results and ignore any duplicate
+** rows. Name means: "Distinct Fifo".
+**
+** SRT_Queue Store results in priority queue pDest->iSDParm (really
+** an index). Append a sequence number so that all entries
+** are distinct.
+**
+** SRT_DistQueue Store results in priority queue pDest->iSDParm only if
+** the same record has never been stored before. The
+** index at pDest->iSDParm+1 hold all prior stores.
*/
#define SRT_Union 1 /* Store result as keys in an index */
#define SRT_Except 2 /* Remove result from a UNION index */
#define SRT_Exists 3 /* Store 1 if the result is not empty */
#define SRT_Discard 4 /* Do not save the results anywhere */
+#define SRT_Fifo 5 /* Store result as data with an automatic rowid */
+#define SRT_DistFifo 6 /* Like SRT_Fifo, but unique results only */
+#define SRT_Queue 7 /* Store result in an queue */
+#define SRT_DistQueue 8 /* Like SRT_Queue, but unique results only */
/* The ORDER BY clause is ignored for all of the above */
-#define IgnorableOrderby(X) ((X->eDest)<=SRT_Discard)
+#define IgnorableOrderby(X) ((X->eDest)<=SRT_DistQueue)
-#define SRT_Output 5 /* Output each row of result */
-#define SRT_Mem 6 /* Store result in a memory cell */
-#define SRT_Set 7 /* Store results as keys in an index */
-#define SRT_Table 8 /* Store result as data with an automatic rowid */
-#define SRT_EphemTab 9 /* Create transient tab and store like SRT_Table */
-#define SRT_Coroutine 10 /* Generate a single row of result */
+#define SRT_Output 9 /* Output each row of result */
+#define SRT_Mem 10 /* Store result in a memory cell */
+#define SRT_Set 11 /* Store results as keys in an index */
+#define SRT_EphemTab 12 /* Create transient tab and store like SRT_Table */
+#define SRT_Coroutine 13 /* Generate a single row of result */
+#define SRT_Table 14 /* Store result as data with an automatic rowid */
/*
-** A structure used to customize the behavior of sqlite3Select(). See
-** comments above sqlite3Select() for details.
+** An instance of this object describes where to put of the results of
+** a SELECT statement.
*/
-typedef struct SelectDest SelectDest;
struct SelectDest {
- u8 eDest; /* How to dispose of the results */
- u8 affinity; /* Affinity used when eDest==SRT_Set */
- int iParm; /* A parameter used by the eDest disposal method */
- int iMem; /* Base register where results are written */
- int nMem; /* Number of registers allocated */
+ u8 eDest; /* How to dispose of the results. On of SRT_* above. */
+ char *zAffSdst; /* Affinity used when eDest==SRT_Set */
+ int iSDParm; /* A parameter used by the eDest disposal method */
+ int iSdst; /* Base register where results are written */
+ int nSdst; /* Number of registers allocated */
+ ExprList *pOrderBy; /* Key columns for SRT_Queue and SRT_DistQueue */
};
/*
-** During code generation of statements that do inserts into AUTOINCREMENT
+** During code generation of statements that do inserts into AUTOINCREMENT
** tables, the following information is attached to the Table.u.autoInc.p
** pointer of each autoincrement table to record some side information that
** the code generator needs. We have to keep per-table autoincrement
-** information in case inserts are down within triggers. Triggers do not
+** information in case inserts are done within triggers. Triggers do not
** normally coordinate their activities, but we do need to coordinate the
** loading and saving of autoincrement information.
*/
@@ -10775,7 +15452,7 @@ struct AutoincInfo {
#endif
/*
-** At least one instance of the following structure is created for each
+** At least one instance of the following structure is created for each
** trigger that may be fired while parsing an INSERT, UPDATE or DELETE
** statement. All such objects are stored in the linked list headed at
** Parse.pTriggerPrg and deleted once statement compilation has been
@@ -10788,25 +15465,35 @@ struct AutoincInfo {
** values for both pTrigger and orconf.
**
** The TriggerPrg.aColmask[0] variable is set to a mask of old.* columns
-** accessed (or set to 0 for triggers fired as a result of INSERT
+** accessed (or set to 0 for triggers fired as a result of INSERT
** statements). Similarly, the TriggerPrg.aColmask[1] variable is set to
** a mask of new.* columns used by the program.
*/
struct TriggerPrg {
Trigger *pTrigger; /* Trigger this program was coded from */
- int orconf; /* Default ON CONFLICT policy */
+ TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */
SubProgram *pProgram; /* Program implementing pTrigger/orconf */
+ int orconf; /* Default ON CONFLICT policy */
u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */
- TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */
};
/*
** The yDbMask datatype for the bitmask of all attached databases.
*/
#if SQLITE_MAX_ATTACHED>30
- typedef sqlite3_uint64 yDbMask;
+ typedef unsigned char yDbMask[(SQLITE_MAX_ATTACHED+9)/8];
+# define DbMaskTest(M,I) (((M)[(I)/8]&(1<<((I)&7)))!=0)
+# define DbMaskZero(M) memset((M),0,sizeof(M))
+# define DbMaskSet(M,I) (M)[(I)/8]|=(1<<((I)&7))
+# define DbMaskAllZero(M) sqlite3DbMaskAllZero(M)
+# define DbMaskNonZero(M) (sqlite3DbMaskAllZero(M)==0)
#else
typedef unsigned int yDbMask;
+# define DbMaskTest(M,I) (((M)&(((yDbMask)1)<<(I)))!=0)
+# define DbMaskZero(M) (M)=0
+# define DbMaskSet(M,I) (M)|=(((yDbMask)1)<<(I))
+# define DbMaskAllZero(M) (M)==0
+# define DbMaskNonZero(M) (M)!=0
#endif
/*
@@ -10819,7 +15506,7 @@ struct TriggerPrg {
** is constant but the second part is reset at the beginning and end of
** each recursion.
**
-** The nTableLock and aTableLock variables are only used if the shared-cache
+** The nTableLock and aTableLock variables are only used if the shared-cache
** feature is enabled (if sqlite3Tsd()->useSharedData is true). They are
** used to store the set of table-locks required by the statement being
** compiled. Function sqlite3TableLock() is used to add entries to the
@@ -10827,94 +15514,124 @@ struct TriggerPrg {
*/
struct Parse {
sqlite3 *db; /* The main database structure */
- int rc; /* Return code from execution */
char *zErrMsg; /* An error message */
Vdbe *pVdbe; /* An engine for executing database bytecode */
+ int rc; /* Return code from execution */
u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
- u8 nameClash; /* A permanent table name clashes with temp table name */
u8 checkSchema; /* Causes schema cookie check after an error */
u8 nested; /* Number of nested calls to the parser/code generator */
- u8 parseError; /* True after a parsing error. Ticket #1794 */
u8 nTempReg; /* Number of temporary registers in aTempReg[] */
- u8 nTempInUse; /* Number of aTempReg[] currently checked out */
- int aTempReg[8]; /* Holding area for temporary registers */
+ u8 isMultiWrite; /* True if statement may modify/insert multiple rows */
+ u8 mayAbort; /* True if statement may throw an ABORT exception */
+ u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
+ u8 okConstFactor; /* OK to factor out constants */
+ u8 disableLookaside; /* Number of times lookaside has been disabled */
+ u8 nColCache; /* Number of entries in aColCache[] */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
int nErr; /* Number of errors seen */
int nTab; /* Number of previously allocated VDBE cursors */
int nMem; /* Number of memory cells used so far */
- int nSet; /* Number of sets used so far */
+ int nOpAlloc; /* Number of slots allocated for Vdbe.aOp[] */
+ int szOpAlloc; /* Bytes of memory space allocated for Vdbe.aOp[] */
int ckBase; /* Base register of data during check constraints */
+ int iSelfTab; /* Table of an index whose exprs are being coded */
int iCacheLevel; /* ColCache valid when aColCache[].iLevel<=iCacheLevel */
int iCacheCnt; /* Counter used to generate aColCache[].lru values */
- u8 nColCache; /* Number of entries in the column cache */
- u8 iColCache; /* Next entry of the cache to replace */
- struct yColCache {
- int iTable; /* Table cursor number */
- int iColumn; /* Table column number */
- u8 tempReg; /* iReg is a temp register that needs to be freed */
- int iLevel; /* Nesting level */
- int iReg; /* Reg with value of this column. 0 means none. */
- int lru; /* Least recently used entry has the smallest value */
- } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
+ int nLabel; /* Number of labels used */
+ int *aLabel; /* Space to hold the labels */
+ ExprList *pConstExpr;/* Constant expressions */
+ Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
- u8 isMultiWrite; /* True if statement may affect/insert multiple rows */
- u8 mayAbort; /* True if statement may throw an ABORT exception */
- int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */
- int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */
+ int regRowid; /* Register holding rowid of CREATE TABLE entry */
+ int regRoot; /* Register holding root page number for new objects */
+ int nMaxArg; /* Max args passed to user function by sub-program */
+#if SELECTTRACE_ENABLED
+ int nSelect; /* Number of SELECT statements seen */
+ int nSelectIndent; /* How far to indent SELECTTRACE() output */
+#endif
#ifndef SQLITE_OMIT_SHARED_CACHE
int nTableLock; /* Number of locks in aTableLock */
TableLock *aTableLock; /* Required table locks for shared-cache mode */
#endif
- int regRowid; /* Register holding rowid of CREATE TABLE entry */
- int regRoot; /* Register holding root page number for new objects */
AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */
- int nMaxArg; /* Max args passed to user function by sub-program */
-
- /* Information used while coding trigger programs. */
Parse *pToplevel; /* Parse structure for main program (or NULL) */
Table *pTriggerTab; /* Table triggers are being coded for */
+ int addrCrTab; /* Address of OP_CreateTable opcode on CREATE TABLE */
+ u32 nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u32 oldmask; /* Mask of old.* columns referenced */
u32 newmask; /* Mask of new.* columns referenced */
u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
u8 disableTriggers; /* True to disable triggers */
- double nQueryLoop; /* Estimated number of iterations of a query */
-
- /* Above is constant between recursions. Below is reset before and after
- ** each recursion */
-
- int nVar; /* Number of '?' variables seen in the SQL so far */
- int nzVar; /* Number of available slots in azVar[] */
- char **azVar; /* Pointers to names of parameters */
- Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
- int nAlias; /* Number of aliased result set columns */
- int nAliasAlloc; /* Number of allocated slots for aAlias[] */
- int *aAlias; /* Register used to hold aliased result */
- u8 explain; /* True if the EXPLAIN flag is found on the query */
- Token sNameToken; /* Token with unqualified schema object name */
- Token sLastToken; /* The last token parsed */
- const char *zTail; /* All SQL text past the last semicolon parsed */
- Table *pNewTable; /* A table being constructed by CREATE TABLE */
- Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
- const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
+
+ /**************************************************************************
+ ** Fields above must be initialized to zero. The fields that follow,
+ ** down to the beginning of the recursive section, do not need to be
+ ** initialized as they will be set before being used. The boundary is
+ ** determined by offsetof(Parse,aColCache).
+ **************************************************************************/
+
+ struct yColCache {
+ int iTable; /* Table cursor number */
+ i16 iColumn; /* Table column number */
+ u8 tempReg; /* iReg is a temp register that needs to be freed */
+ int iLevel; /* Nesting level */
+ int iReg; /* Reg with value of this column. 0 means none. */
+ int lru; /* Least recently used entry has the smallest value */
+ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */
+ int aTempReg[8]; /* Holding area for temporary registers */
+ Token sNameToken; /* Token with unqualified schema object name */
+ Token sLastToken; /* The last token parsed */
+
+ /************************************************************************
+ ** Above is constant between recursions. Below is reset before and after
+ ** each recursion. The boundary between these two regions is determined
+ ** using offsetof(Parse,nVar) so the nVar field must be the first field
+ ** in the recursive region.
+ ************************************************************************/
+
+ ynVar nVar; /* Number of '?' variables seen in the SQL so far */
+ int nzVar; /* Number of available slots in azVar[] */
+ u8 iPkSortOrder; /* ASC or DESC for INTEGER PRIMARY KEY */
+ u8 explain; /* True if the EXPLAIN flag is found on the query */
#ifndef SQLITE_OMIT_VIRTUALTABLE
- Token sArg; /* Complete text of a module argument */
- u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
- int nVtabLock; /* Number of virtual tables to lock */
- Table **apVtabLock; /* Pointer to virtual tables needing locking */
+ u8 declareVtab; /* True if inside sqlite3_declare_vtab() */
+ int nVtabLock; /* Number of virtual tables to lock */
#endif
- int nHeight; /* Expression tree height of current sub-select */
- Table *pZombieTab; /* List of Table objects to delete after code gen */
- TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
-
+ int nHeight; /* Expression tree height of current sub-select */
#ifndef SQLITE_OMIT_EXPLAIN
- int iSelectId;
- int iNextSelectId;
+ int iSelectId; /* ID of current select for EXPLAIN output */
+ int iNextSelectId; /* Next available select ID for EXPLAIN output */
+#endif
+ char **azVar; /* Pointers to names of parameters */
+ Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
+ const char *zTail; /* All SQL text past the last semicolon parsed */
+ Table *pNewTable; /* A table being constructed by CREATE TABLE */
+ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */
+ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ Token sArg; /* Complete text of a module argument */
+ Table **apVtabLock; /* Pointer to virtual tables needing locking */
#endif
+ Table *pZombieTab; /* List of Table objects to delete after code gen */
+ TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
+ With *pWith; /* Current WITH clause, or NULL */
+ With *pWithToFree; /* Free this WITH object at the end of the parse */
};
+/*
+** Sizes and pointers of various parts of the Parse object.
+*/
+#define PARSE_HDR_SZ offsetof(Parse,aColCache) /* Recursive part w/o aColCache*/
+#define PARSE_RECURSE_SZ offsetof(Parse,nVar) /* Recursive part */
+#define PARSE_TAIL_SZ (sizeof(Parse)-PARSE_RECURSE_SZ) /* Non-recursive part */
+#define PARSE_TAIL(X) (((char*)(X))+PARSE_RECURSE_SZ) /* Pointer to tail */
+
+/*
+** Return true if currently inside an sqlite3_declare_vtab() call.
+*/
#ifdef SQLITE_OMIT_VIRTUALTABLE
#define IN_DECLARE_VTAB 0
#else
@@ -10931,21 +15648,43 @@ struct AuthContext {
};
/*
-** Bitfield flags for P5 value in OP_Insert and OP_Delete
+** Bitfield flags for P5 value in various opcodes.
+**
+** Value constraints (enforced via assert()):
+** OPFLAG_LENGTHARG == SQLITE_FUNC_LENGTH
+** OPFLAG_TYPEOFARG == SQLITE_FUNC_TYPEOF
+** OPFLAG_BULKCSR == BTREE_BULKLOAD
+** OPFLAG_SEEKEQ == BTREE_SEEK_EQ
+** OPFLAG_FORDELETE == BTREE_FORDELETE
+** OPFLAG_SAVEPOSITION == BTREE_SAVEPOSITION
+** OPFLAG_AUXDELETE == BTREE_AUXDELETE
*/
-#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */
+#define OPFLAG_NCHANGE 0x01 /* OP_Insert: Set to update db->nChange */
+ /* Also used in P2 (not P5) of OP_Delete */
+#define OPFLAG_EPHEM 0x01 /* OP_Column: Ephemeral output is ok */
#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */
#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */
#define OPFLAG_APPEND 0x08 /* This is likely to be an append */
#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */
-#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+#define OPFLAG_ISNOOP 0x40 /* OP_Delete does pre-update-hook only */
+#endif
+#define OPFLAG_LENGTHARG 0x40 /* OP_Column only used for length() */
+#define OPFLAG_TYPEOFARG 0x80 /* OP_Column only used for typeof() */
+#define OPFLAG_BULKCSR 0x01 /* OP_Open** used to open bulk cursor */
+#define OPFLAG_SEEKEQ 0x02 /* OP_Open** cursor uses EQ seek only */
+#define OPFLAG_FORDELETE 0x08 /* OP_Open should use BTREE_FORDELETE */
+#define OPFLAG_P2ISREG 0x10 /* P2 to OP_Open** is a register number */
+#define OPFLAG_PERMUTE 0x01 /* OP_Compare: use the permutation */
+#define OPFLAG_SAVEPOSITION 0x02 /* OP_Delete: keep cursor position */
+#define OPFLAG_AUXDELETE 0x04 /* OP_Delete: index in a DELETE op */
/*
* Each trigger present in the database schema is stored as an instance of
- * struct Trigger.
+ * struct Trigger.
*
* Pointers to instances of struct Trigger are stored in two ways.
- * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
+ * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
* database). This allows Trigger structures to be retrieved by name.
* 2. All triggers associated with a single table form a linked list, using the
* pNext member of struct Trigger. A pointer to the first element of the
@@ -10971,7 +15710,7 @@ struct Trigger {
/*
** A trigger is either a BEFORE or an AFTER trigger. The following constants
-** determine which.
+** determine which.
**
** If there are multiple triggers, you might of some BEFORE and some AFTER.
** In that cases, the constants below can be ORed together.
@@ -10981,50 +15720,50 @@ struct Trigger {
/*
* An instance of struct TriggerStep is used to store a single SQL statement
- * that is a part of a trigger-program.
+ * that is a part of a trigger-program.
*
* Instances of struct TriggerStep are stored in a singly linked list (linked
- * using the "pNext" member) referenced by the "step_list" member of the
+ * using the "pNext" member) referenced by the "step_list" member of the
* associated struct Trigger instance. The first element of the linked list is
* the first step of the trigger-program.
- *
+ *
* The "op" member indicates whether this is a "DELETE", "INSERT", "UPDATE" or
- * "SELECT" statement. The meanings of the other members is determined by the
+ * "SELECT" statement. The meanings of the other members is determined by the
* value of "op" as follows:
*
* (op == TK_INSERT)
* orconf -> stores the ON CONFLICT algorithm
* pSelect -> If this is an INSERT INTO ... SELECT ... statement, then
* this stores a pointer to the SELECT statement. Otherwise NULL.
- * target -> A token holding the quoted name of the table to insert into.
+ * zTarget -> Dequoted name of the table to insert into.
* pExprList -> If this is an INSERT INTO ... VALUES ... statement, then
* this stores values to be inserted. Otherwise NULL.
- * pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ...
+ * pIdList -> If this is an INSERT INTO ... (<column-names>) VALUES ...
* statement, then this stores the column-names to be
* inserted into.
*
* (op == TK_DELETE)
- * target -> A token holding the quoted name of the table to delete from.
+ * zTarget -> Dequoted name of the table to delete from.
* pWhere -> The WHERE clause of the DELETE statement if one is specified.
* Otherwise NULL.
- *
+ *
* (op == TK_UPDATE)
- * target -> A token holding the quoted name of the table to update rows of.
+ * zTarget -> Dequoted name of the table to update.
* pWhere -> The WHERE clause of the UPDATE statement if one is specified.
* Otherwise NULL.
* pExprList -> A list of the columns to update and the expressions to update
* them to. See sqlite3Update() documentation of "pChanges"
* argument.
- *
+ *
*/
struct TriggerStep {
u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */
u8 orconf; /* OE_Rollback etc. */
Trigger *pTrig; /* The trigger that this step is a part of */
- Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */
- Token target; /* Target table for DELETE, UPDATE, INSERT */
+ Select *pSelect; /* SELECT statement or RHS of INSERT INTO SELECT ... */
+ char *zTarget; /* Target table for DELETE, UPDATE, INSERT */
Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */
- ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */
+ ExprList *pExprList; /* SET clause for UPDATE. */
IdList *pIdList; /* Column names for INSERT */
TriggerStep *pNext; /* Next in the link-list */
TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */
@@ -11033,11 +15772,13 @@ struct TriggerStep {
/*
** The following structure contains information used by the sqliteFix...
** routines as they walk the parse tree to make database references
-** explicit.
+** explicit.
*/
typedef struct DbFixer DbFixer;
struct DbFixer {
Parse *pParse; /* The parsing context. Error messages written here */
+ Schema *pSchema; /* Fix items to this schema */
+ int bVarOnly; /* Check for variable references only */
const char *zDb; /* Make sure all objects are contained in this database */
const char *zType; /* Type of the container - used for error messages */
const Token *pName; /* Name of the container - used for error messages */
@@ -11051,13 +15792,20 @@ struct StrAccum {
sqlite3 *db; /* Optional database for lookaside. Can be NULL */
char *zBase; /* A base allocation. Not from malloc. */
char *zText; /* The string collected so far */
- int nChar; /* Length of the string so far */
- int nAlloc; /* Amount of space allocated in zText */
- int mxAlloc; /* Maximum allowed string length */
- u8 mallocFailed; /* Becomes true if any memory allocation fails */
- u8 useMalloc; /* 0: none, 1: sqlite3DbMalloc, 2: sqlite3_malloc */
- u8 tooBig; /* Becomes true if string size exceeds limits */
+ u32 nChar; /* Length of the string so far */
+ u32 nAlloc; /* Amount of space allocated in zText */
+ u32 mxAlloc; /* Maximum allowed allocation. 0 for no malloc usage */
+ u8 accError; /* STRACCUM_NOMEM or STRACCUM_TOOBIG */
+ u8 printfFlags; /* SQLITE_PRINTF flags below */
};
+#define STRACCUM_NOMEM 1
+#define STRACCUM_TOOBIG 2
+#define SQLITE_PRINTF_INTERNAL 0x01 /* Internal-use-only converters allowed */
+#define SQLITE_PRINTF_SQLFUNC 0x02 /* SQL function arguments to VXPrintf */
+#define SQLITE_PRINTF_MALLOCED 0x04 /* True if xText is allocated space */
+
+#define isMalloced(X) (((X)->printfFlags & SQLITE_PRINTF_MALLOCED)!=0)
+
/*
** A pointer to this structure is used to communicate information
@@ -11065,8 +15813,8 @@ struct StrAccum {
*/
typedef struct {
sqlite3 *db; /* The database being initialized */
- int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
char **pzErrMsg; /* Error message stored here */
+ int iDb; /* 0 for main database. 1 for TEMP, 2.. for ATTACHed */
int rc; /* Result code stored here */
} InitData;
@@ -11080,15 +15828,20 @@ struct Sqlite3Config {
int bCoreMutex; /* True to enable core mutexing */
int bFullMutex; /* True to enable full mutexing */
int bOpenUri; /* True to interpret filenames as URIs */
+ int bUseCis; /* Use covering indices for full-scans */
int mxStrlen; /* Maximum string length */
+ int neverCorrupt; /* Database is always well-formed */
int szLookaside; /* Default lookaside buffer size */
int nLookaside; /* Default lookaside buffer count */
+ int nStmtSpill; /* Stmt-journal spill-to-disk threshold */
sqlite3_mem_methods m; /* Low-level memory allocation interface */
sqlite3_mutex_methods mutex; /* Low-level mutex interface */
- sqlite3_pcache_methods pcache; /* Low-level page-cache interface */
+ sqlite3_pcache_methods2 pcache2; /* Low-level page-cache interface */
void *pHeap; /* Heap storage space */
int nHeap; /* Size of pHeap[] */
int mnReq, mxReq; /* Min and max heap requests sizes */
+ sqlite3_int64 szMmap; /* mmap() space per open file */
+ sqlite3_int64 mxMmap; /* Maximum value for szMmap */
void *pScratch; /* Scratch memory */
int szScratch; /* Size of each scratch buffer */
int nScratch; /* Number of scratch buffers */
@@ -11097,6 +15850,7 @@ struct Sqlite3Config {
int nPage; /* Number of pages in pPage[] */
int mxParserStack; /* maximum depth of the parser stack */
int sharedCacheEnabled; /* true if shared-cache mode enabled */
+ u32 szPma; /* Maximum Sorter PMA size */
/* The above might be initialized to non-zero. The following need to always
** initially be zero, however. */
int isInit; /* True after initialization has finished */
@@ -11104,23 +15858,65 @@ struct Sqlite3Config {
int isMutexInit; /* True after mutexes are initialized */
int isMallocInit; /* True after malloc is initialized */
int isPCacheInit; /* True after malloc is initialized */
- sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
int nRefInitMutex; /* Number of users of pInitMutex */
+ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */
void (*xLog)(void*,int,const char*); /* Function for logging */
void *pLogArg; /* First argument to xLog() */
+#ifdef SQLITE_ENABLE_SQLLOG
+ void(*xSqllog)(void*,sqlite3*,const char*, int);
+ void *pSqllogArg;
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ /* The following callback (if not NULL) is invoked on every VDBE branch
+ ** operation. Set the callback using SQLITE_TESTCTRL_VDBE_COVERAGE.
+ */
+ void (*xVdbeBranch)(void*,int iSrcLine,u8 eThis,u8 eMx); /* Callback */
+ void *pVdbeBranchArg; /* 1st argument */
+#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ int (*xTestCallback)(int); /* Invoked by sqlite3FaultSim() */
+#endif
int bLocaltimeFault; /* True to fail localtime() calls */
+ int iOnceResetThreshold; /* When to reset OP_Once counters */
};
/*
+** This macro is used inside of assert() statements to indicate that
+** the assert is only valid on a well-formed database. Instead of:
+**
+** assert( X );
+**
+** One writes:
+**
+** assert( X || CORRUPT_DB );
+**
+** CORRUPT_DB is true during normal operation. CORRUPT_DB does not indicate
+** that the database is definitely corrupt, only that it might be corrupt.
+** For most test cases, CORRUPT_DB is set to false using a special
+** sqlite3_test_control(). This enables assert() statements to prove
+** things that are always true for well-formed databases.
+*/
+#define CORRUPT_DB (sqlite3Config.neverCorrupt==0)
+
+/*
** Context pointer passed down through the tree-walk.
*/
struct Walker {
+ Parse *pParse; /* Parser context. */
int (*xExprCallback)(Walker*, Expr*); /* Callback for expressions */
int (*xSelectCallback)(Walker*,Select*); /* Callback for SELECTs */
- Parse *pParse; /* Parser context. */
+ void (*xSelectCallback2)(Walker*,Select*);/* Second callback for SELECTs */
+ int walkerDepth; /* Number of subqueries */
+ u8 eCode; /* A small processing code */
union { /* Extra data for callback */
NameContext *pNC; /* Naming context */
- int i; /* Integer value */
+ int n; /* A counter */
+ int iCur; /* A cursor number */
+ SrcList *pSrcList; /* FROM clause */
+ struct SrcCount *pSrcCount; /* Counting column references */
+ struct CCurHint *pCCurHint; /* Used by codeCursorHint() */
+ int *aiCol; /* array of column indexes */
+ struct IdxCover *pIdxCover; /* Check for index coverage */
} u;
};
@@ -11130,6 +15926,7 @@ SQLITE_PRIVATE int sqlite3WalkExprList(Walker*, ExprList*);
SQLITE_PRIVATE int sqlite3WalkSelect(Walker*, Select*);
SQLITE_PRIVATE int sqlite3WalkSelectExpr(Walker*, Select*);
SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*);
+SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker*, Expr*);
/*
** Return code from the parse-tree walking primitives and their
@@ -11140,6 +15937,32 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker*, Select*);
#define WRC_Abort 2 /* Abandon the tree walk */
/*
+** An instance of this structure represents a set of one or more CTEs
+** (common table expressions) created by a single WITH clause.
+*/
+struct With {
+ int nCte; /* Number of CTEs in the WITH clause */
+ With *pOuter; /* Containing WITH clause, or NULL */
+ struct Cte { /* For each CTE in the WITH clause.... */
+ char *zName; /* Name of this CTE */
+ ExprList *pCols; /* List of explicit column names, or NULL */
+ Select *pSelect; /* The definition of this CTE */
+ const char *zCteErr; /* Error message for circular references */
+ } a[1];
+};
+
+#ifdef SQLITE_DEBUG
+/*
+** An instance of the TreeView object is used for printing the content of
+** data structures on sqlite3DebugPrintf() using a tree-like view.
+*/
+struct TreeView {
+ int iLevel; /* Which level of the tree we are on */
+ u8 bLine[100]; /* Draw vertical in column i if bLine[i] is true */
+};
+#endif /* SQLITE_DEBUG */
+
+/*
** Assuming zIn points to the first byte of a UTF-8 character,
** advance zIn to point to the first byte of the next UTF-8 character.
*/
@@ -11162,15 +15985,31 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
#define SQLITE_CORRUPT_BKPT sqlite3CorruptError(__LINE__)
#define SQLITE_MISUSE_BKPT sqlite3MisuseError(__LINE__)
#define SQLITE_CANTOPEN_BKPT sqlite3CantopenError(__LINE__)
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NomemError(int);
+SQLITE_PRIVATE int sqlite3IoerrnomemError(int);
+# define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__)
+# define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__)
+#else
+# define SQLITE_NOMEM_BKPT SQLITE_NOMEM
+# define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM
+#endif
+/*
+** FTS3 and FTS4 both require virtual table support
+*/
+#if defined(SQLITE_OMIT_VIRTUALTABLE)
+# undef SQLITE_ENABLE_FTS3
+# undef SQLITE_ENABLE_FTS4
+#endif
/*
** FTS4 is really an extension for FTS3. It is enabled using the
-** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all
-** the SQLITE_ENABLE_FTS4 macro to serve as an alisse for SQLITE_ENABLE_FTS3.
+** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also call
+** the SQLITE_ENABLE_FTS4 macro to serve as an alias for SQLITE_ENABLE_FTS3.
*/
#if defined(SQLITE_ENABLE_FTS4) && !defined(SQLITE_ENABLE_FTS3)
-# define SQLITE_ENABLE_FTS3
+# define SQLITE_ENABLE_FTS3 1
#endif
/*
@@ -11195,6 +16034,7 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
# define sqlite3Isdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x04)
# define sqlite3Isxdigit(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x08)
# define sqlite3Tolower(x) (sqlite3UpperToLower[(unsigned char)(x)])
+# define sqlite3Isquote(x) (sqlite3CtypeMap[(unsigned char)(x)]&0x80)
#else
# define sqlite3Toupper(x) toupper((unsigned char)(x))
# define sqlite3Isspace(x) isspace((unsigned char)(x))
@@ -11203,26 +16043,32 @@ SQLITE_PRIVATE int sqlite3CantopenError(int);
# define sqlite3Isdigit(x) isdigit((unsigned char)(x))
# define sqlite3Isxdigit(x) isxdigit((unsigned char)(x))
# define sqlite3Tolower(x) tolower((unsigned char)(x))
+# define sqlite3Isquote(x) ((x)=='"'||(x)=='\''||(x)=='['||(x)=='`')
+#endif
+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
+SQLITE_PRIVATE int sqlite3IsIdChar(u8);
#endif
/*
** Internal function prototypes
*/
-SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *);
+SQLITE_PRIVATE int sqlite3StrICmp(const char*,const char*);
SQLITE_PRIVATE int sqlite3Strlen30(const char*);
+SQLITE_PRIVATE char *sqlite3ColumnType(Column*,char*);
#define sqlite3StrNICmp sqlite3_strnicmp
SQLITE_PRIVATE int sqlite3MallocInit(void);
SQLITE_PRIVATE void sqlite3MallocEnd(void);
-SQLITE_PRIVATE void *sqlite3Malloc(int);
-SQLITE_PRIVATE void *sqlite3MallocZero(int);
-SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, int);
-SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, int);
+SQLITE_PRIVATE void *sqlite3Malloc(u64);
+SQLITE_PRIVATE void *sqlite3MallocZero(u64);
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3*, u64);
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3*, u64);
+SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3*, u64);
SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3*,const char*);
-SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, int);
-SQLITE_PRIVATE void *sqlite3Realloc(void*, int);
-SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, int);
-SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, int);
+SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3*,const char*, u64);
+SQLITE_PRIVATE void *sqlite3Realloc(void*, u64);
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *, void *, u64);
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *, void *, u64);
SQLITE_PRIVATE void sqlite3DbFree(sqlite3*, void*);
SQLITE_PRIVATE int sqlite3MallocSize(void*);
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3*, void*);
@@ -11231,7 +16077,9 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void*);
SQLITE_PRIVATE void *sqlite3PageMalloc(int);
SQLITE_PRIVATE void sqlite3PageFree(void*);
SQLITE_PRIVATE void sqlite3MemSetDefault(void);
+#ifndef SQLITE_OMIT_BUILTIN_TEST
SQLITE_PRIVATE void sqlite3BenignMallocHooks(void (*)(void), void (*)(void));
+#endif
SQLITE_PRIVATE int sqlite3HeapNearlyFull(void);
/*
@@ -11245,18 +16093,22 @@ SQLITE_PRIVATE int sqlite3HeapNearlyFull(void);
#ifdef SQLITE_USE_ALLOCA
# define sqlite3StackAllocRaw(D,N) alloca(N)
# define sqlite3StackAllocZero(D,N) memset(alloca(N), 0, N)
-# define sqlite3StackFree(D,P)
+# define sqlite3StackFree(D,P)
#else
# define sqlite3StackAllocRaw(D,N) sqlite3DbMallocRaw(D,N)
# define sqlite3StackAllocZero(D,N) sqlite3DbMallocZero(D,N)
# define sqlite3StackFree(D,P) sqlite3DbFree(D,P)
#endif
-#ifdef SQLITE_ENABLE_MEMSYS3
-SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void);
-#endif
+/* Do not allow both MEMSYS5 and MEMSYS3 to be defined together. If they
+** are, disable MEMSYS3
+*/
#ifdef SQLITE_ENABLE_MEMSYS5
SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void);
+#undef SQLITE_ENABLE_MEMSYS3
+#endif
+#ifdef SQLITE_ENABLE_MEMSYS3
+SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void);
#endif
@@ -11267,10 +16119,20 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int);
SQLITE_PRIVATE int sqlite3MutexInit(void);
SQLITE_PRIVATE int sqlite3MutexEnd(void);
#endif
+#if !defined(SQLITE_MUTEX_OMIT) && !defined(SQLITE_MUTEX_NOOP)
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void);
+#else
+# define sqlite3MemoryBarrier()
+#endif
+
+SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int);
+SQLITE_PRIVATE void sqlite3StatusUp(int, int);
+SQLITE_PRIVATE void sqlite3StatusDown(int, int);
+SQLITE_PRIVATE void sqlite3StatusHighwater(int, int);
-SQLITE_PRIVATE int sqlite3StatusValue(int);
-SQLITE_PRIVATE void sqlite3StatusAdd(int, int);
-SQLITE_PRIVATE void sqlite3StatusSet(int, int);
+/* Access to mutexes used by sqlite3_status() */
+SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void);
+SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void);
#ifndef SQLITE_OMIT_FLOATING_POINT
SQLITE_PRIVATE int sqlite3IsNaN(double);
@@ -11278,22 +16140,40 @@ SQLITE_PRIVATE int sqlite3IsNaN(double);
# define sqlite3IsNaN(X) 0
#endif
-SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, int, const char*, va_list);
-#ifndef SQLITE_OMIT_TRACE
+/*
+** An instance of the following structure holds information about SQL
+** functions arguments that are the parameters to the printf() function.
+*/
+struct PrintfArguments {
+ int nArg; /* Total number of arguments */
+ int nUsed; /* Number of arguments used so far */
+ sqlite3_value **apArg; /* The argument values */
+};
+
+SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, const char*, va_list);
SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, const char*, ...);
-#endif
SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...);
SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list);
-SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
-#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
SQLITE_PRIVATE void sqlite3DebugPrintf(const char*, ...);
#endif
#if defined(SQLITE_TEST)
SQLITE_PRIVATE void *sqlite3TestTextToPtr(const char*);
#endif
-SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*, ...);
+
+#if defined(SQLITE_DEBUG)
+SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView*, const Expr*, u8);
+SQLITE_PRIVATE void sqlite3TreeViewBareExprList(TreeView*, const ExprList*, const char*);
+SQLITE_PRIVATE void sqlite3TreeViewExprList(TreeView*, const ExprList*, u8, const char*);
+SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView*, const Select*, u8);
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView*, const With*, u8);
+#endif
+
+
+SQLITE_PRIVATE void sqlite3SetString(char **, sqlite3*, const char*);
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse*, const char*, ...);
-SQLITE_PRIVATE int sqlite3Dequote(char*);
+SQLITE_PRIVATE void sqlite3Dequote(char*);
+SQLITE_PRIVATE void sqlite3TokenInit(Token*,char*);
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char*, int);
SQLITE_PRIVATE int sqlite3RunParser(Parse*, const char*, char **);
SQLITE_PRIVATE void sqlite3FinishCoding(Parse*);
@@ -11301,53 +16181,81 @@ SQLITE_PRIVATE int sqlite3GetTempReg(Parse*);
SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse*,int);
SQLITE_PRIVATE int sqlite3GetTempRange(Parse*,int);
SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse*,int,int);
+SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse*);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse*,int,int);
+#endif
SQLITE_PRIVATE Expr *sqlite3ExprAlloc(sqlite3*,int,const Token*,int);
SQLITE_PRIVATE Expr *sqlite3Expr(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(sqlite3*,Expr*,Expr*,Expr*);
SQLITE_PRIVATE Expr *sqlite3PExpr(Parse*, int, Expr*, Expr*, const Token*);
+SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse*, Expr*, Select*);
SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*);
-SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*);
+SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
+SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,ExprSpan*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
+SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**);
SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
-SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3*, int);
-SQLITE_PRIVATE void sqlite3BeginParse(Parse*,int);
+SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3*);
+SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3*,int);
+SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3*);
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3*);
+SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3*,Table*);
+SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
+SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(Parse*,Table*,Select*);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*);
SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *, int);
+SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
+SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index*, i16);
SQLITE_PRIVATE void sqlite3StartTable(Parse*,Token*,Token*,int,int,int,int);
-SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*);
+#if SQLITE_ENABLE_HIDDEN_COLUMNS
+SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table*, Column*);
+#else
+# define sqlite3ColumnPropertiesFromName(T,C) /* no-op */
+#endif
+SQLITE_PRIVATE void sqlite3AddColumn(Parse*,Token*,Token*);
SQLITE_PRIVATE void sqlite3AddNotNull(Parse*, int);
SQLITE_PRIVATE void sqlite3AddPrimaryKey(Parse*, ExprList*, int, int, int);
SQLITE_PRIVATE void sqlite3AddCheckConstraint(Parse*, Expr*);
-SQLITE_PRIVATE void sqlite3AddColumnType(Parse*,Token*);
SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse*,ExprSpan*);
SQLITE_PRIVATE void sqlite3AddCollateType(Parse*, Token*);
-SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,Select*);
+SQLITE_PRIVATE void sqlite3EndTable(Parse*,Token*,Token*,u8,Select*);
SQLITE_PRIVATE int sqlite3ParseUri(const char*,const char*,unsigned int*,
sqlite3_vfs**,char**,char **);
+SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3*,const char*);
+
+#ifdef SQLITE_OMIT_BUILTIN_TEST
+# define sqlite3FaultSim(X) SQLITE_OK
+#else
+SQLITE_PRIVATE int sqlite3FaultSim(int);
+#endif
SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32);
SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec*, u32);
+SQLITE_PRIVATE int sqlite3BitvecTestNotNull(Bitvec*, u32);
SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec*, u32);
SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec*, u32, void*);
SQLITE_PRIVATE void sqlite3BitvecDestroy(Bitvec*);
SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec*);
+#ifndef SQLITE_OMIT_BUILTIN_TEST
SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int,int*);
+#endif
SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3*, void*, unsigned int);
SQLITE_PRIVATE void sqlite3RowSetClear(RowSet*);
SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet*, i64);
-SQLITE_PRIVATE int sqlite3RowSetTest(RowSet*, u8 iBatch, i64);
+SQLITE_PRIVATE int sqlite3RowSetTest(RowSet*, int iBatch, i64);
SQLITE_PRIVATE int sqlite3RowSetNext(RowSet*, i64*);
-SQLITE_PRIVATE void sqlite3CreateView(Parse*,Token*,Token*,Token*,Select*,int,int);
+SQLITE_PRIVATE void sqlite3CreateView(Parse*,Token*,Token*,Token*,ExprList*,Select*,int,int);
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_VIRTUALTABLE)
SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse*,Table*);
@@ -11355,7 +16263,11 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse*,Table*);
# define sqlite3ViewGetColumnNames(A,B) 0
#endif
+#if SQLITE_MAX_ATTACHED>30
+SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask);
+#endif
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
+SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse);
@@ -11364,8 +16276,8 @@ SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse);
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)
#endif
-SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, ExprList*, Select*, IdList*, int);
-SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int,int*,int*,int*);
+SQLITE_PRIVATE void sqlite3Insert(Parse*, SrcList*, Select*, IdList*, int);
+SQLITE_PRIVATE void *sqlite3ArrayAllocate(sqlite3*,void*,int,int*,int*);
SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3*, IdList*, Token*);
SQLITE_PRIVATE int sqlite3IdListIndex(IdList*,const char*);
SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(sqlite3*, SrcList*, int, int);
@@ -11373,63 +16285,90 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(sqlite3*, SrcList*, Token*, Token*)
SQLITE_PRIVATE SrcList *sqlite3SrcListAppendFromTerm(Parse*, SrcList*, Token*, Token*,
Token*, Select*, Expr*, IdList*);
SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *, SrcList *, Token *);
+SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse*, SrcList*, ExprList*);
SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *, struct SrcList_item *);
SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList*);
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
-SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
- Token*, int, int);
+SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
+SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
+ Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
- Expr*,ExprList*,int,Expr*,Expr*);
+ Expr*,ExprList*,u32,Expr*,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, int);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
-SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse *, SrcList *, Expr *, ExprList *, Expr *, Expr *, char *);
+SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,Expr*,char*);
#endif
SQLITE_PRIVATE void sqlite3DeleteFrom(Parse*, SrcList*, Expr*);
SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int);
-SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**,ExprList*,u16);
+SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
-SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int);
+SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereOrderedInnerLoop(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
+SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*, int*);
+#define ONEPASS_OFF 0 /* Use of ONEPASS not allowed */
+#define ONEPASS_SINGLE 1 /* ONEPASS valid for a single row update */
+#define ONEPASS_MULTI 2 /* ONEPASS is valid for multiple rows */
+SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(Parse*, Index*, int, int, int);
+SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, u8);
+SQLITE_PRIVATE void sqlite3ExprCodeGetColumnToReg(Parse*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(Vdbe*, Table*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int);
-SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int);
SQLITE_PRIVATE void sqlite3ExprCachePush(Parse*);
-SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*, int);
+SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*);
SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int);
SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*);
SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int);
-SQLITE_PRIVATE int sqlite3ExprCode(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int);
+SQLITE_PRIVATE void sqlite3ExprCodeAtInit(Parse*, Expr*, int, u8);
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int);
-SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse*, Expr*, int);
-SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse*, Expr*);
-SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int);
+SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse*, Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8);
+#define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */
+#define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */
+#define SQLITE_ECEL_REF 0x04 /* Use ExprList.u.x.iOrderByCol */
SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse*, Expr*, int, int);
SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse*, Expr*, int, int);
+SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse*, Expr*, int, int);
SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3*,const char*, const char*);
-SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,int isView,const char*, const char*);
+#define LOCATE_VIEW 0x01
+#define LOCATE_NOERR 0x02
+SQLITE_PRIVATE Table *sqlite3LocateTable(Parse*,u32 flags,const char*, const char*);
+SQLITE_PRIVATE Table *sqlite3LocateTableItem(Parse*,u32 flags,struct SrcList_item *);
SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3*,const char*, const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3*,int,const char*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3*,int,const char*);
-SQLITE_PRIVATE void sqlite3Vacuum(Parse*);
-SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*);
+SQLITE_PRIVATE void sqlite3Vacuum(Parse*,Token*);
+SQLITE_PRIVATE int sqlite3RunVacuum(char**, sqlite3*, int);
SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3*, Token*);
-SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*);
-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*);
+SQLITE_PRIVATE int sqlite3ExprCompare(Expr*, Expr*, int);
+SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList*, ExprList*, int);
+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Expr*, Expr*, int);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*);
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*);
+SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(Expr*, int iCur, Index *pIdx);
+SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr*, SrcList*);
SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*);
+#ifndef SQLITE_OMIT_BUILTIN_TEST
SQLITE_PRIVATE void sqlite3PrngSaveState(void);
SQLITE_PRIVATE void sqlite3PrngRestoreState(void);
-SQLITE_PRIVATE void sqlite3PrngResetState(void);
-SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*);
+#endif
+SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3*,int);
SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse*, int);
SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb);
SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int);
@@ -11437,35 +16376,48 @@ SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*);
SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse*);
SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*);
SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *);
+SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
-SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*);
+SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*, u8);
+SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr*,int);
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr*);
+#endif
SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
-SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int);
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char);
SQLITE_PRIVATE int sqlite3IsRowid(const char*);
-SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int);
-SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*);
-SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int);
-SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int,
- int*,int,int,int,int,int*);
-SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int);
-SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int);
+SQLITE_PRIVATE void sqlite3GenerateRowDelete(
+ Parse*,Table*,Trigger*,int,int,int,i16,u8,u8,u8,int);
+SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int, int*, int);
+SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int, int*,Index*,int);
+SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse*,int);
+SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int*,int,int,int,int,
+ u8,u8,int,int*,int*);
+SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*,Table*,int,int,int,int*,int,int,int);
+SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, u8, int, u8*, int*, int*);
SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int);
SQLITE_PRIVATE void sqlite3MultiWrite(Parse*);
SQLITE_PRIVATE void sqlite3MayAbort(Parse*);
-SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, char*, int);
+SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, int, char*, i8, u8);
+SQLITE_PRIVATE void sqlite3UniqueConstraint(Parse*, int, Index*);
+SQLITE_PRIVATE void sqlite3RowidConstraint(Parse*, int, Table*);
SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int);
SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int);
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int);
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,IdList*);
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,Select*,int);
-SQLITE_PRIVATE void sqlite3FuncDefInsert(FuncDefHash*, FuncDef*);
-SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,int,u8,int);
-SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3*);
+#if SELECTTRACE_ENABLED
+SQLITE_PRIVATE void sqlite3SelectSetName(Select*,const char*);
+#else
+# define sqlite3SelectSetName(A,B)
+#endif
+SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
+SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
+SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
-SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void);
+SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*);
SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*);
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int);
@@ -11489,13 +16441,14 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, i
SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*,
- ExprList*,Select*,u8);
+ Select*,u8);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8);
SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*);
SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*);
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*);
SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int);
# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p))
+# define sqlite3IsToplevel(p) ((p)->pToplevel==0)
#else
# define sqlite3TriggersExist(B,C,D,E,F) 0
# define sqlite3DeleteTrigger(A,B)
@@ -11505,6 +16458,7 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Tab
# define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F)
# define sqlite3TriggerList(X, Y) 0
# define sqlite3ParseToplevel(p) p
+# define sqlite3IsToplevel(p) 1
# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0
#endif
@@ -11525,7 +16479,7 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(Parse*, const char *, const char *, int)
#endif
SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*);
SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*);
-SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
+SQLITE_PRIVATE void sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*);
SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*);
SQLITE_PRIVATE int sqlite3FixSelect(DbFixer*, Select*);
SQLITE_PRIVATE int sqlite3FixExpr(DbFixer*, Expr*);
@@ -11536,61 +16490,69 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *, int*);
SQLITE_PRIVATE int sqlite3Atoi(const char*);
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
-SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8*, const u8**);
+SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);
+SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
+SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
+#endif
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
+ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
+SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
+#endif
/*
** Routines to read and write variable-length integers. These used to
** be defined locally, but now we use the varint routines in the util.c
-** file. Code should use the MACRO forms below, as the Varint32 versions
-** are coded to assume the single byte case is already handled (which
-** the MACRO form does).
+** file.
*/
SQLITE_PRIVATE int sqlite3PutVarint(unsigned char*, u64);
-SQLITE_PRIVATE int sqlite3PutVarint32(unsigned char*, u32);
SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *, u64 *);
SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *, u32 *);
SQLITE_PRIVATE int sqlite3VarintLen(u64 v);
/*
-** The header of a record consists of a sequence variable-length integers.
-** These integers are almost always small and are encoded as a single byte.
-** The following macros take advantage this fact to provide a fast encode
-** and decode of the integers in a record header. It is faster for the common
-** case where the integer is a single byte. It is a little slower when the
-** integer is two or more bytes. But overall it is faster.
-**
-** The following expressions are equivalent:
-**
-** x = sqlite3GetVarint32( A, &B );
-** x = sqlite3PutVarint32( A, B );
-**
-** x = getVarint32( A, B );
-** x = putVarint32( A, B );
-**
+** The common case is for a varint to be a single byte. They following
+** macros handle the common case without a procedure call, but then call
+** the procedure for larger varints.
*/
-#define getVarint32(A,B) (u8)((*(A)<(u8)0x80) ? ((B) = (u32)*(A)),1 : sqlite3GetVarint32((A), (u32 *)&(B)))
-#define putVarint32(A,B) (u8)(((u32)(B)<(u32)0x80) ? (*(A) = (unsigned char)(B)),1 : sqlite3PutVarint32((A), (B)))
+#define getVarint32(A,B) \
+ (u8)((*(A)<(u8)0x80)?((B)=(u32)*(A)),1:sqlite3GetVarint32((A),(u32 *)&(B)))
+#define putVarint32(A,B) \
+ (u8)(((u32)(B)<(u32)0x80)?(*(A)=(unsigned char)(B)),1:\
+ sqlite3PutVarint((A),(B)))
#define getVarint sqlite3GetVarint
#define putVarint sqlite3PutVarint
-SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *);
-SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *);
+SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3*, Index*);
+SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe*, Table*, int);
SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2);
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity);
+SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table*,int);
SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr);
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
-SQLITE_PRIVATE void sqlite3Error(sqlite3*, int, const char*,...);
+SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
+SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
+SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);
+SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int);
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);
+
+#if defined(SQLITE_NEED_ERR_NAME)
+SQLITE_PRIVATE const char *sqlite3ErrName(int);
+#endif
+
SQLITE_PRIVATE const char *sqlite3ErrStr(int);
SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse);
SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(sqlite3*,u8 enc, const char*,int);
SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char*zName);
SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr);
-SQLITE_PRIVATE Expr *sqlite3ExprSetColl(Expr*, CollSeq*);
-SQLITE_PRIVATE Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr*, Token*);
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(Parse *pParse, Expr*, const Token*, int);
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse*,Expr*,const char*);
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr*);
SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *, CollSeq *);
SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *, const char *);
SQLITE_PRIVATE void sqlite3VdbeSetChanges(sqlite3 *, int);
@@ -11603,27 +16565,26 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char*, char*);
#else
# define sqlite3FileSuffix3(X,Y)
#endif
-SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z);
+SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z,u8);
SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value*, u8);
SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value*, u8);
-SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
+SQLITE_PRIVATE void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8,
void(*)(void*));
+SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value*);
SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*);
SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *);
SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int, u8);
-#ifdef SQLITE_ENABLE_STAT2
-SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *);
-#endif
SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **);
SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8);
#ifndef SQLITE_AMALGAMATION
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
+SQLITE_PRIVATE const char sqlite3StrBINARY[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[];
SQLITE_PRIVATE const Token sqlite3IntTokens[];
SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config;
-SQLITE_PRIVATE SQLITE_WSD FuncDefHash sqlite3GlobalFunctions;
+SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte;
#endif
@@ -11635,16 +16596,20 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*);
-SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int);
+SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr *, int, int);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
+SQLITE_PRIVATE int sqlite3MatchSpanName(const char*, const char*, const char*, const char*);
SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
+SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*);
SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
+SQLITE_PRIVATE void sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int);
SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *);
SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *);
-SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(sqlite3*, u8, CollSeq *, const char*);
-SQLITE_PRIVATE char sqlite3AffinityType(const char*);
+SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
+SQLITE_PRIVATE char sqlite3AffinityType(const char*, u8*);
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
@@ -11654,21 +16619,30 @@ SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*);
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*);
SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3*, int);
SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
-SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse*, int, int);
SQLITE_PRIVATE void sqlite3SchemaClear(void *);
SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *, Btree *);
SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *);
-SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *);
-SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3*,int,int);
+SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo*);
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo*);
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse*, Index*);
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo*);
+#endif
+SQLITE_PRIVATE int sqlite3CreateFunc(sqlite3 *, const char *, int, int, void *,
void (*)(sqlite3_context*,int,sqlite3_value **),
void (*)(sqlite3_context*,int,sqlite3_value **), void (*)(sqlite3_context*),
FuncDestructor *pDestructor
);
+SQLITE_PRIVATE void sqlite3OomFault(sqlite3*);
+SQLITE_PRIVATE void sqlite3OomClear(sqlite3*);
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
-SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int);
+SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, sqlite3*, char*, int, int);
SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum*,const char*);
+SQLITE_PRIVATE void sqlite3AppendChar(StrAccum*,int,char);
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*);
SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*);
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int);
@@ -11677,10 +16651,26 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *);
SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
+#ifndef SQLITE_OMIT_SUBQUERY
+SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse*, Expr*);
+#else
+# define sqlite3ExprCheckIN(x,y) SQLITE_OK
+#endif
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void);
+SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
+ Parse*,Index*,UnpackedRecord**,Expr*,int,int,int*);
+SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(Parse*, Expr*, u8, sqlite3_value**);
+SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord*);
+SQLITE_PRIVATE int sqlite3Stat4Column(sqlite3*, const void*, int, int, sqlite3_value**);
+SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3*, Index*, int);
+#endif
+
/*
** The interface to the LEMON-generated parser
*/
-SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(size_t));
+SQLITE_PRIVATE void *sqlite3ParserAlloc(void*(*)(u64));
SQLITE_PRIVATE void sqlite3ParserFree(void*, void(*)(void*));
SQLITE_PRIVATE void sqlite3Parser(void*, int, Token, Parse*);
#ifdef YYTRACKMAXSTACKDEPTH
@@ -11710,23 +16700,29 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char*);
# define sqlite3VtabRollback(X)
# define sqlite3VtabCommit(X)
# define sqlite3VtabInSync(db) 0
-# define sqlite3VtabLock(X)
+# define sqlite3VtabLock(X)
# define sqlite3VtabUnlock(X)
# define sqlite3VtabUnlockList(X)
# define sqlite3VtabSavepoint(X, Y, Z) SQLITE_OK
+# define sqlite3GetVTable(X,Y) ((VTable*)0)
#else
SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table*);
-SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **);
+SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p);
+SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, Vdbe*);
SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db);
SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db);
SQLITE_PRIVATE void sqlite3VtabLock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *);
SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*);
SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *, int, int);
+SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe*, sqlite3_vtab*);
+SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*);
# define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0)
#endif
+SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse*,Module*);
+SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3*,Module*);
SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*);
-SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*);
+SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*, int);
SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse*, Token*);
SQLITE_PRIVATE void sqlite3VtabArgInit(Parse*);
SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse*, Token*);
@@ -11736,42 +16732,55 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
+SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
+SQLITE_PRIVATE void sqlite3ParserReset(Parse*);
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*);
-SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*);
SQLITE_PRIVATE const char *sqlite3JournalModename(int);
-SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
-SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
+#ifndef SQLITE_OMIT_WAL
+SQLITE_PRIVATE int sqlite3Checkpoint(sqlite3*, int, int, int*, int*);
+SQLITE_PRIVATE int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
+#endif
+#ifndef SQLITE_OMIT_CTE
+SQLITE_PRIVATE With *sqlite3WithAdd(Parse*,With*,Token*,ExprList*,Select*);
+SQLITE_PRIVATE void sqlite3WithDelete(sqlite3*,With*);
+SQLITE_PRIVATE void sqlite3WithPush(Parse*, With*, u8);
+#else
+#define sqlite3WithPush(x,y,z)
+#define sqlite3WithDelete(x,y)
+#endif
/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
** key functionality is available. If OMIT_TRIGGER is defined but
** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In
-** this case foreign keys are parsed, but no other functionality is
+** this case foreign keys are parsed, but no other functionality is
** provided (enforcement of FK constraints requires the triggers sub-system).
*/
#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
-SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int);
+SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int, int*, int);
SQLITE_PRIVATE void sqlite3FkDropTable(Parse*, SrcList *, Table*);
-SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int);
+SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int, int*, int);
SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int);
SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*);
SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *);
#else
- #define sqlite3FkActions(a,b,c,d)
- #define sqlite3FkCheck(a,b,c,d)
+ #define sqlite3FkActions(a,b,c,d,e,f)
+ #define sqlite3FkCheck(a,b,c,d,e,f)
#define sqlite3FkDropTable(a,b,c)
- #define sqlite3FkOldmask(a,b) 0
- #define sqlite3FkRequired(a,b,c,d) 0
+ #define sqlite3FkOldmask(a,b) 0
+ #define sqlite3FkRequired(a,b,c,d) 0
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *, Table*);
+SQLITE_PRIVATE int sqlite3FkLocateIndex(Parse*,Table*,FKey*,Index**,int**);
#else
#define sqlite3FkDelete(a,b)
+ #define sqlite3FkLocateIndex(a,b,c,d,e)
#endif
@@ -11794,29 +16803,36 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void);
#define sqlite3EndBenignMalloc()
#endif
-#define IN_INDEX_ROWID 1
-#define IN_INDEX_EPH 2
-#define IN_INDEX_INDEX 3
-SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, int*);
+/*
+** Allowed return values from sqlite3FindInIndex()
+*/
+#define IN_INDEX_ROWID 1 /* Search the rowid of the table */
+#define IN_INDEX_EPH 2 /* Search an ephemeral b-tree */
+#define IN_INDEX_INDEX_ASC 3 /* Existing index ASCENDING */
+#define IN_INDEX_INDEX_DESC 4 /* Existing index DESCENDING */
+#define IN_INDEX_NOOP 5 /* No table available. Use comparisons */
+/*
+** Allowed flags for the 3rd parameter to sqlite3FindInIndex().
+*/
+#define IN_INDEX_NOOP_OK 0x0001 /* OK to return IN_INDEX_NOOP */
+#define IN_INDEX_MEMBERSHIP 0x0002 /* IN operator used for membership test */
+#define IN_INDEX_LOOP 0x0004 /* IN operator used as a loop */
+SQLITE_PRIVATE int sqlite3FindInIndex(Parse *, Expr *, u32, int*, int*);
+SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
+SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
-SQLITE_PRIVATE int sqlite3JournalOpen(sqlite3_vfs *, const char *, sqlite3_file *, int, int);
-SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *);
SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *);
-#else
- #define sqlite3JournalSize(pVfs) ((pVfs)->szOsFile)
#endif
+SQLITE_PRIVATE int sqlite3JournalIsInMemory(sqlite3_file *p);
SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *);
-SQLITE_PRIVATE int sqlite3MemJournalSize(void);
-SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *);
+SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p);
#if SQLITE_MAX_EXPR_DEPTH>0
-SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p);
SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *);
SQLITE_PRIVATE int sqlite3ExprCheckHeight(Parse*, int);
#else
- #define sqlite3ExprSetHeight(x,y)
#define sqlite3SelectExprHeight(x) 0
#define sqlite3ExprCheckHeight(x,y)
#endif
@@ -11841,12 +16857,12 @@ SQLITE_PRIVATE void sqlite3ParserTrace(FILE*, char *);
/*
** If the SQLITE_ENABLE IOTRACE exists then the global variable
** sqlite3IoTrace is a pointer to a printf-like routine used to
-** print I/O tracing messages.
+** print I/O tracing messages.
*/
#ifdef SQLITE_ENABLE_IOTRACE
# define IOTRACE(A) if( sqlite3IoTrace ){ sqlite3IoTrace A; }
SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe*);
-SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*,...);
+SQLITE_API SQLITE_EXTERN void (SQLITE_CDECL *sqlite3IoTrace)(const char*,...);
#else
# define IOTRACE(A)
# define sqlite3VdbeIOTraceSql(X)
@@ -11875,7 +16891,7 @@ SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*,...);
** that allocations that might have been satisfied by lookaside are not
** passed back to non-lookaside free() routines. Asserts such as the
** example above are placed on the non-lookaside free() routines to verify
-** this constraint.
+** this constraint.
**
** All of this is no-op for a production build. It only comes into
** play when the SQLITE_MEMDEBUG compile-time option is used.
@@ -11890,12 +16906,28 @@ SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8);
# define sqlite3MemdebugNoType(X,Y) 1
#endif
#define MEMTYPE_HEAP 0x01 /* General heap allocations */
-#define MEMTYPE_LOOKASIDE 0x02 /* Might have been lookaside memory */
+#define MEMTYPE_LOOKASIDE 0x02 /* Heap that might have been lookaside */
#define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */
#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
-#define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */
-#endif /* _SQLITEINT_H_ */
+/*
+** Threading interface
+*/
+#if SQLITE_MAX_WORKER_THREADS>0
+SQLITE_PRIVATE int sqlite3ThreadCreate(SQLiteThread**,void*(*)(void*),void*);
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread*, void**);
+#endif
+
+#if defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)
+SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3*);
+#endif
+
+SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr);
+SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr);
+SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr*, int);
+SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(Parse*,Expr*,int);
+
+#endif /* SQLITEINT_H */
/************** End of sqliteInt.h *******************************************/
/************** Begin file global.c ******************************************/
@@ -11911,8 +16943,9 @@ SQLITE_PRIVATE int sqlite3MemdebugNoType(void*,u8);
**
*************************************************************************
**
-** This file contains definitions of global variables and contants.
+** This file contains definitions of global variables and constants.
*/
+/* #include "sqliteInt.h" */
/* An array to map all upper-case characters into their corresponding
** lower-case character.
@@ -11946,16 +16979,16 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, /* 3x */
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, /* 4x */
80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, /* 5x */
- 96, 97, 66, 67, 68, 69, 70, 71, 72, 73,106,107,108,109,110,111, /* 6x */
- 112, 81, 82, 83, 84, 85, 86, 87, 88, 89,122,123,124,125,126,127, /* 7x */
+ 96, 97, 98, 99,100,101,102,103,104,105,106,107,108,109,110,111, /* 6x */
+ 112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127, /* 7x */
128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143, /* 8x */
- 144,145,146,147,148,149,150,151,152,153,154,155,156,157,156,159, /* 9x */
+ 144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159, /* 9x */
160,161,162,163,164,165,166,167,168,169,170,171,140,141,142,175, /* Ax */
176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191, /* Bx */
192,129,130,131,132,133,134,135,136,137,202,203,204,205,206,207, /* Cx */
208,145,146,147,148,149,150,151,152,153,218,219,220,221,222,223, /* Dx */
- 224,225,162,163,164,165,166,167,168,169,232,203,204,205,206,207, /* Ex */
- 239,240,241,242,243,244,245,246,247,248,249,219,220,221,222,255, /* Fx */
+ 224,225,162,163,164,165,166,167,168,169,234,235,236,237,238,239, /* Ex */
+ 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255, /* Fx */
#endif
};
@@ -11970,6 +17003,7 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
** isxdigit() 0x08
** toupper() 0x20
** SQLite identifier character 0x40
+** Quote character 0x80
**
** Bit 0x20 is set if the mapped character requires translation to upper
** case. i.e. if the character is a lower-case ASCII character.
@@ -11978,16 +17012,13 @@ SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[] = {
**
** (x & ~(map[x]&0x20))
**
-** Standard function tolower() is implemented using the sqlite3UpperToLower[]
+** The equivalent of tolower() is implemented using the sqlite3UpperToLower[]
** array. tolower() is used more often than toupper() by SQLite.
**
-** Bit 0x40 is set if the character non-alphanumeric and can be used in an
+** Bit 0x40 is set if the character is non-alphanumeric and can be used in an
** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any
** non-ASCII UTF character. Hence the test for whether or not a character is
** part of an identifier is 0x46.
-**
-** SQLite's versions are identical to the standard versions assuming a
-** locale of "C". They are implemented as macros in sqliteInt.h.
*/
#ifdef SQLITE_ASCII
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
@@ -11995,7 +17026,7 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */
- 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */
+ 0x01, 0x00, 0x80, 0x00, 0x40, 0x00, 0x00, 0x80, /* 20..27 !"#$%&' */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */
0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */
0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */
@@ -12003,8 +17034,8 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */
- 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
- 0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
+ 0x02, 0x02, 0x02, 0x80, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */
+ 0x80, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */
0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */
0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */
@@ -12029,10 +17060,48 @@ SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[256] = {
};
#endif
+/* EVIDENCE-OF: R-02982-34736 In order to maintain full backwards
+** compatibility for legacy applications, the URI filename capability is
+** disabled by default.
+**
+** EVIDENCE-OF: R-38799-08373 URI filenames can be enabled or disabled
+** using the SQLITE_USE_URI=1 or SQLITE_USE_URI=0 compile-time options.
+**
+** EVIDENCE-OF: R-43642-56306 By default, URI handling is globally
+** disabled. The default value may be changed by compiling with the
+** SQLITE_USE_URI symbol defined.
+*/
#ifndef SQLITE_USE_URI
# define SQLITE_USE_URI 0
#endif
+/* EVIDENCE-OF: R-38720-18127 The default setting is determined by the
+** SQLITE_ALLOW_COVERING_INDEX_SCAN compile-time option, or is "on" if
+** that compile-time option is omitted.
+*/
+#ifndef SQLITE_ALLOW_COVERING_INDEX_SCAN
+# define SQLITE_ALLOW_COVERING_INDEX_SCAN 1
+#endif
+
+/* The minimum PMA size is set to this value multiplied by the database
+** page size in bytes.
+*/
+#ifndef SQLITE_SORTER_PMASZ
+# define SQLITE_SORTER_PMASZ 250
+#endif
+
+/* Statement journals spill to disk when their size exceeds the following
+** threshold (in bytes). 0 means that statement journals are created and
+** written to disk immediately (the default behavior for SQLite versions
+** before 3.12.0). -1 means always keep the entire statement journal in
+** memory. (The statement journal is also always held entirely in memory
+** if journal_mode=MEMORY or if temp_store=MEMORY, regardless of this
+** setting.)
+*/
+#ifndef SQLITE_STMTJRNL_SPILL
+# define SQLITE_STMTJRNL_SPILL (64*1024)
+#endif
+
/*
** The following singleton contains the global configuration for
** the SQLite library.
@@ -12042,43 +17111,60 @@ SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config = {
1, /* bCoreMutex */
SQLITE_THREADSAFE==1, /* bFullMutex */
SQLITE_USE_URI, /* bOpenUri */
+ SQLITE_ALLOW_COVERING_INDEX_SCAN, /* bUseCis */
0x7ffffffe, /* mxStrlen */
+ 0, /* neverCorrupt */
128, /* szLookaside */
500, /* nLookaside */
+ SQLITE_STMTJRNL_SPILL, /* nStmtSpill */
{0,0,0,0,0,0,0,0}, /* m */
{0,0,0,0,0,0,0,0,0}, /* mutex */
- {0,0,0,0,0,0,0,0,0,0,0}, /* pcache */
+ {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
(void*)0, /* pHeap */
0, /* nHeap */
0, 0, /* mnHeap, mxHeap */
+ SQLITE_DEFAULT_MMAP_SIZE, /* szMmap */
+ SQLITE_MAX_MMAP_SIZE, /* mxMmap */
(void*)0, /* pScratch */
0, /* szScratch */
0, /* nScratch */
(void*)0, /* pPage */
0, /* szPage */
- 0, /* nPage */
+ SQLITE_DEFAULT_PCACHE_INITSZ, /* nPage */
0, /* mxParserStack */
0, /* sharedCacheEnabled */
+ SQLITE_SORTER_PMASZ, /* szPma */
/* All the rest should always be initialized to zero */
0, /* isInit */
0, /* inProgress */
0, /* isMutexInit */
0, /* isMallocInit */
0, /* isPCacheInit */
- 0, /* pInitMutex */
0, /* nRefInitMutex */
+ 0, /* pInitMutex */
0, /* xLog */
0, /* pLogArg */
+#ifdef SQLITE_ENABLE_SQLLOG
+ 0, /* xSqllog */
+ 0, /* pSqllogArg */
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ 0, /* xVdbeBranch */
+ 0, /* pVbeBranchArg */
+#endif
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+ 0, /* xTestCallback */
+#endif
0, /* bLocaltimeFault */
+ 0x7ffffffe /* iOnceResetThreshold */
};
-
/*
** Hash table for global functions - functions common to all
** database connections. After initialization, this table is
** read-only.
*/
-SQLITE_PRIVATE SQLITE_WSD FuncDefHash sqlite3GlobalFunctions;
+SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
/*
** Constant tokens for values 0 and 1.
@@ -12093,7 +17179,7 @@ SQLITE_PRIVATE const Token sqlite3IntTokens[] = {
** The value of the "pending" byte must be 0x40000000 (1 byte past the
** 1-gibabyte boundary) in a compatible database. SQLite never uses
** the database page that contains the pending byte. It never attempts
-** to read or write that page. The pending byte page is set assign
+** to read or write that page. The pending byte page is set aside
** for use by the VFS layers as space for managing file locks.
**
** During testing, it is often desirable to move the pending byte to
@@ -12104,13 +17190,14 @@ SQLITE_PRIVATE const Token sqlite3IntTokens[] = {
**
** IMPORTANT: Changing the pending byte to any value other than
** 0x40000000 results in an incompatible database file format!
-** Changing the pending byte during operating results in undefined
-** and dileterious behavior.
+** Changing the pending byte during operation will result in undefined
+** and incorrect behavior.
*/
#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000;
#endif
+/* #include "opcodes.h" */
/*
** Properties of opcodes. The OPFLG_INITIALIZER macro is
** created by mkopcodeh.awk during compilation. Data is obtained
@@ -12119,6 +17206,11 @@ SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000;
*/
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER;
+/*
+** Name of the default collating sequence
+*/
+SQLITE_PRIVATE const char sqlite3StrBINARY[] = "BINARY";
+
/************** End of global.c **********************************************/
/************** Begin file ctime.c *******************************************/
/*
@@ -12139,6 +17231,7 @@ SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER;
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
+/* #include "sqliteInt.h" */
/*
** An array of names of all compile-time options. This array should
@@ -12155,336 +17248,375 @@ static const char * const azCompileOpt[] = {
#define CTIMEOPT_VAL_(opt) #opt
#define CTIMEOPT_VAL(opt) CTIMEOPT_VAL_(opt)
-#ifdef SQLITE_32BIT_ROWID
+#if SQLITE_32BIT_ROWID
"32BIT_ROWID",
#endif
-#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC
+#if SQLITE_4_BYTE_ALIGNED_MALLOC
"4_BYTE_ALIGNED_MALLOC",
#endif
-#ifdef SQLITE_CASE_SENSITIVE_LIKE
+#if SQLITE_CASE_SENSITIVE_LIKE
"CASE_SENSITIVE_LIKE",
#endif
-#ifdef SQLITE_CHECK_PAGES
+#if SQLITE_CHECK_PAGES
"CHECK_PAGES",
#endif
-#ifdef SQLITE_COVERAGE_TEST
+#if defined(__clang__) && defined(__clang_major__)
+ "COMPILER=clang-" CTIMEOPT_VAL(__clang_major__) "."
+ CTIMEOPT_VAL(__clang_minor__) "."
+ CTIMEOPT_VAL(__clang_patchlevel__),
+#elif defined(_MSC_VER)
+ "COMPILER=msvc-" CTIMEOPT_VAL(_MSC_VER),
+#elif defined(__GNUC__) && defined(__VERSION__)
+ "COMPILER=gcc-" __VERSION__,
+#endif
+#if SQLITE_COVERAGE_TEST
"COVERAGE_TEST",
#endif
-#ifdef SQLITE_DEBUG
+#if SQLITE_DEBUG
"DEBUG",
#endif
-#ifdef SQLITE_DEFAULT_LOCKING_MODE
+#if SQLITE_DEFAULT_LOCKING_MODE
"DEFAULT_LOCKING_MODE=" CTIMEOPT_VAL(SQLITE_DEFAULT_LOCKING_MODE),
#endif
-#ifdef SQLITE_DISABLE_DIRSYNC
+#if defined(SQLITE_DEFAULT_MMAP_SIZE) && !defined(SQLITE_DEFAULT_MMAP_SIZE_xc)
+ "DEFAULT_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_DEFAULT_MMAP_SIZE),
+#endif
+#if SQLITE_DISABLE_DIRSYNC
"DISABLE_DIRSYNC",
#endif
-#ifdef SQLITE_DISABLE_LFS
+#if SQLITE_DISABLE_LFS
"DISABLE_LFS",
#endif
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+#if SQLITE_ENABLE_8_3_NAMES
+ "ENABLE_8_3_NAMES=" CTIMEOPT_VAL(SQLITE_ENABLE_8_3_NAMES),
+#endif
+#if SQLITE_ENABLE_API_ARMOR
+ "ENABLE_API_ARMOR",
+#endif
+#if SQLITE_ENABLE_ATOMIC_WRITE
"ENABLE_ATOMIC_WRITE",
#endif
-#ifdef SQLITE_ENABLE_CEROD
+#if SQLITE_ENABLE_CEROD
"ENABLE_CEROD",
#endif
-#ifdef SQLITE_ENABLE_COLUMN_METADATA
+#if SQLITE_ENABLE_COLUMN_METADATA
"ENABLE_COLUMN_METADATA",
#endif
-#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
+#if SQLITE_ENABLE_DBSTAT_VTAB
+ "ENABLE_DBSTAT_VTAB",
+#endif
+#if SQLITE_ENABLE_EXPENSIVE_ASSERT
"ENABLE_EXPENSIVE_ASSERT",
#endif
-#ifdef SQLITE_ENABLE_FTS1
+#if SQLITE_ENABLE_FTS1
"ENABLE_FTS1",
#endif
-#ifdef SQLITE_ENABLE_FTS2
+#if SQLITE_ENABLE_FTS2
"ENABLE_FTS2",
#endif
-#ifdef SQLITE_ENABLE_FTS3
+#if SQLITE_ENABLE_FTS3
"ENABLE_FTS3",
#endif
-#ifdef SQLITE_ENABLE_FTS3_PARENTHESIS
+#if SQLITE_ENABLE_FTS3_PARENTHESIS
"ENABLE_FTS3_PARENTHESIS",
#endif
-#ifdef SQLITE_ENABLE_FTS4
+#if SQLITE_ENABLE_FTS4
"ENABLE_FTS4",
#endif
-#ifdef SQLITE_ENABLE_ICU
+#if SQLITE_ENABLE_FTS5
+ "ENABLE_FTS5",
+#endif
+#if SQLITE_ENABLE_ICU
"ENABLE_ICU",
#endif
-#ifdef SQLITE_ENABLE_IOTRACE
+#if SQLITE_ENABLE_IOTRACE
"ENABLE_IOTRACE",
#endif
-#ifdef SQLITE_ENABLE_LOAD_EXTENSION
+#if SQLITE_ENABLE_JSON1
+ "ENABLE_JSON1",
+#endif
+#if SQLITE_ENABLE_LOAD_EXTENSION
"ENABLE_LOAD_EXTENSION",
#endif
-#ifdef SQLITE_ENABLE_LOCKING_STYLE
+#if SQLITE_ENABLE_LOCKING_STYLE
"ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
#endif
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+#if SQLITE_ENABLE_MEMORY_MANAGEMENT
"ENABLE_MEMORY_MANAGEMENT",
#endif
-#ifdef SQLITE_ENABLE_MEMSYS3
+#if SQLITE_ENABLE_MEMSYS3
"ENABLE_MEMSYS3",
#endif
-#ifdef SQLITE_ENABLE_MEMSYS5
+#if SQLITE_ENABLE_MEMSYS5
"ENABLE_MEMSYS5",
#endif
-#ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK
+#if SQLITE_ENABLE_OVERSIZE_CELL_CHECK
"ENABLE_OVERSIZE_CELL_CHECK",
#endif
-#ifdef SQLITE_ENABLE_RTREE
+#if SQLITE_ENABLE_RTREE
"ENABLE_RTREE",
#endif
-#ifdef SQLITE_ENABLE_STAT2
- "ENABLE_STAT2",
+#if defined(SQLITE_ENABLE_STAT4)
+ "ENABLE_STAT4",
+#elif defined(SQLITE_ENABLE_STAT3)
+ "ENABLE_STAT3",
#endif
-#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
+#if SQLITE_ENABLE_UNLOCK_NOTIFY
"ENABLE_UNLOCK_NOTIFY",
#endif
-#ifdef SQLITE_ENABLE_UPDATE_DELETE_LIMIT
+#if SQLITE_ENABLE_UPDATE_DELETE_LIMIT
"ENABLE_UPDATE_DELETE_LIMIT",
#endif
-#ifdef SQLITE_HAS_CODEC
+#if SQLITE_HAS_CODEC
"HAS_CODEC",
#endif
-#ifdef SQLITE_HAVE_ISNAN
+#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
"HAVE_ISNAN",
#endif
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
+#if SQLITE_HOMEGROWN_RECURSIVE_MUTEX
"HOMEGROWN_RECURSIVE_MUTEX",
#endif
-#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS
+#if SQLITE_IGNORE_AFP_LOCK_ERRORS
"IGNORE_AFP_LOCK_ERRORS",
#endif
-#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS
+#if SQLITE_IGNORE_FLOCK_LOCK_ERRORS
"IGNORE_FLOCK_LOCK_ERRORS",
#endif
#ifdef SQLITE_INT64_TYPE
"INT64_TYPE",
#endif
-#ifdef SQLITE_LOCK_TRACE
+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ "LIKE_DOESNT_MATCH_BLOBS",
+#endif
+#if SQLITE_LOCK_TRACE
"LOCK_TRACE",
#endif
+#if defined(SQLITE_MAX_MMAP_SIZE) && !defined(SQLITE_MAX_MMAP_SIZE_xc)
+ "MAX_MMAP_SIZE=" CTIMEOPT_VAL(SQLITE_MAX_MMAP_SIZE),
+#endif
#ifdef SQLITE_MAX_SCHEMA_RETRY
"MAX_SCHEMA_RETRY=" CTIMEOPT_VAL(SQLITE_MAX_SCHEMA_RETRY),
#endif
-#ifdef SQLITE_MEMDEBUG
+#if SQLITE_MEMDEBUG
"MEMDEBUG",
#endif
-#ifdef SQLITE_MIXED_ENDIAN_64BIT_FLOAT
+#if SQLITE_MIXED_ENDIAN_64BIT_FLOAT
"MIXED_ENDIAN_64BIT_FLOAT",
#endif
-#ifdef SQLITE_NO_SYNC
+#if SQLITE_NO_SYNC
"NO_SYNC",
#endif
-#ifdef SQLITE_OMIT_ALTERTABLE
+#if SQLITE_OMIT_ALTERTABLE
"OMIT_ALTERTABLE",
#endif
-#ifdef SQLITE_OMIT_ANALYZE
+#if SQLITE_OMIT_ANALYZE
"OMIT_ANALYZE",
#endif
-#ifdef SQLITE_OMIT_ATTACH
+#if SQLITE_OMIT_ATTACH
"OMIT_ATTACH",
#endif
-#ifdef SQLITE_OMIT_AUTHORIZATION
+#if SQLITE_OMIT_AUTHORIZATION
"OMIT_AUTHORIZATION",
#endif
-#ifdef SQLITE_OMIT_AUTOINCREMENT
+#if SQLITE_OMIT_AUTOINCREMENT
"OMIT_AUTOINCREMENT",
#endif
-#ifdef SQLITE_OMIT_AUTOINIT
+#if SQLITE_OMIT_AUTOINIT
"OMIT_AUTOINIT",
#endif
-#ifdef SQLITE_OMIT_AUTOMATIC_INDEX
+#if SQLITE_OMIT_AUTOMATIC_INDEX
"OMIT_AUTOMATIC_INDEX",
#endif
-#ifdef SQLITE_OMIT_AUTORESET
+#if SQLITE_OMIT_AUTORESET
"OMIT_AUTORESET",
#endif
-#ifdef SQLITE_OMIT_AUTOVACUUM
+#if SQLITE_OMIT_AUTOVACUUM
"OMIT_AUTOVACUUM",
#endif
-#ifdef SQLITE_OMIT_BETWEEN_OPTIMIZATION
+#if SQLITE_OMIT_BETWEEN_OPTIMIZATION
"OMIT_BETWEEN_OPTIMIZATION",
#endif
-#ifdef SQLITE_OMIT_BLOB_LITERAL
+#if SQLITE_OMIT_BLOB_LITERAL
"OMIT_BLOB_LITERAL",
#endif
-#ifdef SQLITE_OMIT_BTREECOUNT
+#if SQLITE_OMIT_BTREECOUNT
"OMIT_BTREECOUNT",
#endif
-#ifdef SQLITE_OMIT_BUILTIN_TEST
+#if SQLITE_OMIT_BUILTIN_TEST
"OMIT_BUILTIN_TEST",
#endif
-#ifdef SQLITE_OMIT_CAST
+#if SQLITE_OMIT_CAST
"OMIT_CAST",
#endif
-#ifdef SQLITE_OMIT_CHECK
+#if SQLITE_OMIT_CHECK
"OMIT_CHECK",
#endif
-/* // redundant
-** #ifdef SQLITE_OMIT_COMPILEOPTION_DIAGS
-** "OMIT_COMPILEOPTION_DIAGS",
-** #endif
-*/
-#ifdef SQLITE_OMIT_COMPLETE
+#if SQLITE_OMIT_COMPLETE
"OMIT_COMPLETE",
#endif
-#ifdef SQLITE_OMIT_COMPOUND_SELECT
+#if SQLITE_OMIT_COMPOUND_SELECT
"OMIT_COMPOUND_SELECT",
#endif
-#ifdef SQLITE_OMIT_DATETIME_FUNCS
+#if SQLITE_OMIT_CTE
+ "OMIT_CTE",
+#endif
+#if SQLITE_OMIT_DATETIME_FUNCS
"OMIT_DATETIME_FUNCS",
#endif
-#ifdef SQLITE_OMIT_DECLTYPE
+#if SQLITE_OMIT_DECLTYPE
"OMIT_DECLTYPE",
#endif
-#ifdef SQLITE_OMIT_DEPRECATED
+#if SQLITE_OMIT_DEPRECATED
"OMIT_DEPRECATED",
#endif
-#ifdef SQLITE_OMIT_DISKIO
+#if SQLITE_OMIT_DISKIO
"OMIT_DISKIO",
#endif
-#ifdef SQLITE_OMIT_EXPLAIN
+#if SQLITE_OMIT_EXPLAIN
"OMIT_EXPLAIN",
#endif
-#ifdef SQLITE_OMIT_FLAG_PRAGMAS
+#if SQLITE_OMIT_FLAG_PRAGMAS
"OMIT_FLAG_PRAGMAS",
#endif
-#ifdef SQLITE_OMIT_FLOATING_POINT
+#if SQLITE_OMIT_FLOATING_POINT
"OMIT_FLOATING_POINT",
#endif
-#ifdef SQLITE_OMIT_FOREIGN_KEY
+#if SQLITE_OMIT_FOREIGN_KEY
"OMIT_FOREIGN_KEY",
#endif
-#ifdef SQLITE_OMIT_GET_TABLE
+#if SQLITE_OMIT_GET_TABLE
"OMIT_GET_TABLE",
#endif
-#ifdef SQLITE_OMIT_INCRBLOB
+#if SQLITE_OMIT_INCRBLOB
"OMIT_INCRBLOB",
#endif
-#ifdef SQLITE_OMIT_INTEGRITY_CHECK
+#if SQLITE_OMIT_INTEGRITY_CHECK
"OMIT_INTEGRITY_CHECK",
#endif
-#ifdef SQLITE_OMIT_LIKE_OPTIMIZATION
+#if SQLITE_OMIT_LIKE_OPTIMIZATION
"OMIT_LIKE_OPTIMIZATION",
#endif
-#ifdef SQLITE_OMIT_LOAD_EXTENSION
+#if SQLITE_OMIT_LOAD_EXTENSION
"OMIT_LOAD_EXTENSION",
#endif
-#ifdef SQLITE_OMIT_LOCALTIME
+#if SQLITE_OMIT_LOCALTIME
"OMIT_LOCALTIME",
#endif
-#ifdef SQLITE_OMIT_LOOKASIDE
+#if SQLITE_OMIT_LOOKASIDE
"OMIT_LOOKASIDE",
#endif
-#ifdef SQLITE_OMIT_MEMORYDB
+#if SQLITE_OMIT_MEMORYDB
"OMIT_MEMORYDB",
#endif
-#ifdef SQLITE_OMIT_MERGE_SORT
- "OMIT_MERGE_SORT",
-#endif
-#ifdef SQLITE_OMIT_OR_OPTIMIZATION
+#if SQLITE_OMIT_OR_OPTIMIZATION
"OMIT_OR_OPTIMIZATION",
#endif
-#ifdef SQLITE_OMIT_PAGER_PRAGMAS
+#if SQLITE_OMIT_PAGER_PRAGMAS
"OMIT_PAGER_PRAGMAS",
#endif
-#ifdef SQLITE_OMIT_PRAGMA
+#if SQLITE_OMIT_PRAGMA
"OMIT_PRAGMA",
#endif
-#ifdef SQLITE_OMIT_PROGRESS_CALLBACK
+#if SQLITE_OMIT_PROGRESS_CALLBACK
"OMIT_PROGRESS_CALLBACK",
#endif
-#ifdef SQLITE_OMIT_QUICKBALANCE
+#if SQLITE_OMIT_QUICKBALANCE
"OMIT_QUICKBALANCE",
#endif
-#ifdef SQLITE_OMIT_REINDEX
+#if SQLITE_OMIT_REINDEX
"OMIT_REINDEX",
#endif
-#ifdef SQLITE_OMIT_SCHEMA_PRAGMAS
+#if SQLITE_OMIT_SCHEMA_PRAGMAS
"OMIT_SCHEMA_PRAGMAS",
#endif
-#ifdef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
+#if SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
"OMIT_SCHEMA_VERSION_PRAGMAS",
#endif
-#ifdef SQLITE_OMIT_SHARED_CACHE
+#if SQLITE_OMIT_SHARED_CACHE
"OMIT_SHARED_CACHE",
#endif
-#ifdef SQLITE_OMIT_SUBQUERY
+#if SQLITE_OMIT_SUBQUERY
"OMIT_SUBQUERY",
#endif
-#ifdef SQLITE_OMIT_TCL_VARIABLE
+#if SQLITE_OMIT_TCL_VARIABLE
"OMIT_TCL_VARIABLE",
#endif
-#ifdef SQLITE_OMIT_TEMPDB
+#if SQLITE_OMIT_TEMPDB
"OMIT_TEMPDB",
#endif
-#ifdef SQLITE_OMIT_TRACE
+#if SQLITE_OMIT_TRACE
"OMIT_TRACE",
#endif
-#ifdef SQLITE_OMIT_TRIGGER
+#if SQLITE_OMIT_TRIGGER
"OMIT_TRIGGER",
#endif
-#ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION
+#if SQLITE_OMIT_TRUNCATE_OPTIMIZATION
"OMIT_TRUNCATE_OPTIMIZATION",
#endif
-#ifdef SQLITE_OMIT_UTF16
+#if SQLITE_OMIT_UTF16
"OMIT_UTF16",
#endif
-#ifdef SQLITE_OMIT_VACUUM
+#if SQLITE_OMIT_VACUUM
"OMIT_VACUUM",
#endif
-#ifdef SQLITE_OMIT_VIEW
+#if SQLITE_OMIT_VIEW
"OMIT_VIEW",
#endif
-#ifdef SQLITE_OMIT_VIRTUALTABLE
+#if SQLITE_OMIT_VIRTUALTABLE
"OMIT_VIRTUALTABLE",
#endif
-#ifdef SQLITE_OMIT_WAL
+#if SQLITE_OMIT_WAL
"OMIT_WAL",
#endif
-#ifdef SQLITE_OMIT_WSD
+#if SQLITE_OMIT_WSD
"OMIT_WSD",
#endif
-#ifdef SQLITE_OMIT_XFER_OPT
+#if SQLITE_OMIT_XFER_OPT
"OMIT_XFER_OPT",
#endif
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- "PAGECACHE_BLOCKALLOC",
-#endif
-#ifdef SQLITE_PERFORMANCE_TRACE
+#if SQLITE_PERFORMANCE_TRACE
"PERFORMANCE_TRACE",
#endif
-#ifdef SQLITE_PROXY_DEBUG
+#if SQLITE_PROXY_DEBUG
"PROXY_DEBUG",
#endif
-#ifdef SQLITE_SECURE_DELETE
+#if SQLITE_RTREE_INT_ONLY
+ "RTREE_INT_ONLY",
+#endif
+#if SQLITE_SECURE_DELETE
"SECURE_DELETE",
#endif
-#ifdef SQLITE_SMALL_STACK
+#if SQLITE_SMALL_STACK
"SMALL_STACK",
#endif
-#ifdef SQLITE_SOUNDEX
+#if SQLITE_SOUNDEX
"SOUNDEX",
#endif
-#ifdef SQLITE_TCL
+#if SQLITE_SYSTEM_MALLOC
+ "SYSTEM_MALLOC",
+#endif
+#if SQLITE_TCL
"TCL",
#endif
-#ifdef SQLITE_TEMP_STORE
+#if defined(SQLITE_TEMP_STORE) && !defined(SQLITE_TEMP_STORE_xc)
"TEMP_STORE=" CTIMEOPT_VAL(SQLITE_TEMP_STORE),
#endif
-#ifdef SQLITE_TEST
+#if SQLITE_TEST
"TEST",
#endif
-#ifdef SQLITE_THREADSAFE
+#if defined(SQLITE_THREADSAFE)
"THREADSAFE=" CTIMEOPT_VAL(SQLITE_THREADSAFE),
#endif
-#ifdef SQLITE_USE_ALLOCA
+#if SQLITE_USE_ALLOCA
"USE_ALLOCA",
#endif
-#ifdef SQLITE_ZERO_MALLOC
+#if SQLITE_USER_AUTHENTICATION
+ "USER_AUTHENTICATION",
+#endif
+#if SQLITE_WIN32_MALLOC
+ "WIN32_MALLOC",
+#endif
+#if SQLITE_ZERO_MALLOC
"ZERO_MALLOC"
#endif
};
@@ -12498,14 +17630,24 @@ static const char * const azCompileOpt[] = {
*/
SQLITE_API int sqlite3_compileoption_used(const char *zOptName){
int i, n;
+
+#if SQLITE_ENABLE_API_ARMOR
+ if( zOptName==0 ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
if( sqlite3StrNICmp(zOptName, "SQLITE_", 7)==0 ) zOptName += 7;
n = sqlite3Strlen30(zOptName);
/* Since ArraySize(azCompileOpt) is normally in single digits, a
** linear search is adequate. No need for a binary search. */
for(i=0; i<ArraySize(azCompileOpt); i++){
- if( (sqlite3StrNICmp(zOptName, azCompileOpt[i], n)==0)
- && ( (azCompileOpt[i][n]==0) || (azCompileOpt[i][n]=='=') ) ) return 1;
+ if( sqlite3StrNICmp(zOptName, azCompileOpt[i], n)==0
+ && sqlite3IsIdChar((unsigned char)azCompileOpt[i][n])==0
+ ){
+ return 1;
+ }
}
return 0;
}
@@ -12540,6 +17682,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N){
** This module implements the sqlite3_status() interface and related
** functionality.
*/
+/* #include "sqliteInt.h" */
/************** Include vdbeInt.h in the middle of status.c ******************/
/************** Begin file vdbeInt.h *****************************************/
/*
@@ -12559,8 +17702,27 @@ SQLITE_API const char *sqlite3_compileoption_get(int N){
** 6000 lines long) it was split up into several smaller files and
** this header information was factored out.
*/
-#ifndef _VDBEINT_H_
-#define _VDBEINT_H_
+#ifndef SQLITE_VDBEINT_H
+#define SQLITE_VDBEINT_H
+
+/*
+** The maximum number of times that a statement will try to reparse
+** itself before giving up and returning SQLITE_SCHEMA.
+*/
+#ifndef SQLITE_MAX_SCHEMA_RETRY
+# define SQLITE_MAX_SCHEMA_RETRY 50
+#endif
+
+/*
+** VDBE_DISPLAY_P4 is true or false depending on whether or not the
+** "explain" P4 display logic is enabled.
+*/
+#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
+ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+# define VDBE_DISPLAY_P4 1
+#else
+# define VDBE_DISPLAY_P4 0
+#endif
/*
** SQL is translated into a sequence of instructions to be
@@ -12572,48 +17734,63 @@ typedef struct VdbeOp Op;
/*
** Boolean values
*/
-typedef unsigned char Bool;
+typedef unsigned Bool;
/* Opaque type used by code in vdbesort.c */
typedef struct VdbeSorter VdbeSorter;
+/* Elements of the linked list at Vdbe.pAuxData */
+typedef struct AuxData AuxData;
+
+/* Types of VDBE cursors */
+#define CURTYPE_BTREE 0
+#define CURTYPE_SORTER 1
+#define CURTYPE_VTAB 2
+#define CURTYPE_PSEUDO 3
+
/*
-** A cursor is a pointer into a single BTree within a database file.
-** The cursor can seek to a BTree entry with a particular key, or
-** loop over all entries of the Btree. You can also insert new BTree
-** entries or retrieve the key or data from the entry that the cursor
-** is currently pointing to.
-**
-** Every cursor that the virtual machine has open is represented by an
-** instance of the following structure.
+** A VdbeCursor is an superclass (a wrapper) for various cursor objects:
+**
+** * A b-tree cursor
+** - In the main database or in an ephemeral database
+** - On either an index or a table
+** * A sorter
+** * A virtual table
+** * A one-row "pseudotable" stored in a single register
*/
+typedef struct VdbeCursor VdbeCursor;
struct VdbeCursor {
- BtCursor *pCursor; /* The cursor structure of the backend */
+ u8 eCurType; /* One of the CURTYPE_* values above */
+ i8 iDb; /* Index of cursor database in db->aDb[] (or -1) */
+ u8 nullRow; /* True if pointing to a row with no data */
+ u8 deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
+ u8 isTable; /* True for rowid tables. False for indexes */
+#ifdef SQLITE_DEBUG
+ u8 seekOp; /* Most recent seek operation on this cursor */
+ u8 wrFlag; /* The wrFlag argument to sqlite3BtreeCursor() */
+#endif
+ Bool isEphemeral:1; /* True for an ephemeral table */
+ Bool useRandomRowid:1;/* Generate new record numbers semi-randomly */
+ Bool isOrdered:1; /* True if the table is not BTREE_UNORDERED */
+ Pgno pgnoRoot; /* Root page of the open btree cursor */
+ i16 nField; /* Number of fields in the header */
+ u16 nHdrParsed; /* Number of header fields parsed so far */
+ union {
+ BtCursor *pCursor; /* CURTYPE_BTREE. Btree cursor */
+ sqlite3_vtab_cursor *pVCur; /* CURTYPE_VTAB. Vtab cursor */
+ int pseudoTableReg; /* CURTYPE_PSEUDO. Reg holding content. */
+ VdbeSorter *pSorter; /* CURTYPE_SORTER. Sorter object */
+ } uc;
Btree *pBt; /* Separate file holding temporary table */
KeyInfo *pKeyInfo; /* Info about index keys needed by index cursors */
- int iDb; /* Index of cursor database in db->aDb[] (or -1) */
- int pseudoTableReg; /* Register holding pseudotable content. */
- int nField; /* Number of fields in the header */
- Bool zeroed; /* True if zeroed out and ready for reuse */
- Bool rowidIsValid; /* True if lastRowid is valid */
- Bool atFirst; /* True if pointing to first entry */
- Bool useRandomRowid; /* Generate new record numbers semi-randomly */
- Bool nullRow; /* True if pointing to a row with no data */
- Bool deferredMoveto; /* A call to sqlite3BtreeMoveto() is needed */
- Bool isTable; /* True if a table requiring integer keys */
- Bool isIndex; /* True if an index containing keys only - no data */
- Bool isOrdered; /* True if the underlying table is BTREE_UNORDERED */
- Bool isSorter; /* True if a new-style sorter */
- sqlite3_vtab_cursor *pVtabCursor; /* The cursor for a virtual table */
- const sqlite3_module *pModule; /* Module for cursor pVtabCursor */
+ int seekResult; /* Result of previous sqlite3BtreeMoveto() */
i64 seqCount; /* Sequence counter */
i64 movetoTarget; /* Argument to the deferred sqlite3BtreeMoveto() */
- i64 lastRowid; /* Last rowid from a Next or NextIdx operation */
- VdbeSorter *pSorter; /* Sorter object for OP_SorterOpen cursors */
-
- /* Result of last sqlite3BtreeMoveto() done by an OP_NotExists or
- ** OP_IsUnique opcode on this cursor. */
- int seekResult;
+ VdbeCursor *pAltCursor; /* Associated index cursor from which to read */
+ int *aAltMap; /* Mapping from table to index column numbers */
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ u64 maskUsed; /* Mask of columns used by this cursor */
+#endif
/* Cached information about the header for the data record that the
** cursor is currently pointing to. Only valid if cacheStatus matches
@@ -12625,12 +17802,22 @@ struct VdbeCursor {
** be NULL.
*/
u32 cacheStatus; /* Cache is valid if this matches Vdbe.cacheCtr */
- int payloadSize; /* Total number of bytes in the record */
- u32 *aType; /* Type values for all entries in the record */
- u32 *aOffset; /* Cached offsets to the start of each columns data */
- u8 *aRow; /* Data for the current row, if all on one page */
+ u32 payloadSize; /* Total number of bytes in the record */
+ u32 szRow; /* Byte available in aRow */
+ u32 iHdrOffset; /* Offset to next unparsed byte of the header */
+ const u8 *aRow; /* Data for the current row, if all on one page */
+ u32 *aOffset; /* Pointer to aType[nField] */
+ u32 aType[1]; /* Type values for all entries in the record */
+ /* 2*nField extra array elements allocated for aType[], beyond the one
+ ** static element declared in the structure. nField total array slots for
+ ** aType[] and nField+1 array slots for aOffset[] */
};
-typedef struct VdbeCursor VdbeCursor;
+
+
+/*
+** A value for VdbeCursor.cacheStatus that means the cache is always invalid.
+*/
+#define CACHE_STALE 0
/*
** When a sub-program is executed (OP_Program), a structure of this type
@@ -12656,56 +17843,63 @@ typedef struct VdbeCursor VdbeCursor;
typedef struct VdbeFrame VdbeFrame;
struct VdbeFrame {
Vdbe *v; /* VM this frame belongs to */
- int pc; /* Program Counter in parent (calling) frame */
+ VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
Op *aOp; /* Program instructions for parent frame */
- int nOp; /* Size of aOp array */
+ i64 *anExec; /* Event counters from parent frame */
Mem *aMem; /* Array of memory cells for parent frame */
- int nMem; /* Number of entries in aMem */
VdbeCursor **apCsr; /* Array of Vdbe cursors for parent frame */
- u16 nCursor; /* Number of entries in apCsr */
void *token; /* Copy of SubProgram.token */
+ i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
+ AuxData *pAuxData; /* Linked list of auxdata allocations */
+ int nCursor; /* Number of entries in apCsr */
+ int pc; /* Program Counter in parent (calling) frame */
+ int nOp; /* Size of aOp array */
+ int nMem; /* Number of entries in aMem */
int nChildMem; /* Number of memory cells for child frame */
int nChildCsr; /* Number of cursors for child frame */
- i64 lastRowid; /* Last insert rowid (sqlite3.lastRowid) */
- int nChange; /* Statement changes (Vdbe.nChanges) */
- VdbeFrame *pParent; /* Parent of this frame, or NULL if parent is main */
+ int nChange; /* Statement changes (Vdbe.nChange) */
+ int nDbChange; /* Value of db->nChange */
};
#define VdbeFrameMem(p) ((Mem *)&((u8 *)p)[ROUND8(sizeof(VdbeFrame))])
/*
-** A value for VdbeCursor.cacheValid that means the cache is always invalid.
-*/
-#define CACHE_STALE 0
-
-/*
** Internally, the vdbe manipulates nearly all SQL values as Mem
** structures. Each Mem struct may cache multiple representations (string,
** integer etc.) of the same value.
*/
struct Mem {
- sqlite3 *db; /* The associated database connection */
- char *z; /* String or BLOB value */
- double r; /* Real value */
- union {
+ union MemValue {
+ double r; /* Real value used when MEM_Real is set in flags */
i64 i; /* Integer value used when MEM_Int is set in flags */
int nZero; /* Used when bit MEM_Zero is set in flags */
FuncDef *pDef; /* Used only when flags==MEM_Agg */
RowSet *pRowSet; /* Used only when flags==MEM_RowSet */
VdbeFrame *pFrame; /* Used when flags==MEM_Frame */
} u;
- int n; /* Number of characters in string value, excluding '\0' */
u16 flags; /* Some combination of MEM_Null, MEM_Str, MEM_Dyn, etc. */
- u8 type; /* One of SQLITE_NULL, SQLITE_TEXT, SQLITE_INTEGER, etc */
u8 enc; /* SQLITE_UTF8, SQLITE_UTF16BE, SQLITE_UTF16LE */
+ u8 eSubtype; /* Subtype for this value */
+ int n; /* Number of characters in string value, excluding '\0' */
+ char *z; /* String or BLOB value */
+ /* ShallowCopy only needs to copy the information above */
+ char *zMalloc; /* Space to hold MEM_Str or MEM_Blob if szMalloc>0 */
+ int szMalloc; /* Size of the zMalloc allocation */
+ u32 uTemp; /* Transient storage for serial_type in OP_MakeRecord */
+ sqlite3 *db; /* The associated database connection */
+ void (*xDel)(void*);/* Destructor for Mem.z - only valid if MEM_Dyn */
#ifdef SQLITE_DEBUG
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
void *pFiller; /* So that sizeof(Mem) is a multiple of 8 */
#endif
- void (*xDel)(void *); /* If not null, call this function to delete Mem.z */
- char *zMalloc; /* Dynamic buffer allocated by sqlite3_malloc() */
};
+/*
+** Size of struct Mem not including the Mem.zMalloc member or anything that
+** follows.
+*/
+#define MEMCELLSIZE offsetof(Mem,zMalloc)
+
/* One or more of the following flags are set to indicate the validOK
** representations of the value stored in the Mem struct.
**
@@ -12723,10 +17917,13 @@ struct Mem {
#define MEM_Int 0x0004 /* Value is an integer */
#define MEM_Real 0x0008 /* Value is a real number */
#define MEM_Blob 0x0010 /* Value is a BLOB */
+#define MEM_AffMask 0x001f /* Mask of affinity bits */
#define MEM_RowSet 0x0020 /* Value is a RowSet object */
#define MEM_Frame 0x0040 /* Value is a VdbeFrame object */
-#define MEM_Invalid 0x0080 /* Value is undefined */
-#define MEM_TypeMask 0x00ff /* Mask of type bits */
+#define MEM_Undefined 0x0080 /* Value is undefined */
+#define MEM_Cleared 0x0100 /* NULL set by OP_Null, not from data */
+#define MEM_TypeMask 0x81ff /* Mask of type bits */
+
/* Whenever Mem contains a valid string or blob representation, one of
** the following flags must be set to determine the memory management
@@ -12734,16 +17931,23 @@ struct Mem {
** string is \000 or \u0000 terminated
*/
#define MEM_Term 0x0200 /* String rep is nul terminated */
-#define MEM_Dyn 0x0400 /* Need to call sqliteFree() on Mem.z */
+#define MEM_Dyn 0x0400 /* Need to call Mem.xDel() on Mem.z */
#define MEM_Static 0x0800 /* Mem.z points to a static string */
#define MEM_Ephem 0x1000 /* Mem.z points to an ephemeral string */
#define MEM_Agg 0x2000 /* Mem.z points to an agg function context */
#define MEM_Zero 0x4000 /* Mem.i contains count of 0s appended to blob */
+#define MEM_Subtype 0x8000 /* Mem.eSubtype is valid */
#ifdef SQLITE_OMIT_INCRBLOB
#undef MEM_Zero
#define MEM_Zero 0x0000
#endif
+/* Return TRUE if Mem X contains dynamically allocated content - anything
+** that needs to be deallocated to avoid a leak.
+*/
+#define VdbeMemDynamic(X) \
+ (((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))!=0)
+
/*
** Clear any existing type flags from a Mem and replace them with f
*/
@@ -12755,30 +17959,26 @@ struct Mem {
** is for use inside assert() statements only.
*/
#ifdef SQLITE_DEBUG
-#define memIsValid(M) ((M)->flags & MEM_Invalid)==0
-#endif
-
-
-/* A VdbeFunc is just a FuncDef (defined in sqliteInt.h) that contains
-** additional information about auxiliary information bound to arguments
-** of the function. This is used to implement the sqlite3_get_auxdata()
-** and sqlite3_set_auxdata() APIs. The "auxdata" is some auxiliary data
-** that can be associated with a constant argument to a function. This
-** allows functions such as "regexp" to compile their constant regular
-** expression argument once and reused the compiled code for multiple
-** invocations.
-*/
-struct VdbeFunc {
- FuncDef *pFunc; /* The definition of the function */
- int nAux; /* Number of entries allocated for apAux[] */
- struct AuxData {
- void *pAux; /* Aux data for the i-th argument */
- void (*xDelete)(void *); /* Destructor for the aux data */
- } apAux[1]; /* One slot for each function argument */
+#define memIsValid(M) ((M)->flags & MEM_Undefined)==0
+#endif
+
+/*
+** Each auxiliary data pointer stored by a user defined function
+** implementation calling sqlite3_set_auxdata() is stored in an instance
+** of this structure. All such structures associated with a single VM
+** are stored in a linked list headed at Vdbe.pAuxData. All are destroyed
+** when the VM is halted (if not before).
+*/
+struct AuxData {
+ int iOp; /* Instruction number of OP_Function opcode */
+ int iArg; /* Index of function argument. */
+ void *pAux; /* Aux data pointer */
+ void (*xDelete)(void *); /* Destructor for the aux data */
+ AuxData *pNext; /* Next element in list */
};
/*
-** The "context" argument for a installable function. A pointer to an
+** The "context" argument for an installable function. A pointer to an
** instance of this structure is the first argument to the routines used
** implement the SQL functions.
**
@@ -12791,12 +17991,31 @@ struct VdbeFunc {
** (Mem) which are only defined there.
*/
struct sqlite3_context {
- FuncDef *pFunc; /* Pointer to function information. MUST BE FIRST */
- VdbeFunc *pVdbeFunc; /* Auxilary data, if created. */
- Mem s; /* The return value is stored here */
- Mem *pMem; /* Memory cell used to store aggregate context */
- int isError; /* Error code returned by the function. */
- CollSeq *pColl; /* Collating sequence */
+ Mem *pOut; /* The return value is stored here */
+ FuncDef *pFunc; /* Pointer to function information */
+ Mem *pMem; /* Memory cell used to store aggregate context */
+ Vdbe *pVdbe; /* The VM that owns this context */
+ int iOp; /* Instruction number of OP_Function */
+ int isError; /* Error code returned by the function. */
+ u8 skipFlag; /* Skip accumulator loading if true */
+ u8 fErrorOrAux; /* isError!=0 or pVdbe->pAuxData modified */
+ u8 argc; /* Number of arguments */
+ sqlite3_value *argv[1]; /* Argument set */
+};
+
+/* A bitfield type for use inside of structures. Always follow with :N where
+** N is the number of bits.
+*/
+typedef unsigned bft; /* Bit Field Type */
+
+typedef struct ScanStatus ScanStatus;
+struct ScanStatus {
+ int addrExplain; /* OP_Explain for loop */
+ int addrLoop; /* Address of "loops" counter */
+ int addrVisit; /* Address of "rows visited" counter */
+ int iSelectID; /* The "Select-ID" for this loop */
+ LogEst nEst; /* Estimated output rows per loop */
+ char *zName; /* Name of table or index */
};
/*
@@ -12805,100 +18024,124 @@ struct sqlite3_context {
**
** The "sqlite3_stmt" structure pointer that is returned by sqlite3_prepare()
** is really a pointer to an instance of this structure.
-**
-** The Vdbe.inVtabMethod variable is set to non-zero for the duration of
-** any virtual table method invocations made by the vdbe program. It is
-** set to 2 for xDestroy method calls and 1 for all other methods. This
-** variable is used for two purposes: to allow xDestroy methods to execute
-** "DROP TABLE" statements and to prevent some nasty side effects of
-** malloc failure when SQLite is invoked recursively by a virtual table
-** method function.
*/
struct Vdbe {
sqlite3 *db; /* The database connection that owns this statement */
+ Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
+ Parse *pParse; /* Parsing context used to create this Vdbe */
+ ynVar nVar; /* Number of entries in aVar[] */
+ ynVar nzVar; /* Number of entries in azVar[] */
+ u32 magic; /* Magic number for sanity checking */
+ int nMem; /* Number of memory locations currently allocated */
+ int nCursor; /* Number of slots in apCsr[] */
+ u32 cacheCtr; /* VdbeCursor row cache generation counter */
+ int pc; /* The program counter */
+ int rc; /* Value to return */
+ int nChange; /* Number of db changes made since last reset */
+ int iStatement; /* Statement number (or 0 if has not opened stmt) */
+ i64 iCurrentTime; /* Value of julianday('now') for this statement */
+ i64 nFkConstraint; /* Number of imm. FK constraints this VM */
+ i64 nStmtDefCons; /* Number of def. constraints when stmt started */
+ i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
+
+ /* When allocating a new Vdbe object, all of the fields below should be
+ ** initialized to zero or NULL */
+
Op *aOp; /* Space to hold the virtual machine's program */
Mem *aMem; /* The memory locations */
Mem **apArg; /* Arguments to currently executing user function */
Mem *aColName; /* Column names to return */
Mem *pResultSet; /* Pointer to an array of results */
- int nMem; /* Number of memory locations currently allocated */
- int nOp; /* Number of instructions in the program */
- int nOpAlloc; /* Number of slots allocated for aOp[] */
- int nLabel; /* Number of labels used */
- int nLabelAlloc; /* Number of slots allocated in aLabel[] */
- int *aLabel; /* Space to hold the labels */
- u16 nResColumn; /* Number of columns in one row of the result set */
- u16 nCursor; /* Number of slots in apCsr[] */
- u32 magic; /* Magic number for sanity checking */
char *zErrMsg; /* Error message written here */
- Vdbe *pPrev,*pNext; /* Linked list of VDBEs with the same Vdbe.db */
VdbeCursor **apCsr; /* One element of this array for each open cursor */
Mem *aVar; /* Values for the OP_Variable opcode. */
char **azVar; /* Name of variables */
- ynVar nVar; /* Number of entries in aVar[] */
- ynVar nzVar; /* Number of entries in azVar[] */
- u32 cacheCtr; /* VdbeCursor row cache generation counter */
- int pc; /* The program counter */
- int rc; /* Value to return */
+#ifndef SQLITE_OMIT_TRACE
+ i64 startTime; /* Time when query started - used for profiling */
+#endif
+ int nOp; /* Number of instructions in the program */
+#ifdef SQLITE_DEBUG
+ int rcApp; /* errcode set by sqlite3_result_error_code() */
+#endif
+ u16 nResColumn; /* Number of columns in one row of the result set */
u8 errorAction; /* Recovery action to do in case of an error */
- u8 explain; /* True if EXPLAIN present on SQL command */
- u8 changeCntOn; /* True to update the change-counter */
- u8 expired; /* True if the VM needs to be recompiled */
- u8 runOnlyOnce; /* Automatically expire on reset */
u8 minWriteFileFormat; /* Minimum file format for writable database files */
- u8 inVtabMethod; /* See comments above */
- u8 usesStmtJournal; /* True if uses a statement journal */
- u8 readOnly; /* True for read-only statements */
- u8 isPrepareV2; /* True if prepared with prepare_v2() */
- int nChange; /* Number of db changes made since last reset */
+ bft expired:1; /* True if the VM needs to be recompiled */
+ bft doingRerun:1; /* True if rerunning after an auto-reprepare */
+ bft explain:2; /* True if EXPLAIN present on SQL command */
+ bft changeCntOn:1; /* True to update the change-counter */
+ bft runOnlyOnce:1; /* Automatically expire on reset */
+ bft usesStmtJournal:1; /* True if uses a statement journal */
+ bft readOnly:1; /* True for statements that do not write */
+ bft bIsReader:1; /* True for statements that read */
+ bft isPrepareV2:1; /* True if prepared with prepare_v2() */
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
yDbMask lockMask; /* Subset of btreeMask that requires a lock */
- int iStatement; /* Statement number (or 0 if has not opened stmt) */
- int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
-#ifndef SQLITE_OMIT_TRACE
- i64 startTime; /* Time when query started - used for profiling */
-#endif
- i64 nFkConstraint; /* Number of imm. FK constraints this VM */
- i64 nStmtDefCons; /* Number of def. constraints when stmt started */
+ u32 aCounter[5]; /* Counters used by sqlite3_stmt_status() */
char *zSql; /* Text of the SQL statement that generated this */
void *pFree; /* Free this when deleting the vdbe */
-#ifdef SQLITE_DEBUG
- FILE *trace; /* Write an execution trace here, if not NULL */
-#endif
VdbeFrame *pFrame; /* Parent frame */
VdbeFrame *pDelFrame; /* List of frame objects to free on VM reset */
int nFrame; /* Number of frames in pFrame list */
u32 expmask; /* Binding to these vars invalidates VM */
SubProgram *pProgram; /* Linked list of all sub-programs used by VM */
+ AuxData *pAuxData; /* Linked list of auxdata allocations */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ i64 *anExec; /* Number of times each op has been executed */
+ int nScan; /* Entries in aScan[] */
+ ScanStatus *aScan; /* Scan definitions for sqlite3_stmt_scanstatus() */
+#endif
};
/*
** The following are allowed values for Vdbe.magic
*/
-#define VDBE_MAGIC_INIT 0x26bceaa5 /* Building a VDBE program */
-#define VDBE_MAGIC_RUN 0xbdf20da3 /* VDBE is ready to execute */
-#define VDBE_MAGIC_HALT 0x519c2973 /* VDBE has completed execution */
-#define VDBE_MAGIC_DEAD 0xb606c3c8 /* The VDBE has been deallocated */
+#define VDBE_MAGIC_INIT 0x16bceaa5 /* Building a VDBE program */
+#define VDBE_MAGIC_RUN 0x2df20da3 /* VDBE is ready to execute */
+#define VDBE_MAGIC_HALT 0x319c2973 /* VDBE has completed execution */
+#define VDBE_MAGIC_RESET 0x48fa9f76 /* Reset and ready to run again */
+#define VDBE_MAGIC_DEAD 0x5606c3c8 /* The VDBE has been deallocated */
+
+/*
+** Structure used to store the context required by the
+** sqlite3_preupdate_*() API functions.
+*/
+struct PreUpdate {
+ Vdbe *v;
+ VdbeCursor *pCsr; /* Cursor to read old values from */
+ int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
+ u8 *aRecord; /* old.* database record */
+ KeyInfo keyinfo;
+ UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
+ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
+ int iNewReg; /* Register for new.* values */
+ i64 iKey1; /* First key value passed to hook */
+ i64 iKey2; /* Second key value passed to hook */
+ Mem *aNew; /* Array of new.* values */
+ Table *pTab; /* Schema object being upated */
+};
/*
** Function prototypes
*/
+SQLITE_PRIVATE void sqlite3VdbeError(Vdbe*, const char *, ...);
SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *, VdbeCursor*);
void sqliteVdbePopStack(Vdbe*,int);
-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor*);
+SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor**, int*);
+SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor*);
#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE*, int, Op*);
#endif
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int);
-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, int, Mem*, int);
+SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8);
+SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem*, int, u32*);
+SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(unsigned char*, Mem*, u32);
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc*, int);
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int);
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
-SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(VdbeCursor*,UnpackedRecord*,int*);
-SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor *, i64 *);
-SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*);
+SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
+SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
@@ -12915,59 +18158,57 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem*, i64);
#else
SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem*, double);
#endif
+SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem*,sqlite3*,u16);
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem*,int);
SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem*);
-SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, int);
+SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem*, u8, u8);
SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemIntegerify(Mem*);
SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem*);
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem*);
-SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,int,int,int,Mem*);
+SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem*,u8,u8);
+SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,int,Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
-SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p);
-#define MemReleaseExt(X) \
- if((X)->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame)) \
- sqlite3VdbeMemReleaseExternal(X);
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
+SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame*);
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *);
-SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem);
-
-#ifdef SQLITE_OMIT_MERGE_SORT
-# define sqlite3VdbeSorterInit(Y,Z) SQLITE_OK
-# define sqlite3VdbeSorterWrite(X,Y,Z) SQLITE_OK
-# define sqlite3VdbeSorterClose(Y,Z)
-# define sqlite3VdbeSorterRowkey(Y,Z) SQLITE_OK
-# define sqlite3VdbeSorterRewind(X,Y,Z) SQLITE_OK
-# define sqlite3VdbeSorterNext(X,Y,Z) SQLITE_OK
-# define sqlite3VdbeSorterCompare(X,Y,Z) SQLITE_OK
-#else
-SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, VdbeCursor *);
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(Vdbe*,VdbeCursor*,int,const char*,Table*,i64,int);
+#endif
+SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p);
+
+SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *, int, VdbeCursor *);
+SQLITE_PRIVATE void sqlite3VdbeSorterReset(sqlite3 *, VdbeSorter *);
SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *, VdbeCursor *);
-SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(VdbeCursor *, Mem *);
-SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, VdbeCursor *, int *);
-SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *, VdbeCursor *, int *);
-SQLITE_PRIVATE int sqlite3VdbeSorterWrite(sqlite3 *, VdbeCursor *, Mem *);
-SQLITE_PRIVATE int sqlite3VdbeSorterCompare(VdbeCursor *, Mem *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *, const VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *, int *);
+SQLITE_PRIVATE int sqlite3VdbeSorterWrite(const VdbeCursor *, Mem *);
+SQLITE_PRIVATE int sqlite3VdbeSorterCompare(const VdbeCursor *, Mem *, int, int *);
+
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
+SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*);
+#else
+# define sqlite3VdbeEnter(X)
#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
-SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe*);
SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe*);
#else
-# define sqlite3VdbeEnter(X)
# define sqlite3VdbeLeave(X)
#endif
#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*);
+SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*);
+SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*);
#endif
#ifndef SQLITE_OMIT_FOREIGN_KEY
@@ -12985,11 +18226,13 @@ SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem);
#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *);
+ #define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
#else
#define sqlite3VdbeMemExpandBlob(x) SQLITE_OK
+ #define ExpandBlob(P) SQLITE_OK
#endif
-#endif /* !defined(_VDBEINT_H_) */
+#endif /* !defined(SQLITE_VDBEINT_H) */
/************** End of vdbeInt.h *********************************************/
/************** Continuing where we left off in status.c *********************/
@@ -12997,12 +18240,34 @@ SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *);
/*
** Variables in which to record status information.
*/
+#if SQLITE_PTRSIZE>4
+typedef sqlite3_int64 sqlite3StatValueType;
+#else
+typedef u32 sqlite3StatValueType;
+#endif
typedef struct sqlite3StatType sqlite3StatType;
static SQLITE_WSD struct sqlite3StatType {
- int nowValue[10]; /* Current value */
- int mxValue[10]; /* Maximum value */
+ sqlite3StatValueType nowValue[10]; /* Current value */
+ sqlite3StatValueType mxValue[10]; /* Maximum value */
} sqlite3Stat = { {0,}, {0,} };
+/*
+** Elements of sqlite3Stat[] are protected by either the memory allocator
+** mutex, or by the pcache1 mutex. The following array determines which.
+*/
+static const char statMutex[] = {
+ 0, /* SQLITE_STATUS_MEMORY_USED */
+ 1, /* SQLITE_STATUS_PAGECACHE_USED */
+ 1, /* SQLITE_STATUS_PAGECACHE_OVERFLOW */
+ 0, /* SQLITE_STATUS_SCRATCH_USED */
+ 0, /* SQLITE_STATUS_SCRATCH_OVERFLOW */
+ 0, /* SQLITE_STATUS_MALLOC_SIZE */
+ 0, /* SQLITE_STATUS_PARSER_STACK */
+ 1, /* SQLITE_STATUS_PAGECACHE_SIZE */
+ 0, /* SQLITE_STATUS_SCRATCH_SIZE */
+ 0, /* SQLITE_STATUS_MALLOC_COUNT */
+};
+
/* The "wsdStat" macro will resolve to the status information
** state vector. If writable static data is unsupported on the target,
@@ -13019,58 +18284,113 @@ static SQLITE_WSD struct sqlite3StatType {
#endif
/*
-** Return the current value of a status parameter.
+** Return the current value of a status parameter. The caller must
+** be holding the appropriate mutex.
*/
-SQLITE_PRIVATE int sqlite3StatusValue(int op){
+SQLITE_PRIVATE sqlite3_int64 sqlite3StatusValue(int op){
wsdStatInit;
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
+ assert( op>=0 && op<ArraySize(statMutex) );
+ assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex()
+ : sqlite3MallocMutex()) );
return wsdStat.nowValue[op];
}
/*
-** Add N to the value of a status record. It is assumed that the
-** caller holds appropriate locks.
+** Add N to the value of a status record. The caller must hold the
+** appropriate mutex. (Locking is checked by assert()).
+**
+** The StatusUp() routine can accept positive or negative values for N.
+** The value of N is added to the current status value and the high-water
+** mark is adjusted if necessary.
+**
+** The StatusDown() routine lowers the current value by N. The highwater
+** mark is unchanged. N must be non-negative for StatusDown().
*/
-SQLITE_PRIVATE void sqlite3StatusAdd(int op, int N){
+SQLITE_PRIVATE void sqlite3StatusUp(int op, int N){
wsdStatInit;
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
+ assert( op>=0 && op<ArraySize(statMutex) );
+ assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex()
+ : sqlite3MallocMutex()) );
wsdStat.nowValue[op] += N;
if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){
wsdStat.mxValue[op] = wsdStat.nowValue[op];
}
}
+SQLITE_PRIVATE void sqlite3StatusDown(int op, int N){
+ wsdStatInit;
+ assert( N>=0 );
+ assert( op>=0 && op<ArraySize(statMutex) );
+ assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex()
+ : sqlite3MallocMutex()) );
+ assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
+ wsdStat.nowValue[op] -= N;
+}
/*
-** Set the value of a status to X.
+** Adjust the highwater mark if necessary.
+** The caller must hold the appropriate mutex.
*/
-SQLITE_PRIVATE void sqlite3StatusSet(int op, int X){
+SQLITE_PRIVATE void sqlite3StatusHighwater(int op, int X){
+ sqlite3StatValueType newValue;
wsdStatInit;
+ assert( X>=0 );
+ newValue = (sqlite3StatValueType)X;
assert( op>=0 && op<ArraySize(wsdStat.nowValue) );
- wsdStat.nowValue[op] = X;
- if( wsdStat.nowValue[op]>wsdStat.mxValue[op] ){
- wsdStat.mxValue[op] = wsdStat.nowValue[op];
+ assert( op>=0 && op<ArraySize(statMutex) );
+ assert( sqlite3_mutex_held(statMutex[op] ? sqlite3Pcache1Mutex()
+ : sqlite3MallocMutex()) );
+ assert( op==SQLITE_STATUS_MALLOC_SIZE
+ || op==SQLITE_STATUS_PAGECACHE_SIZE
+ || op==SQLITE_STATUS_SCRATCH_SIZE
+ || op==SQLITE_STATUS_PARSER_STACK );
+ if( newValue>wsdStat.mxValue[op] ){
+ wsdStat.mxValue[op] = newValue;
}
}
/*
** Query status information.
-**
-** This implementation assumes that reading or writing an aligned
-** 32-bit integer is an atomic operation. If that assumption is not true,
-** then this routine is not threadsafe.
*/
-SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){
+SQLITE_API int sqlite3_status64(
+ int op,
+ sqlite3_int64 *pCurrent,
+ sqlite3_int64 *pHighwater,
+ int resetFlag
+){
+ sqlite3_mutex *pMutex;
wsdStatInit;
if( op<0 || op>=ArraySize(wsdStat.nowValue) ){
return SQLITE_MISUSE_BKPT;
}
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+ pMutex = statMutex[op] ? sqlite3Pcache1Mutex() : sqlite3MallocMutex();
+ sqlite3_mutex_enter(pMutex);
*pCurrent = wsdStat.nowValue[op];
*pHighwater = wsdStat.mxValue[op];
if( resetFlag ){
wsdStat.mxValue[op] = wsdStat.nowValue[op];
}
+ sqlite3_mutex_leave(pMutex);
+ (void)pMutex; /* Prevent warning when SQLITE_THREADSAFE=0 */
return SQLITE_OK;
}
+SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag){
+ sqlite3_int64 iCur = 0, iHwtr = 0;
+ int rc;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pCurrent==0 || pHighwater==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+ rc = sqlite3_status64(op, &iCur, &iHwtr, resetFlag);
+ if( rc==0 ){
+ *pCurrent = (int)iCur;
+ *pHighwater = (int)iHwtr;
+ }
+ return rc;
+}
/*
** Query status information for a single database connection
@@ -13083,6 +18403,11 @@ SQLITE_API int sqlite3_db_status(
int resetFlag /* Reset high-water mark if true */
){
int rc = SQLITE_OK; /* Return code */
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwater==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
switch( op ){
case SQLITE_DBSTATUS_LOOKASIDE_USED: {
@@ -13115,6 +18440,7 @@ SQLITE_API int sqlite3_db_status(
** by all pagers associated with the given database connection. The
** highwater mark is meaningless and is returned as zero.
*/
+ case SQLITE_DBSTATUS_CACHE_USED_SHARED:
case SQLITE_DBSTATUS_CACHE_USED: {
int totalUsed = 0;
int i;
@@ -13123,7 +18449,11 @@ SQLITE_API int sqlite3_db_status(
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
Pager *pPager = sqlite3BtreePager(pBt);
- totalUsed += sqlite3PagerMemUsed(pPager);
+ int nByte = sqlite3PagerMemUsed(pPager);
+ if( op==SQLITE_DBSTATUS_CACHE_USED_SHARED ){
+ nByte = nByte / sqlite3BtreeConnectionCount(pBt);
+ }
+ totalUsed += nByte;
}
}
sqlite3BtreeLeaveAll(db);
@@ -13154,10 +18484,10 @@ SQLITE_API int sqlite3_db_status(
+ pSchema->idxHash.count
+ pSchema->fkeyHash.count
);
- nByte += sqlite3MallocSize(pSchema->tblHash.ht);
- nByte += sqlite3MallocSize(pSchema->trigHash.ht);
- nByte += sqlite3MallocSize(pSchema->idxHash.ht);
- nByte += sqlite3MallocSize(pSchema->fkeyHash.ht);
+ nByte += sqlite3_msize(pSchema->tblHash.ht);
+ nByte += sqlite3_msize(pSchema->trigHash.ht);
+ nByte += sqlite3_msize(pSchema->idxHash.ht);
+ nByte += sqlite3_msize(pSchema->fkeyHash.ht);
for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){
sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p));
@@ -13186,16 +18516,53 @@ SQLITE_API int sqlite3_db_status(
db->pnBytesFreed = &nByte;
for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){
- sqlite3VdbeDeleteObject(db, pVdbe);
+ sqlite3VdbeClearObject(db, pVdbe);
+ sqlite3DbFree(db, pVdbe);
}
db->pnBytesFreed = 0;
- *pHighwater = 0;
+ *pHighwater = 0; /* IMP: R-64479-57858 */
*pCurrent = nByte;
break;
}
+ /*
+ ** Set *pCurrent to the total cache hits or misses encountered by all
+ ** pagers the database handle is connected to. *pHighwater is always set
+ ** to zero.
+ */
+ case SQLITE_DBSTATUS_CACHE_HIT:
+ case SQLITE_DBSTATUS_CACHE_MISS:
+ case SQLITE_DBSTATUS_CACHE_WRITE:{
+ int i;
+ int nRet = 0;
+ assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
+ assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
+
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pBt ){
+ Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
+ sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
+ }
+ }
+ *pHighwater = 0; /* IMP: R-42420-56072 */
+ /* IMP: R-54100-20147 */
+ /* IMP: R-29431-39229 */
+ *pCurrent = nRet;
+ break;
+ }
+
+ /* Set *pCurrent to non-zero if there are unresolved deferred foreign
+ ** key constraints. Set *pCurrent to zero if all foreign key constraints
+ ** have been satisfied. The *pHighwater is always set to zero.
+ */
+ case SQLITE_DBSTATUS_DEFERRED_FKS: {
+ *pHighwater = 0; /* IMP: R-11967-56545 */
+ *pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
}
@@ -13224,7 +18591,7 @@ SQLITE_API int sqlite3_db_status(
** sqlite3RegisterDateTimeFunctions() found at the bottom of the file.
** All other code has file scope.
**
-** SQLite processes all times and dates as Julian Day numbers. The
+** SQLite processes all times and dates as julian day numbers. The
** dates and times are stored as the number of days since noon
** in Greenwich on November 24, 4714 B.C. according to the Gregorian
** calendar system.
@@ -13232,14 +18599,14 @@ SQLITE_API int sqlite3_db_status(
** 1970-01-01 00:00:00 is JD 2440587.5
** 2000-01-01 00:00:00 is JD 2451544.5
**
-** This implemention requires years to be expressed as a 4-digit number
+** This implementation requires years to be expressed as a 4-digit number
** which means that only dates between 0000-01-01 and 9999-12-31 can
** be represented, even though julian day numbers allow a much wider
** range of dates.
**
** The Gregorian calendar system is used for all dates and times,
** even those that predate the Gregorian calendar. Historians usually
-** use the Julian calendar for dates prior to 1582-10-15 and for some
+** use the julian calendar for dates prior to 1582-10-15 and for some
** dates afterwards, depending on locale. Beware of this difference.
**
** The conversion algorithms are implemented based on descriptions
@@ -13251,12 +18618,22 @@ SQLITE_API int sqlite3_db_status(
** Willmann-Bell, Inc
** Richmond, Virginia (USA)
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <assert.h> */
#include <time.h>
#ifndef SQLITE_OMIT_DATETIME_FUNCS
+/*
+** The MSVC CRT on Windows CE may not have a localtime() function.
+** So declare a substitute. The substitute function itself is
+** defined in "os_win.c".
+*/
+#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \
+ (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API)
+struct tm *__cdecl localtime(const time_t *);
+#endif
/*
** A structure for holding a single date and time.
@@ -13272,38 +18649,54 @@ struct DateTime {
char validHMS; /* True (1) if h,m,s are valid */
char validJD; /* True (1) if iJD is valid */
char validTZ; /* True (1) if tz is valid */
+ char tzSet; /* Timezone was set explicitly */
};
/*
-** Convert zDate into one or more integers. Additional arguments
-** come in groups of 5 as follows:
+** Convert zDate into one or more integers according to the conversion
+** specifier zFormat.
+**
+** zFormat[] contains 4 characters for each integer converted, except for
+** the last integer which is specified by three characters. The meaning
+** of a four-character format specifiers ABCD is:
**
-** N number of digits in the integer
-** min minimum allowed value of the integer
-** max maximum allowed value of the integer
-** nextC first character after the integer
-** pVal where to write the integers value.
+** A: number of digits to convert. Always "2" or "4".
+** B: minimum value. Always "0" or "1".
+** C: maximum value, decoded as:
+** a: 12
+** b: 14
+** c: 24
+** d: 31
+** e: 59
+** f: 9999
+** D: the separator character, or \000 to indicate this is the
+** last number to convert.
+**
+** Example: To translate an ISO-8601 date YYYY-MM-DD, the format would
+** be "40f-21a-20c". The "40f-" indicates the 4-digit year followed by "-".
+** The "21a-" indicates the 2-digit month followed by "-". The "20c" indicates
+** the 2-digit day which is the last integer in the set.
**
-** Conversions continue until one with nextC==0 is encountered.
** The function returns the number of successful conversions.
*/
-static int getDigits(const char *zDate, ...){
+static int getDigits(const char *zDate, const char *zFormat, ...){
+ /* The aMx[] array translates the 3rd character of each format
+ ** spec into a max size: a b c d e f */
+ static const u16 aMx[] = { 12, 14, 24, 31, 59, 9999 };
va_list ap;
- int val;
- int N;
- int min;
- int max;
- int nextC;
- int *pVal;
int cnt = 0;
- va_start(ap, zDate);
+ char nextC;
+ va_start(ap, zFormat);
do{
- N = va_arg(ap, int);
- min = va_arg(ap, int);
- max = va_arg(ap, int);
- nextC = va_arg(ap, int);
- pVal = va_arg(ap, int*);
+ char N = zFormat[0] - '0';
+ char min = zFormat[1] - '0';
+ int val = 0;
+ u16 max;
+
+ assert( zFormat[2]>='a' && zFormat[2]<='f' );
+ max = aMx[zFormat[2] - 'a'];
+ nextC = zFormat[3];
val = 0;
while( N-- ){
if( !sqlite3Isdigit(*zDate) ){
@@ -13312,12 +18705,13 @@ static int getDigits(const char *zDate, ...){
val = val*10 + *zDate - '0';
zDate++;
}
- if( val<min || val>max || (nextC!=0 && nextC!=*zDate) ){
+ if( val<(int)min || val>(int)max || (nextC!=0 && nextC!=*zDate) ){
goto end_getDigits;
}
- *pVal = val;
+ *va_arg(ap,int*) = val;
zDate++;
cnt++;
+ zFormat += 4;
}while( nextC );
end_getDigits:
va_end(ap);
@@ -13358,13 +18752,14 @@ static int parseTimezone(const char *zDate, DateTime *p){
return c!=0;
}
zDate++;
- if( getDigits(zDate, 2, 0, 14, ':', &nHr, 2, 0, 59, 0, &nMn)!=2 ){
+ if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
return 1;
}
zDate += 5;
p->tz = sgn*(nMn + nHr*60);
zulu_time:
while( sqlite3Isspace(*zDate) ){ zDate++; }
+ p->tzSet = 1;
return *zDate!=0;
}
@@ -13378,13 +18773,13 @@ zulu_time:
static int parseHhMmSs(const char *zDate, DateTime *p){
int h, m, s;
double ms = 0.0;
- if( getDigits(zDate, 2, 0, 24, ':', &h, 2, 0, 59, 0, &m)!=2 ){
+ if( getDigits(zDate, "20c:20e", &h, &m)!=2 ){
return 1;
}
zDate += 5;
if( *zDate==':' ){
zDate++;
- if( getDigits(zDate, 2, 0, 59, 0, &s)!=1 ){
+ if( getDigits(zDate, "20e", &s)!=1 ){
return 1;
}
zDate += 2;
@@ -13472,7 +18867,7 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
}else{
neg = 0;
}
- if( getDigits(zDate,4,0,9999,'-',&Y,2,1,12,'-',&M,2,1,31,0,&D)!=3 ){
+ if( getDigits(zDate, "40f-21a-21d", &Y, &M, &D)!=3 ){
return 1;
}
zDate += 10;
@@ -13496,16 +18891,22 @@ static int parseYyyyMmDd(const char *zDate, DateTime *p){
}
/*
-** Set the time to the current time reported by the VFS
+** Set the time to the current time reported by the VFS.
+**
+** Return the number of errors.
*/
-static void setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
- sqlite3 *db = sqlite3_context_db_handle(context);
- sqlite3OsCurrentTimeInt64(db->pVfs, &p->iJD);
- p->validJD = 1;
+static int setDateTimeToCurrent(sqlite3_context *context, DateTime *p){
+ p->iJD = sqlite3StmtCurrentTime(context);
+ if( p->iJD>0 ){
+ p->validJD = 1;
+ return 0;
+ }else{
+ return 1;
+ }
}
/*
-** Attempt to parse the given string into a Julian Day Number. Return
+** Attempt to parse the given string into a julian day number. Return
** the number of errors.
**
** The following are acceptable forms for the input string:
@@ -13531,8 +18932,7 @@ static int parseDateOrTime(
}else if( parseHhMmSs(zDate, p)==0 ){
return 0;
}else if( sqlite3StrICmp(zDate,"now")==0){
- setDateTimeToCurrent(context, p);
- return 0;
+ return setDateTimeToCurrent(context, p);
}else if( sqlite3AtoF(zDate, &r, sqlite3Strlen30(zDate), SQLITE_UTF8) ){
p->iJD = (sqlite3_int64)(r*86400000.0 + 0.5);
p->validJD = 1;
@@ -13557,7 +18957,7 @@ static void computeYMD(DateTime *p){
A = Z + 1 + A - (A/4);
B = A + 1524;
C = (int)((B - 122.1)/365.25);
- D = (36525*C)/100;
+ D = (36525*(C&32767))/100;
E = (int)((B-D)/30.6001);
X1 = (int)(30.6001*E);
p->D = B - D - X1;
@@ -13602,6 +19002,7 @@ static void clearYMD_HMS_TZ(DateTime *p){
p->validTZ = 0;
}
+#ifndef SQLITE_OMIT_LOCALTIME
/*
** On recent Windows platforms, the localtime_s() function is available
** as part of the "Secure CRT". It is essentially equivalent to
@@ -13614,12 +19015,12 @@ static void clearYMD_HMS_TZ(DateTime *p){
** already, check for an MSVC build environment that provides
** localtime_s().
*/
-#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \
- defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
+#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S \
+ && defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE)
+#undef HAVE_LOCALTIME_S
#define HAVE_LOCALTIME_S 1
#endif
-#ifndef SQLITE_OMIT_LOCALTIME
/*
** The following routine implements the rough equivalent of localtime_r()
** using whatever operating-system specific localtime facility that
@@ -13628,11 +19029,14 @@ static void clearYMD_HMS_TZ(DateTime *p){
**
** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this
** routine will always fail.
+**
+** EVIDENCE-OF: R-62172-00036 In this implementation, the standard C
+** library function localtime_r() is used to assist in the calculation of
+** local time.
*/
static int osLocaltime(time_t *t, struct tm *pTm){
int rc;
-#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \
- && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S)
+#if !HAVE_LOCALTIME_R && !HAVE_LOCALTIME_S
struct tm *pX;
#if SQLITE_THREADSAFE>0
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
@@ -13649,7 +19053,7 @@ static int osLocaltime(time_t *t, struct tm *pTm){
#ifndef SQLITE_OMIT_BUILTIN_TEST
if( sqlite3GlobalConfig.bLocaltimeFault ) return 1;
#endif
-#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R
+#if HAVE_LOCALTIME_R
rc = localtime_r(t, pTm)==0;
#else
rc = localtime_s(pTm, t);
@@ -13684,6 +19088,11 @@ static sqlite3_int64 localtimeOffset(
x = *p;
computeYMD_HMS(&x);
if( x.Y<1971 || x.Y>=2038 ){
+ /* EVIDENCE-OF: R-55269-29598 The localtime_r() C function normally only
+ ** works for years between 1970 and 2037. For dates outside this range,
+ ** SQLite attempts to map the year into an equivalent year within this
+ ** range, do the calculation, then map the year back.
+ */
x.Y = 2000;
x.M = 1;
x.D = 1;
@@ -13783,13 +19192,18 @@ static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){
}
#ifndef SQLITE_OMIT_LOCALTIME
else if( strcmp(z, "utc")==0 ){
- sqlite3_int64 c1;
- computeJD(p);
- c1 = localtimeOffset(p, pCtx, &rc);
- if( rc==SQLITE_OK ){
- p->iJD -= c1;
- clearYMD_HMS_TZ(p);
- p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
+ if( p->tzSet==0 ){
+ sqlite3_int64 c1;
+ computeJD(p);
+ c1 = localtimeOffset(p, pCtx, &rc);
+ if( rc==SQLITE_OK ){
+ p->iJD -= c1;
+ clearYMD_HMS_TZ(p);
+ p->iJD += c1 - localtimeOffset(p, pCtx, &rc);
+ }
+ p->tzSet = 1;
+ }else{
+ rc = SQLITE_OK;
}
}
#endif
@@ -13959,8 +19373,9 @@ static int isDate(
int eType;
memset(p, 0, sizeof(*p));
if( argc==0 ){
- setDateTimeToCurrent(context, p);
- }else if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
+ return setDateTimeToCurrent(context, p);
+ }
+ if( (eType = sqlite3_value_type(argv[0]))==SQLITE_FLOAT
|| eType==SQLITE_INTEGER ){
p->iJD = (sqlite3_int64)(sqlite3_value_double(argv[0])*86400000.0 + 0.5);
p->validJD = 1;
@@ -14067,7 +19482,7 @@ static void dateFunc(
** %f ** fractional seconds SS.SSS
** %H hour 00-24
** %j day of year 000-366
-** %J ** Julian day number
+** %J ** julian day number
** %m month 01-12
** %M minute 00-59
** %s seconds since 1970-01-01
@@ -14087,8 +19502,10 @@ static void strftimeFunc(
size_t i,j;
char *z;
sqlite3 *db;
- const char *zFmt = (const char*)sqlite3_value_text(argv[0]);
+ const char *zFmt;
char zBuf[100];
+ if( argc==0 ) return;
+ zFmt = (const char*)sqlite3_value_text(argv[0]);
if( zFmt==0 || isDate(context, argc-1, argv+1, &x) ) return;
db = sqlite3_context_db_handle(context);
for(i=0, n=1; zFmt[i]; i++, n++){
@@ -14134,7 +19551,7 @@ static void strftimeFunc(
sqlite3_result_error_toobig(context);
return;
}else{
- z = sqlite3DbMallocRaw(db, (int)n);
+ z = sqlite3DbMallocRawNN(db, (int)n);
if( z==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -14270,33 +19687,29 @@ static void currentTimeFunc(
){
time_t t;
char *zFormat = (char *)sqlite3_user_data(context);
- sqlite3 *db;
sqlite3_int64 iT;
+ struct tm *pTm;
+ struct tm sNow;
char zBuf[20];
UNUSED_PARAMETER(argc);
UNUSED_PARAMETER(argv);
- db = sqlite3_context_db_handle(context);
- sqlite3OsCurrentTimeInt64(db->pVfs, &iT);
+ iT = sqlite3StmtCurrentTime(context);
+ if( iT<=0 ) return;
t = iT/1000 - 10000*(sqlite3_int64)21086676;
-#ifdef HAVE_GMTIME_R
- {
- struct tm sNow;
- gmtime_r(&t, &sNow);
- strftime(zBuf, 20, zFormat, &sNow);
- }
+#if HAVE_GMTIME_R
+ pTm = gmtime_r(&t, &sNow);
#else
- {
- struct tm *pTm;
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
- pTm = gmtime(&t);
- strftime(zBuf, 20, zFormat, pTm);
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
- }
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ pTm = gmtime(&t);
+ if( pTm ) memcpy(&sNow, pTm, sizeof(sNow));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
#endif
-
- sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ if( pTm ){
+ strftime(zBuf, 20, zFormat, &sNow);
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ }
}
#endif
@@ -14306,29 +19719,23 @@ static void currentTimeFunc(
** external linkage.
*/
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
- static SQLITE_WSD FuncDef aDateTimeFuncs[] = {
+ static FuncDef aDateTimeFuncs[] = {
#ifndef SQLITE_OMIT_DATETIME_FUNCS
- FUNCTION(julianday, -1, 0, 0, juliandayFunc ),
- FUNCTION(date, -1, 0, 0, dateFunc ),
- FUNCTION(time, -1, 0, 0, timeFunc ),
- FUNCTION(datetime, -1, 0, 0, datetimeFunc ),
- FUNCTION(strftime, -1, 0, 0, strftimeFunc ),
- FUNCTION(current_time, 0, 0, 0, ctimeFunc ),
- FUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
- FUNCTION(current_date, 0, 0, 0, cdateFunc ),
+ DFUNCTION(julianday, -1, 0, 0, juliandayFunc ),
+ DFUNCTION(date, -1, 0, 0, dateFunc ),
+ DFUNCTION(time, -1, 0, 0, timeFunc ),
+ DFUNCTION(datetime, -1, 0, 0, datetimeFunc ),
+ DFUNCTION(strftime, -1, 0, 0, strftimeFunc ),
+ DFUNCTION(current_time, 0, 0, 0, ctimeFunc ),
+ DFUNCTION(current_timestamp, 0, 0, 0, ctimestampFunc),
+ DFUNCTION(current_date, 0, 0, 0, cdateFunc ),
#else
STR_FUNCTION(current_time, 0, "%H:%M:%S", 0, currentTimeFunc),
STR_FUNCTION(current_date, 0, "%Y-%m-%d", 0, currentTimeFunc),
STR_FUNCTION(current_timestamp, 0, "%Y-%m-%d %H:%M:%S", 0, currentTimeFunc),
#endif
};
- int i;
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
- FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aDateTimeFuncs);
-
- for(i=0; i<ArraySize(aDateTimeFuncs); i++){
- sqlite3FuncDefInsert(pHash, &aFunc[i]);
- }
+ sqlite3InsertBuiltinFuncs(aDateTimeFuncs, ArraySize(aDateTimeFuncs));
}
/************** End of date.c ************************************************/
@@ -14348,8 +19755,29 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
** This file contains OS interface code that is common to all
** architectures.
*/
-#define _SQLITE_OS_C_ 1
-#undef _SQLITE_OS_C_
+/* #include "sqliteInt.h" */
+
+/*
+** If we compile with the SQLITE_TEST macro set, then the following block
+** of code will give us the ability to simulate a disk I/O error. This
+** is used for testing the I/O recovery logic.
+*/
+#if defined(SQLITE_TEST)
+SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
+SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
+SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
+SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
+SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
+SQLITE_API int sqlite3_diskfull_pending = 0;
+SQLITE_API int sqlite3_diskfull = 0;
+#endif /* defined(SQLITE_TEST) */
+
+/*
+** When testing, also keep a count of the number of open files.
+*/
+#if defined(SQLITE_TEST)
+SQLITE_API int sqlite3_open_file_count = 0;
+#endif /* defined(SQLITE_TEST) */
/*
** The default SQLite sqlite3_vfs implementations do not allocate
@@ -14358,22 +19786,29 @@ SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void){
** So we test the effects of a malloc() failing and the sqlite3OsXXX()
** function returning SQLITE_IOERR_NOMEM using the DO_OS_MALLOC_TEST macro.
**
-** The following functions are instrumented for malloc() failure
+** The following functions are instrumented for malloc() failure
** testing:
**
-** sqlite3OsOpen()
** sqlite3OsRead()
** sqlite3OsWrite()
** sqlite3OsSync()
+** sqlite3OsFileSize()
** sqlite3OsLock()
+** sqlite3OsCheckReservedLock()
+** sqlite3OsFileControl()
+** sqlite3OsShmMap()
+** sqlite3OsOpen()
+** sqlite3OsDelete()
+** sqlite3OsAccess()
+** sqlite3OsFullPathname()
**
*/
#if defined(SQLITE_TEST)
SQLITE_API int sqlite3_memdebug_vfs_oom_test = 1;
#define DO_OS_MALLOC_TEST(x) \
- if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3IsMemJournal(x))) { \
+ if (sqlite3_memdebug_vfs_oom_test && (!x || !sqlite3JournalIsInMemory(x))) { \
void *pTstAlloc = sqlite3Malloc(10); \
- if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \
+ if (!pTstAlloc) return SQLITE_IOERR_NOMEM_BKPT; \
sqlite3_free(pTstAlloc); \
}
#else
@@ -14386,13 +19821,11 @@ SQLITE_API int sqlite3_memdebug_vfs_oom_test = 1;
** of this would be completely automatic if SQLite were coded using
** C++ instead of plain old C.
*/
-SQLITE_PRIVATE int sqlite3OsClose(sqlite3_file *pId){
- int rc = SQLITE_OK;
+SQLITE_PRIVATE void sqlite3OsClose(sqlite3_file *pId){
if( pId->pMethods ){
- rc = pId->pMethods->xClose(pId);
+ pId->pMethods->xClose(pId);
pId->pMethods = 0;
}
- return rc;
}
SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){
DO_OS_MALLOC_TEST(id);
@@ -14424,9 +19857,37 @@ SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){
DO_OS_MALLOC_TEST(id);
return id->pMethods->xCheckReservedLock(id, pResOut);
}
+
+/*
+** Use sqlite3OsFileControl() when we are doing something that might fail
+** and we need to know about the failures. Use sqlite3OsFileControlHint()
+** when simply tossing information over the wall to the VFS and we do not
+** really care if the VFS receives and understands the information since it
+** is only a hint and can be safely ignored. The sqlite3OsFileControlHint()
+** routine has no return value since the return value would be meaningless.
+*/
SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
+#ifdef SQLITE_TEST
+ if( op!=SQLITE_FCNTL_COMMIT_PHASETWO ){
+ /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
+ ** is using a regular VFS, it is called after the corresponding
+ ** transaction has been committed. Injecting a fault at this point
+ ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
+ ** but the transaction is committed anyway.
+ **
+ ** The core must call OsFileControl() though, not OsFileControlHint(),
+ ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
+ ** means the commit really has failed and an error should be returned
+ ** to the user. */
+ DO_OS_MALLOC_TEST(id);
+ }
+#endif
return id->pMethods->xFileControl(id, op, pArg);
}
+SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
+ (void)id->pMethods->xFileControl(id, op, pArg);
+}
+
SQLITE_PRIVATE int sqlite3OsSectorSize(sqlite3_file *id){
int (*xSectorSize)(sqlite3_file*) = id->pMethods->xSectorSize;
return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE);
@@ -14450,23 +19911,44 @@ SQLITE_PRIVATE int sqlite3OsShmMap(
int bExtend, /* True to extend file if necessary */
void volatile **pp /* OUT: Pointer to mapping */
){
+ DO_OS_MALLOC_TEST(id);
return id->pMethods->xShmMap(id, iPage, pgsz, bExtend, pp);
}
+#if SQLITE_MAX_MMAP_SIZE>0
+/* The real implementation of xFetch and xUnfetch */
+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){
+ DO_OS_MALLOC_TEST(id);
+ return id->pMethods->xFetch(id, iOff, iAmt, pp);
+}
+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){
+ return id->pMethods->xUnfetch(id, iOff, p);
+}
+#else
+/* No-op stubs to use when memory-mapped I/O is disabled */
+SQLITE_PRIVATE int sqlite3OsFetch(sqlite3_file *id, i64 iOff, int iAmt, void **pp){
+ *pp = 0;
+ return SQLITE_OK;
+}
+SQLITE_PRIVATE int sqlite3OsUnfetch(sqlite3_file *id, i64 iOff, void *p){
+ return SQLITE_OK;
+}
+#endif
+
/*
** The next group of routines are convenience wrappers around the
** VFS methods.
*/
SQLITE_PRIVATE int sqlite3OsOpen(
- sqlite3_vfs *pVfs,
- const char *zPath,
- sqlite3_file *pFile,
- int flags,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ sqlite3_file *pFile,
+ int flags,
int *pFlagsOut
){
int rc;
DO_OS_MALLOC_TEST(0);
- /* 0x87f3f is a mask of SQLITE_OPEN_ flags that are valid to be passed
+ /* 0x87f7f is a mask of SQLITE_OPEN_ flags that are valid to be passed
** down into the VFS layer. Some SQLITE_OPEN_ flags (for example,
** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before
** reaching the VFS. */
@@ -14475,23 +19957,26 @@ SQLITE_PRIVATE int sqlite3OsOpen(
return rc;
}
SQLITE_PRIVATE int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ DO_OS_MALLOC_TEST(0);
+ assert( dirSync==0 || dirSync==1 );
return pVfs->xDelete(pVfs, zPath, dirSync);
}
SQLITE_PRIVATE int sqlite3OsAccess(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int flags,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
int *pResOut
){
DO_OS_MALLOC_TEST(0);
return pVfs->xAccess(pVfs, zPath, flags, pResOut);
}
SQLITE_PRIVATE int sqlite3OsFullPathname(
- sqlite3_vfs *pVfs,
- const char *zPath,
- int nPathOut,
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nPathOut,
char *zPathOut
){
+ DO_OS_MALLOC_TEST(0);
zPathOut[0] = 0;
return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut);
}
@@ -14515,6 +20000,9 @@ SQLITE_PRIVATE int sqlite3OsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufO
SQLITE_PRIVATE int sqlite3OsSleep(sqlite3_vfs *pVfs, int nMicro){
return pVfs->xSleep(pVfs, nMicro);
}
+SQLITE_PRIVATE int sqlite3OsGetLastError(sqlite3_vfs *pVfs){
+ return pVfs->xGetLastError ? pVfs->xGetLastError(pVfs, 0, 0) : 0;
+}
SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
int rc;
/* IMPLEMENTATION-OF: R-49045-42493 SQLite will use the xCurrentTimeInt64()
@@ -14534,13 +20022,13 @@ SQLITE_PRIVATE int sqlite3OsCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *p
}
SQLITE_PRIVATE int sqlite3OsOpenMalloc(
- sqlite3_vfs *pVfs,
- const char *zFile,
- sqlite3_file **ppFile,
+ sqlite3_vfs *pVfs,
+ const char *zFile,
+ sqlite3_file **ppFile,
int flags,
int *pOutFlags
){
- int rc = SQLITE_NOMEM;
+ int rc;
sqlite3_file *pFile;
pFile = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile);
if( pFile ){
@@ -14550,15 +20038,15 @@ SQLITE_PRIVATE int sqlite3OsOpenMalloc(
}else{
*ppFile = pFile;
}
+ }else{
+ rc = SQLITE_NOMEM_BKPT;
}
return rc;
}
-SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *pFile){
- int rc = SQLITE_OK;
+SQLITE_PRIVATE void sqlite3OsCloseFree(sqlite3_file *pFile){
assert( pFile );
- rc = sqlite3OsClose(pFile);
+ sqlite3OsClose(pFile);
sqlite3_free(pFile);
- return rc;
}
/*
@@ -14569,7 +20057,7 @@ SQLITE_PRIVATE int sqlite3OsCloseFree(sqlite3_file *pFile){
*/
SQLITE_PRIVATE int sqlite3OsInit(void){
void *p = sqlite3_malloc(10);
- if( p==0 ) return SQLITE_NOMEM;
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
sqlite3_free(p);
return sqlite3_os_init();
}
@@ -14631,12 +20119,16 @@ static void vfsUnlink(sqlite3_vfs *pVfs){
** true.
*/
SQLITE_API int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
- sqlite3_mutex *mutex = 0;
+ MUTEX_LOGIC(sqlite3_mutex *mutex;)
#ifndef SQLITE_OMIT_AUTOINIT
int rc = sqlite3_initialize();
if( rc ) return rc;
#endif
- mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pVfs==0 ) return SQLITE_MISUSE_BKPT;
+#endif
+
+ MUTEX_LOGIC( mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
sqlite3_mutex_enter(mutex);
vfsUnlink(pVfs);
if( makeDflt || vfsList==0 ){
@@ -14692,6 +20184,7 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
** during a hash table resize is a benign fault.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_BUILTIN_TEST
@@ -14773,6 +20266,7 @@ SQLITE_PRIVATE void sqlite3EndBenignMalloc(void){
** are merely placeholders. Real drivers must be substituted using
** sqlite3_config() before SQLite will operate.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is the default. It is
@@ -14833,8 +20327,33 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** to obtain the memory it needs.
**
** This file contains implementations of the low-level memory allocation
-** routines specified in the sqlite3_mem_methods object.
+** routines specified in the sqlite3_mem_methods object. The content of
+** this file is only used if SQLITE_SYSTEM_MALLOC is defined. The
+** SQLITE_SYSTEM_MALLOC macro is defined automatically if neither the
+** SQLITE_MEMDEBUG nor the SQLITE_WIN32_MALLOC macros are defined. The
+** default configuration is to use memory allocation routines in this
+** file.
+**
+** C-preprocessor macro summary:
+**
+** HAVE_MALLOC_USABLE_SIZE The configure script sets this symbol if
+** the malloc_usable_size() interface exists
+** on the target platform. Or, this symbol
+** can be set manually, if desired.
+** If an equivalent interface exists by
+** a different name, using a separate -D
+** option to rename it.
+**
+** SQLITE_WITHOUT_ZONEMALLOC Some older macs lack support for the zone
+** memory allocator. Set this symbol to enable
+** building on older macs.
+**
+** SQLITE_WITHOUT_MSIZE Set this symbol to disable the use of
+** _msize() on windows systems. This might
+** be necessary when compiling for Delphi,
+** for example.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is the default. It is
@@ -14842,6 +20361,71 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** macros.
*/
#ifdef SQLITE_SYSTEM_MALLOC
+#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
+
+/*
+** Use the zone allocator available on apple products unless the
+** SQLITE_WITHOUT_ZONEMALLOC symbol is defined.
+*/
+#include <sys/sysctl.h>
+#include <malloc/malloc.h>
+#include <libkern/OSAtomic.h>
+static malloc_zone_t* _sqliteZone_;
+#define SQLITE_MALLOC(x) malloc_zone_malloc(_sqliteZone_, (x))
+#define SQLITE_FREE(x) malloc_zone_free(_sqliteZone_, (x));
+#define SQLITE_REALLOC(x,y) malloc_zone_realloc(_sqliteZone_, (x), (y))
+#define SQLITE_MALLOCSIZE(x) \
+ (_sqliteZone_ ? _sqliteZone_->size(_sqliteZone_,x) : malloc_size(x))
+
+#else /* if not __APPLE__ */
+
+/*
+** Use standard C library malloc and free on non-Apple systems.
+** Also used by Apple systems if SQLITE_WITHOUT_ZONEMALLOC is defined.
+*/
+#define SQLITE_MALLOC(x) malloc(x)
+#define SQLITE_FREE(x) free(x)
+#define SQLITE_REALLOC(x,y) realloc((x),(y))
+
+/*
+** The malloc.h header file is needed for malloc_usable_size() function
+** on some systems (e.g. Linux).
+*/
+#if HAVE_MALLOC_H && HAVE_MALLOC_USABLE_SIZE
+# define SQLITE_USE_MALLOC_H 1
+# define SQLITE_USE_MALLOC_USABLE_SIZE 1
+/*
+** The MSVCRT has malloc_usable_size(), but it is called _msize(). The
+** use of _msize() is automatic, but can be disabled by compiling with
+** -DSQLITE_WITHOUT_MSIZE. Using the _msize() function also requires
+** the malloc.h header file.
+*/
+#elif defined(_MSC_VER) && !defined(SQLITE_WITHOUT_MSIZE)
+# define SQLITE_USE_MALLOC_H
+# define SQLITE_USE_MSIZE
+#endif
+
+/*
+** Include the malloc.h header file, if necessary. Also set define macro
+** SQLITE_MALLOCSIZE to the appropriate function name, which is _msize()
+** for MSVC and malloc_usable_size() for most other systems (e.g. Linux).
+** The memory size function can always be overridden manually by defining
+** the macro SQLITE_MALLOCSIZE to the desired function name.
+*/
+#if defined(SQLITE_USE_MALLOC_H)
+# include <malloc.h>
+# if defined(SQLITE_USE_MALLOC_USABLE_SIZE)
+# if !defined(SQLITE_MALLOCSIZE)
+# define SQLITE_MALLOCSIZE(x) malloc_usable_size(x)
+# endif
+# elif defined(SQLITE_USE_MSIZE)
+# if !defined(SQLITE_MALLOCSIZE)
+# define SQLITE_MALLOCSIZE _msize
+# endif
+# endif
+#endif /* defined(SQLITE_USE_MALLOC_H) */
+
+#endif /* __APPLE__ or not __APPLE__ */
/*
** Like malloc(), but remember the size of the allocation
@@ -14852,10 +20436,18 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** routines.
*/
static void *sqlite3MemMalloc(int nByte){
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_MALLOC( nByte );
+ if( p==0 ){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
+ }
+ return p;
+#else
sqlite3_int64 *p;
assert( nByte>0 );
nByte = ROUND8(nByte);
- p = malloc( nByte+8 );
+ p = SQLITE_MALLOC( nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -14864,6 +20456,7 @@ static void *sqlite3MemMalloc(int nByte){
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes of memory", nByte);
}
return (void *)p;
+#endif
}
/*
@@ -14875,10 +20468,14 @@ static void *sqlite3MemMalloc(int nByte){
** by higher-level routines.
*/
static void sqlite3MemFree(void *pPrior){
+#ifdef SQLITE_MALLOCSIZE
+ SQLITE_FREE(pPrior);
+#else
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 );
p--;
- free(p);
+ SQLITE_FREE(p);
+#endif
}
/*
@@ -14886,11 +20483,16 @@ static void sqlite3MemFree(void *pPrior){
** or xRealloc().
*/
static int sqlite3MemSize(void *pPrior){
+#ifdef SQLITE_MALLOCSIZE
+ assert( pPrior!=0 );
+ return (int)SQLITE_MALLOCSIZE(pPrior);
+#else
sqlite3_int64 *p;
- if( pPrior==0 ) return 0;
+ assert( pPrior!=0 );
p = (sqlite3_int64*)pPrior;
p--;
return (int)p[0];
+#endif
}
/*
@@ -14899,16 +20501,26 @@ static int sqlite3MemSize(void *pPrior){
**
** For this low-level interface, we know that pPrior!=0. Cases where
** pPrior==0 while have been intercepted by higher-level routine and
-** redirected to xMalloc. Similarly, we know that nByte>0 becauses
+** redirected to xMalloc. Similarly, we know that nByte>0 because
** cases where nByte<=0 will have been intercepted by higher-level
** routines and redirected to xFree.
*/
static void *sqlite3MemRealloc(void *pPrior, int nByte){
+#ifdef SQLITE_MALLOCSIZE
+ void *p = SQLITE_REALLOC(pPrior, nByte);
+ if( p==0 ){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ sqlite3_log(SQLITE_NOMEM,
+ "failed memory resize %u to %u bytes",
+ SQLITE_MALLOCSIZE(pPrior), nByte);
+ }
+ return p;
+#else
sqlite3_int64 *p = (sqlite3_int64*)pPrior;
assert( pPrior!=0 && nByte>0 );
assert( nByte==ROUND8(nByte) ); /* EV: R-46199-30249 */
p--;
- p = realloc(p, nByte+8 );
+ p = SQLITE_REALLOC(p, nByte+8 );
if( p ){
p[0] = nByte;
p++;
@@ -14919,6 +20531,7 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
sqlite3MemSize(pPrior), nByte);
}
return (void*)p;
+#endif
}
/*
@@ -14932,6 +20545,34 @@ static int sqlite3MemRoundup(int n){
** Initialize this module.
*/
static int sqlite3MemInit(void *NotUsed){
+#if defined(__APPLE__) && !defined(SQLITE_WITHOUT_ZONEMALLOC)
+ int cpuCount;
+ size_t len;
+ if( _sqliteZone_ ){
+ return SQLITE_OK;
+ }
+ len = sizeof(cpuCount);
+ /* One usually wants to use hw.acctivecpu for MT decisions, but not here */
+ sysctlbyname("hw.ncpu", &cpuCount, &len, NULL, 0);
+ if( cpuCount>1 ){
+ /* defer MT decisions to system malloc */
+ _sqliteZone_ = malloc_default_zone();
+ }else{
+ /* only 1 core, use our own zone to contention over global locks,
+ ** e.g. we have our own dedicated locks */
+ bool success;
+ malloc_zone_t* newzone = malloc_create_zone(4096, 0);
+ malloc_set_zone_name(newzone, "Sqlite_Heap");
+ do{
+ success = OSAtomicCompareAndSwapPtrBarrier(NULL, newzone,
+ (void * volatile *)&_sqliteZone_);
+ }while(!_sqliteZone_);
+ if( !success ){
+ /* somebody registered a zone first */
+ malloc_destroy_zone(newzone);
+ }
+ }
+#endif
UNUSED_PARAMETER(NotUsed);
return SQLITE_OK;
}
@@ -14989,6 +20630,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
** This file contains implementations of the low-level memory allocation
** routines specified in the sqlite3_mem_methods object.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is used only if the
@@ -15148,7 +20790,7 @@ static int sqlite3MemSize(void *p){
return 0;
}
pHdr = sqlite3MemsysGetHeader(p);
- return pHdr->iSize;
+ return (int)pHdr->iSize;
}
/*
@@ -15190,7 +20832,7 @@ static void randomFill(char *pBuf, int nByte){
x = SQLITE_PTR_TO_INT(pBuf);
y = nByte | 1;
while( nByte >= 4 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(int*)pBuf = r;
@@ -15198,7 +20840,7 @@ static void randomFill(char *pBuf, int nByte){
nByte -= 4;
}
while( nByte-- > 0 ){
- x = (x>>1) ^ (-(x&1) & 0xd0000001);
+ x = (x>>1) ^ (-(int)(x&1) & 0xd0000001);
y = y*1103515245 + 12345;
r = x ^ y;
*(pBuf++) = r & 0xff;
@@ -15293,9 +20935,9 @@ static void sqlite3MemFree(void *pPrior){
}
z = (char*)pBt;
z -= pHdr->nTitle;
- adjustStats(pHdr->iSize, -1);
+ adjustStats((int)pHdr->iSize, -1);
randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) +
- pHdr->iSize + sizeof(int) + pHdr->nTitle);
+ (int)pHdr->iSize + sizeof(int) + pHdr->nTitle);
free(z);
sqlite3_mutex_leave(mem.mutex);
}
@@ -15317,9 +20959,9 @@ static void *sqlite3MemRealloc(void *pPrior, int nByte){
pOldHdr = sqlite3MemsysGetHeader(pPrior);
pNew = sqlite3MemMalloc(nByte);
if( pNew ){
- memcpy(pNew, pPrior, nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize);
+ memcpy(pNew, pPrior, (int)(nByte<pOldHdr->iSize ? nByte : pOldHdr->iSize));
if( nByte>pOldHdr->iSize ){
- randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize);
+ randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - (int)pOldHdr->iSize);
}
sqlite3MemFree(pPrior);
}
@@ -15363,7 +21005,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSetType(void *p, u8 eType){
** This routine is designed for use within an assert() statement, to
** verify the type of an allocation. For example:
**
-** assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
+** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
*/
SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
int rc = 1;
@@ -15385,7 +21027,7 @@ SQLITE_PRIVATE int sqlite3MemdebugHasType(void *p, u8 eType){
** This routine is designed for use within an assert() statement, to
** verify the type of an allocation. For example:
**
-** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
+** assert( sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
*/
SQLITE_PRIVATE int sqlite3MemdebugNoType(void *p, u8 eType){
int rc = 1;
@@ -15434,7 +21076,7 @@ SQLITE_PRIVATE void sqlite3MemdebugSync(){
for(pHdr=mem.pFirst; pHdr; pHdr=pHdr->pNext){
void **pBt = (void**)pHdr;
pBt -= pHdr->nBacktraceSlots;
- mem.xBacktrace(pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
+ mem.xBacktrace((int)pHdr->iSize, pHdr->nBacktrace-1, &pBt[1]);
}
}
@@ -15523,6 +21165,7 @@ SQLITE_PRIVATE int sqlite3MemdebugMallocCount(){
** This version of the memory allocation subsystem is included
** in the build only if SQLITE_ENABLE_MEMSYS3 is defined.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is only built into the library
@@ -15975,7 +21618,7 @@ static void memsys3FreeUnsafe(void *pOld){
*/
static int memsys3Size(void *p){
Mem3Block *pBlock;
- if( p==0 ) return 0;
+ assert( p!=0 );
pBlock = (Mem3Block*)p;
assert( (pBlock[-1].u.hdr.size4x&1)!=0 );
return (pBlock[-1].u.hdr.size4x&~3)*2 - 4;
@@ -16214,10 +21857,10 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
**
** This memory allocator uses the following algorithm:
**
-** 1. All memory allocations sizes are rounded up to a power of 2.
+** 1. All memory allocation sizes are rounded up to a power of 2.
**
** 2. If two adjacent free blocks are the halves of a larger block,
-** then the two blocks are coalesed into the single larger block.
+** then the two blocks are coalesced into the single larger block.
**
** 3. New memory is allocated from the first available free block.
**
@@ -16237,6 +21880,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys3(void){
** The sqlite3_status() logic tracks the maximum values of n and M so
** that an application can, at any time, verify this constraint.
*/
+/* #include "sqliteInt.h" */
/*
** This version of the memory allocator is used only when
@@ -16290,6 +21934,7 @@ static SQLITE_WSD struct Mem5Global {
*/
sqlite3_mutex *mutex;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Performance statistics
*/
@@ -16301,11 +21946,12 @@ static SQLITE_WSD struct Mem5Global {
u32 maxOut; /* Maximum instantaneous currentOut */
u32 maxCount; /* Maximum instantaneous currentCount */
u32 maxRequest; /* Largest allocation (exclusive of internal frag) */
+#endif
/*
** Lists of free blocks. aiFreelist[0] is a list of free blocks of
** size mem5.szAtom. aiFreelist[1] holds blocks of size szAtom*2.
- ** and so forth.
+ ** aiFreelist[2] holds free blocks of size szAtom*4. And so forth.
*/
int aiFreelist[LOGMAX+1];
@@ -16318,13 +21964,13 @@ static SQLITE_WSD struct Mem5Global {
} mem5;
/*
-** Access the static variable through a macro for SQLITE_OMIT_WSD
+** Access the static variable through a macro for SQLITE_OMIT_WSD.
*/
#define mem5 GLOBAL(struct Mem5Global, mem5)
/*
** Assuming mem5.zPool is divided up into an array of Mem5Link
-** structures, return a pointer to the idx-th such lik.
+** structures, return a pointer to the idx-th such link.
*/
#define MEM5LINK(idx) ((Mem5Link *)(&mem5.zPool[(idx)*mem5.szAtom]))
@@ -16371,9 +22017,7 @@ static void memsys5Link(int i, int iLogsize){
}
/*
-** If the STATIC_MEM mutex is not already held, obtain it now. The mutex
-** will already be held (obtained by code in malloc.c) if
-** sqlite3GlobalConfig.bMemStat is true.
+** Obtain or release the mutex needed to access global data structures.
*/
static void memsys5Enter(void){
sqlite3_mutex_enter(mem5.mutex);
@@ -16383,44 +22027,23 @@ static void memsys5Leave(void){
}
/*
-** Return the size of an outstanding allocation, in bytes. The
-** size returned omits the 8-byte header overhead. This only
-** works for chunks that are currently checked out.
+** Return the size of an outstanding allocation, in bytes.
+** This only works for chunks that are currently checked out.
*/
static int memsys5Size(void *p){
- int iSize = 0;
- if( p ){
- int i = ((u8 *)p-mem5.zPool)/mem5.szAtom;
- assert( i>=0 && i<mem5.nBlock );
- iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
- }
+ int iSize, i;
+ assert( p!=0 );
+ i = (int)(((u8 *)p-mem5.zPool)/mem5.szAtom);
+ assert( i>=0 && i<mem5.nBlock );
+ iSize = mem5.szAtom * (1 << (mem5.aCtrl[i]&CTRL_LOGSIZE));
return iSize;
}
/*
-** Find the first entry on the freelist iLogsize. Unlink that
-** entry and return its index.
-*/
-static int memsys5UnlinkFirst(int iLogsize){
- int i;
- int iFirst;
-
- assert( iLogsize>=0 && iLogsize<=LOGMAX );
- i = iFirst = mem5.aiFreelist[iLogsize];
- assert( iFirst>=0 );
- while( i>0 ){
- if( i<iFirst ) iFirst = i;
- i = MEM5LINK(i)->next;
- }
- memsys5Unlink(iFirst, iLogsize);
- return iFirst;
-}
-
-/*
** Return a block of memory of at least nBytes in size.
** Return NULL if unable. Return NULL if nBytes==0.
**
-** The caller guarantees that nByte positive.
+** The caller guarantees that nByte is positive.
**
** The caller has obtained a mutex prior to invoking this
** routine so there is never any chance that two or more
@@ -16435,33 +22058,33 @@ static void *memsys5MallocUnsafe(int nByte){
/* nByte must be a positive */
assert( nByte>0 );
+ /* No more than 1GiB per allocation */
+ if( nByte > 0x40000000 ) return 0;
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/* Keep track of the maximum allocation request. Even unfulfilled
** requests are counted */
if( (u32)nByte>mem5.maxRequest ){
mem5.maxRequest = nByte;
}
+#endif
- /* Abort if the requested allocation size is larger than the largest
- ** power of two that we can represent using 32-bit signed integers.
- */
- if( nByte > 0x40000000 ){
- return 0;
- }
/* Round nByte up to the next valid power of two */
- for(iFullSz=mem5.szAtom, iLogsize=0; iFullSz<nByte; iFullSz *= 2, iLogsize++){}
+ for(iFullSz=mem5.szAtom,iLogsize=0; iFullSz<nByte; iFullSz*=2,iLogsize++){}
/* Make sure mem5.aiFreelist[iLogsize] contains at least one free
** block. If not, then split a block of the next larger power of
** two in order to create a new free block of size iLogsize.
*/
- for(iBin=iLogsize; mem5.aiFreelist[iBin]<0 && iBin<=LOGMAX; iBin++){}
+ for(iBin=iLogsize; iBin<=LOGMAX && mem5.aiFreelist[iBin]<0; iBin++){}
if( iBin>LOGMAX ){
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(SQLITE_NOMEM, "failed to allocate %u bytes", nByte);
return 0;
}
- i = memsys5UnlinkFirst(iBin);
+ i = mem5.aiFreelist[iBin];
+ memsys5Unlink(i, iBin);
while( iBin>iLogsize ){
int newSize;
@@ -16472,6 +22095,7 @@ static void *memsys5MallocUnsafe(int nByte){
}
mem5.aCtrl[i] = iLogsize;
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/* Update allocator performance statistics. */
mem5.nAlloc++;
mem5.totalAlloc += iFullSz;
@@ -16480,6 +22104,13 @@ static void *memsys5MallocUnsafe(int nByte){
mem5.currentOut += iFullSz;
if( mem5.maxCount<mem5.currentCount ) mem5.maxCount = mem5.currentCount;
if( mem5.maxOut<mem5.currentOut ) mem5.maxOut = mem5.currentOut;
+#endif
+
+#ifdef SQLITE_DEBUG
+ /* Make sure the allocated memory does not assume that it is set to zero
+ ** or retains a value from a previous allocation */
+ memset(&mem5.zPool[i*mem5.szAtom], 0xAA, iFullSz);
+#endif
/* Return a pointer to the allocated memory. */
return (void*)&mem5.zPool[i*mem5.szAtom];
@@ -16495,7 +22126,7 @@ static void memsys5FreeUnsafe(void *pOld){
/* Set iBlock to the index of the block pointed to by pOld in
** the array of mem5.szAtom byte blocks pointed to by mem5.zPool.
*/
- iBlock = ((u8 *)pOld-mem5.zPool)/mem5.szAtom;
+ iBlock = (int)(((u8 *)pOld-mem5.zPool)/mem5.szAtom);
/* Check that the pointer pOld points to a valid, non-free block. */
assert( iBlock>=0 && iBlock<mem5.nBlock );
@@ -16508,23 +22139,26 @@ static void memsys5FreeUnsafe(void *pOld){
mem5.aCtrl[iBlock] |= CTRL_FREE;
mem5.aCtrl[iBlock+size-1] |= CTRL_FREE;
+
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
assert( mem5.currentCount>0 );
assert( mem5.currentOut>=(size*mem5.szAtom) );
mem5.currentCount--;
mem5.currentOut -= size*mem5.szAtom;
assert( mem5.currentOut>0 || mem5.currentCount==0 );
assert( mem5.currentCount>0 || mem5.currentOut==0 );
+#endif
mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize;
while( ALWAYS(iLogsize<LOGMAX) ){
int iBuddy;
if( (iBlock>>iLogsize) & 1 ){
iBuddy = iBlock - size;
+ assert( iBuddy>=0 );
}else{
iBuddy = iBlock + size;
+ if( iBuddy>=mem5.nBlock ) break;
}
- assert( iBuddy>=0 );
- if( (iBuddy+(1<<iLogsize))>mem5.nBlock ) break;
if( mem5.aCtrl[iBuddy]!=(CTRL_FREE | iLogsize) ) break;
memsys5Unlink(iBuddy, iLogsize);
iLogsize++;
@@ -16538,11 +22172,18 @@ static void memsys5FreeUnsafe(void *pOld){
}
size *= 2;
}
+
+#ifdef SQLITE_DEBUG
+ /* Overwrite freed memory with the 0x55 bit pattern to verify that it is
+ ** not used after being freed */
+ memset(&mem5.zPool[iBlock*mem5.szAtom], 0x55, size);
+#endif
+
memsys5Link(iBlock, iLogsize);
}
/*
-** Allocate nBytes of memory
+** Allocate nBytes of memory.
*/
static void *memsys5Malloc(int nBytes){
sqlite3_int64 *p = 0;
@@ -16592,13 +22233,11 @@ static void *memsys5Realloc(void *pPrior, int nBytes){
if( nBytes<=nOld ){
return pPrior;
}
- memsys5Enter();
- p = memsys5MallocUnsafe(nBytes);
+ p = memsys5Malloc(nBytes);
if( p ){
memcpy(p, pPrior, nOld);
- memsys5FreeUnsafe(pPrior);
+ memsys5Free(pPrior);
}
- memsys5Leave();
return p;
}
@@ -16785,6 +22424,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){
**
** This file contains code that is common across all mutex implementations.
*/
+/* #include "sqliteInt.h" */
#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT)
/*
@@ -16793,7 +22433,7 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetMemsys5(void){
** allocate a mutex while the system is uninitialized.
*/
static SQLITE_WSD int mutexIsInit = 0;
-#endif /* SQLITE_DEBUG */
+#endif /* SQLITE_DEBUG && !defined(SQLITE_MUTEX_OMIT) */
#ifndef SQLITE_MUTEX_OMIT
@@ -16816,11 +22456,18 @@ SQLITE_PRIVATE int sqlite3MutexInit(void){
}else{
pFrom = sqlite3NoopMutex();
}
- memcpy(pTo, pFrom, offsetof(sqlite3_mutex_methods, xMutexAlloc));
- memcpy(&pTo->xMutexFree, &pFrom->xMutexFree,
- sizeof(*pTo) - offsetof(sqlite3_mutex_methods, xMutexFree));
+ pTo->xMutexInit = pFrom->xMutexInit;
+ pTo->xMutexEnd = pFrom->xMutexEnd;
+ pTo->xMutexFree = pFrom->xMutexFree;
+ pTo->xMutexEnter = pFrom->xMutexEnter;
+ pTo->xMutexTry = pFrom->xMutexTry;
+ pTo->xMutexLeave = pFrom->xMutexLeave;
+ pTo->xMutexHeld = pFrom->xMutexHeld;
+ pTo->xMutexNotheld = pFrom->xMutexNotheld;
+ sqlite3MemoryBarrier();
pTo->xMutexAlloc = pFrom->xMutexAlloc;
}
+ assert( sqlite3GlobalConfig.mutex.xMutexInit );
rc = sqlite3GlobalConfig.mutex.xMutexInit();
#ifdef SQLITE_DEBUG
@@ -16852,8 +22499,10 @@ SQLITE_PRIVATE int sqlite3MutexEnd(void){
*/
SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int id){
#ifndef SQLITE_OMIT_AUTOINIT
- if( sqlite3_initialize() ) return 0;
+ if( id<=SQLITE_MUTEX_RECURSIVE && sqlite3_initialize() ) return 0;
+ if( id>SQLITE_MUTEX_RECURSIVE && sqlite3MutexInit() ) return 0;
#endif
+ assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
}
@@ -16862,6 +22511,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){
return 0;
}
assert( GLOBAL(int, mutexIsInit) );
+ assert( sqlite3GlobalConfig.mutex.xMutexAlloc );
return sqlite3GlobalConfig.mutex.xMutexAlloc(id);
}
@@ -16870,6 +22520,7 @@ SQLITE_PRIVATE sqlite3_mutex *sqlite3MutexAlloc(int id){
*/
SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexFree );
sqlite3GlobalConfig.mutex.xMutexFree(p);
}
}
@@ -16880,6 +22531,7 @@ SQLITE_API void sqlite3_mutex_free(sqlite3_mutex *p){
*/
SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexEnter );
sqlite3GlobalConfig.mutex.xMutexEnter(p);
}
}
@@ -16891,6 +22543,7 @@ SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex *p){
SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){
int rc = SQLITE_OK;
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexTry );
return sqlite3GlobalConfig.mutex.xMutexTry(p);
}
return rc;
@@ -16904,6 +22557,7 @@ SQLITE_API int sqlite3_mutex_try(sqlite3_mutex *p){
*/
SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){
if( p ){
+ assert( sqlite3GlobalConfig.mutex.xMutexLeave );
sqlite3GlobalConfig.mutex.xMutexLeave(p);
}
}
@@ -16914,14 +22568,16 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex *p){
** intended for use inside assert() statements.
*/
SQLITE_API int sqlite3_mutex_held(sqlite3_mutex *p){
+ assert( p==0 || sqlite3GlobalConfig.mutex.xMutexHeld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexHeld(p);
}
SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
+ assert( p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld );
return p==0 || sqlite3GlobalConfig.mutex.xMutexNotheld(p);
}
#endif
-#endif /* SQLITE_MUTEX_OMIT */
+#endif /* !defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex.c ***********************************************/
/************** Begin file mutex_noop.c **************************************/
@@ -16952,6 +22608,7 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex *p){
** that does error checking on mutexes to make sure they are being
** called correctly.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_MUTEX_OMIT
@@ -17033,7 +22690,7 @@ static int debugMutexEnd(void){ return SQLITE_OK; }
** that means that a mutex could not be allocated.
*/
static sqlite3_mutex *debugMutexAlloc(int id){
- static sqlite3_debug_mutex aStatic[6];
+ static sqlite3_debug_mutex aStatic[SQLITE_MUTEX_STATIC_VFS3 - 1];
sqlite3_debug_mutex *pNew = 0;
switch( id ){
case SQLITE_MUTEX_FAST:
@@ -17046,8 +22703,12 @@ static sqlite3_mutex *debugMutexAlloc(int id){
break;
}
default: {
- assert( id-2 >= 0 );
- assert( id-2 < (int)(sizeof(aStatic)/sizeof(aStatic[0])) );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( id-2<0 || id-2>=ArraySize(aStatic) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
pNew = &aStatic[id-2];
pNew->id = id;
break;
@@ -17062,8 +22723,13 @@ static sqlite3_mutex *debugMutexAlloc(int id){
static void debugMutexFree(sqlite3_mutex *pX){
sqlite3_debug_mutex *p = (sqlite3_debug_mutex*)pX;
assert( p->cnt==0 );
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
- sqlite3_free(p);
+ if( p->id==SQLITE_MUTEX_RECURSIVE || p->id==SQLITE_MUTEX_FAST ){
+ sqlite3_free(p);
+ }else{
+#ifdef SQLITE_ENABLE_API_ARMOR
+ (void)SQLITE_MISUSE_BKPT;
+#endif
+ }
}
/*
@@ -17128,286 +22794,10 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3NoopMutex(void){
SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
return sqlite3NoopMutex();
}
-#endif /* SQLITE_MUTEX_NOOP */
-#endif /* SQLITE_MUTEX_OMIT */
+#endif /* defined(SQLITE_MUTEX_NOOP) */
+#endif /* !defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex_noop.c ******************************************/
-/************** Begin file mutex_os2.c ***************************************/
-/*
-** 2007 August 28
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
-** This file contains the C functions that implement mutexes for OS/2
-*/
-
-/*
-** The code in this file is only used if SQLITE_MUTEX_OS2 is defined.
-** See the mutex.h file for details.
-*/
-#ifdef SQLITE_MUTEX_OS2
-
-/********************** OS/2 Mutex Implementation **********************
-**
-** This implementation of mutexes is built using the OS/2 API.
-*/
-
-/*
-** The mutex object
-** Each recursive mutex is an instance of the following structure.
-*/
-struct sqlite3_mutex {
- HMTX mutex; /* Mutex controlling the lock */
- int id; /* Mutex type */
-#ifdef SQLITE_DEBUG
- int trace; /* True to trace changes */
-#endif
-};
-
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 }
-#else
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0 }
-#endif
-
-/*
-** Initialize and deinitialize the mutex subsystem.
-*/
-static int os2MutexInit(void){ return SQLITE_OK; }
-static int os2MutexEnd(void){ return SQLITE_OK; }
-
-/*
-** The sqlite3_mutex_alloc() routine allocates a new
-** mutex and returns a pointer to it. If it returns NULL
-** that means that a mutex could not be allocated.
-** SQLite will unwind its stack and return an error. The argument
-** to sqlite3_mutex_alloc() is one of these integer constants:
-**
-** <ul>
-** <li> SQLITE_MUTEX_FAST
-** <li> SQLITE_MUTEX_RECURSIVE
-** <li> SQLITE_MUTEX_STATIC_MASTER
-** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
-** <li> SQLITE_MUTEX_STATIC_PRNG
-** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
-** </ul>
-**
-** The first two constants cause sqlite3_mutex_alloc() to create
-** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
-** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
-** The mutex implementation does not need to make a distinction
-** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
-** not want to. But SQLite will only request a recursive mutex in
-** cases where it really needs one. If a faster non-recursive mutex
-** implementation is available on the host platform, the mutex subsystem
-** might return such a mutex in response to SQLITE_MUTEX_FAST.
-**
-** The other allowed parameters to sqlite3_mutex_alloc() each return
-** a pointer to a static preexisting mutex. Six static mutexes are
-** used by the current version of SQLite. Future versions of SQLite
-** may add additional static mutexes. Static mutexes are for internal
-** use by SQLite only. Applications that use SQLite mutexes should
-** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
-** SQLITE_MUTEX_RECURSIVE.
-**
-** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
-** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
-** mutex types, the same mutex is returned on every call that has
-** the same type number.
-*/
-static sqlite3_mutex *os2MutexAlloc(int iType){
- sqlite3_mutex *p = NULL;
- switch( iType ){
- case SQLITE_MUTEX_FAST:
- case SQLITE_MUTEX_RECURSIVE: {
- p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
- p->id = iType;
- if( DosCreateMutexSem( 0, &p->mutex, 0, FALSE ) != NO_ERROR ){
- sqlite3_free( p );
- p = NULL;
- }
- }
- break;
- }
- default: {
- static volatile int isInit = 0;
- static sqlite3_mutex staticMutexes[6] = {
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- };
- if ( !isInit ){
- APIRET rc;
- PTIB ptib;
- PPIB ppib;
- HMTX mutex;
- char name[32];
- DosGetInfoBlocks( &ptib, &ppib );
- sqlite3_snprintf( sizeof(name), name, "\\SEM32\\SQLITE%04x",
- ppib->pib_ulpid );
- while( !isInit ){
- mutex = 0;
- rc = DosCreateMutexSem( name, &mutex, 0, FALSE);
- if( rc == NO_ERROR ){
- unsigned int i;
- if( !isInit ){
- for( i = 0; i < sizeof(staticMutexes)/sizeof(staticMutexes[0]); i++ ){
- DosCreateMutexSem( 0, &staticMutexes[i].mutex, 0, FALSE );
- }
- isInit = 1;
- }
- DosCloseMutexSem( mutex );
- }else if( rc == ERROR_DUPLICATE_NAME ){
- DosSleep( 1 );
- }else{
- return p;
- }
- }
- }
- assert( iType-2 >= 0 );
- assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
- p = &staticMutexes[iType-2];
- p->id = iType;
- break;
- }
- }
- return p;
-}
-
-
-/*
-** This routine deallocates a previously allocated mutex.
-** SQLite is careful to deallocate every mutex that it allocates.
-*/
-static void os2MutexFree(sqlite3_mutex *p){
-#ifdef SQLITE_DEBUG
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- assert( ulCount==0 );
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
-#endif
- DosCloseMutexSem( p->mutex );
- sqlite3_free( p );
-}
-
-#ifdef SQLITE_DEBUG
-/*
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
-** intended for use inside assert() statements.
-*/
-static int os2MutexHeld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) )
- return 0;
- DosGetInfoBlocks(&ptib, NULL);
- return tid==ptib->tib_ptib2->tib2_ultid;
-}
-static int os2MutexNotheld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 )
- return 1;
- DosGetInfoBlocks(&ptib, NULL);
- return tid!=ptib->tib_ptib2->tib2_ultid;
-}
-static void os2MutexTrace(sqlite3_mutex *p, char *pAction){
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount);
-}
-#endif
-
-/*
-** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
-** to enter a mutex. If another thread is already within the mutex,
-** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
-** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
-** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
-** be entered multiple times by the same thread. In such cases the,
-** mutex must be exited an equal number of times before another thread
-** can enter. If the same thread tries to enter any other kind of mutex
-** more than once, the behavior is undefined.
-*/
-static void os2MutexEnter(sqlite3_mutex *p){
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT);
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "enter");
-#endif
-}
-static int os2MutexTry(sqlite3_mutex *p){
- int rc = SQLITE_BUSY;
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) {
- rc = SQLITE_OK;
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "try");
-#endif
- }
- return rc;
-}
-
-/*
-** The sqlite3_mutex_leave() routine exits a mutex that was
-** previously entered by the same thread. The behavior
-** is undefined if the mutex is not currently entered or
-** is not currently allocated. SQLite will never do either.
-*/
-static void os2MutexLeave(sqlite3_mutex *p){
- assert( os2MutexHeld(p) );
- DosReleaseMutexSem(p->mutex);
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "leave");
-#endif
-}
-
-SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
- static const sqlite3_mutex_methods sMutex = {
- os2MutexInit,
- os2MutexEnd,
- os2MutexAlloc,
- os2MutexFree,
- os2MutexEnter,
- os2MutexTry,
- os2MutexLeave,
-#ifdef SQLITE_DEBUG
- os2MutexHeld,
- os2MutexNotheld
-#else
- 0,
- 0
-#endif
- };
-
- return &sMutex;
-}
-#endif /* SQLITE_MUTEX_OS2 */
-
-/************** End of mutex_os2.c *******************************************/
/************** Begin file mutex_unix.c **************************************/
/*
** 2007 August 28
@@ -17422,6 +22812,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
*************************************************************************
** This file contains the C functions that implement mutexes for pthreads
*/
+/* #include "sqliteInt.h" */
/*
** The code in this file is only used if we are compiling threadsafe
@@ -17450,15 +22841,19 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
*/
struct sqlite3_mutex {
pthread_mutex_t mutex; /* Mutex controlling the lock */
-#if SQLITE_MUTEX_NREF
+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
int id; /* Mutex type */
+#endif
+#if SQLITE_MUTEX_NREF
volatile int nRef; /* Number of entrances */
volatile pthread_t owner; /* Thread that is within this mutex */
int trace; /* True to trace changes */
#endif
};
#if SQLITE_MUTEX_NREF
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 }
+#define SQLITE3_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,0,0,(pthread_t)0,0}
+#elif defined(SQLITE_ENABLE_API_ARMOR)
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0 }
#else
#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
#endif
@@ -17489,6 +22884,19 @@ static int pthreadMutexNotheld(sqlite3_mutex *p){
#endif
/*
+** Try to provide a memory barrier operation, needed for initialization
+** and also for the implementation of xShmBarrier in the VFS in cases
+** where SQLite is compiled without mutexes.
+*/
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
+#if defined(SQLITE_MEMORY_BARRIER)
+ SQLITE_MEMORY_BARRIER;
+#elif defined(__GNUC__) && GCC_VERSION>=4001000
+ __sync_synchronize();
+#endif
+}
+
+/*
** Initialize and deinitialize the mutex subsystem.
*/
static int pthreadMutexInit(void){ return SQLITE_OK; }
@@ -17506,10 +22914,16 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -17543,6 +22957,12 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
sqlite3_mutex *p;
@@ -17562,32 +22982,30 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
pthread_mutex_init(&p->mutex, &recursiveAttr);
pthread_mutexattr_destroy(&recursiveAttr);
#endif
-#if SQLITE_MUTEX_NREF
- p->id = iType;
-#endif
}
break;
}
case SQLITE_MUTEX_FAST: {
p = sqlite3MallocZero( sizeof(*p) );
if( p ){
-#if SQLITE_MUTEX_NREF
- p->id = iType;
-#endif
pthread_mutex_init(&p->mutex, 0);
}
break;
}
default: {
- assert( iType-2 >= 0 );
- assert( iType-2 < ArraySize(staticMutexes) );
- p = &staticMutexes[iType-2];
-#if SQLITE_MUTEX_NREF
- p->id = iType;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
#endif
+ p = &staticMutexes[iType-2];
break;
}
}
+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR)
+ if( p ) p->id = iType;
+#endif
return p;
}
@@ -17599,9 +23017,18 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
*/
static void pthreadMutexFree(sqlite3_mutex *p){
assert( p->nRef==0 );
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
- pthread_mutex_destroy(&p->mutex);
- sqlite3_free(p);
+#if SQLITE_ENABLE_API_ARMOR
+ if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE )
+#endif
+ {
+ pthread_mutex_destroy(&p->mutex);
+ sqlite3_free(p);
+ }
+#ifdef SQLITE_ENABLE_API_ARMOR
+ else{
+ (void)SQLITE_MISUSE_BKPT;
+ }
+#endif
}
/*
@@ -17758,7 +23185,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
return &sMutex;
}
-#endif /* SQLITE_MUTEX_PTHREAD */
+#endif /* SQLITE_MUTEX_PTHREADS */
/************** End of mutex_unix.c ******************************************/
/************** Begin file mutex_w32.c ***************************************/
@@ -17773,12 +23200,315 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the C functions that implement mutexes for win32
+** This file contains the C functions that implement mutexes for Win32.
+*/
+/* #include "sqliteInt.h" */
+
+#if SQLITE_OS_WIN
+/*
+** Include code that is common to all os_*.c files
+*/
+/************** Include os_common.h in the middle of mutex_w32.c *************/
+/************** Begin file os_common.h ***************************************/
+/*
+** 2004 May 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains macros and a little bit of code that is common to
+** all of the platform-specific files (os_*.c) and is #included into those
+** files.
+**
+** This file should be #included by the os_*.c files only. It is not a
+** general purpose header file.
+*/
+#ifndef _OS_COMMON_H_
+#define _OS_COMMON_H_
+
+/*
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the
+** switch. The following code should catch this problem at compile-time.
+*/
+#ifdef MEMORY_DEBUG
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
+#endif
+
+/*
+** Macros for performance tracing. Normally turned off. Only works
+** on i486 hardware.
+*/
+#ifdef SQLITE_PERFORMANCE_TRACE
+
+/*
+** hwtime.h contains inline assembler code for implementing
+** high-performance timing routines.
+*/
+/************** Include hwtime.h in the middle of os_common.h ****************/
+/************** Begin file hwtime.h ******************************************/
+/*
+** 2008 May 27
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains inline asm code for retrieving "high-performance"
+** counters for x86 class CPUs.
+*/
+#ifndef SQLITE_HWTIME_H
+#define SQLITE_HWTIME_H
+
+/*
+** The following routine only works on pentium-class (or newer) processors.
+** It uses the RDTSC opcode to read the cycle count value out of the
+** processor and returns that value. This can be used for high-res
+** profiling.
+*/
+#if (defined(__GNUC__) || defined(_MSC_VER)) && \
+ (defined(i386) || defined(__i386__) || defined(_M_IX86))
+
+ #if defined(__GNUC__)
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned int lo, hi;
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
+ return (sqlite_uint64)hi << 32 | lo;
+ }
+
+ #elif defined(_MSC_VER)
+
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
+ __asm {
+ rdtsc
+ ret ; return value at EDX:EAX
+ }
+ }
+
+ #endif
+
+#elif (defined(__GNUC__) && defined(__x86_64__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long val;
+ __asm__ __volatile__ ("rdtsc" : "=A" (val));
+ return val;
+ }
+
+#elif (defined(__GNUC__) && defined(__ppc__))
+
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){
+ unsigned long long retval;
+ unsigned long junk;
+ __asm__ __volatile__ ("\n\
+ 1: mftbu %1\n\
+ mftb %L0\n\
+ mftbu %0\n\
+ cmpw %0,%1\n\
+ bne 1b"
+ : "=r" (retval), "=r" (junk));
+ return retval;
+ }
+
+#else
+
+ #error Need implementation of sqlite3Hwtime() for your platform.
+
+ /*
+ ** To compile without implementing sqlite3Hwtime() for your platform,
+ ** you can remove the above #error and use the following
+ ** stub function. You will lose timing support for many
+ ** of the debugging and testing utilities, but it should at
+ ** least compile and run.
+ */
+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
+
+#endif
+
+#endif /* !defined(SQLITE_HWTIME_H) */
+
+/************** End of hwtime.h **********************************************/
+/************** Continuing where we left off in os_common.h ******************/
+
+static sqlite_uint64 g_start;
+static sqlite_uint64 g_elapsed;
+#define TIMER_START g_start=sqlite3Hwtime()
+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
+#define TIMER_ELAPSED g_elapsed
+#else
+#define TIMER_START
+#define TIMER_END
+#define TIMER_ELAPSED ((sqlite_uint64)0)
+#endif
+
+/*
+** If we compile with the SQLITE_TEST macro set, then the following block
+** of code will give us the ability to simulate a disk I/O error. This
+** is used for testing the I/O recovery logic.
*/
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_io_error_hit;
+SQLITE_API extern int sqlite3_io_error_hardhit;
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_persist;
+SQLITE_API extern int sqlite3_io_error_benign;
+SQLITE_API extern int sqlite3_diskfull_pending;
+SQLITE_API extern int sqlite3_diskfull;
+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
+#define SimulateIOError(CODE) \
+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
+ || sqlite3_io_error_pending-- == 1 ) \
+ { local_ioerr(); CODE; }
+static void local_ioerr(){
+ IOTRACE(("IOERR\n"));
+ sqlite3_io_error_hit++;
+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
+}
+#define SimulateDiskfullError(CODE) \
+ if( sqlite3_diskfull_pending ){ \
+ if( sqlite3_diskfull_pending == 1 ){ \
+ local_ioerr(); \
+ sqlite3_diskfull = 1; \
+ sqlite3_io_error_hit = 1; \
+ CODE; \
+ }else{ \
+ sqlite3_diskfull_pending--; \
+ } \
+ }
+#else
+#define SimulateIOErrorBenign(X)
+#define SimulateIOError(A)
+#define SimulateDiskfullError(A)
+#endif /* defined(SQLITE_TEST) */
+
+/*
+** When testing, keep a count of the number of open files.
+*/
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_open_file_count;
+#define OpenCounter(X) sqlite3_open_file_count+=(X)
+#else
+#define OpenCounter(X)
+#endif /* defined(SQLITE_TEST) */
+
+#endif /* !defined(_OS_COMMON_H_) */
+
+/************** End of os_common.h *******************************************/
+/************** Continuing where we left off in mutex_w32.c ******************/
+
+/*
+** Include the header file for the Windows VFS.
+*/
+/************** Include os_win.h in the middle of mutex_w32.c ****************/
+/************** Begin file os_win.h ******************************************/
+/*
+** 2013 November 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains code that is specific to Windows.
+*/
+#ifndef SQLITE_OS_WIN_H
+#define SQLITE_OS_WIN_H
+
+/*
+** Include the primary Windows SDK header file.
+*/
+#include "windows.h"
+
+#ifdef __CYGWIN__
+# include <sys/cygwin.h>
+# include <errno.h> /* amalgamator: dontcache */
+#endif
+
+/*
+** Determine if we are dealing with Windows NT.
+**
+** We ought to be able to determine if we are compiling for Windows 9x or
+** Windows NT using the _WIN32_WINNT macro as follows:
+**
+** #if defined(_WIN32_WINNT)
+** # define SQLITE_OS_WINNT 1
+** #else
+** # define SQLITE_OS_WINNT 0
+** #endif
+**
+** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as
+** it ought to, so the above test does not work. We'll just assume that
+** everything is Windows NT unless the programmer explicitly says otherwise
+** by setting SQLITE_OS_WINNT to 0.
+*/
+#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT)
+# define SQLITE_OS_WINNT 1
+#endif
+
+/*
+** Determine if we are dealing with Windows CE - which has a much reduced
+** API.
+*/
+#if defined(_WIN32_WCE)
+# define SQLITE_OS_WINCE 1
+#else
+# define SQLITE_OS_WINCE 0
+#endif
+
+/*
+** Determine if we are dealing with WinRT, which provides only a subset of
+** the full Win32 API.
+*/
+#if !defined(SQLITE_OS_WINRT)
+# define SQLITE_OS_WINRT 0
+#endif
+
+/*
+** For WinCE, some API function parameters do not appear to be declared as
+** volatile.
+*/
+#if SQLITE_OS_WINCE
+# define SQLITE_WIN32_VOLATILE
+#else
+# define SQLITE_WIN32_VOLATILE volatile
+#endif
+
+/*
+** For some Windows sub-platforms, the _beginthreadex() / _endthreadex()
+** functions are not available (e.g. those not using MSVC, Cygwin, etc).
+*/
+#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
+ SQLITE_THREADSAFE>0 && !defined(__CYGWIN__)
+# define SQLITE_OS_WIN_THREADS 1
+#else
+# define SQLITE_OS_WIN_THREADS 0
+#endif
+
+#endif /* SQLITE_OS_WIN_H */
+
+/************** End of os_win.h **********************************************/
+/************** Continuing where we left off in mutex_w32.c ******************/
+#endif
/*
** The code in this file is only used if we are compiling multithreaded
-** on a win32 system.
+** on a Win32 system.
*/
#ifdef SQLITE_MUTEX_W32
@@ -17791,48 +23521,22 @@ struct sqlite3_mutex {
#ifdef SQLITE_DEBUG
volatile int nRef; /* Number of enterances */
volatile DWORD owner; /* Thread holding this mutex */
- int trace; /* True to trace changes */
+ volatile int trace; /* True to trace changes */
#endif
};
-#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, 0L, (DWORD)0, 0 }
-#else
-#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
-#endif
/*
-** Return true (non-zero) if we are running under WinNT, Win2K, WinXP,
-** or WinCE. Return false (zero) for Win95, Win98, or WinME.
-**
-** Here is an interesting observation: Win95, Win98, and WinME lack
-** the LockFileEx() API. But we can still statically link against that
-** API as long as we don't call it win running Win95/98/ME. A call to
-** this routine is used to determine if the host is Win95/98/ME or
-** WinNT/2K/XP so that we will know whether or not we can safely call
-** the LockFileEx() API.
-**
-** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
-** which is only available if your application was compiled with
-** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
-** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
-** this out as well.
+** These are the initializer values used when declaring a "static" mutex
+** on Win32. It should be noted that all mutexes require initialization
+** on the Win32 platform.
*/
-#if 0
-#if SQLITE_OS_WINCE
-# define mutexIsNT() (1)
+#define SQLITE_W32_MUTEX_INITIALIZER { 0 }
+
+#ifdef SQLITE_DEBUG
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \
+ 0L, (DWORD)0, 0 }
#else
- static int mutexIsNT(void){
- static int osType = 0;
- if( osType==0 ){
- OSVERSIONINFO sInfo;
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
- GetVersionEx(&sInfo);
- osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
- }
- return osType==2;
- }
-#endif /* SQLITE_OS_WINCE */
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 }
#endif
#ifdef SQLITE_DEBUG
@@ -17843,20 +23547,45 @@ struct sqlite3_mutex {
static int winMutexHeld(sqlite3_mutex *p){
return p->nRef!=0 && p->owner==GetCurrentThreadId();
}
+
static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
return p->nRef==0 || p->owner!=tid;
}
+
static int winMutexNotheld(sqlite3_mutex *p){
- DWORD tid = GetCurrentThreadId();
+ DWORD tid = GetCurrentThreadId();
return winMutexNotheld2(p, tid);
}
#endif
+/*
+** Try to provide a memory barrier operation, needed for initialization
+** and also for the xShmBarrier method of the VFS in cases when SQLite is
+** compiled without mutexes (SQLITE_THREADSAFE=0).
+*/
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void){
+#if defined(SQLITE_MEMORY_BARRIER)
+ SQLITE_MEMORY_BARRIER;
+#elif defined(__GNUC__)
+ __sync_synchronize();
+#elif !defined(SQLITE_DISABLE_INTRINSIC) && \
+ defined(_MSC_VER) && _MSC_VER>=1300
+ _ReadWriteBarrier();
+#elif defined(MemoryBarrier)
+ MemoryBarrier();
+#endif
+}
/*
** Initialize and deinitialize the mutex subsystem.
*/
-static sqlite3_mutex winMutex_staticMutexes[6] = {
+static sqlite3_mutex winMutex_staticMutexes[] = {
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
+ SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER,
@@ -17864,33 +23593,43 @@ static sqlite3_mutex winMutex_staticMutexes[6] = {
SQLITE3_MUTEX_INITIALIZER,
SQLITE3_MUTEX_INITIALIZER
};
+
static int winMutex_isInit = 0;
-/* As winMutexInit() and winMutexEnd() are called as part
-** of the sqlite3_initialize and sqlite3_shutdown()
-** processing, the "interlocked" magic is probably not
-** strictly necessary.
+static int winMutex_isNt = -1; /* <0 means "need to query" */
+
+/* As the winMutexInit() and winMutexEnd() functions are called as part
+** of the sqlite3_initialize() and sqlite3_shutdown() processing, the
+** "interlocked" magic used here is probably not strictly necessary.
*/
-static long winMutex_lock = 0;
+static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0;
+
+SQLITE_API int sqlite3_win32_is_nt(void); /* os_win.c */
+SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
-static int winMutexInit(void){
+static int winMutexInit(void){
/* The first to increment to 1 does actual initialization */
if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){
int i;
for(i=0; i<ArraySize(winMutex_staticMutexes); i++){
+#if SQLITE_OS_WINRT
+ InitializeCriticalSectionEx(&winMutex_staticMutexes[i].mutex, 0, 0);
+#else
InitializeCriticalSection(&winMutex_staticMutexes[i].mutex);
+#endif
}
winMutex_isInit = 1;
}else{
- /* Someone else is in the process of initing the static mutexes */
+ /* Another thread is (in the process of) initializing the static
+ ** mutexes */
while( !winMutex_isInit ){
- Sleep(1);
+ sqlite3_win32_sleep(1);
}
}
- return SQLITE_OK;
+ return SQLITE_OK;
}
-static int winMutexEnd(void){
- /* The first to decrement to 0 does actual shutdown
+static int winMutexEnd(void){
+ /* The first to decrement to 0 does actual shutdown
** (which should be the last to shutdown.) */
if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){
if( winMutex_isInit==1 ){
@@ -17901,7 +23640,7 @@ static int winMutexEnd(void){
winMutex_isInit = 0;
}
}
- return SQLITE_OK;
+ return SQLITE_OK;
}
/*
@@ -17916,10 +23655,16 @@ static int winMutexEnd(void){
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
** </ul>
**
** The first two constants cause sqlite3_mutex_alloc() to create
@@ -17942,7 +23687,7 @@ static int winMutexEnd(void){
**
** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
+** returns a different mutex on every call. But for the static
** mutex types, the same mutex is returned on every call that has
** the same type number.
*/
@@ -17953,21 +23698,34 @@ static sqlite3_mutex *winMutexAlloc(int iType){
case SQLITE_MUTEX_FAST:
case SQLITE_MUTEX_RECURSIVE: {
p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
-#ifdef SQLITE_DEBUG
+ if( p ){
p->id = iType;
+#ifdef SQLITE_DEBUG
+#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC
+ p->trace = 1;
+#endif
#endif
+#if SQLITE_OS_WINRT
+ InitializeCriticalSectionEx(&p->mutex, 0, 0);
+#else
InitializeCriticalSection(&p->mutex);
+#endif
}
break;
}
default: {
- assert( winMutex_isInit==1 );
- assert( iType-2 >= 0 );
- assert( iType-2 < ArraySize(winMutex_staticMutexes) );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
p = &winMutex_staticMutexes[iType-2];
-#ifdef SQLITE_DEBUG
p->id = iType;
+#ifdef SQLITE_DEBUG
+#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC
+ p->trace = 1;
+#endif
#endif
break;
}
@@ -17984,9 +23742,14 @@ static sqlite3_mutex *winMutexAlloc(int iType){
static void winMutexFree(sqlite3_mutex *p){
assert( p );
assert( p->nRef==0 && p->owner==0 );
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
- DeleteCriticalSection(&p->mutex);
- sqlite3_free(p);
+ if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){
+ DeleteCriticalSection(&p->mutex);
+ sqlite3_free(p);
+ }else{
+#ifdef SQLITE_ENABLE_API_ARMOR
+ (void)SQLITE_MISUSE_BKPT;
+#endif
+ }
}
/*
@@ -18001,30 +23764,39 @@ static void winMutexFree(sqlite3_mutex *p){
** more than once, the behavior is undefined.
*/
static void winMutexEnter(sqlite3_mutex *p){
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ DWORD tid = GetCurrentThreadId();
+#endif
#ifdef SQLITE_DEBUG
- DWORD tid = GetCurrentThreadId();
+ assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
+#else
+ assert( p );
#endif
+ assert( winMutex_isInit==1 );
EnterCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
assert( p->nRef>0 || p->owner==0 );
- p->owner = tid;
+ p->owner = tid;
p->nRef++;
if( p->trace ){
- printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ OSTRACE(("ENTER-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
+ tid, p, p->trace, p->nRef));
}
#endif
}
+
static int winMutexTry(sqlite3_mutex *p){
-#ifndef NDEBUG
- DWORD tid = GetCurrentThreadId();
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ DWORD tid = GetCurrentThreadId();
#endif
int rc = SQLITE_BUSY;
+ assert( p );
assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
/*
** The sqlite3_mutex_try() routine is very rarely used, and when it
** is used it is merely an optimization. So it is OK for it to always
- ** fail.
+ ** fail.
**
** The TryEnterCriticalSection() interface is only available on WinNT.
** And some windows compilers complain if you try to use it without
@@ -18032,18 +23804,27 @@ static int winMutexTry(sqlite3_mutex *p){
** For that reason, we will omit this optimization for now. See
** ticket #2685.
*/
-#if 0
- if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400
+ assert( winMutex_isInit==1 );
+ assert( winMutex_isNt>=-1 && winMutex_isNt<=1 );
+ if( winMutex_isNt<0 ){
+ winMutex_isNt = sqlite3_win32_is_nt();
+ }
+ assert( winMutex_isNt==0 || winMutex_isNt==1 );
+ if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){
+#ifdef SQLITE_DEBUG
p->owner = tid;
p->nRef++;
+#endif
rc = SQLITE_OK;
}
#else
UNUSED_PARAMETER(p);
#endif
#ifdef SQLITE_DEBUG
- if( rc==SQLITE_OK && p->trace ){
- printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ if( p->trace ){
+ OSTRACE(("TRY-MUTEX tid=%lu, mutex=%p (%d), owner=%lu, nRef=%d, rc=%s\n",
+ tid, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc)));
}
#endif
return rc;
@@ -18056,18 +23837,23 @@ static int winMutexTry(sqlite3_mutex *p){
** is not currently allocated. SQLite will never do either.
*/
static void winMutexLeave(sqlite3_mutex *p){
-#ifndef NDEBUG
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
DWORD tid = GetCurrentThreadId();
+#endif
+ assert( p );
+#ifdef SQLITE_DEBUG
assert( p->nRef>0 );
assert( p->owner==tid );
p->nRef--;
if( p->nRef==0 ) p->owner = 0;
assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
#endif
+ assert( winMutex_isInit==1 );
LeaveCriticalSection(&p->mutex);
#ifdef SQLITE_DEBUG
if( p->trace ){
- printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
+ OSTRACE(("LEAVE-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n",
+ tid, p, p->trace, p->nRef));
}
#endif
}
@@ -18089,9 +23875,9 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
0
#endif
};
-
return &sMutex;
}
+
#endif /* SQLITE_MUTEX_W32 */
/************** End of mutex_w32.c *******************************************/
@@ -18110,6 +23896,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
**
** Memory allocation functions used throughout sqlite.
*/
+/* #include "sqliteInt.h" */
/* #include <stdarg.h> */
/*
@@ -18142,16 +23929,7 @@ typedef struct ScratchFreeslot {
*/
static SQLITE_WSD struct Mem0Global {
sqlite3_mutex *mutex; /* Mutex to serialize access */
-
- /*
- ** The alarm callback and its arguments. The mem0.mutex lock will
- ** be held while the callback is running. Recursive calls into
- ** the memory subsystem are allowed, but no new callbacks will be
- ** issued.
- */
- sqlite3_int64 alarmThreshold;
- void (*alarmCallback)(void*, sqlite3_int64,int);
- void *alarmArg;
+ sqlite3_int64 alarmThreshold; /* The soft heap limit */
/*
** Pointers to the end of sqlite3GlobalConfig.pScratch memory
@@ -18168,54 +23946,32 @@ static SQLITE_WSD struct Mem0Global {
** sqlite3_soft_heap_limit() setting.
*/
int nearlyFull;
-} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 };
+} mem0 = { 0, 0, 0, 0, 0, 0 };
#define mem0 GLOBAL(struct Mem0Global, mem0)
/*
-** This routine runs when the memory allocator sees that the
-** total memory allocation is about to exceed the soft heap
-** limit.
-*/
-static void softHeapLimitEnforcer(
- void *NotUsed,
- sqlite3_int64 NotUsed2,
- int allocSize
-){
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
- sqlite3_release_memory(allocSize);
-}
-
-/*
-** Change the alarm callback
+** Return the memory allocator mutex. sqlite3_status() needs it.
*/
-static int sqlite3MemoryAlarm(
- void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
- void *pArg,
- sqlite3_int64 iThreshold
-){
- int nUsed;
- sqlite3_mutex_enter(mem0.mutex);
- mem0.alarmCallback = xCallback;
- mem0.alarmArg = pArg;
- mem0.alarmThreshold = iThreshold;
- nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
- mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed);
- sqlite3_mutex_leave(mem0.mutex);
- return SQLITE_OK;
+SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void){
+ return mem0.mutex;
}
#ifndef SQLITE_OMIT_DEPRECATED
/*
-** Deprecated external interface. Internal/core SQLite code
-** should call sqlite3MemoryAlarm.
+** Deprecated external interface. It used to set an alarm callback
+** that was invoked when memory usage grew too large. Now it is a
+** no-op.
*/
SQLITE_API int sqlite3_memory_alarm(
void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
void *pArg,
sqlite3_int64 iThreshold
){
- return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
+ (void)xCallback;
+ (void)pArg;
+ (void)iThreshold;
+ return SQLITE_OK;
}
#endif
@@ -18226,18 +23982,21 @@ SQLITE_API int sqlite3_memory_alarm(
SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
sqlite3_int64 priorLimit;
sqlite3_int64 excess;
+ sqlite3_int64 nUsed;
#ifndef SQLITE_OMIT_AUTOINIT
- sqlite3_initialize();
+ int rc = sqlite3_initialize();
+ if( rc ) return -1;
#endif
sqlite3_mutex_enter(mem0.mutex);
priorLimit = mem0.alarmThreshold;
- sqlite3_mutex_leave(mem0.mutex);
- if( n<0 ) return priorLimit;
- if( n>0 ){
- sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
- }else{
- sqlite3MemoryAlarm(0, 0, 0);
+ if( n<0 ){
+ sqlite3_mutex_leave(mem0.mutex);
+ return priorLimit;
}
+ mem0.alarmThreshold = n;
+ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
+ mem0.nearlyFull = (n>0 && n<=nUsed);
+ sqlite3_mutex_leave(mem0.mutex);
excess = sqlite3_memory_used() - n;
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
return priorLimit;
@@ -18251,13 +24010,12 @@ SQLITE_API void sqlite3_soft_heap_limit(int n){
** Initialize the memory allocation subsystem.
*/
SQLITE_PRIVATE int sqlite3MallocInit(void){
+ int rc;
if( sqlite3GlobalConfig.m.xMalloc==0 ){
sqlite3MemSetDefault();
}
memset(&mem0, 0, sizeof(mem0));
- if( sqlite3GlobalConfig.bCoreMutex ){
- mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
- }
+ mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM);
if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100
&& sqlite3GlobalConfig.nScratch>0 ){
int i, n, sz;
@@ -18281,12 +24039,13 @@ SQLITE_PRIVATE int sqlite3MallocInit(void){
sqlite3GlobalConfig.nScratch = 0;
}
if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512
- || sqlite3GlobalConfig.nPage<1 ){
+ || sqlite3GlobalConfig.nPage<=0 ){
sqlite3GlobalConfig.pPage = 0;
sqlite3GlobalConfig.szPage = 0;
- sqlite3GlobalConfig.nPage = 0;
}
- return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
+ rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
+ if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0));
+ return rc;
}
/*
@@ -18312,10 +24071,8 @@ SQLITE_PRIVATE void sqlite3MallocEnd(void){
** Return the amount of memory currently checked out.
*/
SQLITE_API sqlite3_int64 sqlite3_memory_used(void){
- int n, mx;
- sqlite3_int64 res;
- sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0);
- res = (sqlite3_int64)n; /* Work around bug in Borland C. Ticket #3216 */
+ sqlite3_int64 res, mx;
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, 0);
return res;
}
@@ -18325,30 +24082,19 @@ SQLITE_API sqlite3_int64 sqlite3_memory_used(void){
** or since the most recent reset.
*/
SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
- int n, mx;
- sqlite3_int64 res;
- sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag);
- res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */
- return res;
+ sqlite3_int64 res, mx;
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, resetFlag);
+ return mx;
}
/*
** Trigger the alarm
*/
static void sqlite3MallocAlarm(int nByte){
- void (*xCallback)(void*,sqlite3_int64,int);
- sqlite3_int64 nowUsed;
- void *pArg;
- if( mem0.alarmCallback==0 ) return;
- xCallback = mem0.alarmCallback;
- nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
- pArg = mem0.alarmArg;
- mem0.alarmCallback = 0;
+ if( mem0.alarmThreshold<=0 ) return;
sqlite3_mutex_leave(mem0.mutex);
- xCallback(pArg, nowUsed, nByte);
+ sqlite3_release_memory(nByte);
sqlite3_mutex_enter(mem0.mutex);
- mem0.alarmCallback = xCallback;
- mem0.alarmArg = pArg;
}
/*
@@ -18360,9 +24106,9 @@ static int mallocWithAlarm(int n, void **pp){
void *p;
assert( sqlite3_mutex_held(mem0.mutex) );
nFull = sqlite3GlobalConfig.m.xRoundup(n);
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
- if( mem0.alarmCallback!=0 ){
- int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
+ if( mem0.alarmThreshold>0 ){
+ sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
if( nUsed >= mem0.alarmThreshold - nFull ){
mem0.nearlyFull = 1;
sqlite3MallocAlarm(nFull);
@@ -18372,15 +24118,15 @@ static int mallocWithAlarm(int n, void **pp){
}
p = sqlite3GlobalConfig.m.xMalloc(nFull);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
- if( p==0 && mem0.alarmCallback ){
+ if( p==0 && mem0.alarmThreshold>0 ){
sqlite3MallocAlarm(nFull);
p = sqlite3GlobalConfig.m.xMalloc(nFull);
}
#endif
if( p ){
nFull = sqlite3MallocSize(p);
- sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);
- sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1);
+ sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull);
+ sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1);
}
*pp = p;
return nFull;
@@ -18390,11 +24136,9 @@ static int mallocWithAlarm(int n, void **pp){
** Allocate memory. This routine is like sqlite3_malloc() except that it
** assumes the memory subsystem has already been initialized.
*/
-SQLITE_PRIVATE void *sqlite3Malloc(int n){
+SQLITE_PRIVATE void *sqlite3Malloc(u64 n){
void *p;
- if( n<=0 /* IMP: R-65312-04917 */
- || n>=0x7fffff00
- ){
+ if( n==0 || n>=0x7fffff00 ){
/* A memory allocation of a number of bytes which is near the maximum
** signed integer value might cause an integer overflow inside of the
** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving
@@ -18403,12 +24147,12 @@ SQLITE_PRIVATE void *sqlite3Malloc(int n){
p = 0;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
- mallocWithAlarm(n, &p);
+ mallocWithAlarm((int)n, &p);
sqlite3_mutex_leave(mem0.mutex);
}else{
- p = sqlite3GlobalConfig.m.xMalloc(n);
+ p = sqlite3GlobalConfig.m.xMalloc((int)n);
}
- assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */
+ assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */
return p;
}
@@ -18421,6 +24165,12 @@ SQLITE_API void *sqlite3_malloc(int n){
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
+ return n<=0 ? 0 : sqlite3Malloc(n);
+}
+SQLITE_API void *sqlite3_malloc64(sqlite3_uint64 n){
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
return sqlite3Malloc(n);
}
@@ -18448,22 +24198,20 @@ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){
assert( n>0 );
sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusHighwater(SQLITE_STATUS_SCRATCH_SIZE, n);
if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
p = mem0.pScratchFree;
mem0.pScratchFree = mem0.pScratchFree->pNext;
mem0.nScratchFree--;
- sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
+ sqlite3StatusUp(SQLITE_STATUS_SCRATCH_USED, 1);
sqlite3_mutex_leave(mem0.mutex);
}else{
- if( sqlite3GlobalConfig.bMemstat ){
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
- n = mallocWithAlarm(n, &p);
- if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
- sqlite3_mutex_leave(mem0.mutex);
- }else{
+ sqlite3_mutex_leave(mem0.mutex);
+ p = sqlite3Malloc(n);
+ if( sqlite3GlobalConfig.bMemstat && p ){
+ sqlite3_mutex_enter(mem0.mutex);
+ sqlite3StatusUp(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p));
sqlite3_mutex_leave(mem0.mutex);
- p = sqlite3GlobalConfig.m.xMalloc(n);
}
sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
}
@@ -18471,11 +24219,12 @@ SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){
#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
- /* Verify that no more than two scratch allocations per thread
- ** are outstanding at one time. (This is only checked in the
- ** single-threaded case since checking in the multi-threaded case
- ** would be much more complicated.) */
- assert( scratchAllocOut<=1 );
+ /* EVIDENCE-OF: R-12970-05880 SQLite will not use more than one scratch
+ ** buffers per thread.
+ **
+ ** This can only be checked in single-threaded mode.
+ */
+ assert( scratchAllocOut==0 );
if( p ) scratchAllocOut++;
#endif
@@ -18493,7 +24242,7 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
scratchAllocOut--;
#endif
- if( p>=sqlite3GlobalConfig.pScratch && p<mem0.pScratchEnd ){
+ if( SQLITE_WITHIN(p, sqlite3GlobalConfig.pScratch, mem0.pScratchEnd) ){
/* Release memory from the SQLITE_CONFIG_SCRATCH allocation */
ScratchFreeslot *pSlot;
pSlot = (ScratchFreeslot*)p;
@@ -18502,19 +24251,19 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
mem0.pScratchFree = pSlot;
mem0.nScratchFree++;
assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch );
- sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
+ sqlite3StatusDown(SQLITE_STATUS_SCRATCH_USED, 1);
sqlite3_mutex_leave(mem0.mutex);
}else{
/* Release memory back to the heap */
assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
- assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) );
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_SCRATCH) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
if( sqlite3GlobalConfig.bMemstat ){
int iSize = sqlite3MallocSize(p);
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize);
- sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
- sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1);
+ sqlite3StatusDown(SQLITE_STATUS_SCRATCH_OVERFLOW, iSize);
+ sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, iSize);
+ sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
sqlite3GlobalConfig.m.xFree(p);
sqlite3_mutex_leave(mem0.mutex);
}else{
@@ -18529,7 +24278,7 @@ SQLITE_PRIVATE void sqlite3ScratchFree(void *p){
*/
#ifndef SQLITE_OMIT_LOOKASIDE
static int isLookaside(sqlite3 *db, void *p){
- return p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
+ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd);
}
#else
#define isLookaside(A,B) 0
@@ -18541,32 +24290,43 @@ static int isLookaside(sqlite3 *db, void *p){
*/
SQLITE_PRIVATE int sqlite3MallocSize(void *p){
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
return sqlite3GlobalConfig.m.xSize(p);
}
SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
- assert( db==0 || sqlite3_mutex_held(db->mutex) );
- if( db && isLookaside(db, p) ){
- return db->lookaside.sz;
- }else{
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
- assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
+ assert( p!=0 );
+ if( db==0 || !isLookaside(db,p) ){
+#if SQLITE_DEBUG
+ if( db==0 ){
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ }else{
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ }
+#endif
return sqlite3GlobalConfig.m.xSize(p);
+ }else{
+ assert( sqlite3_mutex_held(db->mutex) );
+ return db->lookaside.sz;
}
}
+SQLITE_API sqlite3_uint64 sqlite3_msize(void *p){
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ return p ? sqlite3GlobalConfig.m.xSize(p) : 0;
+}
/*
** Free memory previously obtained from sqlite3Malloc().
*/
SQLITE_API void sqlite3_free(void *p){
if( p==0 ) return; /* IMP: R-49053-54554 */
- assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) );
if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p));
- sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1);
+ sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlite3MallocSize(p));
+ sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1);
sqlite3GlobalConfig.m.xFree(p);
sqlite3_mutex_leave(mem0.mutex);
}else{
@@ -18575,26 +24335,39 @@ SQLITE_API void sqlite3_free(void *p){
}
/*
+** Add the size of memory allocation "p" to the count in
+** *db->pnBytesFreed.
+*/
+static SQLITE_NOINLINE void measureAllocationSize(sqlite3 *db, void *p){
+ *db->pnBytesFreed += sqlite3DbMallocSize(db,p);
+}
+
+/*
** Free memory that might be associated with a particular database
** connection.
*/
SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
+ if( p==0 ) return;
if( db ){
if( db->pnBytesFreed ){
- *db->pnBytesFreed += sqlite3DbMallocSize(db, p);
+ measureAllocationSize(db, p);
return;
}
if( isLookaside(db, p) ){
LookasideSlot *pBuf = (LookasideSlot*)p;
+#if SQLITE_DEBUG
+ /* Trash all content in the buffer being freed */
+ memset(p, 0xaa, db->lookaside.sz);
+#endif
pBuf->pNext = db->lookaside.pFree;
db->lookaside.pFree = pBuf;
db->lookaside.nOut--;
return;
}
}
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
sqlite3_free(p);
@@ -18603,14 +24376,16 @@ SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){
/*
** Change the size of an existing memory allocation
*/
-SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){
+SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){
int nOld, nNew, nDiff;
void *pNew;
+ assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) );
if( pOld==0 ){
- return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
+ return sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */
}
- if( nBytes<=0 ){
- sqlite3_free(pOld); /* IMP: R-31593-10574 */
+ if( nBytes==0 ){
+ sqlite3_free(pOld); /* IMP: R-26507-47431 */
return 0;
}
if( nBytes>=0x7fffff00 ){
@@ -18621,33 +24396,31 @@ SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){
/* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second
** argument to xRealloc is always a value returned by a prior call to
** xRoundup. */
- nNew = sqlite3GlobalConfig.m.xRoundup(nBytes);
+ nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes);
if( nOld==nNew ){
pNew = pOld;
}else if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
nDiff = nNew - nOld;
if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
mem0.alarmThreshold-nDiff ){
sqlite3MallocAlarm(nDiff);
}
- assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
- assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
- if( pNew==0 && mem0.alarmCallback ){
- sqlite3MallocAlarm(nBytes);
+ if( pNew==0 && mem0.alarmThreshold>0 ){
+ sqlite3MallocAlarm((int)nBytes);
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
}
if( pNew ){
nNew = sqlite3MallocSize(pNew);
- sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
+ sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
}
sqlite3_mutex_leave(mem0.mutex);
}else{
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
}
- assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */
return pNew;
}
@@ -18659,6 +24432,13 @@ SQLITE_API void *sqlite3_realloc(void *pOld, int n){
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
+ if( n<0 ) n = 0; /* IMP: R-26507-47431 */
+ return sqlite3Realloc(pOld, n);
+}
+SQLITE_API void *sqlite3_realloc64(void *pOld, sqlite3_uint64 n){
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
return sqlite3Realloc(pOld, n);
}
@@ -18666,10 +24446,10 @@ SQLITE_API void *sqlite3_realloc(void *pOld, int n){
/*
** Allocate and zero memory.
*/
-SQLITE_PRIVATE void *sqlite3MallocZero(int n){
+SQLITE_PRIVATE void *sqlite3MallocZero(u64 n){
void *p = sqlite3Malloc(n);
if( p ){
- memset(p, 0, n);
+ memset(p, 0, (size_t)n);
}
return p;
}
@@ -18678,17 +24458,32 @@ SQLITE_PRIVATE void *sqlite3MallocZero(int n){
** Allocate and zero memory. If the allocation fails, make
** the mallocFailed flag in the connection pointer.
*/
-SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, int n){
- void *p = sqlite3DbMallocRaw(db, n);
- if( p ){
- memset(p, 0, n);
- }
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, u64 n){
+ void *p;
+ testcase( db==0 );
+ p = sqlite3DbMallocRaw(db, n);
+ if( p ) memset(p, 0, (size_t)n);
+ return p;
+}
+
+
+/* Finish the work of sqlite3DbMallocRawNN for the unusual and
+** slower case when the allocation cannot be fulfilled using lookaside.
+*/
+static SQLITE_NOINLINE void *dbMallocRawFinish(sqlite3 *db, u64 n){
+ void *p;
+ assert( db!=0 );
+ p = sqlite3Malloc(n);
+ if( !p ) sqlite3OomFault(db);
+ sqlite3MemdebugSetType(p,
+ (db->lookaside.bDisable==0) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP);
return p;
}
/*
-** Allocate and zero memory. If the allocation fails, make
-** the mallocFailed flag in the connection pointer.
+** Allocate memory, either lookaside (if possible) or heap.
+** If the allocation fails, set the mallocFailed flag in
+** the connection pointer.
**
** If db!=0 and db->mallocFailed is true (indicating a prior malloc
** failure on the same database connection) then always return 0.
@@ -18703,79 +24498,87 @@ SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, int n){
**
** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
** that all prior mallocs (ex: "a") worked too.
+**
+** The sqlite3MallocRawNN() variant guarantees that the "db" parameter is
+** not a NULL pointer.
*/
-SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, int n){
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){
void *p;
- assert( db==0 || sqlite3_mutex_held(db->mutex) );
- assert( db==0 || db->pnBytesFreed==0 );
+ if( db ) return sqlite3DbMallocRawNN(db, n);
+ p = sqlite3Malloc(n);
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
+ return p;
+}
+SQLITE_PRIVATE void *sqlite3DbMallocRawNN(sqlite3 *db, u64 n){
#ifndef SQLITE_OMIT_LOOKASIDE
- if( db ){
- LookasideSlot *pBuf;
- if( db->mallocFailed ){
- return 0;
- }
- if( db->lookaside.bEnabled ){
- if( n>db->lookaside.sz ){
- db->lookaside.anStat[1]++;
- }else if( (pBuf = db->lookaside.pFree)==0 ){
- db->lookaside.anStat[2]++;
- }else{
- db->lookaside.pFree = pBuf->pNext;
- db->lookaside.nOut++;
- db->lookaside.anStat[0]++;
- if( db->lookaside.nOut>db->lookaside.mxOut ){
- db->lookaside.mxOut = db->lookaside.nOut;
- }
- return (void*)pBuf;
+ LookasideSlot *pBuf;
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( db->pnBytesFreed==0 );
+ if( db->lookaside.bDisable==0 ){
+ assert( db->mallocFailed==0 );
+ if( n>db->lookaside.sz ){
+ db->lookaside.anStat[1]++;
+ }else if( (pBuf = db->lookaside.pFree)==0 ){
+ db->lookaside.anStat[2]++;
+ }else{
+ db->lookaside.pFree = pBuf->pNext;
+ db->lookaside.nOut++;
+ db->lookaside.anStat[0]++;
+ if( db->lookaside.nOut>db->lookaside.mxOut ){
+ db->lookaside.mxOut = db->lookaside.nOut;
}
+ return (void*)pBuf;
}
+ }else if( db->mallocFailed ){
+ return 0;
}
#else
- if( db && db->mallocFailed ){
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ assert( db->pnBytesFreed==0 );
+ if( db->mallocFailed ){
return 0;
}
#endif
- p = sqlite3Malloc(n);
- if( !p && db ){
- db->mallocFailed = 1;
- }
- sqlite3MemdebugSetType(p, MEMTYPE_DB |
- ((db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
- return p;
+ return dbMallocRawFinish(db, n);
}
+/* Forward declaration */
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n);
+
/*
** Resize the block of memory pointed to by p to n bytes. If the
** resize fails, set the mallocFailed flag in the connection object.
*/
-SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
- void *pNew = 0;
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){
assert( db!=0 );
+ if( p==0 ) return sqlite3DbMallocRawNN(db, n);
assert( sqlite3_mutex_held(db->mutex) );
+ if( isLookaside(db,p) && n<=db->lookaside.sz ) return p;
+ return dbReallocFinish(db, p, n);
+}
+static SQLITE_NOINLINE void *dbReallocFinish(sqlite3 *db, void *p, u64 n){
+ void *pNew = 0;
+ assert( db!=0 );
+ assert( p!=0 );
if( db->mallocFailed==0 ){
- if( p==0 ){
- return sqlite3DbMallocRaw(db, n);
- }
if( isLookaside(db, p) ){
- if( n<=db->lookaside.sz ){
- return p;
- }
- pNew = sqlite3DbMallocRaw(db, n);
+ pNew = sqlite3DbMallocRawNN(db, n);
if( pNew ){
memcpy(pNew, p, db->lookaside.sz);
sqlite3DbFree(db, p);
}
}else{
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- pNew = sqlite3_realloc(p, n);
+ pNew = sqlite3_realloc64(p, n);
if( !pNew ){
- sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP);
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
- sqlite3MemdebugSetType(pNew, MEMTYPE_DB |
- (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
+ sqlite3MemdebugSetType(pNew,
+ (db->lookaside.bDisable==0 ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
}
}
return pNew;
@@ -18785,7 +24588,7 @@ SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
** Attempt to reallocate p. If the reallocation fails, then free p
** and set the mallocFailed flag in the database connection.
*/
-SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, int n){
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, u64 n){
void *pNew;
pNew = sqlite3DbRealloc(db, p, n);
if( !pNew ){
@@ -18815,36 +24618,69 @@ SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3 *db, const char *z){
}
return zNew;
}
-SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, int n){
+SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){
char *zNew;
+ assert( db!=0 );
if( z==0 ){
return 0;
}
assert( (n&0x7fffffff)==n );
- zNew = sqlite3DbMallocRaw(db, n+1);
+ zNew = sqlite3DbMallocRawNN(db, n+1);
if( zNew ){
- memcpy(zNew, z, n);
+ memcpy(zNew, z, (size_t)n);
zNew[n] = 0;
}
return zNew;
}
/*
-** Create a string from the zFromat argument and the va_list that follows.
-** Store the string in memory obtained from sqliteMalloc() and make *pz
-** point to that string.
+** Free any prior content in *pz and replace it with a copy of zNew.
*/
-SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
- va_list ap;
- char *z;
-
- va_start(ap, zFormat);
- z = sqlite3VMPrintf(db, zFormat, ap);
- va_end(ap);
+SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){
sqlite3DbFree(db, *pz);
- *pz = z;
+ *pz = sqlite3DbStrDup(db, zNew);
}
+/*
+** Call this routine to record the fact that an OOM (out-of-memory) error
+** has happened. This routine will set db->mallocFailed, and also
+** temporarily disable the lookaside memory allocator and interrupt
+** any running VDBEs.
+*/
+SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
+ if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
+ db->mallocFailed = 1;
+ if( db->nVdbeExec>0 ){
+ db->u1.isInterrupted = 1;
+ }
+ db->lookaside.bDisable++;
+ }
+}
+
+/*
+** This routine reactivates the memory allocator and clears the
+** db->mallocFailed flag as necessary.
+**
+** The memory allocator is not restarted if there are running
+** VDBEs.
+*/
+SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){
+ if( db->mallocFailed && db->nVdbeExec==0 ){
+ db->mallocFailed = 0;
+ db->u1.isInterrupted = 0;
+ assert( db->lookaside.bDisable>0 );
+ db->lookaside.bDisable--;
+ }
+}
+
+/*
+** Take actions at the end of an API call to indicate an OOM error
+*/
+static SQLITE_NOINLINE int apiOomError(sqlite3 *db){
+ sqlite3OomClear(db);
+ sqlite3Error(db, SQLITE_NOMEM);
+ return SQLITE_NOMEM_BKPT;
+}
/*
** This function must be called before exiting any API function (i.e.
@@ -18855,103 +24691,61 @@ SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat
** function. However, if a malloc() failure has occurred since the previous
** invocation SQLITE_NOMEM is returned instead.
**
-** If the first argument, db, is not NULL and a malloc() error has occurred,
-** then the connection error-code (the value returned by sqlite3_errcode())
-** is set to SQLITE_NOMEM.
+** If an OOM as occurred, then the connection error-code (the value
+** returned by sqlite3_errcode()) is set to SQLITE_NOMEM.
*/
SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
- /* If the db handle is not NULL, then we must hold the connection handle
- ** mutex here. Otherwise the read (and possible write) of db->mallocFailed
+ /* If the db handle must hold the connection handle mutex here.
+ ** Otherwise the read (and possible write) of db->mallocFailed
** is unsafe, as is the call to sqlite3Error().
*/
- assert( !db || sqlite3_mutex_held(db->mutex) );
- if( db && (db->mallocFailed || rc==SQLITE_IOERR_NOMEM) ){
- sqlite3Error(db, SQLITE_NOMEM, 0);
- db->mallocFailed = 0;
- rc = SQLITE_NOMEM;
+ assert( db!=0 );
+ assert( sqlite3_mutex_held(db->mutex) );
+ if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){
+ return apiOomError(db);
}
- return rc & (db ? db->errMask : 0xff);
+ return rc & db->errMask;
}
/************** End of malloc.c **********************************************/
/************** Begin file printf.c ******************************************/
/*
** The "printf" code that follows dates from the 1980's. It is in
-** the public domain. The original comments are included here for
-** completeness. They are very out-of-date but might be useful as
-** an historical reference. Most of the "enhancements" have been backed
-** out so that the functionality is now the same as standard printf().
+** the public domain.
**
**************************************************************************
**
-** The following modules is an enhanced replacement for the "printf" subroutines
-** found in the standard C library. The following enhancements are
-** supported:
-**
-** + Additional functions. The standard set of "printf" functions
-** includes printf, fprintf, sprintf, vprintf, vfprintf, and
-** vsprintf. This module adds the following:
-**
-** * snprintf -- Works like sprintf, but has an extra argument
-** which is the size of the buffer written to.
-**
-** * mprintf -- Similar to sprintf. Writes output to memory
-** obtained from malloc.
-**
-** * xprintf -- Calls a function to dispose of output.
-**
-** * nprintf -- No output, but returns the number of characters
-** that would have been output by printf.
-**
-** * A v- version (ex: vsnprintf) of every function is also
-** supplied.
-**
-** + A few extensions to the formatting notation are supported:
-**
-** * The "=" flag (similar to "-") causes the output to be
-** be centered in the appropriately sized field.
-**
-** * The %b field outputs an integer in binary notation.
-**
-** * The %c field now accepts a precision. The character output
-** is repeated by the number of times the precision specifies.
-**
-** * The %' field works like %c, but takes as its character the
-** next character of the format string, instead of the next
-** argument. For example, printf("%.78'-") prints 78 minus
-** signs, the same as printf("%.78c",'-').
-**
-** + When compiled using GCC on a SPARC, this version of printf is
-** faster than the library printf for SUN OS 4.1.
-**
-** + All functions are fully reentrant.
-**
+** This file contains code for a set of "printf"-like routines. These
+** routines format strings much like the printf() from the standard C
+** library, though the implementation here has enhancements to support
+** SQLite.
*/
+/* #include "sqliteInt.h" */
/*
** Conversion types fall into various categories as defined by the
** following enumeration.
*/
-#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */
-#define etFLOAT 2 /* Floating point. %f */
-#define etEXP 3 /* Exponentional notation. %e and %E */
-#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */
-#define etSIZE 5 /* Return number of characters processed so far. %n */
-#define etSTRING 6 /* Strings. %s */
-#define etDYNSTRING 7 /* Dynamically allocated strings. %z */
-#define etPERCENT 8 /* Percent symbol. %% */
-#define etCHARX 9 /* Characters. %c */
+#define etRADIX 0 /* Integer types. %d, %x, %o, and so forth */
+#define etFLOAT 1 /* Floating point. %f */
+#define etEXP 2 /* Exponentional notation. %e and %E */
+#define etGENERIC 3 /* Floating or exponential, depending on exponent. %g */
+#define etSIZE 4 /* Return number of characters processed so far. %n */
+#define etSTRING 5 /* Strings. %s */
+#define etDYNSTRING 6 /* Dynamically allocated strings. %z */
+#define etPERCENT 7 /* Percent symbol. %% */
+#define etCHARX 8 /* Characters. %c */
/* The rest are extensions, not normally found in printf() */
-#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */
-#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '',
+#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */
+#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
NULL pointers replaced by SQL NULL. %Q */
-#define etTOKEN 12 /* a pointer to a Token structure */
-#define etSRCLIST 13 /* a pointer to a SrcList */
-#define etPOINTER 14 /* The %p conversion */
-#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */
-#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
+#define etTOKEN 11 /* a pointer to a Token structure */
+#define etSRCLIST 12 /* a pointer to a SrcList */
+#define etPOINTER 13 /* The %p conversion */
+#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
+#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
-#define etINVALID 0 /* Any unrecognized conversion type */
+#define etINVALID 16 /* Any unrecognized conversion type */
/*
@@ -19038,7 +24832,8 @@ static const et_info fmtinfo[] = {
static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
int digit;
LONGDOUBLE_TYPE d;
- if( (*cnt)++ >= 16 ) return '0';
+ if( (*cnt)<=0 ) return '0';
+ (*cnt)--;
digit = (int)*val;
d = digit;
digit += '0';
@@ -19048,64 +24843,47 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
#endif /* SQLITE_OMIT_FLOATING_POINT */
/*
-** Append N space characters to the given string buffer.
+** Set the StrAccum object to an error mode.
*/
-static void appendSpace(StrAccum *pAccum, int N){
- static const char zSpaces[] = " ";
- while( N>=(int)sizeof(zSpaces)-1 ){
- sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
- N -= sizeof(zSpaces)-1;
- }
- if( N>0 ){
- sqlite3StrAccumAppend(pAccum, zSpaces, N);
- }
+static void setStrAccumError(StrAccum *p, u8 eError){
+ assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG );
+ p->accError = eError;
+ p->nAlloc = 0;
}
/*
+** Extra argument values from a PrintfArguments object
+*/
+static sqlite3_int64 getIntArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0;
+ return sqlite3_value_int64(p->apArg[p->nUsed++]);
+}
+static double getDoubleArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0.0;
+ return sqlite3_value_double(p->apArg[p->nUsed++]);
+}
+static char *getTextArg(PrintfArguments *p){
+ if( p->nArg<=p->nUsed ) return 0;
+ return (char*)sqlite3_value_text(p->apArg[p->nUsed++]);
+}
+
+
+/*
** On machines with a small stack size, you can redefine the
-** SQLITE_PRINT_BUF_SIZE to be less than 350.
+** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
*/
#ifndef SQLITE_PRINT_BUF_SIZE
-# if defined(SQLITE_SMALL_STACK)
-# define SQLITE_PRINT_BUF_SIZE 50
-# else
-# define SQLITE_PRINT_BUF_SIZE 350
-# endif
+# define SQLITE_PRINT_BUF_SIZE 70
#endif
#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */
/*
-** The root program. All variations call this core.
-**
-** INPUTS:
-** func This is a pointer to a function taking three arguments
-** 1. A pointer to anything. Same as the "arg" parameter.
-** 2. A pointer to the list of characters to be output
-** (Note, this list is NOT null terminated.)
-** 3. An integer number of characters to be output.
-** (Note: This number might be zero.)
-**
-** arg This is the pointer to anything which will be passed as the
-** first argument to "func". Use it for whatever you like.
-**
-** fmt This is the format string, as in the usual print.
-**
-** ap This is a pointer to a list of arguments. Same as in
-** vfprint.
-**
-** OUTPUTS:
-** The return value is the total number of characters sent to
-** the function "func". Returns -1 on a error.
-**
-** Note that the order in which automatic variables are declared below
-** seems to make a big difference in determining how fast this beast
-** will run.
+** Render a string given by "fmt" into the StrAccum object.
*/
SQLITE_PRIVATE void sqlite3VXPrintf(
- StrAccum *pAccum, /* Accumulate results here */
- int useExtended, /* Allow extended %-conversions */
- const char *fmt, /* Format string */
- va_list ap /* arguments */
+ StrAccum *pAccum, /* Accumulate results here */
+ const char *fmt, /* Format string */
+ va_list ap /* arguments */
){
int c; /* Next character in the format string */
char *bufpt; /* Pointer to the conversion buffer */
@@ -19122,32 +24900,45 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
etByte flag_long; /* True if "l" flag is present */
etByte flag_longlong; /* True if the "ll" flag is present */
etByte done; /* Loop termination flag */
+ etByte xtype = etINVALID; /* Conversion paradigm */
+ u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */
+ u8 useIntern; /* Ok to use internal conversions (ex: %T) */
+ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
sqlite_uint64 longvalue; /* Value for integer types */
LONGDOUBLE_TYPE realvalue; /* Value for real types */
const et_info *infop; /* Pointer to the appropriate info structure */
- char buf[etBUFSIZE]; /* Conversion buffer */
- char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */
- etByte xtype = 0; /* Conversion paradigm */
- char *zExtra; /* Extra memory used for etTCLESCAPE conversions */
+ char *zOut; /* Rendering buffer */
+ int nOut; /* Size of the rendering buffer */
+ char *zExtra = 0; /* Malloced memory used by some conversion */
#ifndef SQLITE_OMIT_FLOATING_POINT
int exp, e2; /* exponent of real numbers */
+ int nsd; /* Number of significant digits returned */
double rounder; /* Used for rounding floating point values */
etByte flag_dp; /* True if decimal point should be shown */
etByte flag_rtz; /* True if trailing zeros should be removed */
- etByte flag_exp; /* True to force display of the exponent */
- int nsd; /* Number of significant digits returned */
#endif
+ PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
+ char buf[etBUFSIZE]; /* Conversion buffer */
- length = 0;
bufpt = 0;
+ if( pAccum->printfFlags ){
+ if( (bArgList = (pAccum->printfFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){
+ pArgList = va_arg(ap, PrintfArguments*);
+ }
+ useIntern = pAccum->printfFlags & SQLITE_PRINTF_INTERNAL;
+ }else{
+ bArgList = useIntern = 0;
+ }
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
- int amt;
bufpt = (char *)fmt;
- amt = 1;
- while( (c=(*++fmt))!='%' && c!=0 ) amt++;
- sqlite3StrAccumAppend(pAccum, bufpt, amt);
- if( c==0 ) break;
+#if HAVE_STRCHRNUL
+ fmt = strchrnul(fmt, '%');
+#else
+ do{ fmt++; }while( *fmt && *fmt != '%' );
+#endif
+ sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt));
+ if( *fmt==0 ) break;
}
if( (c=(*++fmt))==0 ){
sqlite3StrAccumAppend(pAccum, "%", 1);
@@ -19169,40 +24960,66 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}
}while( !done && (c=(*++fmt))!=0 );
/* Get the field width */
- width = 0;
if( c=='*' ){
- width = va_arg(ap,int);
+ if( bArgList ){
+ width = (int)getIntArg(pArgList);
+ }else{
+ width = va_arg(ap,int);
+ }
if( width<0 ){
flag_leftjustify = 1;
- width = -width;
+ width = width >= -2147483647 ? -width : 0;
}
c = *++fmt;
}else{
+ unsigned wx = 0;
while( c>='0' && c<='9' ){
- width = width*10 + c - '0';
+ wx = wx*10 + c - '0';
c = *++fmt;
}
+ testcase( wx>0x7fffffff );
+ width = wx & 0x7fffffff;
}
- if( width > etBUFSIZE-10 ){
- width = etBUFSIZE-10;
+ assert( width>=0 );
+#ifdef SQLITE_PRINTF_PRECISION_LIMIT
+ if( width>SQLITE_PRINTF_PRECISION_LIMIT ){
+ width = SQLITE_PRINTF_PRECISION_LIMIT;
}
+#endif
+
/* Get the precision */
if( c=='.' ){
- precision = 0;
c = *++fmt;
if( c=='*' ){
- precision = va_arg(ap,int);
- if( precision<0 ) precision = -precision;
+ if( bArgList ){
+ precision = (int)getIntArg(pArgList);
+ }else{
+ precision = va_arg(ap,int);
+ }
c = *++fmt;
+ if( precision<0 ){
+ precision = precision >= -2147483647 ? -precision : -1;
+ }
}else{
+ unsigned px = 0;
while( c>='0' && c<='9' ){
- precision = precision*10 + c - '0';
+ px = px*10 + c - '0';
c = *++fmt;
}
+ testcase( px>0x7fffffff );
+ precision = px & 0x7fffffff;
}
}else{
precision = -1;
}
+ assert( precision>=(-1) );
+#ifdef SQLITE_PRINTF_PRECISION_LIMIT
+ if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){
+ precision = SQLITE_PRINTF_PRECISION_LIMIT;
+ }
+#endif
+
+
/* Get the conversion type modifier */
if( c=='l' ){
flag_long = 1;
@@ -19222,7 +25039,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
for(idx=0; idx<ArraySize(fmtinfo); idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
- if( useExtended || (infop->flags & FLAG_INTERN)==0 ){
+ if( useIntern || (infop->flags & FLAG_INTERN)==0 ){
xtype = infop->type;
}else{
return;
@@ -19230,13 +25047,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
break;
}
}
- zExtra = 0;
-
-
- /* Limit the precision to prevent overflowing buf[] during conversion */
- if( precision>etBUFSIZE-40 && (infop->flags & FLAG_STRING)==0 ){
- precision = etBUFSIZE-40;
- }
/*
** At this point, variables are initialized as follows:
@@ -19268,7 +25078,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
case etRADIX:
if( infop->flags & FLAG_SIGNED ){
i64 v;
- if( flag_longlong ){
+ if( bArgList ){
+ v = getIntArg(pArgList);
+ }else if( flag_longlong ){
v = va_arg(ap,i64);
}else if( flag_long ){
v = va_arg(ap,long int);
@@ -19289,7 +25101,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
else prefix = 0;
}
}else{
- if( flag_longlong ){
+ if( bArgList ){
+ longvalue = (u64)getIntArg(pArgList);
+ }else if( flag_longlong ){
longvalue = va_arg(ap,u64);
}else if( flag_long ){
longvalue = va_arg(ap,unsigned long int);
@@ -19302,28 +25116,36 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( flag_zeropad && precision<width-(prefix!=0) ){
precision = width-(prefix!=0);
}
- bufpt = &buf[etBUFSIZE-1];
+ if( precision<etBUFSIZE-10 ){
+ nOut = etBUFSIZE;
+ zOut = buf;
+ }else{
+ nOut = precision + 10;
+ zOut = zExtra = sqlite3Malloc( nOut );
+ if( zOut==0 ){
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
+ return;
+ }
+ }
+ bufpt = &zOut[nOut-1];
if( xtype==etORDINAL ){
static const char zOrd[] = "thstndrd";
int x = (int)(longvalue % 10);
if( x>=4 || (longvalue/10)%10==1 ){
x = 0;
}
- buf[etBUFSIZE-3] = zOrd[x*2];
- buf[etBUFSIZE-2] = zOrd[x*2+1];
- bufpt -= 2;
+ *(--bufpt) = zOrd[x*2+1];
+ *(--bufpt) = zOrd[x*2];
}
{
- register const char *cset; /* Use registers for speed */
- register int base;
- cset = &aDigits[infop->charset];
- base = infop->base;
+ const char *cset = &aDigits[infop->charset];
+ u8 base = infop->base;
do{ /* Convert to ascii */
*(--bufpt) = cset[longvalue%base];
longvalue = longvalue/base;
}while( longvalue>0 );
}
- length = (int)(&buf[etBUFSIZE-1]-bufpt);
+ length = (int)(&zOut[nOut-1]-bufpt);
for(idx=precision-length; idx>0; idx--){
*(--bufpt) = '0'; /* Zero pad */
}
@@ -19334,17 +25156,20 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
pre = &aPrefix[infop->prefix];
for(; (x=(*pre))!=0; pre++) *(--bufpt) = x;
}
- length = (int)(&buf[etBUFSIZE-1]-bufpt);
+ length = (int)(&zOut[nOut-1]-bufpt);
break;
case etFLOAT:
case etEXP:
case etGENERIC:
- realvalue = va_arg(ap,double);
+ if( bArgList ){
+ realvalue = getDoubleArg(pArgList);
+ }else{
+ realvalue = va_arg(ap,double);
+ }
#ifdef SQLITE_OMIT_FLOATING_POINT
length = 0;
#else
if( precision<0 ) precision = 6; /* Set default precision */
- if( precision>etBUFSIZE/2-10 ) precision = etBUFSIZE/2-10;
if( realvalue<0.0 ){
realvalue = -realvalue;
prefix = '-';
@@ -19354,13 +25179,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
else prefix = 0;
}
if( xtype==etGENERIC && precision>0 ) precision--;
-#if 0
- /* Rounding works like BSD when the constant 0.4999 is used. Wierd! */
- for(idx=precision, rounder=0.4999; idx>0; idx--, rounder*=0.1);
-#else
- /* It makes more sense to use 0.5 */
- for(idx=precision, rounder=0.5; idx>0; idx--, rounder*=0.1){}
-#endif
+ testcase( precision>0xfff );
+ for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){}
if( xtype==etFLOAT ) realvalue += rounder;
/* Normalize realvalue to within 10.0 > realvalue >= 1.0 */
exp = 0;
@@ -19370,20 +25190,18 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
break;
}
if( realvalue>0.0 ){
- while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
- while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
- while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+ LONGDOUBLE_TYPE scale = 1.0;
+ while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;}
+ while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; }
+ while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; }
+ realvalue /= scale;
while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; }
while( realvalue<1.0 ){ realvalue *= 10.0; exp--; }
if( exp>350 ){
- if( prefix=='-' ){
- bufpt = "-Inf";
- }else if( prefix=='+' ){
- bufpt = "+Inf";
- }else{
- bufpt = "Inf";
- }
- length = sqlite3Strlen30(bufpt);
+ bufpt = buf;
+ buf[0] = prefix;
+ memcpy(buf+(prefix!=0),"Inf",4);
+ length = 3+(prefix!=0);
break;
}
}
@@ -19392,7 +25210,6 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
** If the field type is etGENERIC, then convert to either etEXP
** or etFLOAT, as appropriate.
*/
- flag_exp = xtype==etEXP;
if( xtype!=etFLOAT ){
realvalue += rounder;
if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; }
@@ -19406,14 +25223,23 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
xtype = etFLOAT;
}
}else{
- flag_rtz = 0;
+ flag_rtz = flag_altform2;
}
if( xtype==etEXP ){
e2 = 0;
}else{
e2 = exp;
}
- nsd = 0;
+ if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){
+ bufpt = zExtra
+ = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 );
+ if( bufpt==0 ){
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
+ return;
+ }
+ }
+ zOut = bufpt;
+ nsd = 16 + flag_altform2*10;
flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2;
/* The sign in front of the number */
if( prefix ){
@@ -19444,7 +25270,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
/* Remove trailing zeros and the "." if no digits follow the "." */
if( flag_rtz && flag_dp ){
while( bufpt[-1]=='0' ) *(--bufpt) = 0;
- assert( bufpt>buf );
+ assert( bufpt>zOut );
if( bufpt[-1]=='.' ){
if( flag_altform2 ){
*(bufpt++) = '0';
@@ -19454,7 +25280,7 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}
}
/* Add the "eNNN" suffix */
- if( flag_exp || xtype==etEXP ){
+ if( xtype==etEXP ){
*(bufpt++) = aDigits[infop->charset];
if( exp<0 ){
*(bufpt++) = '-'; exp = -exp;
@@ -19473,8 +25299,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
/* The converted number is in buf[] and zero terminated. Output it.
** Note that the number is in the usual order, not reversed as with
** integer conversions. */
- length = (int)(bufpt-buf);
- bufpt = buf;
+ length = (int)(bufpt-zOut);
+ bufpt = zOut;
/* Special case: Add leading zeros if the flag_zeropad flag is
** set and we are not left justified */
@@ -19491,7 +25317,9 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */
break;
case etSIZE:
- *(va_arg(ap,int*)) = pAccum->nChar;
+ if( !bArgList ){
+ *(va_arg(ap,int*)) = pAccum->nChar;
+ }
length = width = 0;
break;
case etPERCENT:
@@ -19500,19 +25328,32 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
length = 1;
break;
case etCHARX:
- c = va_arg(ap,int);
- buf[0] = (char)c;
- if( precision>=0 ){
- for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
- length = precision;
+ if( bArgList ){
+ bufpt = getTextArg(pArgList);
+ c = bufpt ? bufpt[0] : 0;
}else{
- length =1;
+ c = va_arg(ap,int);
+ }
+ if( precision>1 ){
+ width -= precision-1;
+ if( width>1 && !flag_leftjustify ){
+ sqlite3AppendChar(pAccum, width-1, ' ');
+ width = 0;
+ }
+ sqlite3AppendChar(pAccum, precision-1, c);
}
+ length = 1;
+ buf[0] = c;
bufpt = buf;
break;
case etSTRING:
case etDYNSTRING:
- bufpt = va_arg(ap,char*);
+ if( bArgList ){
+ bufpt = getTextArg(pArgList);
+ xtype = etSTRING;
+ }else{
+ bufpt = va_arg(ap,char*);
+ }
if( bufpt==0 ){
bufpt = "";
}else if( xtype==etDYNSTRING ){
@@ -19524,14 +25365,20 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
length = sqlite3Strlen30(bufpt);
}
break;
- case etSQLESCAPE:
- case etSQLESCAPE2:
- case etSQLESCAPE3: {
+ case etSQLESCAPE: /* Escape ' characters */
+ case etSQLESCAPE2: /* Escape ' and enclose in '...' */
+ case etSQLESCAPE3: { /* Escape " characters */
int i, j, k, n, isnull;
int needQuote;
char ch;
char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
- char *escarg = va_arg(ap,char*);
+ char *escarg;
+
+ if( bArgList ){
+ escarg = getTextArg(pArgList);
+ }else{
+ escarg = va_arg(ap,char*);
+ }
isnull = escarg==0;
if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
k = precision;
@@ -19539,11 +25386,11 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
if( ch==q ) n++;
}
needQuote = !isnull && xtype==etSQLESCAPE2;
- n += i + 1 + needQuote*2;
+ n += i + 3;
if( n>etBUFSIZE ){
bufpt = zExtra = sqlite3Malloc( n );
if( bufpt==0 ){
- pAccum->mallocFailed = 1;
+ setStrAccumError(pAccum, STRACCUM_NOMEM);
return;
}
}else{
@@ -19566,7 +25413,8 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
}
case etTOKEN: {
Token *pToken = va_arg(ap, Token*);
- if( pToken ){
+ assert( bArgList==0 );
+ if( pToken && pToken->n ){
sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
}
length = width = 0;
@@ -19576,12 +25424,13 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
SrcList *pSrc = va_arg(ap, SrcList*);
int k = va_arg(ap, int);
struct SrcList_item *pItem = &pSrc->a[k];
+ assert( bArgList==0 );
assert( k>=0 && k<pSrc->nSrc );
if( pItem->zDatabase ){
- sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
+ sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase);
sqlite3StrAccumAppend(pAccum, ".", 1);
}
- sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
+ sqlite3StrAccumAppendAll(pAccum, pItem->zName);
length = width = 0;
break;
}
@@ -19595,101 +25444,145 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
** "length" characters long. The field width is "width". Do
** the output.
*/
- if( !flag_leftjustify ){
- register int nspace;
- nspace = width-length;
- if( nspace>0 ){
- appendSpace(pAccum, nspace);
- }
- }
- if( length>0 ){
- sqlite3StrAccumAppend(pAccum, bufpt, length);
- }
- if( flag_leftjustify ){
- register int nspace;
- nspace = width-length;
- if( nspace>0 ){
- appendSpace(pAccum, nspace);
- }
- }
+ width -= length;
+ if( width>0 && !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
+ sqlite3StrAccumAppend(pAccum, bufpt, length);
+ if( width>0 && flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
+
if( zExtra ){
- sqlite3_free(zExtra);
+ sqlite3DbFree(pAccum->db, zExtra);
+ zExtra = 0;
}
}/* End for loop over the format string */
} /* End of function */
/*
-** Append N bytes of text from z to the StrAccum object.
+** Enlarge the memory allocation on a StrAccum object so that it is
+** able to accept at least N more bytes of text.
+**
+** Return the number of bytes of text that StrAccum is able to accept
+** after the attempted enlargement. The value returned might be zero.
*/
-SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
- assert( z!=0 || N==0 );
- if( p->tooBig | p->mallocFailed ){
- testcase(p->tooBig);
- testcase(p->mallocFailed);
- return;
+static int sqlite3StrAccumEnlarge(StrAccum *p, int N){
+ char *zNew;
+ assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */
+ if( p->accError ){
+ testcase(p->accError==STRACCUM_TOOBIG);
+ testcase(p->accError==STRACCUM_NOMEM);
+ return 0;
}
- if( N<0 ){
- N = sqlite3Strlen30(z);
+ if( p->mxAlloc==0 ){
+ N = p->nAlloc - p->nChar - 1;
+ setStrAccumError(p, STRACCUM_TOOBIG);
+ return N;
+ }else{
+ char *zOld = isMalloced(p) ? p->zText : 0;
+ i64 szNew = p->nChar;
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
+ szNew += N + 1;
+ if( szNew+p->nChar<=p->mxAlloc ){
+ /* Force exponential buffer size growth as long as it does not overflow,
+ ** to avoid having to call this routine too often */
+ szNew += p->nChar;
+ }
+ if( szNew > p->mxAlloc ){
+ sqlite3StrAccumReset(p);
+ setStrAccumError(p, STRACCUM_TOOBIG);
+ return 0;
+ }else{
+ p->nAlloc = (int)szNew;
+ }
+ if( p->db ){
+ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
+ }else{
+ zNew = sqlite3_realloc64(zOld, p->nAlloc);
+ }
+ if( zNew ){
+ assert( p->zText!=0 || p->nChar==0 );
+ if( !isMalloced(p) && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
+ p->zText = zNew;
+ p->nAlloc = sqlite3DbMallocSize(p->db, zNew);
+ p->printfFlags |= SQLITE_PRINTF_MALLOCED;
+ }else{
+ sqlite3StrAccumReset(p);
+ setStrAccumError(p, STRACCUM_NOMEM);
+ return 0;
+ }
}
- if( N==0 || NEVER(z==0) ){
+ return N;
+}
+
+/*
+** Append N copies of character c to the given string buffer.
+*/
+SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){
+ testcase( p->nChar + (i64)N > 0x7fffffff );
+ if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){
return;
}
+ assert( (p->zText==p->zBase)==!isMalloced(p) );
+ while( (N--)>0 ) p->zText[p->nChar++] = c;
+}
+
+/*
+** The StrAccum "p" is not large enough to accept N new bytes of z[].
+** So enlarge if first, then do the append.
+**
+** This is a helper routine to sqlite3StrAccumAppend() that does special-case
+** work (enlarging the buffer) using tail recursion, so that the
+** sqlite3StrAccumAppend() routine can use fast calling semantics.
+*/
+static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){
+ N = sqlite3StrAccumEnlarge(p, N);
+ if( N>0 ){
+ memcpy(&p->zText[p->nChar], z, N);
+ p->nChar += N;
+ }
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
+}
+
+/*
+** Append N bytes of text from z to the StrAccum object. Increase the
+** size of the memory allocation for StrAccum if necessary.
+*/
+SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
+ assert( z!=0 || N==0 );
+ assert( p->zText!=0 || p->nChar==0 || p->accError );
+ assert( N>=0 );
+ assert( p->accError==0 || p->nAlloc==0 );
if( p->nChar+N >= p->nAlloc ){
- char *zNew;
- if( !p->useMalloc ){
- p->tooBig = 1;
- N = p->nAlloc - p->nChar - 1;
- if( N<=0 ){
- return;
- }
- }else{
- char *zOld = (p->zText==p->zBase ? 0 : p->zText);
- i64 szNew = p->nChar;
- szNew += N + 1;
- if( szNew > p->mxAlloc ){
- sqlite3StrAccumReset(p);
- p->tooBig = 1;
- return;
- }else{
- p->nAlloc = (int)szNew;
- }
- if( p->useMalloc==1 ){
- zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
- }else{
- zNew = sqlite3_realloc(zOld, p->nAlloc);
- }
- if( zNew ){
- if( zOld==0 ) memcpy(zNew, p->zText, p->nChar);
- p->zText = zNew;
- }else{
- p->mallocFailed = 1;
- sqlite3StrAccumReset(p);
- return;
- }
- }
+ enlargeAndAppend(p,z,N);
+ }else{
+ assert( p->zText );
+ p->nChar += N;
+ memcpy(&p->zText[p->nChar-N], z, N);
}
- memcpy(&p->zText[p->nChar], z, N);
- p->nChar += N;
}
/*
+** Append the complete text of zero-terminated string z[] to the p string.
+*/
+SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){
+ sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z));
+}
+
+
+/*
** Finish off a string by making sure it is zero-terminated.
** Return a pointer to the resulting string. Return a NULL
** pointer if any kind of error was encountered.
*/
SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
if( p->zText ){
+ assert( (p->zText==p->zBase)==!isMalloced(p) );
p->zText[p->nChar] = 0;
- if( p->useMalloc && p->zText==p->zBase ){
- if( p->useMalloc==1 ){
- p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
- }else{
- p->zText = sqlite3_malloc(p->nChar+1);
- }
+ if( p->mxAlloc>0 && !isMalloced(p) ){
+ p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
if( p->zText ){
memcpy(p->zText, p->zBase, p->nChar+1);
+ p->printfFlags |= SQLITE_PRINTF_MALLOCED;
}else{
- p->mallocFailed = 1;
+ setStrAccumError(p, STRACCUM_NOMEM);
}
}
}
@@ -19700,28 +25593,36 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
** Reset an StrAccum string. Reclaim all malloced memory.
*/
SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){
- if( p->zText!=p->zBase ){
- if( p->useMalloc==1 ){
- sqlite3DbFree(p->db, p->zText);
- }else{
- sqlite3_free(p->zText);
- }
+ assert( (p->zText==0 || p->zText==p->zBase)==!isMalloced(p) );
+ if( isMalloced(p) ){
+ sqlite3DbFree(p->db, p->zText);
+ p->printfFlags &= ~SQLITE_PRINTF_MALLOCED;
}
p->zText = 0;
}
/*
-** Initialize a string accumulator
+** Initialize a string accumulator.
+**
+** p: The accumulator to be initialized.
+** db: Pointer to a database connection. May be NULL. Lookaside
+** memory is used if not NULL. db->mallocFailed is set appropriately
+** when not NULL.
+** zBase: An initial buffer. May be NULL in which case the initial buffer
+** is malloced.
+** n: Size of zBase in bytes. If total space requirements never exceed
+** n then no memory allocations ever occur.
+** mx: Maximum number of bytes to accumulate. If mx==0 then no memory
+** allocations will ever occur.
*/
-SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){
+SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){
p->zText = p->zBase = zBase;
- p->db = 0;
+ p->db = db;
p->nChar = 0;
p->nAlloc = n;
p->mxAlloc = mx;
- p->useMalloc = 1;
- p->tooBig = 0;
- p->mallocFailed = 0;
+ p->accError = 0;
+ p->printfFlags = 0;
}
/*
@@ -19733,13 +25634,13 @@ SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list a
char zBase[SQLITE_PRINT_BUF_SIZE];
StrAccum acc;
assert( db!=0 );
- sqlite3StrAccumInit(&acc, zBase, sizeof(zBase),
+ sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
- acc.db = db;
- sqlite3VXPrintf(&acc, 1, zFormat, ap);
+ acc.printfFlags = SQLITE_PRINTF_INTERNAL;
+ sqlite3VXPrintf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
- if( acc.mallocFailed ){
- db->mallocFailed = 1;
+ if( acc.accError==STRACCUM_NOMEM ){
+ sqlite3OomFault(db);
}
return z;
}
@@ -19758,24 +25659,6 @@ SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){
}
/*
-** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting
-** the string and before returnning. This routine is intended to be used
-** to modify an existing string. For example:
-**
-** x = sqlite3MPrintf(db, x, "prefix %s suffix", x);
-**
-*/
-SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zFormat, ...){
- va_list ap;
- char *z;
- va_start(ap, zFormat);
- z = sqlite3VMPrintf(db, zFormat, ap);
- va_end(ap);
- sqlite3DbFree(db, zStr);
- return z;
-}
-
-/*
** Print into memory obtained from sqlite3_malloc(). Omit the internal
** %-conversion extensions.
*/
@@ -19783,12 +25666,18 @@ SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){
char *z;
char zBase[SQLITE_PRINT_BUF_SIZE];
StrAccum acc;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( zFormat==0 ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
- sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
- acc.useMalloc = 2;
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
+ sqlite3VXPrintf(&acc, zFormat, ap);
z = sqlite3StrAccumFinish(&acc);
return z;
}
@@ -19825,9 +25714,15 @@ SQLITE_API char *sqlite3_mprintf(const char *zFormat, ...){
SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
StrAccum acc;
if( n<=0 ) return zBuf;
- sqlite3StrAccumInit(&acc, zBuf, n, 0);
- acc.useMalloc = 0;
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( zBuf==0 || zFormat==0 ) {
+ (void)SQLITE_MISUSE_BKPT;
+ if( zBuf ) zBuf[0] = 0;
+ return zBuf;
+ }
+#endif
+ sqlite3StrAccumInit(&acc, 0, zBuf, n, 0);
+ sqlite3VXPrintf(&acc, zFormat, ap);
return sqlite3StrAccumFinish(&acc);
}
SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
@@ -19847,14 +25742,18 @@ SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
** sqlite3_log() must render into a static buffer. It cannot dynamically
** allocate memory because it might be called while the memory allocator
** mutex is held.
+**
+** sqlite3VXPrintf() might ask for *temporary* memory allocations for
+** certain format characters (%q) or for very large precisions or widths.
+** Care must be taken that any sqlite3_log() calls that occur while the
+** memory mutex is held do not use these mechanisms.
*/
static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
StrAccum acc; /* String accumulator */
char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
- sqlite3StrAccumInit(&acc, zMsg, sizeof(zMsg), 0);
- acc.useMalloc = 0;
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
+ sqlite3VXPrintf(&acc, zFormat, ap);
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
sqlite3StrAccumFinish(&acc));
}
@@ -19871,7 +25770,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...){
}
}
-#if defined(SQLITE_DEBUG)
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
/*
** A version of printf() that understands %lld. Used for debugging.
** The printf() built into some versions of windows does not understand %lld
@@ -19881,10 +25780,9 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
va_list ap;
StrAccum acc;
char zBuf[500];
- sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0);
- acc.useMalloc = 0;
+ sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
va_start(ap,zFormat);
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
+ sqlite3VXPrintf(&acc, zFormat, ap);
va_end(ap);
sqlite3StrAccumFinish(&acc);
fprintf(stdout,"%s", zBuf);
@@ -19892,19 +25790,538 @@ SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
}
#endif
-#ifndef SQLITE_OMIT_TRACE
+
/*
-** variable-argument wrapper around sqlite3VXPrintf().
+** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument
+** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats.
*/
SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
va_list ap;
va_start(ap,zFormat);
- sqlite3VXPrintf(p, 1, zFormat, ap);
+ sqlite3VXPrintf(p, zFormat, ap);
va_end(ap);
}
-#endif
/************** End of printf.c **********************************************/
+/************** Begin file treeview.c ****************************************/
+/*
+** 2015-06-08
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains C code to implement the TreeView debugging routines.
+** These routines print a parse tree to standard output for debugging and
+** analysis.
+**
+** The interfaces in this file is only available when compiling
+** with SQLITE_DEBUG.
+*/
+/* #include "sqliteInt.h" */
+#ifdef SQLITE_DEBUG
+
+/*
+** Add a new subitem to the tree. The moreToFollow flag indicates that this
+** is not the last item in the tree.
+*/
+static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){
+ if( p==0 ){
+ p = sqlite3_malloc64( sizeof(*p) );
+ if( p==0 ) return 0;
+ memset(p, 0, sizeof(*p));
+ }else{
+ p->iLevel++;
+ }
+ assert( moreToFollow==0 || moreToFollow==1 );
+ if( p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow;
+ return p;
+}
+
+/*
+** Finished with one layer of the tree
+*/
+static void sqlite3TreeViewPop(TreeView *p){
+ if( p==0 ) return;
+ p->iLevel--;
+ if( p->iLevel<0 ) sqlite3_free(p);
+}
+
+/*
+** Generate a single line of output for the tree, with a prefix that contains
+** all the appropriate tree lines
+*/
+static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){
+ va_list ap;
+ int i;
+ StrAccum acc;
+ char zBuf[500];
+ sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0);
+ if( p ){
+ for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){
+ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4);
+ }
+ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4);
+ }
+ va_start(ap, zFormat);
+ sqlite3VXPrintf(&acc, zFormat, ap);
+ va_end(ap);
+ if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1);
+ sqlite3StrAccumFinish(&acc);
+ fprintf(stdout,"%s", zBuf);
+ fflush(stdout);
+}
+
+/*
+** Shorthand for starting a new tree item that consists of a single label
+*/
+static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){
+ p = sqlite3TreeViewPush(p, moreFollows);
+ sqlite3TreeViewLine(p, "%s", zLabel);
+}
+
+/*
+** Generate a human-readable description of a WITH clause.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){
+ int i;
+ if( pWith==0 ) return;
+ if( pWith->nCte==0 ) return;
+ if( pWith->pOuter ){
+ sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",pWith,pWith->pOuter);
+ }else{
+ sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith);
+ }
+ if( pWith->nCte>0 ){
+ pView = sqlite3TreeViewPush(pView, 1);
+ for(i=0; i<pWith->nCte; i++){
+ StrAccum x;
+ char zLine[1000];
+ const struct Cte *pCte = &pWith->a[i];
+ sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
+ sqlite3XPrintf(&x, "%s", pCte->zName);
+ if( pCte->pCols && pCte->pCols->nExpr>0 ){
+ char cSep = '(';
+ int j;
+ for(j=0; j<pCte->pCols->nExpr; j++){
+ sqlite3XPrintf(&x, "%c%s", cSep, pCte->pCols->a[j].zName);
+ cSep = ',';
+ }
+ sqlite3XPrintf(&x, ")");
+ }
+ sqlite3XPrintf(&x, " AS");
+ sqlite3StrAccumFinish(&x);
+ sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1);
+ sqlite3TreeViewSelect(pView, pCte->pSelect, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ sqlite3TreeViewPop(pView);
+ }
+}
+
+
+/*
+** Generate a human-readable description of a Select object.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
+ int n = 0;
+ int cnt = 0;
+ pView = sqlite3TreeViewPush(pView, moreToFollow);
+ if( p->pWith ){
+ sqlite3TreeViewWith(pView, p->pWith, 1);
+ cnt = 1;
+ sqlite3TreeViewPush(pView, 1);
+ }
+ do{
+ sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x nSelectRow=%d",
+ ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
+ ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags,
+ (int)p->nSelectRow
+ );
+ if( cnt++ ) sqlite3TreeViewPop(pView);
+ if( p->pPrior ){
+ n = 1000;
+ }else{
+ n = 0;
+ if( p->pSrc && p->pSrc->nSrc ) n++;
+ if( p->pWhere ) n++;
+ if( p->pGroupBy ) n++;
+ if( p->pHaving ) n++;
+ if( p->pOrderBy ) n++;
+ if( p->pLimit ) n++;
+ if( p->pOffset ) n++;
+ }
+ sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set");
+ if( p->pSrc && p->pSrc->nSrc ){
+ int i;
+ pView = sqlite3TreeViewPush(pView, (n--)>0);
+ sqlite3TreeViewLine(pView, "FROM");
+ for(i=0; i<p->pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &p->pSrc->a[i];
+ StrAccum x;
+ char zLine[100];
+ sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0);
+ sqlite3XPrintf(&x, "{%d,*}", pItem->iCursor);
+ if( pItem->zDatabase ){
+ sqlite3XPrintf(&x, " %s.%s", pItem->zDatabase, pItem->zName);
+ }else if( pItem->zName ){
+ sqlite3XPrintf(&x, " %s", pItem->zName);
+ }
+ if( pItem->pTab ){
+ sqlite3XPrintf(&x, " tabname=%Q", pItem->pTab->zName);
+ }
+ if( pItem->zAlias ){
+ sqlite3XPrintf(&x, " (AS %s)", pItem->zAlias);
+ }
+ if( pItem->fg.jointype & JT_LEFT ){
+ sqlite3XPrintf(&x, " LEFT-JOIN");
+ }
+ sqlite3StrAccumFinish(&x);
+ sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1);
+ if( pItem->pSelect ){
+ sqlite3TreeViewSelect(pView, pItem->pSelect, 0);
+ }
+ if( pItem->fg.isTabFunc ){
+ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:");
+ }
+ sqlite3TreeViewPop(pView);
+ }
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pWhere ){
+ sqlite3TreeViewItem(pView, "WHERE", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pWhere, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pGroupBy ){
+ sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY");
+ }
+ if( p->pHaving ){
+ sqlite3TreeViewItem(pView, "HAVING", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pHaving, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pOrderBy ){
+ sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY");
+ }
+ if( p->pLimit ){
+ sqlite3TreeViewItem(pView, "LIMIT", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pLimit, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pOffset ){
+ sqlite3TreeViewItem(pView, "OFFSET", (n--)>0);
+ sqlite3TreeViewExpr(pView, p->pOffset, 0);
+ sqlite3TreeViewPop(pView);
+ }
+ if( p->pPrior ){
+ const char *zOp = "UNION";
+ switch( p->op ){
+ case TK_ALL: zOp = "UNION ALL"; break;
+ case TK_INTERSECT: zOp = "INTERSECT"; break;
+ case TK_EXCEPT: zOp = "EXCEPT"; break;
+ }
+ sqlite3TreeViewItem(pView, zOp, 1);
+ }
+ p = p->pPrior;
+ }while( p!=0 );
+ sqlite3TreeViewPop(pView);
+}
+
+/*
+** Generate a human-readable explanation of an expression tree.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){
+ const char *zBinOp = 0; /* Binary operator */
+ const char *zUniOp = 0; /* Unary operator */
+ char zFlgs[30];
+ pView = sqlite3TreeViewPush(pView, moreToFollow);
+ if( pExpr==0 ){
+ sqlite3TreeViewLine(pView, "nil");
+ sqlite3TreeViewPop(pView);
+ return;
+ }
+ if( pExpr->flags ){
+ sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags);
+ }else{
+ zFlgs[0] = 0;
+ }
+ switch( pExpr->op ){
+ case TK_AGG_COLUMN: {
+ sqlite3TreeViewLine(pView, "AGG{%d:%d}%s",
+ pExpr->iTable, pExpr->iColumn, zFlgs);
+ break;
+ }
+ case TK_COLUMN: {
+ if( pExpr->iTable<0 ){
+ /* This only happens when coding check constraints */
+ sqlite3TreeViewLine(pView, "COLUMN(%d)%s", pExpr->iColumn, zFlgs);
+ }else{
+ sqlite3TreeViewLine(pView, "{%d:%d}%s",
+ pExpr->iTable, pExpr->iColumn, zFlgs);
+ }
+ break;
+ }
+ case TK_INTEGER: {
+ if( pExpr->flags & EP_IntValue ){
+ sqlite3TreeViewLine(pView, "%d", pExpr->u.iValue);
+ }else{
+ sqlite3TreeViewLine(pView, "%s", pExpr->u.zToken);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_FLOATING_POINT
+ case TK_FLOAT: {
+ sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_STRING: {
+ sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken);
+ break;
+ }
+ case TK_NULL: {
+ sqlite3TreeViewLine(pView,"NULL");
+ break;
+ }
+#ifndef SQLITE_OMIT_BLOB_LITERAL
+ case TK_BLOB: {
+ sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_VARIABLE: {
+ sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)",
+ pExpr->u.zToken, pExpr->iColumn);
+ break;
+ }
+ case TK_REGISTER: {
+ sqlite3TreeViewLine(pView,"REGISTER(%d)", pExpr->iTable);
+ break;
+ }
+ case TK_ID: {
+ sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken);
+ break;
+ }
+#ifndef SQLITE_OMIT_CAST
+ case TK_CAST: {
+ /* Expressions of the form: CAST(pLeft AS token) */
+ sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
+#endif /* SQLITE_OMIT_CAST */
+ case TK_LT: zBinOp = "LT"; break;
+ case TK_LE: zBinOp = "LE"; break;
+ case TK_GT: zBinOp = "GT"; break;
+ case TK_GE: zBinOp = "GE"; break;
+ case TK_NE: zBinOp = "NE"; break;
+ case TK_EQ: zBinOp = "EQ"; break;
+ case TK_IS: zBinOp = "IS"; break;
+ case TK_ISNOT: zBinOp = "ISNOT"; break;
+ case TK_AND: zBinOp = "AND"; break;
+ case TK_OR: zBinOp = "OR"; break;
+ case TK_PLUS: zBinOp = "ADD"; break;
+ case TK_STAR: zBinOp = "MUL"; break;
+ case TK_MINUS: zBinOp = "SUB"; break;
+ case TK_REM: zBinOp = "REM"; break;
+ case TK_BITAND: zBinOp = "BITAND"; break;
+ case TK_BITOR: zBinOp = "BITOR"; break;
+ case TK_SLASH: zBinOp = "DIV"; break;
+ case TK_LSHIFT: zBinOp = "LSHIFT"; break;
+ case TK_RSHIFT: zBinOp = "RSHIFT"; break;
+ case TK_CONCAT: zBinOp = "CONCAT"; break;
+ case TK_DOT: zBinOp = "DOT"; break;
+
+ case TK_UMINUS: zUniOp = "UMINUS"; break;
+ case TK_UPLUS: zUniOp = "UPLUS"; break;
+ case TK_BITNOT: zUniOp = "BITNOT"; break;
+ case TK_NOT: zUniOp = "NOT"; break;
+ case TK_ISNULL: zUniOp = "ISNULL"; break;
+ case TK_NOTNULL: zUniOp = "NOTNULL"; break;
+
+ case TK_SPAN: {
+ sqlite3TreeViewLine(pView, "SPAN %Q", pExpr->u.zToken);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
+
+ case TK_COLLATE: {
+ sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ break;
+ }
+
+ case TK_AGG_FUNCTION:
+ case TK_FUNCTION: {
+ ExprList *pFarg; /* List of function arguments */
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
+ pFarg = 0;
+ }else{
+ pFarg = pExpr->x.pList;
+ }
+ if( pExpr->op==TK_AGG_FUNCTION ){
+ sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q",
+ pExpr->op2, pExpr->u.zToken);
+ }else{
+ sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken);
+ }
+ if( pFarg ){
+ sqlite3TreeViewExprList(pView, pFarg, 0, 0);
+ }
+ break;
+ }
+#ifndef SQLITE_OMIT_SUBQUERY
+ case TK_EXISTS: {
+ sqlite3TreeViewLine(pView, "EXISTS-expr");
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
+ break;
+ }
+ case TK_SELECT: {
+ sqlite3TreeViewLine(pView, "SELECT-expr");
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
+ break;
+ }
+ case TK_IN: {
+ sqlite3TreeViewLine(pView, "IN");
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0);
+ }else{
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
+ }
+ break;
+ }
+#endif /* SQLITE_OMIT_SUBQUERY */
+
+ /*
+ ** x BETWEEN y AND z
+ **
+ ** This is equivalent to
+ **
+ ** x>=y AND x<=z
+ **
+ ** X is stored in pExpr->pLeft.
+ ** Y is stored in pExpr->pList->a[0].pExpr.
+ ** Z is stored in pExpr->pList->a[1].pExpr.
+ */
+ case TK_BETWEEN: {
+ Expr *pX = pExpr->pLeft;
+ Expr *pY = pExpr->x.pList->a[0].pExpr;
+ Expr *pZ = pExpr->x.pList->a[1].pExpr;
+ sqlite3TreeViewLine(pView, "BETWEEN");
+ sqlite3TreeViewExpr(pView, pX, 1);
+ sqlite3TreeViewExpr(pView, pY, 1);
+ sqlite3TreeViewExpr(pView, pZ, 0);
+ break;
+ }
+ case TK_TRIGGER: {
+ /* If the opcode is TK_TRIGGER, then the expression is a reference
+ ** to a column in the new.* or old.* pseudo-tables available to
+ ** trigger programs. In this case Expr.iTable is set to 1 for the
+ ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
+ ** is set to the column of the pseudo-table to read, or to -1 to
+ ** read the rowid field.
+ */
+ sqlite3TreeViewLine(pView, "%s(%d)",
+ pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
+ break;
+ }
+ case TK_CASE: {
+ sqlite3TreeViewLine(pView, "CASE");
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0);
+ break;
+ }
+#ifndef SQLITE_OMIT_TRIGGER
+ case TK_RAISE: {
+ const char *zType = "unk";
+ switch( pExpr->affinity ){
+ case OE_Rollback: zType = "rollback"; break;
+ case OE_Abort: zType = "abort"; break;
+ case OE_Fail: zType = "fail"; break;
+ case OE_Ignore: zType = "ignore"; break;
+ }
+ sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken);
+ break;
+ }
+#endif
+ case TK_MATCH: {
+ sqlite3TreeViewLine(pView, "MATCH {%d:%d}%s",
+ pExpr->iTable, pExpr->iColumn, zFlgs);
+ sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
+ break;
+ }
+ case TK_VECTOR: {
+ sqlite3TreeViewBareExprList(pView, pExpr->x.pList, "VECTOR");
+ break;
+ }
+ case TK_SELECT_COLUMN: {
+ sqlite3TreeViewLine(pView, "SELECT-COLUMN %d", pExpr->iColumn);
+ sqlite3TreeViewSelect(pView, pExpr->pLeft->x.pSelect, 0);
+ break;
+ }
+ default: {
+ sqlite3TreeViewLine(pView, "op=%d", pExpr->op);
+ break;
+ }
+ }
+ if( zBinOp ){
+ sqlite3TreeViewLine(pView, "%s%s", zBinOp, zFlgs);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1);
+ sqlite3TreeViewExpr(pView, pExpr->pRight, 0);
+ }else if( zUniOp ){
+ sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs);
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0);
+ }
+ sqlite3TreeViewPop(pView);
+}
+
+
+/*
+** Generate a human-readable explanation of an expression list.
+*/
+SQLITE_PRIVATE void sqlite3TreeViewBareExprList(
+ TreeView *pView,
+ const ExprList *pList,
+ const char *zLabel
+){
+ if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST";
+ if( pList==0 ){
+ sqlite3TreeViewLine(pView, "%s (empty)", zLabel);
+ }else{
+ int i;
+ sqlite3TreeViewLine(pView, "%s", zLabel);
+ for(i=0; i<pList->nExpr; i++){
+ int j = pList->a[i].u.x.iOrderByCol;
+ if( j ){
+ sqlite3TreeViewPush(pView, 0);
+ sqlite3TreeViewLine(pView, "iOrderByCol=%d", j);
+ }
+ sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1);
+ if( j ) sqlite3TreeViewPop(pView);
+ }
+ }
+}
+SQLITE_PRIVATE void sqlite3TreeViewExprList(
+ TreeView *pView,
+ const ExprList *pList,
+ u8 moreToFollow,
+ const char *zLabel
+){
+ pView = sqlite3TreeViewPush(pView, moreToFollow);
+ sqlite3TreeViewBareExprList(pView, pList, zLabel);
+ sqlite3TreeViewPop(pView);
+}
+
+#endif /* SQLITE_DEBUG */
+
+/************** End of treeview.c ********************************************/
/************** Begin file random.c ******************************************/
/*
** 2001 September 15
@@ -19923,6 +26340,7 @@ SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
** Random numbers are used by some of the database backends in order
** to generate random integer keys for tables or random filenames.
*/
+/* #include "sqliteInt.h" */
/* All threads share a single random number generator.
@@ -19935,24 +26353,11 @@ static SQLITE_WSD struct sqlite3PrngType {
} sqlite3Prng;
/*
-** Get a single 8-bit random value from the RC4 PRNG. The Mutex
-** must be held while executing this routine.
-**
-** Why not just use a library random generator like lrand48() for this?
-** Because the OP_NewRowid opcode in the VDBE depends on having a very
-** good source of random numbers. The lrand48() library function may
-** well be good enough. But maybe not. Or maybe lrand48() has some
-** subtle problems on some systems that could cause problems. It is hard
-** to know. To minimize the risk of problems due to bad lrand48()
-** implementations, SQLite uses this random number generator based
-** on RC4, which we know works very well.
-**
-** (Later): Actually, OP_NewRowid does not depend on a good source of
-** randomness any more. But we will leave this code in all the same.
+** Return N random bytes.
*/
-static u8 randomByte(void){
+SQLITE_API void sqlite3_randomness(int N, void *pBuf){
unsigned char t;
-
+ unsigned char *zBuf = pBuf;
/* The "wsdPrng" macro will resolve to the pseudo-random number generator
** state vector. If writable static data is unsupported on the target,
@@ -19967,6 +26372,24 @@ static u8 randomByte(void){
# define wsdPrng sqlite3Prng
#endif
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *mutex;
+#endif
+
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return;
+#endif
+
+#if SQLITE_THREADSAFE
+ mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
+#endif
+
+ sqlite3_mutex_enter(mutex);
+ if( N<=0 || pBuf==0 ){
+ wsdPrng.isInit = 0;
+ sqlite3_mutex_leave(mutex);
+ return;
+ }
/* Initialize the state of the random number generator once,
** the first time this routine is called. The seed value does
@@ -19995,29 +26418,16 @@ static u8 randomByte(void){
wsdPrng.isInit = 1;
}
- /* Generate and return single random byte
- */
- wsdPrng.i++;
- t = wsdPrng.s[wsdPrng.i];
- wsdPrng.j += t;
- wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
- wsdPrng.s[wsdPrng.j] = t;
- t += wsdPrng.s[wsdPrng.i];
- return wsdPrng.s[t];
-}
-
-/*
-** Return N random bytes.
-*/
-SQLITE_API void sqlite3_randomness(int N, void *pBuf){
- unsigned char *zBuf = pBuf;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
-#endif
- sqlite3_mutex_enter(mutex);
- while( N-- ){
- *(zBuf++) = randomByte();
- }
+ assert( N>0 );
+ do{
+ wsdPrng.i++;
+ t = wsdPrng.s[wsdPrng.i];
+ wsdPrng.j += t;
+ wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j];
+ wsdPrng.s[wsdPrng.j] = t;
+ t += wsdPrng.s[wsdPrng.i];
+ *(zBuf++) = wsdPrng.s[t];
+ }while( --N );
sqlite3_mutex_leave(mutex);
}
@@ -20046,12 +26456,286 @@ SQLITE_PRIVATE void sqlite3PrngRestoreState(void){
sizeof(sqlite3Prng)
);
}
-SQLITE_PRIVATE void sqlite3PrngResetState(void){
- GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
-}
#endif /* SQLITE_OMIT_BUILTIN_TEST */
/************** End of random.c **********************************************/
+/************** Begin file threads.c *****************************************/
+/*
+** 2012 July 21
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file presents a simple cross-platform threading interface for
+** use internally by SQLite.
+**
+** A "thread" can be created using sqlite3ThreadCreate(). This thread
+** runs independently of its creator until it is joined using
+** sqlite3ThreadJoin(), at which point it terminates.
+**
+** Threads do not have to be real. It could be that the work of the
+** "thread" is done by the main thread at either the sqlite3ThreadCreate()
+** or sqlite3ThreadJoin() call. This is, in fact, what happens in
+** single threaded systems. Nothing in SQLite requires multiple threads.
+** This interface exists so that applications that want to take advantage
+** of multiple cores can do so, while also allowing applications to stay
+** single-threaded if desired.
+*/
+/* #include "sqliteInt.h" */
+#if SQLITE_OS_WIN
+/* # include "os_win.h" */
+#endif
+
+#if SQLITE_MAX_WORKER_THREADS>0
+
+/********************************* Unix Pthreads ****************************/
+#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0
+
+#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
+/* #include <pthread.h> */
+
+/* A running thread */
+struct SQLiteThread {
+ pthread_t tid; /* Thread ID */
+ int done; /* Set to true when thread finishes */
+ void *pOut; /* Result returned by the thread */
+ void *(*xTask)(void*); /* The thread routine */
+ void *pIn; /* Argument to the thread */
+};
+
+/* Create a new thread */
+SQLITE_PRIVATE int sqlite3ThreadCreate(
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */
+ void *(*xTask)(void*), /* Routine to run in a separate thread */
+ void *pIn /* Argument passed into xTask() */
+){
+ SQLiteThread *p;
+ int rc;
+
+ assert( ppThread!=0 );
+ assert( xTask!=0 );
+ /* This routine is never used in single-threaded mode */
+ assert( sqlite3GlobalConfig.bCoreMutex!=0 );
+
+ *ppThread = 0;
+ p = sqlite3Malloc(sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
+ memset(p, 0, sizeof(*p));
+ p->xTask = xTask;
+ p->pIn = pIn;
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ ** function that returns SQLITE_ERROR when passed the argument 200, that
+ ** forces worker threads to run sequentially and deterministically
+ ** for testing purposes. */
+ if( sqlite3FaultSim(200) ){
+ rc = 1;
+ }else{
+ rc = pthread_create(&p->tid, 0, xTask, pIn);
+ }
+ if( rc ){
+ p->done = 1;
+ p->pOut = xTask(pIn);
+ }
+ *ppThread = p;
+ return SQLITE_OK;
+}
+
+/* Get the results of the thread */
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
+ int rc;
+
+ assert( ppOut!=0 );
+ if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
+ if( p->done ){
+ *ppOut = p->pOut;
+ rc = SQLITE_OK;
+ }else{
+ rc = pthread_join(p->tid, ppOut) ? SQLITE_ERROR : SQLITE_OK;
+ }
+ sqlite3_free(p);
+ return rc;
+}
+
+#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */
+/******************************** End Unix Pthreads *************************/
+
+
+/********************************* Win32 Threads ****************************/
+#if SQLITE_OS_WIN_THREADS
+
+#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */
+#include <process.h>
+
+/* A running thread */
+struct SQLiteThread {
+ void *tid; /* The thread handle */
+ unsigned id; /* The thread identifier */
+ void *(*xTask)(void*); /* The routine to run as a thread */
+ void *pIn; /* Argument to xTask */
+ void *pResult; /* Result of xTask */
+};
+
+/* Thread procedure Win32 compatibility shim */
+static unsigned __stdcall sqlite3ThreadProc(
+ void *pArg /* IN: Pointer to the SQLiteThread structure */
+){
+ SQLiteThread *p = (SQLiteThread *)pArg;
+
+ assert( p!=0 );
+#if 0
+ /*
+ ** This assert appears to trigger spuriously on certain
+ ** versions of Windows, possibly due to _beginthreadex()
+ ** and/or CreateThread() not fully setting their thread
+ ** ID parameter before starting the thread.
+ */
+ assert( p->id==GetCurrentThreadId() );
+#endif
+ assert( p->xTask!=0 );
+ p->pResult = p->xTask(p->pIn);
+
+ _endthreadex(0);
+ return 0; /* NOT REACHED */
+}
+
+/* Create a new thread */
+SQLITE_PRIVATE int sqlite3ThreadCreate(
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */
+ void *(*xTask)(void*), /* Routine to run in a separate thread */
+ void *pIn /* Argument passed into xTask() */
+){
+ SQLiteThread *p;
+
+ assert( ppThread!=0 );
+ assert( xTask!=0 );
+ *ppThread = 0;
+ p = sqlite3Malloc(sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a
+ ** function that returns SQLITE_ERROR when passed the argument 200, that
+ ** forces worker threads to run sequentially and deterministically
+ ** (via the sqlite3FaultSim() term of the conditional) for testing
+ ** purposes. */
+ if( sqlite3GlobalConfig.bCoreMutex==0 || sqlite3FaultSim(200) ){
+ memset(p, 0, sizeof(*p));
+ }else{
+ p->xTask = xTask;
+ p->pIn = pIn;
+ p->tid = (void*)_beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id);
+ if( p->tid==0 ){
+ memset(p, 0, sizeof(*p));
+ }
+ }
+ if( p->xTask==0 ){
+ p->id = GetCurrentThreadId();
+ p->pResult = xTask(pIn);
+ }
+ *ppThread = p;
+ return SQLITE_OK;
+}
+
+SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject); /* os_win.c */
+
+/* Get the results of the thread */
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
+ DWORD rc;
+ BOOL bRc;
+
+ assert( ppOut!=0 );
+ if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
+ if( p->xTask==0 ){
+ /* assert( p->id==GetCurrentThreadId() ); */
+ rc = WAIT_OBJECT_0;
+ assert( p->tid==0 );
+ }else{
+ assert( p->id!=0 && p->id!=GetCurrentThreadId() );
+ rc = sqlite3Win32Wait((HANDLE)p->tid);
+ assert( rc!=WAIT_IO_COMPLETION );
+ bRc = CloseHandle((HANDLE)p->tid);
+ assert( bRc );
+ }
+ if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult;
+ sqlite3_free(p);
+ return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR;
+}
+
+#endif /* SQLITE_OS_WIN_THREADS */
+/******************************** End Win32 Threads *************************/
+
+
+/********************************* Single-Threaded **************************/
+#ifndef SQLITE_THREADS_IMPLEMENTED
+/*
+** This implementation does not actually create a new thread. It does the
+** work of the thread in the main thread, when either the thread is created
+** or when it is joined
+*/
+
+/* A running thread */
+struct SQLiteThread {
+ void *(*xTask)(void*); /* The routine to run as a thread */
+ void *pIn; /* Argument to xTask */
+ void *pResult; /* Result of xTask */
+};
+
+/* Create a new thread */
+SQLITE_PRIVATE int sqlite3ThreadCreate(
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */
+ void *(*xTask)(void*), /* Routine to run in a separate thread */
+ void *pIn /* Argument passed into xTask() */
+){
+ SQLiteThread *p;
+
+ assert( ppThread!=0 );
+ assert( xTask!=0 );
+ *ppThread = 0;
+ p = sqlite3Malloc(sizeof(*p));
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
+ if( (SQLITE_PTR_TO_INT(p)/17)&1 ){
+ p->xTask = xTask;
+ p->pIn = pIn;
+ }else{
+ p->xTask = 0;
+ p->pResult = xTask(pIn);
+ }
+ *ppThread = p;
+ return SQLITE_OK;
+}
+
+/* Get the results of the thread */
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){
+
+ assert( ppOut!=0 );
+ if( NEVER(p==0) ) return SQLITE_NOMEM_BKPT;
+ if( p->xTask ){
+ *ppOut = p->xTask(p->pIn);
+ }else{
+ *ppOut = p->pResult;
+ }
+ sqlite3_free(p);
+
+#if defined(SQLITE_TEST)
+ {
+ void *pTstAlloc = sqlite3Malloc(10);
+ if (!pTstAlloc) return SQLITE_NOMEM_BKPT;
+ sqlite3_free(pTstAlloc);
+ }
+#endif
+
+ return SQLITE_OK;
+}
+
+#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */
+/****************************** End Single-Threaded *************************/
+#endif /* SQLITE_MAX_WORKER_THREADS>0 */
+
+/************** End of threads.c *********************************************/
/************** Begin file utf.c *********************************************/
/*
** 2004 April 13
@@ -20088,15 +26772,17 @@ SQLITE_PRIVATE void sqlite3PrngResetState(void){
** 0xfe 0xff big-endian utf-16 follows
**
*/
+/* #include "sqliteInt.h" */
/* #include <assert.h> */
+/* #include "vdbeInt.h" */
-#ifndef SQLITE_AMALGAMATION
+#if !defined(SQLITE_AMALGAMATION) && SQLITE_BYTEORDER==0
/*
** The following constant value is used by the SQLITE_BIGENDIAN and
** SQLITE_LITTLEENDIAN macros.
*/
SQLITE_PRIVATE const int sqlite3one = 1;
-#endif /* SQLITE_AMALGAMATION */
+#endif /* SQLITE_AMALGAMATION && SQLITE_BYTEORDER==0 */
/*
** This lookup table is used to help decode the first byte of
@@ -20201,8 +26887,8 @@ static const unsigned char sqlite3Utf8Trans1[] = {
** and rendered as themselves even though they are technically
** invalid characters.
**
-** * This routine accepts an infinite number of different UTF8 encodings
-** for unicode values 0x80 and greater. It do not change over-length
+** * This routine accepts over-length UTF8 encodings
+** for unicode values 0x80 and greater. It does not change over-length
** encodings to 0xfffd as some systems recommend.
*/
#define READ_UTF8(zIn, zTerm, c) \
@@ -20217,25 +26903,23 @@ static const unsigned char sqlite3Utf8Trans1[] = {
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
}
SQLITE_PRIVATE u32 sqlite3Utf8Read(
- const unsigned char *zIn, /* First byte of UTF-8 character */
- const unsigned char **pzNext /* Write first byte past UTF-8 char here */
+ const unsigned char **pz /* Pointer to string from which to read char */
){
unsigned int c;
/* Same as READ_UTF8() above but without the zTerm parameter.
** For this routine, we assume the UTF8 string is always zero-terminated.
*/
- c = *(zIn++);
+ c = *((*pz)++);
if( c>=0xc0 ){
c = sqlite3Utf8Trans1[c-0xc0];
- while( (*zIn & 0xc0)==0x80 ){
- c = (c<<6) + (0x3f & *(zIn++));
+ while( (*(*pz) & 0xc0)==0x80 ){
+ c = (c<<6) + (0x3f & *((*pz)++));
}
if( c<0x80
|| (c&0xFFFFF800)==0xD800
|| (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; }
}
- *pzNext = zIn;
return c;
}
@@ -20254,7 +26938,7 @@ SQLITE_PRIVATE u32 sqlite3Utf8Read(
** desiredEnc. It is an error if the string is already of the desired
** encoding, or if *pMem does not contain a string value.
*/
-SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
int len; /* Maximum length of output string in bytes */
unsigned char *zOut; /* Output buffer */
unsigned char *zIn; /* Input iterator */
@@ -20286,7 +26970,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
rc = sqlite3VdbeMemMakeWriteable(pMem);
if( rc!=SQLITE_OK ){
assert( rc==SQLITE_NOMEM );
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
zIn = (u8*)pMem->z;
zTerm = &zIn[pMem->n&~1];
@@ -20328,7 +27012,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
zTerm = &zIn[pMem->n];
zOut = sqlite3DbMallocRaw(pMem->db, len);
if( !zOut ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
z = zOut;
@@ -20336,7 +27020,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
if( desiredEnc==SQLITE_UTF16LE ){
/* UTF-8 -> UTF-16 Little-endian */
while( zIn<zTerm ){
- /* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
READ_UTF8(zIn, zTerm, c);
WRITE_UTF16LE(z, c);
}
@@ -20344,7 +27027,6 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
assert( desiredEnc==SQLITE_UTF16BE );
/* UTF-8 -> UTF-16 Big-endian */
while( zIn<zTerm ){
- /* c = sqlite3Utf8Read(zIn, zTerm, (const u8**)&zIn); */
READ_UTF8(zIn, zTerm, c);
WRITE_UTF16BE(z, c);
}
@@ -20371,12 +27053,13 @@ SQLITE_PRIVATE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){
*z = 0;
assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len );
+ c = pMem->flags;
sqlite3VdbeMemRelease(pMem);
- pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
+ pMem->flags = MEM_Str|MEM_Term|(c&(MEM_AffMask|MEM_Subtype));
pMem->enc = desiredEnc;
- pMem->flags |= (MEM_Term|MEM_Dyn);
pMem->z = (char*)zOut;
pMem->zMalloc = pMem->z;
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->z);
translate_out:
#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
@@ -20472,7 +27155,7 @@ SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char *zIn){
u32 c;
while( zIn[0] && zOut<=zIn ){
- c = sqlite3Utf8Read(zIn, (const u8**)&zIn);
+ c = sqlite3Utf8Read((const u8**)&zIn);
if( c!=0xfffd ){
WRITE_UTF8(zOut, c);
}
@@ -20502,38 +27185,11 @@ SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 e
}
assert( (m.flags & MEM_Term)!=0 || db->mallocFailed );
assert( (m.flags & MEM_Str)!=0 || db->mallocFailed );
- assert( (m.flags & MEM_Dyn)!=0 || db->mallocFailed );
assert( m.z || db->mallocFailed );
return m.z;
}
/*
-** Convert a UTF-8 string to the UTF-16 encoding specified by parameter
-** enc. A pointer to the new string is returned, and the value of *pnOut
-** is set to the length of the returned string in bytes. The call should
-** arrange to call sqlite3DbFree() on the returned pointer when it is
-** no longer required.
-**
-** If a malloc failure occurs, NULL is returned and the db.mallocFailed
-** flag set.
-*/
-#ifdef SQLITE_ENABLE_STAT2
-SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *db, u8 enc, char *z, int n, int *pnOut){
- Mem m;
- memset(&m, 0, sizeof(m));
- m.db = db;
- sqlite3VdbeMemSetStr(&m, z, n, SQLITE_UTF8, SQLITE_STATIC);
- if( sqlite3VdbeMemTranslate(&m, enc) ){
- assert( db->mallocFailed );
- return 0;
- }
- assert( m.z==m.zMalloc );
- *pnOut = m.n;
- return m.z;
-}
-#endif
-
-/*
** zIn is a UTF-16 encoded unicode string at least nChar characters long.
** Return the number of bytes in the first nChar unicode characters
** in pZ. nChar must be non-negative.
@@ -20577,7 +27233,7 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
assert( n>0 && n<=4 );
z[0] = 0;
z = zBuf;
- c = sqlite3Utf8Read(z, (const u8**)&z);
+ c = sqlite3Utf8Read((const u8**)&z);
t = i;
if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD;
if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD;
@@ -20631,8 +27287,9 @@ SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
** strings, and stuff like that.
**
*/
+/* #include "sqliteInt.h" */
/* #include <stdarg.h> */
-#ifdef SQLITE_HAVE_ISNAN
+#if HAVE_ISNAN || SQLITE_HAVE_ISNAN
# include <math.h>
#endif
@@ -20646,6 +27303,24 @@ SQLITE_PRIVATE void sqlite3Coverage(int x){
}
#endif
+/*
+** Give a callback to the test harness that can be used to simulate faults
+** in places where it is difficult or expensive to do so purely by means
+** of inputs.
+**
+** The intent of the integer argument is to let the fault simulator know
+** which of multiple sqlite3FaultSim() calls has been hit.
+**
+** Return whatever integer value the test callback returns, or return
+** SQLITE_OK if no test callback is installed.
+*/
+#ifndef SQLITE_OMIT_BUILTIN_TEST
+SQLITE_PRIVATE int sqlite3FaultSim(int iTest){
+ int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback;
+ return xCallback ? xCallback(iTest) : SQLITE_OK;
+}
+#endif
+
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Return true if the floating point value is Not a Number (NaN).
@@ -20655,7 +27330,7 @@ SQLITE_PRIVATE void sqlite3Coverage(int x){
*/
SQLITE_PRIVATE int sqlite3IsNaN(double x){
int rc; /* The value return */
-#if !defined(SQLITE_HAVE_ISNAN)
+#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN
/*
** Systems that support the isnan() library function should probably
** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have
@@ -20685,9 +27360,9 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){
volatile double y = x;
volatile double z = y;
rc = (y!=z);
-#else /* if defined(SQLITE_HAVE_ISNAN) */
+#else /* if HAVE_ISNAN */
rc = isnan(x);
-#endif /* SQLITE_HAVE_ISNAN */
+#endif /* HAVE_ISNAN */
testcase( rc );
return rc;
}
@@ -20702,10 +27377,53 @@ SQLITE_PRIVATE int sqlite3IsNaN(double x){
** than 1GiB) the value returned might be less than the true string length.
*/
SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
- const char *z2 = z;
if( z==0 ) return 0;
- while( *z2 ){ z2++; }
- return 0x3fffffff & (int)(z2 - z);
+ return 0x3fffffff & (int)strlen(z);
+}
+
+/*
+** Return the declared type of a column. Or return zDflt if the column
+** has no declared type.
+**
+** The column type is an extra string stored after the zero-terminator on
+** the column name if and only if the COLFLAG_HASTYPE flag is set.
+*/
+SQLITE_PRIVATE char *sqlite3ColumnType(Column *pCol, char *zDflt){
+ if( (pCol->colFlags & COLFLAG_HASTYPE)==0 ) return zDflt;
+ return pCol->zName + strlen(pCol->zName) + 1;
+}
+
+/*
+** Helper function for sqlite3Error() - called rarely. Broken out into
+** a separate routine to avoid unnecessary register saves on entry to
+** sqlite3Error().
+*/
+static SQLITE_NOINLINE void sqlite3ErrorFinish(sqlite3 *db, int err_code){
+ if( db->pErr ) sqlite3ValueSetNull(db->pErr);
+ sqlite3SystemError(db, err_code);
+}
+
+/*
+** Set the current error code to err_code and clear any prior error message.
+** Also set iSysErrno (by calling sqlite3System) if the err_code indicates
+** that would be appropriate.
+*/
+SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
+ assert( db!=0 );
+ db->errCode = err_code;
+ if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
+}
+
+/*
+** Load the sqlite3.iSysErrno field if that is an appropriate thing
+** to do based on the SQLite error code in rc.
+*/
+SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
+ if( rc==SQLITE_IOERR_NOMEM ) return;
+ rc &= 0xff;
+ if( rc==SQLITE_CANTOPEN || rc==SQLITE_IOERR ){
+ db->iSysErrno = sqlite3OsGetLastError(db->pVfs);
+ }
}
/*
@@ -20729,19 +27447,19 @@ SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
** should be called with err_code set to SQLITE_OK and zFormat set
** to NULL.
*/
-SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
- if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
- db->errCode = err_code;
- if( zFormat ){
- char *z;
- va_list ap;
- va_start(ap, zFormat);
- z = sqlite3VMPrintf(db, zFormat, ap);
- va_end(ap);
- sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
- }else{
- sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
- }
+SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *zFormat, ...){
+ assert( db!=0 );
+ db->errCode = err_code;
+ sqlite3SystemError(db, err_code);
+ if( zFormat==0 ){
+ sqlite3Error(db, err_code);
+ }else if( db->pErr || (db->pErr = sqlite3ValueNew(db))!=0 ){
+ char *z;
+ va_list ap;
+ va_start(ap, zFormat);
+ z = sqlite3VMPrintf(db, zFormat, ap);
+ va_end(ap);
+ sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC);
}
}
@@ -20755,12 +27473,12 @@ SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat,
** %T Insert a token
** %S Insert the first element of a SrcList
**
-** This function should be used to report any error that occurs whilst
+** This function should be used to report any error that occurs while
** compiling an SQL statement (i.e. within sqlite3_prepare()). The
** last thing the sqlite3_prepare() function does is copy the error
** stored by this function into the database handle using sqlite3Error().
-** Function sqlite3Error() should be used during statement execution
-** (sqlite3_step() etc.).
+** Functions sqlite3Error() or sqlite3ErrorWithMsg() should be used
+** during statement execution (sqlite3_step() etc.).
*/
SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
char *zMsg;
@@ -20793,22 +27511,18 @@ SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){
** occur.
**
** 2002-Feb-14: This routine is extended to remove MS-Access style
-** brackets from around identifers. For example: "[a-b-c]" becomes
+** brackets from around identifiers. For example: "[a-b-c]" becomes
** "a-b-c".
*/
-SQLITE_PRIVATE int sqlite3Dequote(char *z){
+SQLITE_PRIVATE void sqlite3Dequote(char *z){
char quote;
int i, j;
- if( z==0 ) return -1;
+ if( z==0 ) return;
quote = z[0];
- switch( quote ){
- case '\'': break;
- case '"': break;
- case '`': break; /* For MySQL compatibility */
- case '[': quote = ']'; break; /* For MS SqlServer compatibility */
- default: return -1;
- }
- for(i=1, j=0; ALWAYS(z[i]); i++){
+ if( !sqlite3Isquote(quote) ) return;
+ if( quote=='[' ) quote = ']';
+ for(i=1, j=0;; i++){
+ assert( z[i] );
if( z[i]==quote ){
if( z[i+1]==quote ){
z[j++] = quote;
@@ -20821,7 +27535,14 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){
}
}
z[j] = 0;
- return j;
+}
+
+/*
+** Generate a Token object from a string
+*/
+SQLITE_PRIVATE void sqlite3TokenInit(Token *p, char *z){
+ p->z = z;
+ p->n = sqlite3Strlen30(z);
}
/* Convenient short-hand */
@@ -20831,21 +27552,40 @@ SQLITE_PRIVATE int sqlite3Dequote(char *z){
** Some systems have stricmp(). Others have strcasecmp(). Because
** there is no consistency, we will define our own.
**
-** IMPLEMENTATION-OF: R-20522-24639 The sqlite3_strnicmp() API allows
-** applications and extensions to compare the contents of two buffers
-** containing UTF-8 strings in a case-independent fashion, using the same
-** definition of case independence that SQLite uses internally when
-** comparing identifiers.
+** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and
+** sqlite3_strnicmp() APIs allow applications and extensions to compare
+** the contents of two buffers containing UTF-8 strings in a
+** case-independent fashion, using the same definition of "case
+** independence" that SQLite uses internally when comparing identifiers.
*/
+SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){
+ if( zLeft==0 ){
+ return zRight ? -1 : 0;
+ }else if( zRight==0 ){
+ return 1;
+ }
+ return sqlite3StrICmp(zLeft, zRight);
+}
SQLITE_PRIVATE int sqlite3StrICmp(const char *zLeft, const char *zRight){
- register unsigned char *a, *b;
+ unsigned char *a, *b;
+ int c;
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
- while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
- return UpperToLower[*a] - UpperToLower[*b];
+ for(;;){
+ c = (int)UpperToLower[*a] - (int)UpperToLower[*b];
+ if( c || *a==0 ) break;
+ a++;
+ b++;
+ }
+ return c;
}
SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
register unsigned char *a, *b;
+ if( zLeft==0 ){
+ return zRight ? -1 : 0;
+ }else if( zRight==0 ){
+ return 1;
+ }
a = (unsigned char *)zLeft;
b = (unsigned char *)zRight;
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
@@ -20876,7 +27616,7 @@ SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
*/
SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){
#ifndef SQLITE_OMIT_FLOATING_POINT
- int incr = (enc==SQLITE_UTF8?1:2);
+ int incr;
const char *zEnd = z + length;
/* sign * significand * (10 ^ (esign * exponent)) */
int sign = 1; /* sign of significand */
@@ -20887,10 +27627,22 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
int eValid = 1; /* True exponent is either not used or is well-formed */
double result;
int nDigits = 0;
+ int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
- if( enc==SQLITE_UTF16BE ) z++;
+ if( enc==SQLITE_UTF8 ){
+ incr = 1;
+ }else{
+ int i;
+ incr = 2;
+ assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+ for(i=3-enc; i<length && z[i]==0; i+=2){}
+ nonNum = i<length;
+ zEnd = &z[i^1];
+ z += (enc&1);
+ }
/* skip leading spaces */
while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
@@ -20904,9 +27656,6 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
z+=incr;
}
- /* skip leading zeroes */
- while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++;
-
/* copy max significant digits to significand */
while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
s = s*10 + (*z - '0');
@@ -20923,12 +27672,13 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
z+=incr;
/* copy digits from after decimal to significand
** (decrease exponent by d to shift decimal right) */
- while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
- s = s*10 + (*z - '0');
- z+=incr, nDigits++, d--;
+ while( z<zEnd && sqlite3Isdigit(*z) ){
+ if( s<((LARGEST_INT64-9)/10) ){
+ s = s*10 + (*z - '0');
+ d--;
+ }
+ z+=incr, nDigits++;
}
- /* skip non-significant digits */
- while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++;
}
if( z>=zEnd ) goto do_atof_calc;
@@ -20936,7 +27686,12 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
if( *z=='e' || *z=='E' ){
z+=incr;
eValid = 0;
- if( z>=zEnd ) goto do_atof_calc;
+
+ /* This branch is needed to avoid a (harmless) buffer overread. The
+ ** special comment alerts the mutation tester that the correct answer
+ ** is obtained even if the branch is omitted */
+ if( z>=zEnd ) goto do_atof_calc; /*PREVENTS-HARMLESS-OVERREAD*/
+
/* get sign of exponent */
if( *z=='-' ){
esign = -1;
@@ -20946,16 +27701,14 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
}
/* copy digits to exponent */
while( z<zEnd && sqlite3Isdigit(*z) ){
- e = e*10 + (*z - '0');
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000;
z+=incr;
eValid = 1;
}
}
/* skip trailing spaces */
- if( nDigits && eValid ){
- while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
- }
+ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
do_atof_calc:
/* adjust exponent by d, and update sign */
@@ -20967,35 +27720,51 @@ do_atof_calc:
esign = 1;
}
- /* if 0 significand */
- if( !s ) {
- /* In the IEEE 754 standard, zero is signed.
- ** Add the sign if we've seen at least one digit */
- result = (sign<0 && nDigits) ? -(double)0 : (double)0;
+ if( s==0 ) {
+ /* In the IEEE 754 standard, zero is signed. */
+ result = sign<0 ? -(double)0 : (double)0;
} else {
- /* attempt to reduce exponent */
- if( esign>0 ){
- while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10;
- }else{
- while( !(s%10) && e>0 ) e--,s/=10;
+ /* Attempt to reduce exponent.
+ **
+ ** Branches that are not required for the correct answer but which only
+ ** help to obtain the correct answer faster are marked with special
+ ** comments, as a hint to the mutation tester.
+ */
+ while( e>0 ){ /*OPTIMIZATION-IF-TRUE*/
+ if( esign>0 ){
+ if( s>=(LARGEST_INT64/10) ) break; /*OPTIMIZATION-IF-FALSE*/
+ s *= 10;
+ }else{
+ if( s%10!=0 ) break; /*OPTIMIZATION-IF-FALSE*/
+ s /= 10;
+ }
+ e--;
}
/* adjust the sign of significand */
s = sign<0 ? -s : s;
- /* if exponent, scale significand as appropriate
- ** and store in result. */
- if( e ){
- double scale = 1.0;
+ if( e==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ result = (double)s;
+ }else{
+ LONGDOUBLE_TYPE scale = 1.0;
/* attempt to handle extremely small/large numbers better */
- if( e>307 && e<342 ){
- while( e%308 ) { scale *= 1.0e+1; e -= 1; }
- if( esign<0 ){
- result = s / scale;
- result /= 1.0e+308;
- }else{
- result = s * scale;
- result *= 1.0e+308;
+ if( e>307 ){ /*OPTIMIZATION-IF-TRUE*/
+ if( e<342 ){ /*OPTIMIZATION-IF-TRUE*/
+ while( e%308 ) { scale *= 1.0e+1; e -= 1; }
+ if( esign<0 ){
+ result = s / scale;
+ result /= 1.0e+308;
+ }else{
+ result = s * scale;
+ result *= 1.0e+308;
+ }
+ }else{ assert( e>=342 );
+ if( esign<0 ){
+ result = 0.0*s;
+ }else{
+ result = 1e308*1e308*s; /* Infinity */
+ }
}
}else{
/* 1.0e+22 is the largest power of 10 than can be
@@ -21008,8 +27777,6 @@ do_atof_calc:
result = s * scale;
}
}
- } else {
- result = (double)s;
}
}
@@ -21017,7 +27784,7 @@ do_atof_calc:
*pResult = result;
/* return true if number and no extra non-whitespace chracters after */
- return z>=zEnd && nDigits>0 && eValid;
+ return z==zEnd && nDigits>0 && eValid && nonNum==0;
#else
return !sqlite3Atoi64(z, pResult, length, enc);
#endif /* SQLITE_OMIT_FLOATING_POINT */
@@ -21054,33 +27821,45 @@ static int compare2pow63(const char *zNum, int incr){
return c;
}
-
/*
-** Convert zNum to a 64-bit signed integer.
+** Convert zNum to a 64-bit signed integer. zNum must be decimal. This
+** routine does *not* accept hexadecimal notation.
**
** If the zNum value is representable as a 64-bit twos-complement
** integer, then write that value into *pNum and return 0.
**
-** If zNum is exactly 9223372036854665808, return 2. This special
-** case is broken out because while 9223372036854665808 cannot be a
-** signed 64-bit integer, its negative -9223372036854665808 can be.
+** If zNum is exactly 9223372036854775808, return 2. This special
+** case is broken out because while 9223372036854775808 cannot be a
+** signed 64-bit integer, its negative -9223372036854775808 can be.
**
** If zNum is too big for a 64-bit integer and is not
-** 9223372036854665808 then return 1.
+** 9223372036854775808 or if zNum contains any non-numeric text,
+** then return 1.
**
** length is the number of bytes in the string (bytes, not characters).
** The string is not necessarily zero-terminated. The encoding is
** given by enc.
*/
SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){
- int incr = (enc==SQLITE_UTF8?1:2);
+ int incr;
u64 u = 0;
int neg = 0; /* assume positive */
int i;
int c = 0;
+ int nonNum = 0; /* True if input contains UTF16 with high byte non-zero */
const char *zStart;
const char *zEnd = zNum + length;
- if( enc==SQLITE_UTF16BE ) zNum++;
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
+ if( enc==SQLITE_UTF8 ){
+ incr = 1;
+ }else{
+ incr = 2;
+ assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 );
+ for(i=3-enc; i<length && zNum[i]==0; i+=2){}
+ nonNum = i<length;
+ zEnd = &zNum[i^1];
+ zNum += (enc&1);
+ }
while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
if( zNum<zEnd ){
if( *zNum=='-' ){
@@ -21096,7 +27875,7 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
u = u*10 + c - '0';
}
if( u>LARGEST_INT64 ){
- *pNum = SMALLEST_INT64;
+ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64;
}else if( neg ){
*pNum = -(i64)u;
}else{
@@ -21105,7 +27884,11 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
testcase( i==18 );
testcase( i==19 );
testcase( i==20 );
- if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) || i>19*incr ){
+ if( &zNum[i]<zEnd /* Extra bytes at the end */
+ || (i==0 && zStart==zNum) /* No digits */
+ || i>19*incr /* Too many digits */
+ || nonNum /* UTF16 with high-order bytes non-zero */
+ ){
/* zNum is empty or contains non-numeric text or is longer
** than 19 digits (thus guaranteeing that it is too large) */
return 1;
@@ -21127,16 +27910,48 @@ SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc
/* zNum is exactly 9223372036854775808. Fits if negative. The
** special case 2 overflow if positive */
assert( u-1==LARGEST_INT64 );
- assert( (*pNum)==SMALLEST_INT64 );
return neg ? 0 : 2;
}
}
}
/*
+** Transform a UTF-8 integer literal, in either decimal or hexadecimal,
+** into a 64-bit signed integer. This routine accepts hexadecimal literals,
+** whereas sqlite3Atoi64() does not.
+**
+** Returns:
+**
+** 0 Successful transformation. Fits in a 64-bit signed integer.
+** 1 Integer too large for a 64-bit signed integer or is malformed
+** 2 Special case of 9223372036854775808
+*/
+SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ if( z[0]=='0'
+ && (z[1]=='x' || z[1]=='X')
+ ){
+ u64 u = 0;
+ int i, k;
+ for(i=2; z[i]=='0'; i++){}
+ for(k=i; sqlite3Isxdigit(z[k]); k++){
+ u = u*16 + sqlite3HexToInt(z[k]);
+ }
+ memcpy(pOut, &u, 8);
+ return (z[k]==0 && k-i<=16) ? 0 : 1;
+ }else
+#endif /* SQLITE_OMIT_HEX_INTEGER */
+ {
+ return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8);
+ }
+}
+
+/*
** If zNum represents an integer that will fit in 32-bits, then set
** *pValue to that integer and return true. Otherwise return false.
**
+** This routine accepts both decimal and hexadecimal notation for integers.
+**
** Any non-numeric characters that following zNum are ignored.
** This is different from sqlite3Atoi64() which requires the
** input number to be zero-terminated.
@@ -21151,6 +27966,25 @@ SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){
}else if( zNum[0]=='+' ){
zNum++;
}
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ else if( zNum[0]=='0'
+ && (zNum[1]=='x' || zNum[1]=='X')
+ && sqlite3Isxdigit(zNum[2])
+ ){
+ u32 u = 0;
+ zNum += 2;
+ while( zNum[0]=='0' ) zNum++;
+ for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){
+ u = u*16 + sqlite3HexToInt(zNum[i]);
+ }
+ if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){
+ memcpy(pValue, &u, 4);
+ return 1;
+ }else{
+ return 0;
+ }
+ }
+#endif
while( zNum[0]=='0' ) zNum++;
for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){
v = v*10 + c;
@@ -21215,7 +28049,7 @@ SQLITE_PRIVATE int sqlite3Atoi(const char *z){
** bit clear. Except, if we get to the 9th byte, it stores the full
** 8 bits and is the last byte.
*/
-SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
+static int SQLITE_NOINLINE putVarint64(unsigned char *p, u64 v){
int i, j, n;
u8 buf[10];
if( v & (((u64)0xff000000)<<32) ){
@@ -21239,28 +28073,17 @@ SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
}
return n;
}
-
-/*
-** This routine is a faster version of sqlite3PutVarint() that only
-** works for 32-bit positive integers and which is optimized for
-** the common case of small integers. A MACRO version, putVarint32,
-** is provided which inlines the single-byte case. All code should use
-** the MACRO version as this function assumes the single-byte case has
-** already been handled.
-*/
-SQLITE_PRIVATE int sqlite3PutVarint32(unsigned char *p, u32 v){
-#ifndef putVarint32
- if( (v & ~0x7f)==0 ){
- p[0] = v;
+SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){
+ if( v<=0x7f ){
+ p[0] = v&0x7f;
return 1;
}
-#endif
- if( (v & ~0x3fff)==0 ){
- p[0] = (u8)((v>>7) | 0x80);
- p[1] = (u8)(v & 0x7f);
+ if( v<=0x3fff ){
+ p[0] = ((v>>7)&0x7f)|0x80;
+ p[1] = v&0x7f;
return 2;
}
- return sqlite3PutVarint(p, v);
+ return putVarint64(p,v);
}
/*
@@ -21353,7 +28176,8 @@ SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){
/* a: p0<<28 | p2<<14 | p4 (unmasked) */
if (!(a&0x80))
{
- /* we can skip these cause they were (effectively) done above in calc'ing s */
+ /* we can skip these cause they were (effectively) done above
+ ** while calculating s */
/* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
/* b &= (0x7f<<14)|(0x7f); */
b = b<<7;
@@ -21574,11 +28398,8 @@ SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
** 64-bit integer.
*/
SQLITE_PRIVATE int sqlite3VarintLen(u64 v){
- int i = 0;
- do{
- i++;
- v >>= 7;
- }while( v!=0 && ALWAYS(i<9) );
+ int i;
+ for(i=1; (v >>= 7)!=0; i++){ assert( i<10 ); }
return i;
}
@@ -21587,13 +28408,42 @@ SQLITE_PRIVATE int sqlite3VarintLen(u64 v){
** Read or write a four-byte big-endian integer value.
*/
SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){
- return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+#if SQLITE_BYTEORDER==4321
+ u32 x;
+ memcpy(&x,p,4);
+ return x;
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(__GNUC__) && GCC_VERSION>=4003000
+ u32 x;
+ memcpy(&x,p,4);
+ return __builtin_bswap32(x);
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(_MSC_VER) && _MSC_VER>=1300
+ u32 x;
+ memcpy(&x,p,4);
+ return _byteswap_ulong(x);
+#else
+ testcase( p[0]&0x80 );
+ return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
+#endif
}
SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){
+#if SQLITE_BYTEORDER==4321
+ memcpy(p,&v,4);
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(__GNUC__) && GCC_VERSION>=4003000
+ u32 x = __builtin_bswap32(v);
+ memcpy(p,&x,4);
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(_MSC_VER) && _MSC_VER>=1300
+ u32 x = _byteswap_ulong(v);
+ memcpy(p,&x,4);
+#else
p[0] = (u8)(v>>24);
p[1] = (u8)(v>>16);
p[2] = (u8)(v>>8);
p[3] = (u8)v;
+#endif
}
@@ -21625,7 +28475,7 @@ SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){
char *zBlob;
int i;
- zBlob = (char *)sqlite3DbMallocRaw(db, n/2 + 1);
+ zBlob = (char *)sqlite3DbMallocRawNN(db, n/2 + 1);
n--;
if( zBlob ){
for(i=0; i<n; i+=2){
@@ -21708,13 +28558,12 @@ SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){
testcase( iA>0 && LARGEST_INT64 - iA == iB );
testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 );
if( iA>0 && LARGEST_INT64 - iA < iB ) return 1;
- *pA += iB;
}else{
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 );
testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 );
if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1;
- *pA += iB;
}
+ *pA += iB;
return 0;
}
SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){
@@ -21728,27 +28577,21 @@ SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){
return sqlite3AddInt64(pA, -iB);
}
}
-#define TWOPOWER32 (((i64)1)<<32)
-#define TWOPOWER31 (((i64)1)<<31)
SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){
i64 iA = *pA;
- i64 iA1, iA0, iB1, iB0, r;
-
- iA1 = iA/TWOPOWER32;
- iA0 = iA % TWOPOWER32;
- iB1 = iB/TWOPOWER32;
- iB0 = iB % TWOPOWER32;
- if( iA1*iB1 != 0 ) return 1;
- assert( iA1*iB0==0 || iA0*iB1==0 );
- r = iA1*iB0 + iA0*iB1;
- testcase( r==(-TWOPOWER31)-1 );
- testcase( r==(-TWOPOWER31) );
- testcase( r==TWOPOWER31 );
- testcase( r==TWOPOWER31-1 );
- if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1;
- r *= TWOPOWER32;
- if( sqlite3AddInt64(&r, iA0*iB0) ) return 1;
- *pA = r;
+ if( iB>0 ){
+ if( iA>LARGEST_INT64/iB ) return 1;
+ if( iA<SMALLEST_INT64/iB ) return 1;
+ }else if( iB<0 ){
+ if( iA>0 ){
+ if( iB<SMALLEST_INT64/iA ) return 1;
+ }else if( iA<0 ){
+ if( iB==SMALLEST_INT64 ) return 1;
+ if( iA==SMALLEST_INT64 ) return 1;
+ if( -iA>LARGEST_INT64/-iB ) return 1;
+ }
+ }
+ *pA = iA*iB;
return 0;
}
@@ -21778,22 +28621,111 @@ SQLITE_PRIVATE int sqlite3AbsInt32(int x){
** test.db-journal => test.nal
** test.db-wal => test.wal
** test.db-shm => test.shm
+** test.db-mj7f3319fa => test.9fa
*/
SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
#if SQLITE_ENABLE_8_3_NAMES<2
- const char *zOk;
- zOk = sqlite3_uri_parameter(zBaseFilename, "8_3_names");
- if( zOk && sqlite3GetBoolean(zOk) )
+ if( sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) )
#endif
{
int i, sz;
sz = sqlite3Strlen30(z);
for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
- if( z[i]=='.' && ALWAYS(sz>i+4) ) memcpy(&z[i+1], &z[sz-3], 4);
+ if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4);
}
}
#endif
+/*
+** Find (an approximate) sum of two LogEst values. This computation is
+** not a simple "+" operator because LogEst is stored as a logarithmic
+** value.
+**
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst a, LogEst b){
+ static const unsigned char x[] = {
+ 10, 10, /* 0,1 */
+ 9, 9, /* 2,3 */
+ 8, 8, /* 4,5 */
+ 7, 7, 7, /* 6,7,8 */
+ 6, 6, 6, /* 9,10,11 */
+ 5, 5, 5, /* 12-14 */
+ 4, 4, 4, 4, /* 15-18 */
+ 3, 3, 3, 3, 3, 3, /* 19-24 */
+ 2, 2, 2, 2, 2, 2, 2, /* 25-31 */
+ };
+ if( a>=b ){
+ if( a>b+49 ) return a;
+ if( a>b+31 ) return a+1;
+ return a+x[a-b];
+ }else{
+ if( b>a+49 ) return b;
+ if( b>a+31 ) return b+1;
+ return b+x[b-a];
+ }
+}
+
+/*
+** Convert an integer into a LogEst. In other words, compute an
+** approximation for 10*log2(x).
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){
+ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 };
+ LogEst y = 40;
+ if( x<8 ){
+ if( x<2 ) return 0;
+ while( x<8 ){ y -= 10; x <<= 1; }
+ }else{
+ while( x>255 ){ y += 40; x >>= 4; } /*OPTIMIZATION-IF-TRUE*/
+ while( x>15 ){ y += 10; x >>= 1; }
+ }
+ return a[x&7] + y - 10;
+}
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Convert a double into a LogEst
+** In other words, compute an approximation for 10*log2(x).
+*/
+SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){
+ u64 a;
+ LogEst e;
+ assert( sizeof(x)==8 && sizeof(a)==8 );
+ if( x<=1 ) return 0;
+ if( x<=2000000000 ) return sqlite3LogEst((u64)x);
+ memcpy(&a, &x, 8);
+ e = (a>>52) - 1022;
+ return e*10;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
+ defined(SQLITE_ENABLE_STAT3_OR_STAT4) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
+/*
+** Convert a LogEst into an integer.
+**
+** Note that this routine is only used when one or more of various
+** non-standard compile-time options is enabled.
+*/
+SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){
+ u64 n;
+ n = x%10;
+ x /= 10;
+ if( n>=5 ) n -= 2;
+ else if( n>=1 ) n -= 1;
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS) || \
+ defined(SQLITE_EXPLAIN_ESTIMATED_ROWS)
+ if( x>60 ) return (u64)LARGEST_INT64;
+#else
+ /* If only SQLITE_ENABLE_STAT3_OR_STAT4 is on, then the largest input
+ ** possible to this routine is 310, resulting in a maximum x of 31 */
+ assert( x<=60 );
+#endif
+ return x>=3 ? (n+8)<<(x-3) : (n+8)>>(3-x);
+}
+#endif /* defined SCANSTAT or STAT4 or ESTIMATED_ROWS */
+
/************** End of util.c ************************************************/
/************** Begin file hash.c ********************************************/
/*
@@ -21810,6 +28742,7 @@ SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){
** This is the implementation of generic hash-tables
** used in SQLite.
*/
+/* #include "sqliteInt.h" */
/* #include <assert.h> */
/* Turn bulk memory into a hash table object by initializing the
@@ -21849,12 +28782,15 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){
/*
** The hashing function.
*/
-static unsigned int strHash(const char *z, int nKey){
- int h = 0;
- assert( nKey>=0 );
- while( nKey > 0 ){
- h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
- nKey--;
+static unsigned int strHash(const char *z){
+ unsigned int h = 0;
+ unsigned char c;
+ while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
+ ** 0x9e3779b1 is 2654435761 which is the closest prime number to
+ ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
+ h += sqlite3UpperToLower[c];
+ h *= 0x9e3779b1;
}
return h;
}
@@ -21910,7 +28846,11 @@ static int rehash(Hash *pH, unsigned int new_size){
/* The inability to allocates space for a larger hash table is
** a performance hit but it is not a fatal error. So mark the
- ** allocation as a benign.
+ ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of
+ ** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero()
+ ** only zeroes the requested number of bytes whereas this module will
+ ** use the actual amount of space allocated for the hash table (which
+ ** may be larger than the requested amount).
*/
sqlite3BeginBenignMalloc();
new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) );
@@ -21922,7 +28862,7 @@ static int rehash(Hash *pH, unsigned int new_size){
pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht);
memset(new_ht, 0, new_size*sizeof(struct _ht));
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
- unsigned int h = strHash(elem->pKey, elem->nKey) % new_size;
+ unsigned int h = strHash(elem->pKey) % new_size;
next_elem = elem->next;
insertElement(pH, &new_ht[h], elem);
}
@@ -21930,28 +28870,33 @@ static int rehash(Hash *pH, unsigned int new_size){
}
/* This function (for internal use only) locates an element in an
-** hash table that matches the given key. The hash for this key has
-** already been computed and is passed as the 4th parameter.
+** hash table that matches the given key. The hash for this key is
+** also computed and returned in the *pH parameter.
*/
-static HashElem *findElementGivenHash(
+static HashElem *findElementWithHash(
const Hash *pH, /* The pH to be searched */
const char *pKey, /* The key we are searching for */
- int nKey, /* Bytes in key (not counting zero terminator) */
- unsigned int h /* The hash for this key. */
+ unsigned int *pHash /* Write the hash value here */
){
HashElem *elem; /* Used to loop thru the element list */
int count; /* Number of elements left to test */
+ unsigned int h; /* The computed hash */
- if( pH->ht ){
- struct _ht *pEntry = &pH->ht[h];
+ if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
+ struct _ht *pEntry;
+ h = strHash(pKey) % pH->htsize;
+ pEntry = &pH->ht[h];
elem = pEntry->chain;
count = pEntry->count;
}else{
+ h = 0;
elem = pH->first;
count = pH->count;
}
- while( count-- && ALWAYS(elem) ){
- if( elem->nKey==nKey && sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){
+ *pHash = h;
+ while( count-- ){
+ assert( elem!=0 );
+ if( sqlite3StrICmp(elem->pKey,pKey)==0 ){
return elem;
}
elem = elem->next;
@@ -21986,7 +28931,7 @@ static void removeElementGivenHash(
}
sqlite3_free( elem );
pH->count--;
- if( pH->count<=0 ){
+ if( pH->count==0 ){
assert( pH->first==0 );
assert( pH->count==0 );
sqlite3HashClear(pH);
@@ -21994,26 +28939,20 @@ static void removeElementGivenHash(
}
/* Attempt to locate an element of the hash table pH with a key
-** that matches pKey,nKey. Return the data for this element if it is
+** that matches pKey. Return the data for this element if it is
** found, or NULL if there is no match.
*/
-SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey, int nKey){
+SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){
HashElem *elem; /* The element that matches key */
unsigned int h; /* A hash on key */
assert( pH!=0 );
assert( pKey!=0 );
- assert( nKey>=0 );
- if( pH->ht ){
- h = strHash(pKey, nKey) % pH->htsize;
- }else{
- h = 0;
- }
- elem = findElementGivenHash(pH, pKey, nKey, h);
+ elem = findElementWithHash(pH, pKey, &h);
return elem ? elem->data : 0;
}
-/* Insert an element into the hash table pH. The key is pKey,nKey
+/* Insert an element into the hash table pH. The key is pKey
** and the data is "data".
**
** If no element exists with a matching key, then a new
@@ -22027,20 +28966,14 @@ SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey, int nKey)
** If the "data" parameter to this function is NULL, then the
** element corresponding to "key" is removed from the hash table.
*/
-SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, void *data){
+SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){
unsigned int h; /* the hash of the key modulo hash table size */
HashElem *elem; /* Used to loop thru the element list */
HashElem *new_elem; /* New element added to the pH */
assert( pH!=0 );
assert( pKey!=0 );
- assert( nKey>=0 );
- if( pH->htsize ){
- h = strHash(pKey, nKey) % pH->htsize;
- }else{
- h = 0;
- }
- elem = findElementGivenHash(pH,pKey,nKey,h);
+ elem = findElementWithHash(pH,pKey,&h);
if( elem ){
void *old_data = elem->data;
if( data==0 ){
@@ -22048,7 +28981,6 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, voi
}else{
elem->data = data;
elem->pKey = pKey;
- assert(nKey==elem->nKey);
}
return old_data;
}
@@ -22056,2320 +28988,201 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, voi
new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) );
if( new_elem==0 ) return data;
new_elem->pKey = pKey;
- new_elem->nKey = nKey;
new_elem->data = data;
pH->count++;
if( pH->count>=10 && pH->count > 2*pH->htsize ){
if( rehash(pH, pH->count*2) ){
assert( pH->htsize>0 );
- h = strHash(pKey, nKey) % pH->htsize;
+ h = strHash(pKey) % pH->htsize;
}
}
- if( pH->ht ){
- insertElement(pH, &pH->ht[h], new_elem);
- }else{
- insertElement(pH, 0, new_elem);
- }
+ insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem);
return 0;
}
/************** End of hash.c ************************************************/
/************** Begin file opcodes.c *****************************************/
/* Automatically generated. Do not edit */
-/* See the mkopcodec.awk script for details. */
-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+/* See the tool/mkopcodec.tcl script for details. */
+#if !defined(SQLITE_OMIT_EXPLAIN) \
+ || defined(VDBE_PROFILE) \
+ || defined(SQLITE_DEBUG)
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG)
+# define OpHelp(X) "\0" X
+#else
+# define OpHelp(X)
+#endif
SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
- static const char *const azName[] = { "?",
- /* 1 */ "Goto",
- /* 2 */ "Gosub",
- /* 3 */ "Return",
- /* 4 */ "Yield",
- /* 5 */ "HaltIfNull",
- /* 6 */ "Halt",
- /* 7 */ "Integer",
- /* 8 */ "Int64",
- /* 9 */ "String",
- /* 10 */ "Null",
- /* 11 */ "Blob",
- /* 12 */ "Variable",
- /* 13 */ "Move",
- /* 14 */ "Copy",
- /* 15 */ "SCopy",
- /* 16 */ "ResultRow",
- /* 17 */ "CollSeq",
- /* 18 */ "Function",
- /* 19 */ "Not",
- /* 20 */ "AddImm",
- /* 21 */ "MustBeInt",
- /* 22 */ "RealAffinity",
- /* 23 */ "Permutation",
- /* 24 */ "Compare",
- /* 25 */ "Jump",
- /* 26 */ "Once",
- /* 27 */ "If",
- /* 28 */ "IfNot",
- /* 29 */ "Column",
- /* 30 */ "Affinity",
- /* 31 */ "MakeRecord",
- /* 32 */ "Count",
- /* 33 */ "Savepoint",
- /* 34 */ "AutoCommit",
- /* 35 */ "Transaction",
- /* 36 */ "ReadCookie",
- /* 37 */ "SetCookie",
- /* 38 */ "VerifyCookie",
- /* 39 */ "OpenRead",
- /* 40 */ "OpenWrite",
- /* 41 */ "OpenAutoindex",
- /* 42 */ "OpenEphemeral",
- /* 43 */ "SorterOpen",
- /* 44 */ "OpenPseudo",
- /* 45 */ "Close",
- /* 46 */ "SeekLt",
- /* 47 */ "SeekLe",
- /* 48 */ "SeekGe",
- /* 49 */ "SeekGt",
- /* 50 */ "Seek",
- /* 51 */ "NotFound",
- /* 52 */ "Found",
- /* 53 */ "IsUnique",
- /* 54 */ "NotExists",
- /* 55 */ "Sequence",
- /* 56 */ "NewRowid",
- /* 57 */ "Insert",
- /* 58 */ "InsertInt",
- /* 59 */ "Delete",
- /* 60 */ "ResetCount",
- /* 61 */ "SorterCompare",
- /* 62 */ "SorterData",
- /* 63 */ "RowKey",
- /* 64 */ "RowData",
- /* 65 */ "Rowid",
- /* 66 */ "NullRow",
- /* 67 */ "Last",
- /* 68 */ "Or",
- /* 69 */ "And",
- /* 70 */ "SorterSort",
- /* 71 */ "Sort",
- /* 72 */ "Rewind",
- /* 73 */ "IsNull",
- /* 74 */ "NotNull",
- /* 75 */ "Ne",
- /* 76 */ "Eq",
- /* 77 */ "Gt",
- /* 78 */ "Le",
- /* 79 */ "Lt",
- /* 80 */ "Ge",
- /* 81 */ "SorterNext",
- /* 82 */ "BitAnd",
- /* 83 */ "BitOr",
- /* 84 */ "ShiftLeft",
- /* 85 */ "ShiftRight",
- /* 86 */ "Add",
- /* 87 */ "Subtract",
- /* 88 */ "Multiply",
- /* 89 */ "Divide",
- /* 90 */ "Remainder",
- /* 91 */ "Concat",
- /* 92 */ "Prev",
- /* 93 */ "BitNot",
- /* 94 */ "String8",
- /* 95 */ "Next",
- /* 96 */ "SorterInsert",
- /* 97 */ "IdxInsert",
- /* 98 */ "IdxDelete",
- /* 99 */ "IdxRowid",
- /* 100 */ "IdxLT",
- /* 101 */ "IdxGE",
- /* 102 */ "Destroy",
- /* 103 */ "Clear",
- /* 104 */ "CreateIndex",
- /* 105 */ "CreateTable",
- /* 106 */ "ParseSchema",
- /* 107 */ "LoadAnalysis",
- /* 108 */ "DropTable",
- /* 109 */ "DropIndex",
- /* 110 */ "DropTrigger",
- /* 111 */ "IntegrityCk",
- /* 112 */ "RowSetAdd",
- /* 113 */ "RowSetRead",
- /* 114 */ "RowSetTest",
- /* 115 */ "Program",
- /* 116 */ "Param",
- /* 117 */ "FkCounter",
- /* 118 */ "FkIfZero",
- /* 119 */ "MemMax",
- /* 120 */ "IfPos",
- /* 121 */ "IfNeg",
- /* 122 */ "IfZero",
- /* 123 */ "AggStep",
- /* 124 */ "AggFinal",
- /* 125 */ "Checkpoint",
- /* 126 */ "JournalMode",
- /* 127 */ "Vacuum",
- /* 128 */ "IncrVacuum",
- /* 129 */ "Expire",
- /* 130 */ "Real",
- /* 131 */ "TableLock",
- /* 132 */ "VBegin",
- /* 133 */ "VCreate",
- /* 134 */ "VDestroy",
- /* 135 */ "VOpen",
- /* 136 */ "VFilter",
- /* 137 */ "VColumn",
- /* 138 */ "VNext",
- /* 139 */ "VRename",
- /* 140 */ "VUpdate",
- /* 141 */ "ToText",
- /* 142 */ "ToBlob",
- /* 143 */ "ToNumeric",
- /* 144 */ "ToInt",
- /* 145 */ "ToReal",
- /* 146 */ "Pagecount",
- /* 147 */ "MaxPgcnt",
- /* 148 */ "Trace",
- /* 149 */ "Noop",
- /* 150 */ "Explain",
+ static const char *const azName[] = {
+ /* 0 */ "Savepoint" OpHelp(""),
+ /* 1 */ "AutoCommit" OpHelp(""),
+ /* 2 */ "Transaction" OpHelp(""),
+ /* 3 */ "SorterNext" OpHelp(""),
+ /* 4 */ "PrevIfOpen" OpHelp(""),
+ /* 5 */ "NextIfOpen" OpHelp(""),
+ /* 6 */ "Prev" OpHelp(""),
+ /* 7 */ "Next" OpHelp(""),
+ /* 8 */ "Checkpoint" OpHelp(""),
+ /* 9 */ "JournalMode" OpHelp(""),
+ /* 10 */ "Vacuum" OpHelp(""),
+ /* 11 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"),
+ /* 12 */ "VUpdate" OpHelp("data=r[P3@P2]"),
+ /* 13 */ "Goto" OpHelp(""),
+ /* 14 */ "Gosub" OpHelp(""),
+ /* 15 */ "InitCoroutine" OpHelp(""),
+ /* 16 */ "Yield" OpHelp(""),
+ /* 17 */ "MustBeInt" OpHelp(""),
+ /* 18 */ "Jump" OpHelp(""),
+ /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"),
+ /* 20 */ "Once" OpHelp(""),
+ /* 21 */ "If" OpHelp(""),
+ /* 22 */ "IfNot" OpHelp(""),
+ /* 23 */ "SeekLT" OpHelp("key=r[P3@P4]"),
+ /* 24 */ "SeekLE" OpHelp("key=r[P3@P4]"),
+ /* 25 */ "SeekGE" OpHelp("key=r[P3@P4]"),
+ /* 26 */ "SeekGT" OpHelp("key=r[P3@P4]"),
+ /* 27 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"),
+ /* 28 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"),
+ /* 29 */ "NoConflict" OpHelp("key=r[P3@P4]"),
+ /* 30 */ "NotFound" OpHelp("key=r[P3@P4]"),
+ /* 31 */ "Found" OpHelp("key=r[P3@P4]"),
+ /* 32 */ "SeekRowid" OpHelp("intkey=r[P3]"),
+ /* 33 */ "NotExists" OpHelp("intkey=r[P3]"),
+ /* 34 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"),
+ /* 35 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"),
+ /* 36 */ "Ne" OpHelp("IF r[P3]!=r[P1]"),
+ /* 37 */ "Eq" OpHelp("IF r[P3]==r[P1]"),
+ /* 38 */ "Gt" OpHelp("IF r[P3]>r[P1]"),
+ /* 39 */ "Le" OpHelp("IF r[P3]<=r[P1]"),
+ /* 40 */ "Lt" OpHelp("IF r[P3]<r[P1]"),
+ /* 41 */ "Ge" OpHelp("IF r[P3]>=r[P1]"),
+ /* 42 */ "ElseNotEq" OpHelp(""),
+ /* 43 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"),
+ /* 44 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"),
+ /* 45 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"),
+ /* 46 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"),
+ /* 47 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"),
+ /* 48 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"),
+ /* 49 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"),
+ /* 50 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"),
+ /* 51 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"),
+ /* 52 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"),
+ /* 53 */ "Last" OpHelp(""),
+ /* 54 */ "BitNot" OpHelp("r[P1]= ~r[P1]"),
+ /* 55 */ "SorterSort" OpHelp(""),
+ /* 56 */ "Sort" OpHelp(""),
+ /* 57 */ "Rewind" OpHelp(""),
+ /* 58 */ "IdxLE" OpHelp("key=r[P3@P4]"),
+ /* 59 */ "IdxGT" OpHelp("key=r[P3@P4]"),
+ /* 60 */ "IdxLT" OpHelp("key=r[P3@P4]"),
+ /* 61 */ "IdxGE" OpHelp("key=r[P3@P4]"),
+ /* 62 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"),
+ /* 63 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"),
+ /* 64 */ "Program" OpHelp(""),
+ /* 65 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"),
+ /* 66 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"),
+ /* 67 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]-=P3, goto P2"),
+ /* 68 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"),
+ /* 69 */ "IncrVacuum" OpHelp(""),
+ /* 70 */ "VNext" OpHelp(""),
+ /* 71 */ "Init" OpHelp("Start at P2"),
+ /* 72 */ "Return" OpHelp(""),
+ /* 73 */ "EndCoroutine" OpHelp(""),
+ /* 74 */ "HaltIfNull" OpHelp("if r[P3]=null halt"),
+ /* 75 */ "Halt" OpHelp(""),
+ /* 76 */ "Integer" OpHelp("r[P2]=P1"),
+ /* 77 */ "Int64" OpHelp("r[P2]=P4"),
+ /* 78 */ "String" OpHelp("r[P2]='P4' (len=P1)"),
+ /* 79 */ "Null" OpHelp("r[P2..P3]=NULL"),
+ /* 80 */ "SoftNull" OpHelp("r[P1]=NULL"),
+ /* 81 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"),
+ /* 82 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"),
+ /* 83 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"),
+ /* 84 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"),
+ /* 85 */ "SCopy" OpHelp("r[P2]=r[P1]"),
+ /* 86 */ "IntCopy" OpHelp("r[P2]=r[P1]"),
+ /* 87 */ "ResultRow" OpHelp("output=r[P1@P2]"),
+ /* 88 */ "CollSeq" OpHelp(""),
+ /* 89 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"),
+ /* 90 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"),
+ /* 91 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"),
+ /* 92 */ "RealAffinity" OpHelp(""),
+ /* 93 */ "Cast" OpHelp("affinity(r[P1])"),
+ /* 94 */ "Permutation" OpHelp(""),
+ /* 95 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"),
+ /* 96 */ "Column" OpHelp("r[P3]=PX"),
+ /* 97 */ "String8" OpHelp("r[P2]='P4'"),
+ /* 98 */ "Affinity" OpHelp("affinity(r[P1@P2])"),
+ /* 99 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"),
+ /* 100 */ "Count" OpHelp("r[P2]=count()"),
+ /* 101 */ "ReadCookie" OpHelp(""),
+ /* 102 */ "SetCookie" OpHelp(""),
+ /* 103 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"),
+ /* 104 */ "OpenRead" OpHelp("root=P2 iDb=P3"),
+ /* 105 */ "OpenWrite" OpHelp("root=P2 iDb=P3"),
+ /* 106 */ "OpenAutoindex" OpHelp("nColumn=P2"),
+ /* 107 */ "OpenEphemeral" OpHelp("nColumn=P2"),
+ /* 108 */ "SorterOpen" OpHelp(""),
+ /* 109 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"),
+ /* 110 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"),
+ /* 111 */ "Close" OpHelp(""),
+ /* 112 */ "ColumnsUsed" OpHelp(""),
+ /* 113 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"),
+ /* 114 */ "NewRowid" OpHelp("r[P2]=rowid"),
+ /* 115 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"),
+ /* 116 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"),
+ /* 117 */ "Delete" OpHelp(""),
+ /* 118 */ "ResetCount" OpHelp(""),
+ /* 119 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
+ /* 120 */ "SorterData" OpHelp("r[P2]=data"),
+ /* 121 */ "RowKey" OpHelp("r[P2]=key"),
+ /* 122 */ "RowData" OpHelp("r[P2]=data"),
+ /* 123 */ "Rowid" OpHelp("r[P2]=rowid"),
+ /* 124 */ "NullRow" OpHelp(""),
+ /* 125 */ "SorterInsert" OpHelp(""),
+ /* 126 */ "IdxInsert" OpHelp("key=r[P2]"),
+ /* 127 */ "IdxDelete" OpHelp("key=r[P2@P3]"),
+ /* 128 */ "Seek" OpHelp("Move P3 to P1.rowid"),
+ /* 129 */ "IdxRowid" OpHelp("r[P2]=rowid"),
+ /* 130 */ "Destroy" OpHelp(""),
+ /* 131 */ "Clear" OpHelp(""),
+ /* 132 */ "Real" OpHelp("r[P2]=P4"),
+ /* 133 */ "ResetSorter" OpHelp(""),
+ /* 134 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"),
+ /* 135 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"),
+ /* 136 */ "ParseSchema" OpHelp(""),
+ /* 137 */ "LoadAnalysis" OpHelp(""),
+ /* 138 */ "DropTable" OpHelp(""),
+ /* 139 */ "DropIndex" OpHelp(""),
+ /* 140 */ "DropTrigger" OpHelp(""),
+ /* 141 */ "IntegrityCk" OpHelp(""),
+ /* 142 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"),
+ /* 143 */ "Param" OpHelp(""),
+ /* 144 */ "FkCounter" OpHelp("fkctr[P1]+=P2"),
+ /* 145 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"),
+ /* 146 */ "OffsetLimit" OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
+ /* 147 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 148 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"),
+ /* 149 */ "AggFinal" OpHelp("accum=r[P1] N=P2"),
+ /* 150 */ "Expire" OpHelp(""),
+ /* 151 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"),
+ /* 152 */ "VBegin" OpHelp(""),
+ /* 153 */ "VCreate" OpHelp(""),
+ /* 154 */ "VDestroy" OpHelp(""),
+ /* 155 */ "VOpen" OpHelp(""),
+ /* 156 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"),
+ /* 157 */ "VRename" OpHelp(""),
+ /* 158 */ "Pagecount" OpHelp(""),
+ /* 159 */ "MaxPgcnt" OpHelp(""),
+ /* 160 */ "CursorHint" OpHelp(""),
+ /* 161 */ "Noop" OpHelp(""),
+ /* 162 */ "Explain" OpHelp(""),
};
return azName[i];
}
#endif
/************** End of opcodes.c *********************************************/
-/************** Begin file os_os2.c ******************************************/
-/*
-** 2006 Feb 14
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains code that is specific to OS/2.
-*/
-
-
-#if SQLITE_OS_OS2
-
-/*
-** A Note About Memory Allocation:
-**
-** This driver uses malloc()/free() directly rather than going through
-** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
-** are designed for use on embedded systems where memory is scarce and
-** malloc failures happen frequently. OS/2 does not typically run on
-** embedded systems, and when it does the developers normally have bigger
-** problems to worry about than running out of memory. So there is not
-** a compelling need to use the wrappers.
-**
-** But there is a good reason to not use the wrappers. If we use the
-** wrappers then we will get simulated malloc() failures within this
-** driver. And that causes all kinds of problems for our tests. We
-** could enhance SQLite to deal with simulated malloc failures within
-** the OS driver, but the code to deal with those failure would not
-** be exercised on Linux (which does not need to malloc() in the driver)
-** and so we would have difficulty writing coverage tests for that
-** code. Better to leave the code out, we think.
-**
-** The point of this discussion is as follows: When creating a new
-** OS layer for an embedded system, if you use this file as an example,
-** avoid the use of malloc()/free(). Those routines work ok on OS/2
-** desktops but not so well in embedded systems.
-*/
-
-/*
-** Macros used to determine whether or not to use threads.
-*/
-#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE
-# define SQLITE_OS2_THREADS 1
-#endif
-
-/*
-** Include code that is common to all os_*.c files
-*/
-/************** Include os_common.h in the middle of os_os2.c ****************/
-/************** Begin file os_common.h ***************************************/
-/*
-** 2004 May 22
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains macros and a little bit of code that is common to
-** all of the platform-specific files (os_*.c) and is #included into those
-** files.
-**
-** This file should be #included by the os_*.c files only. It is not a
-** general purpose header file.
-*/
-#ifndef _OS_COMMON_H_
-#define _OS_COMMON_H_
-
-/*
-** At least two bugs have slipped in because we changed the MEMORY_DEBUG
-** macro to SQLITE_DEBUG and some older makefiles have not yet made the
-** switch. The following code should catch this problem at compile-time.
-*/
-#ifdef MEMORY_DEBUG
-# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
-#endif
-
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
-# ifndef SQLITE_DEBUG_OS_TRACE
-# define SQLITE_DEBUG_OS_TRACE 0
-# endif
- int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
-# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
-#else
-# define OSTRACE(X)
-#endif
-
-/*
-** Macros for performance tracing. Normally turned off. Only works
-** on i486 hardware.
-*/
-#ifdef SQLITE_PERFORMANCE_TRACE
-
-/*
-** hwtime.h contains inline assembler code for implementing
-** high-performance timing routines.
-*/
-/************** Include hwtime.h in the middle of os_common.h ****************/
-/************** Begin file hwtime.h ******************************************/
-/*
-** 2008 May 27
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains inline asm code for retrieving "high-performance"
-** counters for x86 class CPUs.
-*/
-#ifndef _HWTIME_H_
-#define _HWTIME_H_
-
-/*
-** The following routine only works on pentium-class (or newer) processors.
-** It uses the RDTSC opcode to read the cycle count value out of the
-** processor and returns that value. This can be used for high-res
-** profiling.
-*/
-#if (defined(__GNUC__) || defined(_MSC_VER)) && \
- (defined(i386) || defined(__i386__) || defined(_M_IX86))
-
- #if defined(__GNUC__)
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned int lo, hi;
- __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
- return (sqlite_uint64)hi << 32 | lo;
- }
-
- #elif defined(_MSC_VER)
-
- __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){
- __asm {
- rdtsc
- ret ; return value at EDX:EAX
- }
- }
-
- #endif
-
-#elif (defined(__GNUC__) && defined(__x86_64__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long val;
- __asm__ __volatile__ ("rdtsc" : "=A" (val));
- return val;
- }
-
-#elif (defined(__GNUC__) && defined(__ppc__))
-
- __inline__ sqlite_uint64 sqlite3Hwtime(void){
- unsigned long long retval;
- unsigned long junk;
- __asm__ __volatile__ ("\n\
- 1: mftbu %1\n\
- mftb %L0\n\
- mftbu %0\n\
- cmpw %0,%1\n\
- bne 1b"
- : "=r" (retval), "=r" (junk));
- return retval;
- }
-
-#else
-
- #error Need implementation of sqlite3Hwtime() for your platform.
-
- /*
- ** To compile without implementing sqlite3Hwtime() for your platform,
- ** you can remove the above #error and use the following
- ** stub function. You will lose timing support for many
- ** of the debugging and testing utilities, but it should at
- ** least compile and run.
- */
-SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
-
-#endif
-
-#endif /* !defined(_HWTIME_H_) */
-
-/************** End of hwtime.h **********************************************/
-/************** Continuing where we left off in os_common.h ******************/
-
-static sqlite_uint64 g_start;
-static sqlite_uint64 g_elapsed;
-#define TIMER_START g_start=sqlite3Hwtime()
-#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start
-#define TIMER_ELAPSED g_elapsed
-#else
-#define TIMER_START
-#define TIMER_END
-#define TIMER_ELAPSED ((sqlite_uint64)0)
-#endif
-
-/*
-** If we compile with the SQLITE_TEST macro set, then the following block
-** of code will give us the ability to simulate a disk I/O error. This
-** is used for testing the I/O recovery logic.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
-#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
-#define SimulateIOError(CODE) \
- if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
- || sqlite3_io_error_pending-- == 1 ) \
- { local_ioerr(); CODE; }
-static void local_ioerr(){
- IOTRACE(("IOERR\n"));
- sqlite3_io_error_hit++;
- if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++;
-}
-#define SimulateDiskfullError(CODE) \
- if( sqlite3_diskfull_pending ){ \
- if( sqlite3_diskfull_pending == 1 ){ \
- local_ioerr(); \
- sqlite3_diskfull = 1; \
- sqlite3_io_error_hit = 1; \
- CODE; \
- }else{ \
- sqlite3_diskfull_pending--; \
- } \
- }
-#else
-#define SimulateIOErrorBenign(X)
-#define SimulateIOError(A)
-#define SimulateDiskfullError(A)
-#endif
-
-/*
-** When testing, keep a count of the number of open files.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
-#define OpenCounter(X) sqlite3_open_file_count+=(X)
-#else
-#define OpenCounter(X)
-#endif
-
-#endif /* !defined(_OS_COMMON_H_) */
-
-/************** End of os_common.h *******************************************/
-/************** Continuing where we left off in os_os2.c *********************/
-
-/* Forward references */
-typedef struct os2File os2File; /* The file structure */
-typedef struct os2ShmNode os2ShmNode; /* A shared descritive memory node */
-typedef struct os2ShmLink os2ShmLink; /* A connection to shared-memory */
-
-/*
-** The os2File structure is subclass of sqlite3_file specific for the OS/2
-** protability layer.
-*/
-struct os2File {
- const sqlite3_io_methods *pMethod; /* Always the first entry */
- HFILE h; /* Handle for accessing the file */
- int flags; /* Flags provided to os2Open() */
- int locktype; /* Type of lock currently held on this file */
- int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
- char *zFullPathCp; /* Full path name of this file */
- os2ShmLink *pShmLink; /* Instance of shared memory on this file */
-};
-
-#define LOCK_TIMEOUT 10L /* the default locking timeout */
-
-/*
-** Missing from some versions of the OS/2 toolkit -
-** used to allocate from high memory if possible
-*/
-#ifndef OBJ_ANY
-# define OBJ_ANY 0x00000400
-#endif
-
-/*****************************************************************************
-** The next group of routines implement the I/O methods specified
-** by the sqlite3_io_methods object.
-******************************************************************************/
-
-/*
-** Close a file.
-*/
-static int os2Close( sqlite3_file *id ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
-
- assert( id!=0 );
- OSTRACE(( "CLOSE %d (%s)\n", pFile->h, pFile->zFullPathCp ));
-
- rc = DosClose( pFile->h );
-
- if( pFile->flags & SQLITE_OPEN_DELETEONCLOSE )
- DosForceDelete( (PSZ)pFile->zFullPathCp );
-
- free( pFile->zFullPathCp );
- pFile->zFullPathCp = NULL;
- pFile->locktype = NO_LOCK;
- pFile->h = (HFILE)-1;
- pFile->flags = 0;
-
- OpenCounter( -1 );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-/*
-** Read data from a file into a buffer. Return SQLITE_OK if all
-** bytes were read successfully and SQLITE_IOERR if anything goes
-** wrong.
-*/
-static int os2Read(
- sqlite3_file *id, /* File to read from */
- void *pBuf, /* Write content into this buffer */
- int amt, /* Number of bytes to read */
- sqlite3_int64 offset /* Begin reading at this offset */
-){
- ULONG fileLocation = 0L;
- ULONG got;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_READ );
- OSTRACE(( "READ %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){
- return SQLITE_IOERR_READ;
- }
- if( got == (ULONG)amt )
- return SQLITE_OK;
- else {
- /* Unread portions of the input buffer must be zero-filled */
- memset(&((char*)pBuf)[got], 0, amt-got);
- return SQLITE_IOERR_SHORT_READ;
- }
-}
-
-/*
-** Write data from a buffer into a file. Return SQLITE_OK on success
-** or some other error code on failure.
-*/
-static int os2Write(
- sqlite3_file *id, /* File to write into */
- const void *pBuf, /* The bytes to be written */
- int amt, /* Number of bytes to write */
- sqlite3_int64 offset /* Offset into the file to begin writing at */
-){
- ULONG fileLocation = 0L;
- APIRET rc = NO_ERROR;
- ULONG wrote;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_WRITE );
- SimulateDiskfullError( return SQLITE_FULL );
- OSTRACE(( "WRITE %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- assert( amt>0 );
- while( amt > 0 &&
- ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR &&
- wrote > 0
- ){
- amt -= wrote;
- pBuf = &((char*)pBuf)[wrote];
- }
-
- return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK;
-}
-
-/*
-** Truncate an open file to a specified size
-*/
-static int os2Truncate( sqlite3_file *id, i64 nByte ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte ));
- SimulateIOError( return SQLITE_IOERR_TRUNCATE );
-
- /* If the user has configured a chunk-size for this file, truncate the
- ** file so that it consists of an integer number of chunks (i.e. the
- ** actual file size after the operation may be larger than the requested
- ** size).
- */
- if( pFile->szChunk ){
- nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
- }
-
- rc = DosSetFileSize( pFile->h, nByte );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
-}
-
-#ifdef SQLITE_TEST
-/*
-** Count the number of fullsyncs and normal syncs. This is used to test
-** that syncs and fullsyncs are occuring at the right times.
-*/
-SQLITE_API int sqlite3_sync_count = 0;
-SQLITE_API int sqlite3_fullsync_count = 0;
-#endif
-
-/*
-** Make sure all writes to a particular file are committed to disk.
-*/
-static int os2Sync( sqlite3_file *id, int flags ){
- os2File *pFile = (os2File*)id;
- OSTRACE(( "SYNC %d lock=%d\n", pFile->h, pFile->locktype ));
-#ifdef SQLITE_TEST
- if( flags & SQLITE_SYNC_FULL){
- sqlite3_fullsync_count++;
- }
- sqlite3_sync_count++;
-#endif
- /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
- ** no-op
- */
-#ifdef SQLITE_NO_SYNC
- UNUSED_PARAMETER(pFile);
- return SQLITE_OK;
-#else
- return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-#endif
-}
-
-/*
-** Determine the current size of a file in bytes
-*/
-static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){
- APIRET rc = NO_ERROR;
- FILESTATUS3 fsts3FileInfo;
- memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_FSTAT );
- rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
- if( rc == NO_ERROR ){
- *pSize = fsts3FileInfo.cbFile;
- return SQLITE_OK;
- }else{
- return SQLITE_IOERR_FSTAT;
- }
-}
-
-/*
-** Acquire a reader lock.
-*/
-static int getReadLock( os2File *pFile ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "GETREADLOCK %d res=%d\n", pFile->h, res ));
- return res;
-}
-
-/*
-** Undo a readlock
-*/
-static int unlockReadLock( os2File *id ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res ));
- return res;
-}
-
-/*
-** Lock the file with the lock specified by parameter locktype - one
-** of the following:
-**
-** (1) SHARED_LOCK
-** (2) RESERVED_LOCK
-** (3) PENDING_LOCK
-** (4) EXCLUSIVE_LOCK
-**
-** Sometimes when requesting one lock state, additional lock states
-** are inserted in between. The locking might fail on one of the later
-** transitions leaving the lock state different from what it started but
-** still short of its goal. The following chart shows the allowed
-** transitions and the inserted intermediate states:
-**
-** UNLOCKED -> SHARED
-** SHARED -> RESERVED
-** SHARED -> (PENDING) -> EXCLUSIVE
-** RESERVED -> (PENDING) -> EXCLUSIVE
-** PENDING -> EXCLUSIVE
-**
-** This routine will only increase a lock. The os2Unlock() routine
-** erases all locks at once and returns us immediately to locking level 0.
-** It is not possible to lower the locking level one step at a time. You
-** must go straight to locking level 0.
-*/
-static int os2Lock( sqlite3_file *id, int locktype ){
- int rc = SQLITE_OK; /* Return code from subroutines */
- APIRET res = NO_ERROR; /* Result of an OS/2 lock call */
- int newLocktype; /* Set pFile->locktype to this value before exiting */
- int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
- FILELOCK LockArea,
- UnlockArea;
- os2File *pFile = (os2File*)id;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- OSTRACE(( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype ));
-
- /* If there is already a lock of this type or more restrictive on the
- ** os2File, do nothing. Don't use the end_lock: exit path, as
- ** sqlite3_mutex_enter() hasn't been called yet.
- */
- if( pFile->locktype>=locktype ){
- OSTRACE(( "LOCK %d %d ok (already held)\n", pFile->h, locktype ));
- return SQLITE_OK;
- }
-
- /* Make sure the locking sequence is correct
- */
- assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
- assert( locktype!=PENDING_LOCK );
- assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
-
- /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
- ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
- ** the PENDING_LOCK byte is temporary.
- */
- newLocktype = pFile->locktype;
- if( pFile->locktype==NO_LOCK
- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
- ){
- LockArea.lOffset = PENDING_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
-
- /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L );
- if( res == NO_ERROR ){
- gotPendingLock = 1;
- OSTRACE(( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res ));
- }
- }
-
- /* Acquire a shared lock
- */
- if( locktype==SHARED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
- if( res == NO_ERROR ){
- newLocktype = SHARED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a RESERVED lock
- */
- if( locktype==RESERVED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==SHARED_LOCK );
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = RESERVED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a PENDING lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- newLocktype = PENDING_LOCK;
- gotPendingLock = 0;
- OSTRACE(( "LOCK %d acquire pending lock. pending lock boolean unset.\n",
- pFile->h ));
- }
-
- /* Acquire an EXCLUSIVE lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
- OSTRACE(( "unreadlock = %d\n", res ));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = EXCLUSIVE_LOCK;
- }else{
- OSTRACE(( "OS/2 error-code = %d\n", res ));
- getReadLock(pFile);
- }
- OSTRACE(( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res ));
- }
-
- /* If we are holding a PENDING lock that ought to be released, then
- ** release it now.
- */
- if( gotPendingLock && locktype==SHARED_LOCK ){
- int r;
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r ));
- }
-
- /* Update the state of the lock has held in the file descriptor then
- ** return the appropriate result code.
- */
- if( res == NO_ERROR ){
- rc = SQLITE_OK;
- }else{
- OSTRACE(( "LOCK FAILED %d trying for %d but got %d\n", pFile->h,
- locktype, newLocktype ));
- rc = SQLITE_BUSY;
- }
- pFile->locktype = newLocktype;
- OSTRACE(( "LOCK %d now %d\n", pFile->h, pFile->locktype ));
- return rc;
-}
-
-/*
-** This routine checks if there is a RESERVED lock held on the specified
-** file by this or any other process. If such a lock is held, return
-** non-zero, otherwise zero.
-*/
-static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){
- int r = 0;
- os2File *pFile = (os2File*)id;
- assert( pFile!=0 );
- if( pFile->locktype>=RESERVED_LOCK ){
- r = 1;
- OSTRACE(( "TEST WR-LOCK %d %d (local)\n", pFile->h, r ));
- }else{
- FILELOCK LockArea,
- UnlockArea;
- APIRET rc = NO_ERROR;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc ));
- if( rc == NO_ERROR ){
- APIRET rcu = NO_ERROR; /* return code for unlocking */
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu ));
- }
- r = !(rc == NO_ERROR);
- OSTRACE(( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r ));
- }
- *pOut = r;
- return SQLITE_OK;
-}
-
-/*
-** Lower the locking level on file descriptor id to locktype. locktype
-** must be either NO_LOCK or SHARED_LOCK.
-**
-** If the locking level of the file descriptor is already at or below
-** the requested locking level, this routine is a no-op.
-**
-** It is not possible for this routine to fail if the second argument
-** is NO_LOCK. If the second argument is SHARED_LOCK then this routine
-** might return SQLITE_IOERR;
-*/
-static int os2Unlock( sqlite3_file *id, int locktype ){
- int type;
- os2File *pFile = (os2File*)id;
- APIRET rc = SQLITE_OK;
- APIRET res = NO_ERROR;
- FILELOCK LockArea,
- UnlockArea;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- assert( locktype<=SHARED_LOCK );
- OSTRACE(( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype ));
- type = pFile->locktype;
- if( type>=EXCLUSIVE_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res ));
- if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){
- /* This should never happen. We should always be able to
- ** reacquire the read lock */
- OSTRACE(( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype ));
- rc = SQLITE_IOERR_UNLOCK;
- }
- }
- if( type>=RESERVED_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d reserved res=%d\n", pFile->h, res ));
- }
- if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- res = unlockReadLock(pFile);
- OSTRACE(( "UNLOCK %d is %d want %d res=%d\n",
- pFile->h, type, locktype, res ));
- }
- if( type>=PENDING_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d pending res=%d\n", pFile->h, res ));
- }
- pFile->locktype = locktype;
- OSTRACE(( "UNLOCK %d now %d\n", pFile->h, pFile->locktype ));
- return rc;
-}
-
-/*
-** Control and query of the open file handle.
-*/
-static int os2FileControl(sqlite3_file *id, int op, void *pArg){
- switch( op ){
- case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((os2File*)id)->locktype;
- OSTRACE(( "FCNTL_LOCKSTATE %d lock=%d\n",
- ((os2File*)id)->h, ((os2File*)id)->locktype ));
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_CHUNK_SIZE: {
- ((os2File*)id)->szChunk = *(int*)pArg;
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SIZE_HINT: {
- sqlite3_int64 sz = *(sqlite3_int64*)pArg;
- SimulateIOErrorBenign(1);
- os2Truncate(id, sz);
- SimulateIOErrorBenign(0);
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SYNC_OMITTED: {
- return SQLITE_OK;
- }
- }
- return SQLITE_NOTFOUND;
-}
-
-/*
-** Return the sector size in bytes of the underlying block device for
-** the specified file. This is almost always 512 bytes, but may be
-** larger for some devices.
-**
-** SQLite code assumes this function cannot fail. It also assumes that
-** if two files are created in the same file-system directory (i.e.
-** a database and its journal file) that the sector size will be the
-** same for both.
-*/
-static int os2SectorSize(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_DEFAULT_SECTOR_SIZE;
-}
-
-/*
-** Return a vector of device characteristics.
-*/
-static int os2DeviceCharacteristics(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
-}
-
-
-/*
-** Character set conversion objects used by conversion routines.
-*/
-static UconvObject ucUtf8 = NULL; /* convert between UTF-8 and UCS-2 */
-static UconvObject uclCp = NULL; /* convert between local codepage and UCS-2 */
-
-/*
-** Helper function to initialize the conversion objects from and to UTF-8.
-*/
-static void initUconvObjects( void ){
- if( UniCreateUconvObject( UTF_8, &ucUtf8 ) != ULS_SUCCESS )
- ucUtf8 = NULL;
- if ( UniCreateUconvObject( (UniChar *)L"@path=yes", &uclCp ) != ULS_SUCCESS )
- uclCp = NULL;
-}
-
-/*
-** Helper function to free the conversion objects from and to UTF-8.
-*/
-static void freeUconvObjects( void ){
- if ( ucUtf8 )
- UniFreeUconvObject( ucUtf8 );
- if ( uclCp )
- UniFreeUconvObject( uclCp );
- ucUtf8 = NULL;
- uclCp = NULL;
-}
-
-/*
-** Helper function to convert UTF-8 filenames to local OS/2 codepage.
-** The two-step process: first convert the incoming UTF-8 string
-** into UCS-2 and then from UCS-2 to the current codepage.
-** The returned char pointer has to be freed.
-*/
-static char *convertUtf8PathToCp( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- if( UniStrToUcs( ucUtf8, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* conversion for current codepage which can be used for paths */
- UniStrFromUcs( uclCp, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-/*
-** Helper function to convert filenames from local codepage to UTF-8.
-** The two-step process: first convert the incoming codepage-specific
-** string into UCS-2 and then from UCS-2 to the codepage of UTF-8.
-** The returned char pointer has to be freed.
-**
-** This function is non-static to be able to use this in shell.c and
-** similar applications that take command line arguments.
-*/
-char *convertCpPathToUtf8( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* conversion for current codepage which can be used for paths */
- if( UniStrToUcs( uclCp, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- UniStrFromUcs( ucUtf8, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-
-#ifndef SQLITE_OMIT_WAL
-
-/*
-** Use main database file for interprocess locking. If un-defined
-** a separate file is created for this purpose. The file will be
-** used only to set file locks. There will be no data written to it.
-*/
-#define SQLITE_OS2_NO_WAL_LOCK_FILE
-
-#if 0
-static void _ERR_TRACE( const char *fmt, ... ) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- fflush(stderr);
-}
-#define ERR_TRACE(rc, msg) \
- if( (rc) != SQLITE_OK ) _ERR_TRACE msg;
-#else
-#define ERR_TRACE(rc, msg)
-#endif
-
-/*
-** Helper functions to obtain and relinquish the global mutex. The
-** global mutex is used to protect os2ShmNodeList.
-**
-** Function os2ShmMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
-** statements. e.g.
-**
-** os2ShmEnterMutex()
-** assert( os2ShmMutexHeld() );
-** os2ShmLeaveMutex()
-*/
-static void os2ShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-static void os2ShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-#ifdef SQLITE_DEBUG
-static int os2ShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-int GetCurrentProcessId(void) {
- PPIB pib;
- DosGetInfoBlocks(NULL, &pib);
- return (int)pib->pib_ulpid;
-}
-#endif
-
-/*
-** Object used to represent a the shared memory area for a single log file.
-** When multiple threads all reference the same log-summary, each thread has
-** its own os2File object, but they all point to a single instance of this
-** object. In other words, each log-summary is opened only once per process.
-**
-** os2ShmMutexHeld() must be true when creating or destroying
-** this object or while reading or writing the following fields:
-**
-** nRef
-** pNext
-**
-** The following fields are read-only after the object is created:
-**
-** szRegion
-** hLockFile
-** shmBaseName
-**
-** Either os2ShmNode.mutex must be held or os2ShmNode.nRef==0 and
-** os2ShmMutexHeld() is true when reading or writing any other field
-** in this structure.
-**
-*/
-struct os2ShmNode {
- sqlite3_mutex *mutex; /* Mutex to access this object */
- os2ShmNode *pNext; /* Next in list of all os2ShmNode objects */
-
- int szRegion; /* Size of shared-memory regions */
-
- int nRegion; /* Size of array apRegion */
- void **apRegion; /* Array of pointers to shared-memory regions */
-
- int nRef; /* Number of os2ShmLink objects pointing to this */
- os2ShmLink *pFirst; /* First os2ShmLink object pointing to this */
-
- HFILE hLockFile; /* File used for inter-process memory locking */
- char shmBaseName[1]; /* Name of the memory object !!! must last !!! */
-};
-
-
-/*
-** Structure used internally by this VFS to record the state of an
-** open shared memory connection.
-**
-** The following fields are initialized when this object is created and
-** are read-only thereafter:
-**
-** os2Shm.pShmNode
-** os2Shm.id
-**
-** All other fields are read/write. The os2Shm.pShmNode->mutex must be held
-** while accessing any read/write fields.
-*/
-struct os2ShmLink {
- os2ShmNode *pShmNode; /* The underlying os2ShmNode object */
- os2ShmLink *pNext; /* Next os2Shm with the same os2ShmNode */
- u32 sharedMask; /* Mask of shared locks held */
- u32 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
- u8 id; /* Id of this connection with its os2ShmNode */
-#endif
-};
-
-
-/*
-** A global list of all os2ShmNode objects.
-**
-** The os2ShmMutexHeld() must be true while reading or writing this list.
-*/
-static os2ShmNode *os2ShmNodeList = NULL;
-
-/*
-** Constants used for locking
-*/
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
-#define OS2_SHM_BASE (PENDING_BYTE + 0x10000) /* first lock byte */
-#else
-#define OS2_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
-#endif
-
-#define OS2_SHM_DMS (OS2_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
-
-/*
-** Apply advisory locks for all n bytes beginning at ofst.
-*/
-#define _SHM_UNLCK 1 /* no lock */
-#define _SHM_RDLCK 2 /* shared lock, no wait */
-#define _SHM_WRLCK 3 /* exlusive lock, no wait */
-#define _SHM_WRLCK_WAIT 4 /* exclusive lock, wait */
-static int os2ShmSystemLock(
- os2ShmNode *pNode, /* Apply locks to this open shared-memory segment */
- int lockType, /* _SHM_UNLCK, _SHM_RDLCK, _SHM_WRLCK or _SHM_WRLCK_WAIT */
- int ofst, /* Offset to first byte to be locked/unlocked */
- int nByte /* Number of bytes to lock or unlock */
-){
- APIRET rc;
- FILELOCK area;
- ULONG mode, timeout;
-
- /* Access to the os2ShmNode object is serialized by the caller */
- assert( sqlite3_mutex_held(pNode->mutex) || pNode->nRef==0 );
-
- mode = 1; /* shared lock */
- timeout = 0; /* no wait */
- area.lOffset = ofst;
- area.lRange = nByte;
-
- switch( lockType ) {
- case _SHM_WRLCK_WAIT:
- timeout = (ULONG)-1; /* wait forever */
- case _SHM_WRLCK:
- mode = 0; /* exclusive lock */
- case _SHM_RDLCK:
- rc = DosSetFileLocks(pNode->hLockFile,
- NULL, &area, timeout, mode);
- break;
- /* case _SHM_UNLCK: */
- default:
- rc = DosSetFileLocks(pNode->hLockFile,
- &area, NULL, 0, 0);
- break;
- }
-
- OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",
- pNode->hLockFile,
- rc==SQLITE_OK ? "ok" : "failed",
- lockType==_SHM_UNLCK ? "Unlock" : "Lock",
- rc));
-
- ERR_TRACE(rc, ("os2ShmSystemLock: %d %s\n", rc, pNode->shmBaseName))
-
- return ( rc == 0 ) ? SQLITE_OK : SQLITE_BUSY;
-}
-
-/*
-** Find an os2ShmNode in global list or allocate a new one, if not found.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static int os2OpenSharedMemory( os2File *fd, int szRegion ) {
- os2ShmLink *pLink;
- os2ShmNode *pNode;
- int cbShmName, rc = SQLITE_OK;
- char shmName[CCHMAXPATH + 30];
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- ULONG action;
-#endif
-
- /* We need some additional space at the end to append the region number */
- cbShmName = sprintf(shmName, "\\SHAREMEM\\%s", fd->zFullPathCp );
- if( cbShmName >= CCHMAXPATH-8 )
- return SQLITE_IOERR_SHMOPEN;
-
- /* Replace colon in file name to form a valid shared memory name */
- shmName[10+1] = '!';
-
- /* Allocate link object (we free it later in case of failure) */
- pLink = sqlite3_malloc( sizeof(*pLink) );
- if( !pLink )
- return SQLITE_NOMEM;
-
- /* Access node list */
- os2ShmEnterMutex();
-
- /* Find node by it's shared memory base name */
- for( pNode = os2ShmNodeList;
- pNode && stricmp(shmName, pNode->shmBaseName) != 0;
- pNode = pNode->pNext ) ;
-
- /* Not found: allocate a new node */
- if( !pNode ) {
- pNode = sqlite3_malloc( sizeof(*pNode) + cbShmName );
- if( pNode ) {
- memset(pNode, 0, sizeof(*pNode) );
- pNode->szRegion = szRegion;
- pNode->hLockFile = (HFILE)-1;
- strcpy(pNode->shmBaseName, shmName);
-
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( DosDupHandle(fd->h, &pNode->hLockFile) != 0 ) {
-#else
- sprintf(shmName, "%s-lck", fd->zFullPathCp);
- if( DosOpen((PSZ)shmName, &pNode->hLockFile, &action, 0, FILE_NORMAL,
- OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
- OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR,
- NULL) != 0 ) {
-#endif
- sqlite3_free(pNode);
- rc = SQLITE_IOERR;
- } else {
- pNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( !pNode->mutex ) {
- sqlite3_free(pNode);
- rc = SQLITE_NOMEM;
- }
- }
- } else {
- rc = SQLITE_NOMEM;
- }
-
- if( rc == SQLITE_OK ) {
- pNode->pNext = os2ShmNodeList;
- os2ShmNodeList = pNode;
- } else {
- pNode = NULL;
- }
- } else if( pNode->szRegion != szRegion ) {
- rc = SQLITE_IOERR_SHMSIZE;
- pNode = NULL;
- }
-
- if( pNode ) {
- sqlite3_mutex_enter(pNode->mutex);
-
- memset(pLink, 0, sizeof(*pLink));
-
- pLink->pShmNode = pNode;
- pLink->pNext = pNode->pFirst;
- pNode->pFirst = pLink;
- pNode->nRef++;
-
- fd->pShmLink = pLink;
-
- sqlite3_mutex_leave(pNode->mutex);
-
- } else {
- /* Error occured. Free our link object. */
- sqlite3_free(pLink);
- }
-
- os2ShmLeaveMutex();
-
- ERR_TRACE(rc, ("os2OpenSharedMemory: %d %s\n", rc, fd->zFullPathCp))
-
- return rc;
-}
-
-/*
-** Purge the os2ShmNodeList list of all entries with nRef==0.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static void os2PurgeShmNodes( int deleteFlag ) {
- os2ShmNode *pNode;
- os2ShmNode **ppNode;
-
- os2ShmEnterMutex();
-
- ppNode = &os2ShmNodeList;
-
- while( *ppNode ) {
- pNode = *ppNode;
-
- if( pNode->nRef == 0 ) {
- *ppNode = pNode->pNext;
-
- if( pNode->apRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- while( pNode->nRegion-- ) {
-#ifdef SQLITE_DEBUG
- int rc =
-#endif
- DosFreeMem(pNode->apRegion[pNode->nRegion]);
-
- OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
- (int)GetCurrentProcessId(), pNode->nRegion,
- rc == 0 ? "ok" : "failed"));
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- sqlite3_free(pNode->apRegion);
- }
-
- DosClose(pNode->hLockFile);
-
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( deleteFlag ) {
- char fileName[CCHMAXPATH];
- /* Skip "\\SHAREMEM\\" */
- sprintf(fileName, "%s-lck", pNode->shmBaseName + 10);
- /* restore colon */
- fileName[1] = ':';
-
- DosForceDelete(fileName);
- }
-#endif
-
- sqlite3_mutex_free(pNode->mutex);
-
- sqlite3_free(pNode);
-
- } else {
- ppNode = &pNode->pNext;
- }
- }
-
- os2ShmLeaveMutex();
-}
-
-/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file id. Shared-memory regions
-** are numbered starting from zero. Each shared-memory region is szRegion
-** bytes in size.
-**
-** If an error occurs, an error code is returned and *pp is set to NULL.
-**
-** Otherwise, if the bExtend parameter is 0 and the requested shared-memory
-** region has not been allocated (by any client, including one running in a
-** separate process), then *pp is set to NULL and SQLITE_OK returned. If
-** bExtend is non-zero and the requested shared-memory region has not yet
-** been allocated, it is allocated by this function.
-**
-** If the shared-memory region has already been allocated or is allocated by
-** this call as described above, then it is mapped into this processes
-** address space (if it is not already), *pp is set to point to the mapped
-** memory and SQLITE_OK returned.
-*/
-static int os2ShmMap(
- sqlite3_file *id, /* Handle open on database file */
- int iRegion, /* Region to retrieve */
- int szRegion, /* Size of regions */
- int bExtend, /* True to extend block if necessary */
- void volatile **pp /* OUT: Mapped memory */
-){
- PVOID pvTemp;
- void **apRegion;
- os2ShmNode *pNode;
- int n, rc = SQLITE_OK;
- char shmName[CCHMAXPATH];
- os2File *pFile = (os2File*)id;
-
- *pp = NULL;
-
- if( !pFile->pShmLink )
- rc = os2OpenSharedMemory( pFile, szRegion );
-
- if( rc == SQLITE_OK ) {
- pNode = pFile->pShmLink->pShmNode ;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- assert( szRegion==pNode->szRegion );
-
- /* Unmapped region ? */
- if( iRegion >= pNode->nRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- apRegion = sqlite3_realloc(
- pNode->apRegion, (iRegion + 1) * sizeof(apRegion[0]));
-
- if( apRegion ) {
- pNode->apRegion = apRegion;
-
- while( pNode->nRegion <= iRegion ) {
- sprintf(shmName, "%s-%u",
- pNode->shmBaseName, pNode->nRegion);
-
- if( DosGetNamedSharedMem(&pvTemp, (PSZ)shmName,
- PAG_READ | PAG_WRITE) != NO_ERROR ) {
- if( !bExtend )
- break;
-
- if( DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_ANY) != NO_ERROR &&
- DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT) != NO_ERROR ) {
- rc = SQLITE_NOMEM;
- break;
- }
- }
-
- apRegion[pNode->nRegion++] = pvTemp;
- }
-
- /* zero out remaining entries */
- for( n = pNode->nRegion; n <= iRegion; n++ )
- pNode->apRegion[n] = NULL;
-
- /* Return this region (maybe zero) */
- *pp = pNode->apRegion[iRegion];
- } else {
- rc = SQLITE_NOMEM;
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- } else {
- /* Region has been mapped previously */
- *pp = pNode->apRegion[iRegion];
- }
-
- sqlite3_mutex_leave(pNode->mutex);
- }
-
- ERR_TRACE(rc, ("os2ShmMap: %s iRgn = %d, szRgn = %d, bExt = %d : %d\n",
- pFile->zFullPathCp, iRegion, szRegion, bExtend, rc))
-
- return rc;
-}
-
-/*
-** Close a connection to shared-memory. Delete the underlying
-** storage if deleteFlag is true.
-**
-** If there is no shared memory associated with the connection then this
-** routine is a harmless no-op.
-*/
-static int os2ShmUnmap(
- sqlite3_file *id, /* The underlying database file */
- int deleteFlag /* Delete shared-memory if true */
-){
- os2File *pFile = (os2File*)id;
- os2ShmLink *pLink = pFile->pShmLink;
-
- if( pLink ) {
- int nRef = -1;
- os2ShmLink **ppLink;
- os2ShmNode *pNode = pLink->pShmNode;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- for( ppLink = &pNode->pFirst;
- *ppLink && *ppLink != pLink;
- ppLink = &(*ppLink)->pNext ) ;
-
- assert(*ppLink);
-
- if( *ppLink ) {
- *ppLink = pLink->pNext;
- nRef = --pNode->nRef;
- } else {
- ERR_TRACE(1, ("os2ShmUnmap: link not found ! %s\n",
- pNode->shmBaseName))
- }
-
- pFile->pShmLink = NULL;
- sqlite3_free(pLink);
-
- sqlite3_mutex_leave(pNode->mutex);
-
- if( nRef == 0 )
- os2PurgeShmNodes( deleteFlag );
- }
-
- return SQLITE_OK;
-}
-
-/*
-** Change the lock state for a shared-memory segment.
-**
-** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
-** different here than in posix. In xShmLock(), one can go from unlocked
-** to shared and back or from unlocked to exclusive and back. But one may
-** not go from shared to exclusive or from exclusive to shared.
-*/
-static int os2ShmLock(
- sqlite3_file *id, /* Database file holding the shared memory */
- int ofst, /* First lock to acquire or release */
- int n, /* Number of locks to acquire or release */
- int flags /* What to do with the lock */
-){
- u32 mask; /* Mask of locks to take or release */
- int rc = SQLITE_OK; /* Result code */
- os2File *pFile = (os2File*)id;
- os2ShmLink *p = pFile->pShmLink; /* The shared memory being locked */
- os2ShmLink *pX; /* For looping over all siblings */
- os2ShmNode *pShmNode = p->pShmNode; /* Our node */
-
- assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK );
- assert( n>=1 );
- assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED)
- || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
- || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
- || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
- assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
-
- mask = (u32)((1U<<(ofst+n)) - (1U<<ofst));
- assert( n>1 || mask==(1<<ofst) );
-
-
- sqlite3_mutex_enter(pShmNode->mutex);
-
- if( flags & SQLITE_SHM_UNLOCK ){
- u32 allMask = 0; /* Mask of locks held by siblings */
-
- /* See if any siblings hold this same lock */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
- allMask |= pX->sharedMask;
- }
-
- /* Unlock the system-level locks */
- if( (mask & allMask)==0 ){
- rc = os2ShmSystemLock(pShmNode, _SHM_UNLCK, ofst+OS2_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
-
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- u32 allShared = 0; /* Union of locks held by connections other than "p" */
-
- /* Find out which shared locks are already held by sibling connections.
- ** If any sibling already holds an exclusive lock, go ahead and return
- ** SQLITE_BUSY.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- allShared |= pX->sharedMask;
- }
-
- /* Get shared locks at the system level, if necessary */
- if( rc==SQLITE_OK ){
- if( (allShared & mask)==0 ){
- rc = os2ShmSystemLock(pShmNode, _SHM_RDLCK, ofst+OS2_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
- }
-
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- }
-
- /* Get the exclusive locks at the system level. Then if successful
- ** also mark the local connection as being locked.
- */
- if( rc==SQLITE_OK ){
- rc = os2ShmSystemLock(pShmNode, _SHM_WRLCK, ofst+OS2_SHM_BASE, n);
- if( rc==SQLITE_OK ){
- assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
- }
- }
- }
-
- sqlite3_mutex_leave(pShmNode->mutex);
-
- OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n",
- p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask,
- rc ? "failed" : "ok"));
-
- ERR_TRACE(rc, ("os2ShmLock: ofst = %d, n = %d, flags = 0x%x -> %d \n",
- ofst, n, flags, rc))
-
- return rc;
-}
-
-/*
-** Implement a memory barrier or memory fence on shared memory.
-**
-** All loads and stores begun before the barrier must complete before
-** any load or store begun after the barrier.
-*/
-static void os2ShmBarrier(
- sqlite3_file *id /* Database file holding the shared memory */
-){
- UNUSED_PARAMETER(id);
- os2ShmEnterMutex();
- os2ShmLeaveMutex();
-}
-
-#else
-# define os2ShmMap 0
-# define os2ShmLock 0
-# define os2ShmBarrier 0
-# define os2ShmUnmap 0
-#endif /* #ifndef SQLITE_OMIT_WAL */
-
-
-/*
-** This vector defines all the methods that can operate on an
-** sqlite3_file for os2.
-*/
-static const sqlite3_io_methods os2IoMethod = {
- 2, /* iVersion */
- os2Close, /* xClose */
- os2Read, /* xRead */
- os2Write, /* xWrite */
- os2Truncate, /* xTruncate */
- os2Sync, /* xSync */
- os2FileSize, /* xFileSize */
- os2Lock, /* xLock */
- os2Unlock, /* xUnlock */
- os2CheckReservedLock, /* xCheckReservedLock */
- os2FileControl, /* xFileControl */
- os2SectorSize, /* xSectorSize */
- os2DeviceCharacteristics, /* xDeviceCharacteristics */
- os2ShmMap, /* xShmMap */
- os2ShmLock, /* xShmLock */
- os2ShmBarrier, /* xShmBarrier */
- os2ShmUnmap /* xShmUnmap */
-};
-
-
-/***************************************************************************
-** Here ends the I/O methods that form the sqlite3_io_methods object.
-**
-** The next block of code implements the VFS methods.
-****************************************************************************/
-
-/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
-*/
-static int getTempname(int nBuf, char *zBuf ){
- static const char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- int i, j;
- PSZ zTempPathCp;
- char zTempPath[CCHMAXPATH];
- ULONG ulDriveNum, ulDriveMap;
-
- /* It's odd to simulate an io-error here, but really this is just
- ** using the io-error infrastructure to test that SQLite handles this
- ** function failing.
- */
- SimulateIOError( return SQLITE_IOERR );
-
- if( sqlite3_temp_directory ) {
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", sqlite3_temp_directory);
- } else if( DosScanEnv( (PSZ)"TEMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMPDIR", &zTempPathCp ) == NO_ERROR ) {
- char *zTempPathUTF = convertCpPathToUtf8( (char *)zTempPathCp );
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", zTempPathUTF);
- free( zTempPathUTF );
- } else if( DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ) == NO_ERROR ) {
- zTempPath[0] = (char)('A' + ulDriveNum - 1);
- zTempPath[1] = ':';
- zTempPath[2] = '\0';
- } else {
- zTempPath[0] = '\0';
- }
-
- /* Strip off a trailing slashes or backslashes, otherwise we would get *
- * multiple (back)slashes which causes DosOpen() to fail. *
- * Trailing spaces are not allowed, either. */
- j = sqlite3Strlen30(zTempPath);
- while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' ||
- zTempPath[j-1] == ' ' ) ){
- j--;
- }
- zTempPath[j] = '\0';
-
- /* We use 20 bytes to randomize the name */
- sqlite3_snprintf(nBuf-22, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
- j = sqlite3Strlen30(zBuf);
- sqlite3_randomness( 20, &zBuf[j] );
- for( i = 0; i < 20; i++, j++ ){
- zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
-
- OSTRACE(( "TEMP FILENAME: %s\n", zBuf ));
- return SQLITE_OK;
-}
-
-
-/*
-** Turn a relative pathname into a full pathname. Write the full
-** pathname into zFull[]. zFull[] will be at least pVfs->mxPathname
-** bytes in size.
-*/
-static int os2FullPathname(
- sqlite3_vfs *pVfs, /* Pointer to vfs object */
- const char *zRelative, /* Possibly relative input path */
- int nFull, /* Size of output buffer in bytes */
- char *zFull /* Output buffer */
-){
- char *zRelativeCp = convertUtf8PathToCp( zRelative );
- char zFullCp[CCHMAXPATH] = "\0";
- char *zFullUTF;
- APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME,
- zFullCp, CCHMAXPATH );
- free( zRelativeCp );
- zFullUTF = convertCpPathToUtf8( zFullCp );
- sqlite3_snprintf( nFull, zFull, zFullUTF );
- free( zFullUTF );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-
-/*
-** Open a file.
-*/
-static int os2Open(
- sqlite3_vfs *pVfs, /* Not used */
- const char *zName, /* Name of the file (UTF-8) */
- sqlite3_file *id, /* Write the SQLite file handle here */
- int flags, /* Open mode flags */
- int *pOutFlags /* Status return flags */
-){
- HFILE h;
- ULONG ulOpenFlags = 0;
- ULONG ulOpenMode = 0;
- ULONG ulAction = 0;
- ULONG rc;
- os2File *pFile = (os2File*)id;
- const char *zUtf8Name = zName;
- char *zNameCp;
- char zTmpname[CCHMAXPATH];
-
- int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
- int isCreate = (flags & SQLITE_OPEN_CREATE);
- int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
-#ifndef NDEBUG
- int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
- int isReadonly = (flags & SQLITE_OPEN_READONLY);
- int eType = (flags & 0xFFFFFF00);
- int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
- || eType==SQLITE_OPEN_WAL
- ));
-#endif
-
- UNUSED_PARAMETER(pVfs);
- assert( id!=0 );
-
- /* Check the following statements are true:
- **
- ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
- ** (b) if CREATE is set, then READWRITE must also be set, and
- ** (c) if EXCLUSIVE is set, then CREATE must also be set.
- ** (d) if DELETEONCLOSE is set, then CREATE must also be set.
- */
- assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly));
- assert(isCreate==0 || isReadWrite);
- assert(isExclusive==0 || isCreate);
- assert(isDelete==0 || isCreate);
-
- /* The main DB, main journal, WAL file and master journal are never
- ** automatically deleted. Nor are they ever temporary files. */
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
- assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
-
- /* Assert that the upper layer has set one of the "file-type" flags. */
- assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
- || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
- || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
- );
-
- memset( pFile, 0, sizeof(*pFile) );
- pFile->h = (HFILE)-1;
-
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
- */
- if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- rc = getTempname(CCHMAXPATH, zTmpname);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- zUtf8Name = zTmpname;
- }
-
- if( isReadWrite ){
- ulOpenMode |= OPEN_ACCESS_READWRITE;
- }else{
- ulOpenMode |= OPEN_ACCESS_READONLY;
- }
-
- /* Open in random access mode for possibly better speed. Allow full
- ** sharing because file locks will provide exclusive access when needed.
- ** The handle should not be inherited by child processes and we don't
- ** want popups from the critical error handler.
- */
- ulOpenMode |= OPEN_FLAGS_RANDOM | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR;
-
- /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
- ** created. SQLite doesn't use it to indicate "exclusive access"
- ** as it is usually understood.
- */
- if( isExclusive ){
- /* Creates a new file, only if it does not already exist. */
- /* If the file exists, it fails. */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS;
- }else if( isCreate ){
- /* Open existing file, or create if it doesn't exist */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }else{
- /* Opens a file, only if it exists. */
- ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }
-
- zNameCp = convertUtf8PathToCp( zUtf8Name );
- rc = DosOpen( (PSZ)zNameCp,
- &h,
- &ulAction,
- 0L,
- FILE_NORMAL,
- ulOpenFlags,
- ulOpenMode,
- (PEAOP2)NULL );
- free( zNameCp );
-
- if( rc != NO_ERROR ){
- OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n",
- rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode ));
-
- if( isReadWrite ){
- return os2Open( pVfs, zName, id,
- ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
- pOutFlags );
- }else{
- return SQLITE_CANTOPEN;
- }
- }
-
- if( pOutFlags ){
- *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
- }
-
- os2FullPathname( pVfs, zUtf8Name, sizeof( zTmpname ), zTmpname );
- pFile->zFullPathCp = convertUtf8PathToCp( zTmpname );
- pFile->pMethod = &os2IoMethod;
- pFile->flags = flags;
- pFile->h = h;
-
- OpenCounter(+1);
- OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags ));
- return SQLITE_OK;
-}
-
-/*
-** Delete the named file.
-*/
-static int os2Delete(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to delete */
- int syncDir /* Not used on os2 */
-){
- APIRET rc;
- char *zFilenameCp;
- SimulateIOError( return SQLITE_IOERR_DELETE );
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosDelete( (PSZ)zFilenameCp );
- free( zFilenameCp );
- OSTRACE(( "DELETE \"%s\"\n", zFilename ));
- return (rc == NO_ERROR ||
- rc == ERROR_FILE_NOT_FOUND ||
- rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE;
-}
-
-/*
-** Check the existance and status of a file.
-*/
-static int os2Access(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to check */
- int flags, /* Type of test to make on this file */
- int *pOut /* Write results here */
-){
- APIRET rc;
- FILESTATUS3 fsts3ConfigInfo;
- char *zFilenameCp;
-
- UNUSED_PARAMETER(pVfs);
- SimulateIOError( return SQLITE_IOERR_ACCESS; );
-
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD,
- &fsts3ConfigInfo, sizeof(FILESTATUS3) );
- free( zFilenameCp );
- OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n",
- fsts3ConfigInfo.attrFile, flags, rc ));
-
- switch( flags ){
- case SQLITE_ACCESS_EXISTS:
- /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
- ** as if it does not exist.
- */
- if( fsts3ConfigInfo.cbFile == 0 )
- rc = ERROR_FILE_NOT_FOUND;
- break;
- case SQLITE_ACCESS_READ:
- break;
- case SQLITE_ACCESS_READWRITE:
- if( fsts3ConfigInfo.attrFile & FILE_READONLY )
- rc = ERROR_ACCESS_DENIED;
- break;
- default:
- rc = ERROR_FILE_NOT_FOUND;
- assert( !"Invalid flags argument" );
- }
-
- *pOut = (rc == NO_ERROR);
- OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut ));
-
- return SQLITE_OK;
-}
-
-
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
-static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){
- HMODULE hmod;
- APIRET rc;
- char *zFilenameCp = convertUtf8PathToCp(zFilename);
- rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod);
- free(zFilenameCp);
- return rc != NO_ERROR ? 0 : (void*)hmod;
-}
-/*
-** A no-op since the error code is returned on the DosLoadModule call.
-** os2Dlopen returns zero if DosLoadModule is not successful.
-*/
-static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
-/* no-op */
-}
-static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
- PFN pfn;
- APIRET rc;
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)zSymbol, &pfn);
- if( rc != NO_ERROR ){
- /* if the symbol itself was not found, search again for the same
- * symbol with an extra underscore, that might be needed depending
- * on the calling convention */
- char _zSymbol[256] = "_";
- strncat(_zSymbol, zSymbol, 254);
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn);
- }
- return rc != NO_ERROR ? 0 : (void(*)(void))pfn;
-}
-static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){
- DosFreeModule((HMODULE)pHandle);
-}
-#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
- #define os2DlOpen 0
- #define os2DlError 0
- #define os2DlSym 0
- #define os2DlClose 0
-#endif
-
-
-/*
-** Write up to nBuf bytes of randomness into zBuf.
-*/
-static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
- int n = 0;
-#if defined(SQLITE_TEST)
- n = nBuf;
- memset(zBuf, 0, nBuf);
-#else
- int i;
- PPIB ppib;
- PTIB ptib;
- DATETIME dt;
- static unsigned c = 0;
- /* Ordered by variation probability */
- static ULONG svIdx[6] = { QSV_MS_COUNT, QSV_TIME_LOW,
- QSV_MAXPRMEM, QSV_MAXSHMEM,
- QSV_TOTAVAILMEM, QSV_TOTRESMEM };
-
- /* 8 bytes; timezone and weekday don't increase the randomness much */
- if( (int)sizeof(dt)-3 <= nBuf - n ){
- c += 0x0100;
- DosGetDateTime(&dt);
- dt.year = (USHORT)((dt.year - 1900) | c);
- memcpy(&zBuf[n], &dt, sizeof(dt)-3);
- n += sizeof(dt)-3;
- }
-
- /* 4 bytes; PIDs and TIDs are 16 bit internally, so combine them */
- if( (int)sizeof(ULONG) <= nBuf - n ){
- DosGetInfoBlocks(&ptib, &ppib);
- *(PULONG)&zBuf[n] = MAKELONG(ppib->pib_ulpid,
- ptib->tib_ptib2->tib2_ultid);
- n += sizeof(ULONG);
- }
-
- /* Up to 6 * 4 bytes; variables depend on the system state */
- for( i = 0; i < 6 && (int)sizeof(ULONG) <= nBuf - n; i++ ){
- DosQuerySysInfo(svIdx[i], svIdx[i],
- (PULONG)&zBuf[n], sizeof(ULONG));
- n += sizeof(ULONG);
- }
-#endif
-
- return n;
-}
-
-/*
-** Sleep for a little while. Return the amount of time slept.
-** The argument is the number of microseconds we want to sleep.
-** The return value is the number of microseconds of sleep actually
-** requested from the underlying operating system, a number which
-** might be greater than or equal to the argument, but not less
-** than the argument.
-*/
-static int os2Sleep( sqlite3_vfs *pVfs, int microsec ){
- DosSleep( (microsec/1000) );
- return microsec;
-}
-
-/*
-** The following variable, if set to a non-zero value, becomes the result
-** returned from sqlite3OsCurrentTime(). This is used for testing.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_current_time = 0;
-#endif
-
-/*
-** Find the current time (in Universal Coordinated Time). Write into *piNow
-** the current time and date as a Julian Day number times 86_400_000. In
-** other words, write into *piNow the number of milliseconds since the Julian
-** epoch of noon in Greenwich on November 24, 4714 B.C according to the
-** proleptic Gregorian calendar.
-**
-** On success, return 0. Return 1 if the time and date cannot be found.
-*/
-static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
-#ifdef SQLITE_TEST
- static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
-#endif
- int year, month, datepart, timepart;
-
- DATETIME dt;
- DosGetDateTime( &dt );
-
- year = dt.year;
- month = dt.month;
-
- /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html
- ** http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c
- ** Calculate the Julian days
- */
- datepart = (int)dt.day - 32076 +
- 1461*(year + 4800 + (month - 14)/12)/4 +
- 367*(month - 2 - (month - 14)/12*12)/12 -
- 3*((year + 4900 + (month - 14)/12)/100)/4;
-
- /* Time in milliseconds, hours to noon added */
- timepart = 12*3600*1000 + dt.hundredths*10 + dt.seconds*1000 +
- ((int)dt.minutes + dt.timezone)*60*1000 + dt.hours*3600*1000;
-
- *piNow = (sqlite3_int64)datepart*86400*1000 + timepart;
-
-#ifdef SQLITE_TEST
- if( sqlite3_current_time ){
- *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
- }
-#endif
-
- UNUSED_PARAMETER(pVfs);
- return 0;
-}
-
-/*
-** Find the current time (in Universal Coordinated Time). Write the
-** current time and date as a Julian Day number into *prNow and
-** return 0. Return 1 if the time and date cannot be found.
-*/
-static int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){
- int rc;
- sqlite3_int64 i;
- rc = os2CurrentTimeInt64(pVfs, &i);
- if( !rc ){
- *prNow = i/86400000.0;
- }
- return rc;
-}
-
-/*
-** The idea is that this function works like a combination of
-** GetLastError() and FormatMessage() on windows (or errno and
-** strerror_r() on unix). After an error is returned by an OS
-** function, SQLite calls this function with zBuf pointing to
-** a buffer of nBuf bytes. The OS layer should populate the
-** buffer with a nul-terminated UTF-8 encoded error message
-** describing the last IO error to have occurred within the calling
-** thread.
-**
-** If the error message is too large for the supplied buffer,
-** it should be truncated. The return value of xGetLastError
-** is zero if the error message fits in the buffer, or non-zero
-** otherwise (if the message was truncated). If non-zero is returned,
-** then it is not necessary to include the nul-terminator character
-** in the output buffer.
-**
-** Not supplying an error message will have no adverse effect
-** on SQLite. It is fine to have an implementation that never
-** returns an error message:
-**
-** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
-** assert(zBuf[0]=='\0');
-** return 0;
-** }
-**
-** However if an error message is supplied, it will be incorporated
-** by sqlite into the error message available to the user using
-** sqlite3_errmsg(), possibly making IO errors easier to debug.
-*/
-static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- assert(zBuf[0]=='\0');
- return 0;
-}
-
-/*
-** Initialize and deinitialize the operating system interface.
-*/
-SQLITE_API int sqlite3_os_init(void){
- static sqlite3_vfs os2Vfs = {
- 3, /* iVersion */
- sizeof(os2File), /* szOsFile */
- CCHMAXPATH, /* mxPathname */
- 0, /* pNext */
- "os2", /* zName */
- 0, /* pAppData */
-
- os2Open, /* xOpen */
- os2Delete, /* xDelete */
- os2Access, /* xAccess */
- os2FullPathname, /* xFullPathname */
- os2DlOpen, /* xDlOpen */
- os2DlError, /* xDlError */
- os2DlSym, /* xDlSym */
- os2DlClose, /* xDlClose */
- os2Randomness, /* xRandomness */
- os2Sleep, /* xSleep */
- os2CurrentTime, /* xCurrentTime */
- os2GetLastError, /* xGetLastError */
- os2CurrentTimeInt64, /* xCurrentTimeInt64 */
- 0, /* xSetSystemCall */
- 0, /* xGetSystemCall */
- 0 /* xNextSystemCall */
- };
- sqlite3_vfs_register(&os2Vfs, 1);
- initUconvObjects();
-/* sqlite3OSTrace = 1; */
- return SQLITE_OK;
-}
-SQLITE_API int sqlite3_os_end(void){
- freeUconvObjects();
- return SQLITE_OK;
-}
-
-#endif /* SQLITE_OS_OS2 */
-
-/************** End of os_os2.c **********************************************/
/************** Begin file os_unix.c *****************************************/
/*
** 2004 May 22
@@ -24416,6 +29229,7 @@ SQLITE_API int sqlite3_os_end(void){
** * Definitions of sqlite3_vfs objects for all locking methods
** plus implementations of sqlite3_os_init() and sqlite3_os_end().
*/
+/* #include "sqliteInt.h" */
#if SQLITE_OS_UNIX /* This file is used on unix only */
/*
@@ -24443,42 +29257,17 @@ SQLITE_API int sqlite3_os_end(void){
# endif
#endif
-/*
-** Define the OS_VXWORKS pre-processor macro to 1 if building on
-** vxworks, or 0 otherwise.
-*/
-#ifndef OS_VXWORKS
-# if defined(__RTP__) || defined(_WRS_KERNEL)
-# define OS_VXWORKS 1
-# else
-# define OS_VXWORKS 0
-# endif
+/* Use pread() and pwrite() if they are available */
+#if defined(__APPLE__)
+# define HAVE_PREAD 1
+# define HAVE_PWRITE 1
#endif
-
-/*
-** These #defines should enable >2GB file support on Posix if the
-** underlying operating system supports it. If the OS lacks
-** large file support, these should be no-ops.
-**
-** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
-** on the compiler command line. This is necessary if you are compiling
-** on a recent machine (ex: RedHat 7.2) but you want your code to work
-** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
-** without this option, LFS is enable. But LFS does not exist in the kernel
-** in RedHat 6.0, so the code won't work. Hence, for maximum binary
-** portability you should omit LFS.
-**
-** The previous paragraph was written in 2005. (This paragraph is written
-** on 2008-11-28.) These days, all Linux kernels support large files, so
-** you should probably leave LFS enabled. But some embedded platforms might
-** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful.
-*/
-#ifndef SQLITE_DISABLE_LFS
-# define _LARGE_FILE 1
-# ifndef _FILE_OFFSET_BITS
-# define _FILE_OFFSET_BITS 64
-# endif
-# define _LARGEFILE_SOURCE 1
+#if defined(HAVE_PREAD64) && defined(HAVE_PWRITE64)
+# undef USE_PREAD
+# define USE_PREAD64 1
+#elif defined(HAVE_PREAD) && defined(HAVE_PWRITE)
+# undef USE_PREAD64
+# define USE_PREAD 1
#endif
/*
@@ -24491,22 +29280,34 @@ SQLITE_API int sqlite3_os_end(void){
/* #include <time.h> */
#include <sys/time.h>
#include <errno.h>
-#ifndef SQLITE_OMIT_WAL
-#include <sys/mman.h>
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+# include <sys/mman.h>
#endif
#if SQLITE_ENABLE_LOCKING_STYLE
# include <sys/ioctl.h>
-# if OS_VXWORKS
-# include <semaphore.h>
-# include <limits.h>
-# else
-# include <sys/file.h>
-# include <sys/param.h>
-# endif
+# include <sys/file.h>
+# include <sys/param.h>
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
-#if defined(__APPLE__) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
+#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \
+ (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000))
+# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \
+ && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0))
+# define HAVE_GETHOSTUUID 1
+# else
+# warning "gethostuuid() is disabled."
+# endif
+#endif
+
+
+#if OS_VXWORKS
+/* # include <sys/ioctl.h> */
+# include <semaphore.h>
+# include <limits.h>
+#endif /* OS_VXWORKS */
+
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
# include <sys/mount.h>
#endif
@@ -24536,8 +29337,8 @@ SQLITE_API int sqlite3_os_end(void){
#endif
/*
- ** Default permissions when creating auto proxy dir
- */
+** Default permissions when creating auto proxy dir
+*/
#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS
# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755
#endif
@@ -24548,6 +29349,15 @@ SQLITE_API int sqlite3_os_end(void){
#define MAX_PATHNAME 512
/*
+** Maximum supported symbolic links
+*/
+#define SQLITE_MAX_SYMLINKS 100
+
+/* Always cast the getpid() return type for compatibility with
+** kernel modules in VxWorks. */
+#define osGetpid(X) (pid_t)getpid()
+
+/*
** Only set the lastErrno if the error code is a real error and not
** a normal expected return code of SQLITE_BUSY or SQLITE_OK
*/
@@ -24578,16 +29388,28 @@ struct UnixUnusedFd {
typedef struct unixFile unixFile;
struct unixFile {
sqlite3_io_methods const *pMethod; /* Always the first entry */
+ sqlite3_vfs *pVfs; /* The VFS that created this unixFile */
unixInodeInfo *pInode; /* Info about locks on this inode */
int h; /* The file descriptor */
unsigned char eFileLock; /* The type of lock held on this fd */
- unsigned char ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
+ unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */
int lastErrno; /* The unix errno from last I/O error */
void *lockingContext; /* Locking style specific state */
UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */
const char *zPath; /* Name of the file */
unixShm *pShm; /* Shared memory segment information */
int szChunk; /* Configured by FCNTL_CHUNK_SIZE */
+#if SQLITE_MAX_MMAP_SIZE>0
+ int nFetchOut; /* Number of outstanding xFetch refs */
+ sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */
+ sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */
+ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
+ void *pMapRegion; /* Memory mapped region */
+#endif
+#ifdef __QNXNTO__
+ int sectorSize; /* Device sector size */
+ int deviceCharacteristics; /* Precomputed device characteristics */
+#endif
#if SQLITE_ENABLE_LOCKING_STYLE
int openFlags; /* The flags specified at open() */
#endif
@@ -24595,10 +29417,9 @@ struct unixFile {
unsigned fsFlags; /* cached details from statfs() */
#endif
#if OS_VXWORKS
- int isDelete; /* Delete on close if true */
struct vxworksFileId *pId; /* Unique file ID */
#endif
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* The next group of variables are used to track whether or not the
** transaction counter in bytes 24-27 of database files are updated
** whenever any part of the database changes. An assertion fault will
@@ -24609,7 +29430,9 @@ struct unixFile {
unsigned char transCntrChng; /* True if the transaction counter changed */
unsigned char dbUpdate; /* True if any part of database file changed */
unsigned char inNormalWrite; /* True if in a normal write operation */
+
#endif
+
#ifdef SQLITE_TEST
/* In test mode, increase the size of this structure a bit so that
** it is larger than the struct CrashFile defined in test6.c.
@@ -24618,6 +29441,12 @@ struct unixFile {
#endif
};
+/* This variable holds the process id (pid) from when the xRandomness()
+** method was called. If xOpen() is called from a different process id,
+** indicating that a fork() has occurred, the PRNG will be reset.
+*/
+static pid_t randomnessPid = 0;
+
/*
** Allowed values for the unixFile.ctrlFlags bitmask:
*/
@@ -24629,6 +29458,10 @@ struct unixFile {
#else
# define UNIXFILE_DIRSYNC 0x00
#endif
+#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
+#define UNIXFILE_DELETE 0x20 /* Delete on close */
+#define UNIXFILE_URI 0x40 /* Filename might have query parameters */
+#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */
/*
** Include code that is common to all os_*.c files
@@ -24666,24 +29499,14 @@ struct unixFile {
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
-# ifndef SQLITE_DEBUG_OS_TRACE
-# define SQLITE_DEBUG_OS_TRACE 0
-# endif
- int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
-# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
-#else
-# define OSTRACE(X)
-#endif
-
/*
** Macros for performance tracing. Normally turned off. Only works
** on i486 hardware.
*/
#ifdef SQLITE_PERFORMANCE_TRACE
-/*
-** hwtime.h contains inline assembler code for implementing
+/*
+** hwtime.h contains inline assembler code for implementing
** high-performance timing routines.
*/
/************** Include hwtime.h in the middle of os_common.h ****************/
@@ -24703,8 +29526,8 @@ struct unixFile {
** This file contains inline asm code for retrieving "high-performance"
** counters for x86 class CPUs.
*/
-#ifndef _HWTIME_H_
-#define _HWTIME_H_
+#ifndef SQLITE_HWTIME_H
+#define SQLITE_HWTIME_H
/*
** The following routine only works on pentium-class (or newer) processors.
@@ -24772,7 +29595,7 @@ SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
#endif
-#endif /* !defined(_HWTIME_H_) */
+#endif /* !defined(SQLITE_HWTIME_H) */
/************** End of hwtime.h **********************************************/
/************** Continuing where we left off in os_common.h ******************/
@@ -24793,14 +29616,14 @@ static sqlite_uint64 g_elapsed;
** of code will give us the ability to simulate a disk I/O error. This
** is used for testing the I/O recovery logic.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_io_error_hit;
+SQLITE_API extern int sqlite3_io_error_hardhit;
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_persist;
+SQLITE_API extern int sqlite3_io_error_benign;
+SQLITE_API extern int sqlite3_diskfull_pending;
+SQLITE_API extern int sqlite3_diskfull;
#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
#define SimulateIOError(CODE) \
if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
@@ -24826,17 +29649,17 @@ static void local_ioerr(){
#define SimulateIOErrorBenign(X)
#define SimulateIOError(A)
#define SimulateDiskfullError(A)
-#endif
+#endif /* defined(SQLITE_TEST) */
/*
** When testing, keep a count of the number of open files.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_open_file_count;
#define OpenCounter(X) sqlite3_open_file_count+=(X)
#else
#define OpenCounter(X)
-#endif
+#endif /* defined(SQLITE_TEST) */
#endif /* !defined(_OS_COMMON_H_) */
@@ -24871,6 +29694,25 @@ SQLITE_API int sqlite3_open_file_count = 0;
#endif
/*
+** HAVE_MREMAP defaults to true on Linux and false everywhere else.
+*/
+#if !defined(HAVE_MREMAP)
+# if defined(__linux__) && defined(_GNU_SOURCE)
+# define HAVE_MREMAP 1
+# else
+# define HAVE_MREMAP 0
+# endif
+#endif
+
+/*
+** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek()
+** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined.
+*/
+#ifdef __ANDROID__
+# define lseek lseek64
+#endif
+
+/*
** Different Unix systems declare open() in different ways. Same use
** open(const char*,int,mode_t). Others use open(const char*,int,...).
** The difference is important when using a pointer to the function.
@@ -24884,6 +29726,7 @@ static int posixOpen(const char *zFile, int flags, int mode){
/* Forward reference */
static int openDirectory(const char*, int*);
+static int unixGetpagesize(void);
/*
** Many system calls are accessed through pointer-to-functions so that
@@ -24892,7 +29735,7 @@ static int openDirectory(const char*, int*);
** to all overrideable system calls.
*/
static struct unix_syscall {
- const char *zName; /* Name of the sytem call */
+ const char *zName; /* Name of the system call */
sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
sqlite3_syscall_ptr pDefault; /* Default value */
} aSyscall[] = {
@@ -24946,7 +29789,7 @@ static struct unix_syscall {
#else
{ "pread64", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent)
+#define osPread64 ((ssize_t(*)(int,void*,size_t,off64_t))aSyscall[10].pCurrent)
{ "write", (sqlite3_syscall_ptr)write, 0 },
#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent)
@@ -24964,14 +29807,10 @@ static struct unix_syscall {
#else
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
#endif
-#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\
+#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
-#if SQLITE_ENABLE_LOCKING_STYLE
- { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
-#else
- { "fchmod", (sqlite3_syscall_ptr)0, 0 },
-#endif
+ { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
@@ -24987,8 +29826,80 @@ static struct unix_syscall {
{ "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 },
#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent)
+ { "mkdir", (sqlite3_syscall_ptr)mkdir, 0 },
+#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent)
+
+ { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 },
+#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent)
+
+#if defined(HAVE_FCHOWN)
+ { "fchown", (sqlite3_syscall_ptr)fchown, 0 },
+#else
+ { "fchown", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent)
+
+ { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 },
+#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent)
+
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+ { "mmap", (sqlite3_syscall_ptr)mmap, 0 },
+#else
+ { "mmap", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent)
+
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+ { "munmap", (sqlite3_syscall_ptr)munmap, 0 },
+#else
+ { "munmap", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent)
+
+#if HAVE_MREMAP && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
+ { "mremap", (sqlite3_syscall_ptr)mremap, 0 },
+#else
+ { "mremap", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent)
+
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+ { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 },
+#else
+ { "getpagesize", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent)
+
+#if defined(HAVE_READLINK)
+ { "readlink", (sqlite3_syscall_ptr)readlink, 0 },
+#else
+ { "readlink", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent)
+
+#if defined(HAVE_LSTAT)
+ { "lstat", (sqlite3_syscall_ptr)lstat, 0 },
+#else
+ { "lstat", (sqlite3_syscall_ptr)0, 0 },
+#endif
+#define osLstat ((int(*)(const char*,struct stat*))aSyscall[27].pCurrent)
+
}; /* End of the overrideable system calls */
+
+/*
+** On some systems, calls to fchown() will trigger a message in a security
+** log if they come from non-root processes. So avoid calling fchown() if
+** we are not running as root.
+*/
+static int robustFchown(int fd, uid_t uid, gid_t gid){
+#if defined(HAVE_FCHOWN)
+ return osGeteuid() ? 0 : osFchown(fd,uid,gid);
+#else
+ return 0;
+#endif
+}
+
/*
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
** "unix" VFSes. Return SQLITE_OK opon successfully updating the
@@ -25073,12 +29984,66 @@ static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){
}
/*
-** Retry open() calls that fail due to EINTR
+** Do not accept any file descriptor less than this value, in order to avoid
+** opening database file using file descriptors that are commonly used for
+** standard input, output, and error.
*/
-static int robust_open(const char *z, int f, int m){
- int rc;
- do{ rc = osOpen(z,f,m); }while( rc<0 && errno==EINTR );
- return rc;
+#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR
+# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3
+#endif
+
+/*
+** Invoke open(). Do so multiple times, until it either succeeds or
+** fails for some reason other than EINTR.
+**
+** If the file creation mode "m" is 0 then set it to the default for
+** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally
+** 0644) as modified by the system umask. If m is not 0, then
+** make the file creation mode be exactly m ignoring the umask.
+**
+** The m parameter will be non-zero only when creating -wal, -journal,
+** and -shm files. We want those files to have *exactly* the same
+** permissions as their original database, unadulterated by the umask.
+** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a
+** transaction crashes and leaves behind hot journals, then any
+** process that is able to write to the database will also be able to
+** recover the hot journals.
+*/
+static int robust_open(const char *z, int f, mode_t m){
+ int fd;
+ mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS;
+ while(1){
+#if defined(O_CLOEXEC)
+ fd = osOpen(z,f|O_CLOEXEC,m2);
+#else
+ fd = osOpen(z,f,m2);
+#endif
+ if( fd<0 ){
+ if( errno==EINTR ) continue;
+ break;
+ }
+ if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break;
+ osClose(fd);
+ sqlite3_log(SQLITE_WARNING,
+ "attempt to open \"%s\" as file descriptor %d", z, fd);
+ fd = -1;
+ if( osOpen("/dev/null", f, m)<0 ) break;
+ }
+ if( fd>=0 ){
+ if( m!=0 ){
+ struct stat statbuf;
+ if( osFstat(fd, &statbuf)==0
+ && statbuf.st_size==0
+ && (statbuf.st_mode&0777)!=m
+ ){
+ osFchmod(fd, m);
+ }
+ }
+#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0)
+ osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
+#endif
+ }
+ return fd;
}
/*
@@ -25096,22 +30061,22 @@ static int robust_open(const char *z, int f, int m){
** unixEnterLeave()
*/
static void unixEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
static void unixLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#ifdef SQLITE_DEBUG
static int unixMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#endif
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_HAVE_OS_TRACE
/*
** Helper function for printing out trace information from debugging
-** binaries. This returns the string represetation of the supplied
+** binaries. This returns the string representation of the supplied
** integer lock-type.
*/
static const char *azFileLock(int eFileLock){
@@ -25188,9 +30153,22 @@ static int lockTrace(int fd, int op, struct flock *p){
/*
** Retry ftruncate() calls that fail due to EINTR
+**
+** All calls to ftruncate() within this file should be made through
+** this wrapper. On the Android platform, bypassing the logic below
+** could lead to a corrupt database.
*/
static int robust_ftruncate(int h, sqlite3_int64 sz){
int rc;
+#ifdef __ANDROID__
+ /* On Android, ftruncate() always uses 32-bit offsets, even if
+ ** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to
+ ** truncate a file to any size larger than 2GiB. Silently ignore any
+ ** such attempts. */
+ if( sz>(sqlite3_int64)0x7FFFFFFF ){
+ rc = SQLITE_OK;
+ }else
+#endif
do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR );
return rc;
}
@@ -25206,23 +30184,12 @@ static int robust_ftruncate(int h, sqlite3_int64 sz){
** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately.
*/
static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
+ assert( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
+ (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) );
switch (posixError) {
-#if 0
- /* At one point this code was not commented out. In theory, this branch
- ** should never be hit, as this function should only be called after
- ** a locking-related function (i.e. fcntl()) has returned non-zero with
- ** the value of errno as the first argument. Since a system call has failed,
- ** errno should be non-zero.
- **
- ** Despite this, if errno really is zero, we still don't want to return
- ** SQLITE_OK. The system call failed, and *some* SQLite error should be
- ** propagated back to the caller. Commenting this branch out means errno==0
- ** will be handled by the "default:" case below.
- */
- case 0:
- return SQLITE_OK;
-#endif
-
+ case EACCES:
case EAGAIN:
case ETIMEDOUT:
case EBUSY:
@@ -25232,58 +30199,15 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) {
* introspection, in which it actually means what it says */
return SQLITE_BUSY;
- case EACCES:
- /* EACCES is like EAGAIN during locking operations, but not any other time*/
- if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
- (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
- (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
- return SQLITE_BUSY;
- }
- /* else fall through */
case EPERM:
return SQLITE_PERM;
- /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
- ** this module never makes such a call. And the code in SQLite itself
- ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
- ** this case is also commented out. If the system does set errno to EDEADLK,
- ** the default SQLITE_IOERR_XXX code will be returned. */
-#if 0
- case EDEADLK:
- return SQLITE_IOERR_BLOCKED;
-#endif
-
-#if EOPNOTSUPP!=ENOTSUP
- case EOPNOTSUPP:
- /* something went terribly awry, unless during file system support
- * introspection, in which it actually means what it says */
-#endif
-#ifdef ENOTSUP
- case ENOTSUP:
- /* invalid fd, unless during file system support introspection, in which
- * it actually means what it says */
-#endif
- case EIO:
- case EBADF:
- case EINVAL:
- case ENOTCONN:
- case ENODEV:
- case ENXIO:
- case ENOENT:
-#ifdef ESTALE /* ESTALE is not defined on Interix systems */
- case ESTALE:
-#endif
- case ENOSYS:
- /* these should force the client to close the file and reconnect */
-
default:
return sqliteIOErr;
}
}
-
/******************************************************************************
****************** Begin Unique File ID Utility Used By VxWorks ***************
**
@@ -25369,7 +30293,7 @@ static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){
assert( zAbsoluteName[0]=='/' );
n = (int)strlen(zAbsoluteName);
- pNew = sqlite3_malloc( sizeof(*pNew) + (n+1) );
+ pNew = sqlite3_malloc64( sizeof(*pNew) + (n+1) );
if( pNew==0 ) return 0;
pNew->zCanonicalName = (char*)&pNew[1];
memcpy(pNew->zCanonicalName, zAbsoluteName, n+1);
@@ -25561,7 +30485,7 @@ static unixInodeInfo *inodeList = 0;
/*
**
-** This function - unixLogError_x(), is only ever called via the macro
+** This function - unixLogErrorAtLine(), is only ever called via the macro
** unixLogError().
**
** It is invoked after an error occurs in an OS function and errno has been
@@ -25572,7 +30496,7 @@ static unixInodeInfo *inodeList = 0;
** The first argument passed to the macro should be the error code that
** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
** The two subsequent arguments should be the name of the OS function that
-** failed (e.g. "unlink", "open") and the the associated file-system path,
+** failed (e.g. "unlink", "open") and the associated file-system path,
** if any.
*/
#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__)
@@ -25595,7 +30519,7 @@ static int unixLogErrorAtLine(
zErr = aErr;
/* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined,
- ** assume that the system provides the the GNU version of strerror_r() that
+ ** assume that the system provides the GNU version of strerror_r() that
** returns a pointer to a buffer containing the error message. That pointer
** may point to aErr[], or it may point to some static storage somewhere.
** Otherwise, assume that the system provides the POSIX version of
@@ -25619,7 +30543,6 @@ static int unixLogErrorAtLine(
zErr = strerror(iErrno);
#endif
- assert( errcode!=SQLITE_OK );
if( zPath==0 ) zPath = "";
sqlite3_log(errcode,
"os_unix.c:%d: (%d) %s(%s) - %s",
@@ -25650,6 +30573,14 @@ static void robust_close(unixFile *pFile, int h, int lineno){
}
/*
+** Set the pFile->lastErrno. Do this in a subroutine as that provides
+** a convenient place to set a breakpoint.
+*/
+static void storeLastErrno(unixFile *pFile, int error){
+ pFile->lastErrno = error;
+}
+
+/*
** Close all file descriptors accumuated in the unixInodeInfo->pUnused list.
*/
static void closePendingFds(unixFile *pFile){
@@ -25722,8 +30653,8 @@ static int findInodeInfo(
fd = pFile->h;
rc = osFstat(fd, &statbuf);
if( rc!=0 ){
- pFile->lastErrno = errno;
-#ifdef EOVERFLOW
+ storeLastErrno(pFile, errno);
+#if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS)
if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS;
#endif
return SQLITE_IOERR;
@@ -25743,12 +30674,12 @@ static int findInodeInfo(
if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){
do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
if( rc!=1 ){
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
return SQLITE_IOERR;
}
rc = osFstat(fd, &statbuf);
if( rc!=0 ){
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
return SQLITE_IOERR;
}
}
@@ -25766,9 +30697,9 @@ static int findInodeInfo(
pInode = pInode->pNext;
}
if( pInode==0 ){
- pInode = sqlite3_malloc( sizeof(*pInode) );
+ pInode = sqlite3_malloc64( sizeof(*pInode) );
if( pInode==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(pInode, 0, sizeof(*pInode));
memcpy(&pInode->fileId, &fileId, sizeof(fileId));
@@ -25784,6 +30715,55 @@ static int findInodeInfo(
return SQLITE_OK;
}
+/*
+** Return TRUE if pFile has been renamed or unlinked since it was first opened.
+*/
+static int fileHasMoved(unixFile *pFile){
+#if OS_VXWORKS
+ return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId;
+#else
+ struct stat buf;
+ return pFile->pInode!=0 &&
+ (osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino);
+#endif
+}
+
+
+/*
+** Check a unixFile that is a database. Verify the following:
+**
+** (1) There is exactly one hard link on the file
+** (2) The file is not a symbolic link
+** (3) The file has not been renamed or unlinked
+**
+** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right.
+*/
+static void verifyDbFile(unixFile *pFile){
+ struct stat buf;
+ int rc;
+
+ /* These verifications occurs for the main database only */
+ if( pFile->ctrlFlags & UNIXFILE_NOLOCK ) return;
+
+ rc = osFstat(pFile->h, &buf);
+ if( rc!=0 ){
+ sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath);
+ return;
+ }
+ if( buf.st_nlink==0 ){
+ sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath);
+ return;
+ }
+ if( buf.st_nlink>1 ){
+ sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath);
+ return;
+ }
+ if( fileHasMoved(pFile) ){
+ sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
+ return;
+ }
+}
+
/*
** This routine checks if there is a RESERVED lock held on the specified
@@ -25799,6 +30779,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
+ assert( pFile->eFileLock<=SHARED_LOCK );
unixEnterMutex(); /* Because pFile->pInode is shared across threads */
/* Check if a thread in this process holds such a lock */
@@ -25817,7 +30798,7 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){
lock.l_type = F_WRLCK;
if( osFcntl(pFile->h, F_GETLK, &lock) ){
rc = SQLITE_IOERR_CHECKRESERVEDLOCK;
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
} else if( lock.l_type!=F_UNLCK ){
reserved = 1;
}
@@ -25855,9 +30836,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
unixInodeInfo *pInode = pFile->pInode;
assert( unixMutexHeld() );
assert( pInode!=0 );
- if( ((pFile->ctrlFlags & UNIXFILE_EXCL)!=0 || pInode->bProcessLock)
- && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0)
- ){
+ if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){
if( pInode->bProcessLock==0 ){
struct flock lock;
assert( pInode->nLock==0 );
@@ -25907,7 +30886,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** lock transitions in terms of the POSIX advisory shared and exclusive
** lock primitives (called read-locks and write-locks below, to avoid
** confusion with SQLite lock names). The algorithms are complicated
- ** slightly in order to be compatible with windows systems simultaneously
+ ** slightly in order to be compatible with Windows95 systems simultaneously
** accessing the same database file, in case that is ever required.
**
** Symbols defined in os.h indentify the 'pending byte' and the 'reserved
@@ -25915,8 +30894,14 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** range', a range of 510 bytes at a well known offset.
**
** To obtain a SHARED lock, a read-lock is obtained on the 'pending
- ** byte'. If this is successful, a random byte from the 'shared byte
- ** range' is read-locked and the lock on the 'pending byte' released.
+ ** byte'. If this is successful, 'shared byte range' is read-locked
+ ** and the lock on the 'pending byte' released. (Legacy note: When
+ ** SQLite was first developed, Windows95 systems were still very common,
+ ** and Widnows95 lacks a shared-lock capability. So on Windows95, a
+ ** single randomly selected by from the 'shared byte range' is locked.
+ ** Windows95 is now pretty much extinct, but this work-around for the
+ ** lack of shared-locks on Windows95 lives on, for backwards
+ ** compatibility.)
**
** A process may only obtain a RESERVED lock after it has a SHARED lock.
** A RESERVED lock is implemented by grabbing a write-lock on the
@@ -25935,22 +30920,18 @@ static int unixLock(sqlite3_file *id, int eFileLock){
** range'. Since all other locks require a read-lock on one of the bytes
** within this range, this ensures that no other locks are held on the
** database.
- **
- ** The reason a single byte cannot be used instead of the 'shared byte
- ** range' is that some versions of windows do not support read-locks. By
- ** locking a random byte from a range, concurrent SHARED locks may exist
- ** even if the locking primitive used is always a write-lock.
*/
int rc = SQLITE_OK;
unixFile *pFile = (unixFile*)id;
- unixInodeInfo *pInode = pFile->pInode;
+ unixInodeInfo *pInode;
struct flock lock;
int tErrno = 0;
assert( pFile );
OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h,
azFileLock(eFileLock), azFileLock(pFile->eFileLock),
- azFileLock(pInode->eFileLock), pInode->nShared , getpid()));
+ azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared,
+ osGetpid(0)));
/* If there is already a lock of this type or more restrictive on the
** unixFile, do nothing. Don't use the end_lock: exit path, as
@@ -26017,7 +30998,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( rc!=SQLITE_BUSY ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
goto end_lock;
}
@@ -26052,7 +31033,7 @@ static int unixLock(sqlite3_file *id, int eFileLock){
if( rc ){
if( rc!=SQLITE_BUSY ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
goto end_lock;
}else{
@@ -26085,13 +31066,13 @@ static int unixLock(sqlite3_file *id, int eFileLock){
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( rc!=SQLITE_BUSY ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
}
}
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* Set up the transaction-counter change checking flags when
** transitioning from a SHARED to a RESERVED lock. The change
** from SHARED to RESERVED marks the beginning of a normal
@@ -26154,28 +31135,23 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
unixInodeInfo *pInode;
struct flock lock;
int rc = SQLITE_OK;
- int h;
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock,
pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared,
- getpid()));
+ osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
if( pFile->eFileLock<=eFileLock ){
return SQLITE_OK;
}
unixEnterMutex();
- h = pFile->h;
pInode = pFile->pInode;
assert( pInode->nShared!=0 );
if( pFile->eFileLock>SHARED_LOCK ){
assert( pInode->eFileLock==pFile->eFileLock );
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
** reading the database file again, make sure that the
** transaction counter was updated if any part of the database
@@ -26184,11 +31160,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
** the file has changed and hence might not know to flush their
** cache. The use of a stale cache can lead to database corruption.
*/
-#if 0
- assert( pFile->inNormalWrite==0
- || pFile->dbUpdate==0
- || pFile->transCntrChng==1 );
-#endif
pFile->inNormalWrite = 0;
#endif
@@ -26202,7 +31173,6 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
** 4: [RRRR.]
*/
if( eFileLock==SHARED_LOCK ){
-
#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE
(void)handleNFSUnlock;
assert( handleNFSUnlock==0 );
@@ -26219,9 +31189,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
if( unixFileLock(pFile, &lock)==(-1) ){
tErrno = errno;
rc = SQLITE_IOERR_UNLOCK;
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
- }
+ storeLastErrno(pFile, tErrno);
goto end_unlock;
}
lock.l_type = F_RDLCK;
@@ -26232,7 +31200,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK);
if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
goto end_unlock;
}
@@ -26243,9 +31211,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
if( unixFileLock(pFile, &lock)==(-1) ){
tErrno = errno;
rc = SQLITE_IOERR_UNLOCK;
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
- }
+ storeLastErrno(pFile, tErrno);
goto end_unlock;
}
}else
@@ -26263,7 +31229,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
** SQLITE_BUSY would confuse the upper layer (in practice it causes
** an assert to fail). */
rc = SQLITE_IOERR_RDLOCK;
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
goto end_unlock;
}
}
@@ -26276,7 +31242,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
pInode->eFileLock = SHARED_LOCK;
}else{
rc = SQLITE_IOERR_UNLOCK;
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
goto end_unlock;
}
}
@@ -26290,14 +31256,11 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
lock.l_type = F_UNLCK;
lock.l_whence = SEEK_SET;
lock.l_start = lock.l_len = 0L;
- SimulateIOErrorBenign(1);
- SimulateIOError( h=(-1) )
- SimulateIOErrorBenign(0);
if( unixFileLock(pFile, &lock)==0 ){
pInode->eFileLock = NO_LOCK;
}else{
rc = SQLITE_IOERR_UNLOCK;
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
pInode->eFileLock = NO_LOCK;
pFile->eFileLock = NO_LOCK;
}
@@ -26313,7 +31276,7 @@ static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){
closePendingFds(pFile);
}
}
-
+
end_unlock:
unixLeaveMutex();
if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock;
@@ -26328,9 +31291,17 @@ end_unlock:
** the requested locking level, this routine is a no-op.
*/
static int unixUnlock(sqlite3_file *id, int eFileLock){
+#if SQLITE_MAX_MMAP_SIZE>0
+ assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 );
+#endif
return posixUnlock(id, eFileLock, 0);
}
+#if SQLITE_MAX_MMAP_SIZE>0
+static int unixMapfile(unixFile *pFd, i64 nByte);
+static void unixUnmapfile(unixFile *pFd);
+#endif
+
/*
** This function performs the parts of the "close file" operation
** common to all locking schemes. It closes the directory and file
@@ -26343,19 +31314,29 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){
*/
static int closeUnixFile(sqlite3_file *id){
unixFile *pFile = (unixFile*)id;
+#if SQLITE_MAX_MMAP_SIZE>0
+ unixUnmapfile(pFile);
+#endif
if( pFile->h>=0 ){
robust_close(pFile, pFile->h, __LINE__);
pFile->h = -1;
}
#if OS_VXWORKS
if( pFile->pId ){
- if( pFile->isDelete ){
+ if( pFile->ctrlFlags & UNIXFILE_DELETE ){
osUnlink(pFile->pId->zCanonicalName);
}
vxworksReleaseFileId(pFile->pId);
pFile->pId = 0;
}
#endif
+#ifdef SQLITE_UNLINK_AFTER_CLOSE
+ if( pFile->ctrlFlags & UNIXFILE_DELETE ){
+ osUnlink(pFile->zPath);
+ sqlite3_free(*(char**)&pFile->zPath);
+ pFile->zPath = 0;
+ }
+#endif
OSTRACE(("CLOSE %-3d\n", pFile->h));
OpenCounter(-1);
sqlite3_free(pFile->pUnused);
@@ -26369,6 +31350,7 @@ static int closeUnixFile(sqlite3_file *id){
static int unixClose(sqlite3_file *id){
int rc = SQLITE_OK;
unixFile *pFile = (unixFile *)id;
+ verifyDbFile(pFile);
unixUnlock(id, NO_LOCK);
unixEnterMutex();
@@ -26437,9 +31419,9 @@ static int nolockClose(sqlite3_file *id) {
/******************************************************************************
************************* Begin dot-file Locking ******************************
**
-** The dotfile locking implementation uses the existance of separate lock
-** files in order to control access to the database. This works on just
-** about every filesystem imaginable. But there are serious downsides:
+** The dotfile locking implementation uses the existence of separate lock
+** files (really a directory) to control access to the database. This works
+** on just about every filesystem imaginable. But there are serious downsides:
**
** (1) There is zero concurrency. A single reader blocks all other
** connections from reading or writing the database.
@@ -26450,15 +31432,15 @@ static int nolockClose(sqlite3_file *id) {
** Nevertheless, a dotlock is an appropriate locking mode for use if no
** other locking strategy is available.
**
-** Dotfile locking works by creating a file in the same directory as the
-** database and with the same name but with a ".lock" extension added.
-** The existance of a lock file implies an EXCLUSIVE lock. All other lock
-** types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
+** Dotfile locking works by creating a subdirectory in the same directory as
+** the database and with the same name but with a ".lock" extension added.
+** The existence of a lock directory implies an EXCLUSIVE lock. All other
+** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE.
*/
/*
** The file suffix added to the data base filename in order to create the
-** lock file.
+** lock directory.
*/
#define DOTLOCK_SUFFIX ".lock"
@@ -26480,17 +31462,7 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
assert( pFile );
-
- /* Check if a thread in this process holds such a lock */
- if( pFile->eFileLock>SHARED_LOCK ){
- /* Either this connection or some other connection in the same process
- ** holds a lock on the file. No need to check further. */
- reserved = 1;
- }else{
- /* The lock is held if and only if the lockfile exists */
- const char *zLockFile = (const char*)pFile->lockingContext;
- reserved = osAccess(zLockFile, 0)==0;
- }
+ reserved = osAccess((const char*)pFile->lockingContext, 0)==0;
OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved));
*pResOut = reserved;
return rc;
@@ -26525,7 +31497,6 @@ static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) {
*/
static int dotlockLock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
- int fd;
char *zLockFile = (char *)pFile->lockingContext;
int rc = SQLITE_OK;
@@ -26545,21 +31516,20 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
}
/* grab an exclusive lock */
- fd = robust_open(zLockFile,O_RDONLY|O_CREAT|O_EXCL,0600);
- if( fd<0 ){
- /* failed to open/create the file, someone else may have stolen the lock */
+ rc = osMkdir(zLockFile, 0777);
+ if( rc<0 ){
+ /* failed to open/create the lock directory */
int tErrno = errno;
if( EEXIST == tErrno ){
rc = SQLITE_BUSY;
} else {
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
+ if( rc!=SQLITE_BUSY ){
+ storeLastErrno(pFile, tErrno);
}
}
return rc;
}
- robust_close(pFile, fd, __LINE__);
/* got it, set the type and return ok */
pFile->eFileLock = eFileLock;
@@ -26578,10 +31548,11 @@ static int dotlockLock(sqlite3_file *id, int eFileLock) {
static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
char *zLockFile = (char *)pFile->lockingContext;
+ int rc;
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock,
- pFile->eFileLock, getpid()));
+ pFile->eFileLock, osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
/* no-op if possible */
@@ -26599,14 +31570,14 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
/* To fully unlock the database, delete the lock file */
assert( eFileLock==NO_LOCK );
- if( osUnlink(zLockFile) ){
- int rc = 0;
+ rc = osRmdir(zLockFile);
+ if( rc<0 ){
int tErrno = errno;
- if( ENOENT != tErrno ){
+ if( tErrno==ENOENT ){
+ rc = SQLITE_OK;
+ }else{
rc = SQLITE_IOERR_UNLOCK;
- }
- if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
return rc;
}
@@ -26618,14 +31589,11 @@ static int dotlockUnlock(sqlite3_file *id, int eFileLock) {
** Close a file. Make sure the lock has been released before closing.
*/
static int dotlockClose(sqlite3_file *id) {
- int rc;
- if( id ){
- unixFile *pFile = (unixFile*)id;
- dotlockUnlock(id, NO_LOCK);
- sqlite3_free(pFile->lockingContext);
- }
- rc = closeUnixFile(id);
- return rc;
+ unixFile *pFile = (unixFile*)id;
+ assert( id!=0 );
+ dotlockUnlock(id, NO_LOCK);
+ sqlite3_free(pFile->lockingContext);
+ return closeUnixFile(id);
}
/****************** End of the dot-file lock implementation *******************
******************************************************************************/
@@ -26642,10 +31610,9 @@ static int dotlockClose(sqlite3_file *id) {
** still works when you do this, but concurrency is reduced since
** only a single process can be reading the database at a time.
**
-** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off or if
-** compiling for VXWORKS.
+** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off
*/
-#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
+#if SQLITE_ENABLE_LOCKING_STYLE
/*
** Retry flock() calls that fail with EINTR
@@ -26692,10 +31659,8 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
int tErrno = errno;
/* unlock failed with an error */
lrc = SQLITE_IOERR_UNLOCK;
- if( IS_LOCK_ERROR(lrc) ){
- pFile->lastErrno = tErrno;
- rc = lrc;
- }
+ storeLastErrno(pFile, tErrno);
+ rc = lrc;
}
} else {
int tErrno = errno;
@@ -26703,7 +31668,7 @@ static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){
/* someone else might have it reserved */
lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(lrc) ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
rc = lrc;
}
}
@@ -26769,7 +31734,7 @@ static int flockLock(sqlite3_file *id, int eFileLock) {
/* didn't get, must be busy */
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK);
if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
} else {
/* got it, set the type and return ok */
@@ -26798,7 +31763,7 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) {
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock,
- pFile->eFileLock, getpid()));
+ pFile->eFileLock, osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
/* no-op if possible */
@@ -26828,9 +31793,8 @@ static int flockUnlock(sqlite3_file *id, int eFileLock) {
** Close a file.
*/
static int flockClose(sqlite3_file *id) {
- if( id ){
- flockUnlock(id, NO_LOCK);
- }
+ assert( id!=0 );
+ flockUnlock(id, NO_LOCK);
return closeUnixFile(id);
}
@@ -26857,7 +31821,7 @@ static int flockClose(sqlite3_file *id) {
** to a non-zero value otherwise *pResOut is set to zero. The return value
** is set to SQLITE_OK unless an I/O error occurs during lock checking.
*/
-static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
+static int semXCheckReservedLock(sqlite3_file *id, int *pResOut) {
int rc = SQLITE_OK;
int reserved = 0;
unixFile *pFile = (unixFile*)id;
@@ -26874,13 +31838,12 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
/* Otherwise see if some other process holds it. */
if( !reserved ){
sem_t *pSem = pFile->pInode->pSem;
- struct stat statBuf;
if( sem_trywait(pSem)==-1 ){
int tErrno = errno;
if( EAGAIN != tErrno ){
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
} else {
/* someone else has the lock when we are in NO_LOCK */
reserved = (pFile->eFileLock < SHARED_LOCK);
@@ -26925,9 +31888,8 @@ static int semCheckReservedLock(sqlite3_file *id, int *pResOut) {
** This routine will only increase a lock. Use the sqlite3OsUnlock()
** routine to lower a locking level.
*/
-static int semLock(sqlite3_file *id, int eFileLock) {
+static int semXLock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
- int fd;
sem_t *pSem = pFile->pInode->pSem;
int rc = SQLITE_OK;
@@ -26959,14 +31921,14 @@ static int semLock(sqlite3_file *id, int eFileLock) {
** If the locking level of the file descriptor is already at or below
** the requested locking level, this routine is a no-op.
*/
-static int semUnlock(sqlite3_file *id, int eFileLock) {
+static int semXUnlock(sqlite3_file *id, int eFileLock) {
unixFile *pFile = (unixFile*)id;
sem_t *pSem = pFile->pInode->pSem;
assert( pFile );
assert( pSem );
OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock,
- pFile->eFileLock, getpid()));
+ pFile->eFileLock, osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
/* no-op if possible */
@@ -26985,7 +31947,7 @@ static int semUnlock(sqlite3_file *id, int eFileLock) {
int rc, tErrno = errno;
rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK);
if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
return rc;
}
@@ -26996,10 +31958,10 @@ static int semUnlock(sqlite3_file *id, int eFileLock) {
/*
** Close a file.
*/
-static int semClose(sqlite3_file *id) {
+static int semXClose(sqlite3_file *id) {
if( id ){
unixFile *pFile = (unixFile*)id;
- semUnlock(id, NO_LOCK);
+ semXUnlock(id, NO_LOCK);
assert( pFile );
unixEnterMutex();
releaseInodeInfo(pFile);
@@ -27087,7 +32049,7 @@ static int afpSetLock(
setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK);
#endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */
if( IS_LOCK_ERROR(rc) ){
- pFile->lastErrno = tErrno;
+ storeLastErrno(pFile, tErrno);
}
return rc;
} else {
@@ -27180,7 +32142,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){
assert( pFile );
OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h,
azFileLock(eFileLock), azFileLock(pFile->eFileLock),
- azFileLock(pInode->eFileLock), pInode->nShared , getpid()));
+ azFileLock(pInode->eFileLock), pInode->nShared , osGetpid(0)));
/* If there is already a lock of this type or more restrictive on the
** unixFile, do nothing. Don't use the afp_end_lock: exit path, as
@@ -27270,7 +32232,7 @@ static int afpLock(sqlite3_file *id, int eFileLock){
lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0);
if( IS_LOCK_ERROR(lrc1) ) {
- pFile->lastErrno = lrc1Errno;
+ storeLastErrno(pFile, lrc1Errno);
rc = lrc1;
goto afp_end_lock;
} else if( IS_LOCK_ERROR(lrc2) ){
@@ -27366,7 +32328,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
assert( pFile );
OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock,
pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared,
- getpid()));
+ osGetpid(0)));
assert( eFileLock<=SHARED_LOCK );
if( pFile->eFileLock<=eFileLock ){
@@ -27381,7 +32343,7 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
SimulateIOError( h=(-1) )
SimulateIOErrorBenign(0);
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* When reducing a lock such that other processes can start
** reading the database file again, make sure that the
** transaction counter was updated if any part of the database
@@ -27458,23 +32420,22 @@ static int afpUnlock(sqlite3_file *id, int eFileLock) {
*/
static int afpClose(sqlite3_file *id) {
int rc = SQLITE_OK;
- if( id ){
- unixFile *pFile = (unixFile*)id;
- afpUnlock(id, NO_LOCK);
- unixEnterMutex();
- if( pFile->pInode && pFile->pInode->nLock ){
- /* If there are outstanding locks, do not actually close the file just
- ** yet because that would clear those locks. Instead, add the file
- ** descriptor to pInode->aPending. It will be automatically closed when
- ** the last lock is cleared.
- */
- setPendingFd(pFile);
- }
- releaseInodeInfo(pFile);
- sqlite3_free(pFile->lockingContext);
- rc = closeUnixFile(id);
- unixLeaveMutex();
+ unixFile *pFile = (unixFile*)id;
+ assert( id!=0 );
+ afpUnlock(id, NO_LOCK);
+ unixEnterMutex();
+ if( pFile->pInode && pFile->pInode->nLock ){
+ /* If there are outstanding locks, do not actually close the file just
+ ** yet because that would clear those locks. Instead, add the file
+ ** descriptor to pInode->aPending. It will be automatically closed when
+ ** the last lock is cleared.
+ */
+ setPendingFd(pFile);
}
+ releaseInodeInfo(pFile);
+ sqlite3_free(pFile->lockingContext);
+ rc = closeUnixFile(id);
+ unixLeaveMutex();
return rc;
}
@@ -27529,7 +32490,7 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
** NB: If you define USE_PREAD or USE_PREAD64, then it might also
** be necessary to define _XOPEN_SOURCE to be 500. This varies from
** one system to another. Since SQLite does not define USE_PREAD
-** any any form by default, we will not attempt to define _XOPEN_SOURCE.
+** in any form by default, we will not attempt to define _XOPEN_SOURCE.
** See tickets #2741 and #2681.
**
** To avoid stomping the errno value on a failed read the lastErrno value
@@ -27537,35 +32498,46 @@ static int nfsUnlock(sqlite3_file *id, int eFileLock){
*/
static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){
int got;
+ int prior = 0;
#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
i64 newOffset;
#endif
TIMER_START;
+ assert( cnt==(cnt&0x1ffff) );
+ assert( id->h>2 );
+ do{
#if defined(USE_PREAD)
- do{ got = osPread(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
- SimulateIOError( got = -1 );
+ got = osPread(id->h, pBuf, cnt, offset);
+ SimulateIOError( got = -1 );
#elif defined(USE_PREAD64)
- do{ got = osPread64(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR);
- SimulateIOError( got = -1 );
+ got = osPread64(id->h, pBuf, cnt, offset);
+ SimulateIOError( got = -1 );
#else
- newOffset = lseek(id->h, offset, SEEK_SET);
- SimulateIOError( newOffset-- );
- if( newOffset!=offset ){
- if( newOffset == -1 ){
- ((unixFile*)id)->lastErrno = errno;
- }else{
- ((unixFile*)id)->lastErrno = 0;
+ newOffset = lseek(id->h, offset, SEEK_SET);
+ SimulateIOError( newOffset = -1 );
+ if( newOffset<0 ){
+ storeLastErrno((unixFile*)id, errno);
+ return -1;
}
- return -1;
- }
- do{ got = osRead(id->h, pBuf, cnt); }while( got<0 && errno==EINTR );
+ got = osRead(id->h, pBuf, cnt);
#endif
+ if( got==cnt ) break;
+ if( got<0 ){
+ if( errno==EINTR ){ got = 1; continue; }
+ prior = 0;
+ storeLastErrno((unixFile*)id, errno);
+ break;
+ }else if( got>0 ){
+ cnt -= got;
+ offset += got;
+ prior += got;
+ pBuf = (void*)(got + (char*)pBuf);
+ }
+ }while( got>0 );
TIMER_END;
- if( got<0 ){
- ((unixFile*)id)->lastErrno = errno;
- }
- OSTRACE(("READ %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
- return got;
+ OSTRACE(("READ %-3d %5d %7lld %llu\n",
+ id->h, got+prior, offset-prior, TIMER_ELAPSED));
+ return got+prior;
}
/*
@@ -27582,6 +32554,8 @@ static int unixRead(
unixFile *pFile = (unixFile *)id;
int got;
assert( id );
+ assert( offset>=0 );
+ assert( amt>0 );
/* If this is a database file (not a journal, master-journal or temp
** file), the bytes in the locking range should never be read or written. */
@@ -27592,6 +32566,23 @@ static int unixRead(
);
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this read request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
+ return SQLITE_OK;
+ }else{
+ int nCopy = pFile->mmapSize - offset;
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
+
got = seekAndRead(pFile, offset, pBuf, amt);
if( got==amt ){
return SQLITE_OK;
@@ -27599,7 +32590,7 @@ static int unixRead(
/* lastErrno set by seekAndRead */
return SQLITE_IOERR_READ;
}else{
- pFile->lastErrno = 0; /* not a system error */
+ storeLastErrno(pFile, 0); /* not a system error */
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[got], 0, amt-got);
return SQLITE_IOERR_SHORT_READ;
@@ -27607,44 +32598,60 @@ static int unixRead(
}
/*
-** Seek to the offset in id->offset then read cnt bytes into pBuf.
-** Return the number of bytes actually read. Update the offset.
-**
-** To avoid stomping the errno value on a failed write the lastErrno value
-** is set before returning.
+** Attempt to seek the file-descriptor passed as the first argument to
+** absolute offset iOff, then attempt to write nBuf bytes of data from
+** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise,
+** return the actual number of bytes written (which may be less than
+** nBuf).
*/
-static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
- int got;
-#if (!defined(USE_PREAD) && !defined(USE_PREAD64))
- i64 newOffset;
-#endif
+static int seekAndWriteFd(
+ int fd, /* File descriptor to write to */
+ i64 iOff, /* File offset to begin writing at */
+ const void *pBuf, /* Copy data from this buffer to the file */
+ int nBuf, /* Size of buffer pBuf in bytes */
+ int *piErrno /* OUT: Error number if error occurs */
+){
+ int rc = 0; /* Value returned by system call */
+
+ assert( nBuf==(nBuf&0x1ffff) );
+ assert( fd>2 );
+ assert( piErrno!=0 );
+ nBuf &= 0x1ffff;
TIMER_START;
+
#if defined(USE_PREAD)
- do{ got = osPwrite(id->h, pBuf, cnt, offset); }while( got<0 && errno==EINTR );
+ do{ rc = (int)osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR );
#elif defined(USE_PREAD64)
- do{ got = osPwrite64(id->h, pBuf, cnt, offset);}while( got<0 && errno==EINTR);
+ do{ rc = (int)osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR);
#else
do{
- newOffset = lseek(id->h, offset, SEEK_SET);
- SimulateIOError( newOffset-- );
- if( newOffset!=offset ){
- if( newOffset == -1 ){
- ((unixFile*)id)->lastErrno = errno;
- }else{
- ((unixFile*)id)->lastErrno = 0;
- }
- return -1;
+ i64 iSeek = lseek(fd, iOff, SEEK_SET);
+ SimulateIOError( iSeek = -1 );
+ if( iSeek<0 ){
+ rc = -1;
+ break;
}
- got = osWrite(id->h, pBuf, cnt);
- }while( got<0 && errno==EINTR );
+ rc = osWrite(fd, pBuf, nBuf);
+ }while( rc<0 && errno==EINTR );
#endif
+
TIMER_END;
- if( got<0 ){
- ((unixFile*)id)->lastErrno = errno;
- }
+ OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED));
+
+ if( rc<0 ) *piErrno = errno;
+ return rc;
+}
- OSTRACE(("WRITE %-3d %5d %7lld %llu\n", id->h, got, offset, TIMER_ELAPSED));
- return got;
+
+/*
+** Seek to the offset in id->offset then read cnt bytes into pBuf.
+** Return the number of bytes actually read. Update the offset.
+**
+** To avoid stomping the errno value on a failed write the lastErrno value
+** is set before returning.
+*/
+static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){
+ return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno);
}
@@ -27672,7 +32679,7 @@ static int unixWrite(
);
#endif
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* If we are doing a normal write to a database file (as opposed to
** doing a hot-journal rollback or a write to some file other than a
** normal database file) then record the fact that the database
@@ -27694,7 +32701,24 @@ static int unixWrite(
}
#endif
- while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this write request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
+ return SQLITE_OK;
+ }else{
+ int nCopy = pFile->mmapSize - offset;
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
+
+ while( (wrote = seekAndWrite(pFile, offset, pBuf, amt))<amt && wrote>0 ){
amt -= wrote;
offset += wrote;
pBuf = &((char*)pBuf)[wrote];
@@ -27702,12 +32726,12 @@ static int unixWrite(
SimulateIOError(( wrote=(-1), amt=1 ));
SimulateDiskfullError(( wrote=0, amt=1 ));
- if( amt>0 ){
+ if( amt>wrote ){
if( wrote<0 && pFile->lastErrno!=ENOSPC ){
/* lastErrno set by seekAndWrite */
return SQLITE_IOERR_WRITE;
}else{
- pFile->lastErrno = 0; /* not a system error */
+ storeLastErrno(pFile, 0); /* not a system error */
return SQLITE_FULL;
}
}
@@ -27728,9 +32752,9 @@ SQLITE_API int sqlite3_fullsync_count = 0;
** We do not trust systems to provide a working fdatasync(). Some do.
** Others do no. To be safe, we will stick with the (slightly slower)
** fsync(). If you know that your system does support fdatasync() correctly,
-** then simply compile with -Dfdatasync=fdatasync
+** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC
*/
-#if !defined(fdatasync)
+#if !defined(fdatasync) && !HAVE_FDATASYNC
# define fdatasync fsync
#endif
@@ -27798,10 +32822,15 @@ static int full_fsync(int fd, int fullSync, int dataOnly){
#endif
/* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a
- ** no-op
+ ** no-op. But go ahead and call fstat() to validate the file
+ ** descriptor as we need a method to provoke a failure during
+ ** coverate testing.
*/
#ifdef SQLITE_NO_SYNC
- rc = SQLITE_OK;
+ {
+ struct stat buf;
+ rc = osFstat(fd, &buf);
+ }
#elif HAVE_FULLFSYNC
if( fullSync ){
rc = osFcntl(fd, F_FULLFSYNC, 0);
@@ -27867,19 +32896,20 @@ static int openDirectory(const char *zFilename, int *pFd){
char zDirname[MAX_PATHNAME+1];
sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename);
- for(ii=(int)strlen(zDirname); ii>1 && zDirname[ii]!='/'; ii--);
+ for(ii=(int)strlen(zDirname); ii>0 && zDirname[ii]!='/'; ii--);
if( ii>0 ){
zDirname[ii] = '\0';
- fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
- if( fd>=0 ){
-#ifdef FD_CLOEXEC
- osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
-#endif
- OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
- }
+ }else{
+ if( zDirname[0]!='/' ) zDirname[0] = '.';
+ zDirname[1] = 0;
+ }
+ fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
+ if( fd>=0 ){
+ OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
}
*pFd = fd;
- return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", zDirname));
+ if( fd>=0 ) return SQLITE_OK;
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname);
}
/*
@@ -27919,12 +32949,12 @@ static int unixSync(sqlite3_file *id, int flags){
rc = full_fsync(pFile->h, isFullsync, isDataOnly);
SimulateIOError( rc=1 );
if( rc ){
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath);
}
/* Also fsync the directory containing the file if the DIRSYNC flag
- ** is set. This is a one-time occurrance. Many systems (examples: AIX)
+ ** is set. This is a one-time occurrence. Many systems (examples: AIX)
** are unable to fsync a directory, so ignore errors on the fsync.
*/
if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){
@@ -27932,10 +32962,11 @@ static int unixSync(sqlite3_file *id, int flags){
OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath,
HAVE_FULLFSYNC, isFullsync));
rc = osOpenDirectory(pFile->zPath, &dirfd);
- if( rc==SQLITE_OK && dirfd>=0 ){
+ if( rc==SQLITE_OK ){
full_fsync(dirfd, 0, 0);
robust_close(pFile, dirfd, __LINE__);
- }else if( rc==SQLITE_CANTOPEN ){
+ }else{
+ assert( rc==SQLITE_CANTOPEN );
rc = SQLITE_OK;
}
pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC;
@@ -27957,16 +32988,16 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
** actual file size after the operation may be larger than the requested
** size).
*/
- if( pFile->szChunk ){
+ if( pFile->szChunk>0 ){
nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
}
- rc = robust_ftruncate(pFile->h, (off_t)nByte);
+ rc = robust_ftruncate(pFile->h, nByte);
if( rc ){
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
}else{
-#ifndef NDEBUG
+#ifdef SQLITE_DEBUG
/* If we are doing a normal write to a database file (as opposed to
** doing a hot-journal rollback or a write to some file other than a
** normal database file) and we truncate the file to zero length,
@@ -27979,6 +33010,16 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){
}
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* If the file was just truncated to a size smaller than the currently
+ ** mapped region, reduce the effective mapping size as well. SQLite will
+ ** use read() and write() to access data beyond this point from now on.
+ */
+ if( nByte<pFile->mmapSize ){
+ pFile->mmapSize = nByte;
+ }
+#endif
+
return SQLITE_OK;
}
}
@@ -27993,7 +33034,7 @@ static int unixFileSize(sqlite3_file *id, i64 *pSize){
rc = osFstat(((unixFile*)id)->h, &buf);
SimulateIOError( rc=1 );
if( rc!=0 ){
- ((unixFile*)id)->lastErrno = errno;
+ storeLastErrno((unixFile*)id, errno);
return SQLITE_IOERR_FSTAT;
}
*pSize = buf.st_size;
@@ -28029,7 +33070,9 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
i64 nSize; /* Required file size */
struct stat buf; /* Used to hold return values of fstat() */
- if( osFstat(pFile->h, &buf) ) return SQLITE_IOERR_FSTAT;
+ if( osFstat(pFile->h, &buf) ){
+ return SQLITE_IOERR_FSTAT;
+ }
nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk;
if( nSize>(i64)buf.st_size ){
@@ -28044,33 +33087,67 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){
}while( err==EINTR );
if( err ) return SQLITE_IOERR_WRITE;
#else
- /* If the OS does not have posix_fallocate(), fake it. First use
- ** ftruncate() to set the file size, then write a single byte to
- ** the last byte in each block within the extended region. This
- ** is the same technique used by glibc to implement posix_fallocate()
- ** on systems that do not have a real fallocate() system call.
+ /* If the OS does not have posix_fallocate(), fake it. Write a
+ ** single byte to the last byte in each block that falls entirely
+ ** within the extended region. Then, if required, a single byte
+ ** at offset (nSize-1), to set the size of the file correctly.
+ ** This is a similar technique to that used by glibc on systems
+ ** that do not have a real fallocate() call.
*/
int nBlk = buf.st_blksize; /* File-system block size */
+ int nWrite = 0; /* Number of bytes written by seekAndWrite */
i64 iWrite; /* Next offset to write to */
- if( robust_ftruncate(pFile->h, nSize) ){
- pFile->lastErrno = errno;
- return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
- }
- iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
- while( iWrite<nSize ){
- int nWrite = seekAndWrite(pFile, iWrite, "", 1);
+ iWrite = (buf.st_size/nBlk)*nBlk + nBlk - 1;
+ assert( iWrite>=buf.st_size );
+ assert( ((iWrite+1)%nBlk)==0 );
+ for(/*no-op*/; iWrite<nSize+nBlk-1; iWrite+=nBlk ){
+ if( iWrite>=nSize ) iWrite = nSize - 1;
+ nWrite = seekAndWrite(pFile, iWrite, "", 1);
if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
- iWrite += nBlk;
}
#endif
}
}
+#if SQLITE_MAX_MMAP_SIZE>0
+ if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){
+ int rc;
+ if( pFile->szChunk<=0 ){
+ if( robust_ftruncate(pFile->h, nByte) ){
+ storeLastErrno(pFile, errno);
+ return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
+ }
+ }
+
+ rc = unixMapfile(pFile, nByte);
+ return rc;
+ }
+#endif
+
return SQLITE_OK;
}
/*
+** If *pArg is initially negative then this is a query. Set *pArg to
+** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
+**
+** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
+*/
+static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){
+ if( *pArg<0 ){
+ *pArg = (pFile->ctrlFlags & mask)!=0;
+ }else if( (*pArg)==0 ){
+ pFile->ctrlFlags &= ~mask;
+ }else{
+ pFile->ctrlFlags |= mask;
+ }
+}
+
+/* Forward declaration */
+static int unixGetTempname(int nBuf, char *zBuf);
+
+/*
** Information and control of an open file handle.
*/
static int unixFileControl(sqlite3_file *id, int op, void *pArg){
@@ -28080,7 +33157,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
*(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
}
- case SQLITE_LAST_ERRNO: {
+ case SQLITE_FCNTL_LAST_ERRNO: {
*(int*)pArg = pFile->lastErrno;
return SQLITE_OK;
}
@@ -28096,17 +33173,48 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
return rc;
}
case SQLITE_FCNTL_PERSIST_WAL: {
- int bPersist = *(int*)pArg;
- if( bPersist<0 ){
- *(int*)pArg = (pFile->ctrlFlags & UNIXFILE_PERSIST_WAL)!=0;
- }else if( bPersist==0 ){
- pFile->ctrlFlags &= ~UNIXFILE_PERSIST_WAL;
- }else{
- pFile->ctrlFlags |= UNIXFILE_PERSIST_WAL;
+ unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg);
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
+ unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg);
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_VFSNAME: {
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_TEMPFILENAME: {
+ char *zTFile = sqlite3_malloc64( pFile->pVfs->mxPathname );
+ if( zTFile ){
+ unixGetTempname(pFile->pVfs->mxPathname, zTFile);
+ *(char**)pArg = zTFile;
}
return SQLITE_OK;
}
-#ifndef NDEBUG
+ case SQLITE_FCNTL_HAS_MOVED: {
+ *(int*)pArg = fileHasMoved(pFile);
+ return SQLITE_OK;
+ }
+#if SQLITE_MAX_MMAP_SIZE>0
+ case SQLITE_FCNTL_MMAP_SIZE: {
+ i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
+ if( newLimit>sqlite3GlobalConfig.mxMmap ){
+ newLimit = sqlite3GlobalConfig.mxMmap;
+ }
+ *(i64*)pArg = pFile->mmapSizeMax;
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
+ pFile->mmapSizeMax = newLimit;
+ if( pFile->mmapSize>0 ){
+ unixUnmapfile(pFile);
+ rc = unixMapfile(pFile, -1);
+ }
+ }
+ return rc;
+ }
+#endif
+#ifdef SQLITE_DEBUG
/* The pager calls this method to signal that it has done
** a rollback and that the database is therefore unchanged and
** it hence it is OK for the transaction change counter to be
@@ -28118,14 +33226,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
}
#endif
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
- case SQLITE_SET_LOCKPROXYFILE:
- case SQLITE_GET_LOCKPROXYFILE: {
+ case SQLITE_FCNTL_SET_LOCKPROXYFILE:
+ case SQLITE_FCNTL_GET_LOCKPROXYFILE: {
return proxyFileControl(id,op,pArg);
}
#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */
- case SQLITE_FCNTL_SYNC_OMITTED: {
- return SQLITE_OK; /* A no-op */
- }
}
return SQLITE_NOTFOUND;
}
@@ -28140,21 +33245,140 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
** a database and its journal file) that the sector size will be the
** same for both.
*/
+#ifndef __QNXNTO__
static int unixSectorSize(sqlite3_file *NotUsed){
UNUSED_PARAMETER(NotUsed);
return SQLITE_DEFAULT_SECTOR_SIZE;
}
+#endif
/*
-** Return the device characteristics for the file. This is always 0 for unix.
+** The following version of unixSectorSize() is optimized for QNX.
*/
-static int unixDeviceCharacteristics(sqlite3_file *NotUsed){
- UNUSED_PARAMETER(NotUsed);
- return 0;
+#ifdef __QNXNTO__
+#include <sys/dcmd_blk.h>
+#include <sys/statvfs.h>
+static int unixSectorSize(sqlite3_file *id){
+ unixFile *pFile = (unixFile*)id;
+ if( pFile->sectorSize == 0 ){
+ struct statvfs fsInfo;
+
+ /* Set defaults for non-supported filesystems */
+ pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
+ pFile->deviceCharacteristics = 0;
+ if( fstatvfs(pFile->h, &fsInfo) == -1 ) {
+ return pFile->sectorSize;
+ }
+
+ if( !strcmp(fsInfo.f_basetype, "tmp") ) {
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( strstr(fsInfo.f_basetype, "etfs") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ /* etfs cluster size writes are atomic */
+ (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) |
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ /* full bitset of atomics from max sector size and smaller */
+ ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else if( strstr(fsInfo.f_basetype, "dos") ){
+ pFile->sectorSize = fsInfo.f_bsize;
+ pFile->deviceCharacteristics =
+ /* full bitset of atomics from max sector size and smaller */
+ ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind
+ ** so it is ordered */
+ 0;
+ }else{
+ pFile->deviceCharacteristics =
+ SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until
+ ** the write succeeds */
+ 0;
+ }
+ }
+ /* Last chance verification. If the sector size isn't a multiple of 512
+ ** then it isn't valid.*/
+ if( pFile->sectorSize % 512 != 0 ){
+ pFile->deviceCharacteristics = 0;
+ pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
+ }
+ return pFile->sectorSize;
}
+#endif /* __QNXNTO__ */
-#ifndef SQLITE_OMIT_WAL
+/*
+** Return the device characteristics for the file.
+**
+** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default.
+** However, that choice is controversial since technically the underlying
+** file system does not always provide powersafe overwrites. (In other
+** words, after a power-loss event, parts of the file that were never
+** written might end up being altered.) However, non-PSOW behavior is very,
+** very rare. And asserting PSOW makes a large reduction in the amount
+** of required I/O for journaling, since a lot of padding is eliminated.
+** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control
+** available to turn it off and URI query parameter available to turn it off.
+*/
+static int unixDeviceCharacteristics(sqlite3_file *id){
+ unixFile *p = (unixFile*)id;
+ int rc = 0;
+#ifdef __QNXNTO__
+ if( p->sectorSize==0 ) unixSectorSize(id);
+ rc = p->deviceCharacteristics;
+#endif
+ if( p->ctrlFlags & UNIXFILE_PSOW ){
+ rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
+ }
+ return rc;
+}
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+
+/*
+** Return the system page size.
+**
+** This function should not be called directly by other code in this file.
+** Instead, it should be called via macro osGetpagesize().
+*/
+static int unixGetpagesize(void){
+#if OS_VXWORKS
+ return 1024;
+#elif defined(_BSD_SOURCE)
+ return getpagesize();
+#else
+ return (int)sysconf(_SC_PAGESIZE);
+#endif
+}
+
+#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */
+
+#ifndef SQLITE_OMIT_WAL
/*
** Object used to represent an shared memory buffer.
@@ -28238,22 +33462,24 @@ struct unixShm {
** otherwise.
*/
static int unixShmSystemLock(
- unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */
+ unixFile *pFile, /* Open connection to the WAL file */
int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */
int ofst, /* First byte of the locking range */
int n /* Number of bytes to lock */
){
- struct flock f; /* The posix advisory locking structure */
- int rc = SQLITE_OK; /* Result code form fcntl() */
+ unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */
+ struct flock f; /* The posix advisory locking structure */
+ int rc = SQLITE_OK; /* Result code form fcntl() */
/* Access to the unixShmNode object is serialized by the caller */
+ pShmNode = pFile->pInode->pShmNode;
assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
/* Shared locks never span more than one byte */
assert( n==1 || lockType!=F_RDLCK );
/* Locks are within range */
- assert( n>=1 && n<SQLITE_SHM_NLOCK );
+ assert( n>=1 && n<=SQLITE_SHM_NLOCK );
if( pShmNode->h>=0 ){
/* Initialize the locking parameters */
@@ -28271,7 +33497,7 @@ static int unixShmSystemLock(
#ifdef SQLITE_DEBUG
{ u16 mask;
OSTRACE(("SHM-LOCK "));
- mask = (1<<(ofst+n)) - (1<<ofst);
+ mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
OSTRACE(("unlock %d ok", ofst));
@@ -28305,6 +33531,22 @@ static int unixShmSystemLock(
return rc;
}
+/*
+** Return the minimum number of 32KB shm regions that should be mapped at
+** a time, assuming that each mapping must be an integer multiple of the
+** current system page-size.
+**
+** Usually, this is 1. The exception seems to be systems that are configured
+** to use 64KB pages - in this case each mapping must cover at least two
+** shm regions.
+*/
+static int unixShmRegionPerMap(void){
+ int shmsz = 32*1024; /* SHM region size */
+ int pgsz = osGetpagesize(); /* System page size */
+ assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */
+ if( pgsz<shmsz ) return 1;
+ return pgsz/shmsz;
+}
/*
** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0.
@@ -28315,13 +33557,14 @@ static int unixShmSystemLock(
static void unixShmPurge(unixFile *pFd){
unixShmNode *p = pFd->pInode->pShmNode;
assert( unixMutexHeld() );
- if( p && p->nRef==0 ){
+ if( p && ALWAYS(p->nRef==0) ){
+ int nShmPerMap = unixShmRegionPerMap();
int i;
assert( p->pInode==pFd->pInode );
sqlite3_mutex_free(p->mutex);
- for(i=0; i<p->nRegion; i++){
+ for(i=0; i<p->nRegion; i+=nShmPerMap){
if( p->h>=0 ){
- munmap(p->apRegion[i], p->szRegion);
+ osMunmap(p->apRegion[i], p->szRegion);
}else{
sqlite3_free(p->apRegion[i]);
}
@@ -28380,8 +33623,8 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
int nShmFilename; /* Size of the SHM filename in bytes */
/* Allocate space for the new unixShm object. */
- p = sqlite3_malloc( sizeof(*p) );
- if( p==0 ) return SQLITE_NOMEM;
+ p = sqlite3_malloc64( sizeof(*p) );
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
memset(p, 0, sizeof(*p));
assert( pDbFd->pShm==0 );
@@ -28393,74 +33636,79 @@ static int unixOpenSharedMemory(unixFile *pDbFd){
pShmNode = pInode->pShmNode;
if( pShmNode==0 ){
struct stat sStat; /* fstat() info for database file */
+#ifndef SQLITE_SHM_DIRECTORY
+ const char *zBasePath = pDbFd->zPath;
+#endif
/* Call fstat() to figure out the permissions on the database file. If
** a new *-shm file is created, an attempt will be made to create it
- ** with the same permissions. The actual permissions the file is created
- ** with are subject to the current umask setting.
+ ** with the same permissions.
*/
- if( osFstat(pDbFd->h, &sStat) && pInode->bProcessLock==0 ){
+ if( osFstat(pDbFd->h, &sStat) ){
rc = SQLITE_IOERR_FSTAT;
goto shm_open_err;
}
#ifdef SQLITE_SHM_DIRECTORY
- nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 30;
+ nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31;
#else
- nShmFilename = 5 + (int)strlen(pDbFd->zPath);
+ nShmFilename = 6 + (int)strlen(zBasePath);
#endif
- pShmNode = sqlite3_malloc( sizeof(*pShmNode) + nShmFilename );
+ pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename );
if( pShmNode==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto shm_open_err;
}
- memset(pShmNode, 0, sizeof(*pShmNode));
+ memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename);
zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1];
#ifdef SQLITE_SHM_DIRECTORY
sqlite3_snprintf(nShmFilename, zShmFilename,
SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x",
(u32)sStat.st_ino, (u32)sStat.st_dev);
#else
- sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", pDbFd->zPath);
+ sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath);
sqlite3FileSuffix3(pDbFd->zPath, zShmFilename);
#endif
pShmNode->h = -1;
pDbFd->pInode->pShmNode = pShmNode;
pShmNode->pInode = pDbFd->pInode;
- pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->mutex==0 ){
- rc = SQLITE_NOMEM;
- goto shm_open_err;
+ if( sqlite3GlobalConfig.bCoreMutex ){
+ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->mutex==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto shm_open_err;
+ }
}
if( pInode->bProcessLock==0 ){
- pShmNode->h = robust_open(zShmFilename, O_RDWR|O_CREAT,
- (sStat.st_mode & 0777));
+ int openFlags = O_RDWR | O_CREAT;
+ if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
+ openFlags = O_RDONLY;
+ pShmNode->isReadonly = 1;
+ }
+ pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777));
if( pShmNode->h<0 ){
- const char *zRO;
- zRO = sqlite3_uri_parameter(pDbFd->zPath, "readonly_shm");
- if( zRO && sqlite3GetBoolean(zRO) ){
- pShmNode->h = robust_open(zShmFilename, O_RDONLY,
- (sStat.st_mode & 0777));
- pShmNode->isReadonly = 1;
- }
- if( pShmNode->h<0 ){
- rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
- goto shm_open_err;
- }
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename);
+ goto shm_open_err;
}
+
+ /* If this process is running as root, make sure that the SHM file
+ ** is owned by the same user that owns the original database. Otherwise,
+ ** the original owner will not be able to connect.
+ */
+ robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid);
/* Check to see if another process is holding the dead-man switch.
** If not, truncate the file to zero length.
*/
rc = SQLITE_OK;
- if( unixShmSystemLock(pShmNode, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
+ if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){
if( robust_ftruncate(pShmNode->h, 0) ){
rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename);
}
}
if( rc==SQLITE_OK ){
- rc = unixShmSystemLock(pShmNode, F_RDLCK, UNIX_SHM_DMS, 1);
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1);
}
if( rc ) goto shm_open_err;
}
@@ -28526,6 +33774,8 @@ static int unixShmMap(
unixShm *p;
unixShmNode *pShmNode;
int rc = SQLITE_OK;
+ int nShmPerMap = unixShmRegionPerMap();
+ int nReqRegion;
/* If the shared-memory file has not yet been opened, open it now. */
if( pDbFd->pShm==0 ){
@@ -28541,9 +33791,12 @@ static int unixShmMap(
assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
- if( pShmNode->nRegion<=iRegion ){
+ /* Minimum number of regions required to be mapped. */
+ nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap;
+
+ if( pShmNode->nRegion<nReqRegion ){
char **apNew; /* New apRegion[] array */
- int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
+ int nByte = nReqRegion*szRegion; /* Minimum required file size */
struct stat sStat; /* Used by fstat() */
pShmNode->szRegion = szRegion;
@@ -28561,49 +33814,71 @@ static int unixShmMap(
if( sStat.st_size<nByte ){
/* The requested memory region does not exist. If bExtend is set to
** false, exit early. *pp will be set to NULL and SQLITE_OK returned.
- **
- ** Alternatively, if bExtend is true, use ftruncate() to allocate
- ** the requested memory region.
*/
- if( !bExtend ) goto shmpage_out;
- if( robust_ftruncate(pShmNode->h, nByte) ){
- rc = unixLogError(SQLITE_IOERR_SHMSIZE, "ftruncate",
- pShmNode->zFilename);
+ if( !bExtend ){
goto shmpage_out;
}
+
+ /* Alternatively, if bExtend is true, extend the file. Do this by
+ ** writing a single byte to the end of each (OS) page being
+ ** allocated or extended. Technically, we need only write to the
+ ** last page in order to extend the file. But writing to all new
+ ** pages forces the OS to allocate them immediately, which reduces
+ ** the chances of SIGBUS while accessing the mapped region later on.
+ */
+ else{
+ static const int pgsz = 4096;
+ int iPg;
+
+ /* Write to the last byte of each newly allocated or extended page */
+ assert( (nByte % pgsz)==0 );
+ for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){
+ int x = 0;
+ if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){
+ const char *zFile = pShmNode->zFilename;
+ rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile);
+ goto shmpage_out;
+ }
+ }
+ }
}
}
/* Map the requested memory region into this processes address space. */
apNew = (char **)sqlite3_realloc(
- pShmNode->apRegion, (iRegion+1)*sizeof(char *)
+ pShmNode->apRegion, nReqRegion*sizeof(char *)
);
if( !apNew ){
- rc = SQLITE_IOERR_NOMEM;
+ rc = SQLITE_IOERR_NOMEM_BKPT;
goto shmpage_out;
}
pShmNode->apRegion = apNew;
- while(pShmNode->nRegion<=iRegion){
+ while( pShmNode->nRegion<nReqRegion ){
+ int nMap = szRegion*nShmPerMap;
+ int i;
void *pMem;
if( pShmNode->h>=0 ){
- pMem = mmap(0, szRegion,
+ pMem = osMmap(0, nMap,
pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE,
- MAP_SHARED, pShmNode->h, pShmNode->nRegion*szRegion
+ MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion
);
if( pMem==MAP_FAILED ){
rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename);
goto shmpage_out;
}
}else{
- pMem = sqlite3_malloc(szRegion);
+ pMem = sqlite3_malloc64(szRegion);
if( pMem==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto shmpage_out;
}
memset(pMem, 0, szRegion);
}
- pShmNode->apRegion[pShmNode->nRegion] = pMem;
- pShmNode->nRegion++;
+
+ for(i=0; i<nShmPerMap; i++){
+ pShmNode->apRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i];
+ }
+ pShmNode->nRegion += nShmPerMap;
}
}
@@ -28666,7 +33941,7 @@ static int unixShmLock(
/* Unlock the system-level locks */
if( (mask & allMask)==0 ){
- rc = unixShmSystemLock(pShmNode, F_UNLCK, ofst+UNIX_SHM_BASE, n);
+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
}else{
rc = SQLITE_OK;
}
@@ -28694,7 +33969,7 @@ static int unixShmLock(
/* Get shared locks at the system level, if necessary */
if( rc==SQLITE_OK ){
if( (allShared & mask)==0 ){
- rc = unixShmSystemLock(pShmNode, F_RDLCK, ofst+UNIX_SHM_BASE, n);
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
}else{
rc = SQLITE_OK;
}
@@ -28719,7 +33994,7 @@ static int unixShmLock(
** also mark the local connection as being locked.
*/
if( rc==SQLITE_OK ){
- rc = unixShmSystemLock(pShmNode, F_WRLCK, ofst+UNIX_SHM_BASE, n);
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
if( rc==SQLITE_OK ){
assert( (p->sharedMask & mask)==0 );
p->exclMask |= mask;
@@ -28728,7 +34003,7 @@ static int unixShmLock(
}
sqlite3_mutex_leave(pShmNode->mutex);
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
- p->id, getpid(), p->sharedMask, p->exclMask));
+ p->id, osGetpid(0), p->sharedMask, p->exclMask));
return rc;
}
@@ -28742,7 +34017,8 @@ static void unixShmBarrier(
sqlite3_file *fd /* Database file holding the shared memory */
){
UNUSED_PARAMETER(fd);
- unixEnterMutex();
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ unixEnterMutex(); /* Also mutex, for redundancy */
unixLeaveMutex();
}
@@ -28787,7 +34063,9 @@ static int unixShmUnmap(
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
if( pShmNode->nRef==0 ){
- if( deleteFlag && pShmNode->h>=0 ) osUnlink(pShmNode->zFilename);
+ if( deleteFlag && pShmNode->h>=0 ){
+ osUnlink(pShmNode->zFilename);
+ }
unixShmPurge(pDbFd);
}
unixLeaveMutex();
@@ -28803,6 +34081,223 @@ static int unixShmUnmap(
# define unixShmUnmap 0
#endif /* #ifndef SQLITE_OMIT_WAL */
+#if SQLITE_MAX_MMAP_SIZE>0
+/*
+** If it is currently memory mapped, unmap file pFd.
+*/
+static void unixUnmapfile(unixFile *pFd){
+ assert( pFd->nFetchOut==0 );
+ if( pFd->pMapRegion ){
+ osMunmap(pFd->pMapRegion, pFd->mmapSizeActual);
+ pFd->pMapRegion = 0;
+ pFd->mmapSize = 0;
+ pFd->mmapSizeActual = 0;
+ }
+}
+
+/*
+** Attempt to set the size of the memory mapping maintained by file
+** descriptor pFd to nNew bytes. Any existing mapping is discarded.
+**
+** If successful, this function sets the following variables:
+**
+** unixFile.pMapRegion
+** unixFile.mmapSize
+** unixFile.mmapSizeActual
+**
+** If unsuccessful, an error message is logged via sqlite3_log() and
+** the three variables above are zeroed. In this case SQLite should
+** continue accessing the database using the xRead() and xWrite()
+** methods.
+*/
+static void unixRemapfile(
+ unixFile *pFd, /* File descriptor object */
+ i64 nNew /* Required mapping size */
+){
+ const char *zErr = "mmap";
+ int h = pFd->h; /* File descriptor open on db file */
+ u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */
+ i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */
+ u8 *pNew = 0; /* Location of new mapping */
+ int flags = PROT_READ; /* Flags to pass to mmap() */
+
+ assert( pFd->nFetchOut==0 );
+ assert( nNew>pFd->mmapSize );
+ assert( nNew<=pFd->mmapSizeMax );
+ assert( nNew>0 );
+ assert( pFd->mmapSizeActual>=pFd->mmapSize );
+ assert( MAP_FAILED!=0 );
+
+#ifdef SQLITE_MMAP_READWRITE
+ if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE;
+#endif
+
+ if( pOrig ){
+#if HAVE_MREMAP
+ i64 nReuse = pFd->mmapSize;
+#else
+ const int szSyspage = osGetpagesize();
+ i64 nReuse = (pFd->mmapSize & ~(szSyspage-1));
+#endif
+ u8 *pReq = &pOrig[nReuse];
+
+ /* Unmap any pages of the existing mapping that cannot be reused. */
+ if( nReuse!=nOrig ){
+ osMunmap(pReq, nOrig-nReuse);
+ }
+
+#if HAVE_MREMAP
+ pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE);
+ zErr = "mremap";
+#else
+ pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse);
+ if( pNew!=MAP_FAILED ){
+ if( pNew!=pReq ){
+ osMunmap(pNew, nNew - nReuse);
+ pNew = 0;
+ }else{
+ pNew = pOrig;
+ }
+ }
+#endif
+
+ /* The attempt to extend the existing mapping failed. Free it. */
+ if( pNew==MAP_FAILED || pNew==0 ){
+ osMunmap(pOrig, nReuse);
+ }
+ }
+
+ /* If pNew is still NULL, try to create an entirely new mapping. */
+ if( pNew==0 ){
+ pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0);
+ }
+
+ if( pNew==MAP_FAILED ){
+ pNew = 0;
+ nNew = 0;
+ unixLogError(SQLITE_OK, zErr, pFd->zPath);
+
+ /* If the mmap() above failed, assume that all subsequent mmap() calls
+ ** will probably fail too. Fall back to using xRead/xWrite exclusively
+ ** in this case. */
+ pFd->mmapSizeMax = 0;
+ }
+ pFd->pMapRegion = (void *)pNew;
+ pFd->mmapSize = pFd->mmapSizeActual = nNew;
+}
+
+/*
+** Memory map or remap the file opened by file-descriptor pFd (if the file
+** is already mapped, the existing mapping is replaced by the new). Or, if
+** there already exists a mapping for this file, and there are still
+** outstanding xFetch() references to it, this function is a no-op.
+**
+** If parameter nByte is non-negative, then it is the requested size of
+** the mapping to create. Otherwise, if nByte is less than zero, then the
+** requested size is the size of the file on disk. The actual size of the
+** created mapping is either the requested size or the value configured
+** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller.
+**
+** SQLITE_OK is returned if no error occurs (even if the mapping is not
+** recreated as a result of outstanding references) or an SQLite error
+** code otherwise.
+*/
+static int unixMapfile(unixFile *pFd, i64 nMap){
+ assert( nMap>=0 || pFd->nFetchOut==0 );
+ assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
+ if( pFd->nFetchOut>0 ) return SQLITE_OK;
+
+ if( nMap<0 ){
+ struct stat statbuf; /* Low-level file information */
+ if( osFstat(pFd->h, &statbuf) ){
+ return SQLITE_IOERR_FSTAT;
+ }
+ nMap = statbuf.st_size;
+ }
+ if( nMap>pFd->mmapSizeMax ){
+ nMap = pFd->mmapSizeMax;
+ }
+
+ assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) );
+ if( nMap!=pFd->mmapSize ){
+ unixRemapfile(pFd, nMap);
+ }
+
+ return SQLITE_OK;
+}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
+
+/*
+** If possible, return a pointer to a mapping of file fd starting at offset
+** iOff. The mapping must be valid for at least nAmt bytes.
+**
+** If such a pointer can be obtained, store it in *pp and return SQLITE_OK.
+** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK.
+** Finally, if an error does occur, return an SQLite error code. The final
+** value of *pp is undefined in this case.
+**
+** If this function does return a pointer, the caller must eventually
+** release the reference by calling unixUnfetch().
+*/
+static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
+#if SQLITE_MAX_MMAP_SIZE>0
+ unixFile *pFd = (unixFile *)fd; /* The underlying database file */
+#endif
+ *pp = 0;
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ if( pFd->mmapSizeMax>0 ){
+ if( pFd->pMapRegion==0 ){
+ int rc = unixMapfile(pFd, -1);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ if( pFd->mmapSize >= iOff+nAmt ){
+ *pp = &((u8 *)pFd->pMapRegion)[iOff];
+ pFd->nFetchOut++;
+ }
+ }
+#endif
+ return SQLITE_OK;
+}
+
+/*
+** If the third argument is non-NULL, then this function releases a
+** reference obtained by an earlier call to unixFetch(). The second
+** argument passed to this function must be the same as the corresponding
+** argument that was passed to the unixFetch() invocation.
+**
+** Or, if the third argument is NULL, then this function is being called
+** to inform the VFS layer that, according to POSIX, any existing mapping
+** may now be invalid and should be unmapped.
+*/
+static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){
+#if SQLITE_MAX_MMAP_SIZE>0
+ unixFile *pFd = (unixFile *)fd; /* The underlying database file */
+ UNUSED_PARAMETER(iOff);
+
+ /* If p==0 (unmap the entire file) then there must be no outstanding
+ ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
+ ** then there must be at least one outstanding. */
+ assert( (p==0)==(pFd->nFetchOut==0) );
+
+ /* If p!=0, it must match the iOff value. */
+ assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
+
+ if( p ){
+ pFd->nFetchOut--;
+ }else{
+ unixUnmapfile(pFd);
+ }
+
+ assert( pFd->nFetchOut>=0 );
+#else
+ UNUSED_PARAMETER(fd);
+ UNUSED_PARAMETER(p);
+ UNUSED_PARAMETER(iOff);
+#endif
+ return SQLITE_OK;
+}
+
/*
** Here ends the implementation of all sqlite3_file methods.
**
@@ -28822,7 +34317,7 @@ static int unixShmUnmap(
** looks at the filesystem type and tries to guess the best locking
** strategy from that.
**
-** For finder-funtion F, two objects are created:
+** For finder-function F, two objects are created:
**
** (1) The real finder-function named "FImpt()".
**
@@ -28843,7 +34338,7 @@ static int unixShmUnmap(
** * An I/O method finder function called FINDER that returns a pointer
** to the METHOD object in the previous bullet.
*/
-#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \
+#define IOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \
static const sqlite3_io_methods METHOD = { \
VERSION, /* iVersion */ \
CLOSE, /* xClose */ \
@@ -28858,10 +34353,12 @@ static const sqlite3_io_methods METHOD = { \
unixFileControl, /* xFileControl */ \
unixSectorSize, /* xSectorSize */ \
unixDeviceCharacteristics, /* xDeviceCapabilities */ \
- unixShmMap, /* xShmMap */ \
+ SHMMAP, /* xShmMap */ \
unixShmLock, /* xShmLock */ \
unixShmBarrier, /* xShmBarrier */ \
- unixShmUnmap /* xShmUnmap */ \
+ unixShmUnmap, /* xShmUnmap */ \
+ unixFetch, /* xFetch */ \
+ unixUnfetch, /* xUnfetch */ \
}; \
static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \
UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \
@@ -28878,20 +34375,22 @@ static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \
IOMETHODS(
posixIoFinder, /* Finder function name */
posixIoMethods, /* sqlite3_io_methods object name */
- 2, /* shared memory is enabled */
+ 3, /* shared memory and mmap are enabled */
unixClose, /* xClose method */
unixLock, /* xLock method */
unixUnlock, /* xUnlock method */
- unixCheckReservedLock /* xCheckReservedLock method */
+ unixCheckReservedLock, /* xCheckReservedLock method */
+ unixShmMap /* xShmMap method */
)
IOMETHODS(
nolockIoFinder, /* Finder function name */
nolockIoMethods, /* sqlite3_io_methods object name */
- 1, /* shared memory is disabled */
+ 3, /* shared memory is disabled */
nolockClose, /* xClose method */
nolockLock, /* xLock method */
nolockUnlock, /* xUnlock method */
- nolockCheckReservedLock /* xCheckReservedLock method */
+ nolockCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
IOMETHODS(
dotlockIoFinder, /* Finder function name */
@@ -28900,10 +34399,11 @@ IOMETHODS(
dotlockClose, /* xClose method */
dotlockLock, /* xLock method */
dotlockUnlock, /* xUnlock method */
- dotlockCheckReservedLock /* xCheckReservedLock method */
+ dotlockCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
-#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
+#if SQLITE_ENABLE_LOCKING_STYLE
IOMETHODS(
flockIoFinder, /* Finder function name */
flockIoMethods, /* sqlite3_io_methods object name */
@@ -28911,7 +34411,8 @@ IOMETHODS(
flockClose, /* xClose method */
flockLock, /* xLock method */
flockUnlock, /* xUnlock method */
- flockCheckReservedLock /* xCheckReservedLock method */
+ flockCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28920,10 +34421,11 @@ IOMETHODS(
semIoFinder, /* Finder function name */
semIoMethods, /* sqlite3_io_methods object name */
1, /* shared memory is disabled */
- semClose, /* xClose method */
- semLock, /* xLock method */
- semUnlock, /* xUnlock method */
- semCheckReservedLock /* xCheckReservedLock method */
+ semXClose, /* xClose method */
+ semXLock, /* xLock method */
+ semXUnlock, /* xUnlock method */
+ semXCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28935,7 +34437,8 @@ IOMETHODS(
afpClose, /* xClose method */
afpLock, /* xLock method */
afpUnlock, /* xUnlock method */
- afpCheckReservedLock /* xCheckReservedLock method */
+ afpCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28960,7 +34463,8 @@ IOMETHODS(
proxyClose, /* xClose method */
proxyLock, /* xLock method */
proxyUnlock, /* xUnlock method */
- proxyCheckReservedLock /* xCheckReservedLock method */
+ proxyCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -28973,7 +34477,8 @@ IOMETHODS(
unixClose, /* xClose method */
unixLock, /* xLock method */
nfsUnlock, /* xUnlock method */
- unixCheckReservedLock /* xCheckReservedLock method */
+ unixCheckReservedLock, /* xCheckReservedLock method */
+ 0 /* xShmMap method */
)
#endif
@@ -29043,15 +34548,13 @@ static const sqlite3_io_methods
#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */
-#if OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE
-/*
-** This "finder" function attempts to determine the best locking strategy
-** for the database file "filePath". It then returns the sqlite3_io_methods
-** object that implements that strategy.
-**
-** This is for VXWorks only.
+#if OS_VXWORKS
+/*
+** This "finder" function for VxWorks checks to see if posix advisory
+** locking works. If it does, then that is what is used. If it does not
+** work, then fallback to named semaphore locking.
*/
-static const sqlite3_io_methods *autolockIoFinderImpl(
+static const sqlite3_io_methods *vxworksIoFinderImpl(
const char *filePath, /* name of the database file */
unixFile *pNew /* the open file object */
){
@@ -29077,12 +34580,12 @@ static const sqlite3_io_methods *autolockIoFinderImpl(
}
}
static const sqlite3_io_methods
- *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl;
+ *(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl;
-#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */
+#endif /* OS_VXWORKS */
/*
-** An abstract type for a pointer to a IO method finder function:
+** An abstract type for a pointer to an IO method finder function:
*/
typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*);
@@ -29100,12 +34603,9 @@ typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*);
static int fillInUnixFile(
sqlite3_vfs *pVfs, /* Pointer to vfs object */
int h, /* Open file descriptor of file being opened */
- int syncDir, /* True to sync directory on first sync */
sqlite3_file *pId, /* Write to the unixFile structure here */
const char *zFilename, /* Name of the file being opened */
- int noLock, /* Omit locking if true */
- int isDelete, /* Delete on close if true */
- int isReadOnly /* True if the file is opened read-only */
+ int ctrlFlags /* Zero or more UNIXFILE_* values */
){
const sqlite3_io_methods *pLockingStyle;
unixFile *pNew = (unixFile *)pId;
@@ -29113,11 +34613,6 @@ static int fillInUnixFile(
assert( pNew->pInode==NULL );
- /* Parameter isDelete is only used on vxworks. Express this explicitly
- ** here to prevent compiler warnings about unused parameters.
- */
- UNUSED_PARAMETER(isDelete);
-
/* Usually the path zFilename should not be a relative pathname. The
** exception is when opening the proxy "conch" file in builds that
** include the special Apple locking styles.
@@ -29129,30 +34624,34 @@ static int fillInUnixFile(
assert( zFilename==0 || zFilename[0]=='/' );
#endif
+ /* No locking occurs in temporary files */
+ assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 );
+
OSTRACE(("OPEN %-3d %s\n", h, zFilename));
pNew->h = h;
+ pNew->pVfs = pVfs;
pNew->zPath = zFilename;
- if( memcmp(pVfs->zName,"unix-excl",10)==0 ){
- pNew->ctrlFlags = UNIXFILE_EXCL;
- }else{
- pNew->ctrlFlags = 0;
- }
- if( isReadOnly ){
- pNew->ctrlFlags |= UNIXFILE_RDONLY;
+ pNew->ctrlFlags = (u8)ctrlFlags;
+#if SQLITE_MAX_MMAP_SIZE>0
+ pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap;
+#endif
+ if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0),
+ "psow", SQLITE_POWERSAFE_OVERWRITE) ){
+ pNew->ctrlFlags |= UNIXFILE_PSOW;
}
- if( syncDir ){
- pNew->ctrlFlags |= UNIXFILE_DIRSYNC;
+ if( strcmp(pVfs->zName,"unix-excl")==0 ){
+ pNew->ctrlFlags |= UNIXFILE_EXCL;
}
#if OS_VXWORKS
pNew->pId = vxworksFindFileId(zFilename);
if( pNew->pId==0 ){
- noLock = 1;
- rc = SQLITE_NOMEM;
+ ctrlFlags |= UNIXFILE_NOLOCK;
+ rc = SQLITE_NOMEM_BKPT;
}
#endif
- if( noLock ){
+ if( ctrlFlags & UNIXFILE_NOLOCK ){
pLockingStyle = &nolockIoMethods;
}else{
pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew);
@@ -29172,7 +34671,7 @@ static int fillInUnixFile(
unixEnterMutex();
rc = findInodeInfo(pNew, &pNew->pInode);
if( rc!=SQLITE_OK ){
- /* If an error occured in findInodeInfo(), close the file descriptor
+ /* If an error occurred in findInodeInfo(), close the file descriptor
** immediately, before releasing the mutex. findInodeInfo() may fail
** in two scenarios:
**
@@ -29202,9 +34701,9 @@ static int fillInUnixFile(
** the afpLockingContext.
*/
afpLockingContext *pCtx;
- pNew->lockingContext = pCtx = sqlite3_malloc( sizeof(*pCtx) );
+ pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) );
if( pCtx==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
/* NB: zFilename exists and remains valid until the file is closed
** according to requirement F11141. So we do not need to make a
@@ -29230,10 +34729,11 @@ static int fillInUnixFile(
*/
char *zLockFile;
int nFilename;
+ assert( zFilename!=0 );
nFilename = (int)strlen(zFilename) + 6;
- zLockFile = (char *)sqlite3_malloc(nFilename);
+ zLockFile = (char *)sqlite3_malloc64(nFilename);
if( zLockFile==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename);
}
@@ -29256,7 +34756,7 @@ static int fillInUnixFile(
if( zSemName[n]=='/' ) zSemName[n] = '_';
pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1);
if( pNew->pInode->pSem == SEM_FAILED ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
pNew->pInode->aSemName[0] = '\0';
}
}
@@ -29264,21 +34764,21 @@ static int fillInUnixFile(
}
#endif
- pNew->lastErrno = 0;
+ storeLastErrno(pNew, 0);
#if OS_VXWORKS
if( rc!=SQLITE_OK ){
if( h>=0 ) robust_close(pNew, h, __LINE__);
h = -1;
osUnlink(zFilename);
- isDelete = 0;
+ pNew->ctrlFlags |= UNIXFILE_DELETE;
}
- pNew->isDelete = isDelete;
#endif
if( rc!=SQLITE_OK ){
if( h>=0 ) robust_close(pNew, h, __LINE__);
}else{
pNew->pMethod = pLockingStyle;
OpenCounter(+1);
+ verifyDbFile(pNew);
}
return rc;
}
@@ -29294,22 +34794,26 @@ static const char *unixTempFileDir(void){
"/var/tmp",
"/usr/tmp",
"/tmp",
- 0 /* List terminator */
+ "."
};
- unsigned int i;
+ unsigned int i = 0;
struct stat buf;
- const char *zDir = 0;
+ const char *zDir = sqlite3_temp_directory;
- azDirs[0] = sqlite3_temp_directory;
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
- for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
- if( zDir==0 ) continue;
- if( osStat(zDir, &buf) ) continue;
- if( !S_ISDIR(buf.st_mode) ) continue;
- if( osAccess(zDir, 07) ) continue;
- break;
+ while(1){
+ if( zDir!=0
+ && osStat(zDir, &buf)==0
+ && S_ISDIR(buf.st_mode)
+ && osAccess(zDir, 03)==0
+ ){
+ return zDir;
+ }
+ if( i>=sizeof(azDirs)/sizeof(azDirs[0]) ) break;
+ zDir = azDirs[i++];
}
- return zDir;
+ return 0;
}
/*
@@ -29318,37 +34822,26 @@ static const char *unixTempFileDir(void){
** pVfs->mxPathname bytes.
*/
static int unixGetTempname(int nBuf, char *zBuf){
- static const unsigned char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- unsigned int i, j;
const char *zDir;
+ int iLimit = 0;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
** function failing.
*/
+ zBuf[0] = 0;
SimulateIOError( return SQLITE_IOERR );
zDir = unixTempFileDir();
- if( zDir==0 ) zDir = ".";
-
- /* Check that the output buffer is large enough for the temporary file
- ** name. If it is not, return SQLITE_ERROR.
- */
- if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 17) >= (size_t)nBuf ){
- return SQLITE_ERROR;
- }
-
+ if( zDir==0 ) return SQLITE_IOERR_GETTEMPPATH;
do{
- sqlite3_snprintf(nBuf-17, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
- j = (int)strlen(zBuf);
- sqlite3_randomness(15, &zBuf[j]);
- for(i=0; i<15; i++, j++){
- zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
+ u64 r;
+ sqlite3_randomness(sizeof(r), &r);
+ assert( nBuf>2 );
+ zBuf[nBuf-2] = 0;
+ sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c",
+ zDir, r, 0);
+ if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR;
}while( osAccess(zBuf,0)==0 );
return SQLITE_OK;
}
@@ -29396,7 +34889,7 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
** descriptor on the same path, fail, and return an error to SQLite.
**
** Even if a subsequent open() call does succeed, the consequences of
- ** not searching for a resusable file descriptor are not dire. */
+ ** not searching for a reusable file descriptor are not dire. */
if( 0==osStat(zPath, &sStat) ){
unixInodeInfo *pInode;
@@ -29421,18 +34914,37 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
}
/*
+** Find the mode, uid and gid of file zFile.
+*/
+static int getFileMode(
+ const char *zFile, /* File name */
+ mode_t *pMode, /* OUT: Permissions of zFile */
+ uid_t *pUid, /* OUT: uid of zFile. */
+ gid_t *pGid /* OUT: gid of zFile. */
+){
+ struct stat sStat; /* Output of stat() on database file */
+ int rc = SQLITE_OK;
+ if( 0==osStat(zFile, &sStat) ){
+ *pMode = sStat.st_mode & 0777;
+ *pUid = sStat.st_uid;
+ *pGid = sStat.st_gid;
+ }else{
+ rc = SQLITE_IOERR_FSTAT;
+ }
+ return rc;
+}
+
+/*
** This function is called by unixOpen() to determine the unix permissions
** to create new files with. If no error occurs, then SQLITE_OK is returned
** and a value suitable for passing as the third argument to open(2) is
** written to *pMode. If an IO error occurs, an SQLite error code is
** returned and the value of *pMode is not modified.
**
-** If the file being opened is a temporary file, it is always created with
-** the octal permissions 0600 (read/writable by owner only). If the file
-** is a database or master journal file, it is created with the permissions
-** mask SQLITE_DEFAULT_FILE_PERMISSIONS.
-**
-** Finally, if the file being opened is a WAL or regular journal file, then
+** In most cases, this routine sets *pMode to 0, which will become
+** an indication to robust_open() to create the file using
+** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask.
+** But if the file being opened is a WAL or regular journal file, then
** this function queries the file-system for the permissions on the
** corresponding database file and sets *pMode to this value. Whenever
** possible, WAL and journal files are created using the same permissions
@@ -29446,14 +34958,17 @@ static UnixUnusedFd *findReusableFd(const char *zPath, int flags){
static int findCreateFileMode(
const char *zPath, /* Path of file (possibly) being created */
int flags, /* Flags passed as 4th argument to xOpen() */
- mode_t *pMode /* OUT: Permissions to open file with */
+ mode_t *pMode, /* OUT: Permissions to open file with */
+ uid_t *pUid, /* OUT: uid to set on the file */
+ gid_t *pGid /* OUT: gid to set on the file */
){
int rc = SQLITE_OK; /* Return Code */
- *pMode = SQLITE_DEFAULT_FILE_PERMISSIONS;
+ *pMode = 0;
+ *pUid = 0;
+ *pGid = 0;
if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
char zDb[MAX_PATHNAME+1]; /* Database file path */
int nDb; /* Number of valid bytes in zDb */
- struct stat sStat; /* Output of stat() on database file */
/* zPath is a path to a WAL or journal file. The following block derives
** the path to the associated database file from zPath. This block handles
@@ -29464,22 +34979,38 @@ static int findCreateFileMode(
** "<path to db>-journalNN"
** "<path to db>-walNN"
**
- ** where NN is a 4 digit decimal number. The NN naming schemes are
+ ** where NN is a decimal number. The NN naming schemes are
** used by the test_multiplex.c module.
*/
nDb = sqlite3Strlen30(zPath) - 1;
- while( nDb>0 && zPath[nDb]!='-' ) nDb--;
- if( nDb==0 ) return SQLITE_OK;
+ while( zPath[nDb]!='-' ){
+#ifndef SQLITE_ENABLE_8_3_NAMES
+ /* In the normal case (8+3 filenames disabled) the journal filename
+ ** is guaranteed to contain a '-' character. */
+ assert( nDb>0 );
+ assert( sqlite3Isalnum(zPath[nDb]) );
+#else
+ /* If 8+3 names are possible, then the journal file might not contain
+ ** a '-' character. So check for that case and return early. */
+ if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK;
+#endif
+ nDb--;
+ }
memcpy(zDb, zPath, nDb);
zDb[nDb] = '\0';
- if( 0==osStat(zDb, &sStat) ){
- *pMode = sStat.st_mode & 0777;
- }else{
- rc = SQLITE_IOERR_FSTAT;
- }
+ rc = getFileMode(zDb, pMode, pUid, pGid);
}else if( flags & SQLITE_OPEN_DELETEONCLOSE ){
*pMode = 0600;
+ }else if( flags & SQLITE_OPEN_URI ){
+ /* If this is a main database file and the file was opened using a URI
+ ** filename, check for the "modeof" parameter. If present, interpret
+ ** its value as a filename and try to copy the mode, uid and gid from
+ ** that file. */
+ const char *z = sqlite3_uri_parameter(zPath, "modeof");
+ if( z ){
+ rc = getFileMode(z, pMode, pUid, pGid);
+ }
}
return rc;
}
@@ -29519,6 +35050,7 @@ static int unixOpen(
int eType = flags&0xFFFFFF00; /* Type of file to open */
int noLock; /* True to omit locking primitives */
int rc = SQLITE_OK; /* Function Return Code */
+ int ctrlFlags = 0; /* UNIXFILE_* flags */
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
@@ -29545,7 +35077,7 @@ static int unixOpen(
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
- char zTmpname[MAX_PATHNAME+1];
+ char zTmpname[MAX_PATHNAME+2];
const char *zName = zPath;
/* Check the following statements are true:
@@ -29574,6 +35106,16 @@ static int unixOpen(
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
+ /* Detect a pid change and reset the PRNG. There is a race condition
+ ** here such that two or more threads all trying to open databases at
+ ** the same instant might all reset the PRNG. But multiple resets
+ ** are harmless.
+ */
+ if( randomnessPid!=osGetpid(0) ){
+ randomnessPid = osGetpid(0);
+ sqlite3_randomness(0,0);
+ }
+
memset(p, 0, sizeof(unixFile));
if( eType==SQLITE_OPEN_MAIN_DB ){
@@ -29582,20 +35124,30 @@ static int unixOpen(
if( pUnused ){
fd = pUnused->fd;
}else{
- pUnused = sqlite3_malloc(sizeof(*pUnused));
+ pUnused = sqlite3_malloc64(sizeof(*pUnused));
if( !pUnused ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
}
p->pUnused = pUnused;
+
+ /* Database filenames are double-zero terminated if they are not
+ ** URIs with parameters. Hence, they can always be passed into
+ ** sqlite3_uri_parameter(). */
+ assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 );
+
}else if( !zName ){
/* If zName is NULL, the upper layer is requesting a temp file. */
assert(isDelete && !syncDir);
- rc = unixGetTempname(MAX_PATHNAME+1, zTmpname);
+ rc = unixGetTempname(pVfs->mxPathname, zTmpname);
if( rc!=SQLITE_OK ){
return rc;
}
zName = zTmpname;
+
+ /* Generated temporary filenames are always double-zero terminated
+ ** for use by sqlite3_uri_parameter(). */
+ assert( zName[strlen(zName)+1]==0 );
}
/* Determine the value of the flags parameter passed to POSIX function
@@ -29610,7 +35162,9 @@ static int unixOpen(
if( fd<0 ){
mode_t openMode; /* Permissions to create file with */
- rc = findCreateFileMode(zName, flags, &openMode);
+ uid_t uid; /* Userid for the file */
+ gid_t gid; /* Groupid for the file */
+ rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid);
if( rc!=SQLITE_OK ){
assert( !p->pUnused );
assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL );
@@ -29618,7 +35172,8 @@ static int unixOpen(
}
fd = robust_open(zName, openFlags, openMode);
OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags));
- if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){
+ assert( !isExclusive || (openFlags & O_CREAT)!=0 );
+ if( fd<0 && errno!=EISDIR && isReadWrite ){
/* Failed to open the file for read/write access. Try read-only. */
flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
openFlags &= ~(O_RDWR|O_CREAT);
@@ -29631,6 +35186,14 @@ static int unixOpen(
rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName);
goto open_finished;
}
+
+ /* If this process is running as root and if creating a new rollback
+ ** journal or WAL file, set the ownership of the journal or WAL to be
+ ** the same as the original database.
+ */
+ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){
+ robustFchown(fd, uid, gid);
+ }
}
assert( fd>=0 );
if( pOutFlags ){
@@ -29645,6 +35208,12 @@ static int unixOpen(
if( isDelete ){
#if OS_VXWORKS
zPath = zName;
+#elif defined(SQLITE_UNLINK_AFTER_CLOSE)
+ zPath = sqlite3_mprintf("%s", zName);
+ if( zPath==0 ){
+ robust_close(p, fd, __LINE__);
+ return SQLITE_NOMEM_BKPT;
+ }
#else
osUnlink(zName);
#endif
@@ -29654,25 +35223,29 @@ static int unixOpen(
p->openFlags = openFlags;
}
#endif
-
-#ifdef FD_CLOEXEC
- osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC);
-#endif
-
- noLock = eType!=SQLITE_OPEN_MAIN_DB;
-
#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE
if( fstatfs(fd, &fsInfo) == -1 ){
- ((unixFile*)pFile)->lastErrno = errno;
+ storeLastErrno(p, errno);
robust_close(p, fd, __LINE__);
return SQLITE_IOERR_ACCESS;
}
if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) {
((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS;
}
+ if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) {
+ ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS;
+ }
#endif
-
+
+ /* Set up appropriate ctrlFlags */
+ if( isDelete ) ctrlFlags |= UNIXFILE_DELETE;
+ if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY;
+ noLock = eType!=SQLITE_OPEN_MAIN_DB;
+ if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK;
+ if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC;
+ if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI;
+
#if SQLITE_ENABLE_LOCKING_STYLE
#if SQLITE_PREFER_PROXY_LOCKING
isAutoProxy = 1;
@@ -29686,24 +35259,10 @@ static int unixOpen(
if( envforce!=NULL ){
useProxy = atoi(envforce)>0;
}else{
- if( statfs(zPath, &fsInfo) == -1 ){
- /* In theory, the close(fd) call is sub-optimal. If the file opened
- ** with fd is a database file, and there are other connections open
- ** on that file that are currently holding advisory locks on it,
- ** then the call to close() will cancel those locks. In practice,
- ** we're assuming that statfs() doesn't fail very often. At least
- ** not while other file descriptors opened by the same process on
- ** the same file are working. */
- p->lastErrno = errno;
- robust_close(p, fd, __LINE__);
- rc = SQLITE_IOERR_ACCESS;
- goto open_finished;
- }
useProxy = !(fsInfo.f_flags&MNT_LOCAL);
}
if( useProxy ){
- rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
- isDelete, isReadonly);
+ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
if( rc==SQLITE_OK ){
rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:");
if( rc!=SQLITE_OK ){
@@ -29720,8 +35279,8 @@ static int unixOpen(
}
#endif
- rc = fillInUnixFile(pVfs, fd, syncDir, pFile, zPath, noLock,
- isDelete, isReadonly);
+ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
+
open_finished:
if( rc!=SQLITE_OK ){
sqlite3_free(p->pUnused);
@@ -29742,24 +35301,29 @@ static int unixDelete(
int rc = SQLITE_OK;
UNUSED_PARAMETER(NotUsed);
SimulateIOError(return SQLITE_IOERR_DELETE);
- if( osUnlink(zPath)==(-1) && errno!=ENOENT ){
- return unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
+ if( osUnlink(zPath)==(-1) ){
+ if( errno==ENOENT
+#if OS_VXWORKS
+ || osAccess(zPath,0)!=0
+#endif
+ ){
+ rc = SQLITE_IOERR_DELETE_NOENT;
+ }else{
+ rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath);
+ }
+ return rc;
}
#ifndef SQLITE_DISABLE_DIRSYNC
- if( dirSync ){
+ if( (dirSync & 1)!=0 ){
int fd;
rc = osOpenDirectory(zPath, &fd);
if( rc==SQLITE_OK ){
-#if OS_VXWORKS
- if( fsync(fd)==-1 )
-#else
- if( fsync(fd) )
-#endif
- {
+ if( full_fsync(fd,0,0) ){
rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath);
}
robust_close(0, fd, __LINE__);
- }else if( rc==SQLITE_CANTOPEN ){
+ }else{
+ assert( rc==SQLITE_CANTOPEN );
rc = SQLITE_OK;
}
}
@@ -29768,7 +35332,7 @@ static int unixDelete(
}
/*
-** Test the existance of or access permissions of file zPath. The
+** Test the existence of or access permissions of file zPath. The
** test performed depends on the value of flags:
**
** SQLITE_ACCESS_EXISTS: Return 1 if the file exists
@@ -29783,33 +35347,49 @@ static int unixAccess(
int flags, /* What do we want to learn about the zPath file? */
int *pResOut /* Write result boolean here */
){
- int amode = 0;
UNUSED_PARAMETER(NotUsed);
SimulateIOError( return SQLITE_IOERR_ACCESS; );
- switch( flags ){
- case SQLITE_ACCESS_EXISTS:
- amode = F_OK;
- break;
- case SQLITE_ACCESS_READWRITE:
- amode = W_OK|R_OK;
- break;
- case SQLITE_ACCESS_READ:
- amode = R_OK;
- break;
+ assert( pResOut!=0 );
- default:
- assert(!"Invalid flags argument");
- }
- *pResOut = (osAccess(zPath, amode)==0);
- if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
+ /* The spec says there are three possible values for flags. But only
+ ** two of them are actually used */
+ assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE );
+
+ if( flags==SQLITE_ACCESS_EXISTS ){
struct stat buf;
- if( 0==osStat(zPath, &buf) && buf.st_size==0 ){
- *pResOut = 0;
- }
+ *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0);
+ }else{
+ *pResOut = osAccess(zPath, W_OK|R_OK)==0;
}
return SQLITE_OK;
}
+/*
+**
+*/
+static int mkFullPathname(
+ const char *zPath, /* Input path */
+ char *zOut, /* Output buffer */
+ int nOut /* Allocated size of buffer zOut */
+){
+ int nPath = sqlite3Strlen30(zPath);
+ int iOff = 0;
+ if( zPath[0]!='/' ){
+ if( osGetcwd(zOut, nOut-2)==0 ){
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
+ }
+ iOff = sqlite3Strlen30(zOut);
+ zOut[iOff++] = '/';
+ }
+ if( (iOff+nPath+1)>nOut ){
+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer
+ ** even if it returns an error. */
+ zOut[iOff] = '\0';
+ return SQLITE_CANTOPEN_BKPT;
+ }
+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
+ return SQLITE_OK;
+}
/*
** Turn a relative pathname into a full pathname. The relative path
@@ -29826,6 +35406,17 @@ static int unixFullPathname(
int nOut, /* Size of output buffer in bytes */
char *zOut /* Output buffer */
){
+#if !defined(HAVE_READLINK) || !defined(HAVE_LSTAT)
+ return mkFullPathname(zPath, zOut, nOut);
+#else
+ int rc = SQLITE_OK;
+ int nByte;
+ int nLink = 1; /* Number of symbolic links followed so far */
+ const char *zIn = zPath; /* Input path for each iteration of loop */
+ char *zDel = 0;
+
+ assert( pVfs->mxPathname==MAX_PATHNAME );
+ UNUSED_PARAMETER(pVfs);
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
@@ -29834,21 +35425,62 @@ static int unixFullPathname(
*/
SimulateIOError( return SQLITE_ERROR );
- assert( pVfs->mxPathname==MAX_PATHNAME );
- UNUSED_PARAMETER(pVfs);
+ do {
- zOut[nOut-1] = '\0';
- if( zPath[0]=='/' ){
- sqlite3_snprintf(nOut, zOut, "%s", zPath);
- }else{
- int nCwd;
- if( osGetcwd(zOut, nOut-1)==0 ){
- return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
+ /* Call stat() on path zIn. Set bLink to true if the path is a symbolic
+ ** link, or false otherwise. */
+ int bLink = 0;
+ struct stat buf;
+ if( osLstat(zIn, &buf)!=0 ){
+ if( errno!=ENOENT ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "lstat", zIn);
+ }
+ }else{
+ bLink = S_ISLNK(buf.st_mode);
}
- nCwd = (int)strlen(zOut);
- sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath);
- }
- return SQLITE_OK;
+
+ if( bLink ){
+ if( zDel==0 ){
+ zDel = sqlite3_malloc(nOut);
+ if( zDel==0 ) rc = SQLITE_NOMEM_BKPT;
+ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }
+
+ if( rc==SQLITE_OK ){
+ nByte = osReadlink(zIn, zDel, nOut-1);
+ if( nByte<0 ){
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zIn);
+ }else{
+ if( zDel[0]!='/' ){
+ int n;
+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
+ if( nByte+n+1>nOut ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }else{
+ memmove(&zDel[n], zDel, nByte+1);
+ memcpy(zDel, zIn, n);
+ nByte += n;
+ }
+ }
+ zDel[nByte] = '\0';
+ }
+ }
+
+ zIn = zDel;
+ }
+
+ assert( rc!=SQLITE_OK || zIn!=zOut || zIn[0]=='/' );
+ if( rc==SQLITE_OK && zIn!=zOut ){
+ rc = mkFullPathname(zIn, zOut, nOut);
+ }
+ if( bLink==0 ) break;
+ zIn = zOut;
+ }while( rc==SQLITE_OK );
+
+ sqlite3_free(zDel);
+ return rc;
+#endif /* HAVE_READLINK && HAVE_LSTAT */
}
@@ -29934,20 +35566,20 @@ static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){
** tests repeatable.
*/
memset(zBuf, 0, nBuf);
-#if !defined(SQLITE_TEST)
+ randomnessPid = osGetpid(0);
+#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS)
{
- int pid, fd;
+ int fd, got;
fd = robust_open("/dev/urandom", O_RDONLY, 0);
if( fd<0 ){
time_t t;
time(&t);
memcpy(zBuf, &t, sizeof(t));
- pid = getpid();
- memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
- assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
- nBuf = sizeof(t) + sizeof(pid);
+ memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid));
+ assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf );
+ nBuf = sizeof(t) + sizeof(randomnessPid);
}else{
- do{ nBuf = osRead(fd, zBuf, nBuf); }while( nBuf<0 && errno==EINTR );
+ do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR );
robust_close(0, fd, __LINE__);
}
}
@@ -30001,10 +35633,12 @@ SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
** proleptic Gregorian calendar.
**
-** On success, return 0. Return 1 if the time and date cannot be found.
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
+** cannot be found.
*/
static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
+ int rc = SQLITE_OK;
#if defined(NO_GETTOD)
time_t t;
time(&t);
@@ -30015,7 +35649,7 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000;
#else
struct timeval sNow;
- gettimeofday(&sNow, 0);
+ (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */
*piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
#endif
@@ -30025,34 +35659,38 @@ static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){
}
#endif
UNUSED_PARAMETER(NotUsed);
- return 0;
+ return rc;
}
+#ifndef SQLITE_OMIT_DEPRECATED
/*
** Find the current time (in Universal Coordinated Time). Write the
** current time and date as a Julian Day number into *prNow and
** return 0. Return 1 if the time and date cannot be found.
*/
static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){
- sqlite3_int64 i;
+ sqlite3_int64 i = 0;
+ int rc;
UNUSED_PARAMETER(NotUsed);
- unixCurrentTimeInt64(0, &i);
+ rc = unixCurrentTimeInt64(0, &i);
*prNow = i/86400000.0;
- return 0;
+ return rc;
}
+#else
+# define unixCurrentTime 0
+#endif
/*
-** We added the xGetLastError() method with the intention of providing
-** better low-level error messages when operating-system problems come up
-** during SQLite operation. But so far, none of that has been implemented
-** in the core. So this routine is never called. For now, it is merely
-** a place-holder.
+** The xGetLastError() method is designed to return a better
+** low-level error message when operating-system problems come up
+** during SQLite operation. Only the integer return code is currently
+** used.
*/
static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
UNUSED_PARAMETER(NotUsed);
UNUSED_PARAMETER(NotUsed2);
UNUSED_PARAMETER(NotUsed3);
- return 0;
+ return errno;
}
@@ -30084,7 +35722,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** address in the shared range is taken for a SHARED lock, the entire
** shared range is taken for an EXCLUSIVE lock):
**
-** PENDING_BYTE 0x40000000
+** PENDING_BYTE 0x40000000
** RESERVED_BYTE 0x40000001
** SHARED_RANGE 0x40000002 -> 0x40000200
**
@@ -30110,9 +35748,10 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
**
** C APIs
**
-** sqlite3_file_control(db, dbname, SQLITE_SET_LOCKPROXYFILE,
+** sqlite3_file_control(db, dbname, SQLITE_FCNTL_SET_LOCKPROXYFILE,
** <proxy_path> | ":auto:");
-** sqlite3_file_control(db, dbname, SQLITE_GET_LOCKPROXYFILE, &<proxy_path>);
+** sqlite3_file_control(db, dbname, SQLITE_FCNTL_GET_LOCKPROXYFILE,
+** &<proxy_path>);
**
**
** SQL pragmas
@@ -30153,7 +35792,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** proxy path against the values stored in the conch. The conch file is
** stored in the same directory as the database file and the file name
** is patterned after the database file name as ".<databasename>-conch".
-** If the conch file does not exist, or it's contents do not match the
+** If the conch file does not exist, or its contents do not match the
** host ID and/or proxy path, then the lock is escalated to an exclusive
** lock and the conch file contents is updated with the host ID and proxy
** path and the lock is downgraded to a shared lock again. If the conch
@@ -30205,7 +35844,7 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will
** force proxy locking to be used for every database file opened, and 0
** will force automatic proxy locking to be disabled for all database
-** files (explicity calling the SQLITE_SET_LOCKPROXYFILE pragma or
+** files (explicitly calling the SQLITE_FCNTL_SET_LOCKPROXYFILE pragma or
** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING).
*/
@@ -30226,6 +35865,7 @@ struct proxyLockingContext {
char *lockProxyPath; /* Name of the proxy lock file */
char *dbPath; /* Name of the open file */
int conchHeld; /* 1 if the conch is held, -1 if lockless */
+ int nFails; /* Number of conch taking failures */
void *oldLockingContext; /* Original lockingcontext to restore on close */
sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */
};
@@ -30247,7 +35887,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
{
if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){
OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n",
- lPath, errno, getpid()));
+ lPath, errno, osGetpid(0)));
return SQLITE_IOERR_LOCK;
}
len = strlcat(lPath, "sqliteplocks", maxLen);
@@ -30269,7 +35909,7 @@ static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){
}
lPath[i+len]='\0';
strlcat(lPath, ":auto:", maxLen);
- OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, getpid()));
+ OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, osGetpid(0)));
return SQLITE_OK;
}
@@ -30291,12 +35931,12 @@ static int proxyCreateLockPath(const char *lockPath){
if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/')
|| (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){
buf[i]='\0';
- if( mkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
+ if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){
int err=errno;
if( err!=EEXIST ) {
OSTRACE(("CREATELOCKPATH FAILED creating %s, "
"'%s' proxy lock path=%s pid=%d\n",
- buf, strerror(err), lockPath, getpid()));
+ buf, strerror(err), lockPath, osGetpid(0)));
return err;
}
}
@@ -30305,7 +35945,7 @@ static int proxyCreateLockPath(const char *lockPath){
}
buf[i] = lockPath[i];
}
- OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, getpid()));
+ OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n",lockPath,osGetpid(0)));
return 0;
}
@@ -30339,23 +35979,23 @@ static int proxyCreateUnixFile(
if( pUnused ){
fd = pUnused->fd;
}else{
- pUnused = sqlite3_malloc(sizeof(*pUnused));
+ pUnused = sqlite3_malloc64(sizeof(*pUnused));
if( !pUnused ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
}
if( fd<0 ){
- fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(path, openFlags, 0);
terrno = errno;
if( fd<0 && errno==ENOENT && islockfile ){
if( proxyCreateLockPath(path) == SQLITE_OK ){
- fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(path, openFlags, 0);
}
}
}
if( fd<0 ){
openFlags = O_RDONLY;
- fd = robust_open(path, openFlags, SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(path, openFlags, 0);
terrno = errno;
}
if( fd<0 ){
@@ -30372,9 +36012,9 @@ static int proxyCreateUnixFile(
}
}
- pNew = (unixFile *)sqlite3_malloc(sizeof(*pNew));
+ pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew));
if( pNew==NULL ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto end_create_proxy;
}
memset(pNew, 0, sizeof(unixFile));
@@ -30386,7 +36026,7 @@ static int proxyCreateUnixFile(
pUnused->flags = openFlags;
pNew->pUnused = pUnused;
- rc = fillInUnixFile(&dummyVfs, fd, 0, (sqlite3_file*)pNew, path, 0, 0, 0);
+ rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0);
if( rc==SQLITE_OK ){
*ppFile = pNew;
return SQLITE_OK;
@@ -30405,8 +36045,10 @@ SQLITE_API int sqlite3_hostid_num = 0;
#define PROXY_HOSTIDLEN 16 /* conch file host id length */
+#ifdef HAVE_GETHOSTUUID
/* Not always defined in the headers as it ought to be */
extern int gethostuuid(uuid_t id, const struct timespec *wait);
+#endif
/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN
** bytes of writable memory.
@@ -30414,10 +36056,9 @@ extern int gethostuuid(uuid_t id, const struct timespec *wait);
static int proxyGetHostID(unsigned char *pHostID, int *pError){
assert(PROXY_HOSTIDLEN == sizeof(uuid_t));
memset(pHostID, 0, PROXY_HOSTIDLEN);
-#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\
- && __MAC_OS_X_VERSION_MIN_REQUIRED<1050
+#ifdef HAVE_GETHOSTUUID
{
- static const struct timespec timeout = {1, 0}; /* 1 sec timeout */
+ struct timespec timeout = {1, 0}; /* 1 sec timeout */
if( gethostuuid(pHostID, &timeout) ){
int err = errno;
if( pError ){
@@ -30479,8 +36120,7 @@ static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){
goto end_breaklock;
}
/* write it out to the temporary break file */
- fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL),
- SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0);
if( fd<0 ){
sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno);
goto end_breaklock;
@@ -30533,7 +36173,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
*/
struct stat buf;
if( osFstat(conchFile->h, &buf) ){
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
return SQLITE_IOERR_LOCK;
}
@@ -30553,7 +36193,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
char tBuf[PROXY_MAXCONCHLEN];
int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0);
if( len<0 ){
- pFile->lastErrno = errno;
+ storeLastErrno(pFile, errno);
return SQLITE_IOERR_LOCK;
}
if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){
@@ -30573,7 +36213,7 @@ static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){
if( 0==proxyBreakConchLock(pFile, myHostID) ){
rc = SQLITE_OK;
if( lockType==EXCLUSIVE_LOCK ){
- rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK);
+ rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK);
}
if( !rc ){
rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType);
@@ -30611,11 +36251,12 @@ static int proxyTakeConch(unixFile *pFile){
int forceNewLockPath = 0;
OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h,
- (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), getpid()));
+ (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
+ osGetpid(0)));
rc = proxyGetHostID(myHostID, &pError);
if( (rc&0xff)==SQLITE_IOERR ){
- pFile->lastErrno = pError;
+ storeLastErrno(pFile, pError);
goto end_takeconch;
}
rc = proxyConchLock(pFile, myHostID, SHARED_LOCK);
@@ -30626,7 +36267,7 @@ static int proxyTakeConch(unixFile *pFile){
readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN);
if( readLen<0 ){
/* I/O error: lastErrno set by seekAndRead */
- pFile->lastErrno = conchFile->lastErrno;
+ storeLastErrno(pFile, conchFile->lastErrno);
rc = SQLITE_IOERR_READ;
goto end_takeconch;
}else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) ||
@@ -30699,7 +36340,7 @@ static int proxyTakeConch(unixFile *pFile){
rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK);
}
}else{
- rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, EXCLUSIVE_LOCK);
+ rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK);
}
if( rc==SQLITE_OK ){
char writeBuffer[PROXY_MAXCONCHLEN];
@@ -30708,14 +36349,15 @@ static int proxyTakeConch(unixFile *pFile){
writeBuffer[0] = (char)PROXY_CONCHVERSION;
memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN);
if( pCtx->lockProxyPath!=NULL ){
- strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, MAXPATHLEN);
+ strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath,
+ MAXPATHLEN);
}else{
strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN);
}
writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]);
robust_ftruncate(conchFile->h, writeSize);
rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0);
- fsync(conchFile->h);
+ full_fsync(conchFile->h,0,0);
/* If we created a new conch file (not just updated the contents of a
** valid conch file), try to match the permissions of the database
*/
@@ -30757,8 +36399,7 @@ static int proxyTakeConch(unixFile *pFile){
robust_close(pFile, pFile->h, __LINE__);
}
pFile->h = -1;
- fd = robust_open(pCtx->dbPath, pFile->openFlags,
- SQLITE_DEFAULT_FILE_PERMISSIONS);
+ fd = robust_open(pCtx->dbPath, pFile->openFlags, 0);
OSTRACE(("TRANSPROXY: OPEN %d\n", fd));
if( fd>=0 ){
pFile->h = fd;
@@ -30786,7 +36427,7 @@ static int proxyTakeConch(unixFile *pFile){
if( tempLockPath ){
pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath);
if( !pCtx->lockProxyPath ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
}
@@ -30821,7 +36462,7 @@ static int proxyReleaseConch(unixFile *pFile){
conchFile = pCtx->conchFile;
OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h,
(pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"),
- getpid()));
+ osGetpid(0)));
if( pCtx->conchHeld>0 ){
rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK);
}
@@ -30833,7 +36474,7 @@ static int proxyReleaseConch(unixFile *pFile){
/*
** Given the name of a database file, compute the name of its conch file.
-** Store the conch filename in memory obtained from sqlite3_malloc().
+** Store the conch filename in memory obtained from sqlite3_malloc64().
** Make *pConchPath point to the new name. Return SQLITE_OK on success
** or SQLITE_NOMEM if unable to obtain memory.
**
@@ -30849,9 +36490,9 @@ static int proxyCreateConchPathname(char *dbPath, char **pConchPath){
/* Allocate space for the conch filename and initialize the name to
** the name of the original database file. */
- *pConchPath = conchPath = (char *)sqlite3_malloc(len + 8);
+ *pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8);
if( conchPath==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memcpy(conchPath, dbPath, len+1);
@@ -30921,7 +36562,8 @@ static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){
/* afp style keeps a reference to the db path in the filePath field
** of the struct */
assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN );
- strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, MAXPATHLEN);
+ strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath,
+ MAXPATHLEN);
} else
#endif
if( pFile->pMethod == &dotlockIoMethods ){
@@ -30962,11 +36604,11 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
}
OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h,
- (lockPath ? lockPath : ":auto:"), getpid()));
+ (lockPath ? lockPath : ":auto:"), osGetpid(0)));
- pCtx = sqlite3_malloc( sizeof(*pCtx) );
+ pCtx = sqlite3_malloc64( sizeof(*pCtx) );
if( pCtx==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(pCtx, 0, sizeof(*pCtx));
@@ -31002,7 +36644,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
if( rc==SQLITE_OK ){
pCtx->dbPath = sqlite3DbStrDup(0, dbPath);
if( pCtx->dbPath==NULL ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
if( rc==SQLITE_OK ){
@@ -31034,7 +36676,7 @@ static int proxyTransformUnixFile(unixFile *pFile, const char *path) {
*/
static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
switch( op ){
- case SQLITE_GET_LOCKPROXYFILE: {
+ case SQLITE_FCNTL_GET_LOCKPROXYFILE: {
unixFile *pFile = (unixFile*)id;
if( pFile->pMethod == &proxyIoMethods ){
proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext;
@@ -31049,13 +36691,16 @@ static int proxyFileControl(sqlite3_file *id, int op, void *pArg){
}
return SQLITE_OK;
}
- case SQLITE_SET_LOCKPROXYFILE: {
+ case SQLITE_FCNTL_SET_LOCKPROXYFILE: {
unixFile *pFile = (unixFile*)id;
int rc = SQLITE_OK;
int isProxyStyle = (pFile->pMethod == &proxyIoMethods);
if( pArg==NULL || (const char *)pArg==0 ){
if( isProxyStyle ){
- /* turn off proxy locking - not supported */
+ /* turn off proxy locking - not supported. If support is added for
+ ** switching proxy locking mode off then it will need to fail if
+ ** the journal mode is WAL mode.
+ */
rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/;
}else{
/* turn off proxy locking - already off - NOOP */
@@ -31185,7 +36830,7 @@ static int proxyUnlock(sqlite3_file *id, int eFileLock) {
** Close a file that uses proxy locks.
*/
static int proxyClose(sqlite3_file *id) {
- if( id ){
+ if( ALWAYS(id) ){
unixFile *pFile = (unixFile*)id;
proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext;
unixFile *lockProxy = pCtx->lockProxy;
@@ -31300,8 +36945,10 @@ SQLITE_API int sqlite3_os_init(void){
** array cannot be const.
*/
static sqlite3_vfs aVfs[] = {
-#if SQLITE_ENABLE_LOCKING_STYLE && (OS_VXWORKS || defined(__APPLE__))
+#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
UNIXVFS("unix", autolockIoFinder ),
+#elif OS_VXWORKS
+ UNIXVFS("unix", vxworksIoFinder ),
#else
UNIXVFS("unix", posixIoFinder ),
#endif
@@ -31311,11 +36958,11 @@ SQLITE_API int sqlite3_os_init(void){
#if OS_VXWORKS
UNIXVFS("unix-namedsem", semIoFinder ),
#endif
-#if SQLITE_ENABLE_LOCKING_STYLE
+#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS
UNIXVFS("unix-posix", posixIoFinder ),
-#if !OS_VXWORKS
- UNIXVFS("unix-flock", flockIoFinder ),
#endif
+#if SQLITE_ENABLE_LOCKING_STYLE
+ UNIXVFS("unix-flock", flockIoFinder ),
#endif
#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__)
UNIXVFS("unix-afp", afpIoFinder ),
@@ -31327,7 +36974,7 @@ SQLITE_API int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==18 );
+ assert( ArraySize(aSyscall)==28 );
/* Register all VFSes defined in the aVfs[] array */
for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){
@@ -31363,49 +37010,10 @@ SQLITE_API int sqlite3_os_end(void){
**
******************************************************************************
**
-** This file contains code that is specific to windows.
+** This file contains code that is specific to Windows.
*/
-#if SQLITE_OS_WIN /* This file is used for windows only */
-
-
-/*
-** A Note About Memory Allocation:
-**
-** This driver uses malloc()/free() directly rather than going through
-** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
-** are designed for use on embedded systems where memory is scarce and
-** malloc failures happen frequently. Win32 does not typically run on
-** embedded systems, and when it does the developers normally have bigger
-** problems to worry about than running out of memory. So there is not
-** a compelling need to use the wrappers.
-**
-** But there is a good reason to not use the wrappers. If we use the
-** wrappers then we will get simulated malloc() failures within this
-** driver. And that causes all kinds of problems for our tests. We
-** could enhance SQLite to deal with simulated malloc failures within
-** the OS driver, but the code to deal with those failure would not
-** be exercised on Linux (which does not need to malloc() in the driver)
-** and so we would have difficulty writing coverage tests for that
-** code. Better to leave the code out, we think.
-**
-** The point of this discussion is as follows: When creating a new
-** OS layer for an embedded system, if you use this file as an example,
-** avoid the use of malloc()/free(). Those routines work ok on windows
-** desktops but not so well in embedded systems.
-*/
-
-#include <winbase.h>
-
-#ifdef __CYGWIN__
-# include <sys/cygwin.h>
-#endif
-
-/*
-** Macros used to determine whether or not to use threads.
-*/
-#if defined(THREADSAFE) && THREADSAFE
-# define SQLITE_W32_THREADS 1
-#endif
+/* #include "sqliteInt.h" */
+#if SQLITE_OS_WIN /* This file is used for Windows only */
/*
** Include code that is common to all os_*.c files
@@ -31443,24 +37051,14 @@ SQLITE_API int sqlite3_os_end(void){
# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead."
#endif
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
-# ifndef SQLITE_DEBUG_OS_TRACE
-# define SQLITE_DEBUG_OS_TRACE 0
-# endif
- int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
-# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
-#else
-# define OSTRACE(X)
-#endif
-
/*
** Macros for performance tracing. Normally turned off. Only works
** on i486 hardware.
*/
#ifdef SQLITE_PERFORMANCE_TRACE
-/*
-** hwtime.h contains inline assembler code for implementing
+/*
+** hwtime.h contains inline assembler code for implementing
** high-performance timing routines.
*/
/************** Include hwtime.h in the middle of os_common.h ****************/
@@ -31480,8 +37078,8 @@ SQLITE_API int sqlite3_os_end(void){
** This file contains inline asm code for retrieving "high-performance"
** counters for x86 class CPUs.
*/
-#ifndef _HWTIME_H_
-#define _HWTIME_H_
+#ifndef SQLITE_HWTIME_H
+#define SQLITE_HWTIME_H
/*
** The following routine only works on pentium-class (or newer) processors.
@@ -31549,7 +37147,7 @@ SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
#endif
-#endif /* !defined(_HWTIME_H_) */
+#endif /* !defined(SQLITE_HWTIME_H) */
/************** End of hwtime.h **********************************************/
/************** Continuing where we left off in os_common.h ******************/
@@ -31570,14 +37168,14 @@ static sqlite_uint64 g_elapsed;
** of code will give us the ability to simulate a disk I/O error. This
** is used for testing the I/O recovery logic.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */
-SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */
-SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */
-SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */
-SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */
-SQLITE_API int sqlite3_diskfull_pending = 0;
-SQLITE_API int sqlite3_diskfull = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_io_error_hit;
+SQLITE_API extern int sqlite3_io_error_hardhit;
+SQLITE_API extern int sqlite3_io_error_pending;
+SQLITE_API extern int sqlite3_io_error_persist;
+SQLITE_API extern int sqlite3_io_error_benign;
+SQLITE_API extern int sqlite3_diskfull_pending;
+SQLITE_API extern int sqlite3_diskfull;
#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X)
#define SimulateIOError(CODE) \
if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \
@@ -31603,17 +37201,17 @@ static void local_ioerr(){
#define SimulateIOErrorBenign(X)
#define SimulateIOError(A)
#define SimulateDiskfullError(A)
-#endif
+#endif /* defined(SQLITE_TEST) */
/*
** When testing, keep a count of the number of open files.
*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_open_file_count = 0;
+#if defined(SQLITE_TEST)
+SQLITE_API extern int sqlite3_open_file_count;
#define OpenCounter(X) sqlite3_open_file_count+=(X)
#else
#define OpenCounter(X)
-#endif
+#endif /* defined(SQLITE_TEST) */
#endif /* !defined(_OS_COMMON_H_) */
@@ -31621,24 +37219,226 @@ SQLITE_API int sqlite3_open_file_count = 0;
/************** Continuing where we left off in os_win.c *********************/
/*
-** Some microsoft compilers lack this definition.
+** Include the header file for the Windows VFS.
*/
-#ifndef INVALID_FILE_ATTRIBUTES
-# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+/* #include "os_win.h" */
+
+/*
+** Compiling and using WAL mode requires several APIs that are only
+** available in Windows platforms based on the NT kernel.
+*/
+#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL)
+# error "WAL mode requires support from the Windows NT kernel, compile\
+ with SQLITE_OMIT_WAL."
+#endif
+
+#if !SQLITE_OS_WINNT && SQLITE_MAX_MMAP_SIZE>0
+# error "Memory mapped files require support from the Windows NT kernel,\
+ compile with SQLITE_MAX_MMAP_SIZE=0."
#endif
/*
-** Determine if we are dealing with WindowsCE - which has a much
-** reduced API.
+** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions
+** based on the sub-platform)?
*/
-#if SQLITE_OS_WINCE
-# define AreFileApisANSI() 1
-# define FormatMessageW(a,b,c,d,e,f,g) 0
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI)
+# define SQLITE_WIN32_HAS_ANSI
#endif
-/* Forward references */
+/*
+** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions
+** based on the sub-platform)?
+*/
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \
+ !defined(SQLITE_WIN32_NO_WIDE)
+# define SQLITE_WIN32_HAS_WIDE
+#endif
+
+/*
+** Make sure at least one set of Win32 APIs is available.
+*/
+#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE)
+# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\
+ must be defined."
+#endif
+
+/*
+** Define the required Windows SDK version constants if they are not
+** already available.
+*/
+#ifndef NTDDI_WIN8
+# define NTDDI_WIN8 0x06020000
+#endif
+
+#ifndef NTDDI_WINBLUE
+# define NTDDI_WINBLUE 0x06030000
+#endif
+
+#ifndef NTDDI_WINTHRESHOLD
+# define NTDDI_WINTHRESHOLD 0x06040000
+#endif
+
+/*
+** Check to see if the GetVersionEx[AW] functions are deprecated on the
+** target system. GetVersionEx was first deprecated in Win8.1.
+*/
+#ifndef SQLITE_WIN32_GETVERSIONEX
+# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE
+# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */
+# else
+# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */
+# endif
+#endif
+
+/*
+** Check to see if the CreateFileMappingA function is supported on the
+** target system. It is unavailable when using "mincore.lib" on Win10.
+** When compiling for Windows 10, always assume "mincore.lib" is in use.
+*/
+#ifndef SQLITE_WIN32_CREATEFILEMAPPINGA
+# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINTHRESHOLD
+# define SQLITE_WIN32_CREATEFILEMAPPINGA 0
+# else
+# define SQLITE_WIN32_CREATEFILEMAPPINGA 1
+# endif
+#endif
+
+/*
+** This constant should already be defined (in the "WinDef.h" SDK file).
+*/
+#ifndef MAX_PATH
+# define MAX_PATH (260)
+#endif
+
+/*
+** Maximum pathname length (in chars) for Win32. This should normally be
+** MAX_PATH.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_CHARS
+# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH)
+#endif
+
+/*
+** This constant should already be defined (in the "WinNT.h" SDK file).
+*/
+#ifndef UNICODE_STRING_MAX_CHARS
+# define UNICODE_STRING_MAX_CHARS (32767)
+#endif
+
+/*
+** Maximum pathname length (in chars) for WinNT. This should normally be
+** UNICODE_STRING_MAX_CHARS.
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_CHARS
+# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in
+** characters, so we allocate 4 bytes per character assuming worst-case of
+** 4-bytes-per-character for UTF8.
+*/
+#ifndef SQLITE_WIN32_MAX_PATH_BYTES
+# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4)
+#endif
+
+/*
+** Maximum pathname length (in bytes) for WinNT. This should normally be
+** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR).
+*/
+#ifndef SQLITE_WINNT_MAX_PATH_BYTES
+# define SQLITE_WINNT_MAX_PATH_BYTES \
+ (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS)
+#endif
+
+/*
+** Maximum error message length (in chars) for WinRT.
+*/
+#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS
+# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024)
+#endif
+
+/*
+** Returns non-zero if the character should be treated as a directory
+** separator.
+*/
+#ifndef winIsDirSep
+# define winIsDirSep(a) (((a) == '/') || ((a) == '\\'))
+#endif
+
+/*
+** This macro is used when a local variable is set to a value that is
+** [sometimes] not used by the code (e.g. via conditional compilation).
+*/
+#ifndef UNUSED_VARIABLE_VALUE
+# define UNUSED_VARIABLE_VALUE(x) (void)(x)
+#endif
+
+/*
+** Returns the character that should be used as the directory separator.
+*/
+#ifndef winGetDirSep
+# define winGetDirSep() '\\'
+#endif
+
+/*
+** Do we need to manually define the Win32 file mapping APIs for use with WAL
+** mode or memory mapped files (e.g. these APIs are available in the Windows
+** CE SDK; however, they are not present in the header file)?
+*/
+#if SQLITE_WIN32_FILEMAPPING_API && \
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
+/*
+** Two of the file mapping APIs are different under WinRT. Figure out which
+** set we need.
+*/
+#if SQLITE_OS_WINRT
+WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \
+ LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR);
+
+WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T);
+#else
+#if defined(SQLITE_WIN32_HAS_ANSI)
+WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \
+ DWORD, DWORD, DWORD, LPCSTR);
+#endif /* defined(SQLITE_WIN32_HAS_ANSI) */
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \
+ DWORD, DWORD, DWORD, LPCWSTR);
+#endif /* defined(SQLITE_WIN32_HAS_WIDE) */
+
+WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T);
+#endif /* SQLITE_OS_WINRT */
+
+/*
+** These file mapping APIs are common to both Win32 and WinRT.
+*/
+
+WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T);
+WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
+#endif /* SQLITE_WIN32_FILEMAPPING_API */
+
+/*
+** Some Microsoft compilers lack this definition.
+*/
+#ifndef INVALID_FILE_ATTRIBUTES
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1)
+#endif
+
+#ifndef FILE_FLAG_MASK
+# define FILE_FLAG_MASK (0xFF3C0000)
+#endif
+
+#ifndef FILE_ATTRIBUTE_MASK
+# define FILE_ATTRIBUTE_MASK (0x0003FFF7)
+#endif
+
+#ifndef SQLITE_OMIT_WAL
+/* Forward references to structures used for WAL */
typedef struct winShm winShm; /* A connection to shared-memory */
typedef struct winShmNode winShmNode; /* A region of shared-memory */
+#endif
/*
** WinCE lacks native support for file locking so we have to fake it
@@ -31664,31 +37464,112 @@ struct winFile {
HANDLE h; /* Handle for accessing the file */
u8 locktype; /* Type of lock currently held on this file */
short sharedLockByte; /* Randomly chosen byte used as a shared lock */
- u8 bPersistWal; /* True to persist WAL files */
+ u8 ctrlFlags; /* Flags. See WINFILE_* below */
DWORD lastErrno; /* The Windows errno from the last I/O error */
- DWORD sectorSize; /* Sector size of the device file is on */
+#ifndef SQLITE_OMIT_WAL
winShm *pShm; /* Instance of shared memory on this file */
+#endif
const char *zPath; /* Full pathname of this file */
int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
#if SQLITE_OS_WINCE
- WCHAR *zDeleteOnClose; /* Name of file to delete when closing */
- HANDLE hMutex; /* Mutex used to control access to shared lock */
+ LPWSTR zDeleteOnClose; /* Name of file to delete when closing */
+ HANDLE hMutex; /* Mutex used to control access to shared lock */
HANDLE hShared; /* Shared memory segment used for locking */
winceLock local; /* Locks obtained by this instance of winFile */
winceLock *shared; /* Global shared lock memory for the file */
#endif
+#if SQLITE_MAX_MMAP_SIZE>0
+ int nFetchOut; /* Number of outstanding xFetch references */
+ HANDLE hMap; /* Handle for accessing memory mapping */
+ void *pMapRegion; /* Area memory mapped */
+ sqlite3_int64 mmapSize; /* Usable size of mapped region */
+ sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */
+ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
+#endif
+};
+
+/*
+** The winVfsAppData structure is used for the pAppData member for all of the
+** Win32 VFS variants.
+*/
+typedef struct winVfsAppData winVfsAppData;
+struct winVfsAppData {
+ const sqlite3_io_methods *pMethod; /* The file I/O methods to use. */
+ void *pAppData; /* The extra pAppData, if any. */
+ BOOL bNoLock; /* Non-zero if locking is disabled. */
};
/*
+** Allowed values for winFile.ctrlFlags
+*/
+#define WINFILE_RDONLY 0x02 /* Connection is read only */
+#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */
+#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */
+
+/*
+ * The size of the buffer used by sqlite3_win32_write_debug().
+ */
+#ifndef SQLITE_WIN32_DBG_BUF_SIZE
+# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD)))
+#endif
+
+/*
+ * The value used with sqlite3_win32_set_directory() to specify that
+ * the data directory should be changed.
+ */
+#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE
+# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1)
+#endif
+
+/*
+ * The value used with sqlite3_win32_set_directory() to specify that
+ * the temporary directory should be changed.
+ */
+#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE
+# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2)
+#endif
+
+/*
* If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the
* various Win32 API heap functions instead of our own.
*/
#ifdef SQLITE_WIN32_MALLOC
+
+/*
+ * If this is non-zero, an isolated heap will be created by the native Win32
+ * allocator subsystem; otherwise, the default process heap will be used. This
+ * setting has no effect when compiling for WinRT. By default, this is enabled
+ * and an isolated heap will be created to store all allocated data.
+ *
+ ******************************************************************************
+ * WARNING: It is important to note that when this setting is non-zero and the
+ * winMemShutdown function is called (e.g. by the sqlite3_shutdown
+ * function), all data that was allocated using the isolated heap will
+ * be freed immediately and any attempt to access any of that freed
+ * data will almost certainly result in an immediate access violation.
+ ******************************************************************************
+ */
+#ifndef SQLITE_WIN32_HEAP_CREATE
+# define SQLITE_WIN32_HEAP_CREATE (TRUE)
+#endif
+
+/*
+ * This is cache size used in the calculation of the initial size of the
+ * Win32-specific heap. It cannot be negative.
+ */
+#ifndef SQLITE_WIN32_CACHE_SIZE
+# if SQLITE_DEFAULT_CACHE_SIZE>=0
+# define SQLITE_WIN32_CACHE_SIZE (SQLITE_DEFAULT_CACHE_SIZE)
+# else
+# define SQLITE_WIN32_CACHE_SIZE (-(SQLITE_DEFAULT_CACHE_SIZE))
+# endif
+#endif
+
/*
* The initial size of the Win32-specific heap. This value may be zero.
*/
#ifndef SQLITE_WIN32_HEAP_INIT_SIZE
-# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \
+# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_WIN32_CACHE_SIZE) * \
(SQLITE_DEFAULT_PAGE_SIZE) + 4194304)
#endif
@@ -31707,6 +37588,7 @@ struct winFile {
# define SQLITE_WIN32_HEAP_FLAGS (0)
#endif
+
/*
** The winMemData structure stores information required by the Win32-specific
** sqlite3_mem_methods implementation.
@@ -31714,30 +37596,41 @@ struct winFile {
typedef struct winMemData winMemData;
struct winMemData {
#ifndef NDEBUG
- u32 magic; /* Magic number to detect structure corruption. */
+ u32 magic1; /* Magic number to detect structure corruption. */
#endif
HANDLE hHeap; /* The handle to our heap. */
BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */
+#ifndef NDEBUG
+ u32 magic2; /* Magic number to detect structure corruption. */
+#endif
};
#ifndef NDEBUG
-#define WINMEM_MAGIC 0x42b2830b
+#define WINMEM_MAGIC1 0x42b2830b
+#define WINMEM_MAGIC2 0xbd4d7cf4
#endif
static struct winMemData win_mem_data = {
#ifndef NDEBUG
- WINMEM_MAGIC,
+ WINMEM_MAGIC1,
#endif
NULL, FALSE
+#ifndef NDEBUG
+ ,WINMEM_MAGIC2
+#endif
};
#ifndef NDEBUG
-#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC )
+#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 )
+#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 )
+#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2();
#else
#define winMemAssertMagic()
#endif
-#define winMemGetHeap() win_mem_data.hHeap
+#define winMemGetDataPtr() &win_mem_data
+#define winMemGetHeap() win_mem_data.hHeap
+#define winMemGetOwned() win_mem_data.bOwned
static void *winMemMalloc(int nBytes);
static void winMemFree(void *pPrior);
@@ -31751,29 +37644,939 @@ SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void);
#endif /* SQLITE_WIN32_MALLOC */
/*
-** Forward prototypes.
-*/
-static int getSectorSize(
- sqlite3_vfs *pVfs,
- const char *zRelative /* UTF-8 file name */
-);
-
-/*
** The following variable is (normally) set once and never changes
-** thereafter. It records whether the operating system is Win95
+** thereafter. It records whether the operating system is Win9x
** or WinNT.
**
** 0: Operating system unknown.
-** 1: Operating system is Win95.
+** 1: Operating system is Win9x.
** 2: Operating system is WinNT.
**
** In order to facilitate testing on a WinNT system, the test fixture
** can manually set this value to 1 to emulate Win98 behavior.
*/
#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_os_type = 0;
+SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
#else
-static int sqlite3_os_type = 0;
+static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0;
+#endif
+
+#ifndef SYSCALL
+# define SYSCALL sqlite3_syscall_ptr
+#endif
+
+/*
+** This function is not available on Windows CE or WinRT.
+ */
+
+#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
+# define osAreFileApisANSI() 1
+#endif
+
+/*
+** Many system calls are accessed through pointer-to-functions so that
+** they may be overridden at runtime to facilitate fault injection during
+** testing and sandboxing. The following array holds the names and pointers
+** to all overrideable system calls.
+*/
+static struct win_syscall {
+ const char *zName; /* Name of the system call */
+ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */
+ sqlite3_syscall_ptr pDefault; /* Default value */
+} aSyscall[] = {
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 },
+#else
+ { "AreFileApisANSI", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osAreFileApisANSI
+#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent)
+#endif
+
+#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CharLowerW", (SYSCALL)CharLowerW, 0 },
+#else
+ { "CharLowerW", (SYSCALL)0, 0 },
+#endif
+
+#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent)
+
+#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CharUpperW", (SYSCALL)CharUpperW, 0 },
+#else
+ { "CharUpperW", (SYSCALL)0, 0 },
+#endif
+
+#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent)
+
+ { "CloseHandle", (SYSCALL)CloseHandle, 0 },
+
+#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "CreateFileA", (SYSCALL)CreateFileA, 0 },
+#else
+ { "CreateFileA", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \
+ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CreateFileW", (SYSCALL)CreateFileW, 0 },
+#else
+ { "CreateFileW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \
+ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) && \
+ SQLITE_WIN32_CREATEFILEMAPPINGA
+ { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 },
+#else
+ { "CreateFileMappingA", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
+ DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent)
+
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
+ { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 },
+#else
+ { "CreateFileMappingW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \
+ DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "CreateMutexW", (SYSCALL)CreateMutexW, 0 },
+#else
+ { "CreateMutexW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \
+ LPCWSTR))aSyscall[8].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "DeleteFileA", (SYSCALL)DeleteFileA, 0 },
+#else
+ { "DeleteFileA", (SYSCALL)0, 0 },
+#endif
+
+#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "DeleteFileW", (SYSCALL)DeleteFileW, 0 },
+#else
+ { "DeleteFileW", (SYSCALL)0, 0 },
+#endif
+
+#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent)
+
+#if SQLITE_OS_WINCE
+ { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 },
+#else
+ { "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
+#endif
+
+#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
+ LPFILETIME))aSyscall[11].pCurrent)
+
+#if SQLITE_OS_WINCE
+ { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 },
+#else
+ { "FileTimeToSystemTime", (SYSCALL)0, 0 },
+#endif
+
+#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
+ LPSYSTEMTIME))aSyscall[12].pCurrent)
+
+ { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
+
+#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "FormatMessageA", (SYSCALL)FormatMessageA, 0 },
+#else
+ { "FormatMessageA", (SYSCALL)0, 0 },
+#endif
+
+#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \
+ DWORD,va_list*))aSyscall[14].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "FormatMessageW", (SYSCALL)FormatMessageW, 0 },
+#else
+ { "FormatMessageW", (SYSCALL)0, 0 },
+#endif
+
+#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \
+ DWORD,va_list*))aSyscall[15].pCurrent)
+
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "FreeLibrary", (SYSCALL)FreeLibrary, 0 },
+#else
+ { "FreeLibrary", (SYSCALL)0, 0 },
+#endif
+
+#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent)
+
+ { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 },
+
+#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent)
+
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 },
+#else
+ { "GetDiskFreeSpaceA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \
+ LPDWORD))aSyscall[18].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 },
+#else
+ { "GetDiskFreeSpaceW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \
+ LPDWORD))aSyscall[19].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 },
+#else
+ { "GetFileAttributesA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 },
+#else
+ { "GetFileAttributesW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 },
+#else
+ { "GetFileAttributesExW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \
+ LPVOID))aSyscall[22].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "GetFileSize", (SYSCALL)GetFileSize, 0 },
+#else
+ { "GetFileSize", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent)
+
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 },
+#else
+ { "GetFullPathNameA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \
+ LPSTR*))aSyscall[24].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 },
+#else
+ { "GetFullPathNameW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
+ LPWSTR*))aSyscall[25].pCurrent)
+
+ { "GetLastError", (SYSCALL)GetLastError, 0 },
+
+#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
+
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
+#if SQLITE_OS_WINCE
+ /* The GetProcAddressA() routine is only available on Windows CE. */
+ { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 },
+#else
+ /* All other Windows platforms expect GetProcAddress() to take
+ ** an ANSI string regardless of the _UNICODE setting */
+ { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 },
+#endif
+#else
+ { "GetProcAddressA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \
+ LPCSTR))aSyscall[27].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 },
+#else
+ { "GetSystemInfo", (SYSCALL)0, 0 },
+#endif
+
+#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent)
+
+ { "GetSystemTime", (SYSCALL)GetSystemTime, 0 },
+
+#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent)
+
+#if !SQLITE_OS_WINCE
+ { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 },
+#else
+ { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 },
+#endif
+
+#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \
+ LPFILETIME))aSyscall[30].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "GetTempPathA", (SYSCALL)GetTempPathA, 0 },
+#else
+ { "GetTempPathA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE)
+ { "GetTempPathW", (SYSCALL)GetTempPathW, 0 },
+#else
+ { "GetTempPathW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "GetTickCount", (SYSCALL)GetTickCount, 0 },
+#else
+ { "GetTickCount", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_GETVERSIONEX
+ { "GetVersionExA", (SYSCALL)GetVersionExA, 0 },
+#else
+ { "GetVersionExA", (SYSCALL)0, 0 },
+#endif
+
+#define osGetVersionExA ((BOOL(WINAPI*)( \
+ LPOSVERSIONINFOA))aSyscall[34].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ SQLITE_WIN32_GETVERSIONEX
+ { "GetVersionExW", (SYSCALL)GetVersionExW, 0 },
+#else
+ { "GetVersionExW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetVersionExW ((BOOL(WINAPI*)( \
+ LPOSVERSIONINFOW))aSyscall[35].pCurrent)
+
+ { "HeapAlloc", (SYSCALL)HeapAlloc, 0 },
+
+#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \
+ SIZE_T))aSyscall[36].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "HeapCreate", (SYSCALL)HeapCreate, 0 },
+#else
+ { "HeapCreate", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \
+ SIZE_T))aSyscall[37].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "HeapDestroy", (SYSCALL)HeapDestroy, 0 },
+#else
+ { "HeapDestroy", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent)
+
+ { "HeapFree", (SYSCALL)HeapFree, 0 },
+
+#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent)
+
+ { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 },
+
+#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \
+ SIZE_T))aSyscall[40].pCurrent)
+
+ { "HeapSize", (SYSCALL)HeapSize, 0 },
+
+#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \
+ LPCVOID))aSyscall[41].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "HeapValidate", (SYSCALL)HeapValidate, 0 },
+#else
+ { "HeapValidate", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \
+ LPCVOID))aSyscall[42].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "HeapCompact", (SYSCALL)HeapCompact, 0 },
+#else
+ { "HeapCompact", (SYSCALL)0, 0 },
+#endif
+
+#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 },
+#else
+ { "LoadLibraryA", (SYSCALL)0, 0 },
+#endif
+
+#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent)
+
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \
+ !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 },
+#else
+ { "LoadLibraryW", (SYSCALL)0, 0 },
+#endif
+
+#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "LocalFree", (SYSCALL)LocalFree, 0 },
+#else
+ { "LocalFree", (SYSCALL)0, 0 },
+#endif
+
+#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "LockFile", (SYSCALL)LockFile, 0 },
+#else
+ { "LockFile", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osLockFile
+#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ DWORD))aSyscall[47].pCurrent)
+#endif
+
+#if !SQLITE_OS_WINCE
+ { "LockFileEx", (SYSCALL)LockFileEx, 0 },
+#else
+ { "LockFileEx", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osLockFileEx
+#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \
+ LPOVERLAPPED))aSyscall[48].pCurrent)
+#endif
+
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0))
+ { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 },
+#else
+ { "MapViewOfFile", (SYSCALL)0, 0 },
+#endif
+
+#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ SIZE_T))aSyscall[49].pCurrent)
+
+ { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 },
+
+#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \
+ int))aSyscall[50].pCurrent)
+
+ { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 },
+
+#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \
+ LARGE_INTEGER*))aSyscall[51].pCurrent)
+
+ { "ReadFile", (SYSCALL)ReadFile, 0 },
+
+#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \
+ LPOVERLAPPED))aSyscall[52].pCurrent)
+
+ { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 },
+
+#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "SetFilePointer", (SYSCALL)SetFilePointer, 0 },
+#else
+ { "SetFilePointer", (SYSCALL)0, 0 },
+#endif
+
+#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
+ DWORD))aSyscall[54].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "Sleep", (SYSCALL)Sleep, 0 },
+#else
+ { "Sleep", (SYSCALL)0, 0 },
+#endif
+
+#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
+
+ { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
+
+#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
+ LPFILETIME))aSyscall[56].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ { "UnlockFile", (SYSCALL)UnlockFile, 0 },
+#else
+ { "UnlockFile", (SYSCALL)0, 0 },
+#endif
+
+#ifndef osUnlockFile
+#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ DWORD))aSyscall[57].pCurrent)
+#endif
+
+#if !SQLITE_OS_WINCE
+ { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 },
+#else
+ { "UnlockFileEx", (SYSCALL)0, 0 },
+#endif
+
+#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
+ LPOVERLAPPED))aSyscall[58].pCurrent)
+
+#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0
+ { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
+#else
+ { "UnmapViewOfFile", (SYSCALL)0, 0 },
+#endif
+
+#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent)
+
+ { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 },
+
+#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \
+ LPCSTR,LPBOOL))aSyscall[60].pCurrent)
+
+ { "WriteFile", (SYSCALL)WriteFile, 0 },
+
+#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
+ LPOVERLAPPED))aSyscall[61].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "CreateEventExW", (SYSCALL)CreateEventExW, 0 },
+#else
+ { "CreateEventExW", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
+ DWORD,DWORD))aSyscall[62].pCurrent)
+
+#if !SQLITE_OS_WINRT
+ { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
+#else
+ { "WaitForSingleObject", (SYSCALL)0, 0 },
+#endif
+
+#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
+ DWORD))aSyscall[63].pCurrent)
+
+#if !SQLITE_OS_WINCE
+ { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
+#else
+ { "WaitForSingleObjectEx", (SYSCALL)0, 0 },
+#endif
+
+#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
+ BOOL))aSyscall[64].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 },
+#else
+ { "SetFilePointerEx", (SYSCALL)0, 0 },
+#endif
+
+#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \
+ PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 },
+#else
+ { "GetFileInformationByHandleEx", (SYSCALL)0, 0 },
+#endif
+
+#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \
+ FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent)
+
+#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
+ { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
+#else
+ { "MapViewOfFileFromApp", (SYSCALL)0, 0 },
+#endif
+
+#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
+ SIZE_T))aSyscall[67].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "CreateFile2", (SYSCALL)CreateFile2, 0 },
+#else
+ { "CreateFile2", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \
+ LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent)
+
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 },
+#else
+ { "LoadPackagedLibrary", (SYSCALL)0, 0 },
+#endif
+
+#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \
+ DWORD))aSyscall[69].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetTickCount64", (SYSCALL)GetTickCount64, 0 },
+#else
+ { "GetTickCount64", (SYSCALL)0, 0 },
+#endif
+
+#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent)
+
+#if SQLITE_OS_WINRT
+ { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 },
+#else
+ { "GetNativeSystemInfo", (SYSCALL)0, 0 },
+#endif
+
+#define osGetNativeSystemInfo ((VOID(WINAPI*)( \
+ LPSYSTEM_INFO))aSyscall[71].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 },
+#else
+ { "OutputDebugStringA", (SYSCALL)0, 0 },
+#endif
+
+#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 },
+#else
+ { "OutputDebugStringW", (SYSCALL)0, 0 },
+#endif
+
+#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent)
+
+ { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 },
+
+#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent)
+
+#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)
+ { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
+#else
+ { "CreateFileMappingFromApp", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
+ LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent)
+
+/*
+** NOTE: On some sub-platforms, the InterlockedCompareExchange "function"
+** is really just a macro that uses a compiler intrinsic (e.g. x64).
+** So do not try to make this is into a redefinable interface.
+*/
+#if defined(InterlockedCompareExchange)
+ { "InterlockedCompareExchange", (SYSCALL)0, 0 },
+
+#define osInterlockedCompareExchange InterlockedCompareExchange
+#else
+ { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 },
+
+#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \
+ SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent)
+#endif /* defined(InterlockedCompareExchange) */
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
+ { "UuidCreate", (SYSCALL)UuidCreate, 0 },
+#else
+ { "UuidCreate", (SYSCALL)0, 0 },
+#endif
+
+#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent)
+
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
+ { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 },
+#else
+ { "UuidCreateSequential", (SYSCALL)0, 0 },
+#endif
+
+#define osUuidCreateSequential \
+ ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent)
+
+#if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0
+ { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 },
+#else
+ { "FlushViewOfFile", (SYSCALL)0, 0 },
+#endif
+
+#define osFlushViewOfFile \
+ ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)
+
+}; /* End of the overrideable system calls */
+
+/*
+** This is the xSetSystemCall() method of sqlite3_vfs for all of the
+** "win32" VFSes. Return SQLITE_OK opon successfully updating the
+** system call pointer, or SQLITE_NOTFOUND if there is no configurable
+** system call named zName.
+*/
+static int winSetSystemCall(
+ sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */
+ const char *zName, /* Name of system call to override */
+ sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */
+){
+ unsigned int i;
+ int rc = SQLITE_NOTFOUND;
+
+ UNUSED_PARAMETER(pNotUsed);
+ if( zName==0 ){
+ /* If no zName is given, restore all system calls to their default
+ ** settings and return NULL
+ */
+ rc = SQLITE_OK;
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
+ if( aSyscall[i].pDefault ){
+ aSyscall[i].pCurrent = aSyscall[i].pDefault;
+ }
+ }
+ }else{
+ /* If zName is specified, operate on only the one system call
+ ** specified.
+ */
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ){
+ if( aSyscall[i].pDefault==0 ){
+ aSyscall[i].pDefault = aSyscall[i].pCurrent;
+ }
+ rc = SQLITE_OK;
+ if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault;
+ aSyscall[i].pCurrent = pNewFunc;
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** Return the value of a system call. Return NULL if zName is not a
+** recognized system call name. NULL is also returned if the system call
+** is currently undefined.
+*/
+static sqlite3_syscall_ptr winGetSystemCall(
+ sqlite3_vfs *pNotUsed,
+ const char *zName
+){
+ unsigned int i;
+
+ UNUSED_PARAMETER(pNotUsed);
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent;
+ }
+ return 0;
+}
+
+/*
+** Return the name of the first system call after zName. If zName==NULL
+** then return the name of the first system call. Return NULL if zName
+** is the last system call or if zName is not the name of a valid
+** system call.
+*/
+static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){
+ int i = -1;
+
+ UNUSED_PARAMETER(p);
+ if( zName ){
+ for(i=0; i<ArraySize(aSyscall)-1; i++){
+ if( strcmp(zName, aSyscall[i].zName)==0 ) break;
+ }
+ }
+ for(i++; i<ArraySize(aSyscall); i++){
+ if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
+ }
+ return 0;
+}
+
+#ifdef SQLITE_WIN32_MALLOC
+/*
+** If a Win32 native heap has been configured, this function will attempt to
+** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one
+** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The
+** "pnLargest" argument, if non-zero, will be used to return the size of the
+** largest committed free block in the heap, in bytes.
+*/
+SQLITE_API int sqlite3_win32_compact_heap(LPUINT pnLargest){
+ int rc = SQLITE_OK;
+ UINT nLargest = 0;
+ HANDLE hHeap;
+
+ winMemAssertMagic();
+ hHeap = winMemGetHeap();
+ assert( hHeap!=0 );
+ assert( hHeap!=INVALID_HANDLE_VALUE );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#endif
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){
+ DWORD lastErrno = osGetLastError();
+ if( lastErrno==NO_ERROR ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p",
+ (void*)hHeap);
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p",
+ osGetLastError(), (void*)hHeap);
+ rc = SQLITE_ERROR;
+ }
+ }
+#else
+ sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p",
+ (void*)hHeap);
+ rc = SQLITE_NOTFOUND;
+#endif
+ if( pnLargest ) *pnLargest = nLargest;
+ return rc;
+}
+
+/*
+** If a Win32 native heap has been configured, this function will attempt to
+** destroy and recreate it. If the Win32 native heap is not isolated and/or
+** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will
+** be returned and no changes will be made to the Win32 native heap.
+*/
+SQLITE_API int sqlite3_win32_reset_heap(){
+ int rc;
+ MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
+ MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */
+ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
+ MUTEX_LOGIC( pMem = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); )
+ sqlite3_mutex_enter(pMaster);
+ sqlite3_mutex_enter(pMem);
+ winMemAssertMagic();
+ if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){
+ /*
+ ** At this point, there should be no outstanding memory allocations on
+ ** the heap. Also, since both the master and memsys locks are currently
+ ** being held by us, no other function (i.e. from another thread) should
+ ** be able to even access the heap. Attempt to destroy and recreate our
+ ** isolated Win32 native heap now.
+ */
+ assert( winMemGetHeap()!=NULL );
+ assert( winMemGetOwned() );
+ assert( sqlite3_memory_used()==0 );
+ winMemShutdown(winMemGetDataPtr());
+ assert( winMemGetHeap()==NULL );
+ assert( !winMemGetOwned() );
+ assert( sqlite3_memory_used()==0 );
+ rc = winMemInit(winMemGetDataPtr());
+ assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL );
+ assert( rc!=SQLITE_OK || winMemGetOwned() );
+ assert( rc!=SQLITE_OK || sqlite3_memory_used()==0 );
+ }else{
+ /*
+ ** The Win32 native heap cannot be modified because it may be in use.
+ */
+ rc = SQLITE_BUSY;
+ }
+ sqlite3_mutex_leave(pMem);
+ sqlite3_mutex_leave(pMaster);
+ return rc;
+}
+#endif /* SQLITE_WIN32_MALLOC */
+
+/*
+** This function outputs the specified (ANSI) string to the Win32 debugger
+** (if available).
+*/
+
+SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){
+ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE];
+ int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */
+ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */
+ assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zBuf ){
+ (void)SQLITE_MISUSE_BKPT;
+ return;
+ }
+#endif
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ if( nMin>0 ){
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ memcpy(zDbgBuf, zBuf, nMin);
+ osOutputDebugStringA(zDbgBuf);
+ }else{
+ osOutputDebugStringA(zBuf);
+ }
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ if ( osMultiByteToWideChar(
+ osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf,
+ nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){
+ return;
+ }
+ osOutputDebugStringW((LPCWSTR)zDbgBuf);
+#else
+ if( nMin>0 ){
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE);
+ memcpy(zDbgBuf, zBuf, nMin);
+ fprintf(stderr, "%s", zDbgBuf);
+ }else{
+ fprintf(stderr, "%s", zBuf);
+ }
+#endif
+}
+
+/*
+** The following routine suspends the current thread for at least ms
+** milliseconds. This is equivalent to the Win32 Sleep() interface.
+*/
+#if SQLITE_OS_WINRT
+static HANDLE sleepObj = NULL;
+#endif
+
+SQLITE_API void sqlite3_win32_sleep(DWORD milliseconds){
+#if SQLITE_OS_WINRT
+ if ( sleepObj==NULL ){
+ sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET,
+ SYNCHRONIZE);
+ }
+ assert( sleepObj!=NULL );
+ osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE);
+#else
+ osSleep(milliseconds);
+#endif
+}
+
+#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
+ SQLITE_THREADSAFE>0
+SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){
+ DWORD rc;
+ while( (rc = osWaitForSingleObjectEx(hObject, INFINITE,
+ TRUE))==WAIT_IO_COMPLETION ){}
+ return rc;
+}
#endif
/*
@@ -31787,19 +38590,55 @@ static int sqlite3_os_type = 0;
** WinNT/2K/XP so that we will know whether or not we can safely call
** the LockFileEx() API.
*/
-#if SQLITE_OS_WINCE
-# define isNT() (1)
+
+#if !SQLITE_WIN32_GETVERSIONEX
+# define osIsNT() (1)
+#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
+# define osIsNT() (1)
+#elif !defined(SQLITE_WIN32_HAS_WIDE)
+# define osIsNT() (0)
#else
- static int isNT(void){
- if( sqlite3_os_type==0 ){
- OSVERSIONINFO sInfo;
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
- GetVersionEx(&sInfo);
- sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
- }
- return sqlite3_os_type==2;
- }
-#endif /* SQLITE_OS_WINCE */
+# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt())
+#endif
+
+/*
+** This function determines if the machine is running a version of Windows
+** based on the NT kernel.
+*/
+SQLITE_API int sqlite3_win32_is_nt(void){
+#if SQLITE_OS_WINRT
+ /*
+ ** NOTE: The WinRT sub-platform is always assumed to be based on the NT
+ ** kernel.
+ */
+ return 1;
+#elif SQLITE_WIN32_GETVERSIONEX
+ if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){
+#if defined(SQLITE_WIN32_HAS_ANSI)
+ OSVERSIONINFOA sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ osGetVersionExA(&sInfo);
+ osInterlockedCompareExchange(&sqlite3_os_type,
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ OSVERSIONINFOW sInfo;
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo);
+ osGetVersionExW(&sInfo);
+ osInterlockedCompareExchange(&sqlite3_os_type,
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
+#endif
+ }
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
+#elif SQLITE_TEST
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
+#else
+ /*
+ ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are
+ ** deprecated are always assumed to be based on the NT kernel.
+ */
+ return 1;
+#endif
+}
#ifdef SQLITE_WIN32_MALLOC
/*
@@ -31813,14 +38652,14 @@ static void *winMemMalloc(int nBytes){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
- assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
assert( nBytes>=0 );
- p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
+ p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
if( !p ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%d), heap=%p",
- nBytes, GetLastError(), (void*)hHeap);
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p",
+ nBytes, osGetLastError(), (void*)hHeap);
}
return p;
}
@@ -31835,13 +38674,13 @@ static void winMemFree(void *pPrior){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
- assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
#endif
if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */
- if( !HeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%d), heap=%p",
- pPrior, GetLastError(), (void*)hHeap);
+ if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p",
+ pPrior, osGetLastError(), (void*)hHeap);
}
}
@@ -31856,19 +38695,19 @@ static void *winMemRealloc(void *pPrior, int nBytes){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
- assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) );
#endif
assert( nBytes>=0 );
if( !pPrior ){
- p = HeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
+ p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes);
}else{
- p = HeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
+ p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes);
}
if( !p ){
- sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%d), heap=%p",
- pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, GetLastError(),
- (void*)hHeap);
+ sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p",
+ pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(),
+ (void*)hHeap);
}
return p;
}
@@ -31884,14 +38723,14 @@ static int winMemSize(void *p){
hHeap = winMemGetHeap();
assert( hHeap!=0 );
assert( hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
- assert ( HeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) );
#endif
if( !p ) return 0;
- n = HeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
+ n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p);
if( n==(SIZE_T)-1 ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%d), heap=%p",
- p, GetLastError(), (void*)hHeap);
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p",
+ p, osGetLastError(), (void*)hHeap);
return 0;
}
return (int)n;
@@ -31911,24 +38750,44 @@ static int winMemInit(void *pAppData){
winMemData *pWinMemData = (winMemData *)pAppData;
if( !pWinMemData ) return SQLITE_ERROR;
- assert( pWinMemData->magic==WINMEM_MAGIC );
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 );
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 );
+
+#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE
if( !pWinMemData->hHeap ){
- pWinMemData->hHeap = HeapCreate(SQLITE_WIN32_HEAP_FLAGS,
- SQLITE_WIN32_HEAP_INIT_SIZE,
- SQLITE_WIN32_HEAP_MAX_SIZE);
+ DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE;
+ DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap;
+ if( dwMaximumSize==0 ){
+ dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE;
+ }else if( dwInitialSize>dwMaximumSize ){
+ dwInitialSize = dwMaximumSize;
+ }
+ pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS,
+ dwInitialSize, dwMaximumSize);
if( !pWinMemData->hHeap ){
sqlite3_log(SQLITE_NOMEM,
- "failed to HeapCreate (%d), flags=%u, initSize=%u, maxSize=%u",
- GetLastError(), SQLITE_WIN32_HEAP_FLAGS, SQLITE_WIN32_HEAP_INIT_SIZE,
- SQLITE_WIN32_HEAP_MAX_SIZE);
- return SQLITE_NOMEM;
+ "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu",
+ osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize,
+ dwMaximumSize);
+ return SQLITE_NOMEM_BKPT;
}
pWinMemData->bOwned = TRUE;
+ assert( pWinMemData->bOwned );
+ }
+#else
+ pWinMemData->hHeap = osGetProcessHeap();
+ if( !pWinMemData->hHeap ){
+ sqlite3_log(SQLITE_NOMEM,
+ "failed to GetProcessHeap (%lu)", osGetLastError());
+ return SQLITE_NOMEM_BKPT;
}
+ pWinMemData->bOwned = FALSE;
+ assert( !pWinMemData->bOwned );
+#endif
assert( pWinMemData->hHeap!=0 );
assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
- assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
return SQLITE_OK;
}
@@ -31940,15 +38799,18 @@ static void winMemShutdown(void *pAppData){
winMemData *pWinMemData = (winMemData *)pAppData;
if( !pWinMemData ) return;
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 );
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 );
+
if( pWinMemData->hHeap ){
assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE );
-#ifdef SQLITE_WIN32_MALLOC_VALIDATE
- assert( HeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE)
+ assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) );
#endif
if( pWinMemData->bOwned ){
- if( !HeapDestroy(pWinMemData->hHeap) ){
- sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%d), heap=%p",
- GetLastError(), (void*)pWinMemData->hHeap);
+ if( !osHeapDestroy(pWinMemData->hHeap) ){
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p",
+ osGetLastError(), (void*)pWinMemData->hHeap);
}
pWinMemData->bOwned = FALSE;
}
@@ -31984,193 +38846,359 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
#endif /* SQLITE_WIN32_MALLOC */
/*
-** Convert a UTF-8 string to microsoft unicode (UTF-16?).
+** Convert a UTF-8 string to Microsoft Unicode.
**
-** Space to hold the returned string is obtained from malloc.
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static WCHAR *utf8ToUnicode(const char *zFilename){
+static LPWSTR winUtf8ToUnicode(const char *zText){
int nChar;
- WCHAR *zWideFilename;
+ LPWSTR zWideText;
- nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
- zWideFilename = malloc( nChar*sizeof(zWideFilename[0]) );
- if( zWideFilename==0 ){
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, NULL, 0);
+ if( nChar==0 ){
return 0;
}
- nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, nChar);
+ zWideText = sqlite3MallocZero( nChar*sizeof(WCHAR) );
+ if( zWideText==0 ){
+ return 0;
+ }
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zText, -1, zWideText,
+ nChar);
if( nChar==0 ){
- free(zWideFilename);
- zWideFilename = 0;
+ sqlite3_free(zWideText);
+ zWideText = 0;
}
- return zWideFilename;
+ return zWideText;
}
/*
-** Convert microsoft unicode to UTF-8. Space to hold the returned string is
-** obtained from malloc().
+** Convert a Microsoft Unicode string to UTF-8.
+**
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static char *unicodeToUtf8(const WCHAR *zWideFilename){
+static char *winUnicodeToUtf8(LPCWSTR zWideText){
int nByte;
- char *zFilename;
+ char *zText;
- nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0);
- zFilename = malloc( nByte );
- if( zFilename==0 ){
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
return 0;
}
- nByte = WideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte,
- 0, 0);
+ zText = sqlite3MallocZero( nByte );
+ if( zText==0 ){
+ return 0;
+ }
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideText, -1, zText, nByte,
+ 0, 0);
if( nByte == 0 ){
- free(zFilename);
- zFilename = 0;
+ sqlite3_free(zText);
+ zText = 0;
}
- return zFilename;
+ return zText;
}
/*
-** Convert an ansi string to microsoft unicode, based on the
-** current codepage settings for file apis.
-**
-** Space to hold the returned string is obtained
-** from malloc.
+** Convert an ANSI string to Microsoft Unicode, using the ANSI or OEM
+** code page.
+**
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static WCHAR *mbcsToUnicode(const char *zFilename){
+static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
int nByte;
- WCHAR *zMbcsFilename;
- int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ LPWSTR zMbcsText;
+ int codepage = useAnsi ? CP_ACP : CP_OEMCP;
- nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, NULL,0)*sizeof(WCHAR);
- zMbcsFilename = malloc( nByte*sizeof(zMbcsFilename[0]) );
- if( zMbcsFilename==0 ){
+ nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
+ 0)*sizeof(WCHAR);
+ if( nByte==0 ){
return 0;
}
- nByte = MultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, nByte);
+ zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
+ if( zMbcsText==0 ){
+ return 0;
+ }
+ nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
+ nByte);
if( nByte==0 ){
- free(zMbcsFilename);
- zMbcsFilename = 0;
+ sqlite3_free(zMbcsText);
+ zMbcsText = 0;
}
- return zMbcsFilename;
+ return zMbcsText;
}
/*
-** Convert microsoft unicode to multibyte character string, based on the
-** user's Ansi codepage.
+** Convert a Microsoft Unicode string to a multi-byte character string,
+** using the ANSI or OEM code page.
**
-** Space to hold the returned string is obtained from
-** malloc().
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-static char *unicodeToMbcs(const WCHAR *zWideFilename){
+static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){
int nByte;
- char *zFilename;
- int codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
+ char *zText;
+ int codepage = useAnsi ? CP_ACP : CP_OEMCP;
- nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0);
- zFilename = malloc( nByte );
- if( zFilename==0 ){
+ nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, 0, 0, 0, 0);
+ if( nByte == 0 ){
+ return 0;
+ }
+ zText = sqlite3MallocZero( nByte );
+ if( zText==0 ){
return 0;
}
- nByte = WideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, nByte,
- 0, 0);
+ nByte = osWideCharToMultiByte(codepage, 0, zWideText, -1, zText,
+ nByte, 0, 0);
if( nByte == 0 ){
- free(zFilename);
- zFilename = 0;
+ sqlite3_free(zText);
+ zText = 0;
}
- return zFilename;
+ return zText;
}
/*
-** Convert multibyte character string to UTF-8. Space to hold the
-** returned string is obtained from malloc().
+** Convert a multi-byte character string to UTF-8.
+**
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
- char *zFilenameUtf8;
- WCHAR *zTmpWide;
+static char *winMbcsToUtf8(const char *zText, int useAnsi){
+ char *zTextUtf8;
+ LPWSTR zTmpWide;
- zTmpWide = mbcsToUnicode(zFilename);
+ zTmpWide = winMbcsToUnicode(zText, useAnsi);
if( zTmpWide==0 ){
return 0;
}
- zFilenameUtf8 = unicodeToUtf8(zTmpWide);
- free(zTmpWide);
- return zFilenameUtf8;
+ zTextUtf8 = winUnicodeToUtf8(zTmpWide);
+ sqlite3_free(zTmpWide);
+ return zTextUtf8;
}
/*
-** Convert UTF-8 to multibyte character string. Space to hold the
-** returned string is obtained from malloc().
+** Convert a UTF-8 string to a multi-byte character string.
+**
+** Space to hold the returned string is obtained from sqlite3_malloc().
*/
-SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
- char *zFilenameMbcs;
- WCHAR *zTmpWide;
+static char *winUtf8ToMbcs(const char *zText, int useAnsi){
+ char *zTextMbcs;
+ LPWSTR zTmpWide;
- zTmpWide = utf8ToUnicode(zFilename);
+ zTmpWide = winUtf8ToUnicode(zText);
if( zTmpWide==0 ){
return 0;
}
- zFilenameMbcs = unicodeToMbcs(zTmpWide);
- free(zTmpWide);
- return zFilenameMbcs;
+ zTextMbcs = winUnicodeToMbcs(zTmpWide, useAnsi);
+ sqlite3_free(zTmpWide);
+ return zTextMbcs;
+}
+
+/*
+** This is a public wrapper for the winUtf8ToUnicode() function.
+*/
+SQLITE_API LPWSTR sqlite3_win32_utf8_to_unicode(const char *zText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUtf8ToUnicode(zText);
+}
+
+/*
+** This is a public wrapper for the winUnicodeToUtf8() function.
+*/
+SQLITE_API char *sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zWideText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUnicodeToUtf8(zWideText);
+}
+
+/*
+** This is a public wrapper for the winMbcsToUtf8() function.
+*/
+SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winMbcsToUtf8(zText, osAreFileApisANSI());
+}
+
+/*
+** This is a public wrapper for the winMbcsToUtf8() function.
+*/
+SQLITE_API char *sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winMbcsToUtf8(zText, useAnsi);
}
+/*
+** This is a public wrapper for the winUtf8ToMbcs() function.
+*/
+SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zText){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUtf8ToMbcs(zText, osAreFileApisANSI());
+}
/*
-** The return value of getLastErrorMsg
+** This is a public wrapper for the winUtf8ToMbcs() function.
+*/
+SQLITE_API char *sqlite3_win32_utf8_to_mbcs_v2(const char *zText, int useAnsi){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !zText ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+#ifndef SQLITE_OMIT_AUTOINIT
+ if( sqlite3_initialize() ) return 0;
+#endif
+ return winUtf8ToMbcs(zText, useAnsi);
+}
+
+/*
+** This function sets the data directory or the temporary directory based on
+** the provided arguments. The type argument must be 1 in order to set the
+** data directory or 2 in order to set the temporary directory. The zValue
+** argument is the name of the directory to use. The return value will be
+** SQLITE_OK if successful.
+*/
+SQLITE_API int sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){
+ char **ppDirectory = 0;
+#ifndef SQLITE_OMIT_AUTOINIT
+ int rc = sqlite3_initialize();
+ if( rc ) return rc;
+#endif
+ if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){
+ ppDirectory = &sqlite3_data_directory;
+ }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){
+ ppDirectory = &sqlite3_temp_directory;
+ }
+ assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE
+ || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE
+ );
+ assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) );
+ if( ppDirectory ){
+ char *zValueUtf8 = 0;
+ if( zValue && zValue[0] ){
+ zValueUtf8 = winUnicodeToUtf8(zValue);
+ if ( zValueUtf8==0 ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ }
+ sqlite3_free(*ppDirectory);
+ *ppDirectory = zValueUtf8;
+ return SQLITE_OK;
+ }
+ return SQLITE_ERROR;
+}
+
+/*
+** The return value of winGetLastErrorMsg
** is zero if the error message fits in the buffer, or non-zero
** otherwise (if the message was truncated).
*/
-static int getLastErrorMsg(int nBuf, char *zBuf){
+static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){
/* FormatMessage returns 0 on failure. Otherwise it
** returns the number of TCHARs written to the output
** buffer, excluding the terminating null char.
*/
- DWORD error = GetLastError();
DWORD dwLen = 0;
char *zOut = 0;
- if( isNT() ){
- WCHAR *zTempWide = NULL;
- dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPWSTR) &zTempWide,
- 0,
- 0);
+ if( osIsNT() ){
+#if SQLITE_OS_WINRT
+ WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1];
+ dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ lastErrno,
+ 0,
+ zTempWide,
+ SQLITE_WIN32_MAX_ERRMSG_CHARS,
+ 0);
+#else
+ LPWSTR zTempWide = NULL;
+ dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ lastErrno,
+ 0,
+ (LPWSTR) &zTempWide,
+ 0,
+ 0);
+#endif
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
- zOut = unicodeToUtf8(zTempWide);
+ sqlite3BeginBenignMalloc();
+ zOut = winUnicodeToUtf8(zTempWide);
+ sqlite3EndBenignMalloc();
+#if !SQLITE_OS_WINRT
/* free the system buffer allocated by FormatMessage */
- LocalFree(zTempWide);
+ osLocalFree(zTempWide);
+#endif
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zTemp = NULL;
- dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- NULL,
- error,
- 0,
- (LPSTR) &zTemp,
- 0,
- 0);
+ dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ lastErrno,
+ 0,
+ (LPSTR) &zTemp,
+ 0,
+ 0);
if( dwLen > 0 ){
/* allocate a buffer and convert to UTF8 */
- zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
+ sqlite3BeginBenignMalloc();
+ zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
+ sqlite3EndBenignMalloc();
/* free the system buffer allocated by FormatMessage */
- LocalFree(zTemp);
+ osLocalFree(zTemp);
}
-#endif
}
+#endif
if( 0 == dwLen ){
- sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error);
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno);
}else{
/* copy a maximum of nBuf chars to output buffer */
sqlite3_snprintf(nBuf, zBuf, "%s", zOut);
/* free the UTF8 buffer */
- free(zOut);
+ sqlite3_free(zOut);
}
return 0;
}
@@ -32182,34 +39210,34 @@ static int getLastErrorMsg(int nBuf, char *zBuf){
**
** This routine is invoked after an error occurs in an OS function.
** It logs a message using sqlite3_log() containing the current value of
-** error code and, if possible, the human-readable equivalent from
+** error code and, if possible, the human-readable equivalent from
** FormatMessage.
**
** The first argument passed to the macro should be the error code that
-** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
+** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN).
** The two subsequent arguments should be the name of the OS function that
-** failed and the the associated file-system path, if any.
+** failed and the associated file-system path, if any.
*/
-#define winLogError(a,b,c) winLogErrorAtLine(a,b,c,__LINE__)
+#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__)
static int winLogErrorAtLine(
int errcode, /* SQLite error code */
+ DWORD lastErrno, /* Win32 last error */
const char *zFunc, /* Name of OS function that failed */
const char *zPath, /* File path associated with error */
int iLine /* Source line number where error occurred */
){
char zMsg[500]; /* Human readable error text */
int i; /* Loop counter */
- DWORD iErrno = GetLastError(); /* Error code */
zMsg[0] = 0;
- getLastErrorMsg(sizeof(zMsg), zMsg);
+ winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg);
assert( errcode!=SQLITE_OK );
if( zPath==0 ) zPath = "";
for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){}
zMsg[i] = 0;
sqlite3_log(errcode,
- "os_win.c:%d: (%d) %s(%s) - %s",
- iLine, iErrno, zFunc, zPath, zMsg
+ "os_win.c:%d: (%lu) %s(%s) - %s",
+ iLine, lastErrno, zFunc, zPath, zMsg
);
return errcode;
@@ -32217,7 +39245,7 @@ static int winLogErrorAtLine(
/*
** The number of times that a ReadFile(), WriteFile(), and DeleteFile()
-** will be retried following a locking error - probably caused by
+** will be retried following a locking error - probably caused by
** antivirus software. Also the initial delay before the first retry.
** The delay increases linearly with each retry.
*/
@@ -32227,51 +39255,89 @@ static int winLogErrorAtLine(
#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
#endif
-static int win32IoerrRetry = SQLITE_WIN32_IOERR_RETRY;
-static int win32IoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
+static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY;
+static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
+
+/*
+** The "winIoerrCanRetry1" macro is used to determine if a particular I/O
+** error code obtained via GetLastError() is eligible to be retried. It
+** must accept the error code DWORD as its only argument and should return
+** non-zero if the error code is transient in nature and the operation
+** responsible for generating the original error might succeed upon being
+** retried. The argument to this macro should be a variable.
+**
+** Additionally, a macro named "winIoerrCanRetry2" may be defined. If it
+** is defined, it will be consulted only when the macro "winIoerrCanRetry1"
+** returns zero. The "winIoerrCanRetry2" macro is completely optional and
+** may be used to include additional error codes in the set that should
+** result in the failing I/O operation being retried by the caller. If
+** defined, the "winIoerrCanRetry2" macro must exhibit external semantics
+** identical to those of the "winIoerrCanRetry1" macro.
+*/
+#if !defined(winIoerrCanRetry1)
+#define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \
+ ((a)==ERROR_SHARING_VIOLATION) || \
+ ((a)==ERROR_LOCK_VIOLATION) || \
+ ((a)==ERROR_DEV_NOT_EXIST) || \
+ ((a)==ERROR_NETNAME_DELETED) || \
+ ((a)==ERROR_SEM_TIMEOUT) || \
+ ((a)==ERROR_NETWORK_UNREACHABLE))
+#endif
/*
** If a ReadFile() or WriteFile() error occurs, invoke this routine
** to see if it should be retried. Return TRUE to retry. Return FALSE
** to give up with an error.
*/
-static int retryIoerr(int *pnRetry){
- DWORD e;
- if( *pnRetry>=win32IoerrRetry ){
+static int winRetryIoerr(int *pnRetry, DWORD *pError){
+ DWORD e = osGetLastError();
+ if( *pnRetry>=winIoerrRetry ){
+ if( pError ){
+ *pError = e;
+ }
return 0;
}
- e = GetLastError();
- if( e==ERROR_ACCESS_DENIED ||
- e==ERROR_LOCK_VIOLATION ||
- e==ERROR_SHARING_VIOLATION ){
- Sleep(win32IoerrRetryDelay*(1+*pnRetry));
+ if( winIoerrCanRetry1(e) ){
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
++*pnRetry;
return 1;
}
+#if defined(winIoerrCanRetry2)
+ else if( winIoerrCanRetry2(e) ){
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
+ ++*pnRetry;
+ return 1;
+ }
+#endif
+ if( pError ){
+ *pError = e;
+ }
return 0;
}
/*
** Log a I/O error retry episode.
*/
-static void logIoerr(int nRetry){
+static void winLogIoerr(int nRetry, int lineno){
if( nRetry ){
- sqlite3_log(SQLITE_IOERR,
- "delayed %dms for lock/sharing conflict",
- win32IoerrRetryDelay*nRetry*(nRetry+1)/2
+ sqlite3_log(SQLITE_NOTICE,
+ "delayed %dms for lock/sharing conflict at line %d",
+ winIoerrRetryDelay*nRetry*(nRetry+1)/2, lineno
);
}
}
-#if SQLITE_OS_WINCE
-/*************************************************************************
-** This section contains code for WinCE only.
+/*
+** This #if does not rely on the SQLITE_OS_WINCE define because the
+** corresponding section in "date.c" cannot use it.
*/
+#if !defined(SQLITE_OMIT_LOCALTIME) && defined(_WIN32_WCE) && \
+ (!defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API)
/*
-** WindowsCE does not have a localtime() function. So create a
-** substitute.
+** The MSVC CRT on Windows CE may not have a localtime() function.
+** So define a substitute.
*/
-/* #include <time.h> */
+/* # include <time.h> */
struct tm *__cdecl localtime(const time_t *t)
{
static struct tm y;
@@ -32282,8 +39348,8 @@ struct tm *__cdecl localtime(const time_t *t)
t64 = (t64 + 11644473600)*10000000;
uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF);
uTm.dwHighDateTime= (DWORD)(t64 >> 32);
- FileTimeToLocalFileTime(&uTm,&lTm);
- FileTimeToSystemTime(&lTm,&pTm);
+ osFileTimeToLocalFileTime(&uTm,&lTm);
+ osFileTimeToSystemTime(&lTm,&pTm);
y.tm_year = pTm.wYear - 1900;
y.tm_mon = pTm.wMonth - 1;
y.tm_wday = pTm.wDayOfWeek;
@@ -32293,14 +39359,12 @@ struct tm *__cdecl localtime(const time_t *t)
y.tm_sec = pTm.wSecond;
return &y;
}
+#endif
-/* This will never be called, but defined to make the code compile */
-#define GetTempPathA(a,b)
-
-#define LockFile(a,b,c,d,e) winceLockFile(&a, b, c, d, e)
-#define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e)
-#define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f)
-
+#if SQLITE_OS_WINCE
+/*************************************************************************
+** This section contains code for WinCE only.
+*/
#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)]
/*
@@ -32309,7 +39373,7 @@ struct tm *__cdecl localtime(const time_t *t)
static void winceMutexAcquire(HANDLE h){
DWORD dwErr;
do {
- dwErr = WaitForSingleObject(h, INFINITE);
+ dwErr = osWaitForSingleObject(h, INFINITE);
} while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED);
}
/*
@@ -32321,78 +39385,95 @@ static void winceMutexAcquire(HANDLE h){
** Create the mutex and shared memory used for locking in the file
** descriptor pFile
*/
-static BOOL winceCreateLock(const char *zFilename, winFile *pFile){
- WCHAR *zTok;
- WCHAR *zName = utf8ToUnicode(zFilename);
+static int winceCreateLock(const char *zFilename, winFile *pFile){
+ LPWSTR zTok;
+ LPWSTR zName;
+ DWORD lastErrno;
+ BOOL bLogged = FALSE;
BOOL bInit = TRUE;
+ zName = winUtf8ToUnicode(zFilename);
+ if( zName==0 ){
+ /* out of memory */
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+
/* Initialize the local lockdata */
- ZeroMemory(&pFile->local, sizeof(pFile->local));
+ memset(&pFile->local, 0, sizeof(pFile->local));
/* Replace the backslashes from the filename and lowercase it
** to derive a mutex name. */
- zTok = CharLowerW(zName);
+ zTok = osCharLowerW(zName);
for (;*zTok;zTok++){
if (*zTok == '\\') *zTok = '_';
}
/* Create/open the named mutex */
- pFile->hMutex = CreateMutexW(NULL, FALSE, zName);
+ pFile->hMutex = osCreateMutexW(NULL, FALSE, zName);
if (!pFile->hMutex){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_ERROR, "winceCreateLock1", zFilename);
- free(zName);
- return FALSE;
+ pFile->lastErrno = osGetLastError();
+ sqlite3_free(zName);
+ return winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock1", zFilename);
}
/* Acquire the mutex before continuing */
winceMutexAcquire(pFile->hMutex);
-
- /* Since the names of named mutexes, semaphores, file mappings etc are
+
+ /* Since the names of named mutexes, semaphores, file mappings etc are
** case-sensitive, take advantage of that by uppercasing the mutex name
** and using that as the shared filemapping name.
*/
- CharUpperW(zName);
- pFile->hShared = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
- PAGE_READWRITE, 0, sizeof(winceLock),
- zName);
+ osCharUpperW(zName);
+ pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL,
+ PAGE_READWRITE, 0, sizeof(winceLock),
+ zName);
- /* Set a flag that indicates we're the first to create the memory so it
+ /* Set a flag that indicates we're the first to create the memory so it
** must be zero-initialized */
- if (GetLastError() == ERROR_ALREADY_EXISTS){
+ lastErrno = osGetLastError();
+ if (lastErrno == ERROR_ALREADY_EXISTS){
bInit = FALSE;
}
- free(zName);
+ sqlite3_free(zName);
/* If we succeeded in making the shared memory handle, map it. */
- if (pFile->hShared){
- pFile->shared = (winceLock*)MapViewOfFile(pFile->hShared,
+ if( pFile->hShared ){
+ pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared,
FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock));
/* If mapping failed, close the shared memory handle and erase it */
- if (!pFile->shared){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_ERROR, "winceCreateLock2", zFilename);
- CloseHandle(pFile->hShared);
+ if( !pFile->shared ){
+ pFile->lastErrno = osGetLastError();
+ winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock2", zFilename);
+ bLogged = TRUE;
+ osCloseHandle(pFile->hShared);
pFile->hShared = NULL;
}
}
/* If shared memory could not be created, then close the mutex and fail */
- if (pFile->hShared == NULL){
+ if( pFile->hShared==NULL ){
+ if( !bLogged ){
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_IOERR, pFile->lastErrno,
+ "winceCreateLock3", zFilename);
+ bLogged = TRUE;
+ }
winceMutexRelease(pFile->hMutex);
- CloseHandle(pFile->hMutex);
+ osCloseHandle(pFile->hMutex);
pFile->hMutex = NULL;
- return FALSE;
+ return SQLITE_IOERR;
}
-
+
/* Initialize the shared memory if we're supposed to */
- if (bInit) {
- ZeroMemory(pFile->shared, sizeof(winceLock));
+ if( bInit ){
+ memset(pFile->shared, 0, sizeof(winceLock));
}
winceMutexRelease(pFile->hMutex);
- return TRUE;
+ return SQLITE_OK;
}
/*
@@ -32419,21 +39500,21 @@ static void winceDestroyLock(winFile *pFile){
}
/* De-reference and close our copy of the shared memory handle */
- UnmapViewOfFile(pFile->shared);
- CloseHandle(pFile->hShared);
+ osUnmapViewOfFile(pFile->shared);
+ osCloseHandle(pFile->hShared);
/* Done with the mutex */
- winceMutexRelease(pFile->hMutex);
- CloseHandle(pFile->hMutex);
+ winceMutexRelease(pFile->hMutex);
+ osCloseHandle(pFile->hMutex);
pFile->hMutex = NULL;
}
}
-/*
-** An implementation of the LockFile() API of windows for wince
+/*
+** An implementation of the LockFile() API of Windows for CE
*/
static BOOL winceLockFile(
- HANDLE *phFile,
+ LPHANDLE phFile,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToLockLow,
@@ -32471,7 +39552,8 @@ static BOOL winceLockFile(
}
/* Want a pending lock? */
- else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToLockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)PENDING_BYTE
+ && nNumberOfBytesToLockLow == 1){
/* If no pending lock has been acquired, then acquire it */
if (pFile->shared->bPending == 0) {
pFile->shared->bPending = TRUE;
@@ -32481,7 +39563,8 @@ static BOOL winceLockFile(
}
/* Want a reserved lock? */
- else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToLockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE
+ && nNumberOfBytesToLockLow == 1){
if (pFile->shared->bReserved == 0) {
pFile->shared->bReserved = TRUE;
pFile->local.bReserved = TRUE;
@@ -32494,10 +39577,10 @@ static BOOL winceLockFile(
}
/*
-** An implementation of the UnlockFile API of windows for wince
+** An implementation of the UnlockFile API of Windows for CE
*/
static BOOL winceUnlockFile(
- HANDLE *phFile,
+ LPHANDLE phFile,
DWORD dwFileOffsetLow,
DWORD dwFileOffsetHigh,
DWORD nNumberOfBytesToUnlockLow,
@@ -32524,7 +39607,8 @@ static BOOL winceUnlockFile(
/* Did we just have a reader lock? */
else if (pFile->local.nReaders){
- assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE || nNumberOfBytesToUnlockLow == 1);
+ assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE
+ || nNumberOfBytesToUnlockLow == 1);
pFile->local.nReaders --;
if (pFile->local.nReaders == 0)
{
@@ -32535,7 +39619,8 @@ static BOOL winceUnlockFile(
}
/* Releasing a pending lock */
- else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)PENDING_BYTE
+ && nNumberOfBytesToUnlockLow == 1){
if (pFile->local.bPending){
pFile->local.bPending = FALSE;
pFile->shared->bPending = FALSE;
@@ -32543,7 +39628,8 @@ static BOOL winceUnlockFile(
}
}
/* Releasing a reserved lock */
- else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){
+ else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE
+ && nNumberOfBytesToUnlockLow == 1){
if (pFile->local.bReserved) {
pFile->local.bReserved = FALSE;
pFile->shared->bReserved = FALSE;
@@ -32554,34 +39640,73 @@ static BOOL winceUnlockFile(
winceMutexRelease(pFile->hMutex);
return bReturn;
}
+/*
+** End of the special code for wince
+*****************************************************************************/
+#endif /* SQLITE_OS_WINCE */
/*
-** An implementation of the LockFileEx() API of windows for wince
+** Lock a file region.
*/
-static BOOL winceLockFileEx(
- HANDLE *phFile,
- DWORD dwFlags,
- DWORD dwReserved,
- DWORD nNumberOfBytesToLockLow,
- DWORD nNumberOfBytesToLockHigh,
- LPOVERLAPPED lpOverlapped
+static BOOL winLockFile(
+ LPHANDLE phFile,
+ DWORD flags,
+ DWORD offsetLow,
+ DWORD offsetHigh,
+ DWORD numBytesLow,
+ DWORD numBytesHigh
){
- UNUSED_PARAMETER(dwReserved);
- UNUSED_PARAMETER(nNumberOfBytesToLockHigh);
-
- /* If the caller wants a shared read lock, forward this call
- ** to winceLockFile */
- if (lpOverlapped->Offset == (DWORD)SHARED_FIRST &&
- dwFlags == 1 &&
- nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){
- return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0);
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API LockFile.
+ */
+ return winceLockFile(phFile, offsetLow, offsetHigh,
+ numBytesLow, numBytesHigh);
+#else
+ if( osIsNT() ){
+ OVERLAPPED ovlp;
+ memset(&ovlp, 0, sizeof(OVERLAPPED));
+ ovlp.Offset = offsetLow;
+ ovlp.OffsetHigh = offsetHigh;
+ return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
+ }else{
+ return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
+ numBytesHigh);
}
- return FALSE;
+#endif
}
+
/*
-** End of the special code for wince
-*****************************************************************************/
-#endif /* SQLITE_OS_WINCE */
+** Unlock a file region.
+ */
+static BOOL winUnlockFile(
+ LPHANDLE phFile,
+ DWORD offsetLow,
+ DWORD offsetHigh,
+ DWORD numBytesLow,
+ DWORD numBytesHigh
+){
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API UnlockFile.
+ */
+ return winceUnlockFile(phFile, offsetLow, offsetHigh,
+ numBytesLow, numBytesHigh);
+#else
+ if( osIsNT() ){
+ OVERLAPPED ovlp;
+ memset(&ovlp, 0, sizeof(OVERLAPPED));
+ ovlp.Offset = offsetLow;
+ ovlp.OffsetHigh = offsetHigh;
+ return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
+ }else{
+ return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
+ numBytesHigh);
+ }
+#endif
+}
/*****************************************************************************
** The next group of routines implement the I/O methods specified
@@ -32589,47 +39714,84 @@ static BOOL winceLockFileEx(
******************************************************************************/
/*
-** Some microsoft compilers lack this definition.
+** Some Microsoft compilers lack this definition.
*/
#ifndef INVALID_SET_FILE_POINTER
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
/*
-** Move the current position of the file handle passed as the first
-** argument to offset iOffset within the file. If successful, return 0.
+** Move the current position of the file handle passed as the first
+** argument to offset iOffset within the file. If successful, return 0.
** Otherwise, set pFile->lastErrno and return non-zero.
*/
-static int seekWinFile(winFile *pFile, sqlite3_int64 iOffset){
+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
+#if !SQLITE_OS_WINRT
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
+ DWORD lastErrno; /* Value returned by GetLastError() */
+
+ OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset));
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
- /* API oddity: If successful, SetFilePointer() returns a dword
+ /* API oddity: If successful, SetFilePointer() returns a dword
** containing the lower 32-bits of the new file-offset. Or, if it fails,
- ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
- ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
- ** whether an error has actually occured, it is also necessary to call
+ ** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
+ ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
+ ** whether an error has actually occurred, it is also necessary to call
** GetLastError().
*/
- dwRet = SetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
- if( (dwRet==INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR) ){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_IOERR_SEEK, "seekWinFile", pFile->zPath);
+ dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
+
+ if( (dwRet==INVALID_SET_FILE_POINTER
+ && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
+ "winSeekFile", pFile->zPath);
+ OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
+ return 1;
+ }
+
+ OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
+ return 0;
+#else
+ /*
+ ** Same as above, except that this implementation works for WinRT.
+ */
+
+ LARGE_INTEGER x; /* The new offset */
+ BOOL bRet; /* Value returned by SetFilePointerEx() */
+
+ x.QuadPart = iOffset;
+ bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
+
+ if(!bRet){
+ pFile->lastErrno = osGetLastError();
+ winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
+ "winSeekFile", pFile->zPath);
+ OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
return 1;
}
+ OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
return 0;
+#endif
}
+#if SQLITE_MAX_MMAP_SIZE>0
+/* Forward references to VFS helper methods used for memory mapped files */
+static int winMapfile(winFile*, sqlite3_int64);
+static int winUnmapfile(winFile*);
+#endif
+
/*
** Close a file.
**
** It is reported that an attempt to close a handle might sometimes
-** fail. This is a very unreasonable result, but windows is notorious
+** fail. This is a very unreasonable result, but Windows is notorious
** for being unreasonable so I do not doubt that it might happen. If
** the close fails, we pause for 100 milliseconds and try again. As
** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before
@@ -32641,31 +39803,50 @@ static int winClose(sqlite3_file *id){
winFile *pFile = (winFile*)id;
assert( id!=0 );
+#ifndef SQLITE_OMIT_WAL
assert( pFile->pShm==0 );
- OSTRACE(("CLOSE %d\n", pFile->h));
+#endif
+ assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE );
+ OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ winUnmapfile(pFile);
+#endif
+
do{
- rc = CloseHandle(pFile->h);
+ rc = osCloseHandle(pFile->h);
/* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */
- }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (Sleep(100), 1) );
+ }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) );
#if SQLITE_OS_WINCE
#define WINCE_DELETION_ATTEMPTS 3
- winceDestroyLock(pFile);
+ {
+ winVfsAppData *pAppData = (winVfsAppData*)pFile->pVfs->pAppData;
+ if( pAppData==NULL || !pAppData->bNoLock ){
+ winceDestroyLock(pFile);
+ }
+ }
if( pFile->zDeleteOnClose ){
int cnt = 0;
while(
- DeleteFileW(pFile->zDeleteOnClose)==0
- && GetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
+ osDeleteFileW(pFile->zDeleteOnClose)==0
+ && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff
&& cnt++ < WINCE_DELETION_ATTEMPTS
){
- Sleep(100); /* Wait a little before trying again */
+ sqlite3_win32_sleep(100); /* Wait a little before trying again */
}
- free(pFile->zDeleteOnClose);
+ sqlite3_free(pFile->zDeleteOnClose);
}
#endif
- OSTRACE(("CLOSE %d %s\n", pFile->h, rc ? "ok" : "failed"));
+ if( rc ){
+ pFile->h = NULL;
+ }
OpenCounter(-1);
+ OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed"));
return rc ? SQLITE_OK
- : winLogError(SQLITE_IOERR_CLOSE, "winClose", pFile->zPath);
+ : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(),
+ "winClose", pFile->zPath);
}
/*
@@ -32679,29 +39860,73 @@ static int winRead(
int amt, /* Number of bytes to read */
sqlite3_int64 offset /* Begin reading at this offset */
){
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
+ OVERLAPPED overlapped; /* The offset for ReadFile. */
+#endif
winFile *pFile = (winFile*)id; /* file handle */
DWORD nRead; /* Number of bytes actually read from file */
int nRetry = 0; /* Number of retrys */
assert( id!=0 );
+ assert( amt>0 );
+ assert( offset>=0 );
SimulateIOError(return SQLITE_IOERR_READ);
- OSTRACE(("READ %d lock=%d\n", pFile->h, pFile->locktype));
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
+ "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
+ pFile->h, pBuf, amt, offset, pFile->locktype));
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this read request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt);
+ OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
+ return SQLITE_OK;
+ }else{
+ int nCopy = (int)(pFile->mmapSize - offset);
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
- if( seekWinFile(pFile, offset) ){
+#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
+ if( winSeekFile(pFile, offset) ){
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
return SQLITE_FULL;
}
- while( !ReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
- if( retryIoerr(&nRetry) ) continue;
- pFile->lastErrno = GetLastError();
- return winLogError(SQLITE_IOERR_READ, "winRead", pFile->zPath);
- }
- logIoerr(nRetry);
+ while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){
+#else
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = (LONG)(offset & 0xffffffff);
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
+ while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) &&
+ osGetLastError()!=ERROR_HANDLE_EOF ){
+#endif
+ DWORD lastErrno;
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
+ pFile->lastErrno = lastErrno;
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
+ return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
+ "winRead", pFile->zPath);
+ }
+ winLogIoerr(nRetry, __LINE__);
if( nRead<(DWORD)amt ){
/* Unread parts of the buffer must be zero-filled */
memset(&((char*)pBuf)[nRead], 0, amt-nRead);
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
return SQLITE_IOERR_SHORT_READ;
}
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
return SQLITE_OK;
}
@@ -32715,7 +39940,7 @@ static int winWrite(
int amt, /* Number of bytes to write */
sqlite3_int64 offset /* Offset into the file to begin writing at */
){
- int rc; /* True if error has occured, else false */
+ int rc = 0; /* True if error has occurred, else false */
winFile *pFile = (winFile*)id; /* File handle */
int nRetry = 0; /* Number of retries */
@@ -32724,25 +39949,73 @@ static int winWrite(
SimulateIOError(return SQLITE_IOERR_WRITE);
SimulateDiskfullError(return SQLITE_FULL);
- OSTRACE(("WRITE %d lock=%d\n", pFile->h, pFile->locktype));
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, "
+ "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile,
+ pFile->h, pBuf, amt, offset, pFile->locktype));
+
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0
+ /* Deal with as much of this write request as possible by transfering
+ ** data from the memory mapping using memcpy(). */
+ if( offset<pFile->mmapSize ){
+ if( offset+amt <= pFile->mmapSize ){
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt);
+ OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
+ return SQLITE_OK;
+ }else{
+ int nCopy = (int)(pFile->mmapSize - offset);
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy);
+ pBuf = &((u8 *)pBuf)[nCopy];
+ amt -= nCopy;
+ offset += nCopy;
+ }
+ }
+#endif
- rc = seekWinFile(pFile, offset);
+#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
+ rc = winSeekFile(pFile, offset);
if( rc==0 ){
+#else
+ {
+#endif
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
+ OVERLAPPED overlapped; /* The offset for WriteFile. */
+#endif
u8 *aRem = (u8 *)pBuf; /* Data yet to be written */
int nRem = amt; /* Number of bytes yet to be written */
DWORD nWrite; /* Bytes written by each WriteFile() call */
+ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */
+
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
+ memset(&overlapped, 0, sizeof(OVERLAPPED));
+ overlapped.Offset = (LONG)(offset & 0xffffffff);
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
+#endif
while( nRem>0 ){
- if( !WriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
- if( retryIoerr(&nRetry) ) continue;
+#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED)
+ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){
+#else
+ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){
+#endif
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue;
break;
}
- if( nWrite<=0 ) break;
+ assert( nWrite==0 || nWrite<=(DWORD)nRem );
+ if( nWrite==0 || nWrite>(DWORD)nRem ){
+ lastErrno = osGetLastError();
+ break;
+ }
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED)
+ offset += nWrite;
+ overlapped.Offset = (LONG)(offset & 0xffffffff);
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
+#endif
aRem += nWrite;
nRem -= nWrite;
}
if( nRem>0 ){
- pFile->lastErrno = GetLastError();
+ pFile->lastErrno = lastErrno;
rc = 1;
}
}
@@ -32750,12 +40023,20 @@ static int winWrite(
if( rc ){
if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|| ( pFile->lastErrno==ERROR_DISK_FULL )){
- return SQLITE_FULL;
- }
- return winLogError(SQLITE_IOERR_WRITE, "winWrite", pFile->zPath);
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
+ return winLogError(SQLITE_FULL, pFile->lastErrno,
+ "winWrite1", pFile->zPath);
+ }
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
+ return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
+ "winWrite2", pFile->zPath);
}else{
- logIoerr(nRetry);
+ winLogIoerr(nRetry, __LINE__);
}
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
return SQLITE_OK;
}
@@ -32765,11 +40046,12 @@ static int winWrite(
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
winFile *pFile = (winFile*)id; /* File handle object */
int rc = SQLITE_OK; /* Return code for this function */
+ DWORD lastErrno;
assert( pFile );
-
- OSTRACE(("TRUNCATE %d %lld\n", pFile->h, nByte));
SimulateIOError(return SQLITE_IOERR_TRUNCATE);
+ OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n",
+ osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype));
/* If the user has configured a chunk-size for this file, truncate the
** file so that it consists of an integer number of chunks (i.e. the
@@ -32781,14 +40063,28 @@ static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
}
/* SetEndOfFile() returns non-zero when successful, or zero when it fails. */
- if( seekWinFile(pFile, nByte) ){
- rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate1", pFile->zPath);
- }else if( 0==SetEndOfFile(pFile->h) ){
- pFile->lastErrno = GetLastError();
- rc = winLogError(SQLITE_IOERR_TRUNCATE, "winTruncate2", pFile->zPath);
+ if( winSeekFile(pFile, nByte) ){
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
+ "winTruncate1", pFile->zPath);
+ }else if( 0==osSetEndOfFile(pFile->h) &&
+ ((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){
+ pFile->lastErrno = lastErrno;
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno,
+ "winTruncate2", pFile->zPath);
}
- OSTRACE(("TRUNCATE %d %lld %s\n", pFile->h, nByte, rc ? "failed" : "ok"));
+#if SQLITE_MAX_MMAP_SIZE>0
+ /* If the file was truncated to a size smaller than the currently
+ ** mapped region, reduce the effective mapping size as well. SQLite will
+ ** use read() and write() to access data beyond this point from now on.
+ */
+ if( pFile->pMapRegion && nByte<pFile->mmapSize ){
+ pFile->mmapSize = nByte;
+ }
+#endif
+
+ OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFile, pFile->h, sqlite3ErrName(rc)));
return rc;
}
@@ -32812,7 +40108,7 @@ static int winSync(sqlite3_file *id, int flags){
BOOL rc;
#endif
#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \
- (defined(SQLITE_TEST) && defined(SQLITE_DEBUG))
+ defined(SQLITE_HAVE_OS_TRACE)
/*
** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or
** OSTRACE() macros.
@@ -32828,13 +40124,15 @@ static int winSync(sqlite3_file *id, int flags){
|| (flags&0x0F)==SQLITE_SYNC_FULL
);
- OSTRACE(("SYNC %d lock=%d\n", pFile->h, pFile->locktype));
-
/* Unix cannot, but some systems may return SQLITE_FULL from here. This
** line is to test that doing so does not cause any problems.
*/
SimulateDiskfullError( return SQLITE_FULL );
+ OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n",
+ osGetCurrentProcessId(), pFile, pFile->h, flags,
+ pFile->locktype));
+
#ifndef SQLITE_TEST
UNUSED_PARAMETER(flags);
#else
@@ -32848,15 +40146,38 @@ static int winSync(sqlite3_file *id, int flags){
** no-op
*/
#ifdef SQLITE_NO_SYNC
+ OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
return SQLITE_OK;
#else
- rc = FlushFileBuffers(pFile->h);
+#if SQLITE_MAX_MMAP_SIZE>0
+ if( pFile->pMapRegion ){
+ if( osFlushViewOfFile(pFile->pMapRegion, 0) ){
+ OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, "
+ "rc=SQLITE_OK\n", osGetCurrentProcessId(),
+ pFile, pFile->pMapRegion));
+ }else{
+ pFile->lastErrno = osGetLastError();
+ OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, "
+ "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(),
+ pFile, pFile->pMapRegion));
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
+ "winSync1", pFile->zPath);
+ }
+ }
+#endif
+ rc = osFlushFileBuffers(pFile->h);
SimulateIOError( rc=FALSE );
if( rc ){
+ OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
return SQLITE_OK;
}else{
- pFile->lastErrno = GetLastError();
- return winLogError(SQLITE_IOERR_FSYNC, "winSync", pFile->zPath);
+ pFile->lastErrno = osGetLastError();
+ OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n",
+ osGetCurrentProcessId(), pFile, pFile->h));
+ return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno,
+ "winSync2", pFile->zPath);
}
#endif
}
@@ -32865,22 +40186,45 @@ static int winSync(sqlite3_file *id, int flags){
** Determine the current size of a file in bytes
*/
static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
- DWORD upperBits;
- DWORD lowerBits;
winFile *pFile = (winFile*)id;
- DWORD error;
+ int rc = SQLITE_OK;
assert( id!=0 );
+ assert( pSize!=0 );
SimulateIOError(return SQLITE_IOERR_FSTAT);
- lowerBits = GetFileSize(pFile->h, &upperBits);
- if( (lowerBits == INVALID_FILE_SIZE)
- && ((error = GetLastError()) != NO_ERROR) )
+ OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize));
+
+#if SQLITE_OS_WINRT
{
- pFile->lastErrno = error;
- return winLogError(SQLITE_IOERR_FSTAT, "winFileSize", pFile->zPath);
+ FILE_STANDARD_INFO info;
+ if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo,
+ &info, sizeof(info)) ){
+ *pSize = info.EndOfFile.QuadPart;
+ }else{
+ pFile->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
+ "winFileSize", pFile->zPath);
+ }
}
- *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
- return SQLITE_OK;
+#else
+ {
+ DWORD upperBits;
+ DWORD lowerBits;
+ DWORD lastErrno;
+
+ lowerBits = osGetFileSize(pFile->h, &upperBits);
+ *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits;
+ if( (lowerBits == INVALID_FILE_SIZE)
+ && ((lastErrno = osGetLastError())!=NO_ERROR) ){
+ pFile->lastErrno = lastErrno;
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno,
+ "winFileSize", pFile->zPath);
+ }
+ }
+#endif
+ OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n",
+ pFile->h, pSize, *pSize, sqlite3ErrName(rc)));
+ return rc;
}
/*
@@ -32890,55 +40234,88 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
# define LOCKFILE_FAIL_IMMEDIATELY 1
#endif
+#ifndef LOCKFILE_EXCLUSIVE_LOCK
+# define LOCKFILE_EXCLUSIVE_LOCK 2
+#endif
+
+/*
+** Historically, SQLite has used both the LockFile and LockFileEx functions.
+** When the LockFile function was used, it was always expected to fail
+** immediately if the lock could not be obtained. Also, it always expected to
+** obtain an exclusive lock. These flags are used with the LockFileEx function
+** and reflect those expectations; therefore, they should not be changed.
+*/
+#ifndef SQLITE_LOCKFILE_FLAGS
+# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \
+ LOCKFILE_EXCLUSIVE_LOCK)
+#endif
+
+/*
+** Currently, SQLite never calls the LockFileEx function without wanting the
+** call to fail immediately if the lock cannot be obtained.
+*/
+#ifndef SQLITE_LOCKFILEEX_FLAGS
+# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY)
+#endif
+
/*
** Acquire a reader lock.
** Different API routines are called depending on whether or not this
-** is Win95 or WinNT.
+** is Win9x or WinNT.
*/
-static int getReadLock(winFile *pFile){
+static int winGetReadLock(winFile *pFile){
int res;
- if( isNT() ){
- OVERLAPPED ovlp;
- ovlp.Offset = SHARED_FIRST;
- ovlp.OffsetHigh = 0;
- ovlp.hEvent = 0;
- res = LockFileEx(pFile->h, LOCKFILE_FAIL_IMMEDIATELY,
- 0, SHARED_SIZE, 0, &ovlp);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
+ if( osIsNT() ){
+#if SQLITE_OS_WINCE
+ /*
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32
+ ** API LockFileEx.
+ */
+ res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
+#else
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
+ SHARED_SIZE, 0);
+#endif
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
int lk;
sqlite3_randomness(sizeof(lk), &lk);
pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
- res = LockFile(pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
-#endif
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
+ SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
+#endif
if( res == 0 ){
- pFile->lastErrno = GetLastError();
+ pFile->lastErrno = osGetLastError();
/* No need to log a failure to lock */
}
+ OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
/*
** Undo a readlock
*/
-static int unlockReadLock(winFile *pFile){
+static int winUnlockReadLock(winFile *pFile){
int res;
- if( isNT() ){
- res = UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- res = UnlockFile(pFile->h, SHARED_FIRST + pFile->sharedLockByte, 0, 1, 0);
-#endif
+ DWORD lastErrno;
+ OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
+ if( osIsNT() ){
+ res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
}
- if( res==0 && GetLastError()!=ERROR_NOT_LOCKED ){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_IOERR_UNLOCK, "unlockReadLock", pFile->zPath);
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
+#endif
+ if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno,
+ "winUnlockReadLock", pFile->zPath);
+ }
+ OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res));
return res;
}
@@ -32970,24 +40347,31 @@ static int unlockReadLock(winFile *pFile){
*/
static int winLock(sqlite3_file *id, int locktype){
int rc = SQLITE_OK; /* Return code from subroutines */
- int res = 1; /* Result of a windows lock call */
+ int res = 1; /* Result of a Windows lock call */
int newLocktype; /* Set pFile->locktype to this value before exiting */
int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
winFile *pFile = (winFile*)id;
- DWORD error = NO_ERROR;
+ DWORD lastErrno = NO_ERROR;
assert( id!=0 );
- OSTRACE(("LOCK %d %d was %d(%d)\n",
- pFile->h, locktype, pFile->locktype, pFile->sharedLockByte));
+ OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n",
+ pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
/* If there is already a lock of this type or more restrictive on the
** OsFile, do nothing. Don't use the end_lock: exit path, as
** sqlite3OsEnterMutex() hasn't been called yet.
*/
if( pFile->locktype>=locktype ){
+ OSTRACE(("LOCK-HELD file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
+ /* Do not allow any kind of write-lock on a read-only database
+ */
+ if( (pFile->ctrlFlags & WINFILE_RDONLY)!=0 && locktype>=RESERVED_LOCK ){
+ return SQLITE_IOERR_LOCK;
+ }
+
/* Make sure the locking sequence is correct
*/
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
@@ -32999,21 +40383,33 @@ static int winLock(sqlite3_file *id, int locktype){
** the PENDING_LOCK byte is temporary.
*/
newLocktype = pFile->locktype;
- if( (pFile->locktype==NO_LOCK)
- || ( (locktype==EXCLUSIVE_LOCK)
- && (pFile->locktype==RESERVED_LOCK))
+ if( pFile->locktype==NO_LOCK
+ || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK)
){
int cnt = 3;
- while( cnt-->0 && (res = LockFile(pFile->h, PENDING_BYTE, 0, 1, 0))==0 ){
- /* Try 3 times to get the pending lock. The pending lock might be
- ** held by another reader process who will release it momentarily.
+ while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
+ PENDING_BYTE, 0, 1, 0))==0 ){
+ /* Try 3 times to get the pending lock. This is needed to work
+ ** around problems caused by indexing and/or anti-virus software on
+ ** Windows systems.
+ ** If you are using this code as a model for alternative VFSes, do not
+ ** copy this retry logic. It is a hack intended for Windows only.
*/
- OSTRACE(("could not get a PENDING lock. cnt=%d\n", cnt));
- Sleep(1);
+ lastErrno = osGetLastError();
+ OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
+ pFile->h, cnt, res));
+ if( lastErrno==ERROR_INVALID_HANDLE ){
+ pFile->lastErrno = lastErrno;
+ rc = SQLITE_IOERR_LOCK;
+ OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
+ pFile->h, cnt, sqlite3ErrName(rc)));
+ return rc;
+ }
+ if( cnt ) sqlite3_win32_sleep(1);
}
gotPendingLock = res;
if( !res ){
- error = GetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -33021,11 +40417,11 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==SHARED_LOCK && res ){
assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
+ res = winGetReadLock(pFile);
if( res ){
newLocktype = SHARED_LOCK;
}else{
- error = GetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -33033,11 +40429,11 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==RESERVED_LOCK && res ){
assert( pFile->locktype==SHARED_LOCK );
- res = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0);
if( res ){
newLocktype = RESERVED_LOCK;
}else{
- error = GetLastError();
+ lastErrno = osGetLastError();
}
}
@@ -33052,15 +40448,14 @@ static int winLock(sqlite3_file *id, int locktype){
*/
if( locktype==EXCLUSIVE_LOCK && res ){
assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
- OSTRACE(("unreadlock = %d\n", res));
- res = LockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ res = winUnlockReadLock(pFile);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0,
+ SHARED_SIZE, 0);
if( res ){
newLocktype = EXCLUSIVE_LOCK;
}else{
- error = GetLastError();
- OSTRACE(("error-code = %d\n", error));
- getReadLock(pFile);
+ lastErrno = osGetLastError();
+ winGetReadLock(pFile);
}
}
@@ -33068,7 +40463,7 @@ static int winLock(sqlite3_file *id, int locktype){
** release it now.
*/
if( gotPendingLock && locktype==SHARED_LOCK ){
- UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
}
/* Update the state of the lock has held in the file descriptor then
@@ -33077,12 +40472,14 @@ static int winLock(sqlite3_file *id, int locktype){
if( res ){
rc = SQLITE_OK;
}else{
- OSTRACE(("LOCK FAILED %d trying for %d but got %d\n", pFile->h,
- locktype, newLocktype));
- pFile->lastErrno = error;
+ pFile->lastErrno = lastErrno;
rc = SQLITE_BUSY;
+ OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n",
+ pFile->h, locktype, newLocktype));
}
pFile->locktype = (u8)newLocktype;
+ OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n",
+ pFile->h, pFile->locktype, sqlite3ErrName(rc)));
return rc;
}
@@ -33092,24 +40489,27 @@ static int winLock(sqlite3_file *id, int locktype){
** non-zero, otherwise zero.
*/
static int winCheckReservedLock(sqlite3_file *id, int *pResOut){
- int rc;
+ int res;
winFile *pFile = (winFile*)id;
SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
+ OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut));
assert( id!=0 );
if( pFile->locktype>=RESERVED_LOCK ){
- rc = 1;
- OSTRACE(("TEST WR-LOCK %d %d (local)\n", pFile->h, rc));
+ res = 1;
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res));
}else{
- rc = LockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
- if( rc ){
- UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0);
+ if( res ){
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
- rc = !rc;
- OSTRACE(("TEST WR-LOCK %d %d (remote)\n", pFile->h, rc));
+ res = !res;
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res));
}
- *pResOut = rc;
+ *pResOut = res;
+ OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
+ pFile->h, pResOut, *pResOut));
return SQLITE_OK;
}
@@ -33130,46 +40530,112 @@ static int winUnlock(sqlite3_file *id, int locktype){
int rc = SQLITE_OK;
assert( pFile!=0 );
assert( locktype<=SHARED_LOCK );
- OSTRACE(("UNLOCK %d to %d was %d(%d)\n", pFile->h, locktype,
- pFile->locktype, pFile->sharedLockByte));
+ OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n",
+ pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
type = pFile->locktype;
if( type>=EXCLUSIVE_LOCK ){
- UnlockFile(pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
- if( locktype==SHARED_LOCK && !getReadLock(pFile) ){
+ winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
/* This should never happen. We should always be able to
** reacquire the read lock */
- rc = winLogError(SQLITE_IOERR_UNLOCK, "winUnlock", pFile->zPath);
+ rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
+ "winUnlock", pFile->zPath);
}
}
if( type>=RESERVED_LOCK ){
- UnlockFile(pFile->h, RESERVED_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
}
if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- unlockReadLock(pFile);
+ winUnlockReadLock(pFile);
}
if( type>=PENDING_LOCK ){
- UnlockFile(pFile->h, PENDING_BYTE, 0, 1, 0);
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0);
}
pFile->locktype = (u8)locktype;
+ OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n",
+ pFile->h, pFile->locktype, sqlite3ErrName(rc)));
return rc;
}
+/******************************************************************************
+****************************** No-op Locking **********************************
+**
+** Of the various locking implementations available, this is by far the
+** simplest: locking is ignored. No attempt is made to lock the database
+** file for reading or writing.
+**
+** This locking mode is appropriate for use on read-only databases
+** (ex: databases that are burned into CD-ROM, for example.) It can
+** also be used if the application employs some external mechanism to
+** prevent simultaneous access of the same database by two or more
+** database connections. But there is a serious risk of database
+** corruption if this locking mode is used in situations where multiple
+** database connections are accessing the same database file at the same
+** time and one or more of those connections are writing.
+*/
+
+static int winNolockLock(sqlite3_file *id, int locktype){
+ UNUSED_PARAMETER(id);
+ UNUSED_PARAMETER(locktype);
+ return SQLITE_OK;
+}
+
+static int winNolockCheckReservedLock(sqlite3_file *id, int *pResOut){
+ UNUSED_PARAMETER(id);
+ UNUSED_PARAMETER(pResOut);
+ return SQLITE_OK;
+}
+
+static int winNolockUnlock(sqlite3_file *id, int locktype){
+ UNUSED_PARAMETER(id);
+ UNUSED_PARAMETER(locktype);
+ return SQLITE_OK;
+}
+
+/******************* End of the no-op lock implementation *********************
+******************************************************************************/
+
+/*
+** If *pArg is initially negative then this is a query. Set *pArg to
+** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set.
+**
+** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags.
+*/
+static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){
+ if( *pArg<0 ){
+ *pArg = (pFile->ctrlFlags & mask)!=0;
+ }else if( (*pArg)==0 ){
+ pFile->ctrlFlags &= ~mask;
+ }else{
+ pFile->ctrlFlags |= mask;
+ }
+}
+
+/* Forward references to VFS helper methods used for temporary files */
+static int winGetTempname(sqlite3_vfs *, char **);
+static int winIsDir(const void *);
+static BOOL winIsDriveLetterAndColon(const char *);
+
/*
** Control and query of the open file handle.
*/
static int winFileControl(sqlite3_file *id, int op, void *pArg){
winFile *pFile = (winFile*)id;
+ OSTRACE(("FCNTL file=%p, op=%d, pArg=%p\n", pFile->h, op, pArg));
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = pFile->locktype;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
- case SQLITE_LAST_ERRNO: {
+ case SQLITE_FCNTL_LAST_ERRNO: {
*(int*)pArg = (int)pFile->lastErrno;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_CHUNK_SIZE: {
pFile->szChunk = *(int *)pArg;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_SIZE_HINT: {
@@ -33184,37 +40650,89 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
SimulateIOErrorBenign(0);
}
}
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
return rc;
}
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_PERSIST_WAL: {
- int bPersist = *(int*)pArg;
- if( bPersist<0 ){
- *(int*)pArg = pFile->bPersistWal;
- }else{
- pFile->bPersistWal = bPersist!=0;
- }
+ winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg);
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
- case SQLITE_FCNTL_SYNC_OMITTED: {
+ case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
+ winModeBit(pFile, WINFILE_PSOW, (int*)pArg);
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_VFSNAME: {
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName);
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
return SQLITE_OK;
}
case SQLITE_FCNTL_WIN32_AV_RETRY: {
int *a = (int*)pArg;
if( a[0]>0 ){
- win32IoerrRetry = a[0];
+ winIoerrRetry = a[0];
}else{
- a[0] = win32IoerrRetry;
+ a[0] = winIoerrRetry;
}
if( a[1]>0 ){
- win32IoerrRetryDelay = a[1];
+ winIoerrRetryDelay = a[1];
}else{
- a[1] = win32IoerrRetryDelay;
+ a[1] = winIoerrRetryDelay;
}
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_WIN32_GET_HANDLE: {
+ LPHANDLE phFile = (LPHANDLE)pArg;
+ *phFile = pFile->h;
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h));
+ return SQLITE_OK;
+ }
+#ifdef SQLITE_TEST
+ case SQLITE_FCNTL_WIN32_SET_HANDLE: {
+ LPHANDLE phFile = (LPHANDLE)pArg;
+ HANDLE hOldFile = pFile->h;
+ pFile->h = *phFile;
+ *phFile = hOldFile;
+ OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
+ hOldFile, pFile->h));
return SQLITE_OK;
}
+#endif
+ case SQLITE_FCNTL_TEMPFILENAME: {
+ char *zTFile = 0;
+ int rc = winGetTempname(pFile->pVfs, &zTFile);
+ if( rc==SQLITE_OK ){
+ *(char**)pArg = zTFile;
+ }
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
+ }
+#if SQLITE_MAX_MMAP_SIZE>0
+ case SQLITE_FCNTL_MMAP_SIZE: {
+ i64 newLimit = *(i64*)pArg;
+ int rc = SQLITE_OK;
+ if( newLimit>sqlite3GlobalConfig.mxMmap ){
+ newLimit = sqlite3GlobalConfig.mxMmap;
+ }
+ *(i64*)pArg = pFile->mmapSizeMax;
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){
+ pFile->mmapSizeMax = newLimit;
+ if( pFile->mmapSize>0 ){
+ winUnmapfile(pFile);
+ rc = winMapfile(pFile, -1);
+ }
+ }
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
+ return rc;
+ }
+#endif
}
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
return SQLITE_NOTFOUND;
}
@@ -33229,35 +40747,36 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
** same for both.
*/
static int winSectorSize(sqlite3_file *id){
- assert( id!=0 );
- return (int)(((winFile*)id)->sectorSize);
+ (void)id;
+ return SQLITE_DEFAULT_SECTOR_SIZE;
}
/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
+ winFile *p = (winFile*)id;
+ return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
+ ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
}
-#ifndef SQLITE_OMIT_WAL
-
-/*
+/*
** Windows will only let you create file view mappings
** on allocation size granularity boundaries.
** During sqlite3_os_init() we do a GetSystemInfo()
** to get the granularity size.
*/
-SYSTEM_INFO winSysInfo;
+static SYSTEM_INFO winSysInfo;
+
+#ifndef SQLITE_OMIT_WAL
/*
** Helper functions to obtain and relinquish the global mutex. The
-** global mutex is used to protect the winLockInfo objects used by
+** global mutex is used to protect the winLockInfo objects used by
** this file, all of which may be shared by multiple threads.
**
-** Function winShmMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
+** Function winShmMutexHeld() is used to assert() that the global mutex
+** is held when required. This function is only used as part of assert()
** statements. e.g.
**
** winShmEnterMutex()
@@ -33265,14 +40784,14 @@ SYSTEM_INFO winSysInfo;
** winShmLeaveMutex()
*/
static void winShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
static void winShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
-#ifdef SQLITE_DEBUG
+#ifndef NDEBUG
static int winShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1));
}
#endif
@@ -33287,10 +40806,10 @@ static int winShmMutexHeld(void) {
** this object or while reading or writing the following fields:
**
** nRef
-** pNext
+** pNext
**
** The following fields are read-only after the object is created:
-**
+**
** fid
** zFilename
**
@@ -33315,7 +40834,7 @@ struct winShmNode {
int nRef; /* Number of winShm objects pointing to this */
winShm *pFirst; /* All winShm objects pointing to this */
winShmNode *pNext; /* Next in list of all winShmNode objects */
-#ifdef SQLITE_DEBUG
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 nextShmId; /* Next available winShm.id value */
#endif
};
@@ -33346,7 +40865,7 @@ struct winShm {
u8 hasMutex; /* True if holding the winShmNode mutex */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 id; /* Id of this connection with its winShmNode */
#endif
};
@@ -33360,48 +40879,43 @@ struct winShm {
/*
** Apply advisory locks for all n bytes beginning at ofst.
*/
-#define _SHM_UNLCK 1
-#define _SHM_RDLCK 2
-#define _SHM_WRLCK 3
+#define WINSHM_UNLCK 1
+#define WINSHM_RDLCK 2
+#define WINSHM_WRLCK 3
static int winShmSystemLock(
winShmNode *pFile, /* Apply locks to this open shared-memory segment */
- int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */
+ int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
int ofst, /* Offset to first byte to be locked/unlocked */
int nByte /* Number of bytes to lock or unlock */
){
- OVERLAPPED ovlp;
- DWORD dwFlags;
int rc = 0; /* Result code form Lock/UnlockFileEx() */
/* Access to the winShmNode object is serialized by the caller */
assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
- /* Initialize the locking parameters */
- dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
- if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
-
- memset(&ovlp, 0, sizeof(OVERLAPPED));
- ovlp.Offset = ofst;
+ OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
+ pFile->hFile.h, lockType, ofst, nByte));
/* Release/Acquire the system-level lock */
- if( lockType==_SHM_UNLCK ){
- rc = UnlockFileEx(pFile->hFile.h, 0, nByte, 0, &ovlp);
+ if( lockType==WINSHM_UNLCK ){
+ rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
}else{
- rc = LockFileEx(pFile->hFile.h, dwFlags, 0, nByte, 0, &ovlp);
+ /* Initialize the locking parameters */
+ DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
+ if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
+ rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
}
-
+
if( rc!= 0 ){
rc = SQLITE_OK;
}else{
- pFile->lastErrno = GetLastError();
+ pFile->lastErrno = osGetLastError();
rc = SQLITE_BUSY;
}
- OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",
- pFile->hFile.h,
- rc==SQLITE_OK ? "ok" : "failed",
- lockType==_SHM_UNLCK ? "UnlockFileEx" : "LockFileEx",
- pFile->lastErrno));
+ OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
+ pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
+ "winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));
return rc;
}
@@ -33419,31 +40933,34 @@ static int winDelete(sqlite3_vfs *,const char*,int);
static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
winShmNode **pp;
winShmNode *p;
- BOOL bRc;
assert( winShmMutexHeld() );
+ OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
+ osGetCurrentProcessId(), deleteFlag));
pp = &winShmNodeList;
while( (p = *pp)!=0 ){
if( p->nRef==0 ){
int i;
- if( p->mutex ) sqlite3_mutex_free(p->mutex);
+ if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
for(i=0; i<p->nRegion; i++){
- bRc = UnmapViewOfFile(p->aRegion[i].pMap);
- OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
- (int)GetCurrentProcessId(), i,
- bRc ? "ok" : "failed"));
- bRc = CloseHandle(p->aRegion[i].hMap);
- OSTRACE(("SHM-PURGE pid-%d close region=%d %s\n",
- (int)GetCurrentProcessId(), i,
- bRc ? "ok" : "failed"));
- }
- if( p->hFile.h != INVALID_HANDLE_VALUE ){
+ BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
+ OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
+ bRc = osCloseHandle(p->aRegion[i].hMap);
+ OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
+ UNUSED_VARIABLE_VALUE(bRc);
+ }
+ if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
SimulateIOErrorBenign(1);
winClose((sqlite3_file *)&p->hFile);
SimulateIOErrorBenign(0);
}
if( deleteFlag ){
SimulateIOErrorBenign(1);
+ sqlite3BeginBenignMalloc();
winDelete(pVfs, p->zFilename, 0);
+ sqlite3EndBenignMalloc();
SimulateIOErrorBenign(0);
}
*pp = p->pNext;
@@ -33474,19 +40991,17 @@ static int winOpenSharedMemory(winFile *pDbFd){
/* Allocate space for the new sqlite3_shm object. Also speculatively
** allocate space for a new winShmNode and filename.
*/
- p = sqlite3_malloc( sizeof(*p) );
- if( p==0 ) return SQLITE_NOMEM;
- memset(p, 0, sizeof(*p));
+ p = sqlite3MallocZero( sizeof(*p) );
+ if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
nName = sqlite3Strlen30(pDbFd->zPath);
- pNew = sqlite3_malloc( sizeof(*pShmNode) + nName + 15 );
+ pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 );
if( pNew==0 ){
sqlite3_free(p);
- return SQLITE_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
- memset(pNew, 0, sizeof(*pNew));
pNew->zFilename = (char*)&pNew[1];
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
- sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
+ sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
/* Look to see if there is an existing winShmNode that can be used.
** If no matching winShmNode currently exists, create a new one.
@@ -33507,41 +41022,43 @@ static int winOpenSharedMemory(winFile *pDbFd){
pShmNode->pNext = winShmNodeList;
winShmNodeList = pShmNode;
- pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->mutex==0 ){
- rc = SQLITE_NOMEM;
- goto shm_open_err;
+ if( sqlite3GlobalConfig.bCoreMutex ){
+ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ if( pShmNode->mutex==0 ){
+ rc = SQLITE_IOERR_NOMEM_BKPT;
+ goto shm_open_err;
+ }
}
rc = winOpen(pDbFd->pVfs,
pShmNode->zFilename, /* Name of the file (UTF-8) */
(sqlite3_file*)&pShmNode->hFile, /* File handle here */
- SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, /* Mode flags */
+ SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
0);
if( SQLITE_OK!=rc ){
- rc = SQLITE_CANTOPEN_BKPT;
goto shm_open_err;
}
/* Check to see if another process is holding the dead-man switch.
- ** If not, truncate the file to zero length.
+ ** If not, truncate the file to zero length.
*/
- if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
+ if( winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMOPEN, "winOpenShm", pDbFd->zPath);
+ rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
+ "winOpenShm", pDbFd->zPath);
}
}
if( rc==SQLITE_OK ){
- winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1);
- rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1);
+ winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
+ rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
}
if( rc ) goto shm_open_err;
}
/* Make the new connection a child of the winShmNode */
p->pShmNode = pShmNode;
-#ifdef SQLITE_DEBUG
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
p->id = pShmNode->nextShmId++;
#endif
pShmNode->nRef++;
@@ -33552,7 +41069,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
** the cover of the winShmEnterMutex() mutex and the pointer from the
** new (struct winShm) object to the pShmNode has been set. All that is
** left to do is to link the new object into the linked list starting
- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
+ ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
** mutex.
*/
sqlite3_mutex_enter(pShmNode->mutex);
@@ -33563,7 +41080,7 @@ static int winOpenSharedMemory(winFile *pDbFd){
/* Jump here on any error */
shm_open_err:
- winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1);
+ winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */
sqlite3_free(p);
sqlite3_free(pNew);
@@ -33572,7 +41089,7 @@ shm_open_err:
}
/*
-** Close a connection to shared-memory. Delete the underlying
+** Close a connection to shared-memory. Delete the underlying
** storage if deleteFlag is true.
*/
static int winShmUnmap(
@@ -33652,7 +41169,7 @@ static int winShmLock(
/* Unlock the system-level locks */
if( (mask & allMask)==0 ){
- rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n);
+ rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
}else{
rc = SQLITE_OK;
}
@@ -33661,7 +41178,7 @@ static int winShmLock(
if( rc==SQLITE_OK ){
p->exclMask &= ~mask;
p->sharedMask &= ~mask;
- }
+ }
}else if( flags & SQLITE_SHM_SHARED ){
u16 allShared = 0; /* Union of locks held by connections other than "p" */
@@ -33680,7 +41197,7 @@ static int winShmLock(
/* Get shared locks at the system level, if necessary */
if( rc==SQLITE_OK ){
if( (allShared & mask)==0 ){
- rc = winShmSystemLock(pShmNode, _SHM_RDLCK, ofst+WIN_SHM_BASE, n);
+ rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
}else{
rc = SQLITE_OK;
}
@@ -33700,12 +41217,12 @@ static int winShmLock(
break;
}
}
-
+
/* Get the exclusive locks at the system level. Then if successful
** also mark the local connection as being locked.
*/
if( rc==SQLITE_OK ){
- rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n);
+ rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
if( rc==SQLITE_OK ){
assert( (p->sharedMask & mask)==0 );
p->exclMask |= mask;
@@ -33713,14 +41230,14 @@ static int winShmLock(
}
}
sqlite3_mutex_leave(pShmNode->mutex);
- OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x %s\n",
- p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask,
- rc ? "failed" : "ok"));
+ OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
+ osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
+ sqlite3ErrName(rc)));
return rc;
}
/*
-** Implement a memory barrier or memory fence on shared memory.
+** Implement a memory barrier or memory fence on shared memory.
**
** All loads and stores begun before the barrier must complete before
** any load or store begun after the barrier.
@@ -33729,28 +41246,28 @@ static void winShmBarrier(
sqlite3_file *fd /* Database holding the shared memory */
){
UNUSED_PARAMETER(fd);
- /* MemoryBarrier(); // does not work -- do not know why not */
- winShmEnterMutex();
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */
+ winShmEnterMutex(); /* Also mutex, for redundancy */
winShmLeaveMutex();
}
/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file fd. Shared-memory regions
-** are numbered starting from zero. Each shared-memory region is szRegion
+** This function is called to obtain a pointer to region iRegion of the
+** shared-memory associated with the database file fd. Shared-memory regions
+** are numbered starting from zero. Each shared-memory region is szRegion
** bytes in size.
**
** If an error occurs, an error code is returned and *pp is set to NULL.
**
** Otherwise, if the isWrite parameter is 0 and the requested shared-memory
** region has not been allocated (by any client, including one running in a
-** separate process), then *pp is set to NULL and SQLITE_OK returned. If
-** isWrite is non-zero and the requested shared-memory region has not yet
+** separate process), then *pp is set to NULL and SQLITE_OK returned. If
+** isWrite is non-zero and the requested shared-memory region has not yet
** been allocated, it is allocated by this function.
**
** If the shared-memory region has already been allocated or is allocated by
-** this call as described above, then it is mapped into this processes
-** address space (if it is not already), *pp is set to point to the mapped
+** this call as described above, then it is mapped into this processes
+** address space (if it is not already), *pp is set to point to the mapped
** memory and SQLITE_OK returned.
*/
static int winShmMap(
@@ -33761,16 +41278,16 @@ static int winShmMap(
void volatile **pp /* OUT: Mapped memory */
){
winFile *pDbFd = (winFile*)fd;
- winShm *p = pDbFd->pShm;
+ winShm *pShm = pDbFd->pShm;
winShmNode *pShmNode;
int rc = SQLITE_OK;
- if( !p ){
+ if( !pShm ){
rc = winOpenSharedMemory(pDbFd);
if( rc!=SQLITE_OK ) return rc;
- p = pDbFd->pShm;
+ pShm = pDbFd->pShm;
}
- pShmNode = p->pShmNode;
+ pShmNode = pShm->pShmNode;
sqlite3_mutex_enter(pShmNode->mutex);
assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
@@ -33788,7 +41305,8 @@ static int winShmMap(
*/
rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap1", pDbFd->zPath);
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
+ "winShmMap1", pDbFd->zPath);
goto shmpage_out;
}
@@ -33802,45 +41320,63 @@ static int winShmMap(
if( !isWrite ) goto shmpage_out;
rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, "winShmMap2", pDbFd->zPath);
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
+ "winShmMap2", pDbFd->zPath);
goto shmpage_out;
}
}
/* Map the requested memory region into this processes address space. */
- apNew = (struct ShmRegion *)sqlite3_realloc(
+ apNew = (struct ShmRegion *)sqlite3_realloc64(
pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
);
if( !apNew ){
- rc = SQLITE_IOERR_NOMEM;
+ rc = SQLITE_IOERR_NOMEM_BKPT;
goto shmpage_out;
}
pShmNode->aRegion = apNew;
while( pShmNode->nRegion<=iRegion ){
- HANDLE hMap; /* file-mapping handle */
+ HANDLE hMap = NULL; /* file-mapping handle */
void *pMap = 0; /* Mapped memory region */
-
- hMap = CreateFileMapping(pShmNode->hFile.h,
+
+#if SQLITE_OS_WINRT
+ hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
+ NULL, PAGE_READWRITE, nByte, NULL
+ );
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ hMap = osCreateFileMappingW(pShmNode->hFile.h,
+ NULL, PAGE_READWRITE, 0, nByte, NULL
+ );
+#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
+ hMap = osCreateFileMappingA(pShmNode->hFile.h,
NULL, PAGE_READWRITE, 0, nByte, NULL
);
- OSTRACE(("SHM-MAP pid-%d create region=%d nbyte=%d %s\n",
- (int)GetCurrentProcessId(), pShmNode->nRegion, nByte,
+#endif
+ OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
+ osGetCurrentProcessId(), pShmNode->nRegion, nByte,
hMap ? "ok" : "failed"));
if( hMap ){
int iOffset = pShmNode->nRegion*szRegion;
int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity;
- pMap = MapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
+#if SQLITE_OS_WINRT
+ pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
+ iOffset - iOffsetShift, szRegion + iOffsetShift
+ );
+#else
+ pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ,
0, iOffset - iOffsetShift, szRegion + iOffsetShift
);
- OSTRACE(("SHM-MAP pid-%d map region=%d offset=%d size=%d %s\n",
- (int)GetCurrentProcessId(), pShmNode->nRegion, iOffset, szRegion,
- pMap ? "ok" : "failed"));
+#endif
+ OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n",
+ osGetCurrentProcessId(), pShmNode->nRegion, iOffset,
+ szRegion, pMap ? "ok" : "failed"));
}
if( !pMap ){
- pShmNode->lastErrno = GetLastError();
- rc = winLogError(SQLITE_IOERR_SHMMAP, "winShmMap3", pDbFd->zPath);
- if( hMap ) CloseHandle(hMap);
+ pShmNode->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno,
+ "winShmMap3", pDbFd->zPath);
+ if( hMap ) osCloseHandle(hMap);
goto shmpage_out;
}
@@ -33871,6 +41407,233 @@ shmpage_out:
#endif /* #ifndef SQLITE_OMIT_WAL */
/*
+** Cleans up the mapped region of the specified file, if any.
+*/
+#if SQLITE_MAX_MMAP_SIZE>0
+static int winUnmapfile(winFile *pFile){
+ assert( pFile!=0 );
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, "
+ "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n",
+ osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion,
+ pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax));
+ if( pFile->pMapRegion ){
+ if( !osUnmapViewOfFile(pFile->pMapRegion) ){
+ pFile->lastErrno = osGetLastError();
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, "
+ "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile,
+ pFile->pMapRegion));
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
+ "winUnmapfile1", pFile->zPath);
+ }
+ pFile->pMapRegion = 0;
+ pFile->mmapSize = 0;
+ pFile->mmapSizeActual = 0;
+ }
+ if( pFile->hMap!=NULL ){
+ if( !osCloseHandle(pFile->hMap) ){
+ pFile->lastErrno = osGetLastError();
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n",
+ osGetCurrentProcessId(), pFile, pFile->hMap));
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno,
+ "winUnmapfile2", pFile->zPath);
+ }
+ pFile->hMap = NULL;
+ }
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFile));
+ return SQLITE_OK;
+}
+
+/*
+** Memory map or remap the file opened by file-descriptor pFd (if the file
+** is already mapped, the existing mapping is replaced by the new). Or, if
+** there already exists a mapping for this file, and there are still
+** outstanding xFetch() references to it, this function is a no-op.
+**
+** If parameter nByte is non-negative, then it is the requested size of
+** the mapping to create. Otherwise, if nByte is less than zero, then the
+** requested size is the size of the file on disk. The actual size of the
+** created mapping is either the requested size or the value configured
+** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller.
+**
+** SQLITE_OK is returned if no error occurs (even if the mapping is not
+** recreated as a result of outstanding references) or an SQLite error
+** code otherwise.
+*/
+static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
+ sqlite3_int64 nMap = nByte;
+ int rc;
+
+ assert( nMap>=0 || pFd->nFetchOut==0 );
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n",
+ osGetCurrentProcessId(), pFd, nByte));
+
+ if( pFd->nFetchOut>0 ) return SQLITE_OK;
+
+ if( nMap<0 ){
+ rc = winFileSize((sqlite3_file*)pFd, &nMap);
+ if( rc ){
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n",
+ osGetCurrentProcessId(), pFd));
+ return SQLITE_IOERR_FSTAT;
+ }
+ }
+ if( nMap>pFd->mmapSizeMax ){
+ nMap = pFd->mmapSizeMax;
+ }
+ nMap &= ~(sqlite3_int64)(winSysInfo.dwPageSize - 1);
+
+ if( nMap==0 && pFd->mmapSize>0 ){
+ winUnmapfile(pFd);
+ }
+ if( nMap!=pFd->mmapSize ){
+ void *pNew = 0;
+ DWORD protect = PAGE_READONLY;
+ DWORD flags = FILE_MAP_READ;
+
+ winUnmapfile(pFd);
+#ifdef SQLITE_MMAP_READWRITE
+ if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){
+ protect = PAGE_READWRITE;
+ flags |= FILE_MAP_WRITE;
+ }
+#endif
+#if SQLITE_OS_WINRT
+ pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL);
+#elif defined(SQLITE_WIN32_HAS_WIDE)
+ pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect,
+ (DWORD)((nMap>>32) & 0xffffffff),
+ (DWORD)(nMap & 0xffffffff), NULL);
+#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
+ pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect,
+ (DWORD)((nMap>>32) & 0xffffffff),
+ (DWORD)(nMap & 0xffffffff), NULL);
+#endif
+ if( pFd->hMap==NULL ){
+ pFd->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
+ "winMapfile1", pFd->zPath);
+ /* Log the error, but continue normal operation using xRead/xWrite */
+ OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
+ return SQLITE_OK;
+ }
+ assert( (nMap % winSysInfo.dwPageSize)==0 );
+ assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff );
+#if SQLITE_OS_WINRT
+ pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap);
+#else
+ pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap);
+#endif
+ if( pNew==NULL ){
+ osCloseHandle(pFd->hMap);
+ pFd->hMap = NULL;
+ pFd->lastErrno = osGetLastError();
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno,
+ "winMapfile2", pFd->zPath);
+ /* Log the error, but continue normal operation using xRead/xWrite */
+ OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
+ return SQLITE_OK;
+ }
+ pFd->pMapRegion = pNew;
+ pFd->mmapSize = nMap;
+ pFd->mmapSizeActual = nMap;
+ }
+
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), pFd));
+ return SQLITE_OK;
+}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
+
+/*
+** If possible, return a pointer to a mapping of file fd starting at offset
+** iOff. The mapping must be valid for at least nAmt bytes.
+**
+** If such a pointer can be obtained, store it in *pp and return SQLITE_OK.
+** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK.
+** Finally, if an error does occur, return an SQLite error code. The final
+** value of *pp is undefined in this case.
+**
+** If this function does return a pointer, the caller must eventually
+** release the reference by calling winUnfetch().
+*/
+static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){
+#if SQLITE_MAX_MMAP_SIZE>0
+ winFile *pFd = (winFile*)fd; /* The underlying database file */
+#endif
+ *pp = 0;
+
+ OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
+ osGetCurrentProcessId(), fd, iOff, nAmt, pp));
+
+#if SQLITE_MAX_MMAP_SIZE>0
+ if( pFd->mmapSizeMax>0 ){
+ if( pFd->pMapRegion==0 ){
+ int rc = winMapfile(pFd, -1);
+ if( rc!=SQLITE_OK ){
+ OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
+ return rc;
+ }
+ }
+ if( pFd->mmapSize >= iOff+nAmt ){
+ *pp = &((u8 *)pFd->pMapRegion)[iOff];
+ pFd->nFetchOut++;
+ }
+ }
+#endif
+
+ OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), fd, pp, *pp));
+ return SQLITE_OK;
+}
+
+/*
+** If the third argument is non-NULL, then this function releases a
+** reference obtained by an earlier call to winFetch(). The second
+** argument passed to this function must be the same as the corresponding
+** argument that was passed to the winFetch() invocation.
+**
+** Or, if the third argument is NULL, then this function is being called
+** to inform the VFS layer that, according to POSIX, any existing mapping
+** may now be invalid and should be unmapped.
+*/
+static int winUnfetch(sqlite3_file *fd, i64 iOff, void *p){
+#if SQLITE_MAX_MMAP_SIZE>0
+ winFile *pFd = (winFile*)fd; /* The underlying database file */
+
+ /* If p==0 (unmap the entire file) then there must be no outstanding
+ ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference),
+ ** then there must be at least one outstanding. */
+ assert( (p==0)==(pFd->nFetchOut==0) );
+
+ /* If p!=0, it must match the iOff value. */
+ assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
+
+ OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n",
+ osGetCurrentProcessId(), pFd, iOff, p));
+
+ if( p ){
+ pFd->nFetchOut--;
+ }else{
+ /* FIXME: If Windows truly always prevents truncating or deleting a
+ ** file while a mapping is held, then the following winUnmapfile() call
+ ** is unnecessary can be omitted - potentially improving
+ ** performance. */
+ winUnmapfile(pFd);
+ }
+
+ assert( pFd->nFetchOut>=0 );
+#endif
+
+ OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n",
+ osGetCurrentProcessId(), fd));
+ return SQLITE_OK;
+}
+
+/*
** Here ends the implementation of all sqlite3_file methods.
**
********************** End sqlite3_file Methods *******************************
@@ -33881,7 +41644,7 @@ shmpage_out:
** sqlite3_file for win32.
*/
static const sqlite3_io_methods winIoMethod = {
- 2, /* iVersion */
+ 3, /* iVersion */
winClose, /* xClose */
winRead, /* xRead */
winWrite, /* xWrite */
@@ -33897,7 +41660,47 @@ static const sqlite3_io_methods winIoMethod = {
winShmMap, /* xShmMap */
winShmLock, /* xShmLock */
winShmBarrier, /* xShmBarrier */
- winShmUnmap /* xShmUnmap */
+ winShmUnmap, /* xShmUnmap */
+ winFetch, /* xFetch */
+ winUnfetch /* xUnfetch */
+};
+
+/*
+** This vector defines all the methods that can operate on an
+** sqlite3_file for win32 without performing any locking.
+*/
+static const sqlite3_io_methods winIoNolockMethod = {
+ 3, /* iVersion */
+ winClose, /* xClose */
+ winRead, /* xRead */
+ winWrite, /* xWrite */
+ winTruncate, /* xTruncate */
+ winSync, /* xSync */
+ winFileSize, /* xFileSize */
+ winNolockLock, /* xLock */
+ winNolockUnlock, /* xUnlock */
+ winNolockCheckReservedLock, /* xCheckReservedLock */
+ winFileControl, /* xFileControl */
+ winSectorSize, /* xSectorSize */
+ winDeviceCharacteristics, /* xDeviceCharacteristics */
+ winShmMap, /* xShmMap */
+ winShmLock, /* xShmLock */
+ winShmBarrier, /* xShmBarrier */
+ winShmUnmap, /* xShmUnmap */
+ winFetch, /* xFetch */
+ winUnfetch /* xUnfetch */
+};
+
+static winVfsAppData winAppData = {
+ &winIoMethod, /* pMethod */
+ 0, /* pAppData */
+ 0 /* bNoLock */
+};
+
+static winVfsAppData winNolockAppData = {
+ &winIoNolockMethod, /* pMethod */
+ 0, /* pAppData */
+ 1 /* bNoLock */
};
/****************************************************************************
@@ -33907,111 +41710,339 @@ static const sqlite3_io_methods winIoMethod = {
** sqlite3_vfs object.
*/
+#if defined(__CYGWIN__)
+/*
+** Convert a filename from whatever the underlying operating system
+** supports for filenames into UTF-8. Space to hold the result is
+** obtained from malloc and must be freed by the calling function.
+*/
+static char *winConvertToUtf8Filename(const void *zFilename){
+ char *zConverted = 0;
+ if( osIsNT() ){
+ zConverted = winUnicodeToUtf8(zFilename);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI());
+ }
+#endif
+ /* caller will handle out of memory */
+ return zConverted;
+}
+#endif
+
/*
** Convert a UTF-8 filename into whatever form the underlying
** operating system wants filenames in. Space to hold the result
** is obtained from malloc and must be freed by the calling
** function.
*/
-static void *convertUtf8Filename(const char *zFilename){
+static void *winConvertFromUtf8Filename(const char *zFilename){
void *zConverted = 0;
- if( isNT() ){
- zConverted = utf8ToUnicode(zFilename);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- zConverted = sqlite3_win32_utf8_to_mbcs(zFilename);
-#endif
+ if( osIsNT() ){
+ zConverted = winUtf8ToUnicode(zFilename);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
}
+#endif
/* caller will handle out of memory */
return zConverted;
}
/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
+** This function returns non-zero if the specified UTF-8 string buffer
+** ends with a directory separator character or one was successfully
+** added to it.
+*/
+static int winMakeEndInDirSep(int nBuf, char *zBuf){
+ if( zBuf ){
+ int nLen = sqlite3Strlen30(zBuf);
+ if( nLen>0 ){
+ if( winIsDirSep(zBuf[nLen-1]) ){
+ return 1;
+ }else if( nLen+1<nBuf ){
+ zBuf[nLen] = winGetDirSep();
+ zBuf[nLen+1] = '\0';
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Create a temporary file name and store the resulting pointer into pzBuf.
+** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
-static int getTempname(int nBuf, char *zBuf){
+static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
static char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
- char zTempPath[MAX_PATH+1];
+ int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX);
+ int nMax, nBuf, nDir, nLen;
+ char *zBuf;
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
- ** function failing.
+ ** function failing.
*/
SimulateIOError( return SQLITE_IOERR );
+ /* Allocate a temporary buffer to store the fully qualified file
+ ** name for the temporary file. If this fails, we cannot continue.
+ */
+ nMax = pVfs->mxPathname; nBuf = nMax + 2;
+ zBuf = sqlite3MallocZero( nBuf );
+ if( !zBuf ){
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+
+ /* Figure out the effective temporary directory. First, check if one
+ ** has been explicitly set by the application; otherwise, use the one
+ ** configured by the operating system.
+ */
+ nDir = nMax - (nPre + 15);
+ assert( nDir>0 );
if( sqlite3_temp_directory ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", sqlite3_temp_directory);
- }else if( isNT() ){
+ int nDirLen = sqlite3Strlen30(sqlite3_temp_directory);
+ if( nDirLen>0 ){
+ if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){
+ nDirLen++;
+ }
+ if( nDirLen>nDir ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0);
+ }
+ sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory);
+ }
+ }
+#if defined(__CYGWIN__)
+ else{
+ static const char *azDirs[] = {
+ 0, /* getenv("SQLITE_TMPDIR") */
+ 0, /* getenv("TMPDIR") */
+ 0, /* getenv("TMP") */
+ 0, /* getenv("TEMP") */
+ 0, /* getenv("USERPROFILE") */
+ "/var/tmp",
+ "/usr/tmp",
+ "/tmp",
+ ".",
+ 0 /* List terminator */
+ };
+ unsigned int i;
+ const char *zDir = 0;
+
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = getenv("TMP");
+ if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
+ if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
+ void *zConverted;
+ if( zDir==0 ) continue;
+ /* If the path starts with a drive letter followed by the colon
+ ** character, assume it is already a native Win32 path; otherwise,
+ ** it must be converted to a native Win32 path via the Cygwin API
+ ** prior to using it.
+ */
+ if( winIsDriveLetterAndColon(zDir) ){
+ zConverted = winConvertFromUtf8Filename(zDir);
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ if( winIsDir(zConverted) ){
+ sqlite3_snprintf(nMax, zBuf, "%s", zDir);
+ sqlite3_free(zConverted);
+ break;
+ }
+ sqlite3_free(zConverted);
+ }else{
+ zConverted = sqlite3MallocZero( nMax+1 );
+ if( !zConverted ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ if( cygwin_conv_path(
+ osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
+ zConverted, nMax+1)<0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
+ return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
+ "winGetTempname2", zDir);
+ }
+ if( winIsDir(zConverted) ){
+ /* At this point, we know the candidate directory exists and should
+ ** be used. However, we may need to convert the string containing
+ ** its name into UTF-8 (i.e. if it is UTF-16 right now).
+ */
+ char *zUtf8 = winConvertToUtf8Filename(zConverted);
+ if( !zUtf8 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zConverted);
+ break;
+ }
+ sqlite3_free(zConverted);
+ }
+ }
+ }
+#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+ else if( osIsNT() ){
char *zMulti;
- WCHAR zWidePath[MAX_PATH];
- GetTempPathW(MAX_PATH-30, zWidePath);
- zMulti = unicodeToUtf8(zWidePath);
+ LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
+ if( !zWidePath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ if( osGetTempPathW(nMax, zWidePath)==0 ){
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname2", 0);
+ }
+ zMulti = winUnicodeToUtf8(zWidePath);
if( zMulti ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zMulti);
- free(zMulti);
+ sqlite3_snprintf(nMax, zBuf, "%s", zMulti);
+ sqlite3_free(zMulti);
+ sqlite3_free(zWidePath);
}else{
- return SQLITE_NOMEM;
+ sqlite3_free(zWidePath);
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zUtf8;
- char zMbcsPath[MAX_PATH];
- GetTempPathA(MAX_PATH-30, zMbcsPath);
- zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
+ char *zMbcsPath = sqlite3MallocZero( nMax );
+ if( !zMbcsPath ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ if( osGetTempPathA(nMax, zMbcsPath)==0 ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
+ "winGetTempname3", 0);
+ }
+ zUtf8 = winMbcsToUtf8(zMbcsPath, osAreFileApisANSI());
if( zUtf8 ){
- sqlite3_snprintf(MAX_PATH-30, zTempPath, "%s", zUtf8);
- free(zUtf8);
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
+ sqlite3_free(zUtf8);
}else{
- return SQLITE_NOMEM;
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
+ return SQLITE_IOERR_NOMEM_BKPT;
}
-#endif
}
+#endif /* SQLITE_WIN32_HAS_ANSI */
+#endif /* !SQLITE_OS_WINRT */
- /* Check that the output buffer is large enough for the temporary file
- ** name. If it is not, return SQLITE_ERROR.
+ /*
+ ** Check to make sure the temporary directory ends with an appropriate
+ ** separator. If it does not and there is not enough space left to add
+ ** one, fail.
*/
- if( (sqlite3Strlen30(zTempPath) + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 17) >= nBuf ){
- return SQLITE_ERROR;
+ if( !winMakeEndInDirSep(nDir+1, zBuf) ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname4", 0);
+ }
+
+ /*
+ ** Check that the output buffer is large enough for the temporary file
+ ** name in the following format:
+ **
+ ** "<temporary_directory>/etilqs_XXXXXXXXXXXXXXX\0\0"
+ **
+ ** If not, return SQLITE_ERROR. The number 17 is used here in order to
+ ** account for the space used by the 15 character random suffix and the
+ ** two trailing NUL characters. The final directory separator character
+ ** has already added if it was not already present.
+ */
+ nLen = sqlite3Strlen30(zBuf);
+ if( (nLen + nPre + 17) > nBuf ){
+ sqlite3_free(zBuf);
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0);
}
- for(i=sqlite3Strlen30(zTempPath); i>0 && zTempPath[i-1]=='\\'; i--){}
- zTempPath[i] = 0;
+ sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
- sqlite3_snprintf(nBuf-17, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
j = sqlite3Strlen30(zBuf);
sqlite3_randomness(15, &zBuf[j]);
for(i=0; i<15; i++, j++){
zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
}
zBuf[j] = 0;
+ zBuf[j+1] = 0;
+ *pzBuf = zBuf;
- OSTRACE(("TEMP FILENAME: %s\n", zBuf));
- return SQLITE_OK;
+ OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
+ return SQLITE_OK;
+}
+
+/*
+** Return TRUE if the named file is really a directory. Return false if
+** it is something other than a directory, or if there is any kind of memory
+** allocation failure.
+*/
+static int winIsDir(const void *zConverted){
+ DWORD attr;
+ int rc = 0;
+ DWORD lastErrno;
+
+ if( osIsNT() ){
+ int cnt = 0;
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData;
+ memset(&sAttrData, 0, sizeof(sAttrData));
+ while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
+ GetFileExInfoStandard,
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
+ if( !rc ){
+ return 0; /* Invalid name? */
+ }
+ attr = sAttrData.dwFileAttributes;
+#if SQLITE_OS_WINCE==0
+ }else{
+ attr = osGetFileAttributesA((char*)zConverted);
+#endif
+ }
+ return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
}
/*
** Open a file.
*/
static int winOpen(
- sqlite3_vfs *pVfs, /* Not used */
+ sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */
const char *zName, /* Name of the file (UTF-8) */
sqlite3_file *id, /* Write the SQLite file handle here */
int flags, /* Open mode flags */
int *pOutFlags /* Status return flags */
){
HANDLE h;
+ DWORD lastErrno = 0;
DWORD dwDesiredAccess;
DWORD dwShareMode;
DWORD dwCreationDisposition;
@@ -34019,6 +42050,7 @@ static int winOpen(
#if SQLITE_OS_WINCE
int isTemp = 0;
#endif
+ winVfsAppData *pAppData;
winFile *pFile = (winFile*)id;
void *zConverted; /* Filename in OS encoding */
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
@@ -34027,7 +42059,7 @@ static int winOpen(
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
*/
- char zTmpname[MAX_PATH+1]; /* Buffer used to create temp filename */
+ char *zTmpname = 0; /* For temporary filename, if necessary. */
int rc = SQLITE_OK; /* Function Return Code */
#if !defined(NDEBUG) || SQLITE_OS_WINCE
@@ -34037,22 +42069,23 @@ static int winOpen(
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
int isCreate = (flags & SQLITE_OPEN_CREATE);
-#ifndef NDEBUG
int isReadonly = (flags & SQLITE_OPEN_READONLY);
-#endif
int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
#ifndef NDEBUG
int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
+ eType==SQLITE_OPEN_MASTER_JOURNAL
+ || eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_WAL
));
#endif
- /* Check the following statements are true:
+ OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n",
+ zUtf8Name, id, flags, pOutFlags));
+
+ /* Check the following statements are true:
**
- ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
+ ** (a) Exactly one of the READWRITE and READONLY flags must be set, and
** (b) if CREATE is set, then READWRITE must also be set, and
** (c) if EXCLUSIVE is set, then CREATE must also be set.
** (d) if DELETEONCLOSE is set, then CREATE must also be set.
@@ -34062,7 +42095,7 @@ static int winOpen(
assert(isExclusive==0 || isCreate);
assert(isDelete==0 || isCreate);
- /* The main DB, main journal, WAL file and master journal are never
+ /* The main DB, main journal, WAL file and master journal are never
** automatically deleted. Nor are they ever temporary files. */
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
@@ -34070,33 +42103,56 @@ static int winOpen(
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL );
/* Assert that the upper layer has set one of the "file-type" flags. */
- assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
- || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
- || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
+ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
+ || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
+ || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
- assert( id!=0 );
- UNUSED_PARAMETER(pVfs);
-
+ assert( pFile!=0 );
+ memset(pFile, 0, sizeof(winFile));
pFile->h = INVALID_HANDLE_VALUE;
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
+#if SQLITE_OS_WINRT
+ if( !zUtf8Name && !sqlite3_temp_directory ){
+ sqlite3_log(SQLITE_ERROR,
+ "sqlite3_temp_directory variable should be set for WinRT");
+ }
+#endif
+
+ /* If the second argument to this function is NULL, generate a
+ ** temporary file name to use
*/
if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- rc = getTempname(MAX_PATH+1, zTmpname);
+ assert( isDelete && !isOpenJournal );
+ rc = winGetTempname(pVfs, &zTmpname);
if( rc!=SQLITE_OK ){
+ OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc)));
return rc;
}
zUtf8Name = zTmpname;
}
+ /* Database filenames are double-zero terminated if they are not
+ ** URIs with parameters. Hence, they can always be passed into
+ ** sqlite3_uri_parameter().
+ */
+ assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) ||
+ zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 );
+
/* Convert the filename to the system encoding. */
- zConverted = convertUtf8Filename(zUtf8Name);
+ zConverted = winConvertFromUtf8Filename(zUtf8Name);
if( zConverted==0 ){
- return SQLITE_NOMEM;
+ sqlite3_free(zTmpname);
+ OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name));
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+
+ if( winIsDir(zConverted) ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
+ OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
+ return SQLITE_CANTOPEN_ISDIR;
}
if( isReadWrite ){
@@ -34105,8 +42161,8 @@ static int winOpen(
dwDesiredAccess = GENERIC_READ;
}
- /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
- ** created. SQLite doesn't use it to indicate "exclusive access"
+ /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is
+ ** created. SQLite doesn't use it to indicate "exclusive access"
** as it is usually understood.
*/
if( isExclusive ){
@@ -34141,43 +42197,64 @@ static int winOpen(
dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS;
#endif
- if( isNT() ){
- while( (h = CreateFileW((WCHAR*)zConverted,
- dwDesiredAccess,
- dwShareMode, NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt) ){}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- while( (h = CreateFileA((char*)zConverted,
- dwDesiredAccess,
- dwShareMode, NULL,
- dwCreationDisposition,
- dwFlagsAndAttributes,
- NULL))==INVALID_HANDLE_VALUE &&
- retryIoerr(&cnt) ){}
+ if( osIsNT() ){
+#if SQLITE_OS_WINRT
+ CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
+ extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS);
+ extendedParameters.dwFileAttributes =
+ dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK;
+ extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK;
+ extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
+ extendedParameters.lpSecurityAttributes = NULL;
+ extendedParameters.hTemplateFile = NULL;
+ while( (h = osCreateFile2((LPCWSTR)zConverted,
+ dwDesiredAccess,
+ dwShareMode,
+ dwCreationDisposition,
+ &extendedParameters))==INVALID_HANDLE_VALUE &&
+ winRetryIoerr(&cnt, &lastErrno) ){
+ /* Noop */
+ }
+#else
+ while( (h = osCreateFileW((LPCWSTR)zConverted,
+ dwDesiredAccess,
+ dwShareMode, NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL))==INVALID_HANDLE_VALUE &&
+ winRetryIoerr(&cnt, &lastErrno) ){
+ /* Noop */
+ }
#endif
}
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ while( (h = osCreateFileA((LPCSTR)zConverted,
+ dwDesiredAccess,
+ dwShareMode, NULL,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL))==INVALID_HANDLE_VALUE &&
+ winRetryIoerr(&cnt, &lastErrno) ){
+ /* Noop */
+ }
+ }
+#endif
+ winLogIoerr(cnt, __LINE__);
- logIoerr(cnt);
-
- OSTRACE(("OPEN %d %s 0x%lx %s\n",
- h, zName, dwDesiredAccess,
- h==INVALID_HANDLE_VALUE ? "failed" : "ok"));
+ OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name,
+ dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
if( h==INVALID_HANDLE_VALUE ){
- pFile->lastErrno = GetLastError();
- winLogError(SQLITE_CANTOPEN, "winOpen", zUtf8Name);
- free(zConverted);
- if( isReadWrite ){
- return winOpen(pVfs, zName, id,
- ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), pOutFlags);
+ pFile->lastErrno = lastErrno;
+ winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name);
+ sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
+ if( isReadWrite && !isExclusive ){
+ return winOpen(pVfs, zName, id,
+ ((flags|SQLITE_OPEN_READONLY) &
+ ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
+ pOutFlags);
}else{
return SQLITE_CANTOPEN_BKPT;
}
@@ -34191,30 +42268,52 @@ static int winOpen(
}
}
- memset(pFile, 0, sizeof(*pFile));
- pFile->pMethod = &winIoMethod;
- pFile->h = h;
- pFile->lastErrno = NO_ERROR;
- pFile->pVfs = pVfs;
- pFile->pShm = 0;
- pFile->zPath = zName;
- pFile->sectorSize = getSectorSize(pVfs, zUtf8Name);
+ OSTRACE(("OPEN file=%p, name=%s, access=%lx, pOutFlags=%p, *pOutFlags=%d, "
+ "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ?
+ *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
+
+ pAppData = (winVfsAppData*)pVfs->pAppData;
#if SQLITE_OS_WINCE
- if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
- && !winceCreateLock(zName, pFile)
- ){
- CloseHandle(h);
- free(zConverted);
- return SQLITE_CANTOPEN_BKPT;
+ {
+ if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB
+ && ((pAppData==NULL) || !pAppData->bNoLock)
+ && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK
+ ){
+ osCloseHandle(h);
+ sqlite3_free(zConverted);
+ sqlite3_free(zTmpname);
+ OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc)));
+ return rc;
+ }
}
if( isTemp ){
pFile->zDeleteOnClose = zConverted;
}else
#endif
{
- free(zConverted);
+ sqlite3_free(zConverted);
+ }
+
+ sqlite3_free(zTmpname);
+ pFile->pMethod = pAppData ? pAppData->pMethod : &winIoMethod;
+ pFile->pVfs = pVfs;
+ pFile->h = h;
+ if( isReadonly ){
+ pFile->ctrlFlags |= WINFILE_RDONLY;
+ }
+ if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
+ pFile->ctrlFlags |= WINFILE_PSOW;
}
+ pFile->lastErrno = NO_ERROR;
+ pFile->zPath = zName;
+#if SQLITE_MAX_MMAP_SIZE>0
+ pFile->hMap = NULL;
+ pFile->pMapRegion = 0;
+ pFile->mmapSize = 0;
+ pFile->mmapSizeActual = 0;
+ pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap;
+#endif
OpenCounter(+1);
return rc;
@@ -34223,7 +42322,7 @@ static int winOpen(
/*
** Delete the named file.
**
-** Note that windows does not allow a file to be deleted if some other
+** Note that Windows does not allow a file to be deleted if some other
** process has it open. Sometimes a virus scanner or indexing program
** will open a journal file shortly after it is created in order to do
** whatever it does. While this other process is holding the
@@ -34239,44 +42338,106 @@ static int winDelete(
){
int cnt = 0;
int rc;
+ DWORD attr;
+ DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
UNUSED_PARAMETER(syncDir);
SimulateIOError(return SQLITE_IOERR_DELETE);
- zConverted = convertUtf8Filename(zFilename);
+ OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
+
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
- return SQLITE_NOMEM;
+ OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
+ return SQLITE_IOERR_NOMEM_BKPT;
}
- if( isNT() ){
- rc = 1;
- while( GetFileAttributesW(zConverted)!=INVALID_FILE_ATTRIBUTES &&
- (rc = DeleteFileW(zConverted))==0 && retryIoerr(&cnt) ){}
- rc = rc ? SQLITE_OK : SQLITE_ERROR;
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- rc = 1;
- while( GetFileAttributesA(zConverted)!=INVALID_FILE_ATTRIBUTES &&
- (rc = DeleteFileA(zConverted))==0 && retryIoerr(&cnt) ){}
- rc = rc ? SQLITE_OK : SQLITE_ERROR;
+ if( osIsNT() ){
+ do {
+#if SQLITE_OS_WINRT
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData;
+ memset(&sAttrData, 0, sizeof(sAttrData));
+ if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard,
+ &sAttrData) ){
+ attr = sAttrData.dwFileAttributes;
+ }else{
+ lastErrno = osGetLastError();
+ if( lastErrno==ERROR_FILE_NOT_FOUND
+ || lastErrno==ERROR_PATH_NOT_FOUND ){
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ break;
+ }
+#else
+ attr = osGetFileAttributesW(zConverted);
#endif
+ if ( attr==INVALID_FILE_ATTRIBUTES ){
+ lastErrno = osGetLastError();
+ if( lastErrno==ERROR_FILE_NOT_FOUND
+ || lastErrno==ERROR_PATH_NOT_FOUND ){
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ break;
+ }
+ if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
+ rc = SQLITE_ERROR; /* Files only. */
+ break;
+ }
+ if ( osDeleteFileW(zConverted) ){
+ rc = SQLITE_OK; /* Deleted OK. */
+ break;
+ }
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
+ rc = SQLITE_ERROR; /* No more retries. */
+ break;
+ }
+ } while(1);
}
- if( rc ){
- rc = winLogError(SQLITE_IOERR_DELETE, "winDelete", zFilename);
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ do {
+ attr = osGetFileAttributesA(zConverted);
+ if ( attr==INVALID_FILE_ATTRIBUTES ){
+ lastErrno = osGetLastError();
+ if( lastErrno==ERROR_FILE_NOT_FOUND
+ || lastErrno==ERROR_PATH_NOT_FOUND ){
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ break;
+ }
+ if ( attr&FILE_ATTRIBUTE_DIRECTORY ){
+ rc = SQLITE_ERROR; /* Files only. */
+ break;
+ }
+ if ( osDeleteFileA(zConverted) ){
+ rc = SQLITE_OK; /* Deleted OK. */
+ break;
+ }
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){
+ rc = SQLITE_ERROR; /* No more retries. */
+ break;
+ }
+ } while(1);
+ }
+#endif
+ if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){
+ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename);
}else{
- logIoerr(cnt);
+ winLogIoerr(cnt, __LINE__);
}
- free(zConverted);
- OSTRACE(("DELETE \"%s\" %s\n", zFilename, (rc ? "failed" : "ok" )));
+ sqlite3_free(zConverted);
+ OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
return rc;
}
/*
-** Check the existance and status of a file.
+** Check the existence and status of a file.
*/
static int winAccess(
sqlite3_vfs *pVfs, /* Not used on win32 */
@@ -34286,52 +42447,54 @@ static int winAccess(
){
DWORD attr;
int rc = 0;
+ DWORD lastErrno = 0;
void *zConverted;
UNUSED_PARAMETER(pVfs);
SimulateIOError( return SQLITE_IOERR_ACCESS; );
- zConverted = convertUtf8Filename(zFilename);
+ OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
+ zFilename, flags, pResOut));
+
+ zConverted = winConvertFromUtf8Filename(zFilename);
if( zConverted==0 ){
- return SQLITE_NOMEM;
+ OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename));
+ return SQLITE_IOERR_NOMEM_BKPT;
}
- if( isNT() ){
+ if( osIsNT() ){
int cnt = 0;
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
memset(&sAttrData, 0, sizeof(sAttrData));
- while( !(rc = GetFileAttributesExW((WCHAR*)zConverted,
- GetFileExInfoStandard,
- &sAttrData)) && retryIoerr(&cnt) ){}
+ while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
+ GetFileExInfoStandard,
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
*/
if( flags==SQLITE_ACCESS_EXISTS
- && sAttrData.nFileSizeHigh==0
+ && sAttrData.nFileSizeHigh==0
&& sAttrData.nFileSizeLow==0 ){
attr = INVALID_FILE_ATTRIBUTES;
}else{
attr = sAttrData.dwFileAttributes;
}
}else{
- logIoerr(cnt);
- if( GetLastError()!=ERROR_FILE_NOT_FOUND ){
- winLogError(SQLITE_IOERR_ACCESS, "winAccess", zFilename);
- free(zConverted);
- return SQLITE_IOERR_ACCESS;
+ winLogIoerr(cnt, __LINE__);
+ if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){
+ sqlite3_free(zConverted);
+ return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess",
+ zFilename);
}else{
attr = INVALID_FILE_ATTRIBUTES;
}
}
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- attr = GetFileAttributesA((char*)zConverted);
-#endif
}
- free(zConverted);
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ attr = osGetFileAttributesA((char*)zConverted);
+ }
+#endif
+ sqlite3_free(zConverted);
switch( flags ){
case SQLITE_ACCESS_READ:
case SQLITE_ACCESS_EXISTS:
@@ -34345,9 +42508,57 @@ static int winAccess(
assert(!"Invalid flags argument");
}
*pResOut = rc;
+ OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
+ zFilename, pResOut, *pResOut));
return SQLITE_OK;
}
+/*
+** Returns non-zero if the specified path name starts with a drive letter
+** followed by a colon character.
+*/
+static BOOL winIsDriveLetterAndColon(
+ const char *zPathname
+){
+ return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
+}
+
+/*
+** Returns non-zero if the specified path name should be used verbatim. If
+** non-zero is returned from this function, the calling function must simply
+** use the provided path name verbatim -OR- resolve it into a full path name
+** using the GetFullPathName Win32 API function (if available).
+*/
+static BOOL winIsVerbatimPathname(
+ const char *zPathname
+){
+ /*
+ ** If the path name starts with a forward slash or a backslash, it is either
+ ** a legal UNC name, a volume relative path, or an absolute path name in the
+ ** "Unix" format on Windows. There is no easy way to differentiate between
+ ** the final two cases; therefore, we return the safer return value of TRUE
+ ** so that callers of this function will simply use it verbatim.
+ */
+ if ( winIsDirSep(zPathname[0]) ){
+ return TRUE;
+ }
+
+ /*
+ ** If the path name starts with a letter and a colon it is either a volume
+ ** relative path or an absolute path. Callers of this function must not
+ ** attempt to treat it as a relative path name (i.e. they should simply use
+ ** it verbatim).
+ */
+ if ( winIsDriveLetterAndColon(zPathname) ){
+ return TRUE;
+ }
+
+ /*
+ ** If we get to this point, the path name should almost certainly be a purely
+ ** relative one (i.e. not a UNC name, not absolute, and not volume relative).
+ */
+ return FALSE;
+}
/*
** Turn a relative pathname into a full pathname. Write the full
@@ -34360,201 +42571,241 @@ static int winFullPathname(
int nFull, /* Size of output buffer in bytes */
char *zFull /* Output buffer */
){
-
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+ DWORD nByte;
+ void *zConverted;
+ char *zOut;
+#endif
+
+ /* If this path name begins with "/X:", where "X" is any alphabetic
+ ** character, discard the initial "/" from the pathname.
+ */
+ if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){
+ zRelative++;
+ }
+
#if defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
UNUSED_PARAMETER(nFull);
- cygwin_conv_to_full_win32_path(zRelative, zFull);
+ assert( nFull>=pVfs->mxPathname );
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a slash.
+ */
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ if( cygwin_conv_path(
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
+ CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
+ sqlite3_free(zOut);
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname1", zRelative);
+ }else{
+ char *zUtf8 = winConvertToUtf8Filename(zOut);
+ if( !zUtf8 ){
+ sqlite3_free(zOut);
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zOut);
+ }
+ }else{
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
+ if( !zOut ){
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ if( cygwin_conv_path(
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
+ zRelative, zOut, pVfs->mxPathname+1)<0 ){
+ sqlite3_free(zOut);
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
+ "winFullPathname2", zRelative);
+ }else{
+ char *zUtf8 = winConvertToUtf8Filename(zOut);
+ if( !zUtf8 ){
+ sqlite3_free(zOut);
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
+ sqlite3_free(zUtf8);
+ sqlite3_free(zOut);
+ }
+ }
return SQLITE_OK;
#endif
-#if SQLITE_OS_WINCE
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
- UNUSED_PARAMETER(nFull);
/* WinCE has no concept of a relative pathname, or so I am told. */
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zRelative);
+ /* WinRT has no way to convert a relative path to an absolute one. */
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a backslash.
+ */
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zRelative);
+ }else{
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
+ }
return SQLITE_OK;
#endif
-#if !SQLITE_OS_WINCE && !defined(__CYGWIN__)
- int nByte;
- void *zConverted;
- char *zOut;
-
- /* If this path name begins with "/X:", where "X" is any alphabetic
- ** character, discard the initial "/" from the pathname.
- */
- if( zRelative[0]=='/' && sqlite3Isalpha(zRelative[1]) && zRelative[2]==':' ){
- zRelative++;
- }
-
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
** function failing. This function could fail if, for example, the
** current working directory has been unlinked.
*/
SimulateIOError( return SQLITE_ERROR );
- UNUSED_PARAMETER(nFull);
- zConverted = convertUtf8Filename(zRelative);
- if( isNT() ){
- WCHAR *zTemp;
- nByte = GetFullPathNameW((WCHAR*)zConverted, 0, 0, 0) + 3;
- zTemp = malloc( nByte*sizeof(zTemp[0]) );
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
+ /*
+ ** NOTE: We are dealing with a relative path name and the data
+ ** directory has been set. Therefore, use it as the basis
+ ** for converting the relative path name to an absolute
+ ** one by prepending the data directory and a backslash.
+ */
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
+ sqlite3_data_directory, winGetDirSep(), zRelative);
+ return SQLITE_OK;
+ }
+ zConverted = winConvertFromUtf8Filename(zRelative);
+ if( zConverted==0 ){
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ if( osIsNT() ){
+ LPWSTR zTemp;
+ nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname1", zRelative);
+ }
+ nByte += 3;
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
if( zTemp==0 ){
- free(zConverted);
- return SQLITE_NOMEM;
- }
- GetFullPathNameW((WCHAR*)zConverted, nByte, zTemp, 0);
- free(zConverted);
- zOut = unicodeToUtf8(zTemp);
- free(zTemp);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
+ sqlite3_free(zConverted);
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zTemp);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname2", zRelative);
+ }
+ sqlite3_free(zConverted);
+ zOut = winUnicodeToUtf8(zTemp);
+ sqlite3_free(zTemp);
+ }
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
char *zTemp;
- nByte = GetFullPathNameA((char*)zConverted, 0, 0, 0) + 3;
- zTemp = malloc( nByte*sizeof(zTemp[0]) );
+ nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname3", zRelative);
+ }
+ nByte += 3;
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
if( zTemp==0 ){
- free(zConverted);
- return SQLITE_NOMEM;
- }
- GetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
- free(zConverted);
- zOut = sqlite3_win32_mbcs_to_utf8(zTemp);
- free(zTemp);
-#endif
+ sqlite3_free(zConverted);
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
+ if( nByte==0 ){
+ sqlite3_free(zConverted);
+ sqlite3_free(zTemp);
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
+ "winFullPathname4", zRelative);
+ }
+ sqlite3_free(zConverted);
+ zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
+ sqlite3_free(zTemp);
}
+#endif
if( zOut ){
- sqlite3_snprintf(pVfs->mxPathname, zFull, "%s", zOut);
- free(zOut);
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
+ sqlite3_free(zOut);
return SQLITE_OK;
}else{
- return SQLITE_NOMEM;
+ return SQLITE_IOERR_NOMEM_BKPT;
}
#endif
}
-/*
-** Get the sector size of the device used to store
-** file.
-*/
-static int getSectorSize(
- sqlite3_vfs *pVfs,
- const char *zRelative /* UTF-8 file name */
-){
- DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
- /* GetDiskFreeSpace is not supported under WINCE */
-#if SQLITE_OS_WINCE
- UNUSED_PARAMETER(pVfs);
- UNUSED_PARAMETER(zRelative);
-#else
- char zFullpath[MAX_PATH+1];
- int rc;
- DWORD dwRet = 0;
- DWORD dwDummy;
-
- /*
- ** We need to get the full path name of the file
- ** to get the drive letter to look up the sector
- ** size.
- */
- SimulateIOErrorBenign(1);
- rc = winFullPathname(pVfs, zRelative, MAX_PATH, zFullpath);
- SimulateIOErrorBenign(0);
- if( rc == SQLITE_OK )
- {
- void *zConverted = convertUtf8Filename(zFullpath);
- if( zConverted ){
- if( isNT() ){
- /* trim path to just drive reference */
- WCHAR *p = zConverted;
- for(;*p;p++){
- if( *p == '\\' ){
- *p = '\0';
- break;
- }
- }
- dwRet = GetDiskFreeSpaceW((WCHAR*)zConverted,
- &dwDummy,
- &bytesPerSector,
- &dwDummy,
- &dwDummy);
- }else{
- /* trim path to just drive reference */
- char *p = (char *)zConverted;
- for(;*p;p++){
- if( *p == '\\' ){
- *p = '\0';
- break;
- }
- }
- dwRet = GetDiskFreeSpaceA((char*)zConverted,
- &dwDummy,
- &bytesPerSector,
- &dwDummy,
- &dwDummy);
- }
- free(zConverted);
- }
- if( !dwRet ){
- bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE;
- }
- }
-#endif
- return (int) bytesPerSector;
-}
-
#ifndef SQLITE_OMIT_LOAD_EXTENSION
/*
** Interfaces for opening a shared library, finding entry points
** within the shared library, and closing the shared library.
*/
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
HANDLE h;
- void *zConverted = convertUtf8Filename(zFilename);
+#if defined(__CYGWIN__)
+ int nFull = pVfs->mxPathname+1;
+ char *zFull = sqlite3MallocZero( nFull );
+ void *zConverted = 0;
+ if( zFull==0 ){
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
+ return 0;
+ }
+ if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){
+ sqlite3_free(zFull);
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
+ return 0;
+ }
+ zConverted = winConvertFromUtf8Filename(zFull);
+ sqlite3_free(zFull);
+#else
+ void *zConverted = winConvertFromUtf8Filename(zFilename);
UNUSED_PARAMETER(pVfs);
+#endif
if( zConverted==0 ){
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
return 0;
}
- if( isNT() ){
- h = LoadLibraryW((WCHAR*)zConverted);
-/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed.
-** Since the ASCII version of these Windows API do not exist for WINCE,
-** it's important to not reference them for WINCE builds.
-*/
-#if SQLITE_OS_WINCE==0
- }else{
- h = LoadLibraryA((char*)zConverted);
+ if( osIsNT() ){
+#if SQLITE_OS_WINRT
+ h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
+#else
+ h = osLoadLibraryW((LPCWSTR)zConverted);
#endif
}
- free(zConverted);
+#ifdef SQLITE_WIN32_HAS_ANSI
+ else{
+ h = osLoadLibraryA((char*)zConverted);
+ }
+#endif
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h));
+ sqlite3_free(zConverted);
return (void*)h;
}
static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
UNUSED_PARAMETER(pVfs);
- getLastErrorMsg(nBuf, zBufOut);
+ winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut);
}
-void (*winDlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
+static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){
+ FARPROC proc;
UNUSED_PARAMETER(pVfs);
-#if SQLITE_OS_WINCE
- /* The GetProcAddressA() routine is only available on wince. */
- return (void(*)(void))GetProcAddressA((HANDLE)pHandle, zSymbol);
-#else
- /* All other windows platforms expect GetProcAddress() to take
- ** an Ansi string regardless of the _UNICODE setting */
- return (void(*)(void))GetProcAddress((HANDLE)pHandle, zSymbol);
-#endif
+ proc = osGetProcAddressA((HANDLE)pH, zSym);
+ OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n",
+ (void*)pH, zSym, (void*)proc));
+ return (void(*)(void))proc;
}
-void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
+static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
UNUSED_PARAMETER(pVfs);
- FreeLibrary((HANDLE)pHandle);
+ osFreeLibrary((HANDLE)pHandle);
+ OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle));
}
#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
#define winDlOpen 0
@@ -34563,41 +42814,85 @@ void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
#define winDlClose 0
#endif
+/* State information for the randomness gatherer. */
+typedef struct EntropyGatherer EntropyGatherer;
+struct EntropyGatherer {
+ unsigned char *a; /* Gather entropy into this buffer */
+ int na; /* Size of a[] in bytes */
+ int i; /* XOR next input into a[i] */
+ int nXor; /* Number of XOR operations done */
+};
+
+#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS)
+/* Mix sz bytes of entropy into p. */
+static void xorMemory(EntropyGatherer *p, unsigned char *x, int sz){
+ int j, k;
+ for(j=0, k=p->i; j<sz; j++){
+ p->a[k++] ^= x[j];
+ if( k>=p->na ) k = 0;
+ }
+ p->i = k;
+ p->nXor += sz;
+}
+#endif /* !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) */
/*
** Write up to nBuf bytes of randomness into zBuf.
*/
static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- int n = 0;
+#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS)
UNUSED_PARAMETER(pVfs);
-#if defined(SQLITE_TEST)
- n = nBuf;
memset(zBuf, 0, nBuf);
+ return nBuf;
#else
- if( sizeof(SYSTEMTIME)<=nBuf-n ){
+ EntropyGatherer e;
+ UNUSED_PARAMETER(pVfs);
+ memset(zBuf, 0, nBuf);
+#if defined(_MSC_VER) && _MSC_VER>=1400 && !SQLITE_OS_WINCE
+ rand_s((unsigned int*)zBuf); /* rand_s() is not available with MinGW */
+#endif /* defined(_MSC_VER) && _MSC_VER>=1400 */
+ e.a = (unsigned char*)zBuf;
+ e.na = nBuf;
+ e.nXor = 0;
+ e.i = 0;
+ {
SYSTEMTIME x;
- GetSystemTime(&x);
- memcpy(&zBuf[n], &x, sizeof(x));
- n += sizeof(x);
+ osGetSystemTime(&x);
+ xorMemory(&e, (unsigned char*)&x, sizeof(SYSTEMTIME));
+ }
+ {
+ DWORD pid = osGetCurrentProcessId();
+ xorMemory(&e, (unsigned char*)&pid, sizeof(DWORD));
}
- if( sizeof(DWORD)<=nBuf-n ){
- DWORD pid = GetCurrentProcessId();
- memcpy(&zBuf[n], &pid, sizeof(pid));
- n += sizeof(pid);
+#if SQLITE_OS_WINRT
+ {
+ ULONGLONG cnt = osGetTickCount64();
+ xorMemory(&e, (unsigned char*)&cnt, sizeof(ULONGLONG));
}
- if( sizeof(DWORD)<=nBuf-n ){
- DWORD cnt = GetTickCount();
- memcpy(&zBuf[n], &cnt, sizeof(cnt));
- n += sizeof(cnt);
+#else
+ {
+ DWORD cnt = osGetTickCount();
+ xorMemory(&e, (unsigned char*)&cnt, sizeof(DWORD));
}
- if( sizeof(LARGE_INTEGER)<=nBuf-n ){
+#endif /* SQLITE_OS_WINRT */
+ {
LARGE_INTEGER i;
- QueryPerformanceCounter(&i);
- memcpy(&zBuf[n], &i, sizeof(i));
- n += sizeof(i);
+ osQueryPerformanceCounter(&i);
+ xorMemory(&e, (unsigned char*)&i, sizeof(LARGE_INTEGER));
}
-#endif
- return n;
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID
+ {
+ UUID id;
+ memset(&id, 0, sizeof(UUID));
+ osUuidCreate(&id);
+ xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
+ memset(&id, 0, sizeof(UUID));
+ osUuidCreateSequential(&id);
+ xorMemory(&e, (unsigned char*)&id, sizeof(UUID));
+ }
+#endif /* !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID */
+ return e.nXor>nBuf ? nBuf : e.nXor;
+#endif /* defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) */
}
@@ -34605,7 +42900,7 @@ static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
** Sleep for a little while. Return the amount of time slept.
*/
static int winSleep(sqlite3_vfs *pVfs, int microsec){
- Sleep((microsec+999)/1000);
+ sqlite3_win32_sleep((microsec+999)/1000);
UNUSED_PARAMETER(pVfs);
return ((microsec+999)/1000)*1000;
}
@@ -34626,11 +42921,12 @@ SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1
** epoch of noon in Greenwich on November 24, 4714 B.C according to the
** proleptic Gregorian calendar.
**
-** On success, return 0. Return 1 if the time and date cannot be found.
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date
+** cannot be found.
*/
static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
- /* FILETIME structure is a 64-bit value representing the number of
- 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
+ /* FILETIME structure is a 64-bit value representing the number of
+ 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5).
*/
FILETIME ft;
static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000;
@@ -34638,22 +42934,23 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
#endif
/* 2^32 - to avoid use of LL and warnings in gcc */
- static const sqlite3_int64 max32BitValue =
- (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + (sqlite3_int64)294967296;
+ static const sqlite3_int64 max32BitValue =
+ (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 +
+ (sqlite3_int64)294967296;
#if SQLITE_OS_WINCE
SYSTEMTIME time;
- GetSystemTime(&time);
+ osGetSystemTime(&time);
/* if SystemTimeToFileTime() fails, it returns zero. */
- if (!SystemTimeToFileTime(&time,&ft)){
- return 1;
+ if (!osSystemTimeToFileTime(&time,&ft)){
+ return SQLITE_ERROR;
}
#else
- GetSystemTimeAsFileTime( &ft );
+ osGetSystemTimeAsFileTime( &ft );
#endif
*piNow = winFiletimeEpoch +
- ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
+ ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) +
(sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000;
#ifdef SQLITE_TEST
@@ -34662,7 +42959,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
}
#endif
UNUSED_PARAMETER(pVfs);
- return 0;
+ return SQLITE_OK;
}
/*
@@ -34670,7 +42967,7 @@ static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
** current time and date as a Julian Day number into *prNow and
** return 0. Return 1 if the time and date cannot be found.
*/
-int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
+static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
int rc;
sqlite3_int64 i;
rc = winCurrentTimeInt64(pVfs, &i);
@@ -34682,8 +42979,8 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
/*
** The idea is that this function works like a combination of
-** GetLastError() and FormatMessage() on windows (or errno and
-** strerror_r() on unix). After an error is returned by an OS
+** GetLastError() and FormatMessage() on Windows (or errno and
+** strerror_r() on Unix). After an error is returned by an OS
** function, SQLite calls this function with zBuf pointing to
** a buffer of nBuf bytes. The OS layer should populate the
** buffer with a nul-terminated UTF-8 encoded error message
@@ -34711,52 +43008,153 @@ int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
** sqlite3_errmsg(), possibly making IO errors easier to debug.
*/
static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
+ DWORD e = osGetLastError();
UNUSED_PARAMETER(pVfs);
- return getLastErrorMsg(nBuf, zBuf);
+ if( nBuf>0 ) winGetLastErrorMsg(e, nBuf, zBuf);
+ return e;
}
-
-
/*
** Initialize and deinitialize the operating system interface.
*/
SQLITE_API int sqlite3_os_init(void){
static sqlite3_vfs winVfs = {
- 3, /* iVersion */
- sizeof(winFile), /* szOsFile */
- MAX_PATH, /* mxPathname */
- 0, /* pNext */
- "win32", /* zName */
- 0, /* pAppData */
- winOpen, /* xOpen */
- winDelete, /* xDelete */
- winAccess, /* xAccess */
- winFullPathname, /* xFullPathname */
- winDlOpen, /* xDlOpen */
- winDlError, /* xDlError */
- winDlSym, /* xDlSym */
- winDlClose, /* xDlClose */
- winRandomness, /* xRandomness */
- winSleep, /* xSleep */
- winCurrentTime, /* xCurrentTime */
- winGetLastError, /* xGetLastError */
- winCurrentTimeInt64, /* xCurrentTimeInt64 */
- 0, /* xSetSystemCall */
- 0, /* xGetSystemCall */
- 0, /* xNextSystemCall */
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32", /* zName */
+ &winAppData, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
};
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ static sqlite3_vfs winLongPathVfs = {
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32-longpath", /* zName */
+ &winAppData, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
+ };
+#endif
+ static sqlite3_vfs winNolockVfs = {
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32-none", /* zName */
+ &winNolockAppData, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
+ };
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ static sqlite3_vfs winLongPathNolockVfs = {
+ 3, /* iVersion */
+ sizeof(winFile), /* szOsFile */
+ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */
+ 0, /* pNext */
+ "win32-longpath-none", /* zName */
+ &winNolockAppData, /* pAppData */
+ winOpen, /* xOpen */
+ winDelete, /* xDelete */
+ winAccess, /* xAccess */
+ winFullPathname, /* xFullPathname */
+ winDlOpen, /* xDlOpen */
+ winDlError, /* xDlError */
+ winDlSym, /* xDlSym */
+ winDlClose, /* xDlClose */
+ winRandomness, /* xRandomness */
+ winSleep, /* xSleep */
+ winCurrentTime, /* xCurrentTime */
+ winGetLastError, /* xGetLastError */
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */
+ winSetSystemCall, /* xSetSystemCall */
+ winGetSystemCall, /* xGetSystemCall */
+ winNextSystemCall, /* xNextSystemCall */
+ };
+#endif
+
+ /* Double-check that the aSyscall[] array has been constructed
+ ** correctly. See ticket [bb3a86e890c8e96ab] */
+ assert( ArraySize(aSyscall)==80 );
-#ifndef SQLITE_OMIT_WAL
/* get memory map allocation granularity */
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
- GetSystemInfo(&winSysInfo);
- assert(winSysInfo.dwAllocationGranularity > 0);
+#if SQLITE_OS_WINRT
+ osGetNativeSystemInfo(&winSysInfo);
+#else
+ osGetSystemInfo(&winSysInfo);
#endif
+ assert( winSysInfo.dwAllocationGranularity>0 );
+ assert( winSysInfo.dwPageSize>0 );
sqlite3_vfs_register(&winVfs, 1);
- return SQLITE_OK;
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ sqlite3_vfs_register(&winLongPathVfs, 0);
+#endif
+
+ sqlite3_vfs_register(&winNolockVfs, 0);
+
+#if defined(SQLITE_WIN32_HAS_WIDE)
+ sqlite3_vfs_register(&winLongPathNolockVfs, 0);
+#endif
+
+ return SQLITE_OK;
}
-SQLITE_API int sqlite3_os_end(void){
+
+SQLITE_API int sqlite3_os_end(void){
+#if SQLITE_OS_WINRT
+ if( sleepObj!=NULL ){
+ osCloseHandle(sleepObj);
+ sleepObj = NULL;
+ }
+#endif
return SQLITE_OK;
}
@@ -34800,13 +43198,15 @@ SQLITE_API int sqlite3_os_end(void){
** start of a transaction, and is thus usually less than a few thousand,
** but can be as large as 2 billion for a really big database.
*/
+/* #include "sqliteInt.h" */
/* Size of the Bitvec structure in bytes. */
#define BITVEC_SZ 512
/* Round the union size down to the nearest pointer boundary, since that's how
** it will be aligned within the Bitvec struct. */
-#define BITVEC_USIZE (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
+#define BITVEC_USIZE \
+ (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*))
/* Type of the array "element" for the bitmap representation.
** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE.
@@ -34837,7 +43237,7 @@ SQLITE_API int sqlite3_os_end(void){
/*
** A bitmap is an instance of the following structure.
**
-** This bitmap records the existance of zero or more bits
+** This bitmap records the existence of zero or more bits
** with values between 1 and iSize, inclusive.
**
** There are three possible representations of the bitmap.
@@ -34891,10 +43291,10 @@ SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32 iSize){
** If p is NULL (if the bitmap has not been created) or if
** i is out of range, then return false.
*/
-SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){
- if( p==0 ) return 0;
- if( i>p->iSize || i==0 ) return 0;
+SQLITE_PRIVATE int sqlite3BitvecTestNotNull(Bitvec *p, u32 i){
+ assert( p!=0 );
i--;
+ if( i>=p->iSize ) return 0;
while( p->iDivisor ){
u32 bin = i/p->iDivisor;
i = i%p->iDivisor;
@@ -34914,6 +43314,9 @@ SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){
return 0;
}
}
+SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){
+ return p!=0 && sqlite3BitvecTestNotNull(p,i);
+}
/*
** Set the i-th bit. Return 0 on success and an error code if
@@ -34938,7 +43341,7 @@ SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){
i = i%p->iDivisor;
if( p->u.apSub[bin]==0 ){
p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor );
- if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM;
+ if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM_BKPT;
}
p = p->u.apSub[bin];
}
@@ -34973,7 +43376,7 @@ bitvec_set_rehash:
int rc;
u32 *aiValues = sqlite3StackAllocRaw(0, sizeof(p->u.aHash));
if( aiValues==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}else{
memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
memset(p->u.apSub, 0, sizeof(p->u.apSub));
@@ -35105,10 +43508,9 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){
/* Allocate the Bitvec to be tested and a linear array of
** bits to act as the reference */
pBitvec = sqlite3BitvecCreate( sz );
- pV = sqlite3_malloc( (sz+7)/8 + 1 );
- pTmpSpace = sqlite3_malloc(BITVEC_SZ);
+ pV = sqlite3MallocZero( (sz+7)/8 + 1 );
+ pTmpSpace = sqlite3_malloc64(BITVEC_SZ);
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
- memset(pV, 0, (sz+7)/8 + 1);
/* NULL pBitvec tests */
sqlite3BitvecSet(0, 1);
@@ -35187,113 +43589,217 @@ bitvec_end:
*************************************************************************
** This file implements that page cache.
*/
+/* #include "sqliteInt.h" */
/*
-** A complete page cache is an instance of this structure.
+** A complete page cache is an instance of this structure. Every
+** entry in the cache holds a single page of the database file. The
+** btree layer only operates on the cached copy of the database pages.
+**
+** A page cache entry is "clean" if it exactly matches what is currently
+** on disk. A page is "dirty" if it has been modified and needs to be
+** persisted to disk.
+**
+** pDirty, pDirtyTail, pSynced:
+** All dirty pages are linked into the doubly linked list using
+** PgHdr.pDirtyNext and pDirtyPrev. The list is maintained in LRU order
+** such that p was added to the list more recently than p->pDirtyNext.
+** PCache.pDirty points to the first (newest) element in the list and
+** pDirtyTail to the last (oldest).
+**
+** The PCache.pSynced variable is used to optimize searching for a dirty
+** page to eject from the cache mid-transaction. It is better to eject
+** a page that does not require a journal sync than one that does.
+** Therefore, pSynced is maintained to that it *almost* always points
+** to either the oldest page in the pDirty/pDirtyTail list that has a
+** clear PGHDR_NEED_SYNC flag or to a page that is older than this one
+** (so that the right page to eject can be found by following pDirtyPrev
+** pointers).
*/
struct PCache {
PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */
PgHdr *pSynced; /* Last synced page in dirty page list */
- int nRef; /* Number of referenced pages */
- int nMax; /* Configured cache size */
+ int nRefSum; /* Sum of ref counts over all pages */
+ int szCache; /* Configured cache size */
+ int szSpill; /* Size before spilling occurs */
int szPage; /* Size of every page in this cache */
int szExtra; /* Size of extra space for each page */
- int bPurgeable; /* True if pages are on backing store */
+ u8 bPurgeable; /* True if pages are on backing store */
+ u8 eCreate; /* eCreate value for for xFetch() */
int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */
void *pStress; /* Argument to xStress */
sqlite3_pcache *pCache; /* Pluggable cache module */
- PgHdr *pPage1; /* Reference to page 1 */
};
+/********************************** Test and Debug Logic **********************/
/*
-** Some of the assert() macros in this code are too expensive to run
-** even during normal debugging. Use them only rarely on long-running
-** tests. Enable the expensive asserts using the
-** -DSQLITE_ENABLE_EXPENSIVE_ASSERT=1 compile-time option.
+** Debug tracing macros. Enable by by changing the "0" to "1" and
+** recompiling.
+**
+** When sqlite3PcacheTrace is 1, single line trace messages are issued.
+** When sqlite3PcacheTrace is 2, a dump of the pcache showing all cache entries
+** is displayed for many operations, resulting in a lot of output.
*/
-#ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
-# define expensive_assert(X) assert(X)
-#else
-# define expensive_assert(X)
+#if defined(SQLITE_DEBUG) && 0
+ int sqlite3PcacheTrace = 2; /* 0: off 1: simple 2: cache dumps */
+ int sqlite3PcacheMxDump = 9999; /* Max cache entries for pcacheDump() */
+# define pcacheTrace(X) if(sqlite3PcacheTrace){sqlite3DebugPrintf X;}
+ void pcacheDump(PCache *pCache){
+ int N;
+ int i, j;
+ sqlite3_pcache_page *pLower;
+ PgHdr *pPg;
+ unsigned char *a;
+
+ if( sqlite3PcacheTrace<2 ) return;
+ if( pCache->pCache==0 ) return;
+ N = sqlite3PcachePagecount(pCache);
+ if( N>sqlite3PcacheMxDump ) N = sqlite3PcacheMxDump;
+ for(i=1; i<=N; i++){
+ pLower = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, i, 0);
+ if( pLower==0 ) continue;
+ pPg = (PgHdr*)pLower->pExtra;
+ printf("%3d: nRef %2d flgs %02x data ", i, pPg->nRef, pPg->flags);
+ a = (unsigned char *)pLower->pBuf;
+ for(j=0; j<12; j++) printf("%02x", a[j]);
+ printf("\n");
+ if( pPg->pPage==0 ){
+ sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, pLower, 0);
+ }
+ }
+ }
+ #else
+# define pcacheTrace(X)
+# define pcacheDump(X)
#endif
-/********************************** Linked List Management ********************/
-
-#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT)
/*
-** Check that the pCache->pSynced variable is set correctly. If it
-** is not, either fail an assert or return zero. Otherwise, return
-** non-zero. This is only used in debugging builds, as follows:
+** Check invariants on a PgHdr entry. Return true if everything is OK.
+** Return false if any invariant is violated.
**
-** expensive_assert( pcacheCheckSynced(pCache) );
+** This routine is for use inside of assert() statements only. For
+** example:
+**
+** assert( sqlite3PcachePageSanity(pPg) );
*/
-static int pcacheCheckSynced(PCache *pCache){
- PgHdr *p;
- for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pDirtyPrev){
- assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) );
- }
- return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0);
+#if SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3PcachePageSanity(PgHdr *pPg){
+ PCache *pCache;
+ assert( pPg!=0 );
+ assert( pPg->pgno>0 ); /* Page number is 1 or more */
+ pCache = pPg->pCache;
+ assert( pCache!=0 ); /* Every page has an associated PCache */
+ if( pPg->flags & PGHDR_CLEAN ){
+ assert( (pPg->flags & PGHDR_DIRTY)==0 );/* Cannot be both CLEAN and DIRTY */
+ assert( pCache->pDirty!=pPg ); /* CLEAN pages not on dirty list */
+ assert( pCache->pDirtyTail!=pPg );
+ }
+ /* WRITEABLE pages must also be DIRTY */
+ if( pPg->flags & PGHDR_WRITEABLE ){
+ assert( pPg->flags & PGHDR_DIRTY ); /* WRITEABLE implies DIRTY */
+ }
+ /* NEED_SYNC can be set independently of WRITEABLE. This can happen,
+ ** for example, when using the sqlite3PagerDontWrite() optimization:
+ ** (1) Page X is journalled, and gets WRITEABLE and NEED_SEEK.
+ ** (2) Page X moved to freelist, WRITEABLE is cleared
+ ** (3) Page X reused, WRITEABLE is set again
+ ** If NEED_SYNC had been cleared in step 2, then it would not be reset
+ ** in step 3, and page might be written into the database without first
+ ** syncing the rollback journal, which might cause corruption on a power
+ ** loss.
+ **
+ ** Another example is when the database page size is smaller than the
+ ** disk sector size. When any page of a sector is journalled, all pages
+ ** in that sector are marked NEED_SYNC even if they are still CLEAN, just
+ ** in case they are later modified, since all pages in the same sector
+ ** must be journalled and synced before any of those pages can be safely
+ ** written.
+ */
+ return 1;
}
-#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */
-
-/*
-** Remove page pPage from the list of dirty pages.
-*/
-static void pcacheRemoveFromDirtyList(PgHdr *pPage){
- PCache *p = pPage->pCache;
+#endif /* SQLITE_DEBUG */
- assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
- assert( pPage->pDirtyPrev || pPage==p->pDirty );
- /* Update the PCache1.pSynced variable if necessary. */
- if( p->pSynced==pPage ){
- PgHdr *pSynced = pPage->pDirtyPrev;
- while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){
- pSynced = pSynced->pDirtyPrev;
- }
- p->pSynced = pSynced;
- }
-
- if( pPage->pDirtyNext ){
- pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
- }else{
- assert( pPage==p->pDirtyTail );
- p->pDirtyTail = pPage->pDirtyPrev;
- }
- if( pPage->pDirtyPrev ){
- pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
- }else{
- assert( pPage==p->pDirty );
- p->pDirty = pPage->pDirtyNext;
- }
- pPage->pDirtyNext = 0;
- pPage->pDirtyPrev = 0;
+/********************************** Linked List Management ********************/
- expensive_assert( pcacheCheckSynced(p) );
-}
+/* Allowed values for second argument to pcacheManageDirtyList() */
+#define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */
+#define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */
+#define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */
/*
-** Add page pPage to the head of the dirty list (PCache1.pDirty is set to
-** pPage).
+** Manage pPage's participation on the dirty list. Bits of the addRemove
+** argument determines what operation to do. The 0x01 bit means first
+** remove pPage from the dirty list. The 0x02 means add pPage back to
+** the dirty list. Doing both moves pPage to the front of the dirty list.
*/
-static void pcacheAddToDirtyList(PgHdr *pPage){
+static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){
PCache *p = pPage->pCache;
- assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
+ pcacheTrace(("%p.DIRTYLIST.%s %d\n", p,
+ addRemove==1 ? "REMOVE" : addRemove==2 ? "ADD" : "FRONT",
+ pPage->pgno));
+ if( addRemove & PCACHE_DIRTYLIST_REMOVE ){
+ assert( pPage->pDirtyNext || pPage==p->pDirtyTail );
+ assert( pPage->pDirtyPrev || pPage==p->pDirty );
+
+ /* Update the PCache1.pSynced variable if necessary. */
+ if( p->pSynced==pPage ){
+ p->pSynced = pPage->pDirtyPrev;
+ }
+
+ if( pPage->pDirtyNext ){
+ pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev;
+ }else{
+ assert( pPage==p->pDirtyTail );
+ p->pDirtyTail = pPage->pDirtyPrev;
+ }
+ if( pPage->pDirtyPrev ){
+ pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext;
+ }else{
+ /* If there are now no dirty pages in the cache, set eCreate to 2.
+ ** This is an optimization that allows sqlite3PcacheFetch() to skip
+ ** searching for a dirty page to eject from the cache when it might
+ ** otherwise have to. */
+ assert( pPage==p->pDirty );
+ p->pDirty = pPage->pDirtyNext;
+ assert( p->bPurgeable || p->eCreate==2 );
+ if( p->pDirty==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ assert( p->bPurgeable==0 || p->eCreate==1 );
+ p->eCreate = 2;
+ }
+ }
+ pPage->pDirtyNext = 0;
+ pPage->pDirtyPrev = 0;
+ }
+ if( addRemove & PCACHE_DIRTYLIST_ADD ){
+ assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage );
+
+ pPage->pDirtyNext = p->pDirty;
+ if( pPage->pDirtyNext ){
+ assert( pPage->pDirtyNext->pDirtyPrev==0 );
+ pPage->pDirtyNext->pDirtyPrev = pPage;
+ }else{
+ p->pDirtyTail = pPage;
+ if( p->bPurgeable ){
+ assert( p->eCreate==2 );
+ p->eCreate = 1;
+ }
+ }
+ p->pDirty = pPage;
- pPage->pDirtyNext = p->pDirty;
- if( pPage->pDirtyNext ){
- assert( pPage->pDirtyNext->pDirtyPrev==0 );
- pPage->pDirtyNext->pDirtyPrev = pPage;
- }
- p->pDirty = pPage;
- if( !p->pDirtyTail ){
- p->pDirtyTail = pPage;
- }
- if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){
- p->pSynced = pPage;
+ /* If pSynced is NULL and this page has a clear NEED_SYNC flag, set
+ ** pSynced to point to it. Checking the NEED_SYNC flag is an
+ ** optimization, as if pSynced points to a page with the NEED_SYNC
+ ** flag set sqlite3PcacheFetchStress() searches through all newer
+ ** entries of the dirty-list for a page with NEED_SYNC clear anyway. */
+ if( !p->pSynced
+ && 0==(pPage->flags&PGHDR_NEED_SYNC) /*OPTIMIZATION-IF-FALSE*/
+ ){
+ p->pSynced = pPage;
+ }
}
- expensive_assert( pcacheCheckSynced(p) );
+ pcacheDump(p);
}
/*
@@ -35301,12 +43807,27 @@ static void pcacheAddToDirtyList(PgHdr *pPage){
** being used for an in-memory database, this function is a no-op.
*/
static void pcacheUnpin(PgHdr *p){
- PCache *pCache = p->pCache;
- if( pCache->bPurgeable ){
- if( p->pgno==1 ){
- pCache->pPage1 = 0;
- }
- sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0);
+ if( p->pCache->bPurgeable ){
+ pcacheTrace(("%p.UNPIN %d\n", p->pCache, p->pgno));
+ sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0);
+ pcacheDump(p->pCache);
+ }
+}
+
+/*
+** Compute the number of pages of cache requested. p->szCache is the
+** cache size requested by the "PRAGMA cache_size" statement.
+*/
+static int numberOfCachePages(PCache *p){
+ if( p->szCache>=0 ){
+ /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the
+ ** suggested cache size is set to N. */
+ return p->szCache;
+ }else{
+ /* IMPLEMENTATION-OF: R-61436-13639 If the argument N is negative, then
+ ** the number of cache pages is adjusted to use approximately abs(N*1024)
+ ** bytes of memory. */
+ return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra));
}
}
@@ -35316,18 +43837,18 @@ static void pcacheUnpin(PgHdr *p){
** functions are threadsafe.
*/
SQLITE_PRIVATE int sqlite3PcacheInitialize(void){
- if( sqlite3GlobalConfig.pcache.xInit==0 ){
+ if( sqlite3GlobalConfig.pcache2.xInit==0 ){
/* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
** built-in default page cache is used instead of the application defined
** page cache. */
sqlite3PCacheSetDefault();
}
- return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);
+ return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
}
SQLITE_PRIVATE void sqlite3PcacheShutdown(void){
- if( sqlite3GlobalConfig.pcache.xShutdown ){
+ if( sqlite3GlobalConfig.pcache2.xShutdown ){
/* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
- sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg);
+ sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg);
}
}
@@ -35342,7 +43863,7 @@ SQLITE_PRIVATE int sqlite3PcacheSize(void){ return sizeof(PCache); }
** The caller discovers how much space needs to be allocated by
** calling sqlite3PcacheSize().
*/
-SQLITE_PRIVATE void sqlite3PcacheOpen(
+SQLITE_PRIVATE int sqlite3PcacheOpen(
int szPage, /* Size of every page */
int szExtra, /* Extra space associated with each page */
int bPurgeable, /* True if pages are on backing store */
@@ -35351,73 +43872,126 @@ SQLITE_PRIVATE void sqlite3PcacheOpen(
PCache *p /* Preallocated space for the PCache */
){
memset(p, 0, sizeof(PCache));
- p->szPage = szPage;
+ p->szPage = 1;
p->szExtra = szExtra;
p->bPurgeable = bPurgeable;
+ p->eCreate = 2;
p->xStress = xStress;
p->pStress = pStress;
- p->nMax = 100;
+ p->szCache = 100;
+ p->szSpill = 1;
+ pcacheTrace(("%p.OPEN szPage %d bPurgeable %d\n",p,szPage,bPurgeable));
+ return sqlite3PcacheSetPageSize(p, szPage);
}
/*
** Change the page size for PCache object. The caller must ensure that there
** are no outstanding page references when this function is called.
*/
-SQLITE_PRIVATE void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
- assert( pCache->nRef==0 && pCache->pDirty==0 );
- if( pCache->pCache ){
- sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
- pCache->pCache = 0;
- pCache->pPage1 = 0;
+SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
+ assert( pCache->nRefSum==0 && pCache->pDirty==0 );
+ if( pCache->szPage ){
+ sqlite3_pcache *pNew;
+ pNew = sqlite3GlobalConfig.pcache2.xCreate(
+ szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)),
+ pCache->bPurgeable
+ );
+ if( pNew==0 ) return SQLITE_NOMEM_BKPT;
+ sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache));
+ if( pCache->pCache ){
+ sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
+ }
+ pCache->pCache = pNew;
+ pCache->szPage = szPage;
+ pcacheTrace(("%p.PAGESIZE %d\n",pCache,szPage));
}
- pCache->szPage = szPage;
+ return SQLITE_OK;
}
/*
** Try to obtain a page from the cache.
+**
+** This routine returns a pointer to an sqlite3_pcache_page object if
+** such an object is already in cache, or if a new one is created.
+** This routine returns a NULL pointer if the object was not in cache
+** and could not be created.
+**
+** The createFlags should be 0 to check for existing pages and should
+** be 3 (not 1, but 3) to try to create a new page.
+**
+** If the createFlag is 0, then NULL is always returned if the page
+** is not already in the cache. If createFlag is 1, then a new page
+** is created only if that can be done without spilling dirty pages
+** and without exceeding the cache size limit.
+**
+** The caller needs to invoke sqlite3PcacheFetchFinish() to properly
+** initialize the sqlite3_pcache_page object and convert it into a
+** PgHdr object. The sqlite3PcacheFetch() and sqlite3PcacheFetchFinish()
+** routines are split this way for performance reasons. When separated
+** they can both (usually) operate without having to push values to
+** the stack on entry and pop them back off on exit, which saves a
+** lot of pushing and popping.
*/
-SQLITE_PRIVATE int sqlite3PcacheFetch(
+SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch(
PCache *pCache, /* Obtain the page from this cache */
Pgno pgno, /* Page number to obtain */
- int createFlag, /* If true, create page if it does not exist already */
- PgHdr **ppPage /* Write the page here */
+ int createFlag /* If true, create page if it does not exist already */
){
- PgHdr *pPage = 0;
int eCreate;
+ sqlite3_pcache_page *pRes;
assert( pCache!=0 );
- assert( createFlag==1 || createFlag==0 );
+ assert( pCache->pCache!=0 );
+ assert( createFlag==3 || createFlag==0 );
assert( pgno>0 );
+ assert( pCache->eCreate==((pCache->bPurgeable && pCache->pDirty) ? 1 : 2) );
- /* If the pluggable cache (sqlite3_pcache*) has not been allocated,
- ** allocate it now.
+ /* eCreate defines what to do if the page does not exist.
+ ** 0 Do not allocate a new page. (createFlag==0)
+ ** 1 Allocate a new page if doing so is inexpensive.
+ ** (createFlag==1 AND bPurgeable AND pDirty)
+ ** 2 Allocate a new page even it doing so is difficult.
+ ** (createFlag==1 AND !(bPurgeable AND pDirty)
*/
- if( !pCache->pCache && createFlag ){
- sqlite3_pcache *p;
- int nByte;
- nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr);
- p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable);
- if( !p ){
- return SQLITE_NOMEM;
- }
- sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax);
- pCache->pCache = p;
- }
-
- eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty));
- if( pCache->pCache ){
- pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate);
- }
+ eCreate = createFlag & pCache->eCreate;
+ assert( eCreate==0 || eCreate==1 || eCreate==2 );
+ assert( createFlag==0 || pCache->eCreate==eCreate );
+ assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) );
+ pRes = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
+ pcacheTrace(("%p.FETCH %d%s (result: %p)\n",pCache,pgno,
+ createFlag?" create":"",pRes));
+ return pRes;
+}
- if( !pPage && eCreate==1 ){
- PgHdr *pPg;
+/*
+** If the sqlite3PcacheFetch() routine is unable to allocate a new
+** page because no clean pages are available for reuse and the cache
+** size limit has been reached, then this routine can be invoked to
+** try harder to allocate a page. This routine might invoke the stress
+** callback to spill dirty pages to the journal. It will then try to
+** allocate the new page and will only fail to allocate a new page on
+** an OOM error.
+**
+** This routine should be invoked only after sqlite3PcacheFetch() fails.
+*/
+SQLITE_PRIVATE int sqlite3PcacheFetchStress(
+ PCache *pCache, /* Obtain the page from this cache */
+ Pgno pgno, /* Page number to obtain */
+ sqlite3_pcache_page **ppPage /* Write result here */
+){
+ PgHdr *pPg;
+ if( pCache->eCreate==2 ) return 0;
+ if( sqlite3PcachePagecount(pCache)>pCache->szSpill ){
/* Find a dirty page to write-out and recycle. First try to find a
** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
** cleared), but if that is not possible settle for any other
** unreferenced dirty page.
- */
- expensive_assert( pcacheCheckSynced(pCache) );
+ **
+ ** If the LRU page in the dirty list that has a clear PGHDR_NEED_SYNC
+ ** flag is currently referenced, then the following may leave pSynced
+ ** set incorrectly (pointing to other than the LRU page with NEED_SYNC
+ ** cleared). This is Ok, as pSynced is just an optimization. */
for(pPg=pCache->pSynced;
pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC));
pPg=pPg->pDirtyPrev
@@ -35433,59 +44007,90 @@ SQLITE_PRIVATE int sqlite3PcacheFetch(
"spill page %d making room for %d - cache used: %d/%d",
pPg->pgno, pgno,
sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache),
- pCache->nMax);
+ numberOfCachePages(pCache));
#endif
+ pcacheTrace(("%p.SPILL %d\n",pCache,pPg->pgno));
rc = pCache->xStress(pCache->pStress, pPg);
+ pcacheDump(pCache);
if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
return rc;
}
}
-
- pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2);
}
+ *ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
+ return *ppPage==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
+}
- if( pPage ){
- if( !pPage->pData ){
- memset(pPage, 0, sizeof(PgHdr));
- pPage->pData = (void *)&pPage[1];
- pPage->pExtra = (void*)&((char *)pPage->pData)[pCache->szPage];
- memset(pPage->pExtra, 0, pCache->szExtra);
- pPage->pCache = pCache;
- pPage->pgno = pgno;
- }
- assert( pPage->pCache==pCache );
- assert( pPage->pgno==pgno );
- assert( pPage->pData==(void *)&pPage[1] );
- assert( pPage->pExtra==(void *)&((char *)&pPage[1])[pCache->szPage] );
-
- if( 0==pPage->nRef ){
- pCache->nRef++;
- }
- pPage->nRef++;
- if( pgno==1 ){
- pCache->pPage1 = pPage;
- }
+/*
+** This is a helper routine for sqlite3PcacheFetchFinish()
+**
+** In the uncommon case where the page being fetched has not been
+** initialized, this routine is invoked to do the initialization.
+** This routine is broken out into a separate function since it
+** requires extra stack manipulation that can be avoided in the common
+** case.
+*/
+static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit(
+ PCache *pCache, /* Obtain the page from this cache */
+ Pgno pgno, /* Page number obtained */
+ sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
+){
+ PgHdr *pPgHdr;
+ assert( pPage!=0 );
+ pPgHdr = (PgHdr*)pPage->pExtra;
+ assert( pPgHdr->pPage==0 );
+ memset(&pPgHdr->pDirty, 0, sizeof(PgHdr) - offsetof(PgHdr,pDirty));
+ pPgHdr->pPage = pPage;
+ pPgHdr->pData = pPage->pBuf;
+ pPgHdr->pExtra = (void *)&pPgHdr[1];
+ memset(pPgHdr->pExtra, 0, pCache->szExtra);
+ pPgHdr->pCache = pCache;
+ pPgHdr->pgno = pgno;
+ pPgHdr->flags = PGHDR_CLEAN;
+ return sqlite3PcacheFetchFinish(pCache,pgno,pPage);
+}
+
+/*
+** This routine converts the sqlite3_pcache_page object returned by
+** sqlite3PcacheFetch() into an initialized PgHdr object. This routine
+** must be called after sqlite3PcacheFetch() in order to get a usable
+** result.
+*/
+SQLITE_PRIVATE PgHdr *sqlite3PcacheFetchFinish(
+ PCache *pCache, /* Obtain the page from this cache */
+ Pgno pgno, /* Page number obtained */
+ sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */
+){
+ PgHdr *pPgHdr;
+
+ assert( pPage!=0 );
+ pPgHdr = (PgHdr *)pPage->pExtra;
+
+ if( !pPgHdr->pPage ){
+ return pcacheFetchFinishWithInit(pCache, pgno, pPage);
}
- *ppPage = pPage;
- return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
+ pCache->nRefSum++;
+ pPgHdr->nRef++;
+ assert( sqlite3PcachePageSanity(pPgHdr) );
+ return pPgHdr;
}
/*
** Decrement the reference count on a page. If the page is clean and the
-** reference count drops to 0, then it is made elible for recycling.
+** reference count drops to 0, then it is made eligible for recycling.
*/
-SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr *p){
+SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){
assert( p->nRef>0 );
- p->nRef--;
- if( p->nRef==0 ){
- PCache *pCache = p->pCache;
- pCache->nRef--;
- if( (p->flags&PGHDR_DIRTY)==0 ){
+ p->pCache->nRefSum--;
+ if( (--p->nRef)==0 ){
+ if( p->flags&PGHDR_CLEAN ){
pcacheUnpin(p);
- }else{
- /* Move the page to the head of the dirty list. */
- pcacheRemoveFromDirtyList(p);
- pcacheAddToDirtyList(p);
+ }else if( p->pDirtyPrev!=0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* Move the page to the head of the dirty list. If p->pDirtyPrev==0,
+ ** then page p is already at the head of the dirty list and the
+ ** following call would be a no-op. Hence the OPTIMIZATION-IF-FALSE
+ ** tag above. */
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
}
}
}
@@ -35495,7 +44100,9 @@ SQLITE_PRIVATE void sqlite3PcacheRelease(PgHdr *p){
*/
SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){
assert(p->nRef>0);
+ assert( sqlite3PcachePageSanity(p) );
p->nRef++;
+ p->pCache->nRefSum++;
}
/*
@@ -35504,17 +44111,13 @@ SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){
** page pointed to by p is invalid.
*/
SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){
- PCache *pCache;
assert( p->nRef==1 );
+ assert( sqlite3PcachePageSanity(p) );
if( p->flags&PGHDR_DIRTY ){
- pcacheRemoveFromDirtyList(p);
- }
- pCache = p->pCache;
- pCache->nRef--;
- if( p->pgno==1 ){
- pCache->pPage1 = 0;
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
}
- sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 1);
+ p->pCache->nRefSum--;
+ sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1);
}
/*
@@ -35522,11 +44125,17 @@ SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){
** make it so.
*/
SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
- p->flags &= ~PGHDR_DONT_WRITE;
assert( p->nRef>0 );
- if( 0==(p->flags & PGHDR_DIRTY) ){
- p->flags |= PGHDR_DIRTY;
- pcacheAddToDirtyList( p);
+ assert( sqlite3PcachePageSanity(p) );
+ if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ /*OPTIMIZATION-IF-FALSE*/
+ p->flags &= ~PGHDR_DONT_WRITE;
+ if( p->flags & PGHDR_CLEAN ){
+ p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN);
+ pcacheTrace(("%p.DIRTY %d\n",p->pCache,p->pgno));
+ assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY );
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD);
+ }
+ assert( sqlite3PcachePageSanity(p) );
}
}
@@ -35535,9 +44144,14 @@ SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){
** make it so.
*/
SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
- if( (p->flags & PGHDR_DIRTY) ){
- pcacheRemoveFromDirtyList(p);
- p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC);
+ assert( sqlite3PcachePageSanity(p) );
+ if( ALWAYS((p->flags & PGHDR_DIRTY)!=0) ){
+ assert( (p->flags & PGHDR_CLEAN)==0 );
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE);
+ p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
+ p->flags |= PGHDR_CLEAN;
+ pcacheTrace(("%p.CLEAN %d\n",p->pCache,p->pgno));
+ assert( sqlite3PcachePageSanity(p) );
if( p->nRef==0 ){
pcacheUnpin(p);
}
@@ -35549,12 +44163,25 @@ SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){
*/
SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache *pCache){
PgHdr *p;
+ pcacheTrace(("%p.CLEAN-ALL\n",pCache));
while( (p = pCache->pDirty)!=0 ){
sqlite3PcacheMakeClean(p);
}
}
/*
+** Clear the PGHDR_NEED_SYNC and PGHDR_WRITEABLE flag from all dirty pages.
+*/
+SQLITE_PRIVATE void sqlite3PcacheClearWritable(PCache *pCache){
+ PgHdr *p;
+ pcacheTrace(("%p.CLEAR-WRITEABLE\n",pCache));
+ for(p=pCache->pDirty; p; p=p->pDirtyNext){
+ p->flags &= ~(PGHDR_NEED_SYNC|PGHDR_WRITEABLE);
+ }
+ pCache->pSynced = pCache->pDirtyTail;
+}
+
+/*
** Clear the PGHDR_NEED_SYNC flag from all dirty pages.
*/
SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){
@@ -35572,11 +44199,12 @@ SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
PCache *pCache = p->pCache;
assert( p->nRef>0 );
assert( newPgno>0 );
- sqlite3GlobalConfig.pcache.xRekey(pCache->pCache, p, p->pgno, newPgno);
+ assert( sqlite3PcachePageSanity(p) );
+ pcacheTrace(("%p.MOVE %d -> %d\n",pCache,p->pgno,newPgno));
+ sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
p->pgno = newPgno;
if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
- pcacheRemoveFromDirtyList(p);
- pcacheAddToDirtyList(p);
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT);
}
}
@@ -35593,6 +44221,7 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
if( pCache->pCache ){
PgHdr *p;
PgHdr *pNext;
+ pcacheTrace(("%p.TRUNCATE %d\n",pCache,pgno));
for(p=pCache->pDirty; p; p=pNext){
pNext = p->pDirtyNext;
/* This routine never gets call with a positive pgno except right
@@ -35600,16 +44229,21 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
** it must be that pgno==0.
*/
assert( p->pgno>0 );
- if( ALWAYS(p->pgno>pgno) ){
+ if( p->pgno>pgno ){
assert( p->flags&PGHDR_DIRTY );
sqlite3PcacheMakeClean(p);
}
}
- if( pgno==0 && pCache->pPage1 ){
- memset(pCache->pPage1->pData, 0, pCache->szPage);
- pgno = 1;
+ if( pgno==0 && pCache->nRefSum ){
+ sqlite3_pcache_page *pPage1;
+ pPage1 = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0);
+ if( ALWAYS(pPage1) ){ /* Page 1 is always available in cache, because
+ ** pCache->nRefSum>0 */
+ memset(pPage1->pBuf, 0, pCache->szPage);
+ pgno = 1;
+ }
}
- sqlite3GlobalConfig.pcache.xTruncate(pCache->pCache, pgno+1);
+ sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);
}
}
@@ -35617,9 +44251,9 @@ SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){
** Close a cache.
*/
SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){
- if( pCache->pCache ){
- sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
- }
+ assert( pCache->pCache!=0 );
+ pcacheTrace(("%p.CLOSE\n",pCache));
+ sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
}
/*
@@ -35631,29 +44265,31 @@ SQLITE_PRIVATE void sqlite3PcacheClear(PCache *pCache){
/*
** Merge two lists of pages connected by pDirty and in pgno order.
-** Do not both fixing the pDirtyPrev pointers.
+** Do not bother fixing the pDirtyPrev pointers.
*/
static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){
PgHdr result, *pTail;
pTail = &result;
- while( pA && pB ){
+ assert( pA!=0 && pB!=0 );
+ for(;;){
if( pA->pgno<pB->pgno ){
pTail->pDirty = pA;
pTail = pA;
pA = pA->pDirty;
+ if( pA==0 ){
+ pTail->pDirty = pB;
+ break;
+ }
}else{
pTail->pDirty = pB;
pTail = pB;
pB = pB->pDirty;
+ if( pB==0 ){
+ pTail->pDirty = pA;
+ break;
+ }
}
}
- if( pA ){
- pTail->pDirty = pA;
- }else if( pB ){
- pTail->pDirty = pB;
- }else{
- pTail->pDirty = 0;
- }
return result.pDirty;
}
@@ -35694,7 +44330,8 @@ static PgHdr *pcacheSortDirtyList(PgHdr *pIn){
}
p = a[0];
for(i=1; i<N_SORT_BUCKET; i++){
- p = pcacheMergeDirtyList(p, a[i]);
+ if( a[i]==0 ) continue;
+ p = p ? pcacheMergeDirtyList(p, a[i]) : a[i];
}
return p;
}
@@ -35711,10 +44348,13 @@ SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){
}
/*
-** Return the total number of referenced pages held by the cache.
+** Return the total number of references to all pages held by the cache.
+**
+** This is not the total number of pages referenced, but the sum of the
+** reference count for all pages.
*/
SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){
- return pCache->nRef;
+ return pCache->nRefSum;
}
/*
@@ -35728,11 +44368,8 @@ SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){
** Return the total number of pages in the cache.
*/
SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
- int nPage = 0;
- if( pCache->pCache ){
- nPage = sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache);
- }
- return nPage;
+ assert( pCache->pCache!=0 );
+ return sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache);
}
#ifdef SQLITE_TEST
@@ -35740,7 +44377,7 @@ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){
** Get the suggested cache-size value.
*/
SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){
- return pCache->nMax;
+ return numberOfCachePages(pCache);
}
#endif
@@ -35748,10 +44385,55 @@ SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){
** Set the suggested cache-size value.
*/
SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
- pCache->nMax = mxPage;
- if( pCache->pCache ){
- sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage);
+ assert( pCache->pCache!=0 );
+ pCache->szCache = mxPage;
+ sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache,
+ numberOfCachePages(pCache));
+}
+
+/*
+** Set the suggested cache-spill value. Make no changes if if the
+** argument is zero. Return the effective cache-spill size, which will
+** be the larger of the szSpill and szCache.
+*/
+SQLITE_PRIVATE int sqlite3PcacheSetSpillsize(PCache *p, int mxPage){
+ int res;
+ assert( p->pCache!=0 );
+ if( mxPage ){
+ if( mxPage<0 ){
+ mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra));
+ }
+ p->szSpill = mxPage;
}
+ res = numberOfCachePages(p);
+ if( res<p->szSpill ) res = p->szSpill;
+ return res;
+}
+
+/*
+** Free up as much memory as possible from the page cache.
+*/
+SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){
+ assert( pCache->pCache!=0 );
+ sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache);
+}
+
+/*
+** Return the size of the header added by this middleware layer
+** in the page-cache hierarchy.
+*/
+SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); }
+
+/*
+** Return the number of dirty pages currently in the cache, as a percentage
+** of the configured cache size.
+*/
+SQLITE_PRIVATE int sqlite3PCachePercentDirty(PCache *pCache){
+ PgHdr *pDirty;
+ int nDirty = 0;
+ int nCache = numberOfCachePages(pCache);
+ for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext) nDirty++;
+ return nCache ? (int)(((i64)nDirty * 100) / nCache) : 0;
}
#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
@@ -35785,21 +44467,100 @@ SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHd
** This file implements the default page cache implementation (the
** sqlite3_pcache interface). It also contains part of the implementation
** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features.
-** If the default page cache implementation is overriden, then neither of
+** If the default page cache implementation is overridden, then neither of
** these two features are available.
+**
+** A Page cache line looks like this:
+**
+** -------------------------------------------------------------
+** | database page content | PgHdr1 | MemPage | PgHdr |
+** -------------------------------------------------------------
+**
+** The database page content is up front (so that buffer overreads tend to
+** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage
+** is the extension added by the btree.c module containing information such
+** as the database page number and how that database page is used. PgHdr
+** is added by the pcache.c layer and contains information used to keep track
+** of which pages are "dirty". PgHdr1 is an extension added by this
+** module (pcache1.c). The PgHdr1 header is a subclass of sqlite3_pcache_page.
+** PgHdr1 contains information needed to look up a page by its page number.
+** The superclass sqlite3_pcache_page.pBuf points to the start of the
+** database page content and sqlite3_pcache_page.pExtra points to PgHdr.
+**
+** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at
+** runtime using sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The
+** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this
+** size can vary according to architecture, compile-time options, and
+** SQLite library version number.
+**
+** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained
+** using a separate memory allocation from the database page content. This
+** seeks to overcome the "clownshoe" problem (also called "internal
+** fragmentation" in academic literature) of allocating a few bytes more
+** than a power of two with the memory allocator rounding up to the next
+** power of two, and leaving the rounded-up space unused.
+**
+** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates
+** with this module. Information is passed back and forth as PgHdr1 pointers.
+**
+** The pcache.c and pager.c modules deal pointers to PgHdr objects.
+** The btree.c module deals with pointers to MemPage objects.
+**
+** SOURCE OF PAGE CACHE MEMORY:
+**
+** Memory for a page might come from any of three sources:
+**
+** (1) The general-purpose memory allocator - sqlite3Malloc()
+** (2) Global page-cache memory provided using sqlite3_config() with
+** SQLITE_CONFIG_PAGECACHE.
+** (3) PCache-local bulk allocation.
+**
+** The third case is a chunk of heap memory (defaulting to 100 pages worth)
+** that is allocated when the page cache is created. The size of the local
+** bulk allocation can be adjusted using
+**
+** sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N).
+**
+** If N is positive, then N pages worth of memory are allocated using a single
+** sqlite3Malloc() call and that memory is used for the first N pages allocated.
+** Or if N is negative, then -1024*N bytes of memory are allocated and used
+** for as many pages as can be accomodated.
+**
+** Only one of (2) or (3) can be used. Once the memory available to (2) or
+** (3) is exhausted, subsequent allocations fail over to the general-purpose
+** memory allocator (1).
+**
+** Earlier versions of SQLite used only methods (1) and (2). But experiments
+** show that method (3) with N==100 provides about a 5% performance boost for
+** common workloads.
*/
-
+/* #include "sqliteInt.h" */
typedef struct PCache1 PCache1;
typedef struct PgHdr1 PgHdr1;
typedef struct PgFreeslot PgFreeslot;
typedef struct PGroup PGroup;
-typedef struct PGroupBlock PGroupBlock;
-typedef struct PGroupBlockList PGroupBlockList;
+/*
+** Each cache entry is represented by an instance of the following
+** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
+** PgHdr1.pCache->szPage bytes is allocated directly before this structure
+** in memory.
+*/
+struct PgHdr1 {
+ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */
+ unsigned int iKey; /* Key value (page number) */
+ u8 isPinned; /* Page in use, not on the LRU list */
+ u8 isBulkLocal; /* This page from bulk local storage */
+ u8 isAnchor; /* This is the PGroup.lru element */
+ PgHdr1 *pNext; /* Next in hash table chain */
+ PCache1 *pCache; /* Cache that currently owns this page */
+ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
+ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
+};
/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set
-** of one or more PCaches that are able to recycle each others unpinned
+** of one or more PCaches that are able to recycle each other's unpinned
** pages when they are under memory pressure. A PGroup is an instance of
** the following object.
**
@@ -35814,7 +44575,7 @@ typedef struct PGroupBlockList PGroupBlockList;
** Mode 1 uses more memory (since PCache instances are not able to rob
** unused pages from other PCaches) but it also operates without a mutex,
** and is therefore often faster. Mode 2 requires a mutex in order to be
-** threadsafe, but is able recycle pages more efficient.
+** threadsafe, but recycles pages more efficiently.
**
** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single
** PGroup which is the pcache1.grp global variable and its mutex is
@@ -35822,71 +44583,13 @@ typedef struct PGroupBlockList PGroupBlockList;
*/
struct PGroup {
sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */
- int nMaxPage; /* Sum of nMax for purgeable caches */
- int nMinPage; /* Sum of nMin for purgeable caches */
- int mxPinned; /* nMaxpage + 10 - nMinPage */
- int nCurrentPage; /* Number of purgeable pages allocated */
- PgHdr1 *pLruHead, *pLruTail; /* LRU list of unpinned pages */
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- int isBusy; /* Do not run ReleaseMemory() if true */
- PGroupBlockList *pBlockList; /* List of block-lists for this group */
-#endif
+ unsigned int nMaxPage; /* Sum of nMax for purgeable caches */
+ unsigned int nMinPage; /* Sum of nMin for purgeable caches */
+ unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */
+ unsigned int nCurrentPage; /* Number of purgeable pages allocated */
+ PgHdr1 lru; /* The beginning and end of the LRU list */
};
-/*
-** If SQLITE_PAGECACHE_BLOCKALLOC is defined when the library is built,
-** each PGroup structure has a linked list of the the following starting
-** at PGroup.pBlockList. There is one entry for each distinct page-size
-** currently used by members of the PGroup (i.e. 1024 bytes, 4096 bytes
-** etc.). Variable PGroupBlockList.nByte is set to the actual allocation
-** size requested by each pcache, which is the database page-size plus
-** the various header structures used by the pcache, pager and btree layers.
-** Usually around (pgsz+200) bytes.
-**
-** This size (pgsz+200) bytes is not allocated efficiently by some
-** implementations of malloc. In particular, some implementations are only
-** able to allocate blocks of memory chunks of 2^N bytes, where N is some
-** integer value. Since the page-size is a power of 2, this means we
-** end up wasting (pgsz-200) bytes in each allocation.
-**
-** If SQLITE_PAGECACHE_BLOCKALLOC is defined, the (pgsz+200) byte blocks
-** are not allocated directly. Instead, blocks of roughly M*(pgsz+200) bytes
-** are requested from malloc allocator. After a block is returned,
-** sqlite3MallocSize() is used to determine how many (pgsz+200) byte
-** allocations can fit in the space returned by malloc(). This value may
-** be more than M.
-**
-** The blocks are stored in a doubly-linked list. Variable PGroupBlock.nEntry
-** contains the number of allocations that will fit in the aData[] space.
-** nEntry is limited to the number of bits in bitmask mUsed. If a slot
-** within aData is in use, the corresponding bit in mUsed is set. Thus
-** when (mUsed+1==(1 << nEntry)) the block is completely full.
-**
-** Each time a slot within a block is freed, the block is moved to the start
-** of the linked-list. And if a block becomes completely full, then it is
-** moved to the end of the list. As a result, when searching for a free
-** slot, only the first block in the list need be examined. If it is full,
-** then it is guaranteed that all blocks are full.
-*/
-struct PGroupBlockList {
- int nByte; /* Size of each allocation in bytes */
- PGroupBlock *pFirst; /* First PGroupBlock in list */
- PGroupBlock *pLast; /* Last PGroupBlock in list */
- PGroupBlockList *pNext; /* Next block-list attached to group */
-};
-
-struct PGroupBlock {
- Bitmask mUsed; /* Mask of used slots */
- int nEntry; /* Maximum number of allocations in aData[] */
- u8 *aData; /* Pointer to data block */
- PGroupBlock *pNext; /* Next PGroupBlock in list */
- PGroupBlock *pPrev; /* Previous PGroupBlock in list */
- PGroupBlockList *pList; /* Owner list */
-};
-
-/* Minimum value for PGroupBlock.nEntry */
-#define PAGECACHE_BLOCKALLOC_MINENTRY 15
-
/* Each page cache is an instance of the following object. Every
** open database file (including each in-memory database and each
** temporary or transient database) has a single page cache which
@@ -35898,15 +44601,18 @@ struct PGroupBlock {
struct PCache1 {
/* Cache configuration parameters. Page size (szPage) and the purgeable
** flag (bPurgeable) are set when the cache is created. nMax may be
- ** modified at any time by a call to the pcache1CacheSize() method.
+ ** modified at any time by a call to the pcache1Cachesize() method.
** The PGroup mutex must be held when accessing nMax.
*/
PGroup *pGroup; /* PGroup this cache belongs to */
- int szPage; /* Size of allocated pages in bytes */
+ int szPage; /* Size of database content section */
+ int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */
+ int szAlloc; /* Total size of one pcache line */
int bPurgeable; /* True if cache is purgeable */
unsigned int nMin; /* Minimum number of pages reserved */
unsigned int nMax; /* Configured "cache_size" value */
unsigned int n90pct; /* nMax*9/10 */
+ unsigned int iMaxKey; /* Largest key seen since xTruncate() */
/* Hash table of all pages. The following variables may only be accessed
** when the accessor is holding the PGroup mutex.
@@ -35915,27 +44621,13 @@ struct PCache1 {
unsigned int nPage; /* Total number of pages in apHash */
unsigned int nHash; /* Number of slots in apHash[] */
PgHdr1 **apHash; /* Hash table for fast lookup by key */
-
- unsigned int iMaxKey; /* Largest key seen since xTruncate() */
-};
-
-/*
-** Each cache entry is represented by an instance of the following
-** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated
-** directly before this structure in memory (see the PGHDR1_TO_PAGE()
-** macro below).
-*/
-struct PgHdr1 {
- unsigned int iKey; /* Key value (page number) */
- PgHdr1 *pNext; /* Next in hash table chain */
- PCache1 *pCache; /* Cache that currently owns this page */
- PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */
- PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */
+ PgHdr1 *pFree; /* List of unused pcache-local pages */
+ void *pBulk; /* Bulk memory used by pcache-local */
};
/*
-** Free slots in the allocator used to divide up the buffer provided using
-** the SQLITE_CONFIG_PAGECACHE mechanism.
+** Free slots in the allocator used to divide up the global page cache
+** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism.
*/
struct PgFreeslot {
PgFreeslot *pNext; /* Next free slot */
@@ -35953,14 +44645,16 @@ static SQLITE_WSD struct PCacheGlobal {
** The nFreeSlot and pFree values do require mutex protection.
*/
int isInit; /* True if initialized */
+ int separateCache; /* Use a new PGroup for each PCache */
+ int nInitPage; /* Initial bulk allocation size */
int szSlot; /* Size of each free slot */
int nSlot; /* The number of pcache slots */
int nReserve; /* Try to keep nFreeSlot above this */
- void *pStart, *pEnd; /* Bounds of pagecache malloc range */
+ void *pStart, *pEnd; /* Bounds of global page cache memory */
/* Above requires no mutex. Use mutex below for variable that follow. */
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
- int nFreeSlot; /* Number of unused pcache slots */
PgFreeslot *pFree; /* Free page blocks */
+ int nFreeSlot; /* Number of unused pcache slots */
/* The following value requires a mutex to change. We skip the mutex on
** reading because (1) most platforms read a 32-bit integer atomically and
** (2) even if an incorrect value is read, no great harm is done since this
@@ -35976,40 +44670,22 @@ static SQLITE_WSD struct PCacheGlobal {
#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
/*
-** When a PgHdr1 structure is allocated, the associated PCache1.szPage
-** bytes of data are located directly before it in memory (i.e. the total
-** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
-** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
-** an argument and returns a pointer to the associated block of szPage
-** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
-** a pointer to a block of szPage bytes of data and the return value is
-** a pointer to the associated PgHdr1 structure.
-**
-** assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X );
-*/
-#define PGHDR1_TO_PAGE(p) (void*)(((char*)p) - p->pCache->szPage)
-#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
-
-/*
-** Blocks used by the SQLITE_PAGECACHE_BLOCKALLOC blocks to store/retrieve
-** a PGroupBlock pointer based on a pointer to a page buffer.
-*/
-#define PAGE_SET_BLOCKPTR(pCache, pPg, pBlock) \
- ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) = pBlock )
-
-#define PAGE_GET_BLOCKPTR(pCache, pPg) \
- ( *(PGroupBlock **)&(((u8*)pPg)[sizeof(PgHdr1) + pCache->szPage]) )
-
-
-/*
** Macros to enter and leave the PCache LRU mutex.
*/
-#define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
-#define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
+#if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
+# define pcache1EnterMutex(X) assert((X)->mutex==0)
+# define pcache1LeaveMutex(X) assert((X)->mutex==0)
+# define PCACHE1_MIGHT_USE_GROUP_MUTEX 0
+#else
+# define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
+# define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
+# define PCACHE1_MIGHT_USE_GROUP_MUTEX 1
+#endif
/******************************************************************************/
/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/
+
/*
** This function is called during initialization if a static buffer is
** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE
@@ -36022,6 +44698,7 @@ static SQLITE_WSD struct PCacheGlobal {
SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
if( pcache1.isInit ){
PgFreeslot *p;
+ if( pBuf==0 ) sz = n = 0;
sz = ROUNDDOWN8(sz);
pcache1.szSlot = sz;
pcache1.nSlot = pcache1.nFreeSlot = n;
@@ -36040,6 +44717,44 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
}
/*
+** Try to initialize the pCache->pFree and pCache->pBulk fields. Return
+** true if pCache->pFree ends up containing one or more free pages.
+*/
+static int pcache1InitBulk(PCache1 *pCache){
+ i64 szBulk;
+ char *zBulk;
+ if( pcache1.nInitPage==0 ) return 0;
+ /* Do not bother with a bulk allocation if the cache size very small */
+ if( pCache->nMax<3 ) return 0;
+ sqlite3BeginBenignMalloc();
+ if( pcache1.nInitPage>0 ){
+ szBulk = pCache->szAlloc * (i64)pcache1.nInitPage;
+ }else{
+ szBulk = -1024 * (i64)pcache1.nInitPage;
+ }
+ if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){
+ szBulk = pCache->szAlloc*(i64)pCache->nMax;
+ }
+ zBulk = pCache->pBulk = sqlite3Malloc( szBulk );
+ sqlite3EndBenignMalloc();
+ if( zBulk ){
+ int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc;
+ int i;
+ for(i=0; i<nBulk; i++){
+ PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage];
+ pX->page.pBuf = zBulk;
+ pX->page.pExtra = &pX[1];
+ pX->isBulkLocal = 1;
+ pX->isAnchor = 0;
+ pX->pNext = pCache->pFree;
+ pCache->pFree = pX;
+ zBulk += pCache->szAlloc;
+ }
+ }
+ return pCache->pFree!=0;
+}
+
+/*
** Malloc function used within this file to allocate space from the buffer
** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no
** such buffer exists or there is no space left in it, this function falls
@@ -36051,7 +44766,6 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
static void *pcache1Alloc(int nByte){
void *p = 0;
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
- sqlite3StatusSet(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
if( nByte<=pcache1.szSlot ){
sqlite3_mutex_enter(pcache1.mutex);
p = (PgHdr1 *)pcache1.pFree;
@@ -36060,7 +44774,8 @@ static void *pcache1Alloc(int nByte){
pcache1.nFreeSlot--;
pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
assert( pcache1.nFreeSlot>=0 );
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, 1);
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
+ sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
}
sqlite3_mutex_leave(pcache1.mutex);
}
@@ -36069,12 +44784,15 @@ static void *pcache1Alloc(int nByte){
** it from sqlite3Malloc instead.
*/
p = sqlite3Malloc(nByte);
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
if( p ){
int sz = sqlite3MallocSize(p);
sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
+ sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
sqlite3_mutex_leave(pcache1.mutex);
}
+#endif
sqlite3MemdebugSetType(p, MEMTYPE_PCACHE);
}
return p;
@@ -36085,10 +44803,10 @@ static void *pcache1Alloc(int nByte){
*/
static void pcache1Free(void *p){
if( p==0 ) return;
- if( p>=pcache1.pStart && p<pcache1.pEnd ){
+ if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){
PgFreeslot *pSlot;
sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_USED, -1);
+ sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
pSlot = (PgFreeslot*)p;
pSlot->pNext = pcache1.pFree;
pcache1.pFree = pSlot;
@@ -36097,13 +44815,17 @@ static void pcache1Free(void *p){
assert( pcache1.nFreeSlot<=pcache1.nSlot );
sqlite3_mutex_leave(pcache1.mutex);
}else{
- int iSize;
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- iSize = sqlite3MallocSize(p);
- sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -iSize);
- sqlite3_mutex_leave(pcache1.mutex);
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS
+ {
+ int nFreed = 0;
+ nFreed = sqlite3MallocSize(p);
+ sqlite3_mutex_enter(pcache1.mutex);
+ sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed);
+ sqlite3_mutex_leave(pcache1.mutex);
+ }
+#endif
sqlite3_free(p);
}
}
@@ -36126,214 +44848,75 @@ static int pcache1MemSize(void *p){
}
#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
-/*
-** The block pBlock belongs to list pList but is not currently linked in.
-** Insert it into the start of the list.
-*/
-static void addBlockToList(PGroupBlockList *pList, PGroupBlock *pBlock){
- pBlock->pPrev = 0;
- pBlock->pNext = pList->pFirst;
- pList->pFirst = pBlock;
- if( pBlock->pNext ){
- pBlock->pNext->pPrev = pBlock;
- }else{
- assert( pList->pLast==0 );
- pList->pLast = pBlock;
- }
-}
-
-/*
-** If there are no blocks in the list headed by pList, remove pList
-** from the pGroup->pBlockList list and free it with sqlite3_free().
-*/
-static void freeListIfEmpty(PGroup *pGroup, PGroupBlockList *pList){
- assert( sqlite3_mutex_held(pGroup->mutex) );
- if( pList->pFirst==0 ){
- PGroupBlockList **pp;
- for(pp=&pGroup->pBlockList; *pp!=pList; pp=&(*pp)->pNext);
- *pp = (*pp)->pNext;
- sqlite3_free(pList);
- }
-}
-#endif /* SQLITE_PAGECACHE_BLOCKALLOC */
-
/*
** Allocate a new page object initially associated with cache pCache.
*/
-static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
- int nByte = sizeof(PgHdr1) + pCache->szPage;
- void *pPg = 0;
- PgHdr1 *p;
+static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){
+ PgHdr1 *p = 0;
+ void *pPg;
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- PGroup *pGroup = pCache->pGroup;
- PGroupBlockList *pList;
- PGroupBlock *pBlock;
- int i;
-
- nByte += sizeof(PGroupBlockList *);
- nByte = ROUND8(nByte);
-
- for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
- if( pList->nByte==nByte ) break;
- }
- if( pList==0 ){
- PGroupBlockList *pNew;
- assert( pGroup->isBusy==0 );
- assert( sqlite3_mutex_held(pGroup->mutex) );
- pGroup->isBusy = 1; /* Disable sqlite3PcacheReleaseMemory() */
- pNew = (PGroupBlockList *)sqlite3MallocZero(sizeof(PGroupBlockList));
- pGroup->isBusy = 0; /* Reenable sqlite3PcacheReleaseMemory() */
- if( pNew==0 ){
- /* malloc() failure. Return early. */
- return 0;
- }
-#ifdef SQLITE_DEBUG
- for(pList=pGroup->pBlockList; pList; pList=pList->pNext){
- assert( pList->nByte!=nByte );
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
+ if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){
+ p = pCache->pFree;
+ pCache->pFree = p->pNext;
+ p->pNext = 0;
+ }else{
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
+ /* The group mutex must be released before pcache1Alloc() is called. This
+ ** is because it might call sqlite3_release_memory(), which assumes that
+ ** this mutex is not held. */
+ assert( pcache1.separateCache==0 );
+ assert( pCache->pGroup==&pcache1.grp );
+ pcache1LeaveMutex(pCache->pGroup);
+#endif
+ if( benignMalloc ){ sqlite3BeginBenignMalloc(); }
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER
+ pPg = pcache1Alloc(pCache->szPage);
+ p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
+ if( !pPg || !p ){
+ pcache1Free(pPg);
+ sqlite3_free(p);
+ pPg = 0;
}
+#else
+ pPg = pcache1Alloc(pCache->szAlloc);
+ p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
#endif
- pNew->nByte = nByte;
- pNew->pNext = pGroup->pBlockList;
- pGroup->pBlockList = pNew;
- pList = pNew;
- }
-
- pBlock = pList->pFirst;
- if( pBlock==0 || pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) ){
- int sz;
-
- /* Allocate a new block. Try to allocate enough space for the PGroupBlock
- ** structure and MINENTRY allocations of nByte bytes each. If the
- ** allocator returns more memory than requested, then more than MINENTRY
- ** allocations may fit in it. */
- assert( sqlite3_mutex_held(pGroup->mutex) );
- pcache1LeaveMutex(pCache->pGroup);
- sz = sizeof(PGroupBlock) + PAGECACHE_BLOCKALLOC_MINENTRY * nByte;
- pBlock = (PGroupBlock *)sqlite3Malloc(sz);
+ if( benignMalloc ){ sqlite3EndBenignMalloc(); }
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
pcache1EnterMutex(pCache->pGroup);
-
- if( !pBlock ){
- freeListIfEmpty(pGroup, pList);
- return 0;
- }
- pBlock->nEntry = (sqlite3MallocSize(pBlock) - sizeof(PGroupBlock)) / nByte;
- if( pBlock->nEntry>=BMS ){
- pBlock->nEntry = BMS-1;
- }
- pBlock->pList = pList;
- pBlock->mUsed = 0;
- pBlock->aData = (u8 *)&pBlock[1];
- addBlockToList(pList, pBlock);
-
- sz = sqlite3MallocSize(pBlock);
- sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz);
- sqlite3_mutex_leave(pcache1.mutex);
- }
-
- for(i=0; pPg==0 && ALWAYS(i<pBlock->nEntry); i++){
- if( 0==(pBlock->mUsed & ((Bitmask)1<<i)) ){
- pBlock->mUsed |= ((Bitmask)1<<i);
- pPg = (void *)&pBlock->aData[pList->nByte * i];
- }
- }
- assert( pPg );
- PAGE_SET_BLOCKPTR(pCache, pPg, pBlock);
-
- /* If the block is now full, shift it to the end of the list */
- if( pBlock->mUsed==(((Bitmask)1<<pBlock->nEntry)-1) && pList->pLast!=pBlock ){
- assert( pList->pFirst==pBlock );
- assert( pBlock->pPrev==0 );
- assert( pList->pLast->pNext==0 );
- pList->pFirst = pBlock->pNext;
- pList->pFirst->pPrev = 0;
- pBlock->pPrev = pList->pLast;
- pBlock->pNext = 0;
- pList->pLast->pNext = pBlock;
- pList->pLast = pBlock;
+#endif
+ if( pPg==0 ) return 0;
+ p->page.pBuf = pPg;
+ p->page.pExtra = &p[1];
+ p->isBulkLocal = 0;
+ p->isAnchor = 0;
}
- p = PAGE_TO_PGHDR1(pCache, pPg);
if( pCache->bPurgeable ){
pCache->pGroup->nCurrentPage++;
}
-#else
- /* The group mutex must be released before pcache1Alloc() is called. This
- ** is because it may call sqlite3_release_memory(), which assumes that
- ** this mutex is not held. */
- assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
- pcache1LeaveMutex(pCache->pGroup);
- pPg = pcache1Alloc(nByte);
- pcache1EnterMutex(pCache->pGroup);
- if( pPg ){
- p = PAGE_TO_PGHDR1(pCache, pPg);
- if( pCache->bPurgeable ){
- pCache->pGroup->nCurrentPage++;
- }
- }else{
- p = 0;
- }
-#endif
return p;
}
/*
** Free a page object allocated by pcache1AllocPage().
-**
-** The pointer is allowed to be NULL, which is prudent. But it turns out
-** that the current implementation happens to never call this routine
-** with a NULL pointer, so we mark the NULL test with ALWAYS().
*/
static void pcache1FreePage(PgHdr1 *p){
- if( ALWAYS(p) ){
- PCache1 *pCache = p->pCache;
- void *pPg = PGHDR1_TO_PAGE(p);
-
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- PGroupBlock *pBlock = PAGE_GET_BLOCKPTR(pCache, pPg);
- PGroupBlockList *pList = pBlock->pList;
- int i = ((u8 *)pPg - pBlock->aData) / pList->nByte;
-
- assert( pPg==(void *)&pBlock->aData[i*pList->nByte] );
- assert( pBlock->mUsed & ((Bitmask)1<<i) );
- pBlock->mUsed &= ~((Bitmask)1<<i);
-
- /* Remove the block from the list. If it is completely empty, free it.
- ** Or if it is not completely empty, re-insert it at the start of the
- ** list. */
- if( pList->pFirst==pBlock ){
- pList->pFirst = pBlock->pNext;
- if( pList->pFirst ) pList->pFirst->pPrev = 0;
- }else{
- pBlock->pPrev->pNext = pBlock->pNext;
- }
- if( pList->pLast==pBlock ){
- pList->pLast = pBlock->pPrev;
- if( pList->pLast ) pList->pLast->pNext = 0;
- }else{
- pBlock->pNext->pPrev = pBlock->pPrev;
- }
-
- if( pBlock->mUsed==0 ){
- PGroup *pGroup = p->pCache->pGroup;
-
- int sz = sqlite3MallocSize(pBlock);
- sqlite3_mutex_enter(pcache1.mutex);
- sqlite3StatusAdd(SQLITE_STATUS_PAGECACHE_OVERFLOW, -sz);
- sqlite3_mutex_leave(pcache1.mutex);
- freeListIfEmpty(pGroup, pList);
- sqlite3_free(pBlock);
- }else{
- addBlockToList(pList, pBlock);
- }
-#else
- assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
- pcache1Free(pPg);
+ PCache1 *pCache;
+ assert( p!=0 );
+ pCache = p->pCache;
+ assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
+ if( p->isBulkLocal ){
+ p->pNext = pCache->pFree;
+ pCache->pFree = p;
+ }else{
+ pcache1Free(p->page.pBuf);
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER
+ sqlite3_free(p);
#endif
- if( pCache->bPurgeable ){
- pCache->pGroup->nCurrentPage--;
- }
+ }
+ if( pCache->bPurgeable ){
+ pCache->pGroup->nCurrentPage--;
}
}
@@ -36365,13 +44948,13 @@ SQLITE_PRIVATE void sqlite3PageFree(void *p){
** for all page cache needs and we should not need to spill the
** allocation onto the heap.
**
-** Or, the heap is used for all page cache memory put the heap is
+** Or, the heap is used for all page cache memory but the heap is
** under memory pressure, then again it is desirable to avoid
** allocating a new page cache entry in order to avoid stressing
** the heap even further.
*/
static int pcache1UnderMemoryPressure(PCache1 *pCache){
- if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){
+ if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
return pcache1.bUnderPressure;
}else{
return sqlite3HeapNearlyFull();
@@ -36387,7 +44970,7 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){
**
** The PCache mutex must be held when this function is called.
*/
-static int pcache1ResizeHash(PCache1 *p){
+static void pcache1ResizeHash(PCache1 *p){
PgHdr1 **apNew;
unsigned int nNew;
unsigned int i;
@@ -36401,11 +44984,10 @@ static int pcache1ResizeHash(PCache1 *p){
pcache1LeaveMutex(p->pGroup);
if( p->nHash ){ sqlite3BeginBenignMalloc(); }
- apNew = (PgHdr1 **)sqlite3_malloc(sizeof(PgHdr1 *)*nNew);
+ apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew);
if( p->nHash ){ sqlite3EndBenignMalloc(); }
pcache1EnterMutex(p->pGroup);
if( apNew ){
- memset(apNew, 0, sizeof(PgHdr1 *)*nNew);
for(i=0; i<p->nHash; i++){
PgHdr1 *pPage;
PgHdr1 *pNext = p->apHash[i];
@@ -36420,8 +45002,6 @@ static int pcache1ResizeHash(PCache1 *p){
p->apHash = apNew;
p->nHash = nNew;
}
-
- return (p->apHash ? SQLITE_OK : SQLITE_NOMEM);
}
/*
@@ -36430,44 +45010,36 @@ static int pcache1ResizeHash(PCache1 *p){
** LRU list, then this function is a no-op.
**
** The PGroup mutex must be held when this function is called.
-**
-** If pPage is NULL then this routine is a no-op.
*/
-static void pcache1PinPage(PgHdr1 *pPage){
+static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){
PCache1 *pCache;
- PGroup *pGroup;
- if( pPage==0 ) return;
+ assert( pPage!=0 );
+ assert( pPage->isPinned==0 );
pCache = pPage->pCache;
- pGroup = pCache->pGroup;
- assert( sqlite3_mutex_held(pGroup->mutex) );
- if( pPage->pLruNext || pPage==pGroup->pLruTail ){
- if( pPage->pLruPrev ){
- pPage->pLruPrev->pLruNext = pPage->pLruNext;
- }
- if( pPage->pLruNext ){
- pPage->pLruNext->pLruPrev = pPage->pLruPrev;
- }
- if( pGroup->pLruHead==pPage ){
- pGroup->pLruHead = pPage->pLruNext;
- }
- if( pGroup->pLruTail==pPage ){
- pGroup->pLruTail = pPage->pLruPrev;
- }
- pPage->pLruNext = 0;
- pPage->pLruPrev = 0;
- pPage->pCache->nRecyclable--;
- }
+ assert( pPage->pLruNext );
+ assert( pPage->pLruPrev );
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
+ pPage->pLruPrev->pLruNext = pPage->pLruNext;
+ pPage->pLruNext->pLruPrev = pPage->pLruPrev;
+ pPage->pLruNext = 0;
+ pPage->pLruPrev = 0;
+ pPage->isPinned = 1;
+ assert( pPage->isAnchor==0 );
+ assert( pCache->pGroup->lru.isAnchor==1 );
+ pCache->nRecyclable--;
+ return pPage;
}
/*
** Remove the page supplied as an argument from the hash table
** (PCache1.apHash structure) that it is currently stored in.
+** Also free the page if freePage is true.
**
** The PGroup mutex must be held when this function is called.
*/
-static void pcache1RemoveFromHash(PgHdr1 *pPage){
+static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){
unsigned int h;
PCache1 *pCache = pPage->pCache;
PgHdr1 **pp;
@@ -36478,20 +45050,28 @@ static void pcache1RemoveFromHash(PgHdr1 *pPage){
*pp = (*pp)->pNext;
pCache->nPage--;
+ if( freeFlag ) pcache1FreePage(pPage);
}
/*
** If there are currently more than nMaxPage pages allocated, try
** to recycle pages to reduce the number allocated to nMaxPage.
*/
-static void pcache1EnforceMaxPage(PGroup *pGroup){
+static void pcache1EnforceMaxPage(PCache1 *pCache){
+ PGroup *pGroup = pCache->pGroup;
+ PgHdr1 *p;
assert( sqlite3_mutex_held(pGroup->mutex) );
- while( pGroup->nCurrentPage>pGroup->nMaxPage && pGroup->pLruTail ){
- PgHdr1 *p = pGroup->pLruTail;
+ while( pGroup->nCurrentPage>pGroup->nMaxPage
+ && (p=pGroup->lru.pLruPrev)->isAnchor==0
+ ){
assert( p->pCache->pGroup==pGroup );
+ assert( p->isPinned==0 );
pcache1PinPage(p);
- pcache1RemoveFromHash(p);
- pcache1FreePage(p);
+ pcache1RemoveFromHash(p, 1);
+ }
+ if( pCache->nPage==0 && pCache->pBulk ){
+ sqlite3_free(pCache->pBulk);
+ pCache->pBulk = pCache->pFree = 0;
}
}
@@ -36506,25 +45086,45 @@ static void pcache1TruncateUnsafe(
PCache1 *pCache, /* The cache to truncate */
unsigned int iLimit /* Drop pages with this pgno or larger */
){
- TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */
- unsigned int h;
+ TESTONLY( int nPage = 0; ) /* To assert pCache->nPage is correct */
+ unsigned int h, iStop;
assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
- for(h=0; h<pCache->nHash; h++){
- PgHdr1 **pp = &pCache->apHash[h];
+ assert( pCache->iMaxKey >= iLimit );
+ assert( pCache->nHash > 0 );
+ if( pCache->iMaxKey - iLimit < pCache->nHash ){
+ /* If we are just shaving the last few pages off the end of the
+ ** cache, then there is no point in scanning the entire hash table.
+ ** Only scan those hash slots that might contain pages that need to
+ ** be removed. */
+ h = iLimit % pCache->nHash;
+ iStop = pCache->iMaxKey % pCache->nHash;
+ TESTONLY( nPage = -10; ) /* Disable the pCache->nPage validity check */
+ }else{
+ /* This is the general case where many pages are being removed.
+ ** It is necessary to scan the entire hash table */
+ h = pCache->nHash/2;
+ iStop = h - 1;
+ }
+ for(;;){
+ PgHdr1 **pp;
PgHdr1 *pPage;
+ assert( h<pCache->nHash );
+ pp = &pCache->apHash[h];
while( (pPage = *pp)!=0 ){
if( pPage->iKey>=iLimit ){
pCache->nPage--;
*pp = pPage->pNext;
- pcache1PinPage(pPage);
+ if( !pPage->isPinned ) pcache1PinPage(pPage);
pcache1FreePage(pPage);
}else{
pp = &pPage->pNext;
- TESTONLY( nPage++; )
+ TESTONLY( if( nPage>=0 ) nPage++; )
}
}
+ if( h==iStop ) break;
+ h = (h+1) % pCache->nHash;
}
- assert( pCache->nPage==nPage );
+ assert( nPage<0 || pCache->nPage==(unsigned)nPage );
}
/******************************************************************************/
@@ -36537,9 +45137,44 @@ static int pcache1Init(void *NotUsed){
UNUSED_PARAMETER(NotUsed);
assert( pcache1.isInit==0 );
memset(&pcache1, 0, sizeof(pcache1));
+
+
+ /*
+ ** The pcache1.separateCache variable is true if each PCache has its own
+ ** private PGroup (mode-1). pcache1.separateCache is false if the single
+ ** PGroup in pcache1.grp is used for all page caches (mode-2).
+ **
+ ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
+ **
+ ** * Use a unified cache in single-threaded applications that have
+ ** configured a start-time buffer for use as page-cache memory using
+ ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL
+ ** pBuf argument.
+ **
+ ** * Otherwise use separate caches (mode-1)
+ */
+#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT)
+ pcache1.separateCache = 0;
+#elif SQLITE_THREADSAFE
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0
+ || sqlite3GlobalConfig.bCoreMutex>0;
+#else
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0;
+#endif
+
+#if SQLITE_THREADSAFE
if( sqlite3GlobalConfig.bCoreMutex ){
- pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU);
- pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM);
+ pcache1.grp.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_LRU);
+ pcache1.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PMEM);
+ }
+#endif
+ if( pcache1.separateCache
+ && sqlite3GlobalConfig.nPage!=0
+ && sqlite3GlobalConfig.pPage==0
+ ){
+ pcache1.nInitPage = sqlite3GlobalConfig.nPage;
+ }else{
+ pcache1.nInitPage = 0;
}
pcache1.grp.mxPinned = 10;
pcache1.isInit = 1;
@@ -36557,53 +45192,51 @@ static void pcache1Shutdown(void *NotUsed){
memset(&pcache1, 0, sizeof(pcache1));
}
+/* forward declaration */
+static void pcache1Destroy(sqlite3_pcache *p);
+
/*
** Implementation of the sqlite3_pcache.xCreate method.
**
** Allocate a new cache.
*/
-static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
+static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
PCache1 *pCache; /* The newly created page cache */
PGroup *pGroup; /* The group the new page cache will belong to */
int sz; /* Bytes of memory required to allocate the new cache */
- /*
- ** The seperateCache variable is true if each PCache has its own private
- ** PGroup. In other words, separateCache is true for mode (1) where no
- ** mutexing is required.
- **
- ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT
- **
- ** * Always use a unified cache in single-threaded applications
- **
- ** * Otherwise (if multi-threaded and ENABLE_MEMORY_MANAGEMENT is off)
- ** use separate caches (mode-1)
- */
-#if defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0
- const int separateCache = 0;
-#else
- int separateCache = sqlite3GlobalConfig.bCoreMutex>0;
-#endif
+ assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
+ assert( szExtra < 300 );
- sz = sizeof(PCache1) + sizeof(PGroup)*separateCache;
- pCache = (PCache1 *)sqlite3_malloc(sz);
+ sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache;
+ pCache = (PCache1 *)sqlite3MallocZero(sz);
if( pCache ){
- memset(pCache, 0, sz);
- if( separateCache ){
+ if( pcache1.separateCache ){
pGroup = (PGroup*)&pCache[1];
pGroup->mxPinned = 10;
}else{
pGroup = &pcache1.grp;
}
+ if( pGroup->lru.isAnchor==0 ){
+ pGroup->lru.isAnchor = 1;
+ pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru;
+ }
pCache->pGroup = pGroup;
pCache->szPage = szPage;
+ pCache->szExtra = szExtra;
+ pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1));
pCache->bPurgeable = (bPurgeable ? 1 : 0);
+ pcache1EnterMutex(pGroup);
+ pcache1ResizeHash(pCache);
if( bPurgeable ){
pCache->nMin = 10;
- pcache1EnterMutex(pGroup);
pGroup->nMinPage += pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
- pcache1LeaveMutex(pGroup);
+ }
+ pcache1LeaveMutex(pGroup);
+ if( pCache->nHash==0 ){
+ pcache1Destroy((sqlite3_pcache*)pCache);
+ pCache = 0;
}
}
return (sqlite3_pcache *)pCache;
@@ -36623,7 +45256,26 @@ static void pcache1Cachesize(sqlite3_pcache *p, int nMax){
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
pCache->nMax = nMax;
pCache->n90pct = pCache->nMax*9/10;
- pcache1EnforceMaxPage(pGroup);
+ pcache1EnforceMaxPage(pCache);
+ pcache1LeaveMutex(pGroup);
+ }
+}
+
+/*
+** Implementation of the sqlite3_pcache.xShrink method.
+**
+** Free up as much memory as possible.
+*/
+static void pcache1Shrink(sqlite3_pcache *p){
+ PCache1 *pCache = (PCache1*)p;
+ if( pCache->bPurgeable ){
+ PGroup *pGroup = pCache->pGroup;
+ int savedMaxPage;
+ pcache1EnterMutex(pGroup);
+ savedMaxPage = pGroup->nMaxPage;
+ pGroup->nMaxPage = 0;
+ pcache1EnforceMaxPage(pCache);
+ pGroup->nMaxPage = savedMaxPage;
pcache1LeaveMutex(pGroup);
}
}
@@ -36640,6 +45292,84 @@ static int pcache1Pagecount(sqlite3_pcache *p){
return n;
}
+
+/*
+** Implement steps 3, 4, and 5 of the pcache1Fetch() algorithm described
+** in the header of the pcache1Fetch() procedure.
+**
+** This steps are broken out into a separate procedure because they are
+** usually not needed, and by avoiding the stack initialization required
+** for these steps, the main pcache1Fetch() procedure can run faster.
+*/
+static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2(
+ PCache1 *pCache,
+ unsigned int iKey,
+ int createFlag
+){
+ unsigned int nPinned;
+ PGroup *pGroup = pCache->pGroup;
+ PgHdr1 *pPage = 0;
+
+ /* Step 3: Abort if createFlag is 1 but the cache is nearly full */
+ assert( pCache->nPage >= pCache->nRecyclable );
+ nPinned = pCache->nPage - pCache->nRecyclable;
+ assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
+ assert( pCache->n90pct == pCache->nMax*9/10 );
+ if( createFlag==1 && (
+ nPinned>=pGroup->mxPinned
+ || nPinned>=pCache->n90pct
+ || (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable<nPinned)
+ )){
+ return 0;
+ }
+
+ if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache);
+ assert( pCache->nHash>0 && pCache->apHash );
+
+ /* Step 4. Try to recycle a page. */
+ if( pCache->bPurgeable
+ && !pGroup->lru.pLruPrev->isAnchor
+ && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache))
+ ){
+ PCache1 *pOther;
+ pPage = pGroup->lru.pLruPrev;
+ assert( pPage->isPinned==0 );
+ pcache1RemoveFromHash(pPage, 0);
+ pcache1PinPage(pPage);
+ pOther = pPage->pCache;
+ if( pOther->szAlloc != pCache->szAlloc ){
+ pcache1FreePage(pPage);
+ pPage = 0;
+ }else{
+ pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable);
+ }
+ }
+
+ /* Step 5. If a usable page buffer has still not been found,
+ ** attempt to allocate a new one.
+ */
+ if( !pPage ){
+ pPage = pcache1AllocPage(pCache, createFlag==1);
+ }
+
+ if( pPage ){
+ unsigned int h = iKey % pCache->nHash;
+ pCache->nPage++;
+ pPage->iKey = iKey;
+ pPage->pNext = pCache->apHash[h];
+ pPage->pCache = pCache;
+ pPage->pLruPrev = 0;
+ pPage->pLruNext = 0;
+ pPage->isPinned = 1;
+ *(void **)pPage->page.pExtra = 0;
+ pCache->apHash[h] = pPage;
+ if( iKey>pCache->iMaxKey ){
+ pCache->iMaxKey = iKey;
+ }
+ }
+ return pPage;
+}
+
/*
** Implementation of the sqlite3_pcache.xFetch method.
**
@@ -36653,7 +45383,7 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** For a non-purgeable cache (a cache used as the storage for an in-memory
** database) there is really no difference between createFlag 1 and 2. So
** the calling function (pcache.c) will never have a createFlag of 1 on
-** a non-purgable cache.
+** a non-purgeable cache.
**
** There are three different approaches to obtaining space for a page,
** depending on the value of parameter createFlag (which may be 0, 1 or 2).
@@ -36693,106 +45423,80 @@ static int pcache1Pagecount(sqlite3_pcache *p){
** proceed to step 5.
**
** 5. Otherwise, allocate and return a new page buffer.
+**
+** There are two versions of this routine. pcache1FetchWithMutex() is
+** the general case. pcache1FetchNoMutex() is a faster implementation for
+** the common case where pGroup->mutex is NULL. The pcache1Fetch() wrapper
+** invokes the appropriate routine.
*/
-static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
- int nPinned;
+static PgHdr1 *pcache1FetchNoMutex(
+ sqlite3_pcache *p,
+ unsigned int iKey,
+ int createFlag
+){
PCache1 *pCache = (PCache1 *)p;
- PGroup *pGroup;
PgHdr1 *pPage = 0;
- assert( pCache->bPurgeable || createFlag!=1 );
- assert( pCache->bPurgeable || pCache->nMin==0 );
- assert( pCache->bPurgeable==0 || pCache->nMin==10 );
- assert( pCache->nMin==0 || pCache->bPurgeable );
- pcache1EnterMutex(pGroup = pCache->pGroup);
-
/* Step 1: Search the hash table for an existing entry. */
- if( pCache->nHash>0 ){
- unsigned int h = iKey % pCache->nHash;
- for(pPage=pCache->apHash[h]; pPage&&pPage->iKey!=iKey; pPage=pPage->pNext);
- }
-
- /* Step 2: Abort if no existing page is found and createFlag is 0 */
- if( pPage || createFlag==0 ){
- pcache1PinPage(pPage);
- goto fetch_out;
- }
-
- /* The pGroup local variable will normally be initialized by the
- ** pcache1EnterMutex() macro above. But if SQLITE_MUTEX_OMIT is defined,
- ** then pcache1EnterMutex() is a no-op, so we have to initialize the
- ** local variable here. Delaying the initialization of pGroup is an
- ** optimization: The common case is to exit the module before reaching
- ** this point.
- */
-#ifdef SQLITE_MUTEX_OMIT
- pGroup = pCache->pGroup;
-#endif
+ pPage = pCache->apHash[iKey % pCache->nHash];
+ while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; }
-
- /* Step 3: Abort if createFlag is 1 but the cache is nearly full */
- nPinned = pCache->nPage - pCache->nRecyclable;
- assert( nPinned>=0 );
- assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
- assert( pCache->n90pct == pCache->nMax*9/10 );
- if( createFlag==1 && (
- nPinned>=pGroup->mxPinned
- || nPinned>=(int)pCache->n90pct
- || pcache1UnderMemoryPressure(pCache)
- )){
- goto fetch_out;
- }
-
- if( pCache->nPage>=pCache->nHash && pcache1ResizeHash(pCache) ){
- goto fetch_out;
- }
-
- /* Step 4. Try to recycle a page. */
- if( pCache->bPurgeable && pGroup->pLruTail && (
- (pCache->nPage+1>=pCache->nMax)
- || pGroup->nCurrentPage>=pGroup->nMaxPage
- || pcache1UnderMemoryPressure(pCache)
- )){
- PCache1 *pOtherCache;
- pPage = pGroup->pLruTail;
- pcache1RemoveFromHash(pPage);
- pcache1PinPage(pPage);
- if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){
- pcache1FreePage(pPage);
- pPage = 0;
+ /* Step 2: If the page was found in the hash table, then return it.
+ ** If the page was not in the hash table and createFlag is 0, abort.
+ ** Otherwise (page not in hash and createFlag!=0) continue with
+ ** subsequent steps to try to create the page. */
+ if( pPage ){
+ if( !pPage->isPinned ){
+ return pcache1PinPage(pPage);
}else{
- pGroup->nCurrentPage -=
- (pOtherCache->bPurgeable - pCache->bPurgeable);
+ return pPage;
}
+ }else if( createFlag ){
+ /* Steps 3, 4, and 5 implemented by this subroutine */
+ return pcache1FetchStage2(pCache, iKey, createFlag);
+ }else{
+ return 0;
}
+}
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX
+static PgHdr1 *pcache1FetchWithMutex(
+ sqlite3_pcache *p,
+ unsigned int iKey,
+ int createFlag
+){
+ PCache1 *pCache = (PCache1 *)p;
+ PgHdr1 *pPage;
- /* Step 5. If a usable page buffer has still not been found,
- ** attempt to allocate a new one.
- */
- if( !pPage ){
- if( createFlag==1 ) sqlite3BeginBenignMalloc();
- pPage = pcache1AllocPage(pCache);
- if( createFlag==1 ) sqlite3EndBenignMalloc();
- }
-
- if( pPage ){
- unsigned int h = iKey % pCache->nHash;
- pCache->nPage++;
- pPage->iKey = iKey;
- pPage->pNext = pCache->apHash[h];
- pPage->pCache = pCache;
- pPage->pLruPrev = 0;
- pPage->pLruNext = 0;
- *(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
- pCache->apHash[h] = pPage;
- }
+ pcache1EnterMutex(pCache->pGroup);
+ pPage = pcache1FetchNoMutex(p, iKey, createFlag);
+ assert( pPage==0 || pCache->iMaxKey>=iKey );
+ pcache1LeaveMutex(pCache->pGroup);
+ return pPage;
+}
+#endif
+static sqlite3_pcache_page *pcache1Fetch(
+ sqlite3_pcache *p,
+ unsigned int iKey,
+ int createFlag
+){
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG)
+ PCache1 *pCache = (PCache1 *)p;
+#endif
-fetch_out:
- if( pPage && iKey>pCache->iMaxKey ){
- pCache->iMaxKey = iKey;
+ assert( offsetof(PgHdr1,page)==0 );
+ assert( pCache->bPurgeable || createFlag!=1 );
+ assert( pCache->bPurgeable || pCache->nMin==0 );
+ assert( pCache->bPurgeable==0 || pCache->nMin==10 );
+ assert( pCache->nMin==0 || pCache->bPurgeable );
+ assert( pCache->nHash>0 );
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX
+ if( pCache->pGroup->mutex ){
+ return (sqlite3_pcache_page*)pcache1FetchWithMutex(p, iKey, createFlag);
+ }else
+#endif
+ {
+ return (sqlite3_pcache_page*)pcache1FetchNoMutex(p, iKey, createFlag);
}
- pcache1LeaveMutex(pGroup);
- return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
}
@@ -36801,9 +45505,13 @@ fetch_out:
**
** Mark a page as unpinned (eligible for asynchronous recycling).
*/
-static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
+static void pcache1Unpin(
+ sqlite3_pcache *p,
+ sqlite3_pcache_page *pPg,
+ int reuseUnlikely
+){
PCache1 *pCache = (PCache1 *)p;
- PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
+ PgHdr1 *pPage = (PgHdr1 *)pPg;
PGroup *pGroup = pCache->pGroup;
assert( pPage->pCache==pCache );
@@ -36813,22 +45521,18 @@ static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
** part of the PGroup LRU list.
*/
assert( pPage->pLruPrev==0 && pPage->pLruNext==0 );
- assert( pGroup->pLruHead!=pPage && pGroup->pLruTail!=pPage );
+ assert( pPage->isPinned==1 );
if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){
- pcache1RemoveFromHash(pPage);
- pcache1FreePage(pPage);
+ pcache1RemoveFromHash(pPage, 1);
}else{
/* Add the page to the PGroup LRU list. */
- if( pGroup->pLruHead ){
- pGroup->pLruHead->pLruPrev = pPage;
- pPage->pLruNext = pGroup->pLruHead;
- pGroup->pLruHead = pPage;
- }else{
- pGroup->pLruTail = pPage;
- pGroup->pLruHead = pPage;
- }
+ PgHdr1 **ppFirst = &pGroup->lru.pLruNext;
+ pPage->pLruPrev = &pGroup->lru;
+ (pPage->pLruNext = *ppFirst)->pLruPrev = pPage;
+ *ppFirst = pPage;
pCache->nRecyclable++;
+ pPage->isPinned = 0;
}
pcache1LeaveMutex(pCache->pGroup);
@@ -36839,12 +45543,12 @@ static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
*/
static void pcache1Rekey(
sqlite3_pcache *p,
- void *pPg,
+ sqlite3_pcache_page *pPg,
unsigned int iOld,
unsigned int iNew
){
PCache1 *pCache = (PCache1 *)p;
- PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
+ PgHdr1 *pPage = (PgHdr1 *)pPg;
PgHdr1 **pp;
unsigned int h;
assert( pPage->iKey==iOld );
@@ -36897,12 +45601,15 @@ static void pcache1Destroy(sqlite3_pcache *p){
PGroup *pGroup = pCache->pGroup;
assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) );
pcache1EnterMutex(pGroup);
- pcache1TruncateUnsafe(pCache, 0);
+ if( pCache->nPage ) pcache1TruncateUnsafe(pCache, 0);
+ assert( pGroup->nMaxPage >= pCache->nMax );
pGroup->nMaxPage -= pCache->nMax;
+ assert( pGroup->nMinPage >= pCache->nMin );
pGroup->nMinPage -= pCache->nMin;
pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
- pcache1EnforceMaxPage(pGroup);
+ pcache1EnforceMaxPage(pCache);
pcache1LeaveMutex(pGroup);
+ sqlite3_free(pCache->pBulk);
sqlite3_free(pCache->apHash);
sqlite3_free(pCache);
}
@@ -36913,7 +45620,8 @@ static void pcache1Destroy(sqlite3_pcache *p){
** already provided an alternative.
*/
SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){
- static const sqlite3_pcache_methods defaultMethods = {
+ static const sqlite3_pcache_methods2 defaultMethods = {
+ 1, /* iVersion */
0, /* pArg */
pcache1Init, /* xInit */
pcache1Shutdown, /* xShutdown */
@@ -36924,9 +45632,23 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){
pcache1Unpin, /* xUnpin */
pcache1Rekey, /* xRekey */
pcache1Truncate, /* xTruncate */
- pcache1Destroy /* xDestroy */
+ pcache1Destroy, /* xDestroy */
+ pcache1Shrink /* xShrink */
};
- sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultMethods);
+ sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods);
+}
+
+/*
+** Return the size of the header on each page of this PCACHE implementation.
+*/
+SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); }
+
+/*
+** Return the global mutex used by this PCACHE implementation. The
+** sqlite3_status() routine needs access to this mutex.
+*/
+SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void){
+ return pcache1.mutex;
}
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
@@ -36941,19 +45663,22 @@ SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){
*/
SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){
int nFree = 0;
-#ifdef SQLITE_PAGECACHE_BLOCKALLOC
- if( pcache1.grp.isBusy ) return 0;
-#endif
assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
assert( sqlite3_mutex_notheld(pcache1.mutex) );
- if( pcache1.pStart==0 ){
+ if( sqlite3GlobalConfig.nPage==0 ){
PgHdr1 *p;
pcache1EnterMutex(&pcache1.grp);
- while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
- nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
+ while( (nReq<0 || nFree<nReq)
+ && (p=pcache1.grp.lru.pLruPrev)!=0
+ && p->isAnchor==0
+ ){
+ nFree += pcache1MemSize(p->page.pBuf);
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER
+ nFree += sqlite3MemSize(p);
+#endif
+ assert( p->isPinned==0 );
pcache1PinPage(p);
- pcache1RemoveFromHash(p);
- pcache1FreePage(p);
+ pcache1RemoveFromHash(p, 1);
}
pcache1LeaveMutex(&pcache1.grp);
}
@@ -36974,12 +45699,13 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
){
PgHdr1 *p;
int nRecyclable = 0;
- for(p=pcache1.grp.pLruHead; p; p=p->pLruNext){
+ for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){
+ assert( p->isPinned==0 );
nRecyclable++;
}
*pnCurrent = pcache1.grp.nCurrentPage;
- *pnMax = pcache1.grp.nMaxPage;
- *pnMin = pcache1.grp.nMinPage;
+ *pnMax = (int)pcache1.grp.nMaxPage;
+ *pnMin = (int)pcache1.grp.nMinPage;
*pnRecyclable = nRecyclable;
}
#endif
@@ -37038,16 +45764,18 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
** No INSERTs may occurs after a SMALLEST. An assertion will fail if
** that is attempted.
**
-** The cost of an INSERT is roughly constant. (Sometime new memory
+** The cost of an INSERT is roughly constant. (Sometimes new memory
** has to be allocated on an INSERT.) The cost of a TEST with a new
** batch number is O(NlogN) where N is the number of elements in the RowSet.
** The cost of a TEST using the same batch number is O(logN). The cost
** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST
** primitives are constant time. The cost of DESTROY is O(N).
**
-** There is an added cost of O(N) when switching between TEST and
-** SMALLEST primitives.
+** TEST and SMALLEST may not be used by the same RowSet. This used to
+** be possible, but the feature was not used, so it was removed in order
+** to simplify the code.
*/
+/* #include "sqliteInt.h" */
/*
@@ -37063,6 +45791,11 @@ SQLITE_PRIVATE void sqlite3PcacheStats(
/*
** Each entry in a RowSet is an instance of the following object.
+**
+** This same object is reused to store a linked list of trees of RowSetEntry
+** objects. In that alternative use, pRight points to the next entry
+** in the list, pLeft points to the tree, and v is unused. The
+** RowSet.pForest value points to the head of this forest list.
*/
struct RowSetEntry {
i64 v; /* ROWID value for this entry */
@@ -37092,13 +45825,19 @@ struct RowSet {
struct RowSetEntry *pEntry; /* List of entries using pRight */
struct RowSetEntry *pLast; /* Last entry on the pEntry list */
struct RowSetEntry *pFresh; /* Source of new entry objects */
- struct RowSetEntry *pTree; /* Binary tree of entries */
+ struct RowSetEntry *pForest; /* List of binary trees of entries */
u16 nFresh; /* Number of objects on pFresh */
- u8 isSorted; /* True if pEntry is sorted */
- u8 iBatch; /* Current insert batch */
+ u16 rsFlags; /* Various flags */
+ int iBatch; /* Current insert batch */
};
/*
+** Allowed values for RowSet.rsFlags
+*/
+#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */
+#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */
+
+/*
** Turn bulk memory into a RowSet object. N bytes of memory
** are available at pSpace. The db pointer is used as a memory context
** for any subsequent allocations that need to occur.
@@ -37118,10 +45857,10 @@ SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int
p->db = db;
p->pEntry = 0;
p->pLast = 0;
- p->pTree = 0;
+ p->pForest = 0;
p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p);
p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry));
- p->isSorted = 1;
+ p->rsFlags = ROWSET_SORTED;
p->iBatch = 0;
return p;
}
@@ -37141,43 +45880,63 @@ SQLITE_PRIVATE void sqlite3RowSetClear(RowSet *p){
p->nFresh = 0;
p->pEntry = 0;
p->pLast = 0;
- p->pTree = 0;
- p->isSorted = 1;
+ p->pForest = 0;
+ p->rsFlags = ROWSET_SORTED;
}
/*
-** Insert a new value into a RowSet.
+** Allocate a new RowSetEntry object that is associated with the
+** given RowSet. Return a pointer to the new and completely uninitialized
+** objected.
**
-** The mallocFailed flag of the database connection is set if a
-** memory allocation fails.
+** In an OOM situation, the RowSet.db->mallocFailed flag is set and this
+** routine returns NULL.
*/
-SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
- struct RowSetEntry *pEntry; /* The new entry */
- struct RowSetEntry *pLast; /* The last prior entry */
+static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){
assert( p!=0 );
- if( p->nFresh==0 ){
+ if( p->nFresh==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* We could allocate a fresh RowSetEntry each time one is needed, but it
+ ** is more efficient to pull a preallocated entry from the pool */
struct RowSetChunk *pNew;
- pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew));
+ pNew = sqlite3DbMallocRawNN(p->db, sizeof(*pNew));
if( pNew==0 ){
- return;
+ return 0;
}
pNew->pNextChunk = p->pChunk;
p->pChunk = pNew;
p->pFresh = pNew->aEntry;
p->nFresh = ROWSET_ENTRY_PER_CHUNK;
}
- pEntry = p->pFresh++;
p->nFresh--;
+ return p->pFresh++;
+}
+
+/*
+** Insert a new value into a RowSet.
+**
+** The mallocFailed flag of the database connection is set if a
+** memory allocation fails.
+*/
+SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
+ struct RowSetEntry *pEntry; /* The new entry */
+ struct RowSetEntry *pLast; /* The last prior entry */
+
+ /* This routine is never called after sqlite3RowSetNext() */
+ assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 );
+
+ pEntry = rowSetEntryAlloc(p);
+ if( pEntry==0 ) return;
pEntry->v = rowid;
pEntry->pRight = 0;
pLast = p->pLast;
if( pLast ){
- if( p->isSorted && rowid<=pLast->v ){
- p->isSorted = 0;
+ if( rowid<=pLast->v ){ /*OPTIMIZATION-IF-FALSE*/
+ /* Avoid unnecessary sorts by preserving the ROWSET_SORTED flags
+ ** where possible */
+ p->rsFlags &= ~ROWSET_SORTED;
}
pLast->pRight = pEntry;
}else{
- assert( p->pEntry==0 ); /* Fires if INSERT after SMALLEST */
p->pEntry = pEntry;
}
p->pLast = pEntry;
@@ -37189,7 +45948,7 @@ SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){
** The input lists are connected via pRight pointers and are
** assumed to each already be in sorted order.
*/
-static struct RowSetEntry *rowSetMerge(
+static struct RowSetEntry *rowSetEntryMerge(
struct RowSetEntry *pA, /* First sorted list to be merged */
struct RowSetEntry *pB /* Second sorted list to be merged */
){
@@ -37197,58 +45956,54 @@ static struct RowSetEntry *rowSetMerge(
struct RowSetEntry *pTail;
pTail = &head;
- while( pA && pB ){
+ assert( pA!=0 && pB!=0 );
+ for(;;){
assert( pA->pRight==0 || pA->v<=pA->pRight->v );
assert( pB->pRight==0 || pB->v<=pB->pRight->v );
- if( pA->v<pB->v ){
- pTail->pRight = pA;
+ if( pA->v<=pB->v ){
+ if( pA->v<pB->v ) pTail = pTail->pRight = pA;
pA = pA->pRight;
- pTail = pTail->pRight;
- }else if( pB->v<pA->v ){
- pTail->pRight = pB;
- pB = pB->pRight;
- pTail = pTail->pRight;
+ if( pA==0 ){
+ pTail->pRight = pB;
+ break;
+ }
}else{
- pA = pA->pRight;
+ pTail = pTail->pRight = pB;
+ pB = pB->pRight;
+ if( pB==0 ){
+ pTail->pRight = pA;
+ break;
+ }
}
}
- if( pA ){
- assert( pA->pRight==0 || pA->v<=pA->pRight->v );
- pTail->pRight = pA;
- }else{
- assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v );
- pTail->pRight = pB;
- }
return head.pRight;
}
/*
-** Sort all elements on the pEntry list of the RowSet into ascending order.
+** Sort all elements on the list of RowSetEntry objects into order of
+** increasing v.
*/
-static void rowSetSort(RowSet *p){
+static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){
unsigned int i;
- struct RowSetEntry *pEntry;
- struct RowSetEntry *aBucket[40];
+ struct RowSetEntry *pNext, *aBucket[40];
- assert( p->isSorted==0 );
memset(aBucket, 0, sizeof(aBucket));
- while( p->pEntry ){
- pEntry = p->pEntry;
- p->pEntry = pEntry->pRight;
- pEntry->pRight = 0;
+ while( pIn ){
+ pNext = pIn->pRight;
+ pIn->pRight = 0;
for(i=0; aBucket[i]; i++){
- pEntry = rowSetMerge(aBucket[i], pEntry);
+ pIn = rowSetEntryMerge(aBucket[i], pIn);
aBucket[i] = 0;
}
- aBucket[i] = pEntry;
+ aBucket[i] = pIn;
+ pIn = pNext;
}
- pEntry = 0;
- for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
- pEntry = rowSetMerge(pEntry, aBucket[i]);
+ pIn = aBucket[0];
+ for(i=1; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){
+ if( aBucket[i]==0 ) continue;
+ pIn = pIn ? rowSetEntryMerge(pIn, aBucket[i]) : aBucket[i];
}
- p->pEntry = pEntry;
- p->pLast = 0;
- p->isSorted = 1;
+ return pIn;
}
@@ -37298,23 +46053,29 @@ static struct RowSetEntry *rowSetNDeepTree(
){
struct RowSetEntry *p; /* Root of the new tree */
struct RowSetEntry *pLeft; /* Left subtree */
- if( *ppList==0 ){
- return 0;
- }
- if( iDepth==1 ){
+ if( *ppList==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* Prevent unnecessary deep recursion when we run out of entries */
+ return 0;
+ }
+ if( iDepth>1 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* This branch causes a *balanced* tree to be generated. A valid tree
+ ** is still generated without this branch, but the tree is wildly
+ ** unbalanced and inefficient. */
+ pLeft = rowSetNDeepTree(ppList, iDepth-1);
+ p = *ppList;
+ if( p==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* It is safe to always return here, but the resulting tree
+ ** would be unbalanced */
+ return pLeft;
+ }
+ p->pLeft = pLeft;
+ *ppList = p->pRight;
+ p->pRight = rowSetNDeepTree(ppList, iDepth-1);
+ }else{
p = *ppList;
*ppList = p->pRight;
p->pLeft = p->pRight = 0;
- return p;
- }
- pLeft = rowSetNDeepTree(ppList, iDepth-1);
- p = *ppList;
- if( p==0 ){
- return pLeft;
}
- p->pLeft = pLeft;
- *ppList = p->pRight;
- p->pRight = rowSetNDeepTree(ppList, iDepth-1);
return p;
}
@@ -37342,36 +46103,36 @@ static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){
}
/*
-** Convert the list in p->pEntry into a sorted list if it is not
-** sorted already. If there is a binary tree on p->pTree, then
-** convert it into a list too and merge it into the p->pEntry list.
-*/
-static void rowSetToList(RowSet *p){
- if( !p->isSorted ){
- rowSetSort(p);
- }
- if( p->pTree ){
- struct RowSetEntry *pHead, *pTail;
- rowSetTreeToList(p->pTree, &pHead, &pTail);
- p->pTree = 0;
- p->pEntry = rowSetMerge(p->pEntry, pHead);
- }
-}
-
-/*
** Extract the smallest element from the RowSet.
** Write the element into *pRowid. Return 1 on success. Return
** 0 if the RowSet is already empty.
**
** After this routine has been called, the sqlite3RowSetInsert()
-** routine may not be called again.
+** routine may not be called again.
+**
+** This routine may not be called after sqlite3RowSetTest() has
+** been used. Older versions of RowSet allowed that, but as the
+** capability was not used by the code generator, it was removed
+** for code economy.
*/
SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
- rowSetToList(p);
+ assert( p!=0 );
+ assert( p->pForest==0 ); /* Cannot be used with sqlite3RowSetText() */
+
+ /* Merge the forest into a single sorted list on first call */
+ if( (p->rsFlags & ROWSET_NEXT)==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ if( (p->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ p->pEntry = rowSetEntrySort(p->pEntry);
+ }
+ p->rsFlags |= ROWSET_SORTED|ROWSET_NEXT;
+ }
+
+ /* Return the next entry on the list */
if( p->pEntry ){
*pRowid = p->pEntry->v;
p->pEntry = p->pEntry->pRight;
- if( p->pEntry==0 ){
+ if( p->pEntry==0 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* Free memory immediately, rather than waiting on sqlite3_finalize() */
sqlite3RowSetClear(p);
}
return 1;
@@ -37381,28 +46142,70 @@ SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){
}
/*
-** Check to see if element iRowid was inserted into the the rowset as
+** Check to see if element iRowid was inserted into the rowset as
** part of any insert batch prior to iBatch. Return 1 or 0.
+**
+** If this is the first test of a new batch and if there exist entries
+** on pRowSet->pEntry, then sort those entries into the forest at
+** pRowSet->pForest so that they can be tested.
*/
-SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 iRowid){
- struct RowSetEntry *p;
- if( iBatch!=pRowSet->iBatch ){
- if( pRowSet->pEntry ){
- rowSetToList(pRowSet);
- pRowSet->pTree = rowSetListToTree(pRowSet->pEntry);
+SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){
+ struct RowSetEntry *p, *pTree;
+
+ /* This routine is never called after sqlite3RowSetNext() */
+ assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 );
+
+ /* Sort entries into the forest on the first test of a new batch.
+ ** To save unnecessary work, only do this when the batch number changes.
+ */
+ if( iBatch!=pRowSet->iBatch ){ /*OPTIMIZATION-IF-FALSE*/
+ p = pRowSet->pEntry;
+ if( p ){
+ struct RowSetEntry **ppPrevTree = &pRowSet->pForest;
+ if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ /* Only sort the current set of entiries if they need it */
+ p = rowSetEntrySort(p);
+ }
+ for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
+ ppPrevTree = &pTree->pRight;
+ if( pTree->pLeft==0 ){
+ pTree->pLeft = rowSetListToTree(p);
+ break;
+ }else{
+ struct RowSetEntry *pAux, *pTail;
+ rowSetTreeToList(pTree->pLeft, &pAux, &pTail);
+ pTree->pLeft = 0;
+ p = rowSetEntryMerge(pAux, p);
+ }
+ }
+ if( pTree==0 ){
+ *ppPrevTree = pTree = rowSetEntryAlloc(pRowSet);
+ if( pTree ){
+ pTree->v = 0;
+ pTree->pRight = 0;
+ pTree->pLeft = rowSetListToTree(p);
+ }
+ }
pRowSet->pEntry = 0;
pRowSet->pLast = 0;
+ pRowSet->rsFlags |= ROWSET_SORTED;
}
pRowSet->iBatch = iBatch;
}
- p = pRowSet->pTree;
- while( p ){
- if( p->v<iRowid ){
- p = p->pRight;
- }else if( p->v>iRowid ){
- p = p->pLeft;
- }else{
- return 1;
+
+ /* Test to see if the iRowid value appears anywhere in the forest.
+ ** Return 1 if it does and 0 if not.
+ */
+ for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){
+ p = pTree->pLeft;
+ while( p ){
+ if( p->v<iRowid ){
+ p = p->pRight;
+ }else if( p->v>iRowid ){
+ p = p->pLeft;
+ }else{
+ return 1;
+ }
}
}
return 0;
@@ -37431,6 +46234,7 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i
** another is writing.
*/
#ifndef SQLITE_OMIT_DISKIO
+/* #include "sqliteInt.h" */
/************** Include wal.h in the middle of pager.c ***********************/
/************** Begin file wal.h *********************************************/
/*
@@ -37449,9 +46253,16 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i
** the implementation of each function in log.c for further details.
*/
-#ifndef _WAL_H_
-#define _WAL_H_
+#ifndef SQLITE_WAL_H
+#define SQLITE_WAL_H
+
+/* #include "sqliteInt.h" */
+/* Additional values that can be added to the sync_flags argument of
+** sqlite3WalFrames():
+*/
+#define WAL_SYNC_TRANSACTIONS 0x20 /* Sync at the end of each transaction */
+#define SQLITE_SYNC_MASK 0x13 /* Mask off the SQLITE_SYNC_* values */
#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z) 0
@@ -37459,7 +46270,6 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalBeginReadTransaction(y,z) 0
# define sqlite3WalEndReadTransaction(z)
-# define sqlite3WalRead(v,w,x,y,z) 0
# define sqlite3WalDbsize(y) 0
# define sqlite3WalBeginWriteTransaction(y) 0
# define sqlite3WalEndWriteTransaction(x) 0
@@ -37471,6 +46281,9 @@ SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, u8 iBatch, sqlite3_int64 i
# define sqlite3WalCallback(z) 0
# define sqlite3WalExclusiveMode(y,z) 0
# define sqlite3WalHeapMemory(z) 0
+# define sqlite3WalFramesize(z) 0
+# define sqlite3WalFindFrame(x,y,z) 0
+# define sqlite3WalFile(x) 0
#else
#define WAL_SAVEPOINT_NDATA 4
@@ -37498,7 +46311,8 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *);
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal);
/* Read a page from the write-ahead log, if it is present. */
-SQLITE_PRIVATE int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, int nOut, u8 *pOut);
+SQLITE_PRIVATE int sqlite3WalFindFrame(Wal *, Pgno, u32 *);
+SQLITE_PRIVATE int sqlite3WalReadFrame(Wal *, u32, int, u8 *);
/* If the WAL is not empty, return the size of the database. */
SQLITE_PRIVATE Pgno sqlite3WalDbsize(Wal *pWal);
@@ -37552,8 +46366,23 @@ SQLITE_PRIVATE int sqlite3WalExclusiveMode(Wal *pWal, int op);
*/
SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal);
+#ifdef SQLITE_ENABLE_SNAPSHOT
+SQLITE_PRIVATE int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot);
+SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot);
+#endif
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+/* If the WAL file is not empty, return the number of bytes of content
+** stored in each frame (i.e. the db page-size when the WAL was created).
+*/
+SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
+#endif
+
+/* Return the sqlite3_file object for the WAL file */
+SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);
+
#endif /* ifndef SQLITE_OMIT_WAL */
-#endif /* _WAL_H_ */
+#endif /* SQLITE_WAL_H */
/************** End of wal.h *************************************************/
/************** Continuing where we left off in pager.c **********************/
@@ -37611,13 +46440,13 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal);
**
** Definition: Two databases (or the same database at two points it time)
** are said to be "logically equivalent" if they give the same answer to
-** all queries. Note in particular the the content of freelist leaf
-** pages can be changed arbitarily without effecting the logical equivalence
+** all queries. Note in particular the content of freelist leaf
+** pages can be changed arbitrarily without affecting the logical equivalence
** of the database.
**
** (7) At any time, if any subset, including the empty set and the total set,
** of the unsynced changes to a rollback journal are removed and the
-** journal is rolled back, the resulting database file will be logical
+** journal is rolled back, the resulting database file will be logically
** equivalent to the database file at the beginning of the transaction.
**
** (8) When a transaction is rolled back, the xTruncate method of the VFS
@@ -37809,7 +46638,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
** * A write transaction is active.
** * An EXCLUSIVE or greater lock is held on the database file.
** * All writing and syncing of journal and database data has finished.
-** If no error occured, all that remains is to finalize the journal to
+** If no error occurred, all that remains is to finalize the journal to
** commit the transaction. If an error did occur, the caller will need
** to rollback the transaction.
**
@@ -37914,7 +46743,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
**
** The exception is when the database file is unlocked as the pager moves
** from ERROR to OPEN state. At this point there may be a hot-journal file
-** in the file-system that needs to be rolled back (as part of a OPEN->SHARED
+** in the file-system that needs to be rolled back (as part of an OPEN->SHARED
** transition, by the same pager or any other). If the call to xUnlock()
** fails at this point and the pager is left holding an EXCLUSIVE lock, this
** can confuse the call to xCheckReservedLock() call made later as part
@@ -37964,6 +46793,7 @@ int sqlite3PagerTrace=1; /* True to enable tracing */
*/
#define MAX_SECTOR_SIZE 0x10000
+
/*
** An instance of the following structure is allocated for each active
** savepoint and statement transaction in the system. All such structures
@@ -37990,7 +46820,14 @@ struct PagerSavepoint {
};
/*
-** A open page cache is an instance of struct Pager. A description of
+** Bits of the Pager.doNotSpill flag. See further description below.
+*/
+#define SPILLFLAG_OFF 0x01 /* Never spill cache. Set via pragma */
+#define SPILLFLAG_ROLLBACK 0x02 /* Current rolling back, so do not spill */
+#define SPILLFLAG_NOSYNC 0x04 /* Spill is ok, but do not sync */
+
+/*
+** An open page cache is an instance of struct Pager. A description of
** some of the more important member variables follows:
**
** eState
@@ -38055,23 +46892,25 @@ struct PagerSavepoint {
** journal file from being successfully finalized, the setMaster flag
** is cleared anyway (and the pager will move to ERROR state).
**
-** doNotSpill, doNotSyncSpill
+** doNotSpill
**
-** These two boolean variables control the behaviour of cache-spills
-** (calls made by the pcache module to the pagerStress() routine to
-** write cached data to the file-system in order to free up memory).
+** This variables control the behavior of cache-spills (calls made by
+** the pcache module to the pagerStress() routine to write cached data
+** to the file-system in order to free up memory).
**
-** When doNotSpill is non-zero, writing to the database from pagerStress()
-** is disabled altogether. This is done in a very obscure case that
+** When bits SPILLFLAG_OFF or SPILLFLAG_ROLLBACK of doNotSpill are set,
+** writing to the database from pagerStress() is disabled altogether.
+** The SPILLFLAG_ROLLBACK case is done in a very obscure case that
** comes up during savepoint rollback that requires the pcache module
** to allocate a new page to prevent the journal file from being written
-** while it is being traversed by code in pager_playback().
+** while it is being traversed by code in pager_playback(). The SPILLFLAG_OFF
+** case is a user preference.
**
-** If doNotSyncSpill is non-zero, writing to the database from pagerStress()
-** is permitted, but syncing the journal file is not. This flag is set
-** by sqlite3PagerWrite() when the file-system sector-size is larger than
-** the database page-size in order to prevent a journal sync from happening
-** in between the journalling of two pages on the same sector.
+** If the SPILLFLAG_NOSYNC bit is set, writing to the database from
+** pagerStress() is permitted, but syncing the journal file is not.
+** This flag is set by sqlite3PagerWrite() when the file-system sector-size
+** is larger than the database page-size in order to prevent a journal sync
+** from happening in between the journalling of two pages on the same sector.
**
** subjInMemory
**
@@ -38148,18 +46987,20 @@ struct Pager {
u8 exclusiveMode; /* Boolean. True if locking_mode==EXCLUSIVE */
u8 journalMode; /* One of the PAGER_JOURNALMODE_* values */
u8 useJournal; /* Use a rollback journal on this file */
- u8 noReadlock; /* Do not bother to obtain readlocks */
u8 noSync; /* Do not sync the journal if true */
u8 fullSync; /* Do extra syncs of the journal for robustness */
+ u8 extraSync; /* sync directory after journal delete */
u8 ckptSyncFlags; /* SYNC_NORMAL or SYNC_FULL for checkpoint */
+ u8 walSyncFlags; /* SYNC_NORMAL or SYNC_FULL for wal writes */
u8 syncFlags; /* SYNC_NORMAL or SYNC_FULL otherwise */
- u8 tempFile; /* zFilename is a temporary file */
+ u8 tempFile; /* zFilename is a temporary or immutable file */
+ u8 noLock; /* Do not lock (except in WAL mode) */
u8 readOnly; /* True for a read-only database */
u8 memDb; /* True to inhibit all file I/O */
/**************************************************************************
** The following block contains those class members that change during
- ** routine opertion. Class members not in this block are either fixed
+ ** routine operation. Class members not in this block are either fixed
** when the pager is first created or else only change when there is a
** significant mode change (such as changing the page_size, locking_mode,
** or the journal_mode). From another view, these class members describe
@@ -38171,8 +47012,9 @@ struct Pager {
u8 changeCountDone; /* Set after incrementing the change-counter */
u8 setMaster; /* True if a m-j name has been written to jrnl */
u8 doNotSpill; /* Do not spill the cache when non-zero */
- u8 doNotSyncSpill; /* Do not do a spill that requires jrnl sync */
u8 subjInMemory; /* True to use in-memory sub-journals */
+ u8 bUseFetch; /* True to use xFetch() */
+ u8 hasHeldSharedLock; /* True if a shared lock has ever been held */
Pgno dbSize; /* Number of pages in the database */
Pgno dbOrigSize; /* dbSize before the current transaction */
Pgno dbFileSize; /* Number of pages in the database file */
@@ -38190,7 +47032,12 @@ struct Pager {
sqlite3_backup *pBackup; /* Pointer to list of ongoing backup processes */
PagerSavepoint *aSavepoint; /* Array of active savepoints */
int nSavepoint; /* Number of elements in aSavepoint[] */
+ u32 iDataVersion; /* Changes whenever database content changes */
char dbFileVers[16]; /* Changes whenever database file changes */
+
+ int nMmapOut; /* Number of mmap pages currently outstanding */
+ sqlite3_int64 szMmap; /* Desired maximum mmap size */
+ PgHdr *pMmapFreelist; /* List of free mmap page headers (pDirty) */
/*
** End of the routinely-changing class members
***************************************************************************/
@@ -38206,9 +47053,9 @@ struct Pager {
char *zJournal; /* Name of the journal file */
int (*xBusyHandler)(void*); /* Function to call when busy */
void *pBusyHandlerArg; /* Context argument for xBusyHandler */
+ int aStat[3]; /* Total cache hits, misses and writes */
#ifdef SQLITE_TEST
- int nHit, nMiss; /* Cache hits and missing */
- int nRead, nWrite; /* Database pages read/written */
+ int nRead; /* Database pages read */
#endif
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
#ifdef SQLITE_HAS_CODEC
@@ -38226,6 +47073,15 @@ struct Pager {
};
/*
+** Indexes for use with Pager.aStat[]. The Pager.aStat[] array contains
+** the values accessed by passing SQLITE_DBSTATUS_CACHE_HIT, CACHE_MISS
+** or CACHE_WRITE to sqlite3_db_status().
+*/
+#define PAGER_STAT_HIT 0
+#define PAGER_STAT_MISS 1
+#define PAGER_STAT_WRITE 2
+
+/*
** The following global variables hold counters used for
** testing purposes only. These variables do not exist in
** a non-testing build. These variables are not thread-safe.
@@ -38293,6 +47149,16 @@ static const unsigned char aJournalMagic[] = {
#endif
/*
+** The macro USEFETCH is true if we are allowed to use the xFetch and xUnfetch
+** interfaces to access the database using memory-mapped I/O.
+*/
+#if SQLITE_MAX_MMAP_SIZE>0
+# define USEFETCH(x) ((x)->bUseFetch)
+#else
+# define USEFETCH(x) 0
+#endif
+
+/*
** The maximum legal page number is (2^31 - 1).
*/
#define PAGER_MAX_PGNO 2147483647
@@ -38309,20 +47175,21 @@ static const unsigned char aJournalMagic[] = {
**
** if( pPager->jfd->pMethods ){ ...
*/
-#define isOpen(pFd) ((pFd)->pMethods)
+#define isOpen(pFd) ((pFd)->pMethods!=0)
/*
** Return true if this pager uses a write-ahead log instead of the usual
** rollback journal. Otherwise false.
*/
#ifndef SQLITE_OMIT_WAL
-static int pagerUseWal(Pager *pPager){
+SQLITE_PRIVATE int sqlite3PagerUseWal(Pager *pPager){
return (pPager->pWal!=0);
}
+# define pagerUseWal(x) sqlite3PagerUseWal(x)
#else
# define pagerUseWal(x) 0
# define pagerRollbackWal(x) 0
-# define pagerWalFrames(v,w,x,y,z) 0
+# define pagerWalFrames(v,w,x,y) 0
# define pagerOpenWalIfPresent(z) SQLITE_OK
# define pagerBeginReadTransaction(z) SQLITE_OK
#endif
@@ -38371,6 +47238,7 @@ static int assert_pager_state(Pager *p){
** state.
*/
if( MEMDB ){
+ assert( !isOpen(p->fd) );
assert( p->noSync );
assert( p->journalMode==PAGER_JOURNALMODE_OFF
|| p->journalMode==PAGER_JOURNALMODE_MEMORY
@@ -38395,7 +47263,7 @@ static int assert_pager_state(Pager *p){
case PAGER_READER:
assert( pPager->errCode==SQLITE_OK );
assert( p->eLock!=UNKNOWN_LOCK );
- assert( p->eLock>=SHARED_LOCK || p->noReadlock );
+ assert( p->eLock>=SHARED_LOCK );
break;
case PAGER_WRITER_LOCKED:
@@ -38457,7 +47325,7 @@ static int assert_pager_state(Pager *p){
** back to OPEN state.
*/
assert( pPager->errCode!=SQLITE_OK );
- assert( sqlite3PcacheRefCount(pPager->pPCache)>0 );
+ assert( sqlite3PcacheRefCount(pPager->pPCache)>0 || pPager->tempFile );
break;
}
@@ -38526,24 +47394,27 @@ static char *print_pager_state(Pager *p){
** PagerSavepoint.pInSavepoint.
*/
static int subjRequiresPage(PgHdr *pPg){
- Pgno pgno = pPg->pgno;
Pager *pPager = pPg->pPager;
+ PagerSavepoint *p;
+ Pgno pgno = pPg->pgno;
int i;
for(i=0; i<pPager->nSavepoint; i++){
- PagerSavepoint *p = &pPager->aSavepoint[i];
- if( p->nOrig>=pgno && 0==sqlite3BitvecTest(p->pInSavepoint, pgno) ){
+ p = &pPager->aSavepoint[i];
+ if( p->nOrig>=pgno && 0==sqlite3BitvecTestNotNull(p->pInSavepoint, pgno) ){
return 1;
}
}
return 0;
}
+#ifdef SQLITE_DEBUG
/*
** Return true if the page is already in the journal file.
*/
-static int pageInJournal(PgHdr *pPg){
- return sqlite3BitvecTest(pPg->pPager->pInJournal, pPg->pgno);
+static int pageInJournal(Pager *pPager, PgHdr *pPg){
+ return sqlite3BitvecTest(pPager->pInJournal, pPg->pgno);
}
+#endif
/*
** Read a 32-bit integer from the given file descriptor. Store the integer
@@ -38594,7 +47465,7 @@ static int pagerUnlockDb(Pager *pPager, int eLock){
assert( eLock!=NO_LOCK || pagerUseWal(pPager)==0 );
if( isOpen(pPager->fd) ){
assert( pPager->eLock>=eLock );
- rc = sqlite3OsUnlock(pPager->fd, eLock);
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsUnlock(pPager->fd, eLock);
if( pPager->eLock!=UNKNOWN_LOCK ){
pPager->eLock = (u8)eLock;
}
@@ -38618,7 +47489,7 @@ static int pagerLockDb(Pager *pPager, int eLock){
assert( eLock==SHARED_LOCK || eLock==RESERVED_LOCK || eLock==EXCLUSIVE_LOCK );
if( pPager->eLock<eLock || pPager->eLock==UNKNOWN_LOCK ){
- rc = sqlite3OsLock(pPager->fd, eLock);
+ rc = pPager->noLock ? SQLITE_OK : sqlite3OsLock(pPager->fd, eLock);
if( rc==SQLITE_OK && (pPager->eLock!=UNKNOWN_LOCK||eLock==EXCLUSIVE_LOCK) ){
pPager->eLock = (u8)eLock;
IOTRACE(("LOCK %p %d\n", pPager, eLock))
@@ -38666,6 +47537,8 @@ static int jrnlBufferSize(Pager *pPager){
return JOURNAL_HDR_SZ(pPager) + JOURNAL_PG_SZ(pPager);
}
+#else
+# define jrnlBufferSize(x) 0
#endif
/*
@@ -38749,6 +47622,7 @@ static int readMasterJournal(sqlite3_file *pJrnl, char *zMaster, u32 nMaster){
|| szJ<16
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-16, &len))
|| len>=nMaster
+ || len==0
|| SQLITE_OK!=(rc = read32bits(pJrnl, szJ-12, &cksum))
|| SQLITE_OK!=(rc = sqlite3OsRead(pJrnl, aMagic, 8, szJ-8))
|| memcmp(aMagic, aJournalMagic, 8)
@@ -38825,6 +47699,7 @@ static i64 journalHdrOffset(Pager *pPager){
static int zeroJournalHdr(Pager *pPager, int doTruncate){
int rc = SQLITE_OK; /* Return code */
assert( isOpen(pPager->jfd) );
+ assert( !sqlite3JournalIsInMemory(pPager->jfd) );
if( pPager->journalOff ){
const i64 iLimit = pPager->journalSizeLimit; /* Local cache of jsl */
@@ -38926,7 +47801,7 @@ static int writeJournalHdr(Pager *pPager){
memset(zHeader, 0, sizeof(aJournalMagic)+4);
}
- /* The random check-hash initialiser */
+ /* The random check-hash initializer */
sqlite3_randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
put32bits(&zHeader[sizeof(aJournalMagic)+4], pPager->cksumInit);
/* The initial database size */
@@ -39126,12 +48001,11 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
if( !zMaster
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
- || pPager->journalMode==PAGER_JOURNALMODE_OFF
+ || !isOpen(pPager->jfd)
){
return SQLITE_OK;
}
pPager->setMaster = 1;
- assert( isOpen(pPager->jfd) );
assert( pPager->journalHdr <= pPager->journalOff );
/* Calculate the length in bytes and the checksum of zMaster */
@@ -39155,7 +48029,8 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
|| (0 != (rc = sqlite3OsWrite(pPager->jfd, zMaster, nMaster, iHdrOff+4)))
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster, nMaster)))
|| (0 != (rc = write32bits(pPager->jfd, iHdrOff+4+nMaster+4, cksum)))
- || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8, iHdrOff+4+nMaster+8)))
+ || (0 != (rc = sqlite3OsWrite(pPager->jfd, aJournalMagic, 8,
+ iHdrOff+4+nMaster+8)))
){
return rc;
}
@@ -39180,29 +48055,23 @@ static int writeMasterJournal(Pager *pPager, const char *zMaster){
}
/*
-** Find a page in the hash table given its page number. Return
-** a pointer to the page or NULL if the requested page is not
-** already in memory.
-*/
-static PgHdr *pager_lookup(Pager *pPager, Pgno pgno){
- PgHdr *p; /* Return value */
-
- /* It is not possible for a call to PcacheFetch() with createFlag==0 to
- ** fail, since no attempt to allocate dynamic memory will be made.
- */
- (void)sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &p);
- return p;
-}
-
-/*
** Discard the entire contents of the in-memory page-cache.
*/
static void pager_reset(Pager *pPager){
+ pPager->iDataVersion++;
sqlite3BackupRestart(pPager->pBackup);
sqlite3PcacheClear(pPager->pPCache);
}
/*
+** Return the pPager->iDataVersion value
+*/
+SQLITE_PRIVATE u32 sqlite3PagerDataVersion(Pager *pPager){
+ assert( pPager->eState>PAGER_OPEN );
+ return pPager->iDataVersion;
+}
+
+/*
** Free all structures in the Pager.aSavepoint[] array and set both
** Pager.aSavepoint and Pager.nSavepoint to zero. Close the sub-journal
** if it is open and the pager is not in exclusive mode.
@@ -39212,7 +48081,7 @@ static void releaseAllSavepoints(Pager *pPager){
for(ii=0; ii<pPager->nSavepoint; ii++){
sqlite3BitvecDestroy(pPager->aSavepoint[ii].pInSavepoint);
}
- if( !pPager->exclusiveMode || sqlite3IsMemJournal(pPager->sjfd) ){
+ if( !pPager->exclusiveMode || sqlite3JournalIsInMemory(pPager->sjfd) ){
sqlite3OsClose(pPager->sjfd);
}
sqlite3_free(pPager->aSavepoint);
@@ -39318,11 +48187,16 @@ static void pager_unlock(Pager *pPager){
** it can safely move back to PAGER_OPEN state. This happens in both
** normal and exclusive-locking mode.
*/
+ assert( pPager->errCode==SQLITE_OK || !MEMDB );
if( pPager->errCode ){
- assert( !MEMDB );
- pager_reset(pPager);
- pPager->changeCountDone = pPager->tempFile;
- pPager->eState = PAGER_OPEN;
+ if( pPager->tempFile==0 ){
+ pager_reset(pPager);
+ pPager->changeCountDone = 0;
+ pPager->eState = PAGER_OPEN;
+ }else{
+ pPager->eState = (isOpen(pPager->jfd) ? PAGER_OPEN : PAGER_READER);
+ }
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
pPager->errCode = SQLITE_OK;
}
@@ -39365,6 +48239,31 @@ static int pager_error(Pager *pPager, int rc){
return rc;
}
+static int pager_truncate(Pager *pPager, Pgno nPage);
+
+/*
+** The write transaction open on pPager is being committed (bCommit==1)
+** or rolled back (bCommit==0).
+**
+** Return TRUE if and only if all dirty pages should be flushed to disk.
+**
+** Rules:
+**
+** * For non-TEMP databases, always sync to disk. This is necessary
+** for transactions to be durable.
+**
+** * Sync TEMP database only on a COMMIT (not a ROLLBACK) when the backing
+** file has been created already (via a spill on pagerStress()) and
+** when the number of dirty pages in memory exceeds 25% of the total
+** cache size.
+*/
+static int pagerFlushOnCommit(Pager *pPager, int bCommit){
+ if( pPager->tempFile==0 ) return 1;
+ if( !bCommit ) return 0;
+ if( !isOpen(pPager->fd) ) return 0;
+ return (sqlite3PCachePercentDirty(pPager->pPCache)>=25);
+}
+
/*
** This routine ends a transaction. A transaction is usually ended by
** either a COMMIT or a ROLLBACK operation. This routine may be called
@@ -39418,7 +48317,7 @@ static int pager_error(Pager *pPager, int rc){
** to the first error encountered (the journal finalization one) is
** returned.
*/
-static int pager_end_transaction(Pager *pPager, int hasMaster){
+static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
int rc = SQLITE_OK; /* Error code from journal finalization operation */
int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
@@ -39447,34 +48346,44 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
assert( !pagerUseWal(pPager) );
/* Finalize the journal file. */
- if( sqlite3IsMemJournal(pPager->jfd) ){
- assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY );
+ if( sqlite3JournalIsInMemory(pPager->jfd) ){
+ /* assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); */
sqlite3OsClose(pPager->jfd);
}else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){
if( pPager->journalOff==0 ){
rc = SQLITE_OK;
}else{
rc = sqlite3OsTruncate(pPager->jfd, 0);
+ if( rc==SQLITE_OK && pPager->fullSync ){
+ /* Make sure the new file size is written into the inode right away.
+ ** Otherwise the journal might resurrect following a power loss and
+ ** cause the last transaction to roll back. See
+ ** https://bugzilla.mozilla.org/show_bug.cgi?id=1072773
+ */
+ rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
+ }
}
pPager->journalOff = 0;
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|| (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
){
- rc = zeroJournalHdr(pPager, hasMaster);
+ rc = zeroJournalHdr(pPager, hasMaster||pPager->tempFile);
pPager->journalOff = 0;
}else{
/* This branch may be executed with Pager.journalMode==MEMORY if
** a hot-journal was just rolled back. In this case the journal
** file should be closed and deleted. If this connection writes to
- ** the database file, it will do so using an in-memory journal.
+ ** the database file, it will do so using an in-memory journal.
*/
+ int bDelete = !pPager->tempFile;
+ assert( sqlite3JournalIsInMemory(pPager->jfd)==0 );
assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
|| pPager->journalMode==PAGER_JOURNALMODE_MEMORY
|| pPager->journalMode==PAGER_JOURNALMODE_WAL
);
sqlite3OsClose(pPager->jfd);
- if( !pPager->tempFile ){
- rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0);
+ if( bDelete ){
+ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, pPager->extraSync);
}
}
}
@@ -39482,10 +48391,10 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
#ifdef SQLITE_CHECK_PAGES
sqlite3PcacheIterateDirty(pPager->pPCache, pager_set_pagehash);
if( pPager->dbSize==0 && sqlite3PcacheRefCount(pPager->pPCache)>0 ){
- PgHdr *p = pager_lookup(pPager, 1);
+ PgHdr *p = sqlite3PagerLookup(pPager, 1);
if( p ){
p->pageHash = 0;
- sqlite3PagerUnref(p);
+ sqlite3PagerUnrefNotNull(p);
}
}
#endif
@@ -39493,8 +48402,14 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
pPager->nRec = 0;
- sqlite3PcacheCleanAll(pPager->pPCache);
- sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
+ if( rc==SQLITE_OK ){
+ if( pagerFlushOnCommit(pPager, bCommit) ){
+ sqlite3PcacheCleanAll(pPager->pPCache);
+ }else{
+ sqlite3PcacheClearWritable(pPager->pPCache);
+ }
+ sqlite3PcacheTruncate(pPager->pPCache, pPager->dbSize);
+ }
if( pagerUseWal(pPager) ){
/* Drop the WAL write-lock, if any. Also, if the connection was in
@@ -39503,7 +48418,22 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
*/
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
assert( rc2==SQLITE_OK );
+ }else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){
+ /* This branch is taken when committing a transaction in rollback-journal
+ ** mode if the database file on disk is larger than the database image.
+ ** At this point the journal has been finalized and the transaction
+ ** successfully committed, but the EXCLUSIVE lock is still held on the
+ ** file. So it is safe to truncate the database file to its minimum
+ ** required size. */
+ assert( pPager->eLock==EXCLUSIVE_LOCK );
+ rc = pager_truncate(pPager, pPager->dbSize);
}
+
+ if( rc==SQLITE_OK && bCommit && isOpen(pPager->fd) ){
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_COMMIT_PHASETWO, 0);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ }
+
if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
@@ -39542,7 +48472,7 @@ static void pagerUnlockAndRollback(Pager *pPager){
sqlite3EndBenignMalloc();
}else if( !pPager->exclusiveMode ){
assert( pPager->eState==PAGER_READER );
- pager_end_transaction(pPager, 0);
+ pager_end_transaction(pPager, 0, 0);
}
}
pager_unlock(pPager);
@@ -39592,6 +48522,20 @@ static void pagerReportSize(Pager *pPager){
# define pagerReportSize(X) /* No-op if we do not support a codec */
#endif
+#ifdef SQLITE_HAS_CODEC
+/*
+** Make sure the number of reserved bits is the same in the destination
+** pager as it is in the source. This comes up when a VACUUM changes the
+** number of reserved bits to the "optimal" amount.
+*/
+SQLITE_PRIVATE void sqlite3PagerAlignReserve(Pager *pDest, Pager *pSrc){
+ if( pDest->nReserve!=pSrc->nReserve ){
+ pDest->nReserve = pSrc->nReserve;
+ pagerReportSize(pDest);
+ }
+}
+#endif
+
/*
** Read a single page from either the journal file (if isMainJrnl==1) or
** from the sub-journal (if isMainJrnl==0) and playback that page.
@@ -39694,7 +48638,7 @@ static int pager_playback_one_page(
}
}
- /* If this page has already been played by before during the current
+ /* If this page has already been played back before during the current
** rollback, then don't bother to play it back again.
*/
if( pDone && (rc = sqlite3BitvecSet(pDone, pgno))!=SQLITE_OK ){
@@ -39746,10 +48690,10 @@ static int pager_playback_one_page(
if( pagerUseWal(pPager) ){
pPg = 0;
}else{
- pPg = pager_lookup(pPager, pgno);
+ pPg = sqlite3PagerLookup(pPager, pgno);
}
assert( pPg || !MEMDB );
- assert( pPager->eState!=PAGER_OPEN || pPg==0 );
+ assert( pPager->eState!=PAGER_OPEN || pPg==0 || pPager->tempFile );
PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n",
PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData),
(isMainJrnl?"main-journal":"sub-journal")
@@ -39766,14 +48710,14 @@ static int pager_playback_one_page(
i64 ofst = (pgno-1)*(i64)pPager->pageSize;
testcase( !isSavepnt && pPg!=0 && (pPg->flags&PGHDR_NEED_SYNC)!=0 );
assert( !pagerUseWal(pPager) );
- rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst);
+ rc = sqlite3OsWrite(pPager->fd, (u8 *)aData, pPager->pageSize, ofst);
if( pgno>pPager->dbFileSize ){
pPager->dbFileSize = pgno;
}
if( pPager->pBackup ){
- CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM);
+ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM_BKPT);
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData);
- CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData);
+ CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM_BKPT, aData);
}
}else if( !isMainJrnl && pPg==0 ){
/* If this is a rollback of a savepoint and data was not written to
@@ -39793,13 +48737,12 @@ static int pager_playback_one_page(
** requiring a journal-sync before it is written.
*/
assert( isSavepnt );
- assert( pPager->doNotSpill==0 );
- pPager->doNotSpill++;
- rc = sqlite3PagerAcquire(pPager, pgno, &pPg, 1);
- assert( pPager->doNotSpill==1 );
- pPager->doNotSpill--;
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)==0 );
+ pPager->doNotSpill |= SPILLFLAG_ROLLBACK;
+ rc = sqlite3PagerGet(pPager, pgno, &pPg, 1);
+ assert( (pPager->doNotSpill & SPILLFLAG_ROLLBACK)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_ROLLBACK;
if( rc!=SQLITE_OK ) return rc;
- pPg->flags &= ~PGHDR_NEED_READ;
sqlite3PcacheMakeDirty(pPg);
}
if( pPg ){
@@ -39813,29 +48756,10 @@ static int pager_playback_one_page(
pData = pPg->pData;
memcpy(pData, (u8*)aData, pPager->pageSize);
pPager->xReiniter(pPg);
- if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){
- /* If the contents of this page were just restored from the main
- ** journal file, then its content must be as they were when the
- ** transaction was first opened. In this case we can mark the page
- ** as clean, since there will be no need to write it out to the
- ** database.
- **
- ** There is one exception to this rule. If the page is being rolled
- ** back as part of a savepoint (or statement) rollback from an
- ** unsynced portion of the main journal file, then it is not safe
- ** to mark the page as clean. This is because marking the page as
- ** clean will clear the PGHDR_NEED_SYNC flag. Since the page is
- ** already in the journal file (recorded in Pager.pInJournal) and
- ** the PGHDR_NEED_SYNC flag is cleared, if the page is written to
- ** again within this transaction, it will be marked as dirty but
- ** the PGHDR_NEED_SYNC flag will not be set. It could then potentially
- ** be written out into the database file before its journal file
- ** segment is synced. If a crash occurs during or following this,
- ** database corruption may ensue.
- */
- assert( !pagerUseWal(pPager) );
- sqlite3PcacheMakeClean(pPg);
- }
+ /* It used to be that sqlite3PcacheMakeClean(pPg) was called here. But
+ ** that call was dangerous and had no detectable benefit since the cache
+ ** is normally cleaned by sqlite3PcacheCleanAll() after rollback and so
+ ** has been removed. */
pager_set_pagehash(pPg);
/* If this was page 1, then restore the value of Pager.dbFileVers.
@@ -39845,7 +48769,7 @@ static int pager_playback_one_page(
}
/* Decode the page just read from disk */
- CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM);
+ CODEC1(pPager, pData, pPg->pgno, 3, rc=SQLITE_NOMEM_BKPT);
sqlite3PcacheRelease(pPg);
}
return rc;
@@ -39911,7 +48835,7 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
pMaster = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2);
pJournal = (sqlite3_file *)(((u8 *)pMaster) + pVfs->szOsFile);
if( !pMaster ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
const int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
rc = sqlite3OsOpen(pVfs, zMaster, pMaster, flags, 0);
@@ -39926,9 +48850,9 @@ static int pager_delmaster(Pager *pPager, const char *zMaster){
rc = sqlite3OsFileSize(pMaster, &nMasterJournal);
if( rc!=SQLITE_OK ) goto delmaster_out;
nMasterPtr = pVfs->mxPathname+1;
- zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1);
+ zMasterJournal = sqlite3Malloc(nMasterJournal + nMasterPtr + 1);
if( !zMasterJournal ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto delmaster_out;
}
zMasterPtr = &zMasterJournal[nMasterJournal+1];
@@ -39995,7 +48919,7 @@ delmaster_out:
** If the file on disk is currently larger than nPage pages, then use the VFS
** xTruncate() method to truncate it.
**
-** Or, it might might be the case that the file on disk is smaller than
+** Or, it might be the case that the file on disk is smaller than
** nPage pages. Some operating system implementations can get confused if
** you try to truncate a file to some size that is larger than it
** currently is, so detect this case and write a single zero byte to
@@ -40021,10 +48945,9 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
if( rc==SQLITE_OK && currentSize!=newSize ){
if( currentSize>newSize ){
rc = sqlite3OsTruncate(pPager->fd, newSize);
- }else{
+ }else if( (currentSize+szPage)<=newSize ){
char *pTmp = pPager->pTmpSpace;
memset(pTmp, 0, szPage);
- testcase( (newSize-szPage) < currentSize );
testcase( (newSize-szPage) == currentSize );
testcase( (newSize-szPage) > currentSize );
rc = sqlite3OsWrite(pPager->fd, pTmp, szPage, newSize-szPage);
@@ -40038,9 +48961,24 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
}
/*
+** Return a sanitized version of the sector-size of OS file pFile. The
+** return value is guaranteed to lie between 32 and MAX_SECTOR_SIZE.
+*/
+SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *pFile){
+ int iRet = sqlite3OsSectorSize(pFile);
+ if( iRet<32 ){
+ iRet = 512;
+ }else if( iRet>MAX_SECTOR_SIZE ){
+ assert( MAX_SECTOR_SIZE>=512 );
+ iRet = MAX_SECTOR_SIZE;
+ }
+ return iRet;
+}
+
+/*
** Set the value of the Pager.sectorSize variable for the given
** pager based on the value returned by the xSectorSize method
-** of the open database file. The sector size will be used used
+** of the open database file. The sector size will be used
** to determine the size and alignment of journal header and
** master journal pointers within created journal files.
**
@@ -40050,23 +48988,29 @@ static int pager_truncate(Pager *pPager, Pgno nPage){
** the value returned by the xSectorSize() method rounded up to 32 if
** it is less than 32, or rounded down to MAX_SECTOR_SIZE if it
** is greater than MAX_SECTOR_SIZE.
+**
+** If the file has the SQLITE_IOCAP_POWERSAFE_OVERWRITE property, then set
+** the effective sector size to its minimum value (512). The purpose of
+** pPager->sectorSize is to define the "blast radius" of bytes that
+** might change if a crash occurs while writing to a single byte in
+** that range. But with POWERSAFE_OVERWRITE, the blast radius is zero
+** (that is what POWERSAFE_OVERWRITE means), so we minimize the sector
+** size. For backwards compatibility of the rollback journal file format,
+** we cannot reduce the effective sector size below 512.
*/
static void setSectorSize(Pager *pPager){
assert( isOpen(pPager->fd) || pPager->tempFile );
- if( !pPager->tempFile ){
+ if( pPager->tempFile
+ || (sqlite3OsDeviceCharacteristics(pPager->fd) &
+ SQLITE_IOCAP_POWERSAFE_OVERWRITE)!=0
+ ){
/* Sector size doesn't matter for temporary files. Also, the file
** may not have been opened yet, in which case the OsSectorSize()
- ** call will segfault.
- */
- pPager->sectorSize = sqlite3OsSectorSize(pPager->fd);
- }
- if( pPager->sectorSize<32 ){
+ ** call will segfault. */
pPager->sectorSize = 512;
- }
- if( pPager->sectorSize>MAX_SECTOR_SIZE ){
- assert( MAX_SECTOR_SIZE>=512 );
- pPager->sectorSize = MAX_SECTOR_SIZE;
+ }else{
+ pPager->sectorSize = sqlite3SectorSize(pPager->fd);
}
}
@@ -40137,6 +49081,7 @@ static int pager_playback(Pager *pPager, int isHot){
int res = 1; /* Value returned by sqlite3OsAccess() */
char *zMaster = 0; /* Name of master journal file if any */
int needPagerReset; /* True to reset page prior to first page rollback */
+ int nPlayback = 0; /* Total number of pages restored from journal */
/* Figure out how many records are in the journal. Abort early if
** the journal is empty.
@@ -40155,7 +49100,7 @@ static int pager_playback(Pager *pPager, int isHot){
** TODO: Technically the following is an error because it assumes that
** buffer Pager.pTmpSpace is (mxPathname+1) bytes or larger. i.e. that
** (pPager->pageSize >= pPager->pVfs->mxPathname+1). Using os_unix.c,
- ** mxPathname is 512, which is the same as the minimum allowable value
+ ** mxPathname is 512, which is the same as the minimum allowable value
** for pageSize.
*/
zMaster = pPager->pTmpSpace;
@@ -40237,9 +49182,10 @@ static int pager_playback(Pager *pPager, int isHot){
needPagerReset = 0;
}
rc = pager_playback_one_page(pPager,&pPager->journalOff,0,1,0);
- if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_OK ){
+ nPlayback++;
+ }else{
if( rc==SQLITE_DONE ){
- rc = SQLITE_OK;
pPager->journalOff = szJ;
break;
}else if( rc==SQLITE_IOERR_SHORT_READ ){
@@ -40270,10 +49216,11 @@ end_playback:
** SQLITE_FCNTL_DB_UNCHANGED file-control method to disable the
** assertion that the transaction counter was modified.
*/
- assert(
- pPager->fd->pMethods==0 ||
- sqlite3OsFileControl(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0)>=SQLITE_OK
- );
+#ifdef SQLITE_DEBUG
+ if( pPager->fd->pMethods ){
+ sqlite3OsFileControlHint(pPager->fd,SQLITE_FCNTL_DB_UNCHANGED,0);
+ }
+#endif
/* If this playback is happening automatically as a result of an IO or
** malloc error that occurred after the change-counter was updated but
@@ -40294,10 +49241,10 @@ end_playback:
if( rc==SQLITE_OK
&& (pPager->eState>=PAGER_WRITER_DBMOD || pPager->eState==PAGER_OPEN)
){
- rc = sqlite3PagerSync(pPager);
+ rc = sqlite3PagerSync(pPager, 0);
}
if( rc==SQLITE_OK ){
- rc = pager_end_transaction(pPager, zMaster[0]!='\0');
+ rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0);
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK && zMaster[0] && res ){
@@ -40307,6 +49254,10 @@ end_playback:
rc = pager_delmaster(pPager, zMaster);
testcase( rc!=SQLITE_OK );
}
+ if( isHot && nPlayback ){
+ sqlite3_log(SQLITE_NOTICE_RECOVER_ROLLBACK, "recovered %d pages from %s",
+ nPlayback, pPager->zJournal);
+ }
/* The Pager.sectorSize variable may have been updated while rolling
** back a journal created by a process with a different sector size
@@ -40328,27 +49279,22 @@ end_playback:
** If an IO error occurs, then the IO error is returned to the caller.
** Otherwise, SQLITE_OK is returned.
*/
-static int readDbPage(PgHdr *pPg){
+static int readDbPage(PgHdr *pPg, u32 iFrame){
Pager *pPager = pPg->pPager; /* Pager object associated with page pPg */
Pgno pgno = pPg->pgno; /* Page number to read */
int rc = SQLITE_OK; /* Return code */
- int isInWal = 0; /* True if page is in log file */
int pgsz = pPager->pageSize; /* Number of bytes to read */
assert( pPager->eState>=PAGER_READER && !MEMDB );
assert( isOpen(pPager->fd) );
- if( NEVER(!isOpen(pPager->fd)) ){
- assert( pPager->tempFile );
- memset(pPg->pData, 0, pPager->pageSize);
- return SQLITE_OK;
- }
-
- if( pagerUseWal(pPager) ){
+#ifndef SQLITE_OMIT_WAL
+ if( iFrame ){
/* Try to pull the page from the write-ahead log. */
- rc = sqlite3WalRead(pPager->pWal, pgno, &isInWal, pgsz, pPg->pData);
- }
- if( rc==SQLITE_OK && !isInWal ){
+ rc = sqlite3WalReadFrame(pPager->pWal, iFrame, pgsz, pPg->pData);
+ }else
+#endif
+ {
i64 iOffset = (pgno-1)*(i64)pPager->pageSize;
rc = sqlite3OsRead(pPager->fd, pPg->pData, pgsz, iOffset);
if( rc==SQLITE_IOERR_SHORT_READ ){
@@ -40367,7 +49313,7 @@ static int readDbPage(PgHdr *pPg){
**
** For an encrypted database, the situation is more complex: bytes
** 24..39 of the database are white noise. But the probability of
- ** white noising equaling 16 bytes of 0xff is vanishingly small so
+ ** white noise equaling 16 bytes of 0xff is vanishingly small so
** we should still be ok.
*/
memset(pPager->dbFileVers, 0xff, sizeof(pPager->dbFileVers));
@@ -40376,7 +49322,7 @@ static int readDbPage(PgHdr *pPg){
memcpy(&pPager->dbFileVers, dbFileVers, sizeof(pPager->dbFileVers));
}
}
- CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM);
+ CODEC1(pPager, pPg->pData, pgno, 3, rc = SQLITE_NOMEM_BKPT);
PAGER_INCR(sqlite3_pager_readdb_count);
PAGER_INCR(pPager->nRead);
@@ -40427,16 +49373,21 @@ static int pagerUndoCallback(void *pCtx, Pgno iPg){
Pager *pPager = (Pager *)pCtx;
PgHdr *pPg;
+ assert( pagerUseWal(pPager) );
pPg = sqlite3PagerLookup(pPager, iPg);
if( pPg ){
if( sqlite3PcachePageRefcount(pPg)==1 ){
sqlite3PcacheDrop(pPg);
}else{
- rc = readDbPage(pPg);
+ u32 iFrame = 0;
+ rc = sqlite3WalFindFrame(pPager->pWal, pPg->pgno, &iFrame);
+ if( rc==SQLITE_OK ){
+ rc = readDbPage(pPg, iFrame);
+ }
if( rc==SQLITE_OK ){
pPager->xReiniter(pPg);
}
- sqlite3PagerUnref(pPg);
+ sqlite3PagerUnrefNotNull(pPg);
}
}
@@ -40492,15 +49443,14 @@ static int pagerWalFrames(
Pager *pPager, /* Pager object */
PgHdr *pList, /* List of frames to log */
Pgno nTruncate, /* Database size after this commit */
- int isCommit, /* True if this is a commit */
- int syncFlags /* Flags to pass to OsSync() (or 0) */
+ int isCommit /* True if this is a commit */
){
int rc; /* Return code */
-#if defined(SQLITE_DEBUG) || defined(SQLITE_CHECK_PAGES)
+ int nList; /* Number of pages in pList */
PgHdr *p; /* For looping over pages */
-#endif
assert( pPager->pWal );
+ assert( pList );
#ifdef SQLITE_DEBUG
/* Verify that the page list is in accending order */
for(p=pList; p && p->pDirty; p=p->pDirty){
@@ -40508,25 +49458,31 @@ static int pagerWalFrames(
}
#endif
+ assert( pList->pDirty==0 || isCommit );
if( isCommit ){
/* If a WAL transaction is being committed, there is no point in writing
** any pages with page numbers greater than nTruncate into the WAL file.
** They will never be read by any client. So remove them from the pDirty
** list here. */
- PgHdr *p;
PgHdr **ppNext = &pList;
- for(p=pList; (*ppNext = p); p=p->pDirty){
- if( p->pgno<=nTruncate ) ppNext = &p->pDirty;
+ nList = 0;
+ for(p=pList; (*ppNext = p)!=0; p=p->pDirty){
+ if( p->pgno<=nTruncate ){
+ ppNext = &p->pDirty;
+ nList++;
+ }
}
assert( pList );
+ }else{
+ nList = 1;
}
+ pPager->aStat[PAGER_STAT_WRITE] += nList;
if( pList->pgno==1 ) pager_write_changecounter(pList);
rc = sqlite3WalFrames(pPager->pWal,
- pPager->pageSize, pList, nTruncate, isCommit, syncFlags
+ pPager->pageSize, pList, nTruncate, isCommit, pPager->walSyncFlags
);
if( rc==SQLITE_OK && pPager->pBackup ){
- PgHdr *p;
for(p=pList; p; p=p->pDirty){
sqlite3BackupUpdate(pPager->pBackup, p->pgno, (u8 *)p->pData);
}
@@ -40567,6 +49523,7 @@ static int pagerBeginReadTransaction(Pager *pPager){
rc = sqlite3WalBeginReadTransaction(pPager->pWal, &changed);
if( rc!=SQLITE_OK || changed ){
pager_reset(pPager);
+ if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
}
return rc;
@@ -40592,28 +49549,23 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
** contains no valid committed transactions.
*/
assert( pPager->eState==PAGER_OPEN );
- assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
+ assert( pPager->eLock>=SHARED_LOCK );
+ assert( isOpen(pPager->fd) );
+ assert( pPager->tempFile==0 );
nPage = sqlite3WalDbsize(pPager->pWal);
- /* If the database size was not available from the WAL sub-system,
- ** determine it based on the size of the database file. If the size
- ** of the database file is not an integer multiple of the page-size,
- ** round down to the nearest page. Except, any file larger than 0
- ** bytes in size is considered to contain at least one page.
+ /* If the number of pages in the database is not available from the
+ ** WAL sub-system, determine the page counte based on the size of
+ ** the database file. If the size of the database file is not an
+ ** integer multiple of the page-size, round up the result.
*/
- if( nPage==0 ){
+ if( nPage==0 && ALWAYS(isOpen(pPager->fd)) ){
i64 n = 0; /* Size of db file in bytes */
- assert( isOpen(pPager->fd) || pPager->tempFile );
- if( isOpen(pPager->fd) ){
- int rc = sqlite3OsFileSize(pPager->fd, &n);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- }
- nPage = (Pgno)(n / pPager->pageSize);
- if( nPage==0 && n>0 ){
- nPage = 1;
+ int rc = sqlite3OsFileSize(pPager->fd, &n);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
+ nPage = (Pgno)((n+pPager->pageSize-1) / pPager->pageSize);
}
/* If the current number of pages in the file is greater than the
@@ -40650,7 +49602,7 @@ static int pagerPagecount(Pager *pPager, Pgno *pnPage){
static int pagerOpenWalIfPresent(Pager *pPager){
int rc = SQLITE_OK;
assert( pPager->eState==PAGER_OPEN );
- assert( pPager->eLock>=SHARED_LOCK || pPager->noReadlock );
+ assert( pPager->eLock>=SHARED_LOCK );
if( !pPager->tempFile ){
int isWal; /* True if WAL file exists */
@@ -40660,6 +49612,7 @@ static int pagerOpenWalIfPresent(Pager *pPager){
if( rc ) return rc;
if( nPage==0 ){
rc = sqlite3OsDelete(pPager->pVfs, pPager->zWal, 0);
+ if( rc==SQLITE_IOERR_DELETE_NOENT ) rc = SQLITE_OK;
isWal = 0;
}else{
rc = sqlite3OsAccess(
@@ -40728,7 +49681,7 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
if( pSavepoint ){
pDone = sqlite3BitvecCreate(pSavepoint->nOrig);
if( !pDone ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
}
@@ -40803,13 +49756,13 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
*/
if( pSavepoint ){
u32 ii; /* Loop counter */
- i64 offset = pSavepoint->iSubRec*(4+pPager->pageSize);
+ i64 offset = (i64)pSavepoint->iSubRec*(4+pPager->pageSize);
if( pagerUseWal(pPager) ){
rc = sqlite3WalSavepointUndo(pPager->pWal, pSavepoint->aWalData);
}
for(ii=pSavepoint->iSubRec; rc==SQLITE_OK && ii<pPager->nSubRec; ii++){
- assert( offset==ii*(4+pPager->pageSize) );
+ assert( offset==(i64)ii*(4+pPager->pageSize) );
rc = pager_playback_one_page(pPager, &offset, pDone, 0, 1);
}
assert( rc!=SQLITE_DONE );
@@ -40824,16 +49777,58 @@ static int pagerPlaybackSavepoint(Pager *pPager, PagerSavepoint *pSavepoint){
}
/*
-** Change the maximum number of in-memory pages that are allowed.
+** Change the maximum number of in-memory pages that are allowed
+** before attempting to recycle clean and unused pages.
*/
SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
sqlite3PcacheSetCachesize(pPager->pPCache, mxPage);
}
/*
-** Adjust the robustness of the database to damage due to OS crashes
-** or power failures by changing the number of syncs()s when writing
-** the rollback journal. There are three levels:
+** Change the maximum number of in-memory pages that are allowed
+** before attempting to spill pages to journal.
+*/
+SQLITE_PRIVATE int sqlite3PagerSetSpillsize(Pager *pPager, int mxPage){
+ return sqlite3PcacheSetSpillsize(pPager->pPCache, mxPage);
+}
+
+/*
+** Invoke SQLITE_FCNTL_MMAP_SIZE based on the current value of szMmap.
+*/
+static void pagerFixMaplimit(Pager *pPager){
+#if SQLITE_MAX_MMAP_SIZE>0
+ sqlite3_file *fd = pPager->fd;
+ if( isOpen(fd) && fd->pMethods->iVersion>=3 ){
+ sqlite3_int64 sz;
+ sz = pPager->szMmap;
+ pPager->bUseFetch = (sz>0);
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz);
+ }
+#endif
+}
+
+/*
+** Change the maximum size of any memory mapping made of the database file.
+*/
+SQLITE_PRIVATE void sqlite3PagerSetMmapLimit(Pager *pPager, sqlite3_int64 szMmap){
+ pPager->szMmap = szMmap;
+ pagerFixMaplimit(pPager);
+}
+
+/*
+** Free as much memory as possible from the pager.
+*/
+SQLITE_PRIVATE void sqlite3PagerShrink(Pager *pPager){
+ sqlite3PcacheShrink(pPager->pPCache);
+}
+
+/*
+** Adjust settings of the pager to those specified in the pgFlags parameter.
+**
+** The "level" in pgFlags & PAGER_SYNCHRONOUS_MASK sets the robustness
+** of the database to damage due to OS crashes or power failures by
+** changing the number of syncs()s when writing the journals.
+** There are four levels:
**
** OFF sqlite3OsSync() is never called. This is the default
** for temporary and transient files.
@@ -40853,6 +49848,10 @@ SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
** assurance that the journal will not be corrupted to the
** point of causing damage to the database during rollback.
**
+** EXTRA This is like FULL except that is also syncs the directory
+** that contains the rollback journal after the rollback
+** journal is unlinked.
+**
** The above is for a rollback-journal mode. For WAL mode, OFF continues
** to mean that no syncs ever occur. NORMAL means that the WAL is synced
** prior to the start of checkpoint and that the database file is synced
@@ -40860,7 +49859,8 @@ SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
** was written back into the database. But no sync operations occur for
** an ordinary commit in NORMAL mode with WAL. FULL means that the WAL
** file is synced following each commit operation, in addition to the
-** syncs associated with NORMAL.
+** syncs associated with NORMAL. There is no difference between FULL
+** and EXTRA for WAL mode.
**
** Do not confuse synchronous=FULL with SQLITE_SYNC_FULL. The
** SQLITE_SYNC_FULL macro means to use the MacOSX-style full-fsync
@@ -40874,28 +49874,42 @@ SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager *pPager, int mxPage){
** and FULL=3.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-SQLITE_PRIVATE void sqlite3PagerSetSafetyLevel(
+SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int bFullFsync, /* PRAGMA fullfsync */
- int bCkptFullFsync /* PRAGMA checkpoint_fullfsync */
+ unsigned pgFlags /* Various flags */
){
- assert( level>=1 && level<=3 );
- pPager->noSync = (level==1 || pPager->tempFile) ?1:0;
- pPager->fullSync = (level==3 && !pPager->tempFile) ?1:0;
+ unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
+ if( pPager->tempFile ){
+ pPager->noSync = 1;
+ pPager->fullSync = 0;
+ pPager->extraSync = 0;
+ }else{
+ pPager->noSync = level==PAGER_SYNCHRONOUS_OFF ?1:0;
+ pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
+ pPager->extraSync = level==PAGER_SYNCHRONOUS_EXTRA ?1:0;
+ }
if( pPager->noSync ){
pPager->syncFlags = 0;
pPager->ckptSyncFlags = 0;
- }else if( bFullFsync ){
+ }else if( pgFlags & PAGER_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_FULL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
- }else if( bCkptFullFsync ){
+ }else if( pgFlags & PAGER_CKPT_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_FULL;
}else{
pPager->syncFlags = SQLITE_SYNC_NORMAL;
pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
}
+ pPager->walSyncFlags = pPager->syncFlags;
+ if( pPager->fullSync ){
+ pPager->walSyncFlags |= WAL_SYNC_TRANSACTIONS;
+ }
+ if( pgFlags & PAGER_CACHESPILL ){
+ pPager->doNotSpill &= ~SPILLFLAG_OFF;
+ }else{
+ pPager->doNotSpill |= SPILLFLAG_OFF;
+ }
}
#endif
@@ -40966,9 +49980,16 @@ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(
Pager *pPager, /* Pager object */
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
-){
+){
pPager->xBusyHandler = xBusyHandler;
pPager->pBusyHandlerArg = pBusyHandlerArg;
+
+ if( isOpen(pPager->fd) ){
+ void **ap = (void **)&pPager->xBusyHandler;
+ assert( ((int(*)(void *))(ap[0]))==xBusyHandler );
+ assert( ap[1]==pBusyHandlerArg );
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_BUSYHANDLER, (void *)ap);
+ }
}
/*
@@ -41028,16 +50049,20 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
}
if( rc==SQLITE_OK ){
pNew = (char *)sqlite3PageMalloc(pageSize);
- if( !pNew ) rc = SQLITE_NOMEM;
+ if( !pNew ) rc = SQLITE_NOMEM_BKPT;
}
if( rc==SQLITE_OK ){
pager_reset(pPager);
- pPager->dbSize = (Pgno)(nByte/pageSize);
- pPager->pageSize = pageSize;
+ rc = sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
+ }
+ if( rc==SQLITE_OK ){
sqlite3PageFree(pPager->pTmpSpace);
pPager->pTmpSpace = pNew;
- sqlite3PcacheSetPageSize(pPager->pPCache, pageSize);
+ pPager->dbSize = (Pgno)((nByte+pageSize-1)/pageSize);
+ pPager->pageSize = pageSize;
+ }else{
+ sqlite3PageFree(pNew);
}
}
@@ -41047,6 +50072,7 @@ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u32 *pPageSize, int nR
assert( nReserve>=0 && nReserve<1000 );
pPager->nReserve = (i16)nReserve;
pagerReportSize(pPager);
+ pagerFixMaplimit(pPager);
}
return rc;
}
@@ -41170,7 +50196,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
int rc; /* Return code */
/* Check that this is either a no-op (because the requested lock is
- ** already held, or one of the transistions that the busy-handler
+ ** already held), or one of the transitions that the busy-handler
** may be invoked during, according to the comment above
** sqlite3PagerSetBusyhandler().
*/
@@ -41200,7 +50226,7 @@ static int pager_wait_on_lock(Pager *pPager, int locktype){
** dirty page were to be discarded from the cache via the pagerStress()
** routine, pagerStress() would not write the current page content to
** the database file. If a savepoint transaction were rolled back after
-** this happened, the correct behaviour would be to restore the current
+** this happened, the correct behavior would be to restore the current
** content of the page. However, since this content is not present in either
** the database file or the portion of the rollback journal and
** sub-journal rolled back the content could not be restored and the
@@ -41224,12 +50250,26 @@ static void assertTruncateConstraint(Pager *pPager){
** function does not actually modify the database file on disk. It
** just sets the internal state of the pager object so that the
** truncation will be done when the current transaction is committed.
+**
+** This function is only called right before committing a transaction.
+** Once this function has been called, the transaction must either be
+** rolled back or committed. It is not safe to call this function and
+** then continue writing to the database.
*/
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager *pPager, Pgno nPage){
assert( pPager->dbSize>=nPage );
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
pPager->dbSize = nPage;
- assertTruncateConstraint(pPager);
+
+ /* At one point the code here called assertTruncateConstraint() to
+ ** ensure that all pages being truncated away by this operation are,
+ ** if one or more savepoints are open, present in the savepoint
+ ** journal so that they can be restored if the savepoint is rolled
+ ** back. This is no longer necessary as this function is now only
+ ** called right before committing a transaction. So although the
+ ** Pager object may still have open savepoints (Pager.nSavepoint!=0),
+ ** they cannot be rolled back. So the assertTruncateConstraint() call
+ ** is no longer correct. */
}
@@ -41259,6 +50299,81 @@ static int pagerSyncHotJournal(Pager *pPager){
}
/*
+** Obtain a reference to a memory mapped page object for page number pgno.
+** The new object will use the pointer pData, obtained from xFetch().
+** If successful, set *ppPage to point to the new page reference
+** and return SQLITE_OK. Otherwise, return an SQLite error code and set
+** *ppPage to zero.
+**
+** Page references obtained by calling this function should be released
+** by calling pagerReleaseMapPage().
+*/
+static int pagerAcquireMapPage(
+ Pager *pPager, /* Pager object */
+ Pgno pgno, /* Page number */
+ void *pData, /* xFetch()'d data for this page */
+ PgHdr **ppPage /* OUT: Acquired page object */
+){
+ PgHdr *p; /* Memory mapped page to return */
+
+ if( pPager->pMmapFreelist ){
+ *ppPage = p = pPager->pMmapFreelist;
+ pPager->pMmapFreelist = p->pDirty;
+ p->pDirty = 0;
+ memset(p->pExtra, 0, pPager->nExtra);
+ }else{
+ *ppPage = p = (PgHdr *)sqlite3MallocZero(sizeof(PgHdr) + pPager->nExtra);
+ if( p==0 ){
+ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1) * pPager->pageSize, pData);
+ return SQLITE_NOMEM_BKPT;
+ }
+ p->pExtra = (void *)&p[1];
+ p->flags = PGHDR_MMAP;
+ p->nRef = 1;
+ p->pPager = pPager;
+ }
+
+ assert( p->pExtra==(void *)&p[1] );
+ assert( p->pPage==0 );
+ assert( p->flags==PGHDR_MMAP );
+ assert( p->pPager==pPager );
+ assert( p->nRef==1 );
+
+ p->pgno = pgno;
+ p->pData = pData;
+ pPager->nMmapOut++;
+
+ return SQLITE_OK;
+}
+
+/*
+** Release a reference to page pPg. pPg must have been returned by an
+** earlier call to pagerAcquireMapPage().
+*/
+static void pagerReleaseMapPage(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ pPager->nMmapOut--;
+ pPg->pDirty = pPager->pMmapFreelist;
+ pPager->pMmapFreelist = pPg;
+
+ assert( pPager->fd->pMethods->iVersion>=3 );
+ sqlite3OsUnfetch(pPager->fd, (i64)(pPg->pgno-1)*pPager->pageSize, pPg->pData);
+}
+
+/*
+** Free all PgHdr objects stored in the Pager.pMmapFreelist list.
+*/
+static void pagerFreeMapHdrs(Pager *pPager){
+ PgHdr *p;
+ PgHdr *pNext;
+ for(p=pPager->pMmapFreelist; p; p=pNext){
+ pNext = p->pDirty;
+ sqlite3_free(p);
+ }
+}
+
+
+/*
** Shutdown the page cache. Free all memory and close all files.
**
** If a transaction was in progress when this routine is called, that
@@ -41278,6 +50393,7 @@ SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager){
assert( assert_pager_state(pPager) );
disable_simulated_io_errors();
sqlite3BeginBenignMalloc();
+ pagerFreeMapHdrs(pPager);
/* pPager->errCode = 0; */
pPager->exclusiveMode = 0;
#ifndef SQLITE_OMIT_WAL
@@ -41347,7 +50463,7 @@ SQLITE_PRIVATE void sqlite3PagerRef(DbPage *pPg){
**
** If the Pager.noSync flag is set, then this function is a no-op.
** Otherwise, the actions required depend on the journal-mode and the
-** device characteristics of the the file-system, as follows:
+** device characteristics of the file-system, as follows:
**
** * If the journal file is an in-memory journal file, no action need
** be taken.
@@ -41523,8 +50639,9 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
/* This function is only called for rollback pagers in WRITER_DBMOD state. */
assert( !pagerUseWal(pPager) );
- assert( pPager->eState==PAGER_WRITER_DBMOD );
+ assert( pPager->tempFile || pPager->eState==PAGER_WRITER_DBMOD );
assert( pPager->eLock==EXCLUSIVE_LOCK );
+ assert( isOpen(pPager->fd) || pList->pDirty==0 );
/* If the file is a temp-file has not yet been opened, open it now. It
** is not possible for rc to be other than SQLITE_OK if this branch
@@ -41539,9 +50656,12 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
** file size will be.
*/
assert( rc!=SQLITE_OK || isOpen(pPager->fd) );
- if( rc==SQLITE_OK && pPager->dbSize>pPager->dbHintSize ){
+ if( rc==SQLITE_OK
+ && pPager->dbHintSize<pPager->dbSize
+ && (pList->pDirty || pList->pgno>pPager->dbHintSize)
+ ){
sqlite3_int64 szFile = pPager->pageSize * (sqlite3_int64)pPager->dbSize;
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
+ sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_SIZE_HINT, &szFile);
pPager->dbHintSize = pPager->dbSize;
}
@@ -41564,7 +50684,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
if( pList->pgno==1 ) pager_write_changecounter(pList);
/* Encode the database */
- CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM, pData);
+ CODEC2(pPager, pList->pData, pgno, 6, return SQLITE_NOMEM_BKPT, pData);
/* Write out the page data. */
rc = sqlite3OsWrite(pPager->fd, pData, pPager->pageSize, offset);
@@ -41579,6 +50699,7 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
if( pgno>pPager->dbFileSize ){
pPager->dbFileSize = pgno;
}
+ pPager->aStat[PAGER_STAT_WRITE]++;
/* Update any backup objects copying the contents of this pager. */
sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)pList->pData);
@@ -41587,7 +50708,6 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
PAGERID(pPager), pgno, pager_pagehash(pList)));
IOTRACE(("PGOUT %p %d\n", pPager, pgno));
PAGER_INCR(sqlite3_pager_writedb_count);
- PAGER_INCR(pPager->nWrite);
}else{
PAGERTRACE(("NOSTORE %d page %d\n", PAGERID(pPager), pgno));
}
@@ -41609,19 +50729,20 @@ static int pager_write_pagelist(Pager *pPager, PgHdr *pList){
static int openSubJournal(Pager *pPager){
int rc = SQLITE_OK;
if( !isOpen(pPager->sjfd) ){
+ const int flags = SQLITE_OPEN_SUBJOURNAL | SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE
+ | SQLITE_OPEN_DELETEONCLOSE;
+ int nStmtSpill = sqlite3Config.nStmtSpill;
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY || pPager->subjInMemory ){
- sqlite3MemJournalOpen(pPager->sjfd);
- }else{
- rc = pagerOpentemp(pPager, pPager->sjfd, SQLITE_OPEN_SUBJOURNAL);
+ nStmtSpill = -1;
}
+ rc = sqlite3JournalOpen(pPager->pVfs, 0, pPager->sjfd, flags, nStmtSpill);
}
return rc;
}
/*
** Append a record of the current state of page pPg to the sub-journal.
-** It is the callers responsibility to use subjRequiresPage() to check
-** that it is really required before calling this function.
**
** If successful, set the bit corresponding to pPg->pgno in the bitvecs
** for all open savepoints before returning.
@@ -41641,7 +50762,7 @@ static int subjournalPage(PgHdr *pPg){
assert( isOpen(pPager->jfd) || pagerUseWal(pPager) );
assert( isOpen(pPager->sjfd) || pPager->nSubRec==0 );
assert( pagerUseWal(pPager)
- || pageInJournal(pPg)
+ || pageInJournal(pPager, pPg)
|| pPg->pgno>pPager->dbOrigSize
);
rc = openSubJournal(pPager);
@@ -41650,10 +50771,10 @@ static int subjournalPage(PgHdr *pPg){
** write the journal record into the file. */
if( rc==SQLITE_OK ){
void *pData = pPg->pData;
- i64 offset = pPager->nSubRec*(4+pPager->pageSize);
+ i64 offset = (i64)pPager->nSubRec*(4+pPager->pageSize);
char *pData2;
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
+ CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
PAGERTRACE(("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno));
rc = write32bits(pPager->sjfd, offset, pPg->pgno);
if( rc==SQLITE_OK ){
@@ -41668,6 +50789,13 @@ static int subjournalPage(PgHdr *pPg){
}
return rc;
}
+static int subjournalPageIfRequired(PgHdr *pPg){
+ if( subjRequiresPage(pPg) ){
+ return subjournalPage(pPg);
+ }else{
+ return SQLITE_OK;
+ }
+}
/*
** This function is called by the pcache layer when it has reached some
@@ -41695,35 +50823,39 @@ static int pagerStress(void *p, PgHdr *pPg){
assert( pPg->pPager==pPager );
assert( pPg->flags&PGHDR_DIRTY );
- /* The doNotSyncSpill flag is set during times when doing a sync of
+ /* The doNotSpill NOSYNC bit is set during times when doing a sync of
** journal (and adding a new header) is not allowed. This occurs
** during calls to sqlite3PagerWrite() while trying to journal multiple
** pages belonging to the same sector.
**
- ** The doNotSpill flag inhibits all cache spilling regardless of whether
- ** or not a sync is required. This is set during a rollback.
+ ** The doNotSpill ROLLBACK and OFF bits inhibits all cache spilling
+ ** regardless of whether or not a sync is required. This is set during
+ ** a rollback or by user request, respectively.
**
** Spilling is also prohibited when in an error state since that could
- ** lead to database corruption. In the current implementaton it
- ** is impossible for sqlite3PCacheFetch() to be called with createFlag==1
+ ** lead to database corruption. In the current implementation it
+ ** is impossible for sqlite3PcacheFetch() to be called with createFlag==3
** while in the error state, hence it is impossible for this routine to
** be called in the error state. Nevertheless, we include a NEVER()
** test for the error state as a safeguard against future changes.
*/
if( NEVER(pPager->errCode) ) return SQLITE_OK;
- if( pPager->doNotSpill ) return SQLITE_OK;
- if( pPager->doNotSyncSpill && (pPg->flags & PGHDR_NEED_SYNC)!=0 ){
+ testcase( pPager->doNotSpill & SPILLFLAG_ROLLBACK );
+ testcase( pPager->doNotSpill & SPILLFLAG_OFF );
+ testcase( pPager->doNotSpill & SPILLFLAG_NOSYNC );
+ if( pPager->doNotSpill
+ && ((pPager->doNotSpill & (SPILLFLAG_ROLLBACK|SPILLFLAG_OFF))!=0
+ || (pPg->flags & PGHDR_NEED_SYNC)!=0)
+ ){
return SQLITE_OK;
}
pPg->pDirty = 0;
if( pagerUseWal(pPager) ){
/* Write a single frame for this page to the log. */
- if( subjRequiresPage(pPg) ){
- rc = subjournalPage(pPg);
- }
+ rc = subjournalPageIfRequired(pPg);
if( rc==SQLITE_OK ){
- rc = pagerWalFrames(pPager, pPg, 0, 0, 0);
+ rc = pagerWalFrames(pPager, pPg, 0, 0);
}
}else{
@@ -41734,39 +50866,6 @@ static int pagerStress(void *p, PgHdr *pPg){
rc = syncJournal(pPager, 1);
}
- /* If the page number of this page is larger than the current size of
- ** the database image, it may need to be written to the sub-journal.
- ** This is because the call to pager_write_pagelist() below will not
- ** actually write data to the file in this case.
- **
- ** Consider the following sequence of events:
- **
- ** BEGIN;
- ** <journal page X>
- ** <modify page X>
- ** SAVEPOINT sp;
- ** <shrink database file to Y pages>
- ** pagerStress(page X)
- ** ROLLBACK TO sp;
- **
- ** If (X>Y), then when pagerStress is called page X will not be written
- ** out to the database file, but will be dropped from the cache. Then,
- ** following the "ROLLBACK TO sp" statement, reading page X will read
- ** data from the database file. This will be the copy of page X as it
- ** was when the transaction started, not as it was when "SAVEPOINT sp"
- ** was executed.
- **
- ** The solution is to write the current data for page X into the
- ** sub-journal file now (if it is not already there), so that it will
- ** be restored to its current value when the "ROLLBACK TO sp" is
- ** executed.
- */
- if( NEVER(
- rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg)
- ) ){
- rc = subjournalPage(pPg);
- }
-
/* Write the contents of the page out to the database file. */
if( rc==SQLITE_OK ){
assert( (pPg->flags&PGHDR_NEED_SYNC)==0 );
@@ -41783,6 +50882,25 @@ static int pagerStress(void *p, PgHdr *pPg){
return pager_error(pPager, rc);
}
+/*
+** Flush all unreferenced dirty pages to disk.
+*/
+SQLITE_PRIVATE int sqlite3PagerFlush(Pager *pPager){
+ int rc = pPager->errCode;
+ if( !MEMDB ){
+ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache);
+ assert( assert_pager_state(pPager) );
+ while( rc==SQLITE_OK && pList ){
+ PgHdr *pNext = pList->pDirty;
+ if( pList->nRef==0 ){
+ rc = pagerStress((void*)pPager, pList);
+ }
+ pList = pNext;
+ }
+ }
+
+ return rc;
+}
/*
** Allocate and initialize a new Pager object and put a pointer to it
@@ -41802,7 +50920,7 @@ static int pagerStress(void *p, PgHdr *pPg){
**
** The flags argument is used to specify properties that affect the
** operation of the pager. It should be passed some bitwise combination
-** of the PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK flags.
+** of the PAGER_* flags.
**
** The vfsFlags parameter is a bitmask to pass to the flags parameter
** of the xOpen() method of the supplied VFS when opening files.
@@ -41833,25 +50951,14 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
char *zPathname = 0; /* Full path to database file */
int nPathname = 0; /* Number of bytes in zPathname */
int useJournal = (flags & PAGER_OMIT_JOURNAL)==0; /* False to omit journal */
- int noReadlock = (flags & PAGER_NO_READLOCK)!=0; /* True to omit read-lock */
int pcacheSize = sqlite3PcacheSize(); /* Bytes to allocate for PCache */
u32 szPageDflt = SQLITE_DEFAULT_PAGE_SIZE; /* Default page size */
const char *zUri = 0; /* URI args to copy */
int nUri = 0; /* Number of bytes of URI args at *zUri */
/* Figure out how much space is required for each journal file-handle
- ** (there are two of them, the main journal and the sub-journal). This
- ** is the maximum space required for an in-memory journal file handle
- ** and a regular journal file-handle. Note that a "regular journal-handle"
- ** may be a wrapper capable of caching the first portion of the journal
- ** file in memory to implement the atomic-write optimization (see
- ** source file journal.c).
- */
- if( sqlite3JournalSize(pVfs)>sqlite3MemJournalSize() ){
- journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
- }else{
- journalFileSize = ROUND8(sqlite3MemJournalSize());
- }
+ ** (there are two of them, the main journal and the sub-journal). */
+ journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
/* Set the output variable to NULL in case an error occurs. */
*ppPager = 0;
@@ -41859,7 +50966,12 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
#ifndef SQLITE_OMIT_MEMORYDB
if( flags & PAGER_MEMORY ){
memDb = 1;
- zFilename = 0;
+ if( zFilename && zFilename[0] ){
+ zPathname = sqlite3DbStrDup(0, zFilename);
+ if( zPathname==0 ) return SQLITE_NOMEM_BKPT;
+ nPathname = sqlite3Strlen30(zPathname);
+ zFilename = 0;
+ }
}
#endif
@@ -41870,9 +50982,9 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
if( zFilename && zFilename[0] ){
const char *z;
nPathname = pVfs->mxPathname+1;
- zPathname = sqlite3Malloc(nPathname*2);
+ zPathname = sqlite3DbMallocRaw(0, nPathname*2);
if( zPathname==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
zPathname[0] = 0; /* Make sure initialized even if FullPathname() fails */
rc = sqlite3OsFullPathname(pVfs, zFilename, nPathname, zPathname);
@@ -41882,7 +50994,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
z += sqlite3Strlen30(z)+1;
z += sqlite3Strlen30(z)+1;
}
- nUri = &z[1] - zUri;
+ nUri = (int)(&z[1] - zUri);
+ assert( nUri>=0 );
if( rc==SQLITE_OK && nPathname+8>pVfs->mxPathname ){
/* This branch is taken when the journal path required by
** the database being opened will be more than pVfs->mxPathname
@@ -41893,7 +51006,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
rc = SQLITE_CANTOPEN_BKPT;
}
if( rc!=SQLITE_OK ){
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
return rc;
}
}
@@ -41916,15 +51029,15 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
ROUND8(pVfs->szOsFile) + /* The main db file */
journalFileSize * 2 + /* The two journal files */
nPathname + 1 + nUri + /* zFilename */
- nPathname + 8 + 1 /* zJournal */
+ nPathname + 8 + 2 /* zJournal */
#ifndef SQLITE_OMIT_WAL
- + nPathname + 4 + 1 /* zWal */
+ + nPathname + 4 + 2 /* zWal */
#endif
);
assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
if( !pPtr ){
- sqlite3_free(zPathname);
- return SQLITE_NOMEM;
+ sqlite3DbFree(0, zPathname);
+ return SQLITE_NOMEM_BKPT;
}
pPager = (Pager*)(pPtr);
pPager->pPCache = (PCache*)(pPtr += ROUND8(sizeof(*pPager)));
@@ -41939,17 +51052,17 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
assert( nPathname>0 );
pPager->zJournal = (char*)(pPtr += nPathname + 1 + nUri);
memcpy(pPager->zFilename, zPathname, nPathname);
- memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
+ if( nUri ) memcpy(&pPager->zFilename[nPathname+1], zUri, nUri);
memcpy(pPager->zJournal, zPathname, nPathname);
- memcpy(&pPager->zJournal[nPathname], "-journal", 8);
+ memcpy(&pPager->zJournal[nPathname], "-journal\000", 8+2);
sqlite3FileSuffix3(pPager->zFilename, pPager->zJournal);
#ifndef SQLITE_OMIT_WAL
pPager->zWal = &pPager->zJournal[nPathname+8+1];
memcpy(pPager->zWal, zPathname, nPathname);
- memcpy(&pPager->zWal[nPathname], "-wal", 4);
+ memcpy(&pPager->zWal[nPathname], "-wal\000", 4+1);
sqlite3FileSuffix3(pPager->zFilename, pPager->zWal);
#endif
- sqlite3_free(zPathname);
+ sqlite3DbFree(0, zPathname);
}
pPager->pVfs = pVfs;
pPager->vfsFlags = vfsFlags;
@@ -41970,30 +51083,38 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
** + The value returned by sqlite3OsSectorSize()
** + The largest page size that can be written atomically.
*/
- if( rc==SQLITE_OK && !readOnly ){
- setSectorSize(pPager);
- assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
- if( szPageDflt<pPager->sectorSize ){
- if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
- szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
- }else{
- szPageDflt = (u32)pPager->sectorSize;
+ if( rc==SQLITE_OK ){
+ int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
+ if( !readOnly ){
+ setSectorSize(pPager);
+ assert(SQLITE_DEFAULT_PAGE_SIZE<=SQLITE_MAX_DEFAULT_PAGE_SIZE);
+ if( szPageDflt<pPager->sectorSize ){
+ if( pPager->sectorSize>SQLITE_MAX_DEFAULT_PAGE_SIZE ){
+ szPageDflt = SQLITE_MAX_DEFAULT_PAGE_SIZE;
+ }else{
+ szPageDflt = (u32)pPager->sectorSize;
+ }
}
- }
#ifdef SQLITE_ENABLE_ATOMIC_WRITE
- {
- int iDc = sqlite3OsDeviceCharacteristics(pPager->fd);
- int ii;
- assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
- assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
- assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
- for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
- if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){
- szPageDflt = ii;
+ {
+ int ii;
+ assert(SQLITE_IOCAP_ATOMIC512==(512>>8));
+ assert(SQLITE_IOCAP_ATOMIC64K==(65536>>8));
+ assert(SQLITE_MAX_DEFAULT_PAGE_SIZE<=65536);
+ for(ii=szPageDflt; ii<=SQLITE_MAX_DEFAULT_PAGE_SIZE; ii=ii*2){
+ if( iDc&(SQLITE_IOCAP_ATOMIC|(ii>>8)) ){
+ szPageDflt = ii;
+ }
}
}
- }
#endif
+ }
+ pPager->noLock = sqlite3_uri_boolean(zFilename, "nolock", 0);
+ if( (iDc & SQLITE_IOCAP_IMMUTABLE)!=0
+ || sqlite3_uri_boolean(zFilename, "immutable", 0) ){
+ vfsFlags |= SQLITE_OPEN_READONLY;
+ goto act_like_temp_file;
+ }
}
}else{
/* If a temporary file is requested, it is not opened immediately.
@@ -42003,10 +51124,14 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
** This branch is also run for an in-memory database. An in-memory
** database is the same as a temp-file that is never written out to
** disk and uses an in-memory rollback journal.
+ **
+ ** This branch also runs for files marked as immutable.
*/
+act_like_temp_file:
tempFile = 1;
- pPager->eState = PAGER_READER;
- pPager->eLock = EXCLUSIVE_LOCK;
+ pPager->eState = PAGER_READER; /* Pretend we already have a lock */
+ pPager->eLock = EXCLUSIVE_LOCK; /* Pretend we are in EXCLUSIVE mode */
+ pPager->noLock = 1; /* Do no locking */
readOnly = (vfsFlags&SQLITE_OPEN_READONLY);
}
@@ -42019,27 +51144,27 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
testcase( rc!=SQLITE_OK );
}
- /* If an error occurred in either of the blocks above, free the
- ** Pager structure and close the file.
+ /* Initialize the PCache object. */
+ if( rc==SQLITE_OK ){
+ assert( nExtra<1000 );
+ nExtra = ROUND8(nExtra);
+ rc = sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
+ !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
+ }
+
+ /* If an error occurred above, free the Pager structure and close the file.
*/
if( rc!=SQLITE_OK ){
- assert( !pPager->pTmpSpace );
sqlite3OsClose(pPager->fd);
+ sqlite3PageFree(pPager->pTmpSpace);
sqlite3_free(pPager);
return rc;
}
- /* Initialize the PCache object. */
- assert( nExtra<1000 );
- nExtra = ROUND8(nExtra);
- sqlite3PcacheOpen(szPageDflt, nExtra, !memDb,
- !memDb?pagerStress:0, (void *)pPager, pPager->pPCache);
-
PAGERTRACE(("OPEN %d %s\n", FILEHANDLEID(pPager->fd), pPager->zFilename));
IOTRACE(("OPEN %p %s\n", pPager, pPager->zFilename))
pPager->useJournal = (u8)useJournal;
- pPager->noReadlock = (noReadlock && readOnly) ?1:0;
/* pPager->stmtOpen = 0; */
/* pPager->stmtInUse = 0; */
/* pPager->nRef = 0; */
@@ -42048,9 +51173,6 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
/* pPager->nPage = 0; */
pPager->mxPgno = SQLITE_MAX_PAGE_COUNT;
/* pPager->state = PAGER_UNLOCK; */
-#if 0
- assert( pPager->state == (tempFile ? PAGER_EXCLUSIVE : PAGER_UNLOCK) );
-#endif
/* pPager->errMask = 0; */
pPager->tempFile = (u8)tempFile;
assert( tempFile==PAGER_LOCKINGMODE_NORMAL
@@ -42062,9 +51184,19 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
pPager->readOnly = (u8)readOnly;
assert( useJournal || pPager->tempFile );
pPager->noSync = pPager->tempFile;
- pPager->fullSync = pPager->noSync ?0:1;
- pPager->syncFlags = pPager->noSync ? 0 : SQLITE_SYNC_NORMAL;
- pPager->ckptSyncFlags = pPager->syncFlags;
+ if( pPager->noSync ){
+ assert( pPager->fullSync==0 );
+ assert( pPager->extraSync==0 );
+ assert( pPager->syncFlags==0 );
+ assert( pPager->walSyncFlags==0 );
+ assert( pPager->ckptSyncFlags==0 );
+ }else{
+ pPager->fullSync = 1;
+ pPager->extraSync = 0;
+ pPager->syncFlags = SQLITE_SYNC_NORMAL;
+ pPager->walSyncFlags = SQLITE_SYNC_NORMAL | WAL_SYNC_TRANSACTIONS;
+ pPager->ckptSyncFlags = SQLITE_SYNC_NORMAL;
+ }
/* pPager->pFirst = 0; */
/* pPager->pFirstSynced = 0; */
/* pPager->pLast = 0; */
@@ -42081,12 +51213,37 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
/* pPager->pBusyHandlerArg = 0; */
pPager->xReiniter = xReinit;
/* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
+ /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */
*ppPager = pPager;
return SQLITE_OK;
}
+/* Verify that the database file has not be deleted or renamed out from
+** under the pager. Return SQLITE_OK if the database is still were it ought
+** to be on disk. Return non-zero (SQLITE_READONLY_DBMOVED or some other error
+** code from sqlite3OsAccess()) if the database has gone missing.
+*/
+static int databaseIsUnmoved(Pager *pPager){
+ int bHasMoved = 0;
+ int rc;
+
+ if( pPager->tempFile ) return SQLITE_OK;
+ if( pPager->dbSize==0 ) return SQLITE_OK;
+ assert( pPager->zFilename && pPager->zFilename[0] );
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_HAS_MOVED, &bHasMoved);
+ if( rc==SQLITE_NOTFOUND ){
+ /* If the HAS_MOVED file-control is unimplemented, assume that the file
+ ** has not been moved. That is the historical behavior of SQLite: prior to
+ ** version 3.8.3, it never checked */
+ rc = SQLITE_OK;
+ }else if( rc==SQLITE_OK && bHasMoved ){
+ rc = SQLITE_READONLY_DBMOVED;
+ }
+ return rc;
+}
+
/*
** This function is called after transitioning from PAGER_UNLOCK to
@@ -42152,15 +51309,18 @@ static int hasHotJournal(Pager *pPager, int *pExists){
if( rc==SQLITE_OK && !locked ){
Pgno nPage; /* Number of pages in database file */
- /* Check the size of the database file. If it consists of 0 pages,
- ** then delete the journal file. See the header comment above for
- ** the reasoning here. Delete the obsolete journal file under
- ** a RESERVED lock to avoid race conditions and to avoid violating
- ** [H33020].
- */
+ assert( pPager->tempFile==0 );
rc = pagerPagecount(pPager, &nPage);
if( rc==SQLITE_OK ){
- if( nPage==0 ){
+ /* If the database is zero pages in size, that means that either (1) the
+ ** journal is a remnant from a prior database with the same name where
+ ** the database file but not the journal was deleted, or (2) the initial
+ ** transaction that populates a new database is being rolled back.
+ ** In either case, the journal file can be deleted. However, take care
+ ** not to delete the journal file if it is already open due to
+ ** journal_mode=PERSIST.
+ */
+ if( nPage==0 && !jrnlOpen ){
sqlite3BeginBenignMalloc();
if( pagerLockDb(pPager, RESERVED_LOCK)==SQLITE_OK ){
sqlite3OsDelete(pVfs, pPager->zJournal, 0);
@@ -42190,7 +51350,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
*pExists = (first!=0);
}else if( rc==SQLITE_CANTOPEN ){
/* If we cannot open the rollback journal file in order to see if
- ** its has a zero header, that might be due to an I/O error, or
+ ** it has a zero header, that might be due to an I/O error, or
** it might be due to the race condition described above and in
** ticket #3883. Either way, assume that the journal is hot.
** This might be a false positive. But if it is, then the
@@ -42211,7 +51371,7 @@ static int hasHotJournal(Pager *pPager, int *pExists){
/*
** This function is called to obtain a shared lock on the database file.
-** It is illegal to call sqlite3PagerAcquire() until after this function
+** It is illegal to call sqlite3PagerGet() until after this function
** has been successfully called. If a shared-lock is already held when
** this function is called, it is a no-op.
**
@@ -42242,25 +51402,22 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
/* This routine is only called from b-tree and only when there are no
** outstanding pages. This implies that the pager state should either
** be OPEN or READER. READER is only possible if the pager is or was in
- ** exclusive access mode.
- */
+ ** exclusive access mode. */
assert( sqlite3PcacheRefCount(pPager->pPCache)==0 );
assert( assert_pager_state(pPager) );
assert( pPager->eState==PAGER_OPEN || pPager->eState==PAGER_READER );
- if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; }
+ assert( pPager->errCode==SQLITE_OK );
if( !pagerUseWal(pPager) && pPager->eState==PAGER_OPEN ){
int bHotJournal = 1; /* True if there exists a hot journal-file */
assert( !MEMDB );
- assert( pPager->noReadlock==0 || pPager->readOnly );
+ assert( pPager->tempFile==0 || pPager->eLock==EXCLUSIVE_LOCK );
- if( pPager->noReadlock==0 ){
- rc = pager_wait_on_lock(pPager, SHARED_LOCK);
- if( rc!=SQLITE_OK ){
- assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
- goto failed;
- }
+ rc = pager_wait_on_lock(pPager, SHARED_LOCK);
+ if( rc!=SQLITE_OK ){
+ assert( pPager->eLock==NO_LOCK || pPager->eLock==UNKNOWN_LOCK );
+ goto failed;
}
/* If a journal file exists, and there is no RESERVED lock on the
@@ -42273,6 +51430,11 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
goto failed;
}
if( bHotJournal ){
+ if( pPager->readOnly ){
+ rc = SQLITE_READONLY_ROLLBACK;
+ goto failed;
+ }
+
/* Get an EXCLUSIVE lock on the database file. At this point it is
** important that a RESERVED lock is not obtained on the way to the
** EXCLUSIVE lock. If it were, another process might open the
@@ -42336,7 +51498,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
assert( rc==SQLITE_OK );
rc = pagerSyncHotJournal(pPager);
if( rc==SQLITE_OK ){
- rc = pager_playback(pPager, 1);
+ rc = pager_playback(pPager, !pPager->tempFile);
pPager->eState = PAGER_OPEN;
}
}else if( !pPager->exclusiveMode ){
@@ -42370,16 +51532,14 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
);
}
- if( !pPager->tempFile
- && (pPager->pBackup || sqlite3PcachePagecount(pPager->pPCache)>0)
- ){
- /* The shared-lock has just been acquired on the database file
- ** and there are already pages in the cache (from a previous
- ** read or write transaction). Check to see if the database
- ** has been modified. If the database has changed, flush the
- ** cache.
+ if( !pPager->tempFile && pPager->hasHeldSharedLock ){
+ /* The shared-lock has just been acquired then check to
+ ** see if the database has been modified. If the database has changed,
+ ** flush the cache. The hasHeldSharedLock flag prevents this from
+ ** occurring on the very first access to a file, in order to save a
+ ** single unnecessary sqlite3OsRead() call at the start-up.
**
- ** Database changes is detected by looking at 15 bytes beginning
+ ** Database changes are detected by looking at 15 bytes beginning
** at offset 24 into the file. The first 4 of these 16 bytes are
** a 32-bit counter that is incremented with each change. The
** other bytes change randomly with each file change when
@@ -42398,7 +51558,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
if( nPage>0 ){
IOTRACE(("CKVERS %p %d\n", pPager, sizeof(dbFileVers)));
rc = sqlite3OsRead(pPager->fd, &dbFileVers, sizeof(dbFileVers), 24);
- if( rc!=SQLITE_OK ){
+ if( rc!=SQLITE_OK && rc!=SQLITE_IOERR_SHORT_READ ){
goto failed;
}
}else{
@@ -42407,6 +51567,16 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
if( memcmp(pPager->dbFileVers, dbFileVers, sizeof(dbFileVers))!=0 ){
pager_reset(pPager);
+
+ /* Unmap the database file. It is possible that external processes
+ ** may have truncated the database file and then extended it back
+ ** to its original size while this process was not holding a lock.
+ ** In this case there may exist a Pager.pMap mapping that appears
+ ** to be the right size but is not actually valid. Avoid this
+ ** possibility by unmapping the db here. */
+ if( USEFETCH(pPager) ){
+ sqlite3OsUnfetch(pPager->fd, 0, 0);
+ }
}
}
@@ -42424,7 +51594,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
rc = pagerBeginReadTransaction(pPager);
}
- if( pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
+ if( pPager->tempFile==0 && pPager->eState==PAGER_OPEN && rc==SQLITE_OK ){
rc = pagerPagecount(pPager, &pPager->dbSize);
}
@@ -42435,6 +51605,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
assert( pPager->eState==PAGER_OPEN );
}else{
pPager->eState = PAGER_READER;
+ pPager->hasHeldSharedLock = 1;
}
return rc;
}
@@ -42448,7 +51619,7 @@ SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){
** nothing to rollback, so this routine is a no-op.
*/
static void pagerUnlockIfUnused(Pager *pPager){
- if( (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
+ if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
pagerUnlockAndRollback(pPager);
}
}
@@ -42476,7 +51647,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
** page is initialized to all zeros.
**
** If noContent is true, it means that we do not care about the contents
-** of the page. This occurs in two seperate scenarios:
+** of the page. This occurs in two scenarios:
**
** a) When reading a free-list leaf page from the database, and
**
@@ -42503,28 +51674,93 @@ static void pagerUnlockIfUnused(Pager *pPager){
** Since Lookup() never goes to disk, it never has to deal with locks
** or journal files.
*/
-SQLITE_PRIVATE int sqlite3PagerAcquire(
+SQLITE_PRIVATE int sqlite3PagerGet(
Pager *pPager, /* The pager open on the database file */
Pgno pgno, /* Page number to fetch */
DbPage **ppPage, /* Write a pointer to the page here */
- int noContent /* Do not bother reading content from disk if true */
+ int flags /* PAGER_GET_XXX flags */
){
- int rc;
- PgHdr *pPg;
+ int rc = SQLITE_OK;
+ PgHdr *pPg = 0;
+ u32 iFrame = 0; /* Frame to read from WAL file */
+ const int noContent = (flags & PAGER_GET_NOCONTENT);
+
+ /* It is acceptable to use a read-only (mmap) page for any page except
+ ** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
+ ** flag was specified by the caller. And so long as the db is not a
+ ** temporary or in-memory database. */
+ const int bMmapOk = (pgno>1 && USEFETCH(pPager)
+ && (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
+#ifdef SQLITE_HAS_CODEC
+ && pPager->xCodec==0
+#endif
+ );
+ /* Optimization note: Adding the "pgno<=1" term before "pgno==0" here
+ ** allows the compiler optimizer to reuse the results of the "pgno>1"
+ ** test in the previous statement, and avoid testing pgno==0 in the
+ ** common case where pgno is large. */
+ if( pgno<=1 && pgno==0 ){
+ return SQLITE_CORRUPT_BKPT;
+ }
assert( pPager->eState>=PAGER_READER );
assert( assert_pager_state(pPager) );
+ assert( noContent==0 || bMmapOk==0 );
- if( pgno==0 ){
- return SQLITE_CORRUPT_BKPT;
- }
+ assert( pPager->hasHeldSharedLock==1 );
/* If the pager is in the error state, return an error immediately.
** Otherwise, request the page from the PCache layer. */
if( pPager->errCode!=SQLITE_OK ){
rc = pPager->errCode;
}else{
- rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage);
+ if( bMmapOk && pagerUseWal(pPager) ){
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
+ if( rc!=SQLITE_OK ) goto pager_acquire_err;
+ }
+
+ if( bMmapOk && iFrame==0 ){
+ void *pData = 0;
+
+ rc = sqlite3OsFetch(pPager->fd,
+ (i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData
+ );
+
+ if( rc==SQLITE_OK && pData ){
+ if( pPager->eState>PAGER_READER || pPager->tempFile ){
+ pPg = sqlite3PagerLookup(pPager, pgno);
+ }
+ if( pPg==0 ){
+ rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg);
+ }else{
+ sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData);
+ }
+ if( pPg ){
+ assert( rc==SQLITE_OK );
+ *ppPage = pPg;
+ return SQLITE_OK;
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ goto pager_acquire_err;
+ }
+ }
+
+ {
+ sqlite3_pcache_page *pBase;
+ pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
+ if( pBase==0 ){
+ rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
+ if( rc!=SQLITE_OK ) goto pager_acquire_err;
+ if( pBase==0 ){
+ pPg = *ppPage = 0;
+ rc = SQLITE_NOMEM_BKPT;
+ goto pager_acquire_err;
+ }
+ }
+ pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase);
+ assert( pPg!=0 );
+ }
}
if( rc!=SQLITE_OK ){
@@ -42534,22 +51770,21 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
pPg = 0;
goto pager_acquire_err;
}
- assert( (*ppPage)->pgno==pgno );
- assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 );
+ assert( pPg==(*ppPage) );
+ assert( pPg->pgno==pgno );
+ assert( pPg->pPager==pPager || pPg->pPager==0 );
- if( (*ppPage)->pPager && !noContent ){
+ if( pPg->pPager && !noContent ){
/* In this case the pcache already contains an initialized copy of
** the page. Return without further ado. */
assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
- PAGER_INCR(pPager->nHit);
+ pPager->aStat[PAGER_STAT_HIT]++;
return SQLITE_OK;
}else{
/* The pager cache has created a new page. Its content needs to
** be initialized. */
- PAGER_INCR(pPager->nMiss);
- pPg = *ppPage;
pPg->pPager = pPager;
/* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page
@@ -42559,7 +51794,8 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
goto pager_acquire_err;
}
- if( MEMDB || pPager->dbSize<pgno || noContent || !isOpen(pPager->fd) ){
+ assert( !isOpen(pPager->fd) || !MEMDB );
+ if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
if( pgno>pPager->mxPgno ){
rc = SQLITE_FULL;
goto pager_acquire_err;
@@ -42583,8 +51819,13 @@ SQLITE_PRIVATE int sqlite3PagerAcquire(
memset(pPg->pData, 0, pPager->pageSize);
IOTRACE(("ZERO %p %d\n", pPager, pgno));
}else{
+ if( pagerUseWal(pPager) && bMmapOk==0 ){
+ rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
+ if( rc!=SQLITE_OK ) goto pager_acquire_err;
+ }
assert( pPg->pPager==pPager );
- rc = readDbPage(pPg);
+ pPager->aStat[PAGER_STAT_MISS]++;
+ rc = readDbPage(pPg, iFrame);
if( rc!=SQLITE_OK ){
goto pager_acquire_err;
}
@@ -42617,13 +51858,14 @@ pager_acquire_err:
** has ever happened.
*/
SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
- PgHdr *pPg = 0;
+ sqlite3_pcache_page *pPage;
assert( pPager!=0 );
assert( pgno!=0 );
assert( pPager->pPCache!=0 );
- assert( pPager->eState>=PAGER_READER && pPager->eState!=PAGER_ERROR );
- sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg);
- return pPg;
+ pPage = sqlite3PcacheFetch(pPager->pPCache, pgno, 0);
+ assert( pPage==0 || pPager->hasHeldSharedLock );
+ if( pPage==0 ) return 0;
+ return sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pPage);
}
/*
@@ -42634,12 +51876,19 @@ SQLITE_PRIVATE DbPage *sqlite3PagerLookup(Pager *pPager, Pgno pgno){
** are released, a rollback occurs and the lock on the database is
** removed.
*/
-SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
- if( pPg ){
- Pager *pPager = pPg->pPager;
+SQLITE_PRIVATE void sqlite3PagerUnrefNotNull(DbPage *pPg){
+ Pager *pPager;
+ assert( pPg!=0 );
+ pPager = pPg->pPager;
+ if( pPg->flags & PGHDR_MMAP ){
+ pagerReleaseMapPage(pPg);
+ }else{
sqlite3PcacheRelease(pPg);
- pagerUnlockIfUnused(pPager);
}
+ pagerUnlockIfUnused(pPager);
+}
+SQLITE_PRIVATE void sqlite3PagerUnref(DbPage *pPg){
+ if( pPg ) sqlite3PagerUnrefNotNull(pPg);
}
/*
@@ -42680,7 +51929,7 @@ static int pager_open_journal(Pager *pPager){
if( !pagerUseWal(pPager) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){
pPager->pInJournal = sqlite3BitvecCreate(pPager->dbSize);
if( pPager->pInJournal==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/* Open the journal file if it is not already open. */
@@ -42688,19 +51937,25 @@ static int pager_open_journal(Pager *pPager){
if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){
sqlite3MemJournalOpen(pPager->jfd);
}else{
- const int flags = /* VFS flags to open journal file */
- SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
- (pPager->tempFile ?
- (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL):
- (SQLITE_OPEN_MAIN_JOURNAL)
+ int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+ int nSpill;
+
+ if( pPager->tempFile ){
+ flags |= (SQLITE_OPEN_DELETEONCLOSE|SQLITE_OPEN_TEMP_JOURNAL);
+ nSpill = sqlite3Config.nStmtSpill;
+ }else{
+ flags |= SQLITE_OPEN_MAIN_JOURNAL;
+ nSpill = jrnlBufferSize(pPager);
+ }
+
+ /* Verify that the database still has the same name as it did when
+ ** it was originally opened. */
+ rc = databaseIsUnmoved(pPager);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3JournalOpen (
+ pVfs, pPager->zJournal, pPager->jfd, flags, nSpill
);
- #ifdef SQLITE_ENABLE_ATOMIC_WRITE
- rc = sqlite3JournalOpen(
- pVfs, pPager->zJournal, pPager->jfd, flags, jrnlBufferSize(pPager)
- );
- #else
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, flags, 0);
- #endif
+ }
}
assert( rc!=SQLITE_OK || isOpen(pPager->jfd) );
}
@@ -42766,7 +52021,7 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
if( rc!=SQLITE_OK ){
return rc;
}
- sqlite3WalExclusiveMode(pPager->pWal, 1);
+ (void)sqlite3WalExclusiveMode(pPager->pWal, 1);
}
/* Grab the write lock on the log file. If successful, upgrade to
@@ -42814,6 +52069,59 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
}
/*
+** Write page pPg onto the end of the rollback journal.
+*/
+static SQLITE_NOINLINE int pagerAddPageToRollbackJournal(PgHdr *pPg){
+ Pager *pPager = pPg->pPager;
+ int rc;
+ u32 cksum;
+ char *pData2;
+ i64 iOff = pPager->journalOff;
+
+ /* We should never write to the journal file the page that
+ ** contains the database locks. The following assert verifies
+ ** that we do not. */
+ assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
+
+ assert( pPager->journalHdr<=pPager->journalOff );
+ CODEC2(pPager, pPg->pData, pPg->pgno, 7, return SQLITE_NOMEM_BKPT, pData2);
+ cksum = pager_cksum(pPager, (u8*)pData2);
+
+ /* Even if an IO or diskfull error occurs while journalling the
+ ** page in the block above, set the need-sync flag for the page.
+ ** Otherwise, when the transaction is rolled back, the logic in
+ ** playback_one_page() will think that the page needs to be restored
+ ** in the database file. And if an IO error occurs while doing so,
+ ** then corruption may follow.
+ */
+ pPg->flags |= PGHDR_NEED_SYNC;
+
+ rc = write32bits(pPager->jfd, iOff, pPg->pgno);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
+ if( rc!=SQLITE_OK ) return rc;
+ rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
+ if( rc!=SQLITE_OK ) return rc;
+
+ IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
+ pPager->journalOff, pPager->pageSize));
+ PAGER_INCR(sqlite3_pager_writej_count);
+ PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
+ PAGERID(pPager), pPg->pgno,
+ ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
+
+ pPager->journalOff += 8 + pPager->pageSize;
+ pPager->nRec++;
+ assert( pPager->pInJournal!=0 );
+ rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
+ testcase( rc==SQLITE_NOMEM );
+ assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
+ rc |= addToSavepointBitvecs(pPager, pPg->pgno);
+ assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
+ return rc;
+}
+
+/*
** Mark a single data page as writeable. The page is written into the
** main journal or sub-journal as required. If the page is written into
** one of the journals, the corresponding bit is set in the
@@ -42821,7 +52129,6 @@ SQLITE_PRIVATE int sqlite3PagerBegin(Pager *pPager, int exFlag, int subjInMemory
** of any open savepoints as appropriate.
*/
static int pager_write(PgHdr *pPg){
- void *pData = pPg->pData;
Pager *pPager = pPg->pPager;
int rc = SQLITE_OK;
@@ -42834,15 +52141,8 @@ static int pager_write(PgHdr *pPg){
|| pPager->eState==PAGER_WRITER_DBMOD
);
assert( assert_pager_state(pPager) );
-
- /* If an error has been previously detected, report the same error
- ** again. This should not happen, but the check provides robustness. */
- if( NEVER(pPager->errCode) ) return pPager->errCode;
-
- /* Higher-level routines never call this function if database is not
- ** writable. But check anyway, just for robustness. */
- if( NEVER(pPager->readOnly) ) return SQLITE_PERM;
-
+ assert( pPager->errCode==0 );
+ assert( pPager->readOnly==0 );
CHECK_PAGE(pPg);
/* The journal file needs to be opened. Higher level routines have already
@@ -42861,93 +52161,142 @@ static int pager_write(PgHdr *pPg){
assert( pPager->eState>=PAGER_WRITER_CACHEMOD );
assert( assert_pager_state(pPager) );
- /* Mark the page as dirty. If the page has already been written
- ** to the journal then we can return right away.
- */
+ /* Mark the page that is about to be modified as dirty. */
sqlite3PcacheMakeDirty(pPg);
- if( pageInJournal(pPg) && !subjRequiresPage(pPg) ){
- assert( !pagerUseWal(pPager) );
- }else{
-
- /* The transaction journal now exists and we have a RESERVED or an
- ** EXCLUSIVE lock on the main database file. Write the current page to
- ** the transaction journal if it is not there already.
- */
- if( !pageInJournal(pPg) && !pagerUseWal(pPager) ){
- assert( pagerUseWal(pPager)==0 );
- if( pPg->pgno<=pPager->dbOrigSize && isOpen(pPager->jfd) ){
- u32 cksum;
- char *pData2;
- i64 iOff = pPager->journalOff;
-
- /* We should never write to the journal file the page that
- ** contains the database locks. The following assert verifies
- ** that we do not. */
- assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
-
- assert( pPager->journalHdr<=pPager->journalOff );
- CODEC2(pPager, pData, pPg->pgno, 7, return SQLITE_NOMEM, pData2);
- cksum = pager_cksum(pPager, (u8*)pData2);
-
- /* Even if an IO or diskfull error occurs while journalling the
- ** page in the block above, set the need-sync flag for the page.
- ** Otherwise, when the transaction is rolled back, the logic in
- ** playback_one_page() will think that the page needs to be restored
- ** in the database file. And if an IO error occurs while doing so,
- ** then corruption may follow.
- */
+
+ /* If a rollback journal is in use, them make sure the page that is about
+ ** to change is in the rollback journal, or if the page is a new page off
+ ** then end of the file, make sure it is marked as PGHDR_NEED_SYNC.
+ */
+ assert( (pPager->pInJournal!=0) == isOpen(pPager->jfd) );
+ if( pPager->pInJournal!=0
+ && sqlite3BitvecTestNotNull(pPager->pInJournal, pPg->pgno)==0
+ ){
+ assert( pagerUseWal(pPager)==0 );
+ if( pPg->pgno<=pPager->dbOrigSize ){
+ rc = pagerAddPageToRollbackJournal(pPg);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ }else{
+ if( pPager->eState!=PAGER_WRITER_DBMOD ){
pPg->flags |= PGHDR_NEED_SYNC;
+ }
+ PAGERTRACE(("APPEND %d page %d needSync=%d\n",
+ PAGERID(pPager), pPg->pgno,
+ ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
+ }
+ }
- rc = write32bits(pPager->jfd, iOff, pPg->pgno);
- if( rc!=SQLITE_OK ) return rc;
- rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize, iOff+4);
- if( rc!=SQLITE_OK ) return rc;
- rc = write32bits(pPager->jfd, iOff+pPager->pageSize+4, cksum);
- if( rc!=SQLITE_OK ) return rc;
+ /* The PGHDR_DIRTY bit is set above when the page was added to the dirty-list
+ ** and before writing the page into the rollback journal. Wait until now,
+ ** after the page has been successfully journalled, before setting the
+ ** PGHDR_WRITEABLE bit that indicates that the page can be safely modified.
+ */
+ pPg->flags |= PGHDR_WRITEABLE;
+
+ /* If the statement journal is open and the page is not in it,
+ ** then write the page into the statement journal.
+ */
+ if( pPager->nSavepoint>0 ){
+ rc = subjournalPageIfRequired(pPg);
+ }
- IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
- pPager->journalOff, pPager->pageSize));
- PAGER_INCR(sqlite3_pager_writej_count);
- PAGERTRACE(("JOURNAL %d page %d needSync=%d hash(%08x)\n",
- PAGERID(pPager), pPg->pgno,
- ((pPg->flags&PGHDR_NEED_SYNC)?1:0), pager_pagehash(pPg)));
-
- pPager->journalOff += 8 + pPager->pageSize;
- pPager->nRec++;
- assert( pPager->pInJournal!=0 );
- rc = sqlite3BitvecSet(pPager->pInJournal, pPg->pgno);
- testcase( rc==SQLITE_NOMEM );
- assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
- rc |= addToSavepointBitvecs(pPager, pPg->pgno);
- if( rc!=SQLITE_OK ){
- assert( rc==SQLITE_NOMEM );
- return rc;
- }
- }else{
- if( pPager->eState!=PAGER_WRITER_DBMOD ){
- pPg->flags |= PGHDR_NEED_SYNC;
+ /* Update the database size and return. */
+ if( pPager->dbSize<pPg->pgno ){
+ pPager->dbSize = pPg->pgno;
+ }
+ return rc;
+}
+
+/*
+** This is a variant of sqlite3PagerWrite() that runs when the sector size
+** is larger than the page size. SQLite makes the (reasonable) assumption that
+** all bytes of a sector are written together by hardware. Hence, all bytes of
+** a sector need to be journalled in case of a power loss in the middle of
+** a write.
+**
+** Usually, the sector size is less than or equal to the page size, in which
+** case pages can be individually written. This routine only runs in the
+** exceptional case where the page size is smaller than the sector size.
+*/
+static SQLITE_NOINLINE int pagerWriteLargeSector(PgHdr *pPg){
+ int rc = SQLITE_OK; /* Return code */
+ Pgno nPageCount; /* Total number of pages in database file */
+ Pgno pg1; /* First page of the sector pPg is located on. */
+ int nPage = 0; /* Number of pages starting at pg1 to journal */
+ int ii; /* Loop counter */
+ int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
+ Pager *pPager = pPg->pPager; /* The pager that owns pPg */
+ Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
+
+ /* Set the doNotSpill NOSYNC bit to 1. This is because we cannot allow
+ ** a journal header to be written between the pages journaled by
+ ** this function.
+ */
+ assert( !MEMDB );
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)==0 );
+ pPager->doNotSpill |= SPILLFLAG_NOSYNC;
+
+ /* This trick assumes that both the page-size and sector-size are
+ ** an integer power of 2. It sets variable pg1 to the identifier
+ ** of the first page of the sector pPg is located on.
+ */
+ pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
+
+ nPageCount = pPager->dbSize;
+ if( pPg->pgno>nPageCount ){
+ nPage = (pPg->pgno - pg1)+1;
+ }else if( (pg1+nPagePerSector-1)>nPageCount ){
+ nPage = nPageCount+1-pg1;
+ }else{
+ nPage = nPagePerSector;
+ }
+ assert(nPage>0);
+ assert(pg1<=pPg->pgno);
+ assert((pg1+nPage)>pPg->pgno);
+
+ for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
+ Pgno pg = pg1+ii;
+ PgHdr *pPage;
+ if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
+ if( pg!=PAGER_MJ_PGNO(pPager) ){
+ rc = sqlite3PagerGet(pPager, pg, &pPage, 0);
+ if( rc==SQLITE_OK ){
+ rc = pager_write(pPage);
+ if( pPage->flags&PGHDR_NEED_SYNC ){
+ needSync = 1;
+ }
+ sqlite3PagerUnrefNotNull(pPage);
}
- PAGERTRACE(("APPEND %d page %d needSync=%d\n",
- PAGERID(pPager), pPg->pgno,
- ((pPg->flags&PGHDR_NEED_SYNC)?1:0)));
}
- }
-
- /* If the statement journal is open and the page is not in it,
- ** then write the current page to the statement journal. Note that
- ** the statement journal format differs from the standard journal format
- ** in that it omits the checksums and the header.
- */
- if( subjRequiresPage(pPg) ){
- rc = subjournalPage(pPg);
+ }else if( (pPage = sqlite3PagerLookup(pPager, pg))!=0 ){
+ if( pPage->flags&PGHDR_NEED_SYNC ){
+ needSync = 1;
+ }
+ sqlite3PagerUnrefNotNull(pPage);
}
}
- /* Update the database size and return.
+ /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages
+ ** starting at pg1, then it needs to be set for all of them. Because
+ ** writing to any of these nPage pages may damage the others, the
+ ** journal file must contain sync()ed copies of all of them
+ ** before any of them can be written out to the database file.
*/
- if( pPager->dbSize<pPg->pgno ){
- pPager->dbSize = pPg->pgno;
+ if( rc==SQLITE_OK && needSync ){
+ assert( !MEMDB );
+ for(ii=0; ii<nPage; ii++){
+ PgHdr *pPage = sqlite3PagerLookup(pPager, pg1+ii);
+ if( pPage ){
+ pPage->flags |= PGHDR_NEED_SYNC;
+ sqlite3PagerUnrefNotNull(pPage);
+ }
+ }
}
+
+ assert( (pPager->doNotSpill & SPILLFLAG_NOSYNC)!=0 );
+ pPager->doNotSpill &= ~SPILLFLAG_NOSYNC;
return rc;
}
@@ -42965,95 +52314,22 @@ static int pager_write(PgHdr *pPg){
** If an error occurs, SQLITE_NOMEM or an IO error code is returned
** as appropriate. Otherwise, SQLITE_OK.
*/
-SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){
- int rc = SQLITE_OK;
-
- PgHdr *pPg = pDbPage;
+SQLITE_PRIVATE int sqlite3PagerWrite(PgHdr *pPg){
Pager *pPager = pPg->pPager;
- Pgno nPagePerSector = (pPager->sectorSize/pPager->pageSize);
-
+ assert( (pPg->flags & PGHDR_MMAP)==0 );
assert( pPager->eState>=PAGER_WRITER_LOCKED );
- assert( pPager->eState!=PAGER_ERROR );
assert( assert_pager_state(pPager) );
-
- if( nPagePerSector>1 ){
- Pgno nPageCount; /* Total number of pages in database file */
- Pgno pg1; /* First page of the sector pPg is located on. */
- int nPage = 0; /* Number of pages starting at pg1 to journal */
- int ii; /* Loop counter */
- int needSync = 0; /* True if any page has PGHDR_NEED_SYNC */
-
- /* Set the doNotSyncSpill flag to 1. This is because we cannot allow
- ** a journal header to be written between the pages journaled by
- ** this function.
- */
- assert( !MEMDB );
- assert( pPager->doNotSyncSpill==0 );
- pPager->doNotSyncSpill++;
-
- /* This trick assumes that both the page-size and sector-size are
- ** an integer power of 2. It sets variable pg1 to the identifier
- ** of the first page of the sector pPg is located on.
- */
- pg1 = ((pPg->pgno-1) & ~(nPagePerSector-1)) + 1;
-
- nPageCount = pPager->dbSize;
- if( pPg->pgno>nPageCount ){
- nPage = (pPg->pgno - pg1)+1;
- }else if( (pg1+nPagePerSector-1)>nPageCount ){
- nPage = nPageCount+1-pg1;
- }else{
- nPage = nPagePerSector;
- }
- assert(nPage>0);
- assert(pg1<=pPg->pgno);
- assert((pg1+nPage)>pPg->pgno);
-
- for(ii=0; ii<nPage && rc==SQLITE_OK; ii++){
- Pgno pg = pg1+ii;
- PgHdr *pPage;
- if( pg==pPg->pgno || !sqlite3BitvecTest(pPager->pInJournal, pg) ){
- if( pg!=PAGER_MJ_PGNO(pPager) ){
- rc = sqlite3PagerGet(pPager, pg, &pPage);
- if( rc==SQLITE_OK ){
- rc = pager_write(pPage);
- if( pPage->flags&PGHDR_NEED_SYNC ){
- needSync = 1;
- }
- sqlite3PagerUnref(pPage);
- }
- }
- }else if( (pPage = pager_lookup(pPager, pg))!=0 ){
- if( pPage->flags&PGHDR_NEED_SYNC ){
- needSync = 1;
- }
- sqlite3PagerUnref(pPage);
- }
- }
-
- /* If the PGHDR_NEED_SYNC flag is set for any of the nPage pages
- ** starting at pg1, then it needs to be set for all of them. Because
- ** writing to any of these nPage pages may damage the others, the
- ** journal file must contain sync()ed copies of all of them
- ** before any of them can be written out to the database file.
- */
- if( rc==SQLITE_OK && needSync ){
- assert( !MEMDB );
- for(ii=0; ii<nPage; ii++){
- PgHdr *pPage = pager_lookup(pPager, pg1+ii);
- if( pPage ){
- pPage->flags |= PGHDR_NEED_SYNC;
- sqlite3PagerUnref(pPage);
- }
- }
- }
-
- assert( pPager->doNotSyncSpill==1 );
- pPager->doNotSyncSpill--;
+ if( pPager->errCode ){
+ return pPager->errCode;
+ }else if( (pPg->flags & PGHDR_WRITEABLE)!=0 && pPager->dbSize>=pPg->pgno ){
+ if( pPager->nSavepoint ) return subjournalPageIfRequired(pPg);
+ return SQLITE_OK;
+ }else if( pPager->sectorSize > (u32)pPager->pageSize ){
+ assert( pPager->tempFile==0 );
+ return pagerWriteLargeSector(pPg);
}else{
- rc = pager_write(pDbPage);
+ return pager_write(pPg);
}
- return rc;
}
/*
@@ -43063,7 +52339,7 @@ SQLITE_PRIVATE int sqlite3PagerWrite(DbPage *pDbPage){
*/
#ifndef NDEBUG
SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){
- return pPg->flags&PGHDR_DIRTY;
+ return pPg->flags & PGHDR_WRITEABLE;
}
#endif
@@ -43080,13 +52356,21 @@ SQLITE_PRIVATE int sqlite3PagerIswriteable(DbPage *pPg){
**
** Tests show that this optimization can quadruple the speed of large
** DELETE operations.
+**
+** This optimization cannot be used with a temp-file, as the page may
+** have been dirty at the start of the transaction. In that case, if
+** memory pressure forces page pPg out of the cache, the data does need
+** to be written out to disk so that it may be read back in if the
+** current transaction is rolled back.
*/
SQLITE_PRIVATE void sqlite3PagerDontWrite(PgHdr *pPg){
Pager *pPager = pPg->pPager;
- if( (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
+ if( !pPager->tempFile && (pPg->flags&PGHDR_DIRTY) && pPager->nSavepoint==0 ){
PAGERTRACE(("DONT_WRITE page %d of %d\n", pPg->pgno, PAGERID(pPager)));
IOTRACE(("CLEAN %p %d\n", pPager, pPg->pgno))
pPg->flags |= PGHDR_DONT_WRITE;
+ pPg->flags &= ~PGHDR_WRITEABLE;
+ testcase( pPg->flags & PGHDR_NEED_SYNC );
pager_set_pagehash(pPg);
}
}
@@ -43139,13 +52423,13 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
# define DIRECT_MODE isDirectMode
#endif
- if( !pPager->changeCountDone && pPager->dbSize>0 ){
+ if( !pPager->changeCountDone && ALWAYS(pPager->dbSize>0) ){
PgHdr *pPgHdr; /* Reference to page 1 */
assert( !pPager->tempFile && isOpen(pPager->fd) );
/* Open page 1 of the file for writing. */
- rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
+ rc = sqlite3PagerGet(pPager, 1, &pPgHdr, 0);
assert( pPgHdr==0 || rc==SQLITE_OK );
/* If page one was fetched successfully, and this function is not
@@ -43165,11 +52449,17 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
if( DIRECT_MODE ){
const void *zBuf;
assert( pPager->dbFileSize>0 );
- CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM, zBuf);
+ CODEC2(pPager, pPgHdr->pData, 1, 6, rc=SQLITE_NOMEM_BKPT, zBuf);
if( rc==SQLITE_OK ){
rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0);
+ pPager->aStat[PAGER_STAT_WRITE]++;
}
if( rc==SQLITE_OK ){
+ /* Update the pager's copy of the change-counter. Otherwise, the
+ ** next time a read transaction is opened the cache will be
+ ** flushed (as the change-counter values will not match). */
+ const void *pCopy = (const void *)&((const char *)zBuf)[24];
+ memcpy(&pPager->dbFileVers, pCopy, sizeof(pPager->dbFileVers));
pPager->changeCountDone = 1;
}
}else{
@@ -43190,14 +52480,17 @@ static int pager_incr_changecounter(Pager *pPager, int isDirectMode){
** If successful, or if called on a pager for which it is a no-op, this
** function returns SQLITE_OK. Otherwise, an IO error code is returned.
*/
-SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){
+SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager, const char *zMaster){
int rc = SQLITE_OK;
- if( !pPager->noSync ){
+
+ if( isOpen(pPager->fd) ){
+ void *pArg = (void*)zMaster;
+ rc = sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC, pArg);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ }
+ if( rc==SQLITE_OK && !pPager->noSync ){
assert( !MEMDB );
rc = sqlite3OsSync(pPager->fd, pPager->syncFlags);
- }else if( isOpen(pPager->fd) ){
- assert( !MEMDB );
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_SYNC_OMITTED, (void *)&rc);
}
return rc;
}
@@ -43214,14 +52507,17 @@ SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){
** returned.
*/
SQLITE_PRIVATE int sqlite3PagerExclusiveLock(Pager *pPager){
- int rc = SQLITE_OK;
- assert( pPager->eState==PAGER_WRITER_CACHEMOD
- || pPager->eState==PAGER_WRITER_DBMOD
- || pPager->eState==PAGER_WRITER_LOCKED
- );
+ int rc = pPager->errCode;
assert( assert_pager_state(pPager) );
- if( 0==pagerUseWal(pPager) ){
- rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ if( rc==SQLITE_OK ){
+ assert( pPager->eState==PAGER_WRITER_CACHEMOD
+ || pPager->eState==PAGER_WRITER_DBMOD
+ || pPager->eState==PAGER_WRITER_LOCKED
+ );
+ assert( assert_pager_state(pPager) );
+ if( 0==pagerUseWal(pPager) ){
+ rc = pager_wait_on_lock(pPager, EXCLUSIVE_LOCK);
+ }
}
return rc;
}
@@ -43269,17 +52565,21 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
/* If a prior error occurred, report that error again. */
if( NEVER(pPager->errCode) ) return pPager->errCode;
+ /* Provide the ability to easily simulate an I/O error during testing */
+ if( sqlite3FaultSim(400) ) return SQLITE_IOERR;
+
PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n",
pPager->zFilename, zMaster, pPager->dbSize));
/* If no database changes have been made, return early. */
if( pPager->eState<PAGER_WRITER_CACHEMOD ) return SQLITE_OK;
- if( MEMDB ){
+ assert( MEMDB==0 || pPager->tempFile );
+ assert( isOpen(pPager->fd) || pPager->tempFile );
+ if( 0==pagerFlushOnCommit(pPager, 1) ){
/* If this is an in-memory db, or no pages have been written to, or this
** function has already been called, it is mostly a no-op. However, any
- ** backup in progress needs to be restarted.
- */
+ ** backup in progress needs to be restarted. */
sqlite3BackupRestart(pPager->pBackup);
}else{
if( pagerUseWal(pPager) ){
@@ -43288,15 +52588,13 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
if( pList==0 ){
/* Must have at least one page for the WAL commit flag.
** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */
- rc = sqlite3PagerGet(pPager, 1, &pPageOne);
+ rc = sqlite3PagerGet(pPager, 1, &pPageOne, 0);
pList = pPageOne;
pList->pDirty = 0;
}
assert( rc==SQLITE_OK );
if( ALWAYS(pList) ){
- rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1,
- (pPager->fullSync ? pPager->syncFlags : 0)
- );
+ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1);
}
sqlite3PagerUnref(pPageOne);
if( rc==SQLITE_OK ){
@@ -43355,38 +52653,6 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
#endif
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
- /* If this transaction has made the database smaller, then all pages
- ** being discarded by the truncation must be written to the journal
- ** file. This can only happen in auto-vacuum mode.
- **
- ** Before reading the pages with page numbers larger than the
- ** current value of Pager.dbSize, set dbSize back to the value
- ** that it took at the start of the transaction. Otherwise, the
- ** calls to sqlite3PagerGet() return zeroed pages instead of
- ** reading data from the database file.
- */
- #ifndef SQLITE_OMIT_AUTOVACUUM
- if( pPager->dbSize<pPager->dbOrigSize
- && pPager->journalMode!=PAGER_JOURNALMODE_OFF
- ){
- Pgno i; /* Iterator variable */
- const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
- const Pgno dbSize = pPager->dbSize; /* Database image size */
- pPager->dbSize = pPager->dbOrigSize;
- for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){
- if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
- PgHdr *pPage; /* Page to journal */
- rc = sqlite3PagerGet(pPager, i, &pPage);
- if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
- rc = sqlite3PagerWrite(pPage);
- sqlite3PagerUnref(pPage);
- if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
- }
- }
- pPager->dbSize = dbSize;
- }
- #endif
-
/* Write the master journal name into the journal file. If a master
** journal file name has already been written to the journal file,
** or if zMaster is NULL (no master journal), then this call is a no-op.
@@ -43414,11 +52680,14 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
goto commit_phase_one_exit;
}
sqlite3PcacheCleanAll(pPager->pPCache);
-
- /* If the file on disk is not the same size as the database image,
- ** then use pager_truncate to grow or shrink the file here.
- */
- if( pPager->dbSize!=pPager->dbFileSize ){
+
+ /* If the file on disk is smaller than the database image, use
+ ** pager_truncate to grow the file here. This can happen if the database
+ ** image was extended as part of the current transaction and then the
+ ** last page in the db image moved to the free-list. In this case the
+ ** last page is never written out to disk, leaving the database file
+ ** undersized. Fix this now if it is the case. */
+ if( pPager->dbSize>pPager->dbFileSize ){
Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
assert( pPager->eState==PAGER_WRITER_DBMOD );
rc = pager_truncate(pPager, nNew);
@@ -43427,7 +52696,7 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseOne(
/* Finally, sync the database file. */
if( !noSync ){
- rc = sqlite3PagerSync(pPager);
+ rc = sqlite3PagerSync(pPager, zMaster);
}
IOTRACE(("DBSYNC %p\n", pPager))
}
@@ -43491,7 +52760,8 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){
}
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
- rc = pager_end_transaction(pPager, pPager->setMaster);
+ pPager->iDataVersion++;
+ rc = pager_end_transaction(pPager, pPager->setMaster, 1);
return pager_error(pPager, rc);
}
@@ -43536,11 +52806,11 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
if( pagerUseWal(pPager) ){
int rc2;
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
- rc2 = pager_end_transaction(pPager, pPager->setMaster);
+ rc2 = pager_end_transaction(pPager, pPager->setMaster, 0);
if( rc==SQLITE_OK ) rc = rc2;
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
int eState = pPager->eState;
- rc = pager_end_transaction(pPager, 0);
+ rc = pager_end_transaction(pPager, 0, 0);
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
/* This can happen using journal_mode=off. Move the pager to the error
** state to indicate that the contents of the cache may not be trusted.
@@ -43555,7 +52825,10 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager *pPager){
}
assert( pPager->eState==PAGER_READER || rc!=SQLITE_OK );
- assert( rc==SQLITE_OK || rc==SQLITE_FULL || (rc&0xFF)==SQLITE_IOERR );
+ assert( rc==SQLITE_OK || rc==SQLITE_FULL || rc==SQLITE_CORRUPT
+ || rc==SQLITE_NOMEM || (rc&0xFF)==SQLITE_IOERR
+ || rc==SQLITE_CANTOPEN
+ );
/* If an error occurs during a ROLLBACK, we can no longer trust the pager
** cache. So call pager_error() on the way out to make any error persistent.
@@ -43571,12 +52844,14 @@ SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager *pPager){
return pPager->readOnly;
}
+#ifdef SQLITE_DEBUG
/*
-** Return the number of references to the pager.
+** Return the sum of the reference counts for all pages held by pPager.
*/
SQLITE_PRIVATE int sqlite3PagerRefcount(Pager *pPager){
return sqlite3PcacheRefCount(pPager->pPCache);
}
+#endif
/*
** Return the approximate number of bytes of memory currently
@@ -43609,20 +52884,44 @@ SQLITE_PRIVATE int *sqlite3PagerStats(Pager *pPager){
a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
a[4] = pPager->eState;
a[5] = pPager->errCode;
- a[6] = pPager->nHit;
- a[7] = pPager->nMiss;
+ a[6] = pPager->aStat[PAGER_STAT_HIT];
+ a[7] = pPager->aStat[PAGER_STAT_MISS];
a[8] = 0; /* Used to be pPager->nOvfl */
a[9] = pPager->nRead;
- a[10] = pPager->nWrite;
+ a[10] = pPager->aStat[PAGER_STAT_WRITE];
return a;
}
#endif
/*
-** Return true if this is an in-memory pager.
+** Parameter eStat must be either SQLITE_DBSTATUS_CACHE_HIT or
+** SQLITE_DBSTATUS_CACHE_MISS. Before returning, *pnVal is incremented by the
+** current cache hit or miss count, according to the value of eStat. If the
+** reset parameter is non-zero, the cache hit or miss count is zeroed before
+** returning.
+*/
+SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){
+
+ assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
+ || eStat==SQLITE_DBSTATUS_CACHE_MISS
+ || eStat==SQLITE_DBSTATUS_CACHE_WRITE
+ );
+
+ assert( SQLITE_DBSTATUS_CACHE_HIT+1==SQLITE_DBSTATUS_CACHE_MISS );
+ assert( SQLITE_DBSTATUS_CACHE_HIT+2==SQLITE_DBSTATUS_CACHE_WRITE );
+ assert( PAGER_STAT_HIT==0 && PAGER_STAT_MISS==1 && PAGER_STAT_WRITE==2 );
+
+ *pnVal += pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT];
+ if( reset ){
+ pPager->aStat[eStat - SQLITE_DBSTATUS_CACHE_HIT] = 0;
+ }
+}
+
+/*
+** Return true if this is an in-memory or temp-file backed pager.
*/
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){
- return MEMDB;
+ return pPager->tempFile;
}
/*
@@ -43635,54 +52934,62 @@ SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager *pPager){
** occurs while opening the sub-journal file, then an IO error code is
** returned. Otherwise, SQLITE_OK.
*/
-SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
+static SQLITE_NOINLINE int pagerOpenSavepoint(Pager *pPager, int nSavepoint){
int rc = SQLITE_OK; /* Return code */
int nCurrent = pPager->nSavepoint; /* Current number of savepoints */
+ int ii; /* Iterator variable */
+ PagerSavepoint *aNew; /* New Pager.aSavepoint array */
assert( pPager->eState>=PAGER_WRITER_LOCKED );
assert( assert_pager_state(pPager) );
+ assert( nSavepoint>nCurrent && pPager->useJournal );
- if( nSavepoint>nCurrent && pPager->useJournal ){
- int ii; /* Iterator variable */
- PagerSavepoint *aNew; /* New Pager.aSavepoint array */
+ /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
+ ** if the allocation fails. Otherwise, zero the new portion in case a
+ ** malloc failure occurs while populating it in the for(...) loop below.
+ */
+ aNew = (PagerSavepoint *)sqlite3Realloc(
+ pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
+ );
+ if( !aNew ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
+ pPager->aSavepoint = aNew;
- /* Grow the Pager.aSavepoint array using realloc(). Return SQLITE_NOMEM
- ** if the allocation fails. Otherwise, zero the new portion in case a
- ** malloc failure occurs while populating it in the for(...) loop below.
- */
- aNew = (PagerSavepoint *)sqlite3Realloc(
- pPager->aSavepoint, sizeof(PagerSavepoint)*nSavepoint
- );
- if( !aNew ){
- return SQLITE_NOMEM;
+ /* Populate the PagerSavepoint structures just allocated. */
+ for(ii=nCurrent; ii<nSavepoint; ii++){
+ aNew[ii].nOrig = pPager->dbSize;
+ if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
+ aNew[ii].iOffset = pPager->journalOff;
+ }else{
+ aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
}
- memset(&aNew[nCurrent], 0, (nSavepoint-nCurrent) * sizeof(PagerSavepoint));
- pPager->aSavepoint = aNew;
-
- /* Populate the PagerSavepoint structures just allocated. */
- for(ii=nCurrent; ii<nSavepoint; ii++){
- aNew[ii].nOrig = pPager->dbSize;
- if( isOpen(pPager->jfd) && pPager->journalOff>0 ){
- aNew[ii].iOffset = pPager->journalOff;
- }else{
- aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager);
- }
- aNew[ii].iSubRec = pPager->nSubRec;
- aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
- if( !aNew[ii].pInSavepoint ){
- return SQLITE_NOMEM;
- }
- if( pagerUseWal(pPager) ){
- sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
- }
- pPager->nSavepoint = ii+1;
+ aNew[ii].iSubRec = pPager->nSubRec;
+ aNew[ii].pInSavepoint = sqlite3BitvecCreate(pPager->dbSize);
+ if( !aNew[ii].pInSavepoint ){
+ return SQLITE_NOMEM_BKPT;
}
- assert( pPager->nSavepoint==nSavepoint );
- assertTruncateConstraint(pPager);
+ if( pagerUseWal(pPager) ){
+ sqlite3WalSavepoint(pPager->pWal, aNew[ii].aWalData);
+ }
+ pPager->nSavepoint = ii+1;
}
-
+ assert( pPager->nSavepoint==nSavepoint );
+ assertTruncateConstraint(pPager);
return rc;
}
+SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
+ assert( pPager->eState>=PAGER_WRITER_LOCKED );
+ assert( assert_pager_state(pPager) );
+
+ if( nSavepoint>pPager->nSavepoint && pPager->useJournal ){
+ return pagerOpenSavepoint(pPager, nSavepoint);
+ }else{
+ return SQLITE_OK;
+ }
+}
+
/*
** This function is called to rollback or release (commit) a savepoint.
@@ -43715,7 +53022,11 @@ SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int nSavepoint){
** savepoint. If no errors occur, SQLITE_OK is returned.
*/
SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
- int rc = pPager->errCode; /* Return code */
+ int rc = pPager->errCode;
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+ if( op==SAVEPOINT_RELEASE ) rc = SQLITE_OK;
+#endif
assert( op==SAVEPOINT_RELEASE || op==SAVEPOINT_ROLLBACK );
assert( iSavepoint>=0 || op==SAVEPOINT_ROLLBACK );
@@ -43739,7 +53050,7 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
if( op==SAVEPOINT_RELEASE ){
if( nNew==0 && isOpen(pPager->sjfd) ){
/* Only truncate if it is an in-memory sub-journal. */
- if( sqlite3IsMemJournal(pPager->sjfd) ){
+ if( sqlite3JournalIsInMemory(pPager->sjfd) ){
rc = sqlite3OsTruncate(pPager->sjfd, 0);
assert( rc==SQLITE_OK );
}
@@ -43756,6 +53067,20 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
rc = pagerPlaybackSavepoint(pPager, pSavepoint);
assert(rc!=SQLITE_DONE);
}
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+ /* If the cache has been modified but the savepoint cannot be rolled
+ ** back journal_mode=off, put the pager in the error state. This way,
+ ** if the VFS used by this pager includes ZipVFS, the entire transaction
+ ** can be rolled back at the ZipVFS level. */
+ else if(
+ pPager->journalMode==PAGER_JOURNALMODE_OFF
+ && pPager->eState>=PAGER_WRITER_CACHEMOD
+ ){
+ pPager->errCode = SQLITE_ABORT;
+ pPager->eState = PAGER_ERROR;
+ }
+#endif
}
return rc;
@@ -43763,15 +53088,22 @@ SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
/*
** Return the full pathname of the database file.
+**
+** Except, if the pager is in-memory only, then return an empty string if
+** nullIfMemDb is true. This routine is called with nullIfMemDb==1 when
+** used to report the filename to the user, for compatibility with legacy
+** behavior. But when the Btree needs to know the filename for matching to
+** shared cache, it uses nullIfMemDb==0 so that in-memory databases can
+** participate in shared-cache.
*/
-SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager){
- return pPager->zFilename;
+SQLITE_PRIVATE const char *sqlite3PagerFilename(Pager *pPager, int nullIfMemDb){
+ return (nullIfMemDb && pPager->memDb) ? "" : pPager->zFilename;
}
/*
** Return the VFS structure for the pager.
*/
-SQLITE_PRIVATE const sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){
+SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager *pPager){
return pPager->pVfs;
}
@@ -43785,18 +53117,22 @@ SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
}
/*
-** Return the full pathname of the journal file.
+** Return the file handle for the journal file (if it exists).
+** This will be either the rollback journal or the WAL file.
*/
-SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){
- return pPager->zJournal;
+SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
+#if SQLITE_OMIT_WAL
+ return pPager->jfd;
+#else
+ return pPager->pWal ? sqlite3WalFile(pPager->pWal) : pPager->jfd;
+#endif
}
/*
-** Return true if fsync() calls are disabled for this pager. Return FALSE
-** if fsync()s are executed normally.
+** Return the full pathname of the journal file.
*/
-SQLITE_PRIVATE int sqlite3PagerNosync(Pager *pPager){
- return pPager->noSync;
+SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager *pPager){
+ return pPager->zJournal;
}
#ifdef SQLITE_HAS_CODEC
@@ -43820,7 +53156,27 @@ SQLITE_PRIVATE void sqlite3PagerSetCodec(
SQLITE_PRIVATE void *sqlite3PagerGetCodec(Pager *pPager){
return pPager->pCodec;
}
-#endif
+
+/*
+** This function is called by the wal module when writing page content
+** into the log file.
+**
+** This function returns a pointer to a buffer containing the encrypted
+** page content. If a malloc fails, this function may return NULL.
+*/
+SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
+ void *aData = 0;
+ CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
+ return aData;
+}
+
+/*
+** Return the current pager state
+*/
+SQLITE_PRIVATE int sqlite3PagerState(Pager *pPager){
+ return pPager->eState;
+}
+#endif /* SQLITE_HAS_CODEC */
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
@@ -43863,7 +53219,8 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
/* In order to be able to rollback, an in-memory database must journal
** the page we are moving from.
*/
- if( MEMDB ){
+ assert( pPager->tempFile || !MEMDB );
+ if( pPager->tempFile ){
rc = sqlite3PagerWrite(pPg);
if( rc ) return rc;
}
@@ -43886,9 +53243,8 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** one or more savepoint bitvecs. This is the reason this function
** may return SQLITE_NOMEM.
*/
- if( pPg->flags&PGHDR_DIRTY
- && subjRequiresPage(pPg)
- && SQLITE_OK!=(rc = subjournalPage(pPg))
+ if( (pPg->flags & PGHDR_DIRTY)!=0
+ && SQLITE_OK!=(rc = subjournalPageIfRequired(pPg))
){
return rc;
}
@@ -43906,7 +53262,8 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
*/
if( (pPg->flags&PGHDR_NEED_SYNC) && !isCommit ){
needSyncPgno = pPg->pgno;
- assert( pageInJournal(pPg) || pPg->pgno>pPager->dbOrigSize );
+ assert( pPager->journalMode==PAGER_JOURNALMODE_OFF ||
+ pageInJournal(pPager, pPg) || pPg->pgno>pPager->dbOrigSize );
assert( pPg->flags&PGHDR_DIRTY );
}
@@ -43916,11 +53273,11 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** for the page moved there.
*/
pPg->flags &= ~PGHDR_NEED_SYNC;
- pPgOld = pager_lookup(pPager, pgno);
+ pPgOld = sqlite3PagerLookup(pPager, pgno);
assert( !pPgOld || pPgOld->nRef==1 );
if( pPgOld ){
pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC);
- if( MEMDB ){
+ if( pPager->tempFile ){
/* Do not discard pages from an in-memory database since we might
** need to rollback later. Just move the page out of the way. */
sqlite3PcacheMove(pPgOld, pPager->dbSize+1);
@@ -43937,10 +53294,9 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** to exist, in case the transaction needs to roll back. Use pPgOld
** as the original page since it has already been allocated.
*/
- if( MEMDB ){
- assert( pPgOld );
+ if( pPager->tempFile && pPgOld ){
sqlite3PcacheMove(pPgOld, origPgno);
- sqlite3PagerUnref(pPgOld);
+ sqlite3PagerUnrefNotNull(pPgOld);
}
if( needSyncPgno ){
@@ -43959,7 +53315,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
** the journal file twice, but that is not a problem.
*/
PgHdr *pPgHdr;
- rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr);
+ rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr, 0);
if( rc!=SQLITE_OK ){
if( needSyncPgno<=pPager->dbOrigSize ){
assert( pPager->pTmpSpace!=0 );
@@ -43969,7 +53325,7 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
}
pPgHdr->flags |= PGHDR_NEED_SYNC;
sqlite3PcacheMakeDirty(pPgHdr);
- sqlite3PagerUnref(pPgHdr);
+ sqlite3PagerUnrefNotNull(pPgHdr);
}
return SQLITE_OK;
@@ -43977,6 +53333,18 @@ SQLITE_PRIVATE int sqlite3PagerMovepage(Pager *pPager, DbPage *pPg, Pgno pgno, i
#endif
/*
+** The page handle passed as the first argument refers to a dirty page
+** with a page number other than iNew. This function changes the page's
+** page number to iNew and sets the value of the PgHdr.flags field to
+** the value passed as the third parameter.
+*/
+SQLITE_PRIVATE void sqlite3PagerRekey(DbPage *pPg, Pgno iNew, u16 flags){
+ assert( pPg->pgno!=iNew );
+ pPg->flags = flags;
+ sqlite3PcacheMove(pPg, iNew);
+}
+
+/*
** Return a pointer to the data for the specified page.
*/
SQLITE_PRIVATE void *sqlite3PagerGetData(DbPage *pPg){
@@ -44121,6 +53489,8 @@ SQLITE_PRIVATE int sqlite3PagerSetJournalMode(Pager *pPager, int eMode){
}
assert( state==pPager->eState );
}
+ }else if( eMode==PAGER_JOURNALMODE_OFF ){
+ sqlite3OsClose(pPager->jfd);
}
}
@@ -44171,6 +53541,17 @@ SQLITE_PRIVATE sqlite3_backup **sqlite3PagerBackupPtr(Pager *pPager){
return &pPager->pBackup;
}
+#ifndef SQLITE_OMIT_VACUUM
+/*
+** Unless this is an in-memory or temporary database, clear the pager cache.
+*/
+SQLITE_PRIVATE void sqlite3PagerClearCache(Pager *pPager){
+ assert( MEMDB==0 || pPager->tempFile );
+ if( pPager->tempFile==0 ) pager_reset(pPager);
+}
+#endif
+
+
#ifndef SQLITE_OMIT_WAL
/*
** This function is called when the user invokes "PRAGMA wal_checkpoint",
@@ -44183,7 +53564,8 @@ SQLITE_PRIVATE int sqlite3PagerCheckpoint(Pager *pPager, int eMode, int *pnLog,
int rc = SQLITE_OK;
if( pPager->pWal ){
rc = sqlite3WalCheckpoint(pPager->pWal, eMode,
- pPager->xBusyHandler, pPager->pBusyHandlerArg,
+ (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
+ pPager->pBusyHandlerArg,
pPager->ckptSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
pnLog, pnCkpt
);
@@ -44201,6 +53583,7 @@ SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){
*/
SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager){
const sqlite3_io_methods *pMethods = pPager->fd->pMethods;
+ if( pPager->noLock ) return 0;
return pPager->exclusiveMode || (pMethods->iVersion>=2 && pMethods->xShmMap);
}
@@ -44232,7 +53615,7 @@ static int pagerOpenWal(Pager *pPager){
int rc = SQLITE_OK;
assert( pPager->pWal==0 && pPager->tempFile==0 );
- assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK || pPager->noReadlock);
+ assert( pPager->eLock==SHARED_LOCK || pPager->eLock==EXCLUSIVE_LOCK );
/* If the pager is already in exclusive-mode, the WAL module will use
** heap-memory for the wal-index instead of the VFS shared-memory
@@ -44247,11 +53630,12 @@ static int pagerOpenWal(Pager *pPager){
** (e.g. due to malloc() failure), return an error code.
*/
if( rc==SQLITE_OK ){
- rc = sqlite3WalOpen(pPager->pVfs,
+ rc = sqlite3WalOpen(pPager->pVfs,
pPager->fd, pPager->zWal, pPager->exclusiveMode,
pPager->journalSizeLimit, &pPager->pWal
);
}
+ pagerFixMaplimit(pPager);
return rc;
}
@@ -44342,28 +53726,57 @@ SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager){
rc = sqlite3WalClose(pPager->pWal, pPager->ckptSyncFlags,
pPager->pageSize, (u8*)pPager->pTmpSpace);
pPager->pWal = 0;
+ pagerFixMaplimit(pPager);
+ if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
}
}
return rc;
}
-#ifdef SQLITE_HAS_CODEC
+#ifdef SQLITE_ENABLE_SNAPSHOT
/*
-** This function is called by the wal module when writing page content
-** into the log file.
-**
-** This function returns a pointer to a buffer containing the encrypted
-** page content. If a malloc fails, this function may return NULL.
+** If this is a WAL database, obtain a snapshot handle for the snapshot
+** currently open. Otherwise, return an error.
*/
-SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
- void *aData = 0;
- CODEC2(pPg->pPager, pPg->pData, pPg->pgno, 6, return 0, aData);
- return aData;
+SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot){
+ int rc = SQLITE_ERROR;
+ if( pPager->pWal ){
+ rc = sqlite3WalSnapshotGet(pPager->pWal, ppSnapshot);
+ }
+ return rc;
}
-#endif /* SQLITE_HAS_CODEC */
+/*
+** If this is a WAL database, store a pointer to pSnapshot. Next time a
+** read transaction is opened, attempt to read from the snapshot it
+** identifies. If this is not a WAL database, return an error.
+*/
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
+ int rc = SQLITE_OK;
+ if( pPager->pWal ){
+ sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
+#endif /* SQLITE_ENABLE_SNAPSHOT */
#endif /* !SQLITE_OMIT_WAL */
+#ifdef SQLITE_ENABLE_ZIPVFS
+/*
+** A read-lock must be held on the pager when this function is called. If
+** the pager is in WAL mode and the WAL file currently contains one or more
+** frames, return the size in bytes of the page images stored within the
+** WAL frames. Otherwise, if this is not a WAL database or the WAL file
+** is empty, return 0.
+*/
+SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
+ assert( pPager->eState>=PAGER_READER );
+ return sqlite3WalFramesize(pPager->pWal);
+}
+#endif
+
#endif /* SQLITE_OMIT_DISKIO */
/************** End of pager.c ***********************************************/
@@ -44512,14 +53925,15 @@ SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
** byte order of the host computer.
**
** The purpose of the wal-index is to answer this question quickly: Given
-** a page number P, return the index of the last frame for page P in the WAL,
-** or return NULL if there are no frames for page P in the WAL.
+** a page number P and a maximum frame index M, return the index of the
+** last frame in the wal before frame M for page P in the WAL, or return
+** NULL if there are no frames for page P in the WAL prior to M.
**
** The wal-index consists of a header region, followed by an one or
** more index blocks.
**
** The wal-index header contains the total number of frames within the WAL
-** in the the mxFrame field.
+** in the mxFrame field.
**
** Each index block except for the first contains information on
** HASHTABLE_NPAGE frames. The first index block contains information on
@@ -44611,6 +54025,7 @@ SQLITE_PRIVATE void *sqlite3PagerCodec(PgHdr *pPg){
*/
#ifndef SQLITE_OMIT_WAL
+/* #include "wal.h" */
/*
** Trace output macros
@@ -44640,7 +54055,8 @@ SQLITE_PRIVATE int sqlite3WalTrace = 0;
/*
** Indices of various locking bytes. WAL_NREADER is the number
-** of available reader locks and should be at least 3.
+** of available reader locks and should be at least 3. The default
+** is SQLITE_SHM_NLOCK==8 and WAL_NREADER==5.
*/
#define WAL_WRITE_LOCK 0
#define WAL_ALL_BUT_WRITE 1
@@ -44660,7 +54076,10 @@ typedef struct WalCkptInfo WalCkptInfo;
** The following object holds a copy of the wal-index header content.
**
** The actual header in the wal-index consists of two copies of this
-** object.
+** object followed by one instance of the WalCkptInfo object.
+** For all versions of SQLite through 3.10.0 and probably beyond,
+** the locking bytes (WalCkptInfo.aLock) start at offset 120 and
+** the total header size is 136 bytes.
**
** The szPage value can be any power of 2 between 512 and 32768, inclusive.
** Or it can be 1 to represent a 65536-byte page. The latter case was
@@ -44693,6 +54112,16 @@ struct WalIndexHdr {
** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from
** mxFrame back to zero when the WAL is reset.
**
+** nBackfillAttempted is the largest value of nBackfill that a checkpoint
+** has attempted to achieve. Normally nBackfill==nBackfillAtempted, however
+** the nBackfillAttempted is set before any backfilling is done and the
+** nBackfill is only set after all backfilling completes. So if a checkpoint
+** crashes, nBackfillAttempted might be larger than nBackfill. The
+** WalIndexHdr.mxFrame must never be less than nBackfillAttempted.
+**
+** The aLock[] field is a set of bytes used for locking. These bytes should
+** never be read or written.
+**
** There is one entry in aReadMark[] for each reader lock. If a reader
** holds read-lock K, then the value in aReadMark[K] is no greater than
** the mxFrame for that reader. The value READMARK_NOT_USED (0xffffffff)
@@ -44732,6 +54161,9 @@ struct WalIndexHdr {
struct WalCkptInfo {
u32 nBackfill; /* Number of WAL frames backfilled into DB */
u32 aReadMark[WAL_NREADER]; /* Reader marks */
+ u8 aLock[SQLITE_SHM_NLOCK]; /* Reserved space for locks */
+ u32 nBackfillAttempted; /* WAL frames perhaps written, or maybe not */
+ u32 notUsed0; /* Available for future enhancements */
};
#define READMARK_NOT_USED 0xffffffff
@@ -44741,9 +54173,8 @@ struct WalCkptInfo {
** only support mandatory file-locks, we do not read or write data
** from the region of the file on which locks are applied.
*/
-#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo))
-#define WALINDEX_LOCK_RESERVED 16
-#define WALINDEX_HDR_SIZE (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)
+#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2+offsetof(WalCkptInfo,aLock))
+#define WALINDEX_HDR_SIZE (sizeof(WalIndexHdr)*2+sizeof(WalCkptInfo))
/* Size of header before each frame in wal */
#define WAL_FRAME_HDRSIZE 24
@@ -44783,19 +54214,29 @@ struct Wal {
u32 iCallback; /* Value to pass to log callback (or 0) */
i64 mxWalSize; /* Truncate WAL to this size upon reset */
int nWiData; /* Size of array apWiData */
+ int szFirstBlock; /* Size of first block written to WAL file */
volatile u32 **apWiData; /* Pointer to wal-index content in memory */
u32 szPage; /* Database page size */
i16 readLock; /* Which read lock is being held. -1 for none */
+ u8 syncFlags; /* Flags to use to sync header writes */
u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */
u8 writeLock; /* True if in a write transaction */
u8 ckptLock; /* True if holding a checkpoint lock */
u8 readOnly; /* WAL_RDWR, WAL_RDONLY, or WAL_SHM_RDONLY */
+ u8 truncateOnCommit; /* True to truncate WAL file on commit */
+ u8 syncHeader; /* Fsync the WAL header if true */
+ u8 padToSectorBoundary; /* Pad transactions out to the next sector */
WalIndexHdr hdr; /* Wal-index header for current transaction */
+ u32 minFrame; /* Ignore wal frames before this one */
+ u32 iReCksum; /* On commit, recalculate checksums from here */
const char *zWalName; /* Name of WAL file */
u32 nCkpt; /* Checkpoint sequence counter in the wal-header */
#ifdef SQLITE_DEBUG
u8 lockError; /* True if a locking error has occurred */
#endif
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
+#endif
};
/*
@@ -44885,10 +54326,10 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
if( pWal->nWiData<=iPage ){
int nByte = sizeof(u32*)*(iPage+1);
volatile u32 **apNew;
- apNew = (volatile u32 **)sqlite3_realloc((void *)pWal->apWiData, nByte);
+ apNew = (volatile u32 **)sqlite3_realloc64((void *)pWal->apWiData, nByte);
if( !apNew ){
*ppPage = 0;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset((void*)&apNew[pWal->nWiData], 0,
sizeof(u32*)*(iPage+1-pWal->nWiData));
@@ -44900,7 +54341,7 @@ static int walIndexPage(Wal *pWal, int iPage, volatile u32 **ppPage){
if( pWal->apWiData[iPage]==0 ){
if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
pWal->apWiData[iPage] = (u32 volatile *)sqlite3MallocZero(WALINDEX_PGSZ);
- if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM;
+ if( !pWal->apWiData[iPage] ) rc = SQLITE_NOMEM_BKPT;
}else{
rc = sqlite3OsShmMap(pWal->pDbFd, iPage, WALINDEX_PGSZ,
pWal->writeLock, (void volatile **)&pWal->apWiData[iPage]
@@ -44937,7 +54378,7 @@ static volatile WalIndexHdr *walIndexHdr(Wal *pWal){
** The argument to this macro must be of type u32. On a little-endian
** architecture, it returns the u32 value that results from interpreting
** the 4 bytes as a big-endian value. On a big-endian architecture, it
-** returns the value that would be produced by intepreting the 4 bytes
+** returns the value that would be produced by interpreting the 4 bytes
** of the input value as a little-endian integer.
*/
#define BYTESWAP32(x) ( \
@@ -45011,9 +54452,9 @@ static void walIndexWriteHdr(Wal *pWal){
pWal->hdr.isInit = 1;
pWal->hdr.iVersion = WALINDEX_MAX_VERSION;
walChecksumBytes(1, (u8*)&pWal->hdr, nCksum, 0, pWal->hdr.aCksum);
- memcpy((void *)&aHdr[1], (void *)&pWal->hdr, sizeof(WalIndexHdr));
+ memcpy((void*)&aHdr[1], (const void*)&pWal->hdr, sizeof(WalIndexHdr));
walShmBarrier(pWal);
- memcpy((void *)&aHdr[0], (void *)&pWal->hdr, sizeof(WalIndexHdr));
+ memcpy((void*)&aHdr[0], (const void*)&pWal->hdr, sizeof(WalIndexHdr));
}
/*
@@ -45041,14 +54482,18 @@ static void walEncodeFrame(
assert( WAL_FRAME_HDRSIZE==24 );
sqlite3Put4byte(&aFrame[0], iPage);
sqlite3Put4byte(&aFrame[4], nTruncate);
- memcpy(&aFrame[8], pWal->hdr.aSalt, 8);
+ if( pWal->iReCksum==0 ){
+ memcpy(&aFrame[8], pWal->hdr.aSalt, 8);
- nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
- walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
- walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
+ nativeCksum = (pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN);
+ walChecksumBytes(nativeCksum, aFrame, 8, aCksum, aCksum);
+ walChecksumBytes(nativeCksum, aData, pWal->szPage, aCksum, aCksum);
- sqlite3Put4byte(&aFrame[16], aCksum[0]);
- sqlite3Put4byte(&aFrame[20], aCksum[1]);
+ sqlite3Put4byte(&aFrame[16], aCksum[0]);
+ sqlite3Put4byte(&aFrame[20], aCksum[1]);
+ }else{
+ memset(&aFrame[8], 0, 16);
+ }
}
/*
@@ -45314,13 +54759,13 @@ static void walCleanupHash(Wal *pWal){
** via the hash table even after the cleanup.
*/
if( iLimit ){
- int i; /* Loop counter */
+ int j; /* Loop counter */
int iKey; /* Hash key */
- for(i=1; i<=iLimit; i++){
- for(iKey=walHash(aPgno[i]); aHash[iKey]; iKey=walNextHash(iKey)){
- if( aHash[iKey]==i ) break;
+ for(j=1; j<=iLimit; j++){
+ for(iKey=walHash(aPgno[j]); aHash[iKey]; iKey=walNextHash(iKey)){
+ if( aHash[iKey]==j ) break;
}
- assert( aHash[iKey]==i );
+ assert( aHash[iKey]==j );
}
}
#endif /* SQLITE_ENABLE_EXPENSIVE_ASSERT */
@@ -45351,7 +54796,7 @@ static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
assert( idx <= HASHTABLE_NSLOT/2 + 1 );
/* If this is the first entry to be added to this hash-table, zero the
- ** entire hash table and aPgno[] array before proceding.
+ ** entire hash table and aPgno[] array before proceeding.
*/
if( idx==1 ){
int nByte = (int)((u8 *)&aHash[HASHTABLE_NSLOT] - (u8 *)&aPgno[1]);
@@ -45462,6 +54907,7 @@ static int walIndexRecover(Wal *pWal){
int szPage; /* Page size according to the log */
u32 magic; /* Magic value read from WAL header */
u32 version; /* Magic value read from WAL header */
+ int isValid; /* True if this frame is valid */
/* Read in the WAL header. */
rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
@@ -45508,9 +54954,9 @@ static int walIndexRecover(Wal *pWal){
/* Malloc a buffer to read frames into. */
szFrame = szPage + WAL_FRAME_HDRSIZE;
- aFrame = (u8 *)sqlite3_malloc(szFrame);
+ aFrame = (u8 *)sqlite3_malloc64(szFrame);
if( !aFrame ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto recovery_error;
}
aData = &aFrame[WAL_FRAME_HDRSIZE];
@@ -45520,14 +54966,14 @@ static int walIndexRecover(Wal *pWal){
for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
u32 pgno; /* Database page number for frame */
u32 nTruncate; /* dbsize field from frame header */
- int isValid; /* True if this frame is valid */
/* Read and decode the next log frame. */
+ iFrame++;
rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
if( rc!=SQLITE_OK ) break;
isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
if( !isValid ) break;
- rc = walIndexAppend(pWal, ++iFrame, pgno);
+ rc = walIndexAppend(pWal, iFrame, pgno);
if( rc!=SQLITE_OK ) break;
/* If nTruncate is non-zero, this is a commit record. */
@@ -45559,8 +55005,10 @@ finished:
*/
pInfo = walCkptInfo(pWal);
pInfo->nBackfill = 0;
+ pInfo->nBackfillAttempted = pWal->hdr.mxFrame;
pInfo->aReadMark[0] = 0;
for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+ if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
/* If more than one frame was recovered from the log file, report an
** event via sqlite3_log(). This is to help with identifying performance
@@ -45568,8 +55016,9 @@ finished:
** checkpointing the log file.
*/
if( pWal->hdr.nPage ){
- sqlite3_log(SQLITE_OK, "Recovered %d frames from WAL file %s",
- pWal->hdr.nPage, pWal->zWalName
+ sqlite3_log(SQLITE_NOTICE_RECOVER_WAL,
+ "recovered %d frames from WAL file %s",
+ pWal->hdr.mxFrame, pWal->zWalName
);
}
}
@@ -45628,7 +55077,11 @@ SQLITE_PRIVATE int sqlite3WalOpen(
/* In the amalgamation, the os_unix.c and os_win.c source files come before
** this source file. Verify that the #defines of the locking byte offsets
** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value.
+ ** For that matter, if the lock offset ever changes from its initial design
+ ** value of 120, we need to know that so there is an assert() to check it.
*/
+ assert( 120==WALINDEX_LOCK_OFFSET );
+ assert( 136==WALINDEX_HDR_SIZE );
#ifdef WIN_SHM_BASE
assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET );
#endif
@@ -45641,7 +55094,7 @@ SQLITE_PRIVATE int sqlite3WalOpen(
*ppWal = 0;
pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile);
if( !pRet ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pRet->pVfs = pVfs;
@@ -45650,6 +55103,8 @@ SQLITE_PRIVATE int sqlite3WalOpen(
pRet->readLock = -1;
pRet->mxWalSize = mxWalSize;
pRet->zWalName = zWalName;
+ pRet->syncHeader = 1;
+ pRet->padToSectorBoundary = 1;
pRet->exclusiveMode = (bNoShm ? WAL_HEAPMEMORY_MODE: WAL_NORMAL_MODE);
/* Open file handle on the write-ahead log file. */
@@ -45664,6 +55119,11 @@ SQLITE_PRIVATE int sqlite3WalOpen(
sqlite3OsClose(pRet->pWalFd);
sqlite3_free(pRet);
}else{
+ int iDC = sqlite3OsDeviceCharacteristics(pDbFd);
+ if( iDC & SQLITE_IOCAP_SEQUENTIAL ){ pRet->syncHeader = 0; }
+ if( iDC & SQLITE_IOCAP_POWERSAFE_OVERWRITE ){
+ pRet->padToSectorBoundary = 0;
+ }
*ppWal = pRet;
WALTRACE(("WAL%d: opened\n", pRet));
}
@@ -45812,7 +55272,7 @@ static void walMergesort(
int nMerge = 0; /* Number of elements in list aMerge */
ht_slot *aMerge = 0; /* List to be merged */
int iList; /* Index into input list */
- int iSub = 0; /* Index into aSub array */
+ u32 iSub = 0; /* Index into aSub array */
struct Sublist aSub[13]; /* Array of sub-lists */
memset(aSub, 0, sizeof(aSub));
@@ -45823,7 +55283,9 @@ static void walMergesort(
nMerge = 1;
aMerge = &aList[iList];
for(iSub=0; iList & (1<<iSub); iSub++){
- struct Sublist *p = &aSub[iSub];
+ struct Sublist *p;
+ assert( iSub<ArraySize(aSub) );
+ p = &aSub[iSub];
assert( p->aList && p->nList<=(1<<iSub) );
assert( p->aList==&aList[iList&~((2<<iSub)-1)] );
walMerge(aContent, p->aList, p->nList, &aMerge, &nMerge, aBuffer);
@@ -45834,7 +55296,9 @@ static void walMergesort(
for(iSub++; iSub<ArraySize(aSub); iSub++){
if( nList & (1<<iSub) ){
- struct Sublist *p = &aSub[iSub];
+ struct Sublist *p;
+ assert( iSub<ArraySize(aSub) );
+ p = &aSub[iSub];
assert( p->nList<=(1<<iSub) );
assert( p->aList==&aList[nList&~((2<<iSub)-1)] );
walMerge(aContent, p->aList, p->nList, &aMerge, &nMerge, aBuffer);
@@ -45857,7 +55321,7 @@ static void walMergesort(
** Free an iterator allocated by walIteratorInit().
*/
static void walIteratorFree(WalIterator *p){
- sqlite3ScratchFree(p);
+ sqlite3_free(p);
}
/*
@@ -45892,9 +55356,9 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
nByte = sizeof(WalIterator)
+ (nSegment-1)*sizeof(struct WalSegment)
+ iLast*sizeof(ht_slot);
- p = (WalIterator *)sqlite3ScratchMalloc(nByte);
+ p = (WalIterator *)sqlite3_malloc64(nByte);
if( !p ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(p, 0, nByte);
p->nSegment = nSegment;
@@ -45902,11 +55366,11 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
/* Allocate temporary space used by the merge-sort routine. This block
** of memory will be freed before this function returns.
*/
- aTmp = (ht_slot *)sqlite3ScratchMalloc(
+ aTmp = (ht_slot *)sqlite3_malloc64(
sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
);
if( !aTmp ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
for(i=0; rc==SQLITE_OK && i<nSegment; i++){
@@ -45939,7 +55403,7 @@ static int walIteratorInit(Wal *pWal, WalIterator **pp){
p->aSegment[i].aPgno = (u32 *)aPgno;
}
}
- sqlite3ScratchFree(aTmp);
+ sqlite3_free(aTmp);
if( rc!=SQLITE_OK ){
walIteratorFree(p);
@@ -45977,6 +55441,39 @@ static int walPagesize(Wal *pWal){
}
/*
+** The following is guaranteed when this function is called:
+**
+** a) the WRITER lock is held,
+** b) the entire log file has been checkpointed, and
+** c) any existing readers are reading exclusively from the database
+** file - there are no readers that may attempt to read a frame from
+** the log file.
+**
+** This function updates the shared-memory structures so that the next
+** client to write to the database (which may be this one) does so by
+** writing frames into the start of the log file.
+**
+** The value of parameter salt1 is used as the aSalt[1] value in the
+** new wal-index header. It should be passed a pseudo-random value (i.e.
+** one obtained from sqlite3_randomness()).
+*/
+static void walRestartHdr(Wal *pWal, u32 salt1){
+ volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
+ int i; /* Loop counter */
+ u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
+ pWal->nCkpt++;
+ pWal->hdr.mxFrame = 0;
+ sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
+ memcpy(&pWal->hdr.aSalt[1], &salt1, 4);
+ walIndexWriteHdr(pWal);
+ pInfo->nBackfill = 0;
+ pInfo->nBackfillAttempted = 0;
+ pInfo->aReadMark[1] = 0;
+ for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
+ assert( pInfo->aReadMark[0]==0 );
+}
+
+/*
** Copy as much content as we can from the WAL back into the database file
** in response to an sqlite3_wal_checkpoint() request or the equivalent.
**
@@ -45999,7 +55496,7 @@ static int walPagesize(Wal *pWal){
** database file.
**
** This routine uses and updates the nBackfill field of the wal-index header.
-** This is the only routine tha will increase the value of nBackfill.
+** This is the only routine that will increase the value of nBackfill.
** (A WAL reset or recovery will revert nBackfill to zero, but not increase
** its value.)
**
@@ -46010,12 +55507,12 @@ static int walPagesize(Wal *pWal){
static int walCheckpoint(
Wal *pWal, /* Wal connection */
int eMode, /* One of PASSIVE, FULL or RESTART */
- int (*xBusyCall)(void*), /* Function to call when busy */
+ int (*xBusy)(void*), /* Function to call when busy */
void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags for OsSync() (or 0) */
u8 *zBuf /* Temporary buffer to use */
){
- int rc; /* Return code */
+ int rc = SQLITE_OK; /* Return code */
int szPage; /* Database page-size */
WalIterator *pIter = 0; /* Wal iterator context */
u32 iDbpage = 0; /* Next database page to write */
@@ -46024,122 +55521,156 @@ static int walCheckpoint(
u32 mxPage; /* Max database page to write */
int i; /* Loop counter */
volatile WalCkptInfo *pInfo; /* The checkpoint status information */
- int (*xBusy)(void*) = 0; /* Function to call when waiting for locks */
szPage = walPagesize(pWal);
testcase( szPage<=32768 );
testcase( szPage>=65536 );
pInfo = walCkptInfo(pWal);
- if( pInfo->nBackfill>=pWal->hdr.mxFrame ) return SQLITE_OK;
+ if( pInfo->nBackfill<pWal->hdr.mxFrame ){
- /* Allocate the iterator */
- rc = walIteratorInit(pWal, &pIter);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- assert( pIter );
+ /* Allocate the iterator */
+ rc = walIteratorInit(pWal, &pIter);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ assert( pIter );
- if( eMode!=SQLITE_CHECKPOINT_PASSIVE ) xBusy = xBusyCall;
+ /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
+ ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
+ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
- /* Compute in mxSafeFrame the index of the last frame of the WAL that is
- ** safe to write into the database. Frames beyond mxSafeFrame might
- ** overwrite database pages that are in use by active readers and thus
- ** cannot be backfilled from the WAL.
- */
- mxSafeFrame = pWal->hdr.mxFrame;
- mxPage = pWal->hdr.nPage;
- for(i=1; i<WAL_NREADER; i++){
- u32 y = pInfo->aReadMark[i];
- if( mxSafeFrame>y ){
- assert( y<=pWal->hdr.mxFrame );
- rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
- if( rc==SQLITE_OK ){
- pInfo->aReadMark[i] = READMARK_NOT_USED;
- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
- }else if( rc==SQLITE_BUSY ){
- mxSafeFrame = y;
- xBusy = 0;
- }else{
- goto walcheckpoint_out;
+ /* Compute in mxSafeFrame the index of the last frame of the WAL that is
+ ** safe to write into the database. Frames beyond mxSafeFrame might
+ ** overwrite database pages that are in use by active readers and thus
+ ** cannot be backfilled from the WAL.
+ */
+ mxSafeFrame = pWal->hdr.mxFrame;
+ mxPage = pWal->hdr.nPage;
+ for(i=1; i<WAL_NREADER; i++){
+ /* Thread-sanitizer reports that the following is an unsafe read,
+ ** as some other thread may be in the process of updating the value
+ ** of the aReadMark[] slot. The assumption here is that if that is
+ ** happening, the other client may only be increasing the value,
+ ** not decreasing it. So assuming either that either the "old" or
+ ** "new" version of the value is read, and not some arbitrary value
+ ** that would never be written by a real client, things are still
+ ** safe. */
+ u32 y = pInfo->aReadMark[i];
+ if( mxSafeFrame>y ){
+ assert( y<=pWal->hdr.mxFrame );
+ rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
+ if( rc==SQLITE_OK ){
+ pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ }else if( rc==SQLITE_BUSY ){
+ mxSafeFrame = y;
+ xBusy = 0;
+ }else{
+ goto walcheckpoint_out;
+ }
}
}
- }
- if( pInfo->nBackfill<mxSafeFrame
- && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0), 1))==SQLITE_OK
- ){
- i64 nSize; /* Current size of database file */
- u32 nBackfill = pInfo->nBackfill;
+ if( pInfo->nBackfill<mxSafeFrame
+ && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK
+ ){
+ i64 nSize; /* Current size of database file */
+ u32 nBackfill = pInfo->nBackfill;
- /* Sync the WAL to disk */
- if( sync_flags ){
- rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
- }
+ pInfo->nBackfillAttempted = mxSafeFrame;
- /* If the database file may grow as a result of this checkpoint, hint
- ** about the eventual size of the db file to the VFS layer.
- */
- if( rc==SQLITE_OK ){
- i64 nReq = ((i64)mxPage * szPage);
- rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
- if( rc==SQLITE_OK && nSize<nReq ){
- sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
+ /* Sync the WAL to disk */
+ if( sync_flags ){
+ rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
}
- }
- /* Iterate through the contents of the WAL, copying data to the db file. */
- while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
- i64 iOffset;
- assert( walFramePgno(pWal, iFrame)==iDbpage );
- if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ) continue;
- iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
- rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
- if( rc!=SQLITE_OK ) break;
- iOffset = (iDbpage-1)*(i64)szPage;
- testcase( IS_BIG_INT(iOffset) );
- rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
- if( rc!=SQLITE_OK ) break;
- }
+ /* If the database may grow as a result of this checkpoint, hint
+ ** about the eventual size of the db file to the VFS layer.
+ */
+ if( rc==SQLITE_OK ){
+ i64 nReq = ((i64)mxPage * szPage);
+ rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
+ if( rc==SQLITE_OK && nSize<nReq ){
+ sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
+ }
+ }
- /* If work was actually accomplished... */
- if( rc==SQLITE_OK ){
- if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
- i64 szDb = pWal->hdr.nPage*(i64)szPage;
- testcase( IS_BIG_INT(szDb) );
- rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
- if( rc==SQLITE_OK && sync_flags ){
- rc = sqlite3OsSync(pWal->pDbFd, sync_flags);
+
+ /* Iterate through the contents of the WAL, copying data to the db file */
+ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
+ i64 iOffset;
+ assert( walFramePgno(pWal, iFrame)==iDbpage );
+ if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
+ continue;
}
+ iOffset = walFrameOffset(iFrame, szPage) + WAL_FRAME_HDRSIZE;
+ /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL file */
+ rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
+ if( rc!=SQLITE_OK ) break;
+ iOffset = (iDbpage-1)*(i64)szPage;
+ testcase( IS_BIG_INT(iOffset) );
+ rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
+ if( rc!=SQLITE_OK ) break;
}
+
+ /* If work was actually accomplished... */
if( rc==SQLITE_OK ){
- pInfo->nBackfill = mxSafeFrame;
+ if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
+ i64 szDb = pWal->hdr.nPage*(i64)szPage;
+ testcase( IS_BIG_INT(szDb) );
+ rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
+ if( rc==SQLITE_OK && sync_flags ){
+ rc = sqlite3OsSync(pWal->pDbFd, sync_flags);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ pInfo->nBackfill = mxSafeFrame;
+ }
}
- }
- /* Release the reader lock held while backfilling */
- walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
- }
+ /* Release the reader lock held while backfilling */
+ walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
+ }
- if( rc==SQLITE_BUSY ){
- /* Reset the return code so as not to report a checkpoint failure
- ** just because there are active readers. */
- rc = SQLITE_OK;
+ if( rc==SQLITE_BUSY ){
+ /* Reset the return code so as not to report a checkpoint failure
+ ** just because there are active readers. */
+ rc = SQLITE_OK;
+ }
}
- /* If this is an SQLITE_CHECKPOINT_RESTART operation, and the entire wal
- ** file has been copied into the database file, then block until all
- ** readers have finished using the wal file. This ensures that the next
- ** process to write to the database restarts the wal file.
+ /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
+ ** entire wal file has been copied into the database file, then block
+ ** until all readers have finished using the wal file. This ensures that
+ ** the next process to write to the database restarts the wal file.
*/
if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
assert( pWal->writeLock );
if( pInfo->nBackfill<pWal->hdr.mxFrame ){
rc = SQLITE_BUSY;
- }else if( eMode==SQLITE_CHECKPOINT_RESTART ){
- assert( mxSafeFrame==pWal->hdr.mxFrame );
+ }else if( eMode>=SQLITE_CHECKPOINT_RESTART ){
+ u32 salt1;
+ sqlite3_randomness(4, &salt1);
+ assert( pInfo->nBackfill==pWal->hdr.mxFrame );
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(1), WAL_NREADER-1);
if( rc==SQLITE_OK ){
+ if( eMode==SQLITE_CHECKPOINT_TRUNCATE ){
+ /* IMPLEMENTATION-OF: R-44699-57140 This mode works the same way as
+ ** SQLITE_CHECKPOINT_RESTART with the addition that it also
+ ** truncates the log file to zero bytes just prior to a
+ ** successful return.
+ **
+ ** In theory, it might be safe to do this without updating the
+ ** wal-index header in shared memory, as all subsequent reader or
+ ** writer clients should see that the entire log file has been
+ ** checkpointed and behave accordingly. This seems unsafe though,
+ ** as it would leave the system in a state where the contents of
+ ** the wal-index header do not match the contents of the
+ ** file-system. To avoid this, update the wal-index header to
+ ** indicate that the log file contains zero valid frames. */
+ walRestartHdr(pWal, salt1);
+ rc = sqlite3OsTruncate(pWal->pWalFd, 0);
+ }
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
}
}
@@ -46151,6 +55682,24 @@ static int walCheckpoint(
}
/*
+** If the WAL file is currently larger than nMax bytes in size, truncate
+** it to exactly nMax bytes. If an error occurs while doing so, ignore it.
+*/
+static void walLimitSize(Wal *pWal, i64 nMax){
+ i64 sz;
+ int rx;
+ sqlite3BeginBenignMalloc();
+ rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
+ if( rx==SQLITE_OK && (sz > nMax ) ){
+ rx = sqlite3OsTruncate(pWal->pWalFd, nMax);
+ }
+ sqlite3EndBenignMalloc();
+ if( rx ){
+ sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
+ }
+}
+
+/*
** Close a connection to a log file.
*/
SQLITE_PRIVATE int sqlite3WalClose(
@@ -46173,23 +55722,40 @@ SQLITE_PRIVATE int sqlite3WalClose(
*/
rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE);
if( rc==SQLITE_OK ){
- int bPersistWal = -1;
if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
}
rc = sqlite3WalCheckpoint(
pWal, SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
);
- sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersistWal);
- if( rc==SQLITE_OK && bPersistWal!=1 ){
- isDelete = 1;
+ if( rc==SQLITE_OK ){
+ int bPersist = -1;
+ sqlite3OsFileControlHint(
+ pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist
+ );
+ if( bPersist!=1 ){
+ /* Try to delete the WAL file if the checkpoint completed and
+ ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal
+ ** mode (!bPersist) */
+ isDelete = 1;
+ }else if( pWal->mxWalSize>=0 ){
+ /* Try to truncate the WAL file to zero bytes if the checkpoint
+ ** completed and fsynced (rc==SQLITE_OK) and we are in persistent
+ ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a
+ ** non-negative value (pWal->mxWalSize>=0). Note that we truncate
+ ** to zero bytes as truncating to the journal_size_limit might
+ ** leave a corrupt WAL file on disk. */
+ walLimitSize(pWal, 0);
+ }
}
}
walIndexClose(pWal, isDelete);
sqlite3OsClose(pWal->pWalFd);
if( isDelete ){
+ sqlite3BeginBenignMalloc();
sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0);
+ sqlite3EndBenignMalloc();
}
WALTRACE(("WAL%p: closed\n", pWal));
sqlite3_free((void *)pWal->apWiData);
@@ -46267,7 +55833,7 @@ static int walIndexTryHdr(Wal *pWal, int *pChanged){
** wal-index from the WAL before returning.
**
** Set *pChanged to 1 if the wal-index header value in pWal->hdr is
-** changed by this opertion. If pWal->hdr is unchanged, set *pChanged
+** changed by this operation. If pWal->hdr is unchanged, set *pChanged
** to 0.
**
** If the wal-index header is successfully read, return SQLITE_OK.
@@ -46396,6 +55962,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
+ u32 mxFrame; /* Wal frame to lock to */
assert( pWal->readLock<0 ); /* Not currently locked */
@@ -46413,8 +55980,8 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** calls to sqlite3OsSleep() have a delay of 1 microsecond. Really this
** is more of a scheduler yield than an actual delay. But on the 10th
** an subsequent retries, the delays start becoming longer and longer,
- ** so that on the 100th (and last) RETRY we delay for 21 milliseconds.
- ** The total delay time before giving up is less than 1 second.
+ ** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
+ ** The total delay time before giving up is less than 10 seconds.
*/
if( cnt>5 ){
int nDelay = 1; /* Pause time in microseconds */
@@ -46422,7 +55989,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
VVA_ONLY( pWal->lockError = 1; )
return SQLITE_PROTOCOL;
}
- if( cnt>=10 ) nDelay = (cnt-9)*238; /* Max delay 21ms. Total delay 996ms */
+ if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
sqlite3OsSleep(pWal->pVfs, nDelay);
}
@@ -46459,7 +56026,12 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
pInfo = walCkptInfo(pWal);
- if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame ){
+ if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0
+ || 0==memcmp(&pWal->hdr, pWal->pSnapshot, sizeof(WalIndexHdr)))
+#endif
+ ){
/* The WAL has been completely backfilled (or it is empty).
** and can be safely ignored.
*/
@@ -46471,7 +56043,7 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
** may have been appended to the log before READ_LOCK(0) was obtained.
** When holding READ_LOCK(0), the reader ignores the entire log file,
** which implies that the database file contains a trustworthy
- ** snapshoT. Since holding READ_LOCK(0) prevents a checkpoint from
+ ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
** happening, this is usually correct.
**
** However, if frames have been appended to the log (or if the log
@@ -46497,70 +56069,88 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
*/
mxReadMark = 0;
mxI = 0;
+ mxFrame = pWal->hdr.mxFrame;
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
+ mxFrame = pWal->pSnapshot->mxFrame;
+ }
+#endif
for(i=1; i<WAL_NREADER; i++){
u32 thisMark = pInfo->aReadMark[i];
- if( mxReadMark<=thisMark && thisMark<=pWal->hdr.mxFrame ){
+ if( mxReadMark<=thisMark && thisMark<=mxFrame ){
assert( thisMark!=READMARK_NOT_USED );
mxReadMark = thisMark;
mxI = i;
}
}
- /* There was once an "if" here. The extra "{" is to preserve indentation. */
- {
- if( (pWal->readOnly & WAL_SHM_RDONLY)==0
- && (mxReadMark<pWal->hdr.mxFrame || mxI==0)
- ){
- for(i=1; i<WAL_NREADER; i++){
- rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
- if( rc==SQLITE_OK ){
- mxReadMark = pInfo->aReadMark[i] = pWal->hdr.mxFrame;
- mxI = i;
- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
- break;
- }else if( rc!=SQLITE_BUSY ){
- return rc;
- }
+ if( (pWal->readOnly & WAL_SHM_RDONLY)==0
+ && (mxReadMark<mxFrame || mxI==0)
+ ){
+ for(i=1; i<WAL_NREADER; i++){
+ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ if( rc==SQLITE_OK ){
+ mxReadMark = pInfo->aReadMark[i] = mxFrame;
+ mxI = i;
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ break;
+ }else if( rc!=SQLITE_BUSY ){
+ return rc;
}
}
- if( mxI==0 ){
- assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
- return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
- }
+ }
+ if( mxI==0 ){
+ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
+ }
- rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
- if( rc ){
- return rc==SQLITE_BUSY ? WAL_RETRY : rc;
- }
- /* Now that the read-lock has been obtained, check that neither the
- ** value in the aReadMark[] array or the contents of the wal-index
- ** header have changed.
- **
- ** It is necessary to check that the wal-index header did not change
- ** between the time it was read and when the shared-lock was obtained
- ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
- ** that the log file may have been wrapped by a writer, or that frames
- ** that occur later in the log than pWal->hdr.mxFrame may have been
- ** copied into the database by a checkpointer. If either of these things
- ** happened, then reading the database with the current value of
- ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
- ** instead.
- **
- ** This does not guarantee that the copy of the wal-index header is up to
- ** date before proceeding. That would not be possible without somehow
- ** blocking writers. It only guarantees that a dangerous checkpoint or
- ** log-wrap (either of which would require an exclusive lock on
- ** WAL_READ_LOCK(mxI)) has not occurred since the snapshot was valid.
- */
- walShmBarrier(pWal);
- if( pInfo->aReadMark[mxI]!=mxReadMark
- || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
- ){
- walUnlockShared(pWal, WAL_READ_LOCK(mxI));
- return WAL_RETRY;
- }else{
- assert( mxReadMark<=pWal->hdr.mxFrame );
- pWal->readLock = (i16)mxI;
- }
+ rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ if( rc ){
+ return rc==SQLITE_BUSY ? WAL_RETRY : rc;
+ }
+ /* Now that the read-lock has been obtained, check that neither the
+ ** value in the aReadMark[] array or the contents of the wal-index
+ ** header have changed.
+ **
+ ** It is necessary to check that the wal-index header did not change
+ ** between the time it was read and when the shared-lock was obtained
+ ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
+ ** that the log file may have been wrapped by a writer, or that frames
+ ** that occur later in the log than pWal->hdr.mxFrame may have been
+ ** copied into the database by a checkpointer. If either of these things
+ ** happened, then reading the database with the current value of
+ ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
+ ** instead.
+ **
+ ** Before checking that the live wal-index header has not changed
+ ** since it was read, set Wal.minFrame to the first frame in the wal
+ ** file that has not yet been checkpointed. This client will not need
+ ** to read any frames earlier than minFrame from the wal file - they
+ ** can be safely read directly from the database file.
+ **
+ ** Because a ShmBarrier() call is made between taking the copy of
+ ** nBackfill and checking that the wal-header in shared-memory still
+ ** matches the one cached in pWal->hdr, it is guaranteed that the
+ ** checkpointer that set nBackfill was not working with a wal-index
+ ** header newer than that cached in pWal->hdr. If it were, that could
+ ** cause a problem. The checkpointer could omit to checkpoint
+ ** a version of page X that lies before pWal->minFrame (call that version
+ ** A) on the basis that there is a newer version (version B) of the same
+ ** page later in the wal file. But if version B happens to like past
+ ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
+ ** that it can read version A from the database file. However, since
+ ** we can guarantee that the checkpointer that set nBackfill could not
+ ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
+ */
+ pWal->minFrame = pInfo->nBackfill+1;
+ walShmBarrier(pWal);
+ if( pInfo->aReadMark[mxI]!=mxReadMark
+ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
+ ){
+ walUnlockShared(pWal, WAL_READ_LOCK(mxI));
+ return WAL_RETRY;
+ }else{
+ assert( mxReadMark<=pWal->hdr.mxFrame );
+ pWal->readLock = (i16)mxI;
}
return rc;
}
@@ -46583,6 +56173,14 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
int rc; /* Return code */
int cnt = 0; /* Number of TryBeginRead attempts */
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ int bChanged = 0;
+ WalIndexHdr *pSnapshot = pWal->pSnapshot;
+ if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
+ bChanged = 1;
+ }
+#endif
+
do{
rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
}while( rc==WAL_RETRY );
@@ -46590,6 +56188,66 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
testcase( (rc&0xff)==SQLITE_IOERR );
testcase( rc==SQLITE_PROTOCOL );
testcase( rc==SQLITE_OK );
+
+#ifdef SQLITE_ENABLE_SNAPSHOT
+ if( rc==SQLITE_OK ){
+ if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
+ /* At this point the client has a lock on an aReadMark[] slot holding
+ ** a value equal to or smaller than pSnapshot->mxFrame, but pWal->hdr
+ ** is populated with the wal-index header corresponding to the head
+ ** of the wal file. Verify that pSnapshot is still valid before
+ ** continuing. Reasons why pSnapshot might no longer be valid:
+ **
+ ** (1) The WAL file has been reset since the snapshot was taken.
+ ** In this case, the salt will have changed.
+ **
+ ** (2) A checkpoint as been attempted that wrote frames past
+ ** pSnapshot->mxFrame into the database file. Note that the
+ ** checkpoint need not have completed for this to cause problems.
+ */
+ volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
+
+ assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
+ assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
+
+ /* It is possible that there is a checkpointer thread running
+ ** concurrent with this code. If this is the case, it may be that the
+ ** checkpointer has already determined that it will checkpoint
+ ** snapshot X, where X is later in the wal file than pSnapshot, but
+ ** has not yet set the pInfo->nBackfillAttempted variable to indicate
+ ** its intent. To avoid the race condition this leads to, ensure that
+ ** there is no checkpointer process by taking a shared CKPT lock
+ ** before checking pInfo->nBackfillAttempted. */
+ rc = walLockShared(pWal, WAL_CKPT_LOCK);
+
+ if( rc==SQLITE_OK ){
+ /* Check that the wal file has not been wrapped. Assuming that it has
+ ** not, also check that no checkpointer has attempted to checkpoint any
+ ** frames beyond pSnapshot->mxFrame. If either of these conditions are
+ ** true, return SQLITE_BUSY_SNAPSHOT. Otherwise, overwrite pWal->hdr
+ ** with *pSnapshot and set *pChanged as appropriate for opening the
+ ** snapshot. */
+ if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
+ && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
+ ){
+ assert( pWal->readLock>0 );
+ memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
+ *pChanged = bChanged;
+ }else{
+ rc = SQLITE_BUSY_SNAPSHOT;
+ }
+
+ /* Release the shared CKPT lock obtained above. */
+ walUnlockShared(pWal, WAL_CKPT_LOCK);
+ }
+
+
+ if( rc!=SQLITE_OK ){
+ sqlite3WalEndReadTransaction(pWal);
+ }
+ }
+ }
+#endif
return rc;
}
@@ -46606,23 +56264,22 @@ SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
}
/*
-** Read a page from the WAL, if it is present in the WAL and if the
-** current read transaction is configured to use the WAL.
+** Search the wal file for page pgno. If found, set *piRead to the frame that
+** contains the page. Otherwise, if pgno is not in the wal file, set *piRead
+** to zero.
**
-** The *pInWal is set to 1 if the requested page is in the WAL and
-** has been loaded. Or *pInWal is set to 0 if the page was not in
-** the WAL and needs to be read out of the database.
+** Return SQLITE_OK if successful, or an error code if an error occurs. If an
+** error does occur, the final value of *piRead is undefined.
*/
-SQLITE_PRIVATE int sqlite3WalRead(
+SQLITE_PRIVATE int sqlite3WalFindFrame(
Wal *pWal, /* WAL handle */
Pgno pgno, /* Database page number to read data for */
- int *pInWal, /* OUT: True if data is read from WAL */
- int nOut, /* Size of buffer pOut in bytes */
- u8 *pOut /* Buffer to write page data to */
+ u32 *piRead /* OUT: Frame number (or zero) */
){
u32 iRead = 0; /* If !=0, WAL frame to return data from */
u32 iLast = pWal->hdr.mxFrame; /* Last page in WAL for this reader */
int iHash; /* Used to loop through N hash tables */
+ int iMinHash;
/* This routine is only be called from within a read transaction. */
assert( pWal->readLock>=0 || pWal->lockError );
@@ -46634,7 +56291,7 @@ SQLITE_PRIVATE int sqlite3WalRead(
** WAL were empty.
*/
if( iLast==0 || pWal->readLock==0 ){
- *pInWal = 0;
+ *piRead = 0;
return SQLITE_OK;
}
@@ -46663,7 +56320,8 @@ SQLITE_PRIVATE int sqlite3WalRead(
** This condition filters out entries that were added to the hash
** table after the current read-transaction had started.
*/
- for(iHash=walFramePage(iLast); iHash>=0 && iRead==0; iHash--){
+ iMinHash = walFramePage(pWal->minFrame);
+ for(iHash=walFramePage(iLast); iHash>=iMinHash && iRead==0; iHash--){
volatile ht_slot *aHash; /* Pointer to hash table */
volatile u32 *aPgno; /* Pointer to array of page numbers */
u32 iZero; /* Frame number corresponding to aPgno[0] */
@@ -46678,8 +56336,8 @@ SQLITE_PRIVATE int sqlite3WalRead(
nCollide = HASHTABLE_NSLOT;
for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
u32 iFrame = aHash[iKey] + iZero;
- if( iFrame<=iLast && aPgno[aHash[iKey]]==pgno ){
- assert( iFrame>iRead );
+ if( iFrame<=iLast && iFrame>=pWal->minFrame && aPgno[aHash[iKey]]==pgno ){
+ assert( iFrame>iRead || CORRUPT_DB );
iRead = iFrame;
}
if( (nCollide--)==0 ){
@@ -46695,7 +56353,8 @@ SQLITE_PRIVATE int sqlite3WalRead(
{
u32 iRead2 = 0;
u32 iTest;
- for(iTest=iLast; iTest>0; iTest--){
+ assert( pWal->minFrame>0 );
+ for(iTest=iLast; iTest>=pWal->minFrame; iTest--){
if( walFramePgno(pWal, iTest)==pgno ){
iRead2 = iTest;
break;
@@ -46705,26 +56364,31 @@ SQLITE_PRIVATE int sqlite3WalRead(
}
#endif
- /* If iRead is non-zero, then it is the log frame number that contains the
- ** required page. Read and return data from the log file.
- */
- if( iRead ){
- int sz;
- i64 iOffset;
- sz = pWal->hdr.szPage;
- sz = (pWal->hdr.szPage&0xfe00) + ((pWal->hdr.szPage&0x0001)<<16);
- testcase( sz<=32768 );
- testcase( sz>=65536 );
- iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
- *pInWal = 1;
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
- return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset);
- }
-
- *pInWal = 0;
+ *piRead = iRead;
return SQLITE_OK;
}
+/*
+** Read the contents of frame iRead from the wal file into buffer pOut
+** (which is nOut bytes in size). Return SQLITE_OK if successful, or an
+** error code otherwise.
+*/
+SQLITE_PRIVATE int sqlite3WalReadFrame(
+ Wal *pWal, /* WAL handle */
+ u32 iRead, /* Frame to read */
+ int nOut, /* Size of buffer pOut in bytes */
+ u8 *pOut /* Buffer to write page data to */
+){
+ int sz;
+ i64 iOffset;
+ sz = pWal->hdr.szPage;
+ sz = (sz&0xfe00) + ((sz&0x0001)<<16);
+ testcase( sz<=32768 );
+ testcase( sz>=65536 );
+ iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
+ /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
+ return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset);
+}
/*
** Return the size of the database in pages (or zero, if unknown).
@@ -46756,6 +56420,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
/* Cannot start a write transaction without first holding a read
** transaction. */
assert( pWal->readLock>=0 );
+ assert( pWal->writeLock==0 && pWal->iReCksum==0 );
if( pWal->readOnly ){
return SQLITE_READONLY;
@@ -46777,7 +56442,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
- rc = SQLITE_BUSY;
+ rc = SQLITE_BUSY_SNAPSHOT;
}
return rc;
@@ -46791,6 +56456,8 @@ SQLITE_PRIVATE int sqlite3WalEndWriteTransaction(Wal *pWal){
if( pWal->writeLock ){
walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
pWal->writeLock = 0;
+ pWal->iReCksum = 0;
+ pWal->truncateOnCommit = 0;
}
return SQLITE_OK;
}
@@ -46836,9 +56503,8 @@ SQLITE_PRIVATE int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *p
assert( walFramePgno(pWal, iFrame)!=1 );
rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
}
- walCleanupHash(pWal);
+ if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
}
- assert( rc==SQLITE_OK );
return rc;
}
@@ -46919,36 +56585,8 @@ static int walRestartLog(Wal *pWal){
** In theory it would be Ok to update the cache of the header only
** at this point. But updating the actual wal-index header is also
** safe and means there is no special case for sqlite3WalUndo()
- ** to handle if this transaction is rolled back.
- */
- int i; /* Loop counter */
- u32 *aSalt = pWal->hdr.aSalt; /* Big-endian salt values */
-
- /* Limit the size of WAL file if the journal_size_limit PRAGMA is
- ** set to a non-negative value. Log errors encountered
- ** during the truncation attempt. */
- if( pWal->mxWalSize>=0 ){
- i64 sz;
- int rx;
- sqlite3BeginBenignMalloc();
- rx = sqlite3OsFileSize(pWal->pWalFd, &sz);
- if( rx==SQLITE_OK && (sz > pWal->mxWalSize) ){
- rx = sqlite3OsTruncate(pWal->pWalFd, pWal->mxWalSize);
- }
- sqlite3EndBenignMalloc();
- if( rx ){
- sqlite3_log(rx, "cannot limit WAL size: %s", pWal->zWalName);
- }
- }
-
- pWal->nCkpt++;
- pWal->hdr.mxFrame = 0;
- sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
- aSalt[1] = salt1;
- walIndexWriteHdr(pWal);
- pInfo->nBackfill = 0;
- for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
- assert( pInfo->aReadMark[0]==0 );
+ ** to handle if this transaction is rolled back. */
+ walRestartHdr(pWal, salt1);
walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
}else if( rc!=SQLITE_BUSY ){
return rc;
@@ -46969,6 +56607,127 @@ static int walRestartLog(Wal *pWal){
return rc;
}
+/*
+** Information about the current state of the WAL file and where
+** the next fsync should occur - passed from sqlite3WalFrames() into
+** walWriteToLog().
+*/
+typedef struct WalWriter {
+ Wal *pWal; /* The complete WAL information */
+ sqlite3_file *pFd; /* The WAL file to which we write */
+ sqlite3_int64 iSyncPoint; /* Fsync at this offset */
+ int syncFlags; /* Flags for the fsync */
+ int szPage; /* Size of one page */
+} WalWriter;
+
+/*
+** Write iAmt bytes of content into the WAL file beginning at iOffset.
+** Do a sync when crossing the p->iSyncPoint boundary.
+**
+** In other words, if iSyncPoint is in between iOffset and iOffset+iAmt,
+** first write the part before iSyncPoint, then sync, then write the
+** rest.
+*/
+static int walWriteToLog(
+ WalWriter *p, /* WAL to write to */
+ void *pContent, /* Content to be written */
+ int iAmt, /* Number of bytes to write */
+ sqlite3_int64 iOffset /* Start writing at this offset */
+){
+ int rc;
+ if( iOffset<p->iSyncPoint && iOffset+iAmt>=p->iSyncPoint ){
+ int iFirstAmt = (int)(p->iSyncPoint - iOffset);
+ rc = sqlite3OsWrite(p->pFd, pContent, iFirstAmt, iOffset);
+ if( rc ) return rc;
+ iOffset += iFirstAmt;
+ iAmt -= iFirstAmt;
+ pContent = (void*)(iFirstAmt + (char*)pContent);
+ assert( p->syncFlags & (SQLITE_SYNC_NORMAL|SQLITE_SYNC_FULL) );
+ rc = sqlite3OsSync(p->pFd, p->syncFlags & SQLITE_SYNC_MASK);
+ if( iAmt==0 || rc ) return rc;
+ }
+ rc = sqlite3OsWrite(p->pFd, pContent, iAmt, iOffset);
+ return rc;
+}
+
+/*
+** Write out a single frame of the WAL
+*/
+static int walWriteOneFrame(
+ WalWriter *p, /* Where to write the frame */
+ PgHdr *pPage, /* The page of the frame to be written */
+ int nTruncate, /* The commit flag. Usually 0. >0 for commit */
+ sqlite3_int64 iOffset /* Byte offset at which to write */
+){
+ int rc; /* Result code from subfunctions */
+ void *pData; /* Data actually written */
+ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
+#if defined(SQLITE_HAS_CODEC)
+ if( (pData = sqlite3PagerCodec(pPage))==0 ) return SQLITE_NOMEM_BKPT;
+#else
+ pData = pPage->pData;
+#endif
+ walEncodeFrame(p->pWal, pPage->pgno, nTruncate, pData, aFrame);
+ rc = walWriteToLog(p, aFrame, sizeof(aFrame), iOffset);
+ if( rc ) return rc;
+ /* Write the page data */
+ rc = walWriteToLog(p, pData, p->szPage, iOffset+sizeof(aFrame));
+ return rc;
+}
+
+/*
+** This function is called as part of committing a transaction within which
+** one or more frames have been overwritten. It updates the checksums for
+** all frames written to the wal file by the current transaction starting
+** with the earliest to have been overwritten.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
+*/
+static int walRewriteChecksums(Wal *pWal, u32 iLast){
+ const int szPage = pWal->szPage;/* Database page size */
+ int rc = SQLITE_OK; /* Return code */
+ u8 *aBuf; /* Buffer to load data from wal file into */
+ u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-headers in */
+ u32 iRead; /* Next frame to read from wal file */
+ i64 iCksumOff;
+
+ aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE);
+ if( aBuf==0 ) return SQLITE_NOMEM_BKPT;
+
+ /* Find the checksum values to use as input for the recalculating the
+ ** first checksum. If the first frame is frame 1 (implying that the current
+ ** transaction restarted the wal file), these values must be read from the
+ ** wal-file header. Otherwise, read them from the frame header of the
+ ** previous frame. */
+ assert( pWal->iReCksum>0 );
+ if( pWal->iReCksum==1 ){
+ iCksumOff = 24;
+ }else{
+ iCksumOff = walFrameOffset(pWal->iReCksum-1, szPage) + 16;
+ }
+ rc = sqlite3OsRead(pWal->pWalFd, aBuf, sizeof(u32)*2, iCksumOff);
+ pWal->hdr.aFrameCksum[0] = sqlite3Get4byte(aBuf);
+ pWal->hdr.aFrameCksum[1] = sqlite3Get4byte(&aBuf[sizeof(u32)]);
+
+ iRead = pWal->iReCksum;
+ pWal->iReCksum = 0;
+ for(; rc==SQLITE_OK && iRead<=iLast; iRead++){
+ i64 iOff = walFrameOffset(iRead, szPage);
+ rc = sqlite3OsRead(pWal->pWalFd, aBuf, szPage+WAL_FRAME_HDRSIZE, iOff);
+ if( rc==SQLITE_OK ){
+ u32 iPgno, nDbSize;
+ iPgno = sqlite3Get4byte(aBuf);
+ nDbSize = sqlite3Get4byte(&aBuf[4]);
+
+ walEncodeFrame(pWal, iPgno, nDbSize, &aBuf[WAL_FRAME_HDRSIZE], aFrame);
+ rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOff);
+ }
+ }
+
+ sqlite3_free(aBuf);
+ return rc;
+}
+
/*
** Write a set of frames to the log. The caller must hold the write-lock
** on the log file (obtained using sqlite3WalBeginWriteTransaction()).
@@ -46983,14 +56742,22 @@ SQLITE_PRIVATE int sqlite3WalFrames(
){
int rc; /* Used to catch return codes */
u32 iFrame; /* Next frame address */
- u8 aFrame[WAL_FRAME_HDRSIZE]; /* Buffer to assemble frame-header in */
PgHdr *p; /* Iterator to run through pList with. */
PgHdr *pLast = 0; /* Last frame in list */
- int nLast = 0; /* Number of extra copies of last page */
+ int nExtra = 0; /* Number of extra copies of last page */
+ int szFrame; /* The size of a single frame */
+ i64 iOffset; /* Next byte to write in WAL file */
+ WalWriter w; /* The writer */
+ u32 iFirst = 0; /* First frame that may be overwritten */
+ WalIndexHdr *pLive; /* Pointer to shared header */
assert( pList );
assert( pWal->writeLock );
+ /* If this frame set completes a transaction, then nTruncate>0. If
+ ** nTruncate==0 then this frame set does not complete the transaction. */
+ assert( (isCommit!=0)==(nTruncate!=0) );
+
#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
{ int cnt; for(cnt=0, p=pList; p; p=p->pDirty, cnt++){}
WALTRACE(("WAL%p: frame write begin. %d frames. mxFrame=%d. %s\n",
@@ -46998,6 +56765,11 @@ SQLITE_PRIVATE int sqlite3WalFrames(
}
#endif
+ pLive = (WalIndexHdr*)walIndexHdr(pWal);
+ if( memcmp(&pWal->hdr, (void *)pLive, sizeof(WalIndexHdr))!=0 ){
+ iFirst = pLive->mxFrame+1;
+ }
+
/* See if it is possible to write these frames into the start of the
** log file, instead of appending to it at pWal->hdr.mxFrame.
*/
@@ -47018,7 +56790,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
sqlite3Put4byte(&aWalHdr[4], WAL_MAX_VERSION);
sqlite3Put4byte(&aWalHdr[8], szPage);
sqlite3Put4byte(&aWalHdr[12], pWal->nCkpt);
- sqlite3_randomness(8, pWal->hdr.aSalt);
+ if( pWal->nCkpt==0 ) sqlite3_randomness(8, pWal->hdr.aSalt);
memcpy(&aWalHdr[16], pWal->hdr.aSalt, 8);
walChecksumBytes(1, aWalHdr, WAL_HDRSIZE-2*4, 0, aCksum);
sqlite3Put4byte(&aWalHdr[24], aCksum[0]);
@@ -47028,77 +56800,128 @@ SQLITE_PRIVATE int sqlite3WalFrames(
pWal->hdr.bigEndCksum = SQLITE_BIGENDIAN;
pWal->hdr.aFrameCksum[0] = aCksum[0];
pWal->hdr.aFrameCksum[1] = aCksum[1];
+ pWal->truncateOnCommit = 1;
rc = sqlite3OsWrite(pWal->pWalFd, aWalHdr, sizeof(aWalHdr), 0);
WALTRACE(("WAL%p: wal-header write %s\n", pWal, rc ? "failed" : "ok"));
if( rc!=SQLITE_OK ){
return rc;
}
+
+ /* Sync the header (unless SQLITE_IOCAP_SEQUENTIAL is true or unless
+ ** all syncing is turned off by PRAGMA synchronous=OFF). Otherwise
+ ** an out-of-order write following a WAL restart could result in
+ ** database corruption. See the ticket:
+ **
+ ** http://localhost:591/sqlite/info/ff5be73dee
+ */
+ if( pWal->syncHeader && sync_flags ){
+ rc = sqlite3OsSync(pWal->pWalFd, sync_flags & SQLITE_SYNC_MASK);
+ if( rc ) return rc;
+ }
}
assert( (int)pWal->szPage==szPage );
- /* Write the log file. */
+ /* Setup information needed to write frames into the WAL */
+ w.pWal = pWal;
+ w.pFd = pWal->pWalFd;
+ w.iSyncPoint = 0;
+ w.syncFlags = sync_flags;
+ w.szPage = szPage;
+ iOffset = walFrameOffset(iFrame+1, szPage);
+ szFrame = szPage + WAL_FRAME_HDRSIZE;
+
+ /* Write all frames into the log file exactly once */
for(p=pList; p; p=p->pDirty){
- u32 nDbsize; /* Db-size field for frame header */
- i64 iOffset; /* Write offset in log file */
- void *pData;
-
- iOffset = walFrameOffset(++iFrame, szPage);
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
-
- /* Populate and write the frame header */
- nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0;
+ int nDbSize; /* 0 normally. Positive == commit flag */
+
+ /* Check if this page has already been written into the wal file by
+ ** the current transaction. If so, overwrite the existing frame and
+ ** set Wal.writeLock to WAL_WRITELOCK_RECKSUM - indicating that
+ ** checksums must be recomputed when the transaction is committed. */
+ if( iFirst && (p->pDirty || isCommit==0) ){
+ u32 iWrite = 0;
+ VVA_ONLY(rc =) sqlite3WalFindFrame(pWal, p->pgno, &iWrite);
+ assert( rc==SQLITE_OK || iWrite==0 );
+ if( iWrite>=iFirst ){
+ i64 iOff = walFrameOffset(iWrite, szPage) + WAL_FRAME_HDRSIZE;
+ void *pData;
+ if( pWal->iReCksum==0 || iWrite<pWal->iReCksum ){
+ pWal->iReCksum = iWrite;
+ }
#if defined(SQLITE_HAS_CODEC)
- if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
+ if( (pData = sqlite3PagerCodec(p))==0 ) return SQLITE_NOMEM;
#else
- pData = p->pData;
+ pData = p->pData;
#endif
- walEncodeFrame(pWal, p->pgno, nDbsize, pData, aFrame);
- rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
- if( rc!=SQLITE_OK ){
- return rc;
+ rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOff);
+ if( rc ) return rc;
+ p->flags &= ~PGHDR_WAL_APPEND;
+ continue;
+ }
}
- /* Write the page data */
- rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset+sizeof(aFrame));
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ iFrame++;
+ assert( iOffset==walFrameOffset(iFrame, szPage) );
+ nDbSize = (isCommit && p->pDirty==0) ? nTruncate : 0;
+ rc = walWriteOneFrame(&w, p, nDbSize, iOffset);
+ if( rc ) return rc;
pLast = p;
+ iOffset += szFrame;
+ p->flags |= PGHDR_WAL_APPEND;
}
- /* Sync the log file if the 'isSync' flag was specified. */
- if( sync_flags ){
- i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd);
- i64 iOffset = walFrameOffset(iFrame+1, szPage);
-
- assert( isCommit );
- assert( iSegment>0 );
+ /* Recalculate checksums within the wal file if required. */
+ if( isCommit && pWal->iReCksum ){
+ rc = walRewriteChecksums(pWal, iFrame);
+ if( rc ) return rc;
+ }
- iSegment = (((iOffset+iSegment-1)/iSegment) * iSegment);
- while( iOffset<iSegment ){
- void *pData;
-#if defined(SQLITE_HAS_CODEC)
- if( (pData = sqlite3PagerCodec(pLast))==0 ) return SQLITE_NOMEM;
-#else
- pData = pLast->pData;
-#endif
- walEncodeFrame(pWal, pLast->pgno, nTruncate, pData, aFrame);
- /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
- rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- iOffset += WAL_FRAME_HDRSIZE;
- rc = sqlite3OsWrite(pWal->pWalFd, pData, szPage, iOffset);
- if( rc!=SQLITE_OK ){
- return rc;
+ /* If this is the end of a transaction, then we might need to pad
+ ** the transaction and/or sync the WAL file.
+ **
+ ** Padding and syncing only occur if this set of frames complete a
+ ** transaction and if PRAGMA synchronous=FULL. If synchronous==NORMAL
+ ** or synchronous==OFF, then no padding or syncing are needed.
+ **
+ ** If SQLITE_IOCAP_POWERSAFE_OVERWRITE is defined, then padding is not
+ ** needed and only the sync is done. If padding is needed, then the
+ ** final frame is repeated (with its commit mark) until the next sector
+ ** boundary is crossed. Only the part of the WAL prior to the last
+ ** sector boundary is synced; the part of the last frame that extends
+ ** past the sector boundary is written after the sync.
+ */
+ if( isCommit && (sync_flags & WAL_SYNC_TRANSACTIONS)!=0 ){
+ int bSync = 1;
+ if( pWal->padToSectorBoundary ){
+ int sectorSize = sqlite3SectorSize(pWal->pWalFd);
+ w.iSyncPoint = ((iOffset+sectorSize-1)/sectorSize)*sectorSize;
+ bSync = (w.iSyncPoint==iOffset);
+ testcase( bSync );
+ while( iOffset<w.iSyncPoint ){
+ rc = walWriteOneFrame(&w, pLast, nTruncate, iOffset);
+ if( rc ) return rc;
+ iOffset += szFrame;
+ nExtra++;
}
- nLast++;
- iOffset += szPage;
}
+ if( bSync ){
+ assert( rc==SQLITE_OK );
+ rc = sqlite3OsSync(w.pFd, sync_flags & SQLITE_SYNC_MASK);
+ }
+ }
- rc = sqlite3OsSync(pWal->pWalFd, sync_flags);
+ /* If this frame set completes the first transaction in the WAL and
+ ** if PRAGMA journal_size_limit is set, then truncate the WAL to the
+ ** journal size limit, if possible.
+ */
+ if( isCommit && pWal->truncateOnCommit && pWal->mxWalSize>=0 ){
+ i64 sz = pWal->mxWalSize;
+ if( walFrameOffset(iFrame+nExtra+1, szPage)>pWal->mxWalSize ){
+ sz = walFrameOffset(iFrame+nExtra+1, szPage);
+ }
+ walLimitSize(pWal, sz);
+ pWal->truncateOnCommit = 0;
}
/* Append data to the wal-index. It is not necessary to lock the
@@ -47108,12 +56931,13 @@ SQLITE_PRIVATE int sqlite3WalFrames(
*/
iFrame = pWal->hdr.mxFrame;
for(p=pList; p && rc==SQLITE_OK; p=p->pDirty){
+ if( (p->flags & PGHDR_WAL_APPEND)==0 ) continue;
iFrame++;
rc = walIndexAppend(pWal, iFrame, p->pgno);
}
- while( nLast>0 && rc==SQLITE_OK ){
+ while( rc==SQLITE_OK && nExtra>0 ){
iFrame++;
- nLast--;
+ nExtra--;
rc = walIndexAppend(pWal, iFrame, pLast->pgno);
}
@@ -47150,7 +56974,7 @@ SQLITE_PRIVATE int sqlite3WalFrames(
*/
SQLITE_PRIVATE int sqlite3WalCheckpoint(
Wal *pWal, /* Wal connection */
- int eMode, /* PASSIVE, FULL or RESTART */
+ int eMode, /* PASSIVE, FULL, RESTART, or TRUNCATE */
int (*xBusy)(void*), /* Function to call when busy */
void *pBusyArg, /* Context argument for xBusyHandler */
int sync_flags, /* Flags to sync db file with (or 0) */
@@ -47162,29 +56986,42 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
int rc; /* Return code */
int isChanged = 0; /* True if a new wal-index header is loaded */
int eMode2 = eMode; /* Mode to pass to walCheckpoint() */
+ int (*xBusy2)(void*) = xBusy; /* Busy handler for eMode2 */
assert( pWal->ckptLock==0 );
assert( pWal->writeLock==0 );
+ /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
+ ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
+ assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
+
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
+
+ /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
+ ** "checkpoint" lock on the database file. */
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
if( rc ){
- /* Usually this is SQLITE_BUSY meaning that another thread or process
- ** is already running a checkpoint, or maybe a recovery. But it might
- ** also be SQLITE_IOERR. */
+ /* EVIDENCE-OF: R-10421-19736 If any other process is running a
+ ** checkpoint operation at the same time, the lock cannot be obtained and
+ ** SQLITE_BUSY is returned.
+ ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
+ ** it will not be invoked in this case.
+ */
+ testcase( rc==SQLITE_BUSY );
+ testcase( xBusy!=0 );
return rc;
}
pWal->ckptLock = 1;
- /* If this is a blocking-checkpoint, then obtain the write-lock as well
- ** to prevent any writers from running while the checkpoint is underway.
- ** This has to be done before the call to walIndexReadHdr() below.
+ /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
+ ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
+ ** file.
**
- ** If the writer lock cannot be obtained, then a passive checkpoint is
- ** run instead. Since the checkpointer is not holding the writer lock,
- ** there is no point in blocking waiting for any readers. Assuming no
- ** other error occurs, this function will return SQLITE_BUSY to the caller.
+ ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
+ ** immediately, and a busy-handler is configured, it is invoked and the
+ ** writer lock retried until either the busy-handler returns 0 or the
+ ** lock is successfully obtained.
*/
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
@@ -47192,6 +57029,7 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
pWal->writeLock = 1;
}else if( rc==SQLITE_BUSY ){
eMode2 = SQLITE_CHECKPOINT_PASSIVE;
+ xBusy2 = 0;
rc = SQLITE_OK;
}
}
@@ -47199,14 +57037,18 @@ SQLITE_PRIVATE int sqlite3WalCheckpoint(
/* Read the wal-index header. */
if( rc==SQLITE_OK ){
rc = walIndexReadHdr(pWal, &isChanged);
+ if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
+ sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
+ }
}
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
+
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = walCheckpoint(pWal, eMode2, xBusy, pBusyArg, sync_flags, zBuf);
+ rc = walCheckpoint(pWal, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
}
/* If no error occurred, set the output variables. */
@@ -47318,6 +57160,70 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
return (pWal && pWal->exclusiveMode==WAL_HEAPMEMORY_MODE );
}
+#ifdef SQLITE_ENABLE_SNAPSHOT
+/* Create a snapshot object. The content of a snapshot is opaque to
+** every other subsystem, so the WAL module can put whatever it needs
+** in the object.
+*/
+SQLITE_PRIVATE int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){
+ int rc = SQLITE_OK;
+ WalIndexHdr *pRet;
+
+ assert( pWal->readLock>=0 && pWal->writeLock==0 );
+
+ pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr));
+ if( pRet==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr));
+ *ppSnapshot = (sqlite3_snapshot*)pRet;
+ }
+
+ return rc;
+}
+
+/* Try to open on pSnapshot when the next read-transaction starts
+*/
+SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){
+ pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
+}
+
+/*
+** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
+** p1 is older than p2 and zero if p1 and p2 are the same snapshot.
+*/
+SQLITE_API int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){
+ WalIndexHdr *pHdr1 = (WalIndexHdr*)p1;
+ WalIndexHdr *pHdr2 = (WalIndexHdr*)p2;
+
+ /* aSalt[0] is a copy of the value stored in the wal file header. It
+ ** is incremented each time the wal file is restarted. */
+ if( pHdr1->aSalt[0]<pHdr2->aSalt[0] ) return -1;
+ if( pHdr1->aSalt[0]>pHdr2->aSalt[0] ) return +1;
+ if( pHdr1->mxFrame<pHdr2->mxFrame ) return -1;
+ if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1;
+ return 0;
+}
+#endif /* SQLITE_ENABLE_SNAPSHOT */
+
+#ifdef SQLITE_ENABLE_ZIPVFS
+/*
+** If the argument is not NULL, it points to a Wal object that holds a
+** read-lock. This function returns the database page-size if it is known,
+** or zero if it is not (or if pWal is NULL).
+*/
+SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal){
+ assert( pWal==0 || pWal->readLock>=0 );
+ return (pWal ? pWal->szPage : 0);
+}
+#endif
+
+/* Return the sqlite3_file object for the WAL file
+*/
+SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal){
+ return pWal->pWalFd;
+}
+
#endif /* #ifndef SQLITE_OMIT_WAL */
/************** End of wal.c *************************************************/
@@ -47352,7 +57258,7 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file implements a external (disk-based) database using BTrees.
+** This file implements an external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
@@ -47399,13 +57305,13 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
**
** OFFSET SIZE DESCRIPTION
** 0 16 Header string: "SQLite format 3\000"
-** 16 2 Page size in bytes.
+** 16 2 Page size in bytes. (1 means 65536)
** 18 1 File format write version
** 19 1 File format read version
** 20 1 Bytes of unused space at the end of each page
-** 21 1 Max embedded payload fraction
-** 22 1 Min embedded payload fraction
-** 23 1 Min leaf payload fraction
+** 21 1 Max embedded payload fraction (must be 64)
+** 22 1 Min embedded payload fraction (must be 32)
+** 23 1 Min leaf payload fraction (must be 32)
** 24 4 File change counter
** 28 4 Reserved for future use
** 32 4 First freelist page
@@ -47419,9 +57325,10 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
** 56 4 1=UTF-8 2=UTF16le 3=UTF16be
** 60 4 User version
** 64 4 Incremental vacuum mode
-** 68 4 unused
-** 72 4 unused
-** 76 4 unused
+** 68 4 Application-ID
+** 72 20 unused
+** 92 4 The version-valid-for number
+** 96 4 SQLITE_VERSION_NUMBER
**
** All of the integer values are big-endian (most significant byte first).
**
@@ -47477,7 +57384,7 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
**
** The flags define the format of this btree page. The leaf flag means that
** this page has no children. The zerodata flag means that this page carries
-** only keys and no data. The intkey flag means that the key is a integer
+** only keys and no data. The intkey flag means that the key is an integer
** which is stored in the key size entry of the cell header rather than in
** the payload area.
**
@@ -47555,6 +57462,7 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
** 4 Number of leaf pointers on this page
** * zero or more pages numbers of leaves
*/
+/* #include "sqliteInt.h" */
/* The following value is the maximum cell size assuming a maximum page
@@ -47572,6 +57480,7 @@ SQLITE_PRIVATE int sqlite3WalHeapMemory(Wal *pWal){
/* Forward declarations */
typedef struct MemPage MemPage;
typedef struct BtLock BtLock;
+typedef struct CellInfo CellInfo;
/*
** This is a magic string that appears at the beginning of every
@@ -47614,24 +57523,30 @@ typedef struct BtLock BtLock;
struct MemPage {
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
- u8 intKey; /* True if intkey flag is set */
- u8 leaf; /* True if leaf flag is set */
- u8 hasData; /* True if this page stores data */
+ u8 intKey; /* True if table b-trees. False for index b-trees */
+ u8 intKeyLeaf; /* True if the leaf of an intKey table */
+ u8 leaf; /* True if a leaf page */
u8 hdrOffset; /* 100 for page 1. 0 otherwise */
u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */
+ u8 max1bytePayload; /* min(maxLocal,127) */
+ u8 bBusy; /* Prevent endless loops on corrupt database files */
u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */
u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */
u16 cellOffset; /* Index in aData of first cell pointer */
u16 nFree; /* Number of free bytes on the page */
u16 nCell; /* Number of cells on this page, local and ovfl */
u16 maskPage; /* Mask for page offset */
- struct _OvflCell { /* Cells that will not fit on aData[] */
- u8 *pCell; /* Pointers to the body of the overflow cell */
- u16 idx; /* Insert this cell before idx-th non-overflow cell */
- } aOvfl[5];
+ u16 aiOvfl[5]; /* Insert the i-th overflow cell before the aiOvfl-th
+ ** non-overflow cell */
+ u8 *apOvfl[5]; /* Pointers to the body of overflow cells */
BtShared *pBt; /* Pointer to BtShared that this page is part of */
u8 *aData; /* Pointer to disk image of the page data */
+ u8 *aDataEnd; /* One byte past the end of usable data */
+ u8 *aCellIdx; /* The cell index area */
+ u8 *aDataOfst; /* Same as aData for leaves. aData+4 for interior */
DbPage *pDbPage; /* Pager page handle */
+ u16 (*xCellSize)(MemPage*,u8*); /* cellSizePtr method */
+ void (*xParseCell)(MemPage*,u8*,CellInfo*); /* btreeParseCell method */
Pgno pgno; /* Page number for this page */
};
@@ -47687,8 +57602,10 @@ struct Btree {
u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */
u8 sharable; /* True if we can share pBt with another db */
u8 locked; /* True if db currently has pBt locked */
+ u8 hasIncrblobCur; /* True if there are one or more Incrblob cursors */
int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */
int nBackup; /* Number of backup operations reading this btree */
+ u32 iDataVersion; /* Combines with pBt->pPager->iDataVersion */
Btree *pNext; /* List of other sharable Btrees from the same db */
Btree *pPrev; /* Back pointer of the same list */
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -47710,7 +57627,7 @@ struct Btree {
/*
** An instance of this object represents a single database file.
**
-** A single database file can be in use as the same time by two
+** A single database file can be in use at the same time by two
** or more database connections. When two or more connections are
** sharing the same database file, each connection has it own
** private Btree object for the file and each of those Btrees points
@@ -47747,17 +57664,18 @@ struct BtShared {
sqlite3 *db; /* Database connection currently using this Btree */
BtCursor *pCursor; /* A list of all open cursors */
MemPage *pPage1; /* First page of the database */
- u8 readOnly; /* True if the underlying file is readonly */
- u8 pageSizeFixed; /* True if the page size can no longer be changed */
- u8 secureDelete; /* True if secure_delete is enabled */
- u8 initiallyEmpty; /* Database is empty at start of transaction */
u8 openFlags; /* Flags to sqlite3BtreeOpen() */
#ifndef SQLITE_OMIT_AUTOVACUUM
u8 autoVacuum; /* True if auto-vacuum is enabled */
u8 incrVacuum; /* True if incr-vacuum is enabled */
+ u8 bDoTruncate; /* True to truncate db on commit */
#endif
u8 inTransaction; /* Transaction state */
- u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
+ u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
+#ifdef SQLITE_HAS_CODEC
+ u8 optimalReserve; /* Desired amount of reserved space per page */
+#endif
+ u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
@@ -47775,26 +57693,31 @@ struct BtShared {
BtShared *pNext; /* Next on a list of sharable BtShared structs */
BtLock *pLock; /* List of locks held on this shared-btree struct */
Btree *pWriter; /* Btree with currently open write transaction */
- u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
- u8 isPending; /* If waiting for read-locks to clear */
#endif
- u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
+ u8 *pTmpSpace; /* Temp space sufficient to hold a single cell */
};
/*
+** Allowed values for BtShared.btsFlags
+*/
+#define BTS_READ_ONLY 0x0001 /* Underlying file is readonly */
+#define BTS_PAGESIZE_FIXED 0x0002 /* Page size can no longer be changed */
+#define BTS_SECURE_DELETE 0x0004 /* PRAGMA secure_delete is enabled */
+#define BTS_INITIALLY_EMPTY 0x0008 /* Database was empty at trans start */
+#define BTS_NO_WAL 0x0010 /* Do not open write-ahead-log files */
+#define BTS_EXCLUSIVE 0x0020 /* pWriter has an exclusive lock */
+#define BTS_PENDING 0x0040 /* Waiting for read-locks to clear */
+
+/*
** An instance of the following structure is used to hold information
** about a cell. The parseCellPtr() function fills in this structure
** based on information extract from the raw disk page.
*/
-typedef struct CellInfo CellInfo;
struct CellInfo {
- i64 nKey; /* The key for INTKEY tables, or number of bytes in key */
- u8 *pCell; /* Pointer to the start of cell content */
- u32 nData; /* Number of bytes of data */
- u32 nPayload; /* Total amount of payload */
- u16 nHeader; /* Size of the cell content header in bytes */
- u16 nLocal; /* Amount of payload held locally */
- u16 iOverflow; /* Offset to overflow page number. Zero if no overflow */
+ i64 nKey; /* The key for INTKEY tables, or nPayload otherwise */
+ u8 *pPayload; /* Pointer to the start of payload */
+ u32 nPayload; /* Bytes of payload */
+ u16 nLocal; /* Amount of payload held locally, not on overflow */
u16 nSize; /* Size of the cell content on the main b-tree page */
};
@@ -47816,48 +57739,71 @@ struct CellInfo {
** The entry is identified by its MemPage and the index in
** MemPage.aCell[] of the entry.
**
-** A single database file can shared by two more database connections,
+** A single database file can be shared by two more database connections,
** but cursors cannot be shared. Each cursor is associated with a
** particular database connection identified BtCursor.pBtree.db.
**
** Fields in this structure are accessed under the BtShared.mutex
** found at self->pBt->mutex.
+**
+** skipNext meaning:
+** eState==SKIPNEXT && skipNext>0: Next sqlite3BtreeNext() is no-op.
+** eState==SKIPNEXT && skipNext<0: Next sqlite3BtreePrevious() is no-op.
+** eState==FAULT: Cursor fault with skipNext as error code.
*/
struct BtCursor {
Btree *pBtree; /* The Btree to which this cursor belongs */
BtShared *pBt; /* The BtShared this cursor points to */
- BtCursor *pNext, *pPrev; /* Forms a linked list of all cursors */
- struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
- Pgno pgnoRoot; /* The root page of this tree */
- sqlite3_int64 cachedRowid; /* Next rowid cache. 0 means not valid */
+ BtCursor *pNext; /* Forms a linked list of all cursors */
+ Pgno *aOverflow; /* Cache of overflow page locations */
CellInfo info; /* A parse of the cell we are pointing at */
- i64 nKey; /* Size of pKey, or last integer key */
- void *pKey; /* Saved key that was cursor's last known position */
- int skipNext; /* Prev() is noop if negative. Next() is noop if positive */
- u8 wrFlag; /* True if writable */
- u8 atLast; /* Cursor pointing to the last entry */
- u8 validNKey; /* True if info.nKey is valid */
+ i64 nKey; /* Size of pKey, or last integer key */
+ void *pKey; /* Saved key that was cursor last known position */
+ Pgno pgnoRoot; /* The root page of this tree */
+ int nOvflAlloc; /* Allocated size of aOverflow[] array */
+ int skipNext; /* Prev() is noop if negative. Next() is noop if positive.
+ ** Error code if eState==CURSOR_FAULT */
+ u8 curFlags; /* zero or more BTCF_* flags defined below */
+ u8 curPagerFlags; /* Flags to send to sqlite3PagerGet() */
u8 eState; /* One of the CURSOR_XXX constants (see below) */
-#ifndef SQLITE_OMIT_INCRBLOB
- Pgno *aOverflow; /* Cache of overflow page locations */
- u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */
-#endif
- i16 iPage; /* Index of current page in apPage */
+ u8 hints; /* As configured by CursorSetHints() */
+ /* All fields above are zeroed when the cursor is allocated. See
+ ** sqlite3BtreeCursorZero(). Fields that follow must be manually
+ ** initialized. */
+ i8 iPage; /* Index of current page in apPage */
+ u8 curIntKey; /* Value of apPage[0]->intKey */
+ struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
+ void *padding1; /* Make object size a multiple of 16 */
u16 aiIdx[BTCURSOR_MAX_DEPTH]; /* Current index in apPage[i] */
MemPage *apPage[BTCURSOR_MAX_DEPTH]; /* Pages from root to current page */
};
/*
+** Legal values for BtCursor.curFlags
+*/
+#define BTCF_WriteFlag 0x01 /* True if a write cursor */
+#define BTCF_ValidNKey 0x02 /* True if info.nKey is valid */
+#define BTCF_ValidOvfl 0x04 /* True if aOverflow is valid */
+#define BTCF_AtLast 0x08 /* Cursor is pointing ot the last entry */
+#define BTCF_Incrblob 0x10 /* True if an incremental I/O handle */
+#define BTCF_Multiple 0x20 /* Maybe another cursor on the same btree */
+
+/*
** Potential values for BtCursor.eState.
**
-** CURSOR_VALID:
-** Cursor points to a valid entry. getPayload() etc. may be called.
-**
** CURSOR_INVALID:
** Cursor does not point to a valid entry. This can happen (for example)
** because the table is empty or because BtreeCursorFirst() has not been
** called.
**
+** CURSOR_VALID:
+** Cursor points to a valid entry. getPayload() etc. may be called.
+**
+** CURSOR_SKIPNEXT:
+** Cursor is valid except that the Cursor.skipNext field is non-zero
+** indicating that the next sqlite3BtreeNext() or sqlite3BtreePrevious()
+** operation should be a no-op.
+**
** CURSOR_REQUIRESEEK:
** The table that this cursor was opened on still exists, but has been
** modified since the cursor was last used. The cursor position is saved
@@ -47866,16 +57812,17 @@ struct BtCursor {
** seek the cursor to the saved position.
**
** CURSOR_FAULT:
-** A unrecoverable error (an I/O error or a malloc failure) has occurred
+** An unrecoverable error (an I/O error or a malloc failure) has occurred
** on a different connection that shares the BtShared cache with this
** cursor. The error has left the cache in an inconsistent state.
** Do nothing else with this cursor. Any attempt to use the cursor
-** should return the error code stored in BtCursor.skip
+** should return the error code stored in BtCursor.skipNext
*/
#define CURSOR_INVALID 0
#define CURSOR_VALID 1
-#define CURSOR_REQUIRESEEK 2
-#define CURSOR_FAULT 3
+#define CURSOR_SKIPNEXT 2
+#define CURSOR_REQUIRESEEK 3
+#define CURSOR_FAULT 4
/*
** The database page the PENDING_BYTE occupies. This page is never used.
@@ -47963,27 +57910,53 @@ struct BtCursor {
/*
** This structure is passed around through all the sanity checking routines
** in order to keep track of some global state information.
+**
+** The aRef[] array is allocated so that there is 1 bit for each page in
+** the database. As the integrity-check proceeds, for each page used in
+** the database the corresponding bit is set. This allows integrity-check to
+** detect pages that are used twice and orphaned pages (both of which
+** indicate corruption).
*/
typedef struct IntegrityCk IntegrityCk;
struct IntegrityCk {
BtShared *pBt; /* The tree being checked out */
Pager *pPager; /* The associated pager. Also accessible by pBt->pPager */
+ u8 *aPgRef; /* 1 bit per page in the db (see above) */
Pgno nPage; /* Number of pages in the database */
- int *anRef; /* Number of times each page is referenced */
int mxErr; /* Stop accumulating errors when this reaches zero */
int nErr; /* Number of messages written to zErrMsg so far */
int mallocFailed; /* A memory allocation error has occurred */
+ const char *zPfx; /* Error message prefix */
+ int v1, v2; /* Values for up to two %d fields in zPfx */
StrAccum errMsg; /* Accumulate the error message text here */
+ u32 *heap; /* Min-heap used for analyzing cell coverage */
};
/*
-** Read or write a two- and four-byte big-endian integer values.
+** Routines to read or write a two- and four-byte big-endian integer values.
*/
#define get2byte(x) ((x)[0]<<8 | (x)[1])
#define put2byte(p,v) ((p)[0] = (u8)((v)>>8), (p)[1] = (u8)(v))
#define get4byte sqlite3Get4byte
#define put4byte sqlite3Put4byte
+/*
+** get2byteAligned(), unlike get2byte(), requires that its argument point to a
+** two-byte aligned address. get2bytea() is only used for accessing the
+** cell addresses in a btree header.
+*/
+#if SQLITE_BYTEORDER==4321
+# define get2byteAligned(x) (*(u16*)(x))
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && GCC_VERSION>=4008000
+# define get2byteAligned(x) __builtin_bswap16(*(u16*)(x))
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \
+ && defined(_MSC_VER) && _MSC_VER>=1300
+# define get2byteAligned(x) _byteswap_ushort(*(u16*)(x))
+#else
+# define get2byteAligned(x) ((x)[0]<<8 | (x)[1])
+#endif
+
/************** End of btreeInt.h ********************************************/
/************** Continuing where we left off in btmutex.c ********************/
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -48008,7 +57981,7 @@ static void lockBtreeMutex(Btree *p){
** Release the BtShared mutex associated with B-Tree handle p and
** clear the p->locked boolean.
*/
-static void unlockBtreeMutex(Btree *p){
+static void SQLITE_NOINLINE unlockBtreeMutex(Btree *p){
BtShared *pBt = p->pBt;
assert( p->locked==1 );
assert( sqlite3_mutex_held(pBt->mutex) );
@@ -48019,6 +57992,9 @@ static void unlockBtreeMutex(Btree *p){
p->locked = 0;
}
+/* Forward reference */
+static void SQLITE_NOINLINE btreeLockCarefully(Btree *p);
+
/*
** Enter a mutex on the given BTree object.
**
@@ -48036,8 +58012,6 @@ static void unlockBtreeMutex(Btree *p){
** subsequent Btrees that desire a lock.
*/
SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){
- Btree *pLater;
-
/* Some basic sanity checking on the Btree. The list of Btrees
** connected by pNext and pPrev should be in sorted order by
** Btree.pBt value. All elements of the list should belong to
@@ -48062,9 +58036,20 @@ SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){
if( !p->sharable ) return;
p->wantToLock++;
if( p->locked ) return;
+ btreeLockCarefully(p);
+}
+
+/* This is a helper function for sqlite3BtreeLock(). By moving
+** complex, but seldom used logic, out of sqlite3BtreeLock() and
+** into this routine, we avoid unnecessary stack pointer changes
+** and thus help the sqlite3BtreeLock() routine to run much faster
+** in the common case.
+*/
+static void SQLITE_NOINLINE btreeLockCarefully(Btree *p){
+ Btree *pLater;
/* In most cases, we should be able to acquire the lock we
- ** want without having to go throught the ascending lock
+ ** want without having to go through the ascending lock
** procedure that follows. Just be sure not to block.
*/
if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){
@@ -48094,10 +58079,12 @@ SQLITE_PRIVATE void sqlite3BtreeEnter(Btree *p){
}
}
+
/*
** Exit the recursive mutex on a Btree.
*/
SQLITE_PRIVATE void sqlite3BtreeLeave(Btree *p){
+ assert( sqlite3_mutex_held(p->db->mutex) );
if( p->sharable ){
assert( p->wantToLock>0 );
p->wantToLock--;
@@ -48125,21 +58112,6 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){
#endif
-#ifndef SQLITE_OMIT_INCRBLOB
-/*
-** Enter and leave a mutex on a Btree given a cursor owned by that
-** Btree. These entry points are used by incremental I/O and can be
-** omitted if that module is not used.
-*/
-SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){
- sqlite3BtreeEnter(pCur->pBtree);
-}
-SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){
- sqlite3BtreeLeave(pCur->pBtree);
-}
-#endif /* SQLITE_OMIT_INCRBLOB */
-
-
/*
** Enter the mutex on every Btree associated with a database
** connection. This is needed (for example) prior to parsing
@@ -48173,14 +58145,6 @@ SQLITE_PRIVATE void sqlite3BtreeLeaveAll(sqlite3 *db){
}
}
-/*
-** Return true if a particular Btree requires a lock. Return FALSE if
-** no lock is ever required since it is not sharable.
-*/
-SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){
- return p->sharable;
-}
-
#ifndef NDEBUG
/*
** Return true if the current thread holds the database connection
@@ -48254,6 +58218,25 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){
}
}
#endif /* if SQLITE_THREADSAFE */
+
+#ifndef SQLITE_OMIT_INCRBLOB
+/*
+** Enter a mutex on a Btree given a cursor owned by that Btree.
+**
+** These entry points are used by incremental I/O only. Enter() is required
+** any time OMIT_SHARED_CACHE is not defined, regardless of whether or not
+** the build is threadsafe. Leave() is only required by threadsafe builds.
+*/
+SQLITE_PRIVATE void sqlite3BtreeEnterCursor(BtCursor *pCur){
+ sqlite3BtreeEnter(pCur->pBtree);
+}
+# if SQLITE_THREADSAFE
+SQLITE_PRIVATE void sqlite3BtreeLeaveCursor(BtCursor *pCur){
+ sqlite3BtreeLeave(pCur->pBtree);
+}
+# endif
+#endif /* ifndef SQLITE_OMIT_INCRBLOB */
+
#endif /* ifndef SQLITE_OMIT_SHARED_CACHE */
/************** End of btmutex.c *********************************************/
@@ -48269,10 +58252,11 @@ SQLITE_PRIVATE void sqlite3BtreeEnterAll(sqlite3 *db){
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file implements a external (disk-based) database using BTrees.
+** This file implements an external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
+/* #include "btreeInt.h" */
/*
** The header string that appears at the beginning of every
@@ -48302,6 +58286,25 @@ int sqlite3BtreeTrace=1; /* True to enable tracing */
*/
#define get2byteNotZero(X) (((((int)get2byte(X))-1)&0xffff)+1)
+/*
+** Values passed as the 5th argument to allocateBtreePage()
+*/
+#define BTALLOC_ANY 0 /* Allocate any page */
+#define BTALLOC_EXACT 1 /* Allocate exact page if possible */
+#define BTALLOC_LE 2 /* Allocate any page <= the parameter */
+
+/*
+** Macro IfNotOmitAV(x) returns (x) if SQLITE_OMIT_AUTOVACUUM is not
+** defined, or 0 if it is. For example:
+**
+** bIncrVacuum = IfNotOmitAV(pBtShared->incrVacuum);
+*/
+#ifndef SQLITE_OMIT_AUTOVACUUM
+#define IfNotOmitAV(expr) (expr)
+#else
+#define IfNotOmitAV(expr) 0
+#endif
+
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
** A list of BtShared objects that are eligible for participation
@@ -48402,7 +58405,7 @@ static int hasSharedCacheTableLock(
** the correct locks are held. So do not bother - just return true.
** This case does not come up very often anyhow.
*/
- if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){
+ if( isIndex && (!pSchema || (pSchema->schemaFlags&DB_SchemaLoaded)==0) ){
return 1;
}
@@ -48415,6 +58418,12 @@ static int hasSharedCacheTableLock(
for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){
Index *pIdx = (Index *)sqliteHashData(p);
if( pIdx->tnum==(int)iRoot ){
+ if( iTab ){
+ /* Two or more indexes share the same root page. There must
+ ** be imposter tables. So just return true. The assert is not
+ ** useful in that case. */
+ return 1;
+ }
iTab = pIdx->pTable->tnum;
}
}
@@ -48502,7 +58511,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
/* If some other connection is holding an exclusive lock, the
** requested lock may not be obtained.
*/
- if( pBt->pWriter!=p && pBt->isExclusive ){
+ if( pBt->pWriter!=p && (pBt->btsFlags & BTS_EXCLUSIVE)!=0 ){
sqlite3ConnectionBlocked(p->db, pBt->pWriter->db);
return SQLITE_LOCKED_SHAREDCACHE;
}
@@ -48523,7 +58532,7 @@ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){
sqlite3ConnectionBlocked(p->db, pIter->pBtree->db);
if( eLock==WRITE_LOCK ){
assert( p==pBt->pWriter );
- pBt->isPending = 1;
+ pBt->btsFlags |= BTS_PENDING;
}
return SQLITE_LOCKED_SHAREDCACHE;
}
@@ -48584,7 +58593,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
if( !pLock ){
pLock = (BtLock *)sqlite3MallocZero(sizeof(BtLock));
if( !pLock ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pLock->iTable = iTable;
pLock->pBtree = p;
@@ -48611,7 +58620,7 @@ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){
** the setSharedCacheTableLock() procedure) held by Btree object p.
**
** This function assumes that Btree p has an open read or write
-** transaction. If it does not, then the BtShared.isPending variable
+** transaction. If it does not, then the BTS_PENDING flag
** may be incorrectly cleared.
*/
static void clearAllSharedCacheTableLocks(Btree *p){
@@ -48624,7 +58633,7 @@ static void clearAllSharedCacheTableLocks(Btree *p){
while( *ppIter ){
BtLock *pLock = *ppIter;
- assert( pBt->isExclusive==0 || pBt->pWriter==pLock->pBtree );
+ assert( (pBt->btsFlags & BTS_EXCLUSIVE)==0 || pBt->pWriter==pLock->pBtree );
assert( pLock->pBtree->inTrans>=pLock->eLock );
if( pLock->pBtree==p ){
*ppIter = pLock->pNext;
@@ -48637,22 +58646,21 @@ static void clearAllSharedCacheTableLocks(Btree *p){
}
}
- assert( pBt->isPending==0 || pBt->pWriter );
+ assert( (pBt->btsFlags & BTS_PENDING)==0 || pBt->pWriter );
if( pBt->pWriter==p ){
pBt->pWriter = 0;
- pBt->isExclusive = 0;
- pBt->isPending = 0;
+ pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);
}else if( pBt->nTransaction==2 ){
/* This function is called when Btree p is concluding its
** transaction. If there currently exists a writer, and p is not
** that writer, then the number of locks held by connections other
** than the writer must be about to drop to zero. In this case
- ** set the isPending flag to 0.
+ ** set the BTS_PENDING flag to 0.
**
- ** If there is not currently a writer, then BtShared.isPending must
+ ** If there is not currently a writer, then BTS_PENDING must
** be zero already. So this next line is harmless in that case.
*/
- pBt->isPending = 0;
+ pBt->btsFlags &= ~BTS_PENDING;
}
}
@@ -48664,8 +58672,7 @@ static void downgradeAllSharedCacheTableLocks(Btree *p){
if( pBt->pWriter==p ){
BtLock *pLock;
pBt->pWriter = 0;
- pBt->isExclusive = 0;
- pBt->isPending = 0;
+ pBt->btsFlags &= ~(BTS_EXCLUSIVE|BTS_PENDING);
for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){
assert( pLock->eLock==READ_LOCK || pLock->pBtree==p );
pLock->eLock = READ_LOCK;
@@ -48686,18 +58693,26 @@ static void releasePage(MemPage *pPage); /* Forward reference */
static int cursorHoldsMutex(BtCursor *p){
return sqlite3_mutex_held(p->pBt->mutex);
}
-#endif
+/* Verify that the cursor and the BtShared agree about what is the current
+** database connetion. This is important in shared-cache mode. If the database
+** connection pointers get out-of-sync, it is possible for routines like
+** btreeInitPage() to reference an stale connection pointer that references a
+** a connection that has already closed. This routine is used inside assert()
+** statements only and for the purpose of double-checking that the btree code
+** does keep the database connection pointers up-to-date.
+*/
+static int cursorOwnsBtShared(BtCursor *p){
+ assert( cursorHoldsMutex(p) );
+ return (p->pBtree->db==p->pBt->db);
+}
+#endif
-#ifndef SQLITE_OMIT_INCRBLOB
/*
-** Invalidate the overflow page-list cache for cursor pCur, if any.
+** Invalidate the overflow cache of the cursor passed as the first argument.
+** on the shared btree structure pBt.
*/
-static void invalidateOverflowCache(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
- sqlite3_free(pCur->aOverflow);
- pCur->aOverflow = 0;
-}
+#define invalidateOverflowCache(pCur) (pCur->curFlags &= ~BTCF_ValidOvfl)
/*
** Invalidate the overflow page-list cache for all cursors opened
@@ -48711,6 +58726,7 @@ static void invalidateAllOverflowCache(BtShared *pBt){
}
}
+#ifndef SQLITE_OMIT_INCRBLOB
/*
** This function is called before modifying the contents of a table
** to invalidate any incrblob cursors that are open on the
@@ -48730,19 +58746,21 @@ static void invalidateIncrblobCursors(
int isClearTable /* True if all rows are being deleted */
){
BtCursor *p;
- BtShared *pBt = pBtree->pBt;
+ if( pBtree->hasIncrblobCur==0 ) return;
assert( sqlite3BtreeHoldsMutex(pBtree) );
- for(p=pBt->pCursor; p; p=p->pNext){
- if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){
- p->eState = CURSOR_INVALID;
+ pBtree->hasIncrblobCur = 0;
+ for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+ if( (p->curFlags & BTCF_Incrblob)!=0 ){
+ pBtree->hasIncrblobCur = 1;
+ if( isClearTable || p->info.nKey==iRow ){
+ p->eState = CURSOR_INVALID;
+ }
}
}
}
#else
- /* Stub functions when INCRBLOB is omitted */
- #define invalidateOverflowCache(x)
- #define invalidateAllOverflowCache(x)
+ /* Stub function when INCRBLOB is omitted */
#define invalidateIncrblobCursors(x,y,z)
#endif /* SQLITE_OMIT_INCRBLOB */
@@ -48787,7 +58805,7 @@ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){
assert( pgno<=pBt->nPage );
pBt->pHasContent = sqlite3BitvecCreate(pBt->nPage);
if( !pBt->pHasContent ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){
@@ -48818,30 +58836,44 @@ static void btreeClearHasContent(BtShared *pBt){
}
/*
-** Save the current cursor position in the variables BtCursor.nKey
-** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
-**
-** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID)
-** prior to calling this routine.
+** Release all of the apPage[] pages for a cursor.
*/
-static int saveCursorPosition(BtCursor *pCur){
- int rc;
+static void btreeReleaseAllCursorPages(BtCursor *pCur){
+ int i;
+ for(i=0; i<=pCur->iPage; i++){
+ releasePage(pCur->apPage[i]);
+ pCur->apPage[i] = 0;
+ }
+ pCur->iPage = -1;
+}
+/*
+** The cursor passed as the only argument must point to a valid entry
+** when this function is called (i.e. have eState==CURSOR_VALID). This
+** function saves the current cursor key in variables pCur->nKey and
+** pCur->pKey. SQLITE_OK is returned if successful or an SQLite error
+** code otherwise.
+**
+** If the cursor is open on an intkey table, then the integer key
+** (the rowid) is stored in pCur->nKey and pCur->pKey is left set to
+** NULL. If the cursor is open on a non-intkey table, then pCur->pKey is
+** set to point to a malloced buffer pCur->nKey bytes in size containing
+** the key.
+*/
+static int saveCursorKey(BtCursor *pCur){
+ int rc = SQLITE_OK;
assert( CURSOR_VALID==pCur->eState );
assert( 0==pCur->pKey );
assert( cursorHoldsMutex(pCur) );
- rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
- assert( rc==SQLITE_OK ); /* KeySize() cannot fail */
-
- /* If this is an intKey table, then the above call to BtreeKeySize()
- ** stores the integer key in pCur->nKey. In this case this value is
- ** all that is required. Otherwise, if pCur is not open on an intKey
- ** table, then malloc space for and store the pCur->nKey bytes of key
- ** data.
- */
- if( 0==pCur->apPage[0]->intKey ){
- void *pKey = sqlite3Malloc( (int)pCur->nKey );
+ if( pCur->curIntKey ){
+ /* Only the rowid is required for a table btree */
+ pCur->nKey = sqlite3BtreeIntegerKey(pCur);
+ }else{
+ /* For an index btree, save the complete key content */
+ void *pKey;
+ pCur->nKey = sqlite3BtreePayloadSize(pCur);
+ pKey = sqlite3Malloc( pCur->nKey );
if( pKey ){
rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey);
if( rc==SQLITE_OK ){
@@ -48850,43 +58882,103 @@ static int saveCursorPosition(BtCursor *pCur){
sqlite3_free(pKey);
}
}else{
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
- assert( !pCur->apPage[0]->intKey || !pCur->pKey );
+ assert( !pCur->curIntKey || !pCur->pKey );
+ return rc;
+}
+
+/*
+** Save the current cursor position in the variables BtCursor.nKey
+** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK.
+**
+** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID)
+** prior to calling this routine.
+*/
+static int saveCursorPosition(BtCursor *pCur){
+ int rc;
+
+ assert( CURSOR_VALID==pCur->eState || CURSOR_SKIPNEXT==pCur->eState );
+ assert( 0==pCur->pKey );
+ assert( cursorHoldsMutex(pCur) );
+
+ if( pCur->eState==CURSOR_SKIPNEXT ){
+ pCur->eState = CURSOR_VALID;
+ }else{
+ pCur->skipNext = 0;
+ }
+ rc = saveCursorKey(pCur);
if( rc==SQLITE_OK ){
- int i;
- for(i=0; i<=pCur->iPage; i++){
- releasePage(pCur->apPage[i]);
- pCur->apPage[i] = 0;
- }
- pCur->iPage = -1;
+ btreeReleaseAllCursorPages(pCur);
pCur->eState = CURSOR_REQUIRESEEK;
}
- invalidateOverflowCache(pCur);
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl|BTCF_AtLast);
return rc;
}
+/* Forward reference */
+static int SQLITE_NOINLINE saveCursorsOnList(BtCursor*,Pgno,BtCursor*);
+
/*
** Save the positions of all cursors (except pExcept) that are open on
-** the table with root-page iRoot. Usually, this is called just before cursor
-** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()).
+** the table with root-page iRoot. "Saving the cursor position" means that
+** the location in the btree is remembered in such a way that it can be
+** moved back to the same spot after the btree has been modified. This
+** routine is called just before cursor pExcept is used to modify the
+** table, for example in BtreeDelete() or BtreeInsert().
+**
+** If there are two or more cursors on the same btree, then all such
+** cursors should have their BTCF_Multiple flag set. The btreeCursor()
+** routine enforces that rule. This routine only needs to be called in
+** the uncommon case when pExpect has the BTCF_Multiple flag set.
+**
+** If pExpect!=NULL and if no other cursors are found on the same root-page,
+** then the BTCF_Multiple flag on pExpect is cleared, to avoid another
+** pointless call to this routine.
+**
+** Implementation note: This routine merely checks to see if any cursors
+** need to be saved. It calls out to saveCursorsOnList() in the (unusual)
+** event that cursors are in need to being saved.
*/
static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
BtCursor *p;
assert( sqlite3_mutex_held(pBt->mutex) );
assert( pExcept==0 || pExcept->pBt==pBt );
for(p=pBt->pCursor; p; p=p->pNext){
- if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
- p->eState==CURSOR_VALID ){
- int rc = saveCursorPosition(p);
- if( SQLITE_OK!=rc ){
- return rc;
+ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ) break;
+ }
+ if( p ) return saveCursorsOnList(p, iRoot, pExcept);
+ if( pExcept ) pExcept->curFlags &= ~BTCF_Multiple;
+ return SQLITE_OK;
+}
+
+/* This helper routine to saveAllCursors does the actual work of saving
+** the cursors if and when a cursor is found that actually requires saving.
+** The common case is that no cursors need to be saved, so this routine is
+** broken out from its caller to avoid unnecessary stack pointer movement.
+*/
+static int SQLITE_NOINLINE saveCursorsOnList(
+ BtCursor *p, /* The first cursor that needs saving */
+ Pgno iRoot, /* Only save cursor with this iRoot. Save all if zero */
+ BtCursor *pExcept /* Do not save this cursor */
+){
+ do{
+ if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) ){
+ if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){
+ int rc = saveCursorPosition(p);
+ if( SQLITE_OK!=rc ){
+ return rc;
+ }
+ }else{
+ testcase( p->iPage>0 );
+ btreeReleaseAllCursorPages(p);
}
}
- }
+ p = p->pNext;
+ }while( p );
return SQLITE_OK;
}
@@ -48914,7 +59006,7 @@ static int btreeMoveto(
){
int rc; /* Status code */
UnpackedRecord *pIdxKey; /* Unpacked index key */
- char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */
+ char aSpace[384]; /* Temp space for pIdxKey - to avoid a malloc */
char *pFree = 0;
if( pKey ){
@@ -48922,8 +59014,12 @@ static int btreeMoveto(
pIdxKey = sqlite3VdbeAllocUnpackedRecord(
pCur->pKeyInfo, aSpace, sizeof(aSpace), &pFree
);
- if( pIdxKey==0 ) return SQLITE_NOMEM;
+ if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT;
sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey);
+ if( pIdxKey->nField==0 ){
+ sqlite3DbFree(pCur->pKeyInfo->db, pFree);
+ return SQLITE_CORRUPT_BKPT;
+ }
}else{
pIdxKey = 0;
}
@@ -48943,17 +59039,22 @@ static int btreeMoveto(
*/
static int btreeRestoreCursorPosition(BtCursor *pCur){
int rc;
- assert( cursorHoldsMutex(pCur) );
+ int skipNext;
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState>=CURSOR_REQUIRESEEK );
if( pCur->eState==CURSOR_FAULT ){
return pCur->skipNext;
}
pCur->eState = CURSOR_INVALID;
- rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skipNext);
+ rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &skipNext);
if( rc==SQLITE_OK ){
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_INVALID );
+ pCur->skipNext |= skipNext;
+ if( pCur->skipNext && pCur->eState==CURSOR_VALID ){
+ pCur->eState = CURSOR_SKIPNEXT;
+ }
}
return rc;
}
@@ -48964,29 +59065,73 @@ static int btreeRestoreCursorPosition(BtCursor *pCur){
SQLITE_OK)
/*
-** Determine whether or not a cursor has moved from the position it
-** was last placed at. Cursors can move when the row they are pointing
-** at is deleted out from under them.
+** Determine whether or not a cursor has moved from the position where
+** it was last placed, or has been invalidated for any other reason.
+** Cursors can move when the row they are pointing at is deleted out
+** from under them, for example. Cursor might also move if a btree
+** is rebalanced.
+**
+** Calling this routine with a NULL cursor pointer returns false.
+**
+** Use the separate sqlite3BtreeCursorRestore() routine to restore a cursor
+** back to where it ought to be if this routine returns true.
+*/
+SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur){
+ return pCur->eState!=CURSOR_VALID;
+}
+
+/*
+** This routine restores a cursor back to its original position after it
+** has been moved by some outside activity (such as a btree rebalance or
+** a row having been deleted out from under the cursor).
**
-** This routine returns an error code if something goes wrong. The
-** integer *pHasMoved is set to one if the cursor has moved and 0 if not.
+** On success, the *pDifferentRow parameter is false if the cursor is left
+** pointing at exactly the same row. *pDifferntRow is the row the cursor
+** was pointing to has been deleted, forcing the cursor to point to some
+** nearby row.
+**
+** This routine should only be called for a cursor that just returned
+** TRUE from sqlite3BtreeCursorHasMoved().
*/
-SQLITE_PRIVATE int sqlite3BtreeCursorHasMoved(BtCursor *pCur, int *pHasMoved){
+SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor *pCur, int *pDifferentRow){
int rc;
+ assert( pCur!=0 );
+ assert( pCur->eState!=CURSOR_VALID );
rc = restoreCursorPosition(pCur);
if( rc ){
- *pHasMoved = 1;
+ *pDifferentRow = 1;
return rc;
}
- if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){
- *pHasMoved = 1;
+ if( pCur->eState!=CURSOR_VALID ){
+ *pDifferentRow = 1;
}else{
- *pHasMoved = 0;
+ assert( pCur->skipNext==0 );
+ *pDifferentRow = 0;
}
return SQLITE_OK;
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Provide hints to the cursor. The particular hint given (and the type
+** and number of the varargs parameters) is determined by the eHintType
+** parameter. See the definitions of the BTREE_HINT_* macros for details.
+*/
+SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){
+ /* Used only by system that substitute their own storage engine */
+}
+#endif
+
+/*
+** Provide flag hints to the cursor.
+*/
+SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
+ assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
+ pCur->hints = x;
+}
+
+
#ifndef SQLITE_OMIT_AUTOVACUUM
/*
** Given a page number of a regular database page, return the page
@@ -49040,7 +59185,7 @@ static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){
return;
}
iPtrmap = PTRMAP_PAGENO(pBt, key);
- rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
+ rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0);
if( rc!=SQLITE_OK ){
*pRC = rc;
return;
@@ -49083,7 +59228,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
assert( sqlite3_mutex_held(pBt->mutex) );
iPtrmap = PTRMAP_PAGENO(pBt, key);
- rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
+ rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage, 0);
if( rc!=0 ){
return rc;
}
@@ -49115,129 +59260,214 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
** the page, 1 means the second cell, and so forth) return a pointer
** to the cell content.
**
+** findCellPastPtr() does the same except it skips past the initial
+** 4-byte child pointer found on interior pages, if there is one.
+**
** This routine works only for pages that do not contain overflow cells.
*/
#define findCell(P,I) \
- ((P)->aData + ((P)->maskPage & get2byte(&(P)->aData[(P)->cellOffset+2*(I)])))
-#define findCellv2(D,M,O,I) (D+(M&get2byte(D+(O+2*(I)))))
+ ((P)->aData + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)])))
+#define findCellPastPtr(P,I) \
+ ((P)->aDataOfst + ((P)->maskPage & get2byteAligned(&(P)->aCellIdx[2*(I)])))
/*
-** This a more complex version of findCell() that works for
-** pages that do contain overflow cells.
+** This is common tail processing for btreeParseCellPtr() and
+** btreeParseCellPtrIndex() for the case when the cell does not fit entirely
+** on a single B-tree page. Make necessary adjustments to the CellInfo
+** structure.
*/
-static u8 *findOverflowCell(MemPage *pPage, int iCell){
- int i;
- assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- for(i=pPage->nOverflow-1; i>=0; i--){
- int k;
- struct _OvflCell *pOvfl;
- pOvfl = &pPage->aOvfl[i];
- k = pOvfl->idx;
- if( k<=iCell ){
- if( k==iCell ){
- return pOvfl->pCell;
- }
- iCell--;
- }
+static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow(
+ MemPage *pPage, /* Page containing the cell */
+ u8 *pCell, /* Pointer to the cell text. */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ /* If the payload will not fit completely on the local page, we have
+ ** to decide how much to store locally and how much to spill onto
+ ** overflow pages. The strategy is to minimize the amount of unused
+ ** space on overflow pages while keeping the amount of local storage
+ ** in between minLocal and maxLocal.
+ **
+ ** Warning: changing the way overflow payload is distributed in any
+ ** way will result in an incompatible file format.
+ */
+ int minLocal; /* Minimum amount of payload held locally */
+ int maxLocal; /* Maximum amount of payload held locally */
+ int surplus; /* Overflow payload available for local storage */
+
+ minLocal = pPage->minLocal;
+ maxLocal = pPage->maxLocal;
+ surplus = minLocal + (pInfo->nPayload - minLocal)%(pPage->pBt->usableSize-4);
+ testcase( surplus==maxLocal );
+ testcase( surplus==maxLocal+1 );
+ if( surplus <= maxLocal ){
+ pInfo->nLocal = (u16)surplus;
+ }else{
+ pInfo->nLocal = (u16)minLocal;
}
- return findCell(pPage, iCell);
+ pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4;
}
/*
-** Parse a cell content block and fill in the CellInfo structure. There
-** are two versions of this function. btreeParseCell() takes a
-** cell index as the second argument and btreeParseCellPtr()
-** takes a pointer to the body of the cell as its second argument.
+** The following routines are implementations of the MemPage.xParseCell()
+** method.
+**
+** Parse a cell content block and fill in the CellInfo structure.
+**
+** btreeParseCellPtr() => table btree leaf nodes
+** btreeParseCellNoPayload() => table btree internal nodes
+** btreeParseCellPtrIndex() => index btree nodes
**
-** Within this file, the parseCell() macro can be called instead of
-** btreeParseCellPtr(). Using some compilers, this will be faster.
+** There is also a wrapper function btreeParseCell() that works for
+** all MemPage types and that references the cell by index rather than
+** by pointer.
*/
+static void btreeParseCellPtrNoPayload(
+ MemPage *pPage, /* Page containing the cell */
+ u8 *pCell, /* Pointer to the cell text. */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->leaf==0 );
+ assert( pPage->childPtrSize==4 );
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER(pPage);
+#endif
+ pInfo->nSize = 4 + getVarint(&pCell[4], (u64*)&pInfo->nKey);
+ pInfo->nPayload = 0;
+ pInfo->nLocal = 0;
+ pInfo->pPayload = 0;
+ return;
+}
static void btreeParseCellPtr(
MemPage *pPage, /* Page containing the cell */
u8 *pCell, /* Pointer to the cell text. */
CellInfo *pInfo /* Fill in this structure */
){
- u16 n; /* Number bytes in cell content header */
+ u8 *pIter; /* For scanning through pCell */
u32 nPayload; /* Number of bytes of cell payload */
+ u64 iKey; /* Extracted Key value */
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
-
- pInfo->pCell = pCell;
assert( pPage->leaf==0 || pPage->leaf==1 );
- n = pPage->childPtrSize;
- assert( n==4-4*pPage->leaf );
- if( pPage->intKey ){
- if( pPage->hasData ){
- n += getVarint32(&pCell[n], nPayload);
- }else{
- nPayload = 0;
+ assert( pPage->intKeyLeaf );
+ assert( pPage->childPtrSize==0 );
+ pIter = pCell;
+
+ /* The next block of code is equivalent to:
+ **
+ ** pIter += getVarint32(pIter, nPayload);
+ **
+ ** The code is inlined to avoid a function call.
+ */
+ nPayload = *pIter;
+ if( nPayload>=0x80 ){
+ u8 *pEnd = &pIter[8];
+ nPayload &= 0x7f;
+ do{
+ nPayload = (nPayload<<7) | (*++pIter & 0x7f);
+ }while( (*pIter)>=0x80 && pIter<pEnd );
+ }
+ pIter++;
+
+ /* The next block of code is equivalent to:
+ **
+ ** pIter += getVarint(pIter, (u64*)&pInfo->nKey);
+ **
+ ** The code is inlined to avoid a function call.
+ */
+ iKey = *pIter;
+ if( iKey>=0x80 ){
+ u8 *pEnd = &pIter[7];
+ iKey &= 0x7f;
+ while(1){
+ iKey = (iKey<<7) | (*++pIter & 0x7f);
+ if( (*pIter)<0x80 ) break;
+ if( pIter>=pEnd ){
+ iKey = (iKey<<8) | *++pIter;
+ break;
+ }
}
- n += getVarint(&pCell[n], (u64*)&pInfo->nKey);
- pInfo->nData = nPayload;
- }else{
- pInfo->nData = 0;
- n += getVarint32(&pCell[n], nPayload);
- pInfo->nKey = nPayload;
}
+ pIter++;
+
+ pInfo->nKey = *(i64*)&iKey;
pInfo->nPayload = nPayload;
- pInfo->nHeader = n;
+ pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
testcase( nPayload==pPage->maxLocal+1 );
- if( likely(nPayload<=pPage->maxLocal) ){
+ if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
- if( (pInfo->nSize = (u16)(n+nPayload))<4 ) pInfo->nSize = 4;
+ pInfo->nSize = nPayload + (u16)(pIter - pCell);
+ if( pInfo->nSize<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
- pInfo->iOverflow = 0;
}else{
- /* If the payload will not fit completely on the local page, we have
- ** to decide how much to store locally and how much to spill onto
- ** overflow pages. The strategy is to minimize the amount of unused
- ** space on overflow pages while keeping the amount of local storage
- ** in between minLocal and maxLocal.
- **
- ** Warning: changing the way overflow payload is distributed in any
- ** way will result in an incompatible file format.
- */
- int minLocal; /* Minimum amount of payload held locally */
- int maxLocal; /* Maximum amount of payload held locally */
- int surplus; /* Overflow payload available for local storage */
+ btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
+ }
+}
+static void btreeParseCellPtrIndex(
+ MemPage *pPage, /* Page containing the cell */
+ u8 *pCell, /* Pointer to the cell text. */
+ CellInfo *pInfo /* Fill in this structure */
+){
+ u8 *pIter; /* For scanning through pCell */
+ u32 nPayload; /* Number of bytes of cell payload */
- minLocal = pPage->minLocal;
- maxLocal = pPage->maxLocal;
- surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4);
- testcase( surplus==maxLocal );
- testcase( surplus==maxLocal+1 );
- if( surplus <= maxLocal ){
- pInfo->nLocal = (u16)surplus;
- }else{
- pInfo->nLocal = (u16)minLocal;
- }
- pInfo->iOverflow = (u16)(pInfo->nLocal + n);
- pInfo->nSize = pInfo->iOverflow + 4;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ assert( pPage->leaf==0 || pPage->leaf==1 );
+ assert( pPage->intKeyLeaf==0 );
+ pIter = pCell + pPage->childPtrSize;
+ nPayload = *pIter;
+ if( nPayload>=0x80 ){
+ u8 *pEnd = &pIter[8];
+ nPayload &= 0x7f;
+ do{
+ nPayload = (nPayload<<7) | (*++pIter & 0x7f);
+ }while( *(pIter)>=0x80 && pIter<pEnd );
+ }
+ pIter++;
+ pInfo->nKey = nPayload;
+ pInfo->nPayload = nPayload;
+ pInfo->pPayload = pIter;
+ testcase( nPayload==pPage->maxLocal );
+ testcase( nPayload==pPage->maxLocal+1 );
+ if( nPayload<=pPage->maxLocal ){
+ /* This is the (easy) common case where the entire payload fits
+ ** on the local page. No overflow is required.
+ */
+ pInfo->nSize = nPayload + (u16)(pIter - pCell);
+ if( pInfo->nSize<4 ) pInfo->nSize = 4;
+ pInfo->nLocal = (u16)nPayload;
+ }else{
+ btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
}
}
-#define parseCell(pPage, iCell, pInfo) \
- btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo))
static void btreeParseCell(
MemPage *pPage, /* Page containing the cell */
int iCell, /* The cell index. First cell is 0 */
CellInfo *pInfo /* Fill in this structure */
){
- parseCell(pPage, iCell, pInfo);
+ pPage->xParseCell(pPage, findCell(pPage, iCell), pInfo);
}
/*
+** The following routines are implementations of the MemPage.xCellSize
+** method.
+**
** Compute the total number of bytes that a Cell needs in the cell
** data area of the btree-page. The return number includes the cell
** data header and the local payload, but not any overflow page or
** the space used by the cell pointer.
+**
+** cellSizePtrNoPayload() => table internal nodes
+** cellSizePtr() => all index nodes & table leaf nodes
*/
static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
- u8 *pIter = &pCell[pPage->childPtrSize];
- u32 nSize;
+ u8 *pIter = pCell + pPage->childPtrSize; /* For looping over bytes of pCell */
+ u8 *pEnd; /* End mark for a varint */
+ u32 nSize; /* Size value to return */
#ifdef SQLITE_DEBUG
/* The value returned by this function should always be the same as
@@ -49245,29 +59475,31 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
** this function verifies that this invariant is not violated. */
CellInfo debuginfo;
- btreeParseCellPtr(pPage, pCell, &debuginfo);
+ pPage->xParseCell(pPage, pCell, &debuginfo);
#endif
+ nSize = *pIter;
+ if( nSize>=0x80 ){
+ pEnd = &pIter[8];
+ nSize &= 0x7f;
+ do{
+ nSize = (nSize<<7) | (*++pIter & 0x7f);
+ }while( *(pIter)>=0x80 && pIter<pEnd );
+ }
+ pIter++;
if( pPage->intKey ){
- u8 *pEnd;
- if( pPage->hasData ){
- pIter += getVarint32(pIter, nSize);
- }else{
- nSize = 0;
- }
-
/* pIter now points at the 64-bit integer key value, a variable length
** integer. The following block moves pIter to point at the first byte
** past the end of the key value. */
pEnd = &pIter[9];
while( (*pIter++)&0x80 && pIter<pEnd );
- }else{
- pIter += getVarint32(pIter, nSize);
}
-
testcase( nSize==pPage->maxLocal );
testcase( nSize==pPage->maxLocal+1 );
- if( nSize>pPage->maxLocal ){
+ if( nSize<=pPage->maxLocal ){
+ nSize += (u32)(pIter - pCell);
+ if( nSize<4 ) nSize = 4;
+ }else{
int minLocal = pPage->minLocal;
nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4);
testcase( nSize==pPage->maxLocal );
@@ -49275,24 +59507,39 @@ static u16 cellSizePtr(MemPage *pPage, u8 *pCell){
if( nSize>pPage->maxLocal ){
nSize = minLocal;
}
- nSize += 4;
+ nSize += 4 + (u16)(pIter - pCell);
}
- nSize += (u32)(pIter - pCell);
+ assert( nSize==debuginfo.nSize || CORRUPT_DB );
+ return (u16)nSize;
+}
+static u16 cellSizePtrNoPayload(MemPage *pPage, u8 *pCell){
+ u8 *pIter = pCell + 4; /* For looping over bytes of pCell */
+ u8 *pEnd; /* End mark for a varint */
- /* The minimum size of any cell is 4 bytes. */
- if( nSize<4 ){
- nSize = 4;
- }
+#ifdef SQLITE_DEBUG
+ /* The value returned by this function should always be the same as
+ ** the (CellInfo.nSize) value found by doing a full parse of the
+ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of
+ ** this function verifies that this invariant is not violated. */
+ CellInfo debuginfo;
+ pPage->xParseCell(pPage, pCell, &debuginfo);
+#else
+ UNUSED_PARAMETER(pPage);
+#endif
- assert( nSize==debuginfo.nSize );
- return (u16)nSize;
+ assert( pPage->childPtrSize==4 );
+ pEnd = pIter + 9;
+ while( (*pIter++)&0x80 && pIter<pEnd );
+ assert( debuginfo.nSize==(u16)(pIter - pCell) || CORRUPT_DB );
+ return (u16)(pIter - pCell);
}
+
#ifdef SQLITE_DEBUG
/* This variation on cellSizePtr() is used inside of assert() statements
** only. */
static u16 cellSize(MemPage *pPage, int iCell){
- return cellSizePtr(pPage, findCell(pPage, iCell));
+ return pPage->xCellSize(pPage, findCell(pPage, iCell));
}
#endif
@@ -49306,10 +59553,9 @@ static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
CellInfo info;
if( *pRC ) return;
assert( pCell!=0 );
- btreeParseCellPtr(pPage, pCell, &info);
- assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload );
- if( info.iOverflow ){
- Pgno ovfl = get4byte(&pCell[info.iOverflow]);
+ pPage->xParseCell(pPage, pCell, &info);
+ if( info.nLocal<info.nPayload ){
+ Pgno ovfl = get4byte(&pCell[info.nSize-4]);
ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC);
}
}
@@ -49321,10 +59567,15 @@ static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){
** end of the page and all free space is collected into one
** big FreeBlk that occurs in between the header and cell
** pointer array and the cell content area.
+**
+** EVIDENCE-OF: R-44582-60138 SQLite may from time to time reorganize a
+** b-tree page so that there are no freeblocks or fragment bytes, all
+** unused bytes are contained in the unallocated space region, and all
+** cells are packed tightly at the end of the page.
*/
static int defragmentPage(MemPage *pPage){
int i; /* Loop counter */
- int pc; /* Address of a i-th cell */
+ int pc; /* Address of the i-th cell */
int hdr; /* Offset to the page header */
int size; /* Size of a cell */
int usableSize; /* Number of usable bytes on a page */
@@ -49333,6 +59584,7 @@ static int defragmentPage(MemPage *pPage){
int nCell; /* Number of cells on the page */
unsigned char *data; /* The page data */
unsigned char *temp; /* Temp area for cell content */
+ unsigned char *src; /* Source of content */
int iCellFirst; /* First allowable cell index */
int iCellLast; /* Last possible cell index */
@@ -49342,15 +59594,13 @@ static int defragmentPage(MemPage *pPage){
assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
assert( pPage->nOverflow==0 );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- temp = sqlite3PagerTempSpace(pPage->pBt->pPager);
- data = pPage->aData;
+ temp = 0;
+ src = data = pPage->aData;
hdr = pPage->hdrOffset;
cellOffset = pPage->cellOffset;
nCell = pPage->nCell;
assert( nCell==get2byte(&data[hdr+3]) );
usableSize = pPage->pBt->usableSize;
- cbrk = get2byte(&data[hdr+5]);
- memcpy(&temp[cbrk], &data[cbrk], usableSize - cbrk);
cbrk = usableSize;
iCellFirst = cellOffset + 2*nCell;
iCellLast = usableSize - 4;
@@ -49360,31 +59610,31 @@ static int defragmentPage(MemPage *pPage){
pc = get2byte(pAddr);
testcase( pc==iCellFirst );
testcase( pc==iCellLast );
-#if !defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
/* These conditions have already been verified in btreeInitPage()
- ** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined
+ ** if PRAGMA cell_size_check=ON.
*/
if( pc<iCellFirst || pc>iCellLast ){
return SQLITE_CORRUPT_BKPT;
}
-#endif
assert( pc>=iCellFirst && pc<=iCellLast );
- size = cellSizePtr(pPage, &temp[pc]);
+ size = pPage->xCellSize(pPage, &src[pc]);
cbrk -= size;
-#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
- if( cbrk<iCellFirst ){
- return SQLITE_CORRUPT_BKPT;
- }
-#else
if( cbrk<iCellFirst || pc+size>usableSize ){
return SQLITE_CORRUPT_BKPT;
}
-#endif
assert( cbrk+size<=usableSize && cbrk>=iCellFirst );
testcase( cbrk+size==usableSize );
testcase( pc+size==usableSize );
- memcpy(&data[cbrk], &temp[pc], size);
put2byte(pAddr, cbrk);
+ if( temp==0 ){
+ int x;
+ if( cbrk==pc ) continue;
+ temp = sqlite3PagerTempSpace(pPage->pBt->pPager);
+ x = get2byte(&data[hdr+5]);
+ memcpy(&temp[x], &data[x], (cbrk+size) - x);
+ src = temp;
+ }
+ memcpy(&data[cbrk], &src[pc], size);
}
assert( cbrk>=iCellFirst );
put2byte(&data[hdr+5], cbrk);
@@ -49400,6 +59650,70 @@ static int defragmentPage(MemPage *pPage){
}
/*
+** Search the free-list on page pPg for space to store a cell nByte bytes in
+** size. If one can be found, return a pointer to the space and remove it
+** from the free-list.
+**
+** If no suitable space can be found on the free-list, return NULL.
+**
+** This function may detect corruption within pPg. If corruption is
+** detected then *pRc is set to SQLITE_CORRUPT and NULL is returned.
+**
+** Slots on the free list that are between 1 and 3 bytes larger than nByte
+** will be ignored if adding the extra space to the fragmentation count
+** causes the fragmentation count to exceed 60.
+*/
+static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){
+ const int hdr = pPg->hdrOffset;
+ u8 * const aData = pPg->aData;
+ int iAddr = hdr + 1;
+ int pc = get2byte(&aData[iAddr]);
+ int x;
+ int usableSize = pPg->pBt->usableSize;
+
+ assert( pc>0 );
+ do{
+ int size; /* Size of the free slot */
+ /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
+ ** increasing offset. */
+ if( pc>usableSize-4 || pc<iAddr+4 ){
+ *pRc = SQLITE_CORRUPT_BKPT;
+ return 0;
+ }
+ /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each
+ ** freeblock form a big-endian integer which is the size of the freeblock
+ ** in bytes, including the 4-byte header. */
+ size = get2byte(&aData[pc+2]);
+ if( (x = size - nByte)>=0 ){
+ testcase( x==4 );
+ testcase( x==3 );
+ if( pc < pPg->cellOffset+2*pPg->nCell || size+pc > usableSize ){
+ *pRc = SQLITE_CORRUPT_BKPT;
+ return 0;
+ }else if( x<4 ){
+ /* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total
+ ** number of bytes in fragments may not exceed 60. */
+ if( aData[hdr+7]>57 ) return 0;
+
+ /* Remove the slot from the free-list. Update the number of
+ ** fragmented bytes within the page. */
+ memcpy(&aData[iAddr], &aData[pc], 2);
+ aData[hdr+7] += (u8)x;
+ }else{
+ /* The slot remains on the free-list. Reduce its size to account
+ ** for the portion used by the new allocation. */
+ put2byte(&aData[pc+2], x);
+ }
+ return &aData[pc + x];
+ }
+ iAddr = pc;
+ pc = get2byte(&aData[pc]);
+ }while( pc );
+
+ return 0;
+}
+
+/*
** Allocate nByte bytes of space from within the B-Tree page passed
** as the first argument. Write into *pIdx the index into pPage->aData[]
** of the first byte of allocated space. Return either SQLITE_OK or
@@ -49415,11 +59729,9 @@ static int defragmentPage(MemPage *pPage){
static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */
u8 * const data = pPage->aData; /* Local cache of pPage->aData */
- int nFrag; /* Number of fragmented bytes on pPage */
int top; /* First byte of cell content area */
+ int rc = SQLITE_OK; /* Integer return code */
int gap; /* First byte of gap between cell pointers and cell content */
- int rc; /* Integer return code */
- int usableSize; /* Usable size of the page */
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( pPage->pBt );
@@ -49427,62 +59739,50 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
assert( nByte>=0 ); /* Minimum cell size is 4 */
assert( pPage->nFree>=nByte );
assert( pPage->nOverflow==0 );
- usableSize = pPage->pBt->usableSize;
- assert( nByte < usableSize-8 );
+ assert( nByte < (int)(pPage->pBt->usableSize-8) );
- nFrag = data[hdr+7];
assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf );
gap = pPage->cellOffset + 2*pPage->nCell;
- top = get2byteNotZero(&data[hdr+5]);
- if( gap>top ) return SQLITE_CORRUPT_BKPT;
+ assert( gap<=65536 );
+ /* EVIDENCE-OF: R-29356-02391 If the database uses a 65536-byte page size
+ ** and the reserved space is zero (the usual value for reserved space)
+ ** then the cell content offset of an empty page wants to be 65536.
+ ** However, that integer is too large to be stored in a 2-byte unsigned
+ ** integer, so a value of 0 is used in its place. */
+ top = get2byte(&data[hdr+5]);
+ assert( top<=(int)pPage->pBt->usableSize ); /* Prevent by getAndInitPage() */
+ if( gap>top ){
+ if( top==0 && pPage->pBt->usableSize==65536 ){
+ top = 65536;
+ }else{
+ return SQLITE_CORRUPT_BKPT;
+ }
+ }
+
+ /* If there is enough space between gap and top for one more cell pointer
+ ** array entry offset, and if the freelist is not empty, then search the
+ ** freelist looking for a free slot big enough to satisfy the request.
+ */
testcase( gap+2==top );
testcase( gap+1==top );
testcase( gap==top );
-
- if( nFrag>=60 ){
- /* Always defragment highly fragmented pages */
- rc = defragmentPage(pPage);
- if( rc ) return rc;
- top = get2byteNotZero(&data[hdr+5]);
- }else if( gap+2<=top ){
- /* Search the freelist looking for a free slot big enough to satisfy
- ** the request. The allocation is made from the first free slot in
- ** the list that is large enough to accomadate it.
- */
- int pc, addr;
- for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){
- int size; /* Size of the free slot */
- if( pc>usableSize-4 || pc<addr+4 ){
- return SQLITE_CORRUPT_BKPT;
- }
- size = get2byte(&data[pc+2]);
- if( size>=nByte ){
- int x = size - nByte;
- testcase( x==4 );
- testcase( x==3 );
- if( x<4 ){
- /* Remove the slot from the free-list. Update the number of
- ** fragmented bytes within the page. */
- memcpy(&data[addr], &data[pc], 2);
- data[hdr+7] = (u8)(nFrag + x);
- }else if( size+pc > usableSize ){
- return SQLITE_CORRUPT_BKPT;
- }else{
- /* The slot remains on the free-list. Reduce its size to account
- ** for the portion used by the new allocation. */
- put2byte(&data[pc+2], x);
- }
- *pIdx = pc + x;
- return SQLITE_OK;
- }
+ if( (data[hdr+2] || data[hdr+1]) && gap+2<=top ){
+ u8 *pSpace = pageFindSlot(pPage, nByte, &rc);
+ if( pSpace ){
+ assert( pSpace>=data && (pSpace - data)<65536 );
+ *pIdx = (int)(pSpace - data);
+ return SQLITE_OK;
+ }else if( rc ){
+ return rc;
}
}
- /* Check to make sure there is enough space in the gap to satisfy
- ** the allocation. If not, defragment.
+ /* The request could not be fulfilled using a freelist slot. Check
+ ** to see if defragmentation is necessary.
*/
testcase( gap+2+nByte==top );
if( gap+2+nByte>top ){
+ assert( pPage->nCell>0 || CORRUPT_DB );
rc = defragmentPage(pPage);
if( rc ) return rc;
top = get2byteNotZero(&data[hdr+5]);
@@ -49505,90 +59805,104 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
/*
** Return a section of the pPage->aData to the freelist.
-** The first byte of the new free block is pPage->aDisk[start]
-** and the size of the block is "size" bytes.
-**
-** Most of the effort here is involved in coalesing adjacent
-** free blocks into a single big free block.
-*/
-static int freeSpace(MemPage *pPage, int start, int size){
- int addr, pbegin, hdr;
- int iLast; /* Largest possible freeblock offset */
- unsigned char *data = pPage->aData;
+** The first byte of the new free block is pPage->aData[iStart]
+** and the size of the block is iSize bytes.
+**
+** Adjacent freeblocks are coalesced.
+**
+** Note that even though the freeblock list was checked by btreeInitPage(),
+** that routine will not detect overlap between cells or freeblocks. Nor
+** does it detect cells or freeblocks that encrouch into the reserved bytes
+** at the end of the page. So do additional corruption checks inside this
+** routine and return SQLITE_CORRUPT if any problems are found.
+*/
+static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
+ u16 iPtr; /* Address of ptr to next freeblock */
+ u16 iFreeBlk; /* Address of the next freeblock */
+ u8 hdr; /* Page header size. 0 or 100 */
+ u8 nFrag = 0; /* Reduction in fragmentation */
+ u16 iOrigSize = iSize; /* Original value of iSize */
+ u32 iLast = pPage->pBt->usableSize-4; /* Largest possible freeblock offset */
+ u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */
+ unsigned char *data = pPage->aData; /* Page content */
assert( pPage->pBt!=0 );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
- assert( start>=pPage->hdrOffset+6+pPage->childPtrSize );
- assert( (start + size) <= (int)pPage->pBt->usableSize );
+ assert( CORRUPT_DB || iStart>=pPage->hdrOffset+6+pPage->childPtrSize );
+ assert( CORRUPT_DB || iEnd <= pPage->pBt->usableSize );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- assert( size>=0 ); /* Minimum cell size is 4 */
+ assert( iSize>=4 ); /* Minimum cell size is 4 */
+ assert( iStart<=iLast );
- if( pPage->pBt->secureDelete ){
- /* Overwrite deleted information with zeros when the secure_delete
- ** option is enabled */
- memset(&data[start], 0, size);
+ /* Overwrite deleted information with zeros when the secure_delete
+ ** option is enabled */
+ if( pPage->pBt->btsFlags & BTS_SECURE_DELETE ){
+ memset(&data[iStart], 0, iSize);
}
- /* Add the space back into the linked list of freeblocks. Note that
- ** even though the freeblock list was checked by btreeInitPage(),
- ** btreeInitPage() did not detect overlapping cells or
- ** freeblocks that overlapped cells. Nor does it detect when the
- ** cell content area exceeds the value in the page header. If these
- ** situations arise, then subsequent insert operations might corrupt
- ** the freelist. So we do need to check for corruption while scanning
- ** the freelist.
+ /* The list of freeblocks must be in ascending order. Find the
+ ** spot on the list where iStart should be inserted.
*/
hdr = pPage->hdrOffset;
- addr = hdr + 1;
- iLast = pPage->pBt->usableSize - 4;
- assert( start<=iLast );
- while( (pbegin = get2byte(&data[addr]))<start && pbegin>0 ){
- if( pbegin<addr+4 ){
- return SQLITE_CORRUPT_BKPT;
- }
- addr = pbegin;
- }
- if( pbegin>iLast ){
- return SQLITE_CORRUPT_BKPT;
- }
- assert( pbegin>addr || pbegin==0 );
- put2byte(&data[addr], start);
- put2byte(&data[start], pbegin);
- put2byte(&data[start+2], size);
- pPage->nFree = pPage->nFree + (u16)size;
-
- /* Coalesce adjacent free blocks */
- addr = hdr + 1;
- while( (pbegin = get2byte(&data[addr]))>0 ){
- int pnext, psize, x;
- assert( pbegin>addr );
- assert( pbegin <= (int)pPage->pBt->usableSize-4 );
- pnext = get2byte(&data[pbegin]);
- psize = get2byte(&data[pbegin+2]);
- if( pbegin + psize + 3 >= pnext && pnext>0 ){
- int frag = pnext - (pbegin+psize);
- if( (frag<0) || (frag>(int)data[hdr+7]) ){
+ iPtr = hdr + 1;
+ if( data[iPtr+1]==0 && data[iPtr]==0 ){
+ iFreeBlk = 0; /* Shortcut for the case when the freelist is empty */
+ }else{
+ while( (iFreeBlk = get2byte(&data[iPtr]))<iStart ){
+ if( iFreeBlk<iPtr+4 ){
+ if( iFreeBlk==0 ) break;
return SQLITE_CORRUPT_BKPT;
}
- data[hdr+7] -= (u8)frag;
- x = get2byte(&data[pnext]);
- put2byte(&data[pbegin], x);
- x = pnext + get2byte(&data[pnext+2]) - pbegin;
- put2byte(&data[pbegin+2], x);
- }else{
- addr = pbegin;
+ iPtr = iFreeBlk;
}
+ if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT;
+ assert( iFreeBlk>iPtr || iFreeBlk==0 );
+
+ /* At this point:
+ ** iFreeBlk: First freeblock after iStart, or zero if none
+ ** iPtr: The address of a pointer to iFreeBlk
+ **
+ ** Check to see if iFreeBlk should be coalesced onto the end of iStart.
+ */
+ if( iFreeBlk && iEnd+3>=iFreeBlk ){
+ nFrag = iFreeBlk - iEnd;
+ if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_BKPT;
+ iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
+ if( iEnd > pPage->pBt->usableSize ) return SQLITE_CORRUPT_BKPT;
+ iSize = iEnd - iStart;
+ iFreeBlk = get2byte(&data[iFreeBlk]);
+ }
+
+ /* If iPtr is another freeblock (that is, if iPtr is not the freelist
+ ** pointer in the page header) then check to see if iStart should be
+ ** coalesced onto the end of iPtr.
+ */
+ if( iPtr>hdr+1 ){
+ int iPtrEnd = iPtr + get2byte(&data[iPtr+2]);
+ if( iPtrEnd+3>=iStart ){
+ if( iPtrEnd>iStart ) return SQLITE_CORRUPT_BKPT;
+ nFrag += iStart - iPtrEnd;
+ iSize = iEnd - iPtr;
+ iStart = iPtr;
+ }
+ }
+ if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_BKPT;
+ data[hdr+7] -= nFrag;
+ }
+ if( iStart==get2byte(&data[hdr+5]) ){
+ /* The new freeblock is at the beginning of the cell content area,
+ ** so just extend the cell content area rather than create another
+ ** freelist entry */
+ if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_BKPT;
+ put2byte(&data[hdr+1], iFreeBlk);
+ put2byte(&data[hdr+5], iEnd);
+ }else{
+ /* Insert the new freeblock into the freelist */
+ put2byte(&data[iPtr], iStart);
+ put2byte(&data[iStart], iFreeBlk);
+ put2byte(&data[iStart+2], iSize);
}
-
- /* If the cell content area begins with a freeblock, remove it. */
- if( data[hdr+1]==data[hdr+5] && data[hdr+2]==data[hdr+6] ){
- int top;
- pbegin = get2byte(&data[hdr+1]);
- memcpy(&data[hdr+1], &data[pbegin], 2);
- top = get2byte(&data[hdr+5]) + get2byte(&data[pbegin+2]);
- put2byte(&data[hdr+5], top);
- }
- assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ pPage->nFree += iOrigSize;
return SQLITE_OK;
}
@@ -49612,20 +59926,44 @@ static int decodeFlags(MemPage *pPage, int flagByte){
pPage->leaf = (u8)(flagByte>>3); assert( PTF_LEAF == 1<<3 );
flagByte &= ~PTF_LEAF;
pPage->childPtrSize = 4-4*pPage->leaf;
+ pPage->xCellSize = cellSizePtr;
pBt = pPage->pBt;
if( flagByte==(PTF_LEAFDATA | PTF_INTKEY) ){
+ /* EVIDENCE-OF: R-07291-35328 A value of 5 (0x05) means the page is an
+ ** interior table b-tree page. */
+ assert( (PTF_LEAFDATA|PTF_INTKEY)==5 );
+ /* EVIDENCE-OF: R-26900-09176 A value of 13 (0x0d) means the page is a
+ ** leaf table b-tree page. */
+ assert( (PTF_LEAFDATA|PTF_INTKEY|PTF_LEAF)==13 );
pPage->intKey = 1;
- pPage->hasData = pPage->leaf;
+ if( pPage->leaf ){
+ pPage->intKeyLeaf = 1;
+ pPage->xParseCell = btreeParseCellPtr;
+ }else{
+ pPage->intKeyLeaf = 0;
+ pPage->xCellSize = cellSizePtrNoPayload;
+ pPage->xParseCell = btreeParseCellPtrNoPayload;
+ }
pPage->maxLocal = pBt->maxLeaf;
pPage->minLocal = pBt->minLeaf;
}else if( flagByte==PTF_ZERODATA ){
+ /* EVIDENCE-OF: R-43316-37308 A value of 2 (0x02) means the page is an
+ ** interior index b-tree page. */
+ assert( (PTF_ZERODATA)==2 );
+ /* EVIDENCE-OF: R-59615-42828 A value of 10 (0x0a) means the page is a
+ ** leaf index b-tree page. */
+ assert( (PTF_ZERODATA|PTF_LEAF)==10 );
pPage->intKey = 0;
- pPage->hasData = 0;
+ pPage->intKeyLeaf = 0;
+ pPage->xParseCell = btreeParseCellPtrIndex;
pPage->maxLocal = pBt->maxLocal;
pPage->minLocal = pBt->minLocal;
}else{
+ /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is
+ ** an error. */
return SQLITE_CORRUPT_BKPT;
}
+ pPage->max1bytePayload = pBt->max1bytePayload;
return SQLITE_OK;
}
@@ -49641,6 +59979,7 @@ static int decodeFlags(MemPage *pPage, int flagByte){
static int btreeInitPage(MemPage *pPage){
assert( pPage->pBt!=0 );
+ assert( pPage->pBt->db!=0 );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
@@ -49662,19 +60001,34 @@ static int btreeInitPage(MemPage *pPage){
hdr = pPage->hdrOffset;
data = pPage->aData;
+ /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating
+ ** the b-tree page type. */
if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT;
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
pPage->nOverflow = 0;
usableSize = pBt->usableSize;
- pPage->cellOffset = cellOffset = hdr + 12 - 4*pPage->leaf;
+ pPage->cellOffset = cellOffset = hdr + 8 + pPage->childPtrSize;
+ pPage->aDataEnd = &data[usableSize];
+ pPage->aCellIdx = &data[cellOffset];
+ pPage->aDataOfst = &data[pPage->childPtrSize];
+ /* EVIDENCE-OF: R-58015-48175 The two-byte integer at offset 5 designates
+ ** the start of the cell content area. A zero value for this integer is
+ ** interpreted as 65536. */
top = get2byteNotZero(&data[hdr+5]);
+ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
+ ** number of cells on the page. */
pPage->nCell = get2byte(&data[hdr+3]);
if( pPage->nCell>MX_CELL(pBt) ){
/* To many cells for a single page. The page must be corrupt */
return SQLITE_CORRUPT_BKPT;
}
testcase( pPage->nCell==MX_CELL(pBt) );
+ /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only
+ ** possible for a root page of a table that contains no rows) then the
+ ** offset to the cell content area will equal the page size minus the
+ ** bytes of reserved space. */
+ assert( pPage->nCell>0 || top==usableSize || CORRUPT_DB );
/* A malformed database page might cause us to read past the end
** of page when parsing a cell.
@@ -49685,20 +60039,19 @@ static int btreeInitPage(MemPage *pPage){
*/
iCellFirst = cellOffset + 2*pPage->nCell;
iCellLast = usableSize - 4;
-#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
- {
+ if( pBt->db->flags & SQLITE_CellSizeCk ){
int i; /* Index into the cell pointer array */
int sz; /* Size of a cell */
if( !pPage->leaf ) iCellLast--;
for(i=0; i<pPage->nCell; i++){
- pc = get2byte(&data[cellOffset+i*2]);
+ pc = get2byteAligned(&data[cellOffset+i*2]);
testcase( pc==iCellFirst );
testcase( pc==iCellLast );
if( pc<iCellFirst || pc>iCellLast ){
return SQLITE_CORRUPT_BKPT;
}
- sz = cellSizePtr(pPage, &data[pc]);
+ sz = pPage->xCellSize(pPage, &data[pc]);
testcase( pc+sz==usableSize );
if( pc+sz>usableSize ){
return SQLITE_CORRUPT_BKPT;
@@ -49706,22 +60059,28 @@ static int btreeInitPage(MemPage *pPage){
}
if( !pPage->leaf ) iCellLast++;
}
-#endif
- /* Compute the total free space on the page */
+ /* Compute the total free space on the page
+ ** EVIDENCE-OF: R-23588-34450 The two-byte integer at offset 1 gives the
+ ** start of the first freeblock on the page, or is zero if there are no
+ ** freeblocks. */
pc = get2byte(&data[hdr+1]);
- nFree = data[hdr+7] + top;
+ nFree = data[hdr+7] + top; /* Init nFree to non-freeblock free space */
while( pc>0 ){
u16 next, size;
if( pc<iCellFirst || pc>iCellLast ){
- /* Start of free block is off the page */
+ /* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will
+ ** always be at least one cell before the first freeblock.
+ **
+ ** Or, the freeblock is off the end of the page
+ */
return SQLITE_CORRUPT_BKPT;
}
next = get2byte(&data[pc]);
size = get2byte(&data[pc+2]);
if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){
/* Free blocks must be in ascending order. And the last byte of
- ** the free-block must lie on the database page. */
+ ** the free-block must lie on the database page. */
return SQLITE_CORRUPT_BKPT;
}
nFree = nFree + size;
@@ -49759,18 +60118,20 @@ static void zeroPage(MemPage *pPage, int flags){
assert( sqlite3PagerGetData(pPage->pDbPage) == data );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pBt->mutex) );
- if( pBt->secureDelete ){
+ if( pBt->btsFlags & BTS_SECURE_DELETE ){
memset(&data[hdr], 0, pBt->usableSize - hdr);
}
data[hdr] = (char)flags;
- first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0);
+ first = hdr + ((flags&PTF_LEAF)==0 ? 12 : 8);
memset(&data[hdr+1], 0, 4);
data[hdr+7] = 0;
put2byte(&data[hdr+5], pBt->usableSize);
pPage->nFree = (u16)(pBt->usableSize - first);
decodeFlags(pPage, flags);
- pPage->hdrOffset = hdr;
pPage->cellOffset = first;
+ pPage->aDataEnd = &data[pBt->usableSize];
+ pPage->aCellIdx = &data[first];
+ pPage->aDataOfst = &data[pPage->childPtrSize];
pPage->nOverflow = 0;
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
@@ -49785,20 +60146,23 @@ static void zeroPage(MemPage *pPage, int flags){
*/
static MemPage *btreePageFromDbPage(DbPage *pDbPage, Pgno pgno, BtShared *pBt){
MemPage *pPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
- pPage->aData = sqlite3PagerGetData(pDbPage);
- pPage->pDbPage = pDbPage;
- pPage->pBt = pBt;
- pPage->pgno = pgno;
- pPage->hdrOffset = pPage->pgno==1 ? 100 : 0;
+ if( pgno!=pPage->pgno ){
+ pPage->aData = sqlite3PagerGetData(pDbPage);
+ pPage->pDbPage = pDbPage;
+ pPage->pBt = pBt;
+ pPage->pgno = pgno;
+ pPage->hdrOffset = pgno==1 ? 100 : 0;
+ }
+ assert( pPage->aData==sqlite3PagerGetData(pDbPage) );
return pPage;
}
/*
** Get a page from the pager. Initialize the MemPage.pBt and
-** MemPage.aData elements if needed.
+** MemPage.aData elements if needed. See also: btreeGetUnusedPage().
**
-** If the noContent flag is set, it means that we do not care about
-** the content of the page at this time. So do not go to the disk
+** If the PAGER_GET_NOCONTENT flag is set, it means that we do not care
+** about the content of the page at this time. So do not go to the disk
** to fetch the content. Just fill in the content with zeros for now.
** If in the future we call sqlite3PagerWrite() on this page, that
** means we have started to be concerned about content and the disk
@@ -49808,13 +60172,14 @@ static int btreeGetPage(
BtShared *pBt, /* The btree */
Pgno pgno, /* Number of the page to fetch */
MemPage **ppPage, /* Return the page in this parameter */
- int noContent /* Do not load page content if true */
+ int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */
){
int rc;
DbPage *pDbPage;
+ assert( flags==0 || flags==PAGER_GET_NOCONTENT || flags==PAGER_GET_READONLY );
assert( sqlite3_mutex_held(pBt->mutex) );
- rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent);
+ rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, flags);
if( rc ) return rc;
*ppPage = btreePageFromDbPage(pDbPage, pgno, pBt);
return SQLITE_OK;
@@ -49845,37 +60210,67 @@ static Pgno btreePagecount(BtShared *pBt){
SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree *p){
assert( sqlite3BtreeHoldsMutex(p) );
assert( ((p->pBt->nPage)&0x8000000)==0 );
- return (int)btreePagecount(p->pBt);
+ return btreePagecount(p->pBt);
}
/*
-** Get a page from the pager and initialize it. This routine is just a
-** convenience wrapper around separate calls to btreeGetPage() and
-** btreeInitPage().
+** Get a page from the pager and initialize it.
+**
+** If pCur!=0 then the page is being fetched as part of a moveToChild()
+** call. Do additional sanity checking on the page in this case.
+** And if the fetch fails, this routine must decrement pCur->iPage.
+**
+** The page is fetched as read-write unless pCur is not NULL and is
+** a read-only cursor.
**
-** If an error occurs, then the value *ppPage is set to is undefined. It
+** If an error occurs, then *ppPage is undefined. It
** may remain unchanged, or it may be set to an invalid value.
*/
static int getAndInitPage(
- BtShared *pBt, /* The database file */
- Pgno pgno, /* Number of the page to get */
- MemPage **ppPage /* Write the page pointer here */
+ BtShared *pBt, /* The database file */
+ Pgno pgno, /* Number of the page to get */
+ MemPage **ppPage, /* Write the page pointer here */
+ BtCursor *pCur, /* Cursor to receive the page, or NULL */
+ int bReadOnly /* True for a read-only page */
){
int rc;
+ DbPage *pDbPage;
assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( pCur==0 || ppPage==&pCur->apPage[pCur->iPage] );
+ assert( pCur==0 || bReadOnly==pCur->curPagerFlags );
+ assert( pCur==0 || pCur->iPage>0 );
if( pgno>btreePagecount(pBt) ){
rc = SQLITE_CORRUPT_BKPT;
- }else{
- rc = btreeGetPage(pBt, pgno, ppPage, 0);
- if( rc==SQLITE_OK ){
- rc = btreeInitPage(*ppPage);
- if( rc!=SQLITE_OK ){
- releasePage(*ppPage);
- }
+ goto getAndInitPage_error;
+ }
+ rc = sqlite3PagerGet(pBt->pPager, pgno, (DbPage**)&pDbPage, bReadOnly);
+ if( rc ){
+ goto getAndInitPage_error;
+ }
+ *ppPage = (MemPage*)sqlite3PagerGetExtra(pDbPage);
+ if( (*ppPage)->isInit==0 ){
+ btreePageFromDbPage(pDbPage, pgno, pBt);
+ rc = btreeInitPage(*ppPage);
+ if( rc!=SQLITE_OK ){
+ releasePage(*ppPage);
+ goto getAndInitPage_error;
}
}
+ assert( (*ppPage)->pgno==pgno );
+ assert( (*ppPage)->aData==sqlite3PagerGetData(pDbPage) );
+
+ /* If obtaining a child page for a cursor, we must verify that the page is
+ ** compatible with the root page. */
+ if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ releasePage(*ppPage);
+ goto getAndInitPage_error;
+ }
+ return SQLITE_OK;
+getAndInitPage_error:
+ if( pCur ) pCur->iPage--;
testcase( pgno==0 );
assert( pgno!=0 || rc==SQLITE_CORRUPT );
return rc;
@@ -49885,17 +60280,49 @@ static int getAndInitPage(
** Release a MemPage. This should be called once for each prior
** call to btreeGetPage.
*/
+static void releasePageNotNull(MemPage *pPage){
+ assert( pPage->aData );
+ assert( pPage->pBt );
+ assert( pPage->pDbPage!=0 );
+ assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
+ assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ sqlite3PagerUnrefNotNull(pPage->pDbPage);
+}
static void releasePage(MemPage *pPage){
- if( pPage ){
- assert( pPage->aData );
- assert( pPage->pBt );
- assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
- assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
- assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- sqlite3PagerUnref(pPage->pDbPage);
+ if( pPage ) releasePageNotNull(pPage);
+}
+
+/*
+** Get an unused page.
+**
+** This works just like btreeGetPage() with the addition:
+**
+** * If the page is already in use for some other purpose, immediately
+** release it and return an SQLITE_CURRUPT error.
+** * Make sure the isInit flag is clear
+*/
+static int btreeGetUnusedPage(
+ BtShared *pBt, /* The btree */
+ Pgno pgno, /* Number of the page to fetch */
+ MemPage **ppPage, /* Return the page in this parameter */
+ int flags /* PAGER_GET_NOCONTENT or PAGER_GET_READONLY */
+){
+ int rc = btreeGetPage(pBt, pgno, ppPage, flags);
+ if( rc==SQLITE_OK ){
+ if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
+ releasePage(*ppPage);
+ *ppPage = 0;
+ return SQLITE_CORRUPT_BKPT;
+ }
+ (*ppPage)->isInit = 0;
+ }else{
+ *ppPage = 0;
}
+ return rc;
}
+
/*
** During a rollback, when the pager reloads information into the cache
** so that the cache is restored to its original state at the start of
@@ -49945,11 +60372,8 @@ static int btreeInvokeBusyHandler(void *pArg){
** If zFilename is ":memory:" then an in-memory database is created
** that is automatically destroyed when it is closed.
**
-** The "flags" parameter is a bitmask that might contain bits
-** BTREE_OMIT_JOURNAL and/or BTREE_NO_READLOCK. The BTREE_NO_READLOCK
-** bit is also set if the SQLITE_NoReadlock flags is set in db->flags.
-** These flags are passed through into sqlite3PagerOpen() and must
-** be the same values as PAGER_OMIT_JOURNAL and PAGER_NO_READLOCK.
+** The "flags" parameter is a bitmask that might contain bits like
+** BTREE_OMIT_JOURNAL and/or BTREE_MEMORY.
**
** If the database is already opened in the same database connection
** and we are in shared cache mode, then the open will fail with an
@@ -49982,7 +60406,8 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
const int isMemdb = 0;
#else
const int isMemdb = (zFilename && strcmp(zFilename, ":memory:")==0)
- || (isTempDb && sqlite3TempInMemory(db));
+ || (isTempDb && sqlite3TempInMemory(db))
+ || (vfsFlags & SQLITE_OPEN_MEMORY)!=0;
#endif
assert( db!=0 );
@@ -49996,9 +60421,6 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
/* A BTREE_SINGLE database is always a temporary and/or ephemeral */
assert( (flags & BTREE_SINGLE)==0 || isTempDb );
- if( db->flags & SQLITE_NoReadlock ){
- flags |= BTREE_NO_READLOCK;
- }
if( isMemdb ){
flags |= BTREE_MEMORY;
}
@@ -50007,7 +60429,7 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
}
p = sqlite3MallocZero(sizeof(Btree));
if( !p ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
p->inTrans = TRANS_NONE;
p->db = db;
@@ -50021,24 +60443,38 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
** If this Btree is a candidate for shared cache, try to find an
** existing BtShared object that we can share with
*/
- if( isMemdb==0 && isTempDb==0 ){
+ if( isTempDb==0 && (isMemdb==0 || (vfsFlags&SQLITE_OPEN_URI)!=0) ){
if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){
+ int nFilename = sqlite3Strlen30(zFilename)+1;
int nFullPathname = pVfs->mxPathname+1;
- char *zFullPathname = sqlite3Malloc(nFullPathname);
- sqlite3_mutex *mutexShared;
+ char *zFullPathname = sqlite3Malloc(MAX(nFullPathname,nFilename));
+ MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
+
p->sharable = 1;
if( !zFullPathname ){
sqlite3_free(p);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
+ }
+ if( isMemdb ){
+ memcpy(zFullPathname, zFilename, nFilename);
+ }else{
+ rc = sqlite3OsFullPathname(pVfs, zFilename,
+ nFullPathname, zFullPathname);
+ if( rc ){
+ sqlite3_free(zFullPathname);
+ sqlite3_free(p);
+ return rc;
+ }
}
- sqlite3OsFullPathname(pVfs, zFilename, nFullPathname, zFullPathname);
+#if SQLITE_THREADSAFE
mutexOpen = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_OPEN);
sqlite3_mutex_enter(mutexOpen);
mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(mutexShared);
+#endif
for(pBt=GLOBAL(BtShared*,sqlite3SharedCacheList); pBt; pBt=pBt->pNext){
assert( pBt->nRef>0 );
- if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager))
+ if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager, 0))
&& sqlite3PagerVfs(pBt->pPager)==pVfs ){
int iDb;
for(iDb=db->nDb-1; iDb>=0; iDb--){
@@ -50077,20 +60513,21 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
** the right size. This is to guard against size changes that result
** when compiling on a different architecture.
*/
- assert( sizeof(i64)==8 || sizeof(i64)==4 );
- assert( sizeof(u64)==8 || sizeof(u64)==4 );
+ assert( sizeof(i64)==8 );
+ assert( sizeof(u64)==8 );
assert( sizeof(u32)==4 );
assert( sizeof(u16)==2 );
assert( sizeof(Pgno)==4 );
pBt = sqlite3MallocZero( sizeof(*pBt) );
if( pBt==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto btree_open_out;
}
rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename,
EXTRA_SIZE, flags, vfsFlags, pageReinit);
if( rc==SQLITE_OK ){
+ sqlite3PagerSetMmapLimit(pBt->pPager, db->szMmap);
rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader);
}
if( rc!=SQLITE_OK ){
@@ -50103,10 +60540,13 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
pBt->pCursor = 0;
pBt->pPage1 = 0;
- pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager);
+ if( sqlite3PagerIsreadonly(pBt->pPager) ) pBt->btsFlags |= BTS_READ_ONLY;
#ifdef SQLITE_SECURE_DELETE
- pBt->secureDelete = 1;
+ pBt->btsFlags |= BTS_SECURE_DELETE;
#endif
+ /* EVIDENCE-OF: R-51873-39618 The page size for a database file is
+ ** determined by the 2-byte integer located at an offset of 16 bytes from
+ ** the beginning of the database file. */
pBt->pageSize = (zDbHeader[16]<<8) | (zDbHeader[17]<<16);
if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE
|| ((pBt->pageSize-1)&pBt->pageSize)!=0 ){
@@ -50125,8 +60565,11 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
#endif
nReserve = 0;
}else{
+ /* EVIDENCE-OF: R-37497-42412 The size of the reserved region is
+ ** determined by the one-byte unsigned integer found at an offset of 20
+ ** into the database file header. */
nReserve = zDbHeader[20];
- pBt->pageSizeFixed = 1;
+ pBt->btsFlags |= BTS_PAGESIZE_FIXED;
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0);
pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0);
@@ -50140,15 +60583,14 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
/* Add the new BtShared object to the linked list sharable BtShareds.
*/
+ pBt->nRef = 1;
if( p->sharable ){
- sqlite3_mutex *mutexShared;
- pBt->nRef = 1;
- mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ MUTEX_LOGIC( sqlite3_mutex *mutexShared; )
+ MUTEX_LOGIC( mutexShared = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);)
if( SQLITE_THREADSAFE && sqlite3GlobalConfig.bCoreMutex ){
pBt->mutex = sqlite3MutexAlloc(SQLITE_MUTEX_FAST);
if( pBt->mutex==0 ){
- rc = SQLITE_NOMEM;
- db->mallocFailed = 0;
+ rc = SQLITE_NOMEM_BKPT;
goto btree_open_out;
}
}
@@ -50171,12 +60613,12 @@ SQLITE_PRIVATE int sqlite3BtreeOpen(
for(i=0; i<db->nDb; i++){
if( (pSib = db->aDb[i].pBt)!=0 && pSib->sharable ){
while( pSib->pPrev ){ pSib = pSib->pPrev; }
- if( p->pBt<pSib->pBt ){
+ if( (uptr)p->pBt<(uptr)pSib->pBt ){
p->pNext = pSib;
p->pPrev = 0;
pSib->pPrev = p;
}else{
- while( pSib->pNext && pSib->pNext->pBt<p->pBt ){
+ while( pSib->pNext && (uptr)pSib->pNext->pBt<(uptr)p->pBt ){
pSib = pSib->pNext;
}
p->pNext = pSib->pNext;
@@ -50214,6 +60656,7 @@ btree_open_out:
assert( sqlite3_mutex_held(mutexOpen) );
sqlite3_mutex_leave(mutexOpen);
}
+ assert( rc!=SQLITE_OK || sqlite3BtreeConnectionCount(*ppBtree)>0 );
return rc;
}
@@ -50225,12 +60668,12 @@ btree_open_out:
*/
static int removeFromSharingList(BtShared *pBt){
#ifndef SQLITE_OMIT_SHARED_CACHE
- sqlite3_mutex *pMaster;
+ MUTEX_LOGIC( sqlite3_mutex *pMaster; )
BtShared *pList;
int removed = 0;
assert( sqlite3_mutex_notheld(pBt->mutex) );
- pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
sqlite3_mutex_enter(pMaster);
pBt->nRef--;
if( pBt->nRef<=0 ){
@@ -50259,11 +60702,32 @@ static int removeFromSharingList(BtShared *pBt){
/*
** Make sure pBt->pTmpSpace points to an allocation of
-** MX_CELL_SIZE(pBt) bytes.
+** MX_CELL_SIZE(pBt) bytes with a 4-byte prefix for a left-child
+** pointer.
*/
static void allocateTempSpace(BtShared *pBt){
if( !pBt->pTmpSpace ){
pBt->pTmpSpace = sqlite3PageMalloc( pBt->pageSize );
+
+ /* One of the uses of pBt->pTmpSpace is to format cells before
+ ** inserting them into a leaf page (function fillInCell()). If
+ ** a cell is less than 4 bytes in size, it is rounded up to 4 bytes
+ ** by the various routines that manipulate binary cells. Which
+ ** can mean that fillInCell() only initializes the first 2 or 3
+ ** bytes of pTmpSpace, but that the first 4 bytes are copied from
+ ** it into a database page. This is not actually a problem, but it
+ ** does cause a valgrind error when the 1 or 2 bytes of unitialized
+ ** data is passed to system call write(). So to avoid this error,
+ ** zero the first 4 bytes of temp space here.
+ **
+ ** Also: Provide four bytes of initialized space before the
+ ** beginning of pTmpSpace as an area available to prepend the
+ ** left-child pointer to the beginning of a cell.
+ */
+ if( pBt->pTmpSpace ){
+ memset(pBt->pTmpSpace, 0, 8);
+ pBt->pTmpSpace += 4;
+ }
}
}
@@ -50271,8 +60735,11 @@ static void allocateTempSpace(BtShared *pBt){
** Free the pBt->pTmpSpace allocation
*/
static void freeTempSpace(BtShared *pBt){
- sqlite3PageFree( pBt->pTmpSpace);
- pBt->pTmpSpace = 0;
+ if( pBt->pTmpSpace ){
+ pBt->pTmpSpace -= 4;
+ sqlite3PageFree(pBt->pTmpSpace);
+ pBt->pTmpSpace = 0;
+ }
}
/*
@@ -50298,7 +60765,7 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
** The call to sqlite3BtreeRollback() drops any table-locks held by
** this handle.
*/
- sqlite3BtreeRollback(p);
+ sqlite3BtreeRollback(p, SQLITE_OK, 0);
sqlite3BtreeLeave(p);
/* If there are still other outstanding references to the shared-btree
@@ -50334,19 +60801,11 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btree *p){
}
/*
-** Change the limit on the number of pages allowed in the cache.
-**
-** The maximum number of cache pages is set to the absolute
-** value of mxPage. If mxPage is negative, the pager will
-** operate asynchronously - it will not stop to do fsync()s
-** to insure data is written to the disk surface before
-** continuing. Transactions still work if synchronous is off,
-** and the database cannot be corrupted if this program
-** crashes. But if the operating system crashes or there is
-** an abrupt power failure when synchronous is off, the database
-** could be left in an inconsistent and unrecoverable state.
-** Synchronous is on by default so database corruption is not
-** normally a worry.
+** Change the "soft" limit on the number of pages in the cache.
+** Unused and unmodified pages will be recycled when the number of
+** pages in the cache exceeds this soft limit. But the size of the
+** cache is allowed to grow larger than this limit if it contains
+** dirty pages or pages still in active use.
*/
SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
BtShared *pBt = p->pBt;
@@ -50358,6 +60817,41 @@ SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
}
/*
+** Change the "spill" limit on the number of pages in the cache.
+** If the number of pages exceeds this limit during a write transaction,
+** the pager might attempt to "spill" pages to the journal early in
+** order to free up memory.
+**
+** The value returned is the current spill size. If zero is passed
+** as an argument, no changes are made to the spill size setting, so
+** using mxPage of 0 is a way to query the current spill size.
+*/
+SQLITE_PRIVATE int sqlite3BtreeSetSpillSize(Btree *p, int mxPage){
+ BtShared *pBt = p->pBt;
+ int res;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
+ res = sqlite3PagerSetSpillsize(pBt->pPager, mxPage);
+ sqlite3BtreeLeave(p);
+ return res;
+}
+
+#if SQLITE_MAX_MMAP_SIZE>0
+/*
+** Change the limit on the amount of the database file that may be
+** memory mapped.
+*/
+SQLITE_PRIVATE int sqlite3BtreeSetMmapLimit(Btree *p, sqlite3_int64 szMmap){
+ BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(p->db->mutex) );
+ sqlite3BtreeEnter(p);
+ sqlite3PagerSetMmapLimit(pBt->pPager, szMmap);
+ sqlite3BtreeLeave(p);
+ return SQLITE_OK;
+}
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */
+
+/*
** Change the way data is synced to disk in order to increase or decrease
** how well the database resists damage due to OS crashes and power
** failures. Level 1 is the same as asynchronous (no syncs() occur and
@@ -50366,38 +60860,20 @@ SQLITE_PRIVATE int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
** probability of damage to near zero but with a write performance reduction.
*/
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
-SQLITE_PRIVATE int sqlite3BtreeSetSafetyLevel(
+SQLITE_PRIVATE int sqlite3BtreeSetPagerFlags(
Btree *p, /* The btree to set the safety level on */
- int level, /* PRAGMA synchronous. 1=OFF, 2=NORMAL, 3=FULL */
- int fullSync, /* PRAGMA fullfsync. */
- int ckptFullSync /* PRAGMA checkpoint_fullfync */
+ unsigned pgFlags /* Various PAGER_* flags */
){
BtShared *pBt = p->pBt;
assert( sqlite3_mutex_held(p->db->mutex) );
- assert( level>=1 && level<=3 );
sqlite3BtreeEnter(p);
- sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync, ckptFullSync);
+ sqlite3PagerSetFlags(pBt->pPager, pgFlags);
sqlite3BtreeLeave(p);
return SQLITE_OK;
}
#endif
/*
-** Return TRUE if the given btree is set to safety level 1. In other
-** words, return TRUE if no sync() occurs on the disk files.
-*/
-SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree *p){
- BtShared *pBt = p->pBt;
- int rc;
- assert( sqlite3_mutex_held(p->db->mutex) );
- sqlite3BtreeEnter(p);
- assert( pBt && pBt->pPager );
- rc = sqlite3PagerNosync(pBt->pPager);
- sqlite3BtreeLeave(p);
- return rc;
-}
-
-/*
** Change the default pages size and the number of reserved bytes per page.
** Or, if the page size has already been fixed, return SQLITE_READONLY
** without changing anything.
@@ -50414,7 +60890,7 @@ SQLITE_PRIVATE int sqlite3BtreeSyncDisabled(Btree *p){
** If parameter nReserve is less than zero, then the number of reserved
** bytes per page is left unchanged.
**
-** If the iFix!=0 then the pageSizeFixed flag is set so that the page size
+** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size
** and autovacuum mode can no longer be changed.
*/
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
@@ -50422,7 +60898,10 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve,
BtShared *pBt = p->pBt;
assert( nReserve>=-1 && nReserve<=255 );
sqlite3BtreeEnter(p);
- if( pBt->pageSizeFixed ){
+#if SQLITE_HAS_CODEC
+ if( nReserve>pBt->optimalReserve ) pBt->optimalReserve = (u8)nReserve;
+#endif
+ if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
sqlite3BtreeLeave(p);
return SQLITE_READONLY;
}
@@ -50433,13 +60912,13 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve,
if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
((pageSize-1)&pageSize)==0 ){
assert( (pageSize & 7)==0 );
- assert( !pBt->pPage1 && !pBt->pCursor );
+ assert( !pBt->pCursor );
pBt->pageSize = (u32)pageSize;
freeTempSpace(pBt);
}
rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, nReserve);
pBt->usableSize = pBt->pageSize - (u16)nReserve;
- if( iFix ) pBt->pageSizeFixed = 1;
+ if( iFix ) pBt->btsFlags |= BTS_PAGESIZE_FIXED;
sqlite3BtreeLeave(p);
return rc;
}
@@ -50451,20 +60930,45 @@ SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
-#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
+/*
+** This function is similar to sqlite3BtreeGetReserve(), except that it
+** may only be called if it is guaranteed that the b-tree mutex is already
+** held.
+**
+** This is useful in one special case in the backup API code where it is
+** known that the shared b-tree mutex is held, but the mutex on the
+** database handle that owns *p is not. In this case if sqlite3BtreeEnter()
+** were to be called, it might collide with some other operation on the
+** database handle that owns *p, causing undefined behavior.
+*/
+SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p){
+ int n;
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
+ n = p->pBt->pageSize - p->pBt->usableSize;
+ return n;
+}
+
/*
** Return the number of bytes of space at the end of every page that
** are intentually left unused. This is the "reserved" space that is
** sometimes used by extensions.
+**
+** If SQLITE_HAS_MUTEX is defined then the number returned is the
+** greater of the current reserved space and the maximum requested
+** reserve space.
*/
-SQLITE_PRIVATE int sqlite3BtreeGetReserve(Btree *p){
+SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){
int n;
sqlite3BtreeEnter(p);
- n = p->pBt->pageSize - p->pBt->usableSize;
+ n = sqlite3BtreeGetReserveNoMutex(p);
+#ifdef SQLITE_HAS_CODEC
+ if( n<p->pBt->optimalReserve ) n = p->pBt->optimalReserve;
+#endif
sqlite3BtreeLeave(p);
return n;
}
+
/*
** Set the maximum page count for a database if mxPage is positive.
** No changes are made if mxPage is 0 or negative.
@@ -50479,8 +60983,8 @@ SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
}
/*
-** Set the secureDelete flag if newFlag is 0 or 1. If newFlag is -1,
-** then make no changes. Always return the value of the secureDelete
+** Set the BTS_SECURE_DELETE flag if newFlag is 0 or 1. If newFlag is -1,
+** then make no changes. Always return the value of the BTS_SECURE_DELETE
** setting after the change.
*/
SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
@@ -50488,13 +60992,13 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
if( p==0 ) return 0;
sqlite3BtreeEnter(p);
if( newFlag>=0 ){
- p->pBt->secureDelete = (newFlag!=0) ? 1 : 0;
+ p->pBt->btsFlags &= ~BTS_SECURE_DELETE;
+ if( newFlag ) p->pBt->btsFlags |= BTS_SECURE_DELETE;
}
- b = p->pBt->secureDelete;
+ b = (p->pBt->btsFlags & BTS_SECURE_DELETE)!=0;
sqlite3BtreeLeave(p);
return b;
}
-#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */
/*
** Change the 'auto-vacuum' property of the database. If the 'autoVacuum'
@@ -50511,7 +61015,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
u8 av = (u8)autoVacuum;
sqlite3BtreeEnter(p);
- if( pBt->pageSizeFixed && (av ?1:0)!=pBt->autoVacuum ){
+ if( (pBt->btsFlags & BTS_PAGESIZE_FIXED)!=0 && (av ?1:0)!=pBt->autoVacuum ){
rc = SQLITE_READONLY;
}else{
pBt->autoVacuum = av ?1:0;
@@ -50579,20 +61083,23 @@ static int lockBtree(BtShared *pBt){
u32 usableSize;
u8 *page1 = pPage1->aData;
rc = SQLITE_NOTADB;
+ /* EVIDENCE-OF: R-43737-39999 Every valid SQLite database file begins
+ ** with the following 16 bytes (in hex): 53 51 4c 69 74 65 20 66 6f 72 6d
+ ** 61 74 20 33 00. */
if( memcmp(page1, zMagicHeader, 16)!=0 ){
goto page1_init_failed;
}
#ifdef SQLITE_OMIT_WAL
if( page1[18]>1 ){
- pBt->readOnly = 1;
+ pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>1 ){
goto page1_init_failed;
}
#else
if( page1[18]>2 ){
- pBt->readOnly = 1;
+ pBt->btsFlags |= BTS_READ_ONLY;
}
if( page1[19]>2 ){
goto page1_init_failed;
@@ -50606,28 +61113,50 @@ static int lockBtree(BtShared *pBt){
** may not be the latest version - there may be a newer one in the log
** file.
*/
- if( page1[19]==2 && pBt->doNotUseWAL==0 ){
+ if( page1[19]==2 && (pBt->btsFlags & BTS_NO_WAL)==0 ){
int isOpen = 0;
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
if( rc!=SQLITE_OK ){
goto page1_init_failed;
- }else if( isOpen==0 ){
- releasePage(pPage1);
- return SQLITE_OK;
+ }else{
+#if SQLITE_DEFAULT_SYNCHRONOUS!=SQLITE_DEFAULT_WAL_SYNCHRONOUS
+ sqlite3 *db;
+ Db *pDb;
+ if( (db=pBt->db)!=0 && (pDb=db->aDb)!=0 ){
+ while( pDb->pBt==0 || pDb->pBt->pBt!=pBt ){ pDb++; }
+ if( pDb->bSyncSet==0
+ && pDb->safety_level==SQLITE_DEFAULT_SYNCHRONOUS+1
+ ){
+ pDb->safety_level = SQLITE_DEFAULT_WAL_SYNCHRONOUS+1;
+ sqlite3PagerSetFlags(pBt->pPager,
+ pDb->safety_level | (db->flags & PAGER_FLAGS_MASK));
+ }
+ }
+#endif
+ if( isOpen==0 ){
+ releasePage(pPage1);
+ return SQLITE_OK;
+ }
}
rc = SQLITE_NOTADB;
}
#endif
- /* The maximum embedded fraction must be exactly 25%. And the minimum
- ** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
+ /* EVIDENCE-OF: R-15465-20813 The maximum and minimum embedded payload
+ ** fractions and the leaf payload fraction values must be 64, 32, and 32.
+ **
** The original design allowed these amounts to vary, but as of
** version 3.6.0, we require them to be fixed.
*/
if( memcmp(&page1[21], "\100\040\040",3)!=0 ){
goto page1_init_failed;
}
+ /* EVIDENCE-OF: R-51873-39618 The page size for a database file is
+ ** determined by the 2-byte integer located at an offset of 16 bytes from
+ ** the beginning of the database file. */
pageSize = (page1[16]<<8) | (page1[17]<<16);
+ /* EVIDENCE-OF: R-25008-21688 The size of a page is a power of two
+ ** between 512 and 65536 inclusive. */
if( ((pageSize-1)&pageSize)!=0
|| pageSize>SQLITE_MAX_PAGE_SIZE
|| pageSize<=256
@@ -50635,6 +61164,13 @@ static int lockBtree(BtShared *pBt){
goto page1_init_failed;
}
assert( (pageSize & 7)==0 );
+ /* EVIDENCE-OF: R-59310-51205 The "reserved space" size in the 1-byte
+ ** integer at offset 20 is the number of bytes of space at the end of
+ ** each page to reserve for extensions.
+ **
+ ** EVIDENCE-OF: R-37497-42412 The size of the reserved region is
+ ** determined by the one-byte unsigned integer found at an offset of 20
+ ** into the database file header. */
usableSize = pageSize - page1[20];
if( (u32)pageSize!=pBt->pageSize ){
/* After reading the first page of the database assuming a page size
@@ -50655,6 +61191,9 @@ static int lockBtree(BtShared *pBt){
rc = SQLITE_CORRUPT_BKPT;
goto page1_init_failed;
}
+ /* EVIDENCE-OF: R-28312-64704 However, the usable size is not allowed to
+ ** be less than 480. In other words, if the page size is 512, then the
+ ** reserved space size cannot exceed 32. */
if( usableSize<480 ){
goto page1_init_failed;
}
@@ -50683,6 +61222,11 @@ static int lockBtree(BtShared *pBt){
pBt->minLocal = (u16)((pBt->usableSize-12)*32/255 - 23);
pBt->maxLeaf = (u16)(pBt->usableSize - 35);
pBt->minLeaf = (u16)((pBt->usableSize-12)*32/255 - 23);
+ if( pBt->maxLocal>127 ){
+ pBt->max1bytePayload = 127;
+ }else{
+ pBt->max1bytePayload = (u8)pBt->maxLocal;
+ }
assert( pBt->maxLeaf + 23 <= MX_CELL_SIZE(pBt) );
pBt->pPage1 = pPage1;
pBt->nPage = nPage;
@@ -50694,6 +61238,30 @@ page1_init_failed:
return rc;
}
+#ifndef NDEBUG
+/*
+** Return the number of cursors open on pBt. This is for use
+** in assert() expressions, so it is only compiled if NDEBUG is not
+** defined.
+**
+** Only write cursors are counted if wrOnly is true. If wrOnly is
+** false then all cursors are counted.
+**
+** For the purposes of this routine, a cursor is any cursor that
+** is capable of reading or writing to the database. Cursors that
+** have been tripped into the CURSOR_FAULT state are not counted.
+*/
+static int countValidCursors(BtShared *pBt, int wrOnly){
+ BtCursor *pCur;
+ int r = 0;
+ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+ if( (wrOnly==0 || (pCur->curFlags & BTCF_WriteFlag)!=0)
+ && pCur->eState!=CURSOR_FAULT ) r++;
+ }
+ return r;
+}
+#endif
+
/*
** If there are no outstanding cursors and we are not in the middle
** of a transaction but there is a read lock on the database, then
@@ -50704,13 +61272,13 @@ page1_init_failed:
*/
static void unlockBtreeIfUnused(BtShared *pBt){
assert( sqlite3_mutex_held(pBt->mutex) );
- assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE );
+ assert( countValidCursors(pBt,0)==0 || pBt->inTransaction>TRANS_NONE );
if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){
- assert( pBt->pPage1->aData );
+ MemPage *pPage1 = pBt->pPage1;
+ assert( pPage1->aData );
assert( sqlite3PagerRefcount(pBt->pPager)==1 );
- assert( pBt->pPage1->aData );
- releasePage(pBt->pPage1);
pBt->pPage1 = 0;
+ releasePageNotNull(pPage1);
}
}
@@ -50746,7 +61314,7 @@ static int newDatabase(BtShared *pBt){
data[23] = 32;
memset(&data[24], 0, 100-24);
zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA );
- pBt->pageSizeFixed = 1;
+ pBt->btsFlags |= BTS_PAGESIZE_FIXED;
#ifndef SQLITE_OMIT_AUTOVACUUM
assert( pBt->autoVacuum==1 || pBt->autoVacuum==0 );
assert( pBt->incrVacuum==1 || pBt->incrVacuum==0 );
@@ -50759,6 +61327,20 @@ static int newDatabase(BtShared *pBt){
}
/*
+** Initialize the first page of the database file (creating a database
+** consisting of a single page and no schema objects). Return SQLITE_OK
+** if successful, or an SQLite error code otherwise.
+*/
+SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p){
+ int rc;
+ sqlite3BtreeEnter(p);
+ p->pBt->nPage = 0;
+ rc = newDatabase(p->pBt);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
+
+/*
** Attempt to start a new transaction. A write-transaction
** is started if the second argument is nonzero, otherwise a read-
** transaction. If the second argument is 2 or more and exclusive
@@ -50794,7 +61376,6 @@ static int newDatabase(BtShared *pBt){
** proceed.
*/
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
- sqlite3 *pBlock = 0;
BtShared *pBt = p->pBt;
int rc = SQLITE_OK;
@@ -50808,33 +61389,39 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
goto trans_begun;
}
+ assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
/* Write transactions are not possible on a read-only database */
- if( pBt->readOnly && wrflag ){
+ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
rc = SQLITE_READONLY;
goto trans_begun;
}
#ifndef SQLITE_OMIT_SHARED_CACHE
- /* If another database handle has already opened a write transaction
- ** on this shared-btree structure and a second write transaction is
- ** requested, return SQLITE_LOCKED.
- */
- if( (wrflag && pBt->inTransaction==TRANS_WRITE) || pBt->isPending ){
- pBlock = pBt->pWriter->db;
- }else if( wrflag>1 ){
- BtLock *pIter;
- for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
- if( pIter->pBtree!=p ){
- pBlock = pIter->pBtree->db;
- break;
+ {
+ sqlite3 *pBlock = 0;
+ /* If another database handle has already opened a write transaction
+ ** on this shared-btree structure and a second write transaction is
+ ** requested, return SQLITE_LOCKED.
+ */
+ if( (wrflag && pBt->inTransaction==TRANS_WRITE)
+ || (pBt->btsFlags & BTS_PENDING)!=0
+ ){
+ pBlock = pBt->pWriter->db;
+ }else if( wrflag>1 ){
+ BtLock *pIter;
+ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){
+ if( pIter->pBtree!=p ){
+ pBlock = pIter->pBtree->db;
+ break;
+ }
}
}
- }
- if( pBlock ){
- sqlite3ConnectionBlocked(p->db, pBlock);
- rc = SQLITE_LOCKED_SHAREDCACHE;
- goto trans_begun;
+ if( pBlock ){
+ sqlite3ConnectionBlocked(p->db, pBlock);
+ rc = SQLITE_LOCKED_SHAREDCACHE;
+ goto trans_begun;
+ }
}
#endif
@@ -50844,7 +61431,8 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
if( SQLITE_OK!=rc ) goto trans_begun;
- pBt->initiallyEmpty = (u8)(pBt->nPage==0);
+ pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
+ if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
do {
/* Call lockBtree() until either pBt->pPage1 is populated or
** lockBtree() returns something other than SQLITE_OK. lockBtree()
@@ -50856,7 +61444,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );
if( rc==SQLITE_OK && wrflag ){
- if( pBt->readOnly ){
+ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
rc = SQLITE_READONLY;
}else{
rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
@@ -50877,7 +61465,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
pBt->nTransaction++;
#ifndef SQLITE_OMIT_SHARED_CACHE
if( p->sharable ){
- assert( p->lock.pBtree==p && p->lock.iTable==1 );
+ assert( p->lock.pBtree==p && p->lock.iTable==1 );
p->lock.eLock = READ_LOCK;
p->lock.pNext = pBt->pLock;
pBt->pLock = &p->lock;
@@ -50893,7 +61481,8 @@ SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
#ifndef SQLITE_OMIT_SHARED_CACHE
assert( !pBt->pWriter );
pBt->pWriter = p;
- pBt->isExclusive = (u8)(wrflag>1);
+ pBt->btsFlags &= ~BTS_EXCLUSIVE;
+ if( wrflag>1 ) pBt->btsFlags |= BTS_EXCLUSIVE;
#endif
/* If the db-size header field is incorrect (as it may be if an old
@@ -50996,20 +61585,22 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
u8 isInitOrig = pPage->isInit;
int i;
int nCell;
+ int rc;
- btreeInitPage(pPage);
+ rc = btreeInitPage(pPage);
+ if( rc ) return rc;
nCell = pPage->nCell;
for(i=0; i<nCell; i++){
u8 *pCell = findCell(pPage, i);
if( eType==PTRMAP_OVERFLOW1 ){
CellInfo info;
- btreeParseCellPtr(pPage, pCell, &info);
- if( info.iOverflow
- && pCell+info.iOverflow+3<=pPage->aData+pPage->maskPage
- && iFrom==get4byte(&pCell[info.iOverflow])
+ pPage->xParseCell(pPage, pCell, &info);
+ if( info.nLocal<info.nPayload
+ && pCell+info.nSize-1<=pPage->aData+pPage->maskPage
+ && iFrom==get4byte(pCell+info.nSize-4)
){
- put4byte(&pCell[info.iOverflow], iTo);
+ put4byte(pCell+info.nSize-4, iTo);
break;
}
}else{
@@ -51120,24 +61711,23 @@ static int relocatePage(
static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
/*
-** Perform a single step of an incremental-vacuum. If successful,
-** return SQLITE_OK. If there is no work to do (and therefore no
-** point in calling this function again), return SQLITE_DONE.
+** Perform a single step of an incremental-vacuum. If successful, return
+** SQLITE_OK. If there is no work to do (and therefore no point in
+** calling this function again), return SQLITE_DONE. Or, if an error
+** occurs, return some other error code.
+**
+** More specifically, this function attempts to re-organize the database so
+** that the last page of the file currently in use is no longer in use.
**
-** More specificly, this function attempts to re-organize the
-** database so that the last page of the file currently in use
-** is no longer in use.
+** Parameter nFin is the number of pages that this database would contain
+** were this function called until it returns SQLITE_DONE.
**
-** If the nFin parameter is non-zero, this function assumes
-** that the caller will keep calling incrVacuumStep() until
-** it returns SQLITE_DONE or an error, and that nFin is the
-** number of pages the database file will contain after this
-** process is complete. If nFin is zero, it is assumed that
-** incrVacuumStep() will be called a finite amount of times
-** which may or may not empty the freelist. A full autovacuum
-** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0.
+** If the bCommit parameter is non-zero, this function assumes that the
+** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE
+** or an error. bCommit is passed true for an auto-vacuum-on-commit
+** operation, or false for an incremental vacuum.
*/
-static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
+static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
Pgno nFreeList; /* Number of pages still on the free-list */
int rc;
@@ -51162,15 +61752,15 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
}
if( eType==PTRMAP_FREEPAGE ){
- if( nFin==0 ){
+ if( bCommit==0 ){
/* Remove the page from the files free-list. This is not required
- ** if nFin is non-zero. In that case, the free-list will be
+ ** if bCommit is non-zero. In that case, the free-list will be
** truncated to zero after this function returns, so it doesn't
** matter if it still contains some garbage entries.
*/
Pgno iFreePg;
MemPage *pFreePg;
- rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1);
+ rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -51180,34 +61770,37 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
} else {
Pgno iFreePg; /* Index of free page to move pLastPg to */
MemPage *pLastPg;
+ u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */
+ Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */
rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0);
if( rc!=SQLITE_OK ){
return rc;
}
- /* If nFin is zero, this loop runs exactly once and page pLastPg
+ /* If bCommit is zero, this loop runs exactly once and page pLastPg
** is swapped with the first free page pulled off the free list.
**
- ** On the other hand, if nFin is greater than zero, then keep
+ ** On the other hand, if bCommit is greater than zero, then keep
** looping until a free-page located within the first nFin pages
** of the file is found.
*/
+ if( bCommit==0 ){
+ eMode = BTALLOC_LE;
+ iNear = nFin;
+ }
do {
MemPage *pFreePg;
- rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0);
+ rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode);
if( rc!=SQLITE_OK ){
releasePage(pLastPg);
return rc;
}
releasePage(pFreePg);
- }while( nFin!=0 && iFreePg>nFin );
+ }while( bCommit && iFreePg>nFin );
assert( iFreePg<iLastPg );
- rc = sqlite3PagerWrite(pLastPg->pDbPage);
- if( rc==SQLITE_OK ){
- rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
- }
+ rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, bCommit);
releasePage(pLastPg);
if( rc!=SQLITE_OK ){
return rc;
@@ -51215,30 +61808,40 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
}
}
- if( nFin==0 ){
- iLastPg--;
- while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){
- if( PTRMAP_ISPAGE(pBt, iLastPg) ){
- MemPage *pPg;
- rc = btreeGetPage(pBt, iLastPg, &pPg, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- rc = sqlite3PagerWrite(pPg->pDbPage);
- releasePage(pPg);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- }
+ if( bCommit==0 ){
+ do {
iLastPg--;
- }
- sqlite3PagerTruncateImage(pBt->pPager, iLastPg);
+ }while( iLastPg==PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg) );
+ pBt->bDoTruncate = 1;
pBt->nPage = iLastPg;
}
return SQLITE_OK;
}
/*
+** The database opened by the first argument is an auto-vacuum database
+** nOrig pages in size containing nFree free pages. Return the expected
+** size of the database in pages following an auto-vacuum operation.
+*/
+static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){
+ int nEntry; /* Number of entries on one ptrmap page */
+ Pgno nPtrmap; /* Number of PtrMap pages to be freed */
+ Pgno nFin; /* Return value */
+
+ nEntry = pBt->usableSize/5;
+ nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
+ nFin = nOrig - nFree - nPtrmap;
+ if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
+ nFin--;
+ }
+ while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
+ nFin--;
+ }
+
+ return nFin;
+}
+
+/*
** A write-transaction must be opened before calling this function.
** It performs a single unit of work towards an incremental vacuum.
**
@@ -51255,11 +61858,24 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
if( !pBt->autoVacuum ){
rc = SQLITE_DONE;
}else{
- invalidateAllOverflowCache(pBt);
- rc = incrVacuumStep(pBt, 0, btreePagecount(pBt));
- if( rc==SQLITE_OK ){
- rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
- put4byte(&pBt->pPage1->aData[28], pBt->nPage);
+ Pgno nOrig = btreePagecount(pBt);
+ Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
+ Pgno nFin = finalDbSize(pBt, nOrig, nFree);
+
+ if( nOrig<nFin ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else if( nFree>0 ){
+ rc = saveAllCursors(pBt, 0, 0);
+ if( rc==SQLITE_OK ){
+ invalidateAllOverflowCache(pBt);
+ rc = incrVacuumStep(pBt, nFin, nOrig, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
+ put4byte(&pBt->pPage1->aData[28], pBt->nPage);
+ }
+ }else{
+ rc = SQLITE_DONE;
}
}
sqlite3BtreeLeave(p);
@@ -51268,7 +61884,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
/*
** This routine is called prior to sqlite3PagerCommit when a transaction
-** is commited for an auto-vacuum database.
+** is committed for an auto-vacuum database.
**
** If SQLITE_OK is returned, then *pnTrunc is set to the number of pages
** the database file should be truncated to during the commit process.
@@ -51278,7 +61894,7 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuum(Btree *p){
static int autoVacuumCommit(BtShared *pBt){
int rc = SQLITE_OK;
Pager *pPager = pBt->pPager;
- VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager) );
+ VVA_ONLY( int nRef = sqlite3PagerRefcount(pPager); )
assert( sqlite3_mutex_held(pBt->mutex) );
invalidateAllOverflowCache(pBt);
@@ -51286,9 +61902,7 @@ static int autoVacuumCommit(BtShared *pBt){
if( !pBt->incrVacuum ){
Pgno nFin; /* Number of pages in database after autovacuuming */
Pgno nFree; /* Number of pages on the freelist initially */
- Pgno nPtrmap; /* Number of PtrMap pages to be freed */
Pgno iFree; /* The next page to be freed */
- int nEntry; /* Number of entries on one ptrmap page */
Pgno nOrig; /* Database size before freeing */
nOrig = btreePagecount(pBt);
@@ -51301,26 +61915,20 @@ static int autoVacuumCommit(BtShared *pBt){
}
nFree = get4byte(&pBt->pPage1->aData[36]);
- nEntry = pBt->usableSize/5;
- nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
- nFin = nOrig - nFree - nPtrmap;
- if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
- nFin--;
- }
- while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
- nFin--;
- }
+ nFin = finalDbSize(pBt, nOrig, nFree);
if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT;
-
+ if( nFin<nOrig ){
+ rc = saveAllCursors(pBt, 0, 0);
+ }
for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){
- rc = incrVacuumStep(pBt, nFin, iFree);
+ rc = incrVacuumStep(pBt, nFin, iFree, 1);
}
if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[32], 0);
put4byte(&pBt->pPage1->aData[36], 0);
put4byte(&pBt->pPage1->aData[28], nFin);
- sqlite3PagerTruncateImage(pBt->pPager, nFin);
+ pBt->bDoTruncate = 1;
pBt->nPage = nFin;
}
if( rc!=SQLITE_OK ){
@@ -51328,7 +61936,7 @@ static int autoVacuumCommit(BtShared *pBt){
}
}
- assert( nRef==sqlite3PagerRefcount(pPager) );
+ assert( nRef>=sqlite3PagerRefcount(pPager) );
return rc;
}
@@ -51375,6 +61983,9 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
return rc;
}
}
+ if( pBt->bDoTruncate ){
+ sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
+ }
#endif
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
sqlite3BtreeLeave(p);
@@ -51388,10 +61999,13 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
*/
static void btreeEndTransaction(Btree *p){
BtShared *pBt = p->pBt;
+ sqlite3 *db = p->db;
assert( sqlite3BtreeHoldsMutex(p) );
- btreeClearHasContent(pBt);
- if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ pBt->bDoTruncate = 0;
+#endif
+ if( p->inTrans>TRANS_NONE && db->nVdbeRead>1 ){
/* If there are other active statements that belong to this database
** handle, downgrade to a read-only transaction. The other statements
** may still be reading from the database. */
@@ -51464,7 +62078,9 @@ SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree *p, int bCleanup){
sqlite3BtreeLeave(p);
return rc;
}
+ p->iDataVersion--; /* Compensate for pPager->iDataVersion++; */
pBt->inTransaction = TRANS_READ;
+ btreeClearHasContent(pBt);
}
btreeEndTransaction(p);
@@ -51486,88 +62102,94 @@ SQLITE_PRIVATE int sqlite3BtreeCommit(Btree *p){
return rc;
}
-#ifndef NDEBUG
-/*
-** Return the number of write-cursors open on this handle. This is for use
-** in assert() expressions, so it is only compiled if NDEBUG is not
-** defined.
-**
-** For the purposes of this routine, a write-cursor is any cursor that
-** is capable of writing to the databse. That means the cursor was
-** originally opened for writing and the cursor has not be disabled
-** by having its state changed to CURSOR_FAULT.
-*/
-static int countWriteCursors(BtShared *pBt){
- BtCursor *pCur;
- int r = 0;
- for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
- if( pCur->wrFlag && pCur->eState!=CURSOR_FAULT ) r++;
- }
- return r;
-}
-#endif
-
/*
** This routine sets the state to CURSOR_FAULT and the error
-** code to errCode for every cursor on BtShared that pBtree
-** references.
-**
-** Every cursor is tripped, including cursors that belong
-** to other database connections that happen to be sharing
-** the cache with pBtree.
-**
-** This routine gets called when a rollback occurs.
-** All cursors using the same cache must be tripped
-** to prevent them from trying to use the btree after
-** the rollback. The rollback may have deleted tables
-** or moved root pages, so it is not sufficient to
-** save the state of the cursor. The cursor must be
-** invalidated.
-*/
-SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
+** code to errCode for every cursor on any BtShared that pBtree
+** references. Or if the writeOnly flag is set to 1, then only
+** trip write cursors and leave read cursors unchanged.
+**
+** Every cursor is a candidate to be tripped, including cursors
+** that belong to other database connections that happen to be
+** sharing the cache with pBtree.
+**
+** This routine gets called when a rollback occurs. If the writeOnly
+** flag is true, then only write-cursors need be tripped - read-only
+** cursors save their current positions so that they may continue
+** following the rollback. Or, if writeOnly is false, all cursors are
+** tripped. In general, writeOnly is false if the transaction being
+** rolled back modified the database schema. In this case b-tree root
+** pages may be moved or deleted from the database altogether, making
+** it unsafe for read cursors to continue.
+**
+** If the writeOnly flag is true and an error is encountered while
+** saving the current position of a read-only cursor, all cursors,
+** including all read-cursors are tripped.
+**
+** SQLITE_OK is returned if successful, or if an error occurs while
+** saving a cursor position, an SQLite error code.
+*/
+SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
BtCursor *p;
- sqlite3BtreeEnter(pBtree);
- for(p=pBtree->pBt->pCursor; p; p=p->pNext){
- int i;
- sqlite3BtreeClearCursor(p);
- p->eState = CURSOR_FAULT;
- p->skipNext = errCode;
- for(i=0; i<=p->iPage; i++){
- releasePage(p->apPage[i]);
- p->apPage[i] = 0;
+ int rc = SQLITE_OK;
+
+ assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
+ if( pBtree ){
+ sqlite3BtreeEnter(pBtree);
+ for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+ int i;
+ if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
+ if( p->eState==CURSOR_VALID || p->eState==CURSOR_SKIPNEXT ){
+ rc = saveCursorPosition(p);
+ if( rc!=SQLITE_OK ){
+ (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0);
+ break;
+ }
+ }
+ }else{
+ sqlite3BtreeClearCursor(p);
+ p->eState = CURSOR_FAULT;
+ p->skipNext = errCode;
+ }
+ for(i=0; i<=p->iPage; i++){
+ releasePage(p->apPage[i]);
+ p->apPage[i] = 0;
+ }
}
+ sqlite3BtreeLeave(pBtree);
}
- sqlite3BtreeLeave(pBtree);
+ return rc;
}
/*
-** Rollback the transaction in progress. All cursors will be
-** invalided by this operation. Any attempt to use a cursor
-** that was open at the beginning of this operation will result
-** in an error.
+** Rollback the transaction in progress.
+**
+** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
+** Only write cursors are tripped if writeOnly is true but all cursors are
+** tripped if writeOnly is false. Any attempt to use
+** a tripped cursor will result in an error.
**
** This will release the write lock on the database file. If there
** are no active cursors, it also releases the read lock.
*/
-SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p){
+SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
int rc;
BtShared *pBt = p->pBt;
MemPage *pPage1;
+ assert( writeOnly==1 || writeOnly==0 );
+ assert( tripCode==SQLITE_ABORT_ROLLBACK || tripCode==SQLITE_OK );
sqlite3BtreeEnter(p);
- rc = saveAllCursors(pBt, 0, 0);
-#ifndef SQLITE_OMIT_SHARED_CACHE
- if( rc!=SQLITE_OK ){
- /* This is a horrible situation. An IO or malloc() error occurred whilst
- ** trying to save cursor positions. If this is an automatic rollback (as
- ** the result of a constraint, malloc() failure or IO error) then
- ** the cache may be internally inconsistent (not contain valid trees) so
- ** we cannot simply return the error to the caller. Instead, abort
- ** all queries that may be using any of the cursors that failed to save.
- */
- sqlite3BtreeTripAllCursors(p, rc);
+ if( tripCode==SQLITE_OK ){
+ rc = tripCode = saveAllCursors(pBt, 0, 0);
+ if( rc ) writeOnly = 0;
+ }else{
+ rc = SQLITE_OK;
+ }
+ if( tripCode ){
+ int rc2 = sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
+ assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) );
+ if( rc2!=SQLITE_OK ) rc = rc2;
}
-#endif
btreeIntegrity(p);
if( p->inTrans==TRANS_WRITE ){
@@ -51590,8 +62212,9 @@ SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p){
pBt->nPage = nPage;
releasePage(pPage1);
}
- assert( countWriteCursors(pBt)==0 );
+ assert( countValidCursors(pBt, 1)==0 );
pBt->inTransaction = TRANS_READ;
+ btreeClearHasContent(pBt);
}
btreeEndTransaction(p);
@@ -51600,7 +62223,7 @@ SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p){
}
/*
-** Start a statement subtransaction. The subtransaction can can be rolled
+** Start a statement subtransaction. The subtransaction can be rolled
** back independently of the main transaction. You must start a transaction
** before starting a subtransaction. The subtransaction is ended automatically
** if the main transaction commits or rolls back.
@@ -51622,7 +62245,7 @@ SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree *p, int iStatement){
BtShared *pBt = p->pBt;
sqlite3BtreeEnter(p);
assert( p->inTrans==TRANS_WRITE );
- assert( pBt->readOnly==0 );
+ assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( iStatement>0 );
assert( iStatement>p->db->nSavepoint );
assert( pBt->inTransaction==TRANS_WRITE );
@@ -51657,7 +62280,9 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
sqlite3BtreeEnter(p);
rc = sqlite3PagerSavepoint(pBt->pPager, op, iSavepoint);
if( rc==SQLITE_OK ){
- if( iSavepoint<0 && pBt->initiallyEmpty ) pBt->nPage = 0;
+ if( iSavepoint<0 && (pBt->btsFlags & BTS_INITIALLY_EMPTY)!=0 ){
+ pBt->nPage = 0;
+ }
rc = newDatabase(pBt);
pBt->nPage = get4byte(28 + pBt->pPage1->aData);
@@ -51678,13 +62303,13 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
** on the database already. If a write-cursor is requested, then
** the caller is assumed to have an open write transaction.
**
-** If wrFlag==0, then the cursor can only be used for reading.
-** If wrFlag==1, then the cursor can be used for reading or for
-** writing if other conditions for writing are also met. These
-** are the conditions that must be met in order for writing to
-** be allowed:
+** If the BTREE_WRCSR bit of wrFlag is clear, then the cursor can only
+** be used for reading. If the BTREE_WRCSR bit is set, then the cursor
+** can be used for reading or for writing if other conditions for writing
+** are also met. These are the conditions that must be met in order
+** for writing to be allowed:
**
-** 1: The cursor must have been opened with wrFlag==1
+** 1: The cursor must have been opened with wrFlag containing BTREE_WRCSR
**
** 2: Other database connections that share the same pager cache
** but which are not in the READ_UNCOMMITTED state may not have
@@ -51696,6 +62321,16 @@ SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *p, int op, int iSavepoint){
**
** 4: There must be an active transaction.
**
+** The BTREE_FORDELETE bit of wrFlag may optionally be set if BTREE_WRCSR
+** is set. If FORDELETE is set, that is a hint to the implementation that
+** this cursor will only be used to seek to and delete entries of an index
+** as part of a larger DELETE statement. The FORDELETE hint is not used by
+** this implementation. But in a hypothetical alternative storage engine
+** in which index entries are automatically deleted when corresponding table
+** rows are deleted, the FORDELETE flag is a hint that all SEEK and DELETE
+** operations on this cursor can be no-ops and all READ operations can
+** return a null row (2-bytes: 0x01 0x00).
+**
** No checking is done to make sure that page iTable really is the
** root page of a b-tree. If it is not, then the cursor acquired
** will not work correctly.
@@ -51711,24 +62346,30 @@ static int btreeCursor(
BtCursor *pCur /* Space for new cursor */
){
BtShared *pBt = p->pBt; /* Shared b-tree handle */
+ BtCursor *pX; /* Looping over other all cursors */
assert( sqlite3BtreeHoldsMutex(p) );
- assert( wrFlag==0 || wrFlag==1 );
+ assert( wrFlag==0
+ || wrFlag==BTREE_WRCSR
+ || wrFlag==(BTREE_WRCSR|BTREE_FORDELETE)
+ );
/* The following assert statements verify that if this is a sharable
** b-tree database, the connection is holding the required table locks,
** and that no other connection has any open cursor that conflicts with
** this lock. */
- assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) );
+ assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, (wrFlag?2:1)) );
assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
/* Assert that the caller has opened the required transaction. */
assert( p->inTrans>TRANS_NONE );
assert( wrFlag==0 || p->inTrans==TRANS_WRITE );
assert( pBt->pPage1 && pBt->pPage1->aData );
+ assert( wrFlag==0 || (pBt->btsFlags & BTS_READ_ONLY)==0 );
- if( NEVER(wrFlag && pBt->readOnly) ){
- return SQLITE_READONLY;
+ if( wrFlag ){
+ allocateTempSpace(pBt);
+ if( pBt->pTmpSpace==0 ) return SQLITE_NOMEM_BKPT;
}
if( iTable==1 && btreePagecount(pBt)==0 ){
assert( wrFlag==0 );
@@ -51742,14 +62383,19 @@ static int btreeCursor(
pCur->pKeyInfo = pKeyInfo;
pCur->pBtree = p;
pCur->pBt = pBt;
- pCur->wrFlag = (u8)wrFlag;
- pCur->pNext = pBt->pCursor;
- if( pCur->pNext ){
- pCur->pNext->pPrev = pCur;
+ pCur->curFlags = wrFlag ? BTCF_WriteFlag : 0;
+ pCur->curPagerFlags = wrFlag ? 0 : PAGER_GET_READONLY;
+ /* If there are two or more cursors on the same btree, then all such
+ ** cursors *must* have the BTCF_Multiple flag set. */
+ for(pX=pBt->pCursor; pX; pX=pX->pNext){
+ if( pX->pgnoRoot==(Pgno)iTable ){
+ pX->curFlags |= BTCF_Multiple;
+ pCur->curFlags |= BTCF_Multiple;
+ }
}
+ pCur->pNext = pBt->pCursor;
pBt->pCursor = pCur;
pCur->eState = CURSOR_INVALID;
- pCur->cachedRowid = 0;
return SQLITE_OK;
}
SQLITE_PRIVATE int sqlite3BtreeCursor(
@@ -51760,9 +62406,13 @@ SQLITE_PRIVATE int sqlite3BtreeCursor(
BtCursor *pCur /* Write new cursor here */
){
int rc;
- sqlite3BtreeEnter(p);
- rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
- sqlite3BtreeLeave(p);
+ if( iTable<1 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ sqlite3BtreeEnter(p);
+ rc = btreeCursor(p, iTable, wrFlag, pKeyInfo, pCur);
+ sqlite3BtreeLeave(p);
+ }
return rc;
}
@@ -51791,36 +62441,6 @@ SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){
}
/*
-** Set the cached rowid value of every cursor in the same database file
-** as pCur and having the same root page number as pCur. The value is
-** set to iRowid.
-**
-** Only positive rowid values are considered valid for this cache.
-** The cache is initialized to zero, indicating an invalid cache.
-** A btree will work fine with zero or negative rowids. We just cannot
-** cache zero or negative rowids, which means tables that use zero or
-** negative rowids might run a little slower. But in practice, zero
-** or negative rowids are very uncommon so this should not be a problem.
-*/
-SQLITE_PRIVATE void sqlite3BtreeSetCachedRowid(BtCursor *pCur, sqlite3_int64 iRowid){
- BtCursor *p;
- for(p=pCur->pBt->pCursor; p; p=p->pNext){
- if( p->pgnoRoot==pCur->pgnoRoot ) p->cachedRowid = iRowid;
- }
- assert( pCur->cachedRowid==iRowid );
-}
-
-/*
-** Return the cached rowid for the given cursor. A negative or zero
-** return value indicates that the rowid cache is invalid and should be
-** ignored. If the rowid cache has never before been set, then a
-** zero is returned.
-*/
-SQLITE_PRIVATE sqlite3_int64 sqlite3BtreeGetCachedRowid(BtCursor *pCur){
- return pCur->cachedRowid;
-}
-
-/*
** Close a cursor. The read lock on the database file is released
** when the last cursor is closed.
*/
@@ -51831,19 +62451,24 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBt;
sqlite3BtreeEnter(pBtree);
sqlite3BtreeClearCursor(pCur);
- if( pCur->pPrev ){
- pCur->pPrev->pNext = pCur->pNext;
- }else{
+ assert( pBt->pCursor!=0 );
+ if( pBt->pCursor==pCur ){
pBt->pCursor = pCur->pNext;
- }
- if( pCur->pNext ){
- pCur->pNext->pPrev = pCur->pPrev;
+ }else{
+ BtCursor *pPrev = pBt->pCursor;
+ do{
+ if( pPrev->pNext==pCur ){
+ pPrev->pNext = pCur->pNext;
+ break;
+ }
+ pPrev = pPrev->pNext;
+ }while( ALWAYS(pPrev) );
}
for(i=0; i<=pCur->iPage; i++){
releasePage(pCur->apPage[i]);
}
unlockBtreeIfUnused(pBt);
- invalidateOverflowCache(pCur);
+ sqlite3_free(pCur->aOverflow);
/* sqlite3_free(pCur); */
sqlite3BtreeLeave(pBtree);
}
@@ -51857,13 +62482,6 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
**
** BtCursor.info is a cache of the information in the current cell.
** Using this cache reduces the number of calls to btreeParseCell().
-**
-** 2007-06-25: There is a bug in some versions of MSVC that cause the
-** compiler to crash when getCellInfo() is implemented as a macro.
-** But there is a measureable speed advantage to using the macro on gcc
-** (when less compiler optimizations like -Os or -O0 are used and the
-** compiler is not doing agressive inlining.) So we use a real function
-** for MSVC and a macro for everything else. Ticket #2457.
*/
#ifndef NDEBUG
static void assertCellInfo(BtCursor *pCur){
@@ -51871,33 +62489,20 @@ SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor *pCur){
int iPage = pCur->iPage;
memset(&info, 0, sizeof(info));
btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info);
- assert( memcmp(&info, &pCur->info, sizeof(info))==0 );
+ assert( CORRUPT_DB || memcmp(&info, &pCur->info, sizeof(info))==0 );
}
#else
#define assertCellInfo(x)
#endif
-#ifdef _MSC_VER
- /* Use a real function in MSVC to work around bugs in that compiler. */
- static void getCellInfo(BtCursor *pCur){
- if( pCur->info.nSize==0 ){
- int iPage = pCur->iPage;
- btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
- pCur->validNKey = 1;
- }else{
- assertCellInfo(pCur);
- }
- }
-#else /* if not _MSC_VER */
- /* Use a macro in all other compilers so that the function is inlined */
-#define getCellInfo(pCur) \
- if( pCur->info.nSize==0 ){ \
- int iPage = pCur->iPage; \
- btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \
- pCur->validNKey = 1; \
- }else{ \
- assertCellInfo(pCur); \
+static SQLITE_NOINLINE void getCellInfo(BtCursor *pCur){
+ if( pCur->info.nSize==0 ){
+ int iPage = pCur->iPage;
+ pCur->curFlags |= BTCF_ValidNKey;
+ btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info);
+ }else{
+ assertCellInfo(pCur);
}
-#endif /* _MSC_VER */
+}
#ifndef NDEBUG /* The next routine used only within assert() statements */
/*
@@ -51911,47 +62516,33 @@ SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor *pCur){
#endif /* NDEBUG */
/*
-** Set *pSize to the size of the buffer needed to hold the value of
-** the key for the current entry. If the cursor is not pointing
-** to a valid entry, *pSize is set to 0.
-**
-** For a table with the INTKEY flag set, this routine returns the key
-** itself, not the number of bytes in the key.
-**
-** The caller must position the cursor prior to invoking this routine.
-**
-** This routine cannot fail. It always returns SQLITE_OK.
+** Return the value of the integer key or "rowid" for a table btree.
+** This routine is only valid for a cursor that is pointing into a
+** ordinary table btree. If the cursor points to an index btree or
+** is invalid, the result of this routine is undefined.
*/
-SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
+SQLITE_PRIVATE i64 sqlite3BtreeIntegerKey(BtCursor *pCur){
assert( cursorHoldsMutex(pCur) );
- assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
- if( pCur->eState!=CURSOR_VALID ){
- *pSize = 0;
- }else{
- getCellInfo(pCur);
- *pSize = pCur->info.nKey;
- }
- return SQLITE_OK;
+ assert( pCur->eState==CURSOR_VALID );
+ assert( pCur->curIntKey );
+ getCellInfo(pCur);
+ return pCur->info.nKey;
}
/*
-** Set *pSize to the number of bytes of data in the entry the
-** cursor currently points to.
+** Return the number of bytes of payload for the entry that pCur is
+** currently pointing to. For table btrees, this will be the amount
+** of data. For index btrees, this will be the size of the key.
**
** The caller must guarantee that the cursor is pointing to a non-NULL
** valid entry. In other words, the calling procedure must guarantee
** that the cursor has Cursor.eState==CURSOR_VALID.
-**
-** Failure is not possible. This function always returns SQLITE_OK.
-** It might just as well be a procedure (returning void) but we continue
-** to return an integer result code for historical reasons.
*/
-SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
+SQLITE_PRIVATE u32 sqlite3BtreePayloadSize(BtCursor *pCur){
assert( cursorHoldsMutex(pCur) );
assert( pCur->eState==CURSOR_VALID );
getCellInfo(pCur);
- *pSize = pCur->info.nData;
- return SQLITE_OK;
+ return pCur->info.nPayload;
}
/*
@@ -52014,7 +62605,7 @@ static int getOverflowPage(
assert( next==0 || rc==SQLITE_DONE );
if( rc==SQLITE_OK ){
- rc = btreeGetPage(pBt, ovfl, &pPage, 0);
+ rc = btreeGetPage(pBt, ovfl, &pPage, (ppPage==0) ? PAGER_GET_READONLY : 0);
assert( rc==SQLITE_OK || pPage==0 );
if( rc==SQLITE_OK ){
next = get4byte(pPage->aData);
@@ -52064,10 +62655,12 @@ static int copyPayload(
/*
** This function is used to read or overwrite payload information
-** for the entry that the pCur cursor is pointing to. If the eOp
-** parameter is 0, this is a read operation (data copied into
-** buffer pBuf). If it is non-zero, a write (data copied from
-** buffer pBuf).
+** for the entry that the pCur cursor is pointing to. The eOp
+** argument is interpreted as follows:
+**
+** 0: The operation is a read. Populate the overflow cache.
+** 1: The operation is a write. Populate the overflow cache.
+** 2: The operation is a read. Do not populate the overflow cache.
**
** A total of "amt" bytes are read or written beginning at "offset".
** Data is read to or from the buffer pBuf.
@@ -52075,11 +62668,11 @@ static int copyPayload(
** The content being read or written might appear on the main page
** or be scattered out on multiple overflow pages.
**
-** If the BtCursor.isIncrblobHandle flag is set, and the current
-** cursor entry uses one or more overflow pages, this function
-** allocates space for and lazily popluates the overflow page-list
-** cache array (BtCursor.aOverflow). Subsequent calls use this
-** cache to make seeking to the supplied offset more efficient.
+** If the current cursor entry uses one or more overflow pages and the
+** eOp argument is not 2, this function may allocate space for and lazily
+** populates the overflow page-list cache array (BtCursor.aOverflow).
+** Subsequent calls use this cache to make seeking to the supplied offset
+** more efficient.
**
** Once an overflow page-list cache has been allocated, it may be
** invalidated if some other cursor writes to the same table, or if
@@ -52099,24 +62692,34 @@ static int accessPayload(
){
unsigned char *aPayload;
int rc = SQLITE_OK;
- u32 nKey;
int iIdx = 0;
MemPage *pPage = pCur->apPage[pCur->iPage]; /* Btree page of current entry */
BtShared *pBt = pCur->pBt; /* Btree this cursor belongs to */
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ unsigned char * const pBufStart = pBuf;
+ int bEnd; /* True if reading to end of data */
+#endif
assert( pPage );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
assert( cursorHoldsMutex(pCur) );
+ assert( eOp!=2 || offset==0 ); /* Always start from beginning for eOp==2 */
getCellInfo(pCur);
- aPayload = pCur->info.pCell + pCur->info.nHeader;
- nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey);
-
- if( NEVER(offset+amt > nKey+pCur->info.nData)
- || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
- ){
- /* Trying to read or write past the end of the data is an error */
+ aPayload = pCur->info.pPayload;
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ bEnd = offset+amt==pCur->info.nPayload;
+#endif
+ assert( offset+amt <= pCur->info.nPayload );
+
+ assert( aPayload > pPage->aData );
+ if( (uptr)(aPayload - pPage->aData) > (pBt->usableSize - pCur->info.nLocal) ){
+ /* Trying to read or write past the end of the data is an error. The
+ ** conditional above is really:
+ ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize]
+ ** but is recast into its current form to avoid integer overflow problems
+ */
return SQLITE_CORRUPT_BKPT;
}
@@ -52126,7 +62729,7 @@ static int accessPayload(
if( a+offset>pCur->info.nLocal ){
a = pCur->info.nLocal - offset;
}
- rc = copyPayload(&aPayload[offset], pBuf, a, eOp, pPage->pDbPage);
+ rc = copyPayload(&aPayload[offset], pBuf, a, (eOp & 0x01), pPage->pDbPage);
offset = 0;
pBuf += a;
amt -= a;
@@ -52134,27 +62737,37 @@ static int accessPayload(
offset -= pCur->info.nLocal;
}
+
if( rc==SQLITE_OK && amt>0 ){
const u32 ovflSize = pBt->usableSize - 4; /* Bytes content per ovfl page */
Pgno nextPage;
nextPage = get4byte(&aPayload[pCur->info.nLocal]);
-#ifndef SQLITE_OMIT_INCRBLOB
- /* If the isIncrblobHandle flag is set and the BtCursor.aOverflow[]
- ** has not been allocated, allocate it now. The array is sized at
- ** one entry for each overflow page in the overflow chain. The
- ** page number of the first overflow page is stored in aOverflow[0],
- ** etc. A value of 0 in the aOverflow[] array means "not yet known"
- ** (the cache is lazily populated).
+ /* If the BtCursor.aOverflow[] has not been allocated, allocate it now.
+ ** Except, do not allocate aOverflow[] for eOp==2.
+ **
+ ** The aOverflow[] array is sized at one entry for each overflow page
+ ** in the overflow chain. The page number of the first overflow page is
+ ** stored in aOverflow[0], etc. A value of 0 in the aOverflow[] array
+ ** means "not yet known" (the cache is lazily populated).
*/
- if( pCur->isIncrblobHandle && !pCur->aOverflow ){
+ if( eOp!=2 && (pCur->curFlags & BTCF_ValidOvfl)==0 ){
int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize;
- pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl);
- /* nOvfl is always positive. If it were zero, fetchPayload would have
- ** been used instead of this routine. */
- if( ALWAYS(nOvfl) && !pCur->aOverflow ){
- rc = SQLITE_NOMEM;
+ if( nOvfl>pCur->nOvflAlloc ){
+ Pgno *aNew = (Pgno*)sqlite3Realloc(
+ pCur->aOverflow, nOvfl*2*sizeof(Pgno)
+ );
+ if( aNew==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ pCur->nOvflAlloc = nOvfl*2;
+ pCur->aOverflow = aNew;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ memset(pCur->aOverflow, 0, nOvfl*sizeof(Pgno));
+ pCur->curFlags |= BTCF_ValidOvfl;
}
}
@@ -52162,22 +62775,23 @@ static int accessPayload(
** entry for the first required overflow page is valid, skip
** directly to it.
*/
- if( pCur->aOverflow && pCur->aOverflow[offset/ovflSize] ){
+ if( (pCur->curFlags & BTCF_ValidOvfl)!=0
+ && pCur->aOverflow[offset/ovflSize]
+ ){
iIdx = (offset/ovflSize);
nextPage = pCur->aOverflow[iIdx];
offset = (offset%ovflSize);
}
-#endif
for( ; rc==SQLITE_OK && amt>0 && nextPage; iIdx++){
-#ifndef SQLITE_OMIT_INCRBLOB
/* If required, populate the overflow page-list cache. */
- if( pCur->aOverflow ){
- assert(!pCur->aOverflow[iIdx] || pCur->aOverflow[iIdx]==nextPage);
+ if( (pCur->curFlags & BTCF_ValidOvfl)!=0 ){
+ assert( pCur->aOverflow[iIdx]==0
+ || pCur->aOverflow[iIdx]==nextPage
+ || CORRUPT_DB );
pCur->aOverflow[iIdx] = nextPage;
}
-#endif
if( offset>=ovflSize ){
/* The only reason to read this page is to obtain the page
@@ -52185,33 +62799,79 @@ static int accessPayload(
** data is not required. So first try to lookup the overflow
** page-list cache, if any, then fall back to the getOverflowPage()
** function.
+ **
+ ** Note that the aOverflow[] array must be allocated because eOp!=2
+ ** here. If eOp==2, then offset==0 and this branch is never taken.
*/
-#ifndef SQLITE_OMIT_INCRBLOB
- if( pCur->aOverflow && pCur->aOverflow[iIdx+1] ){
+ assert( eOp!=2 );
+ assert( pCur->curFlags & BTCF_ValidOvfl );
+ assert( pCur->pBtree->db==pBt->db );
+ if( pCur->aOverflow[iIdx+1] ){
nextPage = pCur->aOverflow[iIdx+1];
- } else
-#endif
+ }else{
rc = getOverflowPage(pBt, nextPage, 0, &nextPage);
+ }
offset -= ovflSize;
}else{
/* Need to read this page properly. It contains some of the
** range of data that is being read (eOp==0) or written (eOp!=0).
*/
- DbPage *pDbPage;
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ sqlite3_file *fd;
+#endif
int a = amt;
- rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage);
- if( rc==SQLITE_OK ){
- aPayload = sqlite3PagerGetData(pDbPage);
- nextPage = get4byte(aPayload);
- if( a + offset > ovflSize ){
- a = ovflSize - offset;
+ if( a + offset > ovflSize ){
+ a = ovflSize - offset;
+ }
+
+#ifdef SQLITE_DIRECT_OVERFLOW_READ
+ /* If all the following are true:
+ **
+ ** 1) this is a read operation, and
+ ** 2) data is required from the start of this overflow page, and
+ ** 3) the database is file-backed, and
+ ** 4) there is no open write-transaction, and
+ ** 5) the database is not a WAL database,
+ ** 6) all data from the page is being read.
+ ** 7) at least 4 bytes have already been read into the output buffer
+ **
+ ** then data can be read directly from the database file into the
+ ** output buffer, bypassing the page-cache altogether. This speeds
+ ** up loading large records that span many overflow pages.
+ */
+ if( (eOp&0x01)==0 /* (1) */
+ && offset==0 /* (2) */
+ && (bEnd || a==ovflSize) /* (6) */
+ && pBt->inTransaction==TRANS_READ /* (4) */
+ && (fd = sqlite3PagerFile(pBt->pPager))->pMethods /* (3) */
+ && 0==sqlite3PagerUseWal(pBt->pPager) /* (5) */
+ && &pBuf[-4]>=pBufStart /* (7) */
+ ){
+ u8 aSave[4];
+ u8 *aWrite = &pBuf[-4];
+ assert( aWrite>=pBufStart ); /* hence (7) */
+ memcpy(aSave, aWrite, 4);
+ rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
+ nextPage = get4byte(aWrite);
+ memcpy(aWrite, aSave, 4);
+ }else
+#endif
+
+ {
+ DbPage *pDbPage;
+ rc = sqlite3PagerGet(pBt->pPager, nextPage, &pDbPage,
+ ((eOp&0x01)==0 ? PAGER_GET_READONLY : 0)
+ );
+ if( rc==SQLITE_OK ){
+ aPayload = sqlite3PagerGetData(pDbPage);
+ nextPage = get4byte(aPayload);
+ rc = copyPayload(&aPayload[offset+4], pBuf, a, (eOp&0x01), pDbPage);
+ sqlite3PagerUnref(pDbPage);
+ offset = 0;
}
- rc = copyPayload(&aPayload[offset+4], pBuf, a, eOp, pDbPage);
- sqlite3PagerUnref(pDbPage);
- offset = 0;
- amt -= a;
- pBuf += a;
}
+ amt -= a;
+ pBuf += a;
}
}
}
@@ -52224,7 +62884,7 @@ static int accessPayload(
/*
** Read part of the key associated with cursor pCur. Exactly
-** "amt" bytes will be transfered into pBuf[]. The transfer
+** "amt" bytes will be transferred into pBuf[]. The transfer
** begins at "offset".
**
** The caller must ensure that pCur is pointing to a valid row
@@ -52260,7 +62920,7 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p
}
#endif
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
rc = restoreCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
@@ -52274,10 +62934,10 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p
/*
** Return a pointer to payload information from the entry that the
** pCur cursor is pointing to. The pointer is to the beginning of
-** the key if skipKey==0 and it points to the beginning of data if
-** skipKey==1. The number of bytes of available key/data is written
-** into *pAmt. If *pAmt==0, then the value returned will not be
-** a valid pointer.
+** the key if index btrees (pPage->intKey==0) and is the data for
+** table btrees (pPage->intKey==1). The number of bytes of available
+** key/data is written into *pAmt. If *pAmt==0, then the value
+** returned will not be a valid pointer.
**
** This routine is an optimization. It is common for the entire key
** and data to fit on the local page and for there to be no overflow
@@ -52290,41 +62950,23 @@ SQLITE_PRIVATE int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *p
** page of the database. The data might change or move the next time
** any btree routine is called.
*/
-static const unsigned char *fetchPayload(
+static const void *fetchPayload(
BtCursor *pCur, /* Cursor pointing to entry to read from */
- int *pAmt, /* Write the number of available bytes here */
- int skipKey /* read beginning at data if this is true */
+ u32 *pAmt /* Write the number of available bytes here */
){
- unsigned char *aPayload;
- MemPage *pPage;
- u32 nKey;
- u32 nLocal;
-
+ u32 amt;
assert( pCur!=0 && pCur->iPage>=0 && pCur->apPage[pCur->iPage]);
assert( pCur->eState==CURSOR_VALID );
- assert( cursorHoldsMutex(pCur) );
- pPage = pCur->apPage[pCur->iPage];
- assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
- if( NEVER(pCur->info.nSize==0) ){
- btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage],
- &pCur->info);
- }
- aPayload = pCur->info.pCell;
- aPayload += pCur->info.nHeader;
- if( pPage->intKey ){
- nKey = 0;
- }else{
- nKey = (int)pCur->info.nKey;
- }
- if( skipKey ){
- aPayload += nKey;
- nLocal = pCur->info.nLocal - nKey;
- }else{
- nLocal = pCur->info.nLocal;
- assert( nLocal<=nKey );
- }
- *pAmt = nLocal;
- return aPayload;
+ assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
+ assert( cursorOwnsBtShared(pCur) );
+ assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ assert( pCur->info.nSize>0 );
+ assert( pCur->info.pPayload>pCur->apPage[pCur->iPage]->aData || CORRUPT_DB );
+ assert( pCur->info.pPayload<pCur->apPage[pCur->iPage]->aDataEnd ||CORRUPT_DB);
+ amt = (int)(pCur->apPage[pCur->iPage]->aDataEnd - pCur->info.pPayload);
+ if( pCur->info.nLocal<amt ) amt = pCur->info.nLocal;
+ *pAmt = amt;
+ return (void*)pCur->info.pPayload;
}
@@ -52342,23 +62984,8 @@ static const unsigned char *fetchPayload(
** These routines is used to get quick access to key and data
** in the common case where no overflow pages are used.
*/
-SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
- const void *p = 0;
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- assert( cursorHoldsMutex(pCur) );
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- p = (const void*)fetchPayload(pCur, pAmt, 0);
- }
- return p;
-}
-SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
- const void *p = 0;
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- assert( cursorHoldsMutex(pCur) );
- if( ALWAYS(pCur->eState==CURSOR_VALID) ){
- p = (const void*)fetchPayload(pCur, pAmt, 1);
- }
- return p;
+SQLITE_PRIVATE const void *sqlite3BtreePayloadFetch(BtCursor *pCur, u32 *pAmt){
+ return fetchPayload(pCur, pAmt);
}
@@ -52372,32 +62999,24 @@ SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
** vice-versa).
*/
static int moveToChild(BtCursor *pCur, u32 newPgno){
- int rc;
- int i = pCur->iPage;
- MemPage *pNewPage;
BtShared *pBt = pCur->pBt;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage<BTCURSOR_MAX_DEPTH );
+ assert( pCur->iPage>=0 );
if( pCur->iPage>=(BTCURSOR_MAX_DEPTH-1) ){
return SQLITE_CORRUPT_BKPT;
}
- rc = getAndInitPage(pBt, newPgno, &pNewPage);
- if( rc ) return rc;
- pCur->apPage[i+1] = pNewPage;
- pCur->aiIdx[i+1] = 0;
- pCur->iPage++;
-
pCur->info.nSize = 0;
- pCur->validNKey = 0;
- if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){
- return SQLITE_CORRUPT_BKPT;
- }
- return SQLITE_OK;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
+ pCur->iPage++;
+ pCur->aiIdx[pCur->iPage] = 0;
+ return getAndInitPage(pBt, newPgno, &pCur->apPage[pCur->iPage],
+ pCur, pCur->curPagerFlags);
}
-#ifndef NDEBUG
+#if SQLITE_DEBUG
/*
** Page pParent is an internal (non-leaf) tree page. This function
** asserts that page number iChild is the left-child if the iIdx'th
@@ -52406,6 +63025,8 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
** the page.
*/
static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){
+ if( CORRUPT_DB ) return; /* The conditions tested below might not be true
+ ** in a corrupt database */
assert( iIdx<=pParent->nCell );
if( iIdx==pParent->nCell ){
assert( get4byte(&pParent->aData[pParent->hdrOffset+8])==iChild );
@@ -52426,7 +63047,7 @@ static void assertParentIndex(MemPage *pParent, int iIdx, Pgno iChild){
** the largest cell index.
*/
static void moveToParent(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
assert( pCur->iPage>0 );
assert( pCur->apPage[pCur->iPage] );
@@ -52435,10 +63056,10 @@ static void moveToParent(BtCursor *pCur){
pCur->aiIdx[pCur->iPage-1],
pCur->apPage[pCur->iPage]->pgno
);
- releasePage(pCur->apPage[pCur->iPage]);
- pCur->iPage--;
+ testcase( pCur->aiIdx[pCur->iPage-1] > pCur->apPage[pCur->iPage-1]->nCell );
pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
+ releasePageNotNull(pCur->apPage[pCur->iPage--]);
}
/*
@@ -52465,10 +63086,8 @@ static void moveToParent(BtCursor *pCur){
static int moveToRoot(BtCursor *pCur){
MemPage *pRoot;
int rc = SQLITE_OK;
- Btree *p = pCur->pBtree;
- BtShared *pBt = p->pBt;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( CURSOR_INVALID < CURSOR_REQUIRESEEK );
assert( CURSOR_VALID < CURSOR_REQUIRESEEK );
assert( CURSOR_FAULT > CURSOR_REQUIRESEEK );
@@ -52481,55 +63100,56 @@ static int moveToRoot(BtCursor *pCur){
}
if( pCur->iPage>=0 ){
- int i;
- for(i=1; i<=pCur->iPage; i++){
- releasePage(pCur->apPage[i]);
+ while( pCur->iPage ){
+ assert( pCur->apPage[pCur->iPage]!=0 );
+ releasePageNotNull(pCur->apPage[pCur->iPage--]);
}
- pCur->iPage = 0;
}else if( pCur->pgnoRoot==0 ){
pCur->eState = CURSOR_INVALID;
return SQLITE_OK;
}else{
- rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]);
+ assert( pCur->iPage==(-1) );
+ rc = getAndInitPage(pCur->pBtree->pBt, pCur->pgnoRoot, &pCur->apPage[0],
+ 0, pCur->curPagerFlags);
if( rc!=SQLITE_OK ){
pCur->eState = CURSOR_INVALID;
return rc;
}
pCur->iPage = 0;
-
- /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
- ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
- ** NULL, the caller expects a table b-tree. If this is not the case,
- ** return an SQLITE_CORRUPT error. */
- assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 );
- if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){
- return SQLITE_CORRUPT_BKPT;
- }
+ pCur->curIntKey = pCur->apPage[0]->intKey;
}
-
- /* Assert that the root page is of the correct type. This must be the
- ** case as the call to this function that loaded the root-page (either
- ** this call or a previous invocation) would have detected corruption
- ** if the assumption were not true, and it is not possible for the flags
- ** byte to have been modified while this cursor is holding a reference
- ** to the page. */
pRoot = pCur->apPage[0];
assert( pRoot->pgno==pCur->pgnoRoot );
- assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey );
+
+ /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor
+ ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is
+ ** NULL, the caller expects a table b-tree. If this is not the case,
+ ** return an SQLITE_CORRUPT error.
+ **
+ ** Earlier versions of SQLite assumed that this test could not fail
+ ** if the root page was already loaded when this function was called (i.e.
+ ** if pCur->iPage>=0). But this is not so if the database is corrupted
+ ** in such a way that page pRoot is linked into a second b-tree table
+ ** (or the freelist). */
+ assert( pRoot->intKey==1 || pRoot->intKey==0 );
+ if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){
+ return SQLITE_CORRUPT_BKPT;
+ }
pCur->aiIdx[0] = 0;
pCur->info.nSize = 0;
- pCur->atLast = 0;
- pCur->validNKey = 0;
+ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidNKey|BTCF_ValidOvfl);
- if( pRoot->nCell==0 && !pRoot->leaf ){
+ if( pRoot->nCell>0 ){
+ pCur->eState = CURSOR_VALID;
+ }else if( !pRoot->leaf ){
Pgno subpage;
if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT;
subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]);
pCur->eState = CURSOR_VALID;
rc = moveToChild(pCur, subpage);
}else{
- pCur->eState = ((pRoot->nCell>0)?CURSOR_VALID:CURSOR_INVALID);
+ pCur->eState = CURSOR_INVALID;
}
return rc;
}
@@ -52546,7 +63166,7 @@ static int moveToLeftmost(BtCursor *pCur){
int rc = SQLITE_OK;
MemPage *pPage;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){
assert( pCur->aiIdx[pCur->iPage]<pPage->nCell );
@@ -52571,19 +63191,18 @@ static int moveToRightmost(BtCursor *pCur){
int rc = SQLITE_OK;
MemPage *pPage = 0;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pCur->eState==CURSOR_VALID );
- while( rc==SQLITE_OK && !(pPage = pCur->apPage[pCur->iPage])->leaf ){
+ while( !(pPage = pCur->apPage[pCur->iPage])->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
pCur->aiIdx[pCur->iPage] = pPage->nCell;
rc = moveToChild(pCur, pgno);
+ if( rc ) return rc;
}
- if( rc==SQLITE_OK ){
- pCur->aiIdx[pCur->iPage] = pPage->nCell-1;
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
- }
- return rc;
+ pCur->aiIdx[pCur->iPage] = pPage->nCell-1;
+ assert( pCur->info.nSize==0 );
+ assert( (pCur->curFlags & BTCF_ValidNKey)==0 );
+ return SQLITE_OK;
}
/* Move the cursor to the first entry in the table. Return SQLITE_OK
@@ -52593,7 +63212,7 @@ static int moveToRightmost(BtCursor *pCur){
SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
int rc;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
rc = moveToRoot(pCur);
if( rc==SQLITE_OK ){
@@ -52616,11 +63235,11 @@ SQLITE_PRIVATE int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
/* If the cursor already points to the last entry, this is a no-op. */
- if( CURSOR_VALID==pCur->eState && pCur->atLast ){
+ if( CURSOR_VALID==pCur->eState && (pCur->curFlags & BTCF_AtLast)!=0 ){
#ifdef SQLITE_DEBUG
/* This block serves to assert() that the cursor really does point
** to the last entry in the b-tree. */
@@ -52643,7 +63262,12 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
assert( pCur->eState==CURSOR_VALID );
*pRes = 0;
rc = moveToRightmost(pCur);
- pCur->atLast = rc==SQLITE_OK ?1:0;
+ if( rc==SQLITE_OK ){
+ pCur->curFlags |= BTCF_AtLast;
+ }else{
+ pCur->curFlags &= ~BTCF_AtLast;
+ }
+
}
}
return rc;
@@ -52676,6 +63300,8 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
** *pRes>0 The cursor is left pointing at an entry that
** is larger than intKey/pIdxKey.
**
+** For index tables, the pIdxKey->eqSeen field is set to 1 if there
+** exists an entry in the table that exactly matches pIdxKey.
*/
SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
BtCursor *pCur, /* The cursor to be moved */
@@ -52685,27 +63311,40 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
int *pRes /* Write search results here */
){
int rc;
+ RecordCompare xRecordCompare;
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
assert( pRes );
assert( (pIdxKey==0)==(pCur->pKeyInfo==0) );
+ assert( pCur->eState!=CURSOR_VALID || (pIdxKey==0)==(pCur->curIntKey!=0) );
/* If the cursor is already positioned at the point we are trying
** to move to, then just return without doing any work */
- if( pCur->eState==CURSOR_VALID && pCur->validNKey
- && pCur->apPage[0]->intKey
+ if( pIdxKey==0
+ && pCur->eState==CURSOR_VALID && (pCur->curFlags & BTCF_ValidNKey)!=0
){
if( pCur->info.nKey==intKey ){
*pRes = 0;
return SQLITE_OK;
}
- if( pCur->atLast && pCur->info.nKey<intKey ){
+ if( (pCur->curFlags & BTCF_AtLast)!=0 && pCur->info.nKey<intKey ){
*pRes = -1;
return SQLITE_OK;
}
}
+ if( pIdxKey ){
+ xRecordCompare = sqlite3VdbeFindCompare(pIdxKey);
+ pIdxKey->errCode = 0;
+ assert( pIdxKey->default_rc==1
+ || pIdxKey->default_rc==0
+ || pIdxKey->default_rc==-1
+ );
+ }else{
+ xRecordCompare = 0; /* All keys are integers */
+ }
+
rc = moveToRoot(pCur);
if( rc ){
return rc;
@@ -52718,12 +63357,13 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
assert( pCur->pgnoRoot==0 || pCur->apPage[pCur->iPage]->nCell==0 );
return SQLITE_OK;
}
- assert( pCur->apPage[0]->intKey || pIdxKey );
+ assert( pCur->apPage[0]->intKey==pCur->curIntKey );
+ assert( pCur->curIntKey || pIdxKey );
for(;;){
- int lwr, upr, idx;
+ int lwr, upr, idx, c;
Pgno chldPg;
MemPage *pPage = pCur->apPage[pCur->iPage];
- int c;
+ u8 *pCell; /* Pointer to current cell in pPage */
/* pPage->nCell must be greater than zero. If this is the root-page
** the cursor would have been INVALID above and this for(;;) loop
@@ -52735,35 +63375,47 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
assert( pPage->intKey==(pIdxKey==0) );
lwr = 0;
upr = pPage->nCell-1;
- if( biasRight ){
- pCur->aiIdx[pCur->iPage] = (u16)(idx = upr);
- }else{
- pCur->aiIdx[pCur->iPage] = (u16)(idx = (upr+lwr)/2);
- }
- for(;;){
- u8 *pCell; /* Pointer to current cell in pPage */
-
- assert( idx==pCur->aiIdx[pCur->iPage] );
- pCur->info.nSize = 0;
- pCell = findCell(pPage, idx) + pPage->childPtrSize;
- if( pPage->intKey ){
+ assert( biasRight==0 || biasRight==1 );
+ idx = upr>>(1-biasRight); /* idx = biasRight ? upr : (lwr+upr)/2; */
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( xRecordCompare==0 ){
+ for(;;){
i64 nCellKey;
- if( pPage->hasData ){
- u32 dummy;
- pCell += getVarint32(pCell, dummy);
+ pCell = findCellPastPtr(pPage, idx);
+ if( pPage->intKeyLeaf ){
+ while( 0x80 <= *(pCell++) ){
+ if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT;
+ }
}
getVarint(pCell, (u64*)&nCellKey);
- if( nCellKey==intKey ){
- c = 0;
- }else if( nCellKey<intKey ){
- c = -1;
+ if( nCellKey<intKey ){
+ lwr = idx+1;
+ if( lwr>upr ){ c = -1; break; }
+ }else if( nCellKey>intKey ){
+ upr = idx-1;
+ if( lwr>upr ){ c = +1; break; }
}else{
- assert( nCellKey>intKey );
- c = +1;
+ assert( nCellKey==intKey );
+ pCur->curFlags |= BTCF_ValidNKey;
+ pCur->info.nKey = nCellKey;
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( !pPage->leaf ){
+ lwr = idx;
+ goto moveto_next_layer;
+ }else{
+ *pRes = 0;
+ rc = SQLITE_OK;
+ goto moveto_finish;
+ }
}
- pCur->validNKey = 1;
- pCur->info.nKey = nCellKey;
- }else{
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2; */
+ }
+ }else{
+ for(;;){
+ int nCell; /* Size of the pCell cell in bytes */
+ pCell = findCellPastPtr(pPage, idx);
+
/* The maximum supported page-size is 65536 bytes. This means that
** the maximum number of record bytes stored on an index B-Tree
** page is less than 16384 bytes and may be stored as a 2-byte
@@ -52772,84 +63424,99 @@ SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked(
** stored entirely within the b-tree page by inspecting the first
** 2 bytes of the cell.
*/
- int nCell = pCell[0];
- if( !(nCell & 0x80) && nCell<=pPage->maxLocal ){
+ nCell = pCell[0];
+ if( nCell<=pPage->max1bytePayload ){
/* This branch runs if the record-size field of the cell is a
** single byte varint and the record fits entirely on the main
** b-tree page. */
- c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
+ testcase( pCell+nCell+1==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[1], pIdxKey);
}else if( !(pCell[1] & 0x80)
&& (nCell = ((nCell&0x7f)<<7) + pCell[1])<=pPage->maxLocal
){
/* The record-size field is a 2 byte varint and the record
** fits entirely on the main b-tree page. */
- c = sqlite3VdbeRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
+ testcase( pCell+nCell+2==pPage->aDataEnd );
+ c = xRecordCompare(nCell, (void*)&pCell[2], pIdxKey);
}else{
/* The record flows over onto one or more overflow pages. In
** this case the whole cell needs to be parsed, a buffer allocated
** and accessPayload() used to retrieve the record into the
- ** buffer before VdbeRecordCompare() can be called. */
+ ** buffer before VdbeRecordCompare() can be called.
+ **
+ ** If the record is corrupt, the xRecordCompare routine may read
+ ** up to two varints past the end of the buffer. An extra 18
+ ** bytes of padding is allocated at the end of the buffer in
+ ** case this happens. */
void *pCellKey;
u8 * const pCellBody = pCell - pPage->childPtrSize;
- btreeParseCellPtr(pPage, pCellBody, &pCur->info);
+ pPage->xParseCell(pPage, pCellBody, &pCur->info);
nCell = (int)pCur->info.nKey;
- pCellKey = sqlite3Malloc( nCell );
+ testcase( nCell<0 ); /* True if key size is 2^32 or more */
+ testcase( nCell==0 ); /* Invalid key size: 0x80 0x80 0x00 */
+ testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */
+ testcase( nCell==2 ); /* Minimum legal index key size */
+ if( nCell<2 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto moveto_finish;
+ }
+ pCellKey = sqlite3Malloc( nCell+18 );
if( pCellKey==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto moveto_finish;
}
- rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0);
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 2);
if( rc ){
sqlite3_free(pCellKey);
goto moveto_finish;
}
- c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey);
+ c = xRecordCompare(nCell, pCellKey, pIdxKey);
sqlite3_free(pCellKey);
}
- }
- if( c==0 ){
- if( pPage->intKey && !pPage->leaf ){
- lwr = idx;
- upr = lwr - 1;
- break;
+ assert(
+ (pIdxKey->errCode!=SQLITE_CORRUPT || c==0)
+ && (pIdxKey->errCode!=SQLITE_NOMEM || pCur->pBtree->db->mallocFailed)
+ );
+ if( c<0 ){
+ lwr = idx+1;
+ }else if( c>0 ){
+ upr = idx-1;
}else{
+ assert( c==0 );
*pRes = 0;
rc = SQLITE_OK;
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
+ if( pIdxKey->errCode ) rc = SQLITE_CORRUPT;
goto moveto_finish;
}
+ if( lwr>upr ) break;
+ assert( lwr+upr>=0 );
+ idx = (lwr+upr)>>1; /* idx = (lwr+upr)/2 */
}
- if( c<0 ){
- lwr = idx+1;
- }else{
- upr = idx-1;
- }
- if( lwr>upr ){
- break;
- }
- pCur->aiIdx[pCur->iPage] = (u16)(idx = (lwr+upr)/2);
}
- assert( lwr==upr+1 );
+ assert( lwr==upr+1 || (pPage->intKey && !pPage->leaf) );
assert( pPage->isInit );
if( pPage->leaf ){
- chldPg = 0;
- }else if( lwr>=pPage->nCell ){
- chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- }else{
- chldPg = get4byte(findCell(pPage, lwr));
- }
- if( chldPg==0 ){
assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ pCur->aiIdx[pCur->iPage] = (u16)idx;
*pRes = c;
rc = SQLITE_OK;
goto moveto_finish;
}
+moveto_next_layer:
+ if( lwr>=pPage->nCell ){
+ chldPg = get4byte(&pPage->aData[pPage->hdrOffset+8]);
+ }else{
+ chldPg = get4byte(findCell(pPage, lwr));
+ }
pCur->aiIdx[pCur->iPage] = (u16)lwr;
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
rc = moveToChild(pCur, chldPg);
- if( rc ) goto moveto_finish;
+ if( rc ) break;
}
moveto_finish:
+ pCur->info.nSize = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
return rc;
}
@@ -52874,43 +63541,67 @@ SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor *pCur){
** successful then set *pRes=0. If the cursor
** was already pointing to the last entry in the database before
** this routine was called, then set *pRes=1.
-*/
-SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+**
+** The main entry point is sqlite3BtreeNext(). That routine is optimized
+** for the common case of merely incrementing the cell counter BtCursor.aiIdx
+** to the next cell on the current page. The (slower) btreeNext() helper
+** routine is called when it is necessary to move to a different page or
+** to restore the cursor.
+**
+** The calling function will set *pRes to 0 or 1. The initial *pRes value
+** will be 1 if the cursor being stepped corresponds to an SQL index and
+** if this routine could have been skipped if that SQL index had been
+** a unique index. Otherwise the caller will have set *pRes to zero.
+** Zero is the common case. The btree implementation is free to use the
+** initial *pRes value as a hint to improve performance, but the current
+** SQLite btree implementation does not. (Note that the comdb2 btree
+** implementation does use this hint, however.)
+*/
+static SQLITE_NOINLINE int btreeNext(BtCursor *pCur, int *pRes){
int rc;
int idx;
MemPage *pPage;
- assert( cursorHoldsMutex(pCur) );
- rc = restoreCursorPosition(pCur);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- assert( pRes!=0 );
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
- if( pCur->skipNext>0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ assert( cursorOwnsBtShared(pCur) );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ assert( *pRes==0 );
+ if( pCur->eState!=CURSOR_VALID ){
+ assert( (pCur->curFlags & BTCF_ValidOvfl)==0 );
+ rc = restoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext>0 ){
+ pCur->skipNext = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
idx = ++pCur->aiIdx[pCur->iPage];
assert( pPage->isInit );
- assert( idx<=pPage->nCell );
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ /* If the database file is corrupt, it is possible for the value of idx
+ ** to be invalid here. This can only occur if a second cursor modifies
+ ** the page while cursor pCur is holding a reference to it. Which can
+ ** only happen if the database is corrupt in such a way as to link the
+ ** page into more than one b-tree structure. */
+ testcase( idx>pPage->nCell );
+
if( idx>=pPage->nCell ){
if( !pPage->leaf ){
rc = moveToChild(pCur, get4byte(&pPage->aData[pPage->hdrOffset+8]));
if( rc ) return rc;
- rc = moveToLeftmost(pCur);
- *pRes = 0;
- return rc;
+ return moveToLeftmost(pCur);
}
do{
if( pCur->iPage==0 ){
@@ -52921,58 +63612,97 @@ SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
moveToParent(pCur);
pPage = pCur->apPage[pCur->iPage];
}while( pCur->aiIdx[pCur->iPage]>=pPage->nCell );
- *pRes = 0;
if( pPage->intKey ){
- rc = sqlite3BtreeNext(pCur, pRes);
+ return sqlite3BtreeNext(pCur, pRes);
}else{
- rc = SQLITE_OK;
+ return SQLITE_OK;
}
- return rc;
}
+ if( pPage->leaf ){
+ return SQLITE_OK;
+ }else{
+ return moveToLeftmost(pCur);
+ }
+}
+SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+ MemPage *pPage;
+ assert( cursorOwnsBtShared(pCur) );
+ assert( pRes!=0 );
+ assert( *pRes==0 || *pRes==1 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ pCur->info.nSize = 0;
+ pCur->curFlags &= ~(BTCF_ValidNKey|BTCF_ValidOvfl);
*pRes = 0;
+ if( pCur->eState!=CURSOR_VALID ) return btreeNext(pCur, pRes);
+ pPage = pCur->apPage[pCur->iPage];
+ if( (++pCur->aiIdx[pCur->iPage])>=pPage->nCell ){
+ pCur->aiIdx[pCur->iPage]--;
+ return btreeNext(pCur, pRes);
+ }
if( pPage->leaf ){
return SQLITE_OK;
+ }else{
+ return moveToLeftmost(pCur);
}
- rc = moveToLeftmost(pCur);
- return rc;
}
-
/*
** Step the cursor to the back to the previous entry in the database. If
** successful then set *pRes=0. If the cursor
** was already pointing to the first entry in the database before
** this routine was called, then set *pRes=1.
-*/
-SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+**
+** The main entry point is sqlite3BtreePrevious(). That routine is optimized
+** for the common case of merely decrementing the cell counter BtCursor.aiIdx
+** to the previous cell on the current page. The (slower) btreePrevious()
+** helper routine is called when it is necessary to move to a different page
+** or to restore the cursor.
+**
+** The calling function will set *pRes to 0 or 1. The initial *pRes value
+** will be 1 if the cursor being stepped corresponds to an SQL index and
+** if this routine could have been skipped if that SQL index had been
+** a unique index. Otherwise the caller will have set *pRes to zero.
+** Zero is the common case. The btree implementation is free to use the
+** initial *pRes value as a hint to improve performance, but the current
+** SQLite btree implementation does not. (Note that the comdb2 btree
+** implementation does use this hint, however.)
+*/
+static SQLITE_NOINLINE int btreePrevious(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage;
- assert( cursorHoldsMutex(pCur) );
- rc = restoreCursorPosition(pCur);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- pCur->atLast = 0;
- if( CURSOR_INVALID==pCur->eState ){
- *pRes = 1;
- return SQLITE_OK;
- }
- if( pCur->skipNext<0 ){
- pCur->skipNext = 0;
- *pRes = 0;
- return SQLITE_OK;
+ assert( cursorOwnsBtShared(pCur) );
+ assert( pRes!=0 );
+ assert( *pRes==0 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ assert( (pCur->curFlags & (BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey))==0 );
+ assert( pCur->info.nSize==0 );
+ if( pCur->eState!=CURSOR_VALID ){
+ rc = restoreCursorPosition(pCur);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ if( CURSOR_INVALID==pCur->eState ){
+ *pRes = 1;
+ return SQLITE_OK;
+ }
+ if( pCur->skipNext ){
+ assert( pCur->eState==CURSOR_VALID || pCur->eState==CURSOR_SKIPNEXT );
+ pCur->eState = CURSOR_VALID;
+ if( pCur->skipNext<0 ){
+ pCur->skipNext = 0;
+ return SQLITE_OK;
+ }
+ pCur->skipNext = 0;
+ }
}
- pCur->skipNext = 0;
pPage = pCur->apPage[pCur->iPage];
assert( pPage->isInit );
if( !pPage->leaf ){
int idx = pCur->aiIdx[pCur->iPage];
rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
- if( rc ){
- return rc;
- }
+ if( rc ) return rc;
rc = moveToRightmost(pCur);
}else{
while( pCur->aiIdx[pCur->iPage]==0 ){
@@ -52983,8 +63713,8 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
}
moveToParent(pCur);
}
- pCur->info.nSize = 0;
- pCur->validNKey = 0;
+ assert( pCur->info.nSize==0 );
+ assert( (pCur->curFlags & (BTCF_ValidNKey|BTCF_ValidOvfl))==0 );
pCur->aiIdx[pCur->iPage]--;
pPage = pCur->apPage[pCur->iPage];
@@ -52994,9 +63724,25 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
rc = SQLITE_OK;
}
}
- *pRes = 0;
return rc;
}
+SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+ assert( cursorOwnsBtShared(pCur) );
+ assert( pRes!=0 );
+ assert( *pRes==0 || *pRes==1 );
+ assert( pCur->skipNext==0 || pCur->eState!=CURSOR_VALID );
+ *pRes = 0;
+ pCur->curFlags &= ~(BTCF_AtLast|BTCF_ValidOvfl|BTCF_ValidNKey);
+ pCur->info.nSize = 0;
+ if( pCur->eState!=CURSOR_VALID
+ || pCur->aiIdx[pCur->iPage]==0
+ || pCur->apPage[pCur->iPage]->leaf==0
+ ){
+ return btreePrevious(pCur, pRes);
+ }
+ pCur->aiIdx[pCur->iPage]--;
+ return SQLITE_OK;
+}
/*
** Allocate a new page from the database file.
@@ -53007,24 +63753,25 @@ SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
** sqlite3PagerUnref() on the new page when it is done.
**
** SQLITE_OK is returned on success. Any other return value indicates
-** an error. *ppPage and *pPgno are undefined in the event of an error.
-** Do not invoke sqlite3PagerUnref() on *ppPage if an error is returned.
+** an error. *ppPage is set to NULL in the event of an error.
**
-** If the "nearby" parameter is not 0, then a (feeble) effort is made to
+** If the "nearby" parameter is not 0, then an effort is made to
** locate a page close to the page number "nearby". This can be used in an
** attempt to keep related pages close to each other in the database file,
** which in turn can make database access faster.
**
-** If the "exact" parameter is not 0, and the page-number nearby exists
-** anywhere on the free-list, then it is guarenteed to be returned. This
-** is only used by auto-vacuum databases when allocating a new table.
+** If the eMode parameter is BTALLOC_EXACT and the nearby page exists
+** anywhere on the free-list, then it is guaranteed to be returned. If
+** eMode is BTALLOC_LT then the page returned will be less than or equal
+** to nearby if any such page exists. If eMode is BTALLOC_ANY then there
+** are no restrictions on which page is returned.
*/
static int allocateBtreePage(
- BtShared *pBt,
- MemPage **ppPage,
- Pgno *pPgno,
- Pgno nearby,
- u8 exact
+ BtShared *pBt, /* The btree */
+ MemPage **ppPage, /* Store pointer to the allocated page here */
+ Pgno *pPgno, /* Store the page number here */
+ Pgno nearby, /* Search for a page near this one */
+ u8 eMode /* BTALLOC_EXACT, BTALLOC_LT, or BTALLOC_ANY */
){
MemPage *pPage1;
int rc;
@@ -53035,8 +63782,11 @@ static int allocateBtreePage(
Pgno mxPage; /* Total size of the database file */
assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( eMode==BTALLOC_ANY || (nearby>0 && IfNotOmitAV(pBt->autoVacuum)) );
pPage1 = pBt->pPage1;
mxPage = btreePagecount(pBt);
+ /* EVIDENCE-OF: R-05119-02637 The 4-byte big-endian integer at offset 36
+ ** stores stores the total number of pages on the freelist. */
n = get4byte(&pPage1->aData[36]);
testcase( n==mxPage-1 );
if( n>=mxPage ){
@@ -53046,22 +63796,26 @@ static int allocateBtreePage(
/* There are pages on the freelist. Reuse one of those pages. */
Pgno iTrunk;
u8 searchList = 0; /* If the free-list must be searched for 'nearby' */
+ u32 nSearch = 0; /* Count of the number of search attempts */
- /* If the 'exact' parameter was true and a query of the pointer-map
+ /* If eMode==BTALLOC_EXACT and a query of the pointer-map
** shows that the page 'nearby' is somewhere on the free-list, then
** the entire-list will be searched for that page.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( exact && nearby<=mxPage ){
- u8 eType;
- assert( nearby>0 );
- assert( pBt->autoVacuum );
- rc = ptrmapGet(pBt, nearby, &eType, 0);
- if( rc ) return rc;
- if( eType==PTRMAP_FREEPAGE ){
- searchList = 1;
+ if( eMode==BTALLOC_EXACT ){
+ if( nearby<=mxPage ){
+ u8 eType;
+ assert( nearby>0 );
+ assert( pBt->autoVacuum );
+ rc = ptrmapGet(pBt, nearby, &eType, 0);
+ if( rc ) return rc;
+ if( eType==PTRMAP_FREEPAGE ){
+ searchList = 1;
+ }
}
- *pPgno = nearby;
+ }else if( eMode==BTALLOC_LE ){
+ searchList = 1;
}
#endif
@@ -53074,27 +63828,37 @@ static int allocateBtreePage(
/* The code within this loop is run only once if the 'searchList' variable
** is not true. Otherwise, it runs once for each trunk-page on the
- ** free-list until the page 'nearby' is located.
+ ** free-list until the page 'nearby' is located (eMode==BTALLOC_EXACT)
+ ** or until a page less than 'nearby' is located (eMode==BTALLOC_LT)
*/
do {
pPrevTrunk = pTrunk;
if( pPrevTrunk ){
+ /* EVIDENCE-OF: R-01506-11053 The first integer on a freelist trunk page
+ ** is the page number of the next freelist trunk page in the list or
+ ** zero if this is the last freelist trunk page. */
iTrunk = get4byte(&pPrevTrunk->aData[0]);
}else{
+ /* EVIDENCE-OF: R-59841-13798 The 4-byte big-endian integer at offset 32
+ ** stores the page number of the first page of the freelist, or zero if
+ ** the freelist is empty. */
iTrunk = get4byte(&pPage1->aData[32]);
}
testcase( iTrunk==mxPage );
- if( iTrunk>mxPage ){
+ if( iTrunk>mxPage || nSearch++ > n ){
rc = SQLITE_CORRUPT_BKPT;
}else{
- rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0);
+ rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0);
}
if( rc ){
pTrunk = 0;
goto end_allocate_page;
}
-
- k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */
+ assert( pTrunk!=0 );
+ assert( pTrunk->aData!=0 );
+ /* EVIDENCE-OF: R-13523-04394 The second integer on a freelist trunk page
+ ** is the number of leaf page pointers to follow. */
+ k = get4byte(&pTrunk->aData[4]);
if( k==0 && !searchList ){
/* The trunk has no leaves and the list is not being searched.
** So extract the trunk page itself and use it as the newly
@@ -53114,11 +63878,13 @@ static int allocateBtreePage(
rc = SQLITE_CORRUPT_BKPT;
goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM
- }else if( searchList && nearby==iTrunk ){
+ }else if( searchList
+ && (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE))
+ ){
/* The list is being searched and this trunk page is the page
** to allocate, regardless of whether it has leaves.
*/
- assert( *pPgno==iTrunk );
+ *pPgno = iTrunk;
*ppPage = pTrunk;
searchList = 0;
rc = sqlite3PagerWrite(pTrunk->pDbPage);
@@ -53147,7 +63913,7 @@ static int allocateBtreePage(
goto end_allocate_page;
}
testcase( iNewTrunk==mxPage );
- rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0);
+ rc = btreeGetUnusedPage(pBt, iNewTrunk, &pNewTrunk, 0);
if( rc!=SQLITE_OK ){
goto end_allocate_page;
}
@@ -53181,14 +63947,24 @@ static int allocateBtreePage(
unsigned char *aData = pTrunk->aData;
if( nearby>0 ){
u32 i;
- int dist;
closest = 0;
- dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
- for(i=1; i<k; i++){
- int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
- if( d2<dist ){
- closest = i;
- dist = d2;
+ if( eMode==BTALLOC_LE ){
+ for(i=0; i<k; i++){
+ iPage = get4byte(&aData[8+i*4]);
+ if( iPage<=nearby ){
+ closest = i;
+ break;
+ }
+ }
+ }else{
+ int dist;
+ dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
+ for(i=1; i<k; i++){
+ int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
+ if( d2<dist ){
+ closest = i;
+ dist = d2;
+ }
}
}
}else{
@@ -53202,7 +63978,9 @@ static int allocateBtreePage(
goto end_allocate_page;
}
testcase( iPage==mxPage );
- if( !searchList || iPage==nearby ){
+ if( !searchList
+ || (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
+ ){
int noContent;
*pPgno = iPage;
TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
@@ -53214,12 +63992,13 @@ static int allocateBtreePage(
memcpy(&aData[8+closest*4], &aData[4+k*4], 4);
}
put4byte(&aData[4], k-1);
- noContent = !btreeGetHasContent(pBt, *pPgno);
- rc = btreeGetPage(pBt, *pPgno, ppPage, noContent);
+ noContent = !btreeGetHasContent(pBt, *pPgno)? PAGER_GET_NOCONTENT : 0;
+ rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, noContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
+ *ppPage = 0;
}
}
searchList = 0;
@@ -53229,8 +64008,26 @@ static int allocateBtreePage(
pPrevTrunk = 0;
}while( searchList );
}else{
- /* There are no pages on the freelist, so create a new page at the
- ** end of the file */
+ /* There are no pages on the freelist, so append a new page to the
+ ** database image.
+ **
+ ** Normally, new pages allocated by this block can be requested from the
+ ** pager layer with the 'no-content' flag set. This prevents the pager
+ ** from trying to read the pages content from disk. However, if the
+ ** current transaction has already run one or more incremental-vacuum
+ ** steps, then the page we are about to allocate may contain content
+ ** that is required in the event of a rollback. In this case, do
+ ** not set the no-content flag. This causes the pager to load and journal
+ ** the current page content before overwriting it.
+ **
+ ** Note that the pager will not actually attempt to load or journal
+ ** content for any page that really does lie past the end of the database
+ ** file on disk. So the effects of disabling the no-content optimization
+ ** here are confined to those pages that lie between the end of the
+ ** database image and the end of the database file.
+ */
+ int bNoContent = (0==IfNotOmitAV(pBt->bDoTruncate))? PAGER_GET_NOCONTENT:0;
+
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc ) return rc;
pBt->nPage++;
@@ -53245,7 +64042,7 @@ static int allocateBtreePage(
MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1);
+ rc = btreeGetUnusedPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
@@ -53259,11 +64056,12 @@ static int allocateBtreePage(
*pPgno = pBt->nPage;
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
- rc = btreeGetPage(pBt, *pPgno, ppPage, 1);
+ rc = btreeGetUnusedPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){
releasePage(*ppPage);
+ *ppPage = 0;
}
TRACE(("ALLOCATE: %d from end of file\n", *pPgno));
}
@@ -53273,16 +64071,8 @@ static int allocateBtreePage(
end_allocate_page:
releasePage(pTrunk);
releasePage(pPrevTrunk);
- if( rc==SQLITE_OK ){
- if( sqlite3PagerPageRefcount((*ppPage)->pDbPage)>1 ){
- releasePage(*ppPage);
- return SQLITE_CORRUPT_BKPT;
- }
- (*ppPage)->isInit = 0;
- }else{
- *ppPage = 0;
- }
- assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) );
+ assert( rc!=SQLITE_OK || sqlite3PagerPageRefcount((*ppPage)->pDbPage)<=1 );
+ assert( rc!=SQLITE_OK || (*ppPage)->isInit==0 );
return rc;
}
@@ -53307,9 +64097,10 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
int nFree; /* Initial number of pages on free-list */
assert( sqlite3_mutex_held(pBt->mutex) );
- assert( iPage>1 );
+ assert( CORRUPT_DB || iPage>1 );
assert( !pMemPage || pMemPage->pgno==iPage );
+ if( iPage<2 ) return SQLITE_CORRUPT_BKPT;
if( pMemPage ){
pPage = pMemPage;
sqlite3PagerRef(pPage->pDbPage);
@@ -53323,7 +64114,7 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
nFree = get4byte(&pPage1->aData[36]);
put4byte(&pPage1->aData[36], nFree+1);
- if( pBt->secureDelete ){
+ if( pBt->btsFlags & BTS_SECURE_DELETE ){
/* If the secure_delete option is enabled, then
** always fully overwrite deleted information with zeros.
*/
@@ -53379,12 +64170,17 @@ static int freePage2(BtShared *pBt, MemPage *pMemPage, Pgno iPage){
** for now. At some point in the future (once everyone has upgraded
** to 3.6.0 or later) we should consider fixing the conditional above
** to read "usableSize/4-2" instead of "usableSize/4-8".
+ **
+ ** EVIDENCE-OF: R-19920-11576 However, newer versions of SQLite still
+ ** avoid using the last six entries in the freelist trunk page array in
+ ** order that database files created by newer versions of SQLite can be
+ ** read by older versions of SQLite.
*/
rc = sqlite3PagerWrite(pTrunk->pDbPage);
if( rc==SQLITE_OK ){
put4byte(&pTrunk->aData[4], nLeaf+1);
put4byte(&pTrunk->aData[8+nLeaf*4], iPage);
- if( pPage && !pBt->secureDelete ){
+ if( pPage && (pBt->btsFlags & BTS_SECURE_DELETE)==0 ){
sqlite3PagerDontWrite(pPage->pDbPage);
}
rc = btreeSetHasContent(pBt, iPage);
@@ -53427,9 +64223,15 @@ static void freePage(MemPage *pPage, int *pRC){
}
/*
-** Free any overflow pages associated with the given Cell.
+** Free any overflow pages associated with the given Cell. Write the
+** local Cell size (the number of bytes on the original page, omitting
+** overflow) into *pnSize.
*/
-static int clearCell(MemPage *pPage, unsigned char *pCell){
+static int clearCell(
+ MemPage *pPage, /* The page that contains the Cell */
+ unsigned char *pCell, /* First byte of the Cell */
+ u16 *pnSize /* Write the size of the Cell here */
+){
BtShared *pBt = pPage->pBt;
CellInfo info;
Pgno ovflPgno;
@@ -53438,18 +64240,21 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
u32 ovflPageSize;
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- btreeParseCellPtr(pPage, pCell, &info);
- if( info.iOverflow==0 ){
+ pPage->xParseCell(pPage, pCell, &info);
+ *pnSize = info.nSize;
+ if( info.nLocal==info.nPayload ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
}
- if( pCell+info.iOverflow+3 > pPage->aData+pPage->maskPage ){
- return SQLITE_CORRUPT; /* Cell extends past end of page */
+ if( pCell+info.nSize-1 > pPage->aData+pPage->maskPage ){
+ return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */
}
- ovflPgno = get4byte(&pCell[info.iOverflow]);
+ ovflPgno = get4byte(pCell + info.nSize - 4);
assert( pBt->usableSize > 4 );
ovflPageSize = pBt->usableSize - 4;
nOvfl = (info.nPayload - info.nLocal + ovflPageSize - 1)/ovflPageSize;
- assert( ovflPgno==0 || nOvfl>0 );
+ assert( nOvfl>0 ||
+ (CORRUPT_DB && (info.nPayload + ovflPageSize)<ovflPageSize)
+ );
while( nOvfl-- ){
Pgno iNext = 0;
MemPage *pOvfl = 0;
@@ -53506,9 +64311,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
static int fillInCell(
MemPage *pPage, /* The page that contains the cell */
unsigned char *pCell, /* Complete text of the cell */
- const void *pKey, i64 nKey, /* The key */
- const void *pData,int nData, /* The data */
- int nZero, /* Extra zero bytes to append to pData */
+ const BtreePayload *pX, /* Payload with which to construct the cell */
int *pnSize /* Write cell size here */
){
int nPayload;
@@ -53522,7 +64325,6 @@ static int fillInCell(
BtShared *pBt = pPage->pBt;
Pgno pgnoOvfl = 0;
int nHeader;
- CellInfo info;
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
@@ -53532,40 +64334,66 @@ static int fillInCell(
|| sqlite3PagerIswriteable(pPage->pDbPage) );
/* Fill in the header. */
- nHeader = 0;
- if( !pPage->leaf ){
- nHeader += 4;
- }
- if( pPage->hasData ){
- nHeader += putVarint(&pCell[nHeader], nData+nZero);
+ nHeader = pPage->childPtrSize;
+ if( pPage->intKey ){
+ nPayload = pX->nData + pX->nZero;
+ pSrc = pX->pData;
+ nSrc = pX->nData;
+ assert( pPage->intKeyLeaf ); /* fillInCell() only called for leaves */
+ nHeader += putVarint32(&pCell[nHeader], nPayload);
+ nHeader += putVarint(&pCell[nHeader], *(u64*)&pX->nKey);
}else{
- nData = nZero = 0;
+ assert( pX->nKey<=0x7fffffff && pX->pKey!=0 );
+ nSrc = nPayload = (int)pX->nKey;
+ pSrc = pX->pKey;
+ nHeader += putVarint32(&pCell[nHeader], nPayload);
}
- nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey);
- btreeParseCellPtr(pPage, pCell, &info);
- assert( info.nHeader==nHeader );
- assert( info.nKey==nKey );
- assert( info.nData==(u32)(nData+nZero) );
/* Fill in the payload */
- nPayload = nData + nZero;
- if( pPage->intKey ){
- pSrc = pData;
- nSrc = nData;
- nData = 0;
- }else{
- if( NEVER(nKey>0x7fffffff || pKey==0) ){
- return SQLITE_CORRUPT_BKPT;
- }
- nPayload += (int)nKey;
- pSrc = pKey;
- nSrc = (int)nKey;
+ if( nPayload<=pPage->maxLocal ){
+ n = nHeader + nPayload;
+ testcase( n==3 );
+ testcase( n==4 );
+ if( n<4 ) n = 4;
+ *pnSize = n;
+ spaceLeft = nPayload;
+ pPrior = pCell;
+ }else{
+ int mn = pPage->minLocal;
+ n = mn + (nPayload - mn) % (pPage->pBt->usableSize - 4);
+ testcase( n==pPage->maxLocal );
+ testcase( n==pPage->maxLocal+1 );
+ if( n > pPage->maxLocal ) n = mn;
+ spaceLeft = n;
+ *pnSize = n + nHeader + 4;
+ pPrior = &pCell[nHeader+n];
}
- *pnSize = info.nSize;
- spaceLeft = info.nLocal;
pPayload = &pCell[nHeader];
- pPrior = &pCell[info.iOverflow];
+ /* At this point variables should be set as follows:
+ **
+ ** nPayload Total payload size in bytes
+ ** pPayload Begin writing payload here
+ ** spaceLeft Space available at pPayload. If nPayload>spaceLeft,
+ ** that means content must spill into overflow pages.
+ ** *pnSize Size of the local cell (not counting overflow pages)
+ ** pPrior Where to write the pgno of the first overflow page
+ **
+ ** Use a call to btreeParseCellPtr() to verify that the values above
+ ** were computed correctly.
+ */
+#if SQLITE_DEBUG
+ {
+ CellInfo info;
+ pPage->xParseCell(pPage, pCell, &info);
+ assert( nHeader==(int)(info.pPayload - pCell) );
+ assert( info.nKey==pX->nKey );
+ assert( *pnSize == info.nSize );
+ assert( spaceLeft == info.nLocal );
+ }
+#endif
+
+ /* Write the payload into the local Cell and any extra into overflow pages */
while( nPayload>0 ){
if( spaceLeft==0 ){
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -53587,7 +64415,7 @@ static int fillInCell(
** If this is the first overflow page, then write a partial entry
** to the pointer-map. If we write nothing to this pointer-map slot,
** then the optimistic overflow chain processing in clearCell()
- ** may misinterpret the uninitialised values and delete the
+ ** may misinterpret the uninitialized values and delete the
** wrong pages from the database.
*/
if( pBt->autoVacuum && rc==SQLITE_OK ){
@@ -53644,10 +64472,6 @@ static int fillInCell(
pSrc += n;
nSrc -= n;
spaceLeft -= n;
- if( nSrc==0 ){
- nSrc = nData;
- pSrc = pData;
- }
}
releasePage(pToRelease);
return SQLITE_OK;
@@ -53665,18 +64489,17 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
u32 pc; /* Offset to cell content of cell being deleted */
u8 *data; /* pPage->aData */
u8 *ptr; /* Used to move bytes around within data[] */
- u8 *endPtr; /* End of loop */
int rc; /* The return code */
int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */
if( *pRC ) return;
assert( idx>=0 && idx<pPage->nCell );
- assert( sz==cellSize(pPage, idx) );
+ assert( CORRUPT_DB || sz==cellSize(pPage, idx) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
data = pPage->aData;
- ptr = &data[pPage->cellOffset + 2*idx];
+ ptr = &pPage->aCellIdx[2*idx];
pc = get2byte(ptr);
hdr = pPage->hdrOffset;
testcase( pc==get2byte(&data[hdr+5]) );
@@ -53690,15 +64513,18 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
*pRC = rc;
return;
}
- endPtr = &data[pPage->cellOffset + 2*pPage->nCell - 2];
- assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
- while( ptr<endPtr ){
- *(u16*)ptr = *(u16*)&ptr[2];
- ptr += 2;
- }
pPage->nCell--;
- put2byte(&data[hdr+3], pPage->nCell);
- pPage->nFree += 2;
+ if( pPage->nCell==0 ){
+ memset(&data[hdr+1], 0, 4);
+ data[hdr+7] = 0;
+ put2byte(&data[hdr+5], pPage->pBt->usableSize);
+ pPage->nFree = pPage->pBt->usableSize - pPage->hdrOffset
+ - pPage->childPtrSize - 8;
+ }else{
+ memmove(ptr, ptr+2, 2*(pPage->nCell - idx));
+ put2byte(&data[hdr+3], pPage->nCell);
+ pPage->nFree += 2;
+ }
}
/*
@@ -53708,15 +64534,12 @@ static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){
** If the cell content will fit on the page, then put it there. If it
** will not fit, then make a copy of the cell content into pTemp if
** pTemp is not null. Regardless of pTemp, allocate a new entry
-** in pPage->aOvfl[] and make it point to the cell content (either
+** in pPage->apOvfl[] and make it point to the cell content (either
** in pTemp or the original pCell) and also record its index.
** Allocating a new entry in pPage->aCell[] implies that
** pPage->nOverflow is incremented.
**
-** If nSkip is non-zero, then do not copy the first nSkip bytes of the
-** cell. The caller will overwrite them after this function returns. If
-** nSkip is non-zero, then pCell may not point to an invalid memory location
-** (but pCell+nSkip is always valid).
+** *pRC must be SQLITE_OK when this routine is called.
*/
static void insertCell(
MemPage *pPage, /* Page into which we are copying */
@@ -53729,39 +64552,42 @@ static void insertCell(
){
int idx = 0; /* Where to write new cell content in data[] */
int j; /* Loop counter */
- int end; /* First byte past the last cell pointer in data[] */
- int ins; /* Index in data[] where new cell pointer is inserted */
- int cellOffset; /* Address of first cell pointer in data[] */
u8 *data; /* The content of the whole page */
- u8 *ptr; /* Used for moving information around in data[] */
- u8 *endPtr; /* End of the loop */
-
- int nSkip = (iChild ? 4 : 0);
-
- if( *pRC ) return;
+ u8 *pIns; /* The point in pPage->aCellIdx[] where no cell inserted */
+ assert( *pRC==SQLITE_OK );
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
- assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=10921 );
- assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) );
+ assert( MX_CELL(pPage->pBt)<=10921 );
+ assert( pPage->nCell<=MX_CELL(pPage->pBt) || CORRUPT_DB );
+ assert( pPage->nOverflow<=ArraySize(pPage->apOvfl) );
+ assert( ArraySize(pPage->apOvfl)==ArraySize(pPage->aiOvfl) );
assert( sqlite3_mutex_held(pPage->pBt->mutex) );
/* The cell should normally be sized correctly. However, when moving a
** malformed cell from a leaf page to an interior page, if the cell size
** wanted to be less than 4 but got rounded up to 4 on the leaf, then size
** might be less than 8 (leaf-size + pointer) on the interior node. Hence
** the term after the || in the following assert(). */
- assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) );
+ assert( sz==pPage->xCellSize(pPage, pCell) || (sz==8 && iChild>0) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
- memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
+ memcpy(pTemp, pCell, sz);
pCell = pTemp;
}
if( iChild ){
put4byte(pCell, iChild);
}
j = pPage->nOverflow++;
- assert( j<(int)(sizeof(pPage->aOvfl)/sizeof(pPage->aOvfl[0])) );
- pPage->aOvfl[j].pCell = pCell;
- pPage->aOvfl[j].idx = (u16)i;
+ assert( j<(int)(sizeof(pPage->apOvfl)/sizeof(pPage->apOvfl[0])) );
+ pPage->apOvfl[j] = pCell;
+ pPage->aiOvfl[j] = (u16)i;
+
+ /* When multiple overflows occur, they are always sequential and in
+ ** sorted order. This invariants arise because multiple overflows can
+ ** only occur when inserting divider cells into the parent page during
+ ** balancing, and the dividers are adjacent and sorted.
+ */
+ assert( j==0 || pPage->aiOvfl[j-1]<(u16)i ); /* Overflows in sorted order */
+ assert( j==0 || i==pPage->aiOvfl[j-1]+1 ); /* Overflows are sequential */
}else{
int rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc!=SQLITE_OK ){
@@ -53770,30 +64596,26 @@ static void insertCell(
}
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
data = pPage->aData;
- cellOffset = pPage->cellOffset;
- end = cellOffset + 2*pPage->nCell;
- ins = cellOffset + 2*i;
+ assert( &data[pPage->cellOffset]==pPage->aCellIdx );
rc = allocateSpace(pPage, sz, &idx);
if( rc ){ *pRC = rc; return; }
- /* The allocateSpace() routine guarantees the following two properties
- ** if it returns success */
- assert( idx >= end+2 );
+ /* The allocateSpace() routine guarantees the following properties
+ ** if it returns successfully */
+ assert( idx >= 0 );
+ assert( idx >= pPage->cellOffset+2*pPage->nCell+2 || CORRUPT_DB );
assert( idx+sz <= (int)pPage->pBt->usableSize );
- pPage->nCell++;
pPage->nFree -= (u16)(2 + sz);
- memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip);
+ memcpy(&data[idx], pCell, sz);
if( iChild ){
put4byte(&data[idx], iChild);
}
- ptr = &data[end];
- endPtr = &data[ins];
- assert( (SQLITE_PTR_TO_INT(ptr)&1)==0 ); /* ptr is always 2-byte aligned */
- while( ptr>endPtr ){
- *(u16*)ptr = *(u16*)&ptr[-2];
- ptr -= 2;
- }
- put2byte(&data[ins], idx);
- put2byte(&data[pPage->hdrOffset+3], pPage->nCell);
+ pIns = pPage->aCellIdx + i*2;
+ memmove(pIns+2, pIns, 2*(pPage->nCell - i));
+ put2byte(pIns, idx);
+ pPage->nCell++;
+ /* increment the cell count */
+ if( (++data[pPage->hdrOffset+4])==0 ) data[pPage->hdrOffset+3]++;
+ assert( get2byte(&data[pPage->hdrOffset+3])==pPage->nCell );
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pPage->pBt->autoVacuum ){
/* The cell may contain a pointer to an overflow page. If so, write
@@ -53806,45 +64628,328 @@ static void insertCell(
}
/*
-** Add a list of cells to a page. The page should be initially empty.
-** The cells are guaranteed to fit on the page.
+** A CellArray object contains a cache of pointers and sizes for a
+** consecutive sequence of cells that might be held on multiple pages.
*/
-static void assemblePage(
- MemPage *pPage, /* The page to be assemblied */
- int nCell, /* The number of cells to add to this page */
- u8 **apCell, /* Pointers to cell bodies */
- u16 *aSize /* Sizes of the cells */
+typedef struct CellArray CellArray;
+struct CellArray {
+ int nCell; /* Number of cells in apCell[] */
+ MemPage *pRef; /* Reference page */
+ u8 **apCell; /* All cells begin balanced */
+ u16 *szCell; /* Local size of all cells in apCell[] */
+};
+
+/*
+** Make sure the cell sizes at idx, idx+1, ..., idx+N-1 have been
+** computed.
+*/
+static void populateCellCache(CellArray *p, int idx, int N){
+ assert( idx>=0 && idx+N<=p->nCell );
+ while( N>0 ){
+ assert( p->apCell[idx]!=0 );
+ if( p->szCell[idx]==0 ){
+ p->szCell[idx] = p->pRef->xCellSize(p->pRef, p->apCell[idx]);
+ }else{
+ assert( CORRUPT_DB ||
+ p->szCell[idx]==p->pRef->xCellSize(p->pRef, p->apCell[idx]) );
+ }
+ idx++;
+ N--;
+ }
+}
+
+/*
+** Return the size of the Nth element of the cell array
+*/
+static SQLITE_NOINLINE u16 computeCellSize(CellArray *p, int N){
+ assert( N>=0 && N<p->nCell );
+ assert( p->szCell[N]==0 );
+ p->szCell[N] = p->pRef->xCellSize(p->pRef, p->apCell[N]);
+ return p->szCell[N];
+}
+static u16 cachedCellSize(CellArray *p, int N){
+ assert( N>=0 && N<p->nCell );
+ if( p->szCell[N] ) return p->szCell[N];
+ return computeCellSize(p, N);
+}
+
+/*
+** Array apCell[] contains pointers to nCell b-tree page cells. The
+** szCell[] array contains the size in bytes of each cell. This function
+** replaces the current contents of page pPg with the contents of the cell
+** array.
+**
+** Some of the cells in apCell[] may currently be stored in pPg. This
+** function works around problems caused by this by making a copy of any
+** such cells before overwriting the page data.
+**
+** The MemPage.nFree field is invalidated by this function. It is the
+** responsibility of the caller to set it correctly.
+*/
+static int rebuildPage(
+ MemPage *pPg, /* Edit this page */
+ int nCell, /* Final number of cells on page */
+ u8 **apCell, /* Array of cells */
+ u16 *szCell /* Array of cell sizes */
){
- int i; /* Loop counter */
- u8 *pCellptr; /* Address of next cell pointer */
- int cellbody; /* Address of next cell body */
- u8 * const data = pPage->aData; /* Pointer to data for pPage */
- const int hdr = pPage->hdrOffset; /* Offset of header on pPage */
- const int nUsable = pPage->pBt->usableSize; /* Usable size of page */
+ const int hdr = pPg->hdrOffset; /* Offset of header on pPg */
+ u8 * const aData = pPg->aData; /* Pointer to data for pPg */
+ const int usableSize = pPg->pBt->usableSize;
+ u8 * const pEnd = &aData[usableSize];
+ int i;
+ u8 *pCellptr = pPg->aCellIdx;
+ u8 *pTmp = sqlite3PagerTempSpace(pPg->pBt->pPager);
+ u8 *pData;
- assert( pPage->nOverflow==0 );
- assert( sqlite3_mutex_held(pPage->pBt->mutex) );
- assert( nCell>=0 && nCell<=(int)MX_CELL(pPage->pBt)
- && (int)MX_CELL(pPage->pBt)<=10921);
- assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ i = get2byte(&aData[hdr+5]);
+ memcpy(&pTmp[i], &aData[i], usableSize - i);
+
+ pData = pEnd;
+ for(i=0; i<nCell; i++){
+ u8 *pCell = apCell[i];
+ if( SQLITE_WITHIN(pCell,aData,pEnd) ){
+ pCell = &pTmp[pCell - aData];
+ }
+ pData -= szCell[i];
+ put2byte(pCellptr, (pData - aData));
+ pCellptr += 2;
+ if( pData < pCellptr ) return SQLITE_CORRUPT_BKPT;
+ memcpy(pData, pCell, szCell[i]);
+ assert( szCell[i]==pPg->xCellSize(pPg, pCell) || CORRUPT_DB );
+ testcase( szCell[i]!=pPg->xCellSize(pPg,pCell) );
+ }
+
+ /* The pPg->nFree field is now set incorrectly. The caller will fix it. */
+ pPg->nCell = nCell;
+ pPg->nOverflow = 0;
+
+ put2byte(&aData[hdr+1], 0);
+ put2byte(&aData[hdr+3], pPg->nCell);
+ put2byte(&aData[hdr+5], pData - aData);
+ aData[hdr+7] = 0x00;
+ return SQLITE_OK;
+}
+
+/*
+** Array apCell[] contains nCell pointers to b-tree cells. Array szCell
+** contains the size in bytes of each such cell. This function attempts to
+** add the cells stored in the array to page pPg. If it cannot (because
+** the page needs to be defragmented before the cells will fit), non-zero
+** is returned. Otherwise, if the cells are added successfully, zero is
+** returned.
+**
+** Argument pCellptr points to the first entry in the cell-pointer array
+** (part of page pPg) to populate. After cell apCell[0] is written to the
+** page body, a 16-bit offset is written to pCellptr. And so on, for each
+** cell in the array. It is the responsibility of the caller to ensure
+** that it is safe to overwrite this part of the cell-pointer array.
+**
+** When this function is called, *ppData points to the start of the
+** content area on page pPg. If the size of the content area is extended,
+** *ppData is updated to point to the new start of the content area
+** before returning.
+**
+** Finally, argument pBegin points to the byte immediately following the
+** end of the space required by this page for the cell-pointer area (for
+** all cells - not just those inserted by the current call). If the content
+** area must be extended to before this point in order to accomodate all
+** cells in apCell[], then the cells do not fit and non-zero is returned.
+*/
+static int pageInsertArray(
+ MemPage *pPg, /* Page to add cells to */
+ u8 *pBegin, /* End of cell-pointer array */
+ u8 **ppData, /* IN/OUT: Page content -area pointer */
+ u8 *pCellptr, /* Pointer to cell-pointer area */
+ int iFirst, /* Index of first cell to add */
+ int nCell, /* Number of cells to add to pPg */
+ CellArray *pCArray /* Array of cells */
+){
+ int i;
+ u8 *aData = pPg->aData;
+ u8 *pData = *ppData;
+ int iEnd = iFirst + nCell;
+ assert( CORRUPT_DB || pPg->hdrOffset==0 ); /* Never called on page 1 */
+ for(i=iFirst; i<iEnd; i++){
+ int sz, rc;
+ u8 *pSlot;
+ sz = cachedCellSize(pCArray, i);
+ if( (aData[1]==0 && aData[2]==0) || (pSlot = pageFindSlot(pPg,sz,&rc))==0 ){
+ if( (pData - pBegin)<sz ) return 1;
+ pData -= sz;
+ pSlot = pData;
+ }
+ /* pSlot and pCArray->apCell[i] will never overlap on a well-formed
+ ** database. But they might for a corrupt database. Hence use memmove()
+ ** since memcpy() sends SIGABORT with overlapping buffers on OpenBSD */
+ assert( (pSlot+sz)<=pCArray->apCell[i]
+ || pSlot>=(pCArray->apCell[i]+sz)
+ || CORRUPT_DB );
+ memmove(pSlot, pCArray->apCell[i], sz);
+ put2byte(pCellptr, (pSlot - aData));
+ pCellptr += 2;
+ }
+ *ppData = pData;
+ return 0;
+}
+
+/*
+** Array apCell[] contains nCell pointers to b-tree cells. Array szCell
+** contains the size in bytes of each such cell. This function adds the
+** space associated with each cell in the array that is currently stored
+** within the body of pPg to the pPg free-list. The cell-pointers and other
+** fields of the page are not updated.
+**
+** This function returns the total number of cells added to the free-list.
+*/
+static int pageFreeArray(
+ MemPage *pPg, /* Page to edit */
+ int iFirst, /* First cell to delete */
+ int nCell, /* Cells to delete */
+ CellArray *pCArray /* Array of cells */
+){
+ u8 * const aData = pPg->aData;
+ u8 * const pEnd = &aData[pPg->pBt->usableSize];
+ u8 * const pStart = &aData[pPg->hdrOffset + 8 + pPg->childPtrSize];
+ int nRet = 0;
+ int i;
+ int iEnd = iFirst + nCell;
+ u8 *pFree = 0;
+ int szFree = 0;
+
+ for(i=iFirst; i<iEnd; i++){
+ u8 *pCell = pCArray->apCell[i];
+ if( SQLITE_WITHIN(pCell, pStart, pEnd) ){
+ int sz;
+ /* No need to use cachedCellSize() here. The sizes of all cells that
+ ** are to be freed have already been computing while deciding which
+ ** cells need freeing */
+ sz = pCArray->szCell[i]; assert( sz>0 );
+ if( pFree!=(pCell + sz) ){
+ if( pFree ){
+ assert( pFree>aData && (pFree - aData)<65536 );
+ freeSpace(pPg, (u16)(pFree - aData), szFree);
+ }
+ pFree = pCell;
+ szFree = sz;
+ if( pFree+sz>pEnd ) return 0;
+ }else{
+ pFree = pCell;
+ szFree += sz;
+ }
+ nRet++;
+ }
+ }
+ if( pFree ){
+ assert( pFree>aData && (pFree - aData)<65536 );
+ freeSpace(pPg, (u16)(pFree - aData), szFree);
+ }
+ return nRet;
+}
+
+/*
+** apCell[] and szCell[] contains pointers to and sizes of all cells in the
+** pages being balanced. The current page, pPg, has pPg->nCell cells starting
+** with apCell[iOld]. After balancing, this page should hold nNew cells
+** starting at apCell[iNew].
+**
+** This routine makes the necessary adjustments to pPg so that it contains
+** the correct cells after being balanced.
+**
+** The pPg->nFree field is invalid when this function returns. It is the
+** responsibility of the caller to set it correctly.
+*/
+static int editPage(
+ MemPage *pPg, /* Edit this page */
+ int iOld, /* Index of first cell currently on page */
+ int iNew, /* Index of new first cell on page */
+ int nNew, /* Final number of cells on page */
+ CellArray *pCArray /* Array of cells and sizes */
+){
+ u8 * const aData = pPg->aData;
+ const int hdr = pPg->hdrOffset;
+ u8 *pBegin = &pPg->aCellIdx[nNew * 2];
+ int nCell = pPg->nCell; /* Cells stored on pPg */
+ u8 *pData;
+ u8 *pCellptr;
+ int i;
+ int iOldEnd = iOld + pPg->nCell + pPg->nOverflow;
+ int iNewEnd = iNew + nNew;
+
+#ifdef SQLITE_DEBUG
+ u8 *pTmp = sqlite3PagerTempSpace(pPg->pBt->pPager);
+ memcpy(pTmp, aData, pPg->pBt->usableSize);
+#endif
+
+ /* Remove cells from the start and end of the page */
+ if( iOld<iNew ){
+ int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
+ memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
+ nCell -= nShift;
+ }
+ if( iNewEnd < iOldEnd ){
+ nCell -= pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
+ }
+
+ pData = &aData[get2byteNotZero(&aData[hdr+5])];
+ if( pData<pBegin ) goto editpage_fail;
+
+ /* Add cells to the start of the page */
+ if( iNew<iOld ){
+ int nAdd = MIN(nNew,iOld-iNew);
+ assert( (iOld-iNew)<nNew || nCell==0 || CORRUPT_DB );
+ pCellptr = pPg->aCellIdx;
+ memmove(&pCellptr[nAdd*2], pCellptr, nCell*2);
+ if( pageInsertArray(
+ pPg, pBegin, &pData, pCellptr,
+ iNew, nAdd, pCArray
+ ) ) goto editpage_fail;
+ nCell += nAdd;
+ }
+
+ /* Add any overflow cells */
+ for(i=0; i<pPg->nOverflow; i++){
+ int iCell = (iOld + pPg->aiOvfl[i]) - iNew;
+ if( iCell>=0 && iCell<nNew ){
+ pCellptr = &pPg->aCellIdx[iCell * 2];
+ memmove(&pCellptr[2], pCellptr, (nCell - iCell) * 2);
+ nCell++;
+ if( pageInsertArray(
+ pPg, pBegin, &pData, pCellptr,
+ iCell+iNew, 1, pCArray
+ ) ) goto editpage_fail;
+ }
+ }
+
+ /* Append cells to the end of the page */
+ pCellptr = &pPg->aCellIdx[nCell*2];
+ if( pageInsertArray(
+ pPg, pBegin, &pData, pCellptr,
+ iNew+nCell, nNew-nCell, pCArray
+ ) ) goto editpage_fail;
- /* Check that the page has just been zeroed by zeroPage() */
- assert( pPage->nCell==0 );
- assert( get2byteNotZero(&data[hdr+5])==nUsable );
+ pPg->nCell = nNew;
+ pPg->nOverflow = 0;
- pCellptr = &data[pPage->cellOffset + nCell*2];
- cellbody = nUsable;
- for(i=nCell-1; i>=0; i--){
- u16 sz = aSize[i];
- pCellptr -= 2;
- cellbody -= sz;
- put2byte(pCellptr, cellbody);
- memcpy(&data[cellbody], apCell[i], sz);
+ put2byte(&aData[hdr+3], pPg->nCell);
+ put2byte(&aData[hdr+5], pData - aData);
+
+#ifdef SQLITE_DEBUG
+ for(i=0; i<nNew && !CORRUPT_DB; i++){
+ u8 *pCell = pCArray->apCell[i+iNew];
+ int iOff = get2byteAligned(&pPg->aCellIdx[i*2]);
+ if( SQLITE_WITHIN(pCell, aData, &aData[pPg->pBt->usableSize]) ){
+ pCell = &pTmp[pCell - aData];
+ }
+ assert( 0==memcmp(pCell, &aData[iOff],
+ pCArray->pRef->xCellSize(pCArray->pRef, pCArray->apCell[i+iNew])) );
}
- put2byte(&data[hdr+3], nCell);
- put2byte(&data[hdr+5], cellbody);
- pPage->nFree -= (nCell*2 + nUsable - cellbody);
- pPage->nCell = (u16)nCell;
+#endif
+
+ return SQLITE_OK;
+ editpage_fail:
+ /* Unable to edit this page. Rebuild it from scratch instead. */
+ populateCellCache(pCArray, iNew, nNew);
+ return rebuildPage(pPg, nNew, &pCArray->apCell[iNew], &pCArray->szCell[iNew]);
}
/*
@@ -53898,7 +65003,7 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
assert( pPage->nOverflow==1 );
/* This error condition is now caught prior to reaching this function */
- if( pPage->nCell<=0 ) return SQLITE_CORRUPT_BKPT;
+ if( NEVER(pPage->nCell==0) ) return SQLITE_CORRUPT_BKPT;
/* Allocate a new page. This page will become the right-sibling of
** pPage. Make the parent page writable, so that the new divider cell
@@ -53909,14 +65014,16 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
if( rc==SQLITE_OK ){
u8 *pOut = &pSpace[4];
- u8 *pCell = pPage->aOvfl[0].pCell;
- u16 szCell = cellSizePtr(pPage, pCell);
+ u8 *pCell = pPage->apOvfl[0];
+ u16 szCell = pPage->xCellSize(pPage, pCell);
u8 *pStop;
assert( sqlite3PagerIswriteable(pNew->pDbPage) );
assert( pPage->aData[0]==(PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF) );
zeroPage(pNew, PTF_INTKEY|PTF_LEAFDATA|PTF_LEAF);
- assemblePage(pNew, 1, &pCell, &szCell);
+ rc = rebuildPage(pNew, 1, &pCell, &szCell);
+ if( NEVER(rc) ) return rc;
+ pNew->nFree = pBt->usableSize - pNew->cellOffset - 2 - szCell;
/* If this is an auto-vacuum database, update the pointer map
** with entries for the new page, and any pointer from the
@@ -53954,8 +65061,10 @@ static int balance_quick(MemPage *pParent, MemPage *pPage, u8 *pSpace){
while( ((*(pOut++) = *(pCell++))&0x80) && pCell<pStop );
/* Insert the new divider cell into pParent. */
- insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
- 0, pPage->pgno, &rc);
+ if( rc==SQLITE_OK ){
+ insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace),
+ 0, pPage->pgno, &rc);
+ }
/* Set the right-child pointer of pParent to point to the new page. */
put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew);
@@ -53988,9 +65097,9 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
u8 *z;
z = findCell(pPage, j);
- btreeParseCellPtr(pPage, z, &info);
- if( info.iOverflow ){
- Pgno ovfl = get4byte(&z[info.iOverflow]);
+ pPage->xParseCell(pPage, z, &info);
+ if( info.nLocal<info.nPayload ){
+ Pgno ovfl = get4byte(&z[info.nSize-4]);
ptrmapGet(pBt, ovfl, &e, &n);
assert( n==pPage->pgno && e==PTRMAP_OVERFLOW1 );
}
@@ -54019,7 +65128,7 @@ static int ptrmapCheckPages(MemPage **apPage, int nPage){
** map entries are also updated so that the parent page is page pTo.
**
** If pFrom is currently carrying any overflow cells (entries in the
-** MemPage.aOvfl[] array), they are not copied to pTo.
+** MemPage.apOvfl[] array), they are not copied to pTo.
**
** Before returning, page pTo is reinitialized using btreeInitPage().
**
@@ -54112,10 +65221,10 @@ static int balance_nonroot(
MemPage *pParent, /* Parent page of siblings being balanced */
int iParentIdx, /* Index of "the page" in pParent */
u8 *aOvflSpace, /* page-size bytes of space for parent ovfl */
- int isRoot /* True if pParent is a root-page */
+ int isRoot, /* True if pParent is a root-page */
+ int bBulk /* True if this call is part of a bulk load */
){
BtShared *pBt; /* The whole database */
- int nCell = 0; /* Number of cells in apCell[] */
int nMaxCells = 0; /* Allocated size of apCell, szCell, aFrom. */
int nNew = 0; /* Number of pages in apNew[] */
int nOld; /* Number of pages in apOld[] */
@@ -54126,22 +65235,27 @@ static int balance_nonroot(
int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
int usableSpace; /* Bytes in pPage beyond the header */
int pageFlags; /* Value of pPage->aData[0] */
- int subtotal; /* Subtotal of bytes in cells on one page */
int iSpace1 = 0; /* First unused byte of aSpace1[] */
int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
int szScratch; /* Size of scratch memory requested */
MemPage *apOld[NB]; /* pPage and up to two siblings */
- MemPage *apCopy[NB]; /* Private copies of apOld[] pages */
MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */
u8 *pRight; /* Location in parent of right-sibling pointer */
u8 *apDiv[NB-1]; /* Divider cells in pParent */
- int cntNew[NB+2]; /* Index in aCell[] of cell after i-th page */
- int szNew[NB+2]; /* Combined size of cells place on i-th page */
- u8 **apCell = 0; /* All cells begin balanced */
- u16 *szCell; /* Local size of all cells in apCell[] */
+ int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */
+ int cntOld[NB+2]; /* Old index in b.apCell[] */
+ int szNew[NB+2]; /* Combined size of cells placed on i-th page */
u8 *aSpace1; /* Space for copies of dividers cells */
Pgno pgno; /* Temp var to store a page number in */
-
+ u8 abDone[NB+2]; /* True after i'th new page is populated */
+ Pgno aPgno[NB+2]; /* Page numbers of new pages before shuffling */
+ Pgno aPgOrder[NB+2]; /* Copy of aPgno[] used for sorting pages */
+ u16 aPgFlags[NB+2]; /* flags field of new pages before shuffling */
+ CellArray b; /* Parsed information on cells being balanced */
+
+ memset(abDone, 0, sizeof(abDone));
+ b.nCell = 0;
+ b.apCell = 0;
pBt = pParent->pBt;
assert( sqlite3_mutex_held(pBt->mutex) );
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
@@ -54156,10 +65270,10 @@ static int balance_nonroot(
** is called (indirectly) from sqlite3BtreeDelete().
*/
assert( pParent->nOverflow==0 || pParent->nOverflow==1 );
- assert( pParent->nOverflow==0 || pParent->aOvfl[0].idx==iParentIdx );
+ assert( pParent->nOverflow==0 || pParent->aiOvfl[0]==iParentIdx );
if( !aOvflSpace ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/* Find the sibling pages to balance. Also locate the cells in pParent
@@ -54176,18 +65290,18 @@ static int balance_nonroot(
i = pParent->nOverflow + pParent->nCell;
if( i<2 ){
nxDiv = 0;
- nOld = i+1;
}else{
- nOld = 3;
+ assert( bBulk==0 || bBulk==1 );
if( iParentIdx==0 ){
nxDiv = 0;
}else if( iParentIdx==i ){
- nxDiv = i-2;
+ nxDiv = i-2+bBulk;
}else{
nxDiv = iParentIdx-1;
}
- i = 2;
+ i = 2-bBulk;
}
+ nOld = i+1;
if( (i+nxDiv-pParent->nOverflow)==pParent->nCell ){
pRight = &pParent->aData[pParent->hdrOffset+8];
}else{
@@ -54195,7 +65309,7 @@ static int balance_nonroot(
}
pgno = get4byte(pRight);
while( 1 ){
- rc = getAndInitPage(pBt, pgno, &apOld[i]);
+ rc = getAndInitPage(pBt, pgno, &apOld[i], 0, 0);
if( rc ){
memset(apOld, 0, (i+1)*sizeof(MemPage*));
goto balance_cleanup;
@@ -54203,15 +65317,15 @@ static int balance_nonroot(
nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow;
if( (i--)==0 ) break;
- if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){
- apDiv[i] = pParent->aOvfl[0].pCell;
+ if( i+nxDiv==pParent->aiOvfl[0] && pParent->nOverflow ){
+ apDiv[i] = pParent->apOvfl[0];
pgno = get4byte(apDiv[i]);
- szNew[i] = cellSizePtr(pParent, apDiv[i]);
+ szNew[i] = pParent->xCellSize(pParent, apDiv[i]);
pParent->nOverflow = 0;
}else{
apDiv[i] = findCell(pParent, i+nxDiv-pParent->nOverflow);
pgno = get4byte(apDiv[i]);
- szNew[i] = cellSizePtr(pParent, apDiv[i]);
+ szNew[i] = pParent->xCellSize(pParent, apDiv[i]);
/* Drop the cell from the parent page. apDiv[i] still points to
** the cell within the parent, even though it has been dropped.
@@ -54220,13 +65334,15 @@ static int balance_nonroot(
** four bytes of the divider cell. So the pointer is safe to use
** later on.
**
- ** Unless SQLite is compiled in secure-delete mode. In this case,
+ ** But not if we are in secure-delete mode. In secure-delete mode,
** the dropCell() routine will overwrite the entire cell with zeroes.
** In this case, temporarily copy the cell into the aOvflSpace[]
** buffer. It will be copied out again as soon as the aSpace[] buffer
** is allocated. */
- if( pBt->secureDelete ){
- int iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
+ if( pBt->btsFlags & BTS_SECURE_DELETE ){
+ int iOff;
+
+ iOff = SQLITE_PTR_TO_INT(apDiv[i]) - SQLITE_PTR_TO_INT(pParent->aData);
if( (iOff+szNew[i])>(int)pBt->usableSize ){
rc = SQLITE_CORRUPT_BKPT;
memset(apOld, 0, (i+1)*sizeof(MemPage*));
@@ -54247,138 +65363,208 @@ static int balance_nonroot(
/*
** Allocate space for memory structures
*/
- k = pBt->pageSize + ROUND8(sizeof(MemPage));
szScratch =
- nMaxCells*sizeof(u8*) /* apCell */
- + nMaxCells*sizeof(u16) /* szCell */
- + pBt->pageSize /* aSpace1 */
- + k*nOld; /* Page copies (apCopy) */
- apCell = sqlite3ScratchMalloc( szScratch );
- if( apCell==0 ){
- rc = SQLITE_NOMEM;
+ nMaxCells*sizeof(u8*) /* b.apCell */
+ + nMaxCells*sizeof(u16) /* b.szCell */
+ + pBt->pageSize; /* aSpace1 */
+
+ /* EVIDENCE-OF: R-28375-38319 SQLite will never request a scratch buffer
+ ** that is more than 6 times the database page size. */
+ assert( szScratch<=6*(int)pBt->pageSize );
+ b.apCell = sqlite3ScratchMalloc( szScratch );
+ if( b.apCell==0 ){
+ rc = SQLITE_NOMEM_BKPT;
goto balance_cleanup;
}
- szCell = (u16*)&apCell[nMaxCells];
- aSpace1 = (u8*)&szCell[nMaxCells];
+ b.szCell = (u16*)&b.apCell[nMaxCells];
+ aSpace1 = (u8*)&b.szCell[nMaxCells];
assert( EIGHT_BYTE_ALIGNMENT(aSpace1) );
/*
** Load pointers to all cells on sibling pages and the divider cells
- ** into the local apCell[] array. Make copies of the divider cells
- ** into space obtained from aSpace1[] and remove the the divider Cells
- ** from pParent.
+ ** into the local b.apCell[] array. Make copies of the divider cells
+ ** into space obtained from aSpace1[]. The divider cells have already
+ ** been removed from pParent.
**
** If the siblings are on leaf pages, then the child pointers of the
** divider cells are stripped from the cells before they are copied
- ** into aSpace1[]. In this way, all cells in apCell[] are without
+ ** into aSpace1[]. In this way, all cells in b.apCell[] are without
** child pointers. If siblings are not leaves, then all cell in
- ** apCell[] include child pointers. Either way, all cells in apCell[]
+ ** b.apCell[] include child pointers. Either way, all cells in b.apCell[]
** are alike.
**
** leafCorrection: 4 if pPage is a leaf. 0 if pPage is not a leaf.
** leafData: 1 if pPage holds key+data and pParent holds only keys.
*/
- leafCorrection = apOld[0]->leaf*4;
- leafData = apOld[0]->hasData;
+ b.pRef = apOld[0];
+ leafCorrection = b.pRef->leaf*4;
+ leafData = b.pRef->intKeyLeaf;
for(i=0; i<nOld; i++){
- int limit;
-
- /* Before doing anything else, take a copy of the i'th original sibling
- ** The rest of this function will use data from the copies rather
- ** that the original pages since the original pages will be in the
- ** process of being overwritten. */
- MemPage *pOld = apCopy[i] = (MemPage*)&aSpace1[pBt->pageSize + k*i];
- memcpy(pOld, apOld[i], sizeof(MemPage));
- pOld->aData = (void*)&pOld[1];
- memcpy(pOld->aData, apOld[i]->aData, pBt->pageSize);
-
- limit = pOld->nCell+pOld->nOverflow;
+ MemPage *pOld = apOld[i];
+ int limit = pOld->nCell;
+ u8 *aData = pOld->aData;
+ u16 maskPage = pOld->maskPage;
+ u8 *piCell = aData + pOld->cellOffset;
+ u8 *piEnd;
+
+ /* Verify that all sibling pages are of the same "type" (table-leaf,
+ ** table-interior, index-leaf, or index-interior).
+ */
+ if( pOld->aData[0]!=apOld[0]->aData[0] ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
+
+ /* Load b.apCell[] with pointers to all cells in pOld. If pOld
+ ** constains overflow cells, include them in the b.apCell[] array
+ ** in the correct spot.
+ **
+ ** Note that when there are multiple overflow cells, it is always the
+ ** case that they are sequential and adjacent. This invariant arises
+ ** because multiple overflows can only occurs when inserting divider
+ ** cells into a parent on a prior balance, and divider cells are always
+ ** adjacent and are inserted in order. There is an assert() tagged
+ ** with "NOTE 1" in the overflow cell insertion loop to prove this
+ ** invariant.
+ **
+ ** This must be done in advance. Once the balance starts, the cell
+ ** offset section of the btree page will be overwritten and we will no
+ ** long be able to find the cells if a pointer to each cell is not saved
+ ** first.
+ */
+ memset(&b.szCell[b.nCell], 0, sizeof(b.szCell[0])*(limit+pOld->nOverflow));
if( pOld->nOverflow>0 ){
+ limit = pOld->aiOvfl[0];
for(j=0; j<limit; j++){
- assert( nCell<nMaxCells );
- apCell[nCell] = findOverflowCell(pOld, j);
- szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
- nCell++;
+ b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell));
+ piCell += 2;
+ b.nCell++;
}
- }else{
- u8 *aData = pOld->aData;
- u16 maskPage = pOld->maskPage;
- u16 cellOffset = pOld->cellOffset;
- for(j=0; j<limit; j++){
- assert( nCell<nMaxCells );
- apCell[nCell] = findCellv2(aData, maskPage, cellOffset, j);
- szCell[nCell] = cellSizePtr(pOld, apCell[nCell]);
- nCell++;
+ for(k=0; k<pOld->nOverflow; k++){
+ assert( k==0 || pOld->aiOvfl[k-1]+1==pOld->aiOvfl[k] );/* NOTE 1 */
+ b.apCell[b.nCell] = pOld->apOvfl[k];
+ b.nCell++;
}
- }
+ }
+ piEnd = aData + pOld->cellOffset + 2*pOld->nCell;
+ while( piCell<piEnd ){
+ assert( b.nCell<nMaxCells );
+ b.apCell[b.nCell] = aData + (maskPage & get2byteAligned(piCell));
+ piCell += 2;
+ b.nCell++;
+ }
+
+ cntOld[i] = b.nCell;
if( i<nOld-1 && !leafData){
u16 sz = (u16)szNew[i];
u8 *pTemp;
- assert( nCell<nMaxCells );
- szCell[nCell] = sz;
+ assert( b.nCell<nMaxCells );
+ b.szCell[b.nCell] = sz;
pTemp = &aSpace1[iSpace1];
iSpace1 += sz;
assert( sz<=pBt->maxLocal+23 );
assert( iSpace1 <= (int)pBt->pageSize );
memcpy(pTemp, apDiv[i], sz);
- apCell[nCell] = pTemp+leafCorrection;
+ b.apCell[b.nCell] = pTemp+leafCorrection;
assert( leafCorrection==0 || leafCorrection==4 );
- szCell[nCell] = szCell[nCell] - leafCorrection;
+ b.szCell[b.nCell] = b.szCell[b.nCell] - leafCorrection;
if( !pOld->leaf ){
assert( leafCorrection==0 );
assert( pOld->hdrOffset==0 );
/* The right pointer of the child page pOld becomes the left
** pointer of the divider cell */
- memcpy(apCell[nCell], &pOld->aData[8], 4);
+ memcpy(b.apCell[b.nCell], &pOld->aData[8], 4);
}else{
assert( leafCorrection==4 );
- if( szCell[nCell]<4 ){
- /* Do not allow any cells smaller than 4 bytes. */
- szCell[nCell] = 4;
+ while( b.szCell[b.nCell]<4 ){
+ /* Do not allow any cells smaller than 4 bytes. If a smaller cell
+ ** does exist, pad it with 0x00 bytes. */
+ assert( b.szCell[b.nCell]==3 || CORRUPT_DB );
+ assert( b.apCell[b.nCell]==&aSpace1[iSpace1-3] || CORRUPT_DB );
+ aSpace1[iSpace1++] = 0x00;
+ b.szCell[b.nCell]++;
}
}
- nCell++;
+ b.nCell++;
}
}
/*
- ** Figure out the number of pages needed to hold all nCell cells.
+ ** Figure out the number of pages needed to hold all b.nCell cells.
** Store this number in "k". Also compute szNew[] which is the total
** size of all cells on the i-th page and cntNew[] which is the index
- ** in apCell[] of the cell that divides page i from page i+1.
- ** cntNew[k] should equal nCell.
+ ** in b.apCell[] of the cell that divides page i from page i+1.
+ ** cntNew[k] should equal b.nCell.
**
** Values computed by this block:
**
** k: The total number of sibling pages
** szNew[i]: Spaced used on the i-th sibling page.
- ** cntNew[i]: Index in apCell[] and szCell[] for the first cell to
+ ** cntNew[i]: Index in b.apCell[] and b.szCell[] for the first cell to
** the right of the i-th sibling page.
** usableSpace: Number of bytes of space available on each sibling.
**
*/
usableSpace = pBt->usableSize - 12 + leafCorrection;
- for(subtotal=k=i=0; i<nCell; i++){
- assert( i<nMaxCells );
- subtotal += szCell[i] + 2;
- if( subtotal > usableSpace ){
- szNew[k] = subtotal - szCell[i];
- cntNew[k] = i;
- if( leafData ){ i--; }
- subtotal = 0;
- k++;
- if( k>NB+1 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
- }
- }
- szNew[k] = subtotal;
- cntNew[k] = nCell;
- k++;
+ for(i=0; i<nOld; i++){
+ MemPage *p = apOld[i];
+ szNew[i] = usableSpace - p->nFree;
+ if( szNew[i]<0 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
+ for(j=0; j<p->nOverflow; j++){
+ szNew[i] += 2 + p->xCellSize(p, p->apOvfl[j]);
+ }
+ cntNew[i] = cntOld[i];
+ }
+ k = nOld;
+ for(i=0; i<k; i++){
+ int sz;
+ while( szNew[i]>usableSpace ){
+ if( i+1>=k ){
+ k = i+2;
+ if( k>NB+2 ){ rc = SQLITE_CORRUPT_BKPT; goto balance_cleanup; }
+ szNew[k-1] = 0;
+ cntNew[k-1] = b.nCell;
+ }
+ sz = 2 + cachedCellSize(&b, cntNew[i]-1);
+ szNew[i] -= sz;
+ if( !leafData ){
+ if( cntNew[i]<b.nCell ){
+ sz = 2 + cachedCellSize(&b, cntNew[i]);
+ }else{
+ sz = 0;
+ }
+ }
+ szNew[i+1] += sz;
+ cntNew[i]--;
+ }
+ while( cntNew[i]<b.nCell ){
+ sz = 2 + cachedCellSize(&b, cntNew[i]);
+ if( szNew[i]+sz>usableSpace ) break;
+ szNew[i] += sz;
+ cntNew[i]++;
+ if( !leafData ){
+ if( cntNew[i]<b.nCell ){
+ sz = 2 + cachedCellSize(&b, cntNew[i]);
+ }else{
+ sz = 0;
+ }
+ }
+ szNew[i+1] -= sz;
+ }
+ if( cntNew[i]>=b.nCell ){
+ k = i+1;
+ }else if( cntNew[i] <= (i>0 ? cntNew[i-1] : 0) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
+ }
/*
** The packing computed by the previous block is biased toward the siblings
- ** on the left side. The left siblings are always nearly full, while the
- ** right-most sibling might be nearly empty. This block of code attempts
- ** to adjust the packing of siblings to get a better balance.
+ ** on the left side (siblings with smaller keys). The left siblings are
+ ** always nearly full, while the right-most sibling might be nearly empty.
+ ** The next block of code attempts to adjust the packing of siblings to
+ ** get a better balance.
**
** This adjustment is more than an optimization. The packing above might
** be so out of balance as to be illegal. For example, the right-most
@@ -54392,38 +65578,46 @@ static int balance_nonroot(
r = cntNew[i-1] - 1;
d = r + 1 - leafData;
- assert( d<nMaxCells );
- assert( r<nMaxCells );
- while( szRight==0 || szRight+szCell[d]+2<=szLeft-(szCell[r]+2) ){
- szRight += szCell[d] + 2;
- szLeft -= szCell[r] + 2;
- cntNew[i-1]--;
- r = cntNew[i-1] - 1;
- d = r + 1 - leafData;
- }
+ (void)cachedCellSize(&b, d);
+ do{
+ assert( d<nMaxCells );
+ assert( r<nMaxCells );
+ (void)cachedCellSize(&b, r);
+ if( szRight!=0
+ && (bBulk || szRight+b.szCell[d]+2 > szLeft-(b.szCell[r]+(i==k-1?0:2)))){
+ break;
+ }
+ szRight += b.szCell[d] + 2;
+ szLeft -= b.szCell[r] + 2;
+ cntNew[i-1] = r;
+ r--;
+ d--;
+ }while( r>=0 );
szNew[i] = szRight;
szNew[i-1] = szLeft;
+ if( cntNew[i-1] <= (i>1 ? cntNew[i-2] : 0) ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
+ }
}
- /* Either we found one or more cells (cntnew[0])>0) or pPage is
- ** a virtual root page. A virtual root page is when the real root
- ** page is page 1 and we are the only child of that page.
+ /* Sanity check: For a non-corrupt database file one of the follwing
+ ** must be true:
+ ** (1) We found one or more cells (cntNew[0])>0), or
+ ** (2) pPage is a virtual root page. A virtual root page is when
+ ** the real root page is page 1 and we are the only child of
+ ** that page.
*/
- assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) );
-
- TRACE(("BALANCE: old: %d %d %d ",
- apOld[0]->pgno,
- nOld>=2 ? apOld[1]->pgno : 0,
- nOld>=3 ? apOld[2]->pgno : 0
+ assert( cntNew[0]>0 || (pParent->pgno==1 && pParent->nCell==0) || CORRUPT_DB);
+ TRACE(("BALANCE: old: %d(nc=%d) %d(nc=%d) %d(nc=%d)\n",
+ apOld[0]->pgno, apOld[0]->nCell,
+ nOld>=2 ? apOld[1]->pgno : 0, nOld>=2 ? apOld[1]->nCell : 0,
+ nOld>=3 ? apOld[2]->pgno : 0, nOld>=3 ? apOld[2]->nCell : 0
));
/*
** Allocate k new pages. Reuse old pages where possible.
*/
- if( apOld[0]->pgno<=1 ){
- rc = SQLITE_CORRUPT_BKPT;
- goto balance_cleanup;
- }
pageFlags = apOld[0]->aData[0];
for(i=0; i<k; i++){
MemPage *pNew;
@@ -54435,10 +65629,12 @@ static int balance_nonroot(
if( rc ) goto balance_cleanup;
}else{
assert( i>0 );
- rc = allocateBtreePage(pBt, &pNew, &pgno, pgno, 0);
+ rc = allocateBtreePage(pBt, &pNew, &pgno, (bBulk ? 1 : pgno), 0);
if( rc ) goto balance_cleanup;
+ zeroPage(pNew, pageFlags);
apNew[i] = pNew;
nNew++;
+ cntOld[i] = b.nCell;
/* Set the pointer-map entry for the new sibling page. */
if( ISAUTOVACUUM ){
@@ -54450,135 +65646,249 @@ static int balance_nonroot(
}
}
- /* Free any old pages that were not reused as new pages.
- */
- while( i<nOld ){
- freePage(apOld[i], &rc);
- if( rc ) goto balance_cleanup;
- releasePage(apOld[i]);
- apOld[i] = 0;
- i++;
- }
-
/*
- ** Put the new pages in accending order. This helps to
- ** keep entries in the disk file in order so that a scan
- ** of the table is a linear scan through the file. That
- ** in turn helps the operating system to deliver pages
- ** from the disk more rapidly.
+ ** Reassign page numbers so that the new pages are in ascending order.
+ ** This helps to keep entries in the disk file in order so that a scan
+ ** of the table is closer to a linear scan through the file. That in turn
+ ** helps the operating system to deliver pages from the disk more rapidly.
**
- ** An O(n^2) insertion sort algorithm is used, but since
- ** n is never more than NB (a small constant), that should
- ** not be a problem.
+ ** An O(n^2) insertion sort algorithm is used, but since n is never more
+ ** than (NB+2) (a small constant), that should not be a problem.
**
- ** When NB==3, this one optimization makes the database
- ** about 25% faster for large insertions and deletions.
+ ** When NB==3, this one optimization makes the database about 25% faster
+ ** for large insertions and deletions.
*/
- for(i=0; i<k-1; i++){
- int minV = apNew[i]->pgno;
- int minI = i;
- for(j=i+1; j<k; j++){
- if( apNew[j]->pgno<(unsigned)minV ){
- minI = j;
- minV = apNew[j]->pgno;
+ for(i=0; i<nNew; i++){
+ aPgOrder[i] = aPgno[i] = apNew[i]->pgno;
+ aPgFlags[i] = apNew[i]->pDbPage->flags;
+ for(j=0; j<i; j++){
+ if( aPgno[j]==aPgno[i] ){
+ /* This branch is taken if the set of sibling pages somehow contains
+ ** duplicate entries. This can happen if the database is corrupt.
+ ** It would be simpler to detect this as part of the loop below, but
+ ** we do the detection here in order to avoid populating the pager
+ ** cache with two separate objects associated with the same
+ ** page number. */
+ assert( CORRUPT_DB );
+ rc = SQLITE_CORRUPT_BKPT;
+ goto balance_cleanup;
}
}
- if( minI>i ){
- MemPage *pT;
- pT = apNew[i];
- apNew[i] = apNew[minI];
- apNew[minI] = pT;
+ }
+ for(i=0; i<nNew; i++){
+ int iBest = 0; /* aPgno[] index of page number to use */
+ for(j=1; j<nNew; j++){
+ if( aPgOrder[j]<aPgOrder[iBest] ) iBest = j;
+ }
+ pgno = aPgOrder[iBest];
+ aPgOrder[iBest] = 0xffffffff;
+ if( iBest!=i ){
+ if( iBest>i ){
+ sqlite3PagerRekey(apNew[iBest]->pDbPage, pBt->nPage+iBest+1, 0);
+ }
+ sqlite3PagerRekey(apNew[i]->pDbPage, pgno, aPgFlags[iBest]);
+ apNew[i]->pgno = pgno;
}
}
- TRACE(("new: %d(%d) %d(%d) %d(%d) %d(%d) %d(%d)\n",
- apNew[0]->pgno, szNew[0],
+
+ TRACE(("BALANCE: new: %d(%d nc=%d) %d(%d nc=%d) %d(%d nc=%d) "
+ "%d(%d nc=%d) %d(%d nc=%d)\n",
+ apNew[0]->pgno, szNew[0], cntNew[0],
nNew>=2 ? apNew[1]->pgno : 0, nNew>=2 ? szNew[1] : 0,
+ nNew>=2 ? cntNew[1] - cntNew[0] - !leafData : 0,
nNew>=3 ? apNew[2]->pgno : 0, nNew>=3 ? szNew[2] : 0,
+ nNew>=3 ? cntNew[2] - cntNew[1] - !leafData : 0,
nNew>=4 ? apNew[3]->pgno : 0, nNew>=4 ? szNew[3] : 0,
- nNew>=5 ? apNew[4]->pgno : 0, nNew>=5 ? szNew[4] : 0));
+ nNew>=4 ? cntNew[3] - cntNew[2] - !leafData : 0,
+ nNew>=5 ? apNew[4]->pgno : 0, nNew>=5 ? szNew[4] : 0,
+ nNew>=5 ? cntNew[4] - cntNew[3] - !leafData : 0
+ ));
assert( sqlite3PagerIswriteable(pParent->pDbPage) );
put4byte(pRight, apNew[nNew-1]->pgno);
- /*
- ** Evenly distribute the data in apCell[] across the new pages.
- ** Insert divider cells into pParent as necessary.
+ /* If the sibling pages are not leaves, ensure that the right-child pointer
+ ** of the right-most new sibling page is set to the value that was
+ ** originally in the same field of the right-most old sibling page. */
+ if( (pageFlags & PTF_LEAF)==0 && nOld!=nNew ){
+ MemPage *pOld = (nNew>nOld ? apNew : apOld)[nOld-1];
+ memcpy(&apNew[nNew-1]->aData[8], &pOld->aData[8], 4);
+ }
+
+ /* Make any required updates to pointer map entries associated with
+ ** cells stored on sibling pages following the balance operation. Pointer
+ ** map entries associated with divider cells are set by the insertCell()
+ ** routine. The associated pointer map entries are:
+ **
+ ** a) if the cell contains a reference to an overflow chain, the
+ ** entry associated with the first page in the overflow chain, and
+ **
+ ** b) if the sibling pages are not leaves, the child page associated
+ ** with the cell.
+ **
+ ** If the sibling pages are not leaves, then the pointer map entry
+ ** associated with the right-child of each sibling may also need to be
+ ** updated. This happens below, after the sibling pages have been
+ ** populated, not here.
*/
- j = 0;
- for(i=0; i<nNew; i++){
- /* Assemble the new sibling page. */
+ if( ISAUTOVACUUM ){
+ MemPage *pNew = apNew[0];
+ u8 *aOld = pNew->aData;
+ int cntOldNext = pNew->nCell + pNew->nOverflow;
+ int usableSize = pBt->usableSize;
+ int iNew = 0;
+ int iOld = 0;
+
+ for(i=0; i<b.nCell; i++){
+ u8 *pCell = b.apCell[i];
+ if( i==cntOldNext ){
+ MemPage *pOld = (++iOld)<nNew ? apNew[iOld] : apOld[iOld];
+ cntOldNext += pOld->nCell + pOld->nOverflow + !leafData;
+ aOld = pOld->aData;
+ }
+ if( i==cntNew[iNew] ){
+ pNew = apNew[++iNew];
+ if( !leafData ) continue;
+ }
+
+ /* Cell pCell is destined for new sibling page pNew. Originally, it
+ ** was either part of sibling page iOld (possibly an overflow cell),
+ ** or else the divider cell to the left of sibling page iOld. So,
+ ** if sibling page iOld had the same page number as pNew, and if
+ ** pCell really was a part of sibling page iOld (not a divider or
+ ** overflow cell), we can skip updating the pointer map entries. */
+ if( iOld>=nNew
+ || pNew->pgno!=aPgno[iOld]
+ || !SQLITE_WITHIN(pCell,aOld,&aOld[usableSize])
+ ){
+ if( !leafCorrection ){
+ ptrmapPut(pBt, get4byte(pCell), PTRMAP_BTREE, pNew->pgno, &rc);
+ }
+ if( cachedCellSize(&b,i)>pNew->minLocal ){
+ ptrmapPutOvflPtr(pNew, pCell, &rc);
+ }
+ if( rc ) goto balance_cleanup;
+ }
+ }
+ }
+
+ /* Insert new divider cells into pParent. */
+ for(i=0; i<nNew-1; i++){
+ u8 *pCell;
+ u8 *pTemp;
+ int sz;
MemPage *pNew = apNew[i];
+ j = cntNew[i];
+
assert( j<nMaxCells );
- zeroPage(pNew, pageFlags);
- assemblePage(pNew, cntNew[i]-j, &apCell[j], &szCell[j]);
- assert( pNew->nCell>0 || (nNew==1 && cntNew[0]==0) );
- assert( pNew->nOverflow==0 );
+ assert( b.apCell[j]!=0 );
+ pCell = b.apCell[j];
+ sz = b.szCell[j] + leafCorrection;
+ pTemp = &aOvflSpace[iOvflSpace];
+ if( !pNew->leaf ){
+ memcpy(&pNew->aData[8], pCell, 4);
+ }else if( leafData ){
+ /* If the tree is a leaf-data tree, and the siblings are leaves,
+ ** then there is no divider cell in b.apCell[]. Instead, the divider
+ ** cell consists of the integer key for the right-most cell of
+ ** the sibling-page assembled above only.
+ */
+ CellInfo info;
+ j--;
+ pNew->xParseCell(pNew, b.apCell[j], &info);
+ pCell = pTemp;
+ sz = 4 + putVarint(&pCell[4], info.nKey);
+ pTemp = 0;
+ }else{
+ pCell -= 4;
+ /* Obscure case for non-leaf-data trees: If the cell at pCell was
+ ** previously stored on a leaf node, and its reported size was 4
+ ** bytes, then it may actually be smaller than this
+ ** (see btreeParseCellPtr(), 4 bytes is the minimum size of
+ ** any cell). But it is important to pass the correct size to
+ ** insertCell(), so reparse the cell now.
+ **
+ ** This can only happen for b-trees used to evaluate "IN (SELECT ...)"
+ ** and WITHOUT ROWID tables with exactly one column which is the
+ ** primary key.
+ */
+ if( b.szCell[j]==4 ){
+ assert(leafCorrection==4);
+ sz = pParent->xCellSize(pParent, pCell);
+ }
+ }
+ iOvflSpace += sz;
+ assert( sz<=pBt->maxLocal+23 );
+ assert( iOvflSpace <= (int)pBt->pageSize );
+ insertCell(pParent, nxDiv+i, pCell, sz, pTemp, pNew->pgno, &rc);
+ if( rc!=SQLITE_OK ) goto balance_cleanup;
+ assert( sqlite3PagerIswriteable(pParent->pDbPage) );
+ }
- j = cntNew[i];
+ /* Now update the actual sibling pages. The order in which they are updated
+ ** is important, as this code needs to avoid disrupting any page from which
+ ** cells may still to be read. In practice, this means:
+ **
+ ** (1) If cells are moving left (from apNew[iPg] to apNew[iPg-1])
+ ** then it is not safe to update page apNew[iPg] until after
+ ** the left-hand sibling apNew[iPg-1] has been updated.
+ **
+ ** (2) If cells are moving right (from apNew[iPg] to apNew[iPg+1])
+ ** then it is not safe to update page apNew[iPg] until after
+ ** the right-hand sibling apNew[iPg+1] has been updated.
+ **
+ ** If neither of the above apply, the page is safe to update.
+ **
+ ** The iPg value in the following loop starts at nNew-1 goes down
+ ** to 0, then back up to nNew-1 again, thus making two passes over
+ ** the pages. On the initial downward pass, only condition (1) above
+ ** needs to be tested because (2) will always be true from the previous
+ ** step. On the upward pass, both conditions are always true, so the
+ ** upwards pass simply processes pages that were missed on the downward
+ ** pass.
+ */
+ for(i=1-nNew; i<nNew; i++){
+ int iPg = i<0 ? -i : i;
+ assert( iPg>=0 && iPg<nNew );
+ if( abDone[iPg] ) continue; /* Skip pages already processed */
+ if( i>=0 /* On the upwards pass, or... */
+ || cntOld[iPg-1]>=cntNew[iPg-1] /* Condition (1) is true */
+ ){
+ int iNew;
+ int iOld;
+ int nNewCell;
- /* If the sibling page assembled above was not the right-most sibling,
- ** insert a divider cell into the parent page.
- */
- assert( i<nNew-1 || j==nCell );
- if( j<nCell ){
- u8 *pCell;
- u8 *pTemp;
- int sz;
+ /* Verify condition (1): If cells are moving left, update iPg
+ ** only after iPg-1 has already been updated. */
+ assert( iPg==0 || cntOld[iPg-1]>=cntNew[iPg-1] || abDone[iPg-1] );
- assert( j<nMaxCells );
- pCell = apCell[j];
- sz = szCell[j] + leafCorrection;
- pTemp = &aOvflSpace[iOvflSpace];
- if( !pNew->leaf ){
- memcpy(&pNew->aData[8], pCell, 4);
- }else if( leafData ){
- /* If the tree is a leaf-data tree, and the siblings are leaves,
- ** then there is no divider cell in apCell[]. Instead, the divider
- ** cell consists of the integer key for the right-most cell of
- ** the sibling-page assembled above only.
- */
- CellInfo info;
- j--;
- btreeParseCellPtr(pNew, apCell[j], &info);
- pCell = pTemp;
- sz = 4 + putVarint(&pCell[4], info.nKey);
- pTemp = 0;
+ /* Verify condition (2): If cells are moving right, update iPg
+ ** only after iPg+1 has already been updated. */
+ assert( cntNew[iPg]>=cntOld[iPg] || abDone[iPg+1] );
+
+ if( iPg==0 ){
+ iNew = iOld = 0;
+ nNewCell = cntNew[0];
}else{
- pCell -= 4;
- /* Obscure case for non-leaf-data trees: If the cell at pCell was
- ** previously stored on a leaf node, and its reported size was 4
- ** bytes, then it may actually be smaller than this
- ** (see btreeParseCellPtr(), 4 bytes is the minimum size of
- ** any cell). But it is important to pass the correct size to
- ** insertCell(), so reparse the cell now.
- **
- ** Note that this can never happen in an SQLite data file, as all
- ** cells are at least 4 bytes. It only happens in b-trees used
- ** to evaluate "IN (SELECT ...)" and similar clauses.
- */
- if( szCell[j]==4 ){
- assert(leafCorrection==4);
- sz = cellSizePtr(pParent, pCell);
- }
+ iOld = iPg<nOld ? (cntOld[iPg-1] + !leafData) : b.nCell;
+ iNew = cntNew[iPg-1] + !leafData;
+ nNewCell = cntNew[iPg] - iNew;
}
- iOvflSpace += sz;
- assert( sz<=pBt->maxLocal+23 );
- assert( iOvflSpace <= (int)pBt->pageSize );
- insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc);
- if( rc!=SQLITE_OK ) goto balance_cleanup;
- assert( sqlite3PagerIswriteable(pParent->pDbPage) );
- j++;
- nxDiv++;
+ rc = editPage(apNew[iPg], iOld, iNew, nNewCell, &b);
+ if( rc ) goto balance_cleanup;
+ abDone[iPg]++;
+ apNew[iPg]->nFree = usableSpace-szNew[iPg];
+ assert( apNew[iPg]->nOverflow==0 );
+ assert( apNew[iPg]->nCell==nNewCell );
}
}
- assert( j==nCell );
+
+ /* All pages have been processed exactly once */
+ assert( memcmp(abDone, "\01\01\01\01\01", nNew)==0 );
+
assert( nOld>0 );
assert( nNew>0 );
- if( (pageFlags & PTF_LEAF)==0 ){
- u8 *zChild = &apCopy[nOld-1]->aData[8];
- memcpy(&apNew[nNew-1]->aData[8], zChild, 4);
- }
if( isRoot && pParent->nCell==0 && pParent->hdrOffset<=apNew[0]->nFree ){
/* The root page of the b-tree now contains no cells. The only sibling
@@ -54591,130 +65901,56 @@ static int balance_nonroot(
** sets all pointer-map entries corresponding to database image pages
** for which the pointer is stored within the content being copied.
**
- ** The second assert below verifies that the child page is defragmented
- ** (it must be, as it was just reconstructed using assemblePage()). This
- ** is important if the parent page happens to be page 1 of the database
- ** image. */
- assert( nNew==1 );
+ ** It is critical that the child page be defragmented before being
+ ** copied into the parent, because if the parent is page 1 then it will
+ ** by smaller than the child due to the database header, and so all the
+ ** free space needs to be up front.
+ */
+ assert( nNew==1 || CORRUPT_DB );
+ rc = defragmentPage(apNew[0]);
+ testcase( rc!=SQLITE_OK );
assert( apNew[0]->nFree ==
- (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2)
+ (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2)
+ || rc!=SQLITE_OK
);
copyNodeContent(apNew[0], pParent, &rc);
freePage(apNew[0], &rc);
- }else if( ISAUTOVACUUM ){
- /* Fix the pointer-map entries for all the cells that were shifted around.
- ** There are several different types of pointer-map entries that need to
- ** be dealt with by this routine. Some of these have been set already, but
- ** many have not. The following is a summary:
- **
- ** 1) The entries associated with new sibling pages that were not
- ** siblings when this function was called. These have already
- ** been set. We don't need to worry about old siblings that were
- ** moved to the free-list - the freePage() code has taken care
- ** of those.
- **
- ** 2) The pointer-map entries associated with the first overflow
- ** page in any overflow chains used by new divider cells. These
- ** have also already been taken care of by the insertCell() code.
- **
- ** 3) If the sibling pages are not leaves, then the child pages of
- ** cells stored on the sibling pages may need to be updated.
- **
- ** 4) If the sibling pages are not internal intkey nodes, then any
- ** overflow pages used by these cells may need to be updated
- ** (internal intkey nodes never contain pointers to overflow pages).
- **
- ** 5) If the sibling pages are not leaves, then the pointer-map
- ** entries for the right-child pages of each sibling may need
- ** to be updated.
- **
- ** Cases 1 and 2 are dealt with above by other code. The next
- ** block deals with cases 3 and 4 and the one after that, case 5. Since
- ** setting a pointer map entry is a relatively expensive operation, this
- ** code only sets pointer map entries for child or overflow pages that have
- ** actually moved between pages. */
- MemPage *pNew = apNew[0];
- MemPage *pOld = apCopy[0];
- int nOverflow = pOld->nOverflow;
- int iNextOld = pOld->nCell + nOverflow;
- int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1);
- j = 0; /* Current 'old' sibling page */
- k = 0; /* Current 'new' sibling page */
- for(i=0; i<nCell; i++){
- int isDivider = 0;
- while( i==iNextOld ){
- /* Cell i is the cell immediately following the last cell on old
- ** sibling page j. If the siblings are not leaf pages of an
- ** intkey b-tree, then cell i was a divider cell. */
- pOld = apCopy[++j];
- iNextOld = i + !leafData + pOld->nCell + pOld->nOverflow;
- if( pOld->nOverflow ){
- nOverflow = pOld->nOverflow;
- iOverflow = i + !leafData + pOld->aOvfl[0].idx;
- }
- isDivider = !leafData;
- }
-
- assert(nOverflow>0 || iOverflow<i );
- assert(nOverflow<2 || pOld->aOvfl[0].idx==pOld->aOvfl[1].idx-1);
- assert(nOverflow<3 || pOld->aOvfl[1].idx==pOld->aOvfl[2].idx-1);
- if( i==iOverflow ){
- isDivider = 1;
- if( (--nOverflow)>0 ){
- iOverflow++;
- }
- }
-
- if( i==cntNew[k] ){
- /* Cell i is the cell immediately following the last cell on new
- ** sibling page k. If the siblings are not leaf pages of an
- ** intkey b-tree, then cell i is a divider cell. */
- pNew = apNew[++k];
- if( !leafData ) continue;
- }
- assert( j<nOld );
- assert( k<nNew );
-
- /* If the cell was originally divider cell (and is not now) or
- ** an overflow cell, or if the cell was located on a different sibling
- ** page before the balancing, then the pointer map entries associated
- ** with any child or overflow pages need to be updated. */
- if( isDivider || pOld->pgno!=pNew->pgno ){
- if( !leafCorrection ){
- ptrmapPut(pBt, get4byte(apCell[i]), PTRMAP_BTREE, pNew->pgno, &rc);
- }
- if( szCell[i]>pNew->minLocal ){
- ptrmapPutOvflPtr(pNew, apCell[i], &rc);
- }
- }
+ }else if( ISAUTOVACUUM && !leafCorrection ){
+ /* Fix the pointer map entries associated with the right-child of each
+ ** sibling page. All other pointer map entries have already been taken
+ ** care of. */
+ for(i=0; i<nNew; i++){
+ u32 key = get4byte(&apNew[i]->aData[8]);
+ ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc);
}
+ }
- if( !leafCorrection ){
- for(i=0; i<nNew; i++){
- u32 key = get4byte(&apNew[i]->aData[8]);
- ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc);
- }
- }
+ assert( pParent->isInit );
+ TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n",
+ nOld, nNew, b.nCell));
+
+ /* Free any old pages that were not reused as new pages.
+ */
+ for(i=nNew; i<nOld; i++){
+ freePage(apOld[i], &rc);
+ }
#if 0
+ if( ISAUTOVACUUM && rc==SQLITE_OK && apNew[0]->isInit ){
/* The ptrmapCheckPages() contains assert() statements that verify that
** all pointer map pages are set correctly. This is helpful while
** debugging. This is usually disabled because a corrupt database may
** cause an assert() statement to fail. */
ptrmapCheckPages(apNew, nNew);
ptrmapCheckPages(&pParent, 1);
-#endif
}
-
- assert( pParent->isInit );
- TRACE(("BALANCE: finished: old=%d new=%d cells=%d\n",
- nOld, nNew, nCell));
+#endif
/*
** Cleanup before returning.
*/
balance_cleanup:
- sqlite3ScratchFree(apCell);
+ sqlite3ScratchFree(b.apCell);
for(i=0; i<nOld; i++){
releasePage(apOld[i]);
}
@@ -54778,7 +66014,10 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){
TRACE(("BALANCE: copy root %d into %d\n", pRoot->pgno, pChild->pgno));
/* Copy the overflow cells from pRoot to pChild */
- memcpy(pChild->aOvfl, pRoot->aOvfl, pRoot->nOverflow*sizeof(pRoot->aOvfl[0]));
+ memcpy(pChild->aiOvfl, pRoot->aiOvfl,
+ pRoot->nOverflow*sizeof(pRoot->aiOvfl[0]));
+ memcpy(pChild->apOvfl, pRoot->apOvfl,
+ pRoot->nOverflow*sizeof(pRoot->apOvfl[0]));
pChild->nOverflow = pRoot->nOverflow;
/* Zero the contents of pRoot. Then install pChild as the right-child. */
@@ -54805,8 +66044,8 @@ static int balance(BtCursor *pCur){
u8 aBalanceQuickSpace[13];
u8 *pFree = 0;
- TESTONLY( int balance_quick_called = 0 );
- TESTONLY( int balance_deeper_called = 0 );
+ VVA_ONLY( int balance_quick_called = 0 );
+ VVA_ONLY( int balance_deeper_called = 0 );
do {
int iPage = pCur->iPage;
@@ -54819,7 +66058,8 @@ static int balance(BtCursor *pCur){
** and copy the current contents of the root-page to it. The
** next iteration of the do-loop will balance the child page.
*/
- assert( (balance_deeper_called++)==0 );
+ assert( balance_deeper_called==0 );
+ VVA_ONLY( balance_deeper_called++ );
rc = balance_deeper(pPage, &pCur->apPage[1]);
if( rc==SQLITE_OK ){
pCur->iPage = 1;
@@ -54839,16 +66079,16 @@ static int balance(BtCursor *pCur){
rc = sqlite3PagerWrite(pParent->pDbPage);
if( rc==SQLITE_OK ){
#ifndef SQLITE_OMIT_QUICKBALANCE
- if( pPage->hasData
+ if( pPage->intKeyLeaf
&& pPage->nOverflow==1
- && pPage->aOvfl[0].idx==pPage->nCell
+ && pPage->aiOvfl[0]==pPage->nCell
&& pParent->pgno!=1
&& pParent->nCell==iIdx
){
/* Call balance_quick() to create a new sibling of pPage on which
** to store the overflow cell. balance_quick() inserts a new cell
** into pParent, which may cause pParent overflow. If this
- ** happens, the next interation of the do-loop will balance pParent
+ ** happens, the next iteration of the do-loop will balance pParent
** use either balance_nonroot() or balance_deeper(). Until this
** happens, the overflow cell is stored in the aBalanceQuickSpace[]
** buffer.
@@ -54858,7 +66098,8 @@ static int balance(BtCursor *pCur){
** function. If this were not verified, a subtle bug involving reuse
** of the aBalanceQuickSpace[] might sneak in.
*/
- assert( (balance_quick_called++)==0 );
+ assert( balance_quick_called==0 );
+ VVA_ONLY( balance_quick_called++ );
rc = balance_quick(pParent, pPage, aBalanceQuickSpace);
}else
#endif
@@ -54881,7 +66122,8 @@ static int balance(BtCursor *pCur){
** pSpace buffer passed to the latter call to balance_nonroot().
*/
u8 *pSpace = sqlite3PageMalloc(pCur->pBt->pageSize);
- rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1);
+ rc = balance_nonroot(pParent, iIdx, pSpace, iPage==1,
+ pCur->hints&BTREE_BULKLOAD);
if( pFree ){
/* If pFree is not NULL, it points to the pSpace buffer used
** by a previous call to balance_nonroot(). Its contents are
@@ -54902,6 +66144,7 @@ static int balance(BtCursor *pCur){
/* The next iteration of the do-loop balances the parent page. */
releasePage(pPage);
pCur->iPage--;
+ assert( pCur->iPage>=0 );
}
}while( rc==SQLITE_OK );
@@ -54913,19 +66156,25 @@ static int balance(BtCursor *pCur){
/*
-** Insert a new record into the BTree. The key is given by (pKey,nKey)
-** and the data is given by (pData,nData). The cursor is used only to
-** define what table the record should be inserted into. The cursor
-** is left pointing at a random location.
+** Insert a new record into the BTree. The content of the new record
+** is described by the pX object. The pCur cursor is used only to
+** define what table the record should be inserted into, and is left
+** pointing at a random location.
**
-** For an INTKEY table, only the nKey value of the key is used. pKey is
-** ignored. For a ZERODATA table, the pData and nData are both ignored.
+** For a table btree (used for rowid tables), only the pX.nKey value of
+** the key is used. The pX.pKey value must be NULL. The pX.nKey is the
+** rowid or INTEGER PRIMARY KEY of the row. The pX.nData,pData,nZero fields
+** hold the content of the row.
+**
+** For an index btree (used for indexes and WITHOUT ROWID tables), the
+** key is an arbitrary byte sequence stored in pX.pKey,nKey. The
+** pX.pData,nData,nZero fields must be zero.
**
** If the seekResult parameter is non-zero, then a successful call to
** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already
** been performed. seekResult is the search result returned (a negative
** number if pCur points at an entry that is smaller than (pKey, nKey), or
-** a positive value if pCur points at an etry that is larger than
+** a positive value if pCur points at an entry that is larger than
** (pKey, nKey)).
**
** If the seekResult parameter is non-zero, then the caller guarantees that
@@ -54936,9 +66185,7 @@ static int balance(BtCursor *pCur){
*/
SQLITE_PRIVATE int sqlite3BtreeInsert(
BtCursor *pCur, /* Insert data into the table of this cursor */
- const void *pKey, i64 nKey, /* The key of the new record */
- const void *pData, int nData, /* The data of the new record */
- int nZero, /* Number of extra 0 bytes to append to data */
+ const BtreePayload *pX, /* Content of the row to be inserted */
int appendBias, /* True if this is likely an append */
int seekResult /* Result of prior MovetoUnpacked() call */
){
@@ -54957,8 +66204,10 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
return pCur->skipNext;
}
- assert( cursorHoldsMutex(pCur) );
- assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE && !pBt->readOnly );
+ assert( cursorOwnsBtShared(pCur) );
+ assert( (pCur->curFlags & BTCF_WriteFlag)!=0
+ && pBt->inTransaction==TRANS_WRITE
+ && (pBt->btsFlags & BTS_READ_ONLY)==0 );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
/* Assert that the caller has been consistent. If this cursor was opened
@@ -54966,14 +66215,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** keys with no associated data. If the cursor was opened expecting an
** intkey table, the caller should be inserting integer keys with a
** blob of associated data. */
- assert( (pKey==0)==(pCur->pKeyInfo==0) );
-
- /* If this is an insert into a table b-tree, invalidate any incrblob
- ** cursors open on the row being replaced (assuming this is a replace
- ** operation - if it is not, the following is a no-op). */
- if( pCur->pKeyInfo==0 ){
- invalidateIncrblobCursors(p, nKey, 0);
- }
+ assert( (pX->pKey==0)==(pCur->pKeyInfo==0) );
/* Save the positions of any other cursors open on this table.
**
@@ -54986,28 +66228,46 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** doing any work. To avoid thwarting these optimizations, it is important
** not to clear the cursor here.
*/
- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
- if( rc ) return rc;
- if( !loc ){
- rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc);
+ if( pCur->curFlags & BTCF_Multiple ){
+ rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+ if( rc ) return rc;
+ }
+
+ if( pCur->pKeyInfo==0 ){
+ assert( pX->pKey==0 );
+ /* If this is an insert into a table b-tree, invalidate any incrblob
+ ** cursors open on the row being replaced */
+ invalidateIncrblobCursors(p, pX->nKey, 0);
+
+ /* If the cursor is currently on the last row and we are appending a
+ ** new row onto the end, set the "loc" to avoid an unnecessary
+ ** btreeMoveto() call */
+ if( (pCur->curFlags&BTCF_ValidNKey)!=0 && pX->nKey>0
+ && pCur->info.nKey==pX->nKey-1 ){
+ loc = -1;
+ }else if( loc==0 ){
+ rc = sqlite3BtreeMovetoUnpacked(pCur, 0, pX->nKey, appendBias, &loc);
+ if( rc ) return rc;
+ }
+ }else if( loc==0 ){
+ rc = btreeMoveto(pCur, pX->pKey, pX->nKey, appendBias, &loc);
if( rc ) return rc;
}
assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) );
pPage = pCur->apPage[pCur->iPage];
- assert( pPage->intKey || nKey>=0 );
+ assert( pPage->intKey || pX->nKey>=0 );
assert( pPage->leaf || !pPage->intKey );
TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
- pCur->pgnoRoot, nKey, nData, pPage->pgno,
+ pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit );
- allocateTempSpace(pBt);
newCell = pBt->pTmpSpace;
- if( newCell==0 ) return SQLITE_NOMEM;
- rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
+ assert( newCell!=0 );
+ rc = fillInCell(pPage, newCell, pX, &szNew);
if( rc ) goto end_insert;
- assert( szNew==cellSizePtr(pPage, newCell) );
+ assert( szNew==pPage->xCellSize(pPage, newCell) );
assert( szNew <= MX_CELL_SIZE(pBt) );
idx = pCur->aiIdx[pCur->iPage];
if( loc==0 ){
@@ -55021,8 +66281,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( !pPage->leaf ){
memcpy(newCell, oldCell, 4);
}
- szOld = cellSizePtr(pPage, oldCell);
- rc = clearCell(pPage, oldCell);
+ rc = clearCell(pPage, oldCell, &szOld);
dropCell(pPage, idx, szOld, &rc);
if( rc ) goto end_insert;
}else if( loc<0 && pPage->nCell>0 ){
@@ -55032,11 +66291,12 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
assert( pPage->leaf );
}
insertCell(pPage, idx, newCell, szNew, 0, 0, &rc);
+ assert( pPage->nOverflow==0 || rc==SQLITE_OK );
assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 );
- /* If no error has occured and pPage has an overflow cell, call balance()
+ /* If no error has occurred and pPage has an overflow cell, call balance()
** to redistribute the cells within the tree. Since balance() may move
- ** the cursor, zero the BtCursor.info.nSize and BtCursor.validNKey
+ ** the cursor, zero the BtCursor.info.nSize and BTCF_ValidNKey
** variables.
**
** Previous versions of SQLite called moveToRoot() to move the cursor
@@ -55055,8 +66315,9 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
** row without seeking the cursor. This can be a big performance boost.
*/
pCur->info.nSize = 0;
- pCur->validNKey = 0;
- if( rc==SQLITE_OK && pPage->nOverflow ){
+ if( pPage->nOverflow ){
+ assert( rc==SQLITE_OK );
+ pCur->curFlags &= ~(BTCF_ValidNKey);
rc = balance(pCur);
/* Must make sure nOverflow is reset to zero even if the balance()
@@ -55073,10 +66334,23 @@ end_insert:
}
/*
-** Delete the entry that the cursor is pointing to. The cursor
-** is left pointing at a arbitrary location.
+** Delete the entry that the cursor is pointing to.
+**
+** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then
+** the cursor is left pointing at an arbitrary location after the delete.
+** But if that bit is set, then the cursor is left in a state such that
+** the next call to BtreeNext() or BtreePrev() moves it to the same row
+** as it would have been on if the call to BtreeDelete() had been omitted.
+**
+** The BTREE_AUXDELETE bit of flags indicates that is one of several deletes
+** associated with a single table entry and its indexes. Only one of those
+** deletes is considered the "primary" delete. The primary delete occurs
+** on a cursor that is not a BTREE_FORDELETE cursor. All but one delete
+** operation on non-FORDELETE cursors is tagged with the AUXDELETE flag.
+** The BTREE_AUXDELETE bit is a hint that is not used by this implementation,
+** but which might be used by alternative storage engines.
*/
-SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
+SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur, u8 flags){
Btree *p = pCur->pBtree;
BtShared *pBt = p->pBt;
int rc; /* Return code */
@@ -55084,31 +66358,47 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pCell; /* Pointer to cell to delete */
int iCellIdx; /* Index of cell to delete */
int iCellDepth; /* Depth of node containing pCell */
+ u16 szCell; /* Size of the cell being deleted */
+ int bSkipnext = 0; /* Leaf cursor in SKIPNEXT state */
+ u8 bPreserve = flags & BTREE_SAVEPOSITION; /* Keep cursor valid */
- assert( cursorHoldsMutex(pCur) );
+ assert( cursorOwnsBtShared(pCur) );
assert( pBt->inTransaction==TRANS_WRITE );
- assert( !pBt->readOnly );
- assert( pCur->wrFlag );
+ assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
+ assert( pCur->curFlags & BTCF_WriteFlag );
assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );
assert( !hasReadConflicts(p, pCur->pgnoRoot) );
-
- if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell)
- || NEVER(pCur->eState!=CURSOR_VALID)
- ){
- return SQLITE_ERROR; /* Something has gone awry. */
- }
-
- /* If this is a delete operation to remove a row from a table b-tree,
- ** invalidate any incrblob cursors open on the row being deleted. */
- if( pCur->pKeyInfo==0 ){
- invalidateIncrblobCursors(p, pCur->info.nKey, 0);
- }
+ assert( pCur->aiIdx[pCur->iPage]<pCur->apPage[pCur->iPage]->nCell );
+ assert( pCur->eState==CURSOR_VALID );
+ assert( (flags & ~(BTREE_SAVEPOSITION | BTREE_AUXDELETE))==0 );
iCellDepth = pCur->iPage;
iCellIdx = pCur->aiIdx[iCellDepth];
pPage = pCur->apPage[iCellDepth];
pCell = findCell(pPage, iCellIdx);
+ /* If the bPreserve flag is set to true, then the cursor position must
+ ** be preserved following this delete operation. If the current delete
+ ** will cause a b-tree rebalance, then this is done by saving the cursor
+ ** key and leaving the cursor in CURSOR_REQUIRESEEK state before
+ ** returning.
+ **
+ ** Or, if the current delete will not cause a rebalance, then the cursor
+ ** will be left in CURSOR_SKIPNEXT state pointing to the entry immediately
+ ** before or after the deleted entry. In this case set bSkipnext to true. */
+ if( bPreserve ){
+ if( !pPage->leaf
+ || (pPage->nFree+cellSizePtr(pPage,pCell)+2)>(int)(pBt->usableSize*2/3)
+ ){
+ /* A b-tree rebalance will be required after deleting this entry.
+ ** Save the cursor key. */
+ rc = saveCursorKey(pCur);
+ if( rc ) return rc;
+ }else{
+ bSkipnext = 1;
+ }
+ }
+
/* If the page containing the entry to delete is not a leaf page, move
** the cursor to the largest entry in the tree that is smaller than
** the entry being deleted. This cell will replace the cell being deleted
@@ -55117,22 +66407,31 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
** sub-tree headed by the child page of the cell being deleted. This makes
** balancing the tree following the delete operation easier. */
if( !pPage->leaf ){
- int notUsed;
+ int notUsed = 0;
rc = sqlite3BtreePrevious(pCur, &notUsed);
if( rc ) return rc;
}
/* Save the positions of any other cursors open on this table before
- ** making any modifications. Make the page containing the entry to be
- ** deleted writable. Then free any overflow pages associated with the
- ** entry and finally remove the cell itself from within the page.
- */
- rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
- if( rc ) return rc;
+ ** making any modifications. */
+ if( pCur->curFlags & BTCF_Multiple ){
+ rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur);
+ if( rc ) return rc;
+ }
+
+ /* If this is a delete operation to remove a row from a table b-tree,
+ ** invalidate any incrblob cursors open on the row being deleted. */
+ if( pCur->pKeyInfo==0 ){
+ invalidateIncrblobCursors(p, pCur->info.nKey, 0);
+ }
+
+ /* Make the page containing the entry to be deleted writable. Then free any
+ ** overflow pages associated with the entry and finally remove the cell
+ ** itself from within the page. */
rc = sqlite3PagerWrite(pPage->pDbPage);
if( rc ) return rc;
- rc = clearCell(pPage, pCell);
- dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), &rc);
+ rc = clearCell(pPage, pCell, &szCell);
+ dropCell(pPage, iCellIdx, szCell, &rc);
if( rc ) return rc;
/* If the cell deleted was not located on a leaf page, then the cursor
@@ -55147,14 +66446,15 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pTmp;
pCell = findCell(pLeaf, pLeaf->nCell-1);
- nCell = cellSizePtr(pLeaf, pCell);
+ if( pCell<&pLeaf->aData[4] ) return SQLITE_CORRUPT_BKPT;
+ nCell = pLeaf->xCellSize(pLeaf, pCell);
assert( MX_CELL_SIZE(pBt) >= nCell );
-
- allocateTempSpace(pBt);
pTmp = pBt->pTmpSpace;
-
+ assert( pTmp!=0 );
rc = sqlite3PagerWrite(pLeaf->pDbPage);
- insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
+ if( rc==SQLITE_OK ){
+ insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc);
+ }
dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc);
if( rc ) return rc;
}
@@ -55183,7 +66483,23 @@ SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor *pCur){
}
if( rc==SQLITE_OK ){
- moveToRoot(pCur);
+ if( bSkipnext ){
+ assert( bPreserve && (pCur->iPage==iCellDepth || CORRUPT_DB) );
+ assert( pPage==pCur->apPage[pCur->iPage] || CORRUPT_DB );
+ assert( (pPage->nCell>0 || CORRUPT_DB) && iCellIdx<=pPage->nCell );
+ pCur->eState = CURSOR_SKIPNEXT;
+ if( iCellIdx>=pPage->nCell ){
+ pCur->skipNext = -1;
+ pCur->aiIdx[iCellDepth] = pPage->nCell-1;
+ }else{
+ pCur->skipNext = 1;
+ }
+ }else{
+ rc = moveToRoot(pCur);
+ if( bPreserve ){
+ pCur->eState = CURSOR_REQUIRESEEK;
+ }
+ }
}
return rc;
}
@@ -55208,7 +66524,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
assert( sqlite3BtreeHoldsMutex(p) );
assert( pBt->inTransaction==TRANS_WRITE );
- assert( !pBt->readOnly );
+ assert( (pBt->btsFlags & BTS_READ_ONLY)==0 );
#ifdef SQLITE_OMIT_AUTOVACUUM
rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
@@ -55241,13 +66557,14 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
pgnoRoot==PENDING_BYTE_PAGE(pBt) ){
pgnoRoot++;
}
- assert( pgnoRoot>=3 );
+ assert( pgnoRoot>=3 || CORRUPT_DB );
+ testcase( pgnoRoot<3 );
/* Allocate a page. The page that currently resides at pgnoRoot will
** be moved to the allocated page (unless the allocated page happens
** to reside at pgnoRoot).
*/
- rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1);
+ rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -55262,7 +66579,14 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
u8 eType = 0;
Pgno iPtrPage = 0;
+ /* Save the positions of any open cursors. This is required in
+ ** case they are holding a reference to an xFetch reference
+ ** corresponding to page pgnoRoot. */
+ rc = saveAllCursors(pBt, 0, 0);
releasePage(pPageMove);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
/* Move the page currently at pgnoRoot to pgnoMove. */
rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0);
@@ -55356,37 +66680,46 @@ static int clearDatabasePage(
int rc;
unsigned char *pCell;
int i;
+ int hdr;
+ u16 szCell;
assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>btreePagecount(pBt) ){
return SQLITE_CORRUPT_BKPT;
}
-
- rc = getAndInitPage(pBt, pgno, &pPage);
+ rc = getAndInitPage(pBt, pgno, &pPage, 0, 0);
if( rc ) return rc;
+ if( pPage->bBusy ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto cleardatabasepage_out;
+ }
+ pPage->bBusy = 1;
+ hdr = pPage->hdrOffset;
for(i=0; i<pPage->nCell; i++){
pCell = findCell(pPage, i);
if( !pPage->leaf ){
rc = clearDatabasePage(pBt, get4byte(pCell), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}
- rc = clearCell(pPage, pCell);
+ rc = clearCell(pPage, pCell, &szCell);
if( rc ) goto cleardatabasepage_out;
}
if( !pPage->leaf ){
- rc = clearDatabasePage(pBt, get4byte(&pPage->aData[8]), 1, pnChange);
+ rc = clearDatabasePage(pBt, get4byte(&pPage->aData[hdr+8]), 1, pnChange);
if( rc ) goto cleardatabasepage_out;
}else if( pnChange ){
- assert( pPage->intKey );
+ assert( pPage->intKey || CORRUPT_DB );
+ testcase( !pPage->intKey );
*pnChange += pPage->nCell;
}
if( freePageFlag ){
freePage(pPage, &rc);
}else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){
- zeroPage(pPage, pPage->aData[0] | PTF_LEAF);
+ zeroPage(pPage, pPage->aData[hdr] | PTF_LEAF);
}
cleardatabasepage_out:
+ pPage->bBusy = 0;
releasePage(pPage);
return rc;
}
@@ -55410,13 +66743,13 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
sqlite3BtreeEnter(p);
assert( p->inTrans==TRANS_WRITE );
- /* Invalidate all incrblob cursors open on table iTable (assuming iTable
- ** is the root of a table b-tree - if it is not, the following call is
- ** a no-op). */
- invalidateIncrblobCursors(p, 0, 1);
-
rc = saveAllCursors(pBt, (Pgno)iTable, 0);
+
if( SQLITE_OK==rc ){
+ /* Invalidate all incrblob cursors open on table iTable (assuming iTable
+ ** is the root of a table b-tree - if it is not, the following call is
+ ** a no-op). */
+ invalidateIncrblobCursors(p, 0, 1);
rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange);
}
sqlite3BtreeLeave(p);
@@ -55424,6 +66757,15 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree *p, int iTable, int *pnChange){
}
/*
+** Delete all information from the single table that pCur is open on.
+**
+** This routine only work for pCur on an ephemeral table.
+*/
+SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor *pCur){
+ return sqlite3BtreeClearTable(pCur->pBtree, pCur->pgnoRoot, 0);
+}
+
+/*
** Erase all information in a table and add the root of the table to
** the freelist. Except, the root of the principle table (the one on
** page 1) is never added to the freelist.
@@ -55464,6 +66806,14 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
return SQLITE_LOCKED_SHAREDCACHE;
}
+ /*
+ ** It is illegal to drop the sqlite_master table on page 1. But again,
+ ** this error is caught long before reaching this point.
+ */
+ if( NEVER(iTable<2) ){
+ return SQLITE_CORRUPT_BKPT;
+ }
+
rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0);
if( rc ) return rc;
rc = sqlite3BtreeClearTable(p, iTable, 0);
@@ -55474,76 +66824,67 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
*piMoved = 0;
- if( iTable>1 ){
#ifdef SQLITE_OMIT_AUTOVACUUM
- freePage(pPage, &rc);
- releasePage(pPage);
+ freePage(pPage, &rc);
+ releasePage(pPage);
#else
- if( pBt->autoVacuum ){
- Pgno maxRootPgno;
- sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
-
- if( iTable==maxRootPgno ){
- /* If the table being dropped is the table with the largest root-page
- ** number in the database, put the root page on the free list.
- */
- freePage(pPage, &rc);
- releasePage(pPage);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- }else{
- /* The table being dropped does not have the largest root-page
- ** number in the database. So move the page that does into the
- ** gap left by the deleted root-page.
- */
- MemPage *pMove;
- releasePage(pPage);
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
- releasePage(pMove);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- pMove = 0;
- rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
- freePage(pMove, &rc);
- releasePage(pMove);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- *piMoved = maxRootPgno;
- }
+ if( pBt->autoVacuum ){
+ Pgno maxRootPgno;
+ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
- /* Set the new 'max-root-page' value in the database header. This
- ** is the old value less one, less one more if that happens to
- ** be a root-page number, less one again if that is the
- ** PENDING_BYTE_PAGE.
+ if( iTable==maxRootPgno ){
+ /* If the table being dropped is the table with the largest root-page
+ ** number in the database, put the root page on the free list.
*/
- maxRootPgno--;
- while( maxRootPgno==PENDING_BYTE_PAGE(pBt)
- || PTRMAP_ISPAGE(pBt, maxRootPgno) ){
- maxRootPgno--;
+ freePage(pPage, &rc);
+ releasePage(pPage);
+ if( rc!=SQLITE_OK ){
+ return rc;
}
- assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) );
-
- rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno);
}else{
- freePage(pPage, &rc);
+ /* The table being dropped does not have the largest root-page
+ ** number in the database. So move the page that does into the
+ ** gap left by the deleted root-page.
+ */
+ MemPage *pMove;
releasePage(pPage);
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable, 0);
+ releasePage(pMove);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ pMove = 0;
+ rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0);
+ freePage(pMove, &rc);
+ releasePage(pMove);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ *piMoved = maxRootPgno;
}
-#endif
- }else{
- /* If sqlite3BtreeDropTable was called on page 1.
- ** This really never should happen except in a corrupt
- ** database.
+
+ /* Set the new 'max-root-page' value in the database header. This
+ ** is the old value less one, less one more if that happens to
+ ** be a root-page number, less one again if that is the
+ ** PENDING_BYTE_PAGE.
*/
- zeroPage(pPage, PTF_INTKEY|PTF_LEAF );
+ maxRootPgno--;
+ while( maxRootPgno==PENDING_BYTE_PAGE(pBt)
+ || PTRMAP_ISPAGE(pBt, maxRootPgno) ){
+ maxRootPgno--;
+ }
+ assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) );
+
+ rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno);
+ }else{
+ freePage(pPage, &rc);
releasePage(pPage);
}
+#endif
return rc;
}
SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
@@ -55567,6 +66908,13 @@ SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
** The schema layer numbers meta values differently. At the schema
** layer (and the SetCookie and ReadCookie opcodes) the number of
** free pages is not visible. So Cookie[0] is the same as Meta[1].
+**
+** This routine treats Meta[BTREE_DATA_VERSION] as a special case. Instead
+** of reading the value out of the header, it instead loads the "DataVersion"
+** from the pager. The BTREE_DATA_VERSION value is not actually stored in the
+** database file. It is a number computed by the pager. But its access
+** pattern is the same as header meta values, and so it is convenient to
+** read it from this routine.
*/
SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
BtShared *pBt = p->pBt;
@@ -55577,12 +66925,18 @@ SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
assert( pBt->pPage1 );
assert( idx>=0 && idx<=15 );
- *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
+ if( idx==BTREE_DATA_VERSION ){
+ *pMeta = sqlite3PagerDataVersion(pBt->pPager) + p->iDataVersion;
+ }else{
+ *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
+ }
/* If auto-vacuum is disabled in this build and this is an auto-vacuum
** database, mark the database as read-only. */
#ifdef SQLITE_OMIT_AUTOVACUUM
- if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1;
+ if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ){
+ pBt->btsFlags |= BTS_READ_ONLY;
+ }
#endif
sqlite3BtreeLeave(p);
@@ -55666,7 +67020,7 @@ SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *pCur, i64 *pnEntry){
if( pCur->iPage==0 ){
/* All pages of the b-tree have been visited. Return successfully. */
*pnEntry = nEntry;
- return SQLITE_OK;
+ return moveToRoot(pCur);
}
moveToParent(pCur);
}while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell );
@@ -55705,7 +67059,6 @@ SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){
*/
static void checkAppendMsg(
IntegrityCk *pCheck,
- char *zMsg1,
const char *zFormat,
...
){
@@ -55717,37 +67070,57 @@ static void checkAppendMsg(
if( pCheck->errMsg.nChar ){
sqlite3StrAccumAppend(&pCheck->errMsg, "\n", 1);
}
- if( zMsg1 ){
- sqlite3StrAccumAppend(&pCheck->errMsg, zMsg1, -1);
+ if( pCheck->zPfx ){
+ sqlite3XPrintf(&pCheck->errMsg, pCheck->zPfx, pCheck->v1, pCheck->v2);
}
- sqlite3VXPrintf(&pCheck->errMsg, 1, zFormat, ap);
+ sqlite3VXPrintf(&pCheck->errMsg, zFormat, ap);
va_end(ap);
- if( pCheck->errMsg.mallocFailed ){
+ if( pCheck->errMsg.accError==STRACCUM_NOMEM ){
pCheck->mallocFailed = 1;
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
+
+/*
+** Return non-zero if the bit in the IntegrityCk.aPgRef[] array that
+** corresponds to page iPg is already set.
+*/
+static int getPageReferenced(IntegrityCk *pCheck, Pgno iPg){
+ assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ return (pCheck->aPgRef[iPg/8] & (1 << (iPg & 0x07)));
+}
+
+/*
+** Set the bit in the IntegrityCk.aPgRef[] array that corresponds to page iPg.
+*/
+static void setPageReferenced(IntegrityCk *pCheck, Pgno iPg){
+ assert( iPg<=pCheck->nPage && sizeof(pCheck->aPgRef[0])==1 );
+ pCheck->aPgRef[iPg/8] |= (1 << (iPg & 0x07));
+}
+
+
/*
** Add 1 to the reference count for page iPage. If this is the second
** reference to the page, add an error message to pCheck->zErrMsg.
-** Return 1 if there are 2 ore more references to the page and 0 if
+** Return 1 if there are 2 or more references to the page and 0 if
** if this is the first reference to the page.
**
** Also check that the page number is in bounds.
*/
-static int checkRef(IntegrityCk *pCheck, Pgno iPage, char *zContext){
+static int checkRef(IntegrityCk *pCheck, Pgno iPage){
if( iPage==0 ) return 1;
if( iPage>pCheck->nPage ){
- checkAppendMsg(pCheck, zContext, "invalid page number %d", iPage);
+ checkAppendMsg(pCheck, "invalid page number %d", iPage);
return 1;
}
- if( pCheck->anRef[iPage]==1 ){
- checkAppendMsg(pCheck, zContext, "2nd reference to page %d", iPage);
+ if( getPageReferenced(pCheck, iPage) ){
+ checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
return 1;
}
- return (pCheck->anRef[iPage]++)>1;
+ setPageReferenced(pCheck, iPage);
+ return 0;
}
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -55760,8 +67133,7 @@ static void checkPtrmap(
IntegrityCk *pCheck, /* Integrity check context */
Pgno iChild, /* Child page number */
u8 eType, /* Expected pointer map type */
- Pgno iParent, /* Expected pointer map parent page number */
- char *zContext /* Context description (used for error msg) */
+ Pgno iParent /* Expected pointer map parent page number */
){
int rc;
u8 ePtrmapType;
@@ -55770,12 +67142,12 @@ static void checkPtrmap(
rc = ptrmapGet(pCheck->pBt, iChild, &ePtrmapType, &iPtrmapParent);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1;
- checkAppendMsg(pCheck, zContext, "Failed to read ptrmap key=%d", iChild);
+ checkAppendMsg(pCheck, "Failed to read ptrmap key=%d", iChild);
return;
}
if( ePtrmapType!=eType || iPtrmapParent!=iParent ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"Bad ptr map entry key=%d expected=(%d,%d) got=(%d,%d)",
iChild, eType, iParent, ePtrmapType, iPtrmapParent);
}
@@ -55790,8 +67162,7 @@ static void checkList(
IntegrityCk *pCheck, /* Integrity checking context */
int isFreeList, /* True for a freelist. False for overflow page list */
int iPage, /* Page number for first page in the list */
- int N, /* Expected number of pages in the list */
- char *zContext /* Context for error messages */
+ int N /* Expected number of pages in the list */
){
int i;
int expected = N;
@@ -55800,14 +67171,14 @@ static void checkList(
DbPage *pOvflPage;
unsigned char *pOvflData;
if( iPage<1 ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"%d of %d pages missing from overflow list starting at %d",
N+1, expected, iFirst);
break;
}
- if( checkRef(pCheck, iPage, zContext) ) break;
- if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage) ){
- checkAppendMsg(pCheck, zContext, "failed to get page %d", iPage);
+ if( checkRef(pCheck, iPage) ) break;
+ if( sqlite3PagerGet(pCheck->pPager, (Pgno)iPage, &pOvflPage, 0) ){
+ checkAppendMsg(pCheck, "failed to get page %d", iPage);
break;
}
pOvflData = (unsigned char *)sqlite3PagerGetData(pOvflPage);
@@ -55815,11 +67186,11 @@ static void checkList(
int n = get4byte(&pOvflData[4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
- checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
+ checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0);
}
#endif
if( n>(int)pCheck->pBt->usableSize/4-2 ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"freelist leaf count too big on page %d", iPage);
N--;
}else{
@@ -55827,10 +67198,10 @@ static void checkList(
Pgno iFreePage = get4byte(&pOvflData[8+i*4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pCheck->pBt->autoVacuum ){
- checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext);
+ checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0);
}
#endif
- checkRef(pCheck, iFreePage, zContext);
+ checkRef(pCheck, iFreePage);
}
N -= n;
}
@@ -55843,16 +67214,71 @@ static void checkList(
*/
if( pCheck->pBt->autoVacuum && N>0 ){
i = get4byte(pOvflData);
- checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext);
+ checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage);
}
}
#endif
iPage = get4byte(pOvflData);
sqlite3PagerUnref(pOvflPage);
+
+ if( isFreeList && N<(iPage!=0) ){
+ checkAppendMsg(pCheck, "free-page count in header is too small");
+ }
}
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
+/*
+** An implementation of a min-heap.
+**
+** aHeap[0] is the number of elements on the heap. aHeap[1] is the
+** root element. The daughter nodes of aHeap[N] are aHeap[N*2]
+** and aHeap[N*2+1].
+**
+** The heap property is this: Every node is less than or equal to both
+** of its daughter nodes. A consequence of the heap property is that the
+** root node aHeap[1] is always the minimum value currently in the heap.
+**
+** The btreeHeapInsert() routine inserts an unsigned 32-bit number onto
+** the heap, preserving the heap property. The btreeHeapPull() routine
+** removes the root element from the heap (the minimum value in the heap)
+** and then moves other nodes around as necessary to preserve the heap
+** property.
+**
+** This heap is used for cell overlap and coverage testing. Each u32
+** entry represents the span of a cell or freeblock on a btree page.
+** The upper 16 bits are the index of the first byte of a range and the
+** lower 16 bits are the index of the last byte of that range.
+*/
+static void btreeHeapInsert(u32 *aHeap, u32 x){
+ u32 j, i = ++aHeap[0];
+ aHeap[i] = x;
+ while( (j = i/2)>0 && aHeap[j]>aHeap[i] ){
+ x = aHeap[j];
+ aHeap[j] = aHeap[i];
+ aHeap[i] = x;
+ i = j;
+ }
+}
+static int btreeHeapPull(u32 *aHeap, u32 *pOut){
+ u32 j, i, x;
+ if( (x = aHeap[0])==0 ) return 0;
+ *pOut = aHeap[1];
+ aHeap[1] = aHeap[x];
+ aHeap[x] = 0xffffffff;
+ aHeap[0]--;
+ i = 1;
+ while( (j = i*2)<=aHeap[0] ){
+ if( aHeap[j]>aHeap[j+1] ) j++;
+ if( aHeap[i]<aHeap[j] ) break;
+ x = aHeap[i];
+ aHeap[i] = aHeap[j];
+ aHeap[j] = x;
+ i = j;
+ }
+ return 1;
+}
+
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
/*
** Do various sanity checks on a single page of a tree. Return
@@ -55863,221 +67289,257 @@ static void checkList(
**
** 1. Make sure that cells and freeblocks do not overlap
** but combine to completely cover the page.
-** NO 2. Make sure cell keys are in order.
-** NO 3. Make sure no key is less than or equal to zLowerBound.
-** NO 4. Make sure no key is greater than or equal to zUpperBound.
-** 5. Check the integrity of overflow pages.
-** 6. Recursively call checkTreePage on all children.
-** 7. Verify that the depth of all children is the same.
-** 8. Make sure this page is at least 33% full or else it is
-** the root of the tree.
+** 2. Make sure integer cell keys are in order.
+** 3. Check the integrity of overflow pages.
+** 4. Recursively call checkTreePage on all children.
+** 5. Verify that the depth of all children is the same.
*/
static int checkTreePage(
IntegrityCk *pCheck, /* Context for the sanity check */
int iPage, /* Page number of the page to check */
- char *zParentContext, /* Parent context */
- i64 *pnParentMinKey,
- i64 *pnParentMaxKey
+ i64 *piMinKey, /* Write minimum integer primary key here */
+ i64 maxKey /* Error if integer primary key greater than this */
){
- MemPage *pPage;
- int i, rc, depth, d2, pgno, cnt;
- int hdr, cellStart;
- int nCell;
- u8 *data;
- BtShared *pBt;
- int usableSize;
- char zContext[100];
- char *hit = 0;
- i64 nMinKey = 0;
- i64 nMaxKey = 0;
-
- sqlite3_snprintf(sizeof(zContext), zContext, "Page %d: ", iPage);
+ MemPage *pPage = 0; /* The page being analyzed */
+ int i; /* Loop counter */
+ int rc; /* Result code from subroutine call */
+ int depth = -1, d2; /* Depth of a subtree */
+ int pgno; /* Page number */
+ int nFrag; /* Number of fragmented bytes on the page */
+ int hdr; /* Offset to the page header */
+ int cellStart; /* Offset to the start of the cell pointer array */
+ int nCell; /* Number of cells */
+ int doCoverageCheck = 1; /* True if cell coverage checking should be done */
+ int keyCanBeEqual = 1; /* True if IPK can be equal to maxKey
+ ** False if IPK must be strictly less than maxKey */
+ u8 *data; /* Page content */
+ u8 *pCell; /* Cell content */
+ u8 *pCellIdx; /* Next element of the cell pointer array */
+ BtShared *pBt; /* The BtShared object that owns pPage */
+ u32 pc; /* Address of a cell */
+ u32 usableSize; /* Usable size of the page */
+ u32 contentOffset; /* Offset to the start of the cell content area */
+ u32 *heap = 0; /* Min-heap used for checking cell coverage */
+ u32 x, prev = 0; /* Next and previous entry on the min-heap */
+ const char *saved_zPfx = pCheck->zPfx;
+ int saved_v1 = pCheck->v1;
+ int saved_v2 = pCheck->v2;
+ u8 savedIsInit = 0;
/* Check that the page exists
*/
pBt = pCheck->pBt;
usableSize = pBt->usableSize;
if( iPage==0 ) return 0;
- if( checkRef(pCheck, iPage, zParentContext) ) return 0;
+ if( checkRef(pCheck, iPage) ) return 0;
+ pCheck->zPfx = "Page %d: ";
+ pCheck->v1 = iPage;
if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"unable to get the page. error code=%d", rc);
- return 0;
+ goto end_of_check;
}
/* Clear MemPage.isInit to make sure the corruption detection code in
** btreeInitPage() is executed. */
+ savedIsInit = pPage->isInit;
pPage->isInit = 0;
if( (rc = btreeInitPage(pPage))!=0 ){
assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */
- checkAppendMsg(pCheck, zContext,
+ checkAppendMsg(pCheck,
"btreeInitPage() returns error code %d", rc);
- releasePage(pPage);
- return 0;
+ goto end_of_check;
}
+ data = pPage->aData;
+ hdr = pPage->hdrOffset;
- /* Check out all the cells.
- */
- depth = 0;
- for(i=0; i<pPage->nCell && pCheck->mxErr; i++){
- u8 *pCell;
- u32 sz;
+ /* Set up for cell analysis */
+ pCheck->zPfx = "On tree page %d cell %d: ";
+ contentOffset = get2byteNotZero(&data[hdr+5]);
+ assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
+
+ /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
+ ** number of cells on the page. */
+ nCell = get2byte(&data[hdr+3]);
+ assert( pPage->nCell==nCell );
+
+ /* EVIDENCE-OF: R-23882-45353 The cell pointer array of a b-tree page
+ ** immediately follows the b-tree page header. */
+ cellStart = hdr + 12 - 4*pPage->leaf;
+ assert( pPage->aCellIdx==&data[cellStart] );
+ pCellIdx = &data[cellStart + 2*(nCell-1)];
+
+ if( !pPage->leaf ){
+ /* Analyze the right-child page of internal pages */
+ pgno = get4byte(&data[hdr+8]);
+#ifndef SQLITE_OMIT_AUTOVACUUM
+ if( pBt->autoVacuum ){
+ pCheck->zPfx = "On page %d at right child: ";
+ checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
+ }
+#endif
+ depth = checkTreePage(pCheck, pgno, &maxKey, maxKey);
+ keyCanBeEqual = 0;
+ }else{
+ /* For leaf pages, the coverage check will occur in the same loop
+ ** as the other cell checks, so initialize the heap. */
+ heap = pCheck->heap;
+ heap[0] = 0;
+ }
+
+ /* EVIDENCE-OF: R-02776-14802 The cell pointer array consists of K 2-byte
+ ** integer offsets to the cell contents. */
+ for(i=nCell-1; i>=0 && pCheck->mxErr; i--){
CellInfo info;
- /* Check payload overflow pages
- */
- sqlite3_snprintf(sizeof(zContext), zContext,
- "On tree page %d cell %d: ", iPage, i);
- pCell = findCell(pPage,i);
- btreeParseCellPtr(pPage, pCell, &info);
- sz = info.nData;
- if( !pPage->intKey ) sz += (int)info.nKey;
- /* For intKey pages, check that the keys are in order.
- */
- else if( i==0 ) nMinKey = nMaxKey = info.nKey;
- else{
- if( info.nKey <= nMaxKey ){
- checkAppendMsg(pCheck, zContext,
- "Rowid %lld out of order (previous was %lld)", info.nKey, nMaxKey);
+ /* Check cell size */
+ pCheck->v2 = i;
+ assert( pCellIdx==&data[cellStart + i*2] );
+ pc = get2byteAligned(pCellIdx);
+ pCellIdx -= 2;
+ if( pc<contentOffset || pc>usableSize-4 ){
+ checkAppendMsg(pCheck, "Offset %d out of range %d..%d",
+ pc, contentOffset, usableSize-4);
+ doCoverageCheck = 0;
+ continue;
+ }
+ pCell = &data[pc];
+ pPage->xParseCell(pPage, pCell, &info);
+ if( pc+info.nSize>usableSize ){
+ checkAppendMsg(pCheck, "Extends off end of page");
+ doCoverageCheck = 0;
+ continue;
+ }
+
+ /* Check for integer primary key out of range */
+ if( pPage->intKey ){
+ if( keyCanBeEqual ? (info.nKey > maxKey) : (info.nKey >= maxKey) ){
+ checkAppendMsg(pCheck, "Rowid %lld out of order", info.nKey);
}
- nMaxKey = info.nKey;
+ maxKey = info.nKey;
}
- assert( sz==info.nPayload );
- if( (sz>info.nLocal)
- && (&pCell[info.iOverflow]<=&pPage->aData[pBt->usableSize])
- ){
- int nPage = (sz - info.nLocal + usableSize - 5)/(usableSize - 4);
- Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
+
+ /* Check the content overflow list */
+ if( info.nPayload>info.nLocal ){
+ int nPage; /* Number of pages on the overflow chain */
+ Pgno pgnoOvfl; /* First page of the overflow chain */
+ assert( pc + info.nSize - 4 <= usableSize );
+ nPage = (info.nPayload - info.nLocal + usableSize - 5)/(usableSize - 4);
+ pgnoOvfl = get4byte(&pCell[info.nSize - 4]);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage, zContext);
+ checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage);
}
#endif
- checkList(pCheck, 0, pgnoOvfl, nPage, zContext);
+ checkList(pCheck, 0, pgnoOvfl, nPage);
}
- /* Check sanity of left child page.
- */
if( !pPage->leaf ){
+ /* Check sanity of left child page for internal pages */
pgno = get4byte(pCell);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
+ checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage);
}
#endif
- d2 = checkTreePage(pCheck, pgno, zContext, &nMinKey, i==0 ? NULL : &nMaxKey);
- if( i>0 && d2!=depth ){
- checkAppendMsg(pCheck, zContext, "Child page depth differs");
- }
- depth = d2;
- }
- }
-
- if( !pPage->leaf ){
- pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
- sqlite3_snprintf(sizeof(zContext), zContext,
- "On page %d at right child: ", iPage);
-#ifndef SQLITE_OMIT_AUTOVACUUM
- if( pBt->autoVacuum ){
- checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
- }
-#endif
- checkTreePage(pCheck, pgno, zContext, NULL, !pPage->nCell ? NULL : &nMaxKey);
- }
-
- /* For intKey leaf pages, check that the min/max keys are in order
- ** with any left/parent/right pages.
- */
- if( pPage->leaf && pPage->intKey ){
- /* if we are a left child page */
- if( pnParentMinKey ){
- /* if we are the left most child page */
- if( !pnParentMaxKey ){
- if( nMaxKey > *pnParentMinKey ){
- checkAppendMsg(pCheck, zContext,
- "Rowid %lld out of order (max larger than parent min of %lld)",
- nMaxKey, *pnParentMinKey);
- }
- }else{
- if( nMinKey <= *pnParentMinKey ){
- checkAppendMsg(pCheck, zContext,
- "Rowid %lld out of order (min less than parent min of %lld)",
- nMinKey, *pnParentMinKey);
- }
- if( nMaxKey > *pnParentMaxKey ){
- checkAppendMsg(pCheck, zContext,
- "Rowid %lld out of order (max larger than parent max of %lld)",
- nMaxKey, *pnParentMaxKey);
- }
- *pnParentMinKey = nMaxKey;
- }
- /* else if we're a right child page */
- } else if( pnParentMaxKey ){
- if( nMinKey <= *pnParentMaxKey ){
- checkAppendMsg(pCheck, zContext,
- "Rowid %lld out of order (min less than parent max of %lld)",
- nMinKey, *pnParentMaxKey);
+ d2 = checkTreePage(pCheck, pgno, &maxKey, maxKey);
+ keyCanBeEqual = 0;
+ if( d2!=depth ){
+ checkAppendMsg(pCheck, "Child page depth differs");
+ depth = d2;
}
+ }else{
+ /* Populate the coverage-checking heap for leaf pages */
+ btreeHeapInsert(heap, (pc<<16)|(pc+info.nSize-1));
}
}
+ *piMinKey = maxKey;
/* Check for complete coverage of the page
*/
- data = pPage->aData;
- hdr = pPage->hdrOffset;
- hit = sqlite3PageMalloc( pBt->pageSize );
- if( hit==0 ){
- pCheck->mallocFailed = 1;
- }else{
- int contentOffset = get2byteNotZero(&data[hdr+5]);
- assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */
- memset(hit+contentOffset, 0, usableSize-contentOffset);
- memset(hit, 1, contentOffset);
- nCell = get2byte(&data[hdr+3]);
- cellStart = hdr + 12 - 4*pPage->leaf;
- for(i=0; i<nCell; i++){
- int pc = get2byte(&data[cellStart+i*2]);
- u32 size = 65536;
- int j;
- if( pc<=usableSize-4 ){
- size = cellSizePtr(pPage, &data[pc]);
- }
- if( (int)(pc+size-1)>=usableSize ){
- checkAppendMsg(pCheck, 0,
- "Corruption detected in cell %d on page %d",i,iPage);
- }else{
- for(j=pc+size-1; j>=pc; j--) hit[j]++;
+ pCheck->zPfx = 0;
+ if( doCoverageCheck && pCheck->mxErr>0 ){
+ /* For leaf pages, the min-heap has already been initialized and the
+ ** cells have already been inserted. But for internal pages, that has
+ ** not yet been done, so do it now */
+ if( !pPage->leaf ){
+ heap = pCheck->heap;
+ heap[0] = 0;
+ for(i=nCell-1; i>=0; i--){
+ u32 size;
+ pc = get2byteAligned(&data[cellStart+i*2]);
+ size = pPage->xCellSize(pPage, &data[pc]);
+ btreeHeapInsert(heap, (pc<<16)|(pc+size-1));
}
}
+ /* Add the freeblocks to the min-heap
+ **
+ ** EVIDENCE-OF: R-20690-50594 The second field of the b-tree page header
+ ** is the offset of the first freeblock, or zero if there are no
+ ** freeblocks on the page.
+ */
i = get2byte(&data[hdr+1]);
while( i>0 ){
int size, j;
- assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( (u32)i<=usableSize-4 ); /* Enforced by btreeInitPage() */
size = get2byte(&data[i+2]);
- assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */
- for(j=i+size-1; j>=i; j--) hit[j]++;
+ assert( (u32)(i+size)<=usableSize ); /* Enforced by btreeInitPage() */
+ btreeHeapInsert(heap, (((u32)i)<<16)|(i+size-1));
+ /* EVIDENCE-OF: R-58208-19414 The first 2 bytes of a freeblock are a
+ ** big-endian integer which is the offset in the b-tree page of the next
+ ** freeblock in the chain, or zero if the freeblock is the last on the
+ ** chain. */
j = get2byte(&data[i]);
+ /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of
+ ** increasing offset. */
assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */
- assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */
+ assert( (u32)j<=usableSize-4 ); /* Enforced by btreeInitPage() */
i = j;
}
- for(i=cnt=0; i<usableSize; i++){
- if( hit[i]==0 ){
- cnt++;
- }else if( hit[i]>1 ){
- checkAppendMsg(pCheck, 0,
- "Multiple uses for byte %d of page %d", i, iPage);
+ /* Analyze the min-heap looking for overlap between cells and/or
+ ** freeblocks, and counting the number of untracked bytes in nFrag.
+ **
+ ** Each min-heap entry is of the form: (start_address<<16)|end_address.
+ ** There is an implied first entry the covers the page header, the cell
+ ** pointer index, and the gap between the cell pointer index and the start
+ ** of cell content.
+ **
+ ** The loop below pulls entries from the min-heap in order and compares
+ ** the start_address against the previous end_address. If there is an
+ ** overlap, that means bytes are used multiple times. If there is a gap,
+ ** that gap is added to the fragmentation count.
+ */
+ nFrag = 0;
+ prev = contentOffset - 1; /* Implied first min-heap entry */
+ while( btreeHeapPull(heap,&x) ){
+ if( (prev&0xffff)>=(x>>16) ){
+ checkAppendMsg(pCheck,
+ "Multiple uses for byte %u of page %d", x>>16, iPage);
break;
+ }else{
+ nFrag += (x>>16) - (prev&0xffff) - 1;
+ prev = x;
}
}
- if( cnt!=data[hdr+7] ){
- checkAppendMsg(pCheck, 0,
+ nFrag += usableSize - (prev&0xffff) - 1;
+ /* EVIDENCE-OF: R-43263-13491 The total number of bytes in all fragments
+ ** is stored in the fifth field of the b-tree page header.
+ ** EVIDENCE-OF: R-07161-27322 The one-byte integer at offset 7 gives the
+ ** number of fragmented free bytes within the cell content area.
+ */
+ if( heap[0]==0 && nFrag!=data[hdr+7] ){
+ checkAppendMsg(pCheck,
"Fragmentation of %d bytes reported as %d on page %d",
- cnt, data[hdr+7], iPage);
+ nFrag, data[hdr+7], iPage);
}
}
- sqlite3PageFree(hit);
+
+end_of_check:
+ if( !doCoverageCheck ) pPage->isInit = savedIsInit;
releasePage(pPage);
+ pCheck->zPfx = saved_zPfx;
+ pCheck->v1 = saved_v1;
+ pCheck->v2 = saved_v2;
return depth+1;
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
@@ -56104,113 +67566,120 @@ SQLITE_PRIVATE char *sqlite3BtreeIntegrityCheck(
int *pnErr /* Write number of errors seen to this variable */
){
Pgno i;
- int nRef;
IntegrityCk sCheck;
BtShared *pBt = p->pBt;
+ int savedDbFlags = pBt->db->flags;
char zErr[100];
+ VVA_ONLY( int nRef );
sqlite3BtreeEnter(p);
assert( p->inTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE );
- nRef = sqlite3PagerRefcount(pBt->pPager);
+ VVA_ONLY( nRef = sqlite3PagerRefcount(pBt->pPager) );
+ assert( nRef>=0 );
sCheck.pBt = pBt;
sCheck.pPager = pBt->pPager;
sCheck.nPage = btreePagecount(sCheck.pBt);
sCheck.mxErr = mxErr;
sCheck.nErr = 0;
sCheck.mallocFailed = 0;
- *pnErr = 0;
+ sCheck.zPfx = 0;
+ sCheck.v1 = 0;
+ sCheck.v2 = 0;
+ sCheck.aPgRef = 0;
+ sCheck.heap = 0;
+ sqlite3StrAccumInit(&sCheck.errMsg, 0, zErr, sizeof(zErr), SQLITE_MAX_LENGTH);
+ sCheck.errMsg.printfFlags = SQLITE_PRINTF_INTERNAL;
if( sCheck.nPage==0 ){
- sqlite3BtreeLeave(p);
- return 0;
+ goto integrity_ck_cleanup;
}
- sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
- if( !sCheck.anRef ){
- *pnErr = 1;
- sqlite3BtreeLeave(p);
- return 0;
+
+ sCheck.aPgRef = sqlite3MallocZero((sCheck.nPage / 8)+ 1);
+ if( !sCheck.aPgRef ){
+ sCheck.mallocFailed = 1;
+ goto integrity_ck_cleanup;
}
- for(i=0; i<=sCheck.nPage; i++){ sCheck.anRef[i] = 0; }
- i = PENDING_BYTE_PAGE(pBt);
- if( i<=sCheck.nPage ){
- sCheck.anRef[i] = 1;
+ sCheck.heap = (u32*)sqlite3PageMalloc( pBt->pageSize );
+ if( sCheck.heap==0 ){
+ sCheck.mallocFailed = 1;
+ goto integrity_ck_cleanup;
}
- sqlite3StrAccumInit(&sCheck.errMsg, zErr, sizeof(zErr), 20000);
- sCheck.errMsg.useMalloc = 2;
+
+ i = PENDING_BYTE_PAGE(pBt);
+ if( i<=sCheck.nPage ) setPageReferenced(&sCheck, i);
/* Check the integrity of the freelist
*/
+ sCheck.zPfx = "Main freelist: ";
checkList(&sCheck, 1, get4byte(&pBt->pPage1->aData[32]),
- get4byte(&pBt->pPage1->aData[36]), "Main freelist: ");
+ get4byte(&pBt->pPage1->aData[36]));
+ sCheck.zPfx = 0;
/* Check all the tables.
*/
+ testcase( pBt->db->flags & SQLITE_CellSizeCk );
+ pBt->db->flags &= ~SQLITE_CellSizeCk;
for(i=0; (int)i<nRoot && sCheck.mxErr; i++){
+ i64 notUsed;
if( aRoot[i]==0 ) continue;
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum && aRoot[i]>1 ){
- checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0);
+ checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0);
}
#endif
- checkTreePage(&sCheck, aRoot[i], "List of tree roots: ", NULL, NULL);
+ checkTreePage(&sCheck, aRoot[i], &notUsed, LARGEST_INT64);
}
+ pBt->db->flags = savedDbFlags;
/* Make sure every page in the file is referenced
*/
for(i=1; i<=sCheck.nPage && sCheck.mxErr; i++){
#ifdef SQLITE_OMIT_AUTOVACUUM
- if( sCheck.anRef[i]==0 ){
- checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
+ if( getPageReferenced(&sCheck, i)==0 ){
+ checkAppendMsg(&sCheck, "Page %d is never used", i);
}
#else
/* If the database supports auto-vacuum, make sure no tables contain
** references to pointer-map pages.
*/
- if( sCheck.anRef[i]==0 &&
+ if( getPageReferenced(&sCheck, i)==0 &&
(PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){
- checkAppendMsg(&sCheck, 0, "Page %d is never used", i);
+ checkAppendMsg(&sCheck, "Page %d is never used", i);
}
- if( sCheck.anRef[i]!=0 &&
+ if( getPageReferenced(&sCheck, i)!=0 &&
(PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){
- checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i);
+ checkAppendMsg(&sCheck, "Pointer map page %d is referenced", i);
}
#endif
}
- /* Make sure this analysis did not leave any unref() pages.
- ** This is an internal consistency check; an integrity check
- ** of the integrity check.
- */
- if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){
- checkAppendMsg(&sCheck, 0,
- "Outstanding page count goes from %d to %d during this analysis",
- nRef, sqlite3PagerRefcount(pBt->pPager)
- );
- }
-
/* Clean up and report errors.
*/
- sqlite3BtreeLeave(p);
- sqlite3_free(sCheck.anRef);
+integrity_ck_cleanup:
+ sqlite3PageFree(sCheck.heap);
+ sqlite3_free(sCheck.aPgRef);
if( sCheck.mallocFailed ){
sqlite3StrAccumReset(&sCheck.errMsg);
- *pnErr = sCheck.nErr+1;
- return 0;
+ sCheck.nErr++;
}
*pnErr = sCheck.nErr;
if( sCheck.nErr==0 ) sqlite3StrAccumReset(&sCheck.errMsg);
+ /* Make sure this analysis did not leave any unref() pages. */
+ assert( nRef==sqlite3PagerRefcount(pBt->pPager) );
+ sqlite3BtreeLeave(p);
return sqlite3StrAccumFinish(&sCheck.errMsg);
}
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/*
-** Return the full pathname of the underlying database file.
+** Return the full pathname of the underlying database file. Return
+** an empty string if the database is in-memory or a TEMP database.
**
** The pager filename is invariant as long as the pager is
** open so it is safe to access without the BtShared mutex.
*/
SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *p){
assert( p->pBt->pPager!=0 );
- return sqlite3PagerFilename(p->pBt->pPager);
+ return sqlite3PagerFilename(p->pBt->pPager, 1);
}
/*
@@ -56359,9 +67828,9 @@ SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
*/
SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
int rc;
- assert( cursorHoldsMutex(pCsr) );
+ assert( cursorOwnsBtShared(pCsr) );
assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) );
- assert( pCsr->isIncrblobHandle );
+ assert( pCsr->curFlags & BTCF_Incrblob );
rc = restoreCursorPosition(pCsr);
if( rc!=SQLITE_OK ){
@@ -56372,6 +67841,17 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
return SQLITE_ABORT;
}
+ /* Save the positions of all other cursors open on this table. This is
+ ** required in case any of them are holding references to an xFetch
+ ** version of the b-tree page modified by the accessPayload call below.
+ **
+ ** Note that pCsr must be open on a INTKEY table and saveCursorPosition()
+ ** and hence saveAllCursors() cannot fail on a BTREE_INTKEY table, hence
+ ** saveAllCursors can only return SQLITE_OK.
+ */
+ VVA_ONLY(rc =) saveAllCursors(pCsr->pBt, pCsr->pgnoRoot, pCsr);
+ assert( rc==SQLITE_OK );
+
/* Check some assumptions:
** (a) the cursor is open for writing,
** (b) there is a read/write transaction open,
@@ -56379,10 +67859,11 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
** (d) there are no conflicting read-locks, and
** (e) the cursor points at a valid row of an intKey table.
*/
- if( !pCsr->wrFlag ){
+ if( (pCsr->curFlags & BTCF_WriteFlag)==0 ){
return SQLITE_READONLY;
}
- assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE );
+ assert( (pCsr->pBt->btsFlags & BTS_READ_ONLY)==0
+ && pCsr->pBt->inTransaction==TRANS_WRITE );
assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) );
assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) );
assert( pCsr->apPage[pCsr->iPage]->intKey );
@@ -56391,20 +67872,11 @@ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void
}
/*
-** Set a flag on this cursor to cache the locations of pages from the
-** overflow list for the current row. This is used by cursors opened
-** for incremental blob IO only.
-**
-** This function sets a flag only. The actual page location cache
-** (stored in BtCursor.aOverflow[]) is allocated and used by function
-** accessPayload() (the worker function for sqlite3BtreeData() and
-** sqlite3BtreePutData()).
+** Mark this cursor as an incremental blob cursor.
*/
-SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *pCur){
- assert( cursorHoldsMutex(pCur) );
- assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) );
- invalidateOverflowCache(pCur);
- pCur->isIncrblobHandle = 1;
+SQLITE_PRIVATE void sqlite3BtreeIncrblobCursor(BtCursor *pCur){
+ pCur->curFlags |= BTCF_Incrblob;
+ pCur->pBtree->hasIncrblobCur = 1;
}
#endif
@@ -56422,7 +67894,8 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
/* If setting the version fields to 1, do not automatically open the
** WAL connection, even if the version fields are currently set to 2.
*/
- pBt->doNotUseWAL = (u8)(iVersion==1);
+ pBt->btsFlags &= ~BTS_NO_WAL;
+ if( iVersion==1 ) pBt->btsFlags |= BTS_NO_WAL;
rc = sqlite3BtreeBeginTrans(pBtree, 0);
if( rc==SQLITE_OK ){
@@ -56439,10 +67912,49 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
}
}
- pBt->doNotUseWAL = 0;
+ pBt->btsFlags &= ~BTS_NO_WAL;
return rc;
}
+/*
+** Return true if the cursor has a hint specified. This routine is
+** only used from within assert() statements
+*/
+SQLITE_PRIVATE int sqlite3BtreeCursorHasHint(BtCursor *pCsr, unsigned int mask){
+ return (pCsr->hints & mask)!=0;
+}
+
+/*
+** Return true if the given Btree is read-only.
+*/
+SQLITE_PRIVATE int sqlite3BtreeIsReadonly(Btree *p){
+ return (p->pBt->btsFlags & BTS_READ_ONLY)!=0;
+}
+
+/*
+** Return the size of the header added to each page by this module.
+*/
+SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void){ return ROUND8(sizeof(MemPage)); }
+
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
+/*
+** Return true if the Btree passed as the only argument is sharable.
+*/
+SQLITE_PRIVATE int sqlite3BtreeSharable(Btree *p){
+ return p->sharable;
+}
+
+/*
+** Return the number of connections to the BtShared object accessed by
+** the Btree handle passed as the only argument. For private caches
+** this is always 1. For shared caches it may be 1 or greater.
+*/
+SQLITE_PRIVATE int sqlite3BtreeConnectionCount(Btree *p){
+ testcase( p->sharable );
+ return p->pBt->nRef;
+}
+#endif
+
/************** End of btree.c ***********************************************/
/************** Begin file backup.c ******************************************/
/*
@@ -56459,12 +67971,8 @@ SQLITE_PRIVATE int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
** This file contains the implementation of the sqlite3_backup_XXX()
** API functions and the related features.
*/
-
-/* Macro to find the minimum of two numeric values.
-*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
+/* #include "sqliteInt.h" */
+/* #include "btreeInt.h" */
/*
** Structure allocated for each backup operation.
@@ -56534,28 +68042,23 @@ static Btree *findBtree(sqlite3 *pErrorDb, sqlite3 *pDb, const char *zDb){
int i = sqlite3FindDbName(pDb, zDb);
if( i==1 ){
- Parse *pParse;
+ Parse sParse;
int rc = 0;
- pParse = sqlite3StackAllocZero(pErrorDb, sizeof(*pParse));
- if( pParse==0 ){
- sqlite3Error(pErrorDb, SQLITE_NOMEM, "out of memory");
- rc = SQLITE_NOMEM;
- }else{
- pParse->db = pDb;
- if( sqlite3OpenTempDatabase(pParse) ){
- sqlite3Error(pErrorDb, pParse->rc, "%s", pParse->zErrMsg);
- rc = SQLITE_ERROR;
- }
- sqlite3DbFree(pErrorDb, pParse->zErrMsg);
- sqlite3StackFree(pErrorDb, pParse);
+ memset(&sParse, 0, sizeof(sParse));
+ sParse.db = pDb;
+ if( sqlite3OpenTempDatabase(&sParse) ){
+ sqlite3ErrorWithMsg(pErrorDb, sParse.rc, "%s", sParse.zErrMsg);
+ rc = SQLITE_ERROR;
}
+ sqlite3DbFree(pErrorDb, sParse.zErrMsg);
+ sqlite3ParserReset(&sParse);
if( rc ){
return 0;
}
}
if( i<0 ){
- sqlite3Error(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb);
+ sqlite3ErrorWithMsg(pErrorDb, SQLITE_ERROR, "unknown database %s", zDb);
return 0;
}
@@ -56573,6 +68076,20 @@ static int setDestPgsz(sqlite3_backup *p){
}
/*
+** Check that there is no open read-transaction on the b-tree passed as the
+** second argument. If there is not, return SQLITE_OK. Otherwise, if there
+** is an open read-transaction, return SQLITE_ERROR and leave an error
+** message in database handle db.
+*/
+static int checkReadTransaction(sqlite3 *db, Btree *p){
+ if( sqlite3BtreeIsInReadTrans(p) ){
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "destination database is in use");
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+}
+
+/*
** Create an sqlite3_backup process to copy the contents of zSrcDb from
** connection handle pSrcDb to zDestDb in pDestDb. If successful, return
** a pointer to the new sqlite3_backup object.
@@ -56588,6 +68105,13 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
){
sqlite3_backup *p; /* Value to return */
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(pSrcDb)||!sqlite3SafetyCheckOk(pDestDb) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+
/* Lock the source database handle. The destination database
** handle is not locked in this routine, but it is locked in
** sqlite3_backup_step(). The user is required to ensure that no
@@ -56600,7 +68124,7 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
sqlite3_mutex_enter(pDestDb->mutex);
if( pSrcDb==pDestDb ){
- sqlite3Error(
+ sqlite3ErrorWithMsg(
pDestDb, SQLITE_ERROR, "source and destination must be distinct"
);
p = 0;
@@ -56609,15 +68133,14 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
** EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
** call to sqlite3_backup_init() and is destroyed by a call to
** sqlite3_backup_finish(). */
- p = (sqlite3_backup *)sqlite3_malloc(sizeof(sqlite3_backup));
+ p = (sqlite3_backup *)sqlite3MallocZero(sizeof(sqlite3_backup));
if( !p ){
- sqlite3Error(pDestDb, SQLITE_NOMEM, 0);
+ sqlite3Error(pDestDb, SQLITE_NOMEM_BKPT);
}
}
/* If the allocation succeeded, populate the new object. */
if( p ){
- memset(p, 0, sizeof(sqlite3_backup));
p->pSrc = findBtree(pDestDb, pSrcDb, zSrcDb);
p->pDest = findBtree(pDestDb, pDestDb, zDestDb);
p->pDestDb = pDestDb;
@@ -56625,12 +68148,14 @@ SQLITE_API sqlite3_backup *sqlite3_backup_init(
p->iNext = 1;
p->isAttached = 0;
- if( 0==p->pSrc || 0==p->pDest || setDestPgsz(p)==SQLITE_NOMEM ){
+ if( 0==p->pSrc || 0==p->pDest
+ || checkReadTransaction(pDestDb, p->pDest)!=SQLITE_OK
+ ){
/* One (or both) of the named databases did not exist or an OOM
- ** error was hit. The error has already been written into the
- ** pDestDb handle. All that is left to do here is free the
- ** sqlite3_backup structure.
- */
+ ** error was hit. Or there is a transaction open on the destination
+ ** database. The error has already been written into the pDestDb
+ ** handle. All that is left to do here is free the sqlite3_backup
+ ** structure. */
sqlite3_free(p);
p = 0;
}
@@ -56658,20 +68183,28 @@ static int isFatalError(int rc){
** page iSrcPg from the source database. Copy this data into the
** destination database.
*/
-static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
+static int backupOnePage(
+ sqlite3_backup *p, /* Backup handle */
+ Pgno iSrcPg, /* Source database page to backup */
+ const u8 *zSrcData, /* Source database page data */
+ int bUpdate /* True for an update, false otherwise */
+){
Pager * const pDestPager = sqlite3BtreePager(p->pDest);
const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc);
int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest);
const int nCopy = MIN(nSrcPgsz, nDestPgsz);
const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz;
#ifdef SQLITE_HAS_CODEC
- int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc);
- int nDestReserve = sqlite3BtreeGetReserve(p->pDest);
+ /* Use BtreeGetReserveNoMutex() for the source b-tree, as although it is
+ ** guaranteed that the shared-mutex is held by this thread, handle
+ ** p->pSrc may not actually be the owner. */
+ int nSrcReserve = sqlite3BtreeGetReserveNoMutex(p->pSrc);
+ int nDestReserve = sqlite3BtreeGetOptimalReserve(p->pDest);
#endif
-
int rc = SQLITE_OK;
i64 iOff;
+ assert( sqlite3BtreeGetReserveNoMutex(p->pSrc)>=0 );
assert( p->bDestLocked );
assert( !isFatalError(p->rc) );
assert( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) );
@@ -56712,7 +68245,7 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
DbPage *pDestPg = 0;
Pgno iDest = (Pgno)(iOff/nDestPgsz)+1;
if( iDest==PENDING_BYTE_PAGE(p->pDest->pBt) ) continue;
- if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg))
+ if( SQLITE_OK==(rc = sqlite3PagerGet(pDestPager, iDest, &pDestPg, 0))
&& SQLITE_OK==(rc = sqlite3PagerWrite(pDestPg))
){
const u8 *zIn = &zSrcData[iOff%nSrcPgsz];
@@ -56728,6 +68261,9 @@ static int backupOnePage(sqlite3_backup *p, Pgno iSrcPg, const u8 *zSrcData){
*/
memcpy(zOut, zIn, nCopy);
((u8 *)sqlite3PagerGetExtra(pDestPg))[0] = 0;
+ if( iOff==0 && bUpdate==0 ){
+ sqlite3Put4byte(&zOut[28], sqlite3BtreeLastPage(p->pSrc));
+ }
}
sqlite3PagerUnref(pDestPg);
}
@@ -56774,6 +68310,9 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
int pgszSrc = 0; /* Source page size */
int pgszDest = 0; /* Destination page size */
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(p->pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
if( p->pDestDb ){
@@ -56797,14 +68336,6 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
rc = SQLITE_OK;
}
- /* Lock the destination database, if it is not locked already. */
- if( SQLITE_OK==rc && p->bDestLocked==0
- && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
- ){
- p->bDestLocked = 1;
- sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
- }
-
/* If there is no open read-transaction on the source database, open
** one now. If a transaction is opened here, then it will be closed
** before this function exits.
@@ -56814,6 +68345,24 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
bCloseTrans = 1;
}
+ /* If the destination database has not yet been locked (i.e. if this
+ ** is the first call to backup_step() for the current backup operation),
+ ** try to set its page size to the same as the source database. This
+ ** is especially important on ZipVFS systems, as in that case it is
+ ** not possible to create a database file that uses one page size by
+ ** writing to it with another. */
+ if( p->bDestLocked==0 && rc==SQLITE_OK && setDestPgsz(p)==SQLITE_NOMEM ){
+ rc = SQLITE_NOMEM;
+ }
+
+ /* Lock the destination database, if it is not locked already. */
+ if( SQLITE_OK==rc && p->bDestLocked==0
+ && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2))
+ ){
+ p->bDestLocked = 1;
+ sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema);
+ }
+
/* Do not allow backup if the destination database is in WAL mode
** and the page sizes are different between source and destination */
pgszSrc = sqlite3BtreeGetPageSize(p->pSrc);
@@ -56832,9 +68381,9 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
const Pgno iSrcPg = p->iNext; /* Source page number */
if( iSrcPg!=PENDING_BYTE_PAGE(p->pSrc->pBt) ){
DbPage *pSrcPg; /* Source page object */
- rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
+ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg,PAGER_GET_READONLY);
if( rc==SQLITE_OK ){
- rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg));
+ rc = backupOnePage(p, iSrcPg, sqlite3PagerGetData(pSrcPg), 0);
sqlite3PagerUnref(pSrcPg);
}
}
@@ -56856,10 +68405,16 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
** same schema version.
*/
if( rc==SQLITE_DONE ){
- rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
+ if( nSrcPage==0 ){
+ rc = sqlite3BtreeNewDb(p->pDest);
+ nSrcPage = 1;
+ }
+ if( rc==SQLITE_OK || rc==SQLITE_DONE ){
+ rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1);
+ }
if( rc==SQLITE_OK ){
if( p->pDestDb ){
- sqlite3ResetInternalSchema(p->pDestDb, -1);
+ sqlite3ResetAllSchemasOfConnection(p->pDestDb);
}
if( destMode==PAGER_JOURNALMODE_WAL ){
rc = sqlite3BtreeSetVersion(p->pDest, 2);
@@ -56890,7 +68445,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
}else{
nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
}
- sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
+ assert( nDestTruncate>0 );
if( pgszSrc<pgszDest ){
/* If the source page-size is smaller than the destination page-size,
@@ -56904,22 +68459,38 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
+ Pgno iPg;
+ int nDstPage;
i64 iOff;
i64 iEnd;
assert( pFile );
- assert( (i64)nDestTruncate*(i64)pgszDest >= iSize || (
+ assert( nDestTruncate==0
+ || (i64)nDestTruncate*(i64)pgszDest >= iSize || (
nDestTruncate==(int)(PENDING_BYTE_PAGE(p->pDest->pBt)-1)
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
));
- /* This call ensures that all data required to recreate the original
+ /* This block ensures that all data required to recreate the original
** database has been stored in the journal for pDestPager and the
** journal synced to disk. So at this point we may safely modify
** the database file in any way, knowing that if a power failure
** occurs, the original database will be reconstructed from the
** journal file. */
- rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
+ sqlite3PagerPagecount(pDestPager, &nDstPage);
+ for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
+ if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){
+ DbPage *pPg;
+ rc = sqlite3PagerGet(pDestPager, iPg, &pPg, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerWrite(pPg);
+ sqlite3PagerUnref(pPg);
+ }
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
+ }
/* Write the extra pages and truncate the database file as required */
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
@@ -56930,7 +68501,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
){
PgHdr *pSrcPg = 0;
const Pgno iSrcPg = (Pgno)((iOff/pgszSrc)+1);
- rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg);
+ rc = sqlite3PagerGet(pSrcPager, iSrcPg, &pSrcPg, 0);
if( rc==SQLITE_OK ){
u8 *zData = sqlite3PagerGetData(pSrcPg);
rc = sqlite3OsWrite(pFile, zData, pgszSrc, iOff);
@@ -56943,9 +68514,10 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
/* Sync the database file to disk. */
if( rc==SQLITE_OK ){
- rc = sqlite3PagerSync(pDestPager);
+ rc = sqlite3PagerSync(pDestPager, 0);
}
}else{
+ sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
}
@@ -56971,7 +68543,7 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
}
if( rc==SQLITE_IOERR_NOMEM ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
p->rc = rc;
}
@@ -56988,14 +68560,14 @@ SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/
SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
sqlite3_backup **pp; /* Ptr to head of pagers backup list */
- sqlite3_mutex *mutex; /* Mutex to protect source database */
+ sqlite3 *pSrcDb; /* Source database connection */
int rc; /* Value to return */
/* Enter the mutexes */
if( p==0 ) return SQLITE_OK;
- sqlite3_mutex_enter(p->pSrcDb->mutex);
+ pSrcDb = p->pSrcDb;
+ sqlite3_mutex_enter(pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
- mutex = p->pSrcDb->mutex;
if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex);
}
@@ -57013,15 +68585,15 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
}
/* If a transaction is still open on the Btree, roll it back. */
- sqlite3BtreeRollback(p->pDest);
+ sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0);
/* Set the error code of the destination database handle. */
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
- sqlite3Error(p->pDestDb, rc, 0);
-
- /* Exit the mutexes and free the backup context structure. */
if( p->pDestDb ){
- sqlite3_mutex_leave(p->pDestDb->mutex);
+ sqlite3Error(p->pDestDb, rc);
+
+ /* Exit the mutexes and free the backup context structure. */
+ sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
}
sqlite3BtreeLeave(p->pSrc);
if( p->pDestDb ){
@@ -57030,7 +68602,7 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
** sqlite3_backup_finish(). */
sqlite3_free(p);
}
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(pSrcDb);
return rc;
}
@@ -57039,6 +68611,12 @@ SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
** call to sqlite3_backup_step().
*/
SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
return p->nRemaining;
}
@@ -57047,6 +68625,12 @@ SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p){
** recent call to sqlite3_backup_step().
*/
SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( p==0 ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
return p->nPagecount;
}
@@ -57062,9 +68646,13 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p){
** corresponding to the source database is held when this function is
** called.
*/
-SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
- sqlite3_backup *p; /* Iterator variable */
- for(p=pBackup; p; p=p->pNext){
+static SQLITE_NOINLINE void backupUpdate(
+ sqlite3_backup *p,
+ Pgno iPage,
+ const u8 *aData
+){
+ assert( p!=0 );
+ do{
assert( sqlite3_mutex_held(p->pSrc->pBt->mutex) );
if( !isFatalError(p->rc) && iPage<p->iNext ){
/* The backup process p has already copied page iPage. But now it
@@ -57074,14 +68662,17 @@ SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, con
int rc;
assert( p->pDestDb );
sqlite3_mutex_enter(p->pDestDb->mutex);
- rc = backupOnePage(p, iPage, aData);
+ rc = backupOnePage(p, iPage, aData, 1);
sqlite3_mutex_leave(p->pDestDb->mutex);
assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED );
if( rc!=SQLITE_OK ){
p->rc = rc;
}
}
- }
+ }while( (p = p->pNext)!=0 );
+}
+SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *pBackup, Pgno iPage, const u8 *aData){
+ if( pBackup ) backupUpdate(pBackup, iPage, aData);
}
/*
@@ -57114,10 +68705,20 @@ SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *pBackup){
*/
SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
int rc;
+ sqlite3_file *pFd; /* File descriptor for database pTo */
sqlite3_backup b;
sqlite3BtreeEnter(pTo);
sqlite3BtreeEnter(pFrom);
+ assert( sqlite3BtreeIsInTrans(pTo) );
+ pFd = sqlite3PagerFile(sqlite3BtreePager(pTo));
+ if( pFd->pMethods ){
+ i64 nByte = sqlite3BtreeGetPageSize(pFrom)*(i64)sqlite3BtreeLastPage(pFrom);
+ rc = sqlite3OsFileControl(pFd, SQLITE_FCNTL_OVERWRITE, &nByte);
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ if( rc ) goto copy_finished;
+ }
+
/* Set up an sqlite3_backup object. sqlite3_backup.pDestDb must be set
** to 0. This is used by the implementations of sqlite3_backup_step()
** and sqlite3_backup_finish() to detect that they are being called
@@ -57129,20 +68730,28 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
b.pDest = pTo;
b.iNext = 1;
+#ifdef SQLITE_HAS_CODEC
+ sqlite3PagerAlignReserve(sqlite3BtreePager(pTo), sqlite3BtreePager(pFrom));
+#endif
+
/* 0x7FFFFFFF is the hard limit for the number of pages in a database
** file. By passing this as the number of pages to copy to
** sqlite3_backup_step(), we can guarantee that the copy finishes
** within a single call (unless an error occurs). The assert() statement
** checks this assumption - (p->rc) should be set to either SQLITE_DONE
- ** or an error code.
- */
+ ** or an error code. */
sqlite3_backup_step(&b, 0x7FFFFFFF);
assert( b.rc!=SQLITE_OK );
+
rc = sqlite3_backup_finish(&b);
if( rc==SQLITE_OK ){
- pTo->pBt->pageSizeFixed = 0;
+ pTo->pBt->btsFlags &= ~BTS_PAGESIZE_FIXED;
+ }else{
+ sqlite3PagerClearCache(sqlite3BtreePager(b.pDest));
}
+ assert( sqlite3BtreeIsInTrans(pTo)==0 );
+copy_finished:
sqlite3BtreeLeave(pFrom);
sqlite3BtreeLeave(pTo);
return rc;
@@ -57168,12 +68777,55 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
** only within the VDBE. Interface routines refer to a Mem using the
** name sqlite_value
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
+#ifdef SQLITE_DEBUG
/*
-** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*)
-** P if required.
+** Check invariants on a Mem object.
+**
+** This routine is intended for use inside of assert() statements, like
+** this: assert( sqlite3VdbeCheckMemInvariants(pMem) );
*/
-#define expandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
+SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem *p){
+ /* If MEM_Dyn is set then Mem.xDel!=0.
+ ** Mem.xDel is might not be initialized if MEM_Dyn is clear.
+ */
+ assert( (p->flags & MEM_Dyn)==0 || p->xDel!=0 );
+
+ /* MEM_Dyn may only be set if Mem.szMalloc==0. In this way we
+ ** ensure that if Mem.szMalloc>0 then it is safe to do
+ ** Mem.z = Mem.zMalloc without having to check Mem.flags&MEM_Dyn.
+ ** That saves a few cycles in inner loops. */
+ assert( (p->flags & MEM_Dyn)==0 || p->szMalloc==0 );
+
+ /* Cannot be both MEM_Int and MEM_Real at the same time */
+ assert( (p->flags & (MEM_Int|MEM_Real))!=(MEM_Int|MEM_Real) );
+
+ /* The szMalloc field holds the correct memory allocation size */
+ assert( p->szMalloc==0
+ || p->szMalloc==sqlite3DbMallocSize(p->db,p->zMalloc) );
+
+ /* If p holds a string or blob, the Mem.z must point to exactly
+ ** one of the following:
+ **
+ ** (1) Memory in Mem.zMalloc and managed by the Mem object
+ ** (2) Memory to be freed using Mem.xDel
+ ** (3) An ephemeral string or blob
+ ** (4) A static string or blob
+ */
+ if( (p->flags & (MEM_Str|MEM_Blob)) && p->n>0 ){
+ assert(
+ ((p->szMalloc>0 && p->z==p->zMalloc)? 1 : 0) +
+ ((p->flags&MEM_Dyn)!=0 ? 1 : 0) +
+ ((p->flags&MEM_Ephem)!=0 ? 1 : 0) +
+ ((p->flags&MEM_Static)!=0 ? 1 : 0) == 1
+ );
+ }
+ return 1;
+}
+#endif
+
/*
** If pMem is an object with a valid string representation, this routine
@@ -57189,7 +68841,9 @@ SQLITE_PRIVATE int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
** between formats.
*/
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
+#ifndef SQLITE_OMIT_UTF16
int rc;
+#endif
assert( (pMem->flags&MEM_RowSet)==0 );
assert( desiredEnc==SQLITE_UTF8 || desiredEnc==SQLITE_UTF16LE
|| desiredEnc==SQLITE_UTF16BE );
@@ -57214,79 +68868,106 @@ SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *pMem, int desiredEnc){
/*
** Make sure pMem->z points to a writable allocation of at least
-** n bytes.
-**
-** If the memory cell currently contains string or blob data
-** and the third argument passed to this function is true, the
-** current content of the cell is preserved. Otherwise, it may
-** be discarded.
-**
-** This function sets the MEM_Dyn flag and clears any xDel callback.
-** It also clears MEM_Ephem and MEM_Static. If the preserve flag is
-** not set, Mem.n is zeroed.
-*/
-SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve){
- assert( 1 >=
- ((pMem->zMalloc && pMem->zMalloc==pMem->z) ? 1 : 0) +
- (((pMem->flags&MEM_Dyn)&&pMem->xDel) ? 1 : 0) +
- ((pMem->flags&MEM_Ephem) ? 1 : 0) +
- ((pMem->flags&MEM_Static) ? 1 : 0)
- );
+** min(n,32) bytes.
+**
+** If the bPreserve argument is true, then copy of the content of
+** pMem->z into the new allocation. pMem must be either a string or
+** blob if bPreserve is true. If bPreserve is false, any prior content
+** in pMem->z is discarded.
+*/
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemGrow(Mem *pMem, int n, int bPreserve){
+ assert( sqlite3VdbeCheckMemInvariants(pMem) );
assert( (pMem->flags&MEM_RowSet)==0 );
-
- if( n<32 ) n = 32;
- if( sqlite3DbMallocSize(pMem->db, pMem->zMalloc)<n ){
- if( preserve && pMem->z==pMem->zMalloc ){
+ testcase( pMem->db==0 );
+
+ /* If the bPreserve flag is set to true, then the memory cell must already
+ ** contain a valid string or blob value. */
+ assert( bPreserve==0 || pMem->flags&(MEM_Blob|MEM_Str) );
+ testcase( bPreserve && pMem->z==0 );
+
+ assert( pMem->szMalloc==0
+ || pMem->szMalloc==sqlite3DbMallocSize(pMem->db, pMem->zMalloc) );
+ if( pMem->szMalloc<n ){
+ if( n<32 ) n = 32;
+ if( bPreserve && pMem->szMalloc>0 && pMem->z==pMem->zMalloc ){
pMem->z = pMem->zMalloc = sqlite3DbReallocOrFree(pMem->db, pMem->z, n);
- preserve = 0;
+ bPreserve = 0;
}else{
- sqlite3DbFree(pMem->db, pMem->zMalloc);
+ if( pMem->szMalloc>0 ) sqlite3DbFree(pMem->db, pMem->zMalloc);
pMem->zMalloc = sqlite3DbMallocRaw(pMem->db, n);
}
+ if( pMem->zMalloc==0 ){
+ sqlite3VdbeMemSetNull(pMem);
+ pMem->z = 0;
+ pMem->szMalloc = 0;
+ return SQLITE_NOMEM_BKPT;
+ }else{
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
+ }
}
- if( pMem->z && preserve && pMem->zMalloc && pMem->z!=pMem->zMalloc ){
+ if( bPreserve && pMem->z && pMem->z!=pMem->zMalloc ){
memcpy(pMem->zMalloc, pMem->z, pMem->n);
}
- if( pMem->flags&MEM_Dyn && pMem->xDel ){
+ if( (pMem->flags&MEM_Dyn)!=0 ){
+ assert( pMem->xDel!=0 && pMem->xDel!=SQLITE_DYNAMIC );
pMem->xDel((void *)(pMem->z));
}
pMem->z = pMem->zMalloc;
- if( pMem->z==0 ){
- pMem->flags = MEM_Null;
- }else{
- pMem->flags &= ~(MEM_Ephem|MEM_Static);
+ pMem->flags &= ~(MEM_Dyn|MEM_Ephem|MEM_Static);
+ return SQLITE_OK;
+}
+
+/*
+** Change the pMem->zMalloc allocation to be at least szNew bytes.
+** If pMem->zMalloc already meets or exceeds the requested size, this
+** routine is a no-op.
+**
+** Any prior string or blob content in the pMem object may be discarded.
+** The pMem->xDel destructor is called, if it exists. Though MEM_Str
+** and MEM_Blob values may be discarded, MEM_Int, MEM_Real, and MEM_Null
+** values are preserved.
+**
+** Return SQLITE_OK on success or an error code (probably SQLITE_NOMEM)
+** if unable to complete the resizing.
+*/
+SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int szNew){
+ assert( szNew>0 );
+ assert( (pMem->flags & MEM_Dyn)==0 || pMem->szMalloc==0 );
+ if( pMem->szMalloc<szNew ){
+ return sqlite3VdbeMemGrow(pMem, szNew, 0);
}
- pMem->xDel = 0;
- return (pMem->z ? SQLITE_OK : SQLITE_NOMEM);
+ assert( (pMem->flags & MEM_Dyn)==0 );
+ pMem->z = pMem->zMalloc;
+ pMem->flags &= (MEM_Null|MEM_Int|MEM_Real);
+ return SQLITE_OK;
}
/*
-** Make the given Mem object MEM_Dyn. In other words, make it so
-** that any TEXT or BLOB content is stored in memory obtained from
-** malloc(). In this way, we know that the memory is safe to be
-** overwritten or altered.
+** Change pMem so that its MEM_Str or MEM_Blob value is stored in
+** MEM.zMalloc, where it can be safely written.
**
** Return SQLITE_OK on success or SQLITE_NOMEM if malloc fails.
*/
SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
- int f;
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( (pMem->flags&MEM_RowSet)==0 );
- expandBlob(pMem);
- f = pMem->flags;
- if( (f&(MEM_Str|MEM_Blob)) && pMem->z!=pMem->zMalloc ){
- if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
- return SQLITE_NOMEM;
+ if( (pMem->flags & (MEM_Str|MEM_Blob))!=0 ){
+ if( ExpandBlob(pMem) ) return SQLITE_NOMEM;
+ if( pMem->szMalloc==0 || pMem->z!=pMem->zMalloc ){
+ if( sqlite3VdbeMemGrow(pMem, pMem->n + 2, 1) ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ pMem->z[pMem->n] = 0;
+ pMem->z[pMem->n+1] = 0;
+ pMem->flags |= MEM_Term;
}
- pMem->z[pMem->n] = 0;
- pMem->z[pMem->n+1] = 0;
- pMem->flags |= MEM_Term;
+ }
+ pMem->flags &= ~MEM_Ephem;
#ifdef SQLITE_DEBUG
- pMem->pScopyFrom = 0;
+ pMem->pScopyFrom = 0;
#endif
- }
return SQLITE_OK;
}
@@ -57297,40 +68978,35 @@ SQLITE_PRIVATE int sqlite3VdbeMemMakeWriteable(Mem *pMem){
*/
#ifndef SQLITE_OMIT_INCRBLOB
SQLITE_PRIVATE int sqlite3VdbeMemExpandBlob(Mem *pMem){
- if( pMem->flags & MEM_Zero ){
- int nByte;
- assert( pMem->flags&MEM_Blob );
- assert( (pMem->flags&MEM_RowSet)==0 );
- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
-
- /* Set nByte to the number of bytes required to store the expanded blob. */
- nByte = pMem->n + pMem->u.nZero;
- if( nByte<=0 ){
- nByte = 1;
- }
- if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
- return SQLITE_NOMEM;
- }
+ int nByte;
+ assert( pMem->flags & MEM_Zero );
+ assert( pMem->flags&MEM_Blob );
+ assert( (pMem->flags&MEM_RowSet)==0 );
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
- pMem->n += pMem->u.nZero;
- pMem->flags &= ~(MEM_Zero|MEM_Term);
+ /* Set nByte to the number of bytes required to store the expanded blob. */
+ nByte = pMem->n + pMem->u.nZero;
+ if( nByte<=0 ){
+ nByte = 1;
+ }
+ if( sqlite3VdbeMemGrow(pMem, nByte, 1) ){
+ return SQLITE_NOMEM_BKPT;
}
+
+ memset(&pMem->z[pMem->n], 0, pMem->u.nZero);
+ pMem->n += pMem->u.nZero;
+ pMem->flags &= ~(MEM_Zero|MEM_Term);
return SQLITE_OK;
}
#endif
-
/*
-** Make sure the given Mem is \u0000 terminated.
+** It is already known that pMem contains an unterminated string.
+** Add the zero terminator.
*/
-SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
- assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
- if( (pMem->flags & MEM_Term)!=0 || (pMem->flags & MEM_Str)==0 ){
- return SQLITE_OK; /* Nothing to do */
- }
+static SQLITE_NOINLINE int vdbeMemAddTerminator(Mem *pMem){
if( sqlite3VdbeMemGrow(pMem, pMem->n+2, 1) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pMem->z[pMem->n] = 0;
pMem->z[pMem->n+1] = 0;
@@ -57339,20 +69015,34 @@ SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
}
/*
+** Make sure the given Mem is \u0000 terminated.
+*/
+SQLITE_PRIVATE int sqlite3VdbeMemNulTerminate(Mem *pMem){
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
+ testcase( (pMem->flags & (MEM_Term|MEM_Str))==(MEM_Term|MEM_Str) );
+ testcase( (pMem->flags & (MEM_Term|MEM_Str))==0 );
+ if( (pMem->flags & (MEM_Term|MEM_Str))!=MEM_Str ){
+ return SQLITE_OK; /* Nothing to do */
+ }else{
+ return vdbeMemAddTerminator(pMem);
+ }
+}
+
+/*
** Add MEM_Str to the set of representations for the given Mem. Numbers
** are converted using sqlite3_snprintf(). Converting a BLOB to a string
** is a no-op.
**
-** Existing representations MEM_Int and MEM_Real are *not* invalidated.
+** Existing representations MEM_Int and MEM_Real are invalidated if
+** bForce is true but are retained if bForce is false.
**
** A MEM_Null value will never be passed to this function. This function is
** used for converting values to text for returning to the user (i.e. via
** sqlite3_value_text()), or for ensuring that values to be used as btree
** keys are strings. In the former case a NULL pointer is returned the
-** user and the later is an internal programming error.
+** user and the latter is an internal programming error.
*/
-SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, int enc){
- int rc = SQLITE_OK;
+SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, u8 enc, u8 bForce){
int fg = pMem->flags;
const int nByte = 32;
@@ -57364,11 +69054,12 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, int enc){
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- if( sqlite3VdbeMemGrow(pMem, nByte, 0) ){
- return SQLITE_NOMEM;
+ if( sqlite3VdbeMemClearAndResize(pMem, nByte) ){
+ pMem->enc = 0;
+ return SQLITE_NOMEM_BKPT;
}
- /* For a Real or Integer, use sqlite3_mprintf() to produce the UTF-8
+ /* For a Real or Integer, use sqlite3_snprintf() to produce the UTF-8
** string representation of the value. Then, if the required encoding
** is UTF-16le or UTF-16be do a translation.
**
@@ -57378,13 +69069,14 @@ SQLITE_PRIVATE int sqlite3VdbeMemStringify(Mem *pMem, int enc){
sqlite3_snprintf(nByte, pMem->z, "%lld", pMem->u.i);
}else{
assert( fg & MEM_Real );
- sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->r);
+ sqlite3_snprintf(nByte, pMem->z, "%!.15g", pMem->u.r);
}
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
pMem->flags |= MEM_Str|MEM_Term;
+ if( bForce ) pMem->flags &= ~(MEM_Int|MEM_Real);
sqlite3VdbeChangeEncoding(pMem, enc);
- return rc;
+ return SQLITE_OK;
}
/*
@@ -57399,68 +69091,96 @@ SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem *pMem, FuncDef *pFunc){
int rc = SQLITE_OK;
if( ALWAYS(pFunc && pFunc->xFinalize) ){
sqlite3_context ctx;
+ Mem t;
assert( (pMem->flags & MEM_Null)!=0 || pFunc==pMem->u.pDef );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
memset(&ctx, 0, sizeof(ctx));
- ctx.s.flags = MEM_Null;
- ctx.s.db = pMem->db;
+ memset(&t, 0, sizeof(t));
+ t.flags = MEM_Null;
+ t.db = pMem->db;
+ ctx.pOut = &t;
ctx.pMem = pMem;
ctx.pFunc = pFunc;
pFunc->xFinalize(&ctx); /* IMP: R-24505-23230 */
- assert( 0==(pMem->flags&MEM_Dyn) && !pMem->xDel );
- sqlite3DbFree(pMem->db, pMem->zMalloc);
- memcpy(pMem, &ctx.s, sizeof(ctx.s));
+ assert( (pMem->flags & MEM_Dyn)==0 );
+ if( pMem->szMalloc>0 ) sqlite3DbFree(pMem->db, pMem->zMalloc);
+ memcpy(pMem, &t, sizeof(t));
rc = ctx.isError;
}
return rc;
}
/*
-** If the memory cell contains a string value that must be freed by
-** invoking an external callback, free it now. Calling this function
-** does not free any Mem.zMalloc buffer.
+** If the memory cell contains a value that must be freed by
+** invoking the external callback in Mem.xDel, then this routine
+** will free that value. It also sets Mem.flags to MEM_Null.
+**
+** This is a helper routine for sqlite3VdbeMemSetNull() and
+** for sqlite3VdbeMemRelease(). Use those other routines as the
+** entry point for releasing Mem resources.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){
+static SQLITE_NOINLINE void vdbeMemClearExternAndSetNull(Mem *p){
assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) );
+ assert( VdbeMemDynamic(p) );
if( p->flags&MEM_Agg ){
sqlite3VdbeMemFinalize(p, p->u.pDef);
assert( (p->flags & MEM_Agg)==0 );
- sqlite3VdbeMemRelease(p);
- }else if( p->flags&MEM_Dyn && p->xDel ){
+ testcase( p->flags & MEM_Dyn );
+ }
+ if( p->flags&MEM_Dyn ){
assert( (p->flags&MEM_RowSet)==0 );
+ assert( p->xDel!=SQLITE_DYNAMIC && p->xDel!=0 );
p->xDel((void *)p->z);
- p->xDel = 0;
}else if( p->flags&MEM_RowSet ){
sqlite3RowSetClear(p->u.pRowSet);
}else if( p->flags&MEM_Frame ){
- sqlite3VdbeMemSetNull(p);
+ VdbeFrame *pFrame = p->u.pFrame;
+ pFrame->pParent = pFrame->v->pDelFrame;
+ pFrame->v->pDelFrame = pFrame;
}
+ p->flags = MEM_Null;
}
/*
-** Release any memory held by the Mem. This may leave the Mem in an
-** inconsistent state, for example with (Mem.z==0) and
-** (Mem.type==SQLITE_TEXT).
+** Release memory held by the Mem p, both external memory cleared
+** by p->xDel and memory in p->zMalloc.
+**
+** This is a helper routine invoked by sqlite3VdbeMemRelease() in
+** the unusual case where there really is memory in p that needs
+** to be freed.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){
- MemReleaseExt(p);
- sqlite3DbFree(p->db, p->zMalloc);
+static SQLITE_NOINLINE void vdbeMemClear(Mem *p){
+ if( VdbeMemDynamic(p) ){
+ vdbeMemClearExternAndSetNull(p);
+ }
+ if( p->szMalloc ){
+ sqlite3DbFree(p->db, p->zMalloc);
+ p->szMalloc = 0;
+ }
p->z = 0;
- p->zMalloc = 0;
- p->xDel = 0;
}
/*
-** Convert a 64-bit IEEE double into a 64-bit signed integer.
-** If the double is too large, return 0x8000000000000000.
+** Release any memory resources held by the Mem. Both the memory that is
+** free by Mem.xDel and the Mem.zMalloc allocation are freed.
**
-** Most systems appear to do this simply by assigning
-** variables and without the extra range tests. But
-** there are reports that windows throws an expection
-** if the floating point value is out of range. (See ticket #2880.)
-** Because we do not completely understand the problem, we will
-** take the conservative approach and always do range tests
-** before attempting the conversion.
+** Use this routine prior to clean up prior to abandoning a Mem, or to
+** reset a Mem back to its minimum memory utilization.
+**
+** Use sqlite3VdbeMemSetNull() to release just the Mem.xDel space
+** prior to inserting new content into the Mem.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p){
+ assert( sqlite3VdbeCheckMemInvariants(p) );
+ if( VdbeMemDynamic(p) || p->szMalloc ){
+ vdbeMemClear(p);
+ }
+}
+
+/*
+** Convert a 64-bit IEEE double into a 64-bit signed integer.
+** If the double is out of range of a 64-bit signed integer then
+** return the closest available 64-bit signed integer.
*/
static i64 doubleToInt64(double r){
#ifdef SQLITE_OMIT_FLOATING_POINT
@@ -57477,14 +69197,10 @@ static i64 doubleToInt64(double r){
static const i64 maxInt = LARGEST_INT64;
static const i64 minInt = SMALLEST_INT64;
- if( r<(double)minInt ){
- return minInt;
- }else if( r>(double)maxInt ){
- /* minInt is correct here - not maxInt. It turns out that assigning
- ** a very large positive number to an integer results in a very large
- ** negative integer. This makes no sense, but it is what x86 hardware
- ** does so for compatibility we will do the same in software. */
+ if( r<=(double)minInt ){
return minInt;
+ }else if( r>=(double)maxInt ){
+ return maxInt;
}else{
return (i64)r;
}
@@ -57497,7 +69213,7 @@ static i64 doubleToInt64(double r){
** If pMem is an integer, then the value is exact. If pMem is
** a floating-point then the value returned is the integer part.
** If pMem is a string or blob, then we make an attempt to convert
-** it into a integer and return that. If pMem represents an
+** it into an integer and return that. If pMem represents an
** an SQL-NULL value, return 0.
**
** If pMem represents a string value, its encoding might be changed.
@@ -57510,11 +69226,10 @@ SQLITE_PRIVATE i64 sqlite3VdbeIntValue(Mem *pMem){
if( flags & MEM_Int ){
return pMem->u.i;
}else if( flags & MEM_Real ){
- return doubleToInt64(pMem->r);
+ return doubleToInt64(pMem->u.r);
}else if( flags & (MEM_Str|MEM_Blob) ){
i64 value = 0;
assert( pMem->z || pMem->n==0 );
- testcase( pMem->z==0 );
sqlite3Atoi64(pMem->z, &value, pMem->n, pMem->enc);
return value;
}else{
@@ -57532,7 +69247,7 @@ SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
if( pMem->flags & MEM_Real ){
- return pMem->r;
+ return pMem->u.r;
}else if( pMem->flags & MEM_Int ){
return (double)pMem->u.i;
}else if( pMem->flags & (MEM_Str|MEM_Blob) ){
@@ -57551,12 +69266,13 @@ SQLITE_PRIVATE double sqlite3VdbeRealValue(Mem *pMem){
** MEM_Int if we can.
*/
SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
+ i64 ix;
assert( pMem->flags & MEM_Real );
assert( (pMem->flags & MEM_RowSet)==0 );
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- pMem->u.i = doubleToInt64(pMem->r);
+ ix = doubleToInt64(pMem->u.r);
/* Only mark the value as an integer if
**
@@ -57566,13 +69282,11 @@ SQLITE_PRIVATE void sqlite3VdbeIntegerAffinity(Mem *pMem){
**
** The second and third terms in the following conditional enforces
** the second condition under the assumption that addition overflow causes
- ** values to wrap around. On x86 hardware, the third term is always
- ** true and could be omitted. But we leave it in because other
- ** architectures might behave differently.
+ ** values to wrap around.
*/
- if( pMem->r==(double)pMem->u.i && pMem->u.i>SMALLEST_INT64
- && ALWAYS(pMem->u.i<LARGEST_INT64) ){
- pMem->flags |= MEM_Int;
+ if( pMem->u.r==ix && ix>SMALLEST_INT64 && ix<LARGEST_INT64 ){
+ pMem->u.i = ix;
+ MemSetTypeFlag(pMem, MEM_Int);
}
}
@@ -57597,7 +69311,7 @@ SQLITE_PRIVATE int sqlite3VdbeMemRealify(Mem *pMem){
assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) );
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
- pMem->r = sqlite3VdbeRealValue(pMem);
+ pMem->u.r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
return SQLITE_OK;
}
@@ -57617,30 +69331,94 @@ SQLITE_PRIVATE int sqlite3VdbeMemNumerify(Mem *pMem){
if( 0==sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc) ){
MemSetTypeFlag(pMem, MEM_Int);
}else{
- pMem->r = sqlite3VdbeRealValue(pMem);
+ pMem->u.r = sqlite3VdbeRealValue(pMem);
MemSetTypeFlag(pMem, MEM_Real);
sqlite3VdbeIntegerAffinity(pMem);
}
}
assert( (pMem->flags & (MEM_Int|MEM_Real|MEM_Null))!=0 );
- pMem->flags &= ~(MEM_Str|MEM_Blob);
+ pMem->flags &= ~(MEM_Str|MEM_Blob|MEM_Zero);
return SQLITE_OK;
}
/*
+** Cast the datatype of the value in pMem according to the affinity
+** "aff". Casting is different from applying affinity in that a cast
+** is forced. In other words, the value is converted into the desired
+** affinity even if that results in loss of data. This routine is
+** used (for example) to implement the SQL "cast()" operator.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemCast(Mem *pMem, u8 aff, u8 encoding){
+ if( pMem->flags & MEM_Null ) return;
+ switch( aff ){
+ case SQLITE_AFF_BLOB: { /* Really a cast to BLOB */
+ if( (pMem->flags & MEM_Blob)==0 ){
+ sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
+ assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
+ if( pMem->flags & MEM_Str ) MemSetTypeFlag(pMem, MEM_Blob);
+ }else{
+ pMem->flags &= ~(MEM_TypeMask&~MEM_Blob);
+ }
+ break;
+ }
+ case SQLITE_AFF_NUMERIC: {
+ sqlite3VdbeMemNumerify(pMem);
+ break;
+ }
+ case SQLITE_AFF_INTEGER: {
+ sqlite3VdbeMemIntegerify(pMem);
+ break;
+ }
+ case SQLITE_AFF_REAL: {
+ sqlite3VdbeMemRealify(pMem);
+ break;
+ }
+ default: {
+ assert( aff==SQLITE_AFF_TEXT );
+ assert( MEM_Str==(MEM_Blob>>3) );
+ pMem->flags |= (pMem->flags&MEM_Blob)>>3;
+ sqlite3ValueApplyAffinity(pMem, SQLITE_AFF_TEXT, encoding);
+ assert( pMem->flags & MEM_Str || pMem->db->mallocFailed );
+ pMem->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero);
+ break;
+ }
+ }
+}
+
+/*
+** Initialize bulk memory to be a consistent Mem object.
+**
+** The minimum amount of initialization feasible is performed.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMemInit(Mem *pMem, sqlite3 *db, u16 flags){
+ assert( (flags & ~MEM_TypeMask)==0 );
+ pMem->flags = flags;
+ pMem->db = db;
+ pMem->szMalloc = 0;
+}
+
+
+/*
** Delete any previous value and set the value stored in *pMem to NULL.
+**
+** This routine calls the Mem.xDel destructor to dispose of values that
+** require the destructor. But it preserves the Mem.zMalloc memory allocation.
+** To free all resources, use sqlite3VdbeMemRelease(), which both calls this
+** routine to invoke the destructor and deallocates Mem.zMalloc.
+**
+** Use this routine to reset the Mem prior to insert a new value.
+**
+** Use sqlite3VdbeMemRelease() to complete erase the Mem prior to abandoning it.
*/
SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){
- if( pMem->flags & MEM_Frame ){
- VdbeFrame *pFrame = pMem->u.pFrame;
- pFrame->pParent = pFrame->v->pDelFrame;
- pFrame->v->pDelFrame = pFrame;
- }
- if( pMem->flags & MEM_RowSet ){
- sqlite3RowSetClear(pMem->u.pRowSet);
+ if( VdbeMemDynamic(pMem) ){
+ vdbeMemClearExternAndSetNull(pMem);
+ }else{
+ pMem->flags = MEM_Null;
}
- MemSetTypeFlag(pMem, MEM_Null);
- pMem->type = SQLITE_NULL;
+}
+SQLITE_PRIVATE void sqlite3ValueSetNull(sqlite3_value *p){
+ sqlite3VdbeMemSetNull((Mem*)p);
}
/*
@@ -57650,19 +69428,22 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){
SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
sqlite3VdbeMemRelease(pMem);
pMem->flags = MEM_Blob|MEM_Zero;
- pMem->type = SQLITE_BLOB;
pMem->n = 0;
if( n<0 ) n = 0;
pMem->u.nZero = n;
pMem->enc = SQLITE_UTF8;
+ pMem->z = 0;
+}
-#ifdef SQLITE_OMIT_INCRBLOB
- sqlite3VdbeMemGrow(pMem, n, 0);
- if( pMem->z ){
- pMem->n = n;
- memset(pMem->z, 0, n);
- }
-#endif
+/*
+** The pMem is known to contain content that needs to be destroyed prior
+** to a value change. So invoke the destructor, then set the value to
+** a 64-bit integer.
+*/
+static SQLITE_NOINLINE void vdbeReleaseAndSetInt64(Mem *pMem, i64 val){
+ sqlite3VdbeMemSetNull(pMem);
+ pMem->u.i = val;
+ pMem->flags = MEM_Int;
}
/*
@@ -57670,10 +69451,12 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetZeroBlob(Mem *pMem, int n){
** manifest type INTEGER.
*/
SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
- sqlite3VdbeMemRelease(pMem);
- pMem->u.i = val;
- pMem->flags = MEM_Int;
- pMem->type = SQLITE_INTEGER;
+ if( VdbeMemDynamic(pMem) ){
+ vdbeReleaseAndSetInt64(pMem, val);
+ }else{
+ pMem->u.i = val;
+ pMem->flags = MEM_Int;
+ }
}
#ifndef SQLITE_OMIT_FLOATING_POINT
@@ -57682,13 +69465,10 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetInt64(Mem *pMem, i64 val){
** manifest type REAL.
*/
SQLITE_PRIVATE void sqlite3VdbeMemSetDouble(Mem *pMem, double val){
- if( sqlite3IsNaN(val) ){
- sqlite3VdbeMemSetNull(pMem);
- }else{
- sqlite3VdbeMemRelease(pMem);
- pMem->r = val;
+ sqlite3VdbeMemSetNull(pMem);
+ if( !sqlite3IsNaN(val) ){
+ pMem->u.r = val;
pMem->flags = MEM_Real;
- pMem->type = SQLITE_FLOAT;
}
}
#endif
@@ -57702,13 +69482,14 @@ SQLITE_PRIVATE void sqlite3VdbeMemSetRowSet(Mem *pMem){
assert( db!=0 );
assert( (pMem->flags & MEM_RowSet)==0 );
sqlite3VdbeMemRelease(pMem);
- pMem->zMalloc = sqlite3DbMallocRaw(db, 64);
+ pMem->zMalloc = sqlite3DbMallocRawNN(db, 64);
if( db->mallocFailed ){
pMem->flags = MEM_Null;
+ pMem->szMalloc = 0;
}else{
assert( pMem->zMalloc );
- pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc,
- sqlite3DbMallocSize(db, pMem->zMalloc));
+ pMem->szMalloc = sqlite3DbMallocSize(db, pMem->zMalloc);
+ pMem->u.pRowSet = sqlite3RowSetInit(db, pMem->zMalloc, pMem->szMalloc);
assert( pMem->u.pRowSet!=0 );
pMem->flags = MEM_RowSet;
}
@@ -57732,19 +69513,19 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){
#ifdef SQLITE_DEBUG
/*
-** This routine prepares a memory cell for modication by breaking
+** This routine prepares a memory cell for modification by breaking
** its link to a shallow copy and by marking any current shallow
** copies of this cell as invalid.
**
** This is used for testing and debugging only - to make sure shallow
** copies are not misused.
*/
-SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
+SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
int i;
Mem *pX;
- for(i=1, pX=&pVdbe->aMem[1]; i<=pVdbe->nMem; i++, pX++){
+ for(i=0, pX=pVdbe->aMem; i<pVdbe->nMem; i++, pX++){
if( pX->pScopyFrom==pMem ){
- pX->flags |= MEM_Invalid;
+ pX->flags |= MEM_Undefined;
pX->pScopyFrom = 0;
}
}
@@ -57752,10 +69533,6 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
}
#endif /* SQLITE_DEBUG */
-/*
-** Size of struct Mem not including the Mem.zMalloc member.
-*/
-#define MEMCELLSIZE (size_t)(&(((Mem *)0)->zMalloc))
/*
** Make an shallow copy of pFrom into pTo. Prior contents of
@@ -57763,11 +69540,16 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrepareToChange(Vdbe *pVdbe, Mem *pMem){
** pFrom->z is used, then pTo->z points to the same thing as pFrom->z
** and flags gets srcType (either MEM_Ephem or MEM_Static).
*/
+static SQLITE_NOINLINE void vdbeClrCopy(Mem *pTo, const Mem *pFrom, int eType){
+ vdbeMemClearExternAndSetNull(pTo);
+ assert( !VdbeMemDynamic(pTo) );
+ sqlite3VdbeMemShallowCopy(pTo, pFrom, eType);
+}
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem *pTo, const Mem *pFrom, int srcType){
assert( (pFrom->flags & MEM_RowSet)==0 );
- MemReleaseExt(pTo);
+ assert( pTo->db==pFrom->db );
+ if( VdbeMemDynamic(pTo) ){ vdbeClrCopy(pTo,pFrom,srcType); return; }
memcpy(pTo, pFrom, MEMCELLSIZE);
- pTo->xDel = 0;
if( (pFrom->flags&MEM_Static)==0 ){
pTo->flags &= ~(MEM_Dyn|MEM_Static|MEM_Ephem);
assert( srcType==MEM_Ephem || srcType==MEM_Static );
@@ -57783,10 +69565,9 @@ SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem *pTo, const Mem *pFrom){
int rc = SQLITE_OK;
assert( (pFrom->flags & MEM_RowSet)==0 );
- MemReleaseExt(pTo);
+ if( VdbeMemDynamic(pTo) ) vdbeMemClearExternAndSetNull(pTo);
memcpy(pTo, pFrom, MEMCELLSIZE);
pTo->flags &= ~MEM_Dyn;
-
if( pTo->flags&(MEM_Str|MEM_Blob) ){
if( 0==(pFrom->flags&MEM_Static) ){
pTo->flags |= MEM_Ephem;
@@ -57811,8 +69592,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemMove(Mem *pTo, Mem *pFrom){
sqlite3VdbeMemRelease(pTo);
memcpy(pTo, pFrom, sizeof(Mem));
pFrom->flags = MEM_Null;
- pFrom->xDel = 0;
- pFrom->zMalloc = 0;
+ pFrom->szMalloc = 0;
}
/*
@@ -57859,7 +69639,8 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
if( nByte<0 ){
assert( enc!=0 );
if( enc==SQLITE_UTF8 ){
- for(nByte=0; nByte<=iLimit && z[nByte]; nByte++){}
+ nByte = sqlite3Strlen30(z);
+ if( nByte>iLimit ) nByte = iLimit+1;
}else{
for(nByte=0; nByte<=iLimit && (z[nByte] | z[nByte+1]); nByte+=2){}
}
@@ -57878,14 +69659,17 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
if( nByte>iLimit ){
return SQLITE_TOOBIG;
}
- if( sqlite3VdbeMemGrow(pMem, nAlloc, 0) ){
- return SQLITE_NOMEM;
+ testcase( nAlloc==0 );
+ testcase( nAlloc==31 );
+ testcase( nAlloc==32 );
+ if( sqlite3VdbeMemClearAndResize(pMem, MAX(nAlloc,32)) ){
+ return SQLITE_NOMEM_BKPT;
}
memcpy(pMem->z, z, nAlloc);
}else if( xDel==SQLITE_DYNAMIC ){
sqlite3VdbeMemRelease(pMem);
pMem->zMalloc = pMem->z = (char *)z;
- pMem->xDel = 0;
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->zMalloc);
}else{
sqlite3VdbeMemRelease(pMem);
pMem->z = (char *)z;
@@ -57896,11 +69680,10 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
pMem->n = nByte;
pMem->flags = flags;
pMem->enc = (enc==0 ? SQLITE_UTF8 : enc);
- pMem->type = (enc==0 ? SQLITE_BLOB : SQLITE_TEXT);
#ifndef SQLITE_OMIT_UTF16
if( pMem->enc!=SQLITE_UTF8 && sqlite3VdbeMemHandleBom(pMem) ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
#endif
@@ -57912,218 +69695,103 @@ SQLITE_PRIVATE int sqlite3VdbeMemSetStr(
}
/*
-** Compare the values contained by the two memory cells, returning
-** negative, zero or positive if pMem1 is less than, equal to, or greater
-** than pMem2. Sorting order is NULL's first, followed by numbers (integers
-** and reals) sorted numerically, followed by text ordered by the collating
-** sequence pColl and finally blob's ordered by memcmp().
-**
-** Two NULL values are considered equal by this function.
-*/
-SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
- int rc;
- int f1, f2;
- int combined_flags;
-
- f1 = pMem1->flags;
- f2 = pMem2->flags;
- combined_flags = f1|f2;
- assert( (combined_flags & MEM_RowSet)==0 );
-
- /* If one value is NULL, it is less than the other. If both values
- ** are NULL, return 0.
- */
- if( combined_flags&MEM_Null ){
- return (f2&MEM_Null) - (f1&MEM_Null);
- }
-
- /* If one value is a number and the other is not, the number is less.
- ** If both are numbers, compare as reals if one is a real, or as integers
- ** if both values are integers.
- */
- if( combined_flags&(MEM_Int|MEM_Real) ){
- if( !(f1&(MEM_Int|MEM_Real)) ){
- return 1;
- }
- if( !(f2&(MEM_Int|MEM_Real)) ){
- return -1;
- }
- if( (f1 & f2 & MEM_Int)==0 ){
- double r1, r2;
- if( (f1&MEM_Real)==0 ){
- r1 = (double)pMem1->u.i;
- }else{
- r1 = pMem1->r;
- }
- if( (f2&MEM_Real)==0 ){
- r2 = (double)pMem2->u.i;
- }else{
- r2 = pMem2->r;
- }
- if( r1<r2 ) return -1;
- if( r1>r2 ) return 1;
- return 0;
- }else{
- assert( f1&MEM_Int );
- assert( f2&MEM_Int );
- if( pMem1->u.i < pMem2->u.i ) return -1;
- if( pMem1->u.i > pMem2->u.i ) return 1;
- return 0;
- }
- }
-
- /* If one value is a string and the other is a blob, the string is less.
- ** If both are strings, compare using the collating functions.
- */
- if( combined_flags&MEM_Str ){
- if( (f1 & MEM_Str)==0 ){
- return 1;
- }
- if( (f2 & MEM_Str)==0 ){
- return -1;
- }
-
- assert( pMem1->enc==pMem2->enc );
- assert( pMem1->enc==SQLITE_UTF8 ||
- pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
-
- /* The collation sequence must be defined at this point, even if
- ** the user deletes the collation sequence after the vdbe program is
- ** compiled (this was not always the case).
- */
- assert( !pColl || pColl->xCmp );
-
- if( pColl ){
- if( pMem1->enc==pColl->enc ){
- /* The strings are already in the correct encoding. Call the
- ** comparison function directly */
- return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
- }else{
- const void *v1, *v2;
- int n1, n2;
- Mem c1;
- Mem c2;
- memset(&c1, 0, sizeof(c1));
- memset(&c2, 0, sizeof(c2));
- sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
- sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
- v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
- n1 = v1==0 ? 0 : c1.n;
- v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
- n2 = v2==0 ? 0 : c2.n;
- rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
- sqlite3VdbeMemRelease(&c1);
- sqlite3VdbeMemRelease(&c2);
- return rc;
- }
- }
- /* If a NULL pointer was passed as the collate function, fall through
- ** to the blob case and use memcmp(). */
- }
-
- /* Both values must be blobs. Compare using memcmp(). */
- rc = memcmp(pMem1->z, pMem2->z, (pMem1->n>pMem2->n)?pMem2->n:pMem1->n);
- if( rc==0 ){
- rc = pMem1->n - pMem2->n;
- }
- return rc;
-}
-
-/*
** Move data out of a btree key or data field and into a Mem structure.
** The data or key is taken from the entry that pCur is currently pointing
** to. offset and amt determine what portion of the data or key to retrieve.
** key is true to get the key or false to get data. The result is written
** into the pMem element.
**
-** The pMem structure is assumed to be uninitialized. Any prior content
-** is overwritten without being freed.
+** The pMem object must have been initialized. This routine will use
+** pMem->zMalloc to hold the content from the btree, if possible. New
+** pMem->zMalloc space will be allocated if necessary. The calling routine
+** is responsible for making sure that the pMem object is eventually
+** destroyed.
**
** If this routine fails for any reason (malloc returns NULL or unable
** to read from the disk) then the pMem is left in an inconsistent state.
*/
+static SQLITE_NOINLINE int vdbeMemFromBtreeResize(
+ BtCursor *pCur, /* Cursor pointing at record to retrieve. */
+ u32 offset, /* Offset from the start of data to return bytes from. */
+ u32 amt, /* Number of bytes to return. */
+ int key, /* If true, retrieve from the btree key, not data. */
+ Mem *pMem /* OUT: Return data in this Mem structure. */
+){
+ int rc;
+ pMem->flags = MEM_Null;
+ if( SQLITE_OK==(rc = sqlite3VdbeMemClearAndResize(pMem, amt+2)) ){
+ if( key ){
+ rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z);
+ }else{
+ rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
+ }
+ if( rc==SQLITE_OK ){
+ pMem->z[amt] = 0;
+ pMem->z[amt+1] = 0;
+ pMem->flags = MEM_Blob|MEM_Term;
+ pMem->n = (int)amt;
+ }else{
+ sqlite3VdbeMemRelease(pMem);
+ }
+ }
+ return rc;
+}
SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(
BtCursor *pCur, /* Cursor pointing at record to retrieve. */
- int offset, /* Offset from the start of data to return bytes from. */
- int amt, /* Number of bytes to return. */
+ u32 offset, /* Offset from the start of data to return bytes from. */
+ u32 amt, /* Number of bytes to return. */
int key, /* If true, retrieve from the btree key, not data. */
Mem *pMem /* OUT: Return data in this Mem structure. */
){
char *zData; /* Data from the btree layer */
- int available = 0; /* Number of bytes available on the local btree page */
+ u32 available = 0; /* Number of bytes available on the local btree page */
int rc = SQLITE_OK; /* Return code */
assert( sqlite3BtreeCursorIsValid(pCur) );
+ assert( !VdbeMemDynamic(pMem) );
/* Note: the calls to BtreeKeyFetch() and DataFetch() below assert()
** that both the BtShared and database handle mutexes are held. */
assert( (pMem->flags & MEM_RowSet)==0 );
- if( key ){
- zData = (char *)sqlite3BtreeKeyFetch(pCur, &available);
- }else{
- zData = (char *)sqlite3BtreeDataFetch(pCur, &available);
- }
+ zData = (char *)sqlite3BtreePayloadFetch(pCur, &available);
assert( zData!=0 );
- if( offset+amt<=available && (pMem->flags&MEM_Dyn)==0 ){
- sqlite3VdbeMemRelease(pMem);
+ if( offset+amt<=available ){
pMem->z = &zData[offset];
pMem->flags = MEM_Blob|MEM_Ephem;
- }else if( SQLITE_OK==(rc = sqlite3VdbeMemGrow(pMem, amt+2, 0)) ){
- pMem->flags = MEM_Blob|MEM_Dyn|MEM_Term;
- pMem->enc = 0;
- pMem->type = SQLITE_BLOB;
- if( key ){
- rc = sqlite3BtreeKey(pCur, offset, amt, pMem->z);
- }else{
- rc = sqlite3BtreeData(pCur, offset, amt, pMem->z);
- }
- pMem->z[amt] = 0;
- pMem->z[amt+1] = 0;
- if( rc!=SQLITE_OK ){
- sqlite3VdbeMemRelease(pMem);
- }
+ pMem->n = (int)amt;
+ }else{
+ rc = vdbeMemFromBtreeResize(pCur, offset, amt, key, pMem);
}
- pMem->n = amt;
return rc;
}
-/* This function is only available internally, it is not part of the
-** external API. It works in a similar way to sqlite3_value_text(),
-** except the data returned is in the encoding specified by the second
-** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or
-** SQLITE_UTF8.
-**
-** (2006-02-16:) The enc value can be or-ed with SQLITE_UTF16_ALIGNED.
-** If that is the case, then the result must be aligned on an even byte
-** boundary.
+/*
+** The pVal argument is known to be a value other than NULL.
+** Convert it into a string with encoding enc and return a pointer
+** to a zero-terminated version of that string.
*/
-SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
- if( !pVal ) return 0;
-
+static SQLITE_NOINLINE const void *valueToText(sqlite3_value* pVal, u8 enc){
+ assert( pVal!=0 );
assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
assert( (pVal->flags & MEM_RowSet)==0 );
-
- if( pVal->flags&MEM_Null ){
- return 0;
- }
- assert( (MEM_Blob>>3) == MEM_Str );
- pVal->flags |= (pVal->flags & MEM_Blob)>>3;
- expandBlob(pVal);
- if( pVal->flags&MEM_Str ){
- sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);
+ assert( (pVal->flags & (MEM_Null))==0 );
+ if( pVal->flags & (MEM_Blob|MEM_Str) ){
+ pVal->flags |= MEM_Str;
+ if( pVal->enc != (enc & ~SQLITE_UTF16_ALIGNED) ){
+ sqlite3VdbeChangeEncoding(pVal, enc & ~SQLITE_UTF16_ALIGNED);
+ }
if( (enc & SQLITE_UTF16_ALIGNED)!=0 && 1==(1&SQLITE_PTR_TO_INT(pVal->z)) ){
assert( (pVal->flags & (MEM_Ephem|MEM_Static))!=0 );
if( sqlite3VdbeMemMakeWriteable(pVal)!=SQLITE_OK ){
return 0;
}
}
- sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-59893-45467 */
+ sqlite3VdbeMemNulTerminate(pVal); /* IMP: R-31275-44060 */
}else{
- assert( (pVal->flags&MEM_Blob)==0 );
- sqlite3VdbeMemStringify(pVal, enc);
+ sqlite3VdbeMemStringify(pVal, enc, 0);
assert( 0==(1&SQLITE_PTR_TO_INT(pVal->z)) );
}
assert(pVal->enc==(enc & ~SQLITE_UTF16_ALIGNED) || pVal->db==0
@@ -58135,6 +69803,30 @@ SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
}
}
+/* This function is only available internally, it is not part of the
+** external API. It works in a similar way to sqlite3_value_text(),
+** except the data returned is in the encoding specified by the second
+** parameter, which must be one of SQLITE_UTF16BE, SQLITE_UTF16LE or
+** SQLITE_UTF8.
+**
+** (2006-02-16:) The enc value can be or-ed with SQLITE_UTF16_ALIGNED.
+** If that is the case, then the result must be aligned on an even byte
+** boundary.
+*/
+SQLITE_PRIVATE const void *sqlite3ValueText(sqlite3_value* pVal, u8 enc){
+ if( !pVal ) return 0;
+ assert( pVal->db==0 || sqlite3_mutex_held(pVal->db->mutex) );
+ assert( (enc&3)==(enc&~SQLITE_UTF16_ALIGNED) );
+ assert( (pVal->flags & MEM_RowSet)==0 );
+ if( (pVal->flags&(MEM_Str|MEM_Term))==(MEM_Str|MEM_Term) && pVal->enc==enc ){
+ return pVal->z;
+ }
+ if( pVal->flags&MEM_Null ){
+ return 0;
+ }
+ return valueToText(pVal, enc);
+}
+
/*
** Create a new sqlite3_value object.
*/
@@ -58142,50 +69834,224 @@ SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *db){
Mem *p = sqlite3DbMallocZero(db, sizeof(*p));
if( p ){
p->flags = MEM_Null;
- p->type = SQLITE_NULL;
p->db = db;
}
return p;
}
/*
-** Create a new sqlite3_value object, containing the value of pExpr.
+** Context object passed by sqlite3Stat4ProbeSetValue() through to
+** valueNew(). See comments above valueNew() for details.
+*/
+struct ValueNewStat4Ctx {
+ Parse *pParse;
+ Index *pIdx;
+ UnpackedRecord **ppRec;
+ int iVal;
+};
+
+/*
+** Allocate and return a pointer to a new sqlite3_value object. If
+** the second argument to this function is NULL, the object is allocated
+** by calling sqlite3ValueNew().
**
-** This only works for very simple expressions that consist of one constant
-** token (i.e. "5", "5.1", "'a string'"). If the expression can
-** be converted directly into a value, then the value is allocated and
-** a pointer written to *ppVal. The caller is responsible for deallocating
-** the value by passing it to sqlite3ValueFree() later on. If the expression
-** cannot be converted to a value, then *ppVal is set to NULL.
+** Otherwise, if the second argument is non-zero, then this function is
+** being called indirectly by sqlite3Stat4ProbeSetValue(). If it has not
+** already been allocated, allocate the UnpackedRecord structure that
+** that function will return to its caller here. Then return a pointer to
+** an sqlite3_value within the UnpackedRecord.a[] array.
*/
-SQLITE_PRIVATE int sqlite3ValueFromExpr(
- sqlite3 *db, /* The database connection */
- Expr *pExpr, /* The expression to evaluate */
- u8 enc, /* Encoding to use */
- u8 affinity, /* Affinity to use */
- sqlite3_value **ppVal /* Write the new value here */
+static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( p ){
+ UnpackedRecord *pRec = p->ppRec[0];
+
+ if( pRec==0 ){
+ Index *pIdx = p->pIdx; /* Index being probed */
+ int nByte; /* Bytes of space to allocate */
+ int i; /* Counter variable */
+ int nCol = pIdx->nColumn; /* Number of index columns including rowid */
+
+ nByte = sizeof(Mem) * nCol + ROUND8(sizeof(UnpackedRecord));
+ pRec = (UnpackedRecord*)sqlite3DbMallocZero(db, nByte);
+ if( pRec ){
+ pRec->pKeyInfo = sqlite3KeyInfoOfIndex(p->pParse, pIdx);
+ if( pRec->pKeyInfo ){
+ assert( pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField==nCol );
+ assert( pRec->pKeyInfo->enc==ENC(db) );
+ pRec->aMem = (Mem *)((u8*)pRec + ROUND8(sizeof(UnpackedRecord)));
+ for(i=0; i<nCol; i++){
+ pRec->aMem[i].flags = MEM_Null;
+ pRec->aMem[i].db = db;
+ }
+ }else{
+ sqlite3DbFree(db, pRec);
+ pRec = 0;
+ }
+ }
+ if( pRec==0 ) return 0;
+ p->ppRec[0] = pRec;
+ }
+
+ pRec->nField = p->iVal+1;
+ return &pRec->aMem[p->iVal];
+ }
+#else
+ UNUSED_PARAMETER(p);
+#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+ return sqlite3ValueNew(db);
+}
+
+/*
+** The expression object indicated by the second argument is guaranteed
+** to be a scalar SQL function. If
+**
+** * all function arguments are SQL literals,
+** * one of the SQLITE_FUNC_CONSTANT or _SLOCHNG function flags is set, and
+** * the SQLITE_FUNC_NEEDCOLL function flag is not set,
+**
+** then this routine attempts to invoke the SQL function. Assuming no
+** error occurs, output parameter (*ppVal) is set to point to a value
+** object containing the result before returning SQLITE_OK.
+**
+** Affinity aff is applied to the result of the function before returning.
+** If the result is a text value, the sqlite3_value object uses encoding
+** enc.
+**
+** If the conditions above are not met, this function returns SQLITE_OK
+** and sets (*ppVal) to NULL. Or, if an error occurs, (*ppVal) is set to
+** NULL and an SQLite error code returned.
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static int valueFromFunction(
+ sqlite3 *db, /* The database connection */
+ Expr *p, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 aff, /* Affinity to use */
+ sqlite3_value **ppVal, /* Write the new value here */
+ struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
+){
+ sqlite3_context ctx; /* Context object for function invocation */
+ sqlite3_value **apVal = 0; /* Function arguments */
+ int nVal = 0; /* Size of apVal[] array */
+ FuncDef *pFunc = 0; /* Function definition */
+ sqlite3_value *pVal = 0; /* New value */
+ int rc = SQLITE_OK; /* Return code */
+ ExprList *pList = 0; /* Function arguments */
+ int i; /* Iterator variable */
+
+ assert( pCtx!=0 );
+ assert( (p->flags & EP_TokenOnly)==0 );
+ pList = p->x.pList;
+ if( pList ) nVal = pList->nExpr;
+ pFunc = sqlite3FindFunction(db, p->u.zToken, nVal, enc, 0);
+ assert( pFunc );
+ if( (pFunc->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG))==0
+ || (pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
+ ){
+ return SQLITE_OK;
+ }
+
+ if( pList ){
+ apVal = (sqlite3_value**)sqlite3DbMallocZero(db, sizeof(apVal[0]) * nVal);
+ if( apVal==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto value_from_function_out;
+ }
+ for(i=0; i<nVal; i++){
+ rc = sqlite3ValueFromExpr(db, pList->a[i].pExpr, enc, aff, &apVal[i]);
+ if( apVal[i]==0 || rc!=SQLITE_OK ) goto value_from_function_out;
+ }
+ }
+
+ pVal = valueNew(db, pCtx);
+ if( pVal==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ goto value_from_function_out;
+ }
+
+ assert( pCtx->pParse->rc==SQLITE_OK );
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.pOut = pVal;
+ ctx.pFunc = pFunc;
+ pFunc->xSFunc(&ctx, nVal, apVal);
+ if( ctx.isError ){
+ rc = ctx.isError;
+ sqlite3ErrorMsg(pCtx->pParse, "%s", sqlite3_value_text(pVal));
+ }else{
+ sqlite3ValueApplyAffinity(pVal, aff, SQLITE_UTF8);
+ assert( rc==SQLITE_OK );
+ rc = sqlite3VdbeChangeEncoding(pVal, enc);
+ if( rc==SQLITE_OK && sqlite3VdbeMemTooBig(pVal) ){
+ rc = SQLITE_TOOBIG;
+ pCtx->pParse->nErr++;
+ }
+ }
+ pCtx->pParse->rc = rc;
+
+ value_from_function_out:
+ if( rc!=SQLITE_OK ){
+ pVal = 0;
+ }
+ if( apVal ){
+ for(i=0; i<nVal; i++){
+ sqlite3ValueFree(apVal[i]);
+ }
+ sqlite3DbFree(db, apVal);
+ }
+
+ *ppVal = pVal;
+ return rc;
+}
+#else
+# define valueFromFunction(a,b,c,d,e,f) SQLITE_OK
+#endif /* defined(SQLITE_ENABLE_STAT3_OR_STAT4) */
+
+/*
+** Extract a value from the supplied expression in the manner described
+** above sqlite3ValueFromExpr(). Allocate the sqlite3_value object
+** using valueNew().
+**
+** If pCtx is NULL and an error occurs after the sqlite3_value object
+** has been allocated, it is freed before returning. Or, if pCtx is not
+** NULL, it is assumed that the caller will free any allocated object
+** in all cases.
+*/
+static int valueFromExpr(
+ sqlite3 *db, /* The database connection */
+ Expr *pExpr, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal, /* Write the new value here */
+ struct ValueNewStat4Ctx *pCtx /* Second argument for valueNew() */
){
int op;
char *zVal = 0;
sqlite3_value *pVal = 0;
int negInt = 1;
const char *zNeg = "";
+ int rc = SQLITE_OK;
- if( !pExpr ){
- *ppVal = 0;
- return SQLITE_OK;
- }
- op = pExpr->op;
-
- /* op can only be TK_REGISTER if we have compiled with SQLITE_ENABLE_STAT2.
- ** The ifdef here is to enable us to achieve 100% branch test coverage even
- ** when SQLITE_ENABLE_STAT2 is omitted.
- */
-#ifdef SQLITE_ENABLE_STAT2
- if( op==TK_REGISTER ) op = pExpr->op2;
-#else
+ assert( pExpr!=0 );
+ while( (op = pExpr->op)==TK_UPLUS || op==TK_SPAN ) pExpr = pExpr->pLeft;
if( NEVER(op==TK_REGISTER) ) op = pExpr->op2;
-#endif
+
+ /* Compressed expressions only appear when parsing the DEFAULT clause
+ ** on a table column definition, and hence only when pCtx==0. This
+ ** check ensures that an EP_TokenOnly expression is never passed down
+ ** into valueFromFunction(). */
+ assert( (pExpr->flags & EP_TokenOnly)==0 || pCtx==0 );
+
+ if( op==TK_CAST ){
+ u8 aff = sqlite3AffinityType(pExpr->u.zToken,0);
+ rc = valueFromExpr(db, pExpr->pLeft, enc, aff, ppVal, pCtx);
+ testcase( rc!=SQLITE_OK );
+ if( *ppVal ){
+ sqlite3VdbeMemCast(*ppVal, aff, SQLITE_UTF8);
+ sqlite3ValueApplyAffinity(*ppVal, affinity, SQLITE_UTF8);
+ }
+ return rc;
+ }
/* Handle negative integers in a single step. This is needed in the
** case when the value is -9223372036854775808.
@@ -58199,7 +70065,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
if( ExprHasProperty(pExpr, EP_IntValue) ){
sqlite3VdbeMemSetInt64(pVal, (i64)pExpr->u.iValue*negInt);
@@ -58207,33 +70073,34 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
zVal = sqlite3MPrintf(db, "%s%s", zNeg, pExpr->u.zToken);
if( zVal==0 ) goto no_mem;
sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC);
- if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT;
}
- if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){
+ if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_BLOB ){
sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8);
}else{
sqlite3ValueApplyAffinity(pVal, affinity, SQLITE_UTF8);
}
if( pVal->flags & (MEM_Int|MEM_Real) ) pVal->flags &= ~MEM_Str;
if( enc!=SQLITE_UTF8 ){
- sqlite3VdbeChangeEncoding(pVal, enc);
+ rc = sqlite3VdbeChangeEncoding(pVal, enc);
}
}else if( op==TK_UMINUS ) {
/* This branch happens for multiple negative signs. Ex: -(-5) */
- if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) ){
+ if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal)
+ && pVal!=0
+ ){
sqlite3VdbeMemNumerify(pVal);
- if( pVal->u.i==SMALLEST_INT64 ){
- pVal->flags &= MEM_Int;
- pVal->flags |= MEM_Real;
- pVal->r = (double)LARGEST_INT64;
+ if( pVal->flags & MEM_Real ){
+ pVal->u.r = -pVal->u.r;
+ }else if( pVal->u.i==SMALLEST_INT64 ){
+ pVal->u.r = -(double)SMALLEST_INT64;
+ MemSetTypeFlag(pVal, MEM_Real);
}else{
pVal->u.i = -pVal->u.i;
}
- pVal->r = -pVal->r;
sqlite3ValueApplyAffinity(pVal, affinity, enc);
}
}else if( op==TK_NULL ){
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( pVal==0 ) goto no_mem;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
@@ -58241,7 +70108,7 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
int nVal;
assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' );
assert( pExpr->u.zToken[1]=='\'' );
- pVal = sqlite3ValueNew(db);
+ pVal = valueNew(db, pCtx);
if( !pVal ) goto no_mem;
zVal = &pExpr->u.zToken[2];
nVal = sqlite3Strlen30(zVal)-1;
@@ -58251,21 +70118,316 @@ SQLITE_PRIVATE int sqlite3ValueFromExpr(
}
#endif
- if( pVal ){
- sqlite3VdbeMemStoreType(pVal);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ else if( op==TK_FUNCTION && pCtx!=0 ){
+ rc = valueFromFunction(db, pExpr, enc, affinity, &pVal, pCtx);
}
+#endif
+
*ppVal = pVal;
- return SQLITE_OK;
+ return rc;
no_mem:
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, zVal);
- sqlite3ValueFree(pVal);
- *ppVal = 0;
- return SQLITE_NOMEM;
+ assert( *ppVal==0 );
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( pCtx==0 ) sqlite3ValueFree(pVal);
+#else
+ assert( pCtx==0 ); sqlite3ValueFree(pVal);
+#endif
+ return SQLITE_NOMEM_BKPT;
+}
+
+/*
+** Create a new sqlite3_value object, containing the value of pExpr.
+**
+** This only works for very simple expressions that consist of one constant
+** token (i.e. "5", "5.1", "'a string'"). If the expression can
+** be converted directly into a value, then the value is allocated and
+** a pointer written to *ppVal. The caller is responsible for deallocating
+** the value by passing it to sqlite3ValueFree() later on. If the expression
+** cannot be converted to a value, then *ppVal is set to NULL.
+*/
+SQLITE_PRIVATE int sqlite3ValueFromExpr(
+ sqlite3 *db, /* The database connection */
+ Expr *pExpr, /* The expression to evaluate */
+ u8 enc, /* Encoding to use */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal /* Write the new value here */
+){
+ return pExpr ? valueFromExpr(db, pExpr, enc, affinity, ppVal, 0) : 0;
+}
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** The implementation of the sqlite_record() function. This function accepts
+** a single argument of any type. The return value is a formatted database
+** record (a blob) containing the argument value.
+**
+** This is used to convert the value stored in the 'sample' column of the
+** sqlite_stat3 table to the record format SQLite uses internally.
+*/
+static void recordFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const int file_format = 1;
+ u32 iSerial; /* Serial type */
+ int nSerial; /* Bytes of space for iSerial as varint */
+ u32 nVal; /* Bytes of space required for argv[0] */
+ int nRet;
+ sqlite3 *db;
+ u8 *aRet;
+
+ UNUSED_PARAMETER( argc );
+ iSerial = sqlite3VdbeSerialType(argv[0], file_format, &nVal);
+ nSerial = sqlite3VarintLen(iSerial);
+ db = sqlite3_context_db_handle(context);
+
+ nRet = 1 + nSerial + nVal;
+ aRet = sqlite3DbMallocRawNN(db, nRet);
+ if( aRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ aRet[0] = nSerial+1;
+ putVarint32(&aRet[1], iSerial);
+ sqlite3VdbeSerialPut(&aRet[1+nSerial], argv[0], iSerial);
+ sqlite3_result_blob(context, aRet, nRet, SQLITE_TRANSIENT);
+ sqlite3DbFree(db, aRet);
+ }
+}
+
+/*
+** Register built-in functions used to help read ANALYZE data.
+*/
+SQLITE_PRIVATE void sqlite3AnalyzeFunctions(void){
+ static FuncDef aAnalyzeTableFuncs[] = {
+ FUNCTION(sqlite_record, 1, 0, 0, recordFunc),
+ };
+ sqlite3InsertBuiltinFuncs(aAnalyzeTableFuncs, ArraySize(aAnalyzeTableFuncs));
+}
+
+/*
+** Attempt to extract a value from pExpr and use it to construct *ppVal.
+**
+** If pAlloc is not NULL, then an UnpackedRecord object is created for
+** pAlloc if one does not exist and the new value is added to the
+** UnpackedRecord object.
+**
+** A value is extracted in the following cases:
+**
+** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
+**
+** * The expression is a bound variable, and this is a reprepare, or
+**
+** * The expression is a literal value.
+**
+** On success, *ppVal is made to point to the extracted value. The caller
+** is responsible for ensuring that the value is eventually freed.
+*/
+static int stat4ValueFromExpr(
+ Parse *pParse, /* Parse context */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ struct ValueNewStat4Ctx *pAlloc,/* How to allocate space. Or NULL */
+ sqlite3_value **ppVal /* OUT: New value object (or NULL) */
+){
+ int rc = SQLITE_OK;
+ sqlite3_value *pVal = 0;
+ sqlite3 *db = pParse->db;
+
+ /* Skip over any TK_COLLATE nodes */
+ pExpr = sqlite3ExprSkipCollate(pExpr);
+
+ if( !pExpr ){
+ pVal = valueNew(db, pAlloc);
+ if( pVal ){
+ sqlite3VdbeMemSetNull((Mem*)pVal);
+ }
+ }else if( pExpr->op==TK_VARIABLE
+ || NEVER(pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
+ ){
+ Vdbe *v;
+ int iBindVar = pExpr->iColumn;
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iBindVar);
+ if( (v = pParse->pReprepare)!=0 ){
+ pVal = valueNew(db, pAlloc);
+ if( pVal ){
+ rc = sqlite3VdbeMemCopy((Mem*)pVal, &v->aVar[iBindVar-1]);
+ if( rc==SQLITE_OK ){
+ sqlite3ValueApplyAffinity(pVal, affinity, ENC(db));
+ }
+ pVal->db = pParse->db;
+ }
+ }
+ }else{
+ rc = valueFromExpr(db, pExpr, ENC(db), affinity, &pVal, pAlloc);
+ }
+
+ assert( pVal==0 || pVal->db==db );
+ *ppVal = pVal;
+ return rc;
+}
+
+/*
+** This function is used to allocate and populate UnpackedRecord
+** structures intended to be compared against sample index keys stored
+** in the sqlite_stat4 table.
+**
+** A single call to this function populates zero or more fields of the
+** record starting with field iVal (fields are numbered from left to
+** right starting with 0). A single field is populated if:
+**
+** * (pExpr==0). In this case the value is assumed to be an SQL NULL,
+**
+** * The expression is a bound variable, and this is a reprepare, or
+**
+** * The sqlite3ValueFromExpr() function is able to extract a value
+** from the expression (i.e. the expression is a literal value).
+**
+** Or, if pExpr is a TK_VECTOR, one field is populated for each of the
+** vector components that match either of the two latter criteria listed
+** above.
+**
+** Before any value is appended to the record, the affinity of the
+** corresponding column within index pIdx is applied to it. Before
+** this function returns, output parameter *pnExtract is set to the
+** number of values appended to the record.
+**
+** When this function is called, *ppRec must either point to an object
+** allocated by an earlier call to this function, or must be NULL. If it
+** is NULL and a value can be successfully extracted, a new UnpackedRecord
+** is allocated (and *ppRec set to point to it) before returning.
+**
+** Unless an error is encountered, SQLITE_OK is returned. It is not an
+** error if a value cannot be extracted from pExpr. If an error does
+** occur, an SQLite error code is returned.
+*/
+SQLITE_PRIVATE int sqlite3Stat4ProbeSetValue(
+ Parse *pParse, /* Parse context */
+ Index *pIdx, /* Index being probed */
+ UnpackedRecord **ppRec, /* IN/OUT: Probe record */
+ Expr *pExpr, /* The expression to extract a value from */
+ int nElem, /* Maximum number of values to append */
+ int iVal, /* Array element to populate */
+ int *pnExtract /* OUT: Values appended to the record */
+){
+ int rc = SQLITE_OK;
+ int nExtract = 0;
+
+ if( pExpr==0 || pExpr->op!=TK_SELECT ){
+ int i;
+ struct ValueNewStat4Ctx alloc;
+
+ alloc.pParse = pParse;
+ alloc.pIdx = pIdx;
+ alloc.ppRec = ppRec;
+
+ for(i=0; i<nElem; i++){
+ sqlite3_value *pVal = 0;
+ Expr *pElem = (pExpr ? sqlite3VectorFieldSubexpr(pExpr, i) : 0);
+ u8 aff = sqlite3IndexColumnAffinity(pParse->db, pIdx, iVal+i);
+ alloc.iVal = iVal+i;
+ rc = stat4ValueFromExpr(pParse, pElem, aff, &alloc, &pVal);
+ if( !pVal ) break;
+ nExtract++;
+ }
+ }
+
+ *pnExtract = nExtract;
+ return rc;
}
/*
+** Attempt to extract a value from expression pExpr using the methods
+** as described for sqlite3Stat4ProbeSetValue() above.
+**
+** If successful, set *ppVal to point to a new value object and return
+** SQLITE_OK. If no value can be extracted, but no other error occurs
+** (e.g. OOM), return SQLITE_OK and set *ppVal to NULL. Or, if an error
+** does occur, return an SQLite error code. The final value of *ppVal
+** is undefined in this case.
+*/
+SQLITE_PRIVATE int sqlite3Stat4ValueFromExpr(
+ Parse *pParse, /* Parse context */
+ Expr *pExpr, /* The expression to extract a value from */
+ u8 affinity, /* Affinity to use */
+ sqlite3_value **ppVal /* OUT: New value object (or NULL) */
+){
+ return stat4ValueFromExpr(pParse, pExpr, affinity, 0, ppVal);
+}
+
+/*
+** Extract the iCol-th column from the nRec-byte record in pRec. Write
+** the column value into *ppVal. If *ppVal is initially NULL then a new
+** sqlite3_value object is allocated.
+**
+** If *ppVal is initially NULL then the caller is responsible for
+** ensuring that the value written into *ppVal is eventually freed.
+*/
+SQLITE_PRIVATE int sqlite3Stat4Column(
+ sqlite3 *db, /* Database handle */
+ const void *pRec, /* Pointer to buffer containing record */
+ int nRec, /* Size of buffer pRec in bytes */
+ int iCol, /* Column to extract */
+ sqlite3_value **ppVal /* OUT: Extracted value */
+){
+ u32 t; /* a column type code */
+ int nHdr; /* Size of the header in the record */
+ int iHdr; /* Next unread header byte */
+ int iField; /* Next unread data byte */
+ int szField; /* Size of the current data field */
+ int i; /* Column index */
+ u8 *a = (u8*)pRec; /* Typecast byte array */
+ Mem *pMem = *ppVal; /* Write result into this Mem object */
+
+ assert( iCol>0 );
+ iHdr = getVarint32(a, nHdr);
+ if( nHdr>nRec || iHdr>=nHdr ) return SQLITE_CORRUPT_BKPT;
+ iField = nHdr;
+ for(i=0; i<=iCol; i++){
+ iHdr += getVarint32(&a[iHdr], t);
+ testcase( iHdr==nHdr );
+ testcase( iHdr==nHdr+1 );
+ if( iHdr>nHdr ) return SQLITE_CORRUPT_BKPT;
+ szField = sqlite3VdbeSerialTypeLen(t);
+ iField += szField;
+ }
+ testcase( iField==nRec );
+ testcase( iField==nRec+1 );
+ if( iField>nRec ) return SQLITE_CORRUPT_BKPT;
+ if( pMem==0 ){
+ pMem = *ppVal = sqlite3ValueNew(db);
+ if( pMem==0 ) return SQLITE_NOMEM_BKPT;
+ }
+ sqlite3VdbeSerialGet(&a[iField-szField], t, pMem);
+ pMem->enc = ENC(db);
+ return SQLITE_OK;
+}
+
+/*
+** Unless it is NULL, the argument must be an UnpackedRecord object returned
+** by an earlier call to sqlite3Stat4ProbeSetValue(). This call deletes
+** the object.
+*/
+SQLITE_PRIVATE void sqlite3Stat4ProbeFree(UnpackedRecord *pRec){
+ if( pRec ){
+ int i;
+ int nCol = pRec->pKeyInfo->nField+pRec->pKeyInfo->nXField;
+ Mem *aMem = pRec->aMem;
+ sqlite3 *db = aMem[0].db;
+ for(i=0; i<nCol; i++){
+ sqlite3VdbeMemRelease(&aMem[i]);
+ }
+ sqlite3KeyInfoUnref(pRec->pKeyInfo);
+ sqlite3DbFree(db, pRec);
+ }
+}
+#endif /* ifdef SQLITE_ENABLE_STAT4 */
+
+/*
** Change the string value of an sqlite3_value object
*/
SQLITE_PRIVATE void sqlite3ValueSetStr(
@@ -58288,19 +70450,28 @@ SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value *v){
}
/*
-** Return the number of bytes in the sqlite3_value object assuming
-** that it uses the encoding "enc"
+** The sqlite3ValueBytes() routine returns the number of bytes in the
+** sqlite3_value object assuming that it uses the encoding "enc".
+** The valueBytes() routine is a helper function.
*/
+static SQLITE_NOINLINE int valueBytes(sqlite3_value *pVal, u8 enc){
+ return valueToText(pVal, enc)!=0 ? pVal->n : 0;
+}
SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
Mem *p = (Mem*)pVal;
- if( (p->flags & MEM_Blob)!=0 || sqlite3ValueText(pVal, enc) ){
+ assert( (p->flags & MEM_Null)==0 || (p->flags & (MEM_Str|MEM_Blob))==0 );
+ if( (p->flags & MEM_Str)!=0 && pVal->enc==enc ){
+ return p->n;
+ }
+ if( (p->flags & MEM_Blob)!=0 ){
if( p->flags & MEM_Zero ){
return p->n + p->u.nZero;
}else{
return p->n;
}
}
- return 0;
+ if( p->flags & MEM_Null ) return 0;
+ return valueBytes(pVal, enc);
}
/************** End of vdbemem.c *********************************************/
@@ -58317,30 +70488,20 @@ SQLITE_PRIVATE int sqlite3ValueBytes(sqlite3_value *pVal, u8 enc){
**
*************************************************************************
** This file contains code used for creating, destroying, and populating
-** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior
-** to version 2.8.7, all this code was combined into the vdbe.c source file.
-** But that file was getting too big so this subroutines were split out.
+** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)
*/
-
-
-
-/*
-** When debugging the code generator in a symbolic debugger, one can
-** set the sqlite3VdbeAddopTrace to 1 and all opcodes will be printed
-** as they are added to the instruction stream.
-*/
-#ifdef SQLITE_DEBUG
-SQLITE_PRIVATE int sqlite3VdbeAddopTrace = 0;
-#endif
-
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
/*
** Create a new virtual database engine.
*/
-SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){
+SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse *pParse){
+ sqlite3 *db = pParse->db;
Vdbe *p;
- p = sqlite3DbMallocZero(db, sizeof(Vdbe) );
+ p = sqlite3DbMallocRawNN(db, sizeof(Vdbe) );
if( p==0 ) return 0;
+ memset(&p->aOp, 0, sizeof(Vdbe)-offsetof(Vdbe,aOp));
p->db = db;
if( db->pVdbe ){
db->pVdbe->pPrev = p;
@@ -58349,16 +70510,32 @@ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(sqlite3 *db){
p->pPrev = 0;
db->pVdbe = p;
p->magic = VDBE_MAGIC_INIT;
+ p->pParse = pParse;
+ assert( pParse->aLabel==0 );
+ assert( pParse->nLabel==0 );
+ assert( pParse->nOpAlloc==0 );
+ assert( pParse->szOpAlloc==0 );
return p;
}
/*
+** Change the error string stored in Vdbe.zErrMsg
+*/
+SQLITE_PRIVATE void sqlite3VdbeError(Vdbe *p, const char *zFormat, ...){
+ va_list ap;
+ sqlite3DbFree(p->db, p->zErrMsg);
+ va_start(ap, zFormat);
+ p->zErrMsg = sqlite3VMPrintf(p->db, zFormat, ap);
+ va_end(ap);
+}
+
+/*
** Remember the SQL string for a prepared statement.
*/
SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){
assert( isPrepareV2==1 || isPrepareV2==0 );
if( p==0 ) return;
-#ifdef SQLITE_OMIT_TRACE
+#if defined(SQLITE_OMIT_TRACE) && !defined(SQLITE_ENABLE_SQLLOG)
if( !isPrepareV2 ) return;
#endif
assert( p->zSql==0 );
@@ -58367,19 +70544,12 @@ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepa
}
/*
-** Return the SQL associated with a prepared statement
-*/
-SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt){
- Vdbe *p = (Vdbe *)pStmt;
- return (p && p->isPrepareV2) ? p->zSql : 0;
-}
-
-/*
** Swap all content between two VDBE structures.
*/
SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
Vdbe tmp, *pTmp;
char *zTmp;
+ assert( pA->db==pB->db );
tmp = *pA;
*pA = *pB;
*pB = tmp;
@@ -58395,35 +70565,56 @@ SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){
pB->isPrepareV2 = pA->isPrepareV2;
}
-#ifdef SQLITE_DEBUG
-/*
-** Turn tracing on or off
-*/
-SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe *p, FILE *trace){
- p->trace = trace;
-}
-#endif
-
/*
-** Resize the Vdbe.aOp array so that it is at least one op larger than
-** it was.
+** Resize the Vdbe.aOp array so that it is at least nOp elements larger
+** than its current size. nOp is guaranteed to be less than or equal
+** to 1024/sizeof(Op).
**
** If an out-of-memory error occurs while resizing the array, return
-** SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
+** SQLITE_NOMEM. In this case Vdbe.aOp and Parse.nOpAlloc remain
** unchanged (this is so that any opcodes already allocated can be
** correctly deallocated along with the rest of the Vdbe).
*/
-static int growOpArray(Vdbe *p){
+static int growOpArray(Vdbe *v, int nOp){
VdbeOp *pNew;
+ Parse *p = v->pParse;
+
+ /* The SQLITE_TEST_REALLOC_STRESS compile-time option is designed to force
+ ** more frequent reallocs and hence provide more opportunities for
+ ** simulated OOM faults. SQLITE_TEST_REALLOC_STRESS is generally used
+ ** during testing only. With SQLITE_TEST_REALLOC_STRESS grow the op array
+ ** by the minimum* amount required until the size reaches 512. Normal
+ ** operation (without SQLITE_TEST_REALLOC_STRESS) is to double the current
+ ** size of the op array or add 1KB of space, whichever is smaller. */
+#ifdef SQLITE_TEST_REALLOC_STRESS
+ int nNew = (p->nOpAlloc>=512 ? p->nOpAlloc*2 : p->nOpAlloc+nOp);
+#else
int nNew = (p->nOpAlloc ? p->nOpAlloc*2 : (int)(1024/sizeof(Op)));
- pNew = sqlite3DbRealloc(p->db, p->aOp, nNew*sizeof(Op));
+ UNUSED_PARAMETER(nOp);
+#endif
+
+ assert( nOp<=(1024/sizeof(Op)) );
+ assert( nNew>=(p->nOpAlloc+nOp) );
+ pNew = sqlite3DbRealloc(p->db, v->aOp, nNew*sizeof(Op));
if( pNew ){
- p->nOpAlloc = sqlite3DbMallocSize(p->db, pNew)/sizeof(Op);
- p->aOp = pNew;
+ p->szOpAlloc = sqlite3DbMallocSize(p->db, pNew);
+ p->nOpAlloc = p->szOpAlloc/sizeof(Op);
+ v->aOp = pNew;
}
- return (pNew ? SQLITE_OK : SQLITE_NOMEM);
+ return (pNew ? SQLITE_OK : SQLITE_NOMEM_BKPT);
}
+#ifdef SQLITE_DEBUG
+/* This routine is just a convenient place to set a breakpoint that will
+** fire after each opcode is inserted and displayed using
+** "PRAGMA vdbe_addoptrace=on".
+*/
+static void test_addop_breakpoint(void){
+ static int n = 0;
+ n++;
+}
+#endif
+
/*
** Add a new instruction to the list of instructions current in the
** VDBE. Return the address of the new instruction.
@@ -58440,17 +70631,21 @@ static int growOpArray(Vdbe *p){
** the sqlite3VdbeChangeP4() function to change the value of the P4
** operand.
*/
+static SQLITE_NOINLINE int growOp3(Vdbe *p, int op, int p1, int p2, int p3){
+ assert( p->pParse->nOpAlloc<=p->nOp );
+ if( growOpArray(p, 1) ) return 1;
+ assert( p->pParse->nOpAlloc>p->nOp );
+ return sqlite3VdbeAddOp3(p, op, p1, p2, p3);
+}
SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
int i;
VdbeOp *pOp;
i = p->nOp;
assert( p->magic==VDBE_MAGIC_INIT );
- assert( op>0 && op<0xff );
- if( p->nOpAlloc<=i ){
- if( growOpArray(p) ){
- return 1;
- }
+ assert( op>=0 && op<0xff );
+ if( p->pParse->nOpAlloc<=i ){
+ return growOp3(p, op, p1, p2, p3);
}
p->nOp++;
pOp = &p->aOp[i];
@@ -58461,14 +70656,30 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
pOp->p3 = p3;
pOp->p4.p = 0;
pOp->p4type = P4_NOTUSED;
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
pOp->zComment = 0;
- if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+#endif
+#ifdef SQLITE_DEBUG
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ int jj, kk;
+ Parse *pParse = p->pParse;
+ for(jj=kk=0; jj<pParse->nColCache; jj++){
+ struct yColCache *x = pParse->aColCache + jj;
+ printf(" r[%d]={%d:%d}", x->iReg, x->iTable, x->iColumn);
+ kk++;
+ }
+ if( kk ) printf("\n");
+ sqlite3VdbePrintOp(0, i, &p->aOp[i]);
+ test_addop_breakpoint();
+ }
#endif
#ifdef VDBE_PROFILE
pOp->cycles = 0;
pOp->cnt = 0;
#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ pOp->iSrcLine = 0;
+#endif
return i;
}
SQLITE_PRIVATE int sqlite3VdbeAddOp0(Vdbe *p, int op){
@@ -58481,6 +70692,43 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe *p, int op, int p1, int p2){
return sqlite3VdbeAddOp3(p, op, p1, p2, 0);
}
+/* Generate code for an unconditional jump to instruction iDest
+*/
+SQLITE_PRIVATE int sqlite3VdbeGoto(Vdbe *p, int iDest){
+ return sqlite3VdbeAddOp3(p, OP_Goto, 0, iDest, 0);
+}
+
+/* Generate code to cause the string zStr to be loaded into
+** register iDest
+*/
+SQLITE_PRIVATE int sqlite3VdbeLoadString(Vdbe *p, int iDest, const char *zStr){
+ return sqlite3VdbeAddOp4(p, OP_String8, 0, iDest, 0, zStr, 0);
+}
+
+/*
+** Generate code that initializes multiple registers to string or integer
+** constants. The registers begin with iDest and increase consecutively.
+** One register is initialized for each characgter in zTypes[]. For each
+** "s" character in zTypes[], the register is a string if the argument is
+** not NULL, or OP_Null if the value is a null pointer. For each "i" character
+** in zTypes[], the register is initialized to an integer.
+*/
+SQLITE_PRIVATE void sqlite3VdbeMultiLoad(Vdbe *p, int iDest, const char *zTypes, ...){
+ va_list ap;
+ int i;
+ char c;
+ va_start(ap, zTypes);
+ for(i=0; (c = zTypes[i])!=0; i++){
+ if( c=='s' ){
+ const char *z = va_arg(ap, const char*);
+ sqlite3VdbeAddOp4(p, z==0 ? OP_Null : OP_String8, 0, iDest++, 0, z, 0);
+ }else{
+ assert( c=='i' );
+ sqlite3VdbeAddOp2(p, OP_Integer, va_arg(ap, int), iDest++);
+ }
+ }
+ va_end(ap);
+}
/*
** Add an opcode that includes the p4 value as a pointer.
@@ -58500,16 +70748,34 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4(
}
/*
+** Add an opcode that includes the p4 value with a P4_INT64 or
+** P4_REAL type.
+*/
+SQLITE_PRIVATE int sqlite3VdbeAddOp4Dup8(
+ Vdbe *p, /* Add the opcode to this VM */
+ int op, /* The new opcode */
+ int p1, /* The P1 operand */
+ int p2, /* The P2 operand */
+ int p3, /* The P3 operand */
+ const u8 *zP4, /* The P4 operand */
+ int p4type /* P4 operand type */
+){
+ char *p4copy = sqlite3DbMallocRawNN(sqlite3VdbeDb(p), 8);
+ if( p4copy ) memcpy(p4copy, zP4, 8);
+ return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
+}
+
+/*
** Add an OP_ParseSchema opcode. This routine is broken out from
-** sqlite3VdbeAddOp4() since it needs to also local all btrees.
+** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
+** as having been used.
**
** The zWhere string must have been obtained from sqlite3_malloc().
** This routine will take ownership of the allocated memory.
*/
SQLITE_PRIVATE void sqlite3VdbeAddParseSchemaOp(Vdbe *p, int iDb, char *zWhere){
int j;
- int addr = sqlite3VdbeAddOp3(p, OP_ParseSchema, iDb, 0, 0);
- sqlite3VdbeChangeP4(p, addr, zWhere, P4_DYNAMIC);
+ sqlite3VdbeAddOp4(p, OP_ParseSchema, iDb, 0, 0, zWhere, P4_DYNAMIC);
for(j=0; j<p->db->nDb; j++) sqlite3VdbeUsesBtree(p, j);
}
@@ -58529,6 +70795,21 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
return addr;
}
+/* Insert the end of a co-routine
+*/
+SQLITE_PRIVATE void sqlite3VdbeEndCoroutine(Vdbe *v, int regYield){
+ sqlite3VdbeAddOp1(v, OP_EndCoroutine, regYield);
+
+ /* Clear the temporary register cache, thereby ensuring that each
+ ** co-routine has its own independent set of registers, because co-routines
+ ** might expect their registers to be preserved across an OP_Yield, and
+ ** that could cause problems if two or more co-routines are using the same
+ ** temporary register.
+ */
+ v->pParse->nTempReg = 0;
+ v->pParse->nRangeReg = 0;
+}
+
/*
** Create a new symbolic label for an instruction that has yet to be
** coded. The symbolic label is really just a negative number. The
@@ -58543,20 +70824,18 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(
**
** Zero is returned if a malloc() fails.
*/
-SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){
- int i;
- i = p->nLabel++;
- assert( p->magic==VDBE_MAGIC_INIT );
- if( i>=p->nLabelAlloc ){
- int n = p->nLabelAlloc*2 + 5;
- p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
- n*sizeof(p->aLabel[0]));
- p->nLabelAlloc = sqlite3DbMallocSize(p->db, p->aLabel)/sizeof(p->aLabel[0]);
+SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *v){
+ Parse *p = v->pParse;
+ int i = p->nLabel++;
+ assert( v->magic==VDBE_MAGIC_INIT );
+ if( (i & (i-1))==0 ){
+ p->aLabel = sqlite3DbReallocOrFree(p->db, p->aLabel,
+ (i*2+1)*sizeof(p->aLabel[0]));
}
if( p->aLabel ){
p->aLabel[i] = -1;
}
- return -1-i;
+ return ADDR(i);
}
/*
@@ -58564,12 +70843,14 @@ SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe *p){
** be inserted. The parameter "x" must have been obtained from
** a prior call to sqlite3VdbeMakeLabel().
*/
-SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *p, int x){
- int j = -1-x;
- assert( p->magic==VDBE_MAGIC_INIT );
- assert( j>=0 && j<p->nLabel );
+SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe *v, int x){
+ Parse *p = v->pParse;
+ int j = ADDR(x);
+ assert( v->magic==VDBE_MAGIC_INIT );
+ assert( j<p->nLabel );
+ assert( j>=0 );
if( p->aLabel ){
- p->aLabel[j] = p->nOp;
+ p->aLabel[j] = v->nOp;
}
}
@@ -58580,6 +70861,13 @@ SQLITE_PRIVATE void sqlite3VdbeRunOnlyOnce(Vdbe *p){
p->runOnlyOnce = 1;
}
+/*
+** Mark the VDBE as one that can only be run multiple times.
+*/
+SQLITE_PRIVATE void sqlite3VdbeReusable(Vdbe *p){
+ p->runOnlyOnce = 0;
+}
+
#ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */
/*
@@ -58662,6 +70950,7 @@ static Op *opIterNext(VdbeOpIter *p){
** * OP_VUpdate
** * OP_VRename
** * OP_FkCounter with P2==0 (immediate foreign key constraint)
+** * OP_CreateTable and OP_InitCoroutine (for CREATE TABLE AS SELECT ...)
**
** Then check that the value of Parse.mayAbort is true if an
** ABORT may be thrown, or false otherwise. Return true if it does
@@ -58672,6 +70961,9 @@ static Op *opIterNext(VdbeOpIter *p){
*/
SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
int hasAbort = 0;
+ int hasFkCounter = 0;
+ int hasCreateTable = 0;
+ int hasInitCoroutine = 0;
Op *pOp;
VdbeOpIter sIter;
memset(&sIter, 0, sizeof(sIter));
@@ -58680,81 +70972,132 @@ SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){
while( (pOp = opIterNext(&sIter))!=0 ){
int opcode = pOp->opcode;
if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename
-#ifndef SQLITE_OMIT_FOREIGN_KEY
- || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1)
-#endif
|| ((opcode==OP_Halt || opcode==OP_HaltIfNull)
- && (pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort))
+ && ((pOp->p1&0xff)==SQLITE_CONSTRAINT && pOp->p2==OE_Abort))
){
hasAbort = 1;
break;
}
+ if( opcode==OP_CreateTable ) hasCreateTable = 1;
+ if( opcode==OP_InitCoroutine ) hasInitCoroutine = 1;
+#ifndef SQLITE_OMIT_FOREIGN_KEY
+ if( opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1 ){
+ hasFkCounter = 1;
+ }
+#endif
}
sqlite3DbFree(v->db, sIter.apSub);
- /* Return true if hasAbort==mayAbort. Or if a malloc failure occured.
+ /* Return true if hasAbort==mayAbort. Or if a malloc failure occurred.
** If malloc failed, then the while() loop above may not have iterated
** through all opcodes and hasAbort may be set incorrectly. Return
** true for this case to prevent the assert() in the callers frame
** from failing. */
- return ( v->db->mallocFailed || hasAbort==mayAbort );
+ return ( v->db->mallocFailed || hasAbort==mayAbort || hasFkCounter
+ || (hasCreateTable && hasInitCoroutine) );
}
#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */
/*
-** Loop through the program looking for P2 values that are negative
-** on jump instructions. Each such value is a label. Resolve the
-** label by setting the P2 value to its correct non-zero value.
+** This routine is called after all opcodes have been inserted. It loops
+** through all the opcodes and fixes up some details.
+**
+** (1) For each jump instruction with a negative P2 value (a label)
+** resolve the P2 value to an actual address.
+**
+** (2) Compute the maximum number of arguments used by any SQL function
+** and store that value in *pMaxFuncArgs.
**
-** This routine is called once after all opcodes have been inserted.
+** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately
+** indicate what the prepared statement actually does.
**
-** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument
-** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by
-** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array.
+** (4) Initialize the p4.xAdvance pointer on opcodes that use it.
**
-** The Op.opflags field is set on all opcodes.
+** (5) Reclaim the memory allocated for storing labels.
+**
+** This routine will only function correctly if the mkopcodeh.tcl generator
+** script numbers the opcodes correctly. Changes to this routine must be
+** coordinated with changes to mkopcodeh.tcl.
*/
static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
- int i;
int nMaxArgs = *pMaxFuncArgs;
Op *pOp;
- int *aLabel = p->aLabel;
+ Parse *pParse = p->pParse;
+ int *aLabel = pParse->aLabel;
p->readOnly = 1;
- for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){
- u8 opcode = pOp->opcode;
-
- pOp->opflags = sqlite3OpcodeProperty[opcode];
- if( opcode==OP_Function || opcode==OP_AggStep ){
- if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5;
- }else if( (opcode==OP_Transaction && pOp->p2!=0) || opcode==OP_Vacuum ){
- p->readOnly = 0;
+ p->bIsReader = 0;
+ pOp = &p->aOp[p->nOp-1];
+ while(1){
+
+ /* Only JUMP opcodes and the short list of special opcodes in the switch
+ ** below need to be considered. The mkopcodeh.tcl generator script groups
+ ** all these opcodes together near the front of the opcode list. Skip
+ ** any opcode that does not need processing by virtual of the fact that
+ ** it is larger than SQLITE_MX_JUMP_OPCODE, as a performance optimization.
+ */
+ if( pOp->opcode<=SQLITE_MX_JUMP_OPCODE ){
+ /* NOTE: Be sure to update mkopcodeh.tcl when adding or removing
+ ** cases from this switch! */
+ switch( pOp->opcode ){
+ case OP_Transaction: {
+ if( pOp->p2!=0 ) p->readOnly = 0;
+ /* fall thru */
+ }
+ case OP_AutoCommit:
+ case OP_Savepoint: {
+ p->bIsReader = 1;
+ break;
+ }
+#ifndef SQLITE_OMIT_WAL
+ case OP_Checkpoint:
+#endif
+ case OP_Vacuum:
+ case OP_JournalMode: {
+ p->readOnly = 0;
+ p->bIsReader = 1;
+ break;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( opcode==OP_VUpdate ){
- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
- }else if( opcode==OP_VFilter ){
- int n;
- assert( p->nOp - i >= 3 );
- assert( pOp[-1].opcode==OP_Integer );
- n = pOp[-1].p1;
- if( n>nMaxArgs ) nMaxArgs = n;
+ case OP_VUpdate: {
+ if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+ break;
+ }
+ case OP_VFilter: {
+ int n;
+ assert( (pOp - p->aOp) >= 3 );
+ assert( pOp[-1].opcode==OP_Integer );
+ n = pOp[-1].p1;
+ if( n>nMaxArgs ) nMaxArgs = n;
+ break;
+ }
#endif
- }else if( opcode==OP_Next || opcode==OP_SorterNext ){
- pOp->p4.xAdvance = sqlite3BtreeNext;
- pOp->p4type = P4_ADVANCE;
- }else if( opcode==OP_Prev ){
- pOp->p4.xAdvance = sqlite3BtreePrevious;
- pOp->p4type = P4_ADVANCE;
- }
-
- if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){
- assert( -1-pOp->p2<p->nLabel );
- pOp->p2 = aLabel[-1-pOp->p2];
+ case OP_Next:
+ case OP_NextIfOpen:
+ case OP_SorterNext: {
+ pOp->p4.xAdvance = sqlite3BtreeNext;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
+ case OP_Prev:
+ case OP_PrevIfOpen: {
+ pOp->p4.xAdvance = sqlite3BtreePrevious;
+ pOp->p4type = P4_ADVANCE;
+ break;
+ }
+ }
+ if( (sqlite3OpcodeProperty[pOp->opcode] & OPFLG_JUMP)!=0 && pOp->p2<0 ){
+ assert( ADDR(pOp->p2)<pParse->nLabel );
+ pOp->p2 = aLabel[ADDR(pOp->p2)];
+ }
}
+ if( pOp==p->aOp ) break;
+ pOp--;
}
- sqlite3DbFree(p->db, p->aLabel);
- p->aLabel = 0;
-
+ sqlite3DbFree(p->db, pParse->aLabel);
+ pParse->aLabel = 0;
+ pParse->nLabel = 0;
*pMaxFuncArgs = nMaxArgs;
+ assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) );
}
/*
@@ -58766,6 +71109,20 @@ SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe *p){
}
/*
+** Verify that at least N opcode slots are available in p without
+** having to malloc for more space (except when compiled using
+** SQLITE_TEST_REALLOC_STRESS). This interface is used during testing
+** to verify that certain calls to sqlite3VdbeAddOpList() can never
+** fail due to a OOM fault and hence that the return value from
+** sqlite3VdbeAddOpList() will always be non-NULL.
+*/
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_TEST_REALLOC_STRESS)
+SQLITE_PRIVATE void sqlite3VdbeVerifyNoMallocRequired(Vdbe *p, int N){
+ assert( p->nOp + N <= p->pParse->nOpAlloc );
+}
+#endif
+
+/*
** This function returns a pointer to the array of opcodes associated with
** the Vdbe passed as the first argument. It is the callers responsibility
** to arrange for the returned array to be eventually freed using the
@@ -58781,7 +71138,7 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg)
assert( aOp && !p->db->mallocFailed );
/* Check that sqlite3VdbeUsesBtree() was not called on this VM */
- assert( p->btreeMask==0 );
+ assert( DbMaskAllZero(p->btreeMask) );
resolveP2Values(p, pnMaxArg);
*pnOp = p->nOp;
@@ -58790,89 +71147,103 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg)
}
/*
-** Add a whole list of operations to the operation stack. Return the
-** address of the first operation added.
+** Add a whole list of operations to the operation stack. Return a
+** pointer to the first operation inserted.
+**
+** Non-zero P2 arguments to jump instructions are automatically adjusted
+** so that the jump target is relative to the first operation inserted.
*/
-SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe *p, int nOp, VdbeOpList const *aOp){
- int addr;
+SQLITE_PRIVATE VdbeOp *sqlite3VdbeAddOpList(
+ Vdbe *p, /* Add opcodes to the prepared statement */
+ int nOp, /* Number of opcodes to add */
+ VdbeOpList const *aOp, /* The opcodes to be added */
+ int iLineno /* Source-file line number of first opcode */
+){
+ int i;
+ VdbeOp *pOut, *pFirst;
+ assert( nOp>0 );
assert( p->magic==VDBE_MAGIC_INIT );
- if( p->nOp + nOp > p->nOpAlloc && growOpArray(p) ){
+ if( p->nOp + nOp > p->pParse->nOpAlloc && growOpArray(p, nOp) ){
return 0;
}
- addr = p->nOp;
- if( ALWAYS(nOp>0) ){
- int i;
- VdbeOpList const *pIn = aOp;
- for(i=0; i<nOp; i++, pIn++){
- int p2 = pIn->p2;
- VdbeOp *pOut = &p->aOp[i+addr];
- pOut->opcode = pIn->opcode;
- pOut->p1 = pIn->p1;
- if( p2<0 && (sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP)!=0 ){
- pOut->p2 = addr + ADDR(p2);
- }else{
- pOut->p2 = p2;
- }
- pOut->p3 = pIn->p3;
- pOut->p4type = P4_NOTUSED;
- pOut->p4.p = 0;
- pOut->p5 = 0;
-#ifdef SQLITE_DEBUG
- pOut->zComment = 0;
- if( sqlite3VdbeAddopTrace ){
- sqlite3VdbePrintOp(0, i+addr, &p->aOp[i+addr]);
- }
+ pFirst = pOut = &p->aOp[p->nOp];
+ for(i=0; i<nOp; i++, aOp++, pOut++){
+ pOut->opcode = aOp->opcode;
+ pOut->p1 = aOp->p1;
+ pOut->p2 = aOp->p2;
+ assert( aOp->p2>=0 );
+ if( (sqlite3OpcodeProperty[aOp->opcode] & OPFLG_JUMP)!=0 && aOp->p2>0 ){
+ pOut->p2 += p->nOp;
+ }
+ pOut->p3 = aOp->p3;
+ pOut->p4type = P4_NOTUSED;
+ pOut->p4.p = 0;
+ pOut->p5 = 0;
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ pOut->zComment = 0;
+#endif
+#ifdef SQLITE_VDBE_COVERAGE
+ pOut->iSrcLine = iLineno+i;
+#else
+ (void)iLineno;
#endif
+#ifdef SQLITE_DEBUG
+ if( p->db->flags & SQLITE_VdbeAddopTrace ){
+ sqlite3VdbePrintOp(0, i+p->nOp, &p->aOp[i+p->nOp]);
}
- p->nOp += nOp;
+#endif
}
- return addr;
+ p->nOp += nOp;
+ return pFirst;
}
+#if defined(SQLITE_ENABLE_STMT_SCANSTATUS)
/*
-** Change the value of the P1 operand for a specific instruction.
-** This routine is useful when a large program is loaded from a
-** static array using sqlite3VdbeAddOpList but we want to make a
-** few minor changes to the program.
+** Add an entry to the array of counters managed by sqlite3_stmt_scanstatus().
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
- assert( p!=0 );
- if( ((u32)p->nOp)>addr ){
- p->aOp[addr].p1 = val;
+SQLITE_PRIVATE void sqlite3VdbeScanStatus(
+ Vdbe *p, /* VM to add scanstatus() to */
+ int addrExplain, /* Address of OP_Explain (or 0) */
+ int addrLoop, /* Address of loop counter */
+ int addrVisit, /* Address of rows visited counter */
+ LogEst nEst, /* Estimated number of output rows */
+ const char *zName /* Name of table or index being scanned */
+){
+ int nByte = (p->nScan+1) * sizeof(ScanStatus);
+ ScanStatus *aNew;
+ aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
+ if( aNew ){
+ ScanStatus *pNew = &aNew[p->nScan++];
+ pNew->addrExplain = addrExplain;
+ pNew->addrLoop = addrLoop;
+ pNew->addrVisit = addrVisit;
+ pNew->nEst = nEst;
+ pNew->zName = sqlite3DbStrDup(p->db, zName);
+ p->aScan = aNew;
}
}
+#endif
+
/*
-** Change the value of the P2 operand for a specific instruction.
-** This routine is useful for setting a jump destination.
+** Change the value of the opcode, or P1, P2, P3, or P5 operands
+** for a specific instruction.
*/
+SQLITE_PRIVATE void sqlite3VdbeChangeOpcode(Vdbe *p, u32 addr, u8 iNewOpcode){
+ sqlite3VdbeGetOp(p,addr)->opcode = iNewOpcode;
+}
+SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe *p, u32 addr, int val){
+ sqlite3VdbeGetOp(p,addr)->p1 = val;
+}
SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe *p, u32 addr, int val){
- assert( p!=0 );
- if( ((u32)p->nOp)>addr ){
- p->aOp[addr].p2 = val;
- }
+ sqlite3VdbeGetOp(p,addr)->p2 = val;
}
-
-/*
-** Change the value of the P3 operand for a specific instruction.
-*/
SQLITE_PRIVATE void sqlite3VdbeChangeP3(Vdbe *p, u32 addr, int val){
- assert( p!=0 );
- if( ((u32)p->nOp)>addr ){
- p->aOp[addr].p3 = val;
- }
+ sqlite3VdbeGetOp(p,addr)->p3 = val;
}
-
-/*
-** Change the value of the P5 operand for the most recently
-** added operation.
-*/
-SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
- assert( p!=0 );
- if( p->aOp ){
- assert( p->nOp>0 );
- p->aOp[p->nOp-1].p5 = val;
- }
+SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 p5){
+ assert( p->nOp>0 || p->db->mallocFailed );
+ if( p->nOp>0 ) p->aOp[p->nOp-1].p5 = p5;
}
/*
@@ -58880,7 +71251,6 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u8 val){
** the address of the next instruction to be coded.
*/
SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
- assert( addr>=0 );
sqlite3VdbeChangeP2(p, addr, p->nOp);
}
@@ -58890,7 +71260,7 @@ SQLITE_PRIVATE void sqlite3VdbeJumpHere(Vdbe *p, int addr){
** the FuncDef is not ephermal, then do nothing.
*/
static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){
- if( ALWAYS(pDef) && (pDef->flags & SQLITE_FUNC_EPHEM)!=0 ){
+ if( (pDef->funcFlags & SQLITE_FUNC_EPHEM)!=0 ){
sqlite3DbFree(db, pDef);
}
}
@@ -58900,48 +71270,57 @@ static void vdbeFreeOpArray(sqlite3 *, Op *, int);
/*
** Delete a P4 value if necessary.
*/
+static SQLITE_NOINLINE void freeP4Mem(sqlite3 *db, Mem *p){
+ if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
+ sqlite3DbFree(db, p);
+}
+static SQLITE_NOINLINE void freeP4FuncCtx(sqlite3 *db, sqlite3_context *p){
+ freeEphemeralFunction(db, p->pFunc);
+ sqlite3DbFree(db, p);
+}
static void freeP4(sqlite3 *db, int p4type, void *p4){
- if( p4 ){
- assert( db );
- switch( p4type ){
- case P4_REAL:
- case P4_INT64:
- case P4_DYNAMIC:
- case P4_KEYINFO:
- case P4_INTARRAY:
- case P4_KEYINFO_HANDOFF: {
- sqlite3DbFree(db, p4);
- break;
- }
- case P4_MPRINTF: {
- if( db->pnBytesFreed==0 ) sqlite3_free(p4);
- break;
- }
- case P4_VDBEFUNC: {
- VdbeFunc *pVdbeFunc = (VdbeFunc *)p4;
- freeEphemeralFunction(db, pVdbeFunc->pFunc);
- if( db->pnBytesFreed==0 ) sqlite3VdbeDeleteAuxData(pVdbeFunc, 0);
- sqlite3DbFree(db, pVdbeFunc);
- break;
- }
- case P4_FUNCDEF: {
- freeEphemeralFunction(db, (FuncDef*)p4);
- break;
- }
- case P4_MEM: {
- if( db->pnBytesFreed==0 ){
- sqlite3ValueFree((sqlite3_value*)p4);
- }else{
- Mem *p = (Mem*)p4;
- sqlite3DbFree(db, p->zMalloc);
- sqlite3DbFree(db, p);
- }
- break;
- }
- case P4_VTAB : {
- if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
- break;
+ assert( db );
+ switch( p4type ){
+ case P4_FUNCCTX: {
+ freeP4FuncCtx(db, (sqlite3_context*)p4);
+ break;
+ }
+ case P4_REAL:
+ case P4_INT64:
+ case P4_DYNAMIC:
+ case P4_INTARRAY: {
+ sqlite3DbFree(db, p4);
+ break;
+ }
+ case P4_KEYINFO: {
+ if( db->pnBytesFreed==0 ) sqlite3KeyInfoUnref((KeyInfo*)p4);
+ break;
+ }
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ case P4_EXPR: {
+ sqlite3ExprDelete(db, (Expr*)p4);
+ break;
+ }
+#endif
+ case P4_MPRINTF: {
+ if( db->pnBytesFreed==0 ) sqlite3_free(p4);
+ break;
+ }
+ case P4_FUNCDEF: {
+ freeEphemeralFunction(db, (FuncDef*)p4);
+ break;
+ }
+ case P4_MEM: {
+ if( db->pnBytesFreed==0 ){
+ sqlite3ValueFree((sqlite3_value*)p4);
+ }else{
+ freeP4Mem(db, (Mem*)p4);
}
+ break;
+ }
+ case P4_VTAB : {
+ if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
+ break;
}
}
}
@@ -58955,8 +71334,8 @@ static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){
if( aOp ){
Op *pOp;
for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
- freeP4(db, pOp->p4type, pOp->p4.p);
-#ifdef SQLITE_DEBUG
+ if( pOp->p4type ) freeP4(db, pOp->p4type, pOp->p4.p);
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
sqlite3DbFree(db, pOp->zComment);
#endif
}
@@ -58977,13 +71356,27 @@ SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *pVdbe, SubProgram *p){
/*
** Change the opcode at addr into OP_Noop
*/
-SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
- if( p->aOp ){
- VdbeOp *pOp = &p->aOp[addr];
- sqlite3 *db = p->db;
- freeP4(db, pOp->p4type, pOp->p4.p);
- memset(pOp, 0, sizeof(pOp[0]));
- pOp->opcode = OP_Noop;
+SQLITE_PRIVATE int sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
+ VdbeOp *pOp;
+ if( p->db->mallocFailed ) return 0;
+ assert( addr>=0 && addr<p->nOp );
+ pOp = &p->aOp[addr];
+ freeP4(p->db, pOp->p4type, pOp->p4.p);
+ pOp->p4type = P4_NOTUSED;
+ pOp->p4.z = 0;
+ pOp->opcode = OP_Noop;
+ return 1;
+}
+
+/*
+** If the last opcode is "op" and it is not a jump destination,
+** then remove it. Return true if and only if an opcode was removed.
+*/
+SQLITE_PRIVATE int sqlite3VdbeDeletePriorOpcode(Vdbe *p, u8 op){
+ if( p->nOp>0 && p->aOp[p->nOp-1].opcode==op ){
+ return sqlite3VdbeChangeToNoop(p, p->nOp-1);
+ }else{
+ return 0;
}
}
@@ -58997,14 +71390,6 @@ SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
** the string is made into memory obtained from sqlite3_malloc().
** A value of n==0 means copy bytes of zP4 up to and including the
** first null byte. If n>0 then copy n+1 bytes of zP4.
-**
-** If n==P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure.
-** A copy is made of the KeyInfo structure into memory obtained from
-** sqlite3_malloc, to be freed when the Vdbe is finalized.
-** n==P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure
-** stored in memory that the caller has obtained from sqlite3_malloc. The
-** caller should not free the allocation, it will be freed when the Vdbe is
-** finalized.
**
** Other values of n (P4_STATIC, P4_COLLSEQ etc.) indicate that zP4 points
** to a string or structure that is guaranteed to exist for the lifetime of
@@ -59012,16 +71397,34 @@ SQLITE_PRIVATE void sqlite3VdbeChangeToNoop(Vdbe *p, int addr){
**
** If addr<0 then change P4 on the most recently inserted instruction.
*/
+static void SQLITE_NOINLINE vdbeChangeP4Full(
+ Vdbe *p,
+ Op *pOp,
+ const char *zP4,
+ int n
+){
+ if( pOp->p4type ){
+ freeP4(p->db, pOp->p4type, pOp->p4.p);
+ pOp->p4type = 0;
+ pOp->p4.p = 0;
+ }
+ if( n<0 ){
+ sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n);
+ }else{
+ if( n==0 ) n = sqlite3Strlen30(zP4);
+ pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
+ pOp->p4type = P4_DYNAMIC;
+ }
+}
SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int n){
Op *pOp;
sqlite3 *db;
assert( p!=0 );
db = p->db;
assert( p->magic==VDBE_MAGIC_INIT );
- if( p->aOp==0 || db->mallocFailed ){
- if ( n!=P4_KEYINFO && n!=P4_VTAB ) {
- freeP4(db, n, (void*)*(char**)&zP4);
- }
+ assert( p->aOp!=0 || db->mallocFailed );
+ if( db->mallocFailed ){
+ if( n!=P4_VTAB ) freeP4(db, n, (void*)*(char**)&zP4);
return;
}
assert( p->nOp>0 );
@@ -59030,91 +71433,79 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP4(Vdbe *p, int addr, const char *zP4, int
addr = p->nOp - 1;
}
pOp = &p->aOp[addr];
- freeP4(db, pOp->p4type, pOp->p4.p);
- pOp->p4.p = 0;
+ if( n>=0 || pOp->p4type ){
+ vdbeChangeP4Full(p, pOp, zP4, n);
+ return;
+ }
if( n==P4_INT32 ){
/* Note: this cast is safe, because the origin data point was an int
** that was cast to a (const char *). */
pOp->p4.i = SQLITE_PTR_TO_INT(zP4);
pOp->p4type = P4_INT32;
- }else if( zP4==0 ){
- pOp->p4.p = 0;
- pOp->p4type = P4_NOTUSED;
- }else if( n==P4_KEYINFO ){
- KeyInfo *pKeyInfo;
- int nField, nByte;
-
- nField = ((KeyInfo*)zP4)->nField;
- nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo->aColl[0]) + nField;
- pKeyInfo = sqlite3DbMallocRaw(0, nByte);
- pOp->p4.pKeyInfo = pKeyInfo;
- if( pKeyInfo ){
- u8 *aSortOrder;
- memcpy((char*)pKeyInfo, zP4, nByte - nField);
- aSortOrder = pKeyInfo->aSortOrder;
- if( aSortOrder ){
- pKeyInfo->aSortOrder = (unsigned char*)&pKeyInfo->aColl[nField];
- memcpy(pKeyInfo->aSortOrder, aSortOrder, nField);
- }
- pOp->p4type = P4_KEYINFO;
- }else{
- p->db->mallocFailed = 1;
- pOp->p4type = P4_NOTUSED;
- }
- }else if( n==P4_KEYINFO_HANDOFF ){
- pOp->p4.p = (void*)zP4;
- pOp->p4type = P4_KEYINFO;
- }else if( n==P4_VTAB ){
- pOp->p4.p = (void*)zP4;
- pOp->p4type = P4_VTAB;
- sqlite3VtabLock((VTable *)zP4);
- assert( ((VTable *)zP4)->db==p->db );
- }else if( n<0 ){
+ }else if( zP4!=0 ){
+ assert( n<0 );
pOp->p4.p = (void*)zP4;
pOp->p4type = (signed char)n;
- }else{
- if( n==0 ) n = sqlite3Strlen30(zP4);
- pOp->p4.z = sqlite3DbStrNDup(p->db, zP4, n);
- pOp->p4type = P4_DYNAMIC;
+ if( n==P4_VTAB ) sqlite3VtabLock((VTable*)zP4);
}
}
-#ifndef NDEBUG
/*
-** Change the comment on the the most recently coded instruction. Or
+** Set the P4 on the most recently added opcode to the KeyInfo for the
+** index given.
+*/
+SQLITE_PRIVATE void sqlite3VdbeSetP4KeyInfo(Parse *pParse, Index *pIdx){
+ Vdbe *v = pParse->pVdbe;
+ assert( v!=0 );
+ assert( pIdx!=0 );
+ sqlite3VdbeChangeP4(v, -1, (char*)sqlite3KeyInfoOfIndex(pParse, pIdx),
+ P4_KEYINFO);
+}
+
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+/*
+** Change the comment on the most recently coded instruction. Or
** insert a No-op and add the comment to that new instruction. This
** makes the code easier to read during debugging. None of this happens
** in a production build.
*/
-SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
- va_list ap;
- if( !p ) return;
+static void vdbeVComment(Vdbe *p, const char *zFormat, va_list ap){
assert( p->nOp>0 || p->aOp==0 );
assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
if( p->nOp ){
- char **pz = &p->aOp[p->nOp-1].zComment;
+ assert( p->aOp );
+ sqlite3DbFree(p->db, p->aOp[p->nOp-1].zComment);
+ p->aOp[p->nOp-1].zComment = sqlite3VMPrintf(p->db, zFormat, ap);
+ }
+}
+SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
+ va_list ap;
+ if( p ){
va_start(ap, zFormat);
- sqlite3DbFree(p->db, *pz);
- *pz = sqlite3VMPrintf(p->db, zFormat, ap);
+ vdbeVComment(p, zFormat, ap);
va_end(ap);
}
}
SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
va_list ap;
- if( !p ) return;
- sqlite3VdbeAddOp0(p, OP_Noop);
- assert( p->nOp>0 || p->aOp==0 );
- assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed );
- if( p->nOp ){
- char **pz = &p->aOp[p->nOp-1].zComment;
+ if( p ){
+ sqlite3VdbeAddOp0(p, OP_Noop);
va_start(ap, zFormat);
- sqlite3DbFree(p->db, *pz);
- *pz = sqlite3VMPrintf(p->db, zFormat, ap);
+ vdbeVComment(p, zFormat, ap);
va_end(ap);
}
}
#endif /* NDEBUG */
+#ifdef SQLITE_VDBE_COVERAGE
+/*
+** Set the value if the iSrcLine field for the previously coded instruction.
+*/
+SQLITE_PRIVATE void sqlite3VdbeSetLineNumber(Vdbe *v, int iLine){
+ sqlite3VdbeGetOp(v,-1)->iSrcLine = iLine;
+}
+#endif /* SQLITE_VDBE_COVERAGE */
+
/*
** Return the opcode for a given address. If the address is -1, then
** return the most recently inserted opcode.
@@ -59123,18 +71514,10 @@ SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){
** routine, then a pointer to a dummy VdbeOp will be returned. That opcode
** is readable but not writable, though it is cast to a writable value.
** The return of a dummy opcode allows the call to continue functioning
-** after a OOM fault without having to check to see if the return from
+** after an OOM fault without having to check to see if the return from
** this routine is a valid pointer. But because the dummy.opcode is 0,
** dummy will never be written to. This is verified by code inspection and
** by running with Valgrind.
-**
-** About the #ifdef SQLITE_OMIT_TRACE: Normally, this routine is never called
-** unless p->nOp>0. This is because in the absense of SQLITE_OMIT_TRACE,
-** an OP_Trace instruction is always inserted by sqlite3VdbeGet() as soon as
-** a new VDBE is created. So we are free to set addr to p->nOp-1 without
-** having to double-check to make sure that the result is non-negative. But
-** if SQLITE_OMIT_TRACE is defined, the OP_Trace is omitted and we do need to
-** check the value of p->nOp-1 before continuing.
*/
SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
/* C89 specifies that the constant "dummy" will be initialized to all
@@ -59142,9 +71525,6 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
static VdbeOp dummy; /* Ignore the MSVC warning about no initializer */
assert( p->magic==VDBE_MAGIC_INIT );
if( addr<0 ){
-#ifdef SQLITE_OMIT_TRACE
- if( p->nOp==0 ) return (VdbeOp*)&dummy;
-#endif
addr = p->nOp - 1;
}
assert( (addr>=0 && addr<p->nOp) || p->db->mallocFailed );
@@ -59155,77 +71535,249 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe *p, int addr){
}
}
-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
- || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
+/*
+** Return an integer value for one of the parameters to the opcode pOp
+** determined by character c.
+*/
+static int translateP(char c, const Op *pOp){
+ if( c=='1' ) return pOp->p1;
+ if( c=='2' ) return pOp->p2;
+ if( c=='3' ) return pOp->p3;
+ if( c=='4' ) return pOp->p4.i;
+ return pOp->p5;
+}
+
+/*
+** Compute a string for the "comment" field of a VDBE opcode listing.
+**
+** The Synopsis: field in comments in the vdbe.c source file gets converted
+** to an extra string that is appended to the sqlite3OpcodeName(). In the
+** absence of other comments, this synopsis becomes the comment on the opcode.
+** Some translation occurs:
+**
+** "PX" -> "r[X]"
+** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1
+** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0
+** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x
+*/
+static int displayComment(
+ const Op *pOp, /* The opcode to be commented */
+ const char *zP4, /* Previously obtained value for P4 */
+ char *zTemp, /* Write result here */
+ int nTemp /* Space available in zTemp[] */
+){
+ const char *zOpName;
+ const char *zSynopsis;
+ int nOpName;
+ int ii, jj;
+ char zAlt[50];
+ zOpName = sqlite3OpcodeName(pOp->opcode);
+ nOpName = sqlite3Strlen30(zOpName);
+ if( zOpName[nOpName+1] ){
+ int seenCom = 0;
+ char c;
+ zSynopsis = zOpName += nOpName + 1;
+ if( strncmp(zSynopsis,"IF ",3)==0 ){
+ if( pOp->p5 & SQLITE_STOREP2 ){
+ sqlite3_snprintf(sizeof(zAlt), zAlt, "r[P2] = (%s)", zSynopsis+3);
+ }else{
+ sqlite3_snprintf(sizeof(zAlt), zAlt, "if %s goto P2", zSynopsis+3);
+ }
+ zSynopsis = zAlt;
+ }
+ for(ii=jj=0; jj<nTemp-1 && (c = zSynopsis[ii])!=0; ii++){
+ if( c=='P' ){
+ c = zSynopsis[++ii];
+ if( c=='4' ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", zP4);
+ }else if( c=='X' ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%s", pOp->zComment);
+ seenCom = 1;
+ }else{
+ int v1 = translateP(c, pOp);
+ int v2;
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "%d", v1);
+ if( strncmp(zSynopsis+ii+1, "@P", 2)==0 ){
+ ii += 3;
+ jj += sqlite3Strlen30(zTemp+jj);
+ v2 = translateP(zSynopsis[ii], pOp);
+ if( strncmp(zSynopsis+ii+1,"+1",2)==0 ){
+ ii += 2;
+ v2++;
+ }
+ if( v2>1 ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "..%d", v1+v2-1);
+ }
+ }else if( strncmp(zSynopsis+ii+1, "..P3", 4)==0 && pOp->p3==0 ){
+ ii += 4;
+ }
+ }
+ jj += sqlite3Strlen30(zTemp+jj);
+ }else{
+ zTemp[jj++] = c;
+ }
+ }
+ if( !seenCom && jj<nTemp-5 && pOp->zComment ){
+ sqlite3_snprintf(nTemp-jj, zTemp+jj, "; %s", pOp->zComment);
+ jj += sqlite3Strlen30(zTemp+jj);
+ }
+ if( jj<nTemp ) zTemp[jj] = 0;
+ }else if( pOp->zComment ){
+ sqlite3_snprintf(nTemp, zTemp, "%s", pOp->zComment);
+ jj = sqlite3Strlen30(zTemp);
+ }else{
+ zTemp[0] = 0;
+ jj = 0;
+ }
+ return jj;
+}
+#endif /* SQLITE_DEBUG */
+
+#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
+/*
+** Translate the P4.pExpr value for an OP_CursorHint opcode into text
+** that can be displayed in the P4 column of EXPLAIN output.
+*/
+static void displayP4Expr(StrAccum *p, Expr *pExpr){
+ const char *zOp = 0;
+ switch( pExpr->op ){
+ case TK_STRING:
+ sqlite3XPrintf(p, "%Q", pExpr->u.zToken);
+ break;
+ case TK_INTEGER:
+ sqlite3XPrintf(p, "%d", pExpr->u.iValue);
+ break;
+ case TK_NULL:
+ sqlite3XPrintf(p, "NULL");
+ break;
+ case TK_REGISTER: {
+ sqlite3XPrintf(p, "r[%d]", pExpr->iTable);
+ break;
+ }
+ case TK_COLUMN: {
+ if( pExpr->iColumn<0 ){
+ sqlite3XPrintf(p, "rowid");
+ }else{
+ sqlite3XPrintf(p, "c%d", (int)pExpr->iColumn);
+ }
+ break;
+ }
+ case TK_LT: zOp = "LT"; break;
+ case TK_LE: zOp = "LE"; break;
+ case TK_GT: zOp = "GT"; break;
+ case TK_GE: zOp = "GE"; break;
+ case TK_NE: zOp = "NE"; break;
+ case TK_EQ: zOp = "EQ"; break;
+ case TK_IS: zOp = "IS"; break;
+ case TK_ISNOT: zOp = "ISNOT"; break;
+ case TK_AND: zOp = "AND"; break;
+ case TK_OR: zOp = "OR"; break;
+ case TK_PLUS: zOp = "ADD"; break;
+ case TK_STAR: zOp = "MUL"; break;
+ case TK_MINUS: zOp = "SUB"; break;
+ case TK_REM: zOp = "REM"; break;
+ case TK_BITAND: zOp = "BITAND"; break;
+ case TK_BITOR: zOp = "BITOR"; break;
+ case TK_SLASH: zOp = "DIV"; break;
+ case TK_LSHIFT: zOp = "LSHIFT"; break;
+ case TK_RSHIFT: zOp = "RSHIFT"; break;
+ case TK_CONCAT: zOp = "CONCAT"; break;
+ case TK_UMINUS: zOp = "MINUS"; break;
+ case TK_UPLUS: zOp = "PLUS"; break;
+ case TK_BITNOT: zOp = "BITNOT"; break;
+ case TK_NOT: zOp = "NOT"; break;
+ case TK_ISNULL: zOp = "ISNULL"; break;
+ case TK_NOTNULL: zOp = "NOTNULL"; break;
+
+ default:
+ sqlite3XPrintf(p, "%s", "expr");
+ break;
+ }
+
+ if( zOp ){
+ sqlite3XPrintf(p, "%s(", zOp);
+ displayP4Expr(p, pExpr->pLeft);
+ if( pExpr->pRight ){
+ sqlite3StrAccumAppend(p, ",", 1);
+ displayP4Expr(p, pExpr->pRight);
+ }
+ sqlite3StrAccumAppend(p, ")", 1);
+ }
+}
+#endif /* VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS) */
+
+
+#if VDBE_DISPLAY_P4
/*
** Compute a string that describes the P4 parameter for an opcode.
** Use zTemp for any required temporary buffer space.
*/
static char *displayP4(Op *pOp, char *zTemp, int nTemp){
char *zP4 = zTemp;
+ StrAccum x;
assert( nTemp>=20 );
+ sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
switch( pOp->p4type ){
- case P4_KEYINFO_STATIC:
case P4_KEYINFO: {
- int i, j;
+ int j;
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
- sqlite3_snprintf(nTemp, zTemp, "keyinfo(%d", pKeyInfo->nField);
- i = sqlite3Strlen30(zTemp);
+ assert( pKeyInfo->aSortOrder!=0 );
+ sqlite3XPrintf(&x, "k(%d", pKeyInfo->nField);
for(j=0; j<pKeyInfo->nField; j++){
CollSeq *pColl = pKeyInfo->aColl[j];
- if( pColl ){
- int n = sqlite3Strlen30(pColl->zName);
- if( i+n>nTemp-6 ){
- memcpy(&zTemp[i],",...",4);
- break;
- }
- zTemp[i++] = ',';
- if( pKeyInfo->aSortOrder && pKeyInfo->aSortOrder[j] ){
- zTemp[i++] = '-';
- }
- memcpy(&zTemp[i], pColl->zName,n+1);
- i += n;
- }else if( i+4<nTemp-6 ){
- memcpy(&zTemp[i],",nil",4);
- i += 4;
- }
+ const char *zColl = pColl ? pColl->zName : "";
+ if( strcmp(zColl, "BINARY")==0 ) zColl = "B";
+ sqlite3XPrintf(&x, ",%s%s", pKeyInfo->aSortOrder[j] ? "-" : "", zColl);
}
- zTemp[i++] = ')';
- zTemp[i] = 0;
- assert( i<nTemp );
+ sqlite3StrAccumAppend(&x, ")", 1);
+ break;
+ }
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ case P4_EXPR: {
+ displayP4Expr(&x, pOp->p4.pExpr);
break;
}
+#endif
case P4_COLLSEQ: {
CollSeq *pColl = pOp->p4.pColl;
- sqlite3_snprintf(nTemp, zTemp, "collseq(%.20s)", pColl->zName);
+ sqlite3XPrintf(&x, "(%.20s)", pColl->zName);
break;
}
case P4_FUNCDEF: {
FuncDef *pDef = pOp->p4.pFunc;
- sqlite3_snprintf(nTemp, zTemp, "%s(%d)", pDef->zName, pDef->nArg);
+ sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
break;
}
+#ifdef SQLITE_DEBUG
+ case P4_FUNCCTX: {
+ FuncDef *pDef = pOp->p4.pCtx->pFunc;
+ sqlite3XPrintf(&x, "%s(%d)", pDef->zName, pDef->nArg);
+ break;
+ }
+#endif
case P4_INT64: {
- sqlite3_snprintf(nTemp, zTemp, "%lld", *pOp->p4.pI64);
+ sqlite3XPrintf(&x, "%lld", *pOp->p4.pI64);
break;
}
case P4_INT32: {
- sqlite3_snprintf(nTemp, zTemp, "%d", pOp->p4.i);
+ sqlite3XPrintf(&x, "%d", pOp->p4.i);
break;
}
case P4_REAL: {
- sqlite3_snprintf(nTemp, zTemp, "%.16g", *pOp->p4.pReal);
+ sqlite3XPrintf(&x, "%.16g", *pOp->p4.pReal);
break;
}
case P4_MEM: {
Mem *pMem = pOp->p4.pMem;
- assert( (pMem->flags & MEM_Null)==0 );
if( pMem->flags & MEM_Str ){
zP4 = pMem->z;
}else if( pMem->flags & MEM_Int ){
- sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i);
+ sqlite3XPrintf(&x, "%lld", pMem->u.i);
}else if( pMem->flags & MEM_Real ){
- sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r);
+ sqlite3XPrintf(&x, "%.16g", pMem->u.r);
+ }else if( pMem->flags & MEM_Null ){
+ zP4 = "NULL";
}else{
assert( pMem->flags & MEM_Blob );
zP4 = "(blob)";
@@ -59235,22 +71787,34 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
#ifndef SQLITE_OMIT_VIRTUALTABLE
case P4_VTAB: {
sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab;
- sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule);
+ sqlite3XPrintf(&x, "vtab:%p", pVtab);
break;
}
#endif
case P4_INTARRAY: {
- sqlite3_snprintf(nTemp, zTemp, "intarray");
+ int i;
+ int *ai = pOp->p4.ai;
+ int n = ai[0]; /* The first element of an INTARRAY is always the
+ ** count of the number of elements to follow */
+ for(i=1; i<n; i++){
+ sqlite3XPrintf(&x, ",%d", ai[i]);
+ }
+ zTemp[0] = '[';
+ sqlite3StrAccumAppend(&x, "]", 1);
break;
}
case P4_SUBPROGRAM: {
- sqlite3_snprintf(nTemp, zTemp, "program");
+ sqlite3XPrintf(&x, "program");
break;
}
case P4_ADVANCE: {
zTemp[0] = 0;
break;
}
+ case P4_TABLE: {
+ sqlite3XPrintf(&x, "%s", pOp->p4.pTab->zName);
+ break;
+ }
default: {
zP4 = pOp->p4.z;
if( zP4==0 ){
@@ -59259,28 +71823,30 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
}
}
}
+ sqlite3StrAccumFinish(&x);
assert( zP4!=0 );
return zP4;
}
-#endif
+#endif /* VDBE_DISPLAY_P4 */
/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
**
** The prepared statements need to know in advance the complete set of
-** attached databases that they will be using. A mask of these databases
-** is maintained in p->btreeMask and is used for locking and other purposes.
+** attached databases that will be use. A mask of these databases
+** is maintained in p->btreeMask. The p->lockMask value is the subset of
+** p->btreeMask of databases that will require a lock.
*/
SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){
assert( i>=0 && i<p->db->nDb && i<(int)sizeof(yDbMask)*8 );
assert( i<(int)sizeof(p->btreeMask)*8 );
- p->btreeMask |= ((yDbMask)1)<<i;
+ DbMaskSet(p->btreeMask, i);
if( i!=1 && sqlite3BtreeSharable(p->db->aDb[i].pBt) ){
- p->lockMask |= ((yDbMask)1)<<i;
+ DbMaskSet(p->lockMask, i);
}
}
-#if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE>0
+#if !defined(SQLITE_OMIT_SHARED_CACHE)
/*
** If SQLite is compiled to support shared-cache mode and to be threadsafe,
** this routine obtains the mutex associated with each BtShared structure
@@ -59304,16 +71870,15 @@ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){
*/
SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe *p){
int i;
- yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
- if( p->lockMask==0 ) return; /* The common case */
+ if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
- for(i=0, mask=1; i<nDb; i++, mask += mask){
- if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
+ for(i=0; i<nDb; i++){
+ if( i!=1 && DbMaskTest(p->lockMask,i) && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeEnter(aDb[i].pBt);
}
}
@@ -59324,22 +71889,24 @@ SQLITE_PRIVATE void sqlite3VdbeEnter(Vdbe *p){
/*
** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter().
*/
-SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
+static SQLITE_NOINLINE void vdbeLeave(Vdbe *p){
int i;
- yDbMask mask;
sqlite3 *db;
Db *aDb;
int nDb;
- if( p->lockMask==0 ) return; /* The common case */
db = p->db;
aDb = db->aDb;
nDb = db->nDb;
- for(i=0, mask=1; i<nDb; i++, mask += mask){
- if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
+ for(i=0; i<nDb; i++){
+ if( i!=1 && DbMaskTest(p->lockMask,i) && ALWAYS(aDb[i].pBt!=0) ){
sqlite3BtreeLeave(aDb[i].pBt);
}
}
}
+SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
+ if( DbMaskAllZero(p->lockMask) ) return; /* The common case */
+ vdbeLeave(p);
+}
#endif
#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
@@ -59349,37 +71916,57 @@ SQLITE_PRIVATE void sqlite3VdbeLeave(Vdbe *p){
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, Op *pOp){
char *zP4;
char zPtr[50];
- static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n";
+ char zCom[100];
+ static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
if( pOut==0 ) pOut = stdout;
zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
- fprintf(pOut, zFormat1, pc,
- sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
-#ifdef SQLITE_DEBUG
- pOp->zComment ? pOp->zComment : ""
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ displayComment(pOp, zP4, zCom, sizeof(zCom));
#else
- ""
+ zCom[0] = 0;
#endif
+ /* NB: The sqlite3OpcodeName() function is implemented by code created
+ ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
+ ** information from the vdbe.c source text */
+ fprintf(pOut, zFormat1, pc,
+ sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
+ zCom
);
fflush(pOut);
}
#endif
/*
+** Initialize an array of N Mem element.
+*/
+static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
+ while( (N--)>0 ){
+ p->db = db;
+ p->flags = flags;
+ p->szMalloc = 0;
+#ifdef SQLITE_DEBUG
+ p->pScopyFrom = 0;
+#endif
+ p++;
+ }
+}
+
+/*
** Release an array of N Mem elements
*/
static void releaseMemArray(Mem *p, int N){
if( p && N ){
- Mem *pEnd;
+ Mem *pEnd = &p[N];
sqlite3 *db = p->db;
- u8 malloc_failed = db->mallocFailed;
if( db->pnBytesFreed ){
- for(pEnd=&p[N]; p<pEnd; p++){
- sqlite3DbFree(db, p->zMalloc);
- }
+ do{
+ if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
+ }while( (++p)<pEnd );
return;
}
- for(pEnd=&p[N]; p<pEnd; p++){
+ do{
assert( (&p[1])==pEnd || p[0].db==p[1].db );
+ assert( sqlite3VdbeCheckMemInvariants(p) );
/* This block is really an inlined version of sqlite3VdbeMemRelease()
** that takes advantage of the fact that the memory cell value is
@@ -59393,16 +71980,19 @@ static void releaseMemArray(Mem *p, int N){
** with no indexes using a single prepared INSERT statement, bind()
** and reset(). Inserts are grouped into a transaction.
*/
+ testcase( p->flags & MEM_Agg );
+ testcase( p->flags & MEM_Dyn );
+ testcase( p->flags & MEM_Frame );
+ testcase( p->flags & MEM_RowSet );
if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){
sqlite3VdbeMemRelease(p);
- }else if( p->zMalloc ){
+ }else if( p->szMalloc ){
sqlite3DbFree(db, p->zMalloc);
- p->zMalloc = 0;
+ p->szMalloc = 0;
}
- p->flags = MEM_Null;
- }
- db->mallocFailed = malloc_failed;
+ p->flags = MEM_Undefined;
+ }while( (++p)<pEnd );
}
}
@@ -59418,6 +72008,7 @@ SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame *p){
sqlite3VdbeFreeCursor(p->v, apCsr[i]);
}
releaseMemArray(aMem, p->nChildMem);
+ sqlite3VdbeDeleteAuxData(p->v->db, &p->pAuxData, -1, 0);
sqlite3DbFree(p->v->db, p);
}
@@ -59447,7 +72038,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
sqlite3 *db = p->db; /* The database connection */
int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
- Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */
+ Mem *pMem = &p->aMem[1]; /* First Mem of result set */
assert( p->explain );
assert( p->magic==VDBE_MAGIC_RUN );
@@ -59458,11 +72049,12 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
*/
releaseMemArray(pMem, 8);
+ p->pResultSet = 0;
- if( p->rc==SQLITE_NOMEM ){
+ if( p->rc==SQLITE_NOMEM_BKPT ){
/* This happens if a malloc() inside a call to sqlite3_column_text() or
** sqlite3_column_text16() failed. */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return SQLITE_ERROR;
}
@@ -59501,9 +72093,9 @@ SQLITE_PRIVATE int sqlite3VdbeList(
}else if( db->u1.isInterrupted ){
p->rc = SQLITE_INTERRUPT;
rc = SQLITE_ERROR;
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc));
+ sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
}else{
- char *z;
+ char *zP4;
Op *pOp;
if( i<p->nOp ){
/* The output line number is small enough that we are still in the
@@ -59521,15 +72113,13 @@ SQLITE_PRIVATE int sqlite3VdbeList(
}
if( p->explain==1 ){
pMem->flags = MEM_Int;
- pMem->type = SQLITE_INTEGER;
pMem->u.i = i; /* Program counter */
pMem++;
pMem->flags = MEM_Static|MEM_Str|MEM_Term;
- pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
+ pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
- pMem->type = SQLITE_TEXT;
pMem->enc = SQLITE_UTF8;
pMem++;
@@ -59544,7 +72134,7 @@ SQLITE_PRIVATE int sqlite3VdbeList(
for(j=0; j<nSub; j++){
if( apSub[j]==pOp->p4.pProgram ) break;
}
- if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, 1) ){
+ if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, nSub!=0) ){
apSub = (SubProgram **)pSub->z;
apSub[nSub++] = pOp->p4.pProgram;
pSub->flags |= MEM_Blob;
@@ -59555,63 +72145,58 @@ SQLITE_PRIVATE int sqlite3VdbeList(
pMem->flags = MEM_Int;
pMem->u.i = pOp->p1; /* P1 */
- pMem->type = SQLITE_INTEGER;
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p2; /* P2 */
- pMem->type = SQLITE_INTEGER;
pMem++;
pMem->flags = MEM_Int;
pMem->u.i = pOp->p3; /* P3 */
- pMem->type = SQLITE_INTEGER;
pMem++;
- if( sqlite3VdbeMemGrow(pMem, 32, 0) ){ /* P4 */
+ if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
- pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
- z = displayP4(pOp, pMem->z, 32);
- if( z!=pMem->z ){
- sqlite3VdbeMemSetStr(pMem, z, -1, SQLITE_UTF8, 0);
+ pMem->flags = MEM_Str|MEM_Term;
+ zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
+ if( zP4!=pMem->z ){
+ pMem->n = 0;
+ sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
}else{
assert( pMem->z!=0 );
pMem->n = sqlite3Strlen30(pMem->z);
pMem->enc = SQLITE_UTF8;
}
- pMem->type = SQLITE_TEXT;
pMem++;
if( p->explain==1 ){
- if( sqlite3VdbeMemGrow(pMem, 4, 0) ){
+ if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
assert( p->db->mallocFailed );
return SQLITE_ERROR;
}
- pMem->flags = MEM_Dyn|MEM_Str|MEM_Term;
+ pMem->flags = MEM_Str|MEM_Term;
pMem->n = 2;
sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
- pMem->type = SQLITE_TEXT;
pMem->enc = SQLITE_UTF8;
pMem++;
-#ifdef SQLITE_DEBUG
- if( pOp->zComment ){
- pMem->flags = MEM_Str|MEM_Term;
- pMem->z = pOp->zComment;
- pMem->n = sqlite3Strlen30(pMem->z);
- pMem->enc = SQLITE_UTF8;
- pMem->type = SQLITE_TEXT;
- }else
-#endif
- {
- pMem->flags = MEM_Null; /* Comment */
- pMem->type = SQLITE_NULL;
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
+ assert( p->db->mallocFailed );
+ return SQLITE_ERROR;
}
+ pMem->flags = MEM_Str|MEM_Term;
+ pMem->n = displayComment(pOp, zP4, pMem->z, 500);
+ pMem->enc = SQLITE_UTF8;
+#else
+ pMem->flags = MEM_Null; /* Comment */
+#endif
}
p->nResColumn = 8 - 4*(p->explain-1);
+ p->pResultSet = &p->aMem[1];
p->rc = SQLITE_OK;
rc = SQLITE_ROW;
}
@@ -59624,15 +72209,17 @@ SQLITE_PRIVATE int sqlite3VdbeList(
** Print the SQL that was used to generate a VDBE program.
*/
SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe *p){
- int nOp = p->nOp;
- VdbeOp *pOp;
- if( nOp<1 ) return;
- pOp = &p->aOp[0];
- if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
- const char *z = pOp->p4.z;
- while( sqlite3Isspace(*z) ) z++;
- printf("SQL: [%s]\n", z);
+ const char *z = 0;
+ if( p->zSql ){
+ z = p->zSql;
+ }else if( p->nOp>=1 ){
+ const VdbeOp *pOp = &p->aOp[0];
+ if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){
+ z = pOp->p4.z;
+ while( sqlite3Isspace(*z) ) z++;
+ }
}
+ if( z ) printf("SQL: [%s]\n", z);
}
#endif
@@ -59646,7 +72233,7 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){
if( sqlite3IoTrace==0 ) return;
if( nOp<1 ) return;
pOp = &p->aOp[0];
- if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){
+ if( pOp->opcode==OP_Init && pOp->p4.z!=0 ){
int i, j;
char z[1000];
sqlite3_snprintf(sizeof(z), z, "%s", pOp->p4.z);
@@ -59666,43 +72253,46 @@ SQLITE_PRIVATE void sqlite3VdbeIOTraceSql(Vdbe *p){
}
#endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */
-/*
-** Allocate space from a fixed size buffer and return a pointer to
-** that space. If insufficient space is available, return NULL.
-**
-** The pBuf parameter is the initial value of a pointer which will
-** receive the new memory. pBuf is normally NULL. If pBuf is not
-** NULL, it means that memory space has already been allocated and that
-** this routine should not allocate any new memory. When pBuf is not
-** NULL simply return pBuf. Only allocate new memory space when pBuf
-** is NULL.
-**
-** nByte is the number of bytes of space needed.
+/* An instance of this object describes bulk memory available for use
+** by subcomponents of a prepared statement. Space is allocated out
+** of a ReusableSpace object by the allocSpace() routine below.
+*/
+struct ReusableSpace {
+ u8 *pSpace; /* Available memory */
+ int nFree; /* Bytes of available memory */
+ int nNeeded; /* Total bytes that could not be allocated */
+};
+
+/* Try to allocate nByte bytes of 8-byte aligned bulk memory for pBuf
+** from the ReusableSpace object. Return a pointer to the allocated
+** memory on success. If insufficient memory is available in the
+** ReusableSpace object, increase the ReusableSpace.nNeeded
+** value by the amount needed and return NULL.
**
-** *ppFrom points to available space and pEnd points to the end of the
-** available space. When space is allocated, *ppFrom is advanced past
-** the end of the allocated space.
+** If pBuf is not initially NULL, that means that the memory has already
+** been allocated by a prior call to this routine, so just return a copy
+** of pBuf and leave ReusableSpace unchanged.
**
-** *pnByte is a counter of the number of bytes of space that have failed
-** to allocate. If there is insufficient space in *ppFrom to satisfy the
-** request, then increment *pnByte by the amount of the request.
+** This allocator is employed to repurpose unused slots at the end of the
+** opcode array of prepared state for other memory needs of the prepared
+** statement.
*/
static void *allocSpace(
- void *pBuf, /* Where return pointer will be stored */
- int nByte, /* Number of bytes to allocate */
- u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */
- u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */
- int *pnByte /* If allocation cannot be made, increment *pnByte */
-){
- assert( EIGHT_BYTE_ALIGNMENT(*ppFrom) );
- if( pBuf ) return pBuf;
- nByte = ROUND8(nByte);
- if( &(*ppFrom)[nByte] <= pEnd ){
- pBuf = (void*)*ppFrom;
- *ppFrom += nByte;
- }else{
- *pnByte += nByte;
+ struct ReusableSpace *p, /* Bulk memory available for allocation */
+ void *pBuf, /* Pointer to a prior allocation */
+ int nByte /* Bytes of memory needed */
+){
+ assert( EIGHT_BYTE_ALIGNMENT(p->pSpace) );
+ if( pBuf==0 ){
+ nByte = ROUND8(nByte);
+ if( nByte <= p->nFree ){
+ p->nFree -= nByte;
+ pBuf = &p->pSpace[p->nFree];
+ }else{
+ p->nNeeded += nByte;
+ }
}
+ assert( EIGHT_BYTE_ALIGNMENT(pBuf) );
return pBuf;
}
@@ -59715,7 +72305,7 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
int i;
#endif
assert( p!=0 );
- assert( p->magic==VDBE_MAGIC_INIT );
+ assert( p->magic==VDBE_MAGIC_INIT || p->magic==VDBE_MAGIC_RESET );
/* There should be at least one opcode.
*/
@@ -59725,14 +72315,13 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
p->magic = VDBE_MAGIC_RUN;
#ifdef SQLITE_DEBUG
- for(i=1; i<p->nMem; i++){
+ for(i=0; i<p->nMem; i++){
assert( p->aMem[i].db==p->db );
}
#endif
p->pc = -1;
p->rc = SQLITE_OK;
p->errorAction = OE_Abort;
- p->magic = VDBE_MAGIC_RUN;
p->nChange = 0;
p->cacheCtr = 1;
p->minWriteFileFormat = 255;
@@ -59749,13 +72338,13 @@ SQLITE_PRIVATE void sqlite3VdbeRewind(Vdbe *p){
/*
** Prepare a virtual machine for execution for the first time after
** creating the virtual machine. This involves things such
-** as allocating stack space and initializing the program counter.
+** as allocating registers and initializing the program counter.
** After the VDBE has be prepped, it can be executed by one or more
** calls to sqlite3VdbeExec().
**
-** This function may be called exact once on a each virtual machine.
+** This function may be called exactly once on each virtual machine.
** After this routine is called the VM has been "packaged" and is ready
-** to run. After this routine is called, futher calls to
+** to run. After this routine is called, further calls to
** sqlite3VdbeAddOp() functions are prohibited. This routine disconnects
** the Vdbe from the Parse object that helped generate it so that the
** the Vdbe becomes an independent entity and the Parse object can be
@@ -59774,14 +72363,13 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
int nCursor; /* Number of cursors required */
int nArg; /* Number of arguments in subprograms */
int n; /* Loop counter */
- u8 *zCsr; /* Memory available for allocation */
- u8 *zEnd; /* First byte past allocated memory */
- int nByte; /* How much extra memory is needed */
+ struct ReusableSpace x; /* Reusable bulk memory */
assert( p!=0 );
assert( p->nOp>0 );
assert( pParse!=0 );
assert( p->magic==VDBE_MAGIC_INIT );
+ assert( pParse==p->pParse );
db = p->db;
assert( db->mallocFailed==0 );
nVar = pParse->nVar;
@@ -59789,80 +72377,76 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
nCursor = pParse->nTab;
nArg = pParse->nMaxArg;
- /* For each cursor required, also allocate a memory cell. Memory
- ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
- ** the vdbe program. Instead they are used to allocate space for
- ** VdbeCursor/BtCursor structures. The blob of memory associated with
- ** cursor 0 is stored in memory cell nMem. Memory cell (nMem-1)
- ** stores the blob of memory associated with cursor 1, etc.
- **
+ /* Each cursor uses a memory cell. The first cursor (cursor 0) can
+ ** use aMem[0] which is not otherwise used by the VDBE program. Allocate
+ ** space at the end of aMem[] for cursors 1 and greater.
** See also: allocateCursor().
*/
nMem += nCursor;
+ if( nCursor==0 && nMem>0 ) nMem++; /* Space for aMem[0] even if not used */
- /* Allocate space for memory registers, SQL variables, VDBE cursors and
- ** an array to marshal SQL function arguments in.
+ /* Figure out how much reusable memory is available at the end of the
+ ** opcode array. This extra memory will be reallocated for other elements
+ ** of the prepared statement.
*/
- zCsr = (u8*)&p->aOp[p->nOp]; /* Memory avaliable for allocation */
- zEnd = (u8*)&p->aOp[p->nOpAlloc]; /* First byte past end of zCsr[] */
+ n = ROUND8(sizeof(Op)*p->nOp); /* Bytes of opcode memory used */
+ x.pSpace = &((u8*)p->aOp)[n]; /* Unused opcode memory */
+ assert( EIGHT_BYTE_ALIGNMENT(x.pSpace) );
+ x.nFree = ROUNDDOWN8(pParse->szOpAlloc - n); /* Bytes of unused memory */
+ assert( x.nFree>=0 );
+ assert( EIGHT_BYTE_ALIGNMENT(&x.pSpace[x.nFree]) );
resolveP2Values(p, &nArg);
p->usesStmtJournal = (u8)(pParse->isMultiWrite && pParse->mayAbort);
if( pParse->explain && nMem<10 ){
nMem = 10;
}
- memset(zCsr, 0, zEnd-zCsr);
- zCsr += (zCsr - (u8*)0)&7;
- assert( EIGHT_BYTE_ALIGNMENT(zCsr) );
p->expired = 0;
- /* Memory for registers, parameters, cursor, etc, is allocated in two
- ** passes. On the first pass, we try to reuse unused space at the
+ /* Memory for registers, parameters, cursor, etc, is allocated in one or two
+ ** passes. On the first pass, we try to reuse unused memory at the
** end of the opcode array. If we are unable to satisfy all memory
** requirements by reusing the opcode array tail, then the second
- ** pass will fill in the rest using a fresh allocation.
+ ** pass will fill in the remainder using a fresh memory allocation.
**
** This two-pass approach that reuses as much memory as possible from
- ** the leftover space at the end of the opcode array can significantly
+ ** the leftover memory at the end of the opcode array. This can significantly
** reduce the amount of memory held by a prepared statement.
*/
do {
- nByte = 0;
- p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte);
- p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte);
- p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte);
- p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte);
- p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*),
- &zCsr, zEnd, &nByte);
- if( nByte ){
- p->pFree = sqlite3DbMallocZero(db, nByte);
- }
- zCsr = p->pFree;
- zEnd = &zCsr[nByte];
- }while( nByte && !db->mallocFailed );
-
- p->nCursor = (u16)nCursor;
- if( p->aVar ){
+ x.nNeeded = 0;
+ p->aMem = allocSpace(&x, p->aMem, nMem*sizeof(Mem));
+ p->aVar = allocSpace(&x, p->aVar, nVar*sizeof(Mem));
+ p->apArg = allocSpace(&x, p->apArg, nArg*sizeof(Mem*));
+ p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
+#endif
+ if( x.nNeeded==0 ) break;
+ x.pSpace = p->pFree = sqlite3DbMallocRawNN(db, x.nNeeded);
+ x.nFree = x.nNeeded;
+ }while( !db->mallocFailed );
+
+ p->nzVar = pParse->nzVar;
+ p->azVar = pParse->azVar;
+ pParse->nzVar = 0;
+ pParse->azVar = 0;
+ p->explain = pParse->explain;
+ if( db->mallocFailed ){
+ p->nVar = 0;
+ p->nCursor = 0;
+ p->nMem = 0;
+ }else{
+ p->nCursor = nCursor;
p->nVar = (ynVar)nVar;
- for(n=0; n<nVar; n++){
- p->aVar[n].flags = MEM_Null;
- p->aVar[n].db = db;
- }
- }
- if( p->azVar ){
- p->nzVar = pParse->nzVar;
- memcpy(p->azVar, pParse->azVar, p->nzVar*sizeof(p->azVar[0]));
- memset(pParse->azVar, 0, pParse->nzVar*sizeof(pParse->azVar[0]));
- }
- if( p->aMem ){
- p->aMem--; /* aMem[] goes from 1..nMem */
- p->nMem = nMem; /* not from 0..nMem-1 */
- for(n=1; n<=nMem; n++){
- p->aMem[n].flags = MEM_Null;
- p->aMem[n].db = db;
- }
+ initMemArray(p->aVar, nVar, db, MEM_Null);
+ p->nMem = nMem;
+ initMemArray(p->aMem, nMem, db, MEM_Undefined);
+ memset(p->apCsr, 0, nCursor*sizeof(VdbeCursor*));
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ memset(p->anExec, 0, p->nOp*sizeof(i64));
+#endif
}
- p->explain = pParse->explain;
sqlite3VdbeRewind(p);
}
@@ -59874,23 +72458,50 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
if( pCx==0 ){
return;
}
- sqlite3VdbeSorterClose(p->db, pCx);
- if( pCx->pBt ){
- sqlite3BtreeClose(pCx->pBt);
- /* The pCx->pCursor will be close automatically, if it exists, by
- ** the call above. */
- }else if( pCx->pCursor ){
- sqlite3BtreeCloseCursor(pCx->pCursor);
- }
+ assert( pCx->pBt==0 || pCx->eCurType==CURTYPE_BTREE );
+ switch( pCx->eCurType ){
+ case CURTYPE_SORTER: {
+ sqlite3VdbeSorterClose(p->db, pCx);
+ break;
+ }
+ case CURTYPE_BTREE: {
+ if( pCx->pBt ){
+ sqlite3BtreeClose(pCx->pBt);
+ /* The pCx->pCursor will be close automatically, if it exists, by
+ ** the call above. */
+ }else{
+ assert( pCx->uc.pCursor!=0 );
+ sqlite3BtreeCloseCursor(pCx->uc.pCursor);
+ }
+ break;
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( pCx->pVtabCursor ){
- sqlite3_vtab_cursor *pVtabCursor = pCx->pVtabCursor;
- const sqlite3_module *pModule = pCx->pModule;
- p->inVtabMethod = 1;
- pModule->xClose(pVtabCursor);
- p->inVtabMethod = 0;
- }
+ case CURTYPE_VTAB: {
+ sqlite3_vtab_cursor *pVCur = pCx->uc.pVCur;
+ const sqlite3_module *pModule = pVCur->pVtab->pModule;
+ assert( pVCur->pVtab->nRef>0 );
+ pVCur->pVtab->nRef--;
+ pModule->xClose(pVCur);
+ break;
+ }
#endif
+ }
+}
+
+/*
+** Close all cursors in the current frame.
+*/
+static void closeCursorsInFrame(Vdbe *p){
+ if( p->apCsr ){
+ int i;
+ for(i=0; i<p->nCursor; i++){
+ VdbeCursor *pC = p->apCsr[i];
+ if( pC ){
+ sqlite3VdbeFreeCursor(p, pC);
+ p->apCsr[i] = 0;
+ }
+ }
+ }
}
/*
@@ -59900,6 +72511,10 @@ SQLITE_PRIVATE void sqlite3VdbeFreeCursor(Vdbe *p, VdbeCursor *pCx){
*/
SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
Vdbe *v = pFrame->v;
+ closeCursorsInFrame(v);
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ v->anExec = pFrame->anExec;
+#endif
v->aOp = pFrame->aOp;
v->nOp = pFrame->nOp;
v->aMem = pFrame->aMem;
@@ -59908,6 +72523,10 @@ SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){
v->nCursor = pFrame->nCursor;
v->db->lastRowid = pFrame->lastRowid;
v->nChange = pFrame->nChange;
+ v->db->nChange = pFrame->nDbChange;
+ sqlite3VdbeDeleteAuxData(v->db, &v->pAuxData, -1, 0);
+ v->pAuxData = pFrame->pAuxData;
+ pFrame->pAuxData = 0;
return pFrame->pc;
}
@@ -59924,36 +72543,27 @@ static void closeAllCursors(Vdbe *p){
VdbeFrame *pFrame;
for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
sqlite3VdbeFrameRestore(pFrame);
+ p->pFrame = 0;
+ p->nFrame = 0;
}
- p->pFrame = 0;
- p->nFrame = 0;
-
- if( p->apCsr ){
- int i;
- for(i=0; i<p->nCursor; i++){
- VdbeCursor *pC = p->apCsr[i];
- if( pC ){
- sqlite3VdbeFreeCursor(p, pC);
- p->apCsr[i] = 0;
- }
- }
- }
+ assert( p->nFrame==0 );
+ closeCursorsInFrame(p);
if( p->aMem ){
- releaseMemArray(&p->aMem[1], p->nMem);
+ releaseMemArray(p->aMem, p->nMem);
}
while( p->pDelFrame ){
VdbeFrame *pDel = p->pDelFrame;
p->pDelFrame = pDel->pParent;
sqlite3VdbeFrameDelete(pDel);
}
+
+ /* Delete any auxdata allocations made by the VM */
+ if( p->pAuxData ) sqlite3VdbeDeleteAuxData(p->db, &p->pAuxData, -1, 0);
+ assert( p->pAuxData==0 );
}
/*
-** Clean up the VM after execution.
-**
-** This routine will automatically close any cursors, lists, and/or
-** sorters that were left open. It also deletes the values of
-** variables in the aVar[] array.
+** Clean up the VM after a single run.
*/
static void Cleanup(Vdbe *p){
sqlite3 *db = p->db;
@@ -59962,8 +72572,10 @@ static void Cleanup(Vdbe *p){
/* Execute assert() statements to ensure that the Vdbe.apCsr[] and
** Vdbe.aMem[] arrays have already been cleaned up. */
int i;
- for(i=0; i<p->nCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 );
- for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null );
+ if( p->apCsr ) for(i=0; i<p->nCursor; i++) assert( p->apCsr[i]==0 );
+ if( p->aMem ){
+ for(i=0; i<p->nMem; i++) assert( p->aMem[i].flags==MEM_Undefined );
+ }
#endif
sqlite3DbFree(db, p->zErrMsg);
@@ -59986,13 +72598,9 @@ SQLITE_PRIVATE void sqlite3VdbeSetNumCols(Vdbe *p, int nResColumn){
sqlite3DbFree(db, p->aColName);
n = nResColumn*COLNAME_N;
p->nResColumn = (u16)nResColumn;
- p->aColName = pColName = (Mem*)sqlite3DbMallocZero(db, sizeof(Mem)*n );
+ p->aColName = pColName = (Mem*)sqlite3DbMallocRawNN(db, sizeof(Mem)*n );
if( p->aColName==0 ) return;
- while( n-- > 0 ){
- pColName->flags = MEM_Null;
- pColName->db = p->db;
- pColName++;
- }
+ initMemArray(p->aColName, n, p->db, MEM_Null);
}
/*
@@ -60018,7 +72626,7 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName(
assert( var<COLNAME_N );
if( p->db->mallocFailed ){
assert( !zName || xDel!=SQLITE_DYNAMIC );
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
assert( p->aColName!=0 );
pColName = &(p->aColName[idx+var*p->nResColumn]);
@@ -60035,7 +72643,9 @@ SQLITE_PRIVATE int sqlite3VdbeSetColName(
*/
static int vdbeCommit(sqlite3 *db, Vdbe *p){
int i;
- int nTrans = 0; /* Number of databases with an active write-transaction */
+ int nTrans = 0; /* Number of databases with an active write-transaction
+ ** that are candidates for a two-phase commit using a
+ ** master-journal */
int rc = SQLITE_OK;
int needXcommit = 0;
@@ -60052,7 +72662,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
** required, as an xSync() callback may add an attached database
** to the transaction.
*/
- rc = sqlite3VtabSync(db, &p->zErrMsg);
+ rc = sqlite3VtabSync(db, p);
/* This loop determines (a) if the commit hook should be invoked and
** (b) how many database files have open write transactions, not
@@ -60063,9 +72673,29 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( sqlite3BtreeIsInTrans(pBt) ){
+ /* Whether or not a database might need a master journal depends upon
+ ** its journal mode (among other things). This matrix determines which
+ ** journal modes use a master journal and which do not */
+ static const u8 aMJNeeded[] = {
+ /* DELETE */ 1,
+ /* PERSIST */ 1,
+ /* OFF */ 0,
+ /* TRUNCATE */ 1,
+ /* MEMORY */ 0,
+ /* WAL */ 0
+ };
+ Pager *pPager; /* Pager associated with pBt */
needXcommit = 1;
- if( i!=1 ) nTrans++;
- rc = sqlite3PagerExclusiveLock(sqlite3BtreePager(pBt));
+ sqlite3BtreeEnter(pBt);
+ pPager = sqlite3BtreePager(pBt);
+ if( db->aDb[i].safety_level!=PAGER_SYNCHRONOUS_OFF
+ && aMJNeeded[sqlite3PagerGetJournalMode(pPager)]
+ ){
+ assert( i!=1 );
+ nTrans++;
+ }
+ rc = sqlite3PagerExclusiveLock(pPager);
+ sqlite3BtreeLeave(pBt);
}
}
if( rc!=SQLITE_OK ){
@@ -60076,7 +72706,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
if( needXcommit && db->xCommitCallback ){
rc = db->xCommitCallback(db->pCommitArg);
if( rc ){
- return SQLITE_CONSTRAINT;
+ return SQLITE_CONSTRAINT_COMMITHOOK;
}
}
@@ -60117,27 +72747,41 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
/* The complex case - There is a multi-file write-transaction active.
** This requires a master journal file to ensure the transaction is
- ** committed atomicly.
+ ** committed atomically.
*/
#ifndef SQLITE_OMIT_DISKIO
else{
sqlite3_vfs *pVfs = db->pVfs;
- int needSync = 0;
char *zMaster = 0; /* File-name for the master journal */
char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt);
sqlite3_file *pMaster = 0;
i64 offset = 0;
int res;
+ int retryCount = 0;
+ int nMainFile;
/* Select a master journal file name */
+ nMainFile = sqlite3Strlen30(zMainFile);
+ zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz", zMainFile);
+ if( zMaster==0 ) return SQLITE_NOMEM_BKPT;
do {
u32 iRandom;
- sqlite3DbFree(db, zMaster);
- sqlite3_randomness(sizeof(iRandom), &iRandom);
- zMaster = sqlite3MPrintf(db, "%s-mj%08X", zMainFile, iRandom&0x7fffffff);
- if( !zMaster ){
- return SQLITE_NOMEM;
+ if( retryCount ){
+ if( retryCount>100 ){
+ sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
+ sqlite3OsDelete(pVfs, zMaster, 0);
+ break;
+ }else if( retryCount==1 ){
+ sqlite3_log(SQLITE_FULL, "MJ collide: %s", zMaster);
+ }
}
+ retryCount++;
+ sqlite3_randomness(sizeof(iRandom), &iRandom);
+ sqlite3_snprintf(13, &zMaster[nMainFile], "-mj%06X9%02X",
+ (iRandom>>8)&0xffffff, iRandom&0xff);
+ /* The antipenultimate character of the master journal name must
+ ** be "9" to avoid name collisions when using 8+3 filenames. */
+ assert( zMaster[sqlite3Strlen30(zMaster)-3]=='9' );
sqlite3FileSuffix3(zMainFile, zMaster);
rc = sqlite3OsAccess(pVfs, zMaster, SQLITE_ACCESS_EXISTS, &res);
}while( rc==SQLITE_OK && res );
@@ -60167,9 +72811,6 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
continue; /* Ignore TEMP and :memory: databases */
}
assert( zFile[0]!=0 );
- if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){
- needSync = 1;
- }
rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset);
offset += sqlite3Strlen30(zFile)+1;
if( rc!=SQLITE_OK ){
@@ -60184,8 +72825,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
/* Sync the master journal file. If the IOCAP_SEQUENTIAL device
** flag is set this is not required.
*/
- if( needSync
- && 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
+ if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
&& SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))
){
sqlite3OsCloseFree(pMaster);
@@ -60254,7 +72894,7 @@ static int vdbeCommit(sqlite3 *db, Vdbe *p){
}
/*
-** This routine checks that the sqlite3.activeVdbeCnt count variable
+** This routine checks that the sqlite3.nVdbeActive count variable
** matches the number of vdbe's in the list sqlite3.pVdbe that are
** currently active. An assertion fails if the two counts do not match.
** This is an internal self-check only - it is not an essential processing
@@ -60267,53 +72907,30 @@ static void checkActiveVdbeCnt(sqlite3 *db){
Vdbe *p;
int cnt = 0;
int nWrite = 0;
+ int nRead = 0;
p = db->pVdbe;
while( p ){
- if( p->magic==VDBE_MAGIC_RUN && p->pc>=0 ){
+ if( sqlite3_stmt_busy((sqlite3_stmt*)p) ){
cnt++;
if( p->readOnly==0 ) nWrite++;
+ if( p->bIsReader ) nRead++;
}
p = p->pNext;
}
- assert( cnt==db->activeVdbeCnt );
- assert( nWrite==db->writeVdbeCnt );
+ assert( cnt==db->nVdbeActive );
+ assert( nWrite==db->nVdbeWrite );
+ assert( nRead==db->nVdbeRead );
}
#else
#define checkActiveVdbeCnt(x)
#endif
/*
-** For every Btree that in database connection db which
-** has been modified, "trip" or invalidate each cursor in
-** that Btree might have been modified so that the cursor
-** can never be used again. This happens when a rollback
-*** occurs. We have to trip all the other cursors, even
-** cursor from other VMs in different database connections,
-** so that none of them try to use the data at which they
-** were pointing and which now may have been changed due
-** to the rollback.
-**
-** Remember that a rollback can delete tables complete and
-** reorder rootpages. So it is not sufficient just to save
-** the state of the cursor. We have to invalidate the cursor
-** so that it is never used again.
-*/
-static void invalidateCursorsOnModifiedBtrees(sqlite3 *db){
- int i;
- for(i=0; i<db->nDb; i++){
- Btree *p = db->aDb[i].pBt;
- if( p && sqlite3BtreeIsInTrans(p) ){
- sqlite3BtreeTripAllCursors(p, SQLITE_ABORT);
- }
- }
-}
-
-/*
** If the Vdbe passed as the first argument opened a statement-transaction,
** close it now. Argument eOp must be either SAVEPOINT_ROLLBACK or
** SAVEPOINT_RELEASE. If it is SAVEPOINT_ROLLBACK, then the statement
** transaction is rolled back. If eOp is SAVEPOINT_RELEASE, then the
-** statement transaction is commtted.
+** statement transaction is committed.
**
** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
** Otherwise SQLITE_OK.
@@ -60324,7 +72941,7 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
/* If p->iStatement is greater than zero, then this Vdbe opened a
** statement transaction that should be closed here. The only exception
- ** is that an IO error may have occured, causing an emergency rollback.
+ ** is that an IO error may have occurred, causing an emergency rollback.
** In this case (db->nStatement==0), and there is nothing to do.
*/
if( db->nStatement && p->iStatement ){
@@ -60367,6 +72984,7 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
** the statement transaction was opened. */
if( eOp==SAVEPOINT_ROLLBACK ){
db->nDeferredCons = p->nStmtDefCons;
+ db->nDeferredImmCons = p->nStmtDefImmCons;
}
}
return rc;
@@ -60379,16 +72997,18 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK.
**
** If there are outstanding FK violations and this function returns
-** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT and write
-** an error message to it. Then return SQLITE_ERROR.
+** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
+** and write an error message to it. Then return SQLITE_ERROR.
*/
#ifndef SQLITE_OMIT_FOREIGN_KEY
SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){
sqlite3 *db = p->db;
- if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){
- p->rc = SQLITE_CONSTRAINT;
+ if( (deferred && (db->nDeferredCons+db->nDeferredImmCons)>0)
+ || (!deferred && p->nFkConstraint>0)
+ ){
+ p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
- sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed");
+ sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
return SQLITE_ERROR;
}
return SQLITE_OK;
@@ -60428,8 +73048,8 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
** one, or the complete transaction if there is no statement transaction.
*/
- if( p->db->mallocFailed ){
- p->rc = SQLITE_NOMEM;
+ if( db->mallocFailed ){
+ p->rc = SQLITE_NOMEM_BKPT;
}
closeAllCursors(p);
if( p->magic!=VDBE_MAGIC_RUN ){
@@ -60437,8 +73057,9 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}
checkActiveVdbeCnt(db);
- /* No commit or rollback needed if the program never started */
- if( p->pc>=0 ){
+ /* No commit or rollback needed if the program never started or if the
+ ** SQL statement does not read or write a database file. */
+ if( p->pc>=0 && p->bIsReader ){
int mrc; /* Primary error code from p->rc */
int eStatementOp = 0;
int isSpecialError; /* Set to true if a 'special' error */
@@ -60448,7 +73069,6 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* Check for one of the special errors */
mrc = p->rc & 0xff;
- assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */
isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR
|| mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL;
if( isSpecialError ){
@@ -60459,7 +73079,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
**
** Even if the statement is read-only, it is important to perform
** a statement or transaction rollback operation. If the error
- ** occured while writing to the journal, sub-journal or database
+ ** occurred while writing to the journal, sub-journal or database
** file as part of an effort to free up cache space (see function
** pagerStress() in pager.c), the rollback is required to restore
** the pager to a consistent state.
@@ -60471,10 +73091,10 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* We are forced to roll back the active transaction. Before doing
** so, abort any other statements this handle currently has active.
*/
- invalidateCursorsOnModifiedBtrees(db);
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
+ p->nChange = 0;
}
}
}
@@ -60492,7 +73112,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
*/
if( !sqlite3VtabInSync(db)
&& db->autoCommit
- && db->writeVdbeCnt==(p->readOnly==0)
+ && db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
rc = sqlite3VdbeCheckFk(p, 1);
@@ -60501,7 +73121,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3VdbeLeave(p);
return SQLITE_ERROR;
}
- rc = SQLITE_CONSTRAINT;
+ rc = SQLITE_CONSTRAINT_FOREIGNKEY;
}else{
/* The auto-commit flag is true, the vdbe program was successful
** or hit an 'OR FAIL' constraint and there are no deferred foreign
@@ -60514,13 +73134,17 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
return SQLITE_BUSY;
}else if( rc!=SQLITE_OK ){
p->rc = rc;
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_OK);
+ p->nChange = 0;
}else{
db->nDeferredCons = 0;
+ db->nDeferredImmCons = 0;
+ db->flags &= ~SQLITE_DeferFKs;
sqlite3CommitInternalChanges(db);
}
}else{
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_OK);
+ p->nChange = 0;
}
db->nStatement = 0;
}else if( eStatementOp==0 ){
@@ -60529,10 +73153,10 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}else if( p->errorAction==OE_Abort ){
eStatementOp = SAVEPOINT_ROLLBACK;
}else{
- invalidateCursorsOnModifiedBtrees(db);
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
+ p->nChange = 0;
}
}
@@ -60545,15 +73169,15 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
if( eStatementOp ){
rc = sqlite3VdbeCloseStatement(p, eStatementOp);
if( rc ){
- if( p->rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ){
+ if( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT ){
p->rc = rc;
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}
- invalidateCursorsOnModifiedBtrees(db);
- sqlite3RollbackAll(db);
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
sqlite3CloseSavepoints(db);
db->autoCommit = 1;
+ p->nChange = 0;
}
}
@@ -60568,12 +73192,6 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
}
p->nChange = 0;
}
-
- /* Rollback or commit any schema changes that occurred. */
- if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){
- sqlite3ResetInternalSchema(db, -1);
- db->flags = (db->flags | SQLITE_InternChanges);
- }
/* Release the locks */
sqlite3VdbeLeave(p);
@@ -60581,16 +73199,17 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
/* We have successfully halted and closed the VM. Record this fact. */
if( p->pc>=0 ){
- db->activeVdbeCnt--;
- if( !p->readOnly ){
- db->writeVdbeCnt--;
- }
- assert( db->activeVdbeCnt>=db->writeVdbeCnt );
+ db->nVdbeActive--;
+ if( !p->readOnly ) db->nVdbeWrite--;
+ if( p->bIsReader ) db->nVdbeRead--;
+ assert( db->nVdbeActive>=db->nVdbeRead );
+ assert( db->nVdbeRead>=db->nVdbeWrite );
+ assert( db->nVdbeWrite>=0 );
}
p->magic = VDBE_MAGIC_HALT;
checkActiveVdbeCnt(db);
- if( p->db->mallocFailed ){
- p->rc = SQLITE_NOMEM;
+ if( db->mallocFailed ){
+ p->rc = SQLITE_NOMEM_BKPT;
}
/* If the auto-commit flag is set to true, then any locks that were held
@@ -60601,7 +73220,7 @@ SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe *p){
sqlite3ConnectionUnlocked(db);
}
- assert( db->activeVdbeCnt>0 || db->autoCommit==0 || db->nStatement==0 );
+ assert( db->nVdbeActive>0 || db->autoCommit==0 || db->nStatement==0 );
return (p->rc==SQLITE_BUSY ? SQLITE_BUSY : SQLITE_OK);
}
@@ -60615,6 +73234,52 @@ SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe *p){
}
/*
+** Copy the error code and error message belonging to the VDBE passed
+** as the first argument to its database handle (so that they will be
+** returned by calls to sqlite3_errcode() and sqlite3_errmsg()).
+**
+** This function does not clear the VDBE error code or message, just
+** copies them to the database handle.
+*/
+SQLITE_PRIVATE int sqlite3VdbeTransferError(Vdbe *p){
+ sqlite3 *db = p->db;
+ int rc = p->rc;
+ if( p->zErrMsg ){
+ db->bBenignMalloc++;
+ sqlite3BeginBenignMalloc();
+ if( db->pErr==0 ) db->pErr = sqlite3ValueNew(db);
+ sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
+ sqlite3EndBenignMalloc();
+ db->bBenignMalloc--;
+ db->errCode = rc;
+ }else{
+ sqlite3Error(db, rc);
+ }
+ return rc;
+}
+
+#ifdef SQLITE_ENABLE_SQLLOG
+/*
+** If an SQLITE_CONFIG_SQLLOG hook is registered and the VM has been run,
+** invoke it.
+*/
+static void vdbeInvokeSqllog(Vdbe *v){
+ if( sqlite3GlobalConfig.xSqllog && v->rc==SQLITE_OK && v->zSql && v->pc>=0 ){
+ char *zExpanded = sqlite3VdbeExpandSql(v, v->zSql);
+ assert( v->db->init.busy==0 );
+ if( zExpanded ){
+ sqlite3GlobalConfig.xSqllog(
+ sqlite3GlobalConfig.pSqllogArg, v->db, zExpanded, 1
+ );
+ sqlite3DbFree(v->db, zExpanded);
+ }
+ }
+}
+#else
+# define vdbeInvokeSqllog(x)
+#endif
+
+/*
** Clean up a VDBE after execution but do not delete the VDBE just yet.
** Write any error messages into *pzErrMsg. Return the result code.
**
@@ -60641,26 +73306,17 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
** instructions yet, leave the main database error information unchanged.
*/
if( p->pc>=0 ){
- if( p->zErrMsg ){
- sqlite3BeginBenignMalloc();
- sqlite3ValueSetStr(db->pErr,-1,p->zErrMsg,SQLITE_UTF8,SQLITE_TRANSIENT);
- sqlite3EndBenignMalloc();
- db->errCode = p->rc;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = 0;
- }else if( p->rc ){
- sqlite3Error(db, p->rc, 0);
- }else{
- sqlite3Error(db, SQLITE_OK, 0);
- }
+ vdbeInvokeSqllog(p);
+ sqlite3VdbeTransferError(p);
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = 0;
if( p->runOnlyOnce ) p->expired = 1;
}else if( p->rc && p->expired ){
/* The expired flag was set on the VDBE before the first call
** to sqlite3_step(). For consistency (since sqlite3_step() was
** called), set the database error in this case as well.
*/
- sqlite3Error(db, p->rc, 0);
- sqlite3ValueSetStr(db->pErr, -1, p->zErrMsg, SQLITE_UTF8, SQLITE_TRANSIENT);
+ sqlite3ErrorWithMsg(db, p->rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
sqlite3DbFree(db, p->zErrMsg);
p->zErrMsg = 0;
}
@@ -60681,19 +73337,32 @@ SQLITE_PRIVATE int sqlite3VdbeReset(Vdbe *p){
fprintf(out, "%02x", p->aOp[i].opcode);
}
fprintf(out, "\n");
+ if( p->zSql ){
+ char c, pc = 0;
+ fprintf(out, "-- ");
+ for(i=0; (c = p->zSql[i])!=0; i++){
+ if( pc=='\n' ) fprintf(out, "-- ");
+ putc(c, out);
+ pc = c;
+ }
+ if( pc!='\n' ) fprintf(out, "\n");
+ }
for(i=0; i<p->nOp; i++){
- fprintf(out, "%6d %10lld %8lld ",
+ char zHdr[100];
+ sqlite3_snprintf(sizeof(zHdr), zHdr, "%6u %12llu %8llu ",
p->aOp[i].cnt,
p->aOp[i].cycles,
p->aOp[i].cnt>0 ? p->aOp[i].cycles/p->aOp[i].cnt : 0
);
+ fprintf(out, "%s", zHdr);
sqlite3VdbePrintOp(out, i, &p->aOp[i]);
}
fclose(out);
}
}
#endif
- p->magic = VDBE_MAGIC_INIT;
+ p->iCurrentTime = 0;
+ p->magic = VDBE_MAGIC_RESET;
return p->rc & db->errMask;
}
@@ -60712,48 +73381,72 @@ SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe *p){
}
/*
-** Call the destructor for each auxdata entry in pVdbeFunc for which
-** the corresponding bit in mask is clear. Auxdata entries beyond 31
-** are always destroyed. To destroy all auxdata entries, call this
-** routine with mask==0.
+** If parameter iOp is less than zero, then invoke the destructor for
+** all auxiliary data pointers currently cached by the VM passed as
+** the first argument.
+**
+** Or, if iOp is greater than or equal to zero, then the destructor is
+** only invoked for those auxiliary data pointers created by the user
+** function invoked by the OP_Function opcode at instruction iOp of
+** VM pVdbe, and only then if:
+**
+** * the associated function parameter is the 32nd or later (counting
+** from left to right), or
+**
+** * the corresponding bit in argument mask is clear (where the first
+** function parameter corresponds to bit 0 etc.).
*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){
- int i;
- for(i=0; i<pVdbeFunc->nAux; i++){
- struct AuxData *pAux = &pVdbeFunc->apAux[i];
- if( (i>31 || !(mask&(((u32)1)<<i))) && pAux->pAux ){
+SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3 *db, AuxData **pp, int iOp, int mask){
+ while( *pp ){
+ AuxData *pAux = *pp;
+ if( (iOp<0)
+ || (pAux->iOp==iOp && (pAux->iArg>31 || !(mask & MASKBIT32(pAux->iArg))))
+ ){
+ testcase( pAux->iArg==31 );
if( pAux->xDelete ){
pAux->xDelete(pAux->pAux);
}
- pAux->pAux = 0;
+ *pp = pAux->pNext;
+ sqlite3DbFree(db, pAux);
+ }else{
+ pp= &pAux->pNext;
}
}
}
/*
-** Free all memory associated with the Vdbe passed as the second argument.
+** Free all memory associated with the Vdbe passed as the second argument,
+** except for object itself, which is preserved.
+**
** The difference between this function and sqlite3VdbeDelete() is that
** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with
-** the database connection.
+** the database connection and frees the object itself.
*/
-SQLITE_PRIVATE void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
+SQLITE_PRIVATE void sqlite3VdbeClearObject(sqlite3 *db, Vdbe *p){
SubProgram *pSub, *pNext;
int i;
assert( p->db==0 || p->db==db );
- releaseMemArray(p->aVar, p->nVar);
releaseMemArray(p->aColName, p->nResColumn*COLNAME_N);
for(pSub=p->pProgram; pSub; pSub=pNext){
pNext = pSub->pNext;
vdbeFreeOpArray(db, pSub->aOp, pSub->nOp);
sqlite3DbFree(db, pSub);
}
- for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
+ if( p->magic!=VDBE_MAGIC_INIT ){
+ releaseMemArray(p->aVar, p->nVar);
+ for(i=p->nzVar-1; i>=0; i--) sqlite3DbFree(db, p->azVar[i]);
+ sqlite3DbFree(db, p->azVar);
+ sqlite3DbFree(db, p->pFree);
+ }
vdbeFreeOpArray(db, p->aOp, p->nOp);
- sqlite3DbFree(db, p->aLabel);
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
- sqlite3DbFree(db, p->pFree);
- sqlite3DbFree(db, p);
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ for(i=0; i<p->nScan; i++){
+ sqlite3DbFree(db, p->aScan[i].zName);
+ }
+ sqlite3DbFree(db, p->aScan);
+#endif
}
/*
@@ -60764,6 +73457,8 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
if( NEVER(p==0) ) return;
db = p->db;
+ assert( sqlite3_mutex_held(db->mutex) );
+ sqlite3VdbeClearObject(db, p);
if( p->pPrev ){
p->pPrev->pNext = p->pNext;
}else{
@@ -60775,7 +73470,61 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
}
p->magic = VDBE_MAGIC_DEAD;
p->db = 0;
- sqlite3VdbeDeleteObject(db, p);
+ sqlite3DbFree(db, p);
+}
+
+/*
+** The cursor "p" has a pending seek operation that has not yet been
+** carried out. Seek the cursor now. If an error occurs, return
+** the appropriate error code.
+*/
+static int SQLITE_NOINLINE handleDeferredMoveto(VdbeCursor *p){
+ int res, rc;
+#ifdef SQLITE_TEST
+ extern int sqlite3_search_count;
+#endif
+ assert( p->deferredMoveto );
+ assert( p->isTable );
+ assert( p->eCurType==CURTYPE_BTREE );
+ rc = sqlite3BtreeMovetoUnpacked(p->uc.pCursor, 0, p->movetoTarget, 0, &res);
+ if( rc ) return rc;
+ if( res!=0 ) return SQLITE_CORRUPT_BKPT;
+#ifdef SQLITE_TEST
+ sqlite3_search_count++;
+#endif
+ p->deferredMoveto = 0;
+ p->cacheStatus = CACHE_STALE;
+ return SQLITE_OK;
+}
+
+/*
+** Something has moved cursor "p" out of place. Maybe the row it was
+** pointed to was deleted out from under it. Or maybe the btree was
+** rebalanced. Whatever the cause, try to restore "p" to the place it
+** is supposed to be pointing. If the row was deleted out from under the
+** cursor, set the cursor to point to a NULL row.
+*/
+static int SQLITE_NOINLINE handleMovedCursor(VdbeCursor *p){
+ int isDifferentRow, rc;
+ assert( p->eCurType==CURTYPE_BTREE );
+ assert( p->uc.pCursor!=0 );
+ assert( sqlite3BtreeCursorHasMoved(p->uc.pCursor) );
+ rc = sqlite3BtreeCursorRestore(p->uc.pCursor, &isDifferentRow);
+ p->cacheStatus = CACHE_STALE;
+ if( isDifferentRow ) p->nullRow = 1;
+ return rc;
+}
+
+/*
+** Check to ensure that the cursor is valid. Restore the cursor
+** if need be. Return any I/O error from the restore operation.
+*/
+SQLITE_PRIVATE int sqlite3VdbeCursorRestore(VdbeCursor *p){
+ assert( p->eCurType==CURTYPE_BTREE );
+ if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
+ return handleMovedCursor(p);
+ }
+ return SQLITE_OK;
}
/*
@@ -60791,30 +73540,20 @@ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
** If the cursor is already pointing to the correct row and that row has
** not been deleted out from under the cursor, then this routine is a no-op.
*/
-SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
- if( p->deferredMoveto ){
- int res, rc;
-#ifdef SQLITE_TEST
- extern int sqlite3_search_count;
-#endif
- assert( p->isTable );
- rc = sqlite3BtreeMovetoUnpacked(p->pCursor, 0, p->movetoTarget, 0, &res);
- if( rc ) return rc;
- p->lastRowid = p->movetoTarget;
- if( res!=0 ) return SQLITE_CORRUPT_BKPT;
- p->rowidIsValid = 1;
-#ifdef SQLITE_TEST
- sqlite3_search_count++;
-#endif
- p->deferredMoveto = 0;
- p->cacheStatus = CACHE_STALE;
- }else if( ALWAYS(p->pCursor) ){
- int hasMoved;
- int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved);
- if( rc ) return rc;
- if( hasMoved ){
- p->cacheStatus = CACHE_STALE;
- p->nullRow = 1;
+SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor **pp, int *piCol){
+ VdbeCursor *p = *pp;
+ if( p->eCurType==CURTYPE_BTREE ){
+ if( p->deferredMoveto ){
+ int iMap;
+ if( p->aAltMap && (iMap = p->aAltMap[1+*piCol])>0 ){
+ *pp = p->pAltCursor;
+ *piCol = iMap - 1;
+ return SQLITE_OK;
+ }
+ return handleDeferredMoveto(p);
+ }
+ if( sqlite3BtreeCursorHasMoved(p->uc.pCursor) ){
+ return handleMovedCursor(p);
}
}
return SQLITE_OK;
@@ -60838,7 +73577,7 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
** the blob of data that it corresponds to. In a table record, all serial
** types are stored at the start of the record, and the blobs of data at
** the end. Hence these functions allow the caller to handle the
-** serial-type and data blob seperately.
+** serial-type and data blob separately.
**
** The following table describes the various storage classes for data:
**
@@ -60865,11 +73604,13 @@ SQLITE_PRIVATE int sqlite3VdbeCursorMoveto(VdbeCursor *p){
/*
** Return the serial-type for the value stored in pMem.
*/
-SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
+SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format, u32 *pLen){
int flags = pMem->flags;
- int n;
+ u32 n;
+ assert( pLen!=0 );
if( flags&MEM_Null ){
+ *pLen = 0;
return 0;
}
if( flags&MEM_Int ){
@@ -60877,46 +73618,77 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialType(Mem *pMem, int file_format){
# define MAX_6BYTE ((((i64)0x00008000)<<32)-1)
i64 i = pMem->u.i;
u64 u;
- if( file_format>=4 && (i&1)==i ){
- return 8+(u32)i;
- }
if( i<0 ){
- if( i<(-MAX_6BYTE) ) return 6;
- /* Previous test prevents: u = -(-9223372036854775808) */
- u = -i;
+ u = ~i;
}else{
u = i;
}
- if( u<=127 ) return 1;
- if( u<=32767 ) return 2;
- if( u<=8388607 ) return 3;
- if( u<=2147483647 ) return 4;
- if( u<=MAX_6BYTE ) return 5;
+ if( u<=127 ){
+ if( (i&1)==i && file_format>=4 ){
+ *pLen = 0;
+ return 8+(u32)u;
+ }else{
+ *pLen = 1;
+ return 1;
+ }
+ }
+ if( u<=32767 ){ *pLen = 2; return 2; }
+ if( u<=8388607 ){ *pLen = 3; return 3; }
+ if( u<=2147483647 ){ *pLen = 4; return 4; }
+ if( u<=MAX_6BYTE ){ *pLen = 6; return 5; }
+ *pLen = 8;
return 6;
}
if( flags&MEM_Real ){
+ *pLen = 8;
return 7;
}
assert( pMem->db->mallocFailed || flags&(MEM_Str|MEM_Blob) );
- n = pMem->n;
+ assert( pMem->n>=0 );
+ n = (u32)pMem->n;
if( flags & MEM_Zero ){
n += pMem->u.nZero;
}
- assert( n>=0 );
+ *pLen = n;
return ((n*2) + 12 + ((flags&MEM_Str)!=0));
}
/*
+** The sizes for serial types less than 128
+*/
+static const u8 sqlite3SmallTypeSizes[] = {
+ /* 0 1 2 3 4 5 6 7 8 9 */
+/* 0 */ 0, 1, 2, 3, 4, 6, 8, 8, 0, 0,
+/* 10 */ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3,
+/* 20 */ 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+/* 30 */ 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
+/* 40 */ 14, 14, 15, 15, 16, 16, 17, 17, 18, 18,
+/* 50 */ 19, 19, 20, 20, 21, 21, 22, 22, 23, 23,
+/* 60 */ 24, 24, 25, 25, 26, 26, 27, 27, 28, 28,
+/* 70 */ 29, 29, 30, 30, 31, 31, 32, 32, 33, 33,
+/* 80 */ 34, 34, 35, 35, 36, 36, 37, 37, 38, 38,
+/* 90 */ 39, 39, 40, 40, 41, 41, 42, 42, 43, 43,
+/* 100 */ 44, 44, 45, 45, 46, 46, 47, 47, 48, 48,
+/* 110 */ 49, 49, 50, 50, 51, 51, 52, 52, 53, 53,
+/* 120 */ 54, 54, 55, 55, 56, 56, 57, 57
+};
+
+/*
** Return the length of the data corresponding to the supplied serial-type.
*/
SQLITE_PRIVATE u32 sqlite3VdbeSerialTypeLen(u32 serial_type){
- if( serial_type>=12 ){
+ if( serial_type>=128 ){
return (serial_type-12)/2;
}else{
- static const u8 aSize[] = { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0 };
- return aSize[serial_type];
+ assert( serial_type<12
+ || sqlite3SmallTypeSizes[serial_type]==(serial_type - 12)/2 );
+ return sqlite3SmallTypeSizes[serial_type];
}
}
+SQLITE_PRIVATE u8 sqlite3VdbeOneByteSerialTypeLen(u8 serial_type){
+ assert( serial_type<128 );
+ return sqlite3SmallTypeSizes[serial_type];
+}
/*
** If we are on an architecture with mixed-endian floating
@@ -60976,21 +73748,15 @@ static u64 floatSwap(u64 in){
** buf. It is assumed that the caller has allocated sufficient space.
** Return the number of bytes written.
**
-** nBuf is the amount of space left in buf[]. nBuf must always be
-** large enough to hold the entire field. Except, if the field is
-** a blob with a zero-filled tail, then buf[] might be just the right
-** size to hold everything except for the zero-filled tail. If buf[]
-** is only big enough to hold the non-zero prefix, then only write that
-** prefix into buf[]. But if buf[] is large enough to hold both the
-** prefix and the tail then write the prefix and set the tail to all
-** zeros.
+** nBuf is the amount of space left in buf[]. The caller is responsible
+** for allocating enough space to buf[] to hold the entire field, exclusive
+** of the pMem->u.nZero bytes for a MEM_Zero value.
**
** Return the number of bytes actually written into buf[]. The number
** of bytes in the zero-filled tail is included in the return value only
** if those bytes were zeroed in buf[].
*/
-SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_format){
- u32 serial_type = sqlite3VdbeSerialType(pMem, file_format);
+SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, Mem *pMem, u32 serial_type){
u32 len;
/* Integer and Real */
@@ -60998,18 +73764,18 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f
u64 v;
u32 i;
if( serial_type==7 ){
- assert( sizeof(v)==sizeof(pMem->r) );
- memcpy(&v, &pMem->r, sizeof(v));
+ assert( sizeof(v)==sizeof(pMem->u.r) );
+ memcpy(&v, &pMem->u.r, sizeof(v));
swapMixedEndianFloat(v);
}else{
v = pMem->u.i;
}
- len = i = sqlite3VdbeSerialTypeLen(serial_type);
- assert( len<=(u32)nBuf );
- while( i-- ){
- buf[i] = (u8)(v&0xFF);
+ len = i = sqlite3SmallTypeSizes[serial_type];
+ assert( i>0 );
+ do{
+ buf[--i] = (u8)(v&0xFF);
v >>= 8;
- }
+ }while( i );
return len;
}
@@ -61017,17 +73783,8 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f
if( serial_type>=12 ){
assert( pMem->n + ((pMem->flags & MEM_Zero)?pMem->u.nZero:0)
== (int)sqlite3VdbeSerialTypeLen(serial_type) );
- assert( pMem->n<=nBuf );
len = pMem->n;
- memcpy(buf, pMem->z, len);
- if( pMem->flags & MEM_Zero ){
- len += pMem->u.nZero;
- assert( nBuf>=0 );
- if( len > (u32)nBuf ){
- len = (u32)nBuf;
- }
- memset(&buf[pMem->n], 0, len-pMem->n);
- }
+ if( len>0 ) memcpy(buf, pMem->z, len);
return len;
}
@@ -61035,10 +73792,60 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialPut(u8 *buf, int nBuf, Mem *pMem, int file_f
return 0;
}
+/* Input "x" is a sequence of unsigned characters that represent a
+** big-endian integer. Return the equivalent native integer
+*/
+#define ONE_BYTE_INT(x) ((i8)(x)[0])
+#define TWO_BYTE_INT(x) (256*(i8)((x)[0])|(x)[1])
+#define THREE_BYTE_INT(x) (65536*(i8)((x)[0])|((x)[1]<<8)|(x)[2])
+#define FOUR_BYTE_UINT(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+#define FOUR_BYTE_INT(x) (16777216*(i8)((x)[0])|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+
/*
** Deserialize the data blob pointed to by buf as serial type serial_type
** and store the result in pMem. Return the number of bytes read.
+**
+** This function is implemented as two separate routines for performance.
+** The few cases that require local variables are broken out into a separate
+** routine so that in most cases the overhead of moving the stack pointer
+** is avoided.
*/
+static u32 SQLITE_NOINLINE serialGet(
+ const unsigned char *buf, /* Buffer to deserialize from */
+ u32 serial_type, /* Serial type to deserialize */
+ Mem *pMem /* Memory cell to write value into */
+){
+ u64 x = FOUR_BYTE_UINT(buf);
+ u32 y = FOUR_BYTE_UINT(buf+4);
+ x = (x<<32) + y;
+ if( serial_type==6 ){
+ /* EVIDENCE-OF: R-29851-52272 Value is a big-endian 64-bit
+ ** twos-complement integer. */
+ pMem->u.i = *(i64*)&x;
+ pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
+ }else{
+ /* EVIDENCE-OF: R-57343-49114 Value is a big-endian IEEE 754-2008 64-bit
+ ** floating point number. */
+#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT)
+ /* Verify that integers and floating point values use the same
+ ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
+ ** defined that 64-bit floating point values really are mixed
+ ** endian.
+ */
+ static const u64 t1 = ((u64)0x3ff00000)<<32;
+ static const double r1 = 1.0;
+ u64 t2 = t1;
+ swapMixedEndianFloat(t2);
+ assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 );
+#endif
+ assert( sizeof(x)==8 && sizeof(pMem->u.r)==8 );
+ swapMixedEndianFloat(x);
+ memcpy(&pMem->u.r, &x, sizeof(x));
+ pMem->flags = sqlite3IsNaN(pMem->u.r) ? MEM_Null : MEM_Real;
+ }
+ return 8;
+}
SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
const unsigned char *buf, /* Buffer to deserialize from */
u32 serial_type, /* Serial type to deserialize */
@@ -61047,91 +73854,83 @@ SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(
switch( serial_type ){
case 10: /* Reserved for future use */
case 11: /* Reserved for future use */
- case 0: { /* NULL */
+ case 0: { /* Null */
+ /* EVIDENCE-OF: R-24078-09375 Value is a NULL. */
pMem->flags = MEM_Null;
break;
}
- case 1: { /* 1-byte signed integer */
- pMem->u.i = (signed char)buf[0];
+ case 1: {
+ /* EVIDENCE-OF: R-44885-25196 Value is an 8-bit twos-complement
+ ** integer. */
+ pMem->u.i = ONE_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 1;
}
case 2: { /* 2-byte signed integer */
- pMem->u.i = (((signed char)buf[0])<<8) | buf[1];
+ /* EVIDENCE-OF: R-49794-35026 Value is a big-endian 16-bit
+ ** twos-complement integer. */
+ pMem->u.i = TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 2;
}
case 3: { /* 3-byte signed integer */
- pMem->u.i = (((signed char)buf[0])<<16) | (buf[1]<<8) | buf[2];
+ /* EVIDENCE-OF: R-37839-54301 Value is a big-endian 24-bit
+ ** twos-complement integer. */
+ pMem->u.i = THREE_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 3;
}
case 4: { /* 4-byte signed integer */
- pMem->u.i = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
+ /* EVIDENCE-OF: R-01849-26079 Value is a big-endian 32-bit
+ ** twos-complement integer. */
+ pMem->u.i = FOUR_BYTE_INT(buf);
+#ifdef __HP_cc
+ /* Work around a sign-extension bug in the HP compiler for HP/UX */
+ if( buf[0]&0x80 ) pMem->u.i |= 0xffffffff80000000LL;
+#endif
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 4;
}
case 5: { /* 6-byte signed integer */
- u64 x = (((signed char)buf[0])<<8) | buf[1];
- u32 y = (buf[2]<<24) | (buf[3]<<16) | (buf[4]<<8) | buf[5];
- x = (x<<32) | y;
- pMem->u.i = *(i64*)&x;
+ /* EVIDENCE-OF: R-50385-09674 Value is a big-endian 48-bit
+ ** twos-complement integer. */
+ pMem->u.i = FOUR_BYTE_UINT(buf+2) + (((i64)1)<<32)*TWO_BYTE_INT(buf);
pMem->flags = MEM_Int;
+ testcase( pMem->u.i<0 );
return 6;
}
case 6: /* 8-byte signed integer */
case 7: { /* IEEE floating point */
- u64 x;
- u32 y;
-#if !defined(NDEBUG) && !defined(SQLITE_OMIT_FLOATING_POINT)
- /* Verify that integers and floating point values use the same
- ** byte order. Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
- ** defined that 64-bit floating point values really are mixed
- ** endian.
- */
- static const u64 t1 = ((u64)0x3ff00000)<<32;
- static const double r1 = 1.0;
- u64 t2 = t1;
- swapMixedEndianFloat(t2);
- assert( sizeof(r1)==sizeof(t2) && memcmp(&r1, &t2, sizeof(r1))==0 );
-#endif
-
- x = (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];
- y = (buf[4]<<24) | (buf[5]<<16) | (buf[6]<<8) | buf[7];
- x = (x<<32) | y;
- if( serial_type==6 ){
- pMem->u.i = *(i64*)&x;
- pMem->flags = MEM_Int;
- }else{
- assert( sizeof(x)==8 && sizeof(pMem->r)==8 );
- swapMixedEndianFloat(x);
- memcpy(&pMem->r, &x, sizeof(x));
- pMem->flags = sqlite3IsNaN(pMem->r) ? MEM_Null : MEM_Real;
- }
- return 8;
+ /* These use local variables, so do them in a separate routine
+ ** to avoid having to move the frame pointer in the common case */
+ return serialGet(buf,serial_type,pMem);
}
case 8: /* Integer 0 */
case 9: { /* Integer 1 */
+ /* EVIDENCE-OF: R-12976-22893 Value is the integer 0. */
+ /* EVIDENCE-OF: R-18143-12121 Value is the integer 1. */
pMem->u.i = serial_type-8;
pMem->flags = MEM_Int;
return 0;
}
default: {
- u32 len = (serial_type-12)/2;
+ /* EVIDENCE-OF: R-14606-31564 Value is a BLOB that is (N-12)/2 bytes in
+ ** length.
+ ** EVIDENCE-OF: R-28401-00140 Value is a string in the text encoding and
+ ** (N-13)/2 bytes in length. */
+ static const u16 aFlag[] = { MEM_Blob|MEM_Ephem, MEM_Str|MEM_Ephem };
pMem->z = (char *)buf;
- pMem->n = len;
- pMem->xDel = 0;
- if( serial_type&0x01 ){
- pMem->flags = MEM_Str | MEM_Ephem;
- }else{
- pMem->flags = MEM_Blob | MEM_Ephem;
- }
- return len;
+ pMem->n = (serial_type-12)/2;
+ pMem->flags = aFlag[serial_type&1];
+ return pMem->n;
}
}
return 0;
}
-
/*
** This routine is used to allocate sufficient space for an UnpackedRecord
** structure large enough to be used with sqlite3VdbeRecordUnpack() if
@@ -61172,6 +73971,7 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
}
p->aMem = (Mem*)&((char*)p)[ROUND8(sizeof(UnpackedRecord))];
+ assert( pKeyInfo->aSortOrder!=0 );
p->pKeyInfo = pKeyInfo;
p->nField = pKeyInfo->nField + 1;
return p;
@@ -61195,72 +73995,60 @@ SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(
u32 szHdr;
Mem *pMem = p->aMem;
- p->flags = 0;
+ p->default_rc = 0;
assert( EIGHT_BYTE_ALIGNMENT(pMem) );
idx = getVarint32(aKey, szHdr);
d = szHdr;
u = 0;
- while( idx<szHdr && u<p->nField && d<=nKey ){
+ while( idx<szHdr && d<=nKey ){
u32 serial_type;
idx += getVarint32(&aKey[idx], serial_type);
pMem->enc = pKeyInfo->enc;
pMem->db = pKeyInfo->db;
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
- pMem->zMalloc = 0;
+ pMem->szMalloc = 0;
+ pMem->z = 0;
d += sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
pMem++;
- u++;
+ if( (++u)>=p->nField ) break;
}
assert( u<=pKeyInfo->nField + 1 );
p->nField = u;
}
+#if SQLITE_DEBUG
/*
-** This function compares the two table rows or index records
-** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero
-** or positive integer if key1 is less than, equal to or
-** greater than key2. The {nKey1, pKey1} key must be a blob
-** created by th OP_MakeRecord opcode of the VDBE. The pPKey2
-** key must be a parsed key such as obtained from
-** sqlite3VdbeParseRecord.
+** This function compares two index or table record keys in the same way
+** as the sqlite3VdbeRecordCompare() routine. Unlike VdbeRecordCompare(),
+** this function deserializes and compares values using the
+** sqlite3VdbeSerialGet() and sqlite3MemCompare() functions. It is used
+** in assert() statements to ensure that the optimized code in
+** sqlite3VdbeRecordCompare() returns results with these two primitives.
**
-** Key1 and Key2 do not have to contain the same number of fields.
-** The key with fewer fields is usually compares less than the
-** longer key. However if the UNPACKED_INCRKEY flags in pPKey2 is set
-** and the common prefixes are equal, then key1 is less than key2.
-** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are
-** equal, then the keys are considered to be equal and
-** the parts beyond the common prefix are ignored.
-**
-** If the UNPACKED_IGNORE_ROWID flag is set, then the last byte of
-** the header of pKey1 is ignored. It is assumed that pKey1 is
-** an index key, and thus ends with a rowid value. The last byte
-** of the header will therefore be the serial type of the rowid:
-** one of 1, 2, 3, 4, 5, 6, 8, or 9 - the integer serial types.
-** The serial type of the final rowid will always be a single byte.
-** By ignoring this last byte of the header, we force the comparison
-** to ignore the rowid at the end of key1.
+** Return true if the result of comparison is equivalent to desiredResult.
+** Return false if there is a disagreement.
*/
-SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
+static int vdbeRecordCompareDebug(
int nKey1, const void *pKey1, /* Left key */
- UnpackedRecord *pPKey2 /* Right key */
+ const UnpackedRecord *pPKey2, /* Right key */
+ int desiredResult /* Correct answer */
){
- int d1; /* Offset into aKey[] of next data element */
+ u32 d1; /* Offset into aKey[] of next data element */
u32 idx1; /* Offset into aKey[] of next header element */
u32 szHdr1; /* Number of bytes in header */
int i = 0;
- int nField;
int rc = 0;
const unsigned char *aKey1 = (const unsigned char *)pKey1;
KeyInfo *pKeyInfo;
Mem mem1;
pKeyInfo = pPKey2->pKeyInfo;
+ if( pKeyInfo->db==0 ) return 1;
mem1.enc = pKeyInfo->enc;
mem1.db = pKeyInfo->db;
/* mem1.flags = 0; // Will be initialized by sqlite3VdbeSerialGet() */
- VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */
+ VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
/* Compilers may complain that mem1.u.i is potentially uninitialized.
** We could initialize it, as shown here, to silence those complaints.
@@ -61272,17 +74060,29 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
/* mem1.u.i = 0; // not needed, here to silence compiler warning */
idx1 = getVarint32(aKey1, szHdr1);
+ if( szHdr1>98307 ) return SQLITE_CORRUPT;
d1 = szHdr1;
- if( pPKey2->flags & UNPACKED_IGNORE_ROWID ){
- szHdr1--;
- }
- nField = pKeyInfo->nField;
- while( idx1<szHdr1 && i<pPKey2->nField ){
+ assert( pKeyInfo->nField+pKeyInfo->nXField>=pPKey2->nField || CORRUPT_DB );
+ assert( pKeyInfo->aSortOrder!=0 );
+ assert( pKeyInfo->nField>0 );
+ assert( idx1<=szHdr1 || CORRUPT_DB );
+ do{
u32 serial_type1;
/* Read the serial types for the next element in each key. */
idx1 += getVarint32( aKey1+idx1, serial_type1 );
- if( d1>=nKey1 && sqlite3VdbeSerialTypeLen(serial_type1)>0 ) break;
+
+ /* Verify that there is enough key space remaining to avoid
+ ** a buffer overread. The "d1+serial_type1+2" subexpression will
+ ** always be greater than or equal to the amount of required key space.
+ ** Use that approximation to avoid the more expensive call to
+ ** sqlite3VdbeSerialTypeLen() in the common case.
+ */
+ if( d1+serial_type1+2>(u32)nKey1
+ && d1+sqlite3VdbeSerialTypeLen(serial_type1)>(u32)nKey1
+ ){
+ break;
+ }
/* Extract the values to be compared.
*/
@@ -61290,58 +74090,731 @@ SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
/* Do the comparison
*/
- rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i],
- i<nField ? pKeyInfo->aColl[i] : 0);
+ rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], pKeyInfo->aColl[i]);
if( rc!=0 ){
- assert( mem1.zMalloc==0 ); /* See comment below */
-
- /* Invert the result if we are using DESC sort order. */
- if( pKeyInfo->aSortOrder && i<nField && pKeyInfo->aSortOrder[i] ){
- rc = -rc;
+ assert( mem1.szMalloc==0 ); /* See comment below */
+ if( pKeyInfo->aSortOrder[i] ){
+ rc = -rc; /* Invert the result for DESC sort order. */
}
-
- /* If the PREFIX_SEARCH flag is set and all fields except the final
- ** rowid field were equal, then clear the PREFIX_SEARCH flag and set
- ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
- ** This is used by the OP_IsUnique opcode.
- */
- if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){
- assert( idx1==szHdr1 && rc );
- assert( mem1.flags & MEM_Int );
- pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH;
- pPKey2->rowid = mem1.u.i;
- }
-
- return rc;
+ goto debugCompareEnd;
}
i++;
- }
+ }while( idx1<szHdr1 && i<pPKey2->nField );
/* No memory allocation is ever used on mem1. Prove this using
** the following assert(). If the assert() fails, it indicates a
** memory leak and a need to call sqlite3VdbeMemRelease(&mem1).
*/
- assert( mem1.zMalloc==0 );
+ assert( mem1.szMalloc==0 );
/* rc==0 here means that one of the keys ran out of fields and
- ** all the fields up to that point were equal. If the UNPACKED_INCRKEY
- ** flag is set, then break the tie by treating key2 as larger.
- ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes
- ** are considered to be equal. Otherwise, the longer key is the
- ** larger. As it happens, the pPKey2 will always be the longer
- ** if there is a difference.
- */
- assert( rc==0 );
- if( pPKey2->flags & UNPACKED_INCRKEY ){
- rc = -1;
- }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){
- /* Leave rc==0 */
- }else if( idx1<szHdr1 ){
- rc = 1;
+ ** all the fields up to that point were equal. Return the default_rc
+ ** value. */
+ rc = pPKey2->default_rc;
+
+debugCompareEnd:
+ if( desiredResult==0 && rc==0 ) return 1;
+ if( desiredResult<0 && rc<0 ) return 1;
+ if( desiredResult>0 && rc>0 ) return 1;
+ if( CORRUPT_DB ) return 1;
+ if( pKeyInfo->db->mallocFailed ) return 1;
+ return 0;
+}
+#endif
+
+#if SQLITE_DEBUG
+/*
+** Count the number of fields (a.k.a. columns) in the record given by
+** pKey,nKey. The verify that this count is less than or equal to the
+** limit given by pKeyInfo->nField + pKeyInfo->nXField.
+**
+** If this constraint is not satisfied, it means that the high-speed
+** vdbeRecordCompareInt() and vdbeRecordCompareString() routines will
+** not work correctly. If this assert() ever fires, it probably means
+** that the KeyInfo.nField or KeyInfo.nXField values were computed
+** incorrectly.
+*/
+static void vdbeAssertFieldCountWithinLimits(
+ int nKey, const void *pKey, /* The record to verify */
+ const KeyInfo *pKeyInfo /* Compare size with this KeyInfo */
+){
+ int nField = 0;
+ u32 szHdr;
+ u32 idx;
+ u32 notUsed;
+ const unsigned char *aKey = (const unsigned char*)pKey;
+
+ if( CORRUPT_DB ) return;
+ idx = getVarint32(aKey, szHdr);
+ assert( nKey>=0 );
+ assert( szHdr<=(u32)nKey );
+ while( idx<szHdr ){
+ idx += getVarint32(aKey+idx, notUsed);
+ nField++;
}
- return rc;
+ assert( nField <= pKeyInfo->nField+pKeyInfo->nXField );
}
+#else
+# define vdbeAssertFieldCountWithinLimits(A,B,C)
+#endif
+
+/*
+** Both *pMem1 and *pMem2 contain string values. Compare the two values
+** using the collation sequence pColl. As usual, return a negative , zero
+** or positive value if *pMem1 is less than, equal to or greater than
+** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
+*/
+static int vdbeCompareMemString(
+ const Mem *pMem1,
+ const Mem *pMem2,
+ const CollSeq *pColl,
+ u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
+){
+ if( pMem1->enc==pColl->enc ){
+ /* The strings are already in the correct encoding. Call the
+ ** comparison function directly */
+ return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
+ }else{
+ int rc;
+ const void *v1, *v2;
+ int n1, n2;
+ Mem c1;
+ Mem c2;
+ sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
+ sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
+ sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
+ sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
+ v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
+ n1 = v1==0 ? 0 : c1.n;
+ v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
+ n2 = v2==0 ? 0 : c2.n;
+ rc = pColl->xCmp(pColl->pUser, n1, v1, n2, v2);
+ if( (v1==0 || v2==0) && prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
+ sqlite3VdbeMemRelease(&c1);
+ sqlite3VdbeMemRelease(&c2);
+ return rc;
+ }
+}
+
+/*
+** The input pBlob is guaranteed to be a Blob that is not marked
+** with MEM_Zero. Return true if it could be a zero-blob.
+*/
+static int isAllZero(const char *z, int n){
+ int i;
+ for(i=0; i<n; i++){
+ if( z[i] ) return 0;
+ }
+ return 1;
+}
+
+/*
+** Compare two blobs. Return negative, zero, or positive if the first
+** is less than, equal to, or greater than the second, respectively.
+** If one blob is a prefix of the other, then the shorter is the lessor.
+*/
+static SQLITE_NOINLINE int sqlite3BlobCompare(const Mem *pB1, const Mem *pB2){
+ int c;
+ int n1 = pB1->n;
+ int n2 = pB2->n;
+
+ /* It is possible to have a Blob value that has some non-zero content
+ ** followed by zero content. But that only comes up for Blobs formed
+ ** by the OP_MakeRecord opcode, and such Blobs never get passed into
+ ** sqlite3MemCompare(). */
+ assert( (pB1->flags & MEM_Zero)==0 || n1==0 );
+ assert( (pB2->flags & MEM_Zero)==0 || n2==0 );
+
+ if( (pB1->flags|pB2->flags) & MEM_Zero ){
+ if( pB1->flags & pB2->flags & MEM_Zero ){
+ return pB1->u.nZero - pB2->u.nZero;
+ }else if( pB1->flags & MEM_Zero ){
+ if( !isAllZero(pB2->z, pB2->n) ) return -1;
+ return pB1->u.nZero - n2;
+ }else{
+ if( !isAllZero(pB1->z, pB1->n) ) return +1;
+ return n1 - pB2->u.nZero;
+ }
+ }
+ c = memcmp(pB1->z, pB2->z, n1>n2 ? n2 : n1);
+ if( c ) return c;
+ return n1 - n2;
+}
+
+/*
+** Do a comparison between a 64-bit signed integer and a 64-bit floating-point
+** number. Return negative, zero, or positive if the first (i64) is less than,
+** equal to, or greater than the second (double).
+*/
+static int sqlite3IntFloatCompare(i64 i, double r){
+ if( sizeof(LONGDOUBLE_TYPE)>8 ){
+ LONGDOUBLE_TYPE x = (LONGDOUBLE_TYPE)i;
+ if( x<r ) return -1;
+ if( x>r ) return +1;
+ return 0;
+ }else{
+ i64 y;
+ double s;
+ if( r<-9223372036854775808.0 ) return +1;
+ if( r>9223372036854775807.0 ) return -1;
+ y = (i64)r;
+ if( i<y ) return -1;
+ if( i>y ){
+ if( y==SMALLEST_INT64 && r>0.0 ) return -1;
+ return +1;
+ }
+ s = (double)i;
+ if( s<r ) return -1;
+ if( s>r ) return +1;
+ return 0;
+ }
+}
+
+/*
+** Compare the values contained by the two memory cells, returning
+** negative, zero or positive if pMem1 is less than, equal to, or greater
+** than pMem2. Sorting order is NULL's first, followed by numbers (integers
+** and reals) sorted numerically, followed by text ordered by the collating
+** sequence pColl and finally blob's ordered by memcmp().
+**
+** Two NULL values are considered equal by this function.
+*/
+SQLITE_PRIVATE int sqlite3MemCompare(const Mem *pMem1, const Mem *pMem2, const CollSeq *pColl){
+ int f1, f2;
+ int combined_flags;
+
+ f1 = pMem1->flags;
+ f2 = pMem2->flags;
+ combined_flags = f1|f2;
+ assert( (combined_flags & MEM_RowSet)==0 );
+ /* If one value is NULL, it is less than the other. If both values
+ ** are NULL, return 0.
+ */
+ if( combined_flags&MEM_Null ){
+ return (f2&MEM_Null) - (f1&MEM_Null);
+ }
+
+ /* At least one of the two values is a number
+ */
+ if( combined_flags&(MEM_Int|MEM_Real) ){
+ if( (f1 & f2 & MEM_Int)!=0 ){
+ if( pMem1->u.i < pMem2->u.i ) return -1;
+ if( pMem1->u.i > pMem2->u.i ) return +1;
+ return 0;
+ }
+ if( (f1 & f2 & MEM_Real)!=0 ){
+ if( pMem1->u.r < pMem2->u.r ) return -1;
+ if( pMem1->u.r > pMem2->u.r ) return +1;
+ return 0;
+ }
+ if( (f1&MEM_Int)!=0 ){
+ if( (f2&MEM_Real)!=0 ){
+ return sqlite3IntFloatCompare(pMem1->u.i, pMem2->u.r);
+ }else{
+ return -1;
+ }
+ }
+ if( (f1&MEM_Real)!=0 ){
+ if( (f2&MEM_Int)!=0 ){
+ return -sqlite3IntFloatCompare(pMem2->u.i, pMem1->u.r);
+ }else{
+ return -1;
+ }
+ }
+ return +1;
+ }
+
+ /* If one value is a string and the other is a blob, the string is less.
+ ** If both are strings, compare using the collating functions.
+ */
+ if( combined_flags&MEM_Str ){
+ if( (f1 & MEM_Str)==0 ){
+ return 1;
+ }
+ if( (f2 & MEM_Str)==0 ){
+ return -1;
+ }
+
+ assert( pMem1->enc==pMem2->enc || pMem1->db->mallocFailed );
+ assert( pMem1->enc==SQLITE_UTF8 ||
+ pMem1->enc==SQLITE_UTF16LE || pMem1->enc==SQLITE_UTF16BE );
+
+ /* The collation sequence must be defined at this point, even if
+ ** the user deletes the collation sequence after the vdbe program is
+ ** compiled (this was not always the case).
+ */
+ assert( !pColl || pColl->xCmp );
+
+ if( pColl ){
+ return vdbeCompareMemString(pMem1, pMem2, pColl, 0);
+ }
+ /* If a NULL pointer was passed as the collate function, fall through
+ ** to the blob case and use memcmp(). */
+ }
+
+ /* Both values must be blobs. Compare using memcmp(). */
+ return sqlite3BlobCompare(pMem1, pMem2);
+}
+
+
+/*
+** The first argument passed to this function is a serial-type that
+** corresponds to an integer - all values between 1 and 9 inclusive
+** except 7. The second points to a buffer containing an integer value
+** serialized according to serial_type. This function deserializes
+** and returns the value.
+*/
+static i64 vdbeRecordDecodeInt(u32 serial_type, const u8 *aKey){
+ u32 y;
+ assert( CORRUPT_DB || (serial_type>=1 && serial_type<=9 && serial_type!=7) );
+ switch( serial_type ){
+ case 0:
+ case 1:
+ testcase( aKey[0]&0x80 );
+ return ONE_BYTE_INT(aKey);
+ case 2:
+ testcase( aKey[0]&0x80 );
+ return TWO_BYTE_INT(aKey);
+ case 3:
+ testcase( aKey[0]&0x80 );
+ return THREE_BYTE_INT(aKey);
+ case 4: {
+ testcase( aKey[0]&0x80 );
+ y = FOUR_BYTE_UINT(aKey);
+ return (i64)*(int*)&y;
+ }
+ case 5: {
+ testcase( aKey[0]&0x80 );
+ return FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey);
+ }
+ case 6: {
+ u64 x = FOUR_BYTE_UINT(aKey);
+ testcase( aKey[0]&0x80 );
+ x = (x<<32) | FOUR_BYTE_UINT(aKey+4);
+ return (i64)*(i64*)&x;
+ }
+ }
+
+ return (serial_type - 8);
+}
+
+/*
+** This function compares the two table rows or index records
+** specified by {nKey1, pKey1} and pPKey2. It returns a negative, zero
+** or positive integer if key1 is less than, equal to or
+** greater than key2. The {nKey1, pKey1} key must be a blob
+** created by the OP_MakeRecord opcode of the VDBE. The pPKey2
+** key must be a parsed key such as obtained from
+** sqlite3VdbeParseRecord.
+**
+** If argument bSkip is non-zero, it is assumed that the caller has already
+** determined that the first fields of the keys are equal.
+**
+** Key1 and Key2 do not have to contain the same number of fields. If all
+** fields that appear in both keys are equal, then pPKey2->default_rc is
+** returned.
+**
+** If database corruption is discovered, set pPKey2->errCode to
+** SQLITE_CORRUPT and return 0. If an OOM error is encountered,
+** pPKey2->errCode is set to SQLITE_NOMEM and, if it is not NULL, the
+** malloc-failed flag set on database handle (pPKey2->pKeyInfo->db).
+*/
+SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2, /* Right key */
+ int bSkip /* If true, skip the first field */
+){
+ u32 d1; /* Offset into aKey[] of next data element */
+ int i; /* Index of next field to compare */
+ u32 szHdr1; /* Size of record header in bytes */
+ u32 idx1; /* Offset of first type in header */
+ int rc = 0; /* Return value */
+ Mem *pRhs = pPKey2->aMem; /* Next field of pPKey2 to compare */
+ KeyInfo *pKeyInfo = pPKey2->pKeyInfo;
+ const unsigned char *aKey1 = (const unsigned char *)pKey1;
+ Mem mem1;
+
+ /* If bSkip is true, then the caller has already determined that the first
+ ** two elements in the keys are equal. Fix the various stack variables so
+ ** that this routine begins comparing at the second field. */
+ if( bSkip ){
+ u32 s1;
+ idx1 = 1 + getVarint32(&aKey1[1], s1);
+ szHdr1 = aKey1[0];
+ d1 = szHdr1 + sqlite3VdbeSerialTypeLen(s1);
+ i = 1;
+ pRhs++;
+ }else{
+ idx1 = getVarint32(aKey1, szHdr1);
+ d1 = szHdr1;
+ if( d1>(unsigned)nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }
+ i = 0;
+ }
+
+ VVA_ONLY( mem1.szMalloc = 0; ) /* Only needed by assert() statements */
+ assert( pPKey2->pKeyInfo->nField+pPKey2->pKeyInfo->nXField>=pPKey2->nField
+ || CORRUPT_DB );
+ assert( pPKey2->pKeyInfo->aSortOrder!=0 );
+ assert( pPKey2->pKeyInfo->nField>0 );
+ assert( idx1<=szHdr1 || CORRUPT_DB );
+ do{
+ u32 serial_type;
+
+ /* RHS is an integer */
+ if( pRhs->flags & MEM_Int ){
+ serial_type = aKey1[idx1];
+ testcase( serial_type==12 );
+ if( serial_type>=10 ){
+ rc = +1;
+ }else if( serial_type==0 ){
+ rc = -1;
+ }else if( serial_type==7 ){
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ rc = -sqlite3IntFloatCompare(pRhs->u.i, mem1.u.r);
+ }else{
+ i64 lhs = vdbeRecordDecodeInt(serial_type, &aKey1[d1]);
+ i64 rhs = pRhs->u.i;
+ if( lhs<rhs ){
+ rc = -1;
+ }else if( lhs>rhs ){
+ rc = +1;
+ }
+ }
+ }
+
+ /* RHS is real */
+ else if( pRhs->flags & MEM_Real ){
+ serial_type = aKey1[idx1];
+ if( serial_type>=10 ){
+ /* Serial types 12 or greater are strings and blobs (greater than
+ ** numbers). Types 10 and 11 are currently "reserved for future
+ ** use", so it doesn't really matter what the results of comparing
+ ** them to numberic values are. */
+ rc = +1;
+ }else if( serial_type==0 ){
+ rc = -1;
+ }else{
+ sqlite3VdbeSerialGet(&aKey1[d1], serial_type, &mem1);
+ if( serial_type==7 ){
+ if( mem1.u.r<pRhs->u.r ){
+ rc = -1;
+ }else if( mem1.u.r>pRhs->u.r ){
+ rc = +1;
+ }
+ }else{
+ rc = sqlite3IntFloatCompare(mem1.u.i, pRhs->u.r);
+ }
+ }
+ }
+
+ /* RHS is a string */
+ else if( pRhs->flags & MEM_Str ){
+ getVarint32(&aKey1[idx1], serial_type);
+ testcase( serial_type==12 );
+ if( serial_type<12 ){
+ rc = -1;
+ }else if( !(serial_type & 0x01) ){
+ rc = +1;
+ }else{
+ mem1.n = (serial_type - 12) / 2;
+ testcase( (d1+mem1.n)==(unsigned)nKey1 );
+ testcase( (d1+mem1.n+1)==(unsigned)nKey1 );
+ if( (d1+mem1.n) > (unsigned)nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }else if( pKeyInfo->aColl[i] ){
+ mem1.enc = pKeyInfo->enc;
+ mem1.db = pKeyInfo->db;
+ mem1.flags = MEM_Str;
+ mem1.z = (char*)&aKey1[d1];
+ rc = vdbeCompareMemString(
+ &mem1, pRhs, pKeyInfo->aColl[i], &pPKey2->errCode
+ );
+ }else{
+ int nCmp = MIN(mem1.n, pRhs->n);
+ rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
+ if( rc==0 ) rc = mem1.n - pRhs->n;
+ }
+ }
+ }
+
+ /* RHS is a blob */
+ else if( pRhs->flags & MEM_Blob ){
+ assert( (pRhs->flags & MEM_Zero)==0 || pRhs->n==0 );
+ getVarint32(&aKey1[idx1], serial_type);
+ testcase( serial_type==12 );
+ if( serial_type<12 || (serial_type & 0x01) ){
+ rc = -1;
+ }else{
+ int nStr = (serial_type - 12) / 2;
+ testcase( (d1+nStr)==(unsigned)nKey1 );
+ testcase( (d1+nStr+1)==(unsigned)nKey1 );
+ if( (d1+nStr) > (unsigned)nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }else if( pRhs->flags & MEM_Zero ){
+ if( !isAllZero((const char*)&aKey1[d1],nStr) ){
+ rc = 1;
+ }else{
+ rc = nStr - pRhs->u.nZero;
+ }
+ }else{
+ int nCmp = MIN(nStr, pRhs->n);
+ rc = memcmp(&aKey1[d1], pRhs->z, nCmp);
+ if( rc==0 ) rc = nStr - pRhs->n;
+ }
+ }
+ }
+
+ /* RHS is null */
+ else{
+ serial_type = aKey1[idx1];
+ rc = (serial_type!=0);
+ }
+
+ if( rc!=0 ){
+ if( pKeyInfo->aSortOrder[i] ){
+ rc = -rc;
+ }
+ assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, rc) );
+ assert( mem1.szMalloc==0 ); /* See comment below */
+ return rc;
+ }
+
+ i++;
+ pRhs++;
+ d1 += sqlite3VdbeSerialTypeLen(serial_type);
+ idx1 += sqlite3VarintLen(serial_type);
+ }while( idx1<(unsigned)szHdr1 && i<pPKey2->nField && d1<=(unsigned)nKey1 );
+
+ /* No memory allocation is ever used on mem1. Prove this using
+ ** the following assert(). If the assert() fails, it indicates a
+ ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1). */
+ assert( mem1.szMalloc==0 );
+
+ /* rc==0 here means that one or both of the keys ran out of fields and
+ ** all the fields up to that point were equal. Return the default_rc
+ ** value. */
+ assert( CORRUPT_DB
+ || vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, pPKey2->default_rc)
+ || pKeyInfo->db->mallocFailed
+ );
+ pPKey2->eqSeen = 1;
+ return pPKey2->default_rc;
+}
+SQLITE_PRIVATE int sqlite3VdbeRecordCompare(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2 /* Right key */
+){
+ return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 0);
+}
+
+
+/*
+** This function is an optimized version of sqlite3VdbeRecordCompare()
+** that (a) the first field of pPKey2 is an integer, and (b) the
+** size-of-header varint at the start of (pKey1/nKey1) fits in a single
+** byte (i.e. is less than 128).
+**
+** To avoid concerns about buffer overreads, this routine is only used
+** on schemas where the maximum valid header size is 63 bytes or less.
+*/
+static int vdbeRecordCompareInt(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2 /* Right key */
+){
+ const u8 *aKey = &((const u8*)pKey1)[*(const u8*)pKey1 & 0x3F];
+ int serial_type = ((const u8*)pKey1)[1];
+ int res;
+ u32 y;
+ u64 x;
+ i64 v;
+ i64 lhs;
+
+ vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo);
+ assert( (*(u8*)pKey1)<=0x3F || CORRUPT_DB );
+ switch( serial_type ){
+ case 1: { /* 1-byte signed integer */
+ lhs = ONE_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 2: { /* 2-byte signed integer */
+ lhs = TWO_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 3: { /* 3-byte signed integer */
+ lhs = THREE_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 4: { /* 4-byte signed integer */
+ y = FOUR_BYTE_UINT(aKey);
+ lhs = (i64)*(int*)&y;
+ testcase( lhs<0 );
+ break;
+ }
+ case 5: { /* 6-byte signed integer */
+ lhs = FOUR_BYTE_UINT(aKey+2) + (((i64)1)<<32)*TWO_BYTE_INT(aKey);
+ testcase( lhs<0 );
+ break;
+ }
+ case 6: { /* 8-byte signed integer */
+ x = FOUR_BYTE_UINT(aKey);
+ x = (x<<32) | FOUR_BYTE_UINT(aKey+4);
+ lhs = *(i64*)&x;
+ testcase( lhs<0 );
+ break;
+ }
+ case 8:
+ lhs = 0;
+ break;
+ case 9:
+ lhs = 1;
+ break;
+
+ /* This case could be removed without changing the results of running
+ ** this code. Including it causes gcc to generate a faster switch
+ ** statement (since the range of switch targets now starts at zero and
+ ** is contiguous) but does not cause any duplicate code to be generated
+ ** (as gcc is clever enough to combine the two like cases). Other
+ ** compilers might be similar. */
+ case 0: case 7:
+ return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2);
+
+ default:
+ return sqlite3VdbeRecordCompare(nKey1, pKey1, pPKey2);
+ }
+
+ v = pPKey2->aMem[0].u.i;
+ if( v>lhs ){
+ res = pPKey2->r1;
+ }else if( v<lhs ){
+ res = pPKey2->r2;
+ }else if( pPKey2->nField>1 ){
+ /* The first fields of the two keys are equal. Compare the trailing
+ ** fields. */
+ res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
+ }else{
+ /* The first fields of the two keys are equal and there are no trailing
+ ** fields. Return pPKey2->default_rc in this case. */
+ res = pPKey2->default_rc;
+ pPKey2->eqSeen = 1;
+ }
+
+ assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res) );
+ return res;
+}
+
+/*
+** This function is an optimized version of sqlite3VdbeRecordCompare()
+** that (a) the first field of pPKey2 is a string, that (b) the first field
+** uses the collation sequence BINARY and (c) that the size-of-header varint
+** at the start of (pKey1/nKey1) fits in a single byte.
+*/
+static int vdbeRecordCompareString(
+ int nKey1, const void *pKey1, /* Left key */
+ UnpackedRecord *pPKey2 /* Right key */
+){
+ const u8 *aKey1 = (const u8*)pKey1;
+ int serial_type;
+ int res;
+
+ assert( pPKey2->aMem[0].flags & MEM_Str );
+ vdbeAssertFieldCountWithinLimits(nKey1, pKey1, pPKey2->pKeyInfo);
+ getVarint32(&aKey1[1], serial_type);
+ if( serial_type<12 ){
+ res = pPKey2->r1; /* (pKey1/nKey1) is a number or a null */
+ }else if( !(serial_type & 0x01) ){
+ res = pPKey2->r2; /* (pKey1/nKey1) is a blob */
+ }else{
+ int nCmp;
+ int nStr;
+ int szHdr = aKey1[0];
+
+ nStr = (serial_type-12) / 2;
+ if( (szHdr + nStr) > nKey1 ){
+ pPKey2->errCode = (u8)SQLITE_CORRUPT_BKPT;
+ return 0; /* Corruption */
+ }
+ nCmp = MIN( pPKey2->aMem[0].n, nStr );
+ res = memcmp(&aKey1[szHdr], pPKey2->aMem[0].z, nCmp);
+
+ if( res==0 ){
+ res = nStr - pPKey2->aMem[0].n;
+ if( res==0 ){
+ if( pPKey2->nField>1 ){
+ res = sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, pPKey2, 1);
+ }else{
+ res = pPKey2->default_rc;
+ pPKey2->eqSeen = 1;
+ }
+ }else if( res>0 ){
+ res = pPKey2->r2;
+ }else{
+ res = pPKey2->r1;
+ }
+ }else if( res>0 ){
+ res = pPKey2->r2;
+ }else{
+ res = pPKey2->r1;
+ }
+ }
+
+ assert( vdbeRecordCompareDebug(nKey1, pKey1, pPKey2, res)
+ || CORRUPT_DB
+ || pPKey2->pKeyInfo->db->mallocFailed
+ );
+ return res;
+}
+
+/*
+** Return a pointer to an sqlite3VdbeRecordCompare() compatible function
+** suitable for comparing serialized records to the unpacked record passed
+** as the only argument.
+*/
+SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord *p){
+ /* varintRecordCompareInt() and varintRecordCompareString() both assume
+ ** that the size-of-header varint that occurs at the start of each record
+ ** fits in a single byte (i.e. is 127 or less). varintRecordCompareInt()
+ ** also assumes that it is safe to overread a buffer by at least the
+ ** maximum possible legal header size plus 8 bytes. Because there is
+ ** guaranteed to be at least 74 (but not 136) bytes of padding following each
+ ** buffer passed to varintRecordCompareInt() this makes it convenient to
+ ** limit the size of the header to 64 bytes in cases where the first field
+ ** is an integer.
+ **
+ ** The easiest way to enforce this limit is to consider only records with
+ ** 13 fields or less. If the first field is an integer, the maximum legal
+ ** header size is (12*5 + 1 + 1) bytes. */
+ if( (p->pKeyInfo->nField + p->pKeyInfo->nXField)<=13 ){
+ int flags = p->aMem[0].flags;
+ if( p->pKeyInfo->aSortOrder[0] ){
+ p->r1 = 1;
+ p->r2 = -1;
+ }else{
+ p->r1 = -1;
+ p->r2 = 1;
+ }
+ if( (flags & MEM_Int) ){
+ return vdbeRecordCompareInt;
+ }
+ testcase( flags & MEM_Real );
+ testcase( flags & MEM_Null );
+ testcase( flags & MEM_Blob );
+ if( (flags & (MEM_Real|MEM_Null|MEM_Blob))==0 && p->pKeyInfo->aColl[0]==0 ){
+ assert( flags & MEM_Str );
+ return vdbeRecordCompareString;
+ }
+ }
+
+ return sqlite3VdbeRecordCompare;
+}
/*
** pCur points at an index entry created using the OP_MakeRecord opcode.
@@ -61359,21 +74832,18 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
u32 lenRowid; /* Size of the rowid */
Mem m, v;
- UNUSED_PARAMETER(db);
-
/* Get the size of the index entry. Only indices entries of less
** than 2GiB are support - anything large must be database corruption.
** Any corruption is detected in sqlite3BtreeParseCellPtr(), though, so
** this code can safely assume that nCellKey is 32-bits
*/
assert( sqlite3BtreeCursorIsValid(pCur) );
- rc = sqlite3BtreeKeySize(pCur, &nCellKey);
- assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
+ nCellKey = sqlite3BtreePayloadSize(pCur);
assert( (nCellKey & SQLITE_MAX_U32)==(u64)nCellKey );
/* Read in the complete content of the index entry */
- memset(&m, 0, sizeof(m));
- rc = sqlite3VdbeMemFromBtree(pCur, 0, (int)nCellKey, 1, &m);
+ sqlite3VdbeMemInit(&m, db, 0);
+ rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m);
if( rc ){
return rc;
}
@@ -61400,7 +74870,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
if( unlikely(typeRowid<1 || typeRowid>9 || typeRowid==7) ){
goto idx_rowid_corruption;
}
- lenRowid = sqlite3VdbeSerialTypeLen(typeRowid);
+ lenRowid = sqlite3SmallTypeSizes[typeRowid];
testcase( (u32)m.n==szHdr+lenRowid );
if( unlikely((u32)m.n<szHdr+lenRowid) ){
goto idx_rowid_corruption;
@@ -61415,7 +74885,7 @@ SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3 *db, BtCursor *pCur, i64 *rowid){
/* Jump here if database corruption is detected after m has been
** allocated. Free the m object and return SQLITE_CORRUPT. */
idx_rowid_corruption:
- testcase( m.zMalloc!=0 );
+ testcase( m.szMalloc!=0 );
sqlite3VdbeMemRelease(&m);
return SQLITE_CORRUPT_BKPT;
}
@@ -61432,30 +74902,31 @@ idx_rowid_corruption:
** of the keys prior to the final rowid, not the entire key.
*/
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(
- VdbeCursor *pC, /* The cursor to compare against */
- UnpackedRecord *pUnpacked, /* Unpacked version of key to compare against */
- int *res /* Write the comparison result here */
+ sqlite3 *db, /* Database connection */
+ VdbeCursor *pC, /* The cursor to compare against */
+ UnpackedRecord *pUnpacked, /* Unpacked version of key */
+ int *res /* Write the comparison result here */
){
i64 nCellKey = 0;
int rc;
- BtCursor *pCur = pC->pCursor;
+ BtCursor *pCur;
Mem m;
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCur = pC->uc.pCursor;
assert( sqlite3BtreeCursorIsValid(pCur) );
- rc = sqlite3BtreeKeySize(pCur, &nCellKey);
- assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */
- /* nCellKey will always be between 0 and 0xffffffff because of the say
+ nCellKey = sqlite3BtreePayloadSize(pCur);
+ /* nCellKey will always be between 0 and 0xffffffff because of the way
** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */
if( nCellKey<=0 || nCellKey>0x7fffffff ){
*res = 0;
return SQLITE_CORRUPT_BKPT;
}
- memset(&m, 0, sizeof(m));
- rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m);
+ sqlite3VdbeMemInit(&m, db, 0);
+ rc = sqlite3VdbeMemFromBtree(pCur, 0, (u32)nCellKey, 1, &m);
if( rc ){
return rc;
}
- assert( pUnpacked->flags & UNPACKED_IGNORE_ROWID );
*res = sqlite3VdbeRecordCompare(m.n, m.z, pUnpacked);
sqlite3VdbeMemRelease(&m);
return SQLITE_OK;
@@ -61511,7 +74982,7 @@ SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe *v){
**
** The returned value must be freed by the caller using sqlite3ValueFree().
*/
-SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
+SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe *v, int iVar, u8 aff){
assert( iVar>0 );
if( v ){
Mem *pMem = &v->aVar[iVar-1];
@@ -61520,7 +74991,6 @@ SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){
if( pRet ){
sqlite3VdbeMemCopy((Mem *)pRet, pMem);
sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8);
- sqlite3VdbeMemStoreType((Mem *)pRet);
}
return pRet;
}
@@ -61542,6 +75012,107 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
}
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/*
+** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
+** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
+** in memory obtained from sqlite3DbMalloc).
+*/
+SQLITE_PRIVATE void sqlite3VtabImportErrmsg(Vdbe *p, sqlite3_vtab *pVtab){
+ if( pVtab->zErrMsg ){
+ sqlite3 *db = p->db;
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
+ sqlite3_free(pVtab->zErrMsg);
+ pVtab->zErrMsg = 0;
+ }
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+
+/*
+** If the second argument is not NULL, release any allocations associated
+** with the memory cells in the p->aMem[] array. Also free the UnpackedRecord
+** structure itself, using sqlite3DbFree().
+**
+** This function is used to free UnpackedRecord structures allocated by
+** the vdbeUnpackRecord() function found in vdbeapi.c.
+*/
+static void vdbeFreeUnpacked(sqlite3 *db, UnpackedRecord *p){
+ if( p ){
+ int i;
+ for(i=0; i<p->nField; i++){
+ Mem *pMem = &p->aMem[i];
+ if( pMem->zMalloc ) sqlite3VdbeMemRelease(pMem);
+ }
+ sqlite3DbFree(db, p);
+ }
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** Invoke the pre-update hook. If this is an UPDATE or DELETE pre-update call,
+** then cursor passed as the second argument should point to the row about
+** to be update or deleted. If the application calls sqlite3_preupdate_old(),
+** the required value will be read from the row the cursor points to.
+*/
+SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
+ Vdbe *v, /* Vdbe pre-update hook is invoked by */
+ VdbeCursor *pCsr, /* Cursor to grab old.* values from */
+ int op, /* SQLITE_INSERT, UPDATE or DELETE */
+ const char *zDb, /* Database name */
+ Table *pTab, /* Modified table */
+ i64 iKey1, /* Initial key value */
+ int iReg /* Register for new.* record */
+){
+ sqlite3 *db = v->db;
+ i64 iKey2;
+ PreUpdate preupdate;
+ const char *zTbl = pTab->zName;
+ static const u8 fakeSortOrder = 0;
+
+ assert( db->pPreUpdate==0 );
+ memset(&preupdate, 0, sizeof(PreUpdate));
+ if( op==SQLITE_UPDATE ){
+ iKey2 = v->aMem[iReg].u.i;
+ }else{
+ iKey2 = iKey1;
+ }
+
+ assert( pCsr->nField==pTab->nCol
+ || (pCsr->nField==pTab->nCol+1 && op==SQLITE_DELETE && iReg==-1)
+ );
+
+ preupdate.v = v;
+ preupdate.pCsr = pCsr;
+ preupdate.op = op;
+ preupdate.iNewReg = iReg;
+ preupdate.keyinfo.db = db;
+ preupdate.keyinfo.enc = ENC(db);
+ preupdate.keyinfo.nField = pTab->nCol;
+ preupdate.keyinfo.aSortOrder = (u8*)&fakeSortOrder;
+ preupdate.iKey1 = iKey1;
+ preupdate.iKey2 = iKey2;
+ preupdate.pTab = pTab;
+
+ db->pPreUpdate = &preupdate;
+ db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
+ db->pPreUpdate = 0;
+ sqlite3DbFree(db, preupdate.aRecord);
+ vdbeFreeUnpacked(db, preupdate.pUnpacked);
+ vdbeFreeUnpacked(db, preupdate.pNewUnpacked);
+ if( preupdate.aNew ){
+ int i;
+ for(i=0; i<pCsr->nField; i++){
+ sqlite3VdbeMemRelease(&preupdate.aNew[i]);
+ }
+ sqlite3DbFree(db, preupdate.aNew);
+ }
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
@@ -61559,6 +75130,8 @@ SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){
** This file contains code use to implement APIs that are part of the
** VDBE.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#ifndef SQLITE_OMIT_DEPRECATED
/*
@@ -61597,6 +75170,38 @@ static int vdbeSafetyNotNull(Vdbe *p){
}
}
+#ifndef SQLITE_OMIT_TRACE
+/*
+** Invoke the profile callback. This routine is only called if we already
+** know that the profile callback is defined and needs to be invoked.
+*/
+static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
+ sqlite3_int64 iNow;
+ sqlite3_int64 iElapse;
+ assert( p->startTime>0 );
+ assert( db->xProfile!=0 || (db->mTrace & SQLITE_TRACE_PROFILE)!=0 );
+ assert( db->init.busy==0 );
+ assert( p->zSql!=0 );
+ sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
+ iElapse = (iNow - p->startTime)*1000000;
+ if( db->xProfile ){
+ db->xProfile(db->pProfileArg, p->zSql, iElapse);
+ }
+ if( db->mTrace & SQLITE_TRACE_PROFILE ){
+ db->xTrace(SQLITE_TRACE_PROFILE, db->pTraceArg, p, (void*)&iElapse);
+ }
+ p->startTime = 0;
+}
+/*
+** The checkProfileCallback(DB,P) macro checks to see if a profile callback
+** is needed, and it invokes the callback if it is needed.
+*/
+# define checkProfileCallback(DB,P) \
+ if( ((P)->startTime)>0 ){ invokeProfileCallback(DB,P); }
+#else
+# define checkProfileCallback(DB,P) /*no-op*/
+#endif
+
/*
** The following routine destroys a virtual machine that is created by
** the sqlite3_compile() routine. The integer returned is an SQLITE_
@@ -61615,17 +75220,12 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt){
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3 *db = v->db;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex;
-#endif
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
-#if SQLITE_THREADSAFE
- mutex = v->db->mutex;
-#endif
- sqlite3_mutex_enter(mutex);
+ sqlite3_mutex_enter(db->mutex);
+ checkProfileCallback(db, v);
rc = sqlite3VdbeFinalize(v);
rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(db);
}
return rc;
}
@@ -61644,12 +75244,14 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt){
rc = SQLITE_OK;
}else{
Vdbe *v = (Vdbe*)pStmt;
- sqlite3_mutex_enter(v->db->mutex);
+ sqlite3 *db = v->db;
+ sqlite3_mutex_enter(db->mutex);
+ checkProfileCallback(db, v);
rc = sqlite3VdbeReset(v);
sqlite3VdbeRewind(v);
- assert( (rc & (v->db->errMask))==rc );
- rc = sqlite3ApiExit(v->db, rc);
- sqlite3_mutex_leave(v->db->mutex);
+ assert( (rc & (db->errMask))==rc );
+ rc = sqlite3ApiExit(db, rc);
+ sqlite3_mutex_leave(db->mutex);
}
return rc;
}
@@ -61684,8 +75286,10 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
SQLITE_API const void *sqlite3_value_blob(sqlite3_value *pVal){
Mem *p = (Mem*)pVal;
if( p->flags & (MEM_Blob|MEM_Str) ){
- sqlite3VdbeMemExpandBlob(p);
- p->flags &= ~MEM_Str;
+ if( ExpandBlob(p)!=SQLITE_OK ){
+ assert( p->flags==MEM_Null && p->z==0 );
+ return 0;
+ }
p->flags |= MEM_Blob;
return p->n ? p->z : 0;
}else{
@@ -61707,6 +75311,10 @@ SQLITE_API int sqlite3_value_int(sqlite3_value *pVal){
SQLITE_API sqlite_int64 sqlite3_value_int64(sqlite3_value *pVal){
return sqlite3VdbeIntValue((Mem*)pVal);
}
+SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value *pVal){
+ Mem *pMem = (Mem*)pVal;
+ return ((pMem->flags & MEM_Subtype) ? pMem->eSubtype : 0);
+}
SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value *pVal){
return (const unsigned char *)sqlite3ValueText(pVal, SQLITE_UTF8);
}
@@ -61721,17 +75329,88 @@ SQLITE_API const void *sqlite3_value_text16le(sqlite3_value *pVal){
return sqlite3ValueText(pVal, SQLITE_UTF16LE);
}
#endif /* SQLITE_OMIT_UTF16 */
+/* EVIDENCE-OF: R-12793-43283 Every value in SQLite has one of five
+** fundamental datatypes: 64-bit signed integer 64-bit IEEE floating
+** point number string BLOB NULL
+*/
SQLITE_API int sqlite3_value_type(sqlite3_value* pVal){
- return pVal->type;
+ static const u8 aType[] = {
+ SQLITE_BLOB, /* 0x00 */
+ SQLITE_NULL, /* 0x01 */
+ SQLITE_TEXT, /* 0x02 */
+ SQLITE_NULL, /* 0x03 */
+ SQLITE_INTEGER, /* 0x04 */
+ SQLITE_NULL, /* 0x05 */
+ SQLITE_INTEGER, /* 0x06 */
+ SQLITE_NULL, /* 0x07 */
+ SQLITE_FLOAT, /* 0x08 */
+ SQLITE_NULL, /* 0x09 */
+ SQLITE_FLOAT, /* 0x0a */
+ SQLITE_NULL, /* 0x0b */
+ SQLITE_INTEGER, /* 0x0c */
+ SQLITE_NULL, /* 0x0d */
+ SQLITE_INTEGER, /* 0x0e */
+ SQLITE_NULL, /* 0x0f */
+ SQLITE_BLOB, /* 0x10 */
+ SQLITE_NULL, /* 0x11 */
+ SQLITE_TEXT, /* 0x12 */
+ SQLITE_NULL, /* 0x13 */
+ SQLITE_INTEGER, /* 0x14 */
+ SQLITE_NULL, /* 0x15 */
+ SQLITE_INTEGER, /* 0x16 */
+ SQLITE_NULL, /* 0x17 */
+ SQLITE_FLOAT, /* 0x18 */
+ SQLITE_NULL, /* 0x19 */
+ SQLITE_FLOAT, /* 0x1a */
+ SQLITE_NULL, /* 0x1b */
+ SQLITE_INTEGER, /* 0x1c */
+ SQLITE_NULL, /* 0x1d */
+ SQLITE_INTEGER, /* 0x1e */
+ SQLITE_NULL, /* 0x1f */
+ };
+ return aType[pVal->flags&MEM_AffMask];
+}
+
+/* Make a copy of an sqlite3_value object
+*/
+SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value *pOrig){
+ sqlite3_value *pNew;
+ if( pOrig==0 ) return 0;
+ pNew = sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return 0;
+ memset(pNew, 0, sizeof(*pNew));
+ memcpy(pNew, pOrig, MEMCELLSIZE);
+ pNew->flags &= ~MEM_Dyn;
+ pNew->db = 0;
+ if( pNew->flags&(MEM_Str|MEM_Blob) ){
+ pNew->flags &= ~(MEM_Static|MEM_Dyn);
+ pNew->flags |= MEM_Ephem;
+ if( sqlite3VdbeMemMakeWriteable(pNew)!=SQLITE_OK ){
+ sqlite3ValueFree(pNew);
+ pNew = 0;
+ }
+ }
+ return pNew;
}
+/* Destroy an sqlite3_value object previously obtained from
+** sqlite3_value_dup().
+*/
+SQLITE_API void sqlite3_value_free(sqlite3_value *pOld){
+ sqlite3ValueFree(pOld);
+}
+
+
/**************************** sqlite3_result_ *******************************
** The following routines are used by user-defined functions to specify
** the function result.
**
-** The setStrOrError() funtion calls sqlite3VdbeMemSetStr() to store the
+** The setStrOrError() function calls sqlite3VdbeMemSetStr() to store the
** result as a string or blob but if the string or blob is too large, it
** then sets the error code to SQLITE_TOOBIG
+**
+** The invokeValueDestructor(P,X) routine invokes destructor function X()
+** on value P is not going to be used and need to be destroyed.
*/
static void setResultStrOrError(
sqlite3_context *pCtx, /* Function context */
@@ -61740,10 +75419,26 @@ static void setResultStrOrError(
u8 enc, /* Encoding of z. 0 for BLOBs */
void (*xDel)(void*) /* Destructor function */
){
- if( sqlite3VdbeMemSetStr(&pCtx->s, z, n, enc, xDel)==SQLITE_TOOBIG ){
+ if( sqlite3VdbeMemSetStr(pCtx->pOut, z, n, enc, xDel)==SQLITE_TOOBIG ){
sqlite3_result_error_toobig(pCtx);
}
}
+static int invokeValueDestructor(
+ const void *p, /* Value to destroy */
+ void (*xDel)(void*), /* The destructor */
+ sqlite3_context *pCtx /* Set a SQLITE_TOOBIG error if no NULL */
+){
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( xDel==0 ){
+ /* noop */
+ }else if( xDel==SQLITE_TRANSIENT ){
+ /* noop */
+ }else{
+ xDel((void*)p);
+ }
+ if( pCtx ) sqlite3_result_error_toobig(pCtx);
+ return SQLITE_TOOBIG;
+}
SQLITE_API void sqlite3_result_blob(
sqlite3_context *pCtx,
const void *z,
@@ -61751,36 +75446,58 @@ SQLITE_API void sqlite3_result_blob(
void (*xDel)(void *)
){
assert( n>=0 );
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, 0, xDel);
}
+SQLITE_API void sqlite3_result_blob64(
+ sqlite3_context *pCtx,
+ const void *z,
+ sqlite3_uint64 n,
+ void (*xDel)(void *)
+){
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( n>0x7fffffff ){
+ (void)invokeValueDestructor(z, xDel, pCtx);
+ }else{
+ setResultStrOrError(pCtx, z, (int)n, 0, xDel);
+ }
+}
SQLITE_API void sqlite3_result_double(sqlite3_context *pCtx, double rVal){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetDouble(&pCtx->s, rVal);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetDouble(pCtx->pOut, rVal);
}
SQLITE_API void sqlite3_result_error(sqlite3_context *pCtx, const char *z, int n){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
- sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
+ pCtx->fErrorOrAux = 1;
+ sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF8, SQLITE_TRANSIENT);
}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_error16(sqlite3_context *pCtx, const void *z, int n){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_ERROR;
- sqlite3VdbeMemSetStr(&pCtx->s, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
+ pCtx->fErrorOrAux = 1;
+ sqlite3VdbeMemSetStr(pCtx->pOut, z, n, SQLITE_UTF16NATIVE, SQLITE_TRANSIENT);
}
#endif
SQLITE_API void sqlite3_result_int(sqlite3_context *pCtx, int iVal){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetInt64(&pCtx->s, (i64)iVal);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetInt64(pCtx->pOut, (i64)iVal);
}
SQLITE_API void sqlite3_result_int64(sqlite3_context *pCtx, i64 iVal){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetInt64(&pCtx->s, iVal);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetInt64(pCtx->pOut, iVal);
}
SQLITE_API void sqlite3_result_null(sqlite3_context *pCtx){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetNull(&pCtx->s);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetNull(pCtx->pOut);
+}
+SQLITE_API void sqlite3_result_subtype(sqlite3_context *pCtx, unsigned int eSubtype){
+ Mem *pOut = pCtx->pOut;
+ assert( sqlite3_mutex_held(pOut->db->mutex) );
+ pOut->eSubtype = eSubtype & 0xff;
+ pOut->flags |= MEM_Subtype;
}
SQLITE_API void sqlite3_result_text(
sqlite3_context *pCtx,
@@ -61788,9 +75505,25 @@ SQLITE_API void sqlite3_result_text(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF8, xDel);
}
+SQLITE_API void sqlite3_result_text64(
+ sqlite3_context *pCtx,
+ const char *z,
+ sqlite3_uint64 n,
+ void (*xDel)(void *),
+ unsigned char enc
+){
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ if( n>0x7fffffff ){
+ (void)invokeValueDestructor(z, xDel, pCtx);
+ }else{
+ setResultStrOrError(pCtx, z, (int)n, enc, xDel);
+ }
+}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API void sqlite3_result_text16(
sqlite3_context *pCtx,
@@ -61798,7 +75531,7 @@ SQLITE_API void sqlite3_result_text16(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF16NATIVE, xDel);
}
SQLITE_API void sqlite3_result_text16be(
@@ -61807,7 +75540,7 @@ SQLITE_API void sqlite3_result_text16be(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF16BE, xDel);
}
SQLITE_API void sqlite3_result_text16le(
@@ -61816,40 +75549,55 @@ SQLITE_API void sqlite3_result_text16le(
int n,
void (*xDel)(void *)
){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
setResultStrOrError(pCtx, z, n, SQLITE_UTF16LE, xDel);
}
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API void sqlite3_result_value(sqlite3_context *pCtx, sqlite3_value *pValue){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemCopy(&pCtx->s, pValue);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemCopy(pCtx->pOut, pValue);
}
SQLITE_API void sqlite3_result_zeroblob(sqlite3_context *pCtx, int n){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetZeroBlob(&pCtx->s, n);
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetZeroBlob(pCtx->pOut, n);
+}
+SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context *pCtx, u64 n){
+ Mem *pOut = pCtx->pOut;
+ assert( sqlite3_mutex_held(pOut->db->mutex) );
+ if( n>(u64)pOut->db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ return SQLITE_TOOBIG;
+ }
+ sqlite3VdbeMemSetZeroBlob(pCtx->pOut, (int)n);
+ return SQLITE_OK;
}
SQLITE_API void sqlite3_result_error_code(sqlite3_context *pCtx, int errCode){
pCtx->isError = errCode;
- if( pCtx->s.flags & MEM_Null ){
- sqlite3VdbeMemSetStr(&pCtx->s, sqlite3ErrStr(errCode), -1,
+ pCtx->fErrorOrAux = 1;
+#ifdef SQLITE_DEBUG
+ if( pCtx->pVdbe ) pCtx->pVdbe->rcApp = errCode;
+#endif
+ if( pCtx->pOut->flags & MEM_Null ){
+ sqlite3VdbeMemSetStr(pCtx->pOut, sqlite3ErrStr(errCode), -1,
SQLITE_UTF8, SQLITE_STATIC);
}
}
/* Force an SQLITE_TOOBIG error. */
SQLITE_API void sqlite3_result_error_toobig(sqlite3_context *pCtx){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
pCtx->isError = SQLITE_TOOBIG;
- sqlite3VdbeMemSetStr(&pCtx->s, "string or blob too big", -1,
+ pCtx->fErrorOrAux = 1;
+ sqlite3VdbeMemSetStr(pCtx->pOut, "string or blob too big", -1,
SQLITE_UTF8, SQLITE_STATIC);
}
/* An SQLITE_NOMEM error. */
SQLITE_API void sqlite3_result_error_nomem(sqlite3_context *pCtx){
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- sqlite3VdbeMemSetNull(&pCtx->s);
- pCtx->isError = SQLITE_NOMEM;
- pCtx->s.db->mallocFailed = 1;
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+ sqlite3VdbeMemSetNull(pCtx->pOut);
+ pCtx->isError = SQLITE_NOMEM_BKPT;
+ pCtx->fErrorOrAux = 1;
+ sqlite3OomFault(pCtx->pOut->db);
}
/*
@@ -61863,9 +75611,12 @@ static int doWalCallbacks(sqlite3 *db){
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
- int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
+ int nEntry;
+ sqlite3BtreeEnter(pBt);
+ nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
+ sqlite3BtreeLeave(pBt);
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK ){
- rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry);
+ rc = db->xWalCallback(db->pWalArg, db, db->aDb[i].zDbSName, nEntry);
}
}
}
@@ -61873,6 +75624,7 @@ static int doWalCallbacks(sqlite3 *db){
return rc;
}
+
/*
** Execute the statement pStmt, either until a row of data is ready, the
** statement is completely executed or an error occurs.
@@ -61898,14 +75650,14 @@ static int sqlite3Step(Vdbe *p){
**
** Nevertheless, some published applications that were originally written
** for version 3.6.23 or earlier do in fact depend on SQLITE_MISUSE
- ** returns, and the so were broken by the automatic-reset change. As a
+ ** returns, and those were broken by the automatic-reset change. As a
** a work-around, the SQLITE_OMIT_AUTORESET compile-time restores the
** legacy behavior of returning SQLITE_MISUSE for cases where the
** previous sqlite3_step() returned something other than a SQLITE_LOCKED
** or SQLITE_BUSY error.
*/
#ifdef SQLITE_OMIT_AUTORESET
- if( p->rc==SQLITE_BUSY || p->rc==SQLITE_LOCKED ){
+ if( (rc = p->rc&0xff)==SQLITE_BUSY || rc==SQLITE_LOCKED ){
sqlite3_reset((sqlite3_stmt*)p);
}else{
return SQLITE_MISUSE_BKPT;
@@ -61919,7 +75671,7 @@ static int sqlite3Step(Vdbe *p){
db = p->db;
if( db->mallocFailed ){
p->rc = SQLITE_NOMEM;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
if( p->pc<=0 && p->expired ){
@@ -61932,41 +75684,45 @@ static int sqlite3Step(Vdbe *p){
** reset the interrupt flag. This prevents a call to sqlite3_interrupt
** from interrupting a statement that has not yet started.
*/
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
- assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 );
+ assert( db->nVdbeWrite>0 || db->autoCommit==0
+ || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
+ );
#ifndef SQLITE_OMIT_TRACE
- if( db->xProfile && !db->init.busy ){
+ if( (db->xProfile || (db->mTrace & SQLITE_TRACE_PROFILE)!=0)
+ && !db->init.busy && p->zSql ){
sqlite3OsCurrentTimeInt64(db->pVfs, &p->startTime);
+ }else{
+ assert( p->startTime==0 );
}
#endif
- db->activeVdbeCnt++;
- if( p->readOnly==0 ) db->writeVdbeCnt++;
+ db->nVdbeActive++;
+ if( p->readOnly==0 ) db->nVdbeWrite++;
+ if( p->bIsReader ) db->nVdbeRead++;
p->pc = 0;
}
+#ifdef SQLITE_DEBUG
+ p->rcApp = SQLITE_OK;
+#endif
#ifndef SQLITE_OMIT_EXPLAIN
if( p->explain ){
rc = sqlite3VdbeList(p);
}else
#endif /* SQLITE_OMIT_EXPLAIN */
{
- db->vdbeExecCnt++;
+ db->nVdbeExec++;
rc = sqlite3VdbeExec(p);
- db->vdbeExecCnt--;
+ db->nVdbeExec--;
}
#ifndef SQLITE_OMIT_TRACE
- /* Invoke the profile callback if there is one
- */
- if( rc!=SQLITE_ROW && db->xProfile && !db->init.busy && p->zSql ){
- sqlite3_int64 iNow;
- sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
- db->xProfile(db->pProfileArg, p->zSql, (iNow - p->startTime)*1000000);
- }
+ /* If the statement completed successfully, invoke the profile callback */
+ if( rc!=SQLITE_ROW ) checkProfileCallback(db, p);
#endif
if( rc==SQLITE_DONE ){
@@ -61979,7 +75735,7 @@ static int sqlite3Step(Vdbe *p){
db->errCode = rc;
if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
- p->rc = SQLITE_NOMEM;
+ p->rc = SQLITE_NOMEM_BKPT;
}
end_of_step:
/* At this point local variable rc holds the value that should be
@@ -61990,28 +75746,20 @@ end_of_step:
** were called on statement p.
*/
assert( rc==SQLITE_ROW || rc==SQLITE_DONE || rc==SQLITE_ERROR
- || rc==SQLITE_BUSY || rc==SQLITE_MISUSE
+ || (rc&0xff)==SQLITE_BUSY || rc==SQLITE_MISUSE
);
- assert( p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE );
+ assert( (p->rc!=SQLITE_ROW && p->rc!=SQLITE_DONE) || p->rc==p->rcApp );
if( p->isPrepareV2 && rc!=SQLITE_ROW && rc!=SQLITE_DONE ){
/* If this statement was prepared using sqlite3_prepare_v2(), and an
- ** error has occured, then return the error code in p->rc to the
+ ** error has occurred, then return the error code in p->rc to the
** caller. Set the error code in the database handle to the same value.
*/
- rc = db->errCode = p->rc;
+ rc = sqlite3VdbeTransferError(p);
}
return (rc&db->errMask);
}
/*
-** The maximum number of times that a statement will try to reparse
-** itself before giving up and returning SQLITE_SCHEMA.
-*/
-#ifndef SQLITE_MAX_SCHEMA_RETRY
-# define SQLITE_MAX_SCHEMA_RETRY 5
-#endif
-
-/*
** This is the top-level implementation of sqlite3_step(). Call
** sqlite3Step() to do most of the work. If a schema error occurs,
** call sqlite3Reprepare() and try again.
@@ -62028,13 +75776,17 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
}
db = v->db;
sqlite3_mutex_enter(db->mutex);
+ v->doingRerun = 0;
while( (rc = sqlite3Step(v))==SQLITE_SCHEMA
- && cnt++ < SQLITE_MAX_SCHEMA_RETRY
- && (rc2 = rc = sqlite3Reprepare(v))==SQLITE_OK ){
+ && cnt++ < SQLITE_MAX_SCHEMA_RETRY ){
+ int savedPc = v->pc;
+ rc2 = rc = sqlite3Reprepare(v);
+ if( rc!=SQLITE_OK) break;
sqlite3_reset(pStmt);
+ if( savedPc>=0 ) v->doingRerun = 1;
assert( v->expired==0 );
}
- if( rc2!=SQLITE_OK && ALWAYS(v->isPrepareV2) && ALWAYS(db->pErr) ){
+ if( rc2!=SQLITE_OK ){
/* This case occurs after failing to recompile an sql statement.
** The error message from the SQL compiler has already been loaded
** into the database handle. This block copies the error message
@@ -62050,7 +75802,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
v->rc = rc2;
} else {
v->zErrMsg = 0;
- v->rc = rc = SQLITE_NOMEM;
+ v->rc = rc = SQLITE_NOMEM_BKPT;
}
}
rc = sqlite3ApiExit(db, rc);
@@ -62058,6 +75810,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt *pStmt){
return rc;
}
+
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
@@ -62078,8 +75831,31 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
** application defined function.
*/
SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context *p){
- assert( p && p->pFunc );
- return p->s.db;
+ assert( p && p->pOut );
+ return p->pOut->db;
+}
+
+/*
+** Return the current time for a statement. If the current time
+** is requested more than once within the same run of a single prepared
+** statement, the exact same time is returned for each invocation regardless
+** of the amount of time that elapses between invocations. In other words,
+** the time returned is always the time of the first call.
+*/
+SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context *p){
+ int rc;
+#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3_int64 *piTime = &p->pVdbe->iCurrentTime;
+ assert( p->pVdbe!=0 );
+#else
+ sqlite3_int64 iTime = 0;
+ sqlite3_int64 *piTime = p->pVdbe!=0 ? &p->pVdbe->iCurrentTime : &iTime;
+#endif
+ if( *piTime==0 ){
+ rc = sqlite3OsCurrentTimeInt64(p->pOut->db->pVfs, piTime);
+ if( rc ) *piTime = 0;
+ }
+ return *piTime;
}
/*
@@ -62105,50 +75881,64 @@ SQLITE_PRIVATE void sqlite3InvalidFunction(
}
/*
+** Create a new aggregate context for p and return a pointer to
+** its pMem->z element.
+*/
+static SQLITE_NOINLINE void *createAggContext(sqlite3_context *p, int nByte){
+ Mem *pMem = p->pMem;
+ assert( (pMem->flags & MEM_Agg)==0 );
+ if( nByte<=0 ){
+ sqlite3VdbeMemSetNull(pMem);
+ pMem->z = 0;
+ }else{
+ sqlite3VdbeMemClearAndResize(pMem, nByte);
+ pMem->flags = MEM_Agg;
+ pMem->u.pDef = p->pFunc;
+ if( pMem->z ){
+ memset(pMem->z, 0, nByte);
+ }
+ }
+ return (void*)pMem->z;
+}
+
+/*
** Allocate or return the aggregate context for a user function. A new
** context is allocated on the first call. Subsequent calls return the
** same context that was returned on prior calls.
*/
SQLITE_API void *sqlite3_aggregate_context(sqlite3_context *p, int nByte){
- Mem *pMem;
- assert( p && p->pFunc && p->pFunc->xStep );
- assert( sqlite3_mutex_held(p->s.db->mutex) );
- pMem = p->pMem;
+ assert( p && p->pFunc && p->pFunc->xFinalize );
+ assert( sqlite3_mutex_held(p->pOut->db->mutex) );
testcase( nByte<0 );
- if( (pMem->flags & MEM_Agg)==0 ){
- if( nByte<=0 ){
- sqlite3VdbeMemReleaseExternal(pMem);
- pMem->flags = MEM_Null;
- pMem->z = 0;
- }else{
- sqlite3VdbeMemGrow(pMem, nByte, 0);
- pMem->flags = MEM_Agg;
- pMem->u.pDef = p->pFunc;
- if( pMem->z ){
- memset(pMem->z, 0, nByte);
- }
- }
+ if( (p->pMem->flags & MEM_Agg)==0 ){
+ return createAggContext(p, nByte);
+ }else{
+ return (void*)p->pMem->z;
}
- return (void*)pMem->z;
}
/*
-** Return the auxilary data pointer, if any, for the iArg'th argument to
+** Return the auxiliary data pointer, if any, for the iArg'th argument to
** the user-function defined by pCtx.
*/
SQLITE_API void *sqlite3_get_auxdata(sqlite3_context *pCtx, int iArg){
- VdbeFunc *pVdbeFunc;
+ AuxData *pAuxData;
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- pVdbeFunc = pCtx->pVdbeFunc;
- if( !pVdbeFunc || iArg>=pVdbeFunc->nAux || iArg<0 ){
- return 0;
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
+#if SQLITE_ENABLE_STAT3_OR_STAT4
+ if( pCtx->pVdbe==0 ) return 0;
+#else
+ assert( pCtx->pVdbe!=0 );
+#endif
+ for(pAuxData=pCtx->pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
}
- return pVdbeFunc->apAux[iArg].pAux;
+
+ return (pAuxData ? pAuxData->pAux : 0);
}
/*
-** Set the auxilary data pointer and delete function, for the iArg'th
+** Set the auxiliary data pointer and delete function, for the iArg'th
** argument to the user-function defined by pCtx. Any previous value is
** deleted by calling the delete function specified when it was set.
*/
@@ -62158,29 +75948,35 @@ SQLITE_API void sqlite3_set_auxdata(
void *pAux,
void (*xDelete)(void*)
){
- struct AuxData *pAuxData;
- VdbeFunc *pVdbeFunc;
+ AuxData *pAuxData;
+ Vdbe *pVdbe = pCtx->pVdbe;
+
+ assert( sqlite3_mutex_held(pCtx->pOut->db->mutex) );
if( iArg<0 ) goto failed;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( pVdbe==0 ) goto failed;
+#else
+ assert( pVdbe!=0 );
+#endif
- assert( sqlite3_mutex_held(pCtx->s.db->mutex) );
- pVdbeFunc = pCtx->pVdbeFunc;
- if( !pVdbeFunc || pVdbeFunc->nAux<=iArg ){
- int nAux = (pVdbeFunc ? pVdbeFunc->nAux : 0);
- int nMalloc = sizeof(VdbeFunc) + sizeof(struct AuxData)*iArg;
- pVdbeFunc = sqlite3DbRealloc(pCtx->s.db, pVdbeFunc, nMalloc);
- if( !pVdbeFunc ){
- goto failed;
- }
- pCtx->pVdbeFunc = pVdbeFunc;
- memset(&pVdbeFunc->apAux[nAux], 0, sizeof(struct AuxData)*(iArg+1-nAux));
- pVdbeFunc->nAux = iArg+1;
- pVdbeFunc->pFunc = pCtx->pFunc;
+ for(pAuxData=pVdbe->pAuxData; pAuxData; pAuxData=pAuxData->pNext){
+ if( pAuxData->iOp==pCtx->iOp && pAuxData->iArg==iArg ) break;
}
-
- pAuxData = &pVdbeFunc->apAux[iArg];
- if( pAuxData->pAux && pAuxData->xDelete ){
+ if( pAuxData==0 ){
+ pAuxData = sqlite3DbMallocZero(pVdbe->db, sizeof(AuxData));
+ if( !pAuxData ) goto failed;
+ pAuxData->iOp = pCtx->iOp;
+ pAuxData->iArg = iArg;
+ pAuxData->pNext = pVdbe->pAuxData;
+ pVdbe->pAuxData = pAuxData;
+ if( pCtx->fErrorOrAux==0 ){
+ pCtx->isError = 0;
+ pCtx->fErrorOrAux = 1;
+ }
+ }else if( pAuxData->xDelete ){
pAuxData->xDelete(pAuxData->pAux);
}
+
pAuxData->pAux = pAux;
pAuxData->xDelete = xDelete;
return;
@@ -62193,7 +75989,7 @@ failed:
#ifndef SQLITE_OMIT_DEPRECATED
/*
-** Return the number of times the Step function of a aggregate has been
+** Return the number of times the Step function of an aggregate has been
** called.
**
** This function is deprecated. Do not use it for new code. It is
@@ -62202,7 +75998,7 @@ failed:
** context.
*/
SQLITE_API int sqlite3_aggregate_count(sqlite3_context *p){
- assert( p && p->pMem && p->pFunc && p->pFunc->xStep );
+ assert( p && p->pMem && p->pFunc && p->pFunc->xFinalize );
return p->pMem->n;
}
#endif
@@ -62225,6 +76021,42 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt){
return pVm->nResColumn;
}
+/*
+** Return a pointer to static memory containing an SQL NULL value.
+*/
+static const Mem *columnNullValue(void){
+ /* Even though the Mem structure contains an element
+ ** of type i64, on certain architectures (x86) with certain compiler
+ ** switches (-Os), gcc may align this Mem object on a 4-byte boundary
+ ** instead of an 8-byte one. This all works fine, except that when
+ ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s
+ ** that a Mem structure is located on an 8-byte boundary. To prevent
+ ** these assert()s from failing, when building with SQLITE_DEBUG defined
+ ** using gcc, we force nullMem to be 8-byte aligned using the magical
+ ** __attribute__((aligned(8))) macro. */
+ static const Mem nullMem
+#if defined(SQLITE_DEBUG) && defined(__GNUC__)
+ __attribute__((aligned(8)))
+#endif
+ = {
+ /* .u = */ {0},
+ /* .flags = */ (u16)MEM_Null,
+ /* .enc = */ (u8)0,
+ /* .eSubtype = */ (u8)0,
+ /* .n = */ (int)0,
+ /* .z = */ (char*)0,
+ /* .zMalloc = */ (char*)0,
+ /* .szMalloc = */ (int)0,
+ /* .uTemp = */ (u32)0,
+ /* .db = */ (sqlite3*)0,
+ /* .xDel = */ (void(*)(void*))0,
+#ifdef SQLITE_DEBUG
+ /* .pScopyFrom = */ (Mem*)0,
+ /* .pFiller = */ (void*)0,
+#endif
+ };
+ return &nullMem;
+}
/*
** Check to see if column iCol of the given statement is valid. If
@@ -62237,36 +76069,14 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
Mem *pOut;
pVm = (Vdbe *)pStmt;
- if( pVm && pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
- sqlite3_mutex_enter(pVm->db->mutex);
+ if( pVm==0 ) return (Mem*)columnNullValue();
+ assert( pVm->db );
+ sqlite3_mutex_enter(pVm->db->mutex);
+ if( pVm->pResultSet!=0 && i<pVm->nResColumn && i>=0 ){
pOut = &pVm->pResultSet[i];
}else{
- /* If the value passed as the second argument is out of range, return
- ** a pointer to the following static Mem object which contains the
- ** value SQL NULL. Even though the Mem structure contains an element
- ** of type i64, on certain architecture (x86) with certain compiler
- ** switches (-Os), gcc may align this Mem object on a 4-byte boundary
- ** instead of an 8-byte one. This all works fine, except that when
- ** running with SQLITE_DEBUG defined the SQLite code sometimes assert()s
- ** that a Mem structure is located on an 8-byte boundary. To prevent
- ** this assert() from failing, when building with SQLITE_DEBUG defined
- ** using gcc, force nullMem to be 8-byte aligned using the magical
- ** __attribute__((aligned(8))) macro. */
- static const Mem nullMem
-#if defined(SQLITE_DEBUG) && defined(__GNUC__)
- __attribute__((aligned(8)))
-#endif
- = {0, "", (double)0, {0}, 0, MEM_Null, SQLITE_NULL, 0,
-#ifdef SQLITE_DEBUG
- 0, 0, /* pScopyFrom, pFiller */
-#endif
- 0, 0 };
-
- if( pVm && ALWAYS(pVm->db) ){
- sqlite3_mutex_enter(pVm->db->mutex);
- sqlite3Error(pVm->db, SQLITE_RANGE, 0);
- }
- pOut = (Mem*)&nullMem;
+ sqlite3Error(pVm->db, SQLITE_RANGE);
+ pOut = (Mem*)columnNullValue();
}
return pOut;
}
@@ -62298,6 +76108,8 @@ static void columnMallocFailure(sqlite3_stmt *pStmt)
*/
Vdbe *p = (Vdbe *)pStmt;
if( p ){
+ assert( p->db!=0 );
+ assert( sqlite3_mutex_held(p->db->mutex) );
p->rc = sqlite3ApiExit(p->db, p->rc);
sqlite3_mutex_leave(p->db->mutex);
}
@@ -62369,13 +76181,6 @@ SQLITE_API int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
return iType;
}
-/* The following function is experimental and subject to change or
-** removal */
-/*int sqlite3_column_numeric_type(sqlite3_stmt *pStmt, int i){
-** return sqlite3_value_numeric_type( columnMem(pStmt,i) );
-**}
-*/
-
/*
** Convert the N-th element of pStmt->pColName[] into a string using
** xFunc() then return that string. If N is out of range, return 0.
@@ -62398,11 +76203,19 @@ static const void *columnName(
const void *(*xFunc)(Mem*),
int useType
){
- const void *ret = 0;
- Vdbe *p = (Vdbe *)pStmt;
+ const void *ret;
+ Vdbe *p;
int n;
- sqlite3 *db = p->db;
-
+ sqlite3 *db;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( pStmt==0 ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+ ret = 0;
+ p = (Vdbe *)pStmt;
+ db = p->db;
assert( db!=0 );
n = sqlite3_column_count(pStmt);
if( N<n && N>=0 ){
@@ -62414,7 +76227,7 @@ static const void *columnName(
** is the case, clear the mallocFailed flag and return NULL.
*/
if( db->mallocFailed ){
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
ret = 0;
}
sqlite3_mutex_leave(db->mutex);
@@ -62467,7 +76280,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt *pStmt, int N){
/*
** Return the name of the database from which a result column derives.
** NULL is returned if the result column is an expression or constant or
-** anything else which is not an unabiguous reference to a database column.
+** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt *pStmt, int N){
return columnName(
@@ -62483,7 +76296,7 @@ SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt *pStmt, int N
/*
** Return the name of the table from which a result column derives.
** NULL is returned if the result column is an expression or constant or
-** anything else which is not an unabiguous reference to a database column.
+** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt *pStmt, int N){
return columnName(
@@ -62499,7 +76312,7 @@ SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt *pStmt, int N){
/*
** Return the name of the table column from which a result column derives.
** NULL is returned if the result column is an expression or constant or
-** anything else which is not an unabiguous reference to a database column.
+** anything else which is not an unambiguous reference to a database column.
*/
SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt *pStmt, int N){
return columnName(
@@ -62536,14 +76349,14 @@ static int vdbeUnbind(Vdbe *p, int i){
}
sqlite3_mutex_enter(p->db->mutex);
if( p->magic!=VDBE_MAGIC_RUN || p->pc>=0 ){
- sqlite3Error(p->db, SQLITE_MISUSE, 0);
+ sqlite3Error(p->db, SQLITE_MISUSE);
sqlite3_mutex_leave(p->db->mutex);
sqlite3_log(SQLITE_MISUSE,
"bind on a busy prepared statement: [%s]", p->zSql);
return SQLITE_MISUSE_BKPT;
}
if( i<1 || i>p->nVar ){
- sqlite3Error(p->db, SQLITE_RANGE, 0);
+ sqlite3Error(p->db, SQLITE_RANGE);
sqlite3_mutex_leave(p->db->mutex);
return SQLITE_RANGE;
}
@@ -62551,7 +76364,7 @@ static int vdbeUnbind(Vdbe *p, int i){
pVar = &p->aVar[i];
sqlite3VdbeMemRelease(pVar);
pVar->flags = MEM_Null;
- sqlite3Error(p->db, SQLITE_OK, 0);
+ sqlite3Error(p->db, SQLITE_OK);
/* If the bit corresponding to this variable in Vdbe.expmask is set, then
** binding a new value to this variable invalidates the current query plan.
@@ -62593,7 +76406,7 @@ static int bindText(
if( rc==SQLITE_OK && encoding!=0 ){
rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
}
- sqlite3Error(p->db, rc, 0);
+ sqlite3Error(p->db, rc);
rc = sqlite3ApiExit(p->db, rc);
}
sqlite3_mutex_leave(p->db->mutex);
@@ -62614,8 +76427,25 @@ SQLITE_API int sqlite3_bind_blob(
int nData,
void (*xDel)(void*)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( nData<0 ) return SQLITE_MISUSE_BKPT;
+#endif
return bindText(pStmt, i, zData, nData, xDel, 0);
}
+SQLITE_API int sqlite3_bind_blob64(
+ sqlite3_stmt *pStmt,
+ int i,
+ const void *zData,
+ sqlite3_uint64 nData,
+ void (*xDel)(void*)
+){
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( nData>0x7fffffff ){
+ return invokeValueDestructor(zData, xDel, 0);
+ }else{
+ return bindText(pStmt, i, zData, (int)nData, xDel, 0);
+ }
+}
SQLITE_API int sqlite3_bind_double(sqlite3_stmt *pStmt, int i, double rValue){
int rc;
Vdbe *p = (Vdbe *)pStmt;
@@ -62657,6 +76487,22 @@ SQLITE_API int sqlite3_bind_text(
){
return bindText(pStmt, i, zData, nData, xDel, SQLITE_UTF8);
}
+SQLITE_API int sqlite3_bind_text64(
+ sqlite3_stmt *pStmt,
+ int i,
+ const char *zData,
+ sqlite3_uint64 nData,
+ void (*xDel)(void*),
+ unsigned char enc
+){
+ assert( xDel!=SQLITE_DYNAMIC );
+ if( nData>0x7fffffff ){
+ return invokeValueDestructor(zData, xDel, 0);
+ }else{
+ if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
+ return bindText(pStmt, i, zData, (int)nData, xDel, enc);
+ }
+}
#ifndef SQLITE_OMIT_UTF16
SQLITE_API int sqlite3_bind_text16(
sqlite3_stmt *pStmt,
@@ -62670,13 +76516,13 @@ SQLITE_API int sqlite3_bind_text16(
#endif /* SQLITE_OMIT_UTF16 */
SQLITE_API int sqlite3_bind_value(sqlite3_stmt *pStmt, int i, const sqlite3_value *pValue){
int rc;
- switch( pValue->type ){
+ switch( sqlite3_value_type((sqlite3_value*)pValue) ){
case SQLITE_INTEGER: {
rc = sqlite3_bind_int64(pStmt, i, pValue->u.i);
break;
}
case SQLITE_FLOAT: {
- rc = sqlite3_bind_double(pStmt, i, pValue->r);
+ rc = sqlite3_bind_double(pStmt, i, pValue->u.r);
break;
}
case SQLITE_BLOB: {
@@ -62709,6 +76555,20 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt *pStmt, int i, int n){
}
return rc;
}
+SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt *pStmt, int i, sqlite3_uint64 n){
+ int rc;
+ Vdbe *p = (Vdbe *)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
+ if( n>(u64)p->db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ rc = SQLITE_TOOBIG;
+ }else{
+ assert( (n & 0x7FFFFFFF)==n );
+ rc = sqlite3_bind_zeroblob(pStmt, i, n);
+ }
+ rc = sqlite3ApiExit(p->db, rc);
+ sqlite3_mutex_leave(p->db->mutex);
+ return rc;
+}
/*
** Return the number of wildcards that can be potentially bound to.
@@ -62746,7 +76606,7 @@ SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nNa
if( zName ){
for(i=0; i<p->nzVar; i++){
const char *z = p->azVar[i];
- if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){
+ if( z && strncmp(z,zName,nName)==0 && z[nName]==0 ){
return i+1;
}
}
@@ -62779,7 +76639,7 @@ SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *pFromStmt, sqlite3_stmt
** Deprecated external interface. Internal/core SQLite code
** should call sqlite3TransferBindings.
**
-** Is is misuse to call this routine with statements from different
+** It is misuse to call this routine with statements from different
** database connections. But as this is a deprecated interface, we
** will not bother to check for that condition.
**
@@ -62822,6 +76682,14 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
}
/*
+** Return true if the prepared statement is in need of being reset.
+*/
+SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt *pStmt){
+ Vdbe *v = (Vdbe*)pStmt;
+ return v!=0 && v->magic==VDBE_MAGIC_RUN && v->pc>=0;
+}
+
+/*
** Return a pointer to the next prepared statement after pStmt associated
** with database connection pDb. If pStmt is NULL, return the first
** prepared statement for the database connection. Return NULL if there
@@ -62829,6 +76697,12 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt){
*/
SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
sqlite3_stmt *pNext;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(pDb) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(pDb->mutex);
if( pStmt==0 ){
pNext = (sqlite3_stmt*)pDb->pVdbe;
@@ -62844,10 +76718,304 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt){
*/
SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
Vdbe *pVdbe = (Vdbe*)pStmt;
- int v = pVdbe->aCounter[op-1];
- if( resetFlag ) pVdbe->aCounter[op-1] = 0;
- return v;
+ u32 v;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !pStmt ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+ v = pVdbe->aCounter[op];
+ if( resetFlag ) pVdbe->aCounter[op] = 0;
+ return (int)v;
+}
+
+/*
+** Return the SQL associated with a prepared statement
+*/
+SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt){
+ Vdbe *p = (Vdbe *)pStmt;
+ return p ? p->zSql : 0;
+}
+
+/*
+** Return the SQL associated with a prepared statement with
+** bound parameters expanded. Space to hold the returned string is
+** obtained from sqlite3_malloc(). The caller is responsible for
+** freeing the returned string by passing it to sqlite3_free().
+**
+** The SQLITE_TRACE_SIZE_LIMIT puts an upper bound on the size of
+** expanded bound parameters.
+*/
+SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt){
+#ifdef SQLITE_OMIT_TRACE
+ return 0;
+#else
+ char *z = 0;
+ const char *zSql = sqlite3_sql(pStmt);
+ if( zSql ){
+ Vdbe *p = (Vdbe *)pStmt;
+ sqlite3_mutex_enter(p->db->mutex);
+ z = sqlite3VdbeExpandSql(p, zSql);
+ sqlite3_mutex_leave(p->db->mutex);
+ }
+ return z;
+#endif
+}
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** Allocate and populate an UnpackedRecord structure based on the serialized
+** record in nKey/pKey. Return a pointer to the new UnpackedRecord structure
+** if successful, or a NULL pointer if an OOM error is encountered.
+*/
+static UnpackedRecord *vdbeUnpackRecord(
+ KeyInfo *pKeyInfo,
+ int nKey,
+ const void *pKey
+){
+ char *dummy; /* Dummy argument for AllocUnpackedRecord() */
+ UnpackedRecord *pRet; /* Return value */
+
+ pRet = sqlite3VdbeAllocUnpackedRecord(pKeyInfo, 0, 0, &dummy);
+ if( pRet ){
+ memset(pRet->aMem, 0, sizeof(Mem)*(pKeyInfo->nField+1));
+ sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, pRet);
+ }
+ return pRet;
+}
+
+/*
+** This function is called from within a pre-update callback to retrieve
+** a field of the row currently being updated or deleted.
+*/
+SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
+ PreUpdate *p = db->pPreUpdate;
+ int rc = SQLITE_OK;
+
+ /* Test that this call is being made from within an SQLITE_DELETE or
+ ** SQLITE_UPDATE pre-update callback, and that iIdx is within range. */
+ if( !p || p->op==SQLITE_INSERT ){
+ rc = SQLITE_MISUSE_BKPT;
+ goto preupdate_old_out;
+ }
+ if( iIdx>=p->pCsr->nField || iIdx<0 ){
+ rc = SQLITE_RANGE;
+ goto preupdate_old_out;
+ }
+
+ /* If the old.* record has not yet been loaded into memory, do so now. */
+ if( p->pUnpacked==0 ){
+ u32 nRec;
+ u8 *aRec;
+
+ nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
+ aRec = sqlite3DbMallocRaw(db, nRec);
+ if( !aRec ) goto preupdate_old_out;
+ rc = sqlite3BtreeData(p->pCsr->uc.pCursor, 0, nRec, aRec);
+ if( rc==SQLITE_OK ){
+ p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
+ if( !p->pUnpacked ) rc = SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3DbFree(db, aRec);
+ goto preupdate_old_out;
+ }
+ p->aRecord = aRec;
+ }
+
+ if( iIdx>=p->pUnpacked->nField ){
+ *ppValue = (sqlite3_value *)columnNullValue();
+ }else{
+ Mem *pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
+ *ppValue = &p->pUnpacked->aMem[iIdx];
+ if( iIdx==p->pTab->iPKey ){
+ sqlite3VdbeMemSetInt64(pMem, p->iKey1);
+ }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
+ if( pMem->flags & MEM_Int ){
+ sqlite3VdbeMemRealify(pMem);
+ }
+ }
+ }
+
+ preupdate_old_out:
+ sqlite3Error(db, rc);
+ return sqlite3ApiExit(db, rc);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** This function is called from within a pre-update callback to retrieve
+** the number of columns in the row being updated, deleted or inserted.
+*/
+SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
+ PreUpdate *p = db->pPreUpdate;
+ return (p ? p->keyinfo.nField : 0);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** This function is designed to be called from within a pre-update callback
+** only. It returns zero if the change that caused the callback was made
+** immediately by a user SQL statement. Or, if the change was made by a
+** trigger program, it returns the number of trigger programs currently
+** on the stack (1 for a top-level trigger, 2 for a trigger fired by a
+** top-level trigger etc.).
+**
+** For the purposes of the previous paragraph, a foreign key CASCADE, SET NULL
+** or SET DEFAULT action is considered a trigger.
+*/
+SQLITE_API int sqlite3_preupdate_depth(sqlite3 *db){
+ PreUpdate *p = db->pPreUpdate;
+ return (p ? p->v->nFrame : 0);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** This function is called from within a pre-update callback to retrieve
+** a field of the row currently being updated or inserted.
+*/
+SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppValue){
+ PreUpdate *p = db->pPreUpdate;
+ int rc = SQLITE_OK;
+ Mem *pMem;
+
+ if( !p || p->op==SQLITE_DELETE ){
+ rc = SQLITE_MISUSE_BKPT;
+ goto preupdate_new_out;
+ }
+ if( iIdx>=p->pCsr->nField || iIdx<0 ){
+ rc = SQLITE_RANGE;
+ goto preupdate_new_out;
+ }
+
+ if( p->op==SQLITE_INSERT ){
+ /* For an INSERT, memory cell p->iNewReg contains the serialized record
+ ** that is being inserted. Deserialize it. */
+ UnpackedRecord *pUnpack = p->pNewUnpacked;
+ if( !pUnpack ){
+ Mem *pData = &p->v->aMem[p->iNewReg];
+ rc = ExpandBlob(pData);
+ if( rc!=SQLITE_OK ) goto preupdate_new_out;
+ pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
+ if( !pUnpack ){
+ rc = SQLITE_NOMEM;
+ goto preupdate_new_out;
+ }
+ p->pNewUnpacked = pUnpack;
+ }
+ if( iIdx>=pUnpack->nField ){
+ pMem = (sqlite3_value *)columnNullValue();
+ }else{
+ pMem = &pUnpack->aMem[iIdx];
+ if( iIdx==p->pTab->iPKey ){
+ sqlite3VdbeMemSetInt64(pMem, p->iKey2);
+ }
+ }
+ }else{
+ /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required
+ ** value. Make a copy of the cell contents and return a pointer to it.
+ ** It is not safe to return a pointer to the memory cell itself as the
+ ** caller may modify the value text encoding.
+ */
+ assert( p->op==SQLITE_UPDATE );
+ if( !p->aNew ){
+ p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField);
+ if( !p->aNew ){
+ rc = SQLITE_NOMEM;
+ goto preupdate_new_out;
+ }
+ }
+ assert( iIdx>=0 && iIdx<p->pCsr->nField );
+ pMem = &p->aNew[iIdx];
+ if( pMem->flags==0 ){
+ if( iIdx==p->pTab->iPKey ){
+ sqlite3VdbeMemSetInt64(pMem, p->iKey2);
+ }else{
+ rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]);
+ if( rc!=SQLITE_OK ) goto preupdate_new_out;
+ }
+ }
+ }
+ *ppValue = pMem;
+
+ preupdate_new_out:
+ sqlite3Error(db, rc);
+ return sqlite3ApiExit(db, rc);
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+/*
+** Return status data for a single loop within query pStmt.
+*/
+SQLITE_API int sqlite3_stmt_scanstatus(
+ sqlite3_stmt *pStmt, /* Prepared statement being queried */
+ int idx, /* Index of loop to report on */
+ int iScanStatusOp, /* Which metric to return */
+ void *pOut /* OUT: Write the answer here */
+){
+ Vdbe *p = (Vdbe*)pStmt;
+ ScanStatus *pScan;
+ if( idx<0 || idx>=p->nScan ) return 1;
+ pScan = &p->aScan[idx];
+ switch( iScanStatusOp ){
+ case SQLITE_SCANSTAT_NLOOP: {
+ *(sqlite3_int64*)pOut = p->anExec[pScan->addrLoop];
+ break;
+ }
+ case SQLITE_SCANSTAT_NVISIT: {
+ *(sqlite3_int64*)pOut = p->anExec[pScan->addrVisit];
+ break;
+ }
+ case SQLITE_SCANSTAT_EST: {
+ double r = 1.0;
+ LogEst x = pScan->nEst;
+ while( x<100 ){
+ x += 10;
+ r *= 0.5;
+ }
+ *(double*)pOut = r*sqlite3LogEstToInt(x);
+ break;
+ }
+ case SQLITE_SCANSTAT_NAME: {
+ *(const char**)pOut = pScan->zName;
+ break;
+ }
+ case SQLITE_SCANSTAT_EXPLAIN: {
+ if( pScan->addrExplain ){
+ *(const char**)pOut = p->aOp[ pScan->addrExplain ].p4.z;
+ }else{
+ *(const char**)pOut = 0;
+ }
+ break;
+ }
+ case SQLITE_SCANSTAT_SELECTID: {
+ if( pScan->addrExplain ){
+ *(int*)pOut = p->aOp[ pScan->addrExplain ].p1;
+ }else{
+ *(int*)pOut = -1;
+ }
+ break;
+ }
+ default: {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Zero all counters associated with the sqlite3_stmt_scanstatus() data.
+*/
+SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt *pStmt){
+ Vdbe *p = (Vdbe*)pStmt;
+ memset(p->anExec, 0, p->nOp * sizeof(i64));
}
+#endif /* SQLITE_ENABLE_STMT_SCANSTATUS */
/************** End of vdbeapi.c *********************************************/
/************** Begin file vdbetrace.c ***************************************/
@@ -62865,7 +77033,11 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt *pStmt, int op, int resetFlag){
**
** This file contains code used to insert the values of host parameters
** (aka "wildcards") into the SQL text output by sqlite3_trace().
+**
+** The Vdbe parse-tree explainer is also found here.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#ifndef SQLITE_OMIT_TRACE
@@ -62896,19 +77068,24 @@ static int findNextHostParameter(const char *zSql, int *pnToken){
/*
** This function returns a pointer to a nul-terminated string in memory
-** obtained from sqlite3DbMalloc(). If sqlite3.vdbeExecCnt is 1, then the
+** obtained from sqlite3DbMalloc(). If sqlite3.nVdbeExec is 1, then the
** string contains a copy of zRawSql but with host parameters expanded to
-** their current bindings. Or, if sqlite3.vdbeExecCnt is greater than 1,
+** their current bindings. Or, if sqlite3.nVdbeExec is greater than 1,
** then the returned string holds a copy of zRawSql with "-- " prepended
** to each line of text.
**
+** If the SQLITE_TRACE_SIZE_LIMIT macro is defined to an integer, then
+** then long strings and blobs are truncated to that many bytes. This
+** can be used to prevent unreasonably large trace strings when dealing
+** with large (multi-megabyte) strings and blobs.
+**
** The calling function is responsible for making sure the memory returned
** is eventually freed.
**
** ALGORITHM: Scan the input string looking for host parameters in any of
** these forms: ?, ?N, $A, @A, :A. Take care to avoid text within
** string literals, quoted identifier names, and comments. For text forms,
-** the host parameter index is found by scanning the perpared
+** the host parameter index is found by scanning the prepared
** statement for the corresponding OP_Variable opcode. Once the host
** parameter index is known, locate the value in p->aVar[]. Then render
** the value as a literal in place of the host parameter name.
@@ -62925,19 +77102,24 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
int i; /* Loop counter */
Mem *pVar; /* Value of a host parameter */
StrAccum out; /* Accumulate the output here */
+#ifndef SQLITE_OMIT_UTF16
+ Mem utf8; /* Used to convert UTF16 parameters into UTF8 for display */
+#endif
char zBase[100]; /* Initial working space */
db = p->db;
- sqlite3StrAccumInit(&out, zBase, sizeof(zBase),
+ sqlite3StrAccumInit(&out, 0, zBase, sizeof(zBase),
db->aLimit[SQLITE_LIMIT_LENGTH]);
- out.db = db;
- if( db->vdbeExecCnt>1 ){
+ if( db->nVdbeExec>1 ){
while( *zRawSql ){
const char *zStart = zRawSql;
while( *(zRawSql++)!='\n' && *zRawSql );
sqlite3StrAccumAppend(&out, "-- ", 3);
+ assert( (zRawSql - zStart) > 0 );
sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart));
}
+ }else if( p->nVar==0 ){
+ sqlite3StrAccumAppend(&out, zRawSql, sqlite3Strlen30(zRawSql));
}else{
while( zRawSql[0] ){
n = findNextHostParameter(zRawSql, &nToken);
@@ -62954,10 +77136,12 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
idx = nextIndex;
}
}else{
- assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' );
+ assert( zRawSql[0]==':' || zRawSql[0]=='$' ||
+ zRawSql[0]=='@' || zRawSql[0]=='#' );
testcase( zRawSql[0]==':' );
testcase( zRawSql[0]=='$' );
testcase( zRawSql[0]=='@' );
+ testcase( zRawSql[0]=='#' );
idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
assert( idx>0 );
}
@@ -62970,35 +77154,61 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
}else if( pVar->flags & MEM_Int ){
sqlite3XPrintf(&out, "%lld", pVar->u.i);
}else if( pVar->flags & MEM_Real ){
- sqlite3XPrintf(&out, "%!.15g", pVar->r);
+ sqlite3XPrintf(&out, "%!.15g", pVar->u.r);
}else if( pVar->flags & MEM_Str ){
+ int nOut; /* Number of bytes of the string text to include in output */
#ifndef SQLITE_OMIT_UTF16
u8 enc = ENC(db);
if( enc!=SQLITE_UTF8 ){
- Mem utf8;
memset(&utf8, 0, sizeof(utf8));
utf8.db = db;
sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC);
- sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8);
- sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z);
- sqlite3VdbeMemRelease(&utf8);
- }else
+ if( SQLITE_NOMEM==sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8) ){
+ out.accError = STRACCUM_NOMEM;
+ out.nAlloc = 0;
+ }
+ pVar = &utf8;
+ }
#endif
- {
- sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z);
+ nOut = pVar->n;
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut>SQLITE_TRACE_SIZE_LIMIT ){
+ nOut = SQLITE_TRACE_SIZE_LIMIT;
+ while( nOut<pVar->n && (pVar->z[nOut]&0xc0)==0x80 ){ nOut++; }
+ }
+#endif
+ sqlite3XPrintf(&out, "'%.*q'", nOut, pVar->z);
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut<pVar->n ){
+ sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
}
+#endif
+#ifndef SQLITE_OMIT_UTF16
+ if( enc!=SQLITE_UTF8 ) sqlite3VdbeMemRelease(&utf8);
+#endif
}else if( pVar->flags & MEM_Zero ){
sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero);
}else{
+ int nOut; /* Number of bytes of the blob to include in output */
assert( pVar->flags & MEM_Blob );
sqlite3StrAccumAppend(&out, "x'", 2);
- for(i=0; i<pVar->n; i++){
+ nOut = pVar->n;
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut>SQLITE_TRACE_SIZE_LIMIT ) nOut = SQLITE_TRACE_SIZE_LIMIT;
+#endif
+ for(i=0; i<nOut; i++){
sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff);
}
sqlite3StrAccumAppend(&out, "'", 1);
+#ifdef SQLITE_TRACE_SIZE_LIMIT
+ if( nOut<pVar->n ){
+ sqlite3XPrintf(&out, "/*+%d bytes*/", pVar->n-nOut);
+ }
+#endif
}
}
}
+ if( out.accError ) sqlite3StrAccumReset(&out);
return sqlite3StrAccumFinish(&out);
}
@@ -63017,33 +77227,8 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
** May you share freely, never taking more than you give.
**
*************************************************************************
-** The code in this file implements execution method of the
-** Virtual Database Engine (VDBE). A separate file ("vdbeaux.c")
-** handles housekeeping details such as creating and deleting
-** VDBE instances. This file is solely interested in executing
-** the VDBE program.
-**
-** In the external interface, an "sqlite3_stmt*" is an opaque pointer
-** to a VDBE.
-**
-** The SQL parser generates a program which is then executed by
-** the VDBE to do the work of the SQL statement. VDBE programs are
-** similar in form to assembly language. The program consists of
-** a linear sequence of operations. Each operation has an opcode
-** and 5 operands. Operands P1, P2, and P3 are integers. Operand P4
-** is a null-terminated string. Operand P5 is an unsigned character.
-** Few opcodes use all 5 operands.
-**
-** Computation results are stored on a set of registers numbered beginning
-** with 1 and going up to Vdbe.nMem. Each register can store
-** either an integer, a null-terminated string, a floating point
-** number, or the SQL "NULL" value. An implicit conversion from one
-** type to the other occurs as necessary.
-**
-** Most of the code in this file is taken up by the sqlite3VdbeExec()
-** function which does the work of interpreting a VDBE program.
-** But other routines are also provided to help in building up
-** a program instruction by instruction.
+** The code in this file implements the function that runs the
+** bytecode of a prepared statement.
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files. The formatting
@@ -63051,14 +77236,20 @@ SQLITE_PRIVATE char *sqlite3VdbeExpandSql(
** in this file for details. If in doubt, do not deviate from existing
** commenting and indentation practices when changing or adding code.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
/*
** Invoke this macro on memory cells just prior to changing the
** value of the cell. This macro verifies that shallow copies are
-** not misused.
+** not misused. A shallow copy of a string or blob just copies a
+** pointer to the string or blob, not the content. If the original
+** is changed while the copy is still in use, the string or blob might
+** be changed out from under the copy. This macro verifies that nothing
+** like that ever happens.
*/
#ifdef SQLITE_DEBUG
-# define memAboutToChange(P,M) sqlite3VdbeMemPrepareToChange(P,M)
+# define memAboutToChange(P,M) sqlite3VdbeMemAboutToChange(P,M)
#else
# define memAboutToChange(P,M)
#endif
@@ -63076,8 +77267,8 @@ SQLITE_API int sqlite3_search_count = 0;
/*
** When this global variable is positive, it gets decremented once before
-** each instruction in the VDBE. When reaches zero, the u1.isInterrupted
-** field of the sqlite3 structure is set in order to simulate and interrupt.
+** each instruction in the VDBE. When it reaches zero, the u1.isInterrupted
+** field of the sqlite3 structure is set in order to simulate an interrupt.
**
** This facility is used for testing purposes only. It does not function
** in an ordinary build.
@@ -63114,7 +77305,17 @@ static void updateMaxBlobsize(Mem *p){
#endif
/*
-** The next global variable is incremented each type the OP_Found opcode
+** This macro evaluates to true if either the update hook or the preupdate
+** hook are enabled for database connect DB.
+*/
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+# define HAS_UPDATE_HOOK(DB) ((DB)->xPreUpdateCallback||(DB)->xUpdateCallback)
+#else
+# define HAS_UPDATE_HOOK(DB) ((DB)->xUpdateCallback)
+#endif
+
+/*
+** The next global variable is incremented each time the OP_Found opcode
** is executed. This is used to test whether or not the foreign key
** operation implemented using OP_FkIsZero is working. This variable
** has no function other than to help verify the correct operation of the
@@ -63135,11 +77336,45 @@ SQLITE_API int sqlite3_found_count = 0;
#endif
/*
+** Invoke the VDBE coverage callback, if that callback is defined. This
+** feature is used for test suite validation only and does not appear an
+** production builds.
+**
+** M is an integer, 2 or 3, that indices how many different ways the
+** branch can go. It is usually 2. "I" is the direction the branch
+** goes. 0 means falls through. 1 means branch is taken. 2 means the
+** second alternative branch is taken.
+**
+** iSrcLine is the source code line (from the __LINE__ macro) that
+** generated the VDBE instruction. This instrumentation assumes that all
+** source code is in a single file (the amalgamation). Special values 1
+** and 2 for the iSrcLine parameter mean that this particular branch is
+** always taken or never taken, respectively.
+*/
+#if !defined(SQLITE_VDBE_COVERAGE)
+# define VdbeBranchTaken(I,M)
+#else
+# define VdbeBranchTaken(I,M) vdbeTakeBranch(pOp->iSrcLine,I,M)
+ static void vdbeTakeBranch(int iSrcLine, u8 I, u8 M){
+ if( iSrcLine<=2 && ALWAYS(iSrcLine>0) ){
+ M = iSrcLine;
+ /* Assert the truth of VdbeCoverageAlwaysTaken() and
+ ** VdbeCoverageNeverTaken() */
+ assert( (M & I)==I );
+ }else{
+ if( sqlite3GlobalConfig.xVdbeBranch==0 ) return; /*NO_TEST*/
+ sqlite3GlobalConfig.xVdbeBranch(sqlite3GlobalConfig.pVdbeBranchArg,
+ iSrcLine,I,M);
+ }
+ }
+#endif
+
+/*
** Convert the given register into a string if it isn't one
** already. Return non-zero if a malloc() fails.
*/
#define Stringify(P, enc) \
- if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc)) \
+ if(((P)->flags&(MEM_Str|MEM_Blob))==0 && sqlite3VdbeMemStringify(P,enc,0)) \
{ goto no_mem; }
/*
@@ -63151,48 +77386,14 @@ SQLITE_API int sqlite3_found_count = 0;
**
** This routine converts an ephemeral string into a dynamically allocated
** string that the register itself controls. In other words, it
-** converts an MEM_Ephem string into an MEM_Dyn string.
+** converts an MEM_Ephem string into a string with P.z==P.zMalloc.
*/
#define Deephemeralize(P) \
if( ((P)->flags&MEM_Ephem)!=0 \
&& sqlite3VdbeMemMakeWriteable(P) ){ goto no_mem;}
-/*
-** Call sqlite3VdbeMemExpandBlob() on the supplied value (type Mem*)
-** P if required.
-*/
-#define ExpandBlob(P) (((P)->flags&MEM_Zero)?sqlite3VdbeMemExpandBlob(P):0)
-
/* Return true if the cursor was opened using the OP_OpenSorter opcode. */
-#ifdef SQLITE_OMIT_MERGE_SORT
-# define isSorter(x) 0
-#else
-# define isSorter(x) ((x)->pSorter!=0)
-#endif
-
-/*
-** Argument pMem points at a register that will be passed to a
-** user-defined function or returned to the user as the result of a query.
-** This routine sets the pMem->type variable used by the sqlite3_value_*()
-** routines.
-*/
-SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem){
- int flags = pMem->flags;
- if( flags & MEM_Null ){
- pMem->type = SQLITE_NULL;
- }
- else if( flags & MEM_Int ){
- pMem->type = SQLITE_INTEGER;
- }
- else if( flags & MEM_Real ){
- pMem->type = SQLITE_FLOAT;
- }
- else if( flags & MEM_Str ){
- pMem->type = SQLITE_TEXT;
- }else{
- pMem->type = SQLITE_BLOB;
- }
-}
+#define isSorter(x) ((x)->eCurType==CURTYPE_SORTER)
/*
** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL
@@ -63202,8 +77403,8 @@ static VdbeCursor *allocateCursor(
Vdbe *p, /* The virtual machine */
int iCur, /* Index of the new VdbeCursor */
int nField, /* Number of fields in the table or index */
- int iDb, /* When database the cursor belongs to, or -1 */
- int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */
+ int iDb, /* Database the cursor belongs to, or -1 */
+ u8 eCurType /* Type of the new cursor */
){
/* Find the memory cell that will be used to store the blob of memory
** required for this VdbeCursor structure. It is convenient to use a
@@ -63219,36 +77420,34 @@ static VdbeCursor *allocateCursor(
** be freed lazily via the sqlite3_release_memory() API. This
** minimizes the number of malloc calls made by the system.
**
- ** Memory cells for cursors are allocated at the top of the address
- ** space. Memory cell (p->nMem) corresponds to cursor 0. Space for
- ** cursor 1 is managed by memory cell (p->nMem-1), etc.
+ ** The memory cell for cursor 0 is aMem[0]. The rest are allocated from
+ ** the top of the register space. Cursor 1 is at Mem[p->nMem-1].
+ ** Cursor 2 is at Mem[p->nMem-2]. And so forth.
*/
- Mem *pMem = &p->aMem[p->nMem-iCur];
+ Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
int nByte;
VdbeCursor *pCx = 0;
nByte =
- ROUND8(sizeof(VdbeCursor)) +
- (isBtreeCursor?sqlite3BtreeCursorSize():0) +
- 2*nField*sizeof(u32);
+ ROUND8(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
+ (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
- assert( iCur<p->nCursor );
- if( p->apCsr[iCur] ){
+ assert( iCur>=0 && iCur<p->nCursor );
+ if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
sqlite3VdbeFreeCursor(p, p->apCsr[iCur]);
p->apCsr[iCur] = 0;
}
- if( SQLITE_OK==sqlite3VdbeMemGrow(pMem, nByte, 0) ){
+ if( SQLITE_OK==sqlite3VdbeMemClearAndResize(pMem, nByte) ){
p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z;
memset(pCx, 0, sizeof(VdbeCursor));
+ pCx->eCurType = eCurType;
pCx->iDb = iDb;
pCx->nField = nField;
- if( nField ){
- pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))];
- }
- if( isBtreeCursor ){
- pCx->pCursor = (BtCursor*)
- &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)];
- sqlite3BtreeCursorZero(pCx->pCursor);
+ pCx->aOffset = &pCx->aType[nField];
+ if( eCurType==CURTYPE_BTREE ){
+ pCx->uc.pCursor = (BtCursor*)
+ &pMem->z[ROUND8(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
+ sqlite3BtreeCursorZero(pCx->uc.pCursor);
}
}
return pCx;
@@ -63259,21 +77458,29 @@ static VdbeCursor *allocateCursor(
** do so without loss of information. In other words, if the string
** looks like a number, convert it into a number. If it does not
** look like a number, leave it alone.
-*/
-static void applyNumericAffinity(Mem *pRec){
- if( (pRec->flags & (MEM_Real|MEM_Int))==0 ){
- double rValue;
- i64 iValue;
- u8 enc = pRec->enc;
- if( (pRec->flags&MEM_Str)==0 ) return;
- if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
- if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
- pRec->u.i = iValue;
- pRec->flags |= MEM_Int;
- }else{
- pRec->r = rValue;
- pRec->flags |= MEM_Real;
- }
+**
+** If the bTryForInt flag is true, then extra effort is made to give
+** an integer representation. Strings that look like floating point
+** values but which have no fractional component (example: '48.00')
+** will have a MEM_Int representation when bTryForInt is true.
+**
+** If bTryForInt is false, then if the input string contains a decimal
+** point or exponential notation, the result is only MEM_Real, even
+** if there is an exact integer representation of the quantity.
+*/
+static void applyNumericAffinity(Mem *pRec, int bTryForInt){
+ double rValue;
+ i64 iValue;
+ u8 enc = pRec->enc;
+ assert( (pRec->flags & (MEM_Str|MEM_Int|MEM_Real))==MEM_Str );
+ if( sqlite3AtoF(pRec->z, &rValue, pRec->n, enc)==0 ) return;
+ if( 0==sqlite3Atoi64(pRec->z, &iValue, pRec->n, enc) ){
+ pRec->u.i = iValue;
+ pRec->flags |= MEM_Int;
+ }else{
+ pRec->u.r = rValue;
+ pRec->flags |= MEM_Real;
+ if( bTryForInt ) sqlite3VdbeIntegerAffinity(pRec);
}
}
@@ -63292,7 +77499,7 @@ static void applyNumericAffinity(Mem *pRec){
** SQLITE_AFF_TEXT:
** Convert pRec to a text representation.
**
-** SQLITE_AFF_NONE:
+** SQLITE_AFF_BLOB:
** No-op. pRec is unchanged.
*/
static void applyAffinity(
@@ -63300,22 +77507,28 @@ static void applyAffinity(
char affinity, /* The affinity to be applied */
u8 enc /* Use this text encoding */
){
- if( affinity==SQLITE_AFF_TEXT ){
+ if( affinity>=SQLITE_AFF_NUMERIC ){
+ assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
+ || affinity==SQLITE_AFF_NUMERIC );
+ if( (pRec->flags & MEM_Int)==0 ){ /*OPTIMIZATION-IF-FALSE*/
+ if( (pRec->flags & MEM_Real)==0 ){
+ if( pRec->flags & MEM_Str ) applyNumericAffinity(pRec,1);
+ }else{
+ sqlite3VdbeIntegerAffinity(pRec);
+ }
+ }
+ }else if( affinity==SQLITE_AFF_TEXT ){
/* Only attempt the conversion to TEXT if there is an integer or real
** representation (blob and NULL do not get converted) but no string
- ** representation.
- */
- if( 0==(pRec->flags&MEM_Str) && (pRec->flags&(MEM_Real|MEM_Int)) ){
- sqlite3VdbeMemStringify(pRec, enc);
+ ** representation. It would be harmless to repeat the conversion if
+ ** there is already a string rep, but it is pointless to waste those
+ ** CPU cycles. */
+ if( 0==(pRec->flags&MEM_Str) ){ /*OPTIMIZATION-IF-FALSE*/
+ if( (pRec->flags&(MEM_Real|MEM_Int)) ){
+ sqlite3VdbeMemStringify(pRec, enc, 1);
+ }
}
pRec->flags &= ~(MEM_Real|MEM_Int);
- }else if( affinity!=SQLITE_AFF_NONE ){
- assert( affinity==SQLITE_AFF_INTEGER || affinity==SQLITE_AFF_REAL
- || affinity==SQLITE_AFF_NUMERIC );
- applyNumericAffinity(pRec);
- if( pRec->flags & MEM_Real ){
- sqlite3VdbeIntegerAffinity(pRec);
- }
}
}
@@ -63326,12 +77539,13 @@ static void applyAffinity(
** loss of information and return the revised type of the argument.
*/
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){
- Mem *pMem = (Mem*)pVal;
- if( pMem->type==SQLITE_TEXT ){
- applyNumericAffinity(pMem);
- sqlite3VdbeMemStoreType(pMem);
+ int eType = sqlite3_value_type(pVal);
+ if( eType==SQLITE_TEXT ){
+ Mem *pMem = (Mem*)pVal;
+ applyNumericAffinity(pMem, 0);
+ eType = sqlite3_value_type(pVal);
}
- return pMem->type;
+ return eType;
}
/*
@@ -63346,6 +77560,41 @@ SQLITE_PRIVATE void sqlite3ValueApplyAffinity(
applyAffinity((Mem *)pVal, affinity, enc);
}
+/*
+** pMem currently only holds a string type (or maybe a BLOB that we can
+** interpret as a string if we want to). Compute its corresponding
+** numeric type, if has one. Set the pMem->u.r and pMem->u.i fields
+** accordingly.
+*/
+static u16 SQLITE_NOINLINE computeNumericType(Mem *pMem){
+ assert( (pMem->flags & (MEM_Int|MEM_Real))==0 );
+ assert( (pMem->flags & (MEM_Str|MEM_Blob))!=0 );
+ if( sqlite3AtoF(pMem->z, &pMem->u.r, pMem->n, pMem->enc)==0 ){
+ return 0;
+ }
+ if( sqlite3Atoi64(pMem->z, &pMem->u.i, pMem->n, pMem->enc)==SQLITE_OK ){
+ return MEM_Int;
+ }
+ return MEM_Real;
+}
+
+/*
+** Return the numeric type for pMem, either MEM_Int or MEM_Real or both or
+** none.
+**
+** Unlike applyNumericAffinity(), this routine does not modify pMem->flags.
+** But it does set pMem->u.r and pMem->u.i appropriately.
+*/
+static u16 numericType(Mem *pMem){
+ if( pMem->flags & (MEM_Int|MEM_Real) ){
+ return pMem->flags & (MEM_Int|MEM_Real);
+ }
+ if( pMem->flags & (MEM_Str|MEM_Blob) ){
+ return computeNumericType(pMem);
+ }
+ return 0;
+}
+
#ifdef SQLITE_DEBUG
/*
** Write a nice string representation of the contents of cell pMem
@@ -63433,35 +77682,37 @@ SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){
/*
** Print the value of a register for tracing purposes:
*/
-static void memTracePrint(FILE *out, Mem *p){
- if( p->flags & MEM_Null ){
- fprintf(out, " NULL");
+static void memTracePrint(Mem *p){
+ if( p->flags & MEM_Undefined ){
+ printf(" undefined");
+ }else if( p->flags & MEM_Null ){
+ printf(" NULL");
}else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){
- fprintf(out, " si:%lld", p->u.i);
+ printf(" si:%lld", p->u.i);
}else if( p->flags & MEM_Int ){
- fprintf(out, " i:%lld", p->u.i);
+ printf(" i:%lld", p->u.i);
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( p->flags & MEM_Real ){
- fprintf(out, " r:%g", p->r);
+ printf(" r:%g", p->u.r);
#endif
}else if( p->flags & MEM_RowSet ){
- fprintf(out, " (rowset)");
+ printf(" (rowset)");
}else{
char zBuf[200];
sqlite3VdbeMemPrettyPrint(p, zBuf);
- fprintf(out, " ");
- fprintf(out, "%s", zBuf);
+ printf(" %s", zBuf);
}
+ if( p->flags & MEM_Subtype ) printf(" subtype=0x%02x", p->eSubtype);
}
-static void registerTrace(FILE *out, int iReg, Mem *p){
- fprintf(out, "REG[%d] = ", iReg);
- memTracePrint(out, p);
- fprintf(out, "\n");
+static void registerTrace(int iReg, Mem *p){
+ printf("REG[%d] = ", iReg);
+ memTracePrint(p);
+ printf("\n");
}
#endif
#ifdef SQLITE_DEBUG
-# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M)
+# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M)
#else
# define REGISTER_TRACE(R,M)
#endif
@@ -63490,8 +77741,8 @@ static void registerTrace(FILE *out, int iReg, Mem *p){
** This file contains inline asm code for retrieving "high-performance"
** counters for x86 class CPUs.
*/
-#ifndef _HWTIME_H_
-#define _HWTIME_H_
+#ifndef SQLITE_HWTIME_H
+#define SQLITE_HWTIME_H
/*
** The following routine only works on pentium-class (or newer) processors.
@@ -63559,27 +77810,13 @@ SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); }
#endif
-#endif /* !defined(_HWTIME_H_) */
+#endif /* !defined(SQLITE_HWTIME_H) */
/************** End of hwtime.h **********************************************/
/************** Continuing where we left off in vdbe.c ***********************/
#endif
-/*
-** The CHECK_FOR_INTERRUPT macro defined here looks to see if the
-** sqlite3_interrupt() routine has been called. If it has been, then
-** processing of the VDBE program is interrupted.
-**
-** This macro added to every instruction that does a jump in order to
-** implement a loop. This test used to be on every single instruction,
-** but that meant we more testing that we needed. By only testing the
-** flag on jump instructions, we get a (small) speed improvement.
-*/
-#define CHECK_FOR_INTERRUPT \
- if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
-
-
#ifndef NDEBUG
/*
** This function is only called from within an assert() expression. It
@@ -63601,496 +77838,64 @@ static int checkSavepointCount(sqlite3 *db){
#endif
/*
-** Transfer error message text from an sqlite3_vtab.zErrMsg (text stored
-** in memory obtained from sqlite3_malloc) into a Vdbe.zErrMsg (text stored
-** in memory obtained from sqlite3DbMalloc).
+** Return the register of pOp->p2 after first preparing it to be
+** overwritten with an integer value.
*/
-static void importVtabErrMsg(Vdbe *p, sqlite3_vtab *pVtab){
- sqlite3 *db = p->db;
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
- pVtab->zErrMsg = 0;
+static SQLITE_NOINLINE Mem *out2PrereleaseWithClear(Mem *pOut){
+ sqlite3VdbeMemSetNull(pOut);
+ pOut->flags = MEM_Int;
+ return pOut;
+}
+static Mem *out2Prerelease(Vdbe *p, VdbeOp *pOp){
+ Mem *pOut;
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
+ pOut = &p->aMem[pOp->p2];
+ memAboutToChange(p, pOut);
+ if( VdbeMemDynamic(pOut) ){ /*OPTIMIZATION-IF-FALSE*/
+ return out2PrereleaseWithClear(pOut);
+ }else{
+ pOut->flags = MEM_Int;
+ return pOut;
+ }
}
/*
-** Execute as much of a VDBE program as we can then return.
-**
-** sqlite3VdbeMakeReady() must be called before this routine in order to
-** close the program with a final OP_Halt and to set up the callbacks
-** and the error message pointer.
-**
-** Whenever a row or result data is available, this routine will either
-** invoke the result callback (if there is one) or return with
-** SQLITE_ROW.
-**
-** If an attempt is made to open a locked database, then this routine
-** will either invoke the busy callback (if there is one) or it will
-** return SQLITE_BUSY.
-**
-** If an error occurs, an error message is written to memory obtained
-** from sqlite3_malloc() and p->zErrMsg is made to point to that memory.
-** The error code is stored in p->rc and this routine returns SQLITE_ERROR.
-**
-** If the callback ever returns non-zero, then the program exits
-** immediately. There will be no error message but the p->rc field is
-** set to SQLITE_ABORT and this routine will return SQLITE_ERROR.
-**
-** A memory allocation error causes p->rc to be set to SQLITE_NOMEM and this
-** routine to return SQLITE_ERROR.
-**
-** Other fatal errors return SQLITE_ERROR.
-**
-** After this routine has finished, sqlite3VdbeFinalize() should be
-** used to clean up the mess that was left behind.
+** Execute as much of a VDBE program as we can.
+** This is the core of sqlite3_step().
*/
SQLITE_PRIVATE int sqlite3VdbeExec(
Vdbe *p /* The VDBE */
){
- int pc=0; /* The program counter */
Op *aOp = p->aOp; /* Copy of p->aOp */
- Op *pOp; /* Current operation */
+ Op *pOp = aOp; /* Current operation */
+#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
+ Op *pOrigOp; /* Value of pOp at the top of the loop */
+#endif
+#ifdef SQLITE_DEBUG
+ int nExtraDelete = 0; /* Verifies FORDELETE and AUXDELETE flags */
+#endif
int rc = SQLITE_OK; /* Value to return */
sqlite3 *db = p->db; /* The database */
u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */
u8 encoding = ENC(db); /* The database encoding */
+ int iCompare = 0; /* Result of last comparison */
+ unsigned nVmStep = 0; /* Number of virtual machine steps */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- int checkProgress; /* True if progress callbacks are enabled */
- int nProgressOps = 0; /* Opcodes executed since progress callback. */
+ unsigned nProgressLimit = 0;/* Invoke xProgress() when nVmStep reaches this */
#endif
Mem *aMem = p->aMem; /* Copy of p->aMem */
Mem *pIn1 = 0; /* 1st input operand */
Mem *pIn2 = 0; /* 2nd input operand */
Mem *pIn3 = 0; /* 3rd input operand */
Mem *pOut = 0; /* Output operand */
- int iCompare = 0; /* Result of last OP_Compare operation */
int *aPermute = 0; /* Permutation of columns for OP_Compare */
i64 lastRowid = db->lastRowid; /* Saved value of the last insert ROWID */
#ifdef VDBE_PROFILE
u64 start; /* CPU clock count at start of opcode */
- int origPc; /* Program counter at start of opcode */
#endif
- /********************************************************************
- ** Automatically generated code
- **
- ** The following union is automatically generated by the
- ** vdbe-compress.tcl script. The purpose of this union is to
- ** reduce the amount of stack space required by this function.
- ** See comments in the vdbe-compress.tcl script for details.
- */
- union vdbeExecUnion {
- struct OP_Yield_stack_vars {
- int pcDest;
- } aa;
- struct OP_Variable_stack_vars {
- Mem *pVar; /* Value being transferred */
- } ab;
- struct OP_Move_stack_vars {
- char *zMalloc; /* Holding variable for allocated memory */
- int n; /* Number of registers left to copy */
- int p1; /* Register to copy from */
- int p2; /* Register to copy to */
- } ac;
- struct OP_ResultRow_stack_vars {
- Mem *pMem;
- int i;
- } ad;
- struct OP_Concat_stack_vars {
- i64 nByte;
- } ae;
- struct OP_Remainder_stack_vars {
- int flags; /* Combined MEM_* flags from both inputs */
- i64 iA; /* Integer value of left operand */
- i64 iB; /* Integer value of right operand */
- double rA; /* Real value of left operand */
- double rB; /* Real value of right operand */
- } af;
- struct OP_Function_stack_vars {
- int i;
- Mem *pArg;
- sqlite3_context ctx;
- sqlite3_value **apVal;
- int n;
- } ag;
- struct OP_ShiftRight_stack_vars {
- i64 iA;
- u64 uA;
- i64 iB;
- u8 op;
- } ah;
- struct OP_Ge_stack_vars {
- int res; /* Result of the comparison of pIn1 against pIn3 */
- char affinity; /* Affinity to use for comparison */
- u16 flags1; /* Copy of initial value of pIn1->flags */
- u16 flags3; /* Copy of initial value of pIn3->flags */
- } ai;
- struct OP_Compare_stack_vars {
- int n;
- int i;
- int p1;
- int p2;
- const KeyInfo *pKeyInfo;
- int idx;
- CollSeq *pColl; /* Collating sequence to use on this term */
- int bRev; /* True for DESCENDING sort order */
- } aj;
- struct OP_Or_stack_vars {
- int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
- int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
- } ak;
- struct OP_IfNot_stack_vars {
- int c;
- } al;
- struct OP_Column_stack_vars {
- u32 payloadSize; /* Number of bytes in the record */
- i64 payloadSize64; /* Number of bytes in the record */
- int p1; /* P1 value of the opcode */
- int p2; /* column number to retrieve */
- VdbeCursor *pC; /* The VDBE cursor */
- char *zRec; /* Pointer to complete record-data */
- BtCursor *pCrsr; /* The BTree cursor */
- u32 *aType; /* aType[i] holds the numeric type of the i-th column */
- u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
- int nField; /* number of fields in the record */
- int len; /* The length of the serialized data for the column */
- int i; /* Loop counter */
- char *zData; /* Part of the record being decoded */
- Mem *pDest; /* Where to write the extracted value */
- Mem sMem; /* For storing the record being decoded */
- u8 *zIdx; /* Index into header */
- u8 *zEndHdr; /* Pointer to first byte after the header */
- u32 offset; /* Offset into the data */
- u32 szField; /* Number of bytes in the content of a field */
- int szHdr; /* Size of the header size field at start of record */
- int avail; /* Number of bytes of available data */
- u32 t; /* A type code from the record header */
- Mem *pReg; /* PseudoTable input register */
- } am;
- struct OP_Affinity_stack_vars {
- const char *zAffinity; /* The affinity to be applied */
- char cAff; /* A single character of affinity */
- } an;
- struct OP_MakeRecord_stack_vars {
- u8 *zNewRecord; /* A buffer to hold the data for the new record */
- Mem *pRec; /* The new record */
- u64 nData; /* Number of bytes of data space */
- int nHdr; /* Number of bytes of header space */
- i64 nByte; /* Data space required for this record */
- int nZero; /* Number of zero bytes at the end of the record */
- int nVarint; /* Number of bytes in a varint */
- u32 serial_type; /* Type field */
- Mem *pData0; /* First field to be combined into the record */
- Mem *pLast; /* Last field of the record */
- int nField; /* Number of fields in the record */
- char *zAffinity; /* The affinity string for the record */
- int file_format; /* File format to use for encoding */
- int i; /* Space used in zNewRecord[] */
- int len; /* Length of a field */
- } ao;
- struct OP_Count_stack_vars {
- i64 nEntry;
- BtCursor *pCrsr;
- } ap;
- struct OP_Savepoint_stack_vars {
- int p1; /* Value of P1 operand */
- char *zName; /* Name of savepoint */
- int nName;
- Savepoint *pNew;
- Savepoint *pSavepoint;
- Savepoint *pTmp;
- int iSavepoint;
- int ii;
- } aq;
- struct OP_AutoCommit_stack_vars {
- int desiredAutoCommit;
- int iRollback;
- int turnOnAC;
- } ar;
- struct OP_Transaction_stack_vars {
- Btree *pBt;
- } as;
- struct OP_ReadCookie_stack_vars {
- int iMeta;
- int iDb;
- int iCookie;
- } at;
- struct OP_SetCookie_stack_vars {
- Db *pDb;
- } au;
- struct OP_VerifyCookie_stack_vars {
- int iMeta;
- int iGen;
- Btree *pBt;
- } av;
- struct OP_OpenWrite_stack_vars {
- int nField;
- KeyInfo *pKeyInfo;
- int p2;
- int iDb;
- int wrFlag;
- Btree *pX;
- VdbeCursor *pCur;
- Db *pDb;
- } aw;
- struct OP_OpenEphemeral_stack_vars {
- VdbeCursor *pCx;
- } ax;
- struct OP_SorterOpen_stack_vars {
- VdbeCursor *pCx;
- } ay;
- struct OP_OpenPseudo_stack_vars {
- VdbeCursor *pCx;
- } az;
- struct OP_SeekGt_stack_vars {
- int res;
- int oc;
- VdbeCursor *pC;
- UnpackedRecord r;
- int nField;
- i64 iKey; /* The rowid we are to seek to */
- } ba;
- struct OP_Seek_stack_vars {
- VdbeCursor *pC;
- } bb;
- struct OP_Found_stack_vars {
- int alreadyExists;
- VdbeCursor *pC;
- int res;
- char *pFree;
- UnpackedRecord *pIdxKey;
- UnpackedRecord r;
- char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
- } bc;
- struct OP_IsUnique_stack_vars {
- u16 ii;
- VdbeCursor *pCx;
- BtCursor *pCrsr;
- u16 nField;
- Mem *aMx;
- UnpackedRecord r; /* B-Tree index search key */
- i64 R; /* Rowid stored in register P3 */
- } bd;
- struct OP_NotExists_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- u64 iKey;
- } be;
- struct OP_NewRowid_stack_vars {
- i64 v; /* The new rowid */
- VdbeCursor *pC; /* Cursor of table to get the new rowid */
- int res; /* Result of an sqlite3BtreeLast() */
- int cnt; /* Counter to limit the number of searches */
- Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
- VdbeFrame *pFrame; /* Root frame of VDBE */
- } bf;
- struct OP_InsertInt_stack_vars {
- Mem *pData; /* MEM cell holding data for the record to be inserted */
- Mem *pKey; /* MEM cell holding key for the record */
- i64 iKey; /* The integer ROWID or key for the record to be inserted */
- VdbeCursor *pC; /* Cursor to table into which insert is written */
- int nZero; /* Number of zero-bytes to append */
- int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */
- const char *zDb; /* database name - used by the update hook */
- const char *zTbl; /* Table name - used by the opdate hook */
- int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
- } bg;
- struct OP_Delete_stack_vars {
- i64 iKey;
- VdbeCursor *pC;
- } bh;
- struct OP_SorterCompare_stack_vars {
- VdbeCursor *pC;
- int res;
- } bi;
- struct OP_SorterData_stack_vars {
- VdbeCursor *pC;
- } bj;
- struct OP_RowData_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- u32 n;
- i64 n64;
- } bk;
- struct OP_Rowid_stack_vars {
- VdbeCursor *pC;
- i64 v;
- sqlite3_vtab *pVtab;
- const sqlite3_module *pModule;
- } bl;
- struct OP_NullRow_stack_vars {
- VdbeCursor *pC;
- } bm;
- struct OP_Last_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- } bn;
- struct OP_Rewind_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- } bo;
- struct OP_Next_stack_vars {
- VdbeCursor *pC;
- int res;
- } bp;
- struct OP_IdxInsert_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int nKey;
- const char *zKey;
- } bq;
- struct OP_IdxDelete_stack_vars {
- VdbeCursor *pC;
- BtCursor *pCrsr;
- int res;
- UnpackedRecord r;
- } br;
- struct OP_IdxRowid_stack_vars {
- BtCursor *pCrsr;
- VdbeCursor *pC;
- i64 rowid;
- } bs;
- struct OP_IdxGE_stack_vars {
- VdbeCursor *pC;
- int res;
- UnpackedRecord r;
- } bt;
- struct OP_Destroy_stack_vars {
- int iMoved;
- int iCnt;
- Vdbe *pVdbe;
- int iDb;
- } bu;
- struct OP_Clear_stack_vars {
- int nChange;
- } bv;
- struct OP_CreateTable_stack_vars {
- int pgno;
- int flags;
- Db *pDb;
- } bw;
- struct OP_ParseSchema_stack_vars {
- int iDb;
- const char *zMaster;
- char *zSql;
- InitData initData;
- } bx;
- struct OP_IntegrityCk_stack_vars {
- int nRoot; /* Number of tables to check. (Number of root pages.) */
- int *aRoot; /* Array of rootpage numbers for tables to be checked */
- int j; /* Loop counter */
- int nErr; /* Number of errors reported */
- char *z; /* Text of the error report */
- Mem *pnErr; /* Register keeping track of errors remaining */
- } by;
- struct OP_RowSetRead_stack_vars {
- i64 val;
- } bz;
- struct OP_RowSetTest_stack_vars {
- int iSet;
- int exists;
- } ca;
- struct OP_Program_stack_vars {
- int nMem; /* Number of memory registers for sub-program */
- int nByte; /* Bytes of runtime space required for sub-program */
- Mem *pRt; /* Register to allocate runtime space */
- Mem *pMem; /* Used to iterate through memory cells */
- Mem *pEnd; /* Last memory cell in new array */
- VdbeFrame *pFrame; /* New vdbe frame to execute in */
- SubProgram *pProgram; /* Sub-program to execute */
- void *t; /* Token identifying trigger */
- } cb;
- struct OP_Param_stack_vars {
- VdbeFrame *pFrame;
- Mem *pIn;
- } cc;
- struct OP_MemMax_stack_vars {
- Mem *pIn1;
- VdbeFrame *pFrame;
- } cd;
- struct OP_AggStep_stack_vars {
- int n;
- int i;
- Mem *pMem;
- Mem *pRec;
- sqlite3_context ctx;
- sqlite3_value **apVal;
- } ce;
- struct OP_AggFinal_stack_vars {
- Mem *pMem;
- } cf;
- struct OP_Checkpoint_stack_vars {
- int i; /* Loop counter */
- int aRes[3]; /* Results */
- Mem *pMem; /* Write results here */
- } cg;
- struct OP_JournalMode_stack_vars {
- Btree *pBt; /* Btree to change journal mode of */
- Pager *pPager; /* Pager associated with pBt */
- int eNew; /* New journal mode */
- int eOld; /* The old journal mode */
- const char *zFilename; /* Name of database file for pPager */
- } ch;
- struct OP_IncrVacuum_stack_vars {
- Btree *pBt;
- } ci;
- struct OP_VBegin_stack_vars {
- VTable *pVTab;
- } cj;
- struct OP_VOpen_stack_vars {
- VdbeCursor *pCur;
- sqlite3_vtab_cursor *pVtabCursor;
- sqlite3_vtab *pVtab;
- sqlite3_module *pModule;
- } ck;
- struct OP_VFilter_stack_vars {
- int nArg;
- int iQuery;
- const sqlite3_module *pModule;
- Mem *pQuery;
- Mem *pArgc;
- sqlite3_vtab_cursor *pVtabCursor;
- sqlite3_vtab *pVtab;
- VdbeCursor *pCur;
- int res;
- int i;
- Mem **apArg;
- } cl;
- struct OP_VColumn_stack_vars {
- sqlite3_vtab *pVtab;
- const sqlite3_module *pModule;
- Mem *pDest;
- sqlite3_context sContext;
- } cm;
- struct OP_VNext_stack_vars {
- sqlite3_vtab *pVtab;
- const sqlite3_module *pModule;
- int res;
- VdbeCursor *pCur;
- } cn;
- struct OP_VRename_stack_vars {
- sqlite3_vtab *pVtab;
- Mem *pName;
- } co;
- struct OP_VUpdate_stack_vars {
- sqlite3_vtab *pVtab;
- sqlite3_module *pModule;
- int nArg;
- int i;
- sqlite_int64 rowid;
- Mem **apArg;
- Mem *pX;
- } cp;
- struct OP_Trace_stack_vars {
- char *zTrace;
- char *z;
- } cq;
- } u;
- /* End automatically generated code
- ********************************************************************/
+ /*** INSERT STACK UNION HERE ***/
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
sqlite3VdbeEnter(p);
@@ -64099,46 +77904,68 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
** sqlite3_column_text16() failed. */
goto no_mem;
}
- assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
+ assert( p->rc==SQLITE_OK || (p->rc&0xff)==SQLITE_BUSY );
+ assert( p->bIsReader || p->readOnly!=0 );
p->rc = SQLITE_OK;
+ p->iCurrentTime = 0;
assert( p->explain==0 );
p->pResultSet = 0;
db->busyHandler.nBusy = 0;
- CHECK_FOR_INTERRUPT;
+ if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
sqlite3VdbeIOTraceSql(p);
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- checkProgress = db->xProgress!=0;
+ if( db->xProgress ){
+ u32 iPrior = p->aCounter[SQLITE_STMTSTATUS_VM_STEP];
+ assert( 0 < db->nProgressOps );
+ nProgressLimit = db->nProgressOps - (iPrior % db->nProgressOps);
+ }
#endif
#ifdef SQLITE_DEBUG
sqlite3BeginBenignMalloc();
- if( p->pc==0 && (p->db->flags & SQLITE_VdbeListing)!=0 ){
+ if( p->pc==0
+ && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0
+ ){
int i;
- printf("VDBE Program Listing:\n");
+ int once = 1;
sqlite3VdbePrintSql(p);
- for(i=0; i<p->nOp; i++){
- sqlite3VdbePrintOp(stdout, i, &aOp[i]);
+ if( p->db->flags & SQLITE_VdbeListing ){
+ printf("VDBE Program Listing:\n");
+ for(i=0; i<p->nOp; i++){
+ sqlite3VdbePrintOp(stdout, i, &aOp[i]);
+ }
}
+ if( p->db->flags & SQLITE_VdbeEQP ){
+ for(i=0; i<p->nOp; i++){
+ if( aOp[i].opcode==OP_Explain ){
+ if( once ) printf("VDBE Query Plan:\n");
+ printf("%s\n", aOp[i].p4.z);
+ once = 0;
+ }
+ }
+ }
+ if( p->db->flags & SQLITE_VdbeTrace ) printf("VDBE Trace:\n");
}
sqlite3EndBenignMalloc();
#endif
- for(pc=p->pc; rc==SQLITE_OK; pc++){
- assert( pc>=0 && pc<p->nOp );
- if( db->mallocFailed ) goto no_mem;
+ for(pOp=&aOp[p->pc]; 1; pOp++){
+ /* Errors are detected by individual opcodes, with an immediate
+ ** jumps to abort_due_to_error. */
+ assert( rc==SQLITE_OK );
+
+ assert( pOp>=aOp && pOp<&aOp[p->nOp]);
#ifdef VDBE_PROFILE
- origPc = pc;
start = sqlite3Hwtime();
#endif
- pOp = &aOp[pc];
+ nVmStep++;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ if( p->anExec ) p->anExec[(int)(pOp-aOp)]++;
+#endif
/* Only allow tracing if SQLITE_DEBUG is defined.
*/
#ifdef SQLITE_DEBUG
- if( p->trace ){
- if( pc==0 ){
- printf("VDBE Execution Trace:\n");
- sqlite3VdbePrintSql(p);
- }
- sqlite3VdbePrintOp(p->trace, pc, pOp);
+ if( db->flags & SQLITE_VdbeTrace ){
+ sqlite3VdbePrintOp(stdout, (int)(pOp - aOp), pOp);
}
#endif
@@ -64155,73 +77982,46 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
}
#endif
-#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
- /* Call the progress callback if it is configured and the required number
- ** of VDBE ops have been executed (either since this invocation of
- ** sqlite3VdbeExec() or since last time the progress callback was called).
- ** If the progress callback returns non-zero, exit the virtual machine with
- ** a return code SQLITE_ABORT.
- */
- if( checkProgress ){
- if( db->nProgressOps==nProgressOps ){
- int prc;
- prc = db->xProgress(db->pProgressArg);
- if( prc!=0 ){
- rc = SQLITE_INTERRUPT;
- goto vdbe_error_halt;
- }
- nProgressOps = 0;
- }
- nProgressOps++;
- }
-#endif
-
- /* On any opcode with the "out2-prerelase" tag, free any
- ** external allocations out of mem[p2] and set mem[p2] to be
- ** an undefined integer. Opcodes will either fill in the integer
- ** value or convert mem[p2] to a different type.
- */
- assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] );
- if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){
- assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
- pOut = &aMem[pOp->p2];
- memAboutToChange(p, pOut);
- MemReleaseExt(pOut);
- pOut->flags = MEM_Int;
- }
-
/* Sanity checking on other operands */
#ifdef SQLITE_DEBUG
- if( (pOp->opflags & OPFLG_IN1)!=0 ){
- assert( pOp->p1>0 );
- assert( pOp->p1<=p->nMem );
- assert( memIsValid(&aMem[pOp->p1]) );
- REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
- }
- if( (pOp->opflags & OPFLG_IN2)!=0 ){
- assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
- assert( memIsValid(&aMem[pOp->p2]) );
- REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
- }
- if( (pOp->opflags & OPFLG_IN3)!=0 ){
- assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
- assert( memIsValid(&aMem[pOp->p3]) );
- REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
- }
- if( (pOp->opflags & OPFLG_OUT2)!=0 ){
- assert( pOp->p2>0 );
- assert( pOp->p2<=p->nMem );
- memAboutToChange(p, &aMem[pOp->p2]);
- }
- if( (pOp->opflags & OPFLG_OUT3)!=0 ){
- assert( pOp->p3>0 );
- assert( pOp->p3<=p->nMem );
- memAboutToChange(p, &aMem[pOp->p3]);
+ {
+ u8 opProperty = sqlite3OpcodeProperty[pOp->opcode];
+ if( (opProperty & OPFLG_IN1)!=0 ){
+ assert( pOp->p1>0 );
+ assert( pOp->p1<=(p->nMem+1 - p->nCursor) );
+ assert( memIsValid(&aMem[pOp->p1]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p1]) );
+ REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]);
+ }
+ if( (opProperty & OPFLG_IN2)!=0 ){
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
+ assert( memIsValid(&aMem[pOp->p2]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p2]) );
+ REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]);
+ }
+ if( (opProperty & OPFLG_IN3)!=0 ){
+ assert( pOp->p3>0 );
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
+ assert( memIsValid(&aMem[pOp->p3]) );
+ assert( sqlite3VdbeCheckMemInvariants(&aMem[pOp->p3]) );
+ REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]);
+ }
+ if( (opProperty & OPFLG_OUT2)!=0 ){
+ assert( pOp->p2>0 );
+ assert( pOp->p2<=(p->nMem+1 - p->nCursor) );
+ memAboutToChange(p, &aMem[pOp->p2]);
+ }
+ if( (opProperty & OPFLG_OUT3)!=0 ){
+ assert( pOp->p3>0 );
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
+ memAboutToChange(p, &aMem[pOp->p3]);
+ }
}
#endif
+#if defined(SQLITE_DEBUG) || defined(VDBE_PROFILE)
+ pOrigOp = pOp;
+#endif
switch( pOp->opcode ){
@@ -64245,7 +78045,7 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
**
** Other keywords in the comment that follows each case are used to
** construct the OPFLG_INITIALIZER value that initializes opcodeProperty[].
-** Keywords include: in1, in2, in3, out2_prerelease, out2, out3. See
+** Keywords include: in1, in2, in3, out2, out3. See
** the mkopcodeh.awk script for additional information.
**
** Documentation about VDBE opcodes is generated by scanning this file
@@ -64266,10 +78066,45 @@ SQLITE_PRIVATE int sqlite3VdbeExec(
** The next instruction executed will be
** the one at index P2 from the beginning of
** the program.
+**
+** The P1 parameter is not actually used by this opcode. However, it
+** is sometimes set to 1 instead of 0 as a hint to the command-line shell
+** that this Goto is the bottom of a loop and that the lines from P2 down
+** to the current line should be indented for EXPLAIN output.
*/
case OP_Goto: { /* jump */
- CHECK_FOR_INTERRUPT;
- pc = pOp->p2 - 1;
+jump_to_p2_and_check_for_interrupt:
+ pOp = &aOp[pOp->p2 - 1];
+
+ /* Opcodes that are used as the bottom of a loop (OP_Next, OP_Prev,
+ ** OP_VNext, OP_RowSetNext, or OP_SorterNext) all jump here upon
+ ** completion. Check to see if sqlite3_interrupt() has been called
+ ** or if the progress callback needs to be invoked.
+ **
+ ** This code uses unstructured "goto" statements and does not look clean.
+ ** But that is not due to sloppy coding habits. The code is written this
+ ** way for performance, to avoid having to run the interrupt and progress
+ ** checks on every opcode. This helps sqlite3_step() to run about 1.5%
+ ** faster according to "valgrind --tool=cachegrind" */
+check_for_interrupt:
+ if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Call the progress callback if it is configured and the required number
+ ** of VDBE ops have been executed (either since this invocation of
+ ** sqlite3VdbeExec() or since last time the progress callback was called).
+ ** If the progress callback returns non-zero, exit the virtual machine with
+ ** a return code SQLITE_ABORT.
+ */
+ if( db->xProgress!=0 && nVmStep>=nProgressLimit ){
+ assert( db->nProgressOps!=0 );
+ nProgressLimit = nVmStep + db->nProgressOps - (nVmStep%db->nProgressOps);
+ if( db->xProgress(db->pProgressArg) ){
+ rc = SQLITE_INTERRUPT;
+ goto abort_due_to_error;
+ }
+ }
+#endif
+
break;
}
@@ -64278,51 +78113,111 @@ case OP_Goto: { /* jump */
** Write the current address onto register P1
** and then jump to address P2.
*/
-case OP_Gosub: { /* jump, in1 */
+case OP_Gosub: { /* jump */
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
pIn1 = &aMem[pOp->p1];
- assert( (pIn1->flags & MEM_Dyn)==0 );
+ assert( VdbeMemDynamic(pIn1)==0 );
memAboutToChange(p, pIn1);
pIn1->flags = MEM_Int;
- pIn1->u.i = pc;
+ pIn1->u.i = (int)(pOp-aOp);
REGISTER_TRACE(pOp->p1, pIn1);
- pc = pOp->p2 - 1;
+
+ /* Most jump operations do a goto to this spot in order to update
+ ** the pOp pointer. */
+jump_to_p2:
+ pOp = &aOp[pOp->p2 - 1];
break;
}
/* Opcode: Return P1 * * * *
**
-** Jump to the next instruction after the address in register P1.
+** Jump to the next instruction after the address in register P1. After
+** the jump, register P1 becomes undefined.
*/
case OP_Return: { /* in1 */
pIn1 = &aMem[pOp->p1];
- assert( pIn1->flags & MEM_Int );
- pc = (int)pIn1->u.i;
+ assert( pIn1->flags==MEM_Int );
+ pOp = &aOp[pIn1->u.i];
+ pIn1->flags = MEM_Undefined;
+ break;
+}
+
+/* Opcode: InitCoroutine P1 P2 P3 * *
+**
+** Set up register P1 so that it will Yield to the coroutine
+** located at address P3.
+**
+** If P2!=0 then the coroutine implementation immediately follows
+** this opcode. So jump over the coroutine implementation to
+** address P2.
+**
+** See also: EndCoroutine
+*/
+case OP_InitCoroutine: { /* jump */
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+ assert( pOp->p2>=0 && pOp->p2<p->nOp );
+ assert( pOp->p3>=0 && pOp->p3<p->nOp );
+ pOut = &aMem[pOp->p1];
+ assert( !VdbeMemDynamic(pOut) );
+ pOut->u.i = pOp->p3 - 1;
+ pOut->flags = MEM_Int;
+ if( pOp->p2 ) goto jump_to_p2;
break;
}
-/* Opcode: Yield P1 * * * *
+/* Opcode: EndCoroutine P1 * * * *
**
-** Swap the program counter with the value in register P1.
+** The instruction at the address in register P1 is a Yield.
+** Jump to the P2 parameter of that Yield.
+** After the jump, register P1 becomes undefined.
+**
+** See also: InitCoroutine
+*/
+case OP_EndCoroutine: { /* in1 */
+ VdbeOp *pCaller;
+ pIn1 = &aMem[pOp->p1];
+ assert( pIn1->flags==MEM_Int );
+ assert( pIn1->u.i>=0 && pIn1->u.i<p->nOp );
+ pCaller = &aOp[pIn1->u.i];
+ assert( pCaller->opcode==OP_Yield );
+ assert( pCaller->p2>=0 && pCaller->p2<p->nOp );
+ pOp = &aOp[pCaller->p2 - 1];
+ pIn1->flags = MEM_Undefined;
+ break;
+}
+
+/* Opcode: Yield P1 P2 * * *
+**
+** Swap the program counter with the value in register P1. This
+** has the effect of yielding to a coroutine.
+**
+** If the coroutine that is launched by this instruction ends with
+** Yield or Return then continue to the next instruction. But if
+** the coroutine launched by this instruction ends with
+** EndCoroutine, then jump to P2 rather than continuing with the
+** next instruction.
+**
+** See also: InitCoroutine
*/
-case OP_Yield: { /* in1 */
-#if 0 /* local variables moved into u.aa */
+case OP_Yield: { /* in1, jump */
int pcDest;
-#endif /* local variables moved into u.aa */
pIn1 = &aMem[pOp->p1];
- assert( (pIn1->flags & MEM_Dyn)==0 );
+ assert( VdbeMemDynamic(pIn1)==0 );
pIn1->flags = MEM_Int;
- u.aa.pcDest = (int)pIn1->u.i;
- pIn1->u.i = pc;
+ pcDest = (int)pIn1->u.i;
+ pIn1->u.i = (int)(pOp - aOp);
REGISTER_TRACE(pOp->p1, pIn1);
- pc = u.aa.pcDest;
+ pOp = &aOp[pcDest];
break;
}
-/* Opcode: HaltIfNull P1 P2 P3 P4 *
+/* Opcode: HaltIfNull P1 P2 P3 P4 P5
+** Synopsis: if r[P3]=null halt
**
** Check the value in register P3. If it is NULL then Halt using
** parameter P1, P2, and P4 as if this were a Halt instruction. If the
** value in register P3 is not NULL, then this routine is a no-op.
+** The P5 parameter should be 1.
*/
case OP_HaltIfNull: { /* in3 */
pIn3 = &aMem[pOp->p3];
@@ -64330,7 +78225,7 @@ case OP_HaltIfNull: { /* in3 */
/* Fall through into OP_Halt */
}
-/* Opcode: Halt P1 P2 * P4 *
+/* Opcode: Halt P1 P2 * P4 P5
**
** Exit immediately. All open cursors, etc are closed
** automatically.
@@ -64345,71 +78240,99 @@ case OP_HaltIfNull: { /* in3 */
**
** If P4 is not null then it is an error message string.
**
+** P5 is a value between 0 and 4, inclusive, that modifies the P4 string.
+**
+** 0: (no change)
+** 1: NOT NULL contraint failed: P4
+** 2: UNIQUE constraint failed: P4
+** 3: CHECK constraint failed: P4
+** 4: FOREIGN KEY constraint failed: P4
+**
+** If P5 is not zero and P4 is NULL, then everything after the ":" is
+** omitted.
+**
** There is an implied "Halt 0 0 0" instruction inserted at the very end of
** every program. So a jump past the last instruction of the program
** is the same as executing Halt.
*/
case OP_Halt: {
+ VdbeFrame *pFrame;
+ int pcx;
+
+ pcx = (int)(pOp - aOp);
if( pOp->p1==SQLITE_OK && p->pFrame ){
/* Halt the sub-program. Return control to the parent frame. */
- VdbeFrame *pFrame = p->pFrame;
+ pFrame = p->pFrame;
p->pFrame = pFrame->pParent;
p->nFrame--;
sqlite3VdbeSetChanges(db, p->nChange);
- pc = sqlite3VdbeFrameRestore(pFrame);
+ pcx = sqlite3VdbeFrameRestore(pFrame);
lastRowid = db->lastRowid;
if( pOp->p2==OE_Ignore ){
- /* Instruction pc is the OP_Program that invoked the sub-program
+ /* Instruction pcx is the OP_Program that invoked the sub-program
** currently being halted. If the p2 instruction of this OP_Halt
** instruction is set to OE_Ignore, then the sub-program is throwing
** an IGNORE exception. In this case jump to the address specified
** as the p2 of the calling OP_Program. */
- pc = p->aOp[pc].p2-1;
+ pcx = p->aOp[pcx].p2-1;
}
aOp = p->aOp;
aMem = p->aMem;
+ pOp = &aOp[pcx];
break;
}
-
p->rc = pOp->p1;
p->errorAction = (u8)pOp->p2;
- p->pc = pc;
- if( pOp->p4.z ){
- assert( p->rc!=SQLITE_OK );
- sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z);
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pc, p->zSql, pOp->p4.z);
- }else if( p->rc ){
- testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(pOp->p1, "constraint failed at %d in [%s]", pc, p->zSql);
+ p->pc = pcx;
+ assert( pOp->p5>=0 && pOp->p5<=4 );
+ if( p->rc ){
+ if( pOp->p5 ){
+ static const char * const azType[] = { "NOT NULL", "UNIQUE", "CHECK",
+ "FOREIGN KEY" };
+ testcase( pOp->p5==1 );
+ testcase( pOp->p5==2 );
+ testcase( pOp->p5==3 );
+ testcase( pOp->p5==4 );
+ sqlite3VdbeError(p, "%s constraint failed", azType[pOp->p5-1]);
+ if( pOp->p4.z ){
+ p->zErrMsg = sqlite3MPrintf(db, "%z: %s", p->zErrMsg, pOp->p4.z);
+ }
+ }else{
+ sqlite3VdbeError(p, "%s", pOp->p4.z);
+ }
+ sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
if( rc==SQLITE_BUSY ){
- p->rc = rc = SQLITE_BUSY;
+ p->rc = SQLITE_BUSY;
}else{
- assert( rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT );
- assert( rc==SQLITE_OK || db->nDeferredCons>0 );
+ assert( rc==SQLITE_OK || (p->rc&0xff)==SQLITE_CONSTRAINT );
+ assert( rc==SQLITE_OK || db->nDeferredCons>0 || db->nDeferredImmCons>0 );
rc = p->rc ? SQLITE_ERROR : SQLITE_DONE;
}
goto vdbe_return;
}
/* Opcode: Integer P1 P2 * * *
+** Synopsis: r[P2]=P1
**
** The 32-bit integer value P1 is written into register P2.
*/
-case OP_Integer: { /* out2-prerelease */
+case OP_Integer: { /* out2 */
+ pOut = out2Prerelease(p, pOp);
pOut->u.i = pOp->p1;
break;
}
/* Opcode: Int64 * P2 * P4 *
+** Synopsis: r[P2]=P4
**
** P4 is a pointer to a 64-bit integer value.
** Write that value into register P2.
*/
-case OP_Int64: { /* out2-prerelease */
+case OP_Int64: { /* out2 */
+ pOut = out2Prerelease(p, pOp);
assert( pOp->p4.pI64!=0 );
pOut->u.i = *pOp->p4.pI64;
break;
@@ -64417,38 +78340,43 @@ case OP_Int64: { /* out2-prerelease */
#ifndef SQLITE_OMIT_FLOATING_POINT
/* Opcode: Real * P2 * P4 *
+** Synopsis: r[P2]=P4
**
** P4 is a pointer to a 64-bit floating point value.
** Write that value into register P2.
*/
-case OP_Real: { /* same as TK_FLOAT, out2-prerelease */
+case OP_Real: { /* same as TK_FLOAT, out2 */
+ pOut = out2Prerelease(p, pOp);
pOut->flags = MEM_Real;
assert( !sqlite3IsNaN(*pOp->p4.pReal) );
- pOut->r = *pOp->p4.pReal;
+ pOut->u.r = *pOp->p4.pReal;
break;
}
#endif
/* Opcode: String8 * P2 * P4 *
+** Synopsis: r[P2]='P4'
**
** P4 points to a nul terminated UTF-8 string. This opcode is transformed
-** into an OP_String before it is executed for the first time.
+** into a String opcode before it is executed for the first time. During
+** this transformation, the length of string P4 is computed and stored
+** as the P1 parameter.
*/
-case OP_String8: { /* same as TK_STRING, out2-prerelease */
+case OP_String8: { /* same as TK_STRING, out2 */
assert( pOp->p4.z!=0 );
+ pOut = out2Prerelease(p, pOp);
pOp->opcode = OP_String;
pOp->p1 = sqlite3Strlen30(pOp->p4.z);
#ifndef SQLITE_OMIT_UTF16
if( encoding!=SQLITE_UTF8 ){
rc = sqlite3VdbeMemSetStr(pOut, pOp->p4.z, -1, SQLITE_UTF8, SQLITE_STATIC);
- if( rc==SQLITE_TOOBIG ) goto too_big;
+ assert( rc==SQLITE_OK || rc==SQLITE_TOOBIG );
if( SQLITE_OK!=sqlite3VdbeChangeEncoding(pOut, encoding) ) goto no_mem;
- assert( pOut->zMalloc==pOut->z );
- assert( pOut->flags & MEM_Dyn );
- pOut->zMalloc = 0;
+ assert( pOut->szMalloc>0 && pOut->zMalloc==pOut->z );
+ assert( VdbeMemDynamic(pOut)==0 );
+ pOut->szMalloc = 0;
pOut->flags |= MEM_Static;
- pOut->flags &= ~MEM_Dyn;
if( pOp->p4type==P4_DYNAMIC ){
sqlite3DbFree(db, pOp->p4.z);
}
@@ -64456,44 +78384,101 @@ case OP_String8: { /* same as TK_STRING, out2-prerelease */
pOp->p4.z = pOut->z;
pOp->p1 = pOut->n;
}
+ testcase( rc==SQLITE_TOOBIG );
#endif
if( pOp->p1>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
+ assert( rc==SQLITE_OK );
/* Fall through to the next case, OP_String */
}
-/* Opcode: String P1 P2 * P4 *
+/* Opcode: String P1 P2 P3 P4 P5
+** Synopsis: r[P2]='P4' (len=P1)
**
** The string value P4 of length P1 (bytes) is stored in register P2.
+**
+** If P3 is not zero and the content of register P3 is equal to P5, then
+** the datatype of the register P2 is converted to BLOB. The content is
+** the same sequence of bytes, it is merely interpreted as a BLOB instead
+** of a string, as if it had been CAST. In other words:
+**
+** if( P3!=0 and reg[P3]==P5 ) reg[P2] := CAST(reg[P2] as BLOB)
*/
-case OP_String: { /* out2-prerelease */
+case OP_String: { /* out2 */
assert( pOp->p4.z!=0 );
+ pOut = out2Prerelease(p, pOp);
pOut->flags = MEM_Str|MEM_Static|MEM_Term;
pOut->z = pOp->p4.z;
pOut->n = pOp->p1;
pOut->enc = encoding;
UPDATE_MAX_BLOBSIZE(pOut);
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ if( pOp->p3>0 ){
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
+ pIn3 = &aMem[pOp->p3];
+ assert( pIn3->flags & MEM_Int );
+ if( pIn3->u.i==pOp->p5 ) pOut->flags = MEM_Blob|MEM_Static|MEM_Term;
+ }
+#endif
break;
}
-/* Opcode: Null * P2 * * *
+/* Opcode: Null P1 P2 P3 * *
+** Synopsis: r[P2..P3]=NULL
+**
+** Write a NULL into registers P2. If P3 greater than P2, then also write
+** NULL into register P3 and every register in between P2 and P3. If P3
+** is less than P2 (typically P3 is zero) then only register P2 is
+** set to NULL.
**
-** Write a NULL into register P2.
+** If the P1 value is non-zero, then also set the MEM_Cleared flag so that
+** NULL values will not compare equal even if SQLITE_NULLEQ is set on
+** OP_Ne or OP_Eq.
*/
-case OP_Null: { /* out2-prerelease */
- pOut->flags = MEM_Null;
+case OP_Null: { /* out2 */
+ int cnt;
+ u16 nullFlag;
+ pOut = out2Prerelease(p, pOp);
+ cnt = pOp->p3-pOp->p2;
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
+ pOut->flags = nullFlag = pOp->p1 ? (MEM_Null|MEM_Cleared) : MEM_Null;
+ pOut->n = 0;
+ while( cnt>0 ){
+ pOut++;
+ memAboutToChange(p, pOut);
+ sqlite3VdbeMemSetNull(pOut);
+ pOut->flags = nullFlag;
+ pOut->n = 0;
+ cnt--;
+ }
break;
}
+/* Opcode: SoftNull P1 * * * *
+** Synopsis: r[P1]=NULL
+**
+** Set register P1 to have the value NULL as seen by the OP_MakeRecord
+** instruction, but do not free any string or blob memory associated with
+** the register, so that if the value was a string or blob that was
+** previously copied using OP_SCopy, the copies will continue to be valid.
+*/
+case OP_SoftNull: {
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+ pOut = &aMem[pOp->p1];
+ pOut->flags = (pOut->flags|MEM_Null)&~MEM_Undefined;
+ break;
+}
-/* Opcode: Blob P1 P2 * P4
+/* Opcode: Blob P1 P2 * P4 *
+** Synopsis: r[P2]=P4 (len=P1)
**
** P4 points to a blob of data P1 bytes long. Store this
** blob in register P2.
*/
-case OP_Blob: { /* out2-prerelease */
+case OP_Blob: { /* out2 */
assert( pOp->p1 <= SQLITE_MAX_LENGTH );
+ pOut = out2Prerelease(p, pOp);
sqlite3VdbeMemSetStr(pOut, pOp->p4.z, pOp->p1, 0, 0);
pOut->enc = encoding;
UPDATE_MAX_BLOBSIZE(pOut);
@@ -64501,90 +78486,100 @@ case OP_Blob: { /* out2-prerelease */
}
/* Opcode: Variable P1 P2 * P4 *
+** Synopsis: r[P2]=parameter(P1,P4)
**
** Transfer the values of bound parameter P1 into register P2
**
-** If the parameter is named, then its name appears in P4 and P3==1.
+** If the parameter is named, then its name appears in P4.
** The P4 value is used by sqlite3_bind_parameter_name().
*/
-case OP_Variable: { /* out2-prerelease */
-#if 0 /* local variables moved into u.ab */
+case OP_Variable: { /* out2 */
Mem *pVar; /* Value being transferred */
-#endif /* local variables moved into u.ab */
assert( pOp->p1>0 && pOp->p1<=p->nVar );
assert( pOp->p4.z==0 || pOp->p4.z==p->azVar[pOp->p1-1] );
- u.ab.pVar = &p->aVar[pOp->p1 - 1];
- if( sqlite3VdbeMemTooBig(u.ab.pVar) ){
+ pVar = &p->aVar[pOp->p1 - 1];
+ if( sqlite3VdbeMemTooBig(pVar) ){
goto too_big;
}
- sqlite3VdbeMemShallowCopy(pOut, u.ab.pVar, MEM_Static);
+ pOut = out2Prerelease(p, pOp);
+ sqlite3VdbeMemShallowCopy(pOut, pVar, MEM_Static);
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
/* Opcode: Move P1 P2 P3 * *
+** Synopsis: r[P2@P3]=r[P1@P3]
**
-** Move the values in register P1..P1+P3-1 over into
-** registers P2..P2+P3-1. Registers P1..P1+P1-1 are
+** Move the P3 values in register P1..P1+P3-1 over into
+** registers P2..P2+P3-1. Registers P1..P1+P3-1 are
** left holding a NULL. It is an error for register ranges
-** P1..P1+P3-1 and P2..P2+P3-1 to overlap.
+** P1..P1+P3-1 and P2..P2+P3-1 to overlap. It is an error
+** for P3 to be less than 1.
*/
case OP_Move: {
-#if 0 /* local variables moved into u.ac */
- char *zMalloc; /* Holding variable for allocated memory */
int n; /* Number of registers left to copy */
int p1; /* Register to copy from */
int p2; /* Register to copy to */
-#endif /* local variables moved into u.ac */
-
- u.ac.n = pOp->p3;
- u.ac.p1 = pOp->p1;
- u.ac.p2 = pOp->p2;
- assert( u.ac.n>0 && u.ac.p1>0 && u.ac.p2>0 );
- assert( u.ac.p1+u.ac.n<=u.ac.p2 || u.ac.p2+u.ac.n<=u.ac.p1 );
-
- pIn1 = &aMem[u.ac.p1];
- pOut = &aMem[u.ac.p2];
- while( u.ac.n-- ){
- assert( pOut<=&aMem[p->nMem] );
- assert( pIn1<=&aMem[p->nMem] );
+
+ n = pOp->p3;
+ p1 = pOp->p1;
+ p2 = pOp->p2;
+ assert( n>0 && p1>0 && p2>0 );
+ assert( p1+n<=p2 || p2+n<=p1 );
+
+ pIn1 = &aMem[p1];
+ pOut = &aMem[p2];
+ do{
+ assert( pOut<=&aMem[(p->nMem+1 - p->nCursor)] );
+ assert( pIn1<=&aMem[(p->nMem+1 - p->nCursor)] );
assert( memIsValid(pIn1) );
memAboutToChange(p, pOut);
- u.ac.zMalloc = pOut->zMalloc;
- pOut->zMalloc = 0;
sqlite3VdbeMemMove(pOut, pIn1);
#ifdef SQLITE_DEBUG
- if( pOut->pScopyFrom>=&aMem[u.ac.p1] && pOut->pScopyFrom<&aMem[u.ac.p1+pOp->p3] ){
- pOut->pScopyFrom += u.ac.p1 - pOp->p2;
+ if( pOut->pScopyFrom>=&aMem[p1] && pOut->pScopyFrom<pOut ){
+ pOut->pScopyFrom += pOp->p2 - p1;
}
#endif
- pIn1->zMalloc = u.ac.zMalloc;
- REGISTER_TRACE(u.ac.p2++, pOut);
+ Deephemeralize(pOut);
+ REGISTER_TRACE(p2++, pOut);
pIn1++;
pOut++;
- }
+ }while( --n );
break;
}
-/* Opcode: Copy P1 P2 * * *
+/* Opcode: Copy P1 P2 P3 * *
+** Synopsis: r[P2@P3+1]=r[P1@P3+1]
**
-** Make a copy of register P1 into register P2.
+** Make a copy of registers P1..P1+P3 into registers P2..P2+P3.
**
** This instruction makes a deep copy of the value. A duplicate
** is made of any string or blob constant. See also OP_SCopy.
*/
-case OP_Copy: { /* in1, out2 */
+case OP_Copy: {
+ int n;
+
+ n = pOp->p3;
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
assert( pOut!=pIn1 );
- sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
- Deephemeralize(pOut);
- REGISTER_TRACE(pOp->p2, pOut);
+ while( 1 ){
+ sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem);
+ Deephemeralize(pOut);
+#ifdef SQLITE_DEBUG
+ pOut->pScopyFrom = 0;
+#endif
+ REGISTER_TRACE(pOp->p2+pOp->p3-n, pOut);
+ if( (n--)==0 ) break;
+ pOut++;
+ pIn1++;
+ }
break;
}
/* Opcode: SCopy P1 P2 * * *
+** Synopsis: r[P2]=r[P1]
**
** Make a shallow copy of register P1 into register P2.
**
@@ -64596,7 +78591,7 @@ case OP_Copy: { /* in1, out2 */
** during the lifetime of the copy. Use OP_Copy to make a complete
** copy.
*/
-case OP_SCopy: { /* in1, out2 */
+case OP_SCopy: { /* out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
assert( pOut!=pIn1 );
@@ -64604,26 +78599,52 @@ case OP_SCopy: { /* in1, out2 */
#ifdef SQLITE_DEBUG
if( pOut->pScopyFrom==0 ) pOut->pScopyFrom = pIn1;
#endif
- REGISTER_TRACE(pOp->p2, pOut);
+ break;
+}
+
+/* Opcode: IntCopy P1 P2 * * *
+** Synopsis: r[P2]=r[P1]
+**
+** Transfer the integer value held in register P1 into register P2.
+**
+** This is an optimized version of SCopy that works only for integer
+** values.
+*/
+case OP_IntCopy: { /* out2 */
+ pIn1 = &aMem[pOp->p1];
+ assert( (pIn1->flags & MEM_Int)!=0 );
+ pOut = &aMem[pOp->p2];
+ sqlite3VdbeMemSetInt64(pOut, pIn1->u.i);
break;
}
/* Opcode: ResultRow P1 P2 * * *
+** Synopsis: output=r[P1@P2]
**
** The registers P1 through P1+P2-1 contain a single row of
** results. This opcode causes the sqlite3_step() call to terminate
** with an SQLITE_ROW return code and it sets up the sqlite3_stmt
-** structure to provide access to the top P1 values as the result
-** row.
+** structure to provide access to the r(P1)..r(P1+P2-1) values as
+** the result row.
*/
case OP_ResultRow: {
-#if 0 /* local variables moved into u.ad */
Mem *pMem;
int i;
-#endif /* local variables moved into u.ad */
assert( p->nResColumn==pOp->p2 );
assert( pOp->p1>0 );
- assert( pOp->p1+pOp->p2<=p->nMem+1 );
+ assert( pOp->p1+pOp->p2<=(p->nMem+1 - p->nCursor)+1 );
+
+#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
+ /* Run the progress counter just before returning.
+ */
+ if( db->xProgress!=0
+ && nVmStep>=nProgressLimit
+ && db->xProgress(db->pProgressArg)!=0
+ ){
+ rc = SQLITE_INTERRUPT;
+ goto abort_due_to_error;
+ }
+#endif
/* If this statement has violated immediate foreign key constraints, do
** not return the number of rows modified. And do not RELEASE the statement
@@ -64631,11 +78652,11 @@ case OP_ResultRow: {
if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){
assert( db->flags&SQLITE_CountRows );
assert( p->usesStmtJournal );
- break;
+ goto abort_due_to_error;
}
- /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
- ** DML statements invoke this opcode to return the number of rows
+ /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then
+ ** DML statements invoke this opcode to return the number of rows
** modified to the user. This is the only way that a VM that
** opens a statement transaction may invoke this opcode.
**
@@ -64651,37 +78672,39 @@ case OP_ResultRow: {
*/
assert( p->iStatement==0 || db->flags&SQLITE_CountRows );
rc = sqlite3VdbeCloseStatement(p, SAVEPOINT_RELEASE);
- if( NEVER(rc!=SQLITE_OK) ){
- break;
- }
+ assert( rc==SQLITE_OK );
/* Invalidate all ephemeral cursor row caches */
p->cacheCtr = (p->cacheCtr + 2)|1;
/* Make sure the results of the current row are \000 terminated
** and have an assigned type. The results are de-ephemeralized as
- ** as side effect.
- */
- u.ad.pMem = p->pResultSet = &aMem[pOp->p1];
- for(u.ad.i=0; u.ad.i<pOp->p2; u.ad.i++){
- assert( memIsValid(&u.ad.pMem[u.ad.i]) );
- Deephemeralize(&u.ad.pMem[u.ad.i]);
- assert( (u.ad.pMem[u.ad.i].flags & MEM_Ephem)==0
- || (u.ad.pMem[u.ad.i].flags & (MEM_Str|MEM_Blob))==0 );
- sqlite3VdbeMemNulTerminate(&u.ad.pMem[u.ad.i]);
- sqlite3VdbeMemStoreType(&u.ad.pMem[u.ad.i]);
- REGISTER_TRACE(pOp->p1+u.ad.i, &u.ad.pMem[u.ad.i]);
+ ** a side effect.
+ */
+ pMem = p->pResultSet = &aMem[pOp->p1];
+ for(i=0; i<pOp->p2; i++){
+ assert( memIsValid(&pMem[i]) );
+ Deephemeralize(&pMem[i]);
+ assert( (pMem[i].flags & MEM_Ephem)==0
+ || (pMem[i].flags & (MEM_Str|MEM_Blob))==0 );
+ sqlite3VdbeMemNulTerminate(&pMem[i]);
+ REGISTER_TRACE(pOp->p1+i, &pMem[i]);
}
if( db->mallocFailed ) goto no_mem;
+ if( db->mTrace & SQLITE_TRACE_ROW ){
+ db->xTrace(SQLITE_TRACE_ROW, db->pTraceArg, p, 0);
+ }
+
/* Return SQLITE_ROW
*/
- p->pc = pc + 1;
+ p->pc = (int)(pOp - aOp) + 1;
rc = SQLITE_ROW;
goto vdbe_return;
}
/* Opcode: Concat P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]+r[P1]
**
** Add the text in register P1 onto the end of the text in
** register P2 and store the result in register P3.
@@ -64694,9 +78717,7 @@ case OP_ResultRow: {
** to avoid a memcpy().
*/
case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
-#if 0 /* local variables moved into u.ae */
i64 nByte;
-#endif /* local variables moved into u.ae */
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
@@ -64709,34 +78730,36 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
if( ExpandBlob(pIn1) || ExpandBlob(pIn2) ) goto no_mem;
Stringify(pIn1, encoding);
Stringify(pIn2, encoding);
- u.ae.nByte = pIn1->n + pIn2->n;
- if( u.ae.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ nByte = pIn1->n + pIn2->n;
+ if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- MemSetTypeFlag(pOut, MEM_Str);
- if( sqlite3VdbeMemGrow(pOut, (int)u.ae.nByte+2, pOut==pIn2) ){
+ if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){
goto no_mem;
}
+ MemSetTypeFlag(pOut, MEM_Str);
if( pOut!=pIn2 ){
memcpy(pOut->z, pIn2->z, pIn2->n);
}
memcpy(&pOut->z[pIn2->n], pIn1->z, pIn1->n);
- pOut->z[u.ae.nByte] = 0;
- pOut->z[u.ae.nByte+1] = 0;
+ pOut->z[nByte]=0;
+ pOut->z[nByte+1] = 0;
pOut->flags |= MEM_Term;
- pOut->n = (int)u.ae.nByte;
+ pOut->n = (int)nByte;
pOut->enc = encoding;
UPDATE_MAX_BLOBSIZE(pOut);
break;
}
/* Opcode: Add P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]+r[P2]
**
** Add the value in register P1 to the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Multiply P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]*r[P2]
**
**
** Multiply the value in register P1 by the value in register P2
@@ -64744,12 +78767,14 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** If either input is NULL, the result is NULL.
*/
/* Opcode: Subtract P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]-r[P1]
**
** Subtract the value in register P1 from the value in register P2
** and store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: Divide P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]/r[P1]
**
** Divide the value in register P1 by the value in register P2
** and store the result in register P3 (P3=P2/P1). If the value in
@@ -64757,10 +78782,11 @@ case OP_Concat: { /* same as TK_CONCAT, in1, in2, out3 */
** NULL, the result is NULL.
*/
/* Opcode: Remainder P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]%r[P1]
**
-** Compute the remainder after integer division of the value in
-** register P1 by the value in register P2 and store the result in P3.
-** If the value in register P2 is zero the result is NULL.
+** Compute the remainder after integer register P2 is divided by
+** register P1 and store the result in register P3.
+** If the value in register P1 is zero the result is NULL.
** If either operand is NULL, the result is NULL.
*/
case OP_Add: /* same as TK_PLUS, in1, in2, out3 */
@@ -64768,76 +78794,79 @@ case OP_Subtract: /* same as TK_MINUS, in1, in2, out3 */
case OP_Multiply: /* same as TK_STAR, in1, in2, out3 */
case OP_Divide: /* same as TK_SLASH, in1, in2, out3 */
case OP_Remainder: { /* same as TK_REM, in1, in2, out3 */
-#if 0 /* local variables moved into u.af */
- int flags; /* Combined MEM_* flags from both inputs */
+ char bIntint; /* Started out as two integer operands */
+ u16 flags; /* Combined MEM_* flags from both inputs */
+ u16 type1; /* Numeric type of left operand */
+ u16 type2; /* Numeric type of right operand */
i64 iA; /* Integer value of left operand */
i64 iB; /* Integer value of right operand */
double rA; /* Real value of left operand */
double rB; /* Real value of right operand */
-#endif /* local variables moved into u.af */
pIn1 = &aMem[pOp->p1];
- applyNumericAffinity(pIn1);
+ type1 = numericType(pIn1);
pIn2 = &aMem[pOp->p2];
- applyNumericAffinity(pIn2);
+ type2 = numericType(pIn2);
pOut = &aMem[pOp->p3];
- u.af.flags = pIn1->flags | pIn2->flags;
- if( (u.af.flags & MEM_Null)!=0 ) goto arithmetic_result_is_null;
- if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){
- u.af.iA = pIn1->u.i;
- u.af.iB = pIn2->u.i;
+ flags = pIn1->flags | pIn2->flags;
+ if( (flags & MEM_Null)!=0 ) goto arithmetic_result_is_null;
+ if( (type1 & type2 & MEM_Int)!=0 ){
+ iA = pIn1->u.i;
+ iB = pIn2->u.i;
+ bIntint = 1;
switch( pOp->opcode ){
- case OP_Add: if( sqlite3AddInt64(&u.af.iB,u.af.iA) ) goto fp_math; break;
- case OP_Subtract: if( sqlite3SubInt64(&u.af.iB,u.af.iA) ) goto fp_math; break;
- case OP_Multiply: if( sqlite3MulInt64(&u.af.iB,u.af.iA) ) goto fp_math; break;
+ case OP_Add: if( sqlite3AddInt64(&iB,iA) ) goto fp_math; break;
+ case OP_Subtract: if( sqlite3SubInt64(&iB,iA) ) goto fp_math; break;
+ case OP_Multiply: if( sqlite3MulInt64(&iB,iA) ) goto fp_math; break;
case OP_Divide: {
- if( u.af.iA==0 ) goto arithmetic_result_is_null;
- if( u.af.iA==-1 && u.af.iB==SMALLEST_INT64 ) goto fp_math;
- u.af.iB /= u.af.iA;
+ if( iA==0 ) goto arithmetic_result_is_null;
+ if( iA==-1 && iB==SMALLEST_INT64 ) goto fp_math;
+ iB /= iA;
break;
}
default: {
- if( u.af.iA==0 ) goto arithmetic_result_is_null;
- if( u.af.iA==-1 ) u.af.iA = 1;
- u.af.iB %= u.af.iA;
+ if( iA==0 ) goto arithmetic_result_is_null;
+ if( iA==-1 ) iA = 1;
+ iB %= iA;
break;
}
}
- pOut->u.i = u.af.iB;
+ pOut->u.i = iB;
MemSetTypeFlag(pOut, MEM_Int);
}else{
+ bIntint = 0;
fp_math:
- u.af.rA = sqlite3VdbeRealValue(pIn1);
- u.af.rB = sqlite3VdbeRealValue(pIn2);
+ rA = sqlite3VdbeRealValue(pIn1);
+ rB = sqlite3VdbeRealValue(pIn2);
switch( pOp->opcode ){
- case OP_Add: u.af.rB += u.af.rA; break;
- case OP_Subtract: u.af.rB -= u.af.rA; break;
- case OP_Multiply: u.af.rB *= u.af.rA; break;
+ case OP_Add: rB += rA; break;
+ case OP_Subtract: rB -= rA; break;
+ case OP_Multiply: rB *= rA; break;
case OP_Divide: {
/* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
- if( u.af.rA==(double)0 ) goto arithmetic_result_is_null;
- u.af.rB /= u.af.rA;
+ if( rA==(double)0 ) goto arithmetic_result_is_null;
+ rB /= rA;
break;
}
default: {
- u.af.iA = (i64)u.af.rA;
- u.af.iB = (i64)u.af.rB;
- if( u.af.iA==0 ) goto arithmetic_result_is_null;
- if( u.af.iA==-1 ) u.af.iA = 1;
- u.af.rB = (double)(u.af.iB % u.af.iA);
+ iA = (i64)rA;
+ iB = (i64)rB;
+ if( iA==0 ) goto arithmetic_result_is_null;
+ if( iA==-1 ) iA = 1;
+ rB = (double)(iB % iA);
break;
}
}
#ifdef SQLITE_OMIT_FLOATING_POINT
- pOut->u.i = u.af.rB;
+ pOut->u.i = rB;
MemSetTypeFlag(pOut, MEM_Int);
#else
- if( sqlite3IsNaN(u.af.rB) ){
+ if( sqlite3IsNaN(rB) ){
goto arithmetic_result_is_null;
}
- pOut->r = u.af.rB;
+ pOut->u.r = rB;
MemSetTypeFlag(pOut, MEM_Real);
- if( (u.af.flags & MEM_Real)==0 ){
+ if( ((type1|type2)&MEM_Real)==0 && !bIntint ){
sqlite3VdbeIntegerAffinity(pOut);
}
#endif
@@ -64849,25 +78878,33 @@ arithmetic_result_is_null:
break;
}
-/* Opcode: CollSeq * * P4
+/* Opcode: CollSeq P1 * * P4
**
** P4 is a pointer to a CollSeq struct. If the next call to a user function
** or aggregate calls sqlite3GetFuncCollSeq(), this collation sequence will
** be returned. This is used by the built-in min(), max() and nullif()
** functions.
**
+** If P1 is not zero, then it is a register that a subsequent min() or
+** max() aggregate will set to 1 if the current row is not the minimum or
+** maximum. The P1 register is initialized to 0 by this instruction.
+**
** The interface used by the implementation of the aforementioned functions
** to retrieve the collation sequence set by this opcode is not available
-** publicly, only to user functions defined in func.c.
+** publicly. Only built-in functions have access to this feature.
*/
case OP_CollSeq: {
assert( pOp->p4type==P4_COLLSEQ );
+ if( pOp->p1 ){
+ sqlite3VdbeMemSetInt64(&aMem[pOp->p1], 0);
+ }
break;
}
-/* Opcode: Function P1 P2 P3 P4 P5
+/* Opcode: Function0 P1 P2 P3 P4 P5
+** Synopsis: r[P3]=func(r[P2@P5])
**
-** Invoke a user function (P4 is a pointer to a Function structure that
+** Invoke a user function (P4 is a pointer to a FuncDef object that
** defines the function) with P5 arguments taken from register P2 and
** successors. The result of the function is stored in register P3.
** Register P3 must not be one of the function inputs.
@@ -64879,125 +78916,120 @@ case OP_CollSeq: {
** sqlite3_set_auxdata() API may be safely retained until the next
** invocation of this opcode.
**
-** See also: AggStep and AggFinal
+** See also: Function, AggStep, AggFinal
*/
+/* Opcode: Function P1 P2 P3 P4 P5
+** Synopsis: r[P3]=func(r[P2@P5])
+**
+** Invoke a user function (P4 is a pointer to an sqlite3_context object that
+** contains a pointer to the function to be run) with P5 arguments taken
+** from register P2 and successors. The result of the function is stored
+** in register P3. Register P3 must not be one of the function inputs.
+**
+** P1 is a 32-bit bitmask indicating whether or not each argument to the
+** function was determined to be constant at compile time. If the first
+** argument was constant then bit 0 of P1 is set. This is used to determine
+** whether meta data associated with a user function argument using the
+** sqlite3_set_auxdata() API may be safely retained until the next
+** invocation of this opcode.
+**
+** SQL functions are initially coded as OP_Function0 with P4 pointing
+** to a FuncDef object. But on first evaluation, the P4 operand is
+** automatically converted into an sqlite3_context object and the operation
+** changed to this OP_Function opcode. In this way, the initialization of
+** the sqlite3_context object occurs only once, rather than once for each
+** evaluation of the function.
+**
+** See also: Function0, AggStep, AggFinal
+*/
+case OP_Function0: {
+ int n;
+ sqlite3_context *pCtx;
+
+ assert( pOp->p4type==P4_FUNCDEF );
+ n = pOp->p5;
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
+ assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
+ pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
+ if( pCtx==0 ) goto no_mem;
+ pCtx->pOut = 0;
+ pCtx->pFunc = pOp->p4.pFunc;
+ pCtx->iOp = (int)(pOp - aOp);
+ pCtx->pVdbe = p;
+ pCtx->argc = n;
+ pOp->p4type = P4_FUNCCTX;
+ pOp->p4.pCtx = pCtx;
+ pOp->opcode = OP_Function;
+ /* Fall through into OP_Function */
+}
case OP_Function: {
-#if 0 /* local variables moved into u.ag */
int i;
- Mem *pArg;
- sqlite3_context ctx;
- sqlite3_value **apVal;
- int n;
-#endif /* local variables moved into u.ag */
+ sqlite3_context *pCtx;
- u.ag.n = pOp->p5;
- u.ag.apVal = p->apArg;
- assert( u.ag.apVal || u.ag.n==0 );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- pOut = &aMem[pOp->p3];
- memAboutToChange(p, pOut);
+ assert( pOp->p4type==P4_FUNCCTX );
+ pCtx = pOp->p4.pCtx;
- assert( u.ag.n==0 || (pOp->p2>0 && pOp->p2+u.ag.n<=p->nMem+1) );
- assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+u.ag.n );
- u.ag.pArg = &aMem[pOp->p2];
- for(u.ag.i=0; u.ag.i<u.ag.n; u.ag.i++, u.ag.pArg++){
- assert( memIsValid(u.ag.pArg) );
- u.ag.apVal[u.ag.i] = u.ag.pArg;
- Deephemeralize(u.ag.pArg);
- sqlite3VdbeMemStoreType(u.ag.pArg);
- REGISTER_TRACE(pOp->p2+u.ag.i, u.ag.pArg);
- }
-
- assert( pOp->p4type==P4_FUNCDEF || pOp->p4type==P4_VDBEFUNC );
- if( pOp->p4type==P4_FUNCDEF ){
- u.ag.ctx.pFunc = pOp->p4.pFunc;
- u.ag.ctx.pVdbeFunc = 0;
- }else{
- u.ag.ctx.pVdbeFunc = (VdbeFunc*)pOp->p4.pVdbeFunc;
- u.ag.ctx.pFunc = u.ag.ctx.pVdbeFunc->pFunc;
+ /* If this function is inside of a trigger, the register array in aMem[]
+ ** might change from one evaluation to the next. The next block of code
+ ** checks to see if the register array has changed, and if so it
+ ** reinitializes the relavant parts of the sqlite3_context object */
+ pOut = &aMem[pOp->p3];
+ if( pCtx->pOut != pOut ){
+ pCtx->pOut = pOut;
+ for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
}
- u.ag.ctx.s.flags = MEM_Null;
- u.ag.ctx.s.db = db;
- u.ag.ctx.s.xDel = 0;
- u.ag.ctx.s.zMalloc = 0;
-
- /* The output cell may already have a buffer allocated. Move
- ** the pointer to u.ag.ctx.s so in case the user-function can use
- ** the already allocated buffer instead of allocating a new one.
- */
- sqlite3VdbeMemMove(&u.ag.ctx.s, pOut);
- MemSetTypeFlag(&u.ag.ctx.s, MEM_Null);
-
- u.ag.ctx.isError = 0;
- if( u.ag.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
- assert( pOp>aOp );
- assert( pOp[-1].p4type==P4_COLLSEQ );
- assert( pOp[-1].opcode==OP_CollSeq );
- u.ag.ctx.pColl = pOp[-1].p4.pColl;
+ memAboutToChange(p, pCtx->pOut);
+#ifdef SQLITE_DEBUG
+ for(i=0; i<pCtx->argc; i++){
+ assert( memIsValid(pCtx->argv[i]) );
+ REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
}
+#endif
+ MemSetTypeFlag(pCtx->pOut, MEM_Null);
+ pCtx->fErrorOrAux = 0;
db->lastRowid = lastRowid;
- (*u.ag.ctx.pFunc->xFunc)(&u.ag.ctx, u.ag.n, u.ag.apVal); /* IMP: R-24505-23230 */
- lastRowid = db->lastRowid;
-
- /* If any auxiliary data functions have been called by this user function,
- ** immediately call the destructor for any non-static values.
- */
- if( u.ag.ctx.pVdbeFunc ){
- sqlite3VdbeDeleteAuxData(u.ag.ctx.pVdbeFunc, pOp->p1);
- pOp->p4.pVdbeFunc = u.ag.ctx.pVdbeFunc;
- pOp->p4type = P4_VDBEFUNC;
- }
-
- if( db->mallocFailed ){
- /* Even though a malloc() has failed, the implementation of the
- ** user function may have called an sqlite3_result_XXX() function
- ** to return a value. The following call releases any resources
- ** associated with such a value.
- */
- sqlite3VdbeMemRelease(&u.ag.ctx.s);
- goto no_mem;
- }
+ (*pCtx->pFunc->xSFunc)(pCtx, pCtx->argc, pCtx->argv);/* IMP: R-24505-23230 */
+ lastRowid = db->lastRowid; /* Remember rowid changes made by xSFunc */
/* If the function returned an error, throw an exception */
- if( u.ag.ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ag.ctx.s));
- rc = u.ag.ctx.isError;
+ if( pCtx->fErrorOrAux ){
+ if( pCtx->isError ){
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(pCtx->pOut));
+ rc = pCtx->isError;
+ }
+ sqlite3VdbeDeleteAuxData(db, &p->pAuxData, pCtx->iOp, pOp->p1);
+ if( rc ) goto abort_due_to_error;
}
/* Copy the result of the function into register P3 */
- sqlite3VdbeChangeEncoding(&u.ag.ctx.s, encoding);
- sqlite3VdbeMemMove(pOut, &u.ag.ctx.s);
- if( sqlite3VdbeMemTooBig(pOut) ){
- goto too_big;
+ if( pOut->flags & (MEM_Str|MEM_Blob) ){
+ sqlite3VdbeChangeEncoding(pCtx->pOut, encoding);
+ if( sqlite3VdbeMemTooBig(pCtx->pOut) ) goto too_big;
}
-#if 0
- /* The app-defined function has done something that as caused this
- ** statement to expire. (Perhaps the function called sqlite3_exec()
- ** with a CREATE TABLE statement.)
- */
- if( p->expired ) rc = SQLITE_ABORT;
-#endif
-
- REGISTER_TRACE(pOp->p3, pOut);
- UPDATE_MAX_BLOBSIZE(pOut);
+ REGISTER_TRACE(pOp->p3, pCtx->pOut);
+ UPDATE_MAX_BLOBSIZE(pCtx->pOut);
break;
}
/* Opcode: BitAnd P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]&r[P2]
**
** Take the bit-wise AND of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: BitOr P1 P2 P3 * *
+** Synopsis: r[P3]=r[P1]|r[P2]
**
** Take the bit-wise OR of the values in register P1 and P2 and
** store the result in register P3.
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftLeft P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]<<r[P1]
**
** Shift the integer value in register P2 to the left by the
** number of bits specified by the integer in register P1.
@@ -65005,6 +79037,7 @@ case OP_Function: {
** If either input is NULL, the result is NULL.
*/
/* Opcode: ShiftRight P1 P2 P3 * *
+** Synopsis: r[P3]=r[P2]>>r[P1]
**
** Shift the integer value in register P2 to the right by the
** number of bits specified by the integer in register P1.
@@ -65015,12 +79048,10 @@ case OP_BitAnd: /* same as TK_BITAND, in1, in2, out3 */
case OP_BitOr: /* same as TK_BITOR, in1, in2, out3 */
case OP_ShiftLeft: /* same as TK_LSHIFT, in1, in2, out3 */
case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
-#if 0 /* local variables moved into u.ah */
i64 iA;
u64 uA;
i64 iB;
u8 op;
-#endif /* local variables moved into u.ah */
pIn1 = &aMem[pOp->p1];
pIn2 = &aMem[pOp->p2];
@@ -65029,43 +79060,44 @@ case OP_ShiftRight: { /* same as TK_RSHIFT, in1, in2, out3 */
sqlite3VdbeMemSetNull(pOut);
break;
}
- u.ah.iA = sqlite3VdbeIntValue(pIn2);
- u.ah.iB = sqlite3VdbeIntValue(pIn1);
- u.ah.op = pOp->opcode;
- if( u.ah.op==OP_BitAnd ){
- u.ah.iA &= u.ah.iB;
- }else if( u.ah.op==OP_BitOr ){
- u.ah.iA |= u.ah.iB;
- }else if( u.ah.iB!=0 ){
- assert( u.ah.op==OP_ShiftRight || u.ah.op==OP_ShiftLeft );
+ iA = sqlite3VdbeIntValue(pIn2);
+ iB = sqlite3VdbeIntValue(pIn1);
+ op = pOp->opcode;
+ if( op==OP_BitAnd ){
+ iA &= iB;
+ }else if( op==OP_BitOr ){
+ iA |= iB;
+ }else if( iB!=0 ){
+ assert( op==OP_ShiftRight || op==OP_ShiftLeft );
/* If shifting by a negative amount, shift in the other direction */
- if( u.ah.iB<0 ){
+ if( iB<0 ){
assert( OP_ShiftRight==OP_ShiftLeft+1 );
- u.ah.op = 2*OP_ShiftLeft + 1 - u.ah.op;
- u.ah.iB = u.ah.iB>(-64) ? -u.ah.iB : 64;
+ op = 2*OP_ShiftLeft + 1 - op;
+ iB = iB>(-64) ? -iB : 64;
}
- if( u.ah.iB>=64 ){
- u.ah.iA = (u.ah.iA>=0 || u.ah.op==OP_ShiftLeft) ? 0 : -1;
+ if( iB>=64 ){
+ iA = (iA>=0 || op==OP_ShiftLeft) ? 0 : -1;
}else{
- memcpy(&u.ah.uA, &u.ah.iA, sizeof(u.ah.uA));
- if( u.ah.op==OP_ShiftLeft ){
- u.ah.uA <<= u.ah.iB;
+ memcpy(&uA, &iA, sizeof(uA));
+ if( op==OP_ShiftLeft ){
+ uA <<= iB;
}else{
- u.ah.uA >>= u.ah.iB;
+ uA >>= iB;
/* Sign-extend on a right shift of a negative number */
- if( u.ah.iA<0 ) u.ah.uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-u.ah.iB);
+ if( iA<0 ) uA |= ((((u64)0xffffffff)<<32)|0xffffffff) << (64-iB);
}
- memcpy(&u.ah.iA, &u.ah.uA, sizeof(u.ah.iA));
+ memcpy(&iA, &uA, sizeof(iA));
}
}
- pOut->u.i = u.ah.iA;
+ pOut->u.i = iA;
MemSetTypeFlag(pOut, MEM_Int);
break;
}
/* Opcode: AddImm P1 P2 * * *
+** Synopsis: r[P1]=r[P1]+P2
**
** Add the constant P2 to the value in register P1.
** The result is always an integer.
@@ -65089,17 +79121,19 @@ case OP_AddImm: { /* in1 */
*/
case OP_MustBeInt: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
- applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
if( (pIn1->flags & MEM_Int)==0 ){
- if( pOp->p2==0 ){
- rc = SQLITE_MISMATCH;
- goto abort_due_to_error;
- }else{
- pc = pOp->p2 - 1;
+ applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding);
+ VdbeBranchTaken((pIn1->flags&MEM_Int)==0, 2);
+ if( (pIn1->flags & MEM_Int)==0 ){
+ if( pOp->p2==0 ){
+ rc = SQLITE_MISMATCH;
+ goto abort_due_to_error;
+ }else{
+ goto jump_to_p2;
+ }
}
- }else{
- MemSetTypeFlag(pIn1, MEM_Int);
}
+ MemSetTypeFlag(pIn1, MEM_Int);
break;
}
@@ -65123,113 +79157,93 @@ case OP_RealAffinity: { /* in1 */
#endif
#ifndef SQLITE_OMIT_CAST
-/* Opcode: ToText P1 * * * *
+/* Opcode: Cast P1 P2 * * *
+** Synopsis: affinity(r[P1])
**
-** Force the value in register P1 to be text.
-** If the value is numeric, convert it to a string using the
-** equivalent of printf(). Blob values are unchanged and
-** are afterwards simply interpreted as text.
+** Force the value in register P1 to be the type defined by P2.
+**
+** <ul>
+** <li value="97"> TEXT
+** <li value="98"> BLOB
+** <li value="99"> NUMERIC
+** <li value="100"> INTEGER
+** <li value="101"> REAL
+** </ul>
**
** A NULL value is not changed by this routine. It remains NULL.
*/
-case OP_ToText: { /* same as TK_TO_TEXT, in1 */
+case OP_Cast: { /* in1 */
+ assert( pOp->p2>=SQLITE_AFF_BLOB && pOp->p2<=SQLITE_AFF_REAL );
+ testcase( pOp->p2==SQLITE_AFF_TEXT );
+ testcase( pOp->p2==SQLITE_AFF_BLOB );
+ testcase( pOp->p2==SQLITE_AFF_NUMERIC );
+ testcase( pOp->p2==SQLITE_AFF_INTEGER );
+ testcase( pOp->p2==SQLITE_AFF_REAL );
pIn1 = &aMem[pOp->p1];
memAboutToChange(p, pIn1);
- if( pIn1->flags & MEM_Null ) break;
- assert( MEM_Str==(MEM_Blob>>3) );
- pIn1->flags |= (pIn1->flags&MEM_Blob)>>3;
- applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding);
rc = ExpandBlob(pIn1);
- assert( pIn1->flags & MEM_Str || db->mallocFailed );
- pIn1->flags &= ~(MEM_Int|MEM_Real|MEM_Blob|MEM_Zero);
+ sqlite3VdbeMemCast(pIn1, pOp->p2, encoding);
UPDATE_MAX_BLOBSIZE(pIn1);
+ if( rc ) goto abort_due_to_error;
break;
}
+#endif /* SQLITE_OMIT_CAST */
-/* Opcode: ToBlob P1 * * * *
-**
-** Force the value in register P1 to be a BLOB.
-** If the value is numeric, convert it to a string first.
-** Strings are simply reinterpreted as blobs with no change
-** to the underlying data.
+/* Opcode: Eq P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]==r[P1]
**
-** A NULL value is not changed by this routine. It remains NULL.
-*/
-case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */
- pIn1 = &aMem[pOp->p1];
- if( pIn1->flags & MEM_Null ) break;
- if( (pIn1->flags & MEM_Blob)==0 ){
- applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding);
- assert( pIn1->flags & MEM_Str || db->mallocFailed );
- MemSetTypeFlag(pIn1, MEM_Blob);
- }else{
- pIn1->flags &= ~(MEM_TypeMask&~MEM_Blob);
- }
- UPDATE_MAX_BLOBSIZE(pIn1);
- break;
-}
-
-/* Opcode: ToNumeric P1 * * * *
+** Compare the values in register P1 and P3. If reg(P3)==reg(P1) then
+** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5, then
+** store the result of comparison in register P2.
**
-** Force the value in register P1 to be numeric (either an
-** integer or a floating-point number.)
-** If the value is text or blob, try to convert it to an using the
-** equivalent of atoi() or atof() and store 0 if no such conversion
-** is possible.
+** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
+** SQLITE_AFF_TEXT, SQLITE_AFF_INTEGER, and so forth. An attempt is made
+** to coerce both inputs according to this affinity before the
+** comparison is made. If the SQLITE_AFF_MASK is 0x00, then numeric
+** affinity is used. Note that the affinity conversions are stored
+** back into the input registers P1 and P3. So this opcode can cause
+** persistent changes to registers P1 and P3.
**
-** A NULL value is not changed by this routine. It remains NULL.
-*/
-case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */
- pIn1 = &aMem[pOp->p1];
- sqlite3VdbeMemNumerify(pIn1);
- break;
-}
-#endif /* SQLITE_OMIT_CAST */
-
-/* Opcode: ToInt P1 * * * *
+** Once any conversions have taken place, and neither value is NULL,
+** the values are compared. If both values are blobs then memcmp() is
+** used to determine the results of the comparison. If both values
+** are text, then the appropriate collating function specified in
+** P4 is used to do the comparison. If P4 is not specified then
+** memcmp() is used to compare text string. If both values are
+** numeric, then a numeric comparison is used. If the two values
+** are of different types, then numbers are considered less than
+** strings and strings are considered less than blobs.
**
-** Force the value in register P1 to be an integer. If
-** The value is currently a real number, drop its fractional part.
-** If the value is text or blob, try to convert it to an integer using the
-** equivalent of atoi() and store 0 if no such conversion is possible.
+** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
+** true or false and is never NULL. If both operands are NULL then the result
+** of comparison is true. If either operand is NULL then the result is false.
+** If neither operand is NULL the result is the same as it would be if
+** the SQLITE_NULLEQ flag were omitted from P5.
**
-** A NULL value is not changed by this routine. It remains NULL.
+** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
+** content of r[P2] is only changed if the new value is NULL or 0 (false).
+** In other words, a prior r[P2] value will not be overwritten by 1 (true).
*/
-case OP_ToInt: { /* same as TK_TO_INT, in1 */
- pIn1 = &aMem[pOp->p1];
- if( (pIn1->flags & MEM_Null)==0 ){
- sqlite3VdbeMemIntegerify(pIn1);
- }
- break;
-}
-
-#if !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT)
-/* Opcode: ToReal P1 * * * *
+/* Opcode: Ne P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]!=r[P1]
**
-** Force the value in register P1 to be a floating point number.
-** If The value is currently an integer, convert it.
-** If the value is text or blob, try to convert it to an integer using the
-** equivalent of atoi() and store 0.0 if no such conversion is possible.
+** This works just like the Eq opcode except that the jump is taken if
+** the operands in registers P1 and P3 are not equal. See the Eq opcode for
+** additional information.
**
-** A NULL value is not changed by this routine. It remains NULL.
+** If both SQLITE_STOREP2 and SQLITE_KEEPNULL flags are set then the
+** content of r[P2] is only changed if the new value is NULL or 1 (true).
+** In other words, a prior r[P2] value will not be overwritten by 0 (false).
*/
-case OP_ToReal: { /* same as TK_TO_REAL, in1 */
- pIn1 = &aMem[pOp->p1];
- memAboutToChange(p, pIn1);
- if( (pIn1->flags & MEM_Null)==0 ){
- sqlite3VdbeMemRealify(pIn1);
- }
- break;
-}
-#endif /* !defined(SQLITE_OMIT_CAST) && !defined(SQLITE_OMIT_FLOATING_POINT) */
-
/* Opcode: Lt P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]<r[P1]
**
** Compare the values in register P1 and P3. If reg(P3)<reg(P1) then
-** jump to address P2.
+** jump to address P2. Or if the SQLITE_STOREP2 flag is set in P5 store
+** the result of comparison (0 or 1 or NULL) into register P2.
**
** If the SQLITE_JUMPIFNULL bit of P5 is set and either reg(P1) or
-** reg(P3) is NULL then take the jump. If the SQLITE_JUMPIFNULL
+** reg(P3) is NULL then the take the jump. If the SQLITE_JUMPIFNULL
** bit is clear then fall through if either operand is NULL.
**
** The SQLITE_AFF_MASK portion of P5 must be an affinity character -
@@ -65249,47 +79263,23 @@ case OP_ToReal: { /* same as TK_TO_REAL, in1 */
** numeric, then a numeric comparison is used. If the two values
** are of different types, then numbers are considered less than
** strings and strings are considered less than blobs.
-**
-** If the SQLITE_STOREP2 bit of P5 is set, then do not jump. Instead,
-** store a boolean result (either 0, or 1, or NULL) in register P2.
-*/
-/* Opcode: Ne P1 P2 P3 P4 P5
-**
-** This works just like the Lt opcode except that the jump is taken if
-** the operands in registers P1 and P3 are not equal. See the Lt opcode for
-** additional information.
-**
-** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
-** true or false and is never NULL. If both operands are NULL then the result
-** of comparison is false. If either operand is NULL then the result is true.
-** If neither operand is NULL the result is the same as it would be if
-** the SQLITE_NULLEQ flag were omitted from P5.
-*/
-/* Opcode: Eq P1 P2 P3 P4 P5
-**
-** This works just like the Lt opcode except that the jump is taken if
-** the operands in registers P1 and P3 are equal.
-** See the Lt opcode for additional information.
-**
-** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either
-** true or false and is never NULL. If both operands are NULL then the result
-** of comparison is true. If either operand is NULL then the result is false.
-** If neither operand is NULL the result is the same as it would be if
-** the SQLITE_NULLEQ flag were omitted from P5.
*/
/* Opcode: Le P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]<=r[P1]
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is less than or equal to the content of
** register P1. See the Lt opcode for additional information.
*/
/* Opcode: Gt P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]>r[P1]
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is greater than the content of
** register P1. See the Lt opcode for additional information.
*/
/* Opcode: Ge P1 P2 P3 P4 P5
+** Synopsis: IF r[P3]>=r[P1]
**
** This works just like the Lt opcode except that the jump is taken if
** the content of register P3 is greater than or equal to the content of
@@ -65301,18 +79291,16 @@ case OP_Lt: /* same as TK_LT, jump, in1, in3 */
case OP_Le: /* same as TK_LE, jump, in1, in3 */
case OP_Gt: /* same as TK_GT, jump, in1, in3 */
case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
-#if 0 /* local variables moved into u.ai */
- int res; /* Result of the comparison of pIn1 against pIn3 */
+ int res, res2; /* Result of the comparison of pIn1 against pIn3 */
char affinity; /* Affinity to use for comparison */
u16 flags1; /* Copy of initial value of pIn1->flags */
u16 flags3; /* Copy of initial value of pIn3->flags */
-#endif /* local variables moved into u.ai */
pIn1 = &aMem[pOp->p1];
pIn3 = &aMem[pOp->p3];
- u.ai.flags1 = pIn1->flags;
- u.ai.flags3 = pIn3->flags;
- if( (u.ai.flags1 | u.ai.flags3)&MEM_Null ){
+ flags1 = pIn1->flags;
+ flags3 = pIn3->flags;
+ if( (flags1 | flags3)&MEM_Null ){
/* One or both operands are NULL */
if( pOp->p5 & SQLITE_NULLEQ ){
/* If SQLITE_NULLEQ is set (which will only happen if the operator is
@@ -65320,7 +79308,16 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
** or not both operands are null.
*/
assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne );
- u.ai.res = (u.ai.flags1 & u.ai.flags3 & MEM_Null)==0;
+ assert( (flags1 & MEM_Cleared)==0 );
+ assert( (pOp->p5 & SQLITE_JUMPIFNULL)==0 );
+ if( (flags1&MEM_Null)!=0
+ && (flags3&MEM_Null)!=0
+ && (flags3&MEM_Cleared)==0
+ ){
+ res = 0; /* Operands are equal */
+ }else{
+ res = 1; /* Operands are not equal */
+ }
}else{
/* SQLITE_NULLEQ is clear and at least one operand is NULL,
** then the result is always NULL.
@@ -65328,74 +79325,158 @@ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */
*/
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
+ iCompare = 1; /* Operands are not equal */
+ memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Null);
REGISTER_TRACE(pOp->p2, pOut);
- }else if( pOp->p5 & SQLITE_JUMPIFNULL ){
- pc = pOp->p2-1;
+ }else{
+ VdbeBranchTaken(2,3);
+ if( pOp->p5 & SQLITE_JUMPIFNULL ){
+ goto jump_to_p2;
+ }
}
break;
}
}else{
/* Neither operand is NULL. Do a comparison. */
- u.ai.affinity = pOp->p5 & SQLITE_AFF_MASK;
- if( u.ai.affinity ){
- applyAffinity(pIn1, u.ai.affinity, encoding);
- applyAffinity(pIn3, u.ai.affinity, encoding);
- if( db->mallocFailed ) goto no_mem;
+ affinity = pOp->p5 & SQLITE_AFF_MASK;
+ if( affinity>=SQLITE_AFF_NUMERIC ){
+ if( (flags1 | flags3)&MEM_Str ){
+ if( (flags1 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ applyNumericAffinity(pIn1,0);
+ testcase( flags3!=pIn3->flags ); /* Possible if pIn1==pIn3 */
+ flags3 = pIn3->flags;
+ }
+ if( (flags3 & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ applyNumericAffinity(pIn3,0);
+ }
+ }
+ /* Handle the common case of integer comparison here, as an
+ ** optimization, to avoid a call to sqlite3MemCompare() */
+ if( (pIn1->flags & pIn3->flags & MEM_Int)!=0 ){
+ if( pIn3->u.i > pIn1->u.i ){ res = +1; goto compare_op; }
+ if( pIn3->u.i < pIn1->u.i ){ res = -1; goto compare_op; }
+ res = 0;
+ goto compare_op;
+ }
+ }else if( affinity==SQLITE_AFF_TEXT ){
+ if( (flags1 & MEM_Str)==0 && (flags1 & (MEM_Int|MEM_Real))!=0 ){
+ testcase( pIn1->flags & MEM_Int );
+ testcase( pIn1->flags & MEM_Real );
+ sqlite3VdbeMemStringify(pIn1, encoding, 1);
+ testcase( (flags1&MEM_Dyn) != (pIn1->flags&MEM_Dyn) );
+ flags1 = (pIn1->flags & ~MEM_TypeMask) | (flags1 & MEM_TypeMask);
+ assert( pIn1!=pIn3 );
+ }
+ if( (flags3 & MEM_Str)==0 && (flags3 & (MEM_Int|MEM_Real))!=0 ){
+ testcase( pIn3->flags & MEM_Int );
+ testcase( pIn3->flags & MEM_Real );
+ sqlite3VdbeMemStringify(pIn3, encoding, 1);
+ testcase( (flags3&MEM_Dyn) != (pIn3->flags&MEM_Dyn) );
+ flags3 = (pIn3->flags & ~MEM_TypeMask) | (flags3 & MEM_TypeMask);
+ }
}
-
assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 );
- ExpandBlob(pIn1);
- ExpandBlob(pIn3);
- u.ai.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
+ res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl);
}
+compare_op:
switch( pOp->opcode ){
- case OP_Eq: u.ai.res = u.ai.res==0; break;
- case OP_Ne: u.ai.res = u.ai.res!=0; break;
- case OP_Lt: u.ai.res = u.ai.res<0; break;
- case OP_Le: u.ai.res = u.ai.res<=0; break;
- case OP_Gt: u.ai.res = u.ai.res>0; break;
- default: u.ai.res = u.ai.res>=0; break;
+ case OP_Eq: res2 = res==0; break;
+ case OP_Ne: res2 = res; break;
+ case OP_Lt: res2 = res<0; break;
+ case OP_Le: res2 = res<=0; break;
+ case OP_Gt: res2 = res>0; break;
+ default: res2 = res>=0; break;
}
+ /* Undo any changes made by applyAffinity() to the input registers. */
+ assert( (pIn1->flags & MEM_Dyn) == (flags1 & MEM_Dyn) );
+ pIn1->flags = flags1;
+ assert( (pIn3->flags & MEM_Dyn) == (flags3 & MEM_Dyn) );
+ pIn3->flags = flags3;
+
if( pOp->p5 & SQLITE_STOREP2 ){
pOut = &aMem[pOp->p2];
+ iCompare = res;
+ res2 = res2!=0; /* For this path res2 must be exactly 0 or 1 */
+ if( (pOp->p5 & SQLITE_KEEPNULL)!=0 ){
+ /* The KEEPNULL flag prevents OP_Eq from overwriting a NULL with 1
+ ** and prevents OP_Ne from overwriting NULL with 0. This flag
+ ** is only used in contexts where either:
+ ** (1) op==OP_Eq && (r[P2]==NULL || r[P2]==0)
+ ** (2) op==OP_Ne && (r[P2]==NULL || r[P2]==1)
+ ** Therefore it is not necessary to check the content of r[P2] for
+ ** NULL. */
+ assert( pOp->opcode==OP_Ne || pOp->opcode==OP_Eq );
+ assert( res2==0 || res2==1 );
+ testcase( res2==0 && pOp->opcode==OP_Eq );
+ testcase( res2==1 && pOp->opcode==OP_Eq );
+ testcase( res2==0 && pOp->opcode==OP_Ne );
+ testcase( res2==1 && pOp->opcode==OP_Ne );
+ if( (pOp->opcode==OP_Eq)==res2 ) break;
+ }
memAboutToChange(p, pOut);
MemSetTypeFlag(pOut, MEM_Int);
- pOut->u.i = u.ai.res;
+ pOut->u.i = res2;
REGISTER_TRACE(pOp->p2, pOut);
- }else if( u.ai.res ){
- pc = pOp->p2-1;
+ }else{
+ VdbeBranchTaken(res!=0, (pOp->p5 & SQLITE_NULLEQ)?2:3);
+ if( res2 ){
+ goto jump_to_p2;
+ }
}
+ break;
+}
- /* Undo any changes made by applyAffinity() to the input registers. */
- pIn1->flags = (pIn1->flags&~MEM_TypeMask) | (u.ai.flags1&MEM_TypeMask);
- pIn3->flags = (pIn3->flags&~MEM_TypeMask) | (u.ai.flags3&MEM_TypeMask);
+/* Opcode: ElseNotEq * P2 * * *
+**
+** This opcode must immediately follow an OP_Lt or OP_Gt comparison operator.
+** If result of an OP_Eq comparison on the same two operands
+** would have be NULL or false (0), then then jump to P2.
+** If the result of an OP_Eq comparison on the two previous operands
+** would have been true (1), then fall through.
+*/
+case OP_ElseNotEq: { /* same as TK_ESCAPE, jump */
+ assert( pOp>aOp );
+ assert( pOp[-1].opcode==OP_Lt || pOp[-1].opcode==OP_Gt );
+ assert( pOp[-1].p5 & SQLITE_STOREP2 );
+ VdbeBranchTaken(iCompare!=0, 2);
+ if( iCompare!=0 ) goto jump_to_p2;
break;
}
+
/* Opcode: Permutation * * * P4 *
**
** Set the permutation used by the OP_Compare operator to be the array
** of integers in P4.
**
-** The permutation is only valid until the next OP_Permutation, OP_Compare,
-** OP_Halt, or OP_ResultRow. Typically the OP_Permutation should occur
-** immediately prior to the OP_Compare.
+** The permutation is only valid until the next OP_Compare that has
+** the OPFLAG_PERMUTE bit set in P5. Typically the OP_Permutation should
+** occur immediately prior to the OP_Compare.
+**
+** The first integer in the P4 integer array is the length of the array
+** and does not become part of the permutation.
*/
case OP_Permutation: {
assert( pOp->p4type==P4_INTARRAY );
assert( pOp->p4.ai );
- aPermute = pOp->p4.ai;
+ aPermute = pOp->p4.ai + 1;
break;
}
-/* Opcode: Compare P1 P2 P3 P4 *
+/* Opcode: Compare P1 P2 P3 P4 P5
+** Synopsis: r[P1@P3] <-> r[P2@P3]
**
** Compare two vectors of registers in reg(P1)..reg(P1+P3-1) (call this
** vector "A") and in reg(P2)..reg(P2+P3-1) ("B"). Save the result of
** the comparison for use by the next OP_Jump instruct.
**
+** If P5 has the OPFLAG_PERMUTE bit set, then the order of comparison is
+** determined by the most recent OP_Permutation operator. If the
+** OPFLAG_PERMUTE bit is clear, then register are compared in sequential
+** order.
+**
** P4 is a KeyInfo structure that defines collating sequences and sort
** orders for the comparison. The permutation applies to registers
** only. The KeyInfo elements are used sequentially.
@@ -65405,7 +79486,6 @@ case OP_Permutation: {
** and strings are less than blobs.
*/
case OP_Compare: {
-#if 0 /* local variables moved into u.aj */
int n;
int i;
int p1;
@@ -65414,37 +79494,37 @@ case OP_Compare: {
int idx;
CollSeq *pColl; /* Collating sequence to use on this term */
int bRev; /* True for DESCENDING sort order */
-#endif /* local variables moved into u.aj */
-
- u.aj.n = pOp->p3;
- u.aj.pKeyInfo = pOp->p4.pKeyInfo;
- assert( u.aj.n>0 );
- assert( u.aj.pKeyInfo!=0 );
- u.aj.p1 = pOp->p1;
- u.aj.p2 = pOp->p2;
+
+ if( (pOp->p5 & OPFLAG_PERMUTE)==0 ) aPermute = 0;
+ n = pOp->p3;
+ pKeyInfo = pOp->p4.pKeyInfo;
+ assert( n>0 );
+ assert( pKeyInfo!=0 );
+ p1 = pOp->p1;
+ p2 = pOp->p2;
#if SQLITE_DEBUG
if( aPermute ){
int k, mx = 0;
- for(k=0; k<u.aj.n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
- assert( u.aj.p1>0 && u.aj.p1+mx<=p->nMem+1 );
- assert( u.aj.p2>0 && u.aj.p2+mx<=p->nMem+1 );
+ for(k=0; k<n; k++) if( aPermute[k]>mx ) mx = aPermute[k];
+ assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 );
+ assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 );
}else{
- assert( u.aj.p1>0 && u.aj.p1+u.aj.n<=p->nMem+1 );
- assert( u.aj.p2>0 && u.aj.p2+u.aj.n<=p->nMem+1 );
+ assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 );
+ assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 );
}
#endif /* SQLITE_DEBUG */
- for(u.aj.i=0; u.aj.i<u.aj.n; u.aj.i++){
- u.aj.idx = aPermute ? aPermute[u.aj.i] : u.aj.i;
- assert( memIsValid(&aMem[u.aj.p1+u.aj.idx]) );
- assert( memIsValid(&aMem[u.aj.p2+u.aj.idx]) );
- REGISTER_TRACE(u.aj.p1+u.aj.idx, &aMem[u.aj.p1+u.aj.idx]);
- REGISTER_TRACE(u.aj.p2+u.aj.idx, &aMem[u.aj.p2+u.aj.idx]);
- assert( u.aj.i<u.aj.pKeyInfo->nField );
- u.aj.pColl = u.aj.pKeyInfo->aColl[u.aj.i];
- u.aj.bRev = u.aj.pKeyInfo->aSortOrder[u.aj.i];
- iCompare = sqlite3MemCompare(&aMem[u.aj.p1+u.aj.idx], &aMem[u.aj.p2+u.aj.idx], u.aj.pColl);
+ for(i=0; i<n; i++){
+ idx = aPermute ? aPermute[i] : i;
+ assert( memIsValid(&aMem[p1+idx]) );
+ assert( memIsValid(&aMem[p2+idx]) );
+ REGISTER_TRACE(p1+idx, &aMem[p1+idx]);
+ REGISTER_TRACE(p2+idx, &aMem[p2+idx]);
+ assert( i<pKeyInfo->nField );
+ pColl = pKeyInfo->aColl[i];
+ bRev = pKeyInfo->aSortOrder[i];
+ iCompare = sqlite3MemCompare(&aMem[p1+idx], &aMem[p2+idx], pColl);
if( iCompare ){
- if( u.aj.bRev ) iCompare = -iCompare;
+ if( bRev ) iCompare = -iCompare;
break;
}
}
@@ -65460,16 +79540,17 @@ case OP_Compare: {
*/
case OP_Jump: { /* jump */
if( iCompare<0 ){
- pc = pOp->p1 - 1;
+ VdbeBranchTaken(0,3); pOp = &aOp[pOp->p1 - 1];
}else if( iCompare==0 ){
- pc = pOp->p2 - 1;
+ VdbeBranchTaken(1,3); pOp = &aOp[pOp->p2 - 1];
}else{
- pc = pOp->p3 - 1;
+ VdbeBranchTaken(2,3); pOp = &aOp[pOp->p3 - 1];
}
break;
}
/* Opcode: And P1 P2 P3 * *
+** Synopsis: r[P3]=(r[P1] && r[P2])
**
** Take the logical AND of the values in registers P1 and P2 and
** write the result into register P3.
@@ -65479,6 +79560,7 @@ case OP_Jump: { /* jump */
** a NULL output.
*/
/* Opcode: Or P1 P2 P3 * *
+** Synopsis: r[P3]=(r[P1] || r[P2])
**
** Take the logical OR of the values in register P1 and P2 and
** store the answer in register P3.
@@ -65489,41 +79571,40 @@ case OP_Jump: { /* jump */
*/
case OP_And: /* same as TK_AND, in1, in2, out3 */
case OP_Or: { /* same as TK_OR, in1, in2, out3 */
-#if 0 /* local variables moved into u.ak */
int v1; /* Left operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */
-#endif /* local variables moved into u.ak */
pIn1 = &aMem[pOp->p1];
if( pIn1->flags & MEM_Null ){
- u.ak.v1 = 2;
+ v1 = 2;
}else{
- u.ak.v1 = sqlite3VdbeIntValue(pIn1)!=0;
+ v1 = sqlite3VdbeIntValue(pIn1)!=0;
}
pIn2 = &aMem[pOp->p2];
if( pIn2->flags & MEM_Null ){
- u.ak.v2 = 2;
+ v2 = 2;
}else{
- u.ak.v2 = sqlite3VdbeIntValue(pIn2)!=0;
+ v2 = sqlite3VdbeIntValue(pIn2)!=0;
}
if( pOp->opcode==OP_And ){
static const unsigned char and_logic[] = { 0, 0, 0, 0, 1, 2, 0, 2, 2 };
- u.ak.v1 = and_logic[u.ak.v1*3+u.ak.v2];
+ v1 = and_logic[v1*3+v2];
}else{
static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 };
- u.ak.v1 = or_logic[u.ak.v1*3+u.ak.v2];
+ v1 = or_logic[v1*3+v2];
}
pOut = &aMem[pOp->p3];
- if( u.ak.v1==2 ){
+ if( v1==2 ){
MemSetTypeFlag(pOut, MEM_Null);
}else{
- pOut->u.i = u.ak.v1;
+ pOut->u.i = v1;
MemSetTypeFlag(pOut, MEM_Int);
}
break;
}
/* Opcode: Not P1 P2 * * *
+** Synopsis: r[P2]= !r[P1]
**
** Interpret the value in register P1 as a boolean value. Store the
** boolean complement in register P2. If the value in register P1 is
@@ -65532,15 +79613,16 @@ case OP_Or: { /* same as TK_OR, in1, in2, out3 */
case OP_Not: { /* same as TK_NOT, in1, out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
- if( pIn1->flags & MEM_Null ){
- sqlite3VdbeMemSetNull(pOut);
- }else{
- sqlite3VdbeMemSetInt64(pOut, !sqlite3VdbeIntValue(pIn1));
+ sqlite3VdbeMemSetNull(pOut);
+ if( (pIn1->flags & MEM_Null)==0 ){
+ pOut->flags = MEM_Int;
+ pOut->u.i = !sqlite3VdbeIntValue(pIn1);
}
break;
}
/* Opcode: BitNot P1 P2 * * *
+** Synopsis: r[P1]= ~r[P1]
**
** Interpret the content of register P1 as an integer. Store the
** ones-complement of the P1 value into register P2. If P1 holds
@@ -65549,90 +79631,95 @@ case OP_Not: { /* same as TK_NOT, in1, out2 */
case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
pIn1 = &aMem[pOp->p1];
pOut = &aMem[pOp->p2];
- if( pIn1->flags & MEM_Null ){
- sqlite3VdbeMemSetNull(pOut);
- }else{
- sqlite3VdbeMemSetInt64(pOut, ~sqlite3VdbeIntValue(pIn1));
+ sqlite3VdbeMemSetNull(pOut);
+ if( (pIn1->flags & MEM_Null)==0 ){
+ pOut->flags = MEM_Int;
+ pOut->u.i = ~sqlite3VdbeIntValue(pIn1);
}
break;
}
/* Opcode: Once P1 P2 * * *
**
-** Jump to P2 if the value in register P1 is a not null or zero. If
-** the value is NULL or zero, fall through and change the P1 register
-** to an integer 1.
-**
-** When P1 is not used otherwise in a program, this opcode falls through
-** once and jumps on all subsequent invocations. It is the equivalent
-** of "OP_If P1 P2", followed by "OP_Integer 1 P1".
+** If the P1 value is equal to the P1 value on the OP_Init opcode at
+** instruction 0, then jump to P2. If the two P1 values differ, then
+** set the P1 value on this opcode to equal the P1 value on the OP_Init
+** and fall through.
*/
+case OP_Once: { /* jump */
+ assert( p->aOp[0].opcode==OP_Init );
+ VdbeBranchTaken(p->aOp[0].p1==pOp->p1, 2);
+ if( p->aOp[0].p1==pOp->p1 ){
+ goto jump_to_p2;
+ }else{
+ pOp->p1 = p->aOp[0].p1;
+ }
+ break;
+}
+
/* Opcode: If P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is true. The value
** is considered true if it is numeric and non-zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** in P1 is NULL then take the jump if and only if P3 is non-zero.
*/
/* Opcode: IfNot P1 P2 P3 * *
**
** Jump to P2 if the value in register P1 is False. The value
-** is considered true if it has a numeric value of zero. If the value
-** in P1 is NULL then take the jump if P3 is true.
+** is considered false if it has a numeric value of zero. If the value
+** in P1 is NULL then take the jump if and only if P3 is non-zero.
*/
-case OP_Once: /* jump, in1 */
case OP_If: /* jump, in1 */
case OP_IfNot: { /* jump, in1 */
-#if 0 /* local variables moved into u.al */
int c;
-#endif /* local variables moved into u.al */
pIn1 = &aMem[pOp->p1];
if( pIn1->flags & MEM_Null ){
- u.al.c = pOp->p3;
+ c = pOp->p3;
}else{
#ifdef SQLITE_OMIT_FLOATING_POINT
- u.al.c = sqlite3VdbeIntValue(pIn1)!=0;
+ c = sqlite3VdbeIntValue(pIn1)!=0;
#else
- u.al.c = sqlite3VdbeRealValue(pIn1)!=0.0;
+ c = sqlite3VdbeRealValue(pIn1)!=0.0;
#endif
- if( pOp->opcode==OP_IfNot ) u.al.c = !u.al.c;
+ if( pOp->opcode==OP_IfNot ) c = !c;
}
- if( u.al.c ){
- pc = pOp->p2-1;
- }else if( pOp->opcode==OP_Once ){
- assert( (pIn1->flags & (MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame))==0 );
- memAboutToChange(p, pIn1);
- pIn1->flags = MEM_Int;
- pIn1->u.i = 1;
- REGISTER_TRACE(pOp->p1, pIn1);
+ VdbeBranchTaken(c!=0, 2);
+ if( c ){
+ goto jump_to_p2;
}
break;
}
/* Opcode: IsNull P1 P2 * * *
+** Synopsis: if r[P1]==NULL goto P2
**
** Jump to P2 if the value in register P1 is NULL.
*/
case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */
pIn1 = &aMem[pOp->p1];
+ VdbeBranchTaken( (pIn1->flags & MEM_Null)!=0, 2);
if( (pIn1->flags & MEM_Null)!=0 ){
- pc = pOp->p2 - 1;
+ goto jump_to_p2;
}
break;
}
/* Opcode: NotNull P1 P2 * * *
+** Synopsis: if r[P1]!=NULL goto P2
**
** Jump to P2 if the value in register P1 is not NULL.
*/
case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
pIn1 = &aMem[pOp->p1];
+ VdbeBranchTaken( (pIn1->flags & MEM_Null)==0, 2);
if( (pIn1->flags & MEM_Null)==0 ){
- pc = pOp->p2 - 1;
+ goto jump_to_p2;
}
break;
}
/* Opcode: Column P1 P2 P3 P4 P5
+** Synopsis: r[P3]=PX
**
** Interpret the data that cursor P1 points to as a structure built using
** the MakeRecord instruction. (See the MakeRecord opcode for additional
@@ -65650,302 +79737,255 @@ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */
** then the cache of the cursor is reset prior to extracting the column.
** The first OP_Column against a pseudo-table after the value of the content
** register has changed should have this bit set.
+**
+** If the OPFLAG_LENGTHARG and OPFLAG_TYPEOFARG bits are set on P5 when
+** the result is guaranteed to only be used as the argument of a length()
+** or typeof() function, respectively. The loading of large blobs can be
+** skipped for length() and all content loading can be skipped for typeof().
*/
case OP_Column: {
-#if 0 /* local variables moved into u.am */
- u32 payloadSize; /* Number of bytes in the record */
- i64 payloadSize64; /* Number of bytes in the record */
- int p1; /* P1 value of the opcode */
int p2; /* column number to retrieve */
VdbeCursor *pC; /* The VDBE cursor */
- char *zRec; /* Pointer to complete record-data */
BtCursor *pCrsr; /* The BTree cursor */
- u32 *aType; /* aType[i] holds the numeric type of the i-th column */
u32 *aOffset; /* aOffset[i] is offset to start of data for i-th column */
- int nField; /* number of fields in the record */
int len; /* The length of the serialized data for the column */
int i; /* Loop counter */
- char *zData; /* Part of the record being decoded */
Mem *pDest; /* Where to write the extracted value */
Mem sMem; /* For storing the record being decoded */
- u8 *zIdx; /* Index into header */
- u8 *zEndHdr; /* Pointer to first byte after the header */
+ const u8 *zData; /* Part of the record being decoded */
+ const u8 *zHdr; /* Next unparsed byte of the header */
+ const u8 *zEndHdr; /* Pointer to first byte after the header */
u32 offset; /* Offset into the data */
- u32 szField; /* Number of bytes in the content of a field */
- int szHdr; /* Size of the header size field at start of record */
- int avail; /* Number of bytes of available data */
+ u64 offset64; /* 64-bit offset */
+ u32 avail; /* Number of bytes of available data */
u32 t; /* A type code from the record header */
Mem *pReg; /* PseudoTable input register */
-#endif /* local variables moved into u.am */
-
-
- u.am.p1 = pOp->p1;
- u.am.p2 = pOp->p2;
- u.am.pC = 0;
- memset(&u.am.sMem, 0, sizeof(u.am.sMem));
- assert( u.am.p1<p->nCursor );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.am.pDest = &aMem[pOp->p3];
- memAboutToChange(p, u.am.pDest);
- u.am.zRec = 0;
-
- /* This block sets the variable u.am.payloadSize to be the total number of
- ** bytes in the record.
- **
- ** u.am.zRec is set to be the complete text of the record if it is available.
- ** The complete record text is always available for pseudo-tables
- ** If the record is stored in a cursor, the complete record text
- ** might be available in the u.am.pC->aRow cache. Or it might not be.
- ** If the data is unavailable, u.am.zRec is set to NULL.
- **
- ** We also compute the number of columns in the record. For cursors,
- ** the number of columns is stored in the VdbeCursor.nField element.
- */
- u.am.pC = p->apCsr[u.am.p1];
- assert( u.am.pC!=0 );
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- assert( u.am.pC->pVtabCursor==0 );
-#endif
- u.am.pCrsr = u.am.pC->pCursor;
- if( u.am.pCrsr!=0 ){
- /* The record is stored in a B-Tree */
- rc = sqlite3VdbeCursorMoveto(u.am.pC);
- if( rc ) goto abort_due_to_error;
- if( u.am.pC->nullRow ){
- u.am.payloadSize = 0;
- }else if( u.am.pC->cacheStatus==p->cacheCtr ){
- u.am.payloadSize = u.am.pC->payloadSize;
- u.am.zRec = (char*)u.am.pC->aRow;
- }else if( u.am.pC->isIndex ){
- assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) );
- rc = sqlite3BtreeKeySize(u.am.pCrsr, &u.am.payloadSize64);
- assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
- /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the
- ** payload size, so it is impossible for u.am.payloadSize64 to be
- ** larger than 32 bits. */
- assert( (u.am.payloadSize64 & SQLITE_MAX_U32)==(u64)u.am.payloadSize64 );
- u.am.payloadSize = (u32)u.am.payloadSize64;
- }else{
- assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) );
- rc = sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize);
- assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
- }
- }else if( ALWAYS(u.am.pC->pseudoTableReg>0) ){
- u.am.pReg = &aMem[u.am.pC->pseudoTableReg];
- assert( u.am.pReg->flags & MEM_Blob );
- assert( memIsValid(u.am.pReg) );
- u.am.payloadSize = u.am.pReg->n;
- u.am.zRec = u.am.pReg->z;
- u.am.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr;
- assert( u.am.payloadSize==0 || u.am.zRec!=0 );
- }else{
- /* Consider the row to be NULL */
- u.am.payloadSize = 0;
- }
-
- /* If u.am.payloadSize is 0, then just store a NULL. This can happen because of
- ** nullRow or because of a corrupt database. */
- if( u.am.payloadSize==0 ){
- MemSetTypeFlag(u.am.pDest, MEM_Null);
- goto op_column_out;
- }
- assert( db->aLimit[SQLITE_LIMIT_LENGTH]>=0 );
- if( u.am.payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
- u.am.nField = u.am.pC->nField;
- assert( u.am.p2<u.am.nField );
+ pC = p->apCsr[pOp->p1];
+ p2 = pOp->p2;
- /* Read and parse the table header. Store the results of the parse
- ** into the record header cache fields of the cursor.
- */
- u.am.aType = u.am.pC->aType;
- if( u.am.pC->cacheStatus==p->cacheCtr ){
- u.am.aOffset = u.am.pC->aOffset;
- }else{
- assert(u.am.aType);
- u.am.avail = 0;
- u.am.pC->aOffset = u.am.aOffset = &u.am.aType[u.am.nField];
- u.am.pC->payloadSize = u.am.payloadSize;
- u.am.pC->cacheStatus = p->cacheCtr;
+ /* If the cursor cache is stale, bring it up-to-date */
+ rc = sqlite3VdbeCursorMoveto(&pC, &p2);
+ if( rc ) goto abort_due_to_error;
- /* Figure out how many bytes are in the header */
- if( u.am.zRec ){
- u.am.zData = u.am.zRec;
- }else{
- if( u.am.pC->isIndex ){
- u.am.zData = (char*)sqlite3BtreeKeyFetch(u.am.pCrsr, &u.am.avail);
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ pDest = &aMem[pOp->p3];
+ memAboutToChange(p, pDest);
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pC!=0 );
+ assert( p2<pC->nField );
+ aOffset = pC->aOffset;
+ assert( pC->eCurType!=CURTYPE_VTAB );
+ assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow );
+ assert( pC->eCurType!=CURTYPE_SORTER );
+ pCrsr = pC->uc.pCursor;
+
+ if( pC->cacheStatus!=p->cacheCtr ){ /*OPTIMIZATION-IF-FALSE*/
+ if( pC->nullRow ){
+ if( pC->eCurType==CURTYPE_PSEUDO ){
+ assert( pC->uc.pseudoTableReg>0 );
+ pReg = &aMem[pC->uc.pseudoTableReg];
+ assert( pReg->flags & MEM_Blob );
+ assert( memIsValid(pReg) );
+ pC->payloadSize = pC->szRow = avail = pReg->n;
+ pC->aRow = (u8*)pReg->z;
}else{
- u.am.zData = (char*)sqlite3BtreeDataFetch(u.am.pCrsr, &u.am.avail);
+ sqlite3VdbeMemSetNull(pDest);
+ goto op_column_out;
}
- /* If KeyFetch()/DataFetch() managed to get the entire payload,
- ** save the payload in the u.am.pC->aRow cache. That will save us from
- ** having to make additional calls to fetch the content portion of
- ** the record.
- */
- assert( u.am.avail>=0 );
- if( u.am.payloadSize <= (u32)u.am.avail ){
- u.am.zRec = u.am.zData;
- u.am.pC->aRow = (u8*)u.am.zData;
+ }else{
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pCrsr );
+ assert( sqlite3BtreeCursorIsValid(pCrsr) );
+ pC->payloadSize = sqlite3BtreePayloadSize(pCrsr);
+ pC->aRow = sqlite3BtreePayloadFetch(pCrsr, &avail);
+ assert( avail<=65536 ); /* Maximum page size is 64KiB */
+ if( pC->payloadSize <= (u32)avail ){
+ pC->szRow = pC->payloadSize;
+ }else if( pC->payloadSize > (u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
}else{
- u.am.pC->aRow = 0;
+ pC->szRow = avail;
}
}
- /* The following assert is true in all cases accept when
- ** the database file has been corrupted externally.
- ** assert( u.am.zRec!=0 || u.am.avail>=u.am.payloadSize || u.am.avail>=9 ); */
- u.am.szHdr = getVarint32((u8*)u.am.zData, u.am.offset);
+ pC->cacheStatus = p->cacheCtr;
+ pC->iHdrOffset = getVarint32(pC->aRow, offset);
+ pC->nHdrParsed = 0;
+ aOffset[0] = offset;
- /* Make sure a corrupt database has not given us an oversize header.
- ** Do this now to avoid an oversize memory allocation.
- **
- ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte
- ** types use so much data space that there can only be 4096 and 32 of
- ** them, respectively. So the maximum header length results from a
- ** 3-byte type for each of the maximum of 32768 columns plus three
- ** extra bytes for the header length itself. 32768*3 + 3 = 98307.
- */
- if( u.am.offset > 98307 ){
- rc = SQLITE_CORRUPT_BKPT;
- goto op_column_out;
- }
- /* Compute in u.am.len the number of bytes of data we need to read in order
- ** to get u.am.nField type values. u.am.offset is an upper bound on this. But
- ** u.am.nField might be significantly less than the true number of columns
- ** in the table, and in that case, 5*u.am.nField+3 might be smaller than u.am.offset.
- ** We want to minimize u.am.len in order to limit the size of the memory
- ** allocation, especially if a corrupt database file has caused u.am.offset
- ** to be oversized. Offset is limited to 98307 above. But 98307 might
- ** still exceed Robson memory allocation limits on some configurations.
- ** On systems that cannot tolerate large memory allocations, u.am.nField*5+3
- ** will likely be much smaller since u.am.nField will likely be less than
- ** 20 or so. This insures that Robson memory allocation limits are
- ** not exceeded even for corrupt database files.
- */
- u.am.len = u.am.nField*5 + 3;
- if( u.am.len > (int)u.am.offset ) u.am.len = (int)u.am.offset;
-
- /* The KeyFetch() or DataFetch() above are fast and will get the entire
- ** record header in most cases. But they will fail to get the complete
- ** record header if the record header does not fit on a single page
- ** in the B-Tree. When that happens, use sqlite3VdbeMemFromBtree() to
- ** acquire the complete header text.
- */
- if( !u.am.zRec && u.am.avail<u.am.len ){
- u.am.sMem.flags = 0;
- u.am.sMem.db = 0;
- rc = sqlite3VdbeMemFromBtree(u.am.pCrsr, 0, u.am.len, u.am.pC->isIndex, &u.am.sMem);
- if( rc!=SQLITE_OK ){
- goto op_column_out;
+ if( avail<offset ){ /*OPTIMIZATION-IF-FALSE*/
+ /* pC->aRow does not have to hold the entire row, but it does at least
+ ** need to cover the header of the record. If pC->aRow does not contain
+ ** the complete header, then set it to zero, forcing the header to be
+ ** dynamically allocated. */
+ pC->aRow = 0;
+ pC->szRow = 0;
+
+ /* Make sure a corrupt database has not given us an oversize header.
+ ** Do this now to avoid an oversize memory allocation.
+ **
+ ** Type entries can be between 1 and 5 bytes each. But 4 and 5 byte
+ ** types use so much data space that there can only be 4096 and 32 of
+ ** them, respectively. So the maximum header length results from a
+ ** 3-byte type for each of the maximum of 32768 columns plus three
+ ** extra bytes for the header length itself. 32768*3 + 3 = 98307.
+ */
+ if( offset > 98307 || offset > pC->payloadSize ){
+ rc = SQLITE_CORRUPT_BKPT;
+ goto abort_due_to_error;
}
- u.am.zData = u.am.sMem.z;
+ }else if( offset>0 ){ /*OPTIMIZATION-IF-TRUE*/
+ /* The following goto is an optimization. It can be omitted and
+ ** everything will still work. But OP_Column is measurably faster
+ ** by skipping the subsequent conditional, which is always true.
+ */
+ zData = pC->aRow;
+ assert( pC->nHdrParsed<=p2 ); /* Conditional skipped */
+ goto op_column_read_header;
}
- u.am.zEndHdr = (u8 *)&u.am.zData[u.am.len];
- u.am.zIdx = (u8 *)&u.am.zData[u.am.szHdr];
+ }
- /* Scan the header and use it to fill in the u.am.aType[] and u.am.aOffset[]
- ** arrays. u.am.aType[u.am.i] will contain the type integer for the u.am.i-th
- ** column and u.am.aOffset[u.am.i] will contain the u.am.offset from the beginning
- ** of the record to the start of the data for the u.am.i-th column
+ /* Make sure at least the first p2+1 entries of the header have been
+ ** parsed and valid information is in aOffset[] and pC->aType[].
+ */
+ if( pC->nHdrParsed<=p2 ){
+ /* If there is more header available for parsing in the record, try
+ ** to extract additional fields up through the p2+1-th field
*/
- for(u.am.i=0; u.am.i<u.am.nField; u.am.i++){
- if( u.am.zIdx<u.am.zEndHdr ){
- u.am.aOffset[u.am.i] = u.am.offset;
- if( u.am.zIdx[0]<0x80 ){
- u.am.t = u.am.zIdx[0];
- u.am.zIdx++;
+ if( pC->iHdrOffset<aOffset[0] ){
+ /* Make sure zData points to enough of the record to cover the header. */
+ if( pC->aRow==0 ){
+ memset(&sMem, 0, sizeof(sMem));
+ rc = sqlite3VdbeMemFromBtree(pCrsr, 0, aOffset[0], !pC->isTable, &sMem);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ zData = (u8*)sMem.z;
+ }else{
+ zData = pC->aRow;
+ }
+
+ /* Fill in pC->aType[i] and aOffset[i] values through the p2-th field. */
+ op_column_read_header:
+ i = pC->nHdrParsed;
+ offset64 = aOffset[i];
+ zHdr = zData + pC->iHdrOffset;
+ zEndHdr = zData + aOffset[0];
+ do{
+ if( (t = zHdr[0])<0x80 ){
+ zHdr++;
+ offset64 += sqlite3VdbeOneByteSerialTypeLen(t);
}else{
- u.am.zIdx += sqlite3GetVarint32(u.am.zIdx, &u.am.t);
+ zHdr += sqlite3GetVarint32(zHdr, &t);
+ offset64 += sqlite3VdbeSerialTypeLen(t);
}
- u.am.aType[u.am.i] = u.am.t;
- u.am.szField = sqlite3VdbeSerialTypeLen(u.am.t);
- u.am.offset += u.am.szField;
- if( u.am.offset<u.am.szField ){ /* True if u.am.offset overflows */
- u.am.zIdx = &u.am.zEndHdr[1]; /* Forces SQLITE_CORRUPT return below */
- break;
- }
- }else{
- /* If u.am.i is less that u.am.nField, then there are less fields in this
- ** record than SetNumColumns indicated there are columns in the
- ** table. Set the u.am.offset for any extra columns not present in
- ** the record to 0. This tells code below to store a NULL
- ** instead of deserializing a value from the record.
- */
- u.am.aOffset[u.am.i] = 0;
+ pC->aType[i++] = t;
+ aOffset[i] = (u32)(offset64 & 0xffffffff);
+ }while( i<=p2 && zHdr<zEndHdr );
+
+ /* The record is corrupt if any of the following are true:
+ ** (1) the bytes of the header extend past the declared header size
+ ** (2) the entire header was used but not all data was used
+ ** (3) the end of the data extends beyond the end of the record.
+ */
+ if( (zHdr>=zEndHdr && (zHdr>zEndHdr || offset64!=pC->payloadSize))
+ || (offset64 > pC->payloadSize)
+ ){
+ if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
+ rc = SQLITE_CORRUPT_BKPT;
+ goto abort_due_to_error;
}
+
+ pC->nHdrParsed = i;
+ pC->iHdrOffset = (u32)(zHdr - zData);
+ if( pC->aRow==0 ) sqlite3VdbeMemRelease(&sMem);
+ }else{
+ t = 0;
}
- sqlite3VdbeMemRelease(&u.am.sMem);
- u.am.sMem.flags = MEM_Null;
- /* If we have read more header data than was contained in the header,
- ** or if the end of the last field appears to be past the end of the
- ** record, or if the end of the last field appears to be before the end
- ** of the record (when all fields present), then we must be dealing
- ** with a corrupt database.
+ /* If after trying to extract new entries from the header, nHdrParsed is
+ ** still not up to p2, that means that the record has fewer than p2
+ ** columns. So the result will be either the default value or a NULL.
*/
- if( (u.am.zIdx > u.am.zEndHdr) || (u.am.offset > u.am.payloadSize)
- || (u.am.zIdx==u.am.zEndHdr && u.am.offset!=u.am.payloadSize) ){
- rc = SQLITE_CORRUPT_BKPT;
+ if( pC->nHdrParsed<=p2 ){
+ if( pOp->p4type==P4_MEM ){
+ sqlite3VdbeMemShallowCopy(pDest, pOp->p4.pMem, MEM_Static);
+ }else{
+ sqlite3VdbeMemSetNull(pDest);
+ }
goto op_column_out;
}
+ }else{
+ t = pC->aType[p2];
}
- /* Get the column information. If u.am.aOffset[u.am.p2] is non-zero, then
- ** deserialize the value from the record. If u.am.aOffset[u.am.p2] is zero,
- ** then there are not enough fields in the record to satisfy the
- ** request. In this case, set the value NULL or to P4 if P4 is
- ** a pointer to a Mem object.
+ /* Extract the content for the p2+1-th column. Control can only
+ ** reach this point if aOffset[p2], aOffset[p2+1], and pC->aType[p2] are
+ ** all valid.
*/
- if( u.am.aOffset[u.am.p2] ){
- assert( rc==SQLITE_OK );
- if( u.am.zRec ){
- MemReleaseExt(u.am.pDest);
- sqlite3VdbeSerialGet((u8 *)&u.am.zRec[u.am.aOffset[u.am.p2]], u.am.aType[u.am.p2], u.am.pDest);
+ assert( p2<pC->nHdrParsed );
+ assert( rc==SQLITE_OK );
+ assert( sqlite3VdbeCheckMemInvariants(pDest) );
+ if( VdbeMemDynamic(pDest) ){
+ sqlite3VdbeMemSetNull(pDest);
+ }
+ assert( t==pC->aType[p2] );
+ if( pC->szRow>=aOffset[p2+1] ){
+ /* This is the common case where the desired content fits on the original
+ ** page - where the content is not on an overflow page */
+ zData = pC->aRow + aOffset[p2];
+ if( t<12 ){
+ sqlite3VdbeSerialGet(zData, t, pDest);
}else{
- u.am.len = sqlite3VdbeSerialTypeLen(u.am.aType[u.am.p2]);
- sqlite3VdbeMemMove(&u.am.sMem, u.am.pDest);
- rc = sqlite3VdbeMemFromBtree(u.am.pCrsr, u.am.aOffset[u.am.p2], u.am.len, u.am.pC->isIndex, &u.am.sMem);
- if( rc!=SQLITE_OK ){
- goto op_column_out;
+ /* If the column value is a string, we need a persistent value, not
+ ** a MEM_Ephem value. This branch is a fast short-cut that is equivalent
+ ** to calling sqlite3VdbeSerialGet() and sqlite3VdbeDeephemeralize().
+ */
+ static const u16 aFlag[] = { MEM_Blob, MEM_Str|MEM_Term };
+ pDest->n = len = (t-12)/2;
+ pDest->enc = encoding;
+ if( pDest->szMalloc < len+2 ){
+ pDest->flags = MEM_Null;
+ if( sqlite3VdbeMemGrow(pDest, len+2, 0) ) goto no_mem;
+ }else{
+ pDest->z = pDest->zMalloc;
}
- u.am.zData = u.am.sMem.z;
- sqlite3VdbeSerialGet((u8*)u.am.zData, u.am.aType[u.am.p2], u.am.pDest);
+ memcpy(pDest->z, zData, len);
+ pDest->z[len] = 0;
+ pDest->z[len+1] = 0;
+ pDest->flags = aFlag[t&1];
}
- u.am.pDest->enc = encoding;
}else{
- if( pOp->p4type==P4_MEM ){
- sqlite3VdbeMemShallowCopy(u.am.pDest, pOp->p4.pMem, MEM_Static);
+ pDest->enc = encoding;
+ /* This branch happens only when content is on overflow pages */
+ if( ((pOp->p5 & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG))!=0
+ && ((t>=12 && (t&1)==0) || (pOp->p5 & OPFLAG_TYPEOFARG)!=0))
+ || (len = sqlite3VdbeSerialTypeLen(t))==0
+ ){
+ /* Content is irrelevant for
+ ** 1. the typeof() function,
+ ** 2. the length(X) function if X is a blob, and
+ ** 3. if the content length is zero.
+ ** So we might as well use bogus content rather than reading
+ ** content from disk. */
+ static u8 aZero[8]; /* This is the bogus content */
+ sqlite3VdbeSerialGet(aZero, t, pDest);
}else{
- MemSetTypeFlag(u.am.pDest, MEM_Null);
+ rc = sqlite3VdbeMemFromBtree(pCrsr, aOffset[p2], len, !pC->isTable,
+ pDest);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ sqlite3VdbeSerialGet((const u8*)pDest->z, t, pDest);
+ pDest->flags &= ~MEM_Ephem;
}
}
- /* If we dynamically allocated space to hold the data (in the
- ** sqlite3VdbeMemFromBtree() call above) then transfer control of that
- ** dynamically allocated space over to the u.am.pDest structure.
- ** This prevents a memory copy.
- */
- if( u.am.sMem.zMalloc ){
- assert( u.am.sMem.z==u.am.sMem.zMalloc );
- assert( !(u.am.pDest->flags & MEM_Dyn) );
- assert( !(u.am.pDest->flags & (MEM_Blob|MEM_Str)) || u.am.pDest->z==u.am.sMem.z );
- u.am.pDest->flags &= ~(MEM_Ephem|MEM_Static);
- u.am.pDest->flags |= MEM_Term;
- u.am.pDest->z = u.am.sMem.z;
- u.am.pDest->zMalloc = u.am.sMem.zMalloc;
- }
-
- rc = sqlite3VdbeMemMakeWriteable(u.am.pDest);
-
op_column_out:
- UPDATE_MAX_BLOBSIZE(u.am.pDest);
- REGISTER_TRACE(pOp->p3, u.am.pDest);
+ UPDATE_MAX_BLOBSIZE(pDest);
+ REGISTER_TRACE(pOp->p3, pDest);
break;
}
/* Opcode: Affinity P1 P2 * P4 *
+** Synopsis: affinity(r[P1@P2])
**
** Apply affinities to a range of P2 registers starting with P1.
**
@@ -65954,26 +79994,24 @@ op_column_out:
** memory cell in the range.
*/
case OP_Affinity: {
-#if 0 /* local variables moved into u.an */
const char *zAffinity; /* The affinity to be applied */
char cAff; /* A single character of affinity */
-#endif /* local variables moved into u.an */
- u.an.zAffinity = pOp->p4.z;
- assert( u.an.zAffinity!=0 );
- assert( u.an.zAffinity[pOp->p2]==0 );
+ zAffinity = pOp->p4.z;
+ assert( zAffinity!=0 );
+ assert( zAffinity[pOp->p2]==0 );
pIn1 = &aMem[pOp->p1];
- while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){
- assert( pIn1 <= &p->aMem[p->nMem] );
+ while( (cAff = *(zAffinity++))!=0 ){
+ assert( pIn1 <= &p->aMem[(p->nMem+1 - p->nCursor)] );
assert( memIsValid(pIn1) );
- ExpandBlob(pIn1);
- applyAffinity(pIn1, u.an.cAff, encoding);
+ applyAffinity(pIn1, cAff, encoding);
pIn1++;
}
break;
}
/* Opcode: MakeRecord P1 P2 P3 P4 *
+** Synopsis: r[P3]=mkrec(r[P1@P2])
**
** Convert P2 registers beginning with P1 into the [record format]
** use as a data record in a database table or as a key
@@ -65986,16 +80024,15 @@ case OP_Affinity: {
** The mapping from character to affinity is given by the SQLITE_AFF_
** macros defined in sqliteInt.h.
**
-** If P4 is NULL then all index fields have the affinity NONE.
+** If P4 is NULL then all index fields have the affinity BLOB.
*/
case OP_MakeRecord: {
-#if 0 /* local variables moved into u.ao */
u8 *zNewRecord; /* A buffer to hold the data for the new record */
Mem *pRec; /* The new record */
u64 nData; /* Number of bytes of data space */
int nHdr; /* Number of bytes of header space */
i64 nByte; /* Data space required for this record */
- int nZero; /* Number of zero bytes at the end of the record */
+ i64 nZero; /* Number of zero bytes at the end of the record */
int nVarint; /* Number of bytes in a varint */
u32 serial_type; /* Type field */
Mem *pData0; /* First field to be combined into the record */
@@ -66003,102 +80040,127 @@ case OP_MakeRecord: {
int nField; /* Number of fields in the record */
char *zAffinity; /* The affinity string for the record */
int file_format; /* File format to use for encoding */
- int i; /* Space used in zNewRecord[] */
- int len; /* Length of a field */
-#endif /* local variables moved into u.ao */
+ int i; /* Space used in zNewRecord[] header */
+ int j; /* Space used in zNewRecord[] content */
+ u32 len; /* Length of a field */
/* Assuming the record contains N fields, the record format looks
** like this:
**
** ------------------------------------------------------------------------
- ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
+ ** | hdr-size | type 0 | type 1 | ... | type N-1 | data0 | ... | data N-1 |
** ------------------------------------------------------------------------
**
** Data(0) is taken from register P1. Data(1) comes from register P1+1
- ** and so froth.
+ ** and so forth.
**
- ** Each type field is a varint representing the serial type of the
+ ** Each type field is a varint representing the serial type of the
** corresponding data element (see sqlite3VdbeSerialType()). The
** hdr-size field is also a varint which is the offset from the beginning
** of the record to data0.
*/
- u.ao.nData = 0; /* Number of bytes of data space */
- u.ao.nHdr = 0; /* Number of bytes of header space */
- u.ao.nZero = 0; /* Number of zero bytes at the end of the record */
- u.ao.nField = pOp->p1;
- u.ao.zAffinity = pOp->p4.z;
- assert( u.ao.nField>0 && pOp->p2>0 && pOp->p2+u.ao.nField<=p->nMem+1 );
- u.ao.pData0 = &aMem[u.ao.nField];
- u.ao.nField = pOp->p2;
- u.ao.pLast = &u.ao.pData0[u.ao.nField-1];
- u.ao.file_format = p->minWriteFileFormat;
+ nData = 0; /* Number of bytes of data space */
+ nHdr = 0; /* Number of bytes of header space */
+ nZero = 0; /* Number of zero bytes at the end of the record */
+ nField = pOp->p1;
+ zAffinity = pOp->p4.z;
+ assert( nField>0 && pOp->p2>0 && pOp->p2+nField<=(p->nMem+1 - p->nCursor)+1 );
+ pData0 = &aMem[nField];
+ nField = pOp->p2;
+ pLast = &pData0[nField-1];
+ file_format = p->minWriteFileFormat;
/* Identify the output register */
assert( pOp->p3<pOp->p1 || pOp->p3>=pOp->p1+pOp->p2 );
pOut = &aMem[pOp->p3];
memAboutToChange(p, pOut);
- /* Loop through the elements that will make up the record to figure
- ** out how much space is required for the new record.
+ /* Apply the requested affinity to all inputs
*/
- for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){
- assert( memIsValid(u.ao.pRec) );
- if( u.ao.zAffinity ){
- applyAffinity(u.ao.pRec, u.ao.zAffinity[u.ao.pRec-u.ao.pData0], encoding);
- }
- if( u.ao.pRec->flags&MEM_Zero && u.ao.pRec->n>0 ){
- sqlite3VdbeMemExpandBlob(u.ao.pRec);
- }
- u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format);
- u.ao.len = sqlite3VdbeSerialTypeLen(u.ao.serial_type);
- u.ao.nData += u.ao.len;
- u.ao.nHdr += sqlite3VarintLen(u.ao.serial_type);
- if( u.ao.pRec->flags & MEM_Zero ){
- /* Only pure zero-filled BLOBs can be input to this Opcode.
- ** We do not allow blobs with a prefix and a zero-filled tail. */
- u.ao.nZero += u.ao.pRec->u.nZero;
- }else if( u.ao.len ){
- u.ao.nZero = 0;
- }
+ assert( pData0<=pLast );
+ if( zAffinity ){
+ pRec = pData0;
+ do{
+ applyAffinity(pRec++, *(zAffinity++), encoding);
+ assert( zAffinity[0]==0 || pRec<=pLast );
+ }while( zAffinity[0] );
}
- /* Add the initial header varint and total the size */
- u.ao.nHdr += u.ao.nVarint = sqlite3VarintLen(u.ao.nHdr);
- if( u.ao.nVarint<sqlite3VarintLen(u.ao.nHdr) ){
- u.ao.nHdr++;
+ /* Loop through the elements that will make up the record to figure
+ ** out how much space is required for the new record.
+ */
+ pRec = pLast;
+ do{
+ assert( memIsValid(pRec) );
+ pRec->uTemp = serial_type = sqlite3VdbeSerialType(pRec, file_format, &len);
+ if( pRec->flags & MEM_Zero ){
+ if( nData ){
+ if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
+ }else{
+ nZero += pRec->u.nZero;
+ len -= pRec->u.nZero;
+ }
+ }
+ nData += len;
+ testcase( serial_type==127 );
+ testcase( serial_type==128 );
+ nHdr += serial_type<=127 ? 1 : sqlite3VarintLen(serial_type);
+ if( pRec==pData0 ) break;
+ pRec--;
+ }while(1);
+
+ /* EVIDENCE-OF: R-22564-11647 The header begins with a single varint
+ ** which determines the total number of bytes in the header. The varint
+ ** value is the size of the header in bytes including the size varint
+ ** itself. */
+ testcase( nHdr==126 );
+ testcase( nHdr==127 );
+ if( nHdr<=126 ){
+ /* The common case */
+ nHdr += 1;
+ }else{
+ /* Rare case of a really large header */
+ nVarint = sqlite3VarintLen(nHdr);
+ nHdr += nVarint;
+ if( nVarint<sqlite3VarintLen(nHdr) ) nHdr++;
}
- u.ao.nByte = u.ao.nHdr+u.ao.nData-u.ao.nZero;
- if( u.ao.nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ nByte = nHdr+nData;
+ if( nByte+nZero>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
- /* Make sure the output register has a buffer large enough to store
+ /* Make sure the output register has a buffer large enough to store
** the new record. The output register (pOp->p3) is not allowed to
** be one of the input registers (because the following call to
- ** sqlite3VdbeMemGrow() could clobber the value before it is used).
+ ** sqlite3VdbeMemClearAndResize() could clobber the value before it is used).
*/
- if( sqlite3VdbeMemGrow(pOut, (int)u.ao.nByte, 0) ){
+ if( sqlite3VdbeMemClearAndResize(pOut, (int)nByte) ){
goto no_mem;
}
- u.ao.zNewRecord = (u8 *)pOut->z;
+ zNewRecord = (u8 *)pOut->z;
/* Write the record */
- u.ao.i = putVarint32(u.ao.zNewRecord, u.ao.nHdr);
- for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){
- u.ao.serial_type = sqlite3VdbeSerialType(u.ao.pRec, u.ao.file_format);
- u.ao.i += putVarint32(&u.ao.zNewRecord[u.ao.i], u.ao.serial_type); /* serial type */
- }
- for(u.ao.pRec=u.ao.pData0; u.ao.pRec<=u.ao.pLast; u.ao.pRec++){ /* serial data */
- u.ao.i += sqlite3VdbeSerialPut(&u.ao.zNewRecord[u.ao.i], (int)(u.ao.nByte-u.ao.i), u.ao.pRec,u.ao.file_format);
- }
- assert( u.ao.i==u.ao.nByte );
-
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- pOut->n = (int)u.ao.nByte;
- pOut->flags = MEM_Blob | MEM_Dyn;
- pOut->xDel = 0;
- if( u.ao.nZero ){
- pOut->u.nZero = u.ao.nZero;
+ i = putVarint32(zNewRecord, nHdr);
+ j = nHdr;
+ assert( pData0<=pLast );
+ pRec = pData0;
+ do{
+ serial_type = pRec->uTemp;
+ /* EVIDENCE-OF: R-06529-47362 Following the size varint are one or more
+ ** additional varints, one per column. */
+ i += putVarint32(&zNewRecord[i], serial_type); /* serial type */
+ /* EVIDENCE-OF: R-64536-51728 The values for each column in the record
+ ** immediately follow the header. */
+ j += sqlite3VdbeSerialPut(&zNewRecord[j], pRec, serial_type); /* content */
+ }while( (++pRec)<=pLast );
+ assert( i==nHdr );
+ assert( j==nByte );
+
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ pOut->n = (int)nByte;
+ pOut->flags = MEM_Blob;
+ if( nZero ){
+ pOut->u.nZero = nZero;
pOut->flags |= MEM_Zero;
}
pOut->enc = SQLITE_UTF8; /* In case the blob is ever converted to text */
@@ -66108,24 +80170,24 @@ case OP_MakeRecord: {
}
/* Opcode: Count P1 P2 * * *
+** Synopsis: r[P2]=count()
**
** Store the number of entries (an integer value) in the table or index
** opened by cursor P1 in register P2
*/
#ifndef SQLITE_OMIT_BTREECOUNT
-case OP_Count: { /* out2-prerelease */
-#if 0 /* local variables moved into u.ap */
+case OP_Count: { /* out2 */
i64 nEntry;
BtCursor *pCrsr;
-#endif /* local variables moved into u.ap */
- u.ap.pCrsr = p->apCsr[pOp->p1]->pCursor;
- if( ALWAYS(u.ap.pCrsr) ){
- rc = sqlite3BtreeCount(u.ap.pCrsr, &u.ap.nEntry);
- }else{
- u.ap.nEntry = 0;
- }
- pOut->u.i = u.ap.nEntry;
+ assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE );
+ pCrsr = p->apCsr[pOp->p1]->uc.pCursor;
+ assert( pCrsr );
+ nEntry = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3BtreeCount(pCrsr, &nEntry);
+ if( rc ) goto abort_due_to_error;
+ pOut = out2Prerelease(p, pOp);
+ pOut->u.i = nEntry;
break;
}
#endif
@@ -66137,7 +80199,6 @@ case OP_Count: { /* out2-prerelease */
** existing savepoint, P1==1, or to rollback an existing savepoint P1==2.
*/
case OP_Savepoint: {
-#if 0 /* local variables moved into u.aq */
int p1; /* Value of P1 operand */
char *zName; /* Name of savepoint */
int nName;
@@ -66146,29 +80207,28 @@ case OP_Savepoint: {
Savepoint *pTmp;
int iSavepoint;
int ii;
-#endif /* local variables moved into u.aq */
- u.aq.p1 = pOp->p1;
- u.aq.zName = pOp->p4.z;
+ p1 = pOp->p1;
+ zName = pOp->p4.z;
- /* Assert that the u.aq.p1 parameter is valid. Also that if there is no open
- ** transaction, then there cannot be any savepoints.
+ /* Assert that the p1 parameter is valid. Also that if there is no open
+ ** transaction, then there cannot be any savepoints.
*/
assert( db->pSavepoint==0 || db->autoCommit==0 );
- assert( u.aq.p1==SAVEPOINT_BEGIN||u.aq.p1==SAVEPOINT_RELEASE||u.aq.p1==SAVEPOINT_ROLLBACK );
+ assert( p1==SAVEPOINT_BEGIN||p1==SAVEPOINT_RELEASE||p1==SAVEPOINT_ROLLBACK );
assert( db->pSavepoint || db->isTransactionSavepoint==0 );
assert( checkSavepointCount(db) );
+ assert( p->bIsReader );
- if( u.aq.p1==SAVEPOINT_BEGIN ){
- if( db->writeVdbeCnt>0 ){
- /* A new savepoint cannot be created if there are active write
+ if( p1==SAVEPOINT_BEGIN ){
+ if( db->nVdbeWrite>0 ){
+ /* A new savepoint cannot be created if there are active write
** statements (i.e. open read/write incremental blob handles).
*/
- sqlite3SetString(&p->zErrMsg, db, "cannot open savepoint - "
- "SQL statements in progress");
+ sqlite3VdbeError(p, "cannot open savepoint - SQL statements in progress");
rc = SQLITE_BUSY;
}else{
- u.aq.nName = sqlite3Strlen30(u.aq.zName);
+ nName = sqlite3Strlen30(zName);
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* This call is Ok even if this savepoint is actually a transaction
@@ -66182,11 +80242,11 @@ case OP_Savepoint: {
#endif
/* Create a new savepoint structure. */
- u.aq.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.aq.nName+1);
- if( u.aq.pNew ){
- u.aq.pNew->zName = (char *)&u.aq.pNew[1];
- memcpy(u.aq.pNew->zName, u.aq.zName, u.aq.nName+1);
-
+ pNew = sqlite3DbMallocRawNN(db, sizeof(Savepoint)+nName+1);
+ if( pNew ){
+ pNew->zName = (char *)&pNew[1];
+ memcpy(pNew->zName, zName, nName+1);
+
/* If there is no open transaction, then mark this as a special
** "transaction savepoint". */
if( db->autoCommit ){
@@ -66197,52 +80257,48 @@ case OP_Savepoint: {
}
/* Link the new savepoint into the database handle's list. */
- u.aq.pNew->pNext = db->pSavepoint;
- db->pSavepoint = u.aq.pNew;
- u.aq.pNew->nDeferredCons = db->nDeferredCons;
+ pNew->pNext = db->pSavepoint;
+ db->pSavepoint = pNew;
+ pNew->nDeferredCons = db->nDeferredCons;
+ pNew->nDeferredImmCons = db->nDeferredImmCons;
}
}
}else{
- u.aq.iSavepoint = 0;
+ iSavepoint = 0;
/* Find the named savepoint. If there is no such savepoint, then an
** an error is returned to the user. */
for(
- u.aq.pSavepoint = db->pSavepoint;
- u.aq.pSavepoint && sqlite3StrICmp(u.aq.pSavepoint->zName, u.aq.zName);
- u.aq.pSavepoint = u.aq.pSavepoint->pNext
+ pSavepoint = db->pSavepoint;
+ pSavepoint && sqlite3StrICmp(pSavepoint->zName, zName);
+ pSavepoint = pSavepoint->pNext
){
- u.aq.iSavepoint++;
+ iSavepoint++;
}
- if( !u.aq.pSavepoint ){
- sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.aq.zName);
+ if( !pSavepoint ){
+ sqlite3VdbeError(p, "no such savepoint: %s", zName);
rc = SQLITE_ERROR;
- }else if(
- db->writeVdbeCnt>0 || (u.aq.p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1)
- ){
- /* It is not possible to release (commit) a savepoint if there are
- ** active write statements. It is not possible to rollback a savepoint
- ** if there are any active statements at all.
+ }else if( db->nVdbeWrite>0 && p1==SAVEPOINT_RELEASE ){
+ /* It is not possible to release (commit) a savepoint if there are
+ ** active write statements.
*/
- sqlite3SetString(&p->zErrMsg, db,
- "cannot %s savepoint - SQL statements in progress",
- (u.aq.p1==SAVEPOINT_ROLLBACK ? "rollback": "release")
- );
+ sqlite3VdbeError(p, "cannot release savepoint - "
+ "SQL statements in progress");
rc = SQLITE_BUSY;
}else{
/* Determine whether or not this is a transaction savepoint. If so,
- ** and this is a RELEASE command, then the current transaction
- ** is committed.
+ ** and this is a RELEASE command, then the current transaction
+ ** is committed.
*/
- int isTransaction = u.aq.pSavepoint->pNext==0 && db->isTransactionSavepoint;
- if( isTransaction && u.aq.p1==SAVEPOINT_RELEASE ){
+ int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
+ if( isTransaction && p1==SAVEPOINT_RELEASE ){
if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
}
db->autoCommit = 1;
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
- p->pc = pc;
+ p->pc = (int)(pOp - aOp);
db->autoCommit = 0;
p->rc = rc = SQLITE_BUSY;
goto vdbe_return;
@@ -66250,50 +80306,64 @@ case OP_Savepoint: {
db->isTransactionSavepoint = 0;
rc = p->rc;
}else{
- u.aq.iSavepoint = db->nSavepoint - u.aq.iSavepoint - 1;
- for(u.aq.ii=0; u.aq.ii<db->nDb; u.aq.ii++){
- rc = sqlite3BtreeSavepoint(db->aDb[u.aq.ii].pBt, u.aq.p1, u.aq.iSavepoint);
+ int isSchemaChange;
+ iSavepoint = db->nSavepoint - iSavepoint - 1;
+ if( p1==SAVEPOINT_ROLLBACK ){
+ isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
+ for(ii=0; ii<db->nDb; ii++){
+ rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt,
+ SQLITE_ABORT_ROLLBACK,
+ isSchemaChange==0);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ }
+ }else{
+ isSchemaChange = 0;
+ }
+ for(ii=0; ii<db->nDb; ii++){
+ rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
if( rc!=SQLITE_OK ){
goto abort_due_to_error;
}
}
- if( u.aq.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
+ if( isSchemaChange ){
sqlite3ExpirePreparedStatements(db);
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
db->flags = (db->flags | SQLITE_InternChanges);
}
}
-
- /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all
+
+ /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all
** savepoints nested inside of the savepoint being operated on. */
- while( db->pSavepoint!=u.aq.pSavepoint ){
- u.aq.pTmp = db->pSavepoint;
- db->pSavepoint = u.aq.pTmp->pNext;
- sqlite3DbFree(db, u.aq.pTmp);
+ while( db->pSavepoint!=pSavepoint ){
+ pTmp = db->pSavepoint;
+ db->pSavepoint = pTmp->pNext;
+ sqlite3DbFree(db, pTmp);
db->nSavepoint--;
}
- /* If it is a RELEASE, then destroy the savepoint being operated on
- ** too. If it is a ROLLBACK TO, then set the number of deferred
+ /* If it is a RELEASE, then destroy the savepoint being operated on
+ ** too. If it is a ROLLBACK TO, then set the number of deferred
** constraint violations present in the database to the value stored
** when the savepoint was created. */
- if( u.aq.p1==SAVEPOINT_RELEASE ){
- assert( u.aq.pSavepoint==db->pSavepoint );
- db->pSavepoint = u.aq.pSavepoint->pNext;
- sqlite3DbFree(db, u.aq.pSavepoint);
+ if( p1==SAVEPOINT_RELEASE ){
+ assert( pSavepoint==db->pSavepoint );
+ db->pSavepoint = pSavepoint->pNext;
+ sqlite3DbFree(db, pSavepoint);
if( !isTransaction ){
db->nSavepoint--;
}
}else{
- db->nDeferredCons = u.aq.pSavepoint->nDeferredCons;
+ db->nDeferredCons = pSavepoint->nDeferredCons;
+ db->nDeferredImmCons = pSavepoint->nDeferredImmCons;
}
- if( !isTransaction ){
- rc = sqlite3VtabSavepoint(db, u.aq.p1, u.aq.iSavepoint);
+ if( !isTransaction || p1==SAVEPOINT_ROLLBACK ){
+ rc = sqlite3VtabSavepoint(db, p1, iSavepoint);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}
}
}
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -66308,49 +80378,39 @@ case OP_Savepoint: {
** This instruction causes the VM to halt.
*/
case OP_AutoCommit: {
-#if 0 /* local variables moved into u.ar */
int desiredAutoCommit;
int iRollback;
- int turnOnAC;
-#endif /* local variables moved into u.ar */
-
- u.ar.desiredAutoCommit = pOp->p1;
- u.ar.iRollback = pOp->p2;
- u.ar.turnOnAC = u.ar.desiredAutoCommit && !db->autoCommit;
- assert( u.ar.desiredAutoCommit==1 || u.ar.desiredAutoCommit==0 );
- assert( u.ar.desiredAutoCommit==1 || u.ar.iRollback==0 );
- assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */
-
- if( u.ar.turnOnAC && u.ar.iRollback && db->activeVdbeCnt>1 ){
- /* If this instruction implements a ROLLBACK and other VMs are
- ** still running, and a transaction is active, return an error indicating
- ** that the other VMs must complete first.
- */
- sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - "
- "SQL statements in progress");
- rc = SQLITE_BUSY;
- }else if( u.ar.turnOnAC && !u.ar.iRollback && db->writeVdbeCnt>0 ){
- /* If this instruction implements a COMMIT and other VMs are writing
- ** return an error indicating that the other VMs must complete first.
- */
- sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - "
- "SQL statements in progress");
- rc = SQLITE_BUSY;
- }else if( u.ar.desiredAutoCommit!=db->autoCommit ){
- if( u.ar.iRollback ){
- assert( u.ar.desiredAutoCommit==1 );
- sqlite3RollbackAll(db);
+
+ desiredAutoCommit = pOp->p1;
+ iRollback = pOp->p2;
+ assert( desiredAutoCommit==1 || desiredAutoCommit==0 );
+ assert( desiredAutoCommit==1 || iRollback==0 );
+ assert( db->nVdbeActive>0 ); /* At least this one VM is active */
+ assert( p->bIsReader );
+
+ if( desiredAutoCommit!=db->autoCommit ){
+ if( iRollback ){
+ assert( desiredAutoCommit==1 );
+ sqlite3RollbackAll(db, SQLITE_ABORT_ROLLBACK);
db->autoCommit = 1;
+ }else if( desiredAutoCommit && db->nVdbeWrite>0 ){
+ /* If this instruction implements a COMMIT and other VMs are writing
+ ** return an error indicating that the other VMs must complete first.
+ */
+ sqlite3VdbeError(p, "cannot commit transaction - "
+ "SQL statements in progress");
+ rc = SQLITE_BUSY;
+ goto abort_due_to_error;
}else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){
goto vdbe_return;
}else{
- db->autoCommit = (u8)u.ar.desiredAutoCommit;
- if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
- p->pc = pc;
- db->autoCommit = (u8)(1-u.ar.desiredAutoCommit);
- p->rc = rc = SQLITE_BUSY;
- goto vdbe_return;
- }
+ db->autoCommit = (u8)desiredAutoCommit;
+ }
+ if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
+ p->pc = (int)(pOp - aOp);
+ db->autoCommit = (u8)(1-desiredAutoCommit);
+ p->rc = rc = SQLITE_BUSY;
+ goto vdbe_return;
}
assert( db->nStatement==0 );
sqlite3CloseSavepoints(db);
@@ -66361,88 +80421,135 @@ case OP_AutoCommit: {
}
goto vdbe_return;
}else{
- sqlite3SetString(&p->zErrMsg, db,
- (!u.ar.desiredAutoCommit)?"cannot start a transaction within a transaction":(
- (u.ar.iRollback)?"cannot rollback - no transaction is active":
+ sqlite3VdbeError(p,
+ (!desiredAutoCommit)?"cannot start a transaction within a transaction":(
+ (iRollback)?"cannot rollback - no transaction is active":
"cannot commit - no transaction is active"));
-
+
rc = SQLITE_ERROR;
+ goto abort_due_to_error;
}
break;
}
-/* Opcode: Transaction P1 P2 * * *
+/* Opcode: Transaction P1 P2 P3 P4 P5
**
-** Begin a transaction. The transaction ends when a Commit or Rollback
-** opcode is encountered. Depending on the ON CONFLICT setting, the
-** transaction might also be rolled back if an error is encountered.
+** Begin a transaction on database P1 if a transaction is not already
+** active.
+** If P2 is non-zero, then a write-transaction is started, or if a
+** read-transaction is already active, it is upgraded to a write-transaction.
+** If P2 is zero, then a read-transaction is started.
**
** P1 is the index of the database file on which the transaction is
** started. Index 0 is the main database file and index 1 is the
** file used for temporary tables. Indices of 2 or more are used for
** attached databases.
**
-** If P2 is non-zero, then a write-transaction is started. A RESERVED lock is
-** obtained on the database file when a write-transaction is started. No
-** other process can start another write transaction while this transaction is
-** underway. Starting a write transaction also creates a rollback journal. A
-** write transaction must be started before any changes can be made to the
-** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained
-** on the file.
-**
** If a write-transaction is started and the Vdbe.usesStmtJournal flag is
** true (this flag is set if the Vdbe may modify more than one row and may
** throw an ABORT exception), a statement transaction may also be opened.
** More specifically, a statement transaction is opened iff the database
** connection is currently not in autocommit mode, or if there are other
-** active statements. A statement transaction allows the affects of this
+** active statements. A statement transaction allows the changes made by this
** VDBE to be rolled back after an error without having to roll back the
** entire transaction. If no error is encountered, the statement transaction
** will automatically commit when the VDBE halts.
**
-** If P2 is zero, then a read-lock is obtained on the database file.
+** If P5!=0 then this opcode also checks the schema cookie against P3
+** and the schema generation counter against P4.
+** The cookie changes its value whenever the database schema changes.
+** This operation is used to detect when that the cookie has changed
+** and that the current process needs to reread the schema. If the schema
+** cookie in P3 differs from the schema cookie in the database header or
+** if the schema generation counter in P4 differs from the current
+** generation counter, then an SQLITE_SCHEMA error is raised and execution
+** halts. The sqlite3_step() wrapper function might then reprepare the
+** statement and rerun it from the beginning.
*/
case OP_Transaction: {
-#if 0 /* local variables moved into u.as */
Btree *pBt;
-#endif /* local variables moved into u.as */
+ int iMeta;
+ int iGen;
+ assert( p->bIsReader );
+ assert( p->readOnly==0 || pOp->p2==0 );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.as.pBt = db->aDb[pOp->p1].pBt;
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ if( pOp->p2 && (db->flags & SQLITE_QueryOnly)!=0 ){
+ rc = SQLITE_READONLY;
+ goto abort_due_to_error;
+ }
+ pBt = db->aDb[pOp->p1].pBt;
- if( u.as.pBt ){
- rc = sqlite3BtreeBeginTrans(u.as.pBt, pOp->p2);
- if( rc==SQLITE_BUSY ){
- p->pc = pc;
- p->rc = rc = SQLITE_BUSY;
- goto vdbe_return;
- }
+ if( pBt ){
+ rc = sqlite3BtreeBeginTrans(pBt, pOp->p2);
+ testcase( rc==SQLITE_BUSY_SNAPSHOT );
+ testcase( rc==SQLITE_BUSY_RECOVERY );
if( rc!=SQLITE_OK ){
+ if( (rc&0xff)==SQLITE_BUSY ){
+ p->pc = (int)(pOp - aOp);
+ p->rc = rc;
+ goto vdbe_return;
+ }
goto abort_due_to_error;
}
- if( pOp->p2 && p->usesStmtJournal
- && (db->autoCommit==0 || db->activeVdbeCnt>1)
+ if( pOp->p2 && p->usesStmtJournal
+ && (db->autoCommit==0 || db->nVdbeRead>1)
){
- assert( sqlite3BtreeIsInTrans(u.as.pBt) );
+ assert( sqlite3BtreeIsInTrans(pBt) );
if( p->iStatement==0 ){
assert( db->nStatement>=0 && db->nSavepoint>=0 );
- db->nStatement++;
+ db->nStatement++;
p->iStatement = db->nSavepoint + db->nStatement;
}
rc = sqlite3VtabSavepoint(db, SAVEPOINT_BEGIN, p->iStatement-1);
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement);
+ rc = sqlite3BtreeBeginStmt(pBt, p->iStatement);
}
/* Store the current value of the database handles deferred constraint
** counter. If the statement transaction needs to be rolled back,
** the value of this counter needs to be restored too. */
p->nStmtDefCons = db->nDeferredCons;
+ p->nStmtDefImmCons = db->nDeferredImmCons;
}
+
+ /* Gather the schema version number for checking:
+ ** IMPLEMENTATION-OF: R-03189-51135 As each SQL statement runs, the schema
+ ** version is checked to ensure that the schema has not changed since the
+ ** SQL statement was prepared.
+ */
+ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta);
+ iGen = db->aDb[pOp->p1].pSchema->iGeneration;
+ }else{
+ iGen = iMeta = 0;
}
+ assert( pOp->p5==0 || pOp->p4type==P4_INT32 );
+ if( pOp->p5 && (iMeta!=pOp->p3 || iGen!=pOp->p4.i) ){
+ sqlite3DbFree(db, p->zErrMsg);
+ p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
+ /* If the schema-cookie from the database file matches the cookie
+ ** stored with the in-memory representation of the schema, do
+ ** not reload the schema from the database file.
+ **
+ ** If virtual-tables are in use, this is not just an optimization.
+ ** Often, v-tables store their data in other SQLite tables, which
+ ** are queried from within xNext() and other v-table methods using
+ ** prepared queries. If such a query is out-of-date, we do not want to
+ ** discard the database schema, as the user code implementing the
+ ** v-table would have to be ready for the sqlite3_vtab structure itself
+ ** to be invalidated whenever sqlite3_step() is called from within
+ ** a v-table method.
+ */
+ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
+ sqlite3ResetOneSchema(db, pOp->p1);
+ }
+ p->expired = 1;
+ rc = SQLITE_SCHEMA;
+ }
+ if( rc ) goto abort_due_to_error;
break;
}
@@ -66458,56 +80565,53 @@ case OP_Transaction: {
** must be started or there must be an open cursor) before
** executing this instruction.
*/
-case OP_ReadCookie: { /* out2-prerelease */
-#if 0 /* local variables moved into u.at */
+case OP_ReadCookie: { /* out2 */
int iMeta;
int iDb;
int iCookie;
-#endif /* local variables moved into u.at */
- u.at.iDb = pOp->p1;
- u.at.iCookie = pOp->p3;
+ assert( p->bIsReader );
+ iDb = pOp->p1;
+ iCookie = pOp->p3;
assert( pOp->p3<SQLITE_N_BTREE_META );
- assert( u.at.iDb>=0 && u.at.iDb<db->nDb );
- assert( db->aDb[u.at.iDb].pBt!=0 );
- assert( (p->btreeMask & (((yDbMask)1)<<u.at.iDb))!=0 );
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( db->aDb[iDb].pBt!=0 );
+ assert( DbMaskTest(p->btreeMask, iDb) );
- sqlite3BtreeGetMeta(db->aDb[u.at.iDb].pBt, u.at.iCookie, (u32 *)&u.at.iMeta);
- pOut->u.i = u.at.iMeta;
+ sqlite3BtreeGetMeta(db->aDb[iDb].pBt, iCookie, (u32 *)&iMeta);
+ pOut = out2Prerelease(p, pOp);
+ pOut->u.i = iMeta;
break;
}
/* Opcode: SetCookie P1 P2 P3 * *
**
-** Write the content of register P3 (interpreted as an integer)
-** into cookie number P2 of database P1. P2==1 is the schema version.
-** P2==2 is the database format. P2==3 is the recommended pager cache
+** Write the integer value P3 into cookie number P2 of database P1.
+** P2==1 is the schema version. P2==2 is the database format.
+** P2==3 is the recommended pager cache
** size, and so forth. P1==0 is the main database file and P1==1 is the
** database file used to store temporary tables.
**
** A transaction must be started before executing this opcode.
*/
-case OP_SetCookie: { /* in3 */
-#if 0 /* local variables moved into u.au */
+case OP_SetCookie: {
Db *pDb;
-#endif /* local variables moved into u.au */
assert( pOp->p2<SQLITE_N_BTREE_META );
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.au.pDb = &db->aDb[pOp->p1];
- assert( u.au.pDb->pBt!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ assert( p->readOnly==0 );
+ pDb = &db->aDb[pOp->p1];
+ assert( pDb->pBt!=0 );
assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
- pIn3 = &aMem[pOp->p3];
- sqlite3VdbeMemIntegerify(pIn3);
/* See note about index shifting on OP_ReadCookie */
- rc = sqlite3BtreeUpdateMeta(u.au.pDb->pBt, pOp->p2, (int)pIn3->u.i);
+ rc = sqlite3BtreeUpdateMeta(pDb->pBt, pOp->p2, pOp->p3);
if( pOp->p2==BTREE_SCHEMA_VERSION ){
/* When the schema cookie changes, record the new cookie internally */
- u.au.pDb->pSchema->schema_cookie = (int)pIn3->u.i;
+ pDb->pSchema->schema_cookie = pOp->p3;
db->flags |= SQLITE_InternChanges;
}else if( pOp->p2==BTREE_FILE_FORMAT ){
/* Record changes in the file format */
- u.au.pDb->pSchema->file_format = (u8)pIn3->u.i;
+ pDb->pSchema->file_format = pOp->p3;
}
if( pOp->p1==1 ){
/* Invalidate all prepared statements whenever the TEMP database
@@ -66515,71 +80619,12 @@ case OP_SetCookie: { /* in3 */
sqlite3ExpirePreparedStatements(db);
p->expired = 0;
}
- break;
-}
-
-/* Opcode: VerifyCookie P1 P2 P3 * *
-**
-** Check the value of global database parameter number 0 (the
-** schema version) and make sure it is equal to P2 and that the
-** generation counter on the local schema parse equals P3.
-**
-** P1 is the database number which is 0 for the main database file
-** and 1 for the file holding temporary tables and some higher number
-** for auxiliary databases.
-**
-** The cookie changes its value whenever the database schema changes.
-** This operation is used to detect when that the cookie has changed
-** and that the current process needs to reread the schema.
-**
-** Either a transaction needs to have been started or an OP_Open needs
-** to be executed (to establish a read lock) before this opcode is
-** invoked.
-*/
-case OP_VerifyCookie: {
-#if 0 /* local variables moved into u.av */
- int iMeta;
- int iGen;
- Btree *pBt;
-#endif /* local variables moved into u.av */
-
- assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) );
- u.av.pBt = db->aDb[pOp->p1].pBt;
- if( u.av.pBt ){
- sqlite3BtreeGetMeta(u.av.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.av.iMeta);
- u.av.iGen = db->aDb[pOp->p1].pSchema->iGeneration;
- }else{
- u.av.iGen = u.av.iMeta = 0;
- }
- if( u.av.iMeta!=pOp->p2 || u.av.iGen!=pOp->p3 ){
- sqlite3DbFree(db, p->zErrMsg);
- p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed");
- /* If the schema-cookie from the database file matches the cookie
- ** stored with the in-memory representation of the schema, do
- ** not reload the schema from the database file.
- **
- ** If virtual-tables are in use, this is not just an optimization.
- ** Often, v-tables store their data in other SQLite tables, which
- ** are queried from within xNext() and other v-table methods using
- ** prepared queries. If such a query is out-of-date, we do not want to
- ** discard the database schema, as the user code implementing the
- ** v-table would have to be ready for the sqlite3_vtab structure itself
- ** to be invalidated whenever sqlite3_step() is called from within
- ** a v-table method.
- */
- if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.av.iMeta ){
- sqlite3ResetInternalSchema(db, pOp->p1);
- }
-
- p->expired = 1;
- rc = SQLITE_SCHEMA;
- }
+ if( rc ) goto abort_due_to_error;
break;
}
/* Opcode: OpenRead P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
**
** Open a read-only cursor for the database table whose root page is
** P2 in a database file. The database file is determined by P3.
@@ -66607,9 +80652,24 @@ case OP_VerifyCookie: {
** sequence of the index being opened. Otherwise, if P4 is an integer
** value, it is set to the number of columns in the table.
**
-** See also OpenWrite.
+** See also: OpenWrite, ReopenIdx
+*/
+/* Opcode: ReopenIdx P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
+**
+** The ReopenIdx opcode works exactly like ReadOpen except that it first
+** checks to see if the cursor on P1 is already open with a root page
+** number of P2 and if it is this opcode becomes a no-op. In other words,
+** if the cursor is already open, do not reopen it.
+**
+** The ReopenIdx opcode may only be used with P5==0 and with P4 being
+** a P4_KEYINFO object. Furthermore, the P3 value must be the same as
+** every other ReopenIdx or OpenRead for the same cursor number.
+**
+** See the OpenRead opcode documentation for additional information.
*/
/* Opcode: OpenWrite P1 P2 P3 P4 P5
+** Synopsis: root=P2 iDb=P3
**
** Open a read/write cursor named P1 on the table or index whose root
** page is P2. Or if P5!=0 use the content of register P2 to find the
@@ -66628,9 +80688,7 @@ case OP_VerifyCookie: {
**
** See also OpenRead.
*/
-case OP_OpenRead:
-case OP_OpenWrite: {
-#if 0 /* local variables moved into u.aw */
+case OP_ReopenIdx: {
int nField;
KeyInfo *pKeyInfo;
int p2;
@@ -66639,77 +80697,104 @@ case OP_OpenWrite: {
Btree *pX;
VdbeCursor *pCur;
Db *pDb;
-#endif /* local variables moved into u.aw */
+
+ assert( pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ );
+ assert( pOp->p4type==P4_KEYINFO );
+ pCur = p->apCsr[pOp->p1];
+ if( pCur && pCur->pgnoRoot==(u32)pOp->p2 ){
+ assert( pCur->iDb==pOp->p3 ); /* Guaranteed by the code generator */
+ goto open_cursor_set_hints;
+ }
+ /* If the cursor is not currently open or is open on a different
+ ** index, then fall through into OP_OpenRead to force a reopen */
+case OP_OpenRead:
+case OP_OpenWrite:
+
+ assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 || pOp->p5==OPFLAG_SEEKEQ );
+ assert( p->bIsReader );
+ assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
+ || p->readOnly==0 );
if( p->expired ){
- rc = SQLITE_ABORT;
- break;
+ rc = SQLITE_ABORT_ROLLBACK;
+ goto abort_due_to_error;
}
- u.aw.nField = 0;
- u.aw.pKeyInfo = 0;
- u.aw.p2 = pOp->p2;
- u.aw.iDb = pOp->p3;
- assert( u.aw.iDb>=0 && u.aw.iDb<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<u.aw.iDb))!=0 );
- u.aw.pDb = &db->aDb[u.aw.iDb];
- u.aw.pX = u.aw.pDb->pBt;
- assert( u.aw.pX!=0 );
+ nField = 0;
+ pKeyInfo = 0;
+ p2 = pOp->p2;
+ iDb = pOp->p3;
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( DbMaskTest(p->btreeMask, iDb) );
+ pDb = &db->aDb[iDb];
+ pX = pDb->pBt;
+ assert( pX!=0 );
if( pOp->opcode==OP_OpenWrite ){
- u.aw.wrFlag = 1;
- assert( sqlite3SchemaMutexHeld(db, u.aw.iDb, 0) );
- if( u.aw.pDb->pSchema->file_format < p->minWriteFileFormat ){
- p->minWriteFileFormat = u.aw.pDb->pSchema->file_format;
+ assert( OPFLAG_FORDELETE==BTREE_FORDELETE );
+ wrFlag = BTREE_WRCSR | (pOp->p5 & OPFLAG_FORDELETE);
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ if( pDb->pSchema->file_format < p->minWriteFileFormat ){
+ p->minWriteFileFormat = pDb->pSchema->file_format;
}
}else{
- u.aw.wrFlag = 0;
+ wrFlag = 0;
}
- if( pOp->p5 ){
- assert( u.aw.p2>0 );
- assert( u.aw.p2<=p->nMem );
- pIn2 = &aMem[u.aw.p2];
+ if( pOp->p5 & OPFLAG_P2ISREG ){
+ assert( p2>0 );
+ assert( p2<=(p->nMem+1 - p->nCursor) );
+ pIn2 = &aMem[p2];
assert( memIsValid(pIn2) );
assert( (pIn2->flags & MEM_Int)!=0 );
sqlite3VdbeMemIntegerify(pIn2);
- u.aw.p2 = (int)pIn2->u.i;
- /* The u.aw.p2 value always comes from a prior OP_CreateTable opcode and
- ** that opcode will always set the u.aw.p2 value to 2 or more or else fail.
+ p2 = (int)pIn2->u.i;
+ /* The p2 value always comes from a prior OP_CreateTable opcode and
+ ** that opcode will always set the p2 value to 2 or more or else fail.
** If there were a failure, the prepared statement would have halted
** before reaching this instruction. */
- if( NEVER(u.aw.p2<2) ) {
- rc = SQLITE_CORRUPT_BKPT;
- goto abort_due_to_error;
- }
+ assert( p2>=2 );
}
if( pOp->p4type==P4_KEYINFO ){
- u.aw.pKeyInfo = pOp->p4.pKeyInfo;
- u.aw.pKeyInfo->enc = ENC(p->db);
- u.aw.nField = u.aw.pKeyInfo->nField+1;
+ pKeyInfo = pOp->p4.pKeyInfo;
+ assert( pKeyInfo->enc==ENC(db) );
+ assert( pKeyInfo->db==db );
+ nField = pKeyInfo->nField+pKeyInfo->nXField;
}else if( pOp->p4type==P4_INT32 ){
- u.aw.nField = pOp->p4.i;
+ nField = pOp->p4.i;
}
assert( pOp->p1>=0 );
- u.aw.pCur = allocateCursor(p, pOp->p1, u.aw.nField, u.aw.iDb, 1);
- if( u.aw.pCur==0 ) goto no_mem;
- u.aw.pCur->nullRow = 1;
- u.aw.pCur->isOrdered = 1;
- rc = sqlite3BtreeCursor(u.aw.pX, u.aw.p2, u.aw.wrFlag, u.aw.pKeyInfo, u.aw.pCur->pCursor);
- u.aw.pCur->pKeyInfo = u.aw.pKeyInfo;
-
- /* Since it performs no memory allocation or IO, the only value that
- ** sqlite3BtreeCursor() may return is SQLITE_OK. */
- assert( rc==SQLITE_OK );
-
- /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of
+ assert( nField>=0 );
+ testcase( nField==0 ); /* Table with INTEGER PRIMARY KEY and nothing else */
+ pCur = allocateCursor(p, pOp->p1, nField, iDb, CURTYPE_BTREE);
+ if( pCur==0 ) goto no_mem;
+ pCur->nullRow = 1;
+ pCur->isOrdered = 1;
+ pCur->pgnoRoot = p2;
+#ifdef SQLITE_DEBUG
+ pCur->wrFlag = wrFlag;
+#endif
+ rc = sqlite3BtreeCursor(pX, p2, wrFlag, pKeyInfo, pCur->uc.pCursor);
+ pCur->pKeyInfo = pKeyInfo;
+ /* Set the VdbeCursor.isTable variable. Previous versions of
** SQLite used to check if the root-page flags were sane at this point
** and report database corruption if they were not, but this check has
- ** since moved into the btree layer. */
- u.aw.pCur->isTable = pOp->p4type!=P4_KEYINFO;
- u.aw.pCur->isIndex = !u.aw.pCur->isTable;
+ ** since moved into the btree layer. */
+ pCur->isTable = pOp->p4type!=P4_KEYINFO;
+
+open_cursor_set_hints:
+ assert( OPFLAG_BULKCSR==BTREE_BULKLOAD );
+ assert( OPFLAG_SEEKEQ==BTREE_SEEK_EQ );
+ testcase( pOp->p5 & OPFLAG_BULKCSR );
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ testcase( pOp->p2 & OPFLAG_SEEKEQ );
+#endif
+ sqlite3BtreeCursorHintFlags(pCur->uc.pCursor,
+ (pOp->p5 & (OPFLAG_BULKCSR|OPFLAG_SEEKEQ)));
+ if( rc ) goto abort_due_to_error;
break;
}
/* Opcode: OpenEphemeral P1 P2 * P4 P5
+** Synopsis: nColumn=P2
**
** Open a new cursor P1 to a transient table.
** The cursor is always opened read/write even if
@@ -66721,18 +80806,13 @@ case OP_OpenWrite: {
** if P4 is not 0. If P4 is not NULL, it points to a KeyInfo structure
** that defines the format of keys in the index.
**
-** This opcode was once called OpenTemp. But that created
-** confusion because the term "temp table", might refer either
-** to a TEMP table at the SQL level, or to a table opened by
-** this opcode. Then this opcode was call OpenVirtual. But
-** that created confusion with the whole virtual-table idea.
-**
** The P5 parameter can be a mask of the BTREE_* flags defined
** in btree.h. These flags control aspects of the operation of
** the btree. The BTREE_OMIT_JOURNAL and BTREE_SINGLE flags are
** added automatically.
*/
/* Opcode: OpenAutoindex P1 P2 * P4 *
+** Synopsis: nColumn=P2
**
** This opcode works the same as OP_OpenEphemeral. It has a
** different name to distinguish its use. Tables created using
@@ -66741,24 +80821,25 @@ case OP_OpenWrite: {
*/
case OP_OpenAutoindex:
case OP_OpenEphemeral: {
-#if 0 /* local variables moved into u.ax */
VdbeCursor *pCx;
-#endif /* local variables moved into u.ax */
- static const int vfsFlags =
+ KeyInfo *pKeyInfo;
+
+ static const int vfsFlags =
SQLITE_OPEN_READWRITE |
SQLITE_OPEN_CREATE |
SQLITE_OPEN_EXCLUSIVE |
SQLITE_OPEN_DELETEONCLOSE |
SQLITE_OPEN_TRANSIENT_DB;
-
assert( pOp->p1>=0 );
- u.ax.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
- if( u.ax.pCx==0 ) goto no_mem;
- u.ax.pCx->nullRow = 1;
- rc = sqlite3BtreeOpen(db->pVfs, 0, db, &u.ax.pCx->pBt,
+ assert( pOp->p2>=0 );
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_BTREE);
+ if( pCx==0 ) goto no_mem;
+ pCx->nullRow = 1;
+ pCx->isEphemeral = 1;
+ rc = sqlite3BtreeOpen(db->pVfs, 0, db, &pCx->pBt,
BTREE_OMIT_JOURNAL | BTREE_SINGLE | pOp->p5, vfsFlags);
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeBeginTrans(u.ax.pCx->pBt, 1);
+ rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
}
if( rc==SQLITE_OK ){
/* If a transient index is required, create it by calling
@@ -66766,56 +80847,78 @@ case OP_OpenEphemeral: {
** opening it. If a transient table is required, just use the
** automatically created table with root-page 1 (an BLOB_INTKEY table).
*/
- if( pOp->p4.pKeyInfo ){
+ if( (pKeyInfo = pOp->p4.pKeyInfo)!=0 ){
int pgno;
assert( pOp->p4type==P4_KEYINFO );
- rc = sqlite3BtreeCreateTable(u.ax.pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
+ rc = sqlite3BtreeCreateTable(pCx->pBt, &pgno, BTREE_BLOBKEY | pOp->p5);
if( rc==SQLITE_OK ){
assert( pgno==MASTER_ROOT+1 );
- rc = sqlite3BtreeCursor(u.ax.pCx->pBt, pgno, 1,
- (KeyInfo*)pOp->p4.z, u.ax.pCx->pCursor);
- u.ax.pCx->pKeyInfo = pOp->p4.pKeyInfo;
- u.ax.pCx->pKeyInfo->enc = ENC(p->db);
+ assert( pKeyInfo->db==db );
+ assert( pKeyInfo->enc==ENC(db) );
+ pCx->pKeyInfo = pKeyInfo;
+ rc = sqlite3BtreeCursor(pCx->pBt, pgno, BTREE_WRCSR,
+ pKeyInfo, pCx->uc.pCursor);
}
- u.ax.pCx->isTable = 0;
+ pCx->isTable = 0;
}else{
- rc = sqlite3BtreeCursor(u.ax.pCx->pBt, MASTER_ROOT, 1, 0, u.ax.pCx->pCursor);
- u.ax.pCx->isTable = 1;
+ rc = sqlite3BtreeCursor(pCx->pBt, MASTER_ROOT, BTREE_WRCSR,
+ 0, pCx->uc.pCursor);
+ pCx->isTable = 1;
}
}
- u.ax.pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
- u.ax.pCx->isIndex = !u.ax.pCx->isTable;
+ if( rc ) goto abort_due_to_error;
+ pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
break;
}
-/* Opcode: OpenSorter P1 P2 * P4 *
+/* Opcode: SorterOpen P1 P2 P3 P4 *
**
** This opcode works like OP_OpenEphemeral except that it opens
** a transient index that is specifically designed to sort large
** tables using an external merge-sort algorithm.
+**
+** If argument P3 is non-zero, then it indicates that the sorter may
+** assume that a stable sort considering the first P3 fields of each
+** key is sufficient to produce the required results.
*/
case OP_SorterOpen: {
-#if 0 /* local variables moved into u.ay */
VdbeCursor *pCx;
-#endif /* local variables moved into u.ay */
-#ifndef SQLITE_OMIT_MERGE_SORT
- u.ay.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1);
- if( u.ay.pCx==0 ) goto no_mem;
- u.ay.pCx->pKeyInfo = pOp->p4.pKeyInfo;
- u.ay.pCx->pKeyInfo->enc = ENC(p->db);
- u.ay.pCx->isSorter = 1;
- rc = sqlite3VdbeSorterInit(db, u.ay.pCx);
-#else
- pOp->opcode = OP_OpenEphemeral;
- pc--;
-#endif
+
+ assert( pOp->p1>=0 );
+ assert( pOp->p2>=0 );
+ pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, CURTYPE_SORTER);
+ if( pCx==0 ) goto no_mem;
+ pCx->pKeyInfo = pOp->p4.pKeyInfo;
+ assert( pCx->pKeyInfo->db==db );
+ assert( pCx->pKeyInfo->enc==ENC(db) );
+ rc = sqlite3VdbeSorterInit(db, pOp->p3, pCx);
+ if( rc ) goto abort_due_to_error;
+ break;
+}
+
+/* Opcode: SequenceTest P1 P2 * * *
+** Synopsis: if( cursor[P1].ctr++ ) pc = P2
+**
+** P1 is a sorter cursor. If the sequence counter is currently zero, jump
+** to P2. Regardless of whether or not the jump is taken, increment the
+** the sequence value.
+*/
+case OP_SequenceTest: {
+ VdbeCursor *pC;
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ assert( isSorter(pC) );
+ if( (pC->seqCount++)==0 ){
+ goto jump_to_p2;
+ }
break;
}
/* Opcode: OpenPseudo P1 P2 P3 * *
+** Synopsis: P3 columns in r[P2]
**
** Open a new cursor that points to a fake table that contains a single
-** row of data. The content of that one row in the content of memory
+** row of data. The content of that one row is the content of memory
** register P2. In other words, cursor P1 becomes an alias for the
** MEM_Blob content contained in register P2.
**
@@ -66828,17 +80931,16 @@ case OP_SorterOpen: {
** the pseudo-table.
*/
case OP_OpenPseudo: {
-#if 0 /* local variables moved into u.az */
VdbeCursor *pCx;
-#endif /* local variables moved into u.az */
assert( pOp->p1>=0 );
- u.az.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0);
- if( u.az.pCx==0 ) goto no_mem;
- u.az.pCx->nullRow = 1;
- u.az.pCx->pseudoTableReg = pOp->p2;
- u.az.pCx->isTable = 1;
- u.az.pCx->isIndex = 0;
+ assert( pOp->p3>=0 );
+ pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, CURTYPE_PSEUDO);
+ if( pCx==0 ) goto no_mem;
+ pCx->nullRow = 1;
+ pCx->uc.pseudoTableReg = pOp->p2;
+ pCx->isTable = 1;
+ assert( pOp->p5==0 );
break;
}
@@ -66854,7 +80956,28 @@ case OP_Close: {
break;
}
-/* Opcode: SeekGe P1 P2 P3 P4 *
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+/* Opcode: ColumnsUsed P1 * * P4 *
+**
+** This opcode (which only exists if SQLite was compiled with
+** SQLITE_ENABLE_COLUMN_USED_MASK) identifies which columns of the
+** table or index for cursor P1 are used. P4 is a 64-bit integer
+** (P4_INT64) in which the first 63 bits are one for each of the
+** first 63 columns of the table or index that are actually used
+** by the cursor. The high-order bit is set if any column after
+** the 64th is used.
+*/
+case OP_ColumnsUsed: {
+ VdbeCursor *pC;
+ pC = p->apCsr[pOp->p1];
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pC->maskUsed = *(u64*)pOp->p4.pI64;
+ break;
+}
+#endif
+
+/* Opcode: SeekGE P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as the key. If cursor P1 refers
@@ -66865,9 +80988,21 @@ case OP_Close: {
** is greater than or equal to the key value. If there are no records
** greater than or equal to the key and P2 is not zero, then jump to P2.
**
-** See also: Found, NotFound, Distinct, SeekLt, SeekGt, SeekLe
+** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this
+** opcode will always land on a record that equally equals the key, or
+** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this
+** opcode must be followed by an IdxLE opcode with the same arguments.
+** The IdxLE opcode will be skipped if this opcode succeeds, but the
+** IdxLE opcode will be used on subsequent loop iterations.
+**
+** This opcode leaves the cursor configured to move in forward order,
+** from the beginning toward the end. In other words, the cursor is
+** configured to use Next, not Prev.
+**
+** See also: Found, NotFound, SeekLt, SeekGt, SeekLe
*/
-/* Opcode: SeekGt P1 P2 P3 P4 *
+/* Opcode: SeekGT P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as a key. If cursor P1 refers
@@ -66878,9 +81013,14 @@ case OP_Close: {
** is greater than the key value. If there are no records greater than
** the key and P2 is not zero, then jump to P2.
**
-** See also: Found, NotFound, Distinct, SeekLt, SeekGe, SeekLe
+** This opcode leaves the cursor configured to move in forward order,
+** from the beginning toward the end. In other words, the cursor is
+** configured to use Next, not Prev.
+**
+** See also: Found, NotFound, SeekLt, SeekGe, SeekLe
*/
-/* Opcode: SeekLt P1 P2 P3 P4 *
+/* Opcode: SeekLT P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as a key. If cursor P1 refers
@@ -66891,9 +81031,14 @@ case OP_Close: {
** is less than the key value. If there are no records less than
** the key and P2 is not zero, then jump to P2.
**
-** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLe
+** This opcode leaves the cursor configured to move in reverse order,
+** from the end toward the beginning. In other words, the cursor is
+** configured to use Prev, not Next.
+**
+** See also: Found, NotFound, SeekGt, SeekGe, SeekLe
*/
-/* Opcode: SeekLe P1 P2 P3 P4 *
+/* Opcode: SeekLE P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If cursor P1 refers to an SQL table (B-Tree that uses integer keys),
** use the value in register P3 as a key. If cursor P1 refers
@@ -66904,192 +81049,187 @@ case OP_Close: {
** is less than or equal to the key value. If there are no records
** less than or equal to the key and P2 is not zero, then jump to P2.
**
-** See also: Found, NotFound, Distinct, SeekGt, SeekGe, SeekLt
-*/
-case OP_SeekLt: /* jump, in3 */
-case OP_SeekLe: /* jump, in3 */
-case OP_SeekGe: /* jump, in3 */
-case OP_SeekGt: { /* jump, in3 */
-#if 0 /* local variables moved into u.ba */
- int res;
- int oc;
- VdbeCursor *pC;
- UnpackedRecord r;
- int nField;
- i64 iKey; /* The rowid we are to seek to */
-#endif /* local variables moved into u.ba */
+** This opcode leaves the cursor configured to move in reverse order,
+** from the end toward the beginning. In other words, the cursor is
+** configured to use Prev, not Next.
+**
+** If the cursor P1 was opened using the OPFLAG_SEEKEQ flag, then this
+** opcode will always land on a record that equally equals the key, or
+** else jump immediately to P2. When the cursor is OPFLAG_SEEKEQ, this
+** opcode must be followed by an IdxGE opcode with the same arguments.
+** The IdxGE opcode will be skipped if this opcode succeeds, but the
+** IdxGE opcode will be used on subsequent loop iterations.
+**
+** See also: Found, NotFound, SeekGt, SeekGe, SeekLt
+*/
+case OP_SeekLT: /* jump, in3 */
+case OP_SeekLE: /* jump, in3 */
+case OP_SeekGE: /* jump, in3 */
+case OP_SeekGT: { /* jump, in3 */
+ int res; /* Comparison result */
+ int oc; /* Opcode */
+ VdbeCursor *pC; /* The cursor to seek */
+ UnpackedRecord r; /* The key to seek for */
+ int nField; /* Number of columns or fields in the key */
+ i64 iKey; /* The rowid we are to seek to */
+ int eqOnly; /* Only interested in == results */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p2!=0 );
- u.ba.pC = p->apCsr[pOp->p1];
- assert( u.ba.pC!=0 );
- assert( u.ba.pC->pseudoTableReg==0 );
- assert( OP_SeekLe == OP_SeekLt+1 );
- assert( OP_SeekGe == OP_SeekLt+2 );
- assert( OP_SeekGt == OP_SeekLt+3 );
- assert( u.ba.pC->isOrdered );
- if( ALWAYS(u.ba.pC->pCursor!=0) ){
- u.ba.oc = pOp->opcode;
- u.ba.pC->nullRow = 0;
- if( u.ba.pC->isTable ){
- /* The input value in P3 might be of any type: integer, real, string,
- ** blob, or NULL. But it needs to be an integer before we can do
- ** the seek, so covert it. */
- pIn3 = &aMem[pOp->p3];
- applyNumericAffinity(pIn3);
- u.ba.iKey = sqlite3VdbeIntValue(pIn3);
- u.ba.pC->rowidIsValid = 0;
-
- /* If the P3 value could not be converted into an integer without
- ** loss of information, then special processing is required... */
- if( (pIn3->flags & MEM_Int)==0 ){
- if( (pIn3->flags & MEM_Real)==0 ){
- /* If the P3 value cannot be converted into any kind of a number,
- ** then the seek is not possible, so jump to P2 */
- pc = pOp->p2 - 1;
- break;
- }
- /* If we reach this point, then the P3 value must be a floating
- ** point number. */
- assert( (pIn3->flags & MEM_Real)!=0 );
-
- if( u.ba.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.ba.iKey || pIn3->r>0) ){
- /* The P3 value is too large in magnitude to be expressed as an
- ** integer. */
- u.ba.res = 1;
- if( pIn3->r<0 ){
- if( u.ba.oc>=OP_SeekGe ){ assert( u.ba.oc==OP_SeekGe || u.ba.oc==OP_SeekGt );
- rc = sqlite3BtreeFirst(u.ba.pC->pCursor, &u.ba.res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- }
- }else{
- if( u.ba.oc<=OP_SeekLe ){ assert( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe );
- rc = sqlite3BtreeLast(u.ba.pC->pCursor, &u.ba.res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- }
- }
- if( u.ba.res ){
- pc = pOp->p2 - 1;
- }
- break;
- }else if( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekGe ){
- /* Use the ceiling() function to convert real->int */
- if( pIn3->r > (double)u.ba.iKey ) u.ba.iKey++;
- }else{
- /* Use the floor() function to convert real->int */
- assert( u.ba.oc==OP_SeekLe || u.ba.oc==OP_SeekGt );
- if( pIn3->r < (double)u.ba.iKey ) u.ba.iKey--;
- }
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( OP_SeekLE == OP_SeekLT+1 );
+ assert( OP_SeekGE == OP_SeekLT+2 );
+ assert( OP_SeekGT == OP_SeekLT+3 );
+ assert( pC->isOrdered );
+ assert( pC->uc.pCursor!=0 );
+ oc = pOp->opcode;
+ eqOnly = 0;
+ pC->nullRow = 0;
+#ifdef SQLITE_DEBUG
+ pC->seekOp = pOp->opcode;
+#endif
+
+ if( pC->isTable ){
+ /* The BTREE_SEEK_EQ flag is only set on index cursors */
+ assert( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ)==0 );
+
+ /* The input value in P3 might be of any type: integer, real, string,
+ ** blob, or NULL. But it needs to be an integer before we can do
+ ** the seek, so convert it. */
+ pIn3 = &aMem[pOp->p3];
+ if( (pIn3->flags & (MEM_Int|MEM_Real|MEM_Str))==MEM_Str ){
+ applyNumericAffinity(pIn3, 0);
+ }
+ iKey = sqlite3VdbeIntValue(pIn3);
+
+ /* If the P3 value could not be converted into an integer without
+ ** loss of information, then special processing is required... */
+ if( (pIn3->flags & MEM_Int)==0 ){
+ if( (pIn3->flags & MEM_Real)==0 ){
+ /* If the P3 value cannot be converted into any kind of a number,
+ ** then the seek is not possible, so jump to P2 */
+ VdbeBranchTaken(1,2); goto jump_to_p2;
+ break;
}
- rc = sqlite3BtreeMovetoUnpacked(u.ba.pC->pCursor, 0, (u64)u.ba.iKey, 0, &u.ba.res);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
+
+ /* If the approximation iKey is larger than the actual real search
+ ** term, substitute >= for > and < for <=. e.g. if the search term
+ ** is 4.9 and the integer approximation 5:
+ **
+ ** (x > 4.9) -> (x >= 5)
+ ** (x <= 4.9) -> (x < 5)
+ */
+ if( pIn3->u.r<(double)iKey ){
+ assert( OP_SeekGE==(OP_SeekGT-1) );
+ assert( OP_SeekLT==(OP_SeekLE-1) );
+ assert( (OP_SeekLE & 0x0001)==(OP_SeekGT & 0x0001) );
+ if( (oc & 0x0001)==(OP_SeekGT & 0x0001) ) oc--;
}
- if( u.ba.res==0 ){
- u.ba.pC->rowidIsValid = 1;
- u.ba.pC->lastRowid = u.ba.iKey;
+
+ /* If the approximation iKey is smaller than the actual real search
+ ** term, substitute <= for < and > for >=. */
+ else if( pIn3->u.r>(double)iKey ){
+ assert( OP_SeekLE==(OP_SeekLT+1) );
+ assert( OP_SeekGT==(OP_SeekGE+1) );
+ assert( (OP_SeekLT & 0x0001)==(OP_SeekGE & 0x0001) );
+ if( (oc & 0x0001)==(OP_SeekLT & 0x0001) ) oc++;
}
- }else{
- u.ba.nField = pOp->p4.i;
- assert( pOp->p4type==P4_INT32 );
- assert( u.ba.nField>0 );
- u.ba.r.pKeyInfo = u.ba.pC->pKeyInfo;
- u.ba.r.nField = (u16)u.ba.nField;
+ }
+ rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)iKey, 0, &res);
+ pC->movetoTarget = iKey; /* Used by OP_Delete */
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ }else{
+ /* For a cursor with the BTREE_SEEK_EQ hint, only the OP_SeekGE and
+ ** OP_SeekLE opcodes are allowed, and these must be immediately followed
+ ** by an OP_IdxGT or OP_IdxLT opcode, respectively, with the same key.
+ */
+ if( sqlite3BtreeCursorHasHint(pC->uc.pCursor, BTREE_SEEK_EQ) ){
+ eqOnly = 1;
+ assert( pOp->opcode==OP_SeekGE || pOp->opcode==OP_SeekLE );
+ assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT );
+ assert( pOp[1].p1==pOp[0].p1 );
+ assert( pOp[1].p2==pOp[0].p2 );
+ assert( pOp[1].p3==pOp[0].p3 );
+ assert( pOp[1].p4.i==pOp[0].p4.i );
+ }
- /* The next line of code computes as follows, only faster:
- ** if( u.ba.oc==OP_SeekGt || u.ba.oc==OP_SeekLe ){
- ** u.ba.r.flags = UNPACKED_INCRKEY;
- ** }else{
- ** u.ba.r.flags = 0;
- ** }
- */
- u.ba.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.ba.oc - OP_SeekLt)));
- assert( u.ba.oc!=OP_SeekGt || u.ba.r.flags==UNPACKED_INCRKEY );
- assert( u.ba.oc!=OP_SeekLe || u.ba.r.flags==UNPACKED_INCRKEY );
- assert( u.ba.oc!=OP_SeekGe || u.ba.r.flags==0 );
- assert( u.ba.oc!=OP_SeekLt || u.ba.r.flags==0 );
+ nField = pOp->p4.i;
+ assert( pOp->p4type==P4_INT32 );
+ assert( nField>0 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)nField;
+
+ /* The next line of code computes as follows, only faster:
+ ** if( oc==OP_SeekGT || oc==OP_SeekLE ){
+ ** r.default_rc = -1;
+ ** }else{
+ ** r.default_rc = +1;
+ ** }
+ */
+ r.default_rc = ((1 & (oc - OP_SeekLT)) ? -1 : +1);
+ assert( oc!=OP_SeekGT || r.default_rc==-1 );
+ assert( oc!=OP_SeekLE || r.default_rc==-1 );
+ assert( oc!=OP_SeekGE || r.default_rc==+1 );
+ assert( oc!=OP_SeekLT || r.default_rc==+1 );
- u.ba.r.aMem = &aMem[pOp->p3];
+ r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.ba.r.nField; i++) assert( memIsValid(&u.ba.r.aMem[i]) ); }
+ { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
#endif
- ExpandBlob(u.ba.r.aMem);
- rc = sqlite3BtreeMovetoUnpacked(u.ba.pC->pCursor, &u.ba.r, 0, 0, &u.ba.res);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- u.ba.pC->rowidIsValid = 0;
+ r.eqSeen = 0;
+ rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, &r, 0, 0, &res);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( eqOnly && r.eqSeen==0 ){
+ assert( res!=0 );
+ goto seek_not_found;
}
- u.ba.pC->deferredMoveto = 0;
- u.ba.pC->cacheStatus = CACHE_STALE;
+ }
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
#ifdef SQLITE_TEST
- sqlite3_search_count++;
+ sqlite3_search_count++;
#endif
- if( u.ba.oc>=OP_SeekGe ){ assert( u.ba.oc==OP_SeekGe || u.ba.oc==OP_SeekGt );
- if( u.ba.res<0 || (u.ba.res==0 && u.ba.oc==OP_SeekGt) ){
- rc = sqlite3BtreeNext(u.ba.pC->pCursor, &u.ba.res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- u.ba.pC->rowidIsValid = 0;
- }else{
- u.ba.res = 0;
- }
+ if( oc>=OP_SeekGE ){ assert( oc==OP_SeekGE || oc==OP_SeekGT );
+ if( res<0 || (res==0 && oc==OP_SeekGT) ){
+ res = 0;
+ rc = sqlite3BtreeNext(pC->uc.pCursor, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
}else{
- assert( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe );
- if( u.ba.res>0 || (u.ba.res==0 && u.ba.oc==OP_SeekLt) ){
- rc = sqlite3BtreePrevious(u.ba.pC->pCursor, &u.ba.res);
- if( rc!=SQLITE_OK ) goto abort_due_to_error;
- u.ba.pC->rowidIsValid = 0;
- }else{
- /* u.ba.res might be negative because the table is empty. Check to
- ** see if this is the case.
- */
- u.ba.res = sqlite3BtreeEof(u.ba.pC->pCursor);
- }
- }
- assert( pOp->p2>0 );
- if( u.ba.res ){
- pc = pOp->p2 - 1;
+ res = 0;
}
}else{
- /* This happens when attempting to open the sqlite3_master table
- ** for read access returns SQLITE_EMPTY. In this case always
- ** take the jump (since there are no records in the table).
- */
- pc = pOp->p2 - 1;
+ assert( oc==OP_SeekLT || oc==OP_SeekLE );
+ if( res>0 || (res==0 && oc==OP_SeekLT) ){
+ res = 0;
+ rc = sqlite3BtreePrevious(pC->uc.pCursor, &res);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+ }else{
+ /* res might be negative because the table is empty. Check to
+ ** see if this is the case.
+ */
+ res = sqlite3BtreeEof(pC->uc.pCursor);
+ }
}
- break;
-}
-
-/* Opcode: Seek P1 P2 * * *
-**
-** P1 is an open table cursor and P2 is a rowid integer. Arrange
-** for P1 to move so that it points to the rowid given by P2.
-**
-** This is actually a deferred seek. Nothing actually happens until
-** the cursor is used to read a record. That way, if no reads
-** occur, no unnecessary I/O happens.
-*/
-case OP_Seek: { /* in2 */
-#if 0 /* local variables moved into u.bb */
- VdbeCursor *pC;
-#endif /* local variables moved into u.bb */
-
- assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bb.pC = p->apCsr[pOp->p1];
- assert( u.bb.pC!=0 );
- if( ALWAYS(u.bb.pC->pCursor!=0) ){
- assert( u.bb.pC->isTable );
- u.bb.pC->nullRow = 0;
- pIn2 = &aMem[pOp->p2];
- u.bb.pC->movetoTarget = sqlite3VdbeIntValue(pIn2);
- u.bb.pC->rowidIsValid = 0;
- u.bb.pC->deferredMoveto = 1;
+seek_not_found:
+ assert( pOp->p2>0 );
+ VdbeBranchTaken(res!=0,2);
+ if( res ){
+ goto jump_to_p2;
+ }else if( eqOnly ){
+ assert( pOp[1].opcode==OP_IdxLT || pOp[1].opcode==OP_IdxGT );
+ pOp++; /* Skip the OP_IdxLt or OP_IdxGT that follows */
}
break;
}
-
/* Opcode: Found P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
** P4>0 then register P3 is the first of P4 registers that form an unpacked
@@ -67098,8 +81238,15 @@ case OP_Seek: { /* in2 */
** Cursor P1 is on an index btree. If the record identified by P3 and P4
** is a prefix of any entry in P1 then a jump is made to P2 and
** P1 is left pointing at the matching entry.
+**
+** This operation leaves the cursor in a state where it can be
+** advanced in the forward direction. The Next instruction will work,
+** but not the Prev instruction.
+**
+** See also: NotFound, NoConflict, NotExists. SeekGe
*/
/* Opcode: NotFound P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
**
** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
** P4>0 then register P3 is the first of P4 registers that form an unpacked
@@ -67111,231 +81258,232 @@ case OP_Seek: { /* in2 */
** falls through to the next instruction and P1 is left pointing at the
** matching entry.
**
-** See also: Found, NotExists, IsUnique
+** This operation leaves the cursor in a state where it cannot be
+** advanced in either direction. In other words, the Next and Prev
+** opcodes do not work after this operation.
+**
+** See also: Found, NotExists, NoConflict
+*/
+/* Opcode: NoConflict P1 P2 P3 P4 *
+** Synopsis: key=r[P3@P4]
+**
+** If P4==0 then register P3 holds a blob constructed by MakeRecord. If
+** P4>0 then register P3 is the first of P4 registers that form an unpacked
+** record.
+**
+** Cursor P1 is on an index btree. If the record identified by P3 and P4
+** contains any NULL value, jump immediately to P2. If all terms of the
+** record are not-NULL then a check is done to determine if any row in the
+** P1 index btree has a matching key prefix. If there are no matches, jump
+** immediately to P2. If there is a match, fall through and leave the P1
+** cursor pointing to the matching row.
+**
+** This opcode is similar to OP_NotFound with the exceptions that the
+** branch is always taken if any part of the search key input is NULL.
+**
+** This operation leaves the cursor in a state where it cannot be
+** advanced in either direction. In other words, the Next and Prev
+** opcodes do not work after this operation.
+**
+** See also: NotFound, Found, NotExists
*/
+case OP_NoConflict: /* jump, in3 */
case OP_NotFound: /* jump, in3 */
case OP_Found: { /* jump, in3 */
-#if 0 /* local variables moved into u.bc */
int alreadyExists;
+ int takeJump;
+ int ii;
VdbeCursor *pC;
int res;
char *pFree;
UnpackedRecord *pIdxKey;
UnpackedRecord r;
- char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7];
-#endif /* local variables moved into u.bc */
+ char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*4 + 7];
#ifdef SQLITE_TEST
- sqlite3_found_count++;
+ if( pOp->opcode!=OP_NoConflict ) sqlite3_found_count++;
#endif
- u.bc.alreadyExists = 0;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( pOp->p4type==P4_INT32 );
- u.bc.pC = p->apCsr[pOp->p1];
- assert( u.bc.pC!=0 );
- pIn3 = &aMem[pOp->p3];
- if( ALWAYS(u.bc.pC->pCursor!=0) ){
-
- assert( u.bc.pC->isTable==0 );
- if( pOp->p4.i>0 ){
- u.bc.r.pKeyInfo = u.bc.pC->pKeyInfo;
- u.bc.r.nField = (u16)pOp->p4.i;
- u.bc.r.aMem = pIn3;
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.bc.r.nField; i++) assert( memIsValid(&u.bc.r.aMem[i]) ); }
+ pC->seekOp = pOp->opcode;
#endif
- u.bc.r.flags = UNPACKED_PREFIX_MATCH;
- u.bc.pIdxKey = &u.bc.r;
- }else{
- u.bc.pIdxKey = sqlite3VdbeAllocUnpackedRecord(
- u.bc.pC->pKeyInfo, u.bc.aTempRec, sizeof(u.bc.aTempRec), &u.bc.pFree
- );
- if( u.bc.pIdxKey==0 ) goto no_mem;
- assert( pIn3->flags & MEM_Blob );
- assert( (pIn3->flags & MEM_Zero)==0 ); /* zeroblobs already expanded */
- sqlite3VdbeRecordUnpack(u.bc.pC->pKeyInfo, pIn3->n, pIn3->z, u.bc.pIdxKey);
- u.bc.pIdxKey->flags |= UNPACKED_PREFIX_MATCH;
- }
- rc = sqlite3BtreeMovetoUnpacked(u.bc.pC->pCursor, u.bc.pIdxKey, 0, 0, &u.bc.res);
- if( pOp->p4.i==0 ){
- sqlite3DbFree(db, u.bc.pFree);
+ pIn3 = &aMem[pOp->p3];
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
+ assert( pC->isTable==0 );
+ pFree = 0;
+ if( pOp->p4.i>0 ){
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)pOp->p4.i;
+ r.aMem = pIn3;
+#ifdef SQLITE_DEBUG
+ for(ii=0; ii<r.nField; ii++){
+ assert( memIsValid(&r.aMem[ii]) );
+ assert( (r.aMem[ii].flags & MEM_Zero)==0 || r.aMem[ii].n==0 );
+ if( ii ) REGISTER_TRACE(pOp->p3+ii, &r.aMem[ii]);
}
- if( rc!=SQLITE_OK ){
- break;
+#endif
+ pIdxKey = &r;
+ }else{
+ pIdxKey = sqlite3VdbeAllocUnpackedRecord(
+ pC->pKeyInfo, aTempRec, sizeof(aTempRec), &pFree
+ );
+ if( pIdxKey==0 ) goto no_mem;
+ assert( pIn3->flags & MEM_Blob );
+ (void)ExpandBlob(pIn3);
+ sqlite3VdbeRecordUnpack(pC->pKeyInfo, pIn3->n, pIn3->z, pIdxKey);
+ }
+ pIdxKey->default_rc = 0;
+ takeJump = 0;
+ if( pOp->opcode==OP_NoConflict ){
+ /* For the OP_NoConflict opcode, take the jump if any of the
+ ** input fields are NULL, since any key with a NULL will not
+ ** conflict */
+ for(ii=0; ii<pIdxKey->nField; ii++){
+ if( pIdxKey->aMem[ii].flags & MEM_Null ){
+ takeJump = 1;
+ break;
+ }
}
- u.bc.alreadyExists = (u.bc.res==0);
- u.bc.pC->deferredMoveto = 0;
- u.bc.pC->cacheStatus = CACHE_STALE;
}
+ rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, pIdxKey, 0, 0, &res);
+ sqlite3DbFree(db, pFree);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ pC->seekResult = res;
+ alreadyExists = (res==0);
+ pC->nullRow = 1-alreadyExists;
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
if( pOp->opcode==OP_Found ){
- if( u.bc.alreadyExists ) pc = pOp->p2 - 1;
+ VdbeBranchTaken(alreadyExists!=0,2);
+ if( alreadyExists ) goto jump_to_p2;
}else{
- if( !u.bc.alreadyExists ) pc = pOp->p2 - 1;
+ VdbeBranchTaken(takeJump||alreadyExists==0,2);
+ if( takeJump || !alreadyExists ) goto jump_to_p2;
}
break;
}
-/* Opcode: IsUnique P1 P2 P3 P4 *
+/* Opcode: SeekRowid P1 P2 P3 * *
+** Synopsis: intkey=r[P3]
**
-** Cursor P1 is open on an index b-tree - that is to say, a btree which
-** no data and where the key are records generated by OP_MakeRecord with
-** the list field being the integer ROWID of the entry that the index
-** entry refers to.
+** P1 is the index of a cursor open on an SQL table btree (with integer
+** keys). If register P3 does not contain an integer or if P1 does not
+** contain a record with rowid P3 then jump immediately to P2.
+** Or, if P2 is 0, raise an SQLITE_CORRUPT error. If P1 does contain
+** a record with rowid P3 then
+** leave the cursor pointing at that record and fall through to the next
+** instruction.
**
-** The P3 register contains an integer record number. Call this record
-** number R. Register P4 is the first in a set of N contiguous registers
-** that make up an unpacked index key that can be used with cursor P1.
-** The value of N can be inferred from the cursor. N includes the rowid
-** value appended to the end of the index record. This rowid value may
-** or may not be the same as R.
+** The OP_NotExists opcode performs the same operation, but with OP_NotExists
+** the P3 register must be guaranteed to contain an integer value. With this
+** opcode, register P3 might not contain an integer.
**
-** If any of the N registers beginning with register P4 contains a NULL
-** value, jump immediately to P2.
+** The OP_NotFound opcode performs the same operation on index btrees
+** (with arbitrary multi-value keys).
**
-** Otherwise, this instruction checks if cursor P1 contains an entry
-** where the first (N-1) fields match but the rowid value at the end
-** of the index entry is not R. If there is no such entry, control jumps
-** to instruction P2. Otherwise, the rowid of the conflicting index
-** entry is copied to register P3 and control falls through to the next
-** instruction.
+** This opcode leaves the cursor in a state where it cannot be advanced
+** in either direction. In other words, the Next and Prev opcodes will
+** not work following this opcode.
**
-** See also: NotFound, NotExists, Found
+** See also: Found, NotFound, NoConflict, SeekRowid
*/
-case OP_IsUnique: { /* jump, in3 */
-#if 0 /* local variables moved into u.bd */
- u16 ii;
- VdbeCursor *pCx;
- BtCursor *pCrsr;
- u16 nField;
- Mem *aMx;
- UnpackedRecord r; /* B-Tree index search key */
- i64 R; /* Rowid stored in register P3 */
-#endif /* local variables moved into u.bd */
-
- pIn3 = &aMem[pOp->p3];
- u.bd.aMx = &aMem[pOp->p4.i];
- /* Assert that the values of parameters P1 and P4 are in range. */
- assert( pOp->p4type==P4_INT32 );
- assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem );
- assert( pOp->p1>=0 && pOp->p1<p->nCursor );
-
- /* Find the index cursor. */
- u.bd.pCx = p->apCsr[pOp->p1];
- assert( u.bd.pCx->deferredMoveto==0 );
- u.bd.pCx->seekResult = 0;
- u.bd.pCx->cacheStatus = CACHE_STALE;
- u.bd.pCrsr = u.bd.pCx->pCursor;
-
- /* If any of the values are NULL, take the jump. */
- u.bd.nField = u.bd.pCx->pKeyInfo->nField;
- for(u.bd.ii=0; u.bd.ii<u.bd.nField; u.bd.ii++){
- if( u.bd.aMx[u.bd.ii].flags & MEM_Null ){
- pc = pOp->p2 - 1;
- u.bd.pCrsr = 0;
- break;
- }
- }
- assert( (u.bd.aMx[u.bd.nField].flags & MEM_Null)==0 );
-
- if( u.bd.pCrsr!=0 ){
- /* Populate the index search key. */
- u.bd.r.pKeyInfo = u.bd.pCx->pKeyInfo;
- u.bd.r.nField = u.bd.nField + 1;
- u.bd.r.flags = UNPACKED_PREFIX_SEARCH;
- u.bd.r.aMem = u.bd.aMx;
-#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.bd.r.nField; i++) assert( memIsValid(&u.bd.r.aMem[i]) ); }
-#endif
-
- /* Extract the value of u.bd.R from register P3. */
- sqlite3VdbeMemIntegerify(pIn3);
- u.bd.R = pIn3->u.i;
-
- /* Search the B-Tree index. If no conflicting record is found, jump
- ** to P2. Otherwise, copy the rowid of the conflicting record to
- ** register P3 and fall through to the next instruction. */
- rc = sqlite3BtreeMovetoUnpacked(u.bd.pCrsr, &u.bd.r, 0, 0, &u.bd.pCx->seekResult);
- if( (u.bd.r.flags & UNPACKED_PREFIX_SEARCH) || u.bd.r.rowid==u.bd.R ){
- pc = pOp->p2 - 1;
- }else{
- pIn3->u.i = u.bd.r.rowid;
- }
- }
- break;
-}
-
/* Opcode: NotExists P1 P2 P3 * *
+** Synopsis: intkey=r[P3]
+**
+** P1 is the index of a cursor open on an SQL table btree (with integer
+** keys). P3 is an integer rowid. If P1 does not contain a record with
+** rowid P3 then jump immediately to P2. Or, if P2 is 0, raise an
+** SQLITE_CORRUPT error. If P1 does contain a record with rowid P3 then
+** leave the cursor pointing at that record and fall through to the next
+** instruction.
**
-** Use the content of register P3 as an integer key. If a record
-** with that key does not exist in table of P1, then jump to P2.
-** If the record does exist, then fall through. The cursor is left
-** pointing to the record if it exists.
+** The OP_SeekRowid opcode performs the same operation but also allows the
+** P3 register to contain a non-integer value, in which case the jump is
+** always taken. This opcode requires that P3 always contain an integer.
**
-** The difference between this operation and NotFound is that this
-** operation assumes the key is an integer and that P1 is a table whereas
-** NotFound assumes key is a blob constructed from MakeRecord and
-** P1 is an index.
+** The OP_NotFound opcode performs the same operation on index btrees
+** (with arbitrary multi-value keys).
**
-** See also: Found, NotFound, IsUnique
+** This opcode leaves the cursor in a state where it cannot be advanced
+** in either direction. In other words, the Next and Prev opcodes will
+** not work following this opcode.
+**
+** See also: Found, NotFound, NoConflict, SeekRowid
*/
-case OP_NotExists: { /* jump, in3 */
-#if 0 /* local variables moved into u.be */
+case OP_SeekRowid: { /* jump, in3 */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
u64 iKey;
-#endif /* local variables moved into u.be */
pIn3 = &aMem[pOp->p3];
+ if( (pIn3->flags & MEM_Int)==0 ){
+ applyAffinity(pIn3, SQLITE_AFF_NUMERIC, encoding);
+ if( (pIn3->flags & MEM_Int)==0 ) goto jump_to_p2;
+ }
+ /* Fall through into OP_NotExists */
+case OP_NotExists: /* jump, in3 */
+ pIn3 = &aMem[pOp->p3];
assert( pIn3->flags & MEM_Int );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.be.pC = p->apCsr[pOp->p1];
- assert( u.be.pC!=0 );
- assert( u.be.pC->isTable );
- assert( u.be.pC->pseudoTableReg==0 );
- u.be.pCrsr = u.be.pC->pCursor;
- if( ALWAYS(u.be.pCrsr!=0) ){
- u.be.res = 0;
- u.be.iKey = pIn3->u.i;
- rc = sqlite3BtreeMovetoUnpacked(u.be.pCrsr, 0, u.be.iKey, 0, &u.be.res);
- u.be.pC->lastRowid = pIn3->u.i;
- u.be.pC->rowidIsValid = u.be.res==0 ?1:0;
- u.be.pC->nullRow = 0;
- u.be.pC->cacheStatus = CACHE_STALE;
- u.be.pC->deferredMoveto = 0;
- if( u.be.res!=0 ){
- pc = pOp->p2 - 1;
- assert( u.be.pC->rowidIsValid==0 );
- }
- u.be.pC->seekResult = u.be.res;
- }else{
- /* This happens when an attempt to open a read cursor on the
- ** sqlite_master table returns SQLITE_EMPTY.
- */
- pc = pOp->p2 - 1;
- assert( u.be.pC->rowidIsValid==0 );
- u.be.pC->seekResult = 0;
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+#ifdef SQLITE_DEBUG
+ pC->seekOp = 0;
+#endif
+ assert( pC->isTable );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
+ assert( pCrsr!=0 );
+ res = 0;
+ iKey = pIn3->u.i;
+ rc = sqlite3BtreeMovetoUnpacked(pCrsr, 0, iKey, 0, &res);
+ assert( rc==SQLITE_OK || res==0 );
+ pC->movetoTarget = iKey; /* Used by OP_Delete */
+ pC->nullRow = 0;
+ pC->cacheStatus = CACHE_STALE;
+ pC->deferredMoveto = 0;
+ VdbeBranchTaken(res!=0,2);
+ pC->seekResult = res;
+ if( res!=0 ){
+ assert( rc==SQLITE_OK );
+ if( pOp->p2==0 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }else{
+ goto jump_to_p2;
+ }
}
+ if( rc ) goto abort_due_to_error;
break;
}
/* Opcode: Sequence P1 P2 * * *
+** Synopsis: r[P2]=cursor[P1].ctr++
**
** Find the next available sequence number for cursor P1.
** Write the sequence number into register P2.
** The sequence number on the cursor is incremented after this
** instruction.
*/
-case OP_Sequence: { /* out2-prerelease */
+case OP_Sequence: { /* out2 */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
assert( p->apCsr[pOp->p1]!=0 );
+ assert( p->apCsr[pOp->p1]->eCurType!=CURTYPE_VTAB );
+ pOut = out2Prerelease(p, pOp);
pOut->u.i = p->apCsr[pOp->p1]->seqCount++;
break;
}
/* Opcode: NewRowid P1 P2 P3 * *
+** Synopsis: r[P2]=rowid
**
** Get a new integer record number (a.k.a "rowid") used as the key to a table.
** The record number is not previously used as a key in the database
@@ -67349,24 +81497,23 @@ case OP_Sequence: { /* out2-prerelease */
** generated record number. This P3 mechanism is used to help implement the
** AUTOINCREMENT feature.
*/
-case OP_NewRowid: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bf */
+case OP_NewRowid: { /* out2 */
i64 v; /* The new rowid */
VdbeCursor *pC; /* Cursor of table to get the new rowid */
int res; /* Result of an sqlite3BtreeLast() */
int cnt; /* Counter to limit the number of searches */
Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */
VdbeFrame *pFrame; /* Root frame of VDBE */
-#endif /* local variables moved into u.bf */
- u.bf.v = 0;
- u.bf.res = 0;
+ v = 0;
+ res = 0;
+ pOut = out2Prerelease(p, pOp);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bf.pC = p->apCsr[pOp->p1];
- assert( u.bf.pC!=0 );
- if( NEVER(u.bf.pC->pCursor==0) ){
- /* The zero initialization above is all that is needed */
- }else{
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
+ {
/* The next rowid or record number (different terms for the same
** thing) is obtained in a two-step algorithm.
**
@@ -67380,7 +81527,7 @@ case OP_NewRowid: { /* out2-prerelease */
** succeeded. If the random rowid does exist, we select a new one
** and try again, up to 100 times.
*/
- assert( u.bf.pC->isTable );
+ assert( pC->isTable );
#ifdef SQLITE_32BIT_ROWID
# define MAX_ROWID 0x7fffffff
@@ -67392,101 +81539,85 @@ case OP_NewRowid: { /* out2-prerelease */
# define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff )
#endif
- if( !u.bf.pC->useRandomRowid ){
- u.bf.v = sqlite3BtreeGetCachedRowid(u.bf.pC->pCursor);
- if( u.bf.v==0 ){
- rc = sqlite3BtreeLast(u.bf.pC->pCursor, &u.bf.res);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- if( u.bf.res ){
- u.bf.v = 1; /* IMP: R-61914-48074 */
+ if( !pC->useRandomRowid ){
+ rc = sqlite3BtreeLast(pC->uc.pCursor, &res);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( res ){
+ v = 1; /* IMP: R-61914-48074 */
+ }else{
+ assert( sqlite3BtreeCursorIsValid(pC->uc.pCursor) );
+ v = sqlite3BtreeIntegerKey(pC->uc.pCursor);
+ if( v>=MAX_ROWID ){
+ pC->useRandomRowid = 1;
}else{
- assert( sqlite3BtreeCursorIsValid(u.bf.pC->pCursor) );
- rc = sqlite3BtreeKeySize(u.bf.pC->pCursor, &u.bf.v);
- assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */
- if( u.bf.v==MAX_ROWID ){
- u.bf.pC->useRandomRowid = 1;
- }else{
- u.bf.v++; /* IMP: R-29538-34987 */
- }
+ v++; /* IMP: R-29538-34987 */
}
}
+ }
#ifndef SQLITE_OMIT_AUTOINCREMENT
- if( pOp->p3 ){
+ if( pOp->p3 ){
+ /* Assert that P3 is a valid memory cell. */
+ assert( pOp->p3>0 );
+ if( p->pFrame ){
+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
/* Assert that P3 is a valid memory cell. */
- assert( pOp->p3>0 );
- if( p->pFrame ){
- for(u.bf.pFrame=p->pFrame; u.bf.pFrame->pParent; u.bf.pFrame=u.bf.pFrame->pParent);
- /* Assert that P3 is a valid memory cell. */
- assert( pOp->p3<=u.bf.pFrame->nMem );
- u.bf.pMem = &u.bf.pFrame->aMem[pOp->p3];
- }else{
- /* Assert that P3 is a valid memory cell. */
- assert( pOp->p3<=p->nMem );
- u.bf.pMem = &aMem[pOp->p3];
- memAboutToChange(p, u.bf.pMem);
- }
- assert( memIsValid(u.bf.pMem) );
-
- REGISTER_TRACE(pOp->p3, u.bf.pMem);
- sqlite3VdbeMemIntegerify(u.bf.pMem);
- assert( (u.bf.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
- if( u.bf.pMem->u.i==MAX_ROWID || u.bf.pC->useRandomRowid ){
- rc = SQLITE_FULL; /* IMP: R-12275-61338 */
- goto abort_due_to_error;
- }
- if( u.bf.v<u.bf.pMem->u.i+1 ){
- u.bf.v = u.bf.pMem->u.i + 1;
- }
- u.bf.pMem->u.i = u.bf.v;
+ assert( pOp->p3<=pFrame->nMem );
+ pMem = &pFrame->aMem[pOp->p3];
+ }else{
+ /* Assert that P3 is a valid memory cell. */
+ assert( pOp->p3<=(p->nMem+1 - p->nCursor) );
+ pMem = &aMem[pOp->p3];
+ memAboutToChange(p, pMem);
}
-#endif
+ assert( memIsValid(pMem) );
- sqlite3BtreeSetCachedRowid(u.bf.pC->pCursor, u.bf.v<MAX_ROWID ? u.bf.v+1 : 0);
+ REGISTER_TRACE(pOp->p3, pMem);
+ sqlite3VdbeMemIntegerify(pMem);
+ assert( (pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */
+ if( pMem->u.i==MAX_ROWID || pC->useRandomRowid ){
+ rc = SQLITE_FULL; /* IMP: R-12275-61338 */
+ goto abort_due_to_error;
+ }
+ if( v<pMem->u.i+1 ){
+ v = pMem->u.i + 1;
+ }
+ pMem->u.i = v;
}
- if( u.bf.pC->useRandomRowid ){
+#endif
+ if( pC->useRandomRowid ){
/* IMPLEMENTATION-OF: R-07677-41881 If the largest ROWID is equal to the
** largest possible integer (9223372036854775807) then the database
** engine starts picking positive candidate ROWIDs at random until
** it finds one that is not previously used. */
assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is
** an AUTOINCREMENT table. */
- /* on the first attempt, simply do one more than previous */
- u.bf.v = lastRowid;
- u.bf.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
- u.bf.v++; /* ensure non-zero */
- u.bf.cnt = 0;
- while( ((rc = sqlite3BtreeMovetoUnpacked(u.bf.pC->pCursor, 0, (u64)u.bf.v,
- 0, &u.bf.res))==SQLITE_OK)
- && (u.bf.res==0)
- && (++u.bf.cnt<100)){
- /* collision - try another random rowid */
- sqlite3_randomness(sizeof(u.bf.v), &u.bf.v);
- if( u.bf.cnt<5 ){
- /* try "small" random rowids for the initial attempts */
- u.bf.v &= 0xffffff;
- }else{
- u.bf.v &= (MAX_ROWID>>1); /* ensure doesn't go negative */
- }
- u.bf.v++; /* ensure non-zero */
- }
- if( rc==SQLITE_OK && u.bf.res==0 ){
+ cnt = 0;
+ do{
+ sqlite3_randomness(sizeof(v), &v);
+ v &= (MAX_ROWID>>1); v++; /* Ensure that v is greater than zero */
+ }while( ((rc = sqlite3BtreeMovetoUnpacked(pC->uc.pCursor, 0, (u64)v,
+ 0, &res))==SQLITE_OK)
+ && (res==0)
+ && (++cnt<100));
+ if( rc ) goto abort_due_to_error;
+ if( res==0 ){
rc = SQLITE_FULL; /* IMP: R-38219-53002 */
goto abort_due_to_error;
}
- assert( u.bf.v>0 ); /* EV: R-40812-03570 */
+ assert( v>0 ); /* EV: R-40812-03570 */
}
- u.bf.pC->rowidIsValid = 0;
- u.bf.pC->deferredMoveto = 0;
- u.bf.pC->cacheStatus = CACHE_STALE;
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
}
- pOut->u.i = u.bf.v;
+ pOut->u.i = v;
break;
}
/* Opcode: Insert P1 P2 P3 P4 P5
+** Synopsis: intkey=r[P3] data=r[P2]
**
** Write an entry into the table of cursor P1. A new entry is
** created if it doesn't already exist or the data for an existing
@@ -67500,10 +81631,12 @@ case OP_NewRowid: { /* out2-prerelease */
** sqlite3_last_insert_rowid() function (otherwise it is unmodified).
**
** If the OPFLAG_USESEEKRESULT flag of P5 is set and if the result of
-** the last seek operation (OP_NotExists) was a success, then this
+** the last seek operation (OP_NotExists or OP_SeekRowid) was a success,
+** then this
** operation will not attempt to find the appropriate row before doing
** the insert but will instead overwrite the row that the cursor is
-** currently pointing to. Presumably, the prior OP_NotExists opcode
+** currently pointing to. Presumably, the prior OP_NotExists or
+** OP_SeekRowid opcode
** has already positioned the cursor correctly. This is an optimization
** that boosts performance by avoiding redundant seeks.
**
@@ -67512,9 +81645,9 @@ case OP_NewRowid: { /* out2-prerelease */
** is part of an INSERT operation. The difference is only important to
** the update hook.
**
-** Parameter P4 may point to a string containing the table-name, or
-** may be NULL. If it is not NULL, then the update-hook
-** (sqlite3.xUpdateCallback) is invoked following a successful insert.
+** Parameter P4 may point to a Table structure, or may be NULL. If it is
+** not NULL, then the update-hook (sqlite3.xUpdateCallback) is invoked
+** following a successful insert.
**
** (WARNING/TODO: If P1 is a pseudo-cursor and P2 is dynamically
** allocated, then ownership of P2 is transferred to the pseudo-cursor
@@ -67526,144 +81659,221 @@ case OP_NewRowid: { /* out2-prerelease */
** for indices is OP_IdxInsert.
*/
/* Opcode: InsertInt P1 P2 P3 P4 P5
+** Synopsis: intkey=P3 data=r[P2]
**
** This works exactly like OP_Insert except that the key is the
** integer value P3, not the value of the integer stored in register P3.
*/
case OP_Insert:
case OP_InsertInt: {
-#if 0 /* local variables moved into u.bg */
Mem *pData; /* MEM cell holding data for the record to be inserted */
Mem *pKey; /* MEM cell holding key for the record */
- i64 iKey; /* The integer ROWID or key for the record to be inserted */
VdbeCursor *pC; /* Cursor to table into which insert is written */
- int nZero; /* Number of zero-bytes to append */
int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */
const char *zDb; /* database name - used by the update hook */
- const char *zTbl; /* Table name - used by the opdate hook */
+ Table *pTab; /* Table structure - used by update and pre-update hooks */
int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */
-#endif /* local variables moved into u.bg */
+ BtreePayload x; /* Payload to be inserted */
- u.bg.pData = &aMem[pOp->p2];
+ op = 0;
+ pData = &aMem[pOp->p2];
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( memIsValid(u.bg.pData) );
- u.bg.pC = p->apCsr[pOp->p1];
- assert( u.bg.pC!=0 );
- assert( u.bg.pC->pCursor!=0 );
- assert( u.bg.pC->pseudoTableReg==0 );
- assert( u.bg.pC->isTable );
- REGISTER_TRACE(pOp->p2, u.bg.pData);
+ assert( memIsValid(pData) );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
+ assert( pC->isTable );
+ assert( pOp->p4type==P4_TABLE || pOp->p4type>=P4_STATIC );
+ REGISTER_TRACE(pOp->p2, pData);
if( pOp->opcode==OP_Insert ){
- u.bg.pKey = &aMem[pOp->p3];
- assert( u.bg.pKey->flags & MEM_Int );
- assert( memIsValid(u.bg.pKey) );
- REGISTER_TRACE(pOp->p3, u.bg.pKey);
- u.bg.iKey = u.bg.pKey->u.i;
+ pKey = &aMem[pOp->p3];
+ assert( pKey->flags & MEM_Int );
+ assert( memIsValid(pKey) );
+ REGISTER_TRACE(pOp->p3, pKey);
+ x.nKey = pKey->u.i;
}else{
assert( pOp->opcode==OP_InsertInt );
- u.bg.iKey = pOp->p3;
+ x.nKey = pOp->p3;
}
+ if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
+ assert( pC->isTable );
+ assert( pC->iDb>=0 );
+ zDb = db->aDb[pC->iDb].zDbSName;
+ pTab = pOp->p4.pTab;
+ assert( HasRowid(pTab) );
+ op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
+ }else{
+ pTab = 0; /* Not needed. Silence a comiler warning. */
+ zDb = 0; /* Not needed. Silence a compiler warning. */
+ }
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ /* Invoke the pre-update hook, if any */
+ if( db->xPreUpdateCallback
+ && pOp->p4type==P4_TABLE
+ && !(pOp->p5 & OPFLAG_ISUPDATE)
+ ){
+ sqlite3VdbePreUpdateHook(p, pC, SQLITE_INSERT, zDb, pTab, x.nKey, pOp->p2);
+ }
+#endif
+
if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
- if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = u.bg.iKey;
- if( u.bg.pData->flags & MEM_Null ){
- u.bg.pData->z = 0;
- u.bg.pData->n = 0;
+ if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = lastRowid = x.nKey;
+ if( pData->flags & MEM_Null ){
+ x.pData = 0;
+ x.nData = 0;
}else{
- assert( u.bg.pData->flags & (MEM_Blob|MEM_Str) );
+ assert( pData->flags & (MEM_Blob|MEM_Str) );
+ x.pData = pData->z;
+ x.nData = pData->n;
}
- u.bg.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bg.pC->seekResult : 0);
- if( u.bg.pData->flags & MEM_Zero ){
- u.bg.nZero = u.bg.pData->u.nZero;
+ seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0);
+ if( pData->flags & MEM_Zero ){
+ x.nZero = pData->u.nZero;
}else{
- u.bg.nZero = 0;
+ x.nZero = 0;
}
- sqlite3BtreeSetCachedRowid(u.bg.pC->pCursor, 0);
- rc = sqlite3BtreeInsert(u.bg.pC->pCursor, 0, u.bg.iKey,
- u.bg.pData->z, u.bg.pData->n, u.bg.nZero,
- pOp->p5 & OPFLAG_APPEND, u.bg.seekResult
+ x.pKey = 0;
+ rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
+ (pOp->p5 & OPFLAG_APPEND)!=0, seekResult
);
- u.bg.pC->rowidIsValid = 0;
- u.bg.pC->deferredMoveto = 0;
- u.bg.pC->cacheStatus = CACHE_STALE;
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
/* Invoke the update-hook if required. */
- if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){
- u.bg.zDb = db->aDb[u.bg.pC->iDb].zName;
- u.bg.zTbl = pOp->p4.z;
- u.bg.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT);
- assert( u.bg.pC->isTable );
- db->xUpdateCallback(db->pUpdateArg, u.bg.op, u.bg.zDb, u.bg.zTbl, u.bg.iKey);
- assert( u.bg.pC->iDb>=0 );
+ if( rc ) goto abort_due_to_error;
+ if( db->xUpdateCallback && op ){
+ db->xUpdateCallback(db->pUpdateArg, op, zDb, pTab->zName, x.nKey);
}
break;
}
-/* Opcode: Delete P1 P2 * P4 *
+/* Opcode: Delete P1 P2 P3 P4 P5
**
** Delete the record at which the P1 cursor is currently pointing.
**
-** The cursor will be left pointing at either the next or the previous
+** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then
+** the cursor will be left pointing at either the next or the previous
** record in the table. If it is left pointing at the next record, then
-** the next Next instruction will be a no-op. Hence it is OK to delete
-** a record from within an Next loop.
+** the next Next instruction will be a no-op. As a result, in this case
+** it is ok to delete a record from within a Next loop. If
+** OPFLAG_SAVEPOSITION bit of P5 is clear, then the cursor will be
+** left in an undefined state.
+**
+** If the OPFLAG_AUXDELETE bit is set on P5, that indicates that this
+** delete one of several associated with deleting a table row and all its
+** associated index entries. Exactly one of those deletes is the "primary"
+** delete. The others are all on OPFLAG_FORDELETE cursors or else are
+** marked with the AUXDELETE flag.
**
-** If the OPFLAG_NCHANGE flag of P2 is set, then the row change count is
-** incremented (otherwise not).
+** If the OPFLAG_NCHANGE flag of P2 (NB: P2 not P5) is set, then the row
+** change count is incremented (otherwise not).
**
** P1 must not be pseudo-table. It has to be a real table with
** multiple rows.
**
-** If P4 is not NULL, then it is the name of the table that P1 is
-** pointing to. The update hook will be invoked, if it exists.
-** If P4 is not NULL then the P1 cursor must have been positioned
-** using OP_NotFound prior to invoking this opcode.
+** If P4 is not NULL then it points to a Table object. In this case either
+** the update or pre-update hook, or both, may be invoked. The P1 cursor must
+** have been positioned using OP_NotFound prior to invoking this opcode in
+** this case. Specifically, if one is configured, the pre-update hook is
+** invoked if P4 is not NULL. The update-hook is invoked if one is configured,
+** P4 is not NULL, and the OPFLAG_NCHANGE flag is set in P2.
+**
+** If the OPFLAG_ISUPDATE flag is set in P2, then P3 contains the address
+** of the memory cell that contains the value that the rowid of the row will
+** be set to by the update.
*/
case OP_Delete: {
-#if 0 /* local variables moved into u.bh */
- i64 iKey;
VdbeCursor *pC;
-#endif /* local variables moved into u.bh */
+ const char *zDb;
+ Table *pTab;
+ int opflags;
- u.bh.iKey = 0;
+ opflags = pOp->p2;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bh.pC = p->apCsr[pOp->p1];
- assert( u.bh.pC!=0 );
- assert( u.bh.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
+ assert( pC->deferredMoveto==0 );
- /* If the update-hook will be invoked, set u.bh.iKey to the rowid of the
- ** row being deleted.
- */
- if( db->xUpdateCallback && pOp->p4.z ){
- assert( u.bh.pC->isTable );
- assert( u.bh.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */
- u.bh.iKey = u.bh.pC->lastRowid;
+#ifdef SQLITE_DEBUG
+ if( pOp->p4type==P4_TABLE && HasRowid(pOp->p4.pTab) && pOp->p5==0 ){
+ /* If p5 is zero, the seek operation that positioned the cursor prior to
+ ** OP_Delete will have also set the pC->movetoTarget field to the rowid of
+ ** the row that is being deleted */
+ i64 iKey = sqlite3BtreeIntegerKey(pC->uc.pCursor);
+ assert( pC->movetoTarget==iKey );
+ }
+#endif
+
+ /* If the update-hook or pre-update-hook will be invoked, set zDb to
+ ** the name of the db to pass as to it. Also set local pTab to a copy
+ ** of p4.pTab. Finally, if p5 is true, indicating that this cursor was
+ ** last moved with OP_Next or OP_Prev, not Seek or NotFound, set
+ ** VdbeCursor.movetoTarget to the current rowid. */
+ if( pOp->p4type==P4_TABLE && HAS_UPDATE_HOOK(db) ){
+ assert( pC->iDb>=0 );
+ assert( pOp->p4.pTab!=0 );
+ zDb = db->aDb[pC->iDb].zDbSName;
+ pTab = pOp->p4.pTab;
+ if( (pOp->p5 & OPFLAG_SAVEPOSITION)!=0 && pC->isTable ){
+ pC->movetoTarget = sqlite3BtreeIntegerKey(pC->uc.pCursor);
+ }
+ }else{
+ zDb = 0; /* Not needed. Silence a compiler warning. */
+ pTab = 0; /* Not needed. Silence a compiler warning. */
+ }
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ /* Invoke the pre-update-hook if required. */
+ if( db->xPreUpdateCallback && pOp->p4.pTab && HasRowid(pTab) ){
+ assert( !(opflags & OPFLAG_ISUPDATE) || (aMem[pOp->p3].flags & MEM_Int) );
+ sqlite3VdbePreUpdateHook(p, pC,
+ (opflags & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_DELETE,
+ zDb, pTab, pC->movetoTarget,
+ pOp->p3
+ );
}
+ if( opflags & OPFLAG_ISNOOP ) break;
+#endif
+
+ /* Only flags that can be set are SAVEPOISTION and AUXDELETE */
+ assert( (pOp->p5 & ~(OPFLAG_SAVEPOSITION|OPFLAG_AUXDELETE))==0 );
+ assert( OPFLAG_SAVEPOSITION==BTREE_SAVEPOSITION );
+ assert( OPFLAG_AUXDELETE==BTREE_AUXDELETE );
- /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or
- ** OP_Column on the same table without any intervening operations that
- ** might move or invalidate the cursor. Hence cursor u.bh.pC is always pointing
- ** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation
- ** below is always a no-op and cannot fail. We will run it anyhow, though,
- ** to guard against future changes to the code generator.
- **/
- assert( u.bh.pC->deferredMoveto==0 );
- rc = sqlite3VdbeCursorMoveto(u.bh.pC);
- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
+#ifdef SQLITE_DEBUG
+ if( p->pFrame==0 ){
+ if( pC->isEphemeral==0
+ && (pOp->p5 & OPFLAG_AUXDELETE)==0
+ && (pC->wrFlag & OPFLAG_FORDELETE)==0
+ ){
+ nExtraDelete++;
+ }
+ if( pOp->p2 & OPFLAG_NCHANGE ){
+ nExtraDelete--;
+ }
+ }
+#endif
- sqlite3BtreeSetCachedRowid(u.bh.pC->pCursor, 0);
- rc = sqlite3BtreeDelete(u.bh.pC->pCursor);
- u.bh.pC->cacheStatus = CACHE_STALE;
+ rc = sqlite3BtreeDelete(pC->uc.pCursor, pOp->p5);
+ pC->cacheStatus = CACHE_STALE;
+ if( rc ) goto abort_due_to_error;
/* Invoke the update-hook if required. */
- if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){
- const char *zDb = db->aDb[u.bh.pC->iDb].zName;
- const char *zTbl = pOp->p4.z;
- db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bh.iKey);
- assert( u.bh.pC->iDb>=0 );
+ if( opflags & OPFLAG_NCHANGE ){
+ p->nChange++;
+ if( db->xUpdateCallback && HasRowid(pTab) ){
+ db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, pTab->zName,
+ pC->movetoTarget);
+ assert( pC->iDb>=0 );
+ }
}
- if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++;
+
break;
}
/* Opcode: ResetCount * * * * *
@@ -67679,50 +81889,67 @@ case OP_ResetCount: {
break;
}
-/* Opcode: SorterCompare P1 P2 P3
+/* Opcode: SorterCompare P1 P2 P3 P4
+** Synopsis: if key(P1)!=trim(r[P3],P4) goto P2
+**
+** P1 is a sorter cursor. This instruction compares a prefix of the
+** record blob in register P3 against a prefix of the entry that
+** the sorter cursor currently points to. Only the first P4 fields
+** of r[P3] and the sorter record are compared.
**
-** P1 is a sorter cursor. This instruction compares the record blob in
-** register P3 with the entry that the sorter cursor currently points to.
-** If, excluding the rowid fields at the end, the two records are a match,
-** fall through to the next instruction. Otherwise, jump to instruction P2.
+** If either P3 or the sorter contains a NULL in one of their significant
+** fields (not counting the P4 fields at the end which are ignored) then
+** the comparison is assumed to be equal.
+**
+** Fall through to next instruction if the two records compare equal to
+** each other. Jump to P2 if they are different.
*/
case OP_SorterCompare: {
-#if 0 /* local variables moved into u.bi */
VdbeCursor *pC;
int res;
-#endif /* local variables moved into u.bi */
+ int nKeyCol;
- u.bi.pC = p->apCsr[pOp->p1];
- assert( isSorter(u.bi.pC) );
+ pC = p->apCsr[pOp->p1];
+ assert( isSorter(pC) );
+ assert( pOp->p4type==P4_INT32 );
pIn3 = &aMem[pOp->p3];
- rc = sqlite3VdbeSorterCompare(u.bi.pC, pIn3, &u.bi.res);
- if( u.bi.res ){
- pc = pOp->p2-1;
- }
+ nKeyCol = pOp->p4.i;
+ res = 0;
+ rc = sqlite3VdbeSorterCompare(pC, pIn3, nKeyCol, &res);
+ VdbeBranchTaken(res!=0,2);
+ if( rc ) goto abort_due_to_error;
+ if( res ) goto jump_to_p2;
break;
};
-/* Opcode: SorterData P1 P2 * * *
+/* Opcode: SorterData P1 P2 P3 * *
+** Synopsis: r[P2]=data
**
** Write into register P2 the current sorter data for sorter cursor P1.
+** Then clear the column header cache on cursor P3.
+**
+** This opcode is normally use to move a record out of the sorter and into
+** a register that is the source for a pseudo-table cursor created using
+** OpenPseudo. That pseudo-table cursor is the one that is identified by
+** parameter P3. Clearing the P3 column cache as part of this opcode saves
+** us from having to issue a separate NullRow instruction to clear that cache.
*/
case OP_SorterData: {
-#if 0 /* local variables moved into u.bj */
VdbeCursor *pC;
-#endif /* local variables moved into u.bj */
-#ifndef SQLITE_OMIT_MERGE_SORT
+
pOut = &aMem[pOp->p2];
- u.bj.pC = p->apCsr[pOp->p1];
- assert( u.bj.pC->isSorter );
- rc = sqlite3VdbeSorterRowkey(u.bj.pC, pOut);
-#else
- pOp->opcode = OP_RowKey;
- pc--;
-#endif
+ pC = p->apCsr[pOp->p1];
+ assert( isSorter(pC) );
+ rc = sqlite3VdbeSorterRowkey(pC, pOut);
+ assert( rc!=SQLITE_OK || (pOut->flags & MEM_Blob) );
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ if( rc ) goto abort_due_to_error;
+ p->apCsr[pOp->p3]->cacheStatus = CACHE_STALE;
break;
}
/* Opcode: RowData P1 P2 * * *
+** Synopsis: r[P2]=data
**
** Write into register P2 the complete row data for cursor P1.
** There is no interpretation of the data.
@@ -67733,10 +81960,11 @@ case OP_SorterData: {
** of a real table, not a pseudo-table.
*/
/* Opcode: RowKey P1 P2 * * *
+** Synopsis: r[P2]=key
**
** Write into register P2 the complete row key for cursor P1.
** There is no interpretation of the data.
-** The key is copied onto the P3 register exactly as
+** The key is copied onto the P2 register exactly as
** it is found in the database file.
**
** If the P1 cursor must be pointing to a valid row (not a NULL row)
@@ -67744,70 +81972,64 @@ case OP_SorterData: {
*/
case OP_RowKey:
case OP_RowData: {
-#if 0 /* local variables moved into u.bk */
VdbeCursor *pC;
BtCursor *pCrsr;
u32 n;
- i64 n64;
-#endif /* local variables moved into u.bk */
pOut = &aMem[pOp->p2];
memAboutToChange(p, pOut);
/* Note that RowKey and RowData are really exactly the same instruction */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bk.pC = p->apCsr[pOp->p1];
- assert( u.bk.pC->isSorter==0 );
- assert( u.bk.pC->isTable || pOp->opcode!=OP_RowData );
- assert( u.bk.pC->isIndex || pOp->opcode==OP_RowData );
- assert( u.bk.pC!=0 );
- assert( u.bk.pC->nullRow==0 );
- assert( u.bk.pC->pseudoTableReg==0 );
- assert( !u.bk.pC->isSorter );
- assert( u.bk.pC->pCursor!=0 );
- u.bk.pCrsr = u.bk.pC->pCursor;
- assert( sqlite3BtreeCursorIsValid(u.bk.pCrsr) );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( isSorter(pC)==0 );
+ assert( pC->isTable || pOp->opcode!=OP_RowData );
+ assert( pC->isTable==0 || pOp->opcode==OP_RowData );
+ assert( pC->nullRow==0 );
+ assert( pC->uc.pCursor!=0 );
+ pCrsr = pC->uc.pCursor;
/* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or
- ** OP_Rewind/Op_Next with no intervening instructions that might invalidate
- ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always
- ** a no-op and can never fail. But we leave it in place as a safety.
- */
- assert( u.bk.pC->deferredMoveto==0 );
- rc = sqlite3VdbeCursorMoveto(u.bk.pC);
- if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
-
- if( u.bk.pC->isIndex ){
- assert( !u.bk.pC->isTable );
- rc = sqlite3BtreeKeySize(u.bk.pCrsr, &u.bk.n64);
- assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
- if( u.bk.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
- u.bk.n = (u32)u.bk.n64;
- }else{
- rc = sqlite3BtreeDataSize(u.bk.pCrsr, &u.bk.n);
- assert( rc==SQLITE_OK ); /* DataSize() cannot fail */
- if( u.bk.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
- goto too_big;
- }
+ ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
+ ** that might invalidate the cursor.
+ ** If this where not the case, on of the following assert()s
+ ** would fail. Should this ever change (because of changes in the code
+ ** generator) then the fix would be to insert a call to
+ ** sqlite3VdbeCursorMoveto().
+ */
+ assert( pC->deferredMoveto==0 );
+ assert( sqlite3BtreeCursorIsValid(pCrsr) );
+#if 0 /* Not required due to the previous to assert() statements */
+ rc = sqlite3VdbeCursorMoveto(pC);
+ if( rc!=SQLITE_OK ) goto abort_due_to_error;
+#endif
+
+ n = sqlite3BtreePayloadSize(pCrsr);
+ if( n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){
+ goto too_big;
}
- if( sqlite3VdbeMemGrow(pOut, u.bk.n, 0) ){
+ testcase( n==0 );
+ if( sqlite3VdbeMemClearAndResize(pOut, MAX(n,32)) ){
goto no_mem;
}
- pOut->n = u.bk.n;
+ pOut->n = n;
MemSetTypeFlag(pOut, MEM_Blob);
- if( u.bk.pC->isIndex ){
- rc = sqlite3BtreeKey(u.bk.pCrsr, 0, u.bk.n, pOut->z);
+ if( pC->isTable==0 ){
+ rc = sqlite3BtreeKey(pCrsr, 0, n, pOut->z);
}else{
- rc = sqlite3BtreeData(u.bk.pCrsr, 0, u.bk.n, pOut->z);
+ rc = sqlite3BtreeData(pCrsr, 0, n, pOut->z);
}
+ if( rc ) goto abort_due_to_error;
pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */
UPDATE_MAX_BLOBSIZE(pOut);
+ REGISTER_TRACE(pOp->p2, pOut);
break;
}
/* Opcode: Rowid P1 P2 * * *
+** Synopsis: r[P2]=rowid
**
** Store in register P2 an integer which is the key of the table entry that
** P1 is currently point to.
@@ -67816,43 +82038,44 @@ case OP_RowData: {
** be a separate OP_VRowid opcode for use with virtual tables, but this
** one opcode now works for both table types.
*/
-case OP_Rowid: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bl */
+case OP_Rowid: { /* out2 */
VdbeCursor *pC;
i64 v;
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
-#endif /* local variables moved into u.bl */
+ pOut = out2Prerelease(p, pOp);
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bl.pC = p->apCsr[pOp->p1];
- assert( u.bl.pC!=0 );
- assert( u.bl.pC->pseudoTableReg==0 );
- if( u.bl.pC->nullRow ){
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow );
+ if( pC->nullRow ){
pOut->flags = MEM_Null;
break;
- }else if( u.bl.pC->deferredMoveto ){
- u.bl.v = u.bl.pC->movetoTarget;
+ }else if( pC->deferredMoveto ){
+ v = pC->movetoTarget;
#ifndef SQLITE_OMIT_VIRTUALTABLE
- }else if( u.bl.pC->pVtabCursor ){
- u.bl.pVtab = u.bl.pC->pVtabCursor->pVtab;
- u.bl.pModule = u.bl.pVtab->pModule;
- assert( u.bl.pModule->xRowid );
- rc = u.bl.pModule->xRowid(u.bl.pC->pVtabCursor, &u.bl.v);
- importVtabErrMsg(p, u.bl.pVtab);
+ }else if( pC->eCurType==CURTYPE_VTAB ){
+ assert( pC->uc.pVCur!=0 );
+ pVtab = pC->uc.pVCur->pVtab;
+ pModule = pVtab->pModule;
+ assert( pModule->xRowid );
+ rc = pModule->xRowid(pC->uc.pVCur, &v);
+ sqlite3VtabImportErrmsg(p, pVtab);
+ if( rc ) goto abort_due_to_error;
#endif /* SQLITE_OMIT_VIRTUALTABLE */
}else{
- assert( u.bl.pC->pCursor!=0 );
- rc = sqlite3VdbeCursorMoveto(u.bl.pC);
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
+ rc = sqlite3VdbeCursorRestore(pC);
if( rc ) goto abort_due_to_error;
- if( u.bl.pC->rowidIsValid ){
- u.bl.v = u.bl.pC->lastRowid;
- }else{
- rc = sqlite3BtreeKeySize(u.bl.pC->pCursor, &u.bl.v);
- assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */
+ if( pC->nullRow ){
+ pOut->flags = MEM_Null;
+ break;
}
+ v = sqlite3BtreeIntegerKey(pC->uc.pCursor);
}
- pOut->u.i = u.bl.v;
+ pOut->u.i = v;
break;
}
@@ -67863,52 +82086,56 @@ case OP_Rowid: { /* out2-prerelease */
** write a NULL.
*/
case OP_NullRow: {
-#if 0 /* local variables moved into u.bm */
VdbeCursor *pC;
-#endif /* local variables moved into u.bm */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bm.pC = p->apCsr[pOp->p1];
- assert( u.bm.pC!=0 );
- u.bm.pC->nullRow = 1;
- u.bm.pC->rowidIsValid = 0;
- assert( u.bm.pC->pCursor || u.bm.pC->pVtabCursor );
- if( u.bm.pC->pCursor ){
- sqlite3BtreeClearCursor(u.bm.pC->pCursor);
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ pC->nullRow = 1;
+ pC->cacheStatus = CACHE_STALE;
+ if( pC->eCurType==CURTYPE_BTREE ){
+ assert( pC->uc.pCursor!=0 );
+ sqlite3BtreeClearCursor(pC->uc.pCursor);
}
break;
}
-/* Opcode: Last P1 P2 * * *
+/* Opcode: Last P1 P2 P3 * *
**
-** The next use of the Rowid or Column or Next instruction for P1
+** The next use of the Rowid or Column or Prev instruction for P1
** will refer to the last entry in the database table or index.
** If the table or index is empty and P2>0, then jump immediately to P2.
** If P2 is 0 or if the table or index is not empty, fall through
** to the following instruction.
+**
+** This opcode leaves the cursor configured to move in reverse order,
+** from the end toward the beginning. In other words, the cursor is
+** configured to use Prev, not Next.
*/
case OP_Last: { /* jump */
-#if 0 /* local variables moved into u.bn */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
-#endif /* local variables moved into u.bn */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bn.pC = p->apCsr[pOp->p1];
- assert( u.bn.pC!=0 );
- u.bn.pCrsr = u.bn.pC->pCursor;
- if( NEVER(u.bn.pCrsr==0) ){
- u.bn.res = 1;
- }else{
- rc = sqlite3BtreeLast(u.bn.pCrsr, &u.bn.res);
- }
- u.bn.pC->nullRow = (u8)u.bn.res;
- u.bn.pC->deferredMoveto = 0;
- u.bn.pC->rowidIsValid = 0;
- u.bn.pC->cacheStatus = CACHE_STALE;
- if( pOp->p2>0 && u.bn.res ){
- pc = pOp->p2 - 1;
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
+ res = 0;
+ assert( pCrsr!=0 );
+ rc = sqlite3BtreeLast(pCrsr, &res);
+ pC->nullRow = (u8)res;
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
+ pC->seekResult = pOp->p3;
+#ifdef SQLITE_DEBUG
+ pC->seekOp = OP_Last;
+#endif
+ if( rc ) goto abort_due_to_error;
+ if( pOp->p2>0 ){
+ VdbeBranchTaken(res!=0,2);
+ if( res ) goto jump_to_p2;
}
break;
}
@@ -67927,64 +82154,75 @@ case OP_Last: { /* jump */
** correctly optimizing out sorts.
*/
case OP_SorterSort: /* jump */
-#ifdef SQLITE_OMIT_MERGE_SORT
- pOp->opcode = OP_Sort;
-#endif
case OP_Sort: { /* jump */
#ifdef SQLITE_TEST
sqlite3_sort_count++;
sqlite3_search_count--;
#endif
- p->aCounter[SQLITE_STMTSTATUS_SORT-1]++;
+ p->aCounter[SQLITE_STMTSTATUS_SORT]++;
/* Fall through into OP_Rewind */
}
/* Opcode: Rewind P1 P2 * * *
**
** The next use of the Rowid or Column or Next instruction for P1
** will refer to the first entry in the database table or index.
-** If the table or index is empty and P2>0, then jump immediately to P2.
-** If P2 is 0 or if the table or index is not empty, fall through
-** to the following instruction.
+** If the table or index is empty, jump immediately to P2.
+** If the table or index is not empty, fall through to the following
+** instruction.
+**
+** This opcode leaves the cursor configured to move in forward order,
+** from the beginning toward the end. In other words, the cursor is
+** configured to use Next, not Prev.
*/
case OP_Rewind: { /* jump */
-#if 0 /* local variables moved into u.bo */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
-#endif /* local variables moved into u.bo */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bo.pC = p->apCsr[pOp->p1];
- assert( u.bo.pC!=0 );
- assert( u.bo.pC->isSorter==(pOp->opcode==OP_SorterSort) );
- u.bo.res = 1;
- if( isSorter(u.bo.pC) ){
- rc = sqlite3VdbeSorterRewind(db, u.bo.pC, &u.bo.res);
- }else{
- u.bo.pCrsr = u.bo.pC->pCursor;
- assert( u.bo.pCrsr );
- rc = sqlite3BtreeFirst(u.bo.pCrsr, &u.bo.res);
- u.bo.pC->atFirst = u.bo.res==0 ?1:0;
- u.bo.pC->deferredMoveto = 0;
- u.bo.pC->cacheStatus = CACHE_STALE;
- u.bo.pC->rowidIsValid = 0;
- }
- u.bo.pC->nullRow = (u8)u.bo.res;
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( isSorter(pC)==(pOp->opcode==OP_SorterSort) );
+ res = 1;
+#ifdef SQLITE_DEBUG
+ pC->seekOp = OP_Rewind;
+#endif
+ if( isSorter(pC) ){
+ rc = sqlite3VdbeSorterRewind(pC, &res);
+ }else{
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
+ assert( pCrsr );
+ rc = sqlite3BtreeFirst(pCrsr, &res);
+ pC->deferredMoveto = 0;
+ pC->cacheStatus = CACHE_STALE;
+ }
+ if( rc ) goto abort_due_to_error;
+ pC->nullRow = (u8)res;
assert( pOp->p2>0 && pOp->p2<p->nOp );
- if( u.bo.res ){
- pc = pOp->p2 - 1;
- }
+ VdbeBranchTaken(res!=0,2);
+ if( res ) goto jump_to_p2;
break;
}
-/* Opcode: Next P1 P2 * P4 P5
+/* Opcode: Next P1 P2 P3 P4 P5
**
** Advance cursor P1 so that it points to the next key/data pair in its
** table or index. If there are no more key/value pairs then fall through
** to the following instruction. But if the cursor advance was successful,
** jump immediately to P2.
**
-** The P1 cursor must be for a real table, not a pseudo-table.
+** The Next opcode is only valid following an SeekGT, SeekGE, or
+** OP_Rewind opcode used to position the cursor. Next is not allowed
+** to follow SeekLT, SeekLE, or OP_Last.
+**
+** The P1 cursor must be for a real table, not a pseudo-table. P1 must have
+** been opened prior to this opcode or the program will segfault.
+**
+** The P3 value is a hint to the btree implementation. If P3==1, that
+** means P1 is an SQL index and that this instruction could have been
+** omitted if that index had been unique. P3 is usually 0. P3 is
+** always either 0 or 1.
**
** P4 is always of type P4_ADVANCE. The function pointer points to
** sqlite3BtreeNext().
@@ -67992,16 +82230,32 @@ case OP_Rewind: { /* jump */
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
**
-** See also: Prev
+** See also: Prev, NextIfOpen
+*/
+/* Opcode: NextIfOpen P1 P2 P3 P4 P5
+**
+** This opcode works just like Next except that if cursor P1 is not
+** open it behaves a no-op.
*/
-/* Opcode: Prev P1 P2 * * P5
+/* Opcode: Prev P1 P2 P3 P4 P5
**
** Back up cursor P1 so that it points to the previous key/data pair in its
** table or index. If there is no previous key/value pairs then fall through
** to the following instruction. But if the cursor backup was successful,
** jump immediately to P2.
**
-** The P1 cursor must be for a real table, not a pseudo-table.
+**
+** The Prev opcode is only valid following an SeekLT, SeekLE, or
+** OP_Last opcode used to position the cursor. Prev is not allowed
+** to follow SeekGT, SeekGE, or OP_Rewind.
+**
+** The P1 cursor must be for a real table, not a pseudo-table. If P1 is
+** not open then the behavior is undefined.
+**
+** The P3 value is a hint to the btree implementation. If P3==1, that
+** means P1 is an SQL index and that this instruction could have been
+** omitted if that index had been unique. P3 is usually 0. P3 is
+** always either 0 or 1.
**
** P4 is always of type P4_ADVANCE. The function pointer points to
** sqlite3BtreePrevious().
@@ -68009,50 +82263,69 @@ case OP_Rewind: { /* jump */
** If P5 is positive and the jump is taken, then event counter
** number P5-1 in the prepared statement is incremented.
*/
-case OP_SorterNext: /* jump */
-#ifdef SQLITE_OMIT_MERGE_SORT
- pOp->opcode = OP_Next;
-#endif
-case OP_Prev: /* jump */
-case OP_Next: { /* jump */
-#if 0 /* local variables moved into u.bp */
+/* Opcode: PrevIfOpen P1 P2 P3 P4 P5
+**
+** This opcode works just like Prev except that if cursor P1 is not
+** open it behaves a no-op.
+*/
+case OP_SorterNext: { /* jump */
VdbeCursor *pC;
int res;
-#endif /* local variables moved into u.bp */
- CHECK_FOR_INTERRUPT;
+ pC = p->apCsr[pOp->p1];
+ assert( isSorter(pC) );
+ res = 0;
+ rc = sqlite3VdbeSorterNext(db, pC, &res);
+ goto next_tail;
+case OP_PrevIfOpen: /* jump */
+case OP_NextIfOpen: /* jump */
+ if( p->apCsr[pOp->p1]==0 ) break;
+ /* Fall through */
+case OP_Prev: /* jump */
+case OP_Next: /* jump */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- assert( pOp->p5<=ArraySize(p->aCounter) );
- u.bp.pC = p->apCsr[pOp->p1];
- if( u.bp.pC==0 ){
- break; /* See ticket #2273 */
- }
- assert( u.bp.pC->isSorter==(pOp->opcode==OP_SorterNext) );
- if( isSorter(u.bp.pC) ){
- assert( pOp->opcode==OP_SorterNext );
- rc = sqlite3VdbeSorterNext(db, u.bp.pC, &u.bp.res);
- }else{
- u.bp.res = 1;
- assert( u.bp.pC->deferredMoveto==0 );
- assert( u.bp.pC->pCursor );
- assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
- assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
- rc = pOp->p4.xAdvance(u.bp.pC->pCursor, &u.bp.res);
- }
- u.bp.pC->nullRow = (u8)u.bp.res;
- u.bp.pC->cacheStatus = CACHE_STALE;
- if( u.bp.res==0 ){
- pc = pOp->p2 - 1;
- if( pOp->p5 ) p->aCounter[pOp->p5-1]++;
+ assert( pOp->p5<ArraySize(p->aCounter) );
+ pC = p->apCsr[pOp->p1];
+ res = pOp->p3;
+ assert( pC!=0 );
+ assert( pC->deferredMoveto==0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( res==0 || (res==1 && pC->isTable==0) );
+ testcase( res==1 );
+ assert( pOp->opcode!=OP_Next || pOp->p4.xAdvance==sqlite3BtreeNext );
+ assert( pOp->opcode!=OP_Prev || pOp->p4.xAdvance==sqlite3BtreePrevious );
+ assert( pOp->opcode!=OP_NextIfOpen || pOp->p4.xAdvance==sqlite3BtreeNext );
+ assert( pOp->opcode!=OP_PrevIfOpen || pOp->p4.xAdvance==sqlite3BtreePrevious);
+
+ /* The Next opcode is only used after SeekGT, SeekGE, and Rewind.
+ ** The Prev opcode is only used after SeekLT, SeekLE, and Last. */
+ assert( pOp->opcode!=OP_Next || pOp->opcode!=OP_NextIfOpen
+ || pC->seekOp==OP_SeekGT || pC->seekOp==OP_SeekGE
+ || pC->seekOp==OP_Rewind || pC->seekOp==OP_Found);
+ assert( pOp->opcode!=OP_Prev || pOp->opcode!=OP_PrevIfOpen
+ || pC->seekOp==OP_SeekLT || pC->seekOp==OP_SeekLE
+ || pC->seekOp==OP_Last );
+
+ rc = pOp->p4.xAdvance(pC->uc.pCursor, &res);
+next_tail:
+ pC->cacheStatus = CACHE_STALE;
+ VdbeBranchTaken(res==0,2);
+ if( rc ) goto abort_due_to_error;
+ if( res==0 ){
+ pC->nullRow = 0;
+ p->aCounter[pOp->p5]++;
#ifdef SQLITE_TEST
sqlite3_search_count++;
#endif
+ goto jump_to_p2_and_check_for_interrupt;
+ }else{
+ pC->nullRow = 1;
}
- u.bp.pC->rowidIsValid = 0;
- break;
+ goto check_for_interrupt;
}
/* Opcode: IdxInsert P1 P2 P3 * P5
+** Synopsis: key=r[P2]
**
** Register P2 holds an SQL index key made using the
** MakeRecord instructions. This opcode writes that key
@@ -68061,87 +82334,106 @@ case OP_Next: { /* jump */
** P3 is a flag that provides a hint to the b-tree layer that this
** insert is likely to be an append.
**
+** If P5 has the OPFLAG_NCHANGE bit set, then the change counter is
+** incremented by this instruction. If the OPFLAG_NCHANGE bit is clear,
+** then the change counter is unchanged.
+**
+** If P5 has the OPFLAG_USESEEKRESULT bit set, then the cursor must have
+** just done a seek to the spot where the new entry is to be inserted.
+** This flag avoids doing an extra seek.
+**
** This instruction only works for indices. The equivalent instruction
** for tables is OP_Insert.
*/
case OP_SorterInsert: /* in2 */
-#ifdef SQLITE_OMIT_MERGE_SORT
- pOp->opcode = OP_IdxInsert;
-#endif
case OP_IdxInsert: { /* in2 */
-#if 0 /* local variables moved into u.bq */
VdbeCursor *pC;
- BtCursor *pCrsr;
- int nKey;
- const char *zKey;
-#endif /* local variables moved into u.bq */
+ BtreePayload x;
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bq.pC = p->apCsr[pOp->p1];
- assert( u.bq.pC!=0 );
- assert( u.bq.pC->isSorter==(pOp->opcode==OP_SorterInsert) );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( isSorter(pC)==(pOp->opcode==OP_SorterInsert) );
pIn2 = &aMem[pOp->p2];
assert( pIn2->flags & MEM_Blob );
- u.bq.pCrsr = u.bq.pC->pCursor;
- if( ALWAYS(u.bq.pCrsr!=0) ){
- assert( u.bq.pC->isTable==0 );
- rc = ExpandBlob(pIn2);
- if( rc==SQLITE_OK ){
- if( isSorter(u.bq.pC) ){
- rc = sqlite3VdbeSorterWrite(db, u.bq.pC, pIn2);
- }else{
- u.bq.nKey = pIn2->n;
- u.bq.zKey = pIn2->z;
- rc = sqlite3BtreeInsert(u.bq.pCrsr, u.bq.zKey, u.bq.nKey, "", 0, 0, pOp->p3,
- ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bq.pC->seekResult : 0)
- );
- assert( u.bq.pC->deferredMoveto==0 );
- u.bq.pC->cacheStatus = CACHE_STALE;
- }
- }
+ if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
+ assert( pC->eCurType==CURTYPE_BTREE || pOp->opcode==OP_SorterInsert );
+ assert( pC->isTable==0 );
+ rc = ExpandBlob(pIn2);
+ if( rc ) goto abort_due_to_error;
+ if( pOp->opcode==OP_SorterInsert ){
+ rc = sqlite3VdbeSorterWrite(pC, pIn2);
+ }else{
+ x.nKey = pIn2->n;
+ x.pKey = pIn2->z;
+ rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, pOp->p3,
+ ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
+ );
+ assert( pC->deferredMoveto==0 );
+ pC->cacheStatus = CACHE_STALE;
}
+ if( rc) goto abort_due_to_error;
break;
}
/* Opcode: IdxDelete P1 P2 P3 * *
+** Synopsis: key=r[P2@P3]
**
** The content of P3 registers starting at register P2 form
** an unpacked index key. This opcode removes that entry from the
** index opened by cursor P1.
*/
case OP_IdxDelete: {
-#if 0 /* local variables moved into u.br */
VdbeCursor *pC;
BtCursor *pCrsr;
int res;
UnpackedRecord r;
-#endif /* local variables moved into u.br */
assert( pOp->p3>0 );
- assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 );
+ assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1 );
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.br.pC = p->apCsr[pOp->p1];
- assert( u.br.pC!=0 );
- u.br.pCrsr = u.br.pC->pCursor;
- if( ALWAYS(u.br.pCrsr!=0) ){
- u.br.r.pKeyInfo = u.br.pC->pKeyInfo;
- u.br.r.nField = (u16)pOp->p3;
- u.br.r.flags = 0;
- u.br.r.aMem = &aMem[pOp->p2];
-#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.br.r.nField; i++) assert( memIsValid(&u.br.r.aMem[i]) ); }
-#endif
- rc = sqlite3BtreeMovetoUnpacked(u.br.pCrsr, &u.br.r, 0, 0, &u.br.res);
- if( rc==SQLITE_OK && u.br.res==0 ){
- rc = sqlite3BtreeDelete(u.br.pCrsr);
- }
- assert( u.br.pC->deferredMoveto==0 );
- u.br.pC->cacheStatus = CACHE_STALE;
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ pCrsr = pC->uc.pCursor;
+ assert( pCrsr!=0 );
+ assert( pOp->p5==0 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)pOp->p3;
+ r.default_rc = 0;
+ r.aMem = &aMem[pOp->p2];
+ rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
+ if( rc ) goto abort_due_to_error;
+ if( res==0 ){
+ rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
+ if( rc ) goto abort_due_to_error;
}
+ assert( pC->deferredMoveto==0 );
+ pC->cacheStatus = CACHE_STALE;
break;
}
+/* Opcode: Seek P1 * P3 P4 *
+** Synopsis: Move P3 to P1.rowid
+**
+** P1 is an open index cursor and P3 is a cursor on the corresponding
+** table. This opcode does a deferred seek of the P3 table cursor
+** to the row that corresponds to the current row of P1.
+**
+** This is a deferred seek. Nothing actually happens until
+** the cursor is used to read a record. That way, if no reads
+** occur, no unnecessary I/O happens.
+**
+** P4 may be an array of integers (type P4_INTARRAY) containing
+** one entry for each column in the P3 table. If array entry a(i)
+** is non-zero, then reading column a(i)-1 from cursor P3 is
+** equivalent to performing the deferred seek and then reading column i
+** from P1. This information is stored in P3 and used to redirect
+** reads against P3 over to P1, thus possibly avoiding the need to
+** seek and read cursor P3.
+*/
/* Opcode: IdxRowid P1 P2 * * *
+** Synopsis: r[P2]=rowid
**
** Write into register P2 an integer which is the last entry in the record at
** the end of the index key pointed to by cursor P1. This integer should be
@@ -68149,99 +82441,148 @@ case OP_IdxDelete: {
**
** See also: Rowid, MakeRecord.
*/
-case OP_IdxRowid: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bs */
- BtCursor *pCrsr;
- VdbeCursor *pC;
- i64 rowid;
-#endif /* local variables moved into u.bs */
+case OP_Seek:
+case OP_IdxRowid: { /* out2 */
+ VdbeCursor *pC; /* The P1 index cursor */
+ VdbeCursor *pTabCur; /* The P2 table cursor (OP_Seek only) */
+ i64 rowid; /* Rowid that P1 current points to */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bs.pC = p->apCsr[pOp->p1];
- assert( u.bs.pC!=0 );
- u.bs.pCrsr = u.bs.pC->pCursor;
- pOut->flags = MEM_Null;
- if( ALWAYS(u.bs.pCrsr!=0) ){
- rc = sqlite3VdbeCursorMoveto(u.bs.pC);
- if( NEVER(rc) ) goto abort_due_to_error;
- assert( u.bs.pC->deferredMoveto==0 );
- assert( u.bs.pC->isTable==0 );
- if( !u.bs.pC->nullRow ){
- rc = sqlite3VdbeIdxRowid(db, u.bs.pCrsr, &u.bs.rowid);
- if( rc!=SQLITE_OK ){
- goto abort_due_to_error;
- }
- pOut->u.i = u.bs.rowid;
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0 );
+ assert( pC->isTable==0 );
+ assert( pC->deferredMoveto==0 );
+ assert( !pC->nullRow || pOp->opcode==OP_IdxRowid );
+
+ /* The IdxRowid and Seek opcodes are combined because of the commonality
+ ** of sqlite3VdbeCursorRestore() and sqlite3VdbeIdxRowid(). */
+ rc = sqlite3VdbeCursorRestore(pC);
+
+ /* sqlite3VbeCursorRestore() can only fail if the record has been deleted
+ ** out from under the cursor. That will never happens for an IdxRowid
+ ** or Seek opcode */
+ if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error;
+
+ if( !pC->nullRow ){
+ rowid = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3VdbeIdxRowid(db, pC->uc.pCursor, &rowid);
+ if( rc!=SQLITE_OK ){
+ goto abort_due_to_error;
+ }
+ if( pOp->opcode==OP_Seek ){
+ assert( pOp->p3>=0 && pOp->p3<p->nCursor );
+ pTabCur = p->apCsr[pOp->p3];
+ assert( pTabCur!=0 );
+ assert( pTabCur->eCurType==CURTYPE_BTREE );
+ assert( pTabCur->uc.pCursor!=0 );
+ assert( pTabCur->isTable );
+ pTabCur->nullRow = 0;
+ pTabCur->movetoTarget = rowid;
+ pTabCur->deferredMoveto = 1;
+ assert( pOp->p4type==P4_INTARRAY || pOp->p4.ai==0 );
+ pTabCur->aAltMap = pOp->p4.ai;
+ pTabCur->pAltCursor = pC;
+ }else{
+ pOut = out2Prerelease(p, pOp);
+ pOut->u.i = rowid;
pOut->flags = MEM_Int;
}
+ }else{
+ assert( pOp->opcode==OP_IdxRowid );
+ sqlite3VdbeMemSetNull(&aMem[pOp->p2]);
}
break;
}
/* Opcode: IdxGE P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
**
** The P4 register values beginning with P3 form an unpacked index
-** key that omits the ROWID. Compare this key value against the index
-** that P1 is currently pointing to, ignoring the ROWID on the P1 index.
+** key that omits the PRIMARY KEY. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
+** fields at the end.
**
** If the P1 index entry is greater than or equal to the key value
** then jump to P2. Otherwise fall through to the next instruction.
+*/
+/* Opcode: IdxGT P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
+**
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the PRIMARY KEY. Compare this key value against the index
+** that P1 is currently pointing to, ignoring the PRIMARY KEY or ROWID
+** fields at the end.
**
-** If P5 is non-zero then the key value is increased by an epsilon
-** prior to the comparison. This make the opcode work like IdxGT except
-** that if the key from register P3 is a prefix of the key in the cursor,
-** the result is false whereas it would be true with IdxGT.
+** If the P1 index entry is greater than the key value
+** then jump to P2. Otherwise fall through to the next instruction.
*/
/* Opcode: IdxLT P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
**
** The P4 register values beginning with P3 form an unpacked index
-** key that omits the ROWID. Compare this key value against the index
-** that P1 is currently pointing to, ignoring the ROWID on the P1 index.
+** key that omits the PRIMARY KEY or ROWID. Compare this key value against
+** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or
+** ROWID on the P1 index.
**
** If the P1 index entry is less than the key value then jump to P2.
** Otherwise fall through to the next instruction.
+*/
+/* Opcode: IdxLE P1 P2 P3 P4 P5
+** Synopsis: key=r[P3@P4]
+**
+** The P4 register values beginning with P3 form an unpacked index
+** key that omits the PRIMARY KEY or ROWID. Compare this key value against
+** the index that P1 is currently pointing to, ignoring the PRIMARY KEY or
+** ROWID on the P1 index.
**
-** If P5 is non-zero then the key value is increased by an epsilon prior
-** to the comparison. This makes the opcode work like IdxLE.
+** If the P1 index entry is less than or equal to the key value then jump
+** to P2. Otherwise fall through to the next instruction.
*/
+case OP_IdxLE: /* jump */
+case OP_IdxGT: /* jump */
case OP_IdxLT: /* jump */
-case OP_IdxGE: { /* jump */
-#if 0 /* local variables moved into u.bt */
+case OP_IdxGE: { /* jump */
VdbeCursor *pC;
int res;
UnpackedRecord r;
-#endif /* local variables moved into u.bt */
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
- u.bt.pC = p->apCsr[pOp->p1];
- assert( u.bt.pC!=0 );
- assert( u.bt.pC->isOrdered );
- if( ALWAYS(u.bt.pC->pCursor!=0) ){
- assert( u.bt.pC->deferredMoveto==0 );
- assert( pOp->p5==0 || pOp->p5==1 );
- assert( pOp->p4type==P4_INT32 );
- u.bt.r.pKeyInfo = u.bt.pC->pKeyInfo;
- u.bt.r.nField = (u16)pOp->p4.i;
- if( pOp->p5 ){
- u.bt.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID;
- }else{
- u.bt.r.flags = UNPACKED_IGNORE_ROWID;
- }
- u.bt.r.aMem = &aMem[pOp->p3];
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ assert( pC->isOrdered );
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->uc.pCursor!=0);
+ assert( pC->deferredMoveto==0 );
+ assert( pOp->p5==0 || pOp->p5==1 );
+ assert( pOp->p4type==P4_INT32 );
+ r.pKeyInfo = pC->pKeyInfo;
+ r.nField = (u16)pOp->p4.i;
+ if( pOp->opcode<OP_IdxLT ){
+ assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxGT );
+ r.default_rc = -1;
+ }else{
+ assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxLT );
+ r.default_rc = 0;
+ }
+ r.aMem = &aMem[pOp->p3];
#ifdef SQLITE_DEBUG
- { int i; for(i=0; i<u.bt.r.nField; i++) assert( memIsValid(&u.bt.r.aMem[i]) ); }
-#endif
- rc = sqlite3VdbeIdxKeyCompare(u.bt.pC, &u.bt.r, &u.bt.res);
- if( pOp->opcode==OP_IdxLT ){
- u.bt.res = -u.bt.res;
- }else{
- assert( pOp->opcode==OP_IdxGE );
- u.bt.res++;
- }
- if( u.bt.res>0 ){
- pc = pOp->p2 - 1 ;
- }
+ { int i; for(i=0; i<r.nField; i++) assert( memIsValid(&r.aMem[i]) ); }
+#endif
+ res = 0; /* Not needed. Only used to silence a warning. */
+ rc = sqlite3VdbeIdxKeyCompare(db, pC, &r, &res);
+ assert( (OP_IdxLE&1)==(OP_IdxLT&1) && (OP_IdxGE&1)==(OP_IdxGT&1) );
+ if( (pOp->opcode&1)==(OP_IdxLT&1) ){
+ assert( pOp->opcode==OP_IdxLE || pOp->opcode==OP_IdxLT );
+ res = -res;
+ }else{
+ assert( pOp->opcode==OP_IdxGE || pOp->opcode==OP_IdxGT );
+ res++;
}
+ VdbeBranchTaken(res>0,2);
+ if( rc ) goto abort_due_to_error;
+ if( res>0 ) goto jump_to_p2;
break;
}
@@ -68265,40 +82606,32 @@ case OP_IdxGE: { /* jump */
**
** See also: Clear
*/
-case OP_Destroy: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bu */
+case OP_Destroy: { /* out2 */
int iMoved;
- int iCnt;
- Vdbe *pVdbe;
int iDb;
-#endif /* local variables moved into u.bu */
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- u.bu.iCnt = 0;
- for(u.bu.pVdbe=db->pVdbe; u.bu.pVdbe; u.bu.pVdbe = u.bu.pVdbe->pNext){
- if( u.bu.pVdbe->magic==VDBE_MAGIC_RUN && u.bu.pVdbe->inVtabMethod<2 && u.bu.pVdbe->pc>=0 ){
- u.bu.iCnt++;
- }
- }
-#else
- u.bu.iCnt = db->activeVdbeCnt;
-#endif
+
+ assert( p->readOnly==0 );
+ assert( pOp->p1>1 );
+ pOut = out2Prerelease(p, pOp);
pOut->flags = MEM_Null;
- if( u.bu.iCnt>1 ){
+ if( db->nVdbeRead > db->nVDestroy+1 ){
rc = SQLITE_LOCKED;
p->errorAction = OE_Abort;
+ goto abort_due_to_error;
}else{
- u.bu.iDb = pOp->p3;
- assert( u.bu.iCnt==1 );
- assert( (p->btreeMask & (((yDbMask)1)<<u.bu.iDb))!=0 );
- rc = sqlite3BtreeDropTable(db->aDb[u.bu.iDb].pBt, pOp->p1, &u.bu.iMoved);
+ iDb = pOp->p3;
+ assert( DbMaskTest(p->btreeMask, iDb) );
+ iMoved = 0; /* Not needed. Only to silence a warning. */
+ rc = sqlite3BtreeDropTable(db->aDb[iDb].pBt, pOp->p1, &iMoved);
pOut->flags = MEM_Int;
- pOut->u.i = u.bu.iMoved;
+ pOut->u.i = iMoved;
+ if( rc ) goto abort_due_to_error;
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( rc==SQLITE_OK && u.bu.iMoved!=0 ){
- sqlite3RootPageMoved(db, u.bu.iDb, u.bu.iMoved, pOp->p1);
+ if( iMoved!=0 ){
+ sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1);
/* All OP_Destroy operations occur on the same btree */
- assert( resetSchemaOnFault==0 || resetSchemaOnFault==u.bu.iDb+1 );
- resetSchemaOnFault = u.bu.iDb+1;
+ assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 );
+ resetSchemaOnFault = iDb+1;
}
#endif
}
@@ -68324,27 +82657,53 @@ case OP_Destroy: { /* out2-prerelease */
** See also: Destroy
*/
case OP_Clear: {
-#if 0 /* local variables moved into u.bv */
int nChange;
-#endif /* local variables moved into u.bv */
-
- u.bv.nChange = 0;
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p2))!=0 );
+
+ nChange = 0;
+ assert( p->readOnly==0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p2) );
rc = sqlite3BtreeClearTable(
- db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bv.nChange : 0)
+ db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &nChange : 0)
);
if( pOp->p3 ){
- p->nChange += u.bv.nChange;
+ p->nChange += nChange;
if( pOp->p3>0 ){
assert( memIsValid(&aMem[pOp->p3]) );
memAboutToChange(p, &aMem[pOp->p3]);
- aMem[pOp->p3].u.i += u.bv.nChange;
+ aMem[pOp->p3].u.i += nChange;
}
}
+ if( rc ) goto abort_due_to_error;
+ break;
+}
+
+/* Opcode: ResetSorter P1 * * * *
+**
+** Delete all contents from the ephemeral table or sorter
+** that is open on cursor P1.
+**
+** This opcode only works for cursors used for sorting and
+** opened with OP_OpenEphemeral or OP_SorterOpen.
+*/
+case OP_ResetSorter: {
+ VdbeCursor *pC;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ pC = p->apCsr[pOp->p1];
+ assert( pC!=0 );
+ if( isSorter(pC) ){
+ sqlite3VdbeSorterReset(db, pC->uc.pSorter);
+ }else{
+ assert( pC->eCurType==CURTYPE_BTREE );
+ assert( pC->isEphemeral );
+ rc = sqlite3BtreeClearTableOfCursor(pC->uc.pCursor);
+ if( rc ) goto abort_due_to_error;
+ }
break;
}
/* Opcode: CreateTable P1 P2 * * *
+** Synopsis: r[P2]=root iDb=P1
**
** Allocate a new table in the main database file if P1==0 or in the
** auxiliary database file if P1==1 or in an attached database if
@@ -68358,6 +82717,7 @@ case OP_Clear: {
** See also: CreateIndex
*/
/* Opcode: CreateIndex P1 P2 * * *
+** Synopsis: r[P2]=root iDb=P1
**
** Allocate a new index in the main database file if P1==0 or in the
** auxiliary database file if P1==1 or in an attached database if
@@ -68366,27 +82726,28 @@ case OP_Clear: {
**
** See documentation on OP_CreateTable for additional information.
*/
-case OP_CreateIndex: /* out2-prerelease */
-case OP_CreateTable: { /* out2-prerelease */
-#if 0 /* local variables moved into u.bw */
+case OP_CreateIndex: /* out2 */
+case OP_CreateTable: { /* out2 */
int pgno;
int flags;
Db *pDb;
-#endif /* local variables moved into u.bw */
- u.bw.pgno = 0;
+ pOut = out2Prerelease(p, pOp);
+ pgno = 0;
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.bw.pDb = &db->aDb[pOp->p1];
- assert( u.bw.pDb->pBt!=0 );
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ assert( p->readOnly==0 );
+ pDb = &db->aDb[pOp->p1];
+ assert( pDb->pBt!=0 );
if( pOp->opcode==OP_CreateTable ){
- /* u.bw.flags = BTREE_INTKEY; */
- u.bw.flags = BTREE_INTKEY;
+ /* flags = BTREE_INTKEY; */
+ flags = BTREE_INTKEY;
}else{
- u.bw.flags = BTREE_BLOBKEY;
+ flags = BTREE_BLOBKEY;
}
- rc = sqlite3BtreeCreateTable(u.bw.pDb->pBt, &u.bw.pgno, u.bw.flags);
- pOut->u.i = u.bw.pgno;
+ rc = sqlite3BtreeCreateTable(pDb->pBt, &pgno, flags);
+ if( rc ) goto abort_due_to_error;
+ pOut->u.i = pgno;
break;
}
@@ -68399,51 +82760,53 @@ case OP_CreateTable: { /* out2-prerelease */
** then runs the new virtual machine. It is thus a re-entrant opcode.
*/
case OP_ParseSchema: {
-#if 0 /* local variables moved into u.bx */
int iDb;
const char *zMaster;
char *zSql;
InitData initData;
-#endif /* local variables moved into u.bx */
/* Any prepared statement that invokes this opcode will hold mutexes
- ** on every btree. This is a prerequisite for invoking
+ ** on every btree. This is a prerequisite for invoking
** sqlite3InitCallback().
*/
#ifdef SQLITE_DEBUG
- for(u.bx.iDb=0; u.bx.iDb<db->nDb; u.bx.iDb++){
- assert( u.bx.iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[u.bx.iDb].pBt) );
+ for(iDb=0; iDb<db->nDb; iDb++){
+ assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
}
#endif
- u.bx.iDb = pOp->p1;
- assert( u.bx.iDb>=0 && u.bx.iDb<db->nDb );
- assert( DbHasProperty(db, u.bx.iDb, DB_SchemaLoaded) );
+ iDb = pOp->p1;
+ assert( iDb>=0 && iDb<db->nDb );
+ assert( DbHasProperty(db, iDb, DB_SchemaLoaded) );
/* Used to be a conditional */ {
- u.bx.zMaster = SCHEMA_TABLE(u.bx.iDb);
- u.bx.initData.db = db;
- u.bx.initData.iDb = pOp->p1;
- u.bx.initData.pzErrMsg = &p->zErrMsg;
- u.bx.zSql = sqlite3MPrintf(db,
+ zMaster = SCHEMA_TABLE(iDb);
+ initData.db = db;
+ initData.iDb = pOp->p1;
+ initData.pzErrMsg = &p->zErrMsg;
+ zSql = sqlite3MPrintf(db,
"SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid",
- db->aDb[u.bx.iDb].zName, u.bx.zMaster, pOp->p4.z);
- if( u.bx.zSql==0 ){
- rc = SQLITE_NOMEM;
+ db->aDb[iDb].zDbSName, zMaster, pOp->p4.z);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM_BKPT;
}else{
assert( db->init.busy==0 );
db->init.busy = 1;
- u.bx.initData.rc = SQLITE_OK;
+ initData.rc = SQLITE_OK;
assert( !db->mallocFailed );
- rc = sqlite3_exec(db, u.bx.zSql, sqlite3InitCallback, &u.bx.initData, 0);
- if( rc==SQLITE_OK ) rc = u.bx.initData.rc;
- sqlite3DbFree(db, u.bx.zSql);
+ rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
+ if( rc==SQLITE_OK ) rc = initData.rc;
+ sqlite3DbFree(db, zSql);
db->init.busy = 0;
}
}
- if( rc==SQLITE_NOMEM ){
- goto no_mem;
+ if( rc ){
+ sqlite3ResetAllSchemasOfConnection(db);
+ if( rc==SQLITE_NOMEM ){
+ goto no_mem;
+ }
+ goto abort_due_to_error;
}
- break;
+ break;
}
#if !defined(SQLITE_OMIT_ANALYZE)
@@ -68456,6 +82819,7 @@ case OP_ParseSchema: {
case OP_LoadAnalysis: {
assert( pOp->p1>=0 && pOp->p1<db->nDb );
rc = sqlite3AnalysisLoad(db, pOp->p1);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* !defined(SQLITE_OMIT_ANALYZE) */
@@ -68464,7 +82828,8 @@ case OP_LoadAnalysis: {
**
** Remove the internal (in-memory) data structures that describe
** the table named P4 in database P1. This is called after a table
-** is dropped in order to keep the internal representation of the
+** is dropped from disk (using the Destroy opcode) in order to keep
+** the internal representation of the
** schema consistent with what is on disk.
*/
case OP_DropTable: {
@@ -68476,7 +82841,8 @@ case OP_DropTable: {
**
** Remove the internal (in-memory) data structures that describe
** the index named P4 in database P1. This is called after an index
-** is dropped in order to keep the internal representation of the
+** is dropped from disk (using the Destroy opcode)
+** in order to keep the internal representation of the
** schema consistent with what is on disk.
*/
case OP_DropIndex: {
@@ -68488,7 +82854,8 @@ case OP_DropIndex: {
**
** Remove the internal (in-memory) data structures that describe
** the trigger named P4 in database P1. This is called after a trigger
-** is dropped in order to keep the internal representation of the
+** is dropped from disk (using the Destroy opcode) in order to keep
+** the internal representation of the
** schema consistent with what is on disk.
*/
case OP_DropTrigger: {
@@ -68498,7 +82865,7 @@ case OP_DropTrigger: {
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
-/* Opcode: IntegrityCk P1 P2 P3 * P5
+/* Opcode: IntegrityCk P1 P2 P3 P4 P5
**
** Do an analysis of the currently open database. Store in
** register P1 the text of an error message describing any problems.
@@ -68509,9 +82876,8 @@ case OP_DropTrigger: {
** In other words, the analysis stops as soon as reg(P1) errors are
** seen. Reg(P1) is updated with the number of errors remaining.
**
-** The root page numbers of all tables in the database are integer
-** stored in reg(P1), reg(P1+1), reg(P1+2), .... There are P2 tables
-** total.
+** The root page numbers of all tables in the database are integers
+** stored in P4_INTARRAY argument.
**
** If P5 is not zero, the check is done on the auxiliary database
** file, not the main database file.
@@ -68519,41 +82885,34 @@ case OP_DropTrigger: {
** This opcode is used to implement the integrity_check pragma.
*/
case OP_IntegrityCk: {
-#if 0 /* local variables moved into u.by */
int nRoot; /* Number of tables to check. (Number of root pages.) */
int *aRoot; /* Array of rootpage numbers for tables to be checked */
- int j; /* Loop counter */
int nErr; /* Number of errors reported */
char *z; /* Text of the error report */
Mem *pnErr; /* Register keeping track of errors remaining */
-#endif /* local variables moved into u.by */
-
- u.by.nRoot = pOp->p2;
- assert( u.by.nRoot>0 );
- u.by.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.by.nRoot+1) );
- if( u.by.aRoot==0 ) goto no_mem;
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.by.pnErr = &aMem[pOp->p3];
- assert( (u.by.pnErr->flags & MEM_Int)!=0 );
- assert( (u.by.pnErr->flags & (MEM_Str|MEM_Blob))==0 );
+
+ assert( p->bIsReader );
+ nRoot = pOp->p2;
+ aRoot = pOp->p4.ai;
+ assert( nRoot>0 );
+ assert( aRoot[nRoot]==0 );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ pnErr = &aMem[pOp->p3];
+ assert( (pnErr->flags & MEM_Int)!=0 );
+ assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 );
pIn1 = &aMem[pOp->p1];
- for(u.by.j=0; u.by.j<u.by.nRoot; u.by.j++){
- u.by.aRoot[u.by.j] = (int)sqlite3VdbeIntValue(&pIn1[u.by.j]);
- }
- u.by.aRoot[u.by.j] = 0;
assert( pOp->p5<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p5))!=0 );
- u.by.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.by.aRoot, u.by.nRoot,
- (int)u.by.pnErr->u.i, &u.by.nErr);
- sqlite3DbFree(db, u.by.aRoot);
- u.by.pnErr->u.i -= u.by.nErr;
+ assert( DbMaskTest(p->btreeMask, pOp->p5) );
+ z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, aRoot, nRoot,
+ (int)pnErr->u.i, &nErr);
+ pnErr->u.i -= nErr;
sqlite3VdbeMemSetNull(pIn1);
- if( u.by.nErr==0 ){
- assert( u.by.z==0 );
- }else if( u.by.z==0 ){
+ if( nErr==0 ){
+ assert( z==0 );
+ }else if( z==0 ){
goto no_mem;
}else{
- sqlite3VdbeMemSetStr(pIn1, u.by.z, -1, SQLITE_UTF8, sqlite3_free);
+ sqlite3VdbeMemSetStr(pIn1, z, -1, SQLITE_UTF8, sqlite3_free);
}
UPDATE_MAX_BLOBSIZE(pIn1);
sqlite3VdbeChangeEncoding(pIn1, encoding);
@@ -68562,6 +82921,7 @@ case OP_IntegrityCk: {
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
/* Opcode: RowSetAdd P1 P2 * * *
+** Synopsis: rowset(P1)=r[P2]
**
** Insert the integer value held by register P2 into a boolean index
** held in register P1.
@@ -68581,31 +82941,33 @@ case OP_RowSetAdd: { /* in1, in2 */
}
/* Opcode: RowSetRead P1 P2 P3 * *
+** Synopsis: r[P3]=rowset(P1)
**
** Extract the smallest value from boolean index P1 and put that value into
** register P3. Or, if boolean index P1 is initially empty, leave P3
** unchanged and jump to instruction P2.
*/
case OP_RowSetRead: { /* jump, in1, out3 */
-#if 0 /* local variables moved into u.bz */
i64 val;
-#endif /* local variables moved into u.bz */
- CHECK_FOR_INTERRUPT;
+
pIn1 = &aMem[pOp->p1];
- if( (pIn1->flags & MEM_RowSet)==0
- || sqlite3RowSetNext(pIn1->u.pRowSet, &u.bz.val)==0
+ if( (pIn1->flags & MEM_RowSet)==0
+ || sqlite3RowSetNext(pIn1->u.pRowSet, &val)==0
){
/* The boolean index is empty */
sqlite3VdbeMemSetNull(pIn1);
- pc = pOp->p2 - 1;
+ VdbeBranchTaken(1,2);
+ goto jump_to_p2_and_check_for_interrupt;
}else{
/* A value was pulled from the index */
- sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.bz.val);
+ VdbeBranchTaken(0,2);
+ sqlite3VdbeMemSetInt64(&aMem[pOp->p3], val);
}
- break;
+ goto check_for_interrupt;
}
/* Opcode: RowSetTest P1 P2 P3 P4
+** Synopsis: if r[P3] in rowset(P1) goto P2
**
** Register P3 is assumed to hold a 64-bit integer value. If register P1
** contains a RowSet object and that RowSet object contains
@@ -68629,14 +82991,12 @@ case OP_RowSetRead: { /* jump, in1, out3 */
** inserted as part of some other set).
*/
case OP_RowSetTest: { /* jump, in1, in3 */
-#if 0 /* local variables moved into u.ca */
int iSet;
int exists;
-#endif /* local variables moved into u.ca */
pIn1 = &aMem[pOp->p1];
pIn3 = &aMem[pOp->p3];
- u.ca.iSet = pOp->p4.i;
+ iSet = pOp->p4.i;
assert( pIn3->flags&MEM_Int );
/* If there is anything other than a rowset object in memory cell P1,
@@ -68648,17 +83008,13 @@ case OP_RowSetTest: { /* jump, in1, in3 */
}
assert( pOp->p4type==P4_INT32 );
- assert( u.ca.iSet==-1 || u.ca.iSet>=0 );
- if( u.ca.iSet ){
- u.ca.exists = sqlite3RowSetTest(pIn1->u.pRowSet,
- (u8)(u.ca.iSet>=0 ? u.ca.iSet & 0xf : 0xff),
- pIn3->u.i);
- if( u.ca.exists ){
- pc = pOp->p2 - 1;
- break;
- }
+ assert( iSet==-1 || iSet>=0 );
+ if( iSet ){
+ exists = sqlite3RowSetTest(pIn1->u.pRowSet, iSet, pIn3->u.i);
+ VdbeBranchTaken(exists!=0,2);
+ if( exists ) goto jump_to_p2;
}
- if( u.ca.iSet>=0 ){
+ if( iSet>=0 ){
sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i);
}
break;
@@ -68667,7 +83023,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */
#ifndef SQLITE_OMIT_TRIGGER
-/* Opcode: Program P1 P2 P3 P4 *
+/* Opcode: Program P1 P2 P3 P4 P5
**
** Execute the trigger program passed as P4 (type P4_SUBPROGRAM).
**
@@ -68679,9 +83035,10 @@ case OP_RowSetTest: { /* jump, in1, in3 */
** memory required by the sub-vdbe at runtime.
**
** P4 is a pointer to the VM containing the trigger program.
+**
+** If P5 is non-zero, then recursive program invocation is enabled.
*/
case OP_Program: { /* jump */
-#if 0 /* local variables moved into u.cb */
int nMem; /* Number of memory registers for sub-program */
int nByte; /* Bytes of runtime space required for sub-program */
Mem *pRt; /* Register to allocate runtime space */
@@ -68690,95 +83047,106 @@ case OP_Program: { /* jump */
VdbeFrame *pFrame; /* New vdbe frame to execute in */
SubProgram *pProgram; /* Sub-program to execute */
void *t; /* Token identifying trigger */
-#endif /* local variables moved into u.cb */
-
- u.cb.pProgram = pOp->p4.pProgram;
- u.cb.pRt = &aMem[pOp->p3];
- assert( memIsValid(u.cb.pRt) );
- assert( u.cb.pProgram->nOp>0 );
- /* If the p5 flag is clear, then recursive invocation of triggers is
+ pProgram = pOp->p4.pProgram;
+ pRt = &aMem[pOp->p3];
+ assert( pProgram->nOp>0 );
+
+ /* If the p5 flag is clear, then recursive invocation of triggers is
** disabled for backwards compatibility (p5 is set if this sub-program
** is really a trigger, not a foreign key action, and the flag set
** and cleared by the "PRAGMA recursive_triggers" command is clear).
- **
- ** It is recursive invocation of triggers, at the SQL level, that is
- ** disabled. In some cases a single trigger may generate more than one
- ** SubProgram (if the trigger may be executed with more than one different
+ **
+ ** It is recursive invocation of triggers, at the SQL level, that is
+ ** disabled. In some cases a single trigger may generate more than one
+ ** SubProgram (if the trigger may be executed with more than one different
** ON CONFLICT algorithm). SubProgram structures associated with a
- ** single trigger all have the same value for the SubProgram.token
+ ** single trigger all have the same value for the SubProgram.token
** variable. */
if( pOp->p5 ){
- u.cb.t = u.cb.pProgram->token;
- for(u.cb.pFrame=p->pFrame; u.cb.pFrame && u.cb.pFrame->token!=u.cb.t; u.cb.pFrame=u.cb.pFrame->pParent);
- if( u.cb.pFrame ) break;
+ t = pProgram->token;
+ for(pFrame=p->pFrame; pFrame && pFrame->token!=t; pFrame=pFrame->pParent);
+ if( pFrame ) break;
}
if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){
rc = SQLITE_ERROR;
- sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion");
- break;
+ sqlite3VdbeError(p, "too many levels of trigger recursion");
+ goto abort_due_to_error;
}
- /* Register u.cb.pRt is used to store the memory required to save the state
+ /* Register pRt is used to store the memory required to save the state
** of the current program, and the memory required at runtime to execute
- ** the trigger program. If this trigger has been fired before, then u.cb.pRt
+ ** the trigger program. If this trigger has been fired before, then pRt
** is already allocated. Otherwise, it must be initialized. */
- if( (u.cb.pRt->flags&MEM_Frame)==0 ){
- /* SubProgram.nMem is set to the number of memory cells used by the
+ if( (pRt->flags&MEM_Frame)==0 ){
+ /* SubProgram.nMem is set to the number of memory cells used by the
** program stored in SubProgram.aOp. As well as these, one memory
** cell is required for each cursor used by the program. Set local
- ** variable u.cb.nMem (and later, VdbeFrame.nChildMem) to this value.
+ ** variable nMem (and later, VdbeFrame.nChildMem) to this value.
*/
- u.cb.nMem = u.cb.pProgram->nMem + u.cb.pProgram->nCsr;
- u.cb.nByte = ROUND8(sizeof(VdbeFrame))
- + u.cb.nMem * sizeof(Mem)
- + u.cb.pProgram->nCsr * sizeof(VdbeCursor *);
- u.cb.pFrame = sqlite3DbMallocZero(db, u.cb.nByte);
- if( !u.cb.pFrame ){
+ nMem = pProgram->nMem + pProgram->nCsr;
+ assert( nMem>0 );
+ if( pProgram->nCsr==0 ) nMem++;
+ nByte = ROUND8(sizeof(VdbeFrame))
+ + nMem * sizeof(Mem)
+ + pProgram->nCsr * sizeof(VdbeCursor *);
+ pFrame = sqlite3DbMallocZero(db, nByte);
+ if( !pFrame ){
goto no_mem;
}
- sqlite3VdbeMemRelease(u.cb.pRt);
- u.cb.pRt->flags = MEM_Frame;
- u.cb.pRt->u.pFrame = u.cb.pFrame;
-
- u.cb.pFrame->v = p;
- u.cb.pFrame->nChildMem = u.cb.nMem;
- u.cb.pFrame->nChildCsr = u.cb.pProgram->nCsr;
- u.cb.pFrame->pc = pc;
- u.cb.pFrame->aMem = p->aMem;
- u.cb.pFrame->nMem = p->nMem;
- u.cb.pFrame->apCsr = p->apCsr;
- u.cb.pFrame->nCursor = p->nCursor;
- u.cb.pFrame->aOp = p->aOp;
- u.cb.pFrame->nOp = p->nOp;
- u.cb.pFrame->token = u.cb.pProgram->token;
-
- u.cb.pEnd = &VdbeFrameMem(u.cb.pFrame)[u.cb.pFrame->nChildMem];
- for(u.cb.pMem=VdbeFrameMem(u.cb.pFrame); u.cb.pMem!=u.cb.pEnd; u.cb.pMem++){
- u.cb.pMem->flags = MEM_Null;
- u.cb.pMem->db = db;
+ sqlite3VdbeMemRelease(pRt);
+ pRt->flags = MEM_Frame;
+ pRt->u.pFrame = pFrame;
+
+ pFrame->v = p;
+ pFrame->nChildMem = nMem;
+ pFrame->nChildCsr = pProgram->nCsr;
+ pFrame->pc = (int)(pOp - aOp);
+ pFrame->aMem = p->aMem;
+ pFrame->nMem = p->nMem;
+ pFrame->apCsr = p->apCsr;
+ pFrame->nCursor = p->nCursor;
+ pFrame->aOp = p->aOp;
+ pFrame->nOp = p->nOp;
+ pFrame->token = pProgram->token;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ pFrame->anExec = p->anExec;
+#endif
+
+ pEnd = &VdbeFrameMem(pFrame)[pFrame->nChildMem];
+ for(pMem=VdbeFrameMem(pFrame); pMem!=pEnd; pMem++){
+ pMem->flags = MEM_Undefined;
+ pMem->db = db;
}
}else{
- u.cb.pFrame = u.cb.pRt->u.pFrame;
- assert( u.cb.pProgram->nMem+u.cb.pProgram->nCsr==u.cb.pFrame->nChildMem );
- assert( u.cb.pProgram->nCsr==u.cb.pFrame->nChildCsr );
- assert( pc==u.cb.pFrame->pc );
+ pFrame = pRt->u.pFrame;
+ assert( pProgram->nMem+pProgram->nCsr==pFrame->nChildMem
+ || (pProgram->nCsr==0 && pProgram->nMem+1==pFrame->nChildMem) );
+ assert( pProgram->nCsr==pFrame->nChildCsr );
+ assert( (int)(pOp - aOp)==pFrame->pc );
}
p->nFrame++;
- u.cb.pFrame->pParent = p->pFrame;
- u.cb.pFrame->lastRowid = lastRowid;
- u.cb.pFrame->nChange = p->nChange;
+ pFrame->pParent = p->pFrame;
+ pFrame->lastRowid = lastRowid;
+ pFrame->nChange = p->nChange;
+ pFrame->nDbChange = p->db->nChange;
+ assert( pFrame->pAuxData==0 );
+ pFrame->pAuxData = p->pAuxData;
+ p->pAuxData = 0;
p->nChange = 0;
- p->pFrame = u.cb.pFrame;
- p->aMem = aMem = &VdbeFrameMem(u.cb.pFrame)[-1];
- p->nMem = u.cb.pFrame->nChildMem;
- p->nCursor = (u16)u.cb.pFrame->nChildCsr;
- p->apCsr = (VdbeCursor **)&aMem[p->nMem+1];
- p->aOp = aOp = u.cb.pProgram->aOp;
- p->nOp = u.cb.pProgram->nOp;
- pc = -1;
+ p->pFrame = pFrame;
+ p->aMem = aMem = VdbeFrameMem(pFrame);
+ p->nMem = pFrame->nChildMem;
+ p->nCursor = (u16)pFrame->nChildCsr;
+ p->apCsr = (VdbeCursor **)&aMem[p->nMem];
+ p->aOp = aOp = pProgram->aOp;
+ p->nOp = pProgram->nOp;
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ p->anExec = 0;
+#endif
+ pOp = &aOp[-1];
break;
}
@@ -68795,14 +83163,13 @@ case OP_Program: { /* jump */
** the value of the P1 argument to the value of the P1 argument to the
** calling OP_Program instruction.
*/
-case OP_Param: { /* out2-prerelease */
-#if 0 /* local variables moved into u.cc */
+case OP_Param: { /* out2 */
VdbeFrame *pFrame;
Mem *pIn;
-#endif /* local variables moved into u.cc */
- u.cc.pFrame = p->pFrame;
- u.cc.pIn = &u.cc.pFrame->aMem[pOp->p1 + u.cc.pFrame->aOp[u.cc.pFrame->pc].p1];
- sqlite3VdbeMemShallowCopy(pOut, u.cc.pIn, MEM_Ephem);
+ pOut = out2Prerelease(p, pOp);
+ pFrame = p->pFrame;
+ pIn = &pFrame->aMem[pOp->p1 + pFrame->aOp[pFrame->pc].p1];
+ sqlite3VdbeMemShallowCopy(pOut, pIn, MEM_Ephem);
break;
}
@@ -68810,6 +83177,7 @@ case OP_Param: { /* out2-prerelease */
#ifndef SQLITE_OMIT_FOREIGN_KEY
/* Opcode: FkCounter P1 P2 * * *
+** Synopsis: fkctr[P1]+=P2
**
** Increment a "constraint counter" by P2 (P2 may be negative or positive).
** If P1 is non-zero, the database constraint counter is incremented
@@ -68817,7 +83185,9 @@ case OP_Param: { /* out2-prerelease */
** statement counter is incremented (immediate foreign key constraints).
*/
case OP_FkCounter: {
- if( pOp->p1 ){
+ if( db->flags & SQLITE_DeferFKs ){
+ db->nDeferredImmCons += pOp->p2;
+ }else if( pOp->p1 ){
db->nDeferredCons += pOp->p2;
}else{
p->nFkConstraint += pOp->p2;
@@ -68826,6 +83196,7 @@ case OP_FkCounter: {
}
/* Opcode: FkIfZero P1 P2 * * *
+** Synopsis: if fkctr[P1]==0 goto P2
**
** This opcode tests if a foreign key constraint-counter is currently zero.
** If so, jump to instruction P2. Otherwise, fall through to the next
@@ -68838,9 +83209,11 @@ case OP_FkCounter: {
*/
case OP_FkIfZero: { /* jump */
if( pOp->p1 ){
- if( db->nDeferredCons==0 ) pc = pOp->p2-1;
+ VdbeBranchTaken(db->nDeferredCons==0 && db->nDeferredImmCons==0, 2);
+ if( db->nDeferredCons==0 && db->nDeferredImmCons==0 ) goto jump_to_p2;
}else{
- if( p->nFkConstraint==0 ) pc = pOp->p2-1;
+ VdbeBranchTaken(p->nFkConstraint==0 && db->nDeferredImmCons==0, 2);
+ if( p->nFkConstraint==0 && db->nDeferredImmCons==0 ) goto jump_to_p2;
}
break;
}
@@ -68848,6 +83221,7 @@ case OP_FkIfZero: { /* jump */
#ifndef SQLITE_OMIT_AUTOINCREMENT
/* Opcode: MemMax P1 P2 * * *
+** Synopsis: r[P1]=max(r[P1],r[P2])
**
** P1 is a register in the root frame of this VM (the root frame is
** different from the current frame if this instruction is being executed
@@ -68858,137 +83232,209 @@ case OP_FkIfZero: { /* jump */
** an integer.
*/
case OP_MemMax: { /* in2 */
-#if 0 /* local variables moved into u.cd */
- Mem *pIn1;
VdbeFrame *pFrame;
-#endif /* local variables moved into u.cd */
if( p->pFrame ){
- for(u.cd.pFrame=p->pFrame; u.cd.pFrame->pParent; u.cd.pFrame=u.cd.pFrame->pParent);
- u.cd.pIn1 = &u.cd.pFrame->aMem[pOp->p1];
+ for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent);
+ pIn1 = &pFrame->aMem[pOp->p1];
}else{
- u.cd.pIn1 = &aMem[pOp->p1];
+ pIn1 = &aMem[pOp->p1];
}
- assert( memIsValid(u.cd.pIn1) );
- sqlite3VdbeMemIntegerify(u.cd.pIn1);
+ assert( memIsValid(pIn1) );
+ sqlite3VdbeMemIntegerify(pIn1);
pIn2 = &aMem[pOp->p2];
sqlite3VdbeMemIntegerify(pIn2);
- if( u.cd.pIn1->u.i<pIn2->u.i){
- u.cd.pIn1->u.i = pIn2->u.i;
+ if( pIn1->u.i<pIn2->u.i){
+ pIn1->u.i = pIn2->u.i;
}
break;
}
#endif /* SQLITE_OMIT_AUTOINCREMENT */
-/* Opcode: IfPos P1 P2 * * *
+/* Opcode: IfPos P1 P2 P3 * *
+** Synopsis: if r[P1]>0 then r[P1]-=P3, goto P2
**
-** If the value of register P1 is 1 or greater, jump to P2.
+** Register P1 must contain an integer.
+** If the value of register P1 is 1 or greater, subtract P3 from the
+** value in P1 and jump to P2.
**
-** It is illegal to use this instruction on a register that does
-** not contain an integer. An assertion fault will result if you try.
+** If the initial value of register P1 is less than 1, then the
+** value is unchanged and control passes through to the next instruction.
*/
case OP_IfPos: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
assert( pIn1->flags&MEM_Int );
+ VdbeBranchTaken( pIn1->u.i>0, 2);
if( pIn1->u.i>0 ){
- pc = pOp->p2 - 1;
+ pIn1->u.i -= pOp->p3;
+ goto jump_to_p2;
}
break;
}
-/* Opcode: IfNeg P1 P2 * * *
+/* Opcode: OffsetLimit P1 P2 P3 * *
+** Synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)
**
-** If the value of register P1 is less than zero, jump to P2.
+** This opcode performs a commonly used computation associated with
+** LIMIT and OFFSET process. r[P1] holds the limit counter. r[P3]
+** holds the offset counter. The opcode computes the combined value
+** of the LIMIT and OFFSET and stores that value in r[P2]. The r[P2]
+** value computed is the total number of rows that will need to be
+** visited in order to complete the query.
**
-** It is illegal to use this instruction on a register that does
-** not contain an integer. An assertion fault will result if you try.
+** If r[P3] is zero or negative, that means there is no OFFSET
+** and r[P2] is set to be the value of the LIMIT, r[P1].
+**
+** if r[P1] is zero or negative, that means there is no LIMIT
+** and r[P2] is set to -1.
+**
+** Otherwise, r[P2] is set to the sum of r[P1] and r[P3].
+*/
+case OP_OffsetLimit: { /* in1, out2, in3 */
+ pIn1 = &aMem[pOp->p1];
+ pIn3 = &aMem[pOp->p3];
+ pOut = out2Prerelease(p, pOp);
+ assert( pIn1->flags & MEM_Int );
+ assert( pIn3->flags & MEM_Int );
+ pOut->u.i = pIn1->u.i<=0 ? -1 : pIn1->u.i+(pIn3->u.i>0?pIn3->u.i:0);
+ break;
+}
+
+/* Opcode: IfNotZero P1 P2 P3 * *
+** Synopsis: if r[P1]!=0 then r[P1]-=P3, goto P2
+**
+** Register P1 must contain an integer. If the content of register P1 is
+** initially nonzero, then subtract P3 from the value in register P1 and
+** jump to P2. If register P1 is initially zero, leave it unchanged
+** and fall through.
*/
-case OP_IfNeg: { /* jump, in1 */
+case OP_IfNotZero: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
assert( pIn1->flags&MEM_Int );
- if( pIn1->u.i<0 ){
- pc = pOp->p2 - 1;
+ VdbeBranchTaken(pIn1->u.i<0, 2);
+ if( pIn1->u.i ){
+ pIn1->u.i -= pOp->p3;
+ goto jump_to_p2;
}
break;
}
-/* Opcode: IfZero P1 P2 P3 * *
-**
-** The register P1 must contain an integer. Add literal P3 to the
-** value in register P1. If the result is exactly 0, jump to P2.
+/* Opcode: DecrJumpZero P1 P2 * * *
+** Synopsis: if (--r[P1])==0 goto P2
**
-** It is illegal to use this instruction on a register that does
-** not contain an integer. An assertion fault will result if you try.
+** Register P1 must hold an integer. Decrement the value in register P1
+** then jump to P2 if the new value is exactly zero.
*/
-case OP_IfZero: { /* jump, in1 */
+case OP_DecrJumpZero: { /* jump, in1 */
pIn1 = &aMem[pOp->p1];
assert( pIn1->flags&MEM_Int );
- pIn1->u.i += pOp->p3;
- if( pIn1->u.i==0 ){
- pc = pOp->p2 - 1;
- }
+ pIn1->u.i--;
+ VdbeBranchTaken(pIn1->u.i==0, 2);
+ if( pIn1->u.i==0 ) goto jump_to_p2;
break;
}
-/* Opcode: AggStep * P2 P3 P4 P5
+
+/* Opcode: AggStep0 * P2 P3 P4 P5
+** Synopsis: accum=r[P3] step(r[P2@P5])
**
** Execute the step function for an aggregate. The
** function has P5 arguments. P4 is a pointer to the FuncDef
-** structure that specifies the function. Use register
-** P3 as the accumulator.
+** structure that specifies the function. Register P3 is the
+** accumulator.
**
** The P5 arguments are taken from register P2 and its
** successors.
*/
-case OP_AggStep: {
-#if 0 /* local variables moved into u.ce */
+/* Opcode: AggStep * P2 P3 P4 P5
+** Synopsis: accum=r[P3] step(r[P2@P5])
+**
+** Execute the step function for an aggregate. The
+** function has P5 arguments. P4 is a pointer to an sqlite3_context
+** object that is used to run the function. Register P3 is
+** as the accumulator.
+**
+** The P5 arguments are taken from register P2 and its
+** successors.
+**
+** This opcode is initially coded as OP_AggStep0. On first evaluation,
+** the FuncDef stored in P4 is converted into an sqlite3_context and
+** the opcode is changed. In this way, the initialization of the
+** sqlite3_context only happens once, instead of on each call to the
+** step function.
+*/
+case OP_AggStep0: {
int n;
+ sqlite3_context *pCtx;
+
+ assert( pOp->p4type==P4_FUNCDEF );
+ n = pOp->p5;
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ assert( n==0 || (pOp->p2>0 && pOp->p2+n<=(p->nMem+1 - p->nCursor)+1) );
+ assert( pOp->p3<pOp->p2 || pOp->p3>=pOp->p2+n );
+ pCtx = sqlite3DbMallocRawNN(db, sizeof(*pCtx) + (n-1)*sizeof(sqlite3_value*));
+ if( pCtx==0 ) goto no_mem;
+ pCtx->pMem = 0;
+ pCtx->pFunc = pOp->p4.pFunc;
+ pCtx->iOp = (int)(pOp - aOp);
+ pCtx->pVdbe = p;
+ pCtx->argc = n;
+ pOp->p4type = P4_FUNCCTX;
+ pOp->p4.pCtx = pCtx;
+ pOp->opcode = OP_AggStep;
+ /* Fall through into OP_AggStep */
+}
+case OP_AggStep: {
int i;
+ sqlite3_context *pCtx;
Mem *pMem;
- Mem *pRec;
- sqlite3_context ctx;
- sqlite3_value **apVal;
-#endif /* local variables moved into u.ce */
-
- u.ce.n = pOp->p5;
- assert( u.ce.n>=0 );
- u.ce.pRec = &aMem[pOp->p2];
- u.ce.apVal = p->apArg;
- assert( u.ce.apVal || u.ce.n==0 );
- for(u.ce.i=0; u.ce.i<u.ce.n; u.ce.i++, u.ce.pRec++){
- assert( memIsValid(u.ce.pRec) );
- u.ce.apVal[u.ce.i] = u.ce.pRec;
- memAboutToChange(p, u.ce.pRec);
- sqlite3VdbeMemStoreType(u.ce.pRec);
- }
- u.ce.ctx.pFunc = pOp->p4.pFunc;
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.ce.ctx.pMem = u.ce.pMem = &aMem[pOp->p3];
- u.ce.pMem->n++;
- u.ce.ctx.s.flags = MEM_Null;
- u.ce.ctx.s.z = 0;
- u.ce.ctx.s.zMalloc = 0;
- u.ce.ctx.s.xDel = 0;
- u.ce.ctx.s.db = db;
- u.ce.ctx.isError = 0;
- u.ce.ctx.pColl = 0;
- if( u.ce.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
- assert( pOp>p->aOp );
- assert( pOp[-1].p4type==P4_COLLSEQ );
- assert( pOp[-1].opcode==OP_CollSeq );
- u.ce.ctx.pColl = pOp[-1].p4.pColl;
- }
- (u.ce.ctx.pFunc->xStep)(&u.ce.ctx, u.ce.n, u.ce.apVal); /* IMP: R-24505-23230 */
- if( u.ce.ctx.isError ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.ce.ctx.s));
- rc = u.ce.ctx.isError;
+ Mem t;
+
+ assert( pOp->p4type==P4_FUNCCTX );
+ pCtx = pOp->p4.pCtx;
+ pMem = &aMem[pOp->p3];
+
+ /* If this function is inside of a trigger, the register array in aMem[]
+ ** might change from one evaluation to the next. The next block of code
+ ** checks to see if the register array has changed, and if so it
+ ** reinitializes the relavant parts of the sqlite3_context object */
+ if( pCtx->pMem != pMem ){
+ pCtx->pMem = pMem;
+ for(i=pCtx->argc-1; i>=0; i--) pCtx->argv[i] = &aMem[pOp->p2+i];
}
- sqlite3VdbeMemRelease(&u.ce.ctx.s);
+#ifdef SQLITE_DEBUG
+ for(i=0; i<pCtx->argc; i++){
+ assert( memIsValid(pCtx->argv[i]) );
+ REGISTER_TRACE(pOp->p2+i, pCtx->argv[i]);
+ }
+#endif
+ pMem->n++;
+ sqlite3VdbeMemInit(&t, db, MEM_Null);
+ pCtx->pOut = &t;
+ pCtx->fErrorOrAux = 0;
+ pCtx->skipFlag = 0;
+ (pCtx->pFunc->xSFunc)(pCtx,pCtx->argc,pCtx->argv); /* IMP: R-24505-23230 */
+ if( pCtx->fErrorOrAux ){
+ if( pCtx->isError ){
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(&t));
+ rc = pCtx->isError;
+ }
+ sqlite3VdbeMemRelease(&t);
+ if( rc ) goto abort_due_to_error;
+ }else{
+ assert( t.flags==MEM_Null );
+ }
+ if( pCtx->skipFlag ){
+ assert( pOp[-1].opcode==OP_CollSeq );
+ i = pOp[-1].p1;
+ if( i ) sqlite3VdbeMemSetInt64(&aMem[i], 1);
+ }
break;
}
/* Opcode: AggFinal P1 P2 * P4 *
+** Synopsis: accum=r[P1] N=P2
**
** Execute the finalizer function for an aggregate. P1 is
** the memory location that is the accumulator for the aggregate.
@@ -69001,19 +83447,18 @@ case OP_AggStep: {
** the step function was not previously called.
*/
case OP_AggFinal: {
-#if 0 /* local variables moved into u.cf */
Mem *pMem;
-#endif /* local variables moved into u.cf */
- assert( pOp->p1>0 && pOp->p1<=p->nMem );
- u.cf.pMem = &aMem[pOp->p1];
- assert( (u.cf.pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
- rc = sqlite3VdbeMemFinalize(u.cf.pMem, pOp->p4.pFunc);
+ assert( pOp->p1>0 && pOp->p1<=(p->nMem+1 - p->nCursor) );
+ pMem = &aMem[pOp->p1];
+ assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
+ rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
if( rc ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.cf.pMem));
+ sqlite3VdbeError(p, "%s", sqlite3_value_text(pMem));
+ goto abort_due_to_error;
}
- sqlite3VdbeChangeEncoding(u.cf.pMem, encoding);
- UPDATE_MAX_BLOBSIZE(u.cf.pMem);
- if( sqlite3VdbeMemTooBig(u.cf.pMem) ){
+ sqlite3VdbeChangeEncoding(pMem, encoding);
+ UPDATE_MAX_BLOBSIZE(pMem);
+ if( sqlite3VdbeMemTooBig(pMem) ){
goto too_big;
}
break;
@@ -69023,8 +83468,8 @@ case OP_AggFinal: {
/* Opcode: Checkpoint P1 P2 P3 * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
-** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL
-** or RESTART. Write 1 or 0 into mem[P3] if the checkpoint returns
+** WAL mode. Parameter P2 is one of SQLITE_CHECKPOINT_PASSIVE, FULL,
+** RESTART, or TRUNCATE. Write 1 or 0 into mem[P3] if the checkpoint returns
** SQLITE_BUSY or not, respectively. Write the number of pages in the
** WAL after the checkpoint into mem[P3+1] and the number of pages
** in the WAL that have been checkpointed after the checkpoint
@@ -69032,32 +83477,33 @@ case OP_AggFinal: {
** mem[P3+2] are initialized to -1.
*/
case OP_Checkpoint: {
-#if 0 /* local variables moved into u.cg */
int i; /* Loop counter */
int aRes[3]; /* Results */
Mem *pMem; /* Write results here */
-#endif /* local variables moved into u.cg */
- u.cg.aRes[0] = 0;
- u.cg.aRes[1] = u.cg.aRes[2] = -1;
+ assert( p->readOnly==0 );
+ aRes[0] = 0;
+ aRes[1] = aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
|| pOp->p2==SQLITE_CHECKPOINT_FULL
|| pOp->p2==SQLITE_CHECKPOINT_RESTART
+ || pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
);
- rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &u.cg.aRes[1], &u.cg.aRes[2]);
- if( rc==SQLITE_BUSY ){
+ rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
+ if( rc ){
+ if( rc!=SQLITE_BUSY ) goto abort_due_to_error;
rc = SQLITE_OK;
- u.cg.aRes[0] = 1;
- }
- for(u.cg.i=0, u.cg.pMem = &aMem[pOp->p3]; u.cg.i<3; u.cg.i++, u.cg.pMem++){
- sqlite3VdbeMemSetInt64(u.cg.pMem, (i64)u.cg.aRes[u.cg.i]);
+ aRes[0] = 1;
}
+ for(i=0, pMem = &aMem[pOp->p3]; i<3; i++, pMem++){
+ sqlite3VdbeMemSetInt64(pMem, (i64)aRes[i]);
+ }
break;
};
#endif
#ifndef SQLITE_OMIT_PRAGMA
-/* Opcode: JournalMode P1 P2 P3 * P5
+/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the
** PAGER_JOURNALMODE_XXX values. If changing between the various rollback
@@ -69068,108 +83514,109 @@ case OP_Checkpoint: {
**
** Write a string containing the final journal-mode to register P2.
*/
-case OP_JournalMode: { /* out2-prerelease */
-#if 0 /* local variables moved into u.ch */
+case OP_JournalMode: { /* out2 */
Btree *pBt; /* Btree to change journal mode of */
Pager *pPager; /* Pager associated with pBt */
int eNew; /* New journal mode */
int eOld; /* The old journal mode */
+#ifndef SQLITE_OMIT_WAL
const char *zFilename; /* Name of database file for pPager */
-#endif /* local variables moved into u.ch */
-
- u.ch.eNew = pOp->p3;
- assert( u.ch.eNew==PAGER_JOURNALMODE_DELETE
- || u.ch.eNew==PAGER_JOURNALMODE_TRUNCATE
- || u.ch.eNew==PAGER_JOURNALMODE_PERSIST
- || u.ch.eNew==PAGER_JOURNALMODE_OFF
- || u.ch.eNew==PAGER_JOURNALMODE_MEMORY
- || u.ch.eNew==PAGER_JOURNALMODE_WAL
- || u.ch.eNew==PAGER_JOURNALMODE_QUERY
+#endif
+
+ pOut = out2Prerelease(p, pOp);
+ eNew = pOp->p3;
+ assert( eNew==PAGER_JOURNALMODE_DELETE
+ || eNew==PAGER_JOURNALMODE_TRUNCATE
+ || eNew==PAGER_JOURNALMODE_PERSIST
+ || eNew==PAGER_JOURNALMODE_OFF
+ || eNew==PAGER_JOURNALMODE_MEMORY
+ || eNew==PAGER_JOURNALMODE_WAL
+ || eNew==PAGER_JOURNALMODE_QUERY
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
+ assert( p->readOnly==0 );
- u.ch.pBt = db->aDb[pOp->p1].pBt;
- u.ch.pPager = sqlite3BtreePager(u.ch.pBt);
- u.ch.eOld = sqlite3PagerGetJournalMode(u.ch.pPager);
- if( u.ch.eNew==PAGER_JOURNALMODE_QUERY ) u.ch.eNew = u.ch.eOld;
- if( !sqlite3PagerOkToChangeJournalMode(u.ch.pPager) ) u.ch.eNew = u.ch.eOld;
+ pBt = db->aDb[pOp->p1].pBt;
+ pPager = sqlite3BtreePager(pBt);
+ eOld = sqlite3PagerGetJournalMode(pPager);
+ if( eNew==PAGER_JOURNALMODE_QUERY ) eNew = eOld;
+ if( !sqlite3PagerOkToChangeJournalMode(pPager) ) eNew = eOld;
#ifndef SQLITE_OMIT_WAL
- u.ch.zFilename = sqlite3PagerFilename(u.ch.pPager);
+ zFilename = sqlite3PagerFilename(pPager, 1);
/* Do not allow a transition to journal_mode=WAL for a database
- ** in temporary storage or if the VFS does not support shared memory
+ ** in temporary storage or if the VFS does not support shared memory
*/
- if( u.ch.eNew==PAGER_JOURNALMODE_WAL
- && (u.ch.zFilename[0]==0 /* Temp file */
- || !sqlite3PagerWalSupported(u.ch.pPager)) /* No shared-memory support */
+ if( eNew==PAGER_JOURNALMODE_WAL
+ && (sqlite3Strlen30(zFilename)==0 /* Temp file */
+ || !sqlite3PagerWalSupported(pPager)) /* No shared-memory support */
){
- u.ch.eNew = u.ch.eOld;
+ eNew = eOld;
}
- if( (u.ch.eNew!=u.ch.eOld)
- && (u.ch.eOld==PAGER_JOURNALMODE_WAL || u.ch.eNew==PAGER_JOURNALMODE_WAL)
+ if( (eNew!=eOld)
+ && (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL)
){
- if( !db->autoCommit || db->activeVdbeCnt>1 ){
+ if( !db->autoCommit || db->nVdbeRead>1 ){
rc = SQLITE_ERROR;
- sqlite3SetString(&p->zErrMsg, db,
+ sqlite3VdbeError(p,
"cannot change %s wal mode from within a transaction",
- (u.ch.eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
+ (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
);
- break;
+ goto abort_due_to_error;
}else{
-
- if( u.ch.eOld==PAGER_JOURNALMODE_WAL ){
+
+ if( eOld==PAGER_JOURNALMODE_WAL ){
/* If leaving WAL mode, close the log file. If successful, the call
- ** to PagerCloseWal() checkpoints and deletes the write-ahead-log
- ** file. An EXCLUSIVE lock may still be held on the database file
- ** after a successful return.
+ ** to PagerCloseWal() checkpoints and deletes the write-ahead-log
+ ** file. An EXCLUSIVE lock may still be held on the database file
+ ** after a successful return.
*/
- rc = sqlite3PagerCloseWal(u.ch.pPager);
+ rc = sqlite3PagerCloseWal(pPager);
if( rc==SQLITE_OK ){
- sqlite3PagerSetJournalMode(u.ch.pPager, u.ch.eNew);
+ sqlite3PagerSetJournalMode(pPager, eNew);
}
- }else if( u.ch.eOld==PAGER_JOURNALMODE_MEMORY ){
+ }else if( eOld==PAGER_JOURNALMODE_MEMORY ){
/* Cannot transition directly from MEMORY to WAL. Use mode OFF
** as an intermediate */
- sqlite3PagerSetJournalMode(u.ch.pPager, PAGER_JOURNALMODE_OFF);
+ sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF);
}
-
+
/* Open a transaction on the database file. Regardless of the journal
** mode, this transaction always uses a rollback journal.
*/
- assert( sqlite3BtreeIsInTrans(u.ch.pBt)==0 );
+ assert( sqlite3BtreeIsInTrans(pBt)==0 );
if( rc==SQLITE_OK ){
- rc = sqlite3BtreeSetVersion(u.ch.pBt, (u.ch.eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
+ rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
}
}
}
#endif /* ifndef SQLITE_OMIT_WAL */
- if( rc ){
- u.ch.eNew = u.ch.eOld;
- }
- u.ch.eNew = sqlite3PagerSetJournalMode(u.ch.pPager, u.ch.eNew);
+ if( rc ) eNew = eOld;
+ eNew = sqlite3PagerSetJournalMode(pPager, eNew);
- pOut = &aMem[pOp->p2];
pOut->flags = MEM_Str|MEM_Static|MEM_Term;
- pOut->z = (char *)sqlite3JournalModename(u.ch.eNew);
+ pOut->z = (char *)sqlite3JournalModename(eNew);
pOut->n = sqlite3Strlen30(pOut->z);
pOut->enc = SQLITE_UTF8;
sqlite3VdbeChangeEncoding(pOut, encoding);
+ if( rc ) goto abort_due_to_error;
break;
};
#endif /* SQLITE_OMIT_PRAGMA */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
-/* Opcode: Vacuum * * * * *
+/* Opcode: Vacuum P1 * * * *
**
-** Vacuum the entire database. This opcode will cause other virtual
-** machines to be created and run. It may not be called from within
-** a transaction.
+** Vacuum the entire database P1. P1 is 0 for "main", and 2 or more
+** for an attached database. The "temp" database may not be vacuumed.
*/
case OP_Vacuum: {
- rc = sqlite3RunVacuum(&p->zErrMsg, db);
+ assert( p->readOnly==0 );
+ rc = sqlite3RunVacuum(&p->zErrMsg, db, pOp->p1);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif
@@ -69182,17 +83629,18 @@ case OP_Vacuum: {
** P2. Otherwise, fall through to the next instruction.
*/
case OP_IncrVacuum: { /* jump */
-#if 0 /* local variables moved into u.ci */
Btree *pBt;
-#endif /* local variables moved into u.ci */
assert( pOp->p1>=0 && pOp->p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 );
- u.ci.pBt = db->aDb[pOp->p1].pBt;
- rc = sqlite3BtreeIncrVacuum(u.ci.pBt);
- if( rc==SQLITE_DONE ){
- pc = pOp->p2 - 1;
+ assert( DbMaskTest(p->btreeMask, pOp->p1) );
+ assert( p->readOnly==0 );
+ pBt = db->aDb[pOp->p1].pBt;
+ rc = sqlite3BtreeIncrVacuum(pBt);
+ VdbeBranchTaken(rc==SQLITE_DONE,2);
+ if( rc ){
+ if( rc!=SQLITE_DONE ) goto abort_due_to_error;
rc = SQLITE_OK;
+ goto jump_to_p2;
}
break;
}
@@ -69200,12 +83648,13 @@ case OP_IncrVacuum: { /* jump */
/* Opcode: Expire P1 * * * *
**
-** Cause precompiled statements to become expired. An expired statement
-** fails with an error code of SQLITE_SCHEMA if it is ever executed
-** (via sqlite3_step()).
+** Cause precompiled statements to expire. When an expired statement
+** is executed using sqlite3_step() it will either automatically
+** reprepare itself (if it was originally created using sqlite3_prepare_v2())
+** or it will fail with SQLITE_SCHEMA.
**
** If P1 is 0, then all SQL statements become expired. If P1 is non-zero,
-** then only the currently executing statement is affected.
+** then only the currently executing statement is expired.
*/
case OP_Expire: {
if( !pOp->p1 ){
@@ -69218,6 +83667,7 @@ case OP_Expire: {
#ifndef SQLITE_OMIT_SHARED_CACHE
/* Opcode: TableLock P1 P2 P3 P4 *
+** Synopsis: iDb=P1 root=P2 write=P3
**
** Obtain a lock on a particular table. This instruction is only used when
** the shared-cache feature is enabled.
@@ -69236,12 +83686,15 @@ case OP_TableLock: {
if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){
int p1 = pOp->p1;
assert( p1>=0 && p1<db->nDb );
- assert( (p->btreeMask & (((yDbMask)1)<<p1))!=0 );
+ assert( DbMaskTest(p->btreeMask, p1) );
assert( isWriteLock==0 || isWriteLock==1 );
rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock);
- if( (rc&0xFF)==SQLITE_LOCKED ){
- const char *z = pOp->p4.z;
- sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z);
+ if( rc ){
+ if( (rc&0xFF)==SQLITE_LOCKED ){
+ const char *z = pOp->p4.z;
+ sqlite3VdbeError(p, "database table is locked: %s", z);
+ }
+ goto abort_due_to_error;
}
}
break;
@@ -69259,24 +83712,40 @@ case OP_TableLock: {
** code will be set to SQLITE_LOCKED.
*/
case OP_VBegin: {
-#if 0 /* local variables moved into u.cj */
VTable *pVTab;
-#endif /* local variables moved into u.cj */
- u.cj.pVTab = pOp->p4.pVtab;
- rc = sqlite3VtabBegin(db, u.cj.pVTab);
- if( u.cj.pVTab ) importVtabErrMsg(p, u.cj.pVTab->pVtab);
+ pVTab = pOp->p4.pVtab;
+ rc = sqlite3VtabBegin(db, pVTab);
+ if( pVTab ) sqlite3VtabImportErrmsg(p, pVTab->pVtab);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VCreate P1 * * P4 *
+/* Opcode: VCreate P1 P2 * * *
**
-** P4 is the name of a virtual table in database P1. Call the xCreate method
-** for that table.
+** P2 is a register that holds the name of a virtual table in database
+** P1. Call the xCreate method for that table.
*/
case OP_VCreate: {
- rc = sqlite3VtabCallCreate(db, pOp->p1, pOp->p4.z, &p->zErrMsg);
+ Mem sMem; /* For storing the record being decoded */
+ const char *zTab; /* Name of the virtual table */
+
+ memset(&sMem, 0, sizeof(sMem));
+ sMem.db = db;
+ /* Because P2 is always a static string, it is impossible for the
+ ** sqlite3VdbeMemCopy() to fail */
+ assert( (aMem[pOp->p2].flags & MEM_Str)!=0 );
+ assert( (aMem[pOp->p2].flags & MEM_Static)!=0 );
+ rc = sqlite3VdbeMemCopy(&sMem, &aMem[pOp->p2]);
+ assert( rc==SQLITE_OK );
+ zTab = (const char*)sqlite3_value_text(&sMem);
+ assert( zTab || db->mallocFailed );
+ if( zTab ){
+ rc = sqlite3VtabCallCreate(db, pOp->p1, zTab, &p->zErrMsg);
+ }
+ sqlite3VdbeMemRelease(&sMem);
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -69288,9 +83757,10 @@ case OP_VCreate: {
** of that table.
*/
case OP_VDestroy: {
- p->inVtabMethod = 2;
+ db->nVDestroy++;
rc = sqlite3VtabCallDestroy(db, pOp->p1, pOp->p4.z);
- p->inVtabMethod = 0;
+ db->nVDestroy--;
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -69303,33 +83773,36 @@ case OP_VDestroy: {
** table and stores that cursor in P1.
*/
case OP_VOpen: {
-#if 0 /* local variables moved into u.ck */
VdbeCursor *pCur;
- sqlite3_vtab_cursor *pVtabCursor;
+ sqlite3_vtab_cursor *pVCur;
sqlite3_vtab *pVtab;
- sqlite3_module *pModule;
-#endif /* local variables moved into u.ck */
-
- u.ck.pCur = 0;
- u.ck.pVtabCursor = 0;
- u.ck.pVtab = pOp->p4.pVtab->pVtab;
- u.ck.pModule = (sqlite3_module *)u.ck.pVtab->pModule;
- assert(u.ck.pVtab && u.ck.pModule);
- rc = u.ck.pModule->xOpen(u.ck.pVtab, &u.ck.pVtabCursor);
- importVtabErrMsg(p, u.ck.pVtab);
- if( SQLITE_OK==rc ){
- /* Initialize sqlite3_vtab_cursor base class */
- u.ck.pVtabCursor->pVtab = u.ck.pVtab;
+ const sqlite3_module *pModule;
- /* Initialise vdbe cursor object */
- u.ck.pCur = allocateCursor(p, pOp->p1, 0, -1, 0);
- if( u.ck.pCur ){
- u.ck.pCur->pVtabCursor = u.ck.pVtabCursor;
- u.ck.pCur->pModule = u.ck.pVtabCursor->pVtab->pModule;
- }else{
- db->mallocFailed = 1;
- u.ck.pModule->xClose(u.ck.pVtabCursor);
- }
+ assert( p->bIsReader );
+ pCur = 0;
+ pVCur = 0;
+ pVtab = pOp->p4.pVtab->pVtab;
+ if( pVtab==0 || NEVER(pVtab->pModule==0) ){
+ rc = SQLITE_LOCKED;
+ goto abort_due_to_error;
+ }
+ pModule = pVtab->pModule;
+ rc = pModule->xOpen(pVtab, &pVCur);
+ sqlite3VtabImportErrmsg(p, pVtab);
+ if( rc ) goto abort_due_to_error;
+
+ /* Initialize sqlite3_vtab_cursor base class */
+ pVCur->pVtab = pVtab;
+
+ /* Initialize vdbe cursor object */
+ pCur = allocateCursor(p, pOp->p1, 0, -1, CURTYPE_VTAB);
+ if( pCur ){
+ pCur->uc.pVCur = pVCur;
+ pVtab->nRef++;
+ }else{
+ assert( db->mallocFailed );
+ pModule->xClose(pVCur);
+ goto no_mem;
}
break;
}
@@ -69337,6 +83810,7 @@ case OP_VOpen: {
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VFilter P1 P2 P3 P4 *
+** Synopsis: iplan=r[P3] zplan='P4'
**
** P1 is a cursor opened using VOpen. P2 is an address to jump to if
** the filtered result set is empty.
@@ -69355,117 +83829,92 @@ case OP_VOpen: {
** A jump is made to P2 if the result set after filtering would be empty.
*/
case OP_VFilter: { /* jump */
-#if 0 /* local variables moved into u.cl */
int nArg;
int iQuery;
const sqlite3_module *pModule;
Mem *pQuery;
Mem *pArgc;
- sqlite3_vtab_cursor *pVtabCursor;
+ sqlite3_vtab_cursor *pVCur;
sqlite3_vtab *pVtab;
VdbeCursor *pCur;
int res;
int i;
Mem **apArg;
-#endif /* local variables moved into u.cl */
-
- u.cl.pQuery = &aMem[pOp->p3];
- u.cl.pArgc = &u.cl.pQuery[1];
- u.cl.pCur = p->apCsr[pOp->p1];
- assert( memIsValid(u.cl.pQuery) );
- REGISTER_TRACE(pOp->p3, u.cl.pQuery);
- assert( u.cl.pCur->pVtabCursor );
- u.cl.pVtabCursor = u.cl.pCur->pVtabCursor;
- u.cl.pVtab = u.cl.pVtabCursor->pVtab;
- u.cl.pModule = u.cl.pVtab->pModule;
+
+ pQuery = &aMem[pOp->p3];
+ pArgc = &pQuery[1];
+ pCur = p->apCsr[pOp->p1];
+ assert( memIsValid(pQuery) );
+ REGISTER_TRACE(pOp->p3, pQuery);
+ assert( pCur->eCurType==CURTYPE_VTAB );
+ pVCur = pCur->uc.pVCur;
+ pVtab = pVCur->pVtab;
+ pModule = pVtab->pModule;
/* Grab the index number and argc parameters */
- assert( (u.cl.pQuery->flags&MEM_Int)!=0 && u.cl.pArgc->flags==MEM_Int );
- u.cl.nArg = (int)u.cl.pArgc->u.i;
- u.cl.iQuery = (int)u.cl.pQuery->u.i;
+ assert( (pQuery->flags&MEM_Int)!=0 && pArgc->flags==MEM_Int );
+ nArg = (int)pArgc->u.i;
+ iQuery = (int)pQuery->u.i;
/* Invoke the xFilter method */
- {
- u.cl.res = 0;
- u.cl.apArg = p->apArg;
- for(u.cl.i = 0; u.cl.i<u.cl.nArg; u.cl.i++){
- u.cl.apArg[u.cl.i] = &u.cl.pArgc[u.cl.i+1];
- sqlite3VdbeMemStoreType(u.cl.apArg[u.cl.i]);
- }
-
- p->inVtabMethod = 1;
- rc = u.cl.pModule->xFilter(u.cl.pVtabCursor, u.cl.iQuery, pOp->p4.z, u.cl.nArg, u.cl.apArg);
- p->inVtabMethod = 0;
- importVtabErrMsg(p, u.cl.pVtab);
- if( rc==SQLITE_OK ){
- u.cl.res = u.cl.pModule->xEof(u.cl.pVtabCursor);
- }
-
- if( u.cl.res ){
- pc = pOp->p2 - 1;
- }
- }
- u.cl.pCur->nullRow = 0;
-
+ res = 0;
+ apArg = p->apArg;
+ for(i = 0; i<nArg; i++){
+ apArg[i] = &pArgc[i+1];
+ }
+ rc = pModule->xFilter(pVCur, iQuery, pOp->p4.z, nArg, apArg);
+ sqlite3VtabImportErrmsg(p, pVtab);
+ if( rc ) goto abort_due_to_error;
+ res = pModule->xEof(pVCur);
+ pCur->nullRow = 0;
+ VdbeBranchTaken(res!=0,2);
+ if( res ) goto jump_to_p2;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Opcode: VColumn P1 P2 P3 * *
+** Synopsis: r[P3]=vcolumn(P2)
**
** Store the value of the P2-th column of
** the row of the virtual-table that the
** P1 cursor is pointing to into register P3.
*/
case OP_VColumn: {
-#if 0 /* local variables moved into u.cm */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
Mem *pDest;
sqlite3_context sContext;
-#endif /* local variables moved into u.cm */
VdbeCursor *pCur = p->apCsr[pOp->p1];
- assert( pCur->pVtabCursor );
- assert( pOp->p3>0 && pOp->p3<=p->nMem );
- u.cm.pDest = &aMem[pOp->p3];
- memAboutToChange(p, u.cm.pDest);
+ assert( pCur->eCurType==CURTYPE_VTAB );
+ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
+ pDest = &aMem[pOp->p3];
+ memAboutToChange(p, pDest);
if( pCur->nullRow ){
- sqlite3VdbeMemSetNull(u.cm.pDest);
+ sqlite3VdbeMemSetNull(pDest);
break;
}
- u.cm.pVtab = pCur->pVtabCursor->pVtab;
- u.cm.pModule = u.cm.pVtab->pModule;
- assert( u.cm.pModule->xColumn );
- memset(&u.cm.sContext, 0, sizeof(u.cm.sContext));
-
- /* The output cell may already have a buffer allocated. Move
- ** the current contents to u.cm.sContext.s so in case the user-function
- ** can use the already allocated buffer instead of allocating a
- ** new one.
- */
- sqlite3VdbeMemMove(&u.cm.sContext.s, u.cm.pDest);
- MemSetTypeFlag(&u.cm.sContext.s, MEM_Null);
-
- rc = u.cm.pModule->xColumn(pCur->pVtabCursor, &u.cm.sContext, pOp->p2);
- importVtabErrMsg(p, u.cm.pVtab);
- if( u.cm.sContext.isError ){
- rc = u.cm.sContext.isError;
- }
-
- /* Copy the result of the function to the P3 register. We
- ** do this regardless of whether or not an error occurred to ensure any
- ** dynamic allocation in u.cm.sContext.s (a Mem struct) is released.
- */
- sqlite3VdbeChangeEncoding(&u.cm.sContext.s, encoding);
- sqlite3VdbeMemMove(u.cm.pDest, &u.cm.sContext.s);
- REGISTER_TRACE(pOp->p3, u.cm.pDest);
- UPDATE_MAX_BLOBSIZE(u.cm.pDest);
-
- if( sqlite3VdbeMemTooBig(u.cm.pDest) ){
+ pVtab = pCur->uc.pVCur->pVtab;
+ pModule = pVtab->pModule;
+ assert( pModule->xColumn );
+ memset(&sContext, 0, sizeof(sContext));
+ sContext.pOut = pDest;
+ MemSetTypeFlag(pDest, MEM_Null);
+ rc = pModule->xColumn(pCur->uc.pVCur, &sContext, pOp->p2);
+ sqlite3VtabImportErrmsg(p, pVtab);
+ if( sContext.isError ){
+ rc = sContext.isError;
+ }
+ sqlite3VdbeChangeEncoding(pDest, encoding);
+ REGISTER_TRACE(pOp->p3, pDest);
+ UPDATE_MAX_BLOBSIZE(pDest);
+
+ if( sqlite3VdbeMemTooBig(pDest) ){
goto too_big;
}
+ if( rc ) goto abort_due_to_error;
break;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -69478,42 +83927,37 @@ case OP_VColumn: {
** the end of its result set, then fall through to the next instruction.
*/
case OP_VNext: { /* jump */
-#if 0 /* local variables moved into u.cn */
sqlite3_vtab *pVtab;
const sqlite3_module *pModule;
int res;
VdbeCursor *pCur;
-#endif /* local variables moved into u.cn */
- u.cn.res = 0;
- u.cn.pCur = p->apCsr[pOp->p1];
- assert( u.cn.pCur->pVtabCursor );
- if( u.cn.pCur->nullRow ){
+ res = 0;
+ pCur = p->apCsr[pOp->p1];
+ assert( pCur->eCurType==CURTYPE_VTAB );
+ if( pCur->nullRow ){
break;
}
- u.cn.pVtab = u.cn.pCur->pVtabCursor->pVtab;
- u.cn.pModule = u.cn.pVtab->pModule;
- assert( u.cn.pModule->xNext );
+ pVtab = pCur->uc.pVCur->pVtab;
+ pModule = pVtab->pModule;
+ assert( pModule->xNext );
/* Invoke the xNext() method of the module. There is no way for the
** underlying implementation to return an error if one occurs during
- ** xNext(). Instead, if an error occurs, true is returned (indicating that
+ ** xNext(). Instead, if an error occurs, true is returned (indicating that
** data is available) and the error code returned when xColumn or
** some other method is next invoked on the save virtual table cursor.
*/
- p->inVtabMethod = 1;
- rc = u.cn.pModule->xNext(u.cn.pCur->pVtabCursor);
- p->inVtabMethod = 0;
- importVtabErrMsg(p, u.cn.pVtab);
- if( rc==SQLITE_OK ){
- u.cn.res = u.cn.pModule->xEof(u.cn.pCur->pVtabCursor);
- }
-
- if( !u.cn.res ){
+ rc = pModule->xNext(pCur->uc.pVCur);
+ sqlite3VtabImportErrmsg(p, pVtab);
+ if( rc ) goto abort_due_to_error;
+ res = pModule->xEof(pCur->uc.pVCur);
+ VdbeBranchTaken(!res,2);
+ if( !res ){
/* If there is data, jump to P2 */
- pc = pOp->p2 - 1;
+ goto jump_to_p2_and_check_for_interrupt;
}
- break;
+ goto check_for_interrupt;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -69525,27 +83969,32 @@ case OP_VNext: { /* jump */
** in register P1 is passed as the zName argument to the xRename method.
*/
case OP_VRename: {
-#if 0 /* local variables moved into u.co */
sqlite3_vtab *pVtab;
Mem *pName;
-#endif /* local variables moved into u.co */
-
- u.co.pVtab = pOp->p4.pVtab->pVtab;
- u.co.pName = &aMem[pOp->p1];
- assert( u.co.pVtab->pModule->xRename );
- assert( memIsValid(u.co.pName) );
- REGISTER_TRACE(pOp->p1, u.co.pName);
- assert( u.co.pName->flags & MEM_Str );
- rc = u.co.pVtab->pModule->xRename(u.co.pVtab, u.co.pName->z);
- importVtabErrMsg(p, u.co.pVtab);
- p->expired = 0;
+ pVtab = pOp->p4.pVtab->pVtab;
+ pName = &aMem[pOp->p1];
+ assert( pVtab->pModule->xRename );
+ assert( memIsValid(pName) );
+ assert( p->readOnly==0 );
+ REGISTER_TRACE(pOp->p1, pName);
+ assert( pName->flags & MEM_Str );
+ testcase( pName->enc==SQLITE_UTF8 );
+ testcase( pName->enc==SQLITE_UTF16BE );
+ testcase( pName->enc==SQLITE_UTF16LE );
+ rc = sqlite3VdbeChangeEncoding(pName, SQLITE_UTF8);
+ if( rc ) goto abort_due_to_error;
+ rc = pVtab->pModule->xRename(pVtab, pName->z);
+ sqlite3VtabImportErrmsg(p, pVtab);
+ p->expired = 0;
+ if( rc ) goto abort_due_to_error;
break;
}
#endif
#ifndef SQLITE_OMIT_VIRTUALTABLE
-/* Opcode: VUpdate P1 P2 P3 P4 *
+/* Opcode: VUpdate P1 P2 P3 P4 P5
+** Synopsis: data=r[P3@P2]
**
** P4 is a pointer to a virtual table object, an sqlite3_vtab structure.
** This opcode invokes the corresponding xUpdate method. P2 values
@@ -69567,45 +84016,50 @@ case OP_VRename: {
** P1 is a boolean flag. If it is set to true and the xUpdate call
** is successful, then the value returned by sqlite3_last_insert_rowid()
** is set to the value of the rowid for the row just inserted.
+**
+** P5 is the error actions (OE_Replace, OE_Fail, OE_Ignore, etc) to
+** apply in the case of a constraint failure on an insert or update.
*/
case OP_VUpdate: {
-#if 0 /* local variables moved into u.cp */
sqlite3_vtab *pVtab;
- sqlite3_module *pModule;
+ const sqlite3_module *pModule;
int nArg;
int i;
sqlite_int64 rowid;
Mem **apArg;
Mem *pX;
-#endif /* local variables moved into u.cp */
- assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
+ assert( pOp->p2==1 || pOp->p5==OE_Fail || pOp->p5==OE_Rollback
|| pOp->p5==OE_Abort || pOp->p5==OE_Ignore || pOp->p5==OE_Replace
);
- u.cp.pVtab = pOp->p4.pVtab->pVtab;
- u.cp.pModule = (sqlite3_module *)u.cp.pVtab->pModule;
- u.cp.nArg = pOp->p2;
+ assert( p->readOnly==0 );
+ pVtab = pOp->p4.pVtab->pVtab;
+ if( pVtab==0 || NEVER(pVtab->pModule==0) ){
+ rc = SQLITE_LOCKED;
+ goto abort_due_to_error;
+ }
+ pModule = pVtab->pModule;
+ nArg = pOp->p2;
assert( pOp->p4type==P4_VTAB );
- if( ALWAYS(u.cp.pModule->xUpdate) ){
+ if( ALWAYS(pModule->xUpdate) ){
u8 vtabOnConflict = db->vtabOnConflict;
- u.cp.apArg = p->apArg;
- u.cp.pX = &aMem[pOp->p3];
- for(u.cp.i=0; u.cp.i<u.cp.nArg; u.cp.i++){
- assert( memIsValid(u.cp.pX) );
- memAboutToChange(p, u.cp.pX);
- sqlite3VdbeMemStoreType(u.cp.pX);
- u.cp.apArg[u.cp.i] = u.cp.pX;
- u.cp.pX++;
+ apArg = p->apArg;
+ pX = &aMem[pOp->p3];
+ for(i=0; i<nArg; i++){
+ assert( memIsValid(pX) );
+ memAboutToChange(p, pX);
+ apArg[i] = pX;
+ pX++;
}
db->vtabOnConflict = pOp->p5;
- rc = u.cp.pModule->xUpdate(u.cp.pVtab, u.cp.nArg, u.cp.apArg, &u.cp.rowid);
+ rc = pModule->xUpdate(pVtab, nArg, apArg, &rowid);
db->vtabOnConflict = vtabOnConflict;
- importVtabErrMsg(p, u.cp.pVtab);
+ sqlite3VtabImportErrmsg(p, pVtab);
if( rc==SQLITE_OK && pOp->p1 ){
- assert( u.cp.nArg>1 && u.cp.apArg[0] && (u.cp.apArg[0]->flags&MEM_Null) );
- db->lastRowid = lastRowid = u.cp.rowid;
+ assert( nArg>1 && apArg[0] && (apArg[0]->flags&MEM_Null) );
+ db->lastRowid = lastRowid = rowid;
}
- if( rc==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){
+ if( (rc&0xff)==SQLITE_CONSTRAINT && pOp->p4.pVtab->bConstraint ){
if( pOp->p5==OE_Ignore ){
rc = SQLITE_OK;
}else{
@@ -69614,6 +84068,7 @@ case OP_VUpdate: {
}else{
p->nChange++;
}
+ if( rc ) goto abort_due_to_error;
}
break;
}
@@ -69624,7 +84079,8 @@ case OP_VUpdate: {
**
** Write the current number of pages in database P1 to memory cell P2.
*/
-case OP_Pagecount: { /* out2-prerelease */
+case OP_Pagecount: { /* out2 */
+ pOut = out2Prerelease(p, pOp);
pOut->u.i = sqlite3BtreeLastPage(db->aDb[pOp->p1].pBt);
break;
}
@@ -69640,10 +84096,11 @@ case OP_Pagecount: { /* out2-prerelease */
**
** Store the maximum page count after the change in register P2.
*/
-case OP_MaxPgcnt: { /* out2-prerelease */
+case OP_MaxPgcnt: { /* out2 */
unsigned int newMax;
Btree *pBt;
+ pOut = out2Prerelease(p, pOp);
pBt = db->aDb[pOp->p1].pBt;
newMax = 0;
if( pOp->p3 ){
@@ -69656,34 +84113,105 @@ case OP_MaxPgcnt: { /* out2-prerelease */
#endif
-#ifndef SQLITE_OMIT_TRACE
-/* Opcode: Trace * * * P4 *
+/* Opcode: Init P1 P2 * P4 *
+** Synopsis: Start at P2
+**
+** Programs contain a single instance of this opcode as the very first
+** opcode.
**
** If tracing is enabled (by the sqlite3_trace()) interface, then
** the UTF-8 string contained in P4 is emitted on the trace callback.
+** Or if P4 is blank, use the string returned by sqlite3_sql().
+**
+** If P2 is not zero, jump to instruction P2.
+**
+** Increment the value of P1 so that OP_Once opcodes will jump the
+** first time they are evaluated for this run.
*/
-case OP_Trace: {
-#if 0 /* local variables moved into u.cq */
+case OP_Init: { /* jump */
char *zTrace;
- char *z;
-#endif /* local variables moved into u.cq */
+ int i;
- if( db->xTrace && (u.cq.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0 ){
- u.cq.z = sqlite3VdbeExpandSql(p, u.cq.zTrace);
- db->xTrace(db->pTraceArg, u.cq.z);
- sqlite3DbFree(db, u.cq.z);
+ /* If the P4 argument is not NULL, then it must be an SQL comment string.
+ ** The "--" string is broken up to prevent false-positives with srcck1.c.
+ **
+ ** This assert() provides evidence for:
+ ** EVIDENCE-OF: R-50676-09860 The callback can compute the same text that
+ ** would have been returned by the legacy sqlite3_trace() interface by
+ ** using the X argument when X begins with "--" and invoking
+ ** sqlite3_expanded_sql(P) otherwise.
+ */
+ assert( pOp->p4.z==0 || strncmp(pOp->p4.z, "-" "- ", 3)==0 );
+ assert( pOp==p->aOp ); /* Always instruction 0 */
+
+#ifndef SQLITE_OMIT_TRACE
+ if( (db->mTrace & (SQLITE_TRACE_STMT|SQLITE_TRACE_LEGACY))!=0
+ && !p->doingRerun
+ && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
+ ){
+#ifndef SQLITE_OMIT_DEPRECATED
+ if( db->mTrace & SQLITE_TRACE_LEGACY ){
+ void (*x)(void*,const char*) = (void(*)(void*,const char*))db->xTrace;
+ char *z = sqlite3VdbeExpandSql(p, zTrace);
+ x(db->pTraceArg, z);
+ sqlite3_free(z);
+ }else
+#endif
+ {
+ (void)db->xTrace(SQLITE_TRACE_STMT, db->pTraceArg, p, zTrace);
+ }
+ }
+#ifdef SQLITE_USE_FCNTL_TRACE
+ zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql);
+ if( zTrace ){
+ int j;
+ for(j=0; j<db->nDb; j++){
+ if( DbMaskTest(p->btreeMask, j)==0 ) continue;
+ sqlite3_file_control(db, db->aDb[j].zDbSName, SQLITE_FCNTL_TRACE, zTrace);
+ }
}
+#endif /* SQLITE_USE_FCNTL_TRACE */
#ifdef SQLITE_DEBUG
if( (db->flags & SQLITE_SqlTrace)!=0
- && (u.cq.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
+ && (zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql))!=0
){
- sqlite3DebugPrintf("SQL-trace: %s\n", u.cq.zTrace);
+ sqlite3DebugPrintf("SQL-trace: %s\n", zTrace);
}
#endif /* SQLITE_DEBUG */
- break;
+#endif /* SQLITE_OMIT_TRACE */
+ assert( pOp->p2>0 );
+ if( pOp->p1>=sqlite3GlobalConfig.iOnceResetThreshold ){
+ for(i=1; i<p->nOp; i++){
+ if( p->aOp[i].opcode==OP_Once ) p->aOp[i].p1 = 0;
+ }
+ pOp->p1 = 0;
+ }
+ pOp->p1++;
+ goto jump_to_p2;
}
-#endif
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/* Opcode: CursorHint P1 * * P4 *
+**
+** Provide a hint to cursor P1 that it only needs to return rows that
+** satisfy the Expr in P4. TK_REGISTER terms in the P4 expression refer
+** to values currently held in registers. TK_COLUMN terms in the P4
+** expression refer to columns in the b-tree to which cursor P1 is pointing.
+*/
+case OP_CursorHint: {
+ VdbeCursor *pC;
+
+ assert( pOp->p1>=0 && pOp->p1<p->nCursor );
+ assert( pOp->p4type==P4_EXPR );
+ pC = p->apCsr[pOp->p1];
+ if( pC ){
+ assert( pC->eCurType==CURTYPE_BTREE );
+ sqlite3BtreeCursorHint(pC->uc.pCursor, BTREE_HINT_RANGE,
+ pOp->p4.pExpr, aMem);
+ }
+ break;
+}
+#endif /* SQLITE_ENABLE_CURSOR_HINTS */
/* Opcode: Noop * * * * *
**
@@ -69711,13 +84239,9 @@ default: { /* This is really OP_Noop and OP_Explain */
#ifdef VDBE_PROFILE
{
- u64 elapsed = sqlite3Hwtime() - start;
- pOp->cycles += elapsed;
- pOp->cnt++;
-#if 0
- fprintf(stdout, "%10llu ", elapsed);
- sqlite3VdbePrintOp(stdout, origPc, &aOp[origPc]);
-#endif
+ u64 endTime = sqlite3Hwtime();
+ if( endTime>start ) pOrigOp->cycles += endTime - start;
+ pOrigOp->cnt++;
}
#endif
@@ -69727,16 +84251,17 @@ default: { /* This is really OP_Noop and OP_Explain */
** the evaluator loop. So we can leave it out when NDEBUG is defined.
*/
#ifndef NDEBUG
- assert( pc>=-1 && pc<p->nOp );
+ assert( pOp>=&aOp[-1] && pOp<&aOp[p->nOp-1] );
#ifdef SQLITE_DEBUG
- if( p->trace ){
- if( rc!=0 ) fprintf(p->trace,"rc=%d\n",rc);
- if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){
- registerTrace(p->trace, pOp->p2, &aMem[pOp->p2]);
+ if( db->flags & SQLITE_VdbeTrace ){
+ u8 opProperty = sqlite3OpcodeProperty[pOrigOp->opcode];
+ if( rc!=0 ) printf("rc=%d\n",rc);
+ if( opProperty & (OPFLG_OUT2) ){
+ registerTrace(pOrigOp->p2, &aMem[pOrigOp->p2]);
}
- if( pOp->opflags & OPFLG_OUT3 ){
- registerTrace(p->trace, pOp->p3, &aMem[pOp->p3]);
+ if( opProperty & OPFLG_OUT3 ){
+ registerTrace(pOrigOp->p3, &aMem[pOrigOp->p3]);
}
}
#endif /* SQLITE_DEBUG */
@@ -69746,17 +84271,22 @@ default: { /* This is really OP_Noop and OP_Explain */
/* If we reach this point, it means that execution is finished with
** an error of some kind.
*/
-vdbe_error_halt:
+abort_due_to_error:
+ if( db->mallocFailed ) rc = SQLITE_NOMEM_BKPT;
assert( rc );
+ if( p->zErrMsg==0 && rc!=SQLITE_IOERR_NOMEM ){
+ sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
+ }
p->rc = rc;
+ sqlite3SystemError(db, rc);
testcase( sqlite3GlobalConfig.xLog!=0 );
sqlite3_log(rc, "statement aborts at %d: [%s] %s",
- pc, p->zSql, p->zErrMsg);
+ (int)(pOp - aOp), p->zSql, p->zErrMsg);
sqlite3VdbeHalt(p);
- if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
rc = SQLITE_ERROR;
if( resetSchemaOnFault>0 ){
- sqlite3ResetInternalSchema(db, resetSchemaOnFault-1);
+ sqlite3ResetOneSchema(db, resetSchemaOnFault-1);
}
/* This is the only way out of this procedure. We have to
@@ -69764,47 +84294,42 @@ vdbe_error_halt:
** top. */
vdbe_return:
db->lastRowid = lastRowid;
+ testcase( nVmStep>0 );
+ p->aCounter[SQLITE_STMTSTATUS_VM_STEP] += (int)nVmStep;
sqlite3VdbeLeave(p);
+ assert( rc!=SQLITE_OK || nExtraDelete==0
+ || sqlite3_strlike("DELETE%",p->zSql,0)!=0
+ );
return rc;
/* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH
** is encountered.
*/
too_big:
- sqlite3SetString(&p->zErrMsg, db, "string or blob too big");
+ sqlite3VdbeError(p, "string or blob too big");
rc = SQLITE_TOOBIG;
- goto vdbe_error_halt;
+ goto abort_due_to_error;
/* Jump to here if a malloc() fails.
*/
no_mem:
- db->mallocFailed = 1;
- sqlite3SetString(&p->zErrMsg, db, "out of memory");
- rc = SQLITE_NOMEM;
- goto vdbe_error_halt;
-
- /* Jump to here for any other kind of fatal error. The "rc" variable
- ** should hold the error number.
- */
-abort_due_to_error:
- assert( p->zErrMsg==0 );
- if( db->mallocFailed ) rc = SQLITE_NOMEM;
- if( rc!=SQLITE_IOERR_NOMEM ){
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
- }
- goto vdbe_error_halt;
+ sqlite3OomFault(db);
+ sqlite3VdbeError(p, "out of memory");
+ rc = SQLITE_NOMEM_BKPT;
+ goto abort_due_to_error;
/* Jump to here if the sqlite3_interrupt() API sets the interrupt
** flag.
*/
abort_due_to_interrupt:
assert( db->u1.isInterrupted );
- rc = SQLITE_INTERRUPT;
+ rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
p->rc = rc;
- sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(rc));
- goto vdbe_error_halt;
+ sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
+ goto abort_due_to_error;
}
+
/************** End of vdbe.c ************************************************/
/************** Begin file vdbeblob.c ****************************************/
/*
@@ -69822,6 +84347,8 @@ abort_due_to_interrupt:
** This file contains code used to implement incremental BLOB I/O.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#ifndef SQLITE_OMIT_INCRBLOB
@@ -69837,6 +84364,8 @@ struct Incrblob {
BtCursor *pCsr; /* Cursor pointing at blob row */
sqlite3_stmt *pStmt; /* Statement holding cursor open */
sqlite3 *db; /* The associated database */
+ char *zDb; /* Database name */
+ Table *pTab; /* Table object */
};
@@ -69871,7 +84400,8 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
rc = sqlite3_step(p->pStmt);
if( rc==SQLITE_ROW ){
- u32 type = v->apCsr[0]->aType[p->iCol];
+ VdbeCursor *pC = v->apCsr[0];
+ u32 type = pC->aType[p->iCol];
if( type<12 ){
zErr = sqlite3MPrintf(p->db, "cannot open value of type %s",
type==0?"null": type==7?"real": "integer"
@@ -69880,12 +84410,10 @@ static int blobSeekToRow(Incrblob *p, sqlite3_int64 iRow, char **pzErr){
sqlite3_finalize(p->pStmt);
p->pStmt = 0;
}else{
- p->iOffset = v->apCsr[0]->aOffset[p->iCol];
+ p->iOffset = pC->aType[p->iCol + pC->nField];
p->nByte = sqlite3VdbeSerialTypeLen(type);
- p->pCsr = v->apCsr[0]->pCursor;
- sqlite3BtreeEnterCursor(p->pCsr);
- sqlite3BtreeCacheOverflow(p->pCsr);
- sqlite3BtreeLeaveCursor(p->pCsr);
+ p->pCsr = pC->uc.pCursor;
+ sqlite3BtreeIncrblobCursor(p->pCsr);
}
}
@@ -69923,48 +84451,24 @@ SQLITE_API int sqlite3_blob_open(
){
int nAttempt = 0;
int iCol; /* Index of zColumn in row-record */
-
- /* This VDBE program seeks a btree cursor to the identified
- ** db/table/row entry. The reason for using a vdbe program instead
- ** of writing code to use the b-tree layer directly is that the
- ** vdbe program will take advantage of the various transaction,
- ** locking and error handling infrastructure built into the vdbe.
- **
- ** After seeking the cursor, the vdbe executes an OP_ResultRow.
- ** Code external to the Vdbe then "borrows" the b-tree cursor and
- ** uses it to implement the blob_read(), blob_write() and
- ** blob_bytes() functions.
- **
- ** The sqlite3_blob_close() function finalizes the vdbe program,
- ** which closes the b-tree cursor and (possibly) commits the
- ** transaction.
- */
- static const VdbeOpList openBlob[] = {
- {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */
- {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */
- {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */
-
- /* One of the following two instructions is replaced by an OP_Noop. */
- {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */
- {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */
-
- {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */
- {OP_NotExists, 0, 10, 1}, /* 6: Seek the cursor */
- {OP_Column, 0, 0, 1}, /* 7 */
- {OP_ResultRow, 1, 0, 0}, /* 8 */
- {OP_Goto, 0, 5, 0}, /* 9 */
- {OP_Close, 0, 0, 0}, /* 10 */
- {OP_Halt, 0, 0, 0}, /* 11 */
- };
-
int rc = SQLITE_OK;
char *zErr = 0;
Table *pTab;
Parse *pParse = 0;
Incrblob *pBlob = 0;
- flags = !!flags; /* flags = (flags ? 1 : 0); */
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( ppBlob==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
*ppBlob = 0;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zTable==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ flags = !!flags; /* flags = (flags ? 1 : 0); */
sqlite3_mutex_enter(db->mutex);
@@ -69985,6 +84489,10 @@ SQLITE_API int sqlite3_blob_open(
pTab = 0;
sqlite3ErrorMsg(pParse, "cannot open virtual table: %s", zTable);
}
+ if( pTab && !HasRowid(pTab) ){
+ pTab = 0;
+ sqlite3ErrorMsg(pParse, "cannot open table without rowid: %s", zTable);
+ }
#ifndef SQLITE_OMIT_VIEW
if( pTab && pTab->pSelect ){
pTab = 0;
@@ -70001,6 +84509,8 @@ SQLITE_API int sqlite3_blob_open(
sqlite3BtreeLeaveAll(db);
goto blob_open_out;
}
+ pBlob->pTab = pTab;
+ pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
/* Now search pTab for the exact column. */
for(iCol=0; iCol<pTab->nCol; iCol++) {
@@ -70042,8 +84552,9 @@ SQLITE_API int sqlite3_blob_open(
#endif
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int j;
- for(j=0; j<pIdx->nColumn; j++){
- if( pIdx->aiColumn[j]==iCol ){
+ for(j=0; j<pIdx->nKeyCol; j++){
+ /* FIXME: Be smarter about indexes that use expressions */
+ if( pIdx->aiColumn[j]==iCol || pIdx->aiColumn[j]==XN_EXPR ){
zFault = "indexed";
}
}
@@ -70057,53 +84568,81 @@ SQLITE_API int sqlite3_blob_open(
}
}
- pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(db);
+ pBlob->pStmt = (sqlite3_stmt *)sqlite3VdbeCreate(pParse);
assert( pBlob->pStmt || db->mallocFailed );
if( pBlob->pStmt ){
+
+ /* This VDBE program seeks a btree cursor to the identified
+ ** db/table/row entry. The reason for using a vdbe program instead
+ ** of writing code to use the b-tree layer directly is that the
+ ** vdbe program will take advantage of the various transaction,
+ ** locking and error handling infrastructure built into the vdbe.
+ **
+ ** After seeking the cursor, the vdbe executes an OP_ResultRow.
+ ** Code external to the Vdbe then "borrows" the b-tree cursor and
+ ** uses it to implement the blob_read(), blob_write() and
+ ** blob_bytes() functions.
+ **
+ ** The sqlite3_blob_close() function finalizes the vdbe program,
+ ** which closes the b-tree cursor and (possibly) commits the
+ ** transaction.
+ */
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList openBlob[] = {
+ {OP_TableLock, 0, 0, 0}, /* 0: Acquire a read or write lock */
+ {OP_OpenRead, 0, 0, 0}, /* 1: Open a cursor */
+ {OP_Variable, 1, 1, 0}, /* 2: Move ?1 into reg[1] */
+ {OP_NotExists, 0, 7, 1}, /* 3: Seek the cursor */
+ {OP_Column, 0, 0, 1}, /* 4 */
+ {OP_ResultRow, 1, 0, 0}, /* 5 */
+ {OP_Goto, 0, 2, 0}, /* 6 */
+ {OP_Close, 0, 0, 0}, /* 7 */
+ {OP_Halt, 0, 0, 0}, /* 8 */
+ };
Vdbe *v = (Vdbe *)pBlob->pStmt;
int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ VdbeOp *aOp;
- sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob);
-
-
- /* Configure the OP_Transaction */
- sqlite3VdbeChangeP1(v, 0, iDb);
- sqlite3VdbeChangeP2(v, 0, flags);
-
- /* Configure the OP_VerifyCookie */
- sqlite3VdbeChangeP1(v, 1, iDb);
- sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie);
- sqlite3VdbeChangeP3(v, 1, pTab->pSchema->iGeneration);
+ sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, flags,
+ pTab->pSchema->schema_cookie,
+ pTab->pSchema->iGeneration);
+ sqlite3VdbeChangeP5(v, 1);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(openBlob), openBlob, iLn);
/* Make sure a mutex is held on the table to be accessed */
sqlite3VdbeUsesBtree(v, iDb);
- /* Configure the OP_TableLock instruction */
+ if( db->mallocFailed==0 ){
+ assert( aOp!=0 );
+ /* Configure the OP_TableLock instruction */
#ifdef SQLITE_OMIT_SHARED_CACHE
- sqlite3VdbeChangeToNoop(v, 2);
+ aOp[0].opcode = OP_Noop;
#else
- sqlite3VdbeChangeP1(v, 2, iDb);
- sqlite3VdbeChangeP2(v, 2, pTab->tnum);
- sqlite3VdbeChangeP3(v, 2, flags);
- sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
-#endif
-
- /* Remove either the OP_OpenWrite or OpenRead. Set the P2
- ** parameter of the other to pTab->tnum. */
- sqlite3VdbeChangeToNoop(v, 4 - flags);
- sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum);
- sqlite3VdbeChangeP3(v, 3 + flags, iDb);
-
- /* Configure the number of columns. Configure the cursor to
- ** think that the table has one more column than it really
- ** does. An OP_Column to retrieve this imaginary column will
- ** always return an SQL NULL. This is useful because it means
- ** we can invoke OP_Column to fill in the vdbe cursors type
- ** and offset cache without causing any IO.
- */
- sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32);
- sqlite3VdbeChangeP2(v, 7, pTab->nCol);
- if( !db->mallocFailed ){
+ aOp[0].p1 = iDb;
+ aOp[0].p2 = pTab->tnum;
+ aOp[0].p3 = flags;
+ sqlite3VdbeChangeP4(v, 1, pTab->zName, P4_TRANSIENT);
+ }
+ if( db->mallocFailed==0 ){
+#endif
+
+ /* Remove either the OP_OpenWrite or OpenRead. Set the P2
+ ** parameter of the other to pTab->tnum. */
+ if( flags ) aOp[1].opcode = OP_OpenWrite;
+ aOp[1].p2 = pTab->tnum;
+ aOp[1].p3 = iDb;
+
+ /* Configure the number of columns. Configure the cursor to
+ ** think that the table has one more column than it really
+ ** does. An OP_Column to retrieve this imaginary column will
+ ** always return an SQL NULL. This is useful because it means
+ ** we can invoke OP_Column to fill in the vdbe cursors type
+ ** and offset cache without causing any IO.
+ */
+ aOp[1].p4type = P4_INT32;
+ aOp[1].p4.i = pTab->nCol+1;
+ aOp[4].p2 = pTab->nCol;
+
pParse->nVar = 1;
pParse->nMem = 1;
pParse->nTab = 1;
@@ -70120,7 +84659,7 @@ SQLITE_API int sqlite3_blob_open(
}
sqlite3_bind_int64(pBlob->pStmt, 1, iRow);
rc = blobSeekToRow(pBlob, iRow, &zErr);
- } while( (++nAttempt)<5 && rc==SQLITE_SCHEMA );
+ } while( (++nAttempt)<SQLITE_MAX_SCHEMA_RETRY && rc==SQLITE_SCHEMA );
blob_open_out:
if( rc==SQLITE_OK && db->mallocFailed==0 ){
@@ -70129,8 +84668,9 @@ blob_open_out:
if( pBlob && pBlob->pStmt ) sqlite3VdbeFinalize((Vdbe *)pBlob->pStmt);
sqlite3DbFree(db, pBlob);
}
- sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
+ sqlite3ParserReset(pParse);
sqlite3StackFree(db, pParse);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
@@ -70178,10 +84718,9 @@ static int blobReadWrite(
sqlite3_mutex_enter(db->mutex);
v = (Vdbe*)p->pStmt;
- if( n<0 || iOffset<0 || (iOffset+n)>p->nByte ){
+ if( n<0 || iOffset<0 || ((sqlite3_int64)iOffset+n)>p->nByte ){
/* Request is out of range. Return a transient error. */
rc = SQLITE_ERROR;
- sqlite3Error(db, SQLITE_ERROR, 0);
}else if( v==0 ){
/* If there is no statement handle, then the blob-handle has
** already been invalidated. Return SQLITE_ABORT in this case.
@@ -70193,16 +84732,40 @@ static int blobReadWrite(
*/
assert( db == v->db );
sqlite3BtreeEnterCursor(p->pCsr);
+
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ if( xCall==sqlite3BtreePutData && db->xPreUpdateCallback ){
+ /* If a pre-update hook is registered and this is a write cursor,
+ ** invoke it here.
+ **
+ ** TODO: The preupdate-hook is passed SQLITE_DELETE, even though this
+ ** operation should really be an SQLITE_UPDATE. This is probably
+ ** incorrect, but is convenient because at this point the new.* values
+ ** are not easily obtainable. And for the sessions module, an
+ ** SQLITE_UPDATE where the PK columns do not change is handled in the
+ ** same way as an SQLITE_DELETE (the SQLITE_DELETE code is actually
+ ** slightly more efficient). Since you cannot write to a PK column
+ ** using the incremental-blob API, this works. For the sessions module
+ ** anyhow.
+ */
+ sqlite3_int64 iKey;
+ iKey = sqlite3BtreeIntegerKey(p->pCsr);
+ sqlite3VdbePreUpdateHook(
+ v, v->apCsr[0], SQLITE_DELETE, p->zDb, p->pTab, iKey, -1
+ );
+ }
+#endif
+
rc = xCall(p->pCsr, iOffset+p->iOffset, n, z);
sqlite3BtreeLeaveCursor(p->pCsr);
if( rc==SQLITE_ABORT ){
sqlite3VdbeFinalize(v);
p->pStmt = 0;
}else{
- db->errCode = rc;
v->rc = rc;
}
}
+ sqlite3Error(db, rc);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
@@ -70261,7 +84824,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
char *zErr;
rc = blobSeekToRow(p, iRow, &zErr);
if( rc!=SQLITE_OK ){
- sqlite3Error(db, rc, (zErr ? "%s" : 0), zErr);
+ sqlite3ErrorWithMsg(db, rc, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
}
assert( rc!=SQLITE_SCHEMA );
@@ -70278,7 +84841,7 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
/************** End of vdbeblob.c ********************************************/
/************** Begin file vdbesort.c ****************************************/
/*
-** 2011 July 9
+** 2011-07-09
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -70289,77 +84852,240 @@ SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *pBlob, sqlite3_int64 iRow){
**
*************************************************************************
** This file contains code for the VdbeSorter object, used in concert with
-** a VdbeCursor to sort large numbers of keys (as may be required, for
-** example, by CREATE INDEX statements on tables too large to fit in main
-** memory).
+** a VdbeCursor to sort large numbers of keys for CREATE INDEX statements
+** or by SELECT statements with ORDER BY clauses that cannot be satisfied
+** using indexes and without LIMIT clauses.
+**
+** The VdbeSorter object implements a multi-threaded external merge sort
+** algorithm that is efficient even if the number of elements being sorted
+** exceeds the available memory.
+**
+** Here is the (internal, non-API) interface between this module and the
+** rest of the SQLite system:
+**
+** sqlite3VdbeSorterInit() Create a new VdbeSorter object.
+**
+** sqlite3VdbeSorterWrite() Add a single new row to the VdbeSorter
+** object. The row is a binary blob in the
+** OP_MakeRecord format that contains both
+** the ORDER BY key columns and result columns
+** in the case of a SELECT w/ ORDER BY, or
+** the complete record for an index entry
+** in the case of a CREATE INDEX.
+**
+** sqlite3VdbeSorterRewind() Sort all content previously added.
+** Position the read cursor on the
+** first sorted element.
+**
+** sqlite3VdbeSorterNext() Advance the read cursor to the next sorted
+** element.
+**
+** sqlite3VdbeSorterRowkey() Return the complete binary blob for the
+** row currently under the read cursor.
+**
+** sqlite3VdbeSorterCompare() Compare the binary blob for the row
+** currently under the read cursor against
+** another binary blob X and report if
+** X is strictly less than the read cursor.
+** Used to enforce uniqueness in a
+** CREATE UNIQUE INDEX statement.
+**
+** sqlite3VdbeSorterClose() Close the VdbeSorter object and reclaim
+** all resources.
+**
+** sqlite3VdbeSorterReset() Refurbish the VdbeSorter for reuse. This
+** is like Close() followed by Init() only
+** much faster.
+**
+** The interfaces above must be called in a particular order. Write() can
+** only occur in between Init()/Reset() and Rewind(). Next(), Rowkey(), and
+** Compare() can only occur in between Rewind() and Close()/Reset(). i.e.
+**
+** Init()
+** for each record: Write()
+** Rewind()
+** Rowkey()/Compare()
+** Next()
+** Close()
+**
+** Algorithm:
+**
+** Records passed to the sorter via calls to Write() are initially held
+** unsorted in main memory. Assuming the amount of memory used never exceeds
+** a threshold, when Rewind() is called the set of records is sorted using
+** an in-memory merge sort. In this case, no temporary files are required
+** and subsequent calls to Rowkey(), Next() and Compare() read records
+** directly from main memory.
+**
+** If the amount of space used to store records in main memory exceeds the
+** threshold, then the set of records currently in memory are sorted and
+** written to a temporary file in "Packed Memory Array" (PMA) format.
+** A PMA created at this point is known as a "level-0 PMA". Higher levels
+** of PMAs may be created by merging existing PMAs together - for example
+** merging two or more level-0 PMAs together creates a level-1 PMA.
+**
+** The threshold for the amount of main memory to use before flushing
+** records to a PMA is roughly the same as the limit configured for the
+** page-cache of the main database. Specifically, the threshold is set to
+** the value returned by "PRAGMA main.page_size" multipled by
+** that returned by "PRAGMA main.cache_size", in bytes.
+**
+** If the sorter is running in single-threaded mode, then all PMAs generated
+** are appended to a single temporary file. Or, if the sorter is running in
+** multi-threaded mode then up to (N+1) temporary files may be opened, where
+** N is the configured number of worker threads. In this case, instead of
+** sorting the records and writing the PMA to a temporary file itself, the
+** calling thread usually launches a worker thread to do so. Except, if
+** there are already N worker threads running, the main thread does the work
+** itself.
+**
+** The sorter is running in multi-threaded mode if (a) the library was built
+** with pre-processor symbol SQLITE_MAX_WORKER_THREADS set to a value greater
+** than zero, and (b) worker threads have been enabled at runtime by calling
+** "PRAGMA threads=N" with some value of N greater than 0.
+**
+** When Rewind() is called, any data remaining in memory is flushed to a
+** final PMA. So at this point the data is stored in some number of sorted
+** PMAs within temporary files on disk.
+**
+** If there are fewer than SORTER_MAX_MERGE_COUNT PMAs in total and the
+** sorter is running in single-threaded mode, then these PMAs are merged
+** incrementally as keys are retreived from the sorter by the VDBE. The
+** MergeEngine object, described in further detail below, performs this
+** merge.
+**
+** Or, if running in multi-threaded mode, then a background thread is
+** launched to merge the existing PMAs. Once the background thread has
+** merged T bytes of data into a single sorted PMA, the main thread
+** begins reading keys from that PMA while the background thread proceeds
+** with merging the next T bytes of data. And so on.
+**
+** Parameter T is set to half the value of the memory threshold used
+** by Write() above to determine when to create a new PMA.
+**
+** If there are more than SORTER_MAX_MERGE_COUNT PMAs in total when
+** Rewind() is called, then a hierarchy of incremental-merges is used.
+** First, T bytes of data from the first SORTER_MAX_MERGE_COUNT PMAs on
+** disk are merged together. Then T bytes of data from the second set, and
+** so on, such that no operation ever merges more than SORTER_MAX_MERGE_COUNT
+** PMAs at a time. This done is to improve locality.
+**
+** If running in multi-threaded mode and there are more than
+** SORTER_MAX_MERGE_COUNT PMAs on disk when Rewind() is called, then more
+** than one background thread may be created. Specifically, there may be
+** one background thread for each temporary file on disk, and one background
+** thread to merge the output of each of the others to a single PMA for
+** the main thread to read from.
+*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
+
+/*
+** If SQLITE_DEBUG_SORTER_THREADS is defined, this module outputs various
+** messages to stderr that may be helpful in understanding the performance
+** characteristics of the sorter in multi-threaded mode.
*/
+#if 0
+# define SQLITE_DEBUG_SORTER_THREADS 1
+#endif
+/*
+** Hard-coded maximum amount of data to accumulate in memory before flushing
+** to a level 0 PMA. The purpose of this limit is to prevent various integer
+** overflows. 512MiB.
+*/
+#define SQLITE_MAX_PMASZ (1<<29)
-#ifndef SQLITE_OMIT_MERGE_SORT
+/*
+** Private objects used by the sorter
+*/
+typedef struct MergeEngine MergeEngine; /* Merge PMAs together */
+typedef struct PmaReader PmaReader; /* Incrementally read one PMA */
+typedef struct PmaWriter PmaWriter; /* Incrementally write one PMA */
+typedef struct SorterRecord SorterRecord; /* A record being sorted */
+typedef struct SortSubtask SortSubtask; /* A sub-task in the sort process */
+typedef struct SorterFile SorterFile; /* Temporary file object wrapper */
+typedef struct SorterList SorterList; /* In-memory list of records */
+typedef struct IncrMerger IncrMerger; /* Read & merge multiple PMAs */
-typedef struct VdbeSorterIter VdbeSorterIter;
-typedef struct SorterRecord SorterRecord;
+/*
+** A container for a temp file handle and the current amount of data
+** stored in the file.
+*/
+struct SorterFile {
+ sqlite3_file *pFd; /* File handle */
+ i64 iEof; /* Bytes of data stored in pFd */
+};
/*
-** NOTES ON DATA STRUCTURE USED FOR N-WAY MERGES:
+** An in-memory list of objects to be sorted.
**
-** As keys are added to the sorter, they are written to disk in a series
-** of sorted packed-memory-arrays (PMAs). The size of each PMA is roughly
-** the same as the cache-size allowed for temporary databases. In order
-** to allow the caller to extract keys from the sorter in sorted order,
-** all PMAs currently stored on disk must be merged together. This comment
-** describes the data structure used to do so. The structure supports
-** merging any number of arrays in a single pass with no redundant comparison
-** operations.
+** If aMemory==0 then each object is allocated separately and the objects
+** are connected using SorterRecord.u.pNext. If aMemory!=0 then all objects
+** are stored in the aMemory[] bulk memory, one right after the other, and
+** are connected using SorterRecord.u.iNext.
+*/
+struct SorterList {
+ SorterRecord *pList; /* Linked list of records */
+ u8 *aMemory; /* If non-NULL, bulk memory to hold pList */
+ int szPMA; /* Size of pList as PMA in bytes */
+};
+
+/*
+** The MergeEngine object is used to combine two or more smaller PMAs into
+** one big PMA using a merge operation. Separate PMAs all need to be
+** combined into one big PMA in order to be able to step through the sorted
+** records in order.
**
-** The aIter[] array contains an iterator for each of the PMAs being merged.
-** An aIter[] iterator either points to a valid key or else is at EOF. For
-** the purposes of the paragraphs below, we assume that the array is actually
-** N elements in size, where N is the smallest power of 2 greater to or equal
-** to the number of iterators being merged. The extra aIter[] elements are
-** treated as if they are empty (always at EOF).
+** The aReadr[] array contains a PmaReader object for each of the PMAs being
+** merged. An aReadr[] object either points to a valid key or else is at EOF.
+** ("EOF" means "End Of File". When aReadr[] is at EOF there is no more data.)
+** For the purposes of the paragraphs below, we assume that the array is
+** actually N elements in size, where N is the smallest power of 2 greater
+** to or equal to the number of PMAs being merged. The extra aReadr[] elements
+** are treated as if they are empty (always at EOF).
**
** The aTree[] array is also N elements in size. The value of N is stored in
-** the VdbeSorter.nTree variable.
+** the MergeEngine.nTree variable.
**
** The final (N/2) elements of aTree[] contain the results of comparing
-** pairs of iterator keys together. Element i contains the result of
-** comparing aIter[2*i-N] and aIter[2*i-N+1]. Whichever key is smaller, the
+** pairs of PMA keys together. Element i contains the result of
+** comparing aReadr[2*i-N] and aReadr[2*i-N+1]. Whichever key is smaller, the
** aTree element is set to the index of it.
**
** For the purposes of this comparison, EOF is considered greater than any
** other key value. If the keys are equal (only possible with two EOF
** values), it doesn't matter which index is stored.
**
-** The (N/4) elements of aTree[] that preceed the final (N/2) described
-** above contains the index of the smallest of each block of 4 iterators.
-** And so on. So that aTree[1] contains the index of the iterator that
+** The (N/4) elements of aTree[] that precede the final (N/2) described
+** above contains the index of the smallest of each block of 4 PmaReaders
+** And so on. So that aTree[1] contains the index of the PmaReader that
** currently points to the smallest key value. aTree[0] is unused.
**
** Example:
**
-** aIter[0] -> Banana
-** aIter[1] -> Feijoa
-** aIter[2] -> Elderberry
-** aIter[3] -> Currant
-** aIter[4] -> Grapefruit
-** aIter[5] -> Apple
-** aIter[6] -> Durian
-** aIter[7] -> EOF
+** aReadr[0] -> Banana
+** aReadr[1] -> Feijoa
+** aReadr[2] -> Elderberry
+** aReadr[3] -> Currant
+** aReadr[4] -> Grapefruit
+** aReadr[5] -> Apple
+** aReadr[6] -> Durian
+** aReadr[7] -> EOF
**
** aTree[] = { X, 5 0, 5 0, 3, 5, 6 }
**
** The current element is "Apple" (the value of the key indicated by
-** iterator 5). When the Next() operation is invoked, iterator 5 will
+** PmaReader 5). When the Next() operation is invoked, PmaReader 5 will
** be advanced to the next key in its segment. Say the next key is
** "Eggplant":
**
-** aIter[5] -> Eggplant
+** aReadr[5] -> Eggplant
**
-** The contents of aTree[] are updated first by comparing the new iterator
-** 5 key to the current key of iterator 4 (still "Grapefruit"). The iterator
+** The contents of aTree[] are updated first by comparing the new PmaReader
+** 5 key to the current key of PmaReader 4 (still "Grapefruit"). The PmaReader
** 5 value is still smaller, so aTree[6] is set to 5. And so on up the tree.
-** The value of iterator 6 - "Durian" - is now smaller than that of iterator
+** The value of PmaReader 6 - "Durian" - is now smaller than that of PmaReader
** 5, so aTree[3] is set to 6. Key 0 is smaller than key 6 (Banana<Durian),
** so the value written into element 1 of the array is 0. As follows:
**
@@ -70369,323 +85095,777 @@ typedef struct SorterRecord SorterRecord;
** key comparison operations are required, where N is the number of segments
** being merged (rounded up to the next power of 2).
*/
+struct MergeEngine {
+ int nTree; /* Used size of aTree/aReadr (power of 2) */
+ SortSubtask *pTask; /* Used by this thread only */
+ int *aTree; /* Current state of incremental merge */
+ PmaReader *aReadr; /* Array of PmaReaders to merge data from */
+};
+
+/*
+** This object represents a single thread of control in a sort operation.
+** Exactly VdbeSorter.nTask instances of this object are allocated
+** as part of each VdbeSorter object. Instances are never allocated any
+** other way. VdbeSorter.nTask is set to the number of worker threads allowed
+** (see SQLITE_CONFIG_WORKER_THREADS) plus one (the main thread). Thus for
+** single-threaded operation, there is exactly one instance of this object
+** and for multi-threaded operation there are two or more instances.
+**
+** Essentially, this structure contains all those fields of the VdbeSorter
+** structure for which each thread requires a separate instance. For example,
+** each thread requries its own UnpackedRecord object to unpack records in
+** as part of comparison operations.
+**
+** Before a background thread is launched, variable bDone is set to 0. Then,
+** right before it exits, the thread itself sets bDone to 1. This is used for
+** two purposes:
+**
+** 1. When flushing the contents of memory to a level-0 PMA on disk, to
+** attempt to select a SortSubtask for which there is not already an
+** active background thread (since doing so causes the main thread
+** to block until it finishes).
+**
+** 2. If SQLITE_DEBUG_SORTER_THREADS is defined, to determine if a call
+** to sqlite3ThreadJoin() is likely to block. Cases that are likely to
+** block provoke debugging output.
+**
+** In both cases, the effects of the main thread seeing (bDone==0) even
+** after the thread has finished are not dire. So we don't worry about
+** memory barriers and such here.
+*/
+typedef int (*SorterCompare)(SortSubtask*,int*,const void*,int,const void*,int);
+struct SortSubtask {
+ SQLiteThread *pThread; /* Background thread, if any */
+ int bDone; /* Set if thread is finished but not joined */
+ VdbeSorter *pSorter; /* Sorter that owns this sub-task */
+ UnpackedRecord *pUnpacked; /* Space to unpack a record */
+ SorterList list; /* List for thread to write to a PMA */
+ int nPMA; /* Number of PMAs currently in file */
+ SorterCompare xCompare; /* Compare function to use */
+ SorterFile file; /* Temp file for level-0 PMAs */
+ SorterFile file2; /* Space for other PMAs */
+};
+
+
+/*
+** Main sorter structure. A single instance of this is allocated for each
+** sorter cursor created by the VDBE.
+**
+** mxKeysize:
+** As records are added to the sorter by calls to sqlite3VdbeSorterWrite(),
+** this variable is updated so as to be set to the size on disk of the
+** largest record in the sorter.
+*/
struct VdbeSorter {
- int nInMemory; /* Current size of pRecord list as PMA */
- int nTree; /* Used size of aTree/aIter (power of 2) */
- VdbeSorterIter *aIter; /* Array of iterators to merge */
- int *aTree; /* Current state of incremental merge */
- i64 iWriteOff; /* Current write offset within file pTemp1 */
- i64 iReadOff; /* Current read offset within file pTemp1 */
- sqlite3_file *pTemp1; /* PMA file 1 */
- int nPMA; /* Number of PMAs stored in pTemp1 */
- SorterRecord *pRecord; /* Head of in-memory record list */
int mnPmaSize; /* Minimum PMA size, in bytes */
int mxPmaSize; /* Maximum PMA size, in bytes. 0==no limit */
- UnpackedRecord *pUnpacked; /* Used to unpack keys */
+ int mxKeysize; /* Largest serialized key seen so far */
+ int pgsz; /* Main database page size */
+ PmaReader *pReader; /* Readr data from here after Rewind() */
+ MergeEngine *pMerger; /* Or here, if bUseThreads==0 */
+ sqlite3 *db; /* Database connection */
+ KeyInfo *pKeyInfo; /* How to compare records */
+ UnpackedRecord *pUnpacked; /* Used by VdbeSorterCompare() */
+ SorterList list; /* List of in-memory records */
+ int iMemory; /* Offset of free space in list.aMemory */
+ int nMemory; /* Size of list.aMemory allocation in bytes */
+ u8 bUsePMA; /* True if one or more PMAs created */
+ u8 bUseThreads; /* True to use background threads */
+ u8 iPrev; /* Previous thread used to flush PMA */
+ u8 nTask; /* Size of aTask[] array */
+ u8 typeMask;
+ SortSubtask aTask[1]; /* One or more subtasks */
+};
+
+#define SORTER_TYPE_INTEGER 0x01
+#define SORTER_TYPE_TEXT 0x02
+
+/*
+** An instance of the following object is used to read records out of a
+** PMA, in sorted order. The next key to be read is cached in nKey/aKey.
+** aKey might point into aMap or into aBuffer. If neither of those locations
+** contain a contiguous representation of the key, then aAlloc is allocated
+** and the key is copied into aAlloc and aKey is made to poitn to aAlloc.
+**
+** pFd==0 at EOF.
+*/
+struct PmaReader {
+ i64 iReadOff; /* Current read offset */
+ i64 iEof; /* 1 byte past EOF for this PmaReader */
+ int nAlloc; /* Bytes of space at aAlloc */
+ int nKey; /* Number of bytes in key */
+ sqlite3_file *pFd; /* File handle we are reading from */
+ u8 *aAlloc; /* Space for aKey if aBuffer and pMap wont work */
+ u8 *aKey; /* Pointer to current key */
+ u8 *aBuffer; /* Current read buffer */
+ int nBuffer; /* Size of read buffer in bytes */
+ u8 *aMap; /* Pointer to mapping of entire file */
+ IncrMerger *pIncr; /* Incremental merger */
};
/*
-** The following type is an iterator for a PMA. It caches the current key in
-** variables nKey/aKey. If the iterator is at EOF, pFile==0.
+** Normally, a PmaReader object iterates through an existing PMA stored
+** within a temp file. However, if the PmaReader.pIncr variable points to
+** an object of the following type, it may be used to iterate/merge through
+** multiple PMAs simultaneously.
+**
+** There are two types of IncrMerger object - single (bUseThread==0) and
+** multi-threaded (bUseThread==1).
+**
+** A multi-threaded IncrMerger object uses two temporary files - aFile[0]
+** and aFile[1]. Neither file is allowed to grow to more than mxSz bytes in
+** size. When the IncrMerger is initialized, it reads enough data from
+** pMerger to populate aFile[0]. It then sets variables within the
+** corresponding PmaReader object to read from that file and kicks off
+** a background thread to populate aFile[1] with the next mxSz bytes of
+** sorted record data from pMerger.
+**
+** When the PmaReader reaches the end of aFile[0], it blocks until the
+** background thread has finished populating aFile[1]. It then exchanges
+** the contents of the aFile[0] and aFile[1] variables within this structure,
+** sets the PmaReader fields to read from the new aFile[0] and kicks off
+** another background thread to populate the new aFile[1]. And so on, until
+** the contents of pMerger are exhausted.
+**
+** A single-threaded IncrMerger does not open any temporary files of its
+** own. Instead, it has exclusive access to mxSz bytes of space beginning
+** at offset iStartOff of file pTask->file2. And instead of using a
+** background thread to prepare data for the PmaReader, with a single
+** threaded IncrMerger the allocate part of pTask->file2 is "refilled" with
+** keys from pMerger by the calling thread whenever the PmaReader runs out
+** of data.
+*/
+struct IncrMerger {
+ SortSubtask *pTask; /* Task that owns this merger */
+ MergeEngine *pMerger; /* Merge engine thread reads data from */
+ i64 iStartOff; /* Offset to start writing file at */
+ int mxSz; /* Maximum bytes of data to store */
+ int bEof; /* Set to true when merge is finished */
+ int bUseThread; /* True to use a bg thread for this object */
+ SorterFile aFile[2]; /* aFile[0] for reading, [1] for writing */
+};
+
+/*
+** An instance of this object is used for writing a PMA.
+**
+** The PMA is written one record at a time. Each record is of an arbitrary
+** size. But I/O is more efficient if it occurs in page-sized blocks where
+** each block is aligned on a page boundary. This object caches writes to
+** the PMA so that aligned, page-size blocks are written.
*/
-struct VdbeSorterIter {
- i64 iReadOff; /* Current read offset */
- i64 iEof; /* 1 byte past EOF for this iterator */
- sqlite3_file *pFile; /* File iterator is reading from */
- int nAlloc; /* Bytes of space at aAlloc */
- u8 *aAlloc; /* Allocated space */
- int nKey; /* Number of bytes in key */
- u8 *aKey; /* Pointer to current key */
+struct PmaWriter {
+ int eFWErr; /* Non-zero if in an error state */
+ u8 *aBuffer; /* Pointer to write buffer */
+ int nBuffer; /* Size of write buffer in bytes */
+ int iBufStart; /* First byte of buffer to write */
+ int iBufEnd; /* Last byte of buffer to write */
+ i64 iWriteOff; /* Offset of start of buffer in file */
+ sqlite3_file *pFd; /* File handle to write to */
};
/*
-** A structure to store a single record. All in-memory records are connected
-** together into a linked list headed at VdbeSorter.pRecord using the
-** SorterRecord.pNext pointer.
+** This object is the header on a single record while that record is being
+** held in memory and prior to being written out as part of a PMA.
+**
+** How the linked list is connected depends on how memory is being managed
+** by this module. If using a separate allocation for each in-memory record
+** (VdbeSorter.list.aMemory==0), then the list is always connected using the
+** SorterRecord.u.pNext pointers.
+**
+** Or, if using the single large allocation method (VdbeSorter.list.aMemory!=0),
+** then while records are being accumulated the list is linked using the
+** SorterRecord.u.iNext offset. This is because the aMemory[] array may
+** be sqlite3Realloc()ed while records are being accumulated. Once the VM
+** has finished passing records to the sorter, or when the in-memory buffer
+** is full, the list is sorted. As part of the sorting process, it is
+** converted to use the SorterRecord.u.pNext pointers. See function
+** vdbeSorterSort() for details.
*/
struct SorterRecord {
- void *pVal;
- int nVal;
- SorterRecord *pNext;
+ int nVal; /* Size of the record in bytes */
+ union {
+ SorterRecord *pNext; /* Pointer to next record in list */
+ int iNext; /* Offset within aMemory of next record */
+ } u;
+ /* The data for the record immediately follows this header */
};
-/* Minimum allowable value for the VdbeSorter.nWorking variable */
-#define SORTER_MIN_WORKING 10
+/* Return a pointer to the buffer containing the record data for SorterRecord
+** object p. Should be used as if:
+**
+** void *SRVAL(SorterRecord *p) { return (void*)&p[1]; }
+*/
+#define SRVAL(p) ((void*)((SorterRecord*)(p) + 1))
+
-/* Maximum number of segments to merge in a single pass. */
+/* Maximum number of PMAs that a single MergeEngine can merge */
#define SORTER_MAX_MERGE_COUNT 16
+static int vdbeIncrSwap(IncrMerger*);
+static void vdbeIncrFree(IncrMerger *);
+
/*
-** Free all memory belonging to the VdbeSorterIter object passed as the second
+** Free all memory belonging to the PmaReader object passed as the
** argument. All structure fields are set to zero before returning.
*/
-static void vdbeSorterIterZero(sqlite3 *db, VdbeSorterIter *pIter){
- sqlite3DbFree(db, pIter->aAlloc);
- memset(pIter, 0, sizeof(VdbeSorterIter));
+static void vdbePmaReaderClear(PmaReader *pReadr){
+ sqlite3_free(pReadr->aAlloc);
+ sqlite3_free(pReadr->aBuffer);
+ if( pReadr->aMap ) sqlite3OsUnfetch(pReadr->pFd, 0, pReadr->aMap);
+ vdbeIncrFree(pReadr->pIncr);
+ memset(pReadr, 0, sizeof(PmaReader));
}
/*
-** Advance iterator pIter to the next key in its PMA. Return SQLITE_OK if
-** no error occurs, or an SQLite error code if one does.
+** Read the next nByte bytes of data from the PMA p.
+** If successful, set *ppOut to point to a buffer containing the data
+** and return SQLITE_OK. Otherwise, if an error occurs, return an SQLite
+** error code.
+**
+** The buffer returned in *ppOut is only valid until the
+** next call to this function.
*/
-static int vdbeSorterIterNext(
- sqlite3 *db, /* Database handle (for sqlite3DbMalloc() ) */
- VdbeSorterIter *pIter /* Iterator to advance */
+static int vdbePmaReadBlob(
+ PmaReader *p, /* PmaReader from which to take the blob */
+ int nByte, /* Bytes of data to read */
+ u8 **ppOut /* OUT: Pointer to buffer containing data */
){
- int rc; /* Return Code */
- int nRead; /* Number of bytes read */
- int nRec = 0; /* Size of record in bytes */
- int iOff = 0; /* Size of serialized size varint in bytes */
+ int iBuf; /* Offset within buffer to read from */
+ int nAvail; /* Bytes of data available in buffer */
- assert( pIter->iEof>=pIter->iReadOff );
- if( pIter->iEof-pIter->iReadOff>5 ){
- nRead = 5;
- }else{
- nRead = (int)(pIter->iEof - pIter->iReadOff);
- }
- if( nRead<=0 ){
- /* This is an EOF condition */
- vdbeSorterIterZero(db, pIter);
+ if( p->aMap ){
+ *ppOut = &p->aMap[p->iReadOff];
+ p->iReadOff += nByte;
return SQLITE_OK;
}
- rc = sqlite3OsRead(pIter->pFile, pIter->aAlloc, nRead, pIter->iReadOff);
- if( rc==SQLITE_OK ){
- iOff = getVarint32(pIter->aAlloc, nRec);
- if( (iOff+nRec)>nRead ){
- int nRead2; /* Number of extra bytes to read */
- if( (iOff+nRec)>pIter->nAlloc ){
- int nNew = pIter->nAlloc*2;
- while( (iOff+nRec)>nNew ) nNew = nNew*2;
- pIter->aAlloc = sqlite3DbReallocOrFree(db, pIter->aAlloc, nNew);
- if( !pIter->aAlloc ) return SQLITE_NOMEM;
- pIter->nAlloc = nNew;
- }
-
- nRead2 = iOff + nRec - nRead;
- rc = sqlite3OsRead(
- pIter->pFile, &pIter->aAlloc[nRead], nRead2, pIter->iReadOff+nRead
- );
+ assert( p->aBuffer );
+
+ /* If there is no more data to be read from the buffer, read the next
+ ** p->nBuffer bytes of data from the file into it. Or, if there are less
+ ** than p->nBuffer bytes remaining in the PMA, read all remaining data. */
+ iBuf = p->iReadOff % p->nBuffer;
+ if( iBuf==0 ){
+ int nRead; /* Bytes to read from disk */
+ int rc; /* sqlite3OsRead() return code */
+
+ /* Determine how many bytes of data to read. */
+ if( (p->iEof - p->iReadOff) > (i64)p->nBuffer ){
+ nRead = p->nBuffer;
+ }else{
+ nRead = (int)(p->iEof - p->iReadOff);
}
+ assert( nRead>0 );
+
+ /* Readr data from the file. Return early if an error occurs. */
+ rc = sqlite3OsRead(p->pFd, p->aBuffer, nRead, p->iReadOff);
+ assert( rc!=SQLITE_IOERR_SHORT_READ );
+ if( rc!=SQLITE_OK ) return rc;
}
+ nAvail = p->nBuffer - iBuf;
- assert( rc!=SQLITE_OK || nRec>0 );
- pIter->iReadOff += iOff+nRec;
- pIter->nKey = nRec;
- pIter->aKey = &pIter->aAlloc[iOff];
- return rc;
+ if( nByte<=nAvail ){
+ /* The requested data is available in the in-memory buffer. In this
+ ** case there is no need to make a copy of the data, just return a
+ ** pointer into the buffer to the caller. */
+ *ppOut = &p->aBuffer[iBuf];
+ p->iReadOff += nByte;
+ }else{
+ /* The requested data is not all available in the in-memory buffer.
+ ** In this case, allocate space at p->aAlloc[] to copy the requested
+ ** range into. Then return a copy of pointer p->aAlloc to the caller. */
+ int nRem; /* Bytes remaining to copy */
+
+ /* Extend the p->aAlloc[] allocation if required. */
+ if( p->nAlloc<nByte ){
+ u8 *aNew;
+ int nNew = MAX(128, p->nAlloc*2);
+ while( nByte>nNew ) nNew = nNew*2;
+ aNew = sqlite3Realloc(p->aAlloc, nNew);
+ if( !aNew ) return SQLITE_NOMEM_BKPT;
+ p->nAlloc = nNew;
+ p->aAlloc = aNew;
+ }
+
+ /* Copy as much data as is available in the buffer into the start of
+ ** p->aAlloc[]. */
+ memcpy(p->aAlloc, &p->aBuffer[iBuf], nAvail);
+ p->iReadOff += nAvail;
+ nRem = nByte - nAvail;
+
+ /* The following loop copies up to p->nBuffer bytes per iteration into
+ ** the p->aAlloc[] buffer. */
+ while( nRem>0 ){
+ int rc; /* vdbePmaReadBlob() return code */
+ int nCopy; /* Number of bytes to copy */
+ u8 *aNext; /* Pointer to buffer to copy data from */
+
+ nCopy = nRem;
+ if( nRem>p->nBuffer ) nCopy = p->nBuffer;
+ rc = vdbePmaReadBlob(p, nCopy, &aNext);
+ if( rc!=SQLITE_OK ) return rc;
+ assert( aNext!=p->aAlloc );
+ memcpy(&p->aAlloc[nByte - nRem], aNext, nCopy);
+ nRem -= nCopy;
+ }
+
+ *ppOut = p->aAlloc;
+ }
+
+ return SQLITE_OK;
}
/*
-** Write a single varint, value iVal, to file-descriptor pFile. Return
-** SQLITE_OK if successful, or an SQLite error code if some error occurs.
+** Read a varint from the stream of data accessed by p. Set *pnOut to
+** the value read.
+*/
+static int vdbePmaReadVarint(PmaReader *p, u64 *pnOut){
+ int iBuf;
+
+ if( p->aMap ){
+ p->iReadOff += sqlite3GetVarint(&p->aMap[p->iReadOff], pnOut);
+ }else{
+ iBuf = p->iReadOff % p->nBuffer;
+ if( iBuf && (p->nBuffer-iBuf)>=9 ){
+ p->iReadOff += sqlite3GetVarint(&p->aBuffer[iBuf], pnOut);
+ }else{
+ u8 aVarint[16], *a;
+ int i = 0, rc;
+ do{
+ rc = vdbePmaReadBlob(p, 1, &a);
+ if( rc ) return rc;
+ aVarint[(i++)&0xf] = a[0];
+ }while( (a[0]&0x80)!=0 );
+ sqlite3GetVarint(aVarint, pnOut);
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Attempt to memory map file pFile. If successful, set *pp to point to the
+** new mapping and return SQLITE_OK. If the mapping is not attempted
+** (because the file is too large or the VFS layer is configured not to use
+** mmap), return SQLITE_OK and set *pp to NULL.
**
-** The value of *piOffset when this function is called is used as the byte
-** offset in file pFile to write to. Before returning, *piOffset is
-** incremented by the number of bytes written.
+** Or, if an error occurs, return an SQLite error code. The final value of
+** *pp is undefined in this case.
*/
-static int vdbeSorterWriteVarint(
- sqlite3_file *pFile, /* File to write to */
- i64 iVal, /* Value to write as a varint */
- i64 *piOffset /* IN/OUT: Write offset in file pFile */
+static int vdbeSorterMapFile(SortSubtask *pTask, SorterFile *pFile, u8 **pp){
+ int rc = SQLITE_OK;
+ if( pFile->iEof<=(i64)(pTask->pSorter->db->nMaxSorterMmap) ){
+ sqlite3_file *pFd = pFile->pFd;
+ if( pFd->pMethods->iVersion>=3 ){
+ rc = sqlite3OsFetch(pFd, 0, (int)pFile->iEof, (void**)pp);
+ testcase( rc!=SQLITE_OK );
+ }
+ }
+ return rc;
+}
+
+/*
+** Attach PmaReader pReadr to file pFile (if it is not already attached to
+** that file) and seek it to offset iOff within the file. Return SQLITE_OK
+** if successful, or an SQLite error code if an error occurs.
+*/
+static int vdbePmaReaderSeek(
+ SortSubtask *pTask, /* Task context */
+ PmaReader *pReadr, /* Reader whose cursor is to be moved */
+ SorterFile *pFile, /* Sorter file to read from */
+ i64 iOff /* Offset in pFile */
){
- u8 aVarint[9]; /* Buffer large enough for a varint */
- int nVarint; /* Number of used bytes in varint */
- int rc; /* Result of write() call */
+ int rc = SQLITE_OK;
+
+ assert( pReadr->pIncr==0 || pReadr->pIncr->bEof==0 );
+
+ if( sqlite3FaultSim(201) ) return SQLITE_IOERR_READ;
+ if( pReadr->aMap ){
+ sqlite3OsUnfetch(pReadr->pFd, 0, pReadr->aMap);
+ pReadr->aMap = 0;
+ }
+ pReadr->iReadOff = iOff;
+ pReadr->iEof = pFile->iEof;
+ pReadr->pFd = pFile->pFd;
- nVarint = sqlite3PutVarint(aVarint, iVal);
- rc = sqlite3OsWrite(pFile, aVarint, nVarint, *piOffset);
- *piOffset += nVarint;
+ rc = vdbeSorterMapFile(pTask, pFile, &pReadr->aMap);
+ if( rc==SQLITE_OK && pReadr->aMap==0 ){
+ int pgsz = pTask->pSorter->pgsz;
+ int iBuf = pReadr->iReadOff % pgsz;
+ if( pReadr->aBuffer==0 ){
+ pReadr->aBuffer = (u8*)sqlite3Malloc(pgsz);
+ if( pReadr->aBuffer==0 ) rc = SQLITE_NOMEM_BKPT;
+ pReadr->nBuffer = pgsz;
+ }
+ if( rc==SQLITE_OK && iBuf ){
+ int nRead = pgsz - iBuf;
+ if( (pReadr->iReadOff + nRead) > pReadr->iEof ){
+ nRead = (int)(pReadr->iEof - pReadr->iReadOff);
+ }
+ rc = sqlite3OsRead(
+ pReadr->pFd, &pReadr->aBuffer[iBuf], nRead, pReadr->iReadOff
+ );
+ testcase( rc!=SQLITE_OK );
+ }
+ }
return rc;
}
/*
-** Read a single varint from file-descriptor pFile. Return SQLITE_OK if
-** successful, or an SQLite error code if some error occurs.
-**
-** The value of *piOffset when this function is called is used as the
-** byte offset in file pFile from whence to read the varint. If successful
-** (i.e. if no IO error occurs), then *piOffset is set to the offset of
-** the first byte past the end of the varint before returning. *piVal is
-** set to the integer value read. If an error occurs, the final values of
-** both *piOffset and *piVal are undefined.
+** Advance PmaReader pReadr to the next key in its PMA. Return SQLITE_OK if
+** no error occurs, or an SQLite error code if one does.
*/
-static int vdbeSorterReadVarint(
- sqlite3_file *pFile, /* File to read from */
- i64 *piOffset, /* IN/OUT: Read offset in pFile */
- i64 *piVal /* OUT: Value read from file */
-){
- u8 aVarint[9]; /* Buffer large enough for a varint */
- i64 iOff = *piOffset; /* Offset in file to read from */
- int rc; /* Return code */
+static int vdbePmaReaderNext(PmaReader *pReadr){
+ int rc = SQLITE_OK; /* Return Code */
+ u64 nRec = 0; /* Size of record in bytes */
+
+
+ if( pReadr->iReadOff>=pReadr->iEof ){
+ IncrMerger *pIncr = pReadr->pIncr;
+ int bEof = 1;
+ if( pIncr ){
+ rc = vdbeIncrSwap(pIncr);
+ if( rc==SQLITE_OK && pIncr->bEof==0 ){
+ rc = vdbePmaReaderSeek(
+ pIncr->pTask, pReadr, &pIncr->aFile[0], pIncr->iStartOff
+ );
+ bEof = 0;
+ }
+ }
- rc = sqlite3OsRead(pFile, aVarint, 9, iOff);
+ if( bEof ){
+ /* This is an EOF condition */
+ vdbePmaReaderClear(pReadr);
+ testcase( rc!=SQLITE_OK );
+ return rc;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = vdbePmaReadVarint(pReadr, &nRec);
+ }
if( rc==SQLITE_OK ){
- *piOffset += getVarint(aVarint, (u64 *)piVal);
+ pReadr->nKey = (int)nRec;
+ rc = vdbePmaReadBlob(pReadr, (int)nRec, &pReadr->aKey);
+ testcase( rc!=SQLITE_OK );
}
return rc;
}
/*
-** Initialize iterator pIter to scan through the PMA stored in file pFile
+** Initialize PmaReader pReadr to scan through the PMA stored in file pFile
** starting at offset iStart and ending at offset iEof-1. This function
-** leaves the iterator pointing to the first key in the PMA (or EOF if the
+** leaves the PmaReader pointing to the first key in the PMA (or EOF if the
** PMA is empty).
+**
+** If the pnByte parameter is NULL, then it is assumed that the file
+** contains a single PMA, and that that PMA omits the initial length varint.
*/
-static int vdbeSorterIterInit(
- sqlite3 *db, /* Database handle */
- VdbeSorter *pSorter, /* Sorter object */
+static int vdbePmaReaderInit(
+ SortSubtask *pTask, /* Task context */
+ SorterFile *pFile, /* Sorter file to read from */
i64 iStart, /* Start offset in pFile */
- VdbeSorterIter *pIter, /* Iterator to populate */
+ PmaReader *pReadr, /* PmaReader to populate */
i64 *pnByte /* IN/OUT: Increment this value by PMA size */
){
int rc;
- assert( pSorter->iWriteOff>iStart );
- assert( pIter->aAlloc==0 );
- pIter->pFile = pSorter->pTemp1;
- pIter->iReadOff = iStart;
- pIter->nAlloc = 128;
- pIter->aAlloc = (u8 *)sqlite3DbMallocRaw(db, pIter->nAlloc);
- if( !pIter->aAlloc ){
- rc = SQLITE_NOMEM;
- }else{
- i64 nByte; /* Total size of PMA in bytes */
- rc = vdbeSorterReadVarint(pSorter->pTemp1, &pIter->iReadOff, &nByte);
+ assert( pFile->iEof>iStart );
+ assert( pReadr->aAlloc==0 && pReadr->nAlloc==0 );
+ assert( pReadr->aBuffer==0 );
+ assert( pReadr->aMap==0 );
+
+ rc = vdbePmaReaderSeek(pTask, pReadr, pFile, iStart);
+ if( rc==SQLITE_OK ){
+ u64 nByte = 0; /* Size of PMA in bytes */
+ rc = vdbePmaReadVarint(pReadr, &nByte);
+ pReadr->iEof = pReadr->iReadOff + nByte;
*pnByte += nByte;
- pIter->iEof = pIter->iReadOff + nByte;
}
+
if( rc==SQLITE_OK ){
- rc = vdbeSorterIterNext(db, pIter);
+ rc = vdbePmaReaderNext(pReadr);
}
return rc;
}
+/*
+** A version of vdbeSorterCompare() that assumes that it has already been
+** determined that the first field of key1 is equal to the first field of
+** key2.
+*/
+static int vdbeSorterCompareTail(
+ SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
+ int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
+ const void *pKey1, int nKey1, /* Left side of comparison */
+ const void *pKey2, int nKey2 /* Right side of comparison */
+){
+ UnpackedRecord *r2 = pTask->pUnpacked;
+ if( *pbKey2Cached==0 ){
+ sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
+ *pbKey2Cached = 1;
+ }
+ return sqlite3VdbeRecordCompareWithSkip(nKey1, pKey1, r2, 1);
+}
/*
** Compare key1 (buffer pKey1, size nKey1 bytes) with key2 (buffer pKey2,
-** size nKey2 bytes). Argument pKeyInfo supplies the collation functions
-** used by the comparison. If an error occurs, return an SQLite error code.
-** Otherwise, return SQLITE_OK and set *pRes to a negative, zero or positive
-** value, depending on whether key1 is smaller, equal to or larger than key2.
-**
-** If the bOmitRowid argument is non-zero, assume both keys end in a rowid
-** field. For the purposes of the comparison, ignore it. Also, if bOmitRowid
-** is true and key1 contains even a single NULL value, it is considered to
-** be less than key2. Even if key2 also contains NULL values.
-**
-** If pKey2 is passed a NULL pointer, then it is assumed that the pCsr->aSpace
-** has been allocated and contains an unpacked record that is used as key2.
-*/
-static void vdbeSorterCompare(
- VdbeCursor *pCsr, /* Cursor object (for pKeyInfo) */
- int bOmitRowid, /* Ignore rowid field at end of keys */
- void *pKey1, int nKey1, /* Left side of comparison */
- void *pKey2, int nKey2, /* Right side of comparison */
- int *pRes /* OUT: Result of comparison */
+** size nKey2 bytes). Use (pTask->pKeyInfo) for the collation sequences
+** used by the comparison. Return the result of the comparison.
+**
+** If IN/OUT parameter *pbKey2Cached is true when this function is called,
+** it is assumed that (pTask->pUnpacked) contains the unpacked version
+** of key2. If it is false, (pTask->pUnpacked) is populated with the unpacked
+** version of key2 and *pbKey2Cached set to true before returning.
+**
+** If an OOM error is encountered, (pTask->pUnpacked->error_rc) is set
+** to SQLITE_NOMEM.
+*/
+static int vdbeSorterCompare(
+ SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
+ int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
+ const void *pKey1, int nKey1, /* Left side of comparison */
+ const void *pKey2, int nKey2 /* Right side of comparison */
){
- KeyInfo *pKeyInfo = pCsr->pKeyInfo;
- VdbeSorter *pSorter = pCsr->pSorter;
- UnpackedRecord *r2 = pSorter->pUnpacked;
- int i;
+ UnpackedRecord *r2 = pTask->pUnpacked;
+ if( !*pbKey2Cached ){
+ sqlite3VdbeRecordUnpack(pTask->pSorter->pKeyInfo, nKey2, pKey2, r2);
+ *pbKey2Cached = 1;
+ }
+ return sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
+}
+
+/*
+** A specially optimized version of vdbeSorterCompare() that assumes that
+** the first field of each key is a TEXT value and that the collation
+** sequence to compare them with is BINARY.
+*/
+static int vdbeSorterCompareText(
+ SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
+ int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
+ const void *pKey1, int nKey1, /* Left side of comparison */
+ const void *pKey2, int nKey2 /* Right side of comparison */
+){
+ const u8 * const p1 = (const u8 * const)pKey1;
+ const u8 * const p2 = (const u8 * const)pKey2;
+ const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */
+ const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */
+
+ int n1;
+ int n2;
+ int res;
- if( pKey2 ){
- sqlite3VdbeRecordUnpack(pKeyInfo, nKey2, pKey2, r2);
+ getVarint32(&p1[1], n1); n1 = (n1 - 13) / 2;
+ getVarint32(&p2[1], n2); n2 = (n2 - 13) / 2;
+ res = memcmp(v1, v2, MIN(n1, n2));
+ if( res==0 ){
+ res = n1 - n2;
}
- if( bOmitRowid ){
- r2->nField = pKeyInfo->nField;
- assert( r2->nField>0 );
- for(i=0; i<r2->nField; i++){
- if( r2->aMem[i].flags & MEM_Null ){
- *pRes = -1;
- return;
- }
+ if( res==0 ){
+ if( pTask->pSorter->pKeyInfo->nField>1 ){
+ res = vdbeSorterCompareTail(
+ pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
+ );
+ }
+ }else{
+ if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ res = res * -1;
}
- r2->flags |= UNPACKED_PREFIX_MATCH;
}
- *pRes = sqlite3VdbeRecordCompare(nKey1, pKey1, r2);
+ return res;
}
/*
-** This function is called to compare two iterator keys when merging
-** multiple b-tree segments. Parameter iOut is the index of the aTree[]
-** value to recalculate.
+** A specially optimized version of vdbeSorterCompare() that assumes that
+** the first field of each key is an INTEGER value.
*/
-static int vdbeSorterDoCompare(VdbeCursor *pCsr, int iOut){
- VdbeSorter *pSorter = pCsr->pSorter;
- int i1;
- int i2;
- int iRes;
- VdbeSorterIter *p1;
- VdbeSorterIter *p2;
+static int vdbeSorterCompareInt(
+ SortSubtask *pTask, /* Subtask context (for pKeyInfo) */
+ int *pbKey2Cached, /* True if pTask->pUnpacked is pKey2 */
+ const void *pKey1, int nKey1, /* Left side of comparison */
+ const void *pKey2, int nKey2 /* Right side of comparison */
+){
+ const u8 * const p1 = (const u8 * const)pKey1;
+ const u8 * const p2 = (const u8 * const)pKey2;
+ const int s1 = p1[1]; /* Left hand serial type */
+ const int s2 = p2[1]; /* Right hand serial type */
+ const u8 * const v1 = &p1[ p1[0] ]; /* Pointer to value 1 */
+ const u8 * const v2 = &p2[ p2[0] ]; /* Pointer to value 2 */
+ int res; /* Return value */
- assert( iOut<pSorter->nTree && iOut>0 );
+ assert( (s1>0 && s1<7) || s1==8 || s1==9 );
+ assert( (s2>0 && s2<7) || s2==8 || s2==9 );
- if( iOut>=(pSorter->nTree/2) ){
- i1 = (iOut - pSorter->nTree/2) * 2;
- i2 = i1 + 1;
+ if( s1>7 && s2>7 ){
+ res = s1 - s2;
}else{
- i1 = pSorter->aTree[iOut*2];
- i2 = pSorter->aTree[iOut*2+1];
- }
+ if( s1==s2 ){
+ if( (*v1 ^ *v2) & 0x80 ){
+ /* The two values have different signs */
+ res = (*v1 & 0x80) ? -1 : +1;
+ }else{
+ /* The two values have the same sign. Compare using memcmp(). */
+ static const u8 aLen[] = {0, 1, 2, 3, 4, 6, 8 };
+ int i;
+ res = 0;
+ for(i=0; i<aLen[s1]; i++){
+ if( (res = v1[i] - v2[i]) ) break;
+ }
+ }
+ }else{
+ if( s2>7 ){
+ res = +1;
+ }else if( s1>7 ){
+ res = -1;
+ }else{
+ res = s1 - s2;
+ }
+ assert( res!=0 );
- p1 = &pSorter->aIter[i1];
- p2 = &pSorter->aIter[i2];
+ if( res>0 ){
+ if( *v1 & 0x80 ) res = -1;
+ }else{
+ if( *v2 & 0x80 ) res = +1;
+ }
+ }
+ }
- if( p1->pFile==0 ){
- iRes = i2;
- }else if( p2->pFile==0 ){
- iRes = i1;
- }else{
- int res;
- assert( pCsr->pSorter->pUnpacked!=0 ); /* allocated in vdbeSorterMerge() */
- vdbeSorterCompare(
- pCsr, 0, p1->aKey, p1->nKey, p2->aKey, p2->nKey, &res
- );
- if( res<=0 ){
- iRes = i1;
- }else{
- iRes = i2;
+ if( res==0 ){
+ if( pTask->pSorter->pKeyInfo->nField>1 ){
+ res = vdbeSorterCompareTail(
+ pTask, pbKey2Cached, pKey1, nKey1, pKey2, nKey2
+ );
}
+ }else if( pTask->pSorter->pKeyInfo->aSortOrder[0] ){
+ res = res * -1;
}
- pSorter->aTree[iOut] = iRes;
- return SQLITE_OK;
+ return res;
}
/*
** Initialize the temporary index cursor just opened as a sorter cursor.
+**
+** Usually, the sorter module uses the value of (pCsr->pKeyInfo->nField)
+** to determine the number of fields that should be compared from the
+** records being sorted. However, if the value passed as argument nField
+** is non-zero and the sorter is able to guarantee a stable sort, nField
+** is used instead. This is used when sorting records for a CREATE INDEX
+** statement. In this case, keys are always delivered to the sorter in
+** order of the primary key, which happens to be make up the final part
+** of the records being sorted. So if the sort is stable, there is never
+** any reason to compare PK fields and they can be ignored for a small
+** performance boost.
+**
+** The sorter can guarantee a stable sort when running in single-threaded
+** mode, but not in multi-threaded mode.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
-SQLITE_PRIVATE int sqlite3VdbeSorterInit(sqlite3 *db, VdbeCursor *pCsr){
+SQLITE_PRIVATE int sqlite3VdbeSorterInit(
+ sqlite3 *db, /* Database connection (for malloc()) */
+ int nField, /* Number of key fields in each record */
+ VdbeCursor *pCsr /* Cursor that holds the new sorter */
+){
int pgsz; /* Page size of main database */
- int mxCache; /* Cache size */
+ int i; /* Used to iterate through aTask[] */
VdbeSorter *pSorter; /* The new sorter */
- char *d; /* Dummy */
+ KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */
+ int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */
+ int sz; /* Size of pSorter in bytes */
+ int rc = SQLITE_OK;
+#if SQLITE_MAX_WORKER_THREADS==0
+# define nWorker 0
+#else
+ int nWorker;
+#endif
+
+ /* Initialize the upper limit on the number of worker threads */
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( sqlite3TempInMemory(db) || sqlite3GlobalConfig.bCoreMutex==0 ){
+ nWorker = 0;
+ }else{
+ nWorker = db->aLimit[SQLITE_LIMIT_WORKER_THREADS];
+ }
+#endif
+
+ /* Do not allow the total number of threads (main thread + all workers)
+ ** to exceed the maximum merge count */
+#if SQLITE_MAX_WORKER_THREADS>=SORTER_MAX_MERGE_COUNT
+ if( nWorker>=SORTER_MAX_MERGE_COUNT ){
+ nWorker = SORTER_MAX_MERGE_COUNT-1;
+ }
+#endif
assert( pCsr->pKeyInfo && pCsr->pBt==0 );
- pCsr->pSorter = pSorter = sqlite3DbMallocZero(db, sizeof(VdbeSorter));
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nField-1)*sizeof(CollSeq*);
+ sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
+
+ pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
+ pCsr->uc.pSorter = pSorter;
if( pSorter==0 ){
- return SQLITE_NOMEM;
- }
-
- pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pCsr->pKeyInfo, 0, 0, &d);
- if( pSorter->pUnpacked==0 ) return SQLITE_NOMEM;
- assert( pSorter->pUnpacked==(UnpackedRecord *)d );
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ pSorter->pKeyInfo = pKeyInfo = (KeyInfo*)((u8*)pSorter + sz);
+ memcpy(pKeyInfo, pCsr->pKeyInfo, szKeyInfo);
+ pKeyInfo->db = 0;
+ if( nField && nWorker==0 ){
+ pKeyInfo->nXField += (pKeyInfo->nField - nField);
+ pKeyInfo->nField = nField;
+ }
+ pSorter->pgsz = pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
+ pSorter->nTask = nWorker + 1;
+ pSorter->iPrev = (u8)(nWorker - 1);
+ pSorter->bUseThreads = (pSorter->nTask>1);
+ pSorter->db = db;
+ for(i=0; i<pSorter->nTask; i++){
+ SortSubtask *pTask = &pSorter->aTask[i];
+ pTask->pSorter = pSorter;
+ }
+
+ if( !sqlite3TempInMemory(db) ){
+ i64 mxCache; /* Cache size in bytes*/
+ u32 szPma = sqlite3GlobalConfig.szPma;
+ pSorter->mnPmaSize = szPma * pgsz;
+
+ mxCache = db->aDb[0].pSchema->cache_size;
+ if( mxCache<0 ){
+ /* A negative cache-size value C indicates that the cache is abs(C)
+ ** KiB in size. */
+ mxCache = mxCache * -1024;
+ }else{
+ mxCache = mxCache * pgsz;
+ }
+ mxCache = MIN(mxCache, SQLITE_MAX_PMASZ);
+ pSorter->mxPmaSize = MAX(pSorter->mnPmaSize, (int)mxCache);
- if( !sqlite3TempInMemory(db) ){
- pgsz = sqlite3BtreeGetPageSize(db->aDb[0].pBt);
- pSorter->mnPmaSize = SORTER_MIN_WORKING * pgsz;
- mxCache = db->aDb[0].pSchema->cache_size;
- if( mxCache<SORTER_MIN_WORKING ) mxCache = SORTER_MIN_WORKING;
- pSorter->mxPmaSize = mxCache * pgsz;
+ /* EVIDENCE-OF: R-26747-61719 When the application provides any amount of
+ ** scratch memory using SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary
+ ** large heap allocations.
+ */
+ if( sqlite3GlobalConfig.pScratch==0 ){
+ assert( pSorter->iMemory==0 );
+ pSorter->nMemory = pgsz;
+ pSorter->list.aMemory = (u8*)sqlite3Malloc(pgsz);
+ if( !pSorter->list.aMemory ) rc = SQLITE_NOMEM_BKPT;
+ }
+ }
+
+ if( (pKeyInfo->nField+pKeyInfo->nXField)<13
+ && (pKeyInfo->aColl[0]==0 || pKeyInfo->aColl[0]==db->pDfltColl)
+ ){
+ pSorter->typeMask = SORTER_TYPE_INTEGER | SORTER_TYPE_TEXT;
+ }
}
- return SQLITE_OK;
+ return rc;
}
+#undef nWorker /* Defined at the top of this function */
/*
** Free the list of sorted records starting at pRecord.
@@ -70694,104 +85874,406 @@ static void vdbeSorterRecordFree(sqlite3 *db, SorterRecord *pRecord){
SorterRecord *p;
SorterRecord *pNext;
for(p=pRecord; p; p=pNext){
- pNext = p->pNext;
+ pNext = p->u.pNext;
sqlite3DbFree(db, p);
}
}
/*
+** Free all resources owned by the object indicated by argument pTask. All
+** fields of *pTask are zeroed before returning.
+*/
+static void vdbeSortSubtaskCleanup(sqlite3 *db, SortSubtask *pTask){
+ sqlite3DbFree(db, pTask->pUnpacked);
+#if SQLITE_MAX_WORKER_THREADS>0
+ /* pTask->list.aMemory can only be non-zero if it was handed memory
+ ** from the main thread. That only occurs SQLITE_MAX_WORKER_THREADS>0 */
+ if( pTask->list.aMemory ){
+ sqlite3_free(pTask->list.aMemory);
+ }else
+#endif
+ {
+ assert( pTask->list.aMemory==0 );
+ vdbeSorterRecordFree(0, pTask->list.pList);
+ }
+ if( pTask->file.pFd ){
+ sqlite3OsCloseFree(pTask->file.pFd);
+ }
+ if( pTask->file2.pFd ){
+ sqlite3OsCloseFree(pTask->file2.pFd);
+ }
+ memset(pTask, 0, sizeof(SortSubtask));
+}
+
+#ifdef SQLITE_DEBUG_SORTER_THREADS
+static void vdbeSorterWorkDebug(SortSubtask *pTask, const char *zEvent){
+ i64 t;
+ int iTask = (pTask - pTask->pSorter->aTask);
+ sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t);
+ fprintf(stderr, "%lld:%d %s\n", t, iTask, zEvent);
+}
+static void vdbeSorterRewindDebug(const char *zEvent){
+ i64 t;
+ sqlite3OsCurrentTimeInt64(sqlite3_vfs_find(0), &t);
+ fprintf(stderr, "%lld:X %s\n", t, zEvent);
+}
+static void vdbeSorterPopulateDebug(
+ SortSubtask *pTask,
+ const char *zEvent
+){
+ i64 t;
+ int iTask = (pTask - pTask->pSorter->aTask);
+ sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t);
+ fprintf(stderr, "%lld:bg%d %s\n", t, iTask, zEvent);
+}
+static void vdbeSorterBlockDebug(
+ SortSubtask *pTask,
+ int bBlocked,
+ const char *zEvent
+){
+ if( bBlocked ){
+ i64 t;
+ sqlite3OsCurrentTimeInt64(pTask->pSorter->db->pVfs, &t);
+ fprintf(stderr, "%lld:main %s\n", t, zEvent);
+ }
+}
+#else
+# define vdbeSorterWorkDebug(x,y)
+# define vdbeSorterRewindDebug(y)
+# define vdbeSorterPopulateDebug(x,y)
+# define vdbeSorterBlockDebug(x,y,z)
+#endif
+
+#if SQLITE_MAX_WORKER_THREADS>0
+/*
+** Join thread pTask->thread.
+*/
+static int vdbeSorterJoinThread(SortSubtask *pTask){
+ int rc = SQLITE_OK;
+ if( pTask->pThread ){
+#ifdef SQLITE_DEBUG_SORTER_THREADS
+ int bDone = pTask->bDone;
+#endif
+ void *pRet = SQLITE_INT_TO_PTR(SQLITE_ERROR);
+ vdbeSorterBlockDebug(pTask, !bDone, "enter");
+ (void)sqlite3ThreadJoin(pTask->pThread, &pRet);
+ vdbeSorterBlockDebug(pTask, !bDone, "exit");
+ rc = SQLITE_PTR_TO_INT(pRet);
+ assert( pTask->bDone==1 );
+ pTask->bDone = 0;
+ pTask->pThread = 0;
+ }
+ return rc;
+}
+
+/*
+** Launch a background thread to run xTask(pIn).
+*/
+static int vdbeSorterCreateThread(
+ SortSubtask *pTask, /* Thread will use this task object */
+ void *(*xTask)(void*), /* Routine to run in a separate thread */
+ void *pIn /* Argument passed into xTask() */
+){
+ assert( pTask->pThread==0 && pTask->bDone==0 );
+ return sqlite3ThreadCreate(&pTask->pThread, xTask, pIn);
+}
+
+/*
+** Join all outstanding threads launched by SorterWrite() to create
+** level-0 PMAs.
+*/
+static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){
+ int rc = rcin;
+ int i;
+
+ /* This function is always called by the main user thread.
+ **
+ ** If this function is being called after SorterRewind() has been called,
+ ** it is possible that thread pSorter->aTask[pSorter->nTask-1].pThread
+ ** is currently attempt to join one of the other threads. To avoid a race
+ ** condition where this thread also attempts to join the same object, join
+ ** thread pSorter->aTask[pSorter->nTask-1].pThread first. */
+ for(i=pSorter->nTask-1; i>=0; i--){
+ SortSubtask *pTask = &pSorter->aTask[i];
+ int rc2 = vdbeSorterJoinThread(pTask);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+ return rc;
+}
+#else
+# define vdbeSorterJoinAll(x,rcin) (rcin)
+# define vdbeSorterJoinThread(pTask) SQLITE_OK
+#endif
+
+/*
+** Allocate a new MergeEngine object capable of handling up to
+** nReader PmaReader inputs.
+**
+** nReader is automatically rounded up to the next power of two.
+** nReader may not exceed SORTER_MAX_MERGE_COUNT even after rounding up.
+*/
+static MergeEngine *vdbeMergeEngineNew(int nReader){
+ int N = 2; /* Smallest power of two >= nReader */
+ int nByte; /* Total bytes of space to allocate */
+ MergeEngine *pNew; /* Pointer to allocated object to return */
+
+ assert( nReader<=SORTER_MAX_MERGE_COUNT );
+
+ while( N<nReader ) N += N;
+ nByte = sizeof(MergeEngine) + N * (sizeof(int) + sizeof(PmaReader));
+
+ pNew = sqlite3FaultSim(100) ? 0 : (MergeEngine*)sqlite3MallocZero(nByte);
+ if( pNew ){
+ pNew->nTree = N;
+ pNew->pTask = 0;
+ pNew->aReadr = (PmaReader*)&pNew[1];
+ pNew->aTree = (int*)&pNew->aReadr[N];
+ }
+ return pNew;
+}
+
+/*
+** Free the MergeEngine object passed as the only argument.
+*/
+static void vdbeMergeEngineFree(MergeEngine *pMerger){
+ int i;
+ if( pMerger ){
+ for(i=0; i<pMerger->nTree; i++){
+ vdbePmaReaderClear(&pMerger->aReadr[i]);
+ }
+ }
+ sqlite3_free(pMerger);
+}
+
+/*
+** Free all resources associated with the IncrMerger object indicated by
+** the first argument.
+*/
+static void vdbeIncrFree(IncrMerger *pIncr){
+ if( pIncr ){
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( pIncr->bUseThread ){
+ vdbeSorterJoinThread(pIncr->pTask);
+ if( pIncr->aFile[0].pFd ) sqlite3OsCloseFree(pIncr->aFile[0].pFd);
+ if( pIncr->aFile[1].pFd ) sqlite3OsCloseFree(pIncr->aFile[1].pFd);
+ }
+#endif
+ vdbeMergeEngineFree(pIncr->pMerger);
+ sqlite3_free(pIncr);
+ }
+}
+
+/*
+** Reset a sorting cursor back to its original empty state.
+*/
+SQLITE_PRIVATE void sqlite3VdbeSorterReset(sqlite3 *db, VdbeSorter *pSorter){
+ int i;
+ (void)vdbeSorterJoinAll(pSorter, SQLITE_OK);
+ assert( pSorter->bUseThreads || pSorter->pReader==0 );
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( pSorter->pReader ){
+ vdbePmaReaderClear(pSorter->pReader);
+ sqlite3DbFree(db, pSorter->pReader);
+ pSorter->pReader = 0;
+ }
+#endif
+ vdbeMergeEngineFree(pSorter->pMerger);
+ pSorter->pMerger = 0;
+ for(i=0; i<pSorter->nTask; i++){
+ SortSubtask *pTask = &pSorter->aTask[i];
+ vdbeSortSubtaskCleanup(db, pTask);
+ pTask->pSorter = pSorter;
+ }
+ if( pSorter->list.aMemory==0 ){
+ vdbeSorterRecordFree(0, pSorter->list.pList);
+ }
+ pSorter->list.pList = 0;
+ pSorter->list.szPMA = 0;
+ pSorter->bUsePMA = 0;
+ pSorter->iMemory = 0;
+ pSorter->mxKeysize = 0;
+ sqlite3DbFree(db, pSorter->pUnpacked);
+ pSorter->pUnpacked = 0;
+}
+
+/*
** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
*/
SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
- VdbeSorter *pSorter = pCsr->pSorter;
+ VdbeSorter *pSorter;
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
if( pSorter ){
- if( pSorter->aIter ){
- int i;
- for(i=0; i<pSorter->nTree; i++){
- vdbeSorterIterZero(db, &pSorter->aIter[i]);
- }
- sqlite3DbFree(db, pSorter->aIter);
- }
- if( pSorter->pTemp1 ){
- sqlite3OsCloseFree(pSorter->pTemp1);
- }
- vdbeSorterRecordFree(db, pSorter->pRecord);
- sqlite3DbFree(db, pSorter->pUnpacked);
+ sqlite3VdbeSorterReset(db, pSorter);
+ sqlite3_free(pSorter->list.aMemory);
sqlite3DbFree(db, pSorter);
- pCsr->pSorter = 0;
+ pCsr->uc.pSorter = 0;
+ }
+}
+
+#if SQLITE_MAX_MMAP_SIZE>0
+/*
+** The first argument is a file-handle open on a temporary file. The file
+** is guaranteed to be nByte bytes or smaller in size. This function
+** attempts to extend the file to nByte bytes in size and to ensure that
+** the VFS has memory mapped it.
+**
+** Whether or not the file does end up memory mapped of course depends on
+** the specific VFS implementation.
+*/
+static void vdbeSorterExtendFile(sqlite3 *db, sqlite3_file *pFd, i64 nByte){
+ if( nByte<=(i64)(db->nMaxSorterMmap) && pFd->pMethods->iVersion>=3 ){
+ void *p = 0;
+ int chunksize = 4*1024;
+ sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_CHUNK_SIZE, &chunksize);
+ sqlite3OsFileControlHint(pFd, SQLITE_FCNTL_SIZE_HINT, &nByte);
+ sqlite3OsFetch(pFd, 0, (int)nByte, &p);
+ sqlite3OsUnfetch(pFd, 0, p);
}
}
+#else
+# define vdbeSorterExtendFile(x,y,z)
+#endif
/*
** Allocate space for a file-handle and open a temporary file. If successful,
-** set *ppFile to point to the malloc'd file-handle and return SQLITE_OK.
-** Otherwise, set *ppFile to 0 and return an SQLite error code.
+** set *ppFd to point to the malloc'd file-handle and return SQLITE_OK.
+** Otherwise, set *ppFd to 0 and return an SQLite error code.
*/
-static int vdbeSorterOpenTempFile(sqlite3 *db, sqlite3_file **ppFile){
- int dummy;
- return sqlite3OsOpenMalloc(db->pVfs, 0, ppFile,
+static int vdbeSorterOpenTempFile(
+ sqlite3 *db, /* Database handle doing sort */
+ i64 nExtend, /* Attempt to extend file to this size */
+ sqlite3_file **ppFd
+){
+ int rc;
+ if( sqlite3FaultSim(202) ) return SQLITE_IOERR_ACCESS;
+ rc = sqlite3OsOpenMalloc(db->pVfs, 0, ppFd,
SQLITE_OPEN_TEMP_JOURNAL |
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
- SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &dummy
+ SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE, &rc
);
+ if( rc==SQLITE_OK ){
+ i64 max = SQLITE_MAX_MMAP_SIZE;
+ sqlite3OsFileControlHint(*ppFd, SQLITE_FCNTL_MMAP_SIZE, (void*)&max);
+ if( nExtend>0 ){
+ vdbeSorterExtendFile(db, *ppFd, nExtend);
+ }
+ }
+ return rc;
}
/*
+** If it has not already been allocated, allocate the UnpackedRecord
+** structure at pTask->pUnpacked. Return SQLITE_OK if successful (or
+** if no allocation was required), or SQLITE_NOMEM otherwise.
+*/
+static int vdbeSortAllocUnpacked(SortSubtask *pTask){
+ if( pTask->pUnpacked==0 ){
+ char *pFree;
+ pTask->pUnpacked = sqlite3VdbeAllocUnpackedRecord(
+ pTask->pSorter->pKeyInfo, 0, 0, &pFree
+ );
+ assert( pTask->pUnpacked==(UnpackedRecord*)pFree );
+ if( pFree==0 ) return SQLITE_NOMEM_BKPT;
+ pTask->pUnpacked->nField = pTask->pSorter->pKeyInfo->nField;
+ pTask->pUnpacked->errCode = 0;
+ }
+ return SQLITE_OK;
+}
+
+
+/*
** Merge the two sorted lists p1 and p2 into a single list.
-** Set *ppOut to the head of the new list.
*/
-static void vdbeSorterMerge(
- VdbeCursor *pCsr, /* For pKeyInfo */
+static SorterRecord *vdbeSorterMerge(
+ SortSubtask *pTask, /* Calling thread context */
SorterRecord *p1, /* First list to merge */
- SorterRecord *p2, /* Second list to merge */
- SorterRecord **ppOut /* OUT: Head of merged list */
+ SorterRecord *p2 /* Second list to merge */
){
SorterRecord *pFinal = 0;
SorterRecord **pp = &pFinal;
- void *pVal2 = p2 ? p2->pVal : 0;
+ int bCached = 0;
- while( p1 && p2 ){
+ assert( p1!=0 && p2!=0 );
+ for(;;){
int res;
- vdbeSorterCompare(pCsr, 0, p1->pVal, p1->nVal, pVal2, p2->nVal, &res);
+ res = pTask->xCompare(
+ pTask, &bCached, SRVAL(p1), p1->nVal, SRVAL(p2), p2->nVal
+ );
+
if( res<=0 ){
*pp = p1;
- pp = &p1->pNext;
- p1 = p1->pNext;
- pVal2 = 0;
+ pp = &p1->u.pNext;
+ p1 = p1->u.pNext;
+ if( p1==0 ){
+ *pp = p2;
+ break;
+ }
}else{
*pp = p2;
- pp = &p2->pNext;
- p2 = p2->pNext;
- if( p2==0 ) break;
- pVal2 = p2->pVal;
+ pp = &p2->u.pNext;
+ p2 = p2->u.pNext;
+ bCached = 0;
+ if( p2==0 ){
+ *pp = p1;
+ break;
+ }
}
}
- *pp = p1 ? p1 : p2;
- *ppOut = pFinal;
+ return pFinal;
}
/*
-** Sort the linked list of records headed at pCsr->pRecord. Return SQLITE_OK
-** if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if an error
-** occurs.
+** Return the SorterCompare function to compare values collected by the
+** sorter object passed as the only argument.
+*/
+static SorterCompare vdbeSorterGetCompare(VdbeSorter *p){
+ if( p->typeMask==SORTER_TYPE_INTEGER ){
+ return vdbeSorterCompareInt;
+ }else if( p->typeMask==SORTER_TYPE_TEXT ){
+ return vdbeSorterCompareText;
+ }
+ return vdbeSorterCompare;
+}
+
+/*
+** Sort the linked list of records headed at pTask->pList. Return
+** SQLITE_OK if successful, or an SQLite error code (i.e. SQLITE_NOMEM) if
+** an error occurs.
*/
-static int vdbeSorterSort(VdbeCursor *pCsr){
+static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
int i;
SorterRecord **aSlot;
SorterRecord *p;
- VdbeSorter *pSorter = pCsr->pSorter;
+ int rc;
+
+ rc = vdbeSortAllocUnpacked(pTask);
+ if( rc!=SQLITE_OK ) return rc;
+
+ p = pList->pList;
+ pTask->xCompare = vdbeSorterGetCompare(pTask->pSorter);
aSlot = (SorterRecord **)sqlite3MallocZero(64 * sizeof(SorterRecord *));
if( !aSlot ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
- p = pSorter->pRecord;
while( p ){
- SorterRecord *pNext = p->pNext;
- p->pNext = 0;
+ SorterRecord *pNext;
+ if( pList->aMemory ){
+ if( (u8*)p==pList->aMemory ){
+ pNext = 0;
+ }else{
+ assert( p->u.iNext<sqlite3MallocSize(pList->aMemory) );
+ pNext = (SorterRecord*)&pList->aMemory[p->u.iNext];
+ }
+ }else{
+ pNext = p->u.pNext;
+ }
+
+ p->u.pNext = 0;
for(i=0; aSlot[i]; i++){
- vdbeSorterMerge(pCsr, p, aSlot[i], &p);
+ p = vdbeSorterMerge(pTask, p, aSlot[i]);
aSlot[i] = 0;
}
aSlot[i] = p;
@@ -70800,18 +86282,106 @@ static int vdbeSorterSort(VdbeCursor *pCsr){
p = 0;
for(i=0; i<64; i++){
- vdbeSorterMerge(pCsr, p, aSlot[i], &p);
+ if( aSlot[i]==0 ) continue;
+ p = p ? vdbeSorterMerge(pTask, p, aSlot[i]) : aSlot[i];
}
- pSorter->pRecord = p;
+ pList->pList = p;
sqlite3_free(aSlot);
- return SQLITE_OK;
+ assert( pTask->pUnpacked->errCode==SQLITE_OK
+ || pTask->pUnpacked->errCode==SQLITE_NOMEM
+ );
+ return pTask->pUnpacked->errCode;
}
+/*
+** Initialize a PMA-writer object.
+*/
+static void vdbePmaWriterInit(
+ sqlite3_file *pFd, /* File handle to write to */
+ PmaWriter *p, /* Object to populate */
+ int nBuf, /* Buffer size */
+ i64 iStart /* Offset of pFd to begin writing at */
+){
+ memset(p, 0, sizeof(PmaWriter));
+ p->aBuffer = (u8*)sqlite3Malloc(nBuf);
+ if( !p->aBuffer ){
+ p->eFWErr = SQLITE_NOMEM_BKPT;
+ }else{
+ p->iBufEnd = p->iBufStart = (iStart % nBuf);
+ p->iWriteOff = iStart - p->iBufStart;
+ p->nBuffer = nBuf;
+ p->pFd = pFd;
+ }
+}
/*
-** Write the current contents of the in-memory linked-list to a PMA. Return
-** SQLITE_OK if successful, or an SQLite error code otherwise.
+** Write nData bytes of data to the PMA. Return SQLITE_OK
+** if successful, or an SQLite error code if an error occurs.
+*/
+static void vdbePmaWriteBlob(PmaWriter *p, u8 *pData, int nData){
+ int nRem = nData;
+ while( nRem>0 && p->eFWErr==0 ){
+ int nCopy = nRem;
+ if( nCopy>(p->nBuffer - p->iBufEnd) ){
+ nCopy = p->nBuffer - p->iBufEnd;
+ }
+
+ memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy);
+ p->iBufEnd += nCopy;
+ if( p->iBufEnd==p->nBuffer ){
+ p->eFWErr = sqlite3OsWrite(p->pFd,
+ &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
+ p->iWriteOff + p->iBufStart
+ );
+ p->iBufStart = p->iBufEnd = 0;
+ p->iWriteOff += p->nBuffer;
+ }
+ assert( p->iBufEnd<p->nBuffer );
+
+ nRem -= nCopy;
+ }
+}
+
+/*
+** Flush any buffered data to disk and clean up the PMA-writer object.
+** The results of using the PMA-writer after this call are undefined.
+** Return SQLITE_OK if flushing the buffered data succeeds or is not
+** required. Otherwise, return an SQLite error code.
+**
+** Before returning, set *piEof to the offset immediately following the
+** last byte written to the file.
+*/
+static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof){
+ int rc;
+ if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){
+ p->eFWErr = sqlite3OsWrite(p->pFd,
+ &p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
+ p->iWriteOff + p->iBufStart
+ );
+ }
+ *piEof = (p->iWriteOff + p->iBufEnd);
+ sqlite3_free(p->aBuffer);
+ rc = p->eFWErr;
+ memset(p, 0, sizeof(PmaWriter));
+ return rc;
+}
+
+/*
+** Write value iVal encoded as a varint to the PMA. Return
+** SQLITE_OK if successful, or an SQLite error code if an error occurs.
+*/
+static void vdbePmaWriteVarint(PmaWriter *p, u64 iVal){
+ int nByte;
+ u8 aByte[10];
+ nByte = sqlite3PutVarint(aByte, iVal);
+ vdbePmaWriteBlob(p, aByte, nByte);
+}
+
+/*
+** Write the current contents of in-memory linked-list pList to a level-0
+** PMA in the temp file belonging to sub-task pTask. Return SQLITE_OK if
+** successful, or an SQLite error code otherwise.
**
** The format of a PMA is:
**
@@ -70822,92 +86392,256 @@ static int vdbeSorterSort(VdbeCursor *pCsr){
** Each record consists of a varint followed by a blob of data (the
** key). The varint is the number of bytes in the blob of data.
*/
-static int vdbeSorterListToPMA(sqlite3 *db, VdbeCursor *pCsr){
+static int vdbeSorterListToPMA(SortSubtask *pTask, SorterList *pList){
+ sqlite3 *db = pTask->pSorter->db;
int rc = SQLITE_OK; /* Return code */
- VdbeSorter *pSorter = pCsr->pSorter;
+ PmaWriter writer; /* Object used to write to the file */
- if( pSorter->nInMemory==0 ){
- assert( pSorter->pRecord==0 );
- return rc;
- }
+#ifdef SQLITE_DEBUG
+ /* Set iSz to the expected size of file pTask->file after writing the PMA.
+ ** This is used by an assert() statement at the end of this function. */
+ i64 iSz = pList->szPMA + sqlite3VarintLen(pList->szPMA) + pTask->file.iEof;
+#endif
- rc = vdbeSorterSort(pCsr);
+ vdbeSorterWorkDebug(pTask, "enter");
+ memset(&writer, 0, sizeof(PmaWriter));
+ assert( pList->szPMA>0 );
/* If the first temporary PMA file has not been opened, open it now. */
- if( rc==SQLITE_OK && pSorter->pTemp1==0 ){
- rc = vdbeSorterOpenTempFile(db, &pSorter->pTemp1);
- assert( rc!=SQLITE_OK || pSorter->pTemp1 );
- assert( pSorter->iWriteOff==0 );
- assert( pSorter->nPMA==0 );
+ if( pTask->file.pFd==0 ){
+ rc = vdbeSorterOpenTempFile(db, 0, &pTask->file.pFd);
+ assert( rc!=SQLITE_OK || pTask->file.pFd );
+ assert( pTask->file.iEof==0 );
+ assert( pTask->nPMA==0 );
+ }
+
+ /* Try to get the file to memory map */
+ if( rc==SQLITE_OK ){
+ vdbeSorterExtendFile(db, pTask->file.pFd, pTask->file.iEof+pList->szPMA+9);
+ }
+
+ /* Sort the list */
+ if( rc==SQLITE_OK ){
+ rc = vdbeSorterSort(pTask, pList);
}
if( rc==SQLITE_OK ){
- i64 iOff = pSorter->iWriteOff;
SorterRecord *p;
SorterRecord *pNext = 0;
- static const char eightZeros[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
- pSorter->nPMA++;
- rc = vdbeSorterWriteVarint(pSorter->pTemp1, pSorter->nInMemory, &iOff);
- for(p=pSorter->pRecord; rc==SQLITE_OK && p; p=pNext){
- pNext = p->pNext;
- rc = vdbeSorterWriteVarint(pSorter->pTemp1, p->nVal, &iOff);
+ vdbePmaWriterInit(pTask->file.pFd, &writer, pTask->pSorter->pgsz,
+ pTask->file.iEof);
+ pTask->nPMA++;
+ vdbePmaWriteVarint(&writer, pList->szPMA);
+ for(p=pList->pList; p; p=pNext){
+ pNext = p->u.pNext;
+ vdbePmaWriteVarint(&writer, p->nVal);
+ vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal);
+ if( pList->aMemory==0 ) sqlite3_free(p);
+ }
+ pList->pList = p;
+ rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof);
+ }
- if( rc==SQLITE_OK ){
- rc = sqlite3OsWrite(pSorter->pTemp1, p->pVal, p->nVal, iOff);
- iOff += p->nVal;
+ vdbeSorterWorkDebug(pTask, "exit");
+ assert( rc!=SQLITE_OK || pList->pList==0 );
+ assert( rc!=SQLITE_OK || pTask->file.iEof==iSz );
+ return rc;
+}
+
+/*
+** Advance the MergeEngine to its next entry.
+** Set *pbEof to true there is no next entry because
+** the MergeEngine has reached the end of all its inputs.
+**
+** Return SQLITE_OK if successful or an error code if an error occurs.
+*/
+static int vdbeMergeEngineStep(
+ MergeEngine *pMerger, /* The merge engine to advance to the next row */
+ int *pbEof /* Set TRUE at EOF. Set false for more content */
+){
+ int rc;
+ int iPrev = pMerger->aTree[1];/* Index of PmaReader to advance */
+ SortSubtask *pTask = pMerger->pTask;
+
+ /* Advance the current PmaReader */
+ rc = vdbePmaReaderNext(&pMerger->aReadr[iPrev]);
+
+ /* Update contents of aTree[] */
+ if( rc==SQLITE_OK ){
+ int i; /* Index of aTree[] to recalculate */
+ PmaReader *pReadr1; /* First PmaReader to compare */
+ PmaReader *pReadr2; /* Second PmaReader to compare */
+ int bCached = 0;
+
+ /* Find the first two PmaReaders to compare. The one that was just
+ ** advanced (iPrev) and the one next to it in the array. */
+ pReadr1 = &pMerger->aReadr[(iPrev & 0xFFFE)];
+ pReadr2 = &pMerger->aReadr[(iPrev | 0x0001)];
+
+ for(i=(pMerger->nTree+iPrev)/2; i>0; i=i/2){
+ /* Compare pReadr1 and pReadr2. Store the result in variable iRes. */
+ int iRes;
+ if( pReadr1->pFd==0 ){
+ iRes = +1;
+ }else if( pReadr2->pFd==0 ){
+ iRes = -1;
+ }else{
+ iRes = pTask->xCompare(pTask, &bCached,
+ pReadr1->aKey, pReadr1->nKey, pReadr2->aKey, pReadr2->nKey
+ );
}
- sqlite3DbFree(db, p);
+ /* If pReadr1 contained the smaller value, set aTree[i] to its index.
+ ** Then set pReadr2 to the next PmaReader to compare to pReadr1. In this
+ ** case there is no cache of pReadr2 in pTask->pUnpacked, so set
+ ** pKey2 to point to the record belonging to pReadr2.
+ **
+ ** Alternatively, if pReadr2 contains the smaller of the two values,
+ ** set aTree[i] to its index and update pReadr1. If vdbeSorterCompare()
+ ** was actually called above, then pTask->pUnpacked now contains
+ ** a value equivalent to pReadr2. So set pKey2 to NULL to prevent
+ ** vdbeSorterCompare() from decoding pReadr2 again.
+ **
+ ** If the two values were equal, then the value from the oldest
+ ** PMA should be considered smaller. The VdbeSorter.aReadr[] array
+ ** is sorted from oldest to newest, so pReadr1 contains older values
+ ** than pReadr2 iff (pReadr1<pReadr2). */
+ if( iRes<0 || (iRes==0 && pReadr1<pReadr2) ){
+ pMerger->aTree[i] = (int)(pReadr1 - pMerger->aReadr);
+ pReadr2 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ];
+ bCached = 0;
+ }else{
+ if( pReadr1->pFd ) bCached = 0;
+ pMerger->aTree[i] = (int)(pReadr2 - pMerger->aReadr);
+ pReadr1 = &pMerger->aReadr[ pMerger->aTree[i ^ 0x0001] ];
+ }
}
+ *pbEof = (pMerger->aReadr[pMerger->aTree[1]].pFd==0);
+ }
- /* This assert verifies that unless an error has occurred, the size of
- ** the PMA on disk is the same as the expected size stored in
- ** pSorter->nInMemory. */
- assert( rc!=SQLITE_OK || pSorter->nInMemory==(
- iOff-pSorter->iWriteOff-sqlite3VarintLen(pSorter->nInMemory)
- ));
+ return (rc==SQLITE_OK ? pTask->pUnpacked->errCode : rc);
+}
- pSorter->iWriteOff = iOff;
- if( rc==SQLITE_OK ){
- /* Terminate each file with 8 extra bytes so that from any offset
- ** in the file we can always read 9 bytes without a SHORT_READ error */
- rc = sqlite3OsWrite(pSorter->pTemp1, eightZeros, 8, iOff);
+#if SQLITE_MAX_WORKER_THREADS>0
+/*
+** The main routine for background threads that write level-0 PMAs.
+*/
+static void *vdbeSorterFlushThread(void *pCtx){
+ SortSubtask *pTask = (SortSubtask*)pCtx;
+ int rc; /* Return code */
+ assert( pTask->bDone==0 );
+ rc = vdbeSorterListToPMA(pTask, &pTask->list);
+ pTask->bDone = 1;
+ return SQLITE_INT_TO_PTR(rc);
+}
+#endif /* SQLITE_MAX_WORKER_THREADS>0 */
+
+/*
+** Flush the current contents of VdbeSorter.list to a new PMA, possibly
+** using a background thread.
+*/
+static int vdbeSorterFlushPMA(VdbeSorter *pSorter){
+#if SQLITE_MAX_WORKER_THREADS==0
+ pSorter->bUsePMA = 1;
+ return vdbeSorterListToPMA(&pSorter->aTask[0], &pSorter->list);
+#else
+ int rc = SQLITE_OK;
+ int i;
+ SortSubtask *pTask = 0; /* Thread context used to create new PMA */
+ int nWorker = (pSorter->nTask-1);
+
+ /* Set the flag to indicate that at least one PMA has been written.
+ ** Or will be, anyhow. */
+ pSorter->bUsePMA = 1;
+
+ /* Select a sub-task to sort and flush the current list of in-memory
+ ** records to disk. If the sorter is running in multi-threaded mode,
+ ** round-robin between the first (pSorter->nTask-1) tasks. Except, if
+ ** the background thread from a sub-tasks previous turn is still running,
+ ** skip it. If the first (pSorter->nTask-1) sub-tasks are all still busy,
+ ** fall back to using the final sub-task. The first (pSorter->nTask-1)
+ ** sub-tasks are prefered as they use background threads - the final
+ ** sub-task uses the main thread. */
+ for(i=0; i<nWorker; i++){
+ int iTest = (pSorter->iPrev + i + 1) % nWorker;
+ pTask = &pSorter->aTask[iTest];
+ if( pTask->bDone ){
+ rc = vdbeSorterJoinThread(pTask);
+ }
+ if( rc!=SQLITE_OK || pTask->pThread==0 ) break;
+ }
+
+ if( rc==SQLITE_OK ){
+ if( i==nWorker ){
+ /* Use the foreground thread for this operation */
+ rc = vdbeSorterListToPMA(&pSorter->aTask[nWorker], &pSorter->list);
+ }else{
+ /* Launch a background thread for this operation */
+ u8 *aMem = pTask->list.aMemory;
+ void *pCtx = (void*)pTask;
+
+ assert( pTask->pThread==0 && pTask->bDone==0 );
+ assert( pTask->list.pList==0 );
+ assert( pTask->list.aMemory==0 || pSorter->list.aMemory!=0 );
+
+ pSorter->iPrev = (u8)(pTask - pSorter->aTask);
+ pTask->list = pSorter->list;
+ pSorter->list.pList = 0;
+ pSorter->list.szPMA = 0;
+ if( aMem ){
+ pSorter->list.aMemory = aMem;
+ pSorter->nMemory = sqlite3MallocSize(aMem);
+ }else if( pSorter->list.aMemory ){
+ pSorter->list.aMemory = sqlite3Malloc(pSorter->nMemory);
+ if( !pSorter->list.aMemory ) return SQLITE_NOMEM_BKPT;
+ }
+
+ rc = vdbeSorterCreateThread(pTask, vdbeSorterFlushThread, pCtx);
}
- pSorter->pRecord = p;
}
return rc;
+#endif /* SQLITE_MAX_WORKER_THREADS!=0 */
}
/*
** Add a record to the sorter.
*/
SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
- sqlite3 *db, /* Database handle */
- VdbeCursor *pCsr, /* Sorter cursor */
+ const VdbeCursor *pCsr, /* Sorter cursor */
Mem *pVal /* Memory cell containing record */
){
- VdbeSorter *pSorter = pCsr->pSorter;
+ VdbeSorter *pSorter;
int rc = SQLITE_OK; /* Return Code */
SorterRecord *pNew; /* New list element */
-
- assert( pSorter );
- pSorter->nInMemory += sqlite3VarintLen(pVal->n) + pVal->n;
-
- pNew = (SorterRecord *)sqlite3DbMallocRaw(db, pVal->n + sizeof(SorterRecord));
- if( pNew==0 ){
- rc = SQLITE_NOMEM;
+ int bFlush; /* True to flush contents of memory to PMA */
+ int nReq; /* Bytes of memory required */
+ int nPMA; /* Bytes of PMA space required */
+ int t; /* serial type of first record field */
+
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
+ getVarint32((const u8*)&pVal->z[1], t);
+ if( t>0 && t<10 && t!=7 ){
+ pSorter->typeMask &= SORTER_TYPE_INTEGER;
+ }else if( t>10 && (t & 0x01) ){
+ pSorter->typeMask &= SORTER_TYPE_TEXT;
}else{
- pNew->pVal = (void *)&pNew[1];
- memcpy(pNew->pVal, pVal->z, pVal->n);
- pNew->nVal = pVal->n;
- pNew->pNext = pSorter->pRecord;
- pSorter->pRecord = pNew;
+ pSorter->typeMask = 0;
}
- /* See if the contents of the sorter should now be written out. They
- ** are written out when either of the following are true:
+ assert( pSorter );
+
+ /* Figure out whether or not the current contents of memory should be
+ ** flushed to a PMA before continuing. If so, do so.
+ **
+ ** If using the single large allocation mode (pSorter->aMemory!=0), then
+ ** flush the contents of memory to a new PMA if (a) at least one value is
+ ** already in memory and (b) the new value will not fit in memory.
+ **
+ ** Or, if using separate allocations for each record, flush the contents
+ ** of memory to a PMA if either of the following are true:
**
** * The total memory allocated for the in-memory list is greater
** than (page-size * cache-size), or
@@ -70915,490 +86649,954 @@ SQLITE_PRIVATE int sqlite3VdbeSorterWrite(
** * The total memory allocated for the in-memory list is greater
** than (page-size * 10) and sqlite3HeapNearlyFull() returns true.
*/
- if( rc==SQLITE_OK && pSorter->mxPmaSize>0 && (
- (pSorter->nInMemory>pSorter->mxPmaSize)
- || (pSorter->nInMemory>pSorter->mnPmaSize && sqlite3HeapNearlyFull())
- )){
- rc = vdbeSorterListToPMA(db, pCsr);
- pSorter->nInMemory = 0;
+ nReq = pVal->n + sizeof(SorterRecord);
+ nPMA = pVal->n + sqlite3VarintLen(pVal->n);
+ if( pSorter->mxPmaSize ){
+ if( pSorter->list.aMemory ){
+ bFlush = pSorter->iMemory && (pSorter->iMemory+nReq) > pSorter->mxPmaSize;
+ }else{
+ bFlush = (
+ (pSorter->list.szPMA > pSorter->mxPmaSize)
+ || (pSorter->list.szPMA > pSorter->mnPmaSize && sqlite3HeapNearlyFull())
+ );
+ }
+ if( bFlush ){
+ rc = vdbeSorterFlushPMA(pSorter);
+ pSorter->list.szPMA = 0;
+ pSorter->iMemory = 0;
+ assert( rc!=SQLITE_OK || pSorter->list.pList==0 );
+ }
}
- return rc;
-}
+ pSorter->list.szPMA += nPMA;
+ if( nPMA>pSorter->mxKeysize ){
+ pSorter->mxKeysize = nPMA;
+ }
-/*
-** Helper function for sqlite3VdbeSorterRewind().
-*/
-static int vdbeSorterInitMerge(
- sqlite3 *db, /* Database handle */
- VdbeCursor *pCsr, /* Cursor handle for this sorter */
- i64 *pnByte /* Sum of bytes in all opened PMAs */
-){
- VdbeSorter *pSorter = pCsr->pSorter;
- int rc = SQLITE_OK; /* Return code */
- int i; /* Used to iterator through aIter[] */
- i64 nByte = 0; /* Total bytes in all opened PMAs */
+ if( pSorter->list.aMemory ){
+ int nMin = pSorter->iMemory + nReq;
- /* Initialize the iterators. */
- for(i=0; i<SORTER_MAX_MERGE_COUNT; i++){
- VdbeSorterIter *pIter = &pSorter->aIter[i];
- rc = vdbeSorterIterInit(db, pSorter, pSorter->iReadOff, pIter, &nByte);
- pSorter->iReadOff = pIter->iEof;
- assert( rc!=SQLITE_OK || pSorter->iReadOff<=pSorter->iWriteOff );
- if( rc!=SQLITE_OK || pSorter->iReadOff>=pSorter->iWriteOff ) break;
- }
+ if( nMin>pSorter->nMemory ){
+ u8 *aNew;
+ int iListOff = (u8*)pSorter->list.pList - pSorter->list.aMemory;
+ int nNew = pSorter->nMemory * 2;
+ while( nNew < nMin ) nNew = nNew*2;
+ if( nNew > pSorter->mxPmaSize ) nNew = pSorter->mxPmaSize;
+ if( nNew < nMin ) nNew = nMin;
+
+ aNew = sqlite3Realloc(pSorter->list.aMemory, nNew);
+ if( !aNew ) return SQLITE_NOMEM_BKPT;
+ pSorter->list.pList = (SorterRecord*)&aNew[iListOff];
+ pSorter->list.aMemory = aNew;
+ pSorter->nMemory = nNew;
+ }
- /* Initialize the aTree[] array. */
- for(i=pSorter->nTree-1; rc==SQLITE_OK && i>0; i--){
- rc = vdbeSorterDoCompare(pCsr, i);
+ pNew = (SorterRecord*)&pSorter->list.aMemory[pSorter->iMemory];
+ pSorter->iMemory += ROUND8(nReq);
+ if( pSorter->list.pList ){
+ pNew->u.iNext = (int)((u8*)(pSorter->list.pList) - pSorter->list.aMemory);
+ }
+ }else{
+ pNew = (SorterRecord *)sqlite3Malloc(nReq);
+ if( pNew==0 ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ pNew->u.pNext = pSorter->list.pList;
}
- *pnByte = nByte;
+ memcpy(SRVAL(pNew), pVal->z, pVal->n);
+ pNew->nVal = pVal->n;
+ pSorter->list.pList = pNew;
+
return rc;
}
/*
-** Once the sorter has been populated, this function is called to prepare
-** for iterating through its contents in sorted order.
+** Read keys from pIncr->pMerger and populate pIncr->aFile[1]. The format
+** of the data stored in aFile[1] is the same as that used by regular PMAs,
+** except that the number-of-bytes varint is omitted from the start.
*/
-SQLITE_PRIVATE int sqlite3VdbeSorterRewind(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
- VdbeSorter *pSorter = pCsr->pSorter;
- int rc; /* Return code */
- sqlite3_file *pTemp2 = 0; /* Second temp file to use */
- i64 iWrite2 = 0; /* Write offset for pTemp2 */
- int nIter; /* Number of iterators used */
- int nByte; /* Bytes of space required for aIter/aTree */
- int N = 2; /* Power of 2 >= nIter */
+static int vdbeIncrPopulate(IncrMerger *pIncr){
+ int rc = SQLITE_OK;
+ int rc2;
+ i64 iStart = pIncr->iStartOff;
+ SorterFile *pOut = &pIncr->aFile[1];
+ SortSubtask *pTask = pIncr->pTask;
+ MergeEngine *pMerger = pIncr->pMerger;
+ PmaWriter writer;
+ assert( pIncr->bEof==0 );
- assert( pSorter );
+ vdbeSorterPopulateDebug(pTask, "enter");
- /* If no data has been written to disk, then do not do so now. Instead,
- ** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly
- ** from the in-memory list. */
- if( pSorter->nPMA==0 ){
- *pbEof = !pSorter->pRecord;
- assert( pSorter->aTree==0 );
- return vdbeSorterSort(pCsr);
- }
+ vdbePmaWriterInit(pOut->pFd, &writer, pTask->pSorter->pgsz, iStart);
+ while( rc==SQLITE_OK ){
+ int dummy;
+ PmaReader *pReader = &pMerger->aReadr[ pMerger->aTree[1] ];
+ int nKey = pReader->nKey;
+ i64 iEof = writer.iWriteOff + writer.iBufEnd;
- /* Write the current b-tree to a PMA. Close the b-tree cursor. */
- rc = vdbeSorterListToPMA(db, pCsr);
- if( rc!=SQLITE_OK ) return rc;
+ /* Check if the output file is full or if the input has been exhausted.
+ ** In either case exit the loop. */
+ if( pReader->pFd==0 ) break;
+ if( (iEof + nKey + sqlite3VarintLen(nKey))>(iStart + pIncr->mxSz) ) break;
- /* Allocate space for aIter[] and aTree[]. */
- nIter = pSorter->nPMA;
- if( nIter>SORTER_MAX_MERGE_COUNT ) nIter = SORTER_MAX_MERGE_COUNT;
- assert( nIter>0 );
- while( N<nIter ) N += N;
- nByte = N * (sizeof(int) + sizeof(VdbeSorterIter));
- pSorter->aIter = (VdbeSorterIter *)sqlite3DbMallocZero(db, nByte);
- if( !pSorter->aIter ) return SQLITE_NOMEM;
- pSorter->aTree = (int *)&pSorter->aIter[N];
- pSorter->nTree = N;
+ /* Write the next key to the output. */
+ vdbePmaWriteVarint(&writer, nKey);
+ vdbePmaWriteBlob(&writer, pReader->aKey, nKey);
+ assert( pIncr->pMerger->pTask==pTask );
+ rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy);
+ }
- do {
- int iNew; /* Index of new, merged, PMA */
+ rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof);
+ if( rc==SQLITE_OK ) rc = rc2;
+ vdbeSorterPopulateDebug(pTask, "exit");
+ return rc;
+}
- for(iNew=0;
- rc==SQLITE_OK && iNew*SORTER_MAX_MERGE_COUNT<pSorter->nPMA;
- iNew++
- ){
- i64 nWrite; /* Number of bytes in new PMA */
+#if SQLITE_MAX_WORKER_THREADS>0
+/*
+** The main routine for background threads that populate aFile[1] of
+** multi-threaded IncrMerger objects.
+*/
+static void *vdbeIncrPopulateThread(void *pCtx){
+ IncrMerger *pIncr = (IncrMerger*)pCtx;
+ void *pRet = SQLITE_INT_TO_PTR( vdbeIncrPopulate(pIncr) );
+ pIncr->pTask->bDone = 1;
+ return pRet;
+}
- /* If there are SORTER_MAX_MERGE_COUNT or less PMAs in file pTemp1,
- ** initialize an iterator for each of them and break out of the loop.
- ** These iterators will be incrementally merged as the VDBE layer calls
- ** sqlite3VdbeSorterNext().
- **
- ** Otherwise, if pTemp1 contains more than SORTER_MAX_MERGE_COUNT PMAs,
- ** initialize interators for SORTER_MAX_MERGE_COUNT of them. These PMAs
- ** are merged into a single PMA that is written to file pTemp2.
- */
- rc = vdbeSorterInitMerge(db, pCsr, &nWrite);
- assert( rc!=SQLITE_OK || pSorter->aIter[ pSorter->aTree[1] ].pFile );
- if( rc!=SQLITE_OK || pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){
- break;
- }
+/*
+** Launch a background thread to populate aFile[1] of pIncr.
+*/
+static int vdbeIncrBgPopulate(IncrMerger *pIncr){
+ void *p = (void*)pIncr;
+ assert( pIncr->bUseThread );
+ return vdbeSorterCreateThread(pIncr->pTask, vdbeIncrPopulateThread, p);
+}
+#endif
- /* Open the second temp file, if it is not already open. */
- if( pTemp2==0 ){
- assert( iWrite2==0 );
- rc = vdbeSorterOpenTempFile(db, &pTemp2);
- }
+/*
+** This function is called when the PmaReader corresponding to pIncr has
+** finished reading the contents of aFile[0]. Its purpose is to "refill"
+** aFile[0] such that the PmaReader should start rereading it from the
+** beginning.
+**
+** For single-threaded objects, this is accomplished by literally reading
+** keys from pIncr->pMerger and repopulating aFile[0].
+**
+** For multi-threaded objects, all that is required is to wait until the
+** background thread is finished (if it is not already) and then swap
+** aFile[0] and aFile[1] in place. If the contents of pMerger have not
+** been exhausted, this function also launches a new background thread
+** to populate the new aFile[1].
+**
+** SQLITE_OK is returned on success, or an SQLite error code otherwise.
+*/
+static int vdbeIncrSwap(IncrMerger *pIncr){
+ int rc = SQLITE_OK;
- if( rc==SQLITE_OK ){
- rc = vdbeSorterWriteVarint(pTemp2, nWrite, &iWrite2);
- }
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( pIncr->bUseThread ){
+ rc = vdbeSorterJoinThread(pIncr->pTask);
- if( rc==SQLITE_OK ){
- int bEof = 0;
- while( rc==SQLITE_OK && bEof==0 ){
- int nToWrite;
- VdbeSorterIter *pIter = &pSorter->aIter[ pSorter->aTree[1] ];
- assert( pIter->pFile );
- nToWrite = pIter->nKey + sqlite3VarintLen(pIter->nKey);
- rc = sqlite3OsWrite(pTemp2, pIter->aAlloc, nToWrite, iWrite2);
- iWrite2 += nToWrite;
- if( rc==SQLITE_OK ){
- rc = sqlite3VdbeSorterNext(db, pCsr, &bEof);
- }
- }
- }
+ if( rc==SQLITE_OK ){
+ SorterFile f0 = pIncr->aFile[0];
+ pIncr->aFile[0] = pIncr->aFile[1];
+ pIncr->aFile[1] = f0;
}
- if( pSorter->nPMA<=SORTER_MAX_MERGE_COUNT ){
- break;
- }else{
- sqlite3_file *pTmp = pSorter->pTemp1;
- pSorter->nPMA = iNew;
- pSorter->pTemp1 = pTemp2;
- pTemp2 = pTmp;
- pSorter->iWriteOff = iWrite2;
- pSorter->iReadOff = 0;
- iWrite2 = 0;
+ if( rc==SQLITE_OK ){
+ if( pIncr->aFile[0].iEof==pIncr->iStartOff ){
+ pIncr->bEof = 1;
+ }else{
+ rc = vdbeIncrBgPopulate(pIncr);
+ }
+ }
+ }else
+#endif
+ {
+ rc = vdbeIncrPopulate(pIncr);
+ pIncr->aFile[0] = pIncr->aFile[1];
+ if( pIncr->aFile[0].iEof==pIncr->iStartOff ){
+ pIncr->bEof = 1;
}
- }while( rc==SQLITE_OK );
-
- if( pTemp2 ){
- sqlite3OsCloseFree(pTemp2);
}
- *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+
return rc;
}
/*
-** Advance to the next element in the sorter.
+** Allocate and return a new IncrMerger object to read data from pMerger.
+**
+** If an OOM condition is encountered, return NULL. In this case free the
+** pMerger argument before returning.
*/
-SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, VdbeCursor *pCsr, int *pbEof){
- VdbeSorter *pSorter = pCsr->pSorter;
- int rc; /* Return code */
-
- if( pSorter->aTree ){
- int iPrev = pSorter->aTree[1];/* Index of iterator to advance */
- int i; /* Index of aTree[] to recalculate */
-
- rc = vdbeSorterIterNext(db, &pSorter->aIter[iPrev]);
- for(i=(pSorter->nTree+iPrev)/2; rc==SQLITE_OK && i>0; i=i/2){
- rc = vdbeSorterDoCompare(pCsr, i);
- }
-
- *pbEof = (pSorter->aIter[pSorter->aTree[1]].pFile==0);
+static int vdbeIncrMergerNew(
+ SortSubtask *pTask, /* The thread that will be using the new IncrMerger */
+ MergeEngine *pMerger, /* The MergeEngine that the IncrMerger will control */
+ IncrMerger **ppOut /* Write the new IncrMerger here */
+){
+ int rc = SQLITE_OK;
+ IncrMerger *pIncr = *ppOut = (IncrMerger*)
+ (sqlite3FaultSim(100) ? 0 : sqlite3MallocZero(sizeof(*pIncr)));
+ if( pIncr ){
+ pIncr->pMerger = pMerger;
+ pIncr->pTask = pTask;
+ pIncr->mxSz = MAX(pTask->pSorter->mxKeysize+9,pTask->pSorter->mxPmaSize/2);
+ pTask->file2.iEof += pIncr->mxSz;
}else{
- SorterRecord *pFree = pSorter->pRecord;
- pSorter->pRecord = pFree->pNext;
- pFree->pNext = 0;
- vdbeSorterRecordFree(db, pFree);
- *pbEof = !pSorter->pRecord;
- rc = SQLITE_OK;
+ vdbeMergeEngineFree(pMerger);
+ rc = SQLITE_NOMEM_BKPT;
}
return rc;
}
+#if SQLITE_MAX_WORKER_THREADS>0
/*
-** Return a pointer to a buffer owned by the sorter that contains the
-** current key.
+** Set the "use-threads" flag on object pIncr.
*/
-static void *vdbeSorterRowkey(
- VdbeSorter *pSorter, /* Sorter object */
- int *pnKey /* OUT: Size of current key in bytes */
-){
- void *pKey;
- if( pSorter->aTree ){
- VdbeSorterIter *pIter;
- pIter = &pSorter->aIter[ pSorter->aTree[1] ];
- *pnKey = pIter->nKey;
- pKey = pIter->aKey;
- }else{
- *pnKey = pSorter->pRecord->nVal;
- pKey = pSorter->pRecord->pVal;
- }
- return pKey;
+static void vdbeIncrMergerSetThreads(IncrMerger *pIncr){
+ pIncr->bUseThread = 1;
+ pIncr->pTask->file2.iEof -= pIncr->mxSz;
}
+#endif /* SQLITE_MAX_WORKER_THREADS>0 */
+
+
/*
-** Copy the current sorter key into the memory cell pOut.
+** Recompute pMerger->aTree[iOut] by comparing the next keys on the
+** two PmaReaders that feed that entry. Neither of the PmaReaders
+** are advanced. This routine merely does the comparison.
*/
-SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(VdbeCursor *pCsr, Mem *pOut){
- VdbeSorter *pSorter = pCsr->pSorter;
- void *pKey; int nKey; /* Sorter key to copy into pOut */
+static void vdbeMergeEngineCompare(
+ MergeEngine *pMerger, /* Merge engine containing PmaReaders to compare */
+ int iOut /* Store the result in pMerger->aTree[iOut] */
+){
+ int i1;
+ int i2;
+ int iRes;
+ PmaReader *p1;
+ PmaReader *p2;
- pKey = vdbeSorterRowkey(pSorter, &nKey);
- if( sqlite3VdbeMemGrow(pOut, nKey, 0) ){
- return SQLITE_NOMEM;
+ assert( iOut<pMerger->nTree && iOut>0 );
+
+ if( iOut>=(pMerger->nTree/2) ){
+ i1 = (iOut - pMerger->nTree/2) * 2;
+ i2 = i1 + 1;
+ }else{
+ i1 = pMerger->aTree[iOut*2];
+ i2 = pMerger->aTree[iOut*2+1];
}
- pOut->n = nKey;
- MemSetTypeFlag(pOut, MEM_Blob);
- memcpy(pOut->z, pKey, nKey);
- return SQLITE_OK;
+ p1 = &pMerger->aReadr[i1];
+ p2 = &pMerger->aReadr[i2];
+
+ if( p1->pFd==0 ){
+ iRes = i2;
+ }else if( p2->pFd==0 ){
+ iRes = i1;
+ }else{
+ SortSubtask *pTask = pMerger->pTask;
+ int bCached = 0;
+ int res;
+ assert( pTask->pUnpacked!=0 ); /* from vdbeSortSubtaskMain() */
+ res = pTask->xCompare(
+ pTask, &bCached, p1->aKey, p1->nKey, p2->aKey, p2->nKey
+ );
+ if( res<=0 ){
+ iRes = i1;
+ }else{
+ iRes = i2;
+ }
+ }
+
+ pMerger->aTree[iOut] = iRes;
}
/*
-** Compare the key in memory cell pVal with the key that the sorter cursor
-** passed as the first argument currently points to. For the purposes of
-** the comparison, ignore the rowid field at the end of each record.
+** Allowed values for the eMode parameter to vdbeMergeEngineInit()
+** and vdbePmaReaderIncrMergeInit().
**
-** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM).
-** Otherwise, set *pRes to a negative, zero or positive value if the
-** key in pVal is smaller than, equal to or larger than the current sorter
-** key.
+** Only INCRINIT_NORMAL is valid in single-threaded builds (when
+** SQLITE_MAX_WORKER_THREADS==0). The other values are only used
+** when there exists one or more separate worker threads.
*/
-SQLITE_PRIVATE int sqlite3VdbeSorterCompare(
- VdbeCursor *pCsr, /* Sorter cursor */
- Mem *pVal, /* Value to compare to current sorter key */
- int *pRes /* OUT: Result of comparison */
+#define INCRINIT_NORMAL 0
+#define INCRINIT_TASK 1
+#define INCRINIT_ROOT 2
+
+/*
+** Forward reference required as the vdbeIncrMergeInit() and
+** vdbePmaReaderIncrInit() routines are called mutually recursively when
+** building a merge tree.
+*/
+static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode);
+
+/*
+** Initialize the MergeEngine object passed as the second argument. Once this
+** function returns, the first key of merged data may be read from the
+** MergeEngine object in the usual fashion.
+**
+** If argument eMode is INCRINIT_ROOT, then it is assumed that any IncrMerge
+** objects attached to the PmaReader objects that the merger reads from have
+** already been populated, but that they have not yet populated aFile[0] and
+** set the PmaReader objects up to read from it. In this case all that is
+** required is to call vdbePmaReaderNext() on each PmaReader to point it at
+** its first key.
+**
+** Otherwise, if eMode is any value other than INCRINIT_ROOT, then use
+** vdbePmaReaderIncrMergeInit() to initialize each PmaReader that feeds data
+** to pMerger.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
+*/
+static int vdbeMergeEngineInit(
+ SortSubtask *pTask, /* Thread that will run pMerger */
+ MergeEngine *pMerger, /* MergeEngine to initialize */
+ int eMode /* One of the INCRINIT_XXX constants */
){
- VdbeSorter *pSorter = pCsr->pSorter;
- void *pKey; int nKey; /* Sorter key to compare pVal with */
+ int rc = SQLITE_OK; /* Return code */
+ int i; /* For looping over PmaReader objects */
+ int nTree = pMerger->nTree;
+
+ /* eMode is always INCRINIT_NORMAL in single-threaded mode */
+ assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL );
+
+ /* Verify that the MergeEngine is assigned to a single thread */
+ assert( pMerger->pTask==0 );
+ pMerger->pTask = pTask;
+
+ for(i=0; i<nTree; i++){
+ if( SQLITE_MAX_WORKER_THREADS>0 && eMode==INCRINIT_ROOT ){
+ /* PmaReaders should be normally initialized in order, as if they are
+ ** reading from the same temp file this makes for more linear file IO.
+ ** However, in the INCRINIT_ROOT case, if PmaReader aReadr[nTask-1] is
+ ** in use it will block the vdbePmaReaderNext() call while it uses
+ ** the main thread to fill its buffer. So calling PmaReaderNext()
+ ** on this PmaReader before any of the multi-threaded PmaReaders takes
+ ** better advantage of multi-processor hardware. */
+ rc = vdbePmaReaderNext(&pMerger->aReadr[nTree-i-1]);
+ }else{
+ rc = vdbePmaReaderIncrInit(&pMerger->aReadr[i], INCRINIT_NORMAL);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+ }
- pKey = vdbeSorterRowkey(pSorter, &nKey);
- vdbeSorterCompare(pCsr, 1, pVal->z, pVal->n, pKey, nKey, pRes);
- return SQLITE_OK;
+ for(i=pMerger->nTree-1; i>0; i--){
+ vdbeMergeEngineCompare(pMerger, i);
+ }
+ return pTask->pUnpacked->errCode;
}
-#endif /* #ifndef SQLITE_OMIT_MERGE_SORT */
-
-/************** End of vdbesort.c ********************************************/
-/************** Begin file journal.c *****************************************/
/*
-** 2007 August 22
+** The PmaReader passed as the first argument is guaranteed to be an
+** incremental-reader (pReadr->pIncr!=0). This function serves to open
+** and/or initialize the temp file related fields of the IncrMerge
+** object at (pReadr->pIncr).
**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
+** If argument eMode is set to INCRINIT_NORMAL, then all PmaReaders
+** in the sub-tree headed by pReadr are also initialized. Data is then
+** loaded into the buffers belonging to pReadr and it is set to point to
+** the first key in its range.
**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-*************************************************************************
+** If argument eMode is set to INCRINIT_TASK, then pReadr is guaranteed
+** to be a multi-threaded PmaReader and this function is being called in a
+** background thread. In this case all PmaReaders in the sub-tree are
+** initialized as for INCRINIT_NORMAL and the aFile[1] buffer belonging to
+** pReadr is populated. However, pReadr itself is not set up to point
+** to its first key. A call to vdbePmaReaderNext() is still required to do
+** that.
**
-** This file implements a special kind of sqlite3_file object used
-** by SQLite to create journal files if the atomic-write optimization
-** is enabled.
+** The reason this function does not call vdbePmaReaderNext() immediately
+** in the INCRINIT_TASK case is that vdbePmaReaderNext() assumes that it has
+** to block on thread (pTask->thread) before accessing aFile[1]. But, since
+** this entire function is being run by thread (pTask->thread), that will
+** lead to the current background thread attempting to join itself.
**
-** The distinctive characteristic of this sqlite3_file is that the
-** actual on disk file is created lazily. When the file is created,
-** the caller specifies a buffer size for an in-memory buffer to
-** be used to service read() and write() requests. The actual file
-** on disk is not created or populated until either:
+** Finally, if argument eMode is set to INCRINIT_ROOT, it may be assumed
+** that pReadr->pIncr is a multi-threaded IncrMerge objects, and that all
+** child-trees have already been initialized using IncrInit(INCRINIT_TASK).
+** In this case vdbePmaReaderNext() is called on all child PmaReaders and
+** the current PmaReader set to point to the first key in its range.
**
-** 1) The in-memory representation grows too large for the allocated
-** buffer, or
-** 2) The sqlite3JournalCreate() function is called.
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
-#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+static int vdbePmaReaderIncrMergeInit(PmaReader *pReadr, int eMode){
+ int rc = SQLITE_OK;
+ IncrMerger *pIncr = pReadr->pIncr;
+ SortSubtask *pTask = pIncr->pTask;
+ sqlite3 *db = pTask->pSorter->db;
+
+ /* eMode is always INCRINIT_NORMAL in single-threaded mode */
+ assert( SQLITE_MAX_WORKER_THREADS>0 || eMode==INCRINIT_NORMAL );
+
+ rc = vdbeMergeEngineInit(pTask, pIncr->pMerger, eMode);
+
+ /* Set up the required files for pIncr. A multi-theaded IncrMerge object
+ ** requires two temp files to itself, whereas a single-threaded object
+ ** only requires a region of pTask->file2. */
+ if( rc==SQLITE_OK ){
+ int mxSz = pIncr->mxSz;
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( pIncr->bUseThread ){
+ rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[0].pFd);
+ if( rc==SQLITE_OK ){
+ rc = vdbeSorterOpenTempFile(db, mxSz, &pIncr->aFile[1].pFd);
+ }
+ }else
+#endif
+ /*if( !pIncr->bUseThread )*/{
+ if( pTask->file2.pFd==0 ){
+ assert( pTask->file2.iEof>0 );
+ rc = vdbeSorterOpenTempFile(db, pTask->file2.iEof, &pTask->file2.pFd);
+ pTask->file2.iEof = 0;
+ }
+ if( rc==SQLITE_OK ){
+ pIncr->aFile[1].pFd = pTask->file2.pFd;
+ pIncr->iStartOff = pTask->file2.iEof;
+ pTask->file2.iEof += mxSz;
+ }
+ }
+ }
+
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( rc==SQLITE_OK && pIncr->bUseThread ){
+ /* Use the current thread to populate aFile[1], even though this
+ ** PmaReader is multi-threaded. If this is an INCRINIT_TASK object,
+ ** then this function is already running in background thread
+ ** pIncr->pTask->thread.
+ **
+ ** If this is the INCRINIT_ROOT object, then it is running in the
+ ** main VDBE thread. But that is Ok, as that thread cannot return
+ ** control to the VDBE or proceed with anything useful until the
+ ** first results are ready from this merger object anyway.
+ */
+ assert( eMode==INCRINIT_ROOT || eMode==INCRINIT_TASK );
+ rc = vdbeIncrPopulate(pIncr);
+ }
+#endif
+ if( rc==SQLITE_OK && (SQLITE_MAX_WORKER_THREADS==0 || eMode!=INCRINIT_TASK) ){
+ rc = vdbePmaReaderNext(pReadr);
+ }
+ return rc;
+}
+
+#if SQLITE_MAX_WORKER_THREADS>0
/*
-** A JournalFile object is a subclass of sqlite3_file used by
-** as an open file handle for journal files.
+** The main routine for vdbePmaReaderIncrMergeInit() operations run in
+** background threads.
*/
-struct JournalFile {
- sqlite3_io_methods *pMethod; /* I/O methods on journal files */
- int nBuf; /* Size of zBuf[] in bytes */
- char *zBuf; /* Space to buffer journal writes */
- int iSize; /* Amount of zBuf[] currently used */
- int flags; /* xOpen flags */
- sqlite3_vfs *pVfs; /* The "real" underlying VFS */
- sqlite3_file *pReal; /* The "real" underlying file descriptor */
- const char *zJournal; /* Name of the journal file */
-};
-typedef struct JournalFile JournalFile;
+static void *vdbePmaReaderBgIncrInit(void *pCtx){
+ PmaReader *pReader = (PmaReader*)pCtx;
+ void *pRet = SQLITE_INT_TO_PTR(
+ vdbePmaReaderIncrMergeInit(pReader,INCRINIT_TASK)
+ );
+ pReader->pIncr->pTask->bDone = 1;
+ return pRet;
+}
+#endif
/*
-** If it does not already exists, create and populate the on-disk file
-** for JournalFile p.
+** If the PmaReader passed as the first argument is not an incremental-reader
+** (if pReadr->pIncr==0), then this function is a no-op. Otherwise, it invokes
+** the vdbePmaReaderIncrMergeInit() function with the parameters passed to
+** this routine to initialize the incremental merge.
+**
+** If the IncrMerger object is multi-threaded (IncrMerger.bUseThread==1),
+** then a background thread is launched to call vdbePmaReaderIncrMergeInit().
+** Or, if the IncrMerger is single threaded, the same function is called
+** using the current thread.
*/
-static int createFile(JournalFile *p){
- int rc = SQLITE_OK;
- if( !p->pReal ){
- sqlite3_file *pReal = (sqlite3_file *)&p[1];
- rc = sqlite3OsOpen(p->pVfs, p->zJournal, pReal, p->flags, 0);
- if( rc==SQLITE_OK ){
- p->pReal = pReal;
- if( p->iSize>0 ){
- assert(p->iSize<=p->nBuf);
- rc = sqlite3OsWrite(p->pReal, p->zBuf, p->iSize, 0);
- }
+static int vdbePmaReaderIncrInit(PmaReader *pReadr, int eMode){
+ IncrMerger *pIncr = pReadr->pIncr; /* Incremental merger */
+ int rc = SQLITE_OK; /* Return code */
+ if( pIncr ){
+#if SQLITE_MAX_WORKER_THREADS>0
+ assert( pIncr->bUseThread==0 || eMode==INCRINIT_TASK );
+ if( pIncr->bUseThread ){
+ void *pCtx = (void*)pReadr;
+ rc = vdbeSorterCreateThread(pIncr->pTask, vdbePmaReaderBgIncrInit, pCtx);
+ }else
+#endif
+ {
+ rc = vdbePmaReaderIncrMergeInit(pReadr, eMode);
}
}
return rc;
}
/*
-** Close the file.
+** Allocate a new MergeEngine object to merge the contents of nPMA level-0
+** PMAs from pTask->file. If no error occurs, set *ppOut to point to
+** the new object and return SQLITE_OK. Or, if an error does occur, set *ppOut
+** to NULL and return an SQLite error code.
+**
+** When this function is called, *piOffset is set to the offset of the
+** first PMA to read from pTask->file. Assuming no error occurs, it is
+** set to the offset immediately following the last byte of the last
+** PMA before returning. If an error does occur, then the final value of
+** *piOffset is undefined.
*/
-static int jrnlClose(sqlite3_file *pJfd){
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- sqlite3OsClose(p->pReal);
+static int vdbeMergeEngineLevel0(
+ SortSubtask *pTask, /* Sorter task to read from */
+ int nPMA, /* Number of PMAs to read */
+ i64 *piOffset, /* IN/OUT: Readr offset in pTask->file */
+ MergeEngine **ppOut /* OUT: New merge-engine */
+){
+ MergeEngine *pNew; /* Merge engine to return */
+ i64 iOff = *piOffset;
+ int i;
+ int rc = SQLITE_OK;
+
+ *ppOut = pNew = vdbeMergeEngineNew(nPMA);
+ if( pNew==0 ) rc = SQLITE_NOMEM_BKPT;
+
+ for(i=0; i<nPMA && rc==SQLITE_OK; i++){
+ i64 nDummy = 0;
+ PmaReader *pReadr = &pNew->aReadr[i];
+ rc = vdbePmaReaderInit(pTask, &pTask->file, iOff, pReadr, &nDummy);
+ iOff = pReadr->iEof;
}
- sqlite3_free(p->zBuf);
- return SQLITE_OK;
+
+ if( rc!=SQLITE_OK ){
+ vdbeMergeEngineFree(pNew);
+ *ppOut = 0;
+ }
+ *piOffset = iOff;
+ return rc;
}
/*
-** Read data from the file.
+** Return the depth of a tree comprising nPMA PMAs, assuming a fanout of
+** SORTER_MAX_MERGE_COUNT. The returned value does not include leaf nodes.
+**
+** i.e.
+**
+** nPMA<=16 -> TreeDepth() == 0
+** nPMA<=256 -> TreeDepth() == 1
+** nPMA<=65536 -> TreeDepth() == 2
*/
-static int jrnlRead(
- sqlite3_file *pJfd, /* The journal file from which to read */
- void *zBuf, /* Put the results here */
- int iAmt, /* Number of bytes to read */
- sqlite_int64 iOfst /* Begin reading at this offset */
+static int vdbeSorterTreeDepth(int nPMA){
+ int nDepth = 0;
+ i64 nDiv = SORTER_MAX_MERGE_COUNT;
+ while( nDiv < (i64)nPMA ){
+ nDiv = nDiv * SORTER_MAX_MERGE_COUNT;
+ nDepth++;
+ }
+ return nDepth;
+}
+
+/*
+** pRoot is the root of an incremental merge-tree with depth nDepth (according
+** to vdbeSorterTreeDepth()). pLeaf is the iSeq'th leaf to be added to the
+** tree, counting from zero. This function adds pLeaf to the tree.
+**
+** If successful, SQLITE_OK is returned. If an error occurs, an SQLite error
+** code is returned and pLeaf is freed.
+*/
+static int vdbeSorterAddToTree(
+ SortSubtask *pTask, /* Task context */
+ int nDepth, /* Depth of tree according to TreeDepth() */
+ int iSeq, /* Sequence number of leaf within tree */
+ MergeEngine *pRoot, /* Root of tree */
+ MergeEngine *pLeaf /* Leaf to add to tree */
){
int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst);
- }else if( (iAmt+iOfst)>p->iSize ){
- rc = SQLITE_IOERR_SHORT_READ;
+ int nDiv = 1;
+ int i;
+ MergeEngine *p = pRoot;
+ IncrMerger *pIncr;
+
+ rc = vdbeIncrMergerNew(pTask, pLeaf, &pIncr);
+
+ for(i=1; i<nDepth; i++){
+ nDiv = nDiv * SORTER_MAX_MERGE_COUNT;
+ }
+
+ for(i=1; i<nDepth && rc==SQLITE_OK; i++){
+ int iIter = (iSeq / nDiv) % SORTER_MAX_MERGE_COUNT;
+ PmaReader *pReadr = &p->aReadr[iIter];
+
+ if( pReadr->pIncr==0 ){
+ MergeEngine *pNew = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT);
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM_BKPT;
+ }else{
+ rc = vdbeIncrMergerNew(pTask, pNew, &pReadr->pIncr);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ p = pReadr->pIncr->pMerger;
+ nDiv = nDiv / SORTER_MAX_MERGE_COUNT;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ p->aReadr[iSeq % SORTER_MAX_MERGE_COUNT].pIncr = pIncr;
}else{
- memcpy(zBuf, &p->zBuf[iOfst], iAmt);
+ vdbeIncrFree(pIncr);
}
return rc;
}
/*
-** Write data to the file.
+** This function is called as part of a SorterRewind() operation on a sorter
+** that has already written two or more level-0 PMAs to one or more temp
+** files. It builds a tree of MergeEngine/IncrMerger/PmaReader objects that
+** can be used to incrementally merge all PMAs on disk.
+**
+** If successful, SQLITE_OK is returned and *ppOut set to point to the
+** MergeEngine object at the root of the tree before returning. Or, if an
+** error occurs, an SQLite error code is returned and the final value
+** of *ppOut is undefined.
*/
-static int jrnlWrite(
- sqlite3_file *pJfd, /* The journal file into which to write */
- const void *zBuf, /* Take data to be written from here */
- int iAmt, /* Number of bytes to write */
- sqlite_int64 iOfst /* Begin writing at this offset into the file */
+static int vdbeSorterMergeTreeBuild(
+ VdbeSorter *pSorter, /* The VDBE cursor that implements the sort */
+ MergeEngine **ppOut /* Write the MergeEngine here */
){
+ MergeEngine *pMain = 0;
int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( !p->pReal && (iOfst+iAmt)>p->nBuf ){
- rc = createFile(p);
+ int iTask;
+
+#if SQLITE_MAX_WORKER_THREADS>0
+ /* If the sorter uses more than one task, then create the top-level
+ ** MergeEngine here. This MergeEngine will read data from exactly
+ ** one PmaReader per sub-task. */
+ assert( pSorter->bUseThreads || pSorter->nTask==1 );
+ if( pSorter->nTask>1 ){
+ pMain = vdbeMergeEngineNew(pSorter->nTask);
+ if( pMain==0 ) rc = SQLITE_NOMEM_BKPT;
}
- if( rc==SQLITE_OK ){
- if( p->pReal ){
- rc = sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
- }else{
- memcpy(&p->zBuf[iOfst], zBuf, iAmt);
- if( p->iSize<(iOfst+iAmt) ){
- p->iSize = (iOfst+iAmt);
+#endif
+
+ for(iTask=0; rc==SQLITE_OK && iTask<pSorter->nTask; iTask++){
+ SortSubtask *pTask = &pSorter->aTask[iTask];
+ assert( pTask->nPMA>0 || SQLITE_MAX_WORKER_THREADS>0 );
+ if( SQLITE_MAX_WORKER_THREADS==0 || pTask->nPMA ){
+ MergeEngine *pRoot = 0; /* Root node of tree for this task */
+ int nDepth = vdbeSorterTreeDepth(pTask->nPMA);
+ i64 iReadOff = 0;
+
+ if( pTask->nPMA<=SORTER_MAX_MERGE_COUNT ){
+ rc = vdbeMergeEngineLevel0(pTask, pTask->nPMA, &iReadOff, &pRoot);
+ }else{
+ int i;
+ int iSeq = 0;
+ pRoot = vdbeMergeEngineNew(SORTER_MAX_MERGE_COUNT);
+ if( pRoot==0 ) rc = SQLITE_NOMEM_BKPT;
+ for(i=0; i<pTask->nPMA && rc==SQLITE_OK; i += SORTER_MAX_MERGE_COUNT){
+ MergeEngine *pMerger = 0; /* New level-0 PMA merger */
+ int nReader; /* Number of level-0 PMAs to merge */
+
+ nReader = MIN(pTask->nPMA - i, SORTER_MAX_MERGE_COUNT);
+ rc = vdbeMergeEngineLevel0(pTask, nReader, &iReadOff, &pMerger);
+ if( rc==SQLITE_OK ){
+ rc = vdbeSorterAddToTree(pTask, nDepth, iSeq++, pRoot, pMerger);
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( pMain!=0 ){
+ rc = vdbeIncrMergerNew(pTask, pRoot, &pMain->aReadr[iTask].pIncr);
+ }else
+#endif
+ {
+ assert( pMain==0 );
+ pMain = pRoot;
+ }
+ }else{
+ vdbeMergeEngineFree(pRoot);
}
}
}
+
+ if( rc!=SQLITE_OK ){
+ vdbeMergeEngineFree(pMain);
+ pMain = 0;
+ }
+ *ppOut = pMain;
return rc;
}
/*
-** Truncate the file.
+** This function is called as part of an sqlite3VdbeSorterRewind() operation
+** on a sorter that has written two or more PMAs to temporary files. It sets
+** up either VdbeSorter.pMerger (for single threaded sorters) or pReader
+** (for multi-threaded sorters) so that it can be used to iterate through
+** all records stored in the sorter.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
-static int jrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsTruncate(p->pReal, size);
- }else if( size<p->iSize ){
- p->iSize = size;
+static int vdbeSorterSetupMerge(VdbeSorter *pSorter){
+ int rc; /* Return code */
+ SortSubtask *pTask0 = &pSorter->aTask[0];
+ MergeEngine *pMain = 0;
+#if SQLITE_MAX_WORKER_THREADS
+ sqlite3 *db = pTask0->pSorter->db;
+ int i;
+ SorterCompare xCompare = vdbeSorterGetCompare(pSorter);
+ for(i=0; i<pSorter->nTask; i++){
+ pSorter->aTask[i].xCompare = xCompare;
+ }
+#endif
+
+ rc = vdbeSorterMergeTreeBuild(pSorter, &pMain);
+ if( rc==SQLITE_OK ){
+#if SQLITE_MAX_WORKER_THREADS
+ assert( pSorter->bUseThreads==0 || pSorter->nTask>1 );
+ if( pSorter->bUseThreads ){
+ int iTask;
+ PmaReader *pReadr = 0;
+ SortSubtask *pLast = &pSorter->aTask[pSorter->nTask-1];
+ rc = vdbeSortAllocUnpacked(pLast);
+ if( rc==SQLITE_OK ){
+ pReadr = (PmaReader*)sqlite3DbMallocZero(db, sizeof(PmaReader));
+ pSorter->pReader = pReadr;
+ if( pReadr==0 ) rc = SQLITE_NOMEM_BKPT;
+ }
+ if( rc==SQLITE_OK ){
+ rc = vdbeIncrMergerNew(pLast, pMain, &pReadr->pIncr);
+ if( rc==SQLITE_OK ){
+ vdbeIncrMergerSetThreads(pReadr->pIncr);
+ for(iTask=0; iTask<(pSorter->nTask-1); iTask++){
+ IncrMerger *pIncr;
+ if( (pIncr = pMain->aReadr[iTask].pIncr) ){
+ vdbeIncrMergerSetThreads(pIncr);
+ assert( pIncr->pTask!=pLast );
+ }
+ }
+ for(iTask=0; rc==SQLITE_OK && iTask<pSorter->nTask; iTask++){
+ /* Check that:
+ **
+ ** a) The incremental merge object is configured to use the
+ ** right task, and
+ ** b) If it is using task (nTask-1), it is configured to run
+ ** in single-threaded mode. This is important, as the
+ ** root merge (INCRINIT_ROOT) will be using the same task
+ ** object.
+ */
+ PmaReader *p = &pMain->aReadr[iTask];
+ assert( p->pIncr==0 || (
+ (p->pIncr->pTask==&pSorter->aTask[iTask]) /* a */
+ && (iTask!=pSorter->nTask-1 || p->pIncr->bUseThread==0) /* b */
+ ));
+ rc = vdbePmaReaderIncrInit(p, INCRINIT_TASK);
+ }
+ }
+ pMain = 0;
+ }
+ if( rc==SQLITE_OK ){
+ rc = vdbePmaReaderIncrMergeInit(pReadr, INCRINIT_ROOT);
+ }
+ }else
+#endif
+ {
+ rc = vdbeMergeEngineInit(pTask0, pMain, INCRINIT_NORMAL);
+ pSorter->pMerger = pMain;
+ pMain = 0;
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ vdbeMergeEngineFree(pMain);
}
return rc;
}
+
/*
-** Sync the file.
+** Once the sorter has been populated by calls to sqlite3VdbeSorterWrite,
+** this function is called to prepare for iterating through the records
+** in sorted order.
*/
-static int jrnlSync(sqlite3_file *pJfd, int flags){
- int rc;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsSync(p->pReal, flags);
- }else{
- rc = SQLITE_OK;
+SQLITE_PRIVATE int sqlite3VdbeSorterRewind(const VdbeCursor *pCsr, int *pbEof){
+ VdbeSorter *pSorter;
+ int rc = SQLITE_OK; /* Return code */
+
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
+ assert( pSorter );
+
+ /* If no data has been written to disk, then do not do so now. Instead,
+ ** sort the VdbeSorter.pRecord list. The vdbe layer will read data directly
+ ** from the in-memory list. */
+ if( pSorter->bUsePMA==0 ){
+ if( pSorter->list.pList ){
+ *pbEof = 0;
+ rc = vdbeSorterSort(&pSorter->aTask[0], &pSorter->list);
+ }else{
+ *pbEof = 1;
+ }
+ return rc;
}
+
+ /* Write the current in-memory list to a PMA. When the VdbeSorterWrite()
+ ** function flushes the contents of memory to disk, it immediately always
+ ** creates a new list consisting of a single key immediately afterwards.
+ ** So the list is never empty at this point. */
+ assert( pSorter->list.pList );
+ rc = vdbeSorterFlushPMA(pSorter);
+
+ /* Join all threads */
+ rc = vdbeSorterJoinAll(pSorter, rc);
+
+ vdbeSorterRewindDebug("rewind");
+
+ /* Assuming no errors have occurred, set up a merger structure to
+ ** incrementally read and merge all remaining PMAs. */
+ assert( pSorter->pReader==0 );
+ if( rc==SQLITE_OK ){
+ rc = vdbeSorterSetupMerge(pSorter);
+ *pbEof = 0;
+ }
+
+ vdbeSorterRewindDebug("rewinddone");
return rc;
}
/*
-** Query the size of the file in bytes.
+** Advance to the next element in the sorter.
*/
-static int jrnlFileSize(sqlite3_file *pJfd, sqlite_int64 *pSize){
- int rc = SQLITE_OK;
- JournalFile *p = (JournalFile *)pJfd;
- if( p->pReal ){
- rc = sqlite3OsFileSize(p->pReal, pSize);
+SQLITE_PRIVATE int sqlite3VdbeSorterNext(sqlite3 *db, const VdbeCursor *pCsr, int *pbEof){
+ VdbeSorter *pSorter;
+ int rc; /* Return code */
+
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
+ assert( pSorter->bUsePMA || (pSorter->pReader==0 && pSorter->pMerger==0) );
+ if( pSorter->bUsePMA ){
+ assert( pSorter->pReader==0 || pSorter->pMerger==0 );
+ assert( pSorter->bUseThreads==0 || pSorter->pReader );
+ assert( pSorter->bUseThreads==1 || pSorter->pMerger );
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( pSorter->bUseThreads ){
+ rc = vdbePmaReaderNext(pSorter->pReader);
+ *pbEof = (pSorter->pReader->pFd==0);
+ }else
+#endif
+ /*if( !pSorter->bUseThreads )*/ {
+ assert( pSorter->pMerger!=0 );
+ assert( pSorter->pMerger->pTask==(&pSorter->aTask[0]) );
+ rc = vdbeMergeEngineStep(pSorter->pMerger, pbEof);
+ }
}else{
- *pSize = (sqlite_int64) p->iSize;
+ SorterRecord *pFree = pSorter->list.pList;
+ pSorter->list.pList = pFree->u.pNext;
+ pFree->u.pNext = 0;
+ if( pSorter->list.aMemory==0 ) vdbeSorterRecordFree(db, pFree);
+ *pbEof = !pSorter->list.pList;
+ rc = SQLITE_OK;
}
return rc;
}
/*
-** Table of methods for JournalFile sqlite3_file object.
-*/
-static struct sqlite3_io_methods JournalFileMethods = {
- 1, /* iVersion */
- jrnlClose, /* xClose */
- jrnlRead, /* xRead */
- jrnlWrite, /* xWrite */
- jrnlTruncate, /* xTruncate */
- jrnlSync, /* xSync */
- jrnlFileSize, /* xFileSize */
- 0, /* xLock */
- 0, /* xUnlock */
- 0, /* xCheckReservedLock */
- 0, /* xFileControl */
- 0, /* xSectorSize */
- 0, /* xDeviceCharacteristics */
- 0, /* xShmMap */
- 0, /* xShmLock */
- 0, /* xShmBarrier */
- 0 /* xShmUnmap */
-};
-
-/*
-** Open a journal file.
+** Return a pointer to a buffer owned by the sorter that contains the
+** current key.
*/
-SQLITE_PRIVATE int sqlite3JournalOpen(
- sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
- const char *zName, /* Name of the journal file */
- sqlite3_file *pJfd, /* Preallocated, blank file handle */
- int flags, /* Opening flags */
- int nBuf /* Bytes buffered before opening the file */
+static void *vdbeSorterRowkey(
+ const VdbeSorter *pSorter, /* Sorter object */
+ int *pnKey /* OUT: Size of current key in bytes */
){
- JournalFile *p = (JournalFile *)pJfd;
- memset(p, 0, sqlite3JournalSize(pVfs));
- if( nBuf>0 ){
- p->zBuf = sqlite3MallocZero(nBuf);
- if( !p->zBuf ){
- return SQLITE_NOMEM;
+ void *pKey;
+ if( pSorter->bUsePMA ){
+ PmaReader *pReader;
+#if SQLITE_MAX_WORKER_THREADS>0
+ if( pSorter->bUseThreads ){
+ pReader = pSorter->pReader;
+ }else
+#endif
+ /*if( !pSorter->bUseThreads )*/{
+ pReader = &pSorter->pMerger->aReadr[pSorter->pMerger->aTree[1]];
}
+ *pnKey = pReader->nKey;
+ pKey = pReader->aKey;
}else{
- return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
+ *pnKey = pSorter->list.pList->nVal;
+ pKey = SRVAL(pSorter->list.pList);
}
- p->pMethod = &JournalFileMethods;
- p->nBuf = nBuf;
- p->flags = flags;
- p->zJournal = zName;
- p->pVfs = pVfs;
- return SQLITE_OK;
+ return pKey;
}
/*
-** If the argument p points to a JournalFile structure, and the underlying
-** file has not yet been created, create it now.
+** Copy the current sorter key into the memory cell pOut.
*/
-SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){
- if( p->pMethods!=&JournalFileMethods ){
- return SQLITE_OK;
+SQLITE_PRIVATE int sqlite3VdbeSorterRowkey(const VdbeCursor *pCsr, Mem *pOut){
+ VdbeSorter *pSorter;
+ void *pKey; int nKey; /* Sorter key to copy into pOut */
+
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
+ pKey = vdbeSorterRowkey(pSorter, &nKey);
+ if( sqlite3VdbeMemClearAndResize(pOut, nKey) ){
+ return SQLITE_NOMEM_BKPT;
}
- return createFile((JournalFile *)p);
+ pOut->n = nKey;
+ MemSetTypeFlag(pOut, MEM_Blob);
+ memcpy(pOut->z, pKey, nKey);
+
+ return SQLITE_OK;
}
-/*
-** Return the number of bytes required to store a JournalFile that uses vfs
-** pVfs to create the underlying on-disk files.
+/*
+** Compare the key in memory cell pVal with the key that the sorter cursor
+** passed as the first argument currently points to. For the purposes of
+** the comparison, ignore the rowid field at the end of each record.
+**
+** If the sorter cursor key contains any NULL values, consider it to be
+** less than pVal. Even if pVal also contains NULL values.
+**
+** If an error occurs, return an SQLite error code (i.e. SQLITE_NOMEM).
+** Otherwise, set *pRes to a negative, zero or positive value if the
+** key in pVal is smaller than, equal to or larger than the current sorter
+** key.
+**
+** This routine forms the core of the OP_SorterCompare opcode, which in
+** turn is used to verify uniqueness when constructing a UNIQUE INDEX.
*/
-SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
- return (pVfs->szOsFile+sizeof(JournalFile));
+SQLITE_PRIVATE int sqlite3VdbeSorterCompare(
+ const VdbeCursor *pCsr, /* Sorter cursor */
+ Mem *pVal, /* Value to compare to current sorter key */
+ int nKeyCol, /* Compare this many columns */
+ int *pRes /* OUT: Result of comparison */
+){
+ VdbeSorter *pSorter;
+ UnpackedRecord *r2;
+ KeyInfo *pKeyInfo;
+ int i;
+ void *pKey; int nKey; /* Sorter key to compare pVal with */
+
+ assert( pCsr->eCurType==CURTYPE_SORTER );
+ pSorter = pCsr->uc.pSorter;
+ r2 = pSorter->pUnpacked;
+ pKeyInfo = pCsr->pKeyInfo;
+ if( r2==0 ){
+ char *p;
+ r2 = pSorter->pUnpacked = sqlite3VdbeAllocUnpackedRecord(pKeyInfo,0,0,&p);
+ assert( pSorter->pUnpacked==(UnpackedRecord*)p );
+ if( r2==0 ) return SQLITE_NOMEM_BKPT;
+ r2->nField = nKeyCol;
+ }
+ assert( r2->nField==nKeyCol );
+
+ pKey = vdbeSorterRowkey(pSorter, &nKey);
+ sqlite3VdbeRecordUnpack(pKeyInfo, nKey, pKey, r2);
+ for(i=0; i<nKeyCol; i++){
+ if( r2->aMem[i].flags & MEM_Null ){
+ *pRes = -1;
+ return SQLITE_OK;
+ }
+ }
+
+ *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2);
+ return SQLITE_OK;
}
-#endif
-/************** End of journal.c *********************************************/
+/************** End of vdbesort.c ********************************************/
/************** Begin file memjournal.c **************************************/
/*
** 2008 October 7
@@ -71415,36 +87613,44 @@ SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
** This file contains code use to implement an in-memory rollback journal.
** The in-memory rollback journal is used to journal transactions for
** ":memory:" databases and when the journal_mode=MEMORY pragma is used.
+**
+** Update: The in-memory journal is also used to temporarily cache
+** smaller journals that are not critical for power-loss recovery.
+** For example, statement journals that are not too big will be held
+** entirely in memory, thus reducing the number of file I/O calls, and
+** more importantly, reducing temporary file creation events. If these
+** journals become too large for memory, they are spilled to disk. But
+** in the common case, they are usually small and no file I/O needs to
+** occur.
*/
+/* #include "sqliteInt.h" */
/* Forward references to internal structures */
typedef struct MemJournal MemJournal;
typedef struct FilePoint FilePoint;
typedef struct FileChunk FileChunk;
-/* Space to hold the rollback journal is allocated in increments of
-** this many bytes.
+/*
+** The rollback journal is composed of a linked list of these structures.
**
-** The size chosen is a little less than a power of two. That way,
-** the FileChunk object will have a size that almost exactly fills
-** a power-of-two allocation. This mimimizes wasted space in power-of-two
-** memory allocators.
+** The zChunk array is always at least 8 bytes in size - usually much more.
+** Its actual size is stored in the MemJournal.nChunkSize variable.
*/
-#define JOURNAL_CHUNKSIZE ((int)(1024-sizeof(FileChunk*)))
+struct FileChunk {
+ FileChunk *pNext; /* Next chunk in the journal */
+ u8 zChunk[8]; /* Content of this chunk */
+};
-/* Macro to find the minimum of two numeric values.
+/*
+** By default, allocate this many bytes of memory for each FileChunk object.
*/
-#ifndef MIN
-# define MIN(x,y) ((x)<(y)?(x):(y))
-#endif
+#define MEMJOURNAL_DFLT_FILECHUNKSIZE 1024
/*
-** The rollback journal is composed of a linked list of these structures.
+** For chunk size nChunkSize, return the number of bytes that should
+** be allocated for each FileChunk structure.
*/
-struct FileChunk {
- FileChunk *pNext; /* Next chunk in the journal */
- u8 zChunk[JOURNAL_CHUNKSIZE]; /* Content of this chunk */
-};
+#define fileChunkSize(nChunkSize) (sizeof(FileChunk) + ((nChunkSize)-8))
/*
** An instance of this object serves as a cursor into the rollback journal.
@@ -71456,14 +87662,22 @@ struct FilePoint {
};
/*
-** This subclass is a subclass of sqlite3_file. Each open memory-journal
+** This structure is a subclass of sqlite3_file. Each open memory-journal
** is an instance of this class.
*/
struct MemJournal {
- sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
+ const sqlite3_io_methods *pMethod; /* Parent class. MUST BE FIRST */
+ int nChunkSize; /* In-memory chunk-size */
+
+ int nSpill; /* Bytes of data before flushing */
+ int nSize; /* Bytes of data currently in memory */
FileChunk *pFirst; /* Head of in-memory chunk-list */
FilePoint endpoint; /* Pointer to the end of the file */
FilePoint readpoint; /* Pointer to the end of the last xRead() */
+
+ int flags; /* xOpen flags */
+ sqlite3_vfs *pVfs; /* The "real" underlying VFS */
+ const char *zJournal; /* Name of the journal file */
};
/*
@@ -71482,37 +87696,95 @@ static int memjrnlRead(
int iChunkOffset;
FileChunk *pChunk;
- /* SQLite never tries to read past the end of a rollback journal file */
- assert( iOfst+iAmt<=p->endpoint.iOffset );
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ if( (iAmt+iOfst)>p->endpoint.iOffset ){
+ return SQLITE_IOERR_SHORT_READ;
+ }
+#endif
+ assert( (iAmt+iOfst)<=p->endpoint.iOffset );
+ assert( p->readpoint.iOffset==0 || p->readpoint.pChunk!=0 );
if( p->readpoint.iOffset!=iOfst || iOfst==0 ){
sqlite3_int64 iOff = 0;
for(pChunk=p->pFirst;
- ALWAYS(pChunk) && (iOff+JOURNAL_CHUNKSIZE)<=iOfst;
+ ALWAYS(pChunk) && (iOff+p->nChunkSize)<=iOfst;
pChunk=pChunk->pNext
){
- iOff += JOURNAL_CHUNKSIZE;
+ iOff += p->nChunkSize;
}
}else{
pChunk = p->readpoint.pChunk;
+ assert( pChunk!=0 );
}
- iChunkOffset = (int)(iOfst%JOURNAL_CHUNKSIZE);
+ iChunkOffset = (int)(iOfst%p->nChunkSize);
do {
- int iSpace = JOURNAL_CHUNKSIZE - iChunkOffset;
- int nCopy = MIN(nRead, (JOURNAL_CHUNKSIZE - iChunkOffset));
- memcpy(zOut, &pChunk->zChunk[iChunkOffset], nCopy);
+ int iSpace = p->nChunkSize - iChunkOffset;
+ int nCopy = MIN(nRead, (p->nChunkSize - iChunkOffset));
+ memcpy(zOut, (u8*)pChunk->zChunk + iChunkOffset, nCopy);
zOut += nCopy;
nRead -= iSpace;
iChunkOffset = 0;
} while( nRead>=0 && (pChunk=pChunk->pNext)!=0 && nRead>0 );
- p->readpoint.iOffset = iOfst+iAmt;
+ p->readpoint.iOffset = pChunk ? iOfst+iAmt : 0;
p->readpoint.pChunk = pChunk;
return SQLITE_OK;
}
/*
+** Free the list of FileChunk structures headed at MemJournal.pFirst.
+*/
+static void memjrnlFreeChunks(MemJournal *p){
+ FileChunk *pIter;
+ FileChunk *pNext;
+ for(pIter=p->pFirst; pIter; pIter=pNext){
+ pNext = pIter->pNext;
+ sqlite3_free(pIter);
+ }
+ p->pFirst = 0;
+}
+
+/*
+** Flush the contents of memory to a real file on disk.
+*/
+static int memjrnlCreateFile(MemJournal *p){
+ int rc;
+ sqlite3_file *pReal = (sqlite3_file*)p;
+ MemJournal copy = *p;
+
+ memset(p, 0, sizeof(MemJournal));
+ rc = sqlite3OsOpen(copy.pVfs, copy.zJournal, pReal, copy.flags, 0);
+ if( rc==SQLITE_OK ){
+ int nChunk = copy.nChunkSize;
+ i64 iOff = 0;
+ FileChunk *pIter;
+ for(pIter=copy.pFirst; pIter; pIter=pIter->pNext){
+ if( iOff + nChunk > copy.endpoint.iOffset ){
+ nChunk = copy.endpoint.iOffset - iOff;
+ }
+ rc = sqlite3OsWrite(pReal, (u8*)pIter->zChunk, nChunk, iOff);
+ if( rc ) break;
+ iOff += nChunk;
+ }
+ if( rc==SQLITE_OK ){
+ /* No error has occurred. Free the in-memory buffers. */
+ memjrnlFreeChunks(&copy);
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ /* If an error occurred while creating or writing to the file, restore
+ ** the original before returning. This way, SQLite uses the in-memory
+ ** journal data to roll back changes made to the internal page-cache
+ ** before this function was called. */
+ sqlite3OsClose(pReal);
+ *p = copy;
+ }
+ return rc;
+}
+
+
+/*
** Write data to the file.
*/
static int memjrnlWrite(
@@ -71525,38 +87797,62 @@ static int memjrnlWrite(
int nWrite = iAmt;
u8 *zWrite = (u8 *)zBuf;
- /* An in-memory journal file should only ever be appended to. Random
- ** access writes are not required by sqlite.
- */
- assert( iOfst==p->endpoint.iOffset );
- UNUSED_PARAMETER(iOfst);
+ /* If the file should be created now, create it and write the new data
+ ** into the file on disk. */
+ if( p->nSpill>0 && (iAmt+iOfst)>p->nSpill ){
+ int rc = memjrnlCreateFile(p);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsWrite(pJfd, zBuf, iAmt, iOfst);
+ }
+ return rc;
+ }
- while( nWrite>0 ){
- FileChunk *pChunk = p->endpoint.pChunk;
- int iChunkOffset = (int)(p->endpoint.iOffset%JOURNAL_CHUNKSIZE);
- int iSpace = MIN(nWrite, JOURNAL_CHUNKSIZE - iChunkOffset);
+ /* If the contents of this write should be stored in memory */
+ else{
+ /* An in-memory journal file should only ever be appended to. Random
+ ** access writes are not required. The only exception to this is when
+ ** the in-memory journal is being used by a connection using the
+ ** atomic-write optimization. In this case the first 28 bytes of the
+ ** journal file may be written as part of committing the transaction. */
+ assert( iOfst==p->endpoint.iOffset || iOfst==0 );
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
+ if( iOfst==0 && p->pFirst ){
+ assert( p->nChunkSize>iAmt );
+ memcpy((u8*)p->pFirst->zChunk, zBuf, iAmt);
+ }else
+#else
+ assert( iOfst>0 || p->pFirst==0 );
+#endif
+ {
+ while( nWrite>0 ){
+ FileChunk *pChunk = p->endpoint.pChunk;
+ int iChunkOffset = (int)(p->endpoint.iOffset%p->nChunkSize);
+ int iSpace = MIN(nWrite, p->nChunkSize - iChunkOffset);
+
+ if( iChunkOffset==0 ){
+ /* New chunk is required to extend the file. */
+ FileChunk *pNew = sqlite3_malloc(fileChunkSize(p->nChunkSize));
+ if( !pNew ){
+ return SQLITE_IOERR_NOMEM_BKPT;
+ }
+ pNew->pNext = 0;
+ if( pChunk ){
+ assert( p->pFirst );
+ pChunk->pNext = pNew;
+ }else{
+ assert( !p->pFirst );
+ p->pFirst = pNew;
+ }
+ p->endpoint.pChunk = pNew;
+ }
- if( iChunkOffset==0 ){
- /* New chunk is required to extend the file. */
- FileChunk *pNew = sqlite3_malloc(sizeof(FileChunk));
- if( !pNew ){
- return SQLITE_IOERR_NOMEM;
- }
- pNew->pNext = 0;
- if( pChunk ){
- assert( p->pFirst );
- pChunk->pNext = pNew;
- }else{
- assert( !p->pFirst );
- p->pFirst = pNew;
+ memcpy((u8*)p->endpoint.pChunk->zChunk + iChunkOffset, zWrite, iSpace);
+ zWrite += iSpace;
+ nWrite -= iSpace;
+ p->endpoint.iOffset += iSpace;
}
- p->endpoint.pChunk = pNew;
+ p->nSize = iAmt + iOfst;
}
-
- memcpy(&p->endpoint.pChunk->zChunk[iChunkOffset], zWrite, iSpace);
- zWrite += iSpace;
- nWrite -= iSpace;
- p->endpoint.iOffset += iSpace;
}
return SQLITE_OK;
@@ -71564,19 +87860,21 @@ static int memjrnlWrite(
/*
** Truncate the file.
+**
+** If the journal file is already on disk, truncate it there. Or, if it
+** is still in main memory but is being truncated to zero bytes in size,
+** ignore
*/
static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
MemJournal *p = (MemJournal *)pJfd;
- FileChunk *pChunk;
- assert(size==0);
- UNUSED_PARAMETER(size);
- pChunk = p->pFirst;
- while( pChunk ){
- FileChunk *pTmp = pChunk;
- pChunk = pChunk->pNext;
- sqlite3_free(pTmp);
- }
- sqlite3MemJournalOpen(pJfd);
+ if( ALWAYS(size==0) ){
+ memjrnlFreeChunks(p);
+ p->nSize = 0;
+ p->endpoint.pChunk = 0;
+ p->endpoint.iOffset = 0;
+ p->readpoint.pChunk = 0;
+ p->readpoint.iOffset = 0;
+ }
return SQLITE_OK;
}
@@ -71584,21 +87882,19 @@ static int memjrnlTruncate(sqlite3_file *pJfd, sqlite_int64 size){
** Close the file.
*/
static int memjrnlClose(sqlite3_file *pJfd){
- memjrnlTruncate(pJfd, 0);
+ MemJournal *p = (MemJournal *)pJfd;
+ memjrnlFreeChunks(p);
return SQLITE_OK;
}
-
/*
** Sync the file.
**
-** Syncing an in-memory journal is a no-op. And, in fact, this routine
-** is never called in a working implementation. This implementation
-** exists purely as a contingency, in case some malfunction in some other
-** part of SQLite causes Sync to be called by mistake.
+** If the real file has been created, call its xSync method. Otherwise,
+** syncing an in-memory journal is a no-op.
*/
-static int memjrnlSync(sqlite3_file *NotUsed, int NotUsed2){
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
+static int memjrnlSync(sqlite3_file *pJfd, int flags){
+ UNUSED_PARAMETER2(pJfd, flags);
return SQLITE_OK;
}
@@ -71631,32 +87927,94 @@ static const struct sqlite3_io_methods MemJournalMethods = {
0, /* xShmMap */
0, /* xShmLock */
0, /* xShmBarrier */
- 0 /* xShmUnlock */
+ 0, /* xShmUnmap */
+ 0, /* xFetch */
+ 0 /* xUnfetch */
};
/*
-** Open a journal file.
+** Open a journal file.
+**
+** The behaviour of the journal file depends on the value of parameter
+** nSpill. If nSpill is 0, then the journal file is always create and
+** accessed using the underlying VFS. If nSpill is less than zero, then
+** all content is always stored in main-memory. Finally, if nSpill is a
+** positive value, then the journal file is initially created in-memory
+** but may be flushed to disk later on. In this case the journal file is
+** flushed to disk either when it grows larger than nSpill bytes in size,
+** or when sqlite3JournalCreate() is called.
+*/
+SQLITE_PRIVATE int sqlite3JournalOpen(
+ sqlite3_vfs *pVfs, /* The VFS to use for actual file I/O */
+ const char *zName, /* Name of the journal file */
+ sqlite3_file *pJfd, /* Preallocated, blank file handle */
+ int flags, /* Opening flags */
+ int nSpill /* Bytes buffered before opening the file */
+){
+ MemJournal *p = (MemJournal*)pJfd;
+
+ /* Zero the file-handle object. If nSpill was passed zero, initialize
+ ** it using the sqlite3OsOpen() function of the underlying VFS. In this
+ ** case none of the code in this module is executed as a result of calls
+ ** made on the journal file-handle. */
+ memset(p, 0, sizeof(MemJournal));
+ if( nSpill==0 ){
+ return sqlite3OsOpen(pVfs, zName, pJfd, flags, 0);
+ }
+
+ if( nSpill>0 ){
+ p->nChunkSize = nSpill;
+ }else{
+ p->nChunkSize = 8 + MEMJOURNAL_DFLT_FILECHUNKSIZE - sizeof(FileChunk);
+ assert( MEMJOURNAL_DFLT_FILECHUNKSIZE==fileChunkSize(p->nChunkSize) );
+ }
+
+ p->pMethod = (const sqlite3_io_methods*)&MemJournalMethods;
+ p->nSpill = nSpill;
+ p->flags = flags;
+ p->zJournal = zName;
+ p->pVfs = pVfs;
+ return SQLITE_OK;
+}
+
+/*
+** Open an in-memory journal file.
*/
SQLITE_PRIVATE void sqlite3MemJournalOpen(sqlite3_file *pJfd){
- MemJournal *p = (MemJournal *)pJfd;
- assert( EIGHT_BYTE_ALIGNMENT(p) );
- memset(p, 0, sqlite3MemJournalSize());
- p->pMethod = (sqlite3_io_methods*)&MemJournalMethods;
+ sqlite3JournalOpen(0, 0, pJfd, 0, -1);
}
+#ifdef SQLITE_ENABLE_ATOMIC_WRITE
/*
-** Return true if the file-handle passed as an argument is
-** an in-memory journal
+** If the argument p points to a MemJournal structure that is not an
+** in-memory-only journal file (i.e. is one that was opened with a +ve
+** nSpill parameter), and the underlying file has not yet been created,
+** create it now.
*/
-SQLITE_PRIVATE int sqlite3IsMemJournal(sqlite3_file *pJfd){
- return pJfd->pMethods==&MemJournalMethods;
+SQLITE_PRIVATE int sqlite3JournalCreate(sqlite3_file *p){
+ int rc = SQLITE_OK;
+ if( p->pMethods==&MemJournalMethods && ((MemJournal*)p)->nSpill>0 ){
+ rc = memjrnlCreateFile((MemJournal*)p);
+ }
+ return rc;
+}
+#endif
+
+/*
+** The file-handle passed as the only argument is open on a journal file.
+** Return true if this "journal file" is currently stored in heap memory,
+** or false otherwise.
+*/
+SQLITE_PRIVATE int sqlite3JournalIsInMemory(sqlite3_file *p){
+ return p->pMethods==&MemJournalMethods;
}
/*
-** Return the number of bytes required to store a MemJournal file descriptor.
+** Return the number of bytes required to store a JournalFile that uses vfs
+** pVfs to create the underlying on-disk files.
*/
-SQLITE_PRIVATE int sqlite3MemJournalSize(void){
- return sizeof(MemJournal);
+SQLITE_PRIVATE int sqlite3JournalSize(sqlite3_vfs *pVfs){
+ return MAX(pVfs->szOsFile, (int)sizeof(MemJournal));
}
/************** End of memjournal.c ******************************************/
@@ -71675,13 +88033,14 @@ SQLITE_PRIVATE int sqlite3MemJournalSize(void){
** This file contains routines used for walking the parser tree for
** an SQL statement.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <string.h> */
/*
** Walk an expression tree. Invoke the callback once for each node
-** of the expression, while decending. (In other words, the callback
+** of the expression, while descending. (In other words, the callback
** is invoked before visiting children.)
**
** The return value from the callback should be one of the WRC_*
@@ -71698,23 +88057,25 @@ SQLITE_PRIVATE int sqlite3MemJournalSize(void){
** The return value from this routine is WRC_Abort to abandon the tree walk
** and WRC_Continue to continue.
*/
-SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
+static SQLITE_NOINLINE int walkExpr(Walker *pWalker, Expr *pExpr){
int rc;
- if( pExpr==0 ) return WRC_Continue;
testcase( ExprHasProperty(pExpr, EP_TokenOnly) );
testcase( ExprHasProperty(pExpr, EP_Reduced) );
rc = pWalker->xExprCallback(pWalker, pExpr);
- if( rc==WRC_Continue
- && !ExprHasAnyProperty(pExpr,EP_TokenOnly) ){
- if( sqlite3WalkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
- if( sqlite3WalkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
- }else{
- if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
- }
+ if( rc || ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
+ return rc & WRC_Abort;
}
- return rc & WRC_Abort;
+ if( pExpr->pLeft && walkExpr(pWalker, pExpr->pLeft) ) return WRC_Abort;
+ if( pExpr->pRight && walkExpr(pWalker, pExpr->pRight) ) return WRC_Abort;
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
+ }else if( pExpr->x.pList ){
+ if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
+ }
+ return WRC_Continue;
+}
+SQLITE_PRIVATE int sqlite3WalkExpr(Walker *pWalker, Expr *pExpr){
+ return pExpr ? walkExpr(pWalker,pExpr) : WRC_Continue;
}
/*
@@ -71767,6 +88128,11 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
if( sqlite3WalkSelect(pWalker, pItem->pSelect) ){
return WRC_Abort;
}
+ if( pItem->fg.isTabFunc
+ && sqlite3WalkExprList(pWalker, pItem->u1.pFuncArg)
+ ){
+ return WRC_Abort;
+ }
}
}
return WRC_Continue;
@@ -71775,7 +88141,12 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
/*
** Call sqlite3WalkExpr() for every expression in Select statement p.
** Invoke sqlite3WalkSelect() for subqueries in the FROM clause and
-** on the compound select chain, p->pPrior.
+** on the compound select chain, p->pPrior.
+**
+** If it is not NULL, the xSelectCallback() callback is invoked before
+** the walk of the expressions and FROM clause. The xSelectCallback2()
+** method, if it is not NULL, is invoked following the walk of the
+** expressions and FROM clause.
**
** Return WRC_Continue under normal conditions. Return WRC_Abort if
** there is an abort request.
@@ -71785,15 +88156,28 @@ SQLITE_PRIVATE int sqlite3WalkSelectFrom(Walker *pWalker, Select *p){
*/
SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
int rc;
- if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue;
+ if( p==0 || (pWalker->xSelectCallback==0 && pWalker->xSelectCallback2==0) ){
+ return WRC_Continue;
+ }
rc = WRC_Continue;
- while( p ){
- rc = pWalker->xSelectCallback(pWalker, p);
- if( rc ) break;
- if( sqlite3WalkSelectExpr(pWalker, p) ) return WRC_Abort;
- if( sqlite3WalkSelectFrom(pWalker, p) ) return WRC_Abort;
+ pWalker->walkerDepth++;
+ while( p ){
+ if( pWalker->xSelectCallback ){
+ rc = pWalker->xSelectCallback(pWalker, p);
+ if( rc ) break;
+ }
+ if( sqlite3WalkSelectExpr(pWalker, p)
+ || sqlite3WalkSelectFrom(pWalker, p)
+ ){
+ pWalker->walkerDepth--;
+ return WRC_Abort;
+ }
+ if( pWalker->xSelectCallback2 ){
+ pWalker->xSelectCallback2(pWalker, p);
+ }
p = p->pPrior;
}
+ pWalker->walkerDepth--;
return rc & WRC_Abort;
}
@@ -71815,42 +88199,59 @@ SQLITE_PRIVATE int sqlite3WalkSelect(Walker *pWalker, Select *p){
** resolve all identifiers by associating them with a particular
** table and column.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <string.h> */
/*
+** Walk the expression tree pExpr and increase the aggregate function
+** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node.
+** This needs to occur when copying a TK_AGG_FUNCTION node from an
+** outer query into an inner subquery.
+**
+** incrAggFunctionDepth(pExpr,n) is the main routine. incrAggDepth(..)
+** is a helper function - a callback for the tree walker.
+*/
+static int incrAggDepth(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_AGG_FUNCTION ) pExpr->op2 += pWalker->u.n;
+ return WRC_Continue;
+}
+static void incrAggFunctionDepth(Expr *pExpr, int N){
+ if( N>0 ){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = incrAggDepth;
+ w.u.n = N;
+ sqlite3WalkExpr(&w, pExpr);
+ }
+}
+
+/*
** Turn the pExpr expression into an alias for the iCol-th column of the
** result set in pEList.
**
-** If the result set column is a simple column reference, then this routine
-** makes an exact copy. But for any other kind of expression, this
-** routine make a copy of the result set column as the argument to the
-** TK_AS operator. The TK_AS operator causes the expression to be
-** evaluated just once and then reused for each alias.
-**
-** The reason for suppressing the TK_AS term when the expression is a simple
-** column reference is so that the column reference will be recognized as
-** usable by indices within the WHERE clause processing logic.
+** If the reference is followed by a COLLATE operator, then make sure
+** the COLLATE operator is preserved. For example:
**
-** Hack: The TK_AS operator is inhibited if zType[0]=='G'. This means
-** that in a GROUP BY clause, the expression is evaluated twice. Hence:
+** SELECT a+b, c+d FROM t1 ORDER BY 1 COLLATE nocase;
**
-** SELECT random()%5 AS x, count(*) FROM tab GROUP BY x
+** Should be transformed into:
**
-** Is equivalent to:
+** SELECT a+b, c+d FROM t1 ORDER BY (a+b) COLLATE nocase;
**
-** SELECT random()%5 AS x, count(*) FROM tab GROUP BY random()%5
-**
-** The result of random()%5 in the GROUP BY clause is probably different
-** from the result in the result-set. We might fix this someday. Or
-** then again, we might not...
+** The nSubquery parameter specifies how many levels of subquery the
+** alias is removed from the original expression. The usual value is
+** zero but it might be more if the alias is contained within a subquery
+** of the original expression. The Expr.op2 field of TK_AGG_FUNCTION
+** structures must be increased by the nSubquery amount.
*/
static void resolveAlias(
Parse *pParse, /* Parsing context */
ExprList *pEList, /* A result set */
int iCol, /* A column in the result set. 0..pEList->nExpr-1 */
Expr *pExpr, /* Transform this into an alias to the result set */
- const char *zType /* "GROUP" or "ORDER" or "" */
+ const char *zType, /* "GROUP" or "ORDER" or "" */
+ int nSubquery /* Number of subqueries that the label is moving */
){
Expr *pOrig; /* The iCol-th column of the result set */
Expr *pDup; /* Copy of pOrig */
@@ -71859,45 +88260,80 @@ static void resolveAlias(
assert( iCol>=0 && iCol<pEList->nExpr );
pOrig = pEList->a[iCol].pExpr;
assert( pOrig!=0 );
- assert( pOrig->flags & EP_Resolved );
db = pParse->db;
- if( pOrig->op!=TK_COLUMN && zType[0]!='G' ){
- pDup = sqlite3ExprDup(db, pOrig, 0);
- pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0);
- if( pDup==0 ) return;
- if( pEList->a[iCol].iAlias==0 ){
- pEList->a[iCol].iAlias = (u16)(++pParse->nAlias);
- }
- pDup->iTable = pEList->a[iCol].iAlias;
- }else if( ExprHasProperty(pOrig, EP_IntValue) || pOrig->u.zToken==0 ){
- pDup = sqlite3ExprDup(db, pOrig, 0);
- if( pDup==0 ) return;
- }else{
- char *zToken = pOrig->u.zToken;
- assert( zToken!=0 );
- pOrig->u.zToken = 0;
- pDup = sqlite3ExprDup(db, pOrig, 0);
- pOrig->u.zToken = zToken;
- if( pDup==0 ) return;
- assert( (pDup->flags & (EP_Reduced|EP_TokenOnly))==0 );
- pDup->flags2 |= EP2_MallocedToken;
- pDup->u.zToken = sqlite3DbStrDup(db, zToken);
- }
- if( pExpr->flags & EP_ExpCollate ){
- pDup->pColl = pExpr->pColl;
- pDup->flags |= EP_ExpCollate;
+ pDup = sqlite3ExprDup(db, pOrig, 0);
+ if( pDup==0 ) return;
+ if( zType[0]!='G' ) incrAggFunctionDepth(pDup, nSubquery);
+ if( pExpr->op==TK_COLLATE ){
+ pDup = sqlite3ExprAddCollateString(pParse, pDup, pExpr->u.zToken);
}
+ ExprSetProperty(pDup, EP_Alias);
/* Before calling sqlite3ExprDelete(), set the EP_Static flag. This
** prevents ExprDelete() from deleting the Expr structure itself,
** allowing it to be repopulated by the memcpy() on the following line.
+ ** The pExpr->u.zToken might point into memory that will be freed by the
+ ** sqlite3DbFree(db, pDup) on the last line of this block, so be sure to
+ ** make a copy of the token before doing the sqlite3DbFree().
*/
ExprSetProperty(pExpr, EP_Static);
sqlite3ExprDelete(db, pExpr);
memcpy(pExpr, pDup, sizeof(*pExpr));
+ if( !ExprHasProperty(pExpr, EP_IntValue) && pExpr->u.zToken!=0 ){
+ assert( (pExpr->flags & (EP_Reduced|EP_TokenOnly))==0 );
+ pExpr->u.zToken = sqlite3DbStrDup(db, pExpr->u.zToken);
+ pExpr->flags |= EP_MemToken;
+ }
sqlite3DbFree(db, pDup);
}
+
+/*
+** Return TRUE if the name zCol occurs anywhere in the USING clause.
+**
+** Return FALSE if the USING clause is NULL or if it does not contain
+** zCol.
+*/
+static int nameInUsingClause(IdList *pUsing, const char *zCol){
+ if( pUsing ){
+ int k;
+ for(k=0; k<pUsing->nId; k++){
+ if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ) return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Subqueries stores the original database, table and column names for their
+** result sets in ExprList.a[].zSpan, in the form "DATABASE.TABLE.COLUMN".
+** Check to see if the zSpan given to this routine matches the zDb, zTab,
+** and zCol. If any of zDb, zTab, and zCol are NULL then those fields will
+** match anything.
+*/
+SQLITE_PRIVATE int sqlite3MatchSpanName(
+ const char *zSpan,
+ const char *zCol,
+ const char *zTab,
+ const char *zDb
+){
+ int n;
+ for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
+ if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
+ return 0;
+ }
+ zSpan += n+1;
+ for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
+ if( zTab && (sqlite3StrNICmp(zSpan, zTab, n)!=0 || zTab[n]!=0) ){
+ return 0;
+ }
+ zSpan += n+1;
+ if( zCol && sqlite3StrICmp(zSpan, zCol)!=0 ){
+ return 0;
+ }
+ return 1;
+}
+
/*
** Given the name of a column of the form X.Y.Z or Y.Z or just Z, look up
** that name in the set of source tables in pSrcList and make the pExpr
@@ -71933,24 +88369,51 @@ static int lookupName(
NameContext *pNC, /* The name context used to resolve the name */
Expr *pExpr /* Make this EXPR node point to the selected column */
){
- int i, j; /* Loop counters */
+ int i, j; /* Loop counters */
int cnt = 0; /* Number of matching column names */
int cntTab = 0; /* Number of matching table names */
+ int nSubquery = 0; /* How many levels of subquery */
sqlite3 *db = pParse->db; /* The database connection */
struct SrcList_item *pItem; /* Use for looping over pSrcList items */
struct SrcList_item *pMatch = 0; /* The matching pSrcList item */
NameContext *pTopNC = pNC; /* First namecontext in the list */
Schema *pSchema = 0; /* Schema of the expression */
- int isTrigger = 0;
+ int isTrigger = 0; /* True if resolved to a trigger column */
+ Table *pTab = 0; /* Table hold the row */
+ Column *pCol; /* A column of pTab */
assert( pNC ); /* the name context cannot be NULL. */
assert( zCol ); /* The Z in X.Y.Z cannot be NULL */
- assert( ~ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
/* Initialize the node to no-match */
pExpr->iTable = -1;
pExpr->pTab = 0;
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
+
+ /* Translate the schema name in zDb into a pointer to the corresponding
+ ** schema. If not found, pSchema will remain NULL and nothing will match
+ ** resulting in an appropriate error message toward the end of this routine
+ */
+ if( zDb ){
+ testcase( pNC->ncFlags & NC_PartIdx );
+ testcase( pNC->ncFlags & NC_IsCheck );
+ if( (pNC->ncFlags & (NC_PartIdx|NC_IsCheck))!=0 ){
+ /* Silently ignore database qualifiers inside CHECK constraints and
+ ** partial indices. Do not raise errors because that might break
+ ** legacy and because it does not hurt anything to just ignore the
+ ** database name. */
+ zDb = 0;
+ }else{
+ for(i=0; i<db->nDb; i++){
+ assert( db->aDb[i].zDbSName );
+ if( sqlite3StrICmp(db->aDb[i].zDbSName,zDb)==0 ){
+ pSchema = db->aDb[i].pSchema;
+ break;
+ }
+ }
+ }
+ }
/* Start at the inner-most context and move outward until a match is found */
while( pNC && cnt==0 ){
@@ -71959,77 +88422,72 @@ static int lookupName(
if( pSrcList ){
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
- Table *pTab;
- int iDb;
- Column *pCol;
-
pTab = pItem->pTab;
assert( pTab!=0 && pTab->zName!=0 );
- iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( pTab->nCol>0 );
- if( zTab ){
- if( pItem->zAlias ){
- char *zTabName = pItem->zAlias;
- if( sqlite3StrICmp(zTabName, zTab)!=0 ) continue;
- }else{
- char *zTabName = pTab->zName;
- if( NEVER(zTabName==0) || sqlite3StrICmp(zTabName, zTab)!=0 ){
- continue;
- }
- if( zDb!=0 && sqlite3StrICmp(db->aDb[iDb].zName, zDb)!=0 ){
- continue;
+ if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
+ int hit = 0;
+ pEList = pItem->pSelect->pEList;
+ for(j=0; j<pEList->nExpr; j++){
+ if( sqlite3MatchSpanName(pEList->a[j].zSpan, zCol, zTab, zDb) ){
+ cnt++;
+ cntTab = 2;
+ pMatch = pItem;
+ pExpr->iColumn = j;
+ hit = 1;
}
}
+ if( hit || zTab==0 ) continue;
+ }
+ if( zDb && pTab->pSchema!=pSchema ){
+ continue;
+ }
+ if( zTab ){
+ const char *zTabName = pItem->zAlias ? pItem->zAlias : pTab->zName;
+ assert( zTabName!=0 );
+ if( sqlite3StrICmp(zTabName, zTab)!=0 ){
+ continue;
+ }
}
if( 0==(cntTab++) ){
- pExpr->iTable = pItem->iCursor;
- pExpr->pTab = pTab;
- pSchema = pTab->pSchema;
pMatch = pItem;
}
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
- IdList *pUsing;
+ /* If there has been exactly one prior match and this match
+ ** is for the right-hand table of a NATURAL JOIN or is in a
+ ** USING clause, then skip this match.
+ */
+ if( cnt==1 ){
+ if( pItem->fg.jointype & JT_NATURAL ) continue;
+ if( nameInUsingClause(pItem->pUsing, zCol) ) continue;
+ }
cnt++;
- pExpr->iTable = pItem->iCursor;
- pExpr->pTab = pTab;
pMatch = pItem;
- pSchema = pTab->pSchema;
/* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
- if( i<pSrcList->nSrc-1 ){
- if( pItem[1].jointype & JT_NATURAL ){
- /* If this match occurred in the left table of a natural join,
- ** then skip the right table to avoid a duplicate match */
- pItem++;
- i++;
- }else if( (pUsing = pItem[1].pUsing)!=0 ){
- /* If this match occurs on a column that is in the USING clause
- ** of a join, skip the search of the right table of the join
- ** to avoid a duplicate match there. */
- int k;
- for(k=0; k<pUsing->nId; k++){
- if( sqlite3StrICmp(pUsing->a[k].zName, zCol)==0 ){
- pItem++;
- i++;
- break;
- }
- }
- }
- }
break;
}
}
}
- }
+ if( pMatch ){
+ pExpr->iTable = pMatch->iCursor;
+ pExpr->pTab = pMatch->pTab;
+ /* RIGHT JOIN not (yet) supported */
+ assert( (pMatch->fg.jointype & JT_RIGHT)==0 );
+ if( (pMatch->fg.jointype & JT_LEFT)!=0 ){
+ ExprSetProperty(pExpr, EP_CanBeNull);
+ }
+ pSchema = pExpr->pTab->pSchema;
+ }
+ } /* if( pSrcList ) */
#ifndef SQLITE_OMIT_TRIGGER
/* If we have not already resolved the name, then maybe
** it is a new.* or old.* trigger argument reference
*/
- if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){
+ if( zDb==0 && zTab!=0 && cntTab==0 && pParse->pTriggerTab!=0 ){
int op = pParse->eTriggerOp;
- Table *pTab = 0;
assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT );
if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){
pExpr->iTable = 1;
@@ -72037,14 +88495,15 @@ static int lookupName(
}else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){
pExpr->iTable = 0;
pTab = pParse->pTriggerTab;
+ }else{
+ pTab = 0;
}
if( pTab ){
int iCol;
pSchema = pTab->pSchema;
cntTab++;
- for(iCol=0; iCol<pTab->nCol; iCol++){
- Column *pCol = &pTab->aCol[iCol];
+ for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
if( iCol==pTab->iPKey ){
iCol = -1;
@@ -72052,8 +88511,9 @@ static int lookupName(
break;
}
}
- if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) ){
- iCol = -1; /* IMP: R-44911-55124 */
+ if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){
+ /* IMP: R-51414-32910 */
+ iCol = -1;
}
if( iCol<pTab->nCol ){
cnt++;
@@ -72079,9 +88539,15 @@ static int lookupName(
/*
** Perhaps the name is a reference to the ROWID
*/
- if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){
+ if( cnt==0
+ && cntTab==1
+ && pMatch
+ && (pNC->ncFlags & NC_IdxExpr)==0
+ && sqlite3IsRowid(zCol)
+ && VisibleRowid(pMatch->pTab)
+ ){
cnt = 1;
- pExpr->iColumn = -1; /* IMP: R-44911-55124 */
+ pExpr->iColumn = -1;
pExpr->affinity = SQLITE_AFF_INTEGER;
}
@@ -72096,8 +88562,17 @@ static int lookupName(
** forms the result set entry ("a+b" in the example) and return immediately.
** Note that the expression in the result set should have already been
** resolved by the time the WHERE clause is resolved.
+ **
+ ** The ability to use an output result-set column in the WHERE, GROUP BY,
+ ** or HAVING clauses, or as part of a larger expression in the ORDER BY
+ ** clause is not standard SQL. This is a (goofy) SQLite extension, that
+ ** is supported for backwards compatibility only. Hence, we issue a warning
+ ** on sqlite3_log() whenever the capability is used.
*/
- if( cnt==0 && (pEList = pNC->pEList)!=0 && zTab==0 ){
+ if( (pEList = pNC->pEList)!=0
+ && zTab==0
+ && cnt==0
+ ){
for(j=0; j<pEList->nExpr; j++){
char *zAs = pEList->a[j].zName;
if( zAs!=0 && sqlite3StrICmp(zAs, zCol)==0 ){
@@ -72106,11 +88581,11 @@ static int lookupName(
assert( pExpr->x.pList==0 );
assert( pExpr->x.pSelect==0 );
pOrig = pEList->a[j].pExpr;
- if( !pNC->allowAgg && ExprHasProperty(pOrig, EP_Agg) ){
+ if( (pNC->ncFlags&NC_AllowAgg)==0 && ExprHasProperty(pOrig, EP_Agg) ){
sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs);
return WRC_Abort;
}
- resolveAlias(pParse, pEList, j, pExpr, "");
+ resolveAlias(pParse, pEList, j, pExpr, "", nSubquery);
cnt = 1;
pMatch = 0;
assert( zTab==0 && zDb==0 );
@@ -72124,6 +88599,7 @@ static int lookupName(
*/
if( cnt==0 ){
pNC = pNC->pNext;
+ nSubquery++;
}
}
@@ -72187,7 +88663,9 @@ static int lookupName(
lookupname_end:
if( cnt==1 ){
assert( pNC!=0 );
- sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
+ if( !ExprHasProperty(pExpr, EP_Alias) ){
+ sqlite3AuthRead(pParse, pExpr, pSchema, pNC->pSrcList);
+ }
/* Increment the nRef value on all name contexts from TopNC up to
** the point where the name matched. */
for(;;){
@@ -72226,6 +88704,41 @@ SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSr
}
/*
+** Report an error that an expression is not valid for some set of
+** pNC->ncFlags values determined by validMask.
+*/
+static void notValid(
+ Parse *pParse, /* Leave error message here */
+ NameContext *pNC, /* The name context */
+ const char *zMsg, /* Type of error */
+ int validMask /* Set of contexts for which prohibited */
+){
+ assert( (validMask&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr))==0 );
+ if( (pNC->ncFlags & validMask)!=0 ){
+ const char *zIn = "partial index WHERE clauses";
+ if( pNC->ncFlags & NC_IdxExpr ) zIn = "index expressions";
+#ifndef SQLITE_OMIT_CHECK
+ else if( pNC->ncFlags & NC_IsCheck ) zIn = "CHECK constraints";
+#endif
+ sqlite3ErrorMsg(pParse, "%s prohibited in %s", zMsg, zIn);
+ }
+}
+
+/*
+** Expression p should encode a floating point value between 1.0 and 0.0.
+** Return 1024 times this value. Or return -1 if p is not a floating point
+** value between 1.0 and 0.0.
+*/
+static int exprProbability(Expr *p){
+ double r = -1.0;
+ if( p->op!=TK_FLOAT ) return -1;
+ sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8);
+ assert( r>=0.0 );
+ if( r>1.0 ) return -1;
+ return (int)(r*134217728.0);
+}
+
+/*
** This routine is callback for sqlite3WalkExpr().
**
** Resolve symbolic names into TK_COLUMN operators for the current
@@ -72245,7 +88758,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pParse = pNC->pParse;
assert( pParse==pWalker->pParse );
- if( ExprHasAnyProperty(pExpr, EP_Resolved) ) return WRC_Prune;
+ if( ExprHasProperty(pExpr, EP_Resolved) ) return WRC_Prune;
ExprSetProperty(pExpr, EP_Resolved);
#ifndef NDEBUG
if( pNC->pSrcList && pNC->pSrcList->nAlloc>0 ){
@@ -72275,7 +88788,8 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
pExpr->affinity = SQLITE_AFF_INTEGER;
break;
}
-#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
+#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT)
+ && !defined(SQLITE_OMIT_SUBQUERY) */
/* A lone identifier is the name of a column.
*/
@@ -72293,6 +88807,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
Expr *pRight;
/* if( pSrcList==0 ) break; */
+ notValid(pParse, pNC, "the \".\" operator", NC_IdxExpr);
pRight = pExpr->pRight;
if( pRight->op==TK_ID ){
zDb = 0;
@@ -72309,53 +88824,90 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
/* Resolve function names
*/
- case TK_CONST_FUNC:
case TK_FUNCTION: {
ExprList *pList = pExpr->x.pList; /* The argument list */
int n = pList ? pList->nExpr : 0; /* Number of arguments */
int no_such_func = 0; /* True if no such function exists */
int wrong_num_args = 0; /* True if wrong number of arguments */
int is_agg = 0; /* True if is an aggregate function */
- int auth; /* Authorization to use the function */
int nId; /* Number of characters in function name */
const char *zId; /* The function name. */
FuncDef *pDef; /* Information about the function */
u8 enc = ENC(pParse->db); /* The database encoding */
- testcase( pExpr->op==TK_CONST_FUNC );
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
zId = pExpr->u.zToken;
nId = sqlite3Strlen30(zId);
- pDef = sqlite3FindFunction(pParse->db, zId, nId, n, enc, 0);
+ pDef = sqlite3FindFunction(pParse->db, zId, n, enc, 0);
if( pDef==0 ){
- pDef = sqlite3FindFunction(pParse->db, zId, nId, -1, enc, 0);
+ pDef = sqlite3FindFunction(pParse->db, zId, -2, enc, 0);
if( pDef==0 ){
no_such_func = 1;
}else{
wrong_num_args = 1;
}
}else{
- is_agg = pDef->xFunc==0;
- }
+ is_agg = pDef->xFinalize!=0;
+ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
+ ExprSetProperty(pExpr, EP_Unlikely|EP_Skip);
+ if( n==2 ){
+ pExpr->iTable = exprProbability(pList->a[1].pExpr);
+ if( pExpr->iTable<0 ){
+ sqlite3ErrorMsg(pParse,
+ "second argument to likelihood() must be a "
+ "constant between 0.0 and 1.0");
+ pNC->nErr++;
+ }
+ }else{
+ /* EVIDENCE-OF: R-61304-29449 The unlikely(X) function is
+ ** equivalent to likelihood(X, 0.0625).
+ ** EVIDENCE-OF: R-01283-11636 The unlikely(X) function is
+ ** short-hand for likelihood(X,0.0625).
+ ** EVIDENCE-OF: R-36850-34127 The likely(X) function is short-hand
+ ** for likelihood(X,0.9375).
+ ** EVIDENCE-OF: R-53436-40973 The likely(X) function is equivalent
+ ** to likelihood(X,0.9375). */
+ /* TUNING: unlikely() probability is 0.0625. likely() is 0.9375 */
+ pExpr->iTable = pDef->zName[0]=='u' ? 8388608 : 125829120;
+ }
+ }
#ifndef SQLITE_OMIT_AUTHORIZATION
- if( pDef ){
- auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0, pDef->zName, 0);
- if( auth!=SQLITE_OK ){
- if( auth==SQLITE_DENY ){
- sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
- pDef->zName);
- pNC->nErr++;
+ {
+ int auth = sqlite3AuthCheck(pParse, SQLITE_FUNCTION, 0,pDef->zName,0);
+ if( auth!=SQLITE_OK ){
+ if( auth==SQLITE_DENY ){
+ sqlite3ErrorMsg(pParse, "not authorized to use function: %s",
+ pDef->zName);
+ pNC->nErr++;
+ }
+ pExpr->op = TK_NULL;
+ return WRC_Prune;
}
- pExpr->op = TK_NULL;
- return WRC_Prune;
}
- }
#endif
- if( is_agg && !pNC->allowAgg ){
+ if( pDef->funcFlags & (SQLITE_FUNC_CONSTANT|SQLITE_FUNC_SLOCHNG) ){
+ /* For the purposes of the EP_ConstFunc flag, date and time
+ ** functions and other functions that change slowly are considered
+ ** constant because they are constant for the duration of one query */
+ ExprSetProperty(pExpr,EP_ConstFunc);
+ }
+ if( (pDef->funcFlags & SQLITE_FUNC_CONSTANT)==0 ){
+ /* Date/time functions that use 'now', and other functions like
+ ** sqlite_version() that might change over time cannot be used
+ ** in an index. */
+ notValid(pParse, pNC, "non-deterministic functions",
+ NC_IdxExpr|NC_PartIdx);
+ }
+ }
+ if( is_agg && (pNC->ncFlags & NC_AllowAgg)==0 ){
sqlite3ErrorMsg(pParse, "misuse of aggregate function %.*s()", nId,zId);
pNC->nErr++;
is_agg = 0;
- }else if( no_such_func ){
+ }else if( no_such_func && pParse->db->init.busy==0
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ && pParse->explain==0
+#endif
+ ){
sqlite3ErrorMsg(pParse, "no such function: %.*s", nId, zId);
pNC->nErr++;
}else if( wrong_num_args ){
@@ -72363,13 +88915,25 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
nId, zId);
pNC->nErr++;
}
+ if( is_agg ) pNC->ncFlags &= ~NC_AllowAgg;
+ sqlite3WalkExprList(pWalker, pList);
if( is_agg ){
+ NameContext *pNC2 = pNC;
pExpr->op = TK_AGG_FUNCTION;
- pNC->hasAgg = 1;
+ pExpr->op2 = 0;
+ while( pNC2 && !sqlite3FunctionUsesThisSrc(pExpr, pNC2->pSrcList) ){
+ pExpr->op2++;
+ pNC2 = pNC2->pNext;
+ }
+ assert( pDef!=0 );
+ if( pNC2 ){
+ assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
+ testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
+ pNC2->ncFlags |= NC_HasAgg | (pDef->funcFlags & SQLITE_FUNC_MINMAX);
+
+ }
+ pNC->ncFlags |= NC_AllowAgg;
}
- if( is_agg ) pNC->allowAgg = 0;
- sqlite3WalkExprList(pWalker, pList);
- if( is_agg ) pNC->allowAgg = 1;
/* FIX ME: Compute pExpr->affinity based on the expected return
** type of the function
*/
@@ -72383,27 +88947,47 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_IN );
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
int nRef = pNC->nRef;
-#ifndef SQLITE_OMIT_CHECK
- if( pNC->isCheck ){
- sqlite3ErrorMsg(pParse,"subqueries prohibited in CHECK constraints");
- }
-#endif
+ notValid(pParse, pNC, "subqueries", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
sqlite3WalkSelect(pWalker, pExpr->x.pSelect);
assert( pNC->nRef>=nRef );
if( nRef!=pNC->nRef ){
ExprSetProperty(pExpr, EP_VarSelect);
+ pNC->ncFlags |= NC_VarSelect;
}
}
break;
}
-#ifndef SQLITE_OMIT_CHECK
case TK_VARIABLE: {
- if( pNC->isCheck ){
- sqlite3ErrorMsg(pParse,"parameters prohibited in CHECK constraints");
- }
+ notValid(pParse, pNC, "parameters", NC_IsCheck|NC_PartIdx|NC_IdxExpr);
break;
}
-#endif
+ case TK_EQ:
+ case TK_NE:
+ case TK_LT:
+ case TK_LE:
+ case TK_GT:
+ case TK_GE:
+ case TK_IS:
+ case TK_ISNOT: {
+ int nLeft, nRight;
+ if( pParse->db->mallocFailed ) break;
+ assert( pExpr->pRight!=0 );
+ assert( pExpr->pLeft!=0 );
+ nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
+ nRight = sqlite3ExprVectorSize(pExpr->pRight);
+ if( nLeft!=nRight ){
+ testcase( pExpr->op==TK_EQ );
+ testcase( pExpr->op==TK_NE );
+ testcase( pExpr->op==TK_LT );
+ testcase( pExpr->op==TK_LE );
+ testcase( pExpr->op==TK_GT );
+ testcase( pExpr->op==TK_GE );
+ testcase( pExpr->op==TK_IS );
+ testcase( pExpr->op==TK_ISNOT );
+ sqlite3ErrorMsg(pParse, "row value misused");
+ }
+ break;
+ }
}
return (pParse->nErr || pParse->db->mallocFailed) ? WRC_Abort : WRC_Continue;
}
@@ -72480,7 +89064,7 @@ static int resolveOrderByTermToExprList(
nc.pParse = pParse;
nc.pSrcList = pSelect->pSrc;
nc.pEList = pEList;
- nc.allowAgg = 1;
+ nc.ncFlags = NC_AllowAgg;
nc.nErr = 0;
db = pParse->db;
savedSuppErr = db->suppressErr;
@@ -72494,7 +89078,7 @@ static int resolveOrderByTermToExprList(
** result-set entry.
*/
for(i=0; i<pEList->nExpr; i++){
- if( sqlite3ExprCompare(pEList->a[i].pExpr, pE)<2 ){
+ if( sqlite3ExprCompare(pEList->a[i].pExpr, pE, -1)<2 ){
return i+1;
}
}
@@ -72568,7 +89152,7 @@ static int resolveCompoundOrderBy(
int iCol = -1;
Expr *pE, *pDup;
if( pItem->done ) continue;
- pE = pItem->pExpr;
+ pE = sqlite3ExprSkipCollate(pItem->pExpr);
if( sqlite3ExprIsInteger(pE, &iCol) ){
if( iCol<=0 || iCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, "ORDER", i+1, pEList->nExpr);
@@ -72586,15 +89170,23 @@ static int resolveCompoundOrderBy(
}
}
if( iCol>0 ){
- CollSeq *pColl = pE->pColl;
- int flags = pE->flags & EP_ExpCollate;
+ /* Convert the ORDER BY term into an integer column number iCol,
+ ** taking care to preserve the COLLATE clause if it exists */
+ Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
+ if( pNew==0 ) return 1;
+ pNew->flags |= EP_IntValue;
+ pNew->u.iValue = iCol;
+ if( pItem->pExpr==pE ){
+ pItem->pExpr = pNew;
+ }else{
+ Expr *pParent = pItem->pExpr;
+ assert( pParent->op==TK_COLLATE );
+ while( pParent->pLeft->op==TK_COLLATE ) pParent = pParent->pLeft;
+ assert( pParent->pLeft==pE );
+ pParent->pLeft = pNew;
+ }
sqlite3ExprDelete(db, pE);
- pItem->pExpr = pE = sqlite3Expr(db, TK_INTEGER, 0);
- if( pE==0 ) return 1;
- pE->pColl = pColl;
- pE->flags |= EP_IntValue | flags;
- pE->u.iValue = iCol;
- pItem->iCol = (u16)iCol;
+ pItem->u.x.iOrderByCol = (u16)iCol;
pItem->done = 1;
}else{
moreToDo = 1;
@@ -72615,8 +89207,8 @@ static int resolveCompoundOrderBy(
/*
** Check every term in the ORDER BY or GROUP BY clause pOrderBy of
** the SELECT statement pSelect. If any term is reference to a
-** result set expression (as determined by the ExprList.a.iCol field)
-** then convert that term into a copy of the corresponding result set
+** result set expression (as determined by the ExprList.a.u.x.iOrderByCol
+** field) then convert that term into a copy of the corresponding result set
** column.
**
** If any errors are detected, add an error message to pParse and
@@ -72643,12 +89235,13 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
pEList = pSelect->pEList;
assert( pEList!=0 ); /* sqlite3SelectNew() guarantees this */
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
- if( pItem->iCol ){
- if( pItem->iCol>pEList->nExpr ){
+ if( pItem->u.x.iOrderByCol ){
+ if( pItem->u.x.iOrderByCol>pEList->nExpr ){
resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr);
return 1;
}
- resolveAlias(pParse, pEList, pItem->iCol-1, pItem->pExpr, zType);
+ resolveAlias(pParse, pEList, pItem->u.x.iOrderByCol-1, pItem->pExpr,
+ zType,0);
}
}
return 0;
@@ -72663,7 +89256,7 @@ SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(
** If the order-by term is an integer I between 1 and N (where N is the
** number of columns in the result set of the SELECT) then the expression
** in the resolution is a copy of the I-th result-set expression. If
-** the order-by term is an identify that corresponds to the AS-name of
+** the order-by term is an identifier that corresponds to the AS-name of
** a result-set expression, then the term resolves to a copy of the
** result-set expression. Otherwise, the expression is resolved in
** the usual way - using sqlite3ResolveExprNames().
@@ -72678,7 +89271,7 @@ static int resolveOrderGroupBy(
ExprList *pOrderBy, /* An ORDER BY or GROUP BY clause to resolve */
const char *zType /* Either "ORDER" or "GROUP", as appropriate */
){
- int i; /* Loop counter */
+ int i, j; /* Loop counters */
int iCol; /* Column number */
struct ExprList_item *pItem; /* A term of the ORDER BY clause */
Parse *pParse; /* Parsing context */
@@ -72689,38 +89282,46 @@ static int resolveOrderGroupBy(
pParse = pNC->pParse;
for(i=0, pItem=pOrderBy->a; i<pOrderBy->nExpr; i++, pItem++){
Expr *pE = pItem->pExpr;
- iCol = resolveAsName(pParse, pSelect->pEList, pE);
- if( iCol>0 ){
- /* If an AS-name match is found, mark this ORDER BY column as being
- ** a copy of the iCol-th result-set column. The subsequent call to
- ** sqlite3ResolveOrderGroupBy() will convert the expression to a
- ** copy of the iCol-th result-set expression. */
- pItem->iCol = (u16)iCol;
- continue;
+ Expr *pE2 = sqlite3ExprSkipCollate(pE);
+ if( zType[0]!='G' ){
+ iCol = resolveAsName(pParse, pSelect->pEList, pE2);
+ if( iCol>0 ){
+ /* If an AS-name match is found, mark this ORDER BY column as being
+ ** a copy of the iCol-th result-set column. The subsequent call to
+ ** sqlite3ResolveOrderGroupBy() will convert the expression to a
+ ** copy of the iCol-th result-set expression. */
+ pItem->u.x.iOrderByCol = (u16)iCol;
+ continue;
+ }
}
- if( sqlite3ExprIsInteger(pE, &iCol) ){
+ if( sqlite3ExprIsInteger(pE2, &iCol) ){
/* The ORDER BY term is an integer constant. Again, set the column
** number so that sqlite3ResolveOrderGroupBy() will convert the
** order-by term to a copy of the result-set expression */
- if( iCol<1 ){
+ if( iCol<1 || iCol>0xffff ){
resolveOutOfRangeError(pParse, zType, i+1, nResult);
return 1;
}
- pItem->iCol = (u16)iCol;
+ pItem->u.x.iOrderByCol = (u16)iCol;
continue;
}
/* Otherwise, treat the ORDER BY term as an ordinary expression */
- pItem->iCol = 0;
+ pItem->u.x.iOrderByCol = 0;
if( sqlite3ResolveExprNames(pNC, pE) ){
return 1;
}
+ for(j=0; j<pSelect->pEList->nExpr; j++){
+ if( sqlite3ExprCompare(pE, pSelect->pEList->a[j].pExpr, -1)==0 ){
+ pItem->u.x.iOrderByCol = j+1;
+ }
+ }
}
return sqlite3ResolveOrderGroupBy(pParse, pSelect, pOrderBy, zType);
}
/*
-** Resolve names in the SELECT statement p and all of its descendents.
+** Resolve names in the SELECT statement p and all of its descendants.
*/
static int resolveSelectStep(Walker *pWalker, Select *p){
NameContext *pOuterNC; /* Context that contains this SELECT */
@@ -72728,7 +89329,6 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
int isCompound; /* True if p is a compound select */
int nCompound; /* Number of compound terms processed so far */
Parse *pParse; /* Parsing context */
- ExprList *pEList; /* Result set expression list */
int i; /* Loop counter */
ExprList *pGroupBy; /* The GROUP BY clause */
Select *pLeftmost; /* Left-most of SELECT of a compound */
@@ -72773,22 +89373,19 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
sqlite3ResolveExprNames(&sNC, p->pOffset) ){
return WRC_Abort;
}
-
- /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
- ** resolve the result-set expression list.
- */
- sNC.allowAgg = 1;
- sNC.pSrcList = p->pSrc;
- sNC.pNext = pOuterNC;
-
- /* Resolve names in the result set. */
- pEList = p->pEList;
- assert( pEList!=0 );
- for(i=0; i<pEList->nExpr; i++){
- Expr *pX = pEList->a[i].pExpr;
- if( sqlite3ResolveExprNames(&sNC, pX) ){
- return WRC_Abort;
- }
+
+ /* If the SF_Converted flags is set, then this Select object was
+ ** was created by the convertCompoundSelectToSubquery() function.
+ ** In this case the ORDER BY clause (p->pOrderBy) should be resolved
+ ** as if it were part of the sub-query, not the parent. This block
+ ** moves the pOrderBy down to the sub-query. It will be moved back
+ ** after the names have been resolved. */
+ if( p->selFlags & SF_Converted ){
+ Select *pSub = p->pSrc->a[0].pSelect;
+ assert( p->pSrc->nSrc==1 && p->pOrderBy );
+ assert( pSub->pPrior && pSub->pOrderBy==0 );
+ pSub->pOrderBy = p->pOrderBy;
+ p->pOrderBy = 0;
}
/* Recursively resolve names in all subqueries
@@ -72804,7 +89401,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** parent contexts. After resolving references to expressions in
** pItem->pSelect, check if this value has changed. If so, then
** SELECT statement pItem->pSelect must be correlated. Set the
- ** pItem->isCorrelated flag if this is the case. */
+ ** pItem->fg.isCorrelated flag if this is the case. */
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef += pNC->nRef;
if( pItem->zName ) pParse->zAuthContext = pItem->zName;
@@ -72813,20 +89410,31 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
if( pParse->nErr || db->mallocFailed ) return WRC_Abort;
for(pNC=pOuterNC; pNC; pNC=pNC->pNext) nRef -= pNC->nRef;
- assert( pItem->isCorrelated==0 && nRef<=0 );
- pItem->isCorrelated = (nRef!=0);
+ assert( pItem->fg.isCorrelated==0 && nRef<=0 );
+ pItem->fg.isCorrelated = (nRef!=0);
}
}
+ /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
+ ** resolve the result-set expression list.
+ */
+ sNC.ncFlags = NC_AllowAgg;
+ sNC.pSrcList = p->pSrc;
+ sNC.pNext = pOuterNC;
+
+ /* Resolve names in the result set. */
+ if( sqlite3ResolveExprListNames(&sNC, p->pEList) ) return WRC_Abort;
+
/* If there are no aggregate functions in the result-set, and no GROUP BY
** expression, do not allow aggregates in any of the other expressions.
*/
assert( (p->selFlags & SF_Aggregate)==0 );
pGroupBy = p->pGroupBy;
- if( pGroupBy || sNC.hasAgg ){
- p->selFlags |= SF_Aggregate;
+ if( pGroupBy || (sNC.ncFlags & NC_HasAgg)!=0 ){
+ assert( NC_MinMaxAgg==SF_MinMaxAgg );
+ p->selFlags |= SF_Aggregate | (sNC.ncFlags&NC_MinMaxAgg);
}else{
- sNC.allowAgg = 0;
+ sNC.ncFlags &= ~NC_AllowAgg;
}
/* If a HAVING clause is present, then there must be a GROUP BY clause.
@@ -72836,7 +89444,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
return WRC_Abort;
}
- /* Add the expression list to the name-context before parsing the
+ /* Add the output column list to the name-context before parsing the
** other expressions in the SELECT statement. This is so that
** expressions in the WHERE clause (etc.) can refer to expressions by
** aliases in the result set.
@@ -72845,24 +89453,49 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
** re-evaluated for each reference to it.
*/
sNC.pEList = p->pEList;
- if( sqlite3ResolveExprNames(&sNC, p->pWhere) ||
- sqlite3ResolveExprNames(&sNC, p->pHaving)
- ){
- return WRC_Abort;
+ if( sqlite3ResolveExprNames(&sNC, p->pHaving) ) return WRC_Abort;
+ if( sqlite3ResolveExprNames(&sNC, p->pWhere) ) return WRC_Abort;
+
+ /* Resolve names in table-valued-function arguments */
+ for(i=0; i<p->pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &p->pSrc->a[i];
+ if( pItem->fg.isTabFunc
+ && sqlite3ResolveExprListNames(&sNC, pItem->u1.pFuncArg)
+ ){
+ return WRC_Abort;
+ }
}
/* The ORDER BY and GROUP BY clauses may not refer to terms in
** outer queries
*/
sNC.pNext = 0;
- sNC.allowAgg = 1;
+ sNC.ncFlags |= NC_AllowAgg;
+
+ /* If this is a converted compound query, move the ORDER BY clause from
+ ** the sub-query back to the parent query. At this point each term
+ ** within the ORDER BY clause has been transformed to an integer value.
+ ** These integers will be replaced by copies of the corresponding result
+ ** set expressions by the call to resolveOrderGroupBy() below. */
+ if( p->selFlags & SF_Converted ){
+ Select *pSub = p->pSrc->a[0].pSelect;
+ p->pOrderBy = pSub->pOrderBy;
+ pSub->pOrderBy = 0;
+ }
/* Process the ORDER BY clause for singleton SELECT statements.
** The ORDER BY clause for compounds SELECT statements is handled
** below, after all of the result-sets for all of the elements of
** the compound have been resolved.
+ **
+ ** If there is an ORDER BY clause on a term of a compound-select other
+ ** than the right-most term, then that is a syntax error. But the error
+ ** is not detected until much later, and so we need to go ahead and
+ ** resolve those symbols on the incorrect ORDER BY for consistency.
*/
- if( !isCompound && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER") ){
+ if( isCompound<=nCompound /* Defer right-most ORDER BY of a compound */
+ && resolveOrderGroupBy(&sNC, p, p->pOrderBy, "ORDER")
+ ){
return WRC_Abort;
}
if( db->mallocFailed ){
@@ -72887,6 +89520,13 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
}
}
+ /* If this is part of a compound SELECT, check that it has the right
+ ** number of expressions in the select list. */
+ if( p->pNext && p->pEList->nExpr!=p->pNext->pEList->nExpr ){
+ sqlite3SelectWrongNumTermsError(pParse, p->pNext);
+ return WRC_Abort;
+ }
+
/* Advance to the next term of the compound
*/
p = p->pPrior;
@@ -72943,7 +89583,7 @@ static int resolveSelectStep(Walker *pWalker, Select *p){
**
** Function calls are checked to make sure that the function is
** defined and that the correct number of arguments are specified.
-** If the function is an aggregate function, then the pNC->hasAgg is
+** If the function is an aggregate function, then the NC_HasAgg flag is
** set and the opcode is changed from TK_FUNCTION to TK_AGG_FUNCTION.
** If an expression contains aggregate functions then the EP_Agg
** property on the expression is set.
@@ -72955,7 +89595,7 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
NameContext *pNC, /* Namespace to resolve expressions in. */
Expr *pExpr /* The expression to be analyzed. */
){
- int savedHasAgg;
+ u16 savedHasAgg;
Walker w;
if( pExpr==0 ) return 0;
@@ -72968,11 +89608,14 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
pParse->nHeight += pExpr->nHeight;
}
#endif
- savedHasAgg = pNC->hasAgg;
- pNC->hasAgg = 0;
+ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg);
+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg);
+ w.pParse = pNC->pParse;
w.xExprCallback = resolveExprStep;
w.xSelectCallback = resolveSelectStep;
- w.pParse = pNC->pParse;
+ w.xSelectCallback2 = 0;
+ w.walkerDepth = 0;
+ w.eCode = 0;
w.u.pNC = pNC;
sqlite3WalkExpr(&w, pExpr);
#if SQLITE_MAX_EXPR_DEPTH>0
@@ -72981,14 +89624,30 @@ SQLITE_PRIVATE int sqlite3ResolveExprNames(
if( pNC->nErr>0 || w.pParse->nErr>0 ){
ExprSetProperty(pExpr, EP_Error);
}
- if( pNC->hasAgg ){
+ if( pNC->ncFlags & NC_HasAgg ){
ExprSetProperty(pExpr, EP_Agg);
- }else if( savedHasAgg ){
- pNC->hasAgg = 1;
}
+ pNC->ncFlags |= savedHasAgg;
return ExprHasProperty(pExpr, EP_Error);
}
+/*
+** Resolve all names for all expression in an expression list. This is
+** just like sqlite3ResolveExprNames() except that it works for an expression
+** list rather than a single expression.
+*/
+SQLITE_PRIVATE int sqlite3ResolveExprListNames(
+ NameContext *pNC, /* Namespace to resolve expressions in. */
+ ExprList *pList /* The expression list to be analyzed. */
+){
+ int i;
+ if( pList ){
+ for(i=0; i<pList->nExpr; i++){
+ if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort;
+ }
+ }
+ return WRC_Continue;
+}
/*
** Resolve all names in all expressions of a SELECT and in all
@@ -73010,6 +89669,7 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
Walker w;
assert( p!=0 );
+ memset(&w, 0, sizeof(w));
w.xExprCallback = resolveExprStep;
w.xSelectCallback = resolveSelectStep;
w.pParse = pParse;
@@ -73017,6 +89677,41 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
sqlite3WalkSelect(&w, p);
}
+/*
+** Resolve names in expressions that can only reference a single table:
+**
+** * CHECK constraints
+** * WHERE clauses on partial indices
+**
+** The Expr.iTable value for Expr.op==TK_COLUMN nodes of the expression
+** is set to -1 and the Expr.iColumn value is set to the column number.
+**
+** Any errors cause an error message to be set in pParse.
+*/
+SQLITE_PRIVATE void sqlite3ResolveSelfReference(
+ Parse *pParse, /* Parsing context */
+ Table *pTab, /* The table being referenced */
+ int type, /* NC_IsCheck or NC_PartIdx or NC_IdxExpr */
+ Expr *pExpr, /* Expression to resolve. May be NULL. */
+ ExprList *pList /* Expression list to resolve. May be NUL. */
+){
+ SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
+ NameContext sNC; /* Name context for pParse->pNewTable */
+
+ assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr );
+ memset(&sNC, 0, sizeof(sNC));
+ memset(&sSrc, 0, sizeof(sSrc));
+ sSrc.nSrc = 1;
+ sSrc.a[0].zName = pTab->zName;
+ sSrc.a[0].pTab = pTab;
+ sSrc.a[0].iCursor = -1;
+ sNC.pParse = pParse;
+ sNC.pSrcList = &sSrc;
+ sNC.ncFlags = type;
+ if( sqlite3ResolveExprNames(&sNC, pExpr) ) return;
+ if( pList ) sqlite3ResolveExprListNames(&sNC, pList);
+}
+
/************** End of resolve.c *********************************************/
/************** Begin file expr.c ********************************************/
/*
@@ -73033,6 +89728,19 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
** This file contains routines used for analyzing expressions and
** for generating VDBE code that evaluates expressions in SQLite.
*/
+/* #include "sqliteInt.h" */
+
+/* Forward declarations */
+static void exprCodeBetween(Parse*,Expr*,int,void(*)(Parse*,Expr*,int,int),int);
+static int exprCodeVector(Parse *pParse, Expr *p, int *piToFree);
+
+/*
+** Return the affinity character for a single column of a table.
+*/
+SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table *pTab, int iCol){
+ assert( iCol<pTab->nCol );
+ return iCol>=0 ? pTab->aCol[iCol].affinity : SQLITE_AFF_INTEGER;
+}
/*
** Return the 'affinity' of the expression pExpr if any.
@@ -73042,7 +89750,7 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
** affinity of that column is returned. Otherwise, 0x00 is returned,
** indicating no affinity for the expression.
**
-** i.e. the WHERE clause expresssions in the following statements all
+** i.e. the WHERE clause expressions in the following statements all
** have an affinity:
**
** CREATE TABLE t1(a);
@@ -73051,91 +89759,144 @@ SQLITE_PRIVATE void sqlite3ResolveSelectNames(
** SELECT * FROM t1 WHERE (select a from t1);
*/
SQLITE_PRIVATE char sqlite3ExprAffinity(Expr *pExpr){
- int op = pExpr->op;
+ int op;
+ pExpr = sqlite3ExprSkipCollate(pExpr);
+ if( pExpr->flags & EP_Generic ) return 0;
+ op = pExpr->op;
if( op==TK_SELECT ){
assert( pExpr->flags&EP_xIsSelect );
return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
}
+ if( op==TK_REGISTER ) op = pExpr->op2;
#ifndef SQLITE_OMIT_CAST
if( op==TK_CAST ){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- return sqlite3AffinityType(pExpr->u.zToken);
+ return sqlite3AffinityType(pExpr->u.zToken, 0);
}
#endif
- if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER)
- && pExpr->pTab!=0
- ){
- /* op==TK_REGISTER && pExpr->pTab!=0 happens when pExpr was originally
- ** a TK_COLUMN but was previously evaluated and cached in a register */
- int j = pExpr->iColumn;
- if( j<0 ) return SQLITE_AFF_INTEGER;
- assert( pExpr->pTab && j<pExpr->pTab->nCol );
- return pExpr->pTab->aCol[j].affinity;
+ if( op==TK_AGG_COLUMN || op==TK_COLUMN ){
+ return sqlite3TableColumnAffinity(pExpr->pTab, pExpr->iColumn);
+ }
+ if( op==TK_SELECT_COLUMN ){
+ assert( pExpr->pLeft->flags&EP_xIsSelect );
+ return sqlite3ExprAffinity(
+ pExpr->pLeft->x.pSelect->pEList->a[pExpr->iColumn].pExpr
+ );
}
return pExpr->affinity;
}
/*
-** Set the explicit collating sequence for an expression to the
-** collating sequence supplied in the second argument.
+** Set the collating sequence for expression pExpr to be the collating
+** sequence named by pToken. Return a pointer to a new Expr node that
+** implements the COLLATE operator.
+**
+** If a memory allocation error occurs, that fact is recorded in pParse->db
+** and the pExpr parameter is returned unchanged.
*/
-SQLITE_PRIVATE Expr *sqlite3ExprSetColl(Expr *pExpr, CollSeq *pColl){
- if( pExpr && pColl ){
- pExpr->pColl = pColl;
- pExpr->flags |= EP_ExpCollate;
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* Add the "COLLATE" clause to this expression */
+ const Token *pCollName, /* Name of collating sequence */
+ int dequote /* True to dequote pCollName */
+){
+ if( pCollName->n>0 ){
+ Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
+ if( pNew ){
+ pNew->pLeft = pExpr;
+ pNew->flags |= EP_Collate|EP_Skip;
+ pExpr = pNew;
+ }
}
return pExpr;
}
+SQLITE_PRIVATE Expr *sqlite3ExprAddCollateString(Parse *pParse, Expr *pExpr, const char *zC){
+ Token s;
+ assert( zC!=0 );
+ sqlite3TokenInit(&s, (char*)zC);
+ return sqlite3ExprAddCollateToken(pParse, pExpr, &s, 0);
+}
/*
-** Set the collating sequence for expression pExpr to be the collating
-** sequence named by pToken. Return a pointer to the revised expression.
-** The collating sequence is marked as "explicit" using the EP_ExpCollate
-** flag. An explicit collating sequence will override implicit
-** collating sequences.
+** Skip over any TK_COLLATE operators and any unlikely()
+** or likelihood() function at the root of an expression.
*/
-SQLITE_PRIVATE Expr *sqlite3ExprSetCollByToken(Parse *pParse, Expr *pExpr, Token *pCollName){
- char *zColl = 0; /* Dequoted name of collation sequence */
- CollSeq *pColl;
- sqlite3 *db = pParse->db;
- zColl = sqlite3NameFromToken(db, pCollName);
- pColl = sqlite3LocateCollSeq(pParse, zColl);
- sqlite3ExprSetColl(pExpr, pColl);
- sqlite3DbFree(db, zColl);
+SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
+ while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
+ if( ExprHasProperty(pExpr, EP_Unlikely) ){
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ assert( pExpr->x.pList->nExpr>0 );
+ assert( pExpr->op==TK_FUNCTION );
+ pExpr = pExpr->x.pList->a[0].pExpr;
+ }else{
+ assert( pExpr->op==TK_COLLATE );
+ pExpr = pExpr->pLeft;
+ }
+ }
return pExpr;
}
/*
-** Return the default collation sequence for the expression pExpr. If
-** there is no default collation type, return 0.
+** Return the collation sequence for the expression pExpr. If
+** there is no defined collating sequence, return NULL.
+**
+** The collating sequence might be determined by a COLLATE operator
+** or by the presence of a column with a defined collating sequence.
+** COLLATE operators take first precedence. Left operands take
+** precedence over right operands.
*/
SQLITE_PRIVATE CollSeq *sqlite3ExprCollSeq(Parse *pParse, Expr *pExpr){
+ sqlite3 *db = pParse->db;
CollSeq *pColl = 0;
Expr *p = pExpr;
while( p ){
- int op;
- pColl = p->pColl;
- if( pColl ) break;
- op = p->op;
- if( p->pTab!=0 && (
- op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER
- )){
+ int op = p->op;
+ if( p->flags & EP_Generic ) break;
+ if( op==TK_CAST || op==TK_UPLUS ){
+ p = p->pLeft;
+ continue;
+ }
+ if( op==TK_COLLATE || (op==TK_REGISTER && p->op2==TK_COLLATE) ){
+ pColl = sqlite3GetCollSeq(pParse, ENC(db), 0, p->u.zToken);
+ break;
+ }
+ if( (op==TK_AGG_COLUMN || op==TK_COLUMN
+ || op==TK_REGISTER || op==TK_TRIGGER)
+ && p->pTab!=0
+ ){
/* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally
** a TK_COLUMN but was previously evaluated and cached in a register */
- const char *zColl;
int j = p->iColumn;
if( j>=0 ){
- sqlite3 *db = pParse->db;
- zColl = p->pTab->aCol[j].zColl;
+ const char *zColl = p->pTab->aCol[j].zColl;
pColl = sqlite3FindCollSeq(db, ENC(db), zColl, 0);
- pExpr->pColl = pColl;
}
break;
}
- if( op!=TK_CAST && op!=TK_UPLUS ){
+ if( p->flags & EP_Collate ){
+ if( p->pLeft && (p->pLeft->flags & EP_Collate)!=0 ){
+ p = p->pLeft;
+ }else{
+ Expr *pNext = p->pRight;
+ /* The Expr.x union is never used at the same time as Expr.pRight */
+ assert( p->x.pList==0 || p->pRight==0 );
+ /* p->flags holds EP_Collate and p->pLeft->flags does not. And
+ ** p->x.pSelect cannot. So if p->x.pLeft exists, it must hold at
+ ** least one EP_Collate. Thus the following two ALWAYS. */
+ if( p->x.pList!=0 && ALWAYS(!ExprHasProperty(p, EP_xIsSelect)) ){
+ int i;
+ for(i=0; ALWAYS(i<p->x.pList->nExpr); i++){
+ if( ExprHasProperty(p->x.pList->a[i].pExpr, EP_Collate) ){
+ pNext = p->x.pList->a[i].pExpr;
+ break;
+ }
+ }
+ }
+ p = pNext;
+ }
+ }else{
break;
}
- p = p->pLeft;
}
if( sqlite3CheckCollSeq(pParse, pColl) ){
pColl = 0;
@@ -73157,13 +89918,13 @@ SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2){
if( sqlite3IsNumericAffinity(aff1) || sqlite3IsNumericAffinity(aff2) ){
return SQLITE_AFF_NUMERIC;
}else{
- return SQLITE_AFF_NONE;
+ return SQLITE_AFF_BLOB;
}
}else if( !aff1 && !aff2 ){
/* Neither side of the comparison is a column. Compare the
** results directly.
*/
- return SQLITE_AFF_NONE;
+ return SQLITE_AFF_BLOB;
}else{
/* One side is a column, the other is not. Use the columns affinity. */
assert( aff1==0 || aff2==0 );
@@ -73186,8 +89947,8 @@ static char comparisonAffinity(Expr *pExpr){
aff = sqlite3CompareAffinity(pExpr->pRight, aff);
}else if( ExprHasProperty(pExpr, EP_xIsSelect) ){
aff = sqlite3CompareAffinity(pExpr->x.pSelect->pEList->a[0].pExpr, aff);
- }else if( !aff ){
- aff = SQLITE_AFF_NONE;
+ }else if( NEVER(aff==0) ){
+ aff = SQLITE_AFF_BLOB;
}
return aff;
}
@@ -73201,7 +89962,7 @@ static char comparisonAffinity(Expr *pExpr){
SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity){
char aff = comparisonAffinity(pExpr);
switch( aff ){
- case SQLITE_AFF_NONE:
+ case SQLITE_AFF_BLOB:
return 1;
case SQLITE_AFF_TEXT:
return idx_affinity==SQLITE_AFF_TEXT;
@@ -73239,12 +90000,10 @@ SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(
){
CollSeq *pColl;
assert( pLeft );
- if( pLeft->flags & EP_ExpCollate ){
- assert( pLeft->pColl );
- pColl = pLeft->pColl;
- }else if( pRight && pRight->flags & EP_ExpCollate ){
- assert( pRight->pColl );
- pColl = pRight->pColl;
+ if( pLeft->flags & EP_Collate ){
+ pColl = sqlite3ExprCollSeq(pParse, pLeft);
+ }else if( pRight && (pRight->flags & EP_Collate)!=0 ){
+ pColl = sqlite3ExprCollSeq(pParse, pRight);
}else{
pColl = sqlite3ExprCollSeq(pParse, pLeft);
if( !pColl ){
@@ -73278,6 +90037,270 @@ static int codeCompare(
return addr;
}
+/*
+** Return true if expression pExpr is a vector, or false otherwise.
+**
+** A vector is defined as any expression that results in two or more
+** columns of result. Every TK_VECTOR node is an vector because the
+** parser will not generate a TK_VECTOR with fewer than two entries.
+** But a TK_SELECT might be either a vector or a scalar. It is only
+** considered a vector if it has two or more result columns.
+*/
+SQLITE_PRIVATE int sqlite3ExprIsVector(Expr *pExpr){
+ return sqlite3ExprVectorSize(pExpr)>1;
+}
+
+/*
+** If the expression passed as the only argument is of type TK_VECTOR
+** return the number of expressions in the vector. Or, if the expression
+** is a sub-select, return the number of columns in the sub-select. For
+** any other type of expression, return 1.
+*/
+SQLITE_PRIVATE int sqlite3ExprVectorSize(Expr *pExpr){
+ u8 op = pExpr->op;
+ if( op==TK_REGISTER ) op = pExpr->op2;
+ if( op==TK_VECTOR ){
+ return pExpr->x.pList->nExpr;
+ }else if( op==TK_SELECT ){
+ return pExpr->x.pSelect->pEList->nExpr;
+ }else{
+ return 1;
+ }
+}
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Return a pointer to a subexpression of pVector that is the i-th
+** column of the vector (numbered starting with 0). The caller must
+** ensure that i is within range.
+**
+** If pVector is really a scalar (and "scalar" here includes subqueries
+** that return a single column!) then return pVector unmodified.
+**
+** pVector retains ownership of the returned subexpression.
+**
+** If the vector is a (SELECT ...) then the expression returned is
+** just the expression for the i-th term of the result set, and may
+** not be ready for evaluation because the table cursor has not yet
+** been positioned.
+*/
+SQLITE_PRIVATE Expr *sqlite3VectorFieldSubexpr(Expr *pVector, int i){
+ assert( i<sqlite3ExprVectorSize(pVector) );
+ if( sqlite3ExprIsVector(pVector) ){
+ assert( pVector->op2==0 || pVector->op==TK_REGISTER );
+ if( pVector->op==TK_SELECT || pVector->op2==TK_SELECT ){
+ return pVector->x.pSelect->pEList->a[i].pExpr;
+ }else{
+ return pVector->x.pList->a[i].pExpr;
+ }
+ }
+ return pVector;
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) */
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Compute and return a new Expr object which when passed to
+** sqlite3ExprCode() will generate all necessary code to compute
+** the iField-th column of the vector expression pVector.
+**
+** It is ok for pVector to be a scalar (as long as iField==0).
+** In that case, this routine works like sqlite3ExprDup().
+**
+** The caller owns the returned Expr object and is responsible for
+** ensuring that the returned value eventually gets freed.
+**
+** The caller retains ownership of pVector. If pVector is a TK_SELECT,
+** then the returned object will reference pVector and so pVector must remain
+** valid for the life of the returned object. If pVector is a TK_VECTOR
+** or a scalar expression, then it can be deleted as soon as this routine
+** returns.
+**
+** A trick to cause a TK_SELECT pVector to be deleted together with
+** the returned Expr object is to attach the pVector to the pRight field
+** of the returned TK_SELECT_COLUMN Expr object.
+*/
+SQLITE_PRIVATE Expr *sqlite3ExprForVectorField(
+ Parse *pParse, /* Parsing context */
+ Expr *pVector, /* The vector. List of expressions or a sub-SELECT */
+ int iField /* Which column of the vector to return */
+){
+ Expr *pRet;
+ if( pVector->op==TK_SELECT ){
+ assert( pVector->flags & EP_xIsSelect );
+ /* The TK_SELECT_COLUMN Expr node:
+ **
+ ** pLeft: pVector containing TK_SELECT
+ ** pRight: not used. But recursively deleted.
+ ** iColumn: Index of a column in pVector
+ ** pLeft->iTable: First in an array of register holding result, or 0
+ ** if the result is not yet computed.
+ **
+ ** sqlite3ExprDelete() specifically skips the recursive delete of
+ ** pLeft on TK_SELECT_COLUMN nodes. But pRight is followed, so pVector
+ ** can be attached to pRight to cause this node to take ownership of
+ ** pVector. Typically there will be multiple TK_SELECT_COLUMN nodes
+ ** with the same pLeft pointer to the pVector, but only one of them
+ ** will own the pVector.
+ */
+ pRet = sqlite3PExpr(pParse, TK_SELECT_COLUMN, 0, 0, 0);
+ if( pRet ){
+ pRet->iColumn = iField;
+ pRet->pLeft = pVector;
+ }
+ assert( pRet==0 || pRet->iTable==0 );
+ }else{
+ if( pVector->op==TK_VECTOR ) pVector = pVector->x.pList->a[iField].pExpr;
+ pRet = sqlite3ExprDup(pParse->db, pVector, 0);
+ }
+ return pRet;
+}
+#endif /* !define(SQLITE_OMIT_SUBQUERY) */
+
+/*
+** If expression pExpr is of type TK_SELECT, generate code to evaluate
+** it. Return the register in which the result is stored (or, if the
+** sub-select returns more than one column, the first in an array
+** of registers in which the result is stored).
+**
+** If pExpr is not a TK_SELECT expression, return 0.
+*/
+static int exprCodeSubselect(Parse *pParse, Expr *pExpr){
+ int reg = 0;
+#ifndef SQLITE_OMIT_SUBQUERY
+ if( pExpr->op==TK_SELECT ){
+ reg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ }
+#endif
+ return reg;
+}
+
+/*
+** Argument pVector points to a vector expression - either a TK_VECTOR
+** or TK_SELECT that returns more than one column. This function returns
+** the register number of a register that contains the value of
+** element iField of the vector.
+**
+** If pVector is a TK_SELECT expression, then code for it must have
+** already been generated using the exprCodeSubselect() routine. In this
+** case parameter regSelect should be the first in an array of registers
+** containing the results of the sub-select.
+**
+** If pVector is of type TK_VECTOR, then code for the requested field
+** is generated. In this case (*pRegFree) may be set to the number of
+** a temporary register to be freed by the caller before returning.
+**
+** Before returning, output parameter (*ppExpr) is set to point to the
+** Expr object corresponding to element iElem of the vector.
+*/
+static int exprVectorRegister(
+ Parse *pParse, /* Parse context */
+ Expr *pVector, /* Vector to extract element from */
+ int iField, /* Field to extract from pVector */
+ int regSelect, /* First in array of registers */
+ Expr **ppExpr, /* OUT: Expression element */
+ int *pRegFree /* OUT: Temp register to free */
+){
+ u8 op = pVector->op;
+ assert( op==TK_VECTOR || op==TK_REGISTER || op==TK_SELECT );
+ if( op==TK_REGISTER ){
+ *ppExpr = sqlite3VectorFieldSubexpr(pVector, iField);
+ return pVector->iTable+iField;
+ }
+ if( op==TK_SELECT ){
+ *ppExpr = pVector->x.pSelect->pEList->a[iField].pExpr;
+ return regSelect+iField;
+ }
+ *ppExpr = pVector->x.pList->a[iField].pExpr;
+ return sqlite3ExprCodeTemp(pParse, *ppExpr, pRegFree);
+}
+
+/*
+** Expression pExpr is a comparison between two vector values. Compute
+** the result of the comparison (1, 0, or NULL) and write that
+** result into register dest.
+**
+** The caller must satisfy the following preconditions:
+**
+** if pExpr->op==TK_IS: op==TK_EQ and p5==SQLITE_NULLEQ
+** if pExpr->op==TK_ISNOT: op==TK_NE and p5==SQLITE_NULLEQ
+** otherwise: op==pExpr->op and p5==0
+*/
+static void codeVectorCompare(
+ Parse *pParse, /* Code generator context */
+ Expr *pExpr, /* The comparison operation */
+ int dest, /* Write results into this register */
+ u8 op, /* Comparison operator */
+ u8 p5 /* SQLITE_NULLEQ or zero */
+){
+ Vdbe *v = pParse->pVdbe;
+ Expr *pLeft = pExpr->pLeft;
+ Expr *pRight = pExpr->pRight;
+ int nLeft = sqlite3ExprVectorSize(pLeft);
+ int i;
+ int regLeft = 0;
+ int regRight = 0;
+ u8 opx = op;
+ int addrDone = sqlite3VdbeMakeLabel(v);
+
+ assert( nLeft==sqlite3ExprVectorSize(pRight) );
+ assert( pExpr->op==TK_EQ || pExpr->op==TK_NE
+ || pExpr->op==TK_IS || pExpr->op==TK_ISNOT
+ || pExpr->op==TK_LT || pExpr->op==TK_GT
+ || pExpr->op==TK_LE || pExpr->op==TK_GE
+ );
+ assert( pExpr->op==op || (pExpr->op==TK_IS && op==TK_EQ)
+ || (pExpr->op==TK_ISNOT && op==TK_NE) );
+ assert( p5==0 || pExpr->op!=op );
+ assert( p5==SQLITE_NULLEQ || pExpr->op==op );
+
+ p5 |= SQLITE_STOREP2;
+ if( opx==TK_LE ) opx = TK_LT;
+ if( opx==TK_GE ) opx = TK_GT;
+
+ regLeft = exprCodeSubselect(pParse, pLeft);
+ regRight = exprCodeSubselect(pParse, pRight);
+
+ for(i=0; 1 /*Loop exits by "break"*/; i++){
+ int regFree1 = 0, regFree2 = 0;
+ Expr *pL, *pR;
+ int r1, r2;
+ assert( i>=0 && i<nLeft );
+ if( i>0 ) sqlite3ExprCachePush(pParse);
+ r1 = exprVectorRegister(pParse, pLeft, i, regLeft, &pL, &regFree1);
+ r2 = exprVectorRegister(pParse, pRight, i, regRight, &pR, &regFree2);
+ codeCompare(pParse, pL, pR, opx, r1, r2, dest, p5);
+ testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
+ testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
+ sqlite3ReleaseTempReg(pParse, regFree1);
+ sqlite3ReleaseTempReg(pParse, regFree2);
+ if( i>0 ) sqlite3ExprCachePop(pParse);
+ if( i==nLeft-1 ){
+ break;
+ }
+ if( opx==TK_EQ ){
+ sqlite3VdbeAddOp2(v, OP_IfNot, dest, addrDone); VdbeCoverage(v);
+ p5 |= SQLITE_KEEPNULL;
+ }else if( opx==TK_NE ){
+ sqlite3VdbeAddOp2(v, OP_If, dest, addrDone); VdbeCoverage(v);
+ p5 |= SQLITE_KEEPNULL;
+ }else{
+ assert( op==TK_LT || op==TK_GT || op==TK_LE || op==TK_GE );
+ sqlite3VdbeAddOp2(v, OP_ElseNotEq, 0, addrDone);
+ VdbeCoverageIf(v, op==TK_LT);
+ VdbeCoverageIf(v, op==TK_GT);
+ VdbeCoverageIf(v, op==TK_LE);
+ VdbeCoverageIf(v, op==TK_GE);
+ if( i==nLeft-2 ) opx = op;
+ }
+ }
+ sqlite3VdbeResolveLabel(v, addrDone);
+}
+
#if SQLITE_MAX_EXPR_DEPTH>0
/*
** Check that argument nHeight is less than or equal to the maximum
@@ -73339,6 +90362,9 @@ static void heightOfSelect(Select *p, int *pnHeight){
** Expr.pSelect member has a height of 1. Any other expression
** has a height equal to the maximum height of any other
** referenced Expr plus one.
+**
+** Also propagate EP_Propagate flags up from Expr.x.pList to Expr.flags,
+** if appropriate.
*/
static void exprSetHeight(Expr *p){
int nHeight = 0;
@@ -73346,8 +90372,9 @@ static void exprSetHeight(Expr *p){
heightOfExpr(p->pRight, &nHeight);
if( ExprHasProperty(p, EP_xIsSelect) ){
heightOfSelect(p->x.pSelect, &nHeight);
- }else{
+ }else if( p->x.pList ){
heightOfExprList(p->x.pList, &nHeight);
+ p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList);
}
p->nHeight = nHeight + 1;
}
@@ -73356,8 +90383,12 @@ static void exprSetHeight(Expr *p){
** Set the Expr.nHeight variable using the exprSetHeight() function. If
** the height is greater than the maximum allowed expression depth,
** leave an error in pParse.
+**
+** Also propagate all EP_Propagate flags from the Expr.x.pList into
+** Expr.flags.
*/
-SQLITE_PRIVATE void sqlite3ExprSetHeight(Parse *pParse, Expr *p){
+SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
+ if( pParse->nErr ) return;
exprSetHeight(p);
sqlite3ExprCheckHeight(pParse, p->nHeight);
}
@@ -73371,8 +90402,17 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){
heightOfSelect(p, &nHeight);
return nHeight;
}
-#else
- #define exprSetHeight(y)
+#else /* ABOVE: Height enforcement enabled. BELOW: Height enforcement off */
+/*
+** Propagate all EP_Propagate flags from the Expr.x.pList into
+** Expr.flags.
+*/
+SQLITE_PRIVATE void sqlite3ExprSetHeightAndFlags(Parse *pParse, Expr *p){
+ if( p && p->x.pList && !ExprHasProperty(p, EP_xIsSelect) ){
+ p->flags |= EP_Propagate & sqlite3ExprListFlags(p->x.pList);
+ }
+}
+#define exprSetHeight(y)
#endif /* SQLITE_MAX_EXPR_DEPTH>0 */
/*
@@ -73384,7 +90424,7 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){
** is responsible for making sure the node eventually gets freed.
**
** If dequote is true, then the token (if it exists) is dequoted.
-** If dequote is false, no dequoting is performance. The deQuote
+** If dequote is false, no dequoting is performed. The deQuote
** parameter is ignored if pToken is NULL or if the token does not
** appear to be quoted. If the quotes were of the form "..." (double-quotes)
** then the EP_DblQuoted flag is set on the expression node.
@@ -73396,7 +90436,7 @@ SQLITE_PRIVATE int sqlite3SelectExprHeight(Select *p){
** is allocated to hold the integer text and the dequote flag is ignored.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
- sqlite3 *db, /* Handle for sqlite3DbMallocZero() (may be null) */
+ sqlite3 *db, /* Handle for sqlite3DbMallocRawNN() */
int op, /* Expression opcode */
const Token *pToken, /* Token argument. Might be NULL */
int dequote /* True to dequote */
@@ -73405,6 +90445,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
int nExtra = 0;
int iValue = 0;
+ assert( db!=0 );
if( pToken ){
if( op!=TK_INTEGER || pToken->z==0
|| sqlite3GetInt32(pToken->z, &iValue)==0 ){
@@ -73412,8 +90453,9 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
assert( iValue>=0 );
}
}
- pNew = sqlite3DbMallocZero(db, sizeof(Expr)+nExtra);
+ pNew = sqlite3DbMallocRawNN(db, sizeof(Expr)+nExtra);
if( pNew ){
+ memset(pNew, 0, sizeof(Expr));
pNew->op = (u8)op;
pNew->iAgg = -1;
if( pToken ){
@@ -73421,14 +90463,13 @@ SQLITE_PRIVATE Expr *sqlite3ExprAlloc(
pNew->flags |= EP_IntValue;
pNew->u.iValue = iValue;
}else{
- int c;
pNew->u.zToken = (char*)&pNew[1];
- memcpy(pNew->u.zToken, pToken->z, pToken->n);
+ assert( pToken->z!=0 || pToken->n==0 );
+ if( pToken->n ) memcpy(pNew->u.zToken, pToken->z, pToken->n);
pNew->u.zToken[pToken->n] = 0;
- if( dequote && nExtra>=3
- && ((c = pToken->z[0])=='\'' || c=='"' || c=='[' || c=='`') ){
+ if( dequote && sqlite3Isquote(pNew->u.zToken[0]) ){
+ if( pNew->u.zToken[0]=='"' ) pNew->flags |= EP_DblQuoted;
sqlite3Dequote(pNew->u.zToken);
- if( c=='"' ) pNew->flags |= EP_DblQuoted;
}
}
}
@@ -73473,24 +90514,18 @@ SQLITE_PRIVATE void sqlite3ExprAttachSubtrees(
}else{
if( pRight ){
pRoot->pRight = pRight;
- if( pRight->flags & EP_ExpCollate ){
- pRoot->flags |= EP_ExpCollate;
- pRoot->pColl = pRight->pColl;
- }
+ pRoot->flags |= EP_Propagate & pRight->flags;
}
if( pLeft ){
pRoot->pLeft = pLeft;
- if( pLeft->flags & EP_ExpCollate ){
- pRoot->flags |= EP_ExpCollate;
- pRoot->pColl = pLeft->pColl;
- }
+ pRoot->flags |= EP_Propagate & pLeft->flags;
}
exprSetHeight(pRoot);
}
}
/*
-** Allocate a Expr node which joins as many as two subtrees.
+** Allocate an Expr node which joins as many as two subtrees.
**
** One or both of the subtrees can be NULL. Return a pointer to the new
** Expr node. Or, if an OOM error occurs, set pParse->db->mallocFailed,
@@ -73503,8 +90538,14 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(
Expr *pRight, /* Right operand */
const Token *pToken /* Argument token */
){
- Expr *p = sqlite3ExprAlloc(pParse->db, op, pToken, 1);
- sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
+ Expr *p;
+ if( op==TK_AND && pParse->nErr==0 ){
+ /* Take advantage of short-circuit false optimization for AND */
+ p = sqlite3ExprAnd(pParse->db, pLeft, pRight);
+ }else{
+ p = sqlite3ExprAlloc(pParse->db, op & TKFLG_MASK, pToken, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, p, pLeft, pRight);
+ }
if( p ) {
sqlite3ExprCheckHeight(pParse, p->nHeight);
}
@@ -73512,14 +90553,65 @@ SQLITE_PRIVATE Expr *sqlite3PExpr(
}
/*
+** Add pSelect to the Expr.x.pSelect field. Or, if pExpr is NULL (due
+** do a memory allocation failure) then delete the pSelect object.
+*/
+SQLITE_PRIVATE void sqlite3PExprAddSelect(Parse *pParse, Expr *pExpr, Select *pSelect){
+ if( pExpr ){
+ pExpr->x.pSelect = pSelect;
+ ExprSetProperty(pExpr, EP_xIsSelect|EP_Subquery);
+ sqlite3ExprSetHeightAndFlags(pParse, pExpr);
+ }else{
+ assert( pParse->db->mallocFailed );
+ sqlite3SelectDelete(pParse->db, pSelect);
+ }
+}
+
+
+/*
+** If the expression is always either TRUE or FALSE (respectively),
+** then return 1. If one cannot determine the truth value of the
+** expression at compile-time return 0.
+**
+** This is an optimization. If is OK to return 0 here even if
+** the expression really is always false or false (a false negative).
+** But it is a bug to return 1 if the expression might have different
+** boolean values in different circumstances (a false positive.)
+**
+** Note that if the expression is part of conditional for a
+** LEFT JOIN, then we cannot determine at compile-time whether or not
+** is it true or false, so always return 0.
+*/
+static int exprAlwaysTrue(Expr *p){
+ int v = 0;
+ if( ExprHasProperty(p, EP_FromJoin) ) return 0;
+ if( !sqlite3ExprIsInteger(p, &v) ) return 0;
+ return v!=0;
+}
+static int exprAlwaysFalse(Expr *p){
+ int v = 0;
+ if( ExprHasProperty(p, EP_FromJoin) ) return 0;
+ if( !sqlite3ExprIsInteger(p, &v) ) return 0;
+ return v==0;
+}
+
+/*
** Join two expressions using an AND operator. If either expression is
** NULL, then just return the other expression.
+**
+** If one side or the other of the AND is known to be false, then instead
+** of returning an AND expression, just return a constant expression with
+** a value of false.
*/
SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3 *db, Expr *pLeft, Expr *pRight){
if( pLeft==0 ){
return pRight;
}else if( pRight==0 ){
return pLeft;
+ }else if( exprAlwaysFalse(pLeft) || exprAlwaysFalse(pRight) ){
+ sqlite3ExprDelete(db, pLeft);
+ sqlite3ExprDelete(db, pRight);
+ return sqlite3ExprAlloc(db, TK_INTEGER, &sqlite3IntTokens[0], 0);
}else{
Expr *pNew = sqlite3ExprAlloc(db, TK_AND, 0, 0);
sqlite3ExprAttachSubtrees(db, pNew, pLeft, pRight);
@@ -73542,7 +90634,7 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *
}
pNew->x.pList = pList;
assert( !ExprHasProperty(pNew, EP_xIsSelect) );
- sqlite3ExprSetHeight(pParse, pNew);
+ sqlite3ExprSetHeightAndFlags(pParse, pNew);
return pNew;
}
@@ -73559,31 +90651,31 @@ SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse *pParse, ExprList *pList, Token *
**
** Wildcards of the form ":aaa", "@aaa", or "$aaa" are assigned the same number
** as the previous instance of the same wildcard. Or if this is the first
-** instance of the wildcard, the next sequenial variable number is
+** instance of the wildcard, the next sequential variable number is
** assigned.
*/
-SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
+SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr, u32 n){
sqlite3 *db = pParse->db;
const char *z;
if( pExpr==0 ) return;
- assert( !ExprHasAnyProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) );
+ assert( !ExprHasProperty(pExpr, EP_IntValue|EP_Reduced|EP_TokenOnly) );
z = pExpr->u.zToken;
assert( z!=0 );
assert( z[0]!=0 );
+ assert( n==sqlite3Strlen30(z) );
if( z[1]==0 ){
/* Wildcard of the form "?". Assign the next variable number */
assert( z[0]=='?' );
pExpr->iColumn = (ynVar)(++pParse->nVar);
}else{
- ynVar x = 0;
- u32 n = sqlite3Strlen30(z);
+ ynVar x;
if( z[0]=='?' ){
/* Wildcard of the form "?nnn". Convert "nnn" to an integer and
** use it as the variable number */
i64 i;
int bOk = 0==sqlite3Atoi64(&z[1], &i, n-1, SQLITE_UTF8);
- pExpr->iColumn = x = (ynVar)i;
+ x = (ynVar)i;
testcase( i==0 );
testcase( i==1 );
testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 );
@@ -73591,7 +90683,7 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
if( bOk==0 || i<1 || i>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "variable number must be between ?1 and ?%d",
db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]);
- x = 0;
+ return;
}
if( i>pParse->nVar ){
pParse->nVar = (int)i;
@@ -73602,30 +90694,31 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
** has never appeared before, reuse the same variable number
*/
ynVar i;
- for(i=0; i<pParse->nzVar; i++){
- if( pParse->azVar[i] && memcmp(pParse->azVar[i],z,n+1)==0 ){
- pExpr->iColumn = x = (ynVar)i+1;
+ for(i=x=0; i<pParse->nzVar; i++){
+ if( pParse->azVar[i] && strcmp(pParse->azVar[i],z)==0 ){
+ x = (ynVar)i+1;
break;
}
}
- if( x==0 ) x = pExpr->iColumn = (ynVar)(++pParse->nVar);
+ if( x==0 ) x = (ynVar)(++pParse->nVar);
}
- if( x>0 ){
- if( x>pParse->nzVar ){
- char **a;
- a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
- if( a==0 ) return; /* Error reported through db->mallocFailed */
- pParse->azVar = a;
- memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
- pParse->nzVar = x;
- }
- if( z[0]!='?' || pParse->azVar[x-1]==0 ){
- sqlite3DbFree(db, pParse->azVar[x-1]);
- pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n);
+ pExpr->iColumn = x;
+ if( x>pParse->nzVar ){
+ char **a;
+ a = sqlite3DbRealloc(db, pParse->azVar, x*sizeof(a[0]));
+ if( a==0 ){
+ assert( db->mallocFailed ); /* Error reported through mallocFailed */
+ return;
}
+ pParse->azVar = a;
+ memset(&a[pParse->nzVar], 0, (x-pParse->nzVar)*sizeof(a[0]));
+ pParse->nzVar = x;
+ }
+ if( pParse->azVar[x-1]==0 ){
+ pParse->azVar[x-1] = sqlite3DbStrNDup(db, z, n);
}
}
- if( !pParse->nErr && pParse->nVar>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
+ if( pParse->nVar>db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER] ){
sqlite3ErrorMsg(pParse, "too many SQL variables");
}
}
@@ -73633,26 +90726,36 @@ SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse *pParse, Expr *pExpr){
/*
** Recursively delete an expression tree.
*/
-SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
- if( p==0 ) return;
+static SQLITE_NOINLINE void sqlite3ExprDeleteNN(sqlite3 *db, Expr *p){
+ assert( p!=0 );
/* Sanity check: Assert that the IntValue is non-negative if it exists */
assert( !ExprHasProperty(p, EP_IntValue) || p->u.iValue>=0 );
- if( !ExprHasAnyProperty(p, EP_TokenOnly) ){
- sqlite3ExprDelete(db, p->pLeft);
+#ifdef SQLITE_DEBUG
+ if( ExprHasProperty(p, EP_Leaf) && !ExprHasProperty(p, EP_TokenOnly) ){
+ assert( p->pLeft==0 );
+ assert( p->pRight==0 );
+ assert( p->x.pSelect==0 );
+ }
+#endif
+ if( !ExprHasProperty(p, (EP_TokenOnly|EP_Leaf)) ){
+ /* The Expr.x union is never used at the same time as Expr.pRight */
+ assert( p->x.pList==0 || p->pRight==0 );
+ if( p->pLeft && p->op!=TK_SELECT_COLUMN ) sqlite3ExprDeleteNN(db, p->pLeft);
sqlite3ExprDelete(db, p->pRight);
- if( !ExprHasProperty(p, EP_Reduced) && (p->flags2 & EP2_MallocedToken)!=0 ){
- sqlite3DbFree(db, p->u.zToken);
- }
if( ExprHasProperty(p, EP_xIsSelect) ){
sqlite3SelectDelete(db, p->x.pSelect);
}else{
sqlite3ExprListDelete(db, p->x.pList);
}
}
+ if( ExprHasProperty(p, EP_MemToken) ) sqlite3DbFree(db, p->u.zToken);
if( !ExprHasProperty(p, EP_Static) ){
sqlite3DbFree(db, p);
}
}
+SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
+ if( p ) sqlite3ExprDeleteNN(db, p);
+}
/*
** Return the number of bytes allocated for the expression structure
@@ -73694,7 +90797,7 @@ static int exprStructSize(Expr *p){
** During expression analysis, extra information is computed and moved into
** later parts of teh Expr object and that extra information might get chopped
** off if the expression is reduced. Note also that it does not work to
-** make a EXPRDUP_REDUCE copy of a reduced expression. It is only legal
+** make an EXPRDUP_REDUCE copy of a reduced expression. It is only legal
** to reduce a pristine expression tree from the parser. The implementation
** of dupedExprStructSize() contain multiple assert() statements that attempt
** to enforce this constraint.
@@ -73702,16 +90805,19 @@ static int exprStructSize(Expr *p){
static int dupedExprStructSize(Expr *p, int flags){
int nSize;
assert( flags==EXPRDUP_REDUCE || flags==0 ); /* Only one flag value allowed */
- if( 0==(flags&EXPRDUP_REDUCE) ){
+ assert( EXPR_FULLSIZE<=0xfff );
+ assert( (0xfff & (EP_Reduced|EP_TokenOnly))==0 );
+ if( 0==flags ){
nSize = EXPR_FULLSIZE;
}else{
- assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
assert( !ExprHasProperty(p, EP_FromJoin) );
- assert( (p->flags2 & EP2_MallocedToken)==0 );
- assert( (p->flags2 & EP2_Irreducible)==0 );
- if( p->pLeft || p->pRight || p->pColl || p->x.pList ){
+ assert( !ExprHasProperty(p, EP_MemToken) );
+ assert( !ExprHasProperty(p, EP_NoReduce) );
+ if( p->pLeft || p->x.pList ){
nSize = EXPR_REDUCEDSIZE | EP_Reduced;
}else{
+ assert( p->pRight==0 );
nSize = EXPR_TOKENONLYSIZE | EP_TokenOnly;
}
}
@@ -73760,94 +90866,128 @@ static int dupedExprSize(Expr *p, int flags){
** is not NULL then *pzBuffer is assumed to point to a buffer large enough
** to store the copy of expression p, the copies of p->u.zToken
** (if applicable), and the copies of the p->pLeft and p->pRight expressions,
-** if any. Before returning, *pzBuffer is set to the first byte passed the
+** if any. Before returning, *pzBuffer is set to the first byte past the
** portion of the buffer copied into by this function.
*/
-static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
- Expr *pNew = 0; /* Value to return */
- if( p ){
- const int isReduced = (flags&EXPRDUP_REDUCE);
- u8 *zAlloc;
- u32 staticFlag = 0;
+static Expr *exprDup(sqlite3 *db, Expr *p, int dupFlags, u8 **pzBuffer){
+ Expr *pNew; /* Value to return */
+ u8 *zAlloc; /* Memory space from which to build Expr object */
+ u32 staticFlag; /* EP_Static if space not obtained from malloc */
- assert( pzBuffer==0 || isReduced );
+ assert( db!=0 );
+ assert( p );
+ assert( dupFlags==0 || dupFlags==EXPRDUP_REDUCE );
+ assert( pzBuffer==0 || dupFlags==EXPRDUP_REDUCE );
- /* Figure out where to write the new Expr structure. */
- if( pzBuffer ){
- zAlloc = *pzBuffer;
- staticFlag = EP_Static;
+ /* Figure out where to write the new Expr structure. */
+ if( pzBuffer ){
+ zAlloc = *pzBuffer;
+ staticFlag = EP_Static;
+ }else{
+ zAlloc = sqlite3DbMallocRawNN(db, dupedExprSize(p, dupFlags));
+ staticFlag = 0;
+ }
+ pNew = (Expr *)zAlloc;
+
+ if( pNew ){
+ /* Set nNewSize to the size allocated for the structure pointed to
+ ** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or
+ ** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed
+ ** by the copy of the p->u.zToken string (if any).
+ */
+ const unsigned nStructSize = dupedExprStructSize(p, dupFlags);
+ const int nNewSize = nStructSize & 0xfff;
+ int nToken;
+ if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+ nToken = sqlite3Strlen30(p->u.zToken) + 1;
}else{
- zAlloc = sqlite3DbMallocRaw(db, dupedExprSize(p, flags));
+ nToken = 0;
}
- pNew = (Expr *)zAlloc;
-
- if( pNew ){
- /* Set nNewSize to the size allocated for the structure pointed to
- ** by pNew. This is either EXPR_FULLSIZE, EXPR_REDUCEDSIZE or
- ** EXPR_TOKENONLYSIZE. nToken is set to the number of bytes consumed
- ** by the copy of the p->u.zToken string (if any).
- */
- const unsigned nStructSize = dupedExprStructSize(p, flags);
- const int nNewSize = nStructSize & 0xfff;
- int nToken;
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- nToken = sqlite3Strlen30(p->u.zToken) + 1;
- }else{
- nToken = 0;
- }
- if( isReduced ){
- assert( ExprHasProperty(p, EP_Reduced)==0 );
- memcpy(zAlloc, p, nNewSize);
- }else{
- int nSize = exprStructSize(p);
- memcpy(zAlloc, p, nSize);
+ if( dupFlags ){
+ assert( ExprHasProperty(p, EP_Reduced)==0 );
+ memcpy(zAlloc, p, nNewSize);
+ }else{
+ u32 nSize = (u32)exprStructSize(p);
+ memcpy(zAlloc, p, nSize);
+ if( nSize<EXPR_FULLSIZE ){
memset(&zAlloc[nSize], 0, EXPR_FULLSIZE-nSize);
}
+ }
- /* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
- pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static);
- pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
- pNew->flags |= staticFlag;
+ /* Set the EP_Reduced, EP_TokenOnly, and EP_Static flags appropriately. */
+ pNew->flags &= ~(EP_Reduced|EP_TokenOnly|EP_Static|EP_MemToken);
+ pNew->flags |= nStructSize & (EP_Reduced|EP_TokenOnly);
+ pNew->flags |= staticFlag;
- /* Copy the p->u.zToken string, if any. */
- if( nToken ){
- char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
- memcpy(zToken, p->u.zToken, nToken);
- }
+ /* Copy the p->u.zToken string, if any. */
+ if( nToken ){
+ char *zToken = pNew->u.zToken = (char*)&zAlloc[nNewSize];
+ memcpy(zToken, p->u.zToken, nToken);
+ }
- if( 0==((p->flags|pNew->flags) & EP_TokenOnly) ){
- /* Fill in the pNew->x.pSelect or pNew->x.pList member. */
- if( ExprHasProperty(p, EP_xIsSelect) ){
- pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, isReduced);
- }else{
- pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, isReduced);
- }
+ if( 0==((p->flags|pNew->flags) & (EP_TokenOnly|EP_Leaf)) ){
+ /* Fill in the pNew->x.pSelect or pNew->x.pList member. */
+ if( ExprHasProperty(p, EP_xIsSelect) ){
+ pNew->x.pSelect = sqlite3SelectDup(db, p->x.pSelect, dupFlags);
+ }else{
+ pNew->x.pList = sqlite3ExprListDup(db, p->x.pList, dupFlags);
}
+ }
- /* Fill in pNew->pLeft and pNew->pRight. */
- if( ExprHasAnyProperty(pNew, EP_Reduced|EP_TokenOnly) ){
- zAlloc += dupedExprNodeSize(p, flags);
- if( ExprHasProperty(pNew, EP_Reduced) ){
- pNew->pLeft = exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc);
- pNew->pRight = exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc);
- }
- if( pzBuffer ){
- *pzBuffer = zAlloc;
- }
- }else{
- pNew->flags2 = 0;
- if( !ExprHasAnyProperty(p, EP_TokenOnly) ){
+ /* Fill in pNew->pLeft and pNew->pRight. */
+ if( ExprHasProperty(pNew, EP_Reduced|EP_TokenOnly) ){
+ zAlloc += dupedExprNodeSize(p, dupFlags);
+ if( !ExprHasProperty(pNew, EP_TokenOnly|EP_Leaf) ){
+ pNew->pLeft = p->pLeft ?
+ exprDup(db, p->pLeft, EXPRDUP_REDUCE, &zAlloc) : 0;
+ pNew->pRight = p->pRight ?
+ exprDup(db, p->pRight, EXPRDUP_REDUCE, &zAlloc) : 0;
+ }
+ if( pzBuffer ){
+ *pzBuffer = zAlloc;
+ }
+ }else{
+ if( !ExprHasProperty(p, EP_TokenOnly|EP_Leaf) ){
+ if( pNew->op==TK_SELECT_COLUMN ){
+ pNew->pLeft = p->pLeft;
+ }else{
pNew->pLeft = sqlite3ExprDup(db, p->pLeft, 0);
- pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
}
+ pNew->pRight = sqlite3ExprDup(db, p->pRight, 0);
}
-
}
}
return pNew;
}
/*
+** Create and return a deep copy of the object passed as the second
+** argument. If an OOM condition is encountered, NULL is returned
+** and the db->mallocFailed flag set.
+*/
+#ifndef SQLITE_OMIT_CTE
+static With *withDup(sqlite3 *db, With *p){
+ With *pRet = 0;
+ if( p ){
+ int nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
+ pRet = sqlite3DbMallocZero(db, nByte);
+ if( pRet ){
+ int i;
+ pRet->nCte = p->nCte;
+ for(i=0; i<p->nCte; i++){
+ pRet->a[i].pSelect = sqlite3SelectDup(db, p->a[i].pSelect, 0);
+ pRet->a[i].pCols = sqlite3ExprListDup(db, p->a[i].pCols, 0);
+ pRet->a[i].zName = sqlite3DbStrDup(db, p->a[i].zName);
+ }
+ }
+ }
+ return pRet;
+}
+#else
+# define withDup(x,y) 0
+#endif
+
+/*
** The following group of routines make deep copies of expressions,
** expression lists, ID lists, and select statements. The copies can
** be deleted (by being passed to their respective ...Delete() routines)
@@ -73865,18 +91005,20 @@ static Expr *exprDup(sqlite3 *db, Expr *p, int flags, u8 **pzBuffer){
** part of the in-memory representation of the database schema.
*/
SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3 *db, Expr *p, int flags){
- return exprDup(db, p, flags, 0);
+ assert( flags==0 || flags==EXPRDUP_REDUCE );
+ return p ? exprDup(db, p, flags, 0) : 0;
}
SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags){
ExprList *pNew;
struct ExprList_item *pItem, *pOldItem;
int i;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
- pNew->iECursor = 0;
- pNew->nExpr = pNew->nAlloc = p->nExpr;
- pNew->a = pItem = sqlite3DbMallocRaw(db, p->nExpr*sizeof(p->a[0]) );
+ pNew->nExpr = i = p->nExpr;
+ if( (flags & EXPRDUP_REDUCE)==0 ) for(i=1; i<p->nExpr; i+=i){}
+ pNew->a = pItem = sqlite3DbMallocRawNN(db, i*sizeof(p->a[0]) );
if( pItem==0 ){
sqlite3DbFree(db, pNew);
return 0;
@@ -73889,8 +91031,8 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, ExprList *p, int flags)
pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan);
pItem->sortOrder = pOldItem->sortOrder;
pItem->done = 0;
- pItem->iCol = pOldItem->iCol;
- pItem->iAlias = pOldItem->iAlias;
+ pItem->bSpanIsTab = pOldItem->bSpanIsTab;
+ pItem->u = pOldItem->u;
}
return pNew;
}
@@ -73907,26 +91049,32 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
SrcList *pNew;
int i;
int nByte;
+ assert( db!=0 );
if( p==0 ) return 0;
nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
- pNew = sqlite3DbMallocRaw(db, nByte );
+ pNew = sqlite3DbMallocRawNN(db, nByte );
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; i<p->nSrc; i++){
struct SrcList_item *pNewItem = &pNew->a[i];
struct SrcList_item *pOldItem = &p->a[i];
Table *pTab;
+ pNewItem->pSchema = pOldItem->pSchema;
pNewItem->zDatabase = sqlite3DbStrDup(db, pOldItem->zDatabase);
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
pNewItem->zAlias = sqlite3DbStrDup(db, pOldItem->zAlias);
- pNewItem->jointype = pOldItem->jointype;
+ pNewItem->fg = pOldItem->fg;
pNewItem->iCursor = pOldItem->iCursor;
pNewItem->addrFillSub = pOldItem->addrFillSub;
pNewItem->regReturn = pOldItem->regReturn;
- pNewItem->isCorrelated = pOldItem->isCorrelated;
- pNewItem->zIndex = sqlite3DbStrDup(db, pOldItem->zIndex);
- pNewItem->notIndexed = pOldItem->notIndexed;
- pNewItem->pIndex = pOldItem->pIndex;
+ if( pNewItem->fg.isIndexedBy ){
+ pNewItem->u1.zIndexedBy = sqlite3DbStrDup(db, pOldItem->u1.zIndexedBy);
+ }
+ pNewItem->pIBIndex = pOldItem->pIBIndex;
+ if( pNewItem->fg.isTabFunc ){
+ pNewItem->u1.pFuncArg =
+ sqlite3ExprListDup(db, pOldItem->u1.pFuncArg, flags);
+ }
pTab = pNewItem->pTab = pOldItem->pTab;
if( pTab ){
pTab->nRef++;
@@ -73941,15 +91089,19 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, SrcList *p, int flags){
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
IdList *pNew;
int i;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*pNew) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ) return 0;
- pNew->nId = pNew->nAlloc = p->nId;
- pNew->a = sqlite3DbMallocRaw(db, p->nId*sizeof(p->a[0]) );
+ pNew->nId = p->nId;
+ pNew->a = sqlite3DbMallocRawNN(db, p->nId*sizeof(p->a[0]) );
if( pNew->a==0 ){
sqlite3DbFree(db, pNew);
return 0;
}
+ /* Note that because the size of the allocation for p->a[] is not
+ ** necessarily a power of two, sqlite3IdListAppend() may not be called
+ ** on the duplicate created by this function. */
for(i=0; i<p->nId; i++){
struct IdList_item *pNewItem = &pNew->a[i];
struct IdList_item *pOldItem = &p->a[i];
@@ -73959,9 +91111,10 @@ SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){
return pNew;
}
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
- Select *pNew;
+ Select *pNew, *pPrior;
+ assert( db!=0 );
if( p==0 ) return 0;
- pNew = sqlite3DbMallocRaw(db, sizeof(*p) );
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*p) );
if( pNew==0 ) return 0;
pNew->pEList = sqlite3ExprListDup(db, p->pEList, flags);
pNew->pSrc = sqlite3SrcListDup(db, p->pSrc, flags);
@@ -73970,16 +91123,19 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){
pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags);
pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags);
pNew->op = p->op;
- pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags);
+ pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags);
+ if( pPrior ) pPrior->pNext = pNew;
+ pNew->pNext = 0;
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags);
pNew->iLimit = 0;
pNew->iOffset = 0;
pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
- pNew->pRightmost = 0;
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
- pNew->addrOpenEphm[2] = -1;
+ pNew->nSelectRow = p->nSelectRow;
+ pNew->pWith = withDup(db, p->pWith);
+ sqlite3SelectSetName(pNew, p->zSelName);
return pNew;
}
#else
@@ -74004,22 +91160,23 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(
Expr *pExpr /* Expression to be appended. Might be NULL */
){
sqlite3 *db = pParse->db;
+ assert( db!=0 );
if( pList==0 ){
- pList = sqlite3DbMallocZero(db, sizeof(ExprList) );
+ pList = sqlite3DbMallocRawNN(db, sizeof(ExprList) );
if( pList==0 ){
goto no_mem;
}
- assert( pList->nAlloc==0 );
- }
- if( pList->nAlloc<=pList->nExpr ){
+ pList->nExpr = 0;
+ pList->a = sqlite3DbMallocRawNN(db, sizeof(pList->a[0]));
+ if( pList->a==0 ) goto no_mem;
+ }else if( (pList->nExpr & (pList->nExpr-1))==0 ){
struct ExprList_item *a;
- int n = pList->nAlloc*2 + 4;
- a = sqlite3DbRealloc(db, pList->a, n*sizeof(pList->a[0]));
+ assert( pList->nExpr>0 );
+ a = sqlite3DbRealloc(db, pList->a, pList->nExpr*2*sizeof(pList->a[0]));
if( a==0 ){
goto no_mem;
}
pList->a = a;
- pList->nAlloc = sqlite3DbMallocSize(db, a)/sizeof(a[0]);
}
assert( pList->a!=0 );
if( 1 ){
@@ -74037,6 +91194,74 @@ no_mem:
}
/*
+** pColumns and pExpr form a vector assignment which is part of the SET
+** clause of an UPDATE statement. Like this:
+**
+** (a,b,c) = (expr1,expr2,expr3)
+** Or: (a,b,c) = (SELECT x,y,z FROM ....)
+**
+** For each term of the vector assignment, append new entries to the
+** expression list pList. In the case of a subquery on the LHS, append
+** TK_SELECT_COLUMN expressions.
+*/
+SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* List to which to append. Might be NULL */
+ IdList *pColumns, /* List of names of LHS of the assignment */
+ Expr *pExpr /* Vector expression to be appended. Might be NULL */
+){
+ sqlite3 *db = pParse->db;
+ int n;
+ int i;
+ int iFirst = pList ? pList->nExpr : 0;
+ /* pColumns can only be NULL due to an OOM but an OOM will cause an
+ ** exit prior to this routine being invoked */
+ if( NEVER(pColumns==0) ) goto vector_append_error;
+ if( pExpr==0 ) goto vector_append_error;
+ n = sqlite3ExprVectorSize(pExpr);
+ if( pColumns->nId!=n ){
+ sqlite3ErrorMsg(pParse, "%d columns assigned %d values",
+ pColumns->nId, n);
+ goto vector_append_error;
+ }
+ for(i=0; i<n; i++){
+ Expr *pSubExpr = sqlite3ExprForVectorField(pParse, pExpr, i);
+ pList = sqlite3ExprListAppend(pParse, pList, pSubExpr);
+ if( pList ){
+ assert( pList->nExpr==iFirst+i+1 );
+ pList->a[pList->nExpr-1].zName = pColumns->a[i].zName;
+ pColumns->a[i].zName = 0;
+ }
+ }
+ if( pExpr->op==TK_SELECT ){
+ if( pList && pList->a[iFirst].pExpr ){
+ assert( pList->a[iFirst].pExpr->op==TK_SELECT_COLUMN );
+ pList->a[iFirst].pExpr->pRight = pExpr;
+ pExpr = 0;
+ }
+ }
+
+vector_append_error:
+ sqlite3ExprDelete(db, pExpr);
+ sqlite3IdListDelete(db, pColumns);
+ return pList;
+}
+
+/*
+** Set the sort order for the last element on the given ExprList.
+*/
+SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList *p, int iSortOrder){
+ if( p==0 ) return;
+ assert( SQLITE_SO_UNDEFINED<0 && SQLITE_SO_ASC>=0 && SQLITE_SO_DESC>0 );
+ assert( p->nExpr>0 );
+ if( iSortOrder<0 ){
+ assert( p->a[p->nExpr-1].sortOrder==SQLITE_SO_ASC );
+ return;
+ }
+ p->a[p->nExpr-1].sortOrder = (u8)iSortOrder;
+}
+
+/*
** Set the ExprList.a[].zName element of the most recently added item
** on the expression list.
**
@@ -74057,7 +91282,7 @@ SQLITE_PRIVATE void sqlite3ExprListSetName(
pItem = &pList->a[pList->nExpr-1];
assert( pItem->zName==0 );
pItem->zName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
- if( dequote && pItem->zName ) sqlite3Dequote(pItem->zName);
+ if( dequote ) sqlite3Dequote(pItem->zName);
}
}
@@ -74106,12 +91331,10 @@ SQLITE_PRIVATE void sqlite3ExprListCheckLength(
/*
** Delete an entire expression list.
*/
-SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
+static SQLITE_NOINLINE void exprListDeleteNN(sqlite3 *db, ExprList *pList){
int i;
struct ExprList_item *pItem;
- if( pList==0 ) return;
- assert( pList->a!=0 || (pList->nExpr==0 && pList->nAlloc==0) );
- assert( pList->nExpr<=pList->nAlloc );
+ assert( pList->a!=0 || pList->nExpr==0 );
for(pItem=pList->a, i=0; i<pList->nExpr; i++, pItem++){
sqlite3ExprDelete(db, pItem->pExpr);
sqlite3DbFree(db, pItem->zName);
@@ -74120,36 +91343,73 @@ SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
sqlite3DbFree(db, pList->a);
sqlite3DbFree(db, pList);
}
+SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
+ if( pList ) exprListDeleteNN(db, pList);
+}
+
+/*
+** Return the bitwise-OR of all Expr.flags fields in the given
+** ExprList.
+*/
+SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
+ int i;
+ u32 m = 0;
+ if( pList ){
+ for(i=0; i<pList->nExpr; i++){
+ Expr *pExpr = pList->a[i].pExpr;
+ assert( pExpr!=0 );
+ m |= pExpr->flags;
+ }
+ }
+ return m;
+}
/*
-** These routines are Walker callbacks. Walker.u.pi is a pointer
-** to an integer. These routines are checking an expression to see
-** if it is a constant. Set *Walker.u.pi to 0 if the expression is
-** not constant.
+** These routines are Walker callbacks used to check expressions to
+** see if they are "constant" for some definition of constant. The
+** Walker.eCode value determines the type of "constant" we are looking
+** for.
**
** These callback routines are used to implement the following:
**
-** sqlite3ExprIsConstant()
-** sqlite3ExprIsConstantNotJoin()
-** sqlite3ExprIsConstantOrFunction()
+** sqlite3ExprIsConstant() pWalker->eCode==1
+** sqlite3ExprIsConstantNotJoin() pWalker->eCode==2
+** sqlite3ExprIsTableConstant() pWalker->eCode==3
+** sqlite3ExprIsConstantOrFunction() pWalker->eCode==4 or 5
+**
+** In all cases, the callbacks set Walker.eCode=0 and abort if the expression
+** is found to not be a constant.
**
+** The sqlite3ExprIsConstantOrFunction() is used for evaluating expressions
+** in a CREATE TABLE statement. The Walker.eCode value is 5 when parsing
+** an existing schema and 4 when processing a new statement. A bound
+** parameter raises an error for new statements, but is silently converted
+** to NULL for existing schemas. This allows sqlite_master tables that
+** contain a bound parameter because they were generated by older versions
+** of SQLite to be parsed by newer versions of SQLite without raising a
+** malformed schema error.
*/
static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
- /* If pWalker->u.i is 3 then any term of the expression that comes from
- ** the ON or USING clauses of a join disqualifies the expression
+ /* If pWalker->eCode is 2 then any term of the expression that comes from
+ ** the ON or USING clauses of a left join disqualifies the expression
** from being considered constant. */
- if( pWalker->u.i==3 && ExprHasAnyProperty(pExpr, EP_FromJoin) ){
- pWalker->u.i = 0;
+ if( pWalker->eCode==2 && ExprHasProperty(pExpr, EP_FromJoin) ){
+ pWalker->eCode = 0;
return WRC_Abort;
}
switch( pExpr->op ){
/* Consider functions to be constant if all their arguments are constant
- ** and pWalker->u.i==2 */
+ ** and either pWalker->eCode==4 or 5 or the function has the
+ ** SQLITE_FUNC_CONST flag. */
case TK_FUNCTION:
- if( pWalker->u.i==2 ) return 0;
- /* Fall through */
+ if( pWalker->eCode>=4 || ExprHasProperty(pExpr,EP_ConstFunc) ){
+ return WRC_Continue;
+ }else{
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
case TK_ID:
case TK_COLUMN:
case TK_AGG_FUNCTION:
@@ -74158,8 +91418,25 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
testcase( pExpr->op==TK_COLUMN );
testcase( pExpr->op==TK_AGG_FUNCTION );
testcase( pExpr->op==TK_AGG_COLUMN );
- pWalker->u.i = 0;
- return WRC_Abort;
+ if( pWalker->eCode==3 && pExpr->iTable==pWalker->u.iCur ){
+ return WRC_Continue;
+ }else{
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ case TK_VARIABLE:
+ if( pWalker->eCode==5 ){
+ /* Silently convert bound parameters that appear inside of CREATE
+ ** statements into a NULL when parsing the CREATE statement text out
+ ** of the sqlite_master table */
+ pExpr->op = TK_NULL;
+ }else if( pWalker->eCode==4 ){
+ /* A bound parameter in a CREATE statement that originates from
+ ** sqlite3_prepare() causes an error */
+ pWalker->eCode = 0;
+ return WRC_Abort;
+ }
+ /* Fall through */
default:
testcase( pExpr->op==TK_SELECT ); /* selectNodeIsConstant will disallow */
testcase( pExpr->op==TK_EXISTS ); /* selectNodeIsConstant will disallow */
@@ -74168,20 +91445,22 @@ static int exprNodeIsConstant(Walker *pWalker, Expr *pExpr){
}
static int selectNodeIsConstant(Walker *pWalker, Select *NotUsed){
UNUSED_PARAMETER(NotUsed);
- pWalker->u.i = 0;
+ pWalker->eCode = 0;
return WRC_Abort;
}
-static int exprIsConst(Expr *p, int initFlag){
+static int exprIsConst(Expr *p, int initFlag, int iCur){
Walker w;
- w.u.i = initFlag;
+ memset(&w, 0, sizeof(w));
+ w.eCode = initFlag;
w.xExprCallback = exprNodeIsConstant;
w.xSelectCallback = selectNodeIsConstant;
+ w.u.iCur = iCur;
sqlite3WalkExpr(&w, p);
- return w.u.i;
+ return w.eCode;
}
/*
-** Walk an expression tree. Return 1 if the expression is constant
+** Walk an expression tree. Return non-zero if the expression is constant
** and 0 if it involves variables or function calls.
**
** For the purposes of this function, a double-quoted string (ex: "abc")
@@ -74189,21 +91468,31 @@ static int exprIsConst(Expr *p, int initFlag){
** a constant.
*/
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr *p){
- return exprIsConst(p, 1);
+ return exprIsConst(p, 1, 0);
}
/*
-** Walk an expression tree. Return 1 if the expression is constant
+** Walk an expression tree. Return non-zero if the expression is constant
** that does no originate from the ON or USING clauses of a join.
** Return 0 if it involves variables or function calls or terms from
** an ON or USING clause.
*/
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
- return exprIsConst(p, 3);
+ return exprIsConst(p, 2, 0);
+}
+
+/*
+** Walk an expression tree. Return non-zero if the expression is constant
+** for any single row of the table with cursor iCur. In other words, the
+** expression must not refer to any non-deterministic function nor any
+** table other than iCur.
+*/
+SQLITE_PRIVATE int sqlite3ExprIsTableConstant(Expr *p, int iCur){
+ return exprIsConst(p, 3, iCur);
}
/*
-** Walk an expression tree. Return 1 if the expression is constant
+** Walk an expression tree. Return non-zero if the expression is constant
** or a function call with constant arguments. Return and 0 if there
** are any variables.
**
@@ -74211,9 +91500,26 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr *p){
** is considered a variable but a single-quoted string (ex: 'abc') is
** a constant.
*/
-SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p){
- return exprIsConst(p, 2);
+SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr *p, u8 isInit){
+ assert( isInit==0 || isInit==1 );
+ return exprIsConst(p, 4+isInit, 0);
+}
+
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+/*
+** Walk an expression tree. Return 1 if the expression contains a
+** subquery of some kind. Return 0 if there are no subqueries.
+*/
+SQLITE_PRIVATE int sqlite3ExprContainsSubquery(Expr *p){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.eCode = 1;
+ w.xExprCallback = sqlite3ExprWalkNoop;
+ w.xSelectCallback = selectNodeIsConstant;
+ sqlite3WalkExpr(&w, p);
+ return w.eCode==0;
}
+#endif
/*
** If the expression p codes a constant integer that is small enough
@@ -74241,6 +91547,7 @@ SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr *p, int *pValue){
case TK_UMINUS: {
int v;
if( sqlite3ExprIsInteger(p->pLeft, &v) ){
+ assert( v!=(-2147483647-1) );
*pValue = -v;
rc = 1;
}
@@ -74276,30 +91583,16 @@ SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){
case TK_FLOAT:
case TK_BLOB:
return 0;
+ case TK_COLUMN:
+ assert( p->pTab!=0 );
+ return ExprHasProperty(p, EP_CanBeNull) ||
+ (p->iColumn>=0 && p->pTab->aCol[p->iColumn].notNull==0);
default:
return 1;
}
}
/*
-** Generate an OP_IsNull instruction that tests register iReg and jumps
-** to location iDest if the value in iReg is NULL. The value in iReg
-** was computed by pExpr. If we can look at pExpr at compile-time and
-** determine that it can never generate a NULL, then the OP_IsNull operation
-** can be omitted.
-*/
-SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(
- Vdbe *v, /* The VDBE under construction */
- const Expr *pExpr, /* Only generate OP_IsNull if this expr can be NULL */
- int iReg, /* Test the value in this register for NULL */
- int iDest /* Jump here if the value is null */
-){
- if( sqlite3ExprCanBeNull(pExpr) ){
- sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iDest);
- }
-}
-
-/*
** Return TRUE if the given expression is a constant which would be
** unchanged by OP_Affinity with the affinity given in the second
** argument.
@@ -74311,7 +91604,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(
*/
SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){
u8 op;
- if( aff==SQLITE_AFF_NONE ) return 1;
+ if( aff==SQLITE_AFF_BLOB ) return 1;
while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; }
op = p->op;
if( op==TK_REGISTER ) op = p->op2;
@@ -74350,23 +91643,22 @@ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){
}
/*
-** Return true if we are able to the IN operator optimization on a
-** query of the form
-**
-** x IN (SELECT ...)
-**
-** Where the SELECT... clause is as specified by the parameter to this
-** routine.
-**
-** The Select object passed in has already been preprocessed and no
-** errors have been found.
+** pX is the RHS of an IN operator. If pX is a SELECT statement
+** that can be simplified to a direct table access, then return
+** a pointer to the SELECT statement. If pX is not a SELECT statement,
+** or if the SELECT statement needs to be manifested into a transient
+** table, then return NULL.
*/
#ifndef SQLITE_OMIT_SUBQUERY
-static int isCandidateForInOpt(Select *p){
+static Select *isCandidateForInOpt(Expr *pX){
+ Select *p;
SrcList *pSrc;
ExprList *pEList;
Table *pTab;
- if( p==0 ) return 0; /* right-hand side of IN is SELECT */
+ int i;
+ if( !ExprHasProperty(pX, EP_xIsSelect) ) return 0; /* Not a subquery */
+ if( ExprHasProperty(pX, EP_VarSelect) ) return 0; /* Correlated subq */
+ p = pX->x.pSelect;
if( p->pPrior ) return 0; /* Not a compound SELECT */
if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
testcase( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
@@ -74382,107 +91674,196 @@ static int isCandidateForInOpt(Select *p){
if( pSrc->nSrc!=1 ) return 0; /* Single term in FROM clause */
if( pSrc->a[0].pSelect ) return 0; /* FROM is not a subquery or view */
pTab = pSrc->a[0].pTab;
- if( NEVER(pTab==0) ) return 0;
+ assert( pTab!=0 );
assert( pTab->pSelect==0 ); /* FROM clause is not a view */
if( IsVirtual(pTab) ) return 0; /* FROM clause not a virtual table */
pEList = p->pEList;
- if( pEList->nExpr!=1 ) return 0; /* One column in the result set */
- if( pEList->a[0].pExpr->op!=TK_COLUMN ) return 0; /* Result is a column */
- return 1;
+ assert( pEList!=0 );
+ /* All SELECT results must be columns. */
+ for(i=0; i<pEList->nExpr; i++){
+ Expr *pRes = pEList->a[i].pExpr;
+ if( pRes->op!=TK_COLUMN ) return 0;
+ assert( pRes->iTable==pSrc->a[0].iCursor ); /* Not a correlated subquery */
+ }
+ return p;
}
#endif /* SQLITE_OMIT_SUBQUERY */
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Generate code that checks the left-most column of index table iCur to see if
+** it contains any NULL entries. Cause the register at regHasNull to be set
+** to a non-NULL value if iCur contains no NULLs. Cause register regHasNull
+** to be set to NULL if iCur contains one or more NULL values.
+*/
+static void sqlite3SetHasNullFlag(Vdbe *v, int iCur, int regHasNull){
+ int addr1;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regHasNull);
+ addr1 = sqlite3VdbeAddOp1(v, OP_Rewind, iCur); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Column, iCur, 0, regHasNull);
+ sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
+ VdbeComment((v, "first_entry_in(%d)", iCur));
+ sqlite3VdbeJumpHere(v, addr1);
+}
+#endif
+
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** The argument is an IN operator with a list (not a subquery) on the
+** right-hand side. Return TRUE if that list is constant.
+*/
+static int sqlite3InRhsIsConstant(Expr *pIn){
+ Expr *pLHS;
+ int res;
+ assert( !ExprHasProperty(pIn, EP_xIsSelect) );
+ pLHS = pIn->pLeft;
+ pIn->pLeft = 0;
+ res = sqlite3ExprIsConstant(pIn);
+ pIn->pLeft = pLHS;
+ return res;
+}
+#endif
+
/*
** This function is used by the implementation of the IN (...) operator.
-** It's job is to find or create a b-tree structure that may be used
-** either to test for membership of the (...) set or to iterate through
-** its members, skipping duplicates.
-**
-** The index of the cursor opened on the b-tree (database table, database index
-** or ephermal table) is stored in pX->iTable before this function returns.
-** The returned value of this function indicates the b-tree type, as follows:
-**
-** IN_INDEX_ROWID - The cursor was opened on a database table.
-** IN_INDEX_INDEX - The cursor was opened on a database index.
-** IN_INDEX_EPH - The cursor was opened on a specially created and
-** populated epheremal table.
+** The pX parameter is the expression on the RHS of the IN operator, which
+** might be either a list of expressions or a subquery.
**
-** An existing b-tree may only be used if the SELECT is of the simple
-** form:
+** The job of this routine is to find or create a b-tree object that can
+** be used either to test for membership in the RHS set or to iterate through
+** all members of the RHS set, skipping duplicates.
**
-** SELECT <column> FROM <table>
+** A cursor is opened on the b-tree object that is the RHS of the IN operator
+** and pX->iTable is set to the index of that cursor.
**
-** If the prNotFound parameter is 0, then the b-tree will be used to iterate
-** through the set members, skipping any duplicates. In this case an
-** epheremal table must be used unless the selected <column> is guaranteed
-** to be unique - either because it is an INTEGER PRIMARY KEY or it
-** has a UNIQUE constraint or UNIQUE index.
+** The returned value of this function indicates the b-tree type, as follows:
**
-** If the prNotFound parameter is not 0, then the b-tree will be used
-** for fast set membership tests. In this case an epheremal table must
-** be used unless <column> is an INTEGER PRIMARY KEY or an index can
-** be found with <column> as its left-most column.
+** IN_INDEX_ROWID - The cursor was opened on a database table.
+** IN_INDEX_INDEX_ASC - The cursor was opened on an ascending index.
+** IN_INDEX_INDEX_DESC - The cursor was opened on a descending index.
+** IN_INDEX_EPH - The cursor was opened on a specially created and
+** populated epheremal table.
+** IN_INDEX_NOOP - No cursor was allocated. The IN operator must be
+** implemented as a sequence of comparisons.
+**
+** An existing b-tree might be used if the RHS expression pX is a simple
+** subquery such as:
+**
+** SELECT <column1>, <column2>... FROM <table>
+**
+** If the RHS of the IN operator is a list or a more complex subquery, then
+** an ephemeral table might need to be generated from the RHS and then
+** pX->iTable made to point to the ephemeral table instead of an
+** existing table.
+**
+** The inFlags parameter must contain exactly one of the bits
+** IN_INDEX_MEMBERSHIP or IN_INDEX_LOOP. If inFlags contains
+** IN_INDEX_MEMBERSHIP, then the generated table will be used for a
+** fast membership test. When the IN_INDEX_LOOP bit is set, the
+** IN index will be used to loop over all values of the RHS of the
+** IN operator.
+**
+** When IN_INDEX_LOOP is used (and the b-tree will be used to iterate
+** through the set members) then the b-tree must not contain duplicates.
+** An epheremal table must be used unless the selected columns are guaranteed
+** to be unique - either because it is an INTEGER PRIMARY KEY or due to
+** a UNIQUE constraint or index.
+**
+** When IN_INDEX_MEMBERSHIP is used (and the b-tree will be used
+** for fast set membership tests) then an epheremal table must
+** be used unless <columns> is a single INTEGER PRIMARY KEY column or an
+** index can be found with the specified <columns> as its left-most.
+**
+** If the IN_INDEX_NOOP_OK and IN_INDEX_MEMBERSHIP are both set and
+** if the RHS of the IN operator is a list (not a subquery) then this
+** routine might decide that creating an ephemeral b-tree for membership
+** testing is too expensive and return IN_INDEX_NOOP. In that case, the
+** calling routine should implement the IN operator using a sequence
+** of Eq or Ne comparison operations.
**
** When the b-tree is being used for membership tests, the calling function
-** needs to know whether or not the structure contains an SQL NULL
-** value in order to correctly evaluate expressions like "X IN (Y, Z)".
-** If there is any chance that the (...) might contain a NULL value at
+** might need to know whether or not the RHS side of the IN operator
+** contains a NULL. If prRhsHasNull is not a NULL pointer and
+** if there is any chance that the (...) might contain a NULL value at
** runtime, then a register is allocated and the register number written
-** to *prNotFound. If there is no chance that the (...) contains a
-** NULL value, then *prNotFound is left unchanged.
-**
-** If a register is allocated and its location stored in *prNotFound, then
-** its initial value is NULL. If the (...) does not remain constant
-** for the duration of the query (i.e. the SELECT within the (...)
-** is a correlated subquery) then the value of the allocated register is
-** reset to NULL each time the subquery is rerun. This allows the
-** caller to use vdbe code equivalent to the following:
-**
-** if( register==NULL ){
-** has_null = <test if data structure contains null>
-** register = 1
-** }
+** to *prRhsHasNull. If there is no chance that the (...) contains a
+** NULL value, then *prRhsHasNull is left unchanged.
+**
+** If a register is allocated and its location stored in *prRhsHasNull, then
+** the value in that register will be NULL if the b-tree contains one or more
+** NULL values, and it will be some non-NULL value if the b-tree contains no
+** NULL values.
**
-** in order to avoid running the <test if data structure contains null>
-** test more often than is necessary.
+** If the aiMap parameter is not NULL, it must point to an array containing
+** one element for each column returned by the SELECT statement on the RHS
+** of the IN(...) operator. The i'th entry of the array is populated with the
+** offset of the index column that matches the i'th column returned by the
+** SELECT. For example, if the expression and selected index are:
+**
+** (?,?,?) IN (SELECT a, b, c FROM t1)
+** CREATE INDEX i1 ON t1(b, c, a);
+**
+** then aiMap[] is populated with {2, 0, 1}.
*/
#ifndef SQLITE_OMIT_SUBQUERY
-SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
+SQLITE_PRIVATE int sqlite3FindInIndex(
+ Parse *pParse, /* Parsing context */
+ Expr *pX, /* The right-hand side (RHS) of the IN operator */
+ u32 inFlags, /* IN_INDEX_LOOP, _MEMBERSHIP, and/or _NOOP_OK */
+ int *prRhsHasNull, /* Register holding NULL status. See notes */
+ int *aiMap /* Mapping from Index fields to RHS fields */
+){
Select *p; /* SELECT to the right of IN operator */
int eType = 0; /* Type of RHS table. IN_INDEX_* */
int iTab = pParse->nTab++; /* Cursor of the RHS table */
- int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */
+ int mustBeUnique; /* True if RHS must be unique */
+ Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
assert( pX->op==TK_IN );
+ mustBeUnique = (inFlags & IN_INDEX_LOOP)!=0;
+
+ /* If the RHS of this IN(...) operator is a SELECT, and if it matters
+ ** whether or not the SELECT result contains NULL values, check whether
+ ** or not NULL is actually possible (it may not be, for example, due
+ ** to NOT NULL constraints in the schema). If no NULL values are possible,
+ ** set prRhsHasNull to 0 before continuing. */
+ if( prRhsHasNull && (pX->flags & EP_xIsSelect) ){
+ int i;
+ ExprList *pEList = pX->x.pSelect->pEList;
+ for(i=0; i<pEList->nExpr; i++){
+ if( sqlite3ExprCanBeNull(pEList->a[i].pExpr) ) break;
+ }
+ if( i==pEList->nExpr ){
+ prRhsHasNull = 0;
+ }
+ }
/* Check to see if an existing table or index can be used to
** satisfy the query. This is preferable to generating a new
- ** ephemeral table.
- */
- p = (ExprHasProperty(pX, EP_xIsSelect) ? pX->x.pSelect : 0);
- if( ALWAYS(pParse->nErr==0) && isCandidateForInOpt(p) ){
+ ** ephemeral table. */
+ if( pParse->nErr==0 && (p = isCandidateForInOpt(pX))!=0 ){
sqlite3 *db = pParse->db; /* Database connection */
- Expr *pExpr = p->pEList->a[0].pExpr; /* Expression <column> */
- int iCol = pExpr->iColumn; /* Index of column <column> */
- Vdbe *v = sqlite3GetVdbe(pParse); /* Virtual machine being coded */
- Table *pTab = p->pSrc->a[0].pTab; /* Table <table>. */
- int iDb; /* Database idx for pTab */
-
- /* Code an OP_VerifyCookie and OP_TableLock for <table>. */
+ Table *pTab; /* Table <table>. */
+ i16 iDb; /* Database idx for pTab */
+ ExprList *pEList = p->pEList;
+ int nExpr = pEList->nExpr;
+
+ assert( p->pEList!=0 ); /* Because of isCandidateForInOpt(p) */
+ assert( p->pEList->a[0].pExpr!=0 ); /* Because of isCandidateForInOpt(p) */
+ assert( p->pSrc!=0 ); /* Because of isCandidateForInOpt(p) */
+ pTab = p->pSrc->a[0].pTab;
+
+ /* Code an OP_Transaction and OP_TableLock for <table>. */
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
- /* This function is only called from two places. In both cases the vdbe
- ** has already been allocated. So assume sqlite3GetVdbe() is always
- ** successful here.
- */
- assert(v);
- if( iCol<0 ){
- int iMem = ++pParse->nMem;
- int iAddr;
-
- iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+ assert(v); /* sqlite3GetVdbe() has always been previously called */
+ if( nExpr==1 && pEList->a[0].pExpr->iColumn<0 ){
+ /* The "x IN (SELECT rowid FROM table)" case */
+ int iAddr = sqlite3VdbeAddOp0(v, OP_Once);
+ VdbeCoverage(v);
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
eType = IN_INDEX_ROWID;
@@ -74490,70 +91871,202 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
sqlite3VdbeJumpHere(v, iAddr);
}else{
Index *pIdx; /* Iterator variable */
+ int affinity_ok = 1;
+ int i;
- /* The collation sequence used by the comparison. If an index is to
- ** be used in place of a temp-table, it must be ordered according
- ** to this collation sequence. */
- CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pExpr);
-
- /* Check that the affinity that will be used to perform the
- ** comparison is the same as the affinity of the column. If
- ** it is not, it is not possible to use any index.
- */
- char aff = comparisonAffinity(pX);
- int affinity_ok = (pTab->aCol[iCol].affinity==aff||aff==SQLITE_AFF_NONE);
+ /* Check that the affinity that will be used to perform each
+ ** comparison is the same as the affinity of each column in table
+ ** on the RHS of the IN operator. If it not, it is not possible to
+ ** use any index of the RHS table. */
+ for(i=0; i<nExpr && affinity_ok; i++){
+ Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
+ int iCol = pEList->a[i].pExpr->iColumn;
+ char idxaff = sqlite3TableColumnAffinity(pTab,iCol); /* RHS table */
+ char cmpaff = sqlite3CompareAffinity(pLhs, idxaff);
+ testcase( cmpaff==SQLITE_AFF_BLOB );
+ testcase( cmpaff==SQLITE_AFF_TEXT );
+ switch( cmpaff ){
+ case SQLITE_AFF_BLOB:
+ break;
+ case SQLITE_AFF_TEXT:
+ /* sqlite3CompareAffinity() only returns TEXT if one side or the
+ ** other has no affinity and the other side is TEXT. Hence,
+ ** the only way for cmpaff to be TEXT is for idxaff to be TEXT
+ ** and for the term on the LHS of the IN to have no affinity. */
+ assert( idxaff==SQLITE_AFF_TEXT );
+ break;
+ default:
+ affinity_ok = sqlite3IsNumericAffinity(idxaff);
+ }
+ }
- for(pIdx=pTab->pIndex; pIdx && eType==0 && affinity_ok; pIdx=pIdx->pNext){
- if( (pIdx->aiColumn[0]==iCol)
- && sqlite3FindCollSeq(db, ENC(db), pIdx->azColl[0], 0)==pReq
- && (!mustBeUnique || (pIdx->nColumn==1 && pIdx->onError!=OE_None))
- ){
- int iMem = ++pParse->nMem;
- int iAddr;
- char *pKey;
+ if( affinity_ok ){
+ /* Search for an existing index that will work for this IN operator */
+ for(pIdx=pTab->pIndex; pIdx && eType==0; pIdx=pIdx->pNext){
+ Bitmask colUsed; /* Columns of the index used */
+ Bitmask mCol; /* Mask for the current column */
+ if( pIdx->nColumn<nExpr ) continue;
+ /* Maximum nColumn is BMS-2, not BMS-1, so that we can compute
+ ** BITMASK(nExpr) without overflowing */
+ testcase( pIdx->nColumn==BMS-2 );
+ testcase( pIdx->nColumn==BMS-1 );
+ if( pIdx->nColumn>=BMS-1 ) continue;
+ if( mustBeUnique ){
+ if( pIdx->nKeyCol>nExpr
+ ||(pIdx->nColumn>nExpr && !IsUniqueIndex(pIdx))
+ ){
+ continue; /* This index is not unique over the IN RHS columns */
+ }
+ }
- pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx);
- iAddr = sqlite3VdbeAddOp1(v, OP_Once, iMem);
+ colUsed = 0; /* Columns of index used so far */
+ for(i=0; i<nExpr; i++){
+ Expr *pLhs = sqlite3VectorFieldSubexpr(pX->pLeft, i);
+ Expr *pRhs = pEList->a[i].pExpr;
+ CollSeq *pReq = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
+ int j;
- sqlite3VdbeAddOp4(v, OP_OpenRead, iTab, pIdx->tnum, iDb,
- pKey,P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIdx->zName));
- eType = IN_INDEX_INDEX;
-
- sqlite3VdbeJumpHere(v, iAddr);
- if( prNotFound && !pTab->aCol[iCol].notNull ){
- *prNotFound = ++pParse->nMem;
+ assert( pReq!=0 || pRhs->iColumn==XN_ROWID || pParse->nErr );
+ for(j=0; j<nExpr; j++){
+ if( pIdx->aiColumn[j]!=pRhs->iColumn ) continue;
+ assert( pIdx->azColl[j] );
+ if( pReq!=0 && sqlite3StrICmp(pReq->zName, pIdx->azColl[j])!=0 ){
+ continue;
+ }
+ break;
+ }
+ if( j==nExpr ) break;
+ mCol = MASKBIT(j);
+ if( mCol & colUsed ) break; /* Each column used only once */
+ colUsed |= mCol;
+ if( aiMap ) aiMap[i] = j;
}
- }
- }
- }
+
+ assert( i==nExpr || colUsed!=(MASKBIT(nExpr)-1) );
+ if( colUsed==(MASKBIT(nExpr)-1) ){
+ /* If we reach this point, that means the index pIdx is usable */
+ int iAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+#ifndef SQLITE_OMIT_EXPLAIN
+ sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
+ sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName),
+ P4_DYNAMIC);
+#endif
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ VdbeComment((v, "%s", pIdx->zName));
+ assert( IN_INDEX_INDEX_DESC == IN_INDEX_INDEX_ASC+1 );
+ eType = IN_INDEX_INDEX_ASC + pIdx->aSortOrder[0];
+
+ if( prRhsHasNull ){
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ i64 mask = (1<<nExpr)-1;
+ sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed,
+ iTab, 0, 0, (u8*)&mask, P4_INT64);
+#endif
+ *prRhsHasNull = ++pParse->nMem;
+ if( nExpr==1 ){
+ sqlite3SetHasNullFlag(v, iTab, *prRhsHasNull);
+ }
+ }
+ sqlite3VdbeJumpHere(v, iAddr);
+ }
+ } /* End loop over indexes */
+ } /* End if( affinity_ok ) */
+ } /* End if not an rowid index */
+ } /* End attempt to optimize using an index */
+
+ /* If no preexisting index is available for the IN clause
+ ** and IN_INDEX_NOOP is an allowed reply
+ ** and the RHS of the IN operator is a list, not a subquery
+ ** and the RHS is not constant or has two or fewer terms,
+ ** then it is not worth creating an ephemeral table to evaluate
+ ** the IN operator so return IN_INDEX_NOOP.
+ */
+ if( eType==0
+ && (inFlags & IN_INDEX_NOOP_OK)
+ && !ExprHasProperty(pX, EP_xIsSelect)
+ && (!sqlite3InRhsIsConstant(pX) || pX->x.pList->nExpr<=2)
+ ){
+ eType = IN_INDEX_NOOP;
}
if( eType==0 ){
- /* Could not found an existing table or index to use as the RHS b-tree.
+ /* Could not find an existing table or index to use as the RHS b-tree.
** We will have to generate an ephemeral table to do the job.
*/
- double savedNQueryLoop = pParse->nQueryLoop;
+ u32 savedNQueryLoop = pParse->nQueryLoop;
int rMayHaveNull = 0;
eType = IN_INDEX_EPH;
- if( prNotFound ){
- *prNotFound = rMayHaveNull = ++pParse->nMem;
- }else{
- testcase( pParse->nQueryLoop>(double)1 );
- pParse->nQueryLoop = (double)1;
- if( pX->pLeft->iColumn<0 && !ExprHasAnyProperty(pX, EP_xIsSelect) ){
+ if( inFlags & IN_INDEX_LOOP ){
+ pParse->nQueryLoop = 0;
+ if( pX->pLeft->iColumn<0 && !ExprHasProperty(pX, EP_xIsSelect) ){
eType = IN_INDEX_ROWID;
}
+ }else if( prRhsHasNull ){
+ *prRhsHasNull = rMayHaveNull = ++pParse->nMem;
}
sqlite3CodeSubselect(pParse, pX, rMayHaveNull, eType==IN_INDEX_ROWID);
pParse->nQueryLoop = savedNQueryLoop;
}else{
pX->iTable = iTab;
}
+
+ if( aiMap && eType!=IN_INDEX_INDEX_ASC && eType!=IN_INDEX_INDEX_DESC ){
+ int i, n;
+ n = sqlite3ExprVectorSize(pX->pLeft);
+ for(i=0; i<n; i++) aiMap[i] = i;
+ }
return eType;
}
#endif
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Argument pExpr is an (?, ?...) IN(...) expression. This
+** function allocates and returns a nul-terminated string containing
+** the affinities to be used for each column of the comparison.
+**
+** It is the responsibility of the caller to ensure that the returned
+** string is eventually freed using sqlite3DbFree().
+*/
+static char *exprINAffinity(Parse *pParse, Expr *pExpr){
+ Expr *pLeft = pExpr->pLeft;
+ int nVal = sqlite3ExprVectorSize(pLeft);
+ Select *pSelect = (pExpr->flags & EP_xIsSelect) ? pExpr->x.pSelect : 0;
+ char *zRet;
+
+ assert( pExpr->op==TK_IN );
+ zRet = sqlite3DbMallocZero(pParse->db, nVal+1);
+ if( zRet ){
+ int i;
+ for(i=0; i<nVal; i++){
+ Expr *pA = sqlite3VectorFieldSubexpr(pLeft, i);
+ char a = sqlite3ExprAffinity(pA);
+ if( pSelect ){
+ zRet[i] = sqlite3CompareAffinity(pSelect->pEList->a[i].pExpr, a);
+ }else{
+ zRet[i] = a;
+ }
+ }
+ zRet[nVal] = '\0';
+ }
+ return zRet;
+}
+#endif
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
+** Load the Parse object passed as the first argument with an error
+** message of the form:
+**
+** "sub-select returns N columns - expected M"
+*/
+SQLITE_PRIVATE void sqlite3SubselectError(Parse *pParse, int nActual, int nExpect){
+ const char *zFmt = "sub-select returns %d columns - expected %d";
+ sqlite3ErrorMsg(pParse, zFmt, nActual, nExpect);
+}
+#endif
+
/*
** Generate code for scalar subqueries used as a subquery expression, EXISTS,
** or IN operators. Examples:
@@ -74574,34 +92087,30 @@ SQLITE_PRIVATE int sqlite3FindInIndex(Parse *pParse, Expr *pX, int *prNotFound){
**
** If rMayHaveNull is non-zero, that means that the operation is an IN
** (not a SELECT or EXISTS) and that the RHS might contains NULLs.
-** Furthermore, the IN is in a WHERE clause and that we really want
-** to iterate over the RHS of the IN operator in order to quickly locate
-** all corresponding LHS elements. All this routine does is initialize
-** the register given by rMayHaveNull to NULL. Calling routines will take
-** care of changing this register value to non-NULL if the RHS is NULL-free.
-**
-** If rMayHaveNull is zero, that means that the subquery is being used
-** for membership testing only. There is no need to initialize any
-** registers to indicate the presense or absence of NULLs on the RHS.
+** All this routine does is initialize the register given by rMayHaveNull
+** to NULL. Calling routines will take care of changing this register
+** value to non-NULL if the RHS is NULL-free.
**
** For a SELECT or EXISTS operator, return the register that holds the
-** result. For IN operators or if an error occurs, the return value is 0.
+** result. For a multi-column SELECT, the result is stored in a contiguous
+** array of registers and the return value is the register of the left-most
+** result column. Return 0 for IN operators or if an error occurs.
*/
#ifndef SQLITE_OMIT_SUBQUERY
SQLITE_PRIVATE int sqlite3CodeSubselect(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* The IN, SELECT, or EXISTS operator */
- int rMayHaveNull, /* Register that records whether NULLs exist in RHS */
+ int rHasNullFlag, /* Register that records whether NULLs exist in RHS */
int isRowid /* If true, LHS of IN operator is a rowid */
){
- int testAddr = -1; /* One-time test address */
+ int jmpIfDynamic = -1; /* One-time test address */
int rReg = 0; /* Register storing resulting */
Vdbe *v = sqlite3GetVdbe(pParse);
if( NEVER(v==0) ) return 0;
sqlite3ExprCachePush(pParse);
- /* This code must be run in its entirety every time it is encountered
- ** if any of the following is true:
+ /* The evaluation of the IN/EXISTS/SELECT must be repeated every time it
+ ** is encountered if any of the following is true:
**
** * The right-hand side is a correlated subquery
** * The right-hand side is an expression list containing variables
@@ -74610,16 +92119,16 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** If all of the above are false, then we can run this code just once
** save the results, and reuse the same result on subsequent invocations.
*/
- if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){
- int mem = ++pParse->nMem;
- testAddr = sqlite3VdbeAddOp1(v, OP_Once, mem);
+ if( !ExprHasProperty(pExpr, EP_VarSelect) ){
+ jmpIfDynamic = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
}
#ifndef SQLITE_OMIT_EXPLAIN
if( pParse->explain==2 ){
- char *zMsg = sqlite3MPrintf(
- pParse->db, "EXECUTE %s%s SUBQUERY %d", testAddr>=0?"":"CORRELATED ",
- pExpr->op==TK_IN?"LIST":"SCALAR", pParse->iNextSelectId
+ char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %s%s SUBQUERY %d",
+ jmpIfDynamic>=0?"":"CORRELATED ",
+ pExpr->op==TK_IN?"LIST":"SCALAR",
+ pParse->iNextSelectId
);
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
}
@@ -74627,21 +92136,18 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
switch( pExpr->op ){
case TK_IN: {
- char affinity; /* Affinity of the LHS of the IN */
- KeyInfo keyInfo; /* Keyinfo for the generated table */
int addr; /* Address of OP_OpenEphemeral instruction */
Expr *pLeft = pExpr->pLeft; /* the LHS of the IN operator */
-
- if( rMayHaveNull ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, rMayHaveNull);
- }
-
- affinity = sqlite3ExprAffinity(pLeft);
+ KeyInfo *pKeyInfo = 0; /* Key information */
+ int nVal; /* Size of vector pLeft */
+
+ nVal = sqlite3ExprVectorSize(pLeft);
+ assert( !isRowid || nVal==1 );
/* Whether this is an 'x IN(SELECT...)' or an 'x IN(<exprlist>)'
** expression it is handled the same way. An ephemeral table is
- ** filled with single-field index keys representing the results
- ** from the SELECT or the <exprlist>.
+ ** filled with index keys representing the results from the
+ ** SELECT or the <exprlist>.
**
** If the 'x' expression is a column value, or the SELECT...
** statement returns a column value, then the affinity of that
@@ -74652,10 +92158,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** is used.
*/
pExpr->iTable = pParse->nTab++;
- addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pExpr->iTable, !isRowid);
- if( rMayHaveNull==0 ) sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
- memset(&keyInfo, 0, sizeof(keyInfo));
- keyInfo.nField = 1;
+ addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral,
+ pExpr->iTable, (isRowid?0:nVal));
+ pKeyInfo = isRowid ? 0 : sqlite3KeyInfoAlloc(pParse->db, nVal, 1);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
/* Case 1: expr IN (SELECT ...)
@@ -74663,21 +92168,37 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** Generate code to write the results of the select into the temporary
** table allocated and opened above.
*/
- SelectDest dest;
- ExprList *pEList;
+ Select *pSelect = pExpr->x.pSelect;
+ ExprList *pEList = pSelect->pEList;
assert( !isRowid );
- sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
- dest.affinity = (u8)affinity;
- assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
- pExpr->x.pSelect->iLimit = 0;
- if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){
- return 0;
- }
- pEList = pExpr->x.pSelect->pEList;
- if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){
- keyInfo.aColl[0] = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft,
- pEList->a[0].pExpr);
+ /* If the LHS and RHS of the IN operator do not match, that
+ ** error will have been caught long before we reach this point. */
+ if( ALWAYS(pEList->nExpr==nVal) ){
+ SelectDest dest;
+ int i;
+ sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
+ dest.zAffSdst = exprINAffinity(pParse, pExpr);
+ assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
+ pSelect->iLimit = 0;
+ testcase( pSelect->selFlags & SF_Distinct );
+ testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
+ if( sqlite3Select(pParse, pSelect, &dest) ){
+ sqlite3DbFree(pParse->db, dest.zAffSdst);
+ sqlite3KeyInfoUnref(pKeyInfo);
+ return 0;
+ }
+ sqlite3DbFree(pParse->db, dest.zAffSdst);
+ assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
+ assert( pEList!=0 );
+ assert( pEList->nExpr>0 );
+ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
+ for(i=0; i<nVal; i++){
+ Expr *p = sqlite3VectorFieldSubexpr(pLeft, i);
+ pKeyInfo->aColl[i] = sqlite3BinaryCompareCollSeq(
+ pParse, p, pEList->a[i].pExpr
+ );
+ }
}
}else if( ALWAYS(pExpr->x.pList!=0) ){
/* Case 2: expr IN (exprlist)
@@ -74687,20 +92208,25 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** that columns affinity when building index keys. If <expr> is not
** a column, use numeric affinity.
*/
+ char affinity; /* Affinity of the LHS of the IN */
int i;
ExprList *pList = pExpr->x.pList;
struct ExprList_item *pItem;
int r1, r2, r3;
+ affinity = sqlite3ExprAffinity(pLeft);
if( !affinity ){
- affinity = SQLITE_AFF_NONE;
+ affinity = SQLITE_AFF_BLOB;
+ }
+ if( pKeyInfo ){
+ assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
+ pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
}
- keyInfo.aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
/* Loop through each expression in <exprlist>. */
r1 = sqlite3GetTempReg(pParse);
r2 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
+ if( isRowid ) sqlite3VdbeAddOp2(v, OP_Null, 0, r2);
for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){
Expr *pE2 = pItem->pExpr;
int iValToIns;
@@ -74710,9 +92236,9 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
** this code only executes once. Because for a non-constant
** expression we need to rerun this code each time.
*/
- if( testAddr>=0 && !sqlite3ExprIsConstant(pE2) ){
- sqlite3VdbeChangeToNoop(v, testAddr);
- testAddr = -1;
+ if( jmpIfDynamic>=0 && !sqlite3ExprIsConstant(pE2) ){
+ sqlite3VdbeChangeToNoop(v, jmpIfDynamic);
+ jmpIfDynamic = -1;
}
/* Evaluate the expression and insert it into the temp table */
@@ -74723,6 +92249,7 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
if( isRowid ){
sqlite3VdbeAddOp2(v, OP_MustBeInt, r3,
sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3);
}else{
sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1);
@@ -74734,8 +92261,8 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
}
- if( !isRowid ){
- sqlite3VdbeChangeP4(v, addr, (void *)&keyInfo, P4_KEYINFO);
+ if( pKeyInfo ){
+ sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
}
break;
}
@@ -74743,48 +92270,65 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
case TK_EXISTS:
case TK_SELECT:
default: {
- /* If this has to be a scalar SELECT. Generate code to put the
- ** value of this select in a memory cell and record the number
- ** of the memory cell in iColumn. If this is an EXISTS, write
- ** an integer 0 (not exists) or 1 (exists) into a memory cell
- ** and record that memory cell in iColumn.
+ /* Case 3: (SELECT ... FROM ...)
+ ** or: EXISTS(SELECT ... FROM ...)
+ **
+ ** For a SELECT, generate code to put the values for all columns of
+ ** the first row into an array of registers and return the index of
+ ** the first register.
+ **
+ ** If this is an EXISTS, write an integer 0 (not exists) or 1 (exists)
+ ** into a register and return that register number.
+ **
+ ** In both cases, the query is augmented with "LIMIT 1". Any
+ ** preexisting limit is discarded in place of the new LIMIT 1.
*/
Select *pSel; /* SELECT statement to encode */
- SelectDest dest; /* How to deal with SELECt result */
+ SelectDest dest; /* How to deal with SELECT result */
+ int nReg; /* Registers to allocate */
testcase( pExpr->op==TK_EXISTS );
testcase( pExpr->op==TK_SELECT );
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
-
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
+
pSel = pExpr->x.pSelect;
- sqlite3SelectDestInit(&dest, 0, ++pParse->nMem);
+ nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
+ sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
+ pParse->nMem += nReg;
if( pExpr->op==TK_SELECT ){
dest.eDest = SRT_Mem;
- sqlite3VdbeAddOp2(v, OP_Null, 0, dest.iParm);
+ dest.iSdst = dest.iSDParm;
+ dest.nSdst = nReg;
+ sqlite3VdbeAddOp3(v, OP_Null, 0, dest.iSDParm, dest.iSDParm+nReg-1);
VdbeComment((v, "Init subquery result"));
}else{
dest.eDest = SRT_Exists;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iParm);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, dest.iSDParm);
VdbeComment((v, "Init EXISTS result"));
}
sqlite3ExprDelete(pParse->db, pSel->pLimit);
- pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0,
- &sqlite3IntTokens[1]);
+ pSel->pLimit = sqlite3ExprAlloc(pParse->db, TK_INTEGER,
+ &sqlite3IntTokens[1], 0);
pSel->iLimit = 0;
+ pSel->selFlags &= ~SF_MultiValue;
if( sqlite3Select(pParse, pSel, &dest) ){
return 0;
}
- rReg = dest.iParm;
- ExprSetIrreducible(pExpr);
+ rReg = dest.iSDParm;
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
break;
}
}
- if( testAddr>=0 ){
- sqlite3VdbeJumpHere(v, testAddr);
+ if( rHasNullFlag ){
+ sqlite3SetHasNullFlag(v, pExpr->iTable, rHasNullFlag);
+ }
+
+ if( jmpIfDynamic>=0 ){
+ sqlite3VdbeJumpHere(v, jmpIfDynamic);
}
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
return rReg;
}
@@ -74792,21 +92336,55 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
#ifndef SQLITE_OMIT_SUBQUERY
/*
+** Expr pIn is an IN(...) expression. This function checks that the
+** sub-select on the RHS of the IN() operator has the same number of
+** columns as the vector on the LHS. Or, if the RHS of the IN() is not
+** a sub-query, that the LHS is a vector of size 1.
+*/
+SQLITE_PRIVATE int sqlite3ExprCheckIN(Parse *pParse, Expr *pIn){
+ int nVector = sqlite3ExprVectorSize(pIn->pLeft);
+ if( (pIn->flags & EP_xIsSelect) ){
+ if( nVector!=pIn->x.pSelect->pEList->nExpr ){
+ sqlite3SubselectError(pParse, pIn->x.pSelect->pEList->nExpr, nVector);
+ return 1;
+ }
+ }else if( nVector!=1 ){
+ if( (pIn->pLeft->flags & EP_xIsSelect) ){
+ sqlite3SubselectError(pParse, nVector, 1);
+ }else{
+ sqlite3ErrorMsg(pParse, "row value misused");
+ }
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+#ifndef SQLITE_OMIT_SUBQUERY
+/*
** Generate code for an IN expression.
**
** x IN (SELECT ...)
** x IN (value, value, ...)
**
-** The left-hand side (LHS) is a scalar expression. The right-hand side (RHS)
-** is an array of zero or more values. The expression is true if the LHS is
-** contained within the RHS. The value of the expression is unknown (NULL)
-** if the LHS is NULL or if the LHS is not contained within the RHS and the
-** RHS contains one or more NULL values.
+** The left-hand side (LHS) is a scalar or vector expression. The
+** right-hand side (RHS) is an array of zero or more scalar values, or a
+** subquery. If the RHS is a subquery, the number of result columns must
+** match the number of columns in the vector on the LHS. If the RHS is
+** a list of values, the LHS must be a scalar.
+**
+** The IN operator is true if the LHS value is contained within the RHS.
+** The result is false if the LHS is definitely not in the RHS. The
+** result is NULL if the presence of the LHS in the RHS cannot be
+** determined due to NULLs.
**
-** This routine generates code will jump to destIfFalse if the LHS is not
+** This routine generates code that jumps to destIfFalse if the LHS is not
** contained within the RHS. If due to NULLs we cannot determine if the LHS
** is contained in the RHS then jump to destIfNull. If the LHS is contained
** within the RHS then fall through.
+**
+** See the separate in-operator.md documentation file in the canonical
+** SQLite source tree for additional information.
*/
static void sqlite3ExprCodeIN(
Parse *pParse, /* Parsing and code generating context */
@@ -74815,126 +92393,231 @@ static void sqlite3ExprCodeIN(
int destIfNull /* Jump here if the results are unknown due to NULLs */
){
int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
- char affinity; /* Comparison affinity to use */
int eType; /* Type of the RHS */
- int r1; /* Temporary use register */
+ int rLhs; /* Register(s) holding the LHS values */
+ int rLhsOrig; /* LHS values prior to reordering by aiMap[] */
Vdbe *v; /* Statement under construction */
+ int *aiMap = 0; /* Map from vector field to index column */
+ char *zAff = 0; /* Affinity string for comparisons */
+ int nVector; /* Size of vectors for this IN operator */
+ int iDummy; /* Dummy parameter to exprCodeVector() */
+ Expr *pLeft; /* The LHS of the IN operator */
+ int i; /* loop counter */
+ int destStep2; /* Where to jump when NULLs seen in step 2 */
+ int destStep6 = 0; /* Start of code for Step 6 */
+ int addrTruthOp; /* Address of opcode that determines the IN is true */
+ int destNotNull; /* Jump here if a comparison is not true in step 6 */
+ int addrTop; /* Top of the step-6 loop */
+
+ pLeft = pExpr->pLeft;
+ if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
+ zAff = exprINAffinity(pParse, pExpr);
+ nVector = sqlite3ExprVectorSize(pExpr->pLeft);
+ aiMap = (int*)sqlite3DbMallocZero(
+ pParse->db, nVector*(sizeof(int) + sizeof(char)) + 1
+ );
+ if( pParse->db->mallocFailed ) goto sqlite3ExprCodeIN_oom_error;
- /* Compute the RHS. After this step, the table with cursor
- ** pExpr->iTable will contains the values that make up the RHS.
- */
+ /* Attempt to compute the RHS. After this step, if anything other than
+ ** IN_INDEX_NOOP is returned, the table opened ith cursor pExpr->iTable
+ ** contains the values that make up the RHS. If IN_INDEX_NOOP is returned,
+ ** the RHS has not yet been coded. */
v = pParse->pVdbe;
assert( v!=0 ); /* OOM detected prior to this routine */
VdbeNoopComment((v, "begin IN expr"));
- eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull);
+ eType = sqlite3FindInIndex(pParse, pExpr,
+ IN_INDEX_MEMBERSHIP | IN_INDEX_NOOP_OK,
+ destIfFalse==destIfNull ? 0 : &rRhsHasNull, aiMap);
- /* Figure out the affinity to use to create a key from the results
- ** of the expression. affinityStr stores a static string suitable for
- ** P4 of OP_MakeRecord.
- */
- affinity = comparisonAffinity(pExpr);
+ assert( pParse->nErr || nVector==1 || eType==IN_INDEX_EPH
+ || eType==IN_INDEX_INDEX_ASC || eType==IN_INDEX_INDEX_DESC
+ );
+#ifdef SQLITE_DEBUG
+ /* Confirm that aiMap[] contains nVector integer values between 0 and
+ ** nVector-1. */
+ for(i=0; i<nVector; i++){
+ int j, cnt;
+ for(cnt=j=0; j<nVector; j++) if( aiMap[j]==i ) cnt++;
+ assert( cnt==1 );
+ }
+#endif
- /* Code the LHS, the <expr> from "<expr> IN (...)".
+ /* Code the LHS, the <expr> from "<expr> IN (...)". If the LHS is a
+ ** vector, then it is stored in an array of nVector registers starting
+ ** at r1.
+ **
+ ** sqlite3FindInIndex() might have reordered the fields of the LHS vector
+ ** so that the fields are in the same order as an existing index. The
+ ** aiMap[] array contains a mapping from the original LHS field order to
+ ** the field order that matches the RHS index.
*/
sqlite3ExprCachePush(pParse);
- r1 = sqlite3GetTempReg(pParse);
- sqlite3ExprCode(pParse, pExpr->pLeft, r1);
+ rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
+ for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
+ if( i==nVector ){
+ /* LHS fields are not reordered */
+ rLhs = rLhsOrig;
+ }else{
+ /* Need to reorder the LHS fields according to aiMap */
+ rLhs = sqlite3GetTempRange(pParse, nVector);
+ for(i=0; i<nVector; i++){
+ sqlite3VdbeAddOp3(v, OP_Copy, rLhsOrig+i, rLhs+aiMap[i], 0);
+ }
+ }
- /* If the LHS is NULL, then the result is either false or NULL depending
- ** on whether the RHS is empty or not, respectively.
+ /* If sqlite3FindInIndex() did not find or create an index that is
+ ** suitable for evaluating the IN operator, then evaluate using a
+ ** sequence of comparisons.
+ **
+ ** This is step (1) in the in-operator.md optimized algorithm.
+ */
+ if( eType==IN_INDEX_NOOP ){
+ ExprList *pList = pExpr->x.pList;
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ int labelOk = sqlite3VdbeMakeLabel(v);
+ int r2, regToFree;
+ int regCkNull = 0;
+ int ii;
+ assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
+ if( destIfNull!=destIfFalse ){
+ regCkNull = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
+ }
+ for(ii=0; ii<pList->nExpr; ii++){
+ r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
+ if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
+ sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
+ }
+ if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){
+ sqlite3VdbeAddOp4(v, OP_Eq, rLhs, labelOk, r2,
+ (void*)pColl, P4_COLLSEQ);
+ VdbeCoverageIf(v, ii<pList->nExpr-1);
+ VdbeCoverageIf(v, ii==pList->nExpr-1);
+ sqlite3VdbeChangeP5(v, zAff[0]);
+ }else{
+ assert( destIfNull==destIfFalse );
+ sqlite3VdbeAddOp4(v, OP_Ne, rLhs, destIfFalse, r2,
+ (void*)pColl, P4_COLLSEQ); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, zAff[0] | SQLITE_JUMPIFNULL);
+ }
+ sqlite3ReleaseTempReg(pParse, regToFree);
+ }
+ if( regCkNull ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regCkNull, destIfNull); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, destIfFalse);
+ }
+ sqlite3VdbeResolveLabel(v, labelOk);
+ sqlite3ReleaseTempReg(pParse, regCkNull);
+ goto sqlite3ExprCodeIN_finished;
+ }
+
+ /* Step 2: Check to see if the LHS contains any NULL columns. If the
+ ** LHS does contain NULLs then the result must be either FALSE or NULL.
+ ** We will then skip the binary search of the RHS.
*/
if( destIfNull==destIfFalse ){
- /* Shortcut for the common case where the false and NULL outcomes are
- ** the same. */
- sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull);
+ destStep2 = destIfFalse;
}else{
- int addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, r1);
- sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull);
- sqlite3VdbeJumpHere(v, addr1);
+ destStep2 = destStep6 = sqlite3VdbeMakeLabel(v);
+ }
+ for(i=0; i<nVector; i++){
+ Expr *p = sqlite3VectorFieldSubexpr(pExpr->pLeft, i);
+ if( sqlite3ExprCanBeNull(p) ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, rLhs+i, destStep2);
+ VdbeCoverage(v);
+ }
}
+ /* Step 3. The LHS is now known to be non-NULL. Do the binary search
+ ** of the RHS using the LHS as a probe. If found, the result is
+ ** true.
+ */
if( eType==IN_INDEX_ROWID ){
- /* In this case, the RHS is the ROWID of table b-tree
- */
- sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse);
- sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1);
+ /* In this case, the RHS is the ROWID of table b-tree and so we also
+ ** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
+ ** into a single opcode. */
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, pExpr->iTable, destIfFalse, rLhs);
+ VdbeCoverage(v);
+ addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
}else{
- /* In this case, the RHS is an index b-tree.
- */
- sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1);
-
- /* If the set membership test fails, then the result of the
- ** "x IN (...)" expression must be either 0 or NULL. If the set
- ** contains no NULL values, then the result is 0. If the set
- ** contains one or more NULL values, then the result of the
- ** expression is also NULL.
- */
- if( rRhsHasNull==0 || destIfFalse==destIfNull ){
- /* This branch runs if it is known at compile time that the RHS
- ** cannot contain NULL values. This happens as the result
- ** of a "NOT NULL" constraint in the database schema.
- **
- ** Also run this branch if NULL is equivalent to FALSE
- ** for this particular IN operator.
- */
- sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1);
+ sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
+ if( destIfFalse==destIfNull ){
+ /* Combine Step 3 and Step 5 into a single opcode */
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse,
+ rLhs, nVector); VdbeCoverage(v);
+ goto sqlite3ExprCodeIN_finished;
+ }
+ /* Ordinary Step 3, for the case where FALSE and NULL are distinct */
+ addrTruthOp = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0,
+ rLhs, nVector); VdbeCoverage(v);
+ }
- }else{
- /* In this branch, the RHS of the IN might contain a NULL and
- ** the presence of a NULL on the RHS makes a difference in the
- ** outcome.
- */
- int j1, j2, j3;
+ /* Step 4. If the RHS is known to be non-NULL and we did not find
+ ** an match on the search above, then the result must be FALSE.
+ */
+ if( rRhsHasNull && nVector==1 ){
+ sqlite3VdbeAddOp2(v, OP_NotNull, rRhsHasNull, destIfFalse);
+ VdbeCoverage(v);
+ }
- /* First check to see if the LHS is contained in the RHS. If so,
- ** then the presence of NULLs in the RHS does not matter, so jump
- ** over all of the code that follows.
- */
- j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1);
+ /* Step 5. If we do not care about the difference between NULL and
+ ** FALSE, then just return false.
+ */
+ if( destIfFalse==destIfNull ) sqlite3VdbeGoto(v, destIfFalse);
- /* Here we begin generating code that runs if the LHS is not
- ** contained within the RHS. Generate additional code that
- ** tests the RHS for NULLs. If the RHS contains a NULL then
- ** jump to destIfNull. If there are no NULLs in the RHS then
- ** jump to destIfFalse.
- */
- j2 = sqlite3VdbeAddOp1(v, OP_NotNull, rRhsHasNull);
- j3 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, rRhsHasNull);
- sqlite3VdbeJumpHere(v, j3);
- sqlite3VdbeAddOp2(v, OP_AddImm, rRhsHasNull, 1);
- sqlite3VdbeJumpHere(v, j2);
-
- /* Jump to the appropriate target depending on whether or not
- ** the RHS contains a NULL
- */
- sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
+ /* Step 6: Loop through rows of the RHS. Compare each row to the LHS.
+ ** If any comparison is NULL, then the result is NULL. If all
+ ** comparisons are FALSE then the final result is FALSE.
+ **
+ ** For a scalar LHS, it is sufficient to check just the first row
+ ** of the RHS.
+ */
+ if( destStep6 ) sqlite3VdbeResolveLabel(v, destStep6);
+ addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, pExpr->iTable, destIfFalse);
+ VdbeCoverage(v);
+ if( nVector>1 ){
+ destNotNull = sqlite3VdbeMakeLabel(v);
+ }else{
+ /* For nVector==1, combine steps 6 and 7 by immediately returning
+ ** FALSE if the first comparison is not NULL */
+ destNotNull = destIfFalse;
+ }
+ for(i=0; i<nVector; i++){
+ Expr *p;
+ CollSeq *pColl;
+ int r3 = sqlite3GetTempReg(pParse);
+ p = sqlite3VectorFieldSubexpr(pLeft, i);
+ pColl = sqlite3ExprCollSeq(pParse, p);
+ sqlite3VdbeAddOp3(v, OP_Column, pExpr->iTable, i, r3);
+ sqlite3VdbeAddOp4(v, OP_Ne, rLhs+i, destNotNull, r3,
+ (void*)pColl, P4_COLLSEQ);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, r3);
+ }
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfNull);
+ if( nVector>1 ){
+ sqlite3VdbeResolveLabel(v, destNotNull);
+ sqlite3VdbeAddOp2(v, OP_Next, pExpr->iTable, addrTop+1);
+ VdbeCoverage(v);
- /* The OP_Found at the top of this branch jumps here when true,
- ** causing the overall IN expression evaluation to fall through.
- */
- sqlite3VdbeJumpHere(v, j1);
- }
+ /* Step 7: If we reach this point, we know that the result must
+ ** be false. */
+ sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
}
- sqlite3ReleaseTempReg(pParse, r1);
- sqlite3ExprCachePop(pParse, 1);
+
+ /* Jumps here in order to return true. */
+ sqlite3VdbeJumpHere(v, addrTruthOp);
+
+sqlite3ExprCodeIN_finished:
+ if( rLhs!=rLhsOrig ) sqlite3ReleaseTempReg(pParse, rLhs);
+ sqlite3ExprCachePop(pParse);
VdbeComment((v, "end IN expr"));
+sqlite3ExprCodeIN_oom_error:
+ sqlite3DbFree(pParse->db, aiMap);
+ sqlite3DbFree(pParse->db, zAff);
}
#endif /* SQLITE_OMIT_SUBQUERY */
-/*
-** Duplicate an 8-byte value
-*/
-static char *dup8bytes(Vdbe *v, const char *in){
- char *out = sqlite3DbMallocRaw(sqlite3VdbeDb(v), 8);
- if( out ){
- memcpy(out, in, 8);
- }
- return out;
-}
-
#ifndef SQLITE_OMIT_FLOATING_POINT
/*
** Generate an instruction that will put the floating point
@@ -74947,12 +92630,10 @@ static char *dup8bytes(Vdbe *v, const char *in){
static void codeReal(Vdbe *v, const char *z, int negateFlag, int iMem){
if( ALWAYS(z!=0) ){
double value;
- char *zV;
sqlite3AtoF(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */
if( negateFlag ) value = -value;
- zV = dup8bytes(v, (char*)&value);
- sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL);
+ sqlite3VdbeAddOp4Dup8(v, OP_Real, 0, iMem, 0, (u8*)&value, P4_REAL);
}
}
#endif
@@ -74976,31 +92657,39 @@ static void codeInteger(Parse *pParse, Expr *pExpr, int negFlag, int iMem){
i64 value;
const char *z = pExpr->u.zToken;
assert( z!=0 );
- c = sqlite3Atoi64(z, &value, sqlite3Strlen30(z), SQLITE_UTF8);
+ c = sqlite3DecOrHexToI64(z, &value);
if( c==0 || (c==2 && negFlag) ){
- char *zV;
if( negFlag ){ value = c==2 ? SMALLEST_INT64 : -value; }
- zV = dup8bytes(v, (char*)&value);
- sqlite3VdbeAddOp4(v, OP_Int64, 0, iMem, 0, zV, P4_INT64);
+ sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, iMem, 0, (u8*)&value, P4_INT64);
}else{
#ifdef SQLITE_OMIT_FLOATING_POINT
sqlite3ErrorMsg(pParse, "oversized integer: %s%s", negFlag ? "-" : "", z);
#else
- codeReal(v, z, negFlag, iMem);
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ if( sqlite3_strnicmp(z,"0x",2)==0 ){
+ sqlite3ErrorMsg(pParse, "hex literal too big: %s", z);
+ }else
+#endif
+ {
+ codeReal(v, z, negFlag, iMem);
+ }
#endif
}
}
}
/*
-** Clear a cache entry.
+** Erase column-cache entry number i
*/
-static void cacheEntryClear(Parse *pParse, struct yColCache *p){
- if( p->tempReg ){
+static void cacheEntryClear(Parse *pParse, int i){
+ if( pParse->aColCache[i].tempReg ){
if( pParse->nTempReg<ArraySize(pParse->aTempReg) ){
- pParse->aTempReg[pParse->nTempReg++] = p->iReg;
+ pParse->aTempReg[pParse->nTempReg++] = pParse->aColCache[i].iReg;
}
- p->tempReg = 0;
+ }
+ pParse->nColCache--;
+ if( i<pParse->nColCache ){
+ pParse->aColCache[i] = pParse->aColCache[pParse->nColCache];
}
}
@@ -75015,14 +92704,15 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
int idxLru;
struct yColCache *p;
- assert( iReg>0 ); /* Register numbers are always positive */
+ /* Unless an error has occurred, register numbers are always positive. */
+ assert( iReg>0 || pParse->nErr || pParse->db->mallocFailed );
assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */
/* The SQLITE_ColumnCache flag disables the column cache. This is used
** for testing only - to verify that SQLite always gets the same answer
** with and without the column cache.
*/
- if( pParse->db->flags & SQLITE_ColumnCache ) return;
+ if( OptimizationDisabled(pParse->db, SQLITE_ColumnCache) ) return;
/* First replace any existing entry.
**
@@ -75030,52 +92720,33 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
** that the object will never already be in cache. Verify this guarantee.
*/
#ifndef NDEBUG
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
-#if 0 /* This code wold remove the entry from the cache if it existed */
- if( p->iReg && p->iTable==iTab && p->iColumn==iCol ){
- cacheEntryClear(pParse, p);
- p->iLevel = pParse->iCacheLevel;
- p->iReg = iReg;
- p->lru = pParse->iCacheCnt++;
- return;
- }
-#endif
- assert( p->iReg==0 || p->iTable!=iTab || p->iColumn!=iCol );
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
+ assert( p->iTable!=iTab || p->iColumn!=iCol );
}
#endif
- /* Find an empty slot and replace it */
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg==0 ){
- p->iLevel = pParse->iCacheLevel;
- p->iTable = iTab;
- p->iColumn = iCol;
- p->iReg = iReg;
- p->tempReg = 0;
- p->lru = pParse->iCacheCnt++;
- return;
- }
- }
-
- /* Replace the last recently used */
- minLru = 0x7fffffff;
- idxLru = -1;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->lru<minLru ){
- idxLru = i;
- minLru = p->lru;
+ /* If the cache is already full, delete the least recently used entry */
+ if( pParse->nColCache>=SQLITE_N_COLCACHE ){
+ minLru = 0x7fffffff;
+ idxLru = -1;
+ for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ if( p->lru<minLru ){
+ idxLru = i;
+ minLru = p->lru;
+ }
}
- }
- if( ALWAYS(idxLru>=0) ){
p = &pParse->aColCache[idxLru];
- p->iLevel = pParse->iCacheLevel;
- p->iTable = iTab;
- p->iColumn = iCol;
- p->iReg = iReg;
- p->tempReg = 0;
- p->lru = pParse->iCacheCnt++;
- return;
+ }else{
+ p = &pParse->aColCache[pParse->nColCache++];
}
+
+ /* Add the new entry to the end of the cache */
+ p->iLevel = pParse->iCacheLevel;
+ p->iTable = iTab;
+ p->iColumn = iCol;
+ p->iReg = iReg;
+ p->tempReg = 0;
+ p->lru = pParse->iCacheCnt++;
}
/*
@@ -75083,14 +92754,13 @@ SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse *pParse, int iTab, int iCol, int
** Purge the range of registers from the column cache.
*/
SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){
- int i;
- int iLast = iReg + nReg - 1;
- struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- int r = p->iReg;
- if( r>=iReg && r<=iLast ){
- cacheEntryClear(pParse, p);
- p->iReg = 0;
+ int i = 0;
+ while( i<pParse->nColCache ){
+ struct yColCache *p = &pParse->aColCache[i];
+ if( p->iReg >= iReg && p->iReg < iReg+nReg ){
+ cacheEntryClear(pParse, i);
+ }else{
+ i++;
}
}
}
@@ -75102,23 +92772,32 @@ SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){
*/
SQLITE_PRIVATE void sqlite3ExprCachePush(Parse *pParse){
pParse->iCacheLevel++;
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("PUSH to %d\n", pParse->iCacheLevel);
+ }
+#endif
}
/*
** Remove from the column cache any entries that were added since the
-** the previous N Push operations. In other words, restore the cache
-** to the state it was in N Pushes ago.
+** the previous sqlite3ExprCachePush operation. In other words, restore
+** the cache to the state it was in prior the most recent Push.
*/
-SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse, int N){
- int i;
- struct yColCache *p;
- assert( N>0 );
- assert( pParse->iCacheLevel>=N );
- pParse->iCacheLevel -= N;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg && p->iLevel>pParse->iCacheLevel ){
- cacheEntryClear(pParse, p);
- p->iReg = 0;
+SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse){
+ int i = 0;
+ assert( pParse->iCacheLevel>=1 );
+ pParse->iCacheLevel--;
+#ifdef SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("POP to %d\n", pParse->iCacheLevel);
+ }
+#endif
+ while( i<pParse->nColCache ){
+ if( pParse->aColCache[i].iLevel>pParse->iCacheLevel ){
+ cacheEntryClear(pParse, i);
+ }else{
+ i++;
}
}
}
@@ -75132,28 +92811,54 @@ SQLITE_PRIVATE void sqlite3ExprCachePop(Parse *pParse, int N){
static void sqlite3ExprCachePinRegister(Parse *pParse, int iReg){
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
if( p->iReg==iReg ){
p->tempReg = 0;
}
}
}
+/* Generate code that will load into register regOut a value that is
+** appropriate for the iIdxCol-th column of index pIdx.
+*/
+SQLITE_PRIVATE void sqlite3ExprCodeLoadIndexColumn(
+ Parse *pParse, /* The parsing context */
+ Index *pIdx, /* The index whose column is to be loaded */
+ int iTabCur, /* Cursor pointing to a table row */
+ int iIdxCol, /* The column of the index to be loaded */
+ int regOut /* Store the index column value in this register */
+){
+ i16 iTabCol = pIdx->aiColumn[iIdxCol];
+ if( iTabCol==XN_EXPR ){
+ assert( pIdx->aColExpr );
+ assert( pIdx->aColExpr->nExpr>iIdxCol );
+ pParse->iSelfTab = iTabCur;
+ sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[iIdxCol].pExpr, regOut);
+ }else{
+ sqlite3ExprCodeGetColumnOfTable(pParse->pVdbe, pIdx->pTable, iTabCur,
+ iTabCol, regOut);
+ }
+}
+
/*
** Generate code to extract the value of the iCol-th column of a table.
*/
SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(
Vdbe *v, /* The VDBE under construction */
Table *pTab, /* The table containing the value */
- int iTabCur, /* The cursor for this table */
+ int iTabCur, /* The table cursor. Or the PK cursor for WITHOUT ROWID */
int iCol, /* Index of the column to extract */
- int regOut /* Extract the valud into this register */
+ int regOut /* Extract the value into this register */
){
if( iCol<0 || iCol==pTab->iPKey ){
sqlite3VdbeAddOp2(v, OP_Rowid, iTabCur, regOut);
}else{
int op = IsVirtual(pTab) ? OP_VColumn : OP_Column;
- sqlite3VdbeAddOp3(v, op, iTabCur, iCol, regOut);
+ int x = iCol;
+ if( !HasRowid(pTab) && !IsVirtual(pTab) ){
+ x = sqlite3ColumnOfIndex(sqlite3PrimaryKeyIndex(pTab), iCol);
+ }
+ sqlite3VdbeAddOp3(v, op, iTabCur, x, regOut);
}
if( iCol>=0 ){
sqlite3ColumnDefault(v, pTab, iCol, regOut);
@@ -75162,9 +92867,12 @@ SQLITE_PRIVATE void sqlite3ExprCodeGetColumnOfTable(
/*
** Generate code that will extract the iColumn-th column from
-** table pTab and store the column value in a register. An effort
-** is made to store the column value in register iReg, but this is
-** not guaranteed. The location of the column value is returned.
+** table pTab and store the column value in a register.
+**
+** An effort is made to store the column value in register iReg. This
+** is not garanteeed for GetColumn() - the result can be stored in
+** any register. But the result is guaranteed to land in register iReg
+** for GetColumnToReg().
**
** There must be an open cursor to pTab in iTable when this routine
** is called. If iColumn<0 then code is generated that extracts the rowid.
@@ -75174,14 +92882,15 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
Table *pTab, /* Description of the table we are reading from */
int iColumn, /* Index of the table column */
int iTable, /* The cursor pointing to the table */
- int iReg /* Store results here */
+ int iReg, /* Store results here */
+ u8 p5 /* P5 value for OP_Column + FLAGS */
){
Vdbe *v = pParse->pVdbe;
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg>0 && p->iTable==iTable && p->iColumn==iColumn ){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
+ if( p->iTable==iTable && p->iColumn==iColumn ){
p->lru = pParse->iCacheCnt++;
sqlite3ExprCachePinRegister(pParse, p->iReg);
return p->iReg;
@@ -75189,23 +92898,44 @@ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(
}
assert( v!=0 );
sqlite3ExprCodeGetColumnOfTable(v, pTab, iTable, iColumn, iReg);
- sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
+ if( p5 ){
+ sqlite3VdbeChangeP5(v, p5);
+ }else{
+ sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg);
+ }
return iReg;
}
+SQLITE_PRIVATE void sqlite3ExprCodeGetColumnToReg(
+ Parse *pParse, /* Parsing and code generating context */
+ Table *pTab, /* Description of the table we are reading from */
+ int iColumn, /* Index of the table column */
+ int iTable, /* The cursor pointing to the table */
+ int iReg /* Store results here */
+){
+ int r1 = sqlite3ExprCodeGetColumn(pParse, pTab, iColumn, iTable, iReg, 0);
+ if( r1!=iReg ) sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, r1, iReg);
+}
+
/*
** Clear all column cache entries.
*/
SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse *pParse){
int i;
- struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- if( p->iReg ){
- cacheEntryClear(pParse, p);
- p->iReg = 0;
+#if SQLITE_DEBUG
+ if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
+ printf("CLEAR\n");
+ }
+#endif
+ for(i=0; i<pParse->nColCache; i++){
+ if( pParse->aColCache[i].tempReg
+ && pParse->nTempReg<ArraySize(pParse->aTempReg)
+ ){
+ pParse->aTempReg[pParse->nTempReg++] = pParse->aColCache[i].iReg;
}
}
+ pParse->nColCache = 0;
}
/*
@@ -75221,28 +92951,9 @@ SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, in
** over to iTo..iTo+nReg-1. Keep the column cache up-to-date.
*/
SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse *pParse, int iFrom, int iTo, int nReg){
- int i;
- struct yColCache *p;
- if( NEVER(iFrom==iTo) ) return;
+ assert( iFrom>=iTo+nReg || iFrom+nReg<=iTo );
sqlite3VdbeAddOp3(pParse->pVdbe, OP_Move, iFrom, iTo, nReg);
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
- int x = p->iReg;
- if( x>=iFrom && x<iFrom+nReg ){
- p->iReg += iTo-iFrom;
- }
- }
-}
-
-/*
-** Generate code to copy content from registers iFrom...iFrom+nReg-1
-** over to iTo..iTo+nReg-1.
-*/
-SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, int iFrom, int iTo, int nReg){
- int i;
- if( NEVER(iFrom==iTo) ) return;
- for(i=0; i<nReg; i++){
- sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, iFrom+i, iTo+i);
- }
+ sqlite3ExprCacheRemove(pParse, iFrom, nReg);
}
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
@@ -75256,7 +92967,7 @@ SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, int iFrom, int iTo, int n
static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
int r = p->iReg;
if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/
}
@@ -75264,6 +92975,51 @@ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){
}
#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */
+
+/*
+** Convert a scalar expression node to a TK_REGISTER referencing
+** register iReg. The caller must ensure that iReg already contains
+** the correct value for the expression.
+*/
+static void exprToRegister(Expr *p, int iReg){
+ p->op2 = p->op;
+ p->op = TK_REGISTER;
+ p->iTable = iReg;
+ ExprClearProperty(p, EP_Skip);
+}
+
+/*
+** Evaluate an expression (either a vector or a scalar expression) and store
+** the result in continguous temporary registers. Return the index of
+** the first register used to store the result.
+**
+** If the returned result register is a temporary scalar, then also write
+** that register number into *piFreeable. If the returned result register
+** is not a temporary or if the expression is a vector set *piFreeable
+** to 0.
+*/
+static int exprCodeVector(Parse *pParse, Expr *p, int *piFreeable){
+ int iResult;
+ int nResult = sqlite3ExprVectorSize(p);
+ if( nResult==1 ){
+ iResult = sqlite3ExprCodeTemp(pParse, p, piFreeable);
+ }else{
+ *piFreeable = 0;
+ if( p->op==TK_SELECT ){
+ iResult = sqlite3CodeSubselect(pParse, p, 0, 0);
+ }else{
+ int i;
+ iResult = pParse->nMem+1;
+ pParse->nMem += nResult;
+ for(i=0; i<nResult; i++){
+ sqlite3ExprCode(pParse, p->x.pList->a[i].pExpr, i+iResult);
+ }
+ }
+ }
+ return iResult;
+}
+
+
/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
@@ -75281,8 +93037,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
int inReg = target; /* Results stored in register inReg */
int regFree1 = 0; /* If non-zero free this temporary register */
int regFree2 = 0; /* If non-zero free this temporary register */
- int r1, r2, r3, r4; /* Various register numbers */
- sqlite3 *db = pParse->db; /* The database connection */
+ int r1, r2; /* Various register numbers */
+ Expr tempX; /* Temporary expression node */
+ int p5 = 0;
assert( target>0 && target<=pParse->nMem );
if( v==0 ){
@@ -75301,45 +93058,49 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
struct AggInfo_col *pCol = &pAggInfo->aCol[pExpr->iAgg];
if( !pAggInfo->directMode ){
assert( pCol->iMem>0 );
- inReg = pCol->iMem;
- break;
+ return pCol->iMem;
}else if( pAggInfo->useSortingIdx ){
sqlite3VdbeAddOp3(v, OP_Column, pAggInfo->sortingIdxPTab,
pCol->iSorterColumn, target);
- break;
+ return target;
}
/* Otherwise, fall thru into the TK_COLUMN case */
}
case TK_COLUMN: {
- if( pExpr->iTable<0 ){
- /* This only happens when coding check constraints */
- assert( pParse->ckBase>0 );
- inReg = pExpr->iColumn + pParse->ckBase;
- }else{
- inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
- pExpr->iColumn, pExpr->iTable, target);
+ int iTab = pExpr->iTable;
+ if( iTab<0 ){
+ if( pParse->ckBase>0 ){
+ /* Generating CHECK constraints or inserting into partial index */
+ return pExpr->iColumn + pParse->ckBase;
+ }else{
+ /* Coding an expression that is part of an index where column names
+ ** in the index refer to the table to which the index belongs */
+ iTab = pParse->iSelfTab;
+ }
}
- break;
+ return sqlite3ExprCodeGetColumn(pParse, pExpr->pTab,
+ pExpr->iColumn, iTab, target,
+ pExpr->op2);
}
case TK_INTEGER: {
codeInteger(pParse, pExpr, 0, target);
- break;
+ return target;
}
#ifndef SQLITE_OMIT_FLOATING_POINT
case TK_FLOAT: {
assert( !ExprHasProperty(pExpr, EP_IntValue) );
codeReal(v, pExpr->u.zToken, 0, target);
- break;
+ return target;
}
#endif
case TK_STRING: {
assert( !ExprHasProperty(pExpr, EP_IntValue) );
- sqlite3VdbeAddOp4(v, OP_String8, 0, target, 0, pExpr->u.zToken, 0);
- break;
+ sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
+ return target;
}
case TK_NULL: {
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
- break;
+ return target;
}
#ifndef SQLITE_OMIT_BLOB_LITERAL
case TK_BLOB: {
@@ -75354,7 +93115,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( z[n]=='\'' );
zBlob = sqlite3HexToBlob(sqlite3VdbeDb(v), z, n);
sqlite3VdbeAddOp4(v, OP_Blob, n/2, target, 0, zBlob, P4_DYNAMIC);
- break;
+ return target;
}
#endif
case TK_VARIABLE: {
@@ -75367,81 +93128,54 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
|| strcmp(pExpr->u.zToken, pParse->azVar[pExpr->iColumn-1])==0 );
sqlite3VdbeChangeP4(v, -1, pParse->azVar[pExpr->iColumn-1], P4_STATIC);
}
- break;
+ return target;
}
case TK_REGISTER: {
- inReg = pExpr->iTable;
- break;
- }
- case TK_AS: {
- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
- break;
+ return pExpr->iTable;
}
#ifndef SQLITE_OMIT_CAST
case TK_CAST: {
/* Expressions of the form: CAST(pLeft AS token) */
- int aff, to_op;
inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- aff = sqlite3AffinityType(pExpr->u.zToken);
- to_op = aff - SQLITE_AFF_TEXT + OP_ToText;
- assert( to_op==OP_ToText || aff!=SQLITE_AFF_TEXT );
- assert( to_op==OP_ToBlob || aff!=SQLITE_AFF_NONE );
- assert( to_op==OP_ToNumeric || aff!=SQLITE_AFF_NUMERIC );
- assert( to_op==OP_ToInt || aff!=SQLITE_AFF_INTEGER );
- assert( to_op==OP_ToReal || aff!=SQLITE_AFF_REAL );
- testcase( to_op==OP_ToText );
- testcase( to_op==OP_ToBlob );
- testcase( to_op==OP_ToNumeric );
- testcase( to_op==OP_ToInt );
- testcase( to_op==OP_ToReal );
if( inReg!=target ){
sqlite3VdbeAddOp2(v, OP_SCopy, inReg, target);
inReg = target;
}
- sqlite3VdbeAddOp1(v, to_op, inReg);
+ sqlite3VdbeAddOp2(v, OP_Cast, target,
+ sqlite3AffinityType(pExpr->u.zToken, 0));
testcase( usedAsColumnCache(pParse, inReg, inReg) );
sqlite3ExprCacheAffinityChange(pParse, inReg, 1);
- break;
+ return inReg;
}
#endif /* SQLITE_OMIT_CAST */
+ case TK_IS:
+ case TK_ISNOT:
+ op = (op==TK_IS) ? TK_EQ : TK_NE;
+ p5 = SQLITE_NULLEQ;
+ /* fall-through */
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ: {
- assert( TK_LT==OP_Lt );
- assert( TK_LE==OP_Le );
- assert( TK_GT==OP_Gt );
- assert( TK_GE==OP_Ge );
- assert( TK_EQ==OP_Eq );
- assert( TK_NE==OP_Ne );
- testcase( op==TK_LT );
- testcase( op==TK_LE );
- testcase( op==TK_GT );
- testcase( op==TK_GE );
- testcase( op==TK_EQ );
- testcase( op==TK_NE );
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, inReg, SQLITE_STOREP2);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- break;
- }
- case TK_IS:
- case TK_ISNOT: {
- testcase( op==TK_IS );
- testcase( op==TK_ISNOT );
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- op = (op==TK_IS) ? TK_EQ : TK_NE;
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
+ Expr *pLeft = pExpr->pLeft;
+ if( sqlite3ExprIsVector(pLeft) ){
+ codeVectorCompare(pParse, pExpr, target, op, p5);
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
+ r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
+ codeCompare(pParse, pLeft, pExpr->pRight, op,
+ r1, r2, inReg, SQLITE_STOREP2 | p5);
+ assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq); VdbeCoverageIf(v,op==OP_Eq);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne); VdbeCoverageIf(v,op==OP_Ne);
+ testcase( regFree1==0 );
+ testcase( regFree2==0 );
+ }
break;
}
case TK_AND:
@@ -75456,28 +93190,17 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
case TK_LSHIFT:
case TK_RSHIFT:
case TK_CONCAT: {
- assert( TK_AND==OP_And );
- assert( TK_OR==OP_Or );
- assert( TK_PLUS==OP_Add );
- assert( TK_MINUS==OP_Subtract );
- assert( TK_REM==OP_Remainder );
- assert( TK_BITAND==OP_BitAnd );
- assert( TK_BITOR==OP_BitOr );
- assert( TK_SLASH==OP_Divide );
- assert( TK_LSHIFT==OP_ShiftLeft );
- assert( TK_RSHIFT==OP_ShiftRight );
- assert( TK_CONCAT==OP_Concat );
- testcase( op==TK_AND );
- testcase( op==TK_OR );
- testcase( op==TK_PLUS );
- testcase( op==TK_MINUS );
- testcase( op==TK_REM );
- testcase( op==TK_BITAND );
- testcase( op==TK_BITOR );
- testcase( op==TK_SLASH );
- testcase( op==TK_LSHIFT );
- testcase( op==TK_RSHIFT );
- testcase( op==TK_CONCAT );
+ assert( TK_AND==OP_And ); testcase( op==TK_AND );
+ assert( TK_OR==OP_Or ); testcase( op==TK_OR );
+ assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
+ assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS );
+ assert( TK_REM==OP_Remainder ); testcase( op==TK_REM );
+ assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND );
+ assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR );
+ assert( TK_SLASH==OP_Divide ); testcase( op==TK_SLASH );
+ assert( TK_LSHIFT==OP_ShiftLeft ); testcase( op==TK_LSHIFT );
+ assert( TK_RSHIFT==OP_ShiftRight ); testcase( op==TK_RSHIFT );
+ assert( TK_CONCAT==OP_Concat ); testcase( op==TK_CONCAT );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
sqlite3VdbeAddOp3(v, op, r2, r1, target);
@@ -75490,45 +93213,45 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( pLeft );
if( pLeft->op==TK_INTEGER ){
codeInteger(pParse, pLeft, 1, target);
+ return target;
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( pLeft->op==TK_FLOAT ){
assert( !ExprHasProperty(pExpr, EP_IntValue) );
codeReal(v, pLeft->u.zToken, 1, target);
+ return target;
#endif
}else{
- regFree1 = r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, r1);
+ tempX.op = TK_INTEGER;
+ tempX.flags = EP_IntValue|EP_TokenOnly;
+ tempX.u.iValue = 0;
+ r1 = sqlite3ExprCodeTemp(pParse, &tempX, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree2);
sqlite3VdbeAddOp3(v, OP_Subtract, r2, r1, target);
testcase( regFree2==0 );
}
- inReg = target;
break;
}
case TK_BITNOT:
case TK_NOT: {
- assert( TK_BITNOT==OP_BitNot );
- assert( TK_NOT==OP_Not );
- testcase( op==TK_BITNOT );
- testcase( op==TK_NOT );
+ assert( TK_BITNOT==OP_BitNot ); testcase( op==TK_BITNOT );
+ assert( TK_NOT==OP_Not ); testcase( op==TK_NOT );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
testcase( regFree1==0 );
- inReg = target;
sqlite3VdbeAddOp2(v, op, r1, inReg);
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
int addr;
- assert( TK_ISNULL==OP_IsNull );
- assert( TK_NOTNULL==OP_NotNull );
- testcase( op==TK_ISNULL );
- testcase( op==TK_NOTNULL );
+ assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
+ assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
sqlite3VdbeAddOp2(v, OP_Integer, 1, target);
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
testcase( regFree1==0 );
addr = sqlite3VdbeAddOp1(v, op, r1);
- sqlite3VdbeAddOp2(v, OP_AddImm, target, -1);
+ VdbeCoverageIf(v, op==TK_ISNULL);
+ VdbeCoverageIf(v, op==TK_NOTNULL);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, target);
sqlite3VdbeJumpHere(v, addr);
break;
}
@@ -75538,26 +93261,23 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3ErrorMsg(pParse, "misuse of aggregate: %s()", pExpr->u.zToken);
}else{
- inReg = pInfo->aFunc[pExpr->iAgg].iMem;
+ return pInfo->aFunc[pExpr->iAgg].iMem;
}
break;
}
- case TK_CONST_FUNC:
case TK_FUNCTION: {
ExprList *pFarg; /* List of function arguments */
int nFarg; /* Number of function arguments */
FuncDef *pDef; /* The function definition object */
- int nId; /* Length of the function name in bytes */
const char *zId; /* The function name */
- int constMask = 0; /* Mask of function arguments that are constant */
+ u32 constMask = 0; /* Mask of function arguments that are constant */
int i; /* Loop counter */
+ sqlite3 *db = pParse->db; /* The database connection */
u8 enc = ENC(db); /* The text encoding used by this database */
CollSeq *pColl = 0; /* A collating sequence */
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- testcase( op==TK_CONST_FUNC );
- testcase( op==TK_FUNCTION );
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){
pFarg = 0;
}else{
pFarg = pExpr->x.pList;
@@ -75565,38 +93285,85 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
nFarg = pFarg ? pFarg->nExpr : 0;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
zId = pExpr->u.zToken;
- nId = sqlite3Strlen30(zId);
- pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0);
- if( pDef==0 ){
- sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId);
+ pDef = sqlite3FindFunction(db, zId, nFarg, enc, 0);
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ if( pDef==0 && pParse->explain ){
+ pDef = sqlite3FindFunction(db, "unknown", nFarg, enc, 0);
+ }
+#endif
+ if( pDef==0 || pDef->xFinalize!=0 ){
+ sqlite3ErrorMsg(pParse, "unknown function: %s()", zId);
break;
}
/* Attempt a direct implementation of the built-in COALESCE() and
- ** IFNULL() functions. This avoids unnecessary evalation of
+ ** IFNULL() functions. This avoids unnecessary evaluation of
** arguments past the first non-NULL argument.
*/
- if( pDef->flags & SQLITE_FUNC_COALESCE ){
+ if( pDef->funcFlags & SQLITE_FUNC_COALESCE ){
int endCoalesce = sqlite3VdbeMakeLabel(v);
assert( nFarg>=2 );
sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target);
for(i=1; i<nFarg; i++){
sqlite3VdbeAddOp2(v, OP_NotNull, target, endCoalesce);
+ VdbeCoverage(v);
sqlite3ExprCacheRemove(pParse, target, 1);
sqlite3ExprCachePush(pParse);
sqlite3ExprCode(pParse, pFarg->a[i].pExpr, target);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
}
sqlite3VdbeResolveLabel(v, endCoalesce);
break;
}
+ /* The UNLIKELY() function is a no-op. The result is the value
+ ** of the first argument.
+ */
+ if( pDef->funcFlags & SQLITE_FUNC_UNLIKELY ){
+ assert( nFarg>=1 );
+ return sqlite3ExprCodeTarget(pParse, pFarg->a[0].pExpr, target);
+ }
+ for(i=0; i<nFarg; i++){
+ if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
+ testcase( i==31 );
+ constMask |= MASKBIT32(i);
+ }
+ if( (pDef->funcFlags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
+ pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
+ }
+ }
if( pFarg ){
- r1 = sqlite3GetTempRange(pParse, nFarg);
+ if( constMask ){
+ r1 = pParse->nMem+1;
+ pParse->nMem += nFarg;
+ }else{
+ r1 = sqlite3GetTempRange(pParse, nFarg);
+ }
+
+ /* For length() and typeof() functions with a column argument,
+ ** set the P5 parameter to the OP_Column opcode to OPFLAG_LENGTHARG
+ ** or OPFLAG_TYPEOFARG respectively, to avoid unnecessary data
+ ** loading.
+ */
+ if( (pDef->funcFlags & (SQLITE_FUNC_LENGTH|SQLITE_FUNC_TYPEOF))!=0 ){
+ u8 exprOp;
+ assert( nFarg==1 );
+ assert( pFarg->a[0].pExpr!=0 );
+ exprOp = pFarg->a[0].pExpr->op;
+ if( exprOp==TK_COLUMN || exprOp==TK_AGG_COLUMN ){
+ assert( SQLITE_FUNC_LENGTH==OPFLAG_LENGTHARG );
+ assert( SQLITE_FUNC_TYPEOF==OPFLAG_TYPEOFARG );
+ testcase( pDef->funcFlags & OPFLAG_LENGTHARG );
+ pFarg->a[0].pExpr->op2 =
+ pDef->funcFlags & (OPFLAG_LENGTHARG|OPFLAG_TYPEOFARG);
+ }
+ }
+
sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */
- sqlite3ExprCodeExprList(pParse, pFarg, r1, 1);
- sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */
+ sqlite3ExprCodeExprList(pParse, pFarg, r1, 0,
+ SQLITE_ECEL_DUP|SQLITE_ECEL_FACTOR);
+ sqlite3ExprCachePop(pParse); /* Ticket 2ea2425d34be */
}else{
r1 = 0;
}
@@ -75619,34 +93386,37 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
pDef = sqlite3VtabOverloadFunction(db, pDef, nFarg, pFarg->a[0].pExpr);
}
#endif
- for(i=0; i<nFarg; i++){
- if( i<32 && sqlite3ExprIsConstant(pFarg->a[i].pExpr) ){
- constMask |= (1<<i);
- }
- if( (pDef->flags & SQLITE_FUNC_NEEDCOLL)!=0 && !pColl ){
- pColl = sqlite3ExprCollSeq(pParse, pFarg->a[i].pExpr);
- }
- }
- if( pDef->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( pDef->funcFlags & SQLITE_FUNC_NEEDCOLL ){
if( !pColl ) pColl = db->pDfltColl;
sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
}
- sqlite3VdbeAddOp4(v, OP_Function, constMask, r1, target,
+ sqlite3VdbeAddOp4(v, OP_Function0, constMask, r1, target,
(char*)pDef, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nFarg);
- if( nFarg ){
+ if( nFarg && constMask==0 ){
sqlite3ReleaseTempRange(pParse, r1, nFarg);
}
- break;
+ return target;
}
#ifndef SQLITE_OMIT_SUBQUERY
case TK_EXISTS:
case TK_SELECT: {
+ int nCol;
testcase( op==TK_EXISTS );
testcase( op==TK_SELECT );
- inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ if( op==TK_SELECT && (nCol = pExpr->x.pSelect->pEList->nExpr)!=1 ){
+ sqlite3SubselectError(pParse, nCol, 1);
+ }else{
+ return sqlite3CodeSubselect(pParse, pExpr, 0, 0);
+ }
break;
}
+ case TK_SELECT_COLUMN: {
+ if( pExpr->pLeft->iTable==0 ){
+ pExpr->pLeft->iTable = sqlite3CodeSubselect(pParse, pExpr->pLeft, 0, 0);
+ }
+ return pExpr->pLeft->iTable + pExpr->iColumn;
+ }
case TK_IN: {
int destIfFalse = sqlite3VdbeMakeLabel(v);
int destIfNull = sqlite3VdbeMakeLabel(v);
@@ -75656,7 +93426,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
sqlite3VdbeResolveLabel(v, destIfFalse);
sqlite3VdbeAddOp2(v, OP_AddImm, target, 0);
sqlite3VdbeResolveLabel(v, destIfNull);
- break;
+ return target;
}
#endif /* SQLITE_OMIT_SUBQUERY */
@@ -75673,32 +93443,13 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** Z is stored in pExpr->pList->a[1].pExpr.
*/
case TK_BETWEEN: {
- Expr *pLeft = pExpr->pLeft;
- struct ExprList_item *pLItem = pExpr->x.pList->a;
- Expr *pRight = pLItem->pExpr;
-
- r1 = sqlite3ExprCodeTemp(pParse, pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pRight, &regFree2);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- r3 = sqlite3GetTempReg(pParse);
- r4 = sqlite3GetTempReg(pParse);
- codeCompare(pParse, pLeft, pRight, OP_Ge,
- r1, r2, r3, SQLITE_STOREP2);
- pLItem++;
- pRight = pLItem->pExpr;
- sqlite3ReleaseTempReg(pParse, regFree2);
- r2 = sqlite3ExprCodeTemp(pParse, pRight, &regFree2);
- testcase( regFree2==0 );
- codeCompare(pParse, pLeft, pRight, OP_Le, r1, r2, r4, SQLITE_STOREP2);
- sqlite3VdbeAddOp3(v, OP_And, r3, r4, target);
- sqlite3ReleaseTempReg(pParse, r3);
- sqlite3ReleaseTempReg(pParse, r4);
- break;
+ exprCodeBetween(pParse, pExpr, target, 0, 0);
+ return target;
}
+ case TK_SPAN:
+ case TK_COLLATE:
case TK_UPLUS: {
- inReg = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
- break;
+ return sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
}
case TK_TRIGGER: {
@@ -75744,7 +93495,10 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
#ifndef SQLITE_OMIT_FLOATING_POINT
/* If the column has REAL affinity, it may currently be stored as an
- ** integer. Use OP_RealAffinity to make sure it is really real. */
+ ** integer. Use OP_RealAffinity to make sure it is really real.
+ **
+ ** EVIDENCE-OF: R-60985-57662 SQLite will convert the value back to
+ ** floating point when extracting it from the record. */
if( pExpr->iColumn>=0
&& pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL
){
@@ -75754,6 +93508,10 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
break;
}
+ case TK_VECTOR: {
+ sqlite3ErrorMsg(pParse, "row value misused");
+ break;
+ }
/*
** Form A:
@@ -75767,9 +93525,9 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** WHEN x=eN THEN rN ELSE y END
**
** X (if it exists) is in pExpr->pLeft.
- ** Y is in pExpr->pRight. The Y is also optional. If there is no
- ** ELSE clause and no other term matches, then the result of the
- ** exprssion is NULL.
+ ** Y is in the last element of pExpr->x.pList if pExpr->x.pList->nExpr is
+ ** odd. The Y is also optional. If the number of elements in x.pList
+ ** is even, then Y is omitted and the "otherwise" result is NULL.
** Ei is in pExpr->pList->a[i*2] and Ri is pExpr->pList->a[i*2+1].
**
** The result of the expression is the Ri for the first matching Ei,
@@ -75784,27 +93542,24 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
ExprList *pEList; /* List of WHEN terms */
struct ExprList_item *aListelem; /* Array of WHEN terms */
Expr opCompare; /* The X==Ei expression */
- Expr cacheX; /* Cached expression X */
Expr *pX; /* The X expression */
Expr *pTest = 0; /* X==Ei (form A) or just Ei (form B) */
VVA_ONLY( int iCacheLevel = pParse->iCacheLevel; )
assert( !ExprHasProperty(pExpr, EP_xIsSelect) && pExpr->x.pList );
- assert((pExpr->x.pList->nExpr % 2) == 0);
assert(pExpr->x.pList->nExpr > 0);
pEList = pExpr->x.pList;
aListelem = pEList->a;
nExpr = pEList->nExpr;
endLabel = sqlite3VdbeMakeLabel(v);
if( (pX = pExpr->pLeft)!=0 ){
- cacheX = *pX;
+ tempX = *pX;
testcase( pX->op==TK_COLUMN );
- testcase( pX->op==TK_REGISTER );
- cacheX.iTable = sqlite3ExprCodeTemp(pParse, pX, &regFree1);
+ exprToRegister(&tempX, exprCodeVector(pParse, &tempX, &regFree1));
testcase( regFree1==0 );
- cacheX.op = TK_REGISTER;
+ memset(&opCompare, 0, sizeof(opCompare));
opCompare.op = TK_EQ;
- opCompare.pLeft = &cacheX;
+ opCompare.pLeft = &tempX;
pTest = &opCompare;
/* Ticket b351d95f9cd5ef17e9d9dbae18f5ca8611190001:
** The value in regFree1 might get SCopy-ed into the file result.
@@ -75812,7 +93567,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** purposes and possibly overwritten. */
regFree1 = 0;
}
- for(i=0; i<nExpr; i=i+2){
+ for(i=0; i<nExpr-1; i=i+2){
sqlite3ExprCachePush(pParse);
if( pX ){
assert( pTest!=0 );
@@ -75824,20 +93579,19 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
testcase( pTest->op==TK_COLUMN );
sqlite3ExprIfFalse(pParse, pTest, nextCase, SQLITE_JUMPIFNULL);
testcase( aListelem[i+1].pExpr->op==TK_COLUMN );
- testcase( aListelem[i+1].pExpr->op==TK_REGISTER );
sqlite3ExprCode(pParse, aListelem[i+1].pExpr, target);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, endLabel);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3VdbeGoto(v, endLabel);
+ sqlite3ExprCachePop(pParse);
sqlite3VdbeResolveLabel(v, nextCase);
}
- if( pExpr->pRight ){
+ if( (nExpr&1)!=0 ){
sqlite3ExprCachePush(pParse);
- sqlite3ExprCode(pParse, pExpr->pRight, target);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCode(pParse, pEList->a[nExpr-1].pExpr, target);
+ sqlite3ExprCachePop(pParse);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
}
- assert( db->mallocFailed || pParse->nErr>0
+ assert( pParse->db->mallocFailed || pParse->nErr>0
|| pParse->iCacheLevel==iCacheLevel );
sqlite3VdbeResolveLabel(v, endLabel);
break;
@@ -75861,8 +93615,10 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
if( pExpr->affinity==OE_Ignore ){
sqlite3VdbeAddOp4(
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
+ VdbeCoverage(v);
}else{
- sqlite3HaltConstraint(pParse, pExpr->affinity, pExpr->u.zToken, 0);
+ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
+ pExpr->affinity, pExpr->u.zToken, 0, 0);
}
break;
@@ -75875,6 +93631,28 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
}
/*
+** Factor out the code of the given expression to initialization time.
+*/
+SQLITE_PRIVATE void sqlite3ExprCodeAtInit(
+ Parse *pParse, /* Parsing context */
+ Expr *pExpr, /* The expression to code when the VDBE initializes */
+ int regDest, /* Store the value in this register */
+ u8 reusable /* True if this expression is reusable */
+){
+ ExprList *p;
+ assert( ConstFactorOk(pParse) );
+ p = pParse->pConstExpr;
+ pExpr = sqlite3ExprDup(pParse->db, pExpr, 0);
+ p = sqlite3ExprListAppend(pParse, p, pExpr);
+ if( p ){
+ struct ExprList_item *pItem = &p->a[p->nExpr-1];
+ pItem->u.iConstExprReg = regDest;
+ pItem->reusable = reusable;
+ }
+ pParse->pConstExpr = p;
+}
+
+/*
** Generate code to evaluate an expression and store the results
** into a register. Return the register number where the results
** are stored.
@@ -75882,15 +93660,40 @@ SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse *pParse, Expr *pExpr, int target)
** If the register is a temporary register that can be deallocated,
** then write its number into *pReg. If the result register is not
** a temporary, then set *pReg to zero.
+**
+** If pExpr is a constant, then this routine might generate this
+** code to fill the register in the initialization section of the
+** VDBE program, in order to factor it out of the evaluation loop.
*/
SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
- int r1 = sqlite3GetTempReg(pParse);
- int r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
- if( r2==r1 ){
- *pReg = r1;
+ int r2;
+ pExpr = sqlite3ExprSkipCollate(pExpr);
+ if( ConstFactorOk(pParse)
+ && pExpr->op!=TK_REGISTER
+ && sqlite3ExprIsConstantNotJoin(pExpr)
+ ){
+ ExprList *p = pParse->pConstExpr;
+ int i;
+ *pReg = 0;
+ if( p ){
+ struct ExprList_item *pItem;
+ for(pItem=p->a, i=p->nExpr; i>0; pItem++, i--){
+ if( pItem->reusable && sqlite3ExprCompare(pItem->pExpr,pExpr,-1)==0 ){
+ return pItem->u.iConstExprReg;
+ }
+ }
+ }
+ r2 = ++pParse->nMem;
+ sqlite3ExprCodeAtInit(pParse, pExpr, r2, 1);
}else{
- sqlite3ReleaseTempReg(pParse, r1);
- *pReg = 0;
+ int r1 = sqlite3GetTempReg(pParse);
+ r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
+ if( r2==r1 ){
+ *pReg = r1;
+ }else{
+ sqlite3ReleaseTempReg(pParse, r1);
+ *pReg = 0;
+ }
}
return r2;
}
@@ -75900,7 +93703,7 @@ SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse *pParse, Expr *pExpr, int *pReg){
** results in register target. The results are guaranteed to appear
** in register target.
*/
-SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
+SQLITE_PRIVATE void sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
int inReg;
assert( target>0 && target<=pParse->nMem );
@@ -75908,203 +93711,115 @@ SQLITE_PRIVATE int sqlite3ExprCode(Parse *pParse, Expr *pExpr, int target){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_Copy, pExpr->iTable, target);
}else{
inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
- assert( pParse->pVdbe || pParse->db->mallocFailed );
+ assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
if( inReg!=target && pParse->pVdbe ){
sqlite3VdbeAddOp2(pParse->pVdbe, OP_SCopy, inReg, target);
}
}
- return target;
-}
-
-/*
-** Generate code that evalutes the given expression and puts the result
-** in register target.
-**
-** Also make a copy of the expression results into another "cache" register
-** and modify the expression so that the next time it is evaluated,
-** the result is a copy of the cache register.
-**
-** This routine is used for expressions that are used multiple
-** times. They are evaluated once and the results of the expression
-** are reused.
-*/
-SQLITE_PRIVATE int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
- Vdbe *v = pParse->pVdbe;
- int inReg;
- inReg = sqlite3ExprCode(pParse, pExpr, target);
- assert( target>0 );
- /* This routine is called for terms to INSERT or UPDATE. And the only
- ** other place where expressions can be converted into TK_REGISTER is
- ** in WHERE clause processing. So as currently implemented, there is
- ** no way for a TK_REGISTER to exist here. But it seems prudent to
- ** keep the ALWAYS() in case the conditions above change with future
- ** modifications or enhancements. */
- if( ALWAYS(pExpr->op!=TK_REGISTER) ){
- int iMem;
- iMem = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem);
- pExpr->iTable = iMem;
- pExpr->op2 = pExpr->op;
- pExpr->op = TK_REGISTER;
- }
- return inReg;
}
/*
-** Return TRUE if pExpr is an constant expression that is appropriate
-** for factoring out of a loop. Appropriate expressions are:
-**
-** * Any expression that evaluates to two or more opcodes.
-**
-** * Any OP_Integer, OP_Real, OP_String, OP_Blob, OP_Null,
-** or OP_Variable that does not need to be placed in a
-** specific register.
-**
-** There is no point in factoring out single-instruction constant
-** expressions that need to be placed in a particular register.
-** We could factor them out, but then we would end up adding an
-** OP_SCopy instruction to move the value into the correct register
-** later. We might as well just use the original instruction and
-** avoid the OP_SCopy.
+** Make a transient copy of expression pExpr and then code it using
+** sqlite3ExprCode(). This routine works just like sqlite3ExprCode()
+** except that the input expression is guaranteed to be unchanged.
*/
-static int isAppropriateForFactoring(Expr *p){
- if( !sqlite3ExprIsConstantNotJoin(p) ){
- return 0; /* Only constant expressions are appropriate for factoring */
- }
- if( (p->flags & EP_FixedDest)==0 ){
- return 1; /* Any constant without a fixed destination is appropriate */
- }
- while( p->op==TK_UPLUS ) p = p->pLeft;
- switch( p->op ){
-#ifndef SQLITE_OMIT_BLOB_LITERAL
- case TK_BLOB:
-#endif
- case TK_VARIABLE:
- case TK_INTEGER:
- case TK_FLOAT:
- case TK_NULL:
- case TK_STRING: {
- testcase( p->op==TK_BLOB );
- testcase( p->op==TK_VARIABLE );
- testcase( p->op==TK_INTEGER );
- testcase( p->op==TK_FLOAT );
- testcase( p->op==TK_NULL );
- testcase( p->op==TK_STRING );
- /* Single-instruction constants with a fixed destination are
- ** better done in-line. If we factor them, they will just end
- ** up generating an OP_SCopy to move the value to the destination
- ** register. */
- return 0;
- }
- case TK_UMINUS: {
- if( p->pLeft->op==TK_FLOAT || p->pLeft->op==TK_INTEGER ){
- return 0;
- }
- break;
- }
- default: {
- break;
- }
- }
- return 1;
+SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse *pParse, Expr *pExpr, int target){
+ sqlite3 *db = pParse->db;
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ if( !db->mallocFailed ) sqlite3ExprCode(pParse, pExpr, target);
+ sqlite3ExprDelete(db, pExpr);
}
/*
-** If pExpr is a constant expression that is appropriate for
-** factoring out of a loop, then evaluate the expression
-** into a register and convert the expression into a TK_REGISTER
-** expression.
+** Generate code that will evaluate expression pExpr and store the
+** results in register target. The results are guaranteed to appear
+** in register target. If the expression is constant, then this routine
+** might choose to code the expression at initialization time.
*/
-static int evalConstExpr(Walker *pWalker, Expr *pExpr){
- Parse *pParse = pWalker->pParse;
- switch( pExpr->op ){
- case TK_IN:
- case TK_REGISTER: {
- return WRC_Prune;
- }
- case TK_FUNCTION:
- case TK_AGG_FUNCTION:
- case TK_CONST_FUNC: {
- /* The arguments to a function have a fixed destination.
- ** Mark them this way to avoid generated unneeded OP_SCopy
- ** instructions.
- */
- ExprList *pList = pExpr->x.pList;
- assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- if( pList ){
- int i = pList->nExpr;
- struct ExprList_item *pItem = pList->a;
- for(; i>0; i--, pItem++){
- if( ALWAYS(pItem->pExpr) ) pItem->pExpr->flags |= EP_FixedDest;
- }
- }
- break;
- }
- }
- if( isAppropriateForFactoring(pExpr) ){
- int r1 = ++pParse->nMem;
- int r2;
- r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1);
- if( NEVER(r1!=r2) ) sqlite3ReleaseTempReg(pParse, r1);
- pExpr->op2 = pExpr->op;
- pExpr->op = TK_REGISTER;
- pExpr->iTable = r2;
- return WRC_Prune;
+SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse *pParse, Expr *pExpr, int target){
+ if( pParse->okConstFactor && sqlite3ExprIsConstant(pExpr) ){
+ sqlite3ExprCodeAtInit(pParse, pExpr, target, 0);
+ }else{
+ sqlite3ExprCode(pParse, pExpr, target);
}
- return WRC_Continue;
}
/*
-** Preevaluate constant subexpressions within pExpr and store the
-** results in registers. Modify pExpr so that the constant subexpresions
-** are TK_REGISTER opcodes that refer to the precomputed values.
+** Generate code that evaluates the given expression and puts the result
+** in register target.
**
-** This routine is a no-op if the jump to the cookie-check code has
-** already occur. Since the cookie-check jump is generated prior to
-** any other serious processing, this check ensures that there is no
-** way to accidently bypass the constant initializations.
+** Also make a copy of the expression results into another "cache" register
+** and modify the expression so that the next time it is evaluated,
+** the result is a copy of the cache register.
**
-** This routine is also a no-op if the SQLITE_FactorOutConst optimization
-** is disabled via the sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS)
-** interface. This allows test logic to verify that the same answer is
-** obtained for queries regardless of whether or not constants are
-** precomputed into registers or if they are inserted in-line.
+** This routine is used for expressions that are used multiple
+** times. They are evaluated once and the results of the expression
+** are reused.
*/
-SQLITE_PRIVATE void sqlite3ExprCodeConstants(Parse *pParse, Expr *pExpr){
- Walker w;
- if( pParse->cookieGoto ) return;
- if( (pParse->db->flags & SQLITE_FactorOutConst)!=0 ) return;
- w.xExprCallback = evalConstExpr;
- w.xSelectCallback = 0;
- w.pParse = pParse;
- sqlite3WalkExpr(&w, pExpr);
-}
+SQLITE_PRIVATE void sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
+ Vdbe *v = pParse->pVdbe;
+ int iMem;
+ assert( target>0 );
+ assert( pExpr->op!=TK_REGISTER );
+ sqlite3ExprCode(pParse, pExpr, target);
+ iMem = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Copy, target, iMem);
+ exprToRegister(pExpr, iMem);
+}
/*
** Generate code that pushes the value of every element of the given
** expression list into a sequence of registers beginning at target.
**
** Return the number of elements evaluated.
+**
+** The SQLITE_ECEL_DUP flag prevents the arguments from being
+** filled using OP_SCopy. OP_Copy must be used instead.
+**
+** The SQLITE_ECEL_FACTOR argument allows constant arguments to be
+** factored out into initialization code.
+**
+** The SQLITE_ECEL_REF flag means that expressions in the list with
+** ExprList.a[].u.x.iOrderByCol>0 have already been evaluated and stored
+** in registers at srcReg, and so the value can be copied from there.
*/
SQLITE_PRIVATE int sqlite3ExprCodeExprList(
Parse *pParse, /* Parsing context */
ExprList *pList, /* The expression list to be coded */
int target, /* Where to write results */
- int doHardCopy /* Make a hard copy of every element */
+ int srcReg, /* Source registers if SQLITE_ECEL_REF */
+ u8 flags /* SQLITE_ECEL_* flags */
){
struct ExprList_item *pItem;
- int i, n;
+ int i, j, n;
+ u8 copyOp = (flags & SQLITE_ECEL_DUP) ? OP_Copy : OP_SCopy;
+ Vdbe *v = pParse->pVdbe;
assert( pList!=0 );
assert( target>0 );
assert( pParse->pVdbe!=0 ); /* Never gets this far otherwise */
n = pList->nExpr;
+ if( !ConstFactorOk(pParse) ) flags &= ~SQLITE_ECEL_FACTOR;
for(pItem=pList->a, i=0; i<n; i++, pItem++){
Expr *pExpr = pItem->pExpr;
- int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
- if( inReg!=target+i ){
- sqlite3VdbeAddOp2(pParse->pVdbe, doHardCopy ? OP_Copy : OP_SCopy,
- inReg, target+i);
+ if( (flags & SQLITE_ECEL_REF)!=0 && (j = pList->a[i].u.x.iOrderByCol)>0 ){
+ sqlite3VdbeAddOp2(v, copyOp, j+srcReg-1, target+i);
+ }else if( (flags & SQLITE_ECEL_FACTOR)!=0 && sqlite3ExprIsConstant(pExpr) ){
+ sqlite3ExprCodeAtInit(pParse, pExpr, target+i, 0);
+ }else{
+ int inReg = sqlite3ExprCodeTarget(pParse, pExpr, target+i);
+ if( inReg!=target+i ){
+ VdbeOp *pOp;
+ if( copyOp==OP_Copy
+ && (pOp=sqlite3VdbeGetOp(v, -1))->opcode==OP_Copy
+ && pOp->p1+pOp->p3+1==inReg
+ && pOp->p2+pOp->p3+1==target+i
+ ){
+ pOp->p3++;
+ }else{
+ sqlite3VdbeAddOp2(v, copyOp, inReg, target+i);
+ }
+ }
}
}
return n;
@@ -76120,21 +93835,34 @@ SQLITE_PRIVATE int sqlite3ExprCodeExprList(
** x>=y AND x<=z
**
** Code it as such, taking care to do the common subexpression
-** elementation of x.
+** elimination of x.
+**
+** The xJumpIf parameter determines details:
+**
+** NULL: Store the boolean result in reg[dest]
+** sqlite3ExprIfTrue: Jump to dest if true
+** sqlite3ExprIfFalse: Jump to dest if false
+**
+** The jumpIfNull parameter is ignored if xJumpIf is NULL.
*/
static void exprCodeBetween(
Parse *pParse, /* Parsing and code generating context */
Expr *pExpr, /* The BETWEEN expression */
- int dest, /* Jump here if the jump is taken */
- int jumpIfTrue, /* Take the jump if the BETWEEN is true */
+ int dest, /* Jump destination or storage location */
+ void (*xJump)(Parse*,Expr*,int,int), /* Action to take */
int jumpIfNull /* Take the jump if the BETWEEN is NULL */
){
- Expr exprAnd; /* The AND operator in x>=y AND x<=z */
+ Expr exprAnd; /* The AND operator in x>=y AND x<=z */
Expr compLeft; /* The x>=y term */
Expr compRight; /* The x<=z term */
Expr exprX; /* The x subexpression */
int regFree1 = 0; /* Temporary use register */
+
+ memset(&compLeft, 0, sizeof(Expr));
+ memset(&compRight, 0, sizeof(Expr));
+ memset(&exprAnd, 0, sizeof(Expr));
+
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
exprX = *pExpr->pLeft;
exprAnd.op = TK_AND;
@@ -76146,24 +93874,25 @@ static void exprCodeBetween(
compRight.op = TK_LE;
compRight.pLeft = &exprX;
compRight.pRight = pExpr->x.pList->a[1].pExpr;
- exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, &regFree1);
- exprX.op = TK_REGISTER;
- if( jumpIfTrue ){
- sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull);
+ exprToRegister(&exprX, exprCodeVector(pParse, &exprX, &regFree1));
+ if( xJump ){
+ xJump(pParse, &exprAnd, dest, jumpIfNull);
}else{
- sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull);
+ exprX.flags |= EP_FromJoin;
+ sqlite3ExprCodeTarget(pParse, &exprAnd, dest);
}
sqlite3ReleaseTempReg(pParse, regFree1);
/* Ensure adequate test coverage */
- testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1==0 );
- testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1!=0 );
- testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1==0 );
- testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1!=0 );
- testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1==0 );
- testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1!=0 );
- testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1==0 );
- testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull==0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfTrue && jumpIfNull!=0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull==0 && regFree1!=0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 && regFree1==0 );
+ testcase( xJump==sqlite3ExprIfFalse && jumpIfNull!=0 && regFree1!=0 );
+ testcase( xJump==0 );
}
/*
@@ -76188,24 +93917,26 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
int r1, r2;
assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
- if( NEVER(v==0) ) return; /* Existance of VDBE checked by caller */
+ if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
if( NEVER(pExpr==0) ) return; /* No way this can happen */
op = pExpr->op;
switch( op ){
case TK_AND: {
int d2 = sqlite3VdbeMakeLabel(v);
testcase( jumpIfNull==0 );
- sqlite3ExprCachePush(pParse);
sqlite3ExprIfFalse(pParse, pExpr->pLeft, d2,jumpIfNull^SQLITE_JUMPIFNULL);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_OR: {
testcase( jumpIfNull==0 );
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfTrue(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_NOT: {
@@ -76213,60 +93944,53 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
break;
}
+ case TK_IS:
+ case TK_ISNOT:
+ testcase( op==TK_IS );
+ testcase( op==TK_ISNOT );
+ op = (op==TK_IS) ? TK_EQ : TK_NE;
+ jumpIfNull = SQLITE_NULLEQ;
+ /* Fall thru */
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ: {
- assert( TK_LT==OP_Lt );
- assert( TK_LE==OP_Le );
- assert( TK_GT==OP_Gt );
- assert( TK_GE==OP_Ge );
- assert( TK_EQ==OP_Eq );
- assert( TK_NE==OP_Ne );
- testcase( op==TK_LT );
- testcase( op==TK_LE );
- testcase( op==TK_GT );
- testcase( op==TK_GE );
- testcase( op==TK_EQ );
- testcase( op==TK_NE );
+ if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
testcase( jumpIfNull==0 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, jumpIfNull);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- break;
- }
- case TK_IS:
- case TK_ISNOT: {
- testcase( op==TK_IS );
- testcase( op==TK_ISNOT );
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- op = (op==TK_IS) ? TK_EQ : TK_NE;
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, dest, SQLITE_NULLEQ);
+ assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull==SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull!=SQLITE_NULLEQ);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull==SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull!=SQLITE_NULLEQ);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
- assert( TK_ISNULL==OP_IsNull );
- assert( TK_NOTNULL==OP_NotNull );
- testcase( op==TK_ISNULL );
- testcase( op==TK_NOTNULL );
+ assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
+ assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
sqlite3VdbeAddOp2(v, op, r1, dest);
+ VdbeCoverageIf(v, op==TK_ISNULL);
+ VdbeCoverageIf(v, op==TK_NOTNULL);
testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
testcase( jumpIfNull==0 );
- exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull);
+ exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
@@ -76274,16 +93998,24 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
int destIfFalse = sqlite3VdbeMakeLabel(v);
int destIfNull = jumpIfNull ? dest : destIfFalse;
sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, dest);
+ sqlite3VdbeGoto(v, dest);
sqlite3VdbeResolveLabel(v, destIfFalse);
break;
}
#endif
default: {
- r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
- sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
- testcase( regFree1==0 );
- testcase( jumpIfNull==0 );
+ default_expr:
+ if( exprAlwaysTrue(pExpr) ){
+ sqlite3VdbeGoto(v, dest);
+ }else if( exprAlwaysFalse(pExpr) ){
+ /* No-op */
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
+ sqlite3VdbeAddOp3(v, OP_If, r1, dest, jumpIfNull!=0);
+ VdbeCoverage(v);
+ testcase( regFree1==0 );
+ testcase( jumpIfNull==0 );
+ }
break;
}
}
@@ -76308,7 +94040,7 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
int r1, r2;
assert( jumpIfNull==SQLITE_JUMPIFNULL || jumpIfNull==0 );
- if( NEVER(v==0) ) return; /* Existance of VDBE checked by caller */
+ if( NEVER(v==0) ) return; /* Existence of VDBE checked by caller */
if( pExpr==0 ) return;
/* The value of pExpr->op and op are related as follows:
@@ -76346,17 +94078,19 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
case TK_AND: {
testcase( jumpIfNull==0 );
sqlite3ExprIfFalse(pParse, pExpr->pLeft, dest, jumpIfNull);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_OR: {
int d2 = sqlite3VdbeMakeLabel(v);
testcase( jumpIfNull==0 );
- sqlite3ExprCachePush(pParse);
sqlite3ExprIfTrue(pParse, pExpr->pLeft, d2, jumpIfNull^SQLITE_JUMPIFNULL);
+ sqlite3ExprCachePush(pParse);
sqlite3ExprIfFalse(pParse, pExpr->pRight, dest, jumpIfNull);
sqlite3VdbeResolveLabel(v, d2);
- sqlite3ExprCachePop(pParse, 1);
+ sqlite3ExprCachePop(pParse);
break;
}
case TK_NOT: {
@@ -76364,52 +94098,51 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull);
break;
}
+ case TK_IS:
+ case TK_ISNOT:
+ testcase( pExpr->op==TK_IS );
+ testcase( pExpr->op==TK_ISNOT );
+ op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
+ jumpIfNull = SQLITE_NULLEQ;
+ /* Fall thru */
case TK_LT:
case TK_LE:
case TK_GT:
case TK_GE:
case TK_NE:
case TK_EQ: {
- testcase( op==TK_LT );
- testcase( op==TK_LE );
- testcase( op==TK_GT );
- testcase( op==TK_GE );
- testcase( op==TK_EQ );
- testcase( op==TK_NE );
+ if( sqlite3ExprIsVector(pExpr->pLeft) ) goto default_expr;
testcase( jumpIfNull==0 );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
r1, r2, dest, jumpIfNull);
- testcase( regFree1==0 );
- testcase( regFree2==0 );
- break;
- }
- case TK_IS:
- case TK_ISNOT: {
- testcase( pExpr->op==TK_IS );
- testcase( pExpr->op==TK_ISNOT );
- r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, &regFree2);
- op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ;
- codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op,
- r1, r2, dest, SQLITE_NULLEQ);
+ assert(TK_LT==OP_Lt); testcase(op==OP_Lt); VdbeCoverageIf(v,op==OP_Lt);
+ assert(TK_LE==OP_Le); testcase(op==OP_Le); VdbeCoverageIf(v,op==OP_Le);
+ assert(TK_GT==OP_Gt); testcase(op==OP_Gt); VdbeCoverageIf(v,op==OP_Gt);
+ assert(TK_GE==OP_Ge); testcase(op==OP_Ge); VdbeCoverageIf(v,op==OP_Ge);
+ assert(TK_EQ==OP_Eq); testcase(op==OP_Eq);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull!=SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Eq && jumpIfNull==SQLITE_NULLEQ);
+ assert(TK_NE==OP_Ne); testcase(op==OP_Ne);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull!=SQLITE_NULLEQ);
+ VdbeCoverageIf(v, op==OP_Ne && jumpIfNull==SQLITE_NULLEQ);
testcase( regFree1==0 );
testcase( regFree2==0 );
break;
}
case TK_ISNULL:
case TK_NOTNULL: {
- testcase( op==TK_ISNULL );
- testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
sqlite3VdbeAddOp2(v, op, r1, dest);
+ testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
+ testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
testcase( jumpIfNull==0 );
- exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull);
+ exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull);
break;
}
#ifndef SQLITE_OMIT_SUBQUERY
@@ -76425,10 +94158,18 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
}
#endif
default: {
- r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
- sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
- testcase( regFree1==0 );
- testcase( jumpIfNull==0 );
+ default_expr:
+ if( exprAlwaysFalse(pExpr) ){
+ sqlite3VdbeGoto(v, dest);
+ }else if( exprAlwaysTrue(pExpr) ){
+ /* no-op */
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pExpr, &regFree1);
+ sqlite3VdbeAddOp3(v, OP_IfNot, r1, dest, jumpIfNull!=0);
+ VdbeCoverage(v);
+ testcase( regFree1==0 );
+ testcase( jumpIfNull==0 );
+ }
break;
}
}
@@ -76437,11 +94178,32 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
}
/*
+** Like sqlite3ExprIfFalse() except that a copy is made of pExpr before
+** code generation, and that copy is deleted after code generation. This
+** ensures that the original pExpr is unchanged.
+*/
+SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int dest,int jumpIfNull){
+ sqlite3 *db = pParse->db;
+ Expr *pCopy = sqlite3ExprDup(db, pExpr, 0);
+ if( db->mallocFailed==0 ){
+ sqlite3ExprIfFalse(pParse, pCopy, dest, jumpIfNull);
+ }
+ sqlite3ExprDelete(db, pCopy);
+}
+
+
+/*
** Do a deep comparison of two expression trees. Return 0 if the two
** expressions are completely identical. Return 1 if they differ only
** by a COLLATE operator at the top level. Return 2 if there are differences
** other than the top-level COLLATE operator.
**
+** If any subelement of pB has Expr.iTable==(-1) then it is allowed
+** to compare equal to an equivalent element in pA with Expr.iTable==iTab.
+**
+** The pA side might be using TK_REGISTER. If that is the case and pB is
+** not using TK_REGISTER but is otherwise equivalent, then still return 0.
+**
** Sometimes this routine will return 2 even if the two expressions
** really are equivalent. If we cannot prove that the expressions are
** identical, we return 2 just to be safe. So if this routine
@@ -76452,33 +94214,46 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
** just might result in some slightly slower code. But returning
** an incorrect 0 or 1 could lead to a malfunction.
*/
-SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB){
- if( pA==0||pB==0 ){
+SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB, int iTab){
+ u32 combinedFlags;
+ if( pA==0 || pB==0 ){
return pB==pA ? 0 : 2;
}
- assert( !ExprHasAnyProperty(pA, EP_TokenOnly|EP_Reduced) );
- assert( !ExprHasAnyProperty(pB, EP_TokenOnly|EP_Reduced) );
- if( ExprHasProperty(pA, EP_xIsSelect) || ExprHasProperty(pB, EP_xIsSelect) ){
+ combinedFlags = pA->flags | pB->flags;
+ if( combinedFlags & EP_IntValue ){
+ if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){
+ return 0;
+ }
return 2;
}
- if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
- if( pA->op!=pB->op ) return 2;
- if( sqlite3ExprCompare(pA->pLeft, pB->pLeft) ) return 2;
- if( sqlite3ExprCompare(pA->pRight, pB->pRight) ) return 2;
- if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList) ) return 2;
- if( pA->iTable!=pB->iTable || pA->iColumn!=pB->iColumn ) return 2;
- if( ExprHasProperty(pA, EP_IntValue) ){
- if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){
- return 2;
+ if( pA->op!=pB->op ){
+ if( pA->op==TK_COLLATE && sqlite3ExprCompare(pA->pLeft, pB, iTab)<2 ){
+ return 1;
}
- }else if( pA->op!=TK_COLUMN && pA->u.zToken ){
- if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2;
- if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ){
- return 2;
+ if( pB->op==TK_COLLATE && sqlite3ExprCompare(pA, pB->pLeft, iTab)<2 ){
+ return 1;
+ }
+ return 2;
+ }
+ if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){
+ if( pA->op==TK_FUNCTION ){
+ if( sqlite3StrICmp(pA->u.zToken,pB->u.zToken)!=0 ) return 2;
+ }else if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){
+ return pA->op==TK_COLLATE ? 1 : 2;
+ }
+ }
+ if( (pA->flags & EP_Distinct)!=(pB->flags & EP_Distinct) ) return 2;
+ if( ALWAYS((combinedFlags & EP_TokenOnly)==0) ){
+ if( combinedFlags & EP_xIsSelect ) return 2;
+ if( sqlite3ExprCompare(pA->pLeft, pB->pLeft, iTab) ) return 2;
+ if( sqlite3ExprCompare(pA->pRight, pB->pRight, iTab) ) return 2;
+ if( sqlite3ExprListCompare(pA->x.pList, pB->x.pList, iTab) ) return 2;
+ if( ALWAYS((combinedFlags & EP_Reduced)==0) && pA->op!=TK_STRING ){
+ if( pA->iColumn!=pB->iColumn ) return 2;
+ if( pA->iTable!=pB->iTable
+ && (pA->iTable!=iTab || NEVER(pB->iTable>=0)) ) return 2;
}
}
- if( (pA->flags & EP_ExpCollate)!=(pB->flags & EP_ExpCollate) ) return 1;
- if( (pA->flags & EP_ExpCollate)!=0 && pA->pColl!=pB->pColl ) return 2;
return 0;
}
@@ -76486,6 +94261,9 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB){
** Compare two ExprList objects. Return 0 if they are identical and
** non-zero if they differ in any way.
**
+** If any subelement of pB has Expr.iTable==(-1) then it is allowed
+** to compare equal to an equivalent element in pA with Expr.iTable==iTab.
+**
** This routine might return non-zero for equivalent ExprLists. The
** only consequence will be disabled optimizations. But this routine
** must never return 0 if the two ExprList objects are different, or
@@ -76494,7 +94272,7 @@ SQLITE_PRIVATE int sqlite3ExprCompare(Expr *pA, Expr *pB){
** Two NULL pointers are considered to be the same. But a NULL pointer
** always differs from a non-NULL pointer.
*/
-SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
+SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB, int iTab){
int i;
if( pA==0 && pB==0 ) return 0;
if( pA==0 || pB==0 ) return 1;
@@ -76503,12 +94281,164 @@ SQLITE_PRIVATE int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){
Expr *pExprA = pA->a[i].pExpr;
Expr *pExprB = pB->a[i].pExpr;
if( pA->a[i].sortOrder!=pB->a[i].sortOrder ) return 1;
- if( sqlite3ExprCompare(pExprA, pExprB) ) return 1;
+ if( sqlite3ExprCompare(pExprA, pExprB, iTab) ) return 1;
+ }
+ return 0;
+}
+
+/*
+** Return true if we can prove the pE2 will always be true if pE1 is
+** true. Return false if we cannot complete the proof or if pE2 might
+** be false. Examples:
+**
+** pE1: x==5 pE2: x==5 Result: true
+** pE1: x>0 pE2: x==5 Result: false
+** pE1: x=21 pE2: x=21 OR y=43 Result: true
+** pE1: x!=123 pE2: x IS NOT NULL Result: true
+** pE1: x!=?1 pE2: x IS NOT NULL Result: true
+** pE1: x IS NULL pE2: x IS NOT NULL Result: false
+** pE1: x IS ?2 pE2: x IS NOT NULL Reuslt: false
+**
+** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
+** Expr.iTable<0 then assume a table number given by iTab.
+**
+** When in doubt, return false. Returning true might give a performance
+** improvement. Returning false might cause a performance reduction, but
+** it will always give the correct answer and is hence always safe.
+*/
+SQLITE_PRIVATE int sqlite3ExprImpliesExpr(Expr *pE1, Expr *pE2, int iTab){
+ if( sqlite3ExprCompare(pE1, pE2, iTab)==0 ){
+ return 1;
+ }
+ if( pE2->op==TK_OR
+ && (sqlite3ExprImpliesExpr(pE1, pE2->pLeft, iTab)
+ || sqlite3ExprImpliesExpr(pE1, pE2->pRight, iTab) )
+ ){
+ return 1;
+ }
+ if( pE2->op==TK_NOTNULL
+ && sqlite3ExprCompare(pE1->pLeft, pE2->pLeft, iTab)==0
+ && (pE1->op!=TK_ISNULL && pE1->op!=TK_IS)
+ ){
+ return 1;
}
return 0;
}
/*
+** An instance of the following structure is used by the tree walker
+** to determine if an expression can be evaluated by reference to the
+** index only, without having to do a search for the corresponding
+** table entry. The IdxCover.pIdx field is the index. IdxCover.iCur
+** is the cursor for the table.
+*/
+struct IdxCover {
+ Index *pIdx; /* The index to be tested for coverage */
+ int iCur; /* Cursor number for the table corresponding to the index */
+};
+
+/*
+** Check to see if there are references to columns in table
+** pWalker->u.pIdxCover->iCur can be satisfied using the index
+** pWalker->u.pIdxCover->pIdx.
+*/
+static int exprIdxCover(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN
+ && pExpr->iTable==pWalker->u.pIdxCover->iCur
+ && sqlite3ColumnOfIndex(pWalker->u.pIdxCover->pIdx, pExpr->iColumn)<0
+ ){
+ pWalker->eCode = 1;
+ return WRC_Abort;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Determine if an index pIdx on table with cursor iCur contains will
+** the expression pExpr. Return true if the index does cover the
+** expression and false if the pExpr expression references table columns
+** that are not found in the index pIdx.
+**
+** An index covering an expression means that the expression can be
+** evaluated using only the index and without having to lookup the
+** corresponding table entry.
+*/
+SQLITE_PRIVATE int sqlite3ExprCoveredByIndex(
+ Expr *pExpr, /* The index to be tested */
+ int iCur, /* The cursor number for the corresponding table */
+ Index *pIdx /* The index that might be used for coverage */
+){
+ Walker w;
+ struct IdxCover xcov;
+ memset(&w, 0, sizeof(w));
+ xcov.iCur = iCur;
+ xcov.pIdx = pIdx;
+ w.xExprCallback = exprIdxCover;
+ w.u.pIdxCover = &xcov;
+ sqlite3WalkExpr(&w, pExpr);
+ return !w.eCode;
+}
+
+
+/*
+** An instance of the following structure is used by the tree walker
+** to count references to table columns in the arguments of an
+** aggregate function, in order to implement the
+** sqlite3FunctionThisSrc() routine.
+*/
+struct SrcCount {
+ SrcList *pSrc; /* One particular FROM clause in a nested query */
+ int nThis; /* Number of references to columns in pSrcList */
+ int nOther; /* Number of references to columns in other FROM clauses */
+};
+
+/*
+** Count the number of references to columns.
+*/
+static int exprSrcCount(Walker *pWalker, Expr *pExpr){
+ /* The NEVER() on the second term is because sqlite3FunctionUsesThisSrc()
+ ** is always called before sqlite3ExprAnalyzeAggregates() and so the
+ ** TK_COLUMNs have not yet been converted into TK_AGG_COLUMN. If
+ ** sqlite3FunctionUsesThisSrc() is used differently in the future, the
+ ** NEVER() will need to be removed. */
+ if( pExpr->op==TK_COLUMN || NEVER(pExpr->op==TK_AGG_COLUMN) ){
+ int i;
+ struct SrcCount *p = pWalker->u.pSrcCount;
+ SrcList *pSrc = p->pSrc;
+ int nSrc = pSrc ? pSrc->nSrc : 0;
+ for(i=0; i<nSrc; i++){
+ if( pExpr->iTable==pSrc->a[i].iCursor ) break;
+ }
+ if( i<nSrc ){
+ p->nThis++;
+ }else{
+ p->nOther++;
+ }
+ }
+ return WRC_Continue;
+}
+
+/*
+** Determine if any of the arguments to the pExpr Function reference
+** pSrcList. Return true if they do. Also return true if the function
+** has no arguments or has only constant arguments. Return false if pExpr
+** references columns but not columns of tables found in pSrcList.
+*/
+SQLITE_PRIVATE int sqlite3FunctionUsesThisSrc(Expr *pExpr, SrcList *pSrcList){
+ Walker w;
+ struct SrcCount cnt;
+ assert( pExpr->op==TK_AGG_FUNCTION );
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = exprSrcCount;
+ w.u.pSrcCount = &cnt;
+ cnt.pSrc = pSrcList;
+ cnt.nThis = 0;
+ cnt.nOther = 0;
+ sqlite3WalkExprList(&w, pExpr->x.pList);
+ return cnt.nThis>0 || cnt.nOther==0;
+}
+
+/*
** Add a new element to the pAggInfo->aCol[] array. Return the index of
** the new element. Return a negative number if malloc fails.
*/
@@ -76518,9 +94448,7 @@ static int addAggInfoColumn(sqlite3 *db, AggInfo *pInfo){
db,
pInfo->aCol,
sizeof(pInfo->aCol[0]),
- 3,
&pInfo->nColumn,
- &pInfo->nColumnAlloc,
&i
);
return i;
@@ -76536,9 +94464,7 @@ static int addAggInfoFunc(sqlite3 *db, AggInfo *pInfo){
db,
pInfo->aFunc,
sizeof(pInfo->aFunc[0]),
- 3,
&pInfo->nFunc,
- &pInfo->nFuncAlloc,
&i
);
return i;
@@ -76567,7 +94493,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
struct SrcList_item *pItem = pSrcList->a;
for(i=0; i<pSrcList->nSrc; i++, pItem++){
struct AggInfo_col *pCol;
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
if( pExpr->iTable==pItem->iCursor ){
/* If we reach this point, it means that pExpr refers to a table
** that is in the FROM clause of the aggregate query.
@@ -76616,7 +94542,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
** Convert the pExpr to be a TK_AGG_COLUMN referring to that
** pAggInfo->aCol[] entry.
*/
- ExprSetIrreducible(pExpr);
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->pAggInfo = pAggInfo;
pExpr->op = TK_AGG_COLUMN;
pExpr->iAgg = (i16)k;
@@ -76627,15 +94553,15 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
return WRC_Prune;
}
case TK_AGG_FUNCTION: {
- /* The pNC->nDepth==0 test causes aggregate functions in subqueries
- ** to be ignored */
- if( pNC->nDepth==0 ){
+ if( (pNC->ncFlags & NC_InAggFunc)==0
+ && pWalker->walkerDepth==pExpr->op2
+ ){
/* Check to see if pExpr is a duplicate of another aggregate
** function that is already in the pAggInfo structure
*/
struct AggInfo_func *pItem = pAggInfo->aFunc;
for(i=0; i<pAggInfo->nFunc; i++, pItem++){
- if( sqlite3ExprCompare(pItem->pExpr, pExpr)==0 ){
+ if( sqlite3ExprCompare(pItem->pExpr, pExpr, -1)==0 ){
break;
}
}
@@ -76651,7 +94577,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
pItem->iMem = ++pParse->nMem;
assert( !ExprHasProperty(pExpr, EP_IntValue) );
pItem->pFunc = sqlite3FindFunction(pParse->db,
- pExpr->u.zToken, sqlite3Strlen30(pExpr->u.zToken),
+ pExpr->u.zToken,
pExpr->x.pList ? pExpr->x.pList->nExpr : 0, enc, 0);
if( pExpr->flags & EP_Distinct ){
pItem->iDistinct = pParse->nTab++;
@@ -76662,38 +94588,36 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){
}
/* Make pExpr point to the appropriate pAggInfo->aFunc[] entry
*/
- assert( !ExprHasAnyProperty(pExpr, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(pExpr);
+ assert( !ExprHasProperty(pExpr, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(pExpr, EP_NoReduce);
pExpr->iAgg = (i16)i;
pExpr->pAggInfo = pAggInfo;
return WRC_Prune;
+ }else{
+ return WRC_Continue;
}
}
}
return WRC_Continue;
}
static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){
- NameContext *pNC = pWalker->u.pNC;
- if( pNC->nDepth==0 ){
- pNC->nDepth++;
- sqlite3WalkSelect(pWalker, pSelect);
- pNC->nDepth--;
- return WRC_Prune;
- }else{
- return WRC_Continue;
- }
+ UNUSED_PARAMETER(pWalker);
+ UNUSED_PARAMETER(pSelect);
+ return WRC_Continue;
}
/*
-** Analyze the given expression looking for aggregate functions and
-** for variables that need to be added to the pParse->aAgg[] array.
-** Make additional entries to the pParse->aAgg[] array as necessary.
+** Analyze the pExpr expression looking for aggregate functions and
+** for variables that need to be added to AggInfo object that pNC->pAggInfo
+** points to. Additional entries are made on the AggInfo object as
+** necessary.
**
** This routine should only be called after the expression has been
** analyzed by sqlite3ResolveExprNames().
*/
SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){
Walker w;
+ memset(&w, 0, sizeof(w));
w.xExprCallback = analyzeAggregate;
w.xSelectCallback = analyzeAggregatesInSelect;
w.u.pNC = pNC;
@@ -76732,14 +94656,14 @@ SQLITE_PRIVATE int sqlite3GetTempReg(Parse *pParse){
** purpose.
**
** If a register is currently being used by the column cache, then
-** the dallocation is deferred until the column cache line that uses
+** the deallocation is deferred until the column cache line that uses
** the register becomes stale.
*/
SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
if( iReg && pParse->nTempReg<ArraySize(pParse->aTempReg) ){
int i;
struct yColCache *p;
- for(i=0, p=pParse->aColCache; i<SQLITE_N_COLCACHE; i++, p++){
+ for(i=0, p=pParse->aColCache; i<pParse->nColCache; i++, p++){
if( p->iReg==iReg ){
p->tempReg = 1;
return;
@@ -76750,10 +94674,11 @@ SQLITE_PRIVATE void sqlite3ReleaseTempReg(Parse *pParse, int iReg){
}
/*
-** Allocate or deallocate a block of nReg consecutive registers
+** Allocate or deallocate a block of nReg consecutive registers.
*/
SQLITE_PRIVATE int sqlite3GetTempRange(Parse *pParse, int nReg){
int i, n;
+ if( nReg==1 ) return sqlite3GetTempReg(pParse);
i = pParse->iRangeReg;
n = pParse->nRangeReg;
if( nReg<=n ){
@@ -76767,6 +94692,10 @@ SQLITE_PRIVATE int sqlite3GetTempRange(Parse *pParse, int nReg){
return i;
}
SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
+ if( nReg==1 ){
+ sqlite3ReleaseTempReg(pParse, iReg);
+ return;
+ }
sqlite3ExprCacheRemove(pParse, iReg, nReg);
if( nReg>pParse->nRangeReg ){
pParse->nRangeReg = nReg;
@@ -76774,6 +94703,37 @@ SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
}
}
+/*
+** Mark all temporary registers as being unavailable for reuse.
+*/
+SQLITE_PRIVATE void sqlite3ClearTempRegCache(Parse *pParse){
+ pParse->nTempReg = 0;
+ pParse->nRangeReg = 0;
+}
+
+/*
+** Validate that no temporary register falls within the range of
+** iFirst..iLast, inclusive. This routine is only call from within assert()
+** statements.
+*/
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NoTempsInRange(Parse *pParse, int iFirst, int iLast){
+ int i;
+ if( pParse->nRangeReg>0
+ && pParse->iRangeReg+pParse->nRangeReg<iLast
+ && pParse->iRangeReg>=iFirst
+ ){
+ return 0;
+ }
+ for(i=0; i<pParse->nTempReg; i++){
+ if( pParse->aTempReg[i]>=iFirst && pParse->aTempReg[i]<=iLast ){
+ return 0;
+ }
+ }
+ return 1;
+}
+#endif /* SQLITE_DEBUG */
+
/************** End of expr.c ************************************************/
/************** Begin file alter.c *******************************************/
/*
@@ -76790,6 +94750,7 @@ SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){
** This file contains C code routines that used to generate VDBE code
** that implements the ALTER TABLE command.
*/
+/* #include "sqliteInt.h" */
/*
** The code in this file only exists if we are not omitting the
@@ -76854,8 +94815,8 @@ static void renameTableFunc(
assert( len>0 );
} while( token!=TK_LP && token!=TK_USING );
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql,
- zTableName, tname.z+tname.n);
+ zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
+ zSql, zTableName, tname.z+tname.n);
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
}
}
@@ -76893,6 +94854,7 @@ static void renameParentFunc(
int token; /* Type of token */
UNUSED_PARAMETER(NotUsed);
+ if( zInput==0 || zOld==0 ) return;
for(z=zInput; *z; z=z+n){
n = sqlite3GetToken(z, &token);
if( token==TK_REFERENCES ){
@@ -76902,12 +94864,13 @@ static void renameParentFunc(
n = sqlite3GetToken(z, &token);
}while( token==TK_SPACE );
+ if( token==TK_ILLEGAL ) break;
zParent = sqlite3DbStrNDup(db, (const char *)z, n);
if( zParent==0 ) break;
sqlite3Dequote(zParent);
if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){
char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"",
- (zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew
+ (zOutput?zOutput:""), (int)(z-zInput), zInput, (const char *)zNew
);
sqlite3DbFree(db, zOutput);
zOutput = zOut;
@@ -76950,8 +94913,8 @@ static void renameTriggerFunc(
UNUSED_PARAMETER(NotUsed);
/* The principle used to locate the table name in the CREATE TRIGGER
- ** statement is that the table name is the first token that is immediatedly
- ** preceded by either TK_ON or TK_DOT and immediatedly followed by one
+ ** statement is that the table name is the first token that is immediately
+ ** preceded by either TK_ON or TK_DOT and immediately followed by one
** of TK_WHEN, TK_BEGIN or TK_FOR.
*/
if( zSql ){
@@ -76993,8 +94956,8 @@ static void renameTriggerFunc(
/* Variable tname now contains the token that is the old table-name
** in the CREATE TRIGGER statement.
*/
- zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", ((u8*)tname.z) - zSql, zSql,
- zTableName, tname.z+tname.n);
+ zRet = sqlite3MPrintf(db, "%.*s\"%w\"%s", (int)(((u8*)tname.z) - zSql),
+ zSql, zTableName, tname.z+tname.n);
sqlite3_result_text(context, zRet, -1, SQLITE_DYNAMIC);
}
}
@@ -77004,7 +94967,7 @@ static void renameTriggerFunc(
** Register built-in functions used to help implement ALTER TABLE
*/
SQLITE_PRIVATE void sqlite3AlterFunctions(void){
- static SQLITE_WSD FuncDef aAlterTableFuncs[] = {
+ static FuncDef aAlterTableFuncs[] = {
FUNCTION(sqlite_rename_table, 2, 0, 0, renameTableFunc),
#ifndef SQLITE_OMIT_TRIGGER
FUNCTION(sqlite_rename_trigger, 2, 0, 0, renameTriggerFunc),
@@ -77013,13 +94976,7 @@ SQLITE_PRIVATE void sqlite3AlterFunctions(void){
FUNCTION(sqlite_rename_parent, 3, 0, 0, renameParentFunc),
#endif
};
- int i;
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
- FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aAlterTableFuncs);
-
- for(i=0; i<ArraySize(aAlterTableFuncs); i++){
- sqlite3FuncDefInsert(pHash, &aFunc[i]);
- }
+ sqlite3InsertBuiltinFuncs(aAlterTableFuncs, ArraySize(aAlterTableFuncs));
}
/*
@@ -77191,10 +95148,10 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
assert( pSrc->nSrc==1 );
assert( sqlite3BtreeHoldsAllMutexes(pParse->db) );
- pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase);
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_rename_table;
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
db->flags |= SQLITE_PreferBuiltin;
/* Get a NULL terminated version of the new table name. */
@@ -77246,7 +95203,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
}
#endif
- /* Begin a transaction and code the VerifyCookie for database iDb.
+ /* Begin a transaction for database iDb.
** Then modify the schema cookie (since the ALTER TABLE modifies the
** schema). Open a statement transaction if the table is a virtual
** table.
@@ -77266,7 +95223,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pVTab ){
int i = ++pParse->nMem;
- sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0);
+ sqlite3VdbeLoadString(v, i, zName);
sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB);
sqlite3MayAbort(pParse);
}
@@ -77307,7 +95264,7 @@ SQLITE_PRIVATE void sqlite3AlterRenameTable(
"WHEN name LIKE 'sqlite_autoindex%%' AND type='index' THEN "
"'sqlite_autoindex_' || %Q || substr(name,%d+18) "
"ELSE name END "
- "WHERE tbl_name=%Q AND "
+ "WHERE tbl_name=%Q COLLATE nocase AND "
"(type='table' OR type='index' OR type='trigger');",
zDb, SCHEMA_TABLE(iDb), zName, zName, zName,
#ifndef SQLITE_OMIT_TRIGGER
@@ -77363,32 +95320,6 @@ exit_rename_table:
db->flags = savedDbFlags;
}
-
-/*
-** Generate code to make sure the file format number is at least minFormat.
-** The generated code will increase the file format number if necessary.
-*/
-SQLITE_PRIVATE void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){
- Vdbe *v;
- v = sqlite3GetVdbe(pParse);
- /* The VDBE should have been allocated before this routine is called.
- ** If that allocation failed, we would have quit before reaching this
- ** point */
- if( ALWAYS(v) ){
- int r1 = sqlite3GetTempReg(pParse);
- int r2 = sqlite3GetTempReg(pParse);
- int j1;
- sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
- sqlite3VdbeUsesBtree(v, iDb);
- sqlite3VdbeAddOp2(v, OP_Integer, minFormat, r2);
- j1 = sqlite3VdbeAddOp3(v, OP_Ge, r2, 0, r1);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, r2);
- sqlite3VdbeJumpHere(v, j1);
- sqlite3ReleaseTempReg(pParse, r1);
- sqlite3ReleaseTempReg(pParse, r2);
- }
-}
-
/*
** This function is called after an "ALTER TABLE ... ADD" statement
** has been parsed. Argument pColDef contains the text of the new
@@ -77407,15 +95338,18 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
Column *pCol; /* The new column */
Expr *pDflt; /* Default value for the new column */
sqlite3 *db; /* The database connection; */
+ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
+ int r1; /* Temporary registers */
db = pParse->db;
if( pParse->nErr || db->mallocFailed ) return;
+ assert( v!=0 );
pNew = pParse->pNewTable;
assert( pNew );
assert( sqlite3BtreeHoldsAllMutexes(db) );
iDb = sqlite3SchemaToIndex(db, pNew->pSchema);
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
zTab = &pNew->zName[16]; /* Skip the "sqlite_altertab_" prefix on the name */
pCol = &pNew->aCol[pNew->nCol-1];
pDflt = pCol->pDflt;
@@ -77433,7 +95367,8 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
** literal NULL, then set pDflt to 0. This simplifies checking
** for an SQL NULL default below.
*/
- if( pDflt && pDflt->op==TK_NULL ){
+ assert( pDflt==0 || pDflt->op==TK_SPAN );
+ if( pDflt && pDflt->pLeft->op==TK_NULL ){
pDflt = 0;
}
@@ -77441,7 +95376,7 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
** If there is a NOT NULL constraint, then the default value for the
** column must not be NULL.
*/
- if( pCol->isPrimKey ){
+ if( pCol->colFlags & COLFLAG_PRIMKEY ){
sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
return;
}
@@ -77464,9 +95399,12 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
** can handle (i.e. not CURRENT_TIME etc.)
*/
if( pDflt ){
- sqlite3_value *pVal;
- if( sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_NONE, &pVal) ){
- db->mallocFailed = 1;
+ sqlite3_value *pVal = 0;
+ int rc;
+ rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
+ assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
+ if( rc!=SQLITE_OK ){
+ assert( db->mallocFailed == 1 );
return;
}
if( !pVal ){
@@ -77496,11 +95434,18 @@ SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *pParse, Token *pColDef){
db->flags = savedDbFlags;
}
- /* If the default value of the new column is NULL, then set the file
- ** format to 2. If the default value of the new column is not NULL,
- ** the file format becomes 3.
+ /* Make sure the schema version is at least 3. But do not upgrade
+ ** from less than 3 to 4, as that will corrupt any preexisting DESC
+ ** index.
*/
- sqlite3MinimumFileFormat(pParse, iDb, pDflt ? 3 : 2);
+ r1 = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, r1, BTREE_FILE_FORMAT);
+ sqlite3VdbeUsesBtree(v, iDb);
+ sqlite3VdbeAddOp2(v, OP_AddImm, r1, -2);
+ sqlite3VdbeAddOp2(v, OP_IfPos, r1, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, 3);
+ sqlite3ReleaseTempReg(pParse, r1);
/* Reload the schema of the modified table. */
reloadTableSchema(pParse, pTab, pTab->zName);
@@ -77534,7 +95479,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
assert( pParse->pNewTable==0 );
assert( sqlite3BtreeHoldsAllMutexes(db) );
if( db->mallocFailed ) goto exit_begin_add_column;
- pTab = sqlite3LocateTable(pParse, 0, pSrc->a[0].zName, pSrc->a[0].zDatabase);
+ pTab = sqlite3LocateTableItem(pParse, 0, &pSrc->a[0]);
if( !pTab ) goto exit_begin_add_column;
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -77574,7 +95519,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
if( !pNew->aCol || !pNew->zName ){
- db->mallocFailed = 1;
+ assert( db->mallocFailed );
goto exit_begin_add_column;
}
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
@@ -77582,9 +95527,7 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
Column *pCol = &pNew->aCol[i];
pCol->zName = sqlite3DbStrDup(db, pCol->zName);
pCol->zColl = 0;
- pCol->zType = 0;
pCol->pDflt = 0;
- pCol->zDflt = 0;
}
pNew->pSchema = db->aDb[iDb].pSchema;
pNew->addColOffset = pTab->addColOffset;
@@ -77605,7 +95548,7 @@ exit_begin_add_column:
/************** End of alter.c ***********************************************/
/************** Begin file analyze.c *****************************************/
/*
-** 2005 July 8
+** 2005-07-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -77616,24 +95559,164 @@ exit_begin_add_column:
**
*************************************************************************
** This file contains code associated with the ANALYZE command.
+**
+** The ANALYZE command gather statistics about the content of tables
+** and indices. These statistics are made available to the query planner
+** to help it make better decisions about how to perform queries.
+**
+** The following system tables are or have been supported:
+**
+** CREATE TABLE sqlite_stat1(tbl, idx, stat);
+** CREATE TABLE sqlite_stat2(tbl, idx, sampleno, sample);
+** CREATE TABLE sqlite_stat3(tbl, idx, nEq, nLt, nDLt, sample);
+** CREATE TABLE sqlite_stat4(tbl, idx, nEq, nLt, nDLt, sample);
+**
+** Additional tables might be added in future releases of SQLite.
+** The sqlite_stat2 table is not created or used unless the SQLite version
+** is between 3.6.18 and 3.7.8, inclusive, and unless SQLite is compiled
+** with SQLITE_ENABLE_STAT2. The sqlite_stat2 table is deprecated.
+** The sqlite_stat2 table is superseded by sqlite_stat3, which is only
+** created and used by SQLite versions 3.7.9 and later and with
+** SQLITE_ENABLE_STAT3 defined. The functionality of sqlite_stat3
+** is a superset of sqlite_stat2. The sqlite_stat4 is an enhanced
+** version of sqlite_stat3 and is only available when compiled with
+** SQLITE_ENABLE_STAT4 and in SQLite versions 3.8.1 and later. It is
+** not possible to enable both STAT3 and STAT4 at the same time. If they
+** are both enabled, then STAT4 takes precedence.
+**
+** For most applications, sqlite_stat1 provides all the statistics required
+** for the query planner to make good choices.
+**
+** Format of sqlite_stat1:
+**
+** There is normally one row per index, with the index identified by the
+** name in the idx column. The tbl column is the name of the table to
+** which the index belongs. In each such row, the stat column will be
+** a string consisting of a list of integers. The first integer in this
+** list is the number of rows in the index. (This is the same as the
+** number of rows in the table, except for partial indices.) The second
+** integer is the average number of rows in the index that have the same
+** value in the first column of the index. The third integer is the average
+** number of rows in the index that have the same value for the first two
+** columns. The N-th integer (for N>1) is the average number of rows in
+** the index which have the same value for the first N-1 columns. For
+** a K-column index, there will be K+1 integers in the stat column. If
+** the index is unique, then the last integer will be 1.
+**
+** The list of integers in the stat column can optionally be followed
+** by the keyword "unordered". The "unordered" keyword, if it is present,
+** must be separated from the last integer by a single space. If the
+** "unordered" keyword is present, then the query planner assumes that
+** the index is unordered and will not use the index for a range query.
+**
+** If the sqlite_stat1.idx column is NULL, then the sqlite_stat1.stat
+** column contains a single integer which is the (estimated) number of
+** rows in the table identified by sqlite_stat1.tbl.
+**
+** Format of sqlite_stat2:
+**
+** The sqlite_stat2 is only created and is only used if SQLite is compiled
+** with SQLITE_ENABLE_STAT2 and if the SQLite version number is between
+** 3.6.18 and 3.7.8. The "stat2" table contains additional information
+** about the distribution of keys within an index. The index is identified by
+** the "idx" column and the "tbl" column is the name of the table to which
+** the index belongs. There are usually 10 rows in the sqlite_stat2
+** table for each index.
+**
+** The sqlite_stat2 entries for an index that have sampleno between 0 and 9
+** inclusive are samples of the left-most key value in the index taken at
+** evenly spaced points along the index. Let the number of samples be S
+** (10 in the standard build) and let C be the number of rows in the index.
+** Then the sampled rows are given by:
+**
+** rownumber = (i*C*2 + C)/(S*2)
+**
+** For i between 0 and S-1. Conceptually, the index space is divided into
+** S uniform buckets and the samples are the middle row from each bucket.
+**
+** The format for sqlite_stat2 is recorded here for legacy reference. This
+** version of SQLite does not support sqlite_stat2. It neither reads nor
+** writes the sqlite_stat2 table. This version of SQLite only supports
+** sqlite_stat3.
+**
+** Format for sqlite_stat3:
+**
+** The sqlite_stat3 format is a subset of sqlite_stat4. Hence, the
+** sqlite_stat4 format will be described first. Further information
+** about sqlite_stat3 follows the sqlite_stat4 description.
+**
+** Format for sqlite_stat4:
+**
+** As with sqlite_stat2, the sqlite_stat4 table contains histogram data
+** to aid the query planner in choosing good indices based on the values
+** that indexed columns are compared against in the WHERE clauses of
+** queries.
+**
+** The sqlite_stat4 table contains multiple entries for each index.
+** The idx column names the index and the tbl column is the table of the
+** index. If the idx and tbl columns are the same, then the sample is
+** of the INTEGER PRIMARY KEY. The sample column is a blob which is the
+** binary encoding of a key from the index. The nEq column is a
+** list of integers. The first integer is the approximate number
+** of entries in the index whose left-most column exactly matches
+** the left-most column of the sample. The second integer in nEq
+** is the approximate number of entries in the index where the
+** first two columns match the first two columns of the sample.
+** And so forth. nLt is another list of integers that show the approximate
+** number of entries that are strictly less than the sample. The first
+** integer in nLt contains the number of entries in the index where the
+** left-most column is less than the left-most column of the sample.
+** The K-th integer in the nLt entry is the number of index entries
+** where the first K columns are less than the first K columns of the
+** sample. The nDLt column is like nLt except that it contains the
+** number of distinct entries in the index that are less than the
+** sample.
+**
+** There can be an arbitrary number of sqlite_stat4 entries per index.
+** The ANALYZE command will typically generate sqlite_stat4 tables
+** that contain between 10 and 40 samples which are distributed across
+** the key space, though not uniformly, and which include samples with
+** large nEq values.
+**
+** Format for sqlite_stat3 redux:
+**
+** The sqlite_stat3 table is like sqlite_stat4 except that it only
+** looks at the left-most column of the index. The sqlite_stat3.sample
+** column contains the actual value of the left-most column instead
+** of a blob encoding of the complete index key as is found in
+** sqlite_stat4.sample. The nEq, nLt, and nDLt entries of sqlite_stat3
+** all contain just a single integer which is the same as the first
+** integer in the equivalent columns in sqlite_stat4.
*/
#ifndef SQLITE_OMIT_ANALYZE
+/* #include "sqliteInt.h" */
+
+#if defined(SQLITE_ENABLE_STAT4)
+# define IsStat4 1
+# define IsStat3 0
+#elif defined(SQLITE_ENABLE_STAT3)
+# define IsStat4 0
+# define IsStat3 1
+#else
+# define IsStat4 0
+# define IsStat3 0
+# undef SQLITE_STAT4_SAMPLES
+# define SQLITE_STAT4_SAMPLES 1
+#endif
+#define IsStat34 (IsStat3+IsStat4) /* 1 for STAT3 or STAT4. 0 otherwise */
/*
-** This routine generates code that opens the sqlite_stat1 table for
-** writing with cursor iStatCur. If the library was built with the
-** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is
-** opened for writing using cursor (iStatCur+1)
+** This routine generates code that opens the sqlite_statN tables.
+** The sqlite_stat1 table is always relevant. sqlite_stat2 is now
+** obsolete. sqlite_stat3 and sqlite_stat4 are only opened when
+** appropriate compile-time options are provided.
**
-** If the sqlite_stat1 tables does not previously exist, it is created.
-** Similarly, if the sqlite_stat2 table does not exist and the library
-** is compiled with SQLITE_ENABLE_STAT2 defined, it is created.
+** If the sqlite_statN tables do not previously exist, it is created.
**
** Argument zWhere may be a pointer to a buffer containing a table name,
** or it may be a NULL pointer. If it is not NULL, then all entries in
-** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated
-** with the named table are deleted. If zWhere==0, then code is generated
-** to delete all stat table entries.
+** the sqlite_statN tables associated with the named table are deleted.
+** If zWhere==0, then code is generated to delete all stat table entries.
*/
static void openStatTable(
Parse *pParse, /* Parsing context */
@@ -77647,60 +95730,766 @@ static void openStatTable(
const char *zCols;
} aTable[] = {
{ "sqlite_stat1", "tbl,idx,stat" },
-#ifdef SQLITE_ENABLE_STAT2
- { "sqlite_stat2", "tbl,idx,sampleno,sample" },
+#if defined(SQLITE_ENABLE_STAT4)
+ { "sqlite_stat4", "tbl,idx,neq,nlt,ndlt,sample" },
+ { "sqlite_stat3", 0 },
+#elif defined(SQLITE_ENABLE_STAT3)
+ { "sqlite_stat3", "tbl,idx,neq,nlt,ndlt,sample" },
+ { "sqlite_stat4", 0 },
+#else
+ { "sqlite_stat3", 0 },
+ { "sqlite_stat4", 0 },
#endif
};
-
- int aRoot[] = {0, 0};
- u8 aCreateTbl[] = {0, 0};
-
int i;
sqlite3 *db = pParse->db;
Db *pDb;
Vdbe *v = sqlite3GetVdbe(pParse);
+ int aRoot[ArraySize(aTable)];
+ u8 aCreateTbl[ArraySize(aTable)];
+
if( v==0 ) return;
assert( sqlite3BtreeHoldsAllMutexes(db) );
assert( sqlite3VdbeDb(v)==db );
pDb = &db->aDb[iDb];
+ /* Create new statistic tables if they do not exist, or clear them
+ ** if they do already exist.
+ */
for(i=0; i<ArraySize(aTable); i++){
const char *zTab = aTable[i].zName;
Table *pStat;
- if( (pStat = sqlite3FindTable(db, zTab, pDb->zName))==0 ){
- /* The sqlite_stat[12] table does not exist. Create it. Note that a
- ** side-effect of the CREATE TABLE statement is to leave the rootpage
- ** of the new table in register pParse->regRoot. This is important
- ** because the OpenWrite opcode below will be needing it. */
- sqlite3NestedParse(pParse,
- "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols
- );
- aRoot[i] = pParse->regRoot;
- aCreateTbl[i] = 1;
+ if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
+ if( aTable[i].zCols ){
+ /* The sqlite_statN table does not exist. Create it. Note that a
+ ** side-effect of the CREATE TABLE statement is to leave the rootpage
+ ** of the new table in register pParse->regRoot. This is important
+ ** because the OpenWrite opcode below will be needing it. */
+ sqlite3NestedParse(pParse,
+ "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
+ );
+ aRoot[i] = pParse->regRoot;
+ aCreateTbl[i] = OPFLAG_P2ISREG;
+ }
}else{
/* The table already exists. If zWhere is not NULL, delete all entries
** associated with the table zWhere. If zWhere is NULL, delete the
** entire contents of the table. */
aRoot[i] = pStat->tnum;
+ aCreateTbl[i] = 0;
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
if( zWhere ){
sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE %s=%Q", pDb->zName, zTab, zWhereType, zWhere
+ "DELETE FROM %Q.%s WHERE %s=%Q",
+ pDb->zDbSName, zTab, zWhereType, zWhere
);
}else{
- /* The sqlite_stat[12] table already exists. Delete all rows. */
+ /* The sqlite_stat[134] table already exists. Delete all rows. */
sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
}
}
}
- /* Open the sqlite_stat[12] tables for writing. */
- for(i=0; i<ArraySize(aTable); i++){
- sqlite3VdbeAddOp3(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb);
- sqlite3VdbeChangeP4(v, -1, (char *)3, P4_INT32);
+ /* Open the sqlite_stat[134] tables for writing. */
+ for(i=0; aTable[i].zCols; i++){
+ assert( i<ArraySize(aTable) );
+ sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
+ VdbeComment((v, aTable[i].zName));
+ }
+}
+
+/*
+** Recommended number of samples for sqlite_stat4
+*/
+#ifndef SQLITE_STAT4_SAMPLES
+# define SQLITE_STAT4_SAMPLES 24
+#endif
+
+/*
+** Three SQL functions - stat_init(), stat_push(), and stat_get() -
+** share an instance of the following structure to hold their state
+** information.
+*/
+typedef struct Stat4Accum Stat4Accum;
+typedef struct Stat4Sample Stat4Sample;
+struct Stat4Sample {
+ tRowcnt *anEq; /* sqlite_stat4.nEq */
+ tRowcnt *anDLt; /* sqlite_stat4.nDLt */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ tRowcnt *anLt; /* sqlite_stat4.nLt */
+ union {
+ i64 iRowid; /* Rowid in main table of the key */
+ u8 *aRowid; /* Key for WITHOUT ROWID tables */
+ } u;
+ u32 nRowid; /* Sizeof aRowid[] */
+ u8 isPSample; /* True if a periodic sample */
+ int iCol; /* If !isPSample, the reason for inclusion */
+ u32 iHash; /* Tiebreaker hash */
+#endif
+};
+struct Stat4Accum {
+ tRowcnt nRow; /* Number of rows in the entire table */
+ tRowcnt nPSample; /* How often to do a periodic sample */
+ int nCol; /* Number of columns in index + pk/rowid */
+ int nKeyCol; /* Number of index columns w/o the pk/rowid */
+ int mxSample; /* Maximum number of samples to accumulate */
+ Stat4Sample current; /* Current row as a Stat4Sample */
+ u32 iPrn; /* Pseudo-random number used for sampling */
+ Stat4Sample *aBest; /* Array of nCol best samples */
+ int iMin; /* Index in a[] of entry with minimum score */
+ int nSample; /* Current number of samples */
+ int iGet; /* Index of current sample accessed by stat_get() */
+ Stat4Sample *a; /* Array of mxSample Stat4Sample objects */
+ sqlite3 *db; /* Database connection, for malloc() */
+};
+
+/* Reclaim memory used by a Stat4Sample
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleClear(sqlite3 *db, Stat4Sample *p){
+ assert( db!=0 );
+ if( p->nRowid ){
+ sqlite3DbFree(db, p->u.aRowid);
+ p->nRowid = 0;
}
}
+#endif
+
+/* Initialize the BLOB value of a ROWID
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleSetRowid(sqlite3 *db, Stat4Sample *p, int n, const u8 *pData){
+ assert( db!=0 );
+ if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
+ p->u.aRowid = sqlite3DbMallocRawNN(db, n);
+ if( p->u.aRowid ){
+ p->nRowid = n;
+ memcpy(p->u.aRowid, pData, n);
+ }else{
+ p->nRowid = 0;
+ }
+}
+#endif
+
+/* Initialize the INTEGER value of a ROWID.
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleSetRowidInt64(sqlite3 *db, Stat4Sample *p, i64 iRowid){
+ assert( db!=0 );
+ if( p->nRowid ) sqlite3DbFree(db, p->u.aRowid);
+ p->nRowid = 0;
+ p->u.iRowid = iRowid;
+}
+#endif
+
+
+/*
+** Copy the contents of object (*pFrom) into (*pTo).
+*/
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+static void sampleCopy(Stat4Accum *p, Stat4Sample *pTo, Stat4Sample *pFrom){
+ pTo->isPSample = pFrom->isPSample;
+ pTo->iCol = pFrom->iCol;
+ pTo->iHash = pFrom->iHash;
+ memcpy(pTo->anEq, pFrom->anEq, sizeof(tRowcnt)*p->nCol);
+ memcpy(pTo->anLt, pFrom->anLt, sizeof(tRowcnt)*p->nCol);
+ memcpy(pTo->anDLt, pFrom->anDLt, sizeof(tRowcnt)*p->nCol);
+ if( pFrom->nRowid ){
+ sampleSetRowid(p->db, pTo, pFrom->nRowid, pFrom->u.aRowid);
+ }else{
+ sampleSetRowidInt64(p->db, pTo, pFrom->u.iRowid);
+ }
+}
+#endif
+
+/*
+** Reclaim all memory of a Stat4Accum structure.
+*/
+static void stat4Destructor(void *pOld){
+ Stat4Accum *p = (Stat4Accum*)pOld;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int i;
+ for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
+ for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
+ sampleClear(p->db, &p->current);
+#endif
+ sqlite3DbFree(p->db, p);
+}
+
+/*
+** Implementation of the stat_init(N,K,C) SQL function. The three parameters
+** are:
+** N: The number of columns in the index including the rowid/pk (note 1)
+** K: The number of columns in the index excluding the rowid/pk.
+** C: The number of rows in the index (note 2)
+**
+** Note 1: In the special case of the covering index that implements a
+** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
+** total number of columns in the table.
+**
+** Note 2: C is only used for STAT3 and STAT4.
+**
+** For indexes on ordinary rowid tables, N==K+1. But for indexes on
+** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
+** PRIMARY KEY of the table. The covering index that implements the
+** original WITHOUT ROWID table as N==K as a special case.
+**
+** This routine allocates the Stat4Accum object in heap memory. The return
+** value is a pointer to the Stat4Accum object. The datatype of the
+** return value is BLOB, but it is really just a pointer to the Stat4Accum
+** object.
+*/
+static void statInit(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Stat4Accum *p;
+ int nCol; /* Number of columns in index being sampled */
+ int nKeyCol; /* Number of key columns */
+ int nColUp; /* nCol rounded up for alignment */
+ int n; /* Bytes of space to allocate */
+ sqlite3 *db; /* Database connection */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int mxSample = SQLITE_STAT4_SAMPLES;
+#endif
+
+ /* Decode the three function arguments */
+ UNUSED_PARAMETER(argc);
+ nCol = sqlite3_value_int(argv[0]);
+ assert( nCol>0 );
+ nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol;
+ nKeyCol = sqlite3_value_int(argv[1]);
+ assert( nKeyCol<=nCol );
+ assert( nKeyCol>0 );
+
+ /* Allocate the space required for the Stat4Accum object */
+ n = sizeof(*p)
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anEq */
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anDLt */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ + sizeof(tRowcnt)*nColUp /* Stat4Accum.anLt */
+ + sizeof(Stat4Sample)*(nCol+mxSample) /* Stat4Accum.aBest[], a[] */
+ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
+#endif
+ ;
+ db = sqlite3_context_db_handle(context);
+ p = sqlite3DbMallocZero(db, n);
+ if( p==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+
+ p->db = db;
+ p->nRow = 0;
+ p->nCol = nCol;
+ p->nKeyCol = nKeyCol;
+ p->current.anDLt = (tRowcnt*)&p[1];
+ p->current.anEq = &p->current.anDLt[nColUp];
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ {
+ u8 *pSpace; /* Allocated space not yet assigned */
+ int i; /* Used to iterate through p->aSample[] */
+
+ p->iGet = -1;
+ p->mxSample = mxSample;
+ p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
+ p->current.anLt = &p->current.anEq[nColUp];
+ p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
+
+ /* Set up the Stat4Accum.a[] and aBest[] arrays */
+ p->a = (struct Stat4Sample*)&p->current.anLt[nColUp];
+ p->aBest = &p->a[mxSample];
+ pSpace = (u8*)(&p->a[mxSample+nCol]);
+ for(i=0; i<(mxSample+nCol); i++){
+ p->a[i].anEq = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ p->a[i].anLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ p->a[i].anDLt = (tRowcnt *)pSpace; pSpace += (sizeof(tRowcnt) * nColUp);
+ }
+ assert( (pSpace - (u8*)p)==n );
+
+ for(i=0; i<nCol; i++){
+ p->aBest[i].iCol = i;
+ }
+ }
+#endif
+
+ /* Return a pointer to the allocated object to the caller. Note that
+ ** only the pointer (the 2nd parameter) matters. The size of the object
+ ** (given by the 3rd parameter) is never used and can be any positive
+ ** value. */
+ sqlite3_result_blob(context, p, sizeof(*p), stat4Destructor);
+}
+static const FuncDef statInitFuncdef = {
+ 2+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statInit, /* xSFunc */
+ 0, /* xFinalize */
+ "stat_init", /* zName */
+ {0}
+};
+
+#ifdef SQLITE_ENABLE_STAT4
+/*
+** pNew and pOld are both candidate non-periodic samples selected for
+** the same column (pNew->iCol==pOld->iCol). Ignoring this column and
+** considering only any trailing columns and the sample hash value, this
+** function returns true if sample pNew is to be preferred over pOld.
+** In other words, if we assume that the cardinalities of the selected
+** column for pNew and pOld are equal, is pNew to be preferred over pOld.
+**
+** This function assumes that for each argument sample, the contents of
+** the anEq[] array from pSample->anEq[pSample->iCol+1] onwards are valid.
+*/
+static int sampleIsBetterPost(
+ Stat4Accum *pAccum,
+ Stat4Sample *pNew,
+ Stat4Sample *pOld
+){
+ int nCol = pAccum->nCol;
+ int i;
+ assert( pNew->iCol==pOld->iCol );
+ for(i=pNew->iCol+1; i<nCol; i++){
+ if( pNew->anEq[i]>pOld->anEq[i] ) return 1;
+ if( pNew->anEq[i]<pOld->anEq[i] ) return 0;
+ }
+ if( pNew->iHash>pOld->iHash ) return 1;
+ return 0;
+}
+#endif
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** Return true if pNew is to be preferred over pOld.
+**
+** This function assumes that for each argument sample, the contents of
+** the anEq[] array from pSample->anEq[pSample->iCol] onwards are valid.
+*/
+static int sampleIsBetter(
+ Stat4Accum *pAccum,
+ Stat4Sample *pNew,
+ Stat4Sample *pOld
+){
+ tRowcnt nEqNew = pNew->anEq[pNew->iCol];
+ tRowcnt nEqOld = pOld->anEq[pOld->iCol];
+
+ assert( pOld->isPSample==0 && pNew->isPSample==0 );
+ assert( IsStat4 || (pNew->iCol==0 && pOld->iCol==0) );
+
+ if( (nEqNew>nEqOld) ) return 1;
+#ifdef SQLITE_ENABLE_STAT4
+ if( nEqNew==nEqOld ){
+ if( pNew->iCol<pOld->iCol ) return 1;
+ return (pNew->iCol==pOld->iCol && sampleIsBetterPost(pAccum, pNew, pOld));
+ }
+ return 0;
+#else
+ return (nEqNew==nEqOld && pNew->iHash>pOld->iHash);
+#endif
+}
+
+/*
+** Copy the contents of sample *pNew into the p->a[] array. If necessary,
+** remove the least desirable sample from p->a[] to make room.
+*/
+static void sampleInsert(Stat4Accum *p, Stat4Sample *pNew, int nEqZero){
+ Stat4Sample *pSample = 0;
+ int i;
+
+ assert( IsStat4 || nEqZero==0 );
+
+#ifdef SQLITE_ENABLE_STAT4
+ if( pNew->isPSample==0 ){
+ Stat4Sample *pUpgrade = 0;
+ assert( pNew->anEq[pNew->iCol]>0 );
+
+ /* This sample is being added because the prefix that ends in column
+ ** iCol occurs many times in the table. However, if we have already
+ ** added a sample that shares this prefix, there is no need to add
+ ** this one. Instead, upgrade the priority of the highest priority
+ ** existing sample that shares this prefix. */
+ for(i=p->nSample-1; i>=0; i--){
+ Stat4Sample *pOld = &p->a[i];
+ if( pOld->anEq[pNew->iCol]==0 ){
+ if( pOld->isPSample ) return;
+ assert( pOld->iCol>pNew->iCol );
+ assert( sampleIsBetter(p, pNew, pOld) );
+ if( pUpgrade==0 || sampleIsBetter(p, pOld, pUpgrade) ){
+ pUpgrade = pOld;
+ }
+ }
+ }
+ if( pUpgrade ){
+ pUpgrade->iCol = pNew->iCol;
+ pUpgrade->anEq[pUpgrade->iCol] = pNew->anEq[pUpgrade->iCol];
+ goto find_new_min;
+ }
+ }
+#endif
+
+ /* If necessary, remove sample iMin to make room for the new sample. */
+ if( p->nSample>=p->mxSample ){
+ Stat4Sample *pMin = &p->a[p->iMin];
+ tRowcnt *anEq = pMin->anEq;
+ tRowcnt *anLt = pMin->anLt;
+ tRowcnt *anDLt = pMin->anDLt;
+ sampleClear(p->db, pMin);
+ memmove(pMin, &pMin[1], sizeof(p->a[0])*(p->nSample-p->iMin-1));
+ pSample = &p->a[p->nSample-1];
+ pSample->nRowid = 0;
+ pSample->anEq = anEq;
+ pSample->anDLt = anDLt;
+ pSample->anLt = anLt;
+ p->nSample = p->mxSample-1;
+ }
+
+ /* The "rows less-than" for the rowid column must be greater than that
+ ** for the last sample in the p->a[] array. Otherwise, the samples would
+ ** be out of order. */
+#ifdef SQLITE_ENABLE_STAT4
+ assert( p->nSample==0
+ || pNew->anLt[p->nCol-1] > p->a[p->nSample-1].anLt[p->nCol-1] );
+#endif
+
+ /* Insert the new sample */
+ pSample = &p->a[p->nSample];
+ sampleCopy(p, pSample, pNew);
+ p->nSample++;
+
+ /* Zero the first nEqZero entries in the anEq[] array. */
+ memset(pSample->anEq, 0, sizeof(tRowcnt)*nEqZero);
+
+#ifdef SQLITE_ENABLE_STAT4
+ find_new_min:
+#endif
+ if( p->nSample>=p->mxSample ){
+ int iMin = -1;
+ for(i=0; i<p->mxSample; i++){
+ if( p->a[i].isPSample ) continue;
+ if( iMin<0 || sampleIsBetter(p, &p->a[iMin], &p->a[i]) ){
+ iMin = i;
+ }
+ }
+ assert( iMin>=0 );
+ p->iMin = iMin;
+ }
+}
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+/*
+** Field iChng of the index being scanned has changed. So at this point
+** p->current contains a sample that reflects the previous row of the
+** index. The value of anEq[iChng] and subsequent anEq[] elements are
+** correct at this point.
+*/
+static void samplePushPrevious(Stat4Accum *p, int iChng){
+#ifdef SQLITE_ENABLE_STAT4
+ int i;
+
+ /* Check if any samples from the aBest[] array should be pushed
+ ** into IndexSample.a[] at this point. */
+ for(i=(p->nCol-2); i>=iChng; i--){
+ Stat4Sample *pBest = &p->aBest[i];
+ pBest->anEq[i] = p->current.anEq[i];
+ if( p->nSample<p->mxSample || sampleIsBetter(p, pBest, &p->a[p->iMin]) ){
+ sampleInsert(p, pBest, i);
+ }
+ }
+
+ /* Update the anEq[] fields of any samples already collected. */
+ for(i=p->nSample-1; i>=0; i--){
+ int j;
+ for(j=iChng; j<p->nCol; j++){
+ if( p->a[i].anEq[j]==0 ) p->a[i].anEq[j] = p->current.anEq[j];
+ }
+ }
+#endif
+
+#if defined(SQLITE_ENABLE_STAT3) && !defined(SQLITE_ENABLE_STAT4)
+ if( iChng==0 ){
+ tRowcnt nLt = p->current.anLt[0];
+ tRowcnt nEq = p->current.anEq[0];
+
+ /* Check if this is to be a periodic sample. If so, add it. */
+ if( (nLt/p->nPSample)!=(nLt+nEq)/p->nPSample ){
+ p->current.isPSample = 1;
+ sampleInsert(p, &p->current, 0);
+ p->current.isPSample = 0;
+ }else
+
+ /* Or if it is a non-periodic sample. Add it in this case too. */
+ if( p->nSample<p->mxSample
+ || sampleIsBetter(p, &p->current, &p->a[p->iMin])
+ ){
+ sampleInsert(p, &p->current, 0);
+ }
+ }
+#endif
+
+#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+ UNUSED_PARAMETER( p );
+ UNUSED_PARAMETER( iChng );
+#endif
+}
+
+/*
+** Implementation of the stat_push SQL function: stat_push(P,C,R)
+** Arguments:
+**
+** P Pointer to the Stat4Accum object created by stat_init()
+** C Index of left-most column to differ from previous row
+** R Rowid for the current row. Might be a key record for
+** WITHOUT ROWID tables.
+**
+** This SQL function always returns NULL. It's purpose it to accumulate
+** statistical data and/or samples in the Stat4Accum object about the
+** index being analyzed. The stat_get() SQL function will later be used to
+** extract relevant information for constructing the sqlite_statN tables.
+**
+** The R parameter is only used for STAT3 and STAT4
+*/
+static void statPush(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+
+ /* The three function arguments */
+ Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
+ int iChng = sqlite3_value_int(argv[1]);
+
+ UNUSED_PARAMETER( argc );
+ UNUSED_PARAMETER( context );
+ assert( p->nCol>0 );
+ assert( iChng<p->nCol );
+
+ if( p->nRow==0 ){
+ /* This is the first call to this function. Do initialization. */
+ for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
+ }else{
+ /* Second and subsequent calls get processed here */
+ samplePushPrevious(p, iChng);
+
+ /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
+ ** to the current row of the index. */
+ for(i=0; i<iChng; i++){
+ p->current.anEq[i]++;
+ }
+ for(i=iChng; i<p->nCol; i++){
+ p->current.anDLt[i]++;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ p->current.anLt[i] += p->current.anEq[i];
+#endif
+ p->current.anEq[i] = 1;
+ }
+ }
+ p->nRow++;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
+ sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
+ }else{
+ sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
+ sqlite3_value_blob(argv[2]));
+ }
+ p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
+#endif
+
+#ifdef SQLITE_ENABLE_STAT4
+ {
+ tRowcnt nLt = p->current.anLt[p->nCol-1];
+
+ /* Check if this is to be a periodic sample. If so, add it. */
+ if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
+ p->current.isPSample = 1;
+ p->current.iCol = 0;
+ sampleInsert(p, &p->current, p->nCol-1);
+ p->current.isPSample = 0;
+ }
+
+ /* Update the aBest[] array. */
+ for(i=0; i<(p->nCol-1); i++){
+ p->current.iCol = i;
+ if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){
+ sampleCopy(p, &p->aBest[i], &p->current);
+ }
+ }
+ }
+#endif
+}
+static const FuncDef statPushFuncdef = {
+ 2+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statPush, /* xSFunc */
+ 0, /* xFinalize */
+ "stat_push", /* zName */
+ {0}
+};
+
+#define STAT_GET_STAT1 0 /* "stat" column of stat1 table */
+#define STAT_GET_ROWID 1 /* "rowid" column of stat[34] entry */
+#define STAT_GET_NEQ 2 /* "neq" column of stat[34] entry */
+#define STAT_GET_NLT 3 /* "nlt" column of stat[34] entry */
+#define STAT_GET_NDLT 4 /* "ndlt" column of stat[34] entry */
+
+/*
+** Implementation of the stat_get(P,J) SQL function. This routine is
+** used to query statistical information that has been gathered into
+** the Stat4Accum object by prior calls to stat_push(). The P parameter
+** has type BLOB but it is really just a pointer to the Stat4Accum object.
+** The content to returned is determined by the parameter J
+** which is one of the STAT_GET_xxxx values defined above.
+**
+** If neither STAT3 nor STAT4 are enabled, then J is always
+** STAT_GET_STAT1 and is hence omitted and this routine becomes
+** a one-parameter function, stat_get(P), that always returns the
+** stat1 table entry information.
+*/
+static void statGet(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ Stat4Accum *p = (Stat4Accum*)sqlite3_value_blob(argv[0]);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ /* STAT3 and STAT4 have a parameter on this routine. */
+ int eCall = sqlite3_value_int(argv[1]);
+ assert( argc==2 );
+ assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
+ || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
+ || eCall==STAT_GET_NDLT
+ );
+ if( eCall==STAT_GET_STAT1 )
+#else
+ assert( argc==1 );
+#endif
+ {
+ /* Return the value to store in the "stat" column of the sqlite_stat1
+ ** table for this index.
+ **
+ ** The value is a string composed of a list of integers describing
+ ** the index. The first integer in the list is the total number of
+ ** entries in the index. There is one additional integer in the list
+ ** for each indexed column. This additional integer is an estimate of
+ ** the number of rows matched by a stabbing query on the index using
+ ** a key with the corresponding number of fields. In other words,
+ ** if the index is on columns (a,b) and the sqlite_stat1 value is
+ ** "100 10 2", then SQLite estimates that:
+ **
+ ** * the index contains 100 rows,
+ ** * "WHERE a=?" matches 10 rows, and
+ ** * "WHERE a=? AND b=?" matches 2 rows.
+ **
+ ** If D is the count of distinct values and K is the total number of
+ ** rows, then each estimate is computed as:
+ **
+ ** I = (K+D-1)/D
+ */
+ char *z;
+ int i;
+
+ char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 );
+ if( zRet==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+
+ sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
+ z = zRet + sqlite3Strlen30(zRet);
+ for(i=0; i<p->nKeyCol; i++){
+ u64 nDistinct = p->current.anDLt[i] + 1;
+ u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
+ sqlite3_snprintf(24, z, " %llu", iVal);
+ z += sqlite3Strlen30(z);
+ assert( p->current.anEq[i] );
+ }
+ assert( z[0]=='\0' && z>zRet );
+
+ sqlite3_result_text(context, zRet, -1, sqlite3_free);
+ }
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ else if( eCall==STAT_GET_ROWID ){
+ if( p->iGet<0 ){
+ samplePushPrevious(p, 0);
+ p->iGet = 0;
+ }
+ if( p->iGet<p->nSample ){
+ Stat4Sample *pS = p->a + p->iGet;
+ if( pS->nRowid==0 ){
+ sqlite3_result_int64(context, pS->u.iRowid);
+ }else{
+ sqlite3_result_blob(context, pS->u.aRowid, pS->nRowid,
+ SQLITE_TRANSIENT);
+ }
+ }
+ }else{
+ tRowcnt *aCnt = 0;
+
+ assert( p->iGet<p->nSample );
+ switch( eCall ){
+ case STAT_GET_NEQ: aCnt = p->a[p->iGet].anEq; break;
+ case STAT_GET_NLT: aCnt = p->a[p->iGet].anLt; break;
+ default: {
+ aCnt = p->a[p->iGet].anDLt;
+ p->iGet++;
+ break;
+ }
+ }
+
+ if( IsStat3 ){
+ sqlite3_result_int64(context, (i64)aCnt[0]);
+ }else{
+ char *zRet = sqlite3MallocZero(p->nCol * 25);
+ if( zRet==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ int i;
+ char *z = zRet;
+ for(i=0; i<p->nCol; i++){
+ sqlite3_snprintf(24, z, "%llu ", (u64)aCnt[i]);
+ z += sqlite3Strlen30(z);
+ }
+ assert( z[0]=='\0' && z>zRet );
+ z[-1] = '\0';
+ sqlite3_result_text(context, zRet, -1, sqlite3_free);
+ }
+ }
+ }
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER( argc );
+#endif
+}
+static const FuncDef statGetFuncdef = {
+ 1+IsStat34, /* nArg */
+ SQLITE_UTF8, /* funcFlags */
+ 0, /* pUserData */
+ 0, /* pNext */
+ statGet, /* xSFunc */
+ 0, /* xFinalize */
+ "stat_get", /* zName */
+ {0}
+};
+
+static void callStatGet(Vdbe *v, int regStat4, int iParam, int regOut){
+ assert( regOut!=regStat4 && regOut!=regStat4+1 );
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3VdbeAddOp2(v, OP_Integer, iParam, regStat4+1);
+#elif SQLITE_DEBUG
+ assert( iParam==STAT_GET_STAT1 );
+#else
+ UNUSED_PARAMETER( iParam );
+#endif
+ sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4, regOut,
+ (char*)&statGetFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 1 + IsStat34);
+}
/*
** Generate code to do an analysis of all indices associated with
@@ -77711,34 +96500,31 @@ static void analyzeOneTable(
Table *pTab, /* Table whose indices are to be analyzed */
Index *pOnlyIdx, /* If not NULL, only analyze this one index */
int iStatCur, /* Index of VdbeCursor that writes the sqlite_stat1 table */
- int iMem /* Available memory locations begin here */
+ int iMem, /* Available memory locations begin here */
+ int iTab /* Next available cursor */
){
sqlite3 *db = pParse->db; /* Database handle */
Index *pIdx; /* An index to being analyzed */
int iIdxCur; /* Cursor open on index being analyzed */
+ int iTabCur; /* Table cursor */
Vdbe *v; /* The virtual machine being built up */
int i; /* Loop counter */
- int topOfLoop; /* The top of the loop */
- int endOfLoop; /* The end of the loop */
int jZeroRows = -1; /* Jump from here if number of rows is zero */
int iDb; /* Index of database containing pTab */
+ u8 needTableCnt = 1; /* True to count the table */
+ int regNewRowid = iMem++; /* Rowid for the inserted record */
+ int regStat4 = iMem++; /* Register to hold Stat4Accum object */
+ int regChng = iMem++; /* Index of changed index field */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int regRowid = iMem++; /* Rowid argument passed to stat_push() */
+#endif
+ int regTemp = iMem++; /* Temporary use register */
int regTabname = iMem++; /* Register containing table name */
int regIdxname = iMem++; /* Register containing index name */
- int regSampleno = iMem++; /* Register containing next sample number */
- int regCol = iMem++; /* Content of a column analyzed table */
- int regRec = iMem++; /* Register holding completed record */
- int regTemp = iMem++; /* Temporary use register */
- int regRowid = iMem++; /* Rowid for the inserted record */
-
-#ifdef SQLITE_ENABLE_STAT2
- int addr = 0; /* Instruction address */
- int regTemp2 = iMem++; /* Temporary use register */
- int regSamplerecno = iMem++; /* Index of next sample to record */
- int regRecno = iMem++; /* Current sample index */
- int regLast = iMem++; /* Index of last sample to record */
- int regFirst = iMem++; /* Index of first sample to record */
-#endif
+ int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
+ int regPrev = iMem; /* MUST BE LAST (see below) */
+ pParse->nMem = MAX(pParse->nMem, iMem);
v = sqlite3GetVdbe(pParse);
if( v==0 || NEVER(pTab==0) ){
return;
@@ -77747,7 +96533,7 @@ static void analyzeOneTable(
/* Do not gather statistics on views or virtual tables */
return;
}
- if( memcmp(pTab->zName, "sqlite_", 7)==0 ){
+ if( sqlite3_strlike("sqlite_%", pTab->zName, 0)==0 ){
/* Do not gather statistics on system tables */
return;
}
@@ -77757,219 +96543,280 @@ static void analyzeOneTable(
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0,
- db->aDb[iDb].zName ) ){
+ db->aDb[iDb].zDbSName ) ){
return;
}
#endif
- /* Establish a read-lock on the table at the shared-cache level. */
+ /* Establish a read-lock on the table at the shared-cache level.
+ ** Open a read-only cursor on the table. Also allocate a cursor number
+ ** to use for scanning indexes (iIdxCur). No index cursor is opened at
+ ** this time though. */
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
+ iTabCur = iTab++;
+ iIdxCur = iTab++;
+ pParse->nTab = MAX(pParse->nTab, iTab);
+ sqlite3OpenTable(pParse, iTabCur, iDb, pTab, OP_OpenRead);
+ sqlite3VdbeLoadString(v, regTabname, pTab->zName);
- iIdxCur = pParse->nTab++;
- sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- int nCol;
- KeyInfo *pKey;
+ int nCol; /* Number of columns in pIdx. "N" */
+ int addrRewind; /* Address of "OP_Rewind iIdxCur" */
+ int addrNextRow; /* Address of "next_row:" */
+ const char *zIdxName; /* Name of the index */
+ int nColTest; /* Number of columns to test for changes */
if( pOnlyIdx && pOnlyIdx!=pIdx ) continue;
- nCol = pIdx->nColumn;
- pKey = sqlite3IndexKeyinfo(pParse, pIdx);
- if( iMem+1+(nCol*2)>pParse->nMem ){
- pParse->nMem = iMem+1+(nCol*2);
+ if( pIdx->pPartIdxWhere==0 ) needTableCnt = 0;
+ if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIdx) ){
+ nCol = pIdx->nKeyCol;
+ zIdxName = pTab->zName;
+ nColTest = nCol - 1;
+ }else{
+ nCol = pIdx->nColumn;
+ zIdxName = pIdx->zName;
+ nColTest = pIdx->uniqNotNull ? pIdx->nKeyCol-1 : nCol-1;
}
- /* Open a cursor to the index to be analyzed. */
- assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
- sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb,
- (char *)pKey, P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIdx->zName));
-
/* Populate the register containing the index name. */
- sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0);
-
-#ifdef SQLITE_ENABLE_STAT2
-
- /* If this iteration of the loop is generating code to analyze the
- ** first index in the pTab->pIndex list, then register regLast has
- ** not been populated. In this case populate it now. */
- if( pTab->pIndex==pIdx ){
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2-1, regTemp);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regTemp2);
-
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regLast);
- sqlite3VdbeAddOp2(v, OP_Null, 0, regFirst);
- addr = sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, 0, regLast);
- sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regFirst);
- sqlite3VdbeAddOp3(v, OP_Multiply, regLast, regTemp, regLast);
- sqlite3VdbeAddOp2(v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES*2-2);
- sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regLast);
- sqlite3VdbeJumpHere(v, addr);
- }
-
- /* Zero the regSampleno and regRecno registers. */
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno);
- sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno);
-#endif
+ sqlite3VdbeLoadString(v, regIdxname, zIdxName);
+ VdbeComment((v, "Analysis for %s.%s", pTab->zName, zIdxName));
- /* The block of memory cells initialized here is used as follows.
+ /*
+ ** Pseudo-code for loop that calls stat_push():
+ **
+ ** Rewind csr
+ ** if eof(csr) goto end_of_scan;
+ ** regChng = 0
+ ** goto chng_addr_0;
**
- ** iMem:
- ** The total number of rows in the table.
+ ** next_row:
+ ** regChng = 0
+ ** if( idx(0) != regPrev(0) ) goto chng_addr_0
+ ** regChng = 1
+ ** if( idx(1) != regPrev(1) ) goto chng_addr_1
+ ** ...
+ ** regChng = N
+ ** goto chng_addr_N
**
- ** iMem+1 .. iMem+nCol:
- ** Number of distinct entries in index considering the
- ** left-most N columns only, where N is between 1 and nCol,
- ** inclusive.
+ ** chng_addr_0:
+ ** regPrev(0) = idx(0)
+ ** chng_addr_1:
+ ** regPrev(1) = idx(1)
+ ** ...
**
- ** iMem+nCol+1 .. Mem+2*nCol:
- ** Previous value of indexed columns, from left to right.
+ ** endDistinctTest:
+ ** regRowid = idx(rowid)
+ ** stat_push(P, regChng, regRowid)
+ ** Next csr
+ ** if !eof(csr) goto next_row;
**
- ** Cells iMem through iMem+nCol are initialized to 0. The others are
- ** initialized to contain an SQL NULL.
+ ** end_of_scan:
*/
- for(i=0; i<=nCol; i++){
- sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i);
- }
- for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp2(v, OP_Null, 0, iMem+nCol+i+1);
- }
- /* Start the analysis loop. This loop runs through all the entries in
- ** the index b-tree. */
- endOfLoop = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop);
- topOfLoop = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1);
+ /* Make sure there are enough memory cells allocated to accommodate
+ ** the regPrev array and a trailing rowid (the rowid slot is required
+ ** when building a record to insert into the sample column of
+ ** the sqlite_stat4 table. */
+ pParse->nMem = MAX(pParse->nMem, regPrev+nColTest);
- for(i=0; i<nCol; i++){
- CollSeq *pColl;
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regCol);
- if( i==0 ){
-#ifdef SQLITE_ENABLE_STAT2
- /* Check if the record that cursor iIdxCur points to contains a
- ** value that should be stored in the sqlite_stat2 table. If so,
- ** store it. */
- int ne = sqlite3VdbeAddOp3(v, OP_Ne, regRecno, 0, regSamplerecno);
- assert( regTabname+1==regIdxname
- && regTabname+2==regSampleno
- && regTabname+3==regCol
- );
- sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 4, regRec, "aaab", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regRec, regRowid);
-
- /* Calculate new values for regSamplerecno and regSampleno.
- **
- ** sampleno = sampleno + 1
- ** samplerecno = samplerecno+(remaining records)/(remaining samples)
- */
- sqlite3VdbeAddOp2(v, OP_AddImm, regSampleno, 1);
- sqlite3VdbeAddOp3(v, OP_Subtract, regRecno, regLast, regTemp);
- sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
- sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regTemp2);
- sqlite3VdbeAddOp3(v, OP_Subtract, regSampleno, regTemp2, regTemp2);
- sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regTemp, regTemp);
- sqlite3VdbeAddOp3(v, OP_Add, regSamplerecno, regTemp, regSamplerecno);
-
- sqlite3VdbeJumpHere(v, ne);
- sqlite3VdbeAddOp2(v, OP_AddImm, regRecno, 1);
-#endif
-
- /* Always record the very first row */
- sqlite3VdbeAddOp1(v, OP_IfNot, iMem+1);
- }
- assert( pIdx->azColl!=0 );
- assert( pIdx->azColl[i]!=0 );
- pColl = sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
- sqlite3VdbeAddOp4(v, OP_Ne, regCol, 0, iMem+nCol+i+1,
- (char*)pColl, P4_COLLSEQ);
- sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
- }
- if( db->mallocFailed ){
- /* If a malloc failure has occurred, then the result of the expression
- ** passed as the second argument to the call to sqlite3VdbeJumpHere()
- ** below may be negative. Which causes an assert() to fail (or an
- ** out-of-bounds write if SQLITE_DEBUG is not defined). */
- return;
- }
- sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop);
- for(i=0; i<nCol; i++){
- int addr2 = sqlite3VdbeCurrentAddr(v) - (nCol*2);
- if( i==0 ){
- sqlite3VdbeJumpHere(v, addr2-1); /* Set jump dest for the OP_IfNot */
- }
- sqlite3VdbeJumpHere(v, addr2); /* Set jump dest for the OP_Ne */
- sqlite3VdbeAddOp2(v, OP_AddImm, iMem+i+1, 1);
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, iMem+nCol+i+1);
- }
-
- /* End of the analysis loop. */
- sqlite3VdbeResolveLabel(v, endOfLoop);
- sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, topOfLoop);
- sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
+ /* Open a read-only cursor on the index being analyzed. */
+ assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) );
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ VdbeComment((v, "%s", pIdx->zName));
- /* Store the results in sqlite_stat1.
+ /* Invoke the stat_init() function. The arguments are:
+ **
+ ** (1) the number of columns in the index including the rowid
+ ** (or for a WITHOUT ROWID table, the number of PK columns),
+ ** (2) the number of columns in the key without the rowid/pk
+ ** (3) the number of rows in the index,
**
- ** The result is a single row of the sqlite_stat1 table. The first
- ** two columns are the names of the table and index. The third column
- ** is a string composed of a list of integer statistics about the
- ** index. The first integer in the list is the total number of entries
- ** in the index. There is one additional integer in the list for each
- ** column of the table. This additional integer is a guess of how many
- ** rows of the table the index will select. If D is the count of distinct
- ** values and K is the total number of rows, then the integer is computed
- ** as:
**
- ** I = (K+D-1)/D
+ ** The third argument is only used for STAT3 and STAT4
+ */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
+#endif
+ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
+ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
+ sqlite3VdbeAddOp4(v, OP_Function0, 0, regStat4+1, regStat4,
+ (char*)&statInitFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 2+IsStat34);
+
+ /* Implementation of the following:
+ **
+ ** Rewind csr
+ ** if eof(csr) goto end_of_scan;
+ ** regChng = 0
+ ** goto next_push_0;
**
- ** If K==0 then no entry is made into the sqlite_stat1 table.
- ** If K>0 then it is always the case the D>0 so division by zero
- ** is never possible.
*/
- sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno);
- if( jZeroRows<0 ){
- jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, iMem);
+ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
+ addrNextRow = sqlite3VdbeCurrentAddr(v);
+
+ if( nColTest>0 ){
+ int endDistinctTest = sqlite3VdbeMakeLabel(v);
+ int *aGotoChng; /* Array of jump instruction addresses */
+ aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest);
+ if( aGotoChng==0 ) continue;
+
+ /*
+ ** next_row:
+ ** regChng = 0
+ ** if( idx(0) != regPrev(0) ) goto chng_addr_0
+ ** regChng = 1
+ ** if( idx(1) != regPrev(1) ) goto chng_addr_1
+ ** ...
+ ** regChng = N
+ ** goto endDistinctTest
+ */
+ sqlite3VdbeAddOp0(v, OP_Goto);
+ addrNextRow = sqlite3VdbeCurrentAddr(v);
+ if( nColTest==1 && pIdx->nKeyCol==1 && IsUniqueIndex(pIdx) ){
+ /* For a single-column UNIQUE index, once we have found a non-NULL
+ ** row, we know that all the rest will be distinct, so skip
+ ** subsequent distinctness tests. */
+ sqlite3VdbeAddOp2(v, OP_NotNull, regPrev, endDistinctTest);
+ VdbeCoverage(v);
+ }
+ for(i=0; i<nColTest; i++){
+ char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
+ sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
+ aGotoChng[i] =
+ sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeAddOp2(v, OP_Integer, nColTest, regChng);
+ sqlite3VdbeGoto(v, endDistinctTest);
+
+
+ /*
+ ** chng_addr_0:
+ ** regPrev(0) = idx(0)
+ ** chng_addr_1:
+ ** regPrev(1) = idx(1)
+ ** ...
+ */
+ sqlite3VdbeJumpHere(v, addrNextRow-1);
+ for(i=0; i<nColTest; i++){
+ sqlite3VdbeJumpHere(v, aGotoChng[i]);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
+ }
+ sqlite3VdbeResolveLabel(v, endDistinctTest);
+ sqlite3DbFree(db, aGotoChng);
}
- for(i=0; i<nCol; i++){
- sqlite3VdbeAddOp4(v, OP_String8, 0, regTemp, 0, " ", 0);
- sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
- sqlite3VdbeAddOp3(v, OP_Add, iMem, iMem+i+1, regTemp);
- sqlite3VdbeAddOp2(v, OP_AddImm, regTemp, -1);
- sqlite3VdbeAddOp3(v, OP_Divide, iMem+i+1, regTemp, regTemp);
- sqlite3VdbeAddOp1(v, OP_ToInt, regTemp);
- sqlite3VdbeAddOp3(v, OP_Concat, regTemp, regSampleno, regSampleno);
- }
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
+
+ /*
+ ** chng_addr_N:
+ ** regRowid = idx(rowid) // STAT34 only
+ ** stat_push(P, regChng, regRowid) // 3rd parameter STAT34 only
+ ** Next csr
+ ** if !eof(csr) goto next_row;
+ */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ assert( regRowid==(regStat4+2) );
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
+ int j, k, regKey;
+ regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ for(j=0; j<pPk->nKeyCol; j++){
+ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
+ assert( k>=0 && k<pTab->nCol );
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
+ VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
+ sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
+ }
+#endif
+ assert( regChng==(regStat4+1) );
+ sqlite3VdbeAddOp4(v, OP_Function0, 1, regStat4, regTemp,
+ (char*)&statPushFuncdef, P4_FUNCDEF);
+ sqlite3VdbeChangeP5(v, 2+IsStat34);
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
+
+ /* Add the entry to the stat1 table. */
+ callStatGet(v, regStat4, STAT_GET_STAT1, regStat1);
+ assert( "BBB"[0]==SQLITE_AFF_TEXT );
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
+
+ /* Add the entries to the stat3 or stat4 table. */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ {
+ int regEq = regStat1;
+ int regLt = regStat1+1;
+ int regDLt = regStat1+2;
+ int regSample = regStat1+3;
+ int regCol = regStat1+4;
+ int regSampleRowid = regCol + nCol;
+ int addrNext;
+ int addrIsNull;
+ u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
+
+ pParse->nMem = MAX(pParse->nMem, regCol+nCol);
+
+ addrNext = sqlite3VdbeCurrentAddr(v);
+ callStatGet(v, regStat4, STAT_GET_ROWID, regSampleRowid);
+ addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
+ VdbeCoverage(v);
+ callStatGet(v, regStat4, STAT_GET_NEQ, regEq);
+ callStatGet(v, regStat4, STAT_GET_NLT, regLt);
+ callStatGet(v, regStat4, STAT_GET_NDLT, regDLt);
+ sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
+ /* We know that the regSampleRowid row exists because it was read by
+ ** the previous loop. Thus the not-found jump of seekOp will never
+ ** be taken */
+ VdbeCoverageNeverTaken(v);
+#ifdef SQLITE_ENABLE_STAT3
+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, 0, regSample);
+#else
+ for(i=0; i<nCol; i++){
+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
+#endif
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur+1, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur+1, regTemp, regNewRowid);
+ sqlite3VdbeAddOp2(v, OP_Goto, 1, addrNext); /* P1==1 for end-of-loop */
+ sqlite3VdbeJumpHere(v, addrIsNull);
+ }
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+ /* End of analysis */
+ sqlite3VdbeJumpHere(v, addrRewind);
}
- /* If the table has no indices, create a single sqlite_stat1 entry
- ** containing NULL as the index name and the row count as the content.
+
+ /* Create a single sqlite_stat1 entry containing NULL as the index
+ ** name and the row count as the content.
*/
- if( pTab->pIndex==0 ){
- sqlite3VdbeAddOp3(v, OP_OpenRead, iIdxCur, pTab->tnum, iDb);
+ if( pOnlyIdx==0 && needTableCnt ){
VdbeComment((v, "%s", pTab->zName));
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regSampleno);
- sqlite3VdbeAddOp1(v, OP_Close, iIdxCur);
- jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regSampleno);
- }else{
+ sqlite3VdbeAddOp2(v, OP_Count, iTabCur, regStat1);
+ jZeroRows = sqlite3VdbeAddOp1(v, OP_IfNot, regStat1); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
+ assert( "BBB"[0]==SQLITE_AFF_TEXT );
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
+ sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeJumpHere(v, jZeroRows);
- jZeroRows = sqlite3VdbeAddOp0(v, OP_Goto);
}
- sqlite3VdbeAddOp2(v, OP_Null, 0, regIdxname);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regRec, "aaa", 0);
- sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regRowid);
- sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regRec, regRowid);
- sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- if( pParse->nMem<regRec ) pParse->nMem = regRec;
- sqlite3VdbeJumpHere(v, jZeroRows);
}
+
/*
** Generate code that will cause the most recent index analysis to
** be loaded into internal hash tables where is can be used.
@@ -77990,16 +96837,18 @@ static void analyzeDatabase(Parse *pParse, int iDb){
HashElem *k;
int iStatCur;
int iMem;
+ int iTab;
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
- pParse->nTab += 2;
+ pParse->nTab += 3;
openStatTable(pParse, iDb, iStatCur, 0, 0);
iMem = pParse->nMem+1;
+ iTab = pParse->nTab;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
Table *pTab = (Table*)sqliteHashData(k);
- analyzeOneTable(pParse, pTab, 0, iStatCur, iMem);
+ analyzeOneTable(pParse, pTab, 0, iStatCur, iMem, iTab);
}
loadAnalysis(pParse, iDb);
}
@@ -78018,13 +96867,13 @@ static void analyzeTable(Parse *pParse, Table *pTab, Index *pOnlyIdx){
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
sqlite3BeginWriteOperation(pParse, 0, iDb);
iStatCur = pParse->nTab;
- pParse->nTab += 2;
+ pParse->nTab += 3;
if( pOnlyIdx ){
openStatTable(pParse, iDb, iStatCur, pOnlyIdx->zName, "idx");
}else{
openStatTable(pParse, iDb, iStatCur, pTab->zName, "tbl");
}
- analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur, pParse->nMem+1);
+ analyzeOneTable(pParse, pTab, pOnlyIdx, iStatCur,pParse->nMem+1,pParse->nTab);
loadAnalysis(pParse, iDb);
}
@@ -78048,6 +96897,7 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
Table *pTab;
Index *pIdx;
Token *pTableName;
+ Vdbe *v;
/* Read the database schema. If an error occurs, leave an error message
** and code in pParse and return NULL. */
@@ -78083,7 +96933,7 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
/* Form 3: Analyze the fully qualified table name */
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pTableName);
if( iDb>=0 ){
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
z = sqlite3NameFromToken(db, pTableName);
if( z ){
if( (pIdx = sqlite3FindIndex(db, z, zDb))!=0 ){
@@ -78095,6 +96945,8 @@ SQLITE_PRIVATE void sqlite3Analyze(Parse *pParse, Token *pName1, Token *pName2){
}
}
}
+ v = sqlite3GetVdbe(pParse);
+ if( v ) sqlite3VdbeAddOp0(v, OP_Expire);
}
/*
@@ -78108,6 +96960,71 @@ struct analysisInfo {
};
/*
+** The first argument points to a nul-terminated string containing a
+** list of space separated integers. Read the first nOut of these into
+** the array aOut[].
+*/
+static void decodeIntArray(
+ char *zIntArray, /* String containing int array to decode */
+ int nOut, /* Number of slots in aOut[] */
+ tRowcnt *aOut, /* Store integers here */
+ LogEst *aLog, /* Or, if aOut==0, here */
+ Index *pIndex /* Handle extra flags for this index, if not NULL */
+){
+ char *z = zIntArray;
+ int c;
+ int i;
+ tRowcnt v;
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( z==0 ) z = "";
+#else
+ assert( z!=0 );
+#endif
+ for(i=0; *z && i<nOut; i++){
+ v = 0;
+ while( (c=z[0])>='0' && c<='9' ){
+ v = v*10 + c - '0';
+ z++;
+ }
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( aOut ) aOut[i] = v;
+ if( aLog ) aLog[i] = sqlite3LogEst(v);
+#else
+ assert( aOut==0 );
+ UNUSED_PARAMETER(aOut);
+ assert( aLog!=0 );
+ aLog[i] = sqlite3LogEst(v);
+#endif
+ if( *z==' ' ) z++;
+ }
+#ifndef SQLITE_ENABLE_STAT3_OR_STAT4
+ assert( pIndex!=0 ); {
+#else
+ if( pIndex ){
+#endif
+ pIndex->bUnordered = 0;
+ pIndex->noSkipScan = 0;
+ while( z[0] ){
+ if( sqlite3_strglob("unordered*", z)==0 ){
+ pIndex->bUnordered = 1;
+ }else if( sqlite3_strglob("sz=[0-9]*", z)==0 ){
+ pIndex->szIdxRow = sqlite3LogEst(sqlite3Atoi(z+3));
+ }else if( sqlite3_strglob("noskipscan*", z)==0 ){
+ pIndex->noSkipScan = 1;
+ }
+#ifdef SQLITE_ENABLE_COSTMULT
+ else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
+ pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
+ }
+#endif
+ while( z[0]!=0 && z[0]!=' ' ) z++;
+ while( z[0]==' ' ) z++;
+ }
+ }
+}
+
+/*
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
@@ -78122,8 +97039,6 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
analysisInfo *pInfo = (analysisInfo*)pData;
Index *pIndex;
Table *pTable;
- int i, c, n;
- unsigned int v;
const char *z;
assert( argc==3 );
@@ -78136,28 +97051,41 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
if( pTable==0 ){
return 0;
}
- if( argv[1] ){
- pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase);
- }else{
+ if( argv[1]==0 ){
pIndex = 0;
+ }else if( sqlite3_stricmp(argv[0],argv[1])==0 ){
+ pIndex = sqlite3PrimaryKeyIndex(pTable);
+ }else{
+ pIndex = sqlite3FindIndex(pInfo->db, argv[1], pInfo->zDatabase);
}
- n = pIndex ? pIndex->nColumn : 0;
z = argv[2];
- for(i=0; *z && i<=n; i++){
- v = 0;
- while( (c=z[0])>='0' && c<='9' ){
- v = v*10 + c - '0';
- z++;
- }
- if( i==0 ) pTable->nRowEst = v;
- if( pIndex==0 ) break;
- pIndex->aiRowEst[i] = v;
- if( *z==' ' ) z++;
- if( memcmp(z, "unordered", 10)==0 ){
- pIndex->bUnordered = 1;
- break;
- }
+
+ if( pIndex ){
+ tRowcnt *aiRowEst = 0;
+ int nCol = pIndex->nKeyCol+1;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ /* Index.aiRowEst may already be set here if there are duplicate
+ ** sqlite_stat1 entries for this index. In that case just clobber
+ ** the old data with the new instead of allocating a new array. */
+ if( pIndex->aiRowEst==0 ){
+ pIndex->aiRowEst = (tRowcnt*)sqlite3MallocZero(sizeof(tRowcnt) * nCol);
+ if( pIndex->aiRowEst==0 ) sqlite3OomFault(pInfo->db);
+ }
+ aiRowEst = pIndex->aiRowEst;
+#endif
+ pIndex->bUnordered = 0;
+ decodeIntArray((char*)z, nCol, aiRowEst, pIndex->aiRowLogEst, pIndex);
+ if( pIndex->pPartIdxWhere==0 ) pTable->nRowLogEst = pIndex->aiRowLogEst[0];
+ }else{
+ Index fakeIdx;
+ fakeIdx.szIdxRow = pTable->szTabRow;
+#ifdef SQLITE_ENABLE_COSTMULT
+ fakeIdx.pTable = pTable;
+#endif
+ decodeIntArray((char*)z, 1, 0, &pTable->nRowLogEst, &fakeIdx);
+ pTable->szTabRow = fakeIdx.szIdxRow;
}
+
return 0;
}
@@ -78166,36 +97094,276 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
** and its contents.
*/
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
if( pIdx->aSample ){
int j;
- for(j=0; j<SQLITE_INDEX_SAMPLES; j++){
+ for(j=0; j<pIdx->nSample; j++){
IndexSample *p = &pIdx->aSample[j];
- if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){
- sqlite3DbFree(db, p->u.z);
- }
+ sqlite3DbFree(db, p->p);
}
sqlite3DbFree(db, pIdx->aSample);
}
+ if( db && db->pnBytesFreed==0 ){
+ pIdx->nSample = 0;
+ pIdx->aSample = 0;
+ }
#else
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(pIdx);
-#endif
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+}
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** Populate the pIdx->aAvgEq[] array based on the samples currently
+** stored in pIdx->aSample[].
+*/
+static void initAvgEq(Index *pIdx){
+ if( pIdx ){
+ IndexSample *aSample = pIdx->aSample;
+ IndexSample *pFinal = &aSample[pIdx->nSample-1];
+ int iCol;
+ int nCol = 1;
+ if( pIdx->nSampleCol>1 ){
+ /* If this is stat4 data, then calculate aAvgEq[] values for all
+ ** sample columns except the last. The last is always set to 1, as
+ ** once the trailing PK fields are considered all index keys are
+ ** unique. */
+ nCol = pIdx->nSampleCol-1;
+ pIdx->aAvgEq[nCol] = 1;
+ }
+ for(iCol=0; iCol<nCol; iCol++){
+ int nSample = pIdx->nSample;
+ int i; /* Used to iterate through samples */
+ tRowcnt sumEq = 0; /* Sum of the nEq values */
+ tRowcnt avgEq = 0;
+ tRowcnt nRow; /* Number of rows in index */
+ i64 nSum100 = 0; /* Number of terms contributing to sumEq */
+ i64 nDist100; /* Number of distinct values in index */
+
+ if( !pIdx->aiRowEst || iCol>=pIdx->nKeyCol || pIdx->aiRowEst[iCol+1]==0 ){
+ nRow = pFinal->anLt[iCol];
+ nDist100 = (i64)100 * pFinal->anDLt[iCol];
+ nSample--;
+ }else{
+ nRow = pIdx->aiRowEst[0];
+ nDist100 = ((i64)100 * pIdx->aiRowEst[0]) / pIdx->aiRowEst[iCol+1];
+ }
+ pIdx->nRowEst0 = nRow;
+
+ /* Set nSum to the number of distinct (iCol+1) field prefixes that
+ ** occur in the stat4 table for this index. Set sumEq to the sum of
+ ** the nEq values for column iCol for the same set (adding the value
+ ** only once where there exist duplicate prefixes). */
+ for(i=0; i<nSample; i++){
+ if( i==(pIdx->nSample-1)
+ || aSample[i].anDLt[iCol]!=aSample[i+1].anDLt[iCol]
+ ){
+ sumEq += aSample[i].anEq[iCol];
+ nSum100 += 100;
+ }
+ }
+
+ if( nDist100>nSum100 ){
+ avgEq = ((i64)100 * (nRow - sumEq))/(nDist100 - nSum100);
+ }
+ if( avgEq==0 ) avgEq = 1;
+ pIdx->aAvgEq[iCol] = avgEq;
+ }
+ }
+}
+
+/*
+** Look up an index by name. Or, if the name of a WITHOUT ROWID table
+** is supplied instead, find the PRIMARY KEY index for that table.
+*/
+static Index *findIndexOrPrimaryKey(
+ sqlite3 *db,
+ const char *zName,
+ const char *zDb
+){
+ Index *pIdx = sqlite3FindIndex(db, zName, zDb);
+ if( pIdx==0 ){
+ Table *pTab = sqlite3FindTable(db, zName, zDb);
+ if( pTab && !HasRowid(pTab) ) pIdx = sqlite3PrimaryKeyIndex(pTab);
+ }
+ return pIdx;
+}
+
+/*
+** Load the content from either the sqlite_stat4 or sqlite_stat3 table
+** into the relevant Index.aSample[] arrays.
+**
+** Arguments zSql1 and zSql2 must point to SQL statements that return
+** data equivalent to the following (statements are different for stat3,
+** see the caller of this function for details):
+**
+** zSql1: SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx
+** zSql2: SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4
+**
+** where %Q is replaced with the database name before the SQL is executed.
+*/
+static int loadStatTbl(
+ sqlite3 *db, /* Database handle */
+ int bStat3, /* Assume single column records only */
+ const char *zSql1, /* SQL statement 1 (see above) */
+ const char *zSql2, /* SQL statement 2 (see above) */
+ const char *zDb /* Database name (e.g. "main") */
+){
+ int rc; /* Result codes from subroutines */
+ sqlite3_stmt *pStmt = 0; /* An SQL statement being run */
+ char *zSql; /* Text of the SQL statement */
+ Index *pPrevIdx = 0; /* Previous index in the loop */
+ IndexSample *pSample; /* A slot in pIdx->aSample[] */
+
+ assert( db->lookaside.bDisable );
+ zSql = sqlite3MPrintf(db, zSql1, zDb);
+ if( !zSql ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ sqlite3DbFree(db, zSql);
+ if( rc ) return rc;
+
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ int nIdxCol = 1; /* Number of columns in stat4 records */
+
+ char *zIndex; /* Index name */
+ Index *pIdx; /* Pointer to the index object */
+ int nSample; /* Number of samples */
+ int nByte; /* Bytes of space required */
+ int i; /* Bytes of space required */
+ tRowcnt *pSpace;
+
+ zIndex = (char *)sqlite3_column_text(pStmt, 0);
+ if( zIndex==0 ) continue;
+ nSample = sqlite3_column_int(pStmt, 1);
+ pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
+ assert( pIdx==0 || bStat3 || pIdx->nSample==0 );
+ /* Index.nSample is non-zero at this point if data has already been
+ ** loaded from the stat4 table. In this case ignore stat3 data. */
+ if( pIdx==0 || pIdx->nSample ) continue;
+ if( bStat3==0 ){
+ assert( !HasRowid(pIdx->pTable) || pIdx->nColumn==pIdx->nKeyCol+1 );
+ if( !HasRowid(pIdx->pTable) && IsPrimaryKeyIndex(pIdx) ){
+ nIdxCol = pIdx->nKeyCol;
+ }else{
+ nIdxCol = pIdx->nColumn;
+ }
+ }
+ pIdx->nSampleCol = nIdxCol;
+ nByte = sizeof(IndexSample) * nSample;
+ nByte += sizeof(tRowcnt) * nIdxCol * 3 * nSample;
+ nByte += nIdxCol * sizeof(tRowcnt); /* Space for Index.aAvgEq[] */
+
+ pIdx->aSample = sqlite3DbMallocZero(db, nByte);
+ if( pIdx->aSample==0 ){
+ sqlite3_finalize(pStmt);
+ return SQLITE_NOMEM_BKPT;
+ }
+ pSpace = (tRowcnt*)&pIdx->aSample[nSample];
+ pIdx->aAvgEq = pSpace; pSpace += nIdxCol;
+ for(i=0; i<nSample; i++){
+ pIdx->aSample[i].anEq = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anLt = pSpace; pSpace += nIdxCol;
+ pIdx->aSample[i].anDLt = pSpace; pSpace += nIdxCol;
+ }
+ assert( ((u8*)pSpace)-nByte==(u8*)(pIdx->aSample) );
+ }
+ rc = sqlite3_finalize(pStmt);
+ if( rc ) return rc;
+
+ zSql = sqlite3MPrintf(db, zSql2, zDb);
+ if( !zSql ){
+ return SQLITE_NOMEM_BKPT;
+ }
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ sqlite3DbFree(db, zSql);
+ if( rc ) return rc;
+
+ while( sqlite3_step(pStmt)==SQLITE_ROW ){
+ char *zIndex; /* Index name */
+ Index *pIdx; /* Pointer to the index object */
+ int nCol = 1; /* Number of columns in index */
+
+ zIndex = (char *)sqlite3_column_text(pStmt, 0);
+ if( zIndex==0 ) continue;
+ pIdx = findIndexOrPrimaryKey(db, zIndex, zDb);
+ if( pIdx==0 ) continue;
+ /* This next condition is true if data has already been loaded from
+ ** the sqlite_stat4 table. In this case ignore stat3 data. */
+ nCol = pIdx->nSampleCol;
+ if( bStat3 && nCol>1 ) continue;
+ if( pIdx!=pPrevIdx ){
+ initAvgEq(pPrevIdx);
+ pPrevIdx = pIdx;
+ }
+ pSample = &pIdx->aSample[pIdx->nSample];
+ decodeIntArray((char*)sqlite3_column_text(pStmt,1),nCol,pSample->anEq,0,0);
+ decodeIntArray((char*)sqlite3_column_text(pStmt,2),nCol,pSample->anLt,0,0);
+ decodeIntArray((char*)sqlite3_column_text(pStmt,3),nCol,pSample->anDLt,0,0);
+
+ /* Take a copy of the sample. Add two 0x00 bytes the end of the buffer.
+ ** This is in case the sample record is corrupted. In that case, the
+ ** sqlite3VdbeRecordCompare() may read up to two varints past the
+ ** end of the allocated buffer before it realizes it is dealing with
+ ** a corrupt record. Adding the two 0x00 bytes prevents this from causing
+ ** a buffer overread. */
+ pSample->n = sqlite3_column_bytes(pStmt, 4);
+ pSample->p = sqlite3DbMallocZero(db, pSample->n + 2);
+ if( pSample->p==0 ){
+ sqlite3_finalize(pStmt);
+ return SQLITE_NOMEM_BKPT;
+ }
+ memcpy(pSample->p, sqlite3_column_blob(pStmt, 4), pSample->n);
+ pIdx->nSample++;
+ }
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) initAvgEq(pPrevIdx);
+ return rc;
}
/*
-** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
+** Load content from the sqlite_stat4 and sqlite_stat3 tables into
+** the Index.aSample[] arrays of all indices.
+*/
+static int loadStat4(sqlite3 *db, const char *zDb){
+ int rc = SQLITE_OK; /* Result codes from subroutines */
+
+ assert( db->lookaside.bDisable );
+ if( sqlite3FindTable(db, "sqlite_stat4", zDb) ){
+ rc = loadStatTbl(db, 0,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat4 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sample FROM %Q.sqlite_stat4",
+ zDb
+ );
+ }
+
+ if( rc==SQLITE_OK && sqlite3FindTable(db, "sqlite_stat3", zDb) ){
+ rc = loadStatTbl(db, 1,
+ "SELECT idx,count(*) FROM %Q.sqlite_stat3 GROUP BY idx",
+ "SELECT idx,neq,nlt,ndlt,sqlite_record(sample) FROM %Q.sqlite_stat3",
+ zDb
+ );
+ }
+
+ return rc;
+}
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+
+/*
+** Load the content of the sqlite_stat1 and sqlite_stat3/4 tables. The
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
-** arrays. The contents of sqlite_stat2 are used to populate the
+** arrays. The contents of sqlite_stat3/4 are used to populate the
** Index.aSample[] arrays.
**
** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR
-** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined
-** during compilation and the sqlite_stat2 table is present, no data is
+** is returned. In this case, even if SQLITE_ENABLE_STAT3/4 was defined
+** during compilation and the sqlite_stat3/4 table is present, no data is
** read from it.
**
-** If SQLITE_ENABLE_STAT2 was defined during compilation and the
-** sqlite_stat2 table is not present in the database, SQLITE_ERROR is
+** If SQLITE_ENABLE_STAT3/4 was defined during compilation and the
+** sqlite_stat4 table is not present in the database, SQLITE_ERROR is
** returned. However, in this case, data is read from the sqlite_stat1
** table (if it is present) before returning.
**
@@ -78207,7 +97375,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
analysisInfo sInfo;
HashElem *i;
char *zSql;
- int rc;
+ int rc = SQLITE_OK;
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pBt!=0 );
@@ -78216,106 +97384,50 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
Index *pIdx = sqliteHashData(i);
- sqlite3DefaultRowEst(pIdx);
+ pIdx->aiRowLogEst[0] = 0;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
sqlite3DeleteIndexSamples(db, pIdx);
pIdx->aSample = 0;
- }
-
- /* Check to make sure the sqlite_stat1 table exists */
- sInfo.db = db;
- sInfo.zDatabase = db->aDb[iDb].zName;
- if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){
- return SQLITE_ERROR;
+#endif
}
/* Load new statistics out of the sqlite_stat1 table */
- zSql = sqlite3MPrintf(db,
- "SELECT tbl, idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
- if( zSql==0 ){
- rc = SQLITE_NOMEM;
- }else{
- rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
- sqlite3DbFree(db, zSql);
- }
-
-
- /* Load the statistics from the sqlite_stat2 table. */
-#ifdef SQLITE_ENABLE_STAT2
- if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){
- rc = SQLITE_ERROR;
- }
- if( rc==SQLITE_OK ){
- sqlite3_stmt *pStmt = 0;
-
+ sInfo.db = db;
+ sInfo.zDatabase = db->aDb[iDb].zDbSName;
+ if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)!=0 ){
zSql = sqlite3MPrintf(db,
- "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase);
- if( !zSql ){
- rc = SQLITE_NOMEM;
+ "SELECT tbl,idx,stat FROM %Q.sqlite_stat1", sInfo.zDatabase);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM_BKPT;
}else{
- rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0);
sqlite3DbFree(db, zSql);
}
+ }
- if( rc==SQLITE_OK ){
- while( sqlite3_step(pStmt)==SQLITE_ROW ){
- char *zIndex; /* Index name */
- Index *pIdx; /* Pointer to the index object */
-
- zIndex = (char *)sqlite3_column_text(pStmt, 0);
- pIdx = zIndex ? sqlite3FindIndex(db, zIndex, sInfo.zDatabase) : 0;
- if( pIdx ){
- int iSample = sqlite3_column_int(pStmt, 1);
- if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
- int eType = sqlite3_column_type(pStmt, 2);
-
- if( pIdx->aSample==0 ){
- static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
- pIdx->aSample = (IndexSample *)sqlite3DbMallocRaw(0, sz);
- if( pIdx->aSample==0 ){
- db->mallocFailed = 1;
- break;
- }
- memset(pIdx->aSample, 0, sz);
- }
+ /* Set appropriate defaults on all indexes not in the sqlite_stat1 table */
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
+ Index *pIdx = sqliteHashData(i);
+ if( pIdx->aiRowLogEst[0]==0 ) sqlite3DefaultRowEst(pIdx);
+ }
- assert( pIdx->aSample );
- {
- IndexSample *pSample = &pIdx->aSample[iSample];
- pSample->eType = (u8)eType;
- if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
- pSample->u.r = sqlite3_column_double(pStmt, 2);
- }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
- const char *z = (const char *)(
- (eType==SQLITE_BLOB) ?
- sqlite3_column_blob(pStmt, 2):
- sqlite3_column_text(pStmt, 2)
- );
- int n = sqlite3_column_bytes(pStmt, 2);
- if( n>24 ){
- n = 24;
- }
- pSample->nByte = (u8)n;
- if( n < 1){
- pSample->u.z = 0;
- }else{
- pSample->u.z = sqlite3DbStrNDup(0, z, n);
- if( pSample->u.z==0 ){
- db->mallocFailed = 1;
- break;
- }
- }
- }
- }
- }
- }
- }
- rc = sqlite3_finalize(pStmt);
- }
+ /* Load the statistics from the sqlite_stat4 table. */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ if( rc==SQLITE_OK && OptimizationEnabled(db, SQLITE_Stat34) ){
+ db->lookaside.bDisable++;
+ rc = loadStat4(db, sInfo.zDatabase);
+ db->lookaside.bDisable--;
+ }
+ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){
+ Index *pIdx = sqliteHashData(i);
+ sqlite3_free(pIdx->aiRowEst);
+ pIdx->aiRowEst = 0;
}
#endif
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return rc;
}
@@ -78338,6 +97450,7 @@ SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
*************************************************************************
** This file contains code used to implement the ATTACH and DETACH commands.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_ATTACH
/*
@@ -78364,10 +97477,6 @@ static int resolveAttachExpr(NameContext *pName, Expr *pExpr)
if( pExpr ){
if( pExpr->op!=TK_ID ){
rc = sqlite3ResolveExprNames(pName, pExpr);
- if( rc==SQLITE_OK && !sqlite3ExprIsConstant(pExpr) ){
- sqlite3ErrorMsg(pName->pParse, "invalid name: \"%s\"", pExpr->u.zToken);
- return SQLITE_ERROR;
- }
}else{
pExpr->op = TK_STRING;
}
@@ -78427,7 +97536,7 @@ static void attachFunc(
goto attach_error;
}
for(i=0; i<db->nDb; i++){
- char *z = db->aDb[i].zName;
+ char *z = db->aDb[i].zDbSName;
assert( z && zName );
if( sqlite3StrICmp(z, zName)==0 ){
zErrDyn = sqlite3MPrintf(db, "database %s is already in use", zName);
@@ -78435,11 +97544,11 @@ static void attachFunc(
}
}
- /* Allocate the new entry in the db->aDb[] array and initialise the schema
+ /* Allocate the new entry in the db->aDb[] array and initialize the schema
** hash tables.
*/
if( db->aDb==db->aDbStatic ){
- aNew = sqlite3DbMallocRaw(db, sizeof(db->aDb[0])*3 );
+ aNew = sqlite3DbMallocRawNN(db, sizeof(db->aDb[0])*3 );
if( aNew==0 ) return;
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
}else{
@@ -78452,12 +97561,12 @@ static void attachFunc(
/* Open the database file. If the btree is successfully opened, use
** it to obtain the database schema. At this point the schema may
- ** or may not be initialised.
+ ** or may not be initialized.
*/
flags = db->openFlags;
rc = sqlite3ParseUri(db->pVfs->zName, zFile, &flags, &pVfs, &zPath, &zErr);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
sqlite3_result_error(context, zErr, -1);
sqlite3_free(zErr);
return;
@@ -78474,21 +97583,27 @@ static void attachFunc(
Pager *pPager;
aNew->pSchema = sqlite3SchemaGet(db, aNew->pBt);
if( !aNew->pSchema ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else if( aNew->pSchema->file_format && aNew->pSchema->enc!=ENC(db) ){
zErrDyn = sqlite3MPrintf(db,
"attached databases must use the same text encoding as main database");
rc = SQLITE_ERROR;
}
+ sqlite3BtreeEnter(aNew->pBt);
pPager = sqlite3BtreePager(aNew->pBt);
sqlite3PagerLockingMode(pPager, db->dfltLockMode);
sqlite3BtreeSecureDelete(aNew->pBt,
sqlite3BtreeSecureDelete(db->aDb[0].pBt,-1) );
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+ sqlite3BtreeSetPagerFlags(aNew->pBt,
+ PAGER_SYNCHRONOUS_FULL | (db->flags & PAGER_FLAGS_MASK));
+#endif
+ sqlite3BtreeLeave(aNew->pBt);
}
- aNew->safety_level = 3;
- aNew->zName = sqlite3DbStrDup(db, zName);
- if( rc==SQLITE_OK && aNew->zName==0 ){
- rc = SQLITE_NOMEM;
+ aNew->safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
+ aNew->zDbSName = sqlite3DbStrDup(db, zName);
+ if( rc==SQLITE_OK && aNew->zDbSName==0 ){
+ rc = SQLITE_NOMEM_BKPT;
}
@@ -78516,7 +97631,7 @@ static void attachFunc(
case SQLITE_NULL:
/* No key specified. Use the key from the main database */
sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey);
- if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){
+ if( nKey || sqlite3BtreeGetOptimalReserve(db->aDb[0].pBt)>0 ){
rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey);
}
break;
@@ -78534,6 +97649,15 @@ static void attachFunc(
rc = sqlite3Init(db, &zErrDyn);
sqlite3BtreeLeaveAll(db);
}
+#ifdef SQLITE_USER_AUTHENTICATION
+ if( rc==SQLITE_OK ){
+ u8 newAuth = 0;
+ rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
+ if( newAuth<db->auth.authLevel ){
+ rc = SQLITE_AUTH_USER;
+ }
+ }
+#endif
if( rc ){
int iDb = db->nDb - 1;
assert( iDb>=2 );
@@ -78542,10 +97666,10 @@ static void attachFunc(
db->aDb[iDb].pBt = 0;
db->aDb[iDb].pSchema = 0;
}
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
db->nDb = iDb;
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, zErrDyn);
zErrDyn = sqlite3MPrintf(db, "out of memory");
}else if( zErrDyn==0 ){
@@ -78590,7 +97714,7 @@ static void detachFunc(
for(i=0; i<db->nDb; i++){
pDb = &db->aDb[i];
if( pDb->pBt==0 ) continue;
- if( sqlite3StrICmp(pDb->zName, zName)==0 ) break;
+ if( sqlite3StrICmp(pDb->zDbSName, zName)==0 ) break;
}
if( i>=db->nDb ){
@@ -78614,7 +97738,7 @@ static void detachFunc(
sqlite3BtreeClose(pDb->pBt);
pDb->pBt = 0;
pDb->pSchema = 0;
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3CollapseDatabaseArray(db);
return;
detach_error:
@@ -78648,7 +97772,6 @@ static void codeAttach(
SQLITE_OK!=(rc = resolveAttachExpr(&sName, pDbname)) ||
SQLITE_OK!=(rc = resolveAttachExpr(&sName, pKey))
){
- pParse->nErr++;
goto attach_end;
}
@@ -78676,11 +97799,11 @@ static void codeAttach(
assert( v || db->mallocFailed );
if( v ){
- sqlite3VdbeAddOp3(v, OP_Function, 0, regArgs+3-pFunc->nArg, regArgs+3);
+ sqlite3VdbeAddOp4(v, OP_Function0, 0, regArgs+3-pFunc->nArg, regArgs+3,
+ (char *)pFunc, P4_FUNCDEF);
assert( pFunc->nArg==-1 || (pFunc->nArg&0xff)==pFunc->nArg );
sqlite3VdbeChangeP5(v, (u8)(pFunc->nArg));
- sqlite3VdbeChangeP4(v, -1, (char *)pFunc, P4_FUNCDEF);
-
+
/* Code an OP_Expire. For an ATTACH statement, set P1 to true (expire this
** statement only). For DETACH, set it to false (expire all existing
** statements).
@@ -78702,16 +97825,13 @@ attach_end:
SQLITE_PRIVATE void sqlite3Detach(Parse *pParse, Expr *pDbname){
static const FuncDef detach_func = {
1, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
+ SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
- detachFunc, /* xFunc */
- 0, /* xStep */
+ detachFunc, /* xSFunc */
0, /* xFinalize */
"sqlite_detach", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ {0}
};
codeAttach(pParse, SQLITE_DETACH, &detach_func, pDbname, 0, 0, pDbname);
}
@@ -78724,16 +97844,13 @@ SQLITE_PRIVATE void sqlite3Detach(Parse *pParse, Expr *pDbname){
SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *pKey){
static const FuncDef attach_func = {
3, /* nArg */
- SQLITE_UTF8, /* iPrefEnc */
- 0, /* flags */
+ SQLITE_UTF8, /* funcFlags */
0, /* pUserData */
0, /* pNext */
- attachFunc, /* xFunc */
- 0, /* xStep */
+ attachFunc, /* xSFunc */
0, /* xFinalize */
"sqlite_attach", /* zName */
- 0, /* pHash */
- 0 /* pDestructor */
+ {0}
};
codeAttach(pParse, SQLITE_ATTACH, &attach_func, p, p, pDbname, pKey);
}
@@ -78742,11 +97859,8 @@ SQLITE_PRIVATE void sqlite3Attach(Parse *pParse, Expr *p, Expr *pDbname, Expr *p
/*
** Initialize a DbFixer structure. This routine must be called prior
** to passing the structure to one of the sqliteFixAAAA() routines below.
-**
-** The return value indicates whether or not fixation is required. TRUE
-** means we do need to fix the database references, FALSE means we do not.
*/
-SQLITE_PRIVATE int sqlite3FixInit(
+SQLITE_PRIVATE void sqlite3FixInit(
DbFixer *pFix, /* The fixer to be initialized */
Parse *pParse, /* Error messages will be written here */
int iDb, /* This is the database that must be used */
@@ -78755,14 +97869,14 @@ SQLITE_PRIVATE int sqlite3FixInit(
){
sqlite3 *db;
- if( NEVER(iDb<0) || iDb==1 ) return 0;
db = pParse->db;
assert( db->nDb>iDb );
pFix->pParse = pParse;
- pFix->zDb = db->aDb[iDb].zName;
+ pFix->zDb = db->aDb[iDb].zDbSName;
+ pFix->pSchema = db->aDb[iDb].pSchema;
pFix->zType = zType;
pFix->pName = pName;
- return 1;
+ pFix->bVarOnly = (iDb==1);
}
/*
@@ -78790,13 +97904,16 @@ SQLITE_PRIVATE int sqlite3FixSrcList(
if( NEVER(pList==0) ) return 0;
zDb = pFix->zDb;
for(i=0, pItem=pList->a; i<pList->nSrc; i++, pItem++){
- if( pItem->zDatabase==0 ){
- pItem->zDatabase = sqlite3DbStrDup(pFix->pParse->db, zDb);
- }else if( sqlite3StrICmp(pItem->zDatabase,zDb)!=0 ){
- sqlite3ErrorMsg(pFix->pParse,
- "%s %T cannot reference objects in database %s",
- pFix->zType, pFix->pName, pItem->zDatabase);
- return 1;
+ if( pFix->bVarOnly==0 ){
+ if( pItem->zDatabase && sqlite3StrICmp(pItem->zDatabase, zDb) ){
+ sqlite3ErrorMsg(pFix->pParse,
+ "%s %T cannot reference objects in database %s",
+ pFix->zType, pFix->pName, pItem->zDatabase);
+ return 1;
+ }
+ sqlite3DbFree(pFix->pParse->db, pItem->zDatabase);
+ pItem->zDatabase = 0;
+ pItem->pSchema = pFix->pSchema;
}
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER)
if( sqlite3FixSelect(pFix, pItem->pSelect) ) return 1;
@@ -78820,9 +97937,21 @@ SQLITE_PRIVATE int sqlite3FixSelect(
if( sqlite3FixExpr(pFix, pSelect->pWhere) ){
return 1;
}
+ if( sqlite3FixExprList(pFix, pSelect->pGroupBy) ){
+ return 1;
+ }
if( sqlite3FixExpr(pFix, pSelect->pHaving) ){
return 1;
}
+ if( sqlite3FixExprList(pFix, pSelect->pOrderBy) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pLimit) ){
+ return 1;
+ }
+ if( sqlite3FixExpr(pFix, pSelect->pOffset) ){
+ return 1;
+ }
pSelect = pSelect->pPrior;
}
return 0;
@@ -78832,7 +97961,15 @@ SQLITE_PRIVATE int sqlite3FixExpr(
Expr *pExpr /* The expression to be fixed to one database */
){
while( pExpr ){
- if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ) break;
+ if( pExpr->op==TK_VARIABLE ){
+ if( pFix->pParse->db->init.busy ){
+ pExpr->op = TK_NULL;
+ }else{
+ sqlite3ErrorMsg(pFix->pParse, "%s cannot use variables", pFix->zType);
+ return 1;
+ }
+ }
+ if( ExprHasProperty(pExpr, EP_TokenOnly|EP_Leaf) ) break;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
if( sqlite3FixSelect(pFix, pExpr->x.pSelect) ) return 1;
}else{
@@ -78900,6 +98037,7 @@ SQLITE_PRIVATE int sqlite3FixTriggerStep(
** systems that do not need this facility may omit it by recompiling
** the library with -DSQLITE_OMIT_AUTHORIZATION=1
*/
+/* #include "sqliteInt.h" */
/*
** All of the code in this file may be omitted by defining a single
@@ -78957,8 +98095,11 @@ SQLITE_API int sqlite3_set_authorizer(
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
void *pArg
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
- db->xAuth = xAuth;
+ db->xAuth = (sqlite3_xauth)xAuth;
db->pAuthArg = pArg;
sqlite3ExpirePreparedStatements(db);
sqlite3_mutex_leave(db->mutex);
@@ -78989,11 +98130,16 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(
const char *zCol, /* Column name */
int iDb /* Index of containing database. */
){
- sqlite3 *db = pParse->db; /* Database handle */
- char *zDb = db->aDb[iDb].zName; /* Name of attached database */
- int rc; /* Auth callback return code */
+ sqlite3 *db = pParse->db; /* Database handle */
+ char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
+ int rc; /* Auth callback return code */
- rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext);
+ if( db->init.busy ) return SQLITE_OK;
+ rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext
+#ifdef SQLITE_USER_AUTHENTICATION
+ ,db->auth.zAuthUser
+#endif
+ );
if( rc==SQLITE_DENY ){
if( db->nDb>2 || iDb!=0 ){
sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol);
@@ -79093,7 +98239,11 @@ SQLITE_PRIVATE int sqlite3AuthCheck(
if( db->xAuth==0 ){
return SQLITE_OK;
}
- rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext);
+ rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext
+#ifdef SQLITE_USER_AUTHENTICATION
+ ,db->auth.zAuthUser
+#endif
+ );
if( rc==SQLITE_DENY ){
sqlite3ErrorMsg(pParse, "not authorized");
pParse->rc = SQLITE_AUTH;
@@ -79159,15 +98309,7 @@ SQLITE_PRIVATE void sqlite3AuthContextPop(AuthContext *pContext){
** COMMIT
** ROLLBACK
*/
-
-/*
-** This routine is called when a new SQL statement is beginning to
-** be parsed. Initialize the pParse structure as needed.
-*/
-SQLITE_PRIVATE void sqlite3BeginParse(Parse *pParse, int explainFlag){
- pParse->explain = (u8)explainFlag;
- pParse->nVar = 0;
-}
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_SHARED_CACHE
/*
@@ -79223,7 +98365,7 @@ SQLITE_PRIVATE void sqlite3TableLock(
p->zName = zName;
}else{
pToplevel->nTableLock = 0;
- pToplevel->db->mallocFailed = 1;
+ sqlite3OomFault(pToplevel->db);
}
}
@@ -79250,6 +98392,19 @@ static void codeTableLocks(Parse *pParse){
#endif
/*
+** Return TRUE if the given yDbMask object is empty - if it contains no
+** 1 bits. This routine is used by the DbMaskAllZero() and DbMaskNotZero()
+** macros when SQLITE_MAX_ATTACHED is greater than 30.
+*/
+#if SQLITE_MAX_ATTACHED>30
+SQLITE_PRIVATE int sqlite3DbMaskAllZero(yDbMask m){
+ int i;
+ for(i=0; i<sizeof(yDbMask); i++) if( m[i] ) return 0;
+ return 1;
+}
+#endif
+
+/*
** This routine is called after a single SQL statement has been
** parsed and a VDBE program to execute that statement has been
** prepared. This routine puts the finishing touches on the
@@ -79263,10 +98418,13 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
sqlite3 *db;
Vdbe *v;
+ assert( pParse->pToplevel==0 );
db = pParse->db;
- if( db->mallocFailed ) return;
if( pParse->nested ) return;
- if( pParse->nErr ) return;
+ if( db->mallocFailed || pParse->nErr ){
+ if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR;
+ return;
+ }
/* Begin by generating some termination code at the end of the
** vdbe program
@@ -79277,36 +98435,51 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
if( v ){
sqlite3VdbeAddOp0(v, OP_Halt);
+#if SQLITE_USER_AUTHENTICATION
+ if( pParse->nTableLock>0 && db->init.busy==0 ){
+ sqlite3UserAuthInit(db);
+ if( db->auth.authLevel<UAUTH_User ){
+ sqlite3ErrorMsg(pParse, "user not authenticated");
+ pParse->rc = SQLITE_AUTH_USER;
+ return;
+ }
+ }
+#endif
+
/* The cookie mask contains one bit for each database file open.
** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are
** set for each database that is used. Generate code to start a
** transaction on each used database and to verify the schema cookie
** on each used database.
*/
- if( pParse->cookieGoto>0 ){
- yDbMask mask;
- int iDb;
- sqlite3VdbeJumpHere(v, pParse->cookieGoto-1);
- for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){
- if( (mask & pParse->cookieMask)==0 ) continue;
+ if( db->mallocFailed==0
+ && (DbMaskNonZero(pParse->cookieMask) || pParse->pConstExpr)
+ ){
+ int iDb, i;
+ assert( sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
+ sqlite3VdbeJumpHere(v, 0);
+ for(iDb=0; iDb<db->nDb; iDb++){
+ Schema *pSchema;
+ if( DbMaskTest(pParse->cookieMask, iDb)==0 ) continue;
sqlite3VdbeUsesBtree(v, iDb);
- sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0);
- if( db->init.busy==0 ){
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- sqlite3VdbeAddOp3(v, OP_VerifyCookie,
- iDb, pParse->cookieValue[iDb],
- db->aDb[iDb].pSchema->iGeneration);
- }
+ pSchema = db->aDb[iDb].pSchema;
+ sqlite3VdbeAddOp4Int(v,
+ OP_Transaction, /* Opcode */
+ iDb, /* P1 */
+ DbMaskTest(pParse->writeMask,iDb), /* P2 */
+ pSchema->schema_cookie, /* P3 */
+ pSchema->iGeneration /* P4 */
+ );
+ if( db->init.busy==0 ) sqlite3VdbeChangeP5(v, 1);
+ VdbeComment((v,
+ "usesStmtJournal=%d", pParse->mayAbort && pParse->isMultiWrite));
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
- {
- int i;
- for(i=0; i<pParse->nVtabLock; i++){
- char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
- sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
- }
- pParse->nVtabLock = 0;
+ for(i=0; i<pParse->nVtabLock; i++){
+ char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]);
+ sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB);
}
+ pParse->nVtabLock = 0;
#endif
/* Once all the cookies have been verified and transactions opened,
@@ -79319,35 +98492,33 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
*/
sqlite3AutoincrementBegin(pParse);
+ /* Code constant expressions that where factored out of inner loops */
+ if( pParse->pConstExpr ){
+ ExprList *pEL = pParse->pConstExpr;
+ pParse->okConstFactor = 0;
+ for(i=0; i<pEL->nExpr; i++){
+ sqlite3ExprCode(pParse, pEL->a[i].pExpr, pEL->a[i].u.iConstExprReg);
+ }
+ }
+
/* Finally, jump back to the beginning of the executable code. */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->cookieGoto);
+ sqlite3VdbeGoto(v, 1);
}
}
/* Get the VDBE program ready for execution
*/
- if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){
-#ifdef SQLITE_DEBUG
- FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0;
- sqlite3VdbeTrace(v, trace);
-#endif
+ if( v && pParse->nErr==0 && !db->mallocFailed ){
assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */
/* A minimum of one cursor is required if autoincrement is used
* See ticket [a696379c1f08866] */
if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1;
sqlite3VdbeMakeReady(v, pParse);
pParse->rc = SQLITE_DONE;
- pParse->colNamesSet = 0;
}else{
pParse->rc = SQLITE_ERROR;
}
- pParse->nTab = 0;
- pParse->nMem = 0;
- pParse->nSet = 0;
- pParse->nVar = 0;
- pParse->cookieMask = 0;
- pParse->cookieGoto = 0;
}
/*
@@ -79367,8 +98538,7 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
char *zSql;
char *zErrMsg = 0;
sqlite3 *db = pParse->db;
-# define SAVE_SZ (sizeof(Parse) - offsetof(Parse,nVar))
- char saveBuf[SAVE_SZ];
+ char saveBuf[PARSE_TAIL_SZ];
if( pParse->nErr ) return;
assert( pParse->nested<10 ); /* Nesting should only be of limited depth */
@@ -79379,15 +98549,25 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
return; /* A malloc must have failed */
}
pParse->nested++;
- memcpy(saveBuf, &pParse->nVar, SAVE_SZ);
- memset(&pParse->nVar, 0, SAVE_SZ);
+ memcpy(saveBuf, PARSE_TAIL(pParse), PARSE_TAIL_SZ);
+ memset(PARSE_TAIL(pParse), 0, PARSE_TAIL_SZ);
sqlite3RunParser(pParse, zSql, &zErrMsg);
sqlite3DbFree(db, zErrMsg);
sqlite3DbFree(db, zSql);
- memcpy(&pParse->nVar, saveBuf, SAVE_SZ);
+ memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
pParse->nested--;
}
+#if SQLITE_USER_AUTHENTICATION
+/*
+** Return TRUE if zTable is the name of the system table that stores the
+** list of users and their access credentials.
+*/
+SQLITE_PRIVATE int sqlite3UserAuthTable(const char *zTable){
+ return sqlite3_stricmp(zTable, "sqlite_user")==0;
+}
+#endif
+
/*
** Locate the in-memory structure that describes a particular database
** table given the name of that table and (optionally) the name of the
@@ -79403,17 +98583,23 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
Table *p = 0;
int i;
- int nName;
- assert( zName!=0 );
- nName = sqlite3Strlen30(zName);
+
/* All mutexes are required for schema access. Make sure we hold them. */
assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) );
+#if SQLITE_USER_AUTHENTICATION
+ /* Only the admin user is allowed to know that the sqlite_user table
+ ** exists */
+ if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){
+ return 0;
+ }
+#endif
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
- if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue;
- assert( sqlite3SchemaMutexHeld(db, j, 0) );
- p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, nName);
- if( p ) break;
+ if( zDatabase==0 || sqlite3StrICmp(zDatabase, db->aDb[j].zDbSName)==0 ){
+ assert( sqlite3SchemaMutexHeld(db, j, 0) );
+ p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName);
+ if( p ) break;
+ }
}
return p;
}
@@ -79430,7 +98616,7 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha
*/
SQLITE_PRIVATE Table *sqlite3LocateTable(
Parse *pParse, /* context in which to report errors */
- int isView, /* True if looking for a VIEW rather than a TABLE */
+ u32 flags, /* LOCATE_VIEW or LOCATE_NOERR */
const char *zName, /* Name of the table we are looking for */
const char *zDbase /* Name of the database. Might be NULL */
){
@@ -79444,18 +98630,57 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
p = sqlite3FindTable(pParse->db, zName, zDbase);
if( p==0 ){
- const char *zMsg = isView ? "no such view" : "no such table";
- if( zDbase ){
- sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
- }else{
- sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
+ const char *zMsg = flags & LOCATE_VIEW ? "no such view" : "no such table";
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( sqlite3FindDbName(pParse->db, zDbase)<1 ){
+ /* If zName is the not the name of a table in the schema created using
+ ** CREATE, then check to see if it is the name of an virtual table that
+ ** can be an eponymous virtual table. */
+ Module *pMod = (Module*)sqlite3HashFind(&pParse->db->aModule, zName);
+ if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
+ return pMod->pEpoTab;
+ }
+ }
+#endif
+ if( (flags & LOCATE_NOERR)==0 ){
+ if( zDbase ){
+ sqlite3ErrorMsg(pParse, "%s: %s.%s", zMsg, zDbase, zName);
+ }else{
+ sqlite3ErrorMsg(pParse, "%s: %s", zMsg, zName);
+ }
+ pParse->checkSchema = 1;
}
- pParse->checkSchema = 1;
}
+
return p;
}
/*
+** Locate the table identified by *p.
+**
+** This is a wrapper around sqlite3LocateTable(). The difference between
+** sqlite3LocateTable() and this function is that this function restricts
+** the search to schema (p->pSchema) if it is not NULL. p->pSchema may be
+** non-NULL if it is part of a view or trigger program definition. See
+** sqlite3FixSrcList() for details.
+*/
+SQLITE_PRIVATE Table *sqlite3LocateTableItem(
+ Parse *pParse,
+ u32 flags,
+ struct SrcList_item *p
+){
+ const char *zDb;
+ assert( p->pSchema==0 || p->zDatabase==0 );
+ if( p->pSchema ){
+ int iDb = sqlite3SchemaToIndex(pParse->db, p->pSchema);
+ zDb = pParse->db->aDb[iDb].zDbSName;
+ }else{
+ zDb = p->zDatabase;
+ }
+ return sqlite3LocateTable(pParse, flags, p->zName, zDb);
+}
+
+/*
** Locate the in-memory structure that describes
** a particular index given the name of that index
** and the name of the database that contains the index.
@@ -79470,16 +98695,15 @@ SQLITE_PRIVATE Table *sqlite3LocateTable(
SQLITE_PRIVATE Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
Index *p = 0;
int i;
- int nName = sqlite3Strlen30(zName);
/* All mutexes are required for schema access. Make sure we hold them. */
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
Schema *pSchema = db->aDb[j].pSchema;
assert( pSchema );
- if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue;
+ if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zDbSName) ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
- p = sqlite3HashFind(&pSchema->idxHash, zName, nName);
+ p = sqlite3HashFind(&pSchema->idxHash, zName);
if( p ) break;
}
return p;
@@ -79492,7 +98716,13 @@ static void freeIndex(sqlite3 *db, Index *p){
#ifndef SQLITE_OMIT_ANALYZE
sqlite3DeleteIndexSamples(db, p);
#endif
+ sqlite3ExprDelete(db, p->pPartIdxWhere);
+ sqlite3ExprListDelete(db, p->aColExpr);
sqlite3DbFree(db, p->zColAff);
+ if( p->isResized ) sqlite3DbFree(db, (void *)p->azColl);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3_free(p->aiRowEst);
+#endif
sqlite3DbFree(db, p);
}
@@ -79504,13 +98734,11 @@ static void freeIndex(sqlite3 *db, Index *p){
*/
SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){
Index *pIndex;
- int len;
Hash *pHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pHash = &db->aDb[iDb].pSchema->idxHash;
- len = sqlite3Strlen30(zIdxName);
- pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0);
+ pIndex = sqlite3HashInsert(pHash, zIdxName, 0);
if( ALWAYS(pIndex) ){
if( pIndex->pTable->pIndex==pIndex ){
pIndex->pTable->pIndex = pIndex->pNext;
@@ -79530,63 +98758,20 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char
}
/*
-** Erase all schema information from the in-memory hash tables of
-** a single database. This routine is called to reclaim memory
-** before the database closes. It is also called during a rollback
-** if there were schema changes during the transaction or if a
-** schema-cookie mismatch occurs.
+** Look through the list of open database files in db->aDb[] and if
+** any have been closed, remove them from the list. Reallocate the
+** db->aDb[] structure to a smaller size, if possible.
**
-** If iDb<0 then reset the internal schema tables for all database
-** files. If iDb>=0 then reset the internal schema for only the
-** single file indicated.
+** Entry 0 (the "main" database) and entry 1 (the "temp" database)
+** are never candidates for being collapsed.
*/
-SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
+SQLITE_PRIVATE void sqlite3CollapseDatabaseArray(sqlite3 *db){
int i, j;
- assert( iDb<db->nDb );
-
- if( iDb>=0 ){
- /* Case 1: Reset the single schema identified by iDb */
- Db *pDb = &db->aDb[iDb];
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- assert( pDb->pSchema!=0 );
- sqlite3SchemaClear(pDb->pSchema);
-
- /* If any database other than TEMP is reset, then also reset TEMP
- ** since TEMP might be holding triggers that reference tables in the
- ** other database.
- */
- if( iDb!=1 ){
- pDb = &db->aDb[1];
- assert( pDb->pSchema!=0 );
- sqlite3SchemaClear(pDb->pSchema);
- }
- return;
- }
- /* Case 2 (from here to the end): Reset all schemas for all attached
- ** databases. */
- assert( iDb<0 );
- sqlite3BtreeEnterAll(db);
- for(i=0; i<db->nDb; i++){
- Db *pDb = &db->aDb[i];
- if( pDb->pSchema ){
- sqlite3SchemaClear(pDb->pSchema);
- }
- }
- db->flags &= ~SQLITE_InternChanges;
- sqlite3VtabUnlockList(db);
- sqlite3BtreeLeaveAll(db);
-
- /* If one or more of the auxiliary database files has been closed,
- ** then remove them from the auxiliary database list. We take the
- ** opportunity to do this here since we have just deleted all of the
- ** schema hash tables and therefore do not have to make any changes
- ** to any of those tables.
- */
for(i=j=2; i<db->nDb; i++){
struct Db *pDb = &db->aDb[i];
if( pDb->pBt==0 ){
- sqlite3DbFree(db, pDb->zName);
- pDb->zName = 0;
+ sqlite3DbFree(db, pDb->zDbSName);
+ pDb->zDbSName = 0;
continue;
}
if( j<i ){
@@ -79594,7 +98779,6 @@ SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
}
j++;
}
- memset(&db->aDb[j], 0, (db->nDb-j)*sizeof(db->aDb[j]));
db->nDb = j;
if( db->nDb<=2 && db->aDb!=db->aDbStatic ){
memcpy(db->aDbStatic, db->aDb, 2*sizeof(db->aDb[0]));
@@ -79604,6 +98788,51 @@ SQLITE_PRIVATE void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){
}
/*
+** Reset the schema for the database at index iDb. Also reset the
+** TEMP schema.
+*/
+SQLITE_PRIVATE void sqlite3ResetOneSchema(sqlite3 *db, int iDb){
+ Db *pDb;
+ assert( iDb<db->nDb );
+
+ /* Case 1: Reset the single schema identified by iDb */
+ pDb = &db->aDb[iDb];
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( pDb->pSchema!=0 );
+ sqlite3SchemaClear(pDb->pSchema);
+
+ /* If any database other than TEMP is reset, then also reset TEMP
+ ** since TEMP might be holding triggers that reference tables in the
+ ** other database.
+ */
+ if( iDb!=1 ){
+ pDb = &db->aDb[1];
+ assert( pDb->pSchema!=0 );
+ sqlite3SchemaClear(pDb->pSchema);
+ }
+ return;
+}
+
+/*
+** Erase all schema information from all attached databases (including
+** "main" and "temp") for a single database connection.
+*/
+SQLITE_PRIVATE void sqlite3ResetAllSchemasOfConnection(sqlite3 *db){
+ int i;
+ sqlite3BtreeEnterAll(db);
+ for(i=0; i<db->nDb; i++){
+ Db *pDb = &db->aDb[i];
+ if( pDb->pSchema ){
+ sqlite3SchemaClear(pDb->pSchema);
+ }
+ }
+ db->flags &= ~SQLITE_InternChanges;
+ sqlite3VtabUnlockList(db);
+ sqlite3BtreeLeaveAll(db);
+ sqlite3CollapseDatabaseArray(db);
+}
+
+/*
** This routine is called when a commit occurs.
*/
SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){
@@ -79614,7 +98843,7 @@ SQLITE_PRIVATE void sqlite3CommitInternalChanges(sqlite3 *db){
** Delete memory allocated for the column names of a table or view (the
** Table.aCol[] array).
*/
-static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){
+SQLITE_PRIVATE void sqlite3DeleteColumnNames(sqlite3 *db, Table *pTable){
int i;
Column *pCol;
assert( pTable!=0 );
@@ -79622,8 +98851,6 @@ static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){
for(i=0; i<pTable->nCol; i++, pCol++){
sqlite3DbFree(db, pCol->zName);
sqlite3ExprDelete(db, pCol->pDflt);
- sqlite3DbFree(db, pCol->zDflt);
- sqlite3DbFree(db, pCol->zType);
sqlite3DbFree(db, pCol->zColl);
}
sqlite3DbFree(db, pTable->aCol);
@@ -79638,24 +98865,32 @@ static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){
** the table data structure from the hash table. But it does destroy
** memory structures of the indices and foreign keys associated with
** the table.
+**
+** The db parameter is optional. It is needed if the Table object
+** contains lookaside memory. (Table objects in the schema do not use
+** lookaside memory, but some ephemeral Table objects do.) Or the
+** db parameter can be used with db->pnBytesFreed to measure the memory
+** used by the Table object.
*/
-SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
+static void SQLITE_NOINLINE deleteTable(sqlite3 *db, Table *pTable){
Index *pIndex, *pNext;
+ TESTONLY( int nLookaside; ) /* Used to verify lookaside not used for schema */
- assert( !pTable || pTable->nRef>0 );
-
- /* Do not delete the table until the reference count reaches zero. */
- if( !pTable ) return;
- if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
+ /* Record the number of outstanding lookaside allocations in schema Tables
+ ** prior to doing any free() operations. Since schema Tables do not use
+ ** lookaside, this number should not change. */
+ TESTONLY( nLookaside = (db && (pTable->tabFlags & TF_Ephemeral)==0) ?
+ db->lookaside.nOut : 0 );
/* Delete all indices associated with this table. */
for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){
pNext = pIndex->pNext;
- assert( pIndex->pSchema==pTable->pSchema );
- if( !db || db->pnBytesFreed==0 ){
+ assert( pIndex->pSchema==pTable->pSchema
+ || (IsVirtual(pTable) && pIndex->idxType!=SQLITE_IDXTYPE_APPDEF) );
+ if( (db==0 || db->pnBytesFreed==0) && !IsVirtual(pTable) ){
char *zName = pIndex->zName;
TESTONLY ( Index *pOld = ) sqlite3HashInsert(
- &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0
+ &pIndex->pSchema->idxHash, zName, 0
);
assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
assert( pOld==pIndex || pOld==0 );
@@ -79668,19 +98903,27 @@ SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
/* Delete the Table structure itself.
*/
- sqliteDeleteColumnNames(db, pTable);
+ sqlite3DeleteColumnNames(db, pTable);
sqlite3DbFree(db, pTable->zName);
sqlite3DbFree(db, pTable->zColAff);
sqlite3SelectDelete(db, pTable->pSelect);
-#ifndef SQLITE_OMIT_CHECK
- sqlite3ExprDelete(db, pTable->pCheck);
-#endif
+ sqlite3ExprListDelete(db, pTable->pCheck);
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3VtabClear(db, pTable);
#endif
sqlite3DbFree(db, pTable);
+
+ /* Verify that no lookaside memory was used by schema tables */
+ assert( nLookaside==0 || nLookaside==db->lookaside.nOut );
+}
+SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
+ /* Do not delete the table until the reference count reaches zero. */
+ if( !pTable ) return;
+ if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return;
+ deleteTable(db, pTable);
}
+
/*
** Unlink the given table from the hash tables and the delete the
** table structure with all its indices and foreign keys.
@@ -79695,8 +98938,7 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */
pDb = &db->aDb[iDb];
- p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName,
- sqlite3Strlen30(zTabName),0);
+ p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, 0);
sqlite3DeleteTable(db, p);
db->flags |= SQLITE_InternChanges;
}
@@ -79732,8 +98974,7 @@ SQLITE_PRIVATE char *sqlite3NameFromToken(sqlite3 *db, Token *pName){
SQLITE_PRIVATE void sqlite3OpenMasterTable(Parse *p, int iDb){
Vdbe *v = sqlite3GetVdbe(p);
sqlite3TableLock(p, iDb, MASTER_ROOT, 1, SCHEMA_TABLE(iDb));
- sqlite3VdbeAddOp3(v, OP_OpenWrite, 0, MASTER_ROOT, iDb);
- sqlite3VdbeChangeP4(v, -1, (char *)5, P4_INT32); /* 5 column table */
+ sqlite3VdbeAddOp4Int(v, OP_OpenWrite, 0, MASTER_ROOT, iDb, 5);
if( p->nTab==0 ){
p->nTab = 1;
}
@@ -79749,12 +98990,8 @@ SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *db, const char *zName){
int i = -1; /* Database number */
if( zName ){
Db *pDb;
- int n = sqlite3Strlen30(zName);
for(i=(db->nDb-1), pDb=&db->aDb[i]; i>=0; i--, pDb--){
- if( (!OMIT_TEMPDB || i!=1 ) && n==sqlite3Strlen30(pDb->zName) &&
- 0==sqlite3StrICmp(pDb->zName, zName) ){
- break;
- }
+ if( 0==sqlite3StrICmp(pDb->zDbSName, zName) ) break;
}
}
return i;
@@ -79800,21 +99037,20 @@ SQLITE_PRIVATE int sqlite3TwoPartName(
int iDb; /* Database holding the object */
sqlite3 *db = pParse->db;
- if( ALWAYS(pName2!=0) && pName2->n>0 ){
+ assert( pName2!=0 );
+ if( pName2->n>0 ){
if( db->init.busy ) {
sqlite3ErrorMsg(pParse, "corrupt database");
- pParse->nErr++;
return -1;
}
*pUnqual = pName2;
iDb = sqlite3FindDb(db, pName1);
if( iDb<0 ){
sqlite3ErrorMsg(pParse, "unknown database %T", pName1);
- pParse->nErr++;
return -1;
}
}else{
- assert( db->init.iDb==0 || db->init.busy );
+ assert( db->init.iDb==0 || db->init.busy || (db->flags & SQLITE_Vacuum)!=0);
iDb = db->init.iDb;
*pUnqual = pName1;
}
@@ -79839,6 +99075,27 @@ SQLITE_PRIVATE int sqlite3CheckObjectName(Parse *pParse, const char *zName){
}
/*
+** Return the PRIMARY KEY index of a table
+*/
+SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table *pTab){
+ Index *p;
+ for(p=pTab->pIndex; p && !IsPrimaryKeyIndex(p); p=p->pNext){}
+ return p;
+}
+
+/*
+** Return the column of index pIdx that corresponds to table
+** column iCol. Return -1 if not found.
+*/
+SQLITE_PRIVATE i16 sqlite3ColumnOfIndex(Index *pIdx, i16 iCol){
+ int i;
+ for(i=0; i<pIdx->nColumn; i++){
+ if( iCol==pIdx->aiColumn[i] ) return i;
+ }
+ return -1;
+}
+
+/*
** Begin constructing a new table representation in memory. This is
** the first of several action routines that get called in response
** to a CREATE TABLE statement. In particular, this routine is called
@@ -79870,62 +99127,46 @@ SQLITE_PRIVATE void sqlite3StartTable(
int iDb; /* Database number to create the table in */
Token *pName; /* Unqualified name of the table to create */
- /* The table or view name to create is passed to this routine via tokens
- ** pName1 and pName2. If the table name was fully qualified, for example:
- **
- ** CREATE TABLE xxx.yyy (...);
- **
- ** Then pName1 is set to "xxx" and pName2 "yyy". On the other hand if
- ** the table name is not fully qualified, i.e.:
- **
- ** CREATE TABLE yyy(...);
- **
- ** Then pName1 is set to "yyy" and pName2 is "".
- **
- ** The call below sets the pName pointer to point at the token (pName1 or
- ** pName2) that stores the unqualified table name. The variable iDb is
- ** set to the index of the database that the table or view is to be
- ** created in.
- */
- iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
- if( iDb<0 ) return;
- if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
- /* If creating a temp table, the name may not be qualified. Unless
- ** the database name is "temp" anyway. */
- sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
- return;
+ if( db->init.busy && db->init.newTnum==1 ){
+ /* Special case: Parsing the sqlite_master or sqlite_temp_master schema */
+ iDb = db->init.iDb;
+ zName = sqlite3DbStrDup(db, SCHEMA_TABLE(iDb));
+ pName = pName1;
+ }else{
+ /* The common case */
+ iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
+ if( iDb<0 ) return;
+ if( !OMIT_TEMPDB && isTemp && pName2->n>0 && iDb!=1 ){
+ /* If creating a temp table, the name may not be qualified. Unless
+ ** the database name is "temp" anyway. */
+ sqlite3ErrorMsg(pParse, "temporary table name must be unqualified");
+ return;
+ }
+ if( !OMIT_TEMPDB && isTemp ) iDb = 1;
+ zName = sqlite3NameFromToken(db, pName);
}
- if( !OMIT_TEMPDB && isTemp ) iDb = 1;
-
pParse->sNameToken = *pName;
- zName = sqlite3NameFromToken(db, pName);
if( zName==0 ) return;
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto begin_table_error;
}
if( db->init.iDb==1 ) isTemp = 1;
#ifndef SQLITE_OMIT_AUTHORIZATION
- assert( (isTemp & 1)==isTemp );
+ assert( isTemp==0 || isTemp==1 );
+ assert( isView==0 || isView==1 );
{
- int code;
- char *zDb = db->aDb[iDb].zName;
+ static const u8 aCode[] = {
+ SQLITE_CREATE_TABLE,
+ SQLITE_CREATE_TEMP_TABLE,
+ SQLITE_CREATE_VIEW,
+ SQLITE_CREATE_TEMP_VIEW
+ };
+ char *zDb = db->aDb[iDb].zDbSName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(isTemp), 0, zDb) ){
goto begin_table_error;
}
- if( isView ){
- if( !OMIT_TEMPDB && isTemp ){
- code = SQLITE_CREATE_TEMP_VIEW;
- }else{
- code = SQLITE_CREATE_VIEW;
- }
- }else{
- if( !OMIT_TEMPDB && isTemp ){
- code = SQLITE_CREATE_TEMP_TABLE;
- }else{
- code = SQLITE_CREATE_TABLE;
- }
- }
- if( !isVirtual && sqlite3AuthCheck(pParse, code, zName, 0, zDb) ){
+ if( !isVirtual && sqlite3AuthCheck(pParse, (int)aCode[isTemp+2*isView],
+ zName, 0, zDb) ){
goto begin_table_error;
}
}
@@ -79939,7 +99180,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
** collisions.
*/
if( !IN_DECLARE_VTAB ){
- char *zDb = db->aDb[iDb].zName;
+ char *zDb = db->aDb[iDb].zDbSName;
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
goto begin_table_error;
}
@@ -79948,7 +99189,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
if( !noErr ){
sqlite3ErrorMsg(pParse, "table %T already exists", pName);
}else{
- assert( !db->init.busy );
+ assert( !db->init.busy || CORRUPT_DB );
sqlite3CodeVerifySchema(pParse, iDb);
}
goto begin_table_error;
@@ -79961,8 +99202,8 @@ SQLITE_PRIVATE void sqlite3StartTable(
pTable = sqlite3DbMallocZero(db, sizeof(Table));
if( pTable==0 ){
- db->mallocFailed = 1;
- pParse->rc = SQLITE_NOMEM;
+ assert( db->mallocFailed );
+ pParse->rc = SQLITE_NOMEM_BKPT;
pParse->nErr++;
goto begin_table_error;
}
@@ -79970,7 +99211,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
pTable->iPKey = -1;
pTable->pSchema = db->aDb[iDb].pSchema;
pTable->nRef = 1;
- pTable->nRowEst = 1000000;
+ pTable->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
assert( pParse->pNewTable==0 );
pParse->pNewTable = pTable;
@@ -79994,10 +99235,12 @@ SQLITE_PRIVATE void sqlite3StartTable(
** now.
*/
if( !db->init.busy && (v = sqlite3GetVdbe(pParse))!=0 ){
- int j1;
+ int addr1;
int fileFormat;
int reg1, reg2, reg3;
- sqlite3BeginWriteOperation(pParse, 0, iDb);
+ /* nullRow[] is an OP_Record encoding of a row containing 5 NULLs */
+ static const char nullRow[] = { 6, 0, 0, 0, 0, 0 };
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( isVirtual ){
@@ -80013,14 +99256,12 @@ SQLITE_PRIVATE void sqlite3StartTable(
reg3 = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT);
sqlite3VdbeUsesBtree(v, iDb);
- j1 = sqlite3VdbeAddOp1(v, OP_If, reg3);
+ addr1 = sqlite3VdbeAddOp1(v, OP_If, reg3); VdbeCoverage(v);
fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ?
1 : SQLITE_MAX_FILE_FORMAT;
- sqlite3VdbeAddOp2(v, OP_Integer, fileFormat, reg3);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, reg3);
- sqlite3VdbeAddOp2(v, OP_Integer, ENC(db), reg3);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, reg3);
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_FILE_FORMAT, fileFormat);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_TEXT_ENCODING, ENC(db));
+ sqlite3VdbeJumpHere(v, addr1);
/* This just creates a place-holder record in the sqlite_master table.
** The record created does not contain anything yet. It will be replaced
@@ -80037,11 +99278,11 @@ SQLITE_PRIVATE void sqlite3StartTable(
}else
#endif
{
- sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
+ pParse->addrCrTab = sqlite3VdbeAddOp2(v, OP_CreateTable, iDb, reg2);
}
sqlite3OpenMasterTable(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
- sqlite3VdbeAddOp2(v, OP_Null, 0, reg3);
+ sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeAddOp0(v, OP_Close);
@@ -80056,18 +99297,19 @@ begin_table_error:
return;
}
-/*
-** This macro is used to compare two strings in a case-insensitive manner.
-** It is slightly faster than calling sqlite3StrICmp() directly, but
-** produces larger code.
-**
-** WARNING: This macro is not compatible with the strcmp() family. It
-** returns true if the two strings are equal, otherwise false.
+/* Set properties of a table column based on the (magical)
+** name of the column.
*/
-#define STRICMP(x, y) (\
-sqlite3UpperToLower[*(unsigned char *)(x)]== \
-sqlite3UpperToLower[*(unsigned char *)(y)] \
-&& sqlite3StrICmp((x)+1,(y)+1)==0 )
+#if SQLITE_ENABLE_HIDDEN_COLUMNS
+SQLITE_PRIVATE void sqlite3ColumnPropertiesFromName(Table *pTab, Column *pCol){
+ if( sqlite3_strnicmp(pCol->zName, "__hidden__", 10)==0 ){
+ pCol->colFlags |= COLFLAG_HIDDEN;
+ }else if( pTab && pCol!=pTab->aCol && (pCol[-1].colFlags & COLFLAG_HIDDEN) ){
+ pTab->tabFlags |= TF_OOOHidden;
+ }
+}
+#endif
+
/*
** Add a new column to the table currently being constructed.
@@ -80077,10 +99319,11 @@ sqlite3UpperToLower[*(unsigned char *)(y)] \
** first to get things going. Then this routine is called for each
** column.
*/
-SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){
+SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
Table *p;
int i;
char *z;
+ char *zType;
Column *pCol;
sqlite3 *db = pParse->db;
if( (p = pParse->pNewTable)==0 ) return;
@@ -80090,10 +99333,13 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){
return;
}
#endif
- z = sqlite3NameFromToken(db, pName);
+ z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
if( z==0 ) return;
+ memcpy(z, pName->z, pName->n);
+ z[pName->n] = 0;
+ sqlite3Dequote(z);
for(i=0; i<p->nCol; i++){
- if( STRICMP(z, p->aCol[i].zName) ){
+ if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
sqlite3DbFree(db, z);
return;
@@ -80111,13 +99357,23 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName){
pCol = &p->aCol[p->nCol];
memset(pCol, 0, sizeof(p->aCol[0]));
pCol->zName = z;
+ sqlite3ColumnPropertiesFromName(p, pCol);
- /* If there is no type specified, columns have the default affinity
- ** 'NONE'. If there is a type specified, then sqlite3AddColumnType() will
- ** be called next to set pCol->affinity correctly.
- */
- pCol->affinity = SQLITE_AFF_NONE;
+ if( pType->n==0 ){
+ /* If there is no type specified, columns have the default affinity
+ ** 'BLOB'. */
+ pCol->affinity = SQLITE_AFF_BLOB;
+ pCol->szEst = 1;
+ }else{
+ zType = z + sqlite3Strlen30(z) + 1;
+ memcpy(zType, pType->z, pType->n);
+ zType[pType->n] = 0;
+ sqlite3Dequote(zType);
+ pCol->affinity = sqlite3AffinityType(zType, &pCol->szEst);
+ pCol->colFlags |= COLFLAG_HASTYPE;
+ }
p->nCol++;
+ pParse->constraintName.n = 0;
}
/*
@@ -80150,7 +99406,7 @@ SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){
** 'CHAR' | SQLITE_AFF_TEXT
** 'CLOB' | SQLITE_AFF_TEXT
** 'TEXT' | SQLITE_AFF_TEXT
-** 'BLOB' | SQLITE_AFF_NONE
+** 'BLOB' | SQLITE_AFF_BLOB
** 'REAL' | SQLITE_AFF_REAL
** 'FLOA' | SQLITE_AFF_REAL
** 'DOUB' | SQLITE_AFF_REAL
@@ -80158,22 +99414,26 @@ SQLITE_PRIVATE void sqlite3AddNotNull(Parse *pParse, int onError){
** If none of the substrings in the above table are found,
** SQLITE_AFF_NUMERIC is returned.
*/
-SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn){
+SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, u8 *pszEst){
u32 h = 0;
char aff = SQLITE_AFF_NUMERIC;
+ const char *zChar = 0;
- if( zIn ) while( zIn[0] ){
+ assert( zIn!=0 );
+ while( zIn[0] ){
h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
zIn++;
if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){ /* CHAR */
- aff = SQLITE_AFF_TEXT;
+ aff = SQLITE_AFF_TEXT;
+ zChar = zIn;
}else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){ /* CLOB */
aff = SQLITE_AFF_TEXT;
}else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){ /* TEXT */
aff = SQLITE_AFF_TEXT;
}else if( h==(('b'<<24)+('l'<<16)+('o'<<8)+'b') /* BLOB */
&& (aff==SQLITE_AFF_NUMERIC || aff==SQLITE_AFF_REAL) ){
- aff = SQLITE_AFF_NONE;
+ aff = SQLITE_AFF_BLOB;
+ if( zIn[0]=='(' ) zChar = zIn;
#ifndef SQLITE_OMIT_FLOATING_POINT
}else if( h==(('r'<<24)+('e'<<16)+('a'<<8)+'l') /* REAL */
&& aff==SQLITE_AFF_NUMERIC ){
@@ -80191,31 +99451,32 @@ SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn){
}
}
+ /* If pszEst is not NULL, store an estimate of the field size. The
+ ** estimate is scaled so that the size of an integer is 1. */
+ if( pszEst ){
+ *pszEst = 1; /* default size is approx 4 bytes */
+ if( aff<SQLITE_AFF_NUMERIC ){
+ if( zChar ){
+ while( zChar[0] ){
+ if( sqlite3Isdigit(zChar[0]) ){
+ int v = 0;
+ sqlite3GetInt32(zChar, &v);
+ v = v/4 + 1;
+ if( v>255 ) v = 255;
+ *pszEst = v; /* BLOB(k), VARCHAR(k), CHAR(k) -> r=(k/4+1) */
+ break;
+ }
+ zChar++;
+ }
+ }else{
+ *pszEst = 5; /* BLOB, TEXT, CLOB -> r=5 (approx 20 bytes)*/
+ }
+ }
+ }
return aff;
}
/*
-** This routine is called by the parser while in the middle of
-** parsing a CREATE TABLE statement. The pFirst token is the first
-** token in the sequence of tokens that describe the type of the
-** column currently under construction. pLast is the last token
-** in the sequence. Use this information to construct a string
-** that contains the typename of the column and store that string
-** in zType.
-*/
-SQLITE_PRIVATE void sqlite3AddColumnType(Parse *pParse, Token *pType){
- Table *p;
- Column *pCol;
-
- p = pParse->pNewTable;
- if( p==0 || NEVER(p->nCol<1) ) return;
- pCol = &p->aCol[p->nCol-1];
- assert( pCol->zType==0 );
- pCol->zType = sqlite3NameFromToken(pParse->db, pType);
- pCol->affinity = sqlite3AffinityType(pCol->zType);
-}
-
-/*
** The expression is the default value for the most recently added column
** of the table currently under construction.
**
@@ -80232,7 +99493,7 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
p = pParse->pNewTable;
if( p!=0 ){
pCol = &(p->aCol[p->nCol-1]);
- if( !sqlite3ExprIsConstantOrFunction(pSpan->pExpr) ){
+ if( !sqlite3ExprIsConstantOrFunction(pSpan->pExpr, db->init.busy) ){
sqlite3ErrorMsg(pParse, "default value of column [%s] is not constant",
pCol->zName);
}else{
@@ -80240,17 +99501,46 @@ SQLITE_PRIVATE void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
** tokens that point to volatile memory. The 'span' of the expression
** is required by pragma table_info.
*/
+ Expr x;
sqlite3ExprDelete(db, pCol->pDflt);
- pCol->pDflt = sqlite3ExprDup(db, pSpan->pExpr, EXPRDUP_REDUCE);
- sqlite3DbFree(db, pCol->zDflt);
- pCol->zDflt = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
- (int)(pSpan->zEnd - pSpan->zStart));
+ memset(&x, 0, sizeof(x));
+ x.op = TK_SPAN;
+ x.u.zToken = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
+ (int)(pSpan->zEnd - pSpan->zStart));
+ x.pLeft = pSpan->pExpr;
+ x.flags = EP_Skip;
+ pCol->pDflt = sqlite3ExprDup(db, &x, EXPRDUP_REDUCE);
+ sqlite3DbFree(db, x.u.zToken);
}
}
sqlite3ExprDelete(db, pSpan->pExpr);
}
/*
+** Backwards Compatibility Hack:
+**
+** Historical versions of SQLite accepted strings as column names in
+** indexes and PRIMARY KEY constraints and in UNIQUE constraints. Example:
+**
+** CREATE TABLE xyz(a,b,c,d,e,PRIMARY KEY('a'),UNIQUE('b','c' COLLATE trim)
+** CREATE INDEX abc ON xyz('c','d' DESC,'e' COLLATE nocase DESC);
+**
+** This is goofy. But to preserve backwards compatibility we continue to
+** accept it. This routine does the necessary conversion. It converts
+** the expression given in its argument from a TK_STRING into a TK_ID
+** if the expression is just a TK_STRING with an optional COLLATE clause.
+** If the epxression is anything other than TK_STRING, the expression is
+** unchanged.
+*/
+static void sqlite3StringToId(Expr *p){
+ if( p->op==TK_STRING ){
+ p->op = TK_ID;
+ }else if( p->op==TK_COLLATE && p->pLeft->op==TK_STRING ){
+ p->pLeft->op = TK_ID;
+ }
+}
+
+/*
** Designate the PRIMARY KEY for the table. pList is a list of names
** of columns that form the primary key. If pList is NULL, then the
** most recently added column of the table is the primary key.
@@ -80276,9 +99566,10 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
int sortOrder /* SQLITE_SO_ASC or SQLITE_SO_DESC */
){
Table *pTab = pParse->pNewTable;
- char *zType = 0;
+ Column *pCol = 0;
int iCol = -1, i;
- if( pTab==0 || IN_DECLARE_VTAB ) goto primary_key_exit;
+ int nTerm;
+ if( pTab==0 ) goto primary_key_exit;
if( pTab->tabFlags & TF_HasPrimaryKey ){
sqlite3ErrorMsg(pParse,
"table \"%s\" has more than one primary key", pTab->zName);
@@ -80287,40 +99578,45 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
pTab->tabFlags |= TF_HasPrimaryKey;
if( pList==0 ){
iCol = pTab->nCol - 1;
- pTab->aCol[iCol].isPrimKey = 1;
+ pCol = &pTab->aCol[iCol];
+ pCol->colFlags |= COLFLAG_PRIMKEY;
+ nTerm = 1;
}else{
- for(i=0; i<pList->nExpr; i++){
- for(iCol=0; iCol<pTab->nCol; iCol++){
- if( sqlite3StrICmp(pList->a[i].zName, pTab->aCol[iCol].zName)==0 ){
- break;
+ nTerm = pList->nExpr;
+ for(i=0; i<nTerm; i++){
+ Expr *pCExpr = sqlite3ExprSkipCollate(pList->a[i].pExpr);
+ assert( pCExpr!=0 );
+ sqlite3StringToId(pCExpr);
+ if( pCExpr->op==TK_ID ){
+ const char *zCName = pCExpr->u.zToken;
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zName)==0 ){
+ pCol = &pTab->aCol[iCol];
+ pCol->colFlags |= COLFLAG_PRIMKEY;
+ break;
+ }
}
}
- if( iCol<pTab->nCol ){
- pTab->aCol[iCol].isPrimKey = 1;
- }
}
- if( pList->nExpr>1 ) iCol = -1;
- }
- if( iCol>=0 && iCol<pTab->nCol ){
- zType = pTab->aCol[iCol].zType;
}
- if( zType && sqlite3StrICmp(zType, "INTEGER")==0
- && sortOrder==SQLITE_SO_ASC ){
+ if( nTerm==1
+ && pCol
+ && sqlite3StrICmp(sqlite3ColumnType(pCol,""), "INTEGER")==0
+ && sortOrder!=SQLITE_SO_DESC
+ ){
pTab->iPKey = iCol;
pTab->keyConf = (u8)onError;
assert( autoInc==0 || autoInc==1 );
pTab->tabFlags |= autoInc*TF_Autoincrement;
+ if( pList ) pParse->iPkSortOrder = pList->a[0].sortOrder;
}else if( autoInc ){
#ifndef SQLITE_OMIT_AUTOINCREMENT
sqlite3ErrorMsg(pParse, "AUTOINCREMENT is only allowed on an "
"INTEGER PRIMARY KEY");
#endif
}else{
- Index *p;
- p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0);
- if( p ){
- p->autoIndex = 2;
- }
+ sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0,
+ 0, sortOrder, 0, SQLITE_IDXTYPE_PRIMARYKEY);
pList = 0;
}
@@ -80336,15 +99632,20 @@ SQLITE_PRIVATE void sqlite3AddCheckConstraint(
Parse *pParse, /* Parsing context */
Expr *pCheckExpr /* The check expression */
){
- sqlite3 *db = pParse->db;
#ifndef SQLITE_OMIT_CHECK
Table *pTab = pParse->pNewTable;
- if( pTab && !IN_DECLARE_VTAB ){
- pTab->pCheck = sqlite3ExprAnd(db, pTab->pCheck, pCheckExpr);
+ sqlite3 *db = pParse->db;
+ if( pTab && !IN_DECLARE_VTAB
+ && !sqlite3BtreeIsReadonly(db->aDb[db->init.iDb].pBt)
+ ){
+ pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr);
+ if( pParse->constraintName.n ){
+ sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1);
+ }
}else
#endif
{
- sqlite3ExprDelete(db, pCheckExpr);
+ sqlite3ExprDelete(pParse->db, pCheckExpr);
}
}
@@ -80366,6 +99667,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){
if( sqlite3LocateCollSeq(pParse, zColl) ){
Index *pIdx;
+ sqlite3DbFree(db, p->aCol[i].zColl);
p->aCol[i].zColl = zColl;
/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
@@ -80373,7 +99675,7 @@ SQLITE_PRIVATE void sqlite3AddCollateType(Parse *pParse, Token *pToken){
** collation type was added. Correct this if it is the case.
*/
for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
- assert( pIdx->nColumn==1 );
+ assert( pIdx->nKeyCol==1 );
if( pIdx->aiColumn[0]==i ){
pIdx->azColl[0] = p->aCol[i].zColl;
}
@@ -80411,10 +99713,7 @@ SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
pColl = sqlite3FindCollSeq(db, enc, zName, initbusy);
if( !initbusy && (!pColl || !pColl->xCmp) ){
- pColl = sqlite3GetCollSeq(db, enc, pColl, zName);
- if( !pColl ){
- sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
- }
+ pColl = sqlite3GetCollSeq(pParse, enc, pColl, zName);
}
return pColl;
@@ -80436,15 +99735,16 @@ SQLITE_PRIVATE CollSeq *sqlite3LocateCollSeq(Parse *pParse, const char *zName){
** set back to prior value. But schema changes are infrequent
** and the probability of hitting the same cookie value is only
** 1 chance in 2^32. So we're safe enough.
+**
+** IMPLEMENTATION-OF: R-34230-56049 SQLite automatically increments
+** the schema-version whenever the schema changes.
*/
SQLITE_PRIVATE void sqlite3ChangeCookie(Parse *pParse, int iDb){
- int r1 = sqlite3GetTempReg(pParse);
sqlite3 *db = pParse->db;
Vdbe *v = pParse->pVdbe;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1);
- sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION,
+ db->aDb[iDb].pSchema->schema_cookie+1);
}
/*
@@ -80484,10 +99784,10 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
for(j=0; zIdent[j]; j++){
if( !sqlite3Isalnum(zIdent[j]) && zIdent[j]!='_' ) break;
}
- needQuote = sqlite3Isdigit(zIdent[0]) || sqlite3KeywordCode(zIdent, j)!=TK_ID;
- if( !needQuote ){
- needQuote = zIdent[j];
- }
+ needQuote = sqlite3Isdigit(zIdent[0])
+ || sqlite3KeywordCode(zIdent, j)!=TK_ID
+ || zIdent[j]!=0
+ || j==0;
if( needQuote ) z[i++] = '"';
for(j=0; zIdent[j]; j++){
@@ -80526,7 +99826,7 @@ static char *createTableStmt(sqlite3 *db, Table *p){
n += 35 + 6*p->nCol;
zStmt = sqlite3DbMallocRaw(0, n);
if( zStmt==0 ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 0;
}
sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
@@ -80535,8 +99835,8 @@ static char *createTableStmt(sqlite3 *db, Table *p){
zStmt[k++] = '(';
for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
static const char * const azType[] = {
+ /* SQLITE_AFF_BLOB */ "",
/* SQLITE_AFF_TEXT */ " TEXT",
- /* SQLITE_AFF_NONE */ "",
/* SQLITE_AFF_NUMERIC */ " NUM",
/* SQLITE_AFF_INTEGER */ " INT",
/* SQLITE_AFF_REAL */ " REAL"
@@ -80548,18 +99848,18 @@ static char *createTableStmt(sqlite3 *db, Table *p){
k += sqlite3Strlen30(&zStmt[k]);
zSep = zSep2;
identPut(zStmt, &k, pCol->zName);
- assert( pCol->affinity-SQLITE_AFF_TEXT >= 0 );
- assert( pCol->affinity-SQLITE_AFF_TEXT < ArraySize(azType) );
+ assert( pCol->affinity-SQLITE_AFF_BLOB >= 0 );
+ assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) );
+ testcase( pCol->affinity==SQLITE_AFF_BLOB );
testcase( pCol->affinity==SQLITE_AFF_TEXT );
- testcase( pCol->affinity==SQLITE_AFF_NONE );
testcase( pCol->affinity==SQLITE_AFF_NUMERIC );
testcase( pCol->affinity==SQLITE_AFF_INTEGER );
testcase( pCol->affinity==SQLITE_AFF_REAL );
- zType = azType[pCol->affinity - SQLITE_AFF_TEXT];
+ zType = azType[pCol->affinity - SQLITE_AFF_BLOB];
len = sqlite3Strlen30(zType);
- assert( pCol->affinity==SQLITE_AFF_NONE
- || pCol->affinity==sqlite3AffinityType(zType) );
+ assert( pCol->affinity==SQLITE_AFF_BLOB
+ || pCol->affinity==sqlite3AffinityType(zType, 0) );
memcpy(&zStmt[k], zType, len);
k += len;
assert( k<=n );
@@ -80569,6 +99869,219 @@ static char *createTableStmt(sqlite3 *db, Table *p){
}
/*
+** Resize an Index object to hold N columns total. Return SQLITE_OK
+** on success and SQLITE_NOMEM on an OOM error.
+*/
+static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
+ char *zExtra;
+ int nByte;
+ if( pIdx->nColumn>=N ) return SQLITE_OK;
+ assert( pIdx->isResized==0 );
+ nByte = (sizeof(char*) + sizeof(i16) + 1)*N;
+ zExtra = sqlite3DbMallocZero(db, nByte);
+ if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
+ memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
+ pIdx->azColl = (const char**)zExtra;
+ zExtra += sizeof(char*)*N;
+ memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
+ pIdx->aiColumn = (i16*)zExtra;
+ zExtra += sizeof(i16)*N;
+ memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn);
+ pIdx->aSortOrder = (u8*)zExtra;
+ pIdx->nColumn = N;
+ pIdx->isResized = 1;
+ return SQLITE_OK;
+}
+
+/*
+** Estimate the total row width for a table.
+*/
+static void estimateTableWidth(Table *pTab){
+ unsigned wTable = 0;
+ const Column *pTabCol;
+ int i;
+ for(i=pTab->nCol, pTabCol=pTab->aCol; i>0; i--, pTabCol++){
+ wTable += pTabCol->szEst;
+ }
+ if( pTab->iPKey<0 ) wTable++;
+ pTab->szTabRow = sqlite3LogEst(wTable*4);
+}
+
+/*
+** Estimate the average size of a row for an index.
+*/
+static void estimateIndexWidth(Index *pIdx){
+ unsigned wIndex = 0;
+ int i;
+ const Column *aCol = pIdx->pTable->aCol;
+ for(i=0; i<pIdx->nColumn; i++){
+ i16 x = pIdx->aiColumn[i];
+ assert( x<pIdx->pTable->nCol );
+ wIndex += x<0 ? 1 : aCol[pIdx->aiColumn[i]].szEst;
+ }
+ pIdx->szIdxRow = sqlite3LogEst(wIndex*4);
+}
+
+/* Return true if value x is found any of the first nCol entries of aiCol[]
+*/
+static int hasColumn(const i16 *aiCol, int nCol, int x){
+ while( nCol-- > 0 ) if( x==*(aiCol++) ) return 1;
+ return 0;
+}
+
+/*
+** This routine runs at the end of parsing a CREATE TABLE statement that
+** has a WITHOUT ROWID clause. The job of this routine is to convert both
+** internal schema data structures and the generated VDBE code so that they
+** are appropriate for a WITHOUT ROWID table instead of a rowid table.
+** Changes include:
+**
+** (1) Set all columns of the PRIMARY KEY schema object to be NOT NULL.
+** (2) Convert the OP_CreateTable into an OP_CreateIndex. There is
+** no rowid btree for a WITHOUT ROWID. Instead, the canonical
+** data storage is a covering index btree.
+** (3) Bypass the creation of the sqlite_master table entry
+** for the PRIMARY KEY as the primary key index is now
+** identified by the sqlite_master table entry of the table itself.
+** (4) Set the Index.tnum of the PRIMARY KEY Index object in the
+** schema to the rootpage from the main table.
+** (5) Add all table columns to the PRIMARY KEY Index object
+** so that the PRIMARY KEY is a covering index. The surplus
+** columns are part of KeyInfo.nXField and are not used for
+** sorting or lookup or uniqueness checks.
+** (6) Replace the rowid tail on all automatically generated UNIQUE
+** indices with the PRIMARY KEY columns.
+**
+** For virtual tables, only (1) is performed.
+*/
+static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
+ Index *pIdx;
+ Index *pPk;
+ int nPk;
+ int i, j;
+ sqlite3 *db = pParse->db;
+ Vdbe *v = pParse->pVdbe;
+
+ /* Mark every PRIMARY KEY column as NOT NULL (except for imposter tables)
+ */
+ if( !db->init.imposterTable ){
+ for(i=0; i<pTab->nCol; i++){
+ if( (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0 ){
+ pTab->aCol[i].notNull = OE_Abort;
+ }
+ }
+ }
+
+ /* The remaining transformations only apply to b-tree tables, not to
+ ** virtual tables */
+ if( IN_DECLARE_VTAB ) return;
+
+ /* Convert the OP_CreateTable opcode that would normally create the
+ ** root-page for the table into an OP_CreateIndex opcode. The index
+ ** created will become the PRIMARY KEY index.
+ */
+ if( pParse->addrCrTab ){
+ assert( v );
+ sqlite3VdbeChangeOpcode(v, pParse->addrCrTab, OP_CreateIndex);
+ }
+
+ /* Locate the PRIMARY KEY index. Or, if this table was originally
+ ** an INTEGER PRIMARY KEY table, create a new PRIMARY KEY index.
+ */
+ if( pTab->iPKey>=0 ){
+ ExprList *pList;
+ Token ipkToken;
+ sqlite3TokenInit(&ipkToken, pTab->aCol[pTab->iPKey].zName);
+ pList = sqlite3ExprListAppend(pParse, 0,
+ sqlite3ExprAlloc(db, TK_ID, &ipkToken, 0));
+ if( pList==0 ) return;
+ pList->a[0].sortOrder = pParse->iPkSortOrder;
+ assert( pParse->pNewTable==pTab );
+ sqlite3CreateIndex(pParse, 0, 0, 0, pList, pTab->keyConf, 0, 0, 0, 0,
+ SQLITE_IDXTYPE_PRIMARYKEY);
+ if( db->mallocFailed ) return;
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ pTab->iPKey = -1;
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+
+ /* Bypass the creation of the PRIMARY KEY btree and the sqlite_master
+ ** table entry. This is only required if currently generating VDBE
+ ** code for a CREATE TABLE (not when parsing one as part of reading
+ ** a database schema). */
+ if( v ){
+ assert( db->init.busy==0 );
+ sqlite3VdbeChangeOpcode(v, pPk->tnum, OP_Goto);
+ }
+
+ /*
+ ** Remove all redundant columns from the PRIMARY KEY. For example, change
+ ** "PRIMARY KEY(a,b,a,b,c,b,c,d)" into just "PRIMARY KEY(a,b,c,d)". Later
+ ** code assumes the PRIMARY KEY contains no repeated columns.
+ */
+ for(i=j=1; i<pPk->nKeyCol; i++){
+ if( hasColumn(pPk->aiColumn, j, pPk->aiColumn[i]) ){
+ pPk->nColumn--;
+ }else{
+ pPk->aiColumn[j++] = pPk->aiColumn[i];
+ }
+ }
+ pPk->nKeyCol = j;
+ }
+ assert( pPk!=0 );
+ pPk->isCovering = 1;
+ if( !db->init.imposterTable ) pPk->uniqNotNull = 1;
+ nPk = pPk->nKeyCol;
+
+ /* The root page of the PRIMARY KEY is the table root page */
+ pPk->tnum = pTab->tnum;
+
+ /* Update the in-memory representation of all UNIQUE indices by converting
+ ** the final rowid column into one or more columns of the PRIMARY KEY.
+ */
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int n;
+ if( IsPrimaryKeyIndex(pIdx) ) continue;
+ for(i=n=0; i<nPk; i++){
+ if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ) n++;
+ }
+ if( n==0 ){
+ /* This index is a superset of the primary key */
+ pIdx->nColumn = pIdx->nKeyCol;
+ continue;
+ }
+ if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
+ for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
+ if( !hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ){
+ pIdx->aiColumn[j] = pPk->aiColumn[i];
+ pIdx->azColl[j] = pPk->azColl[i];
+ j++;
+ }
+ }
+ assert( pIdx->nColumn>=pIdx->nKeyCol+n );
+ assert( pIdx->nColumn>=j );
+ }
+
+ /* Add all table columns to the PRIMARY KEY index
+ */
+ if( nPk<pTab->nCol ){
+ if( resizeIndexObject(db, pPk, pTab->nCol) ) return;
+ for(i=0, j=nPk; i<pTab->nCol; i++){
+ if( !hasColumn(pPk->aiColumn, j, i) ){
+ assert( j<pPk->nColumn );
+ pPk->aiColumn[j] = i;
+ pPk->azColl[j] = sqlite3StrBINARY;
+ j++;
+ }
+ }
+ assert( pPk->nColumn==j );
+ assert( pTab->nCol==j );
+ }else{
+ pPk->nColumn = pTab->nCol;
+ }
+}
+
+/*
** This routine is called to report the final ")" that terminates
** a CREATE TABLE statement.
**
@@ -80591,53 +100104,67 @@ static char *createTableStmt(sqlite3 *db, Table *p){
SQLITE_PRIVATE void sqlite3EndTable(
Parse *pParse, /* Parse context */
Token *pCons, /* The ',' token after the last column defn. */
- Token *pEnd, /* The final ')' token in the CREATE TABLE */
+ Token *pEnd, /* The ')' before options in the CREATE TABLE */
+ u8 tabOpts, /* Extra table options. Usually 0. */
Select *pSelect /* Select from a "CREATE ... AS SELECT" */
){
- Table *p;
- sqlite3 *db = pParse->db;
- int iDb;
+ Table *p; /* The new table */
+ sqlite3 *db = pParse->db; /* The database connection */
+ int iDb; /* Database in which the table lives */
+ Index *pIdx; /* An implied index of the table */
- if( (pEnd==0 && pSelect==0) || db->mallocFailed ){
+ if( pEnd==0 && pSelect==0 ){
return;
}
+ assert( !db->mallocFailed );
p = pParse->pNewTable;
if( p==0 ) return;
assert( !db->init.busy || !pSelect );
+ /* If the db->init.busy is 1 it means we are reading the SQL off the
+ ** "sqlite_master" or "sqlite_temp_master" table on the disk.
+ ** So do not write to the disk again. Extract the root page number
+ ** for the table from the db->init.newTnum field. (The page number
+ ** should have been put there by the sqliteOpenCb routine.)
+ **
+ ** If the root page number is 1, that means this is the sqlite_master
+ ** table itself. So mark it read-only.
+ */
+ if( db->init.busy ){
+ p->tnum = db->init.newTnum;
+ if( p->tnum==1 ) p->tabFlags |= TF_Readonly;
+ }
+
+ /* Special processing for WITHOUT ROWID Tables */
+ if( tabOpts & TF_WithoutRowid ){
+ if( (p->tabFlags & TF_Autoincrement) ){
+ sqlite3ErrorMsg(pParse,
+ "AUTOINCREMENT not allowed on WITHOUT ROWID tables");
+ return;
+ }
+ if( (p->tabFlags & TF_HasPrimaryKey)==0 ){
+ sqlite3ErrorMsg(pParse, "PRIMARY KEY missing on table %s", p->zName);
+ }else{
+ p->tabFlags |= TF_WithoutRowid | TF_NoVisibleRowid;
+ convertToWithoutRowidTable(pParse, p);
+ }
+ }
+
iDb = sqlite3SchemaToIndex(db, p->pSchema);
#ifndef SQLITE_OMIT_CHECK
/* Resolve names in all CHECK constraint expressions.
*/
if( p->pCheck ){
- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
- NameContext sNC; /* Name context for pParse->pNewTable */
-
- memset(&sNC, 0, sizeof(sNC));
- memset(&sSrc, 0, sizeof(sSrc));
- sSrc.nSrc = 1;
- sSrc.a[0].zName = p->zName;
- sSrc.a[0].pTab = p;
- sSrc.a[0].iCursor = -1;
- sNC.pParse = pParse;
- sNC.pSrcList = &sSrc;
- sNC.isCheck = 1;
- if( sqlite3ResolveExprNames(&sNC, p->pCheck) ){
- return;
- }
+ sqlite3ResolveSelfReference(pParse, p, NC_IsCheck, 0, p->pCheck);
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
- /* If the db->init.busy is 1 it means we are reading the SQL off the
- ** "sqlite_master" or "sqlite_temp_master" table on the disk.
- ** So do not write to the disk again. Extract the root page number
- ** for the table from the db->init.newTnum field. (The page number
- ** should have been put there by the sqliteOpenCb routine.)
- */
- if( db->init.busy ){
- p->tnum = db->init.newTnum;
+ /* Estimate the average row size for the table and for all implied indices */
+ estimateTableWidth(p);
+ for(pIdx=p->pIndex; pIdx; pIdx=pIdx->pNext){
+ estimateIndexWidth(pIdx);
}
/* If not initializing, then create a record for the new table
@@ -80687,33 +100214,55 @@ SQLITE_PRIVATE void sqlite3EndTable(
** be redundant.
*/
if( pSelect ){
- SelectDest dest;
- Table *pSelTab;
-
+ SelectDest dest; /* Where the SELECT should store results */
+ int regYield; /* Register holding co-routine entry-point */
+ int addrTop; /* Top of the co-routine */
+ int regRec; /* A record to be insert into the new table */
+ int regRowid; /* Rowid of the next row to insert */
+ int addrInsLoop; /* Top of the loop for inserting rows */
+ Table *pSelTab; /* A table that describes the SELECT results */
+
+ regYield = ++pParse->nMem;
+ regRec = ++pParse->nMem;
+ regRowid = ++pParse->nMem;
assert(pParse->nTab==1);
+ sqlite3MayAbort(pParse);
sqlite3VdbeAddOp3(v, OP_OpenWrite, 1, pParse->regRoot, iDb);
- sqlite3VdbeChangeP5(v, 1);
+ sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
pParse->nTab = 2;
- sqlite3SelectDestInit(&dest, SRT_Table, 1);
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
sqlite3Select(pParse, pSelect, &dest);
+ sqlite3VdbeEndCoroutine(v, regYield);
+ sqlite3VdbeJumpHere(v, addrTop - 1);
+ if( pParse->nErr ) return;
+ pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
+ if( pSelTab==0 ) return;
+ assert( p->aCol==0 );
+ p->nCol = pSelTab->nCol;
+ p->aCol = pSelTab->aCol;
+ pSelTab->nCol = 0;
+ pSelTab->aCol = 0;
+ sqlite3DeleteTable(db, pSelTab);
+ addrInsLoop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, dest.iSdst, dest.nSdst, regRec);
+ sqlite3TableAffinity(v, p, 0);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, 1, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, 1, regRec, regRowid);
+ sqlite3VdbeGoto(v, addrInsLoop);
+ sqlite3VdbeJumpHere(v, addrInsLoop);
sqlite3VdbeAddOp1(v, OP_Close, 1);
- if( pParse->nErr==0 ){
- pSelTab = sqlite3ResultSetOfSelect(pParse, pSelect);
- if( pSelTab==0 ) return;
- assert( p->aCol==0 );
- p->nCol = pSelTab->nCol;
- p->aCol = pSelTab->aCol;
- pSelTab->nCol = 0;
- pSelTab->aCol = 0;
- sqlite3DeleteTable(db, pSelTab);
- }
}
/* Compute the complete text of the CREATE statement */
if( pSelect ){
zStmt = createTableStmt(db, p);
}else{
- n = (int)(pEnd->z - pParse->sNameToken.z) + 1;
+ Token *pEnd2 = tabOpts ? &pParse->sLastToken : pEnd;
+ n = (int)(pEnd2->z - pParse->sNameToken.z);
+ if( pEnd2->z[0]!=';' ) n += pEnd2->n;
zStmt = sqlite3MPrintf(db,
"CREATE %s %.*s", zType2, n, pParse->sNameToken.z
);
@@ -80727,7 +100276,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
"UPDATE %Q.%s "
"SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q "
"WHERE rowid=#%d",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
zType,
p->zName,
p->zName,
@@ -80742,13 +100291,13 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Check to see if we need to create an sqlite_sequence table for
** keeping track of autoincrement keys.
*/
- if( p->tabFlags & TF_Autoincrement ){
+ if( (p->tabFlags & TF_Autoincrement)!=0 ){
Db *pDb = &db->aDb[iDb];
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( pDb->pSchema->pSeqTab==0 ){
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.sqlite_sequence(name,seq)",
- pDb->zName
+ pDb->zDbSName
);
}
}
@@ -80756,7 +100305,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* Reparse everything to update our internal data structures */
sqlite3VdbeAddParseSchemaOp(v, iDb,
- sqlite3MPrintf(db, "tbl_name='%q'", p->zName));
+ sqlite3MPrintf(db, "tbl_name='%q' AND type!='trigger'", p->zName));
}
@@ -80766,15 +100315,13 @@ SQLITE_PRIVATE void sqlite3EndTable(
Table *pOld;
Schema *pSchema = p->pSchema;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName,
- sqlite3Strlen30(p->zName),p);
+ pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, p);
if( pOld ){
assert( p==pOld ); /* Malloc must have failed inside HashInsert() */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return;
}
pParse->pNewTable = 0;
- db->nTable++;
db->flags |= SQLITE_InternChanges;
#ifndef SQLITE_OMIT_ALTERTABLE
@@ -80801,6 +100348,7 @@ SQLITE_PRIVATE void sqlite3CreateView(
Token *pBegin, /* The CREATE token that begins the statement */
Token *pName1, /* The token that holds the name of the view */
Token *pName2, /* The token that holds the name of the view */
+ ExprList *pCNames, /* Optional list of view column names */
Select *pSelect, /* A SELECT statement that will become the new view */
int isTemp, /* TRUE for a TEMPORARY view */
int noErr /* Suppress error messages if VIEW already exists */
@@ -80816,23 +100364,15 @@ SQLITE_PRIVATE void sqlite3CreateView(
if( pParse->nVar>0 ){
sqlite3ErrorMsg(pParse, "parameters are not allowed in views");
- sqlite3SelectDelete(db, pSelect);
- return;
+ goto create_view_fail;
}
sqlite3StartTable(pParse, pName1, pName2, isTemp, 1, 0, noErr);
p = pParse->pNewTable;
- if( p==0 || pParse->nErr ){
- sqlite3SelectDelete(db, pSelect);
- return;
- }
+ if( p==0 || pParse->nErr ) goto create_view_fail;
sqlite3TwoPartName(pParse, pName1, pName2, &pName);
iDb = sqlite3SchemaToIndex(db, p->pSchema);
- if( sqlite3FixInit(&sFix, pParse, iDb, "view", pName)
- && sqlite3FixSelect(&sFix, pSelect)
- ){
- sqlite3SelectDelete(db, pSelect);
- return;
- }
+ sqlite3FixInit(&sFix, pParse, iDb, "view", pName);
+ if( sqlite3FixSelect(&sFix, pSelect) ) goto create_view_fail;
/* Make a copy of the entire SELECT statement that defines the view.
** This will force all the Expr.token.z values to be dynamically
@@ -80840,30 +100380,31 @@ SQLITE_PRIVATE void sqlite3CreateView(
** they will persist after the current sqlite3_exec() call returns.
*/
p->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
- sqlite3SelectDelete(db, pSelect);
- if( db->mallocFailed ){
- return;
- }
- if( !db->init.busy ){
- sqlite3ViewGetColumnNames(pParse, p);
- }
+ p->pCheck = sqlite3ExprListDup(db, pCNames, EXPRDUP_REDUCE);
+ if( db->mallocFailed ) goto create_view_fail;
/* Locate the end of the CREATE VIEW statement. Make sEnd point to
** the end.
*/
sEnd = pParse->sLastToken;
- if( ALWAYS(sEnd.z[0]!=0) && sEnd.z[0]!=';' ){
+ assert( sEnd.z[0]!=0 );
+ if( sEnd.z[0]!=';' ){
sEnd.z += sEnd.n;
}
sEnd.n = 0;
n = (int)(sEnd.z - pBegin->z);
+ assert( n>0 );
z = pBegin->z;
- while( ALWAYS(n>0) && sqlite3Isspace(z[n-1]) ){ n--; }
+ while( sqlite3Isspace(z[n-1]) ){ n--; }
sEnd.z = &z[n-1];
sEnd.n = 1;
/* Use sqlite3EndTable() to add the view to the SQLITE_MASTER table */
- sqlite3EndTable(pParse, 0, &sEnd, 0);
+ sqlite3EndTable(pParse, 0, &sEnd, 0, 0);
+
+create_view_fail:
+ sqlite3SelectDelete(db, pSelect);
+ sqlite3ExprListDelete(db, pCNames);
return;
}
#endif /* SQLITE_OMIT_VIEW */
@@ -80880,7 +100421,9 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
int nErr = 0; /* Number of errors encountered */
int n; /* Temporarily holds the number of cursors assigned */
sqlite3 *db = pParse->db; /* Database connection for malloc errors */
- int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
+#ifndef SQLITE_OMIT_AUTHORIZATION
+ sqlite3_xauth xAuth; /* Saved xAuth pointer */
+#endif
assert( pTable );
@@ -80928,11 +100471,10 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
assert( pTable->pSelect );
pSel = sqlite3SelectDup(db, pTable->pSelect, 0);
if( pSel ){
- u8 enableLookaside = db->lookaside.bEnabled;
n = pParse->nTab;
sqlite3SrcListAssignCursors(pParse, pSel->pSrc);
pTable->nCol = -1;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
#ifndef SQLITE_OMIT_AUTHORIZATION
xAuth = db->xAuth;
db->xAuth = 0;
@@ -80941,25 +100483,43 @@ SQLITE_PRIVATE int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){
#else
pSelTab = sqlite3ResultSetOfSelect(pParse, pSel);
#endif
- db->lookaside.bEnabled = enableLookaside;
pParse->nTab = n;
- if( pSelTab ){
+ if( pTable->pCheck ){
+ /* CREATE VIEW name(arglist) AS ...
+ ** The names of the columns in the table are taken from
+ ** arglist which is stored in pTable->pCheck. The pCheck field
+ ** normally holds CHECK constraints on an ordinary table, but for
+ ** a VIEW it holds the list of column names.
+ */
+ sqlite3ColumnsFromExprList(pParse, pTable->pCheck,
+ &pTable->nCol, &pTable->aCol);
+ if( db->mallocFailed==0
+ && pParse->nErr==0
+ && pTable->nCol==pSel->pEList->nExpr
+ ){
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTable, pSel);
+ }
+ }else if( pSelTab ){
+ /* CREATE VIEW name AS... without an argument list. Construct
+ ** the column names from the SELECT statement that defines the view.
+ */
assert( pTable->aCol==0 );
pTable->nCol = pSelTab->nCol;
pTable->aCol = pSelTab->aCol;
pSelTab->nCol = 0;
pSelTab->aCol = 0;
- sqlite3DeleteTable(db, pSelTab);
assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) );
- pTable->pSchema->flags |= DB_UnresetViews;
}else{
pTable->nCol = 0;
nErr++;
}
+ sqlite3DeleteTable(db, pSelTab);
sqlite3SelectDelete(db, pSel);
+ db->lookaside.bDisable--;
} else {
nErr++;
}
+ pTable->pSchema->schemaFlags |= DB_UnresetViews;
#endif /* SQLITE_OMIT_VIEW */
return nErr;
}
@@ -80976,7 +100536,7 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){
for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){
Table *pTab = sqliteHashData(i);
if( pTab->pSelect ){
- sqliteDeleteColumnNames(db, pTab);
+ sqlite3DeleteColumnNames(db, pTab);
pTab->aCol = 0;
pTab->nCol = 0;
}
@@ -81038,6 +100598,7 @@ SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iT
static void destroyRootPage(Parse *pParse, int iTable, int iDb){
Vdbe *v = sqlite3GetVdbe(pParse);
int r1 = sqlite3GetTempReg(pParse);
+ assert( iTable>1 );
sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb);
sqlite3MayAbort(pParse);
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -81052,7 +100613,7 @@ static void destroyRootPage(Parse *pParse, int iTable, int iDb){
*/
sqlite3NestedParse(pParse,
"UPDATE %Q.%s SET rootpage=%d WHERE #%d AND rootpage=#%d",
- pParse->db->aDb[iDb].zName, SCHEMA_TABLE(iDb), iTable, r1, r1);
+ pParse->db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), iTable, r1, r1);
#endif
sqlite3ReleaseTempReg(pParse, r1);
}
@@ -81109,6 +100670,7 @@ static void destroyTable(Parse *pParse, Table *pTab){
return;
}else{
int iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
+ assert( iDb>=0 && iDb<pParse->db->nDb );
destroyRootPage(pParse, iLargest, iDb);
iDestroyed = iLargest;
}
@@ -81117,7 +100679,7 @@ static void destroyTable(Parse *pParse, Table *pTab){
}
/*
-** Remove entries from the sqlite_stat1 and sqlite_stat2 tables
+** Remove entries from the sqlite_statN tables (for N in (1,2,3))
** after a DROP INDEX or DROP TABLE command.
*/
static void sqlite3ClearStatTables(
@@ -81126,20 +100688,91 @@ static void sqlite3ClearStatTables(
const char *zType, /* "idx" or "tbl" */
const char *zName /* Name of index or table */
){
- static const char *azStatTab[] = { "sqlite_stat1", "sqlite_stat2" };
int i;
- const char *zDbName = pParse->db->aDb[iDb].zName;
- for(i=0; i<ArraySize(azStatTab); i++){
- if( sqlite3FindTable(pParse->db, azStatTab[i], zDbName) ){
+ const char *zDbName = pParse->db->aDb[iDb].zDbSName;
+ for(i=1; i<=4; i++){
+ char zTab[24];
+ sqlite3_snprintf(sizeof(zTab),zTab,"sqlite_stat%d",i);
+ if( sqlite3FindTable(pParse->db, zTab, zDbName) ){
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE %s=%Q",
- zDbName, azStatTab[i], zType, zName
+ zDbName, zTab, zType, zName
);
}
}
}
/*
+** Generate code to drop a table.
+*/
+SQLITE_PRIVATE void sqlite3CodeDropTable(Parse *pParse, Table *pTab, int iDb, int isView){
+ Vdbe *v;
+ sqlite3 *db = pParse->db;
+ Trigger *pTrigger;
+ Db *pDb = &db->aDb[iDb];
+
+ v = sqlite3GetVdbe(pParse);
+ assert( v!=0 );
+ sqlite3BeginWriteOperation(pParse, 1, iDb);
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( IsVirtual(pTab) ){
+ sqlite3VdbeAddOp0(v, OP_VBegin);
+ }
+#endif
+
+ /* Drop all triggers associated with the table being dropped. Code
+ ** is generated to remove entries from sqlite_master and/or
+ ** sqlite_temp_master if required.
+ */
+ pTrigger = sqlite3TriggerList(pParse, pTab);
+ while( pTrigger ){
+ assert( pTrigger->pSchema==pTab->pSchema ||
+ pTrigger->pSchema==db->aDb[1].pSchema );
+ sqlite3DropTriggerPtr(pParse, pTrigger);
+ pTrigger = pTrigger->pNext;
+ }
+
+#ifndef SQLITE_OMIT_AUTOINCREMENT
+ /* Remove any entries of the sqlite_sequence table associated with
+ ** the table being dropped. This is done before the table is dropped
+ ** at the btree level, in case the sqlite_sequence table needs to
+ ** move as a result of the drop (can happen in auto-vacuum mode).
+ */
+ if( pTab->tabFlags & TF_Autoincrement ){
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.sqlite_sequence WHERE name=%Q",
+ pDb->zDbSName, pTab->zName
+ );
+ }
+#endif
+
+ /* Drop all SQLITE_MASTER table and index entries that refer to the
+ ** table. The program name loops through the master table and deletes
+ ** every row that refers to a table of the same name as the one being
+ ** dropped. Triggers are handled separately because a trigger can be
+ ** created in the temp database that refers to a table in another
+ ** database.
+ */
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
+ pDb->zDbSName, SCHEMA_TABLE(iDb), pTab->zName);
+ if( !isView && !IsVirtual(pTab) ){
+ destroyTable(pParse, pTab);
+ }
+
+ /* Remove the table entry from SQLite's internal schema and modify
+ ** the schema cookie.
+ */
+ if( IsVirtual(pTab) ){
+ sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
+ }
+ sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
+ sqlite3ChangeCookie(pParse, iDb);
+ sqliteViewResetAll(db, iDb);
+}
+
+/*
** This routine is called to do the work of a DROP TABLE statement.
** pName is the name of the table to be dropped.
*/
@@ -81154,9 +100787,10 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
}
assert( pParse->nErr==0 );
assert( pName->nSrc==1 );
+ if( sqlite3ReadSchema(pParse) ) goto exit_drop_table;
if( noErr ) db->suppressErr++;
- pTab = sqlite3LocateTable(pParse, isView,
- pName->a[0].zName, pName->a[0].zDatabase);
+ assert( isView==0 || isView==LOCATE_VIEW );
+ pTab = sqlite3LocateTableItem(pParse, isView, &pName->a[0]);
if( noErr ) db->suppressErr--;
if( pTab==0 ){
@@ -81176,7 +100810,7 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
{
int code;
const char *zTab = SCHEMA_TABLE(iDb);
- const char *zDb = db->aDb[iDb].zName;
+ const char *zDb = db->aDb[iDb].zDbSName;
const char *zArg2 = 0;
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb)){
goto exit_drop_table;
@@ -81207,7 +100841,8 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
}
}
#endif
- if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
+ if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
+ && sqlite3StrNICmp(pTab->zName, "sqlite_stat", 11)!=0 ){
sqlite3ErrorMsg(pParse, "table %s may not be dropped", pTab->zName);
goto exit_drop_table;
}
@@ -81231,68 +100866,11 @@ SQLITE_PRIVATE void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView,
*/
v = sqlite3GetVdbe(pParse);
if( v ){
- Trigger *pTrigger;
- Db *pDb = &db->aDb[iDb];
sqlite3BeginWriteOperation(pParse, 1, iDb);
-
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(pTab) ){
- sqlite3VdbeAddOp0(v, OP_VBegin);
- }
-#endif
- sqlite3FkDropTable(pParse, pName, pTab);
-
- /* Drop all triggers associated with the table being dropped. Code
- ** is generated to remove entries from sqlite_master and/or
- ** sqlite_temp_master if required.
- */
- pTrigger = sqlite3TriggerList(pParse, pTab);
- while( pTrigger ){
- assert( pTrigger->pSchema==pTab->pSchema ||
- pTrigger->pSchema==db->aDb[1].pSchema );
- sqlite3DropTriggerPtr(pParse, pTrigger);
- pTrigger = pTrigger->pNext;
- }
-
-#ifndef SQLITE_OMIT_AUTOINCREMENT
- /* Remove any entries of the sqlite_sequence table associated with
- ** the table being dropped. This is done before the table is dropped
- ** at the btree level, in case the sqlite_sequence table needs to
- ** move as a result of the drop (can happen in auto-vacuum mode).
- */
- if( pTab->tabFlags & TF_Autoincrement ){
- sqlite3NestedParse(pParse,
- "DELETE FROM %s.sqlite_sequence WHERE name=%Q",
- pDb->zName, pTab->zName
- );
- }
-#endif
-
- /* Drop all SQLITE_MASTER table and index entries that refer to the
- ** table. The program name loops through the master table and deletes
- ** every row that refers to a table of the same name as the one being
- ** dropped. Triggers are handled seperately because a trigger can be
- ** created in the temp database that refers to a table in another
- ** database.
- */
- sqlite3NestedParse(pParse,
- "DELETE FROM %Q.%s WHERE tbl_name=%Q and type!='trigger'",
- pDb->zName, SCHEMA_TABLE(iDb), pTab->zName);
sqlite3ClearStatTables(pParse, iDb, "tbl", pTab->zName);
- if( !isView && !IsVirtual(pTab) ){
- destroyTable(pParse, pTab);
- }
-
- /* Remove the table entry from SQLite's internal schema and modify
- ** the schema cookie.
- */
- if( IsVirtual(pTab) ){
- sqlite3VdbeAddOp4(v, OP_VDestroy, iDb, 0, 0, pTab->zName, 0);
- }
- sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0);
- sqlite3ChangeCookie(pParse, iDb);
+ sqlite3FkDropTable(pParse, pName, pTab);
+ sqlite3CodeDropTable(pParse, pTab, iDb, isView);
}
- sqliteViewResetAll(db, iDb);
exit_drop_table:
sqlite3SrcListDelete(db, pName);
@@ -81303,8 +100881,8 @@ exit_drop_table:
** currently under construction. pFromCol determines which columns
** in the current table point to the foreign key. If pFromCol==0 then
** connect the key to the last column inserted. pTo is the name of
-** the table referred to. pToCol is a list of tables in the other
-** pTo table that the foreign key points to. flags contains all
+** the table referred to (a.k.a the "parent" table). pToCol is a list
+** of tables in the parent pTo table. flags contains all
** information about the conflict resolution algorithms specified
** in the ON DELETE, ON UPDATE and ON INSERT clauses.
**
@@ -81404,10 +100982,10 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
assert( sqlite3SchemaMutexHeld(db, 0, p->pSchema) );
pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash,
- pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey
+ pFKey->zTo, (void *)pFKey
);
if( pNextTo==pFKey ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto fk_end;
}
if( pNextTo ){
@@ -81460,20 +101038,20 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
Table *pTab = pIndex->pTable; /* The table that is indexed */
int iTab = pParse->nTab++; /* Btree cursor used for pTab */
int iIdx = pParse->nTab++; /* Btree cursor used for pIndex */
- int iSorter = iTab; /* Cursor opened by OpenSorter (if in use) */
+ int iSorter; /* Cursor opened by OpenSorter (if in use) */
int addr1; /* Address of top of loop */
int addr2; /* Address to jump to for next iteration */
int tnum; /* Root page of index */
+ int iPartIdxLabel; /* Jump to this label to skip a row */
Vdbe *v; /* Generate code into this virtual machine */
KeyInfo *pKey; /* KeyInfo for index */
- int regIdxKey; /* Registers containing the index key */
- int regRecord; /* Register holding assemblied index record */
+ int regRecord; /* Register holding assembled index record */
sqlite3 *db = pParse->db; /* The database connection */
int iDb = sqlite3SchemaToIndex(db, pIndex->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
if( sqlite3AuthCheck(pParse, SQLITE_REINDEX, pIndex->zName, 0,
- db->aDb[iDb].zName ) ){
+ db->aDb[iDb].zDbSName ) ){
return;
}
#endif
@@ -81487,72 +101065,48 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
tnum = memRootPage;
}else{
tnum = pIndex->tnum;
- sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
- }
- pKey = sqlite3IndexKeyinfo(pParse, pIndex);
- sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
- (char *)pKey, P4_KEYINFO_HANDOFF);
- if( memRootPage>=0 ){
- sqlite3VdbeChangeP5(v, 1);
}
+ pKey = sqlite3KeyInfoOfIndex(pParse, pIndex);
+ assert( pKey!=0 || db->mallocFailed || pParse->nErr );
-#ifndef SQLITE_OMIT_MERGE_SORT
/* Open the sorter cursor if we are to use one. */
iSorter = pParse->nTab++;
- sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, 0, (char*)pKey, P4_KEYINFO);
-#endif
+ sqlite3VdbeAddOp4(v, OP_SorterOpen, iSorter, 0, pIndex->nKeyCol, (char*)
+ sqlite3KeyInfoRef(pKey), P4_KEYINFO);
/* Open the table. Loop through all rows of the table, inserting index
** records into the sorter. */
sqlite3OpenTable(pParse, iTab, iDb, pTab, OP_OpenRead);
- addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
- addr2 = addr1 + 1;
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0); VdbeCoverage(v);
regRecord = sqlite3GetTempReg(pParse);
- regIdxKey = sqlite3GenerateIndexKey(pParse, pIndex, iTab, regRecord, 1);
-#ifndef SQLITE_OMIT_MERGE_SORT
+ sqlite3GenerateIndexKey(pParse,pIndex,iTab,regRecord,0,&iPartIdxLabel,0,0);
sqlite3VdbeAddOp2(v, OP_SorterInsert, iSorter, regRecord);
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1);
+ sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
- addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0);
- if( pIndex->onError!=OE_None ){
+ if( memRootPage<0 ) sqlite3VdbeAddOp2(v, OP_Clear, tnum, iDb);
+ sqlite3VdbeAddOp4(v, OP_OpenWrite, iIdx, tnum, iDb,
+ (char *)pKey, P4_KEYINFO);
+ sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR|((memRootPage>=0)?OPFLAG_P2ISREG:0));
+
+ addr1 = sqlite3VdbeAddOp2(v, OP_SorterSort, iSorter, 0); VdbeCoverage(v);
+ if( IsUniqueIndex(pIndex) ){
int j2 = sqlite3VdbeCurrentAddr(v) + 3;
- sqlite3VdbeAddOp2(v, OP_Goto, 0, j2);
+ sqlite3VdbeGoto(v, j2);
addr2 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp3(v, OP_SorterCompare, iSorter, j2, regRecord);
- sqlite3HaltConstraint(
- pParse, OE_Abort, "indexed columns are not unique", P4_STATIC
- );
+ sqlite3VdbeAddOp4Int(v, OP_SorterCompare, iSorter, j2, regRecord,
+ pIndex->nKeyCol); VdbeCoverage(v);
+ sqlite3UniqueConstraint(pParse, OE_Abort, pIndex);
}else{
addr2 = sqlite3VdbeCurrentAddr(v);
}
- sqlite3VdbeAddOp2(v, OP_SorterData, iSorter, regRecord);
- sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 1);
- sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
-#else
- if( pIndex->onError!=OE_None ){
- const int regRowid = regIdxKey + pIndex->nColumn;
- const int j2 = sqlite3VdbeCurrentAddr(v) + 2;
- void * const pRegKey = SQLITE_INT_TO_PTR(regIdxKey);
-
- /* The registers accessed by the OP_IsUnique opcode were allocated
- ** using sqlite3GetTempRange() inside of the sqlite3GenerateIndexKey()
- ** call above. Just before that function was freed they were released
- ** (made available to the compiler for reuse) using
- ** sqlite3ReleaseTempRange(). So in some ways having the OP_IsUnique
- ** opcode use the values stored within seems dangerous. However, since
- ** we can be sure that no other temp registers have been allocated
- ** since sqlite3ReleaseTempRange() was called, it is safe to do so.
- */
- sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32);
- sqlite3HaltConstraint(
- pParse, OE_Abort, "indexed columns are not unique", P4_STATIC);
- }
+ sqlite3VdbeAddOp3(v, OP_SorterData, iSorter, regRecord, iIdx);
+ sqlite3VdbeAddOp3(v, OP_Last, iIdx, 0, -1);
sqlite3VdbeAddOp3(v, OP_IdxInsert, iIdx, regRecord, 0);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
-#endif
sqlite3ReleaseTempReg(pParse, regRecord);
- sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2);
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iSorter, addr2); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
sqlite3VdbeAddOp1(v, OP_Close, iTab);
@@ -81561,6 +101115,41 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
}
/*
+** Allocate heap space to hold an Index object with nCol columns.
+**
+** Increase the allocation size to provide an extra nExtra bytes
+** of 8-byte aligned space after the Index object and return a
+** pointer to this extra space in *ppExtra.
+*/
+SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
+ sqlite3 *db, /* Database connection */
+ i16 nCol, /* Total number of columns in the index */
+ int nExtra, /* Number of bytes of extra space to alloc */
+ char **ppExtra /* Pointer to the "extra" space */
+){
+ Index *p; /* Allocated index object */
+ int nByte; /* Bytes of space for Index object + arrays */
+
+ nByte = ROUND8(sizeof(Index)) + /* Index structure */
+ ROUND8(sizeof(char*)*nCol) + /* Index.azColl */
+ ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */
+ sizeof(i16)*nCol + /* Index.aiColumn */
+ sizeof(u8)*nCol); /* Index.aSortOrder */
+ p = sqlite3DbMallocZero(db, nByte + nExtra);
+ if( p ){
+ char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
+ p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
+ p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
+ p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
+ p->aSortOrder = (u8*)pExtra;
+ p->nColumn = nCol;
+ p->nKeyCol = nCol - 1;
+ *ppExtra = ((char*)p) + nByte;
+ }
+ return p;
+}
+
+/*
** Create a new index for an SQL table. pName1.pName2 is the name of the index
** and pTblList is the name of the table that is to be indexed. Both will
** be NULL for a primary key or an index that is created to satisfy a
@@ -81571,12 +101160,8 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
** pList is a list of columns to be indexed. pList will be NULL if this
** is a primary key or unique-constraint on the most recent column added
** to the table currently under construction.
-**
-** If the index is created successfully, return a pointer to the new Index
-** structure. This is used by sqlite3AddPrimaryKey() to mark the index
-** as the tables primary key (Index.autoIndex==2).
*/
-SQLITE_PRIVATE Index *sqlite3CreateIndex(
+SQLITE_PRIVATE void sqlite3CreateIndex(
Parse *pParse, /* All information about this parse */
Token *pName1, /* First part of index name. May be NULL */
Token *pName2, /* Second part of index name. May be NULL */
@@ -81584,17 +101169,16 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
ExprList *pList, /* A list of columns to be indexed */
int onError, /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
Token *pStart, /* The CREATE token that begins this statement */
- Token *pEnd, /* The ")" that closes the CREATE INDEX statement */
+ Expr *pPIWhere, /* WHERE clause for partial indices */
int sortOrder, /* Sort order of primary key when pList==NULL */
- int ifNotExist /* Omit error if index already exists */
+ int ifNotExist, /* Omit error if index already exists */
+ u8 idxType /* The index type */
){
- Index *pRet = 0; /* Pointer to return */
Table *pTab = 0; /* Table to be indexed */
Index *pIndex = 0; /* The index to be created */
char *zName = 0; /* Name of the index */
int nName; /* Number of characters in zName */
int i, j;
- Token nullId; /* Fake token for an empty ID list */
DbFixer sFix; /* For assigning database names to pTable */
int sortOrderMask; /* 1 to honor DESC in index. 0 to ignore. */
sqlite3 *db = pParse->db;
@@ -81602,13 +101186,15 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
int iDb; /* Index of the database that is being written */
Token *pName = 0; /* Unqualified name of the index to create */
struct ExprList_item *pListItem; /* For looping over pList */
- int nCol;
- int nExtra = 0;
- char *zExtra;
+ int nExtra = 0; /* Space allocated for zExtra[] */
+ int nExtraCol; /* Number of extra columns needed */
+ char *zExtra = 0; /* Extra space after the Index object */
+ Index *pPk = 0; /* PRIMARY KEY index for WITHOUT ROWID tables */
- assert( pStart==0 || pEnd!=0 ); /* pEnd must be non-NULL if pStart is */
- assert( pParse->nErr==0 ); /* Never called with prior errors */
- if( db->mallocFailed || IN_DECLARE_VTAB ){
+ if( db->mallocFailed || pParse->nErr>0 ){
+ goto exit_create_index;
+ }
+ if( IN_DECLARE_VTAB && idxType!=SQLITE_IDXTYPE_PRIMARYKEY ){
goto exit_create_index;
}
if( SQLITE_OK!=sqlite3ReadSchema(pParse) ){
@@ -81627,9 +101213,10 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
assert( pName1 && pName2 );
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
if( iDb<0 ) goto exit_create_index;
+ assert( pName && pName->z );
#ifndef SQLITE_OMIT_TEMPDB
- /* If the index name was unqualified, check if the the table
+ /* If the index name was unqualified, check if the table
** is a temp table. If so, set the database to 1. Do not do this
** if initialising a database schema.
*/
@@ -81641,19 +101228,25 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
}
#endif
- if( sqlite3FixInit(&sFix, pParse, iDb, "index", pName) &&
- sqlite3FixSrcList(&sFix, pTblName)
- ){
+ sqlite3FixInit(&sFix, pParse, iDb, "index", pName);
+ if( sqlite3FixSrcList(&sFix, pTblName) ){
/* Because the parser constructs pTblName from a single identifier,
** sqlite3FixSrcList can never fail. */
assert(0);
}
- pTab = sqlite3LocateTable(pParse, 0, pTblName->a[0].zName,
- pTblName->a[0].zDatabase);
- if( !pTab || db->mallocFailed ) goto exit_create_index;
- assert( db->aDb[iDb].pSchema==pTab->pSchema );
+ pTab = sqlite3LocateTableItem(pParse, 0, &pTblName->a[0]);
+ assert( db->mallocFailed==0 || pTab==0 );
+ if( pTab==0 ) goto exit_create_index;
+ if( iDb==1 && db->aDb[iDb].pSchema!=pTab->pSchema ){
+ sqlite3ErrorMsg(pParse,
+ "cannot create a TEMP index on non-TEMP table \"%s\"",
+ pTab->zName);
+ goto exit_create_index;
+ }
+ if( !HasRowid(pTab) ) pPk = sqlite3PrimaryKeyIndex(pTab);
}else{
assert( pName==0 );
+ assert( pStart==0 );
pTab = pParse->pNewTable;
if( !pTab ) goto exit_create_index;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
@@ -81663,7 +101256,11 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
assert( pTab!=0 );
assert( pParse->nErr==0 );
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
- && memcmp(&pTab->zName[7],"altertab_",9)!=0 ){
+ && db->init.busy==0
+#if SQLITE_USER_AUTHENTICATION
+ && sqlite3UserAuthTable(pTab->zName)==0
+#endif
+ && sqlite3StrNICmp(&pTab->zName[7],"altertab_",9)!=0 ){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
}
@@ -81696,6 +101293,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
if( pName ){
zName = sqlite3NameFromToken(db, pName);
if( zName==0 ) goto exit_create_index;
+ assert( pName->z!=0 );
if( SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){
goto exit_create_index;
}
@@ -81705,7 +101303,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
goto exit_create_index;
}
}
- if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){
+ if( sqlite3FindIndex(db, zName, pDb->zDbSName)!=0 ){
if( !ifNotExist ){
sqlite3ErrorMsg(pParse, "index %s already exists", zName);
}else{
@@ -81722,13 +101320,20 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
if( zName==0 ){
goto exit_create_index;
}
+
+ /* Automatic index names generated from within sqlite3_declare_vtab()
+ ** must have names that are distinct from normal automatic index names.
+ ** The following statement converts "sqlite3_autoindex..." into
+ ** "sqlite3_butoindex..." in order to make the names distinct.
+ ** The "vtab_err.test" test demonstrates the need of this statement. */
+ if( IN_DECLARE_VTAB ) zName[7]++;
}
/* Check for authorization to create an index.
*/
#ifndef SQLITE_OMIT_AUTHORIZATION
{
- const char *zDb = pDb->zName;
+ const char *zDb = pDb->zDbSName;
if( sqlite3AuthCheck(pParse, SQLITE_INSERT, SCHEMA_TABLE(iDb), 0, zDb) ){
goto exit_create_index;
}
@@ -81745,12 +101350,15 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
** So create a fake list to simulate this.
*/
if( pList==0 ){
- nullId.z = pTab->aCol[pTab->nCol-1].zName;
- nullId.n = sqlite3Strlen30((char*)nullId.z);
- pList = sqlite3ExprListAppend(pParse, 0, 0);
+ Token prevCol;
+ sqlite3TokenInit(&prevCol, pTab->aCol[pTab->nCol-1].zName);
+ pList = sqlite3ExprListAppend(pParse, 0,
+ sqlite3ExprAlloc(db, TK_ID, &prevCol, 0));
if( pList==0 ) goto exit_create_index;
- sqlite3ExprListSetName(pParse, pList, &nullId, 0);
- pList->a[0].sortOrder = (u8)sortOrder;
+ assert( pList->nExpr==1 );
+ sqlite3ExprListSetSortOrder(pList, sortOrder);
+ }else{
+ sqlite3ExprListCheckLength(pParse, pList, "index");
}
/* Figure out how many bytes of space are required to store explicitly
@@ -81758,13 +101366,9 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
*/
for(i=0; i<pList->nExpr; i++){
Expr *pExpr = pList->a[i].pExpr;
- if( pExpr ){
- CollSeq *pColl = pExpr->pColl;
- /* Either pColl!=0 or there was an OOM failure. But if an OOM
- ** failure we have quit before reaching this point. */
- if( ALWAYS(pColl) ){
- nExtra += (1 + sqlite3Strlen30(pColl->zName));
- }
+ assert( pExpr!=0 );
+ if( pExpr->op==TK_COLLATE ){
+ nExtra += (1 + sqlite3Strlen30(pExpr->u.zToken));
}
}
@@ -81772,31 +101376,28 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
** Allocate the index structure.
*/
nName = sqlite3Strlen30(zName);
- nCol = pList->nExpr;
- pIndex = sqlite3DbMallocZero(db,
- sizeof(Index) + /* Index structure */
- sizeof(int)*nCol + /* Index.aiColumn */
- sizeof(int)*(nCol+1) + /* Index.aiRowEst */
- sizeof(char *)*nCol + /* Index.azColl */
- sizeof(u8)*nCol + /* Index.aSortOrder */
- nName + 1 + /* Index.zName */
- nExtra /* Collation sequence names */
- );
+ nExtraCol = pPk ? pPk->nKeyCol : 1;
+ pIndex = sqlite3AllocateIndexObject(db, pList->nExpr + nExtraCol,
+ nName + nExtra + 1, &zExtra);
if( db->mallocFailed ){
goto exit_create_index;
}
- pIndex->azColl = (char**)(&pIndex[1]);
- pIndex->aiColumn = (int *)(&pIndex->azColl[nCol]);
- pIndex->aiRowEst = (unsigned *)(&pIndex->aiColumn[nCol]);
- pIndex->aSortOrder = (u8 *)(&pIndex->aiRowEst[nCol+1]);
- pIndex->zName = (char *)(&pIndex->aSortOrder[nCol]);
- zExtra = (char *)(&pIndex->zName[nName+1]);
+ assert( EIGHT_BYTE_ALIGNMENT(pIndex->aiRowLogEst) );
+ assert( EIGHT_BYTE_ALIGNMENT(pIndex->azColl) );
+ pIndex->zName = zExtra;
+ zExtra += nName + 1;
memcpy(pIndex->zName, zName, nName+1);
pIndex->pTable = pTab;
- pIndex->nColumn = pList->nExpr;
pIndex->onError = (u8)onError;
- pIndex->autoIndex = (u8)(pName==0);
+ pIndex->uniqNotNull = onError!=OE_None;
+ pIndex->idxType = idxType;
pIndex->pSchema = db->aDb[iDb].pSchema;
+ pIndex->nKeyCol = pList->nExpr;
+ if( pPIWhere ){
+ sqlite3ResolveSelfReference(pParse, pTab, NC_PartIdx, pPIWhere, 0);
+ pIndex->pPartIdxWhere = pPIWhere;
+ pPIWhere = 0;
+ }
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
/* Check to see if we should honor DESC requests on index columns
@@ -81807,52 +101408,65 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
sortOrderMask = 0; /* Ignore DESC */
}
- /* Scan the names of the columns of the table to be indexed and
- ** load the column indices into the Index structure. Report an error
- ** if any column is not found.
+ /* Analyze the list of expressions that form the terms of the index and
+ ** report any errors. In the common case where the expression is exactly
+ ** a table column, store that column in aiColumn[]. For general expressions,
+ ** populate pIndex->aColExpr and store XN_EXPR (-2) in aiColumn[].
**
- ** TODO: Add a test to make sure that the same column is not named
- ** more than once within the same index. Only the first instance of
- ** the column will ever be used by the optimizer. Note that using the
- ** same column more than once cannot be an error because that would
- ** break backwards compatibility - it needs to be a warning.
+ ** TODO: Issue a warning if two or more columns of the index are identical.
+ ** TODO: Issue a warning if the table primary key is used as part of the
+ ** index key.
*/
for(i=0, pListItem=pList->a; i<pList->nExpr; i++, pListItem++){
- const char *zColName = pListItem->zName;
- Column *pTabCol;
- int requestedSortOrder;
- char *zColl; /* Collation sequence name */
-
- for(j=0, pTabCol=pTab->aCol; j<pTab->nCol; j++, pTabCol++){
- if( sqlite3StrICmp(zColName, pTabCol->zName)==0 ) break;
- }
- if( j>=pTab->nCol ){
- sqlite3ErrorMsg(pParse, "table %s has no column named %s",
- pTab->zName, zColName);
- pParse->checkSchema = 1;
- goto exit_create_index;
+ Expr *pCExpr; /* The i-th index expression */
+ int requestedSortOrder; /* ASC or DESC on the i-th expression */
+ const char *zColl; /* Collation sequence name */
+
+ sqlite3StringToId(pListItem->pExpr);
+ sqlite3ResolveSelfReference(pParse, pTab, NC_IdxExpr, pListItem->pExpr, 0);
+ if( pParse->nErr ) goto exit_create_index;
+ pCExpr = sqlite3ExprSkipCollate(pListItem->pExpr);
+ if( pCExpr->op!=TK_COLUMN ){
+ if( pTab==pParse->pNewTable ){
+ sqlite3ErrorMsg(pParse, "expressions prohibited in PRIMARY KEY and "
+ "UNIQUE constraints");
+ goto exit_create_index;
+ }
+ if( pIndex->aColExpr==0 ){
+ ExprList *pCopy = sqlite3ExprListDup(db, pList, 0);
+ pIndex->aColExpr = pCopy;
+ if( !db->mallocFailed ){
+ assert( pCopy!=0 );
+ pListItem = &pCopy->a[i];
+ }
+ }
+ j = XN_EXPR;
+ pIndex->aiColumn[i] = XN_EXPR;
+ pIndex->uniqNotNull = 0;
+ }else{
+ j = pCExpr->iColumn;
+ assert( j<=0x7fff );
+ if( j<0 ){
+ j = pTab->iPKey;
+ }else if( pTab->aCol[j].notNull==0 ){
+ pIndex->uniqNotNull = 0;
+ }
+ pIndex->aiColumn[i] = (i16)j;
}
- pIndex->aiColumn[i] = j;
- /* Justification of the ALWAYS(pListItem->pExpr->pColl): Because of
- ** the way the "idxlist" non-terminal is constructed by the parser,
- ** if pListItem->pExpr is not null then either pListItem->pExpr->pColl
- ** must exist or else there must have been an OOM error. But if there
- ** was an OOM error, we would never reach this point. */
- if( pListItem->pExpr && ALWAYS(pListItem->pExpr->pColl) ){
+ zColl = 0;
+ if( pListItem->pExpr->op==TK_COLLATE ){
int nColl;
- zColl = pListItem->pExpr->pColl->zName;
+ zColl = pListItem->pExpr->u.zToken;
nColl = sqlite3Strlen30(zColl) + 1;
assert( nExtra>=nColl );
memcpy(zExtra, zColl, nColl);
zColl = zExtra;
zExtra += nColl;
nExtra -= nColl;
- }else{
+ }else if( j>=0 ){
zColl = pTab->aCol[j].zColl;
- if( !zColl ){
- zColl = db->pDfltColl->zName;
- }
}
+ if( !zColl ) zColl = sqlite3StrBINARY;
if( !db->init.busy && !sqlite3LocateCollSeq(pParse, zColl) ){
goto exit_create_index;
}
@@ -81860,7 +101474,45 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
requestedSortOrder = pListItem->sortOrder & sortOrderMask;
pIndex->aSortOrder[i] = (u8)requestedSortOrder;
}
+
+ /* Append the table key to the end of the index. For WITHOUT ROWID
+ ** tables (when pPk!=0) this will be the declared PRIMARY KEY. For
+ ** normal tables (when pPk==0) this will be the rowid.
+ */
+ if( pPk ){
+ for(j=0; j<pPk->nKeyCol; j++){
+ int x = pPk->aiColumn[j];
+ assert( x>=0 );
+ if( hasColumn(pIndex->aiColumn, pIndex->nKeyCol, x) ){
+ pIndex->nColumn--;
+ }else{
+ pIndex->aiColumn[i] = x;
+ pIndex->azColl[i] = pPk->azColl[j];
+ pIndex->aSortOrder[i] = pPk->aSortOrder[j];
+ i++;
+ }
+ }
+ assert( i==pIndex->nColumn );
+ }else{
+ pIndex->aiColumn[i] = XN_ROWID;
+ pIndex->azColl[i] = sqlite3StrBINARY;
+ }
sqlite3DefaultRowEst(pIndex);
+ if( pParse->pNewTable==0 ) estimateIndexWidth(pIndex);
+
+ /* If this index contains every column of its table, then mark
+ ** it as a covering index */
+ assert( HasRowid(pTab)
+ || pTab->iPKey<0 || sqlite3ColumnOfIndex(pIndex, pTab->iPKey)>=0 );
+ if( pTblName!=0 && pIndex->nColumn>=pTab->nCol ){
+ pIndex->isCovering = 1;
+ for(j=0; j<pTab->nCol; j++){
+ if( j==pTab->iPKey ) continue;
+ if( sqlite3ColumnOfIndex(pIndex,j)>=0 ) continue;
+ pIndex->isCovering = 0;
+ break;
+ }
+ }
if( pTab==pParse->pNewTable ){
/* This routine has been called to create an automatic index as a
@@ -81887,27 +101539,28 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
Index *pIdx;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
int k;
- assert( pIdx->onError!=OE_None );
- assert( pIdx->autoIndex );
- assert( pIndex->onError!=OE_None );
+ assert( IsUniqueIndex(pIdx) );
+ assert( pIdx->idxType!=SQLITE_IDXTYPE_APPDEF );
+ assert( IsUniqueIndex(pIndex) );
- if( pIdx->nColumn!=pIndex->nColumn ) continue;
- for(k=0; k<pIdx->nColumn; k++){
+ if( pIdx->nKeyCol!=pIndex->nKeyCol ) continue;
+ for(k=0; k<pIdx->nKeyCol; k++){
const char *z1;
const char *z2;
+ assert( pIdx->aiColumn[k]>=0 );
if( pIdx->aiColumn[k]!=pIndex->aiColumn[k] ) break;
z1 = pIdx->azColl[k];
z2 = pIndex->azColl[k];
- if( z1!=z2 && sqlite3StrICmp(z1, z2) ) break;
+ if( sqlite3StrICmp(z1, z2) ) break;
}
- if( k==pIdx->nColumn ){
+ if( k==pIdx->nKeyCol ){
if( pIdx->onError!=pIndex->onError ){
/* This constraint creates the same index as a previous
** constraint specified somewhere in the CREATE TABLE statement.
** However the ON CONFLICT clauses are different. If both this
** constraint and the previous equivalent constraint have explicit
** ON CONFLICT clauses this is an error. Otherwise, use the
- ** explicitly specified behaviour for the index.
+ ** explicitly specified behavior for the index.
*/
if( !(pIdx->onError==OE_Default || pIndex->onError==OE_Default) ){
sqlite3ErrorMsg(pParse,
@@ -81917,6 +101570,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
pIdx->onError = pIndex->onError;
}
}
+ if( idxType==SQLITE_IDXTYPE_PRIMARYKEY ) pIdx->idxType = idxType;
goto exit_create_index;
}
}
@@ -81925,15 +101579,16 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
/* Link the new Index structure to its table and to the other
** in-memory database structures.
*/
+ assert( pParse->nErr==0 );
if( db->init.busy ){
Index *p;
+ assert( !IN_DECLARE_VTAB );
assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) );
p = sqlite3HashInsert(&pIndex->pSchema->idxHash,
- pIndex->zName, sqlite3Strlen30(pIndex->zName),
- pIndex);
+ pIndex->zName, pIndex);
if( p ){
assert( p==pIndex ); /* Malloc must have failed */
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto exit_create_index;
}
db->flags |= SQLITE_InternChanges;
@@ -81942,22 +101597,20 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
}
}
- /* If the db->init.busy is 0 then create the index on disk. This
- ** involves writing the index into the master table and filling in the
- ** index with the current table contents.
+ /* If this is the initial CREATE INDEX statement (or CREATE TABLE if the
+ ** index is an implied index for a UNIQUE or PRIMARY KEY constraint) then
+ ** emit code to allocate the index rootpage on disk and make an entry for
+ ** the index in the sqlite_master table and populate the index with
+ ** content. But, do not do this if we are simply reading the sqlite_master
+ ** table to parse the schema, or if this index is the PRIMARY KEY index
+ ** of a WITHOUT ROWID table.
**
- ** The db->init.busy is 0 when the user first enters a CREATE INDEX
- ** command. db->init.busy is 1 when a database is opened and
- ** CREATE INDEX statements are read out of the master table. In
- ** the latter case the index already exists on disk, which is why
- ** we don't want to recreate it.
- **
- ** If pTblName==0 it means this index is generated as a primary key
- ** or UNIQUE constraint of a CREATE TABLE statement. Since the table
+ ** If pTblName==0 it means this index is generated as an implied PRIMARY KEY
+ ** or UNIQUE index in a CREATE TABLE statement. Since the table
** has just been created, it contains no data and the index initialization
** step can be skipped.
*/
- else{ /* if( db->init.busy==0 ) */
+ else if( HasRowid(pTab) || pTblName!=0 ){
Vdbe *v;
char *zStmt;
int iMem = ++pParse->nMem;
@@ -81965,22 +101618,26 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto exit_create_index;
-
- /* Create the rootpage for the index
- */
sqlite3BeginWriteOperation(pParse, 1, iDb);
+
+ /* Create the rootpage for the index using CreateIndex. But before
+ ** doing so, code a Noop instruction and store its address in
+ ** Index.tnum. This is required in case this index is actually a
+ ** PRIMARY KEY and the table is actually a WITHOUT ROWID table. In
+ ** that case the convertToWithoutRowidTable() routine will replace
+ ** the Noop with a Goto to jump over the VDBE code generated below. */
+ pIndex->tnum = sqlite3VdbeAddOp0(v, OP_Noop);
sqlite3VdbeAddOp2(v, OP_CreateIndex, iDb, iMem);
/* Gather the complete text of the CREATE INDEX statement into
** the zStmt variable
*/
if( pStart ){
- assert( pEnd!=0 );
+ int n = (int)(pParse->sLastToken.z - pName->z) + pParse->sLastToken.n;
+ if( pName->z[n-1]==';' ) n--;
/* A named index with an explicit CREATE INDEX statement */
zStmt = sqlite3MPrintf(db, "CREATE%s INDEX %.*s",
- onError==OE_None ? "" : " UNIQUE",
- (int)(pEnd->z - pName->z) + 1,
- pName->z);
+ onError==OE_None ? "" : " UNIQUE", n, pName->z);
}else{
/* An automatic index created by a PRIMARY KEY or UNIQUE constraint */
/* zStmt = sqlite3MPrintf(""); */
@@ -81991,7 +101648,7 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
*/
sqlite3NestedParse(pParse,
"INSERT INTO %Q.%s VALUES('index',%Q,%Q,#%d,%Q);",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
pIndex->zName,
pTab->zName,
iMem,
@@ -82007,8 +101664,10 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
sqlite3ChangeCookie(pParse, iDb);
sqlite3VdbeAddParseSchemaOp(v, iDb,
sqlite3MPrintf(db, "name='%q' AND type='index'", pIndex->zName));
- sqlite3VdbeAddOp1(v, OP_Expire, 0);
+ sqlite3VdbeAddOp0(v, OP_Expire);
}
+
+ sqlite3VdbeJumpHere(v, pIndex->tnum);
}
/* When adding an index to the list of indices for a table, make
@@ -82030,31 +101689,27 @@ SQLITE_PRIVATE Index *sqlite3CreateIndex(
pIndex->pNext = pOther->pNext;
pOther->pNext = pIndex;
}
- pRet = pIndex;
pIndex = 0;
}
/* Clean up before exiting */
exit_create_index:
- if( pIndex ){
- sqlite3DbFree(db, pIndex->zColAff);
- sqlite3DbFree(db, pIndex);
- }
+ if( pIndex ) freeIndex(db, pIndex);
+ sqlite3ExprDelete(db, pPIWhere);
sqlite3ExprListDelete(db, pList);
sqlite3SrcListDelete(db, pTblName);
sqlite3DbFree(db, zName);
- return pRet;
}
/*
** Fill the Index.aiRowEst[] array with default information - information
** to be used when we have not run the ANALYZE command.
**
-** aiRowEst[0] is suppose to contain the number of elements in the index.
+** aiRowEst[0] is supposed to contain the number of elements in the index.
** Since we do not know, guess 1 million. aiRowEst[1] is an estimate of the
** number of rows in the table that match any particular value of the
** first column of the index. aiRowEst[2] is an estimate of the number
-** of rows that match any particular combiniation of the first 2 columns
+** of rows that match any particular combination of the first 2 columns
** of the index. And so forth. It must always be the case that
*
** aiRowEst[N]<=aiRowEst[N-1]
@@ -82065,20 +101720,28 @@ exit_create_index:
** are based on typical values found in actual indices.
*/
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index *pIdx){
- unsigned *a = pIdx->aiRowEst;
+ /* 10, 9, 8, 7, 6 */
+ LogEst aVal[] = { 33, 32, 30, 28, 26 };
+ LogEst *a = pIdx->aiRowLogEst;
+ int nCopy = MIN(ArraySize(aVal), pIdx->nKeyCol);
int i;
- unsigned n;
- assert( a!=0 );
- a[0] = pIdx->pTable->nRowEst;
- if( a[0]<10 ) a[0] = 10;
- n = 10;
- for(i=1; i<=pIdx->nColumn; i++){
- a[i] = n;
- if( n>5 ) n--;
- }
- if( pIdx->onError!=OE_None ){
- a[pIdx->nColumn] = 1;
+
+ /* Set the first entry (number of rows in the index) to the estimated
+ ** number of rows in the table, or half the number of rows in the table
+ ** for a partial index. But do not let the estimate drop below 10. */
+ a[0] = pIdx->pTable->nRowLogEst;
+ if( pIdx->pPartIdxWhere!=0 ) a[0] -= 10; assert( 10==sqlite3LogEst(2) );
+ if( a[0]<33 ) a[0] = 33; assert( 33==sqlite3LogEst(10) );
+
+ /* Estimate that a[1] is 10, a[2] is 9, a[3] is 8, a[4] is 7, a[5] is
+ ** 6 and each subsequent value (if any) is 5. */
+ memcpy(&a[1], aVal, nCopy*sizeof(LogEst));
+ for(i=nCopy+1; i<=pIdx->nKeyCol; i++){
+ a[i] = 23; assert( 23==sqlite3LogEst(5) );
}
+
+ assert( 0==sqlite3LogEst(1) );
+ if( IsUniqueIndex(pIdx) ) a[pIdx->nKeyCol] = 0;
}
/*
@@ -82109,7 +101772,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
pParse->checkSchema = 1;
goto exit_drop_index;
}
- if( pIndex->autoIndex ){
+ if( pIndex->idxType!=SQLITE_IDXTYPE_APPDEF ){
sqlite3ErrorMsg(pParse, "index associated with UNIQUE "
"or PRIMARY KEY constraint cannot be dropped", 0);
goto exit_drop_index;
@@ -82119,7 +101782,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
{
int code = SQLITE_DROP_INDEX;
Table *pTab = pIndex->pTable;
- const char *zDb = db->aDb[iDb].zName;
+ const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
if( sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){
goto exit_drop_index;
@@ -82137,7 +101800,7 @@ SQLITE_PRIVATE void sqlite3DropIndex(Parse *pParse, SrcList *pName, int ifExists
sqlite3BeginWriteOperation(pParse, 1, iDb);
sqlite3NestedParse(pParse,
"DELETE FROM %Q.%s WHERE name=%Q AND type='index'",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb), pIndex->zName
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pIndex->zName
);
sqlite3ClearStatTables(pParse, iDb, "idx", pIndex->zName);
sqlite3ChangeCookie(pParse, iDb);
@@ -82150,45 +101813,43 @@ exit_drop_index:
}
/*
-** pArray is a pointer to an array of objects. Each object in the
-** array is szEntry bytes in size. This routine allocates a new
-** object on the end of the array.
+** pArray is a pointer to an array of objects. Each object in the
+** array is szEntry bytes in size. This routine uses sqlite3DbRealloc()
+** to extend the array so that there is space for a new object at the end.
**
-** *pnEntry is the number of entries already in use. *pnAlloc is
-** the previously allocated size of the array. initSize is the
-** suggested initial array size allocation.
+** When this function is called, *pnEntry contains the current size of
+** the array (in entries - so the allocation is ((*pnEntry) * szEntry) bytes
+** in total).
**
-** The index of the new entry is returned in *pIdx.
+** If the realloc() is successful (i.e. if no OOM condition occurs), the
+** space allocated for the new object is zeroed, *pnEntry updated to
+** reflect the new size of the array and a pointer to the new allocation
+** returned. *pIdx is set to the index of the new array entry in this case.
**
-** This routine returns a pointer to the array of objects. This
-** might be the same as the pArray parameter or it might be a different
-** pointer if the array was resized.
+** Otherwise, if the realloc() fails, *pIdx is set to -1, *pnEntry remains
+** unchanged and a copy of pArray returned.
*/
SQLITE_PRIVATE void *sqlite3ArrayAllocate(
sqlite3 *db, /* Connection to notify of malloc failures */
void *pArray, /* Array of objects. Might be reallocated */
int szEntry, /* Size of each object in the array */
- int initSize, /* Suggested initial allocation, in elements */
int *pnEntry, /* Number of objects currently in use */
- int *pnAlloc, /* Current size of the allocation, in elements */
int *pIdx /* Write the index of a new slot here */
){
char *z;
- if( *pnEntry >= *pnAlloc ){
- void *pNew;
- int newSize;
- newSize = (*pnAlloc)*2 + initSize;
- pNew = sqlite3DbRealloc(db, pArray, newSize*szEntry);
+ int n = *pnEntry;
+ if( (n & (n-1))==0 ){
+ int sz = (n==0) ? 1 : 2*n;
+ void *pNew = sqlite3DbRealloc(db, pArray, sz*szEntry);
if( pNew==0 ){
*pIdx = -1;
return pArray;
}
- *pnAlloc = sqlite3DbMallocSize(db, pNew)/szEntry;
pArray = pNew;
}
z = (char*)pArray;
- memset(&z[*pnEntry * szEntry], 0, szEntry);
- *pIdx = *pnEntry;
+ memset(&z[n * szEntry], 0, szEntry);
+ *pIdx = n;
++*pnEntry;
return pArray;
}
@@ -82204,15 +101865,12 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(sqlite3 *db, IdList *pList, Token *pT
if( pList==0 ){
pList = sqlite3DbMallocZero(db, sizeof(IdList) );
if( pList==0 ) return 0;
- pList->nAlloc = 0;
}
pList->a = sqlite3ArrayAllocate(
db,
pList->a,
sizeof(pList->a[0]),
- 5,
&pList->nId,
- &pList->nAlloc,
&i
);
if( i<0 ){
@@ -82283,7 +101941,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
assert( iStart<=pSrc->nSrc );
/* Allocate additional space if needed */
- if( pSrc->nSrc+nExtra>pSrc->nAlloc ){
+ if( (u32)pSrc->nSrc+nExtra>pSrc->nAlloc ){
SrcList *pNew;
int nAlloc = pSrc->nSrc+nExtra;
int nGot;
@@ -82295,7 +101953,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
}
pSrc = pNew;
nGot = (sqlite3DbMallocSize(db, pNew) - sizeof(*pSrc))/sizeof(pSrc->a[0])+1;
- pSrc->nAlloc = (u16)nGot;
+ pSrc->nAlloc = nGot;
}
/* Move existing slots that come after the newly inserted slots
@@ -82303,7 +101961,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
for(i=pSrc->nSrc-1; i>=iStart; i--){
pSrc->a[i+nExtra] = pSrc->a[i];
}
- pSrc->nSrc += (i16)nExtra;
+ pSrc->nSrc += nExtra;
/* Zero the newly allocated slots */
memset(&pSrc->a[iStart], 0, sizeof(pSrc->a[0])*nExtra);
@@ -82358,10 +102016,12 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(
){
struct SrcList_item *pItem;
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
+ assert( db!=0 );
if( pList==0 ){
- pList = sqlite3DbMallocZero(db, sizeof(SrcList) );
+ pList = sqlite3DbMallocRawNN(db, sizeof(SrcList) );
if( pList==0 ) return 0;
pList->nAlloc = 1;
+ pList->nSrc = 0;
}
pList = sqlite3SrcListEnlarge(db, pList, 1, pList->nSrc);
if( db->mallocFailed ){
@@ -82411,7 +102071,8 @@ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
sqlite3DbFree(db, pItem->zDatabase);
sqlite3DbFree(db, pItem->zName);
sqlite3DbFree(db, pItem->zAlias);
- sqlite3DbFree(db, pItem->zIndex);
+ if( pItem->fg.isIndexedBy ) sqlite3DbFree(db, pItem->u1.zIndexedBy);
+ if( pItem->fg.isTabFunc ) sqlite3ExprListDelete(db, pItem->u1.pFuncArg);
sqlite3DeleteTable(db, pItem->pTab);
sqlite3SelectDelete(db, pItem->pSelect);
sqlite3ExprDelete(db, pItem->pOn);
@@ -82427,7 +102088,7 @@ SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3 *db, SrcList *pList){
** if this is the first term of the FROM clause. pTable and pDatabase
** are the name of the table and database named in the FROM clause term.
** pDatabase is NULL if the database name qualifier is missing - the
-** usual case. If the term has a alias, then pAlias points to the
+** usual case. If the term has an alias, then pAlias points to the
** alias token. If the term is a subquery, then pSubquery is the
** SELECT statement that the subquery encodes. The pTable and
** pDatabase parameters are NULL for subqueries. The pOn and pUsing
@@ -82484,18 +102145,38 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI
assert( pIndexedBy!=0 );
if( p && ALWAYS(p->nSrc>0) ){
struct SrcList_item *pItem = &p->a[p->nSrc-1];
- assert( pItem->notIndexed==0 && pItem->zIndex==0 );
+ assert( pItem->fg.notIndexed==0 );
+ assert( pItem->fg.isIndexedBy==0 );
+ assert( pItem->fg.isTabFunc==0 );
if( pIndexedBy->n==1 && !pIndexedBy->z ){
/* A "NOT INDEXED" clause was supplied. See parse.y
** construct "indexed_opt" for details. */
- pItem->notIndexed = 1;
+ pItem->fg.notIndexed = 1;
}else{
- pItem->zIndex = sqlite3NameFromToken(pParse->db, pIndexedBy);
+ pItem->u1.zIndexedBy = sqlite3NameFromToken(pParse->db, pIndexedBy);
+ pItem->fg.isIndexedBy = (pItem->u1.zIndexedBy!=0);
}
}
}
/*
+** Add the list of function arguments to the SrcList entry for a
+** table-valued-function.
+*/
+SQLITE_PRIVATE void sqlite3SrcListFuncArgs(Parse *pParse, SrcList *p, ExprList *pList){
+ if( p ){
+ struct SrcList_item *pItem = &p->a[p->nSrc-1];
+ assert( pItem->fg.notIndexed==0 );
+ assert( pItem->fg.isIndexedBy==0 );
+ assert( pItem->fg.isTabFunc==0 );
+ pItem->u1.pFuncArg = pList;
+ pItem->fg.isTabFunc = 1;
+ }else{
+ sqlite3ExprListDelete(pParse->db, pList);
+ }
+}
+
+/*
** When building up a FROM clause in the parser, the join operator
** is initially attached to the left operand. But the code generator
** expects the join operator to be on the right operand. This routine
@@ -82513,16 +102194,15 @@ SQLITE_PRIVATE void sqlite3SrcListIndexedBy(Parse *pParse, SrcList *p, Token *pI
SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(SrcList *p){
if( p ){
int i;
- assert( p->a || p->nSrc==0 );
for(i=p->nSrc-1; i>0; i--){
- p->a[i].jointype = p->a[i-1].jointype;
+ p->a[i].fg.jointype = p->a[i-1].fg.jointype;
}
- p->a[0].jointype = 0;
+ p->a[0].fg.jointype = 0;
}
}
/*
-** Begin a transaction
+** Generate VDBE code for a BEGIN statement.
*/
SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3 *db;
@@ -82532,7 +102212,6 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
assert( pParse!=0 );
db = pParse->db;
assert( db!=0 );
-/* if( db->aDb[0].pBt==0 ) return; */
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "BEGIN", 0, 0) ){
return;
}
@@ -82544,40 +102223,34 @@ SQLITE_PRIVATE void sqlite3BeginTransaction(Parse *pParse, int type){
sqlite3VdbeUsesBtree(v, i);
}
}
- sqlite3VdbeAddOp2(v, OP_AutoCommit, 0, 0);
+ sqlite3VdbeAddOp0(v, OP_AutoCommit);
}
/*
-** Commit a transaction
+** Generate VDBE code for a COMMIT statement.
*/
SQLITE_PRIVATE void sqlite3CommitTransaction(Parse *pParse){
- sqlite3 *db;
Vdbe *v;
assert( pParse!=0 );
- db = pParse->db;
- assert( db!=0 );
-/* if( db->aDb[0].pBt==0 ) return; */
+ assert( pParse->db!=0 );
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "COMMIT", 0, 0) ){
return;
}
v = sqlite3GetVdbe(pParse);
if( v ){
- sqlite3VdbeAddOp2(v, OP_AutoCommit, 1, 0);
+ sqlite3VdbeAddOp1(v, OP_AutoCommit, 1);
}
}
/*
-** Rollback a transaction
+** Generate VDBE code for a ROLLBACK statement.
*/
SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse *pParse){
- sqlite3 *db;
Vdbe *v;
assert( pParse!=0 );
- db = pParse->db;
- assert( db!=0 );
-/* if( db->aDb[0].pBt==0 ) return; */
+ assert( pParse->db!=0 );
if( sqlite3AuthCheck(pParse, SQLITE_TRANSACTION, "ROLLBACK", 0, 0) ){
return;
}
@@ -82633,7 +102306,7 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){
db->aDb[1].pBt = pBt;
assert( db->aDb[1].pSchema );
if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 1;
}
}
@@ -82641,50 +102314,22 @@ SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *pParse){
}
/*
-** Generate VDBE code that will verify the schema cookie and start
-** a read-transaction for all named database files.
-**
-** It is important that all schema cookies be verified and all
-** read transactions be started before anything else happens in
-** the VDBE program. But this routine can be called after much other
-** code has been generated. So here is what we do:
-**
-** The first time this routine is called, we code an OP_Goto that
-** will jump to a subroutine at the end of the program. Then we
-** record every database that needs its schema verified in the
-** pParse->cookieMask field. Later, after all other code has been
-** generated, the subroutine that does the cookie verifications and
-** starts the transactions will be coded and the OP_Goto P2 value
-** will be made to point to that subroutine. The generation of the
-** cookie verification subroutine code happens in sqlite3FinishCoding().
-**
-** If iDb<0 then code the OP_Goto only - don't set flag to verify the
-** schema on any databases. This can be used to position the OP_Goto
-** early in the code, before we know if any database tables will be used.
+** Record the fact that the schema cookie will need to be verified
+** for database iDb. The code to actually verify the schema cookie
+** will occur at the end of the top-level VDBE and will be generated
+** later, by sqlite3FinishCoding().
*/
SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
- if( pToplevel->cookieGoto==0 ){
- Vdbe *v = sqlite3GetVdbe(pToplevel);
- if( v==0 ) return; /* This only happens if there was a prior error */
- pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1;
- }
- if( iDb>=0 ){
- sqlite3 *db = pToplevel->db;
- yDbMask mask;
-
- assert( iDb<db->nDb );
- assert( db->aDb[iDb].pBt!=0 || iDb==1 );
- assert( iDb<SQLITE_MAX_ATTACHED+2 );
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- mask = ((yDbMask)1)<<iDb;
- if( (pToplevel->cookieMask & mask)==0 ){
- pToplevel->cookieMask |= mask;
- pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie;
- if( !OMIT_TEMPDB && iDb==1 ){
- sqlite3OpenTempDatabase(pToplevel);
- }
+ assert( iDb>=0 && iDb<pParse->db->nDb );
+ assert( pParse->db->aDb[iDb].pBt!=0 || iDb==1 );
+ assert( iDb<SQLITE_MAX_ATTACHED+2 );
+ assert( sqlite3SchemaMutexHeld(pParse->db, iDb, 0) );
+ if( DbMaskTest(pToplevel->cookieMask, iDb)==0 ){
+ DbMaskSet(pToplevel->cookieMask, iDb);
+ if( !OMIT_TEMPDB && iDb==1 ){
+ sqlite3OpenTempDatabase(pToplevel);
}
}
}
@@ -82698,7 +102343,7 @@ SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb)
int i;
for(i=0; i<db->nDb; i++){
Db *pDb = &db->aDb[i];
- if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){
+ if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zDbSName)) ){
sqlite3CodeVerifySchema(pParse, i);
}
}
@@ -82720,7 +102365,7 @@ SQLITE_PRIVATE void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb)
SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
sqlite3CodeVerifySchema(pParse, iDb);
- pToplevel->writeMask |= ((yDbMask)1)<<iDb;
+ DbMaskSet(pToplevel->writeMask, iDb);
pToplevel->isMultiWrite |= setStatement;
}
@@ -82762,12 +102407,76 @@ SQLITE_PRIVATE void sqlite3MayAbort(Parse *pParse){
** error. The onError parameter determines which (if any) of the statement
** and/or current transaction is rolled back.
*/
-SQLITE_PRIVATE void sqlite3HaltConstraint(Parse *pParse, int onError, char *p4, int p4type){
+SQLITE_PRIVATE void sqlite3HaltConstraint(
+ Parse *pParse, /* Parsing context */
+ int errCode, /* extended error code */
+ int onError, /* Constraint type */
+ char *p4, /* Error message */
+ i8 p4type, /* P4_STATIC or P4_TRANSIENT */
+ u8 p5Errmsg /* P5_ErrMsg type */
+){
Vdbe *v = sqlite3GetVdbe(pParse);
+ assert( (errCode&0xff)==SQLITE_CONSTRAINT );
if( onError==OE_Abort ){
sqlite3MayAbort(pParse);
}
- sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type);
+ sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
+ sqlite3VdbeChangeP5(v, p5Errmsg);
+}
+
+/*
+** Code an OP_Halt due to UNIQUE or PRIMARY KEY constraint violation.
+*/
+SQLITE_PRIVATE void sqlite3UniqueConstraint(
+ Parse *pParse, /* Parsing context */
+ int onError, /* Constraint type */
+ Index *pIdx /* The index that triggers the constraint */
+){
+ char *zErr;
+ int j;
+ StrAccum errMsg;
+ Table *pTab = pIdx->pTable;
+
+ sqlite3StrAccumInit(&errMsg, pParse->db, 0, 0, 200);
+ if( pIdx->aColExpr ){
+ sqlite3XPrintf(&errMsg, "index '%q'", pIdx->zName);
+ }else{
+ for(j=0; j<pIdx->nKeyCol; j++){
+ char *zCol;
+ assert( pIdx->aiColumn[j]>=0 );
+ zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
+ if( j ) sqlite3StrAccumAppend(&errMsg, ", ", 2);
+ sqlite3XPrintf(&errMsg, "%s.%s", pTab->zName, zCol);
+ }
+ }
+ zErr = sqlite3StrAccumFinish(&errMsg);
+ sqlite3HaltConstraint(pParse,
+ IsPrimaryKeyIndex(pIdx) ? SQLITE_CONSTRAINT_PRIMARYKEY
+ : SQLITE_CONSTRAINT_UNIQUE,
+ onError, zErr, P4_DYNAMIC, P5_ConstraintUnique);
+}
+
+
+/*
+** Code an OP_Halt due to non-unique rowid.
+*/
+SQLITE_PRIVATE void sqlite3RowidConstraint(
+ Parse *pParse, /* Parsing context */
+ int onError, /* Conflict resolution algorithm */
+ Table *pTab /* The table with the non-unique rowid */
+){
+ char *zMsg;
+ int rc;
+ if( pTab->iPKey>=0 ){
+ zMsg = sqlite3MPrintf(pParse->db, "%s.%s", pTab->zName,
+ pTab->aCol[pTab->iPKey].zName);
+ rc = SQLITE_CONSTRAINT_PRIMARYKEY;
+ }else{
+ zMsg = sqlite3MPrintf(pParse->db, "%s.rowid", pTab->zName);
+ rc = SQLITE_CONSTRAINT_ROWID;
+ }
+ sqlite3HaltConstraint(pParse, rc, onError, zMsg, P4_DYNAMIC,
+ P5_ConstraintUnique);
}
/*
@@ -82780,8 +102489,8 @@ static int collationMatch(const char *zColl, Index *pIndex){
assert( zColl!=0 );
for(i=0; i<pIndex->nColumn; i++){
const char *z = pIndex->azColl[i];
- assert( z!=0 );
- if( 0==sqlite3StrICmp(z, zColl) ){
+ assert( z!=0 || pIndex->aiColumn[i]<0 );
+ if( pIndex->aiColumn[i]>=0 && 0==sqlite3StrICmp(z, zColl) ){
return 1;
}
}
@@ -82881,7 +102590,7 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
if( iDb<0 ) return;
z = sqlite3NameFromToken(db, pObjName);
if( z==0 ) return;
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
pTab = sqlite3FindTable(db, z, zDb);
if( pTab ){
reindexTable(pParse, pTab, 0);
@@ -82900,42 +102609,107 @@ SQLITE_PRIVATE void sqlite3Reindex(Parse *pParse, Token *pName1, Token *pName2){
#endif
/*
-** Return a dynamicly allocated KeyInfo structure that can be used
-** with OP_OpenRead or OP_OpenWrite to access database index pIdx.
+** Return a KeyInfo structure that is appropriate for the given Index.
**
-** If successful, a pointer to the new structure is returned. In this case
-** the caller is responsible for calling sqlite3DbFree(db, ) on the returned
-** pointer. If an error occurs (out of memory or missing collation
-** sequence), NULL is returned and the state of pParse updated to reflect
-** the error.
+** The caller should invoke sqlite3KeyInfoUnref() on the returned object
+** when it has finished using it.
*/
-SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoOfIndex(Parse *pParse, Index *pIdx){
int i;
int nCol = pIdx->nColumn;
- int nBytes = sizeof(KeyInfo) + (nCol-1)*sizeof(CollSeq*) + nCol;
- sqlite3 *db = pParse->db;
- KeyInfo *pKey = (KeyInfo *)sqlite3DbMallocZero(db, nBytes);
-
+ int nKey = pIdx->nKeyCol;
+ KeyInfo *pKey;
+ if( pParse->nErr ) return 0;
+ if( pIdx->uniqNotNull ){
+ pKey = sqlite3KeyInfoAlloc(pParse->db, nKey, nCol-nKey);
+ }else{
+ pKey = sqlite3KeyInfoAlloc(pParse->db, nCol, 0);
+ }
if( pKey ){
- pKey->db = pParse->db;
- pKey->aSortOrder = (u8 *)&(pKey->aColl[nCol]);
- assert( &pKey->aSortOrder[nCol]==&(((u8 *)pKey)[nBytes]) );
+ assert( sqlite3KeyInfoIsWriteable(pKey) );
for(i=0; i<nCol; i++){
- char *zColl = pIdx->azColl[i];
- assert( zColl );
- pKey->aColl[i] = sqlite3LocateCollSeq(pParse, zColl);
+ const char *zColl = pIdx->azColl[i];
+ pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
+ sqlite3LocateCollSeq(pParse, zColl);
pKey->aSortOrder[i] = pIdx->aSortOrder[i];
}
- pKey->nField = (u16)nCol;
+ if( pParse->nErr ){
+ sqlite3KeyInfoUnref(pKey);
+ pKey = 0;
+ }
}
+ return pKey;
+}
- if( pParse->nErr ){
- sqlite3DbFree(db, pKey);
- pKey = 0;
+#ifndef SQLITE_OMIT_CTE
+/*
+** This routine is invoked once per CTE by the parser while parsing a
+** WITH clause.
+*/
+SQLITE_PRIVATE With *sqlite3WithAdd(
+ Parse *pParse, /* Parsing context */
+ With *pWith, /* Existing WITH clause, or NULL */
+ Token *pName, /* Name of the common-table */
+ ExprList *pArglist, /* Optional column name list for the table */
+ Select *pQuery /* Query used to initialize the table */
+){
+ sqlite3 *db = pParse->db;
+ With *pNew;
+ char *zName;
+
+ /* Check that the CTE name is unique within this WITH clause. If
+ ** not, store an error in the Parse structure. */
+ zName = sqlite3NameFromToken(pParse->db, pName);
+ if( zName && pWith ){
+ int i;
+ for(i=0; i<pWith->nCte; i++){
+ if( sqlite3StrICmp(zName, pWith->a[i].zName)==0 ){
+ sqlite3ErrorMsg(pParse, "duplicate WITH table name: %s", zName);
+ }
+ }
}
- return pKey;
+
+ if( pWith ){
+ int nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
+ pNew = sqlite3DbRealloc(db, pWith, nByte);
+ }else{
+ pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
+ }
+ assert( (pNew!=0 && zName!=0) || db->mallocFailed );
+
+ if( db->mallocFailed ){
+ sqlite3ExprListDelete(db, pArglist);
+ sqlite3SelectDelete(db, pQuery);
+ sqlite3DbFree(db, zName);
+ pNew = pWith;
+ }else{
+ pNew->a[pNew->nCte].pSelect = pQuery;
+ pNew->a[pNew->nCte].pCols = pArglist;
+ pNew->a[pNew->nCte].zName = zName;
+ pNew->a[pNew->nCte].zCteErr = 0;
+ pNew->nCte++;
+ }
+
+ return pNew;
}
+/*
+** Free the contents of the With object passed as the second argument.
+*/
+SQLITE_PRIVATE void sqlite3WithDelete(sqlite3 *db, With *pWith){
+ if( pWith ){
+ int i;
+ for(i=0; i<pWith->nCte; i++){
+ struct Cte *pCte = &pWith->a[i];
+ sqlite3ExprListDelete(db, pCte->pCols);
+ sqlite3SelectDelete(db, pCte->pSelect);
+ sqlite3DbFree(db, pCte->zName);
+ }
+ sqlite3DbFree(db, pWith);
+ }
+}
+#endif /* !defined(SQLITE_OMIT_CTE) */
+
/************** End of build.c ***********************************************/
/************** Begin file callback.c ****************************************/
/*
@@ -82954,6 +102728,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3IndexKeyinfo(Parse *pParse, Index *pIdx){
** of user defined functions and collation sequences.
*/
+/* #include "sqliteInt.h" */
/*
** Invoke the 'collation needed' callback to request a collation sequence
@@ -83014,17 +102789,18 @@ static int synthCollSeq(sqlite3 *db, CollSeq *pColl){
**
** The return value is either the collation sequence to be used in database
** db for collation type name zName, length nName, or NULL, if no collation
-** sequence can be found.
+** sequence can be found. If no collation is found, leave an error message.
**
** See also: sqlite3LocateCollSeq(), sqlite3FindCollSeq()
*/
SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(
- sqlite3* db, /* The database connection */
+ Parse *pParse, /* Parsing context */
u8 enc, /* The desired encoding for the collating sequence */
CollSeq *pColl, /* Collating sequence with native encoding, or NULL */
const char *zName /* Collating sequence name */
){
CollSeq *p;
+ sqlite3 *db = pParse->db;
p = pColl;
if( !p ){
@@ -83041,6 +102817,9 @@ SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(
p = 0;
}
assert( !p || p->xCmp );
+ if( p==0 ){
+ sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
+ }
return p;
}
@@ -83059,10 +102838,8 @@ SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){
if( pColl ){
const char *zName = pColl->zName;
sqlite3 *db = pParse->db;
- CollSeq *p = sqlite3GetCollSeq(db, ENC(db), pColl, zName);
+ CollSeq *p = sqlite3GetCollSeq(pParse, ENC(db), pColl, zName);
if( !p ){
- sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName);
- pParse->nErr++;
return SQLITE_ERROR;
}
assert( p==pColl );
@@ -83079,7 +102856,7 @@ SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){
**
** Each pointer stored in the sqlite3.aCollSeq hash table contains an
** array of three CollSeq structures. The first is the collation sequence
-** prefferred for UTF-8, the second UTF-16le, and the third UTF-16be.
+** preferred for UTF-8, the second UTF-16le, and the third UTF-16be.
**
** Stored immediately after the three collation sequences is a copy of
** the collation sequence name. A pointer to this string is stored in
@@ -83091,11 +102868,11 @@ static CollSeq *findCollSeqEntry(
int create /* Create a new entry if true */
){
CollSeq *pColl;
- int nName = sqlite3Strlen30(zName);
- pColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
+ pColl = sqlite3HashFind(&db->aCollSeq, zName);
if( 0==pColl && create ){
- pColl = sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName + 1 );
+ int nName = sqlite3Strlen30(zName);
+ pColl = sqlite3DbMallocZero(db, 3*sizeof(*pColl) + nName + 1);
if( pColl ){
CollSeq *pDel = 0;
pColl[0].zName = (char*)&pColl[3];
@@ -83106,7 +102883,7 @@ static CollSeq *findCollSeqEntry(
pColl[2].enc = SQLITE_UTF16BE;
memcpy(pColl[0].zName, zName, nName);
pColl[0].zName[nName] = 0;
- pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, nName, pColl);
+ pDel = sqlite3HashInsert(&db->aCollSeq, pColl[0].zName, pColl);
/* If a malloc() failure occurred in sqlite3HashInsert(), it will
** return the pColl pointer to be deleted (because it wasn't added
@@ -83114,7 +102891,7 @@ static CollSeq *findCollSeqEntry(
*/
assert( pDel==0 || pDel==pColl );
if( pDel!=0 ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
sqlite3DbFree(db, pDel);
pColl = 0;
}
@@ -83162,38 +102939,57 @@ SQLITE_PRIVATE CollSeq *sqlite3FindCollSeq(
** that uses encoding enc. The value returned indicates how well the
** request is matched. A higher value indicates a better match.
**
+** If nArg is -1 that means to only return a match (non-zero) if p->nArg
+** is also -1. In other words, we are searching for a function that
+** takes a variable number of arguments.
+**
+** If nArg is -2 that means that we are searching for any function
+** regardless of the number of arguments it uses, so return a positive
+** match score for any
+**
** The returned value is always between 0 and 6, as follows:
**
-** 0: Not a match, or if nArg<0 and the function is has no implementation.
-** 1: A variable arguments function that prefers UTF-8 when a UTF-16
-** encoding is requested, or vice versa.
-** 2: A variable arguments function that uses UTF-16BE when UTF-16LE is
-** requested, or vice versa.
-** 3: A variable arguments function using the same text encoding.
-** 4: A function with the exact number of arguments requested that
-** prefers UTF-8 when a UTF-16 encoding is requested, or vice versa.
-** 5: A function with the exact number of arguments requested that
-** prefers UTF-16LE when UTF-16BE is requested, or vice versa.
-** 6: An exact match.
-**
-*/
-static int matchQuality(FuncDef *p, int nArg, u8 enc){
- int match = 0;
- if( p->nArg==-1 || p->nArg==nArg
- || (nArg==-1 && (p->xFunc!=0 || p->xStep!=0))
- ){
+** 0: Not a match.
+** 1: UTF8/16 conversion required and function takes any number of arguments.
+** 2: UTF16 byte order change required and function takes any number of args.
+** 3: encoding matches and function takes any number of arguments
+** 4: UTF8/16 conversion required - argument count matches exactly
+** 5: UTF16 byte order conversion required - argument count matches exactly
+** 6: Perfect match: encoding and argument count match exactly.
+**
+** If nArg==(-2) then any function with a non-null xSFunc is
+** a perfect match and any function with xSFunc NULL is
+** a non-match.
+*/
+#define FUNC_PERFECT_MATCH 6 /* The score for a perfect match */
+static int matchQuality(
+ FuncDef *p, /* The function we are evaluating for match quality */
+ int nArg, /* Desired number of arguments. (-1)==any */
+ u8 enc /* Desired text encoding */
+){
+ int match;
+
+ /* nArg of -2 is a special case */
+ if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
+
+ /* Wrong number of arguments means "no match" */
+ if( p->nArg!=nArg && p->nArg>=0 ) return 0;
+
+ /* Give a better score to a function with a specific number of arguments
+ ** than to function that accepts any number of arguments. */
+ if( p->nArg==nArg ){
+ match = 4;
+ }else{
match = 1;
- if( p->nArg==nArg || nArg==-1 ){
- match = 4;
- }
- if( enc==p->iPrefEnc ){
- match += 2;
- }
- else if( (enc==SQLITE_UTF16LE && p->iPrefEnc==SQLITE_UTF16BE) ||
- (enc==SQLITE_UTF16BE && p->iPrefEnc==SQLITE_UTF16LE) ){
- match += 1;
- }
}
+
+ /* Bonus points if the text encoding matches */
+ if( enc==(p->funcFlags & SQLITE_FUNC_ENCMASK) ){
+ match += 2; /* Exact encoding match */
+ }else if( (enc & p->funcFlags & 2)!=0 ){
+ match += 1; /* Both are UTF16, but with different byte orders */
+ }
+
return match;
}
@@ -83202,14 +102998,12 @@ static int matchQuality(FuncDef *p, int nArg, u8 enc){
** a pointer to the matching FuncDef if found, or 0 if there is no match.
*/
static FuncDef *functionSearch(
- FuncDefHash *pHash, /* Hash table to search */
int h, /* Hash of the name */
- const char *zFunc, /* Name of function */
- int nFunc /* Number of bytes in zFunc */
+ const char *zFunc /* Name of function */
){
FuncDef *p;
- for(p=pHash->a[h]; p; p=p->pHash){
- if( sqlite3StrNICmp(p->zName, zFunc, nFunc)==0 && p->zName[nFunc]==0 ){
+ for(p=sqlite3BuiltinFunctions.a[h]; p; p=p->u.pHash){
+ if( sqlite3StrICmp(p->zName, zFunc)==0 ){
return p;
}
}
@@ -83219,23 +103013,26 @@ static FuncDef *functionSearch(
/*
** Insert a new FuncDef into a FuncDefHash hash table.
*/
-SQLITE_PRIVATE void sqlite3FuncDefInsert(
- FuncDefHash *pHash, /* The hash table into which to insert */
- FuncDef *pDef /* The function definition to insert */
+SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(
+ FuncDef *aDef, /* List of global functions to be inserted */
+ int nDef /* Length of the apDef[] list */
){
- FuncDef *pOther;
- int nName = sqlite3Strlen30(pDef->zName);
- u8 c1 = (u8)pDef->zName[0];
- int h = (sqlite3UpperToLower[c1] + nName) % ArraySize(pHash->a);
- pOther = functionSearch(pHash, h, pDef->zName, nName);
- if( pOther ){
- assert( pOther!=pDef && pOther->pNext!=pDef );
- pDef->pNext = pOther->pNext;
- pOther->pNext = pDef;
- }else{
- pDef->pNext = 0;
- pDef->pHash = pHash->a[h];
- pHash->a[h] = pDef;
+ int i;
+ for(i=0; i<nDef; i++){
+ FuncDef *pOther;
+ const char *zName = aDef[i].zName;
+ int nName = sqlite3Strlen30(zName);
+ int h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
+ pOther = functionSearch(h, zName);
+ if( pOther ){
+ assert( pOther!=&aDef[i] && pOther->pNext!=&aDef[i] );
+ aDef[i].pNext = pOther->pNext;
+ pOther->pNext = &aDef[i];
+ }else{
+ aDef[i].pNext = 0;
+ aDef[i].u.pHash = sqlite3BuiltinFunctions.a[h];
+ sqlite3BuiltinFunctions.a[h] = &aDef[i];
+ }
}
}
@@ -83249,13 +103046,12 @@ SQLITE_PRIVATE void sqlite3FuncDefInsert(
**
** If the createFlag argument is true, then a new (blank) FuncDef
** structure is created and liked into the "db" structure if a
-** no matching function previously existed. When createFlag is true
-** and the nArg parameter is -1, then only a function that accepts
-** any number of arguments will be returned.
+** no matching function previously existed.
**
-** If createFlag is false and nArg is -1, then the first valid
-** function found is returned. A function is valid if either xFunc
-** or xStep is non-zero.
+** If nArg is -2, then the first valid function found is returned. A
+** function is valid if xSFunc is non-zero. The nArg==(-2)
+** case is used to see if zName is a valid function name for some number
+** of arguments. If nArg is -2, then createFlag must be 0.
**
** If createFlag is false, then a function with the required name and
** number of arguments may be returned even if the eTextRep flag does not
@@ -83263,24 +103059,24 @@ SQLITE_PRIVATE void sqlite3FuncDefInsert(
*/
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
sqlite3 *db, /* An open database */
- const char *zName, /* Name of the function. Not null-terminated */
- int nName, /* Number of characters in the name */
+ const char *zName, /* Name of the function. zero-terminated */
int nArg, /* Number of arguments. -1 means any number */
u8 enc, /* Preferred text encoding */
- int createFlag /* Create new entry if true and does not otherwise exist */
+ u8 createFlag /* Create new entry if true and does not otherwise exist */
){
FuncDef *p; /* Iterator variable */
FuncDef *pBest = 0; /* Best match found so far */
int bestScore = 0; /* Score of best match */
int h; /* Hash value */
+ int nName; /* Length of the name */
-
- assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
- h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % ArraySize(db->aFunc.a);
+ assert( nArg>=(-2) );
+ assert( nArg>=(-1) || createFlag==0 );
+ nName = sqlite3Strlen30(zName);
/* First search for a match amongst the application-defined functions.
*/
- p = functionSearch(&db->aFunc, h, zName, nName);
+ p = (FuncDef*)sqlite3HashFind(&db->aFunc, zName);
while( p ){
int score = matchQuality(p, nArg, enc);
if( score>bestScore ){
@@ -83303,9 +103099,9 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
** So we must not search for built-ins when creating a new function.
*/
if( !createFlag && (pBest==0 || (db->flags & SQLITE_PreferBuiltin)!=0) ){
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
bestScore = 0;
- p = functionSearch(pHash, h, zName, nName);
+ h = (sqlite3UpperToLower[(u8)zName[0]] + nName) % SQLITE_FUNC_HASH_SZ;
+ p = functionSearch(h, zName);
while( p ){
int score = matchQuality(p, nArg, enc);
if( score>bestScore ){
@@ -83320,17 +103116,24 @@ SQLITE_PRIVATE FuncDef *sqlite3FindFunction(
** exact match for the name, number of arguments and encoding, then add a
** new entry to the hash table and return it.
*/
- if( createFlag && (bestScore<6 || pBest->nArg!=nArg) &&
+ if( createFlag && bestScore<FUNC_PERFECT_MATCH &&
(pBest = sqlite3DbMallocZero(db, sizeof(*pBest)+nName+1))!=0 ){
- pBest->zName = (char *)&pBest[1];
+ FuncDef *pOther;
+ pBest->zName = (const char*)&pBest[1];
pBest->nArg = (u16)nArg;
- pBest->iPrefEnc = enc;
- memcpy(pBest->zName, zName, nName);
- pBest->zName[nName] = 0;
- sqlite3FuncDefInsert(&db->aFunc, pBest);
+ pBest->funcFlags = enc;
+ memcpy((char*)&pBest[1], zName, nName+1);
+ pOther = (FuncDef*)sqlite3HashInsert(&db->aFunc, pBest->zName, pBest);
+ if( pOther==pBest ){
+ sqlite3DbFree(db, pBest);
+ sqlite3OomFault(db);
+ return 0;
+ }else{
+ pBest->pNext = pOther;
+ }
}
- if( pBest && (pBest->xStep || pBest->xFunc || createFlag) ){
+ if( pBest && (pBest->xSFunc || createFlag) ){
return pBest;
}
return 0;
@@ -83366,9 +103169,9 @@ SQLITE_PRIVATE void sqlite3SchemaClear(void *p){
sqlite3HashClear(&temp1);
sqlite3HashClear(&pSchema->fkeyHash);
pSchema->pSeqTab = 0;
- if( pSchema->flags & DB_SchemaLoaded ){
+ if( pSchema->schemaFlags & DB_SchemaLoaded ){
pSchema->iGeneration++;
- pSchema->flags &= ~DB_SchemaLoaded;
+ pSchema->schemaFlags &= ~DB_SchemaLoaded;
}
}
@@ -83384,7 +103187,7 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema));
}
if( !p ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if ( 0==p->file_format ){
sqlite3HashInit(&p->tblHash);
sqlite3HashInit(&p->idxHash);
@@ -83411,6 +103214,7 @@ SQLITE_PRIVATE Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){
** This file contains C code routines that are called by the parser
** in order to generate code for DELETE FROM statements.
*/
+/* #include "sqliteInt.h" */
/*
** While a SrcList can in general represent multiple tables and subqueries
@@ -83430,7 +103234,7 @@ SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse *pParse, SrcList *pSrc){
struct SrcList_item *pItem = pSrc->a;
Table *pTab;
assert( pItem && pSrc->nSrc==1 );
- pTab = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
+ pTab = sqlite3LocateTableItem(pParse, 0, pItem);
sqlite3DeleteTable(pParse->db, pItem->pTab);
pItem->pTab = pTab;
if( pTab ){
@@ -83488,32 +103292,27 @@ SQLITE_PRIVATE void sqlite3MaterializeView(
Parse *pParse, /* Parsing context */
Table *pView, /* View definition */
Expr *pWhere, /* Optional WHERE clause to be added */
- int iCur /* Cursor number for ephemerial table */
+ int iCur /* Cursor number for ephemeral table */
){
SelectDest dest;
- Select *pDup;
+ Select *pSel;
+ SrcList *pFrom;
sqlite3 *db = pParse->db;
-
- pDup = sqlite3SelectDup(db, pView->pSelect, 0);
- if( pWhere ){
- SrcList *pFrom;
-
- pWhere = sqlite3ExprDup(db, pWhere, 0);
- pFrom = sqlite3SrcListAppend(db, 0, 0, 0);
- if( pFrom ){
- assert( pFrom->nSrc==1 );
- pFrom->a[0].zAlias = sqlite3DbStrDup(db, pView->zName);
- pFrom->a[0].pSelect = pDup;
- assert( pFrom->a[0].pOn==0 );
- assert( pFrom->a[0].pUsing==0 );
- }else{
- sqlite3SelectDelete(db, pDup);
- }
- pDup = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0, 0, 0, 0);
- }
+ int iDb = sqlite3SchemaToIndex(db, pView->pSchema);
+ pWhere = sqlite3ExprDup(db, pWhere, 0);
+ pFrom = sqlite3SrcListAppend(db, 0, 0, 0);
+ if( pFrom ){
+ assert( pFrom->nSrc==1 );
+ pFrom->a[0].zName = sqlite3DbStrDup(db, pView->zName);
+ pFrom->a[0].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zDbSName);
+ assert( pFrom->a[0].pOn==0 );
+ assert( pFrom->a[0].pUsing==0 );
+ }
+ pSel = sqlite3SelectNew(pParse, 0, pFrom, pWhere, 0, 0, 0,
+ SF_IncludeHidden, 0, 0);
sqlite3SelectDestInit(&dest, SRT_EphemTab, iCur);
- sqlite3Select(pParse, pDup, &dest);
- sqlite3SelectDelete(db, pDup);
+ sqlite3Select(pParse, pSel, &dest);
+ sqlite3SelectDelete(db, pSel);
}
#endif /* !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) */
@@ -83533,7 +103332,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
ExprList *pOrderBy, /* The ORDER BY clause. May be null */
Expr *pLimit, /* The LIMIT clause. May be null */
Expr *pOffset, /* The OFFSET clause. May be null */
- char *zStmtType /* Either DELETE or UPDATE. For error messages. */
+ char *zStmtType /* Either DELETE or UPDATE. For err msgs. */
){
Expr *pWhereRowid = NULL; /* WHERE rowid .. */
Expr *pInClause = NULL; /* WHERE rowid IN ( select ) */
@@ -83546,8 +103345,7 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
*/
if( pOrderBy && (pLimit == 0) ) {
sqlite3ErrorMsg(pParse, "ORDER BY without LIMIT on %s", zStmtType);
- pParse->parseError = 1;
- goto limit_where_cleanup_2;
+ goto limit_where_cleanup;
}
/* We only need to generate a select expression if there
@@ -83569,16 +103367,16 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
*/
pSelectRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
- if( pSelectRowid == 0 ) goto limit_where_cleanup_2;
+ if( pSelectRowid == 0 ) goto limit_where_cleanup;
pEList = sqlite3ExprListAppend(pParse, 0, pSelectRowid);
- if( pEList == 0 ) goto limit_where_cleanup_2;
+ if( pEList == 0 ) goto limit_where_cleanup;
/* duplicate the FROM clause as it is needed by both the DELETE/UPDATE tree
** and the SELECT subtree. */
pSelectSrc = sqlite3SrcListDup(pParse->db, pSrc, 0);
if( pSelectSrc == 0 ) {
sqlite3ExprListDelete(pParse->db, pEList);
- goto limit_where_cleanup_2;
+ goto limit_where_cleanup;
}
/* generate the SELECT expression tree. */
@@ -83588,28 +103386,19 @@ SQLITE_PRIVATE Expr *sqlite3LimitWhere(
/* now generate the new WHERE rowid IN clause for the DELETE/UDPATE */
pWhereRowid = sqlite3PExpr(pParse, TK_ROW, 0, 0, 0);
- if( pWhereRowid == 0 ) goto limit_where_cleanup_1;
- pInClause = sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0);
- if( pInClause == 0 ) goto limit_where_cleanup_1;
-
- pInClause->x.pSelect = pSelect;
- pInClause->flags |= EP_xIsSelect;
- sqlite3ExprSetHeight(pParse, pInClause);
+ pInClause = pWhereRowid ? sqlite3PExpr(pParse, TK_IN, pWhereRowid, 0, 0) : 0;
+ sqlite3PExprAddSelect(pParse, pInClause, pSelect);
return pInClause;
- /* something went wrong. clean up anything allocated. */
-limit_where_cleanup_1:
- sqlite3SelectDelete(pParse->db, pSelect);
- return 0;
-
-limit_where_cleanup_2:
+limit_where_cleanup:
sqlite3ExprDelete(pParse->db, pWhere);
sqlite3ExprListDelete(pParse->db, pOrderBy);
sqlite3ExprDelete(pParse->db, pLimit);
sqlite3ExprDelete(pParse->db, pOffset);
return 0;
}
-#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY) */
+#endif /* defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) */
+ /* && !defined(SQLITE_OMIT_SUBQUERY) */
/*
** Generate code for a DELETE FROM statement.
@@ -83625,19 +103414,35 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
){
Vdbe *v; /* The virtual database engine */
Table *pTab; /* The table from which records will be deleted */
- const char *zDb; /* Name of database holding pTab */
- int end, addr = 0; /* A couple addresses of generated code */
int i; /* Loop counter */
WhereInfo *pWInfo; /* Information about the WHERE clause */
Index *pIdx; /* For looping over indices of the table */
- int iCur; /* VDBE Cursor number for pTab */
+ int iTabCur; /* Cursor number for the table */
+ int iDataCur = 0; /* VDBE cursor for the canonical data source */
+ int iIdxCur = 0; /* Cursor number of the first index */
+ int nIdx; /* Number of indices */
sqlite3 *db; /* Main database structure */
AuthContext sContext; /* Authorization context */
NameContext sNC; /* Name context to resolve expressions in */
int iDb; /* Database number */
int memCnt = -1; /* Memory cell used for change counting */
int rcauth; /* Value returned by authorization callback */
-
+ int eOnePass; /* ONEPASS_OFF or _SINGLE or _MULTI */
+ int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
+ u8 *aToOpen = 0; /* Open cursor iTabCur+j if aToOpen[j] is true */
+ Index *pPk; /* The PRIMARY KEY index on the table */
+ int iPk = 0; /* First of nPk registers holding PRIMARY KEY value */
+ i16 nPk = 1; /* Number of columns in the PRIMARY KEY */
+ int iKey; /* Memory cell holding key of row to be deleted */
+ i16 nKey; /* Number of memory cells in the row key */
+ int iEphCur = 0; /* Ephemeral table holding all primary key values */
+ int iRowSet = 0; /* Register for rowset of rows to delete */
+ int addrBypass = 0; /* Address of jump over the delete logic */
+ int addrLoop = 0; /* Top of the delete loop */
+ int addrEphOpen = 0; /* Instruction to open the Ephemeral table */
+ int bComplex; /* True if there are triggers or FKs or
+ ** subqueries in the WHERE clause */
+
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to delete from a view */
Trigger *pTrigger; /* List of table triggers, if required */
@@ -83664,6 +103469,7 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
#ifndef SQLITE_OMIT_TRIGGER
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
isView = pTab->pSelect!=0;
+ bComplex = pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0);
#else
# define pTrigger 0
# define isView 0
@@ -83684,19 +103490,19 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb<db->nDb );
- zDb = db->aDb[iDb].zName;
- rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0, zDb);
+ rcauth = sqlite3AuthCheck(pParse, SQLITE_DELETE, pTab->zName, 0,
+ db->aDb[iDb].zDbSName);
assert( rcauth==SQLITE_OK || rcauth==SQLITE_DENY || rcauth==SQLITE_IGNORE );
if( rcauth==SQLITE_DENY ){
goto delete_from_cleanup;
}
assert(!isView || pTrigger);
- /* Assign cursor number to the table and all its indices.
+ /* Assign cursor numbers to the table and all its indices.
*/
assert( pTabList->nSrc==1 );
- iCur = pTabList->a[0].iCursor = pParse->nTab++;
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ iTabCur = pTabList->a[0].iCursor = pParse->nTab++;
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
pParse->nTab++;
}
@@ -83716,11 +103522,12 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
sqlite3BeginWriteOperation(pParse, 1, iDb);
/* If we are trying to delete from a view, realize that view into
- ** a ephemeral table.
+ ** an ephemeral table.
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
- sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
+ sqlite3MaterializeView(pParse, pTab, pWhere, iTabCur);
+ iDataCur = iIdxCur = iTabCur;
}
#endif
@@ -83746,82 +103553,202 @@ SQLITE_PRIVATE void sqlite3DeleteFrom(
** It is easier just to erase the whole table. Prior to version 3.6.5,
** this optimization caused the row change count (the value returned by
** API function sqlite3_count_changes) to be set incorrectly. */
- if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab)
- && 0==sqlite3FkRequired(pParse, pTab, 0, 0)
+ if( rcauth==SQLITE_OK
+ && pWhere==0
+ && !bComplex
+ && !IsVirtual(pTab)
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ && db->xPreUpdateCallback==0
+#endif
){
assert( !isView );
- sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
- pTab->zName, P4_STATIC);
+ sqlite3TableLock(pParse, iDb, pTab->tnum, 1, pTab->zName);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt,
+ pTab->zName, P4_STATIC);
+ }
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
assert( pIdx->pSchema==pTab->pSchema );
sqlite3VdbeAddOp2(v, OP_Clear, pIdx->tnum, iDb);
}
}else
#endif /* SQLITE_OMIT_TRUNCATE_OPTIMIZATION */
- /* The usual case: There is a WHERE clause so we have to scan through
- ** the table and pick which records to delete.
- */
{
- int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */
- int iRowid = ++pParse->nMem; /* Used for storing rowid values. */
- int regRowid; /* Actual register containing rowids */
-
- /* Collect rowids of every row to be deleted.
+ u16 wcf = WHERE_ONEPASS_DESIRED|WHERE_DUPLICATES_OK|WHERE_SEEK_TABLE;
+ if( sNC.ncFlags & NC_VarSelect ) bComplex = 1;
+ wcf |= (bComplex ? 0 : WHERE_ONEPASS_MULTIROW);
+ if( HasRowid(pTab) ){
+ /* For a rowid table, initialize the RowSet to an empty set */
+ pPk = 0;
+ nPk = 1;
+ iRowSet = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
+ }else{
+ /* For a WITHOUT ROWID table, create an ephemeral table used to
+ ** hold all primary keys for rows to be deleted. */
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
+ nPk = pPk->nKeyCol;
+ iPk = pParse->nMem+1;
+ pParse->nMem += nPk;
+ iEphCur = pParse->nTab++;
+ addrEphOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEphCur, nPk);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ }
+
+ /* Construct a query to find the rowid or primary key for every row
+ ** to be deleted, based on the WHERE clause. Set variable eOnePass
+ ** to indicate the strategy used to implement this delete:
+ **
+ ** ONEPASS_OFF: Two-pass approach - use a FIFO for rowids/PK values.
+ ** ONEPASS_SINGLE: One-pass approach - at most one row deleted.
+ ** ONEPASS_MULTI: One-pass approach - any number of rows may be deleted.
*/
- sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet);
- pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_DUPLICATES_OK
- );
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0, wcf, iTabCur+1);
if( pWInfo==0 ) goto delete_from_cleanup;
- regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid);
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid);
+ eOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ assert( IsVirtual(pTab)==0 || eOnePass!=ONEPASS_MULTI );
+ assert( IsVirtual(pTab) || bComplex || eOnePass!=ONEPASS_OFF );
+
+ /* Keep track of the number of rows to be deleted */
if( db->flags & SQLITE_CountRows ){
sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1);
}
- sqlite3WhereEnd(pWInfo);
-
- /* Delete every item whose key was written to the list during the
- ** database scan. We have to delete items after the scan is complete
- ** because deleting an item can change the scan order. */
- end = sqlite3VdbeMakeLabel(v);
-
+
+ /* Extract the rowid or primary key for the current row */
+ if( pPk ){
+ for(i=0; i<nPk; i++){
+ assert( pPk->aiColumn[i]>=0 );
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iTabCur,
+ pPk->aiColumn[i], iPk+i);
+ }
+ iKey = iPk;
+ }else{
+ iKey = pParse->nMem + 1;
+ iKey = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iTabCur, iKey, 0);
+ if( iKey>pParse->nMem ) pParse->nMem = iKey;
+ }
+
+ if( eOnePass!=ONEPASS_OFF ){
+ /* For ONEPASS, no need to store the rowid/primary-key. There is only
+ ** one, so just keep it in its register(s) and fall through to the
+ ** delete code. */
+ nKey = nPk; /* OP_Found will use an unpacked key */
+ aToOpen = sqlite3DbMallocRawNN(db, nIdx+2);
+ if( aToOpen==0 ){
+ sqlite3WhereEnd(pWInfo);
+ goto delete_from_cleanup;
+ }
+ memset(aToOpen, 1, nIdx+1);
+ aToOpen[nIdx+1] = 0;
+ if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iTabCur] = 0;
+ if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iTabCur] = 0;
+ if( addrEphOpen ) sqlite3VdbeChangeToNoop(v, addrEphOpen);
+ }else{
+ if( pPk ){
+ /* Add the PK key for this row to the temporary table */
+ iKey = ++pParse->nMem;
+ nKey = 0; /* Zero tells OP_Found to use a composite key */
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, iKey,
+ sqlite3IndexAffinityStr(pParse->db, pPk), nPk);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iEphCur, iKey);
+ }else{
+ /* Add the rowid of the row to be deleted to the RowSet */
+ nKey = 1; /* OP_Seek always uses a single rowid */
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
+ }
+ }
+
+ /* If this DELETE cannot use the ONEPASS strategy, this is the
+ ** end of the WHERE loop */
+ if( eOnePass!=ONEPASS_OFF ){
+ addrBypass = sqlite3VdbeMakeLabel(v);
+ }else{
+ sqlite3WhereEnd(pWInfo);
+ }
+
/* Unless this is a view, open cursors for the table we are
** deleting from and all its indices. If this is a view, then the
** only effect this statement has is to fire the INSTEAD OF
- ** triggers. */
+ ** triggers.
+ */
if( !isView ){
- sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite);
+ int iAddrOnce = 0;
+ if( eOnePass==ONEPASS_MULTI ){
+ iAddrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ }
+ testcase( IsVirtual(pTab) );
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, OPFLAG_FORDELETE,
+ iTabCur, aToOpen, &iDataCur, &iIdxCur);
+ assert( pPk || IsVirtual(pTab) || iDataCur==iTabCur );
+ assert( pPk || IsVirtual(pTab) || iIdxCur==iDataCur+1 );
+ if( eOnePass==ONEPASS_MULTI ) sqlite3VdbeJumpHere(v, iAddrOnce);
}
-
- addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid);
-
+
+ /* Set up a loop over the rowids/primary-keys that were found in the
+ ** where-clause loop above.
+ */
+ if( eOnePass!=ONEPASS_OFF ){
+ assert( nKey==nPk ); /* OP_Found will use an unpacked key */
+ if( !IsVirtual(pTab) && aToOpen[iDataCur-iTabCur] ){
+ assert( pPk!=0 || pTab->pSelect!=0 );
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, addrBypass, iKey, nKey);
+ VdbeCoverage(v);
+ }
+ }else if( pPk ){
+ addrLoop = sqlite3VdbeAddOp1(v, OP_Rewind, iEphCur); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_RowKey, iEphCur, iKey);
+ assert( nKey==0 ); /* OP_Found will use a composite key */
+ }else{
+ addrLoop = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, 0, iKey);
+ VdbeCoverage(v);
+ assert( nKey==1 );
+ }
+
/* Delete the row */
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( IsVirtual(pTab) ){
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
sqlite3VtabMakeWritable(pParse, pTab);
- sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iKey, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, OE_Abort);
+ assert( eOnePass==ONEPASS_OFF || eOnePass==ONEPASS_SINGLE );
sqlite3MayAbort(pParse);
+ if( eOnePass==ONEPASS_SINGLE && sqlite3IsToplevel(pParse) ){
+ pParse->isMultiWrite = 0;
+ }
}else
#endif
{
int count = (pParse->nested==0); /* True to count changes */
- sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default);
+ int iIdxNoSeek = -1;
+ if( bComplex==0 && aiCurOnePass[1]!=iDataCur ){
+ iIdxNoSeek = aiCurOnePass[1];
+ }
+ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
+ iKey, nKey, count, OE_Default, eOnePass, iIdxNoSeek);
}
-
- /* End of the delete loop */
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
- sqlite3VdbeResolveLabel(v, end);
-
+
+ /* End of the loop over all rowids/primary-keys. */
+ if( eOnePass!=ONEPASS_OFF ){
+ sqlite3VdbeResolveLabel(v, addrBypass);
+ sqlite3WhereEnd(pWInfo);
+ }else if( pPk ){
+ sqlite3VdbeAddOp2(v, OP_Next, iEphCur, addrLoop+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrLoop);
+ }else{
+ sqlite3VdbeGoto(v, addrLoop);
+ sqlite3VdbeJumpHere(v, addrLoop);
+ }
+
/* Close the cursors open on the table and its indexes. */
if( !isView && !IsVirtual(pTab) ){
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum);
+ if( !pPk ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
+ for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ sqlite3VdbeAddOp1(v, OP_Close, iIdxCur + i);
}
- sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
- }
+ } /* End non-truncate path */
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
@@ -83845,10 +103772,11 @@ delete_from_cleanup:
sqlite3AuthContextPop(&sContext);
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprDelete(db, pWhere);
+ sqlite3DbFree(db, aToOpen);
return;
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
-** thely may interfere with compilation of other functions in this file
+** they may interfere with compilation of other functions in this file
** (or in another file, if this file becomes part of the amalgamation). */
#ifdef isView
#undef isView
@@ -83859,50 +103787,83 @@ delete_from_cleanup:
/*
** This routine generates VDBE code that causes a single row of a
-** single table to be deleted.
+** single table to be deleted. Both the original table entry and
+** all indices are removed.
**
-** The VDBE must be in a particular state when this routine is called.
-** These are the requirements:
+** Preconditions:
**
-** 1. A read/write cursor pointing to pTab, the table containing the row
-** to be deleted, must be opened as cursor number $iCur.
+** 1. iDataCur is an open cursor on the btree that is the canonical data
+** store for the table. (This will be either the table itself,
+** in the case of a rowid table, or the PRIMARY KEY index in the case
+** of a WITHOUT ROWID table.)
**
** 2. Read/write cursors for all indices of pTab must be open as
-** cursor number base+i for the i-th index.
-**
-** 3. The record number of the row to be deleted must be stored in
-** memory cell iRowid.
-**
-** This routine generates code to remove both the table record and all
-** index entries that point to that record.
+** cursor number iIdxCur+i for the i-th index.
+**
+** 3. The primary key for the row to be deleted must be stored in a
+** sequence of nPk memory cells starting at iPk. If nPk==0 that means
+** that a search record formed from OP_MakeRecord is contained in the
+** single memory location iPk.
+**
+** eMode:
+** Parameter eMode may be passed either ONEPASS_OFF (0), ONEPASS_SINGLE, or
+** ONEPASS_MULTI. If eMode is not ONEPASS_OFF, then the cursor
+** iDataCur already points to the row to delete. If eMode is ONEPASS_OFF
+** then this function must seek iDataCur to the entry identified by iPk
+** and nPk before reading from it.
+**
+** If eMode is ONEPASS_MULTI, then this call is being made as part
+** of a ONEPASS delete that affects multiple rows. In this case, if
+** iIdxNoSeek is a valid cursor number (>=0), then its position should
+** be preserved following the delete operation. Or, if iIdxNoSeek is not
+** a valid cursor number, the position of iDataCur should be preserved
+** instead.
+**
+** iIdxNoSeek:
+** If iIdxNoSeek is a valid cursor number (>=0), then it identifies an
+** index cursor (from within array of cursors starting at iIdxCur) that
+** already points to the index entry to be deleted.
*/
SQLITE_PRIVATE void sqlite3GenerateRowDelete(
Parse *pParse, /* Parsing context */
Table *pTab, /* Table containing the row to be deleted */
- int iCur, /* Cursor number for the table */
- int iRowid, /* Memory cell that contains the rowid to delete */
- int count, /* If non-zero, increment the row change counter */
Trigger *pTrigger, /* List of triggers to (potentially) fire */
- int onconf /* Default ON CONFLICT policy for triggers */
+ int iDataCur, /* Cursor from which column data is extracted */
+ int iIdxCur, /* First index cursor */
+ int iPk, /* First memory cell containing the PRIMARY KEY */
+ i16 nPk, /* Number of PRIMARY KEY memory cells */
+ u8 count, /* If non-zero, increment the row change counter */
+ u8 onconf, /* Default ON CONFLICT policy for triggers */
+ u8 eMode, /* ONEPASS_OFF, _SINGLE, or _MULTI. See above */
+ int iIdxNoSeek /* Cursor number of cursor that does not need seeking */
){
Vdbe *v = pParse->pVdbe; /* Vdbe */
int iOld = 0; /* First register in OLD.* array */
int iLabel; /* Label resolved to end of generated code */
+ u8 opSeek; /* Seek opcode */
/* Vdbe is guaranteed to have been allocated by this stage. */
assert( v );
+ VdbeModuleComment((v, "BEGIN: GenRowDel(%d,%d,%d,%d)",
+ iDataCur, iIdxCur, iPk, (int)nPk));
/* Seek cursor iCur to the row to delete. If this row no longer exists
** (this can happen if a trigger program has already deleted it), do
** not attempt to delete it or fire any DELETE triggers. */
iLabel = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
+ opSeek = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
+ if( eMode==ONEPASS_OFF ){
+ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
+ VdbeCoverageIf(v, opSeek==OP_NotExists);
+ VdbeCoverageIf(v, opSeek==OP_NotFound);
+ }
/* If there are any triggers to fire, allocate a range of registers to
** use for the old.* references in the triggers. */
if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){
u32 mask; /* Mask of OLD.* columns in use */
int iCol; /* Iterator used while populating OLD.* */
+ int addrStart; /* Start of BEFORE trigger programs */
/* TODO: Could use temporary registers here. Also could attempt to
** avoid copying the contents of the rowid register. */
@@ -83915,45 +103876,67 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
/* Populate the OLD.* pseudo-table register array. These values will be
** used by any BEFORE and AFTER triggers that exist. */
- sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld);
+ sqlite3VdbeAddOp2(v, OP_Copy, iPk, iOld);
for(iCol=0; iCol<pTab->nCol; iCol++){
- if( mask==0xffffffff || mask&(1<<iCol) ){
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, iCol, iOld+iCol+1);
+ testcase( mask!=0xffffffff && iCol==31 );
+ testcase( mask!=0xffffffff && iCol==32 );
+ if( mask==0xffffffff || (iCol<=31 && (mask & MASKBIT32(iCol))!=0) ){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, iCol, iOld+iCol+1);
}
}
/* Invoke BEFORE DELETE trigger programs. */
+ addrStart = sqlite3VdbeCurrentAddr(v);
sqlite3CodeRowTrigger(pParse, pTrigger,
TK_DELETE, 0, TRIGGER_BEFORE, pTab, iOld, onconf, iLabel
);
- /* Seek the cursor to the row to be deleted again. It may be that
- ** the BEFORE triggers coded above have already removed the row
- ** being deleted. Do not attempt to delete the row a second time, and
- ** do not fire AFTER triggers. */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid);
+ /* If any BEFORE triggers were coded, then seek the cursor to the
+ ** row to be deleted again. It may be that the BEFORE triggers moved
+ ** the cursor or of already deleted the row that the cursor was
+ ** pointing to.
+ */
+ if( addrStart<sqlite3VdbeCurrentAddr(v) ){
+ sqlite3VdbeAddOp4Int(v, opSeek, iDataCur, iLabel, iPk, nPk);
+ VdbeCoverageIf(v, opSeek==OP_NotExists);
+ VdbeCoverageIf(v, opSeek==OP_NotFound);
+ }
/* Do FK processing. This call checks that any FK constraints that
** refer to this table (i.e. constraints attached to other tables)
** are not violated by deleting this row. */
- sqlite3FkCheck(pParse, pTab, iOld, 0);
+ sqlite3FkCheck(pParse, pTab, iOld, 0, 0, 0);
}
/* Delete the index and table entries. Skip this step if pTab is really
** a view (in which case the only effect of the DELETE statement is to
- ** fire the INSTEAD OF triggers). */
+ ** fire the INSTEAD OF triggers).
+ **
+ ** If variable 'count' is non-zero, then this OP_Delete instruction should
+ ** invoke the update-hook. The pre-update-hook, on the other hand should
+ ** be invoked unless table pTab is a system table. The difference is that
+ ** the update-hook is not invoked for rows removed by REPLACE, but the
+ ** pre-update-hook is.
+ */
if( pTab->pSelect==0 ){
- sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0);
- sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0));
- if( count ){
- sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
+ u8 p5 = 0;
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,iIdxNoSeek);
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, (count?OPFLAG_NCHANGE:0));
+ sqlite3VdbeChangeP4(v, -1, (char*)pTab, P4_TABLE);
+ if( eMode!=ONEPASS_OFF ){
+ sqlite3VdbeChangeP5(v, OPFLAG_AUXDELETE);
}
+ if( iIdxNoSeek>=0 ){
+ sqlite3VdbeAddOp1(v, OP_Delete, iIdxNoSeek);
+ }
+ if( eMode==ONEPASS_MULTI ) p5 |= OPFLAG_SAVEPOSITION;
+ sqlite3VdbeChangeP5(v, p5);
}
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
** handle rows (possibly in other tables) that refer via a foreign key
** to the row just deleted. */
- sqlite3FkActions(pParse, pTab, 0, iOld);
+ sqlite3FkActions(pParse, pTab, 0, iOld, 0, 0);
/* Invoke AFTER DELETE trigger programs. */
sqlite3CodeRowTrigger(pParse, pTrigger,
@@ -83964,91 +103947,156 @@ SQLITE_PRIVATE void sqlite3GenerateRowDelete(
** trigger programs were invoked. Or if a trigger program throws a
** RAISE(IGNORE) exception. */
sqlite3VdbeResolveLabel(v, iLabel);
+ VdbeModuleComment((v, "END: GenRowDel()"));
}
/*
** This routine generates VDBE code that causes the deletion of all
-** index entries associated with a single row of a single table.
+** index entries associated with a single row of a single table, pTab
**
-** The VDBE must be in a particular state when this routine is called.
-** These are the requirements:
+** Preconditions:
**
-** 1. A read/write cursor pointing to pTab, the table containing the row
-** to be deleted, must be opened as cursor number "iCur".
+** 1. A read/write cursor "iDataCur" must be open on the canonical storage
+** btree for the table pTab. (This will be either the table itself
+** for rowid tables or to the primary key index for WITHOUT ROWID
+** tables.)
**
** 2. Read/write cursors for all indices of pTab must be open as
-** cursor number iCur+i for the i-th index.
+** cursor number iIdxCur+i for the i-th index. (The pTab->pIndex
+** index is the 0-th index.)
**
-** 3. The "iCur" cursor must be pointing to the row that is to be
-** deleted.
+** 3. The "iDataCur" cursor must be already be positioned on the row
+** that is to be deleted.
*/
SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(
Parse *pParse, /* Parsing and code generating context */
Table *pTab, /* Table containing the row to be deleted */
- int iCur, /* Cursor number for the table */
- int *aRegIdx /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
-){
- int i;
- Index *pIdx;
- int r1;
+ int iDataCur, /* Cursor of table holding data. */
+ int iIdxCur, /* First index cursor */
+ int *aRegIdx, /* Only delete if aRegIdx!=0 && aRegIdx[i]>0 */
+ int iIdxNoSeek /* Do not delete from this cursor */
+){
+ int i; /* Index loop counter */
+ int r1 = -1; /* Register holding an index key */
+ int iPartIdxLabel; /* Jump destination for skipping partial index entries */
+ Index *pIdx; /* Current index */
+ Index *pPrior = 0; /* Prior index */
+ Vdbe *v; /* The prepared statement under construction */
+ Index *pPk; /* PRIMARY KEY index, or NULL for rowid tables */
- for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
- if( aRegIdx!=0 && aRegIdx[i-1]==0 ) continue;
- r1 = sqlite3GenerateIndexKey(pParse, pIdx, iCur, 0, 0);
- sqlite3VdbeAddOp3(pParse->pVdbe, OP_IdxDelete, iCur+i, r1,pIdx->nColumn+1);
+ v = pParse->pVdbe;
+ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+ for(i=0, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
+ assert( iIdxCur+i!=iDataCur || pPk==pIdx );
+ if( aRegIdx!=0 && aRegIdx[i]==0 ) continue;
+ if( pIdx==pPk ) continue;
+ if( iIdxCur+i==iIdxNoSeek ) continue;
+ VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
+ &iPartIdxLabel, pPrior, r1);
+ sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
+ pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
+ sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
+ pPrior = pIdx;
}
}
/*
-** Generate code that will assemble an index key and put it in register
+** Generate code that will assemble an index key and stores it in register
** regOut. The key with be for index pIdx which is an index on pTab.
** iCur is the index of a cursor open on the pTab table and pointing to
-** the entry that needs indexing.
+** the entry that needs indexing. If pTab is a WITHOUT ROWID table, then
+** iCur must be the cursor of the PRIMARY KEY index.
**
** Return a register number which is the first in a block of
** registers that holds the elements of the index key. The
** block of registers has already been deallocated by the time
** this routine returns.
+**
+** If *piPartIdxLabel is not NULL, fill it in with a label and jump
+** to that label if pIdx is a partial index that should be skipped.
+** The label should be resolved using sqlite3ResolvePartIdxLabel().
+** A partial index should be skipped if its WHERE clause evaluates
+** to false or null. If pIdx is not a partial index, *piPartIdxLabel
+** will be set to zero which is an empty label that is ignored by
+** sqlite3ResolvePartIdxLabel().
+**
+** The pPrior and regPrior parameters are used to implement a cache to
+** avoid unnecessary register loads. If pPrior is not NULL, then it is
+** a pointer to a different index for which an index key has just been
+** computed into register regPrior. If the current pIdx index is generating
+** its key into the same sequence of registers and if pPrior and pIdx share
+** a column in common, then the register corresponding to that column already
+** holds the correct value and the loading of that register is skipped.
+** This optimization is helpful when doing a DELETE or an INTEGRITY_CHECK
+** on a table with multiple indices, and especially with the ROWID or
+** PRIMARY KEY columns of the index.
*/
SQLITE_PRIVATE int sqlite3GenerateIndexKey(
- Parse *pParse, /* Parsing context */
- Index *pIdx, /* The index for which to generate a key */
- int iCur, /* Cursor number for the pIdx->pTable table */
- int regOut, /* Write the new index key to this register */
- int doMakeRec /* Run the OP_MakeRecord instruction if true */
+ Parse *pParse, /* Parsing context */
+ Index *pIdx, /* The index for which to generate a key */
+ int iDataCur, /* Cursor number from which to take column data */
+ int regOut, /* Put the new key into this register if not 0 */
+ int prefixOnly, /* Compute only a unique prefix of the key */
+ int *piPartIdxLabel, /* OUT: Jump to this label to skip partial index */
+ Index *pPrior, /* Previously generated index key */
+ int regPrior /* Register holding previous generated key */
){
Vdbe *v = pParse->pVdbe;
int j;
- Table *pTab = pIdx->pTable;
int regBase;
int nCol;
- nCol = pIdx->nColumn;
- regBase = sqlite3GetTempRange(pParse, nCol+1);
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regBase+nCol);
- for(j=0; j<nCol; j++){
- int idx = pIdx->aiColumn[j];
- if( idx==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j);
+ if( piPartIdxLabel ){
+ if( pIdx->pPartIdxWhere ){
+ *piPartIdxLabel = sqlite3VdbeMakeLabel(v);
+ pParse->iSelfTab = iDataCur;
+ sqlite3ExprCachePush(pParse);
+ sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, *piPartIdxLabel,
+ SQLITE_JUMPIFNULL);
}else{
- sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j);
- sqlite3ColumnDefault(v, pTab, idx, -1);
+ *piPartIdxLabel = 0;
}
}
- if( doMakeRec ){
- const char *zAff;
- if( pTab->pSelect || (pParse->db->flags & SQLITE_IdxRealAsInt)!=0 ){
- zAff = 0;
- }else{
- zAff = sqlite3IndexAffinityStr(v, pIdx);
+ nCol = (prefixOnly && pIdx->uniqNotNull) ? pIdx->nKeyCol : pIdx->nColumn;
+ regBase = sqlite3GetTempRange(pParse, nCol);
+ if( pPrior && (regBase!=regPrior || pPrior->pPartIdxWhere) ) pPrior = 0;
+ for(j=0; j<nCol; j++){
+ if( pPrior
+ && pPrior->aiColumn[j]==pIdx->aiColumn[j]
+ && pPrior->aiColumn[j]!=XN_EXPR
+ ){
+ /* This column was already computed by the previous index */
+ continue;
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut);
- sqlite3VdbeChangeP4(v, -1, zAff, P4_TRANSIENT);
+ sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iDataCur, j, regBase+j);
+ /* If the column affinity is REAL but the number is an integer, then it
+ ** might be stored in the table as an integer (using a compact
+ ** representation) then converted to REAL by an OP_RealAffinity opcode.
+ ** But we are getting ready to store this value back into an index, where
+ ** it should be converted by to INTEGER again. So omit the OP_RealAffinity
+ ** opcode if it is present */
+ sqlite3VdbeDeletePriorOpcode(v, OP_RealAffinity);
+ }
+ if( regOut ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol, regOut);
}
- sqlite3ReleaseTempRange(pParse, regBase, nCol+1);
+ sqlite3ReleaseTempRange(pParse, regBase, nCol);
return regBase;
}
+/*
+** If a prior call to sqlite3GenerateIndexKey() generated a jump-over label
+** because it was a partial index, then this routine should be called to
+** resolve that label.
+*/
+SQLITE_PRIVATE void sqlite3ResolvePartIdxLabel(Parse *pParse, int iLabel){
+ if( iLabel ){
+ sqlite3VdbeResolveLabel(pParse->pVdbe, iLabel);
+ sqlite3ExprCachePop(pParse);
+ }
+}
+
/************** End of delete.c **********************************************/
/************** Begin file func.c ********************************************/
/*
@@ -84062,21 +104110,33 @@ SQLITE_PRIVATE int sqlite3GenerateIndexKey(
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This file contains the C functions that implement various SQL
-** functions of SQLite.
-**
-** There is only one exported symbol in this file - the function
-** sqliteRegisterBuildinFunctions() found at the bottom of the file.
-** All other code has file scope.
+** This file contains the C-language implementations for many of the SQL
+** functions of SQLite. (Some function, and in particular the date and
+** time functions, are implemented separately.)
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <assert.h> */
+/* #include "vdbeInt.h" */
/*
** Return the collating function associated with a function.
*/
static CollSeq *sqlite3GetFuncCollSeq(sqlite3_context *context){
- return context->pColl;
+ VdbeOp *pOp;
+ assert( context->pVdbe!=0 );
+ pOp = &context->pVdbe->aOp[context->iOp-1];
+ assert( pOp->opcode==OP_CollSeq );
+ assert( pOp->p4type==P4_COLLSEQ );
+ return pOp->p4.pColl;
+}
+
+/*
+** Indicate that the accumulator load should be skipped on this
+** iteration of the aggregate loop.
+*/
+static void sqlite3SkipAccumulatorLoad(sqlite3_context *context){
+ context->skipFlag = 1;
}
/*
@@ -84180,9 +104240,9 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
case SQLITE_INTEGER: {
i64 iVal = sqlite3_value_int64(argv[0]);
if( iVal<0 ){
- if( (iVal<<1)==0 ){
- /* IMP: R-35460-15084 If X is the integer -9223372036854775807 then
- ** abs(X) throws an integer overflow error since there is no
+ if( iVal==SMALLEST_INT64 ){
+ /* IMP: R-31676-45509 If X is the integer -9223372036854775808
+ ** then abs(X) throws an integer overflow error since there is no
** equivalent positive 64-bit two complement value. */
sqlite3_result_error(context, "integer overflow", -1);
return;
@@ -84200,8 +104260,8 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
default: {
/* Because sqlite3_value_double() returns 0.0 if the argument is not
** something that can be converted into a number, we have:
- ** IMP: R-57326-31541 Abs(X) return 0.0 if X is a string or blob that
- ** cannot be converted to a numeric value.
+ ** IMP: R-01992-00519 Abs(X) returns 0.0 if X is a string or blob
+ ** that cannot be converted to a numeric value.
*/
double rVal = sqlite3_value_double(argv[0]);
if( rVal<0 ) rVal = -rVal;
@@ -84212,6 +104272,83 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}
/*
+** Implementation of the instr() function.
+**
+** instr(haystack,needle) finds the first occurrence of needle
+** in haystack and returns the number of previous characters plus 1,
+** or 0 if needle does not occur within haystack.
+**
+** If both haystack and needle are BLOBs, then the result is one more than
+** the number of bytes in haystack prior to the first occurrence of needle,
+** or 0 if needle never occurs in haystack.
+*/
+static void instrFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *zHaystack;
+ const unsigned char *zNeedle;
+ int nHaystack;
+ int nNeedle;
+ int typeHaystack, typeNeedle;
+ int N = 1;
+ int isText;
+
+ UNUSED_PARAMETER(argc);
+ typeHaystack = sqlite3_value_type(argv[0]);
+ typeNeedle = sqlite3_value_type(argv[1]);
+ if( typeHaystack==SQLITE_NULL || typeNeedle==SQLITE_NULL ) return;
+ nHaystack = sqlite3_value_bytes(argv[0]);
+ nNeedle = sqlite3_value_bytes(argv[1]);
+ if( typeHaystack==SQLITE_BLOB && typeNeedle==SQLITE_BLOB ){
+ zHaystack = sqlite3_value_blob(argv[0]);
+ zNeedle = sqlite3_value_blob(argv[1]);
+ isText = 0;
+ }else{
+ zHaystack = sqlite3_value_text(argv[0]);
+ zNeedle = sqlite3_value_text(argv[1]);
+ isText = 1;
+ }
+ while( nNeedle<=nHaystack && memcmp(zHaystack, zNeedle, nNeedle)!=0 ){
+ N++;
+ do{
+ nHaystack--;
+ zHaystack++;
+ }while( isText && (zHaystack[0]&0xc0)==0x80 );
+ }
+ if( nNeedle>nHaystack ) N = 0;
+ sqlite3_result_int(context, N);
+}
+
+/*
+** Implementation of the printf() function.
+*/
+static void printfFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ PrintfArguments x;
+ StrAccum str;
+ const char *zFormat;
+ int n;
+ sqlite3 *db = sqlite3_context_db_handle(context);
+
+ if( argc>=1 && (zFormat = (const char*)sqlite3_value_text(argv[0]))!=0 ){
+ x.nArg = argc-1;
+ x.nUsed = 0;
+ x.apArg = argv+1;
+ sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
+ str.printfFlags = SQLITE_PRINTF_SQLFUNC;
+ sqlite3XPrintf(&str, zFormat, &x);
+ n = str.nChar;
+ sqlite3_result_text(context, sqlite3StrAccumFinish(&str), n,
+ SQLITE_DYNAMIC);
+ }
+}
+
+/*
** Implementation of the substr() function.
**
** substr(x,p1,p2) returns p2 characters of x[] beginning with p1.
@@ -84221,7 +104358,7 @@ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
**
** If p1 is negative, then we begin abs(p1) from the end of x[].
**
-** If p2 is negative, return the p2 characters preceeding p1.
+** If p2 is negative, return the p2 characters preceding p1.
*/
static void substrFunc(
sqlite3_context *context,
@@ -84258,6 +104395,14 @@ static void substrFunc(
}
}
}
+#ifdef SQLITE_SUBSTR_COMPATIBILITY
+ /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as
+ ** as substr(X,1,N) - it returns the first N characters of X. This
+ ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8]
+ ** from 2009-02-02 for compatibility of applications that exploited the
+ ** old buggy behavior. */
+ if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
+#endif
if( argc==3 ){
p2 = sqlite3_value_int(argv[2]);
if( p2<0 ){
@@ -84295,13 +104440,14 @@ static void substrFunc(
for(z2=z; *z2 && p2; p2--){
SQLITE_SKIP_UTF8(z2);
}
- sqlite3_result_text(context, (char*)z, (int)(z2-z), SQLITE_TRANSIENT);
+ sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT,
+ SQLITE_UTF8);
}else{
if( p1+p2>len ){
p2 = len-p1;
if( p2<0 ) p2 = 0;
}
- sqlite3_result_blob(context, (char*)&z[p1], (int)p2, SQLITE_TRANSIENT);
+ sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT);
}
}
@@ -84344,7 +104490,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
#endif
/*
-** Allocate nByte bytes of space using sqlite3_malloc(). If the
+** Allocate nByte bytes of space using sqlite3Malloc(). If the
** allocation fails, call sqlite3_result_error_nomem() to notify
** the database handle that malloc() has failed and return NULL.
** If nByte is larger than the maximum string or blob length, then
@@ -84360,7 +104506,7 @@ static void *contextMalloc(sqlite3_context *context, i64 nByte){
sqlite3_result_error_toobig(context);
z = 0;
}else{
- z = sqlite3Malloc((int)nByte);
+ z = sqlite3Malloc(nByte);
if( !z ){
sqlite3_result_error_nomem(context);
}
@@ -84383,16 +104529,15 @@ static void upperFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
if( z2 ){
z1 = contextMalloc(context, ((i64)n)+1);
if( z1 ){
- memcpy(z1, z2, n+1);
- for(i=0; z1[i]; i++){
- z1[i] = (char)sqlite3Toupper(z1[i]);
+ for(i=0; i<n; i++){
+ z1[i] = (char)sqlite3Toupper(z2[i]);
}
- sqlite3_result_text(context, z1, -1, sqlite3_free);
+ sqlite3_result_text(context, z1, n, sqlite3_free);
}
}
}
static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
- u8 *z1;
+ char *z1;
const char *z2;
int i, n;
UNUSED_PARAMETER(argc);
@@ -84403,43 +104548,23 @@ static void lowerFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
if( z2 ){
z1 = contextMalloc(context, ((i64)n)+1);
if( z1 ){
- memcpy(z1, z2, n+1);
- for(i=0; z1[i]; i++){
- z1[i] = sqlite3Tolower(z1[i]);
+ for(i=0; i<n; i++){
+ z1[i] = sqlite3Tolower(z2[i]);
}
- sqlite3_result_text(context, (char *)z1, -1, sqlite3_free);
+ sqlite3_result_text(context, z1, n, sqlite3_free);
}
}
}
-
-#if 0 /* This function is never used. */
/*
-** The COALESCE() and IFNULL() functions used to be implemented as shown
-** here. But now they are implemented as VDBE code so that unused arguments
-** do not have to be computed. This legacy implementation is retained as
-** comment.
-*/
-/*
-** Implementation of the IFNULL(), NVL(), and COALESCE() functions.
-** All three do the same thing. They return the first non-NULL
-** argument.
+** Some functions like COALESCE() and IFNULL() and UNLIKELY() are implemented
+** as VDBE code so that unused argument values do not have to be computed.
+** However, we still need some kind of function implementation for this
+** routines in the function table. The noopFunc macro provides this.
+** noopFunc will never be called so it doesn't matter what the implementation
+** is. We might as well use the "version()" function as a substitute.
*/
-static void ifnullFunc(
- sqlite3_context *context,
- int argc,
- sqlite3_value **argv
-){
- int i;
- for(i=0; i<argc; i++){
- if( SQLITE_NULL!=sqlite3_value_type(argv[i]) ){
- sqlite3_result_value(context, argv[i]);
- break;
- }
- }
-}
-#endif /* NOT USED */
-#define ifnullFunc versionFunc /* Substitute function - never called */
+#define noopFunc versionFunc /* Substitute function - never called */
/*
** Implementation of random(). Return a random integer.
@@ -84461,7 +104586,7 @@ static void randomFunc(
** 2s complement of that positive value. The end result can
** therefore be no less than -9223372036854775807.
*/
- r = -(r ^ (((sqlite3_int64)1)<<63));
+ r = -(r & LARGEST_INT64);
}
sqlite3_result_int64(context, r);
}
@@ -84544,23 +104669,23 @@ static void total_changes(
** A structure defining how to do GLOB-style comparisons.
*/
struct compareInfo {
- u8 matchAll;
- u8 matchOne;
- u8 matchSet;
- u8 noCase;
+ u8 matchAll; /* "*" or "%" */
+ u8 matchOne; /* "?" or "_" */
+ u8 matchSet; /* "[" or 0 */
+ u8 noCase; /* true to ignore case differences */
};
/*
** For LIKE and GLOB matching on EBCDIC machines, assume that every
-** character is exactly one byte in size. Also, all characters are
-** able to participate in upper-case-to-lower-case mappings in EBCDIC
-** whereas only characters less than 0x80 do in ASCII.
+** character is exactly one byte in size. Also, provde the Utf8Read()
+** macro for fast reading of the next character in the common case where
+** the next character is ASCII.
*/
#if defined(SQLITE_EBCDIC)
-# define sqlite3Utf8Read(A,C) (*(A++))
-# define GlogUpperToLower(A) A = sqlite3UpperToLower[A]
+# define sqlite3Utf8Read(A) (*((*A)++))
+# define Utf8Read(A) (*(A++))
#else
-# define GlogUpperToLower(A) if( !((A)&~0x7f) ){ A = sqlite3UpperToLower[A]; }
+# define Utf8Read(A) (A[0]<0x80?*(A++):sqlite3Utf8Read(&A))
#endif
static const struct compareInfo globInfo = { '*', '?', '[', 0 };
@@ -84573,7 +104698,7 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
/*
** Compare two UTF-8 strings for equality where the first string can
-** potentially be a "glob" expression. Return true (1) if they
+** potentially be a "glob" or "like" expression. Return true (1) if they
** are the same and false (0) if they are different.
**
** Globbing rules:
@@ -84593,121 +104718,153 @@ static const struct compareInfo likeInfoAlt = { '%', '_', 0, 0 };
** "[a-z]" matches any single lower-case letter. To match a '-', make
** it the last character in the list.
**
-** This routine is usually quick, but can be N**2 in the worst case.
+** Like matching rules:
+**
+** '%' Matches any sequence of zero or more characters
+**
+*** '_' Matches any one character
**
-** Hints: to match '*' or '?', put them in "[]". Like this:
+** Ec Where E is the "esc" character and c is any other
+** character, including '%', '_', and esc, match exactly c.
**
-** abc[*]xyz Matches "abc*xyz" only
+** The comments within this routine usually assume glob matching.
+**
+** This routine is usually quick, but can be N**2 in the worst case.
*/
static int patternCompare(
const u8 *zPattern, /* The glob pattern */
const u8 *zString, /* The string to compare against the glob */
const struct compareInfo *pInfo, /* Information about how to do the compare */
- u32 esc /* The escape character */
-){
- u32 c, c2;
- int invert;
- int seen;
- u8 matchOne = pInfo->matchOne;
- u8 matchAll = pInfo->matchAll;
- u8 matchSet = pInfo->matchSet;
- u8 noCase = pInfo->noCase;
- int prevEscape = 0; /* True if the previous character was 'escape' */
-
- while( (c = sqlite3Utf8Read(zPattern,&zPattern))!=0 ){
- if( !prevEscape && c==matchAll ){
- while( (c=sqlite3Utf8Read(zPattern,&zPattern)) == matchAll
- || c == matchOne ){
- if( c==matchOne && sqlite3Utf8Read(zString, &zString)==0 ){
+ u32 matchOther /* The escape char (LIKE) or '[' (GLOB) */
+){
+ u32 c, c2; /* Next pattern and input string chars */
+ u32 matchOne = pInfo->matchOne; /* "?" or "_" */
+ u32 matchAll = pInfo->matchAll; /* "*" or "%" */
+ u8 noCase = pInfo->noCase; /* True if uppercase==lowercase */
+ const u8 *zEscaped = 0; /* One past the last escaped input char */
+
+ while( (c = Utf8Read(zPattern))!=0 ){
+ if( c==matchAll ){ /* Match "*" */
+ /* Skip over multiple "*" characters in the pattern. If there
+ ** are also "?" characters, skip those as well, but consume a
+ ** single character of the input string for each "?" skipped */
+ while( (c=Utf8Read(zPattern)) == matchAll || c == matchOne ){
+ if( c==matchOne && sqlite3Utf8Read(&zString)==0 ){
return 0;
}
}
if( c==0 ){
- return 1;
- }else if( c==esc ){
- c = sqlite3Utf8Read(zPattern, &zPattern);
- if( c==0 ){
- return 0;
- }
- }else if( c==matchSet ){
- assert( esc==0 ); /* This is GLOB, not LIKE */
- assert( matchSet<0x80 ); /* '[' is a single-byte character */
- while( *zString && patternCompare(&zPattern[-1],zString,pInfo,esc)==0 ){
- SQLITE_SKIP_UTF8(zString);
+ return 1; /* "*" at the end of the pattern matches */
+ }else if( c==matchOther ){
+ if( pInfo->matchSet==0 ){
+ c = sqlite3Utf8Read(&zPattern);
+ if( c==0 ) return 0;
+ }else{
+ /* "[...]" immediately follows the "*". We have to do a slow
+ ** recursive search in this case, but it is an unusual case. */
+ assert( matchOther<0x80 ); /* '[' is a single-byte character */
+ while( *zString
+ && patternCompare(&zPattern[-1],zString,pInfo,matchOther)==0 ){
+ SQLITE_SKIP_UTF8(zString);
+ }
+ return *zString!=0;
}
- return *zString!=0;
}
- while( (c2 = sqlite3Utf8Read(zString,&zString))!=0 ){
+
+ /* At this point variable c contains the first character of the
+ ** pattern string past the "*". Search in the input string for the
+ ** first matching character and recursively contine the match from
+ ** that point.
+ **
+ ** For a case-insensitive search, set variable cx to be the same as
+ ** c but in the other case and search the input string for either
+ ** c or cx.
+ */
+ if( c<=0x80 ){
+ u32 cx;
if( noCase ){
- GlogUpperToLower(c2);
- GlogUpperToLower(c);
- while( c2 != 0 && c2 != c ){
- c2 = sqlite3Utf8Read(zString, &zString);
- GlogUpperToLower(c2);
- }
+ cx = sqlite3Toupper(c);
+ c = sqlite3Tolower(c);
}else{
- while( c2 != 0 && c2 != c ){
- c2 = sqlite3Utf8Read(zString, &zString);
- }
+ cx = c;
+ }
+ while( (c2 = *(zString++))!=0 ){
+ if( c2!=c && c2!=cx ) continue;
+ if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1;
+ }
+ }else{
+ while( (c2 = Utf8Read(zString))!=0 ){
+ if( c2!=c ) continue;
+ if( patternCompare(zPattern,zString,pInfo,matchOther) ) return 1;
}
- if( c2==0 ) return 0;
- if( patternCompare(zPattern,zString,pInfo,esc) ) return 1;
}
return 0;
- }else if( !prevEscape && c==matchOne ){
- if( sqlite3Utf8Read(zString, &zString)==0 ){
- return 0;
- }
- }else if( c==matchSet ){
- u32 prior_c = 0;
- assert( esc==0 ); /* This only occurs for GLOB, not LIKE */
- seen = 0;
- invert = 0;
- c = sqlite3Utf8Read(zString, &zString);
- if( c==0 ) return 0;
- c2 = sqlite3Utf8Read(zPattern, &zPattern);
- if( c2=='^' ){
- invert = 1;
- c2 = sqlite3Utf8Read(zPattern, &zPattern);
- }
- if( c2==']' ){
- if( c==']' ) seen = 1;
- c2 = sqlite3Utf8Read(zPattern, &zPattern);
- }
- while( c2 && c2!=']' ){
- if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
- c2 = sqlite3Utf8Read(zPattern, &zPattern);
- if( c>=prior_c && c<=c2 ) seen = 1;
- prior_c = 0;
- }else{
- if( c==c2 ){
- seen = 1;
+ }
+ if( c==matchOther ){
+ if( pInfo->matchSet==0 ){
+ c = sqlite3Utf8Read(&zPattern);
+ if( c==0 ) return 0;
+ zEscaped = zPattern;
+ }else{
+ u32 prior_c = 0;
+ int seen = 0;
+ int invert = 0;
+ c = sqlite3Utf8Read(&zString);
+ if( c==0 ) return 0;
+ c2 = sqlite3Utf8Read(&zPattern);
+ if( c2=='^' ){
+ invert = 1;
+ c2 = sqlite3Utf8Read(&zPattern);
+ }
+ if( c2==']' ){
+ if( c==']' ) seen = 1;
+ c2 = sqlite3Utf8Read(&zPattern);
+ }
+ while( c2 && c2!=']' ){
+ if( c2=='-' && zPattern[0]!=']' && zPattern[0]!=0 && prior_c>0 ){
+ c2 = sqlite3Utf8Read(&zPattern);
+ if( c>=prior_c && c<=c2 ) seen = 1;
+ prior_c = 0;
+ }else{
+ if( c==c2 ){
+ seen = 1;
+ }
+ prior_c = c2;
}
- prior_c = c2;
+ c2 = sqlite3Utf8Read(&zPattern);
}
- c2 = sqlite3Utf8Read(zPattern, &zPattern);
- }
- if( c2==0 || (seen ^ invert)==0 ){
- return 0;
- }
- }else if( esc==c && !prevEscape ){
- prevEscape = 1;
- }else{
- c2 = sqlite3Utf8Read(zString, &zString);
- if( noCase ){
- GlogUpperToLower(c);
- GlogUpperToLower(c2);
- }
- if( c!=c2 ){
- return 0;
+ if( c2==0 || (seen ^ invert)==0 ){
+ return 0;
+ }
+ continue;
}
- prevEscape = 0;
}
+ c2 = Utf8Read(zString);
+ if( c==c2 ) continue;
+ if( noCase && sqlite3Tolower(c)==sqlite3Tolower(c2) && c<0x80 && c2<0x80 ){
+ continue;
+ }
+ if( c==matchOne && zPattern!=zEscaped && c2!=0 ) continue;
+ return 0;
}
return *zString==0;
}
/*
+** The sqlite3_strglob() interface.
+*/
+SQLITE_API int sqlite3_strglob(const char *zGlobPattern, const char *zString){
+ return patternCompare((u8*)zGlobPattern, (u8*)zString, &globInfo, '[')==0;
+}
+
+/*
+** The sqlite3_strlike() interface.
+*/
+SQLITE_API int sqlite3_strlike(const char *zPattern, const char *zStr, unsigned int esc){
+ return patternCompare((u8*)zPattern, (u8*)zStr, &likeInfoNorm, esc)==0;
+}
+
+/*
** Count the number of times that the LIKE operator (or GLOB which is
** just a variation of LIKE) gets called. This is used for testing
** only.
@@ -84735,10 +104892,22 @@ static void likeFunc(
sqlite3_value **argv
){
const unsigned char *zA, *zB;
- u32 escape = 0;
+ u32 escape;
int nPat;
sqlite3 *db = sqlite3_context_db_handle(context);
+ struct compareInfo *pInfo = sqlite3_user_data(context);
+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ if( sqlite3_value_type(argv[0])==SQLITE_BLOB
+ || sqlite3_value_type(argv[1])==SQLITE_BLOB
+ ){
+#ifdef SQLITE_TEST
+ sqlite3_like_count++;
+#endif
+ sqlite3_result_int(context, 0);
+ return;
+ }
+#endif
zB = sqlite3_value_text(argv[0]);
zA = sqlite3_value_text(argv[1]);
@@ -84765,14 +104934,14 @@ static void likeFunc(
"ESCAPE expression must be a single character", -1);
return;
}
- escape = sqlite3Utf8Read(zEsc, &zEsc);
+ escape = sqlite3Utf8Read(&zEsc);
+ }else{
+ escape = pInfo->matchSet;
}
if( zA && zB ){
- struct compareInfo *pInfo = sqlite3_user_data(context);
#ifdef SQLITE_TEST
sqlite3_like_count++;
#endif
-
sqlite3_result_int(context, patternCompare(zB, zA, pInfo, escape));
}
}
@@ -84894,10 +105063,6 @@ static const char hexdigits[] = {
};
/*
-** EXPERIMENTAL - This is not an official function. The interface may
-** change. This function may disappear. Do not write code that depends
-** on this function.
-**
** Implementation of the QUOTE() function. This function takes a single
** argument. If the argument is numeric, the return value is the same as
** the argument. If the argument is NULL, the return value is the string
@@ -84908,8 +105073,19 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
assert( argc==1 );
UNUSED_PARAMETER(argc);
switch( sqlite3_value_type(argv[0]) ){
- case SQLITE_INTEGER:
case SQLITE_FLOAT: {
+ double r1, r2;
+ char zBuf[50];
+ r1 = sqlite3_value_double(argv[0]);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
+ sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8);
+ if( r1!=r2 ){
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1);
+ }
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ break;
+ }
+ case SQLITE_INTEGER: {
sqlite3_result_value(context, argv[0]);
break;
}
@@ -84966,6 +105142,62 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}
/*
+** The unicode() function. Return the integer unicode code-point value
+** for the first character of the input string.
+*/
+static void unicodeFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const unsigned char *z = sqlite3_value_text(argv[0]);
+ (void)argc;
+ if( z && z[0] ) sqlite3_result_int(context, sqlite3Utf8Read(&z));
+}
+
+/*
+** The char() function takes zero or more arguments, each of which is
+** an integer. It constructs a string where each character of the string
+** is the unicode character for the corresponding integer argument.
+*/
+static void charFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ unsigned char *z, *zOut;
+ int i;
+ zOut = z = sqlite3_malloc64( argc*4+1 );
+ if( z==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ for(i=0; i<argc; i++){
+ sqlite3_int64 x;
+ unsigned c;
+ x = sqlite3_value_int64(argv[i]);
+ if( x<0 || x>0x10ffff ) x = 0xfffd;
+ c = (unsigned)(x & 0x1fffff);
+ if( c<0x00080 ){
+ *zOut++ = (u8)(c&0xFF);
+ }else if( c<0x00800 ){
+ *zOut++ = 0xC0 + (u8)((c>>6)&0x1F);
+ *zOut++ = 0x80 + (u8)(c & 0x3F);
+ }else if( c<0x10000 ){
+ *zOut++ = 0xE0 + (u8)((c>>12)&0x0F);
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F);
+ *zOut++ = 0x80 + (u8)(c & 0x3F);
+ }else{
+ *zOut++ = 0xF0 + (u8)((c>>18) & 0x07);
+ *zOut++ = 0x80 + (u8)((c>>12) & 0x3F);
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F);
+ *zOut++ = 0x80 + (u8)(c & 0x3F);
+ } \
+ }
+ sqlite3_result_text64(context, (char*)z, zOut-z, sqlite3_free, SQLITE_UTF8);
+}
+
+/*
** The hex() function. Interpret the argument as a blob. Return
** a hexadecimal rendering as text.
*/
@@ -85003,23 +105235,21 @@ static void zeroblobFunc(
sqlite3_value **argv
){
i64 n;
- sqlite3 *db = sqlite3_context_db_handle(context);
+ int rc;
assert( argc==1 );
UNUSED_PARAMETER(argc);
n = sqlite3_value_int64(argv[0]);
- testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH] );
- testcase( n==db->aLimit[SQLITE_LIMIT_LENGTH]+1 );
- if( n>db->aLimit[SQLITE_LIMIT_LENGTH] ){
- sqlite3_result_error_toobig(context);
- }else{
- sqlite3_result_zeroblob(context, (int)n); /* IMP: R-00293-64994 */
+ if( n<0 ) n = 0;
+ rc = sqlite3_result_zeroblob64(context, n); /* IMP: R-00293-64994 */
+ if( rc ){
+ sqlite3_result_error_code(context, rc);
}
}
/*
** The replace() function. Three arguments are all strings: call
** them A, B, and C. The result is also a string which is derived
-** from A by replacing every occurance of B with C. The match
+** from A by replacing every occurrence of B with C. The match
** must be exact. Collating sequences are not used.
*/
static void replaceFunc(
@@ -85083,7 +105313,7 @@ static void replaceFunc(
return;
}
zOld = zOut;
- zOut = sqlite3_realloc(zOut, (int)nOut);
+ zOut = sqlite3_realloc64(zOut, (int)nOut);
if( zOut==0 ){
sqlite3_result_error_nomem(context);
sqlite3_free(zOld);
@@ -85187,6 +105417,26 @@ static void trimFunc(
}
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+/*
+** The "unknown" function is automatically substituted in place of
+** any unrecognized function name when doing an EXPLAIN or EXPLAIN QUERY PLAN
+** when the SQLITE_ENABLE_UNKNOWN_FUNCTION compile-time option is used.
+** When the "sqlite3" command-line shell is built using this functionality,
+** that allows an EXPLAIN or EXPLAIN QUERY PLAN for complex queries
+** involving application-defined functions to be examined in a generic
+** sqlite3 shell.
+*/
+static void unknownFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ /* no-op */
+}
+#endif /*SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION*/
+
+
/* IMP: R-25361-16150 This function is omitted from SQLite by default. It
** is only available if the SQLITE_SOUNDEX compile-time option is used
** when SQLite is built.
@@ -85257,6 +105507,14 @@ static void loadExt(sqlite3_context *context, int argc, sqlite3_value **argv){
sqlite3 *db = sqlite3_context_db_handle(context);
char *zErrMsg = 0;
+ /* Disallow the load_extension() SQL function unless the SQLITE_LoadExtFunc
+ ** flag is set. See the sqlite3_enable_load_extension() API.
+ */
+ if( (db->flags & SQLITE_LoadExtFunc)==0 ){
+ sqlite3_result_error(context, "not authorized", -1);
+ return;
+ }
+
if( argc==2 ){
zProc = (const char *)sqlite3_value_text(argv[1]);
}else{
@@ -85387,11 +105645,12 @@ static void minmaxStep(
Mem *pBest;
UNUSED_PARAMETER(NotUsed);
- if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
pBest = (Mem *)sqlite3_aggregate_context(context, sizeof(*pBest));
if( !pBest ) return;
- if( pBest->flags ){
+ if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
+ if( pBest->flags ) sqlite3SkipAccumulatorLoad(context);
+ }else if( pBest->flags ){
int max;
int cmp;
CollSeq *pColl = sqlite3GetFuncCollSeq(context);
@@ -85407,8 +105666,11 @@ static void minmaxStep(
cmp = sqlite3MemCompare(pBest, pArg, pColl);
if( (max && cmp<0) || (!max && cmp>0) ){
sqlite3VdbeMemCopy(pBest, pArg);
+ }else{
+ sqlite3SkipAccumulatorLoad(context);
}
}else{
+ pBest->db = sqlite3_context_db_handle(context);
sqlite3VdbeMemCopy(pBest, pArg);
}
}
@@ -85416,7 +105678,7 @@ static void minMaxFinalize(sqlite3_context *context){
sqlite3_value *pRes;
pRes = (sqlite3_value *)sqlite3_aggregate_context(context, 0);
if( pRes ){
- if( ALWAYS(pRes->flags) ){
+ if( pRes->flags ){
sqlite3_result_value(context, pRes);
}
sqlite3VdbeMemRelease(pRes);
@@ -85441,8 +105703,7 @@ static void groupConcatStep(
if( pAccum ){
sqlite3 *db = sqlite3_context_db_handle(context);
- int firstTerm = pAccum->useMalloc==0;
- pAccum->useMalloc = 2;
+ int firstTerm = pAccum->mxAlloc==0;
pAccum->mxAlloc = db->aLimit[SQLITE_LIMIT_LENGTH];
if( !firstTerm ){
if( argc==2 ){
@@ -85452,20 +105713,20 @@ static void groupConcatStep(
zSep = ",";
nSep = 1;
}
- sqlite3StrAccumAppend(pAccum, zSep, nSep);
+ if( nSep ) sqlite3StrAccumAppend(pAccum, zSep, nSep);
}
zVal = (char*)sqlite3_value_text(argv[0]);
nVal = sqlite3_value_bytes(argv[0]);
- sqlite3StrAccumAppend(pAccum, zVal, nVal);
+ if( zVal ) sqlite3StrAccumAppend(pAccum, zVal, nVal);
}
}
static void groupConcatFinalize(sqlite3_context *context){
StrAccum *pAccum;
pAccum = sqlite3_aggregate_context(context, 0);
if( pAccum ){
- if( pAccum->tooBig ){
+ if( pAccum->accError==STRACCUM_TOOBIG ){
sqlite3_result_error_toobig(context);
- }else if( pAccum->mallocFailed ){
+ }else if( pAccum->accError==STRACCUM_NOMEM ){
sqlite3_result_error_nomem(context);
}else{
sqlite3_result_text(context, sqlite3StrAccumFinish(pAccum), -1,
@@ -85479,11 +105740,11 @@ static void groupConcatFinalize(sqlite3_context *context){
** of the built-in functions above are part of the global function set.
** This routine only deals with those that are not global.
*/
-SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
+SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3 *db){
int rc = sqlite3_overload_function(db, "MATCH", 2);
assert( rc==SQLITE_NOMEM || rc==SQLITE_OK );
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
}
@@ -85492,10 +105753,9 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(sqlite3 *db){
*/
static void setLikeOptFlag(sqlite3 *db, const char *zName, u8 flagVal){
FuncDef *pDef;
- pDef = sqlite3FindFunction(db, zName, sqlite3Strlen30(zName),
- 2, SQLITE_UTF8, 0);
+ pDef = sqlite3FindFunction(db, zName, 2, SQLITE_UTF8, 0);
if( ALWAYS(pDef) ){
- pDef->flags = flagVal;
+ pDef->funcFlags |= flagVal;
}
}
@@ -85526,6 +105786,11 @@ SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3 *db, int caseSensitive)
** then set aWc[0] through aWc[2] to the wildcard characters and
** return TRUE. If the function is not a LIKE-style function then
** return FALSE.
+**
+** *pIsNocase is set to true if uppercase and lowercase are equivalent for
+** the function (default for LIKE). If the function makes the distinction
+** between uppercase and lowercase (as does GLOB) then *pIsNocase is set to
+** false.
*/
SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocase, char *aWc){
FuncDef *pDef;
@@ -85536,10 +105801,8 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas
return 0;
}
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
- pDef = sqlite3FindFunction(db, pExpr->u.zToken,
- sqlite3Strlen30(pExpr->u.zToken),
- 2, SQLITE_UTF8, 0);
- if( NEVER(pDef==0) || (pDef->flags & SQLITE_FUNC_LIKE)==0 ){
+ pDef = sqlite3FindFunction(db, pExpr->u.zToken, 2, SQLITE_UTF8, 0);
+ if( NEVER(pDef==0) || (pDef->funcFlags & SQLITE_FUNC_LIKE)==0 ){
return 0;
}
@@ -85551,18 +105814,18 @@ SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3 *db, Expr *pExpr, int *pIsNocas
assert( (char*)&likeInfoAlt == (char*)&likeInfoAlt.matchAll );
assert( &((char*)&likeInfoAlt)[1] == (char*)&likeInfoAlt.matchOne );
assert( &((char*)&likeInfoAlt)[2] == (char*)&likeInfoAlt.matchSet );
- *pIsNocase = (pDef->flags & SQLITE_FUNC_CASE)==0;
+ *pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
return 1;
}
/*
-** All all of the FuncDef structures in the aBuiltinFunc[] array above
+** All of the FuncDef structures in the aBuiltinFunc[] array above
** to the global function hash table. This occurs at start-time (as
** a consequence of calling sqlite3_initialize()).
**
** After this routine runs
*/
-SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
+SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
/*
** The following array holds FuncDef structures for all of the functions
** defined in this file.
@@ -85570,8 +105833,27 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
** The array cannot be constant since changes are made to the
** FuncDef.pHash elements at start-time. The elements of this array
** are read-only after initialization is complete.
+ **
+ ** For peak efficiency, put the most frequently used function last.
*/
- static SQLITE_WSD FuncDef aBuiltinFunc[] = {
+ static FuncDef aBuiltinFunc[] = {
+#ifdef SQLITE_SOUNDEX
+ FUNCTION(soundex, 1, 0, 0, soundexFunc ),
+#endif
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ VFUNCTION(load_extension, 1, 0, 0, loadExt ),
+ VFUNCTION(load_extension, 2, 0, 0, loadExt ),
+#endif
+#if SQLITE_USER_AUTHENTICATION
+ FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
+#endif
+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
+ DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
+ DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
+#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
+ FUNCTION2(unlikely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ FUNCTION2(likelihood, 2, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
+ FUNCTION2(likely, 1, 0, 0, noopFunc, SQLITE_FUNC_UNLIKELY),
FUNCTION(ltrim, 1, 1, 0, trimFunc ),
FUNCTION(ltrim, 2, 1, 0, trimFunc ),
FUNCTION(rtrim, 1, 2, 0, trimFunc ),
@@ -85580,14 +105862,18 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
FUNCTION(trim, 2, 3, 0, trimFunc ),
FUNCTION(min, -1, 0, 1, minmaxFunc ),
FUNCTION(min, 0, 0, 1, 0 ),
- AGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize ),
+ AGGREGATE2(min, 1, 0, 1, minmaxStep, minMaxFinalize,
+ SQLITE_FUNC_MINMAX ),
FUNCTION(max, -1, 1, 1, minmaxFunc ),
FUNCTION(max, 0, 1, 1, 0 ),
- AGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize ),
- FUNCTION(typeof, 1, 0, 0, typeofFunc ),
- FUNCTION(length, 1, 0, 0, lengthFunc ),
- FUNCTION(substr, 2, 0, 0, substrFunc ),
- FUNCTION(substr, 3, 0, 0, substrFunc ),
+ AGGREGATE2(max, 1, 1, 1, minmaxStep, minMaxFinalize,
+ SQLITE_FUNC_MINMAX ),
+ FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
+ FUNCTION2(length, 1, 0, 0, lengthFunc, SQLITE_FUNC_LENGTH),
+ FUNCTION(instr, 2, 0, 0, instrFunc ),
+ FUNCTION(printf, -1, 0, 0, printfFunc ),
+ FUNCTION(unicode, 1, 0, 0, unicodeFunc ),
+ FUNCTION(char, -1, 0, 0, charFunc ),
FUNCTION(abs, 1, 0, 0, absFunc ),
#ifndef SQLITE_OMIT_FLOATING_POINT
FUNCTION(round, 1, 0, 0, roundFunc ),
@@ -85595,66 +105881,70 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
#endif
FUNCTION(upper, 1, 0, 0, upperFunc ),
FUNCTION(lower, 1, 0, 0, lowerFunc ),
- FUNCTION(coalesce, 1, 0, 0, 0 ),
- FUNCTION(coalesce, 0, 0, 0, 0 ),
-/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */
- {-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0,0},
FUNCTION(hex, 1, 0, 0, hexFunc ),
-/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */
- {2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0,0},
- FUNCTION(random, 0, 0, 0, randomFunc ),
- FUNCTION(randomblob, 1, 0, 0, randomBlob ),
+ FUNCTION2(ifnull, 2, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
+ VFUNCTION(random, 0, 0, 0, randomFunc ),
+ VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
- FUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
- FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
+ DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
+ DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
-#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
- FUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
- FUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
-#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
FUNCTION(quote, 1, 0, 0, quoteFunc ),
- FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
- FUNCTION(changes, 0, 0, 0, changes ),
- FUNCTION(total_changes, 0, 0, 0, total_changes ),
+ VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
+ VFUNCTION(changes, 0, 0, 0, changes ),
+ VFUNCTION(total_changes, 0, 0, 0, total_changes ),
FUNCTION(replace, 3, 0, 0, replaceFunc ),
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
- #ifdef SQLITE_SOUNDEX
- FUNCTION(soundex, 1, 0, 0, soundexFunc ),
- #endif
- #ifndef SQLITE_OMIT_LOAD_EXTENSION
- FUNCTION(load_extension, 1, 0, 0, loadExt ),
- FUNCTION(load_extension, 2, 0, 0, loadExt ),
- #endif
+ FUNCTION(substr, 2, 0, 0, substrFunc ),
+ FUNCTION(substr, 3, 0, 0, substrFunc ),
AGGREGATE(sum, 1, 0, 0, sumStep, sumFinalize ),
AGGREGATE(total, 1, 0, 0, sumStep, totalFinalize ),
AGGREGATE(avg, 1, 0, 0, sumStep, avgFinalize ),
- /* AGGREGATE(count, 0, 0, 0, countStep, countFinalize ), */
- {0,SQLITE_UTF8,SQLITE_FUNC_COUNT,0,0,0,countStep,countFinalize,"count",0,0},
+ AGGREGATE2(count, 0, 0, 0, countStep, countFinalize,
+ SQLITE_FUNC_COUNT ),
AGGREGATE(count, 1, 0, 0, countStep, countFinalize ),
AGGREGATE(group_concat, 1, 0, 0, groupConcatStep, groupConcatFinalize),
AGGREGATE(group_concat, 2, 0, 0, groupConcatStep, groupConcatFinalize),
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
- #ifdef SQLITE_CASE_SENSITIVE_LIKE
+#ifdef SQLITE_CASE_SENSITIVE_LIKE
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
- #else
+#else
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
- #endif
+#endif
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ FUNCTION(unknown, -1, 0, 0, unknownFunc ),
+#endif
+ FUNCTION(coalesce, 1, 0, 0, 0 ),
+ FUNCTION(coalesce, 0, 0, 0, 0 ),
+ FUNCTION2(coalesce, -1, 0, 0, noopFunc, SQLITE_FUNC_COALESCE),
};
-
- int i;
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
- FuncDef *aFunc = (FuncDef*)&GLOBAL(FuncDef, aBuiltinFunc);
-
- for(i=0; i<ArraySize(aBuiltinFunc); i++){
- sqlite3FuncDefInsert(pHash, &aFunc[i]);
- }
- sqlite3RegisterDateTimeFunctions();
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
+#if defined(SQLITE_ENABLE_STAT3) || defined(SQLITE_ENABLE_STAT4)
+ sqlite3AnalyzeFunctions();
+#endif
+ sqlite3RegisterDateTimeFunctions();
+ sqlite3InsertBuiltinFuncs(aBuiltinFunc, ArraySize(aBuiltinFunc));
+
+#if 0 /* Enable to print out how the built-in functions are hashed */
+ {
+ int i;
+ FuncDef *p;
+ for(i=0; i<SQLITE_FUNC_HASH_SZ; i++){
+ printf("FUNC-HASH %02d:", i);
+ for(p=sqlite3BuiltinFunctions.a[i]; p; p=p->u.pHash){
+ int n = sqlite3Strlen30(p->zName);
+ int h = p->zName[0] + n;
+ printf(" %s(%d)", p->zName, h);
+ }
+ printf("\n");
+ }
+ }
+#endif
}
/************** End of func.c ************************************************/
@@ -85672,6 +105962,7 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
** This file contains code used by the compiler to add foreign key
** support to compiled SQL statements.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_FOREIGN_KEY
#ifndef SQLITE_OMIT_TRIGGER
@@ -85681,8 +105972,9 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
** --------------------------
**
** Foreign keys in SQLite come in two flavours: deferred and immediate.
-** If an immediate foreign key constraint is violated, SQLITE_CONSTRAINT
-** is returned and the current statement transaction rolled back. If a
+** If an immediate foreign key constraint is violated,
+** SQLITE_CONSTRAINT_FOREIGNKEY is returned and the current
+** statement transaction rolled back. If a
** deferred foreign key constraint is violated, no action is taken
** immediately. However if the application attempts to commit the
** transaction before fixing the constraint violation, the attempt fails.
@@ -85746,7 +106038,8 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
** Immediate constraints are usually handled similarly. The only difference
** is that the counter used is stored as part of each individual statement
** object (struct Vdbe). If, after the statement has run, its immediate
-** constraint counter is greater than zero, it returns SQLITE_CONSTRAINT
+** constraint counter is greater than zero,
+** it returns SQLITE_CONSTRAINT_FOREIGNKEY
** and the statement transaction is rolled back. An exception is an INSERT
** statement that inserts a single row only (no triggers). In this case,
** instead of using a counter, an exception is thrown immediately if the
@@ -85802,7 +106095,7 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
** A foreign key constraint requires that the key columns in the parent
** table are collectively subject to a UNIQUE or PRIMARY KEY constraint.
** Given that pParent is the parent table for foreign key constraint pFKey,
-** search the schema a unique index on the parent key columns.
+** search the schema for a unique index on the parent key columns.
**
** If successful, zero is returned. If the parent key is an INTEGER PRIMARY
** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx
@@ -85831,14 +106124,14 @@ SQLITE_PRIVATE void sqlite3RegisterGlobalFunctions(void){
**
** 4) No parent key columns were provided explicitly as part of the
** foreign key definition, and the PRIMARY KEY of the parent table
-** consists of a a different number of columns to the child key in
+** consists of a different number of columns to the child key in
** the child table.
**
** then non-zero is returned, and a "foreign key mismatch" error loaded
** into pParse. If an OOM error occurs, non-zero is returned and the
** pParse->db->mallocFailed flag is set.
*/
-static int locateFkeyIndex(
+SQLITE_PRIVATE int sqlite3FkLocateIndex(
Parse *pParse, /* Parse context to store any error in */
Table *pParent, /* Parent table of FK constraint pFKey */
FKey *pFKey, /* Foreign key to find index for */
@@ -85877,13 +106170,13 @@ static int locateFkeyIndex(
}
}else if( paiCol ){
assert( nCol>1 );
- aiCol = (int *)sqlite3DbMallocRaw(pParse->db, nCol*sizeof(int));
+ aiCol = (int *)sqlite3DbMallocRawNN(pParse->db, nCol*sizeof(int));
if( !aiCol ) return 1;
*paiCol = aiCol;
}
for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){
+ if( pIdx->nKeyCol==nCol && IsUniqueIndex(pIdx) ){
/* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number
** of columns. If each indexed column corresponds to a foreign key
** column of pFKey, then this index is a winner. */
@@ -85891,8 +106184,8 @@ static int locateFkeyIndex(
if( zKey==0 ){
/* If zKey is NULL, then this foreign key is implicitly mapped to
** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be
- ** identified by the test (Index.autoIndex==2). */
- if( pIdx->autoIndex==2 ){
+ ** identified by the test. */
+ if( IsPrimaryKeyIndex(pIdx) ){
if( aiCol ){
int i;
for(i=0; i<nCol; i++) aiCol[i] = pFKey->aCol[i].iFrom;
@@ -85906,17 +106199,17 @@ static int locateFkeyIndex(
** the default collation sequences for each column. */
int i, j;
for(i=0; i<nCol; i++){
- int iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
- char *zDfltColl; /* Def. collation for column */
+ i16 iCol = pIdx->aiColumn[i]; /* Index of column in parent tbl */
+ const char *zDfltColl; /* Def. collation for column */
char *zIdxCol; /* Name of indexed column */
+ if( iCol<0 ) break; /* No foreign keys against expression indexes */
+
/* If the index uses a collation sequence that is different from
** the default collation sequence for the column, this index is
** unusable. Bail out early in this case. */
zDfltColl = pParent->aCol[iCol].zColl;
- if( !zDfltColl ){
- zDfltColl = "BINARY";
- }
+ if( !zDfltColl ) zDfltColl = sqlite3StrBINARY;
if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break;
zIdxCol = pParent->aCol[iCol].zName;
@@ -85935,7 +106228,9 @@ static int locateFkeyIndex(
if( !pIdx ){
if( !pParse->disableTriggers ){
- sqlite3ErrorMsg(pParse, "foreign key mismatch");
+ sqlite3ErrorMsg(pParse,
+ "foreign key mismatch - \"%w\" referencing \"%w\"",
+ pFKey->pFrom->zName, pFKey->zTo);
}
sqlite3DbFree(pParse->db, aiCol);
return 1;
@@ -85996,10 +106291,11 @@ static void fkLookupParent(
** search for a matching row in the parent table. */
if( nIncr<0 ){
sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk);
+ VdbeCoverage(v);
}
for(i=0; i<pFKey->nCol; i++){
int iReg = aiCol[i] + regData + 1;
- sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk);
+ sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); VdbeCoverage(v);
}
if( isIgnore==0 ){
@@ -86016,18 +106312,20 @@ static void fkLookupParent(
** will have INTEGER affinity applied to it, which may not be correct. */
sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp);
iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0);
+ VdbeCoverage(v);
/* If the parent table is the same as the child table, and we are about
** to increment the constraint-counter (i.e. this is an INSERT operation),
** then check if the row being inserted matches itself. If so, do not
** increment the constraint-counter. */
if( pTab==pFKey->pFrom && nIncr==1 ){
- sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp);
+ sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
}
sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, iOk);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
sqlite3VdbeJumpHere(v, iMustBeInt);
sqlite3ReleaseTempReg(pParse, regTemp);
@@ -86035,10 +106333,9 @@ static void fkLookupParent(
int nCol = pFKey->nCol;
int regTemp = sqlite3GetTempRange(pParse, nCol);
int regRec = sqlite3GetTempReg(pParse);
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb);
- sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
for(i=0; i<nCol; i++){
sqlite3VdbeAddOp2(v, OP_Copy, aiCol[i]+1+regData, regTemp+i);
}
@@ -86058,38 +106355,41 @@ static void fkLookupParent(
for(i=0; i<nCol; i++){
int iChild = aiCol[i]+1+regData;
int iParent = pIdx->aiColumn[i]+1+regData;
+ assert( pIdx->aiColumn[i]>=0 );
assert( aiCol[i]!=pTab->iPKey );
if( pIdx->aiColumn[i]==pTab->iPKey ){
/* The parent key is a composite key that includes the IPK column */
iParent = regData;
}
- sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent);
+ sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); VdbeCoverage(v);
sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
}
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk);
+ sqlite3VdbeGoto(v, iOk);
}
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec);
- sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v,pIdx), P4_TRANSIENT);
- sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regTemp, nCol, regRec,
+ sqlite3IndexAffinityStr(pParse->db,pIdx), nCol);
+ sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempRange(pParse, regTemp, nCol);
}
}
- if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
+ if( !pFKey->isDeferred && !(pParse->db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel
+ && !pParse->isMultiWrite
+ ){
/* Special case: If this is an INSERT statement that will insert exactly
** one row into the table, raise a constraint immediately instead of
** incrementing a counter. This is necessary as the VM code is being
** generated for will not open a statement transaction. */
assert( nIncr==1 );
- sqlite3HaltConstraint(
- pParse, OE_Abort, "foreign key constraint failed", P4_STATIC
- );
+ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
+ OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
}else{
if( nIncr>0 && pFKey->isDeferred==0 ){
- sqlite3ParseToplevel(pParse)->mayAbort = 1;
+ sqlite3MayAbort(pParse);
}
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
}
@@ -86098,6 +106398,62 @@ static void fkLookupParent(
sqlite3VdbeAddOp1(v, OP_Close, iCur);
}
+
+/*
+** Return an Expr object that refers to a memory register corresponding
+** to column iCol of table pTab.
+**
+** regBase is the first of an array of register that contains the data
+** for pTab. regBase itself holds the rowid. regBase+1 holds the first
+** column. regBase+2 holds the second column, and so forth.
+*/
+static Expr *exprTableRegister(
+ Parse *pParse, /* Parsing and code generating context */
+ Table *pTab, /* The table whose content is at r[regBase]... */
+ int regBase, /* Contents of table pTab */
+ i16 iCol /* Which column of pTab is desired */
+){
+ Expr *pExpr;
+ Column *pCol;
+ const char *zColl;
+ sqlite3 *db = pParse->db;
+
+ pExpr = sqlite3Expr(db, TK_REGISTER, 0);
+ if( pExpr ){
+ if( iCol>=0 && iCol!=pTab->iPKey ){
+ pCol = &pTab->aCol[iCol];
+ pExpr->iTable = regBase + iCol + 1;
+ pExpr->affinity = pCol->affinity;
+ zColl = pCol->zColl;
+ if( zColl==0 ) zColl = db->pDfltColl->zName;
+ pExpr = sqlite3ExprAddCollateString(pParse, pExpr, zColl);
+ }else{
+ pExpr->iTable = regBase;
+ pExpr->affinity = SQLITE_AFF_INTEGER;
+ }
+ }
+ return pExpr;
+}
+
+/*
+** Return an Expr object that refers to column iCol of table pTab which
+** has cursor iCur.
+*/
+static Expr *exprTableColumn(
+ sqlite3 *db, /* The database connection */
+ Table *pTab, /* The table whose column is desired */
+ int iCursor, /* The open cursor on the table */
+ i16 iCol /* The column that is wanted */
+){
+ Expr *pExpr = sqlite3Expr(db, TK_COLUMN, 0);
+ if( pExpr ){
+ pExpr->pTab = pTab;
+ pExpr->iTable = iCursor;
+ pExpr->iColumn = iCol;
+ }
+ return pExpr;
+}
+
/*
** This function is called to generate code executed when a row is deleted
** from the parent table of foreign key constraint pFKey and, if pFKey is
@@ -86105,6 +106461,10 @@ static void fkLookupParent(
** code for an SQL UPDATE operation, this function may be called twice -
** once to "delete" the old row and once to "insert" the new row.
**
+** Parameter nIncr is passed -1 when inserting a row (as this may decrease
+** the number of FK violations in the db) or +1 when deleting one (as this
+** may increase the number of FK constraint problems).
+**
** The code generated by this function scans through the rows in the child
** table that correspond to the parent table row being deleted or inserted.
** For each child row found, one of the following actions is taken:
@@ -86113,13 +106473,13 @@ static void fkLookupParent(
** --------------------------------------------------------------------------
** DELETE immediate Increment the "immediate constraint counter".
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "foreign key constraint failed" exception.
+** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT immediate Decrement the "immediate constraint counter".
**
** DELETE deferred Increment the "deferred constraint counter".
** Or, if the ON (UPDATE|DELETE) action is RESTRICT,
-** throw a "foreign key constraint failed" exception.
+** throw a "FOREIGN KEY constraint failed" exception.
**
** INSERT deferred Decrement the "deferred constraint counter".
**
@@ -86128,12 +106488,12 @@ static void fkLookupParent(
*/
static void fkScanChildren(
Parse *pParse, /* Parse context */
- SrcList *pSrc, /* SrcList containing the table to scan */
- Table *pTab,
- Index *pIdx, /* Foreign key index */
- FKey *pFKey, /* Foreign key relationship */
+ SrcList *pSrc, /* The child table to be scanned */
+ Table *pTab, /* The parent table */
+ Index *pIdx, /* Index on parent covering the foreign key */
+ FKey *pFKey, /* The foreign key linking pSrc to pTab */
int *aiCol, /* Map from pIdx cols to child table cols */
- int regData, /* Referenced table data starts here */
+ int regData, /* Parent row data starts here */
int nIncr /* Amount to increment deferred counter by */
){
sqlite3 *db = pParse->db; /* Database handle */
@@ -86144,10 +106504,14 @@ static void fkScanChildren(
int iFkIfZero = 0; /* Address of OP_FkIfZero */
Vdbe *v = sqlite3GetVdbe(pParse);
- assert( !pIdx || pIdx->pTable==pTab );
+ assert( pIdx==0 || pIdx->pTable==pTab );
+ assert( pIdx==0 || pIdx->nKeyCol==pFKey->nCol );
+ assert( pIdx!=0 || pFKey->nCol==1 );
+ assert( pIdx!=0 || HasRowid(pTab) );
if( nIncr<0 ){
iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0);
+ VdbeCoverage(v);
}
/* Create an Expr object representing an SQL expression like:
@@ -86162,26 +106526,11 @@ static void fkScanChildren(
Expr *pLeft; /* Value from parent table row */
Expr *pRight; /* Column ref to child table */
Expr *pEq; /* Expression (pLeft = pRight) */
- int iCol; /* Index of column in child table */
+ i16 iCol; /* Index of column in child table */
const char *zCol; /* Name of column in child table */
- pLeft = sqlite3Expr(db, TK_REGISTER, 0);
- if( pLeft ){
- /* Set the collation sequence and affinity of the LHS of each TK_EQ
- ** expression to the parent key column defaults. */
- if( pIdx ){
- Column *pCol;
- iCol = pIdx->aiColumn[i];
- pCol = &pTab->aCol[iCol];
- if( pTab->iPKey==iCol ) iCol = -1;
- pLeft->iTable = regData+iCol+1;
- pLeft->affinity = pCol->affinity;
- pLeft->pColl = sqlite3LocateCollSeq(pParse, pCol->zColl);
- }else{
- pLeft->iTable = regData;
- pLeft->affinity = SQLITE_AFF_INTEGER;
- }
- }
+ iCol = pIdx ? pIdx->aiColumn[i] : -1;
+ pLeft = exprTableRegister(pParse, pTab, regData, iCol);
iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iCol>=0 );
zCol = pFKey->pFrom->aCol[iCol].zName;
@@ -86190,24 +106539,40 @@ static void fkScanChildren(
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
}
- /* If the child table is the same as the parent table, and this scan
- ** is taking place as part of a DELETE operation (operation D.2), omit the
- ** row being deleted from the scan by adding ($rowid != rowid) to the WHERE
- ** clause, where $rowid is the rowid of the row being deleted. */
+ /* If the child table is the same as the parent table, then add terms
+ ** to the WHERE clause that prevent this entry from being scanned.
+ ** The added WHERE clause terms are like this:
+ **
+ ** $current_rowid!=rowid
+ ** NOT( $current_a==a AND $current_b==b AND ... )
+ **
+ ** The first form is used for rowid tables. The second form is used
+ ** for WITHOUT ROWID tables. In the second form, the primary key is
+ ** (a,b,...)
+ */
if( pTab==pFKey->pFrom && nIncr>0 ){
- Expr *pEq; /* Expression (pLeft = pRight) */
+ Expr *pNe; /* Expression (pLeft != pRight) */
Expr *pLeft; /* Value from parent table row */
Expr *pRight; /* Column ref to child table */
- pLeft = sqlite3Expr(db, TK_REGISTER, 0);
- pRight = sqlite3Expr(db, TK_COLUMN, 0);
- if( pLeft && pRight ){
- pLeft->iTable = regData;
- pLeft->affinity = SQLITE_AFF_INTEGER;
- pRight->iTable = pSrc->a[0].iCursor;
- pRight->iColumn = -1;
- }
- pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
- pWhere = sqlite3ExprAnd(db, pWhere, pEq);
+ if( HasRowid(pTab) ){
+ pLeft = exprTableRegister(pParse, pTab, regData, -1);
+ pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, -1);
+ pNe = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0);
+ }else{
+ Expr *pEq, *pAll = 0;
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pIdx!=0 );
+ for(i=0; i<pPk->nKeyCol; i++){
+ i16 iCol = pIdx->aiColumn[i];
+ assert( iCol>=0 );
+ pLeft = exprTableRegister(pParse, pTab, regData, iCol);
+ pRight = exprTableColumn(db, pTab, pSrc->a[0].iCursor, iCol);
+ pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0);
+ pAll = sqlite3ExprAnd(db, pAll, pEq);
+ }
+ pNe = sqlite3PExpr(pParse, TK_NOT, pAll, 0, 0);
+ }
+ pWhere = sqlite3ExprAnd(db, pWhere, pNe);
}
/* Resolve the references in the WHERE clause. */
@@ -86217,13 +106582,9 @@ static void fkScanChildren(
sqlite3ResolveExprNames(&sNameContext, pWhere);
/* Create VDBE to loop through the entries in pSrc that match the WHERE
- ** clause. If the constraint is not deferred, throw an exception for
- ** each row found. Otherwise, for deferred constraints, increment the
- ** deferred constraint counter by nIncr for each row selected. */
- pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0);
- if( nIncr>0 && pFKey->isDeferred==0 ){
- sqlite3ParseToplevel(pParse)->mayAbort = 1;
- }
+ ** clause. For each row found, increment either the deferred or immediate
+ ** foreign key constraint counter. */
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0, 0, 0);
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr);
if( pWInfo ){
sqlite3WhereEnd(pWInfo);
@@ -86237,8 +106598,8 @@ static void fkScanChildren(
}
/*
-** This function returns a pointer to the head of a linked list of FK
-** constraints for which table pTab is the parent table. For example,
+** This function returns a linked list of FKey objects (connected by
+** FKey.pNextTo) holding all children of table pTab. For example,
** given the following schema:
**
** CREATE TABLE t1(a PRIMARY KEY);
@@ -86251,8 +106612,7 @@ static void fkScanChildren(
** table).
*/
SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *pTab){
- int nName = sqlite3Strlen30(pTab->zName);
- return (FKey *)sqlite3HashFind(&pTab->pSchema->fkeyHash, pTab->zName, nName);
+ return (FKey *)sqlite3HashFind(&pTab->pSchema->fkeyHash, pTab->zName);
}
/*
@@ -86306,11 +106666,11 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
** when this statement is run. */
FKey *p;
for(p=pTab->pFKey; p; p=p->pNextFrom){
- if( p->isDeferred ) break;
+ if( p->isDeferred || (db->flags & SQLITE_DeferFKs) ) break;
}
if( !p ) return;
iSkip = sqlite3VdbeMakeLabel(v);
- sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip);
+ sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); VdbeCoverage(v);
}
pParse->disableTriggers = 1;
@@ -86320,11 +106680,18 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
/* If the DELETE has generated immediate foreign key constraint
** violations, halt the VDBE and return an error at this point, before
** any modifications to the schema are made. This is because statement
- ** transactions are not able to rollback schema changes. */
- sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
- sqlite3HaltConstraint(
- pParse, OE_Abort, "foreign key constraint failed", P4_STATIC
- );
+ ** transactions are not able to rollback schema changes.
+ **
+ ** If the SQLITE_DeferFKs flag is set, then this is not required, as
+ ** the statement transaction will not be rolled back even if FK
+ ** constraints are violated.
+ */
+ if( (db->flags & SQLITE_DeferFKs)==0 ){
+ sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_FOREIGNKEY,
+ OE_Abort, 0, P4_STATIC, P5_ConstraintFK);
+ }
if( iSkip ){
sqlite3VdbeResolveLabel(v, iSkip);
@@ -86332,6 +106699,88 @@ SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTa
}
}
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the child table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** child key for FK constraint *p are modified.
+*/
+static int fkChildIsModified(
+ Table *pTab, /* Table being updated */
+ FKey *p, /* Foreign key for which pTab is the child */
+ int *aChange, /* Array indicating modified columns */
+ int bChngRowid /* True if rowid is modified by this update */
+){
+ int i;
+ for(i=0; i<p->nCol; i++){
+ int iChildKey = p->aCol[i].iFrom;
+ if( aChange[iChildKey]>=0 ) return 1;
+ if( iChildKey==pTab->iPKey && bChngRowid ) return 1;
+ }
+ return 0;
+}
+
+/*
+** The second argument points to an FKey object representing a foreign key
+** for which pTab is the parent table. An UPDATE statement against pTab
+** is currently being processed. For each column of the table that is
+** actually updated, the corresponding element in the aChange[] array
+** is zero or greater (if a column is unmodified the corresponding element
+** is set to -1). If the rowid column is modified by the UPDATE statement
+** the bChngRowid argument is non-zero.
+**
+** This function returns true if any of the columns that are part of the
+** parent key for FK constraint *p are modified.
+*/
+static int fkParentIsModified(
+ Table *pTab,
+ FKey *p,
+ int *aChange,
+ int bChngRowid
+){
+ int i;
+ for(i=0; i<p->nCol; i++){
+ char *zKey = p->aCol[i].zCol;
+ int iKey;
+ for(iKey=0; iKey<pTab->nCol; iKey++){
+ if( aChange[iKey]>=0 || (iKey==pTab->iPKey && bChngRowid) ){
+ Column *pCol = &pTab->aCol[iKey];
+ if( zKey ){
+ if( 0==sqlite3StrICmp(pCol->zName, zKey) ) return 1;
+ }else if( pCol->colFlags & COLFLAG_PRIMKEY ){
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+** Return true if the parser passed as the first argument is being
+** used to code a trigger that is really a "SET NULL" action belonging
+** to trigger pFKey.
+*/
+static int isSetNullAction(Parse *pParse, FKey *pFKey){
+ Parse *pTop = sqlite3ParseToplevel(pParse);
+ if( pTop->pTriggerPrg ){
+ Trigger *p = pTop->pTriggerPrg->pTrigger;
+ if( (p==pFKey->apTrigger[0] && pFKey->aAction[0]==OE_SetNull)
+ || (p==pFKey->apTrigger[1] && pFKey->aAction[1]==OE_SetNull)
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
/*
** This function is called when inserting, deleting or updating a row of
** table pTab to generate VDBE code to perform foreign key constraint
@@ -86356,7 +106805,9 @@ SQLITE_PRIVATE void sqlite3FkCheck(
Parse *pParse, /* Parse context */
Table *pTab, /* Row is being deleted from this table */
int regOld, /* Previous row data is stored here */
- int regNew /* New row data is stored here */
+ int regNew, /* New row data is stored here */
+ int *aChange, /* Array indicating UPDATEd columns (or 0) */
+ int bChngRowid /* True if rowid is UPDATEd */
){
sqlite3 *db = pParse->db; /* Database handle */
FKey *pFKey; /* Used to iterate through FKs */
@@ -86371,7 +106822,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
if( (db->flags&SQLITE_ForeignKeys)==0 ) return;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
- zDb = db->aDb[iDb].zName;
+ zDb = db->aDb[iDb].zDbSName;
/* Loop through all the foreign key constraints for which pTab is the
** child table (the table that the foreign key definition is part of). */
@@ -86382,7 +106833,14 @@ SQLITE_PRIVATE void sqlite3FkCheck(
int *aiCol;
int iCol;
int i;
- int isIgnore = 0;
+ int bIgnore = 0;
+
+ if( aChange
+ && sqlite3_stricmp(pTab->zName, pFKey->zTo)!=0
+ && fkChildIsModified(pTab, pFKey, aChange, bChngRowid)==0
+ ){
+ continue;
+ }
/* Find the parent table of this foreign key. Also find a unique index
** on the parent key columns in the parent table. If either of these
@@ -86393,7 +106851,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
}else{
pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb);
}
- if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
+ if( !pTo || sqlite3FkLocateIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){
assert( isIgnoreErrors==0 || (regOld!=0 && regNew==0) );
if( !isIgnoreErrors || db->mallocFailed ) return;
if( pTo==0 ){
@@ -86408,7 +106866,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
int iJump = sqlite3VdbeCurrentAddr(v) + pFKey->nCol + 1;
for(i=0; i<pFKey->nCol; i++){
int iReg = pFKey->aCol[i].iFrom + regOld + 1;
- sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump);
+ sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iJump); VdbeCoverage(v);
}
sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, -1);
}
@@ -86426,6 +106884,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
if( aiCol[i]==pTab->iPKey ){
aiCol[i] = -1;
}
+ assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
#ifndef SQLITE_OMIT_AUTHORIZATION
/* Request permission to read the parent key columns. If the
** authorization callback returns SQLITE_IGNORE, behave as if any
@@ -86434,7 +106893,7 @@ SQLITE_PRIVATE void sqlite3FkCheck(
int rcauth;
char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName;
rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb);
- isIgnore = (rcauth==SQLITE_IGNORE);
+ bIgnore = (rcauth==SQLITE_IGNORE);
}
#endif
}
@@ -86449,39 +106908,51 @@ SQLITE_PRIVATE void sqlite3FkCheck(
/* A row is being removed from the child table. Search for the parent.
** If the parent does not exist, removing the child row resolves an
** outstanding foreign key constraint violation. */
- fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore);
+ fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1, bIgnore);
}
- if( regNew!=0 ){
+ if( regNew!=0 && !isSetNullAction(pParse, pFKey) ){
/* A row is being added to the child table. If a parent row cannot
- ** be found, adding the child row has violated the FK constraint. */
- fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore);
+ ** be found, adding the child row has violated the FK constraint.
+ **
+ ** If this operation is being performed as part of a trigger program
+ ** that is actually a "SET NULL" action belonging to this very
+ ** foreign key, then omit this scan altogether. As all child key
+ ** values are guaranteed to be NULL, it is not possible for adding
+ ** this row to cause an FK violation. */
+ fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1, bIgnore);
}
sqlite3DbFree(db, aiFree);
}
- /* Loop through all the foreign key constraints that refer to this table */
+ /* Loop through all the foreign key constraints that refer to this table.
+ ** (the "child" constraints) */
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
Index *pIdx = 0; /* Foreign key index for pFKey */
SrcList *pSrc;
int *aiCol = 0;
- if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){
+ if( aChange && fkParentIsModified(pTab, pFKey, aChange, bChngRowid)==0 ){
+ continue;
+ }
+
+ if( !pFKey->isDeferred && !(db->flags & SQLITE_DeferFKs)
+ && !pParse->pToplevel && !pParse->isMultiWrite
+ ){
assert( regOld==0 && regNew!=0 );
- /* Inserting a single row into a parent table cannot cause an immediate
- ** foreign key violation. So do nothing in this case. */
+ /* Inserting a single row into a parent table cannot cause (or fix)
+ ** an immediate foreign key violation. So do nothing in this case. */
continue;
}
- if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
+ if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){
if( !isIgnoreErrors || db->mallocFailed ) return;
continue;
}
assert( aiCol || pFKey->nCol==1 );
- /* Create a SrcList structure containing a single table (the table
- ** the foreign key that refers to this table is attached to). This
- ** is required for the sqlite3WhereXXX() interface. */
+ /* Create a SrcList structure containing the child table. We need the
+ ** child table as a SrcList for sqlite3WhereBegin() */
pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
if( pSrc ){
struct SrcList_item *pItem = pSrc->a;
@@ -86494,13 +106965,28 @@ SQLITE_PRIVATE void sqlite3FkCheck(
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1);
}
if( regOld!=0 ){
- /* If there is a RESTRICT action configured for the current operation
- ** on the parent table of this FK, then throw an exception
- ** immediately if the FK constraint is violated, even if this is a
- ** deferred trigger. That's what RESTRICT means. To defer checking
- ** the constraint, the FK should specify NO ACTION (represented
- ** using OE_None). NO ACTION is the default. */
+ int eAction = pFKey->aAction[aChange!=0];
fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1);
+ /* If this is a deferred FK constraint, or a CASCADE or SET NULL
+ ** action applies, then any foreign key violations caused by
+ ** removing the parent key will be rectified by the action trigger.
+ ** So do not set the "may-abort" flag in this case.
+ **
+ ** Note 1: If the FK is declared "ON UPDATE CASCADE", then the
+ ** may-abort flag will eventually be set on this statement anyway
+ ** (when this function is called as part of processing the UPDATE
+ ** within the action trigger).
+ **
+ ** Note 2: At first glance it may seem like SQLite could simply omit
+ ** all OP_FkCounter related scans when either CASCADE or SET NULL
+ ** applies. The trouble starts if the CASCADE or SET NULL action
+ ** trigger causes other triggers or action rules attached to the
+ ** child table to fire. In these cases the fk constraint counters
+ ** might be set incorrectly if any OP_FkCounter related scans are
+ ** omitted. */
+ if( !pFKey->isDeferred && eAction!=OE_Cascade && eAction!=OE_SetNull ){
+ sqlite3MayAbort(pParse);
+ }
}
pItem->zName = 0;
sqlite3SrcListDelete(db, pSrc);
@@ -86528,15 +107014,19 @@ SQLITE_PRIVATE u32 sqlite3FkOldmask(
}
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
Index *pIdx = 0;
- locateFkeyIndex(pParse, pTab, p, &pIdx, 0);
+ sqlite3FkLocateIndex(pParse, pTab, p, &pIdx, 0);
if( pIdx ){
- for(i=0; i<pIdx->nColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]);
+ for(i=0; i<pIdx->nKeyCol; i++){
+ assert( pIdx->aiColumn[i]>=0 );
+ mask |= COLUMN_MASK(pIdx->aiColumn[i]);
+ }
}
}
}
return mask;
}
+
/*
** This function is called before generating code to update or delete a
** row contained in table pTab. If the operation is a DELETE, then
@@ -86566,31 +107056,16 @@ SQLITE_PRIVATE int sqlite3FkRequired(
}else{
/* This is an UPDATE. Foreign key processing is only required if the
** operation modifies one or more child or parent key columns. */
- int i;
FKey *p;
/* Check if any child key columns are being modified. */
for(p=pTab->pFKey; p; p=p->pNextFrom){
- for(i=0; i<p->nCol; i++){
- int iChildKey = p->aCol[i].iFrom;
- if( aChange[iChildKey]>=0 ) return 1;
- if( iChildKey==pTab->iPKey && chngRowid ) return 1;
- }
+ if( fkChildIsModified(pTab, p, aChange, chngRowid) ) return 1;
}
/* Check if any parent key columns are being modified. */
for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){
- for(i=0; i<p->nCol; i++){
- char *zKey = p->aCol[i].zCol;
- int iKey;
- for(iKey=0; iKey<pTab->nCol; iKey++){
- Column *pCol = &pTab->aCol[iKey];
- if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey) : pCol->isPrimKey) ){
- if( aChange[iKey]>=0 ) return 1;
- if( iKey==pTab->iPKey && chngRowid ) return 1;
- }
- }
- }
+ if( fkParentIsModified(pTab, p, aChange, chngRowid) ) return 1;
}
}
}
@@ -86638,10 +107113,12 @@ static Trigger *fkActionTrigger(
int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */
action = pFKey->aAction[iAction];
+ if( action==OE_Restrict && (db->flags & SQLITE_DeferFKs) ){
+ return 0;
+ }
pTrigger = pFKey->apTrigger[iAction];
if( action!=OE_None && !pTrigger ){
- u8 enableLookaside; /* Copy of db->lookaside.bEnabled */
char const *zFrom; /* Name of child table */
int nFrom; /* Length in bytes of zFrom */
Index *pIdx = 0; /* Parent key index for this FK */
@@ -86653,7 +107130,7 @@ static Trigger *fkActionTrigger(
int i; /* Iterator variable */
Expr *pWhen = 0; /* WHEN clause for the trigger */
- if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
+ if( sqlite3FkLocateIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0;
assert( aiCol || pFKey->nCol==1 );
for(i=0; i<pFKey->nCol; i++){
@@ -86666,11 +107143,11 @@ static Trigger *fkActionTrigger(
iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom;
assert( iFromCol>=0 );
- tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid";
- tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName;
-
- tToCol.n = sqlite3Strlen30(tToCol.z);
- tFromCol.n = sqlite3Strlen30(tFromCol.z);
+ assert( pIdx!=0 || (pTab->iPKey>=0 && pTab->iPKey<pTab->nCol) );
+ assert( pIdx==0 || pIdx->aiColumn[i]>=0 );
+ sqlite3TokenInit(&tToCol,
+ pTab->aCol[pIdx ? pIdx->aiColumn[i] : pTab->iPKey].zName);
+ sqlite3TokenInit(&tFromCol, pFKey->pFrom->aCol[iFromCol].zName);
/* Create the expression "OLD.zToCol = zFromCol". It is important
** that the "OLD.zToCol" term is on the LHS of the = operator, so
@@ -86678,10 +107155,10 @@ static Trigger *fkActionTrigger(
** parent table are used for the comparison. */
pEq = sqlite3PExpr(pParse, TK_EQ,
sqlite3PExpr(pParse, TK_DOT,
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld),
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol)
+ sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
+ sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)
, 0),
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol)
+ sqlite3ExprAlloc(db, TK_ID, &tFromCol, 0)
, 0);
pWhere = sqlite3ExprAnd(db, pWhere, pEq);
@@ -86693,12 +107170,12 @@ static Trigger *fkActionTrigger(
if( pChanges ){
pEq = sqlite3PExpr(pParse, TK_IS,
sqlite3PExpr(pParse, TK_DOT,
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld),
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol),
+ sqlite3ExprAlloc(db, TK_ID, &tOld, 0),
+ sqlite3ExprAlloc(db, TK_ID, &tToCol, 0),
0),
sqlite3PExpr(pParse, TK_DOT,
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew),
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol),
+ sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
+ sqlite3ExprAlloc(db, TK_ID, &tToCol, 0),
0),
0);
pWhen = sqlite3ExprAnd(db, pWhen, pEq);
@@ -86708,18 +107185,18 @@ static Trigger *fkActionTrigger(
Expr *pNew;
if( action==OE_Cascade ){
pNew = sqlite3PExpr(pParse, TK_DOT,
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew),
- sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol)
+ sqlite3ExprAlloc(db, TK_ID, &tNew, 0),
+ sqlite3ExprAlloc(db, TK_ID, &tToCol, 0)
, 0);
}else if( action==OE_SetDflt ){
Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt;
if( pDflt ){
pNew = sqlite3ExprDup(db, pDflt, 0);
}else{
- pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
+ pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
}
}else{
- pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0);
+ pNew = sqlite3ExprAlloc(db, TK_NULL, 0, 0);
}
pList = sqlite3ExprListAppend(pParse, pList, pNew);
sqlite3ExprListSetName(pParse, pList, &tFromCol, 0);
@@ -86736,7 +107213,7 @@ static Trigger *fkActionTrigger(
tFrom.z = zFrom;
tFrom.n = nFrom;
- pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed");
+ pRaise = sqlite3Expr(db, TK_RAISE, "FOREIGN KEY constraint failed");
if( pRaise ){
pRaise->affinity = OE_Abort;
}
@@ -86750,19 +107227,17 @@ static Trigger *fkActionTrigger(
}
/* Disable lookaside memory allocation */
- enableLookaside = db->lookaside.bEnabled;
- db->lookaside.bEnabled = 0;
+ db->lookaside.bDisable++;
pTrigger = (Trigger *)sqlite3DbMallocZero(db,
sizeof(Trigger) + /* struct Trigger */
sizeof(TriggerStep) + /* Single step in trigger program */
- nFrom + 1 /* Space for pStep->target.z */
+ nFrom + 1 /* Space for pStep->zTarget */
);
if( pTrigger ){
pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1];
- pStep->target.z = (char *)&pStep[1];
- pStep->target.n = nFrom;
- memcpy((char *)pStep->target.z, zFrom, nFrom);
+ pStep->zTarget = (char *)&pStep[1];
+ memcpy((char *)pStep->zTarget, zFrom, nFrom);
pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE);
pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE);
@@ -86774,7 +107249,7 @@ static Trigger *fkActionTrigger(
}
/* Re-enable the lookaside buffer, if it was disabled earlier. */
- db->lookaside.bEnabled = enableLookaside;
+ db->lookaside.bDisable--;
sqlite3ExprDelete(db, pWhere);
sqlite3ExprDelete(db, pWhen);
@@ -86784,6 +107259,7 @@ static Trigger *fkActionTrigger(
fkTriggerDelete(db, pTrigger);
return 0;
}
+ assert( pStep!=0 );
switch( action ){
case OE_Restrict:
@@ -86815,7 +107291,9 @@ SQLITE_PRIVATE void sqlite3FkActions(
Parse *pParse, /* Parse context */
Table *pTab, /* Table being updated or deleted from */
ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */
- int regOld /* Address of array containing old row */
+ int regOld, /* Address of array containing old row */
+ int *aChange, /* Array indicating UPDATEd columns (or 0) */
+ int bChngRowid /* True if rowid is UPDATEd */
){
/* If foreign-key support is enabled, iterate through all FKs that
** refer to table pTab. If there is an action associated with the FK
@@ -86824,9 +107302,11 @@ SQLITE_PRIVATE void sqlite3FkActions(
if( pParse->db->flags&SQLITE_ForeignKeys ){
FKey *pFKey; /* Iterator variable */
for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){
- Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges);
- if( pAction ){
- sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0);
+ if( aChange==0 || fkParentIsModified(pTab, pFKey, aChange, bChngRowid) ){
+ Trigger *pAct = fkActionTrigger(pParse, pTab, pFKey, pChanges);
+ if( pAct ){
+ sqlite3CodeRowTriggerDirect(pParse, pAct, pTab, regOld, OE_Abort, 0);
+ }
}
}
}
@@ -86843,7 +107323,8 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){
FKey *pFKey; /* Iterator variable */
FKey *pNext; /* Copy of pFKey->pNextFrom */
- assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
+ assert( db==0 || IsVirtual(pTab)
+ || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) );
for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){
/* Remove the FK from the fkeyHash hash table. */
@@ -86853,7 +107334,7 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){
}else{
void *p = (void *)pFKey->pNextTo;
const char *z = (p ? pFKey->pNextTo->zTo : pFKey->zTo);
- sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, sqlite3Strlen30(z), p);
+ sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, p);
}
if( pFKey->pNextTo ){
pFKey->pNextTo->pPrevTo = pFKey->pPrevTo;
@@ -86893,25 +107374,41 @@ SQLITE_PRIVATE void sqlite3FkDelete(sqlite3 *db, Table *pTab){
** This file contains C code routines that are called by the parser
** to handle INSERT statements in SQLite.
*/
+/* #include "sqliteInt.h" */
/*
-** Generate code that will open a table for reading.
+** Generate code that will
+**
+** (1) acquire a lock for table pTab then
+** (2) open pTab as cursor iCur.
+**
+** If pTab is a WITHOUT ROWID table, then it is the PRIMARY KEY index
+** for that table that is actually opened.
*/
SQLITE_PRIVATE void sqlite3OpenTable(
- Parse *p, /* Generate code into this VDBE */
+ Parse *pParse, /* Generate code into this VDBE */
int iCur, /* The cursor number of the table */
int iDb, /* The database index in sqlite3.aDb[] */
Table *pTab, /* The table to be opened */
int opcode /* OP_OpenRead or OP_OpenWrite */
){
Vdbe *v;
- if( IsVirtual(pTab) ) return;
- v = sqlite3GetVdbe(p);
+ assert( !IsVirtual(pTab) );
+ v = sqlite3GetVdbe(pParse);
assert( opcode==OP_OpenWrite || opcode==OP_OpenRead );
- sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName);
- sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb);
- sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32);
- VdbeComment((v, "%s", pTab->zName));
+ sqlite3TableLock(pParse, iDb, pTab->tnum,
+ (opcode==OP_OpenWrite)?1:0, pTab->zName);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp4Int(v, opcode, iCur, pTab->tnum, iDb, pTab->nCol);
+ VdbeComment((v, "%s", pTab->zName));
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ assert( pPk!=0 );
+ assert( pPk->tnum==pTab->tnum );
+ sqlite3VdbeAddOp3(v, opcode, iCur, pPk->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ VdbeComment((v, "%s", pTab->zName));
+ }
}
/*
@@ -86921,20 +107418,20 @@ SQLITE_PRIVATE void sqlite3OpenTable(
**
** Character Column affinity
** ------------------------------
-** 'a' TEXT
-** 'b' NONE
-** 'c' NUMERIC
-** 'd' INTEGER
-** 'e' REAL
+** 'A' BLOB
+** 'B' TEXT
+** 'C' NUMERIC
+** 'D' INTEGER
+** 'F' REAL
**
-** An extra 'b' is appended to the end of the string to cover the
+** An extra 'D' is appended to the end of the string to cover the
** rowid that appears as the last column in every index.
**
** Memory for the buffer containing the column index affinity string
** is managed along with the rest of the Index structure. It will be
** released when sqlite3DeleteIndex() is called.
*/
-SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
+SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(sqlite3 *db, Index *pIdx){
if( !pIdx->zColAff ){
/* The first time a column affinity string for a particular index is
** required, it is allocated and populated here. It is then stored as
@@ -86946,16 +107443,26 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
*/
int n;
Table *pTab = pIdx->pTable;
- sqlite3 *db = sqlite3VdbeDb(v);
- pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+2);
+ pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+1);
if( !pIdx->zColAff ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return 0;
}
for(n=0; n<pIdx->nColumn; n++){
- pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity;
+ i16 x = pIdx->aiColumn[n];
+ if( x>=0 ){
+ pIdx->zColAff[n] = pTab->aCol[x].affinity;
+ }else if( x==XN_ROWID ){
+ pIdx->zColAff[n] = SQLITE_AFF_INTEGER;
+ }else{
+ char aff;
+ assert( x==XN_EXPR );
+ assert( pIdx->aColExpr!=0 );
+ aff = sqlite3ExprAffinity(pIdx->aColExpr->a[n].pExpr);
+ if( aff==0 ) aff = SQLITE_AFF_BLOB;
+ pIdx->zColAff[n] = aff;
+ }
}
- pIdx->zColAff[n++] = SQLITE_AFF_NONE;
pIdx->zColAff[n] = 0;
}
@@ -86963,57 +107470,61 @@ SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
}
/*
-** Set P4 of the most recently inserted opcode to a column affinity
-** string for table pTab. A column affinity string has one character
-** for each column indexed by the index, according to the affinity of the
-** column:
+** Compute the affinity string for table pTab, if it has not already been
+** computed. As an optimization, omit trailing SQLITE_AFF_BLOB affinities.
+**
+** If the affinity exists (if it is no entirely SQLITE_AFF_BLOB values) and
+** if iReg>0 then code an OP_Affinity opcode that will set the affinities
+** for register iReg and following. Or if affinities exists and iReg==0,
+** then just set the P4 operand of the previous opcode (which should be
+** an OP_MakeRecord) to the affinity string.
+**
+** A column affinity string has one character per column:
**
** Character Column affinity
** ------------------------------
-** 'a' TEXT
-** 'b' NONE
-** 'c' NUMERIC
-** 'd' INTEGER
-** 'e' REAL
-*/
-SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
- /* The first time a column affinity string for a particular table
- ** is required, it is allocated and populated here. It is then
- ** stored as a member of the Table structure for subsequent use.
- **
- ** The column affinity string will eventually be deleted by
- ** sqlite3DeleteTable() when the Table structure itself is cleaned up.
- */
- if( !pTab->zColAff ){
- char *zColAff;
- int i;
+** 'A' BLOB
+** 'B' TEXT
+** 'C' NUMERIC
+** 'D' INTEGER
+** 'E' REAL
+*/
+SQLITE_PRIVATE void sqlite3TableAffinity(Vdbe *v, Table *pTab, int iReg){
+ int i;
+ char *zColAff = pTab->zColAff;
+ if( zColAff==0 ){
sqlite3 *db = sqlite3VdbeDb(v);
-
zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
if( !zColAff ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
return;
}
for(i=0; i<pTab->nCol; i++){
zColAff[i] = pTab->aCol[i].affinity;
}
- zColAff[pTab->nCol] = '\0';
-
+ do{
+ zColAff[i--] = 0;
+ }while( i>=0 && zColAff[i]==SQLITE_AFF_BLOB );
pTab->zColAff = zColAff;
}
-
- sqlite3VdbeChangeP4(v, -1, pTab->zColAff, P4_TRANSIENT);
+ i = sqlite3Strlen30(zColAff);
+ if( i ){
+ if( iReg ){
+ sqlite3VdbeAddOp4(v, OP_Affinity, iReg, i, 0, zColAff, i);
+ }else{
+ sqlite3VdbeChangeP4(v, -1, zColAff, i);
+ }
+ }
}
/*
** Return non-zero if the table pTab in database iDb or any of its indices
-** have been opened at any point in the VDBE program beginning at location
-** iStartAddr throught the end of the program. This is used to see if
+** have been opened at any point in the VDBE program. This is used to see if
** a statement of the form "INSERT INTO <iDb, pTab> SELECT ..." can
-** run without using temporary table for the results of the SELECT.
+** run without using a temporary table for the results of the SELECT.
*/
-static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){
+static int readsTable(Parse *p, int iDb, Table *pTab){
Vdbe *v = sqlite3GetVdbe(p);
int i;
int iEnd = sqlite3VdbeCurrentAddr(v);
@@ -87021,7 +107532,7 @@ static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){
VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0;
#endif
- for(i=iStartAddr; i<iEnd; i++){
+ for(i=1; i<iEnd; i++){
VdbeOp *pOp = sqlite3VdbeGetOp(v, i);
assert( pOp!=0 );
if( pOp->opcode==OP_OpenRead && pOp->p3==iDb ){
@@ -87051,7 +107562,9 @@ static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){
/*
** Locate or create an AutoincInfo structure associated with table pTab
** which is in database iDb. Return the register number for the register
-** that holds the maximum rowid.
+** that holds the maximum rowid. Return zero if pTab is not an AUTOINCREMENT
+** table. (Also return zero when doing a VACUUM since we do not want to
+** update the AUTOINCREMENT counters during a VACUUM.)
**
** There is at most one AutoincInfo structure per table even if the
** same table is autoincremented multiple times due to inserts within
@@ -87074,14 +107587,16 @@ static int autoIncBegin(
Table *pTab /* The table we are writing to */
){
int memId = 0; /* Register holding maximum rowid */
- if( pTab->tabFlags & TF_Autoincrement ){
+ if( (pTab->tabFlags & TF_Autoincrement)!=0
+ && (pParse->db->flags & SQLITE_Vacuum)==0
+ ){
Parse *pToplevel = sqlite3ParseToplevel(pParse);
AutoincInfo *pInfo;
pInfo = pToplevel->pAinc;
while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
if( pInfo==0 ){
- pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo));
+ pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));
if( pInfo==0 ) return 0;
pInfo->pNext = pToplevel->pAinc;
pToplevel->pAinc = pInfo;
@@ -87105,42 +107620,55 @@ SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){
sqlite3 *db = pParse->db; /* The database connection */
Db *pDb; /* Database only autoinc table */
int memId; /* Register holding max rowid */
- int addr; /* A VDBE address */
Vdbe *v = pParse->pVdbe; /* VDBE under construction */
/* This routine is never called during trigger-generation. It is
** only called from the top-level */
assert( pParse->pTriggerTab==0 );
- assert( pParse==sqlite3ParseToplevel(pParse) );
+ assert( sqlite3IsToplevel(pParse) );
assert( v ); /* We failed long ago if this is not so */
for(p = pParse->pAinc; p; p = p->pNext){
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList autoInc[] = {
+ /* 0 */ {OP_Null, 0, 0, 0},
+ /* 1 */ {OP_Rewind, 0, 9, 0},
+ /* 2 */ {OP_Column, 0, 0, 0},
+ /* 3 */ {OP_Ne, 0, 7, 0},
+ /* 4 */ {OP_Rowid, 0, 0, 0},
+ /* 5 */ {OP_Column, 0, 1, 0},
+ /* 6 */ {OP_Goto, 0, 9, 0},
+ /* 7 */ {OP_Next, 0, 2, 0},
+ /* 8 */ {OP_Integer, 0, 0, 0},
+ /* 9 */ {OP_Close, 0, 0, 0}
+ };
+ VdbeOp *aOp;
pDb = &db->aDb[p->iDb];
memId = p->regCtr;
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead);
- addr = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9);
- sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId);
- sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId);
- sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL);
- sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1);
- sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9);
- sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, memId);
- sqlite3VdbeAddOp0(v, OP_Close);
+ sqlite3VdbeLoadString(v, memId-1, p->pTab->zName);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoInc), autoInc, iLn);
+ if( aOp==0 ) break;
+ aOp[0].p2 = memId;
+ aOp[0].p3 = memId+1;
+ aOp[2].p3 = memId;
+ aOp[3].p1 = memId-1;
+ aOp[3].p3 = memId;
+ aOp[3].p5 = SQLITE_JUMPIFNULL;
+ aOp[4].p2 = memId+1;
+ aOp[5].p3 = memId;
+ aOp[8].p2 = memId;
}
}
/*
** Update the maximum rowid for an autoincrement calculation.
**
-** This routine should be called when the top of the stack holds a
+** This routine should be called when the regRowid register holds a
** new rowid that is about to be inserted. If that new rowid is
** larger than the maximum rowid in the memId memory cell, then the
-** memory cell is updated. The stack is unchanged.
+** memory cell is updated.
*/
static void autoIncStep(Parse *pParse, int memId, int regRowid){
if( memId>0 ){
@@ -87155,40 +107683,44 @@ static void autoIncStep(Parse *pParse, int memId, int regRowid){
** table (either directly or through triggers) needs to call this
** routine just before the "exit" code.
*/
-SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){
+static SQLITE_NOINLINE void autoIncrementEnd(Parse *pParse){
AutoincInfo *p;
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
assert( v );
for(p = pParse->pAinc; p; p = p->pNext){
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList autoIncEnd[] = {
+ /* 0 */ {OP_NotNull, 0, 2, 0},
+ /* 1 */ {OP_NewRowid, 0, 0, 0},
+ /* 2 */ {OP_MakeRecord, 0, 2, 0},
+ /* 3 */ {OP_Insert, 0, 0, 0},
+ /* 4 */ {OP_Close, 0, 0, 0}
+ };
+ VdbeOp *aOp;
Db *pDb = &db->aDb[p->iDb];
- int j1, j2, j3, j4, j5;
int iRec;
int memId = p->regCtr;
iRec = sqlite3GetTempReg(pParse);
assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) );
sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite);
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1);
- j2 = sqlite3VdbeAddOp0(v, OP_Rewind);
- j3 = sqlite3VdbeAddOp3(v, OP_Column, 0, 0, iRec);
- j4 = sqlite3VdbeAddOp3(v, OP_Eq, memId-1, 0, iRec);
- sqlite3VdbeAddOp2(v, OP_Next, 0, j3);
- sqlite3VdbeJumpHere(v, j2);
- sqlite3VdbeAddOp2(v, OP_NewRowid, 0, memId+1);
- j5 = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, j4);
- sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1);
- sqlite3VdbeJumpHere(v, j1);
- sqlite3VdbeJumpHere(v, j5);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, memId-1, 2, iRec);
- sqlite3VdbeAddOp3(v, OP_Insert, 0, iRec, memId+1);
- sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
- sqlite3VdbeAddOp0(v, OP_Close);
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(autoIncEnd), autoIncEnd, iLn);
+ if( aOp==0 ) break;
+ aOp[0].p1 = memId+1;
+ aOp[1].p2 = memId+1;
+ aOp[2].p1 = memId-1;
+ aOp[2].p3 = iRec;
+ aOp[3].p2 = iRec;
+ aOp[3].p3 = memId+1;
+ aOp[3].p5 = OPFLAG_APPEND;
sqlite3ReleaseTempReg(pParse, iRec);
}
}
+SQLITE_PRIVATE void sqlite3AutoincrementEnd(Parse *pParse){
+ if( pParse->pAinc ) autoIncrementEnd(pParse);
+}
#else
/*
** If SQLITE_OMIT_AUTOINCREMENT is defined, then the three routines
@@ -87209,27 +107741,30 @@ static int xferOptimization(
);
/*
-** This routine is call to handle SQL of the following forms:
+** This routine is called to handle SQL of the following forms:
**
-** insert into TABLE (IDLIST) values(EXPRLIST)
+** insert into TABLE (IDLIST) values(EXPRLIST),(EXPRLIST),...
** insert into TABLE (IDLIST) select
+** insert into TABLE (IDLIST) default values
**
** The IDLIST following the table name is always optional. If omitted,
-** then a list of all columns for the table is substituted. The IDLIST
-** appears in the pColumn parameter. pColumn is NULL if IDLIST is omitted.
+** then a list of all (non-hidden) columns for the table is substituted.
+** The IDLIST appears in the pColumn parameter. pColumn is NULL if IDLIST
+** is omitted.
**
-** The pList parameter holds EXPRLIST in the first form of the INSERT
-** statement above, and pSelect is NULL. For the second form, pList is
-** NULL and pSelect is a pointer to the select statement used to generate
-** data for the insert.
+** For the pSelect parameter holds the values to be inserted for the
+** first two forms shown above. A VALUES clause is really just short-hand
+** for a SELECT statement that omits the FROM clause and everything else
+** that follows. If the pSelect parameter is NULL, that means that the
+** DEFAULT VALUES form of the INSERT statement is intended.
**
** The code generated follows one of four templates. For a simple
-** select with data coming from a VALUES clause, the code executes
+** insert with data coming from a single-row VALUES clause, the code executes
** once straight down through. Pseudo-code follows (we call this
** the "1st template"):
**
** open write cursor to <table> and its indices
-** puts VALUES clause expressions onto the stack
+** put VALUES clause expressions into registers
** write the resulting record into <table>
** cleanup
**
@@ -87261,7 +107796,6 @@ static int xferOptimization(
** and the SELECT clause does not read from <table> at any time.
** The generated code follows this template:
**
-** EOF <- 0
** X <- A
** goto B
** A: setup for the SELECT
@@ -87270,12 +107804,9 @@ static int xferOptimization(
** yield X
** end loop
** cleanup after the SELECT
-** EOF <- 1
-** yield X
-** goto A
+** end-coroutine X
** B: open write cursor to <table> and its indices
-** C: yield X
-** if EOF goto D
+** C: yield X, at EOF goto D
** insert the select result into <table> from R..R+n
** goto C
** D: cleanup
@@ -87283,10 +107814,9 @@ static int xferOptimization(
** The 4th template is used if the insert statement takes its
** values from a SELECT but the data is being inserted into a table
** that is also read as part of the SELECT. In the third form,
-** we have to use a intermediate table to store the results of
+** we have to use an intermediate table to store the results of
** the select. The template is like this:
**
-** EOF <- 0
** X <- A
** goto B
** A: setup for the SELECT
@@ -87295,12 +107825,9 @@ static int xferOptimization(
** yield X
** end loop
** cleanup after the SELECT
-** EOF <- 1
-** yield X
-** halt-error
+** end co-routine R
** B: open temp table
-** L: yield X
-** if EOF goto M
+** L: yield X, at EOF goto M
** insert row from R..R+n into temp table
** goto L
** M: open write cursor to <table> and its indices
@@ -87313,7 +107840,6 @@ static int xferOptimization(
SQLITE_PRIVATE void sqlite3Insert(
Parse *pParse, /* Parser context */
SrcList *pTabList, /* Name of table into which we are inserting */
- ExprList *pList, /* List of values to be inserted */
Select *pSelect, /* A SELECT statement to use as the data source */
IdList *pColumn, /* Column names corresponding to IDLIST. */
int onError /* How to handle constraint errors */
@@ -87321,24 +107847,25 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3 *db; /* The main database structure */
Table *pTab; /* The table to insert into. aka TABLE */
char *zTab; /* Name of the table into which we are inserting */
- const char *zDb; /* Name of the database holding this table */
int i, j, idx; /* Loop counters */
Vdbe *v; /* Generate code into this virtual machine */
Index *pIdx; /* For looping over indices of the table */
int nColumn; /* Number of columns in the data */
int nHidden = 0; /* Number of hidden columns if TABLE is virtual */
- int baseCur = 0; /* VDBE Cursor number for pTab */
- int keyColumn = -1; /* Column that is the INTEGER PRIMARY KEY */
+ int iDataCur = 0; /* VDBE cursor that is the main data repository */
+ int iIdxCur = 0; /* First index cursor */
+ int ipkColumn = -1; /* Column that is the INTEGER PRIMARY KEY */
int endOfLoop; /* Label for the end of the insertion loop */
- int useTempTable = 0; /* Store SELECT results in intermediate table */
int srcTab = 0; /* Data comes from this temporary cursor if >=0 */
int addrInsTop = 0; /* Jump to label "D" */
int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */
- int addrSelect = 0; /* Address of coroutine that implements the SELECT */
SelectDest dest; /* Destination for SELECT on rhs of INSERT */
int iDb; /* Index of database holding TABLE */
- Db *pDb; /* The database containing table being inserted into */
- int appendFlag = 0; /* True if the insert is likely to be an append */
+ u8 useTempTable = 0; /* Store SELECT results in intermediate table */
+ u8 appendFlag = 0; /* True if the insert is likely to be an append */
+ u8 withoutRowid; /* 0 for normal table. 1 for WITHOUT ROWID table */
+ u8 bIdListInOrder; /* True if IDLIST is in table order */
+ ExprList *pList = 0; /* List of VALUES() to be inserted */
/* Register allocations */
int regFromSelect = 0;/* Base register for data coming from SELECT */
@@ -87347,7 +107874,6 @@ SQLITE_PRIVATE void sqlite3Insert(
int regIns; /* Block of regs holding rowid+data being inserted */
int regRowid; /* registers holding insert rowid */
int regData; /* register holding first column to insert */
- int regEof = 0; /* Register recording end of SELECT data */
int *aRegIdx = 0; /* One register allocated to each index */
#ifndef SQLITE_OMIT_TRIGGER
@@ -87362,6 +107888,17 @@ SQLITE_PRIVATE void sqlite3Insert(
goto insert_cleanup;
}
+ /* If the Select object is really just a simple VALUES() list with a
+ ** single row (the common case) then keep that one row of values
+ ** and discard the other (unused) parts of the pSelect object
+ */
+ if( pSelect && (pSelect->selFlags & SF_Values)!=0 && pSelect->pPrior==0 ){
+ pList = pSelect->pEList;
+ pSelect->pEList = 0;
+ sqlite3SelectDelete(db, pSelect);
+ pSelect = 0;
+ }
+
/* Locate the table into which we will be inserting new information.
*/
assert( pTabList->nSrc==1 );
@@ -87373,11 +107910,11 @@ SQLITE_PRIVATE void sqlite3Insert(
}
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
assert( iDb<db->nDb );
- pDb = &db->aDb[iDb];
- zDb = pDb->zName;
- if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0, zDb) ){
+ if( sqlite3AuthCheck(pParse, SQLITE_INSERT, pTab->zName, 0,
+ db->aDb[iDb].zDbSName) ){
goto insert_cleanup;
}
+ withoutRowid = !HasRowid(pTab);
/* Figure out if we have any triggers and if the table being
** inserted into is a view
@@ -87397,16 +107934,13 @@ SQLITE_PRIVATE void sqlite3Insert(
assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) );
/* If pTab is really a view, make sure it has been initialized.
- ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual
- ** module table).
+ ** ViewGetColumnNames() is a no-op if pTab is not a view.
*/
if( sqlite3ViewGetColumnNames(pParse, pTab) ){
goto insert_cleanup;
}
- /* Ensure that:
- * (a) the table is not read-only,
- * (b) that if it is a view then ON INSERT triggers exist
+ /* Cannot insert into a read-only table.
*/
if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
goto insert_cleanup;
@@ -87441,72 +107975,94 @@ SQLITE_PRIVATE void sqlite3Insert(
*/
regAutoinc = autoIncBegin(pParse, iDb, pTab);
+ /* Allocate registers for holding the rowid of the new row,
+ ** the content of the new row, and the assembled row record.
+ */
+ regRowid = regIns = pParse->nMem+1;
+ pParse->nMem += pTab->nCol + 1;
+ if( IsVirtual(pTab) ){
+ regRowid++;
+ pParse->nMem++;
+ }
+ regData = regRowid+1;
+
+ /* If the INSERT statement included an IDLIST term, then make sure
+ ** all elements of the IDLIST really are columns of the table and
+ ** remember the column indices.
+ **
+ ** If the table has an INTEGER PRIMARY KEY column and that column
+ ** is named in the IDLIST, then record in the ipkColumn variable
+ ** the index into IDLIST of the primary key column. ipkColumn is
+ ** the index of the primary key as it appears in IDLIST, not as
+ ** is appears in the original table. (The index of the INTEGER
+ ** PRIMARY KEY in the original table is pTab->iPKey.)
+ */
+ bIdListInOrder = (pTab->tabFlags & TF_OOOHidden)==0;
+ if( pColumn ){
+ for(i=0; i<pColumn->nId; i++){
+ pColumn->a[i].idx = -1;
+ }
+ for(i=0; i<pColumn->nId; i++){
+ for(j=0; j<pTab->nCol; j++){
+ if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
+ pColumn->a[i].idx = j;
+ if( i!=j ) bIdListInOrder = 0;
+ if( j==pTab->iPKey ){
+ ipkColumn = i; assert( !withoutRowid );
+ }
+ break;
+ }
+ }
+ if( j>=pTab->nCol ){
+ if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){
+ ipkColumn = i;
+ bIdListInOrder = 0;
+ }else{
+ sqlite3ErrorMsg(pParse, "table %S has no column named %s",
+ pTabList, 0, pColumn->a[i].zName);
+ pParse->checkSchema = 1;
+ goto insert_cleanup;
+ }
+ }
+ }
+ }
+
/* Figure out how many columns of data are supplied. If the data
** is coming from a SELECT statement, then generate a co-routine that
** produces a single row of the SELECT on each invocation. The
** co-routine is the common header to the 3rd and 4th templates.
*/
if( pSelect ){
- /* Data is coming from a SELECT. Generate code to implement that SELECT
- ** as a co-routine. The code is common to both the 3rd and 4th
- ** templates:
- **
- ** EOF <- 0
- ** X <- A
- ** goto B
- ** A: setup for the SELECT
- ** loop over the tables in the SELECT
- ** load value into register R..R+n
- ** yield X
- ** end loop
- ** cleanup after the SELECT
- ** EOF <- 1
- ** yield X
- ** halt-error
- **
- ** On each invocation of the co-routine, it puts a single row of the
- ** SELECT result into registers dest.iMem...dest.iMem+dest.nMem-1.
- ** (These output registers are allocated by sqlite3Select().) When
- ** the SELECT completes, it sets the EOF flag stored in regEof.
- */
- int rc, j1;
-
- regEof = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regEof); /* EOF <- 0 */
- VdbeComment((v, "SELECT eof flag"));
- sqlite3SelectDestInit(&dest, SRT_Coroutine, ++pParse->nMem);
- addrSelect = sqlite3VdbeCurrentAddr(v)+2;
- sqlite3VdbeAddOp2(v, OP_Integer, addrSelect-1, dest.iParm);
- j1 = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
- VdbeComment((v, "Jump over SELECT coroutine"));
-
- /* Resolve the expressions in the SELECT statement and execute it. */
+ /* Data is coming from a SELECT or from a multi-row VALUES clause.
+ ** Generate a co-routine to run the SELECT. */
+ int regYield; /* Register holding co-routine entry-point */
+ int addrTop; /* Top of the co-routine */
+ int rc; /* Result code */
+
+ regYield = ++pParse->nMem;
+ addrTop = sqlite3VdbeCurrentAddr(v) + 1;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, regYield);
+ dest.iSdst = bIdListInOrder ? regData : 0;
+ dest.nSdst = pTab->nCol;
rc = sqlite3Select(pParse, pSelect, &dest);
- assert( pParse->nErr==0 || rc );
- if( rc || NEVER(pParse->nErr) || db->mallocFailed ){
- goto insert_cleanup;
- }
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regEof); /* EOF <- 1 */
- sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm); /* yield X */
- sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_INTERNAL, OE_Abort);
- VdbeComment((v, "End of SELECT coroutine"));
- sqlite3VdbeJumpHere(v, j1); /* label B: */
-
- regFromSelect = dest.iMem;
+ regFromSelect = dest.iSdst;
+ if( rc || db->mallocFailed || pParse->nErr ) goto insert_cleanup;
+ sqlite3VdbeEndCoroutine(v, regYield);
+ sqlite3VdbeJumpHere(v, addrTop - 1); /* label B: */
assert( pSelect->pEList );
nColumn = pSelect->pEList->nExpr;
- assert( dest.nMem==nColumn );
/* Set useTempTable to TRUE if the result of the SELECT statement
** should be written into a temporary table (template 4). Set to
- ** FALSE if each* row of the SELECT can be written directly into
+ ** FALSE if each output row of the SELECT can be written directly into
** the destination table (template 3).
**
** A temp table must be used if the table being updated is also one
** of the tables being read by the SELECT statement. Also use a
** temp table in the case of row triggers.
*/
- if( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){
+ if( pTrigger || readsTable(pParse, iDb, pTab) ){
useTempTable = 1;
}
@@ -87516,55 +108072,60 @@ SQLITE_PRIVATE void sqlite3Insert(
** here is from the 4th template:
**
** B: open temp table
- ** L: yield X
- ** if EOF goto M
+ ** L: yield X, goto M at EOF
** insert row from R..R+n into temp table
** goto L
** M: ...
*/
int regRec; /* Register to hold packed record */
int regTempRowid; /* Register to hold temp table ROWID */
- int addrTop; /* Label "L" */
- int addrIf; /* Address of jump to M */
+ int addrL; /* Label "L" */
srcTab = pParse->nTab++;
regRec = sqlite3GetTempReg(pParse);
regTempRowid = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, srcTab, nColumn);
- addrTop = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm);
- addrIf = sqlite3VdbeAddOp1(v, OP_If, regEof);
+ addrL = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regFromSelect, nColumn, regRec);
sqlite3VdbeAddOp2(v, OP_NewRowid, srcTab, regTempRowid);
sqlite3VdbeAddOp3(v, OP_Insert, srcTab, regRec, regTempRowid);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrTop);
- sqlite3VdbeJumpHere(v, addrIf);
+ sqlite3VdbeGoto(v, addrL);
+ sqlite3VdbeJumpHere(v, addrL);
sqlite3ReleaseTempReg(pParse, regRec);
sqlite3ReleaseTempReg(pParse, regTempRowid);
}
}else{
- /* This is the case if the data for the INSERT is coming from a VALUES
- ** clause
+ /* This is the case if the data for the INSERT is coming from a
+ ** single-row VALUES clause
*/
NameContext sNC;
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
srcTab = -1;
assert( useTempTable==0 );
- nColumn = pList ? pList->nExpr : 0;
- for(i=0; i<nColumn; i++){
- if( sqlite3ResolveExprNames(&sNC, pList->a[i].pExpr) ){
+ if( pList ){
+ nColumn = pList->nExpr;
+ if( sqlite3ResolveExprListNames(&sNC, pList) ){
goto insert_cleanup;
}
+ }else{
+ nColumn = 0;
}
}
+ /* If there is no IDLIST term but the table has an integer primary
+ ** key, the set the ipkColumn variable to the integer primary key
+ ** column index in the original table definition.
+ */
+ if( pColumn==0 && nColumn>0 ){
+ ipkColumn = pTab->iPKey;
+ }
+
/* Make sure the number of columns in the source data matches the number
** of columns to be inserted into the table.
*/
- if( IsVirtual(pTab) ){
- for(i=0; i<pTab->nCol; i++){
- nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0);
- }
+ for(i=0; i<pTab->nCol; i++){
+ nHidden += (IsHiddenColumn(&pTab->aCol[i]) ? 1 : 0);
}
if( pColumn==0 && nColumn && nColumn!=(pTab->nCol-nHidden) ){
sqlite3ErrorMsg(pParse,
@@ -87576,52 +108137,6 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3ErrorMsg(pParse, "%d values for %d columns", nColumn, pColumn->nId);
goto insert_cleanup;
}
-
- /* If the INSERT statement included an IDLIST term, then make sure
- ** all elements of the IDLIST really are columns of the table and
- ** remember the column indices.
- **
- ** If the table has an INTEGER PRIMARY KEY column and that column
- ** is named in the IDLIST, then record in the keyColumn variable
- ** the index into IDLIST of the primary key column. keyColumn is
- ** the index of the primary key as it appears in IDLIST, not as
- ** is appears in the original table. (The index of the primary
- ** key in the original table is pTab->iPKey.)
- */
- if( pColumn ){
- for(i=0; i<pColumn->nId; i++){
- pColumn->a[i].idx = -1;
- }
- for(i=0; i<pColumn->nId; i++){
- for(j=0; j<pTab->nCol; j++){
- if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zName)==0 ){
- pColumn->a[i].idx = j;
- if( j==pTab->iPKey ){
- keyColumn = i;
- }
- break;
- }
- }
- if( j>=pTab->nCol ){
- if( sqlite3IsRowid(pColumn->a[i].zName) ){
- keyColumn = i;
- }else{
- sqlite3ErrorMsg(pParse, "table %S has no column named %s",
- pTabList, 0, pColumn->a[i].zName);
- pParse->checkSchema = 1;
- goto insert_cleanup;
- }
- }
- }
- }
-
- /* If there is no IDLIST term but the table has an integer primary
- ** key, the set the keyColumn variable to the primary key column index
- ** in the original table definition.
- */
- if( pColumn==0 && nColumn>0 ){
- keyColumn = pTab->iPKey;
- }
/* Initialize the count of rows to be inserted
*/
@@ -87633,10 +108148,9 @@ SQLITE_PRIVATE void sqlite3Insert(
/* If this is not a view, open the table and and all indices */
if( !isView ){
int nIdx;
-
- baseCur = pParse->nTab;
- nIdx = sqlite3OpenTableAndIndices(pParse, pTab, baseCur, OP_OpenWrite);
- aRegIdx = sqlite3DbMallocRaw(db, sizeof(int)*(nIdx+1));
+ nIdx = sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, -1, 0,
+ &iDataCur, &iIdxCur);
+ aRegIdx = sqlite3DbMallocRawNN(db, sizeof(int)*(nIdx+1));
if( aRegIdx==0 ){
goto insert_cleanup;
}
@@ -87650,38 +108164,26 @@ SQLITE_PRIVATE void sqlite3Insert(
/* This block codes the top of loop only. The complete loop is the
** following pseudocode (template 4):
**
- ** rewind temp table
+ ** rewind temp table, if empty goto D
** C: loop over rows of intermediate table
** transfer values form intermediate table into <table>
** end loop
** D: ...
*/
- addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab);
+ addrInsTop = sqlite3VdbeAddOp1(v, OP_Rewind, srcTab); VdbeCoverage(v);
addrCont = sqlite3VdbeCurrentAddr(v);
}else if( pSelect ){
/* This block codes the top of loop only. The complete loop is the
** following pseudocode (template 3):
**
- ** C: yield X
- ** if EOF goto D
+ ** C: yield X, at EOF goto D
** insert the select result into <table> from R..R+n
** goto C
** D: ...
*/
- addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iParm);
- addrInsTop = sqlite3VdbeAddOp1(v, OP_If, regEof);
- }
-
- /* Allocate registers for holding the rowid of the new row,
- ** the content of the new row, and the assemblied row record.
- */
- regRowid = regIns = pParse->nMem+1;
- pParse->nMem += pTab->nCol + 1;
- if( IsVirtual(pTab) ){
- regRowid++;
- pParse->nMem++;
+ addrInsTop = addrCont = sqlite3VdbeAddOp1(v, OP_Yield, dest.iSDParm);
+ VdbeCoverage(v);
}
- regData = regRowid+1;
/* Run the BEFORE and INSTEAD OF triggers, if there are any
*/
@@ -87695,20 +108197,21 @@ SQLITE_PRIVATE void sqlite3Insert(
** we do not know what the unique ID will be (because the insert has
** not happened yet) so we substitute a rowid of -1
*/
- if( keyColumn<0 ){
+ if( ipkColumn<0 ){
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
}else{
- int j1;
+ int addr1;
+ assert( !withoutRowid );
if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regCols);
}else{
assert( pSelect==0 ); /* Otherwise useTempTable is true */
- sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regCols);
+ sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regCols);
}
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols);
+ addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols);
- sqlite3VdbeJumpHere(v, j1);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols);
+ sqlite3VdbeJumpHere(v, addr1);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); VdbeCoverage(v);
}
/* Cannot have triggers on a virtual table. If it were possible,
@@ -87718,15 +108221,14 @@ SQLITE_PRIVATE void sqlite3Insert(
/* Create the new column data
*/
- for(i=0; i<pTab->nCol; i++){
- if( pColumn==0 ){
- j = i;
- }else{
+ for(i=j=0; i<pTab->nCol; i++){
+ if( pColumn ){
for(j=0; j<pColumn->nId; j++){
if( pColumn->a[j].idx==i ) break;
}
}
- if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId) ){
+ if( (!useTempTable && !pList) || (pColumn && j>=pColumn->nId)
+ || (pColumn==0 && IsOrdinaryHiddenColumn(&pTab->aCol[i])) ){
sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1);
}else if( useTempTable ){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1);
@@ -87734,6 +108236,7 @@ SQLITE_PRIVATE void sqlite3Insert(
assert( pSelect==0 ); /* Otherwise useTempTable is true */
sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1);
}
+ if( pColumn==0 && !IsOrdinaryHiddenColumn(&pTab->aCol[i]) ) j++;
}
/* If this is an INSERT on a view with an INSTEAD OF INSERT trigger,
@@ -87742,8 +108245,7 @@ SQLITE_PRIVATE void sqlite3Insert(
** table column affinities.
*/
if( !isView ){
- sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol);
- sqlite3TableAffinityStr(v, pTab);
+ sqlite3TableAffinity(v, pTab, regCols+1);
}
/* Fire BEFORE or INSTEAD OF triggers */
@@ -87753,29 +108255,27 @@ SQLITE_PRIVATE void sqlite3Insert(
sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1);
}
- /* Push the record number for the new entry onto the stack. The
- ** record number is a randomly generate integer created by NewRowid
- ** except when the table has an INTEGER PRIMARY KEY column, in which
- ** case the record number is the same as that column.
+ /* Compute the content of the next row to insert into a range of
+ ** registers beginning at regIns.
*/
if( !isView ){
if( IsVirtual(pTab) ){
/* The row that the VUpdate opcode will delete: none */
sqlite3VdbeAddOp2(v, OP_Null, 0, regIns);
}
- if( keyColumn>=0 ){
+ if( ipkColumn>=0 ){
if( useTempTable ){
- sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Column, srcTab, ipkColumn, regRowid);
}else if( pSelect ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+keyColumn, regRowid);
+ sqlite3VdbeAddOp2(v, OP_Copy, regFromSelect+ipkColumn, regRowid);
}else{
VdbeOp *pOp;
- sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regRowid);
+ sqlite3ExprCode(pParse, pList->a[ipkColumn].pExpr, regRowid);
pOp = sqlite3VdbeGetOp(v, -1);
if( ALWAYS(pOp) && pOp->opcode==OP_Null && !IsVirtual(pTab) ){
appendFlag = 1;
pOp->opcode = OP_NewRowid;
- pOp->p1 = baseCur;
+ pOp->p1 = iDataCur;
pOp->p2 = regRowid;
pOp->p3 = regAutoinc;
}
@@ -87784,26 +108284,26 @@ SQLITE_PRIVATE void sqlite3Insert(
** to generate a unique primary key value.
*/
if( !appendFlag ){
- int j1;
+ int addr1;
if( !IsVirtual(pTab) ){
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid);
- sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc);
- sqlite3VdbeJumpHere(v, j1);
+ addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regRowid); VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc);
+ sqlite3VdbeJumpHere(v, addr1);
}else{
- j1 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, j1+2);
+ addr1 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRowid, addr1+2); VdbeCoverage(v);
}
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); VdbeCoverage(v);
}
- }else if( IsVirtual(pTab) ){
+ }else if( IsVirtual(pTab) || withoutRowid ){
sqlite3VdbeAddOp2(v, OP_Null, 0, regRowid);
}else{
- sqlite3VdbeAddOp3(v, OP_NewRowid, baseCur, regRowid, regAutoinc);
+ sqlite3VdbeAddOp3(v, OP_NewRowid, iDataCur, regRowid, regAutoinc);
appendFlag = 1;
}
autoIncStep(pParse, regAutoinc, regRowid);
- /* Push onto the stack, data for all columns of the new entry, beginning
+ /* Compute data for all columns of the new entry, beginning
** with the first column.
*/
nHidden = 0;
@@ -87811,15 +108311,15 @@ SQLITE_PRIVATE void sqlite3Insert(
int iRegStore = regRowid+1+i;
if( i==pTab->iPKey ){
/* The value of the INTEGER PRIMARY KEY column is always a NULL.
- ** Whenever this column is read, the record number will be substituted
- ** in its place. So will fill this column with a NULL to avoid
- ** taking up data space with information that will never be used. */
- sqlite3VdbeAddOp2(v, OP_Null, 0, iRegStore);
+ ** Whenever this column is read, the rowid will be substituted
+ ** in its place. Hence, fill this column with a NULL to avoid
+ ** taking up data space with information that will never be used.
+ ** As there may be shallow copies of this value, make it a soft-NULL */
+ sqlite3VdbeAddOp1(v, OP_SoftNull, iRegStore);
continue;
}
if( pColumn==0 ){
if( IsHiddenColumn(&pTab->aCol[i]) ){
- assert( IsVirtual(pTab) );
j = -1;
nHidden++;
}else{
@@ -87831,11 +108331,13 @@ SQLITE_PRIVATE void sqlite3Insert(
}
}
if( j<0 || nColumn==0 || (pColumn && j>=pColumn->nId) ){
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, iRegStore);
+ sqlite3ExprCodeFactorable(pParse, pTab->aCol[i].pDflt, iRegStore);
}else if( useTempTable ){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, iRegStore);
}else if( pSelect ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
+ if( regFromSelect!=regData ){
+ sqlite3VdbeAddOp2(v, OP_SCopy, regFromSelect+j, iRegStore);
+ }
}else{
sqlite3ExprCode(pParse, pList->a[j].pExpr, iRegStore);
}
@@ -87855,13 +108357,12 @@ SQLITE_PRIVATE void sqlite3Insert(
#endif
{
int isReplace; /* Set to true if constraints may cause a replace */
- sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx,
- keyColumn>=0, 0, onError, endOfLoop, &isReplace
- );
- sqlite3FkCheck(pParse, pTab, 0, regIns);
- sqlite3CompleteInsertion(
- pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0
+ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
+ regIns, 0, ipkColumn>=0, onError, endOfLoop, &isReplace, 0
);
+ sqlite3FkCheck(pParse, pTab, 0, regIns, 0, 0);
+ sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
+ regIns, aRegIdx, 0, appendFlag, isReplace==0);
}
}
@@ -87882,19 +108383,19 @@ SQLITE_PRIVATE void sqlite3Insert(
*/
sqlite3VdbeResolveLabel(v, endOfLoop);
if( useTempTable ){
- sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont);
+ sqlite3VdbeAddOp2(v, OP_Next, srcTab, addrCont); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrInsTop);
sqlite3VdbeAddOp1(v, OP_Close, srcTab);
}else if( pSelect ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrCont);
+ sqlite3VdbeGoto(v, addrCont);
sqlite3VdbeJumpHere(v, addrInsTop);
}
if( !IsVirtual(pTab) && !isView ){
/* Close all tables opened */
- sqlite3VdbeAddOp1(v, OP_Close, baseCur);
- for(idx=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
- sqlite3VdbeAddOp1(v, OP_Close, idx+baseCur);
+ if( iDataCur<iIdxCur ) sqlite3VdbeAddOp1(v, OP_Close, iDataCur);
+ for(idx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, idx++){
+ sqlite3VdbeAddOp1(v, OP_Close, idx+iIdxCur);
}
}
@@ -87927,7 +108428,7 @@ insert_cleanup:
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
-** thely may interfere with compilation of other functions in this file
+** they may interfere with compilation of other functions in this file
** (or in another file, if this file becomes part of the amalgamation). */
#ifdef isView
#undef isView
@@ -87939,36 +108440,101 @@ insert_cleanup:
#undef tmask
#endif
+/*
+** Meanings of bits in of pWalker->eCode for checkConstraintUnchanged()
+*/
+#define CKCNSTRNT_COLUMN 0x01 /* CHECK constraint uses a changing column */
+#define CKCNSTRNT_ROWID 0x02 /* CHECK constraint references the ROWID */
+
+/* This is the Walker callback from checkConstraintUnchanged(). Set
+** bit 0x01 of pWalker->eCode if
+** pWalker->eCode to 0 if this expression node references any of the
+** columns that are being modifed by an UPDATE statement.
+*/
+static int checkConstraintExprNode(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_COLUMN ){
+ assert( pExpr->iColumn>=0 || pExpr->iColumn==-1 );
+ if( pExpr->iColumn>=0 ){
+ if( pWalker->u.aiCol[pExpr->iColumn]>=0 ){
+ pWalker->eCode |= CKCNSTRNT_COLUMN;
+ }
+ }else{
+ pWalker->eCode |= CKCNSTRNT_ROWID;
+ }
+ }
+ return WRC_Continue;
+}
/*
-** Generate code to do constraint checks prior to an INSERT or an UPDATE.
+** pExpr is a CHECK constraint on a row that is being UPDATE-ed. The
+** only columns that are modified by the UPDATE are those for which
+** aiChng[i]>=0, and also the ROWID is modified if chngRowid is true.
**
-** The input is a range of consecutive registers as follows:
-**
-** 1. The rowid of the row after the update.
-**
-** 2. The data in the first column of the entry after the update.
-**
-** i. Data from middle columns...
-**
-** N. The data in the last column of the entry after the update.
-**
-** The regRowid parameter is the index of the register containing (1).
-**
-** If isUpdate is true and rowidChng is non-zero, then rowidChng contains
-** the address of a register containing the rowid before the update takes
-** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate
-** is false, indicating an INSERT statement, then a non-zero rowidChng
-** indicates that the rowid was explicitly specified as part of the
-** INSERT statement. If rowidChng is false, it means that the rowid is
-** computed automatically in an insert or that the rowid value is not
-** modified by an update.
-**
-** The code generated by this routine store new index entries into
+** Return true if CHECK constraint pExpr does not use any of the
+** changing columns (or the rowid if it is changing). In other words,
+** return true if this CHECK constraint can be skipped when validating
+** the new row in the UPDATE statement.
+*/
+static int checkConstraintUnchanged(Expr *pExpr, int *aiChng, int chngRowid){
+ Walker w;
+ memset(&w, 0, sizeof(w));
+ w.eCode = 0;
+ w.xExprCallback = checkConstraintExprNode;
+ w.u.aiCol = aiChng;
+ sqlite3WalkExpr(&w, pExpr);
+ if( !chngRowid ){
+ testcase( (w.eCode & CKCNSTRNT_ROWID)!=0 );
+ w.eCode &= ~CKCNSTRNT_ROWID;
+ }
+ testcase( w.eCode==0 );
+ testcase( w.eCode==CKCNSTRNT_COLUMN );
+ testcase( w.eCode==CKCNSTRNT_ROWID );
+ testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) );
+ return !w.eCode;
+}
+
+/*
+** Generate code to do constraint checks prior to an INSERT or an UPDATE
+** on table pTab.
+**
+** The regNewData parameter is the first register in a range that contains
+** the data to be inserted or the data after the update. There will be
+** pTab->nCol+1 registers in this range. The first register (the one
+** that regNewData points to) will contain the new rowid, or NULL in the
+** case of a WITHOUT ROWID table. The second register in the range will
+** contain the content of the first table column. The third register will
+** contain the content of the second table column. And so forth.
+**
+** The regOldData parameter is similar to regNewData except that it contains
+** the data prior to an UPDATE rather than afterwards. regOldData is zero
+** for an INSERT. This routine can distinguish between UPDATE and INSERT by
+** checking regOldData for zero.
+**
+** For an UPDATE, the pkChng boolean is true if the true primary key (the
+** rowid for a normal table or the PRIMARY KEY for a WITHOUT ROWID table)
+** might be modified by the UPDATE. If pkChng is false, then the key of
+** the iDataCur content table is guaranteed to be unchanged by the UPDATE.
+**
+** For an INSERT, the pkChng boolean indicates whether or not the rowid
+** was explicitly specified as part of the INSERT statement. If pkChng
+** is zero, it means that the either rowid is computed automatically or
+** that the table is a WITHOUT ROWID table and has no rowid. On an INSERT,
+** pkChng will only be true if the INSERT statement provides an integer
+** value for either the rowid column or its INTEGER PRIMARY KEY alias.
+**
+** The code generated by this routine will store new index entries into
** registers identified by aRegIdx[]. No index entry is created for
** indices where aRegIdx[i]==0. The order of indices in aRegIdx[] is
** the same as the order of indices on the linked list of indices
-** attached to the table.
+** at pTab->pIndex.
+**
+** The caller must have already opened writeable cursors on the main
+** table and all applicable indices (that is to say, all indices for which
+** aRegIdx[] is not zero). iDataCur is the cursor for the main table when
+** inserting or updating a rowid table, or the cursor for the PRIMARY KEY
+** index when operating on a WITHOUT ROWID table. iIdxCur is the cursor
+** for the first index in the pTab->pIndex list. Cursors for other indices
+** are at iIdxCur+N for the N-th element of the pTab->pIndex list.
**
** This routine also generates code to check constraints. NOT NULL,
** CHECK, and UNIQUE constraints are all checked. If a constraint fails,
@@ -87978,22 +108544,23 @@ insert_cleanup:
** Constraint type Action What Happens
** --------------- ---------- ----------------------------------------
** any ROLLBACK The current transaction is rolled back and
-** sqlite3_exec() returns immediately with a
+** sqlite3_step() returns immediately with a
** return code of SQLITE_CONSTRAINT.
**
** any ABORT Back out changes from the current command
** only (do not do a complete rollback) then
-** cause sqlite3_exec() to return immediately
+** cause sqlite3_step() to return immediately
** with SQLITE_CONSTRAINT.
**
-** any FAIL Sqlite_exec() returns immediately with a
+** any FAIL Sqlite3_step() returns immediately with a
** return code of SQLITE_CONSTRAINT. The
** transaction is not rolled back and any
-** prior changes are retained.
+** changes to prior rows are retained.
**
-** any IGNORE The record number and data is popped from
-** the stack and there is an immediate jump
-** to label ignoreDest.
+** any IGNORE The attempt in insert or update the current
+** row is skipped, without throwing an error.
+** Processing continues with the next row.
+** (There is an immediate jump to ignoreDest.)
**
** NOT NULL REPLACE The NULL value is replace by the default
** value for that column. If the default value
@@ -88008,51 +108575,73 @@ insert_cleanup:
** Or if overrideError==OE_Default, then the pParse->onError parameter
** is used. Or if pParse->onError==OE_Default then the onError value
** for the constraint is used.
-**
-** The calling routine must open a read/write cursor for pTab with
-** cursor number "baseCur". All indices of pTab must also have open
-** read/write cursors with cursor number baseCur+i for the i-th cursor.
-** Except, if there is no possibility of a REPLACE action then
-** cursors do not need to be open for indices where aRegIdx[i]==0.
*/
SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
- Parse *pParse, /* The parser context */
- Table *pTab, /* the table into which we are inserting */
- int baseCur, /* Index of a read/write cursor pointing at pTab */
- int regRowid, /* Index of the range of input registers */
- int *aRegIdx, /* Register used by each index. 0 for unused indices */
- int rowidChng, /* True if the rowid might collide with existing entry */
- int isUpdate, /* True for UPDATE, False for INSERT */
- int overrideError, /* Override onError to this if not OE_Default */
- int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
- int *pbMayReplace /* OUT: Set to true if constraint may cause a replace */
-){
- int i; /* loop counter */
- Vdbe *v; /* VDBE under constrution */
- int nCol; /* Number of columns */
- int onError; /* Conflict resolution strategy */
- int j1; /* Addresss of jump instruction */
- int j2 = 0, j3; /* Addresses of jump instructions */
- int regData; /* Register containing first data column */
- int iCur; /* Table cursor number */
+ Parse *pParse, /* The parser context */
+ Table *pTab, /* The table being inserted or updated */
+ int *aRegIdx, /* Use register aRegIdx[i] for index i. 0 for unused */
+ int iDataCur, /* Canonical data cursor (main table or PK index) */
+ int iIdxCur, /* First index cursor */
+ int regNewData, /* First register in a range holding values to insert */
+ int regOldData, /* Previous content. 0 for INSERTs */
+ u8 pkChng, /* Non-zero if the rowid or PRIMARY KEY changed */
+ u8 overrideError, /* Override onError to this if not OE_Default */
+ int ignoreDest, /* Jump to this label on an OE_Ignore resolution */
+ int *pbMayReplace, /* OUT: Set to true if constraint may cause a replace */
+ int *aiChng /* column i is unchanged if aiChng[i]<0 */
+){
+ Vdbe *v; /* VDBE under constrution */
Index *pIdx; /* Pointer to one of the indices */
+ Index *pPk = 0; /* The PRIMARY KEY index */
+ sqlite3 *db; /* Database connection */
+ int i; /* loop counter */
+ int ix; /* Index loop counter */
+ int nCol; /* Number of columns */
+ int onError; /* Conflict resolution strategy */
+ int addr1; /* Address of jump instruction */
int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
- int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid;
-
+ int nPkField; /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
+ int ipkTop = 0; /* Top of the rowid change constraint check */
+ int ipkBottom = 0; /* Bottom of the rowid change constraint check */
+ u8 isUpdate; /* True if this is an UPDATE operation */
+ u8 bAffinityDone = 0; /* True if the OP_Affinity operation has been run */
+ int regRowid = -1; /* Register holding ROWID value */
+
+ isUpdate = regOldData!=0;
+ db = pParse->db;
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
nCol = pTab->nCol;
- regData = regRowid + 1;
+
+ /* pPk is the PRIMARY KEY index for WITHOUT ROWID tables and NULL for
+ ** normal rowid tables. nPkField is the number of key fields in the
+ ** pPk index or 1 for a rowid table. In other words, nPkField is the
+ ** number of fields in the true primary key of the table. */
+ if( HasRowid(pTab) ){
+ pPk = 0;
+ nPkField = 1;
+ }else{
+ pPk = sqlite3PrimaryKeyIndex(pTab);
+ nPkField = pPk->nKeyCol;
+ }
+
+ /* Record that this module has started */
+ VdbeModuleComment((v, "BEGIN: GenCnstCks(%d,%d,%d,%d,%d)",
+ iDataCur, iIdxCur, regNewData, regOldData, pkChng));
/* Test all NOT NULL constraints.
*/
for(i=0; i<nCol; i++){
if( i==pTab->iPKey ){
+ continue; /* ROWID is never NULL */
+ }
+ if( aiChng && aiChng[i]<0 ){
+ /* Don't bother checking for NOT NULL on columns that do not change */
continue;
}
onError = pTab->aCol[i].notNull;
- if( onError==OE_None ) continue;
+ if( onError==OE_None ) continue; /* This column is allowed to be NULL */
if( overrideError!=OE_Default ){
onError = overrideError;
}else if( onError==OE_Default ){
@@ -88066,25 +108655,28 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
switch( onError ){
case OE_Abort:
sqlite3MayAbort(pParse);
+ /* Fall through */
case OE_Rollback:
case OE_Fail: {
- char *zMsg;
- sqlite3VdbeAddOp3(v, OP_HaltIfNull,
- SQLITE_CONSTRAINT, onError, regData+i);
- zMsg = sqlite3MPrintf(pParse->db, "%s.%s may not be NULL",
- pTab->zName, pTab->aCol[i].zName);
- sqlite3VdbeChangeP4(v, -1, zMsg, P4_DYNAMIC);
+ char *zMsg = sqlite3MPrintf(db, "%s.%s", pTab->zName,
+ pTab->aCol[i].zName);
+ sqlite3VdbeAddOp4(v, OP_HaltIfNull, SQLITE_CONSTRAINT_NOTNULL, onError,
+ regNewData+1+i, zMsg, P4_DYNAMIC);
+ sqlite3VdbeChangeP5(v, P5_ConstraintNotNull);
+ VdbeCoverage(v);
break;
}
case OE_Ignore: {
- sqlite3VdbeAddOp2(v, OP_IsNull, regData+i, ignoreDest);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regNewData+1+i, ignoreDest);
+ VdbeCoverage(v);
break;
}
default: {
assert( onError==OE_Replace );
- j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regData+i);
- sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regData+i);
- sqlite3VdbeJumpHere(v, j1);
+ addr1 = sqlite3VdbeAddOp1(v, OP_NotNull, regNewData+1+i);
+ VdbeCoverage(v);
+ sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regNewData+1+i);
+ sqlite3VdbeJumpHere(v, addr1);
break;
}
}
@@ -88093,37 +108685,74 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
/* Test all CHECK constraints
*/
#ifndef SQLITE_OMIT_CHECK
- if( pTab->pCheck && (pParse->db->flags & SQLITE_IgnoreChecks)==0 ){
- int allOk = sqlite3VdbeMakeLabel(v);
- pParse->ckBase = regData;
- sqlite3ExprIfTrue(pParse, pTab->pCheck, allOk, SQLITE_JUMPIFNULL);
+ if( pTab->pCheck && (db->flags & SQLITE_IgnoreChecks)==0 ){
+ ExprList *pCheck = pTab->pCheck;
+ pParse->ckBase = regNewData+1;
onError = overrideError!=OE_Default ? overrideError : OE_Abort;
- if( onError==OE_Ignore ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
- }else{
- if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
- sqlite3HaltConstraint(pParse, onError, 0, 0);
+ for(i=0; i<pCheck->nExpr; i++){
+ int allOk;
+ Expr *pExpr = pCheck->a[i].pExpr;
+ if( aiChng && checkConstraintUnchanged(pExpr, aiChng, pkChng) ) continue;
+ allOk = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprIfTrue(pParse, pExpr, allOk, SQLITE_JUMPIFNULL);
+ if( onError==OE_Ignore ){
+ sqlite3VdbeGoto(v, ignoreDest);
+ }else{
+ char *zName = pCheck->a[i].zName;
+ if( zName==0 ) zName = pTab->zName;
+ if( onError==OE_Replace ) onError = OE_Abort; /* IMP: R-15569-63625 */
+ sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_CHECK,
+ onError, zName, P4_TRANSIENT,
+ P5_ConstraintCheck);
+ }
+ sqlite3VdbeResolveLabel(v, allOk);
}
- sqlite3VdbeResolveLabel(v, allOk);
}
#endif /* !defined(SQLITE_OMIT_CHECK) */
- /* If we have an INTEGER PRIMARY KEY, make sure the primary key
- ** of the new record does not previously exist. Except, if this
- ** is an UPDATE and the primary key is not changing, that is OK.
+ /* If rowid is changing, make sure the new rowid does not previously
+ ** exist in the table.
*/
- if( rowidChng ){
+ if( pkChng && pPk==0 ){
+ int addrRowidOk = sqlite3VdbeMakeLabel(v);
+
+ /* Figure out what action to take in case of a rowid collision */
onError = pTab->keyConf;
if( overrideError!=OE_Default ){
onError = overrideError;
}else if( onError==OE_Default ){
onError = OE_Abort;
}
-
+
if( isUpdate ){
- j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng);
+ /* pkChng!=0 does not mean that the rowid has change, only that
+ ** it might have changed. Skip the conflict logic below if the rowid
+ ** is unchanged. */
+ sqlite3VdbeAddOp3(v, OP_Eq, regNewData, addrRowidOk, regOldData);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ VdbeCoverage(v);
+ }
+
+ /* If the response to a rowid conflict is REPLACE but the response
+ ** to some other UNIQUE constraint is FAIL or IGNORE, then we need
+ ** to defer the running of the rowid conflict checking until after
+ ** the UNIQUE constraints have run.
+ */
+ if( onError==OE_Replace && overrideError!=OE_Replace ){
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->onError==OE_Ignore || pIdx->onError==OE_Fail ){
+ ipkTop = sqlite3VdbeAddOp0(v, OP_Goto);
+ break;
+ }
+ }
}
- j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid);
+
+ /* Check to see if the new rowid already exists in the table. Skip
+ ** the following conflict logic if it does not. */
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, addrRowidOk, regNewData);
+ VdbeCoverage(v);
+
+ /* Generate code that deals with a rowid collision */
switch( onError ){
default: {
onError = OE_Abort;
@@ -88132,14 +108761,13 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- sqlite3HaltConstraint(
- pParse, onError, "PRIMARY KEY must be unique", P4_STATIC);
+ sqlite3RowidConstraint(pParse, onError, pTab);
break;
}
case OE_Replace: {
/* If there are DELETE triggers on this table and the
** recursive-triggers flag is set, call GenerateRowDelete() to
- ** remove the conflicting row from the the table. This will fire
+ ** remove the conflicting row from the table. This will fire
** the triggers and remove both the table and index b-tree entries.
**
** Otherwise, if there are no triggers or the recursive-triggers
@@ -88160,62 +108788,117 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** table.
*/
Trigger *pTrigger = 0;
- if( pParse->db->flags&SQLITE_RecTriggers ){
+ if( db->flags&SQLITE_RecTriggers ){
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
}
if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){
sqlite3MultiWrite(pParse);
- sqlite3GenerateRowDelete(
- pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace
- );
- }else if( pTab->pIndex ){
- sqlite3MultiWrite(pParse);
- sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0);
+ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
+ regNewData, 1, 0, OE_Replace, 1, -1);
+ }else{
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ if( HasRowid(pTab) ){
+ /* This OP_Delete opcode fires the pre-update-hook only. It does
+ ** not modify the b-tree. It is more efficient to let the coming
+ ** OP_Insert replace the existing entry than it is to delete the
+ ** existing entry and then insert a new one. */
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, OPFLAG_ISNOOP);
+ sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
+ }
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+ if( pTab->pIndex ){
+ sqlite3MultiWrite(pParse);
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur,0,-1);
+ }
}
seenReplace = 1;
break;
}
case OE_Ignore: {
- assert( seenReplace==0 );
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
+ /*assert( seenReplace==0 );*/
+ sqlite3VdbeGoto(v, ignoreDest);
break;
}
}
- sqlite3VdbeJumpHere(v, j3);
- if( isUpdate ){
- sqlite3VdbeJumpHere(v, j2);
+ sqlite3VdbeResolveLabel(v, addrRowidOk);
+ if( ipkTop ){
+ ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
+ sqlite3VdbeJumpHere(v, ipkTop);
}
}
/* Test all UNIQUE constraints by creating entries for each UNIQUE
** index and making sure that duplicate entries do not already exist.
- ** Add the new records to the indices as we go.
- */
- for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){
- int regIdx;
- int regR;
-
- if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */
-
- /* Create a key for accessing the index entry */
- regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1);
+ ** Compute the revised record entries for indices as we go.
+ **
+ ** This loop also handles the case of the PRIMARY KEY index for a
+ ** WITHOUT ROWID table.
+ */
+ for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){
+ int regIdx; /* Range of registers hold conent for pIdx */
+ int regR; /* Range of registers holding conflicting PK */
+ int iThisCur; /* Cursor for this UNIQUE index */
+ int addrUniqueOk; /* Jump here if the UNIQUE constraint is satisfied */
+
+ if( aRegIdx[ix]==0 ) continue; /* Skip indices that do not change */
+ if( bAffinityDone==0 ){
+ sqlite3TableAffinity(v, pTab, regNewData+1);
+ bAffinityDone = 1;
+ }
+ iThisCur = iIdxCur+ix;
+ addrUniqueOk = sqlite3VdbeMakeLabel(v);
+
+ /* Skip partial indices for which the WHERE clause is not true */
+ if( pIdx->pPartIdxWhere ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, aRegIdx[ix]);
+ pParse->ckBase = regNewData+1;
+ sqlite3ExprIfFalseDup(pParse, pIdx->pPartIdxWhere, addrUniqueOk,
+ SQLITE_JUMPIFNULL);
+ pParse->ckBase = 0;
+ }
+
+ /* Create a record for this index entry as it should appear after
+ ** the insert or update. Store that record in the aRegIdx[ix] register
+ */
+ regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn);
for(i=0; i<pIdx->nColumn; i++){
- int idx = pIdx->aiColumn[i];
- if( idx==pTab->iPKey ){
- sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
+ int iField = pIdx->aiColumn[i];
+ int x;
+ if( iField==XN_EXPR ){
+ pParse->ckBase = regNewData+1;
+ sqlite3ExprCodeCopy(pParse, pIdx->aColExpr->a[i].pExpr, regIdx+i);
+ pParse->ckBase = 0;
+ VdbeComment((v, "%s column %d", pIdx->zName, i));
}else{
- sqlite3VdbeAddOp2(v, OP_SCopy, regData+idx, regIdx+i);
+ if( iField==XN_ROWID || iField==pTab->iPKey ){
+ if( regRowid==regIdx+i ) continue; /* ROWID already in regIdx+i */
+ x = regNewData;
+ regRowid = pIdx->pPartIdxWhere ? -1 : regIdx+i;
+ }else{
+ x = iField + regNewData + 1;
+ }
+ sqlite3VdbeAddOp2(v, iField<0 ? OP_IntCopy : OP_SCopy, x, regIdx+i);
+ VdbeComment((v, "%s", iField<0 ? "rowid" : pTab->aCol[iField].zName));
}
}
- sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]);
- sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT);
- sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn, aRegIdx[ix]);
+ VdbeComment((v, "for %s", pIdx->zName));
+ sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn);
- /* Find out what action to take in case there is an indexing conflict */
+ /* In an UPDATE operation, if this index is the PRIMARY KEY index
+ ** of a WITHOUT ROWID table and there has been no change the
+ ** primary key, then no collision is possible. The collision detection
+ ** logic below can all be skipped. */
+ if( isUpdate && pPk==pIdx && pkChng==0 ){
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ continue;
+ }
+
+ /* Find out what action to take in case there is a uniqueness conflict */
onError = pIdx->onError;
if( onError==OE_None ){
- sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
+ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
continue; /* pIdx is not a UNIQUE index */
}
if( overrideError!=OE_Default ){
@@ -88223,18 +108906,66 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
}else if( onError==OE_Default ){
onError = OE_Abort;
}
- if( seenReplace ){
- if( onError==OE_Ignore ) onError = OE_Replace;
- else if( onError==OE_Fail ) onError = OE_Abort;
- }
/* Check to see if the new index entry will be unique */
- regR = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR);
- j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0,
- regR, SQLITE_INT_TO_PTR(regIdx),
- P4_INT32);
- sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1);
+ sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
+ regIdx, pIdx->nKeyCol); VdbeCoverage(v);
+
+ /* Generate code to handle collisions */
+ regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField);
+ if( isUpdate || onError==OE_Replace ){
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
+ /* Conflict only if the rowid of the existing index entry
+ ** is different from old-rowid */
+ if( isUpdate ){
+ sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ VdbeCoverage(v);
+ }
+ }else{
+ int x;
+ /* Extract the PRIMARY KEY from the end of the index entry and
+ ** store it in registers regR..regR+nPk-1 */
+ if( pIdx!=pPk ){
+ for(i=0; i<pPk->nKeyCol; i++){
+ assert( pPk->aiColumn[i]>=0 );
+ x = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[i]);
+ sqlite3VdbeAddOp3(v, OP_Column, iThisCur, x, regR+i);
+ VdbeComment((v, "%s.%s", pTab->zName,
+ pTab->aCol[pPk->aiColumn[i]].zName));
+ }
+ }
+ if( isUpdate ){
+ /* If currently processing the PRIMARY KEY of a WITHOUT ROWID
+ ** table, only conflict if the new PRIMARY KEY values are actually
+ ** different from the old.
+ **
+ ** For a UNIQUE index, only conflict if the PRIMARY KEY values
+ ** of the matched index row are different from the original PRIMARY
+ ** KEY values of this row before the update. */
+ int addrJump = sqlite3VdbeCurrentAddr(v)+pPk->nKeyCol;
+ int op = OP_Ne;
+ int regCmp = (IsPrimaryKeyIndex(pIdx) ? regIdx : regR);
+
+ for(i=0; i<pPk->nKeyCol; i++){
+ char *p4 = (char*)sqlite3LocateCollSeq(pParse, pPk->azColl[i]);
+ x = pPk->aiColumn[i];
+ assert( x>=0 );
+ if( i==(pPk->nKeyCol-1) ){
+ addrJump = addrUniqueOk;
+ op = OP_Eq;
+ }
+ sqlite3VdbeAddOp4(v, op,
+ regOldData+1+x, addrJump, regCmp+i, p4, P4_COLLSEQ
+ );
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ VdbeCoverageIf(v, op==OP_Eq);
+ VdbeCoverageIf(v, op==OP_Ne);
+ }
+ }
+ }
+ }
/* Generate code that executes if the new index entry is not unique */
assert( onError==OE_Rollback || onError==OE_Abort || onError==OE_Fail
@@ -88243,59 +108974,44 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
case OE_Rollback:
case OE_Abort:
case OE_Fail: {
- int j;
- StrAccum errMsg;
- const char *zSep;
- char *zErr;
-
- sqlite3StrAccumInit(&errMsg, 0, 0, 200);
- errMsg.db = pParse->db;
- zSep = pIdx->nColumn>1 ? "columns " : "column ";
- for(j=0; j<pIdx->nColumn; j++){
- char *zCol = pTab->aCol[pIdx->aiColumn[j]].zName;
- sqlite3StrAccumAppend(&errMsg, zSep, -1);
- zSep = ", ";
- sqlite3StrAccumAppend(&errMsg, zCol, -1);
- }
- sqlite3StrAccumAppend(&errMsg,
- pIdx->nColumn>1 ? " are not unique" : " is not unique", -1);
- zErr = sqlite3StrAccumFinish(&errMsg);
- sqlite3HaltConstraint(pParse, onError, zErr, 0);
- sqlite3DbFree(errMsg.db, zErr);
+ sqlite3UniqueConstraint(pParse, onError, pIdx);
break;
}
case OE_Ignore: {
- assert( seenReplace==0 );
- sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest);
+ sqlite3VdbeGoto(v, ignoreDest);
break;
}
default: {
Trigger *pTrigger = 0;
assert( onError==OE_Replace );
sqlite3MultiWrite(pParse);
- if( pParse->db->flags&SQLITE_RecTriggers ){
+ if( db->flags&SQLITE_RecTriggers ){
pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0);
}
- sqlite3GenerateRowDelete(
- pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace
- );
+ sqlite3GenerateRowDelete(pParse, pTab, pTrigger, iDataCur, iIdxCur,
+ regR, nPkField, 0, OE_Replace,
+ (pIdx==pPk ? ONEPASS_SINGLE : ONEPASS_OFF), -1);
seenReplace = 1;
break;
}
}
- sqlite3VdbeJumpHere(v, j3);
- sqlite3ReleaseTempReg(pParse, regR);
+ sqlite3VdbeResolveLabel(v, addrUniqueOk);
+ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn);
+ if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
}
-
- if( pbMayReplace ){
- *pbMayReplace = seenReplace;
+ if( ipkTop ){
+ sqlite3VdbeGoto(v, ipkTop+1);
+ sqlite3VdbeJumpHere(v, ipkBottom);
}
+
+ *pbMayReplace = seenReplace;
+ VdbeModuleComment((v, "END: GenCnstCks(%d)", seenReplace));
}
/*
** This routine generates code to finish the INSERT or UPDATE operation
** that was started by a prior call to sqlite3GenerateConstraintChecks.
-** A consecutive range of registers starting at regRowid contains the
+** A consecutive range of registers starting at regNewData contains the
** rowid and the content to be inserted.
**
** The arguments to this routine should be the same as the first six
@@ -88304,36 +109020,46 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
SQLITE_PRIVATE void sqlite3CompleteInsertion(
Parse *pParse, /* The parser context */
Table *pTab, /* the table into which we are inserting */
- int baseCur, /* Index of a read/write cursor pointing at pTab */
- int regRowid, /* Range of content */
+ int iDataCur, /* Cursor of the canonical data source */
+ int iIdxCur, /* First index cursor */
+ int regNewData, /* Range of content */
int *aRegIdx, /* Register used by each index. 0 for unused indices */
int isUpdate, /* True for UPDATE, False for INSERT */
int appendBias, /* True if this is likely to be an append */
int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */
){
- int i;
- Vdbe *v;
- int nIdx;
- Index *pIdx;
- u8 pik_flags;
- int regData;
- int regRec;
+ Vdbe *v; /* Prepared statements under construction */
+ Index *pIdx; /* An index being inserted or updated */
+ u8 pik_flags; /* flag values passed to the btree insert */
+ int regData; /* Content registers (after the rowid) */
+ int regRec; /* Register holding assembled record for the table */
+ int i; /* Loop counter */
+ u8 bAffinityDone = 0; /* True if OP_Affinity has been run already */
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
assert( pTab->pSelect==0 ); /* This table is not a VIEW */
- for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
- for(i=nIdx-1; i>=0; i--){
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
if( aRegIdx[i]==0 ) continue;
- sqlite3VdbeAddOp2(v, OP_IdxInsert, baseCur+i+1, aRegIdx[i]);
- if( useSeekResult ){
- sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ bAffinityDone = 1;
+ if( pIdx->pPartIdxWhere ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, aRegIdx[i], sqlite3VdbeCurrentAddr(v)+2);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdxCur+i, aRegIdx[i]);
+ pik_flags = 0;
+ if( useSeekResult ) pik_flags = OPFLAG_USESEEKRESULT;
+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
+ assert( pParse->nested==0 );
+ pik_flags |= OPFLAG_NCHANGE;
}
+ sqlite3VdbeChangeP5(v, pik_flags);
}
- regData = regRowid + 1;
+ if( !HasRowid(pTab) ) return;
+ regData = regNewData + 1;
regRec = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec);
- sqlite3TableAffinityStr(v, pTab);
+ if( !bAffinityDone ) sqlite3TableAffinity(v, pTab, 0);
sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol);
if( pParse->nested ){
pik_flags = 0;
@@ -88347,47 +109073,86 @@ SQLITE_PRIVATE void sqlite3CompleteInsertion(
if( useSeekResult ){
pik_flags |= OPFLAG_USESEEKRESULT;
}
- sqlite3VdbeAddOp3(v, OP_Insert, baseCur, regRec, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, iDataCur, regRec, regNewData);
if( !pParse->nested ){
- sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_TRANSIENT);
+ sqlite3VdbeChangeP4(v, -1, (char *)pTab, P4_TABLE);
}
sqlite3VdbeChangeP5(v, pik_flags);
}
/*
-** Generate code that will open cursors for a table and for all
-** indices of that table. The "baseCur" parameter is the cursor number used
-** for the table. Indices are opened on subsequent cursors.
+** Allocate cursors for the pTab table and all its indices and generate
+** code to open and initialized those cursors.
**
-** Return the number of indices on the table.
+** The cursor for the object that contains the complete data (normally
+** the table itself, but the PRIMARY KEY index in the case of a WITHOUT
+** ROWID table) is returned in *piDataCur. The first index cursor is
+** returned in *piIdxCur. The number of indices is returned.
+**
+** Use iBase as the first cursor (either the *piDataCur for rowid tables
+** or the first index for WITHOUT ROWID tables) if it is non-negative.
+** If iBase is negative, then allocate the next available cursor.
+**
+** For a rowid table, *piDataCur will be exactly one less than *piIdxCur.
+** For a WITHOUT ROWID table, *piDataCur will be somewhere in the range
+** of *piIdxCurs, depending on where the PRIMARY KEY index appears on the
+** pTab->pIndex list.
+**
+** If pTab is a virtual table, then this routine is a no-op and the
+** *piDataCur and *piIdxCur values are left uninitialized.
*/
SQLITE_PRIVATE int sqlite3OpenTableAndIndices(
Parse *pParse, /* Parsing context */
Table *pTab, /* Table to be opened */
- int baseCur, /* Cursor number assigned to the table */
- int op /* OP_OpenRead or OP_OpenWrite */
+ int op, /* OP_OpenRead or OP_OpenWrite */
+ u8 p5, /* P5 value for OP_Open* opcodes (except on WITHOUT ROWID) */
+ int iBase, /* Use this for the table cursor, if there is one */
+ u8 *aToOpen, /* If not NULL: boolean for each table and index */
+ int *piDataCur, /* Write the database source cursor number here */
+ int *piIdxCur /* Write the first index cursor number here */
){
int i;
int iDb;
+ int iDataCur;
Index *pIdx;
Vdbe *v;
- if( IsVirtual(pTab) ) return 0;
+ assert( op==OP_OpenRead || op==OP_OpenWrite );
+ assert( op==OP_OpenWrite || p5==0 );
+ if( IsVirtual(pTab) ){
+ /* This routine is a no-op for virtual tables. Leave the output
+ ** variables *piDataCur and *piIdxCur uninitialized so that valgrind
+ ** can detect if they are used by mistake in the caller. */
+ return 0;
+ }
iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema);
v = sqlite3GetVdbe(pParse);
assert( v!=0 );
- sqlite3OpenTable(pParse, baseCur, iDb, pTab, op);
- for(i=1, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
- assert( pIdx->pSchema==pTab->pSchema );
- sqlite3VdbeAddOp4(v, op, i+baseCur, pIdx->tnum, iDb,
- (char*)pKey, P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIdx->zName));
+ if( iBase<0 ) iBase = pParse->nTab;
+ iDataCur = iBase++;
+ if( piDataCur ) *piDataCur = iDataCur;
+ if( HasRowid(pTab) && (aToOpen==0 || aToOpen[0]) ){
+ sqlite3OpenTable(pParse, iDataCur, iDb, pTab, op);
+ }else{
+ sqlite3TableLock(pParse, iDb, pTab->tnum, op==OP_OpenWrite, pTab->zName);
}
- if( pParse->nTab<baseCur+i ){
- pParse->nTab = baseCur+i;
+ if( piIdxCur ) *piIdxCur = iBase;
+ for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
+ int iIdxCur = iBase++;
+ assert( pIdx->pSchema==pTab->pSchema );
+ if( IsPrimaryKeyIndex(pIdx) && !HasRowid(pTab) ){
+ if( piDataCur ) *piDataCur = iIdxCur;
+ p5 = 0;
+ }
+ if( aToOpen==0 || aToOpen[i+1] ){
+ sqlite3VdbeAddOp3(v, op, iIdxCur, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ sqlite3VdbeChangeP5(v, p5);
+ VdbeComment((v, "%s", pIdx->zName));
+ }
}
- return i-1;
+ if( iBase>pParse->nTab ) pParse->nTab = iBase;
+ return i;
}
@@ -88396,7 +109161,7 @@ SQLITE_PRIVATE int sqlite3OpenTableAndIndices(
** The following global variable is incremented whenever the
** transfer optimization is used. This is used for testing
** purposes only - to make sure the transfer optimization really
-** is happening when it is suppose to.
+** is happening when it is supposed to.
*/
SQLITE_API int sqlite3_xferopt_count;
#endif /* SQLITE_TEST */
@@ -88404,20 +109169,6 @@ SQLITE_API int sqlite3_xferopt_count;
#ifndef SQLITE_OMIT_XFER_OPT
/*
-** Check to collation names to see if they are compatible.
-*/
-static int xferCompatibleCollation(const char *z1, const char *z2){
- if( z1==0 ){
- return z2==0;
- }
- if( z2==0 ){
- return 0;
- }
- return sqlite3StrICmp(z1, z2)==0;
-}
-
-
-/*
** Check to see if index pSrc is compatible as a source of data
** for index pDest in an insert transfer optimization. The rules
** for a compatible index:
@@ -88426,28 +109177,39 @@ static int xferCompatibleCollation(const char *z1, const char *z2){
** * The same DESC and ASC markings occurs on all columns
** * The same onError processing (OE_Abort, OE_Ignore, etc)
** * The same collating sequence on each column
+** * The index has the exact same WHERE clause
*/
static int xferCompatibleIndex(Index *pDest, Index *pSrc){
int i;
assert( pDest && pSrc );
assert( pDest->pTable!=pSrc->pTable );
- if( pDest->nColumn!=pSrc->nColumn ){
+ if( pDest->nKeyCol!=pSrc->nKeyCol ){
return 0; /* Different number of columns */
}
if( pDest->onError!=pSrc->onError ){
return 0; /* Different conflict resolution strategies */
}
- for(i=0; i<pSrc->nColumn; i++){
+ for(i=0; i<pSrc->nKeyCol; i++){
if( pSrc->aiColumn[i]!=pDest->aiColumn[i] ){
return 0; /* Different columns indexed */
}
+ if( pSrc->aiColumn[i]==XN_EXPR ){
+ assert( pSrc->aColExpr!=0 && pDest->aColExpr!=0 );
+ if( sqlite3ExprCompare(pSrc->aColExpr->a[i].pExpr,
+ pDest->aColExpr->a[i].pExpr, -1)!=0 ){
+ return 0; /* Different expressions in the index */
+ }
+ }
if( pSrc->aSortOrder[i]!=pDest->aSortOrder[i] ){
return 0; /* Different sort orders */
}
- if( !xferCompatibleCollation(pSrc->azColl[i],pDest->azColl[i]) ){
+ if( sqlite3_stricmp(pSrc->azColl[i],pDest->azColl[i])!=0 ){
return 0; /* Different collating sequences */
}
}
+ if( sqlite3ExprCompare(pSrc->pPartIdxWhere, pDest->pPartIdxWhere, -1) ){
+ return 0; /* Different WHERE clauses */
+ }
/* If no test above fails then the indices must be compatible */
return 1;
@@ -88458,31 +109220,25 @@ static int xferCompatibleIndex(Index *pDest, Index *pSrc){
**
** INSERT INTO tab1 SELECT * FROM tab2;
**
-** This optimization is only attempted if
-**
-** (1) tab1 and tab2 have identical schemas including all the
-** same indices and constraints
-**
-** (2) tab1 and tab2 are different tables
-**
-** (3) There must be no triggers on tab1
+** The xfer optimization transfers raw records from tab2 over to tab1.
+** Columns are not decoded and reassembled, which greatly improves
+** performance. Raw index records are transferred in the same way.
**
-** (4) The result set of the SELECT statement is "*"
+** The xfer optimization is only attempted if tab1 and tab2 are compatible.
+** There are lots of rules for determining compatibility - see comments
+** embedded in the code for details.
**
-** (5) The SELECT statement has no WHERE, HAVING, ORDER BY, GROUP BY,
-** or LIMIT clause.
+** This routine returns TRUE if the optimization is guaranteed to be used.
+** Sometimes the xfer optimization will only work if the destination table
+** is empty - a factor that can only be determined at run-time. In that
+** case, this routine generates code for the xfer optimization but also
+** does a test to see if the destination table is empty and jumps over the
+** xfer optimization code if the test fails. In that case, this routine
+** returns FALSE so that the caller will know to go ahead and generate
+** an unoptimized transfer. This routine also returns FALSE if there
+** is no chance that the xfer optimization can be applied.
**
-** (6) The SELECT statement is a simple (not a compound) select that
-** contains only tab2 in its FROM clause
-**
-** This method for implementing the INSERT transfers raw records from
-** tab2 over to tab1. The columns are not decoded. Raw records from
-** the indices of tab2 are transfered to tab1 as well. In so doing,
-** the resulting tab1 has much less fragmentation.
-**
-** This routine returns TRUE if the optimization is attempted. If any
-** of the conditions above fail so that the optimization should not
-** be attempted, then this routine returns FALSE.
+** This optimization is particularly useful at making VACUUM run faster.
*/
static int xferOptimization(
Parse *pParse, /* Parser context */
@@ -88491,6 +109247,7 @@ static int xferOptimization(
int onError, /* How to handle constraint errors */
int iDbDest /* The database of pDest */
){
+ sqlite3 *db = pParse->db;
ExprList *pEList; /* The result set of the SELECT */
Table *pSrc; /* The table in the FROM clause of SELECT */
Index *pSrcIdx, *pDestIdx; /* Source and destination indices */
@@ -88499,10 +109256,9 @@ static int xferOptimization(
int iDbSrc; /* The database of pSrc */
int iSrc, iDest; /* Cursors from source and destination */
int addr1, addr2; /* Loop addresses */
- int emptyDestTest; /* Address of test for empty pDest */
- int emptySrcTest; /* Address of test for empty pSrc */
+ int emptyDestTest = 0; /* Address of test for empty pDest */
+ int emptySrcTest = 0; /* Address of test for empty pSrc */
Vdbe *v; /* The VDBE we are building */
- KeyInfo *pKey; /* Key information for an index */
int regAutoinc; /* Memory register used by AUTOINC */
int destHasUniqueIdx = 0; /* True if pDest has a UNIQUE index */
int regData, regRowid; /* Registers holding data and rowid */
@@ -88510,6 +109266,12 @@ static int xferOptimization(
if( pSelect==0 ){
return 0; /* Must be of the form INSERT INTO ... SELECT ... */
}
+ if( pParse->pWith || pSelect->pWith ){
+ /* Do not attempt to process this query if there are an WITH clauses
+ ** attached to it. Proceeding may generate a false "no such table: xxx"
+ ** error if pSelect reads from a CTE named "xxx". */
+ return 0;
+ }
if( sqlite3TriggerList(pParse, pDest) ){
return 0; /* tab1 must not have triggers */
}
@@ -88519,10 +109281,8 @@ static int xferOptimization(
}
#endif
if( onError==OE_Default ){
- onError = OE_Abort;
- }
- if( onError!=OE_Abort && onError!=OE_Rollback ){
- return 0; /* Cannot do OR REPLACE or OR IGNORE or OR FAIL */
+ if( pDest->iPKey>=0 ) onError = pDest->keyConf;
+ if( onError==OE_Default ) onError = OE_Abort;
}
assert(pSelect->pSrc); /* allocated even if there is no FROM clause */
if( pSelect->pSrc->nSrc!=1 ){
@@ -88558,7 +109318,7 @@ static int xferOptimization(
return 0; /* The result set must have exactly one column */
}
assert( pEList->a[0].pExpr );
- if( pEList->a[0].pExpr->op!=TK_ALL ){
+ if( pEList->a[0].pExpr->op!=TK_ASTERISK ){
return 0; /* The result set must be the special operator "*" */
}
@@ -88567,13 +109327,16 @@ static int xferOptimization(
** we have to check the semantics.
*/
pItem = pSelect->pSrc->a;
- pSrc = sqlite3LocateTable(pParse, 0, pItem->zName, pItem->zDatabase);
+ pSrc = sqlite3LocateTableItem(pParse, 0, pItem);
if( pSrc==0 ){
return 0; /* FROM clause does not contain a real table */
}
if( pSrc==pDest ){
return 0; /* tab1 and tab2 may not be the same table */
}
+ if( HasRowid(pDest)!=HasRowid(pSrc) ){
+ return 0; /* source and destination must both be WITHOUT ROWID or not */
+ }
#ifndef SQLITE_OMIT_VIRTUALTABLE
if( pSrc->tabFlags & TF_Virtual ){
return 0; /* tab2 must not be a virtual table */
@@ -88589,18 +109352,38 @@ static int xferOptimization(
return 0; /* Both tables must have the same INTEGER PRIMARY KEY */
}
for(i=0; i<pDest->nCol; i++){
- if( pDest->aCol[i].affinity!=pSrc->aCol[i].affinity ){
+ Column *pDestCol = &pDest->aCol[i];
+ Column *pSrcCol = &pSrc->aCol[i];
+#ifdef SQLITE_ENABLE_HIDDEN_COLUMNS
+ if( (db->flags & SQLITE_Vacuum)==0
+ && (pDestCol->colFlags | pSrcCol->colFlags) & COLFLAG_HIDDEN
+ ){
+ return 0; /* Neither table may have __hidden__ columns */
+ }
+#endif
+ if( pDestCol->affinity!=pSrcCol->affinity ){
return 0; /* Affinity must be the same on all columns */
}
- if( !xferCompatibleCollation(pDest->aCol[i].zColl, pSrc->aCol[i].zColl) ){
+ if( sqlite3_stricmp(pDestCol->zColl, pSrcCol->zColl)!=0 ){
return 0; /* Collating sequence must be the same on all columns */
}
- if( pDest->aCol[i].notNull && !pSrc->aCol[i].notNull ){
+ if( pDestCol->notNull && !pSrcCol->notNull ){
return 0; /* tab2 must be NOT NULL if tab1 is */
}
+ /* Default values for second and subsequent columns need to match. */
+ if( i>0 ){
+ assert( pDestCol->pDflt==0 || pDestCol->pDflt->op==TK_SPAN );
+ assert( pSrcCol->pDflt==0 || pSrcCol->pDflt->op==TK_SPAN );
+ if( (pDestCol->pDflt==0)!=(pSrcCol->pDflt==0)
+ || (pDestCol->pDflt && strcmp(pDestCol->pDflt->u.zToken,
+ pSrcCol->pDflt->u.zToken)!=0)
+ ){
+ return 0; /* Default values must be the same for all columns */
+ }
+ }
}
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
- if( pDestIdx->onError!=OE_None ){
+ if( IsUniqueIndex(pDestIdx) ){
destHasUniqueIdx = 1;
}
for(pSrcIdx=pSrc->pIndex; pSrcIdx; pSrcIdx=pSrcIdx->pNext){
@@ -88611,7 +109394,7 @@ static int xferOptimization(
}
}
#ifndef SQLITE_OMIT_CHECK
- if( pDest->pCheck && sqlite3ExprCompare(pSrc->pCheck, pDest->pCheck) ){
+ if( pDest->pCheck && sqlite3ExprListCompare(pSrc->pCheck,pDest->pCheck,-1) ){
return 0; /* Tables have different CHECK constraints. Ticket #2252 */
}
#endif
@@ -88623,95 +109406,140 @@ static int xferOptimization(
** the extra complication to make this rule less restrictive is probably
** not worth the effort. Ticket [6284df89debdfa61db8073e062908af0c9b6118e]
*/
- if( (pParse->db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
+ if( (db->flags & SQLITE_ForeignKeys)!=0 && pDest->pFKey!=0 ){
return 0;
}
#endif
+ if( (db->flags & SQLITE_CountRows)!=0 ){
+ return 0; /* xfer opt does not play well with PRAGMA count_changes */
+ }
- /* If we get this far, it means either:
- **
- ** * We can always do the transfer if the table contains an
- ** an integer primary key
- **
- ** * We can conditionally do the transfer if the destination
- ** table is empty.
+ /* If we get this far, it means that the xfer optimization is at
+ ** least a possibility, though it might only work if the destination
+ ** table (tab1) is initially empty.
*/
#ifdef SQLITE_TEST
sqlite3_xferopt_count++;
#endif
- iDbSrc = sqlite3SchemaToIndex(pParse->db, pSrc->pSchema);
+ iDbSrc = sqlite3SchemaToIndex(db, pSrc->pSchema);
v = sqlite3GetVdbe(pParse);
sqlite3CodeVerifySchema(pParse, iDbSrc);
iSrc = pParse->nTab++;
iDest = pParse->nTab++;
regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
+ regData = sqlite3GetTempReg(pParse);
+ regRowid = sqlite3GetTempReg(pParse);
sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
- if( (pDest->iPKey<0 && pDest->pIndex!=0) || destHasUniqueIdx ){
- /* If tables do not have an INTEGER PRIMARY KEY and there
- ** are indices to be copied and the destination is not empty,
- ** we have to disallow the transfer optimization because the
- ** the rowids might change which will mess up indexing.
+ assert( HasRowid(pDest) || destHasUniqueIdx );
+ if( (db->flags & SQLITE_Vacuum)==0 && (
+ (pDest->iPKey<0 && pDest->pIndex!=0) /* (1) */
+ || destHasUniqueIdx /* (2) */
+ || (onError!=OE_Abort && onError!=OE_Rollback) /* (3) */
+ )){
+ /* In some circumstances, we are able to run the xfer optimization
+ ** only if the destination table is initially empty. Unless the
+ ** SQLITE_Vacuum flag is set, this block generates code to make
+ ** that determination. If SQLITE_Vacuum is set, then the destination
+ ** table is always empty.
+ **
+ ** Conditions under which the destination must be empty:
+ **
+ ** (1) There is no INTEGER PRIMARY KEY but there are indices.
+ ** (If the destination is not initially empty, the rowid fields
+ ** of index entries might need to change.)
+ **
+ ** (2) The destination has a unique index. (The xfer optimization
+ ** is unable to test uniqueness.)
**
- ** Or if the destination has a UNIQUE index and is not empty,
- ** we also disallow the transfer optimization because we cannot
- ** insure that all entries in the union of DEST and SRC will be
- ** unique.
+ ** (3) onError is something other than OE_Abort and OE_Rollback.
*/
- addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0);
- emptyDestTest = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iDest, 0); VdbeCoverage(v);
+ emptyDestTest = sqlite3VdbeAddOp0(v, OP_Goto);
sqlite3VdbeJumpHere(v, addr1);
- }else{
- emptyDestTest = 0;
}
- sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
- emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
- regData = sqlite3GetTempReg(pParse);
- regRowid = sqlite3GetTempReg(pParse);
- if( pDest->iPKey>=0 ){
- addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
- addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
- sqlite3HaltConstraint(
- pParse, onError, "PRIMARY KEY must be unique", P4_STATIC);
- sqlite3VdbeJumpHere(v, addr2);
- autoIncStep(pParse, regAutoinc, regRowid);
- }else if( pDest->pIndex==0 ){
- addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
+ if( HasRowid(pSrc) ){
+ sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
+ emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
+ if( pDest->iPKey>=0 ){
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
+ addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
+ VdbeCoverage(v);
+ sqlite3RowidConstraint(pParse, onError, pDest);
+ sqlite3VdbeJumpHere(v, addr2);
+ autoIncStep(pParse, regAutoinc, regRowid);
+ }else if( pDest->pIndex==0 ){
+ addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
+ }else{
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
+ assert( (pDest->tabFlags & TF_Autoincrement)==0 );
+ }
+ sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
+ sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
+ (char*)pDest, P4_TABLE);
+ sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
+ sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
}else{
- addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
- assert( (pDest->tabFlags & TF_Autoincrement)==0 );
+ sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName);
+ sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName);
}
- sqlite3VdbeAddOp2(v, OP_RowData, iSrc, regData);
- sqlite3VdbeAddOp3(v, OP_Insert, iDest, regData, regRowid);
- sqlite3VdbeChangeP5(v, OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND);
- sqlite3VdbeChangeP4(v, -1, pDest->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1);
for(pDestIdx=pDest->pIndex; pDestIdx; pDestIdx=pDestIdx->pNext){
+ u8 idxInsFlags = 0;
for(pSrcIdx=pSrc->pIndex; ALWAYS(pSrcIdx); pSrcIdx=pSrcIdx->pNext){
if( xferCompatibleIndex(pDestIdx, pSrcIdx) ) break;
}
assert( pSrcIdx );
- sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
- sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
- pKey = sqlite3IndexKeyinfo(pParse, pSrcIdx);
- sqlite3VdbeAddOp4(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc,
- (char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp3(v, OP_OpenRead, iSrc, pSrcIdx->tnum, iDbSrc);
+ sqlite3VdbeSetP4KeyInfo(pParse, pSrcIdx);
VdbeComment((v, "%s", pSrcIdx->zName));
- pKey = sqlite3IndexKeyinfo(pParse, pDestIdx);
- sqlite3VdbeAddOp4(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest,
- (char*)pKey, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, iDest, pDestIdx->tnum, iDbDest);
+ sqlite3VdbeSetP4KeyInfo(pParse, pDestIdx);
+ sqlite3VdbeChangeP5(v, OPFLAG_BULKCSR);
VdbeComment((v, "%s", pDestIdx->zName));
- addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0);
+ addr1 = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_RowKey, iSrc, regData);
+ if( db->flags & SQLITE_Vacuum ){
+ /* This INSERT command is part of a VACUUM operation, which guarantees
+ ** that the destination table is empty. If all indexed columns use
+ ** collation sequence BINARY, then it can also be assumed that the
+ ** index will be populated by inserting keys in strictly sorted
+ ** order. In this case, instead of seeking within the b-tree as part
+ ** of every OP_IdxInsert opcode, an OP_Last is added before the
+ ** OP_IdxInsert to seek to the point within the b-tree where each key
+ ** should be inserted. This is faster.
+ **
+ ** If any of the indexed columns use a collation sequence other than
+ ** BINARY, this optimization is disabled. This is because the user
+ ** might change the definition of a collation sequence and then run
+ ** a VACUUM command. In that case keys may not be written in strictly
+ ** sorted order. */
+ for(i=0; i<pSrcIdx->nColumn; i++){
+ const char *zColl = pSrcIdx->azColl[i];
+ assert( sqlite3_stricmp(sqlite3StrBINARY, zColl)!=0
+ || sqlite3StrBINARY==zColl );
+ if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break;
+ }
+ if( i==pSrcIdx->nColumn ){
+ idxInsFlags = OPFLAG_USESEEKRESULT;
+ sqlite3VdbeAddOp3(v, OP_Last, iDest, 0, -1);
+ }
+ }
+ if( !HasRowid(pSrc) && pDestIdx->idxType==2 ){
+ idxInsFlags |= OPFLAG_NCHANGE;
+ }
sqlite3VdbeAddOp3(v, OP_IdxInsert, iDest, regData, 1);
- sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1);
+ sqlite3VdbeChangeP5(v, idxInsFlags);
+ sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr1);
+ sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
+ sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
}
- sqlite3VdbeJumpHere(v, emptySrcTest);
+ if( emptySrcTest ) sqlite3VdbeJumpHere(v, emptySrcTest);
sqlite3ReleaseTempReg(pParse, regRowid);
sqlite3ReleaseTempReg(pParse, regData);
- sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
- sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
if( emptyDestTest ){
+ sqlite3AutoincrementEnd(pParse);
sqlite3VdbeAddOp2(v, OP_Halt, SQLITE_OK, 0);
sqlite3VdbeJumpHere(v, emptyDestTest);
sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
@@ -88741,6 +109569,7 @@ static int xferOptimization(
** accessed by users of the library.
*/
+/* #include "sqliteInt.h" */
/*
** Execute SQL code. Return one of the SQLITE_ success/failure
@@ -88763,20 +109592,19 @@ SQLITE_API int sqlite3_exec(
const char *zLeftover; /* Tail of unprocessed SQL */
sqlite3_stmt *pStmt = 0; /* The current SQL statement */
char **azCols = 0; /* Names of result columns */
- int nRetry = 0; /* Number of retry attempts */
int callbackIsInit; /* True if callback data is initialized */
if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
if( zSql==0 ) zSql = "";
sqlite3_mutex_enter(db->mutex);
- sqlite3Error(db, SQLITE_OK, 0);
- while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){
+ sqlite3Error(db, SQLITE_OK);
+ while( rc==SQLITE_OK && zSql[0] ){
int nCol;
char **azVals = 0;
pStmt = 0;
- rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover);
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);
assert( rc==SQLITE_OK || pStmt==0 );
if( rc!=SQLITE_OK ){
continue;
@@ -88816,16 +109644,19 @@ SQLITE_API int sqlite3_exec(
for(i=0; i<nCol; i++){
azVals[i] = (char *)sqlite3_column_text(pStmt, i);
if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
goto exec_out;
}
}
}
if( xCallback(pArg, nCol, azVals, azCols) ){
+ /* EVIDENCE-OF: R-38229-40159 If the callback function to
+ ** sqlite3_exec() returns non-zero, then sqlite3_exec() will
+ ** return SQLITE_ABORT. */
rc = SQLITE_ABORT;
sqlite3VdbeFinalize((Vdbe *)pStmt);
pStmt = 0;
- sqlite3Error(db, SQLITE_ABORT, 0);
+ sqlite3Error(db, SQLITE_ABORT);
goto exec_out;
}
}
@@ -88833,11 +109664,8 @@ SQLITE_API int sqlite3_exec(
if( rc!=SQLITE_ROW ){
rc = sqlite3VdbeFinalize((Vdbe *)pStmt);
pStmt = 0;
- if( rc!=SQLITE_SCHEMA ){
- nRetry = 0;
- zSql = zLeftover;
- while( sqlite3Isspace(zSql[0]) ) zSql++;
- }
+ zSql = zLeftover;
+ while( sqlite3Isspace(zSql[0]) ) zSql++;
break;
}
}
@@ -88851,14 +109679,14 @@ exec_out:
sqlite3DbFree(db, azCols);
rc = sqlite3ApiExit(db, rc);
- if( rc!=SQLITE_OK && ALWAYS(rc==sqlite3_errcode(db)) && pzErrMsg ){
+ if( rc!=SQLITE_OK && pzErrMsg ){
int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db));
*pzErrMsg = sqlite3Malloc(nErrMsg);
if( *pzErrMsg ){
memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg);
}else{
- rc = SQLITE_NOMEM;
- sqlite3Error(db, SQLITE_NOMEM, 0);
+ rc = SQLITE_NOMEM_BKPT;
+ sqlite3Error(db, SQLITE_NOMEM);
}
}else if( pzErrMsg ){
*pzErrMsg = 0;
@@ -88908,10 +109736,9 @@ exec_out:
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
-#ifndef _SQLITE3EXT_H_
-#define _SQLITE3EXT_H_
-
-typedef struct sqlite3_api_routines sqlite3_api_routines;
+#ifndef SQLITE3EXT_H
+#define SQLITE3EXT_H
+/* #include "sqlite3.h" */
/*
** The following structure holds pointers to all of the SQLite API
@@ -88920,7 +109747,7 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
-** versions of SQLite will not be able to load each others' shared
+** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
@@ -88941,8 +109768,10 @@ struct sqlite3_api_routines {
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
- int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
- int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
+ int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
+ int eTextRep,const char*));
+ int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
+ int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
@@ -88967,10 +109796,18 @@ struct sqlite3_api_routines {
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
- int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
- int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
- int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
- int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
+ int (*create_collation)(sqlite3*,const char*,int,void*,
+ int(*)(void*,int,const void*,int,const void*));
+ int (*create_collation16)(sqlite3*,const void*,int,void*,
+ int(*)(void*,int,const void*,int,const void*));
+ int (*create_function)(sqlite3*,const char*,int,int,void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*));
+ int (*create_function16)(sqlite3*,const void*,int,int,void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
@@ -89015,16 +109852,19 @@ struct sqlite3_api_routines {
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
- int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
+ int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
+ const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
- int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
+ int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
+ char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
- void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
+ void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
+ sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
@@ -89046,15 +109886,19 @@ struct sqlite3_api_routines {
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
- int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
+ int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
+ void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
- int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
+ int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
+ int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
- int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
+ int (*create_collation_v2)(sqlite3*,const char*,int,void*,
+ int(*)(void*,int,const void*,int,const void*),
+ void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
@@ -89090,7 +109934,11 @@ struct sqlite3_api_routines {
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
- int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
+ int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*),
+ void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
@@ -89107,11 +109955,69 @@ struct sqlite3_api_routines {
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
+ /* Version 3.7.16 and later */
+ int (*close_v2)(sqlite3*);
+ const char *(*db_filename)(sqlite3*,const char*);
+ int (*db_readonly)(sqlite3*,const char*);
+ int (*db_release_memory)(sqlite3*);
+ const char *(*errstr)(int);
+ int (*stmt_busy)(sqlite3_stmt*);
+ int (*stmt_readonly)(sqlite3_stmt*);
+ int (*stricmp)(const char*,const char*);
+ int (*uri_boolean)(const char*,const char*,int);
+ sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
+ const char *(*uri_parameter)(const char*,const char*);
+ char *(*vsnprintf)(int,char*,const char*,va_list);
+ int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
+ /* Version 3.8.7 and later */
+ int (*auto_extension)(void(*)(void));
+ int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
+ void(*)(void*));
+ int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
+ void(*)(void*),unsigned char);
+ int (*cancel_auto_extension)(void(*)(void));
+ int (*load_extension)(sqlite3*,const char*,const char*,char**);
+ void *(*malloc64)(sqlite3_uint64);
+ sqlite3_uint64 (*msize)(void*);
+ void *(*realloc64)(void*,sqlite3_uint64);
+ void (*reset_auto_extension)(void);
+ void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
+ void(*)(void*));
+ void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
+ void(*)(void*), unsigned char);
+ int (*strglob)(const char*,const char*);
+ /* Version 3.8.11 and later */
+ sqlite3_value *(*value_dup)(const sqlite3_value*);
+ void (*value_free)(sqlite3_value*);
+ int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
+ int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
+ /* Version 3.9.0 and later */
+ unsigned int (*value_subtype)(sqlite3_value*);
+ void (*result_subtype)(sqlite3_context*,unsigned int);
+ /* Version 3.10.0 and later */
+ int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
+ int (*strlike)(const char*,const char*,unsigned int);
+ int (*db_cacheflush)(sqlite3*);
+ /* Version 3.12.0 and later */
+ int (*system_errno)(sqlite3*);
+ /* Version 3.14.0 and later */
+ int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
+ char *(*expanded_sql)(sqlite3_stmt*);
};
/*
+** This is the function signature used for all extension entry points. It
+** is also defined in the file "loadext.c".
+*/
+typedef int (*sqlite3_loadext_entry)(
+ sqlite3 *db, /* Handle to the database. */
+ char **pzErrMsg, /* Used to set error string on failure. */
+ const sqlite3_api_routines *pThunk /* Extension API function pointers. */
+);
+
+/*
** The following macros redefine the API routines so that they are
-** redirected throught the global sqlite3_api structure.
+** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
@@ -89120,7 +110026,7 @@ struct sqlite3_api_routines {
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
-#ifndef SQLITE_CORE
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
@@ -89247,6 +110153,7 @@ struct sqlite3_api_routines {
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
+#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
@@ -89310,19 +110217,75 @@ struct sqlite3_api_routines {
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
-#endif /* SQLITE_CORE */
-
-#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
-#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
+/* Version 3.7.16 and later */
+#define sqlite3_close_v2 sqlite3_api->close_v2
+#define sqlite3_db_filename sqlite3_api->db_filename
+#define sqlite3_db_readonly sqlite3_api->db_readonly
+#define sqlite3_db_release_memory sqlite3_api->db_release_memory
+#define sqlite3_errstr sqlite3_api->errstr
+#define sqlite3_stmt_busy sqlite3_api->stmt_busy
+#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
+#define sqlite3_stricmp sqlite3_api->stricmp
+#define sqlite3_uri_boolean sqlite3_api->uri_boolean
+#define sqlite3_uri_int64 sqlite3_api->uri_int64
+#define sqlite3_uri_parameter sqlite3_api->uri_parameter
+#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
+#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
+/* Version 3.8.7 and later */
+#define sqlite3_auto_extension sqlite3_api->auto_extension
+#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
+#define sqlite3_bind_text64 sqlite3_api->bind_text64
+#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
+#define sqlite3_load_extension sqlite3_api->load_extension
+#define sqlite3_malloc64 sqlite3_api->malloc64
+#define sqlite3_msize sqlite3_api->msize
+#define sqlite3_realloc64 sqlite3_api->realloc64
+#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
+#define sqlite3_result_blob64 sqlite3_api->result_blob64
+#define sqlite3_result_text64 sqlite3_api->result_text64
+#define sqlite3_strglob sqlite3_api->strglob
+/* Version 3.8.11 and later */
+#define sqlite3_value_dup sqlite3_api->value_dup
+#define sqlite3_value_free sqlite3_api->value_free
+#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
+#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
+/* Version 3.9.0 and later */
+#define sqlite3_value_subtype sqlite3_api->value_subtype
+#define sqlite3_result_subtype sqlite3_api->result_subtype
+/* Version 3.10.0 and later */
+#define sqlite3_status64 sqlite3_api->status64
+#define sqlite3_strlike sqlite3_api->strlike
+#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
+/* Version 3.12.0 and later */
+#define sqlite3_system_errno sqlite3_api->system_errno
+/* Version 3.14.0 and later */
+#define sqlite3_trace_v2 sqlite3_api->trace_v2
+#define sqlite3_expanded_sql sqlite3_api->expanded_sql
+#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
+
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ /* This case when the file really is being compiled as a loadable
+ ** extension */
+# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
+# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
+# define SQLITE_EXTENSION_INIT3 \
+ extern const sqlite3_api_routines *sqlite3_api;
+#else
+ /* This case when the file is being statically linked into the
+ ** application */
+# define SQLITE_EXTENSION_INIT1 /*no-op*/
+# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
+# define SQLITE_EXTENSION_INIT3 /*no-op*/
+#endif
-#endif /* _SQLITE3EXT_H_ */
+#endif /* SQLITE3EXT_H */
/************** End of sqlite3ext.h ******************************************/
/************** Continuing where we left off in loadext.c ********************/
+/* #include "sqliteInt.h" */
/* #include <string.h> */
#ifndef SQLITE_OMIT_LOAD_EXTENSION
-
/*
** Some API routines are omitted when various features are
** excluded from a build of SQLite. Substitute a NULL pointer
@@ -89335,7 +110298,6 @@ struct sqlite3_api_routines {
# define sqlite3_column_table_name16 0
# define sqlite3_column_origin_name 0
# define sqlite3_column_origin_name16 0
-# define sqlite3_table_column_metadata 0
#endif
#ifdef SQLITE_OMIT_AUTHORIZATION
@@ -89393,7 +110355,7 @@ struct sqlite3_api_routines {
# define sqlite3_enable_shared_cache 0
#endif
-#ifdef SQLITE_OMIT_TRACE
+#if defined(SQLITE_OMIT_TRACE) || defined(SQLITE_OMIT_DEPRECATED)
# define sqlite3_profile 0
# define sqlite3_trace 0
#endif
@@ -89413,6 +110375,10 @@ struct sqlite3_api_routines {
#define sqlite3_blob_reopen 0
#endif
+#if defined(SQLITE_OMIT_TRACE)
+# define sqlite3_trace_v2 0
+#endif
+
/*
** The following structure contains pointers to all SQLite API routines.
** A pointer to this structure is passed into extensions when they are
@@ -89679,6 +110645,49 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_blob_reopen,
sqlite3_vtab_config,
sqlite3_vtab_on_conflict,
+ sqlite3_close_v2,
+ sqlite3_db_filename,
+ sqlite3_db_readonly,
+ sqlite3_db_release_memory,
+ sqlite3_errstr,
+ sqlite3_stmt_busy,
+ sqlite3_stmt_readonly,
+ sqlite3_stricmp,
+ sqlite3_uri_boolean,
+ sqlite3_uri_int64,
+ sqlite3_uri_parameter,
+ sqlite3_vsnprintf,
+ sqlite3_wal_checkpoint_v2,
+ /* Version 3.8.7 and later */
+ sqlite3_auto_extension,
+ sqlite3_bind_blob64,
+ sqlite3_bind_text64,
+ sqlite3_cancel_auto_extension,
+ sqlite3_load_extension,
+ sqlite3_malloc64,
+ sqlite3_msize,
+ sqlite3_realloc64,
+ sqlite3_reset_auto_extension,
+ sqlite3_result_blob64,
+ sqlite3_result_text64,
+ sqlite3_strglob,
+ /* Version 3.8.11 and later */
+ (sqlite3_value*(*)(const sqlite3_value*))sqlite3_value_dup,
+ sqlite3_value_free,
+ sqlite3_result_zeroblob64,
+ sqlite3_bind_zeroblob64,
+ /* Version 3.9.0 and later */
+ sqlite3_value_subtype,
+ sqlite3_result_subtype,
+ /* Version 3.10.0 and later */
+ sqlite3_status64,
+ sqlite3_strlike,
+ sqlite3_db_cacheflush,
+ /* Version 3.12.0 and later */
+ sqlite3_system_errno,
+ /* Version 3.14.0 and later */
+ sqlite3_trace_v2,
+ sqlite3_expanded_sql
};
/*
@@ -89701,18 +110710,35 @@ static int sqlite3LoadExtension(
){
sqlite3_vfs *pVfs = db->pVfs;
void *handle;
- int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
+ sqlite3_loadext_entry xInit;
char *zErrmsg = 0;
+ const char *zEntry;
+ char *zAltEntry = 0;
void **aHandle;
- const int nMsg = 300;
+ u64 nMsg = 300 + sqlite3Strlen30(zFile);
+ int ii;
+ int rc;
+
+ /* Shared library endings to try if zFile cannot be loaded as written */
+ static const char *azEndings[] = {
+#if SQLITE_OS_WIN
+ "dll"
+#elif defined(__APPLE__)
+ "dylib"
+#else
+ "so"
+#endif
+ };
+
if( pzErrMsg ) *pzErrMsg = 0;
/* Ticket #1863. To avoid a creating security problems for older
** applications that relink against newer versions of SQLite, the
** ability to run load_extension is turned off by default. One
- ** must call sqlite3_enable_load_extension() to turn on extension
- ** loading. Otherwise you get the following error.
+ ** must call either sqlite3_enable_load_extension(db) or
+ ** sqlite3_db_config(db, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, 1, 0)
+ ** to turn on extension loading.
*/
if( (db->flags & SQLITE_LoadExtension)==0 ){
if( pzErrMsg ){
@@ -89721,14 +110747,20 @@ static int sqlite3LoadExtension(
return SQLITE_ERROR;
}
- if( zProc==0 ){
- zProc = "sqlite3_extension_init";
- }
+ zEntry = zProc ? zProc : "sqlite3_extension_init";
handle = sqlite3OsDlOpen(pVfs, zFile);
+#if SQLITE_OS_UNIX || SQLITE_OS_WIN
+ for(ii=0; ii<ArraySize(azEndings) && handle==0; ii++){
+ char *zAltFile = sqlite3_mprintf("%s.%s", zFile, azEndings[ii]);
+ if( zAltFile==0 ) return SQLITE_NOMEM_BKPT;
+ handle = sqlite3OsDlOpen(pVfs, zAltFile);
+ sqlite3_free(zAltFile);
+ }
+#endif
if( handle==0 ){
if( pzErrMsg ){
- *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg);
+ *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg);
if( zErrmsg ){
sqlite3_snprintf(nMsg, zErrmsg,
"unable to open shared library [%s]", zFile);
@@ -89737,20 +110769,58 @@ static int sqlite3LoadExtension(
}
return SQLITE_ERROR;
}
- xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
- sqlite3OsDlSym(pVfs, handle, zProc);
+ xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
+
+ /* If no entry point was specified and the default legacy
+ ** entry point name "sqlite3_extension_init" was not found, then
+ ** construct an entry point name "sqlite3_X_init" where the X is
+ ** replaced by the lowercase value of every ASCII alphabetic
+ ** character in the filename after the last "/" upto the first ".",
+ ** and eliding the first three characters if they are "lib".
+ ** Examples:
+ **
+ ** /usr/local/lib/libExample5.4.3.so ==> sqlite3_example_init
+ ** C:/lib/mathfuncs.dll ==> sqlite3_mathfuncs_init
+ */
+ if( xInit==0 && zProc==0 ){
+ int iFile, iEntry, c;
+ int ncFile = sqlite3Strlen30(zFile);
+ zAltEntry = sqlite3_malloc64(ncFile+30);
+ if( zAltEntry==0 ){
+ sqlite3OsDlClose(pVfs, handle);
+ return SQLITE_NOMEM_BKPT;
+ }
+ memcpy(zAltEntry, "sqlite3_", 8);
+ for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
+ iFile++;
+ if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
+ for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
+ if( sqlite3Isalpha(c) ){
+ zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c];
+ }
+ }
+ memcpy(zAltEntry+iEntry, "_init", 6);
+ zEntry = zAltEntry;
+ xInit = (sqlite3_loadext_entry)sqlite3OsDlSym(pVfs, handle, zEntry);
+ }
if( xInit==0 ){
if( pzErrMsg ){
- *pzErrMsg = zErrmsg = sqlite3_malloc(nMsg);
+ nMsg += sqlite3Strlen30(zEntry);
+ *pzErrMsg = zErrmsg = sqlite3_malloc64(nMsg);
if( zErrmsg ){
sqlite3_snprintf(nMsg, zErrmsg,
- "no entry point [%s] in shared library [%s]", zProc,zFile);
+ "no entry point [%s] in shared library [%s]", zEntry, zFile);
sqlite3OsDlError(pVfs, nMsg-1, zErrmsg);
}
- sqlite3OsDlClose(pVfs, handle);
}
+ sqlite3OsDlClose(pVfs, handle);
+ sqlite3_free(zAltEntry);
return SQLITE_ERROR;
- }else if( xInit(db, &zErrmsg, &sqlite3Apis) ){
+ }
+ sqlite3_free(zAltEntry);
+ rc = xInit(db, &zErrmsg, &sqlite3Apis);
+ if( rc ){
+ if( rc==SQLITE_OK_LOAD_PERMANENTLY ) return SQLITE_OK;
if( pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("error during initialization: %s", zErrmsg);
}
@@ -89762,7 +110832,7 @@ static int sqlite3LoadExtension(
/* Append the new shared library handle to the db->aExtension array. */
aHandle = sqlite3DbMallocZero(db, sizeof(handle)*(db->nExtension+1));
if( aHandle==0 ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
if( db->nExtension>0 ){
memcpy(aHandle, db->aExtension, sizeof(handle)*db->nExtension);
@@ -89807,26 +110877,15 @@ SQLITE_PRIVATE void sqlite3CloseExtensions(sqlite3 *db){
SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff){
sqlite3_mutex_enter(db->mutex);
if( onoff ){
- db->flags |= SQLITE_LoadExtension;
+ db->flags |= SQLITE_LoadExtension|SQLITE_LoadExtFunc;
}else{
- db->flags &= ~SQLITE_LoadExtension;
+ db->flags &= ~(SQLITE_LoadExtension|SQLITE_LoadExtFunc);
}
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
-#endif /* SQLITE_OMIT_LOAD_EXTENSION */
-
-/*
-** The auto-extension code added regardless of whether or not extension
-** loading is supported. We need a dummy sqlite3Apis pointer for that
-** code if regular extension loading is not available. This is that
-** dummy pointer.
-*/
-#ifdef SQLITE_OMIT_LOAD_EXTENSION
-static const sqlite3_api_routines sqlite3Apis = { 0 };
-#endif
-
+#endif /* !defined(SQLITE_OMIT_LOAD_EXTENSION) */
/*
** The following object holds the list of automatically loaded
@@ -89837,7 +110896,7 @@ static const sqlite3_api_routines sqlite3Apis = { 0 };
*/
typedef struct sqlite3AutoExtList sqlite3AutoExtList;
static SQLITE_WSD struct sqlite3AutoExtList {
- int nExt; /* Number of entries in aExt[] */
+ u32 nExt; /* Number of entries in aExt[] */
void (**aExt)(void); /* Pointers to the extension init functions */
} sqlite3Autoext = { 0, 0 };
@@ -89861,7 +110920,9 @@ static SQLITE_WSD struct sqlite3AutoExtList {
** Register a statically linked extension that is automatically
** loaded by every new database connection.
*/
-SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){
+SQLITE_API int sqlite3_auto_extension(
+ void (*xInit)(void)
+){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
@@ -89870,7 +110931,7 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){
}else
#endif
{
- int i;
+ u32 i;
#if SQLITE_THREADSAFE
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
#endif
@@ -89880,11 +110941,11 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){
if( wsdAutoext.aExt[i]==xInit ) break;
}
if( i==wsdAutoext.nExt ){
- int nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]);
+ u64 nByte = (wsdAutoext.nExt+1)*sizeof(wsdAutoext.aExt[0]);
void (**aNew)(void);
- aNew = sqlite3_realloc(wsdAutoext.aExt, nByte);
+ aNew = sqlite3_realloc64(wsdAutoext.aExt, nByte);
if( aNew==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
wsdAutoext.aExt = aNew;
wsdAutoext.aExt[wsdAutoext.nExt] = xInit;
@@ -89898,6 +110959,37 @@ SQLITE_API int sqlite3_auto_extension(void (*xInit)(void)){
}
/*
+** Cancel a prior call to sqlite3_auto_extension. Remove xInit from the
+** set of routines that is invoked for each new database connection, if it
+** is currently on the list. If xInit is not on the list, then this
+** routine is a no-op.
+**
+** Return 1 if xInit was found on the list and removed. Return 0 if xInit
+** was not on the list.
+*/
+SQLITE_API int sqlite3_cancel_auto_extension(
+ void (*xInit)(void)
+){
+#if SQLITE_THREADSAFE
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+#endif
+ int i;
+ int n = 0;
+ wsdAutoextInit;
+ sqlite3_mutex_enter(mutex);
+ for(i=(int)wsdAutoext.nExt-1; i>=0; i--){
+ if( wsdAutoext.aExt[i]==xInit ){
+ wsdAutoext.nExt--;
+ wsdAutoext.aExt[i] = wsdAutoext.aExt[wsdAutoext.nExt];
+ n++;
+ break;
+ }
+ }
+ sqlite3_mutex_leave(mutex);
+ return n;
+}
+
+/*
** Reset the automatic extension loading mechanism.
*/
SQLITE_API void sqlite3_reset_auto_extension(void){
@@ -89923,9 +111015,10 @@ SQLITE_API void sqlite3_reset_auto_extension(void){
** If anything goes wrong, set an error in the database connection.
*/
SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
- int i;
+ u32 i;
int go = 1;
- int (*xInit)(sqlite3*,char**,const sqlite3_api_routines*);
+ int rc;
+ sqlite3_loadext_entry xInit;
wsdAutoextInit;
if( wsdAutoext.nExt==0 ){
@@ -89937,18 +111030,22 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
#if SQLITE_THREADSAFE
sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
#endif
+#ifdef SQLITE_OMIT_LOAD_EXTENSION
+ const sqlite3_api_routines *pThunk = 0;
+#else
+ const sqlite3_api_routines *pThunk = &sqlite3Apis;
+#endif
sqlite3_mutex_enter(mutex);
if( i>=wsdAutoext.nExt ){
xInit = 0;
go = 0;
}else{
- xInit = (int(*)(sqlite3*,char**,const sqlite3_api_routines*))
- wsdAutoext.aExt[i];
+ xInit = (sqlite3_loadext_entry)wsdAutoext.aExt[i];
}
sqlite3_mutex_leave(mutex);
zErrmsg = 0;
- if( xInit && xInit(db, &zErrmsg, &sqlite3Apis) ){
- sqlite3Error(db, SQLITE_ERROR,
+ if( xInit && (rc = xInit(db, &zErrmsg, pThunk))!=0 ){
+ sqlite3ErrorWithMsg(db, rc,
"automatic extension loading failed: %s", zErrmsg);
go = 0;
}
@@ -89971,41 +111068,531 @@ SQLITE_PRIVATE void sqlite3AutoLoadExtensions(sqlite3 *db){
*************************************************************************
** This file contains code used to implement the PRAGMA command.
*/
+/* #include "sqliteInt.h" */
+
+#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
+# if defined(__APPLE__)
+# define SQLITE_ENABLE_LOCKING_STYLE 1
+# else
+# define SQLITE_ENABLE_LOCKING_STYLE 0
+# endif
+#endif
+
+/***************************************************************************
+** The "pragma.h" include file is an automatically generated file that
+** that includes the PragType_XXXX macro definitions and the aPragmaName[]
+** object. This ensures that the aPragmaName[] table is arranged in
+** lexicographical order to facility a binary search of the pragma name.
+** Do not edit pragma.h directly. Edit and rerun the script in at
+** ../tool/mkpragmatab.tcl. */
+/************** Include pragma.h in the middle of pragma.c *******************/
+/************** Begin file pragma.h ******************************************/
+/* DO NOT EDIT!
+** This file is automatically generated by the script at
+** ../tool/mkpragmatab.tcl. To update the set of pragmas, edit
+** that script and rerun it.
+*/
+#define PragTyp_HEADER_VALUE 0
+#define PragTyp_AUTO_VACUUM 1
+#define PragTyp_FLAG 2
+#define PragTyp_BUSY_TIMEOUT 3
+#define PragTyp_CACHE_SIZE 4
+#define PragTyp_CACHE_SPILL 5
+#define PragTyp_CASE_SENSITIVE_LIKE 6
+#define PragTyp_COLLATION_LIST 7
+#define PragTyp_COMPILE_OPTIONS 8
+#define PragTyp_DATA_STORE_DIRECTORY 9
+#define PragTyp_DATABASE_LIST 10
+#define PragTyp_DEFAULT_CACHE_SIZE 11
+#define PragTyp_ENCODING 12
+#define PragTyp_FOREIGN_KEY_CHECK 13
+#define PragTyp_FOREIGN_KEY_LIST 14
+#define PragTyp_INCREMENTAL_VACUUM 15
+#define PragTyp_INDEX_INFO 16
+#define PragTyp_INDEX_LIST 17
+#define PragTyp_INTEGRITY_CHECK 18
+#define PragTyp_JOURNAL_MODE 19
+#define PragTyp_JOURNAL_SIZE_LIMIT 20
+#define PragTyp_LOCK_PROXY_FILE 21
+#define PragTyp_LOCKING_MODE 22
+#define PragTyp_PAGE_COUNT 23
+#define PragTyp_MMAP_SIZE 24
+#define PragTyp_PAGE_SIZE 25
+#define PragTyp_SECURE_DELETE 26
+#define PragTyp_SHRINK_MEMORY 27
+#define PragTyp_SOFT_HEAP_LIMIT 28
+#define PragTyp_STATS 29
+#define PragTyp_SYNCHRONOUS 30
+#define PragTyp_TABLE_INFO 31
+#define PragTyp_TEMP_STORE 32
+#define PragTyp_TEMP_STORE_DIRECTORY 33
+#define PragTyp_THREADS 34
+#define PragTyp_WAL_AUTOCHECKPOINT 35
+#define PragTyp_WAL_CHECKPOINT 36
+#define PragTyp_ACTIVATE_EXTENSIONS 37
+#define PragTyp_HEXKEY 38
+#define PragTyp_KEY 39
+#define PragTyp_REKEY 40
+#define PragTyp_LOCK_STATUS 41
+#define PragTyp_PARSER_TRACE 42
+#define PragFlag_NeedSchema 0x01
+#define PragFlag_ReadOnly 0x02
+static const struct sPragmaNames {
+ const char *const zName; /* Name of pragma */
+ u8 ePragTyp; /* PragTyp_XXX value */
+ u8 mPragFlag; /* Zero or more PragFlag_XXX values */
+ u32 iArg; /* Extra argument */
+} aPragmaNames[] = {
+#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
+ { /* zName: */ "activate_extensions",
+ /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "application_id",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ BTREE_APPLICATION_ID },
+#endif
+#if !defined(SQLITE_OMIT_AUTOVACUUM)
+ { /* zName: */ "auto_vacuum",
+ /* ePragTyp: */ PragTyp_AUTO_VACUUM,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_AUTOMATIC_INDEX)
+ { /* zName: */ "automatic_index",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_AutoIndex },
+#endif
+#endif
+ { /* zName: */ "busy_timeout",
+ /* ePragTyp: */ PragTyp_BUSY_TIMEOUT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "cache_size",
+ /* ePragTyp: */ PragTyp_CACHE_SIZE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "cache_spill",
+ /* ePragTyp: */ PragTyp_CACHE_SPILL,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "case_sensitive_like",
+ /* ePragTyp: */ PragTyp_CASE_SENSITIVE_LIKE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "cell_size_check",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CellSizeCk },
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "checkpoint_fullfsync",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CkptFullFSync },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "collation_list",
+ /* ePragTyp: */ PragTyp_COLLATION_LIST,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
+ { /* zName: */ "compile_options",
+ /* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "count_changes",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_CountRows },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_OS_WIN
+ { /* zName: */ "data_store_directory",
+ /* ePragTyp: */ PragTyp_DATA_STORE_DIRECTORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "data_version",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ PragFlag_ReadOnly,
+ /* iArg: */ BTREE_DATA_VERSION },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "database_list",
+ /* ePragTyp: */ PragTyp_DATABASE_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
+ { /* zName: */ "default_cache_size",
+ /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "defer_foreign_keys",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_DeferFKs },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "empty_result_callbacks",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_NullCallback },
+#endif
+#if !defined(SQLITE_OMIT_UTF16)
+ { /* zName: */ "encoding",
+ /* ePragTyp: */ PragTyp_ENCODING,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "foreign_key_check",
+ /* ePragTyp: */ PragTyp_FOREIGN_KEY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FOREIGN_KEY)
+ { /* zName: */ "foreign_key_list",
+ /* ePragTyp: */ PragTyp_FOREIGN_KEY_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
+ { /* zName: */ "foreign_keys",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ForeignKeys },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "freelist_count",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ PragFlag_ReadOnly,
+ /* iArg: */ BTREE_FREE_PAGE_COUNT },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "full_column_names",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_FullColNames },
+ { /* zName: */ "fullfsync",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_FullFSync },
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "hexkey",
+ /* ePragTyp: */ PragTyp_HEXKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "hexrekey",
+ /* ePragTyp: */ PragTyp_HEXKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if !defined(SQLITE_OMIT_CHECK)
+ { /* zName: */ "ignore_check_constraints",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_IgnoreChecks },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_AUTOVACUUM)
+ { /* zName: */ "incremental_vacuum",
+ /* ePragTyp: */ PragTyp_INCREMENTAL_VACUUM,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "index_info",
+ /* ePragTyp: */ PragTyp_INDEX_INFO,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "index_list",
+ /* ePragTyp: */ PragTyp_INDEX_LIST,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "index_xinfo",
+ /* ePragTyp: */ PragTyp_INDEX_INFO,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 1 },
+#endif
+#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+ { /* zName: */ "integrity_check",
+ /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "journal_mode",
+ /* ePragTyp: */ PragTyp_JOURNAL_MODE,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "journal_size_limit",
+ /* ePragTyp: */ PragTyp_JOURNAL_SIZE_LIMIT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "key",
+ /* ePragTyp: */ PragTyp_KEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "legacy_file_format",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_LegacyFileFmt },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && SQLITE_ENABLE_LOCKING_STYLE
+ { /* zName: */ "lock_proxy_file",
+ /* ePragTyp: */ PragTyp_LOCK_PROXY_FILE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
+ { /* zName: */ "lock_status",
+ /* ePragTyp: */ PragTyp_LOCK_STATUS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "locking_mode",
+ /* ePragTyp: */ PragTyp_LOCKING_MODE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "max_page_count",
+ /* ePragTyp: */ PragTyp_PAGE_COUNT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "mmap_size",
+ /* ePragTyp: */ PragTyp_MMAP_SIZE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "page_count",
+ /* ePragTyp: */ PragTyp_PAGE_COUNT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+ { /* zName: */ "page_size",
+ /* ePragTyp: */ PragTyp_PAGE_SIZE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_PARSER_TRACE)
+ { /* zName: */ "parser_trace",
+ /* ePragTyp: */ PragTyp_PARSER_TRACE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "query_only",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_QueryOnly },
+#endif
+#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
+ { /* zName: */ "quick_check",
+ /* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "read_uncommitted",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ReadUncommitted },
+ { /* zName: */ "recursive_triggers",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_RecTriggers },
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ { /* zName: */ "rekey",
+ /* ePragTyp: */ PragTyp_REKEY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "reverse_unordered_selects",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ReverseOrder },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "schema_version",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ BTREE_SCHEMA_VERSION },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "secure_delete",
+ /* ePragTyp: */ PragTyp_SECURE_DELETE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "short_column_names",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_ShortColNames },
+#endif
+ { /* zName: */ "shrink_memory",
+ /* ePragTyp: */ PragTyp_SHRINK_MEMORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "soft_heap_limit",
+ /* ePragTyp: */ PragTyp_SOFT_HEAP_LIMIT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "sql_trace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_SqlTrace },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "stats",
+ /* ePragTyp: */ PragTyp_STATS,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "synchronous",
+ /* ePragTyp: */ PragTyp_SYNCHRONOUS,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
+ { /* zName: */ "table_info",
+ /* ePragTyp: */ PragTyp_TABLE_INFO,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
+ { /* zName: */ "temp_store",
+ /* ePragTyp: */ PragTyp_TEMP_STORE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "temp_store_directory",
+ /* ePragTyp: */ PragTyp_TEMP_STORE_DIRECTORY,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#endif
+ { /* zName: */ "threads",
+ /* ePragTyp: */ PragTyp_THREADS,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
+ { /* zName: */ "user_version",
+ /* ePragTyp: */ PragTyp_HEADER_VALUE,
+ /* ePragFlag: */ 0,
+ /* iArg: */ BTREE_USER_VERSION },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+#if defined(SQLITE_DEBUG)
+ { /* zName: */ "vdbe_addoptrace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeAddopTrace },
+ { /* zName: */ "vdbe_debug",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace },
+ { /* zName: */ "vdbe_eqp",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeEQP },
+ { /* zName: */ "vdbe_listing",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeListing },
+ { /* zName: */ "vdbe_trace",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_VdbeTrace },
+#endif
+#endif
+#if !defined(SQLITE_OMIT_WAL)
+ { /* zName: */ "wal_autocheckpoint",
+ /* ePragTyp: */ PragTyp_WAL_AUTOCHECKPOINT,
+ /* ePragFlag: */ 0,
+ /* iArg: */ 0 },
+ { /* zName: */ "wal_checkpoint",
+ /* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
+ /* ePragFlag: */ PragFlag_NeedSchema,
+ /* iArg: */ 0 },
+#endif
+#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
+ { /* zName: */ "writable_schema",
+ /* ePragTyp: */ PragTyp_FLAG,
+ /* ePragFlag: */ 0,
+ /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode },
+#endif
+};
+/* Number of pragmas: 60 on by default, 73 total. */
+
+/************** End of pragma.h **********************************************/
+/************** Continuing where we left off in pragma.c *********************/
/*
** Interpret the given string as a safety level. Return 0 for OFF,
-** 1 for ON or NORMAL and 2 for FULL. Return 1 for an empty or
-** unrecognized string argument.
+** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA. Return 1 for an empty or
+** unrecognized string argument. The FULL and EXTRA option is disallowed
+** if the omitFull parameter it 1.
**
** Note that the values returned are one less that the values that
** should be passed into sqlite3BtreeSetSafetyLevel(). The is done
** to support legacy SQL code. The safety level used to be boolean
** and older scripts may have used numbers 0 for OFF and 1 for ON.
*/
-static u8 getSafetyLevel(const char *z){
- /* 123456789 123456789 */
- static const char zText[] = "onoffalseyestruefull";
- static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 16};
- static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 4};
- static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 2};
+static u8 getSafetyLevel(const char *z, int omitFull, u8 dflt){
+ /* 123456789 123456789 123 */
+ static const char zText[] = "onoffalseyestruextrafull";
+ static const u8 iOffset[] = {0, 1, 2, 4, 9, 12, 15, 20};
+ static const u8 iLength[] = {2, 2, 3, 5, 3, 4, 5, 4};
+ static const u8 iValue[] = {1, 0, 0, 0, 1, 1, 3, 2};
+ /* on no off false yes true extra full */
int i, n;
if( sqlite3Isdigit(*z) ){
return (u8)sqlite3Atoi(z);
}
n = sqlite3Strlen30(z);
for(i=0; i<ArraySize(iLength); i++){
- if( iLength[i]==n && sqlite3StrNICmp(&zText[iOffset[i]],z,n)==0 ){
+ if( iLength[i]==n && sqlite3StrNICmp(&zText[iOffset[i]],z,n)==0
+ && (!omitFull || iValue[i]<=1)
+ ){
return iValue[i];
}
}
- return 1;
+ return dflt;
}
/*
** Interpret the given string as a boolean value.
*/
-SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z){
- return getSafetyLevel(z)&1;
+SQLITE_PRIVATE u8 sqlite3GetBoolean(const char *z, u8 dflt){
+ return getSafetyLevel(z,1,dflt)!=0;
}
/* The sqlite3GetBoolean() function is used by other modules but the
@@ -90076,7 +111663,7 @@ static int invalidateTempStorage(Parse *pParse){
}
sqlite3BtreeClose(db->aDb[1].pBt);
db->aDb[1].pBt = 0;
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
}
return SQLITE_OK;
}
@@ -90101,105 +111688,76 @@ static int changeTempStorage(Parse *pParse, const char *zStorageType){
#endif /* SQLITE_PAGER_PRAGMAS */
/*
-** Generate code to return a single integer value.
+** Set the names of the first N columns to the values in azCol[]
*/
-static void returnSingleInt(Parse *pParse, const char *zLabel, i64 value){
- Vdbe *v = sqlite3GetVdbe(pParse);
- int mem = ++pParse->nMem;
- i64 *pI64 = sqlite3DbMallocRaw(pParse->db, sizeof(value));
- if( pI64 ){
- memcpy(pI64, &value, sizeof(value));
+static void setAllColumnNames(
+ Vdbe *v, /* The query under construction */
+ int N, /* Number of columns */
+ const char **azCol /* Names of columns */
+){
+ int i;
+ sqlite3VdbeSetNumCols(v, N);
+ for(i=0; i<N; i++){
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, azCol[i], SQLITE_STATIC);
}
- sqlite3VdbeAddOp4(v, OP_Int64, 0, mem, 0, (char*)pI64, P4_INT64);
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLabel, SQLITE_STATIC);
- sqlite3VdbeAddOp2(v, OP_ResultRow, mem, 1);
+}
+static void setOneColumnName(Vdbe *v, const char *z){
+ setAllColumnNames(v, 1, &z);
}
-#ifndef SQLITE_OMIT_FLAG_PRAGMAS
/*
-** Check to see if zRight and zLeft refer to a pragma that queries
-** or changes one of the flags in db->flags. Return 1 if so and 0 if not.
-** Also, implement the pragma.
-*/
-static int flagPragma(Parse *pParse, const char *zLeft, const char *zRight){
- static const struct sPragmaType {
- const char *zName; /* Name of the pragma */
- int mask; /* Mask for the db->flags value */
- } aPragma[] = {
- { "full_column_names", SQLITE_FullColNames },
- { "short_column_names", SQLITE_ShortColNames },
- { "count_changes", SQLITE_CountRows },
- { "empty_result_callbacks", SQLITE_NullCallback },
- { "legacy_file_format", SQLITE_LegacyFileFmt },
- { "fullfsync", SQLITE_FullFSync },
- { "checkpoint_fullfsync", SQLITE_CkptFullFSync },
- { "reverse_unordered_selects", SQLITE_ReverseOrder },
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- { "automatic_index", SQLITE_AutoIndex },
-#endif
-#ifdef SQLITE_DEBUG
- { "sql_trace", SQLITE_SqlTrace },
- { "vdbe_listing", SQLITE_VdbeListing },
- { "vdbe_trace", SQLITE_VdbeTrace },
-#endif
-#ifndef SQLITE_OMIT_CHECK
- { "ignore_check_constraints", SQLITE_IgnoreChecks },
-#endif
- /* The following is VERY experimental */
- { "writable_schema", SQLITE_WriteSchema|SQLITE_RecoveryMode },
- { "omit_readlock", SQLITE_NoReadlock },
-
- /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted
- ** flag if there are any active statements. */
- { "read_uncommitted", SQLITE_ReadUncommitted },
- { "recursive_triggers", SQLITE_RecTriggers },
+** Generate code to return a single integer value.
+*/
+static void returnSingleInt(Vdbe *v, const char *zLabel, i64 value){
+ sqlite3VdbeAddOp4Dup8(v, OP_Int64, 0, 1, 0, (const u8*)&value, P4_INT64);
+ setOneColumnName(v, zLabel);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+}
- /* This flag may only be set if both foreign-key and trigger support
- ** are present in the build. */
-#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER)
- { "foreign_keys", SQLITE_ForeignKeys },
-#endif
- };
- int i;
- const struct sPragmaType *p;
- for(i=0, p=aPragma; i<ArraySize(aPragma); i++, p++){
- if( sqlite3StrICmp(zLeft, p->zName)==0 ){
- sqlite3 *db = pParse->db;
- Vdbe *v;
- v = sqlite3GetVdbe(pParse);
- assert( v!=0 ); /* Already allocated by sqlite3Pragma() */
- if( ALWAYS(v) ){
- if( zRight==0 ){
- returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 );
- }else{
- int mask = p->mask; /* Mask of bits to set or clear. */
- if( db->autoCommit==0 ){
- /* Foreign key support may not be enabled or disabled while not
- ** in auto-commit mode. */
- mask &= ~(SQLITE_ForeignKeys);
- }
+/*
+** Generate code to return a single text value.
+*/
+static void returnSingleText(
+ Vdbe *v, /* Prepared statement under construction */
+ const char *zLabel, /* Name of the result column */
+ const char *zValue /* Value to be returned */
+){
+ if( zValue ){
+ sqlite3VdbeLoadString(v, 1, (const char*)zValue);
+ setOneColumnName(v, zLabel);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+ }
+}
- if( sqlite3GetBoolean(zRight) ){
- db->flags |= mask;
- }else{
- db->flags &= ~mask;
- }
- /* Many of the flag-pragmas modify the code generated by the SQL
- ** compiler (eg. count_changes). So add an opcode to expire all
- ** compiled SQL statements after modifying a pragma value.
- */
- sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
- }
+/*
+** Set the safety_level and pager flags for pager iDb. Or if iDb<0
+** set these values for all pagers.
+*/
+#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+static void setAllPagerFlags(sqlite3 *db){
+ if( db->autoCommit ){
+ Db *pDb = db->aDb;
+ int n = db->nDb;
+ assert( SQLITE_FullFSync==PAGER_FULLFSYNC );
+ assert( SQLITE_CkptFullFSync==PAGER_CKPT_FULLFSYNC );
+ assert( SQLITE_CacheSpill==PAGER_CACHESPILL );
+ assert( (PAGER_FULLFSYNC | PAGER_CKPT_FULLFSYNC | PAGER_CACHESPILL)
+ == PAGER_FLAGS_MASK );
+ assert( (pDb->safety_level & PAGER_SYNCHRONOUS_MASK)==pDb->safety_level );
+ while( (n--) > 0 ){
+ if( pDb->pBt ){
+ sqlite3BtreeSetPagerFlags(pDb->pBt,
+ pDb->safety_level | (db->flags & PAGER_FLAGS_MASK) );
}
-
- return 1;
+ pDb++;
}
}
- return 0;
}
-#endif /* SQLITE_OMIT_FLAG_PRAGMAS */
+#else
+# define setAllPagerFlags(X) /* no-op */
+#endif
+
/*
** Return a human-readable name for a constraint resolution action.
@@ -90249,7 +111807,7 @@ SQLITE_PRIVATE const char *sqlite3JournalModename(int eMode){
**
** Pragmas are of this form:
**
-** PRAGMA [database.]id [= value]
+** PRAGMA [schema.]id [= value]
**
** The identifier might also be a string. The value is a string, and
** identifier, or a number. If minusFlag is true, then the value is
@@ -90261,8 +111819,8 @@ SQLITE_PRIVATE const char *sqlite3JournalModename(int eMode){
*/
SQLITE_PRIVATE void sqlite3Pragma(
Parse *pParse,
- Token *pId1, /* First part of [database.]id field */
- Token *pId2, /* Second part of [database.]id field, or NULL */
+ Token *pId1, /* First part of [schema.]id field */
+ Token *pId2, /* Second part of [schema.]id field, or NULL */
Token *pValue, /* Token for <value>, or NULL */
int minusFlag /* True if a '-' sign preceded <value> */
){
@@ -90270,15 +111828,20 @@ SQLITE_PRIVATE void sqlite3Pragma(
char *zRight = 0; /* Nul-terminated UTF-8 string <value>, or NULL */
const char *zDb = 0; /* The database name */
Token *pId; /* Pointer to <id> token */
+ char *aFcntl[4]; /* Argument to SQLITE_FCNTL_PRAGMA */
int iDb; /* Database index for <database> */
- sqlite3 *db = pParse->db;
- Db *pDb;
- Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(db);
+ int lwr, upr, mid = 0; /* Binary search bounds */
+ int rc; /* return value form SQLITE_FCNTL_PRAGMA */
+ sqlite3 *db = pParse->db; /* The database connection */
+ Db *pDb; /* The specific database being pragmaed */
+ Vdbe *v = sqlite3GetVdbe(pParse); /* Prepared statement */
+ const struct sPragmaNames *pPragma;
+
if( v==0 ) return;
sqlite3VdbeRunOnlyOnce(v);
pParse->nMem = 2;
- /* Interpret the [database.] part of the pragma statement. iDb is the
+ /* Interpret the [schema.] part of the pragma statement. iDb is the
** index of the database this pragma is being applied to in db.aDb[]. */
iDb = sqlite3TwoPartName(pParse, pId1, pId2, &pId);
if( iDb<0 ) return;
@@ -90300,15 +111863,75 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
assert( pId2 );
- zDb = pId2->n>0 ? pDb->zName : 0;
+ zDb = pId2->n>0 ? pDb->zDbSName : 0;
if( sqlite3AuthCheck(pParse, SQLITE_PRAGMA, zLeft, zRight, zDb) ){
goto pragma_out;
}
-
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
+
+ /* Send an SQLITE_FCNTL_PRAGMA file-control to the underlying VFS
+ ** connection. If it returns SQLITE_OK, then assume that the VFS
+ ** handled the pragma and generate a no-op prepared statement.
+ **
+ ** IMPLEMENTATION-OF: R-12238-55120 Whenever a PRAGMA statement is parsed,
+ ** an SQLITE_FCNTL_PRAGMA file control is sent to the open sqlite3_file
+ ** object corresponding to the database file to which the pragma
+ ** statement refers.
+ **
+ ** IMPLEMENTATION-OF: R-29875-31678 The argument to the SQLITE_FCNTL_PRAGMA
+ ** file control is an array of pointers to strings (char**) in which the
+ ** second element of the array is the name of the pragma and the third
+ ** element is the argument to the pragma or NULL if the pragma has no
+ ** argument.
+ */
+ aFcntl[0] = 0;
+ aFcntl[1] = zLeft;
+ aFcntl[2] = zRight;
+ aFcntl[3] = 0;
+ db->busyHandler.nBusy = 0;
+ rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_PRAGMA, (void*)aFcntl);
+ if( rc==SQLITE_OK ){
+ returnSingleText(v, "result", aFcntl[0]);
+ sqlite3_free(aFcntl[0]);
+ goto pragma_out;
+ }
+ if( rc!=SQLITE_NOTFOUND ){
+ if( aFcntl[0] ){
+ sqlite3ErrorMsg(pParse, "%s", aFcntl[0]);
+ sqlite3_free(aFcntl[0]);
+ }
+ pParse->nErr++;
+ pParse->rc = rc;
+ goto pragma_out;
+ }
+
+ /* Locate the pragma in the lookup table */
+ lwr = 0;
+ upr = ArraySize(aPragmaNames)-1;
+ while( lwr<=upr ){
+ mid = (lwr+upr)/2;
+ rc = sqlite3_stricmp(zLeft, aPragmaNames[mid].zName);
+ if( rc==0 ) break;
+ if( rc<0 ){
+ upr = mid - 1;
+ }else{
+ lwr = mid + 1;
+ }
+ }
+ if( lwr>upr ) goto pragma_out;
+ pPragma = &aPragmaNames[mid];
+
+ /* Make sure the database schema is loaded if the pragma requires that */
+ if( (pPragma->mPragFlag & PragFlag_NeedSchema)!=0 ){
+ if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ }
+
+ /* Jump to the appropriate pragma handler */
+ switch( pPragma->ePragTyp ){
+
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
/*
- ** PRAGMA [database.]default_cache_size
- ** PRAGMA [database.]default_cache_size=N
+ ** PRAGMA [schema.]default_cache_size
+ ** PRAGMA [schema.]default_cache_size=N
**
** The first form reports the current persistent setting for the
** page cache size. The value returned is the maximum number of
@@ -90322,79 +111945,84 @@ SQLITE_PRIVATE void sqlite3Pragma(
** size. But continue to take the absolute value of the default cache
** size of historical compatibility.
*/
- if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){
+ case PragTyp_DEFAULT_CACHE_SIZE: {
+ static const int iLn = VDBE_OFFSET_LINENO(2);
static const VdbeOpList getCacheSize[] = {
{ OP_Transaction, 0, 0, 0}, /* 0 */
{ OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */
- { OP_IfPos, 1, 7, 0},
+ { OP_IfPos, 1, 8, 0},
{ OP_Integer, 0, 2, 0},
{ OP_Subtract, 1, 2, 1},
- { OP_IfPos, 1, 7, 0},
+ { OP_IfPos, 1, 8, 0},
{ OP_Integer, 0, 1, 0}, /* 6 */
+ { OP_Noop, 0, 0, 0},
{ OP_ResultRow, 1, 1, 0},
};
- int addr;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ VdbeOp *aOp;
sqlite3VdbeUsesBtree(v, iDb);
if( !zRight ){
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", SQLITE_STATIC);
+ setOneColumnName(v, "cache_size");
pParse->nMem += 2;
- addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize);
- sqlite3VdbeChangeP1(v, addr, iDb);
- sqlite3VdbeChangeP1(v, addr+1, iDb);
- sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE);
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(getCacheSize));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize, iLn);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[6].p1 = SQLITE_DEFAULT_CACHE_SIZE;
}else{
int size = sqlite3AbsInt32(sqlite3Atoi(zRight));
sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3VdbeAddOp2(v, OP_Integer, size, 1);
- sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1);
+ sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, size);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
- }else
+ break;
+ }
+#endif /* !SQLITE_OMIT_PAGER_PRAGMAS && !SQLITE_OMIT_DEPRECATED */
+#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
/*
- ** PRAGMA [database.]page_size
- ** PRAGMA [database.]page_size=N
+ ** PRAGMA [schema.]page_size
+ ** PRAGMA [schema.]page_size=N
**
** The first form reports the current setting for the
** database page size in bytes. The second form sets the
** database page size value. The value can only be set if
** the database has not yet been created.
*/
- if( sqlite3StrICmp(zLeft,"page_size")==0 ){
+ case PragTyp_PAGE_SIZE: {
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
if( !zRight ){
int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0;
- returnSingleInt(pParse, "page_size", size);
+ returnSingleInt(v, "page_size", size);
}else{
/* Malloc may fail when setting the page-size, as there is an internal
** buffer that the pager module resizes using sqlite3_realloc().
*/
db->nextPagesize = sqlite3Atoi(zRight);
- if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
- db->mallocFailed = 1;
+ if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
+ sqlite3OomFault(db);
}
}
- }else
+ break;
+ }
/*
- ** PRAGMA [database.]secure_delete
- ** PRAGMA [database.]secure_delete=ON/OFF
+ ** PRAGMA [schema.]secure_delete
+ ** PRAGMA [schema.]secure_delete=ON/OFF
**
** The first form reports the current setting for the
** secure_delete flag. The second form changes the secure_delete
** flag setting and reports thenew value.
*/
- if( sqlite3StrICmp(zLeft,"secure_delete")==0 ){
+ case PragTyp_SECURE_DELETE: {
Btree *pBt = pDb->pBt;
int b = -1;
assert( pBt!=0 );
if( zRight ){
- b = sqlite3GetBoolean(zRight);
+ b = sqlite3GetBoolean(zRight, 0);
}
if( pId2->n==0 && b>=0 ){
int ii;
@@ -90403,44 +112031,48 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
b = sqlite3BtreeSecureDelete(pBt, b);
- returnSingleInt(pParse, "secure_delete", b);
- }else
+ returnSingleInt(v, "secure_delete", b);
+ break;
+ }
/*
- ** PRAGMA [database.]max_page_count
- ** PRAGMA [database.]max_page_count=N
+ ** PRAGMA [schema.]max_page_count
+ ** PRAGMA [schema.]max_page_count=N
**
** The first form reports the current setting for the
** maximum number of pages in the database file. The
** second form attempts to change this setting. Both
** forms return the current setting.
**
- ** PRAGMA [database.]page_count
+ ** The absolute value of N is used. This is undocumented and might
+ ** change. The only purpose is to provide an easy way to test
+ ** the sqlite3AbsInt32() function.
+ **
+ ** PRAGMA [schema.]page_count
**
** Return the number of pages in the specified database.
*/
- if( sqlite3StrICmp(zLeft,"page_count")==0
- || sqlite3StrICmp(zLeft,"max_page_count")==0
- ){
+ case PragTyp_PAGE_COUNT: {
int iReg;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3CodeVerifySchema(pParse, iDb);
iReg = ++pParse->nMem;
- if( zLeft[0]=='p' ){
+ if( sqlite3Tolower(zLeft[0])=='p' ){
sqlite3VdbeAddOp2(v, OP_Pagecount, iDb, iReg);
}else{
- sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg, sqlite3Atoi(zRight));
+ sqlite3VdbeAddOp3(v, OP_MaxPgcnt, iDb, iReg,
+ sqlite3AbsInt32(sqlite3Atoi(zRight)));
}
sqlite3VdbeAddOp2(v, OP_ResultRow, iReg, 1);
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
- }else
+ break;
+ }
/*
- ** PRAGMA [database.]locking_mode
- ** PRAGMA [database.]locking_mode = (normal|exclusive)
+ ** PRAGMA [schema.]locking_mode
+ ** PRAGMA [schema.]locking_mode = (normal|exclusive)
*/
- if( sqlite3StrICmp(zLeft,"locking_mode")==0 ){
+ case PragTyp_LOCKING_MODE: {
const char *zRet = "normal";
int eMode = getLockingMode(zRight);
@@ -90473,34 +112105,25 @@ SQLITE_PRIVATE void sqlite3Pragma(
eMode = sqlite3PagerLockingMode(pPager, eMode);
}
- assert(eMode==PAGER_LOCKINGMODE_NORMAL||eMode==PAGER_LOCKINGMODE_EXCLUSIVE);
+ assert( eMode==PAGER_LOCKINGMODE_NORMAL
+ || eMode==PAGER_LOCKINGMODE_EXCLUSIVE );
if( eMode==PAGER_LOCKINGMODE_EXCLUSIVE ){
zRet = "exclusive";
}
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "locking_mode", SQLITE_STATIC);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zRet, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }else
+ returnSingleText(v, "locking_mode", zRet);
+ break;
+ }
/*
- ** PRAGMA [database.]journal_mode
- ** PRAGMA [database.]journal_mode =
+ ** PRAGMA [schema.]journal_mode
+ ** PRAGMA [schema.]journal_mode =
** (delete|persist|off|truncate|memory|wal|off)
*/
- if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
+ case PragTyp_JOURNAL_MODE: {
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
int ii; /* Loop counter */
- /* Force the schema to be loaded on all databases. This cases all
- ** database files to be opened and the journal_modes set. */
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
-
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
-
+ setOneColumnName(v, "journal_mode");
if( zRight==0 ){
/* If there is no "=MODE" part of the pragma, do a query for the
** current mode */
@@ -90529,138 +112152,215 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }else
+ break;
+ }
/*
- ** PRAGMA [database.]journal_size_limit
- ** PRAGMA [database.]journal_size_limit=N
+ ** PRAGMA [schema.]journal_size_limit
+ ** PRAGMA [schema.]journal_size_limit=N
**
** Get or set the size limit on rollback journal files.
*/
- if( sqlite3StrICmp(zLeft,"journal_size_limit")==0 ){
+ case PragTyp_JOURNAL_SIZE_LIMIT: {
Pager *pPager = sqlite3BtreePager(pDb->pBt);
i64 iLimit = -2;
if( zRight ){
- sqlite3Atoi64(zRight, &iLimit, 1000000, SQLITE_UTF8);
+ sqlite3DecOrHexToI64(zRight, &iLimit);
if( iLimit<-1 ) iLimit = -1;
}
iLimit = sqlite3PagerJournalSizeLimit(pPager, iLimit);
- returnSingleInt(pParse, "journal_size_limit", iLimit);
- }else
+ returnSingleInt(v, "journal_size_limit", iLimit);
+ break;
+ }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
/*
- ** PRAGMA [database.]auto_vacuum
- ** PRAGMA [database.]auto_vacuum=N
+ ** PRAGMA [schema.]auto_vacuum
+ ** PRAGMA [schema.]auto_vacuum=N
**
** Get or set the value of the database 'auto-vacuum' parameter.
** The value is one of: 0 NONE 1 FULL 2 INCREMENTAL
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( sqlite3StrICmp(zLeft,"auto_vacuum")==0 ){
+ case PragTyp_AUTO_VACUUM: {
Btree *pBt = pDb->pBt;
assert( pBt!=0 );
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
if( !zRight ){
- int auto_vacuum;
- if( ALWAYS(pBt) ){
- auto_vacuum = sqlite3BtreeGetAutoVacuum(pBt);
- }else{
- auto_vacuum = SQLITE_DEFAULT_AUTOVACUUM;
- }
- returnSingleInt(pParse, "auto_vacuum", auto_vacuum);
+ returnSingleInt(v, "auto_vacuum", sqlite3BtreeGetAutoVacuum(pBt));
}else{
int eAuto = getAutoVacuum(zRight);
assert( eAuto>=0 && eAuto<=2 );
db->nextAutovac = (u8)eAuto;
- if( ALWAYS(eAuto>=0) ){
- /* Call SetAutoVacuum() to set initialize the internal auto and
- ** incr-vacuum flags. This is required in case this connection
- ** creates the database file. It is important that it is created
- ** as an auto-vacuum capable db.
+ /* Call SetAutoVacuum() to set initialize the internal auto and
+ ** incr-vacuum flags. This is required in case this connection
+ ** creates the database file. It is important that it is created
+ ** as an auto-vacuum capable db.
+ */
+ rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
+ if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
+ /* When setting the auto_vacuum mode to either "full" or
+ ** "incremental", write the value of meta[6] in the database
+ ** file. Before writing to meta[6], check that meta[3] indicates
+ ** that this really is an auto-vacuum capable database.
*/
- int rc = sqlite3BtreeSetAutoVacuum(pBt, eAuto);
- if( rc==SQLITE_OK && (eAuto==1 || eAuto==2) ){
- /* When setting the auto_vacuum mode to either "full" or
- ** "incremental", write the value of meta[6] in the database
- ** file. Before writing to meta[6], check that meta[3] indicates
- ** that this really is an auto-vacuum capable database.
- */
- static const VdbeOpList setMeta6[] = {
- { OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
- { OP_If, 1, 0, 0}, /* 2 */
- { OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
- { OP_Integer, 0, 1, 0}, /* 4 */
- { OP_SetCookie, 0, BTREE_INCR_VACUUM, 1}, /* 5 */
- };
- int iAddr;
- iAddr = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6);
- sqlite3VdbeChangeP1(v, iAddr, iDb);
- sqlite3VdbeChangeP1(v, iAddr+1, iDb);
- sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4);
- sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1);
- sqlite3VdbeChangeP1(v, iAddr+5, iDb);
- sqlite3VdbeUsesBtree(v, iDb);
- }
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList setMeta6[] = {
+ { OP_Transaction, 0, 1, 0}, /* 0 */
+ { OP_ReadCookie, 0, 1, BTREE_LARGEST_ROOT_PAGE},
+ { OP_If, 1, 0, 0}, /* 2 */
+ { OP_Halt, SQLITE_OK, OE_Abort, 0}, /* 3 */
+ { OP_SetCookie, 0, BTREE_INCR_VACUUM, 0}, /* 4 */
+ };
+ VdbeOp *aOp;
+ int iAddr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setMeta6));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(setMeta6), setMeta6, iLn);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[2].p2 = iAddr+4;
+ aOp[4].p1 = iDb;
+ aOp[4].p3 = eAuto - 1;
+ sqlite3VdbeUsesBtree(v, iDb);
}
}
- }else
+ break;
+ }
#endif
/*
- ** PRAGMA [database.]incremental_vacuum(N)
+ ** PRAGMA [schema.]incremental_vacuum(N)
**
** Do N steps of incremental vacuuming on a database.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
- if( sqlite3StrICmp(zLeft,"incremental_vacuum")==0 ){
+ case PragTyp_INCREMENTAL_VACUUM: {
int iLimit, addr;
- if( sqlite3ReadSchema(pParse) ){
- goto pragma_out;
- }
if( zRight==0 || !sqlite3GetInt32(zRight, &iLimit) || iLimit<=0 ){
iLimit = 0x7fffffff;
}
sqlite3BeginWriteOperation(pParse, 0, iDb);
sqlite3VdbeAddOp2(v, OP_Integer, iLimit, 1);
- addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb);
+ addr = sqlite3VdbeAddOp1(v, OP_IncrVacuum, iDb); VdbeCoverage(v);
sqlite3VdbeAddOp1(v, OP_ResultRow, 1);
sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
- sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr);
+ sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addr);
- }else
+ break;
+ }
#endif
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
/*
- ** PRAGMA [database.]cache_size
- ** PRAGMA [database.]cache_size=N
+ ** PRAGMA [schema.]cache_size
+ ** PRAGMA [schema.]cache_size=N
**
** The first form reports the current local setting for the
- ** page cache size. The local setting can be different from
- ** the persistent cache size value that is stored in the database
- ** file itself. The value returned is the maximum number of
- ** pages in the page cache. The second form sets the local
- ** page cache size value. It does not change the persistent
- ** cache size stored on the disk so the cache size will revert
- ** to its default value when the database is closed and reopened.
- ** N should be a positive integer.
- */
- if( sqlite3StrICmp(zLeft,"cache_size")==0 ){
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ ** page cache size. The second form sets the local
+ ** page cache size value. If N is positive then that is the
+ ** number of pages in the cache. If N is negative, then the
+ ** number of pages is adjusted so that the cache uses -N kibibytes
+ ** of memory.
+ */
+ case PragTyp_CACHE_SIZE: {
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( !zRight ){
- returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size);
+ returnSingleInt(v, "cache_size", pDb->pSchema->cache_size);
}else{
- int size = sqlite3AbsInt32(sqlite3Atoi(zRight));
+ int size = sqlite3Atoi(zRight);
pDb->pSchema->cache_size = size;
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
- }else
+ break;
+ }
+
+ /*
+ ** PRAGMA [schema.]cache_spill
+ ** PRAGMA cache_spill=BOOLEAN
+ ** PRAGMA [schema.]cache_spill=N
+ **
+ ** The first form reports the current local setting for the
+ ** page cache spill size. The second form turns cache spill on
+ ** or off. When turnning cache spill on, the size is set to the
+ ** current cache_size. The third form sets a spill size that
+ ** may be different form the cache size.
+ ** If N is positive then that is the
+ ** number of pages in the cache. If N is negative, then the
+ ** number of pages is adjusted so that the cache uses -N kibibytes
+ ** of memory.
+ **
+ ** If the number of cache_spill pages is less then the number of
+ ** cache_size pages, no spilling occurs until the page count exceeds
+ ** the number of cache_size pages.
+ **
+ ** The cache_spill=BOOLEAN setting applies to all attached schemas,
+ ** not just the schema specified.
+ */
+ case PragTyp_CACHE_SPILL: {
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ if( !zRight ){
+ returnSingleInt(v, "cache_spill",
+ (db->flags & SQLITE_CacheSpill)==0 ? 0 :
+ sqlite3BtreeSetSpillSize(pDb->pBt,0));
+ }else{
+ int size = 1;
+ if( sqlite3GetInt32(zRight, &size) ){
+ sqlite3BtreeSetSpillSize(pDb->pBt, size);
+ }
+ if( sqlite3GetBoolean(zRight, size!=0) ){
+ db->flags |= SQLITE_CacheSpill;
+ }else{
+ db->flags &= ~SQLITE_CacheSpill;
+ }
+ setAllPagerFlags(db);
+ }
+ break;
+ }
+
+ /*
+ ** PRAGMA [schema.]mmap_size(N)
+ **
+ ** Used to set mapping size limit. The mapping size limit is
+ ** used to limit the aggregate size of all memory mapped regions of the
+ ** database file. If this parameter is set to zero, then memory mapping
+ ** is not used at all. If N is negative, then the default memory map
+ ** limit determined by sqlite3_config(SQLITE_CONFIG_MMAP_SIZE) is set.
+ ** The parameter N is measured in bytes.
+ **
+ ** This value is advisory. The underlying VFS is free to memory map
+ ** as little or as much as it wants. Except, if N is set to 0 then the
+ ** upper layers will never invoke the xFetch interfaces to the VFS.
+ */
+ case PragTyp_MMAP_SIZE: {
+ sqlite3_int64 sz;
+#if SQLITE_MAX_MMAP_SIZE>0
+ assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ if( zRight ){
+ int ii;
+ sqlite3DecOrHexToI64(zRight, &sz);
+ if( sz<0 ) sz = sqlite3GlobalConfig.szMmap;
+ if( pId2->n==0 ) db->szMmap = sz;
+ for(ii=db->nDb-1; ii>=0; ii--){
+ if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){
+ sqlite3BtreeSetMmapLimit(db->aDb[ii].pBt, sz);
+ }
+ }
+ }
+ sz = -1;
+ rc = sqlite3_file_control(db, zDb, SQLITE_FCNTL_MMAP_SIZE, &sz);
+#else
+ sz = 0;
+ rc = SQLITE_OK;
+#endif
+ if( rc==SQLITE_OK ){
+ returnSingleInt(v, "mmap_size", sz);
+ }else if( rc!=SQLITE_NOTFOUND ){
+ pParse->nErr++;
+ pParse->rc = rc;
+ }
+ break;
+ }
/*
** PRAGMA temp_store
@@ -90673,13 +112373,14 @@ SQLITE_PRIVATE void sqlite3Pragma(
** Note that it is possible for the library compile-time options to
** override this setting
*/
- if( sqlite3StrICmp(zLeft, "temp_store")==0 ){
+ case PragTyp_TEMP_STORE: {
if( !zRight ){
- returnSingleInt(pParse, "temp_store", db->temp_store);
+ returnSingleInt(v, "temp_store", db->temp_store);
}else{
changeTempStorage(pParse, zRight);
}
- }else
+ break;
+ }
/*
** PRAGMA temp_store_directory
@@ -90691,19 +112392,12 @@ SQLITE_PRIVATE void sqlite3Pragma(
** If temporary directory is changed, then invalidateTempStorage.
**
*/
- if( sqlite3StrICmp(zLeft, "temp_store_directory")==0 ){
+ case PragTyp_TEMP_STORE_DIRECTORY: {
if( !zRight ){
- if( sqlite3_temp_directory ){
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
- "temp_store_directory", SQLITE_STATIC);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, sqlite3_temp_directory, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }
+ returnSingleText(v, "temp_store_directory", sqlite3_temp_directory);
}else{
#ifndef SQLITE_OMIT_WSD
if( zRight[0] ){
- int rc;
int res;
rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
if( rc!=SQLITE_OK || res==0 ){
@@ -90725,39 +112419,65 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
#endif /* SQLITE_OMIT_WSD */
}
- }else
+ break;
+ }
-#if !defined(SQLITE_ENABLE_LOCKING_STYLE)
-# if defined(__APPLE__)
-# define SQLITE_ENABLE_LOCKING_STYLE 1
-# else
-# define SQLITE_ENABLE_LOCKING_STYLE 0
-# endif
+#if SQLITE_OS_WIN
+ /*
+ ** PRAGMA data_store_directory
+ ** PRAGMA data_store_directory = ""|"directory_name"
+ **
+ ** Return or set the local value of the data_store_directory flag. Changing
+ ** the value sets a specific directory to be used for database files that
+ ** were specified with a relative pathname. Setting to a null string reverts
+ ** to the default database directory, which for database files specified with
+ ** a relative path will probably be based on the current directory for the
+ ** process. Database file specified with an absolute path are not impacted
+ ** by this setting, regardless of its value.
+ **
+ */
+ case PragTyp_DATA_STORE_DIRECTORY: {
+ if( !zRight ){
+ returnSingleText(v, "data_store_directory", sqlite3_data_directory);
+ }else{
+#ifndef SQLITE_OMIT_WSD
+ if( zRight[0] ){
+ int res;
+ rc = sqlite3OsAccess(db->pVfs, zRight, SQLITE_ACCESS_READWRITE, &res);
+ if( rc!=SQLITE_OK || res==0 ){
+ sqlite3ErrorMsg(pParse, "not a writable directory");
+ goto pragma_out;
+ }
+ }
+ sqlite3_free(sqlite3_data_directory);
+ if( zRight[0] ){
+ sqlite3_data_directory = sqlite3_mprintf("%s", zRight);
+ }else{
+ sqlite3_data_directory = 0;
+ }
+#endif /* SQLITE_OMIT_WSD */
+ }
+ break;
+ }
#endif
+
#if SQLITE_ENABLE_LOCKING_STYLE
/*
- ** PRAGMA [database.]lock_proxy_file
- ** PRAGMA [database.]lock_proxy_file = ":auto:"|"lock_file_path"
- **
- ** Return or set the value of the lock_proxy_file flag. Changing
- ** the value sets a specific file to be used for database access locks.
- **
- */
- if( sqlite3StrICmp(zLeft, "lock_proxy_file")==0 ){
+ ** PRAGMA [schema.]lock_proxy_file
+ ** PRAGMA [schema.]lock_proxy_file = ":auto:"|"lock_file_path"
+ **
+ ** Return or set the value of the lock_proxy_file flag. Changing
+ ** the value sets a specific file to be used for database access locks.
+ **
+ */
+ case PragTyp_LOCK_PROXY_FILE: {
if( !zRight ){
Pager *pPager = sqlite3BtreePager(pDb->pBt);
char *proxy_file_path = NULL;
sqlite3_file *pFile = sqlite3PagerFile(pPager);
- sqlite3OsFileControl(pFile, SQLITE_GET_LOCKPROXYFILE,
+ sqlite3OsFileControlHint(pFile, SQLITE_GET_LOCKPROXYFILE,
&proxy_file_path);
-
- if( proxy_file_path ){
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME,
- "lock_proxy_file", SQLITE_STATIC);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, proxy_file_path, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
- }
+ returnSingleText(v, "lock_proxy_file", proxy_file_path);
}else{
Pager *pPager = sqlite3BtreePager(pDb->pBt);
sqlite3_file *pFile = sqlite3PagerFile(pPager);
@@ -90774,38 +112494,72 @@ SQLITE_PRIVATE void sqlite3Pragma(
goto pragma_out;
}
}
- }else
+ break;
+ }
#endif /* SQLITE_ENABLE_LOCKING_STYLE */
/*
- ** PRAGMA [database.]synchronous
- ** PRAGMA [database.]synchronous=OFF|ON|NORMAL|FULL
+ ** PRAGMA [schema.]synchronous
+ ** PRAGMA [schema.]synchronous=OFF|ON|NORMAL|FULL|EXTRA
**
** Return or set the local value of the synchronous flag. Changing
** the local value does not make changes to the disk file and the
** default value will be restored the next time the database is
** opened.
*/
- if( sqlite3StrICmp(zLeft,"synchronous")==0 ){
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ case PragTyp_SYNCHRONOUS: {
if( !zRight ){
- returnSingleInt(pParse, "synchronous", pDb->safety_level-1);
+ returnSingleInt(v, "synchronous", pDb->safety_level-1);
}else{
if( !db->autoCommit ){
sqlite3ErrorMsg(pParse,
"Safety level may not be changed inside a transaction");
}else{
- pDb->safety_level = getSafetyLevel(zRight)+1;
+ int iLevel = (getSafetyLevel(zRight,0,1)+1) & PAGER_SYNCHRONOUS_MASK;
+ if( iLevel==0 ) iLevel = 1;
+ pDb->safety_level = iLevel;
+ pDb->bSyncSet = 1;
+ setAllPagerFlags(db);
}
}
- }else
+ break;
+ }
#endif /* SQLITE_OMIT_PAGER_PRAGMAS */
#ifndef SQLITE_OMIT_FLAG_PRAGMAS
- if( flagPragma(pParse, zLeft, zRight) ){
- /* The flagPragma() subroutine also generates any necessary code
- ** there is nothing more to do here */
- }else
+ case PragTyp_FLAG: {
+ if( zRight==0 ){
+ returnSingleInt(v, pPragma->zName, (db->flags & pPragma->iArg)!=0 );
+ }else{
+ int mask = pPragma->iArg; /* Mask of bits to set or clear. */
+ if( db->autoCommit==0 ){
+ /* Foreign key support may not be enabled or disabled while not
+ ** in auto-commit mode. */
+ mask &= ~(SQLITE_ForeignKeys);
+ }
+#if SQLITE_USER_AUTHENTICATION
+ if( db->auth.authLevel==UAUTH_User ){
+ /* Do not allow non-admin users to modify the schema arbitrarily */
+ mask &= ~(SQLITE_WriteSchema);
+ }
+#endif
+
+ if( sqlite3GetBoolean(zRight, 0) ){
+ db->flags |= mask;
+ }else{
+ db->flags &= ~mask;
+ if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
+ }
+
+ /* Many of the flag-pragmas modify the code generated by the SQL
+ ** compiler (eg. count_changes). So add an opcode to expire all
+ ** compiled SQL statements after modifying a pragma value.
+ */
+ sqlite3VdbeAddOp0(v, OP_Expire);
+ setAllPagerFlags(db);
+ }
+ break;
+ }
#endif /* SQLITE_OMIT_FLAG_PRAGMAS */
#ifndef SQLITE_OMIT_SCHEMA_PRAGMAS
@@ -90821,166 +112575,201 @@ SQLITE_PRIVATE void sqlite3Pragma(
** notnull: True if 'NOT NULL' is part of column declaration
** dflt_value: The default value for the column, if any.
*/
- if( sqlite3StrICmp(zLeft, "table_info")==0 && zRight ){
+ case PragTyp_TABLE_INFO: if( zRight ){
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
- pTab = sqlite3FindTable(db, zRight, zDb);
+ pTab = sqlite3LocateTable(pParse, LOCATE_NOERR, zRight, zDb);
if( pTab ){
- int i;
+ static const char *azCol[] = {
+ "cid", "name", "type", "notnull", "dflt_value", "pk"
+ };
+ int i, k;
int nHidden = 0;
Column *pCol;
- sqlite3VdbeSetNumCols(v, 6);
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
pParse->nMem = 6;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cid", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "type", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "notnull", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "dflt_value", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "pk", SQLITE_STATIC);
+ sqlite3CodeVerifySchema(pParse, iDb);
+ setAllColumnNames(v, 6, azCol); assert( 6==ArraySize(azCol) );
sqlite3ViewGetColumnNames(pParse, pTab);
for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
if( IsHiddenColumn(pCol) ){
nHidden++;
continue;
}
- sqlite3VdbeAddOp2(v, OP_Integer, i-nHidden, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pCol->zName, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
- pCol->zType ? pCol->zType : "", 0);
- sqlite3VdbeAddOp2(v, OP_Integer, (pCol->notNull ? 1 : 0), 4);
- if( pCol->zDflt ){
- sqlite3VdbeAddOp4(v, OP_String8, 0, 5, 0, (char*)pCol->zDflt, 0);
+ if( (pCol->colFlags & COLFLAG_PRIMKEY)==0 ){
+ k = 0;
+ }else if( pPk==0 ){
+ k = 1;
}else{
- sqlite3VdbeAddOp2(v, OP_Null, 0, 5);
+ for(k=1; k<=pTab->nCol && pPk->aiColumn[k-1]!=i; k++){}
}
- sqlite3VdbeAddOp2(v, OP_Integer, pCol->isPrimKey, 6);
+ assert( pCol->pDflt==0 || pCol->pDflt->op==TK_SPAN );
+ sqlite3VdbeMultiLoad(v, 1, "issisi",
+ i-nHidden,
+ pCol->zName,
+ sqlite3ColumnType(pCol,""),
+ pCol->notNull ? 1 : 0,
+ pCol->pDflt ? pCol->pDflt->u.zToken : 0,
+ k);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 6);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "index_info")==0 && zRight ){
+ case PragTyp_STATS: {
+ static const char *azCol[] = { "table", "index", "width", "height" };
+ Index *pIdx;
+ HashElem *i;
+ v = sqlite3GetVdbe(pParse);
+ pParse->nMem = 4;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ setAllColumnNames(v, 4, azCol); assert( 4==ArraySize(azCol) );
+ for(i=sqliteHashFirst(&pDb->pSchema->tblHash); i; i=sqliteHashNext(i)){
+ Table *pTab = sqliteHashData(i);
+ sqlite3VdbeMultiLoad(v, 1, "ssii",
+ pTab->zName,
+ 0,
+ pTab->szTabRow,
+ pTab->nRowLogEst);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ sqlite3VdbeMultiLoad(v, 2, "sii",
+ pIdx->zName,
+ pIdx->szIdxRow,
+ pIdx->aiRowLogEst[0]);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 4);
+ }
+ }
+ }
+ break;
+
+ case PragTyp_INDEX_INFO: if( zRight ){
Index *pIdx;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pIdx = sqlite3FindIndex(db, zRight, zDb);
if( pIdx ){
+ static const char *azCol[] = {
+ "seqno", "cid", "name", "desc", "coll", "key"
+ };
int i;
+ int mx;
+ if( pPragma->iArg ){
+ /* PRAGMA index_xinfo (newer version with more rows and columns) */
+ mx = pIdx->nColumn;
+ pParse->nMem = 6;
+ }else{
+ /* PRAGMA index_info (legacy version) */
+ mx = pIdx->nKeyCol;
+ pParse->nMem = 3;
+ }
pTab = pIdx->pTable;
- sqlite3VdbeSetNumCols(v, 3);
- pParse->nMem = 3;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seqno", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "cid", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "name", SQLITE_STATIC);
- for(i=0; i<pIdx->nColumn; i++){
- int cnum = pIdx->aiColumn[i];
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, cnum, 2);
- assert( pTab->nCol>cnum );
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pTab->aCol[cnum].zName, 0);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
+ sqlite3CodeVerifySchema(pParse, iDb);
+ assert( pParse->nMem<=ArraySize(azCol) );
+ setAllColumnNames(v, pParse->nMem, azCol);
+ for(i=0; i<mx; i++){
+ i16 cnum = pIdx->aiColumn[i];
+ sqlite3VdbeMultiLoad(v, 1, "iis", i, cnum,
+ cnum<0 ? 0 : pTab->aCol[cnum].zName);
+ if( pPragma->iArg ){
+ sqlite3VdbeMultiLoad(v, 4, "isi",
+ pIdx->aSortOrder[i],
+ pIdx->azColl[i],
+ i<pIdx->nKeyCol);
+ }
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, pParse->nMem);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "index_list")==0 && zRight ){
+ case PragTyp_INDEX_LIST: if( zRight ){
Index *pIdx;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
+ int i;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
+ static const char *azCol[] = {
+ "seq", "name", "unique", "origin", "partial"
+ };
v = sqlite3GetVdbe(pParse);
- pIdx = pTab->pIndex;
- if( pIdx ){
- int i = 0;
- sqlite3VdbeSetNumCols(v, 3);
- pParse->nMem = 3;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "unique", SQLITE_STATIC);
- while(pIdx){
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pIdx->zName, 0);
- sqlite3VdbeAddOp2(v, OP_Integer, pIdx->onError!=OE_None, 3);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
- ++i;
- pIdx = pIdx->pNext;
- }
+ pParse->nMem = 5;
+ sqlite3CodeVerifySchema(pParse, iDb);
+ setAllColumnNames(v, 5, azCol); assert( 5==ArraySize(azCol) );
+ for(pIdx=pTab->pIndex, i=0; pIdx; pIdx=pIdx->pNext, i++){
+ const char *azOrigin[] = { "c", "u", "pk" };
+ sqlite3VdbeMultiLoad(v, 1, "isisi",
+ i,
+ pIdx->zName,
+ IsUniqueIndex(pIdx),
+ azOrigin[pIdx->idxType],
+ pIdx->pPartIdxWhere!=0);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 5);
}
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "database_list")==0 ){
+ case PragTyp_DATABASE_LIST: {
+ static const char *azCol[] = { "seq", "name", "file" };
int i;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
- sqlite3VdbeSetNumCols(v, 3);
pParse->nMem = 3;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "file", SQLITE_STATIC);
+ setAllColumnNames(v, 3, azCol); assert( 3==ArraySize(azCol) );
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt==0 ) continue;
- assert( db->aDb[i].zName!=0 );
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, db->aDb[i].zName, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
- sqlite3BtreeGetFilename(db->aDb[i].pBt), 0);
+ assert( db->aDb[i].zDbSName!=0 );
+ sqlite3VdbeMultiLoad(v, 1, "iss",
+ i,
+ db->aDb[i].zDbSName,
+ sqlite3BtreeGetFilename(db->aDb[i].pBt));
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
- }else
+ }
+ break;
- if( sqlite3StrICmp(zLeft, "collation_list")==0 ){
+ case PragTyp_COLLATION_LIST: {
+ static const char *azCol[] = { "seq", "name" };
int i = 0;
HashElem *p;
- sqlite3VdbeSetNumCols(v, 2);
pParse->nMem = 2;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "name", SQLITE_STATIC);
+ setAllColumnNames(v, 2, azCol); assert( 2==ArraySize(azCol) );
for(p=sqliteHashFirst(&db->aCollSeq); p; p=sqliteHashNext(p)){
CollSeq *pColl = (CollSeq *)sqliteHashData(p);
- sqlite3VdbeAddOp2(v, OP_Integer, i++, 1);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, pColl->zName, 0);
+ sqlite3VdbeMultiLoad(v, 1, "is", i++, pColl->zName);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_SCHEMA_PRAGMAS */
#ifndef SQLITE_OMIT_FOREIGN_KEY
- if( sqlite3StrICmp(zLeft, "foreign_key_list")==0 && zRight ){
+ case PragTyp_FOREIGN_KEY_LIST: if( zRight ){
FKey *pFK;
Table *pTab;
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pTab = sqlite3FindTable(db, zRight, zDb);
if( pTab ){
v = sqlite3GetVdbe(pParse);
pFK = pTab->pFKey;
if( pFK ){
+ static const char *azCol[] = {
+ "id", "seq", "table", "from", "to", "on_update", "on_delete",
+ "match"
+ };
int i = 0;
- sqlite3VdbeSetNumCols(v, 8);
pParse->nMem = 8;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "id", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "seq", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "table", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 3, COLNAME_NAME, "from", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 4, COLNAME_NAME, "to", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 5, COLNAME_NAME, "on_update", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 6, COLNAME_NAME, "on_delete", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 7, COLNAME_NAME, "match", SQLITE_STATIC);
+ sqlite3CodeVerifySchema(pParse, iDb);
+ setAllColumnNames(v, 8, azCol); assert( 8==ArraySize(azCol) );
while(pFK){
int j;
for(j=0; j<pFK->nCol; j++){
- char *zCol = pFK->aCol[j].zCol;
- char *zOnDelete = (char *)actionName(pFK->aAction[0]);
- char *zOnUpdate = (char *)actionName(pFK->aAction[1]);
- sqlite3VdbeAddOp2(v, OP_Integer, i, 1);
- sqlite3VdbeAddOp2(v, OP_Integer, j, 2);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 4, 0,
- pTab->aCol[pFK->aCol[j].iFrom].zName, 0);
- sqlite3VdbeAddOp4(v, zCol ? OP_String8 : OP_Null, 0, 5, 0, zCol, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 6, 0, zOnUpdate, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 7, 0, zOnDelete, 0);
- sqlite3VdbeAddOp4(v, OP_String8, 0, 8, 0, "NONE", 0);
+ sqlite3VdbeMultiLoad(v, 1, "iissssss",
+ i,
+ j,
+ pFK->zTo,
+ pTab->aCol[pFK->aCol[j].iFrom].zName,
+ pFK->aCol[j].zCol,
+ actionName(pFK->aAction[1]), /* ON UPDATE */
+ actionName(pFK->aAction[0]), /* ON DELETE */
+ "NONE");
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 8);
}
++i;
@@ -90988,62 +112777,174 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
}
- }else
+ }
+ break;
+#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
+
+#ifndef SQLITE_OMIT_FOREIGN_KEY
+#ifndef SQLITE_OMIT_TRIGGER
+ case PragTyp_FOREIGN_KEY_CHECK: {
+ FKey *pFK; /* A foreign key constraint */
+ Table *pTab; /* Child table contain "REFERENCES" keyword */
+ Table *pParent; /* Parent table that child points to */
+ Index *pIdx; /* Index in the parent table */
+ int i; /* Loop counter: Foreign key number for pTab */
+ int j; /* Loop counter: Field of the foreign key */
+ HashElem *k; /* Loop counter: Next table in schema */
+ int x; /* result variable */
+ int regResult; /* 3 registers to hold a result row */
+ int regKey; /* Register to hold key for checking the FK */
+ int regRow; /* Registers to hold a row from pTab */
+ int addrTop; /* Top of a loop checking foreign keys */
+ int addrOk; /* Jump here if the key is OK */
+ int *aiCols; /* child to parent column mapping */
+ static const char *azCol[] = { "table", "rowid", "parent", "fkid" };
+
+ regResult = pParse->nMem+1;
+ pParse->nMem += 4;
+ regKey = ++pParse->nMem;
+ regRow = ++pParse->nMem;
+ v = sqlite3GetVdbe(pParse);
+ setAllColumnNames(v, 4, azCol); assert( 4==ArraySize(azCol) );
+ sqlite3CodeVerifySchema(pParse, iDb);
+ k = sqliteHashFirst(&db->aDb[iDb].pSchema->tblHash);
+ while( k ){
+ if( zRight ){
+ pTab = sqlite3LocateTable(pParse, 0, zRight, zDb);
+ k = 0;
+ }else{
+ pTab = (Table*)sqliteHashData(k);
+ k = sqliteHashNext(k);
+ }
+ if( pTab==0 || pTab->pFKey==0 ) continue;
+ sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
+ if( pTab->nCol+regRow>pParse->nMem ) pParse->nMem = pTab->nCol + regRow;
+ sqlite3OpenTable(pParse, 0, iDb, pTab, OP_OpenRead);
+ sqlite3VdbeLoadString(v, regResult, pTab->zName);
+ for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
+ pParent = sqlite3FindTable(db, pFK->zTo, zDb);
+ if( pParent==0 ) continue;
+ pIdx = 0;
+ sqlite3TableLock(pParse, iDb, pParent->tnum, 0, pParent->zName);
+ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, 0);
+ if( x==0 ){
+ if( pIdx==0 ){
+ sqlite3OpenTable(pParse, i, iDb, pParent, OP_OpenRead);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_OpenRead, i, pIdx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
+ }
+ }else{
+ k = 0;
+ break;
+ }
+ }
+ assert( pParse->nErr>0 || pFK==0 );
+ if( pFK ) break;
+ if( pParse->nTab<i ) pParse->nTab = i;
+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, 0); VdbeCoverage(v);
+ for(i=1, pFK=pTab->pFKey; pFK; i++, pFK=pFK->pNextFrom){
+ pParent = sqlite3FindTable(db, pFK->zTo, zDb);
+ pIdx = 0;
+ aiCols = 0;
+ if( pParent ){
+ x = sqlite3FkLocateIndex(pParse, pParent, pFK, &pIdx, &aiCols);
+ assert( x==0 );
+ }
+ addrOk = sqlite3VdbeMakeLabel(v);
+ if( pParent && pIdx==0 ){
+ int iKey = pFK->aCol[0].iFrom;
+ assert( iKey>=0 && iKey<pTab->nCol );
+ if( iKey!=pTab->iPKey ){
+ sqlite3VdbeAddOp3(v, OP_Column, 0, iKey, regRow);
+ sqlite3ColumnDefault(v, pTab, iKey, regRow);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRow, addrOk); VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regRow);
+ }
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, i, 0, regRow); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, addrOk);
+ sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
+ }else{
+ for(j=0; j<pFK->nCol; j++){
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, 0,
+ aiCols ? aiCols[j] : pFK->aCol[j].iFrom, regRow+j);
+ sqlite3VdbeAddOp2(v, OP_IsNull, regRow+j, addrOk); VdbeCoverage(v);
+ }
+ if( pParent ){
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, pFK->nCol, regKey,
+ sqlite3IndexAffinityStr(db,pIdx), pFK->nCol);
+ sqlite3VdbeAddOp4Int(v, OP_Found, i, addrOk, regKey, 0);
+ VdbeCoverage(v);
+ }
+ }
+ sqlite3VdbeAddOp2(v, OP_Rowid, 0, regResult+1);
+ sqlite3VdbeMultiLoad(v, regResult+2, "si", pFK->zTo, i-1);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, 4);
+ sqlite3VdbeResolveLabel(v, addrOk);
+ sqlite3DbFree(db, aiCols);
+ }
+ sqlite3VdbeAddOp2(v, OP_Next, 0, addrTop+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addrTop);
+ }
+ }
+ break;
+#endif /* !defined(SQLITE_OMIT_TRIGGER) */
#endif /* !defined(SQLITE_OMIT_FOREIGN_KEY) */
#ifndef NDEBUG
- if( sqlite3StrICmp(zLeft, "parser_trace")==0 ){
+ case PragTyp_PARSER_TRACE: {
if( zRight ){
- if( sqlite3GetBoolean(zRight) ){
- sqlite3ParserTrace(stderr, "parser: ");
+ if( sqlite3GetBoolean(zRight, 0) ){
+ sqlite3ParserTrace(stdout, "parser: ");
}else{
sqlite3ParserTrace(0, 0);
}
}
- }else
+ }
+ break;
#endif
/* Reinstall the LIKE and GLOB functions. The variant of LIKE
** used will be case sensitive or not depending on the RHS.
*/
- if( sqlite3StrICmp(zLeft, "case_sensitive_like")==0 ){
+ case PragTyp_CASE_SENSITIVE_LIKE: {
if( zRight ){
- sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight));
+ sqlite3RegisterLikeFunctions(db, sqlite3GetBoolean(zRight, 0));
}
- }else
+ }
+ break;
#ifndef SQLITE_INTEGRITY_CHECK_ERROR_MAX
# define SQLITE_INTEGRITY_CHECK_ERROR_MAX 100
#endif
#ifndef SQLITE_OMIT_INTEGRITY_CHECK
- /* Pragma "quick_check" is an experimental reduced version of
+ /* Pragma "quick_check" is reduced version of
** integrity_check designed to detect most database corruption
** without most of the overhead of a full integrity-check.
*/
- if( sqlite3StrICmp(zLeft, "integrity_check")==0
- || sqlite3StrICmp(zLeft, "quick_check")==0
- ){
+ case PragTyp_INTEGRITY_CHECK: {
int i, j, addr, mxErr;
- /* Code that appears at the end of the integrity check. If no error
- ** messages have been generated, output OK. Otherwise output the
- ** error message
- */
- static const VdbeOpList endCode[] = {
- { OP_AddImm, 1, 0, 0}, /* 0 */
- { OP_IfNeg, 1, 0, 0}, /* 1 */
- { OP_String8, 0, 3, 0}, /* 2 */
- { OP_ResultRow, 3, 1, 0},
- };
+ int isQuick = (sqlite3Tolower(zLeft[0])=='q');
- int isQuick = (zLeft[0]=='q');
+ /* If the PRAGMA command was of the form "PRAGMA <db>.integrity_check",
+ ** then iDb is set to the index of the database identified by <db>.
+ ** In this case, the integrity of database iDb only is verified by
+ ** the VDBE created below.
+ **
+ ** Otherwise, if the command was simply "PRAGMA integrity_check" (or
+ ** "PRAGMA quick_check"), then iDb is set to 0. In this case, set iDb
+ ** to -1 here, to indicate that the VDBE should verify the integrity
+ ** of all attached databases. */
+ assert( iDb>=0 );
+ assert( iDb==0 || pId2->z );
+ if( pId2->z==0 ) iDb = -1;
/* Initialize the VDBE program */
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
pParse->nMem = 6;
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "integrity_check", SQLITE_STATIC);
+ setOneColumnName(v, "integrity_check");
/* Set the maximum error count */
mxErr = SQLITE_INTEGRITY_CHECK_ERROR_MAX;
@@ -91059,44 +112960,55 @@ SQLITE_PRIVATE void sqlite3Pragma(
for(i=0; i<db->nDb; i++){
HashElem *x;
Hash *pTbls;
+ int *aRoot;
int cnt = 0;
+ int mxIdx = 0;
+ int nIdx;
if( OMIT_TEMPDB && i==1 ) continue;
+ if( iDb>=0 && i!=iDb ) continue;
sqlite3CodeVerifySchema(pParse, i);
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Halt if out of errors */
+ VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
/* Do an integrity check of the B-Tree
**
- ** Begin by filling registers 2, 3, ... with the root pages numbers
+ ** Begin by finding the root pages numbers
** for all tables and indices in the database.
*/
- assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
+ assert( sqlite3SchemaMutexHeld(db, i, 0) );
pTbls = &db->aDb[i].pSchema->tblHash;
- for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
+ Table *pTab = sqliteHashData(x);
+ Index *pIdx;
+ if( HasRowid(pTab) ) cnt++;
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
+ if( nIdx>mxIdx ) mxIdx = nIdx;
+ }
+ aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1));
+ if( aRoot==0 ) break;
+ for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
- sqlite3VdbeAddOp2(v, OP_Integer, pTab->tnum, 2+cnt);
- cnt++;
+ if( HasRowid(pTab) ) aRoot[cnt++] = pTab->tnum;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- sqlite3VdbeAddOp2(v, OP_Integer, pIdx->tnum, 2+cnt);
- cnt++;
+ aRoot[cnt++] = pIdx->tnum;
}
}
+ aRoot[cnt] = 0;
/* Make sure sufficient number of registers have been allocated */
- if( pParse->nMem < cnt+4 ){
- pParse->nMem = cnt+4;
- }
+ pParse->nMem = MAX( pParse->nMem, 8+mxIdx );
/* Do the b-tree integrity checks */
- sqlite3VdbeAddOp3(v, OP_IntegrityCk, 2, cnt, 1);
+ sqlite3VdbeAddOp4(v, OP_IntegrityCk, 2, cnt, 1, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u8)i);
- addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2);
+ addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
- sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zName),
+ sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
P4_DYNAMIC);
sqlite3VdbeAddOp3(v, OP_Move, 2, 4, 1);
sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 2);
@@ -91107,77 +113019,137 @@ SQLITE_PRIVATE void sqlite3Pragma(
*/
for(x=sqliteHashFirst(pTbls); x && !isQuick; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
- Index *pIdx;
+ Index *pIdx, *pPk;
+ Index *pPrior = 0;
int loopTop;
+ int iDataCur, iIdxCur;
+ int r1 = -1;
if( pTab->pIndex==0 ) continue;
+ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1); /* Stop if out of errors */
+ VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
sqlite3VdbeJumpHere(v, addr);
- sqlite3OpenTableAndIndices(pParse, pTab, 1, OP_OpenRead);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, 2); /* reg(2) will count entries */
- loopTop = sqlite3VdbeAddOp2(v, OP_Rewind, 1, 0);
- sqlite3VdbeAddOp2(v, OP_AddImm, 2, 1); /* increment entry count */
+ sqlite3ExprCacheClear(pParse);
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenRead, 0,
+ 1, 0, &iDataCur, &iIdxCur);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, 7);
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- int jmp2;
- int r1;
- static const VdbeOpList idxErr[] = {
- { OP_AddImm, 1, -1, 0},
- { OP_String8, 0, 3, 0}, /* 1 */
- { OP_Rowid, 1, 4, 0},
- { OP_String8, 0, 5, 0}, /* 3 */
- { OP_String8, 0, 6, 0}, /* 4 */
- { OP_Concat, 4, 3, 3},
- { OP_Concat, 5, 3, 3},
- { OP_Concat, 6, 3, 3},
- { OP_ResultRow, 3, 1, 0},
- { OP_IfPos, 1, 0, 0}, /* 9 */
- { OP_Halt, 0, 0, 0},
- };
- r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0);
- jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1);
- addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr);
- sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC);
- sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC);
- sqlite3VdbeChangeP4(v, addr+4, pIdx->zName, P4_TRANSIENT);
- sqlite3VdbeJumpHere(v, addr+9);
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, 8+j); /* index entries counter */
+ }
+ assert( pParse->nMem>=8+j );
+ assert( sqlite3NoTempsInRange(pParse,1,7+j) );
+ sqlite3VdbeAddOp2(v, OP_Rewind, iDataCur, 0); VdbeCoverage(v);
+ loopTop = sqlite3VdbeAddOp2(v, OP_AddImm, 7, 1);
+ /* Verify that all NOT NULL columns really are NOT NULL */
+ for(j=0; j<pTab->nCol; j++){
+ char *zErr;
+ int jmp2, jmp3;
+ if( j==pTab->iPKey ) continue;
+ if( pTab->aCol[j].notNull==0 ) continue;
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, j, 3);
+ sqlite3VdbeChangeP5(v, OPFLAG_TYPEOFARG);
+ jmp2 = sqlite3VdbeAddOp1(v, OP_NotNull, 3); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
+ zErr = sqlite3MPrintf(db, "NULL value in %s.%s", pTab->zName,
+ pTab->aCol[j].zName);
+ sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, zErr, P4_DYNAMIC);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
+ jmp3 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); VdbeCoverage(v);
+ sqlite3VdbeAddOp0(v, OP_Halt);
sqlite3VdbeJumpHere(v, jmp2);
+ sqlite3VdbeJumpHere(v, jmp3);
}
- sqlite3VdbeAddOp2(v, OP_Next, 1, loopTop+1);
- sqlite3VdbeJumpHere(v, loopTop);
+ /* Validate index entries for the current row */
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
- static const VdbeOpList cntIdx[] = {
- { OP_Integer, 0, 3, 0},
- { OP_Rewind, 0, 0, 0}, /* 1 */
- { OP_AddImm, 3, 1, 0},
- { OP_Next, 0, 0, 0}, /* 3 */
- { OP_Eq, 2, 0, 3}, /* 4 */
- { OP_AddImm, 1, -1, 0},
- { OP_String8, 0, 2, 0}, /* 6 */
- { OP_String8, 0, 3, 0}, /* 7 */
- { OP_Concat, 3, 2, 2},
- { OP_ResultRow, 2, 1, 0},
- };
- addr = sqlite3VdbeAddOp1(v, OP_IfPos, 1);
+ int jmp2, jmp3, jmp4, jmp5;
+ int ckUniq = sqlite3VdbeMakeLabel(v);
+ if( pPk==pIdx ) continue;
+ r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 0, &jmp3,
+ pPrior, r1);
+ pPrior = pIdx;
+ sqlite3VdbeAddOp2(v, OP_AddImm, 8+j, 1); /* increment entry count */
+ /* Verify that an index entry exists for the current table row */
+ jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, iIdxCur+j, ckUniq, r1,
+ pIdx->nColumn); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
+ sqlite3VdbeLoadString(v, 3, "row ");
+ sqlite3VdbeAddOp3(v, OP_Concat, 7, 3, 3);
+ sqlite3VdbeLoadString(v, 4, " missing from index ");
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
+ jmp5 = sqlite3VdbeLoadString(v, 4, pIdx->zName);
+ sqlite3VdbeAddOp3(v, OP_Concat, 4, 3, 3);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
+ jmp4 = sqlite3VdbeAddOp1(v, OP_IfPos, 1); VdbeCoverage(v);
+ sqlite3VdbeAddOp0(v, OP_Halt);
+ sqlite3VdbeJumpHere(v, jmp2);
+ /* For UNIQUE indexes, verify that only one entry exists with the
+ ** current key. The entry is unique if (1) any column is NULL
+ ** or (2) the next entry has a different key */
+ if( IsUniqueIndex(pIdx) ){
+ int uniqOk = sqlite3VdbeMakeLabel(v);
+ int jmp6;
+ int kk;
+ for(kk=0; kk<pIdx->nKeyCol; kk++){
+ int iCol = pIdx->aiColumn[kk];
+ assert( iCol!=XN_ROWID && iCol<pTab->nCol );
+ if( iCol>=0 && pTab->aCol[iCol].notNull ) continue;
+ sqlite3VdbeAddOp2(v, OP_IsNull, r1+kk, uniqOk);
+ VdbeCoverage(v);
+ }
+ jmp6 = sqlite3VdbeAddOp1(v, OP_Next, iIdxCur+j); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, uniqOk);
+ sqlite3VdbeJumpHere(v, jmp6);
+ sqlite3VdbeAddOp4Int(v, OP_IdxGT, iIdxCur+j, uniqOk, r1,
+ pIdx->nKeyCol); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1); /* Decrement error limit */
+ sqlite3VdbeLoadString(v, 3, "non-unique entry in index ");
+ sqlite3VdbeGoto(v, jmp5);
+ sqlite3VdbeResolveLabel(v, uniqOk);
+ }
+ sqlite3VdbeJumpHere(v, jmp4);
+ sqlite3ResolvePartIdxLabel(pParse, jmp3);
+ }
+ sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, loopTop-1);
+#ifndef SQLITE_OMIT_BTREECOUNT
+ sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
+ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
+ if( pPk==pIdx ) continue;
+ addr = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_IfPos, 1, addr+2); VdbeCoverage(v);
sqlite3VdbeAddOp2(v, OP_Halt, 0, 0);
- sqlite3VdbeJumpHere(v, addr);
- addr = sqlite3VdbeAddOpList(v, ArraySize(cntIdx), cntIdx);
- sqlite3VdbeChangeP1(v, addr+1, j+2);
- sqlite3VdbeChangeP2(v, addr+1, addr+4);
- sqlite3VdbeChangeP1(v, addr+3, j+2);
- sqlite3VdbeChangeP2(v, addr+3, addr+2);
- sqlite3VdbeJumpHere(v, addr+4);
- sqlite3VdbeChangeP4(v, addr+6,
- "wrong # of entries in index ", P4_STATIC);
- sqlite3VdbeChangeP4(v, addr+7, pIdx->zName, P4_TRANSIENT);
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
+ sqlite3VdbeAddOp3(v, OP_Eq, 8+j, addr+8, 3); VdbeCoverage(v);
+ sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
+ sqlite3VdbeAddOp2(v, OP_AddImm, 1, -1);
+ sqlite3VdbeLoadString(v, 3, pIdx->zName);
+ sqlite3VdbeAddOp3(v, OP_Concat, 3, 2, 7);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, 7, 1);
}
+#endif /* SQLITE_OMIT_BTREECOUNT */
}
}
- addr = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode);
- sqlite3VdbeChangeP2(v, addr, -mxErr);
- sqlite3VdbeJumpHere(v, addr+1);
- sqlite3VdbeChangeP4(v, addr+2, "ok", P4_STATIC);
- }else
+ {
+ static const int iLn = VDBE_OFFSET_LINENO(2);
+ static const VdbeOpList endCode[] = {
+ { OP_AddImm, 1, 0, 0}, /* 0 */
+ { OP_If, 1, 4, 0}, /* 1 */
+ { OP_String8, 0, 3, 0}, /* 2 */
+ { OP_ResultRow, 3, 1, 0}, /* 3 */
+ };
+ VdbeOp *aOp;
+
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(endCode), endCode, iLn);
+ if( aOp ){
+ aOp[0].p2 = -mxErr;
+ aOp[2].p4type = P4_STATIC;
+ aOp[2].p4.z = "ok";
+ }
+ }
+ }
+ break;
#endif /* SQLITE_OMIT_INTEGRITY_CHECK */
#ifndef SQLITE_OMIT_UTF16
@@ -91203,7 +113175,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
** new database files created using this database handle. It is only
** useful if invoked immediately after the main database i
*/
- if( sqlite3StrICmp(zLeft, "encoding")==0 ){
+ case PragTyp_ENCODING: {
static const struct EncName {
char *zName;
u8 enc;
@@ -91221,14 +113193,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
const struct EncName *pEnc;
if( !zRight ){ /* "PRAGMA encoding" */
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
- sqlite3VdbeSetNumCols(v, 1);
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "encoding", SQLITE_STATIC);
- sqlite3VdbeAddOp2(v, OP_String8, 0, 1);
assert( encnames[SQLITE_UTF8].enc==SQLITE_UTF8 );
assert( encnames[SQLITE_UTF16LE].enc==SQLITE_UTF16LE );
assert( encnames[SQLITE_UTF16BE].enc==SQLITE_UTF16BE );
- sqlite3VdbeChangeP4(v, -1, encnames[ENC(pParse->db)].zName, P4_STATIC);
- sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
+ returnSingleText(v, "encoding", encnames[ENC(pParse->db)].zName);
}else{ /* "PRAGMA encoding = XXX" */
/* Only change the value of sqlite.enc if the database handle is not
** initialized. If the main database exists, the new sqlite.enc value
@@ -91241,7 +113209,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
){
for(pEnc=&encnames[0]; pEnc->zName; pEnc++){
if( 0==sqlite3StrICmp(zRight, pEnc->zName) ){
- ENC(pParse->db) = pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE;
+ SCHEMA_ENC(db) = ENC(db) =
+ pEnc->enc ? pEnc->enc : SQLITE_UTF16NATIVE;
break;
}
}
@@ -91250,16 +113219,24 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_UTF16 */
#ifndef SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS
/*
- ** PRAGMA [database.]schema_version
- ** PRAGMA [database.]schema_version = <integer>
+ ** PRAGMA [schema.]schema_version
+ ** PRAGMA [schema.]schema_version = <integer>
+ **
+ ** PRAGMA [schema.]user_version
+ ** PRAGMA [schema.]user_version = <integer>
+ **
+ ** PRAGMA [schema.]freelist_count
**
- ** PRAGMA [database.]user_version
- ** PRAGMA [database.]user_version = <integer>
+ ** PRAGMA [schema.]data_version
+ **
+ ** PRAGMA [schema.]application_id
+ ** PRAGMA [schema.]application_id = <integer>
**
** The pragma's schema_version and user_version are used to set or get
** the value of the schema-version and user-version, respectively. Both
@@ -91279,36 +113256,23 @@ SQLITE_PRIVATE void sqlite3Pragma(
** The user-version is not used internally by SQLite. It may be used by
** applications for any purpose.
*/
- if( sqlite3StrICmp(zLeft, "schema_version")==0
- || sqlite3StrICmp(zLeft, "user_version")==0
- || sqlite3StrICmp(zLeft, "freelist_count")==0
- ){
- int iCookie; /* Cookie index. 1 for schema-cookie, 6 for user-cookie. */
+ case PragTyp_HEADER_VALUE: {
+ int iCookie = pPragma->iArg; /* Which cookie to read or write */
sqlite3VdbeUsesBtree(v, iDb);
- switch( zLeft[0] ){
- case 'f': case 'F':
- iCookie = BTREE_FREE_PAGE_COUNT;
- break;
- case 's': case 'S':
- iCookie = BTREE_SCHEMA_VERSION;
- break;
- default:
- iCookie = BTREE_USER_VERSION;
- break;
- }
-
- if( zRight && iCookie!=BTREE_FREE_PAGE_COUNT ){
+ if( zRight && (pPragma->mPragFlag & PragFlag_ReadOnly)==0 ){
/* Write the specified cookie value */
static const VdbeOpList setCookie[] = {
{ OP_Transaction, 0, 1, 0}, /* 0 */
- { OP_Integer, 0, 1, 0}, /* 1 */
- { OP_SetCookie, 0, 0, 1}, /* 2 */
+ { OP_SetCookie, 0, 0, 0}, /* 1 */
};
- int addr = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie);
- sqlite3VdbeChangeP1(v, addr, iDb);
- sqlite3VdbeChangeP1(v, addr+1, sqlite3Atoi(zRight));
- sqlite3VdbeChangeP1(v, addr+2, iDb);
- sqlite3VdbeChangeP2(v, addr+2, iCookie);
+ VdbeOp *aOp;
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(setCookie));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(setCookie), setCookie, 0);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[1].p2 = iCookie;
+ aOp[1].p3 = sqlite3Atoi(zRight);
}else{
/* Read the specified cookie value */
static const VdbeOpList readCookie[] = {
@@ -91316,14 +113280,19 @@ SQLITE_PRIVATE void sqlite3Pragma(
{ OP_ReadCookie, 0, 1, 0}, /* 1 */
{ OP_ResultRow, 1, 1, 0}
};
- int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie);
- sqlite3VdbeChangeP1(v, addr, iDb);
- sqlite3VdbeChangeP1(v, addr+1, iDb);
- sqlite3VdbeChangeP3(v, addr+1, iCookie);
+ VdbeOp *aOp;
+ sqlite3VdbeVerifyNoMallocRequired(v, ArraySize(readCookie));
+ aOp = sqlite3VdbeAddOpList(v, ArraySize(readCookie),readCookie,0);
+ if( ONLY_IF_REALLOC_STRESS(aOp==0) ) break;
+ aOp[0].p1 = iDb;
+ aOp[1].p1 = iDb;
+ aOp[1].p3 = iCookie;
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT);
+ sqlite3VdbeReusable(v);
}
- }else
+ }
+ break;
#endif /* SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS */
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
@@ -91333,26 +113302,28 @@ SQLITE_PRIVATE void sqlite3Pragma(
** Return the names of all compile-time options used in this build,
** one option per row.
*/
- if( sqlite3StrICmp(zLeft, "compile_options")==0 ){
+ case PragTyp_COMPILE_OPTIONS: {
int i = 0;
const char *zOpt;
- sqlite3VdbeSetNumCols(v, 1);
pParse->nMem = 1;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "compile_option", SQLITE_STATIC);
+ setOneColumnName(v, "compile_option");
while( (zOpt = sqlite3_compileoption_get(i++))!=0 ){
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, zOpt, 0);
+ sqlite3VdbeLoadString(v, 1, zOpt);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}
- }else
+ sqlite3VdbeReusable(v);
+ }
+ break;
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
#ifndef SQLITE_OMIT_WAL
/*
- ** PRAGMA [database.]wal_checkpoint = passive|full|restart
+ ** PRAGMA [schema.]wal_checkpoint = passive|full|restart|truncate
**
** Checkpoint the database.
*/
- if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){
+ case PragTyp_WAL_CHECKPOINT: {
+ static const char *azCol[] = { "busy", "log", "checkpointed" };
int iBt = (pId2->z?iDb:SQLITE_MAX_ATTACHED);
int eMode = SQLITE_CHECKPOINT_PASSIVE;
if( zRight ){
@@ -91360,18 +113331,16 @@ SQLITE_PRIVATE void sqlite3Pragma(
eMode = SQLITE_CHECKPOINT_FULL;
}else if( sqlite3StrICmp(zRight, "restart")==0 ){
eMode = SQLITE_CHECKPOINT_RESTART;
+ }else if( sqlite3StrICmp(zRight, "truncate")==0 ){
+ eMode = SQLITE_CHECKPOINT_TRUNCATE;
}
}
- if( sqlite3ReadSchema(pParse) ) goto pragma_out;
- sqlite3VdbeSetNumCols(v, 3);
+ setAllColumnNames(v, 3, azCol); assert( 3==ArraySize(azCol) );
pParse->nMem = 3;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "busy", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "log", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 2, COLNAME_NAME, "checkpointed", SQLITE_STATIC);
-
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
- }else
+ }
+ break;
/*
** PRAGMA wal_autocheckpoint
@@ -91381,75 +113350,147 @@ SQLITE_PRIVATE void sqlite3Pragma(
** after accumulating N frames in the log. Or query for the current value
** of N.
*/
- if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){
+ case PragTyp_WAL_AUTOCHECKPOINT: {
if( zRight ){
sqlite3_wal_autocheckpoint(db, sqlite3Atoi(zRight));
}
- returnSingleInt(pParse, "wal_autocheckpoint",
+ returnSingleInt(v, "wal_autocheckpoint",
db->xWalCallback==sqlite3WalDefaultHook ?
SQLITE_PTR_TO_INT(db->pWalArg) : 0);
- }else
+ }
+ break;
#endif
+ /*
+ ** PRAGMA shrink_memory
+ **
+ ** IMPLEMENTATION-OF: R-23445-46109 This pragma causes the database
+ ** connection on which it is invoked to free up as much memory as it
+ ** can, by calling sqlite3_db_release_memory().
+ */
+ case PragTyp_SHRINK_MEMORY: {
+ sqlite3_db_release_memory(db);
+ break;
+ }
+
+ /*
+ ** PRAGMA busy_timeout
+ ** PRAGMA busy_timeout = N
+ **
+ ** Call sqlite3_busy_timeout(db, N). Return the current timeout value
+ ** if one is set. If no busy handler or a different busy handler is set
+ ** then 0 is returned. Setting the busy_timeout to 0 or negative
+ ** disables the timeout.
+ */
+ /*case PragTyp_BUSY_TIMEOUT*/ default: {
+ assert( pPragma->ePragTyp==PragTyp_BUSY_TIMEOUT );
+ if( zRight ){
+ sqlite3_busy_timeout(db, sqlite3Atoi(zRight));
+ }
+ returnSingleInt(v, "timeout", db->busyTimeout);
+ break;
+ }
+
+ /*
+ ** PRAGMA soft_heap_limit
+ ** PRAGMA soft_heap_limit = N
+ **
+ ** IMPLEMENTATION-OF: R-26343-45930 This pragma invokes the
+ ** sqlite3_soft_heap_limit64() interface with the argument N, if N is
+ ** specified and is a non-negative integer.
+ ** IMPLEMENTATION-OF: R-64451-07163 The soft_heap_limit pragma always
+ ** returns the same integer that would be returned by the
+ ** sqlite3_soft_heap_limit64(-1) C-language function.
+ */
+ case PragTyp_SOFT_HEAP_LIMIT: {
+ sqlite3_int64 N;
+ if( zRight && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK ){
+ sqlite3_soft_heap_limit64(N);
+ }
+ returnSingleInt(v, "soft_heap_limit", sqlite3_soft_heap_limit64(-1));
+ break;
+ }
+
+ /*
+ ** PRAGMA threads
+ ** PRAGMA threads = N
+ **
+ ** Configure the maximum number of worker threads. Return the new
+ ** maximum, which might be less than requested.
+ */
+ case PragTyp_THREADS: {
+ sqlite3_int64 N;
+ if( zRight
+ && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK
+ && N>=0
+ ){
+ sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff));
+ }
+ returnSingleInt(v, "threads",
+ sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1));
+ break;
+ }
+
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Report the current state of file logs for all databases
*/
- if( sqlite3StrICmp(zLeft, "lock_status")==0 ){
+ case PragTyp_LOCK_STATUS: {
static const char *const azLockName[] = {
"unlocked", "shared", "reserved", "pending", "exclusive"
};
+ static const char *azCol[] = { "database", "status" };
int i;
- sqlite3VdbeSetNumCols(v, 2);
+ setAllColumnNames(v, 2, azCol); assert( 2==ArraySize(azCol) );
pParse->nMem = 2;
- sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "database", SQLITE_STATIC);
- sqlite3VdbeSetColName(v, 1, COLNAME_NAME, "status", SQLITE_STATIC);
for(i=0; i<db->nDb; i++){
Btree *pBt;
- Pager *pPager;
const char *zState = "unknown";
int j;
- if( db->aDb[i].zName==0 ) continue;
- sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0, db->aDb[i].zName, P4_STATIC);
+ if( db->aDb[i].zDbSName==0 ) continue;
pBt = db->aDb[i].pBt;
- if( pBt==0 || (pPager = sqlite3BtreePager(pBt))==0 ){
+ if( pBt==0 || sqlite3BtreePager(pBt)==0 ){
zState = "closed";
- }else if( sqlite3_file_control(db, i ? db->aDb[i].zName : 0,
+ }else if( sqlite3_file_control(db, i ? db->aDb[i].zDbSName : 0,
SQLITE_FCNTL_LOCKSTATE, &j)==SQLITE_OK ){
zState = azLockName[j];
}
- sqlite3VdbeAddOp4(v, OP_String8, 0, 2, 0, zState, P4_STATIC);
+ sqlite3VdbeMultiLoad(v, 1, "ss", db->aDb[i].zDbSName, zState);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 2);
}
-
- }else
+ break;
+ }
#endif
#ifdef SQLITE_HAS_CODEC
- if( sqlite3StrICmp(zLeft, "key")==0 && zRight ){
- sqlite3_key(db, zRight, sqlite3Strlen30(zRight));
- }else
- if( sqlite3StrICmp(zLeft, "rekey")==0 && zRight ){
- sqlite3_rekey(db, zRight, sqlite3Strlen30(zRight));
- }else
- if( zRight && (sqlite3StrICmp(zLeft, "hexkey")==0 ||
- sqlite3StrICmp(zLeft, "hexrekey")==0) ){
- int i, h1, h2;
- char zKey[40];
- for(i=0; (h1 = zRight[i])!=0 && (h2 = zRight[i+1])!=0; i+=2){
- h1 += 9*(1&(h1>>6));
- h2 += 9*(1&(h2>>6));
- zKey[i/2] = (h2 & 0x0f) | ((h1 & 0xf)<<4);
- }
- if( (zLeft[3] & 0xf)==0xb ){
- sqlite3_key(db, zKey, i/2);
- }else{
- sqlite3_rekey(db, zKey, i/2);
+ case PragTyp_KEY: {
+ if( zRight ) sqlite3_key_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
+ break;
+ }
+ case PragTyp_REKEY: {
+ if( zRight ) sqlite3_rekey_v2(db, zDb, zRight, sqlite3Strlen30(zRight));
+ break;
+ }
+ case PragTyp_HEXKEY: {
+ if( zRight ){
+ u8 iByte;
+ int i;
+ char zKey[40];
+ for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zRight[i]); i++){
+ iByte = (iByte<<4) + sqlite3HexToInt(zRight[i]);
+ if( (i&1)!=0 ) zKey[i/2] = iByte;
+ }
+ if( (zLeft[3] & 0xf)==0xb ){
+ sqlite3_key_v2(db, zDb, zKey, i/2);
+ }else{
+ sqlite3_rekey_v2(db, zDb, zKey, i/2);
+ }
}
- }else
+ break;
+ }
#endif
#if defined(SQLITE_HAS_CODEC) || defined(SQLITE_ENABLE_CEROD)
- if( sqlite3StrICmp(zLeft, "activate_extensions")==0 ){
+ case PragTyp_ACTIVATE_EXTENSIONS: if( zRight ){
#ifdef SQLITE_HAS_CODEC
if( sqlite3StrNICmp(zRight, "see-", 4)==0 ){
sqlite3_activate_see(&zRight[4]);
@@ -91460,23 +113501,12 @@ SQLITE_PRIVATE void sqlite3Pragma(
sqlite3_activate_cerod(&zRight[6]);
}
#endif
- }else
+ }
+ break;
#endif
-
- {/* Empty ELSE clause */}
+ } /* End of the PRAGMA switch */
- /*
- ** Reset the safety level, in case the fullfsync flag or synchronous
- ** setting changed.
- */
-#ifndef SQLITE_OMIT_PAGER_PRAGMAS
- if( db->autoCommit ){
- sqlite3BtreeSetSafetyLevel(pDb->pBt, pDb->safety_level,
- (db->flags&SQLITE_FullFSync)!=0,
- (db->flags&SQLITE_CkptFullFSync)!=0);
- }
-#endif
pragma_out:
sqlite3DbFree(db, zLeft);
sqlite3DbFree(db, zRight);
@@ -91501,6 +113531,7 @@ pragma_out:
** interface, and routines that contribute to loading the database schema
** from disk.
*/
+/* #include "sqliteInt.h" */
/*
** Fill the InitData structure with an error message that indicates
@@ -91513,15 +113544,14 @@ static void corruptSchema(
){
sqlite3 *db = pData->db;
if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){
+ char *z;
if( zObj==0 ) zObj = "?";
- sqlite3SetString(pData->pzErrMsg, db,
- "malformed database schema (%s)", zObj);
- if( zExtra ){
- *pData->pzErrMsg = sqlite3MAppendf(db, *pData->pzErrMsg,
- "%s - %s", *pData->pzErrMsg, zExtra);
- }
+ z = sqlite3MPrintf(db, "malformed database schema (%s)", zObj);
+ if( zExtra ) z = sqlite3MPrintf(db, "%z - %s", z, zExtra);
+ sqlite3DbFree(db, *pData->pzErrMsg);
+ *pData->pzErrMsg = z;
}
- pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT;
+ pData->rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_CORRUPT_BKPT;
}
/*
@@ -91554,13 +113584,14 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
if( argv==0 ) return 0; /* Might happen if EMPTY_RESULT_CALLBACKS are on */
if( argv[1]==0 ){
corruptSchema(pData, argv[0], 0);
- }else if( argv[2] && argv[2][0] ){
+ }else if( sqlite3_strnicmp(argv[2],"create ",7)==0 ){
/* Call the parser to process a CREATE TABLE, INDEX or VIEW.
** But because db->init.busy is set to 1, no VDBE code is generated
** or executed. All the parser does is build the internal data
** structures that describe the table, index, or view.
*/
int rc;
+ u8 saved_iDb = db->init.iDb;
sqlite3_stmt *pStmt;
TESTONLY(int rcp); /* Return code from sqlite3_prepare() */
@@ -91571,22 +113602,23 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
TESTONLY(rcp = ) sqlite3_prepare(db, argv[2], -1, &pStmt, 0);
rc = db->errCode;
assert( (rc&0xFF)==(rcp&0xFF) );
- db->init.iDb = 0;
+ db->init.iDb = saved_iDb;
+ assert( saved_iDb==0 || (db->flags & SQLITE_Vacuum)!=0 );
if( SQLITE_OK!=rc ){
if( db->init.orphanTrigger ){
assert( iDb==1 );
}else{
pData->rc = rc;
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if( rc!=SQLITE_INTERRUPT && (rc&0xFF)!=SQLITE_LOCKED ){
corruptSchema(pData, argv[0], sqlite3_errmsg(db));
}
}
}
sqlite3_finalize(pStmt);
- }else if( argv[0]==0 ){
- corruptSchema(pData, 0, 0);
+ }else if( argv[0]==0 || (argv[2]!=0 && argv[2][0]!=0) ){
+ corruptSchema(pData, argv[0], 0);
}else{
/* If the SQL column is blank it means this is an index that
** was created to be the PRIMARY KEY or to fulfill a UNIQUE
@@ -91595,7 +113627,7 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
** to do here is record the root page number for that index.
*/
Index *pIndex;
- pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zName);
+ pIndex = sqlite3FindIndex(db, argv[0], db->aDb[iDb].zDbSName);
if( pIndex==0 ){
/* This can occur if there exists an index on a TEMP table which
** has the same name as another index on a permanent index. Since
@@ -91621,62 +113653,30 @@ SQLITE_PRIVATE int sqlite3InitCallback(void *pInit, int argc, char **argv, char
static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
int rc;
int i;
+#ifndef SQLITE_OMIT_DEPRECATED
int size;
- Table *pTab;
+#endif
Db *pDb;
char const *azArg[4];
int meta[5];
InitData initData;
- char const *zMasterSchema;
- char const *zMasterName;
+ const char *zMasterName;
int openedTransaction = 0;
- /*
- ** The master database table has a structure like this
- */
- static const char master_schema[] =
- "CREATE TABLE sqlite_master(\n"
- " type text,\n"
- " name text,\n"
- " tbl_name text,\n"
- " rootpage integer,\n"
- " sql text\n"
- ")"
- ;
-#ifndef SQLITE_OMIT_TEMPDB
- static const char temp_master_schema[] =
- "CREATE TEMP TABLE sqlite_temp_master(\n"
- " type text,\n"
- " name text,\n"
- " tbl_name text,\n"
- " rootpage integer,\n"
- " sql text\n"
- ")"
- ;
-#else
- #define temp_master_schema 0
-#endif
-
assert( iDb>=0 && iDb<db->nDb );
assert( db->aDb[iDb].pSchema );
assert( sqlite3_mutex_held(db->mutex) );
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
- /* zMasterSchema and zInitScript are set to point at the master schema
- ** and initialisation script appropriate for the database being
- ** initialised. zMasterName is the name of the master table.
- */
- if( !OMIT_TEMPDB && iDb==1 ){
- zMasterSchema = temp_master_schema;
- }else{
- zMasterSchema = master_schema;
- }
- zMasterName = SCHEMA_TABLE(iDb);
-
- /* Construct the schema tables. */
- azArg[0] = zMasterName;
+ /* Construct the in-memory representation schema tables (sqlite_master or
+ ** sqlite_temp_master) by invoking the parser directly. The appropriate
+ ** table name will be inserted automatically by the parser so we can just
+ ** use the abbreviation "x" here. The parser will also automatically tag
+ ** the schema table as read-only. */
+ azArg[0] = zMasterName = SCHEMA_TABLE(iDb);
azArg[1] = "1";
- azArg[2] = zMasterSchema;
+ azArg[2] = "CREATE TABLE x(type text,name text,tbl_name text,"
+ "rootpage integer,sql text)";
azArg[3] = 0;
initData.db = db;
initData.iDb = iDb;
@@ -91687,10 +113687,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
rc = initData.rc;
goto error_out;
}
- pTab = sqlite3FindTable(db, zMasterName, db->aDb[iDb].zName);
- if( ALWAYS(pTab) ){
- pTab->tabFlags |= TF_Readonly;
- }
/* Create a cursor to hold the database open
*/
@@ -91709,7 +113705,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){
rc = sqlite3BtreeBeginTrans(pDb->pBt, 0);
if( rc!=SQLITE_OK ){
- sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc));
+ sqlite3SetString(pzErrMsg, db, sqlite3ErrStr(rc));
goto initone_error_out;
}
openedTransaction = 1;
@@ -91744,12 +113740,15 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
*/
if( meta[BTREE_TEXT_ENCODING-1] ){ /* text encoding */
if( iDb==0 ){
+#ifndef SQLITE_OMIT_UTF16
u8 encoding;
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
ENC(db) = encoding;
- db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0);
+#else
+ ENC(db) = SQLITE_UTF8;
+#endif
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( meta[BTREE_TEXT_ENCODING-1]!=ENC(db) ){
@@ -91765,9 +113764,13 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
pDb->pSchema->enc = ENC(db);
if( pDb->pSchema->cache_size==0 ){
+#ifndef SQLITE_OMIT_DEPRECATED
size = sqlite3AbsInt32(meta[BTREE_DEFAULT_CACHE_SIZE-1]);
if( size==0 ){ size = SQLITE_DEFAULT_CACHE_SIZE; }
pDb->pSchema->cache_size = size;
+#else
+ pDb->pSchema->cache_size = SQLITE_DEFAULT_CACHE_SIZE;
+#endif
sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size);
}
@@ -91802,11 +113805,11 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
{
char *zSql;
zSql = sqlite3MPrintf(db,
- "SELECT name, rootpage, sql FROM '%q'.%s ORDER BY rowid",
- db->aDb[iDb].zName, zMasterName);
+ "SELECT name, rootpage, sql FROM \"%w\".%s ORDER BY rowid",
+ db->aDb[iDb].zDbSName, zMasterName);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
- int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
+ sqlite3_xauth xAuth;
xAuth = db->xAuth;
db->xAuth = 0;
#endif
@@ -91824,8 +113827,8 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){
#endif
}
if( db->mallocFailed ){
- rc = SQLITE_NOMEM;
- sqlite3ResetInternalSchema(db, -1);
+ rc = SQLITE_NOMEM_BKPT;
+ sqlite3ResetAllSchemasOfConnection(db);
}
if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){
/* Black magic: If the SQLITE_RecoveryMode flag is set, then consider
@@ -91852,7 +113855,7 @@ initone_error_out:
error_out:
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
return rc;
}
@@ -91872,26 +113875,29 @@ SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){
int commit_internal = !(db->flags&SQLITE_InternChanges);
assert( sqlite3_mutex_held(db->mutex) );
+ assert( sqlite3BtreeHoldsMutex(db->aDb[0].pBt) );
+ assert( db->init.busy==0 );
rc = SQLITE_OK;
db->init.busy = 1;
+ ENC(db) = SCHEMA_ENC(db);
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
if( DbHasProperty(db, i, DB_SchemaLoaded) || i==1 ) continue;
rc = sqlite3InitOne(db, i, pzErrMsg);
if( rc ){
- sqlite3ResetInternalSchema(db, i);
+ sqlite3ResetOneSchema(db, i);
}
}
- /* Once all the other databases have been initialised, load the schema
+ /* Once all the other databases have been initialized, load the schema
** for the TEMP database. This is loaded last, as the TEMP database
** schema may contain references to objects in other databases.
*/
#ifndef SQLITE_OMIT_TEMPDB
- if( rc==SQLITE_OK && ALWAYS(db->nDb>1)
- && !DbHasProperty(db, 1, DB_SchemaLoaded) ){
+ assert( db->nDb>1 );
+ if( rc==SQLITE_OK && !DbHasProperty(db, 1, DB_SchemaLoaded) ){
rc = sqlite3InitOne(db, 1, pzErrMsg);
if( rc ){
- sqlite3ResetInternalSchema(db, 1);
+ sqlite3ResetOneSchema(db, 1);
}
}
#endif
@@ -91905,7 +113911,7 @@ SQLITE_PRIVATE int sqlite3Init(sqlite3 *db, char **pzErrMsg){
}
/*
-** This routine is a no-op if the database schema is already initialised.
+** This routine is a no-op if the database schema is already initialized.
** Otherwise, the schema is loaded. An error code is returned.
*/
SQLITE_PRIVATE int sqlite3ReadSchema(Parse *pParse){
@@ -91947,7 +113953,7 @@ static void schemaIsValid(Parse *pParse){
if( !sqlite3BtreeIsInReadTrans(pBt) ){
rc = sqlite3BtreeBeginTrans(pBt, 0);
if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
if( rc!=SQLITE_OK ) return;
openedTransaction = 1;
@@ -91959,7 +113965,7 @@ static void schemaIsValid(Parse *pParse){
sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie);
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){
- sqlite3ResetInternalSchema(db, iDb);
+ sqlite3ResetOneSchema(db, iDb);
pParse->rc = SQLITE_SCHEMA;
}
@@ -92003,6 +114009,22 @@ SQLITE_PRIVATE int sqlite3SchemaToIndex(sqlite3 *db, Schema *pSchema){
}
/*
+** Free all memory allocations in the pParse object
+*/
+SQLITE_PRIVATE void sqlite3ParserReset(Parse *pParse){
+ if( pParse ){
+ sqlite3 *db = pParse->db;
+ sqlite3DbFree(db, pParse->aLabel);
+ sqlite3ExprListDelete(db, pParse->pConstExpr);
+ if( db ){
+ assert( db->lookaside.bDisable >= pParse->disableLookaside );
+ db->lookaside.bDisable -= pParse->disableLookaside;
+ }
+ pParse->disableLookaside = 0;
+ }
+}
+
+/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare(
@@ -92014,20 +114036,16 @@ static int sqlite3Prepare(
sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */
const char **pzTail /* OUT: End of parsed string */
){
- Parse *pParse; /* Parsing context */
char *zErrMsg = 0; /* Error message */
int rc = SQLITE_OK; /* Result code */
int i; /* Loop counter */
+ Parse sParse; /* Parsing context */
- /* Allocate the parsing context */
- pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
- if( pParse==0 ){
- rc = SQLITE_NOMEM;
- goto end_prepare;
- }
- pParse->pReprepare = pReprepare;
+ memset(&sParse, 0, PARSE_HDR_SZ);
+ memset(PARSE_TAIL(&sParse), 0, PARSE_TAIL_SZ);
+ sParse.pReprepare = pReprepare;
assert( ppStmt && *ppStmt==0 );
- assert( !db->mallocFailed );
+ /* assert( !db->mallocFailed ); // not true with SQLITE_USE_ALLOCA */
assert( sqlite3_mutex_held(db->mutex) );
/* Check to verify that it is possible to get a read lock on all
@@ -92059,8 +114077,8 @@ static int sqlite3Prepare(
assert( sqlite3BtreeHoldsMutex(pBt) );
rc = sqlite3BtreeSchemaLocked(pBt);
if( rc ){
- const char *zDb = db->aDb[i].zName;
- sqlite3Error(db, rc, "database schema is locked: %s", zDb);
+ const char *zDb = db->aDb[i].zDbSName;
+ sqlite3ErrorWithMsg(db, rc, "database schema is locked: %s", zDb);
testcase( db->flags & SQLITE_ReadUncommitted );
goto end_prepare;
}
@@ -92069,98 +114087,93 @@ static int sqlite3Prepare(
sqlite3VtabUnlockList(db);
- pParse->db = db;
- pParse->nQueryLoop = (double)1;
+ sParse.db = db;
if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){
char *zSqlCopy;
int mxLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
testcase( nBytes==mxLen );
testcase( nBytes==mxLen+1 );
if( nBytes>mxLen ){
- sqlite3Error(db, SQLITE_TOOBIG, "statement too long");
+ sqlite3ErrorWithMsg(db, SQLITE_TOOBIG, "statement too long");
rc = sqlite3ApiExit(db, SQLITE_TOOBIG);
goto end_prepare;
}
zSqlCopy = sqlite3DbStrNDup(db, zSql, nBytes);
if( zSqlCopy ){
- sqlite3RunParser(pParse, zSqlCopy, &zErrMsg);
+ sqlite3RunParser(&sParse, zSqlCopy, &zErrMsg);
+ sParse.zTail = &zSql[sParse.zTail-zSqlCopy];
sqlite3DbFree(db, zSqlCopy);
- pParse->zTail = &zSql[pParse->zTail-zSqlCopy];
}else{
- pParse->zTail = &zSql[nBytes];
+ sParse.zTail = &zSql[nBytes];
}
}else{
- sqlite3RunParser(pParse, zSql, &zErrMsg);
+ sqlite3RunParser(&sParse, zSql, &zErrMsg);
}
- assert( 1==(int)pParse->nQueryLoop );
+ assert( 0==sParse.nQueryLoop );
- if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM;
- }
- if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK;
- if( pParse->checkSchema ){
- schemaIsValid(pParse);
+ if( sParse.rc==SQLITE_DONE ) sParse.rc = SQLITE_OK;
+ if( sParse.checkSchema ){
+ schemaIsValid(&sParse);
}
if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM;
+ sParse.rc = SQLITE_NOMEM_BKPT;
}
if( pzTail ){
- *pzTail = pParse->zTail;
+ *pzTail = sParse.zTail;
}
- rc = pParse->rc;
+ rc = sParse.rc;
#ifndef SQLITE_OMIT_EXPLAIN
- if( rc==SQLITE_OK && pParse->pVdbe && pParse->explain ){
+ if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
static const char * const azColName[] = {
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
"selectid", "order", "from", "detail"
};
int iFirst, mx;
- if( pParse->explain==2 ){
- sqlite3VdbeSetNumCols(pParse->pVdbe, 4);
+ if( sParse.explain==2 ){
+ sqlite3VdbeSetNumCols(sParse.pVdbe, 4);
iFirst = 8;
mx = 12;
}else{
- sqlite3VdbeSetNumCols(pParse->pVdbe, 8);
+ sqlite3VdbeSetNumCols(sParse.pVdbe, 8);
iFirst = 0;
mx = 8;
}
for(i=iFirst; i<mx; i++){
- sqlite3VdbeSetColName(pParse->pVdbe, i-iFirst, COLNAME_NAME,
+ sqlite3VdbeSetColName(sParse.pVdbe, i-iFirst, COLNAME_NAME,
azColName[i], SQLITE_STATIC);
}
}
#endif
- assert( db->init.busy==0 || saveSqlFlag==0 );
if( db->init.busy==0 ){
- Vdbe *pVdbe = pParse->pVdbe;
- sqlite3VdbeSetSql(pVdbe, zSql, (int)(pParse->zTail-zSql), saveSqlFlag);
+ Vdbe *pVdbe = sParse.pVdbe;
+ sqlite3VdbeSetSql(pVdbe, zSql, (int)(sParse.zTail-zSql), saveSqlFlag);
}
- if( pParse->pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
- sqlite3VdbeFinalize(pParse->pVdbe);
+ if( sParse.pVdbe && (rc!=SQLITE_OK || db->mallocFailed) ){
+ sqlite3VdbeFinalize(sParse.pVdbe);
assert(!(*ppStmt));
}else{
- *ppStmt = (sqlite3_stmt*)pParse->pVdbe;
+ *ppStmt = (sqlite3_stmt*)sParse.pVdbe;
}
if( zErrMsg ){
- sqlite3Error(db, rc, "%s", zErrMsg);
+ sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
sqlite3DbFree(db, zErrMsg);
}else{
- sqlite3Error(db, rc, 0);
+ sqlite3Error(db, rc);
}
/* Delete any TriggerPrg structures allocated while parsing this statement. */
- while( pParse->pTriggerPrg ){
- TriggerPrg *pT = pParse->pTriggerPrg;
- pParse->pTriggerPrg = pT->pNext;
+ while( sParse.pTriggerPrg ){
+ TriggerPrg *pT = sParse.pTriggerPrg;
+ sParse.pTriggerPrg = pT->pNext;
sqlite3DbFree(db, pT);
}
end_prepare:
- sqlite3StackFree(db, pParse);
+ sqlite3ParserReset(&sParse);
rc = sqlite3ApiExit(db, rc);
assert( (rc&db->errMask)==rc );
return rc;
@@ -92175,9 +114188,12 @@ static int sqlite3LockAndPrepare(
const char **pzTail /* OUT: End of parsed string */
){
int rc;
- assert( ppStmt!=0 );
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
+#endif
*ppStmt = 0;
- if( !sqlite3SafetyCheckOk(db) ){
+ if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(db->mutex);
@@ -92189,6 +114205,7 @@ static int sqlite3LockAndPrepare(
}
sqlite3BtreeLeaveAll(db);
sqlite3_mutex_leave(db->mutex);
+ assert( rc==SQLITE_OK || *ppStmt==0 );
return rc;
}
@@ -92214,7 +114231,7 @@ SQLITE_PRIVATE int sqlite3Reprepare(Vdbe *p){
rc = sqlite3LockAndPrepare(db, zSql, -1, 0, p, &pNew, 0);
if( rc ){
if( rc==SQLITE_NOMEM ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}
assert( pNew==0 );
return rc;
@@ -92283,11 +114300,19 @@ static int sqlite3Prepare16(
const char *zTail8 = 0;
int rc = SQLITE_OK;
- assert( ppStmt );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( ppStmt==0 ) return SQLITE_MISUSE_BKPT;
+#endif
*ppStmt = 0;
- if( !sqlite3SafetyCheckOk(db) ){
+ if( !sqlite3SafetyCheckOk(db)||zSql==0 ){
return SQLITE_MISUSE_BKPT;
}
+ if( nBytes>=0 ){
+ int sz;
+ const char *z = (const char*)zSql;
+ for(sz=0; sz<nBytes && (z[sz]!=0 || z[sz+1]!=0); sz += 2){}
+ nBytes = sz;
+ }
sqlite3_mutex_enter(db->mutex);
zSql8 = sqlite3Utf16to8(db, zSql, nBytes, SQLITE_UTF16NATIVE);
if( zSql8 ){
@@ -92360,22 +114385,74 @@ SQLITE_API int sqlite3_prepare16_v2(
** This file contains C code routines that are called by the parser
** to handle SELECT statements in SQLite.
*/
+/* #include "sqliteInt.h" */
+
+/*
+** Trace output macros
+*/
+#if SELECTTRACE_ENABLED
+/***/ int sqlite3SelectTrace = 0;
+# define SELECTTRACE(K,P,S,X) \
+ if(sqlite3SelectTrace&(K)) \
+ sqlite3DebugPrintf("%*s%s.%p: ",(P)->nSelectIndent*2-2,"",\
+ (S)->zSelName,(S)),\
+ sqlite3DebugPrintf X
+#else
+# define SELECTTRACE(K,P,S,X)
+#endif
/*
-** Delete all the content of a Select structure but do not deallocate
-** the select structure itself.
+** An instance of the following object is used to record information about
+** how to process the DISTINCT keyword, to simplify passing that information
+** into the selectInnerLoop() routine.
*/
-static void clearSelect(sqlite3 *db, Select *p){
- sqlite3ExprListDelete(db, p->pEList);
- sqlite3SrcListDelete(db, p->pSrc);
- sqlite3ExprDelete(db, p->pWhere);
- sqlite3ExprListDelete(db, p->pGroupBy);
- sqlite3ExprDelete(db, p->pHaving);
- sqlite3ExprListDelete(db, p->pOrderBy);
- sqlite3SelectDelete(db, p->pPrior);
- sqlite3ExprDelete(db, p->pLimit);
- sqlite3ExprDelete(db, p->pOffset);
+typedef struct DistinctCtx DistinctCtx;
+struct DistinctCtx {
+ u8 isTnct; /* True if the DISTINCT keyword is present */
+ u8 eTnctType; /* One of the WHERE_DISTINCT_* operators */
+ int tabTnct; /* Ephemeral table used for DISTINCT processing */
+ int addrTnct; /* Address of OP_OpenEphemeral opcode for tabTnct */
+};
+
+/*
+** An instance of the following object is used to record information about
+** the ORDER BY (or GROUP BY) clause of query is being coded.
+*/
+typedef struct SortCtx SortCtx;
+struct SortCtx {
+ ExprList *pOrderBy; /* The ORDER BY (or GROUP BY clause) */
+ int nOBSat; /* Number of ORDER BY terms satisfied by indices */
+ int iECursor; /* Cursor number for the sorter */
+ int regReturn; /* Register holding block-output return address */
+ int labelBkOut; /* Start label for the block-output subroutine */
+ int addrSortIndex; /* Address of the OP_SorterOpen or OP_OpenEphemeral */
+ int labelDone; /* Jump here when done, ex: LIMIT reached */
+ u8 sortFlags; /* Zero or more SORTFLAG_* bits */
+ u8 bOrderedInnerLoop; /* ORDER BY correctly sorts the inner loop */
+};
+#define SORTFLAG_UseSorter 0x01 /* Use SorterOpen instead of OpenEphemeral */
+
+/*
+** Delete all the content of a Select structure. Deallocate the structure
+** itself only if bFree is true.
+*/
+static void clearSelect(sqlite3 *db, Select *p, int bFree){
+ while( p ){
+ Select *pPrior = p->pPrior;
+ sqlite3ExprListDelete(db, p->pEList);
+ sqlite3SrcListDelete(db, p->pSrc);
+ sqlite3ExprDelete(db, p->pWhere);
+ sqlite3ExprListDelete(db, p->pGroupBy);
+ sqlite3ExprDelete(db, p->pHaving);
+ sqlite3ExprListDelete(db, p->pOrderBy);
+ sqlite3ExprDelete(db, p->pLimit);
+ sqlite3ExprDelete(db, p->pOffset);
+ if( p->pWith ) sqlite3WithDelete(db, p->pWith);
+ if( bFree ) sqlite3DbFree(db, p);
+ p = pPrior;
+ bFree = 1;
+ }
}
/*
@@ -92383,10 +114460,10 @@ static void clearSelect(sqlite3 *db, Select *p){
*/
SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest *pDest, int eDest, int iParm){
pDest->eDest = (u8)eDest;
- pDest->iParm = iParm;
- pDest->affinity = 0;
- pDest->iMem = 0;
- pDest->nMem = 0;
+ pDest->iSDParm = iParm;
+ pDest->zAffSdst = 0;
+ pDest->iSdst = 0;
+ pDest->nSdst = 0;
}
@@ -92402,58 +114479,83 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
ExprList *pGroupBy, /* the GROUP BY clause */
Expr *pHaving, /* the HAVING clause */
ExprList *pOrderBy, /* the ORDER BY clause */
- int isDistinct, /* true if the DISTINCT keyword is present */
+ u32 selFlags, /* Flag parameters, such as SF_Distinct */
Expr *pLimit, /* LIMIT value. NULL means not used */
Expr *pOffset /* OFFSET value. NULL means no offset */
){
Select *pNew;
Select standin;
sqlite3 *db = pParse->db;
- pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
- assert( db->mallocFailed || !pOffset || pLimit ); /* OFFSET implies LIMIT */
+ pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew) );
if( pNew==0 ){
+ assert( db->mallocFailed );
pNew = &standin;
- memset(pNew, 0, sizeof(*pNew));
}
if( pEList==0 ){
- pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ALL,0));
+ pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db,TK_ASTERISK,0));
}
pNew->pEList = pEList;
+ pNew->op = TK_SELECT;
+ pNew->selFlags = selFlags;
+ pNew->iLimit = 0;
+ pNew->iOffset = 0;
+#if SELECTTRACE_ENABLED
+ pNew->zSelName[0] = 0;
+#endif
+ pNew->addrOpenEphm[0] = -1;
+ pNew->addrOpenEphm[1] = -1;
+ pNew->nSelectRow = 0;
+ if( pSrc==0 ) pSrc = sqlite3DbMallocZero(db, sizeof(*pSrc));
pNew->pSrc = pSrc;
pNew->pWhere = pWhere;
pNew->pGroupBy = pGroupBy;
pNew->pHaving = pHaving;
pNew->pOrderBy = pOrderBy;
- pNew->selFlags = isDistinct ? SF_Distinct : 0;
- pNew->op = TK_SELECT;
+ pNew->pPrior = 0;
+ pNew->pNext = 0;
pNew->pLimit = pLimit;
pNew->pOffset = pOffset;
- assert( pOffset==0 || pLimit!=0 );
- pNew->addrOpenEphm[0] = -1;
- pNew->addrOpenEphm[1] = -1;
- pNew->addrOpenEphm[2] = -1;
+ pNew->pWith = 0;
+ assert( pOffset==0 || pLimit!=0 || pParse->nErr>0 || db->mallocFailed!=0 );
if( db->mallocFailed ) {
- clearSelect(db, pNew);
- if( pNew!=&standin ) sqlite3DbFree(db, pNew);
+ clearSelect(db, pNew, pNew!=&standin);
pNew = 0;
}else{
assert( pNew->pSrc!=0 || pParse->nErr>0 );
}
+ assert( pNew!=&standin );
return pNew;
}
+#if SELECTTRACE_ENABLED
+/*
+** Set the name of a Select object
+*/
+SQLITE_PRIVATE void sqlite3SelectSetName(Select *p, const char *zName){
+ if( p && zName ){
+ sqlite3_snprintf(sizeof(p->zSelName), p->zSelName, "%s", zName);
+ }
+}
+#endif
+
+
/*
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
- if( p ){
- clearSelect(db, p);
- sqlite3DbFree(db, p);
- }
+ if( p ) clearSelect(db, p, 1);
+}
+
+/*
+** Return a pointer to the right-most SELECT statement in a compound.
+*/
+static Select *findRightmost(Select *p){
+ while( p->pNext ) p = p->pNext;
+ return p;
}
/*
-** Given 1 to 3 identifiers preceeding the JOIN keyword, determine the
+** Given 1 to 3 identifiers preceding the JOIN keyword, determine the
** type of join. Return an integer constant that expresses that type
** in terms of the following bit values:
**
@@ -92608,8 +114710,8 @@ static void addWhereTerm(
pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2, 0);
if( pEq && isOuterJoin ){
ExprSetProperty(pEq, EP_FromJoin);
- assert( !ExprHasAnyProperty(pEq, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(pEq);
+ assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(pEq, EP_NoReduce);
pEq->iRightJoinTable = (i16)pE2->iTable;
}
*ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq);
@@ -92644,9 +114746,15 @@ static void addWhereTerm(
static void setJoinExpr(Expr *p, int iTable){
while( p ){
ExprSetProperty(p, EP_FromJoin);
- assert( !ExprHasAnyProperty(p, EP_TokenOnly|EP_Reduced) );
- ExprSetIrreducible(p);
+ assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
+ ExprSetVVAProperty(p, EP_NoReduce);
p->iRightJoinTable = (i16)iTable;
+ if( p->op==TK_FUNCTION && p->x.pList ){
+ int i;
+ for(i=0; i<p->x.pList->nExpr; i++){
+ setJoinExpr(p->x.pList->a[i].pExpr, iTable);
+ }
+ }
setJoinExpr(p->pLeft, iTable);
p = p->pRight;
}
@@ -92681,12 +114789,12 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
int isOuter;
if( NEVER(pLeftTab==0 || pRightTab==0) ) continue;
- isOuter = (pRight->jointype & JT_OUTER)!=0;
+ isOuter = (pRight->fg.jointype & JT_OUTER)!=0;
/* When the NATURAL keyword is present, add WHERE clause terms for
** every column that the two tables have in common.
*/
- if( pRight->jointype & JT_NATURAL ){
+ if( pRight->fg.jointype & JT_NATURAL ){
if( pRight->pOn || pRight->pUsing ){
sqlite3ErrorMsg(pParse, "a NATURAL join may not have "
"an ON or USING clause", 0);
@@ -92754,49 +114862,132 @@ static int sqliteProcessJoin(Parse *pParse, Select *p){
return 0;
}
+/* Forward reference */
+static KeyInfo *keyInfoFromExprList(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* Form the KeyInfo object from this ExprList */
+ int iStart, /* Begin with this column of pList */
+ int nExtra /* Add this many extra columns to the end */
+);
+
/*
-** Insert code into "v" that will push the record on the top of the
-** stack into the sorter.
+** Generate code that will push the record in registers regData
+** through regData+nData-1 onto the sorter.
*/
static void pushOntoSorter(
Parse *pParse, /* Parser context */
- ExprList *pOrderBy, /* The ORDER BY clause */
+ SortCtx *pSort, /* Information about the ORDER BY clause */
Select *pSelect, /* The whole SELECT statement */
- int regData /* Register holding data to be sorted */
-){
- Vdbe *v = pParse->pVdbe;
- int nExpr = pOrderBy->nExpr;
- int regBase = sqlite3GetTempRange(pParse, nExpr+2);
- int regRecord = sqlite3GetTempReg(pParse);
- int op;
- sqlite3ExprCacheClear(pParse);
- sqlite3ExprCodeExprList(pParse, pOrderBy, regBase, 0);
- sqlite3VdbeAddOp2(v, OP_Sequence, pOrderBy->iECursor, regBase+nExpr);
- sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+1, 1);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nExpr + 2, regRecord);
- if( pSelect->selFlags & SF_UseSorter ){
+ int regData, /* First register holding data to be sorted */
+ int regOrigData, /* First register holding data before packing */
+ int nData, /* Number of elements in the data array */
+ int nPrefixReg /* No. of reg prior to regData available for use */
+){
+ Vdbe *v = pParse->pVdbe; /* Stmt under construction */
+ int bSeq = ((pSort->sortFlags & SORTFLAG_UseSorter)==0);
+ int nExpr = pSort->pOrderBy->nExpr; /* No. of ORDER BY terms */
+ int nBase = nExpr + bSeq + nData; /* Fields in sorter record */
+ int regBase; /* Regs for sorter record */
+ int regRecord = ++pParse->nMem; /* Assembled sorter record */
+ int nOBSat = pSort->nOBSat; /* ORDER BY terms to skip */
+ int op; /* Opcode to add sorter record to sorter */
+ int iLimit; /* LIMIT counter */
+
+ assert( bSeq==0 || bSeq==1 );
+ assert( nData==1 || regData==regOrigData );
+ if( nPrefixReg ){
+ assert( nPrefixReg==nExpr+bSeq );
+ regBase = regData - nExpr - bSeq;
+ }else{
+ regBase = pParse->nMem + 1;
+ pParse->nMem += nBase;
+ }
+ assert( pSelect->iOffset==0 || pSelect->iLimit!=0 );
+ iLimit = pSelect->iOffset ? pSelect->iOffset+1 : pSelect->iLimit;
+ pSort->labelDone = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprCodeExprList(pParse, pSort->pOrderBy, regBase, regOrigData,
+ SQLITE_ECEL_DUP|SQLITE_ECEL_REF);
+ if( bSeq ){
+ sqlite3VdbeAddOp2(v, OP_Sequence, pSort->iECursor, regBase+nExpr);
+ }
+ if( nPrefixReg==0 ){
+ sqlite3ExprCodeMove(pParse, regData, regBase+nExpr+bSeq, nData);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase+nOBSat, nBase-nOBSat, regRecord);
+ if( nOBSat>0 ){
+ int regPrevKey; /* The first nOBSat columns of the previous row */
+ int addrFirst; /* Address of the OP_IfNot opcode */
+ int addrJmp; /* Address of the OP_Jump opcode */
+ VdbeOp *pOp; /* Opcode that opens the sorter */
+ int nKey; /* Number of sorting key columns, including OP_Sequence */
+ KeyInfo *pKI; /* Original KeyInfo on the sorter table */
+
+ regPrevKey = pParse->nMem+1;
+ pParse->nMem += pSort->nOBSat;
+ nKey = nExpr - pSort->nOBSat + bSeq;
+ if( bSeq ){
+ addrFirst = sqlite3VdbeAddOp1(v, OP_IfNot, regBase+nExpr);
+ }else{
+ addrFirst = sqlite3VdbeAddOp1(v, OP_SequenceTest, pSort->iECursor);
+ }
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_Compare, regPrevKey, regBase, pSort->nOBSat);
+ pOp = sqlite3VdbeGetOp(v, pSort->addrSortIndex);
+ if( pParse->db->mallocFailed ) return;
+ pOp->p2 = nKey + nData;
+ pKI = pOp->p4.pKeyInfo;
+ memset(pKI->aSortOrder, 0, pKI->nField); /* Makes OP_Jump below testable */
+ sqlite3VdbeChangeP4(v, -1, (char*)pKI, P4_KEYINFO);
+ testcase( pKI->nXField>2 );
+ pOp->p4.pKeyInfo = keyInfoFromExprList(pParse, pSort->pOrderBy, nOBSat,
+ pKI->nXField-1);
+ addrJmp = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_Jump, addrJmp+1, 0, addrJmp+1); VdbeCoverage(v);
+ pSort->labelBkOut = sqlite3VdbeMakeLabel(v);
+ pSort->regReturn = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
+ sqlite3VdbeAddOp1(v, OP_ResetSorter, pSort->iECursor);
+ if( iLimit ){
+ sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, pSort->labelDone);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeJumpHere(v, addrFirst);
+ sqlite3ExprCodeMove(pParse, regBase, regPrevKey, pSort->nOBSat);
+ sqlite3VdbeJumpHere(v, addrJmp);
+ }
+ if( pSort->sortFlags & SORTFLAG_UseSorter ){
op = OP_SorterInsert;
}else{
op = OP_IdxInsert;
}
- sqlite3VdbeAddOp2(v, op, pOrderBy->iECursor, regRecord);
- sqlite3ReleaseTempReg(pParse, regRecord);
- sqlite3ReleaseTempRange(pParse, regBase, nExpr+2);
- if( pSelect->iLimit ){
- int addr1, addr2;
- int iLimit;
- if( pSelect->iOffset ){
- iLimit = pSelect->iOffset+1;
- }else{
- iLimit = pSelect->iLimit;
+ sqlite3VdbeAddOp2(v, op, pSort->iECursor, regRecord);
+ if( iLimit ){
+ int addr;
+ int r1 = 0;
+ /* Fill the sorter until it contains LIMIT+OFFSET entries. (The iLimit
+ ** register is initialized with value of LIMIT+OFFSET.) After the sorter
+ ** fills up, delete the least entry in the sorter after each insert.
+ ** Thus we never hold more than the LIMIT+OFFSET rows in memory at once */
+ addr = sqlite3VdbeAddOp3(v, OP_IfNotZero, iLimit, 0, 1); VdbeCoverage(v);
+ sqlite3VdbeAddOp1(v, OP_Last, pSort->iECursor);
+ if( pSort->bOrderedInnerLoop ){
+ r1 = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_Column, pSort->iECursor, nExpr, r1);
+ VdbeComment((v, "seq"));
+ }
+ sqlite3VdbeAddOp1(v, OP_Delete, pSort->iECursor);
+ if( pSort->bOrderedInnerLoop ){
+ /* If the inner loop is driven by an index such that values from
+ ** the same iteration of the inner loop are in sorted order, then
+ ** immediately jump to the next iteration of an inner loop if the
+ ** entry from the current iteration does not fit into the top
+ ** LIMIT+OFFSET entries of the sorter. */
+ int iBrk = sqlite3VdbeCurrentAddr(v) + 2;
+ sqlite3VdbeAddOp3(v, OP_Eq, regBase+nExpr, iBrk, r1);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ VdbeCoverage(v);
}
- addr1 = sqlite3VdbeAddOp1(v, OP_IfZero, iLimit);
- sqlite3VdbeAddOp2(v, OP_AddImm, iLimit, -1);
- addr2 = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp1(v, OP_Last, pOrderBy->iECursor);
- sqlite3VdbeAddOp1(v, OP_Delete, pOrderBy->iECursor);
- sqlite3VdbeJumpHere(v, addr2);
+ sqlite3VdbeJumpHere(v, addr);
}
}
@@ -92805,16 +114996,12 @@ static void pushOntoSorter(
*/
static void codeOffset(
Vdbe *v, /* Generate code into this VM */
- Select *p, /* The SELECT statement being coded */
+ int iOffset, /* Register holding the offset counter */
int iContinue /* Jump here to skip the current record */
){
- if( p->iOffset && iContinue!=0 ){
- int addr;
- sqlite3VdbeAddOp2(v, OP_AddImm, p->iOffset, -1);
- addr = sqlite3VdbeAddOp1(v, OP_IfNeg, p->iOffset);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iContinue);
- VdbeComment((v, "skip OFFSET records"));
- sqlite3VdbeJumpHere(v, addr);
+ if( iOffset>0 ){
+ sqlite3VdbeAddOp3(v, OP_IfPos, iOffset, iContinue, 1); VdbeCoverage(v);
+ VdbeComment((v, "OFFSET"));
}
}
@@ -92839,53 +115026,28 @@ static void codeDistinct(
v = pParse->pVdbe;
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N);
+ sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); VdbeCoverage(v);
sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1);
sqlite3ReleaseTempReg(pParse, r1);
}
-#ifndef SQLITE_OMIT_SUBQUERY
-/*
-** Generate an error message when a SELECT is used within a subexpression
-** (example: "a IN (SELECT * FROM table)") but it has more than 1 result
-** column. We do this in a subroutine because the error used to occur
-** in multiple places. (The error only occurs in one place now, but we
-** retain the subroutine to minimize code disruption.)
-*/
-static int checkForMultiColumnSelectError(
- Parse *pParse, /* Parse context. */
- SelectDest *pDest, /* Destination of SELECT results */
- int nExpr /* Number of result columns returned by SELECT */
-){
- int eDest = pDest->eDest;
- if( nExpr>1 && (eDest==SRT_Mem || eDest==SRT_Set) ){
- sqlite3ErrorMsg(pParse, "only a single result allowed for "
- "a SELECT that is part of an expression");
- return 1;
- }else{
- return 0;
- }
-}
-#endif
-
/*
** This routine generates the code for the inside of the inner loop
** of a SELECT.
**
-** If srcTab and nColumn are both zero, then the pEList expressions
-** are evaluated in order to get the data for this row. If nColumn>0
-** then data is pulled from srcTab and pEList is used only to get the
-** datatypes for each column.
+** If srcTab is negative, then the pEList expressions
+** are evaluated in order to get the data for this row. If srcTab is
+** zero or more, then data is pulled from srcTab and pEList is used only
+** to get number columns and the datatype for each column.
*/
static void selectInnerLoop(
Parse *pParse, /* The parser context */
Select *p, /* The complete select statement being coded */
ExprList *pEList, /* List of values being extracted */
int srcTab, /* Pull data from this table */
- int nColumn, /* Number of columns in the source table */
- ExprList *pOrderBy, /* If not NULL, sort results using this key */
- int distinct, /* If >=0, make sure results are distinct */
+ SortCtx *pSort, /* If not NULL, info on how to process ORDER BY */
+ DistinctCtx *pDistinct, /* If not NULL, info on how to process DISTINCT */
SelectDest *pDest, /* How to dispose of the results */
int iContinue, /* Jump here to continue with next row */
int iBreak /* Jump here to break out of the inner loop */
@@ -92895,55 +115057,118 @@ static void selectInnerLoop(
int hasDistinct; /* True if the DISTINCT keyword is present */
int regResult; /* Start of memory holding result set */
int eDest = pDest->eDest; /* How to dispose of results */
- int iParm = pDest->iParm; /* First argument to disposal method */
+ int iParm = pDest->iSDParm; /* First argument to disposal method */
int nResultCol; /* Number of result columns */
+ int nPrefixReg = 0; /* Number of extra registers before regResult */
assert( v );
- if( NEVER(v==0) ) return;
assert( pEList!=0 );
- hasDistinct = distinct>=0;
- if( pOrderBy==0 && !hasDistinct ){
- codeOffset(v, p, iContinue);
+ hasDistinct = pDistinct ? pDistinct->eTnctType : WHERE_DISTINCT_NOOP;
+ if( pSort && pSort->pOrderBy==0 ) pSort = 0;
+ if( pSort==0 && !hasDistinct ){
+ assert( iContinue!=0 );
+ codeOffset(v, p->iOffset, iContinue);
}
/* Pull the requested columns.
*/
- if( nColumn>0 ){
- nResultCol = nColumn;
- }else{
- nResultCol = pEList->nExpr;
- }
- if( pDest->iMem==0 ){
- pDest->iMem = pParse->nMem+1;
- pDest->nMem = nResultCol;
+ nResultCol = pEList->nExpr;
+
+ if( pDest->iSdst==0 ){
+ if( pSort ){
+ nPrefixReg = pSort->pOrderBy->nExpr;
+ if( !(pSort->sortFlags & SORTFLAG_UseSorter) ) nPrefixReg++;
+ pParse->nMem += nPrefixReg;
+ }
+ pDest->iSdst = pParse->nMem+1;
+ pParse->nMem += nResultCol;
+ }else if( pDest->iSdst+nResultCol > pParse->nMem ){
+ /* This is an error condition that can result, for example, when a SELECT
+ ** on the right-hand side of an INSERT contains more result columns than
+ ** there are columns in the table on the left. The error will be caught
+ ** and reported later. But we need to make sure enough memory is allocated
+ ** to avoid other spurious errors in the meantime. */
pParse->nMem += nResultCol;
- }else{
- assert( pDest->nMem==nResultCol );
}
- regResult = pDest->iMem;
- if( nColumn>0 ){
- for(i=0; i<nColumn; i++){
+ pDest->nSdst = nResultCol;
+ regResult = pDest->iSdst;
+ if( srcTab>=0 ){
+ for(i=0; i<nResultCol; i++){
sqlite3VdbeAddOp3(v, OP_Column, srcTab, i, regResult+i);
+ VdbeComment((v, "%s", pEList->a[i].zName));
}
}else if( eDest!=SRT_Exists ){
/* If the destination is an EXISTS(...) expression, the actual
** values returned by the SELECT are not required.
*/
- sqlite3ExprCacheClear(pParse);
- sqlite3ExprCodeExprList(pParse, pEList, regResult, eDest==SRT_Output);
+ u8 ecelFlags;
+ if( eDest==SRT_Mem || eDest==SRT_Output || eDest==SRT_Coroutine ){
+ ecelFlags = SQLITE_ECEL_DUP;
+ }else{
+ ecelFlags = 0;
+ }
+ sqlite3ExprCodeExprList(pParse, pEList, regResult, 0, ecelFlags);
}
- nColumn = nResultCol;
/* If the DISTINCT keyword was present on the SELECT statement
** and this row has been seen before, then do not make this row
** part of the result.
*/
if( hasDistinct ){
- assert( pEList!=0 );
- assert( pEList->nExpr==nColumn );
- codeDistinct(pParse, distinct, iContinue, nColumn, regResult);
- if( pOrderBy==0 ){
- codeOffset(v, p, iContinue);
+ switch( pDistinct->eTnctType ){
+ case WHERE_DISTINCT_ORDERED: {
+ VdbeOp *pOp; /* No longer required OpenEphemeral instr. */
+ int iJump; /* Jump destination */
+ int regPrev; /* Previous row content */
+
+ /* Allocate space for the previous row */
+ regPrev = pParse->nMem+1;
+ pParse->nMem += nResultCol;
+
+ /* Change the OP_OpenEphemeral coded earlier to an OP_Null
+ ** sets the MEM_Cleared bit on the first register of the
+ ** previous value. This will cause the OP_Ne below to always
+ ** fail on the first iteration of the loop even if the first
+ ** row is all NULLs.
+ */
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
+ pOp = sqlite3VdbeGetOp(v, pDistinct->addrTnct);
+ pOp->opcode = OP_Null;
+ pOp->p1 = 1;
+ pOp->p2 = regPrev;
+
+ iJump = sqlite3VdbeCurrentAddr(v) + nResultCol;
+ for(i=0; i<nResultCol; i++){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[i].pExpr);
+ if( i<nResultCol-1 ){
+ sqlite3VdbeAddOp3(v, OP_Ne, regResult+i, iJump, regPrev+i);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_Eq, regResult+i, iContinue, regPrev+i);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ);
+ sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
+ }
+ assert( sqlite3VdbeCurrentAddr(v)==iJump || pParse->db->mallocFailed );
+ sqlite3VdbeAddOp3(v, OP_Copy, regResult, regPrev, nResultCol-1);
+ break;
+ }
+
+ case WHERE_DISTINCT_UNIQUE: {
+ sqlite3VdbeChangeToNoop(v, pDistinct->addrTnct);
+ break;
+ }
+
+ default: {
+ assert( pDistinct->eTnctType==WHERE_DISTINCT_UNORDERED );
+ codeDistinct(pParse, pDistinct->tabTnct, iContinue, nResultCol,
+ regResult);
+ break;
+ }
+ }
+ if( pSort==0 ){
+ codeOffset(v, p->iOffset, iContinue);
}
}
@@ -92955,7 +115180,7 @@ static void selectInnerLoop(
case SRT_Union: {
int r1;
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
break;
@@ -92966,21 +115191,39 @@ static void selectInnerLoop(
** the temporary table iParm.
*/
case SRT_Except: {
- sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nColumn);
+ sqlite3VdbeAddOp3(v, OP_IdxDelete, iParm, regResult, nResultCol);
break;
}
-#endif
+#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/* Store the result as data using a unique key.
*/
+ case SRT_Fifo:
+ case SRT_DistFifo:
case SRT_Table:
case SRT_EphemTab: {
- int r1 = sqlite3GetTempReg(pParse);
+ int r1 = sqlite3GetTempRange(pParse, nPrefixReg+1);
testcase( eDest==SRT_Table );
testcase( eDest==SRT_EphemTab );
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
- if( pOrderBy ){
- pushOntoSorter(pParse, pOrderBy, p, r1);
+ testcase( eDest==SRT_Fifo );
+ testcase( eDest==SRT_DistFifo );
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r1+nPrefixReg);
+#ifndef SQLITE_OMIT_CTE
+ if( eDest==SRT_DistFifo ){
+ /* If the destination is DistFifo, then cursor (iParm+1) is open
+ ** on an ephemeral index. If the current row is already present
+ ** in the index, do not write it to the output. If not, add the
+ ** current row to the index and proceed with writing it to the
+ ** output table as well. */
+ int addr = sqlite3VdbeCurrentAddr(v) + 4;
+ sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, addr, r1, 0);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r1);
+ assert( pSort==0 );
+ }
+#endif
+ if( pSort ){
+ pushOntoSorter(pParse, pSort, p, r1+nPrefixReg,regResult,1,nPrefixReg);
}else{
int r2 = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, r2);
@@ -92988,7 +115231,7 @@ static void selectInnerLoop(
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3ReleaseTempReg(pParse, r2);
}
- sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3ReleaseTempRange(pParse, r1, nPrefixReg+1);
break;
}
@@ -92998,18 +115241,19 @@ static void selectInnerLoop(
** item into the set table with bogus data.
*/
case SRT_Set: {
- assert( nColumn==1 );
- p->affinity = sqlite3CompareAffinity(pEList->a[0].pExpr, pDest->affinity);
- if( pOrderBy ){
+ if( pSort ){
/* At first glance you would think we could optimize out the
** ORDER BY in this case since the order of entries in the set
** does not matter. But there might be a LIMIT clause, in which
** case the order does matter */
- pushOntoSorter(pParse, pOrderBy, p, regResult);
+ pushOntoSorter(
+ pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
}else{
int r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, 1, r1, &p->affinity, 1);
- sqlite3ExprCacheAffinityChange(pParse, regResult, 1);
+ assert( sqlite3Strlen30(pDest->zAffSdst)==nResultCol );
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regResult, nResultCol,
+ r1, pDest->zAffSdst, nResultCol);
+ sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
}
@@ -93025,43 +115269,87 @@ static void selectInnerLoop(
}
/* If this is a scalar select that is part of an expression, then
- ** store the results in the appropriate memory cell and break out
- ** of the scan loop.
+ ** store the results in the appropriate memory cell or array of
+ ** memory cells and break out of the scan loop.
*/
case SRT_Mem: {
- assert( nColumn==1 );
- if( pOrderBy ){
- pushOntoSorter(pParse, pOrderBy, p, regResult);
+ assert( nResultCol==pDest->nSdst );
+ if( pSort ){
+ pushOntoSorter(
+ pParse, pSort, p, regResult, regResult, nResultCol, nPrefixReg);
}else{
- sqlite3ExprCodeMove(pParse, regResult, iParm, 1);
+ assert( regResult==iParm );
/* The LIMIT clause will jump out of the loop for us */
}
break;
}
#endif /* #ifndef SQLITE_OMIT_SUBQUERY */
- /* Send the data to the callback function or to a subroutine. In the
- ** case of a subroutine, the subroutine itself is responsible for
- ** popping the data from the stack.
- */
- case SRT_Coroutine:
- case SRT_Output: {
+ case SRT_Coroutine: /* Send data to a co-routine */
+ case SRT_Output: { /* Return the results */
testcase( eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
- if( pOrderBy ){
- int r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nColumn, r1);
- pushOntoSorter(pParse, pOrderBy, p, r1);
- sqlite3ReleaseTempReg(pParse, r1);
+ if( pSort ){
+ pushOntoSorter(pParse, pSort, p, regResult, regResult, nResultCol,
+ nPrefixReg);
}else if( eDest==SRT_Coroutine ){
- sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}else{
- sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nColumn);
- sqlite3ExprCacheAffinityChange(pParse, regResult, nColumn);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, regResult, nResultCol);
+ sqlite3ExprCacheAffinityChange(pParse, regResult, nResultCol);
}
break;
}
+#ifndef SQLITE_OMIT_CTE
+ /* Write the results into a priority queue that is order according to
+ ** pDest->pOrderBy (in pSO). pDest->iSDParm (in iParm) is the cursor for an
+ ** index with pSO->nExpr+2 columns. Build a key using pSO for the first
+ ** pSO->nExpr columns, then make sure all keys are unique by adding a
+ ** final OP_Sequence column. The last column is the record as a blob.
+ */
+ case SRT_DistQueue:
+ case SRT_Queue: {
+ int nKey;
+ int r1, r2, r3;
+ int addrTest = 0;
+ ExprList *pSO;
+ pSO = pDest->pOrderBy;
+ assert( pSO );
+ nKey = pSO->nExpr;
+ r1 = sqlite3GetTempReg(pParse);
+ r2 = sqlite3GetTempRange(pParse, nKey+2);
+ r3 = r2+nKey+1;
+ if( eDest==SRT_DistQueue ){
+ /* If the destination is DistQueue, then cursor (iParm+1) is open
+ ** on a second ephemeral index that holds all values every previously
+ ** added to the queue. */
+ addrTest = sqlite3VdbeAddOp4Int(v, OP_Found, iParm+1, 0,
+ regResult, nResultCol);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regResult, nResultCol, r3);
+ if( eDest==SRT_DistQueue ){
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm+1, r3);
+ sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ }
+ for(i=0; i<nKey; i++){
+ sqlite3VdbeAddOp2(v, OP_SCopy,
+ regResult + pSO->a[i].u.x.iOrderByCol - 1,
+ r2+i);
+ }
+ sqlite3VdbeAddOp2(v, OP_Sequence, iParm, r2+nKey);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, r2, nKey+2, r1);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, r1);
+ if( addrTest ) sqlite3VdbeJumpHere(v, addrTest);
+ sqlite3ReleaseTempReg(pParse, r1);
+ sqlite3ReleaseTempRange(pParse, r2, nKey+2);
+ break;
+ }
+#endif /* SQLITE_OMIT_CTE */
+
+
+
#if !defined(SQLITE_OMIT_TRIGGER)
/* Discard the results. This is used for SELECT statements inside
** the body of a TRIGGER. The purpose of such selects is to call
@@ -93079,12 +115367,65 @@ static void selectInnerLoop(
** there is a sorter, in which case the sorter has already limited
** the output for us.
*/
- if( pOrderBy==0 && p->iLimit ){
- sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1);
+ if( pSort==0 && p->iLimit ){
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v);
+ }
+}
+
+/*
+** Allocate a KeyInfo object sufficient for an index of N key columns and
+** X extra columns.
+*/
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
+ int nExtra = (N+X)*(sizeof(CollSeq*)+1);
+ KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
+ if( p ){
+ p->aSortOrder = (u8*)&p->aColl[N+X];
+ p->nField = (u16)N;
+ p->nXField = (u16)X;
+ p->enc = ENC(db);
+ p->db = db;
+ p->nRef = 1;
+ memset(&p[1], 0, nExtra);
+ }else{
+ sqlite3OomFault(db);
}
+ return p;
}
/*
+** Deallocate a KeyInfo object
+*/
+SQLITE_PRIVATE void sqlite3KeyInfoUnref(KeyInfo *p){
+ if( p ){
+ assert( p->nRef>0 );
+ p->nRef--;
+ if( p->nRef==0 ) sqlite3DbFree(p->db, p);
+ }
+}
+
+/*
+** Make a new pointer to a KeyInfo object
+*/
+SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoRef(KeyInfo *p){
+ if( p ){
+ assert( p->nRef>0 );
+ p->nRef++;
+ }
+ return p;
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** Return TRUE if a KeyInfo object can be change. The KeyInfo object
+** can only be changed if this is just a single reference to the object.
+**
+** This routine is used only inside of assert() statements.
+*/
+SQLITE_PRIVATE int sqlite3KeyInfoIsWriteable(KeyInfo *p){ return p->nRef==1; }
+#endif /* SQLITE_DEBUG */
+
+/*
** Given an expression list, generate a KeyInfo structure that records
** the collating sequence for each expression in that expression list.
**
@@ -93094,39 +115435,37 @@ static void selectInnerLoop(
** then the KeyInfo structure is appropriate for initializing a virtual
** index to implement a DISTINCT test.
**
-** Space to hold the KeyInfo structure is obtain from malloc. The calling
+** Space to hold the KeyInfo structure is obtained from malloc. The calling
** function is responsible for seeing that this structure is eventually
-** freed. Add the KeyInfo structure to the P4 field of an opcode using
-** P4_KEYINFO_HANDOFF is the usual way of dealing with this.
+** freed.
*/
-static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
- sqlite3 *db = pParse->db;
+static KeyInfo *keyInfoFromExprList(
+ Parse *pParse, /* Parsing context */
+ ExprList *pList, /* Form the KeyInfo object from this ExprList */
+ int iStart, /* Begin with this column of pList */
+ int nExtra /* Add this many extra columns to the end */
+){
int nExpr;
KeyInfo *pInfo;
struct ExprList_item *pItem;
+ sqlite3 *db = pParse->db;
int i;
nExpr = pList->nExpr;
- pInfo = sqlite3DbMallocZero(db, sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) );
+ pInfo = sqlite3KeyInfoAlloc(db, nExpr-iStart, nExtra+1);
if( pInfo ){
- pInfo->aSortOrder = (u8*)&pInfo->aColl[nExpr];
- pInfo->nField = (u16)nExpr;
- pInfo->enc = ENC(db);
- pInfo->db = db;
- for(i=0, pItem=pList->a; i<nExpr; i++, pItem++){
+ assert( sqlite3KeyInfoIsWriteable(pInfo) );
+ for(i=iStart, pItem=pList->a+iStart; i<nExpr; i++, pItem++){
CollSeq *pColl;
pColl = sqlite3ExprCollSeq(pParse, pItem->pExpr);
- if( !pColl ){
- pColl = db->pDfltColl;
- }
- pInfo->aColl[i] = pColl;
- pInfo->aSortOrder[i] = pItem->sortOrder;
+ if( !pColl ) pColl = db->pDfltColl;
+ pInfo->aColl[i-iStart] = pColl;
+ pInfo->aSortOrder[i-iStart] = pItem->sortOrder;
}
}
return pInfo;
}
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** Name of the connection operator, used for error messages.
*/
@@ -93140,7 +115479,6 @@ static const char *selectOpName(int id){
}
return z;
}
-#endif /* SQLITE_OMIT_COMPOUND_SELECT */
#ifndef SQLITE_OMIT_EXPLAIN
/*
@@ -93222,51 +115560,72 @@ static void explainComposite(
static void generateSortTail(
Parse *pParse, /* Parsing context */
Select *p, /* The SELECT statement */
- Vdbe *v, /* Generate code into this VDBE */
+ SortCtx *pSort, /* Information on the ORDER BY clause */
int nColumn, /* Number of columns of data */
SelectDest *pDest /* Write the sorted results here */
){
- int addrBreak = sqlite3VdbeMakeLabel(v); /* Jump here to exit loop */
+ Vdbe *v = pParse->pVdbe; /* The prepared statement */
+ int addrBreak = pSort->labelDone; /* Jump here to exit loop */
int addrContinue = sqlite3VdbeMakeLabel(v); /* Jump here for next cycle */
int addr;
+ int addrOnce = 0;
int iTab;
- int pseudoTab = 0;
- ExprList *pOrderBy = p->pOrderBy;
-
+ ExprList *pOrderBy = pSort->pOrderBy;
int eDest = pDest->eDest;
- int iParm = pDest->iParm;
-
+ int iParm = pDest->iSDParm;
int regRow;
int regRowid;
+ int nKey;
+ int iSortTab; /* Sorter cursor to read from */
+ int nSortData; /* Trailing values to read from sorter */
+ int i;
+ int bSeq; /* True if sorter record includes seq. no. */
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
+ struct ExprList_item *aOutEx = p->pEList->a;
+#endif
- iTab = pOrderBy->iECursor;
- regRow = sqlite3GetTempReg(pParse);
- if( eDest==SRT_Output || eDest==SRT_Coroutine ){
- pseudoTab = pParse->nTab++;
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn);
+ assert( addrBreak<0 );
+ if( pSort->labelBkOut ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pSort->regReturn, pSort->labelBkOut);
+ sqlite3VdbeGoto(v, addrBreak);
+ sqlite3VdbeResolveLabel(v, pSort->labelBkOut);
+ }
+ iTab = pSort->iECursor;
+ if( eDest==SRT_Output || eDest==SRT_Coroutine || eDest==SRT_Mem ){
regRowid = 0;
+ regRow = pDest->iSdst;
+ nSortData = nColumn;
}else{
regRowid = sqlite3GetTempReg(pParse);
+ regRow = sqlite3GetTempRange(pParse, nColumn);
+ nSortData = nColumn;
}
- if( p->selFlags & SF_UseSorter ){
+ nKey = pOrderBy->nExpr - pSort->nOBSat;
+ if( pSort->sortFlags & SORTFLAG_UseSorter ){
int regSortOut = ++pParse->nMem;
- int ptab2 = pParse->nTab++;
- sqlite3VdbeAddOp3(v, OP_OpenPseudo, ptab2, regSortOut, pOrderBy->nExpr+2);
+ iSortTab = pParse->nTab++;
+ if( pSort->labelBkOut ){
+ addrOnce = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ }
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iSortTab, regSortOut, nKey+1+nSortData);
+ if( addrOnce ) sqlite3VdbeJumpHere(v, addrOnce);
addr = 1 + sqlite3VdbeAddOp2(v, OP_SorterSort, iTab, addrBreak);
- codeOffset(v, p, addrContinue);
- sqlite3VdbeAddOp2(v, OP_SorterData, iTab, regSortOut);
- sqlite3VdbeAddOp3(v, OP_Column, ptab2, pOrderBy->nExpr+1, regRow);
- sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
+ VdbeCoverage(v);
+ codeOffset(v, p->iOffset, addrContinue);
+ sqlite3VdbeAddOp3(v, OP_SorterData, iTab, regSortOut, iSortTab);
+ bSeq = 0;
}else{
- addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak);
- codeOffset(v, p, addrContinue);
- sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr+1, regRow);
+ addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); VdbeCoverage(v);
+ codeOffset(v, p->iOffset, addrContinue);
+ iSortTab = iTab;
+ bSeq = 1;
+ }
+ for(i=0; i<nSortData; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, iSortTab, nKey+bSeq+i, regRow+i);
+ VdbeComment((v, "%s", aOutEx[i].zName ? aOutEx[i].zName : aOutEx[i].zSpan));
}
switch( eDest ){
- case SRT_Table:
case SRT_EphemTab: {
- testcase( eDest==SRT_Table );
- testcase( eDest==SRT_EphemTab );
sqlite3VdbeAddOp2(v, OP_NewRowid, iParm, regRowid);
sqlite3VdbeAddOp3(v, OP_Insert, iParm, regRow, regRowid);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
@@ -93274,61 +115633,58 @@ static void generateSortTail(
}
#ifndef SQLITE_OMIT_SUBQUERY
case SRT_Set: {
- assert( nColumn==1 );
- sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, 1, regRowid, &p->affinity, 1);
- sqlite3ExprCacheAffinityChange(pParse, regRow, 1);
+ assert( nColumn==sqlite3Strlen30(pDest->zAffSdst) );
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, regRow, nColumn, regRowid,
+ pDest->zAffSdst, nColumn);
+ sqlite3ExprCacheAffinityChange(pParse, regRow, nColumn);
sqlite3VdbeAddOp2(v, OP_IdxInsert, iParm, regRowid);
break;
}
case SRT_Mem: {
- assert( nColumn==1 );
- sqlite3ExprCodeMove(pParse, regRow, iParm, 1);
/* The LIMIT clause will terminate the loop for us */
break;
}
#endif
default: {
- int i;
assert( eDest==SRT_Output || eDest==SRT_Coroutine );
testcase( eDest==SRT_Output );
testcase( eDest==SRT_Coroutine );
- for(i=0; i<nColumn; i++){
- assert( regRow!=pDest->iMem+i );
- sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i);
- if( i==0 ){
- sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
- }
- }
if( eDest==SRT_Output ){
- sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn);
- sqlite3ExprCacheAffinityChange(pParse, pDest->iMem, nColumn);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iSdst, nColumn);
+ sqlite3ExprCacheAffinityChange(pParse, pDest->iSdst, nColumn);
}else{
- sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
}
break;
}
}
- sqlite3ReleaseTempReg(pParse, regRow);
- sqlite3ReleaseTempReg(pParse, regRowid);
-
+ if( regRowid ){
+ if( eDest==SRT_Set ){
+ sqlite3ReleaseTempRange(pParse, regRow, nColumn);
+ }else{
+ sqlite3ReleaseTempReg(pParse, regRow);
+ }
+ sqlite3ReleaseTempReg(pParse, regRowid);
+ }
/* The bottom of the loop
*/
sqlite3VdbeResolveLabel(v, addrContinue);
- if( p->selFlags & SF_UseSorter ){
- sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr);
+ if( pSort->sortFlags & SORTFLAG_UseSorter ){
+ sqlite3VdbeAddOp2(v, OP_SorterNext, iTab, addr); VdbeCoverage(v);
}else{
- sqlite3VdbeAddOp2(v, OP_Next, iTab, addr);
+ sqlite3VdbeAddOp2(v, OP_Next, iTab, addr); VdbeCoverage(v);
}
+ if( pSort->regReturn ) sqlite3VdbeAddOp1(v, OP_Return, pSort->regReturn);
sqlite3VdbeResolveLabel(v, addrBreak);
- if( eDest==SRT_Output || eDest==SRT_Coroutine ){
- sqlite3VdbeAddOp2(v, OP_Close, pseudoTab, 0);
- }
}
/*
** Return a pointer to a string containing the 'declaration type' of the
** expression pExpr. The string may be treated as static by the caller.
**
+** Also try to estimate the size of the returned value and return that
+** result in *pEstWidth.
+**
** The declaration type is the exact datatype definition extracted from the
** original CREATE TABLE statement if the expression is a column. The
** declaration type for a ROWID field is INTEGER. Exactly when an expression
@@ -93342,21 +115698,36 @@ static void generateSortTail(
** SELECT abc FROM (SELECT col AS abc FROM tbl);
**
** The declaration type for any expression other than a column is NULL.
+**
+** This routine has either 3 or 6 parameters depending on whether or not
+** the SQLITE_ENABLE_COLUMN_METADATA compile-time option is used.
*/
-static const char *columnType(
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,C,D,E,F)
+#else /* if !defined(SQLITE_ENABLE_COLUMN_METADATA) */
+# define columnType(A,B,C,D,E,F) columnTypeImpl(A,B,F)
+#endif
+static const char *columnTypeImpl(
NameContext *pNC,
Expr *pExpr,
- const char **pzOriginDb,
- const char **pzOriginTab,
- const char **pzOriginCol
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ const char **pzOrigDb,
+ const char **pzOrigTab,
+ const char **pzOrigCol,
+#endif
+ u8 *pEstWidth
){
char const *zType = 0;
- char const *zOriginDb = 0;
- char const *zOriginTab = 0;
- char const *zOriginCol = 0;
int j;
- if( NEVER(pExpr==0) || pNC->pSrcList==0 ) return 0;
+ u8 estWidth = 1;
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ char const *zOrigDb = 0;
+ char const *zOrigTab = 0;
+ char const *zOrigCol = 0;
+#endif
+ assert( pExpr!=0 );
+ assert( pNC->pSrcList!=0 );
switch( pExpr->op ){
case TK_AGG_COLUMN:
case TK_COLUMN: {
@@ -93411,31 +115782,44 @@ static const char *columnType(
/* If iCol is less than zero, then the expression requests the
** rowid of the sub-select or view. This expression is legal (see
** test case misc2.2.2) - it always evaluates to NULL.
+ **
+ ** The ALWAYS() is because iCol>=pS->pEList->nExpr will have been
+ ** caught already by name resolution.
*/
NameContext sNC;
Expr *p = pS->pEList->a[iCol].pExpr;
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol);
+ zType = columnType(&sNC, p,&zOrigDb,&zOrigTab,&zOrigCol, &estWidth);
}
- }else if( ALWAYS(pTab->pSchema) ){
+ }else if( pTab->pSchema ){
/* A real table */
assert( !pS );
if( iCol<0 ) iCol = pTab->iPKey;
assert( iCol==-1 || (iCol>=0 && iCol<pTab->nCol) );
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
if( iCol<0 ){
zType = "INTEGER";
- zOriginCol = "rowid";
+ zOrigCol = "rowid";
}else{
- zType = pTab->aCol[iCol].zType;
- zOriginCol = pTab->aCol[iCol].zName;
+ zOrigCol = pTab->aCol[iCol].zName;
+ zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
+ estWidth = pTab->aCol[iCol].szEst;
}
- zOriginTab = pTab->zName;
+ zOrigTab = pTab->zName;
if( pNC->pParse ){
int iDb = sqlite3SchemaToIndex(pNC->pParse->db, pTab->pSchema);
- zOriginDb = pNC->pParse->db->aDb[iDb].zName;
+ zOrigDb = pNC->pParse->db->aDb[iDb].zDbSName;
+ }
+#else
+ if( iCol<0 ){
+ zType = "INTEGER";
+ }else{
+ zType = sqlite3ColumnType(&pTab->aCol[iCol],0);
+ estWidth = pTab->aCol[iCol].szEst;
}
+#endif
}
break;
}
@@ -93452,18 +115836,21 @@ static const char *columnType(
sNC.pSrcList = pS->pSrc;
sNC.pNext = pNC;
sNC.pParse = pNC->pParse;
- zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, &estWidth);
break;
}
#endif
}
-
- if( pzOriginDb ){
- assert( pzOriginTab && pzOriginCol );
- *pzOriginDb = zOriginDb;
- *pzOriginTab = zOriginTab;
- *pzOriginCol = zOriginCol;
+
+#ifdef SQLITE_ENABLE_COLUMN_METADATA
+ if( pzOrigDb ){
+ assert( pzOrigTab && pzOrigCol );
+ *pzOrigDb = zOrigDb;
+ *pzOrigTab = zOrigTab;
+ *pzOrigCol = zOrigCol;
}
+#endif
+ if( pEstWidth ) *pEstWidth = estWidth;
return zType;
}
@@ -93489,7 +115876,7 @@ static void generateColumnTypes(
const char *zOrigDb = 0;
const char *zOrigTab = 0;
const char *zOrigCol = 0;
- zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol);
+ zType = columnType(&sNC, p, &zOrigDb, &zOrigTab, &zOrigCol, 0);
/* The vdbe must make its own copy of the column-type and other
** column specific strings, in case the schema is reset before this
@@ -93499,11 +115886,11 @@ static void generateColumnTypes(
sqlite3VdbeSetColName(v, i, COLNAME_TABLE, zOrigTab, SQLITE_TRANSIENT);
sqlite3VdbeSetColName(v, i, COLNAME_COLUMN, zOrigCol, SQLITE_TRANSIENT);
#else
- zType = columnType(&sNC, p, 0, 0, 0);
+ zType = columnType(&sNC, p, 0, 0, 0, 0);
#endif
sqlite3VdbeSetColName(v, i, COLNAME_DECLTYPE, zType, SQLITE_TRANSIENT);
}
-#endif /* SQLITE_OMIT_DECLTYPE */
+#endif /* !defined(SQLITE_OMIT_DECLTYPE) */
}
/*
@@ -93528,7 +115915,9 @@ static void generateColumnNames(
}
#endif
- if( pParse->colNamesSet || NEVER(v==0) || db->mallocFailed ) return;
+ if( pParse->colNamesSet || db->mallocFailed ) return;
+ assert( v!=0 );
+ assert( pTabList!=0 );
pParse->colNamesSet = 1;
fullNames = (db->flags & SQLITE_FullColNames)!=0;
shortNames = (db->flags & SQLITE_ShortColNames)!=0;
@@ -93540,7 +115929,7 @@ static void generateColumnNames(
if( pEList->a[i].zName ){
char *zName = pEList->a[i].zName;
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zName, SQLITE_TRANSIENT);
- }else if( (p->op==TK_COLUMN || p->op==TK_AGG_COLUMN) && pTabList ){
+ }else if( p->op==TK_COLUMN || p->op==TK_AGG_COLUMN ){
Table *pTab;
char *zCol;
int iCol = p->iColumn;
@@ -93567,15 +115956,16 @@ static void generateColumnNames(
sqlite3VdbeSetColName(v, i, COLNAME_NAME, zCol, SQLITE_TRANSIENT);
}
}else{
- sqlite3VdbeSetColName(v, i, COLNAME_NAME,
- sqlite3DbStrDup(db, pEList->a[i].zSpan), SQLITE_DYNAMIC);
+ const char *z = pEList->a[i].zSpan;
+ z = z==0 ? sqlite3MPrintf(db, "column%d", i+1) : sqlite3DbStrDup(db, z);
+ sqlite3VdbeSetColName(v, i, COLNAME_NAME, z, SQLITE_DYNAMIC);
}
}
generateColumnTypes(pParse, pTabList, pEList);
}
/*
-** Given a an expression list (which is really the list of expressions
+** Given an expression list (which is really the list of expressions
** that form the result set of a SELECT statement) compute appropriate
** column names for a table that would hold the expression list.
**
@@ -93587,74 +115977,84 @@ static void generateColumnNames(
** Return SQLITE_OK on success. If a memory allocation error occurs,
** store NULL in *paCol and 0 in *pnCol and return SQLITE_NOMEM.
*/
-static int selectColumnsFromExprList(
+SQLITE_PRIVATE int sqlite3ColumnsFromExprList(
Parse *pParse, /* Parsing context */
ExprList *pEList, /* Expr list from which to derive column names */
- int *pnCol, /* Write the number of columns here */
+ i16 *pnCol, /* Write the number of columns here */
Column **paCol /* Write the new column list here */
){
sqlite3 *db = pParse->db; /* Database connection */
int i, j; /* Loop counters */
- int cnt; /* Index added to make the name unique */
+ u32 cnt; /* Index added to make the name unique */
Column *aCol, *pCol; /* For looping over result columns */
int nCol; /* Number of columns in the result set */
Expr *p; /* Expression for a single result column */
char *zName; /* Column name */
int nName; /* Size of name in zName[] */
+ Hash ht; /* Hash table of column names */
+
+ sqlite3HashInit(&ht);
+ if( pEList ){
+ nCol = pEList->nExpr;
+ aCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
+ testcase( aCol==0 );
+ }else{
+ nCol = 0;
+ aCol = 0;
+ }
+ assert( nCol==(i16)nCol );
+ *pnCol = nCol;
+ *paCol = aCol;
- *pnCol = nCol = pEList->nExpr;
- aCol = *paCol = sqlite3DbMallocZero(db, sizeof(aCol[0])*nCol);
- if( aCol==0 ) return SQLITE_NOMEM;
- for(i=0, pCol=aCol; i<nCol; i++, pCol++){
+ for(i=0, pCol=aCol; i<nCol && !db->mallocFailed; i++, pCol++){
/* Get an appropriate name for the column
*/
- p = pEList->a[i].pExpr;
- assert( p->pRight==0 || ExprHasProperty(p->pRight, EP_IntValue)
- || p->pRight->u.zToken==0 || p->pRight->u.zToken[0]!=0 );
+ p = sqlite3ExprSkipCollate(pEList->a[i].pExpr);
if( (zName = pEList->a[i].zName)!=0 ){
/* If the column contains an "AS <name>" phrase, use <name> as the name */
- zName = sqlite3DbStrDup(db, zName);
}else{
Expr *pColExpr = p; /* The expression that is the result column name */
Table *pTab; /* Table associated with this expression */
- while( pColExpr->op==TK_DOT ) pColExpr = pColExpr->pRight;
+ while( pColExpr->op==TK_DOT ){
+ pColExpr = pColExpr->pRight;
+ assert( pColExpr!=0 );
+ }
if( pColExpr->op==TK_COLUMN && ALWAYS(pColExpr->pTab!=0) ){
/* For columns use the column name name */
int iCol = pColExpr->iColumn;
pTab = pColExpr->pTab;
if( iCol<0 ) iCol = pTab->iPKey;
- zName = sqlite3MPrintf(db, "%s",
- iCol>=0 ? pTab->aCol[iCol].zName : "rowid");
+ zName = iCol>=0 ? pTab->aCol[iCol].zName : "rowid";
}else if( pColExpr->op==TK_ID ){
assert( !ExprHasProperty(pColExpr, EP_IntValue) );
- zName = sqlite3MPrintf(db, "%s", pColExpr->u.zToken);
+ zName = pColExpr->u.zToken;
}else{
/* Use the original text of the column expression as its name */
- zName = sqlite3MPrintf(db, "%s", pEList->a[i].zSpan);
+ zName = pEList->a[i].zSpan;
}
}
- if( db->mallocFailed ){
- sqlite3DbFree(db, zName);
- break;
- }
+ zName = sqlite3MPrintf(db, "%s", zName);
/* Make sure the column name is unique. If the name is not unique,
- ** append a integer to the name so that it becomes unique.
+ ** append an integer to the name so that it becomes unique.
*/
- nName = sqlite3Strlen30(zName);
- for(j=cnt=0; j<i; j++){
- if( sqlite3StrICmp(aCol[j].zName, zName)==0 ){
- char *zNewName;
- zName[nName] = 0;
- zNewName = sqlite3MPrintf(db, "%s:%d", zName, ++cnt);
- sqlite3DbFree(db, zName);
- zName = zNewName;
- j = -1;
- if( zName==0 ) break;
+ cnt = 0;
+ while( zName && sqlite3HashFind(&ht, zName)!=0 ){
+ nName = sqlite3Strlen30(zName);
+ if( nName>0 ){
+ for(j=nName-1; j>0 && sqlite3Isdigit(zName[j]); j--){}
+ if( zName[j]==':' ) nName = j;
}
+ zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
+ if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
}
pCol->zName = zName;
+ sqlite3ColumnPropertiesFromName(0, pCol);
+ if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
+ sqlite3OomFault(db);
+ }
}
+ sqlite3HashClear(&ht);
if( db->mallocFailed ){
for(j=0; j<i; j++){
sqlite3DbFree(db, aCol[j].zName);
@@ -93662,7 +116062,7 @@ static int selectColumnsFromExprList(
sqlite3DbFree(db, aCol);
*paCol = 0;
*pnCol = 0;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
return SQLITE_OK;
}
@@ -93678,10 +116078,9 @@ static int selectColumnsFromExprList(
** This routine requires that all identifiers in the SELECT
** statement be resolved.
*/
-static void selectAddColumnTypeAndCollation(
+SQLITE_PRIVATE void sqlite3SelectAddColumnTypeAndCollation(
Parse *pParse, /* Parsing contexts */
- int nCol, /* Number of columns */
- Column *aCol, /* List of columns */
+ Table *pTab, /* Add column type information to this table */
Select *pSelect /* SELECT used to determine types and collations */
){
sqlite3 *db = pParse->db;
@@ -93691,24 +116090,37 @@ static void selectAddColumnTypeAndCollation(
int i;
Expr *p;
struct ExprList_item *a;
+ u64 szAll = 0;
assert( pSelect!=0 );
assert( (pSelect->selFlags & SF_Resolved)!=0 );
- assert( nCol==pSelect->pEList->nExpr || db->mallocFailed );
+ assert( pTab->nCol==pSelect->pEList->nExpr || db->mallocFailed );
if( db->mallocFailed ) return;
memset(&sNC, 0, sizeof(sNC));
sNC.pSrcList = pSelect->pSrc;
a = pSelect->pEList->a;
- for(i=0, pCol=aCol; i<nCol; i++, pCol++){
+ for(i=0, pCol=pTab->aCol; i<pTab->nCol; i++, pCol++){
+ const char *zType;
+ int n, m;
p = a[i].pExpr;
- pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0));
+ zType = columnType(&sNC, p, 0, 0, 0, &pCol->szEst);
+ szAll += pCol->szEst;
pCol->affinity = sqlite3ExprAffinity(p);
- if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE;
+ if( zType && (m = sqlite3Strlen30(zType))>0 ){
+ n = sqlite3Strlen30(pCol->zName);
+ pCol->zName = sqlite3DbReallocOrFree(db, pCol->zName, n+m+2);
+ if( pCol->zName ){
+ memcpy(&pCol->zName[n+1], zType, m+1);
+ pCol->colFlags |= COLFLAG_HASTYPE;
+ }
+ }
+ if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_BLOB;
pColl = sqlite3ExprCollSeq(pParse, p);
- if( pColl ){
+ if( pColl && pCol->zColl==0 ){
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
}
}
+ pTab->szTabRow = sqlite3LogEst(szAll*4);
}
/*
@@ -93733,12 +116145,12 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
}
/* The sqlite3ResultSetOfSelect() is only used n contexts where lookaside
** is disabled */
- assert( db->lookaside.bEnabled==0 );
+ assert( db->lookaside.bDisable );
pTab->nRef = 1;
pTab->zName = 0;
- pTab->nRowEst = 1000000;
- selectColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
- selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSelect);
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
+ sqlite3ColumnsFromExprList(pParse, pSelect->pEList, &pTab->nCol, &pTab->aCol);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSelect);
pTab->iPKey = -1;
if( db->mallocFailed ){
sqlite3DeleteTable(db, pTab);
@@ -93751,18 +116163,20 @@ SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse *pParse, Select *pSelect){
** Get a VDBE for the given parser context. Create a new one if necessary.
** If an error occurs, return NULL and leave a message in pParse.
*/
-SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
- Vdbe *v = pParse->pVdbe;
- if( v==0 ){
- v = pParse->pVdbe = sqlite3VdbeCreate(pParse->db);
-#ifndef SQLITE_OMIT_TRACE
- if( v ){
- sqlite3VdbeAddOp0(v, OP_Trace);
- }
-#endif
+static SQLITE_NOINLINE Vdbe *allocVdbe(Parse *pParse){
+ Vdbe *v = pParse->pVdbe = sqlite3VdbeCreate(pParse);
+ if( v ) sqlite3VdbeAddOp2(v, OP_Init, 0, 1);
+ if( pParse->pToplevel==0
+ && OptimizationEnabled(pParse->db,SQLITE_FactorOutConst)
+ ){
+ pParse->okConstFactor = 1;
}
return v;
}
+SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
+ Vdbe *v = pParse->pVdbe;
+ return v ? v : allocVdbe(pParse);
+}
/*
@@ -93776,8 +116190,13 @@ SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse *pParse){
**
** This routine changes the values of iLimit and iOffset only if
** a limit or offset is defined by pLimit and pOffset. iLimit and
-** iOffset should have been preset to appropriate default values
-** (usually but not always -1) prior to calling this routine.
+** iOffset should have been preset to appropriate default values (zero)
+** prior to calling this routine.
+**
+** The iOffset register (if it exists) is initialized to the value
+** of the OFFSET. The iLimit register is initialized to LIMIT. Register
+** iOffset+1 is initialized to LIMIT+OFFSET.
+**
** Only if pLimit!=0 or pOffset!=0 do the limit registers get
** redefined. The UNION ALL operator uses this property to force
** the reuse of the same limit and offset registers across multiple
@@ -93787,12 +116206,12 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
Vdbe *v = 0;
int iLimit = 0;
int iOffset;
- int addr1, n;
+ int n;
if( p->iLimit ) return;
/*
** "LIMIT -1" always shows all rows. There is some
- ** contraversy about what the correct behavior should be.
+ ** controversy about what the correct behavior should be.
** The current implementation interprets "LIMIT 0" to mean
** no rows.
*/
@@ -93801,35 +116220,30 @@ static void computeLimitRegisters(Parse *pParse, Select *p, int iBreak){
if( p->pLimit ){
p->iLimit = iLimit = ++pParse->nMem;
v = sqlite3GetVdbe(pParse);
- if( NEVER(v==0) ) return; /* VDBE should have already been allocated */
+ assert( v!=0 );
if( sqlite3ExprIsInteger(p->pLimit, &n) ){
sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit);
VdbeComment((v, "LIMIT counter"));
if( n==0 ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak);
- }else{
- if( p->nSelectRow > (double)n ) p->nSelectRow = (double)n;
+ sqlite3VdbeGoto(v, iBreak);
+ }else if( n>=0 && p->nSelectRow>sqlite3LogEst((u64)n) ){
+ p->nSelectRow = sqlite3LogEst((u64)n);
+ p->selFlags |= SF_FixedLimit;
}
}else{
sqlite3ExprCode(pParse, p->pLimit, iLimit);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); VdbeCoverage(v);
VdbeComment((v, "LIMIT counter"));
- sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak);
+ sqlite3VdbeAddOp2(v, OP_IfNot, iLimit, iBreak); VdbeCoverage(v);
}
if( p->pOffset ){
p->iOffset = iOffset = ++pParse->nMem;
pParse->nMem++; /* Allocate an extra register for limit+offset */
sqlite3ExprCode(pParse, p->pOffset, iOffset);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, iOffset); VdbeCoverage(v);
VdbeComment((v, "OFFSET counter"));
- addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iOffset);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, iOffset);
- sqlite3VdbeJumpHere(v, addr1);
- sqlite3VdbeAddOp3(v, OP_Add, iLimit, iOffset, iOffset+1);
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit, iLimit, iOffset+1, iOffset);
VdbeComment((v, "LIMIT+OFFSET"));
- addr1 = sqlite3VdbeAddOp1(v, OP_IfPos, iLimit);
- sqlite3VdbeAddOp2(v, OP_Integer, -1, iOffset+1);
- sqlite3VdbeJumpHere(v, addr1);
}
}
}
@@ -93851,22 +116265,271 @@ static CollSeq *multiSelectCollSeq(Parse *pParse, Select *p, int iCol){
pRet = 0;
}
assert( iCol>=0 );
- if( pRet==0 && iCol<p->pEList->nExpr ){
+ /* iCol must be less than p->pEList->nExpr. Otherwise an error would
+ ** have been thrown during name resolution and we would not have gotten
+ ** this far */
+ if( pRet==0 && ALWAYS(iCol<p->pEList->nExpr) ){
pRet = sqlite3ExprCollSeq(pParse, p->pEList->a[iCol].pExpr);
}
return pRet;
}
-#endif /* SQLITE_OMIT_COMPOUND_SELECT */
-/* Forward reference */
+/*
+** The select statement passed as the second parameter is a compound SELECT
+** with an ORDER BY clause. This function allocates and returns a KeyInfo
+** structure suitable for implementing the ORDER BY.
+**
+** Space to hold the KeyInfo structure is obtained from malloc. The calling
+** function is responsible for ensuring that this structure is eventually
+** freed.
+*/
+static KeyInfo *multiSelectOrderByKeyInfo(Parse *pParse, Select *p, int nExtra){
+ ExprList *pOrderBy = p->pOrderBy;
+ int nOrderBy = p->pOrderBy->nExpr;
+ sqlite3 *db = pParse->db;
+ KeyInfo *pRet = sqlite3KeyInfoAlloc(db, nOrderBy+nExtra, 1);
+ if( pRet ){
+ int i;
+ for(i=0; i<nOrderBy; i++){
+ struct ExprList_item *pItem = &pOrderBy->a[i];
+ Expr *pTerm = pItem->pExpr;
+ CollSeq *pColl;
+
+ if( pTerm->flags & EP_Collate ){
+ pColl = sqlite3ExprCollSeq(pParse, pTerm);
+ }else{
+ pColl = multiSelectCollSeq(pParse, p, pItem->u.x.iOrderByCol-1);
+ if( pColl==0 ) pColl = db->pDfltColl;
+ pOrderBy->a[i].pExpr =
+ sqlite3ExprAddCollateString(pParse, pTerm, pColl->zName);
+ }
+ assert( sqlite3KeyInfoIsWriteable(pRet) );
+ pRet->aColl[i] = pColl;
+ pRet->aSortOrder[i] = pOrderBy->a[i].sortOrder;
+ }
+ }
+
+ return pRet;
+}
+
+#ifndef SQLITE_OMIT_CTE
+/*
+** This routine generates VDBE code to compute the content of a WITH RECURSIVE
+** query of the form:
+**
+** <recursive-table> AS (<setup-query> UNION [ALL] <recursive-query>)
+** \___________/ \_______________/
+** p->pPrior p
+**
+**
+** There is exactly one reference to the recursive-table in the FROM clause
+** of recursive-query, marked with the SrcList->a[].fg.isRecursive flag.
+**
+** The setup-query runs once to generate an initial set of rows that go
+** into a Queue table. Rows are extracted from the Queue table one by
+** one. Each row extracted from Queue is output to pDest. Then the single
+** extracted row (now in the iCurrent table) becomes the content of the
+** recursive-table for a recursive-query run. The output of the recursive-query
+** is added back into the Queue table. Then another row is extracted from Queue
+** and the iteration continues until the Queue table is empty.
+**
+** If the compound query operator is UNION then no duplicate rows are ever
+** inserted into the Queue table. The iDistinct table keeps a copy of all rows
+** that have ever been inserted into Queue and causes duplicates to be
+** discarded. If the operator is UNION ALL, then duplicates are allowed.
+**
+** If the query has an ORDER BY, then entries in the Queue table are kept in
+** ORDER BY order and the first entry is extracted for each cycle. Without
+** an ORDER BY, the Queue table is just a FIFO.
+**
+** If a LIMIT clause is provided, then the iteration stops after LIMIT rows
+** have been output to pDest. A LIMIT of zero means to output no rows and a
+** negative LIMIT means to output all rows. If there is also an OFFSET clause
+** with a positive value, then the first OFFSET outputs are discarded rather
+** than being sent to pDest. The LIMIT count does not begin until after OFFSET
+** rows have been skipped.
+*/
+static void generateWithRecursiveQuery(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The recursive SELECT to be coded */
+ SelectDest *pDest /* What to do with query results */
+){
+ SrcList *pSrc = p->pSrc; /* The FROM clause of the recursive query */
+ int nCol = p->pEList->nExpr; /* Number of columns in the recursive table */
+ Vdbe *v = pParse->pVdbe; /* The prepared statement under construction */
+ Select *pSetup = p->pPrior; /* The setup query */
+ int addrTop; /* Top of the loop */
+ int addrCont, addrBreak; /* CONTINUE and BREAK addresses */
+ int iCurrent = 0; /* The Current table */
+ int regCurrent; /* Register holding Current table */
+ int iQueue; /* The Queue table */
+ int iDistinct = 0; /* To ensure unique results if UNION */
+ int eDest = SRT_Fifo; /* How to write to Queue */
+ SelectDest destQueue; /* SelectDest targetting the Queue table */
+ int i; /* Loop counter */
+ int rc; /* Result code */
+ ExprList *pOrderBy; /* The ORDER BY clause */
+ Expr *pLimit, *pOffset; /* Saved LIMIT and OFFSET */
+ int regLimit, regOffset; /* Registers used by LIMIT and OFFSET */
+
+ /* Obtain authorization to do a recursive query */
+ if( sqlite3AuthCheck(pParse, SQLITE_RECURSIVE, 0, 0, 0) ) return;
+
+ /* Process the LIMIT and OFFSET clauses, if they exist */
+ addrBreak = sqlite3VdbeMakeLabel(v);
+ computeLimitRegisters(pParse, p, addrBreak);
+ pLimit = p->pLimit;
+ pOffset = p->pOffset;
+ regLimit = p->iLimit;
+ regOffset = p->iOffset;
+ p->pLimit = p->pOffset = 0;
+ p->iLimit = p->iOffset = 0;
+ pOrderBy = p->pOrderBy;
+
+ /* Locate the cursor number of the Current table */
+ for(i=0; ALWAYS(i<pSrc->nSrc); i++){
+ if( pSrc->a[i].fg.isRecursive ){
+ iCurrent = pSrc->a[i].iCursor;
+ break;
+ }
+ }
+
+ /* Allocate cursors numbers for Queue and Distinct. The cursor number for
+ ** the Distinct table must be exactly one greater than Queue in order
+ ** for the SRT_DistFifo and SRT_DistQueue destinations to work. */
+ iQueue = pParse->nTab++;
+ if( p->op==TK_UNION ){
+ eDest = pOrderBy ? SRT_DistQueue : SRT_DistFifo;
+ iDistinct = pParse->nTab++;
+ }else{
+ eDest = pOrderBy ? SRT_Queue : SRT_Fifo;
+ }
+ sqlite3SelectDestInit(&destQueue, eDest, iQueue);
+
+ /* Allocate cursors for Current, Queue, and Distinct. */
+ regCurrent = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_OpenPseudo, iCurrent, regCurrent, nCol);
+ if( pOrderBy ){
+ KeyInfo *pKeyInfo = multiSelectOrderByKeyInfo(pParse, p, 1);
+ sqlite3VdbeAddOp4(v, OP_OpenEphemeral, iQueue, pOrderBy->nExpr+2, 0,
+ (char*)pKeyInfo, P4_KEYINFO);
+ destQueue.pOrderBy = pOrderBy;
+ }else{
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iQueue, nCol);
+ }
+ VdbeComment((v, "Queue table"));
+ if( iDistinct ){
+ p->addrOpenEphm[0] = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iDistinct, 0);
+ p->selFlags |= SF_UsesEphemeral;
+ }
+
+ /* Detach the ORDER BY clause from the compound SELECT */
+ p->pOrderBy = 0;
+
+ /* Store the results of the setup-query in Queue. */
+ pSetup->pNext = 0;
+ rc = sqlite3Select(pParse, pSetup, &destQueue);
+ pSetup->pNext = p;
+ if( rc ) goto end_of_recursive_query;
+
+ /* Find the next row in the Queue and output that row */
+ addrTop = sqlite3VdbeAddOp2(v, OP_Rewind, iQueue, addrBreak); VdbeCoverage(v);
+
+ /* Transfer the next row in Queue over to Current */
+ sqlite3VdbeAddOp1(v, OP_NullRow, iCurrent); /* To reset column cache */
+ if( pOrderBy ){
+ sqlite3VdbeAddOp3(v, OP_Column, iQueue, pOrderBy->nExpr+1, regCurrent);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_RowData, iQueue, regCurrent);
+ }
+ sqlite3VdbeAddOp1(v, OP_Delete, iQueue);
+
+ /* Output the single row in Current */
+ addrCont = sqlite3VdbeMakeLabel(v);
+ codeOffset(v, regOffset, addrCont);
+ selectInnerLoop(pParse, p, p->pEList, iCurrent,
+ 0, 0, pDest, addrCont, addrBreak);
+ if( regLimit ){
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, regLimit, addrBreak);
+ VdbeCoverage(v);
+ }
+ sqlite3VdbeResolveLabel(v, addrCont);
+
+ /* Execute the recursive SELECT taking the single row in Current as
+ ** the value for the recursive-table. Store the results in the Queue.
+ */
+ if( p->selFlags & SF_Aggregate ){
+ sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
+ }else{
+ p->pPrior = 0;
+ sqlite3Select(pParse, p, &destQueue);
+ assert( p->pPrior==0 );
+ p->pPrior = pSetup;
+ }
+
+ /* Keep running the loop until the Queue is empty */
+ sqlite3VdbeGoto(v, addrTop);
+ sqlite3VdbeResolveLabel(v, addrBreak);
+
+end_of_recursive_query:
+ sqlite3ExprListDelete(pParse->db, p->pOrderBy);
+ p->pOrderBy = pOrderBy;
+ p->pLimit = pLimit;
+ p->pOffset = pOffset;
+ return;
+}
+#endif /* SQLITE_OMIT_CTE */
+
+/* Forward references */
static int multiSelectOrderBy(
Parse *pParse, /* Parsing context */
Select *p, /* The right-most of SELECTs to be coded */
SelectDest *pDest /* What to do with query results */
);
+/*
+** Handle the special case of a compound-select that originates from a
+** VALUES clause. By handling this as a special case, we avoid deep
+** recursion, and thus do not need to enforce the SQLITE_LIMIT_COMPOUND_SELECT
+** on a VALUES clause.
+**
+** Because the Select object originates from a VALUES clause:
+** (1) It has no LIMIT or OFFSET
+** (2) All terms are UNION ALL
+** (3) There is no ORDER BY clause
+*/
+static int multiSelectValues(
+ Parse *pParse, /* Parsing context */
+ Select *p, /* The right-most of SELECTs to be coded */
+ SelectDest *pDest /* What to do with query results */
+){
+ Select *pPrior;
+ int nRow = 1;
+ int rc = 0;
+ assert( p->selFlags & SF_MultiValue );
+ do{
+ assert( p->selFlags & SF_Values );
+ assert( p->op==TK_ALL || (p->op==TK_SELECT && p->pPrior==0) );
+ assert( p->pLimit==0 );
+ assert( p->pOffset==0 );
+ assert( p->pNext==0 || p->pEList->nExpr==p->pNext->pEList->nExpr );
+ if( p->pPrior==0 ) break;
+ assert( p->pPrior->pNext==p );
+ p = p->pPrior;
+ nRow++;
+ }while(1);
+ while( p ){
+ pPrior = p->pPrior;
+ p->pPrior = 0;
+ rc = sqlite3Select(pParse, p, pDest);
+ p->pPrior = pPrior;
+ if( rc ) break;
+ p->nSelectRow = nRow;
+ p = p->pNext;
+ }
+ return rc;
+}
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** This routine is called to process a compound query form from
** two or more separate queries using UNION, UNION ALL, EXCEPT, or
@@ -93910,18 +116573,17 @@ static int multiSelect(
Select *pDelete = 0; /* Chain of simple selects to delete */
sqlite3 *db; /* Database connection */
#ifndef SQLITE_OMIT_EXPLAIN
- int iSub1; /* EQP id of left-hand query */
- int iSub2; /* EQP id of right-hand query */
+ int iSub1 = 0; /* EQP id of left-hand query */
+ int iSub2 = 0; /* EQP id of right-hand query */
#endif
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
*/
assert( p && p->pPrior ); /* Calling function guarantees this much */
+ assert( (p->selFlags & SF_Recursive)==0 || p->op==TK_ALL || p->op==TK_UNION );
db = pParse->db;
pPrior = p->pPrior;
- assert( pPrior->pRightmost!=pPrior );
- assert( pPrior->pRightmost==p->pRightmost );
dest = *pDest;
if( pPrior->pOrderBy ){
sqlite3ErrorMsg(pParse,"ORDER BY clause should come after %s not before",
@@ -93943,27 +116605,34 @@ static int multiSelect(
*/
if( dest.eDest==SRT_EphemTab ){
assert( p->pEList );
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iParm, p->pEList->nExpr);
- sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, dest.iSDParm, p->pEList->nExpr);
dest.eDest = SRT_Table;
}
+ /* Special handling for a compound-select that originates as a VALUES clause.
+ */
+ if( p->selFlags & SF_MultiValue ){
+ rc = multiSelectValues(pParse, p, &dest);
+ goto multi_select_end;
+ }
+
/* Make sure all SELECTs in the statement have the same number of elements
** in their result sets.
*/
assert( p->pEList && pPrior->pEList );
- if( p->pEList->nExpr!=pPrior->pEList->nExpr ){
- sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
- " do not have the same number of result columns", selectOpName(p->op));
- rc = 1;
- goto multi_select_end;
- }
+ assert( p->pEList->nExpr==pPrior->pEList->nExpr );
+
+#ifndef SQLITE_OMIT_CTE
+ if( p->selFlags & SF_Recursive ){
+ generateWithRecursiveQuery(pParse, p, &dest);
+ }else
+#endif
/* Compound SELECTs that have an ORDER BY clause are handled separately.
*/
if( p->pOrderBy ){
return multiSelectOrderBy(pParse, p, pDest);
- }
+ }else
/* Generate code for the left and right SELECT statements.
*/
@@ -93972,6 +116641,8 @@ static int multiSelect(
int addr = 0;
int nLimit;
assert( !pPrior->pLimit );
+ pPrior->iLimit = p->iLimit;
+ pPrior->iOffset = p->iOffset;
pPrior->pLimit = p->pLimit;
pPrior->pOffset = p->pOffset;
explainSetInteger(iSub1, pParse->iNextSelectId);
@@ -93985,20 +116656,24 @@ static int multiSelect(
p->iLimit = pPrior->iLimit;
p->iOffset = pPrior->iOffset;
if( p->iLimit ){
- addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit);
+ addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
VdbeComment((v, "Jump ahead if LIMIT reached"));
+ if( p->iOffset ){
+ sqlite3VdbeAddOp3(v, OP_OffsetLimit,
+ p->iLimit, p->iOffset+1, p->iOffset);
+ }
}
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &dest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
p->pPrior = pPrior;
- p->nSelectRow += pPrior->nSelectRow;
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
if( pPrior->pLimit
&& sqlite3ExprIsInteger(pPrior->pLimit, &nLimit)
- && p->nSelectRow > (double)nLimit
+ && nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
){
- p->nSelectRow = (double)nLimit;
+ p->nSelectRow = sqlite3LogEst((u64)nLimit);
}
if( addr ){
sqlite3VdbeJumpHere(v, addr);
@@ -94017,15 +116692,13 @@ static int multiSelect(
testcase( p->op==TK_EXCEPT );
testcase( p->op==TK_UNION );
priorOp = SRT_Union;
- if( dest.eDest==priorOp && ALWAYS(!p->pLimit &&!p->pOffset) ){
+ if( dest.eDest==priorOp ){
/* We can reuse a temporary table generated by a SELECT to our
** right.
*/
- assert( p->pRightmost!=p ); /* Can only happen for leftward elements
- ** of a 3-way or more compound */
assert( p->pLimit==0 ); /* Not allowed on leftward elements */
assert( p->pOffset==0 ); /* Not allowed on leftward elements */
- unionTab = dest.iParm;
+ unionTab = dest.iSDParm;
}else{
/* We will need to create our own temporary table to hold the
** intermediate results.
@@ -94035,7 +116708,7 @@ static int multiSelect(
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
- p->pRightmost->selFlags |= SF_UsesEphemeral;
+ findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
}
@@ -94072,7 +116745,9 @@ static int multiSelect(
pDelete = p->pPrior;
p->pPrior = pPrior;
p->pOrderBy = 0;
- if( p->op==TK_UNION ) p->nSelectRow += pPrior->nSelectRow;
+ if( p->op==TK_UNION ){
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
+ }
sqlite3ExprDelete(db, p->pLimit);
p->pLimit = pLimit;
p->pOffset = pOffset;
@@ -94082,24 +116757,24 @@ static int multiSelect(
/* Convert the data in the temporary table into whatever form
** it is that we currently need.
*/
- assert( unionTab==dest.iParm || dest.eDest!=priorOp );
+ assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
if( dest.eDest!=priorOp ){
int iCont, iBreak, iStart;
assert( p->pEList );
if( dest.eDest==SRT_Output ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
- generateColumnNames(pParse, 0, pFirst->pEList);
+ generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
}
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
iStart = sqlite3VdbeCurrentAddr(v);
- selectInnerLoop(pParse, p, p->pEList, unionTab, p->pEList->nExpr,
- 0, -1, &dest, iCont, iBreak);
+ selectInnerLoop(pParse, p, p->pEList, unionTab,
+ 0, 0, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart);
+ sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
}
@@ -94124,7 +116799,7 @@ static int multiSelect(
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
assert( p->addrOpenEphm[0] == -1 );
p->addrOpenEphm[0] = addr;
- p->pRightmost->selFlags |= SF_UsesEphemeral;
+ findRightmost(p)->selFlags |= SF_UsesEphemeral;
assert( p->pEList );
/* Code the SELECTs to our left into temporary table "tab1".
@@ -94146,7 +116821,7 @@ static int multiSelect(
p->pLimit = 0;
pOffset = p->pOffset;
p->pOffset = 0;
- intersectdest.iParm = tab2;
+ intersectdest.iSDParm = tab2;
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &intersectdest);
testcase( rc!=SQLITE_OK );
@@ -94164,20 +116839,20 @@ static int multiSelect(
if( dest.eDest==SRT_Output ){
Select *pFirst = p;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
- generateColumnNames(pParse, 0, pFirst->pEList);
+ generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
}
iBreak = sqlite3VdbeMakeLabel(v);
iCont = sqlite3VdbeMakeLabel(v);
computeLimitRegisters(pParse, p, iBreak);
- sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak);
+ sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
r1 = sqlite3GetTempReg(pParse);
iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1);
- sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, r1);
- selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr,
- 0, -1, &dest, iCont, iBreak);
+ selectInnerLoop(pParse, p, p->pEList, tab1,
+ 0, 0, &dest, iCont, iBreak);
sqlite3VdbeResolveLabel(v, iCont);
- sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart);
+ sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
sqlite3VdbeResolveLabel(v, iBreak);
sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
@@ -94203,18 +116878,13 @@ static int multiSelect(
CollSeq **apColl; /* For looping through pKeyInfo->aColl[] */
int nCol; /* Number of columns in result set */
- assert( p->pRightmost==p );
+ assert( p->pNext==0 );
nCol = p->pEList->nExpr;
- pKeyInfo = sqlite3DbMallocZero(db,
- sizeof(*pKeyInfo)+nCol*(sizeof(CollSeq*) + 1));
+ pKeyInfo = sqlite3KeyInfoAlloc(db, nCol, 1);
if( !pKeyInfo ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
goto multi_select_end;
}
-
- pKeyInfo->enc = ENC(db);
- pKeyInfo->nField = (u16)nCol;
-
for(i=0, apColl=pKeyInfo->aColl; i<nCol; i++, apColl++){
*apColl = multiSelectCollSeq(pParse, p, i);
if( 0==*apColl ){
@@ -94232,27 +116902,41 @@ static int multiSelect(
break;
}
sqlite3VdbeChangeP2(v, addr, nCol);
- sqlite3VdbeChangeP4(v, addr, (char*)pKeyInfo, P4_KEYINFO);
+ sqlite3VdbeChangeP4(v, addr, (char*)sqlite3KeyInfoRef(pKeyInfo),
+ P4_KEYINFO);
pLoop->addrOpenEphm[i] = -1;
}
}
- sqlite3DbFree(db, pKeyInfo);
+ sqlite3KeyInfoUnref(pKeyInfo);
}
multi_select_end:
- pDest->iMem = dest.iMem;
- pDest->nMem = dest.nMem;
+ pDest->iSdst = dest.iSdst;
+ pDest->nSdst = dest.nSdst;
sqlite3SelectDelete(db, pDelete);
return rc;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/*
+** Error message for when two or more terms of a compound select have different
+** size result sets.
+*/
+SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p){
+ if( p->selFlags & SF_Values ){
+ sqlite3ErrorMsg(pParse, "all VALUES must have the same number of terms");
+ }else{
+ sqlite3ErrorMsg(pParse, "SELECTs to the left and right of %s"
+ " do not have the same number of result columns", selectOpName(p->op));
+ }
+}
+
+/*
** Code an output subroutine for a coroutine implementation of a
** SELECT statment.
**
-** The data to be output is contained in pIn->iMem. There are
-** pIn->nMem columns to be output. pDest is where the output should
+** The data to be output is contained in pIn->iSdst. There are
+** pIn->nSdst columns to be output. pDest is where the output should
** be sent.
**
** regReturn is the number of the register holding the subroutine
@@ -94275,7 +116959,6 @@ static int generateOutputSubroutine(
int regReturn, /* The return address register */
int regPrev, /* Previous result register. No uniqueness if 0 */
KeyInfo *pKeyInfo, /* For comparing with previous entry */
- int p4type, /* The p4 type for pKeyInfo */
int iBreak /* Jump here if we hit the LIMIT */
){
Vdbe *v = pParse->pVdbe;
@@ -94288,33 +116971,32 @@ static int generateOutputSubroutine(
/* Suppress duplicates for UNION, EXCEPT, and INTERSECT
*/
if( regPrev ){
- int j1, j2;
- j1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev);
- j2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iMem, regPrev+1, pIn->nMem,
- (char*)pKeyInfo, p4type);
- sqlite3VdbeAddOp3(v, OP_Jump, j2+2, iContinue, j2+2);
- sqlite3VdbeJumpHere(v, j1);
- sqlite3ExprCodeCopy(pParse, pIn->iMem, regPrev+1, pIn->nMem);
+ int addr1, addr2;
+ addr1 = sqlite3VdbeAddOp1(v, OP_IfNot, regPrev); VdbeCoverage(v);
+ addr2 = sqlite3VdbeAddOp4(v, OP_Compare, pIn->iSdst, regPrev+1, pIn->nSdst,
+ (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
+ sqlite3VdbeAddOp3(v, OP_Jump, addr2+2, iContinue, addr2+2); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr1);
+ sqlite3VdbeAddOp3(v, OP_Copy, pIn->iSdst, regPrev+1, pIn->nSdst-1);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regPrev);
}
if( pParse->db->mallocFailed ) return 0;
- /* Suppress the the first OFFSET entries if there is an OFFSET clause
+ /* Suppress the first OFFSET entries if there is an OFFSET clause
*/
- codeOffset(v, p, iContinue);
+ codeOffset(v, p->iOffset, iContinue);
+ assert( pDest->eDest!=SRT_Exists );
+ assert( pDest->eDest!=SRT_Table );
switch( pDest->eDest ){
/* Store the result as data using a unique key.
*/
- case SRT_Table:
case SRT_EphemTab: {
int r1 = sqlite3GetTempReg(pParse);
int r2 = sqlite3GetTempReg(pParse);
- testcase( pDest->eDest==SRT_Table );
- testcase( pDest->eDest==SRT_EphemTab );
- sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iMem, pIn->nMem, r1);
- sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iParm, r2);
- sqlite3VdbeAddOp3(v, OP_Insert, pDest->iParm, r1, r2);
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst, r1);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, pDest->iSDParm, r2);
+ sqlite3VdbeAddOp3(v, OP_Insert, pDest->iSDParm, r1, r2);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3ReleaseTempReg(pParse, r2);
sqlite3ReleaseTempReg(pParse, r1);
@@ -94322,55 +117004,42 @@ static int generateOutputSubroutine(
}
#ifndef SQLITE_OMIT_SUBQUERY
- /* If we are creating a set for an "expr IN (SELECT ...)" construct,
- ** then there should be a single item on the stack. Write this
- ** item into the set table with bogus data.
+ /* If we are creating a set for an "expr IN (SELECT ...)".
*/
case SRT_Set: {
int r1;
- assert( pIn->nMem==1 );
- p->affinity =
- sqlite3CompareAffinity(p->pEList->a[0].pExpr, pDest->affinity);
+ testcase( pIn->nSdst>1 );
r1 = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iMem, 1, r1, &p->affinity, 1);
- sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, 1);
- sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iParm, r1);
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, pIn->iSdst, pIn->nSdst,
+ r1, pDest->zAffSdst, pIn->nSdst);
+ sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, pDest->iSDParm, r1);
sqlite3ReleaseTempReg(pParse, r1);
break;
}
-#if 0 /* Never occurs on an ORDER BY query */
- /* If any row exist in the result set, record that fact and abort.
- */
- case SRT_Exists: {
- sqlite3VdbeAddOp2(v, OP_Integer, 1, pDest->iParm);
- /* The LIMIT clause will terminate the loop for us */
- break;
- }
-#endif
-
/* If this is a scalar select that is part of an expression, then
** store the results in the appropriate memory cell and break out
** of the scan loop.
*/
case SRT_Mem: {
- assert( pIn->nMem==1 );
- sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iParm, 1);
+ assert( pIn->nSdst==1 || pParse->nErr>0 ); testcase( pIn->nSdst!=1 );
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSDParm, 1);
/* The LIMIT clause will jump out of the loop for us */
break;
}
#endif /* #ifndef SQLITE_OMIT_SUBQUERY */
/* The results are stored in a sequence of registers
- ** starting at pDest->iMem. Then the co-routine yields.
+ ** starting at pDest->iSdst. Then the co-routine yields.
*/
case SRT_Coroutine: {
- if( pDest->iMem==0 ){
- pDest->iMem = sqlite3GetTempRange(pParse, pIn->nMem);
- pDest->nMem = pIn->nMem;
+ if( pDest->iSdst==0 ){
+ pDest->iSdst = sqlite3GetTempRange(pParse, pIn->nSdst);
+ pDest->nSdst = pIn->nSdst;
}
- sqlite3ExprCodeMove(pParse, pIn->iMem, pDest->iMem, pDest->nMem);
- sqlite3VdbeAddOp1(v, OP_Yield, pDest->iParm);
+ sqlite3ExprCodeMove(pParse, pIn->iSdst, pDest->iSdst, pIn->nSdst);
+ sqlite3VdbeAddOp1(v, OP_Yield, pDest->iSDParm);
break;
}
@@ -94384,8 +117053,8 @@ static int generateOutputSubroutine(
*/
default: {
assert( pDest->eDest==SRT_Output );
- sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iMem, pIn->nMem);
- sqlite3ExprCacheAffinityChange(pParse, pIn->iMem, pIn->nMem);
+ sqlite3VdbeAddOp2(v, OP_ResultRow, pIn->iSdst, pIn->nSdst);
+ sqlite3ExprCacheAffinityChange(pParse, pIn->iSdst, pIn->nSdst);
break;
}
}
@@ -94393,7 +117062,7 @@ static int generateOutputSubroutine(
/* Jump to the end of the loop if the LIMIT is reached.
*/
if( p->iLimit ){
- sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1);
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, p->iLimit, iBreak); VdbeCoverage(v);
}
/* Generate the subroutine return
@@ -94501,9 +117170,7 @@ static int multiSelectOrderBy(
SelectDest destA; /* Destination for coroutine A */
SelectDest destB; /* Destination for coroutine B */
int regAddrA; /* Address register for select-A coroutine */
- int regEofA; /* Flag to indicate when select-A is complete */
int regAddrB; /* Address register for select-B coroutine */
- int regEofB; /* Flag to indicate when select-B is complete */
int addrSelectA; /* Address of the select-A coroutine */
int addrSelectB; /* Address of the select-B coroutine */
int regOutA; /* Address register for the output-A subroutine */
@@ -94511,6 +117178,7 @@ static int multiSelectOrderBy(
int addrOutA; /* Address of the output-A subroutine */
int addrOutB = 0; /* Address of the output-B subroutine */
int addrEofA; /* Address of the select-A-exhausted subroutine */
+ int addrEofA_noB; /* Alternate addrEofA if B is uninitialized */
int addrEofB; /* Address of the select-B-exhausted subroutine */
int addrAltB; /* Address of the A<B subroutine */
int addrAeqB; /* Address of the A==B subroutine */
@@ -94522,7 +117190,7 @@ static int multiSelectOrderBy(
int savedOffset; /* Saved value of p->iOffset */
int labelCmpr; /* Label for the start of the merge algorithm */
int labelEnd; /* Label for the end of the overall SELECT stmt */
- int j1; /* Jump instructions that get retargetted */
+ int addr1; /* Jump instructions that get retargetted */
int op; /* One of TK_ALL, TK_UNION, TK_EXCEPT, TK_INTERSECT */
KeyInfo *pKeyDup = 0; /* Comparison information for duplicate removal */
KeyInfo *pKeyMerge; /* Comparison information for merging rows */
@@ -94561,16 +117229,16 @@ static int multiSelectOrderBy(
for(i=1; db->mallocFailed==0 && i<=p->pEList->nExpr; i++){
struct ExprList_item *pItem;
for(j=0, pItem=pOrderBy->a; j<nOrderBy; j++, pItem++){
- assert( pItem->iCol>0 );
- if( pItem->iCol==i ) break;
+ assert( pItem->u.x.iOrderByCol>0 );
+ if( pItem->u.x.iOrderByCol==i ) break;
}
if( j==nOrderBy ){
Expr *pNew = sqlite3Expr(db, TK_INTEGER, 0);
- if( pNew==0 ) return SQLITE_NOMEM;
+ if( pNew==0 ) return SQLITE_NOMEM_BKPT;
pNew->flags |= EP_IntValue;
pNew->u.iValue = i;
pOrderBy = sqlite3ExprListAppend(pParse, pOrderBy, pNew);
- pOrderBy->a[nOrderBy++].iCol = (u16)i;
+ if( pOrderBy ) pOrderBy->a[nOrderBy++].u.x.iOrderByCol = (u16)i;
}
}
}
@@ -94582,33 +117250,16 @@ static int multiSelectOrderBy(
** to the right and the left are evaluated, they use the correct
** collation.
*/
- aPermute = sqlite3DbMallocRaw(db, sizeof(int)*nOrderBy);
+ aPermute = sqlite3DbMallocRawNN(db, sizeof(int)*(nOrderBy + 1));
if( aPermute ){
struct ExprList_item *pItem;
- for(i=0, pItem=pOrderBy->a; i<nOrderBy; i++, pItem++){
- assert( pItem->iCol>0 && pItem->iCol<=p->pEList->nExpr );
- aPermute[i] = pItem->iCol - 1;
- }
- pKeyMerge =
- sqlite3DbMallocRaw(db, sizeof(*pKeyMerge)+nOrderBy*(sizeof(CollSeq*)+1));
- if( pKeyMerge ){
- pKeyMerge->aSortOrder = (u8*)&pKeyMerge->aColl[nOrderBy];
- pKeyMerge->nField = (u16)nOrderBy;
- pKeyMerge->enc = ENC(db);
- for(i=0; i<nOrderBy; i++){
- CollSeq *pColl;
- Expr *pTerm = pOrderBy->a[i].pExpr;
- if( pTerm->flags & EP_ExpCollate ){
- pColl = pTerm->pColl;
- }else{
- pColl = multiSelectCollSeq(pParse, p, aPermute[i]);
- pTerm->flags |= EP_ExpCollate;
- pTerm->pColl = pColl;
- }
- pKeyMerge->aColl[i] = pColl;
- pKeyMerge->aSortOrder[i] = pOrderBy->a[i].sortOrder;
- }
+ aPermute[0] = nOrderBy;
+ for(i=1, pItem=pOrderBy->a; i<=nOrderBy; i++, pItem++){
+ assert( pItem->u.x.iOrderByCol>0 );
+ assert( pItem->u.x.iOrderByCol<=p->pEList->nExpr );
+ aPermute[i] = pItem->u.x.iOrderByCol - 1;
}
+ pKeyMerge = multiSelectOrderByKeyInfo(pParse, p, 1);
}else{
pKeyMerge = 0;
}
@@ -94627,14 +117278,12 @@ static int multiSelectOrderBy(
}else{
int nExpr = p->pEList->nExpr;
assert( nOrderBy>=nExpr || db->mallocFailed );
- regPrev = sqlite3GetTempRange(pParse, nExpr+1);
+ regPrev = pParse->nMem+1;
+ pParse->nMem += nExpr+1;
sqlite3VdbeAddOp2(v, OP_Integer, 0, regPrev);
- pKeyDup = sqlite3DbMallocZero(db,
- sizeof(*pKeyDup) + nExpr*(sizeof(CollSeq*)+1) );
+ pKeyDup = sqlite3KeyInfoAlloc(db, nExpr, 1);
if( pKeyDup ){
- pKeyDup->aSortOrder = (u8*)&pKeyDup->aColl[nExpr];
- pKeyDup->nField = (u16)nExpr;
- pKeyDup->enc = ENC(db);
+ assert( sqlite3KeyInfoIsWriteable(pKeyDup) );
for(i=0; i<nExpr; i++){
pKeyDup->aColl[i] = multiSelectCollSeq(pParse, p, i);
pKeyDup->aSortOrder[i] = 0;
@@ -94645,6 +117294,7 @@ static int multiSelectOrderBy(
/* Separate the left and the right query from one another
*/
p->pPrior = 0;
+ pPrior->pNext = 0;
sqlite3ResolveOrderGroupBy(pParse, p, p->pOrderBy, "ORDER");
if( pPrior->pPrior==0 ){
sqlite3ResolveOrderGroupBy(pParse, pPrior, pPrior->pOrderBy, "ORDER");
@@ -94667,37 +117317,30 @@ static int multiSelectOrderBy(
p->pOffset = 0;
regAddrA = ++pParse->nMem;
- regEofA = ++pParse->nMem;
regAddrB = ++pParse->nMem;
- regEofB = ++pParse->nMem;
regOutA = ++pParse->nMem;
regOutB = ++pParse->nMem;
sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
- /* Jump past the various subroutines and coroutines to the main
- ** merge loop
- */
- j1 = sqlite3VdbeAddOp0(v, OP_Goto);
- addrSelectA = sqlite3VdbeCurrentAddr(v);
-
-
/* Generate a coroutine to evaluate the SELECT statement to the
** left of the compound operator - the "A" select.
*/
- VdbeNoopComment((v, "Begin coroutine for left SELECT"));
+ addrSelectA = sqlite3VdbeCurrentAddr(v) + 1;
+ addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
+ VdbeComment((v, "left SELECT"));
pPrior->iLimit = regLimitA;
explainSetInteger(iSub1, pParse->iNextSelectId);
sqlite3Select(pParse, pPrior, &destA);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
- VdbeNoopComment((v, "End coroutine for left SELECT"));
+ sqlite3VdbeEndCoroutine(v, regAddrA);
+ sqlite3VdbeJumpHere(v, addr1);
/* Generate a coroutine to evaluate the SELECT statement on
** the right - the "B" select
*/
- addrSelectB = sqlite3VdbeCurrentAddr(v);
- VdbeNoopComment((v, "Begin coroutine for right SELECT"));
+ addrSelectB = sqlite3VdbeCurrentAddr(v) + 1;
+ addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrB, 0, addrSelectB);
+ VdbeComment((v, "right SELECT"));
savedLimit = p->iLimit;
savedOffset = p->iOffset;
p->iLimit = regLimitB;
@@ -94706,9 +117349,7 @@ static int multiSelectOrderBy(
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
- sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofB);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
- VdbeNoopComment((v, "End coroutine for right SELECT"));
+ sqlite3VdbeEndCoroutine(v, regAddrB);
/* Generate a subroutine that outputs the current row of the A
** select as the next output row of the compound select.
@@ -94716,7 +117357,7 @@ static int multiSelectOrderBy(
VdbeNoopComment((v, "Output routine for A"));
addrOutA = generateOutputSubroutine(pParse,
p, &destA, pDest, regOutA,
- regPrev, pKeyDup, P4_KEYINFO_HANDOFF, labelEnd);
+ regPrev, pKeyDup, labelEnd);
/* Generate a subroutine that outputs the current row of the B
** select as the next output row of the compound select.
@@ -94725,21 +117366,22 @@ static int multiSelectOrderBy(
VdbeNoopComment((v, "Output routine for B"));
addrOutB = generateOutputSubroutine(pParse,
p, &destB, pDest, regOutB,
- regPrev, pKeyDup, P4_KEYINFO_STATIC, labelEnd);
+ regPrev, pKeyDup, labelEnd);
}
+ sqlite3KeyInfoUnref(pKeyDup);
/* Generate a subroutine to run when the results from select A
** are exhausted and only data in select B remains.
*/
- VdbeNoopComment((v, "eof-A subroutine"));
if( op==TK_EXCEPT || op==TK_INTERSECT ){
- addrEofA = sqlite3VdbeAddOp2(v, OP_Goto, 0, labelEnd);
+ addrEofA_noB = addrEofA = labelEnd;
}else{
- addrEofA = sqlite3VdbeAddOp2(v, OP_If, regEofB, labelEnd);
- sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofA);
- p->nSelectRow += pPrior->nSelectRow;
+ VdbeNoopComment((v, "eof-A subroutine"));
+ addrEofA = sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
+ addrEofA_noB = sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, labelEnd);
+ VdbeCoverage(v);
+ sqlite3VdbeGoto(v, addrEofA);
+ p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
}
/* Generate a subroutine to run when the results from select B
@@ -94750,19 +117392,17 @@ static int multiSelectOrderBy(
if( p->nSelectRow > pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
}else{
VdbeNoopComment((v, "eof-B subroutine"));
- addrEofB = sqlite3VdbeAddOp2(v, OP_If, regEofA, labelEnd);
- sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEofB);
+ addrEofB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, labelEnd); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, addrEofB);
}
/* Generate code to handle the case of A<B
*/
VdbeNoopComment((v, "A-lt-B subroutine"));
addrAltB = sqlite3VdbeAddOp2(v, OP_Gosub, regOutA, addrOutA);
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
- sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, labelCmpr);
/* Generate code to handle the case of A==B
*/
@@ -94774,9 +117414,8 @@ static int multiSelectOrderBy(
}else{
VdbeNoopComment((v, "A-eq-B subroutine"));
addrAeqB =
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
- sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, labelCmpr);
}
/* Generate code to handle the case of A>B
@@ -94786,33 +117425,23 @@ static int multiSelectOrderBy(
if( op==TK_ALL || op==TK_UNION ){
sqlite3VdbeAddOp2(v, OP_Gosub, regOutB, addrOutB);
}
- sqlite3VdbeAddOp1(v, OP_Yield, regAddrB);
- sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB);
- sqlite3VdbeAddOp2(v, OP_Goto, 0, labelCmpr);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
+ sqlite3VdbeGoto(v, labelCmpr);
/* This code runs once to initialize everything.
*/
- sqlite3VdbeJumpHere(v, j1);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofA);
- sqlite3VdbeAddOp2(v, OP_Integer, 0, regEofB);
- sqlite3VdbeAddOp2(v, OP_Gosub, regAddrA, addrSelectA);
- sqlite3VdbeAddOp2(v, OP_Gosub, regAddrB, addrSelectB);
- sqlite3VdbeAddOp2(v, OP_If, regEofA, addrEofA);
- sqlite3VdbeAddOp2(v, OP_If, regEofB, addrEofB);
+ sqlite3VdbeJumpHere(v, addr1);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrA, addrEofA_noB); VdbeCoverage(v);
+ sqlite3VdbeAddOp2(v, OP_Yield, regAddrB, addrEofB); VdbeCoverage(v);
/* Implement the main merge loop
*/
sqlite3VdbeResolveLabel(v, labelCmpr);
sqlite3VdbeAddOp4(v, OP_Permutation, 0, 0, 0, (char*)aPermute, P4_INTARRAY);
- sqlite3VdbeAddOp4(v, OP_Compare, destA.iMem, destB.iMem, nOrderBy,
- (char*)pKeyMerge, P4_KEYINFO_HANDOFF);
- sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB);
-
- /* Release temporary registers
- */
- if( regPrev ){
- sqlite3ReleaseTempRange(pParse, regPrev, nOrderBy+1);
- }
+ sqlite3VdbeAddOp4(v, OP_Compare, destA.iSdst, destB.iSdst, nOrderBy,
+ (char*)pKeyMerge, P4_KEYINFO);
+ sqlite3VdbeChangeP5(v, OPFLAG_PERMUTE);
+ sqlite3VdbeAddOp3(v, OP_Jump, addrAltB, addrAeqB, addrAgtB); VdbeCoverage(v);
/* Jump to the this point in order to terminate the query.
*/
@@ -94823,7 +117452,7 @@ static int multiSelectOrderBy(
if( pDest->eDest==SRT_Output ){
Select *pFirst = pPrior;
while( pFirst->pPrior ) pFirst = pFirst->pPrior;
- generateColumnNames(pParse, 0, pFirst->pEList);
+ generateColumnNames(pParse, pFirst->pSrc, pFirst->pEList);
}
/* Reassembly the compound query so that it will be freed correctly
@@ -94832,18 +117461,19 @@ static int multiSelectOrderBy(
sqlite3SelectDelete(db, p->pPrior);
}
p->pPrior = pPrior;
+ pPrior->pNext = p;
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
explainComposite(pParse, p->op, iSub1, iSub2, 0);
- return SQLITE_OK;
+ return pParse->nErr!=0;
}
#endif
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/* Forward Declarations */
static void substExprList(sqlite3*, ExprList*, int, ExprList*);
-static void substSelect(sqlite3*, Select *, int, ExprList *);
+static void substSelect(sqlite3*, Select *, int, ExprList*, int);
/*
** Scan through the expression pExpr. Replace every reference to
@@ -94873,9 +117503,6 @@ static Expr *substExpr(
assert( pEList!=0 && pExpr->iColumn<pEList->nExpr );
assert( pExpr->pLeft==0 && pExpr->pRight==0 );
pNew = sqlite3ExprDup(db, pEList->a[pExpr->iColumn].pExpr, 0);
- if( pNew && pExpr->pColl ){
- pNew->pColl = pExpr->pColl;
- }
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
}
@@ -94883,7 +117510,7 @@ static Expr *substExpr(
pExpr->pLeft = substExpr(db, pExpr->pLeft, iTable, pEList);
pExpr->pRight = substExpr(db, pExpr->pRight, iTable, pEList);
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- substSelect(db, pExpr->x.pSelect, iTable, pEList);
+ substSelect(db, pExpr->x.pSelect, iTable, pEList, 1);
}else{
substExprList(db, pExpr->x.pList, iTable, pEList);
}
@@ -94906,33 +117533,35 @@ static void substSelect(
sqlite3 *db, /* Report malloc errors here */
Select *p, /* SELECT statement in which to make substitutions */
int iTable, /* Table to be replaced */
- ExprList *pEList /* Substitute values */
+ ExprList *pEList, /* Substitute values */
+ int doPrior /* Do substitutes on p->pPrior too */
){
SrcList *pSrc;
struct SrcList_item *pItem;
int i;
if( !p ) return;
- substExprList(db, p->pEList, iTable, pEList);
- substExprList(db, p->pGroupBy, iTable, pEList);
- substExprList(db, p->pOrderBy, iTable, pEList);
- p->pHaving = substExpr(db, p->pHaving, iTable, pEList);
- p->pWhere = substExpr(db, p->pWhere, iTable, pEList);
- substSelect(db, p->pPrior, iTable, pEList);
- pSrc = p->pSrc;
- assert( pSrc ); /* Even for (SELECT 1) we have: pSrc!=0 but pSrc->nSrc==0 */
- if( ALWAYS(pSrc) ){
+ do{
+ substExprList(db, p->pEList, iTable, pEList);
+ substExprList(db, p->pGroupBy, iTable, pEList);
+ substExprList(db, p->pOrderBy, iTable, pEList);
+ p->pHaving = substExpr(db, p->pHaving, iTable, pEList);
+ p->pWhere = substExpr(db, p->pWhere, iTable, pEList);
+ pSrc = p->pSrc;
+ assert( pSrc!=0 );
for(i=pSrc->nSrc, pItem=pSrc->a; i>0; i--, pItem++){
- substSelect(db, pItem->pSelect, iTable, pEList);
+ substSelect(db, pItem->pSelect, iTable, pEList, 1);
+ if( pItem->fg.isTabFunc ){
+ substExprList(db, pItem->u1.pFuncArg, iTable, pEList);
+ }
}
- }
+ }while( doPrior && (p = p->pPrior)!=0 );
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
-** This routine attempts to flatten subqueries in order to speed
-** execution. It returns 1 if it makes changes and 0 if no flattening
-** occurs.
+** This routine attempts to flatten subqueries as a performance optimization.
+** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following
** query:
@@ -94951,7 +117580,7 @@ static void substSelect(
**
** SELECT x+y AS a FROM t1 WHERE z<100 AND a>5
**
-** The code generated for this simpification gives the same result
+** The code generated for this simplification gives the same result
** but only has to scan the data once. And because indices might
** exist on the table t1, a complete scan of the data might be
** avoided.
@@ -94960,7 +117589,10 @@ static void substSelect(
**
** (1) The subquery and the outer query do not both use aggregates.
**
-** (2) The subquery is not an aggregate or the outer query is not a join.
+** (2) The subquery is not an aggregate or (2a) the outer query is not a join
+** and (2b) the outer query does not use subqueries other than the one
+** FROM-clause subquery that is a candidate for flattening. (2b is
+** due to ticket [2f7170d73bf9abf80] from 2015-02-09.)
**
** (3) The subquery is not the right operand of a left outer join
** (Originally ticket #306. Strengthened by ticket #3300)
@@ -94974,15 +117606,20 @@ static void substSelect(
** (6) The subquery does not use aggregates or the outer query is not
** DISTINCT.
**
-** (7) The subquery has a FROM clause.
+** (7) The subquery has a FROM clause. TODO: For subqueries without
+** A FROM clause, consider adding a FROM close with the special
+** table sqlite_once that consists of a single row containing a
+** single NULL.
**
** (8) The subquery does not use LIMIT or the outer query is not a join.
**
** (9) The subquery does not use LIMIT or the outer query does not use
** aggregates.
**
-** (10) The subquery does not use aggregates or the outer query does not
-** use LIMIT.
+** (**) Restriction (10) was removed from the code on 2005-02-05 but we
+** accidently carried the comment forward until 2014-09-15. Original
+** text: "The subquery does not use aggregates or the outer query
+** does not use LIMIT."
**
** (11) The subquery and the outer query do not both have ORDER BY clauses.
**
@@ -95007,11 +117644,20 @@ static void substSelect(
**
** * is not itself part of a compound select,
** * is not an aggregate or DISTINCT query, and
-** * has no other tables or sub-selects in the FROM clause.
+** * is not a join
**
** The parent and sub-query may contain WHERE clauses. Subject to
** rules (11), (13) and (14), they may also contain ORDER BY,
-** LIMIT and OFFSET clauses.
+** LIMIT and OFFSET clauses. The subquery cannot use any compound
+** operator other than UNION ALL because all the other compound
+** operators have an implied DISTINCT which is disallowed by
+** restriction (4).
+**
+** Also, each component of the sub-query must return the same number
+** of result columns. This is actually a requirement for any compound
+** SELECT statement, but all the code here does is make sure that no
+** such (illegal) sub-query is flattened. The caller will detect the
+** syntax error and return a detailed message.
**
** (18) If the sub-query is a compound select, then all terms of the
** ORDER by clause of the parent must be simple references to
@@ -95023,12 +117669,25 @@ static void substSelect(
** (20) If the sub-query is a compound select, then it must not use
** an ORDER BY clause. Ticket #3773. We could relax this constraint
** somewhat by saying that the terms of the ORDER BY clause must
-** appear as unmodified result columns in the outer query. But
+** appear as unmodified result columns in the outer query. But we
** have other optimizations in mind to deal with that case.
**
** (21) The subquery does not use LIMIT or the outer query is not
** DISTINCT. (See ticket [752e1646fc]).
**
+** (22) The subquery is not a recursive CTE.
+**
+** (23) The parent is not a recursive CTE, or the sub-query is not a
+** compound query. This restriction is because transforming the
+** parent to a compound query confuses the code that handles
+** recursive queries in multiSelect().
+**
+** (24) The subquery is not an aggregate that uses the built-in min() or
+** or max() functions. (Without this restriction, a query like:
+** "SELECT x FROM (SELECT max(y), x FROM t1)" would not necessarily
+** return the value X for which Y was maximal.)
+**
+**
** In this routine, the "p" parameter is a pointer to the outer query.
** The subquery is p->pSrc->a[iFrom]. isAgg is true if the outer query
** uses aggregates and subqueryIsAgg is true if the subquery uses aggregates.
@@ -95047,7 +117706,7 @@ static int flattenSubquery(
int subqueryIsAgg /* True if the subquery uses aggregate functions */
){
const char *zSavedAuthContext = pParse->zAuthContext;
- Select *pParent;
+ Select *pParent; /* Current UNION ALL term of the other query */
Select *pSub; /* The inner query or "subquery" */
Select *pSub1; /* Pointer to the rightmost select in sub-query */
SrcList *pSrc; /* The FROM clause of the outer query */
@@ -95063,25 +117722,34 @@ static int flattenSubquery(
*/
assert( p!=0 );
assert( p->pPrior==0 ); /* Unable to flatten compound queries */
- if( db->flags & SQLITE_QueryFlattener ) return 0;
+ if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0;
pSrc = p->pSrc;
assert( pSrc && iFrom>=0 && iFrom<pSrc->nSrc );
pSubitem = &pSrc->a[iFrom];
iParent = pSubitem->iCursor;
pSub = pSubitem->pSelect;
assert( pSub!=0 );
- if( isAgg && subqueryIsAgg ) return 0; /* Restriction (1) */
- if( subqueryIsAgg && pSrc->nSrc>1 ) return 0; /* Restriction (2) */
+ if( subqueryIsAgg ){
+ if( isAgg ) return 0; /* Restriction (1) */
+ if( pSrc->nSrc>1 ) return 0; /* Restriction (2a) */
+ if( (p->pWhere && ExprHasProperty(p->pWhere,EP_Subquery))
+ || (sqlite3ExprListFlags(p->pEList) & EP_Subquery)!=0
+ || (sqlite3ExprListFlags(p->pOrderBy) & EP_Subquery)!=0
+ ){
+ return 0; /* Restriction (2b) */
+ }
+ }
+
pSubSrc = pSub->pSrc;
assert( pSubSrc );
/* Prior to version 3.1.2, when LIMIT and OFFSET had to be simple constants,
- ** not arbitrary expresssions, we allowed some combining of LIMIT and OFFSET
+ ** not arbitrary expressions, we allowed some combining of LIMIT and OFFSET
** because they could be computed at compile-time. But when LIMIT and OFFSET
** became arbitrary expressions, we were forced to add restrictions (13)
** and (14). */
if( pSub->pLimit && p->pLimit ) return 0; /* Restriction (13) */
if( pSub->pOffset ) return 0; /* Restriction (14) */
- if( p->pRightmost && pSub->pLimit ){
+ if( (p->selFlags & SF_Compound)!=0 && pSub->pLimit ){
return 0; /* Restriction (15) */
}
if( pSubSrc->nSrc==0 ) return 0; /* Restriction (7) */
@@ -95100,6 +117768,14 @@ static int flattenSubquery(
if( pSub->pLimit && (p->selFlags & SF_Distinct)!=0 ){
return 0; /* Restriction (21) */
}
+ testcase( pSub->selFlags & SF_Recursive );
+ testcase( pSub->selFlags & SF_MinMaxAgg );
+ if( pSub->selFlags & (SF_Recursive|SF_MinMaxAgg) ){
+ return 0; /* Restrictions (22) and (24) */
+ }
+ if( (p->selFlags & SF_Recursive) && pSub->pPrior ){
+ return 0; /* Restriction (23) */
+ }
/* OBSOLETE COMMENT 1:
** Restriction 3: If the subquery is a join, make sure the subquery is
@@ -95133,7 +117809,7 @@ static int flattenSubquery(
** is fraught with danger. Best to avoid the whole thing. If the
** subquery is the right term of a LEFT JOIN, then do not flatten.
*/
- if( (pSubitem->jointype & JT_OUTER)!=0 ){
+ if( (pSubitem->fg.jointype & JT_OUTER)!=0 ){
return 0;
}
@@ -95152,28 +117828,34 @@ static int flattenSubquery(
for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
+ assert( pSub->pSrc!=0 );
+ assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0
|| (pSub1->pPrior && pSub1->op!=TK_ALL)
- || NEVER(pSub1->pSrc==0) || pSub1->pSrc->nSrc!=1
+ || pSub1->pSrc->nSrc<1
){
return 0;
}
+ testcase( pSub1->pSrc->nSrc>1 );
}
/* Restriction 18. */
if( p->pOrderBy ){
int ii;
for(ii=0; ii<p->pOrderBy->nExpr; ii++){
- if( p->pOrderBy->a[ii].iCol==0 ) return 0;
+ if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
}
}
}
/***** If we reach this point, flattening is permitted. *****/
+ SELECTTRACE(1,pParse,p,("flatten %s.%p from term %d\n",
+ pSub->zSelName, pSub, iFrom));
/* Authorize the subquery */
pParse->zAuthContext = pSubitem->zName;
- sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0);
+ TESTONLY(i =) sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0);
+ testcase( i==SQLITE_DENY );
pParse->zAuthContext = zSavedAuthContext;
/* If the sub-query is a compound SELECT statement, then (by restrictions
@@ -95213,24 +117895,31 @@ static int flattenSubquery(
Select *pNew;
ExprList *pOrderBy = p->pOrderBy;
Expr *pLimit = p->pLimit;
+ Expr *pOffset = p->pOffset;
Select *pPrior = p->pPrior;
p->pOrderBy = 0;
p->pSrc = 0;
p->pPrior = 0;
p->pLimit = 0;
+ p->pOffset = 0;
pNew = sqlite3SelectDup(db, p, 0);
+ sqlite3SelectSetName(pNew, pSub->zSelName);
+ p->pOffset = pOffset;
p->pLimit = pLimit;
p->pOrderBy = pOrderBy;
p->pSrc = pSrc;
p->op = TK_ALL;
- p->pRightmost = 0;
if( pNew==0 ){
- pNew = pPrior;
+ p->pPrior = pPrior;
}else{
pNew->pPrior = pPrior;
- pNew->pRightmost = 0;
+ if( pPrior ) pPrior->pNext = pNew;
+ pNew->pNext = p;
+ p->pPrior = pNew;
+ SELECTTRACE(2,pParse,p,
+ ("compound-subquery flattener creates %s.%p as peer\n",
+ pNew->zSelName, pNew));
}
- p->pPrior = pNew;
if( db->mallocFailed ) return 1;
}
@@ -95291,7 +117980,7 @@ static int flattenSubquery(
if( pSrc ){
assert( pParent==p ); /* First time through the loop */
- jointype = pSubitem->jointype;
+ jointype = pSubitem->fg.jointype;
}else{
assert( pParent!=p ); /* 2nd and subsequent times through the loop */
pSrc = pParent->pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
@@ -95312,9 +118001,9 @@ static int flattenSubquery(
**
** The outer query has 3 slots in its FROM clause. One slot of the
** outer query (the middle slot) is used by the subquery. The next
- ** block of code will expand the out query to 4 slots. The middle
- ** slot is expanded to two slots in order to make space for the
- ** two elements in the FROM clause of the subquery.
+ ** block of code will expand the outer query FROM clause to 4 slots.
+ ** The middle slot is expanded to two slots in order to make space
+ ** for the two elements in the FROM clause of the subquery.
*/
if( nSubSrc>1 ){
pParent->pSrc = pSrc = sqlite3SrcListEnlarge(db, pSrc, nSubSrc-1,iFrom+1);
@@ -95328,10 +118017,11 @@ static int flattenSubquery(
*/
for(i=0; i<nSubSrc; i++){
sqlite3IdListDelete(db, pSrc->a[i+iFrom].pUsing);
+ assert( pSrc->a[i+iFrom].fg.isTabFunc==0 );
pSrc->a[i+iFrom] = pSubSrc->a[i];
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
- pSrc->a[iFrom].jointype = jointype;
+ pSrc->a[iFrom].fg.jointype = jointype;
/* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
@@ -95348,42 +118038,45 @@ static int flattenSubquery(
pList = pParent->pEList;
for(i=0; i<pList->nExpr; i++){
if( pList->a[i].zName==0 ){
- const char *zSpan = pList->a[i].zSpan;
- if( ALWAYS(zSpan) ){
- pList->a[i].zName = sqlite3DbStrDup(db, zSpan);
- }
+ char *zName = sqlite3DbStrDup(db, pList->a[i].zSpan);
+ sqlite3Dequote(zName);
+ pList->a[i].zName = zName;
}
}
- substExprList(db, pParent->pEList, iParent, pSub->pEList);
- if( isAgg ){
- substExprList(db, pParent->pGroupBy, iParent, pSub->pEList);
- pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList);
- }
if( pSub->pOrderBy ){
+ /* At this point, any non-zero iOrderByCol values indicate that the
+ ** ORDER BY column expression is identical to the iOrderByCol'th
+ ** expression returned by SELECT statement pSub. Since these values
+ ** do not necessarily correspond to columns in SELECT statement pParent,
+ ** zero them before transfering the ORDER BY clause.
+ **
+ ** Not doing this may cause an error if a subsequent call to this
+ ** function attempts to flatten a compound sub-query into pParent
+ ** (the only way this can happen is if the compound sub-query is
+ ** currently part of pSub->pSrc). See ticket [d11a6e908f]. */
+ ExprList *pOrderBy = pSub->pOrderBy;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ pOrderBy->a[i].u.x.iOrderByCol = 0;
+ }
assert( pParent->pOrderBy==0 );
- pParent->pOrderBy = pSub->pOrderBy;
+ assert( pSub->pPrior==0 );
+ pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
- }else if( pParent->pOrderBy ){
- substExprList(db, pParent->pOrderBy, iParent, pSub->pEList);
- }
- if( pSub->pWhere ){
- pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
- }else{
- pWhere = 0;
}
+ pWhere = sqlite3ExprDup(db, pSub->pWhere, 0);
if( subqueryIsAgg ){
assert( pParent->pHaving==0 );
pParent->pHaving = pParent->pWhere;
pParent->pWhere = pWhere;
- pParent->pHaving = substExpr(db, pParent->pHaving, iParent, pSub->pEList);
- pParent->pHaving = sqlite3ExprAnd(db, pParent->pHaving,
- sqlite3ExprDup(db, pSub->pHaving, 0));
+ pParent->pHaving = sqlite3ExprAnd(db,
+ sqlite3ExprDup(db, pSub->pHaving, 0), pParent->pHaving
+ );
assert( pParent->pGroupBy==0 );
pParent->pGroupBy = sqlite3ExprListDup(db, pSub->pGroupBy, 0);
}else{
- pParent->pWhere = substExpr(db, pParent->pWhere, iParent, pSub->pEList);
- pParent->pWhere = sqlite3ExprAnd(db, pParent->pWhere, pWhere);
+ pParent->pWhere = sqlite3ExprAnd(db, pWhere, pParent->pWhere);
}
+ substSelect(db, pParent, iParent, pSub->pEList, 0);
/* The flattened query is distinct if either the inner or the
** outer query is distinct.
@@ -95407,44 +118100,137 @@ static int flattenSubquery(
*/
sqlite3SelectDelete(db, pSub1);
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x100 ){
+ SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
+
return 1;
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
+
+
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
-** Analyze the SELECT statement passed as an argument to see if it
-** is a min() or max() query. Return WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX if
-** it is, or 0 otherwise. At present, a query is considered to be
-** a min()/max() query if:
+** Make copies of relevant WHERE clause terms of the outer query into
+** the WHERE clause of subquery. Example:
+**
+** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1) WHERE x=5 AND y=10;
+**
+** Transformed into:
+**
+** SELECT * FROM (SELECT a AS x, c-d AS y FROM t1 WHERE a=5 AND c-d=10)
+** WHERE x=5 AND y=10;
**
-** 1. There is a single object in the FROM clause.
+** The hope is that the terms added to the inner query will make it more
+** efficient.
**
-** 2. There is a single expression in the result set, and it is
-** either min(x) or max(x), where x is a column reference.
+** Do not attempt this optimization if:
+**
+** (1) The inner query is an aggregate. (In that case, we'd really want
+** to copy the outer WHERE-clause terms onto the HAVING clause of the
+** inner query. But they probably won't help there so do not bother.)
+**
+** (2) The inner query is the recursive part of a common table expression.
+**
+** (3) The inner query has a LIMIT clause (since the changes to the WHERE
+** close would change the meaning of the LIMIT).
+**
+** (4) The inner query is the right operand of a LEFT JOIN. (The caller
+** enforces this restriction since this routine does not have enough
+** information to know.)
+**
+** (5) The WHERE clause expression originates in the ON or USING clause
+** of a LEFT JOIN.
+**
+** Return 0 if no changes are made and non-zero if one or more WHERE clause
+** terms are duplicated into the subquery.
*/
-static u8 minMaxQuery(Select *p){
- Expr *pExpr;
- ExprList *pEList = p->pEList;
+static int pushDownWhereTerms(
+ sqlite3 *db, /* The database connection (for malloc()) */
+ Select *pSubq, /* The subquery whose WHERE clause is to be augmented */
+ Expr *pWhere, /* The WHERE clause of the outer query */
+ int iCursor /* Cursor number of the subquery */
+){
+ Expr *pNew;
+ int nChng = 0;
+ Select *pX; /* For looping over compound SELECTs in pSubq */
+ if( pWhere==0 ) return 0;
+ for(pX=pSubq; pX; pX=pX->pPrior){
+ if( (pX->selFlags & (SF_Aggregate|SF_Recursive))!=0 ){
+ testcase( pX->selFlags & SF_Aggregate );
+ testcase( pX->selFlags & SF_Recursive );
+ testcase( pX!=pSubq );
+ return 0; /* restrictions (1) and (2) */
+ }
+ }
+ if( pSubq->pLimit!=0 ){
+ return 0; /* restriction (3) */
+ }
+ while( pWhere->op==TK_AND ){
+ nChng += pushDownWhereTerms(db, pSubq, pWhere->pRight, iCursor);
+ pWhere = pWhere->pLeft;
+ }
+ if( ExprHasProperty(pWhere,EP_FromJoin) ) return 0; /* restriction 5 */
+ if( sqlite3ExprIsTableConstant(pWhere, iCursor) ){
+ nChng++;
+ while( pSubq ){
+ pNew = sqlite3ExprDup(db, pWhere, 0);
+ pNew = substExpr(db, pNew, iCursor, pSubq->pEList);
+ pSubq->pWhere = sqlite3ExprAnd(db, pSubq->pWhere, pNew);
+ pSubq = pSubq->pPrior;
+ }
+ }
+ return nChng;
+}
+#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */
- if( pEList->nExpr!=1 ) return WHERE_ORDERBY_NORMAL;
- pExpr = pEList->a[0].pExpr;
- if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
- if( NEVER(ExprHasProperty(pExpr, EP_xIsSelect)) ) return 0;
- pEList = pExpr->x.pList;
- if( pEList==0 || pEList->nExpr!=1 ) return 0;
- if( pEList->a[0].pExpr->op!=TK_AGG_COLUMN ) return WHERE_ORDERBY_NORMAL;
- assert( !ExprHasProperty(pExpr, EP_IntValue) );
- if( sqlite3StrICmp(pExpr->u.zToken,"min")==0 ){
- return WHERE_ORDERBY_MIN;
- }else if( sqlite3StrICmp(pExpr->u.zToken,"max")==0 ){
- return WHERE_ORDERBY_MAX;
+/*
+** Based on the contents of the AggInfo structure indicated by the first
+** argument, this function checks if the following are true:
+**
+** * the query contains just a single aggregate function,
+** * the aggregate function is either min() or max(), and
+** * the argument to the aggregate function is a column value.
+**
+** If all of the above are true, then WHERE_ORDERBY_MIN or WHERE_ORDERBY_MAX
+** is returned as appropriate. Also, *ppMinMax is set to point to the
+** list of arguments passed to the aggregate before returning.
+**
+** Or, if the conditions above are not met, *ppMinMax is set to 0 and
+** WHERE_ORDERBY_NORMAL is returned.
+*/
+static u8 minMaxQuery(AggInfo *pAggInfo, ExprList **ppMinMax){
+ int eRet = WHERE_ORDERBY_NORMAL; /* Return value */
+
+ *ppMinMax = 0;
+ if( pAggInfo->nFunc==1 ){
+ Expr *pExpr = pAggInfo->aFunc[0].pExpr; /* Aggregate function */
+ ExprList *pEList = pExpr->x.pList; /* Arguments to agg function */
+
+ assert( pExpr->op==TK_AGG_FUNCTION );
+ if( pEList && pEList->nExpr==1 && pEList->a[0].pExpr->op==TK_AGG_COLUMN ){
+ const char *zFunc = pExpr->u.zToken;
+ if( sqlite3StrICmp(zFunc, "min")==0 ){
+ eRet = WHERE_ORDERBY_MIN;
+ *ppMinMax = pEList;
+ }else if( sqlite3StrICmp(zFunc, "max")==0 ){
+ eRet = WHERE_ORDERBY_MAX;
+ *ppMinMax = pEList;
+ }
+ }
}
- return WHERE_ORDERBY_NORMAL;
+
+ assert( *ppMinMax==0 || (*ppMinMax)->nExpr==1 );
+ return eRet;
}
/*
** The select statement passed as the first argument is an aggregate query.
-** The second argment is the associated aggregate-info object. This
+** The second argument is the associated aggregate-info object. This
** function tests if the SELECT is of the form:
**
** SELECT count(*) FROM <tbl>
@@ -95470,7 +118256,8 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
if( IsVirtual(pTab) ) return 0;
if( pExpr->op!=TK_AGG_FUNCTION ) return 0;
- if( (pAggInfo->aFunc[0].pFunc->flags&SQLITE_FUNC_COUNT)==0 ) return 0;
+ if( NEVER(pAggInfo->nFunc==0) ) return 0;
+ if( (pAggInfo->aFunc[0].pFunc->funcFlags&SQLITE_FUNC_COUNT)==0 ) return 0;
if( pExpr->flags&EP_Distinct ) return 0;
return pTab;
@@ -95484,23 +118271,301 @@ static Table *isSimpleCount(Select *p, AggInfo *pAggInfo){
** pFrom->pIndex and return SQLITE_OK.
*/
SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pFrom){
- if( pFrom->pTab && pFrom->zIndex ){
+ if( pFrom->pTab && pFrom->fg.isIndexedBy ){
Table *pTab = pFrom->pTab;
- char *zIndex = pFrom->zIndex;
+ char *zIndexedBy = pFrom->u1.zIndexedBy;
Index *pIdx;
for(pIdx=pTab->pIndex;
- pIdx && sqlite3StrICmp(pIdx->zName, zIndex);
+ pIdx && sqlite3StrICmp(pIdx->zName, zIndexedBy);
pIdx=pIdx->pNext
);
if( !pIdx ){
- sqlite3ErrorMsg(pParse, "no such index: %s", zIndex, 0);
+ sqlite3ErrorMsg(pParse, "no such index: %s", zIndexedBy, 0);
pParse->checkSchema = 1;
return SQLITE_ERROR;
}
- pFrom->pIndex = pIdx;
+ pFrom->pIBIndex = pIdx;
+ }
+ return SQLITE_OK;
+}
+/*
+** Detect compound SELECT statements that use an ORDER BY clause with
+** an alternative collating sequence.
+**
+** SELECT ... FROM t1 EXCEPT SELECT ... FROM t2 ORDER BY .. COLLATE ...
+**
+** These are rewritten as a subquery:
+**
+** SELECT * FROM (SELECT ... FROM t1 EXCEPT SELECT ... FROM t2)
+** ORDER BY ... COLLATE ...
+**
+** This transformation is necessary because the multiSelectOrderBy() routine
+** above that generates the code for a compound SELECT with an ORDER BY clause
+** uses a merge algorithm that requires the same collating sequence on the
+** result columns as on the ORDER BY clause. See ticket
+** http://www.sqlite.org/src/info/6709574d2a
+**
+** This transformation is only needed for EXCEPT, INTERSECT, and UNION.
+** The UNION ALL operator works fine with multiSelectOrderBy() even when
+** there are COLLATE terms in the ORDER BY.
+*/
+static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
+ int i;
+ Select *pNew;
+ Select *pX;
+ sqlite3 *db;
+ struct ExprList_item *a;
+ SrcList *pNewSrc;
+ Parse *pParse;
+ Token dummy;
+
+ if( p->pPrior==0 ) return WRC_Continue;
+ if( p->pOrderBy==0 ) return WRC_Continue;
+ for(pX=p; pX && (pX->op==TK_ALL || pX->op==TK_SELECT); pX=pX->pPrior){}
+ if( pX==0 ) return WRC_Continue;
+ a = p->pOrderBy->a;
+ for(i=p->pOrderBy->nExpr-1; i>=0; i--){
+ if( a[i].pExpr->flags & EP_Collate ) break;
+ }
+ if( i<0 ) return WRC_Continue;
+
+ /* If we reach this point, that means the transformation is required. */
+
+ pParse = pWalker->pParse;
+ db = pParse->db;
+ pNew = sqlite3DbMallocZero(db, sizeof(*pNew) );
+ if( pNew==0 ) return WRC_Abort;
+ memset(&dummy, 0, sizeof(dummy));
+ pNewSrc = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&dummy,pNew,0,0);
+ if( pNewSrc==0 ) return WRC_Abort;
+ *pNew = *p;
+ p->pSrc = pNewSrc;
+ p->pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ASTERISK, 0));
+ p->op = TK_SELECT;
+ p->pWhere = 0;
+ pNew->pGroupBy = 0;
+ pNew->pHaving = 0;
+ pNew->pOrderBy = 0;
+ p->pPrior = 0;
+ p->pNext = 0;
+ p->pWith = 0;
+ p->selFlags &= ~SF_Compound;
+ assert( (p->selFlags & SF_Converted)==0 );
+ p->selFlags |= SF_Converted;
+ assert( pNew->pPrior!=0 );
+ pNew->pPrior->pNext = pNew;
+ pNew->pLimit = 0;
+ pNew->pOffset = 0;
+ return WRC_Continue;
+}
+
+/*
+** Check to see if the FROM clause term pFrom has table-valued function
+** arguments. If it does, leave an error message in pParse and return
+** non-zero, since pFrom is not allowed to be a table-valued function.
+*/
+static int cannotBeFunction(Parse *pParse, struct SrcList_item *pFrom){
+ if( pFrom->fg.isTabFunc ){
+ sqlite3ErrorMsg(pParse, "'%s' is not a function", pFrom->zName);
+ return 1;
+ }
+ return 0;
+}
+
+#ifndef SQLITE_OMIT_CTE
+/*
+** Argument pWith (which may be NULL) points to a linked list of nested
+** WITH contexts, from inner to outermost. If the table identified by
+** FROM clause element pItem is really a common-table-expression (CTE)
+** then return a pointer to the CTE definition for that table. Otherwise
+** return NULL.
+**
+** If a non-NULL value is returned, set *ppContext to point to the With
+** object that the returned CTE belongs to.
+*/
+static struct Cte *searchWith(
+ With *pWith, /* Current innermost WITH clause */
+ struct SrcList_item *pItem, /* FROM clause element to resolve */
+ With **ppContext /* OUT: WITH clause return value belongs to */
+){
+ const char *zName;
+ if( pItem->zDatabase==0 && (zName = pItem->zName)!=0 ){
+ With *p;
+ for(p=pWith; p; p=p->pOuter){
+ int i;
+ for(i=0; i<p->nCte; i++){
+ if( sqlite3StrICmp(zName, p->a[i].zName)==0 ){
+ *ppContext = p;
+ return &p->a[i];
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/* The code generator maintains a stack of active WITH clauses
+** with the inner-most WITH clause being at the top of the stack.
+**
+** This routine pushes the WITH clause passed as the second argument
+** onto the top of the stack. If argument bFree is true, then this
+** WITH clause will never be popped from the stack. In this case it
+** should be freed along with the Parse object. In other cases, when
+** bFree==0, the With object will be freed along with the SELECT
+** statement with which it is associated.
+*/
+SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
+ assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
+ if( pWith ){
+ assert( pParse->pWith!=pWith );
+ pWith->pOuter = pParse->pWith;
+ pParse->pWith = pWith;
+ if( bFree ) pParse->pWithToFree = pWith;
+ }
+}
+
+/*
+** This function checks if argument pFrom refers to a CTE declared by
+** a WITH clause on the stack currently maintained by the parser. And,
+** if currently processing a CTE expression, if it is a recursive
+** reference to the current CTE.
+**
+** If pFrom falls into either of the two categories above, pFrom->pTab
+** and other fields are populated accordingly. The caller should check
+** (pFrom->pTab!=0) to determine whether or not a successful match
+** was found.
+**
+** Whether or not a match is found, SQLITE_OK is returned if no error
+** occurs. If an error does occur, an error message is stored in the
+** parser and some error code other than SQLITE_OK returned.
+*/
+static int withExpand(
+ Walker *pWalker,
+ struct SrcList_item *pFrom
+){
+ Parse *pParse = pWalker->pParse;
+ sqlite3 *db = pParse->db;
+ struct Cte *pCte; /* Matched CTE (or NULL if no match) */
+ With *pWith; /* WITH clause that pCte belongs to */
+
+ assert( pFrom->pTab==0 );
+
+ pCte = searchWith(pParse->pWith, pFrom, &pWith);
+ if( pCte ){
+ Table *pTab;
+ ExprList *pEList;
+ Select *pSel;
+ Select *pLeft; /* Left-most SELECT statement */
+ int bMayRecursive; /* True if compound joined by UNION [ALL] */
+ With *pSavedWith; /* Initial value of pParse->pWith */
+
+ /* If pCte->zCteErr is non-NULL at this point, then this is an illegal
+ ** recursive reference to CTE pCte. Leave an error in pParse and return
+ ** early. If pCte->zCteErr is NULL, then this is not a recursive reference.
+ ** In this case, proceed. */
+ if( pCte->zCteErr ){
+ sqlite3ErrorMsg(pParse, pCte->zCteErr, pCte->zName);
+ return SQLITE_ERROR;
+ }
+ if( cannotBeFunction(pParse, pFrom) ) return SQLITE_ERROR;
+
+ assert( pFrom->pTab==0 );
+ pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ) return WRC_Abort;
+ pTab->nRef = 1;
+ pTab->zName = sqlite3DbStrDup(db, pCte->zName);
+ pTab->iPKey = -1;
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
+ pTab->tabFlags |= TF_Ephemeral | TF_NoVisibleRowid;
+ pFrom->pSelect = sqlite3SelectDup(db, pCte->pSelect, 0);
+ if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
+ assert( pFrom->pSelect );
+
+ /* Check if this is a recursive CTE. */
+ pSel = pFrom->pSelect;
+ bMayRecursive = ( pSel->op==TK_ALL || pSel->op==TK_UNION );
+ if( bMayRecursive ){
+ int i;
+ SrcList *pSrc = pFrom->pSelect->pSrc;
+ for(i=0; i<pSrc->nSrc; i++){
+ struct SrcList_item *pItem = &pSrc->a[i];
+ if( pItem->zDatabase==0
+ && pItem->zName!=0
+ && 0==sqlite3StrICmp(pItem->zName, pCte->zName)
+ ){
+ pItem->pTab = pTab;
+ pItem->fg.isRecursive = 1;
+ pTab->nRef++;
+ pSel->selFlags |= SF_Recursive;
+ }
+ }
+ }
+
+ /* Only one recursive reference is permitted. */
+ if( pTab->nRef>2 ){
+ sqlite3ErrorMsg(
+ pParse, "multiple references to recursive table: %s", pCte->zName
+ );
+ return SQLITE_ERROR;
+ }
+ assert( pTab->nRef==1 || ((pSel->selFlags&SF_Recursive) && pTab->nRef==2 ));
+
+ pCte->zCteErr = "circular reference: %s";
+ pSavedWith = pParse->pWith;
+ pParse->pWith = pWith;
+ sqlite3WalkSelect(pWalker, bMayRecursive ? pSel->pPrior : pSel);
+ pParse->pWith = pWith;
+
+ for(pLeft=pSel; pLeft->pPrior; pLeft=pLeft->pPrior);
+ pEList = pLeft->pEList;
+ if( pCte->pCols ){
+ if( pEList && pEList->nExpr!=pCte->pCols->nExpr ){
+ sqlite3ErrorMsg(pParse, "table %s has %d values for %d columns",
+ pCte->zName, pEList->nExpr, pCte->pCols->nExpr
+ );
+ pParse->pWith = pSavedWith;
+ return SQLITE_ERROR;
+ }
+ pEList = pCte->pCols;
+ }
+
+ sqlite3ColumnsFromExprList(pParse, pEList, &pTab->nCol, &pTab->aCol);
+ if( bMayRecursive ){
+ if( pSel->selFlags & SF_Recursive ){
+ pCte->zCteErr = "multiple recursive references: %s";
+ }else{
+ pCte->zCteErr = "recursive reference in a subquery: %s";
+ }
+ sqlite3WalkSelect(pWalker, pSel);
+ }
+ pCte->zCteErr = 0;
+ pParse->pWith = pSavedWith;
}
+
return SQLITE_OK;
}
+#endif
+
+#ifndef SQLITE_OMIT_CTE
+/*
+** If the SELECT passed as the second argument has an associated WITH
+** clause, pop it from the stack stored as part of the Parse object.
+**
+** This function is used as the xSelectCallback2() callback by
+** sqlite3SelectExpand() when walking a SELECT tree to resolve table
+** names and other FROM clause elements.
+*/
+static void selectPopWith(Walker *pWalker, Select *p){
+ Parse *pParse = pWalker->pParse;
+ With *pWith = findRightmost(p)->pWith;
+ if( pWith!=0 ){
+ assert( pParse->pWith==pWith );
+ pParse->pWith = pWith->pOuter;
+ }
+}
+#else
+#define selectPopWith 0
+#endif
/*
** This routine is a Walker callback for "expanding" a SELECT statement.
@@ -95514,10 +118579,10 @@ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, struct SrcList_item *pF
** fill pTabList->a[].pSelect with a copy of the SELECT statement
** that implements the view. A copy is made of the view's SELECT
** statement so that we can freely modify or delete that statement
-** without worrying about messing up the presistent representation
+** without worrying about messing up the persistent representation
** of the view.
**
-** (3) Add terms to the WHERE clause to accomodate the NATURAL keyword
+** (3) Add terms to the WHERE clause to accommodate the NATURAL keyword
** on joins and the ON and USING clause of joins.
**
** (4) Scan the list of columns in the result set (pEList) looking
@@ -95533,16 +118598,21 @@ static int selectExpander(Walker *pWalker, Select *p){
ExprList *pEList;
struct SrcList_item *pFrom;
sqlite3 *db = pParse->db;
+ Expr *pE, *pRight, *pExpr;
+ u16 selFlags = p->selFlags;
+ p->selFlags |= SF_Expanded;
if( db->mallocFailed ){
return WRC_Abort;
}
- if( NEVER(p->pSrc==0) || (p->selFlags & SF_Expanded)!=0 ){
+ if( NEVER(p->pSrc==0) || (selFlags & SF_Expanded)!=0 ){
return WRC_Prune;
}
- p->selFlags |= SF_Expanded;
pTabList = p->pSrc;
pEList = p->pEList;
+ if( pWalker->xSelectCallback2==selectPopWith ){
+ sqlite3WithPush(pParse, findRightmost(p)->pWith, 0);
+ }
/* Make sure cursor numbers have been assigned to all entries in
** the FROM clause of the SELECT statement.
@@ -95555,43 +118625,56 @@ static int selectExpander(Walker *pWalker, Select *p){
*/
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab;
- if( pFrom->pTab!=0 ){
- /* This statement has already been prepared. There is no need
- ** to go further. */
- assert( i==0 );
- return WRC_Prune;
- }
+ assert( pFrom->fg.isRecursive==0 || pFrom->pTab!=0 );
+ if( pFrom->fg.isRecursive ) continue;
+ assert( pFrom->pTab==0 );
+#ifndef SQLITE_OMIT_CTE
+ if( withExpand(pWalker, pFrom) ) return WRC_Abort;
+ if( pFrom->pTab ) {} else
+#endif
if( pFrom->zName==0 ){
#ifndef SQLITE_OMIT_SUBQUERY
Select *pSel = pFrom->pSelect;
/* A sub-query in the FROM clause of a SELECT */
assert( pSel!=0 );
assert( pFrom->pTab==0 );
- sqlite3WalkSelect(pWalker, pSel);
+ if( sqlite3WalkSelect(pWalker, pSel) ) return WRC_Abort;
pFrom->pTab = pTab = sqlite3DbMallocZero(db, sizeof(Table));
if( pTab==0 ) return WRC_Abort;
pTab->nRef = 1;
- pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
+ pTab->zName = sqlite3MPrintf(db, "sqlite_sq_%p", (void*)pTab);
while( pSel->pPrior ){ pSel = pSel->pPrior; }
- selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
+ sqlite3ColumnsFromExprList(pParse, pSel->pEList,&pTab->nCol,&pTab->aCol);
pTab->iPKey = -1;
- pTab->nRowEst = 1000000;
+ pTab->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) );
pTab->tabFlags |= TF_Ephemeral;
#endif
}else{
/* An ordinary table or view name in the FROM clause */
assert( pFrom->pTab==0 );
- pFrom->pTab = pTab =
- sqlite3LocateTable(pParse,0,pFrom->zName,pFrom->zDatabase);
+ pFrom->pTab = pTab = sqlite3LocateTableItem(pParse, 0, pFrom);
if( pTab==0 ) return WRC_Abort;
+ if( pTab->nRef==0xffff ){
+ sqlite3ErrorMsg(pParse, "too many references to \"%s\": max 65535",
+ pTab->zName);
+ pFrom->pTab = 0;
+ return WRC_Abort;
+ }
pTab->nRef++;
+ if( !IsVirtual(pTab) && cannotBeFunction(pParse, pFrom) ){
+ return WRC_Abort;
+ }
#if !defined(SQLITE_OMIT_VIEW) || !defined (SQLITE_OMIT_VIRTUALTABLE)
- if( pTab->pSelect || IsVirtual(pTab) ){
- /* We reach here if the named table is a really a view */
+ if( IsVirtual(pTab) || pTab->pSelect ){
+ i16 nCol;
if( sqlite3ViewGetColumnNames(pParse, pTab) ) return WRC_Abort;
assert( pFrom->pSelect==0 );
pFrom->pSelect = sqlite3SelectDup(db, pTab->pSelect, 0);
+ sqlite3SelectSetName(pFrom->pSelect, pTab->zName);
+ nCol = pTab->nCol;
+ pTab->nCol = -1;
sqlite3WalkSelect(pWalker, pFrom->pSelect);
+ pTab->nCol = nCol;
}
#endif
}
@@ -95611,19 +118694,20 @@ static int selectExpander(Walker *pWalker, Select *p){
/* For every "*" that occurs in the column list, insert the names of
** all columns in all tables. And for every TABLE.* insert the names
** of all columns in TABLE. The parser inserted a special expression
- ** with the TK_ALL operator for each "*" that it found in the column list.
- ** The following code just has to locate the TK_ALL expressions and expand
- ** each one to the list of all columns in all tables.
+ ** with the TK_ASTERISK operator for each "*" that it found in the column
+ ** list. The following code just has to locate the TK_ASTERISK
+ ** expressions and expand each one to the list of all columns in
+ ** all tables.
**
** The first loop just checks to see if there are any "*" operators
** that need expanding.
*/
for(k=0; k<pEList->nExpr; k++){
- Expr *pE = pEList->a[k].pExpr;
- if( pE->op==TK_ALL ) break;
+ pE = pEList->a[k].pExpr;
+ if( pE->op==TK_ASTERISK ) break;
assert( pE->op!=TK_DOT || pE->pRight!=0 );
assert( pE->op!=TK_DOT || (pE->pLeft!=0 && pE->pLeft->op==TK_ID) );
- if( pE->op==TK_DOT && pE->pRight->op==TK_ALL ) break;
+ if( pE->op==TK_DOT && pE->pRight->op==TK_ASTERISK ) break;
}
if( k<pEList->nExpr ){
/*
@@ -95638,9 +118722,12 @@ static int selectExpander(Walker *pWalker, Select *p){
&& (flags & SQLITE_ShortColNames)==0;
for(k=0; k<pEList->nExpr; k++){
- Expr *pE = a[k].pExpr;
- assert( pE->op!=TK_DOT || pE->pRight!=0 );
- if( pE->op!=TK_ALL && (pE->op!=TK_DOT || pE->pRight->op!=TK_ALL) ){
+ pE = a[k].pExpr;
+ pRight = pE->pRight;
+ assert( pE->op!=TK_DOT || pRight!=0 );
+ if( pE->op!=TK_ASTERISK
+ && (pE->op!=TK_DOT || pRight->op!=TK_ASTERISK)
+ ){
/* This particular expression does not need to be expanded.
*/
pNew = sqlite3ExprListAppend(pParse, pNew, a[k].pExpr);
@@ -95655,43 +118742,56 @@ static int selectExpander(Walker *pWalker, Select *p){
/* This expression is a "*" or a "TABLE.*" and needs to be
** expanded. */
int tableSeen = 0; /* Set to 1 when TABLE matches */
- char *zTName; /* text of name of TABLE */
+ char *zTName = 0; /* text of name of TABLE */
if( pE->op==TK_DOT ){
assert( pE->pLeft!=0 );
assert( !ExprHasProperty(pE->pLeft, EP_IntValue) );
zTName = pE->pLeft->u.zToken;
- }else{
- zTName = 0;
}
for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
Table *pTab = pFrom->pTab;
+ Select *pSub = pFrom->pSelect;
char *zTabName = pFrom->zAlias;
+ const char *zSchemaName = 0;
+ int iDb;
if( zTabName==0 ){
zTabName = pTab->zName;
}
if( db->mallocFailed ) break;
- if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
- continue;
+ if( pSub==0 || (pSub->selFlags & SF_NestedFrom)==0 ){
+ pSub = 0;
+ if( zTName && sqlite3StrICmp(zTName, zTabName)!=0 ){
+ continue;
+ }
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ zSchemaName = iDb>=0 ? db->aDb[iDb].zDbSName : "*";
}
- tableSeen = 1;
for(j=0; j<pTab->nCol; j++){
- Expr *pExpr, *pRight;
char *zName = pTab->aCol[j].zName;
char *zColname; /* The computed column name */
char *zToFree; /* Malloced string that needs to be freed */
Token sColname; /* Computed column name as a token */
- /* If a column is marked as 'hidden' (currently only possible
- ** for virtual tables), do not include it in the expanded
- ** result-set list.
+ assert( zName );
+ if( zTName && pSub
+ && sqlite3MatchSpanName(pSub->pEList->a[j].zSpan, 0, zTName, 0)==0
+ ){
+ continue;
+ }
+
+ /* If a column is marked as 'hidden', omit it from the expanded
+ ** result-set list unless the SELECT has the SF_IncludeHidden
+ ** bit set.
*/
- if( IsHiddenColumn(&pTab->aCol[j]) ){
- assert(IsVirtual(pTab));
+ if( (p->selFlags & SF_IncludeHidden)==0
+ && IsHiddenColumn(&pTab->aCol[j])
+ ){
continue;
}
+ tableSeen = 1;
if( i>0 && zTName==0 ){
- if( (pFrom->jointype & JT_NATURAL)!=0
+ if( (pFrom->fg.jointype & JT_NATURAL)!=0
&& tableAndColumnIndex(pTabList, i, zName, 0, 0)
){
/* In a NATURAL join, omit the join columns from the
@@ -95711,6 +118811,10 @@ static int selectExpander(Walker *pWalker, Select *p){
Expr *pLeft;
pLeft = sqlite3Expr(db, TK_ID, zTabName);
pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
+ if( zSchemaName ){
+ pLeft = sqlite3Expr(db, TK_ID, zSchemaName);
+ pExpr = sqlite3PExpr(pParse, TK_DOT, pLeft, pExpr, 0);
+ }
if( longNames ){
zColname = sqlite3MPrintf(db, "%s.%s", zTabName, zName);
zToFree = zColname;
@@ -95719,9 +118823,20 @@ static int selectExpander(Walker *pWalker, Select *p){
pExpr = pRight;
}
pNew = sqlite3ExprListAppend(pParse, pNew, pExpr);
- sColname.z = zColname;
- sColname.n = sqlite3Strlen30(zColname);
+ sqlite3TokenInit(&sColname, zColname);
sqlite3ExprListSetName(pParse, pNew, &sColname, 0);
+ if( pNew && (p->selFlags & SF_NestedFrom)!=0 ){
+ struct ExprList_item *pX = &pNew->a[pNew->nExpr-1];
+ if( pSub ){
+ pX->zSpan = sqlite3DbStrDup(db, pSub->pEList->a[j].zSpan);
+ testcase( pX->zSpan==0 );
+ }else{
+ pX->zSpan = sqlite3MPrintf(db, "%s.%s.%s",
+ zSchemaName, zTabName, zColname);
+ testcase( pX->zSpan==0 );
+ }
+ pX->bSpanIsTab = 1;
+ }
sqlite3DbFree(db, zToFree);
}
}
@@ -95740,6 +118855,7 @@ static int selectExpander(Walker *pWalker, Select *p){
#if SQLITE_MAX_COLUMN
if( p->pEList && p->pEList->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many columns in result set");
+ return WRC_Abort;
}
#endif
return WRC_Continue;
@@ -95754,7 +118870,7 @@ static int selectExpander(Walker *pWalker, Select *p){
** Walker.xSelectCallback is set to do something useful for every
** subquery in the parser tree.
*/
-static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
+SQLITE_PRIVATE int sqlite3ExprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
UNUSED_PARAMETER2(NotUsed, NotUsed2);
return WRC_Continue;
}
@@ -95774,9 +118890,17 @@ static int exprWalkNoop(Walker *NotUsed, Expr *NotUsed2){
*/
static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
Walker w;
- w.xSelectCallback = selectExpander;
- w.xExprCallback = exprWalkNoop;
+ memset(&w, 0, sizeof(w));
+ w.xExprCallback = sqlite3ExprWalkNoop;
w.pParse = pParse;
+ if( pParse->hasCompound ){
+ w.xSelectCallback = convertCompoundSelectToSubquery;
+ sqlite3WalkSelect(&w, pSelect);
+ }
+ w.xSelectCallback = selectExpander;
+ if( (pSelect->selFlags & SF_MultiValue)==0 ){
+ w.xSelectCallback2 = selectPopWith;
+ }
sqlite3WalkSelect(&w, pSelect);
}
@@ -95795,29 +118919,29 @@ static void sqlite3SelectExpand(Parse *pParse, Select *pSelect){
** at that point because identifiers had not yet been resolved. This
** routine is called after identifier resolution.
*/
-static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
+static void selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
Parse *pParse;
int i;
SrcList *pTabList;
struct SrcList_item *pFrom;
assert( p->selFlags & SF_Resolved );
- if( (p->selFlags & SF_HasTypeInfo)==0 ){
- p->selFlags |= SF_HasTypeInfo;
- pParse = pWalker->pParse;
- pTabList = p->pSrc;
- for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
- Table *pTab = pFrom->pTab;
- if( ALWAYS(pTab!=0) && (pTab->tabFlags & TF_Ephemeral)!=0 ){
- /* A sub-query in the FROM clause of a SELECT */
- Select *pSel = pFrom->pSelect;
- assert( pSel );
+ assert( (p->selFlags & SF_HasTypeInfo)==0 );
+ p->selFlags |= SF_HasTypeInfo;
+ pParse = pWalker->pParse;
+ pTabList = p->pSrc;
+ for(i=0, pFrom=pTabList->a; i<pTabList->nSrc; i++, pFrom++){
+ Table *pTab = pFrom->pTab;
+ assert( pTab!=0 );
+ if( (pTab->tabFlags & TF_Ephemeral)!=0 ){
+ /* A sub-query in the FROM clause of a SELECT */
+ Select *pSel = pFrom->pSelect;
+ if( pSel ){
while( pSel->pPrior ) pSel = pSel->pPrior;
- selectAddColumnTypeAndCollation(pParse, pTab->nCol, pTab->aCol, pSel);
+ sqlite3SelectAddColumnTypeAndCollation(pParse, pTab, pSel);
}
}
}
- return WRC_Continue;
}
#endif
@@ -95832,8 +118956,9 @@ static int selectAddSubqueryTypeInfo(Walker *pWalker, Select *p){
static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
#ifndef SQLITE_OMIT_SUBQUERY
Walker w;
- w.xSelectCallback = selectAddSubqueryTypeInfo;
- w.xExprCallback = exprWalkNoop;
+ memset(&w, 0, sizeof(w));
+ w.xSelectCallback2 = selectAddSubqueryTypeInfo;
+ w.xExprCallback = sqlite3ExprWalkNoop;
w.pParse = pParse;
sqlite3WalkSelect(&w, pSelect);
#endif
@@ -95841,7 +118966,7 @@ static void sqlite3SelectAddTypeInfo(Parse *pParse, Select *pSelect){
/*
-** This routine sets of a SELECT statement for processing. The
+** This routine sets up a SELECT statement for processing. The
** following is accomplished:
**
** * VDBE Cursor numbers are assigned to all FROM-clause terms.
@@ -95860,6 +118985,7 @@ SQLITE_PRIVATE void sqlite3SelectPrep(
sqlite3 *db;
if( NEVER(p==0) ) return;
db = pParse->db;
+ if( db->mallocFailed ) return;
if( p->selFlags & SF_HasTypeInfo ) return;
sqlite3SelectExpand(pParse, p);
if( pParse->nErr || db->mallocFailed ) return;
@@ -95873,20 +118999,30 @@ SQLITE_PRIVATE void sqlite3SelectPrep(
**
** The aggregate accumulator is a set of memory cells that hold
** intermediate results while calculating an aggregate. This
-** routine simply stores NULLs in all of those memory cells.
+** routine generates code that stores NULLs in all of those memory
+** cells.
*/
static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
Vdbe *v = pParse->pVdbe;
int i;
struct AggInfo_func *pFunc;
- if( pAggInfo->nFunc+pAggInfo->nColumn==0 ){
- return;
- }
+ int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
+ if( nReg==0 ) return;
+#ifdef SQLITE_DEBUG
+ /* Verify that all AggInfo registers are within the range specified by
+ ** AggInfo.mnReg..AggInfo.mxReg */
+ assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
for(i=0; i<pAggInfo->nColumn; i++){
- sqlite3VdbeAddOp2(v, OP_Null, 0, pAggInfo->aCol[i].iMem);
+ assert( pAggInfo->aCol[i].iMem>=pAggInfo->mnReg
+ && pAggInfo->aCol[i].iMem<=pAggInfo->mxReg );
}
+ for(i=0; i<pAggInfo->nFunc; i++){
+ assert( pAggInfo->aFunc[i].iMem>=pAggInfo->mnReg
+ && pAggInfo->aFunc[i].iMem<=pAggInfo->mxReg );
+ }
+#endif
+ sqlite3VdbeAddOp3(v, OP_Null, 0, pAggInfo->mnReg, pAggInfo->mxReg);
for(pFunc=pAggInfo->aFunc, i=0; i<pAggInfo->nFunc; i++, pFunc++){
- sqlite3VdbeAddOp2(v, OP_Null, 0, pFunc->iMem);
if( pFunc->iDistinct>=0 ){
Expr *pE = pFunc->pExpr;
assert( !ExprHasProperty(pE, EP_xIsSelect) );
@@ -95895,9 +119031,9 @@ static void resetAccumulator(Parse *pParse, AggInfo *pAggInfo){
"argument");
pFunc->iDistinct = -1;
}else{
- KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList);
+ KeyInfo *pKeyInfo = keyInfoFromExprList(pParse, pE->x.pList, 0, 0);
sqlite3VdbeAddOp4(v, OP_OpenEphemeral, pFunc->iDistinct, 0, 0,
- (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ (char*)pKeyInfo, P4_KEYINFO);
}
}
}
@@ -95926,11 +119062,12 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
Vdbe *v = pParse->pVdbe;
int i;
+ int regHit = 0;
+ int addrHitTest = 0;
struct AggInfo_func *pF;
struct AggInfo_col *pC;
pAggInfo->directMode = 1;
- sqlite3ExprCacheClear(pParse);
for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
int nArg;
int addrNext = 0;
@@ -95940,17 +119077,18 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( pList ){
nArg = pList->nExpr;
regAgg = sqlite3GetTempRange(pParse, nArg);
- sqlite3ExprCodeExprList(pParse, pList, regAgg, 1);
+ sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
}else{
nArg = 0;
regAgg = 0;
}
if( pF->iDistinct>=0 ){
addrNext = sqlite3VdbeMakeLabel(v);
- assert( nArg==1 );
+ testcase( nArg==0 ); /* Error condition */
+ testcase( nArg>1 ); /* Also an error */
codeDistinct(pParse, pF->iDistinct, addrNext, 1, regAgg);
}
- if( pF->pFunc->flags & SQLITE_FUNC_NEEDCOLL ){
+ if( pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL ){
CollSeq *pColl = 0;
struct ExprList_item *pItem;
int j;
@@ -95961,9 +119099,10 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
if( !pColl ){
pColl = pParse->db->pDfltColl;
}
- sqlite3VdbeAddOp4(v, OP_CollSeq, 0, 0, 0, (char *)pColl, P4_COLLSEQ);
+ if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
+ sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, (char *)pColl, P4_COLLSEQ);
}
- sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem,
+ sqlite3VdbeAddOp4(v, OP_AggStep0, 0, regAgg, pF->iMem,
(void*)pF->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u8)nArg);
sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg);
@@ -95984,12 +119123,18 @@ static void updateAccumulator(Parse *pParse, AggInfo *pAggInfo){
** Another solution would be to change the OP_SCopy used to copy cached
** values to an OP_Copy.
*/
+ if( regHit ){
+ addrHitTest = sqlite3VdbeAddOp1(v, OP_If, regHit); VdbeCoverage(v);
+ }
sqlite3ExprCacheClear(pParse);
for(i=0, pC=pAggInfo->aCol; i<pAggInfo->nAccumulator; i++, pC++){
sqlite3ExprCode(pParse, pC->pExpr, pC->iMem);
}
pAggInfo->directMode = 0;
sqlite3ExprCacheClear(pParse);
+ if( addrHitTest ){
+ sqlite3VdbeJumpHere(v, addrHitTest);
+ }
}
/*
@@ -96003,11 +119148,11 @@ static void explainSimpleCount(
Index *pIdx /* Index used to optimize scan, or NULL */
){
if( pParse->explain==2 ){
- char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s %s%s(~%d rows)",
- pTab->zName,
- pIdx ? "USING COVERING INDEX " : "",
- pIdx ? pIdx->zName : "",
- pTab->nRowEst
+ int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
+ char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
+ pTab->zName,
+ bCover ? " USING COVERING INDEX " : "",
+ bCover ? pIdx->zName : ""
);
sqlite3VdbeAddOp4(
pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC
@@ -96021,49 +119166,8 @@ static void explainSimpleCount(
/*
** Generate code for the SELECT statement given in the p argument.
**
-** The results are distributed in various ways depending on the
-** contents of the SelectDest structure pointed to by argument pDest
-** as follows:
-**
-** pDest->eDest Result
-** ------------ -------------------------------------------
-** SRT_Output Generate a row of output (using the OP_ResultRow
-** opcode) for each row in the result set.
-**
-** SRT_Mem Only valid if the result is a single column.
-** Store the first column of the first result row
-** in register pDest->iParm then abandon the rest
-** of the query. This destination implies "LIMIT 1".
-**
-** SRT_Set The result must be a single column. Store each
-** row of result as the key in table pDest->iParm.
-** Apply the affinity pDest->affinity before storing
-** results. Used to implement "IN (SELECT ...)".
-**
-** SRT_Union Store results as a key in a temporary table pDest->iParm.
-**
-** SRT_Except Remove results from the temporary table pDest->iParm.
-**
-** SRT_Table Store results in temporary table pDest->iParm.
-** This is like SRT_EphemTab except that the table
-** is assumed to already be open.
-**
-** SRT_EphemTab Create an temporary table pDest->iParm and store
-** the result there. The cursor is left open after
-** returning. This is like SRT_Table except that
-** this destination uses OP_OpenEphemeral to create
-** the table first.
-**
-** SRT_Coroutine Generate a co-routine that returns a new row of
-** results each time it is invoked. The entry point
-** of the co-routine is stored in register pDest->iParm.
-**
-** SRT_Exists Store a 1 in memory cell pDest->iParm if the result
-** set is not empty.
-**
-** SRT_Discard Throw the results away. This is used by SELECT
-** statements within triggers whose only purpose is
-** the side-effects of functions.
+** The results are returned according to the SelectDest structure.
+** See comments in sqliteInt.h for further information.
**
** This routine returns the number of errors. If any errors are
** encountered, then an appropriate error message is left in
@@ -96081,17 +119185,14 @@ SQLITE_PRIVATE int sqlite3Select(
WhereInfo *pWInfo; /* Return from sqlite3WhereBegin() */
Vdbe *v; /* The virtual machine under construction */
int isAgg; /* True for select lists like "count(*)" */
- ExprList *pEList; /* List of columns to extract. */
+ ExprList *pEList = 0; /* List of columns to extract. */
SrcList *pTabList; /* List of tables to select from */
Expr *pWhere; /* The WHERE clause. May be NULL */
- ExprList *pOrderBy; /* The ORDER BY clause. May be NULL */
ExprList *pGroupBy; /* The GROUP BY clause. May be NULL */
Expr *pHaving; /* The HAVING clause. May be NULL */
- int isDistinct; /* True if the DISTINCT keyword is present */
- int distinct; /* Table to use for the distinct set */
int rc = 1; /* Value to return from this function */
- int addrSortIndex; /* Address of an OP_OpenEphemeral instruction */
- int addrDistinctIndex; /* Address of an OP_OpenEphemeral instruction */
+ DistinctCtx sDistinct; /* Info on how to code the DISTINCT keyword */
+ SortCtx sSort; /* Info on how to code the ORDER BY clause */
AggInfo sAggInfo; /* Information used by aggregate queries */
int iEnd; /* Address of the end of the query */
sqlite3 *db; /* The database connection */
@@ -96107,10 +119208,23 @@ SQLITE_PRIVATE int sqlite3Select(
}
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
memset(&sAggInfo, 0, sizeof(sAggInfo));
+#if SELECTTRACE_ENABLED
+ pParse->nSelectIndent++;
+ SELECTTRACE(1,pParse,p, ("begin processing:\n"));
+ if( sqlite3SelectTrace & 0x100 ){
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo );
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo );
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );
+ assert( p->pOrderBy==0 || pDest->eDest!=SRT_Queue );
if( IgnorableOrderby(pDest) ){
assert(pDest->eDest==SRT_Exists || pDest->eDest==SRT_Union ||
- pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard);
+ pDest->eDest==SRT_Except || pDest->eDest==SRT_Discard ||
+ pDest->eDest==SRT_Queue || pDest->eDest==SRT_DistFifo ||
+ pDest->eDest==SRT_DistQueue || pDest->eDest==SRT_Fifo);
/* If ORDER BY makes no difference in the output then neither does
** DISTINCT so it can be removed too. */
sqlite3ExprListDelete(db, p->pOrderBy);
@@ -96118,46 +119232,100 @@ SQLITE_PRIVATE int sqlite3Select(
p->selFlags &= ~SF_Distinct;
}
sqlite3SelectPrep(pParse, p, 0);
- pOrderBy = p->pOrderBy;
+ memset(&sSort, 0, sizeof(sSort));
+ sSort.pOrderBy = p->pOrderBy;
pTabList = p->pSrc;
- pEList = p->pEList;
if( pParse->nErr || db->mallocFailed ){
goto select_end;
}
+ assert( p->pEList!=0 );
isAgg = (p->selFlags & SF_Aggregate)!=0;
- assert( pEList!=0 );
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x100 ){
+ SELECTTRACE(0x100,pParse,p, ("after name resolution:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
- /* Begin generating code.
+ /* Try to flatten subqueries in the FROM clause up into the main query
*/
+#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
+ for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
+ struct SrcList_item *pItem = &pTabList->a[i];
+ Select *pSub = pItem->pSelect;
+ int isAggSub;
+ Table *pTab = pItem->pTab;
+ if( pSub==0 ) continue;
+
+ /* Catch mismatch in the declared columns of a view and the number of
+ ** columns in the SELECT on the RHS */
+ if( pTab->nCol!=pSub->pEList->nExpr ){
+ sqlite3ErrorMsg(pParse, "expected %d columns for '%s' but got %d",
+ pTab->nCol, pTab->zName, pSub->pEList->nExpr);
+ goto select_end;
+ }
+
+ isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
+ if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
+ /* This subquery can be absorbed into its parent. */
+ if( isAggSub ){
+ isAgg = 1;
+ p->selFlags |= SF_Aggregate;
+ }
+ i = -1;
+ }
+ pTabList = p->pSrc;
+ if( db->mallocFailed ) goto select_end;
+ if( !IgnorableOrderby(pDest) ){
+ sSort.pOrderBy = p->pOrderBy;
+ }
+ }
+#endif
+
+ /* Get a pointer the VDBE under construction, allocating a new VDBE if one
+ ** does not already exist */
v = sqlite3GetVdbe(pParse);
if( v==0 ) goto select_end;
- /* If writing to memory or generating a set
- ** only a single column may be output.
+#ifndef SQLITE_OMIT_COMPOUND_SELECT
+ /* Handle compound SELECT statements using the separate multiSelect()
+ ** procedure.
*/
-#ifndef SQLITE_OMIT_SUBQUERY
- if( checkForMultiColumnSelectError(pParse, pDest, pEList->nExpr) ){
- goto select_end;
+ if( p->pPrior ){
+ rc = multiSelect(pParse, p, pDest);
+ explainSetInteger(pParse->iSelectId, iRestoreSelectId);
+#if SELECTTRACE_ENABLED
+ SELECTTRACE(1,pParse,p,("end compound-select processing\n"));
+ pParse->nSelectIndent--;
+#endif
+ return rc;
}
#endif
/* Generate code for all sub-queries in the FROM clause
*/
#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
- for(i=0; !p->pPrior && i<pTabList->nSrc; i++){
+ for(i=0; i<pTabList->nSrc; i++){
struct SrcList_item *pItem = &pTabList->a[i];
SelectDest dest;
Select *pSub = pItem->pSelect;
- int isAggSub;
-
if( pSub==0 ) continue;
+
+ /* Sometimes the code for a subquery will be generated more than
+ ** once, if the subquery is part of the WHERE clause in a LEFT JOIN,
+ ** for example. In that case, do not regenerate the code to manifest
+ ** a view or the co-routine to implement a view. The first instance
+ ** is sufficient, though the subroutine to manifest the view does need
+ ** to be invoked again. */
if( pItem->addrFillSub ){
- sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
+ if( pItem->fg.viaCoroutine==0 ){
+ sqlite3VdbeAddOp2(v, OP_Gosub, pItem->regReturn, pItem->addrFillSub);
+ }
continue;
}
/* Increment Parse.nHeight by the height of the largest expression
- ** tree refered to by this, the parent select. The child select
+ ** tree referred to by this, the parent select. The child select
** may contain expression trees of at most
** (SQLITE_MAX_EXPR_DEPTH-Parse.nHeight) height. This is a bit
** more conservative than necessary, but much easier than enforcing
@@ -96165,14 +119333,57 @@ SQLITE_PRIVATE int sqlite3Select(
*/
pParse->nHeight += sqlite3SelectExprHeight(p);
- isAggSub = (pSub->selFlags & SF_Aggregate)!=0;
- if( flattenSubquery(pParse, p, i, isAgg, isAggSub) ){
- /* This subquery can be absorbed into its parent. */
- if( isAggSub ){
- isAgg = 1;
- p->selFlags |= SF_Aggregate;
+ /* Make copies of constant WHERE-clause terms in the outer query down
+ ** inside the subquery. This can help the subquery to run more efficiently.
+ */
+ if( (pItem->fg.jointype & JT_OUTER)==0
+ && pushDownWhereTerms(db, pSub, p->pWhere, pItem->iCursor)
+ ){
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x100 ){
+ SELECTTRACE(0x100,pParse,p,("After WHERE-clause push-down:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
}
- i = -1;
+#endif
+ }
+
+ /* Generate code to implement the subquery
+ **
+ ** The subquery is implemented as a co-routine if all of these are true:
+ ** (1) The subquery is guaranteed to be the outer loop (so that it
+ ** does not need to be computed more than once)
+ ** (2) The ALL keyword after SELECT is omitted. (Applications are
+ ** allowed to say "SELECT ALL" instead of just "SELECT" to disable
+ ** the use of co-routines.)
+ ** (3) Co-routines are not disabled using sqlite3_test_control()
+ ** with SQLITE_TESTCTRL_OPTIMIZATIONS.
+ **
+ ** TODO: Are there other reasons beside (1) to use a co-routine
+ ** implementation?
+ */
+ if( i==0
+ && (pTabList->nSrc==1
+ || (pTabList->a[1].fg.jointype&(JT_LEFT|JT_CROSS))!=0) /* (1) */
+ && (p->selFlags & SF_All)==0 /* (2) */
+ && OptimizationEnabled(db, SQLITE_SubqCoroutine) /* (3) */
+ ){
+ /* Implement a co-routine that will return a single row of the result
+ ** set on each invocation.
+ */
+ int addrTop = sqlite3VdbeCurrentAddr(v)+1;
+ pItem->regReturn = ++pParse->nMem;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, pItem->regReturn, 0, addrTop);
+ VdbeComment((v, "%s", pItem->pTab->zName));
+ pItem->addrFillSub = addrTop;
+ sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
+ explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
+ sqlite3Select(pParse, pSub, &dest);
+ pItem->pTab->nRowLogEst = pSub->nSelectRow;
+ pItem->fg.viaCoroutine = 1;
+ pItem->regResult = dest.iSdst;
+ sqlite3VdbeEndCoroutine(v, pItem->regReturn);
+ sqlite3VdbeJumpHere(v, addrTop-1);
+ sqlite3ClearTempRegCache(pParse);
}else{
/* Generate a subroutine that will fill an ephemeral table with
** the content of this subquery. pItem->addrFillSub will point
@@ -96186,77 +119397,45 @@ SQLITE_PRIVATE int sqlite3Select(
pItem->regReturn = ++pParse->nMem;
topAddr = sqlite3VdbeAddOp2(v, OP_Integer, 0, pItem->regReturn);
pItem->addrFillSub = topAddr+1;
- VdbeNoopComment((v, "materialize %s", pItem->pTab->zName));
- if( pItem->isCorrelated==0 && pParse->pTriggerTab==0 ){
- /* If the subquery is no correlated and if we are not inside of
+ if( pItem->fg.isCorrelated==0 ){
+ /* If the subquery is not correlated and if we are not inside of
** a trigger, then we only need to compute the value of the subquery
** once. */
- int regOnce = ++pParse->nMem;
- onceAddr = sqlite3VdbeAddOp1(v, OP_Once, regOnce);
+ onceAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
+ VdbeComment((v, "materialize \"%s\"", pItem->pTab->zName));
+ }else{
+ VdbeNoopComment((v, "materialize \"%s\"", pItem->pTab->zName));
}
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
sqlite3Select(pParse, pSub, &dest);
- pItem->pTab->nRowEst = (unsigned)pSub->nSelectRow;
+ pItem->pTab->nRowLogEst = pSub->nSelectRow;
if( onceAddr ) sqlite3VdbeJumpHere(v, onceAddr);
retAddr = sqlite3VdbeAddOp1(v, OP_Return, pItem->regReturn);
VdbeComment((v, "end %s", pItem->pTab->zName));
sqlite3VdbeChangeP1(v, topAddr, retAddr);
-
- }
- if( /*pParse->nErr ||*/ db->mallocFailed ){
- goto select_end;
+ sqlite3ClearTempRegCache(pParse);
}
+ if( db->mallocFailed ) goto select_end;
pParse->nHeight -= sqlite3SelectExprHeight(p);
- pTabList = p->pSrc;
- if( !IgnorableOrderby(pDest) ){
- pOrderBy = p->pOrderBy;
- }
}
- pEList = p->pEList;
#endif
+
+ /* Various elements of the SELECT copied into local variables for
+ ** convenience */
+ pEList = p->pEList;
pWhere = p->pWhere;
pGroupBy = p->pGroupBy;
pHaving = p->pHaving;
- isDistinct = (p->selFlags & SF_Distinct)!=0;
+ sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;
-#ifndef SQLITE_OMIT_COMPOUND_SELECT
- /* If there is are a sequence of queries, do the earlier ones first.
- */
- if( p->pPrior ){
- if( p->pRightmost==0 ){
- Select *pLoop, *pRight = 0;
- int cnt = 0;
- int mxSelect;
- for(pLoop=p; pLoop; pLoop=pLoop->pPrior, cnt++){
- pLoop->pRightmost = p;
- pLoop->pNext = pRight;
- pRight = pLoop;
- }
- mxSelect = db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT];
- if( mxSelect && cnt>mxSelect ){
- sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
- goto select_end;
- }
- }
- rc = multiSelect(pParse, p, pDest);
- explainSetInteger(pParse->iSelectId, iRestoreSelectId);
- return rc;
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x400 ){
+ SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
}
#endif
- /* If there is both a GROUP BY and an ORDER BY clause and they are
- ** identical, then disable the ORDER BY clause since the GROUP BY
- ** will cause elements to come out in the correct order. This is
- ** an optimization - the correct answer should result regardless.
- ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER
- ** to disable this optimization for testing purposes.
- */
- if( sqlite3ExprListCompare(p->pGroupBy, pOrderBy)==0
- && (db->flags & SQLITE_GroupByOrder)==0 ){
- pOrderBy = 0;
- }
-
/* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
** if the select-list is the same as the ORDER BY list, then this query
** can be rewritten as a GROUP BY. In other words, this:
@@ -96265,7 +119444,7 @@ SQLITE_PRIVATE int sqlite3Select(
**
** is transformed to:
**
- ** SELECT xyz FROM ... GROUP BY xyz
+ ** SELECT xyz FROM ... GROUP BY xyz ORDER BY xyz
**
** The second form is preferred as a single index (or temp-table) may be
** used for both the ORDER BY and DISTINCT processing. As originally
@@ -96273,133 +119452,117 @@ SQLITE_PRIVATE int sqlite3Select(
** BY and DISTINCT, and an index or separate temp-table for the other.
*/
if( (p->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct
- && sqlite3ExprListCompare(pOrderBy, p->pEList)==0
+ && sqlite3ExprListCompare(sSort.pOrderBy, pEList, -1)==0
){
p->selFlags &= ~SF_Distinct;
- p->pGroupBy = sqlite3ExprListDup(db, p->pEList, 0);
- pGroupBy = p->pGroupBy;
- pOrderBy = 0;
+ pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
+ /* Notice that even thought SF_Distinct has been cleared from p->selFlags,
+ ** the sDistinct.isTnct is still set. Hence, isTnct represents the
+ ** original setting of the SF_Distinct flag, not the current setting */
+ assert( sDistinct.isTnct );
+
+#if SELECTTRACE_ENABLED
+ if( sqlite3SelectTrace & 0x400 ){
+ SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
+ sqlite3TreeViewSelect(0, p, 0);
+ }
+#endif
}
- /* If there is an ORDER BY clause, then this sorting
- ** index might end up being unused if the data can be
- ** extracted in pre-sorted order. If that is the case, then the
- ** OP_OpenEphemeral instruction will be changed to an OP_Noop once
- ** we figure out that the sorting index is not needed. The addrSortIndex
- ** variable is used to facilitate that change.
+ /* If there is an ORDER BY clause, then create an ephemeral index to
+ ** do the sorting. But this sorting ephemeral index might end up
+ ** being unused if the data can be extracted in pre-sorted order.
+ ** If that is the case, then the OP_OpenEphemeral instruction will be
+ ** changed to an OP_Noop once we figure out that the sorting index is
+ ** not needed. The sSort.addrSortIndex variable is used to facilitate
+ ** that change.
*/
- if( pOrderBy ){
+ if( sSort.pOrderBy ){
KeyInfo *pKeyInfo;
- pKeyInfo = keyInfoFromExprList(pParse, pOrderBy);
- pOrderBy->iECursor = pParse->nTab++;
- p->addrOpenEphm[2] = addrSortIndex =
+ pKeyInfo = keyInfoFromExprList(pParse, sSort.pOrderBy, 0, pEList->nExpr);
+ sSort.iECursor = pParse->nTab++;
+ sSort.addrSortIndex =
sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
- pOrderBy->iECursor, pOrderBy->nExpr+2, 0,
- (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ sSort.iECursor, sSort.pOrderBy->nExpr+1+pEList->nExpr, 0,
+ (char*)pKeyInfo, P4_KEYINFO
+ );
}else{
- addrSortIndex = -1;
+ sSort.addrSortIndex = -1;
}
/* If the output is destined for a temporary table, open that table.
*/
if( pDest->eDest==SRT_EphemTab ){
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iParm, pEList->nExpr);
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pDest->iSDParm, pEList->nExpr);
}
/* Set the limiter.
*/
iEnd = sqlite3VdbeMakeLabel(v);
- p->nSelectRow = (double)LARGEST_INT64;
+ p->nSelectRow = 320; /* 4 billion rows */
computeLimitRegisters(pParse, p, iEnd);
- if( p->iLimit==0 && addrSortIndex>=0 ){
- sqlite3VdbeGetOp(v, addrSortIndex)->opcode = OP_SorterOpen;
- p->selFlags |= SF_UseSorter;
+ if( p->iLimit==0 && sSort.addrSortIndex>=0 ){
+ sqlite3VdbeChangeOpcode(v, sSort.addrSortIndex, OP_SorterOpen);
+ sSort.sortFlags |= SORTFLAG_UseSorter;
}
- /* Open a virtual index to use for the distinct set.
+ /* Open an ephemeral index to use for the distinct set.
*/
if( p->selFlags & SF_Distinct ){
- KeyInfo *pKeyInfo;
- distinct = pParse->nTab++;
- pKeyInfo = keyInfoFromExprList(pParse, p->pEList);
- addrDistinctIndex = sqlite3VdbeAddOp4(v, OP_OpenEphemeral, distinct, 0, 0,
- (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ sDistinct.tabTnct = pParse->nTab++;
+ sDistinct.addrTnct = sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
+ sDistinct.tabTnct, 0, 0,
+ (char*)keyInfoFromExprList(pParse, p->pEList,0,0),
+ P4_KEYINFO);
sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
+ sDistinct.eTnctType = WHERE_DISTINCT_UNORDERED;
}else{
- distinct = addrDistinctIndex = -1;
+ sDistinct.eTnctType = WHERE_DISTINCT_NOOP;
}
- /* Aggregate and non-aggregate queries are handled differently */
if( !isAgg && pGroupBy==0 ){
- ExprList *pDist = (isDistinct ? p->pEList : 0);
+ /* No aggregate functions and no GROUP BY clause */
+ u16 wctrlFlags = (sDistinct.isTnct ? WHERE_WANT_DISTINCT : 0);
+ assert( WHERE_USE_LIMIT==SF_FixedLimit );
+ wctrlFlags |= p->selFlags & SF_FixedLimit;
/* Begin the database scan. */
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pOrderBy, pDist, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, sSort.pOrderBy,
+ p->pEList, wctrlFlags, p->nSelectRow);
if( pWInfo==0 ) goto select_end;
- if( pWInfo->nRowOut < p->nSelectRow ) p->nSelectRow = pWInfo->nRowOut;
+ if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
+ p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
+ }
+ if( sDistinct.isTnct && sqlite3WhereIsDistinct(pWInfo) ){
+ sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo);
+ }
+ if( sSort.pOrderBy ){
+ sSort.nOBSat = sqlite3WhereIsOrdered(pWInfo);
+ sSort.bOrderedInnerLoop = sqlite3WhereOrderedInnerLoop(pWInfo);
+ if( sSort.nOBSat==sSort.pOrderBy->nExpr ){
+ sSort.pOrderBy = 0;
+ }
+ }
/* If sorting index that was created by a prior OP_OpenEphemeral
** instruction ended up not being needed, then change the OP_OpenEphemeral
** into an OP_Noop.
*/
- if( addrSortIndex>=0 && pOrderBy==0 ){
- sqlite3VdbeChangeToNoop(v, addrSortIndex);
- p->addrOpenEphm[2] = -1;
- }
-
- if( pWInfo->eDistinct ){
- VdbeOp *pOp; /* No longer required OpenEphemeral instr. */
-
- assert( addrDistinctIndex>=0 );
- pOp = sqlite3VdbeGetOp(v, addrDistinctIndex);
-
- assert( isDistinct );
- assert( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED
- || pWInfo->eDistinct==WHERE_DISTINCT_UNIQUE
- );
- distinct = -1;
- if( pWInfo->eDistinct==WHERE_DISTINCT_ORDERED ){
- int iJump;
- int iExpr;
- int iFlag = ++pParse->nMem;
- int iBase = pParse->nMem+1;
- int iBase2 = iBase + pEList->nExpr;
- pParse->nMem += (pEList->nExpr*2);
-
- /* Change the OP_OpenEphemeral coded earlier to an OP_Integer. The
- ** OP_Integer initializes the "first row" flag. */
- pOp->opcode = OP_Integer;
- pOp->p1 = 1;
- pOp->p2 = iFlag;
-
- sqlite3ExprCodeExprList(pParse, pEList, iBase, 1);
- iJump = sqlite3VdbeCurrentAddr(v) + 1 + pEList->nExpr + 1 + 1;
- sqlite3VdbeAddOp2(v, OP_If, iFlag, iJump-1);
- for(iExpr=0; iExpr<pEList->nExpr; iExpr++){
- CollSeq *pColl = sqlite3ExprCollSeq(pParse, pEList->a[iExpr].pExpr);
- sqlite3VdbeAddOp3(v, OP_Ne, iBase+iExpr, iJump, iBase2+iExpr);
- sqlite3VdbeChangeP4(v, -1, (const char *)pColl, P4_COLLSEQ);
- sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
- }
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iContinue);
-
- sqlite3VdbeAddOp2(v, OP_Integer, 0, iFlag);
- assert( sqlite3VdbeCurrentAddr(v)==iJump );
- sqlite3VdbeAddOp3(v, OP_Move, iBase, iBase2, pEList->nExpr);
- }else{
- pOp->opcode = OP_Noop;
- }
+ if( sSort.addrSortIndex>=0 && sSort.pOrderBy==0 ){
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
}
/* Use the standard inner loop. */
- selectInnerLoop(pParse, p, pEList, 0, 0, pOrderBy, distinct, pDest,
- pWInfo->iContinue, pWInfo->iBreak);
+ selectInnerLoop(pParse, p, pEList, -1, &sSort, &sDistinct, pDest,
+ sqlite3WhereContinueLabel(pWInfo),
+ sqlite3WhereBreakLabel(pWInfo));
/* End the database scan loop.
*/
sqlite3WhereEnd(pWInfo);
}else{
- /* This is the processing for aggregate queries */
+ /* This case when there exist aggregate functions or a GROUP BY clause
+ ** or both */
NameContext sNC; /* Name context for processing aggregate information */
int iAMem; /* First Mem address for storing current GROUP BY */
int iBMem; /* First Mem address for previous GROUP BY */
@@ -96411,6 +119574,7 @@ SQLITE_PRIVATE int sqlite3Select(
int addrEnd; /* End of processing for this SELECT */
int sortPTab = 0; /* Pseudotable used to decode sorting results */
int sortOut = 0; /* Output register from the sorter */
+ int orderByGrp = 0; /* True if the GROUP BY and ORDER BY are the same */
/* Remove any and all aliases between the result set and the
** GROUP BY clause.
@@ -96420,16 +119584,29 @@ SQLITE_PRIVATE int sqlite3Select(
struct ExprList_item *pItem; /* For looping over expression in a list */
for(k=p->pEList->nExpr, pItem=p->pEList->a; k>0; k--, pItem++){
- pItem->iAlias = 0;
+ pItem->u.x.iAlias = 0;
}
for(k=pGroupBy->nExpr, pItem=pGroupBy->a; k>0; k--, pItem++){
- pItem->iAlias = 0;
+ pItem->u.x.iAlias = 0;
}
- if( p->nSelectRow>(double)100 ) p->nSelectRow = (double)100;
+ assert( 66==sqlite3LogEst(100) );
+ if( p->nSelectRow>66 ) p->nSelectRow = 66;
}else{
- p->nSelectRow = (double)1;
+ assert( 0==sqlite3LogEst(1) );
+ p->nSelectRow = 0;
}
+ /* If there is both a GROUP BY and an ORDER BY clause and they are
+ ** identical, then it may be possible to disable the ORDER BY clause
+ ** on the grounds that the GROUP BY will cause elements to come out
+ ** in the correct order. It also may not - the GROUP BY might use a
+ ** database index that causes rows to be grouped together as required
+ ** but not actually sorted. Either way, record the fact that the
+ ** ORDER BY and GROUP BY clauses are the same by setting the orderByGrp
+ ** variable. */
+ if( sqlite3ExprListCompare(pGroupBy, sSort.pOrderBy, -1)==0 ){
+ orderByGrp = 1;
+ }
/* Create a label to jump to when we want to abort the query */
addrEnd = sqlite3VdbeMakeLabel(v);
@@ -96442,18 +119619,22 @@ SQLITE_PRIVATE int sqlite3Select(
sNC.pParse = pParse;
sNC.pSrcList = pTabList;
sNC.pAggInfo = &sAggInfo;
- sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr+1 : 0;
+ sAggInfo.mnReg = pParse->nMem+1;
+ sAggInfo.nSortingColumn = pGroupBy ? pGroupBy->nExpr : 0;
sAggInfo.pGroupBy = pGroupBy;
sqlite3ExprAnalyzeAggList(&sNC, pEList);
- sqlite3ExprAnalyzeAggList(&sNC, pOrderBy);
+ sqlite3ExprAnalyzeAggList(&sNC, sSort.pOrderBy);
if( pHaving ){
sqlite3ExprAnalyzeAggregates(&sNC, pHaving);
}
sAggInfo.nAccumulator = sAggInfo.nColumn;
for(i=0; i<sAggInfo.nFunc; i++){
assert( !ExprHasProperty(sAggInfo.aFunc[i].pExpr, EP_xIsSelect) );
+ sNC.ncFlags |= NC_InAggFunc;
sqlite3ExprAnalyzeAggList(&sNC, sAggInfo.aFunc[i].pExpr->x.pList);
+ sNC.ncFlags &= ~NC_InAggFunc;
}
+ sAggInfo.mxReg = pParse->nMem;
if( db->mallocFailed ) goto select_end;
/* Processing for aggregates with GROUP BY is very different and
@@ -96461,7 +119642,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/
if( pGroupBy ){
KeyInfo *pKeyInfo; /* Keying information for the group by clause */
- int j1; /* A-vs-B comparision jump */
+ int addr1; /* A-vs-B comparision jump */
int addrOutputRow; /* Start of subroutine that outputs a result row */
int regOutputRow; /* Return address register for output subroutine */
int addrSetAbort; /* Set the abort flag and return */
@@ -96476,10 +119657,10 @@ SQLITE_PRIVATE int sqlite3Select(
** will be converted into a Noop.
*/
sAggInfo.sortingIdx = pParse->nTab++;
- pKeyInfo = keyInfoFromExprList(pParse, pGroupBy);
+ pKeyInfo = keyInfoFromExprList(pParse, pGroupBy, 0, sAggInfo.nColumn);
addrSortingIdx = sqlite3VdbeAddOp4(v, OP_SorterOpen,
sAggInfo.sortingIdx, sAggInfo.nSortingColumn,
- 0, (char*)pKeyInfo, P4_KEYINFO_HANDOFF);
+ 0, (char*)pKeyInfo, P4_KEYINFO);
/* Initialize memory locations used by GROUP BY aggregate processing
*/
@@ -96497,6 +119678,7 @@ SQLITE_PRIVATE int sqlite3Select(
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
VdbeComment((v, "indicate accumulator empty"));
+ sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
@@ -96504,14 +119686,15 @@ SQLITE_PRIVATE int sqlite3Select(
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pGroupBy, 0, 0);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pGroupBy, 0,
+ WHERE_GROUPBY | (orderByGrp ? WHERE_SORTBYGROUP : 0), 0
+ );
if( pWInfo==0 ) goto select_end;
- if( pGroupBy==0 ){
+ if( sqlite3WhereIsOrdered(pWInfo)==pGroupBy->nExpr ){
/* The optimizer is able to deliver rows in group by order so
** we do not have to sort. The OP_OpenEphemeral table will be
** cancelled later because we still need to use the pKeyInfo
*/
- pGroupBy = p->pGroupBy;
groupBySort = 0;
}else{
/* Rows are coming out in undetermined order. We have to push
@@ -96525,12 +119708,13 @@ SQLITE_PRIVATE int sqlite3Select(
int nGroupBy;
explainTempTable(pParse,
- isDistinct && !(p->selFlags&SF_Distinct)?"DISTINCT":"GROUP BY");
+ (sDistinct.isTnct && (p->selFlags&SF_Distinct)==0) ?
+ "DISTINCT" : "GROUP BY");
groupBySort = 1;
nGroupBy = pGroupBy->nExpr;
- nCol = nGroupBy + 1;
- j = nGroupBy+1;
+ nCol = nGroupBy;
+ j = nGroupBy;
for(i=0; i<sAggInfo.nColumn; i++){
if( sAggInfo.aCol[i].iSorterColumn>=j ){
nCol++;
@@ -96539,20 +119723,14 @@ SQLITE_PRIVATE int sqlite3Select(
}
regBase = sqlite3GetTempRange(pParse, nCol);
sqlite3ExprCacheClear(pParse);
- sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0);
- sqlite3VdbeAddOp2(v, OP_Sequence, sAggInfo.sortingIdx,regBase+nGroupBy);
- j = nGroupBy+1;
+ sqlite3ExprCodeExprList(pParse, pGroupBy, regBase, 0, 0);
+ j = nGroupBy;
for(i=0; i<sAggInfo.nColumn; i++){
struct AggInfo_col *pCol = &sAggInfo.aCol[i];
if( pCol->iSorterColumn>=j ){
int r1 = j + regBase;
- int r2;
-
- r2 = sqlite3ExprCodeGetColumn(pParse,
+ sqlite3ExprCodeGetColumnToReg(pParse,
pCol->pTab, pCol->iColumn, pCol->iTable, r1);
- if( r1!=r2 ){
- sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1);
- }
j++;
}
}
@@ -96566,9 +119744,24 @@ SQLITE_PRIVATE int sqlite3Select(
sortOut = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_OpenPseudo, sortPTab, sortOut, nCol);
sqlite3VdbeAddOp2(v, OP_SorterSort, sAggInfo.sortingIdx, addrEnd);
- VdbeComment((v, "GROUP BY sort"));
+ VdbeComment((v, "GROUP BY sort")); VdbeCoverage(v);
sAggInfo.useSortingIdx = 1;
sqlite3ExprCacheClear(pParse);
+
+ }
+
+ /* If the index or temporary table used by the GROUP BY sort
+ ** will naturally deliver rows in the order required by the ORDER BY
+ ** clause, cancel the ephemeral table open coded earlier.
+ **
+ ** This is an optimization - the correct answer should result regardless.
+ ** Use the SQLITE_GroupByOrder flag with SQLITE_TESTCTRL_OPTIMIZER to
+ ** disable this optimization for testing purposes. */
+ if( orderByGrp && OptimizationEnabled(db, SQLITE_GroupByOrder)
+ && (groupBySort || sqlite3WhereIsSorted(pWInfo))
+ ){
+ sSort.pOrderBy = 0;
+ sqlite3VdbeChangeToNoop(v, sSort.addrSortIndex);
}
/* Evaluate the current GROUP BY terms and store in b0, b1, b2...
@@ -96579,21 +119772,21 @@ SQLITE_PRIVATE int sqlite3Select(
addrTopOfLoop = sqlite3VdbeCurrentAddr(v);
sqlite3ExprCacheClear(pParse);
if( groupBySort ){
- sqlite3VdbeAddOp2(v, OP_SorterData, sAggInfo.sortingIdx, sortOut);
+ sqlite3VdbeAddOp3(v, OP_SorterData, sAggInfo.sortingIdx,
+ sortOut, sortPTab);
}
for(j=0; j<pGroupBy->nExpr; j++){
if( groupBySort ){
sqlite3VdbeAddOp3(v, OP_Column, sortPTab, j, iBMem+j);
- if( j==0 ) sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE);
}else{
sAggInfo.directMode = 1;
sqlite3ExprCode(pParse, pGroupBy->a[j].pExpr, iBMem+j);
}
}
sqlite3VdbeAddOp4(v, OP_Compare, iAMem, iBMem, pGroupBy->nExpr,
- (char*)pKeyInfo, P4_KEYINFO);
- j1 = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp3(v, OP_Jump, j1+1, 0, j1+1);
+ (char*)sqlite3KeyInfoRef(pKeyInfo), P4_KEYINFO);
+ addr1 = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp3(v, OP_Jump, addr1+1, 0, addr1+1); VdbeCoverage(v);
/* Generate code that runs whenever the GROUP BY changes.
** Changes in the GROUP BY are detected by the previous code
@@ -96607,7 +119800,7 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3ExprCodeMove(pParse, iBMem, iAMem, pGroupBy->nExpr);
sqlite3VdbeAddOp2(v, OP_Gosub, regOutputRow, addrOutputRow);
VdbeComment((v, "output one row"));
- sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd);
+ sqlite3VdbeAddOp2(v, OP_IfPos, iAbortFlag, addrEnd); VdbeCoverage(v);
VdbeComment((v, "check abort flag"));
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
VdbeComment((v, "reset accumulator"));
@@ -96615,7 +119808,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Update the aggregate accumulators based on the content of
** the current row
*/
- sqlite3VdbeJumpHere(v, j1);
+ sqlite3VdbeJumpHere(v, addr1);
updateAccumulator(pParse, &sAggInfo);
sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
VdbeComment((v, "indicate data in accumulator"));
@@ -96624,6 +119817,7 @@ SQLITE_PRIVATE int sqlite3Select(
*/
if( groupBySort ){
sqlite3VdbeAddOp2(v, OP_SorterNext, sAggInfo.sortingIdx, addrTopOfLoop);
+ VdbeCoverage(v);
}else{
sqlite3WhereEnd(pWInfo);
sqlite3VdbeChangeToNoop(v, addrSortingIdx);
@@ -96636,7 +119830,7 @@ SQLITE_PRIVATE int sqlite3Select(
/* Jump over the subroutines
*/
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addrEnd);
+ sqlite3VdbeGoto(v, addrEnd);
/* Generate a subroutine that outputs a single row of the result
** set. This subroutine first looks at the iUseFlag. If iUseFlag
@@ -96652,12 +119846,13 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3VdbeResolveLabel(v, addrOutputRow);
addrOutputRow = sqlite3VdbeCurrentAddr(v);
sqlite3VdbeAddOp2(v, OP_IfPos, iUseFlag, addrOutputRow+2);
+ VdbeCoverage(v);
VdbeComment((v, "Groupby result generator entry point"));
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
finalizeAggFunctions(pParse, &sAggInfo);
sqlite3ExprIfFalse(pParse, pHaving, addrOutputRow+1, SQLITE_JUMPIFNULL);
- selectInnerLoop(pParse, p, p->pEList, 0, 0, pOrderBy,
- distinct, pDest,
+ selectInnerLoop(pParse, p, p->pEList, -1, &sSort,
+ &sDistinct, pDest,
addrOutputRow+1, addrSetAbort);
sqlite3VdbeAddOp1(v, OP_Return, regOutputRow);
VdbeComment((v, "end groupby result generator"));
@@ -96697,33 +119892,34 @@ SQLITE_PRIVATE int sqlite3Select(
sqlite3CodeVerifySchema(pParse, iDb);
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
- /* Search for the index that has the least amount of columns. If
- ** there is such an index, and it has less columns than the table
- ** does, then we can assume that it consumes less space on disk and
- ** will therefore be cheaper to scan to determine the query result.
- ** In this case set iRoot to the root page number of the index b-tree
- ** and pKeyInfo to the KeyInfo structure required to navigate the
- ** index.
+ /* Search for the index that has the lowest scan cost.
**
** (2011-04-15) Do not do a full scan of an unordered index.
**
+ ** (2013-10-03) Do not count the entries in a partial index.
+ **
** In practice the KeyInfo structure will not be used. It is only
** passed to keep OP_OpenRead happy.
*/
+ if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab);
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->bUnordered==0 && (!pBest || pIdx->nColumn<pBest->nColumn) ){
+ if( pIdx->bUnordered==0
+ && pIdx->szIdxRow<pTab->szTabRow
+ && pIdx->pPartIdxWhere==0
+ && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
+ ){
pBest = pIdx;
}
}
- if( pBest && pBest->nColumn<pTab->nCol ){
+ if( pBest ){
iRoot = pBest->tnum;
- pKeyInfo = sqlite3IndexKeyinfo(pParse, pBest);
+ pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest);
}
/* Open a read-only cursor, execute the OP_Count, close the cursor. */
- sqlite3VdbeAddOp3(v, OP_OpenRead, iCsr, iRoot, iDb);
+ sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1);
if( pKeyInfo ){
- sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO_HANDOFF);
+ sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
}
sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
@@ -96745,7 +119941,7 @@ SQLITE_PRIVATE int sqlite3Select(
** value of x, the only row required).
**
** A special flag must be passed to sqlite3WhereBegin() to slightly
- ** modify behaviour as follows:
+ ** modify behavior as follows:
**
** + If the query is a "SELECT min(x)", then the loop coded by
** where.c should not iterate over any values with a NULL value
@@ -96757,12 +119953,20 @@ SQLITE_PRIVATE int sqlite3Select(
** Refer to code and comments in where.c for details.
*/
ExprList *pMinMax = 0;
- u8 flag = minMaxQuery(p);
+ u8 flag = WHERE_ORDERBY_NORMAL;
+
+ assert( p->pGroupBy==0 );
+ assert( flag==0 );
+ if( p->pHaving==0 ){
+ flag = minMaxQuery(&sAggInfo, &pMinMax);
+ }
+ assert( flag==0 || (pMinMax!=0 && pMinMax->nExpr==1) );
+
if( flag ){
- assert( !ExprHasProperty(p->pEList->a[0].pExpr, EP_xIsSelect) );
- pMinMax = sqlite3ExprListDup(db, p->pEList->a[0].pExpr->x.pList,0);
+ pMinMax = sqlite3ExprListDup(db, pMinMax, 0);
pDel = pMinMax;
- if( pMinMax && !db->mallocFailed ){
+ assert( db->mallocFailed || pMinMax!=0 );
+ if( !db->mallocFailed ){
pMinMax->a[0].sortOrder = flag!=WHERE_ORDERBY_MIN ?1:0;
pMinMax->a[0].pExpr->op = TK_COLUMN;
}
@@ -96773,14 +119977,15 @@ SQLITE_PRIVATE int sqlite3Select(
** of output.
*/
resetAccumulator(pParse, &sAggInfo);
- pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, &pMinMax, 0, flag);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMax,0,flag,0);
if( pWInfo==0 ){
sqlite3ExprListDelete(db, pDel);
goto select_end;
}
updateAccumulator(pParse, &sAggInfo);
- if( !pMinMax && flag ){
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pWInfo->iBreak);
+ assert( pMinMax==0 || pMinMax->nExpr==1 );
+ if( sqlite3WhereIsOrdered(pWInfo)>0 ){
+ sqlite3VdbeGoto(v, sqlite3WhereBreakLabel(pWInfo));
VdbeComment((v, "%s() by index",
(flag==WHERE_ORDERBY_MIN?"min":"max")));
}
@@ -96788,9 +119993,9 @@ SQLITE_PRIVATE int sqlite3Select(
finalizeAggFunctions(pParse, &sAggInfo);
}
- pOrderBy = 0;
+ sSort.pOrderBy = 0;
sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
- selectInnerLoop(pParse, p, p->pEList, 0, 0, 0, -1,
+ selectInnerLoop(pParse, p, p->pEList, -1, 0, 0,
pDest, addrEnd, addrEnd);
sqlite3ExprListDelete(db, pDel);
}
@@ -96798,26 +120003,26 @@ SQLITE_PRIVATE int sqlite3Select(
} /* endif aggregate query */
- if( distinct>=0 ){
+ if( sDistinct.eTnctType==WHERE_DISTINCT_UNORDERED ){
explainTempTable(pParse, "DISTINCT");
}
/* If there is an ORDER BY clause, then we need to sort the results
** and send them to the callback one by one.
*/
- if( pOrderBy ){
- explainTempTable(pParse, "ORDER BY");
- generateSortTail(pParse, p, v, pEList->nExpr, pDest);
+ if( sSort.pOrderBy ){
+ explainTempTable(pParse,
+ sSort.nOBSat>0 ? "RIGHT PART OF ORDER BY":"ORDER BY");
+ generateSortTail(pParse, p, &sSort, pEList->nExpr, pDest);
}
/* Jump here to skip this query
*/
sqlite3VdbeResolveLabel(v, iEnd);
- /* The SELECT was successfully coded. Set the return code to 0
- ** to indicate no errors.
- */
- rc = 0;
+ /* The SELECT has been coded. If there is an error in the Parse structure,
+ ** set the return code to 1. Otherwise 0. */
+ rc = (pParse->nErr>0);
/* Control jumps to here if an error is encountered above, or upon
** successful coding of the SELECT.
@@ -96833,105 +120038,13 @@ select_end:
sqlite3DbFree(db, sAggInfo.aCol);
sqlite3DbFree(db, sAggInfo.aFunc);
+#if SELECTTRACE_ENABLED
+ SELECTTRACE(1,pParse,p,("end processing\n"));
+ pParse->nSelectIndent--;
+#endif
return rc;
}
-#if defined(SQLITE_DEBUG)
-/*
-*******************************************************************************
-** The following code is used for testing and debugging only. The code
-** that follows does not appear in normal builds.
-**
-** These routines are used to print out the content of all or part of a
-** parse structures such as Select or Expr. Such printouts are useful
-** for helping to understand what is happening inside the code generator
-** during the execution of complex SELECT statements.
-**
-** These routine are not called anywhere from within the normal
-** code base. Then are intended to be called from within the debugger
-** or from temporary "printf" statements inserted for debugging.
-*/
-SQLITE_PRIVATE void sqlite3PrintExpr(Expr *p){
- if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
- sqlite3DebugPrintf("(%s", p->u.zToken);
- }else{
- sqlite3DebugPrintf("(%d", p->op);
- }
- if( p->pLeft ){
- sqlite3DebugPrintf(" ");
- sqlite3PrintExpr(p->pLeft);
- }
- if( p->pRight ){
- sqlite3DebugPrintf(" ");
- sqlite3PrintExpr(p->pRight);
- }
- sqlite3DebugPrintf(")");
-}
-SQLITE_PRIVATE void sqlite3PrintExprList(ExprList *pList){
- int i;
- for(i=0; i<pList->nExpr; i++){
- sqlite3PrintExpr(pList->a[i].pExpr);
- if( i<pList->nExpr-1 ){
- sqlite3DebugPrintf(", ");
- }
- }
-}
-SQLITE_PRIVATE void sqlite3PrintSelect(Select *p, int indent){
- sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
- sqlite3PrintExprList(p->pEList);
- sqlite3DebugPrintf("\n");
- if( p->pSrc ){
- char *zPrefix;
- int i;
- zPrefix = "FROM";
- for(i=0; i<p->pSrc->nSrc; i++){
- struct SrcList_item *pItem = &p->pSrc->a[i];
- sqlite3DebugPrintf("%*s ", indent+6, zPrefix);
- zPrefix = "";
- if( pItem->pSelect ){
- sqlite3DebugPrintf("(\n");
- sqlite3PrintSelect(pItem->pSelect, indent+10);
- sqlite3DebugPrintf("%*s)", indent+8, "");
- }else if( pItem->zName ){
- sqlite3DebugPrintf("%s", pItem->zName);
- }
- if( pItem->pTab ){
- sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
- }
- if( pItem->zAlias ){
- sqlite3DebugPrintf(" AS %s", pItem->zAlias);
- }
- if( i<p->pSrc->nSrc-1 ){
- sqlite3DebugPrintf(",");
- }
- sqlite3DebugPrintf("\n");
- }
- }
- if( p->pWhere ){
- sqlite3DebugPrintf("%*s WHERE ", indent, "");
- sqlite3PrintExpr(p->pWhere);
- sqlite3DebugPrintf("\n");
- }
- if( p->pGroupBy ){
- sqlite3DebugPrintf("%*s GROUP BY ", indent, "");
- sqlite3PrintExprList(p->pGroupBy);
- sqlite3DebugPrintf("\n");
- }
- if( p->pHaving ){
- sqlite3DebugPrintf("%*s HAVING ", indent, "");
- sqlite3PrintExpr(p->pHaving);
- sqlite3DebugPrintf("\n");
- }
- if( p->pOrderBy ){
- sqlite3DebugPrintf("%*s ORDER BY ", indent, "");
- sqlite3PrintExprList(p->pOrderBy);
- sqlite3DebugPrintf("\n");
- }
-}
-/* End of the structure debug printing code
-*****************************************************************************/
-#endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
-
/************** End of select.c **********************************************/
/************** Begin file table.c *******************************************/
/*
@@ -96952,6 +120065,7 @@ SQLITE_PRIVATE void sqlite3PrintSelect(Select *p, int indent){
** These routines are in a separate files so that they will not be linked
** if they are not used.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
/* #include <string.h> */
@@ -96964,10 +120078,10 @@ SQLITE_PRIVATE void sqlite3PrintSelect(Select *p, int indent){
typedef struct TabResult {
char **azResult; /* Accumulated output */
char *zErrMsg; /* Error message text, if an error occurs */
- int nAlloc; /* Slots allocated for azResult[] */
- int nRow; /* Number of rows in the result */
- int nColumn; /* Number of columns in the result */
- int nData; /* Slots used in azResult[]. (nRow+1)*nColumn */
+ u32 nAlloc; /* Slots allocated for azResult[] */
+ u32 nRow; /* Number of rows in the result */
+ u32 nColumn; /* Number of columns in the result */
+ u32 nData; /* Slots used in azResult[]. (nRow+1)*nColumn */
int rc; /* Return code from sqlite3_exec() */
} TabResult;
@@ -96993,7 +120107,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
if( p->nData + need > p->nAlloc ){
char **azNew;
p->nAlloc = p->nAlloc*2 + need;
- azNew = sqlite3_realloc( p->azResult, sizeof(char*)*p->nAlloc );
+ azNew = sqlite3_realloc64( p->azResult, sizeof(char*)*p->nAlloc );
if( azNew==0 ) goto malloc_failed;
p->azResult = azNew;
}
@@ -97008,7 +120122,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
if( z==0 ) goto malloc_failed;
p->azResult[p->nData++] = z;
}
- }else if( p->nColumn!=nCol ){
+ }else if( (int)p->nColumn!=nCol ){
sqlite3_free(p->zErrMsg);
p->zErrMsg = sqlite3_mprintf(
"sqlite3_get_table() called with two or more incompatible queries"
@@ -97025,7 +120139,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
z = 0;
}else{
int n = sqlite3Strlen30(argv[i])+1;
- z = sqlite3_malloc( n );
+ z = sqlite3_malloc64( n );
if( z==0 ) goto malloc_failed;
memcpy(z, argv[i], n);
}
@@ -97036,7 +120150,7 @@ static int sqlite3_get_table_cb(void *pArg, int nCol, char **argv, char **colv){
return 0;
malloc_failed:
- p->rc = SQLITE_NOMEM;
+ p->rc = SQLITE_NOMEM_BKPT;
return 1;
}
@@ -97061,6 +120175,9 @@ SQLITE_API int sqlite3_get_table(
int rc;
TabResult res;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || pazResult==0 ) return SQLITE_MISUSE_BKPT;
+#endif
*pazResult = 0;
if( pnColumn ) *pnColumn = 0;
if( pnRow ) *pnRow = 0;
@@ -97071,10 +120188,10 @@ SQLITE_API int sqlite3_get_table(
res.nData = 1;
res.nAlloc = 20;
res.rc = SQLITE_OK;
- res.azResult = sqlite3_malloc(sizeof(char*)*res.nAlloc );
+ res.azResult = sqlite3_malloc64(sizeof(char*)*res.nAlloc );
if( res.azResult==0 ){
db->errCode = SQLITE_NOMEM;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
res.azResult[0] = 0;
rc = sqlite3_exec(db, zSql, sqlite3_get_table_cb, &res, pzErrMsg);
@@ -97099,11 +120216,11 @@ SQLITE_API int sqlite3_get_table(
}
if( res.nAlloc>res.nData ){
char **azNew;
- azNew = sqlite3_realloc( res.azResult, sizeof(char*)*res.nData );
+ azNew = sqlite3_realloc64( res.azResult, sizeof(char*)*res.nData );
if( azNew==0 ){
sqlite3_free_table(&res.azResult[1]);
db->errCode = SQLITE_NOMEM;
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
res.azResult = azNew;
}
@@ -97117,7 +120234,7 @@ SQLITE_API int sqlite3_get_table(
** This routine frees the space the sqlite3_get_table() malloced.
*/
SQLITE_API void sqlite3_free_table(
- char **azResult /* Result returned from from sqlite3_get_table() */
+ char **azResult /* Result returned from sqlite3_get_table() */
){
if( azResult ){
int i, n;
@@ -97145,6 +120262,7 @@ SQLITE_API void sqlite3_free_table(
*************************************************************************
** This file contains the implementation for TRIGGERs
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_TRIGGER
/*
@@ -97230,7 +120348,6 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
int iDb; /* The database to store the trigger in */
Token *pName; /* The unqualified db name */
DbFixer sFix; /* State vector for the DB fixer */
- int iTabDb; /* Index of the database holding pTab */
assert( pName1!=0 ); /* pName1->z might be NULL, but not pName1 itself */
assert( pName2!=0 );
@@ -97245,7 +120362,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
iDb = 1;
pName = pName1;
}else{
- /* Figure out the db that the the trigger will be created in */
+ /* Figure out the db that the trigger will be created in */
iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pName);
if( iDb<0 ){
goto trigger_cleanup;
@@ -97261,7 +120378,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
** ^^^^^^^^
**
** To maintain backwards compatibility, ignore the database
- ** name on pTableName if we are reparsing our of SQLITE_MASTER.
+ ** name on pTableName if we are reparsing out of SQLITE_MASTER.
*/
if( db->init.busy && iDb!=1 ){
sqlite3DbFree(db, pTableName->a[0].zDatabase);
@@ -97282,8 +120399,8 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
/* Ensure the table name matches database name and that the table exists */
if( db->mallocFailed ) goto trigger_cleanup;
assert( pTableName->nSrc==1 );
- if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName) &&
- sqlite3FixSrcList(&sFix, pTableName) ){
+ sqlite3FixInit(&sFix, pParse, iDb, "trigger", pName);
+ if( sqlite3FixSrcList(&sFix, pTableName) ){
goto trigger_cleanup;
}
pTab = sqlite3SrcListLookup(pParse, pTableName);
@@ -97314,8 +120431,7 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
goto trigger_cleanup;
}
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),
- zName, sqlite3Strlen30(zName)) ){
+ if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash),zName) ){
if( !noErr ){
sqlite3ErrorMsg(pParse, "trigger %T already exists", pName);
}else{
@@ -97328,7 +120444,6 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
/* Do not create a trigger on a system table */
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0 ){
sqlite3ErrorMsg(pParse, "cannot create trigger on system table");
- pParse->nErr++;
goto trigger_cleanup;
}
@@ -97345,13 +120460,13 @@ SQLITE_PRIVATE void sqlite3BeginTrigger(
" trigger on table: %S", pTableName, 0);
goto trigger_cleanup;
}
- iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
#ifndef SQLITE_OMIT_AUTHORIZATION
{
+ int iTabDb = sqlite3SchemaToIndex(db, pTab->pSchema);
int code = SQLITE_CREATE_TRIGGER;
- const char *zDb = db->aDb[iTabDb].zName;
- const char *zDbTrig = isTemp ? db->aDb[1].zName : zDb;
+ const char *zDb = db->aDb[iTabDb].zDbSName;
+ const char *zDbTrig = isTemp ? db->aDb[1].zDbSName : zDb;
if( iTabDb==1 || isTemp ) code = SQLITE_CREATE_TEMP_TRIGGER;
if( sqlite3AuthCheck(pParse, code, zName, pTab->zName, zDbTrig) ){
goto trigger_cleanup;
@@ -97423,10 +120538,11 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
pStepList->pTrig = pTrig;
pStepList = pStepList->pNext;
}
- nameToken.z = pTrig->zName;
- nameToken.n = sqlite3Strlen30(nameToken.z);
- if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken)
- && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){
+ sqlite3TokenInit(&nameToken, pTrig->zName);
+ sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken);
+ if( sqlite3FixTriggerStep(&sFix, pTrig->step_list)
+ || sqlite3FixExpr(&sFix, pTrig->pWhen)
+ ){
goto triggerfinish_cleanup;
}
@@ -97444,7 +120560,7 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n);
sqlite3NestedParse(pParse,
"INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb), zName,
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), zName,
pTrig->table, z);
sqlite3DbFree(db, z);
sqlite3ChangeCookie(pParse, iDb);
@@ -97456,13 +120572,12 @@ SQLITE_PRIVATE void sqlite3FinishTrigger(
Trigger *pLink = pTrig;
Hash *pHash = &db->aDb[iDb].pSchema->trigHash;
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
- pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig);
+ pTrig = sqlite3HashInsert(pHash, zName, pTrig);
if( pTrig ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
}else if( pLink->pSchema==pLink->pTabSchema ){
Table *pTab;
- int n = sqlite3Strlen30(pLink->table);
- pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table, n);
+ pTab = sqlite3HashFind(&pLink->pTabSchema->tblHash, pLink->table);
assert( pTab!=0 );
pLink->pNext = pTab->pTrigger;
pTab->pTrigger = pLink;
@@ -97507,12 +120622,12 @@ static TriggerStep *triggerStepAllocate(
){
TriggerStep *pTriggerStep;
- pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n);
+ pTriggerStep = sqlite3DbMallocZero(db, sizeof(TriggerStep) + pName->n + 1);
if( pTriggerStep ){
char *z = (char*)&pTriggerStep[1];
memcpy(z, pName->z, pName->n);
- pTriggerStep->target.z = z;
- pTriggerStep->target.n = pName->n;
+ sqlite3Dequote(z);
+ pTriggerStep->zTarget = z;
pTriggerStep->op = op;
}
return pTriggerStep;
@@ -97529,25 +120644,21 @@ SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(
sqlite3 *db, /* The database connection */
Token *pTableName, /* Name of the table into which we insert */
IdList *pColumn, /* List of columns in pTableName to insert into */
- ExprList *pEList, /* The VALUE clause: a list of values to be inserted */
Select *pSelect, /* A SELECT statement that supplies values */
u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */
){
TriggerStep *pTriggerStep;
- assert(pEList == 0 || pSelect == 0);
- assert(pEList != 0 || pSelect != 0 || db->mallocFailed);
+ assert(pSelect != 0 || db->mallocFailed);
pTriggerStep = triggerStepAllocate(db, TK_INSERT, pTableName);
if( pTriggerStep ){
pTriggerStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE);
pTriggerStep->pIdList = pColumn;
- pTriggerStep->pExprList = sqlite3ExprListDup(db, pEList, EXPRDUP_REDUCE);
pTriggerStep->orconf = orconf;
}else{
sqlite3IdListDelete(db, pColumn);
}
- sqlite3ExprListDelete(db, pEList);
sqlite3SelectDelete(db, pSelect);
return pTriggerStep;
@@ -97625,7 +120736,6 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr)
int i;
const char *zDb;
const char *zName;
- int nName;
sqlite3 *db = pParse->db;
if( db->mallocFailed ) goto drop_trigger_cleanup;
@@ -97636,13 +120746,12 @@ SQLITE_PRIVATE void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr)
assert( pName->nSrc==1 );
zDb = pName->a[0].zDatabase;
zName = pName->a[0].zName;
- nName = sqlite3Strlen30(zName);
assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) );
for(i=OMIT_TEMPDB; i<db->nDb; i++){
int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */
- if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue;
+ if( zDb && sqlite3StrICmp(db->aDb[j].zDbSName, zDb) ) continue;
assert( sqlite3SchemaMutexHeld(db, j, 0) );
- pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName);
+ pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName);
if( pTrigger ) break;
}
if( !pTrigger ){
@@ -97665,8 +120774,7 @@ drop_trigger_cleanup:
** is set on.
*/
static Table *tableOfTrigger(Trigger *pTrigger){
- int n = sqlite3Strlen30(pTrigger->table);
- return sqlite3HashFind(&pTrigger->pTabSchema->tblHash, pTrigger->table, n);
+ return sqlite3HashFind(&pTrigger->pTabSchema->tblHash, pTrigger->table);
}
@@ -97687,7 +120795,7 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
#ifndef SQLITE_OMIT_AUTHORIZATION
{
int code = SQLITE_DROP_TRIGGER;
- const char *zDb = db->aDb[iDb].zName;
+ const char *zDb = db->aDb[iDb].zDbSName;
const char *zTab = SCHEMA_TABLE(iDb);
if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER;
if( sqlite3AuthCheck(pParse, code, pTrigger->zName, pTable->zName, zDb) ||
@@ -97701,30 +120809,12 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){
*/
assert( pTable!=0 );
if( (v = sqlite3GetVdbe(pParse))!=0 ){
- int base;
- static const VdbeOpList dropTrigger[] = {
- { OP_Rewind, 0, ADDR(9), 0},
- { OP_String8, 0, 1, 0}, /* 1 */
- { OP_Column, 0, 1, 2},
- { OP_Ne, 2, ADDR(8), 1},
- { OP_String8, 0, 1, 0}, /* 4: "trigger" */
- { OP_Column, 0, 0, 2},
- { OP_Ne, 2, ADDR(8), 1},
- { OP_Delete, 0, 0, 0},
- { OP_Next, 0, ADDR(1), 0}, /* 8 */
- };
-
- sqlite3BeginWriteOperation(pParse, 0, iDb);
- sqlite3OpenMasterTable(pParse, iDb);
- base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger);
- sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, P4_TRANSIENT);
- sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC);
+ sqlite3NestedParse(pParse,
+ "DELETE FROM %Q.%s WHERE name=%Q AND type='trigger'",
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb), pTrigger->zName
+ );
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddOp2(v, OP_Close, 0, 0);
sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0);
- if( pParse->nMem<3 ){
- pParse->nMem = 3;
- }
}
}
@@ -97737,7 +120827,7 @@ SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const ch
assert( sqlite3SchemaMutexHeld(db, iDb, 0) );
pHash = &(db->aDb[iDb].pSchema->trigHash);
- pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0);
+ pTrigger = sqlite3HashInsert(pHash, zName, 0);
if( ALWAYS(pTrigger) ){
if( pTrigger->pSchema==pTrigger->pTabSchema ){
Table *pTab = tableOfTrigger(pTrigger);
@@ -97801,7 +120891,7 @@ SQLITE_PRIVATE Trigger *sqlite3TriggersExist(
}
/*
-** Convert the pStep->target token into a SrcList and return a pointer
+** Convert the pStep->zTarget string into a SrcList and return a pointer
** to that SrcList.
**
** This routine adds a specific database name, if needed, to the target when
@@ -97814,18 +120904,20 @@ static SrcList *targetSrcList(
Parse *pParse, /* The parsing context */
TriggerStep *pStep /* The trigger containing the target token */
){
+ sqlite3 *db = pParse->db;
int iDb; /* Index of the database to use */
SrcList *pSrc; /* SrcList to be returned */
- pSrc = sqlite3SrcListAppend(pParse->db, 0, &pStep->target, 0);
+ pSrc = sqlite3SrcListAppend(db, 0, 0, 0);
if( pSrc ){
assert( pSrc->nSrc>0 );
- assert( pSrc->a!=0 );
- iDb = sqlite3SchemaToIndex(pParse->db, pStep->pTrig->pSchema);
+ pSrc->a[pSrc->nSrc-1].zName = sqlite3DbStrDup(db, pStep->zTarget);
+ iDb = sqlite3SchemaToIndex(db, pStep->pTrig->pSchema);
if( iDb==0 || iDb>=2 ){
- sqlite3 *db = pParse->db;
- assert( iDb<pParse->db->nDb );
- pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, db->aDb[iDb].zName);
+ const char *zDb;
+ assert( iDb<db->nDb );
+ zDb = db->aDb[iDb].zDbSName;
+ pSrc->a[pSrc->nSrc-1].zDatabase = sqlite3DbStrDup(db, zDb);
}
}
return pSrc;
@@ -97862,6 +120954,7 @@ static int codeTriggerProgram(
** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy
*/
pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf;
+ assert( pParse->okConstFactor==0 );
switch( pStep->op ){
case TK_UPDATE: {
@@ -97876,7 +120969,6 @@ static int codeTriggerProgram(
case TK_INSERT: {
sqlite3Insert(pParse,
targetSrcList(pParse, pStep),
- sqlite3ExprListDup(db, pStep->pExprList, 0),
sqlite3SelectDup(db, pStep->pSelect, 0),
sqlite3IdListDup(db, pStep->pIdList),
pParse->eOrconf
@@ -97907,7 +120999,7 @@ static int codeTriggerProgram(
return 0;
}
-#ifdef SQLITE_DEBUG
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
/*
** This function is used to add VdbeComment() annotations to a VDBE
** program. It is not used in production code, only for debugging.
@@ -97936,6 +121028,7 @@ static void transferParseError(Parse *pTo, Parse *pFrom){
if( pTo->nErr==0 ){
pTo->zErrMsg = pFrom->zErrMsg;
pTo->nErr = pFrom->nErr;
+ pTo->rc = pFrom->rc;
}else{
sqlite3DbFree(pFrom->db, pFrom->zErrMsg);
}
@@ -98046,6 +121139,7 @@ static TriggerPrg *codeRowTrigger(
assert( !pSubParse->pAinc && !pSubParse->pZombieTab );
assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
+ sqlite3ParserReset(pSubParse);
sqlite3StackFree(db, pSubParse);
return pPrg;
@@ -98109,8 +121203,8 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
if( pPrg ){
int bRecursive = (p->zName && 0==(pParse->db->flags&SQLITE_RecTriggers));
- sqlite3VdbeAddOp3(v, OP_Program, reg, ignoreJump, ++pParse->nMem);
- sqlite3VdbeChangeP4(v, -1, (const char *)pPrg->pProgram, P4_SUBPROGRAM);
+ sqlite3VdbeAddOp4(v, OP_Program, reg, ignoreJump, ++pParse->nMem,
+ (const char *)pPrg->pProgram, P4_SUBPROGRAM);
VdbeComment(
(v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf)));
@@ -98126,7 +121220,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
/*
** This is called to code the required FOR EACH ROW triggers for an operation
** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE)
-** is given by the op paramater. The tr_tm parameter determines whether the
+** is given by the op parameter. The tr_tm parameter determines whether the
** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then
** parameter pChanges is passed the list of columns being modified.
**
@@ -98272,6 +121366,7 @@ SQLITE_PRIVATE u32 sqlite3TriggerColmask(
** This file contains C code routines that are called by the parser
** to handle UPDATE statements.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* Forward declaration */
@@ -98320,7 +121415,7 @@ static void updateVirtualTable(
SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
assert( pTab!=0 );
if( !pTab->pSelect ){
- sqlite3_value *pValue;
+ sqlite3_value *pValue = 0;
u8 enc = ENC(sqlite3VdbeDb(v));
Column *pCol = &pTab->aCol[i];
VdbeComment((v, "%s.%s", pTab->zName, pCol->zName));
@@ -98331,7 +121426,7 @@ SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){
sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM);
}
#ifndef SQLITE_OMIT_FLOATING_POINT
- if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
+ if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg);
}
#endif
@@ -98354,25 +121449,32 @@ SQLITE_PRIVATE void sqlite3Update(
){
int i, j; /* Loop counters */
Table *pTab; /* The table to be updated */
- int addr = 0; /* VDBE instruction address of the start of the loop */
+ int addrTop = 0; /* VDBE instruction address of the start of the loop */
WhereInfo *pWInfo; /* Information about the WHERE clause */
Vdbe *v; /* The virtual database engine */
Index *pIdx; /* For looping over indices */
+ Index *pPk; /* The PRIMARY KEY index for WITHOUT ROWID tables */
int nIdx; /* Number of indices that need updating */
- int iCur; /* VDBE Cursor number of pTab */
+ int iBaseCur; /* Base cursor number */
+ int iDataCur; /* Cursor for the canonical data btree */
+ int iIdxCur; /* Cursor for the first index */
sqlite3 *db; /* The database structure */
int *aRegIdx = 0; /* One register assigned to each index to be updated */
int *aXRef = 0; /* aXRef[i] is the index in pChanges->a[] of the
** an expression for the i-th column of the table.
** aXRef[i]==-1 if the i-th column is not changed. */
- int chngRowid; /* True if the record number is being changed */
+ u8 *aToOpen; /* 1 for tables and indices to be opened */
+ u8 chngPk; /* PRIMARY KEY changed in a WITHOUT ROWID table */
+ u8 chngRowid; /* Rowid changed in a normal table */
+ u8 chngKey; /* Either chngPk or chngRowid */
Expr *pRowidExpr = 0; /* Expression defining the new record number */
- int openAll = 0; /* True if all indices need to be opened */
AuthContext sContext; /* The authorization context */
NameContext sNC; /* The name-context to resolve expressions in */
int iDb; /* Database containing the table being updated */
int okOnePass; /* True for one-pass algorithm without the FIFO */
int hasFK; /* True if foreign key processing is required */
+ int labelBreak; /* Jump here to break out of UPDATE loop */
+ int labelContinue; /* Jump here to continue next step of UPDATE loop */
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True when updating a view (INSTEAD OF trigger) */
@@ -98380,14 +121482,18 @@ SQLITE_PRIVATE void sqlite3Update(
int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */
#endif
int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */
+ int iEph = 0; /* Ephemeral table holding all primary key values */
+ int nKey = 0; /* Number of elements in regKey for WITHOUT ROWID */
+ int aiCurOnePass[2]; /* The write cursors opened by WHERE_ONEPASS */
/* Register Allocations */
int regRowCount = 0; /* A count of rows changed */
- int regOldRowid; /* The old rowid */
- int regNewRowid; /* The new rowid */
- int regNew;
- int regOld = 0;
+ int regOldRowid = 0; /* The old rowid */
+ int regNewRowid = 0; /* The new rowid */
+ int regNew = 0; /* Content of the NEW.* table in triggers */
+ int regOld = 0; /* Content of OLD.* table in triggers */
int regRowSet = 0; /* Rowset of rows to be updated */
+ int regKey = 0; /* composite PRIMARY KEY value */
memset(&sContext, 0, sizeof(sContext));
db = pParse->db;
@@ -98425,20 +121531,34 @@ SQLITE_PRIVATE void sqlite3Update(
if( sqlite3IsReadOnly(pParse, pTab, tmask) ){
goto update_cleanup;
}
- aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol );
- if( aXRef==0 ) goto update_cleanup;
- for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
/* Allocate a cursors for the main database table and for all indices.
** The index cursors might not be used, but if they are used they
** need to occur right after the database cursor. So go ahead and
** allocate enough space, just in case.
*/
- pTabList->a[0].iCursor = iCur = pParse->nTab++;
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ pTabList->a[0].iCursor = iBaseCur = iDataCur = pParse->nTab++;
+ iIdxCur = iDataCur+1;
+ pPk = HasRowid(pTab) ? 0 : sqlite3PrimaryKeyIndex(pTab);
+ for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
+ if( IsPrimaryKeyIndex(pIdx) && pPk!=0 ){
+ iDataCur = pParse->nTab;
+ pTabList->a[0].iCursor = iDataCur;
+ }
pParse->nTab++;
}
+ /* Allocate space for aXRef[], aRegIdx[], and aToOpen[].
+ ** Initialize aXRef[] and aToOpen[] to their default values.
+ */
+ aXRef = sqlite3DbMallocRawNN(db, sizeof(int) * (pTab->nCol+nIdx) + nIdx+2 );
+ if( aXRef==0 ) goto update_cleanup;
+ aRegIdx = aXRef+pTab->nCol;
+ aToOpen = (u8*)(aRegIdx+nIdx);
+ memset(aToOpen, 1, nIdx+1);
+ aToOpen[nIdx+1] = 0;
+ for(i=0; i<pTab->nCol; i++) aXRef[i] = -1;
+
/* Initialize the name-context */
memset(&sNC, 0, sizeof(sNC));
sNC.pParse = pParse;
@@ -98450,7 +121570,7 @@ SQLITE_PRIVATE void sqlite3Update(
** column to be updated, make sure we have authorization to change
** that column.
*/
- chngRowid = 0;
+ chngRowid = chngPk = 0;
for(i=0; i<pChanges->nExpr; i++){
if( sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
goto update_cleanup;
@@ -98460,13 +121580,16 @@ SQLITE_PRIVATE void sqlite3Update(
if( j==pTab->iPKey ){
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
+ }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
+ chngPk = 1;
}
aXRef[j] = i;
break;
}
}
if( j>=pTab->nCol ){
- if( sqlite3IsRowid(pChanges->a[i].zName) ){
+ if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zName) ){
+ j = -1;
chngRowid = 1;
pRowidExpr = pChanges->a[i].pExpr;
}else{
@@ -98479,7 +121602,8 @@ SQLITE_PRIVATE void sqlite3Update(
{
int rc;
rc = sqlite3AuthCheck(pParse, SQLITE_UPDATE, pTab->zName,
- pTab->aCol[j].zName, db->aDb[iDb].zName);
+ j<0 ? "ROWID" : pTab->aCol[j].zName,
+ db->aDb[iDb].zDbSName);
if( rc==SQLITE_DENY ){
goto update_cleanup;
}else if( rc==SQLITE_IGNORE ){
@@ -98488,32 +121612,41 @@ SQLITE_PRIVATE void sqlite3Update(
}
#endif
}
+ assert( (chngRowid & chngPk)==0 );
+ assert( chngRowid==0 || chngRowid==1 );
+ assert( chngPk==0 || chngPk==1 );
+ chngKey = chngRowid + chngPk;
- hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid);
+ /* The SET expressions are not actually used inside the WHERE loop.
+ ** So reset the colUsed mask. Unless this is a virtual table. In that
+ ** case, set all bits of the colUsed mask (to ensure that the virtual
+ ** table implementation makes all columns available).
+ */
+ pTabList->a[0].colUsed = IsVirtual(pTab) ? ALLBITS : 0;
+
+ hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngKey);
- /* Allocate memory for the array aRegIdx[]. There is one entry in the
- ** array for each index associated with table being updated. Fill in
- ** the value with a register number for indices that are to be used
- ** and with zero for unused indices.
+ /* There is one entry in the aRegIdx[] array for each index on the table
+ ** being updated. Fill in aRegIdx[] with a register number that will hold
+ ** the key for accessing each index.
+ **
+ ** FIXME: Be smarter about omitting indexes that use expressions.
*/
- for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){}
- if( nIdx>0 ){
- aRegIdx = sqlite3DbMallocRaw(db, sizeof(Index*) * nIdx );
- if( aRegIdx==0 ) goto update_cleanup;
- }
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
int reg;
- if( hasFK || chngRowid ){
+ if( chngKey || hasFK || pIdx->pPartIdxWhere || pIdx==pPk ){
reg = ++pParse->nMem;
}else{
reg = 0;
- for(i=0; i<pIdx->nColumn; i++){
- if( aXRef[pIdx->aiColumn[i]]>=0 ){
+ for(i=0; i<pIdx->nKeyCol; i++){
+ i16 iIdxCol = pIdx->aiColumn[i];
+ if( iIdxCol<0 || aXRef[iIdxCol]>=0 ){
reg = ++pParse->nMem;
break;
}
}
}
+ if( reg==0 ) aToOpen[j+1] = 0;
aRegIdx[j] = reg;
}
@@ -98523,28 +121656,20 @@ SQLITE_PRIVATE void sqlite3Update(
if( pParse->nested==0 ) sqlite3VdbeCountChanges(v);
sqlite3BeginWriteOperation(pParse, 1, iDb);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- /* Virtual tables must be handled separately */
- if( IsVirtual(pTab) ){
- updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
- pWhere, onError);
- pWhere = 0;
- pTabList = 0;
- goto update_cleanup;
- }
-#endif
-
/* Allocate required registers. */
- regOldRowid = regNewRowid = ++pParse->nMem;
- if( pTrigger || hasFK ){
- regOld = pParse->nMem + 1;
+ if( !IsVirtual(pTab) ){
+ regRowSet = ++pParse->nMem;
+ regOldRowid = regNewRowid = ++pParse->nMem;
+ if( chngPk || pTrigger || hasFK ){
+ regOld = pParse->nMem + 1;
+ pParse->nMem += pTab->nCol;
+ }
+ if( chngKey || pTrigger || hasFK ){
+ regNewRowid = ++pParse->nMem;
+ }
+ regNew = pParse->nMem + 1;
pParse->nMem += pTab->nCol;
}
- if( chngRowid || pTrigger || hasFK ){
- regNewRowid = ++pParse->nMem;
- }
- regNew = pParse->nMem + 1;
- pParse->nMem += pTab->nCol;
/* Start the view context. */
if( isView ){
@@ -98552,11 +121677,11 @@ SQLITE_PRIVATE void sqlite3Update(
}
/* If we are trying to update a view, realize that view into
- ** a ephemeral table.
+ ** an ephemeral table.
*/
#if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER)
if( isView ){
- sqlite3MaterializeView(pParse, pTab, pWhere, iCur);
+ sqlite3MaterializeView(pParse, pTab, pWhere, iDataCur);
}
#endif
@@ -98567,26 +121692,70 @@ SQLITE_PRIVATE void sqlite3Update(
goto update_cleanup;
}
- /* Begin the database scan
- */
- sqlite3VdbeAddOp2(v, OP_Null, 0, regOldRowid);
- pWInfo = sqlite3WhereBegin(
- pParse, pTabList, pWhere, 0, 0, WHERE_ONEPASS_DESIRED
- );
- if( pWInfo==0 ) goto update_cleanup;
- okOnePass = pWInfo->okOnePass;
-
- /* Remember the rowid of every item to be updated.
- */
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regOldRowid);
- if( !okOnePass ){
- regRowSet = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ /* Virtual tables must be handled separately */
+ if( IsVirtual(pTab) ){
+ updateVirtualTable(pParse, pTabList, pTab, pChanges, pRowidExpr, aXRef,
+ pWhere, onError);
+ goto update_cleanup;
}
+#endif
- /* End the database scan loop.
+ /* Begin the database scan
*/
- sqlite3WhereEnd(pWInfo);
+ if( HasRowid(pTab) ){
+ sqlite3VdbeAddOp3(v, OP_Null, 0, regRowSet, regOldRowid);
+ pWInfo = sqlite3WhereBegin(
+ pParse, pTabList, pWhere, 0, 0,
+ WHERE_ONEPASS_DESIRED | WHERE_SEEK_TABLE, iIdxCur
+ );
+ if( pWInfo==0 ) goto update_cleanup;
+ okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+
+ /* Remember the rowid of every item to be updated.
+ */
+ sqlite3VdbeAddOp2(v, OP_Rowid, iDataCur, regOldRowid);
+ if( !okOnePass ){
+ sqlite3VdbeAddOp2(v, OP_RowSetAdd, regRowSet, regOldRowid);
+ }
+
+ /* End the database scan loop.
+ */
+ sqlite3WhereEnd(pWInfo);
+ }else{
+ int iPk; /* First of nPk memory cells holding PRIMARY KEY value */
+ i16 nPk; /* Number of components of the PRIMARY KEY */
+ int addrOpen; /* Address of the OpenEphemeral instruction */
+
+ assert( pPk!=0 );
+ nPk = pPk->nKeyCol;
+ iPk = pParse->nMem+1;
+ pParse->nMem += nPk;
+ regKey = ++pParse->nMem;
+ iEph = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iPk);
+ addrOpen = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, iEph, nPk);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, 0, 0,
+ WHERE_ONEPASS_DESIRED, iIdxCur);
+ if( pWInfo==0 ) goto update_cleanup;
+ okOnePass = sqlite3WhereOkOnePass(pWInfo, aiCurOnePass);
+ for(i=0; i<nPk; i++){
+ assert( pPk->aiColumn[i]>=0 );
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, pPk->aiColumn[i],
+ iPk+i);
+ }
+ if( okOnePass ){
+ sqlite3VdbeChangeToNoop(v, addrOpen);
+ nKey = nPk;
+ regKey = iPk;
+ }else{
+ sqlite3VdbeAddOp4(v, OP_MakeRecord, iPk, nPk, regKey,
+ sqlite3IndexAffinityStr(db, pPk), nPk);
+ sqlite3VdbeAddOp2(v, OP_IdxInsert, iEph, regKey);
+ }
+ sqlite3WhereEnd(pWInfo);
+ }
/* Initialize the count of updated rows
*/
@@ -98595,6 +121764,7 @@ SQLITE_PRIVATE void sqlite3Update(
sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount);
}
+ labelBreak = sqlite3VdbeMakeLabel(v);
if( !isView ){
/*
** Open every index that needs updating. Note that if any
@@ -98602,73 +121772,84 @@ SQLITE_PRIVATE void sqlite3Update(
** action, then we need to open all indices because we might need
** to be deleting some records.
*/
- if( !okOnePass ) sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenWrite);
if( onError==OE_Replace ){
- openAll = 1;
+ memset(aToOpen, 1, nIdx+1);
}else{
- openAll = 0;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
if( pIdx->onError==OE_Replace ){
- openAll = 1;
+ memset(aToOpen, 1, nIdx+1);
break;
}
}
}
- for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- if( openAll || aRegIdx[i]>0 ){
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx);
- sqlite3VdbeAddOp4(v, OP_OpenWrite, iCur+i+1, pIdx->tnum, iDb,
- (char*)pKey, P4_KEYINFO_HANDOFF);
- assert( pParse->nTab>iCur+i+1 );
- }
+ if( okOnePass ){
+ if( aiCurOnePass[0]>=0 ) aToOpen[aiCurOnePass[0]-iBaseCur] = 0;
+ if( aiCurOnePass[1]>=0 ) aToOpen[aiCurOnePass[1]-iBaseCur] = 0;
}
+ sqlite3OpenTableAndIndices(pParse, pTab, OP_OpenWrite, 0, iBaseCur, aToOpen,
+ 0, 0);
}
/* Top of the update loop */
if( okOnePass ){
- int a1 = sqlite3VdbeAddOp1(v, OP_NotNull, regOldRowid);
- addr = sqlite3VdbeAddOp0(v, OP_Goto);
- sqlite3VdbeJumpHere(v, a1);
+ if( aToOpen[iDataCur-iBaseCur] && !isView ){
+ assert( pPk );
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelBreak, regKey, nKey);
+ VdbeCoverageNeverTaken(v);
+ }
+ labelContinue = labelBreak;
+ sqlite3VdbeAddOp2(v, OP_IsNull, pPk ? regKey : regOldRowid, labelBreak);
+ VdbeCoverageIf(v, pPk==0);
+ VdbeCoverageIf(v, pPk!=0);
+ }else if( pPk ){
+ labelContinue = sqlite3VdbeMakeLabel(v);
+ sqlite3VdbeAddOp2(v, OP_Rewind, iEph, labelBreak); VdbeCoverage(v);
+ addrTop = sqlite3VdbeAddOp2(v, OP_RowKey, iEph, regKey);
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue, regKey, 0);
+ VdbeCoverage(v);
}else{
- addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid);
+ labelContinue = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, labelBreak,
+ regOldRowid);
+ VdbeCoverage(v);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
+ VdbeCoverage(v);
}
- /* Make cursor iCur point to the record that is being updated. If
- ** this record does not exist for some reason (deleted by a trigger,
- ** for example, then jump to the next iteration of the RowSet loop. */
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
-
/* If the record number will change, set register regNewRowid to
** contain the new value. If the record number is not being modified,
** then regNewRowid is the same register as regOldRowid, which is
** already populated. */
- assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid );
+ assert( chngKey || pTrigger || hasFK || regOldRowid==regNewRowid );
if( chngRowid ){
sqlite3ExprCode(pParse, pRowidExpr, regNewRowid);
- sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid);
+ sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); VdbeCoverage(v);
}
- /* If there are triggers on this table, populate an array of registers
- ** with the required old.* column data. */
- if( hasFK || pTrigger ){
+ /* Compute the old pre-UPDATE content of the row being changed, if that
+ ** information is needed */
+ if( chngPk || hasFK || pTrigger ){
u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0);
oldmask |= sqlite3TriggerColmask(pParse,
pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError
);
for(i=0; i<pTab->nCol; i++){
- if( aXRef[i]<0 || oldmask==0xffffffff || (i<32 && (oldmask & (1<<i))) ){
- sqlite3ExprCodeGetColumnOfTable(v, pTab, iCur, i, regOld+i);
+ if( oldmask==0xffffffff
+ || (i<32 && (oldmask & MASKBIT32(i))!=0)
+ || (pTab->aCol[i].colFlags & COLFLAG_PRIMKEY)!=0
+ ){
+ testcase( oldmask!=0xffffffff && i==31 );
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regOld+i);
}else{
sqlite3VdbeAddOp2(v, OP_Null, 0, regOld+i);
}
}
- if( chngRowid==0 ){
+ if( chngRowid==0 && pPk==0 ){
sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid);
}
}
/* Populate the array of registers beginning at regNew with the new
- ** row data. This array is used to check constaints, create the new
+ ** row data. This array is used to check constants, create the new
** table and index records, and as the values for any new.* references
** made by triggers.
**
@@ -98690,7 +121871,7 @@ SQLITE_PRIVATE void sqlite3Update(
j = aXRef[i];
if( j>=0 ){
sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i);
- }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<<i)) ){
+ }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask & MASKBIT32(i)) ){
/* This branch loads the value of a column that will not be changed
** into a register. This is done if there are no BEFORE triggers, or
** if there are one or more BEFORE triggers that use this value via
@@ -98698,8 +121879,9 @@ SQLITE_PRIVATE void sqlite3Update(
*/
testcase( i==31 );
testcase( i==32 );
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
- sqlite3ColumnDefault(v, pTab, i, regNew+i);
+ sqlite3ExprCodeGetColumnToReg(pParse, pTab, i, iDataCur, regNew+i);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i);
}
}
}
@@ -98708,18 +121890,23 @@ SQLITE_PRIVATE void sqlite3Update(
** verified. One could argue that this is wrong.
*/
if( tmask&TRIGGER_BEFORE ){
- sqlite3VdbeAddOp2(v, OP_Affinity, regNew, pTab->nCol);
- sqlite3TableAffinityStr(v, pTab);
+ sqlite3TableAffinity(v, pTab, regNew);
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_BEFORE, pTab, regOldRowid, onError, addr);
+ TRIGGER_BEFORE, pTab, regOldRowid, onError, labelContinue);
/* The row-trigger may have deleted the row being updated. In this
** case, jump to the next row. No updates or AFTER triggers are
- ** required. This behaviour - what happens when the row being updated
+ ** required. This behavior - what happens when the row being updated
** is deleted or renamed by a BEFORE trigger - is left undefined in the
** documentation.
*/
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid);
+ if( pPk ){
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, labelContinue,regKey,nKey);
+ VdbeCoverage(v);
+ }else{
+ sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, labelContinue, regOldRowid);
+ VdbeCoverage(v);
+ }
/* If it did not delete it, the row-trigger may still have modified
** some of the columns of the row being updated. Load the values for
@@ -98728,46 +121915,77 @@ SQLITE_PRIVATE void sqlite3Update(
*/
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]<0 && i!=pTab->iPKey ){
- sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i);
- sqlite3ColumnDefault(v, pTab, i, regNew+i);
+ sqlite3ExprCodeGetColumnOfTable(v, pTab, iDataCur, i, regNew+i);
}
}
}
if( !isView ){
- int j1; /* Address of jump instruction */
+ int addr1 = 0; /* Address of jump instruction */
+ int bReplace = 0; /* True if REPLACE conflict resolution might happen */
/* Do constraint checks. */
- sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid,
- aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0);
+ assert( regOldRowid>0 );
+ sqlite3GenerateConstraintChecks(pParse, pTab, aRegIdx, iDataCur, iIdxCur,
+ regNewRowid, regOldRowid, chngKey, onError, labelContinue, &bReplace,
+ aXRef);
/* Do FK constraint checks. */
if( hasFK ){
- sqlite3FkCheck(pParse, pTab, regOldRowid, 0);
+ sqlite3FkCheck(pParse, pTab, regOldRowid, 0, aXRef, chngKey);
}
/* Delete the index entries associated with the current record. */
- j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid);
- sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx);
-
- /* If changing the record number, delete the old record. */
- if( hasFK || chngRowid ){
- sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0);
+ if( bReplace || chngKey ){
+ if( pPk ){
+ addr1 = sqlite3VdbeAddOp4Int(v, OP_NotFound, iDataCur, 0, regKey, nKey);
+ }else{
+ addr1 = sqlite3VdbeAddOp3(v, OP_NotExists, iDataCur, 0, regOldRowid);
+ }
+ VdbeCoverageNeverTaken(v);
+ }
+ sqlite3GenerateRowIndexDelete(pParse, pTab, iDataCur, iIdxCur, aRegIdx, -1);
+
+ /* If changing the rowid value, or if there are foreign key constraints
+ ** to process, delete the old record. Otherwise, add a noop OP_Delete
+ ** to invoke the pre-update hook.
+ **
+ ** That (regNew==regnewRowid+1) is true is also important for the
+ ** pre-update hook. If the caller invokes preupdate_new(), the returned
+ ** value is copied from memory cell (regNewRowid+1+iCol), where iCol
+ ** is the column index supplied by the user.
+ */
+ assert( regNew==regNewRowid+1 );
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+ sqlite3VdbeAddOp3(v, OP_Delete, iDataCur,
+ OPFLAG_ISUPDATE | ((hasFK || chngKey || pPk!=0) ? 0 : OPFLAG_ISNOOP),
+ regNewRowid
+ );
+ if( !pParse->nested ){
+ sqlite3VdbeChangeP4(v, -1, (char*)pTab, P4_TABLE);
+ }
+#else
+ if( hasFK || chngKey || pPk!=0 ){
+ sqlite3VdbeAddOp2(v, OP_Delete, iDataCur, 0);
+ }
+#endif
+ if( bReplace || chngKey ){
+ sqlite3VdbeJumpHere(v, addr1);
}
- sqlite3VdbeJumpHere(v, j1);
if( hasFK ){
- sqlite3FkCheck(pParse, pTab, 0, regNewRowid);
+ sqlite3FkCheck(pParse, pTab, 0, regNewRowid, aXRef, chngKey);
}
/* Insert the new index entries and the new record. */
- sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0);
+ sqlite3CompleteInsertion(pParse, pTab, iDataCur, iIdxCur,
+ regNewRowid, aRegIdx, 1, 0, 0);
/* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to
** handle rows (possibly in other tables) that refer via a foreign key
** to the row just updated. */
if( hasFK ){
- sqlite3FkActions(pParse, pTab, pChanges, regOldRowid);
+ sqlite3FkActions(pParse, pTab, pChanges, regOldRowid, aXRef, chngKey);
}
}
@@ -98778,21 +121996,29 @@ SQLITE_PRIVATE void sqlite3Update(
}
sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges,
- TRIGGER_AFTER, pTab, regOldRowid, onError, addr);
+ TRIGGER_AFTER, pTab, regOldRowid, onError, labelContinue);
/* Repeat the above with the next record to be updated, until
** all record selected by the WHERE clause have been updated.
*/
- sqlite3VdbeAddOp2(v, OP_Goto, 0, addr);
- sqlite3VdbeJumpHere(v, addr);
+ if( okOnePass ){
+ /* Nothing to do at end-of-loop for a single-pass */
+ }else if( pPk ){
+ sqlite3VdbeResolveLabel(v, labelContinue);
+ sqlite3VdbeAddOp2(v, OP_Next, iEph, addrTop); VdbeCoverage(v);
+ }else{
+ sqlite3VdbeGoto(v, labelContinue);
+ }
+ sqlite3VdbeResolveLabel(v, labelBreak);
/* Close all tables */
for(i=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, i++){
- if( openAll || aRegIdx[i]>0 ){
- sqlite3VdbeAddOp2(v, OP_Close, iCur+i+1, 0);
+ assert( aRegIdx );
+ if( aToOpen[i+1] ){
+ sqlite3VdbeAddOp2(v, OP_Close, iIdxCur+i, 0);
}
}
- sqlite3VdbeAddOp2(v, OP_Close, iCur, 0);
+ if( iDataCur<iIdxCur ) sqlite3VdbeAddOp2(v, OP_Close, iDataCur, 0);
/* Update the sqlite_sequence table by storing the content of the
** maximum rowid counter values recorded while inserting into
@@ -98815,15 +122041,14 @@ SQLITE_PRIVATE void sqlite3Update(
update_cleanup:
sqlite3AuthContextPop(&sContext);
- sqlite3DbFree(db, aRegIdx);
- sqlite3DbFree(db, aXRef);
+ sqlite3DbFree(db, aXRef); /* Also frees aRegIdx[] and aToOpen[] */
sqlite3SrcListDelete(db, pTabList);
sqlite3ExprListDelete(db, pChanges);
sqlite3ExprDelete(db, pWhere);
return;
}
/* Make sure "isView" and other macros defined above are undefined. Otherwise
-** thely may interfere with compilation of other functions in this file
+** they may interfere with compilation of other functions in this file
** (or in another file, if this file becomes part of the amalgamation). */
#ifdef isView
#undef isView
@@ -98836,21 +122061,23 @@ update_cleanup:
/*
** Generate code for an UPDATE of a virtual table.
**
-** The strategy is that we create an ephemerial table that contains
+** There are two possible strategies - the default and the special
+** "onepass" strategy. Onepass is only used if the virtual table
+** implementation indicates that pWhere may match at most one row.
+**
+** The default strategy is to create an ephemeral table that contains
** for each row to be changed:
**
** (A) The original rowid of that row.
-** (B) The revised rowid for the row. (note1)
+** (B) The revised rowid for the row.
** (C) The content of every column in the row.
**
-** Then we loop over this ephemeral table and for each row in
-** the ephermeral table call VUpdate.
+** Then loop through the contents of this ephemeral table executing a
+** VUpdate for each row. When finished, drop the ephemeral table.
**
-** When finished, drop the ephemeral table.
-**
-** (note1) Actually, if we know in advance that (A) is always the same
-** as (B) we only store (A), then duplicate (A) when pulling
-** it out of the ephemeral table before calling VUpdate.
+** The "onepass" strategy does not use an ephemeral table. Instead, it
+** stores the same values (A, B and C above) in a register array and
+** makes a single invocation of VUpdate.
*/
static void updateVirtualTable(
Parse *pParse, /* The parsing context */
@@ -98863,68 +122090,96 @@ static void updateVirtualTable(
int onError /* ON CONFLICT strategy */
){
Vdbe *v = pParse->pVdbe; /* Virtual machine under construction */
- ExprList *pEList = 0; /* The result set of the SELECT statement */
- Select *pSelect = 0; /* The SELECT statement */
- Expr *pExpr; /* Temporary expression */
int ephemTab; /* Table holding the result of the SELECT */
int i; /* Loop counter */
- int addr; /* Address of top of loop */
- int iReg; /* First register in set passed to OP_VUpdate */
sqlite3 *db = pParse->db; /* Database connection */
const char *pVTab = (const char*)sqlite3GetVTable(db, pTab);
- SelectDest dest;
-
- /* Construct the SELECT statement that will find the new values for
- ** all updated rows.
- */
- pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_"));
+ WhereInfo *pWInfo;
+ int nArg = 2 + pTab->nCol; /* Number of arguments to VUpdate */
+ int regArg; /* First register in VUpdate arg array */
+ int regRec; /* Register in which to assemble record */
+ int regRowid; /* Register for ephem table rowid */
+ int iCsr = pSrc->a[0].iCursor; /* Cursor used for virtual table scan */
+ int aDummy[2]; /* Unused arg for sqlite3WhereOkOnePass() */
+ int bOnePass; /* True to use onepass strategy */
+ int addr; /* Address of OP_OpenEphemeral */
+
+ /* Allocate nArg registers to martial the arguments to VUpdate. Then
+ ** create and open the ephemeral table in which the records created from
+ ** these arguments will be temporarily stored. */
+ assert( v );
+ ephemTab = pParse->nTab++;
+ addr= sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, nArg);
+ regArg = pParse->nMem + 1;
+ pParse->nMem += nArg;
+ regRec = ++pParse->nMem;
+ regRowid = ++pParse->nMem;
+
+ /* Start scanning the virtual table */
+ pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0,0,WHERE_ONEPASS_DESIRED,0);
+ if( pWInfo==0 ) return;
+
+ /* Populate the argument registers. */
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg);
if( pRowid ){
- pEList = sqlite3ExprListAppend(pParse, pEList,
- sqlite3ExprDup(db, pRowid, 0));
+ sqlite3ExprCode(pParse, pRowid, regArg+1);
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCsr, regArg+1);
}
- assert( pTab->iPKey<0 );
for(i=0; i<pTab->nCol; i++){
if( aXRef[i]>=0 ){
- pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0);
+ sqlite3ExprCode(pParse, pChanges->a[aXRef[i]].pExpr, regArg+2+i);
}else{
- pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName);
+ sqlite3VdbeAddOp3(v, OP_VColumn, iCsr, i, regArg+2+i);
}
- pEList = sqlite3ExprListAppend(pParse, pEList, pExpr);
}
- pSelect = sqlite3SelectNew(pParse, pEList, pSrc, pWhere, 0, 0, 0, 0, 0, 0);
-
- /* Create the ephemeral table into which the update results will
- ** be stored.
- */
- assert( v );
- ephemTab = pParse->nTab++;
- sqlite3VdbeAddOp2(v, OP_OpenEphemeral, ephemTab, pTab->nCol+1+(pRowid!=0));
- sqlite3VdbeChangeP5(v, BTREE_UNORDERED);
- /* fill the ephemeral table
- */
- sqlite3SelectDestInit(&dest, SRT_Table, ephemTab);
- sqlite3Select(pParse, pSelect, &dest);
+ bOnePass = sqlite3WhereOkOnePass(pWInfo, aDummy);
- /* Generate code to scan the ephemeral table and call VUpdate. */
- iReg = ++pParse->nMem;
- pParse->nMem += pTab->nCol+1;
- addr = sqlite3VdbeAddOp2(v, OP_Rewind, ephemTab, 0);
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, 0, iReg);
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, (pRowid?1:0), iReg+1);
- for(i=0; i<pTab->nCol; i++){
- sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i);
+ if( bOnePass ){
+ /* If using the onepass strategy, no-op out the OP_OpenEphemeral coded
+ ** above. Also, if this is a top-level parse (not a trigger), clear the
+ ** multi-write flag so that the VM does not open a statement journal */
+ sqlite3VdbeChangeToNoop(v, addr);
+ if( sqlite3IsToplevel(pParse) ){
+ pParse->isMultiWrite = 0;
+ }
+ }else{
+ /* Create a record from the argument register contents and insert it into
+ ** the ephemeral table. */
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regArg, nArg, regRec);
+ sqlite3VdbeAddOp2(v, OP_NewRowid, ephemTab, regRowid);
+ sqlite3VdbeAddOp3(v, OP_Insert, ephemTab, regRec, regRowid);
+ }
+
+
+ if( bOnePass==0 ){
+ /* End the virtual table scan */
+ sqlite3WhereEnd(pWInfo);
+
+ /* Begin scannning through the ephemeral table. */
+ addr = sqlite3VdbeAddOp1(v, OP_Rewind, ephemTab); VdbeCoverage(v);
+
+ /* Extract arguments from the current row of the ephemeral table and
+ ** invoke the VUpdate method. */
+ for(i=0; i<nArg; i++){
+ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i, regArg+i);
+ }
}
sqlite3VtabMakeWritable(pParse, pTab);
- sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB);
+ sqlite3VdbeAddOp4(v, OP_VUpdate, 0, nArg, regArg, pVTab, P4_VTAB);
sqlite3VdbeChangeP5(v, onError==OE_Default ? OE_Abort : onError);
sqlite3MayAbort(pParse);
- sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1);
- sqlite3VdbeJumpHere(v, addr);
- sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
- /* Cleanup */
- sqlite3SelectDelete(db, pSelect);
+ /* End of the ephemeral table scan. Or, if using the onepass strategy,
+ ** jump to here if the scan visited zero rows. */
+ if( bOnePass==0 ){
+ sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); VdbeCoverage(v);
+ sqlite3VdbeJumpHere(v, addr);
+ sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0);
+ }else{
+ sqlite3WhereEnd(pWInfo);
+ }
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -98946,75 +122201,94 @@ static void updateVirtualTable(
** Most of the code in this file may be omitted by defining the
** SQLITE_OMIT_VACUUM macro.
*/
+/* #include "sqliteInt.h" */
+/* #include "vdbeInt.h" */
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
-/*
-** Finalize a prepared statement. If there was an error, store the
-** text of the error message in *pzErrMsg. Return the result code.
-*/
-static int vacuumFinalize(sqlite3 *db, sqlite3_stmt *pStmt, char **pzErrMsg){
- int rc;
- rc = sqlite3VdbeFinalize((Vdbe*)pStmt);
- if( rc ){
- sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
- }
- return rc;
-}
/*
-** Execute zSql on database db. Return an error code.
+** Execute zSql on database db.
+**
+** If zSql returns rows, then each row will have exactly one
+** column. (This will only happen if zSql begins with "SELECT".)
+** Take each row of result and call execSql() again recursively.
+**
+** The execSqlF() routine does the same thing, except it accepts
+** a format string as its third argument
*/
static int execSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
sqlite3_stmt *pStmt;
- VVA_ONLY( int rc; )
- if( !zSql ){
- return SQLITE_NOMEM;
- }
- if( SQLITE_OK!=sqlite3_prepare(db, zSql, -1, &pStmt, 0) ){
- sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
- return sqlite3_errcode(db);
- }
- VVA_ONLY( rc = ) sqlite3_step(pStmt);
- assert( rc!=SQLITE_ROW );
- return vacuumFinalize(db, pStmt, pzErrMsg);
-}
-
-/*
-** Execute zSql on database db. The statement returns exactly
-** one column. Execute this as SQL on the same database.
-*/
-static int execExecSql(sqlite3 *db, char **pzErrMsg, const char *zSql){
- sqlite3_stmt *pStmt;
int rc;
- rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ /* printf("SQL: [%s]\n", zSql); fflush(stdout); */
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
-
- while( SQLITE_ROW==sqlite3_step(pStmt) ){
- rc = execSql(db, pzErrMsg, (char*)sqlite3_column_text(pStmt, 0));
- if( rc!=SQLITE_OK ){
- vacuumFinalize(db, pStmt, pzErrMsg);
- return rc;
+ while( SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
+ const char *zSubSql = (const char*)sqlite3_column_text(pStmt,0);
+ assert( sqlite3_strnicmp(zSql,"SELECT",6)==0 );
+ if( zSubSql ){
+ assert( zSubSql[0]!='S' );
+ rc = execSql(db, pzErrMsg, zSubSql);
+ if( rc!=SQLITE_OK ) break;
}
}
-
- return vacuumFinalize(db, pStmt, pzErrMsg);
+ assert( rc!=SQLITE_ROW );
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ if( rc ){
+ sqlite3SetString(pzErrMsg, db, sqlite3_errmsg(db));
+ }
+ (void)sqlite3_finalize(pStmt);
+ return rc;
+}
+static int execSqlF(sqlite3 *db, char **pzErrMsg, const char *zSql, ...){
+ char *z;
+ va_list ap;
+ int rc;
+ va_start(ap, zSql);
+ z = sqlite3VMPrintf(db, zSql, ap);
+ va_end(ap);
+ if( z==0 ) return SQLITE_NOMEM;
+ rc = execSql(db, pzErrMsg, z);
+ sqlite3DbFree(db, z);
+ return rc;
}
/*
-** The non-standard VACUUM command is used to clean up the database,
+** The VACUUM command is used to clean up the database,
** collapse free space, etc. It is modelled after the VACUUM command
-** in PostgreSQL.
-**
-** In version 1.0.x of SQLite, the VACUUM command would call
-** gdbm_reorganize() on all the database tables. But beginning
-** with 2.0.0, SQLite no longer uses GDBM so this command has
-** become a no-op.
-*/
-SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse){
+** in PostgreSQL. The VACUUM command works as follows:
+**
+** (1) Create a new transient database file
+** (2) Copy all content from the database being vacuumed into
+** the new transient database file
+** (3) Copy content from the transient database back into the
+** original database.
+**
+** The transient database requires temporary disk space approximately
+** equal to the size of the original database. The copy operation of
+** step (3) requires additional temporary disk space approximately equal
+** to the size of the original database for the rollback journal.
+** Hence, temporary disk space that is approximately 2x the size of the
+** original database is required. Every page of the database is written
+** approximately 3 times: Once for step (2) and twice for step (3).
+** Two writes per page are required in step (3) because the original
+** database content must be written into the rollback journal prior to
+** overwriting the database with the vacuumed content.
+**
+** Only 1x temporary space and only 1x writes would be required if
+** the copy of step (3) were replaced by deleting the original database
+** and renaming the transient database as the original. But that will
+** not work if other processes are attached to the original database.
+** And a power loss in between deleting the original and renaming the
+** transient would cause the database file to appear to be deleted
+** following reboot.
+*/
+SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm){
Vdbe *v = sqlite3GetVdbe(pParse);
- if( v ){
- sqlite3VdbeAddOp2(v, OP_Vacuum, 0, 0);
+ int iDb = pNm ? sqlite3TwoPartName(pParse, pNm, pNm, &pNm) : 0;
+ if( v && (iDb>=2 || iDb==0) ){
+ sqlite3VdbeAddOp1(v, OP_Vacuum, iDb);
+ sqlite3VdbeUsesBtree(v, iDb);
}
return;
}
@@ -99022,25 +122296,25 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse){
/*
** This routine implements the OP_Vacuum opcode of the VDBE.
*/
-SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
+SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db, int iDb){
int rc = SQLITE_OK; /* Return code from service routines */
Btree *pMain; /* The database being vacuumed */
Btree *pTemp; /* The temporary database we vacuum into */
- char *zSql = 0; /* SQL statements */
int saved_flags; /* Saved value of the db->flags */
int saved_nChange; /* Saved value of db->nChange */
int saved_nTotalChange; /* Saved value of db->nTotalChange */
- void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */
+ u8 saved_mTrace; /* Saved trace settings */
Db *pDb = 0; /* Database to detach at end of vacuum */
int isMemDb; /* True if vacuuming a :memory: database */
int nRes; /* Bytes of reserved space at the end of each page */
int nDb; /* Number of attached databases */
+ const char *zDbMain; /* Schema name of database to vacuum */
if( !db->autoCommit ){
sqlite3SetString(pzErrMsg, db, "cannot VACUUM from within a transaction");
return SQLITE_ERROR;
}
- if( db->activeVdbeCnt>1 ){
+ if( db->nVdbeActive>1 ){
sqlite3SetString(pzErrMsg, db,"cannot VACUUM - SQL statements in progress");
return SQLITE_ERROR;
}
@@ -99051,12 +122325,14 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
saved_flags = db->flags;
saved_nChange = db->nChange;
saved_nTotalChange = db->nTotalChange;
- saved_xTrace = db->xTrace;
- db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_PreferBuiltin;
- db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder);
- db->xTrace = 0;
-
- pMain = db->aDb[0].pBt;
+ saved_mTrace = db->mTrace;
+ db->flags |= (SQLITE_WriteSchema | SQLITE_IgnoreChecks
+ | SQLITE_PreferBuiltin | SQLITE_Vacuum);
+ db->flags &= ~(SQLITE_ForeignKeys | SQLITE_ReverseOrder | SQLITE_CountRows);
+ db->mTrace = 0;
+
+ zDbMain = db->aDb[iDb].zDbSName;
+ pMain = db->aDb[iDb].pBt;
isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain));
/* Attach the temporary database as 'vacuum_db'. The synchronous pragma
@@ -99074,18 +122350,12 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
** to write the journal header file.
*/
nDb = db->nDb;
- if( sqlite3TempInMemory(db) ){
- zSql = "ATTACH ':memory:' AS vacuum_db;";
- }else{
- zSql = "ATTACH '' AS vacuum_db;";
- }
- rc = execSql(db, pzErrMsg, zSql);
- if( db->nDb>nDb ){
- pDb = &db->aDb[db->nDb-1];
- assert( strcmp(pDb->zName,"vacuum_db")==0 );
- }
+ rc = execSql(db, pzErrMsg, "ATTACH''AS vacuum_db");
if( rc!=SQLITE_OK ) goto end_of_vacuum;
- pTemp = db->aDb[db->nDb-1].pBt;
+ assert( (db->nDb-1)==nDb );
+ pDb = &db->aDb[nDb];
+ assert( strcmp(pDb->zDbSName,"vacuum_db")==0 );
+ pTemp = pDb->pBt;
/* The call to execSql() to attach the temp database has left the file
** locked (as there was more than one active statement when the transaction
@@ -99093,7 +122363,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
** cause problems for the call to BtreeSetPageSize() below. */
sqlite3BtreeCommit(pTemp);
- nRes = sqlite3BtreeGetReserve(pMain);
+ nRes = sqlite3BtreeGetOptimalReserve(pMain);
/* A VACUUM cannot change the pagesize of an encrypted database. */
#ifdef SQLITE_HAS_CODEC
@@ -99106,6 +122376,19 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
}
#endif
+ sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
+ sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
+ sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
+
+ /* Begin a transaction and take an exclusive lock on the main database
+ ** file. This is done before the sqlite3BtreeGetPageSize(pMain) call below,
+ ** to ensure that we do not try to change the page-size on a WAL database.
+ */
+ rc = execSql(db, pzErrMsg, "BEGIN");
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ rc = sqlite3BtreeBeginTrans(pMain, 2);
+ if( rc!=SQLITE_OK ) goto end_of_vacuum;
+
/* Do not attempt to change the page size for a WAL database */
if( sqlite3PagerGetJournalMode(sqlite3BtreePager(pMain))
==PAGER_JOURNALMODE_WAL ){
@@ -99116,11 +122399,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
|| (!isMemDb && sqlite3BtreeSetPageSize(pTemp, db->nextPagesize, nRes, 0))
|| NEVER(db->mallocFailed)
){
- rc = SQLITE_NOMEM;
- goto end_of_vacuum;
- }
- rc = execSql(db, pzErrMsg, "PRAGMA vacuum_db.synchronous=OFF");
- if( rc!=SQLITE_OK ){
+ rc = SQLITE_NOMEM_BKPT;
goto end_of_vacuum;
}
@@ -99129,77 +122408,59 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
sqlite3BtreeGetAutoVacuum(pMain));
#endif
- /* Begin a transaction */
- rc = execSql(db, pzErrMsg, "BEGIN EXCLUSIVE;");
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
-
/* Query the schema of the main database. Create a mirror schema
** in the temporary database.
*/
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'CREATE TABLE vacuum_db.' || substr(sql,14) "
- " FROM sqlite_master WHERE type='table' AND name!='sqlite_sequence'"
- " AND rootpage>0"
+ db->init.iDb = nDb; /* force new CREATE statements into vacuum_db */
+ rc = execSqlF(db, pzErrMsg,
+ "SELECT sql FROM \"%w\".sqlite_master"
+ " WHERE type='table'AND name<>'sqlite_sequence'"
+ " AND coalesce(rootpage,1)>0",
+ zDbMain
);
if( rc!=SQLITE_OK ) goto end_of_vacuum;
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'CREATE INDEX vacuum_db.' || substr(sql,14)"
- " FROM sqlite_master WHERE sql LIKE 'CREATE INDEX %' ");
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'CREATE UNIQUE INDEX vacuum_db.' || substr(sql,21) "
- " FROM sqlite_master WHERE sql LIKE 'CREATE UNIQUE INDEX %'");
+ rc = execSqlF(db, pzErrMsg,
+ "SELECT sql FROM \"%w\".sqlite_master"
+ " WHERE type='index' AND length(sql)>10",
+ zDbMain
+ );
if( rc!=SQLITE_OK ) goto end_of_vacuum;
+ db->init.iDb = 0;
/* Loop through the tables in the main database. For each, do
** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy
** the contents to the temporary database.
*/
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
- "|| ' SELECT * FROM main.' || quote(name) || ';'"
- "FROM main.sqlite_master "
- "WHERE type = 'table' AND name!='sqlite_sequence' "
- " AND rootpage>0"
- );
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
-
- /* Copy over the sequence table
- */
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'DELETE FROM vacuum_db.' || quote(name) || ';' "
- "FROM vacuum_db.sqlite_master WHERE name='sqlite_sequence' "
- );
- if( rc!=SQLITE_OK ) goto end_of_vacuum;
- rc = execExecSql(db, pzErrMsg,
- "SELECT 'INSERT INTO vacuum_db.' || quote(name) "
- "|| ' SELECT * FROM main.' || quote(name) || ';' "
- "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';"
+ rc = execSqlF(db, pzErrMsg,
+ "SELECT'INSERT INTO vacuum_db.'||quote(name)"
+ "||' SELECT*FROM\"%w\".'||quote(name)"
+ "FROM vacuum_db.sqlite_master "
+ "WHERE type='table'AND coalesce(rootpage,1)>0",
+ zDbMain
);
+ assert( (db->flags & SQLITE_Vacuum)!=0 );
+ db->flags &= ~SQLITE_Vacuum;
if( rc!=SQLITE_OK ) goto end_of_vacuum;
-
/* Copy the triggers, views, and virtual tables from the main database
** over to the temporary database. None of these objects has any
** associated storage, so all we have to do is copy their entries
** from the SQLITE_MASTER table.
*/
- rc = execSql(db, pzErrMsg,
- "INSERT INTO vacuum_db.sqlite_master "
- " SELECT type, name, tbl_name, rootpage, sql"
- " FROM main.sqlite_master"
- " WHERE type='view' OR type='trigger'"
- " OR (type='table' AND rootpage=0)"
+ rc = execSqlF(db, pzErrMsg,
+ "INSERT INTO vacuum_db.sqlite_master"
+ " SELECT*FROM \"%w\".sqlite_master"
+ " WHERE type IN('view','trigger')"
+ " OR(type='table'AND rootpage=0)",
+ zDbMain
);
if( rc ) goto end_of_vacuum;
- /* At this point, unless the main db was completely empty, there is now a
- ** transaction open on the vacuum database, but not on the main database.
- ** Open a btree level transaction on the main database. This allows a
- ** call to sqlite3BtreeCopyFile(). The main database btree level
- ** transaction is then committed, so the SQL level never knows it was
- ** opened for writing. This way, the SQL transaction used to create the
- ** temporary database never needs to be committed.
+ /* At this point, there is a write transaction open on both the
+ ** vacuum database and the main database. Assuming no error occurs,
+ ** both transactions are closed by this block - the main database
+ ** transaction by sqlite3BtreeCopyFile() and the other by an explicit
+ ** call to sqlite3BtreeCommit().
*/
{
u32 meta;
@@ -99216,6 +122477,7 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
BTREE_DEFAULT_CACHE_SIZE, 0, /* Preserve the default page cache size */
BTREE_TEXT_ENCODING, 0, /* Preserve the text encoding */
BTREE_USER_VERSION, 0, /* Preserve the user version */
+ BTREE_APPLICATION_ID, 0, /* Preserve the application id */
};
assert( 1==sqlite3BtreeIsInTrans(pTemp) );
@@ -99244,10 +122506,11 @@ SQLITE_PRIVATE int sqlite3RunVacuum(char **pzErrMsg, sqlite3 *db){
end_of_vacuum:
/* Restore the original value of db->flags */
+ db->init.iDb = 0;
db->flags = saved_flags;
db->nChange = saved_nChange;
db->nTotalChange = saved_nTotalChange;
- db->xTrace = saved_xTrace;
+ db->mTrace = saved_mTrace;
sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
/* Currently there is an SQL level transaction open on the vacuum
@@ -99267,7 +122530,7 @@ end_of_vacuum:
/* This both clears the schemas and reduces the size of the db->aDb[]
** array. */
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
return rc;
}
@@ -99290,6 +122553,7 @@ end_of_vacuum:
** This file contains code used to help implement virtual tables.
*/
#ifndef SQLITE_OMIT_VIRTUALTABLE
+/* #include "sqliteInt.h" */
/*
** Before a virtual table xCreate() or xConnect() method is invoked, the
@@ -99299,8 +122563,10 @@ end_of_vacuum:
** are invoked only from within xCreate and xConnect methods.
*/
struct VtabCtx {
- Table *pTab;
- VTable *pVTable;
+ VTable *pVTable; /* The virtual table being constructed */
+ Table *pTab; /* The Table object to which the virtual table belongs */
+ VtabCtx *pPrior; /* Parent context (if any) */
+ int bDeclared; /* True after sqlite3_declare_vtab() is called */
};
/*
@@ -99315,33 +122581,36 @@ static int createModule(
void *pAux, /* Context pointer for xCreate/xConnect */
void (*xDestroy)(void *) /* Module destructor function */
){
- int rc, nName;
- Module *pMod;
+ int rc = SQLITE_OK;
+ int nName;
sqlite3_mutex_enter(db->mutex);
nName = sqlite3Strlen30(zName);
- pMod = (Module *)sqlite3DbMallocRaw(db, sizeof(Module) + nName + 1);
- if( pMod ){
- Module *pDel;
- char *zCopy = (char *)(&pMod[1]);
- memcpy(zCopy, zName, nName+1);
- pMod->zName = zCopy;
- pMod->pModule = pModule;
- pMod->pAux = pAux;
- pMod->xDestroy = xDestroy;
- pDel = (Module *)sqlite3HashInsert(&db->aModule, zCopy, nName, (void*)pMod);
- if( pDel && pDel->xDestroy ){
- sqlite3ResetInternalSchema(db, -1);
- pDel->xDestroy(pDel->pAux);
- }
- sqlite3DbFree(db, pDel);
- if( pDel==pMod ){
- db->mallocFailed = 1;
- }
- }else if( xDestroy ){
- xDestroy(pAux);
- }
- rc = sqlite3ApiExit(db, SQLITE_OK);
+ if( sqlite3HashFind(&db->aModule, zName) ){
+ rc = SQLITE_MISUSE_BKPT;
+ }else{
+ Module *pMod;
+ pMod = (Module *)sqlite3DbMallocRawNN(db, sizeof(Module) + nName + 1);
+ if( pMod ){
+ Module *pDel;
+ char *zCopy = (char *)(&pMod[1]);
+ memcpy(zCopy, zName, nName+1);
+ pMod->zName = zCopy;
+ pMod->pModule = pModule;
+ pMod->pAux = pAux;
+ pMod->xDestroy = xDestroy;
+ pMod->pEpoTab = 0;
+ pDel = (Module *)sqlite3HashInsert(&db->aModule,zCopy,(void*)pMod);
+ assert( pDel==0 || pDel==pMod );
+ if( pDel ){
+ sqlite3OomFault(db);
+ sqlite3DbFree(db, pDel);
+ }
+ }
+ }
+ rc = sqlite3ApiExit(db, rc);
+ if( rc!=SQLITE_OK && xDestroy ) xDestroy(pAux);
+
sqlite3_mutex_leave(db->mutex);
return rc;
}
@@ -99356,6 +122625,9 @@ SQLITE_API int sqlite3_create_module(
const sqlite3_module *pModule, /* The definition of the module */
void *pAux /* Context pointer for xCreate/xConnect */
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
+#endif
return createModule(db, zName, pModule, pAux, 0);
}
@@ -99369,6 +122641,9 @@ SQLITE_API int sqlite3_create_module_v2(
void *pAux, /* Context pointer for xCreate/xConnect */
void (*xDestroy)(void *) /* Module destructor function */
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
+#endif
return createModule(db, zName, pModule, pAux, xDestroy);
}
@@ -99406,7 +122681,7 @@ SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){
assert( db );
assert( pVTab->nRef>0 );
- assert( sqlite3SafetyCheckOk(db) );
+ assert( db->magic==SQLITE_MAGIC_OPEN || db->magic==SQLITE_MAGIC_ZOMBIE );
pVTab->nRef--;
if( pVTab->nRef==0 ){
@@ -99457,6 +122732,31 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){
return pRet;
}
+/*
+** Table *p is a virtual table. This function removes the VTable object
+** for table *p associated with database connection db from the linked
+** list in p->pVTab. It also decrements the VTable ref count. This is
+** used when closing database connection db to free all of its VTable
+** objects without disturbing the rest of the Schema object (which may
+** be being used by other shared-cache connections).
+*/
+SQLITE_PRIVATE void sqlite3VtabDisconnect(sqlite3 *db, Table *p){
+ VTable **ppVTab;
+
+ assert( IsVirtual(p) );
+ assert( sqlite3BtreeHoldsAllMutexes(db) );
+ assert( sqlite3_mutex_held(db->mutex) );
+
+ for(ppVTab=&p->pVTable; *ppVTab; ppVTab=&(*ppVTab)->pNext){
+ if( (*ppVTab)->db==db ){
+ VTable *pVTab = *ppVTab;
+ *ppVTab = pVTab->pNext;
+ sqlite3VtabUnlock(pVTab);
+ break;
+ }
+ }
+}
+
/*
** Disconnect all the virtual table objects in the sqlite3.pDisconnect list.
@@ -99514,7 +122814,7 @@ SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){
if( p->azModuleArg ){
int i;
for(i=0; i<p->nModuleArg; i++){
- sqlite3DbFree(db, p->azModuleArg[i]);
+ if( i!=1 ) sqlite3DbFree(db, p->azModuleArg[i]);
}
sqlite3DbFree(db, p->azModuleArg);
}
@@ -99527,23 +122827,17 @@ SQLITE_PRIVATE void sqlite3VtabClear(sqlite3 *db, Table *p){
** deleted.
*/
static void addModuleArgument(sqlite3 *db, Table *pTable, char *zArg){
- int i = pTable->nModuleArg++;
- int nBytes = sizeof(char *)*(1+pTable->nModuleArg);
+ int nBytes = sizeof(char *)*(2+pTable->nModuleArg);
char **azModuleArg;
azModuleArg = sqlite3DbRealloc(db, pTable->azModuleArg, nBytes);
if( azModuleArg==0 ){
- int j;
- for(j=0; j<i; j++){
- sqlite3DbFree(db, pTable->azModuleArg[j]);
- }
sqlite3DbFree(db, zArg);
- sqlite3DbFree(db, pTable->azModuleArg);
- pTable->nModuleArg = 0;
}else{
+ int i = pTable->nModuleArg++;
azModuleArg[i] = zArg;
azModuleArg[i+1] = 0;
+ pTable->azModuleArg = azModuleArg;
}
- pTable->azModuleArg = azModuleArg;
}
/*
@@ -99555,13 +122849,14 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
Parse *pParse, /* Parsing context */
Token *pName1, /* Name of new table, or database name */
Token *pName2, /* Name of new table or NULL */
- Token *pModuleName /* Name of the module for the virtual table */
+ Token *pModuleName, /* Name of the module for the virtual table */
+ int ifNotExists /* No error if the table already exists */
){
int iDb; /* The database the table is being created in */
Table *pTable; /* The new virtual table */
sqlite3 *db; /* Database connection */
- sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0);
+ sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, ifNotExists);
pTable = pParse->pNewTable;
if( pTable==0 ) return;
assert( 0==pTable->pIndex );
@@ -99573,9 +122868,14 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
pTable->tabFlags |= TF_Virtual;
pTable->nModuleArg = 0;
addModuleArgument(db, pTable, sqlite3NameFromToken(db, pModuleName));
- addModuleArgument(db, pTable, sqlite3DbStrDup(db, db->aDb[iDb].zName));
+ addModuleArgument(db, pTable, 0);
addModuleArgument(db, pTable, sqlite3DbStrDup(db, pTable->zName));
- pParse->sNameToken.n = (int)(&pModuleName->z[pModuleName->n] - pName1->z);
+ assert( (pParse->sNameToken.z==pName2->z && pName2->z!=0)
+ || (pParse->sNameToken.z==pName1->z && pName2->z==0)
+ );
+ pParse->sNameToken.n = (int)(
+ &pModuleName->z[pModuleName->n] - pParse->sNameToken.z
+ );
#ifndef SQLITE_OMIT_AUTHORIZATION
/* Creating a virtual table invokes the authorization callback twice.
@@ -99585,7 +122885,7 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
*/
if( pTable->azModuleArg ){
sqlite3AuthCheck(pParse, SQLITE_CREATE_VTABLE, pTable->zName,
- pTable->azModuleArg[0], pParse->db->aDb[iDb].zName);
+ pTable->azModuleArg[0], pParse->db->aDb[iDb].zDbSName);
}
#endif
}
@@ -99596,7 +122896,7 @@ SQLITE_PRIVATE void sqlite3VtabBeginParse(
** virtual table currently under construction in pParse->pTable.
*/
static void addArgumentToVtab(Parse *pParse){
- if( pParse->sArg.z && ALWAYS(pParse->pNewTable) ){
+ if( pParse->sArg.z && pParse->pNewTable ){
const char *z = (const char*)pParse->sArg.z;
int n = pParse->sArg.n;
sqlite3 *db = pParse->db;
@@ -99627,6 +122927,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
char *zStmt;
char *zWhere;
int iDb;
+ int iReg;
Vdbe *v;
/* Compute the complete text of the CREATE VIRTUAL TABLE statement */
@@ -99648,7 +122949,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
"UPDATE %Q.%s "
"SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q "
"WHERE rowid=#%d",
- db->aDb[iDb].zName, SCHEMA_TABLE(iDb),
+ db->aDb[iDb].zDbSName, SCHEMA_TABLE(iDb),
pTab->zName,
pTab->zName,
zStmt,
@@ -99658,11 +122959,13 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
v = sqlite3GetVdbe(pParse);
sqlite3ChangeCookie(pParse, iDb);
- sqlite3VdbeAddOp2(v, OP_Expire, 0, 0);
+ sqlite3VdbeAddOp0(v, OP_Expire);
zWhere = sqlite3MPrintf(db, "name='%q' AND type='table'", pTab->zName);
sqlite3VdbeAddParseSchemaOp(v, iDb, zWhere);
- sqlite3VdbeAddOp4(v, OP_VCreate, iDb, 0, 0,
- pTab->zName, sqlite3Strlen30(pTab->zName) + 1);
+
+ iReg = ++pParse->nMem;
+ sqlite3VdbeLoadString(v, iReg, pTab->zName);
+ sqlite3VdbeAddOp2(v, OP_VCreate, iDb, iReg);
}
/* If we are rereading the sqlite_master table create the in-memory
@@ -99674,11 +122977,10 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
Table *pOld;
Schema *pSchema = pTab->pSchema;
const char *zName = pTab->zName;
- int nName = sqlite3Strlen30(zName);
assert( sqlite3SchemaMutexHeld(db, 0, pSchema) );
- pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab);
+ pOld = sqlite3HashInsert(&pSchema->tblHash, zName, pTab);
if( pOld ){
- db->mallocFailed = 1;
+ sqlite3OomFault(db);
assert( pTab==pOld ); /* Malloc must have failed inside HashInsert() */
return;
}
@@ -99706,7 +123008,7 @@ SQLITE_PRIVATE void sqlite3VtabArgExtend(Parse *pParse, Token *p){
pArg->z = p->z;
pArg->n = p->n;
}else{
- assert(pArg->z < p->z);
+ assert(pArg->z <= p->z);
pArg->n = (int)(&p->z[p->n] - pArg->z);
}
}
@@ -99729,29 +123031,48 @@ static int vtabCallConstructor(
const char *const*azArg = (const char *const*)pTab->azModuleArg;
int nArg = pTab->nModuleArg;
char *zErr = 0;
- char *zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
+ char *zModuleName;
+ int iDb;
+ VtabCtx *pCtx;
+
+ /* Check that the virtual-table is not already being initialized */
+ for(pCtx=db->pVtabCtx; pCtx; pCtx=pCtx->pPrior){
+ if( pCtx->pTab==pTab ){
+ *pzErr = sqlite3MPrintf(db,
+ "vtable constructor called recursively: %s", pTab->zName
+ );
+ return SQLITE_LOCKED;
+ }
+ }
+ zModuleName = sqlite3MPrintf(db, "%s", pTab->zName);
if( !zModuleName ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pVTable = sqlite3DbMallocZero(db, sizeof(VTable));
if( !pVTable ){
sqlite3DbFree(db, zModuleName);
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
pVTable->db = db;
pVTable->pMod = pMod;
+ iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ pTab->azModuleArg[1] = db->aDb[iDb].zDbSName;
+
/* Invoke the virtual table constructor */
assert( &db->pVtabCtx );
assert( xConstruct );
sCtx.pTab = pTab;
sCtx.pVTable = pVTable;
+ sCtx.pPrior = db->pVtabCtx;
+ sCtx.bDeclared = 0;
db->pVtabCtx = &sCtx;
rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr);
- db->pVtabCtx = 0;
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
+ db->pVtabCtx = sCtx.pPrior;
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
+ assert( sCtx.pTab==pTab );
if( SQLITE_OK!=rc ){
if( zErr==0 ){
@@ -99764,37 +123085,36 @@ static int vtabCallConstructor(
}else if( ALWAYS(pVTable->pVtab) ){
/* Justification of ALWAYS(): A correct vtab constructor must allocate
** the sqlite3_vtab object if successful. */
+ memset(pVTable->pVtab, 0, sizeof(pVTable->pVtab[0]));
pVTable->pVtab->pModule = pMod->pModule;
pVTable->nRef = 1;
- if( sCtx.pTab ){
+ if( sCtx.bDeclared==0 ){
const char *zFormat = "vtable constructor did not declare schema: %s";
*pzErr = sqlite3MPrintf(db, zFormat, pTab->zName);
sqlite3VtabUnlock(pVTable);
rc = SQLITE_ERROR;
}else{
int iCol;
+ u8 oooHidden = 0;
/* If everything went according to plan, link the new VTable structure
** into the linked list headed by pTab->pVTable. Then loop through the
** columns of the table to see if any of them contain the token "hidden".
- ** If so, set the Column.isHidden flag and remove the token from
+ ** If so, set the Column COLFLAG_HIDDEN flag and remove the token from
** the type string. */
pVTable->pNext = pTab->pVTable;
pTab->pVTable = pVTable;
for(iCol=0; iCol<pTab->nCol; iCol++){
- char *zType = pTab->aCol[iCol].zType;
+ char *zType = sqlite3ColumnType(&pTab->aCol[iCol], "");
int nType;
int i = 0;
- if( !zType ) continue;
nType = sqlite3Strlen30(zType);
- if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){
- for(i=0; i<nType; i++){
- if( (0==sqlite3StrNICmp(" hidden", &zType[i], 7))
- && (zType[i+7]=='\0' || zType[i+7]==' ')
- ){
- i++;
- break;
- }
+ for(i=0; i<nType; i++){
+ if( 0==sqlite3StrNICmp("hidden", &zType[i], 6)
+ && (i==0 || zType[i-1]==' ')
+ && (zType[i+6]=='\0' || zType[i+6]==' ')
+ ){
+ break;
}
}
if( i<nType ){
@@ -99807,7 +123127,10 @@ static int vtabCallConstructor(
assert(zType[i-1]==' ');
zType[i-1] = '\0';
}
- pTab->aCol[iCol].isHidden = 1;
+ pTab->aCol[iCol].colFlags |= COLFLAG_HIDDEN;
+ oooHidden = TF_OOOHidden;
+ }else{
+ pTab->tabFlags |= oooHidden;
}
}
}
@@ -99837,7 +123160,7 @@ SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){
/* Locate the required virtual table module */
zMod = pTab->azModuleArg[0];
- pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod));
+ pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
if( !pMod ){
const char *zModule = pTab->azModuleArg[0];
@@ -99867,7 +123190,7 @@ static int growVTrans(sqlite3 *db){
int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR);
aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes);
if( !aVTrans ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
memset(&aVTrans[db->nVTrans], 0, sizeof(sqlite3_vtab *)*ARRAY_INCR);
db->aVTrans = aVTrans;
@@ -99890,7 +123213,7 @@ static void addToVTrans(sqlite3 *db, VTable *pVTab){
** This function is invoked by the vdbe to call the xCreate method
** of the virtual table named zTab in database iDb.
**
-** If an error occurs, *pzErr is set to point an an English language
+** If an error occurs, *pzErr is set to point to an English language
** description of the error and an SQLITE_XXX error code is returned.
** In this case the caller must call sqlite3DbFree(db, ) on *pzErr.
*/
@@ -99900,18 +123223,18 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab,
Module *pMod;
const char *zMod;
- pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
+ pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
assert( pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVTable );
/* Locate the required virtual table module */
zMod = pTab->azModuleArg[0];
- pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod));
+ pMod = (Module*)sqlite3HashFind(&db->aModule, zMod);
/* If the module has been registered and includes a Create method,
** invoke it now. If the module has not been registered, return an
** error. Otherwise, do nothing.
*/
- if( !pMod ){
+ if( pMod==0 || pMod->pModule->xCreate==0 || pMod->pModule->xDestroy==0 ){
*pzErr = sqlite3MPrintf(db, "no such module: %s", zMod);
rc = SQLITE_ERROR;
}else{
@@ -99936,23 +123259,30 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3 *db, int iDb, const char *zTab,
** virtual table module.
*/
SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
+ VtabCtx *pCtx;
Parse *pParse;
-
int rc = SQLITE_OK;
Table *pTab;
char *zErr = 0;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zCreateTable==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
- if( !db->pVtabCtx || !(pTab = db->pVtabCtx->pTab) ){
- sqlite3Error(db, SQLITE_MISUSE, 0);
+ pCtx = db->pVtabCtx;
+ if( !pCtx || pCtx->bDeclared ){
+ sqlite3Error(db, SQLITE_MISUSE);
sqlite3_mutex_leave(db->mutex);
return SQLITE_MISUSE_BKPT;
}
+ pTab = pCtx->pTab;
assert( (pTab->tabFlags & TF_Virtual)!=0 );
pParse = sqlite3StackAllocZero(db, sizeof(*pParse));
if( pParse==0 ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}else{
pParse->declareVtab = 1;
pParse->db = db;
@@ -99965,14 +123295,28 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
&& (pParse->pNewTable->tabFlags & TF_Virtual)==0
){
if( !pTab->aCol ){
- pTab->aCol = pParse->pNewTable->aCol;
- pTab->nCol = pParse->pNewTable->nCol;
- pParse->pNewTable->nCol = 0;
- pParse->pNewTable->aCol = 0;
+ Table *pNew = pParse->pNewTable;
+ Index *pIdx;
+ pTab->aCol = pNew->aCol;
+ pTab->nCol = pNew->nCol;
+ pTab->tabFlags |= pNew->tabFlags & (TF_WithoutRowid|TF_NoVisibleRowid);
+ pNew->nCol = 0;
+ pNew->aCol = 0;
+ assert( pTab->pIndex==0 );
+ if( !HasRowid(pNew) && pCtx->pVTable->pMod->pModule->xUpdate!=0 ){
+ rc = SQLITE_ERROR;
+ }
+ pIdx = pNew->pIndex;
+ if( pIdx ){
+ assert( pIdx->pNext==0 );
+ pTab->pIndex = pIdx;
+ pNew->pIndex = 0;
+ pIdx->pTable = pTab;
+ }
}
- db->pVtabCtx->pTab = 0;
+ pCtx->bDeclared = 1;
}else{
- sqlite3Error(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, (zErr ? "%s" : 0), zErr);
sqlite3DbFree(db, zErr);
rc = SQLITE_ERROR;
}
@@ -99982,6 +123326,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
sqlite3VdbeFinalize(pParse->pVdbe);
}
sqlite3DeleteTable(db, pParse->pNewTable);
+ sqlite3ParserReset(pParse);
sqlite3StackFree(db, pParse);
}
@@ -100002,13 +123347,20 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
int rc = SQLITE_OK;
Table *pTab;
- pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName);
- if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){
- VTable *p = vtabDisconnectAll(db, pTab);
-
- assert( rc==SQLITE_OK );
- rc = p->pMod->pModule->xDestroy(p->pVtab);
-
+ pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zDbSName);
+ if( pTab!=0 && ALWAYS(pTab->pVTable!=0) ){
+ VTable *p;
+ int (*xDestroy)(sqlite3_vtab *);
+ for(p=pTab->pVTable; p; p=p->pNext){
+ assert( p->pVtab );
+ if( p->pVtab->nRef>0 ){
+ return SQLITE_LOCKED;
+ }
+ }
+ p = vtabDisconnectAll(db, pTab);
+ xDestroy = p->pMod->pModule->xDestroy;
+ assert( xDestroy!=0 ); /* Checked before the virtual table is created */
+ rc = xDestroy(p->pVtab);
/* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */
if( rc==SQLITE_OK ){
assert( pTab->pVTable==p && p->pNext==0 );
@@ -100032,8 +123384,10 @@ SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3 *db, int iDb, const char *zTab
static void callFinaliser(sqlite3 *db, int offset){
int i;
if( db->aVTrans ){
+ VTable **aVTrans = db->aVTrans;
+ db->aVTrans = 0;
for(i=0; i<db->nVTrans; i++){
- VTable *pVTab = db->aVTrans[i];
+ VTable *pVTab = aVTrans[i];
sqlite3_vtab *p = pVTab->pVtab;
if( p ){
int (*x)(sqlite3_vtab *);
@@ -100043,9 +123397,8 @@ static void callFinaliser(sqlite3 *db, int offset){
pVTab->iSavepoint = 0;
sqlite3VtabUnlock(pVTab);
}
- sqlite3DbFree(db, db->aVTrans);
+ sqlite3DbFree(db, aVTrans);
db->nVTrans = 0;
- db->aVTrans = 0;
}
}
@@ -100054,10 +123407,9 @@ static void callFinaliser(sqlite3 *db, int offset){
** array. Return the error code for the first error that occurs, or
** SQLITE_OK if all xSync operations are successful.
**
-** Set *pzErrmsg to point to a buffer that should be released using
-** sqlite3DbFree() containing an error message, if one is available.
+** If an error message is available, leave it in p->zErrMsg.
*/
-SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){
+SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, Vdbe *p){
int i;
int rc = SQLITE_OK;
VTable **aVTrans = db->aVTrans;
@@ -100068,9 +123420,7 @@ SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **pzErrmsg){
sqlite3_vtab *pVtab = aVTrans[i]->pVtab;
if( pVtab && (x = pVtab->pModule->xSync)!=0 ){
rc = x(pVtab);
- sqlite3DbFree(db, *pzErrmsg);
- *pzErrmsg = sqlite3DbStrDup(db, pVtab->zErrMsg);
- sqlite3_free(pVtab->zErrMsg);
+ sqlite3VtabImportErrmsg(p, pVtab);
}
}
db->aVTrans = aVTrans;
@@ -100136,7 +123486,12 @@ SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){
if( rc==SQLITE_OK ){
rc = pModule->xBegin(pVTab->pVtab);
if( rc==SQLITE_OK ){
+ int iSvpt = db->nStatement + db->nSavepoint;
addToVTrans(db, pVTab);
+ if( iSvpt && pModule->xSavepoint ){
+ pVTab->iSavepoint = iSvpt;
+ rc = pModule->xSavepoint(pVTab->pVtab, iSvpt-1);
+ }
}
}
}
@@ -100162,13 +123517,13 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
int rc = SQLITE_OK;
assert( op==SAVEPOINT_RELEASE||op==SAVEPOINT_ROLLBACK||op==SAVEPOINT_BEGIN );
- assert( iSavepoint>=0 );
+ assert( iSavepoint>=-1 );
if( db->aVTrans ){
int i;
for(i=0; rc==SQLITE_OK && i<db->nVTrans; i++){
VTable *pVTab = db->aVTrans[i];
const sqlite3_module *pMod = pVTab->pMod->pModule;
- if( pMod->iVersion>=2 ){
+ if( pVTab->pVtab && pMod->iVersion>=2 ){
int (*xMethod)(sqlite3_vtab *, int);
switch( op ){
case SAVEPOINT_BEGIN:
@@ -100183,7 +123538,7 @@ SQLITE_PRIVATE int sqlite3VtabSavepoint(sqlite3 *db, int op, int iSavepoint){
break;
}
if( xMethod && pVTab->iSavepoint>iSavepoint ){
- rc = xMethod(db->aVTrans[i]->pVtab, iSavepoint);
+ rc = xMethod(pVTab->pVtab, iSavepoint);
}
}
}
@@ -100213,7 +123568,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
Table *pTab;
sqlite3_vtab *pVtab;
sqlite3_module *pMod;
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**) = 0;
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**) = 0;
void *pArg = 0;
FuncDef *pNew;
int rc = 0;
@@ -100241,7 +123596,7 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
for(z=(unsigned char*)zLowerName; *z; z++){
*z = sqlite3UpperToLower[*z];
}
- rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xFunc, &pArg);
+ rc = pMod->xFindFunction(pVtab, nArg, zLowerName, &xSFunc, &pArg);
sqlite3DbFree(db, zLowerName);
}
if( rc==0 ){
@@ -100256,11 +123611,11 @@ SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(
return pDef;
}
*pNew = *pDef;
- pNew->zName = (char *)&pNew[1];
- memcpy(pNew->zName, pDef->zName, sqlite3Strlen30(pDef->zName)+1);
- pNew->xFunc = xFunc;
+ pNew->zName = (const char*)&pNew[1];
+ memcpy((char*)&pNew[1], pDef->zName, sqlite3Strlen30(pDef->zName)+1);
+ pNew->xSFunc = xSFunc;
pNew->pUserData = pArg;
- pNew->flags |= SQLITE_FUNC_EPHEM;
+ pNew->funcFlags |= SQLITE_FUNC_EPHEM;
return pNew;
}
@@ -100280,12 +123635,76 @@ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){
if( pTab==pToplevel->apVtabLock[i] ) return;
}
n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]);
- apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n);
+ apVtabLock = sqlite3_realloc64(pToplevel->apVtabLock, n);
if( apVtabLock ){
pToplevel->apVtabLock = apVtabLock;
pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab;
}else{
- pToplevel->db->mallocFailed = 1;
+ sqlite3OomFault(pToplevel->db);
+ }
+}
+
+/*
+** Check to see if virtual table module pMod can be have an eponymous
+** virtual table instance. If it can, create one if one does not already
+** exist. Return non-zero if the eponymous virtual table instance exists
+** when this routine returns, and return zero if it does not exist.
+**
+** An eponymous virtual table instance is one that is named after its
+** module, and more importantly, does not require a CREATE VIRTUAL TABLE
+** statement in order to come into existance. Eponymous virtual table
+** instances always exist. They cannot be DROP-ed.
+**
+** Any virtual table module for which xConnect and xCreate are the same
+** method can have an eponymous virtual table instance.
+*/
+SQLITE_PRIVATE int sqlite3VtabEponymousTableInit(Parse *pParse, Module *pMod){
+ const sqlite3_module *pModule = pMod->pModule;
+ Table *pTab;
+ char *zErr = 0;
+ int rc;
+ sqlite3 *db = pParse->db;
+ if( pMod->pEpoTab ) return 1;
+ if( pModule->xCreate!=0 && pModule->xCreate!=pModule->xConnect ) return 0;
+ pTab = sqlite3DbMallocZero(db, sizeof(Table));
+ if( pTab==0 ) return 0;
+ pTab->zName = sqlite3DbStrDup(db, pMod->zName);
+ if( pTab->zName==0 ){
+ sqlite3DbFree(db, pTab);
+ return 0;
+ }
+ pMod->pEpoTab = pTab;
+ pTab->nRef = 1;
+ pTab->pSchema = db->aDb[0].pSchema;
+ pTab->tabFlags |= TF_Virtual;
+ pTab->nModuleArg = 0;
+ pTab->iPKey = -1;
+ addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
+ addModuleArgument(db, pTab, 0);
+ addModuleArgument(db, pTab, sqlite3DbStrDup(db, pTab->zName));
+ rc = vtabCallConstructor(db, pTab, pMod, pModule->xConnect, &zErr);
+ if( rc ){
+ sqlite3ErrorMsg(pParse, "%s", zErr);
+ sqlite3DbFree(db, zErr);
+ sqlite3VtabEponymousTableClear(db, pMod);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+** Erase the eponymous virtual table instance associated with
+** virtual table module pMod, if it exists.
+*/
+SQLITE_PRIVATE void sqlite3VtabEponymousTableClear(sqlite3 *db, Module *pMod){
+ Table *pTab = pMod->pEpoTab;
+ if( pTab!=0 ){
+ /* Mark the table as Ephemeral prior to deleting it, so that the
+ ** sqlite3DeleteTable() routine will know that it is not stored in
+ ** the schema. */
+ pTab->tabFlags |= TF_Ephemeral;
+ sqlite3DeleteTable(db, pTab);
+ pMod->pEpoTab = 0;
}
}
@@ -100300,6 +123719,9 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *db){
static const unsigned char aMap[] = {
SQLITE_ROLLBACK, SQLITE_ABORT, SQLITE_FAIL, SQLITE_IGNORE, SQLITE_REPLACE
};
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
assert( OE_Rollback==1 && OE_Abort==2 && OE_Fail==3 );
assert( OE_Ignore==4 && OE_Replace==5 );
assert( db->vtabOnConflict>=1 && db->vtabOnConflict<=5 );
@@ -100315,8 +123737,10 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
va_list ap;
int rc = SQLITE_OK;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
-
va_start(ap, op);
switch( op ){
case SQLITE_VTAB_CONSTRAINT_SUPPORT: {
@@ -100335,7 +123759,7 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
}
va_end(ap);
- if( rc!=SQLITE_OK ) sqlite3Error(db, rc, 0);
+ if( rc!=SQLITE_OK ) sqlite3Error(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
@@ -100343,9 +123767,9 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
#endif /* SQLITE_OMIT_VIRTUALTABLE */
/************** End of vtab.c ************************************************/
-/************** Begin file where.c *******************************************/
+/************** Begin file wherecode.c ***************************************/
/*
-** 2001 September 15
+** 2015-06-06
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
@@ -100356,33 +123780,210 @@ SQLITE_API int sqlite3_vtab_config(sqlite3 *db, int op, ...){
**
*************************************************************************
** This module contains C code that generates VDBE code used to process
-** the WHERE clause of SQL statements. This module is responsible for
-** generating the code that loops through a table looking for applicable
-** rows. Indices are selected and used to speed the search when doing
-** so is applicable. Because this module is responsible for selecting
-** indices, you might also think of this module as the "query optimizer".
+** the WHERE clause of SQL statements.
+**
+** This file was split off from where.c on 2015-06-06 in order to reduce the
+** size of where.c and make it easier to edit. This file contains the routines
+** that actually generate the bulk of the WHERE loop code. The original where.c
+** file retains the code that does query planning and analysis.
+*/
+/* #include "sqliteInt.h" */
+/************** Include whereInt.h in the middle of wherecode.c **************/
+/************** Begin file whereInt.h ****************************************/
+/*
+** 2013-11-12
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains structure and macro definitions for the query
+** planner logic in "where.c". These definitions are broken out into
+** a separate source file for easier editing.
*/
-
/*
** Trace output macros
*/
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
-SQLITE_PRIVATE int sqlite3WhereTrace = 0;
+/***/ int sqlite3WhereTrace;
#endif
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
-# define WHERETRACE(X) if(sqlite3WhereTrace) sqlite3DebugPrintf X
+#if defined(SQLITE_DEBUG) \
+ && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
+# define WHERETRACE(K,X) if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
+# define WHERETRACE_ENABLED 1
#else
-# define WHERETRACE(X)
+# define WHERETRACE(K,X)
#endif
-/* Forward reference
+/* Forward references
*/
typedef struct WhereClause WhereClause;
typedef struct WhereMaskSet WhereMaskSet;
typedef struct WhereOrInfo WhereOrInfo;
typedef struct WhereAndInfo WhereAndInfo;
-typedef struct WhereCost WhereCost;
+typedef struct WhereLevel WhereLevel;
+typedef struct WhereLoop WhereLoop;
+typedef struct WherePath WherePath;
+typedef struct WhereTerm WhereTerm;
+typedef struct WhereLoopBuilder WhereLoopBuilder;
+typedef struct WhereScan WhereScan;
+typedef struct WhereOrCost WhereOrCost;
+typedef struct WhereOrSet WhereOrSet;
+
+/*
+** This object contains information needed to implement a single nested
+** loop in WHERE clause.
+**
+** Contrast this object with WhereLoop. This object describes the
+** implementation of the loop. WhereLoop describes the algorithm.
+** This object contains a pointer to the WhereLoop algorithm as one of
+** its elements.
+**
+** The WhereInfo object contains a single instance of this object for
+** each term in the FROM clause (which is to say, for each of the
+** nested loops as implemented). The order of WhereLevel objects determines
+** the loop nested order, with WhereInfo.a[0] being the outer loop and
+** WhereInfo.a[WhereInfo.nLevel-1] being the inner loop.
+*/
+struct WhereLevel {
+ int iLeftJoin; /* Memory cell used to implement LEFT OUTER JOIN */
+ int iTabCur; /* The VDBE cursor used to access the table */
+ int iIdxCur; /* The VDBE cursor used to access pIdx */
+ int addrBrk; /* Jump here to break out of the loop */
+ int addrNxt; /* Jump here to start the next IN combination */
+ int addrSkip; /* Jump here for next iteration of skip-scan */
+ int addrCont; /* Jump here to continue with the next loop cycle */
+ int addrFirst; /* First instruction of interior of the loop */
+ int addrBody; /* Beginning of the body of this loop */
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ u32 iLikeRepCntr; /* LIKE range processing counter register (times 2) */
+ int addrLikeRep; /* LIKE range processing address */
+#endif
+ u8 iFrom; /* Which entry in the FROM clause */
+ u8 op, p3, p5; /* Opcode, P3 & P5 of the opcode that ends the loop */
+ int p1, p2; /* Operands of the opcode used to ends the loop */
+ union { /* Information that depends on pWLoop->wsFlags */
+ struct {
+ int nIn; /* Number of entries in aInLoop[] */
+ struct InLoop {
+ int iCur; /* The VDBE cursor used by this IN operator */
+ int addrInTop; /* Top of the IN loop */
+ u8 eEndLoopOp; /* IN Loop terminator. OP_Next or OP_Prev */
+ } *aInLoop; /* Information about each nested IN operator */
+ } in; /* Used when pWLoop->wsFlags&WHERE_IN_ABLE */
+ Index *pCovidx; /* Possible covering index for WHERE_MULTI_OR */
+ } u;
+ struct WhereLoop *pWLoop; /* The selected WhereLoop object */
+ Bitmask notReady; /* FROM entries not usable at this level */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ int addrVisit; /* Address at which row is visited */
+#endif
+};
+
+/*
+** Each instance of this object represents an algorithm for evaluating one
+** term of a join. Every term of the FROM clause will have at least
+** one corresponding WhereLoop object (unless INDEXED BY constraints
+** prevent a query solution - which is an error) and many terms of the
+** FROM clause will have multiple WhereLoop objects, each describing a
+** potential way of implementing that FROM-clause term, together with
+** dependencies and cost estimates for using the chosen algorithm.
+**
+** Query planning consists of building up a collection of these WhereLoop
+** objects, then computing a particular sequence of WhereLoop objects, with
+** one WhereLoop object per FROM clause term, that satisfy all dependencies
+** and that minimize the overall cost.
+*/
+struct WhereLoop {
+ Bitmask prereq; /* Bitmask of other loops that must run first */
+ Bitmask maskSelf; /* Bitmask identifying table iTab */
+#ifdef SQLITE_DEBUG
+ char cId; /* Symbolic ID of this loop for debugging use */
+#endif
+ u8 iTab; /* Position in FROM clause of table for this loop */
+ u8 iSortIdx; /* Sorting index number. 0==None */
+ LogEst rSetup; /* One-time setup cost (ex: create transient index) */
+ LogEst rRun; /* Cost of running each loop */
+ LogEst nOut; /* Estimated number of output rows */
+ union {
+ struct { /* Information for internal btree tables */
+ u16 nEq; /* Number of equality constraints */
+ u16 nBtm; /* Size of BTM vector */
+ u16 nTop; /* Size of TOP vector */
+ Index *pIndex; /* Index used, or NULL */
+ } btree;
+ struct { /* Information for virtual tables */
+ int idxNum; /* Index number */
+ u8 needFree; /* True if sqlite3_free(idxStr) is needed */
+ i8 isOrdered; /* True if satisfies ORDER BY */
+ u16 omitMask; /* Terms that may be omitted */
+ char *idxStr; /* Index identifier string */
+ } vtab;
+ } u;
+ u32 wsFlags; /* WHERE_* flags describing the plan */
+ u16 nLTerm; /* Number of entries in aLTerm[] */
+ u16 nSkip; /* Number of NULL aLTerm[] entries */
+ /**** whereLoopXfer() copies fields above ***********************/
+# define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot)
+ u16 nLSlot; /* Number of slots allocated for aLTerm[] */
+ WhereTerm **aLTerm; /* WhereTerms used */
+ WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */
+ WhereTerm *aLTermSpace[3]; /* Initial aLTerm[] space */
+};
+
+/* This object holds the prerequisites and the cost of running a
+** subquery on one operand of an OR operator in the WHERE clause.
+** See WhereOrSet for additional information
+*/
+struct WhereOrCost {
+ Bitmask prereq; /* Prerequisites */
+ LogEst rRun; /* Cost of running this subquery */
+ LogEst nOut; /* Number of outputs for this subquery */
+};
+
+/* The WhereOrSet object holds a set of possible WhereOrCosts that
+** correspond to the subquery(s) of OR-clause processing. Only the
+** best N_OR_COST elements are retained.
+*/
+#define N_OR_COST 3
+struct WhereOrSet {
+ u16 n; /* Number of valid a[] entries */
+ WhereOrCost a[N_OR_COST]; /* Set of best costs */
+};
+
+/*
+** Each instance of this object holds a sequence of WhereLoop objects
+** that implement some or all of a query plan.
+**
+** Think of each WhereLoop object as a node in a graph with arcs
+** showing dependencies and costs for travelling between nodes. (That is
+** not a completely accurate description because WhereLoop costs are a
+** vector, not a scalar, and because dependencies are many-to-one, not
+** one-to-one as are graph nodes. But it is a useful visualization aid.)
+** Then a WherePath object is a path through the graph that visits some
+** or all of the WhereLoop objects once.
+**
+** The "solver" works by creating the N best WherePath objects of length
+** 1. Then using those as a basis to compute the N best WherePath objects
+** of length 2. And so forth until the length of WherePaths equals the
+** number of nodes in the FROM clause. The best (lowest cost) WherePath
+** at the end is the chosen query plan.
+*/
+struct WherePath {
+ Bitmask maskLoop; /* Bitmask of all WhereLoop objects in this path */
+ Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */
+ LogEst nRow; /* Estimated number of rows generated by this path */
+ LogEst rCost; /* Total cost of this path */
+ LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */
+ i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */
+ WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */
+};
/*
** The query generator uses an array of instances of this structure to
@@ -100410,9 +124011,9 @@ typedef struct WhereCost WhereCost;
**
** (t1.X <op> <expr>) OR (t1.Y <op> <expr>) OR ....
**
-** In this second case, wtFlag as the TERM_ORINFO set and eOperator==WO_OR
+** In this second case, wtFlag has the TERM_ORINFO bit set and eOperator==WO_OR
** and the WhereTerm.u.pOrInfo field points to auxiliary information that
-** is collected about the
+** is collected about the OR clause.
**
** If a term in the WHERE clause does not match either of the two previous
** categories, then eOperator==0. The WhereTerm.pExpr field is still set
@@ -100435,20 +124036,22 @@ typedef struct WhereCost WhereCost;
** in prereqRight and prereqAll. The default is 64 bits, hence SQLite
** is only able to process joins with 64 or fewer tables.
*/
-typedef struct WhereTerm WhereTerm;
struct WhereTerm {
Expr *pExpr; /* Pointer to the subexpression that is this term */
+ WhereClause *pWC; /* The clause this term is part of */
+ LogEst truthProb; /* Probability of truth for this expression */
+ u16 wtFlags; /* TERM_xxx bit flags. See below */
+ u16 eOperator; /* A WO_xx value describing <op> */
+ u8 nChild; /* Number of children that must disable us */
+ u8 eMatchOp; /* Op for vtab MATCH/LIKE/GLOB/REGEXP terms */
int iParent; /* Disable pWC->a[iParent] when this term disabled */
int leftCursor; /* Cursor number of X in "X <op> <expr>" */
+ int iField; /* Field in (?,?,?) IN (SELECT...) vector */
union {
int leftColumn; /* Column number of X in "X <op> <expr>" */
- WhereOrInfo *pOrInfo; /* Extra information if eOperator==WO_OR */
- WhereAndInfo *pAndInfo; /* Extra information if eOperator==WO_AND */
+ WhereOrInfo *pOrInfo; /* Extra information if (eOperator & WO_OR)!=0 */
+ WhereAndInfo *pAndInfo; /* Extra information if (eOperator& WO_AND)!=0 */
} u;
- u16 eOperator; /* A WO_xx value describing <op> */
- u8 wtFlags; /* TERM_xxx bit flags. See below */
- u8 nChild; /* Number of children that must disable us */
- WhereClause *pWC; /* The clause this term is part of */
Bitmask prereqRight; /* Bitmask of tables used by pExpr->pRight */
Bitmask prereqAll; /* Bitmask of tables referenced by pExpr */
};
@@ -100463,20 +124066,49 @@ struct WhereTerm {
#define TERM_ORINFO 0x10 /* Need to free the WhereTerm.u.pOrInfo object */
#define TERM_ANDINFO 0x20 /* Need to free the WhereTerm.u.pAndInfo obj */
#define TERM_OR_OK 0x40 /* Used during OR-clause processing */
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
# define TERM_VNULL 0x80 /* Manufactured x>NULL or x<=NULL term */
#else
-# define TERM_VNULL 0x00 /* Disabled if not using stat2 */
-#endif
+# define TERM_VNULL 0x00 /* Disabled if not using stat3 */
+#endif
+#define TERM_LIKEOPT 0x100 /* Virtual terms from the LIKE optimization */
+#define TERM_LIKECOND 0x200 /* Conditionally this LIKE operator term */
+#define TERM_LIKE 0x400 /* The original LIKE operator */
+#define TERM_IS 0x800 /* Term.pExpr is an IS operator */
+
+/*
+** An instance of the WhereScan object is used as an iterator for locating
+** terms in the WHERE clause that are useful to the query planner.
+*/
+struct WhereScan {
+ WhereClause *pOrigWC; /* Original, innermost WhereClause */
+ WhereClause *pWC; /* WhereClause currently being scanned */
+ const char *zCollName; /* Required collating sequence, if not NULL */
+ Expr *pIdxExpr; /* Search for this index expression */
+ char idxaff; /* Must match this affinity, if zCollName!=NULL */
+ unsigned char nEquiv; /* Number of entries in aEquiv[] */
+ unsigned char iEquiv; /* Next unused slot in aEquiv[] */
+ u32 opMask; /* Acceptable operators */
+ int k; /* Resume scanning at this->pWC->a[this->k] */
+ int aiCur[11]; /* Cursors in the equivalence class */
+ i16 aiColumn[11]; /* Corresponding column number in the eq-class */
+};
/*
** An instance of the following structure holds all information about a
** WHERE clause. Mostly this is a container for one or more WhereTerms.
+**
+** Explanation of pOuter: For a WHERE clause of the form
+**
+** a AND ((b AND c) OR (d AND e)) AND f
+**
+** There are separate WhereClause objects for the whole clause and for
+** the subclauses "(b AND c)" and "(d AND e)". The pOuter field of the
+** subclauses points to the WhereClause object for the whole clause.
*/
struct WhereClause {
- Parse *pParse; /* The parser context */
- WhereMaskSet *pMaskSet; /* Mapping of table cursor numbers to bitmasks */
- Bitmask vmask; /* Bitmask identifying virtual table cursors */
+ WhereInfo *pWInfo; /* WHERE clause processing context */
+ WhereClause *pOuter; /* Outer conjunction */
u8 op; /* Split operator. TK_AND or TK_OR */
int nTerm; /* Number of terms */
int nSlot; /* Number of entries in a[] */
@@ -100537,345 +124169,2335 @@ struct WhereMaskSet {
};
/*
-** A WhereCost object records a lookup strategy and the estimated
-** cost of pursuing that strategy.
+** Initialize a WhereMaskSet object
*/
-struct WhereCost {
- WherePlan plan; /* The lookup strategy */
- double rCost; /* Overall cost of pursuing this search strategy */
- Bitmask used; /* Bitmask of cursors used by this plan */
+#define initMaskSet(P) (P)->n=0
+
+/*
+** This object is a convenience wrapper holding all information needed
+** to construct WhereLoop objects for a particular query.
+*/
+struct WhereLoopBuilder {
+ WhereInfo *pWInfo; /* Information about this WHERE */
+ WhereClause *pWC; /* WHERE clause terms */
+ ExprList *pOrderBy; /* ORDER BY clause */
+ WhereLoop *pNew; /* Template WhereLoop */
+ WhereOrSet *pOrSet; /* Record best loops here, if not NULL */
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ UnpackedRecord *pRec; /* Probe for stat4 (if required) */
+ int nRecValid; /* Number of valid fields currently in pRec */
+#endif
};
/*
-** Bitmasks for the operators that indices are able to exploit. An
-** OR-ed combination of these values can be used when searching for
-** terms in the where clause.
+** The WHERE clause processing routine has two halves. The
+** first part does the start of the WHERE loop and the second
+** half does the tail of the WHERE loop. An instance of
+** this structure is returned by the first half and passed
+** into the second half to give some continuity.
+**
+** An instance of this object holds the complete state of the query
+** planner.
+*/
+struct WhereInfo {
+ Parse *pParse; /* Parsing and code generating context */
+ SrcList *pTabList; /* List of tables in the join */
+ ExprList *pOrderBy; /* The ORDER BY clause or NULL */
+ ExprList *pDistinctSet; /* DISTINCT over all these values */
+ LogEst iLimit; /* LIMIT if wctrlFlags has WHERE_USE_LIMIT */
+ int aiCurOnePass[2]; /* OP_OpenWrite cursors for the ONEPASS opt */
+ int iContinue; /* Jump here to continue with next record */
+ int iBreak; /* Jump here to break out of the loop */
+ int savedNQueryLoop; /* pParse->nQueryLoop outside the WHERE loop */
+ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */
+ u8 nLevel; /* Number of nested loop */
+ i8 nOBSat; /* Number of ORDER BY terms satisfied by indices */
+ u8 sorted; /* True if really sorted (not just grouped) */
+ u8 eOnePass; /* ONEPASS_OFF, or _SINGLE, or _MULTI */
+ u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */
+ u8 eDistinct; /* One of the WHERE_DISTINCT_* values */
+ u8 bOrderedInnerLoop; /* True if only the inner-most loop is ordered */
+ int iTop; /* The very beginning of the WHERE loop */
+ WhereLoop *pLoops; /* List of all WhereLoop objects */
+ Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
+ LogEst nRowOut; /* Estimated number of output rows */
+ WhereClause sWC; /* Decomposition of the WHERE clause */
+ WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
+ WhereLevel a[1]; /* Information about each nest loop in WHERE */
+};
+
+/*
+** Private interfaces - callable only by other where.c routines.
+**
+** where.c:
*/
-#define WO_IN 0x001
-#define WO_EQ 0x002
+SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
+#ifdef WHERETRACE_ENABLED
+SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC);
+#endif
+SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
+ WhereClause *pWC, /* The WHERE clause to be searched */
+ int iCur, /* Cursor number of LHS */
+ int iColumn, /* Column number of LHS */
+ Bitmask notReady, /* RHS must not overlap with this mask */
+ u32 op, /* Mask of WO_xx values describing operator */
+ Index *pIdx /* Must be compatible with this index, if not NULL */
+);
+
+/* wherecode.c: */
+#ifndef SQLITE_OMIT_EXPLAIN
+SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
+ Parse *pParse, /* Parse context */
+ SrcList *pTabList, /* Table list this loop refers to */
+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
+ int iLevel, /* Value for "level" column of output */
+ int iFrom, /* Value for "from" column of output */
+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
+);
+#else
+# define sqlite3WhereExplainOneScan(u,v,w,x,y,z) 0
+#endif /* SQLITE_OMIT_EXPLAIN */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
+ Vdbe *v, /* Vdbe to add scanstatus entry to */
+ SrcList *pSrclist, /* FROM clause pLvl reads data from */
+ WhereLevel *pLvl, /* Level to add scanstatus() entry for */
+ int addrExplain /* Address of OP_Explain (or 0) */
+);
+#else
+# define sqlite3WhereAddScanStatus(a, b, c, d) ((void)d)
+#endif
+SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
+ WhereInfo *pWInfo, /* Complete information about the WHERE clause */
+ int iLevel, /* Which level of pWInfo->a[] should be coded */
+ Bitmask notReady /* Which tables are currently available */
+);
+
+/* whereexpr.c: */
+SQLITE_PRIVATE void sqlite3WhereClauseInit(WhereClause*,WhereInfo*);
+SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause*);
+SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause*,Expr*,u8);
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet*, Expr*);
+SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet*, ExprList*);
+SQLITE_PRIVATE void sqlite3WhereExprAnalyze(SrcList*, WhereClause*);
+SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(Parse*, struct SrcList_item*, WhereClause*);
+
+
+
+
+
+/*
+** Bitmasks for the operators on WhereTerm objects. These are all
+** operators that are of interest to the query planner. An
+** OR-ed combination of these values can be used when searching for
+** particular WhereTerms within a WhereClause.
+**
+** Value constraints:
+** WO_EQ == SQLITE_INDEX_CONSTRAINT_EQ
+** WO_LT == SQLITE_INDEX_CONSTRAINT_LT
+** WO_LE == SQLITE_INDEX_CONSTRAINT_LE
+** WO_GT == SQLITE_INDEX_CONSTRAINT_GT
+** WO_GE == SQLITE_INDEX_CONSTRAINT_GE
+** WO_MATCH == SQLITE_INDEX_CONSTRAINT_MATCH
+*/
+#define WO_IN 0x0001
+#define WO_EQ 0x0002
#define WO_LT (WO_EQ<<(TK_LT-TK_EQ))
#define WO_LE (WO_EQ<<(TK_LE-TK_EQ))
#define WO_GT (WO_EQ<<(TK_GT-TK_EQ))
#define WO_GE (WO_EQ<<(TK_GE-TK_EQ))
-#define WO_MATCH 0x040
-#define WO_ISNULL 0x080
-#define WO_OR 0x100 /* Two or more OR-connected terms */
-#define WO_AND 0x200 /* Two or more AND-connected terms */
-#define WO_NOOP 0x800 /* This term does not restrict search space */
-
-#define WO_ALL 0xfff /* Mask of all possible WO_* values */
-#define WO_SINGLE 0x0ff /* Mask of all non-compound WO_* values */
-
-/*
-** Value for wsFlags returned by bestIndex() and stored in
-** WhereLevel.wsFlags. These flags determine which search
-** strategies are appropriate.
-**
-** The least significant 12 bits is reserved as a mask for WO_ values above.
-** The WhereLevel.wsFlags field is usually set to WO_IN|WO_EQ|WO_ISNULL.
-** But if the table is the right table of a left join, WhereLevel.wsFlags
-** is set to WO_IN|WO_EQ. The WhereLevel.wsFlags field can then be used as
-** the "op" parameter to findTerm when we are resolving equality constraints.
-** ISNULL constraints will then not be used on the right table of a left
-** join. Tickets #2177 and #2189.
-*/
-#define WHERE_ROWID_EQ 0x00001000 /* rowid=EXPR or rowid IN (...) */
-#define WHERE_ROWID_RANGE 0x00002000 /* rowid<EXPR and/or rowid>EXPR */
-#define WHERE_COLUMN_EQ 0x00010000 /* x=EXPR or x IN (...) or x IS NULL */
-#define WHERE_COLUMN_RANGE 0x00020000 /* x<EXPR and/or x>EXPR */
-#define WHERE_COLUMN_IN 0x00040000 /* x IN (...) */
-#define WHERE_COLUMN_NULL 0x00080000 /* x IS NULL */
-#define WHERE_INDEXED 0x000f0000 /* Anything that uses an index */
-#define WHERE_NOT_FULLSCAN 0x100f3000 /* Does not do a full table scan */
-#define WHERE_IN_ABLE 0x000f1000 /* Able to support an IN operator */
-#define WHERE_TOP_LIMIT 0x00100000 /* x<EXPR or x<=EXPR constraint */
-#define WHERE_BTM_LIMIT 0x00200000 /* x>EXPR or x>=EXPR constraint */
-#define WHERE_BOTH_LIMIT 0x00300000 /* Both x>EXPR and x<EXPR */
-#define WHERE_IDX_ONLY 0x00800000 /* Use index only - omit table */
-#define WHERE_ORDERBY 0x01000000 /* Output will appear in correct order */
-#define WHERE_REVERSE 0x02000000 /* Scan in reverse order */
-#define WHERE_UNIQUE 0x04000000 /* Selects no more than one row */
-#define WHERE_VIRTUALTABLE 0x08000000 /* Use virtual-table processing */
-#define WHERE_MULTI_OR 0x10000000 /* OR using multiple indices */
-#define WHERE_TEMP_INDEX 0x20000000 /* Uses an ephemeral index */
-#define WHERE_DISTINCT 0x40000000 /* Correct order for DISTINCT */
+#define WO_MATCH 0x0040
+#define WO_IS 0x0080
+#define WO_ISNULL 0x0100
+#define WO_OR 0x0200 /* Two or more OR-connected terms */
+#define WO_AND 0x0400 /* Two or more AND-connected terms */
+#define WO_EQUIV 0x0800 /* Of the form A==B, both columns */
+#define WO_NOOP 0x1000 /* This term does not restrict search space */
+
+#define WO_ALL 0x1fff /* Mask of all possible WO_* values */
+#define WO_SINGLE 0x01ff /* Mask of all non-compound WO_* values */
+
+/*
+** These are definitions of bits in the WhereLoop.wsFlags field.
+** The particular combination of bits in each WhereLoop help to
+** determine the algorithm that WhereLoop represents.
+*/
+#define WHERE_COLUMN_EQ 0x00000001 /* x=EXPR */
+#define WHERE_COLUMN_RANGE 0x00000002 /* x<EXPR and/or x>EXPR */
+#define WHERE_COLUMN_IN 0x00000004 /* x IN (...) */
+#define WHERE_COLUMN_NULL 0x00000008 /* x IS NULL */
+#define WHERE_CONSTRAINT 0x0000000f /* Any of the WHERE_COLUMN_xxx values */
+#define WHERE_TOP_LIMIT 0x00000010 /* x<EXPR or x<=EXPR constraint */
+#define WHERE_BTM_LIMIT 0x00000020 /* x>EXPR or x>=EXPR constraint */
+#define WHERE_BOTH_LIMIT 0x00000030 /* Both x>EXPR and x<EXPR */
+#define WHERE_IDX_ONLY 0x00000040 /* Use index only - omit table */
+#define WHERE_IPK 0x00000100 /* x is the INTEGER PRIMARY KEY */
+#define WHERE_INDEXED 0x00000200 /* WhereLoop.u.btree.pIndex is valid */
+#define WHERE_VIRTUALTABLE 0x00000400 /* WhereLoop.u.vtab is valid */
+#define WHERE_IN_ABLE 0x00000800 /* Able to support an IN operator */
+#define WHERE_ONEROW 0x00001000 /* Selects no more than one row */
+#define WHERE_MULTI_OR 0x00002000 /* OR using multiple indices */
+#define WHERE_AUTO_INDEX 0x00004000 /* Uses an ephemeral index */
+#define WHERE_SKIPSCAN 0x00008000 /* Uses the skip-scan algorithm */
+#define WHERE_UNQ_WANTED 0x00010000 /* WHERE_ONEROW would have been helpful*/
+#define WHERE_PARTIALIDX 0x00020000 /* The automatic index is partial */
+
+/************** End of whereInt.h ********************************************/
+/************** Continuing where we left off in wherecode.c ******************/
+
+#ifndef SQLITE_OMIT_EXPLAIN
/*
-** Initialize a preallocated WhereClause structure.
+** Return the name of the i-th column of the pIdx index.
*/
-static void whereClauseInit(
- WhereClause *pWC, /* The WhereClause to be initialized */
- Parse *pParse, /* The parsing context */
- WhereMaskSet *pMaskSet /* Mapping from table cursor numbers to bitmasks */
+static const char *explainIndexColumnName(Index *pIdx, int i){
+ i = pIdx->aiColumn[i];
+ if( i==XN_EXPR ) return "<expr>";
+ if( i==XN_ROWID ) return "rowid";
+ return pIdx->pTable->aCol[i].zName;
+}
+
+/*
+** This routine is a helper for explainIndexRange() below
+**
+** pStr holds the text of an expression that we are building up one term
+** at a time. This routine adds a new term to the end of the expression.
+** Terms are separated by AND so add the "AND" text for second and subsequent
+** terms only.
+*/
+static void explainAppendTerm(
+ StrAccum *pStr, /* The text expression being built */
+ Index *pIdx, /* Index to read column names from */
+ int nTerm, /* Number of terms */
+ int iTerm, /* Zero-based index of first term. */
+ int bAnd, /* Non-zero to append " AND " */
+ const char *zOp /* Name of the operator */
){
- pWC->pParse = pParse;
- pWC->pMaskSet = pMaskSet;
- pWC->nTerm = 0;
- pWC->nSlot = ArraySize(pWC->aStatic);
- pWC->a = pWC->aStatic;
- pWC->vmask = 0;
+ int i;
+
+ assert( nTerm>=1 );
+ if( bAnd ) sqlite3StrAccumAppend(pStr, " AND ", 5);
+
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
+ for(i=0; i<nTerm; i++){
+ if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
+ sqlite3StrAccumAppendAll(pStr, explainIndexColumnName(pIdx, iTerm+i));
+ }
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
+
+ sqlite3StrAccumAppend(pStr, zOp, 1);
+
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, "(", 1);
+ for(i=0; i<nTerm; i++){
+ if( i ) sqlite3StrAccumAppend(pStr, ",", 1);
+ sqlite3StrAccumAppend(pStr, "?", 1);
+ }
+ if( nTerm>1 ) sqlite3StrAccumAppend(pStr, ")", 1);
}
-/* Forward reference */
-static void whereClauseClear(WhereClause*);
+/*
+** Argument pLevel describes a strategy for scanning table pTab. This
+** function appends text to pStr that describes the subset of table
+** rows scanned by the strategy in the form of an SQL expression.
+**
+** For example, if the query:
+**
+** SELECT * FROM t1 WHERE a=1 AND b>2;
+**
+** is run and there is an index on (a, b), then this function returns a
+** string similar to:
+**
+** "a=? AND b>?"
+*/
+static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
+ Index *pIndex = pLoop->u.btree.pIndex;
+ u16 nEq = pLoop->u.btree.nEq;
+ u16 nSkip = pLoop->nSkip;
+ int i, j;
+
+ if( nEq==0 && (pLoop->wsFlags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ) return;
+ sqlite3StrAccumAppend(pStr, " (", 2);
+ for(i=0; i<nEq; i++){
+ const char *z = explainIndexColumnName(pIndex, i);
+ if( i ) sqlite3StrAccumAppend(pStr, " AND ", 5);
+ sqlite3XPrintf(pStr, i>=nSkip ? "%s=?" : "ANY(%s)", z);
+ }
+
+ j = i;
+ if( pLoop->wsFlags&WHERE_BTM_LIMIT ){
+ explainAppendTerm(pStr, pIndex, pLoop->u.btree.nBtm, j, i, ">");
+ i = 1;
+ }
+ if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
+ explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
+ }
+ sqlite3StrAccumAppend(pStr, ")", 1);
+}
/*
-** Deallocate all memory associated with a WhereOrInfo object.
+** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
+** command, or if either SQLITE_DEBUG or SQLITE_ENABLE_STMT_SCANSTATUS was
+** defined at compile-time. If it is not a no-op, a single OP_Explain opcode
+** is added to the output to describe the table scan strategy in pLevel.
+**
+** If an OP_Explain opcode is added to the VM, its address is returned.
+** Otherwise, if no OP_Explain is coded, zero is returned.
*/
-static void whereOrInfoDelete(sqlite3 *db, WhereOrInfo *p){
- whereClauseClear(&p->wc);
- sqlite3DbFree(db, p);
+SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
+ Parse *pParse, /* Parse context */
+ SrcList *pTabList, /* Table list this loop refers to */
+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
+ int iLevel, /* Value for "level" column of output */
+ int iFrom, /* Value for "from" column of output */
+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
+){
+ int ret = 0;
+#if !defined(SQLITE_DEBUG) && !defined(SQLITE_ENABLE_STMT_SCANSTATUS)
+ if( pParse->explain==2 )
+#endif
+ {
+ struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
+ Vdbe *v = pParse->pVdbe; /* VM being constructed */
+ sqlite3 *db = pParse->db; /* Database handle */
+ int iId = pParse->iSelectId; /* Select id (left-most output column) */
+ int isSearch; /* True for a SEARCH. False for SCAN. */
+ WhereLoop *pLoop; /* The controlling WhereLoop object */
+ u32 flags; /* Flags that describe this loop */
+ char *zMsg; /* Text to add to EQP output */
+ StrAccum str; /* EQP output string */
+ char zBuf[100]; /* Initial space for EQP output string */
+
+ pLoop = pLevel->pWLoop;
+ flags = pLoop->wsFlags;
+ if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0;
+
+ isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
+ || ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
+ || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
+
+ sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
+ sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
+ if( pItem->pSelect ){
+ sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
+ }else{
+ sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
+ }
+
+ if( pItem->zAlias ){
+ sqlite3XPrintf(&str, " AS %s", pItem->zAlias);
+ }
+ if( (flags & (WHERE_IPK|WHERE_VIRTUALTABLE))==0 ){
+ const char *zFmt = 0;
+ Index *pIdx;
+
+ assert( pLoop->u.btree.pIndex!=0 );
+ pIdx = pLoop->u.btree.pIndex;
+ assert( !(flags&WHERE_AUTO_INDEX) || (flags&WHERE_IDX_ONLY) );
+ if( !HasRowid(pItem->pTab) && IsPrimaryKeyIndex(pIdx) ){
+ if( isSearch ){
+ zFmt = "PRIMARY KEY";
+ }
+ }else if( flags & WHERE_PARTIALIDX ){
+ zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
+ }else if( flags & WHERE_AUTO_INDEX ){
+ zFmt = "AUTOMATIC COVERING INDEX";
+ }else if( flags & WHERE_IDX_ONLY ){
+ zFmt = "COVERING INDEX %s";
+ }else{
+ zFmt = "INDEX %s";
+ }
+ if( zFmt ){
+ sqlite3StrAccumAppend(&str, " USING ", 7);
+ sqlite3XPrintf(&str, zFmt, pIdx->zName);
+ explainIndexRange(&str, pLoop);
+ }
+ }else if( (flags & WHERE_IPK)!=0 && (flags & WHERE_CONSTRAINT)!=0 ){
+ const char *zRangeOp;
+ if( flags&(WHERE_COLUMN_EQ|WHERE_COLUMN_IN) ){
+ zRangeOp = "=";
+ }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
+ zRangeOp = ">? AND rowid<";
+ }else if( flags&WHERE_BTM_LIMIT ){
+ zRangeOp = ">";
+ }else{
+ assert( flags&WHERE_TOP_LIMIT);
+ zRangeOp = "<";
+ }
+ sqlite3XPrintf(&str, " USING INTEGER PRIMARY KEY (rowid%s?)",zRangeOp);
+ }
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
+ sqlite3XPrintf(&str, " VIRTUAL TABLE INDEX %d:%s",
+ pLoop->u.vtab.idxNum, pLoop->u.vtab.idxStr);
+ }
+#endif
+#ifdef SQLITE_EXPLAIN_ESTIMATED_ROWS
+ if( pLoop->nOut>=10 ){
+ sqlite3XPrintf(&str, " (~%llu rows)", sqlite3LogEstToInt(pLoop->nOut));
+ }else{
+ sqlite3StrAccumAppend(&str, " (~1 row)", 9);
+ }
+#endif
+ zMsg = sqlite3StrAccumFinish(&str);
+ ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
+ }
+ return ret;
}
+#endif /* SQLITE_OMIT_EXPLAIN */
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
/*
-** Deallocate all memory associated with a WhereAndInfo object.
+** Configure the VM passed as the first argument with an
+** sqlite3_stmt_scanstatus() entry corresponding to the scan used to
+** implement level pLvl. Argument pSrclist is a pointer to the FROM
+** clause that the scan reads data from.
+**
+** If argument addrExplain is not 0, it must be the address of an
+** OP_Explain instruction that describes the same loop.
*/
-static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){
- whereClauseClear(&p->wc);
- sqlite3DbFree(db, p);
+SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
+ Vdbe *v, /* Vdbe to add scanstatus entry to */
+ SrcList *pSrclist, /* FROM clause pLvl reads data from */
+ WhereLevel *pLvl, /* Level to add scanstatus() entry for */
+ int addrExplain /* Address of OP_Explain (or 0) */
+){
+ const char *zObj = 0;
+ WhereLoop *pLoop = pLvl->pWLoop;
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 && pLoop->u.btree.pIndex!=0 ){
+ zObj = pLoop->u.btree.pIndex->zName;
+ }else{
+ zObj = pSrclist->a[pLvl->iFrom].zName;
+ }
+ sqlite3VdbeScanStatus(
+ v, addrExplain, pLvl->addrBody, pLvl->addrVisit, pLoop->nOut, zObj
+ );
}
+#endif
+
/*
-** Deallocate a WhereClause structure. The WhereClause structure
-** itself is not freed. This routine is the inverse of whereClauseInit().
+** Disable a term in the WHERE clause. Except, do not disable the term
+** if it controls a LEFT OUTER JOIN and it did not originate in the ON
+** or USING clause of that join.
+**
+** Consider the term t2.z='ok' in the following queries:
+**
+** (1) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x WHERE t2.z='ok'
+** (2) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x AND t2.z='ok'
+** (3) SELECT * FROM t1, t2 WHERE t1.a=t2.x AND t2.z='ok'
+**
+** The t2.z='ok' is disabled in the in (2) because it originates
+** in the ON clause. The term is disabled in (3) because it is not part
+** of a LEFT OUTER JOIN. In (1), the term is not disabled.
+**
+** Disabling a term causes that term to not be tested in the inner loop
+** of the join. Disabling is an optimization. When terms are satisfied
+** by indices, we disable them to prevent redundant tests in the inner
+** loop. We would get the correct results if nothing were ever disabled,
+** but joins might run a little slower. The trick is to disable as much
+** as we can without disabling too much. If we disabled in (1), we'd get
+** the wrong answer. See ticket #813.
+**
+** If all the children of a term are disabled, then that term is also
+** automatically disabled. In this way, terms get disabled if derived
+** virtual terms are tested first. For example:
+**
+** x GLOB 'abc*' AND x>='abc' AND x<'acd'
+** \___________/ \______/ \_____/
+** parent child1 child2
+**
+** Only the parent term was in the original WHERE clause. The child1
+** and child2 terms were added by the LIKE optimization. If both of
+** the virtual child terms are valid, then testing of the parent can be
+** skipped.
+**
+** Usually the parent term is marked as TERM_CODED. But if the parent
+** term was originally TERM_LIKE, then the parent gets TERM_LIKECOND instead.
+** The TERM_LIKECOND marking indicates that the term should be coded inside
+** a conditional such that is only evaluated on the second pass of a
+** LIKE-optimization loop, when scanning BLOBs instead of strings.
*/
-static void whereClauseClear(WhereClause *pWC){
- int i;
- WhereTerm *a;
- sqlite3 *db = pWC->pParse->db;
- for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
- if( a->wtFlags & TERM_DYNAMIC ){
- sqlite3ExprDelete(db, a->pExpr);
- }
- if( a->wtFlags & TERM_ORINFO ){
- whereOrInfoDelete(db, a->u.pOrInfo);
- }else if( a->wtFlags & TERM_ANDINFO ){
- whereAndInfoDelete(db, a->u.pAndInfo);
+static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
+ int nLoop = 0;
+ while( ALWAYS(pTerm!=0)
+ && (pTerm->wtFlags & TERM_CODED)==0
+ && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ && (pLevel->notReady & pTerm->prereqAll)==0
+ ){
+ if( nLoop && (pTerm->wtFlags & TERM_LIKE)!=0 ){
+ pTerm->wtFlags |= TERM_LIKECOND;
+ }else{
+ pTerm->wtFlags |= TERM_CODED;
}
+ if( pTerm->iParent<0 ) break;
+ pTerm = &pTerm->pWC->a[pTerm->iParent];
+ pTerm->nChild--;
+ if( pTerm->nChild!=0 ) break;
+ nLoop++;
}
- if( pWC->a!=pWC->aStatic ){
- sqlite3DbFree(db, pWC->a);
+}
+
+/*
+** Code an OP_Affinity opcode to apply the column affinity string zAff
+** to the n registers starting at base.
+**
+** As an optimization, SQLITE_AFF_BLOB entries (which are no-ops) at the
+** beginning and end of zAff are ignored. If all entries in zAff are
+** SQLITE_AFF_BLOB, then no code gets generated.
+**
+** This routine makes its own copy of zAff so that the caller is free
+** to modify zAff after this routine returns.
+*/
+static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
+ Vdbe *v = pParse->pVdbe;
+ if( zAff==0 ){
+ assert( pParse->db->mallocFailed );
+ return;
+ }
+ assert( v!=0 );
+
+ /* Adjust base and n to skip over SQLITE_AFF_BLOB entries at the beginning
+ ** and end of the affinity string.
+ */
+ while( n>0 && zAff[0]==SQLITE_AFF_BLOB ){
+ n--;
+ base++;
+ zAff++;
+ }
+ while( n>1 && zAff[n-1]==SQLITE_AFF_BLOB ){
+ n--;
+ }
+
+ /* Code the OP_Affinity opcode if there is anything left to do. */
+ if( n>0 ){
+ sqlite3VdbeAddOp4(v, OP_Affinity, base, n, 0, zAff, n);
+ sqlite3ExprCacheAffinityChange(pParse, base, n);
}
}
/*
-** Add a single new WhereTerm entry to the WhereClause object pWC.
-** The new WhereTerm object is constructed from Expr p and with wtFlags.
-** The index in pWC->a[] of the new WhereTerm is returned on success.
-** 0 is returned if the new WhereTerm could not be added due to a memory
-** allocation error. The memory allocation failure will be recorded in
-** the db->mallocFailed flag so that higher-level functions can detect it.
+** Expression pRight, which is the RHS of a comparison operation, is
+** either a vector of n elements or, if n==1, a scalar expression.
+** Before the comparison operation, affinity zAff is to be applied
+** to the pRight values. This function modifies characters within the
+** affinity string to SQLITE_AFF_BLOB if either:
**
-** This routine will increase the size of the pWC->a[] array as necessary.
+** * the comparison will be performed with no affinity, or
+** * the affinity change in zAff is guaranteed not to change the value.
+*/
+static void updateRangeAffinityStr(
+ Expr *pRight, /* RHS of comparison */
+ int n, /* Number of vector elements in comparison */
+ char *zAff /* Affinity string to modify */
+){
+ int i;
+ for(i=0; i<n; i++){
+ Expr *p = sqlite3VectorFieldSubexpr(pRight, i);
+ if( sqlite3CompareAffinity(p, zAff[i])==SQLITE_AFF_BLOB
+ || sqlite3ExprNeedsNoAffinityChange(p, zAff[i])
+ ){
+ zAff[i] = SQLITE_AFF_BLOB;
+ }
+ }
+}
+
+/*
+** Generate code for a single equality term of the WHERE clause. An equality
+** term can be either X=expr or X IN (...). pTerm is the term to be
+** coded.
**
-** If the wtFlags argument includes TERM_DYNAMIC, then responsibility
-** for freeing the expression p is assumed by the WhereClause object pWC.
-** This is true even if this routine fails to allocate a new WhereTerm.
+** The current value for the constraint is left in a register, the index
+** of which is returned. An attempt is made store the result in iTarget but
+** this is only guaranteed for TK_ISNULL and TK_IN constraints. If the
+** constraint is a TK_EQ or TK_IS, then the current value might be left in
+** some other register and it is the caller's responsibility to compensate.
**
-** WARNING: This routine might reallocate the space used to store
-** WhereTerms. All pointers to WhereTerms should be invalidated after
-** calling this routine. Such pointers may be reinitialized by referencing
-** the pWC->a[] array.
+** For a constraint of the form X=expr, the expression is evaluated in
+** straight-line code. For constraints of the form X IN (...)
+** this routine sets up a loop that will iterate over all values of X.
*/
-static int whereClauseInsert(WhereClause *pWC, Expr *p, u8 wtFlags){
- WhereTerm *pTerm;
- int idx;
- testcase( wtFlags & TERM_VIRTUAL ); /* EV: R-00211-15100 */
- if( pWC->nTerm>=pWC->nSlot ){
- WhereTerm *pOld = pWC->a;
- sqlite3 *db = pWC->pParse->db;
- pWC->a = sqlite3DbMallocRaw(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
- if( pWC->a==0 ){
- if( wtFlags & TERM_DYNAMIC ){
- sqlite3ExprDelete(db, p);
+static int codeEqualityTerm(
+ Parse *pParse, /* The parsing context */
+ WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
+ WhereLevel *pLevel, /* The level of the FROM clause we are working on */
+ int iEq, /* Index of the equality term within this level */
+ int bRev, /* True for reverse-order IN operations */
+ int iTarget /* Attempt to leave results in this register */
+){
+ Expr *pX = pTerm->pExpr;
+ Vdbe *v = pParse->pVdbe;
+ int iReg; /* Register holding results */
+
+ assert( pLevel->pWLoop->aLTerm[iEq]==pTerm );
+ assert( iTarget>0 );
+ if( pX->op==TK_EQ || pX->op==TK_IS ){
+ iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
+ }else if( pX->op==TK_ISNULL ){
+ iReg = iTarget;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
+#ifndef SQLITE_OMIT_SUBQUERY
+ }else{
+ int eType = IN_INDEX_NOOP;
+ int iTab;
+ struct InLoop *pIn;
+ WhereLoop *pLoop = pLevel->pWLoop;
+ int i;
+ int nEq = 0;
+ int *aiMap = 0;
+
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0
+ && pLoop->u.btree.pIndex!=0
+ && pLoop->u.btree.pIndex->aSortOrder[iEq]
+ ){
+ testcase( iEq==0 );
+ testcase( bRev );
+ bRev = !bRev;
+ }
+ assert( pX->op==TK_IN );
+ iReg = iTarget;
+
+ for(i=0; i<iEq; i++){
+ if( pLoop->aLTerm[i] && pLoop->aLTerm[i]->pExpr==pX ){
+ disableTerm(pLevel, pTerm);
+ return iTarget;
}
- pWC->a = pOld;
- return 0;
}
- memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm);
- if( pOld!=pWC->aStatic ){
- sqlite3DbFree(db, pOld);
+ for(i=iEq;i<pLoop->nLTerm; i++){
+ if( ALWAYS(pLoop->aLTerm[i]) && pLoop->aLTerm[i]->pExpr==pX ) nEq++;
}
- pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
+
+ if( (pX->flags & EP_xIsSelect)==0 || pX->x.pSelect->pEList->nExpr==1 ){
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, 0);
+ }else{
+ Select *pSelect = pX->x.pSelect;
+ sqlite3 *db = pParse->db;
+ ExprList *pOrigRhs = pSelect->pEList;
+ ExprList *pOrigLhs = pX->pLeft->x.pList;
+ ExprList *pRhs = 0; /* New Select.pEList for RHS */
+ ExprList *pLhs = 0; /* New pX->pLeft vector */
+
+ for(i=iEq;i<pLoop->nLTerm; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ int iField = pLoop->aLTerm[i]->iField - 1;
+ Expr *pNewRhs = sqlite3ExprDup(db, pOrigRhs->a[iField].pExpr, 0);
+ Expr *pNewLhs = sqlite3ExprDup(db, pOrigLhs->a[iField].pExpr, 0);
+
+ pRhs = sqlite3ExprListAppend(pParse, pRhs, pNewRhs);
+ pLhs = sqlite3ExprListAppend(pParse, pLhs, pNewLhs);
+ }
+ }
+ if( !db->mallocFailed ){
+ Expr *pLeft = pX->pLeft;
+
+ if( pSelect->pOrderBy ){
+ /* If the SELECT statement has an ORDER BY clause, zero the
+ ** iOrderByCol variables. These are set to non-zero when an
+ ** ORDER BY term exactly matches one of the terms of the
+ ** result-set. Since the result-set of the SELECT statement may
+ ** have been modified or reordered, these variables are no longer
+ ** set correctly. Since setting them is just an optimization,
+ ** it's easiest just to zero them here. */
+ ExprList *pOrderBy = pSelect->pOrderBy;
+ for(i=0; i<pOrderBy->nExpr; i++){
+ pOrderBy->a[i].u.x.iOrderByCol = 0;
+ }
+ }
+
+ /* Take care here not to generate a TK_VECTOR containing only a
+ ** single value. Since the parser never creates such a vector, some
+ ** of the subroutines do not handle this case. */
+ if( pLhs->nExpr==1 ){
+ pX->pLeft = pLhs->a[0].pExpr;
+ }else{
+ pLeft->x.pList = pLhs;
+ aiMap = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int) * nEq);
+ testcase( aiMap==0 );
+ }
+ pSelect->pEList = pRhs;
+ eType = sqlite3FindInIndex(pParse, pX, IN_INDEX_LOOP, 0, aiMap);
+ testcase( aiMap!=0 && aiMap[0]!=0 );
+ pSelect->pEList = pOrigRhs;
+ pLeft->x.pList = pOrigLhs;
+ pX->pLeft = pLeft;
+ }
+ sqlite3ExprListDelete(pParse->db, pLhs);
+ sqlite3ExprListDelete(pParse->db, pRhs);
+ }
+
+ if( eType==IN_INDEX_INDEX_DESC ){
+ testcase( bRev );
+ bRev = !bRev;
+ }
+ iTab = pX->iTable;
+ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iTab, 0);
+ VdbeCoverageIf(v, bRev);
+ VdbeCoverageIf(v, !bRev);
+ assert( (pLoop->wsFlags & WHERE_MULTI_OR)==0 );
+
+ pLoop->wsFlags |= WHERE_IN_ABLE;
+ if( pLevel->u.in.nIn==0 ){
+ pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
+ }
+
+ i = pLevel->u.in.nIn;
+ pLevel->u.in.nIn += nEq;
+ pLevel->u.in.aInLoop =
+ sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
+ sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
+ pIn = pLevel->u.in.aInLoop;
+ if( pIn ){
+ int iMap = 0; /* Index in aiMap[] */
+ pIn += i;
+ for(i=iEq;i<pLoop->nLTerm; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ int iOut = iReg + i - iEq;
+ if( eType==IN_INDEX_ROWID ){
+ testcase( nEq>1 ); /* Happens with a UNIQUE index on ROWID */
+ pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iOut);
+ }else{
+ int iCol = aiMap ? aiMap[iMap++] : 0;
+ pIn->addrInTop = sqlite3VdbeAddOp3(v,OP_Column,iTab, iCol, iOut);
+ }
+ sqlite3VdbeAddOp1(v, OP_IsNull, iOut); VdbeCoverage(v);
+ if( i==iEq ){
+ pIn->iCur = iTab;
+ pIn->eEndLoopOp = bRev ? OP_PrevIfOpen : OP_NextIfOpen;
+ }else{
+ pIn->eEndLoopOp = OP_Noop;
+ }
+ pIn++;
+ }
+ }
+ }else{
+ pLevel->u.in.nIn = 0;
+ }
+ sqlite3DbFree(pParse->db, aiMap);
+#endif
}
- pTerm = &pWC->a[idx = pWC->nTerm++];
- pTerm->pExpr = p;
- pTerm->wtFlags = wtFlags;
- pTerm->pWC = pWC;
- pTerm->iParent = -1;
- return idx;
+ disableTerm(pLevel, pTerm);
+ return iReg;
}
/*
-** This routine identifies subexpressions in the WHERE clause where
-** each subexpression is separated by the AND operator or some other
-** operator specified in the op parameter. The WhereClause structure
-** is filled with pointers to subexpressions. For example:
+** Generate code that will evaluate all == and IN constraints for an
+** index scan.
**
-** WHERE a=='hello' AND coalesce(b,11)<10 AND (c+12!=d OR c==22)
-** \________/ \_______________/ \________________/
-** slot[0] slot[1] slot[2]
+** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c).
+** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10
+** The index has as many as three equality constraints, but in this
+** example, the third "c" value is an inequality. So only two
+** constraints are coded. This routine will generate code to evaluate
+** a==5 and b IN (1,2,3). The current values for a and b will be stored
+** in consecutive registers and the index of the first register is returned.
**
-** The original WHERE clause in pExpr is unaltered. All this routine
-** does is make slot[] entries point to substructure within pExpr.
+** In the example above nEq==2. But this subroutine works for any value
+** of nEq including 0. If nEq==0, this routine is nearly a no-op.
+** The only thing it does is allocate the pLevel->iMem memory cell and
+** compute the affinity string.
**
-** In the previous sentence and in the diagram, "slot[]" refers to
-** the WhereClause.a[] array. The slot[] array grows as needed to contain
-** all terms of the WHERE clause.
+** The nExtraReg parameter is 0 or 1. It is 0 if all WHERE clause constraints
+** are == or IN and are covered by the nEq. nExtraReg is 1 if there is
+** an inequality constraint (such as the "c>=5 AND c<10" in the example) that
+** occurs after the nEq quality constraints.
+**
+** This routine allocates a range of nEq+nExtraReg memory cells and returns
+** the index of the first memory cell in that range. The code that
+** calls this routine will use that memory range to store keys for
+** start and termination conditions of the loop.
+** key value of the loop. If one or more IN operators appear, then
+** this routine allocates an additional nEq memory cells for internal
+** use.
+**
+** Before returning, *pzAff is set to point to a buffer containing a
+** copy of the column affinity string of the index allocated using
+** sqlite3DbMalloc(). Except, entries in the copy of the string associated
+** with equality constraints that use BLOB or NONE affinity are set to
+** SQLITE_AFF_BLOB. This is to deal with SQL such as the following:
+**
+** CREATE TABLE t1(a TEXT PRIMARY KEY, b);
+** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
+**
+** In the example above, the index on t1(a) has TEXT affinity. But since
+** the right hand side of the equality constraint (t2.b) has BLOB/NONE affinity,
+** no conversion should be attempted before using a t2.b value as part of
+** a key to search the index. Hence the first byte in the returned affinity
+** string in this example would be set to SQLITE_AFF_BLOB.
*/
-static void whereSplit(WhereClause *pWC, Expr *pExpr, int op){
- pWC->op = (u8)op;
- if( pExpr==0 ) return;
- if( pExpr->op!=op ){
- whereClauseInsert(pWC, pExpr, 0);
- }else{
- whereSplit(pWC, pExpr->pLeft, op);
- whereSplit(pWC, pExpr->pRight, op);
+static int codeAllEqualityTerms(
+ Parse *pParse, /* Parsing context */
+ WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
+ int bRev, /* Reverse the order of IN operators */
+ int nExtraReg, /* Number of extra registers to allocate */
+ char **pzAff /* OUT: Set to point to affinity string */
+){
+ u16 nEq; /* The number of == or IN constraints to code */
+ u16 nSkip; /* Number of left-most columns to skip */
+ Vdbe *v = pParse->pVdbe; /* The vm under construction */
+ Index *pIdx; /* The index being used for this loop */
+ WhereTerm *pTerm; /* A single constraint term */
+ WhereLoop *pLoop; /* The WhereLoop object */
+ int j; /* Loop counter */
+ int regBase; /* Base register */
+ int nReg; /* Number of registers to allocate */
+ char *zAff; /* Affinity string to return */
+
+ /* This module is only called on query plans that use an index. */
+ pLoop = pLevel->pWLoop;
+ assert( (pLoop->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ nEq = pLoop->u.btree.nEq;
+ nSkip = pLoop->nSkip;
+ pIdx = pLoop->u.btree.pIndex;
+ assert( pIdx!=0 );
+
+ /* Figure out how many memory cells we will need then allocate them.
+ */
+ regBase = pParse->nMem + 1;
+ nReg = pLoop->u.btree.nEq + nExtraReg;
+ pParse->nMem += nReg;
+
+ zAff = sqlite3DbStrDup(pParse->db,sqlite3IndexAffinityStr(pParse->db,pIdx));
+ assert( zAff!=0 || pParse->db->mallocFailed );
+
+ if( nSkip ){
+ int iIdxCur = pLevel->iIdxCur;
+ sqlite3VdbeAddOp1(v, (bRev?OP_Last:OP_Rewind), iIdxCur);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ VdbeComment((v, "begin skip-scan on %s", pIdx->zName));
+ j = sqlite3VdbeAddOp0(v, OP_Goto);
+ pLevel->addrSkip = sqlite3VdbeAddOp4Int(v, (bRev?OP_SeekLT:OP_SeekGT),
+ iIdxCur, 0, regBase, nSkip);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ sqlite3VdbeJumpHere(v, j);
+ for(j=0; j<nSkip; j++){
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, j, regBase+j);
+ testcase( pIdx->aiColumn[j]==XN_EXPR );
+ VdbeComment((v, "%s", explainIndexColumnName(pIdx, j)));
+ }
+ }
+
+ /* Evaluate the equality constraints
+ */
+ assert( zAff==0 || (int)strlen(zAff)>=nEq );
+ for(j=nSkip; j<nEq; j++){
+ int r1;
+ pTerm = pLoop->aLTerm[j];
+ assert( pTerm!=0 );
+ /* The following testcase is true for indices with redundant columns.
+ ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
+ testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ r1 = codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, regBase+j);
+ if( r1!=regBase+j ){
+ if( nReg==1 ){
+ sqlite3ReleaseTempReg(pParse, regBase);
+ regBase = r1;
+ }else{
+ sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
+ }
+ }
+ if( pTerm->eOperator & WO_IN ){
+ if( pTerm->pExpr->flags & EP_xIsSelect ){
+ /* No affinity ever needs to be (or should be) applied to a value
+ ** from the RHS of an "? IN (SELECT ...)" expression. The
+ ** sqlite3FindInIndex() routine has already ensured that the
+ ** affinity of the comparison has been applied to the value. */
+ if( zAff ) zAff[j] = SQLITE_AFF_BLOB;
+ }
+ }else if( (pTerm->eOperator & WO_ISNULL)==0 ){
+ Expr *pRight = pTerm->pExpr->pRight;
+ if( (pTerm->wtFlags & TERM_IS)==0 && sqlite3ExprCanBeNull(pRight) ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk);
+ VdbeCoverage(v);
+ }
+ if( zAff ){
+ if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_BLOB ){
+ zAff[j] = SQLITE_AFF_BLOB;
+ }
+ if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){
+ zAff[j] = SQLITE_AFF_BLOB;
+ }
+ }
+ }
}
+ *pzAff = zAff;
+ return regBase;
}
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
/*
-** Initialize an expression mask set (a WhereMaskSet object)
+** If the most recently coded instruction is a constant range constraint
+** (a string literal) that originated from the LIKE optimization, then
+** set P3 and P5 on the OP_String opcode so that the string will be cast
+** to a BLOB at appropriate times.
+**
+** The LIKE optimization trys to evaluate "x LIKE 'abc%'" as a range
+** expression: "x>='ABC' AND x<'abd'". But this requires that the range
+** scan loop run twice, once for strings and a second time for BLOBs.
+** The OP_String opcodes on the second pass convert the upper and lower
+** bound string constants to blobs. This routine makes the necessary changes
+** to the OP_String opcodes for that to happen.
+**
+** Except, of course, if SQLITE_LIKE_DOESNT_MATCH_BLOBS is defined, then
+** only the one pass through the string space is required, so this routine
+** becomes a no-op.
*/
-#define initMaskSet(P) memset(P, 0, sizeof(*P))
+static void whereLikeOptimizationStringFixup(
+ Vdbe *v, /* prepared statement under construction */
+ WhereLevel *pLevel, /* The loop that contains the LIKE operator */
+ WhereTerm *pTerm /* The upper or lower bound just coded */
+){
+ if( pTerm->wtFlags & TERM_LIKEOPT ){
+ VdbeOp *pOp;
+ assert( pLevel->iLikeRepCntr>0 );
+ pOp = sqlite3VdbeGetOp(v, -1);
+ assert( pOp!=0 );
+ assert( pOp->opcode==OP_String8
+ || pTerm->pWC->pWInfo->pParse->db->mallocFailed );
+ pOp->p3 = (int)(pLevel->iLikeRepCntr>>1); /* Register holding counter */
+ pOp->p5 = (u8)(pLevel->iLikeRepCntr&1); /* ASC or DESC */
+ }
+}
+#else
+# define whereLikeOptimizationStringFixup(A,B,C)
+#endif
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
/*
-** Return the bitmask for the given cursor number. Return 0 if
-** iCursor is not in the set.
+** Information is passed from codeCursorHint() down to individual nodes of
+** the expression tree (by sqlite3WalkExpr()) using an instance of this
+** structure.
*/
-static Bitmask getMask(WhereMaskSet *pMaskSet, int iCursor){
- int i;
- assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
- for(i=0; i<pMaskSet->n; i++){
- if( pMaskSet->ix[i]==iCursor ){
- return ((Bitmask)1)<<i;
+struct CCurHint {
+ int iTabCur; /* Cursor for the main table */
+ int iIdxCur; /* Cursor for the index, if pIdx!=0. Unused otherwise */
+ Index *pIdx; /* The index used to access the table */
+};
+
+/*
+** This function is called for every node of an expression that is a candidate
+** for a cursor hint on an index cursor. For TK_COLUMN nodes that reference
+** the table CCurHint.iTabCur, verify that the same column can be
+** accessed through the index. If it cannot, then set pWalker->eCode to 1.
+*/
+static int codeCursorHintCheckExpr(Walker *pWalker, Expr *pExpr){
+ struct CCurHint *pHint = pWalker->u.pCCurHint;
+ assert( pHint->pIdx!=0 );
+ if( pExpr->op==TK_COLUMN
+ && pExpr->iTable==pHint->iTabCur
+ && sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn)<0
+ ){
+ pWalker->eCode = 1;
+ }
+ return WRC_Continue;
+}
+
+/*
+** Test whether or not expression pExpr, which was part of a WHERE clause,
+** should be included in the cursor-hint for a table that is on the rhs
+** of a LEFT JOIN. Set Walker.eCode to non-zero before returning if the
+** expression is not suitable.
+**
+** An expression is unsuitable if it might evaluate to non NULL even if
+** a TK_COLUMN node that does affect the value of the expression is set
+** to NULL. For example:
+**
+** col IS NULL
+** col IS NOT NULL
+** coalesce(col, 1)
+** CASE WHEN col THEN 0 ELSE 1 END
+*/
+static int codeCursorHintIsOrFunction(Walker *pWalker, Expr *pExpr){
+ if( pExpr->op==TK_IS
+ || pExpr->op==TK_ISNULL || pExpr->op==TK_ISNOT
+ || pExpr->op==TK_NOTNULL || pExpr->op==TK_CASE
+ ){
+ pWalker->eCode = 1;
+ }else if( pExpr->op==TK_FUNCTION ){
+ int d1;
+ char d2[3];
+ if( 0==sqlite3IsLikeFunction(pWalker->pParse->db, pExpr, &d1, d2) ){
+ pWalker->eCode = 1;
}
}
- return 0;
+
+ return WRC_Continue;
}
+
/*
-** Create a new mask for cursor iCursor.
+** This function is called on every node of an expression tree used as an
+** argument to the OP_CursorHint instruction. If the node is a TK_COLUMN
+** that accesses any table other than the one identified by
+** CCurHint.iTabCur, then do the following:
**
-** There is one cursor per table in the FROM clause. The number of
-** tables in the FROM clause is limited by a test early in the
-** sqlite3WhereBegin() routine. So we know that the pMaskSet->ix[]
-** array will never overflow.
+** 1) allocate a register and code an OP_Column instruction to read
+** the specified column into the new register, and
+**
+** 2) transform the expression node to a TK_REGISTER node that reads
+** from the newly populated register.
+**
+** Also, if the node is a TK_COLUMN that does access the table idenified
+** by pCCurHint.iTabCur, and an index is being used (which we will
+** know because CCurHint.pIdx!=0) then transform the TK_COLUMN into
+** an access of the index rather than the original table.
*/
-static void createMask(WhereMaskSet *pMaskSet, int iCursor){
- assert( pMaskSet->n < ArraySize(pMaskSet->ix) );
- pMaskSet->ix[pMaskSet->n++] = iCursor;
+static int codeCursorHintFixExpr(Walker *pWalker, Expr *pExpr){
+ int rc = WRC_Continue;
+ struct CCurHint *pHint = pWalker->u.pCCurHint;
+ if( pExpr->op==TK_COLUMN ){
+ if( pExpr->iTable!=pHint->iTabCur ){
+ Vdbe *v = pWalker->pParse->pVdbe;
+ int reg = ++pWalker->pParse->nMem; /* Register for column value */
+ sqlite3ExprCodeGetColumnOfTable(
+ v, pExpr->pTab, pExpr->iTable, pExpr->iColumn, reg
+ );
+ pExpr->op = TK_REGISTER;
+ pExpr->iTable = reg;
+ }else if( pHint->pIdx!=0 ){
+ pExpr->iTable = pHint->iIdxCur;
+ pExpr->iColumn = sqlite3ColumnOfIndex(pHint->pIdx, pExpr->iColumn);
+ assert( pExpr->iColumn>=0 );
+ }
+ }else if( pExpr->op==TK_AGG_FUNCTION ){
+ /* An aggregate function in the WHERE clause of a query means this must
+ ** be a correlated sub-query, and expression pExpr is an aggregate from
+ ** the parent context. Do not walk the function arguments in this case.
+ **
+ ** todo: It should be possible to replace this node with a TK_REGISTER
+ ** expression, as the result of the expression must be stored in a
+ ** register at this point. The same holds for TK_AGG_COLUMN nodes. */
+ rc = WRC_Prune;
+ }
+ return rc;
}
/*
-** This routine walks (recursively) an expression tree and generates
-** a bitmask indicating which tables are used in that expression
-** tree.
+** Insert an OP_CursorHint instruction if it is appropriate to do so.
+*/
+static void codeCursorHint(
+ struct SrcList_item *pTabItem, /* FROM clause item */
+ WhereInfo *pWInfo, /* The where clause */
+ WhereLevel *pLevel, /* Which loop to provide hints for */
+ WhereTerm *pEndRange /* Hint this end-of-scan boundary term if not NULL */
+){
+ Parse *pParse = pWInfo->pParse;
+ sqlite3 *db = pParse->db;
+ Vdbe *v = pParse->pVdbe;
+ Expr *pExpr = 0;
+ WhereLoop *pLoop = pLevel->pWLoop;
+ int iCur;
+ WhereClause *pWC;
+ WhereTerm *pTerm;
+ int i, j;
+ struct CCurHint sHint;
+ Walker sWalker;
+
+ if( OptimizationDisabled(db, SQLITE_CursorHints) ) return;
+ iCur = pLevel->iTabCur;
+ assert( iCur==pWInfo->pTabList->a[pLevel->iFrom].iCursor );
+ sHint.iTabCur = iCur;
+ sHint.iIdxCur = pLevel->iIdxCur;
+ sHint.pIdx = pLoop->u.btree.pIndex;
+ memset(&sWalker, 0, sizeof(sWalker));
+ sWalker.pParse = pParse;
+ sWalker.u.pCCurHint = &sHint;
+ pWC = &pWInfo->sWC;
+ for(i=0; i<pWC->nTerm; i++){
+ pTerm = &pWC->a[i];
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( pTerm->prereqAll & pLevel->notReady ) continue;
+
+ /* Any terms specified as part of the ON(...) clause for any LEFT
+ ** JOIN for which the current table is not the rhs are omitted
+ ** from the cursor-hint.
+ **
+ ** If this table is the rhs of a LEFT JOIN, "IS" or "IS NULL" terms
+ ** that were specified as part of the WHERE clause must be excluded.
+ ** This is to address the following:
+ **
+ ** SELECT ... t1 LEFT JOIN t2 ON (t1.a=t2.b) WHERE t2.c IS NULL;
+ **
+ ** Say there is a single row in t2 that matches (t1.a=t2.b), but its
+ ** t2.c values is not NULL. If the (t2.c IS NULL) constraint is
+ ** pushed down to the cursor, this row is filtered out, causing
+ ** SQLite to synthesize a row of NULL values. Which does match the
+ ** WHERE clause, and so the query returns a row. Which is incorrect.
+ **
+ ** For the same reason, WHERE terms such as:
+ **
+ ** WHERE 1 = (t2.c IS NULL)
+ **
+ ** are also excluded. See codeCursorHintIsOrFunction() for details.
+ */
+ if( pTabItem->fg.jointype & JT_LEFT ){
+ Expr *pExpr = pTerm->pExpr;
+ if( !ExprHasProperty(pExpr, EP_FromJoin)
+ || pExpr->iRightJoinTable!=pTabItem->iCursor
+ ){
+ sWalker.eCode = 0;
+ sWalker.xExprCallback = codeCursorHintIsOrFunction;
+ sqlite3WalkExpr(&sWalker, pTerm->pExpr);
+ if( sWalker.eCode ) continue;
+ }
+ }else{
+ if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) continue;
+ }
+
+ /* All terms in pWLoop->aLTerm[] except pEndRange are used to initialize
+ ** the cursor. These terms are not needed as hints for a pure range
+ ** scan (that has no == terms) so omit them. */
+ if( pLoop->u.btree.nEq==0 && pTerm!=pEndRange ){
+ for(j=0; j<pLoop->nLTerm && pLoop->aLTerm[j]!=pTerm; j++){}
+ if( j<pLoop->nLTerm ) continue;
+ }
+
+ /* No subqueries or non-deterministic functions allowed */
+ if( sqlite3ExprContainsSubquery(pTerm->pExpr) ) continue;
+
+ /* For an index scan, make sure referenced columns are actually in
+ ** the index. */
+ if( sHint.pIdx!=0 ){
+ sWalker.eCode = 0;
+ sWalker.xExprCallback = codeCursorHintCheckExpr;
+ sqlite3WalkExpr(&sWalker, pTerm->pExpr);
+ if( sWalker.eCode ) continue;
+ }
+
+ /* If we survive all prior tests, that means this term is worth hinting */
+ pExpr = sqlite3ExprAnd(db, pExpr, sqlite3ExprDup(db, pTerm->pExpr, 0));
+ }
+ if( pExpr!=0 ){
+ sWalker.xExprCallback = codeCursorHintFixExpr;
+ sqlite3WalkExpr(&sWalker, pExpr);
+ sqlite3VdbeAddOp4(v, OP_CursorHint,
+ (sHint.pIdx ? sHint.iIdxCur : sHint.iTabCur), 0, 0,
+ (const char*)pExpr, P4_EXPR);
+ }
+}
+#else
+# define codeCursorHint(A,B,C,D) /* No-op */
+#endif /* SQLITE_ENABLE_CURSOR_HINTS */
+
+/*
+** Cursor iCur is open on an intkey b-tree (a table). Register iRowid contains
+** a rowid value just read from cursor iIdxCur, open on index pIdx. This
+** function generates code to do a deferred seek of cursor iCur to the
+** rowid stored in register iRowid.
**
-** In order for this routine to work, the calling function must have
-** previously invoked sqlite3ResolveExprNames() on the expression. See
-** the header comment on that routine for additional information.
-** The sqlite3ResolveExprNames() routines looks for column names and
-** sets their opcodes to TK_COLUMN and their Expr.iTable fields to
-** the VDBE cursor number of the table. This routine just has to
-** translate the cursor numbers into bitmask values and OR all
-** the bitmasks together.
-*/
-static Bitmask exprListTableUsage(WhereMaskSet*, ExprList*);
-static Bitmask exprSelectTableUsage(WhereMaskSet*, Select*);
-static Bitmask exprTableUsage(WhereMaskSet *pMaskSet, Expr *p){
- Bitmask mask = 0;
- if( p==0 ) return 0;
- if( p->op==TK_COLUMN ){
- mask = getMask(pMaskSet, p->iTable);
- return mask;
+** Normally, this is just:
+**
+** OP_Seek $iCur $iRowid
+**
+** However, if the scan currently being coded is a branch of an OR-loop and
+** the statement currently being coded is a SELECT, then P3 of the OP_Seek
+** is set to iIdxCur and P4 is set to point to an array of integers
+** containing one entry for each column of the table cursor iCur is open
+** on. For each table column, if the column is the i'th column of the
+** index, then the corresponding array entry is set to (i+1). If the column
+** does not appear in the index at all, the array entry is set to 0.
+*/
+static void codeDeferredSeek(
+ WhereInfo *pWInfo, /* Where clause context */
+ Index *pIdx, /* Index scan is using */
+ int iCur, /* Cursor for IPK b-tree */
+ int iIdxCur /* Index cursor */
+){
+ Parse *pParse = pWInfo->pParse; /* Parse context */
+ Vdbe *v = pParse->pVdbe; /* Vdbe to generate code within */
+
+ assert( iIdxCur>0 );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
+
+ sqlite3VdbeAddOp3(v, OP_Seek, iIdxCur, 0, iCur);
+ if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
+ && DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
+ ){
+ int i;
+ Table *pTab = pIdx->pTable;
+ int *ai = (int*)sqlite3DbMallocZero(pParse->db, sizeof(int)*(pTab->nCol+1));
+ if( ai ){
+ ai[0] = pTab->nCol;
+ for(i=0; i<pIdx->nColumn-1; i++){
+ assert( pIdx->aiColumn[i]<pTab->nCol );
+ if( pIdx->aiColumn[i]>=0 ) ai[pIdx->aiColumn[i]+1] = i+1;
+ }
+ sqlite3VdbeChangeP4(v, -1, (char*)ai, P4_INTARRAY);
+ }
}
- mask = exprTableUsage(pMaskSet, p->pRight);
- mask |= exprTableUsage(pMaskSet, p->pLeft);
- if( ExprHasProperty(p, EP_xIsSelect) ){
- mask |= exprSelectTableUsage(pMaskSet, p->x.pSelect);
+}
+
+/*
+** If the expression passed as the second argument is a vector, generate
+** code to write the first nReg elements of the vector into an array
+** of registers starting with iReg.
+**
+** If the expression is not a vector, then nReg must be passed 1. In
+** this case, generate code to evaluate the expression and leave the
+** result in register iReg.
+*/
+static void codeExprOrVector(Parse *pParse, Expr *p, int iReg, int nReg){
+ assert( nReg>0 );
+ if( sqlite3ExprIsVector(p) ){
+#ifndef SQLITE_OMIT_SUBQUERY
+ if( (p->flags & EP_xIsSelect) ){
+ Vdbe *v = pParse->pVdbe;
+ int iSelect = sqlite3CodeSubselect(pParse, p, 0, 0);
+ sqlite3VdbeAddOp3(v, OP_Copy, iSelect, iReg, nReg-1);
+ }else
+#endif
+ {
+ int i;
+ ExprList *pList = p->x.pList;
+ assert( nReg<=pList->nExpr );
+ for(i=0; i<nReg; i++){
+ sqlite3ExprCode(pParse, pList->a[i].pExpr, iReg+i);
+ }
+ }
}else{
- mask |= exprListTableUsage(pMaskSet, p->x.pList);
+ assert( nReg==1 );
+ sqlite3ExprCode(pParse, p, iReg);
}
- return mask;
}
-static Bitmask exprListTableUsage(WhereMaskSet *pMaskSet, ExprList *pList){
- int i;
- Bitmask mask = 0;
- if( pList ){
- for(i=0; i<pList->nExpr; i++){
- mask |= exprTableUsage(pMaskSet, pList->a[i].pExpr);
+
+/*
+** Generate code for the start of the iLevel-th loop in the WHERE clause
+** implementation described by pWInfo.
+*/
+SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
+ WhereInfo *pWInfo, /* Complete information about the WHERE clause */
+ int iLevel, /* Which level of pWInfo->a[] should be coded */
+ Bitmask notReady /* Which tables are currently available */
+){
+ int j, k; /* Loop counters */
+ int iCur; /* The VDBE cursor for the table */
+ int addrNxt; /* Where to jump to continue with the next IN case */
+ int omitTable; /* True if we use the index only */
+ int bRev; /* True if we need to scan in reverse order */
+ WhereLevel *pLevel; /* The where level to be coded */
+ WhereLoop *pLoop; /* The WhereLoop object being coded */
+ WhereClause *pWC; /* Decomposition of the entire WHERE clause */
+ WhereTerm *pTerm; /* A WHERE clause term */
+ Parse *pParse; /* Parsing context */
+ sqlite3 *db; /* Database connection */
+ Vdbe *v; /* The prepared stmt under constructions */
+ struct SrcList_item *pTabItem; /* FROM clause term being coded */
+ int addrBrk; /* Jump here to break out of the loop */
+ int addrCont; /* Jump here to continue with next cycle */
+ int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
+ int iReleaseReg = 0; /* Temp register to free before returning */
+
+ pParse = pWInfo->pParse;
+ v = pParse->pVdbe;
+ pWC = &pWInfo->sWC;
+ db = pParse->db;
+ pLevel = &pWInfo->a[iLevel];
+ pLoop = pLevel->pWLoop;
+ pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
+ iCur = pTabItem->iCursor;
+ pLevel->notReady = notReady & ~sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
+ bRev = (pWInfo->revMask>>iLevel)&1;
+ omitTable = (pLoop->wsFlags & WHERE_IDX_ONLY)!=0
+ && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0;
+ VdbeModuleComment((v, "Begin WHERE-loop%d: %s",iLevel,pTabItem->pTab->zName));
+
+ /* Create labels for the "break" and "continue" instructions
+ ** for the current loop. Jump to addrBrk to break out of a loop.
+ ** Jump to cont to go immediately to the next iteration of the
+ ** loop.
+ **
+ ** When there is an IN operator, we also have a "addrNxt" label that
+ ** means to continue with the next IN value combination. When
+ ** there are no IN operators in the constraints, the "addrNxt" label
+ ** is the same as "addrBrk".
+ */
+ addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
+ addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v);
+
+ /* If this is the right table of a LEFT OUTER JOIN, allocate and
+ ** initialize a memory cell that records if this table matches any
+ ** row of the left table of the join.
+ */
+ if( pLevel->iFrom>0 && (pTabItem[0].fg.jointype & JT_LEFT)!=0 ){
+ pLevel->iLeftJoin = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
+ VdbeComment((v, "init LEFT JOIN no-match flag"));
+ }
+
+ /* Special case of a FROM clause subquery implemented as a co-routine */
+ if( pTabItem->fg.viaCoroutine ){
+ int regYield = pTabItem->regReturn;
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
+ pLevel->p2 = sqlite3VdbeAddOp2(v, OP_Yield, regYield, addrBrk);
+ VdbeCoverage(v);
+ VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
+ pLevel->op = OP_Goto;
+ }else
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+ /* Case 1: The table is a virtual-table. Use the VFilter and VNext
+ ** to access the data.
+ */
+ int iReg; /* P3 Value for OP_VFilter */
+ int addrNotFound;
+ int nConstraint = pLoop->nLTerm;
+ int iIn; /* Counter for IN constraints */
+
+ sqlite3ExprCachePush(pParse);
+ iReg = sqlite3GetTempRange(pParse, nConstraint+2);
+ addrNotFound = pLevel->addrBrk;
+ for(j=0; j<nConstraint; j++){
+ int iTarget = iReg+j+2;
+ pTerm = pLoop->aLTerm[j];
+ if( NEVER(pTerm==0) ) continue;
+ if( pTerm->eOperator & WO_IN ){
+ codeEqualityTerm(pParse, pTerm, pLevel, j, bRev, iTarget);
+ addrNotFound = pLevel->addrNxt;
+ }else{
+ Expr *pRight = pTerm->pExpr->pRight;
+ codeExprOrVector(pParse, pRight, iTarget, 1);
+ }
+ }
+ sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
+ sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1);
+ sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg,
+ pLoop->u.vtab.idxStr,
+ pLoop->u.vtab.needFree ? P4_MPRINTF : P4_STATIC);
+ VdbeCoverage(v);
+ pLoop->u.vtab.needFree = 0;
+ pLevel->p1 = iCur;
+ pLevel->op = pWInfo->eOnePass ? OP_Noop : OP_VNext;
+ pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+ iIn = pLevel->u.in.nIn;
+ for(j=nConstraint-1; j>=0; j--){
+ pTerm = pLoop->aLTerm[j];
+ if( j<16 && (pLoop->u.vtab.omitMask>>j)&1 ){
+ disableTerm(pLevel, pTerm);
+ }else if( (pTerm->eOperator & WO_IN)!=0 ){
+ Expr *pCompare; /* The comparison operator */
+ Expr *pRight; /* RHS of the comparison */
+ VdbeOp *pOp; /* Opcode to access the value of the IN constraint */
+
+ /* Reload the constraint value into reg[iReg+j+2]. The same value
+ ** was loaded into the same register prior to the OP_VFilter, but
+ ** the xFilter implementation might have changed the datatype or
+ ** encoding of the value in the register, so it *must* be reloaded. */
+ assert( pLevel->u.in.aInLoop!=0 || db->mallocFailed );
+ if( !db->mallocFailed ){
+ assert( iIn>0 );
+ pOp = sqlite3VdbeGetOp(v, pLevel->u.in.aInLoop[--iIn].addrInTop);
+ assert( pOp->opcode==OP_Column || pOp->opcode==OP_Rowid );
+ assert( pOp->opcode!=OP_Column || pOp->p3==iReg+j+2 );
+ assert( pOp->opcode!=OP_Rowid || pOp->p2==iReg+j+2 );
+ testcase( pOp->opcode==OP_Rowid );
+ sqlite3VdbeAddOp3(v, pOp->opcode, pOp->p1, pOp->p2, pOp->p3);
+ }
+
+ /* Generate code that will continue to the next row if
+ ** the IN constraint is not satisfied */
+ pCompare = sqlite3PExpr(pParse, TK_EQ, 0, 0, 0);
+ assert( pCompare!=0 || db->mallocFailed );
+ if( pCompare ){
+ pCompare->pLeft = pTerm->pExpr->pLeft;
+ pCompare->pRight = pRight = sqlite3Expr(db, TK_REGISTER, 0);
+ if( pRight ){
+ pRight->iTable = iReg+j+2;
+ sqlite3ExprIfFalse(pParse, pCompare, pLevel->addrCont, 0);
+ }
+ pCompare->pLeft = 0;
+ sqlite3ExprDelete(db, pCompare);
+ }
+ }
+ }
+ /* These registers need to be preserved in case there is an IN operator
+ ** loop. So we could deallocate the registers here (and potentially
+ ** reuse them later) if (pLoop->wsFlags & WHERE_IN_ABLE)==0. But it seems
+ ** simpler and safer to simply not reuse the registers.
+ **
+ ** sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
+ */
+ sqlite3ExprCachePop(pParse);
+ }else
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+ if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_EQ))!=0
+ ){
+ /* Case 2: We can directly reference a single row using an
+ ** equality comparison against the ROWID field. Or
+ ** we reference multiple rows using a "rowid IN (...)"
+ ** construct.
+ */
+ assert( pLoop->u.btree.nEq==1 );
+ pTerm = pLoop->aLTerm[0];
+ assert( pTerm!=0 );
+ assert( pTerm->pExpr!=0 );
+ assert( omitTable==0 );
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ iReleaseReg = ++pParse->nMem;
+ iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, 0, bRev, iReleaseReg);
+ if( iRowidReg!=iReleaseReg ) sqlite3ReleaseTempReg(pParse, iReleaseReg);
+ addrNxt = pLevel->addrNxt;
+ sqlite3VdbeAddOp3(v, OP_SeekRowid, iCur, addrNxt, iRowidReg);
+ VdbeCoverage(v);
+ sqlite3ExprCacheAffinityChange(pParse, iRowidReg, 1);
+ sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
+ VdbeComment((v, "pk"));
+ pLevel->op = OP_Noop;
+ }else if( (pLoop->wsFlags & WHERE_IPK)!=0
+ && (pLoop->wsFlags & WHERE_COLUMN_RANGE)!=0
+ ){
+ /* Case 3: We have an inequality comparison against the ROWID field.
+ */
+ int testOp = OP_Noop;
+ int start;
+ int memEndValue = 0;
+ WhereTerm *pStart, *pEnd;
+
+ assert( omitTable==0 );
+ j = 0;
+ pStart = pEnd = 0;
+ if( pLoop->wsFlags & WHERE_BTM_LIMIT ) pStart = pLoop->aLTerm[j++];
+ if( pLoop->wsFlags & WHERE_TOP_LIMIT ) pEnd = pLoop->aLTerm[j++];
+ assert( pStart!=0 || pEnd!=0 );
+ if( bRev ){
+ pTerm = pStart;
+ pStart = pEnd;
+ pEnd = pTerm;
+ }
+ codeCursorHint(pTabItem, pWInfo, pLevel, pEnd);
+ if( pStart ){
+ Expr *pX; /* The expression that defines the start bound */
+ int r1, rTemp; /* Registers for holding the start boundary */
+ int op; /* Cursor seek operation */
+
+ /* The following constant maps TK_xx codes into corresponding
+ ** seek opcodes. It depends on a particular ordering of TK_xx
+ */
+ const u8 aMoveOp[] = {
+ /* TK_GT */ OP_SeekGT,
+ /* TK_LE */ OP_SeekLE,
+ /* TK_LT */ OP_SeekLT,
+ /* TK_GE */ OP_SeekGE
+ };
+ assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */
+ assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
+ assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
+
+ assert( (pStart->wtFlags & TERM_VNULL)==0 );
+ testcase( pStart->wtFlags & TERM_VIRTUAL );
+ pX = pStart->pExpr;
+ assert( pX!=0 );
+ testcase( pStart->leftCursor!=iCur ); /* transitive constraints */
+ if( sqlite3ExprIsVector(pX->pRight) ){
+ r1 = rTemp = sqlite3GetTempReg(pParse);
+ codeExprOrVector(pParse, pX->pRight, r1, 1);
+ op = aMoveOp[(pX->op - TK_GT) | 0x0001];
+ }else{
+ r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
+ disableTerm(pLevel, pStart);
+ op = aMoveOp[(pX->op - TK_GT)];
+ }
+ sqlite3VdbeAddOp3(v, op, iCur, addrBrk, r1);
+ VdbeComment((v, "pk"));
+ VdbeCoverageIf(v, pX->op==TK_GT);
+ VdbeCoverageIf(v, pX->op==TK_LE);
+ VdbeCoverageIf(v, pX->op==TK_LT);
+ VdbeCoverageIf(v, pX->op==TK_GE);
+ sqlite3ExprCacheAffinityChange(pParse, r1, 1);
+ sqlite3ReleaseTempReg(pParse, rTemp);
+ }else{
+ sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ }
+ if( pEnd ){
+ Expr *pX;
+ pX = pEnd->pExpr;
+ assert( pX!=0 );
+ assert( (pEnd->wtFlags & TERM_VNULL)==0 );
+ testcase( pEnd->leftCursor!=iCur ); /* Transitive constraints */
+ testcase( pEnd->wtFlags & TERM_VIRTUAL );
+ memEndValue = ++pParse->nMem;
+ codeExprOrVector(pParse, pX->pRight, memEndValue, 1);
+ if( 0==sqlite3ExprIsVector(pX->pRight)
+ && (pX->op==TK_LT || pX->op==TK_GT)
+ ){
+ testOp = bRev ? OP_Le : OP_Ge;
+ }else{
+ testOp = bRev ? OP_Lt : OP_Gt;
+ }
+ if( 0==sqlite3ExprIsVector(pX->pRight) ){
+ disableTerm(pLevel, pEnd);
+ }
+ }
+ start = sqlite3VdbeCurrentAddr(v);
+ pLevel->op = bRev ? OP_Prev : OP_Next;
+ pLevel->p1 = iCur;
+ pLevel->p2 = start;
+ assert( pLevel->p5==0 );
+ if( testOp!=OP_Noop ){
+ iRowidReg = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
+ sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
+ sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
+ VdbeCoverageIf(v, testOp==OP_Le);
+ VdbeCoverageIf(v, testOp==OP_Lt);
+ VdbeCoverageIf(v, testOp==OP_Ge);
+ VdbeCoverageIf(v, testOp==OP_Gt);
+ sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
+ }
+ }else if( pLoop->wsFlags & WHERE_INDEXED ){
+ /* Case 4: A scan using an index.
+ **
+ ** The WHERE clause may contain zero or more equality
+ ** terms ("==" or "IN" operators) that refer to the N
+ ** left-most columns of the index. It may also contain
+ ** inequality constraints (>, <, >= or <=) on the indexed
+ ** column that immediately follows the N equalities. Only
+ ** the right-most column can be an inequality - the rest must
+ ** use the "==" and "IN" operators. For example, if the
+ ** index is on (x,y,z), then the following clauses are all
+ ** optimized:
+ **
+ ** x=5
+ ** x=5 AND y=10
+ ** x=5 AND y<10
+ ** x=5 AND y>5 AND y<10
+ ** x=5 AND y=5 AND z<=10
+ **
+ ** The z<10 term of the following cannot be used, only
+ ** the x=5 term:
+ **
+ ** x=5 AND z<10
+ **
+ ** N may be zero if there are inequality constraints.
+ ** If there are no inequality constraints, then N is at
+ ** least one.
+ **
+ ** This case is also used when there are no WHERE clause
+ ** constraints but an index is selected anyway, in order
+ ** to force the output order to conform to an ORDER BY.
+ */
+ static const u8 aStartOp[] = {
+ 0,
+ 0,
+ OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
+ OP_Last, /* 3: (!start_constraints && startEq && bRev) */
+ OP_SeekGT, /* 4: (start_constraints && !startEq && !bRev) */
+ OP_SeekLT, /* 5: (start_constraints && !startEq && bRev) */
+ OP_SeekGE, /* 6: (start_constraints && startEq && !bRev) */
+ OP_SeekLE /* 7: (start_constraints && startEq && bRev) */
+ };
+ static const u8 aEndOp[] = {
+ OP_IdxGE, /* 0: (end_constraints && !bRev && !endEq) */
+ OP_IdxGT, /* 1: (end_constraints && !bRev && endEq) */
+ OP_IdxLE, /* 2: (end_constraints && bRev && !endEq) */
+ OP_IdxLT, /* 3: (end_constraints && bRev && endEq) */
+ };
+ u16 nEq = pLoop->u.btree.nEq; /* Number of == or IN terms */
+ u16 nBtm = pLoop->u.btree.nBtm; /* Length of BTM vector */
+ u16 nTop = pLoop->u.btree.nTop; /* Length of TOP vector */
+ int regBase; /* Base register holding constraint values */
+ WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
+ WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
+ int startEq; /* True if range start uses ==, >= or <= */
+ int endEq; /* True if range end uses ==, >= or <= */
+ int start_constraints; /* Start of range is constrained */
+ int nConstraint; /* Number of constraint terms */
+ Index *pIdx; /* The index we will be using */
+ int iIdxCur; /* The VDBE cursor for the index */
+ int nExtraReg = 0; /* Number of extra registers needed */
+ int op; /* Instruction opcode */
+ char *zStartAff; /* Affinity for start of range constraint */
+ char *zEndAff = 0; /* Affinity for end of range constraint */
+ u8 bSeekPastNull = 0; /* True to seek past initial nulls */
+ u8 bStopAtNull = 0; /* Add condition to terminate at NULLs */
+
+ pIdx = pLoop->u.btree.pIndex;
+ iIdxCur = pLevel->iIdxCur;
+ assert( nEq>=pLoop->nSkip );
+
+ /* If this loop satisfies a sort order (pOrderBy) request that
+ ** was passed to this function to implement a "SELECT min(x) ..."
+ ** query, then the caller will only allow the loop to run for
+ ** a single iteration. This means that the first row returned
+ ** should not have a NULL value stored in 'x'. If column 'x' is
+ ** the first one after the nEq equality constraints in the index,
+ ** this requires some special handling.
+ */
+ assert( pWInfo->pOrderBy==0
+ || pWInfo->pOrderBy->nExpr==1
+ || (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0 );
+ if( (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)!=0
+ && pWInfo->nOBSat>0
+ && (pIdx->nKeyCol>nEq)
+ ){
+ assert( pLoop->nSkip==0 );
+ bSeekPastNull = 1;
+ nExtraReg = 1;
+ }
+
+ /* Find any inequality constraint terms for the start and end
+ ** of the range.
+ */
+ j = nEq;
+ if( pLoop->wsFlags & WHERE_BTM_LIMIT ){
+ pRangeStart = pLoop->aLTerm[j++];
+ nExtraReg = MAX(nExtraReg, pLoop->u.btree.nBtm);
+ /* Like optimization range constraints always occur in pairs */
+ assert( (pRangeStart->wtFlags & TERM_LIKEOPT)==0 ||
+ (pLoop->wsFlags & WHERE_TOP_LIMIT)!=0 );
+ }
+ if( pLoop->wsFlags & WHERE_TOP_LIMIT ){
+ pRangeEnd = pLoop->aLTerm[j++];
+ nExtraReg = MAX(nExtraReg, pLoop->u.btree.nTop);
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ if( (pRangeEnd->wtFlags & TERM_LIKEOPT)!=0 ){
+ assert( pRangeStart!=0 ); /* LIKE opt constraints */
+ assert( pRangeStart->wtFlags & TERM_LIKEOPT ); /* occur in pairs */
+ pLevel->iLikeRepCntr = (u32)++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, (int)pLevel->iLikeRepCntr);
+ VdbeComment((v, "LIKE loop counter"));
+ pLevel->addrLikeRep = sqlite3VdbeCurrentAddr(v);
+ /* iLikeRepCntr actually stores 2x the counter register number. The
+ ** bottom bit indicates whether the search order is ASC or DESC. */
+ testcase( bRev );
+ testcase( pIdx->aSortOrder[nEq]==SQLITE_SO_DESC );
+ assert( (bRev & ~1)==0 );
+ pLevel->iLikeRepCntr <<=1;
+ pLevel->iLikeRepCntr |= bRev ^ (pIdx->aSortOrder[nEq]==SQLITE_SO_DESC);
+ }
+#endif
+ if( pRangeStart==0 ){
+ j = pIdx->aiColumn[nEq];
+ if( (j>=0 && pIdx->pTable->aCol[j].notNull==0) || j==XN_EXPR ){
+ bSeekPastNull = 1;
+ }
+ }
+ }
+ assert( pRangeEnd==0 || (pRangeEnd->wtFlags & TERM_VNULL)==0 );
+
+ /* If we are doing a reverse order scan on an ascending index, or
+ ** a forward order scan on a descending index, interchange the
+ ** start and end terms (pRangeStart and pRangeEnd).
+ */
+ if( (nEq<pIdx->nKeyCol && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC))
+ || (bRev && pIdx->nKeyCol==nEq)
+ ){
+ SWAP(WhereTerm *, pRangeEnd, pRangeStart);
+ SWAP(u8, bSeekPastNull, bStopAtNull);
+ SWAP(u8, nBtm, nTop);
+ }
+
+ /* Generate code to evaluate all constraint terms using == or IN
+ ** and store the values of those terms in an array of registers
+ ** starting at regBase.
+ */
+ codeCursorHint(pTabItem, pWInfo, pLevel, pRangeEnd);
+ regBase = codeAllEqualityTerms(pParse,pLevel,bRev,nExtraReg,&zStartAff);
+ assert( zStartAff==0 || sqlite3Strlen30(zStartAff)>=nEq );
+ if( zStartAff && nTop ){
+ zEndAff = sqlite3DbStrDup(db, &zStartAff[nEq]);
+ }
+ addrNxt = pLevel->addrNxt;
+
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_LE)!=0 );
+ testcase( pRangeStart && (pRangeStart->eOperator & WO_GE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_LE)!=0 );
+ testcase( pRangeEnd && (pRangeEnd->eOperator & WO_GE)!=0 );
+ startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE);
+ endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE);
+ start_constraints = pRangeStart || nEq>0;
+
+ /* Seek the index cursor to the start of the range. */
+ nConstraint = nEq;
+ if( pRangeStart ){
+ Expr *pRight = pRangeStart->pExpr->pRight;
+ codeExprOrVector(pParse, pRight, regBase+nEq, nBtm);
+ whereLikeOptimizationStringFixup(v, pLevel, pRangeStart);
+ if( (pRangeStart->wtFlags & TERM_VNULL)==0
+ && sqlite3ExprCanBeNull(pRight)
+ ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
+ VdbeCoverage(v);
+ }
+ if( zStartAff ){
+ updateRangeAffinityStr(pRight, nBtm, &zStartAff[nEq]);
+ }
+ nConstraint += nBtm;
+ testcase( pRangeStart->wtFlags & TERM_VIRTUAL );
+ if( sqlite3ExprIsVector(pRight)==0 ){
+ disableTerm(pLevel, pRangeStart);
+ }else{
+ startEq = 1;
+ }
+ bSeekPastNull = 0;
+ }else if( bSeekPastNull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ nConstraint++;
+ startEq = 0;
+ start_constraints = 1;
+ }
+ codeApplyAffinity(pParse, regBase, nConstraint - bSeekPastNull, zStartAff);
+ if( pLoop->nSkip>0 && nConstraint==pLoop->nSkip ){
+ /* The skip-scan logic inside the call to codeAllEqualityConstraints()
+ ** above has already left the cursor sitting on the correct row,
+ ** so no further seeking is needed */
+ }else{
+ op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
+ assert( op!=0 );
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, op==OP_Rewind); testcase( op==OP_Rewind );
+ VdbeCoverageIf(v, op==OP_Last); testcase( op==OP_Last );
+ VdbeCoverageIf(v, op==OP_SeekGT); testcase( op==OP_SeekGT );
+ VdbeCoverageIf(v, op==OP_SeekGE); testcase( op==OP_SeekGE );
+ VdbeCoverageIf(v, op==OP_SeekLE); testcase( op==OP_SeekLE );
+ VdbeCoverageIf(v, op==OP_SeekLT); testcase( op==OP_SeekLT );
+ }
+
+ /* Load the value for the inequality constraint at the end of the
+ ** range (if any).
+ */
+ nConstraint = nEq;
+ if( pRangeEnd ){
+ Expr *pRight = pRangeEnd->pExpr->pRight;
+ sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
+ codeExprOrVector(pParse, pRight, regBase+nEq, nTop);
+ whereLikeOptimizationStringFixup(v, pLevel, pRangeEnd);
+ if( (pRangeEnd->wtFlags & TERM_VNULL)==0
+ && sqlite3ExprCanBeNull(pRight)
+ ){
+ sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt);
+ VdbeCoverage(v);
+ }
+ if( zEndAff ){
+ updateRangeAffinityStr(pRight, nTop, zEndAff);
+ codeApplyAffinity(pParse, regBase+nEq, nTop, zEndAff);
+ }else{
+ assert( pParse->db->mallocFailed );
+ }
+ nConstraint += nTop;
+ testcase( pRangeEnd->wtFlags & TERM_VIRTUAL );
+
+ if( sqlite3ExprIsVector(pRight)==0 ){
+ disableTerm(pLevel, pRangeEnd);
+ }else{
+ endEq = 1;
+ }
+ }else if( bStopAtNull ){
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
+ endEq = 0;
+ nConstraint++;
+ }
+ sqlite3DbFree(db, zStartAff);
+ sqlite3DbFree(db, zEndAff);
+
+ /* Top of the loop body */
+ pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+
+ /* Check if the index cursor is past the end of the range. */
+ if( nConstraint ){
+ op = aEndOp[bRev*2 + endEq];
+ sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
+ testcase( op==OP_IdxGT ); VdbeCoverageIf(v, op==OP_IdxGT );
+ testcase( op==OP_IdxGE ); VdbeCoverageIf(v, op==OP_IdxGE );
+ testcase( op==OP_IdxLT ); VdbeCoverageIf(v, op==OP_IdxLT );
+ testcase( op==OP_IdxLE ); VdbeCoverageIf(v, op==OP_IdxLE );
+ }
+
+ /* Seek the table cursor, if required */
+ if( omitTable ){
+ /* pIdx is a covering index. No need to access the main table. */
+ }else if( HasRowid(pIdx->pTable) ){
+ if( (pWInfo->wctrlFlags & WHERE_SEEK_TABLE)!=0 ){
+ iRowidReg = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
+ sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
+ sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowidReg);
+ VdbeCoverage(v);
+ }else{
+ codeDeferredSeek(pWInfo, pIdx, iCur, iIdxCur);
+ }
+ }else if( iCur!=iIdxCur ){
+ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
+ iRowidReg = sqlite3GetTempRange(pParse, pPk->nKeyCol);
+ for(j=0; j<pPk->nKeyCol; j++){
+ k = sqlite3ColumnOfIndex(pIdx, pPk->aiColumn[j]);
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, iRowidReg+j);
+ }
+ sqlite3VdbeAddOp4Int(v, OP_NotFound, iCur, addrCont,
+ iRowidReg, pPk->nKeyCol); VdbeCoverage(v);
+ }
+
+ /* Record the instruction used to terminate the loop. */
+ if( pLoop->wsFlags & WHERE_ONEROW ){
+ pLevel->op = OP_Noop;
+ }else if( bRev ){
+ pLevel->op = OP_Prev;
+ }else{
+ pLevel->op = OP_Next;
+ }
+ pLevel->p1 = iIdxCur;
+ pLevel->p3 = (pLoop->wsFlags&WHERE_UNQ_WANTED)!=0 ? 1:0;
+ if( (pLoop->wsFlags & WHERE_CONSTRAINT)==0 ){
+ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
+ }else{
+ assert( pLevel->p5==0 );
+ }
+ }else
+
+#ifndef SQLITE_OMIT_OR_OPTIMIZATION
+ if( pLoop->wsFlags & WHERE_MULTI_OR ){
+ /* Case 5: Two or more separately indexed terms connected by OR
+ **
+ ** Example:
+ **
+ ** CREATE TABLE t1(a,b,c,d);
+ ** CREATE INDEX i1 ON t1(a);
+ ** CREATE INDEX i2 ON t1(b);
+ ** CREATE INDEX i3 ON t1(c);
+ **
+ ** SELECT * FROM t1 WHERE a=5 OR b=7 OR (c=11 AND d=13)
+ **
+ ** In the example, there are three indexed terms connected by OR.
+ ** The top of the loop looks like this:
+ **
+ ** Null 1 # Zero the rowset in reg 1
+ **
+ ** Then, for each indexed term, the following. The arguments to
+ ** RowSetTest are such that the rowid of the current row is inserted
+ ** into the RowSet. If it is already present, control skips the
+ ** Gosub opcode and jumps straight to the code generated by WhereEnd().
+ **
+ ** sqlite3WhereBegin(<term>)
+ ** RowSetTest # Insert rowid into rowset
+ ** Gosub 2 A
+ ** sqlite3WhereEnd()
+ **
+ ** Following the above, code to terminate the loop. Label A, the target
+ ** of the Gosub above, jumps to the instruction right after the Goto.
+ **
+ ** Null 1 # Zero the rowset in reg 1
+ ** Goto B # The loop is finished.
+ **
+ ** A: <loop body> # Return data, whatever.
+ **
+ ** Return 2 # Jump back to the Gosub
+ **
+ ** B: <after the loop>
+ **
+ ** Added 2014-05-26: If the table is a WITHOUT ROWID table, then
+ ** use an ephemeral index instead of a RowSet to record the primary
+ ** keys of the rows we have already seen.
+ **
+ */
+ WhereClause *pOrWc; /* The OR-clause broken out into subterms */
+ SrcList *pOrTab; /* Shortened table list or OR-clause generation */
+ Index *pCov = 0; /* Potential covering index (or NULL) */
+ int iCovCur = pParse->nTab++; /* Cursor used for index scans (if any) */
+
+ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
+ int regRowset = 0; /* Register for RowSet object */
+ int regRowid = 0; /* Register holding rowid */
+ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
+ int iRetInit; /* Address of regReturn init */
+ int untestedTerms = 0; /* Some terms not completely tested */
+ int ii; /* Loop counter */
+ u16 wctrlFlags; /* Flags for sub-WHERE clause */
+ Expr *pAndExpr = 0; /* An ".. AND (...)" expression */
+ Table *pTab = pTabItem->pTab;
+
+ pTerm = pLoop->aLTerm[0];
+ assert( pTerm!=0 );
+ assert( pTerm->eOperator & WO_OR );
+ assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
+ pOrWc = &pTerm->u.pOrInfo->wc;
+ pLevel->op = OP_Return;
+ pLevel->p1 = regReturn;
+
+ /* Set up a new SrcList in pOrTab containing the table being scanned
+ ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
+ ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
+ */
+ if( pWInfo->nLevel>1 ){
+ int nNotReady; /* The number of notReady tables */
+ struct SrcList_item *origSrc; /* Original list of tables */
+ nNotReady = pWInfo->nLevel - iLevel - 1;
+ pOrTab = sqlite3StackAllocRaw(db,
+ sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
+ if( pOrTab==0 ) return notReady;
+ pOrTab->nAlloc = (u8)(nNotReady + 1);
+ pOrTab->nSrc = pOrTab->nAlloc;
+ memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
+ origSrc = pWInfo->pTabList->a;
+ for(k=1; k<=nNotReady; k++){
+ memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
+ }
+ }else{
+ pOrTab = pWInfo->pTabList;
+ }
+
+ /* Initialize the rowset register to contain NULL. An SQL NULL is
+ ** equivalent to an empty rowset. Or, create an ephemeral index
+ ** capable of holding primary keys in the case of a WITHOUT ROWID.
+ **
+ ** Also initialize regReturn to contain the address of the instruction
+ ** immediately following the OP_Return at the bottom of the loop. This
+ ** is required in a few obscure LEFT JOIN cases where control jumps
+ ** over the top of the loop into the body of it. In this case the
+ ** correct response for the end-of-loop code (the OP_Return) is to
+ ** fall through to the next instruction, just as an OP_Next does if
+ ** called on an uninitialized cursor.
+ */
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
+ if( HasRowid(pTab) ){
+ regRowset = ++pParse->nMem;
+ sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ regRowset = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_OpenEphemeral, regRowset, pPk->nKeyCol);
+ sqlite3VdbeSetP4KeyInfo(pParse, pPk);
+ }
+ regRowid = ++pParse->nMem;
+ }
+ iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
+
+ /* If the original WHERE clause is z of the form: (x1 OR x2 OR ...) AND y
+ ** Then for every term xN, evaluate as the subexpression: xN AND z
+ ** That way, terms in y that are factored into the disjunction will
+ ** be picked up by the recursive calls to sqlite3WhereBegin() below.
+ **
+ ** Actually, each subexpression is converted to "xN AND w" where w is
+ ** the "interesting" terms of z - terms that did not originate in the
+ ** ON or USING clause of a LEFT JOIN, and terms that are usable as
+ ** indices.
+ **
+ ** This optimization also only applies if the (x1 OR x2 OR ...) term
+ ** is not contained in the ON clause of a LEFT JOIN.
+ ** See ticket http://www.sqlite.org/src/info/f2369304e4
+ */
+ if( pWC->nTerm>1 ){
+ int iTerm;
+ for(iTerm=0; iTerm<pWC->nTerm; iTerm++){
+ Expr *pExpr = pWC->a[iTerm].pExpr;
+ if( &pWC->a[iTerm] == pTerm ) continue;
+ if( ExprHasProperty(pExpr, EP_FromJoin) ) continue;
+ testcase( pWC->a[iTerm].wtFlags & TERM_VIRTUAL );
+ testcase( pWC->a[iTerm].wtFlags & TERM_CODED );
+ if( (pWC->a[iTerm].wtFlags & (TERM_VIRTUAL|TERM_CODED))!=0 ) continue;
+ if( (pWC->a[iTerm].eOperator & WO_ALL)==0 ) continue;
+ testcase( pWC->a[iTerm].wtFlags & TERM_ORINFO );
+ pExpr = sqlite3ExprDup(db, pExpr, 0);
+ pAndExpr = sqlite3ExprAnd(db, pAndExpr, pExpr);
+ }
+ if( pAndExpr ){
+ pAndExpr = sqlite3PExpr(pParse, TK_AND|TKFLG_DONTFOLD, 0, pAndExpr, 0);
+ }
+ }
+
+ /* Run a separate WHERE clause for each term of the OR clause. After
+ ** eliminating duplicates from other WHERE clauses, the action for each
+ ** sub-WHERE clause is to to invoke the main loop body as a subroutine.
+ */
+ wctrlFlags = WHERE_OR_SUBCLAUSE | (pWInfo->wctrlFlags & WHERE_SEEK_TABLE);
+ for(ii=0; ii<pOrWc->nTerm; ii++){
+ WhereTerm *pOrTerm = &pOrWc->a[ii];
+ if( pOrTerm->leftCursor==iCur || (pOrTerm->eOperator & WO_AND)!=0 ){
+ WhereInfo *pSubWInfo; /* Info for single OR-term scan */
+ Expr *pOrExpr = pOrTerm->pExpr; /* Current OR clause term */
+ int jmp1 = 0; /* Address of jump operation */
+ if( pAndExpr && !ExprHasProperty(pOrExpr, EP_FromJoin) ){
+ pAndExpr->pLeft = pOrExpr;
+ pOrExpr = pAndExpr;
+ }
+ /* Loop through table entries that match term pOrTerm. */
+ WHERETRACE(0xffff, ("Subplan for OR-clause:\n"));
+ pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrExpr, 0, 0,
+ wctrlFlags, iCovCur);
+ assert( pSubWInfo || pParse->nErr || db->mallocFailed );
+ if( pSubWInfo ){
+ WhereLoop *pSubLoop;
+ int addrExplain = sqlite3WhereExplainOneScan(
+ pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
+ );
+ sqlite3WhereAddScanStatus(v, pOrTab, &pSubWInfo->a[0], addrExplain);
+
+ /* This is the sub-WHERE clause body. First skip over
+ ** duplicate rows from prior sub-WHERE clauses, and record the
+ ** rowid (or PRIMARY KEY) for the current row so that the same
+ ** row will be skipped in subsequent sub-WHERE clauses.
+ */
+ if( (pWInfo->wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
+ int r;
+ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
+ if( HasRowid(pTab) ){
+ r = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, regRowid, 0);
+ jmp1 = sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, 0,
+ r,iSet);
+ VdbeCoverage(v);
+ }else{
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ int nPk = pPk->nKeyCol;
+ int iPk;
+
+ /* Read the PK into an array of temp registers. */
+ r = sqlite3GetTempRange(pParse, nPk);
+ for(iPk=0; iPk<nPk; iPk++){
+ int iCol = pPk->aiColumn[iPk];
+ sqlite3ExprCodeGetColumnToReg(pParse, pTab, iCol, iCur, r+iPk);
+ }
+
+ /* Check if the temp table already contains this key. If so,
+ ** the row has already been included in the result set and
+ ** can be ignored (by jumping past the Gosub below). Otherwise,
+ ** insert the key into the temp table and proceed with processing
+ ** the row.
+ **
+ ** Use some of the same optimizations as OP_RowSetTest: If iSet
+ ** is zero, assume that the key cannot already be present in
+ ** the temp table. And if iSet is -1, assume that there is no
+ ** need to insert the key into the temp table, as it will never
+ ** be tested for. */
+ if( iSet ){
+ jmp1 = sqlite3VdbeAddOp4Int(v, OP_Found, regRowset, 0, r, nPk);
+ VdbeCoverage(v);
+ }
+ if( iSet>=0 ){
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, r, nPk, regRowid);
+ sqlite3VdbeAddOp3(v, OP_IdxInsert, regRowset, regRowid, 0);
+ if( iSet ) sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
+ }
+
+ /* Release the array of temp registers */
+ sqlite3ReleaseTempRange(pParse, r, nPk);
+ }
+ }
+
+ /* Invoke the main loop body as a subroutine */
+ sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
+
+ /* Jump here (skipping the main loop body subroutine) if the
+ ** current sub-WHERE row is a duplicate from prior sub-WHEREs. */
+ if( jmp1 ) sqlite3VdbeJumpHere(v, jmp1);
+
+ /* The pSubWInfo->untestedTerms flag means that this OR term
+ ** contained one or more AND term from a notReady table. The
+ ** terms from the notReady table could not be tested and will
+ ** need to be tested later.
+ */
+ if( pSubWInfo->untestedTerms ) untestedTerms = 1;
+
+ /* If all of the OR-connected terms are optimized using the same
+ ** index, and the index is opened using the same cursor number
+ ** by each call to sqlite3WhereBegin() made by this loop, it may
+ ** be possible to use that index as a covering index.
+ **
+ ** If the call to sqlite3WhereBegin() above resulted in a scan that
+ ** uses an index, and this is either the first OR-connected term
+ ** processed or the index is the same as that used by all previous
+ ** terms, set pCov to the candidate covering index. Otherwise, set
+ ** pCov to NULL to indicate that no candidate covering index will
+ ** be available.
+ */
+ pSubLoop = pSubWInfo->a[0].pWLoop;
+ assert( (pSubLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
+ if( (pSubLoop->wsFlags & WHERE_INDEXED)!=0
+ && (ii==0 || pSubLoop->u.btree.pIndex==pCov)
+ && (HasRowid(pTab) || !IsPrimaryKeyIndex(pSubLoop->u.btree.pIndex))
+ ){
+ assert( pSubWInfo->a[0].iIdxCur==iCovCur );
+ pCov = pSubLoop->u.btree.pIndex;
+ }else{
+ pCov = 0;
+ }
+
+ /* Finish the loop through table entries that match term pOrTerm. */
+ sqlite3WhereEnd(pSubWInfo);
+ }
+ }
+ }
+ pLevel->u.pCovidx = pCov;
+ if( pCov ) pLevel->iIdxCur = iCovCur;
+ if( pAndExpr ){
+ pAndExpr->pLeft = 0;
+ sqlite3ExprDelete(db, pAndExpr);
+ }
+ sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
+ sqlite3VdbeGoto(v, pLevel->addrBrk);
+ sqlite3VdbeResolveLabel(v, iLoopBody);
+
+ if( pWInfo->nLevel>1 ) sqlite3StackFree(db, pOrTab);
+ if( !untestedTerms ) disableTerm(pLevel, pTerm);
+ }else
+#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
+
+ {
+ /* Case 6: There is no usable index. We must do a complete
+ ** scan of the entire table.
+ */
+ static const u8 aStep[] = { OP_Next, OP_Prev };
+ static const u8 aStart[] = { OP_Rewind, OP_Last };
+ assert( bRev==0 || bRev==1 );
+ if( pTabItem->fg.isRecursive ){
+ /* Tables marked isRecursive have only a single row that is stored in
+ ** a pseudo-cursor. No need to Rewind or Next such cursors. */
+ pLevel->op = OP_Noop;
+ }else{
+ codeCursorHint(pTabItem, pWInfo, pLevel, 0);
+ pLevel->op = aStep[bRev];
+ pLevel->p1 = iCur;
+ pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
+ VdbeCoverageIf(v, bRev==0);
+ VdbeCoverageIf(v, bRev!=0);
+ pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
}
}
- return mask;
+
+#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
+ pLevel->addrVisit = sqlite3VdbeCurrentAddr(v);
+#endif
+
+ /* Insert code to test every subexpression that can be completely
+ ** computed using the current set of tables.
+ */
+ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
+ Expr *pE;
+ int skipLikeAddr = 0;
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ testcase( pTerm->wtFlags & TERM_CODED );
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
+ testcase( pWInfo->untestedTerms==0
+ && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 );
+ pWInfo->untestedTerms = 1;
+ continue;
+ }
+ pE = pTerm->pExpr;
+ assert( pE!=0 );
+ if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
+ continue;
+ }
+ if( pTerm->wtFlags & TERM_LIKECOND ){
+ /* If the TERM_LIKECOND flag is set, that means that the range search
+ ** is sufficient to guarantee that the LIKE operator is true, so we
+ ** can skip the call to the like(A,B) function. But this only works
+ ** for strings. So do not skip the call to the function on the pass
+ ** that compares BLOBs. */
+#ifdef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ continue;
+#else
+ u32 x = pLevel->iLikeRepCntr;
+ assert( x>0 );
+ skipLikeAddr = sqlite3VdbeAddOp1(v, (x&1)? OP_IfNot : OP_If, (int)(x>>1));
+ VdbeCoverage(v);
+#endif
+ }
+ sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL);
+ if( skipLikeAddr ) sqlite3VdbeJumpHere(v, skipLikeAddr);
+ pTerm->wtFlags |= TERM_CODED;
+ }
+
+ /* Insert code to test for implied constraints based on transitivity
+ ** of the "==" operator.
+ **
+ ** Example: If the WHERE clause contains "t1.a=t2.b" and "t2.b=123"
+ ** and we are coding the t1 loop and the t2 loop has not yet coded,
+ ** then we cannot use the "t1.a=t2.b" constraint, but we can code
+ ** the implied "t1.a=123" constraint.
+ */
+ for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
+ Expr *pE, sEAlt;
+ WhereTerm *pAlt;
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) continue;
+ if( (pTerm->eOperator & WO_EQUIV)==0 ) continue;
+ if( pTerm->leftCursor!=iCur ) continue;
+ if( pLevel->iLeftJoin ) continue;
+ pE = pTerm->pExpr;
+ assert( !ExprHasProperty(pE, EP_FromJoin) );
+ assert( (pTerm->prereqRight & pLevel->notReady)!=0 );
+ pAlt = sqlite3WhereFindTerm(pWC, iCur, pTerm->u.leftColumn, notReady,
+ WO_EQ|WO_IN|WO_IS, 0);
+ if( pAlt==0 ) continue;
+ if( pAlt->wtFlags & (TERM_CODED) ) continue;
+ testcase( pAlt->eOperator & WO_EQ );
+ testcase( pAlt->eOperator & WO_IS );
+ testcase( pAlt->eOperator & WO_IN );
+ VdbeModuleComment((v, "begin transitive constraint"));
+ sEAlt = *pAlt->pExpr;
+ sEAlt.pLeft = pE->pLeft;
+ sqlite3ExprIfFalse(pParse, &sEAlt, addrCont, SQLITE_JUMPIFNULL);
+ }
+
+ /* For a LEFT OUTER JOIN, generate code that will record the fact that
+ ** at least one row of the right table has matched the left table.
+ */
+ if( pLevel->iLeftJoin ){
+ pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
+ sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
+ VdbeComment((v, "record LEFT JOIN hit"));
+ sqlite3ExprCacheClear(pParse);
+ for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
+ testcase( pTerm->wtFlags & TERM_VIRTUAL );
+ testcase( pTerm->wtFlags & TERM_CODED );
+ if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
+ if( (pTerm->prereqAll & pLevel->notReady)!=0 ){
+ assert( pWInfo->untestedTerms );
+ continue;
+ }
+ assert( pTerm->pExpr );
+ sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
+ pTerm->wtFlags |= TERM_CODED;
+ }
+ }
+
+ return pLevel->notReady;
}
-static Bitmask exprSelectTableUsage(WhereMaskSet *pMaskSet, Select *pS){
- Bitmask mask = 0;
- while( pS ){
- SrcList *pSrc = pS->pSrc;
- mask |= exprListTableUsage(pMaskSet, pS->pEList);
- mask |= exprListTableUsage(pMaskSet, pS->pGroupBy);
- mask |= exprListTableUsage(pMaskSet, pS->pOrderBy);
- mask |= exprTableUsage(pMaskSet, pS->pWhere);
- mask |= exprTableUsage(pMaskSet, pS->pHaving);
- if( ALWAYS(pSrc!=0) ){
- int i;
- for(i=0; i<pSrc->nSrc; i++){
- mask |= exprSelectTableUsage(pMaskSet, pSrc->a[i].pSelect);
- mask |= exprTableUsage(pMaskSet, pSrc->a[i].pOn);
+
+/************** End of wherecode.c *******************************************/
+/************** Begin file whereexpr.c ***************************************/
+/*
+** 2015-06-08
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This module contains C code that generates VDBE code used to process
+** the WHERE clause of SQL statements.
+**
+** This file was originally part of where.c but was split out to improve
+** readability and editabiliity. This file contains utility routines for
+** analyzing Expr objects in the WHERE clause.
+*/
+/* #include "sqliteInt.h" */
+/* #include "whereInt.h" */
+
+/* Forward declarations */
+static void exprAnalyze(SrcList*, WhereClause*, int);
+
+/*
+** Deallocate all memory associated with a WhereOrInfo object.
+*/
+static void whereOrInfoDelete(sqlite3 *db, WhereOrInfo *p){
+ sqlite3WhereClauseClear(&p->wc);
+ sqlite3DbFree(db, p);
+}
+
+/*
+** Deallocate all memory associated with a WhereAndInfo object.
+*/
+static void whereAndInfoDelete(sqlite3 *db, WhereAndInfo *p){
+ sqlite3WhereClauseClear(&p->wc);
+ sqlite3DbFree(db, p);
+}
+
+/*
+** Add a single new WhereTerm entry to the WhereClause object pWC.
+** The new WhereTerm object is constructed from Expr p and with wtFlags.
+** The index in pWC->a[] of the new WhereTerm is returned on success.
+** 0 is returned if the new WhereTerm could not be added due to a memory
+** allocation error. The memory allocation failure will be recorded in
+** the db->mallocFailed flag so that higher-level functions can detect it.
+**
+** This routine will increase the size of the pWC->a[] array as necessary.
+**
+** If the wtFlags argument includes TERM_DYNAMIC, then responsibility
+** for freeing the expression p is assumed by the WhereClause object pWC.
+** This is true even if this routine fails to allocate a new WhereTerm.
+**
+** WARNING: This routine might reallocate the space used to store
+** WhereTerms. All pointers to WhereTerms should be invalidated after
+** calling this routine. Such pointers may be reinitialized by referencing
+** the pWC->a[] array.
+*/
+static int whereClauseInsert(WhereClause *pWC, Expr *p, u16 wtFlags){
+ WhereTerm *pTerm;
+ int idx;
+ testcase( wtFlags & TERM_VIRTUAL );
+ if( pWC->nTerm>=pWC->nSlot ){
+ WhereTerm *pOld = pWC->a;
+ sqlite3 *db = pWC->pWInfo->pParse->db;
+ pWC->a = sqlite3DbMallocRawNN(db, sizeof(pWC->a[0])*pWC->nSlot*2 );
+ if( pWC->a==0 ){
+ if( wtFlags & TERM_DYNAMIC ){
+ sqlite3ExprDelete(db, p);
}
+ pWC->a = pOld;
+ return 0;
}
- pS = pS->pPrior;
+ memcpy(pWC->a, pOld, sizeof(pWC->a[0])*pWC->nTerm);
+ if( pOld!=pWC->aStatic ){
+ sqlite3DbFree(db, pOld);
+ }
+ pWC->nSlot = sqlite3DbMallocSize(db, pWC->a)/sizeof(pWC->a[0]);
}
- return mask;
+ pTerm = &pWC->a[idx = pWC->nTerm++];
+ if( p && ExprHasProperty(p, EP_Unlikely) ){
+ pTerm->truthProb = sqlite3LogEst(p->iTable) - 270;
+ }else{
+ pTerm->truthProb = 1;
+ }
+ pTerm->pExpr = sqlite3ExprSkipCollate(p);
+ pTerm->wtFlags = wtFlags;
+ pTerm->pWC = pWC;
+ pTerm->iParent = -1;
+ memset(&pTerm->eOperator, 0,
+ sizeof(WhereTerm) - offsetof(WhereTerm,eOperator));
+ return idx;
}
/*
** Return TRUE if the given operator is one of the operators that is
** allowed for an indexable WHERE clause term. The allowed operators are
-** "=", "<", ">", "<=", ">=", and "IN".
-**
-** IMPLEMENTATION-OF: R-59926-26393 To be usable by an index a term must be
-** of one of the following forms: column = expression column > expression
-** column >= expression column < expression column <= expression
-** expression = column expression > column expression >= column
-** expression < column expression <= column column IN
-** (expression-list) column IN (subquery) column IS NULL
+** "=", "<", ">", "<=", ">=", "IN", "IS", and "IS NULL"
*/
static int allowedOp(int op){
assert( TK_GT>TK_EQ && TK_GT<TK_GE );
assert( TK_LT>TK_EQ && TK_LT<TK_GE );
assert( TK_LE>TK_EQ && TK_LE<TK_GE );
assert( TK_GE==TK_EQ+4 );
- return op==TK_IN || (op>=TK_EQ && op<=TK_GE) || op==TK_ISNULL;
+ return op==TK_IN || (op>=TK_EQ && op<=TK_GE) || op==TK_ISNULL || op==TK_IS;
}
/*
-** Swap two objects of type TYPE.
-*/
-#define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
-
-/*
** Commute a comparison operator. Expressions of the form "X op Y"
** are converted into "Y op X".
**
-** If a collation sequence is associated with either the left or right
-** side of the comparison, it remains associated with the same side after
-** the commutation. So "Y collate NOCASE op X" becomes
-** "X collate NOCASE op Y". This is because any collation sequence on
+** If left/right precedence rules come into play when determining the
+** collating sequence, then COLLATE operators are adjusted to ensure
+** that the collating sequence does not change. For example:
+** "Y collate NOCASE op X" becomes "X op Y" because any collation sequence on
** the left hand side of a comparison overrides any collation sequence
-** attached to the right. For the same reason the EP_ExpCollate flag
+** attached to the right. For the same reason the EP_Collate flag
** is not commuted.
*/
static void exprCommute(Parse *pParse, Expr *pExpr){
- u16 expRight = (pExpr->pRight->flags & EP_ExpCollate);
- u16 expLeft = (pExpr->pLeft->flags & EP_ExpCollate);
+ u16 expRight = (pExpr->pRight->flags & EP_Collate);
+ u16 expLeft = (pExpr->pLeft->flags & EP_Collate);
assert( allowedOp(pExpr->op) && pExpr->op!=TK_IN );
- pExpr->pRight->pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
- pExpr->pLeft->pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
- SWAP(CollSeq*,pExpr->pRight->pColl,pExpr->pLeft->pColl);
- pExpr->pRight->flags = (pExpr->pRight->flags & ~EP_ExpCollate) | expLeft;
- pExpr->pLeft->flags = (pExpr->pLeft->flags & ~EP_ExpCollate) | expRight;
+ if( expRight==expLeft ){
+ /* Either X and Y both have COLLATE operator or neither do */
+ if( expRight ){
+ /* Both X and Y have COLLATE operators. Make sure X is always
+ ** used by clearing the EP_Collate flag from Y. */
+ pExpr->pRight->flags &= ~EP_Collate;
+ }else if( sqlite3ExprCollSeq(pParse, pExpr->pLeft)!=0 ){
+ /* Neither X nor Y have COLLATE operators, but X has a non-default
+ ** collating sequence. So add the EP_Collate marker on X to cause
+ ** it to be searched first. */
+ pExpr->pLeft->flags |= EP_Collate;
+ }
+ }
SWAP(Expr*,pExpr->pRight,pExpr->pLeft);
if( pExpr->op>=TK_GT ){
assert( TK_LT==TK_GT+2 );
@@ -100897,6 +126519,8 @@ static u16 operatorMask(int op){
c = WO_IN;
}else if( op==TK_ISNULL ){
c = WO_ISNULL;
+ }else if( op==TK_IS ){
+ c = WO_IS;
}else{
assert( (WO_EQ<<(op-TK_EQ)) < 0x7fff );
c = (u16)(WO_EQ<<(op-TK_EQ));
@@ -100908,79 +126532,10 @@ static u16 operatorMask(int op){
assert( op!=TK_LE || c==WO_LE );
assert( op!=TK_GT || c==WO_GT );
assert( op!=TK_GE || c==WO_GE );
+ assert( op!=TK_IS || c==WO_IS );
return c;
}
-/*
-** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
-** where X is a reference to the iColumn of table iCur and <op> is one of
-** the WO_xx operator codes specified by the op parameter.
-** Return a pointer to the term. Return 0 if not found.
-*/
-static WhereTerm *findTerm(
- WhereClause *pWC, /* The WHERE clause to be searched */
- int iCur, /* Cursor number of LHS */
- int iColumn, /* Column number of LHS */
- Bitmask notReady, /* RHS must not overlap with this mask */
- u32 op, /* Mask of WO_xx values describing operator */
- Index *pIdx /* Must be compatible with this index, if not NULL */
-){
- WhereTerm *pTerm;
- int k;
- assert( iCur>=0 );
- op &= WO_ALL;
- for(pTerm=pWC->a, k=pWC->nTerm; k; k--, pTerm++){
- if( pTerm->leftCursor==iCur
- && (pTerm->prereqRight & notReady)==0
- && pTerm->u.leftColumn==iColumn
- && (pTerm->eOperator & op)!=0
- ){
- if( pIdx && pTerm->eOperator!=WO_ISNULL ){
- Expr *pX = pTerm->pExpr;
- CollSeq *pColl;
- char idxaff;
- int j;
- Parse *pParse = pWC->pParse;
-
- idxaff = pIdx->pTable->aCol[iColumn].affinity;
- if( !sqlite3IndexAffinityOk(pX, idxaff) ) continue;
-
- /* Figure out the collation sequence required from an index for
- ** it to be useful for optimising expression pX. Store this
- ** value in variable pColl.
- */
- assert(pX->pLeft);
- pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
- assert(pColl || pParse->nErr);
-
- for(j=0; pIdx->aiColumn[j]!=iColumn; j++){
- if( NEVER(j>=pIdx->nColumn) ) return 0;
- }
- if( pColl && sqlite3StrICmp(pColl->zName, pIdx->azColl[j]) ) continue;
- }
- return pTerm;
- }
- }
- return 0;
-}
-
-/* Forward reference */
-static void exprAnalyze(SrcList*, WhereClause*, int);
-
-/*
-** Call exprAnalyze on all terms in a WHERE clause.
-**
-**
-*/
-static void exprAnalyzeAll(
- SrcList *pTabList, /* the FROM clause */
- WhereClause *pWC /* the WHERE clause to be analyzed */
-){
- int i;
- for(i=pWC->nTerm-1; i>=0; i--){
- exprAnalyze(pTabList, pWC, i);
- }
-}
#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
/*
@@ -100989,7 +126544,11 @@ static void exprAnalyzeAll(
** so and false if not.
**
** In order for the operator to be optimizible, the RHS must be a string
-** literal that does not begin with a wildcard.
+** literal that does not begin with a wildcard. The LHS must be a column
+** that may only be NULL, a string, or a BLOB, never a number. (This means
+** that virtual tables cannot participate in the LIKE optimization.) The
+** collating sequence for the column on the LHS must be appropriate for
+** the operator.
*/
static int isLikeOrGlob(
Parse *pParse, /* Parsing and code generating context */
@@ -101007,6 +126566,7 @@ static int isLikeOrGlob(
sqlite3 *db = pParse->db; /* Database connection */
sqlite3_value *pVal = 0;
int op; /* Opcode of pRight */
+ int rc; /* Result code to return */
if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){
return 0;
@@ -101016,26 +126576,26 @@ static int isLikeOrGlob(
#endif
pList = pExpr->x.pList;
pLeft = pList->a[1].pExpr;
- if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT ){
+ if( pLeft->op!=TK_COLUMN
+ || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT
+ || IsVirtual(pLeft->pTab) /* Value might be numeric */
+ ){
/* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must
** be the name of an indexed column with TEXT affinity. */
return 0;
}
assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */
- pRight = pList->a[0].pExpr;
+ pRight = sqlite3ExprSkipCollate(pList->a[0].pExpr);
op = pRight->op;
- if( op==TK_REGISTER ){
- op = pRight->op2;
- }
if( op==TK_VARIABLE ){
Vdbe *pReprepare = pParse->pReprepare;
int iCol = pRight->iColumn;
- pVal = sqlite3VdbeGetValue(pReprepare, iCol, SQLITE_AFF_NONE);
+ pVal = sqlite3VdbeGetBoundValue(pReprepare, iCol, SQLITE_AFF_BLOB);
if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){
z = (char *)sqlite3_value_text(pVal);
}
- sqlite3VdbeSetVarmask(pParse->pVdbe, iCol); /* IMP: R-23257-02778 */
+ sqlite3VdbeSetVarmask(pParse->pVdbe, iCol);
assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER );
}else if( op==TK_STRING ){
z = pRight->u.zToken;
@@ -101053,13 +126613,13 @@ static int isLikeOrGlob(
*ppPrefix = pPrefix;
if( op==TK_VARIABLE ){
Vdbe *v = pParse->pVdbe;
- sqlite3VdbeSetVarmask(v, pRight->iColumn); /* IMP: R-23257-02778 */
+ sqlite3VdbeSetVarmask(v, pRight->iColumn);
if( *pisComplete && pRight->u.zToken[1] ){
/* If the rhs of the LIKE expression is a variable, and the current
** value of the variable means there is no need to invoke the LIKE
** function, then no OP_Variable will be added to the program.
** This causes problems for the sqlite3_bind_parameter_name()
- ** API. To workaround them, add a dummy OP_Variable here.
+ ** API. To work around them, add a dummy OP_Variable here.
*/
int r1 = sqlite3GetTempReg(pParse);
sqlite3ExprCodeTarget(pParse, pRight, r1);
@@ -101072,8 +126632,9 @@ static int isLikeOrGlob(
}
}
+ rc = (z!=0);
sqlite3ValueFree(pVal);
- return (z!=0);
+ return rc;
}
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
@@ -101082,29 +126643,48 @@ static int isLikeOrGlob(
/*
** Check to see if the given expression is of the form
**
-** column MATCH expr
+** column OP expr
+**
+** where OP is one of MATCH, GLOB, LIKE or REGEXP and "column" is a
+** column of a virtual table.
**
** If it is then return TRUE. If not, return FALSE.
*/
static int isMatchOfColumn(
- Expr *pExpr /* Test this expression */
-){
+ Expr *pExpr, /* Test this expression */
+ unsigned char *peOp2 /* OUT: 0 for MATCH, or else an op2 value */
+){
+ static const struct Op2 {
+ const char *zOp;
+ unsigned char eOp2;
+ } aOp[] = {
+ { "match", SQLITE_INDEX_CONSTRAINT_MATCH },
+ { "glob", SQLITE_INDEX_CONSTRAINT_GLOB },
+ { "like", SQLITE_INDEX_CONSTRAINT_LIKE },
+ { "regexp", SQLITE_INDEX_CONSTRAINT_REGEXP }
+ };
ExprList *pList;
+ Expr *pCol; /* Column reference */
+ int i;
if( pExpr->op!=TK_FUNCTION ){
return 0;
}
- if( sqlite3StrICmp(pExpr->u.zToken,"match")!=0 ){
- return 0;
- }
pList = pExpr->x.pList;
- if( pList->nExpr!=2 ){
+ if( pList==0 || pList->nExpr!=2 ){
return 0;
}
- if( pList->a[1].pExpr->op != TK_COLUMN ){
+ pCol = pList->a[1].pExpr;
+ if( pCol->op!=TK_COLUMN || !IsVirtual(pCol->pTab) ){
return 0;
}
- return 1;
+ for(i=0; i<ArraySize(aOp); i++){
+ if( sqlite3StrICmp(pExpr->u.zToken, aOp[i].zOp)==0 ){
+ *peOp2 = aOp[i].eOp2;
+ return 1;
+ }
+ }
+ return 0;
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
@@ -101113,8 +126693,92 @@ static int isMatchOfColumn(
** a join, then transfer the appropriate markings over to derived.
*/
static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
- pDerived->flags |= pBase->flags & EP_FromJoin;
- pDerived->iRightJoinTable = pBase->iRightJoinTable;
+ if( pDerived ){
+ pDerived->flags |= pBase->flags & EP_FromJoin;
+ pDerived->iRightJoinTable = pBase->iRightJoinTable;
+ }
+}
+
+/*
+** Mark term iChild as being a child of term iParent
+*/
+static void markTermAsChild(WhereClause *pWC, int iChild, int iParent){
+ pWC->a[iChild].iParent = iParent;
+ pWC->a[iChild].truthProb = pWC->a[iParent].truthProb;
+ pWC->a[iParent].nChild++;
+}
+
+/*
+** Return the N-th AND-connected subterm of pTerm. Or if pTerm is not
+** a conjunction, then return just pTerm when N==0. If N is exceeds
+** the number of available subterms, return NULL.
+*/
+static WhereTerm *whereNthSubterm(WhereTerm *pTerm, int N){
+ if( pTerm->eOperator!=WO_AND ){
+ return N==0 ? pTerm : 0;
+ }
+ if( N<pTerm->u.pAndInfo->wc.nTerm ){
+ return &pTerm->u.pAndInfo->wc.a[N];
+ }
+ return 0;
+}
+
+/*
+** Subterms pOne and pTwo are contained within WHERE clause pWC. The
+** two subterms are in disjunction - they are OR-ed together.
+**
+** If these two terms are both of the form: "A op B" with the same
+** A and B values but different operators and if the operators are
+** compatible (if one is = and the other is <, for example) then
+** add a new virtual AND term to pWC that is the combination of the
+** two.
+**
+** Some examples:
+**
+** x<y OR x=y --> x<=y
+** x=y OR x=y --> x=y
+** x<=y OR x<y --> x<=y
+**
+** The following is NOT generated:
+**
+** x<y OR x>y --> x!=y
+*/
+static void whereCombineDisjuncts(
+ SrcList *pSrc, /* the FROM clause */
+ WhereClause *pWC, /* The complete WHERE clause */
+ WhereTerm *pOne, /* First disjunct */
+ WhereTerm *pTwo /* Second disjunct */
+){
+ u16 eOp = pOne->eOperator | pTwo->eOperator;
+ sqlite3 *db; /* Database connection (for malloc) */
+ Expr *pNew; /* New virtual expression */
+ int op; /* Operator for the combined expression */
+ int idxNew; /* Index in pWC of the next virtual term */
+
+ if( (pOne->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
+ if( (pTwo->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE))==0 ) return;
+ if( (eOp & (WO_EQ|WO_LT|WO_LE))!=eOp
+ && (eOp & (WO_EQ|WO_GT|WO_GE))!=eOp ) return;
+ assert( pOne->pExpr->pLeft!=0 && pOne->pExpr->pRight!=0 );
+ assert( pTwo->pExpr->pLeft!=0 && pTwo->pExpr->pRight!=0 );
+ if( sqlite3ExprCompare(pOne->pExpr->pLeft, pTwo->pExpr->pLeft, -1) ) return;
+ if( sqlite3ExprCompare(pOne->pExpr->pRight, pTwo->pExpr->pRight, -1) )return;
+ /* If we reach this point, it means the two subterms can be combined */
+ if( (eOp & (eOp-1))!=0 ){
+ if( eOp & (WO_LT|WO_LE) ){
+ eOp = WO_LE;
+ }else{
+ assert( eOp & (WO_GT|WO_GE) );
+ eOp = WO_GE;
+ }
+ }
+ db = pWC->pWInfo->pParse->db;
+ pNew = sqlite3ExprDup(db, pOne->pExpr, 0);
+ if( pNew==0 ) return;
+ for(op=TK_EQ; eOp!=(WO_EQ<<(op-TK_EQ)); op++){ assert( op<TK_GE ); }
+ pNew->op = op;
+ idxNew = whereClauseInsert(pWC, pNew, TERM_VIRTUAL|TERM_DYNAMIC);
+ exprAnalyze(pSrc, pWC, idxNew);
}
#if !defined(SQLITE_OMIT_OR_OPTIMIZATION) && !defined(SQLITE_OMIT_SUBQUERY)
@@ -101141,10 +126805,11 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
** (C) t1.x=t2.y OR (t1.x=t2.z AND t1.y=15)
** (D) x=expr1 OR (y>11 AND y<22 AND z LIKE '*hello*')
** (E) (p.a=1 AND q.b=2 AND r.c=3) OR (p.x=4 AND q.y=5 AND r.z=6)
+** (F) x>A OR (x=A AND y>=B)
**
** CASE 1:
**
-** If all subterms are of the form T.C=expr for some single column of C
+** If all subterms are of the form T.C=expr for some single column of C and
** a single table T (as shown in example B above) then create a new virtual
** term that is an equivalent IN expression. In other words, if the term
** being analyzed is:
@@ -101157,6 +126822,16 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
**
** CASE 2:
**
+** If there are exactly two disjuncts and one side has x>A and the other side
+** has x=A (for the same x and A) then add a new virtual conjunct term to the
+** WHERE clause of the form "x>=A". Example:
+**
+** x>A OR (x=A AND y>B) adds: x>=A
+**
+** The added conjunct can sometimes be helpful in query planning.
+**
+** CASE 3:
+**
** If all subterms are indexable by a single table T, then set
**
** WhereTerm.eOperator = WO_OR
@@ -101173,25 +126848,25 @@ static void transferJoinMarkings(Expr *pDerived, Expr *pBase){
** From another point of view, "indexable" means that the subterm could
** potentially be used with an index if an appropriate index exists.
** This analysis does not consider whether or not the index exists; that
-** is something the bestIndex() routine will determine. This analysis
-** only looks at whether subterms appropriate for indexing exist.
+** is decided elsewhere. This analysis only looks at whether subterms
+** appropriate for indexing exist.
**
-** All examples A through E above all satisfy case 2. But if a term
-** also statisfies case 1 (such as B) we know that the optimizer will
-** always prefer case 1, so in that case we pretend that case 2 is not
+** All examples A through E above satisfy case 3. But if a term
+** also satisfies case 1 (such as B) we know that the optimizer will
+** always prefer case 1, so in that case we pretend that case 3 is not
** satisfied.
**
** It might be the case that multiple tables are indexable. For example,
** (E) above is indexable on tables P, Q, and R.
**
-** Terms that satisfy case 2 are candidates for lookup by using
+** Terms that satisfy case 3 are candidates for lookup by using
** separate indices to find rowids for each subterm and composing
** the union of all rowids using a RowSet object. This is similar
** to "bitmap indices" in other database engines.
**
** OTHERWISE:
**
-** If neither case 1 nor case 2 apply, then leave the eOperator set to
+** If none of cases 1, 2, or 3 apply, then leave the eOperator set to
** zero. This term is not useful for search.
*/
static void exprAnalyzeOrTerm(
@@ -101199,11 +126874,11 @@ static void exprAnalyzeOrTerm(
WhereClause *pWC, /* the complete WHERE clause */
int idxTerm /* Index of the OR-term to be analyzed */
){
- Parse *pParse = pWC->pParse; /* Parser context */
+ WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */
+ Parse *pParse = pWInfo->pParse; /* Parser context */
sqlite3 *db = pParse->db; /* Database connection */
WhereTerm *pTerm = &pWC->a[idxTerm]; /* The term to be analyzed */
Expr *pExpr = pTerm->pExpr; /* The expression of the term */
- WhereMaskSet *pMaskSet = pWC->pMaskSet; /* Table use masks */
int i; /* Loop counters */
WhereClause *pOrWc; /* Breakup of pTerm into subterms */
WhereTerm *pOrTerm; /* A Sub-term within the pOrWc */
@@ -101222,24 +126897,24 @@ static void exprAnalyzeOrTerm(
if( pOrInfo==0 ) return;
pTerm->wtFlags |= TERM_ORINFO;
pOrWc = &pOrInfo->wc;
- whereClauseInit(pOrWc, pWC->pParse, pMaskSet);
- whereSplit(pOrWc, pExpr, TK_OR);
- exprAnalyzeAll(pSrc, pOrWc);
+ memset(pOrWc->aStatic, 0, sizeof(pOrWc->aStatic));
+ sqlite3WhereClauseInit(pOrWc, pWInfo);
+ sqlite3WhereSplit(pOrWc, pExpr, TK_OR);
+ sqlite3WhereExprAnalyze(pSrc, pOrWc);
if( db->mallocFailed ) return;
assert( pOrWc->nTerm>=2 );
/*
- ** Compute the set of tables that might satisfy cases 1 or 2.
+ ** Compute the set of tables that might satisfy cases 1 or 3.
*/
indexable = ~(Bitmask)0;
- chngToIN = ~(pWC->vmask);
+ chngToIN = ~(Bitmask)0;
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0 && indexable; i--, pOrTerm++){
if( (pOrTerm->eOperator & WO_SINGLE)==0 ){
WhereAndInfo *pAndInfo;
- assert( pOrTerm->eOperator==0 );
assert( (pOrTerm->wtFlags & (TERM_ANDINFO|TERM_ORINFO))==0 );
chngToIN = 0;
- pAndInfo = sqlite3DbMallocRaw(db, sizeof(*pAndInfo));
+ pAndInfo = sqlite3DbMallocRawNN(db, sizeof(*pAndInfo));
if( pAndInfo ){
WhereClause *pAndWC;
WhereTerm *pAndTerm;
@@ -101249,15 +126924,18 @@ static void exprAnalyzeOrTerm(
pOrTerm->wtFlags |= TERM_ANDINFO;
pOrTerm->eOperator = WO_AND;
pAndWC = &pAndInfo->wc;
- whereClauseInit(pAndWC, pWC->pParse, pMaskSet);
- whereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
- exprAnalyzeAll(pSrc, pAndWC);
- testcase( db->mallocFailed );
+ memset(pAndWC->aStatic, 0, sizeof(pAndWC->aStatic));
+ sqlite3WhereClauseInit(pAndWC, pWC->pWInfo);
+ sqlite3WhereSplit(pAndWC, pOrTerm->pExpr, TK_AND);
+ sqlite3WhereExprAnalyze(pSrc, pAndWC);
+ pAndWC->pOuter = pWC;
if( !db->mallocFailed ){
for(j=0, pAndTerm=pAndWC->a; j<pAndWC->nTerm; j++, pAndTerm++){
assert( pAndTerm->pExpr );
- if( allowedOp(pAndTerm->pExpr->op) ){
- b |= getMask(pMaskSet, pAndTerm->leftCursor);
+ if( allowedOp(pAndTerm->pExpr->op)
+ || pAndTerm->eOperator==WO_MATCH
+ ){
+ b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pAndTerm->leftCursor);
}
}
}
@@ -101268,13 +126946,13 @@ static void exprAnalyzeOrTerm(
** corresponding TERM_VIRTUAL term */
}else{
Bitmask b;
- b = getMask(pMaskSet, pOrTerm->leftCursor);
+ b = sqlite3WhereGetMask(&pWInfo->sMaskSet, pOrTerm->leftCursor);
if( pOrTerm->wtFlags & TERM_VIRTUAL ){
WhereTerm *pOther = &pOrWc->a[pOrTerm->iParent];
- b |= getMask(pMaskSet, pOther->leftCursor);
+ b |= sqlite3WhereGetMask(&pWInfo->sMaskSet, pOther->leftCursor);
}
indexable &= b;
- if( pOrTerm->eOperator!=WO_EQ ){
+ if( (pOrTerm->eOperator & WO_EQ)==0 ){
chngToIN = 0;
}else{
chngToIN &= b;
@@ -101283,12 +126961,26 @@ static void exprAnalyzeOrTerm(
}
/*
- ** Record the set of tables that satisfy case 2. The set might be
+ ** Record the set of tables that satisfy case 3. The set might be
** empty.
*/
pOrInfo->indexable = indexable;
pTerm->eOperator = indexable==0 ? 0 : WO_OR;
+ /* For a two-way OR, attempt to implementation case 2.
+ */
+ if( indexable && pOrWc->nTerm==2 ){
+ int iOne = 0;
+ WhereTerm *pOne;
+ while( (pOne = whereNthSubterm(&pOrWc->a[0],iOne++))!=0 ){
+ int iTwo = 0;
+ WhereTerm *pTwo;
+ while( (pTwo = whereNthSubterm(&pOrWc->a[1],iTwo++))!=0 ){
+ whereCombineDisjuncts(pSrc, pWC, pOne, pTwo);
+ }
+ }
+ }
+
/*
** chngToIN holds a set of tables that *might* satisfy case 1. But
** we have to do some additional checking to see if case 1 really
@@ -101325,7 +127017,7 @@ static void exprAnalyzeOrTerm(
for(j=0; j<2 && !okToChngToIN; j++){
pOrTerm = pOrWc->a;
for(i=pOrWc->nTerm-1; i>=0; i--, pOrTerm++){
- assert( pOrTerm->eOperator==WO_EQ );
+ assert( pOrTerm->eOperator & WO_EQ );
pOrTerm->wtFlags &= ~TERM_OR_OK;
if( pOrTerm->leftCursor==iCursor ){
/* This is the 2-bit case and we are on the second iteration and
@@ -101333,9 +127025,10 @@ static void exprAnalyzeOrTerm(
assert( j==1 );
continue;
}
- if( (chngToIN & getMask(pMaskSet, pOrTerm->leftCursor))==0 ){
+ if( (chngToIN & sqlite3WhereGetMask(&pWInfo->sMaskSet,
+ pOrTerm->leftCursor))==0 ){
/* This term must be of the form t1.a==t2.b where t2 is in the
- ** chngToIN set but t1 is not. This term will be either preceeded
+ ** chngToIN set but t1 is not. This term will be either preceded
** or follwed by an inverted copy (t2.b==t1.a). Skip this term
** and use its inversion. */
testcase( pOrTerm->wtFlags & TERM_COPIED );
@@ -101351,8 +127044,8 @@ static void exprAnalyzeOrTerm(
/* No candidate table+column was found. This can only occur
** on the second iteration */
assert( j==1 );
- assert( (chngToIN&(chngToIN-1))==0 );
- assert( chngToIN==getMask(pMaskSet, iCursor) );
+ assert( IsPowerOfTwo(chngToIN) );
+ assert( chngToIN==sqlite3WhereGetMask(&pWInfo->sMaskSet, iCursor) );
break;
}
testcase( j==1 );
@@ -101361,7 +127054,7 @@ static void exprAnalyzeOrTerm(
** table and column is common to every term in the OR clause */
okToChngToIN = 1;
for(; i>=0 && okToChngToIN; i--, pOrTerm++){
- assert( pOrTerm->eOperator==WO_EQ );
+ assert( pOrTerm->eOperator & WO_EQ );
if( pOrTerm->leftCursor!=iCursor ){
pOrTerm->wtFlags &= ~TERM_OR_OK;
}else if( pOrTerm->u.leftColumn!=iColumn ){
@@ -101386,8 +127079,6 @@ static void exprAnalyzeOrTerm(
/* At this point, okToChngToIN is true if original pTerm satisfies
** case 1. In that case, construct a new virtual term that is
** pTerm converted into an IN operator.
- **
- ** EV: R-00211-15100
*/
if( okToChngToIN ){
Expr *pDup; /* A transient duplicate expression */
@@ -101397,11 +127088,11 @@ static void exprAnalyzeOrTerm(
for(i=pOrWc->nTerm-1, pOrTerm=pOrWc->a; i>=0; i--, pOrTerm++){
if( (pOrTerm->wtFlags & TERM_OR_OK)==0 ) continue;
- assert( pOrTerm->eOperator==WO_EQ );
+ assert( pOrTerm->eOperator & WO_EQ );
assert( pOrTerm->leftCursor==iCursor );
assert( pOrTerm->u.leftColumn==iColumn );
pDup = sqlite3ExprDup(db, pOrTerm->pExpr->pRight, 0);
- pList = sqlite3ExprListAppend(pWC->pParse, pList, pDup);
+ pList = sqlite3ExprListAppend(pWInfo->pParse, pList, pDup);
pLeft = pOrTerm->pExpr->pLeft;
}
assert( pLeft!=0 );
@@ -101416,17 +127107,137 @@ static void exprAnalyzeOrTerm(
testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew);
pTerm = &pWC->a[idxTerm];
- pWC->a[idxNew].iParent = idxTerm;
- pTerm->nChild = 1;
+ markTermAsChild(pWC, idxNew, idxTerm);
}else{
sqlite3ExprListDelete(db, pList);
}
- pTerm->eOperator = WO_NOOP; /* case 1 trumps case 2 */
+ pTerm->eOperator = WO_NOOP; /* case 1 trumps case 3 */
}
}
}
#endif /* !SQLITE_OMIT_OR_OPTIMIZATION && !SQLITE_OMIT_SUBQUERY */
+/*
+** We already know that pExpr is a binary operator where both operands are
+** column references. This routine checks to see if pExpr is an equivalence
+** relation:
+** 1. The SQLITE_Transitive optimization must be enabled
+** 2. Must be either an == or an IS operator
+** 3. Not originating in the ON clause of an OUTER JOIN
+** 4. The affinities of A and B must be compatible
+** 5a. Both operands use the same collating sequence OR
+** 5b. The overall collating sequence is BINARY
+** If this routine returns TRUE, that means that the RHS can be substituted
+** for the LHS anyplace else in the WHERE clause where the LHS column occurs.
+** This is an optimization. No harm comes from returning 0. But if 1 is
+** returned when it should not be, then incorrect answers might result.
+*/
+static int termIsEquivalence(Parse *pParse, Expr *pExpr){
+ char aff1, aff2;
+ CollSeq *pColl;
+ const char *zColl1, *zColl2;
+ if( !OptimizationEnabled(pParse->db, SQLITE_Transitive) ) return 0;
+ if( pExpr->op!=TK_EQ && pExpr->op!=TK_IS ) return 0;
+ if( ExprHasProperty(pExpr, EP_FromJoin) ) return 0;
+ aff1 = sqlite3ExprAffinity(pExpr->pLeft);
+ aff2 = sqlite3ExprAffinity(pExpr->pRight);
+ if( aff1!=aff2
+ && (!sqlite3IsNumericAffinity(aff1) || !sqlite3IsNumericAffinity(aff2))
+ ){
+ return 0;
+ }
+ pColl = sqlite3BinaryCompareCollSeq(pParse, pExpr->pLeft, pExpr->pRight);
+ if( pColl==0 || sqlite3StrICmp(pColl->zName, "BINARY")==0 ) return 1;
+ pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
+ zColl1 = pColl ? pColl->zName : 0;
+ pColl = sqlite3ExprCollSeq(pParse, pExpr->pRight);
+ zColl2 = pColl ? pColl->zName : 0;
+ return sqlite3_stricmp(zColl1, zColl2)==0;
+}
+
+/*
+** Recursively walk the expressions of a SELECT statement and generate
+** a bitmask indicating which tables are used in that expression
+** tree.
+*/
+static Bitmask exprSelectUsage(WhereMaskSet *pMaskSet, Select *pS){
+ Bitmask mask = 0;
+ while( pS ){
+ SrcList *pSrc = pS->pSrc;
+ mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pEList);
+ mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pGroupBy);
+ mask |= sqlite3WhereExprListUsage(pMaskSet, pS->pOrderBy);
+ mask |= sqlite3WhereExprUsage(pMaskSet, pS->pWhere);
+ mask |= sqlite3WhereExprUsage(pMaskSet, pS->pHaving);
+ if( ALWAYS(pSrc!=0) ){
+ int i;
+ for(i=0; i<pSrc->nSrc; i++){
+ mask |= exprSelectUsage(pMaskSet, pSrc->a[i].pSelect);
+ mask |= sqlite3WhereExprUsage(pMaskSet, pSrc->a[i].pOn);
+ }
+ }
+ pS = pS->pPrior;
+ }
+ return mask;
+}
+
+/*
+** Expression pExpr is one operand of a comparison operator that might
+** be useful for indexing. This routine checks to see if pExpr appears
+** in any index. Return TRUE (1) if pExpr is an indexed term and return
+** FALSE (0) if not. If TRUE is returned, also set *piCur to the cursor
+** number of the table that is indexed and *piColumn to the column number
+** of the column that is indexed, or XN_EXPR (-2) if an expression is being
+** indexed.
+**
+** If pExpr is a TK_COLUMN column reference, then this routine always returns
+** true even if that particular column is not indexed, because the column
+** might be added to an automatic index later.
+*/
+static int exprMightBeIndexed(
+ SrcList *pFrom, /* The FROM clause */
+ int op, /* The specific comparison operator */
+ Bitmask mPrereq, /* Bitmask of FROM clause terms referenced by pExpr */
+ Expr *pExpr, /* An operand of a comparison operator */
+ int *piCur, /* Write the referenced table cursor number here */
+ int *piColumn /* Write the referenced table column number here */
+){
+ Index *pIdx;
+ int i;
+ int iCur;
+
+ /* If this expression is a vector to the left or right of a
+ ** inequality constraint (>, <, >= or <=), perform the processing
+ ** on the first element of the vector. */
+ assert( TK_GT+1==TK_LE && TK_GT+2==TK_LT && TK_GT+3==TK_GE );
+ assert( TK_IS<TK_GE && TK_ISNULL<TK_GE && TK_IN<TK_GE );
+ assert( op<=TK_GE );
+ if( pExpr->op==TK_VECTOR && (op>=TK_GT && ALWAYS(op<=TK_GE)) ){
+ pExpr = pExpr->x.pList->a[0].pExpr;
+ }
+
+ if( pExpr->op==TK_COLUMN ){
+ *piCur = pExpr->iTable;
+ *piColumn = pExpr->iColumn;
+ return 1;
+ }
+ if( mPrereq==0 ) return 0; /* No table references */
+ if( (mPrereq&(mPrereq-1))!=0 ) return 0; /* Refs more than one table */
+ for(i=0; mPrereq>1; i++, mPrereq>>=1){}
+ iCur = pFrom->a[i].iCursor;
+ for(pIdx=pFrom->a[i].pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ if( pIdx->aColExpr==0 ) continue;
+ for(i=0; i<pIdx->nKeyCol; i++){
+ if( pIdx->aiColumn[i]!=XN_EXPR ) continue;
+ if( sqlite3ExprCompare(pExpr, pIdx->aColExpr->a[i].pExpr, iCur)==0 ){
+ *piCur = iCur;
+ *piColumn = XN_EXPR;
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
/*
** The input to this routine is an WhereTerm structure with only the
@@ -101451,6 +127262,7 @@ static void exprAnalyze(
WhereClause *pWC, /* the WHERE clause */
int idxTerm /* Index of the term to be analyzed */
){
+ WhereInfo *pWInfo = pWC->pWInfo; /* WHERE clause processing context */
WhereTerm *pTerm; /* The term to be analyzed */
WhereMaskSet *pMaskSet; /* Set of table index masks */
Expr *pExpr; /* The expression to be analyzed */
@@ -101459,34 +127271,37 @@ static void exprAnalyze(
Bitmask extraRight = 0; /* Extra dependencies on LEFT JOIN */
Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */
int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */
- int noCase = 0; /* LIKE/GLOB distinguishes case */
+ int noCase = 0; /* uppercase equivalent to lowercase */
int op; /* Top-level operator. pExpr->op */
- Parse *pParse = pWC->pParse; /* Parsing context */
+ Parse *pParse = pWInfo->pParse; /* Parsing context */
sqlite3 *db = pParse->db; /* Database connection */
+ unsigned char eOp2; /* op2 value for LIKE/REGEXP/GLOB */
if( db->mallocFailed ){
return;
}
pTerm = &pWC->a[idxTerm];
- pMaskSet = pWC->pMaskSet;
+ pMaskSet = &pWInfo->sMaskSet;
pExpr = pTerm->pExpr;
- prereqLeft = exprTableUsage(pMaskSet, pExpr->pLeft);
+ assert( pExpr->op!=TK_AS && pExpr->op!=TK_COLLATE );
+ prereqLeft = sqlite3WhereExprUsage(pMaskSet, pExpr->pLeft);
op = pExpr->op;
if( op==TK_IN ){
assert( pExpr->pRight==0 );
+ if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- pTerm->prereqRight = exprSelectTableUsage(pMaskSet, pExpr->x.pSelect);
+ pTerm->prereqRight = exprSelectUsage(pMaskSet, pExpr->x.pSelect);
}else{
- pTerm->prereqRight = exprListTableUsage(pMaskSet, pExpr->x.pList);
+ pTerm->prereqRight = sqlite3WhereExprListUsage(pMaskSet, pExpr->x.pList);
}
}else if( op==TK_ISNULL ){
pTerm->prereqRight = 0;
}else{
- pTerm->prereqRight = exprTableUsage(pMaskSet, pExpr->pRight);
+ pTerm->prereqRight = sqlite3WhereExprUsage(pMaskSet, pExpr->pRight);
}
- prereqAll = exprTableUsage(pMaskSet, pExpr);
+ prereqAll = sqlite3WhereExprUsage(pMaskSet, pExpr);
if( ExprHasProperty(pExpr, EP_FromJoin) ){
- Bitmask x = getMask(pMaskSet, pExpr->iRightJoinTable);
+ Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->iRightJoinTable);
prereqAll |= x;
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
@@ -101495,17 +127310,31 @@ static void exprAnalyze(
pTerm->leftCursor = -1;
pTerm->iParent = -1;
pTerm->eOperator = 0;
- if( allowedOp(op) && (pTerm->prereqRight & prereqLeft)==0 ){
- Expr *pLeft = pExpr->pLeft;
- Expr *pRight = pExpr->pRight;
- if( pLeft->op==TK_COLUMN ){
- pTerm->leftCursor = pLeft->iTable;
- pTerm->u.leftColumn = pLeft->iColumn;
- pTerm->eOperator = operatorMask(op);
- }
- if( pRight && pRight->op==TK_COLUMN ){
+ if( allowedOp(op) ){
+ int iCur, iColumn;
+ Expr *pLeft = sqlite3ExprSkipCollate(pExpr->pLeft);
+ Expr *pRight = sqlite3ExprSkipCollate(pExpr->pRight);
+ u16 opMask = (pTerm->prereqRight & prereqLeft)==0 ? WO_ALL : WO_EQUIV;
+
+ if( pTerm->iField>0 ){
+ assert( op==TK_IN );
+ assert( pLeft->op==TK_VECTOR );
+ pLeft = pLeft->x.pList->a[pTerm->iField-1].pExpr;
+ }
+
+ if( exprMightBeIndexed(pSrc, op, prereqLeft, pLeft, &iCur, &iColumn) ){
+ pTerm->leftCursor = iCur;
+ pTerm->u.leftColumn = iColumn;
+ pTerm->eOperator = operatorMask(op) & opMask;
+ }
+ if( op==TK_IS ) pTerm->wtFlags |= TERM_IS;
+ if( pRight
+ && exprMightBeIndexed(pSrc, op, pTerm->prereqRight, pRight, &iCur,&iColumn)
+ ){
WhereTerm *pNew;
Expr *pDup;
+ u16 eExtraOp = 0; /* Extra bits for pNew->eOperator */
+ assert( pTerm->iField==0 );
if( pTerm->leftCursor>=0 ){
int idxNew;
pDup = sqlite3ExprDup(db, pExpr, 0);
@@ -101516,22 +127345,26 @@ static void exprAnalyze(
idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
if( idxNew==0 ) return;
pNew = &pWC->a[idxNew];
- pNew->iParent = idxTerm;
+ markTermAsChild(pWC, idxNew, idxTerm);
+ if( op==TK_IS ) pNew->wtFlags |= TERM_IS;
pTerm = &pWC->a[idxTerm];
- pTerm->nChild = 1;
pTerm->wtFlags |= TERM_COPIED;
+
+ if( termIsEquivalence(pParse, pDup) ){
+ pTerm->eOperator |= WO_EQUIV;
+ eExtraOp = WO_EQUIV;
+ }
}else{
pDup = pExpr;
pNew = pTerm;
}
exprCommute(pParse, pDup);
- pLeft = pDup->pLeft;
- pNew->leftCursor = pLeft->iTable;
- pNew->u.leftColumn = pLeft->iColumn;
+ pNew->leftCursor = iCur;
+ pNew->u.leftColumn = iColumn;
testcase( (prereqLeft | extraRight) != prereqLeft );
pNew->prereqRight = prereqLeft | extraRight;
pNew->prereqAll = prereqAll;
- pNew->eOperator = operatorMask(pDup->op);
+ pNew->eOperator = (operatorMask(pDup->op) + eExtraOp) & opMask;
}
}
@@ -101563,13 +127396,13 @@ static void exprAnalyze(
pNewExpr = sqlite3PExpr(pParse, ops[i],
sqlite3ExprDup(db, pExpr->pLeft, 0),
sqlite3ExprDup(db, pList->a[i].pExpr, 0), 0);
+ transferJoinMarkings(pNewExpr, pExpr);
idxNew = whereClauseInsert(pWC, pNewExpr, TERM_VIRTUAL|TERM_DYNAMIC);
testcase( idxNew==0 );
exprAnalyze(pSrc, pWC, idxNew);
pTerm = &pWC->a[idxTerm];
- pWC->a[idxNew].iParent = idxTerm;
+ markTermAsChild(pWC, idxNew, idxTerm);
}
- pTerm->nChild = 2;
}
#endif /* SQLITE_OMIT_BETWEEN_OPTIMIZATION */
@@ -101588,12 +127421,15 @@ static void exprAnalyze(
/* Add constraints to reduce the search space on a LIKE or GLOB
** operator.
**
- ** A like pattern of the form "x LIKE 'abc%'" is changed into constraints
+ ** A like pattern of the form "x LIKE 'aBc%'" is changed into constraints
**
- ** x>='abc' AND x<'abd' AND x LIKE 'abc%'
+ ** x>='ABC' AND x<'abd' AND x LIKE 'aBc%'
**
** The last character of the prefix "abc" is incremented to form the
- ** termination condition "abd".
+ ** termination condition "abd". If case is not significant (the default
+ ** for LIKE) then the lower-bound is made all uppercase and the upper-
+ ** bound is made all lowercase so that the bounds also work when comparing
+ ** BLOBs.
*/
if( pWC->op==TK_AND
&& isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase)
@@ -101604,10 +127440,26 @@ static void exprAnalyze(
Expr *pNewExpr2;
int idxNew1;
int idxNew2;
- CollSeq *pColl; /* Collating sequence to use */
+ const char *zCollSeqName; /* Name of collating sequence */
+ const u16 wtFlags = TERM_LIKEOPT | TERM_VIRTUAL | TERM_DYNAMIC;
pLeft = pExpr->x.pList->a[1].pExpr;
pStr2 = sqlite3ExprDup(db, pStr1, 0);
+
+ /* Convert the lower bound to upper-case and the upper bound to
+ ** lower-case (upper-case is less than lower-case in ASCII) so that
+ ** the range constraints also work for BLOBs
+ */
+ if( noCase && !pParse->db->mallocFailed ){
+ int i;
+ char c;
+ pTerm->wtFlags |= TERM_LIKE;
+ for(i=0; (c = pStr1->u.zToken[i])!=0; i++){
+ pStr1->u.zToken[i] = sqlite3Toupper(c);
+ pStr2->u.zToken[i] = sqlite3Tolower(c);
+ }
+ }
+
if( !db->mallocFailed ){
u8 c, *pC; /* Last character before the first wildcard */
pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1];
@@ -101619,31 +127471,32 @@ static void exprAnalyze(
** inequality. To avoid this, make sure to also run the full
** LIKE on all candidate expressions by clearing the isComplete flag
*/
- if( c=='A'-1 ) isComplete = 0; /* EV: R-64339-08207 */
-
-
+ if( c=='A'-1 ) isComplete = 0;
c = sqlite3UpperToLower[c];
}
*pC = c + 1;
}
- pColl = sqlite3FindCollSeq(db, SQLITE_UTF8, noCase ? "NOCASE" : "BINARY",0);
- pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
- sqlite3ExprSetColl(sqlite3ExprDup(db,pLeft,0), pColl),
- pStr1, 0);
- idxNew1 = whereClauseInsert(pWC, pNewExpr1, TERM_VIRTUAL|TERM_DYNAMIC);
+ zCollSeqName = noCase ? "NOCASE" : "BINARY";
+ pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
+ pNewExpr1 = sqlite3PExpr(pParse, TK_GE,
+ sqlite3ExprAddCollateString(pParse,pNewExpr1,zCollSeqName),
+ pStr1, 0);
+ transferJoinMarkings(pNewExpr1, pExpr);
+ idxNew1 = whereClauseInsert(pWC, pNewExpr1, wtFlags);
testcase( idxNew1==0 );
exprAnalyze(pSrc, pWC, idxNew1);
+ pNewExpr2 = sqlite3ExprDup(db, pLeft, 0);
pNewExpr2 = sqlite3PExpr(pParse, TK_LT,
- sqlite3ExprSetColl(sqlite3ExprDup(db,pLeft,0), pColl),
- pStr2, 0);
- idxNew2 = whereClauseInsert(pWC, pNewExpr2, TERM_VIRTUAL|TERM_DYNAMIC);
+ sqlite3ExprAddCollateString(pParse,pNewExpr2,zCollSeqName),
+ pStr2, 0);
+ transferJoinMarkings(pNewExpr2, pExpr);
+ idxNew2 = whereClauseInsert(pWC, pNewExpr2, wtFlags);
testcase( idxNew2==0 );
exprAnalyze(pSrc, pWC, idxNew2);
pTerm = &pWC->a[idxTerm];
if( isComplete ){
- pWC->a[idxNew1].iParent = idxTerm;
- pWC->a[idxNew2].iParent = idxTerm;
- pTerm->nChild = 2;
+ markTermAsChild(pWC, idxNew1, idxTerm);
+ markTermAsChild(pWC, idxNew2, idxTerm);
}
}
#endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */
@@ -101655,7 +127508,7 @@ static void exprAnalyze(
** virtual tables. The native query optimizer does not attempt
** to do anything with MATCH functions.
*/
- if( isMatchOfColumn(pExpr) ){
+ if( pWC->op==TK_AND && isMatchOfColumn(pExpr, &eOp2) ){
int idxNew;
Expr *pRight, *pLeft;
WhereTerm *pNewTerm;
@@ -101663,8 +127516,8 @@ static void exprAnalyze(
pRight = pExpr->x.pList->a[0].pExpr;
pLeft = pExpr->x.pList->a[1].pExpr;
- prereqExpr = exprTableUsage(pMaskSet, pRight);
- prereqColumn = exprTableUsage(pMaskSet, pLeft);
+ prereqExpr = sqlite3WhereExprUsage(pMaskSet, pRight);
+ prereqColumn = sqlite3WhereExprUsage(pMaskSet, pLeft);
if( (prereqExpr & prereqColumn)==0 ){
Expr *pNewExpr;
pNewExpr = sqlite3PExpr(pParse, TK_MATCH,
@@ -101676,29 +127529,81 @@ static void exprAnalyze(
pNewTerm->leftCursor = pLeft->iTable;
pNewTerm->u.leftColumn = pLeft->iColumn;
pNewTerm->eOperator = WO_MATCH;
- pNewTerm->iParent = idxTerm;
+ pNewTerm->eMatchOp = eOp2;
+ markTermAsChild(pWC, idxNew, idxTerm);
pTerm = &pWC->a[idxTerm];
- pTerm->nChild = 1;
pTerm->wtFlags |= TERM_COPIED;
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
#endif /* SQLITE_OMIT_VIRTUALTABLE */
-#ifdef SQLITE_ENABLE_STAT2
- /* When sqlite_stat2 histogram data is available an operator of the
+ /* If there is a vector == or IS term - e.g. "(a, b) == (?, ?)" - create
+ ** new terms for each component comparison - "a = ?" and "b = ?". The
+ ** new terms completely replace the original vector comparison, which is
+ ** no longer used.
+ **
+ ** This is only required if at least one side of the comparison operation
+ ** is not a sub-select. */
+ if( pWC->op==TK_AND
+ && (pExpr->op==TK_EQ || pExpr->op==TK_IS)
+ && sqlite3ExprIsVector(pExpr->pLeft)
+ && ( (pExpr->pLeft->flags & EP_xIsSelect)==0
+ || (pExpr->pRight->flags & EP_xIsSelect)==0
+ )){
+ int nLeft = sqlite3ExprVectorSize(pExpr->pLeft);
+ int i;
+ assert( nLeft==sqlite3ExprVectorSize(pExpr->pRight) );
+ for(i=0; i<nLeft; i++){
+ int idxNew;
+ Expr *pNew;
+ Expr *pLeft = sqlite3ExprForVectorField(pParse, pExpr->pLeft, i);
+ Expr *pRight = sqlite3ExprForVectorField(pParse, pExpr->pRight, i);
+
+ pNew = sqlite3PExpr(pParse, pExpr->op, pLeft, pRight, 0);
+ transferJoinMarkings(pNew, pExpr);
+ idxNew = whereClauseInsert(pWC, pNew, TERM_DYNAMIC);
+ exprAnalyze(pSrc, pWC, idxNew);
+ }
+ pTerm = &pWC->a[idxTerm];
+ pTerm->wtFlags = TERM_CODED|TERM_VIRTUAL; /* Disable the original */
+ pTerm->eOperator = 0;
+ }
+
+ /* If there is a vector IN term - e.g. "(a, b) IN (SELECT ...)" - create
+ ** a virtual term for each vector component. The expression object
+ ** used by each such virtual term is pExpr (the full vector IN(...)
+ ** expression). The WhereTerm.iField variable identifies the index within
+ ** the vector on the LHS that the virtual term represents.
+ **
+ ** This only works if the RHS is a simple SELECT, not a compound
+ */
+ if( pWC->op==TK_AND && pExpr->op==TK_IN && pTerm->iField==0
+ && pExpr->pLeft->op==TK_VECTOR
+ && pExpr->x.pSelect->pPrior==0
+ ){
+ int i;
+ for(i=0; i<sqlite3ExprVectorSize(pExpr->pLeft); i++){
+ int idxNew;
+ idxNew = whereClauseInsert(pWC, pExpr, TERM_VIRTUAL);
+ pWC->a[idxNew].iField = i+1;
+ exprAnalyze(pSrc, pWC, idxNew);
+ markTermAsChild(pWC, idxNew, idxTerm);
+ }
+ }
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ /* When sqlite_stat3 histogram data is available an operator of the
** form "x IS NOT NULL" can sometimes be evaluated more efficiently
** as "x>NULL" if x is not an INTEGER PRIMARY KEY. So construct a
** virtual term of that form.
**
- ** Note that the virtual term must be tagged with TERM_VNULL. This
- ** TERM_VNULL tag will suppress the not-null check at the beginning
- ** of the loop. Without the TERM_VNULL flag, the not-null check at
- ** the start of the loop will prevent any results from being returned.
+ ** Note that the virtual term must be tagged with TERM_VNULL.
*/
if( pExpr->op==TK_NOTNULL
&& pExpr->pLeft->op==TK_COLUMN
&& pExpr->pLeft->iColumn>=0
+ && OptimizationEnabled(db, SQLITE_Stat34)
){
Expr *pNewExpr;
Expr *pLeft = pExpr->pLeft;
@@ -101707,7 +127612,7 @@ static void exprAnalyze(
pNewExpr = sqlite3PExpr(pParse, TK_GT,
sqlite3ExprDup(db, pLeft, 0),
- sqlite3PExpr(pParse, TK_NULL, 0, 0, 0), 0);
+ sqlite3ExprAlloc(db, TK_NULL, 0, 0), 0);
idxNew = whereClauseInsert(pWC, pNewExpr,
TERM_VIRTUAL|TERM_DYNAMIC|TERM_VNULL);
@@ -101717,14 +127622,13 @@ static void exprAnalyze(
pNewTerm->leftCursor = pLeft->iTable;
pNewTerm->u.leftColumn = pLeft->iColumn;
pNewTerm->eOperator = WO_GT;
- pNewTerm->iParent = idxTerm;
+ markTermAsChild(pWC, idxNew, idxTerm);
pTerm = &pWC->a[idxTerm];
- pTerm->nChild = 1;
pTerm->wtFlags |= TERM_COPIED;
pNewTerm->prereqAll = pTerm->prereqAll;
}
}
-#endif /* SQLITE_ENABLE_STAT2 */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/* Prevent ON clause terms of a LEFT JOIN from being used to drive
** an index for tables to the left of the join.
@@ -101732,31 +127636,552 @@ static void exprAnalyze(
pTerm->prereqRight |= extraRight;
}
+/***************************************************************************
+** Routines with file scope above. Interface to the rest of the where.c
+** subsystem follows.
+***************************************************************************/
+
+/*
+** This routine identifies subexpressions in the WHERE clause where
+** each subexpression is separated by the AND operator or some other
+** operator specified in the op parameter. The WhereClause structure
+** is filled with pointers to subexpressions. For example:
+**
+** WHERE a=='hello' AND coalesce(b,11)<10 AND (c+12!=d OR c==22)
+** \________/ \_______________/ \________________/
+** slot[0] slot[1] slot[2]
+**
+** The original WHERE clause in pExpr is unaltered. All this routine
+** does is make slot[] entries point to substructure within pExpr.
+**
+** In the previous sentence and in the diagram, "slot[]" refers to
+** the WhereClause.a[] array. The slot[] array grows as needed to contain
+** all terms of the WHERE clause.
+*/
+SQLITE_PRIVATE void sqlite3WhereSplit(WhereClause *pWC, Expr *pExpr, u8 op){
+ Expr *pE2 = sqlite3ExprSkipCollate(pExpr);
+ pWC->op = op;
+ if( pE2==0 ) return;
+ if( pE2->op!=op ){
+ whereClauseInsert(pWC, pExpr, 0);
+ }else{
+ sqlite3WhereSplit(pWC, pE2->pLeft, op);
+ sqlite3WhereSplit(pWC, pE2->pRight, op);
+ }
+}
+
/*
-** Return TRUE if any of the expressions in pList->a[iFirst...] contain
-** a reference to any table other than the iBase table.
+** Initialize a preallocated WhereClause structure.
*/
-static int referencesOtherTables(
- ExprList *pList, /* Search expressions in ths list */
- WhereMaskSet *pMaskSet, /* Mapping from tables to bitmaps */
- int iFirst, /* Be searching with the iFirst-th expression */
- int iBase /* Ignore references to this table */
+SQLITE_PRIVATE void sqlite3WhereClauseInit(
+ WhereClause *pWC, /* The WhereClause to be initialized */
+ WhereInfo *pWInfo /* The WHERE processing context */
){
- Bitmask allowed = ~getMask(pMaskSet, iBase);
- while( iFirst<pList->nExpr ){
- if( (exprTableUsage(pMaskSet, pList->a[iFirst++].pExpr)&allowed)!=0 ){
- return 1;
+ pWC->pWInfo = pWInfo;
+ pWC->pOuter = 0;
+ pWC->nTerm = 0;
+ pWC->nSlot = ArraySize(pWC->aStatic);
+ pWC->a = pWC->aStatic;
+}
+
+/*
+** Deallocate a WhereClause structure. The WhereClause structure
+** itself is not freed. This routine is the inverse of
+** sqlite3WhereClauseInit().
+*/
+SQLITE_PRIVATE void sqlite3WhereClauseClear(WhereClause *pWC){
+ int i;
+ WhereTerm *a;
+ sqlite3 *db = pWC->pWInfo->pParse->db;
+ for(i=pWC->nTerm-1, a=pWC->a; i>=0; i--, a++){
+ if( a->wtFlags & TERM_DYNAMIC ){
+ sqlite3ExprDelete(db, a->pExpr);
+ }
+ if( a->wtFlags & TERM_ORINFO ){
+ whereOrInfoDelete(db, a->u.pOrInfo);
+ }else if( a->wtFlags & TERM_ANDINFO ){
+ whereAndInfoDelete(db, a->u.pAndInfo);
+ }
+ }
+ if( pWC->a!=pWC->aStatic ){
+ sqlite3DbFree(db, pWC->a);
+ }
+}
+
+
+/*
+** These routines walk (recursively) an expression tree and generate
+** a bitmask indicating which tables are used in that expression
+** tree.
+*/
+SQLITE_PRIVATE Bitmask sqlite3WhereExprUsage(WhereMaskSet *pMaskSet, Expr *p){
+ Bitmask mask;
+ if( p==0 ) return 0;
+ if( p->op==TK_COLUMN ){
+ mask = sqlite3WhereGetMask(pMaskSet, p->iTable);
+ return mask;
+ }
+ assert( !ExprHasProperty(p, EP_TokenOnly) );
+ mask = p->pRight ? sqlite3WhereExprUsage(pMaskSet, p->pRight) : 0;
+ if( p->pLeft ) mask |= sqlite3WhereExprUsage(pMaskSet, p->pLeft);
+ if( ExprHasProperty(p, EP_xIsSelect) ){
+ mask |= exprSelectUsage(pMaskSet, p->x.pSelect);
+ }else if( p->x.pList ){
+ mask |= sqlite3WhereExprListUsage(pMaskSet, p->x.pList);
+ }
+ return mask;
+}
+SQLITE_PRIVATE Bitmask sqlite3WhereExprListUsage(WhereMaskSet *pMaskSet, ExprList *pList){
+ int i;
+ Bitmask mask = 0;
+ if( pList ){
+ for(i=0; i<pList->nExpr; i++){
+ mask |= sqlite3WhereExprUsage(pMaskSet, pList->a[i].pExpr);
+ }
+ }
+ return mask;
+}
+
+
+/*
+** Call exprAnalyze on all terms in a WHERE clause.
+**
+** Note that exprAnalyze() might add new virtual terms onto the
+** end of the WHERE clause. We do not want to analyze these new
+** virtual terms, so start analyzing at the end and work forward
+** so that the added virtual terms are never processed.
+*/
+SQLITE_PRIVATE void sqlite3WhereExprAnalyze(
+ SrcList *pTabList, /* the FROM clause */
+ WhereClause *pWC /* the WHERE clause to be analyzed */
+){
+ int i;
+ for(i=pWC->nTerm-1; i>=0; i--){
+ exprAnalyze(pTabList, pWC, i);
+ }
+}
+
+/*
+** For table-valued-functions, transform the function arguments into
+** new WHERE clause terms.
+**
+** Each function argument translates into an equality constraint against
+** a HIDDEN column in the table.
+*/
+SQLITE_PRIVATE void sqlite3WhereTabFuncArgs(
+ Parse *pParse, /* Parsing context */
+ struct SrcList_item *pItem, /* The FROM clause term to process */
+ WhereClause *pWC /* Xfer function arguments to here */
+){
+ Table *pTab;
+ int j, k;
+ ExprList *pArgs;
+ Expr *pColRef;
+ Expr *pTerm;
+ if( pItem->fg.isTabFunc==0 ) return;
+ pTab = pItem->pTab;
+ assert( pTab!=0 );
+ pArgs = pItem->u1.pFuncArg;
+ if( pArgs==0 ) return;
+ for(j=k=0; j<pArgs->nExpr; j++){
+ while( k<pTab->nCol && (pTab->aCol[k].colFlags & COLFLAG_HIDDEN)==0 ){k++;}
+ if( k>=pTab->nCol ){
+ sqlite3ErrorMsg(pParse, "too many arguments on %s() - max %d",
+ pTab->zName, j);
+ return;
+ }
+ pColRef = sqlite3ExprAlloc(pParse->db, TK_COLUMN, 0, 0);
+ if( pColRef==0 ) return;
+ pColRef->iTable = pItem->iCursor;
+ pColRef->iColumn = k++;
+ pColRef->pTab = pTab;
+ pTerm = sqlite3PExpr(pParse, TK_EQ, pColRef,
+ sqlite3ExprDup(pParse->db, pArgs->a[j].pExpr, 0), 0);
+ whereClauseInsert(pWC, pTerm, TERM_DYNAMIC);
+ }
+}
+
+/************** End of whereexpr.c *******************************************/
+/************** Begin file where.c *******************************************/
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** This module contains C code that generates VDBE code used to process
+** the WHERE clause of SQL statements. This module is responsible for
+** generating the code that loops through a table looking for applicable
+** rows. Indices are selected and used to speed the search when doing
+** so is applicable. Because this module is responsible for selecting
+** indices, you might also think of this module as the "query optimizer".
+*/
+/* #include "sqliteInt.h" */
+/* #include "whereInt.h" */
+
+/* Forward declaration of methods */
+static int whereLoopResize(sqlite3*, WhereLoop*, int);
+
+/* Test variable that can be set to enable WHERE tracing */
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
+/***/ int sqlite3WhereTrace = 0;
+#endif
+
+
+/*
+** Return the estimated number of output rows from a WHERE clause
+*/
+SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
+ return pWInfo->nRowOut;
+}
+
+/*
+** Return one of the WHERE_DISTINCT_xxxxx values to indicate how this
+** WHERE clause returns outputs for DISTINCT processing.
+*/
+SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo *pWInfo){
+ return pWInfo->eDistinct;
+}
+
+/*
+** Return TRUE if the WHERE clause returns rows in ORDER BY order.
+** Return FALSE if the output needs to be sorted.
+*/
+SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo *pWInfo){
+ return pWInfo->nOBSat;
+}
+
+/*
+** Return TRUE if the innermost loop of the WHERE clause implementation
+** returns rows in ORDER BY order for complete run of the inner loop.
+**
+** Across multiple iterations of outer loops, the output rows need not be
+** sorted. As long as rows are sorted for just the innermost loop, this
+** routine can return TRUE.
+*/
+SQLITE_PRIVATE int sqlite3WhereOrderedInnerLoop(WhereInfo *pWInfo){
+ return pWInfo->bOrderedInnerLoop;
+}
+
+/*
+** Return the VDBE address or label to jump to in order to continue
+** immediately with the next row of a WHERE clause.
+*/
+SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo *pWInfo){
+ assert( pWInfo->iContinue!=0 );
+ return pWInfo->iContinue;
+}
+
+/*
+** Return the VDBE address or label to jump to in order to break
+** out of a WHERE loop.
+*/
+SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo *pWInfo){
+ return pWInfo->iBreak;
+}
+
+/*
+** Return ONEPASS_OFF (0) if an UPDATE or DELETE statement is unable to
+** operate directly on the rowis returned by a WHERE clause. Return
+** ONEPASS_SINGLE (1) if the statement can operation directly because only
+** a single row is to be changed. Return ONEPASS_MULTI (2) if the one-pass
+** optimization can be used on multiple
+**
+** If the ONEPASS optimization is used (if this routine returns true)
+** then also write the indices of open cursors used by ONEPASS
+** into aiCur[0] and aiCur[1]. iaCur[0] gets the cursor of the data
+** table and iaCur[1] gets the cursor used by an auxiliary index.
+** Either value may be -1, indicating that cursor is not used.
+** Any cursors returned will have been opened for writing.
+**
+** aiCur[0] and aiCur[1] both get -1 if the where-clause logic is
+** unable to use the ONEPASS optimization.
+*/
+SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo *pWInfo, int *aiCur){
+ memcpy(aiCur, pWInfo->aiCurOnePass, sizeof(int)*2);
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace && pWInfo->eOnePass!=ONEPASS_OFF ){
+ sqlite3DebugPrintf("%s cursors: %d %d\n",
+ pWInfo->eOnePass==ONEPASS_SINGLE ? "ONEPASS_SINGLE" : "ONEPASS_MULTI",
+ aiCur[0], aiCur[1]);
+ }
+#endif
+ return pWInfo->eOnePass;
+}
+
+/*
+** Move the content of pSrc into pDest
+*/
+static void whereOrMove(WhereOrSet *pDest, WhereOrSet *pSrc){
+ pDest->n = pSrc->n;
+ memcpy(pDest->a, pSrc->a, pDest->n*sizeof(pDest->a[0]));
+}
+
+/*
+** Try to insert a new prerequisite/cost entry into the WhereOrSet pSet.
+**
+** The new entry might overwrite an existing entry, or it might be
+** appended, or it might be discarded. Do whatever is the right thing
+** so that pSet keeps the N_OR_COST best entries seen so far.
+*/
+static int whereOrInsert(
+ WhereOrSet *pSet, /* The WhereOrSet to be updated */
+ Bitmask prereq, /* Prerequisites of the new entry */
+ LogEst rRun, /* Run-cost of the new entry */
+ LogEst nOut /* Number of outputs for the new entry */
+){
+ u16 i;
+ WhereOrCost *p;
+ for(i=pSet->n, p=pSet->a; i>0; i--, p++){
+ if( rRun<=p->rRun && (prereq & p->prereq)==prereq ){
+ goto whereOrInsert_done;
+ }
+ if( p->rRun<=rRun && (p->prereq & prereq)==p->prereq ){
+ return 0;
+ }
+ }
+ if( pSet->n<N_OR_COST ){
+ p = &pSet->a[pSet->n++];
+ p->nOut = nOut;
+ }else{
+ p = pSet->a;
+ for(i=1; i<pSet->n; i++){
+ if( p->rRun>pSet->a[i].rRun ) p = pSet->a + i;
+ }
+ if( p->rRun<=rRun ) return 0;
+ }
+whereOrInsert_done:
+ p->prereq = prereq;
+ p->rRun = rRun;
+ if( p->nOut>nOut ) p->nOut = nOut;
+ return 1;
+}
+
+/*
+** Return the bitmask for the given cursor number. Return 0 if
+** iCursor is not in the set.
+*/
+SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet *pMaskSet, int iCursor){
+ int i;
+ assert( pMaskSet->n<=(int)sizeof(Bitmask)*8 );
+ for(i=0; i<pMaskSet->n; i++){
+ if( pMaskSet->ix[i]==iCursor ){
+ return MASKBIT(i);
}
}
return 0;
}
/*
-** This function searches the expression list passed as the second argument
-** for an expression of type TK_COLUMN that refers to the same column and
-** uses the same collation sequence as the iCol'th column of index pIdx.
-** Argument iBase is the cursor number used for the table that pIdx refers
-** to.
+** Create a new mask for cursor iCursor.
+**
+** There is one cursor per table in the FROM clause. The number of
+** tables in the FROM clause is limited by a test early in the
+** sqlite3WhereBegin() routine. So we know that the pMaskSet->ix[]
+** array will never overflow.
+*/
+static void createMask(WhereMaskSet *pMaskSet, int iCursor){
+ assert( pMaskSet->n < ArraySize(pMaskSet->ix) );
+ pMaskSet->ix[pMaskSet->n++] = iCursor;
+}
+
+/*
+** Advance to the next WhereTerm that matches according to the criteria
+** established when the pScan object was initialized by whereScanInit().
+** Return NULL if there are no more matching WhereTerms.
+*/
+static WhereTerm *whereScanNext(WhereScan *pScan){
+ int iCur; /* The cursor on the LHS of the term */
+ i16 iColumn; /* The column on the LHS of the term. -1 for IPK */
+ Expr *pX; /* An expression being tested */
+ WhereClause *pWC; /* Shorthand for pScan->pWC */
+ WhereTerm *pTerm; /* The term being tested */
+ int k = pScan->k; /* Where to start scanning */
+
+ while( pScan->iEquiv<=pScan->nEquiv ){
+ iCur = pScan->aiCur[pScan->iEquiv-1];
+ iColumn = pScan->aiColumn[pScan->iEquiv-1];
+ if( iColumn==XN_EXPR && pScan->pIdxExpr==0 ) return 0;
+ while( (pWC = pScan->pWC)!=0 ){
+ for(pTerm=pWC->a+k; k<pWC->nTerm; k++, pTerm++){
+ if( pTerm->leftCursor==iCur
+ && pTerm->u.leftColumn==iColumn
+ && (iColumn!=XN_EXPR
+ || sqlite3ExprCompare(pTerm->pExpr->pLeft,pScan->pIdxExpr,iCur)==0)
+ && (pScan->iEquiv<=1 || !ExprHasProperty(pTerm->pExpr, EP_FromJoin))
+ ){
+ if( (pTerm->eOperator & WO_EQUIV)!=0
+ && pScan->nEquiv<ArraySize(pScan->aiCur)
+ && (pX = sqlite3ExprSkipCollate(pTerm->pExpr->pRight))->op==TK_COLUMN
+ ){
+ int j;
+ for(j=0; j<pScan->nEquiv; j++){
+ if( pScan->aiCur[j]==pX->iTable
+ && pScan->aiColumn[j]==pX->iColumn ){
+ break;
+ }
+ }
+ if( j==pScan->nEquiv ){
+ pScan->aiCur[j] = pX->iTable;
+ pScan->aiColumn[j] = pX->iColumn;
+ pScan->nEquiv++;
+ }
+ }
+ if( (pTerm->eOperator & pScan->opMask)!=0 ){
+ /* Verify the affinity and collating sequence match */
+ if( pScan->zCollName && (pTerm->eOperator & WO_ISNULL)==0 ){
+ CollSeq *pColl;
+ Parse *pParse = pWC->pWInfo->pParse;
+ pX = pTerm->pExpr;
+ if( !sqlite3IndexAffinityOk(pX, pScan->idxaff) ){
+ continue;
+ }
+ assert(pX->pLeft);
+ pColl = sqlite3BinaryCompareCollSeq(pParse,
+ pX->pLeft, pX->pRight);
+ if( pColl==0 ) pColl = pParse->db->pDfltColl;
+ if( sqlite3StrICmp(pColl->zName, pScan->zCollName) ){
+ continue;
+ }
+ }
+ if( (pTerm->eOperator & (WO_EQ|WO_IS))!=0
+ && (pX = pTerm->pExpr->pRight)->op==TK_COLUMN
+ && pX->iTable==pScan->aiCur[0]
+ && pX->iColumn==pScan->aiColumn[0]
+ ){
+ testcase( pTerm->eOperator & WO_IS );
+ continue;
+ }
+ pScan->k = k+1;
+ return pTerm;
+ }
+ }
+ }
+ pScan->pWC = pScan->pWC->pOuter;
+ k = 0;
+ }
+ pScan->pWC = pScan->pOrigWC;
+ k = 0;
+ pScan->iEquiv++;
+ }
+ return 0;
+}
+
+/*
+** Initialize a WHERE clause scanner object. Return a pointer to the
+** first match. Return NULL if there are no matches.
+**
+** The scanner will be searching the WHERE clause pWC. It will look
+** for terms of the form "X <op> <expr>" where X is column iColumn of table
+** iCur. Or if pIdx!=0 then X is column iColumn of index pIdx. pIdx
+** must be one of the indexes of table iCur.
+**
+** The <op> must be one of the operators described by opMask.
+**
+** If the search is for X and the WHERE clause contains terms of the
+** form X=Y then this routine might also return terms of the form
+** "Y <op> <expr>". The number of levels of transitivity is limited,
+** but is enough to handle most commonly occurring SQL statements.
+**
+** If X is not the INTEGER PRIMARY KEY then X must be compatible with
+** index pIdx.
+*/
+static WhereTerm *whereScanInit(
+ WhereScan *pScan, /* The WhereScan object being initialized */
+ WhereClause *pWC, /* The WHERE clause to be scanned */
+ int iCur, /* Cursor to scan for */
+ int iColumn, /* Column to scan for */
+ u32 opMask, /* Operator(s) to scan for */
+ Index *pIdx /* Must be compatible with this index */
+){
+ int j = 0;
+
+ /* memset(pScan, 0, sizeof(*pScan)); */
+ pScan->pOrigWC = pWC;
+ pScan->pWC = pWC;
+ pScan->pIdxExpr = 0;
+ if( pIdx ){
+ j = iColumn;
+ iColumn = pIdx->aiColumn[j];
+ if( iColumn==XN_EXPR ) pScan->pIdxExpr = pIdx->aColExpr->a[j].pExpr;
+ if( iColumn==pIdx->pTable->iPKey ) iColumn = XN_ROWID;
+ }
+ if( pIdx && iColumn>=0 ){
+ pScan->idxaff = pIdx->pTable->aCol[iColumn].affinity;
+ pScan->zCollName = pIdx->azColl[j];
+ }else{
+ pScan->idxaff = 0;
+ pScan->zCollName = 0;
+ }
+ pScan->opMask = opMask;
+ pScan->k = 0;
+ pScan->aiCur[0] = iCur;
+ pScan->aiColumn[0] = iColumn;
+ pScan->nEquiv = 1;
+ pScan->iEquiv = 1;
+ return whereScanNext(pScan);
+}
+
+/*
+** Search for a term in the WHERE clause that is of the form "X <op> <expr>"
+** where X is a reference to the iColumn of table iCur or of index pIdx
+** if pIdx!=0 and <op> is one of the WO_xx operator codes specified by
+** the op parameter. Return a pointer to the term. Return 0 if not found.
+**
+** If pIdx!=0 then it must be one of the indexes of table iCur.
+** Search for terms matching the iColumn-th column of pIdx
+** rather than the iColumn-th column of table iCur.
+**
+** The term returned might by Y=<expr> if there is another constraint in
+** the WHERE clause that specifies that X=Y. Any such constraints will be
+** identified by the WO_EQUIV bit in the pTerm->eOperator field. The
+** aiCur[]/iaColumn[] arrays hold X and all its equivalents. There are 11
+** slots in aiCur[]/aiColumn[] so that means we can look for X plus up to 10
+** other equivalent values. Hence a search for X will return <expr> if X=A1
+** and A1=A2 and A2=A3 and ... and A9=A10 and A10=<expr>.
+**
+** If there are multiple terms in the WHERE clause of the form "X <op> <expr>"
+** then try for the one with no dependencies on <expr> - in other words where
+** <expr> is a constant expression of some kind. Only return entries of
+** the form "X <op> Y" where Y is a column in another table if no terms of
+** the form "X <op> <const-expr>" exist. If no terms with a constant RHS
+** exist, try to return a term that does not use WO_EQUIV.
+*/
+SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
+ WhereClause *pWC, /* The WHERE clause to be searched */
+ int iCur, /* Cursor number of LHS */
+ int iColumn, /* Column number of LHS */
+ Bitmask notReady, /* RHS must not overlap with this mask */
+ u32 op, /* Mask of WO_xx values describing operator */
+ Index *pIdx /* Must be compatible with this index, if not NULL */
+){
+ WhereTerm *pResult = 0;
+ WhereTerm *p;
+ WhereScan scan;
+
+ p = whereScanInit(&scan, pWC, iCur, iColumn, op, pIdx);
+ op &= WO_EQ|WO_IS;
+ while( p ){
+ if( (p->prereqRight & notReady)==0 ){
+ if( p->prereqRight==0 && (p->eOperator&op)!=0 ){
+ testcase( p->eOperator & WO_IS );
+ return p;
+ }
+ if( pResult==0 ) pResult = p;
+ }
+ p = whereScanNext(&scan);
+ }
+ return pResult;
+}
+
+/*
+** This function searches pList for an entry that matches the iCol-th column
+** of index pIdx.
**
** If such an expression is found, its index in pList->a[] is returned. If
** no expression is found, -1 is returned.
@@ -101772,13 +128197,13 @@ static int findIndexCol(
const char *zColl = pIdx->azColl[iCol];
for(i=0; i<pList->nExpr; i++){
- Expr *p = pList->a[i].pExpr;
+ Expr *p = sqlite3ExprSkipCollate(pList->a[i].pExpr);
if( p->op==TK_COLUMN
&& p->iColumn==pIdx->aiColumn[iCol]
&& p->iTable==iBase
){
- CollSeq *pColl = sqlite3ExprCollSeq(pParse, p);
- if( ALWAYS(pColl) && 0==sqlite3StrICmp(pColl->zName, zColl) ){
+ CollSeq *pColl = sqlite3ExprCollSeq(pParse, pList->a[i].pExpr);
+ if( pColl && 0==sqlite3StrICmp(pColl->zName, zColl) ){
return i;
}
}
@@ -101788,75 +128213,36 @@ static int findIndexCol(
}
/*
-** This routine determines if pIdx can be used to assist in processing a
-** DISTINCT qualifier. In other words, it tests whether or not using this
-** index for the outer loop guarantees that rows with equal values for
-** all expressions in the pDistinct list are delivered grouped together.
-**
-** For example, the query
-**
-** SELECT DISTINCT a, b, c FROM tbl WHERE a = ?
-**
-** can benefit from any index on columns "b" and "c".
+** Return TRUE if the iCol-th column of index pIdx is NOT NULL
*/
-static int isDistinctIndex(
- Parse *pParse, /* Parsing context */
- WhereClause *pWC, /* The WHERE clause */
- Index *pIdx, /* The index being considered */
- int base, /* Cursor number for the table pIdx is on */
- ExprList *pDistinct, /* The DISTINCT expressions */
- int nEqCol /* Number of index columns with == */
-){
- Bitmask mask = 0; /* Mask of unaccounted for pDistinct exprs */
- int i; /* Iterator variable */
-
- if( pIdx->zName==0 || pDistinct==0 || pDistinct->nExpr>=BMS ) return 0;
- testcase( pDistinct->nExpr==BMS-1 );
-
- /* Loop through all the expressions in the distinct list. If any of them
- ** are not simple column references, return early. Otherwise, test if the
- ** WHERE clause contains a "col=X" clause. If it does, the expression
- ** can be ignored. If it does not, and the column does not belong to the
- ** same table as index pIdx, return early. Finally, if there is no
- ** matching "col=X" expression and the column is on the same table as pIdx,
- ** set the corresponding bit in variable mask.
- */
- for(i=0; i<pDistinct->nExpr; i++){
- WhereTerm *pTerm;
- Expr *p = pDistinct->a[i].pExpr;
- if( p->op!=TK_COLUMN ) return 0;
- pTerm = findTerm(pWC, p->iTable, p->iColumn, ~(Bitmask)0, WO_EQ, 0);
- if( pTerm ){
- Expr *pX = pTerm->pExpr;
- CollSeq *p1 = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
- CollSeq *p2 = sqlite3ExprCollSeq(pParse, p);
- if( p1==p2 ) continue;
- }
- if( p->iTable!=base ) return 0;
- mask |= (((Bitmask)1) << i);
- }
+static int indexColumnNotNull(Index *pIdx, int iCol){
+ int j;
+ assert( pIdx!=0 );
+ assert( iCol>=0 && iCol<pIdx->nColumn );
+ j = pIdx->aiColumn[iCol];
+ if( j>=0 ){
+ return pIdx->pTable->aCol[j].notNull;
+ }else if( j==(-1) ){
+ return 1;
+ }else{
+ assert( j==(-2) );
+ return 0; /* Assume an indexed expression can always yield a NULL */
- for(i=nEqCol; mask && i<pIdx->nColumn; i++){
- int iExpr = findIndexCol(pParse, pDistinct, base, pIdx, i);
- if( iExpr<0 ) break;
- mask &= ~(((Bitmask)1) << iExpr);
}
-
- return (mask==0);
}
-
/*
** Return true if the DISTINCT expression-list passed as the third argument
-** is redundant. A DISTINCT list is redundant if the database contains a
-** UNIQUE index that guarantees that the result of the query will be distinct
-** anyway.
+** is redundant.
+**
+** A DISTINCT list is redundant if any subset of the columns in the
+** DISTINCT list are collectively unique and individually non-null.
*/
static int isDistinctRedundant(
- Parse *pParse,
- SrcList *pTabList,
- WhereClause *pWC,
- ExprList *pDistinct
+ Parse *pParse, /* Parsing context */
+ SrcList *pTabList, /* The FROM clause */
+ WhereClause *pWC, /* The WHERE clause */
+ ExprList *pDistinct /* The result set that needs to be DISTINCT */
){
Table *pTab;
Index *pIdx;
@@ -101875,7 +128261,7 @@ static int isDistinctRedundant(
** current SELECT is a correlated sub-query.
*/
for(i=0; i<pDistinct->nExpr; i++){
- Expr *p = pDistinct->a[i].pExpr;
+ Expr *p = sqlite3ExprSkipCollate(pDistinct->a[i].pExpr);
if( p->op==TK_COLUMN && p->iTable==iBase && p->iColumn<0 ) return 1;
}
@@ -101888,18 +128274,19 @@ static int isDistinctRedundant(
** list, or else the WHERE clause contains a term of the form "col=X",
** where X is a constant value. The collation sequences of the
** comparison and select-list expressions must match those of the index.
+ **
+ ** 3. All of those index columns for which the WHERE clause does not
+ ** contain a "col=X" term are subject to a NOT NULL constraint.
*/
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
- if( pIdx->onError==OE_None ) continue;
- for(i=0; i<pIdx->nColumn; i++){
- int iCol = pIdx->aiColumn[i];
- if( 0==findTerm(pWC, iBase, iCol, ~(Bitmask)0, WO_EQ, pIdx)
- && 0>findIndexCol(pParse, pDistinct, iBase, pIdx, i)
- ){
- break;
+ if( !IsUniqueIndex(pIdx) ) continue;
+ for(i=0; i<pIdx->nKeyCol; i++){
+ if( 0==sqlite3WhereFindTerm(pWC, iBase, i, ~(Bitmask)0, WO_EQ, pIdx) ){
+ if( findIndexCol(pParse, pDistinct, iBase, pIdx, i)<0 ) break;
+ if( indexColumnNotNull(pIdx, i)==0 ) break;
}
}
- if( i==pIdx->nColumn ){
+ if( i==pIdx->nKeyCol ){
/* This index implies that the DISTINCT qualifier is redundant. */
return 1;
}
@@ -101908,169 +128295,55 @@ static int isDistinctRedundant(
return 0;
}
+
/*
-** This routine decides if pIdx can be used to satisfy the ORDER BY
-** clause. If it can, it returns 1. If pIdx cannot satisfy the
-** ORDER BY clause, this routine returns 0.
-**
-** pOrderBy is an ORDER BY clause from a SELECT statement. pTab is the
-** left-most table in the FROM clause of that same SELECT statement and
-** the table has a cursor number of "base". pIdx is an index on pTab.
+** Estimate the logarithm of the input value to base 2.
+*/
+static LogEst estLog(LogEst N){
+ return N<=10 ? 0 : sqlite3LogEst(N) - 33;
+}
+
+/*
+** Convert OP_Column opcodes to OP_Copy in previously generated code.
**
-** nEqCol is the number of columns of pIdx that are used as equality
-** constraints. Any of these columns may be missing from the ORDER BY
-** clause and the match can still be a success.
+** This routine runs over generated VDBE code and translates OP_Column
+** opcodes into OP_Copy when the table is being accessed via co-routine
+** instead of via table lookup.
**
-** All terms of the ORDER BY that match against the index must be either
-** ASC or DESC. (Terms of the ORDER BY clause past the end of a UNIQUE
-** index do not need to satisfy this constraint.) The *pbRev value is
-** set to 1 if the ORDER BY clause is all DESC and it is set to 0 if
-** the ORDER BY clause is all ASC.
+** If the bIncrRowid parameter is 0, then any OP_Rowid instructions on
+** cursor iTabCur are transformed into OP_Null. Or, if bIncrRowid is non-zero,
+** then each OP_Rowid is transformed into an instruction to increment the
+** value stored in its output register.
*/
-static int isSortingIndex(
- Parse *pParse, /* Parsing context */
- WhereMaskSet *pMaskSet, /* Mapping from table cursor numbers to bitmaps */
- Index *pIdx, /* The index we are testing */
- int base, /* Cursor number for the table to be sorted */
- ExprList *pOrderBy, /* The ORDER BY clause */
- int nEqCol, /* Number of index columns with == constraints */
- int wsFlags, /* Index usages flags */
- int *pbRev /* Set to 1 if ORDER BY is DESC */
+static void translateColumnToCopy(
+ Vdbe *v, /* The VDBE containing code to translate */
+ int iStart, /* Translate from this opcode to the end */
+ int iTabCur, /* OP_Column/OP_Rowid references to this table */
+ int iRegister, /* The first column is in this register */
+ int bIncrRowid /* If non-zero, transform OP_rowid to OP_AddImm(1) */
){
- int i, j; /* Loop counters */
- int sortOrder = 0; /* XOR of index and ORDER BY sort direction */
- int nTerm; /* Number of ORDER BY terms */
- struct ExprList_item *pTerm; /* A term of the ORDER BY clause */
- sqlite3 *db = pParse->db;
-
- if( !pOrderBy ) return 0;
- if( wsFlags & WHERE_COLUMN_IN ) return 0;
- if( pIdx->bUnordered ) return 0;
-
- nTerm = pOrderBy->nExpr;
- assert( nTerm>0 );
-
- /* Argument pIdx must either point to a 'real' named index structure,
- ** or an index structure allocated on the stack by bestBtreeIndex() to
- ** represent the rowid index that is part of every table. */
- assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) );
-
- /* Match terms of the ORDER BY clause against columns of
- ** the index.
- **
- ** Note that indices have pIdx->nColumn regular columns plus
- ** one additional column containing the rowid. The rowid column
- ** of the index is also allowed to match against the ORDER BY
- ** clause.
- */
- for(i=j=0, pTerm=pOrderBy->a; j<nTerm && i<=pIdx->nColumn; i++){
- Expr *pExpr; /* The expression of the ORDER BY pTerm */
- CollSeq *pColl; /* The collating sequence of pExpr */
- int termSortOrder; /* Sort order for this term */
- int iColumn; /* The i-th column of the index. -1 for rowid */
- int iSortOrder; /* 1 for DESC, 0 for ASC on the i-th index term */
- const char *zColl; /* Name of the collating sequence for i-th index term */
-
- pExpr = pTerm->pExpr;
- if( pExpr->op!=TK_COLUMN || pExpr->iTable!=base ){
- /* Can not use an index sort on anything that is not a column in the
- ** left-most table of the FROM clause */
- break;
- }
- pColl = sqlite3ExprCollSeq(pParse, pExpr);
- if( !pColl ){
- pColl = db->pDfltColl;
- }
- if( pIdx->zName && i<pIdx->nColumn ){
- iColumn = pIdx->aiColumn[i];
- if( iColumn==pIdx->pTable->iPKey ){
- iColumn = -1;
- }
- iSortOrder = pIdx->aSortOrder[i];
- zColl = pIdx->azColl[i];
- }else{
- iColumn = -1;
- iSortOrder = 0;
- zColl = pColl->zName;
- }
- if( pExpr->iColumn!=iColumn || sqlite3StrICmp(pColl->zName, zColl) ){
- /* Term j of the ORDER BY clause does not match column i of the index */
- if( i<nEqCol ){
- /* If an index column that is constrained by == fails to match an
- ** ORDER BY term, that is OK. Just ignore that column of the index
- */
- continue;
- }else if( i==pIdx->nColumn ){
- /* Index column i is the rowid. All other terms match. */
- break;
+ VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
+ int iEnd = sqlite3VdbeCurrentAddr(v);
+ for(; iStart<iEnd; iStart++, pOp++){
+ if( pOp->p1!=iTabCur ) continue;
+ if( pOp->opcode==OP_Column ){
+ pOp->opcode = OP_Copy;
+ pOp->p1 = pOp->p2 + iRegister;
+ pOp->p2 = pOp->p3;
+ pOp->p3 = 0;
+ }else if( pOp->opcode==OP_Rowid ){
+ if( bIncrRowid ){
+ /* Increment the value stored in the P2 operand of the OP_Rowid. */
+ pOp->opcode = OP_AddImm;
+ pOp->p1 = pOp->p2;
+ pOp->p2 = 1;
}else{
- /* If an index column fails to match and is not constrained by ==
- ** then the index cannot satisfy the ORDER BY constraint.
- */
- return 0;
- }
- }
- assert( pIdx->aSortOrder!=0 || iColumn==-1 );
- assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 );
- assert( iSortOrder==0 || iSortOrder==1 );
- termSortOrder = iSortOrder ^ pTerm->sortOrder;
- if( i>nEqCol ){
- if( termSortOrder!=sortOrder ){
- /* Indices can only be used if all ORDER BY terms past the
- ** equality constraints are all either DESC or ASC. */
- return 0;
+ pOp->opcode = OP_Null;
+ pOp->p1 = 0;
+ pOp->p3 = 0;
}
- }else{
- sortOrder = termSortOrder;
- }
- j++;
- pTerm++;
- if( iColumn<0 && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){
- /* If the indexed column is the primary key and everything matches
- ** so far and none of the ORDER BY terms to the right reference other
- ** tables in the join, then we are assured that the index can be used
- ** to sort because the primary key is unique and so none of the other
- ** columns will make any difference
- */
- j = nTerm;
}
}
-
- *pbRev = sortOrder!=0;
- if( j>=nTerm ){
- /* All terms of the ORDER BY clause are covered by this index so
- ** this index can be used for sorting. */
- return 1;
- }
- if( pIdx->onError!=OE_None && i==pIdx->nColumn
- && (wsFlags & WHERE_COLUMN_NULL)==0
- && !referencesOtherTables(pOrderBy, pMaskSet, j, base) ){
- /* All terms of this index match some prefix of the ORDER BY clause
- ** and the index is UNIQUE and no terms on the tail of the ORDER BY
- ** clause reference other tables in a join. If this is all true then
- ** the order by clause is superfluous. Not that if the matching
- ** condition is IS NULL then the result is not necessarily unique
- ** even on a UNIQUE index, so disallow those cases. */
- return 1;
- }
- return 0;
-}
-
-/*
-** Prepare a crude estimate of the logarithm of the input value.
-** The results need not be exact. This is only used for estimating
-** the total cost of performing operations with O(logN) or O(NlogN)
-** complexity. Because N is just a guess, it is no great tragedy if
-** logN is a little off.
-*/
-static double estLog(double N){
- double logN = 1;
- double x = 10;
- while( N>x ){
- logN += 1;
- x *= 10;
- }
- return logN;
}
/*
@@ -102079,7 +128352,7 @@ static double estLog(double N){
** SQLITE_TEST or SQLITE_DEBUG are defined, then these routines
** are no-ops.
*/
-#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_DEBUG)
+#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(WHERETRACE_ENABLED)
static void TRACE_IDX_INPUTS(sqlite3_index_info *p){
int i;
if( !sqlite3WhereTrace ) return;
@@ -102111,110 +128384,13 @@ static void TRACE_IDX_OUTPUTS(sqlite3_index_info *p){
sqlite3DebugPrintf(" idxStr=%s\n", p->idxStr);
sqlite3DebugPrintf(" orderByConsumed=%d\n", p->orderByConsumed);
sqlite3DebugPrintf(" estimatedCost=%g\n", p->estimatedCost);
+ sqlite3DebugPrintf(" estimatedRows=%lld\n", p->estimatedRows);
}
#else
#define TRACE_IDX_INPUTS(A)
#define TRACE_IDX_OUTPUTS(A)
#endif
-/*
-** Required because bestIndex() is called by bestOrClauseIndex()
-*/
-static void bestIndex(
- Parse*, WhereClause*, struct SrcList_item*,
- Bitmask, Bitmask, ExprList*, WhereCost*);
-
-/*
-** This routine attempts to find an scanning strategy that can be used
-** to optimize an 'OR' expression that is part of a WHERE clause.
-**
-** The table associated with FROM clause term pSrc may be either a
-** regular B-Tree table or a virtual table.
-*/
-static void bestOrClauseIndex(
- Parse *pParse, /* The parsing context */
- WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to search */
- Bitmask notReady, /* Mask of cursors not available for indexing */
- Bitmask notValid, /* Cursors not available for any purpose */
- ExprList *pOrderBy, /* The ORDER BY clause */
- WhereCost *pCost /* Lowest cost query plan */
-){
-#ifndef SQLITE_OMIT_OR_OPTIMIZATION
- const int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */
- const Bitmask maskSrc = getMask(pWC->pMaskSet, iCur); /* Bitmask for pSrc */
- WhereTerm * const pWCEnd = &pWC->a[pWC->nTerm]; /* End of pWC->a[] */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
-
- /* No OR-clause optimization allowed if the INDEXED BY or NOT INDEXED clauses
- ** are used */
- if( pSrc->notIndexed || pSrc->pIndex!=0 ){
- return;
- }
-
- /* Search the WHERE clause terms for a usable WO_OR term. */
- for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
- if( pTerm->eOperator==WO_OR
- && ((pTerm->prereqAll & ~maskSrc) & notReady)==0
- && (pTerm->u.pOrInfo->indexable & maskSrc)!=0
- ){
- WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
- WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
- WhereTerm *pOrTerm;
- int flags = WHERE_MULTI_OR;
- double rTotal = 0;
- double nRow = 0;
- Bitmask used = 0;
-
- for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
- WhereCost sTermCost;
- WHERETRACE(("... Multi-index OR testing for term %d of %d....\n",
- (pOrTerm - pOrWC->a), (pTerm - pWC->a)
- ));
- if( pOrTerm->eOperator==WO_AND ){
- WhereClause *pAndWC = &pOrTerm->u.pAndInfo->wc;
- bestIndex(pParse, pAndWC, pSrc, notReady, notValid, 0, &sTermCost);
- }else if( pOrTerm->leftCursor==iCur ){
- WhereClause tempWC;
- tempWC.pParse = pWC->pParse;
- tempWC.pMaskSet = pWC->pMaskSet;
- tempWC.op = TK_AND;
- tempWC.a = pOrTerm;
- tempWC.nTerm = 1;
- bestIndex(pParse, &tempWC, pSrc, notReady, notValid, 0, &sTermCost);
- }else{
- continue;
- }
- rTotal += sTermCost.rCost;
- nRow += sTermCost.plan.nRow;
- used |= sTermCost.used;
- if( rTotal>=pCost->rCost ) break;
- }
-
- /* If there is an ORDER BY clause, increase the scan cost to account
- ** for the cost of the sort. */
- if( pOrderBy!=0 ){
- WHERETRACE(("... sorting increases OR cost %.9g to %.9g\n",
- rTotal, rTotal+nRow*estLog(nRow)));
- rTotal += nRow*estLog(nRow);
- }
-
- /* If the cost of scanning using this OR term for optimization is
- ** less than the current cost stored in pCost, replace the contents
- ** of pCost. */
- WHERETRACE(("... multi-index OR cost=%.9g nrow=%.9g\n", rTotal, nRow));
- if( rTotal<pCost->rCost ){
- pCost->rCost = rTotal;
- pCost->used = used;
- pCost->plan.nRow = nRow;
- pCost->plan.wsFlags = flags;
- pCost->plan.u.pTerm = pTerm;
- }
- }
- }
-#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
-}
-
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
** Return TRUE if the WHERE clause term pTerm is of a form where it
@@ -102228,87 +128404,16 @@ static int termCanDriveIndex(
){
char aff;
if( pTerm->leftCursor!=pSrc->iCursor ) return 0;
- if( pTerm->eOperator!=WO_EQ ) return 0;
+ if( (pTerm->eOperator & (WO_EQ|WO_IS))==0 ) return 0;
if( (pTerm->prereqRight & notReady)!=0 ) return 0;
+ if( pTerm->u.leftColumn<0 ) return 0;
aff = pSrc->pTab->aCol[pTerm->u.leftColumn].affinity;
if( !sqlite3IndexAffinityOk(pTerm->pExpr, aff) ) return 0;
+ testcase( pTerm->pExpr->op==TK_IS );
return 1;
}
#endif
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
-/*
-** If the query plan for pSrc specified in pCost is a full table scan
-** and indexing is allows (if there is no NOT INDEXED clause) and it
-** possible to construct a transient index that would perform better
-** than a full table scan even when the cost of constructing the index
-** is taken into account, then alter the query plan to use the
-** transient index.
-*/
-static void bestAutomaticIndex(
- Parse *pParse, /* The parsing context */
- WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to search */
- Bitmask notReady, /* Mask of cursors that are not available */
- WhereCost *pCost /* Lowest cost query plan */
-){
- double nTableRow; /* Rows in the input table */
- double logN; /* log(nTableRow) */
- double costTempIdx; /* per-query cost of the transient index */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
- WhereTerm *pWCEnd; /* End of pWC->a[] */
- Table *pTable; /* Table tht might be indexed */
-
- if( pParse->nQueryLoop<=(double)1 ){
- /* There is no point in building an automatic index for a single scan */
- return;
- }
- if( (pParse->db->flags & SQLITE_AutoIndex)==0 ){
- /* Automatic indices are disabled at run-time */
- return;
- }
- if( (pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)!=0 ){
- /* We already have some kind of index in use for this query. */
- return;
- }
- if( pSrc->notIndexed ){
- /* The NOT INDEXED clause appears in the SQL. */
- return;
- }
- if( pSrc->isCorrelated ){
- /* The source is a correlated sub-query. No point in indexing it. */
- return;
- }
-
- assert( pParse->nQueryLoop >= (double)1 );
- pTable = pSrc->pTab;
- nTableRow = pTable->nRowEst;
- logN = estLog(nTableRow);
- costTempIdx = 2*logN*(nTableRow/pParse->nQueryLoop + 1);
- if( costTempIdx>=pCost->rCost ){
- /* The cost of creating the transient table would be greater than
- ** doing the full table scan */
- return;
- }
-
- /* Search for any equality comparison term */
- pWCEnd = &pWC->a[pWC->nTerm];
- for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
- if( termCanDriveIndex(pTerm, pSrc, notReady) ){
- WHERETRACE(("auto-index reduces cost from %.1f to %.1f\n",
- pCost->rCost, costTempIdx));
- pCost->rCost = costTempIdx;
- pCost->plan.nRow = logN + 1;
- pCost->plan.wsFlags = WHERE_TEMP_INDEX;
- pCost->used = pTerm->prereqRight;
- break;
- }
- }
-}
-#else
-# define bestAutomaticIndex(A,B,C,D,E) /* no-op */
-#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
-
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
/*
@@ -102323,52 +128428,79 @@ static void constructAutomaticIndex(
Bitmask notReady, /* Mask of cursors that are not available */
WhereLevel *pLevel /* Write new index here */
){
- int nColumn; /* Number of columns in the constructed index */
+ int nKeyCol; /* Number of columns in the constructed index */
WhereTerm *pTerm; /* A single term of the WHERE clause */
WhereTerm *pWCEnd; /* End of pWC->a[] */
- int nByte; /* Byte of memory needed for pIdx */
Index *pIdx; /* Object describing the transient index */
Vdbe *v; /* Prepared statement under construction */
- int regIsInit; /* Register set by initialization */
int addrInit; /* Address of the initialization bypass jump */
Table *pTable; /* The table being indexed */
- KeyInfo *pKeyinfo; /* Key information for the index */
int addrTop; /* Top of the index fill loop */
int regRecord; /* Register holding an index record */
int n; /* Column counter */
int i; /* Loop counter */
int mxBitCol; /* Maximum column in pSrc->colUsed */
CollSeq *pColl; /* Collating sequence to on a column */
+ WhereLoop *pLoop; /* The Loop object */
+ char *zNotUsed; /* Extra space on the end of pIdx */
Bitmask idxCols; /* Bitmap of columns used for indexing */
Bitmask extraCols; /* Bitmap of additional columns */
+ u8 sentWarning = 0; /* True if a warnning has been issued */
+ Expr *pPartial = 0; /* Partial Index Expression */
+ int iContinue = 0; /* Jump here to skip excluded rows */
+ struct SrcList_item *pTabItem; /* FROM clause term being indexed */
+ int addrCounter = 0; /* Address where integer counter is initialized */
+ int regBase; /* Array of registers where record is assembled */
/* Generate code to skip over the creation and initialization of the
** transient index on 2nd and subsequent iterations of the loop. */
v = pParse->pVdbe;
assert( v!=0 );
- regIsInit = ++pParse->nMem;
- addrInit = sqlite3VdbeAddOp1(v, OP_Once, regIsInit);
+ addrInit = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
/* Count the number of columns that will be added to the index
** and used to match WHERE clause constraints */
- nColumn = 0;
+ nKeyCol = 0;
pTable = pSrc->pTab;
pWCEnd = &pWC->a[pWC->nTerm];
+ pLoop = pLevel->pWLoop;
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
+ Expr *pExpr = pTerm->pExpr;
+ assert( !ExprHasProperty(pExpr, EP_FromJoin) /* prereq always non-zero */
+ || pExpr->iRightJoinTable!=pSrc->iCursor /* for the right-hand */
+ || pLoop->prereq!=0 ); /* table of a LEFT JOIN */
+ if( pLoop->prereq==0
+ && (pTerm->wtFlags & TERM_VIRTUAL)==0
+ && !ExprHasProperty(pExpr, EP_FromJoin)
+ && sqlite3ExprIsTableConstant(pExpr, pSrc->iCursor) ){
+ pPartial = sqlite3ExprAnd(pParse->db, pPartial,
+ sqlite3ExprDup(pParse->db, pExpr, 0));
+ }
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
+ Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
testcase( iCol==BMS );
testcase( iCol==BMS-1 );
+ if( !sentWarning ){
+ sqlite3_log(SQLITE_WARNING_AUTOINDEX,
+ "automatic index on %s(%s)", pTable->zName,
+ pTable->aCol[iCol].zName);
+ sentWarning = 1;
+ }
if( (idxCols & cMask)==0 ){
- nColumn++;
+ if( whereLoopResize(pParse->db, pLoop, nKeyCol+1) ){
+ goto end_auto_index_create;
+ }
+ pLoop->aLTerm[nKeyCol++] = pTerm;
idxCols |= cMask;
}
}
}
- assert( nColumn>0 );
- pLevel->plan.nEq = nColumn;
+ assert( nKeyCol>0 );
+ pLoop->u.btree.nEq = pLoop->nLTerm = nKeyCol;
+ pLoop->wsFlags = WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WHERE_INDEXED
+ | WHERE_AUTO_INDEX;
/* Count the number of additional columns needed to create a
** covering index. A "covering index" is an index that contains all
@@ -102378,88 +128510,113 @@ static void constructAutomaticIndex(
** original table changes and the index and table cannot both be used
** if they go out of sync.
*/
- extraCols = pSrc->colUsed & (~idxCols | (((Bitmask)1)<<(BMS-1)));
- mxBitCol = (pTable->nCol >= BMS-1) ? BMS-1 : pTable->nCol;
+ extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
+ mxBitCol = MIN(BMS-1,pTable->nCol);
testcase( pTable->nCol==BMS-1 );
testcase( pTable->nCol==BMS-2 );
for(i=0; i<mxBitCol; i++){
- if( extraCols & (((Bitmask)1)<<i) ) nColumn++;
+ if( extraCols & MASKBIT(i) ) nKeyCol++;
}
- if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
- nColumn += pTable->nCol - BMS + 1;
+ if( pSrc->colUsed & MASKBIT(BMS-1) ){
+ nKeyCol += pTable->nCol - BMS + 1;
}
- pLevel->plan.wsFlags |= WHERE_COLUMN_EQ | WHERE_IDX_ONLY | WO_EQ;
/* Construct the Index object to describe this index */
- nByte = sizeof(Index);
- nByte += nColumn*sizeof(int); /* Index.aiColumn */
- nByte += nColumn*sizeof(char*); /* Index.azColl */
- nByte += nColumn; /* Index.aSortOrder */
- pIdx = sqlite3DbMallocZero(pParse->db, nByte);
- if( pIdx==0 ) return;
- pLevel->plan.u.pIdx = pIdx;
- pIdx->azColl = (char**)&pIdx[1];
- pIdx->aiColumn = (int*)&pIdx->azColl[nColumn];
- pIdx->aSortOrder = (u8*)&pIdx->aiColumn[nColumn];
+ pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed);
+ if( pIdx==0 ) goto end_auto_index_create;
+ pLoop->u.btree.pIndex = pIdx;
pIdx->zName = "auto-index";
- pIdx->nColumn = nColumn;
pIdx->pTable = pTable;
n = 0;
idxCols = 0;
for(pTerm=pWC->a; pTerm<pWCEnd; pTerm++){
if( termCanDriveIndex(pTerm, pSrc, notReady) ){
int iCol = pTerm->u.leftColumn;
- Bitmask cMask = iCol>=BMS ? ((Bitmask)1)<<(BMS-1) : ((Bitmask)1)<<iCol;
+ Bitmask cMask = iCol>=BMS ? MASKBIT(BMS-1) : MASKBIT(iCol);
+ testcase( iCol==BMS-1 );
+ testcase( iCol==BMS );
if( (idxCols & cMask)==0 ){
Expr *pX = pTerm->pExpr;
idxCols |= cMask;
pIdx->aiColumn[n] = pTerm->u.leftColumn;
pColl = sqlite3BinaryCompareCollSeq(pParse, pX->pLeft, pX->pRight);
- pIdx->azColl[n] = ALWAYS(pColl) ? pColl->zName : "BINARY";
+ pIdx->azColl[n] = pColl ? pColl->zName : sqlite3StrBINARY;
n++;
}
}
}
- assert( (u32)n==pLevel->plan.nEq );
+ assert( (u32)n==pLoop->u.btree.nEq );
/* Add additional columns needed to make the automatic index into
** a covering index */
for(i=0; i<mxBitCol; i++){
- if( extraCols & (((Bitmask)1)<<i) ){
+ if( extraCols & MASKBIT(i) ){
pIdx->aiColumn[n] = i;
- pIdx->azColl[n] = "BINARY";
+ pIdx->azColl[n] = sqlite3StrBINARY;
n++;
}
}
- if( pSrc->colUsed & (((Bitmask)1)<<(BMS-1)) ){
+ if( pSrc->colUsed & MASKBIT(BMS-1) ){
for(i=BMS-1; i<pTable->nCol; i++){
pIdx->aiColumn[n] = i;
- pIdx->azColl[n] = "BINARY";
+ pIdx->azColl[n] = sqlite3StrBINARY;
n++;
}
}
- assert( n==nColumn );
+ assert( n==nKeyCol );
+ pIdx->aiColumn[n] = XN_ROWID;
+ pIdx->azColl[n] = sqlite3StrBINARY;
/* Create the automatic index */
- pKeyinfo = sqlite3IndexKeyinfo(pParse, pIdx);
assert( pLevel->iIdxCur>=0 );
- sqlite3VdbeAddOp4(v, OP_OpenAutoindex, pLevel->iIdxCur, nColumn+1, 0,
- (char*)pKeyinfo, P4_KEYINFO_HANDOFF);
+ pLevel->iIdxCur = pParse->nTab++;
+ sqlite3VdbeAddOp2(v, OP_OpenAutoindex, pLevel->iIdxCur, nKeyCol+1);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
VdbeComment((v, "for %s", pTable->zName));
/* Fill the automatic index with content */
- addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur);
+ sqlite3ExprCachePush(pParse);
+ pTabItem = &pWC->pWInfo->pTabList->a[pLevel->iFrom];
+ if( pTabItem->fg.viaCoroutine ){
+ int regYield = pTabItem->regReturn;
+ addrCounter = sqlite3VdbeAddOp2(v, OP_Integer, 0, 0);
+ sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, pTabItem->addrFillSub);
+ addrTop = sqlite3VdbeAddOp1(v, OP_Yield, regYield);
+ VdbeCoverage(v);
+ VdbeComment((v, "next row of \"%s\"", pTabItem->pTab->zName));
+ }else{
+ addrTop = sqlite3VdbeAddOp1(v, OP_Rewind, pLevel->iTabCur); VdbeCoverage(v);
+ }
+ if( pPartial ){
+ iContinue = sqlite3VdbeMakeLabel(v);
+ sqlite3ExprIfFalse(pParse, pPartial, iContinue, SQLITE_JUMPIFNULL);
+ pLoop->wsFlags |= WHERE_PARTIALIDX;
+ }
regRecord = sqlite3GetTempReg(pParse);
- sqlite3GenerateIndexKey(pParse, pIdx, pLevel->iTabCur, regRecord, 1);
+ regBase = sqlite3GenerateIndexKey(
+ pParse, pIdx, pLevel->iTabCur, regRecord, 0, 0, 0, 0
+ );
sqlite3VdbeAddOp2(v, OP_IdxInsert, pLevel->iIdxCur, regRecord);
sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT);
- sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1);
+ if( pPartial ) sqlite3VdbeResolveLabel(v, iContinue);
+ if( pTabItem->fg.viaCoroutine ){
+ sqlite3VdbeChangeP2(v, addrCounter, regBase+n);
+ translateColumnToCopy(v, addrTop, pLevel->iTabCur, pTabItem->regResult, 1);
+ sqlite3VdbeGoto(v, addrTop);
+ pTabItem->fg.viaCoroutine = 0;
+ }else{
+ sqlite3VdbeAddOp2(v, OP_Next, pLevel->iTabCur, addrTop+1); VdbeCoverage(v);
+ }
sqlite3VdbeChangeP5(v, SQLITE_STMTSTATUS_AUTOINDEX);
sqlite3VdbeJumpHere(v, addrTop);
sqlite3ReleaseTempReg(pParse, regRecord);
+ sqlite3ExprCachePop(pParse);
/* Jump here when skipping the initialization */
sqlite3VdbeJumpHere(v, addrInit);
+
+end_auto_index_create:
+ sqlite3ExprDelete(pParse->db, pPartial);
}
#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
@@ -102470,10 +128627,12 @@ static void constructAutomaticIndex(
** by passing the pointer returned by this function to sqlite3_free().
*/
static sqlite3_index_info *allocateIndexInfo(
- Parse *pParse,
+ Parse *pParse,
WhereClause *pWC,
+ Bitmask mUnusable, /* Ignore terms with these prereqs */
struct SrcList_item *pSrc,
- ExprList *pOrderBy
+ ExprList *pOrderBy,
+ u16 *pmNoOmit /* Mask of terms not to omit */
){
int i, j;
int nTerm;
@@ -102483,18 +128642,21 @@ static sqlite3_index_info *allocateIndexInfo(
WhereTerm *pTerm;
int nOrderBy;
sqlite3_index_info *pIdxInfo;
-
- WHERETRACE(("Recomputing index info for %s...\n", pSrc->pTab->zName));
+ u16 mNoOmit = 0;
/* Count the number of possible WHERE clause constraints referring
** to this virtual table */
for(i=nTerm=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
if( pTerm->leftCursor != pSrc->iCursor ) continue;
- assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
- testcase( pTerm->eOperator==WO_IN );
- testcase( pTerm->eOperator==WO_ISNULL );
- if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
+ if( pTerm->prereqRight & mUnusable ) continue;
+ assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
+ testcase( pTerm->eOperator & WO_IN );
+ testcase( pTerm->eOperator & WO_ISNULL );
+ testcase( pTerm->eOperator & WO_IS );
+ testcase( pTerm->eOperator & WO_ALL );
+ if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
+ assert( pTerm->u.leftColumn>=(-1) );
nTerm++;
}
@@ -102504,12 +128666,13 @@ static sqlite3_index_info *allocateIndexInfo(
*/
nOrderBy = 0;
if( pOrderBy ){
- for(i=0; i<pOrderBy->nExpr; i++){
+ int n = pOrderBy->nExpr;
+ for(i=0; i<n; i++){
Expr *pExpr = pOrderBy->a[i].pExpr;
if( pExpr->op!=TK_COLUMN || pExpr->iTable!=pSrc->iCursor ) break;
}
- if( i==pOrderBy->nExpr ){
- nOrderBy = pOrderBy->nExpr;
+ if( i==n){
+ nOrderBy = n;
}
}
@@ -102520,7 +128683,6 @@ static sqlite3_index_info *allocateIndexInfo(
+ sizeof(*pIdxOrderBy)*nOrderBy );
if( pIdxInfo==0 ){
sqlite3ErrorMsg(pParse, "out of memory");
- /* (double)0 In case of SQLITE_OMIT_FLOATING_POINT... */
return 0;
}
@@ -102540,15 +128702,25 @@ static sqlite3_index_info *allocateIndexInfo(
pUsage;
for(i=j=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
+ u8 op;
if( pTerm->leftCursor != pSrc->iCursor ) continue;
- assert( (pTerm->eOperator&(pTerm->eOperator-1))==0 );
- testcase( pTerm->eOperator==WO_IN );
- testcase( pTerm->eOperator==WO_ISNULL );
- if( pTerm->eOperator & (WO_IN|WO_ISNULL) ) continue;
+ if( pTerm->prereqRight & mUnusable ) continue;
+ assert( IsPowerOfTwo(pTerm->eOperator & ~WO_EQUIV) );
+ testcase( pTerm->eOperator & WO_IN );
+ testcase( pTerm->eOperator & WO_IS );
+ testcase( pTerm->eOperator & WO_ISNULL );
+ testcase( pTerm->eOperator & WO_ALL );
+ if( (pTerm->eOperator & ~(WO_ISNULL|WO_EQUIV|WO_IS))==0 ) continue;
if( pTerm->wtFlags & TERM_VNULL ) continue;
+ assert( pTerm->u.leftColumn>=(-1) );
pIdxCons[j].iColumn = pTerm->u.leftColumn;
pIdxCons[j].iTermOffset = i;
- pIdxCons[j].op = (u8)pTerm->eOperator;
+ op = (u8)pTerm->eOperator & WO_ALL;
+ if( op==WO_IN ) op = WO_EQ;
+ if( op==WO_MATCH ){
+ op = pTerm->eMatchOp;
+ }
+ pIdxCons[j].op = op;
/* The direct assignment in the previous line is possible only because
** the WO_ and SQLITE_INDEX_CONSTRAINT_ codes are identical. The
** following asserts verify this fact. */
@@ -102558,7 +128730,16 @@ static sqlite3_index_info *allocateIndexInfo(
assert( WO_GT==SQLITE_INDEX_CONSTRAINT_GT );
assert( WO_GE==SQLITE_INDEX_CONSTRAINT_GE );
assert( WO_MATCH==SQLITE_INDEX_CONSTRAINT_MATCH );
- assert( pTerm->eOperator & (WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
+ assert( pTerm->eOperator & (WO_IN|WO_EQ|WO_LT|WO_LE|WO_GT|WO_GE|WO_MATCH) );
+
+ if( op & (WO_LT|WO_LE|WO_GT|WO_GE)
+ && sqlite3ExprIsVector(pTerm->pExpr->pRight)
+ ){
+ if( i<16 ) mNoOmit |= (1 << i);
+ if( op==WO_LT ) pIdxCons[j].op = WO_LE;
+ if( op==WO_GT ) pIdxCons[j].op = WO_GE;
+ }
+
j++;
}
for(i=0; i<nOrderBy; i++){
@@ -102567,14 +128748,15 @@ static sqlite3_index_info *allocateIndexInfo(
pIdxOrderBy[i].desc = pOrderBy->a[i].sortOrder;
}
+ *pmNoOmit = mNoOmit;
return pIdxInfo;
}
/*
** The table object reference passed as the second argument to this function
** must represent a virtual table. This function invokes the xBestIndex()
-** method of the virtual table with the sqlite3_index_info pointer passed
-** as the argument.
+** method of the virtual table with the sqlite3_index_info object that
+** comes in as the 3rd argument to this function.
**
** If an error occurs, pParse is populated with an error message and a
** non-zero value is returned. Otherwise, 0 is returned and the output
@@ -102586,17 +128768,15 @@ static sqlite3_index_info *allocateIndexInfo(
*/
static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab;
- int i;
int rc;
- WHERETRACE(("xBestIndex for %s\n", pTab->zName));
TRACE_IDX_INPUTS(p);
rc = pVtab->pModule->xBestIndex(pVtab, p);
TRACE_IDX_OUTPUTS(p);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_NOMEM ){
- pParse->db->mallocFailed = 1;
+ sqlite3OomFault(pParse->db);
}else if( !pVtab->zErrMsg ){
sqlite3ErrorMsg(pParse, "%s", sqlite3ErrStr(rc));
}else{
@@ -102606,319 +128786,361 @@ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = 0;
+#if 0
+ /* This error is now caught by the caller.
+ ** Search for "xBestIndex malfunction" below */
for(i=0; i<p->nConstraint; i++){
if( !p->aConstraint[i].usable && p->aConstraintUsage[i].argvIndex>0 ){
sqlite3ErrorMsg(pParse,
"table %s: xBestIndex returned an invalid plan", pTab->zName);
}
}
+#endif
return pParse->nErr;
}
+#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) */
-
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
-** Compute the best index for a virtual table.
+** Estimate the location of a particular key among all keys in an
+** index. Store the results in aStat as follows:
**
-** The best index is computed by the xBestIndex method of the virtual
-** table module. This routine is really just a wrapper that sets up
-** the sqlite3_index_info structure that is used to communicate with
-** xBestIndex.
+** aStat[0] Est. number of rows less than pRec
+** aStat[1] Est. number of rows equal to pRec
**
-** In a join, this routine might be called multiple times for the
-** same virtual table. The sqlite3_index_info structure is created
-** and initialized on the first invocation and reused on all subsequent
-** invocations. The sqlite3_index_info structure is also used when
-** code is generated to access the virtual table. The whereInfoDelete()
-** routine takes care of freeing the sqlite3_index_info structure after
-** everybody has finished with it.
+** Return the index of the sample that is the smallest sample that
+** is greater than or equal to pRec. Note that this index is not an index
+** into the aSample[] array - it is an index into a virtual set of samples
+** based on the contents of aSample[] and the number of fields in record
+** pRec.
*/
-static void bestVirtualIndex(
- Parse *pParse, /* The parsing context */
- WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to search */
- Bitmask notReady, /* Mask of cursors not available for index */
- Bitmask notValid, /* Cursors not valid for any purpose */
- ExprList *pOrderBy, /* The order by clause */
- WhereCost *pCost, /* Lowest cost query plan */
- sqlite3_index_info **ppIdxInfo /* Index information passed to xBestIndex */
-){
- Table *pTab = pSrc->pTab;
- sqlite3_index_info *pIdxInfo;
- struct sqlite3_index_constraint *pIdxCons;
- struct sqlite3_index_constraint_usage *pUsage;
- WhereTerm *pTerm;
- int i, j;
- int nOrderBy;
- double rCost;
-
- /* Make sure wsFlags is initialized to some sane value. Otherwise, if the
- ** malloc in allocateIndexInfo() fails and this function returns leaving
- ** wsFlags in an uninitialized state, the caller may behave unpredictably.
- */
- memset(pCost, 0, sizeof(*pCost));
- pCost->plan.wsFlags = WHERE_VIRTUALTABLE;
-
- /* If the sqlite3_index_info structure has not been previously
- ** allocated and initialized, then allocate and initialize it now.
- */
- pIdxInfo = *ppIdxInfo;
- if( pIdxInfo==0 ){
- *ppIdxInfo = pIdxInfo = allocateIndexInfo(pParse, pWC, pSrc, pOrderBy);
- }
- if( pIdxInfo==0 ){
- return;
- }
-
- /* At this point, the sqlite3_index_info structure that pIdxInfo points
- ** to will have been initialized, either during the current invocation or
- ** during some prior invocation. Now we just have to customize the
- ** details of pIdxInfo for the current invocation and pass it to
- ** xBestIndex.
- */
+static int whereKeyStats(
+ Parse *pParse, /* Database connection */
+ Index *pIdx, /* Index to consider domain of */
+ UnpackedRecord *pRec, /* Vector of values to consider */
+ int roundUp, /* Round up if true. Round down if false */
+ tRowcnt *aStat /* OUT: stats written here */
+){
+ IndexSample *aSample = pIdx->aSample;
+ int iCol; /* Index of required stats in anEq[] etc. */
+ int i; /* Index of first sample >= pRec */
+ int iSample; /* Smallest sample larger than or equal to pRec */
+ int iMin = 0; /* Smallest sample not yet tested */
+ int iTest; /* Next sample to test */
+ int res; /* Result of comparison operation */
+ int nField; /* Number of fields in pRec */
+ tRowcnt iLower = 0; /* anLt[] + anEq[] of largest sample pRec is > */
- /* The module name must be defined. Also, by this point there must
- ** be a pointer to an sqlite3_vtab structure. Otherwise
- ** sqlite3ViewGetColumnNames() would have picked up the error.
- */
- assert( pTab->azModuleArg && pTab->azModuleArg[0] );
- assert( sqlite3GetVTable(pParse->db, pTab) );
+#ifndef SQLITE_DEBUG
+ UNUSED_PARAMETER( pParse );
+#endif
+ assert( pRec!=0 );
+ assert( pIdx->nSample>0 );
+ assert( pRec->nField>0 && pRec->nField<=pIdx->nSampleCol );
- /* Set the aConstraint[].usable fields and initialize all
- ** output variables to zero.
+ /* Do a binary search to find the first sample greater than or equal
+ ** to pRec. If pRec contains a single field, the set of samples to search
+ ** is simply the aSample[] array. If the samples in aSample[] contain more
+ ** than one fields, all fields following the first are ignored.
**
- ** aConstraint[].usable is true for constraints where the right-hand
- ** side contains only references to tables to the left of the current
- ** table. In other words, if the constraint is of the form:
+ ** If pRec contains N fields, where N is more than one, then as well as the
+ ** samples in aSample[] (truncated to N fields), the search also has to
+ ** consider prefixes of those samples. For example, if the set of samples
+ ** in aSample is:
**
- ** column = expr
+ ** aSample[0] = (a, 5)
+ ** aSample[1] = (a, 10)
+ ** aSample[2] = (b, 5)
+ ** aSample[3] = (c, 100)
+ ** aSample[4] = (c, 105)
**
- ** and we are evaluating a join, then the constraint on column is
- ** only valid if all tables referenced in expr occur to the left
- ** of the table containing column.
+ ** Then the search space should ideally be the samples above and the
+ ** unique prefixes [a], [b] and [c]. But since that is hard to organize,
+ ** the code actually searches this set:
**
- ** The aConstraints[] array contains entries for all constraints
- ** on the current table. That way we only have to compute it once
- ** even though we might try to pick the best index multiple times.
- ** For each attempt at picking an index, the order of tables in the
- ** join might be different so we have to recompute the usable flag
- ** each time.
- */
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- pUsage = pIdxInfo->aConstraintUsage;
- for(i=0; i<pIdxInfo->nConstraint; i++, pIdxCons++){
- j = pIdxCons->iTermOffset;
- pTerm = &pWC->a[j];
- pIdxCons->usable = (pTerm->prereqRight&notReady) ? 0 : 1;
- }
- memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint);
- if( pIdxInfo->needToFreeIdxStr ){
- sqlite3_free(pIdxInfo->idxStr);
- }
- pIdxInfo->idxStr = 0;
- pIdxInfo->idxNum = 0;
- pIdxInfo->needToFreeIdxStr = 0;
- pIdxInfo->orderByConsumed = 0;
- /* ((double)2) In case of SQLITE_OMIT_FLOATING_POINT... */
- pIdxInfo->estimatedCost = SQLITE_BIG_DBL / ((double)2);
- nOrderBy = pIdxInfo->nOrderBy;
- if( !pOrderBy ){
- pIdxInfo->nOrderBy = 0;
- }
-
- if( vtabBestIndex(pParse, pTab, pIdxInfo) ){
- return;
- }
-
- pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
- for(i=0; i<pIdxInfo->nConstraint; i++){
- if( pUsage[i].argvIndex>0 ){
- pCost->used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight;
+ ** 0: (a)
+ ** 1: (a, 5)
+ ** 2: (a, 10)
+ ** 3: (a, 10)
+ ** 4: (b)
+ ** 5: (b, 5)
+ ** 6: (c)
+ ** 7: (c, 100)
+ ** 8: (c, 105)
+ ** 9: (c, 105)
+ **
+ ** For each sample in the aSample[] array, N samples are present in the
+ ** effective sample array. In the above, samples 0 and 1 are based on
+ ** sample aSample[0]. Samples 2 and 3 on aSample[1] etc.
+ **
+ ** Often, sample i of each block of N effective samples has (i+1) fields.
+ ** Except, each sample may be extended to ensure that it is greater than or
+ ** equal to the previous sample in the array. For example, in the above,
+ ** sample 2 is the first sample of a block of N samples, so at first it
+ ** appears that it should be 1 field in size. However, that would make it
+ ** smaller than sample 1, so the binary search would not work. As a result,
+ ** it is extended to two fields. The duplicates that this creates do not
+ ** cause any problems.
+ */
+ nField = pRec->nField;
+ iCol = 0;
+ iSample = pIdx->nSample * nField;
+ do{
+ int iSamp; /* Index in aSample[] of test sample */
+ int n; /* Number of fields in test sample */
+
+ iTest = (iMin+iSample)/2;
+ iSamp = iTest / nField;
+ if( iSamp>0 ){
+ /* The proposed effective sample is a prefix of sample aSample[iSamp].
+ ** Specifically, the shortest prefix of at least (1 + iTest%nField)
+ ** fields that is greater than the previous effective sample. */
+ for(n=(iTest % nField) + 1; n<nField; n++){
+ if( aSample[iSamp-1].anLt[n-1]!=aSample[iSamp].anLt[n-1] ) break;
+ }
+ }else{
+ n = iTest + 1;
+ }
+
+ pRec->nField = n;
+ res = sqlite3VdbeRecordCompare(aSample[iSamp].n, aSample[iSamp].p, pRec);
+ if( res<0 ){
+ iLower = aSample[iSamp].anLt[n-1] + aSample[iSamp].anEq[n-1];
+ iMin = iTest+1;
+ }else if( res==0 && n<nField ){
+ iLower = aSample[iSamp].anLt[n-1];
+ iMin = iTest+1;
+ res = -1;
+ }else{
+ iSample = iTest;
+ iCol = n-1;
}
- }
+ }while( res && iMin<iSample );
+ i = iSample / nField;
- /* If there is an ORDER BY clause, and the selected virtual table index
- ** does not satisfy it, increase the cost of the scan accordingly. This
- ** matches the processing for non-virtual tables in bestBtreeIndex().
- */
- rCost = pIdxInfo->estimatedCost;
- if( pOrderBy && pIdxInfo->orderByConsumed==0 ){
- rCost += estLog(rCost)*rCost;
+#ifdef SQLITE_DEBUG
+ /* The following assert statements check that the binary search code
+ ** above found the right answer. This block serves no purpose other
+ ** than to invoke the asserts. */
+ if( pParse->db->mallocFailed==0 ){
+ if( res==0 ){
+ /* If (res==0) is true, then pRec must be equal to sample i. */
+ assert( i<pIdx->nSample );
+ assert( iCol==nField-1 );
+ pRec->nField = nField;
+ assert( 0==sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)
+ || pParse->db->mallocFailed
+ );
+ }else{
+ /* Unless i==pIdx->nSample, indicating that pRec is larger than
+ ** all samples in the aSample[] array, pRec must be smaller than the
+ ** (iCol+1) field prefix of sample i. */
+ assert( i<=pIdx->nSample && i>=0 );
+ pRec->nField = iCol+1;
+ assert( i==pIdx->nSample
+ || sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)>0
+ || pParse->db->mallocFailed );
+
+ /* if i==0 and iCol==0, then record pRec is smaller than all samples
+ ** in the aSample[] array. Otherwise, if (iCol>0) then pRec must
+ ** be greater than or equal to the (iCol) field prefix of sample i.
+ ** If (i>0), then pRec must also be greater than sample (i-1). */
+ if( iCol>0 ){
+ pRec->nField = iCol;
+ assert( sqlite3VdbeRecordCompare(aSample[i].n, aSample[i].p, pRec)<=0
+ || pParse->db->mallocFailed );
+ }
+ if( i>0 ){
+ pRec->nField = nField;
+ assert( sqlite3VdbeRecordCompare(aSample[i-1].n, aSample[i-1].p, pRec)<0
+ || pParse->db->mallocFailed );
+ }
+ }
}
+#endif /* ifdef SQLITE_DEBUG */
- /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the
- ** inital value of lowestCost in this loop. If it is, then the
- ** (cost<lowestCost) test below will never be true.
- **
- ** Use "(double)2" instead of "2.0" in case OMIT_FLOATING_POINT
- ** is defined.
- */
- if( (SQLITE_BIG_DBL/((double)2))<rCost ){
- pCost->rCost = (SQLITE_BIG_DBL/((double)2));
+ if( res==0 ){
+ /* Record pRec is equal to sample i */
+ assert( iCol==nField-1 );
+ aStat[0] = aSample[i].anLt[iCol];
+ aStat[1] = aSample[i].anEq[iCol];
}else{
- pCost->rCost = rCost;
- }
- pCost->plan.u.pVtabIdx = pIdxInfo;
- if( pIdxInfo->orderByConsumed ){
- pCost->plan.wsFlags |= WHERE_ORDERBY;
+ /* At this point, the (iCol+1) field prefix of aSample[i] is the first
+ ** sample that is greater than pRec. Or, if i==pIdx->nSample then pRec
+ ** is larger than all samples in the array. */
+ tRowcnt iUpper, iGap;
+ if( i>=pIdx->nSample ){
+ iUpper = sqlite3LogEstToInt(pIdx->aiRowLogEst[0]);
+ }else{
+ iUpper = aSample[i].anLt[iCol];
+ }
+
+ if( iLower>=iUpper ){
+ iGap = 0;
+ }else{
+ iGap = iUpper - iLower;
+ }
+ if( roundUp ){
+ iGap = (iGap*2)/3;
+ }else{
+ iGap = iGap/3;
+ }
+ aStat[0] = iLower + iGap;
+ aStat[1] = pIdx->aAvgEq[iCol];
}
- pCost->plan.nEq = 0;
- pIdxInfo->nOrderBy = nOrderBy;
- /* Try to find a more efficient access pattern by using multiple indexes
- ** to optimize an OR expression within the WHERE clause.
- */
- bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
+ /* Restore the pRec->nField value before returning. */
+ pRec->nField = nField;
+ return i;
}
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
-** Argument pIdx is a pointer to an index structure that has an array of
-** SQLITE_INDEX_SAMPLES evenly spaced samples of the first indexed column
-** stored in Index.aSample. These samples divide the domain of values stored
-** the index into (SQLITE_INDEX_SAMPLES+1) regions.
-** Region 0 contains all values less than the first sample value. Region
-** 1 contains values between the first and second samples. Region 2 contains
-** values between samples 2 and 3. And so on. Region SQLITE_INDEX_SAMPLES
-** contains values larger than the last sample.
-**
-** If the index contains many duplicates of a single value, then it is
-** possible that two or more adjacent samples can hold the same value.
-** When that is the case, the smallest possible region code is returned
-** when roundUp is false and the largest possible region code is returned
-** when roundUp is true.
-**
-** If successful, this function determines which of the regions value
-** pVal lies in, sets *piRegion to the region index (a value between 0
-** and SQLITE_INDEX_SAMPLES+1, inclusive) and returns SQLITE_OK.
-** Or, if an OOM occurs while converting text values between encodings,
-** SQLITE_NOMEM is returned and *piRegion is undefined.
-*/
-#ifdef SQLITE_ENABLE_STAT2
-static int whereRangeRegion(
- Parse *pParse, /* Database connection */
- Index *pIdx, /* Index to consider domain of */
- sqlite3_value *pVal, /* Value to consider */
- int roundUp, /* Return largest valid region if true */
- int *piRegion /* OUT: Region of domain in which value lies */
-){
- assert( roundUp==0 || roundUp==1 );
- if( ALWAYS(pVal) ){
- IndexSample *aSample = pIdx->aSample;
- int i = 0;
- int eType = sqlite3_value_type(pVal);
-
- if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
- double r = sqlite3_value_double(pVal);
- for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
- if( aSample[i].eType==SQLITE_NULL ) continue;
- if( aSample[i].eType>=SQLITE_TEXT ) break;
- if( roundUp ){
- if( aSample[i].u.r>r ) break;
- }else{
- if( aSample[i].u.r>=r ) break;
- }
- }
- }else if( eType==SQLITE_NULL ){
- i = 0;
- if( roundUp ){
- while( i<SQLITE_INDEX_SAMPLES && aSample[i].eType==SQLITE_NULL ) i++;
- }
- }else{
- sqlite3 *db = pParse->db;
- CollSeq *pColl;
- const u8 *z;
- int n;
-
- /* pVal comes from sqlite3ValueFromExpr() so the type cannot be NULL */
- assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
-
- if( eType==SQLITE_BLOB ){
- z = (const u8 *)sqlite3_value_blob(pVal);
- pColl = db->pDfltColl;
- assert( pColl->enc==SQLITE_UTF8 );
- }else{
- pColl = sqlite3GetCollSeq(db, SQLITE_UTF8, 0, *pIdx->azColl);
- if( pColl==0 ){
- sqlite3ErrorMsg(pParse, "no such collation sequence: %s",
- *pIdx->azColl);
- return SQLITE_ERROR;
- }
- z = (const u8 *)sqlite3ValueText(pVal, pColl->enc);
- if( !z ){
- return SQLITE_NOMEM;
- }
- assert( z && pColl && pColl->xCmp );
- }
- n = sqlite3ValueBytes(pVal, pColl->enc);
-
- for(i=0; i<SQLITE_INDEX_SAMPLES; i++){
- int c;
- int eSampletype = aSample[i].eType;
- if( eSampletype==SQLITE_NULL || eSampletype<eType ) continue;
- if( (eSampletype!=eType) ) break;
-#ifndef SQLITE_OMIT_UTF16
- if( pColl->enc!=SQLITE_UTF8 ){
- int nSample;
- char *zSample = sqlite3Utf8to16(
- db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample
- );
- if( !zSample ){
- assert( db->mallocFailed );
- return SQLITE_NOMEM;
- }
- c = pColl->xCmp(pColl->pUser, nSample, zSample, n, z);
- sqlite3DbFree(db, zSample);
- }else
-#endif
- {
- c = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z);
- }
- if( c-roundUp>=0 ) break;
- }
+** If it is not NULL, pTerm is a term that provides an upper or lower
+** bound on a range scan. Without considering pTerm, it is estimated
+** that the scan will visit nNew rows. This function returns the number
+** estimated to be visited after taking pTerm into account.
+**
+** If the user explicitly specified a likelihood() value for this term,
+** then the return value is the likelihood multiplied by the number of
+** input rows. Otherwise, this function assumes that an "IS NOT NULL" term
+** has a likelihood of 0.50, and any other term a likelihood of 0.25.
+*/
+static LogEst whereRangeAdjust(WhereTerm *pTerm, LogEst nNew){
+ LogEst nRet = nNew;
+ if( pTerm ){
+ if( pTerm->truthProb<=0 ){
+ nRet += pTerm->truthProb;
+ }else if( (pTerm->wtFlags & TERM_VNULL)==0 ){
+ nRet -= 20; assert( 20==sqlite3LogEst(4) );
}
-
- assert( i>=0 && i<=SQLITE_INDEX_SAMPLES );
- *piRegion = i;
}
- return SQLITE_OK;
+ return nRet;
}
-#endif /* #ifdef SQLITE_ENABLE_STAT2 */
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
-** If expression pExpr represents a literal value, set *pp to point to
-** an sqlite3_value structure containing the same value, with affinity
-** aff applied to it, before returning. It is the responsibility of the
-** caller to eventually release this structure by passing it to
-** sqlite3ValueFree().
+** Return the affinity for a single column of an index.
+*/
+SQLITE_PRIVATE char sqlite3IndexColumnAffinity(sqlite3 *db, Index *pIdx, int iCol){
+ assert( iCol>=0 && iCol<pIdx->nColumn );
+ if( !pIdx->zColAff ){
+ if( sqlite3IndexAffinityStr(db, pIdx)==0 ) return SQLITE_AFF_BLOB;
+ }
+ return pIdx->zColAff[iCol];
+}
+#endif
+
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+/*
+** This function is called to estimate the number of rows visited by a
+** range-scan on a skip-scan index. For example:
+**
+** CREATE INDEX i1 ON t1(a, b, c);
+** SELECT * FROM t1 WHERE a=? AND c BETWEEN ? AND ?;
+**
+** Value pLoop->nOut is currently set to the estimated number of rows
+** visited for scanning (a=? AND b=?). This function reduces that estimate
+** by some factor to account for the (c BETWEEN ? AND ?) expression based
+** on the stat4 data for the index. this scan will be peformed multiple
+** times (once for each (a,b) combination that matches a=?) is dealt with
+** by the caller.
+**
+** It does this by scanning through all stat4 samples, comparing values
+** extracted from pLower and pUpper with the corresponding column in each
+** sample. If L and U are the number of samples found to be less than or
+** equal to the values extracted from pLower and pUpper respectively, and
+** N is the total number of samples, the pLoop->nOut value is adjusted
+** as follows:
**
-** If the current parse is a recompile (sqlite3Reprepare()) and pExpr
-** is an SQL variable that currently has a non-NULL value bound to it,
-** create an sqlite3_value structure containing this value, again with
-** affinity aff applied to it, instead.
+** nOut = nOut * ( min(U - L, 1) / N )
**
-** If neither of the above apply, set *pp to NULL.
+** If pLower is NULL, or a value cannot be extracted from the term, L is
+** set to zero. If pUpper is NULL, or a value cannot be extracted from it,
+** U is set to N.
**
-** If an error occurs, return an error code. Otherwise, SQLITE_OK.
+** Normally, this function sets *pbDone to 1 before returning. However,
+** if no value can be extracted from either pLower or pUpper (and so the
+** estimate of the number of rows delivered remains unchanged), *pbDone
+** is left as is.
+**
+** If an error occurs, an SQLite error code is returned. Otherwise,
+** SQLITE_OK.
*/
-#ifdef SQLITE_ENABLE_STAT2
-static int valueFromExpr(
- Parse *pParse,
- Expr *pExpr,
- u8 aff,
- sqlite3_value **pp
+static int whereRangeSkipScanEst(
+ Parse *pParse, /* Parsing & code generating context */
+ WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
+ WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
+ WhereLoop *pLoop, /* Update the .nOut value of this loop */
+ int *pbDone /* Set to true if at least one expr. value extracted */
){
- if( pExpr->op==TK_VARIABLE
- || (pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE)
- ){
- int iVar = pExpr->iColumn;
- sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); /* IMP: R-23257-02778 */
- *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff);
- return SQLITE_OK;
+ Index *p = pLoop->u.btree.pIndex;
+ int nEq = pLoop->u.btree.nEq;
+ sqlite3 *db = pParse->db;
+ int nLower = -1;
+ int nUpper = p->nSample+1;
+ int rc = SQLITE_OK;
+ u8 aff = sqlite3IndexColumnAffinity(db, p, nEq);
+ CollSeq *pColl;
+
+ sqlite3_value *p1 = 0; /* Value extracted from pLower */
+ sqlite3_value *p2 = 0; /* Value extracted from pUpper */
+ sqlite3_value *pVal = 0; /* Value extracted from record */
+
+ pColl = sqlite3LocateCollSeq(pParse, p->azColl[nEq]);
+ if( pLower ){
+ rc = sqlite3Stat4ValueFromExpr(pParse, pLower->pExpr->pRight, aff, &p1);
+ nLower = 0;
+ }
+ if( pUpper && rc==SQLITE_OK ){
+ rc = sqlite3Stat4ValueFromExpr(pParse, pUpper->pExpr->pRight, aff, &p2);
+ nUpper = p2 ? 0 : p->nSample;
}
- return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp);
+
+ if( p1 || p2 ){
+ int i;
+ int nDiff;
+ for(i=0; rc==SQLITE_OK && i<p->nSample; i++){
+ rc = sqlite3Stat4Column(db, p->aSample[i].p, p->aSample[i].n, nEq, &pVal);
+ if( rc==SQLITE_OK && p1 ){
+ int res = sqlite3MemCompare(p1, pVal, pColl);
+ if( res>=0 ) nLower++;
+ }
+ if( rc==SQLITE_OK && p2 ){
+ int res = sqlite3MemCompare(p2, pVal, pColl);
+ if( res>=0 ) nUpper++;
+ }
+ }
+ nDiff = (nUpper - nLower);
+ if( nDiff<=0 ) nDiff = 1;
+
+ /* If there is both an upper and lower bound specified, and the
+ ** comparisons indicate that they are close together, use the fallback
+ ** method (assume that the scan visits 1/64 of the rows) for estimating
+ ** the number of rows visited. Otherwise, estimate the number of rows
+ ** using the method described in the header comment for this function. */
+ if( nDiff!=1 || pUpper==0 || pLower==0 ){
+ int nAdjust = (sqlite3LogEst(p->nSample) - sqlite3LogEst(nDiff));
+ pLoop->nOut -= nAdjust;
+ *pbDone = 1;
+ WHERETRACE(0x10, ("range skip-scan regions: %u..%u adjust=%d est=%d\n",
+ nLower, nUpper, nAdjust*-1, pLoop->nOut));
+ }
+
+ }else{
+ assert( *pbDone==0 );
+ }
+
+ sqlite3ValueFree(p1);
+ sqlite3ValueFree(p2);
+ sqlite3ValueFree(pVal);
+
+ return rc;
}
-#endif
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
/*
** This function is used to estimate the number of rows that will be visited
@@ -102935,116 +129157,198 @@ static int valueFromExpr(
** If either of the upper or lower bound is not present, then NULL is passed in
** place of the corresponding WhereTerm.
**
-** The nEq parameter is passed the index of the index column subject to the
-** range constraint. Or, equivalently, the number of equality constraints
-** optimized by the proposed index scan. For example, assuming index p is
-** on t1(a, b), and the SQL query is:
+** The value in (pBuilder->pNew->u.btree.nEq) is the number of the index
+** column subject to the range constraint. Or, equivalently, the number of
+** equality constraints optimized by the proposed index scan. For example,
+** assuming index p is on t1(a, b), and the SQL query is:
**
** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ...
**
-** then nEq should be passed the value 1 (as the range restricted column,
-** b, is the second left-most column of the index). Or, if the query is:
+** then nEq is set to 1 (as the range restricted column, b, is the second
+** left-most column of the index). Or, if the query is:
**
** ... FROM t1 WHERE a > ? AND a < ? ...
**
-** then nEq should be passed 0.
-**
-** The returned value is an integer between 1 and 100, inclusive. A return
-** value of 1 indicates that the proposed range scan is expected to visit
-** approximately 1/100th (1%) of the rows selected by the nEq equality
-** constraints (if any). A return value of 100 indicates that it is expected
-** that the range scan will visit every row (100%) selected by the equality
-** constraints.
+** then nEq is set to 0.
**
-** In the absence of sqlite_stat2 ANALYZE data, each range inequality
-** reduces the search space by 3/4ths. Hence a single constraint (x>?)
-** results in a return of 25 and a range constraint (x>? AND x<?) results
-** in a return of 6.
+** When this function is called, *pnOut is set to the sqlite3LogEst() of the
+** number of rows that the index scan is expected to visit without
+** considering the range constraints. If nEq is 0, then *pnOut is the number of
+** rows in the index. Assuming no error occurs, *pnOut is adjusted (reduced)
+** to account for the range constraints pLower and pUpper.
+**
+** In the absence of sqlite_stat4 ANALYZE data, or if such data cannot be
+** used, a single range inequality reduces the search space by a factor of 4.
+** and a pair of constraints (x>? AND x<?) reduces the expected number of
+** rows visited by a factor of 64.
*/
static int whereRangeScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index containing the range-compared column; "x" */
- int nEq, /* index into p->aCol[] of the range-compared column */
+ WhereLoopBuilder *pBuilder,
WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */
WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */
- int *piEst /* OUT: Return value */
+ WhereLoop *pLoop /* Modify the .nOut and maybe .rRun fields */
){
int rc = SQLITE_OK;
+ int nOut = pLoop->nOut;
+ LogEst nNew;
+
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ Index *p = pLoop->u.btree.pIndex;
+ int nEq = pLoop->u.btree.nEq;
+
+ if( p->nSample>0 && nEq<p->nSampleCol ){
+ if( nEq==pBuilder->nRecValid ){
+ UnpackedRecord *pRec = pBuilder->pRec;
+ tRowcnt a[2];
+ int nBtm = pLoop->u.btree.nBtm;
+ int nTop = pLoop->u.btree.nTop;
+
+ /* Variable iLower will be set to the estimate of the number of rows in
+ ** the index that are less than the lower bound of the range query. The
+ ** lower bound being the concatenation of $P and $L, where $P is the
+ ** key-prefix formed by the nEq values matched against the nEq left-most
+ ** columns of the index, and $L is the value in pLower.
+ **
+ ** Or, if pLower is NULL or $L cannot be extracted from it (because it
+ ** is not a simple variable or literal value), the lower bound of the
+ ** range is $P. Due to a quirk in the way whereKeyStats() works, even
+ ** if $L is available, whereKeyStats() is called for both ($P) and
+ ** ($P:$L) and the larger of the two returned values is used.
+ **
+ ** Similarly, iUpper is to be set to the estimate of the number of rows
+ ** less than the upper bound of the range query. Where the upper bound
+ ** is either ($P) or ($P:$U). Again, even if $U is available, both values
+ ** of iUpper are requested of whereKeyStats() and the smaller used.
+ **
+ ** The number of rows between the two bounds is then just iUpper-iLower.
+ */
+ tRowcnt iLower; /* Rows less than the lower bound */
+ tRowcnt iUpper; /* Rows less than the upper bound */
+ int iLwrIdx = -2; /* aSample[] for the lower bound */
+ int iUprIdx = -1; /* aSample[] for the upper bound */
+
+ if( pRec ){
+ testcase( pRec->nField!=pBuilder->nRecValid );
+ pRec->nField = pBuilder->nRecValid;
+ }
+ /* Determine iLower and iUpper using ($P) only. */
+ if( nEq==0 ){
+ iLower = 0;
+ iUpper = p->nRowEst0;
+ }else{
+ /* Note: this call could be optimized away - since the same values must
+ ** have been requested when testing key $P in whereEqualScanEst(). */
+ whereKeyStats(pParse, p, pRec, 0, a);
+ iLower = a[0];
+ iUpper = a[0] + a[1];
+ }
+
+ assert( pLower==0 || (pLower->eOperator & (WO_GT|WO_GE))!=0 );
+ assert( pUpper==0 || (pUpper->eOperator & (WO_LT|WO_LE))!=0 );
+ assert( p->aSortOrder!=0 );
+ if( p->aSortOrder[nEq] ){
+ /* The roles of pLower and pUpper are swapped for a DESC index */
+ SWAP(WhereTerm*, pLower, pUpper);
+ SWAP(int, nBtm, nTop);
+ }
+
+ /* If possible, improve on the iLower estimate using ($P:$L). */
+ if( pLower ){
+ int n; /* Values extracted from pExpr */
+ Expr *pExpr = pLower->pExpr->pRight;
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nBtm, nEq, &n);
+ if( rc==SQLITE_OK && n ){
+ tRowcnt iNew;
+ u16 mask = WO_GT|WO_LE;
+ if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
+ iLwrIdx = whereKeyStats(pParse, p, pRec, 0, a);
+ iNew = a[0] + ((pLower->eOperator & mask) ? a[1] : 0);
+ if( iNew>iLower ) iLower = iNew;
+ nOut--;
+ pLower = 0;
+ }
+ }
-#ifdef SQLITE_ENABLE_STAT2
-
- if( nEq==0 && p->aSample ){
- sqlite3_value *pLowerVal = 0;
- sqlite3_value *pUpperVal = 0;
- int iEst;
- int iLower = 0;
- int iUpper = SQLITE_INDEX_SAMPLES;
- int roundUpUpper = 0;
- int roundUpLower = 0;
- u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity;
-
- if( pLower ){
- Expr *pExpr = pLower->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal);
- assert( pLower->eOperator==WO_GT || pLower->eOperator==WO_GE );
- roundUpLower = (pLower->eOperator==WO_GT) ?1:0;
- }
- if( rc==SQLITE_OK && pUpper ){
- Expr *pExpr = pUpper->pExpr->pRight;
- rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal);
- assert( pUpper->eOperator==WO_LT || pUpper->eOperator==WO_LE );
- roundUpUpper = (pUpper->eOperator==WO_LE) ?1:0;
- }
-
- if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){
- sqlite3ValueFree(pLowerVal);
- sqlite3ValueFree(pUpperVal);
- goto range_est_fallback;
- }else if( pLowerVal==0 ){
- rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
- if( pLower ) iLower = iUpper/2;
- }else if( pUpperVal==0 ){
- rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
- if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2;
- }else{
- rc = whereRangeRegion(pParse, p, pUpperVal, roundUpUpper, &iUpper);
- if( rc==SQLITE_OK ){
- rc = whereRangeRegion(pParse, p, pLowerVal, roundUpLower, &iLower);
+ /* If possible, improve on the iUpper estimate using ($P:$U). */
+ if( pUpper ){
+ int n; /* Values extracted from pExpr */
+ Expr *pExpr = pUpper->pExpr->pRight;
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, nTop, nEq, &n);
+ if( rc==SQLITE_OK && n ){
+ tRowcnt iNew;
+ u16 mask = WO_GT|WO_LE;
+ if( sqlite3ExprVectorSize(pExpr)>n ) mask = (WO_LE|WO_LT);
+ iUprIdx = whereKeyStats(pParse, p, pRec, 1, a);
+ iNew = a[0] + ((pUpper->eOperator & mask) ? a[1] : 0);
+ if( iNew<iUpper ) iUpper = iNew;
+ nOut--;
+ pUpper = 0;
+ }
}
- }
- WHERETRACE(("range scan regions: %d..%d\n", iLower, iUpper));
- iEst = iUpper - iLower;
- testcase( iEst==SQLITE_INDEX_SAMPLES );
- assert( iEst<=SQLITE_INDEX_SAMPLES );
- if( iEst<1 ){
- *piEst = 50/SQLITE_INDEX_SAMPLES;
+ pBuilder->pRec = pRec;
+ if( rc==SQLITE_OK ){
+ if( iUpper>iLower ){
+ nNew = sqlite3LogEst(iUpper - iLower);
+ /* TUNING: If both iUpper and iLower are derived from the same
+ ** sample, then assume they are 4x more selective. This brings
+ ** the estimated selectivity more in line with what it would be
+ ** if estimated without the use of STAT3/4 tables. */
+ if( iLwrIdx==iUprIdx ) nNew -= 20; assert( 20==sqlite3LogEst(4) );
+ }else{
+ nNew = 10; assert( 10==sqlite3LogEst(2) );
+ }
+ if( nNew<nOut ){
+ nOut = nNew;
+ }
+ WHERETRACE(0x10, ("STAT4 range scan: %u..%u est=%d\n",
+ (u32)iLower, (u32)iUpper, nOut));
+ }
}else{
- *piEst = (iEst*100)/SQLITE_INDEX_SAMPLES;
+ int bDone = 0;
+ rc = whereRangeSkipScanEst(pParse, pLower, pUpper, pLoop, &bDone);
+ if( bDone ) return rc;
}
- sqlite3ValueFree(pLowerVal);
- sqlite3ValueFree(pUpperVal);
- return rc;
}
-range_est_fallback:
#else
UNUSED_PARAMETER(pParse);
- UNUSED_PARAMETER(p);
- UNUSED_PARAMETER(nEq);
-#endif
+ UNUSED_PARAMETER(pBuilder);
assert( pLower || pUpper );
- *piEst = 100;
- if( pLower && (pLower->wtFlags & TERM_VNULL)==0 ) *piEst /= 4;
- if( pUpper ) *piEst /= 4;
+#endif
+ assert( pUpper==0 || (pUpper->wtFlags & TERM_VNULL)==0 );
+ nNew = whereRangeAdjust(pLower, nOut);
+ nNew = whereRangeAdjust(pUpper, nNew);
+
+ /* TUNING: If there is both an upper and lower limit and neither limit
+ ** has an application-defined likelihood(), assume the range is
+ ** reduced by an additional 75%. This means that, by default, an open-ended
+ ** range query (e.g. col > ?) is assumed to match 1/4 of the rows in the
+ ** index. While a closed range (e.g. col BETWEEN ? AND ?) is estimated to
+ ** match 1/64 of the index. */
+ if( pLower && pLower->truthProb>0 && pUpper && pUpper->truthProb>0 ){
+ nNew -= 20;
+ }
+
+ nOut -= (pLower!=0) + (pUpper!=0);
+ if( nNew<10 ) nNew = 10;
+ if( nNew<nOut ) nOut = nNew;
+#if defined(WHERETRACE_ENABLED)
+ if( pLoop->nOut>nOut ){
+ WHERETRACE(0x10,("Range scan lowers nOut from %d to %d\n",
+ pLoop->nOut, nOut));
+ }
+#endif
+ pLoop->nOut = (LogEst)nOut;
return rc;
}
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the number of rows that will be returned based on
** an equality constraint x=VALUE and where that VALUE occurs in
** the histogram data. This only works when x is the left-most
-** column of an index and sqlite_stat2 histogram data is available
+** column of an index and sqlite_stat3 histogram data is available
** for that index. When pExpr==NULL that means the constraint is
** "x IS NULL" instead of "x=VALUE".
**
@@ -103059,45 +129363,52 @@ range_est_fallback:
*/
static int whereEqualScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index whose left-most column is pTerm */
+ WhereLoopBuilder *pBuilder,
Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */
- double *pnRow /* Write the revised row estimate here */
+ tRowcnt *pnRow /* Write the revised row estimate here */
){
- sqlite3_value *pRhs = 0; /* VALUE on right-hand side of pTerm */
- int iLower, iUpper; /* Range of histogram regions containing pRhs */
- u8 aff; /* Column affinity */
+ Index *p = pBuilder->pNew->u.btree.pIndex;
+ int nEq = pBuilder->pNew->u.btree.nEq;
+ UnpackedRecord *pRec = pBuilder->pRec;
int rc; /* Subfunction return code */
- double nRowEst; /* New estimate of the number of rows */
+ tRowcnt a[2]; /* Statistics */
+ int bOk;
+ assert( nEq>=1 );
+ assert( nEq<=p->nColumn );
assert( p->aSample!=0 );
- aff = p->pTable->aCol[p->aiColumn[0]].affinity;
- if( pExpr ){
- rc = valueFromExpr(pParse, pExpr, aff, &pRhs);
- if( rc ) goto whereEqualScanEst_cancel;
- }else{
- pRhs = sqlite3ValueNew(pParse->db);
- }
- if( pRhs==0 ) return SQLITE_NOTFOUND;
- rc = whereRangeRegion(pParse, p, pRhs, 0, &iLower);
- if( rc ) goto whereEqualScanEst_cancel;
- rc = whereRangeRegion(pParse, p, pRhs, 1, &iUpper);
- if( rc ) goto whereEqualScanEst_cancel;
- WHERETRACE(("equality scan regions: %d..%d\n", iLower, iUpper));
- if( iLower>=iUpper ){
- nRowEst = p->aiRowEst[0]/(SQLITE_INDEX_SAMPLES*2);
- if( nRowEst<*pnRow ) *pnRow = nRowEst;
- }else{
- nRowEst = (iUpper-iLower)*p->aiRowEst[0]/SQLITE_INDEX_SAMPLES;
- *pnRow = nRowEst;
+ assert( p->nSample>0 );
+ assert( pBuilder->nRecValid<nEq );
+
+ /* If values are not available for all fields of the index to the left
+ ** of this one, no estimate can be made. Return SQLITE_NOTFOUND. */
+ if( pBuilder->nRecValid<(nEq-1) ){
+ return SQLITE_NOTFOUND;
+ }
+
+ /* This is an optimization only. The call to sqlite3Stat4ProbeSetValue()
+ ** below would return the same value. */
+ if( nEq>=p->nColumn ){
+ *pnRow = 1;
+ return SQLITE_OK;
}
-whereEqualScanEst_cancel:
- sqlite3ValueFree(pRhs);
+ rc = sqlite3Stat4ProbeSetValue(pParse, p, &pRec, pExpr, 1, nEq-1, &bOk);
+ pBuilder->pRec = pRec;
+ if( rc!=SQLITE_OK ) return rc;
+ if( bOk==0 ) return SQLITE_NOTFOUND;
+ pBuilder->nRecValid = nEq;
+
+ whereKeyStats(pParse, p, pRec, 0, a);
+ WHERETRACE(0x10,("equality scan regions %s(%d): %d\n",
+ p->zName, nEq-1, (int)a[1]));
+ *pnRow = a[1];
+
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT2) */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
-#ifdef SQLITE_ENABLE_STAT2
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
/*
** Estimate the number of rows that will be returned based on
** an IN constraint where the right-hand side of the IN operator
@@ -103116,1696 +129427,2638 @@ whereEqualScanEst_cancel:
*/
static int whereInScanEst(
Parse *pParse, /* Parsing & code generating context */
- Index *p, /* The index whose left-most column is pTerm */
+ WhereLoopBuilder *pBuilder,
ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */
- double *pnRow /* Write the revised row estimate here */
-){
- sqlite3_value *pVal = 0; /* One value from list */
- int iLower, iUpper; /* Range of histogram regions containing pRhs */
- u8 aff; /* Column affinity */
- int rc = SQLITE_OK; /* Subfunction return code */
- double nRowEst; /* New estimate of the number of rows */
- int nSpan = 0; /* Number of histogram regions spanned */
- int nSingle = 0; /* Histogram regions hit by a single value */
- int nNotFound = 0; /* Count of values that are not constants */
- int i; /* Loop counter */
- u8 aSpan[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions that are spanned */
- u8 aSingle[SQLITE_INDEX_SAMPLES+1]; /* Histogram regions hit once */
+ tRowcnt *pnRow /* Write the revised row estimate here */
+){
+ Index *p = pBuilder->pNew->u.btree.pIndex;
+ i64 nRow0 = sqlite3LogEstToInt(p->aiRowLogEst[0]);
+ int nRecValid = pBuilder->nRecValid;
+ int rc = SQLITE_OK; /* Subfunction return code */
+ tRowcnt nEst; /* Number of rows for a single term */
+ tRowcnt nRowEst = 0; /* New estimate of the number of rows */
+ int i; /* Loop counter */
assert( p->aSample!=0 );
- aff = p->pTable->aCol[p->aiColumn[0]].affinity;
- memset(aSpan, 0, sizeof(aSpan));
- memset(aSingle, 0, sizeof(aSingle));
- for(i=0; i<pList->nExpr; i++){
- sqlite3ValueFree(pVal);
- rc = valueFromExpr(pParse, pList->a[i].pExpr, aff, &pVal);
- if( rc ) break;
- if( pVal==0 || sqlite3_value_type(pVal)==SQLITE_NULL ){
- nNotFound++;
- continue;
- }
- rc = whereRangeRegion(pParse, p, pVal, 0, &iLower);
- if( rc ) break;
- rc = whereRangeRegion(pParse, p, pVal, 1, &iUpper);
- if( rc ) break;
- if( iLower>=iUpper ){
- aSingle[iLower] = 1;
- }else{
- assert( iLower>=0 && iUpper<=SQLITE_INDEX_SAMPLES );
- while( iLower<iUpper ) aSpan[iLower++] = 1;
- }
+ for(i=0; rc==SQLITE_OK && i<pList->nExpr; i++){
+ nEst = nRow0;
+ rc = whereEqualScanEst(pParse, pBuilder, pList->a[i].pExpr, &nEst);
+ nRowEst += nEst;
+ pBuilder->nRecValid = nRecValid;
}
+
if( rc==SQLITE_OK ){
- for(i=nSpan=0; i<=SQLITE_INDEX_SAMPLES; i++){
- if( aSpan[i] ){
- nSpan++;
- }else if( aSingle[i] ){
- nSingle++;
- }
- }
- nRowEst = (nSpan*2+nSingle)*p->aiRowEst[0]/(2*SQLITE_INDEX_SAMPLES)
- + nNotFound*p->aiRowEst[1];
- if( nRowEst > p->aiRowEst[0] ) nRowEst = p->aiRowEst[0];
+ if( nRowEst > nRow0 ) nRowEst = nRow0;
*pnRow = nRowEst;
- WHERETRACE(("IN row estimate: nSpan=%d, nSingle=%d, nNotFound=%d, est=%g\n",
- nSpan, nSingle, nNotFound, nRowEst));
+ WHERETRACE(0x10,("IN row estimate: est=%d\n", nRowEst));
}
- sqlite3ValueFree(pVal);
+ assert( pBuilder->nRecValid==nRecValid );
return rc;
}
-#endif /* defined(SQLITE_ENABLE_STAT2) */
+#endif /* SQLITE_ENABLE_STAT3_OR_STAT4 */
+#ifdef WHERETRACE_ENABLED
/*
-** Find the best query plan for accessing a particular table. Write the
-** best query plan and its cost into the WhereCost object supplied as the
-** last parameter.
-**
-** The lowest cost plan wins. The cost is an estimate of the amount of
-** CPU and disk I/O needed to process the requested result.
-** Factors that influence cost include:
-**
-** * The estimated number of rows that will be retrieved. (The
-** fewer the better.)
-**
-** * Whether or not sorting must occur.
-**
-** * Whether or not there must be separate lookups in the
-** index and in the main table.
-**
-** If there was an INDEXED BY clause (pSrc->pIndex) attached to the table in
-** the SQL statement, then this function only considers plans using the
-** named index. If no such plan is found, then the returned cost is
-** SQLITE_BIG_DBL. If a plan is found that uses the named index,
-** then the cost is calculated in the usual way.
-**
-** If a NOT INDEXED clause (pSrc->notIndexed!=0) was attached to the table
-** in the SELECT statement, then no indexes are considered. However, the
-** selected plan may still take advantage of the built-in rowid primary key
-** index.
+** Print the content of a WhereTerm object
*/
-static void bestBtreeIndex(
- Parse *pParse, /* The parsing context */
- WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to search */
- Bitmask notReady, /* Mask of cursors not available for indexing */
- Bitmask notValid, /* Cursors not available for any purpose */
- ExprList *pOrderBy, /* The ORDER BY clause */
- ExprList *pDistinct, /* The select-list if query is DISTINCT */
- WhereCost *pCost /* Lowest cost query plan */
-){
- int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */
- Index *pProbe; /* An index we are evaluating */
- Index *pIdx; /* Copy of pProbe, or zero for IPK index */
- int eqTermMask; /* Current mask of valid equality operators */
- int idxEqTermMask; /* Index mask of valid equality operators */
- Index sPk; /* A fake index object for the primary key */
- unsigned int aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */
- int aiColumnPk = -1; /* The aColumn[] value for the sPk index */
- int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */
-
- /* Initialize the cost to a worst-case value */
- memset(pCost, 0, sizeof(*pCost));
- pCost->rCost = SQLITE_BIG_DBL;
-
- /* If the pSrc table is the right table of a LEFT JOIN then we may not
- ** use an index to satisfy IS NULL constraints on that table. This is
- ** because columns might end up being NULL if the table does not match -
- ** a circumstance which the index cannot help us discover. Ticket #2177.
- */
- if( pSrc->jointype & JT_LEFT ){
- idxEqTermMask = WO_EQ|WO_IN;
- }else{
- idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL;
- }
-
- if( pSrc->pIndex ){
- /* An INDEXED BY clause specifies a particular index to use */
- pIdx = pProbe = pSrc->pIndex;
- wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE);
- eqTermMask = idxEqTermMask;
+static void whereTermPrint(WhereTerm *pTerm, int iTerm){
+ if( pTerm==0 ){
+ sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm);
}else{
- /* There is no INDEXED BY clause. Create a fake Index object in local
- ** variable sPk to represent the rowid primary key index. Make this
- ** fake index the first in a chain of Index objects with all of the real
- ** indices to follow */
- Index *pFirst; /* First of real indices on the table */
- memset(&sPk, 0, sizeof(Index));
- sPk.nColumn = 1;
- sPk.aiColumn = &aiColumnPk;
- sPk.aiRowEst = aiRowEstPk;
- sPk.onError = OE_Replace;
- sPk.pTable = pSrc->pTab;
- aiRowEstPk[0] = pSrc->pTab->nRowEst;
- aiRowEstPk[1] = 1;
- pFirst = pSrc->pTab->pIndex;
- if( pSrc->notIndexed==0 ){
- /* The real indices of the table are only considered if the
- ** NOT INDEXED qualifier is omitted from the FROM clause */
- sPk.pNext = pFirst;
+ char zType[4];
+ char zLeft[50];
+ memcpy(zType, "...", 4);
+ if( pTerm->wtFlags & TERM_VIRTUAL ) zType[0] = 'V';
+ if( pTerm->eOperator & WO_EQUIV ) zType[1] = 'E';
+ if( ExprHasProperty(pTerm->pExpr, EP_FromJoin) ) zType[2] = 'L';
+ if( pTerm->eOperator & WO_SINGLE ){
+ sqlite3_snprintf(sizeof(zLeft),zLeft,"left={%d:%d}",
+ pTerm->leftCursor, pTerm->u.leftColumn);
+ }else if( (pTerm->eOperator & WO_OR)!=0 && pTerm->u.pOrInfo!=0 ){
+ sqlite3_snprintf(sizeof(zLeft),zLeft,"indexable=0x%lld",
+ pTerm->u.pOrInfo->indexable);
+ }else{
+ sqlite3_snprintf(sizeof(zLeft),zLeft,"left=%d", pTerm->leftCursor);
+ }
+ sqlite3DebugPrintf(
+ "TERM-%-3d %p %s %-12s prob=%-3d op=0x%03x wtFlags=0x%04x",
+ iTerm, pTerm, zType, zLeft, pTerm->truthProb,
+ pTerm->eOperator, pTerm->wtFlags);
+ if( pTerm->iField ){
+ sqlite3DebugPrintf(" iField=%d\n", pTerm->iField);
+ }else{
+ sqlite3DebugPrintf("\n");
}
- pProbe = &sPk;
- wsFlagMask = ~(
- WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE
- );
- eqTermMask = WO_EQ|WO_IN;
- pIdx = 0;
+ sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
+}
+#endif
- /* Loop over all indices looking for the best one to use
- */
- for(; pProbe; pIdx=pProbe=pProbe->pNext){
- const unsigned int * const aiRowEst = pProbe->aiRowEst;
- double cost; /* Cost of using pProbe */
- double nRow; /* Estimated number of rows in result set */
- double log10N; /* base-10 logarithm of nRow (inexact) */
- int rev; /* True to scan in reverse order */
- int wsFlags = 0;
- Bitmask used = 0;
-
- /* The following variables are populated based on the properties of
- ** index being evaluated. They are then used to determine the expected
- ** cost and number of rows returned.
- **
- ** nEq:
- ** Number of equality terms that can be implemented using the index.
- ** In other words, the number of initial fields in the index that
- ** are used in == or IN or NOT NULL constraints of the WHERE clause.
- **
- ** nInMul:
- ** The "in-multiplier". This is an estimate of how many seek operations
- ** SQLite must perform on the index in question. For example, if the
- ** WHERE clause is:
- **
- ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6)
- **
- ** SQLite must perform 9 lookups on an index on (a, b), so nInMul is
- ** set to 9. Given the same schema and either of the following WHERE
- ** clauses:
- **
- ** WHERE a = 1
- ** WHERE a >= 2
- **
- ** nInMul is set to 1.
- **
- ** If there exists a WHERE term of the form "x IN (SELECT ...)", then
- ** the sub-select is assumed to return 25 rows for the purposes of
- ** determining nInMul.
- **
- ** bInEst:
- ** Set to true if there was at least one "x IN (SELECT ...)" term used
- ** in determining the value of nInMul. Note that the RHS of the
- ** IN operator must be a SELECT, not a value list, for this variable
- ** to be true.
- **
- ** estBound:
- ** An estimate on the amount of the table that must be searched. A
- ** value of 100 means the entire table is searched. Range constraints
- ** might reduce this to a value less than 100 to indicate that only
- ** a fraction of the table needs searching. In the absence of
- ** sqlite_stat2 ANALYZE data, a single inequality reduces the search
- ** space to 1/4rd its original size. So an x>? constraint reduces
- ** estBound to 25. Two constraints (x>? AND x<?) reduce estBound to 6.
- **
- ** bSort:
- ** Boolean. True if there is an ORDER BY clause that will require an
- ** external sort (i.e. scanning the index being evaluated will not
- ** correctly order records).
- **
- ** bLookup:
- ** Boolean. True if a table lookup is required for each index entry
- ** visited. In other words, true if this is not a covering index.
- ** This is always false for the rowid primary key index of a table.
- ** For other indexes, it is true unless all the columns of the table
- ** used by the SELECT statement are present in the index (such an
- ** index is sometimes described as a covering index).
- ** For example, given the index on (a, b), the second of the following
- ** two queries requires table b-tree lookups in order to find the value
- ** of column c, but the first does not because columns a and b are
- ** both available in the index.
- **
- ** SELECT a, b FROM tbl WHERE a = 1;
- ** SELECT a, b, c FROM tbl WHERE a = 1;
- */
- int nEq; /* Number of == or IN terms matching index */
- int bInEst = 0; /* True if "x IN (SELECT...)" seen */
- int nInMul = 1; /* Number of distinct equalities to lookup */
- int estBound = 100; /* Estimated reduction in search space */
- int nBound = 0; /* Number of range constraints seen */
- int bSort = !!pOrderBy; /* True if external sort required */
- int bDist = !!pDistinct; /* True if index cannot help with DISTINCT */
- int bLookup = 0; /* True if not a covering index */
- WhereTerm *pTerm; /* A single term of the WHERE clause */
-#ifdef SQLITE_ENABLE_STAT2
- WhereTerm *pFirstTerm = 0; /* First term matching the index */
-#endif
-
- /* Determine the values of nEq and nInMul */
- for(nEq=0; nEq<pProbe->nColumn; nEq++){
- int j = pProbe->aiColumn[nEq];
- pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx);
- if( pTerm==0 ) break;
- wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ);
- if( pTerm->eOperator & WO_IN ){
- Expr *pExpr = pTerm->pExpr;
- wsFlags |= WHERE_COLUMN_IN;
- if( ExprHasProperty(pExpr, EP_xIsSelect) ){
- /* "x IN (SELECT ...)": Assume the SELECT returns 25 rows */
- nInMul *= 25;
- bInEst = 1;
- }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
- /* "x IN (value, value, ...)" */
- nInMul *= pExpr->x.pList->nExpr;
- }
- }else if( pTerm->eOperator & WO_ISNULL ){
- wsFlags |= WHERE_COLUMN_NULL;
- }
-#ifdef SQLITE_ENABLE_STAT2
- if( nEq==0 && pProbe->aSample ) pFirstTerm = pTerm;
-#endif
- used |= pTerm->prereqRight;
- }
-
- /* Determine the value of estBound. */
- if( nEq<pProbe->nColumn && pProbe->bUnordered==0 ){
- int j = pProbe->aiColumn[nEq];
- if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){
- WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx);
- WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx);
- whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound);
- if( pTop ){
- nBound = 1;
- wsFlags |= WHERE_TOP_LIMIT;
- used |= pTop->prereqRight;
- }
- if( pBtm ){
- nBound++;
- wsFlags |= WHERE_BTM_LIMIT;
- used |= pBtm->prereqRight;
- }
- wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE);
- }
- }else if( pProbe->onError!=OE_None ){
- testcase( wsFlags & WHERE_COLUMN_IN );
- testcase( wsFlags & WHERE_COLUMN_NULL );
- if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){
- wsFlags |= WHERE_UNIQUE;
- }
- }
-
- /* If there is an ORDER BY clause and the index being considered will
- ** naturally scan rows in the required order, set the appropriate flags
- ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index
- ** will scan rows in a different order, set the bSort variable. */
- if( isSortingIndex(
- pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, nEq, wsFlags, &rev)
- ){
- bSort = 0;
- wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY;
- wsFlags |= (rev ? WHERE_REVERSE : 0);
- }
-
- /* If there is a DISTINCT qualifier and this index will scan rows in
- ** order of the DISTINCT expressions, clear bDist and set the appropriate
- ** flags in wsFlags. */
- if( isDistinctIndex(pParse, pWC, pProbe, iCur, pDistinct, nEq) ){
- bDist = 0;
- wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_DISTINCT;
- }
-
- /* If currently calculating the cost of using an index (not the IPK
- ** index), determine if all required column data may be obtained without
- ** using the main table (i.e. if the index is a covering
- ** index for this query). If it is, set the WHERE_IDX_ONLY flag in
- ** wsFlags. Otherwise, set the bLookup variable to true. */
- if( pIdx && wsFlags ){
- Bitmask m = pSrc->colUsed;
- int j;
- for(j=0; j<pIdx->nColumn; j++){
- int x = pIdx->aiColumn[j];
- if( x<BMS-1 ){
- m &= ~(((Bitmask)1)<<x);
- }
- }
- if( m==0 ){
- wsFlags |= WHERE_IDX_ONLY;
- }else{
- bLookup = 1;
- }
- }
+#ifdef WHERETRACE_ENABLED
+/*
+** Show the complete content of a WhereClause
+*/
+SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){
+ int i;
+ for(i=0; i<pWC->nTerm; i++){
+ whereTermPrint(&pWC->a[i], i);
+ }
+}
+#endif
- /*
- ** Estimate the number of rows of output. For an "x IN (SELECT...)"
- ** constraint, do not let the estimate exceed half the rows in the table.
- */
- nRow = (double)(aiRowEst[nEq] * nInMul);
- if( bInEst && nRow*2>aiRowEst[0] ){
- nRow = aiRowEst[0]/2;
- nInMul = (int)(nRow / aiRowEst[nEq]);
- }
-
-#ifdef SQLITE_ENABLE_STAT2
- /* If the constraint is of the form x=VALUE or x IN (E1,E2,...)
- ** and we do not think that values of x are unique and if histogram
- ** data is available for column x, then it might be possible
- ** to get a better estimate on the number of rows based on
- ** VALUE and how common that value is according to the histogram.
- */
- if( nRow>(double)1 && nEq==1 && pFirstTerm!=0 && aiRowEst[1]>1 ){
- if( pFirstTerm->eOperator & (WO_EQ|WO_ISNULL) ){
- testcase( pFirstTerm->eOperator==WO_EQ );
- testcase( pFirstTerm->eOperator==WO_ISNULL );
- whereEqualScanEst(pParse, pProbe, pFirstTerm->pExpr->pRight, &nRow);
- }else if( pFirstTerm->eOperator==WO_IN && bInEst==0 ){
- whereInScanEst(pParse, pProbe, pFirstTerm->pExpr->x.pList, &nRow);
+#ifdef WHERETRACE_ENABLED
+/*
+** Print a WhereLoop object for debugging purposes
+*/
+static void whereLoopPrint(WhereLoop *p, WhereClause *pWC){
+ WhereInfo *pWInfo = pWC->pWInfo;
+ int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
+ struct SrcList_item *pItem = pWInfo->pTabList->a + p->iTab;
+ Table *pTab = pItem->pTab;
+ Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
+ sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
+ p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
+ sqlite3DebugPrintf(" %12s",
+ pItem->zAlias ? pItem->zAlias : pTab->zName);
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ const char *zName;
+ if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
+ if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
+ int i = sqlite3Strlen30(zName) - 1;
+ while( zName[i]!='_' ) i--;
+ zName += i;
}
- }
-#endif /* SQLITE_ENABLE_STAT2 */
-
- /* Adjust the number of output rows and downward to reflect rows
- ** that are excluded by range constraints.
- */
- nRow = (nRow * (double)estBound) / (double)100;
- if( nRow<1 ) nRow = 1;
-
- /* Experiments run on real SQLite databases show that the time needed
- ** to do a binary search to locate a row in a table or index is roughly
- ** log10(N) times the time to move from one row to the next row within
- ** a table or index. The actual times can vary, with the size of
- ** records being an important factor. Both moves and searches are
- ** slower with larger records, presumably because fewer records fit
- ** on one page and hence more pages have to be fetched.
- **
- ** The ANALYZE command and the sqlite_stat1 and sqlite_stat2 tables do
- ** not give us data on the relative sizes of table and index records.
- ** So this computation assumes table records are about twice as big
- ** as index records
- */
- if( (wsFlags & WHERE_NOT_FULLSCAN)==0 ){
- /* The cost of a full table scan is a number of move operations equal
- ** to the number of rows in the table.
- **
- ** We add an additional 4x penalty to full table scans. This causes
- ** the cost function to err on the side of choosing an index over
- ** choosing a full scan. This 4x full-scan penalty is an arguable
- ** decision and one which we expect to revisit in the future. But
- ** it seems to be working well enough at the moment.
- */
- cost = aiRowEst[0]*4;
+ sqlite3DebugPrintf(".%-16s %2d", zName, p->u.btree.nEq);
}else{
- log10N = estLog(aiRowEst[0]);
- cost = nRow;
- if( pIdx ){
- if( bLookup ){
- /* For an index lookup followed by a table lookup:
- ** nInMul index searches to find the start of each index range
- ** + nRow steps through the index
- ** + nRow table searches to lookup the table entry using the rowid
- */
- cost += (nInMul + nRow)*log10N;
- }else{
- /* For a covering index:
- ** nInMul index searches to find the initial entry
- ** + nRow steps through the index
- */
- cost += nInMul*log10N;
- }
- }else{
- /* For a rowid primary key lookup:
- ** nInMult table searches to find the initial entry for each range
- ** + nRow steps through the table
- */
- cost += nInMul*log10N;
- }
+ sqlite3DebugPrintf("%20s","");
}
-
- /* Add in the estimated cost of sorting the result. Actual experimental
- ** measurements of sorting performance in SQLite show that sorting time
- ** adds C*N*log10(N) to the cost, where N is the number of rows to be
- ** sorted and C is a factor between 1.95 and 4.3. We will split the
- ** difference and select C of 3.0.
- */
- if( bSort ){
- cost += nRow*estLog(nRow)*3;
+ }else{
+ char *z;
+ if( p->u.vtab.idxStr ){
+ z = sqlite3_mprintf("(%d,\"%s\",%x)",
+ p->u.vtab.idxNum, p->u.vtab.idxStr, p->u.vtab.omitMask);
+ }else{
+ z = sqlite3_mprintf("(%d,%x)", p->u.vtab.idxNum, p->u.vtab.omitMask);
}
- if( bDist ){
- cost += nRow*estLog(nRow)*3;
+ sqlite3DebugPrintf(" %-19s", z);
+ sqlite3_free(z);
+ }
+ if( p->wsFlags & WHERE_SKIPSCAN ){
+ sqlite3DebugPrintf(" f %05x %d-%d", p->wsFlags, p->nLTerm,p->nSkip);
+ }else{
+ sqlite3DebugPrintf(" f %05x N %d", p->wsFlags, p->nLTerm);
+ }
+ sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
+ if( p->nLTerm && (sqlite3WhereTrace & 0x100)!=0 ){
+ int i;
+ for(i=0; i<p->nLTerm; i++){
+ whereTermPrint(p->aLTerm[i], i);
}
+ }
+}
+#endif
- /**** Cost of using this index has now been computed ****/
+/*
+** Convert bulk memory into a valid WhereLoop that can be passed
+** to whereLoopClear harmlessly.
+*/
+static void whereLoopInit(WhereLoop *p){
+ p->aLTerm = p->aLTermSpace;
+ p->nLTerm = 0;
+ p->nLSlot = ArraySize(p->aLTermSpace);
+ p->wsFlags = 0;
+}
- /* If there are additional constraints on this table that cannot
- ** be used with the current index, but which might lower the number
- ** of output rows, adjust the nRow value accordingly. This only
- ** matters if the current index is the least costly, so do not bother
- ** with this step if we already know this index will not be chosen.
- ** Also, never reduce the output row count below 2 using this step.
- **
- ** It is critical that the notValid mask be used here instead of
- ** the notReady mask. When computing an "optimal" index, the notReady
- ** mask will only have one bit set - the bit for the current table.
- ** The notValid mask, on the other hand, always has all bits set for
- ** tables that are not in outer loops. If notReady is used here instead
- ** of notValid, then a optimal index that depends on inner joins loops
- ** might be selected even when there exists an optimal index that has
- ** no such dependency.
- */
- if( nRow>2 && cost<=pCost->rCost ){
- int k; /* Loop counter */
- int nSkipEq = nEq; /* Number of == constraints to skip */
- int nSkipRange = nBound; /* Number of < constraints to skip */
- Bitmask thisTab; /* Bitmap for pSrc */
-
- thisTab = getMask(pWC->pMaskSet, iCur);
- for(pTerm=pWC->a, k=pWC->nTerm; nRow>2 && k; k--, pTerm++){
- if( pTerm->wtFlags & TERM_VIRTUAL ) continue;
- if( (pTerm->prereqAll & notValid)!=thisTab ) continue;
- if( pTerm->eOperator & (WO_EQ|WO_IN|WO_ISNULL) ){
- if( nSkipEq ){
- /* Ignore the first nEq equality matches since the index
- ** has already accounted for these */
- nSkipEq--;
- }else{
- /* Assume each additional equality match reduces the result
- ** set size by a factor of 10 */
- nRow /= 10;
- }
- }else if( pTerm->eOperator & (WO_LT|WO_LE|WO_GT|WO_GE) ){
- if( nSkipRange ){
- /* Ignore the first nSkipRange range constraints since the index
- ** has already accounted for these */
- nSkipRange--;
- }else{
- /* Assume each additional range constraint reduces the result
- ** set size by a factor of 3. Indexed range constraints reduce
- ** the search space by a larger factor: 4. We make indexed range
- ** more selective intentionally because of the subjective
- ** observation that indexed range constraints really are more
- ** selective in practice, on average. */
- nRow /= 3;
- }
- }else if( pTerm->eOperator!=WO_NOOP ){
- /* Any other expression lowers the output row count by half */
- nRow /= 2;
- }
- }
- if( nRow<2 ) nRow = 2;
+/*
+** Clear the WhereLoop.u union. Leave WhereLoop.pLTerm intact.
+*/
+static void whereLoopClearUnion(sqlite3 *db, WhereLoop *p){
+ if( p->wsFlags & (WHERE_VIRTUALTABLE|WHERE_AUTO_INDEX) ){
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 && p->u.vtab.needFree ){
+ sqlite3_free(p->u.vtab.idxStr);
+ p->u.vtab.needFree = 0;
+ p->u.vtab.idxStr = 0;
+ }else if( (p->wsFlags & WHERE_AUTO_INDEX)!=0 && p->u.btree.pIndex!=0 ){
+ sqlite3DbFree(db, p->u.btree.pIndex->zColAff);
+ sqlite3DbFree(db, p->u.btree.pIndex);
+ p->u.btree.pIndex = 0;
}
+ }
+}
+/*
+** Deallocate internal memory used by a WhereLoop object
+*/
+static void whereLoopClear(sqlite3 *db, WhereLoop *p){
+ if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
+ whereLoopClearUnion(db, p);
+ whereLoopInit(p);
+}
- WHERETRACE((
- "%s(%s): nEq=%d nInMul=%d estBound=%d bSort=%d bLookup=%d wsFlags=0x%x\n"
- " notReady=0x%llx log10N=%.1f nRow=%.1f cost=%.1f used=0x%llx\n",
- pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"),
- nEq, nInMul, estBound, bSort, bLookup, wsFlags,
- notReady, log10N, nRow, cost, used
- ));
-
- /* If this index is the best we have seen so far, then record this
- ** index and its cost in the pCost structure.
- */
- if( (!pIdx || wsFlags)
- && (cost<pCost->rCost || (cost<=pCost->rCost && nRow<pCost->plan.nRow))
- ){
- pCost->rCost = cost;
- pCost->used = used;
- pCost->plan.nRow = nRow;
- pCost->plan.wsFlags = (wsFlags&wsFlagMask);
- pCost->plan.nEq = nEq;
- pCost->plan.u.pIdx = pIdx;
- }
-
- /* If there was an INDEXED BY clause, then only that one index is
- ** considered. */
- if( pSrc->pIndex ) break;
+/*
+** Increase the memory allocation for pLoop->aLTerm[] to be at least n.
+*/
+static int whereLoopResize(sqlite3 *db, WhereLoop *p, int n){
+ WhereTerm **paNew;
+ if( p->nLSlot>=n ) return SQLITE_OK;
+ n = (n+7)&~7;
+ paNew = sqlite3DbMallocRawNN(db, sizeof(p->aLTerm[0])*n);
+ if( paNew==0 ) return SQLITE_NOMEM_BKPT;
+ memcpy(paNew, p->aLTerm, sizeof(p->aLTerm[0])*p->nLSlot);
+ if( p->aLTerm!=p->aLTermSpace ) sqlite3DbFree(db, p->aLTerm);
+ p->aLTerm = paNew;
+ p->nLSlot = n;
+ return SQLITE_OK;
+}
- /* Reset masks for the next index in the loop */
- wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE);
- eqTermMask = idxEqTermMask;
+/*
+** Transfer content from the second pLoop into the first.
+*/
+static int whereLoopXfer(sqlite3 *db, WhereLoop *pTo, WhereLoop *pFrom){
+ whereLoopClearUnion(db, pTo);
+ if( whereLoopResize(db, pTo, pFrom->nLTerm) ){
+ memset(&pTo->u, 0, sizeof(pTo->u));
+ return SQLITE_NOMEM_BKPT;
}
-
- /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag
- ** is set, then reverse the order that the index will be scanned
- ** in. This is used for application testing, to help find cases
- ** where application behaviour depends on the (undefined) order that
- ** SQLite outputs rows in in the absence of an ORDER BY clause. */
- if( !pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){
- pCost->plan.wsFlags |= WHERE_REVERSE;
+ memcpy(pTo, pFrom, WHERE_LOOP_XFER_SZ);
+ memcpy(pTo->aLTerm, pFrom->aLTerm, pTo->nLTerm*sizeof(pTo->aLTerm[0]));
+ if( pFrom->wsFlags & WHERE_VIRTUALTABLE ){
+ pFrom->u.vtab.needFree = 0;
+ }else if( (pFrom->wsFlags & WHERE_AUTO_INDEX)!=0 ){
+ pFrom->u.btree.pIndex = 0;
}
+ return SQLITE_OK;
+}
- assert( pOrderBy || (pCost->plan.wsFlags&WHERE_ORDERBY)==0 );
- assert( pCost->plan.u.pIdx==0 || (pCost->plan.wsFlags&WHERE_ROWID_EQ)==0 );
- assert( pSrc->pIndex==0
- || pCost->plan.u.pIdx==0
- || pCost->plan.u.pIdx==pSrc->pIndex
- );
-
- WHERETRACE(("best index is: %s\n",
- ((pCost->plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ? "none" :
- pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk")
- ));
-
- bestOrClauseIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost);
- bestAutomaticIndex(pParse, pWC, pSrc, notReady, pCost);
- pCost->plan.wsFlags |= eqTermMask;
+/*
+** Delete a WhereLoop object
+*/
+static void whereLoopDelete(sqlite3 *db, WhereLoop *p){
+ whereLoopClear(db, p);
+ sqlite3DbFree(db, p);
}
/*
-** Find the query plan for accessing table pSrc->pTab. Write the
-** best query plan and its cost into the WhereCost object supplied
-** as the last parameter. This function may calculate the cost of
-** both real and virtual table scans.
+** Free a WhereInfo structure
*/
-static void bestIndex(
- Parse *pParse, /* The parsing context */
- WhereClause *pWC, /* The WHERE clause */
- struct SrcList_item *pSrc, /* The FROM clause term to search */
- Bitmask notReady, /* Mask of cursors not available for indexing */
- Bitmask notValid, /* Cursors not available for any purpose */
- ExprList *pOrderBy, /* The ORDER BY clause */
- WhereCost *pCost /* Lowest cost query plan */
-){
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(pSrc->pTab) ){
- sqlite3_index_info *p = 0;
- bestVirtualIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, pCost,&p);
- if( p->needToFreeIdxStr ){
- sqlite3_free(p->idxStr);
+static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
+ if( ALWAYS(pWInfo) ){
+ int i;
+ for(i=0; i<pWInfo->nLevel; i++){
+ WhereLevel *pLevel = &pWInfo->a[i];
+ if( pLevel->pWLoop && (pLevel->pWLoop->wsFlags & WHERE_IN_ABLE) ){
+ sqlite3DbFree(db, pLevel->u.in.aInLoop);
+ }
}
- sqlite3DbFree(pParse->db, p);
- }else
-#endif
- {
- bestBtreeIndex(pParse, pWC, pSrc, notReady, notValid, pOrderBy, 0, pCost);
+ sqlite3WhereClauseClear(&pWInfo->sWC);
+ while( pWInfo->pLoops ){
+ WhereLoop *p = pWInfo->pLoops;
+ pWInfo->pLoops = p->pNextLoop;
+ whereLoopDelete(db, p);
+ }
+ sqlite3DbFree(db, pWInfo);
}
}
/*
-** Disable a term in the WHERE clause. Except, do not disable the term
-** if it controls a LEFT OUTER JOIN and it did not originate in the ON
-** or USING clause of that join.
+** Return TRUE if all of the following are true:
**
-** Consider the term t2.z='ok' in the following queries:
+** (1) X has the same or lower cost that Y
+** (2) X is a proper subset of Y
+** (3) X skips at least as many columns as Y
**
-** (1) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x WHERE t2.z='ok'
-** (2) SELECT * FROM t1 LEFT JOIN t2 ON t1.a=t2.x AND t2.z='ok'
-** (3) SELECT * FROM t1, t2 WHERE t1.a=t2.x AND t2.z='ok'
+** By "proper subset" we mean that X uses fewer WHERE clause terms
+** than Y and that every WHERE clause term used by X is also used
+** by Y.
**
-** The t2.z='ok' is disabled in the in (2) because it originates
-** in the ON clause. The term is disabled in (3) because it is not part
-** of a LEFT OUTER JOIN. In (1), the term is not disabled.
+** If X is a proper subset of Y then Y is a better choice and ought
+** to have a lower cost. This routine returns TRUE when that cost
+** relationship is inverted and needs to be adjusted. The third rule
+** was added because if X uses skip-scan less than Y it still might
+** deserve a lower cost even if it is a proper subset of Y.
+*/
+static int whereLoopCheaperProperSubset(
+ const WhereLoop *pX, /* First WhereLoop to compare */
+ const WhereLoop *pY /* Compare against this WhereLoop */
+){
+ int i, j;
+ if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
+ return 0; /* X is not a subset of Y */
+ }
+ if( pY->nSkip > pX->nSkip ) return 0;
+ if( pX->rRun >= pY->rRun ){
+ if( pX->rRun > pY->rRun ) return 0; /* X costs more than Y */
+ if( pX->nOut > pY->nOut ) return 0; /* X costs more than Y */
+ }
+ for(i=pX->nLTerm-1; i>=0; i--){
+ if( pX->aLTerm[i]==0 ) continue;
+ for(j=pY->nLTerm-1; j>=0; j--){
+ if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
+ }
+ if( j<0 ) return 0; /* X not a subset of Y since term X[i] not used by Y */
+ }
+ return 1; /* All conditions meet */
+}
+
+/*
+** Try to adjust the cost of WhereLoop pTemplate upwards or downwards so
+** that:
**
-** IMPLEMENTATION-OF: R-24597-58655 No tests are done for terms that are
-** completely satisfied by indices.
+** (1) pTemplate costs less than any other WhereLoops that are a proper
+** subset of pTemplate
**
-** Disabling a term causes that term to not be tested in the inner loop
-** of the join. Disabling is an optimization. When terms are satisfied
-** by indices, we disable them to prevent redundant tests in the inner
-** loop. We would get the correct results if nothing were ever disabled,
-** but joins might run a little slower. The trick is to disable as much
-** as we can without disabling too much. If we disabled in (1), we'd get
-** the wrong answer. See ticket #813.
+** (2) pTemplate costs more than any other WhereLoops for which pTemplate
+** is a proper subset.
+**
+** To say "WhereLoop X is a proper subset of Y" means that X uses fewer
+** WHERE clause terms than Y and that every WHERE clause term used by X is
+** also used by Y.
*/
-static void disableTerm(WhereLevel *pLevel, WhereTerm *pTerm){
- if( pTerm
- && (pTerm->wtFlags & TERM_CODED)==0
- && (pLevel->iLeftJoin==0 || ExprHasProperty(pTerm->pExpr, EP_FromJoin))
- ){
- pTerm->wtFlags |= TERM_CODED;
- if( pTerm->iParent>=0 ){
- WhereTerm *pOther = &pTerm->pWC->a[pTerm->iParent];
- if( (--pOther->nChild)==0 ){
- disableTerm(pLevel, pOther);
- }
+static void whereLoopAdjustCost(const WhereLoop *p, WhereLoop *pTemplate){
+ if( (pTemplate->wsFlags & WHERE_INDEXED)==0 ) return;
+ for(; p; p=p->pNextLoop){
+ if( p->iTab!=pTemplate->iTab ) continue;
+ if( (p->wsFlags & WHERE_INDEXED)==0 ) continue;
+ if( whereLoopCheaperProperSubset(p, pTemplate) ){
+ /* Adjust pTemplate cost downward so that it is cheaper than its
+ ** subset p. */
+ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n",
+ pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut-1));
+ pTemplate->rRun = p->rRun;
+ pTemplate->nOut = p->nOut - 1;
+ }else if( whereLoopCheaperProperSubset(pTemplate, p) ){
+ /* Adjust pTemplate cost upward so that it is costlier than p since
+ ** pTemplate is a proper subset of p */
+ WHERETRACE(0x80,("subset cost adjustment %d,%d to %d,%d\n",
+ pTemplate->rRun, pTemplate->nOut, p->rRun, p->nOut+1));
+ pTemplate->rRun = p->rRun;
+ pTemplate->nOut = p->nOut + 1;
}
}
}
/*
-** Code an OP_Affinity opcode to apply the column affinity string zAff
-** to the n registers starting at base.
+** Search the list of WhereLoops in *ppPrev looking for one that can be
+** supplanted by pTemplate.
**
-** As an optimization, SQLITE_AFF_NONE entries (which are no-ops) at the
-** beginning and end of zAff are ignored. If all entries in zAff are
-** SQLITE_AFF_NONE, then no code gets generated.
+** Return NULL if the WhereLoop list contains an entry that can supplant
+** pTemplate, in other words if pTemplate does not belong on the list.
**
-** This routine makes its own copy of zAff so that the caller is free
-** to modify zAff after this routine returns.
+** If pX is a WhereLoop that pTemplate can supplant, then return the
+** link that points to pX.
+**
+** If pTemplate cannot supplant any existing element of the list but needs
+** to be added to the list, then return a pointer to the tail of the list.
*/
-static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){
- Vdbe *v = pParse->pVdbe;
- if( zAff==0 ){
- assert( pParse->db->mallocFailed );
- return;
- }
- assert( v!=0 );
+static WhereLoop **whereLoopFindLesser(
+ WhereLoop **ppPrev,
+ const WhereLoop *pTemplate
+){
+ WhereLoop *p;
+ for(p=(*ppPrev); p; ppPrev=&p->pNextLoop, p=*ppPrev){
+ if( p->iTab!=pTemplate->iTab || p->iSortIdx!=pTemplate->iSortIdx ){
+ /* If either the iTab or iSortIdx values for two WhereLoop are different
+ ** then those WhereLoops need to be considered separately. Neither is
+ ** a candidate to replace the other. */
+ continue;
+ }
+ /* In the current implementation, the rSetup value is either zero
+ ** or the cost of building an automatic index (NlogN) and the NlogN
+ ** is the same for compatible WhereLoops. */
+ assert( p->rSetup==0 || pTemplate->rSetup==0
+ || p->rSetup==pTemplate->rSetup );
+
+ /* whereLoopAddBtree() always generates and inserts the automatic index
+ ** case first. Hence compatible candidate WhereLoops never have a larger
+ ** rSetup. Call this SETUP-INVARIANT */
+ assert( p->rSetup>=pTemplate->rSetup );
+
+ /* Any loop using an appliation-defined index (or PRIMARY KEY or
+ ** UNIQUE constraint) with one or more == constraints is better
+ ** than an automatic index. Unless it is a skip-scan. */
+ if( (p->wsFlags & WHERE_AUTO_INDEX)!=0
+ && (pTemplate->nSkip)==0
+ && (pTemplate->wsFlags & WHERE_INDEXED)!=0
+ && (pTemplate->wsFlags & WHERE_COLUMN_EQ)!=0
+ && (p->prereq & pTemplate->prereq)==pTemplate->prereq
+ ){
+ break;
+ }
- /* Adjust base and n to skip over SQLITE_AFF_NONE entries at the beginning
- ** and end of the affinity string.
- */
- while( n>0 && zAff[0]==SQLITE_AFF_NONE ){
- n--;
- base++;
- zAff++;
- }
- while( n>1 && zAff[n-1]==SQLITE_AFF_NONE ){
- n--;
- }
+ /* If existing WhereLoop p is better than pTemplate, pTemplate can be
+ ** discarded. WhereLoop p is better if:
+ ** (1) p has no more dependencies than pTemplate, and
+ ** (2) p has an equal or lower cost than pTemplate
+ */
+ if( (p->prereq & pTemplate->prereq)==p->prereq /* (1) */
+ && p->rSetup<=pTemplate->rSetup /* (2a) */
+ && p->rRun<=pTemplate->rRun /* (2b) */
+ && p->nOut<=pTemplate->nOut /* (2c) */
+ ){
+ return 0; /* Discard pTemplate */
+ }
- /* Code the OP_Affinity opcode if there is anything left to do. */
- if( n>0 ){
- sqlite3VdbeAddOp2(v, OP_Affinity, base, n);
- sqlite3VdbeChangeP4(v, -1, zAff, n);
- sqlite3ExprCacheAffinityChange(pParse, base, n);
+ /* If pTemplate is always better than p, then cause p to be overwritten
+ ** with pTemplate. pTemplate is better than p if:
+ ** (1) pTemplate has no more dependences than p, and
+ ** (2) pTemplate has an equal or lower cost than p.
+ */
+ if( (p->prereq & pTemplate->prereq)==pTemplate->prereq /* (1) */
+ && p->rRun>=pTemplate->rRun /* (2a) */
+ && p->nOut>=pTemplate->nOut /* (2b) */
+ ){
+ assert( p->rSetup>=pTemplate->rSetup ); /* SETUP-INVARIANT above */
+ break; /* Cause p to be overwritten by pTemplate */
+ }
}
+ return ppPrev;
}
-
/*
-** Generate code for a single equality term of the WHERE clause. An equality
-** term can be either X=expr or X IN (...). pTerm is the term to be
-** coded.
+** Insert or replace a WhereLoop entry using the template supplied.
**
-** The current value for the constraint is left in register iReg.
+** An existing WhereLoop entry might be overwritten if the new template
+** is better and has fewer dependencies. Or the template will be ignored
+** and no insert will occur if an existing WhereLoop is faster and has
+** fewer dependencies than the template. Otherwise a new WhereLoop is
+** added based on the template.
**
-** For a constraint of the form X=expr, the expression is evaluated and its
-** result is left on the stack. For constraints of the form X IN (...)
-** this routine sets up a loop that will iterate over all values of X.
+** If pBuilder->pOrSet is not NULL then we care about only the
+** prerequisites and rRun and nOut costs of the N best loops. That
+** information is gathered in the pBuilder->pOrSet object. This special
+** processing mode is used only for OR clause processing.
+**
+** When accumulating multiple loops (when pBuilder->pOrSet is NULL) we
+** still might overwrite similar loops with the new template if the
+** new template is better. Loops may be overwritten if the following
+** conditions are met:
+**
+** (1) They have the same iTab.
+** (2) They have the same iSortIdx.
+** (3) The template has same or fewer dependencies than the current loop
+** (4) The template has the same or lower cost than the current loop
*/
-static int codeEqualityTerm(
- Parse *pParse, /* The parsing context */
- WhereTerm *pTerm, /* The term of the WHERE clause to be coded */
- WhereLevel *pLevel, /* When level of the FROM clause we are working on */
- int iTarget /* Attempt to leave results in this register */
-){
- Expr *pX = pTerm->pExpr;
- Vdbe *v = pParse->pVdbe;
- int iReg; /* Register holding results */
+static int whereLoopInsert(WhereLoopBuilder *pBuilder, WhereLoop *pTemplate){
+ WhereLoop **ppPrev, *p;
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ sqlite3 *db = pWInfo->pParse->db;
+ int rc;
- assert( iTarget>0 );
- if( pX->op==TK_EQ ){
- iReg = sqlite3ExprCodeTarget(pParse, pX->pRight, iTarget);
- }else if( pX->op==TK_ISNULL ){
- iReg = iTarget;
- sqlite3VdbeAddOp2(v, OP_Null, 0, iReg);
-#ifndef SQLITE_OMIT_SUBQUERY
+ /* If pBuilder->pOrSet is defined, then only keep track of the costs
+ ** and prereqs.
+ */
+ if( pBuilder->pOrSet!=0 ){
+ if( pTemplate->nLTerm ){
+#if WHERETRACE_ENABLED
+ u16 n = pBuilder->pOrSet->n;
+ int x =
+#endif
+ whereOrInsert(pBuilder->pOrSet, pTemplate->prereq, pTemplate->rRun,
+ pTemplate->nOut);
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf(x?" or-%d: ":" or-X: ", n);
+ whereLoopPrint(pTemplate, pBuilder->pWC);
+ }
+#endif
+ }
+ return SQLITE_OK;
+ }
+
+ /* Look for an existing WhereLoop to replace with pTemplate
+ */
+ whereLoopAdjustCost(pWInfo->pLoops, pTemplate);
+ ppPrev = whereLoopFindLesser(&pWInfo->pLoops, pTemplate);
+
+ if( ppPrev==0 ){
+ /* There already exists a WhereLoop on the list that is better
+ ** than pTemplate, so just ignore pTemplate */
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf(" skip: ");
+ whereLoopPrint(pTemplate, pBuilder->pWC);
+ }
+#endif
+ return SQLITE_OK;
}else{
- int eType;
- int iTab;
- struct InLoop *pIn;
+ p = *ppPrev;
+ }
- assert( pX->op==TK_IN );
- iReg = iTarget;
- eType = sqlite3FindInIndex(pParse, pX, 0);
- iTab = pX->iTable;
- sqlite3VdbeAddOp2(v, OP_Rewind, iTab, 0);
- assert( pLevel->plan.wsFlags & WHERE_IN_ABLE );
- if( pLevel->u.in.nIn==0 ){
- pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
+ /* If we reach this point it means that either p[] should be overwritten
+ ** with pTemplate[] if p[] exists, or if p==NULL then allocate a new
+ ** WhereLoop and insert it.
+ */
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ if( p!=0 ){
+ sqlite3DebugPrintf("replace: ");
+ whereLoopPrint(p, pBuilder->pWC);
}
- pLevel->u.in.nIn++;
- pLevel->u.in.aInLoop =
- sqlite3DbReallocOrFree(pParse->db, pLevel->u.in.aInLoop,
- sizeof(pLevel->u.in.aInLoop[0])*pLevel->u.in.nIn);
- pIn = pLevel->u.in.aInLoop;
- if( pIn ){
- pIn += pLevel->u.in.nIn - 1;
- pIn->iCur = iTab;
- if( eType==IN_INDEX_ROWID ){
- pIn->addrInTop = sqlite3VdbeAddOp2(v, OP_Rowid, iTab, iReg);
- }else{
- pIn->addrInTop = sqlite3VdbeAddOp3(v, OP_Column, iTab, 0, iReg);
+ sqlite3DebugPrintf(" add: ");
+ whereLoopPrint(pTemplate, pBuilder->pWC);
+ }
+#endif
+ if( p==0 ){
+ /* Allocate a new WhereLoop to add to the end of the list */
+ *ppPrev = p = sqlite3DbMallocRawNN(db, sizeof(WhereLoop));
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
+ whereLoopInit(p);
+ p->pNextLoop = 0;
+ }else{
+ /* We will be overwriting WhereLoop p[]. But before we do, first
+ ** go through the rest of the list and delete any other entries besides
+ ** p[] that are also supplated by pTemplate */
+ WhereLoop **ppTail = &p->pNextLoop;
+ WhereLoop *pToDel;
+ while( *ppTail ){
+ ppTail = whereLoopFindLesser(ppTail, pTemplate);
+ if( ppTail==0 ) break;
+ pToDel = *ppTail;
+ if( pToDel==0 ) break;
+ *ppTail = pToDel->pNextLoop;
+#if WHERETRACE_ENABLED /* 0x8 */
+ if( sqlite3WhereTrace & 0x8 ){
+ sqlite3DebugPrintf(" delete: ");
+ whereLoopPrint(pToDel, pBuilder->pWC);
}
- sqlite3VdbeAddOp1(v, OP_IsNull, iReg);
- }else{
- pLevel->u.in.nIn = 0;
- }
#endif
+ whereLoopDelete(db, pToDel);
+ }
}
- disableTerm(pLevel, pTerm);
- return iReg;
+ rc = whereLoopXfer(db, p, pTemplate);
+ if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
+ Index *pIndex = p->u.btree.pIndex;
+ if( pIndex && pIndex->tnum==0 ){
+ p->u.btree.pIndex = 0;
+ }
+ }
+ return rc;
}
/*
-** Generate code that will evaluate all == and IN constraints for an
+** Adjust the WhereLoop.nOut value downward to account for terms of the
+** WHERE clause that reference the loop but which are not used by an
** index.
+*
+** For every WHERE clause term that is not used by the index
+** and which has a truth probability assigned by one of the likelihood(),
+** likely(), or unlikely() SQL functions, reduce the estimated number
+** of output rows by the probability specified.
+**
+** TUNING: For every WHERE clause term that is not used by the index
+** and which does not have an assigned truth probability, heuristics
+** described below are used to try to estimate the truth probability.
+** TODO --> Perhaps this is something that could be improved by better
+** table statistics.
+**
+** Heuristic 1: Estimate the truth probability as 93.75%. The 93.75%
+** value corresponds to -1 in LogEst notation, so this means decrement
+** the WhereLoop.nOut field for every such WHERE clause term.
+**
+** Heuristic 2: If there exists one or more WHERE clause terms of the
+** form "x==EXPR" and EXPR is not a constant 0 or 1, then make sure the
+** final output row estimate is no greater than 1/4 of the total number
+** of rows in the table. In other words, assume that x==EXPR will filter
+** out at least 3 out of 4 rows. If EXPR is -1 or 0 or 1, then maybe the
+** "x" column is boolean or else -1 or 0 or 1 is a common default value
+** on the "x" column and so in that case only cap the output row estimate
+** at 1/2 instead of 1/4.
+*/
+static void whereLoopOutputAdjust(
+ WhereClause *pWC, /* The WHERE clause */
+ WhereLoop *pLoop, /* The loop to adjust downward */
+ LogEst nRow /* Number of rows in the entire table */
+){
+ WhereTerm *pTerm, *pX;
+ Bitmask notAllowed = ~(pLoop->prereq|pLoop->maskSelf);
+ int i, j, k;
+ LogEst iReduce = 0; /* pLoop->nOut should not exceed nRow-iReduce */
+
+ assert( (pLoop->wsFlags & WHERE_AUTO_INDEX)==0 );
+ for(i=pWC->nTerm, pTerm=pWC->a; i>0; i--, pTerm++){
+ if( (pTerm->wtFlags & TERM_VIRTUAL)!=0 ) break;
+ if( (pTerm->prereqAll & pLoop->maskSelf)==0 ) continue;
+ if( (pTerm->prereqAll & notAllowed)!=0 ) continue;
+ for(j=pLoop->nLTerm-1; j>=0; j--){
+ pX = pLoop->aLTerm[j];
+ if( pX==0 ) continue;
+ if( pX==pTerm ) break;
+ if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break;
+ }
+ if( j<0 ){
+ if( pTerm->truthProb<=0 ){
+ /* If a truth probability is specified using the likelihood() hints,
+ ** then use the probability provided by the application. */
+ pLoop->nOut += pTerm->truthProb;
+ }else{
+ /* In the absence of explicit truth probabilities, use heuristics to
+ ** guess a reasonable truth probability. */
+ pLoop->nOut--;
+ if( pTerm->eOperator&(WO_EQ|WO_IS) ){
+ Expr *pRight = pTerm->pExpr->pRight;
+ testcase( pTerm->pExpr->op==TK_IS );
+ if( sqlite3ExprIsInteger(pRight, &k) && k>=(-1) && k<=1 ){
+ k = 10;
+ }else{
+ k = 20;
+ }
+ if( iReduce<k ) iReduce = k;
+ }
+ }
+ }
+ }
+ if( pLoop->nOut > nRow-iReduce ) pLoop->nOut = nRow - iReduce;
+}
+
+/*
+** Term pTerm is a vector range comparison operation. The first comparison
+** in the vector can be optimized using column nEq of the index. This
+** function returns the total number of vector elements that can be used
+** as part of the range comparison.
**
-** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c).
-** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10
-** The index has as many as three equality constraints, but in this
-** example, the third "c" value is an inequality. So only two
-** constraints are coded. This routine will generate code to evaluate
-** a==5 and b IN (1,2,3). The current values for a and b will be stored
-** in consecutive registers and the index of the first register is returned.
-**
-** In the example above nEq==2. But this subroutine works for any value
-** of nEq including 0. If nEq==0, this routine is nearly a no-op.
-** The only thing it does is allocate the pLevel->iMem memory cell and
-** compute the affinity string.
+** For example, if the query is:
**
-** This routine always allocates at least one memory cell and returns
-** the index of that memory cell. The code that
-** calls this routine will use that memory cell to store the termination
-** key value of the loop. If one or more IN operators appear, then
-** this routine allocates an additional nEq memory cells for internal
-** use.
+** WHERE a = ? AND (b, c, d) > (?, ?, ?)
**
-** Before returning, *pzAff is set to point to a buffer containing a
-** copy of the column affinity string of the index allocated using
-** sqlite3DbMalloc(). Except, entries in the copy of the string associated
-** with equality constraints that use NONE affinity are set to
-** SQLITE_AFF_NONE. This is to deal with SQL such as the following:
+** and the index:
**
-** CREATE TABLE t1(a TEXT PRIMARY KEY, b);
-** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b;
+** CREATE INDEX ... ON (a, b, c, d, e)
**
-** In the example above, the index on t1(a) has TEXT affinity. But since
-** the right hand side of the equality constraint (t2.b) has NONE affinity,
-** no conversion should be attempted before using a t2.b value as part of
-** a key to search the index. Hence the first byte in the returned affinity
-** string in this example would be set to SQLITE_AFF_NONE.
+** then this function would be invoked with nEq=1. The value returned in
+** this case is 3.
*/
-static int codeAllEqualityTerms(
- Parse *pParse, /* Parsing context */
- WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */
- WhereClause *pWC, /* The WHERE clause */
- Bitmask notReady, /* Which parts of FROM have not yet been coded */
- int nExtraReg, /* Number of extra registers to allocate */
- char **pzAff /* OUT: Set to point to affinity string */
+static int whereRangeVectorLen(
+ Parse *pParse, /* Parsing context */
+ int iCur, /* Cursor open on pIdx */
+ Index *pIdx, /* The index to be used for a inequality constraint */
+ int nEq, /* Number of prior equality constraints on same index */
+ WhereTerm *pTerm /* The vector inequality constraint */
){
- int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */
- Vdbe *v = pParse->pVdbe; /* The vm under construction */
- Index *pIdx; /* The index being used for this loop */
- int iCur = pLevel->iTabCur; /* The cursor of the table */
- WhereTerm *pTerm; /* A single constraint term */
- int j; /* Loop counter */
- int regBase; /* Base register */
- int nReg; /* Number of registers to allocate */
- char *zAff; /* Affinity string to return */
+ int nCmp = sqlite3ExprVectorSize(pTerm->pExpr->pLeft);
+ int i;
- /* This module is only called on query plans that use an index. */
- assert( pLevel->plan.wsFlags & WHERE_INDEXED );
- pIdx = pLevel->plan.u.pIdx;
+ nCmp = MIN(nCmp, (pIdx->nColumn - nEq));
+ for(i=1; i<nCmp; i++){
+ /* Test if comparison i of pTerm is compatible with column (i+nEq)
+ ** of the index. If not, exit the loop. */
+ char aff; /* Comparison affinity */
+ char idxaff = 0; /* Indexed columns affinity */
+ CollSeq *pColl; /* Comparison collation sequence */
+ Expr *pLhs = pTerm->pExpr->pLeft->x.pList->a[i].pExpr;
+ Expr *pRhs = pTerm->pExpr->pRight;
+ if( pRhs->flags & EP_xIsSelect ){
+ pRhs = pRhs->x.pSelect->pEList->a[i].pExpr;
+ }else{
+ pRhs = pRhs->x.pList->a[i].pExpr;
+ }
- /* Figure out how many memory cells we will need then allocate them.
- */
- regBase = pParse->nMem + 1;
- nReg = pLevel->plan.nEq + nExtraReg;
- pParse->nMem += nReg;
+ /* Check that the LHS of the comparison is a column reference to
+ ** the right column of the right source table. And that the sort
+ ** order of the index column is the same as the sort order of the
+ ** leftmost index column. */
+ if( pLhs->op!=TK_COLUMN
+ || pLhs->iTable!=iCur
+ || pLhs->iColumn!=pIdx->aiColumn[i+nEq]
+ || pIdx->aSortOrder[i+nEq]!=pIdx->aSortOrder[nEq]
+ ){
+ break;
+ }
+
+ testcase( pLhs->iColumn==XN_ROWID );
+ aff = sqlite3CompareAffinity(pRhs, sqlite3ExprAffinity(pLhs));
+ idxaff = sqlite3TableColumnAffinity(pIdx->pTable, pLhs->iColumn);
+ if( aff!=idxaff ) break;
- zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx));
- if( !zAff ){
- pParse->db->mallocFailed = 1;
+ pColl = sqlite3BinaryCompareCollSeq(pParse, pLhs, pRhs);
+ if( pColl==0 ) break;
+ if( sqlite3StrICmp(pColl->zName, pIdx->azColl[i+nEq]) ) break;
}
+ return i;
+}
- /* Evaluate the equality constraints
- */
- assert( pIdx->nColumn>=nEq );
- for(j=0; j<nEq; j++){
- int r1;
- int k = pIdx->aiColumn[j];
- pTerm = findTerm(pWC, iCur, k, notReady, pLevel->plan.wsFlags, pIdx);
- if( NEVER(pTerm==0) ) break;
- /* The following true for indices with redundant columns.
- ** Ex: CREATE INDEX i1 ON t1(a,b,a); SELECT * FROM t1 WHERE a=0 AND b=0; */
- testcase( (pTerm->wtFlags & TERM_CODED)!=0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- r1 = codeEqualityTerm(pParse, pTerm, pLevel, regBase+j);
- if( r1!=regBase+j ){
- if( nReg==1 ){
- sqlite3ReleaseTempReg(pParse, regBase);
- regBase = r1;
- }else{
- sqlite3VdbeAddOp2(v, OP_SCopy, r1, regBase+j);
- }
+/*
+** Adjust the cost C by the costMult facter T. This only occurs if
+** compiled with -DSQLITE_ENABLE_COSTMULT
+*/
+#ifdef SQLITE_ENABLE_COSTMULT
+# define ApplyCostMultiplier(C,T) C += T
+#else
+# define ApplyCostMultiplier(C,T)
+#endif
+
+/*
+** We have so far matched pBuilder->pNew->u.btree.nEq terms of the
+** index pIndex. Try to match one more.
+**
+** When this function is called, pBuilder->pNew->nOut contains the
+** number of rows expected to be visited by filtering using the nEq
+** terms only. If it is modified, this value is restored before this
+** function returns.
+**
+** If pProbe->tnum==0, that means pIndex is a fake index used for the
+** INTEGER PRIMARY KEY.
+*/
+static int whereLoopAddBtreeIndex(
+ WhereLoopBuilder *pBuilder, /* The WhereLoop factory */
+ struct SrcList_item *pSrc, /* FROM clause term being analyzed */
+ Index *pProbe, /* An index on pSrc */
+ LogEst nInMul /* log(Number of iterations due to IN) */
+){
+ WhereInfo *pWInfo = pBuilder->pWInfo; /* WHERE analyse context */
+ Parse *pParse = pWInfo->pParse; /* Parsing context */
+ sqlite3 *db = pParse->db; /* Database connection malloc context */
+ WhereLoop *pNew; /* Template WhereLoop under construction */
+ WhereTerm *pTerm; /* A WhereTerm under consideration */
+ int opMask; /* Valid operators for constraints */
+ WhereScan scan; /* Iterator for WHERE terms */
+ Bitmask saved_prereq; /* Original value of pNew->prereq */
+ u16 saved_nLTerm; /* Original value of pNew->nLTerm */
+ u16 saved_nEq; /* Original value of pNew->u.btree.nEq */
+ u16 saved_nBtm; /* Original value of pNew->u.btree.nBtm */
+ u16 saved_nTop; /* Original value of pNew->u.btree.nTop */
+ u16 saved_nSkip; /* Original value of pNew->nSkip */
+ u32 saved_wsFlags; /* Original value of pNew->wsFlags */
+ LogEst saved_nOut; /* Original value of pNew->nOut */
+ int rc = SQLITE_OK; /* Return code */
+ LogEst rSize; /* Number of rows in the table */
+ LogEst rLogSize; /* Logarithm of table size */
+ WhereTerm *pTop = 0, *pBtm = 0; /* Top and bottom range constraints */
+
+ pNew = pBuilder->pNew;
+ if( db->mallocFailed ) return SQLITE_NOMEM_BKPT;
+ WHERETRACE(0x800, ("BEGIN addBtreeIdx(%s), nEq=%d\n",
+ pProbe->zName, pNew->u.btree.nEq));
+
+ assert( (pNew->wsFlags & WHERE_VIRTUALTABLE)==0 );
+ assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
+ if( pNew->wsFlags & WHERE_BTM_LIMIT ){
+ opMask = WO_LT|WO_LE;
+ }else{
+ assert( pNew->u.btree.nBtm==0 );
+ opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
+ }
+ if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
+
+ assert( pNew->u.btree.nEq<pProbe->nColumn );
+
+ saved_nEq = pNew->u.btree.nEq;
+ saved_nBtm = pNew->u.btree.nBtm;
+ saved_nTop = pNew->u.btree.nTop;
+ saved_nSkip = pNew->nSkip;
+ saved_nLTerm = pNew->nLTerm;
+ saved_wsFlags = pNew->wsFlags;
+ saved_prereq = pNew->prereq;
+ saved_nOut = pNew->nOut;
+ pTerm = whereScanInit(&scan, pBuilder->pWC, pSrc->iCursor, saved_nEq,
+ opMask, pProbe);
+ pNew->rSetup = 0;
+ rSize = pProbe->aiRowLogEst[0];
+ rLogSize = estLog(rSize);
+ for(; rc==SQLITE_OK && pTerm!=0; pTerm = whereScanNext(&scan)){
+ u16 eOp = pTerm->eOperator; /* Shorthand for pTerm->eOperator */
+ LogEst rCostIdx;
+ LogEst nOutUnadjusted; /* nOut before IN() and WHERE adjustments */
+ int nIn = 0;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ int nRecValid = pBuilder->nRecValid;
+#endif
+ if( (eOp==WO_ISNULL || (pTerm->wtFlags&TERM_VNULL)!=0)
+ && indexColumnNotNull(pProbe, saved_nEq)
+ ){
+ continue; /* ignore IS [NOT] NULL constraints on NOT NULL columns */
}
- testcase( pTerm->eOperator & WO_ISNULL );
- testcase( pTerm->eOperator & WO_IN );
- if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){
- Expr *pRight = pTerm->pExpr->pRight;
- sqlite3ExprCodeIsNullJump(v, pRight, regBase+j, pLevel->addrBrk);
- if( zAff ){
- if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_NONE ){
- zAff[j] = SQLITE_AFF_NONE;
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+
+ /* Do not allow the upper bound of a LIKE optimization range constraint
+ ** to mix with a lower range bound from some other source */
+ if( pTerm->wtFlags & TERM_LIKEOPT && pTerm->eOperator==WO_LT ) continue;
+
+ /* Do not allow IS constraints from the WHERE clause to be used by the
+ ** right table of a LEFT JOIN. Only constraints in the ON clause are
+ ** allowed */
+ if( (pSrc->fg.jointype & JT_LEFT)!=0
+ && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
+ && (eOp & (WO_IS|WO_ISNULL))!=0
+ ){
+ testcase( eOp & WO_IS );
+ testcase( eOp & WO_ISNULL );
+ continue;
+ }
+
+ pNew->wsFlags = saved_wsFlags;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->u.btree.nBtm = saved_nBtm;
+ pNew->u.btree.nTop = saved_nTop;
+ pNew->nLTerm = saved_nLTerm;
+ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ pNew->aLTerm[pNew->nLTerm++] = pTerm;
+ pNew->prereq = (saved_prereq | pTerm->prereqRight) & ~pNew->maskSelf;
+
+ assert( nInMul==0
+ || (pNew->wsFlags & WHERE_COLUMN_NULL)!=0
+ || (pNew->wsFlags & WHERE_COLUMN_IN)!=0
+ || (pNew->wsFlags & WHERE_SKIPSCAN)!=0
+ );
+
+ if( eOp & WO_IN ){
+ Expr *pExpr = pTerm->pExpr;
+ pNew->wsFlags |= WHERE_COLUMN_IN;
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+ /* "x IN (SELECT ...)": TUNING: the SELECT returns 25 rows */
+ int i;
+ nIn = 46; assert( 46==sqlite3LogEst(25) );
+
+ /* The expression may actually be of the form (x, y) IN (SELECT...).
+ ** In this case there is a separate term for each of (x) and (y).
+ ** However, the nIn multiplier should only be applied once, not once
+ ** for each such term. The following loop checks that pTerm is the
+ ** first such term in use, and sets nIn back to 0 if it is not. */
+ for(i=0; i<pNew->nLTerm-1; i++){
+ if( pNew->aLTerm[i] && pNew->aLTerm[i]->pExpr==pExpr ) nIn = 0;
}
- if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){
- zAff[j] = SQLITE_AFF_NONE;
+ }else if( ALWAYS(pExpr->x.pList && pExpr->x.pList->nExpr) ){
+ /* "x IN (value, value, ...)" */
+ nIn = sqlite3LogEst(pExpr->x.pList->nExpr);
+ assert( nIn>0 ); /* RHS always has 2 or more terms... The parser
+ ** changes "x IN (?)" into "x=?". */
+ }
+ }else if( eOp & (WO_EQ|WO_IS) ){
+ int iCol = pProbe->aiColumn[saved_nEq];
+ pNew->wsFlags |= WHERE_COLUMN_EQ;
+ assert( saved_nEq==pNew->u.btree.nEq );
+ if( iCol==XN_ROWID
+ || (iCol>0 && nInMul==0 && saved_nEq==pProbe->nKeyCol-1)
+ ){
+ if( iCol>=0 && pProbe->uniqNotNull==0 ){
+ pNew->wsFlags |= WHERE_UNQ_WANTED;
+ }else{
+ pNew->wsFlags |= WHERE_ONEROW;
+ }
+ }
+ }else if( eOp & WO_ISNULL ){
+ pNew->wsFlags |= WHERE_COLUMN_NULL;
+ }else if( eOp & (WO_GT|WO_GE) ){
+ testcase( eOp & WO_GT );
+ testcase( eOp & WO_GE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
+ pNew->u.btree.nBtm = whereRangeVectorLen(
+ pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
+ );
+ pBtm = pTerm;
+ pTop = 0;
+ if( pTerm->wtFlags & TERM_LIKEOPT ){
+ /* Range contraints that come from the LIKE optimization are
+ ** always used in pairs. */
+ pTop = &pTerm[1];
+ assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm );
+ assert( pTop->wtFlags & TERM_LIKEOPT );
+ assert( pTop->eOperator==WO_LT );
+ if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
+ pNew->aLTerm[pNew->nLTerm++] = pTop;
+ pNew->wsFlags |= WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = 1;
+ }
+ }else{
+ assert( eOp & (WO_LT|WO_LE) );
+ testcase( eOp & WO_LT );
+ testcase( eOp & WO_LE );
+ pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_TOP_LIMIT;
+ pNew->u.btree.nTop = whereRangeVectorLen(
+ pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
+ );
+ pTop = pTerm;
+ pBtm = (pNew->wsFlags & WHERE_BTM_LIMIT)!=0 ?
+ pNew->aLTerm[pNew->nLTerm-2] : 0;
+ }
+
+ /* At this point pNew->nOut is set to the number of rows expected to
+ ** be visited by the index scan before considering term pTerm, or the
+ ** values of nIn and nInMul. In other words, assuming that all
+ ** "x IN(...)" terms are replaced with "x = ?". This block updates
+ ** the value of pNew->nOut to account for pTerm (but not nIn/nInMul). */
+ assert( pNew->nOut==saved_nOut );
+ if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
+ /* Adjust nOut using stat3/stat4 data. Or, if there is no stat3/stat4
+ ** data, using some other estimate. */
+ whereRangeScanEst(pParse, pBuilder, pBtm, pTop, pNew);
+ }else{
+ int nEq = ++pNew->u.btree.nEq;
+ assert( eOp & (WO_ISNULL|WO_EQ|WO_IN|WO_IS) );
+
+ assert( pNew->nOut==saved_nOut );
+ if( pTerm->truthProb<=0 && pProbe->aiColumn[saved_nEq]>=0 ){
+ assert( (eOp & WO_IN) || nIn==0 );
+ testcase( eOp & WO_IN );
+ pNew->nOut += pTerm->truthProb;
+ pNew->nOut -= nIn;
+ }else{
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ tRowcnt nOut = 0;
+ if( nInMul==0
+ && pProbe->nSample
+ && pNew->u.btree.nEq<=pProbe->nSampleCol
+ && ((eOp & WO_IN)==0 || !ExprHasProperty(pTerm->pExpr, EP_xIsSelect))
+ ){
+ Expr *pExpr = pTerm->pExpr;
+ if( (eOp & (WO_EQ|WO_ISNULL|WO_IS))!=0 ){
+ testcase( eOp & WO_EQ );
+ testcase( eOp & WO_IS );
+ testcase( eOp & WO_ISNULL );
+ rc = whereEqualScanEst(pParse, pBuilder, pExpr->pRight, &nOut);
+ }else{
+ rc = whereInScanEst(pParse, pBuilder, pExpr->x.pList, &nOut);
+ }
+ if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
+ if( rc!=SQLITE_OK ) break; /* Jump out of the pTerm loop */
+ if( nOut ){
+ pNew->nOut = sqlite3LogEst(nOut);
+ if( pNew->nOut>saved_nOut ) pNew->nOut = saved_nOut;
+ pNew->nOut -= nIn;
+ }
+ }
+ if( nOut==0 )
+#endif
+ {
+ pNew->nOut += (pProbe->aiRowLogEst[nEq] - pProbe->aiRowLogEst[nEq-1]);
+ if( eOp & WO_ISNULL ){
+ /* TUNING: If there is no likelihood() value, assume that a
+ ** "col IS NULL" expression matches twice as many rows
+ ** as (col=?). */
+ pNew->nOut += 10;
+ }
}
}
}
+
+ /* Set rCostIdx to the cost of visiting selected rows in index. Add
+ ** it to pNew->rRun, which is currently set to the cost of the index
+ ** seek only. Then, if this is a non-covering index, add the cost of
+ ** visiting the rows in the main table. */
+ rCostIdx = pNew->nOut + 1 + (15*pProbe->szIdxRow)/pSrc->pTab->szTabRow;
+ pNew->rRun = sqlite3LogEstAdd(rLogSize, rCostIdx);
+ if( (pNew->wsFlags & (WHERE_IDX_ONLY|WHERE_IPK))==0 ){
+ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, pNew->nOut + 16);
+ }
+ ApplyCostMultiplier(pNew->rRun, pProbe->pTable->costMult);
+
+ nOutUnadjusted = pNew->nOut;
+ pNew->rRun += nInMul + nIn;
+ pNew->nOut += nInMul + nIn;
+ whereLoopOutputAdjust(pBuilder->pWC, pNew, rSize);
+ rc = whereLoopInsert(pBuilder, pNew);
+
+ if( pNew->wsFlags & WHERE_COLUMN_RANGE ){
+ pNew->nOut = saved_nOut;
+ }else{
+ pNew->nOut = nOutUnadjusted;
+ }
+
+ if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
+ && pNew->u.btree.nEq<pProbe->nColumn
+ ){
+ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nInMul+nIn);
+ }
+ pNew->nOut = saved_nOut;
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ pBuilder->nRecValid = nRecValid;
+#endif
}
- *pzAff = zAff;
- return regBase;
+ pNew->prereq = saved_prereq;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->u.btree.nBtm = saved_nBtm;
+ pNew->u.btree.nTop = saved_nTop;
+ pNew->nSkip = saved_nSkip;
+ pNew->wsFlags = saved_wsFlags;
+ pNew->nOut = saved_nOut;
+ pNew->nLTerm = saved_nLTerm;
+
+ /* Consider using a skip-scan if there are no WHERE clause constraints
+ ** available for the left-most terms of the index, and if the average
+ ** number of repeats in the left-most terms is at least 18.
+ **
+ ** The magic number 18 is selected on the basis that scanning 17 rows
+ ** is almost always quicker than an index seek (even though if the index
+ ** contains fewer than 2^17 rows we assume otherwise in other parts of
+ ** the code). And, even if it is not, it should not be too much slower.
+ ** On the other hand, the extra seeks could end up being significantly
+ ** more expensive. */
+ assert( 42==sqlite3LogEst(18) );
+ if( saved_nEq==saved_nSkip
+ && saved_nEq+1<pProbe->nKeyCol
+ && pProbe->noSkipScan==0
+ && pProbe->aiRowLogEst[saved_nEq+1]>=42 /* TUNING: Minimum for skip-scan */
+ && (rc = whereLoopResize(db, pNew, pNew->nLTerm+1))==SQLITE_OK
+ ){
+ LogEst nIter;
+ pNew->u.btree.nEq++;
+ pNew->nSkip++;
+ pNew->aLTerm[pNew->nLTerm++] = 0;
+ pNew->wsFlags |= WHERE_SKIPSCAN;
+ nIter = pProbe->aiRowLogEst[saved_nEq] - pProbe->aiRowLogEst[saved_nEq+1];
+ pNew->nOut -= nIter;
+ /* TUNING: Because uncertainties in the estimates for skip-scan queries,
+ ** add a 1.375 fudge factor to make skip-scan slightly less likely. */
+ nIter += 5;
+ whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, nIter + nInMul);
+ pNew->nOut = saved_nOut;
+ pNew->u.btree.nEq = saved_nEq;
+ pNew->nSkip = saved_nSkip;
+ pNew->wsFlags = saved_wsFlags;
+ }
+
+ WHERETRACE(0x800, ("END addBtreeIdx(%s), nEq=%d, rc=%d\n",
+ pProbe->zName, saved_nEq, rc));
+ return rc;
}
-#ifndef SQLITE_OMIT_EXPLAIN
/*
-** This routine is a helper for explainIndexRange() below
+** Return True if it is possible that pIndex might be useful in
+** implementing the ORDER BY clause in pBuilder.
**
-** pStr holds the text of an expression that we are building up one term
-** at a time. This routine adds a new term to the end of the expression.
-** Terms are separated by AND so add the "AND" text for second and subsequent
-** terms only.
+** Return False if pBuilder does not contain an ORDER BY clause or
+** if there is no way for pIndex to be useful in implementing that
+** ORDER BY clause.
*/
-static void explainAppendTerm(
- StrAccum *pStr, /* The text expression being built */
- int iTerm, /* Index of this term. First is zero */
- const char *zColumn, /* Name of the column */
- const char *zOp /* Name of the operator */
+static int indexMightHelpWithOrderBy(
+ WhereLoopBuilder *pBuilder,
+ Index *pIndex,
+ int iCursor
){
- if( iTerm ) sqlite3StrAccumAppend(pStr, " AND ", 5);
- sqlite3StrAccumAppend(pStr, zColumn, -1);
- sqlite3StrAccumAppend(pStr, zOp, 1);
- sqlite3StrAccumAppend(pStr, "?", 1);
+ ExprList *pOB;
+ ExprList *aColExpr;
+ int ii, jj;
+
+ if( pIndex->bUnordered ) return 0;
+ if( (pOB = pBuilder->pWInfo->pOrderBy)==0 ) return 0;
+ for(ii=0; ii<pOB->nExpr; ii++){
+ Expr *pExpr = sqlite3ExprSkipCollate(pOB->a[ii].pExpr);
+ if( pExpr->op==TK_COLUMN && pExpr->iTable==iCursor ){
+ if( pExpr->iColumn<0 ) return 1;
+ for(jj=0; jj<pIndex->nKeyCol; jj++){
+ if( pExpr->iColumn==pIndex->aiColumn[jj] ) return 1;
+ }
+ }else if( (aColExpr = pIndex->aColExpr)!=0 ){
+ for(jj=0; jj<pIndex->nKeyCol; jj++){
+ if( pIndex->aiColumn[jj]!=XN_EXPR ) continue;
+ if( sqlite3ExprCompare(pExpr,aColExpr->a[jj].pExpr,iCursor)==0 ){
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
}
/*
-** Argument pLevel describes a strategy for scanning table pTab. This
-** function returns a pointer to a string buffer containing a description
-** of the subset of table rows scanned by the strategy in the form of an
-** SQL expression. Or, if all rows are scanned, NULL is returned.
+** Return a bitmask where 1s indicate that the corresponding column of
+** the table is used by an index. Only the first 63 columns are considered.
+*/
+static Bitmask columnsInIndex(Index *pIdx){
+ Bitmask m = 0;
+ int j;
+ for(j=pIdx->nColumn-1; j>=0; j--){
+ int x = pIdx->aiColumn[j];
+ if( x>=0 ){
+ testcase( x==BMS-1 );
+ testcase( x==BMS-2 );
+ if( x<BMS-1 ) m |= MASKBIT(x);
+ }
+ }
+ return m;
+}
+
+/* Check to see if a partial index with pPartIndexWhere can be used
+** in the current query. Return true if it can be and false if not.
+*/
+static int whereUsablePartialIndex(int iTab, WhereClause *pWC, Expr *pWhere){
+ int i;
+ WhereTerm *pTerm;
+ while( pWhere->op==TK_AND ){
+ if( !whereUsablePartialIndex(iTab,pWC,pWhere->pLeft) ) return 0;
+ pWhere = pWhere->pRight;
+ }
+ for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
+ Expr *pExpr = pTerm->pExpr;
+ if( sqlite3ExprImpliesExpr(pExpr, pWhere, iTab)
+ && (!ExprHasProperty(pExpr, EP_FromJoin) || pExpr->iRightJoinTable==iTab)
+ ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+** Add all WhereLoop objects for a single table of the join where the table
+** is identified by pBuilder->pNew->iTab. That table is guaranteed to be
+** a b-tree table, not a virtual table.
**
-** For example, if the query:
+** The costs (WhereLoop.rRun) of the b-tree loops added by this function
+** are calculated as follows:
**
-** SELECT * FROM t1 WHERE a=1 AND b>2;
+** For a full scan, assuming the table (or index) contains nRow rows:
**
-** is run and there is an index on (a, b), then this function returns a
-** string similar to:
+** cost = nRow * 3.0 // full-table scan
+** cost = nRow * K // scan of covering index
+** cost = nRow * (K+3.0) // scan of non-covering index
**
-** "a=? AND b>?"
+** where K is a value between 1.1 and 3.0 set based on the relative
+** estimated average size of the index and table records.
+**
+** For an index scan, where nVisit is the number of index rows visited
+** by the scan, and nSeek is the number of seek operations required on
+** the index b-tree:
+**
+** cost = nSeek * (log(nRow) + K * nVisit) // covering index
+** cost = nSeek * (log(nRow) + (K+3.0) * nVisit) // non-covering index
+**
+** Normally, nSeek is 1. nSeek values greater than 1 come about if the
+** WHERE clause includes "x IN (....)" terms used in place of "x=?". Or when
+** implicit "x IN (SELECT x FROM tbl)" terms are added for skip-scans.
**
-** The returned pointer points to memory obtained from sqlite3DbMalloc().
-** It is the responsibility of the caller to free the buffer when it is
-** no longer required.
+** The estimated values (nRow, nVisit, nSeek) often contain a large amount
+** of uncertainty. For this reason, scoring is designed to pick plans that
+** "do the least harm" if the estimates are inaccurate. For example, a
+** log(nRow) factor is omitted from a non-covering index scan in order to
+** bias the scoring in favor of using an index, since the worst-case
+** performance of using an index is far better than the worst-case performance
+** of a full table scan.
*/
-static char *explainIndexRange(sqlite3 *db, WhereLevel *pLevel, Table *pTab){
- WherePlan *pPlan = &pLevel->plan;
- Index *pIndex = pPlan->u.pIdx;
- int nEq = pPlan->nEq;
- int i, j;
- Column *aCol = pTab->aCol;
- int *aiColumn = pIndex->aiColumn;
- StrAccum txt;
-
- if( nEq==0 && (pPlan->wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))==0 ){
- return 0;
- }
- sqlite3StrAccumInit(&txt, 0, 0, SQLITE_MAX_LENGTH);
- txt.db = db;
- sqlite3StrAccumAppend(&txt, " (", 2);
- for(i=0; i<nEq; i++){
- explainAppendTerm(&txt, i, aCol[aiColumn[i]].zName, "=");
+static int whereLoopAddBtree(
+ WhereLoopBuilder *pBuilder, /* WHERE clause information */
+ Bitmask mPrereq /* Extra prerequesites for using this table */
+){
+ WhereInfo *pWInfo; /* WHERE analysis context */
+ Index *pProbe; /* An index we are evaluating */
+ Index sPk; /* A fake index object for the primary key */
+ LogEst aiRowEstPk[2]; /* The aiRowLogEst[] value for the sPk index */
+ i16 aiColumnPk = -1; /* The aColumn[] value for the sPk index */
+ SrcList *pTabList; /* The FROM clause */
+ struct SrcList_item *pSrc; /* The FROM clause btree term to add */
+ WhereLoop *pNew; /* Template WhereLoop object */
+ int rc = SQLITE_OK; /* Return code */
+ int iSortIdx = 1; /* Index number */
+ int b; /* A boolean value */
+ LogEst rSize; /* number of rows in the table */
+ LogEst rLogSize; /* Logarithm of the number of rows in the table */
+ WhereClause *pWC; /* The parsed WHERE clause */
+ Table *pTab; /* Table being queried */
+
+ pNew = pBuilder->pNew;
+ pWInfo = pBuilder->pWInfo;
+ pTabList = pWInfo->pTabList;
+ pSrc = pTabList->a + pNew->iTab;
+ pTab = pSrc->pTab;
+ pWC = pBuilder->pWC;
+ assert( !IsVirtual(pSrc->pTab) );
+
+ if( pSrc->pIBIndex ){
+ /* An INDEXED BY clause specifies a particular index to use */
+ pProbe = pSrc->pIBIndex;
+ }else if( !HasRowid(pTab) ){
+ pProbe = pTab->pIndex;
+ }else{
+ /* There is no INDEXED BY clause. Create a fake Index object in local
+ ** variable sPk to represent the rowid primary key index. Make this
+ ** fake index the first in a chain of Index objects with all of the real
+ ** indices to follow */
+ Index *pFirst; /* First of real indices on the table */
+ memset(&sPk, 0, sizeof(Index));
+ sPk.nKeyCol = 1;
+ sPk.nColumn = 1;
+ sPk.aiColumn = &aiColumnPk;
+ sPk.aiRowLogEst = aiRowEstPk;
+ sPk.onError = OE_Replace;
+ sPk.pTable = pTab;
+ sPk.szIdxRow = pTab->szTabRow;
+ aiRowEstPk[0] = pTab->nRowLogEst;
+ aiRowEstPk[1] = 0;
+ pFirst = pSrc->pTab->pIndex;
+ if( pSrc->fg.notIndexed==0 ){
+ /* The real indices of the table are only considered if the
+ ** NOT INDEXED qualifier is omitted from the FROM clause */
+ sPk.pNext = pFirst;
+ }
+ pProbe = &sPk;
}
+ rSize = pTab->nRowLogEst;
+ rLogSize = estLog(rSize);
- j = i;
- if( pPlan->wsFlags&WHERE_BTM_LIMIT ){
- explainAppendTerm(&txt, i++, aCol[aiColumn[j]].zName, ">");
+#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+ /* Automatic indexes */
+ if( !pBuilder->pOrSet /* Not part of an OR optimization */
+ && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
+ && (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
+ && pSrc->pIBIndex==0 /* Has no INDEXED BY clause */
+ && !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
+ && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
+ && !pSrc->fg.isCorrelated /* Not a correlated subquery */
+ && !pSrc->fg.isRecursive /* Not a recursive common table expression. */
+ ){
+ /* Generate auto-index WhereLoops */
+ WhereTerm *pTerm;
+ WhereTerm *pWCEnd = pWC->a + pWC->nTerm;
+ for(pTerm=pWC->a; rc==SQLITE_OK && pTerm<pWCEnd; pTerm++){
+ if( pTerm->prereqRight & pNew->maskSelf ) continue;
+ if( termCanDriveIndex(pTerm, pSrc, 0) ){
+ pNew->u.btree.nEq = 1;
+ pNew->nSkip = 0;
+ pNew->u.btree.pIndex = 0;
+ pNew->nLTerm = 1;
+ pNew->aLTerm[0] = pTerm;
+ /* TUNING: One-time cost for computing the automatic index is
+ ** estimated to be X*N*log2(N) where N is the number of rows in
+ ** the table being indexed and where X is 7 (LogEst=28) for normal
+ ** tables or 1.375 (LogEst=4) for views and subqueries. The value
+ ** of X is smaller for views and subqueries so that the query planner
+ ** will be more aggressive about generating automatic indexes for
+ ** those objects, since there is no opportunity to add schema
+ ** indexes on subqueries and views. */
+ pNew->rSetup = rLogSize + rSize + 4;
+ if( pTab->pSelect==0 && (pTab->tabFlags & TF_Ephemeral)==0 ){
+ pNew->rSetup += 24;
+ }
+ ApplyCostMultiplier(pNew->rSetup, pTab->costMult);
+ if( pNew->rSetup<0 ) pNew->rSetup = 0;
+ /* TUNING: Each index lookup yields 20 rows in the table. This
+ ** is more than the usual guess of 10 rows, since we have no way
+ ** of knowing how selective the index will ultimately be. It would
+ ** not be unreasonable to make this value much larger. */
+ pNew->nOut = 43; assert( 43==sqlite3LogEst(20) );
+ pNew->rRun = sqlite3LogEstAdd(rLogSize,pNew->nOut);
+ pNew->wsFlags = WHERE_AUTO_INDEX;
+ pNew->prereq = mPrereq | pTerm->prereqRight;
+ rc = whereLoopInsert(pBuilder, pNew);
+ }
+ }
}
- if( pPlan->wsFlags&WHERE_TOP_LIMIT ){
- explainAppendTerm(&txt, i, aCol[aiColumn[j]].zName, "<");
+#endif /* SQLITE_OMIT_AUTOMATIC_INDEX */
+
+ /* Loop over all indices
+ */
+ for(; rc==SQLITE_OK && pProbe; pProbe=pProbe->pNext, iSortIdx++){
+ if( pProbe->pPartIdxWhere!=0
+ && !whereUsablePartialIndex(pSrc->iCursor, pWC, pProbe->pPartIdxWhere) ){
+ testcase( pNew->iTab!=pSrc->iCursor ); /* See ticket [98d973b8f5] */
+ continue; /* Partial index inappropriate for this query */
+ }
+ rSize = pProbe->aiRowLogEst[0];
+ pNew->u.btree.nEq = 0;
+ pNew->u.btree.nBtm = 0;
+ pNew->u.btree.nTop = 0;
+ pNew->nSkip = 0;
+ pNew->nLTerm = 0;
+ pNew->iSortIdx = 0;
+ pNew->rSetup = 0;
+ pNew->prereq = mPrereq;
+ pNew->nOut = rSize;
+ pNew->u.btree.pIndex = pProbe;
+ b = indexMightHelpWithOrderBy(pBuilder, pProbe, pSrc->iCursor);
+ /* The ONEPASS_DESIRED flags never occurs together with ORDER BY */
+ assert( (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || b==0 );
+ if( pProbe->tnum<=0 ){
+ /* Integer primary key index */
+ pNew->wsFlags = WHERE_IPK;
+
+ /* Full table scan */
+ pNew->iSortIdx = b ? iSortIdx : 0;
+ /* TUNING: Cost of full table scan is (N*3.0). */
+ pNew->rRun = rSize + 16;
+ ApplyCostMultiplier(pNew->rRun, pTab->costMult);
+ whereLoopOutputAdjust(pWC, pNew, rSize);
+ rc = whereLoopInsert(pBuilder, pNew);
+ pNew->nOut = rSize;
+ if( rc ) break;
+ }else{
+ Bitmask m;
+ if( pProbe->isCovering ){
+ pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
+ m = 0;
+ }else{
+ m = pSrc->colUsed & ~columnsInIndex(pProbe);
+ pNew->wsFlags = (m==0) ? (WHERE_IDX_ONLY|WHERE_INDEXED) : WHERE_INDEXED;
+ }
+
+ /* Full scan via index */
+ if( b
+ || !HasRowid(pTab)
+ || pProbe->pPartIdxWhere!=0
+ || ( m==0
+ && pProbe->bUnordered==0
+ && (pProbe->szIdxRow<pTab->szTabRow)
+ && (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0
+ && sqlite3GlobalConfig.bUseCis
+ && OptimizationEnabled(pWInfo->pParse->db, SQLITE_CoverIdxScan)
+ )
+ ){
+ pNew->iSortIdx = b ? iSortIdx : 0;
+
+ /* The cost of visiting the index rows is N*K, where K is
+ ** between 1.1 and 3.0, depending on the relative sizes of the
+ ** index and table rows. */
+ pNew->rRun = rSize + 1 + (15*pProbe->szIdxRow)/pTab->szTabRow;
+ if( m!=0 ){
+ /* If this is a non-covering index scan, add in the cost of
+ ** doing table lookups. The cost will be 3x the number of
+ ** lookups. Take into account WHERE clause terms that can be
+ ** satisfied using just the index, and that do not require a
+ ** table lookup. */
+ LogEst nLookup = rSize + 16; /* Base cost: N*3 */
+ int ii;
+ int iCur = pSrc->iCursor;
+ WhereClause *pWC2 = &pWInfo->sWC;
+ for(ii=0; ii<pWC2->nTerm; ii++){
+ WhereTerm *pTerm = &pWC2->a[ii];
+ if( !sqlite3ExprCoveredByIndex(pTerm->pExpr, iCur, pProbe) ){
+ break;
+ }
+ /* pTerm can be evaluated using just the index. So reduce
+ ** the expected number of table lookups accordingly */
+ if( pTerm->truthProb<=0 ){
+ nLookup += pTerm->truthProb;
+ }else{
+ nLookup--;
+ if( pTerm->eOperator & (WO_EQ|WO_IS) ) nLookup -= 19;
+ }
+ }
+
+ pNew->rRun = sqlite3LogEstAdd(pNew->rRun, nLookup);
+ }
+ ApplyCostMultiplier(pNew->rRun, pTab->costMult);
+ whereLoopOutputAdjust(pWC, pNew, rSize);
+ rc = whereLoopInsert(pBuilder, pNew);
+ pNew->nOut = rSize;
+ if( rc ) break;
+ }
+ }
+
+ rc = whereLoopAddBtreeIndex(pBuilder, pSrc, pProbe, 0);
+#ifdef SQLITE_ENABLE_STAT3_OR_STAT4
+ sqlite3Stat4ProbeFree(pBuilder->pRec);
+ pBuilder->nRecValid = 0;
+ pBuilder->pRec = 0;
+#endif
+
+ /* If there was an INDEXED BY clause, then only that one index is
+ ** considered. */
+ if( pSrc->pIBIndex ) break;
}
- sqlite3StrAccumAppend(&txt, ")", 1);
- return sqlite3StrAccumFinish(&txt);
+ return rc;
}
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+
/*
-** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
-** command. If the query being compiled is an EXPLAIN QUERY PLAN, a single
-** record is added to the output to describe the table scan strategy in
-** pLevel.
+** Argument pIdxInfo is already populated with all constraints that may
+** be used by the virtual table identified by pBuilder->pNew->iTab. This
+** function marks a subset of those constraints usable, invokes the
+** xBestIndex method and adds the returned plan to pBuilder.
+**
+** A constraint is marked usable if:
+**
+** * Argument mUsable indicates that its prerequisites are available, and
+**
+** * It is not one of the operators specified in the mExclude mask passed
+** as the fourth argument (which in practice is either WO_IN or 0).
+**
+** Argument mPrereq is a mask of tables that must be scanned before the
+** virtual table in question. These are added to the plans prerequisites
+** before it is added to pBuilder.
+**
+** Output parameter *pbIn is set to true if the plan added to pBuilder
+** uses one or more WO_IN terms, or false otherwise.
*/
-static void explainOneScan(
- Parse *pParse, /* Parse context */
- SrcList *pTabList, /* Table list this loop refers to */
- WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
- int iLevel, /* Value for "level" column of output */
- int iFrom, /* Value for "from" column of output */
- u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
+static int whereLoopAddVirtualOne(
+ WhereLoopBuilder *pBuilder,
+ Bitmask mPrereq, /* Mask of tables that must be used. */
+ Bitmask mUsable, /* Mask of usable tables */
+ u16 mExclude, /* Exclude terms using these operators */
+ sqlite3_index_info *pIdxInfo, /* Populated object for xBestIndex */
+ u16 mNoOmit, /* Do not omit these constraints */
+ int *pbIn /* OUT: True if plan uses an IN(...) op */
){
- if( pParse->explain==2 ){
- u32 flags = pLevel->plan.wsFlags;
- struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
- Vdbe *v = pParse->pVdbe; /* VM being constructed */
- sqlite3 *db = pParse->db; /* Database handle */
- char *zMsg; /* Text to add to EQP output */
- sqlite3_int64 nRow; /* Expected number of rows visited by scan */
- int iId = pParse->iSelectId; /* Select id (left-most output column) */
- int isSearch; /* True for a SEARCH. False for SCAN. */
-
- if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_ONETABLE_ONLY) ) return;
+ WhereClause *pWC = pBuilder->pWC;
+ struct sqlite3_index_constraint *pIdxCons;
+ struct sqlite3_index_constraint_usage *pUsage = pIdxInfo->aConstraintUsage;
+ int i;
+ int mxTerm;
+ int rc = SQLITE_OK;
+ WhereLoop *pNew = pBuilder->pNew;
+ Parse *pParse = pBuilder->pWInfo->pParse;
+ struct SrcList_item *pSrc = &pBuilder->pWInfo->pTabList->a[pNew->iTab];
+ int nConstraint = pIdxInfo->nConstraint;
- isSearch = (pLevel->plan.nEq>0)
- || (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
- || (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
+ assert( (mUsable & mPrereq)==mPrereq );
+ *pbIn = 0;
+ pNew->prereq = mPrereq;
- zMsg = sqlite3MPrintf(db, "%s", isSearch?"SEARCH":"SCAN");
- if( pItem->pSelect ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s SUBQUERY %d", zMsg,pItem->iSelectId);
- }else{
- zMsg = sqlite3MAppendf(db, zMsg, "%s TABLE %s", zMsg, pItem->zName);
+ /* Set the usable flag on the subset of constraints identified by
+ ** arguments mUsable and mExclude. */
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ WhereTerm *pTerm = &pWC->a[pIdxCons->iTermOffset];
+ pIdxCons->usable = 0;
+ if( (pTerm->prereqRight & mUsable)==pTerm->prereqRight
+ && (pTerm->eOperator & mExclude)==0
+ ){
+ pIdxCons->usable = 1;
}
+ }
- if( pItem->zAlias ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s AS %s", zMsg, pItem->zAlias);
- }
- if( (flags & WHERE_INDEXED)!=0 ){
- char *zWhere = explainIndexRange(db, pLevel, pItem->pTab);
- zMsg = sqlite3MAppendf(db, zMsg, "%s USING %s%sINDEX%s%s%s", zMsg,
- ((flags & WHERE_TEMP_INDEX)?"AUTOMATIC ":""),
- ((flags & WHERE_IDX_ONLY)?"COVERING ":""),
- ((flags & WHERE_TEMP_INDEX)?"":" "),
- ((flags & WHERE_TEMP_INDEX)?"": pLevel->plan.u.pIdx->zName),
- zWhere
- );
- sqlite3DbFree(db, zWhere);
- }else if( flags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s USING INTEGER PRIMARY KEY", zMsg);
+ /* Initialize the output fields of the sqlite3_index_info structure */
+ memset(pUsage, 0, sizeof(pUsage[0])*nConstraint);
+ assert( pIdxInfo->needToFreeIdxStr==0 );
+ pIdxInfo->idxStr = 0;
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->estimatedCost = SQLITE_BIG_DBL / (double)2;
+ pIdxInfo->estimatedRows = 25;
+ pIdxInfo->idxFlags = 0;
+ pIdxInfo->colUsed = (sqlite3_int64)pSrc->colUsed;
- if( flags&WHERE_ROWID_EQ ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid=?)", zMsg);
- }else if( (flags&WHERE_BOTH_LIMIT)==WHERE_BOTH_LIMIT ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>? AND rowid<?)", zMsg);
- }else if( flags&WHERE_BTM_LIMIT ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid>?)", zMsg);
- }else if( flags&WHERE_TOP_LIMIT ){
- zMsg = sqlite3MAppendf(db, zMsg, "%s (rowid<?)", zMsg);
+ /* Invoke the virtual table xBestIndex() method */
+ rc = vtabBestIndex(pParse, pSrc->pTab, pIdxInfo);
+ if( rc ) return rc;
+
+ mxTerm = -1;
+ assert( pNew->nLSlot>=nConstraint );
+ for(i=0; i<nConstraint; i++) pNew->aLTerm[i] = 0;
+ pNew->u.vtab.omitMask = 0;
+ pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint;
+ for(i=0; i<nConstraint; i++, pIdxCons++){
+ int iTerm;
+ if( (iTerm = pUsage[i].argvIndex - 1)>=0 ){
+ WhereTerm *pTerm;
+ int j = pIdxCons->iTermOffset;
+ if( iTerm>=nConstraint
+ || j<0
+ || j>=pWC->nTerm
+ || pNew->aLTerm[iTerm]!=0
+ || pIdxCons->usable==0
+ ){
+ rc = SQLITE_ERROR;
+ sqlite3ErrorMsg(pParse,"%s.xBestIndex malfunction",pSrc->pTab->zName);
+ return rc;
}
- }
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- else if( (flags & WHERE_VIRTUALTABLE)!=0 ){
- sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
- zMsg = sqlite3MAppendf(db, zMsg, "%s VIRTUAL TABLE INDEX %d:%s", zMsg,
- pVtabIdx->idxNum, pVtabIdx->idxStr);
- }
-#endif
- if( wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX) ){
- testcase( wctrlFlags & WHERE_ORDERBY_MIN );
- nRow = 1;
- }else{
- nRow = (sqlite3_int64)pLevel->plan.nRow;
- }
- zMsg = sqlite3MAppendf(db, zMsg, "%s (~%lld rows)", zMsg, nRow);
- sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg, P4_DYNAMIC);
+ testcase( iTerm==nConstraint-1 );
+ testcase( j==0 );
+ testcase( j==pWC->nTerm-1 );
+ pTerm = &pWC->a[j];
+ pNew->prereq |= pTerm->prereqRight;
+ assert( iTerm<pNew->nLSlot );
+ pNew->aLTerm[iTerm] = pTerm;
+ if( iTerm>mxTerm ) mxTerm = iTerm;
+ testcase( iTerm==15 );
+ testcase( iTerm==16 );
+ if( iTerm<16 && pUsage[i].omit ) pNew->u.vtab.omitMask |= 1<<iTerm;
+ if( (pTerm->eOperator & WO_IN)!=0 ){
+ /* A virtual table that is constrained by an IN clause may not
+ ** consume the ORDER BY clause because (1) the order of IN terms
+ ** is not necessarily related to the order of output terms and
+ ** (2) Multiple outputs from a single IN value will not merge
+ ** together. */
+ pIdxInfo->orderByConsumed = 0;
+ pIdxInfo->idxFlags &= ~SQLITE_INDEX_SCAN_UNIQUE;
+ *pbIn = 1; assert( (mExclude & WO_IN)==0 );
+ }
+ }
+ }
+ pNew->u.vtab.omitMask &= ~mNoOmit;
+
+ pNew->nLTerm = mxTerm+1;
+ assert( pNew->nLTerm<=pNew->nLSlot );
+ pNew->u.vtab.idxNum = pIdxInfo->idxNum;
+ pNew->u.vtab.needFree = pIdxInfo->needToFreeIdxStr;
+ pIdxInfo->needToFreeIdxStr = 0;
+ pNew->u.vtab.idxStr = pIdxInfo->idxStr;
+ pNew->u.vtab.isOrdered = (i8)(pIdxInfo->orderByConsumed ?
+ pIdxInfo->nOrderBy : 0);
+ pNew->rSetup = 0;
+ pNew->rRun = sqlite3LogEstFromDouble(pIdxInfo->estimatedCost);
+ pNew->nOut = sqlite3LogEst(pIdxInfo->estimatedRows);
+
+ /* Set the WHERE_ONEROW flag if the xBestIndex() method indicated
+ ** that the scan will visit at most one row. Clear it otherwise. */
+ if( pIdxInfo->idxFlags & SQLITE_INDEX_SCAN_UNIQUE ){
+ pNew->wsFlags |= WHERE_ONEROW;
+ }else{
+ pNew->wsFlags &= ~WHERE_ONEROW;
}
+ rc = whereLoopInsert(pBuilder, pNew);
+ if( pNew->u.vtab.needFree ){
+ sqlite3_free(pNew->u.vtab.idxStr);
+ pNew->u.vtab.needFree = 0;
+ }
+ WHERETRACE(0xffff, (" bIn=%d prereqIn=%04llx prereqOut=%04llx\n",
+ *pbIn, (sqlite3_uint64)mPrereq,
+ (sqlite3_uint64)(pNew->prereq & ~mPrereq)));
+
+ return rc;
}
-#else
-# define explainOneScan(u,v,w,x,y,z)
-#endif /* SQLITE_OMIT_EXPLAIN */
/*
-** Generate code for the start of the iLevel-th loop in the WHERE clause
-** implementation described by pWInfo.
+** Add all WhereLoop objects for a table of the join identified by
+** pBuilder->pNew->iTab. That table is guaranteed to be a virtual table.
+**
+** If there are no LEFT or CROSS JOIN joins in the query, both mPrereq and
+** mUnusable are set to 0. Otherwise, mPrereq is a mask of all FROM clause
+** entries that occur before the virtual table in the FROM clause and are
+** separated from it by at least one LEFT or CROSS JOIN. Similarly, the
+** mUnusable mask contains all FROM clause entries that occur after the
+** virtual table and are separated from it by at least one LEFT or
+** CROSS JOIN.
+**
+** For example, if the query were:
+**
+** ... FROM t1, t2 LEFT JOIN t3, t4, vt CROSS JOIN t5, t6;
+**
+** then mPrereq corresponds to (t1, t2) and mUnusable to (t5, t6).
+**
+** All the tables in mPrereq must be scanned before the current virtual
+** table. So any terms for which all prerequisites are satisfied by
+** mPrereq may be specified as "usable" in all calls to xBestIndex.
+** Conversely, all tables in mUnusable must be scanned after the current
+** virtual table, so any terms for which the prerequisites overlap with
+** mUnusable should always be configured as "not-usable" for xBestIndex.
*/
-static Bitmask codeOneLoopStart(
- WhereInfo *pWInfo, /* Complete information about the WHERE clause */
- int iLevel, /* Which level of pWInfo->a[] should be coded */
- u16 wctrlFlags, /* One of the WHERE_* flags defined in sqliteInt.h */
- Bitmask notReady /* Which tables are currently available */
+static int whereLoopAddVirtual(
+ WhereLoopBuilder *pBuilder, /* WHERE clause information */
+ Bitmask mPrereq, /* Tables that must be scanned before this one */
+ Bitmask mUnusable /* Tables that must be scanned after this one */
){
- int j, k; /* Loop counters */
- int iCur; /* The VDBE cursor for the table */
- int addrNxt; /* Where to jump to continue with the next IN case */
- int omitTable; /* True if we use the index only */
- int bRev; /* True if we need to scan in reverse order */
- WhereLevel *pLevel; /* The where level to be coded */
- WhereClause *pWC; /* Decomposition of the entire WHERE clause */
- WhereTerm *pTerm; /* A WHERE clause term */
- Parse *pParse; /* Parsing context */
- Vdbe *v; /* The prepared stmt under constructions */
- struct SrcList_item *pTabItem; /* FROM clause term being coded */
- int addrBrk; /* Jump here to break out of the loop */
- int addrCont; /* Jump here to continue with next cycle */
- int iRowidReg = 0; /* Rowid is stored in this register, if not zero */
- int iReleaseReg = 0; /* Temp register to free before returning */
-
+ int rc = SQLITE_OK; /* Return code */
+ WhereInfo *pWInfo; /* WHERE analysis context */
+ Parse *pParse; /* The parsing context */
+ WhereClause *pWC; /* The WHERE clause */
+ struct SrcList_item *pSrc; /* The FROM clause term to search */
+ sqlite3_index_info *p; /* Object to pass to xBestIndex() */
+ int nConstraint; /* Number of constraints in p */
+ int bIn; /* True if plan uses IN(...) operator */
+ WhereLoop *pNew;
+ Bitmask mBest; /* Tables used by best possible plan */
+ u16 mNoOmit;
+
+ assert( (mPrereq & mUnusable)==0 );
+ pWInfo = pBuilder->pWInfo;
pParse = pWInfo->pParse;
- v = pParse->pVdbe;
- pWC = pWInfo->pWC;
- pLevel = &pWInfo->a[iLevel];
- pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
- iCur = pTabItem->iCursor;
- bRev = (pLevel->plan.wsFlags & WHERE_REVERSE)!=0;
- omitTable = (pLevel->plan.wsFlags & WHERE_IDX_ONLY)!=0
- && (wctrlFlags & WHERE_FORCE_TABLE)==0;
+ pWC = pBuilder->pWC;
+ pNew = pBuilder->pNew;
+ pSrc = &pWInfo->pTabList->a[pNew->iTab];
+ assert( IsVirtual(pSrc->pTab) );
+ p = allocateIndexInfo(pParse, pWC, mUnusable, pSrc, pBuilder->pOrderBy,
+ &mNoOmit);
+ if( p==0 ) return SQLITE_NOMEM_BKPT;
+ pNew->rSetup = 0;
+ pNew->wsFlags = WHERE_VIRTUALTABLE;
+ pNew->nLTerm = 0;
+ pNew->u.vtab.needFree = 0;
+ nConstraint = p->nConstraint;
+ if( whereLoopResize(pParse->db, pNew, nConstraint) ){
+ sqlite3DbFree(pParse->db, p);
+ return SQLITE_NOMEM_BKPT;
+ }
+
+ /* First call xBestIndex() with all constraints usable. */
+ WHERETRACE(0x40, (" VirtualOne: all usable\n"));
+ rc = whereLoopAddVirtualOne(pBuilder, mPrereq, ALLBITS, 0, p, mNoOmit, &bIn);
+
+ /* If the call to xBestIndex() with all terms enabled produced a plan
+ ** that does not require any source tables (IOW: a plan with mBest==0),
+ ** then there is no point in making any further calls to xBestIndex()
+ ** since they will all return the same result (if the xBestIndex()
+ ** implementation is sane). */
+ if( rc==SQLITE_OK && (mBest = (pNew->prereq & ~mPrereq))!=0 ){
+ int seenZero = 0; /* True if a plan with no prereqs seen */
+ int seenZeroNoIN = 0; /* Plan with no prereqs and no IN(...) seen */
+ Bitmask mPrev = 0;
+ Bitmask mBestNoIn = 0;
+
+ /* If the plan produced by the earlier call uses an IN(...) term, call
+ ** xBestIndex again, this time with IN(...) terms disabled. */
+ if( bIn ){
+ WHERETRACE(0x40, (" VirtualOne: all usable w/o IN\n"));
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, ALLBITS, WO_IN, p, mNoOmit, &bIn);
+ assert( bIn==0 );
+ mBestNoIn = pNew->prereq & ~mPrereq;
+ if( mBestNoIn==0 ){
+ seenZero = 1;
+ seenZeroNoIN = 1;
+ }
+ }
+
+ /* Call xBestIndex once for each distinct value of (prereqRight & ~mPrereq)
+ ** in the set of terms that apply to the current virtual table. */
+ while( rc==SQLITE_OK ){
+ int i;
+ Bitmask mNext = ALLBITS;
+ assert( mNext>0 );
+ for(i=0; i<nConstraint; i++){
+ Bitmask mThis = (
+ pWC->a[p->aConstraint[i].iTermOffset].prereqRight & ~mPrereq
+ );
+ if( mThis>mPrev && mThis<mNext ) mNext = mThis;
+ }
+ mPrev = mNext;
+ if( mNext==ALLBITS ) break;
+ if( mNext==mBest || mNext==mBestNoIn ) continue;
+ WHERETRACE(0x40, (" VirtualOne: mPrev=%04llx mNext=%04llx\n",
+ (sqlite3_uint64)mPrev, (sqlite3_uint64)mNext));
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, mNext|mPrereq, 0, p, mNoOmit, &bIn);
+ if( pNew->prereq==mPrereq ){
+ seenZero = 1;
+ if( bIn==0 ) seenZeroNoIN = 1;
+ }
+ }
- /* Create labels for the "break" and "continue" instructions
- ** for the current loop. Jump to addrBrk to break out of a loop.
- ** Jump to cont to go immediately to the next iteration of the
- ** loop.
- **
- ** When there is an IN operator, we also have a "addrNxt" label that
- ** means to continue with the next IN value combination. When
- ** there are no IN operators in the constraints, the "addrNxt" label
- ** is the same as "addrBrk".
- */
- addrBrk = pLevel->addrBrk = pLevel->addrNxt = sqlite3VdbeMakeLabel(v);
- addrCont = pLevel->addrCont = sqlite3VdbeMakeLabel(v);
+ /* If the calls to xBestIndex() in the above loop did not find a plan
+ ** that requires no source tables at all (i.e. one guaranteed to be
+ ** usable), make a call here with all source tables disabled */
+ if( rc==SQLITE_OK && seenZero==0 ){
+ WHERETRACE(0x40, (" VirtualOne: all disabled\n"));
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, mPrereq, 0, p, mNoOmit, &bIn);
+ if( bIn==0 ) seenZeroNoIN = 1;
+ }
- /* If this is the right table of a LEFT OUTER JOIN, allocate and
- ** initialize a memory cell that records if this table matches any
- ** row of the left table of the join.
- */
- if( pLevel->iFrom>0 && (pTabItem[0].jointype & JT_LEFT)!=0 ){
- pLevel->iLeftJoin = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Integer, 0, pLevel->iLeftJoin);
- VdbeComment((v, "init LEFT JOIN no-match flag"));
+ /* If the calls to xBestIndex() have so far failed to find a plan
+ ** that requires no source tables at all and does not use an IN(...)
+ ** operator, make a final call to obtain one here. */
+ if( rc==SQLITE_OK && seenZeroNoIN==0 ){
+ WHERETRACE(0x40, (" VirtualOne: all disabled and w/o IN\n"));
+ rc = whereLoopAddVirtualOne(
+ pBuilder, mPrereq, mPrereq, WO_IN, p, mNoOmit, &bIn);
+ }
}
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
- /* Case 0: The table is a virtual-table. Use the VFilter and VNext
- ** to access the data.
- */
- int iReg; /* P3 Value for OP_VFilter */
- sqlite3_index_info *pVtabIdx = pLevel->plan.u.pVtabIdx;
- int nConstraint = pVtabIdx->nConstraint;
- struct sqlite3_index_constraint_usage *aUsage =
- pVtabIdx->aConstraintUsage;
- const struct sqlite3_index_constraint *aConstraint =
- pVtabIdx->aConstraint;
+ if( p->needToFreeIdxStr ) sqlite3_free(p->idxStr);
+ sqlite3DbFree(pParse->db, p);
+ return rc;
+}
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
- sqlite3ExprCachePush(pParse);
- iReg = sqlite3GetTempRange(pParse, nConstraint+2);
- for(j=1; j<=nConstraint; j++){
- for(k=0; k<nConstraint; k++){
- if( aUsage[k].argvIndex==j ){
- int iTerm = aConstraint[k].iTermOffset;
- sqlite3ExprCode(pParse, pWC->a[iTerm].pExpr->pRight, iReg+j+1);
+/*
+** Add WhereLoop entries to handle OR terms. This works for either
+** btrees or virtual tables.
+*/
+static int whereLoopAddOr(
+ WhereLoopBuilder *pBuilder,
+ Bitmask mPrereq,
+ Bitmask mUnusable
+){
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ WhereClause *pWC;
+ WhereLoop *pNew;
+ WhereTerm *pTerm, *pWCEnd;
+ int rc = SQLITE_OK;
+ int iCur;
+ WhereClause tempWC;
+ WhereLoopBuilder sSubBuild;
+ WhereOrSet sSum, sCur;
+ struct SrcList_item *pItem;
+
+ pWC = pBuilder->pWC;
+ pWCEnd = pWC->a + pWC->nTerm;
+ pNew = pBuilder->pNew;
+ memset(&sSum, 0, sizeof(sSum));
+ pItem = pWInfo->pTabList->a + pNew->iTab;
+ iCur = pItem->iCursor;
+
+ for(pTerm=pWC->a; pTerm<pWCEnd && rc==SQLITE_OK; pTerm++){
+ if( (pTerm->eOperator & WO_OR)!=0
+ && (pTerm->u.pOrInfo->indexable & pNew->maskSelf)!=0
+ ){
+ WhereClause * const pOrWC = &pTerm->u.pOrInfo->wc;
+ WhereTerm * const pOrWCEnd = &pOrWC->a[pOrWC->nTerm];
+ WhereTerm *pOrTerm;
+ int once = 1;
+ int i, j;
+
+ sSubBuild = *pBuilder;
+ sSubBuild.pOrderBy = 0;
+ sSubBuild.pOrSet = &sCur;
+
+ WHERETRACE(0x200, ("Begin processing OR-clause %p\n", pTerm));
+ for(pOrTerm=pOrWC->a; pOrTerm<pOrWCEnd; pOrTerm++){
+ if( (pOrTerm->eOperator & WO_AND)!=0 ){
+ sSubBuild.pWC = &pOrTerm->u.pAndInfo->wc;
+ }else if( pOrTerm->leftCursor==iCur ){
+ tempWC.pWInfo = pWC->pWInfo;
+ tempWC.pOuter = pWC;
+ tempWC.op = TK_AND;
+ tempWC.nTerm = 1;
+ tempWC.a = pOrTerm;
+ sSubBuild.pWC = &tempWC;
+ }else{
+ continue;
+ }
+ sCur.n = 0;
+#ifdef WHERETRACE_ENABLED
+ WHERETRACE(0x200, ("OR-term %d of %p has %d subterms:\n",
+ (int)(pOrTerm-pOrWC->a), pTerm, sSubBuild.pWC->nTerm));
+ if( sqlite3WhereTrace & 0x400 ){
+ sqlite3WhereClausePrint(sSubBuild.pWC);
+ }
+#endif
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( IsVirtual(pItem->pTab) ){
+ rc = whereLoopAddVirtual(&sSubBuild, mPrereq, mUnusable);
+ }else
+#endif
+ {
+ rc = whereLoopAddBtree(&sSubBuild, mPrereq);
+ }
+ if( rc==SQLITE_OK ){
+ rc = whereLoopAddOr(&sSubBuild, mPrereq, mUnusable);
+ }
+ assert( rc==SQLITE_OK || sCur.n==0 );
+ if( sCur.n==0 ){
+ sSum.n = 0;
break;
+ }else if( once ){
+ whereOrMove(&sSum, &sCur);
+ once = 0;
+ }else{
+ WhereOrSet sPrev;
+ whereOrMove(&sPrev, &sSum);
+ sSum.n = 0;
+ for(i=0; i<sPrev.n; i++){
+ for(j=0; j<sCur.n; j++){
+ whereOrInsert(&sSum, sPrev.a[i].prereq | sCur.a[j].prereq,
+ sqlite3LogEstAdd(sPrev.a[i].rRun, sCur.a[j].rRun),
+ sqlite3LogEstAdd(sPrev.a[i].nOut, sCur.a[j].nOut));
+ }
+ }
}
}
- if( k==nConstraint ) break;
- }
- sqlite3VdbeAddOp2(v, OP_Integer, pVtabIdx->idxNum, iReg);
- sqlite3VdbeAddOp2(v, OP_Integer, j-1, iReg+1);
- sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrBrk, iReg, pVtabIdx->idxStr,
- pVtabIdx->needToFreeIdxStr ? P4_MPRINTF : P4_STATIC);
- pVtabIdx->needToFreeIdxStr = 0;
- for(j=0; j<nConstraint; j++){
- if( aUsage[j].omit ){
- int iTerm = aConstraint[j].iTermOffset;
- disableTerm(pLevel, &pWC->a[iTerm]);
+ pNew->nLTerm = 1;
+ pNew->aLTerm[0] = pTerm;
+ pNew->wsFlags = WHERE_MULTI_OR;
+ pNew->rSetup = 0;
+ pNew->iSortIdx = 0;
+ memset(&pNew->u, 0, sizeof(pNew->u));
+ for(i=0; rc==SQLITE_OK && i<sSum.n; i++){
+ /* TUNING: Currently sSum.a[i].rRun is set to the sum of the costs
+ ** of all sub-scans required by the OR-scan. However, due to rounding
+ ** errors, it may be that the cost of the OR-scan is equal to its
+ ** most expensive sub-scan. Add the smallest possible penalty
+ ** (equivalent to multiplying the cost by 1.07) to ensure that
+ ** this does not happen. Otherwise, for WHERE clauses such as the
+ ** following where there is an index on "y":
+ **
+ ** WHERE likelihood(x=?, 0.99) OR y=?
+ **
+ ** the planner may elect to "OR" together a full-table scan and an
+ ** index lookup. And other similarly odd results. */
+ pNew->rRun = sSum.a[i].rRun + 1;
+ pNew->nOut = sSum.a[i].nOut;
+ pNew->prereq = sSum.a[i].prereq;
+ rc = whereLoopInsert(pBuilder, pNew);
}
+ WHERETRACE(0x200, ("End processing OR-clause %p\n", pTerm));
}
- pLevel->op = OP_VNext;
- pLevel->p1 = iCur;
- pLevel->p2 = sqlite3VdbeCurrentAddr(v);
- sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2);
- sqlite3ExprCachePop(pParse, 1);
- }else
-#endif /* SQLITE_OMIT_VIRTUALTABLE */
-
- if( pLevel->plan.wsFlags & WHERE_ROWID_EQ ){
- /* Case 1: We can directly reference a single row using an
- ** equality comparison against the ROWID field. Or
- ** we reference multiple rows using a "rowid IN (...)"
- ** construct.
- */
- iReleaseReg = sqlite3GetTempReg(pParse);
- pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0);
- assert( pTerm!=0 );
- assert( pTerm->pExpr!=0 );
- assert( pTerm->leftCursor==iCur );
- assert( omitTable==0 );
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- iRowidReg = codeEqualityTerm(pParse, pTerm, pLevel, iReleaseReg);
- addrNxt = pLevel->addrNxt;
- sqlite3VdbeAddOp2(v, OP_MustBeInt, iRowidReg, addrNxt);
- sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addrNxt, iRowidReg);
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
- VdbeComment((v, "pk"));
- pLevel->op = OP_Noop;
- }else if( pLevel->plan.wsFlags & WHERE_ROWID_RANGE ){
- /* Case 2: We have an inequality comparison against the ROWID field.
- */
- int testOp = OP_Noop;
- int start;
- int memEndValue = 0;
- WhereTerm *pStart, *pEnd;
-
- assert( omitTable==0 );
- pStart = findTerm(pWC, iCur, -1, notReady, WO_GT|WO_GE, 0);
- pEnd = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0);
- if( bRev ){
- pTerm = pStart;
- pStart = pEnd;
- pEnd = pTerm;
- }
- if( pStart ){
- Expr *pX; /* The expression that defines the start bound */
- int r1, rTemp; /* Registers for holding the start boundary */
-
- /* The following constant maps TK_xx codes into corresponding
- ** seek opcodes. It depends on a particular ordering of TK_xx
- */
- const u8 aMoveOp[] = {
- /* TK_GT */ OP_SeekGt,
- /* TK_LE */ OP_SeekLe,
- /* TK_LT */ OP_SeekLt,
- /* TK_GE */ OP_SeekGe
- };
- assert( TK_LE==TK_GT+1 ); /* Make sure the ordering.. */
- assert( TK_LT==TK_GT+2 ); /* ... of the TK_xx values... */
- assert( TK_GE==TK_GT+3 ); /* ... is correcct. */
+ }
+ return rc;
+}
- testcase( pStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- pX = pStart->pExpr;
- assert( pX!=0 );
- assert( pStart->leftCursor==iCur );
- r1 = sqlite3ExprCodeTemp(pParse, pX->pRight, &rTemp);
- sqlite3VdbeAddOp3(v, aMoveOp[pX->op-TK_GT], iCur, addrBrk, r1);
- VdbeComment((v, "pk"));
- sqlite3ExprCacheAffinityChange(pParse, r1, 1);
- sqlite3ReleaseTempReg(pParse, rTemp);
- disableTerm(pLevel, pStart);
- }else{
- sqlite3VdbeAddOp2(v, bRev ? OP_Last : OP_Rewind, iCur, addrBrk);
- }
- if( pEnd ){
- Expr *pX;
- pX = pEnd->pExpr;
- assert( pX!=0 );
- assert( pEnd->leftCursor==iCur );
- testcase( pEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- memEndValue = ++pParse->nMem;
- sqlite3ExprCode(pParse, pX->pRight, memEndValue);
- if( pX->op==TK_LT || pX->op==TK_GT ){
- testOp = bRev ? OP_Le : OP_Ge;
- }else{
- testOp = bRev ? OP_Lt : OP_Gt;
+/*
+** Add all WhereLoop objects for all tables
+*/
+static int whereLoopAddAll(WhereLoopBuilder *pBuilder){
+ WhereInfo *pWInfo = pBuilder->pWInfo;
+ Bitmask mPrereq = 0;
+ Bitmask mPrior = 0;
+ int iTab;
+ SrcList *pTabList = pWInfo->pTabList;
+ struct SrcList_item *pItem;
+ struct SrcList_item *pEnd = &pTabList->a[pWInfo->nLevel];
+ sqlite3 *db = pWInfo->pParse->db;
+ int rc = SQLITE_OK;
+ WhereLoop *pNew;
+ u8 priorJointype = 0;
+
+ /* Loop over the tables in the join, from left to right */
+ pNew = pBuilder->pNew;
+ whereLoopInit(pNew);
+ for(iTab=0, pItem=pTabList->a; pItem<pEnd; iTab++, pItem++){
+ Bitmask mUnusable = 0;
+ pNew->iTab = iTab;
+ pNew->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, pItem->iCursor);
+ if( ((pItem->fg.jointype|priorJointype) & (JT_LEFT|JT_CROSS))!=0 ){
+ /* This condition is true when pItem is the FROM clause term on the
+ ** right-hand-side of a LEFT or CROSS JOIN. */
+ mPrereq = mPrior;
+ }
+ priorJointype = pItem->fg.jointype;
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ if( IsVirtual(pItem->pTab) ){
+ struct SrcList_item *p;
+ for(p=&pItem[1]; p<pEnd; p++){
+ if( mUnusable || (p->fg.jointype & (JT_LEFT|JT_CROSS)) ){
+ mUnusable |= sqlite3WhereGetMask(&pWInfo->sMaskSet, p->iCursor);
+ }
}
- disableTerm(pLevel, pEnd);
- }
- start = sqlite3VdbeCurrentAddr(v);
- pLevel->op = bRev ? OP_Prev : OP_Next;
- pLevel->p1 = iCur;
- pLevel->p2 = start;
- if( pStart==0 && pEnd==0 ){
- pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
- }else{
- assert( pLevel->p5==0 );
+ rc = whereLoopAddVirtual(pBuilder, mPrereq, mUnusable);
+ }else
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+ {
+ rc = whereLoopAddBtree(pBuilder, mPrereq);
}
- if( testOp!=OP_Noop ){
- iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_Rowid, iCur, iRowidReg);
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
- sqlite3VdbeAddOp3(v, testOp, memEndValue, addrBrk, iRowidReg);
- sqlite3VdbeChangeP5(v, SQLITE_AFF_NUMERIC | SQLITE_JUMPIFNULL);
+ if( rc==SQLITE_OK ){
+ rc = whereLoopAddOr(pBuilder, mPrereq, mUnusable);
}
- }else if( pLevel->plan.wsFlags & (WHERE_COLUMN_RANGE|WHERE_COLUMN_EQ) ){
- /* Case 3: A scan using an index.
- **
- ** The WHERE clause may contain zero or more equality
- ** terms ("==" or "IN" operators) that refer to the N
- ** left-most columns of the index. It may also contain
- ** inequality constraints (>, <, >= or <=) on the indexed
- ** column that immediately follows the N equalities. Only
- ** the right-most column can be an inequality - the rest must
- ** use the "==" and "IN" operators. For example, if the
- ** index is on (x,y,z), then the following clauses are all
- ** optimized:
- **
- ** x=5
- ** x=5 AND y=10
- ** x=5 AND y<10
- ** x=5 AND y>5 AND y<10
- ** x=5 AND y=5 AND z<=10
- **
- ** The z<10 term of the following cannot be used, only
- ** the x=5 term:
- **
- ** x=5 AND z<10
- **
- ** N may be zero if there are inequality constraints.
- ** If there are no inequality constraints, then N is at
- ** least one.
- **
- ** This case is also used when there are no WHERE clause
- ** constraints but an index is selected anyway, in order
- ** to force the output order to conform to an ORDER BY.
- */
- static const u8 aStartOp[] = {
- 0,
- 0,
- OP_Rewind, /* 2: (!start_constraints && startEq && !bRev) */
- OP_Last, /* 3: (!start_constraints && startEq && bRev) */
- OP_SeekGt, /* 4: (start_constraints && !startEq && !bRev) */
- OP_SeekLt, /* 5: (start_constraints && !startEq && bRev) */
- OP_SeekGe, /* 6: (start_constraints && startEq && !bRev) */
- OP_SeekLe /* 7: (start_constraints && startEq && bRev) */
- };
- static const u8 aEndOp[] = {
- OP_Noop, /* 0: (!end_constraints) */
- OP_IdxGE, /* 1: (end_constraints && !bRev) */
- OP_IdxLT /* 2: (end_constraints && bRev) */
- };
- int nEq = pLevel->plan.nEq; /* Number of == or IN terms */
- int isMinQuery = 0; /* If this is an optimized SELECT min(x).. */
- int regBase; /* Base register holding constraint values */
- int r1; /* Temp register */
- WhereTerm *pRangeStart = 0; /* Inequality constraint at range start */
- WhereTerm *pRangeEnd = 0; /* Inequality constraint at range end */
- int startEq; /* True if range start uses ==, >= or <= */
- int endEq; /* True if range end uses ==, >= or <= */
- int start_constraints; /* Start of range is constrained */
- int nConstraint; /* Number of constraint terms */
- Index *pIdx; /* The index we will be using */
- int iIdxCur; /* The VDBE cursor for the index */
- int nExtraReg = 0; /* Number of extra registers needed */
- int op; /* Instruction opcode */
- char *zStartAff; /* Affinity for start of range constraint */
- char *zEndAff; /* Affinity for end of range constraint */
+ mPrior |= pNew->maskSelf;
+ if( rc || db->mallocFailed ) break;
+ }
- pIdx = pLevel->plan.u.pIdx;
- iIdxCur = pLevel->iIdxCur;
- k = pIdx->aiColumn[nEq]; /* Column for inequality constraints */
+ whereLoopClear(db, pNew);
+ return rc;
+}
- /* If this loop satisfies a sort order (pOrderBy) request that
- ** was passed to this function to implement a "SELECT min(x) ..."
- ** query, then the caller will only allow the loop to run for
- ** a single iteration. This means that the first row returned
- ** should not have a NULL value stored in 'x'. If column 'x' is
- ** the first one after the nEq equality constraints in the index,
- ** this requires some special handling.
- */
- if( (wctrlFlags&WHERE_ORDERBY_MIN)!=0
- && (pLevel->plan.wsFlags&WHERE_ORDERBY)
- && (pIdx->nColumn>nEq)
- ){
- /* assert( pOrderBy->nExpr==1 ); */
- /* assert( pOrderBy->a[0].pExpr->iColumn==pIdx->aiColumn[nEq] ); */
- isMinQuery = 1;
- nExtraReg = 1;
- }
+/*
+** Examine a WherePath (with the addition of the extra WhereLoop of the 5th
+** parameters) to see if it outputs rows in the requested ORDER BY
+** (or GROUP BY) without requiring a separate sort operation. Return N:
+**
+** N>0: N terms of the ORDER BY clause are satisfied
+** N==0: No terms of the ORDER BY clause are satisfied
+** N<0: Unknown yet how many terms of ORDER BY might be satisfied.
+**
+** Note that processing for WHERE_GROUPBY and WHERE_DISTINCTBY is not as
+** strict. With GROUP BY and DISTINCT the only requirement is that
+** equivalent rows appear immediately adjacent to one another. GROUP BY
+** and DISTINCT do not require rows to appear in any particular order as long
+** as equivalent rows are grouped together. Thus for GROUP BY and DISTINCT
+** the pOrderBy terms can be matched in any order. With ORDER BY, the
+** pOrderBy terms must be matched in strict left-to-right order.
+*/
+static i8 wherePathSatisfiesOrderBy(
+ WhereInfo *pWInfo, /* The WHERE clause */
+ ExprList *pOrderBy, /* ORDER BY or GROUP BY or DISTINCT clause to check */
+ WherePath *pPath, /* The WherePath to check */
+ u16 wctrlFlags, /* WHERE_GROUPBY or _DISTINCTBY or _ORDERBY_LIMIT */
+ u16 nLoop, /* Number of entries in pPath->aLoop[] */
+ WhereLoop *pLast, /* Add this WhereLoop to the end of pPath->aLoop[] */
+ Bitmask *pRevMask /* OUT: Mask of WhereLoops to run in reverse order */
+){
+ u8 revSet; /* True if rev is known */
+ u8 rev; /* Composite sort order */
+ u8 revIdx; /* Index sort order */
+ u8 isOrderDistinct; /* All prior WhereLoops are order-distinct */
+ u8 distinctColumns; /* True if the loop has UNIQUE NOT NULL columns */
+ u8 isMatch; /* iColumn matches a term of the ORDER BY clause */
+ u16 eqOpMask; /* Allowed equality operators */
+ u16 nKeyCol; /* Number of key columns in pIndex */
+ u16 nColumn; /* Total number of ordered columns in the index */
+ u16 nOrderBy; /* Number terms in the ORDER BY clause */
+ int iLoop; /* Index of WhereLoop in pPath being processed */
+ int i, j; /* Loop counters */
+ int iCur; /* Cursor number for current WhereLoop */
+ int iColumn; /* A column number within table iCur */
+ WhereLoop *pLoop = 0; /* Current WhereLoop being processed. */
+ WhereTerm *pTerm; /* A single term of the WHERE clause */
+ Expr *pOBExpr; /* An expression from the ORDER BY clause */
+ CollSeq *pColl; /* COLLATE function from an ORDER BY clause term */
+ Index *pIndex; /* The index associated with pLoop */
+ sqlite3 *db = pWInfo->pParse->db; /* Database connection */
+ Bitmask obSat = 0; /* Mask of ORDER BY terms satisfied so far */
+ Bitmask obDone; /* Mask of all ORDER BY terms */
+ Bitmask orderDistinctMask; /* Mask of all well-ordered loops */
+ Bitmask ready; /* Mask of inner loops */
- /* Find any inequality constraint terms for the start and end
- ** of the range.
- */
- if( pLevel->plan.wsFlags & WHERE_TOP_LIMIT ){
- pRangeEnd = findTerm(pWC, iCur, k, notReady, (WO_LT|WO_LE), pIdx);
- nExtraReg = 1;
+ /*
+ ** We say the WhereLoop is "one-row" if it generates no more than one
+ ** row of output. A WhereLoop is one-row if all of the following are true:
+ ** (a) All index columns match with WHERE_COLUMN_EQ.
+ ** (b) The index is unique
+ ** Any WhereLoop with an WHERE_COLUMN_EQ constraint on the rowid is one-row.
+ ** Every one-row WhereLoop will have the WHERE_ONEROW bit set in wsFlags.
+ **
+ ** We say the WhereLoop is "order-distinct" if the set of columns from
+ ** that WhereLoop that are in the ORDER BY clause are different for every
+ ** row of the WhereLoop. Every one-row WhereLoop is automatically
+ ** order-distinct. A WhereLoop that has no columns in the ORDER BY clause
+ ** is not order-distinct. To be order-distinct is not quite the same as being
+ ** UNIQUE since a UNIQUE column or index can have multiple rows that
+ ** are NULL and NULL values are equivalent for the purpose of order-distinct.
+ ** To be order-distinct, the columns must be UNIQUE and NOT NULL.
+ **
+ ** The rowid for a table is always UNIQUE and NOT NULL so whenever the
+ ** rowid appears in the ORDER BY clause, the corresponding WhereLoop is
+ ** automatically order-distinct.
+ */
+
+ assert( pOrderBy!=0 );
+ if( nLoop && OptimizationDisabled(db, SQLITE_OrderByIdxJoin) ) return 0;
+
+ nOrderBy = pOrderBy->nExpr;
+ testcase( nOrderBy==BMS-1 );
+ if( nOrderBy>BMS-1 ) return 0; /* Cannot optimize overly large ORDER BYs */
+ isOrderDistinct = 1;
+ obDone = MASKBIT(nOrderBy)-1;
+ orderDistinctMask = 0;
+ ready = 0;
+ eqOpMask = WO_EQ | WO_IS | WO_ISNULL;
+ if( wctrlFlags & WHERE_ORDERBY_LIMIT ) eqOpMask |= WO_IN;
+ for(iLoop=0; isOrderDistinct && obSat<obDone && iLoop<=nLoop; iLoop++){
+ if( iLoop>0 ) ready |= pLoop->maskSelf;
+ if( iLoop<nLoop ){
+ pLoop = pPath->aLoop[iLoop];
+ if( wctrlFlags & WHERE_ORDERBY_LIMIT ) continue;
+ }else{
+ pLoop = pLast;
}
- if( pLevel->plan.wsFlags & WHERE_BTM_LIMIT ){
- pRangeStart = findTerm(pWC, iCur, k, notReady, (WO_GT|WO_GE), pIdx);
- nExtraReg = 1;
+ if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
+ if( pLoop->u.vtab.isOrdered ) obSat = obDone;
+ break;
}
+ iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
- /* Generate code to evaluate all constraint terms using == or IN
- ** and store the values of those terms in an array of registers
- ** starting at regBase.
+ /* Mark off any ORDER BY term X that is a column in the table of
+ ** the current loop for which there is term in the WHERE
+ ** clause of the form X IS NULL or X=? that reference only outer
+ ** loops.
*/
- regBase = codeAllEqualityTerms(
- pParse, pLevel, pWC, notReady, nExtraReg, &zStartAff
- );
- zEndAff = sqlite3DbStrDup(pParse->db, zStartAff);
- addrNxt = pLevel->addrNxt;
+ for(i=0; i<nOrderBy; i++){
+ if( MASKBIT(i) & obSat ) continue;
+ pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ pTerm = sqlite3WhereFindTerm(&pWInfo->sWC, iCur, pOBExpr->iColumn,
+ ~ready, eqOpMask, 0);
+ if( pTerm==0 ) continue;
+ if( pTerm->eOperator==WO_IN ){
+ /* IN terms are only valid for sorting in the ORDER BY LIMIT
+ ** optimization, and then only if they are actually used
+ ** by the query plan */
+ assert( wctrlFlags & WHERE_ORDERBY_LIMIT );
+ for(j=0; j<pLoop->nLTerm && pTerm!=pLoop->aLTerm[j]; j++){}
+ if( j>=pLoop->nLTerm ) continue;
+ }
+ if( (pTerm->eOperator&(WO_EQ|WO_IS))!=0 && pOBExpr->iColumn>=0 ){
+ const char *z1, *z2;
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ z1 = pColl->zName;
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pTerm->pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ z2 = pColl->zName;
+ if( sqlite3StrICmp(z1, z2)!=0 ) continue;
+ testcase( pTerm->pExpr->op==TK_IS );
+ }
+ obSat |= MASKBIT(i);
+ }
+
+ if( (pLoop->wsFlags & WHERE_ONEROW)==0 ){
+ if( pLoop->wsFlags & WHERE_IPK ){
+ pIndex = 0;
+ nKeyCol = 0;
+ nColumn = 1;
+ }else if( (pIndex = pLoop->u.btree.pIndex)==0 || pIndex->bUnordered ){
+ return 0;
+ }else{
+ nKeyCol = pIndex->nKeyCol;
+ nColumn = pIndex->nColumn;
+ assert( nColumn==nKeyCol+1 || !HasRowid(pIndex->pTable) );
+ assert( pIndex->aiColumn[nColumn-1]==XN_ROWID
+ || !HasRowid(pIndex->pTable));
+ isOrderDistinct = IsUniqueIndex(pIndex);
+ }
- /* If we are doing a reverse order scan on an ascending index, or
- ** a forward order scan on a descending index, interchange the
- ** start and end terms (pRangeStart and pRangeEnd).
- */
- if( nEq<pIdx->nColumn && bRev==(pIdx->aSortOrder[nEq]==SQLITE_SO_ASC) ){
- SWAP(WhereTerm *, pRangeEnd, pRangeStart);
- }
+ /* Loop through all columns of the index and deal with the ones
+ ** that are not constrained by == or IN.
+ */
+ rev = revSet = 0;
+ distinctColumns = 0;
+ for(j=0; j<nColumn; j++){
+ u8 bOnce = 1; /* True to run the ORDER BY search loop */
- testcase( pRangeStart && pRangeStart->eOperator & WO_LE );
- testcase( pRangeStart && pRangeStart->eOperator & WO_GE );
- testcase( pRangeEnd && pRangeEnd->eOperator & WO_LE );
- testcase( pRangeEnd && pRangeEnd->eOperator & WO_GE );
- startEq = !pRangeStart || pRangeStart->eOperator & (WO_LE|WO_GE);
- endEq = !pRangeEnd || pRangeEnd->eOperator & (WO_LE|WO_GE);
- start_constraints = pRangeStart || nEq>0;
+ assert( j>=pLoop->u.btree.nEq
+ || (pLoop->aLTerm[j]==0)==(j<pLoop->nSkip)
+ );
+ if( j<pLoop->u.btree.nEq && j>=pLoop->nSkip ){
+ u16 eOp = pLoop->aLTerm[j]->eOperator;
- /* Seek the index cursor to the start of the range. */
- nConstraint = nEq;
- if( pRangeStart ){
- Expr *pRight = pRangeStart->pExpr->pRight;
- sqlite3ExprCode(pParse, pRight, regBase+nEq);
- if( (pRangeStart->wtFlags & TERM_VNULL)==0 ){
- sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
- }
- if( zStartAff ){
- if( sqlite3CompareAffinity(pRight, zStartAff[nEq])==SQLITE_AFF_NONE){
- /* Since the comparison is to be performed with no conversions
- ** applied to the operands, set the affinity to apply to pRight to
- ** SQLITE_AFF_NONE. */
- zStartAff[nEq] = SQLITE_AFF_NONE;
- }
- if( sqlite3ExprNeedsNoAffinityChange(pRight, zStartAff[nEq]) ){
- zStartAff[nEq] = SQLITE_AFF_NONE;
+ /* Skip over == and IS and ISNULL terms. (Also skip IN terms when
+ ** doing WHERE_ORDERBY_LIMIT processing).
+ **
+ ** If the current term is a column of an ((?,?) IN (SELECT...))
+ ** expression for which the SELECT returns more than one column,
+ ** check that it is the only column used by this loop. Otherwise,
+ ** if it is one of two or more, none of the columns can be
+ ** considered to match an ORDER BY term. */
+ if( (eOp & eqOpMask)!=0 ){
+ if( eOp & WO_ISNULL ){
+ testcase( isOrderDistinct );
+ isOrderDistinct = 0;
+ }
+ continue;
+ }else if( ALWAYS(eOp & WO_IN) ){
+ /* ALWAYS() justification: eOp is an equality operator due to the
+ ** j<pLoop->u.btree.nEq constraint above. Any equality other
+ ** than WO_IN is captured by the previous "if". So this one
+ ** always has to be WO_IN. */
+ Expr *pX = pLoop->aLTerm[j]->pExpr;
+ for(i=j+1; i<pLoop->u.btree.nEq; i++){
+ if( pLoop->aLTerm[i]->pExpr==pX ){
+ assert( (pLoop->aLTerm[i]->eOperator & WO_IN) );
+ bOnce = 0;
+ break;
+ }
+ }
+ }
}
- }
- nConstraint++;
- testcase( pRangeStart->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- }else if( isMinQuery ){
- sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq);
- nConstraint++;
- startEq = 0;
- start_constraints = 1;
- }
- codeApplyAffinity(pParse, regBase, nConstraint, zStartAff);
- op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev];
- assert( op!=0 );
- testcase( op==OP_Rewind );
- testcase( op==OP_Last );
- testcase( op==OP_SeekGt );
- testcase( op==OP_SeekGe );
- testcase( op==OP_SeekLe );
- testcase( op==OP_SeekLt );
- sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
- /* Load the value for the inequality constraint at the end of the
- ** range (if any).
- */
- nConstraint = nEq;
- if( pRangeEnd ){
- Expr *pRight = pRangeEnd->pExpr->pRight;
- sqlite3ExprCacheRemove(pParse, regBase+nEq, 1);
- sqlite3ExprCode(pParse, pRight, regBase+nEq);
- if( (pRangeEnd->wtFlags & TERM_VNULL)==0 ){
- sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt);
- }
- if( zEndAff ){
- if( sqlite3CompareAffinity(pRight, zEndAff[nEq])==SQLITE_AFF_NONE){
- /* Since the comparison is to be performed with no conversions
- ** applied to the operands, set the affinity to apply to pRight to
- ** SQLITE_AFF_NONE. */
- zEndAff[nEq] = SQLITE_AFF_NONE;
+ /* Get the column number in the table (iColumn) and sort order
+ ** (revIdx) for the j-th column of the index.
+ */
+ if( pIndex ){
+ iColumn = pIndex->aiColumn[j];
+ revIdx = pIndex->aSortOrder[j];
+ if( iColumn==pIndex->pTable->iPKey ) iColumn = -1;
+ }else{
+ iColumn = XN_ROWID;
+ revIdx = 0;
}
- if( sqlite3ExprNeedsNoAffinityChange(pRight, zEndAff[nEq]) ){
- zEndAff[nEq] = SQLITE_AFF_NONE;
+
+ /* An unconstrained column that might be NULL means that this
+ ** WhereLoop is not well-ordered
+ */
+ if( isOrderDistinct
+ && iColumn>=0
+ && j>=pLoop->u.btree.nEq
+ && pIndex->pTable->aCol[iColumn].notNull==0
+ ){
+ isOrderDistinct = 0;
}
- }
- codeApplyAffinity(pParse, regBase, nEq+1, zEndAff);
- nConstraint++;
- testcase( pRangeEnd->wtFlags & TERM_VIRTUAL ); /* EV: R-30575-11662 */
- }
- sqlite3DbFree(pParse->db, zStartAff);
- sqlite3DbFree(pParse->db, zEndAff);
- /* Top of the loop body */
- pLevel->p2 = sqlite3VdbeCurrentAddr(v);
+ /* Find the ORDER BY term that corresponds to the j-th column
+ ** of the index and mark that ORDER BY term off
+ */
+ isMatch = 0;
+ for(i=0; bOnce && i<nOrderBy; i++){
+ if( MASKBIT(i) & obSat ) continue;
+ pOBExpr = sqlite3ExprSkipCollate(pOrderBy->a[i].pExpr);
+ testcase( wctrlFlags & WHERE_GROUPBY );
+ testcase( wctrlFlags & WHERE_DISTINCTBY );
+ if( (wctrlFlags & (WHERE_GROUPBY|WHERE_DISTINCTBY))==0 ) bOnce = 0;
+ if( iColumn>=(-1) ){
+ if( pOBExpr->op!=TK_COLUMN ) continue;
+ if( pOBExpr->iTable!=iCur ) continue;
+ if( pOBExpr->iColumn!=iColumn ) continue;
+ }else{
+ if( sqlite3ExprCompare(pOBExpr,pIndex->aColExpr->a[j].pExpr,iCur) ){
+ continue;
+ }
+ }
+ if( iColumn>=0 ){
+ pColl = sqlite3ExprCollSeq(pWInfo->pParse, pOrderBy->a[i].pExpr);
+ if( !pColl ) pColl = db->pDfltColl;
+ if( sqlite3StrICmp(pColl->zName, pIndex->azColl[j])!=0 ) continue;
+ }
+ isMatch = 1;
+ break;
+ }
+ if( isMatch && (wctrlFlags & WHERE_GROUPBY)==0 ){
+ /* Make sure the sort order is compatible in an ORDER BY clause.
+ ** Sort order is irrelevant for a GROUP BY clause. */
+ if( revSet ){
+ if( (rev ^ revIdx)!=pOrderBy->a[i].sortOrder ) isMatch = 0;
+ }else{
+ rev = revIdx ^ pOrderBy->a[i].sortOrder;
+ if( rev ) *pRevMask |= MASKBIT(iLoop);
+ revSet = 1;
+ }
+ }
+ if( isMatch ){
+ if( iColumn==XN_ROWID ){
+ testcase( distinctColumns==0 );
+ distinctColumns = 1;
+ }
+ obSat |= MASKBIT(i);
+ }else{
+ /* No match found */
+ if( j==0 || j<nKeyCol ){
+ testcase( isOrderDistinct!=0 );
+ isOrderDistinct = 0;
+ }
+ break;
+ }
+ } /* end Loop over all index columns */
+ if( distinctColumns ){
+ testcase( isOrderDistinct==0 );
+ isOrderDistinct = 1;
+ }
+ } /* end-if not one-row */
- /* Check if the index cursor is past the end of the range. */
- op = aEndOp[(pRangeEnd || nEq) * (1 + bRev)];
- testcase( op==OP_Noop );
- testcase( op==OP_IdxGE );
- testcase( op==OP_IdxLT );
- if( op!=OP_Noop ){
- sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint);
- sqlite3VdbeChangeP5(v, endEq!=bRev ?1:0);
+ /* Mark off any other ORDER BY terms that reference pLoop */
+ if( isOrderDistinct ){
+ orderDistinctMask |= pLoop->maskSelf;
+ for(i=0; i<nOrderBy; i++){
+ Expr *p;
+ Bitmask mTerm;
+ if( MASKBIT(i) & obSat ) continue;
+ p = pOrderBy->a[i].pExpr;
+ mTerm = sqlite3WhereExprUsage(&pWInfo->sMaskSet,p);
+ if( mTerm==0 && !sqlite3ExprIsConstant(p) ) continue;
+ if( (mTerm&~orderDistinctMask)==0 ){
+ obSat |= MASKBIT(i);
+ }
+ }
}
-
- /* If there are inequality constraints, check that the value
- ** of the table column that the inequality contrains is not NULL.
- ** If it is, jump to the next iteration of the loop.
- */
- r1 = sqlite3GetTempReg(pParse);
- testcase( pLevel->plan.wsFlags & WHERE_BTM_LIMIT );
- testcase( pLevel->plan.wsFlags & WHERE_TOP_LIMIT );
- if( (pLevel->plan.wsFlags & (WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0 ){
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, nEq, r1);
- sqlite3VdbeAddOp2(v, OP_IsNull, r1, addrCont);
+ } /* End the loop over all WhereLoops from outer-most down to inner-most */
+ if( obSat==obDone ) return (i8)nOrderBy;
+ if( !isOrderDistinct ){
+ for(i=nOrderBy-1; i>0; i--){
+ Bitmask m = MASKBIT(i) - 1;
+ if( (obSat&m)==m ) return i;
}
- sqlite3ReleaseTempReg(pParse, r1);
+ return 0;
+ }
+ return -1;
+}
- /* Seek the table cursor, if required */
- disableTerm(pLevel, pRangeStart);
- disableTerm(pLevel, pRangeEnd);
- if( !omitTable ){
- iRowidReg = iReleaseReg = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, iRowidReg);
- sqlite3ExprCacheStore(pParse, iCur, -1, iRowidReg);
- sqlite3VdbeAddOp2(v, OP_Seek, iCur, iRowidReg); /* Deferred seek */
- }
- /* Record the instruction used to terminate the loop. Disable
- ** WHERE clause terms made redundant by the index range scan.
- */
- if( pLevel->plan.wsFlags & WHERE_UNIQUE ){
- pLevel->op = OP_Noop;
- }else if( bRev ){
- pLevel->op = OP_Prev;
- }else{
- pLevel->op = OP_Next;
- }
- pLevel->p1 = iIdxCur;
- }else
+/*
+** If the WHERE_GROUPBY flag is set in the mask passed to sqlite3WhereBegin(),
+** the planner assumes that the specified pOrderBy list is actually a GROUP
+** BY clause - and so any order that groups rows as required satisfies the
+** request.
+**
+** Normally, in this case it is not possible for the caller to determine
+** whether or not the rows are really being delivered in sorted order, or
+** just in some other order that provides the required grouping. However,
+** if the WHERE_SORTBYGROUP flag is also passed to sqlite3WhereBegin(), then
+** this function may be called on the returned WhereInfo object. It returns
+** true if the rows really will be sorted in the specified order, or false
+** otherwise.
+**
+** For example, assuming:
+**
+** CREATE INDEX i1 ON t1(x, Y);
+**
+** then
+**
+** SELECT * FROM t1 GROUP BY x,y ORDER BY x,y; -- IsSorted()==1
+** SELECT * FROM t1 GROUP BY y,x ORDER BY y,x; -- IsSorted()==0
+*/
+SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo *pWInfo){
+ assert( pWInfo->wctrlFlags & WHERE_GROUPBY );
+ assert( pWInfo->wctrlFlags & WHERE_SORTBYGROUP );
+ return pWInfo->sorted;
+}
-#ifndef SQLITE_OMIT_OR_OPTIMIZATION
- if( pLevel->plan.wsFlags & WHERE_MULTI_OR ){
- /* Case 4: Two or more separately indexed terms connected by OR
- **
- ** Example:
- **
- ** CREATE TABLE t1(a,b,c,d);
- ** CREATE INDEX i1 ON t1(a);
- ** CREATE INDEX i2 ON t1(b);
- ** CREATE INDEX i3 ON t1(c);
- **
- ** SELECT * FROM t1 WHERE a=5 OR b=7 OR (c=11 AND d=13)
- **
- ** In the example, there are three indexed terms connected by OR.
- ** The top of the loop looks like this:
- **
- ** Null 1 # Zero the rowset in reg 1
- **
- ** Then, for each indexed term, the following. The arguments to
- ** RowSetTest are such that the rowid of the current row is inserted
- ** into the RowSet. If it is already present, control skips the
- ** Gosub opcode and jumps straight to the code generated by WhereEnd().
- **
- ** sqlite3WhereBegin(<term>)
- ** RowSetTest # Insert rowid into rowset
- ** Gosub 2 A
- ** sqlite3WhereEnd()
- **
- ** Following the above, code to terminate the loop. Label A, the target
- ** of the Gosub above, jumps to the instruction right after the Goto.
- **
- ** Null 1 # Zero the rowset in reg 1
- ** Goto B # The loop is finished.
- **
- ** A: <loop body> # Return data, whatever.
- **
- ** Return 2 # Jump back to the Gosub
- **
- ** B: <after the loop>
- **
- */
- WhereClause *pOrWc; /* The OR-clause broken out into subterms */
- SrcList *pOrTab; /* Shortened table list or OR-clause generation */
+#ifdef WHERETRACE_ENABLED
+/* For debugging use only: */
+static const char *wherePathName(WherePath *pPath, int nLoop, WhereLoop *pLast){
+ static char zName[65];
+ int i;
+ for(i=0; i<nLoop; i++){ zName[i] = pPath->aLoop[i]->cId; }
+ if( pLast ) zName[i++] = pLast->cId;
+ zName[i] = 0;
+ return zName;
+}
+#endif
- int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */
- int regRowset = 0; /* Register for RowSet object */
- int regRowid = 0; /* Register holding rowid */
- int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */
- int iRetInit; /* Address of regReturn init */
- int untestedTerms = 0; /* Some terms not completely tested */
- int ii;
-
- pTerm = pLevel->plan.u.pTerm;
- assert( pTerm!=0 );
- assert( pTerm->eOperator==WO_OR );
- assert( (pTerm->wtFlags & TERM_ORINFO)!=0 );
- pOrWc = &pTerm->u.pOrInfo->wc;
- pLevel->op = OP_Return;
- pLevel->p1 = regReturn;
+/*
+** Return the cost of sorting nRow rows, assuming that the keys have
+** nOrderby columns and that the first nSorted columns are already in
+** order.
+*/
+static LogEst whereSortingCost(
+ WhereInfo *pWInfo,
+ LogEst nRow,
+ int nOrderBy,
+ int nSorted
+){
+ /* TUNING: Estimated cost of a full external sort, where N is
+ ** the number of rows to sort is:
+ **
+ ** cost = (3.0 * N * log(N)).
+ **
+ ** Or, if the order-by clause has X terms but only the last Y
+ ** terms are out of order, then block-sorting will reduce the
+ ** sorting cost to:
+ **
+ ** cost = (3.0 * N * log(N)) * (Y/X)
+ **
+ ** The (Y/X) term is implemented using stack variable rScale
+ ** below. */
+ LogEst rScale, rSortCost;
+ assert( nOrderBy>0 && 66==sqlite3LogEst(100) );
+ rScale = sqlite3LogEst((nOrderBy-nSorted)*100/nOrderBy) - 66;
+ rSortCost = nRow + rScale + 16;
- /* Set up a new SrcList ni pOrTab containing the table being scanned
- ** by this loop in the a[0] slot and all notReady tables in a[1..] slots.
- ** This becomes the SrcList in the recursive call to sqlite3WhereBegin().
- */
- if( pWInfo->nLevel>1 ){
- int nNotReady; /* The number of notReady tables */
- struct SrcList_item *origSrc; /* Original list of tables */
- nNotReady = pWInfo->nLevel - iLevel - 1;
- pOrTab = sqlite3StackAllocRaw(pParse->db,
- sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
- if( pOrTab==0 ) return notReady;
- pOrTab->nAlloc = (i16)(nNotReady + 1);
- pOrTab->nSrc = pOrTab->nAlloc;
- memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
- origSrc = pWInfo->pTabList->a;
- for(k=1; k<=nNotReady; k++){
- memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k]));
- }
- }else{
- pOrTab = pWInfo->pTabList;
- }
+ /* Multiple by log(M) where M is the number of output rows.
+ ** Use the LIMIT for M if it is smaller */
+ if( (pWInfo->wctrlFlags & WHERE_USE_LIMIT)!=0 && pWInfo->iLimit<nRow ){
+ nRow = pWInfo->iLimit;
+ }
+ rSortCost += estLog(nRow);
+ return rSortCost;
+}
- /* Initialize the rowset register to contain NULL. An SQL NULL is
- ** equivalent to an empty rowset.
- **
- ** Also initialize regReturn to contain the address of the instruction
- ** immediately following the OP_Return at the bottom of the loop. This
- ** is required in a few obscure LEFT JOIN cases where control jumps
- ** over the top of the loop into the body of it. In this case the
- ** correct response for the end-of-loop code (the OP_Return) is to
- ** fall through to the next instruction, just as an OP_Next does if
- ** called on an uninitialized cursor.
- */
- if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
- regRowset = ++pParse->nMem;
- regRowid = ++pParse->nMem;
- sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset);
- }
- iRetInit = sqlite3VdbeAddOp2(v, OP_Integer, 0, regReturn);
+/*
+** Given the list of WhereLoop objects at pWInfo->pLoops, this routine
+** attempts to find the lowest cost path that visits each WhereLoop
+** once. This path is then loaded into the pWInfo->a[].pWLoop fields.
+**
+** Assume that the total number of output rows that will need to be sorted
+** will be nRowEst (in the 10*log2 representation). Or, ignore sorting
+** costs if nRowEst==0.
+**
+** Return SQLITE_OK on success or SQLITE_NOMEM of a memory allocation
+** error occurs.
+*/
+static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
+ int mxChoice; /* Maximum number of simultaneous paths tracked */
+ int nLoop; /* Number of terms in the join */
+ Parse *pParse; /* Parsing context */
+ sqlite3 *db; /* The database connection */
+ int iLoop; /* Loop counter over the terms of the join */
+ int ii, jj; /* Loop counters */
+ int mxI = 0; /* Index of next entry to replace */
+ int nOrderBy; /* Number of ORDER BY clause terms */
+ LogEst mxCost = 0; /* Maximum cost of a set of paths */
+ LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */
+ int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */
+ WherePath *aFrom; /* All nFrom paths at the previous level */
+ WherePath *aTo; /* The nTo best paths at the current level */
+ WherePath *pFrom; /* An element of aFrom[] that we are working on */
+ WherePath *pTo; /* An element of aTo[] that we are working on */
+ WhereLoop *pWLoop; /* One of the WhereLoop objects */
+ WhereLoop **pX; /* Used to divy up the pSpace memory */
+ LogEst *aSortCost = 0; /* Sorting and partial sorting costs */
+ char *pSpace; /* Temporary memory used by this routine */
+ int nSpace; /* Bytes of space allocated at pSpace */
- for(ii=0; ii<pOrWc->nTerm; ii++){
- WhereTerm *pOrTerm = &pOrWc->a[ii];
- if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){
- WhereInfo *pSubWInfo; /* Info for single OR-term scan */
- /* Loop through table entries that match term pOrTerm. */
- pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, 0,
- WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE |
- WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY);
- if( pSubWInfo ){
- explainOneScan(
- pParse, pOrTab, &pSubWInfo->a[0], iLevel, pLevel->iFrom, 0
- );
- if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){
- int iSet = ((ii==pOrWc->nTerm-1)?-1:ii);
- int r;
- r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur,
- regRowid);
- sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset,
- sqlite3VdbeCurrentAddr(v)+2, r, iSet);
+ pParse = pWInfo->pParse;
+ db = pParse->db;
+ nLoop = pWInfo->nLevel;
+ /* TUNING: For simple queries, only the best path is tracked.
+ ** For 2-way joins, the 5 best paths are followed.
+ ** For joins of 3 or more tables, track the 10 best paths */
+ mxChoice = (nLoop<=1) ? 1 : (nLoop==2 ? 5 : 10);
+ assert( nLoop<=pWInfo->pTabList->nSrc );
+ WHERETRACE(0x002, ("---- begin solver. (nRowEst=%d)\n", nRowEst));
+
+ /* If nRowEst is zero and there is an ORDER BY clause, ignore it. In this
+ ** case the purpose of this call is to estimate the number of rows returned
+ ** by the overall query. Once this estimate has been obtained, the caller
+ ** will invoke this function a second time, passing the estimate as the
+ ** nRowEst parameter. */
+ if( pWInfo->pOrderBy==0 || nRowEst==0 ){
+ nOrderBy = 0;
+ }else{
+ nOrderBy = pWInfo->pOrderBy->nExpr;
+ }
+
+ /* Allocate and initialize space for aTo, aFrom and aSortCost[] */
+ nSpace = (sizeof(WherePath)+sizeof(WhereLoop*)*nLoop)*mxChoice*2;
+ nSpace += sizeof(LogEst) * nOrderBy;
+ pSpace = sqlite3DbMallocRawNN(db, nSpace);
+ if( pSpace==0 ) return SQLITE_NOMEM_BKPT;
+ aTo = (WherePath*)pSpace;
+ aFrom = aTo+mxChoice;
+ memset(aFrom, 0, sizeof(aFrom[0]));
+ pX = (WhereLoop**)(aFrom+mxChoice);
+ for(ii=mxChoice*2, pFrom=aTo; ii>0; ii--, pFrom++, pX += nLoop){
+ pFrom->aLoop = pX;
+ }
+ if( nOrderBy ){
+ /* If there is an ORDER BY clause and it is not being ignored, set up
+ ** space for the aSortCost[] array. Each element of the aSortCost array
+ ** is either zero - meaning it has not yet been initialized - or the
+ ** cost of sorting nRowEst rows of data where the first X terms of
+ ** the ORDER BY clause are already in order, where X is the array
+ ** index. */
+ aSortCost = (LogEst*)pX;
+ memset(aSortCost, 0, sizeof(LogEst) * nOrderBy);
+ }
+ assert( aSortCost==0 || &pSpace[nSpace]==(char*)&aSortCost[nOrderBy] );
+ assert( aSortCost!=0 || &pSpace[nSpace]==(char*)pX );
+
+ /* Seed the search with a single WherePath containing zero WhereLoops.
+ **
+ ** TUNING: Do not let the number of iterations go above 28. If the cost
+ ** of computing an automatic index is not paid back within the first 28
+ ** rows, then do not use the automatic index. */
+ aFrom[0].nRow = MIN(pParse->nQueryLoop, 48); assert( 48==sqlite3LogEst(28) );
+ nFrom = 1;
+ assert( aFrom[0].isOrdered==0 );
+ if( nOrderBy ){
+ /* If nLoop is zero, then there are no FROM terms in the query. Since
+ ** in this case the query may return a maximum of one row, the results
+ ** are already in the requested order. Set isOrdered to nOrderBy to
+ ** indicate this. Or, if nLoop is greater than zero, set isOrdered to
+ ** -1, indicating that the result set may or may not be ordered,
+ ** depending on the loops added to the current plan. */
+ aFrom[0].isOrdered = nLoop>0 ? -1 : nOrderBy;
+ }
+
+ /* Compute successively longer WherePaths using the previous generation
+ ** of WherePaths as the basis for the next. Keep track of the mxChoice
+ ** best paths at each generation */
+ for(iLoop=0; iLoop<nLoop; iLoop++){
+ nTo = 0;
+ for(ii=0, pFrom=aFrom; ii<nFrom; ii++, pFrom++){
+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
+ LogEst nOut; /* Rows visited by (pFrom+pWLoop) */
+ LogEst rCost; /* Cost of path (pFrom+pWLoop) */
+ LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */
+ i8 isOrdered = pFrom->isOrdered; /* isOrdered for (pFrom+pWLoop) */
+ Bitmask maskNew; /* Mask of src visited by (..) */
+ Bitmask revMask = 0; /* Mask of rev-order loops for (..) */
+
+ if( (pWLoop->prereq & ~pFrom->maskLoop)!=0 ) continue;
+ if( (pWLoop->maskSelf & pFrom->maskLoop)!=0 ) continue;
+ if( (pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 && pFrom->nRow<10 ){
+ /* Do not use an automatic index if the this loop is expected
+ ** to run less than 2 times. */
+ assert( 10==sqlite3LogEst(2) );
+ continue;
+ }
+ /* At this point, pWLoop is a candidate to be the next loop.
+ ** Compute its cost */
+ rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup,pWLoop->rRun + pFrom->nRow);
+ rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted);
+ nOut = pFrom->nRow + pWLoop->nOut;
+ maskNew = pFrom->maskLoop | pWLoop->maskSelf;
+ if( isOrdered<0 ){
+ isOrdered = wherePathSatisfiesOrderBy(pWInfo,
+ pWInfo->pOrderBy, pFrom, pWInfo->wctrlFlags,
+ iLoop, pWLoop, &revMask);
+ }else{
+ revMask = pFrom->revLoop;
+ }
+ if( isOrdered>=0 && isOrdered<nOrderBy ){
+ if( aSortCost[isOrdered]==0 ){
+ aSortCost[isOrdered] = whereSortingCost(
+ pWInfo, nRowEst, nOrderBy, isOrdered
+ );
}
- sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody);
+ rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]);
- /* The pSubWInfo->untestedTerms flag means that this OR term
- ** contained one or more AND term from a notReady table. The
- ** terms from the notReady table could not be tested and will
- ** need to be tested later.
- */
- if( pSubWInfo->untestedTerms ) untestedTerms = 1;
+ WHERETRACE(0x002,
+ ("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n",
+ aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy,
+ rUnsorted, rCost));
+ }else{
+ rCost = rUnsorted;
+ }
- /* Finish the loop through table entries that match term pOrTerm. */
- sqlite3WhereEnd(pSubWInfo);
+ /* Check to see if pWLoop should be added to the set of
+ ** mxChoice best-so-far paths.
+ **
+ ** First look for an existing path among best-so-far paths
+ ** that covers the same set of loops and has the same isOrdered
+ ** setting as the current path candidate.
+ **
+ ** The term "((pTo->isOrdered^isOrdered)&0x80)==0" is equivalent
+ ** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range
+ ** of legal values for isOrdered, -1..64.
+ */
+ for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
+ if( pTo->maskLoop==maskNew
+ && ((pTo->isOrdered^isOrdered)&0x80)==0
+ ){
+ testcase( jj==nTo-1 );
+ break;
+ }
+ }
+ if( jj>=nTo ){
+ /* None of the existing best-so-far paths match the candidate. */
+ if( nTo>=mxChoice
+ && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted))
+ ){
+ /* The current candidate is no better than any of the mxChoice
+ ** paths currently in the best-so-far buffer. So discard
+ ** this candidate as not viable. */
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf("Skip %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ }
+#endif
+ continue;
+ }
+ /* If we reach this points it means that the new candidate path
+ ** needs to be added to the set of best-so-far paths. */
+ if( nTo<mxChoice ){
+ /* Increase the size of the aTo set by one */
+ jj = nTo++;
+ }else{
+ /* New path replaces the prior worst to keep count below mxChoice */
+ jj = mxI;
+ }
+ pTo = &aTo[jj];
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf("New %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ }
+#endif
+ }else{
+ /* Control reaches here if best-so-far path pTo=aTo[jj] covers the
+ ** same set of loops and has the sam isOrdered setting as the
+ ** candidate path. Check to see if the candidate should replace
+ ** pTo or if the candidate should be skipped */
+ if( pTo->rCost<rCost || (pTo->rCost==rCost && pTo->nRow<=nOut) ){
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf(
+ "Skip %s cost=%-3d,%3d order=%c",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ sqlite3DebugPrintf(" vs %s cost=%-3d,%d order=%c\n",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
+ }
+#endif
+ /* Discard the candidate path from further consideration */
+ testcase( pTo->rCost==rCost );
+ continue;
+ }
+ testcase( pTo->rCost==rCost+1 );
+ /* Control reaches here if the candidate path is better than the
+ ** pTo path. Replace pTo with the candidate. */
+#ifdef WHERETRACE_ENABLED /* 0x4 */
+ if( sqlite3WhereTrace&0x4 ){
+ sqlite3DebugPrintf(
+ "Update %s cost=%-3d,%3d order=%c",
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut,
+ isOrdered>=0 ? isOrdered+'0' : '?');
+ sqlite3DebugPrintf(" was %s cost=%-3d,%3d order=%c\n",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
+ }
+#endif
+ }
+ /* pWLoop is a winner. Add it to the set of best so far */
+ pTo->maskLoop = pFrom->maskLoop | pWLoop->maskSelf;
+ pTo->revLoop = revMask;
+ pTo->nRow = nOut;
+ pTo->rCost = rCost;
+ pTo->rUnsorted = rUnsorted;
+ pTo->isOrdered = isOrdered;
+ memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop);
+ pTo->aLoop[iLoop] = pWLoop;
+ if( nTo>=mxChoice ){
+ mxI = 0;
+ mxCost = aTo[0].rCost;
+ mxUnsorted = aTo[0].nRow;
+ for(jj=1, pTo=&aTo[1]; jj<mxChoice; jj++, pTo++){
+ if( pTo->rCost>mxCost
+ || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted)
+ ){
+ mxCost = pTo->rCost;
+ mxUnsorted = pTo->rUnsorted;
+ mxI = jj;
+ }
+ }
}
}
}
- sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v));
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk);
- sqlite3VdbeResolveLabel(v, iLoopBody);
- if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab);
- if( !untestedTerms ) disableTerm(pLevel, pTerm);
- }else
-#endif /* SQLITE_OMIT_OR_OPTIMIZATION */
+#ifdef WHERETRACE_ENABLED /* >=2 */
+ if( sqlite3WhereTrace & 0x02 ){
+ sqlite3DebugPrintf("---- after round %d ----\n", iLoop);
+ for(ii=0, pTo=aTo; ii<nTo; ii++, pTo++){
+ sqlite3DebugPrintf(" %s cost=%-3d nrow=%-3d order=%c",
+ wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
+ pTo->isOrdered>=0 ? (pTo->isOrdered+'0') : '?');
+ if( pTo->isOrdered>0 ){
+ sqlite3DebugPrintf(" rev=0x%llx\n", pTo->revLoop);
+ }else{
+ sqlite3DebugPrintf("\n");
+ }
+ }
+ }
+#endif
- {
- /* Case 5: There is no usable index. We must do a complete
- ** scan of the entire table.
- */
- static const u8 aStep[] = { OP_Next, OP_Prev };
- static const u8 aStart[] = { OP_Rewind, OP_Last };
- assert( bRev==0 || bRev==1 );
- assert( omitTable==0 );
- pLevel->op = aStep[bRev];
- pLevel->p1 = iCur;
- pLevel->p2 = 1 + sqlite3VdbeAddOp2(v, aStart[bRev], iCur, addrBrk);
- pLevel->p5 = SQLITE_STMTSTATUS_FULLSCAN_STEP;
+ /* Swap the roles of aFrom and aTo for the next generation */
+ pFrom = aTo;
+ aTo = aFrom;
+ aFrom = pFrom;
+ nFrom = nTo;
}
- notReady &= ~getMask(pWC->pMaskSet, iCur);
- /* Insert code to test every subexpression that can be completely
- ** computed using the current set of tables.
- **
- ** IMPLEMENTATION-OF: R-49525-50935 Terms that cannot be satisfied through
- ** the use of indices become tests that are evaluated against each row of
- ** the relevant input tables.
- */
- for(pTerm=pWC->a, j=pWC->nTerm; j>0; j--, pTerm++){
- Expr *pE;
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */
- testcase( pTerm->wtFlags & TERM_CODED );
- if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & notReady)!=0 ){
- testcase( pWInfo->untestedTerms==0
- && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 );
- pWInfo->untestedTerms = 1;
- continue;
- }
- pE = pTerm->pExpr;
- assert( pE!=0 );
- if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){
- continue;
+ if( nFrom==0 ){
+ sqlite3ErrorMsg(pParse, "no query solution");
+ sqlite3DbFree(db, pSpace);
+ return SQLITE_ERROR;
+ }
+
+ /* Find the lowest cost path. pFrom will be left pointing to that path */
+ pFrom = aFrom;
+ for(ii=1; ii<nFrom; ii++){
+ if( pFrom->rCost>aFrom[ii].rCost ) pFrom = &aFrom[ii];
+ }
+ assert( pWInfo->nLevel==nLoop );
+ /* Load the lowest cost path into pWInfo */
+ for(iLoop=0; iLoop<nLoop; iLoop++){
+ WhereLevel *pLevel = pWInfo->a + iLoop;
+ pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop];
+ pLevel->iFrom = pWLoop->iTab;
+ pLevel->iTabCur = pWInfo->pTabList->a[pLevel->iFrom].iCursor;
+ }
+ if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0
+ && (pWInfo->wctrlFlags & WHERE_DISTINCTBY)==0
+ && pWInfo->eDistinct==WHERE_DISTINCT_NOOP
+ && nRowEst
+ ){
+ Bitmask notUsed;
+ int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pDistinctSet, pFrom,
+ WHERE_DISTINCTBY, nLoop-1, pFrom->aLoop[nLoop-1], &notUsed);
+ if( rc==pWInfo->pDistinctSet->nExpr ){
+ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
}
- sqlite3ExprIfFalse(pParse, pE, addrCont, SQLITE_JUMPIFNULL);
- pTerm->wtFlags |= TERM_CODED;
}
-
- /* For a LEFT OUTER JOIN, generate code that will record the fact that
- ** at least one row of the right table has matched the left table.
- */
- if( pLevel->iLeftJoin ){
- pLevel->addrFirst = sqlite3VdbeCurrentAddr(v);
- sqlite3VdbeAddOp2(v, OP_Integer, 1, pLevel->iLeftJoin);
- VdbeComment((v, "record LEFT JOIN hit"));
- sqlite3ExprCacheClear(pParse);
- for(pTerm=pWC->a, j=0; j<pWC->nTerm; j++, pTerm++){
- testcase( pTerm->wtFlags & TERM_VIRTUAL ); /* IMP: R-30575-11662 */
- testcase( pTerm->wtFlags & TERM_CODED );
- if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue;
- if( (pTerm->prereqAll & notReady)!=0 ){
- assert( pWInfo->untestedTerms );
- continue;
+ if( pWInfo->pOrderBy ){
+ if( pWInfo->wctrlFlags & WHERE_DISTINCTBY ){
+ if( pFrom->isOrdered==pWInfo->pOrderBy->nExpr ){
+ pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }
+ }else{
+ pWInfo->nOBSat = pFrom->isOrdered;
+ pWInfo->revMask = pFrom->revLoop;
+ if( pWInfo->nOBSat<=0 ){
+ pWInfo->nOBSat = 0;
+ if( nLoop>0 ){
+ u32 wsFlags = pFrom->aLoop[nLoop-1]->wsFlags;
+ if( (wsFlags & WHERE_ONEROW)==0
+ && (wsFlags&(WHERE_IPK|WHERE_COLUMN_IN))!=(WHERE_IPK|WHERE_COLUMN_IN)
+ ){
+ Bitmask m = 0;
+ int rc = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy, pFrom,
+ WHERE_ORDERBY_LIMIT, nLoop-1, pFrom->aLoop[nLoop-1], &m);
+ testcase( wsFlags & WHERE_IPK );
+ testcase( wsFlags & WHERE_COLUMN_IN );
+ if( rc==pWInfo->pOrderBy->nExpr ){
+ pWInfo->bOrderedInnerLoop = 1;
+ pWInfo->revMask = m;
+ }
+ }
+ }
+ }
+ }
+ if( (pWInfo->wctrlFlags & WHERE_SORTBYGROUP)
+ && pWInfo->nOBSat==pWInfo->pOrderBy->nExpr && nLoop>0
+ ){
+ Bitmask revMask = 0;
+ int nOrder = wherePathSatisfiesOrderBy(pWInfo, pWInfo->pOrderBy,
+ pFrom, 0, nLoop-1, pFrom->aLoop[nLoop-1], &revMask
+ );
+ assert( pWInfo->sorted==0 );
+ if( nOrder==pWInfo->pOrderBy->nExpr ){
+ pWInfo->sorted = 1;
+ pWInfo->revMask = revMask;
}
- assert( pTerm->pExpr );
- sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL);
- pTerm->wtFlags |= TERM_CODED;
}
}
- sqlite3ReleaseTempReg(pParse, iReleaseReg);
-
- return notReady;
-}
-#if defined(SQLITE_TEST)
-/*
-** The following variable holds a text description of query plan generated
-** by the most recent call to sqlite3WhereBegin(). Each call to WhereBegin
-** overwrites the previous. This information is used for testing and
-** analysis only.
-*/
-SQLITE_API char sqlite3_query_plan[BMS*2*40]; /* Text of the join */
-static int nQPlan = 0; /* Next free slow in _query_plan[] */
-#endif /* SQLITE_TEST */
+ pWInfo->nRowOut = pFrom->nRow;
+ /* Free temporary memory and return success */
+ sqlite3DbFree(db, pSpace);
+ return SQLITE_OK;
+}
/*
-** Free a WhereInfo structure
+** Most queries use only a single table (they are not joins) and have
+** simple == constraints against indexed fields. This routine attempts
+** to plan those simple cases using much less ceremony than the
+** general-purpose query planner, and thereby yield faster sqlite3_prepare()
+** times for the common case.
+**
+** Return non-zero on success, if this query can be handled by this
+** no-frills query planner. Return zero if this query needs the
+** general-purpose query planner.
*/
-static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
- if( ALWAYS(pWInfo) ){
- int i;
- for(i=0; i<pWInfo->nLevel; i++){
- sqlite3_index_info *pInfo = pWInfo->a[i].pIdxInfo;
- if( pInfo ){
- /* assert( pInfo->needToFreeIdxStr==0 || db->mallocFailed ); */
- if( pInfo->needToFreeIdxStr ){
- sqlite3_free(pInfo->idxStr);
- }
- sqlite3DbFree(db, pInfo);
- }
- if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){
- Index *pIdx = pWInfo->a[i].plan.u.pIdx;
- if( pIdx ){
- sqlite3DbFree(db, pIdx->zColAff);
- sqlite3DbFree(db, pIdx);
- }
- }
+static int whereShortCut(WhereLoopBuilder *pBuilder){
+ WhereInfo *pWInfo;
+ struct SrcList_item *pItem;
+ WhereClause *pWC;
+ WhereTerm *pTerm;
+ WhereLoop *pLoop;
+ int iCur;
+ int j;
+ Table *pTab;
+ Index *pIdx;
+
+ pWInfo = pBuilder->pWInfo;
+ if( pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE ) return 0;
+ assert( pWInfo->pTabList->nSrc>=1 );
+ pItem = pWInfo->pTabList->a;
+ pTab = pItem->pTab;
+ if( IsVirtual(pTab) ) return 0;
+ if( pItem->fg.isIndexedBy ) return 0;
+ iCur = pItem->iCursor;
+ pWC = &pWInfo->sWC;
+ pLoop = pBuilder->pNew;
+ pLoop->wsFlags = 0;
+ pLoop->nSkip = 0;
+ pTerm = sqlite3WhereFindTerm(pWC, iCur, -1, 0, WO_EQ|WO_IS, 0);
+ if( pTerm ){
+ testcase( pTerm->eOperator & WO_IS );
+ pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_IPK|WHERE_ONEROW;
+ pLoop->aLTerm[0] = pTerm;
+ pLoop->nLTerm = 1;
+ pLoop->u.btree.nEq = 1;
+ /* TUNING: Cost of a rowid lookup is 10 */
+ pLoop->rRun = 33; /* 33==sqlite3LogEst(10) */
+ }else{
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
+ int opMask;
+ assert( pLoop->aLTermSpace==pLoop->aLTerm );
+ if( !IsUniqueIndex(pIdx)
+ || pIdx->pPartIdxWhere!=0
+ || pIdx->nKeyCol>ArraySize(pLoop->aLTermSpace)
+ ) continue;
+ opMask = pIdx->uniqNotNull ? (WO_EQ|WO_IS) : WO_EQ;
+ for(j=0; j<pIdx->nKeyCol; j++){
+ pTerm = sqlite3WhereFindTerm(pWC, iCur, j, 0, opMask, pIdx);
+ if( pTerm==0 ) break;
+ testcase( pTerm->eOperator & WO_IS );
+ pLoop->aLTerm[j] = pTerm;
+ }
+ if( j!=pIdx->nKeyCol ) continue;
+ pLoop->wsFlags = WHERE_COLUMN_EQ|WHERE_ONEROW|WHERE_INDEXED;
+ if( pIdx->isCovering || (pItem->colUsed & ~columnsInIndex(pIdx))==0 ){
+ pLoop->wsFlags |= WHERE_IDX_ONLY;
+ }
+ pLoop->nLTerm = j;
+ pLoop->u.btree.nEq = j;
+ pLoop->u.btree.pIndex = pIdx;
+ /* TUNING: Cost of a unique index lookup is 15 */
+ pLoop->rRun = 39; /* 39==sqlite3LogEst(15) */
+ break;
}
- whereClauseClear(pWInfo->pWC);
- sqlite3DbFree(db, pWInfo);
}
+ if( pLoop->wsFlags ){
+ pLoop->nOut = (LogEst)1;
+ pWInfo->a[0].pWLoop = pLoop;
+ pLoop->maskSelf = sqlite3WhereGetMask(&pWInfo->sMaskSet, iCur);
+ pWInfo->a[0].iTabCur = iCur;
+ pWInfo->nRowOut = 1;
+ if( pWInfo->pOrderBy ) pWInfo->nOBSat = pWInfo->pOrderBy->nExpr;
+ if( pWInfo->wctrlFlags & WHERE_WANT_DISTINCT ){
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }
+#ifdef SQLITE_DEBUG
+ pLoop->cId = '0';
+#endif
+ return 1;
+ }
+ return 0;
}
-
/*
** Generate the beginning of the loop used for WHERE clause processing.
** The return value is a pointer to an opaque structure that contains
@@ -104881,40 +132134,66 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
**
** ORDER BY CLAUSE PROCESSING
**
-** *ppOrderBy is a pointer to the ORDER BY clause of a SELECT statement,
+** pOrderBy is a pointer to the ORDER BY clause (or the GROUP BY clause
+** if the WHERE_GROUPBY flag is set in wctrlFlags) of a SELECT statement
** if there is one. If there is no ORDER BY clause or if this routine
-** is called from an UPDATE or DELETE statement, then ppOrderBy is NULL.
-**
-** If an index can be used so that the natural output order of the table
-** scan is correct for the ORDER BY clause, then that index is used and
-** *ppOrderBy is set to NULL. This is an optimization that prevents an
-** unnecessary sort of the result set if an index appropriate for the
-** ORDER BY clause already exists.
-**
-** If the where clause loops cannot be arranged to provide the correct
-** output order, then the *ppOrderBy is unchanged.
+** is called from an UPDATE or DELETE statement, then pOrderBy is NULL.
+**
+** The iIdxCur parameter is the cursor number of an index. If
+** WHERE_OR_SUBCLAUSE is set, iIdxCur is the cursor number of an index
+** to use for OR clause processing. The WHERE clause should use this
+** specific cursor. If WHERE_ONEPASS_DESIRED is set, then iIdxCur is
+** the first cursor in an array of cursors for all indices. iIdxCur should
+** be used to compute the appropriate cursor depending on which index is
+** used.
*/
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
- Parse *pParse, /* The parser context */
- SrcList *pTabList, /* A list of all tables to be scanned */
- Expr *pWhere, /* The WHERE clause */
- ExprList **ppOrderBy, /* An ORDER BY clause, or NULL */
- ExprList *pDistinct, /* The select-list for DISTINCT queries - or NULL */
- u16 wctrlFlags /* One of the WHERE_* flags defined in sqliteInt.h */
+ Parse *pParse, /* The parser context */
+ SrcList *pTabList, /* FROM clause: A list of all tables to be scanned */
+ Expr *pWhere, /* The WHERE clause */
+ ExprList *pOrderBy, /* An ORDER BY (or GROUP BY) clause, or NULL */
+ ExprList *pDistinctSet, /* Try not to output two rows that duplicate these */
+ u16 wctrlFlags, /* The WHERE_* flags defined in sqliteInt.h */
+ int iAuxArg /* If WHERE_OR_SUBCLAUSE is set, index cursor number
+ ** If WHERE_USE_LIMIT, then the limit amount */
){
- int i; /* Loop counter */
int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */
int nTabList; /* Number of elements in pTabList */
WhereInfo *pWInfo; /* Will become the return value of this function */
Vdbe *v = pParse->pVdbe; /* The virtual database engine */
Bitmask notReady; /* Cursors that are not yet positioned */
+ WhereLoopBuilder sWLB; /* The WhereLoop builder */
WhereMaskSet *pMaskSet; /* The expression mask set */
- WhereClause *pWC; /* Decomposition of the WHERE clause */
- struct SrcList_item *pTabItem; /* A single entry from pTabList */
- WhereLevel *pLevel; /* A single level in the pWInfo list */
- int iFrom; /* First unused FROM clause element */
- int andFlags; /* AND-ed combination of all pWC->a[].wtFlags */
+ WhereLevel *pLevel; /* A single level in pWInfo->a[] */
+ WhereLoop *pLoop; /* Pointer to a single WhereLoop object */
+ int ii; /* Loop counter */
sqlite3 *db; /* Database connection */
+ int rc; /* Return code */
+ u8 bFordelete = 0; /* OPFLAG_FORDELETE or zero, as appropriate */
+
+ assert( (wctrlFlags & WHERE_ONEPASS_MULTIROW)==0 || (
+ (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0
+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
+ ));
+
+ /* Only one of WHERE_OR_SUBCLAUSE or WHERE_USE_LIMIT */
+ assert( (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
+ || (wctrlFlags & WHERE_USE_LIMIT)==0 );
+
+ /* Variable initialization */
+ db = pParse->db;
+ memset(&sWLB, 0, sizeof(sWLB));
+
+ /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
+ testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
+ if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;
+ sWLB.pOrderBy = pOrderBy;
+
+ /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
+ ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
+ if( OptimizationDisabled(db, SQLITE_DistinctOpt) ){
+ wctrlFlags &= ~WHERE_WANT_DISTINCT;
+ }
/* The number of tables in the FROM clause is limited by the number of
** bits in a Bitmask
@@ -104926,11 +132205,11 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
/* This function normally generates a nested loop for all tables in
- ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should
+ ** pTabList. But if the WHERE_OR_SUBCLAUSE flag is set, then we should
** only generate code for the first table in pTabList and assume that
** any cursors associated with subsequent tables are uninitialized.
*/
- nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc;
+ nTabList = (wctrlFlags & WHERE_OR_SUBCLAUSE) ? 1 : pTabList->nSrc;
/* Allocate and initialize the WhereInfo structure that will become the
** return value. A single allocation is used to store the WhereInfo
@@ -104939,396 +132218,348 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- db = pParse->db;
nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));
- pWInfo = sqlite3DbMallocZero(db,
- nByteWInfo +
- sizeof(WhereClause) +
- sizeof(WhereMaskSet)
- );
+ pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
pWInfo = 0;
goto whereBeginError;
}
- pWInfo->nLevel = nTabList;
pWInfo->pParse = pParse;
pWInfo->pTabList = pTabList;
- pWInfo->iBreak = sqlite3VdbeMakeLabel(v);
- pWInfo->pWC = pWC = (WhereClause *)&((u8 *)pWInfo)[nByteWInfo];
+ pWInfo->pOrderBy = pOrderBy;
+ pWInfo->pDistinctSet = pDistinctSet;
+ pWInfo->aiCurOnePass[0] = pWInfo->aiCurOnePass[1] = -1;
+ pWInfo->nLevel = nTabList;
+ pWInfo->iBreak = pWInfo->iContinue = sqlite3VdbeMakeLabel(v);
pWInfo->wctrlFlags = wctrlFlags;
+ pWInfo->iLimit = iAuxArg;
pWInfo->savedNQueryLoop = pParse->nQueryLoop;
- pMaskSet = (WhereMaskSet*)&pWC[1];
-
- /* Disable the DISTINCT optimization if SQLITE_DistinctOpt is set via
- ** sqlite3_test_ctrl(SQLITE_TESTCTRL_OPTIMIZATIONS,...) */
- if( db->flags & SQLITE_DistinctOpt ) pDistinct = 0;
+ memset(&pWInfo->nOBSat, 0,
+ offsetof(WhereInfo,sWC) - offsetof(WhereInfo,nOBSat));
+ memset(&pWInfo->a[0], 0, sizeof(WhereLoop)+nTabList*sizeof(WhereLevel));
+ assert( pWInfo->eOnePass==ONEPASS_OFF ); /* ONEPASS defaults to OFF */
+ pMaskSet = &pWInfo->sMaskSet;
+ sWLB.pWInfo = pWInfo;
+ sWLB.pWC = &pWInfo->sWC;
+ sWLB.pNew = (WhereLoop*)(((char*)pWInfo)+nByteWInfo);
+ assert( EIGHT_BYTE_ALIGNMENT(sWLB.pNew) );
+ whereLoopInit(sWLB.pNew);
+#ifdef SQLITE_DEBUG
+ sWLB.pNew->cId = '*';
+#endif
/* Split the WHERE clause into separate subexpressions where each
** subexpression is separated by an AND operator.
*/
initMaskSet(pMaskSet);
- whereClauseInit(pWC, pParse, pMaskSet);
- sqlite3ExprCodeConstants(pParse, pWhere);
- whereSplit(pWC, pWhere, TK_AND); /* IMP: R-15842-53296 */
+ sqlite3WhereClauseInit(&pWInfo->sWC, pWInfo);
+ sqlite3WhereSplit(&pWInfo->sWC, pWhere, TK_AND);
/* Special case: a WHERE clause that is constant. Evaluate the
** expression and either jump over all of the code or fall thru.
*/
- if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){
- sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL);
- pWhere = 0;
+ for(ii=0; ii<sWLB.pWC->nTerm; ii++){
+ if( nTabList==0 || sqlite3ExprIsConstantNotJoin(sWLB.pWC->a[ii].pExpr) ){
+ sqlite3ExprIfFalse(pParse, sWLB.pWC->a[ii].pExpr, pWInfo->iBreak,
+ SQLITE_JUMPIFNULL);
+ sWLB.pWC->a[ii].wtFlags |= TERM_CODED;
+ }
+ }
+
+ /* Special case: No FROM clause
+ */
+ if( nTabList==0 ){
+ if( pOrderBy ) pWInfo->nOBSat = pOrderBy->nExpr;
+ if( wctrlFlags & WHERE_WANT_DISTINCT ){
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }
}
/* Assign a bit from the bitmask to every term in the FROM clause.
**
- ** When assigning bitmask values to FROM clause cursors, it must be
- ** the case that if X is the bitmask for the N-th FROM clause term then
- ** the bitmask for all FROM clause terms to the left of the N-th term
- ** is (X-1). An expression from the ON clause of a LEFT JOIN can use
- ** its Expr.iRightJoinTable value to find the bitmask of the right table
- ** of the join. Subtracting one from the right table bitmask gives a
- ** bitmask for all tables to the left of the join. Knowing the bitmask
- ** for all tables to the left of a left join is important. Ticket #3015.
+ ** The N-th term of the FROM clause is assigned a bitmask of 1<<N.
**
- ** Configure the WhereClause.vmask variable so that bits that correspond
- ** to virtual table cursors are set. This is used to selectively disable
- ** the OR-to-IN transformation in exprAnalyzeOrTerm(). It is not helpful
- ** with virtual tables.
+ ** The rule of the previous sentence ensures thta if X is the bitmask for
+ ** a table T, then X-1 is the bitmask for all other tables to the left of T.
+ ** Knowing the bitmask for all tables to the left of a left join is
+ ** important. Ticket #3015.
**
** Note that bitmasks are created for all pTabList->nSrc tables in
** pTabList, not just the first nTabList tables. nTabList is normally
** equal to pTabList->nSrc but might be shortened to 1 if the
- ** WHERE_ONETABLE_ONLY flag is set.
+ ** WHERE_OR_SUBCLAUSE flag is set.
*/
- assert( pWC->vmask==0 && pMaskSet->n==0 );
- for(i=0; i<pTabList->nSrc; i++){
- createMask(pMaskSet, pTabList->a[i].iCursor);
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( ALWAYS(pTabList->a[i].pTab) && IsVirtual(pTabList->a[i].pTab) ){
- pWC->vmask |= ((Bitmask)1 << i);
- }
-#endif
+ for(ii=0; ii<pTabList->nSrc; ii++){
+ createMask(pMaskSet, pTabList->a[ii].iCursor);
+ sqlite3WhereTabFuncArgs(pParse, &pTabList->a[ii], &pWInfo->sWC);
}
-#ifndef NDEBUG
- {
- Bitmask toTheLeft = 0;
- for(i=0; i<pTabList->nSrc; i++){
- Bitmask m = getMask(pMaskSet, pTabList->a[i].iCursor);
- assert( (m-1)==toTheLeft );
- toTheLeft |= m;
- }
+#ifdef SQLITE_DEBUG
+ for(ii=0; ii<pTabList->nSrc; ii++){
+ Bitmask m = sqlite3WhereGetMask(pMaskSet, pTabList->a[ii].iCursor);
+ assert( m==MASKBIT(ii) );
}
#endif
- /* Analyze all of the subexpressions. Note that exprAnalyze() might
- ** add new virtual terms onto the end of the WHERE clause. We do not
- ** want to analyze these virtual terms, so start analyzing at the end
- ** and work forward so that the added virtual terms are never processed.
- */
- exprAnalyzeAll(pTabList, pWC);
- if( db->mallocFailed ){
- goto whereBeginError;
- }
+ /* Analyze all of the subexpressions. */
+ sqlite3WhereExprAnalyze(pTabList, &pWInfo->sWC);
+ if( db->mallocFailed ) goto whereBeginError;
- /* Check if the DISTINCT qualifier, if there is one, is redundant.
- ** If it is, then set pDistinct to NULL and WhereInfo.eDistinct to
- ** WHERE_DISTINCT_UNIQUE to tell the caller to ignore the DISTINCT.
- */
- if( pDistinct && isDistinctRedundant(pParse, pTabList, pWC, pDistinct) ){
- pDistinct = 0;
- pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ if( wctrlFlags & WHERE_WANT_DISTINCT ){
+ if( isDistinctRedundant(pParse, pTabList, &pWInfo->sWC, pDistinctSet) ){
+ /* The DISTINCT marking is pointless. Ignore it. */
+ pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
+ }else if( pOrderBy==0 ){
+ /* Try to ORDER BY the result set to make distinct processing easier */
+ pWInfo->wctrlFlags |= WHERE_DISTINCTBY;
+ pWInfo->pOrderBy = pDistinctSet;
+ }
}
- /* Chose the best index to use for each table in the FROM clause.
- **
- ** This loop fills in the following fields:
- **
- ** pWInfo->a[].pIdx The index to use for this level of the loop.
- ** pWInfo->a[].wsFlags WHERE_xxx flags associated with pIdx
- ** pWInfo->a[].nEq The number of == and IN constraints
- ** pWInfo->a[].iFrom Which term of the FROM clause is being coded
- ** pWInfo->a[].iTabCur The VDBE cursor for the database table
- ** pWInfo->a[].iIdxCur The VDBE cursor for the index
- ** pWInfo->a[].pTerm When wsFlags==WO_OR, the OR-clause term
- **
- ** This loop also figures out the nesting order of tables in the FROM
- ** clause.
- */
- notReady = ~(Bitmask)0;
- andFlags = ~0;
- WHERETRACE(("*** Optimizer Start ***\n"));
- for(i=iFrom=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
- WhereCost bestPlan; /* Most efficient plan seen so far */
- Index *pIdx; /* Index for FROM table at pTabItem */
- int j; /* For looping over FROM tables */
- int bestJ = -1; /* The value of j */
- Bitmask m; /* Bitmask value for j or bestJ */
- int isOptimal; /* Iterator for optimal/non-optimal search */
- int nUnconstrained; /* Number tables without INDEXED BY */
- Bitmask notIndexed; /* Mask of tables that cannot use an index */
-
- memset(&bestPlan, 0, sizeof(bestPlan));
- bestPlan.rCost = SQLITE_BIG_DBL;
- WHERETRACE(("*** Begin search for loop %d ***\n", i));
-
- /* Loop through the remaining entries in the FROM clause to find the
- ** next nested loop. The loop tests all FROM clause entries
- ** either once or twice.
- **
- ** The first test is always performed if there are two or more entries
- ** remaining and never performed if there is only one FROM clause entry
- ** to choose from. The first test looks for an "optimal" scan. In
- ** this context an optimal scan is one that uses the same strategy
- ** for the given FROM clause entry as would be selected if the entry
- ** were used as the innermost nested loop. In other words, a table
- ** is chosen such that the cost of running that table cannot be reduced
- ** by waiting for other tables to run first. This "optimal" test works
- ** by first assuming that the FROM clause is on the inner loop and finding
- ** its query plan, then checking to see if that query plan uses any
- ** other FROM clause terms that are notReady. If no notReady terms are
- ** used then the "optimal" query plan works.
- **
- ** Note that the WhereCost.nRow parameter for an optimal scan might
- ** not be as small as it would be if the table really were the innermost
- ** join. The nRow value can be reduced by WHERE clause constraints
- ** that do not use indices. But this nRow reduction only happens if the
- ** table really is the innermost join.
- **
- ** The second loop iteration is only performed if no optimal scan
- ** strategies were found by the first iteration. This second iteration
- ** is used to search for the lowest cost scan overall.
- **
- ** Previous versions of SQLite performed only the second iteration -
- ** the next outermost loop was always that with the lowest overall
- ** cost. However, this meant that SQLite could select the wrong plan
- ** for scripts such as the following:
- **
- ** CREATE TABLE t1(a, b);
- ** CREATE TABLE t2(c, d);
- ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a;
- **
- ** The best strategy is to iterate through table t1 first. However it
- ** is not possible to determine this with a simple greedy algorithm.
- ** Since the cost of a linear scan through table t2 is the same
- ** as the cost of a linear scan through table t1, a simple greedy
- ** algorithm may choose to use t2 for the outer loop, which is a much
- ** costlier approach.
- */
- nUnconstrained = 0;
- notIndexed = 0;
- for(isOptimal=(iFrom<nTabList-1); isOptimal>=0 && bestJ<0; isOptimal--){
- Bitmask mask; /* Mask of tables not yet ready */
- for(j=iFrom, pTabItem=&pTabList->a[j]; j<nTabList; j++, pTabItem++){
- int doNotReorder; /* True if this table should not be reordered */
- WhereCost sCost; /* Cost information from best[Virtual]Index() */
- ExprList *pOrderBy; /* ORDER BY clause for index to optimize */
- ExprList *pDist; /* DISTINCT clause for index to optimize */
-
- doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0;
- if( j!=iFrom && doNotReorder ) break;
- m = getMask(pMaskSet, pTabItem->iCursor);
- if( (m & notReady)==0 ){
- if( j==iFrom ) iFrom++;
- continue;
- }
- mask = (isOptimal ? m : notReady);
- pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0);
- pDist = (i==0 ? pDistinct : 0);
- if( pTabItem->pIndex==0 ) nUnconstrained++;
-
- WHERETRACE(("=== trying table %d with isOptimal=%d ===\n",
- j, isOptimal));
- assert( pTabItem->pTab );
-#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( IsVirtual(pTabItem->pTab) ){
- sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo;
- bestVirtualIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
- &sCost, pp);
- }else
+ /* Construct the WhereLoop objects */
+#if defined(WHERETRACE_ENABLED)
+ if( sqlite3WhereTrace & 0xffff ){
+ sqlite3DebugPrintf("*** Optimizer Start *** (wctrlFlags: 0x%x",wctrlFlags);
+ if( wctrlFlags & WHERE_USE_LIMIT ){
+ sqlite3DebugPrintf(", limit: %d", iAuxArg);
+ }
+ sqlite3DebugPrintf(")\n");
+ }
+ if( sqlite3WhereTrace & 0x100 ){ /* Display all terms of the WHERE clause */
+ sqlite3WhereClausePrint(sWLB.pWC);
+ }
#endif
- {
- bestBtreeIndex(pParse, pWC, pTabItem, mask, notReady, pOrderBy,
- pDist, &sCost);
- }
- assert( isOptimal || (sCost.used&notReady)==0 );
-
- /* If an INDEXED BY clause is present, then the plan must use that
- ** index if it uses any index at all */
- assert( pTabItem->pIndex==0
- || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
- || sCost.plan.u.pIdx==pTabItem->pIndex );
- if( isOptimal && (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)==0 ){
- notIndexed |= m;
- }
-
- /* Conditions under which this table becomes the best so far:
- **
- ** (1) The table must not depend on other tables that have not
- ** yet run.
- **
- ** (2) A full-table-scan plan cannot supercede indexed plan unless
- ** the full-table-scan is an "optimal" plan as defined above.
- **
- ** (3) All tables have an INDEXED BY clause or this table lacks an
- ** INDEXED BY clause or this table uses the specific
- ** index specified by its INDEXED BY clause. This rule ensures
- ** that a best-so-far is always selected even if an impossible
- ** combination of INDEXED BY clauses are given. The error
- ** will be detected and relayed back to the application later.
- ** The NEVER() comes about because rule (2) above prevents
- ** An indexable full-table-scan from reaching rule (3).
- **
- ** (4) The plan cost must be lower than prior plans or else the
- ** cost must be the same and the number of rows must be lower.
- */
- if( (sCost.used&notReady)==0 /* (1) */
- && (bestJ<0 || (notIndexed&m)!=0 /* (2) */
- || (bestPlan.plan.wsFlags & WHERE_NOT_FULLSCAN)==0
- || (sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0)
- && (nUnconstrained==0 || pTabItem->pIndex==0 /* (3) */
- || NEVER((sCost.plan.wsFlags & WHERE_NOT_FULLSCAN)!=0))
- && (bestJ<0 || sCost.rCost<bestPlan.rCost /* (4) */
- || (sCost.rCost<=bestPlan.rCost
- && sCost.plan.nRow<bestPlan.plan.nRow))
- ){
- WHERETRACE(("=== table %d is best so far"
- " with cost=%g and nRow=%g\n",
- j, sCost.rCost, sCost.plan.nRow));
- bestPlan = sCost;
- bestJ = j;
- }
- if( doNotReorder ) break;
+ if( nTabList!=1 || whereShortCut(&sWLB)==0 ){
+ rc = whereLoopAddAll(&sWLB);
+ if( rc ) goto whereBeginError;
+
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace ){ /* Display all of the WhereLoop objects */
+ WhereLoop *p;
+ int i;
+ static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz"
+ "ABCDEFGHIJKLMNOPQRSTUVWYXZ";
+ for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){
+ p->cId = zLabel[i%sizeof(zLabel)];
+ whereLoopPrint(p, sWLB.pWC);
}
}
- assert( bestJ>=0 );
- assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) );
- WHERETRACE(("*** Optimizer selects table %d for loop %d"
- " with cost=%g and nRow=%g\n",
- bestJ, pLevel-pWInfo->a, bestPlan.rCost, bestPlan.plan.nRow));
- if( (bestPlan.plan.wsFlags & WHERE_ORDERBY)!=0 ){
- *ppOrderBy = 0;
+#endif
+
+ wherePathSolver(pWInfo, 0);
+ if( db->mallocFailed ) goto whereBeginError;
+ if( pWInfo->pOrderBy ){
+ wherePathSolver(pWInfo, pWInfo->nRowOut+1);
+ if( db->mallocFailed ) goto whereBeginError;
}
- if( (bestPlan.plan.wsFlags & WHERE_DISTINCT)!=0 ){
- assert( pWInfo->eDistinct==0 );
- pWInfo->eDistinct = WHERE_DISTINCT_ORDERED;
+ }
+ if( pWInfo->pOrderBy==0 && (db->flags & SQLITE_ReverseOrder)!=0 ){
+ pWInfo->revMask = ALLBITS;
+ }
+ if( pParse->nErr || NEVER(db->mallocFailed) ){
+ goto whereBeginError;
+ }
+#ifdef WHERETRACE_ENABLED
+ if( sqlite3WhereTrace ){
+ sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
+ if( pWInfo->nOBSat>0 ){
+ sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask);
}
- andFlags &= bestPlan.plan.wsFlags;
- pLevel->plan = bestPlan.plan;
- testcase( bestPlan.plan.wsFlags & WHERE_INDEXED );
- testcase( bestPlan.plan.wsFlags & WHERE_TEMP_INDEX );
- if( bestPlan.plan.wsFlags & (WHERE_INDEXED|WHERE_TEMP_INDEX) ){
- pLevel->iIdxCur = pParse->nTab++;
- }else{
- pLevel->iIdxCur = -1;
+ switch( pWInfo->eDistinct ){
+ case WHERE_DISTINCT_UNIQUE: {
+ sqlite3DebugPrintf(" DISTINCT=unique");
+ break;
+ }
+ case WHERE_DISTINCT_ORDERED: {
+ sqlite3DebugPrintf(" DISTINCT=ordered");
+ break;
+ }
+ case WHERE_DISTINCT_UNORDERED: {
+ sqlite3DebugPrintf(" DISTINCT=unordered");
+ break;
+ }
}
- notReady &= ~getMask(pMaskSet, pTabList->a[bestJ].iCursor);
- pLevel->iFrom = (u8)bestJ;
- if( bestPlan.plan.nRow>=(double)1 ){
- pParse->nQueryLoop *= bestPlan.plan.nRow;
+ sqlite3DebugPrintf("\n");
+ for(ii=0; ii<pWInfo->nLevel; ii++){
+ whereLoopPrint(pWInfo->a[ii].pWLoop, sWLB.pWC);
}
-
- /* Check that if the table scanned by this loop iteration had an
- ** INDEXED BY clause attached to it, that the named index is being
- ** used for the scan. If not, then query compilation has failed.
- ** Return an error.
- */
- pIdx = pTabList->a[bestJ].pIndex;
- if( pIdx ){
- if( (bestPlan.plan.wsFlags & WHERE_INDEXED)==0 ){
- sqlite3ErrorMsg(pParse, "cannot use index: %s", pIdx->zName);
- goto whereBeginError;
- }else{
- /* If an INDEXED BY clause is used, the bestIndex() function is
- ** guaranteed to find the index specified in the INDEXED BY clause
- ** if it find an index at all. */
- assert( bestPlan.plan.u.pIdx==pIdx );
+ }
+#endif
+ /* Attempt to omit tables from the join that do not effect the result */
+ if( pWInfo->nLevel>=2
+ && pDistinctSet!=0
+ && OptimizationEnabled(db, SQLITE_OmitNoopJoin)
+ ){
+ Bitmask tabUsed = sqlite3WhereExprListUsage(pMaskSet, pDistinctSet);
+ if( sWLB.pOrderBy ){
+ tabUsed |= sqlite3WhereExprListUsage(pMaskSet, sWLB.pOrderBy);
+ }
+ while( pWInfo->nLevel>=2 ){
+ WhereTerm *pTerm, *pEnd;
+ pLoop = pWInfo->a[pWInfo->nLevel-1].pWLoop;
+ if( (pWInfo->pTabList->a[pLoop->iTab].fg.jointype & JT_LEFT)==0 ) break;
+ if( (wctrlFlags & WHERE_WANT_DISTINCT)==0
+ && (pLoop->wsFlags & WHERE_ONEROW)==0
+ ){
+ break;
}
+ if( (tabUsed & pLoop->maskSelf)!=0 ) break;
+ pEnd = sWLB.pWC->a + sWLB.pWC->nTerm;
+ for(pTerm=sWLB.pWC->a; pTerm<pEnd; pTerm++){
+ if( (pTerm->prereqAll & pLoop->maskSelf)!=0
+ && !ExprHasProperty(pTerm->pExpr, EP_FromJoin)
+ ){
+ break;
+ }
+ }
+ if( pTerm<pEnd ) break;
+ WHERETRACE(0xffff, ("-> drop loop %c not used\n", pLoop->cId));
+ pWInfo->nLevel--;
+ nTabList--;
}
}
- WHERETRACE(("*** Optimizer Finished ***\n"));
- if( pParse->nErr || db->mallocFailed ){
- goto whereBeginError;
- }
-
- /* If the total query only selects a single row, then the ORDER BY
- ** clause is irrelevant.
- */
- if( (andFlags & WHERE_UNIQUE)!=0 && ppOrderBy ){
- *ppOrderBy = 0;
- }
+ WHERETRACE(0xffff,("*** Optimizer Finished ***\n"));
+ pWInfo->pParse->nQueryLoop += pWInfo->nRowOut;
/* If the caller is an UPDATE or DELETE statement that is requesting
** to use a one-pass algorithm, determine if this is appropriate.
- ** The one-pass algorithm only works if the WHERE clause constraints
- ** the statement to update a single row.
*/
assert( (wctrlFlags & WHERE_ONEPASS_DESIRED)==0 || pWInfo->nLevel==1 );
- if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 && (andFlags & WHERE_UNIQUE)!=0 ){
- pWInfo->okOnePass = 1;
- pWInfo->a[0].plan.wsFlags &= ~WHERE_IDX_ONLY;
+ if( (wctrlFlags & WHERE_ONEPASS_DESIRED)!=0 ){
+ int wsFlags = pWInfo->a[0].pWLoop->wsFlags;
+ int bOnerow = (wsFlags & WHERE_ONEROW)!=0;
+ if( bOnerow
+ || ((wctrlFlags & WHERE_ONEPASS_MULTIROW)!=0
+ && 0==(wsFlags & WHERE_VIRTUALTABLE))
+ ){
+ pWInfo->eOnePass = bOnerow ? ONEPASS_SINGLE : ONEPASS_MULTI;
+ if( HasRowid(pTabList->a[0].pTab) && (wsFlags & WHERE_IDX_ONLY) ){
+ if( wctrlFlags & WHERE_ONEPASS_MULTIROW ){
+ bFordelete = OPFLAG_FORDELETE;
+ }
+ pWInfo->a[0].pWLoop->wsFlags = (wsFlags & ~WHERE_IDX_ONLY);
+ }
+ }
}
/* Open all tables in the pTabList and any indices selected for
** searching those tables.
*/
- sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */
- notReady = ~(Bitmask)0;
- pWInfo->nRowOut = (double)1;
- for(i=0, pLevel=pWInfo->a; i<nTabList; i++, pLevel++){
+ for(ii=0, pLevel=pWInfo->a; ii<nTabList; ii++, pLevel++){
Table *pTab; /* Table to open */
int iDb; /* Index of database containing table/index */
+ struct SrcList_item *pTabItem;
pTabItem = &pTabList->a[pLevel->iFrom];
pTab = pTabItem->pTab;
- pLevel->iTabCur = pTabItem->iCursor;
- pWInfo->nRowOut *= pLevel->plan.nRow;
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ pLoop = pLevel->pWLoop;
if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ){
/* Do nothing */
}else
#ifndef SQLITE_OMIT_VIRTUALTABLE
- if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){
+ if( (pLoop->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
const char *pVTab = (const char *)sqlite3GetVTable(db, pTab);
int iCur = pTabItem->iCursor;
sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB);
+ }else if( IsVirtual(pTab) ){
+ /* noop */
}else
#endif
- if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- && (wctrlFlags & WHERE_OMIT_OPEN)==0 ){
- int op = pWInfo->okOnePass ? OP_OpenWrite : OP_OpenRead;
+ if( (pLoop->wsFlags & WHERE_IDX_ONLY)==0
+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0 ){
+ int op = OP_OpenRead;
+ if( pWInfo->eOnePass!=ONEPASS_OFF ){
+ op = OP_OpenWrite;
+ pWInfo->aiCurOnePass[0] = pTabItem->iCursor;
+ };
sqlite3OpenTable(pParse, pTabItem->iCursor, iDb, pTab, op);
- testcase( pTab->nCol==BMS-1 );
- testcase( pTab->nCol==BMS );
- if( !pWInfo->okOnePass && pTab->nCol<BMS ){
+ assert( pTabItem->iCursor==pLevel->iTabCur );
+ testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS-1 );
+ testcase( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol==BMS );
+ if( pWInfo->eOnePass==ONEPASS_OFF && pTab->nCol<BMS && HasRowid(pTab) ){
Bitmask b = pTabItem->colUsed;
int n = 0;
for(; b; b=b>>1, n++){}
- sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1,
- SQLITE_INT_TO_PTR(n), P4_INT32);
+ sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(n), P4_INT32);
assert( n<=pTab->nCol );
}
+#ifdef SQLITE_ENABLE_CURSOR_HINTS
+ if( pLoop->u.btree.pIndex!=0 ){
+ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ|bFordelete);
+ }else
+#endif
+ {
+ sqlite3VdbeChangeP5(v, bFordelete);
+ }
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, pTabItem->iCursor, 0, 0,
+ (const u8*)&pTabItem->colUsed, P4_INT64);
+#endif
}else{
sqlite3TableLock(pParse, iDb, pTab->tnum, 0, pTab->zName);
}
-#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
- if( (pLevel->plan.wsFlags & WHERE_TEMP_INDEX)!=0 ){
- constructAutomaticIndex(pParse, pWC, pTabItem, notReady, pLevel);
- }else
-#endif
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
- Index *pIx = pLevel->plan.u.pIdx;
- KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIx);
- int iIdxCur = pLevel->iIdxCur;
+ if( pLoop->wsFlags & WHERE_INDEXED ){
+ Index *pIx = pLoop->u.btree.pIndex;
+ int iIndexCur;
+ int op = OP_OpenRead;
+ /* iAuxArg is always set if to a positive value if ONEPASS is possible */
+ assert( iAuxArg!=0 || (pWInfo->wctrlFlags & WHERE_ONEPASS_DESIRED)==0 );
+ if( !HasRowid(pTab) && IsPrimaryKeyIndex(pIx)
+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0
+ ){
+ /* This is one term of an OR-optimization using the PRIMARY KEY of a
+ ** WITHOUT ROWID table. No need for a separate index */
+ iIndexCur = pLevel->iTabCur;
+ op = 0;
+ }else if( pWInfo->eOnePass!=ONEPASS_OFF ){
+ Index *pJ = pTabItem->pTab->pIndex;
+ iIndexCur = iAuxArg;
+ assert( wctrlFlags & WHERE_ONEPASS_DESIRED );
+ while( ALWAYS(pJ) && pJ!=pIx ){
+ iIndexCur++;
+ pJ = pJ->pNext;
+ }
+ op = OP_OpenWrite;
+ pWInfo->aiCurOnePass[1] = iIndexCur;
+ }else if( iAuxArg && (wctrlFlags & WHERE_OR_SUBCLAUSE)!=0 ){
+ iIndexCur = iAuxArg;
+ op = OP_ReopenIdx;
+ }else{
+ iIndexCur = pParse->nTab++;
+ }
+ pLevel->iIdxCur = iIndexCur;
assert( pIx->pSchema==pTab->pSchema );
- assert( iIdxCur>=0 );
- sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIx->tnum, iDb,
- (char*)pKey, P4_KEYINFO_HANDOFF);
- VdbeComment((v, "%s", pIx->zName));
+ assert( iIndexCur>=0 );
+ if( op ){
+ sqlite3VdbeAddOp3(v, op, iIndexCur, pIx->tnum, iDb);
+ sqlite3VdbeSetP4KeyInfo(pParse, pIx);
+ if( (pLoop->wsFlags & WHERE_CONSTRAINT)!=0
+ && (pLoop->wsFlags & (WHERE_COLUMN_RANGE|WHERE_SKIPSCAN))==0
+ && (pWInfo->wctrlFlags&WHERE_ORDERBY_MIN)==0
+ ){
+ sqlite3VdbeChangeP5(v, OPFLAG_SEEKEQ); /* Hint to COMDB2 */
+ }
+ VdbeComment((v, "%s", pIx->zName));
+#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
+ {
+ u64 colUsed = 0;
+ int ii, jj;
+ for(ii=0; ii<pIx->nColumn; ii++){
+ jj = pIx->aiColumn[ii];
+ if( jj<0 ) continue;
+ if( jj>63 ) jj = 63;
+ if( (pTabItem->colUsed & MASKBIT(jj))==0 ) continue;
+ colUsed |= ((u64)1)<<(ii<63 ? ii : 63);
+ }
+ sqlite3VdbeAddOp4Dup8(v, OP_ColumnsUsed, iIndexCur, 0, 0,
+ (u8*)&colUsed, P4_INT64);
+ }
+#endif /* SQLITE_ENABLE_COLUMN_USED_MASK */
+ }
}
- sqlite3CodeVerifySchema(pParse, iDb);
- notReady &= ~getMask(pWC->pMaskSet, pTabItem->iCursor);
+ if( iDb>=0 ) sqlite3CodeVerifySchema(pParse, iDb);
}
pWInfo->iTop = sqlite3VdbeCurrentAddr(v);
if( db->mallocFailed ) goto whereBeginError;
@@ -105338,65 +132569,31 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** program.
*/
notReady = ~(Bitmask)0;
- for(i=0; i<nTabList; i++){
- pLevel = &pWInfo->a[i];
- explainOneScan(pParse, pTabList, pLevel, i, pLevel->iFrom, wctrlFlags);
- notReady = codeOneLoopStart(pWInfo, i, wctrlFlags, notReady);
- pWInfo->iContinue = pLevel->addrCont;
- }
-
-#ifdef SQLITE_TEST /* For testing and debugging use only */
- /* Record in the query plan information about the current table
- ** and the index used to access it (if any). If the table itself
- ** is not used, its name is just '{}'. If no index is used
- ** the index is listed as "{}". If the primary key is used the
- ** index name is '*'.
- */
- for(i=0; i<nTabList; i++){
- char *z;
- int n;
- pLevel = &pWInfo->a[i];
- pTabItem = &pTabList->a[pLevel->iFrom];
- z = pTabItem->zAlias;
- if( z==0 ) z = pTabItem->pTab->zName;
- n = sqlite3Strlen30(z);
- if( n+nQPlan < sizeof(sqlite3_query_plan)-10 ){
- if( pLevel->plan.wsFlags & WHERE_IDX_ONLY ){
- memcpy(&sqlite3_query_plan[nQPlan], "{}", 2);
- nQPlan += 2;
- }else{
- memcpy(&sqlite3_query_plan[nQPlan], z, n);
- nQPlan += n;
- }
- sqlite3_query_plan[nQPlan++] = ' ';
+ for(ii=0; ii<nTabList; ii++){
+ int addrExplain;
+ int wsFlags;
+ pLevel = &pWInfo->a[ii];
+ wsFlags = pLevel->pWLoop->wsFlags;
+#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
+ if( (pLevel->pWLoop->wsFlags & WHERE_AUTO_INDEX)!=0 ){
+ constructAutomaticIndex(pParse, &pWInfo->sWC,
+ &pTabList->a[pLevel->iFrom], notReady, pLevel);
+ if( db->mallocFailed ) goto whereBeginError;
}
- testcase( pLevel->plan.wsFlags & WHERE_ROWID_EQ );
- testcase( pLevel->plan.wsFlags & WHERE_ROWID_RANGE );
- if( pLevel->plan.wsFlags & (WHERE_ROWID_EQ|WHERE_ROWID_RANGE) ){
- memcpy(&sqlite3_query_plan[nQPlan], "* ", 2);
- nQPlan += 2;
- }else if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ){
- n = sqlite3Strlen30(pLevel->plan.u.pIdx->zName);
- if( n+nQPlan < sizeof(sqlite3_query_plan)-2 ){
- memcpy(&sqlite3_query_plan[nQPlan], pLevel->plan.u.pIdx->zName, n);
- nQPlan += n;
- sqlite3_query_plan[nQPlan++] = ' ';
- }
- }else{
- memcpy(&sqlite3_query_plan[nQPlan], "{} ", 3);
- nQPlan += 3;
+#endif
+ addrExplain = sqlite3WhereExplainOneScan(
+ pParse, pTabList, pLevel, ii, pLevel->iFrom, wctrlFlags
+ );
+ pLevel->addrBody = sqlite3VdbeCurrentAddr(v);
+ notReady = sqlite3WhereCodeOneLoopStart(pWInfo, ii, notReady);
+ pWInfo->iContinue = pLevel->addrCont;
+ if( (wsFlags&WHERE_MULTI_OR)==0 && (wctrlFlags&WHERE_OR_SUBCLAUSE)==0 ){
+ sqlite3WhereAddScanStatus(v, pTabList, pLevel, addrExplain);
}
}
- while( nQPlan>0 && sqlite3_query_plan[nQPlan-1]==' ' ){
- sqlite3_query_plan[--nQPlan] = 0;
- }
- sqlite3_query_plan[nQPlan] = 0;
- nQPlan = 0;
-#endif /* SQLITE_TEST // Testing and debugging use only */
- /* Record the continuation address in the WhereInfo structure. Then
- ** clean up and return.
- */
+ /* Done. */
+ VdbeModuleComment((v, "Begin WHERE-core"));
return pWInfo;
/* Jump here if malloc fails */
@@ -105417,49 +132614,77 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
Vdbe *v = pParse->pVdbe;
int i;
WhereLevel *pLevel;
+ WhereLoop *pLoop;
SrcList *pTabList = pWInfo->pTabList;
sqlite3 *db = pParse->db;
/* Generate loop termination code.
*/
+ VdbeModuleComment((v, "End WHERE-core"));
sqlite3ExprCacheClear(pParse);
for(i=pWInfo->nLevel-1; i>=0; i--){
+ int addr;
pLevel = &pWInfo->a[i];
+ pLoop = pLevel->pWLoop;
sqlite3VdbeResolveLabel(v, pLevel->addrCont);
if( pLevel->op!=OP_Noop ){
- sqlite3VdbeAddOp2(v, pLevel->op, pLevel->p1, pLevel->p2);
+ sqlite3VdbeAddOp3(v, pLevel->op, pLevel->p1, pLevel->p2, pLevel->p3);
sqlite3VdbeChangeP5(v, pLevel->p5);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, pLevel->op==OP_Next);
+ VdbeCoverageIf(v, pLevel->op==OP_Prev);
+ VdbeCoverageIf(v, pLevel->op==OP_VNext);
}
- if( pLevel->plan.wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
+ if( pLoop->wsFlags & WHERE_IN_ABLE && pLevel->u.in.nIn>0 ){
struct InLoop *pIn;
int j;
sqlite3VdbeResolveLabel(v, pLevel->addrNxt);
for(j=pLevel->u.in.nIn, pIn=&pLevel->u.in.aInLoop[j-1]; j>0; j--, pIn--){
sqlite3VdbeJumpHere(v, pIn->addrInTop+1);
- sqlite3VdbeAddOp2(v, OP_Next, pIn->iCur, pIn->addrInTop);
+ if( pIn->eEndLoopOp!=OP_Noop ){
+ sqlite3VdbeAddOp2(v, pIn->eEndLoopOp, pIn->iCur, pIn->addrInTop);
+ VdbeCoverage(v);
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_PrevIfOpen);
+ VdbeCoverageIf(v, pIn->eEndLoopOp==OP_NextIfOpen);
+ }
sqlite3VdbeJumpHere(v, pIn->addrInTop-1);
}
- sqlite3DbFree(db, pLevel->u.in.aInLoop);
}
sqlite3VdbeResolveLabel(v, pLevel->addrBrk);
+ if( pLevel->addrSkip ){
+ sqlite3VdbeGoto(v, pLevel->addrSkip);
+ VdbeComment((v, "next skip-scan on %s", pLoop->u.btree.pIndex->zName));
+ sqlite3VdbeJumpHere(v, pLevel->addrSkip);
+ sqlite3VdbeJumpHere(v, pLevel->addrSkip-2);
+ }
+#ifndef SQLITE_LIKE_DOESNT_MATCH_BLOBS
+ if( pLevel->addrLikeRep ){
+ sqlite3VdbeAddOp2(v, OP_DecrJumpZero, (int)(pLevel->iLikeRepCntr>>1),
+ pLevel->addrLikeRep);
+ VdbeCoverage(v);
+ }
+#endif
if( pLevel->iLeftJoin ){
- int addr;
- addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin);
- assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- || (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 );
- if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 ){
+ int ws = pLoop->wsFlags;
+ addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); VdbeCoverage(v);
+ assert( (ws & WHERE_IDX_ONLY)==0 || (ws & WHERE_INDEXED)!=0 );
+ if( (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor);
}
- if( pLevel->iIdxCur>=0 ){
+ if( (ws & WHERE_INDEXED)
+ || ((ws & WHERE_MULTI_OR) && pLevel->u.pCovidx)
+ ){
sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur);
}
if( pLevel->op==OP_Return ){
sqlite3VdbeAddOp2(v, OP_Gosub, pLevel->p1, pLevel->addrFirst);
}else{
- sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrFirst);
+ sqlite3VdbeGoto(v, pLevel->addrFirst);
}
sqlite3VdbeJumpHere(v, addr);
}
+ VdbeModuleComment((v, "End WHERE-loop%d: %s", i,
+ pWInfo->pTabList->a[pLevel->iFrom].pTab->zName));
}
/* The "break" point is here, just past the end of the outer loop.
@@ -105467,32 +132692,51 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
*/
sqlite3VdbeResolveLabel(v, pWInfo->iBreak);
- /* Close all of the cursors that were opened by sqlite3WhereBegin.
- */
- assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc );
+ assert( pWInfo->nLevel<=pTabList->nSrc );
for(i=0, pLevel=pWInfo->a; i<pWInfo->nLevel; i++, pLevel++){
+ int k, last;
+ VdbeOp *pOp;
+ Index *pIdx = 0;
struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom];
Table *pTab = pTabItem->pTab;
assert( pTab!=0 );
+ pLoop = pLevel->pWLoop;
+
+ /* For a co-routine, change all OP_Column references to the table of
+ ** the co-routine into OP_Copy of result contained in a register.
+ ** OP_Rowid becomes OP_Null.
+ */
+ if( pTabItem->fg.viaCoroutine && !db->mallocFailed ){
+ translateColumnToCopy(v, pLevel->addrBody, pLevel->iTabCur,
+ pTabItem->regResult, 0);
+ continue;
+ }
+
+ /* Close all of the cursors that were opened by sqlite3WhereBegin.
+ ** Except, do not close cursors that will be reused by the OR optimization
+ ** (WHERE_OR_SUBCLAUSE). And do not close the OP_OpenWrite cursors
+ ** created for the ONEPASS optimization.
+ */
if( (pTab->tabFlags & TF_Ephemeral)==0
&& pTab->pSelect==0
- && (pWInfo->wctrlFlags & WHERE_OMIT_CLOSE)==0
+ && (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)==0
){
- int ws = pLevel->plan.wsFlags;
- if( !pWInfo->okOnePass && (ws & WHERE_IDX_ONLY)==0 ){
+ int ws = pLoop->wsFlags;
+ if( pWInfo->eOnePass==ONEPASS_OFF && (ws & WHERE_IDX_ONLY)==0 ){
sqlite3VdbeAddOp1(v, OP_Close, pTabItem->iCursor);
}
- if( (ws & WHERE_INDEXED)!=0 && (ws & WHERE_TEMP_INDEX)==0 ){
+ if( (ws & WHERE_INDEXED)!=0
+ && (ws & (WHERE_IPK|WHERE_AUTO_INDEX))==0
+ && pLevel->iIdxCur!=pWInfo->aiCurOnePass[1]
+ ){
sqlite3VdbeAddOp1(v, OP_Close, pLevel->iIdxCur);
}
}
- /* If this scan uses an index, make code substitutions to read data
- ** from the index in preference to the table. Sometimes, this means
- ** the table need never be read from. This is a performance boost,
- ** as the vdbe level waits until the table is read before actually
- ** seeking the table cursor to the record corresponding to the current
- ** position in the index.
+ /* If this scan uses an index, make VDBE code substitutions to read data
+ ** from the index instead of from the table where possible. In some cases
+ ** this optimization prevents the table from ever being read, which can
+ ** yield a significant performance boost.
**
** Calls to the code generator in between sqlite3WhereBegin and
** sqlite3WhereEnd will have created code that references the table
@@ -105500,26 +132744,34 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
** that reference the table and converts them into opcodes that
** reference the index.
*/
- if( (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 && !db->mallocFailed){
- int k, j, last;
- VdbeOp *pOp;
- Index *pIdx = pLevel->plan.u.pIdx;
-
- assert( pIdx!=0 );
- pOp = sqlite3VdbeGetOp(v, pWInfo->iTop);
+ if( pLoop->wsFlags & (WHERE_INDEXED|WHERE_IDX_ONLY) ){
+ pIdx = pLoop->u.btree.pIndex;
+ }else if( pLoop->wsFlags & WHERE_MULTI_OR ){
+ pIdx = pLevel->u.pCovidx;
+ }
+ if( pIdx
+ && (pWInfo->eOnePass==ONEPASS_OFF || !HasRowid(pIdx->pTable))
+ && !db->mallocFailed
+ ){
last = sqlite3VdbeCurrentAddr(v);
- for(k=pWInfo->iTop; k<last; k++, pOp++){
+ k = pLevel->addrBody;
+ pOp = sqlite3VdbeGetOp(v, k);
+ for(; k<last; k++, pOp++){
if( pOp->p1!=pLevel->iTabCur ) continue;
if( pOp->opcode==OP_Column ){
- for(j=0; j<pIdx->nColumn; j++){
- if( pOp->p2==pIdx->aiColumn[j] ){
- pOp->p2 = j;
- pOp->p1 = pLevel->iIdxCur;
- break;
- }
+ int x = pOp->p2;
+ assert( pIdx->pTable==pTab );
+ if( !HasRowid(pTab) ){
+ Index *pPk = sqlite3PrimaryKeyIndex(pTab);
+ x = pPk->aiColumn[x];
+ assert( x>=0 );
+ }
+ x = sqlite3ColumnOfIndex(pIdx, x);
+ if( x>=0 ){
+ pOp->p2 = x;
+ pOp->p1 = pLevel->iIdxCur;
}
- assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0
- || j<pIdx->nColumn );
+ assert( (pLoop->wsFlags & WHERE_IDX_ONLY)==0 || x>=0 );
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
@@ -105537,19 +132789,34 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
/************** End of where.c ***********************************************/
/************** Begin file parse.c *******************************************/
-/* Driver template for the LEMON parser generator.
-** The author disclaims copyright to this source code.
+/*
+** 2000-05-29
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Driver template for the LEMON parser generator.
**
-** This version of "lempar.c" is modified, slightly, for use by SQLite.
-** The only modifications are the addition of a couple of NEVER()
-** macros to disable tests that are needed in the case of a general
-** LALR(1) grammar but which are always false in the
-** specific grammar used by SQLite.
+** The "lemon" program processes an LALR(1) input grammar file, then uses
+** this template to construct a parser. The "lemon" program inserts text
+** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
+** interstitial "-" characters) contained in this template is changed into
+** the value of the %name directive from the grammar. Otherwise, the content
+** of this template is copied straight through into the generate parser
+** source file.
+**
+** The following is the concatenation of all %include directives from the
+** input grammar file:
*/
-/* First off, code is included that follows the "include" declaration
-** in the input grammar file. */
/* #include <stdio.h> */
+/************ Begin %include sections from the grammar ************************/
+/* #include "sqliteInt.h" */
/*
** Disable all error recovery processing in the parser push-down
@@ -105563,6 +132830,18 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
#define yytestcase(X) testcase(X)
/*
+** Indicate that sqlite3ParserFree() will never be called with a null
+** pointer.
+*/
+#define YYPARSEFREENEVERNULL 1
+
+/*
+** Alternative datatype for the argument to the malloc() routine passed
+** into sqlite3ParserAlloc(). The default is size_t.
+*/
+#define YYMALLOCARGTYPE u64
+
+/*
** An instance of this structure holds information about the
** LIMIT clause of a SELECT statement.
*/
@@ -105572,15 +132851,6 @@ struct LimitVal {
};
/*
-** An instance of this structure is used to store the LIKE,
-** GLOB, NOT LIKE, and NOT GLOB operators.
-*/
-struct LikeOp {
- Token eOperator; /* "like" or "glob" or "regexp" */
- int not; /* True if the NOT keyword is present */
-};
-
-/*
** An instance of the following structure describes the event of a
** TRIGGER. "a" is the event type, one of TK_UPDATE, TK_INSERT,
** TK_DELETE, or TK_INSTEAD. If the event is of the form
@@ -105592,10 +132862,36 @@ struct LikeOp {
struct TrigEvent { int a; IdList * b; };
/*
-** An instance of this structure holds the ATTACH key and the key type.
+** Disable lookaside memory allocation for objects that might be
+** shared across database connections.
*/
-struct AttachKey { int type; Token key; };
+static void disableLookaside(Parse *pParse){
+ pParse->disableLookaside++;
+ pParse->db->lookaside.bDisable++;
+}
+
+ /*
+ ** For a compound SELECT statement, make sure p->pPrior->pNext==p for
+ ** all elements in the list. And make sure list length does not exceed
+ ** SQLITE_LIMIT_COMPOUND_SELECT.
+ */
+ static void parserDoubleLinkSelect(Parse *pParse, Select *p){
+ if( p->pPrior ){
+ Select *pNext = 0, *pLoop;
+ int mxSelect, cnt = 0;
+ for(pLoop=p; pLoop; pNext=pLoop, pLoop=pLoop->pPrior, cnt++){
+ pLoop->pNext = pNext;
+ pLoop->selFlags |= SF_Compound;
+ }
+ if( (p->selFlags & SF_MultiValue)==0 &&
+ (mxSelect = pParse->db->aLimit[SQLITE_LIMIT_COMPOUND_SELECT])>0 &&
+ cnt>mxSelect
+ ){
+ sqlite3ErrorMsg(pParse, "too many terms in compound SELECT");
+ }
+ }
+ }
/* This is a utility routine used to set the ExprSpan.zStart and
** ExprSpan.zEnd values of pOut so that the span covers the complete
@@ -105610,46 +132906,68 @@ struct AttachKey { int type; Token key; };
** new Expr to populate pOut. Set the span of pOut to be the identifier
** that created the expression.
*/
- static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token *pValue){
- pOut->pExpr = sqlite3PExpr(pParse, op, 0, 0, pValue);
- pOut->zStart = pValue->z;
- pOut->zEnd = &pValue->z[pValue->n];
+ static void spanExpr(ExprSpan *pOut, Parse *pParse, int op, Token t){
+ Expr *p = sqlite3DbMallocRawNN(pParse->db, sizeof(Expr)+t.n+1);
+ if( p ){
+ memset(p, 0, sizeof(Expr));
+ p->op = (u8)op;
+ p->flags = EP_Leaf;
+ p->iAgg = -1;
+ p->u.zToken = (char*)&p[1];
+ memcpy(p->u.zToken, t.z, t.n);
+ p->u.zToken[t.n] = 0;
+ if( sqlite3Isquote(p->u.zToken[0]) ){
+ if( p->u.zToken[0]=='"' ) p->flags |= EP_DblQuoted;
+ sqlite3Dequote(p->u.zToken);
+ }
+#if SQLITE_MAX_EXPR_DEPTH>0
+ p->nHeight = 1;
+#endif
+ }
+ pOut->pExpr = p;
+ pOut->zStart = t.z;
+ pOut->zEnd = &t.z[t.n];
}
/* This routine constructs a binary expression node out of two ExprSpan
** objects and uses the result to populate a new ExprSpan object.
*/
static void spanBinaryExpr(
- ExprSpan *pOut, /* Write the result here */
Parse *pParse, /* The parsing context. Errors accumulate here */
int op, /* The binary operation */
- ExprSpan *pLeft, /* The left operand */
+ ExprSpan *pLeft, /* The left operand, and output */
ExprSpan *pRight /* The right operand */
){
- pOut->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0);
- pOut->zStart = pLeft->zStart;
- pOut->zEnd = pRight->zEnd;
+ pLeft->pExpr = sqlite3PExpr(pParse, op, pLeft->pExpr, pRight->pExpr, 0);
+ pLeft->zEnd = pRight->zEnd;
+ }
+
+ /* If doNot is true, then add a TK_NOT Expr-node wrapper around the
+ ** outside of *ppExpr.
+ */
+ static void exprNot(Parse *pParse, int doNot, ExprSpan *pSpan){
+ if( doNot ){
+ pSpan->pExpr = sqlite3PExpr(pParse, TK_NOT, pSpan->pExpr, 0, 0);
+ }
}
/* Construct an expression node for a unary postfix operator
*/
static void spanUnaryPostfix(
- ExprSpan *pOut, /* Write the new expression node here */
Parse *pParse, /* Parsing context to record errors */
int op, /* The operator */
- ExprSpan *pOperand, /* The operand */
+ ExprSpan *pOperand, /* The operand, and output */
Token *pPostOp /* The operand token for setting the span */
){
- pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
- pOut->zStart = pOperand->zStart;
- pOut->zEnd = &pPostOp->z[pPostOp->n];
+ pOperand->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
+ pOperand->zEnd = &pPostOp->z[pPostOp->n];
}
/* A routine to convert a binary TK_IS or TK_ISNOT expression into a
** unary TK_ISNULL or TK_NOTNULL expression. */
static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){
sqlite3 *db = pParse->db;
- if( db->mallocFailed==0 && pY->op==TK_NULL ){
+ if( pA && pY && pY->op==TK_NULL ){
pA->op = (u8)op;
sqlite3ExprDelete(db, pA->pRight);
pA->pRight = 0;
@@ -105665,80 +132983,111 @@ struct AttachKey { int type; Token key; };
ExprSpan *pOperand, /* The operand */
Token *pPreOp /* The operand token for setting the span */
){
- pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
pOut->zStart = pPreOp->z;
+ pOut->pExpr = sqlite3PExpr(pParse, op, pOperand->pExpr, 0, 0);
pOut->zEnd = pOperand->zEnd;
}
-/* Next is all token values, in a form suitable for use by makeheaders.
-** This section will be null unless lemon is run with the -m switch.
-*/
-/*
-** These constants (all generated automatically by the parser generator)
-** specify the various kinds of tokens (terminals) that the parser
-** understands.
-**
-** Each symbol here is a terminal symbol in the grammar.
-*/
-/* Make sure the INTERFACE macro is defined.
-*/
-#ifndef INTERFACE
-# define INTERFACE 1
-#endif
-/* The next thing included is series of defines which control
+
+ /* Add a single new term to an ExprList that is used to store a
+ ** list of identifiers. Report an error if the ID list contains
+ ** a COLLATE clause or an ASC or DESC keyword, except ignore the
+ ** error while parsing a legacy schema.
+ */
+ static ExprList *parserAddExprIdListTerm(
+ Parse *pParse,
+ ExprList *pPrior,
+ Token *pIdToken,
+ int hasCollate,
+ int sortOrder
+ ){
+ ExprList *p = sqlite3ExprListAppend(pParse, pPrior, 0);
+ if( (hasCollate || sortOrder!=SQLITE_SO_UNDEFINED)
+ && pParse->db->init.busy==0
+ ){
+ sqlite3ErrorMsg(pParse, "syntax error after column name \"%.*s\"",
+ pIdToken->n, pIdToken->z);
+ }
+ sqlite3ExprListSetName(pParse, p, pIdToken, 1);
+ return p;
+ }
+/**************** End of %include directives **********************************/
+/* These constants specify the various numeric values for terminal symbols
+** in a format understandable to "makeheaders". This section is blank unless
+** "lemon" is run with the "-m" command-line option.
+***************** Begin makeheaders token definitions *************************/
+/**************** End makeheaders token definitions ***************************/
+
+/* The next sections is a series of control #defines.
** various aspects of the generated parser.
-** YYCODETYPE is the data type used for storing terminal
-** and nonterminal numbers. "unsigned char" is
-** used if there are fewer than 250 terminals
-** and nonterminals. "int" is used otherwise.
-** YYNOCODE is a number of type YYCODETYPE which corresponds
-** to no legal terminal or nonterminal number. This
-** number is used to fill in empty slots of the hash
-** table.
+** YYCODETYPE is the data type used to store the integer codes
+** that represent terminal and non-terminal symbols.
+** "unsigned char" is used if there are fewer than
+** 256 symbols. Larger types otherwise.
+** YYNOCODE is a number of type YYCODETYPE that is not used for
+** any terminal or nonterminal symbol.
** YYFALLBACK If defined, this indicates that one or more tokens
-** have fall-back values which should be used if the
-** original value of the token will not parse.
-** YYACTIONTYPE is the data type used for storing terminal
-** and nonterminal numbers. "unsigned char" is
-** used if there are fewer than 250 rules and
-** states combined. "int" is used otherwise.
-** sqlite3ParserTOKENTYPE is the data type used for minor tokens given
-** directly to the parser from the tokenizer.
-** YYMINORTYPE is the data type used for all minor tokens.
+** (also known as: "terminal symbols") have fall-back
+** values which should be used if the original symbol
+** would not parse. This permits keywords to sometimes
+** be used as identifiers, for example.
+** YYACTIONTYPE is the data type used for "action codes" - numbers
+** that indicate what to do in response to the next
+** token.
+** sqlite3ParserTOKENTYPE is the data type used for minor type for terminal
+** symbols. Background: A "minor type" is a semantic
+** value associated with a terminal or non-terminal
+** symbols. For example, for an "ID" terminal symbol,
+** the minor type might be the name of the identifier.
+** Each non-terminal can have a different minor type.
+** Terminal symbols all have the same minor type, though.
+** This macros defines the minor type for terminal
+** symbols.
+** YYMINORTYPE is the data type used for all minor types.
** This is typically a union of many types, one of
** which is sqlite3ParserTOKENTYPE. The entry in the union
-** for base tokens is called "yy0".
+** for terminal symbols is called "yy0".
** YYSTACKDEPTH is the maximum depth of the parser's stack. If
** zero the stack is dynamically sized using realloc()
** sqlite3ParserARG_SDECL A static variable declaration for the %extra_argument
** sqlite3ParserARG_PDECL A parameter declaration for the %extra_argument
** sqlite3ParserARG_STORE Code to store %extra_argument into yypParser
** sqlite3ParserARG_FETCH Code to extract %extra_argument from yypParser
-** YYNSTATE the combined number of states.
-** YYNRULE the number of rules in the grammar
** YYERRORSYMBOL is the code number of the error symbol. If not
** defined, then do no error processing.
+** YYNSTATE the combined number of states.
+** YYNRULE the number of rules in the grammar
+** YY_MAX_SHIFT Maximum value for shift actions
+** YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
+** YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
+** YY_MIN_REDUCE Maximum value for reduce actions
+** YY_ERROR_ACTION The yy_action[] code for syntax error
+** YY_ACCEPT_ACTION The yy_action[] code for accept
+** YY_NO_ACTION The yy_action[] code for no-op
*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned char
-#define YYNOCODE 253
+#define YYNOCODE 252
#define YYACTIONTYPE unsigned short int
-#define YYWILDCARD 67
+#define YYWILDCARD 96
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- int yy4;
- struct TrigEvent yy90;
- ExprSpan yy118;
- TriggerStep* yy203;
- u8 yy210;
- struct {int value; int mask;} yy215;
- SrcList* yy259;
- struct LimitVal yy292;
- Expr* yy314;
- ExprList* yy322;
- struct LikeOp yy342;
- IdList* yy384;
- Select* yy387;
+ Expr* yy72;
+ TriggerStep* yy145;
+ ExprList* yy148;
+ SrcList* yy185;
+ ExprSpan yy190;
+ int yy194;
+ Select* yy243;
+ IdList* yy254;
+ With* yy285;
+ struct TrigEvent yy332;
+ struct LimitVal yy354;
+ struct {int value; int mask;} yy497;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -105747,16 +133096,18 @@ typedef union {
#define sqlite3ParserARG_PDECL ,Parse *pParse
#define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse
#define sqlite3ParserARG_STORE yypParser->pParse = pParse
-#define YYNSTATE 630
-#define YYNRULE 329
#define YYFALLBACK 1
-#define YY_NO_ACTION (YYNSTATE+YYNRULE+2)
-#define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1)
-#define YY_ERROR_ACTION (YYNSTATE+YYNRULE)
-
-/* The yyzerominor constant is used to initialize instances of
-** YYMINORTYPE objects to zero. */
-static const YYMINORTYPE yyzerominor = { 0 };
+#define YYNSTATE 456
+#define YYNRULE 332
+#define YY_MAX_SHIFT 455
+#define YY_MIN_SHIFTREDUCE 668
+#define YY_MAX_SHIFTREDUCE 999
+#define YY_MIN_REDUCE 1000
+#define YY_MAX_REDUCE 1331
+#define YY_ERROR_ACTION 1332
+#define YY_ACCEPT_ACTION 1333
+#define YY_NO_ACTION 1334
+/************* End control #defines *******************************************/
/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
@@ -105779,29 +133130,37 @@ static const YYMINORTYPE yyzerominor = { 0 };
** Suppose the action integer is N. Then the action is determined as
** follows
**
-** 0 <= N < YYNSTATE Shift N. That is, push the lookahead
+** 0 <= N <= YY_MAX_SHIFT Shift N. That is, push the lookahead
** token onto the stack and goto state N.
**
-** YYNSTATE <= N < YYNSTATE+YYNRULE Reduce by rule N-YYNSTATE.
+** N between YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
+** and YY_MAX_SHIFTREDUCE reduce by rule N-YY_MIN_SHIFTREDUCE.
**
-** N == YYNSTATE+YYNRULE A syntax error has occurred.
+** N between YY_MIN_REDUCE Reduce by rule N-YY_MIN_REDUCE
+** and YY_MAX_REDUCE
**
-** N == YYNSTATE+YYNRULE+1 The parser accepts its input.
+** N == YY_ERROR_ACTION A syntax error has occurred.
**
-** N == YYNSTATE+YYNRULE+2 No such action. Denotes unused
+** N == YY_ACCEPT_ACTION The parser accepts its input.
+**
+** N == YY_NO_ACTION No such action. Denotes unused
** slots in the yy_action[] table.
**
** The action table is constructed as a single large table named yy_action[].
-** Given state S and lookahead X, the action is computed as
+** Given state S and lookahead X, the action is computed as either:
**
-** yy_action[ yy_shift_ofst[S] + X ]
+** (A) N = yy_action[ yy_shift_ofst[S] + X ]
+** (B) N = yy_default[S]
**
-** If the index value yy_shift_ofst[S]+X is out of range or if the value
-** yy_lookahead[yy_shift_ofst[S]+X] is not equal to X or if yy_shift_ofst[S]
-** is equal to YY_SHIFT_USE_DFLT, it means that the action is not in the table
-** and that yy_default[S] should be used instead.
+** The (A) formula is preferred. The B formula is used instead if:
+** (1) The yy_shift_ofst[S]+X value is out of range, or
+** (2) yy_lookahead[yy_shift_ofst[S]+X] is not equal to X, or
+** (3) yy_shift_ofst[S] equal YY_SHIFT_USE_DFLT.
+** (Implementation note: YY_SHIFT_USE_DFLT is chosen so that
+** YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
+** Hence only tests (1) and (2) need to be evaluated.)
**
-** The formula above is for computing the action when the lookahead is
+** The formulas above are for computing the action when the lookahead is
** a terminal symbol. If the lookahead is a non-terminal (as occurs after
** a reduce action) then the yy_reduce_ofst[] array is used in place of
** the yy_shift_ofst[] array and YY_REDUCE_USE_DFLT is used in place of
@@ -105817,478 +133176,470 @@ static const YYMINORTYPE yyzerominor = { 0 };
** yy_reduce_ofst[] For each state, the offset into yy_action for
** shifting non-terminals after a reduce.
** yy_default[] Default action for each state.
-*/
-#define YY_ACTTAB_COUNT (1557)
+**
+*********** Begin parsing tables **********************************************/
+#define YY_ACTTAB_COUNT (1567)
static const YYACTIONTYPE yy_action[] = {
- /* 0 */ 313, 960, 186, 419, 2, 172, 627, 597, 55, 55,
- /* 10 */ 55, 55, 48, 53, 53, 53, 53, 52, 52, 51,
- /* 20 */ 51, 51, 50, 238, 302, 283, 623, 622, 516, 515,
- /* 30 */ 590, 584, 55, 55, 55, 55, 282, 53, 53, 53,
- /* 40 */ 53, 52, 52, 51, 51, 51, 50, 238, 6, 56,
- /* 50 */ 57, 47, 582, 581, 583, 583, 54, 54, 55, 55,
- /* 60 */ 55, 55, 608, 53, 53, 53, 53, 52, 52, 51,
- /* 70 */ 51, 51, 50, 238, 313, 597, 409, 330, 579, 579,
- /* 80 */ 32, 53, 53, 53, 53, 52, 52, 51, 51, 51,
- /* 90 */ 50, 238, 330, 217, 620, 619, 166, 411, 624, 382,
- /* 100 */ 379, 378, 7, 491, 590, 584, 200, 199, 198, 58,
- /* 110 */ 377, 300, 414, 621, 481, 66, 623, 622, 621, 580,
- /* 120 */ 254, 601, 94, 56, 57, 47, 582, 581, 583, 583,
- /* 130 */ 54, 54, 55, 55, 55, 55, 671, 53, 53, 53,
- /* 140 */ 53, 52, 52, 51, 51, 51, 50, 238, 313, 532,
- /* 150 */ 226, 506, 507, 133, 177, 139, 284, 385, 279, 384,
- /* 160 */ 169, 197, 342, 398, 251, 226, 253, 275, 388, 167,
- /* 170 */ 139, 284, 385, 279, 384, 169, 570, 236, 590, 584,
- /* 180 */ 672, 240, 275, 157, 620, 619, 554, 437, 51, 51,
- /* 190 */ 51, 50, 238, 343, 439, 553, 438, 56, 57, 47,
- /* 200 */ 582, 581, 583, 583, 54, 54, 55, 55, 55, 55,
- /* 210 */ 465, 53, 53, 53, 53, 52, 52, 51, 51, 51,
- /* 220 */ 50, 238, 313, 390, 52, 52, 51, 51, 51, 50,
- /* 230 */ 238, 391, 166, 491, 566, 382, 379, 378, 409, 440,
- /* 240 */ 579, 579, 252, 440, 607, 66, 377, 513, 621, 49,
- /* 250 */ 46, 147, 590, 584, 621, 16, 466, 189, 621, 441,
- /* 260 */ 442, 673, 526, 441, 340, 577, 595, 64, 194, 482,
- /* 270 */ 434, 56, 57, 47, 582, 581, 583, 583, 54, 54,
- /* 280 */ 55, 55, 55, 55, 30, 53, 53, 53, 53, 52,
- /* 290 */ 52, 51, 51, 51, 50, 238, 313, 593, 593, 593,
- /* 300 */ 387, 578, 606, 493, 259, 351, 258, 411, 1, 623,
- /* 310 */ 622, 496, 623, 622, 65, 240, 623, 622, 597, 443,
- /* 320 */ 237, 239, 414, 341, 237, 602, 590, 584, 18, 603,
- /* 330 */ 166, 601, 87, 382, 379, 378, 67, 623, 622, 38,
- /* 340 */ 623, 622, 176, 270, 377, 56, 57, 47, 582, 581,
- /* 350 */ 583, 583, 54, 54, 55, 55, 55, 55, 175, 53,
- /* 360 */ 53, 53, 53, 52, 52, 51, 51, 51, 50, 238,
- /* 370 */ 313, 396, 233, 411, 531, 565, 317, 620, 619, 44,
- /* 380 */ 620, 619, 240, 206, 620, 619, 597, 266, 414, 268,
- /* 390 */ 409, 597, 579, 579, 352, 184, 505, 601, 73, 533,
- /* 400 */ 590, 584, 466, 548, 190, 620, 619, 576, 620, 619,
- /* 410 */ 547, 383, 551, 35, 332, 575, 574, 600, 504, 56,
- /* 420 */ 57, 47, 582, 581, 583, 583, 54, 54, 55, 55,
- /* 430 */ 55, 55, 567, 53, 53, 53, 53, 52, 52, 51,
- /* 440 */ 51, 51, 50, 238, 313, 411, 561, 561, 528, 364,
- /* 450 */ 259, 351, 258, 183, 361, 549, 524, 374, 411, 597,
- /* 460 */ 414, 240, 560, 560, 409, 604, 579, 579, 328, 601,
- /* 470 */ 93, 623, 622, 414, 590, 584, 237, 564, 559, 559,
- /* 480 */ 520, 402, 601, 87, 409, 210, 579, 579, 168, 421,
- /* 490 */ 950, 519, 950, 56, 57, 47, 582, 581, 583, 583,
- /* 500 */ 54, 54, 55, 55, 55, 55, 192, 53, 53, 53,
- /* 510 */ 53, 52, 52, 51, 51, 51, 50, 238, 313, 600,
- /* 520 */ 293, 563, 511, 234, 357, 146, 475, 475, 367, 411,
- /* 530 */ 562, 411, 358, 542, 425, 171, 411, 215, 144, 620,
- /* 540 */ 619, 544, 318, 353, 414, 203, 414, 275, 590, 584,
- /* 550 */ 549, 414, 174, 601, 94, 601, 79, 558, 471, 61,
- /* 560 */ 601, 79, 421, 949, 350, 949, 34, 56, 57, 47,
- /* 570 */ 582, 581, 583, 583, 54, 54, 55, 55, 55, 55,
- /* 580 */ 535, 53, 53, 53, 53, 52, 52, 51, 51, 51,
- /* 590 */ 50, 238, 313, 307, 424, 394, 272, 49, 46, 147,
- /* 600 */ 349, 322, 4, 411, 491, 312, 321, 425, 568, 492,
- /* 610 */ 216, 264, 407, 575, 574, 429, 66, 549, 414, 621,
- /* 620 */ 540, 602, 590, 584, 13, 603, 621, 601, 72, 12,
- /* 630 */ 618, 617, 616, 202, 210, 621, 546, 469, 422, 319,
- /* 640 */ 148, 56, 57, 47, 582, 581, 583, 583, 54, 54,
- /* 650 */ 55, 55, 55, 55, 338, 53, 53, 53, 53, 52,
- /* 660 */ 52, 51, 51, 51, 50, 238, 313, 600, 600, 411,
- /* 670 */ 39, 21, 37, 170, 237, 875, 411, 572, 572, 201,
- /* 680 */ 144, 473, 538, 331, 414, 474, 143, 146, 630, 628,
- /* 690 */ 334, 414, 353, 601, 68, 168, 590, 584, 132, 365,
- /* 700 */ 601, 96, 307, 423, 530, 336, 49, 46, 147, 568,
- /* 710 */ 406, 216, 549, 360, 529, 56, 57, 47, 582, 581,
- /* 720 */ 583, 583, 54, 54, 55, 55, 55, 55, 411, 53,
- /* 730 */ 53, 53, 53, 52, 52, 51, 51, 51, 50, 238,
- /* 740 */ 313, 411, 605, 414, 484, 510, 172, 422, 597, 318,
- /* 750 */ 496, 485, 601, 99, 411, 142, 414, 411, 231, 411,
- /* 760 */ 540, 411, 359, 629, 2, 601, 97, 426, 308, 414,
- /* 770 */ 590, 584, 414, 20, 414, 621, 414, 621, 601, 106,
- /* 780 */ 503, 601, 105, 601, 108, 601, 109, 204, 28, 56,
- /* 790 */ 57, 47, 582, 581, 583, 583, 54, 54, 55, 55,
- /* 800 */ 55, 55, 411, 53, 53, 53, 53, 52, 52, 51,
- /* 810 */ 51, 51, 50, 238, 313, 411, 597, 414, 411, 276,
- /* 820 */ 214, 600, 411, 366, 213, 381, 601, 134, 274, 500,
- /* 830 */ 414, 167, 130, 414, 621, 411, 354, 414, 376, 601,
- /* 840 */ 135, 129, 601, 100, 590, 584, 601, 104, 522, 521,
- /* 850 */ 414, 621, 224, 273, 600, 167, 327, 282, 600, 601,
- /* 860 */ 103, 468, 521, 56, 57, 47, 582, 581, 583, 583,
- /* 870 */ 54, 54, 55, 55, 55, 55, 411, 53, 53, 53,
- /* 880 */ 53, 52, 52, 51, 51, 51, 50, 238, 313, 411,
- /* 890 */ 27, 414, 411, 375, 276, 167, 359, 544, 50, 238,
- /* 900 */ 601, 95, 128, 223, 414, 411, 165, 414, 411, 621,
- /* 910 */ 411, 621, 612, 601, 102, 372, 601, 76, 590, 584,
- /* 920 */ 414, 570, 236, 414, 470, 414, 167, 621, 188, 601,
- /* 930 */ 98, 225, 601, 138, 601, 137, 232, 56, 45, 47,
- /* 940 */ 582, 581, 583, 583, 54, 54, 55, 55, 55, 55,
- /* 950 */ 411, 53, 53, 53, 53, 52, 52, 51, 51, 51,
- /* 960 */ 50, 238, 313, 276, 276, 414, 411, 276, 544, 459,
- /* 970 */ 359, 171, 209, 479, 601, 136, 628, 334, 621, 621,
- /* 980 */ 125, 414, 621, 368, 411, 621, 257, 540, 589, 588,
- /* 990 */ 601, 75, 590, 584, 458, 446, 23, 23, 124, 414,
- /* 1000 */ 326, 325, 621, 427, 324, 309, 600, 288, 601, 92,
- /* 1010 */ 586, 585, 57, 47, 582, 581, 583, 583, 54, 54,
- /* 1020 */ 55, 55, 55, 55, 411, 53, 53, 53, 53, 52,
- /* 1030 */ 52, 51, 51, 51, 50, 238, 313, 587, 411, 414,
- /* 1040 */ 411, 207, 611, 476, 171, 472, 160, 123, 601, 91,
- /* 1050 */ 323, 261, 15, 414, 464, 414, 411, 621, 411, 354,
- /* 1060 */ 222, 411, 601, 74, 601, 90, 590, 584, 159, 264,
- /* 1070 */ 158, 414, 461, 414, 621, 600, 414, 121, 120, 25,
- /* 1080 */ 601, 89, 601, 101, 621, 601, 88, 47, 582, 581,
- /* 1090 */ 583, 583, 54, 54, 55, 55, 55, 55, 544, 53,
- /* 1100 */ 53, 53, 53, 52, 52, 51, 51, 51, 50, 238,
- /* 1110 */ 43, 405, 263, 3, 610, 264, 140, 415, 622, 24,
- /* 1120 */ 410, 11, 456, 594, 118, 155, 219, 452, 408, 621,
- /* 1130 */ 621, 621, 156, 43, 405, 621, 3, 286, 621, 113,
- /* 1140 */ 415, 622, 111, 445, 411, 400, 557, 403, 545, 10,
- /* 1150 */ 411, 408, 264, 110, 205, 436, 541, 566, 453, 414,
- /* 1160 */ 621, 621, 63, 621, 435, 414, 411, 621, 601, 94,
- /* 1170 */ 403, 621, 411, 337, 601, 86, 150, 40, 41, 534,
- /* 1180 */ 566, 414, 242, 264, 42, 413, 412, 414, 600, 595,
- /* 1190 */ 601, 85, 191, 333, 107, 451, 601, 84, 621, 539,
- /* 1200 */ 40, 41, 420, 230, 411, 149, 316, 42, 413, 412,
- /* 1210 */ 398, 127, 595, 315, 621, 399, 278, 625, 181, 414,
- /* 1220 */ 593, 593, 593, 592, 591, 14, 450, 411, 601, 71,
- /* 1230 */ 240, 621, 43, 405, 264, 3, 615, 180, 264, 415,
- /* 1240 */ 622, 614, 414, 593, 593, 593, 592, 591, 14, 621,
- /* 1250 */ 408, 601, 70, 621, 417, 33, 405, 613, 3, 411,
- /* 1260 */ 264, 411, 415, 622, 418, 626, 178, 509, 8, 403,
- /* 1270 */ 241, 416, 126, 408, 414, 621, 414, 449, 208, 566,
- /* 1280 */ 240, 221, 621, 601, 83, 601, 82, 599, 297, 277,
- /* 1290 */ 296, 30, 403, 31, 395, 264, 295, 397, 489, 40,
- /* 1300 */ 41, 411, 566, 220, 621, 294, 42, 413, 412, 271,
- /* 1310 */ 621, 595, 600, 621, 59, 60, 414, 269, 267, 623,
- /* 1320 */ 622, 36, 40, 41, 621, 601, 81, 598, 235, 42,
- /* 1330 */ 413, 412, 621, 621, 595, 265, 344, 411, 248, 556,
- /* 1340 */ 173, 185, 593, 593, 593, 592, 591, 14, 218, 29,
- /* 1350 */ 621, 543, 414, 305, 304, 303, 179, 301, 411, 566,
- /* 1360 */ 454, 601, 80, 289, 335, 593, 593, 593, 592, 591,
- /* 1370 */ 14, 411, 287, 414, 151, 392, 246, 260, 411, 196,
- /* 1380 */ 195, 523, 601, 69, 411, 245, 414, 526, 537, 285,
- /* 1390 */ 389, 595, 621, 414, 536, 601, 17, 362, 153, 414,
- /* 1400 */ 466, 463, 601, 78, 154, 414, 462, 152, 601, 77,
- /* 1410 */ 355, 255, 621, 455, 601, 9, 621, 386, 444, 517,
- /* 1420 */ 247, 621, 593, 593, 593, 621, 621, 244, 621, 243,
- /* 1430 */ 430, 518, 292, 621, 329, 621, 145, 393, 280, 513,
- /* 1440 */ 291, 131, 621, 514, 621, 621, 311, 621, 259, 346,
- /* 1450 */ 249, 621, 621, 229, 314, 621, 228, 512, 227, 240,
- /* 1460 */ 494, 488, 310, 164, 487, 486, 373, 480, 163, 262,
- /* 1470 */ 369, 371, 162, 26, 212, 478, 477, 161, 141, 363,
- /* 1480 */ 467, 122, 339, 187, 119, 348, 347, 117, 116, 115,
- /* 1490 */ 114, 112, 182, 457, 320, 22, 433, 432, 448, 19,
- /* 1500 */ 609, 431, 428, 62, 193, 596, 573, 298, 555, 552,
- /* 1510 */ 571, 404, 290, 380, 498, 510, 495, 306, 281, 499,
- /* 1520 */ 250, 5, 497, 460, 345, 447, 569, 550, 238, 299,
- /* 1530 */ 527, 525, 508, 961, 502, 501, 961, 401, 961, 211,
- /* 1540 */ 490, 356, 256, 961, 483, 961, 961, 961, 961, 961,
- /* 1550 */ 961, 961, 961, 961, 961, 961, 370,
+ /* 0 */ 325, 832, 351, 825, 5, 203, 203, 819, 99, 100,
+ /* 10 */ 90, 842, 842, 854, 857, 846, 846, 97, 97, 98,
+ /* 20 */ 98, 98, 98, 301, 96, 96, 96, 96, 95, 95,
+ /* 30 */ 94, 94, 94, 93, 351, 325, 977, 977, 824, 824,
+ /* 40 */ 826, 947, 354, 99, 100, 90, 842, 842, 854, 857,
+ /* 50 */ 846, 846, 97, 97, 98, 98, 98, 98, 338, 96,
+ /* 60 */ 96, 96, 96, 95, 95, 94, 94, 94, 93, 351,
+ /* 70 */ 95, 95, 94, 94, 94, 93, 351, 791, 977, 977,
+ /* 80 */ 325, 94, 94, 94, 93, 351, 792, 75, 99, 100,
+ /* 90 */ 90, 842, 842, 854, 857, 846, 846, 97, 97, 98,
+ /* 100 */ 98, 98, 98, 450, 96, 96, 96, 96, 95, 95,
+ /* 110 */ 94, 94, 94, 93, 351, 1333, 155, 155, 2, 325,
+ /* 120 */ 275, 146, 132, 52, 52, 93, 351, 99, 100, 90,
+ /* 130 */ 842, 842, 854, 857, 846, 846, 97, 97, 98, 98,
+ /* 140 */ 98, 98, 101, 96, 96, 96, 96, 95, 95, 94,
+ /* 150 */ 94, 94, 93, 351, 958, 958, 325, 268, 428, 413,
+ /* 160 */ 411, 61, 752, 752, 99, 100, 90, 842, 842, 854,
+ /* 170 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 60,
+ /* 180 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 190 */ 351, 325, 270, 329, 273, 277, 959, 960, 250, 99,
+ /* 200 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 210 */ 98, 98, 98, 98, 301, 96, 96, 96, 96, 95,
+ /* 220 */ 95, 94, 94, 94, 93, 351, 325, 938, 1326, 698,
+ /* 230 */ 706, 1326, 242, 412, 99, 100, 90, 842, 842, 854,
+ /* 240 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 347,
+ /* 250 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 260 */ 351, 325, 938, 1327, 384, 699, 1327, 381, 379, 99,
+ /* 270 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 280 */ 98, 98, 98, 98, 701, 96, 96, 96, 96, 95,
+ /* 290 */ 95, 94, 94, 94, 93, 351, 325, 92, 89, 178,
+ /* 300 */ 833, 936, 373, 700, 99, 100, 90, 842, 842, 854,
+ /* 310 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 375,
+ /* 320 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 330 */ 351, 325, 1276, 947, 354, 818, 936, 739, 739, 99,
+ /* 340 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 350 */ 98, 98, 98, 98, 230, 96, 96, 96, 96, 95,
+ /* 360 */ 95, 94, 94, 94, 93, 351, 325, 969, 227, 92,
+ /* 370 */ 89, 178, 373, 300, 99, 100, 90, 842, 842, 854,
+ /* 380 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 921,
+ /* 390 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 400 */ 351, 325, 449, 447, 447, 447, 147, 737, 737, 99,
+ /* 410 */ 100, 90, 842, 842, 854, 857, 846, 846, 97, 97,
+ /* 420 */ 98, 98, 98, 98, 296, 96, 96, 96, 96, 95,
+ /* 430 */ 95, 94, 94, 94, 93, 351, 325, 419, 231, 958,
+ /* 440 */ 958, 158, 25, 422, 99, 100, 90, 842, 842, 854,
+ /* 450 */ 857, 846, 846, 97, 97, 98, 98, 98, 98, 450,
+ /* 460 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 470 */ 351, 443, 224, 224, 420, 958, 958, 962, 325, 52,
+ /* 480 */ 52, 959, 960, 176, 415, 78, 99, 100, 90, 842,
+ /* 490 */ 842, 854, 857, 846, 846, 97, 97, 98, 98, 98,
+ /* 500 */ 98, 379, 96, 96, 96, 96, 95, 95, 94, 94,
+ /* 510 */ 94, 93, 351, 325, 428, 418, 298, 959, 960, 962,
+ /* 520 */ 81, 99, 88, 90, 842, 842, 854, 857, 846, 846,
+ /* 530 */ 97, 97, 98, 98, 98, 98, 717, 96, 96, 96,
+ /* 540 */ 96, 95, 95, 94, 94, 94, 93, 351, 325, 843,
+ /* 550 */ 843, 855, 858, 996, 318, 343, 379, 100, 90, 842,
+ /* 560 */ 842, 854, 857, 846, 846, 97, 97, 98, 98, 98,
+ /* 570 */ 98, 450, 96, 96, 96, 96, 95, 95, 94, 94,
+ /* 580 */ 94, 93, 351, 325, 350, 350, 350, 260, 377, 340,
+ /* 590 */ 929, 52, 52, 90, 842, 842, 854, 857, 846, 846,
+ /* 600 */ 97, 97, 98, 98, 98, 98, 361, 96, 96, 96,
+ /* 610 */ 96, 95, 95, 94, 94, 94, 93, 351, 86, 445,
+ /* 620 */ 847, 3, 1203, 361, 360, 378, 344, 813, 958, 958,
+ /* 630 */ 1300, 86, 445, 729, 3, 212, 169, 287, 405, 282,
+ /* 640 */ 404, 199, 232, 450, 300, 760, 83, 84, 280, 245,
+ /* 650 */ 262, 365, 251, 85, 352, 352, 92, 89, 178, 83,
+ /* 660 */ 84, 242, 412, 52, 52, 448, 85, 352, 352, 246,
+ /* 670 */ 959, 960, 194, 455, 670, 402, 399, 398, 448, 243,
+ /* 680 */ 221, 114, 434, 776, 361, 450, 397, 268, 747, 224,
+ /* 690 */ 224, 132, 132, 198, 832, 434, 452, 451, 428, 427,
+ /* 700 */ 819, 415, 734, 713, 132, 52, 52, 832, 268, 452,
+ /* 710 */ 451, 734, 194, 819, 363, 402, 399, 398, 450, 1271,
+ /* 720 */ 1271, 23, 958, 958, 86, 445, 397, 3, 228, 429,
+ /* 730 */ 895, 824, 824, 826, 827, 19, 203, 720, 52, 52,
+ /* 740 */ 428, 408, 439, 249, 824, 824, 826, 827, 19, 229,
+ /* 750 */ 403, 153, 83, 84, 761, 177, 241, 450, 721, 85,
+ /* 760 */ 352, 352, 120, 157, 959, 960, 58, 977, 409, 355,
+ /* 770 */ 330, 448, 268, 428, 430, 320, 790, 32, 32, 86,
+ /* 780 */ 445, 776, 3, 341, 98, 98, 98, 98, 434, 96,
+ /* 790 */ 96, 96, 96, 95, 95, 94, 94, 94, 93, 351,
+ /* 800 */ 832, 120, 452, 451, 813, 887, 819, 83, 84, 977,
+ /* 810 */ 813, 132, 410, 920, 85, 352, 352, 132, 407, 789,
+ /* 820 */ 958, 958, 92, 89, 178, 917, 448, 262, 370, 261,
+ /* 830 */ 82, 914, 80, 262, 370, 261, 776, 824, 824, 826,
+ /* 840 */ 827, 19, 934, 434, 96, 96, 96, 96, 95, 95,
+ /* 850 */ 94, 94, 94, 93, 351, 832, 74, 452, 451, 958,
+ /* 860 */ 958, 819, 959, 960, 120, 92, 89, 178, 945, 2,
+ /* 870 */ 918, 965, 268, 1, 976, 76, 445, 762, 3, 708,
+ /* 880 */ 901, 901, 387, 958, 958, 757, 919, 371, 740, 778,
+ /* 890 */ 756, 257, 824, 824, 826, 827, 19, 417, 741, 450,
+ /* 900 */ 24, 959, 960, 83, 84, 369, 958, 958, 177, 226,
+ /* 910 */ 85, 352, 352, 885, 315, 314, 313, 215, 311, 10,
+ /* 920 */ 10, 683, 448, 349, 348, 959, 960, 909, 777, 157,
+ /* 930 */ 120, 958, 958, 337, 776, 416, 711, 310, 450, 434,
+ /* 940 */ 450, 321, 450, 791, 103, 200, 175, 450, 959, 960,
+ /* 950 */ 908, 832, 792, 452, 451, 9, 9, 819, 10, 10,
+ /* 960 */ 52, 52, 51, 51, 180, 716, 248, 10, 10, 171,
+ /* 970 */ 170, 167, 339, 959, 960, 247, 984, 702, 702, 450,
+ /* 980 */ 715, 233, 686, 982, 889, 983, 182, 914, 824, 824,
+ /* 990 */ 826, 827, 19, 183, 256, 423, 132, 181, 394, 10,
+ /* 1000 */ 10, 889, 891, 749, 958, 958, 917, 268, 985, 198,
+ /* 1010 */ 985, 349, 348, 425, 415, 299, 817, 832, 326, 825,
+ /* 1020 */ 120, 332, 133, 819, 268, 98, 98, 98, 98, 91,
+ /* 1030 */ 96, 96, 96, 96, 95, 95, 94, 94, 94, 93,
+ /* 1040 */ 351, 157, 810, 371, 382, 359, 959, 960, 358, 268,
+ /* 1050 */ 450, 918, 368, 324, 824, 824, 826, 450, 709, 450,
+ /* 1060 */ 264, 380, 889, 450, 877, 746, 253, 919, 255, 433,
+ /* 1070 */ 36, 36, 234, 450, 234, 120, 269, 37, 37, 12,
+ /* 1080 */ 12, 334, 272, 27, 27, 450, 330, 118, 450, 162,
+ /* 1090 */ 742, 280, 450, 38, 38, 450, 985, 356, 985, 450,
+ /* 1100 */ 709, 1210, 450, 132, 450, 39, 39, 450, 40, 40,
+ /* 1110 */ 450, 362, 41, 41, 450, 42, 42, 450, 254, 28,
+ /* 1120 */ 28, 450, 29, 29, 31, 31, 450, 43, 43, 450,
+ /* 1130 */ 44, 44, 450, 714, 45, 45, 450, 11, 11, 767,
+ /* 1140 */ 450, 46, 46, 450, 268, 450, 105, 105, 450, 47,
+ /* 1150 */ 47, 450, 48, 48, 450, 237, 33, 33, 450, 172,
+ /* 1160 */ 49, 49, 450, 50, 50, 34, 34, 274, 122, 122,
+ /* 1170 */ 450, 123, 123, 450, 124, 124, 450, 898, 56, 56,
+ /* 1180 */ 450, 897, 35, 35, 450, 267, 450, 817, 450, 817,
+ /* 1190 */ 106, 106, 450, 53, 53, 385, 107, 107, 450, 817,
+ /* 1200 */ 108, 108, 817, 450, 104, 104, 121, 121, 119, 119,
+ /* 1210 */ 450, 117, 112, 112, 450, 276, 450, 225, 111, 111,
+ /* 1220 */ 450, 730, 450, 109, 109, 450, 673, 674, 675, 912,
+ /* 1230 */ 110, 110, 317, 998, 55, 55, 57, 57, 692, 331,
+ /* 1240 */ 54, 54, 26, 26, 696, 30, 30, 317, 937, 197,
+ /* 1250 */ 196, 195, 335, 281, 336, 446, 331, 745, 689, 436,
+ /* 1260 */ 440, 444, 120, 72, 386, 223, 175, 345, 757, 933,
+ /* 1270 */ 20, 286, 319, 756, 815, 372, 374, 202, 202, 202,
+ /* 1280 */ 263, 395, 285, 74, 208, 21, 696, 719, 718, 884,
+ /* 1290 */ 120, 120, 120, 120, 120, 754, 278, 828, 77, 74,
+ /* 1300 */ 726, 727, 785, 783, 880, 202, 999, 208, 894, 893,
+ /* 1310 */ 894, 893, 694, 816, 763, 116, 774, 1290, 431, 432,
+ /* 1320 */ 302, 999, 390, 303, 823, 697, 691, 680, 159, 289,
+ /* 1330 */ 679, 884, 681, 952, 291, 218, 293, 7, 316, 828,
+ /* 1340 */ 173, 805, 259, 364, 252, 911, 376, 713, 295, 435,
+ /* 1350 */ 308, 168, 955, 993, 135, 400, 990, 284, 882, 881,
+ /* 1360 */ 205, 928, 926, 59, 333, 62, 144, 156, 130, 72,
+ /* 1370 */ 802, 366, 367, 393, 137, 185, 189, 160, 139, 383,
+ /* 1380 */ 67, 896, 140, 141, 142, 148, 389, 812, 775, 266,
+ /* 1390 */ 219, 190, 154, 391, 913, 876, 271, 406, 191, 322,
+ /* 1400 */ 682, 733, 192, 342, 732, 724, 731, 711, 723, 421,
+ /* 1410 */ 705, 71, 323, 6, 204, 771, 288, 79, 297, 346,
+ /* 1420 */ 772, 704, 290, 283, 703, 770, 292, 294, 967, 239,
+ /* 1430 */ 769, 102, 862, 438, 426, 240, 424, 442, 73, 213,
+ /* 1440 */ 688, 238, 22, 453, 953, 214, 217, 216, 454, 677,
+ /* 1450 */ 676, 671, 753, 125, 115, 235, 126, 669, 353, 166,
+ /* 1460 */ 127, 244, 179, 357, 306, 304, 305, 307, 113, 892,
+ /* 1470 */ 327, 890, 811, 328, 134, 128, 136, 138, 743, 258,
+ /* 1480 */ 907, 184, 143, 129, 910, 186, 63, 64, 145, 187,
+ /* 1490 */ 906, 65, 8, 66, 13, 188, 202, 899, 265, 149,
+ /* 1500 */ 987, 388, 150, 685, 161, 392, 285, 193, 279, 396,
+ /* 1510 */ 151, 401, 68, 14, 15, 722, 69, 236, 831, 131,
+ /* 1520 */ 830, 860, 70, 751, 16, 414, 755, 4, 174, 220,
+ /* 1530 */ 222, 784, 201, 152, 779, 77, 74, 17, 18, 875,
+ /* 1540 */ 861, 859, 916, 864, 915, 207, 206, 942, 163, 437,
+ /* 1550 */ 948, 943, 164, 209, 1002, 441, 863, 165, 210, 829,
+ /* 1560 */ 695, 87, 312, 211, 1292, 1291, 309,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 19, 142, 143, 144, 145, 24, 1, 26, 77, 78,
- /* 10 */ 79, 80, 81, 82, 83, 84, 85, 86, 87, 88,
- /* 20 */ 89, 90, 91, 92, 15, 98, 26, 27, 7, 8,
- /* 30 */ 49, 50, 77, 78, 79, 80, 109, 82, 83, 84,
- /* 40 */ 85, 86, 87, 88, 89, 90, 91, 92, 22, 68,
- /* 50 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
- /* 60 */ 79, 80, 23, 82, 83, 84, 85, 86, 87, 88,
- /* 70 */ 89, 90, 91, 92, 19, 94, 112, 19, 114, 115,
- /* 80 */ 25, 82, 83, 84, 85, 86, 87, 88, 89, 90,
- /* 90 */ 91, 92, 19, 22, 94, 95, 96, 150, 150, 99,
- /* 100 */ 100, 101, 76, 150, 49, 50, 105, 106, 107, 54,
- /* 110 */ 110, 158, 165, 165, 161, 162, 26, 27, 165, 113,
- /* 120 */ 16, 174, 175, 68, 69, 70, 71, 72, 73, 74,
- /* 130 */ 75, 76, 77, 78, 79, 80, 118, 82, 83, 84,
- /* 140 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 23,
- /* 150 */ 92, 97, 98, 24, 96, 97, 98, 99, 100, 101,
- /* 160 */ 102, 25, 97, 216, 60, 92, 62, 109, 221, 25,
- /* 170 */ 97, 98, 99, 100, 101, 102, 86, 87, 49, 50,
- /* 180 */ 118, 116, 109, 25, 94, 95, 32, 97, 88, 89,
- /* 190 */ 90, 91, 92, 128, 104, 41, 106, 68, 69, 70,
- /* 200 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
- /* 210 */ 11, 82, 83, 84, 85, 86, 87, 88, 89, 90,
- /* 220 */ 91, 92, 19, 19, 86, 87, 88, 89, 90, 91,
- /* 230 */ 92, 27, 96, 150, 66, 99, 100, 101, 112, 150,
- /* 240 */ 114, 115, 138, 150, 161, 162, 110, 103, 165, 222,
- /* 250 */ 223, 224, 49, 50, 165, 22, 57, 24, 165, 170,
- /* 260 */ 171, 118, 94, 170, 171, 23, 98, 25, 185, 186,
- /* 270 */ 243, 68, 69, 70, 71, 72, 73, 74, 75, 76,
- /* 280 */ 77, 78, 79, 80, 126, 82, 83, 84, 85, 86,
- /* 290 */ 87, 88, 89, 90, 91, 92, 19, 129, 130, 131,
- /* 300 */ 88, 23, 172, 173, 105, 106, 107, 150, 22, 26,
- /* 310 */ 27, 181, 26, 27, 22, 116, 26, 27, 26, 230,
- /* 320 */ 231, 197, 165, 230, 231, 113, 49, 50, 204, 117,
- /* 330 */ 96, 174, 175, 99, 100, 101, 22, 26, 27, 136,
- /* 340 */ 26, 27, 118, 16, 110, 68, 69, 70, 71, 72,
- /* 350 */ 73, 74, 75, 76, 77, 78, 79, 80, 118, 82,
- /* 360 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
- /* 370 */ 19, 214, 215, 150, 23, 23, 155, 94, 95, 22,
- /* 380 */ 94, 95, 116, 160, 94, 95, 94, 60, 165, 62,
- /* 390 */ 112, 26, 114, 115, 128, 23, 36, 174, 175, 88,
- /* 400 */ 49, 50, 57, 120, 22, 94, 95, 23, 94, 95,
- /* 410 */ 120, 51, 25, 136, 169, 170, 171, 194, 58, 68,
- /* 420 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
- /* 430 */ 79, 80, 23, 82, 83, 84, 85, 86, 87, 88,
- /* 440 */ 89, 90, 91, 92, 19, 150, 12, 12, 23, 228,
- /* 450 */ 105, 106, 107, 23, 233, 25, 165, 19, 150, 94,
- /* 460 */ 165, 116, 28, 28, 112, 174, 114, 115, 108, 174,
- /* 470 */ 175, 26, 27, 165, 49, 50, 231, 11, 44, 44,
- /* 480 */ 46, 46, 174, 175, 112, 160, 114, 115, 50, 22,
- /* 490 */ 23, 57, 25, 68, 69, 70, 71, 72, 73, 74,
- /* 500 */ 75, 76, 77, 78, 79, 80, 119, 82, 83, 84,
- /* 510 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 194,
- /* 520 */ 225, 23, 23, 215, 19, 95, 105, 106, 107, 150,
- /* 530 */ 23, 150, 27, 23, 67, 25, 150, 206, 207, 94,
- /* 540 */ 95, 166, 104, 218, 165, 22, 165, 109, 49, 50,
- /* 550 */ 120, 165, 25, 174, 175, 174, 175, 23, 21, 234,
- /* 560 */ 174, 175, 22, 23, 239, 25, 25, 68, 69, 70,
- /* 570 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
- /* 580 */ 205, 82, 83, 84, 85, 86, 87, 88, 89, 90,
- /* 590 */ 91, 92, 19, 22, 23, 216, 23, 222, 223, 224,
- /* 600 */ 63, 220, 35, 150, 150, 163, 220, 67, 166, 167,
- /* 610 */ 168, 150, 169, 170, 171, 161, 162, 25, 165, 165,
- /* 620 */ 150, 113, 49, 50, 25, 117, 165, 174, 175, 35,
- /* 630 */ 7, 8, 9, 160, 160, 165, 120, 100, 67, 247,
- /* 640 */ 248, 68, 69, 70, 71, 72, 73, 74, 75, 76,
- /* 650 */ 77, 78, 79, 80, 193, 82, 83, 84, 85, 86,
- /* 660 */ 87, 88, 89, 90, 91, 92, 19, 194, 194, 150,
- /* 670 */ 135, 24, 137, 35, 231, 138, 150, 129, 130, 206,
- /* 680 */ 207, 30, 27, 213, 165, 34, 118, 95, 0, 1,
- /* 690 */ 2, 165, 218, 174, 175, 50, 49, 50, 22, 48,
- /* 700 */ 174, 175, 22, 23, 23, 244, 222, 223, 224, 166,
- /* 710 */ 167, 168, 120, 239, 23, 68, 69, 70, 71, 72,
- /* 720 */ 73, 74, 75, 76, 77, 78, 79, 80, 150, 82,
- /* 730 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
- /* 740 */ 19, 150, 173, 165, 181, 182, 24, 67, 26, 104,
- /* 750 */ 181, 188, 174, 175, 150, 39, 165, 150, 52, 150,
- /* 760 */ 150, 150, 150, 144, 145, 174, 175, 249, 250, 165,
- /* 770 */ 49, 50, 165, 52, 165, 165, 165, 165, 174, 175,
- /* 780 */ 29, 174, 175, 174, 175, 174, 175, 160, 22, 68,
- /* 790 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78,
- /* 800 */ 79, 80, 150, 82, 83, 84, 85, 86, 87, 88,
- /* 810 */ 89, 90, 91, 92, 19, 150, 94, 165, 150, 150,
- /* 820 */ 160, 194, 150, 213, 160, 52, 174, 175, 23, 23,
- /* 830 */ 165, 25, 22, 165, 165, 150, 150, 165, 52, 174,
- /* 840 */ 175, 22, 174, 175, 49, 50, 174, 175, 190, 191,
- /* 850 */ 165, 165, 240, 23, 194, 25, 187, 109, 194, 174,
- /* 860 */ 175, 190, 191, 68, 69, 70, 71, 72, 73, 74,
- /* 870 */ 75, 76, 77, 78, 79, 80, 150, 82, 83, 84,
- /* 880 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 150,
- /* 890 */ 22, 165, 150, 23, 150, 25, 150, 166, 91, 92,
- /* 900 */ 174, 175, 22, 217, 165, 150, 102, 165, 150, 165,
- /* 910 */ 150, 165, 150, 174, 175, 19, 174, 175, 49, 50,
- /* 920 */ 165, 86, 87, 165, 23, 165, 25, 165, 24, 174,
- /* 930 */ 175, 187, 174, 175, 174, 175, 205, 68, 69, 70,
- /* 940 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
- /* 950 */ 150, 82, 83, 84, 85, 86, 87, 88, 89, 90,
- /* 960 */ 91, 92, 19, 150, 150, 165, 150, 150, 166, 23,
- /* 970 */ 150, 25, 160, 20, 174, 175, 1, 2, 165, 165,
- /* 980 */ 104, 165, 165, 43, 150, 165, 240, 150, 49, 50,
- /* 990 */ 174, 175, 49, 50, 23, 23, 25, 25, 53, 165,
- /* 1000 */ 187, 187, 165, 23, 187, 25, 194, 205, 174, 175,
- /* 1010 */ 71, 72, 69, 70, 71, 72, 73, 74, 75, 76,
- /* 1020 */ 77, 78, 79, 80, 150, 82, 83, 84, 85, 86,
- /* 1030 */ 87, 88, 89, 90, 91, 92, 19, 98, 150, 165,
- /* 1040 */ 150, 160, 150, 59, 25, 53, 104, 22, 174, 175,
- /* 1050 */ 213, 138, 5, 165, 1, 165, 150, 165, 150, 150,
- /* 1060 */ 240, 150, 174, 175, 174, 175, 49, 50, 118, 150,
- /* 1070 */ 35, 165, 27, 165, 165, 194, 165, 108, 127, 76,
- /* 1080 */ 174, 175, 174, 175, 165, 174, 175, 70, 71, 72,
- /* 1090 */ 73, 74, 75, 76, 77, 78, 79, 80, 166, 82,
- /* 1100 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92,
- /* 1110 */ 19, 20, 193, 22, 150, 150, 150, 26, 27, 76,
- /* 1120 */ 150, 22, 1, 150, 119, 121, 217, 20, 37, 165,
- /* 1130 */ 165, 165, 16, 19, 20, 165, 22, 205, 165, 119,
- /* 1140 */ 26, 27, 108, 128, 150, 150, 150, 56, 150, 22,
- /* 1150 */ 150, 37, 150, 127, 160, 23, 150, 66, 193, 165,
- /* 1160 */ 165, 165, 16, 165, 23, 165, 150, 165, 174, 175,
- /* 1170 */ 56, 165, 150, 65, 174, 175, 15, 86, 87, 88,
- /* 1180 */ 66, 165, 140, 150, 93, 94, 95, 165, 194, 98,
- /* 1190 */ 174, 175, 22, 3, 164, 193, 174, 175, 165, 150,
- /* 1200 */ 86, 87, 4, 180, 150, 248, 251, 93, 94, 95,
- /* 1210 */ 216, 180, 98, 251, 165, 221, 150, 149, 6, 165,
- /* 1220 */ 129, 130, 131, 132, 133, 134, 193, 150, 174, 175,
- /* 1230 */ 116, 165, 19, 20, 150, 22, 149, 151, 150, 26,
- /* 1240 */ 27, 149, 165, 129, 130, 131, 132, 133, 134, 165,
- /* 1250 */ 37, 174, 175, 165, 149, 19, 20, 13, 22, 150,
- /* 1260 */ 150, 150, 26, 27, 146, 147, 151, 150, 25, 56,
- /* 1270 */ 152, 159, 154, 37, 165, 165, 165, 193, 160, 66,
- /* 1280 */ 116, 193, 165, 174, 175, 174, 175, 194, 199, 150,
- /* 1290 */ 200, 126, 56, 124, 123, 150, 201, 122, 150, 86,
- /* 1300 */ 87, 150, 66, 193, 165, 202, 93, 94, 95, 150,
- /* 1310 */ 165, 98, 194, 165, 125, 22, 165, 150, 150, 26,
- /* 1320 */ 27, 135, 86, 87, 165, 174, 175, 203, 226, 93,
- /* 1330 */ 94, 95, 165, 165, 98, 150, 218, 150, 193, 157,
- /* 1340 */ 118, 157, 129, 130, 131, 132, 133, 134, 5, 104,
- /* 1350 */ 165, 211, 165, 10, 11, 12, 13, 14, 150, 66,
- /* 1360 */ 17, 174, 175, 210, 246, 129, 130, 131, 132, 133,
- /* 1370 */ 134, 150, 210, 165, 31, 121, 33, 150, 150, 86,
- /* 1380 */ 87, 176, 174, 175, 150, 42, 165, 94, 211, 210,
- /* 1390 */ 150, 98, 165, 165, 211, 174, 175, 150, 55, 165,
- /* 1400 */ 57, 150, 174, 175, 61, 165, 150, 64, 174, 175,
- /* 1410 */ 150, 150, 165, 150, 174, 175, 165, 104, 150, 184,
- /* 1420 */ 150, 165, 129, 130, 131, 165, 165, 150, 165, 150,
- /* 1430 */ 150, 176, 150, 165, 47, 165, 150, 150, 176, 103,
- /* 1440 */ 150, 22, 165, 178, 165, 165, 179, 165, 105, 106,
- /* 1450 */ 107, 165, 165, 229, 111, 165, 92, 176, 229, 116,
- /* 1460 */ 184, 176, 179, 156, 176, 176, 18, 157, 156, 237,
- /* 1470 */ 45, 157, 156, 135, 157, 157, 238, 156, 68, 157,
- /* 1480 */ 189, 189, 139, 219, 22, 157, 18, 192, 192, 192,
- /* 1490 */ 192, 189, 219, 199, 157, 242, 40, 157, 199, 242,
- /* 1500 */ 153, 157, 38, 245, 196, 166, 232, 198, 177, 177,
- /* 1510 */ 232, 227, 209, 178, 166, 182, 166, 148, 177, 177,
- /* 1520 */ 209, 196, 177, 199, 209, 199, 166, 208, 92, 195,
- /* 1530 */ 174, 174, 183, 252, 183, 183, 252, 191, 252, 235,
- /* 1540 */ 186, 241, 241, 252, 186, 252, 252, 252, 252, 252,
- /* 1550 */ 252, 252, 252, 252, 252, 252, 236,
+ /* 0 */ 19, 95, 53, 97, 22, 24, 24, 101, 27, 28,
+ /* 10 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ /* 20 */ 39, 40, 41, 152, 43, 44, 45, 46, 47, 48,
+ /* 30 */ 49, 50, 51, 52, 53, 19, 55, 55, 132, 133,
+ /* 40 */ 134, 1, 2, 27, 28, 29, 30, 31, 32, 33,
+ /* 50 */ 34, 35, 36, 37, 38, 39, 40, 41, 187, 43,
+ /* 60 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 70 */ 47, 48, 49, 50, 51, 52, 53, 61, 97, 97,
+ /* 80 */ 19, 49, 50, 51, 52, 53, 70, 26, 27, 28,
+ /* 90 */ 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
+ /* 100 */ 39, 40, 41, 152, 43, 44, 45, 46, 47, 48,
+ /* 110 */ 49, 50, 51, 52, 53, 144, 145, 146, 147, 19,
+ /* 120 */ 16, 22, 92, 172, 173, 52, 53, 27, 28, 29,
+ /* 130 */ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ /* 140 */ 40, 41, 81, 43, 44, 45, 46, 47, 48, 49,
+ /* 150 */ 50, 51, 52, 53, 55, 56, 19, 152, 207, 208,
+ /* 160 */ 115, 24, 117, 118, 27, 28, 29, 30, 31, 32,
+ /* 170 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 79,
+ /* 180 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 190 */ 53, 19, 88, 157, 90, 23, 97, 98, 193, 27,
+ /* 200 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 210 */ 38, 39, 40, 41, 152, 43, 44, 45, 46, 47,
+ /* 220 */ 48, 49, 50, 51, 52, 53, 19, 22, 23, 172,
+ /* 230 */ 23, 26, 119, 120, 27, 28, 29, 30, 31, 32,
+ /* 240 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 187,
+ /* 250 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 260 */ 53, 19, 22, 23, 228, 23, 26, 231, 152, 27,
+ /* 270 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 280 */ 38, 39, 40, 41, 172, 43, 44, 45, 46, 47,
+ /* 290 */ 48, 49, 50, 51, 52, 53, 19, 221, 222, 223,
+ /* 300 */ 23, 96, 152, 172, 27, 28, 29, 30, 31, 32,
+ /* 310 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 152,
+ /* 320 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 330 */ 53, 19, 0, 1, 2, 23, 96, 190, 191, 27,
+ /* 340 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 350 */ 38, 39, 40, 41, 238, 43, 44, 45, 46, 47,
+ /* 360 */ 48, 49, 50, 51, 52, 53, 19, 185, 218, 221,
+ /* 370 */ 222, 223, 152, 152, 27, 28, 29, 30, 31, 32,
+ /* 380 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 241,
+ /* 390 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 400 */ 53, 19, 152, 168, 169, 170, 22, 190, 191, 27,
+ /* 410 */ 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ /* 420 */ 38, 39, 40, 41, 152, 43, 44, 45, 46, 47,
+ /* 430 */ 48, 49, 50, 51, 52, 53, 19, 19, 218, 55,
+ /* 440 */ 56, 24, 22, 152, 27, 28, 29, 30, 31, 32,
+ /* 450 */ 33, 34, 35, 36, 37, 38, 39, 40, 41, 152,
+ /* 460 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 470 */ 53, 250, 194, 195, 56, 55, 56, 55, 19, 172,
+ /* 480 */ 173, 97, 98, 152, 206, 138, 27, 28, 29, 30,
+ /* 490 */ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ /* 500 */ 41, 152, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 510 */ 51, 52, 53, 19, 207, 208, 152, 97, 98, 97,
+ /* 520 */ 138, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ /* 530 */ 36, 37, 38, 39, 40, 41, 181, 43, 44, 45,
+ /* 540 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 30,
+ /* 550 */ 31, 32, 33, 247, 248, 19, 152, 28, 29, 30,
+ /* 560 */ 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ /* 570 */ 41, 152, 43, 44, 45, 46, 47, 48, 49, 50,
+ /* 580 */ 51, 52, 53, 19, 168, 169, 170, 238, 19, 53,
+ /* 590 */ 152, 172, 173, 29, 30, 31, 32, 33, 34, 35,
+ /* 600 */ 36, 37, 38, 39, 40, 41, 152, 43, 44, 45,
+ /* 610 */ 46, 47, 48, 49, 50, 51, 52, 53, 19, 20,
+ /* 620 */ 101, 22, 23, 169, 170, 56, 207, 85, 55, 56,
+ /* 630 */ 23, 19, 20, 26, 22, 99, 100, 101, 102, 103,
+ /* 640 */ 104, 105, 238, 152, 152, 210, 47, 48, 112, 152,
+ /* 650 */ 108, 109, 110, 54, 55, 56, 221, 222, 223, 47,
+ /* 660 */ 48, 119, 120, 172, 173, 66, 54, 55, 56, 152,
+ /* 670 */ 97, 98, 99, 148, 149, 102, 103, 104, 66, 154,
+ /* 680 */ 23, 156, 83, 26, 230, 152, 113, 152, 163, 194,
+ /* 690 */ 195, 92, 92, 30, 95, 83, 97, 98, 207, 208,
+ /* 700 */ 101, 206, 179, 180, 92, 172, 173, 95, 152, 97,
+ /* 710 */ 98, 188, 99, 101, 219, 102, 103, 104, 152, 119,
+ /* 720 */ 120, 196, 55, 56, 19, 20, 113, 22, 193, 163,
+ /* 730 */ 11, 132, 133, 134, 135, 136, 24, 65, 172, 173,
+ /* 740 */ 207, 208, 250, 152, 132, 133, 134, 135, 136, 193,
+ /* 750 */ 78, 84, 47, 48, 49, 98, 199, 152, 86, 54,
+ /* 760 */ 55, 56, 196, 152, 97, 98, 209, 55, 163, 244,
+ /* 770 */ 107, 66, 152, 207, 208, 164, 175, 172, 173, 19,
+ /* 780 */ 20, 124, 22, 111, 38, 39, 40, 41, 83, 43,
+ /* 790 */ 44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
+ /* 800 */ 95, 196, 97, 98, 85, 152, 101, 47, 48, 97,
+ /* 810 */ 85, 92, 207, 193, 54, 55, 56, 92, 49, 175,
+ /* 820 */ 55, 56, 221, 222, 223, 12, 66, 108, 109, 110,
+ /* 830 */ 137, 163, 139, 108, 109, 110, 26, 132, 133, 134,
+ /* 840 */ 135, 136, 152, 83, 43, 44, 45, 46, 47, 48,
+ /* 850 */ 49, 50, 51, 52, 53, 95, 26, 97, 98, 55,
+ /* 860 */ 56, 101, 97, 98, 196, 221, 222, 223, 146, 147,
+ /* 870 */ 57, 171, 152, 22, 26, 19, 20, 49, 22, 179,
+ /* 880 */ 108, 109, 110, 55, 56, 116, 73, 219, 75, 124,
+ /* 890 */ 121, 152, 132, 133, 134, 135, 136, 163, 85, 152,
+ /* 900 */ 232, 97, 98, 47, 48, 237, 55, 56, 98, 5,
+ /* 910 */ 54, 55, 56, 193, 10, 11, 12, 13, 14, 172,
+ /* 920 */ 173, 17, 66, 47, 48, 97, 98, 152, 124, 152,
+ /* 930 */ 196, 55, 56, 186, 124, 152, 106, 160, 152, 83,
+ /* 940 */ 152, 164, 152, 61, 22, 211, 212, 152, 97, 98,
+ /* 950 */ 152, 95, 70, 97, 98, 172, 173, 101, 172, 173,
+ /* 960 */ 172, 173, 172, 173, 60, 181, 62, 172, 173, 47,
+ /* 970 */ 48, 123, 186, 97, 98, 71, 100, 55, 56, 152,
+ /* 980 */ 181, 186, 21, 107, 152, 109, 82, 163, 132, 133,
+ /* 990 */ 134, 135, 136, 89, 16, 207, 92, 93, 19, 172,
+ /* 1000 */ 173, 169, 170, 195, 55, 56, 12, 152, 132, 30,
+ /* 1010 */ 134, 47, 48, 186, 206, 225, 152, 95, 114, 97,
+ /* 1020 */ 196, 245, 246, 101, 152, 38, 39, 40, 41, 42,
+ /* 1030 */ 43, 44, 45, 46, 47, 48, 49, 50, 51, 52,
+ /* 1040 */ 53, 152, 163, 219, 152, 141, 97, 98, 193, 152,
+ /* 1050 */ 152, 57, 91, 164, 132, 133, 134, 152, 55, 152,
+ /* 1060 */ 152, 237, 230, 152, 103, 193, 88, 73, 90, 75,
+ /* 1070 */ 172, 173, 183, 152, 185, 196, 152, 172, 173, 172,
+ /* 1080 */ 173, 217, 152, 172, 173, 152, 107, 22, 152, 24,
+ /* 1090 */ 193, 112, 152, 172, 173, 152, 132, 242, 134, 152,
+ /* 1100 */ 97, 140, 152, 92, 152, 172, 173, 152, 172, 173,
+ /* 1110 */ 152, 100, 172, 173, 152, 172, 173, 152, 140, 172,
+ /* 1120 */ 173, 152, 172, 173, 172, 173, 152, 172, 173, 152,
+ /* 1130 */ 172, 173, 152, 152, 172, 173, 152, 172, 173, 213,
+ /* 1140 */ 152, 172, 173, 152, 152, 152, 172, 173, 152, 172,
+ /* 1150 */ 173, 152, 172, 173, 152, 210, 172, 173, 152, 26,
+ /* 1160 */ 172, 173, 152, 172, 173, 172, 173, 152, 172, 173,
+ /* 1170 */ 152, 172, 173, 152, 172, 173, 152, 59, 172, 173,
+ /* 1180 */ 152, 63, 172, 173, 152, 193, 152, 152, 152, 152,
+ /* 1190 */ 172, 173, 152, 172, 173, 77, 172, 173, 152, 152,
+ /* 1200 */ 172, 173, 152, 152, 172, 173, 172, 173, 172, 173,
+ /* 1210 */ 152, 22, 172, 173, 152, 152, 152, 22, 172, 173,
+ /* 1220 */ 152, 152, 152, 172, 173, 152, 7, 8, 9, 163,
+ /* 1230 */ 172, 173, 22, 23, 172, 173, 172, 173, 166, 167,
+ /* 1240 */ 172, 173, 172, 173, 55, 172, 173, 22, 23, 108,
+ /* 1250 */ 109, 110, 217, 152, 217, 166, 167, 163, 163, 163,
+ /* 1260 */ 163, 163, 196, 130, 217, 211, 212, 217, 116, 23,
+ /* 1270 */ 22, 101, 26, 121, 23, 23, 23, 26, 26, 26,
+ /* 1280 */ 23, 23, 112, 26, 26, 37, 97, 100, 101, 55,
+ /* 1290 */ 196, 196, 196, 196, 196, 23, 23, 55, 26, 26,
+ /* 1300 */ 7, 8, 23, 152, 23, 26, 96, 26, 132, 132,
+ /* 1310 */ 134, 134, 23, 152, 152, 26, 152, 122, 152, 191,
+ /* 1320 */ 152, 96, 234, 152, 152, 152, 152, 152, 197, 210,
+ /* 1330 */ 152, 97, 152, 152, 210, 233, 210, 198, 150, 97,
+ /* 1340 */ 184, 201, 239, 214, 214, 201, 239, 180, 214, 227,
+ /* 1350 */ 200, 198, 155, 67, 243, 176, 69, 175, 175, 175,
+ /* 1360 */ 122, 159, 159, 240, 159, 240, 22, 220, 27, 130,
+ /* 1370 */ 201, 18, 159, 18, 189, 158, 158, 220, 192, 159,
+ /* 1380 */ 137, 236, 192, 192, 192, 189, 74, 189, 159, 235,
+ /* 1390 */ 159, 158, 22, 177, 201, 201, 159, 107, 158, 177,
+ /* 1400 */ 159, 174, 158, 76, 174, 182, 174, 106, 182, 125,
+ /* 1410 */ 174, 107, 177, 22, 159, 216, 215, 137, 159, 53,
+ /* 1420 */ 216, 176, 215, 174, 174, 216, 215, 215, 174, 229,
+ /* 1430 */ 216, 129, 224, 177, 126, 229, 127, 177, 128, 25,
+ /* 1440 */ 162, 226, 26, 161, 13, 153, 6, 153, 151, 151,
+ /* 1450 */ 151, 151, 205, 165, 178, 178, 165, 4, 3, 22,
+ /* 1460 */ 165, 142, 15, 94, 202, 204, 203, 201, 16, 23,
+ /* 1470 */ 249, 23, 120, 249, 246, 111, 131, 123, 20, 16,
+ /* 1480 */ 1, 125, 123, 111, 56, 64, 37, 37, 131, 122,
+ /* 1490 */ 1, 37, 5, 37, 22, 107, 26, 80, 140, 80,
+ /* 1500 */ 87, 72, 107, 20, 24, 19, 112, 105, 23, 79,
+ /* 1510 */ 22, 79, 22, 22, 22, 58, 22, 79, 23, 68,
+ /* 1520 */ 23, 23, 26, 116, 22, 26, 23, 22, 122, 23,
+ /* 1530 */ 23, 56, 64, 22, 124, 26, 26, 64, 64, 23,
+ /* 1540 */ 23, 23, 23, 11, 23, 22, 26, 23, 22, 24,
+ /* 1550 */ 1, 23, 22, 26, 251, 24, 23, 22, 122, 23,
+ /* 1560 */ 23, 22, 15, 122, 122, 122, 23,
};
-#define YY_SHIFT_USE_DFLT (-74)
-#define YY_SHIFT_COUNT (418)
-#define YY_SHIFT_MIN (-73)
-#define YY_SHIFT_MAX (1468)
+#define YY_SHIFT_USE_DFLT (1567)
+#define YY_SHIFT_COUNT (455)
+#define YY_SHIFT_MIN (-94)
+#define YY_SHIFT_MAX (1549)
static const short yy_shift_ofst[] = {
- /* 0 */ 975, 1114, 1343, 1114, 1213, 1213, 90, 90, 0, -19,
- /* 10 */ 1213, 1213, 1213, 1213, 1213, 345, 445, 721, 1091, 1213,
- /* 20 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
- /* 30 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
- /* 40 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1236, 1213, 1213,
- /* 50 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213,
- /* 60 */ 1213, 199, 445, 445, 835, 835, 365, 1164, 55, 647,
- /* 70 */ 573, 499, 425, 351, 277, 203, 129, 795, 795, 795,
- /* 80 */ 795, 795, 795, 795, 795, 795, 795, 795, 795, 795,
- /* 90 */ 795, 795, 795, 795, 795, 869, 795, 943, 1017, 1017,
- /* 100 */ -69, -45, -45, -45, -45, -45, -1, 58, 138, 100,
- /* 110 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445,
- /* 120 */ 445, 445, 445, 445, 445, 445, 537, 438, 445, 445,
- /* 130 */ 445, 445, 445, 365, 807, 1436, -74, -74, -74, 1293,
- /* 140 */ 73, 434, 434, 311, 314, 290, 283, 286, 540, 467,
- /* 150 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445,
- /* 160 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445,
- /* 170 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445,
- /* 180 */ 445, 445, 65, 722, 722, 722, 688, 266, 1164, 1164,
- /* 190 */ 1164, -74, -74, -74, 136, 168, 168, 234, 360, 360,
- /* 200 */ 360, 430, 372, 435, 352, 278, 126, -36, -36, -36,
- /* 210 */ -36, 421, 651, -36, -36, 592, 292, 212, 623, 158,
- /* 220 */ 204, 204, 505, 158, 505, 144, 365, 154, 365, 154,
- /* 230 */ 645, 154, 204, 154, 154, 535, 548, 548, 365, 387,
- /* 240 */ 508, 233, 1464, 1222, 1222, 1456, 1456, 1222, 1462, 1410,
- /* 250 */ 1165, 1468, 1468, 1468, 1468, 1222, 1165, 1462, 1410, 1410,
- /* 260 */ 1222, 1448, 1338, 1425, 1222, 1222, 1448, 1222, 1448, 1222,
- /* 270 */ 1448, 1419, 1313, 1313, 1313, 1387, 1364, 1364, 1419, 1313,
- /* 280 */ 1336, 1313, 1387, 1313, 1313, 1254, 1245, 1254, 1245, 1254,
- /* 290 */ 1245, 1222, 1222, 1186, 1189, 1175, 1169, 1171, 1165, 1164,
- /* 300 */ 1243, 1244, 1244, 1212, 1212, 1212, 1212, -74, -74, -74,
- /* 310 */ -74, -74, -74, 939, 104, 680, 571, 327, 1, 980,
- /* 320 */ 26, 972, 971, 946, 901, 870, 830, 806, 54, 21,
- /* 330 */ -73, 510, 242, 1198, 1190, 1170, 1042, 1161, 1108, 1146,
- /* 340 */ 1141, 1132, 1015, 1127, 1026, 1034, 1020, 1107, 1004, 1116,
- /* 350 */ 1121, 1005, 1099, 951, 1043, 1003, 969, 1045, 1035, 950,
- /* 360 */ 1053, 1047, 1025, 942, 913, 992, 1019, 945, 984, 940,
- /* 370 */ 876, 904, 953, 896, 748, 804, 880, 786, 868, 819,
- /* 380 */ 805, 810, 773, 751, 766, 706, 716, 691, 681, 568,
- /* 390 */ 655, 638, 676, 516, 541, 594, 599, 567, 541, 534,
- /* 400 */ 507, 527, 498, 523, 466, 382, 409, 384, 357, 6,
- /* 410 */ 240, 224, 143, 62, 18, 71, 39, 9, 5,
+ /* 0 */ 40, 599, 904, 612, 760, 760, 760, 760, 725, -19,
+ /* 10 */ 16, 16, 100, 760, 760, 760, 760, 760, 760, 760,
+ /* 20 */ 876, 876, 573, 542, 719, 600, 61, 137, 172, 207,
+ /* 30 */ 242, 277, 312, 347, 382, 417, 459, 459, 459, 459,
+ /* 40 */ 459, 459, 459, 459, 459, 459, 459, 459, 459, 459,
+ /* 50 */ 459, 459, 459, 494, 459, 529, 564, 564, 705, 760,
+ /* 60 */ 760, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 70 */ 760, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 80 */ 760, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 90 */ 856, 760, 760, 760, 760, 760, 760, 760, 760, 760,
+ /* 100 */ 760, 760, 760, 760, 987, 746, 746, 746, 746, 746,
+ /* 110 */ 801, 23, 32, 949, 961, 979, 964, 964, 949, 73,
+ /* 120 */ 113, -51, 1567, 1567, 1567, 536, 536, 536, 99, 99,
+ /* 130 */ 813, 813, 667, 205, 240, 949, 949, 949, 949, 949,
+ /* 140 */ 949, 949, 949, 949, 949, 949, 949, 949, 949, 949,
+ /* 150 */ 949, 949, 949, 949, 949, 332, 1011, 422, 422, 113,
+ /* 160 */ 30, 30, 30, 30, 30, 30, 1567, 1567, 1567, 922,
+ /* 170 */ -94, -94, 384, 613, 828, 420, 765, 804, 851, 949,
+ /* 180 */ 949, 949, 949, 949, 949, 949, 949, 949, 949, 949,
+ /* 190 */ 949, 949, 949, 949, 949, 672, 672, 672, 949, 949,
+ /* 200 */ 657, 949, 949, 949, -18, 949, 949, 994, 949, 949,
+ /* 210 */ 949, 949, 949, 949, 949, 949, 949, 949, 772, 1118,
+ /* 220 */ 712, 712, 712, 810, 45, 769, 1219, 1133, 418, 418,
+ /* 230 */ 569, 1133, 569, 830, 607, 663, 882, 418, 693, 882,
+ /* 240 */ 882, 848, 1152, 1065, 1286, 1238, 1238, 1287, 1287, 1238,
+ /* 250 */ 1344, 1341, 1239, 1353, 1353, 1353, 1353, 1238, 1355, 1239,
+ /* 260 */ 1344, 1341, 1341, 1239, 1238, 1355, 1243, 1312, 1238, 1238,
+ /* 270 */ 1355, 1370, 1238, 1355, 1238, 1355, 1370, 1290, 1290, 1290,
+ /* 280 */ 1327, 1370, 1290, 1301, 1290, 1327, 1290, 1290, 1284, 1304,
+ /* 290 */ 1284, 1304, 1284, 1304, 1284, 1304, 1238, 1391, 1238, 1280,
+ /* 300 */ 1370, 1366, 1366, 1370, 1302, 1308, 1310, 1309, 1239, 1414,
+ /* 310 */ 1416, 1431, 1431, 1440, 1440, 1440, 1440, 1567, 1567, 1567,
+ /* 320 */ 1567, 1567, 1567, 1567, 1567, 519, 978, 1210, 1225, 104,
+ /* 330 */ 1141, 1189, 1246, 1248, 1251, 1252, 1253, 1257, 1258, 1273,
+ /* 340 */ 1003, 1187, 1293, 1170, 1272, 1279, 1234, 1281, 1176, 1177,
+ /* 350 */ 1289, 1242, 1195, 1453, 1455, 1437, 1319, 1447, 1369, 1452,
+ /* 360 */ 1446, 1448, 1352, 1345, 1364, 1354, 1458, 1356, 1463, 1479,
+ /* 370 */ 1359, 1357, 1449, 1450, 1454, 1456, 1372, 1428, 1421, 1367,
+ /* 380 */ 1489, 1487, 1472, 1388, 1358, 1417, 1470, 1419, 1413, 1429,
+ /* 390 */ 1395, 1480, 1483, 1486, 1394, 1402, 1488, 1430, 1490, 1491,
+ /* 400 */ 1485, 1492, 1432, 1457, 1494, 1438, 1451, 1495, 1497, 1498,
+ /* 410 */ 1496, 1407, 1502, 1503, 1505, 1499, 1406, 1506, 1507, 1475,
+ /* 420 */ 1468, 1511, 1410, 1509, 1473, 1510, 1474, 1516, 1509, 1517,
+ /* 430 */ 1518, 1519, 1520, 1521, 1523, 1532, 1524, 1526, 1525, 1527,
+ /* 440 */ 1528, 1530, 1531, 1527, 1533, 1535, 1536, 1537, 1539, 1436,
+ /* 450 */ 1441, 1442, 1443, 1543, 1547, 1549,
};
-#define YY_REDUCE_USE_DFLT (-142)
-#define YY_REDUCE_COUNT (312)
-#define YY_REDUCE_MIN (-141)
-#define YY_REDUCE_MAX (1369)
+#define YY_REDUCE_USE_DFLT (-130)
+#define YY_REDUCE_COUNT (324)
+#define YY_REDUCE_MIN (-129)
+#define YY_REDUCE_MAX (1300)
static const short yy_reduce_ofst[] = {
- /* 0 */ -141, 994, 1118, 223, 157, -53, 93, 89, 83, 375,
- /* 10 */ 386, 381, 379, 308, 295, 325, -47, 27, 1240, 1234,
- /* 20 */ 1228, 1221, 1208, 1187, 1151, 1111, 1109, 1077, 1054, 1022,
- /* 30 */ 1016, 1000, 911, 908, 906, 890, 888, 874, 834, 816,
- /* 40 */ 800, 760, 758, 755, 742, 739, 726, 685, 672, 668,
- /* 50 */ 665, 652, 611, 609, 607, 604, 591, 578, 526, 519,
- /* 60 */ 453, 474, 454, 461, 443, 245, 442, 473, 484, 484,
- /* 70 */ 484, 484, 484, 484, 484, 484, 484, 484, 484, 484,
- /* 80 */ 484, 484, 484, 484, 484, 484, 484, 484, 484, 484,
- /* 90 */ 484, 484, 484, 484, 484, 484, 484, 484, 484, 484,
- /* 100 */ 484, 484, 484, 484, 484, 484, 484, 130, 484, 484,
- /* 110 */ 1145, 909, 1110, 1088, 1084, 1033, 1002, 965, 820, 837,
- /* 120 */ 746, 686, 612, 817, 610, 919, 221, 563, 814, 813,
- /* 130 */ 744, 669, 470, 543, 484, 484, 484, 484, 484, 291,
- /* 140 */ 569, 671, 658, 970, 1290, 1287, 1286, 1282, 518, 518,
- /* 150 */ 1280, 1279, 1277, 1270, 1268, 1263, 1261, 1260, 1256, 1251,
- /* 160 */ 1247, 1227, 1185, 1168, 1167, 1159, 1148, 1139, 1117, 1066,
- /* 170 */ 1049, 1006, 998, 996, 995, 973, 970, 966, 964, 892,
- /* 180 */ 762, -52, 881, 932, 802, 731, 619, 812, 664, 660,
- /* 190 */ 627, 392, 331, 124, 1358, 1357, 1356, 1354, 1352, 1351,
- /* 200 */ 1349, 1319, 1334, 1346, 1334, 1334, 1334, 1334, 1334, 1334,
- /* 210 */ 1334, 1320, 1304, 1334, 1334, 1319, 1360, 1325, 1369, 1326,
- /* 220 */ 1315, 1311, 1301, 1324, 1300, 1335, 1350, 1345, 1348, 1342,
- /* 230 */ 1333, 1341, 1303, 1332, 1331, 1284, 1278, 1274, 1339, 1309,
- /* 240 */ 1308, 1347, 1258, 1344, 1340, 1257, 1253, 1337, 1273, 1302,
- /* 250 */ 1299, 1298, 1297, 1296, 1295, 1328, 1294, 1264, 1292, 1291,
- /* 260 */ 1322, 1321, 1238, 1232, 1318, 1317, 1316, 1314, 1312, 1310,
- /* 270 */ 1307, 1283, 1289, 1288, 1285, 1276, 1229, 1224, 1267, 1281,
- /* 280 */ 1265, 1262, 1235, 1255, 1205, 1183, 1179, 1177, 1162, 1140,
- /* 290 */ 1153, 1184, 1182, 1102, 1124, 1103, 1095, 1090, 1089, 1093,
- /* 300 */ 1112, 1115, 1086, 1105, 1092, 1087, 1068, 962, 955, 957,
- /* 310 */ 1031, 1023, 1030,
+ /* 0 */ -29, 566, 525, 605, -49, 307, 491, 533, 668, 435,
+ /* 10 */ 601, 644, 148, 747, 786, 795, 419, 788, 827, 790,
+ /* 20 */ 454, 832, 889, 495, 824, 734, 76, 76, 76, 76,
+ /* 30 */ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ /* 40 */ 76, 76, 76, 76, 76, 76, 76, 76, 76, 76,
+ /* 50 */ 76, 76, 76, 76, 76, 76, 76, 76, 783, 898,
+ /* 60 */ 905, 907, 911, 921, 933, 936, 940, 943, 947, 950,
+ /* 70 */ 952, 955, 958, 962, 965, 969, 974, 977, 980, 984,
+ /* 80 */ 988, 991, 993, 996, 999, 1002, 1006, 1010, 1018, 1021,
+ /* 90 */ 1024, 1028, 1032, 1034, 1036, 1040, 1046, 1051, 1058, 1062,
+ /* 100 */ 1064, 1068, 1070, 1073, 76, 76, 76, 76, 76, 76,
+ /* 110 */ 76, 76, 76, 855, 36, 523, 235, 416, 777, 76,
+ /* 120 */ 278, 76, 76, 76, 76, 700, 700, 700, 150, 220,
+ /* 130 */ 147, 217, 221, 306, 306, 611, 5, 535, 556, 620,
+ /* 140 */ 720, 872, 897, 116, 864, 349, 1035, 1037, 404, 1047,
+ /* 150 */ 992, -129, 1050, 492, 62, 722, 879, 1072, 1089, 808,
+ /* 160 */ 1066, 1094, 1095, 1096, 1097, 1098, 776, 1054, 557, 57,
+ /* 170 */ 112, 131, 167, 182, 250, 272, 291, 331, 364, 438,
+ /* 180 */ 497, 517, 591, 653, 690, 739, 775, 798, 892, 908,
+ /* 190 */ 924, 930, 1015, 1063, 1069, 355, 784, 799, 981, 1101,
+ /* 200 */ 926, 1151, 1161, 1162, 945, 1164, 1166, 1128, 1168, 1171,
+ /* 210 */ 1172, 250, 1173, 1174, 1175, 1178, 1180, 1181, 1088, 1102,
+ /* 220 */ 1119, 1124, 1126, 926, 1131, 1139, 1188, 1140, 1129, 1130,
+ /* 230 */ 1103, 1144, 1107, 1179, 1156, 1167, 1182, 1134, 1122, 1183,
+ /* 240 */ 1184, 1150, 1153, 1197, 1111, 1202, 1203, 1123, 1125, 1205,
+ /* 250 */ 1147, 1185, 1169, 1186, 1190, 1191, 1192, 1213, 1217, 1193,
+ /* 260 */ 1157, 1196, 1198, 1194, 1220, 1218, 1145, 1154, 1229, 1231,
+ /* 270 */ 1233, 1216, 1237, 1240, 1241, 1244, 1222, 1227, 1230, 1232,
+ /* 280 */ 1223, 1235, 1236, 1245, 1249, 1226, 1250, 1254, 1199, 1201,
+ /* 290 */ 1204, 1207, 1209, 1211, 1214, 1212, 1255, 1208, 1259, 1215,
+ /* 300 */ 1256, 1200, 1206, 1260, 1247, 1261, 1263, 1262, 1266, 1278,
+ /* 310 */ 1282, 1292, 1294, 1297, 1298, 1299, 1300, 1221, 1224, 1228,
+ /* 320 */ 1288, 1291, 1276, 1277, 1295,
};
static const YYACTIONTYPE yy_default[] = {
- /* 0 */ 635, 870, 959, 959, 959, 870, 899, 899, 959, 759,
- /* 10 */ 959, 959, 959, 959, 868, 959, 959, 933, 959, 959,
- /* 20 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 30 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 40 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 50 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 60 */ 959, 959, 959, 959, 899, 899, 674, 763, 794, 959,
- /* 70 */ 959, 959, 959, 959, 959, 959, 959, 932, 934, 809,
- /* 80 */ 808, 802, 801, 912, 774, 799, 792, 785, 796, 871,
- /* 90 */ 864, 865, 863, 867, 872, 959, 795, 831, 848, 830,
- /* 100 */ 842, 847, 854, 846, 843, 833, 832, 666, 834, 835,
- /* 110 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 120 */ 959, 959, 959, 959, 959, 959, 661, 728, 959, 959,
- /* 130 */ 959, 959, 959, 959, 836, 837, 851, 850, 849, 959,
- /* 140 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 150 */ 959, 939, 937, 959, 883, 959, 959, 959, 959, 959,
- /* 160 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 170 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 180 */ 959, 641, 959, 759, 759, 759, 635, 959, 959, 959,
- /* 190 */ 959, 951, 763, 753, 719, 959, 959, 959, 959, 959,
- /* 200 */ 959, 959, 959, 959, 959, 959, 959, 804, 742, 922,
- /* 210 */ 924, 959, 905, 740, 663, 761, 676, 751, 643, 798,
- /* 220 */ 776, 776, 917, 798, 917, 700, 959, 788, 959, 788,
- /* 230 */ 697, 788, 776, 788, 788, 866, 959, 959, 959, 760,
- /* 240 */ 751, 959, 944, 767, 767, 936, 936, 767, 810, 732,
- /* 250 */ 798, 739, 739, 739, 739, 767, 798, 810, 732, 732,
- /* 260 */ 767, 658, 911, 909, 767, 767, 658, 767, 658, 767,
- /* 270 */ 658, 876, 730, 730, 730, 715, 880, 880, 876, 730,
- /* 280 */ 700, 730, 715, 730, 730, 780, 775, 780, 775, 780,
- /* 290 */ 775, 767, 767, 959, 793, 781, 791, 789, 798, 959,
- /* 300 */ 718, 651, 651, 640, 640, 640, 640, 956, 956, 951,
- /* 310 */ 702, 702, 684, 959, 959, 959, 959, 959, 959, 959,
- /* 320 */ 885, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 330 */ 959, 959, 959, 959, 636, 946, 959, 959, 943, 959,
- /* 340 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 350 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 915,
- /* 360 */ 959, 959, 959, 959, 959, 959, 908, 907, 959, 959,
- /* 370 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 380 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 959,
- /* 390 */ 959, 959, 959, 959, 790, 959, 782, 959, 869, 959,
- /* 400 */ 959, 959, 959, 959, 959, 959, 959, 959, 959, 745,
- /* 410 */ 819, 959, 818, 822, 817, 668, 959, 649, 959, 632,
- /* 420 */ 637, 955, 958, 957, 954, 953, 952, 947, 945, 942,
- /* 430 */ 941, 940, 938, 935, 931, 889, 887, 894, 893, 892,
- /* 440 */ 891, 890, 888, 886, 884, 805, 803, 800, 797, 930,
- /* 450 */ 882, 741, 738, 737, 657, 948, 914, 923, 921, 811,
- /* 460 */ 920, 919, 918, 916, 913, 900, 807, 806, 733, 874,
- /* 470 */ 873, 660, 904, 903, 902, 906, 910, 901, 769, 659,
- /* 480 */ 656, 665, 722, 721, 729, 727, 726, 725, 724, 723,
- /* 490 */ 720, 667, 675, 686, 714, 699, 698, 879, 881, 878,
- /* 500 */ 877, 707, 706, 712, 711, 710, 709, 708, 705, 704,
- /* 510 */ 703, 696, 695, 701, 694, 717, 716, 713, 693, 736,
- /* 520 */ 735, 734, 731, 692, 691, 690, 822, 689, 688, 828,
- /* 530 */ 827, 815, 858, 756, 755, 754, 766, 765, 778, 777,
- /* 540 */ 813, 812, 779, 764, 758, 757, 773, 772, 771, 770,
- /* 550 */ 762, 752, 784, 787, 786, 783, 860, 768, 857, 929,
- /* 560 */ 928, 927, 926, 925, 862, 861, 829, 826, 679, 680,
- /* 570 */ 898, 896, 897, 895, 682, 681, 678, 677, 859, 747,
- /* 580 */ 746, 855, 852, 844, 840, 856, 853, 845, 841, 839,
- /* 590 */ 838, 824, 823, 821, 820, 816, 825, 670, 748, 744,
- /* 600 */ 743, 814, 750, 749, 687, 685, 683, 664, 662, 655,
- /* 610 */ 653, 652, 654, 650, 648, 647, 646, 645, 644, 673,
- /* 620 */ 672, 671, 669, 668, 642, 639, 638, 634, 633, 631,
+ /* 0 */ 1281, 1271, 1271, 1271, 1203, 1203, 1203, 1203, 1271, 1096,
+ /* 10 */ 1125, 1125, 1255, 1332, 1332, 1332, 1332, 1332, 1332, 1202,
+ /* 20 */ 1332, 1332, 1332, 1332, 1271, 1100, 1131, 1332, 1332, 1332,
+ /* 30 */ 1332, 1204, 1205, 1332, 1332, 1332, 1254, 1256, 1141, 1140,
+ /* 40 */ 1139, 1138, 1237, 1112, 1136, 1129, 1133, 1204, 1198, 1199,
+ /* 50 */ 1197, 1201, 1205, 1332, 1132, 1167, 1182, 1166, 1332, 1332,
+ /* 60 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 70 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 80 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 90 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 100 */ 1332, 1332, 1332, 1332, 1176, 1181, 1188, 1180, 1177, 1169,
+ /* 110 */ 1168, 1170, 1171, 1332, 1019, 1067, 1332, 1332, 1332, 1172,
+ /* 120 */ 1332, 1173, 1185, 1184, 1183, 1262, 1289, 1288, 1332, 1332,
+ /* 130 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 140 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 150 */ 1332, 1332, 1332, 1332, 1332, 1281, 1271, 1025, 1025, 1332,
+ /* 160 */ 1271, 1271, 1271, 1271, 1271, 1271, 1267, 1100, 1091, 1332,
+ /* 170 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 180 */ 1259, 1257, 1332, 1218, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 190 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 200 */ 1332, 1332, 1332, 1332, 1096, 1332, 1332, 1332, 1332, 1332,
+ /* 210 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1283, 1332, 1232,
+ /* 220 */ 1096, 1096, 1096, 1098, 1080, 1090, 1004, 1135, 1114, 1114,
+ /* 230 */ 1321, 1135, 1321, 1042, 1303, 1039, 1125, 1114, 1200, 1125,
+ /* 240 */ 1125, 1097, 1090, 1332, 1324, 1105, 1105, 1323, 1323, 1105,
+ /* 250 */ 1146, 1070, 1135, 1076, 1076, 1076, 1076, 1105, 1016, 1135,
+ /* 260 */ 1146, 1070, 1070, 1135, 1105, 1016, 1236, 1318, 1105, 1105,
+ /* 270 */ 1016, 1211, 1105, 1016, 1105, 1016, 1211, 1068, 1068, 1068,
+ /* 280 */ 1057, 1211, 1068, 1042, 1068, 1057, 1068, 1068, 1118, 1113,
+ /* 290 */ 1118, 1113, 1118, 1113, 1118, 1113, 1105, 1206, 1105, 1332,
+ /* 300 */ 1211, 1215, 1215, 1211, 1130, 1119, 1128, 1126, 1135, 1022,
+ /* 310 */ 1060, 1286, 1286, 1282, 1282, 1282, 1282, 1329, 1329, 1267,
+ /* 320 */ 1298, 1298, 1044, 1044, 1298, 1332, 1332, 1332, 1332, 1332,
+ /* 330 */ 1332, 1293, 1332, 1220, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 340 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 350 */ 1332, 1332, 1152, 1332, 1000, 1264, 1332, 1332, 1263, 1332,
+ /* 360 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 370 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1320,
+ /* 380 */ 1332, 1332, 1332, 1332, 1332, 1332, 1235, 1234, 1332, 1332,
+ /* 390 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 400 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332,
+ /* 410 */ 1332, 1082, 1332, 1332, 1332, 1307, 1332, 1332, 1332, 1332,
+ /* 420 */ 1332, 1332, 1332, 1127, 1332, 1120, 1332, 1332, 1311, 1332,
+ /* 430 */ 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1332, 1273,
+ /* 440 */ 1332, 1332, 1332, 1272, 1332, 1332, 1332, 1332, 1332, 1154,
+ /* 450 */ 1332, 1153, 1157, 1332, 1010, 1332,
};
+/********** End of lemon-generated parsing tables *****************************/
-/* The next table maps tokens into fallback tokens. If a construct
-** like the following:
+/* The next table maps tokens (terminal symbols) into fallback tokens.
+** If a construct like the following:
**
** %fallback ID X Y Z.
**
@@ -106296,76 +133647,109 @@ static const YYACTIONTYPE yy_default[] = {
** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
** but it does not parse, the type of the token is changed to ID and
** the parse is retried before an error is thrown.
+**
+** This feature can be used, for example, to cause some keywords in a language
+** to revert to identifiers if they keyword does not apply in the context where
+** it appears.
*/
#ifdef YYFALLBACK
static const YYCODETYPE yyFallback[] = {
0, /* $ => nothing */
0, /* SEMI => nothing */
- 26, /* EXPLAIN => ID */
- 26, /* QUERY => ID */
- 26, /* PLAN => ID */
- 26, /* BEGIN => ID */
+ 55, /* EXPLAIN => ID */
+ 55, /* QUERY => ID */
+ 55, /* PLAN => ID */
+ 55, /* BEGIN => ID */
0, /* TRANSACTION => nothing */
- 26, /* DEFERRED => ID */
- 26, /* IMMEDIATE => ID */
- 26, /* EXCLUSIVE => ID */
+ 55, /* DEFERRED => ID */
+ 55, /* IMMEDIATE => ID */
+ 55, /* EXCLUSIVE => ID */
0, /* COMMIT => nothing */
- 26, /* END => ID */
- 26, /* ROLLBACK => ID */
- 26, /* SAVEPOINT => ID */
- 26, /* RELEASE => ID */
+ 55, /* END => ID */
+ 55, /* ROLLBACK => ID */
+ 55, /* SAVEPOINT => ID */
+ 55, /* RELEASE => ID */
0, /* TO => nothing */
0, /* TABLE => nothing */
0, /* CREATE => nothing */
- 26, /* IF => ID */
+ 55, /* IF => ID */
0, /* NOT => nothing */
0, /* EXISTS => nothing */
- 26, /* TEMP => ID */
+ 55, /* TEMP => ID */
0, /* LP => nothing */
0, /* RP => nothing */
0, /* AS => nothing */
+ 55, /* WITHOUT => ID */
0, /* COMMA => nothing */
+ 0, /* OR => nothing */
+ 0, /* AND => nothing */
+ 0, /* IS => nothing */
+ 55, /* MATCH => ID */
+ 55, /* LIKE_KW => ID */
+ 0, /* BETWEEN => nothing */
+ 0, /* IN => nothing */
+ 0, /* ISNULL => nothing */
+ 0, /* NOTNULL => nothing */
+ 0, /* NE => nothing */
+ 0, /* EQ => nothing */
+ 0, /* GT => nothing */
+ 0, /* LE => nothing */
+ 0, /* LT => nothing */
+ 0, /* GE => nothing */
+ 0, /* ESCAPE => nothing */
+ 0, /* BITAND => nothing */
+ 0, /* BITOR => nothing */
+ 0, /* LSHIFT => nothing */
+ 0, /* RSHIFT => nothing */
+ 0, /* PLUS => nothing */
+ 0, /* MINUS => nothing */
+ 0, /* STAR => nothing */
+ 0, /* SLASH => nothing */
+ 0, /* REM => nothing */
+ 0, /* CONCAT => nothing */
+ 0, /* COLLATE => nothing */
+ 0, /* BITNOT => nothing */
0, /* ID => nothing */
0, /* INDEXED => nothing */
- 26, /* ABORT => ID */
- 26, /* ACTION => ID */
- 26, /* AFTER => ID */
- 26, /* ANALYZE => ID */
- 26, /* ASC => ID */
- 26, /* ATTACH => ID */
- 26, /* BEFORE => ID */
- 26, /* BY => ID */
- 26, /* CASCADE => ID */
- 26, /* CAST => ID */
- 26, /* COLUMNKW => ID */
- 26, /* CONFLICT => ID */
- 26, /* DATABASE => ID */
- 26, /* DESC => ID */
- 26, /* DETACH => ID */
- 26, /* EACH => ID */
- 26, /* FAIL => ID */
- 26, /* FOR => ID */
- 26, /* IGNORE => ID */
- 26, /* INITIALLY => ID */
- 26, /* INSTEAD => ID */
- 26, /* LIKE_KW => ID */
- 26, /* MATCH => ID */
- 26, /* NO => ID */
- 26, /* KEY => ID */
- 26, /* OF => ID */
- 26, /* OFFSET => ID */
- 26, /* PRAGMA => ID */
- 26, /* RAISE => ID */
- 26, /* REPLACE => ID */
- 26, /* RESTRICT => ID */
- 26, /* ROW => ID */
- 26, /* TRIGGER => ID */
- 26, /* VACUUM => ID */
- 26, /* VIEW => ID */
- 26, /* VIRTUAL => ID */
- 26, /* REINDEX => ID */
- 26, /* RENAME => ID */
- 26, /* CTIME_KW => ID */
+ 55, /* ABORT => ID */
+ 55, /* ACTION => ID */
+ 55, /* AFTER => ID */
+ 55, /* ANALYZE => ID */
+ 55, /* ASC => ID */
+ 55, /* ATTACH => ID */
+ 55, /* BEFORE => ID */
+ 55, /* BY => ID */
+ 55, /* CASCADE => ID */
+ 55, /* CAST => ID */
+ 55, /* COLUMNKW => ID */
+ 55, /* CONFLICT => ID */
+ 55, /* DATABASE => ID */
+ 55, /* DESC => ID */
+ 55, /* DETACH => ID */
+ 55, /* EACH => ID */
+ 55, /* FAIL => ID */
+ 55, /* FOR => ID */
+ 55, /* IGNORE => ID */
+ 55, /* INITIALLY => ID */
+ 55, /* INSTEAD => ID */
+ 55, /* NO => ID */
+ 55, /* KEY => ID */
+ 55, /* OF => ID */
+ 55, /* OFFSET => ID */
+ 55, /* PRAGMA => ID */
+ 55, /* RAISE => ID */
+ 55, /* RECURSIVE => ID */
+ 55, /* REPLACE => ID */
+ 55, /* RESTRICT => ID */
+ 55, /* ROW => ID */
+ 55, /* TRIGGER => ID */
+ 55, /* VACUUM => ID */
+ 55, /* VIEW => ID */
+ 55, /* VIRTUAL => ID */
+ 55, /* WITH => ID */
+ 55, /* REINDEX => ID */
+ 55, /* RENAME => ID */
+ 55, /* CTIME_KW => ID */
};
#endif /* YYFALLBACK */
@@ -106380,9 +133764,13 @@ static const YYCODETYPE yyFallback[] = {
** + The semantic value stored at this level of the stack. This is
** the information used by the action routines in the grammar.
** It is sometimes called the "minor" token.
+**
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
+** actually contains the reduce action for the second half of the
+** SHIFTREDUCE.
*/
struct yyStackEntry {
- YYACTIONTYPE stateno; /* The state-number */
+ YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
YYCODETYPE major; /* The major token value. This is the code
** number for the token at this stack level */
YYMINORTYPE minor; /* The user-supplied minor token value. This
@@ -106393,15 +133781,18 @@ typedef struct yyStackEntry yyStackEntry;
/* The state of the parser is completely contained in an instance of
** the following structure */
struct yyParser {
- int yyidx; /* Index of top element in stack */
+ yyStackEntry *yytos; /* Pointer to top element of the stack */
#ifdef YYTRACKMAXSTACKDEPTH
- int yyidxMax; /* Maximum value of yyidx */
+ int yyhwm; /* High-water mark of the stack */
#endif
+#ifndef YYNOERRORRECOVERY
int yyerrcnt; /* Shifts left before out of the error */
+#endif
sqlite3ParserARG_SDECL /* A place to hold %extra_argument */
#if YYSTACKDEPTH<=0
int yystksz; /* Current side of the stack */
yyStackEntry *yystack; /* The parser's stack */
+ yyStackEntry yystk0; /* First stack entry */
#else
yyStackEntry yystack[YYSTACKDEPTH]; /* The parser's stack */
#endif
@@ -106450,63 +133841,63 @@ static const char *const yyTokenName[] = {
"ROLLBACK", "SAVEPOINT", "RELEASE", "TO",
"TABLE", "CREATE", "IF", "NOT",
"EXISTS", "TEMP", "LP", "RP",
- "AS", "COMMA", "ID", "INDEXED",
- "ABORT", "ACTION", "AFTER", "ANALYZE",
- "ASC", "ATTACH", "BEFORE", "BY",
- "CASCADE", "CAST", "COLUMNKW", "CONFLICT",
- "DATABASE", "DESC", "DETACH", "EACH",
- "FAIL", "FOR", "IGNORE", "INITIALLY",
- "INSTEAD", "LIKE_KW", "MATCH", "NO",
- "KEY", "OF", "OFFSET", "PRAGMA",
- "RAISE", "REPLACE", "RESTRICT", "ROW",
+ "AS", "WITHOUT", "COMMA", "OR",
+ "AND", "IS", "MATCH", "LIKE_KW",
+ "BETWEEN", "IN", "ISNULL", "NOTNULL",
+ "NE", "EQ", "GT", "LE",
+ "LT", "GE", "ESCAPE", "BITAND",
+ "BITOR", "LSHIFT", "RSHIFT", "PLUS",
+ "MINUS", "STAR", "SLASH", "REM",
+ "CONCAT", "COLLATE", "BITNOT", "ID",
+ "INDEXED", "ABORT", "ACTION", "AFTER",
+ "ANALYZE", "ASC", "ATTACH", "BEFORE",
+ "BY", "CASCADE", "CAST", "COLUMNKW",
+ "CONFLICT", "DATABASE", "DESC", "DETACH",
+ "EACH", "FAIL", "FOR", "IGNORE",
+ "INITIALLY", "INSTEAD", "NO", "KEY",
+ "OF", "OFFSET", "PRAGMA", "RAISE",
+ "RECURSIVE", "REPLACE", "RESTRICT", "ROW",
"TRIGGER", "VACUUM", "VIEW", "VIRTUAL",
- "REINDEX", "RENAME", "CTIME_KW", "ANY",
- "OR", "AND", "IS", "BETWEEN",
- "IN", "ISNULL", "NOTNULL", "NE",
- "EQ", "GT", "LE", "LT",
- "GE", "ESCAPE", "BITAND", "BITOR",
- "LSHIFT", "RSHIFT", "PLUS", "MINUS",
- "STAR", "SLASH", "REM", "CONCAT",
- "COLLATE", "BITNOT", "STRING", "JOIN_KW",
- "CONSTRAINT", "DEFAULT", "NULL", "PRIMARY",
- "UNIQUE", "CHECK", "REFERENCES", "AUTOINCR",
- "ON", "INSERT", "DELETE", "UPDATE",
- "SET", "DEFERRABLE", "FOREIGN", "DROP",
- "UNION", "ALL", "EXCEPT", "INTERSECT",
- "SELECT", "DISTINCT", "DOT", "FROM",
+ "WITH", "REINDEX", "RENAME", "CTIME_KW",
+ "ANY", "STRING", "JOIN_KW", "CONSTRAINT",
+ "DEFAULT", "NULL", "PRIMARY", "UNIQUE",
+ "CHECK", "REFERENCES", "AUTOINCR", "ON",
+ "INSERT", "DELETE", "UPDATE", "SET",
+ "DEFERRABLE", "FOREIGN", "DROP", "UNION",
+ "ALL", "EXCEPT", "INTERSECT", "SELECT",
+ "VALUES", "DISTINCT", "DOT", "FROM",
"JOIN", "USING", "ORDER", "GROUP",
"HAVING", "LIMIT", "WHERE", "INTO",
- "VALUES", "INTEGER", "FLOAT", "BLOB",
- "REGISTER", "VARIABLE", "CASE", "WHEN",
- "THEN", "ELSE", "INDEX", "ALTER",
- "ADD", "error", "input", "cmdlist",
- "ecmd", "explain", "cmdx", "cmd",
- "transtype", "trans_opt", "nm", "savepoint_opt",
- "create_table", "create_table_args", "createkw", "temp",
- "ifnotexists", "dbnm", "columnlist", "conslist_opt",
- "select", "column", "columnid", "type",
- "carglist", "id", "ids", "typetoken",
- "typename", "signed", "plus_num", "minus_num",
- "carg", "ccons", "term", "expr",
- "onconf", "sortorder", "autoinc", "idxlist_opt",
- "refargs", "defer_subclause", "refarg", "refact",
- "init_deferred_pred_opt", "conslist", "tcons", "idxlist",
+ "FLOAT", "BLOB", "INTEGER", "VARIABLE",
+ "CASE", "WHEN", "THEN", "ELSE",
+ "INDEX", "ALTER", "ADD", "error",
+ "input", "cmdlist", "ecmd", "explain",
+ "cmdx", "cmd", "transtype", "trans_opt",
+ "nm", "savepoint_opt", "create_table", "create_table_args",
+ "createkw", "temp", "ifnotexists", "dbnm",
+ "columnlist", "conslist_opt", "table_options", "select",
+ "columnname", "carglist", "typetoken", "typename",
+ "signed", "plus_num", "minus_num", "ccons",
+ "term", "expr", "onconf", "sortorder",
+ "autoinc", "eidlist_opt", "refargs", "defer_subclause",
+ "refarg", "refact", "init_deferred_pred_opt", "conslist",
+ "tconscomma", "tcons", "sortlist", "eidlist",
"defer_subclause_opt", "orconf", "resolvetype", "raisetype",
- "ifexists", "fullname", "oneselect", "multiselect_op",
- "distinct", "selcollist", "from", "where_opt",
- "groupby_opt", "having_opt", "orderby_opt", "limit_opt",
- "sclp", "as", "seltablist", "stl_prefix",
- "joinop", "indexed_opt", "on_opt", "using_opt",
- "joinop2", "inscollist", "sortlist", "sortitem",
- "nexprlist", "setlist", "insert_cmd", "inscollist_opt",
- "itemlist", "exprlist", "likeop", "between_op",
- "in_op", "case_operand", "case_exprlist", "case_else",
- "uniqueflag", "collate", "nmnum", "plus_opt",
- "number", "trigger_decl", "trigger_cmd_list", "trigger_time",
- "trigger_event", "foreach_clause", "when_clause", "trigger_cmd",
- "trnm", "tridxby", "database_kw_opt", "key_opt",
- "add_column_fullname", "kwcolumn_opt", "create_vtab", "vtabarglist",
- "vtabarg", "vtabargtoken", "lp", "anylist",
+ "ifexists", "fullname", "selectnowith", "oneselect",
+ "with", "multiselect_op", "distinct", "selcollist",
+ "from", "where_opt", "groupby_opt", "having_opt",
+ "orderby_opt", "limit_opt", "values", "nexprlist",
+ "exprlist", "sclp", "as", "seltablist",
+ "stl_prefix", "joinop", "indexed_opt", "on_opt",
+ "using_opt", "idlist", "setlist", "insert_cmd",
+ "idlist_opt", "likeop", "between_op", "in_op",
+ "paren_exprlist", "case_operand", "case_exprlist", "case_else",
+ "uniqueflag", "collate", "nmnum", "trigger_decl",
+ "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause",
+ "when_clause", "trigger_cmd", "trnm", "tridxby",
+ "database_kw_opt", "key_opt", "add_column_fullname", "kwcolumn_opt",
+ "create_vtab", "vtabarglist", "vtabarg", "vtabargtoken",
+ "lp", "anylist", "wqlist",
};
#endif /* NDEBUG */
@@ -106514,362 +133905,384 @@ static const char *const yyTokenName[] = {
/* For tracing reduce actions, the names of all rules are required.
*/
static const char *const yyRuleName[] = {
- /* 0 */ "input ::= cmdlist",
- /* 1 */ "cmdlist ::= cmdlist ecmd",
- /* 2 */ "cmdlist ::= ecmd",
- /* 3 */ "ecmd ::= SEMI",
- /* 4 */ "ecmd ::= explain cmdx SEMI",
- /* 5 */ "explain ::=",
- /* 6 */ "explain ::= EXPLAIN",
- /* 7 */ "explain ::= EXPLAIN QUERY PLAN",
- /* 8 */ "cmdx ::= cmd",
- /* 9 */ "cmd ::= BEGIN transtype trans_opt",
- /* 10 */ "trans_opt ::=",
- /* 11 */ "trans_opt ::= TRANSACTION",
- /* 12 */ "trans_opt ::= TRANSACTION nm",
- /* 13 */ "transtype ::=",
- /* 14 */ "transtype ::= DEFERRED",
- /* 15 */ "transtype ::= IMMEDIATE",
- /* 16 */ "transtype ::= EXCLUSIVE",
- /* 17 */ "cmd ::= COMMIT trans_opt",
- /* 18 */ "cmd ::= END trans_opt",
- /* 19 */ "cmd ::= ROLLBACK trans_opt",
- /* 20 */ "savepoint_opt ::= SAVEPOINT",
- /* 21 */ "savepoint_opt ::=",
- /* 22 */ "cmd ::= SAVEPOINT nm",
- /* 23 */ "cmd ::= RELEASE savepoint_opt nm",
- /* 24 */ "cmd ::= ROLLBACK trans_opt TO savepoint_opt nm",
- /* 25 */ "cmd ::= create_table create_table_args",
- /* 26 */ "create_table ::= createkw temp TABLE ifnotexists nm dbnm",
- /* 27 */ "createkw ::= CREATE",
- /* 28 */ "ifnotexists ::=",
- /* 29 */ "ifnotexists ::= IF NOT EXISTS",
- /* 30 */ "temp ::= TEMP",
- /* 31 */ "temp ::=",
- /* 32 */ "create_table_args ::= LP columnlist conslist_opt RP",
- /* 33 */ "create_table_args ::= AS select",
- /* 34 */ "columnlist ::= columnlist COMMA column",
- /* 35 */ "columnlist ::= column",
- /* 36 */ "column ::= columnid type carglist",
- /* 37 */ "columnid ::= nm",
- /* 38 */ "id ::= ID",
- /* 39 */ "id ::= INDEXED",
- /* 40 */ "ids ::= ID|STRING",
- /* 41 */ "nm ::= id",
- /* 42 */ "nm ::= STRING",
- /* 43 */ "nm ::= JOIN_KW",
- /* 44 */ "type ::=",
- /* 45 */ "type ::= typetoken",
- /* 46 */ "typetoken ::= typename",
- /* 47 */ "typetoken ::= typename LP signed RP",
- /* 48 */ "typetoken ::= typename LP signed COMMA signed RP",
- /* 49 */ "typename ::= ids",
- /* 50 */ "typename ::= typename ids",
- /* 51 */ "signed ::= plus_num",
- /* 52 */ "signed ::= minus_num",
- /* 53 */ "carglist ::= carglist carg",
- /* 54 */ "carglist ::=",
- /* 55 */ "carg ::= CONSTRAINT nm ccons",
- /* 56 */ "carg ::= ccons",
- /* 57 */ "ccons ::= DEFAULT term",
- /* 58 */ "ccons ::= DEFAULT LP expr RP",
- /* 59 */ "ccons ::= DEFAULT PLUS term",
- /* 60 */ "ccons ::= DEFAULT MINUS term",
- /* 61 */ "ccons ::= DEFAULT id",
- /* 62 */ "ccons ::= NULL onconf",
- /* 63 */ "ccons ::= NOT NULL onconf",
- /* 64 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
- /* 65 */ "ccons ::= UNIQUE onconf",
- /* 66 */ "ccons ::= CHECK LP expr RP",
- /* 67 */ "ccons ::= REFERENCES nm idxlist_opt refargs",
- /* 68 */ "ccons ::= defer_subclause",
- /* 69 */ "ccons ::= COLLATE ids",
- /* 70 */ "autoinc ::=",
- /* 71 */ "autoinc ::= AUTOINCR",
- /* 72 */ "refargs ::=",
- /* 73 */ "refargs ::= refargs refarg",
- /* 74 */ "refarg ::= MATCH nm",
- /* 75 */ "refarg ::= ON INSERT refact",
- /* 76 */ "refarg ::= ON DELETE refact",
- /* 77 */ "refarg ::= ON UPDATE refact",
- /* 78 */ "refact ::= SET NULL",
- /* 79 */ "refact ::= SET DEFAULT",
- /* 80 */ "refact ::= CASCADE",
- /* 81 */ "refact ::= RESTRICT",
- /* 82 */ "refact ::= NO ACTION",
- /* 83 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
- /* 84 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
- /* 85 */ "init_deferred_pred_opt ::=",
- /* 86 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
- /* 87 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
- /* 88 */ "conslist_opt ::=",
- /* 89 */ "conslist_opt ::= COMMA conslist",
- /* 90 */ "conslist ::= conslist COMMA tcons",
- /* 91 */ "conslist ::= conslist tcons",
- /* 92 */ "conslist ::= tcons",
- /* 93 */ "tcons ::= CONSTRAINT nm",
- /* 94 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf",
- /* 95 */ "tcons ::= UNIQUE LP idxlist RP onconf",
- /* 96 */ "tcons ::= CHECK LP expr RP onconf",
- /* 97 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt",
- /* 98 */ "defer_subclause_opt ::=",
- /* 99 */ "defer_subclause_opt ::= defer_subclause",
- /* 100 */ "onconf ::=",
- /* 101 */ "onconf ::= ON CONFLICT resolvetype",
- /* 102 */ "orconf ::=",
- /* 103 */ "orconf ::= OR resolvetype",
- /* 104 */ "resolvetype ::= raisetype",
- /* 105 */ "resolvetype ::= IGNORE",
- /* 106 */ "resolvetype ::= REPLACE",
- /* 107 */ "cmd ::= DROP TABLE ifexists fullname",
- /* 108 */ "ifexists ::= IF EXISTS",
- /* 109 */ "ifexists ::=",
- /* 110 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select",
- /* 111 */ "cmd ::= DROP VIEW ifexists fullname",
- /* 112 */ "cmd ::= select",
- /* 113 */ "select ::= oneselect",
- /* 114 */ "select ::= select multiselect_op oneselect",
- /* 115 */ "multiselect_op ::= UNION",
- /* 116 */ "multiselect_op ::= UNION ALL",
- /* 117 */ "multiselect_op ::= EXCEPT|INTERSECT",
- /* 118 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
- /* 119 */ "distinct ::= DISTINCT",
- /* 120 */ "distinct ::= ALL",
- /* 121 */ "distinct ::=",
- /* 122 */ "sclp ::= selcollist COMMA",
- /* 123 */ "sclp ::=",
- /* 124 */ "selcollist ::= sclp expr as",
- /* 125 */ "selcollist ::= sclp STAR",
- /* 126 */ "selcollist ::= sclp nm DOT STAR",
- /* 127 */ "as ::= AS nm",
- /* 128 */ "as ::= ids",
- /* 129 */ "as ::=",
- /* 130 */ "from ::=",
- /* 131 */ "from ::= FROM seltablist",
- /* 132 */ "stl_prefix ::= seltablist joinop",
- /* 133 */ "stl_prefix ::=",
- /* 134 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt",
- /* 135 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt",
- /* 136 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt",
- /* 137 */ "dbnm ::=",
- /* 138 */ "dbnm ::= DOT nm",
- /* 139 */ "fullname ::= nm dbnm",
- /* 140 */ "joinop ::= COMMA|JOIN",
- /* 141 */ "joinop ::= JOIN_KW JOIN",
- /* 142 */ "joinop ::= JOIN_KW nm JOIN",
- /* 143 */ "joinop ::= JOIN_KW nm nm JOIN",
- /* 144 */ "on_opt ::= ON expr",
- /* 145 */ "on_opt ::=",
- /* 146 */ "indexed_opt ::=",
- /* 147 */ "indexed_opt ::= INDEXED BY nm",
- /* 148 */ "indexed_opt ::= NOT INDEXED",
- /* 149 */ "using_opt ::= USING LP inscollist RP",
- /* 150 */ "using_opt ::=",
- /* 151 */ "orderby_opt ::=",
- /* 152 */ "orderby_opt ::= ORDER BY sortlist",
- /* 153 */ "sortlist ::= sortlist COMMA sortitem sortorder",
- /* 154 */ "sortlist ::= sortitem sortorder",
- /* 155 */ "sortitem ::= expr",
- /* 156 */ "sortorder ::= ASC",
- /* 157 */ "sortorder ::= DESC",
- /* 158 */ "sortorder ::=",
- /* 159 */ "groupby_opt ::=",
- /* 160 */ "groupby_opt ::= GROUP BY nexprlist",
- /* 161 */ "having_opt ::=",
- /* 162 */ "having_opt ::= HAVING expr",
- /* 163 */ "limit_opt ::=",
- /* 164 */ "limit_opt ::= LIMIT expr",
- /* 165 */ "limit_opt ::= LIMIT expr OFFSET expr",
- /* 166 */ "limit_opt ::= LIMIT expr COMMA expr",
- /* 167 */ "cmd ::= DELETE FROM fullname indexed_opt where_opt",
- /* 168 */ "where_opt ::=",
- /* 169 */ "where_opt ::= WHERE expr",
- /* 170 */ "cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt",
- /* 171 */ "setlist ::= setlist COMMA nm EQ expr",
- /* 172 */ "setlist ::= nm EQ expr",
- /* 173 */ "cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP",
- /* 174 */ "cmd ::= insert_cmd INTO fullname inscollist_opt select",
- /* 175 */ "cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES",
- /* 176 */ "insert_cmd ::= INSERT orconf",
- /* 177 */ "insert_cmd ::= REPLACE",
- /* 178 */ "itemlist ::= itemlist COMMA expr",
- /* 179 */ "itemlist ::= expr",
- /* 180 */ "inscollist_opt ::=",
- /* 181 */ "inscollist_opt ::= LP inscollist RP",
- /* 182 */ "inscollist ::= inscollist COMMA nm",
- /* 183 */ "inscollist ::= nm",
- /* 184 */ "expr ::= term",
- /* 185 */ "expr ::= LP expr RP",
- /* 186 */ "term ::= NULL",
- /* 187 */ "expr ::= id",
- /* 188 */ "expr ::= JOIN_KW",
- /* 189 */ "expr ::= nm DOT nm",
- /* 190 */ "expr ::= nm DOT nm DOT nm",
- /* 191 */ "term ::= INTEGER|FLOAT|BLOB",
- /* 192 */ "term ::= STRING",
- /* 193 */ "expr ::= REGISTER",
- /* 194 */ "expr ::= VARIABLE",
- /* 195 */ "expr ::= expr COLLATE ids",
- /* 196 */ "expr ::= CAST LP expr AS typetoken RP",
- /* 197 */ "expr ::= ID LP distinct exprlist RP",
- /* 198 */ "expr ::= ID LP STAR RP",
- /* 199 */ "term ::= CTIME_KW",
- /* 200 */ "expr ::= expr AND expr",
- /* 201 */ "expr ::= expr OR expr",
- /* 202 */ "expr ::= expr LT|GT|GE|LE expr",
- /* 203 */ "expr ::= expr EQ|NE expr",
- /* 204 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
- /* 205 */ "expr ::= expr PLUS|MINUS expr",
- /* 206 */ "expr ::= expr STAR|SLASH|REM expr",
- /* 207 */ "expr ::= expr CONCAT expr",
- /* 208 */ "likeop ::= LIKE_KW",
- /* 209 */ "likeop ::= NOT LIKE_KW",
- /* 210 */ "likeop ::= MATCH",
- /* 211 */ "likeop ::= NOT MATCH",
- /* 212 */ "expr ::= expr likeop expr",
- /* 213 */ "expr ::= expr likeop expr ESCAPE expr",
- /* 214 */ "expr ::= expr ISNULL|NOTNULL",
- /* 215 */ "expr ::= expr NOT NULL",
- /* 216 */ "expr ::= expr IS expr",
- /* 217 */ "expr ::= expr IS NOT expr",
- /* 218 */ "expr ::= NOT expr",
- /* 219 */ "expr ::= BITNOT expr",
- /* 220 */ "expr ::= MINUS expr",
- /* 221 */ "expr ::= PLUS expr",
- /* 222 */ "between_op ::= BETWEEN",
- /* 223 */ "between_op ::= NOT BETWEEN",
- /* 224 */ "expr ::= expr between_op expr AND expr",
- /* 225 */ "in_op ::= IN",
- /* 226 */ "in_op ::= NOT IN",
- /* 227 */ "expr ::= expr in_op LP exprlist RP",
- /* 228 */ "expr ::= LP select RP",
- /* 229 */ "expr ::= expr in_op LP select RP",
- /* 230 */ "expr ::= expr in_op nm dbnm",
- /* 231 */ "expr ::= EXISTS LP select RP",
- /* 232 */ "expr ::= CASE case_operand case_exprlist case_else END",
- /* 233 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
- /* 234 */ "case_exprlist ::= WHEN expr THEN expr",
- /* 235 */ "case_else ::= ELSE expr",
- /* 236 */ "case_else ::=",
- /* 237 */ "case_operand ::= expr",
- /* 238 */ "case_operand ::=",
- /* 239 */ "exprlist ::= nexprlist",
- /* 240 */ "exprlist ::=",
- /* 241 */ "nexprlist ::= nexprlist COMMA expr",
- /* 242 */ "nexprlist ::= expr",
- /* 243 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP",
- /* 244 */ "uniqueflag ::= UNIQUE",
- /* 245 */ "uniqueflag ::=",
- /* 246 */ "idxlist_opt ::=",
- /* 247 */ "idxlist_opt ::= LP idxlist RP",
- /* 248 */ "idxlist ::= idxlist COMMA nm collate sortorder",
- /* 249 */ "idxlist ::= nm collate sortorder",
- /* 250 */ "collate ::=",
- /* 251 */ "collate ::= COLLATE ids",
- /* 252 */ "cmd ::= DROP INDEX ifexists fullname",
- /* 253 */ "cmd ::= VACUUM",
- /* 254 */ "cmd ::= VACUUM nm",
- /* 255 */ "cmd ::= PRAGMA nm dbnm",
- /* 256 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
- /* 257 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
- /* 258 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
- /* 259 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
- /* 260 */ "nmnum ::= plus_num",
- /* 261 */ "nmnum ::= nm",
- /* 262 */ "nmnum ::= ON",
- /* 263 */ "nmnum ::= DELETE",
- /* 264 */ "nmnum ::= DEFAULT",
- /* 265 */ "plus_num ::= plus_opt number",
- /* 266 */ "minus_num ::= MINUS number",
- /* 267 */ "number ::= INTEGER|FLOAT",
- /* 268 */ "plus_opt ::= PLUS",
- /* 269 */ "plus_opt ::=",
- /* 270 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
- /* 271 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
- /* 272 */ "trigger_time ::= BEFORE",
- /* 273 */ "trigger_time ::= AFTER",
- /* 274 */ "trigger_time ::= INSTEAD OF",
- /* 275 */ "trigger_time ::=",
- /* 276 */ "trigger_event ::= DELETE|INSERT",
- /* 277 */ "trigger_event ::= UPDATE",
- /* 278 */ "trigger_event ::= UPDATE OF inscollist",
- /* 279 */ "foreach_clause ::=",
- /* 280 */ "foreach_clause ::= FOR EACH ROW",
- /* 281 */ "when_clause ::=",
- /* 282 */ "when_clause ::= WHEN expr",
- /* 283 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
- /* 284 */ "trigger_cmd_list ::= trigger_cmd SEMI",
- /* 285 */ "trnm ::= nm",
- /* 286 */ "trnm ::= nm DOT nm",
- /* 287 */ "tridxby ::=",
- /* 288 */ "tridxby ::= INDEXED BY nm",
- /* 289 */ "tridxby ::= NOT INDEXED",
- /* 290 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt",
- /* 291 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt VALUES LP itemlist RP",
- /* 292 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select",
- /* 293 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt",
- /* 294 */ "trigger_cmd ::= select",
- /* 295 */ "expr ::= RAISE LP IGNORE RP",
- /* 296 */ "expr ::= RAISE LP raisetype COMMA nm RP",
- /* 297 */ "raisetype ::= ROLLBACK",
- /* 298 */ "raisetype ::= ABORT",
- /* 299 */ "raisetype ::= FAIL",
- /* 300 */ "cmd ::= DROP TRIGGER ifexists fullname",
- /* 301 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
- /* 302 */ "cmd ::= DETACH database_kw_opt expr",
- /* 303 */ "key_opt ::=",
- /* 304 */ "key_opt ::= KEY expr",
- /* 305 */ "database_kw_opt ::= DATABASE",
- /* 306 */ "database_kw_opt ::=",
- /* 307 */ "cmd ::= REINDEX",
- /* 308 */ "cmd ::= REINDEX nm dbnm",
- /* 309 */ "cmd ::= ANALYZE",
- /* 310 */ "cmd ::= ANALYZE nm dbnm",
- /* 311 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
- /* 312 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column",
- /* 313 */ "add_column_fullname ::= fullname",
- /* 314 */ "kwcolumn_opt ::=",
- /* 315 */ "kwcolumn_opt ::= COLUMNKW",
- /* 316 */ "cmd ::= create_vtab",
- /* 317 */ "cmd ::= create_vtab LP vtabarglist RP",
- /* 318 */ "create_vtab ::= createkw VIRTUAL TABLE nm dbnm USING nm",
- /* 319 */ "vtabarglist ::= vtabarg",
- /* 320 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
- /* 321 */ "vtabarg ::=",
- /* 322 */ "vtabarg ::= vtabarg vtabargtoken",
- /* 323 */ "vtabargtoken ::= ANY",
- /* 324 */ "vtabargtoken ::= lp anylist RP",
- /* 325 */ "lp ::= LP",
- /* 326 */ "anylist ::=",
- /* 327 */ "anylist ::= anylist LP anylist RP",
- /* 328 */ "anylist ::= anylist ANY",
+ /* 0 */ "explain ::= EXPLAIN",
+ /* 1 */ "explain ::= EXPLAIN QUERY PLAN",
+ /* 2 */ "cmdx ::= cmd",
+ /* 3 */ "cmd ::= BEGIN transtype trans_opt",
+ /* 4 */ "transtype ::=",
+ /* 5 */ "transtype ::= DEFERRED",
+ /* 6 */ "transtype ::= IMMEDIATE",
+ /* 7 */ "transtype ::= EXCLUSIVE",
+ /* 8 */ "cmd ::= COMMIT trans_opt",
+ /* 9 */ "cmd ::= END trans_opt",
+ /* 10 */ "cmd ::= ROLLBACK trans_opt",
+ /* 11 */ "cmd ::= SAVEPOINT nm",
+ /* 12 */ "cmd ::= RELEASE savepoint_opt nm",
+ /* 13 */ "cmd ::= ROLLBACK trans_opt TO savepoint_opt nm",
+ /* 14 */ "create_table ::= createkw temp TABLE ifnotexists nm dbnm",
+ /* 15 */ "createkw ::= CREATE",
+ /* 16 */ "ifnotexists ::=",
+ /* 17 */ "ifnotexists ::= IF NOT EXISTS",
+ /* 18 */ "temp ::= TEMP",
+ /* 19 */ "temp ::=",
+ /* 20 */ "create_table_args ::= LP columnlist conslist_opt RP table_options",
+ /* 21 */ "create_table_args ::= AS select",
+ /* 22 */ "table_options ::=",
+ /* 23 */ "table_options ::= WITHOUT nm",
+ /* 24 */ "columnname ::= nm typetoken",
+ /* 25 */ "typetoken ::=",
+ /* 26 */ "typetoken ::= typename LP signed RP",
+ /* 27 */ "typetoken ::= typename LP signed COMMA signed RP",
+ /* 28 */ "typename ::= typename ID|STRING",
+ /* 29 */ "ccons ::= CONSTRAINT nm",
+ /* 30 */ "ccons ::= DEFAULT term",
+ /* 31 */ "ccons ::= DEFAULT LP expr RP",
+ /* 32 */ "ccons ::= DEFAULT PLUS term",
+ /* 33 */ "ccons ::= DEFAULT MINUS term",
+ /* 34 */ "ccons ::= DEFAULT ID|INDEXED",
+ /* 35 */ "ccons ::= NOT NULL onconf",
+ /* 36 */ "ccons ::= PRIMARY KEY sortorder onconf autoinc",
+ /* 37 */ "ccons ::= UNIQUE onconf",
+ /* 38 */ "ccons ::= CHECK LP expr RP",
+ /* 39 */ "ccons ::= REFERENCES nm eidlist_opt refargs",
+ /* 40 */ "ccons ::= defer_subclause",
+ /* 41 */ "ccons ::= COLLATE ID|STRING",
+ /* 42 */ "autoinc ::=",
+ /* 43 */ "autoinc ::= AUTOINCR",
+ /* 44 */ "refargs ::=",
+ /* 45 */ "refargs ::= refargs refarg",
+ /* 46 */ "refarg ::= MATCH nm",
+ /* 47 */ "refarg ::= ON INSERT refact",
+ /* 48 */ "refarg ::= ON DELETE refact",
+ /* 49 */ "refarg ::= ON UPDATE refact",
+ /* 50 */ "refact ::= SET NULL",
+ /* 51 */ "refact ::= SET DEFAULT",
+ /* 52 */ "refact ::= CASCADE",
+ /* 53 */ "refact ::= RESTRICT",
+ /* 54 */ "refact ::= NO ACTION",
+ /* 55 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt",
+ /* 56 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt",
+ /* 57 */ "init_deferred_pred_opt ::=",
+ /* 58 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED",
+ /* 59 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE",
+ /* 60 */ "conslist_opt ::=",
+ /* 61 */ "tconscomma ::= COMMA",
+ /* 62 */ "tcons ::= CONSTRAINT nm",
+ /* 63 */ "tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf",
+ /* 64 */ "tcons ::= UNIQUE LP sortlist RP onconf",
+ /* 65 */ "tcons ::= CHECK LP expr RP onconf",
+ /* 66 */ "tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt",
+ /* 67 */ "defer_subclause_opt ::=",
+ /* 68 */ "onconf ::=",
+ /* 69 */ "onconf ::= ON CONFLICT resolvetype",
+ /* 70 */ "orconf ::=",
+ /* 71 */ "orconf ::= OR resolvetype",
+ /* 72 */ "resolvetype ::= IGNORE",
+ /* 73 */ "resolvetype ::= REPLACE",
+ /* 74 */ "cmd ::= DROP TABLE ifexists fullname",
+ /* 75 */ "ifexists ::= IF EXISTS",
+ /* 76 */ "ifexists ::=",
+ /* 77 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select",
+ /* 78 */ "cmd ::= DROP VIEW ifexists fullname",
+ /* 79 */ "cmd ::= select",
+ /* 80 */ "select ::= with selectnowith",
+ /* 81 */ "selectnowith ::= selectnowith multiselect_op oneselect",
+ /* 82 */ "multiselect_op ::= UNION",
+ /* 83 */ "multiselect_op ::= UNION ALL",
+ /* 84 */ "multiselect_op ::= EXCEPT|INTERSECT",
+ /* 85 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt",
+ /* 86 */ "values ::= VALUES LP nexprlist RP",
+ /* 87 */ "values ::= values COMMA LP exprlist RP",
+ /* 88 */ "distinct ::= DISTINCT",
+ /* 89 */ "distinct ::= ALL",
+ /* 90 */ "distinct ::=",
+ /* 91 */ "sclp ::=",
+ /* 92 */ "selcollist ::= sclp expr as",
+ /* 93 */ "selcollist ::= sclp STAR",
+ /* 94 */ "selcollist ::= sclp nm DOT STAR",
+ /* 95 */ "as ::= AS nm",
+ /* 96 */ "as ::=",
+ /* 97 */ "from ::=",
+ /* 98 */ "from ::= FROM seltablist",
+ /* 99 */ "stl_prefix ::= seltablist joinop",
+ /* 100 */ "stl_prefix ::=",
+ /* 101 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt",
+ /* 102 */ "seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt",
+ /* 103 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt",
+ /* 104 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt",
+ /* 105 */ "dbnm ::=",
+ /* 106 */ "dbnm ::= DOT nm",
+ /* 107 */ "fullname ::= nm dbnm",
+ /* 108 */ "joinop ::= COMMA|JOIN",
+ /* 109 */ "joinop ::= JOIN_KW JOIN",
+ /* 110 */ "joinop ::= JOIN_KW nm JOIN",
+ /* 111 */ "joinop ::= JOIN_KW nm nm JOIN",
+ /* 112 */ "on_opt ::= ON expr",
+ /* 113 */ "on_opt ::=",
+ /* 114 */ "indexed_opt ::=",
+ /* 115 */ "indexed_opt ::= INDEXED BY nm",
+ /* 116 */ "indexed_opt ::= NOT INDEXED",
+ /* 117 */ "using_opt ::= USING LP idlist RP",
+ /* 118 */ "using_opt ::=",
+ /* 119 */ "orderby_opt ::=",
+ /* 120 */ "orderby_opt ::= ORDER BY sortlist",
+ /* 121 */ "sortlist ::= sortlist COMMA expr sortorder",
+ /* 122 */ "sortlist ::= expr sortorder",
+ /* 123 */ "sortorder ::= ASC",
+ /* 124 */ "sortorder ::= DESC",
+ /* 125 */ "sortorder ::=",
+ /* 126 */ "groupby_opt ::=",
+ /* 127 */ "groupby_opt ::= GROUP BY nexprlist",
+ /* 128 */ "having_opt ::=",
+ /* 129 */ "having_opt ::= HAVING expr",
+ /* 130 */ "limit_opt ::=",
+ /* 131 */ "limit_opt ::= LIMIT expr",
+ /* 132 */ "limit_opt ::= LIMIT expr OFFSET expr",
+ /* 133 */ "limit_opt ::= LIMIT expr COMMA expr",
+ /* 134 */ "cmd ::= with DELETE FROM fullname indexed_opt where_opt",
+ /* 135 */ "where_opt ::=",
+ /* 136 */ "where_opt ::= WHERE expr",
+ /* 137 */ "cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt",
+ /* 138 */ "setlist ::= setlist COMMA nm EQ expr",
+ /* 139 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
+ /* 140 */ "setlist ::= nm EQ expr",
+ /* 141 */ "setlist ::= LP idlist RP EQ expr",
+ /* 142 */ "cmd ::= with insert_cmd INTO fullname idlist_opt select",
+ /* 143 */ "cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES",
+ /* 144 */ "insert_cmd ::= INSERT orconf",
+ /* 145 */ "insert_cmd ::= REPLACE",
+ /* 146 */ "idlist_opt ::=",
+ /* 147 */ "idlist_opt ::= LP idlist RP",
+ /* 148 */ "idlist ::= idlist COMMA nm",
+ /* 149 */ "idlist ::= nm",
+ /* 150 */ "expr ::= LP expr RP",
+ /* 151 */ "term ::= NULL",
+ /* 152 */ "expr ::= ID|INDEXED",
+ /* 153 */ "expr ::= JOIN_KW",
+ /* 154 */ "expr ::= nm DOT nm",
+ /* 155 */ "expr ::= nm DOT nm DOT nm",
+ /* 156 */ "term ::= FLOAT|BLOB",
+ /* 157 */ "term ::= STRING",
+ /* 158 */ "term ::= INTEGER",
+ /* 159 */ "expr ::= VARIABLE",
+ /* 160 */ "expr ::= expr COLLATE ID|STRING",
+ /* 161 */ "expr ::= CAST LP expr AS typetoken RP",
+ /* 162 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
+ /* 163 */ "expr ::= ID|INDEXED LP STAR RP",
+ /* 164 */ "term ::= CTIME_KW",
+ /* 165 */ "expr ::= LP nexprlist COMMA expr RP",
+ /* 166 */ "expr ::= expr AND expr",
+ /* 167 */ "expr ::= expr OR expr",
+ /* 168 */ "expr ::= expr LT|GT|GE|LE expr",
+ /* 169 */ "expr ::= expr EQ|NE expr",
+ /* 170 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
+ /* 171 */ "expr ::= expr PLUS|MINUS expr",
+ /* 172 */ "expr ::= expr STAR|SLASH|REM expr",
+ /* 173 */ "expr ::= expr CONCAT expr",
+ /* 174 */ "likeop ::= LIKE_KW|MATCH",
+ /* 175 */ "likeop ::= NOT LIKE_KW|MATCH",
+ /* 176 */ "expr ::= expr likeop expr",
+ /* 177 */ "expr ::= expr likeop expr ESCAPE expr",
+ /* 178 */ "expr ::= expr ISNULL|NOTNULL",
+ /* 179 */ "expr ::= expr NOT NULL",
+ /* 180 */ "expr ::= expr IS expr",
+ /* 181 */ "expr ::= expr IS NOT expr",
+ /* 182 */ "expr ::= NOT expr",
+ /* 183 */ "expr ::= BITNOT expr",
+ /* 184 */ "expr ::= MINUS expr",
+ /* 185 */ "expr ::= PLUS expr",
+ /* 186 */ "between_op ::= BETWEEN",
+ /* 187 */ "between_op ::= NOT BETWEEN",
+ /* 188 */ "expr ::= expr between_op expr AND expr",
+ /* 189 */ "in_op ::= IN",
+ /* 190 */ "in_op ::= NOT IN",
+ /* 191 */ "expr ::= expr in_op LP exprlist RP",
+ /* 192 */ "expr ::= LP select RP",
+ /* 193 */ "expr ::= expr in_op LP select RP",
+ /* 194 */ "expr ::= expr in_op nm dbnm paren_exprlist",
+ /* 195 */ "expr ::= EXISTS LP select RP",
+ /* 196 */ "expr ::= CASE case_operand case_exprlist case_else END",
+ /* 197 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
+ /* 198 */ "case_exprlist ::= WHEN expr THEN expr",
+ /* 199 */ "case_else ::= ELSE expr",
+ /* 200 */ "case_else ::=",
+ /* 201 */ "case_operand ::= expr",
+ /* 202 */ "case_operand ::=",
+ /* 203 */ "exprlist ::=",
+ /* 204 */ "nexprlist ::= nexprlist COMMA expr",
+ /* 205 */ "nexprlist ::= expr",
+ /* 206 */ "paren_exprlist ::=",
+ /* 207 */ "paren_exprlist ::= LP exprlist RP",
+ /* 208 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
+ /* 209 */ "uniqueflag ::= UNIQUE",
+ /* 210 */ "uniqueflag ::=",
+ /* 211 */ "eidlist_opt ::=",
+ /* 212 */ "eidlist_opt ::= LP eidlist RP",
+ /* 213 */ "eidlist ::= eidlist COMMA nm collate sortorder",
+ /* 214 */ "eidlist ::= nm collate sortorder",
+ /* 215 */ "collate ::=",
+ /* 216 */ "collate ::= COLLATE ID|STRING",
+ /* 217 */ "cmd ::= DROP INDEX ifexists fullname",
+ /* 218 */ "cmd ::= VACUUM",
+ /* 219 */ "cmd ::= VACUUM nm",
+ /* 220 */ "cmd ::= PRAGMA nm dbnm",
+ /* 221 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
+ /* 222 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
+ /* 223 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
+ /* 224 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
+ /* 225 */ "plus_num ::= PLUS INTEGER|FLOAT",
+ /* 226 */ "minus_num ::= MINUS INTEGER|FLOAT",
+ /* 227 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
+ /* 228 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
+ /* 229 */ "trigger_time ::= BEFORE",
+ /* 230 */ "trigger_time ::= AFTER",
+ /* 231 */ "trigger_time ::= INSTEAD OF",
+ /* 232 */ "trigger_time ::=",
+ /* 233 */ "trigger_event ::= DELETE|INSERT",
+ /* 234 */ "trigger_event ::= UPDATE",
+ /* 235 */ "trigger_event ::= UPDATE OF idlist",
+ /* 236 */ "when_clause ::=",
+ /* 237 */ "when_clause ::= WHEN expr",
+ /* 238 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
+ /* 239 */ "trigger_cmd_list ::= trigger_cmd SEMI",
+ /* 240 */ "trnm ::= nm DOT nm",
+ /* 241 */ "tridxby ::= INDEXED BY nm",
+ /* 242 */ "tridxby ::= NOT INDEXED",
+ /* 243 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt",
+ /* 244 */ "trigger_cmd ::= insert_cmd INTO trnm idlist_opt select",
+ /* 245 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt",
+ /* 246 */ "trigger_cmd ::= select",
+ /* 247 */ "expr ::= RAISE LP IGNORE RP",
+ /* 248 */ "expr ::= RAISE LP raisetype COMMA nm RP",
+ /* 249 */ "raisetype ::= ROLLBACK",
+ /* 250 */ "raisetype ::= ABORT",
+ /* 251 */ "raisetype ::= FAIL",
+ /* 252 */ "cmd ::= DROP TRIGGER ifexists fullname",
+ /* 253 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
+ /* 254 */ "cmd ::= DETACH database_kw_opt expr",
+ /* 255 */ "key_opt ::=",
+ /* 256 */ "key_opt ::= KEY expr",
+ /* 257 */ "cmd ::= REINDEX",
+ /* 258 */ "cmd ::= REINDEX nm dbnm",
+ /* 259 */ "cmd ::= ANALYZE",
+ /* 260 */ "cmd ::= ANALYZE nm dbnm",
+ /* 261 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
+ /* 262 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
+ /* 263 */ "add_column_fullname ::= fullname",
+ /* 264 */ "cmd ::= create_vtab",
+ /* 265 */ "cmd ::= create_vtab LP vtabarglist RP",
+ /* 266 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
+ /* 267 */ "vtabarg ::=",
+ /* 268 */ "vtabargtoken ::= ANY",
+ /* 269 */ "vtabargtoken ::= lp anylist RP",
+ /* 270 */ "lp ::= LP",
+ /* 271 */ "with ::=",
+ /* 272 */ "with ::= WITH wqlist",
+ /* 273 */ "with ::= WITH RECURSIVE wqlist",
+ /* 274 */ "wqlist ::= nm eidlist_opt AS LP select RP",
+ /* 275 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
+ /* 276 */ "input ::= cmdlist",
+ /* 277 */ "cmdlist ::= cmdlist ecmd",
+ /* 278 */ "cmdlist ::= ecmd",
+ /* 279 */ "ecmd ::= SEMI",
+ /* 280 */ "ecmd ::= explain cmdx SEMI",
+ /* 281 */ "explain ::=",
+ /* 282 */ "trans_opt ::=",
+ /* 283 */ "trans_opt ::= TRANSACTION",
+ /* 284 */ "trans_opt ::= TRANSACTION nm",
+ /* 285 */ "savepoint_opt ::= SAVEPOINT",
+ /* 286 */ "savepoint_opt ::=",
+ /* 287 */ "cmd ::= create_table create_table_args",
+ /* 288 */ "columnlist ::= columnlist COMMA columnname carglist",
+ /* 289 */ "columnlist ::= columnname carglist",
+ /* 290 */ "nm ::= ID|INDEXED",
+ /* 291 */ "nm ::= STRING",
+ /* 292 */ "nm ::= JOIN_KW",
+ /* 293 */ "typetoken ::= typename",
+ /* 294 */ "typename ::= ID|STRING",
+ /* 295 */ "signed ::= plus_num",
+ /* 296 */ "signed ::= minus_num",
+ /* 297 */ "carglist ::= carglist ccons",
+ /* 298 */ "carglist ::=",
+ /* 299 */ "ccons ::= NULL onconf",
+ /* 300 */ "conslist_opt ::= COMMA conslist",
+ /* 301 */ "conslist ::= conslist tconscomma tcons",
+ /* 302 */ "conslist ::= tcons",
+ /* 303 */ "tconscomma ::=",
+ /* 304 */ "defer_subclause_opt ::= defer_subclause",
+ /* 305 */ "resolvetype ::= raisetype",
+ /* 306 */ "selectnowith ::= oneselect",
+ /* 307 */ "oneselect ::= values",
+ /* 308 */ "sclp ::= selcollist COMMA",
+ /* 309 */ "as ::= ID|STRING",
+ /* 310 */ "expr ::= term",
+ /* 311 */ "exprlist ::= nexprlist",
+ /* 312 */ "nmnum ::= plus_num",
+ /* 313 */ "nmnum ::= nm",
+ /* 314 */ "nmnum ::= ON",
+ /* 315 */ "nmnum ::= DELETE",
+ /* 316 */ "nmnum ::= DEFAULT",
+ /* 317 */ "plus_num ::= INTEGER|FLOAT",
+ /* 318 */ "foreach_clause ::=",
+ /* 319 */ "foreach_clause ::= FOR EACH ROW",
+ /* 320 */ "trnm ::= nm",
+ /* 321 */ "tridxby ::=",
+ /* 322 */ "database_kw_opt ::= DATABASE",
+ /* 323 */ "database_kw_opt ::=",
+ /* 324 */ "kwcolumn_opt ::=",
+ /* 325 */ "kwcolumn_opt ::= COLUMNKW",
+ /* 326 */ "vtabarglist ::= vtabarg",
+ /* 327 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
+ /* 328 */ "vtabarg ::= vtabarg vtabargtoken",
+ /* 329 */ "anylist ::=",
+ /* 330 */ "anylist ::= anylist LP anylist RP",
+ /* 331 */ "anylist ::= anylist ANY",
};
#endif /* NDEBUG */
#if YYSTACKDEPTH<=0
/*
-** Try to increase the size of the parser stack.
+** Try to increase the size of the parser stack. Return the number
+** of errors. Return 0 on success.
*/
-static void yyGrowStack(yyParser *p){
+static int yyGrowStack(yyParser *p){
int newSize;
+ int idx;
yyStackEntry *pNew;
newSize = p->yystksz*2 + 100;
- pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+ idx = p->yytos ? (int)(p->yytos - p->yystack) : 0;
+ if( p->yystack==&p->yystk0 ){
+ pNew = malloc(newSize*sizeof(pNew[0]));
+ if( pNew ) pNew[0] = p->yystk0;
+ }else{
+ pNew = realloc(p->yystack, newSize*sizeof(pNew[0]));
+ }
if( pNew ){
p->yystack = pNew;
- p->yystksz = newSize;
+ p->yytos = &p->yystack[idx];
#ifndef NDEBUG
if( yyTraceFILE ){
- fprintf(yyTraceFILE,"%sStack grows to %d entries!\n",
- yyTracePrompt, p->yystksz);
+ fprintf(yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+ yyTracePrompt, p->yystksz, newSize);
}
#endif
+ p->yystksz = newSize;
}
+ return pNew==0;
}
#endif
+/* Datatype of the argument to the memory allocated passed as the
+** second argument to sqlite3ParserAlloc() below. This can be changed by
+** putting an appropriate #define in the %include section of the input
+** grammar.
+*/
+#ifndef YYMALLOCARGTYPE
+# define YYMALLOCARGTYPE size_t
+#endif
+
/*
** This function allocates a new parser.
** The only argument is a pointer to a function which works like
@@ -106882,27 +134295,38 @@ static void yyGrowStack(yyParser *p){
** A pointer to a parser. This pointer is used in subsequent calls
** to sqlite3Parser and sqlite3ParserFree.
*/
-SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(size_t)){
+SQLITE_PRIVATE void *sqlite3ParserAlloc(void *(*mallocProc)(YYMALLOCARGTYPE)){
yyParser *pParser;
- pParser = (yyParser*)(*mallocProc)( (size_t)sizeof(yyParser) );
+ pParser = (yyParser*)(*mallocProc)( (YYMALLOCARGTYPE)sizeof(yyParser) );
if( pParser ){
- pParser->yyidx = -1;
#ifdef YYTRACKMAXSTACKDEPTH
- pParser->yyidxMax = 0;
+ pParser->yyhwm = 0;
#endif
#if YYSTACKDEPTH<=0
+ pParser->yytos = NULL;
pParser->yystack = NULL;
pParser->yystksz = 0;
- yyGrowStack(pParser);
+ if( yyGrowStack(pParser) ){
+ pParser->yystack = &pParser->yystk0;
+ pParser->yystksz = 1;
+ }
#endif
+#ifndef YYNOERRORRECOVERY
+ pParser->yyerrcnt = -1;
+#endif
+ pParser->yytos = pParser->yystack;
+ pParser->yystack[0].stateno = 0;
+ pParser->yystack[0].major = 0;
}
return pParser;
}
-/* The following function deletes the value associated with a
-** symbol. The symbol can be either a terminal or nonterminal.
-** "yymajor" is the symbol code, and "yypminor" is a pointer to
-** the value.
+/* The following function deletes the "minor type" or semantic value
+** associated with a symbol. The symbol can be either a terminal
+** or nonterminal. "yymajor" is the symbol code, and "yypminor" is
+** a pointer to the value to be deleted. The code used to do the
+** deletions is derived from the %destructor and/or %token_destructor
+** directives of the input grammar.
*/
static void yy_destructor(
yyParser *yypParser, /* The parser */
@@ -106918,75 +134342,84 @@ static void yy_destructor(
** being destroyed before it is finished parsing.
**
** Note: during a reduce, the only symbols destroyed are those
- ** which appear on the RHS of the rule, but which are not used
+ ** which appear on the RHS of the rule, but which are *not* used
** inside the C code.
*/
- case 160: /* select */
- case 194: /* oneselect */
+/********* Begin destructor definitions ***************************************/
+ case 163: /* select */
+ case 194: /* selectnowith */
+ case 195: /* oneselect */
+ case 206: /* values */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy387));
+sqlite3SelectDelete(pParse->db, (yypminor->yy243));
}
break;
- case 174: /* term */
- case 175: /* expr */
+ case 172: /* term */
+ case 173: /* expr */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy118).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy190).pExpr);
}
break;
- case 179: /* idxlist_opt */
- case 187: /* idxlist */
- case 197: /* selcollist */
- case 200: /* groupby_opt */
- case 202: /* orderby_opt */
- case 204: /* sclp */
- case 214: /* sortlist */
- case 216: /* nexprlist */
- case 217: /* setlist */
- case 220: /* itemlist */
- case 221: /* exprlist */
+ case 177: /* eidlist_opt */
+ case 186: /* sortlist */
+ case 187: /* eidlist */
+ case 199: /* selcollist */
+ case 202: /* groupby_opt */
+ case 204: /* orderby_opt */
+ case 207: /* nexprlist */
+ case 208: /* exprlist */
+ case 209: /* sclp */
+ case 218: /* setlist */
+ case 224: /* paren_exprlist */
case 226: /* case_exprlist */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy322));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy148));
}
break;
case 193: /* fullname */
- case 198: /* from */
- case 206: /* seltablist */
- case 207: /* stl_prefix */
+ case 200: /* from */
+ case 211: /* seltablist */
+ case 212: /* stl_prefix */
+{
+sqlite3SrcListDelete(pParse->db, (yypminor->yy185));
+}
+ break;
+ case 196: /* with */
+ case 250: /* wqlist */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy259));
+sqlite3WithDelete(pParse->db, (yypminor->yy285));
}
break;
- case 199: /* where_opt */
- case 201: /* having_opt */
- case 210: /* on_opt */
- case 215: /* sortitem */
+ case 201: /* where_opt */
+ case 203: /* having_opt */
+ case 215: /* on_opt */
case 225: /* case_operand */
case 227: /* case_else */
- case 238: /* when_clause */
- case 243: /* key_opt */
+ case 236: /* when_clause */
+ case 241: /* key_opt */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy314));
+sqlite3ExprDelete(pParse->db, (yypminor->yy72));
}
break;
- case 211: /* using_opt */
- case 213: /* inscollist */
- case 219: /* inscollist_opt */
+ case 216: /* using_opt */
+ case 217: /* idlist */
+ case 220: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy384));
+sqlite3IdListDelete(pParse->db, (yypminor->yy254));
}
break;
- case 234: /* trigger_cmd_list */
- case 239: /* trigger_cmd */
+ case 232: /* trigger_cmd_list */
+ case 237: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy203));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy145));
}
break;
- case 236: /* trigger_event */
+ case 234: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy90).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy332).b);
}
break;
+/********* End destructor definitions *****************************************/
default: break; /* If no destructor action specified: do nothing */
}
}
@@ -106996,52 +134429,41 @@ sqlite3IdListDelete(pParse->db, (yypminor->yy90).b);
**
** If there is a destructor routine associated with the token which
** is popped from the stack, then call it.
-**
-** Return the major token number for the symbol popped.
*/
-static int yy_pop_parser_stack(yyParser *pParser){
- YYCODETYPE yymajor;
- yyStackEntry *yytos = &pParser->yystack[pParser->yyidx];
-
- /* There is no mechanism by which the parser stack can be popped below
- ** empty in SQLite. */
- if( NEVER(pParser->yyidx<0) ) return 0;
+static void yy_pop_parser_stack(yyParser *pParser){
+ yyStackEntry *yytos;
+ assert( pParser->yytos!=0 );
+ assert( pParser->yytos > pParser->yystack );
+ yytos = pParser->yytos--;
#ifndef NDEBUG
- if( yyTraceFILE && pParser->yyidx>=0 ){
+ if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sPopping %s\n",
yyTracePrompt,
yyTokenName[yytos->major]);
}
#endif
- yymajor = yytos->major;
- yy_destructor(pParser, yymajor, &yytos->minor);
- pParser->yyidx--;
- return yymajor;
+ yy_destructor(pParser, yytos->major, &yytos->minor);
}
/*
-** Deallocate and destroy a parser. Destructors are all called for
+** Deallocate and destroy a parser. Destructors are called for
** all stack elements before shutting the parser down.
**
-** Inputs:
-** <ul>
-** <li> A pointer to the parser. This should be a pointer
-** obtained from sqlite3ParserAlloc.
-** <li> A pointer to a function used to reclaim memory obtained
-** from malloc.
-** </ul>
+** If the YYPARSEFREENEVERNULL macro exists (for example because it
+** is defined in a %include section of the input grammar) then it is
+** assumed that the input pointer is never NULL.
*/
SQLITE_PRIVATE void sqlite3ParserFree(
void *p, /* The parser to be deleted */
void (*freeProc)(void*) /* Function used to reclaim memory */
){
yyParser *pParser = (yyParser*)p;
- /* In SQLite, we never try to destroy a parser that was not successfully
- ** created in the first place. */
- if( NEVER(pParser==0) ) return;
- while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser);
+#ifndef YYPARSEFREENEVERNULL
+ if( pParser==0 ) return;
+#endif
+ while( pParser->yytos>pParser->yystack ) yy_pop_parser_stack(pParser);
#if YYSTACKDEPTH<=0
- free(pParser->yystack);
+ if( pParser->yystack!=&pParser->yystk0 ) free(pParser->yystack);
#endif
(*freeProc)((void*)pParser);
}
@@ -107052,33 +134474,28 @@ SQLITE_PRIVATE void sqlite3ParserFree(
#ifdef YYTRACKMAXSTACKDEPTH
SQLITE_PRIVATE int sqlite3ParserStackPeak(void *p){
yyParser *pParser = (yyParser*)p;
- return pParser->yyidxMax;
+ return pParser->yyhwm;
}
#endif
/*
** Find the appropriate action for a parser given the terminal
** look-ahead token iLookAhead.
-**
-** If the look-ahead token is YYNOCODE, then check to see if the action is
-** independent of the look-ahead. If it is, return the action, otherwise
-** return YY_NO_ACTION.
*/
-static int yy_find_shift_action(
+static unsigned int yy_find_shift_action(
yyParser *pParser, /* The parser */
YYCODETYPE iLookAhead /* The look-ahead token */
){
int i;
- int stateno = pParser->yystack[pParser->yyidx].stateno;
+ int stateno = pParser->yytos->stateno;
- if( stateno>YY_SHIFT_COUNT
- || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){
- return yy_default[stateno];
- }
- assert( iLookAhead!=YYNOCODE );
- i += iLookAhead;
- if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
- if( iLookAhead>0 ){
+ if( stateno>=YY_MIN_REDUCE ) return stateno;
+ assert( stateno <= YY_SHIFT_COUNT );
+ do{
+ i = yy_shift_ofst[stateno];
+ assert( iLookAhead!=YYNOCODE );
+ i += iLookAhead;
+ if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){
#ifdef YYFALLBACK
YYCODETYPE iFallback; /* Fallback token */
if( iLookAhead<sizeof(yyFallback)/sizeof(yyFallback[0])
@@ -107089,7 +134506,9 @@ static int yy_find_shift_action(
yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[iFallback]);
}
#endif
- return yy_find_shift_action(pParser, iFallback);
+ assert( yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
+ iLookAhead = iFallback;
+ continue;
}
#endif
#ifdef YYWILDCARD
@@ -107102,32 +134521,29 @@ static int yy_find_shift_action(
#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT
j<YY_ACTTAB_COUNT &&
#endif
- yy_lookahead[j]==YYWILDCARD
+ yy_lookahead[j]==YYWILDCARD && iLookAhead>0
){
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE, "%sWILDCARD %s => %s\n",
- yyTracePrompt, yyTokenName[iLookAhead], yyTokenName[YYWILDCARD]);
+ yyTracePrompt, yyTokenName[iLookAhead],
+ yyTokenName[YYWILDCARD]);
}
#endif /* NDEBUG */
return yy_action[j];
}
}
#endif /* YYWILDCARD */
+ return yy_default[stateno];
+ }else{
+ return yy_action[i];
}
- return yy_default[stateno];
- }else{
- return yy_action[i];
- }
+ }while(1);
}
/*
** Find the appropriate action for a parser given the non-terminal
** look-ahead token iLookAhead.
-**
-** If the look-ahead token is YYNOCODE, then check to see if the action is
-** independent of the look-ahead. If it is, return the action, otherwise
-** return YY_NO_ACTION.
*/
static int yy_find_reduce_action(
int stateno, /* Current state number */
@@ -107159,68 +134575,82 @@ static int yy_find_reduce_action(
/*
** The following routine is called if the stack overflows.
*/
-static void yyStackOverflow(yyParser *yypParser, YYMINORTYPE *yypMinor){
+static void yyStackOverflow(yyParser *yypParser){
sqlite3ParserARG_FETCH;
- yypParser->yyidx--;
+ yypParser->yytos--;
#ifndef NDEBUG
if( yyTraceFILE ){
fprintf(yyTraceFILE,"%sStack Overflow!\n",yyTracePrompt);
}
#endif
- while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will execute if the parser
** stack every overflows */
+/******** Begin %stack_overflow code ******************************************/
- UNUSED_PARAMETER(yypMinor); /* Silence some compiler warnings */
sqlite3ErrorMsg(pParse, "parser stack overflow");
- pParse->parseError = 1;
+/******** End %stack_overflow code ********************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
}
/*
+** Print tracing information for a SHIFT action
+*/
+#ifndef NDEBUG
+static void yyTraceShift(yyParser *yypParser, int yyNewState){
+ if( yyTraceFILE ){
+ if( yyNewState<YYNSTATE ){
+ fprintf(yyTraceFILE,"%sShift '%s', go to state %d\n",
+ yyTracePrompt,yyTokenName[yypParser->yytos->major],
+ yyNewState);
+ }else{
+ fprintf(yyTraceFILE,"%sShift '%s'\n",
+ yyTracePrompt,yyTokenName[yypParser->yytos->major]);
+ }
+ }
+}
+#else
+# define yyTraceShift(X,Y)
+#endif
+
+/*
** Perform a shift action.
*/
static void yy_shift(
yyParser *yypParser, /* The parser to be shifted */
int yyNewState, /* The new state to shift in */
int yyMajor, /* The major token to shift in */
- YYMINORTYPE *yypMinor /* Pointer to the minor token to shift in */
+ sqlite3ParserTOKENTYPE yyMinor /* The minor token to shift in */
){
yyStackEntry *yytos;
- yypParser->yyidx++;
+ yypParser->yytos++;
#ifdef YYTRACKMAXSTACKDEPTH
- if( yypParser->yyidx>yypParser->yyidxMax ){
- yypParser->yyidxMax = yypParser->yyidx;
+ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
+ yypParser->yyhwm++;
+ assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack) );
}
#endif
#if YYSTACKDEPTH>0
- if( yypParser->yyidx>=YYSTACKDEPTH ){
- yyStackOverflow(yypParser, yypMinor);
+ if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH] ){
+ yyStackOverflow(yypParser);
return;
}
#else
- if( yypParser->yyidx>=yypParser->yystksz ){
- yyGrowStack(yypParser);
- if( yypParser->yyidx>=yypParser->yystksz ){
- yyStackOverflow(yypParser, yypMinor);
+ if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz] ){
+ if( yyGrowStack(yypParser) ){
+ yyStackOverflow(yypParser);
return;
}
}
#endif
- yytos = &yypParser->yystack[yypParser->yyidx];
+ if( yyNewState > YY_MAX_SHIFT ){
+ yyNewState += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+ }
+ yytos = yypParser->yytos;
yytos->stateno = (YYACTIONTYPE)yyNewState;
yytos->major = (YYCODETYPE)yyMajor;
- yytos->minor = *yypMinor;
-#ifndef NDEBUG
- if( yyTraceFILE && yypParser->yyidx>0 ){
- int i;
- fprintf(yyTraceFILE,"%sShift %d\n",yyTracePrompt,yyNewState);
- fprintf(yyTraceFILE,"%sStack:",yyTracePrompt);
- for(i=1; i<=yypParser->yyidx; i++)
- fprintf(yyTraceFILE," %s",yyTokenName[yypParser->yystack[i].major]);
- fprintf(yyTraceFILE,"\n");
- }
-#endif
+ yytos->minor.yy0 = yyMinor;
+ yyTraceShift(yypParser, yyNewState);
}
/* The following table contains information about every rule that
@@ -107230,335 +134660,338 @@ static const struct {
YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
unsigned char nrhs; /* Number of right-hand side symbols in the rule */
} yyRuleInfo[] = {
- { 142, 1 },
- { 143, 2 },
- { 143, 1 },
- { 144, 1 },
- { 144, 3 },
- { 145, 0 },
- { 145, 1 },
- { 145, 3 },
- { 146, 1 },
+ { 147, 1 },
{ 147, 3 },
- { 149, 0 },
- { 149, 1 },
- { 149, 2 },
- { 148, 0 },
- { 148, 1 },
{ 148, 1 },
- { 148, 1 },
- { 147, 2 },
- { 147, 2 },
- { 147, 2 },
- { 151, 1 },
- { 151, 0 },
- { 147, 2 },
- { 147, 3 },
- { 147, 5 },
- { 147, 2 },
- { 152, 6 },
- { 154, 1 },
- { 156, 0 },
- { 156, 3 },
- { 155, 1 },
- { 155, 0 },
- { 153, 4 },
- { 153, 2 },
- { 158, 3 },
- { 158, 1 },
- { 161, 3 },
- { 162, 1 },
- { 165, 1 },
- { 165, 1 },
- { 166, 1 },
+ { 149, 3 },
+ { 150, 0 },
{ 150, 1 },
{ 150, 1 },
{ 150, 1 },
- { 163, 0 },
- { 163, 1 },
- { 167, 1 },
- { 167, 4 },
- { 167, 6 },
- { 168, 1 },
- { 168, 2 },
- { 169, 1 },
- { 169, 1 },
+ { 149, 2 },
+ { 149, 2 },
+ { 149, 2 },
+ { 149, 2 },
+ { 149, 3 },
+ { 149, 5 },
+ { 154, 6 },
+ { 156, 1 },
+ { 158, 0 },
+ { 158, 3 },
+ { 157, 1 },
+ { 157, 0 },
+ { 155, 5 },
+ { 155, 2 },
+ { 162, 0 },
+ { 162, 2 },
{ 164, 2 },
- { 164, 0 },
- { 172, 3 },
- { 172, 1 },
- { 173, 2 },
- { 173, 4 },
- { 173, 3 },
- { 173, 3 },
- { 173, 2 },
- { 173, 2 },
- { 173, 3 },
- { 173, 5 },
- { 173, 2 },
- { 173, 4 },
- { 173, 4 },
- { 173, 1 },
- { 173, 2 },
+ { 166, 0 },
+ { 166, 4 },
+ { 166, 6 },
+ { 167, 2 },
+ { 171, 2 },
+ { 171, 2 },
+ { 171, 4 },
+ { 171, 3 },
+ { 171, 3 },
+ { 171, 2 },
+ { 171, 3 },
+ { 171, 5 },
+ { 171, 2 },
+ { 171, 4 },
+ { 171, 4 },
+ { 171, 1 },
+ { 171, 2 },
+ { 176, 0 },
+ { 176, 1 },
{ 178, 0 },
- { 178, 1 },
- { 180, 0 },
+ { 178, 2 },
{ 180, 2 },
- { 182, 2 },
- { 182, 3 },
- { 182, 3 },
- { 182, 3 },
- { 183, 2 },
- { 183, 2 },
- { 183, 1 },
- { 183, 1 },
- { 183, 2 },
- { 181, 3 },
+ { 180, 3 },
+ { 180, 3 },
+ { 180, 3 },
{ 181, 2 },
- { 184, 0 },
- { 184, 2 },
- { 184, 2 },
- { 159, 0 },
- { 159, 2 },
- { 185, 3 },
+ { 181, 2 },
+ { 181, 1 },
+ { 181, 1 },
+ { 181, 2 },
+ { 179, 3 },
+ { 179, 2 },
+ { 182, 0 },
+ { 182, 2 },
+ { 182, 2 },
+ { 161, 0 },
+ { 184, 1 },
{ 185, 2 },
- { 185, 1 },
- { 186, 2 },
- { 186, 7 },
- { 186, 5 },
- { 186, 5 },
- { 186, 10 },
+ { 185, 7 },
+ { 185, 5 },
+ { 185, 5 },
+ { 185, 10 },
{ 188, 0 },
- { 188, 1 },
- { 176, 0 },
- { 176, 3 },
+ { 174, 0 },
+ { 174, 3 },
{ 189, 0 },
{ 189, 2 },
{ 190, 1 },
{ 190, 1 },
- { 190, 1 },
- { 147, 4 },
+ { 149, 4 },
{ 192, 2 },
{ 192, 0 },
- { 147, 8 },
- { 147, 4 },
- { 147, 1 },
- { 160, 1 },
- { 160, 3 },
- { 195, 1 },
- { 195, 2 },
- { 195, 1 },
- { 194, 9 },
- { 196, 1 },
- { 196, 1 },
- { 196, 0 },
- { 204, 2 },
- { 204, 0 },
- { 197, 3 },
+ { 149, 9 },
+ { 149, 4 },
+ { 149, 1 },
+ { 163, 2 },
+ { 194, 3 },
+ { 197, 1 },
{ 197, 2 },
- { 197, 4 },
- { 205, 2 },
- { 205, 1 },
- { 205, 0 },
+ { 197, 1 },
+ { 195, 9 },
+ { 206, 4 },
+ { 206, 5 },
+ { 198, 1 },
+ { 198, 1 },
{ 198, 0 },
- { 198, 2 },
- { 207, 2 },
- { 207, 0 },
- { 206, 7 },
- { 206, 7 },
- { 206, 7 },
- { 157, 0 },
- { 157, 2 },
- { 193, 2 },
- { 208, 1 },
- { 208, 2 },
- { 208, 3 },
- { 208, 4 },
+ { 209, 0 },
+ { 199, 3 },
+ { 199, 2 },
+ { 199, 4 },
{ 210, 2 },
{ 210, 0 },
- { 209, 0 },
- { 209, 3 },
- { 209, 2 },
- { 211, 4 },
- { 211, 0 },
+ { 200, 0 },
+ { 200, 2 },
+ { 212, 2 },
+ { 212, 0 },
+ { 211, 7 },
+ { 211, 9 },
+ { 211, 7 },
+ { 211, 7 },
+ { 159, 0 },
+ { 159, 2 },
+ { 193, 2 },
+ { 213, 1 },
+ { 213, 2 },
+ { 213, 3 },
+ { 213, 4 },
+ { 215, 2 },
+ { 215, 0 },
+ { 214, 0 },
+ { 214, 3 },
+ { 214, 2 },
+ { 216, 4 },
+ { 216, 0 },
+ { 204, 0 },
+ { 204, 3 },
+ { 186, 4 },
+ { 186, 2 },
+ { 175, 1 },
+ { 175, 1 },
+ { 175, 0 },
{ 202, 0 },
{ 202, 3 },
- { 214, 4 },
- { 214, 2 },
- { 215, 1 },
- { 177, 1 },
- { 177, 1 },
- { 177, 0 },
- { 200, 0 },
- { 200, 3 },
- { 201, 0 },
- { 201, 2 },
{ 203, 0 },
{ 203, 2 },
- { 203, 4 },
- { 203, 4 },
- { 147, 5 },
- { 199, 0 },
- { 199, 2 },
- { 147, 7 },
- { 217, 5 },
- { 217, 3 },
- { 147, 8 },
- { 147, 5 },
- { 147, 6 },
- { 218, 2 },
- { 218, 1 },
+ { 205, 0 },
+ { 205, 2 },
+ { 205, 4 },
+ { 205, 4 },
+ { 149, 6 },
+ { 201, 0 },
+ { 201, 2 },
+ { 149, 8 },
+ { 218, 5 },
+ { 218, 7 },
+ { 218, 3 },
+ { 218, 5 },
+ { 149, 6 },
+ { 149, 7 },
+ { 219, 2 },
+ { 219, 1 },
+ { 220, 0 },
{ 220, 3 },
- { 220, 1 },
- { 219, 0 },
- { 219, 3 },
- { 213, 3 },
- { 213, 1 },
- { 175, 1 },
- { 175, 3 },
- { 174, 1 },
- { 175, 1 },
- { 175, 1 },
- { 175, 3 },
- { 175, 5 },
- { 174, 1 },
- { 174, 1 },
- { 175, 1 },
- { 175, 1 },
- { 175, 3 },
- { 175, 6 },
- { 175, 5 },
- { 175, 4 },
- { 174, 1 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 175, 3 },
- { 222, 1 },
- { 222, 2 },
+ { 217, 3 },
+ { 217, 1 },
+ { 173, 3 },
+ { 172, 1 },
+ { 173, 1 },
+ { 173, 1 },
+ { 173, 3 },
+ { 173, 5 },
+ { 172, 1 },
+ { 172, 1 },
+ { 172, 1 },
+ { 173, 1 },
+ { 173, 3 },
+ { 173, 6 },
+ { 173, 5 },
+ { 173, 4 },
+ { 172, 1 },
+ { 173, 5 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 3 },
+ { 221, 1 },
+ { 221, 2 },
+ { 173, 3 },
+ { 173, 5 },
+ { 173, 2 },
+ { 173, 3 },
+ { 173, 3 },
+ { 173, 4 },
+ { 173, 2 },
+ { 173, 2 },
+ { 173, 2 },
+ { 173, 2 },
{ 222, 1 },
{ 222, 2 },
- { 175, 3 },
- { 175, 5 },
- { 175, 2 },
- { 175, 3 },
- { 175, 3 },
- { 175, 4 },
- { 175, 2 },
- { 175, 2 },
- { 175, 2 },
- { 175, 2 },
+ { 173, 5 },
{ 223, 1 },
{ 223, 2 },
- { 175, 5 },
- { 224, 1 },
- { 224, 2 },
- { 175, 5 },
- { 175, 3 },
- { 175, 5 },
- { 175, 4 },
- { 175, 4 },
- { 175, 5 },
+ { 173, 5 },
+ { 173, 3 },
+ { 173, 5 },
+ { 173, 5 },
+ { 173, 4 },
+ { 173, 5 },
{ 226, 5 },
{ 226, 4 },
{ 227, 2 },
{ 227, 0 },
{ 225, 1 },
{ 225, 0 },
- { 221, 1 },
- { 221, 0 },
- { 216, 3 },
- { 216, 1 },
- { 147, 11 },
+ { 208, 0 },
+ { 207, 3 },
+ { 207, 1 },
+ { 224, 0 },
+ { 224, 3 },
+ { 149, 12 },
{ 228, 1 },
{ 228, 0 },
- { 179, 0 },
- { 179, 3 },
+ { 177, 0 },
+ { 177, 3 },
{ 187, 5 },
{ 187, 3 },
{ 229, 0 },
{ 229, 2 },
- { 147, 4 },
- { 147, 1 },
- { 147, 2 },
- { 147, 3 },
- { 147, 5 },
- { 147, 6 },
- { 147, 5 },
- { 147, 6 },
+ { 149, 4 },
+ { 149, 1 },
+ { 149, 2 },
+ { 149, 3 },
+ { 149, 5 },
+ { 149, 6 },
+ { 149, 5 },
+ { 149, 6 },
+ { 169, 2 },
+ { 170, 2 },
+ { 149, 5 },
+ { 231, 11 },
+ { 233, 1 },
+ { 233, 1 },
+ { 233, 2 },
+ { 233, 0 },
+ { 234, 1 },
+ { 234, 1 },
+ { 234, 3 },
+ { 236, 0 },
+ { 236, 2 },
+ { 232, 3 },
+ { 232, 2 },
+ { 238, 3 },
+ { 239, 3 },
+ { 239, 2 },
+ { 237, 7 },
+ { 237, 5 },
+ { 237, 5 },
+ { 237, 1 },
+ { 173, 4 },
+ { 173, 6 },
+ { 191, 1 },
+ { 191, 1 },
+ { 191, 1 },
+ { 149, 4 },
+ { 149, 6 },
+ { 149, 3 },
+ { 241, 0 },
+ { 241, 2 },
+ { 149, 1 },
+ { 149, 3 },
+ { 149, 1 },
+ { 149, 3 },
+ { 149, 6 },
+ { 149, 7 },
+ { 242, 1 },
+ { 149, 1 },
+ { 149, 4 },
+ { 244, 8 },
+ { 246, 0 },
+ { 247, 1 },
+ { 247, 3 },
+ { 248, 1 },
+ { 196, 0 },
+ { 196, 2 },
+ { 196, 3 },
+ { 250, 6 },
+ { 250, 8 },
+ { 144, 1 },
+ { 145, 2 },
+ { 145, 1 },
+ { 146, 1 },
+ { 146, 3 },
+ { 147, 0 },
+ { 151, 0 },
+ { 151, 1 },
+ { 151, 2 },
+ { 153, 1 },
+ { 153, 0 },
+ { 149, 2 },
+ { 160, 4 },
+ { 160, 2 },
+ { 152, 1 },
+ { 152, 1 },
+ { 152, 1 },
+ { 166, 1 },
+ { 167, 1 },
+ { 168, 1 },
+ { 168, 1 },
+ { 165, 2 },
+ { 165, 0 },
+ { 171, 2 },
+ { 161, 2 },
+ { 183, 3 },
+ { 183, 1 },
+ { 184, 0 },
+ { 188, 1 },
+ { 190, 1 },
+ { 194, 1 },
+ { 195, 1 },
+ { 209, 2 },
+ { 210, 1 },
+ { 173, 1 },
+ { 208, 1 },
{ 230, 1 },
{ 230, 1 },
{ 230, 1 },
{ 230, 1 },
{ 230, 1 },
- { 170, 2 },
- { 171, 2 },
- { 232, 1 },
- { 231, 1 },
- { 231, 0 },
- { 147, 5 },
- { 233, 11 },
- { 235, 1 },
- { 235, 1 },
- { 235, 2 },
+ { 169, 1 },
{ 235, 0 },
- { 236, 1 },
- { 236, 1 },
- { 236, 3 },
- { 237, 0 },
- { 237, 3 },
- { 238, 0 },
- { 238, 2 },
- { 234, 3 },
- { 234, 2 },
+ { 235, 3 },
+ { 238, 1 },
+ { 239, 0 },
{ 240, 1 },
- { 240, 3 },
- { 241, 0 },
- { 241, 3 },
- { 241, 2 },
- { 239, 7 },
- { 239, 8 },
- { 239, 5 },
- { 239, 5 },
- { 239, 1 },
- { 175, 4 },
- { 175, 6 },
- { 191, 1 },
- { 191, 1 },
- { 191, 1 },
- { 147, 4 },
- { 147, 6 },
- { 147, 3 },
+ { 240, 0 },
{ 243, 0 },
- { 243, 2 },
- { 242, 1 },
- { 242, 0 },
- { 147, 1 },
- { 147, 3 },
- { 147, 1 },
- { 147, 3 },
- { 147, 6 },
- { 147, 6 },
- { 244, 1 },
- { 245, 0 },
+ { 243, 1 },
{ 245, 1 },
- { 147, 1 },
- { 147, 4 },
- { 246, 7 },
- { 247, 1 },
- { 247, 3 },
- { 248, 0 },
- { 248, 2 },
- { 249, 1 },
- { 249, 3 },
- { 250, 1 },
- { 251, 0 },
- { 251, 4 },
- { 251, 2 },
+ { 245, 3 },
+ { 246, 2 },
+ { 249, 0 },
+ { 249, 4 },
+ { 249, 2 },
};
static void yy_accept(yyParser*); /* Forward Declaration */
@@ -107569,40 +135002,47 @@ static void yy_accept(yyParser*); /* Forward Declaration */
*/
static void yy_reduce(
yyParser *yypParser, /* The parser */
- int yyruleno /* Number of the rule by which to reduce */
+ unsigned int yyruleno /* Number of the rule by which to reduce */
){
int yygoto; /* The next state */
int yyact; /* The next action */
- YYMINORTYPE yygotominor; /* The LHS of the rule reduced */
yyStackEntry *yymsp; /* The top of the parser's stack */
int yysize; /* Amount to pop the stack */
sqlite3ParserARG_FETCH;
- yymsp = &yypParser->yystack[yypParser->yyidx];
+ yymsp = yypParser->yytos;
#ifndef NDEBUG
- if( yyTraceFILE && yyruleno>=0
- && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
- fprintf(yyTraceFILE, "%sReduce [%s].\n", yyTracePrompt,
- yyRuleName[yyruleno]);
+ if( yyTraceFILE && yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) ){
+ yysize = yyRuleInfo[yyruleno].nrhs;
+ fprintf(yyTraceFILE, "%sReduce [%s], go to state %d.\n", yyTracePrompt,
+ yyRuleName[yyruleno], yymsp[-yysize].stateno);
}
#endif /* NDEBUG */
- /* Silence complaints from purify about yygotominor being uninitialized
- ** in some cases when it is copied into the stack after the following
- ** switch. yygotominor is uninitialized when a rule reduces that does
- ** not set the value of its left-hand side nonterminal. Leaving the
- ** value of the nonterminal uninitialized is utterly harmless as long
- ** as the value is never used. So really the only thing this code
- ** accomplishes is to quieten purify.
- **
- ** 2007-01-16: The wireshark project (www.wireshark.org) reports that
- ** without this code, their parser segfaults. I'm not sure what there
- ** parser is doing to make this happen. This is the second bug report
- ** from wireshark this week. Clearly they are stressing Lemon in ways
- ** that it has not been previously stressed... (SQLite ticket #2172)
- */
- /*memset(&yygotominor, 0, sizeof(yygotominor));*/
- yygotominor = yyzerominor;
-
+ /* Check that the stack is large enough to grow by a single entry
+ ** if the RHS of the rule is empty. This ensures that there is room
+ ** enough on the stack to push the LHS value */
+ if( yyRuleInfo[yyruleno].nrhs==0 ){
+#ifdef YYTRACKMAXSTACKDEPTH
+ if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
+ yypParser->yyhwm++;
+ assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
+ }
+#endif
+#if YYSTACKDEPTH>0
+ if( yypParser->yytos>=&yypParser->yystack[YYSTACKDEPTH-1] ){
+ yyStackOverflow(yypParser);
+ return;
+ }
+#else
+ if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
+ if( yyGrowStack(yypParser) ){
+ yyStackOverflow(yypParser);
+ return;
+ }
+ yymsp = yypParser->yytos;
+ }
+#endif
+ }
switch( yyruleno ){
/* Beginning here are the reduction cases. A typical example
@@ -107613,711 +135053,813 @@ static void yy_reduce(
** #line <lineno> <thisfile>
** break;
*/
- case 5: /* explain ::= */
-{ sqlite3BeginParse(pParse, 0); }
+/********** Begin reduce actions **********************************************/
+ YYMINORTYPE yylhsminor;
+ case 0: /* explain ::= EXPLAIN */
+{ pParse->explain = 1; }
break;
- case 6: /* explain ::= EXPLAIN */
-{ sqlite3BeginParse(pParse, 1); }
+ case 1: /* explain ::= EXPLAIN QUERY PLAN */
+{ pParse->explain = 2; }
break;
- case 7: /* explain ::= EXPLAIN QUERY PLAN */
-{ sqlite3BeginParse(pParse, 2); }
- break;
- case 8: /* cmdx ::= cmd */
+ case 2: /* cmdx ::= cmd */
{ sqlite3FinishCoding(pParse); }
break;
- case 9: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy4);}
+ case 3: /* cmd ::= BEGIN transtype trans_opt */
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy194);}
break;
- case 13: /* transtype ::= */
-{yygotominor.yy4 = TK_DEFERRED;}
+ case 4: /* transtype ::= */
+{yymsp[1].minor.yy194 = TK_DEFERRED;}
break;
- case 14: /* transtype ::= DEFERRED */
- case 15: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==15);
- case 16: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==16);
- case 115: /* multiselect_op ::= UNION */ yytestcase(yyruleno==115);
- case 117: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==117);
-{yygotominor.yy4 = yymsp[0].major;}
+ case 5: /* transtype ::= DEFERRED */
+ case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
+ case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
+{yymsp[0].minor.yy194 = yymsp[0].major; /*A-overwrites-X*/}
break;
- case 17: /* cmd ::= COMMIT trans_opt */
- case 18: /* cmd ::= END trans_opt */ yytestcase(yyruleno==18);
+ case 8: /* cmd ::= COMMIT trans_opt */
+ case 9: /* cmd ::= END trans_opt */ yytestcase(yyruleno==9);
{sqlite3CommitTransaction(pParse);}
break;
- case 19: /* cmd ::= ROLLBACK trans_opt */
+ case 10: /* cmd ::= ROLLBACK trans_opt */
{sqlite3RollbackTransaction(pParse);}
break;
- case 22: /* cmd ::= SAVEPOINT nm */
+ case 11: /* cmd ::= SAVEPOINT nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_BEGIN, &yymsp[0].minor.yy0);
}
break;
- case 23: /* cmd ::= RELEASE savepoint_opt nm */
+ case 12: /* cmd ::= RELEASE savepoint_opt nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_RELEASE, &yymsp[0].minor.yy0);
}
break;
- case 24: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ case 13: /* cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
{
sqlite3Savepoint(pParse, SAVEPOINT_ROLLBACK, &yymsp[0].minor.yy0);
}
break;
- case 26: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ case 14: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy4,0,0,yymsp[-2].minor.yy4);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy194,0,0,yymsp[-2].minor.yy194);
}
break;
- case 27: /* createkw ::= CREATE */
-{
- pParse->db->lookaside.bEnabled = 0;
- yygotominor.yy0 = yymsp[0].minor.yy0;
-}
+ case 15: /* createkw ::= CREATE */
+{disableLookaside(pParse);}
break;
- case 28: /* ifnotexists ::= */
- case 31: /* temp ::= */ yytestcase(yyruleno==31);
- case 70: /* autoinc ::= */ yytestcase(yyruleno==70);
- case 83: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==83);
- case 85: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==85);
- case 87: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */ yytestcase(yyruleno==87);
- case 98: /* defer_subclause_opt ::= */ yytestcase(yyruleno==98);
- case 109: /* ifexists ::= */ yytestcase(yyruleno==109);
- case 120: /* distinct ::= ALL */ yytestcase(yyruleno==120);
- case 121: /* distinct ::= */ yytestcase(yyruleno==121);
- case 222: /* between_op ::= BETWEEN */ yytestcase(yyruleno==222);
- case 225: /* in_op ::= IN */ yytestcase(yyruleno==225);
-{yygotominor.yy4 = 0;}
+ case 16: /* ifnotexists ::= */
+ case 19: /* temp ::= */ yytestcase(yyruleno==19);
+ case 22: /* table_options ::= */ yytestcase(yyruleno==22);
+ case 42: /* autoinc ::= */ yytestcase(yyruleno==42);
+ case 57: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==57);
+ case 67: /* defer_subclause_opt ::= */ yytestcase(yyruleno==67);
+ case 76: /* ifexists ::= */ yytestcase(yyruleno==76);
+ case 90: /* distinct ::= */ yytestcase(yyruleno==90);
+ case 215: /* collate ::= */ yytestcase(yyruleno==215);
+{yymsp[1].minor.yy194 = 0;}
break;
- case 29: /* ifnotexists ::= IF NOT EXISTS */
- case 30: /* temp ::= TEMP */ yytestcase(yyruleno==30);
- case 71: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==71);
- case 86: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */ yytestcase(yyruleno==86);
- case 108: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==108);
- case 119: /* distinct ::= DISTINCT */ yytestcase(yyruleno==119);
- case 223: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==223);
- case 226: /* in_op ::= NOT IN */ yytestcase(yyruleno==226);
-{yygotominor.yy4 = 1;}
+ case 17: /* ifnotexists ::= IF NOT EXISTS */
+{yymsp[-2].minor.yy194 = 1;}
break;
- case 32: /* create_table_args ::= LP columnlist conslist_opt RP */
-{
- sqlite3EndTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0);
-}
+ case 18: /* temp ::= TEMP */
+ case 43: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==43);
+{yymsp[0].minor.yy194 = 1;}
break;
- case 33: /* create_table_args ::= AS select */
+ case 20: /* create_table_args ::= LP columnlist conslist_opt RP table_options */
{
- sqlite3EndTable(pParse,0,0,yymsp[0].minor.yy387);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy387);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy194,0);
}
break;
- case 36: /* column ::= columnid type carglist */
+ case 21: /* create_table_args ::= AS select */
{
- yygotominor.yy0.z = yymsp[-2].minor.yy0.z;
- yygotominor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-2].minor.yy0.z) + pParse->sLastToken.n;
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy243);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy243);
}
break;
- case 37: /* columnid ::= nm */
+ case 23: /* table_options ::= WITHOUT nm */
{
- sqlite3AddColumn(pParse,&yymsp[0].minor.yy0);
- yygotominor.yy0 = yymsp[0].minor.yy0;
+ if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
+ yymsp[-1].minor.yy194 = TF_WithoutRowid | TF_NoVisibleRowid;
+ }else{
+ yymsp[-1].minor.yy194 = 0;
+ sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
+ }
}
break;
- case 38: /* id ::= ID */
- case 39: /* id ::= INDEXED */ yytestcase(yyruleno==39);
- case 40: /* ids ::= ID|STRING */ yytestcase(yyruleno==40);
- case 41: /* nm ::= id */ yytestcase(yyruleno==41);
- case 42: /* nm ::= STRING */ yytestcase(yyruleno==42);
- case 43: /* nm ::= JOIN_KW */ yytestcase(yyruleno==43);
- case 46: /* typetoken ::= typename */ yytestcase(yyruleno==46);
- case 49: /* typename ::= ids */ yytestcase(yyruleno==49);
- case 127: /* as ::= AS nm */ yytestcase(yyruleno==127);
- case 128: /* as ::= ids */ yytestcase(yyruleno==128);
- case 138: /* dbnm ::= DOT nm */ yytestcase(yyruleno==138);
- case 147: /* indexed_opt ::= INDEXED BY nm */ yytestcase(yyruleno==147);
- case 251: /* collate ::= COLLATE ids */ yytestcase(yyruleno==251);
- case 260: /* nmnum ::= plus_num */ yytestcase(yyruleno==260);
- case 261: /* nmnum ::= nm */ yytestcase(yyruleno==261);
- case 262: /* nmnum ::= ON */ yytestcase(yyruleno==262);
- case 263: /* nmnum ::= DELETE */ yytestcase(yyruleno==263);
- case 264: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==264);
- case 265: /* plus_num ::= plus_opt number */ yytestcase(yyruleno==265);
- case 266: /* minus_num ::= MINUS number */ yytestcase(yyruleno==266);
- case 267: /* number ::= INTEGER|FLOAT */ yytestcase(yyruleno==267);
- case 285: /* trnm ::= nm */ yytestcase(yyruleno==285);
-{yygotominor.yy0 = yymsp[0].minor.yy0;}
+ case 24: /* columnname ::= nm typetoken */
+{sqlite3AddColumn(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
break;
- case 45: /* type ::= typetoken */
-{sqlite3AddColumnType(pParse,&yymsp[0].minor.yy0);}
+ case 25: /* typetoken ::= */
+ case 60: /* conslist_opt ::= */ yytestcase(yyruleno==60);
+ case 96: /* as ::= */ yytestcase(yyruleno==96);
+{yymsp[1].minor.yy0.n = 0; yymsp[1].minor.yy0.z = 0;}
break;
- case 47: /* typetoken ::= typename LP signed RP */
+ case 26: /* typetoken ::= typename LP signed RP */
{
- yygotominor.yy0.z = yymsp[-3].minor.yy0.z;
- yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z);
+ yymsp[-3].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-3].minor.yy0.z);
}
break;
- case 48: /* typetoken ::= typename LP signed COMMA signed RP */
+ case 27: /* typetoken ::= typename LP signed COMMA signed RP */
{
- yygotominor.yy0.z = yymsp[-5].minor.yy0.z;
- yygotominor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z);
+ yymsp[-5].minor.yy0.n = (int)(&yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] - yymsp[-5].minor.yy0.z);
}
break;
- case 50: /* typename ::= typename ids */
-{yygotominor.yy0.z=yymsp[-1].minor.yy0.z; yygotominor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
+ case 28: /* typename ::= typename ID|STRING */
+{yymsp[-1].minor.yy0.n=yymsp[0].minor.yy0.n+(int)(yymsp[0].minor.yy0.z-yymsp[-1].minor.yy0.z);}
break;
- case 57: /* ccons ::= DEFAULT term */
- case 59: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==59);
-{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy118);}
+ case 29: /* ccons ::= CONSTRAINT nm */
+ case 62: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==62);
+{pParse->constraintName = yymsp[0].minor.yy0;}
break;
- case 58: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy118);}
+ case 30: /* ccons ::= DEFAULT term */
+ case 32: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==32);
+{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy190);}
break;
- case 60: /* ccons ::= DEFAULT MINUS term */
+ case 31: /* ccons ::= DEFAULT LP expr RP */
+{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy190);}
+ break;
+ case 33: /* ccons ::= DEFAULT MINUS term */
{
ExprSpan v;
- v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy118.pExpr, 0, 0);
+ v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy190.pExpr, 0, 0);
v.zStart = yymsp[-1].minor.yy0.z;
- v.zEnd = yymsp[0].minor.yy118.zEnd;
+ v.zEnd = yymsp[0].minor.yy190.zEnd;
sqlite3AddDefaultValue(pParse,&v);
}
break;
- case 61: /* ccons ::= DEFAULT id */
+ case 34: /* ccons ::= DEFAULT ID|INDEXED */
{
ExprSpan v;
- spanExpr(&v, pParse, TK_STRING, &yymsp[0].minor.yy0);
+ spanExpr(&v, pParse, TK_STRING, yymsp[0].minor.yy0);
sqlite3AddDefaultValue(pParse,&v);
}
break;
- case 63: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy4);}
+ case 35: /* ccons ::= NOT NULL onconf */
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy194);}
break;
- case 64: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy4,yymsp[0].minor.yy4,yymsp[-2].minor.yy4);}
+ case 36: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy194,yymsp[0].minor.yy194,yymsp[-2].minor.yy194);}
break;
- case 65: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy4,0,0,0,0);}
+ case 37: /* ccons ::= UNIQUE onconf */
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy194,0,0,0,0,
+ SQLITE_IDXTYPE_UNIQUE);}
break;
- case 66: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy118.pExpr);}
+ case 38: /* ccons ::= CHECK LP expr RP */
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy190.pExpr);}
break;
- case 67: /* ccons ::= REFERENCES nm idxlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy322,yymsp[0].minor.yy4);}
+ case 39: /* ccons ::= REFERENCES nm eidlist_opt refargs */
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy148,yymsp[0].minor.yy194);}
break;
- case 68: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy4);}
+ case 40: /* ccons ::= defer_subclause */
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy194);}
break;
- case 69: /* ccons ::= COLLATE ids */
+ case 41: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
- case 72: /* refargs ::= */
-{ yygotominor.yy4 = OE_None*0x0101; /* EV: R-19803-45884 */}
+ case 44: /* refargs ::= */
+{ yymsp[1].minor.yy194 = OE_None*0x0101; /* EV: R-19803-45884 */}
+ break;
+ case 45: /* refargs ::= refargs refarg */
+{ yymsp[-1].minor.yy194 = (yymsp[-1].minor.yy194 & ~yymsp[0].minor.yy497.mask) | yymsp[0].minor.yy497.value; }
+ break;
+ case 46: /* refarg ::= MATCH nm */
+{ yymsp[-1].minor.yy497.value = 0; yymsp[-1].minor.yy497.mask = 0x000000; }
break;
- case 73: /* refargs ::= refargs refarg */
-{ yygotominor.yy4 = (yymsp[-1].minor.yy4 & ~yymsp[0].minor.yy215.mask) | yymsp[0].minor.yy215.value; }
+ case 47: /* refarg ::= ON INSERT refact */
+{ yymsp[-2].minor.yy497.value = 0; yymsp[-2].minor.yy497.mask = 0x000000; }
break;
- case 74: /* refarg ::= MATCH nm */
- case 75: /* refarg ::= ON INSERT refact */ yytestcase(yyruleno==75);
-{ yygotominor.yy215.value = 0; yygotominor.yy215.mask = 0x000000; }
+ case 48: /* refarg ::= ON DELETE refact */
+{ yymsp[-2].minor.yy497.value = yymsp[0].minor.yy194; yymsp[-2].minor.yy497.mask = 0x0000ff; }
break;
- case 76: /* refarg ::= ON DELETE refact */
-{ yygotominor.yy215.value = yymsp[0].minor.yy4; yygotominor.yy215.mask = 0x0000ff; }
+ case 49: /* refarg ::= ON UPDATE refact */
+{ yymsp[-2].minor.yy497.value = yymsp[0].minor.yy194<<8; yymsp[-2].minor.yy497.mask = 0x00ff00; }
break;
- case 77: /* refarg ::= ON UPDATE refact */
-{ yygotominor.yy215.value = yymsp[0].minor.yy4<<8; yygotominor.yy215.mask = 0x00ff00; }
+ case 50: /* refact ::= SET NULL */
+{ yymsp[-1].minor.yy194 = OE_SetNull; /* EV: R-33326-45252 */}
break;
- case 78: /* refact ::= SET NULL */
-{ yygotominor.yy4 = OE_SetNull; /* EV: R-33326-45252 */}
+ case 51: /* refact ::= SET DEFAULT */
+{ yymsp[-1].minor.yy194 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
- case 79: /* refact ::= SET DEFAULT */
-{ yygotominor.yy4 = OE_SetDflt; /* EV: R-33326-45252 */}
+ case 52: /* refact ::= CASCADE */
+{ yymsp[0].minor.yy194 = OE_Cascade; /* EV: R-33326-45252 */}
break;
- case 80: /* refact ::= CASCADE */
-{ yygotominor.yy4 = OE_Cascade; /* EV: R-33326-45252 */}
+ case 53: /* refact ::= RESTRICT */
+{ yymsp[0].minor.yy194 = OE_Restrict; /* EV: R-33326-45252 */}
break;
- case 81: /* refact ::= RESTRICT */
-{ yygotominor.yy4 = OE_Restrict; /* EV: R-33326-45252 */}
+ case 54: /* refact ::= NO ACTION */
+{ yymsp[-1].minor.yy194 = OE_None; /* EV: R-33326-45252 */}
break;
- case 82: /* refact ::= NO ACTION */
-{ yygotominor.yy4 = OE_None; /* EV: R-33326-45252 */}
+ case 55: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+{yymsp[-2].minor.yy194 = 0;}
break;
- case 84: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- case 99: /* defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==99);
- case 101: /* onconf ::= ON CONFLICT resolvetype */ yytestcase(yyruleno==101);
- case 104: /* resolvetype ::= raisetype */ yytestcase(yyruleno==104);
-{yygotominor.yy4 = yymsp[0].minor.yy4;}
+ case 56: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ case 71: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==71);
+ case 144: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==144);
+{yymsp[-1].minor.yy194 = yymsp[0].minor.yy194;}
break;
- case 88: /* conslist_opt ::= */
-{yygotominor.yy0.n = 0; yygotominor.yy0.z = 0;}
+ case 58: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ case 75: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==75);
+ case 187: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==187);
+ case 190: /* in_op ::= NOT IN */ yytestcase(yyruleno==190);
+ case 216: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==216);
+{yymsp[-1].minor.yy194 = 1;}
break;
- case 89: /* conslist_opt ::= COMMA conslist */
-{yygotominor.yy0 = yymsp[-1].minor.yy0;}
+ case 59: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+{yymsp[-1].minor.yy194 = 0;}
break;
- case 94: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy322,yymsp[0].minor.yy4,yymsp[-2].minor.yy4,0);}
+ case 61: /* tconscomma ::= COMMA */
+{pParse->constraintName.n = 0;}
break;
- case 95: /* tcons ::= UNIQUE LP idxlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy322,yymsp[0].minor.yy4,0,0,0,0);}
+ case 63: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy148,yymsp[0].minor.yy194,yymsp[-2].minor.yy194,0);}
break;
- case 96: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy118.pExpr);}
+ case 64: /* tcons ::= UNIQUE LP sortlist RP onconf */
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy148,yymsp[0].minor.yy194,0,0,0,0,
+ SQLITE_IDXTYPE_UNIQUE);}
break;
- case 97: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */
+ case 65: /* tcons ::= CHECK LP expr RP onconf */
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy190.pExpr);}
+ break;
+ case 66: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy322, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy322, yymsp[-1].minor.yy4);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy4);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy148, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy148, yymsp[-1].minor.yy194);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy194);
}
break;
- case 100: /* onconf ::= */
-{yygotominor.yy4 = OE_Default;}
- break;
- case 102: /* orconf ::= */
-{yygotominor.yy210 = OE_Default;}
+ case 68: /* onconf ::= */
+ case 70: /* orconf ::= */ yytestcase(yyruleno==70);
+{yymsp[1].minor.yy194 = OE_Default;}
break;
- case 103: /* orconf ::= OR resolvetype */
-{yygotominor.yy210 = (u8)yymsp[0].minor.yy4;}
+ case 69: /* onconf ::= ON CONFLICT resolvetype */
+{yymsp[-2].minor.yy194 = yymsp[0].minor.yy194;}
break;
- case 105: /* resolvetype ::= IGNORE */
-{yygotominor.yy4 = OE_Ignore;}
+ case 72: /* resolvetype ::= IGNORE */
+{yymsp[0].minor.yy194 = OE_Ignore;}
break;
- case 106: /* resolvetype ::= REPLACE */
-{yygotominor.yy4 = OE_Replace;}
+ case 73: /* resolvetype ::= REPLACE */
+ case 145: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==145);
+{yymsp[0].minor.yy194 = OE_Replace;}
break;
- case 107: /* cmd ::= DROP TABLE ifexists fullname */
+ case 74: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy259, 0, yymsp[-1].minor.yy4);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy185, 0, yymsp[-1].minor.yy194);
}
break;
- case 110: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select */
+ case 77: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy387, yymsp[-6].minor.yy4, yymsp[-4].minor.yy4);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy148, yymsp[0].minor.yy243, yymsp[-7].minor.yy194, yymsp[-5].minor.yy194);
}
break;
- case 111: /* cmd ::= DROP VIEW ifexists fullname */
+ case 78: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy259, 1, yymsp[-1].minor.yy4);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy185, 1, yymsp[-1].minor.yy194);
}
break;
- case 112: /* cmd ::= select */
+ case 79: /* cmd ::= select */
{
- SelectDest dest = {SRT_Output, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy387, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy387);
+ SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0};
+ sqlite3Select(pParse, yymsp[0].minor.yy243, &dest);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy243);
}
break;
- case 113: /* select ::= oneselect */
-{yygotominor.yy387 = yymsp[0].minor.yy387;}
+ case 80: /* select ::= with selectnowith */
+{
+ Select *p = yymsp[0].minor.yy243;
+ if( p ){
+ p->pWith = yymsp[-1].minor.yy285;
+ parserDoubleLinkSelect(pParse, p);
+ }else{
+ sqlite3WithDelete(pParse->db, yymsp[-1].minor.yy285);
+ }
+ yymsp[-1].minor.yy243 = p; /*A-overwrites-W*/
+}
break;
- case 114: /* select ::= select multiselect_op oneselect */
+ case 81: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- if( yymsp[0].minor.yy387 ){
- yymsp[0].minor.yy387->op = (u8)yymsp[-1].minor.yy4;
- yymsp[0].minor.yy387->pPrior = yymsp[-2].minor.yy387;
+ Select *pRhs = yymsp[0].minor.yy243;
+ Select *pLhs = yymsp[-2].minor.yy243;
+ if( pRhs && pRhs->pPrior ){
+ SrcList *pFrom;
+ Token x;
+ x.n = 0;
+ parserDoubleLinkSelect(pParse, pRhs);
+ pFrom = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&x,pRhs,0,0);
+ pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0,0);
+ }
+ if( pRhs ){
+ pRhs->op = (u8)yymsp[-1].minor.yy194;
+ pRhs->pPrior = pLhs;
+ if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
+ pRhs->selFlags &= ~SF_MultiValue;
+ if( yymsp[-1].minor.yy194!=TK_ALL ) pParse->hasCompound = 1;
}else{
- sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy387);
+ sqlite3SelectDelete(pParse->db, pLhs);
+ }
+ yymsp[-2].minor.yy243 = pRhs;
+}
+ break;
+ case 82: /* multiselect_op ::= UNION */
+ case 84: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==84);
+{yymsp[0].minor.yy194 = yymsp[0].major; /*A-overwrites-OP*/}
+ break;
+ case 83: /* multiselect_op ::= UNION ALL */
+{yymsp[-1].minor.yy194 = TK_ALL;}
+ break;
+ case 85: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+{
+#if SELECTTRACE_ENABLED
+ Token s = yymsp[-8].minor.yy0; /*A-overwrites-S*/
+#endif
+ yymsp[-8].minor.yy243 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy148,yymsp[-5].minor.yy185,yymsp[-4].minor.yy72,yymsp[-3].minor.yy148,yymsp[-2].minor.yy72,yymsp[-1].minor.yy148,yymsp[-7].minor.yy194,yymsp[0].minor.yy354.pLimit,yymsp[0].minor.yy354.pOffset);
+#if SELECTTRACE_ENABLED
+ /* Populate the Select.zSelName[] string that is used to help with
+ ** query planner debugging, to differentiate between multiple Select
+ ** objects in a complex query.
+ **
+ ** If the SELECT keyword is immediately followed by a C-style comment
+ ** then extract the first few alphanumeric characters from within that
+ ** comment to be the zSelName value. Otherwise, the label is #N where
+ ** is an integer that is incremented with each SELECT statement seen.
+ */
+ if( yymsp[-8].minor.yy243!=0 ){
+ const char *z = s.z+6;
+ int i;
+ sqlite3_snprintf(sizeof(yymsp[-8].minor.yy243->zSelName), yymsp[-8].minor.yy243->zSelName, "#%d",
+ ++pParse->nSelect);
+ while( z[0]==' ' ) z++;
+ if( z[0]=='/' && z[1]=='*' ){
+ z += 2;
+ while( z[0]==' ' ) z++;
+ for(i=0; sqlite3Isalnum(z[i]); i++){}
+ sqlite3_snprintf(sizeof(yymsp[-8].minor.yy243->zSelName), yymsp[-8].minor.yy243->zSelName, "%.*s", i, z);
+ }
}
- yygotominor.yy387 = yymsp[0].minor.yy387;
+#endif /* SELECTRACE_ENABLED */
}
break;
- case 116: /* multiselect_op ::= UNION ALL */
-{yygotominor.yy4 = TK_ALL;}
+ case 86: /* values ::= VALUES LP nexprlist RP */
+{
+ yymsp[-3].minor.yy243 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy148,0,0,0,0,0,SF_Values,0,0);
+}
break;
- case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ case 87: /* values ::= values COMMA LP exprlist RP */
{
- yygotominor.yy387 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy322,yymsp[-5].minor.yy259,yymsp[-4].minor.yy314,yymsp[-3].minor.yy322,yymsp[-2].minor.yy314,yymsp[-1].minor.yy322,yymsp[-7].minor.yy4,yymsp[0].minor.yy292.pLimit,yymsp[0].minor.yy292.pOffset);
+ Select *pRight, *pLeft = yymsp[-4].minor.yy243;
+ pRight = sqlite3SelectNew(pParse,yymsp[-1].minor.yy148,0,0,0,0,0,SF_Values|SF_MultiValue,0,0);
+ if( ALWAYS(pLeft) ) pLeft->selFlags &= ~SF_MultiValue;
+ if( pRight ){
+ pRight->op = TK_ALL;
+ pRight->pPrior = pLeft;
+ yymsp[-4].minor.yy243 = pRight;
+ }else{
+ yymsp[-4].minor.yy243 = pLeft;
+ }
}
break;
- case 122: /* sclp ::= selcollist COMMA */
- case 247: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==247);
-{yygotominor.yy322 = yymsp[-1].minor.yy322;}
+ case 88: /* distinct ::= DISTINCT */
+{yymsp[0].minor.yy194 = SF_Distinct;}
+ break;
+ case 89: /* distinct ::= ALL */
+{yymsp[0].minor.yy194 = SF_All;}
break;
- case 123: /* sclp ::= */
- case 151: /* orderby_opt ::= */ yytestcase(yyruleno==151);
- case 159: /* groupby_opt ::= */ yytestcase(yyruleno==159);
- case 240: /* exprlist ::= */ yytestcase(yyruleno==240);
- case 246: /* idxlist_opt ::= */ yytestcase(yyruleno==246);
-{yygotominor.yy322 = 0;}
+ case 91: /* sclp ::= */
+ case 119: /* orderby_opt ::= */ yytestcase(yyruleno==119);
+ case 126: /* groupby_opt ::= */ yytestcase(yyruleno==126);
+ case 203: /* exprlist ::= */ yytestcase(yyruleno==203);
+ case 206: /* paren_exprlist ::= */ yytestcase(yyruleno==206);
+ case 211: /* eidlist_opt ::= */ yytestcase(yyruleno==211);
+{yymsp[1].minor.yy148 = 0;}
break;
- case 124: /* selcollist ::= sclp expr as */
+ case 92: /* selcollist ::= sclp expr as */
{
- yygotominor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy322, yymsp[-1].minor.yy118.pExpr);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yygotominor.yy322, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yygotominor.yy322,&yymsp[-1].minor.yy118);
+ yymsp[-2].minor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy148, yymsp[-1].minor.yy190.pExpr);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-2].minor.yy148, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-2].minor.yy148,&yymsp[-1].minor.yy190);
}
break;
- case 125: /* selcollist ::= sclp STAR */
+ case 93: /* selcollist ::= sclp STAR */
{
- Expr *p = sqlite3Expr(pParse->db, TK_ALL, 0);
- yygotominor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy322, p);
+ Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
+ yymsp[-1].minor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy148, p);
}
break;
- case 126: /* selcollist ::= sclp nm DOT STAR */
+ case 94: /* selcollist ::= sclp nm DOT STAR */
{
- Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &yymsp[0].minor.yy0);
+ Expr *pRight = sqlite3PExpr(pParse, TK_ASTERISK, 0, 0, 0);
Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0);
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322, pDot);
+ yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148, pDot);
}
break;
- case 129: /* as ::= */
-{yygotominor.yy0.n = 0;}
+ case 95: /* as ::= AS nm */
+ case 106: /* dbnm ::= DOT nm */ yytestcase(yyruleno==106);
+ case 225: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==225);
+ case 226: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==226);
+{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 130: /* from ::= */
-{yygotominor.yy259 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy259));}
+ case 97: /* from ::= */
+{yymsp[1].minor.yy185 = sqlite3DbMallocZero(pParse->db, sizeof(*yymsp[1].minor.yy185));}
break;
- case 131: /* from ::= FROM seltablist */
+ case 98: /* from ::= FROM seltablist */
{
- yygotominor.yy259 = yymsp[0].minor.yy259;
- sqlite3SrcListShiftJoinType(yygotominor.yy259);
+ yymsp[-1].minor.yy185 = yymsp[0].minor.yy185;
+ sqlite3SrcListShiftJoinType(yymsp[-1].minor.yy185);
}
break;
- case 132: /* stl_prefix ::= seltablist joinop */
+ case 99: /* stl_prefix ::= seltablist joinop */
{
- yygotominor.yy259 = yymsp[-1].minor.yy259;
- if( ALWAYS(yygotominor.yy259 && yygotominor.yy259->nSrc>0) ) yygotominor.yy259->a[yygotominor.yy259->nSrc-1].jointype = (u8)yymsp[0].minor.yy4;
+ if( ALWAYS(yymsp[-1].minor.yy185 && yymsp[-1].minor.yy185->nSrc>0) ) yymsp[-1].minor.yy185->a[yymsp[-1].minor.yy185->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy194;
}
break;
- case 133: /* stl_prefix ::= */
-{yygotominor.yy259 = 0;}
+ case 100: /* stl_prefix ::= */
+{yymsp[1].minor.yy185 = 0;}
+ break;
+ case 101: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
+{
+ yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-6].minor.yy185, &yymsp[-2].minor.yy0);
+}
break;
- case 134: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */
+ case 102: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_opt using_opt */
{
- yygotominor.yy259 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy259,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy314,yymsp[0].minor.yy384);
- sqlite3SrcListIndexedBy(pParse, yygotominor.yy259, &yymsp[-2].minor.yy0);
+ yymsp[-8].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-8].minor.yy185,&yymsp[-7].minor.yy0,&yymsp[-6].minor.yy0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-8].minor.yy185, yymsp[-4].minor.yy148);
}
break;
- case 135: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
+ case 103: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */
{
- yygotominor.yy259 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy259,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy387,yymsp[-1].minor.yy314,yymsp[0].minor.yy384);
+ yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy243,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
}
break;
- case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
+ case 104: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */
{
- if( yymsp[-6].minor.yy259==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy314==0 && yymsp[0].minor.yy384==0 ){
- yygotominor.yy259 = yymsp[-4].minor.yy259;
+ if( yymsp[-6].minor.yy185==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy72==0 && yymsp[0].minor.yy254==0 ){
+ yymsp[-6].minor.yy185 = yymsp[-4].minor.yy185;
+ }else if( yymsp[-4].minor.yy185->nSrc==1 ){
+ yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,0,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
+ if( yymsp[-6].minor.yy185 ){
+ struct SrcList_item *pNew = &yymsp[-6].minor.yy185->a[yymsp[-6].minor.yy185->nSrc-1];
+ struct SrcList_item *pOld = yymsp[-4].minor.yy185->a;
+ pNew->zName = pOld->zName;
+ pNew->zDatabase = pOld->zDatabase;
+ pNew->pSelect = pOld->pSelect;
+ pOld->zName = pOld->zDatabase = 0;
+ pOld->pSelect = 0;
+ }
+ sqlite3SrcListDelete(pParse->db, yymsp[-4].minor.yy185);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy259);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy259,0,0,0,0,0,0,0);
- yygotominor.yy259 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy259,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy314,yymsp[0].minor.yy384);
+ sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy185);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy185,0,0,0,0,SF_NestedFrom,0,0);
+ yymsp[-6].minor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy72,yymsp[0].minor.yy254);
}
}
break;
- case 137: /* dbnm ::= */
- case 146: /* indexed_opt ::= */ yytestcase(yyruleno==146);
-{yygotominor.yy0.z=0; yygotominor.yy0.n=0;}
+ case 105: /* dbnm ::= */
+ case 114: /* indexed_opt ::= */ yytestcase(yyruleno==114);
+{yymsp[1].minor.yy0.z=0; yymsp[1].minor.yy0.n=0;}
break;
- case 139: /* fullname ::= nm dbnm */
-{yygotominor.yy259 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);}
+ case 107: /* fullname ::= nm dbnm */
+{yymsp[-1].minor.yy185 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 140: /* joinop ::= COMMA|JOIN */
-{ yygotominor.yy4 = JT_INNER; }
+ case 108: /* joinop ::= COMMA|JOIN */
+{ yymsp[0].minor.yy194 = JT_INNER; }
break;
- case 141: /* joinop ::= JOIN_KW JOIN */
-{ yygotominor.yy4 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); }
+ case 109: /* joinop ::= JOIN_KW JOIN */
+{yymsp[-1].minor.yy194 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
- case 142: /* joinop ::= JOIN_KW nm JOIN */
-{ yygotominor.yy4 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); }
+ case 110: /* joinop ::= JOIN_KW nm JOIN */
+{yymsp[-2].minor.yy194 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
- case 143: /* joinop ::= JOIN_KW nm nm JOIN */
-{ yygotominor.yy4 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); }
+ case 111: /* joinop ::= JOIN_KW nm nm JOIN */
+{yymsp[-3].minor.yy194 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
- case 144: /* on_opt ::= ON expr */
- case 155: /* sortitem ::= expr */ yytestcase(yyruleno==155);
- case 162: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==162);
- case 169: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==169);
- case 235: /* case_else ::= ELSE expr */ yytestcase(yyruleno==235);
- case 237: /* case_operand ::= expr */ yytestcase(yyruleno==237);
-{yygotominor.yy314 = yymsp[0].minor.yy118.pExpr;}
+ case 112: /* on_opt ::= ON expr */
+ case 129: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==129);
+ case 136: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==136);
+ case 199: /* case_else ::= ELSE expr */ yytestcase(yyruleno==199);
+{yymsp[-1].minor.yy72 = yymsp[0].minor.yy190.pExpr;}
break;
- case 145: /* on_opt ::= */
- case 161: /* having_opt ::= */ yytestcase(yyruleno==161);
- case 168: /* where_opt ::= */ yytestcase(yyruleno==168);
- case 236: /* case_else ::= */ yytestcase(yyruleno==236);
- case 238: /* case_operand ::= */ yytestcase(yyruleno==238);
-{yygotominor.yy314 = 0;}
+ case 113: /* on_opt ::= */
+ case 128: /* having_opt ::= */ yytestcase(yyruleno==128);
+ case 135: /* where_opt ::= */ yytestcase(yyruleno==135);
+ case 200: /* case_else ::= */ yytestcase(yyruleno==200);
+ case 202: /* case_operand ::= */ yytestcase(yyruleno==202);
+{yymsp[1].minor.yy72 = 0;}
break;
- case 148: /* indexed_opt ::= NOT INDEXED */
-{yygotominor.yy0.z=0; yygotominor.yy0.n=1;}
+ case 115: /* indexed_opt ::= INDEXED BY nm */
+{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
break;
- case 149: /* using_opt ::= USING LP inscollist RP */
- case 181: /* inscollist_opt ::= LP inscollist RP */ yytestcase(yyruleno==181);
-{yygotominor.yy384 = yymsp[-1].minor.yy384;}
+ case 116: /* indexed_opt ::= NOT INDEXED */
+{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
break;
- case 150: /* using_opt ::= */
- case 180: /* inscollist_opt ::= */ yytestcase(yyruleno==180);
-{yygotominor.yy384 = 0;}
+ case 117: /* using_opt ::= USING LP idlist RP */
+{yymsp[-3].minor.yy254 = yymsp[-1].minor.yy254;}
break;
- case 152: /* orderby_opt ::= ORDER BY sortlist */
- case 160: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==160);
- case 239: /* exprlist ::= nexprlist */ yytestcase(yyruleno==239);
-{yygotominor.yy322 = yymsp[0].minor.yy322;}
+ case 118: /* using_opt ::= */
+ case 146: /* idlist_opt ::= */ yytestcase(yyruleno==146);
+{yymsp[1].minor.yy254 = 0;}
break;
- case 153: /* sortlist ::= sortlist COMMA sortitem sortorder */
+ case 120: /* orderby_opt ::= ORDER BY sortlist */
+ case 127: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==127);
+{yymsp[-2].minor.yy148 = yymsp[0].minor.yy148;}
+ break;
+ case 121: /* sortlist ::= sortlist COMMA expr sortorder */
{
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy322,yymsp[-1].minor.yy314);
- if( yygotominor.yy322 ) yygotominor.yy322->a[yygotominor.yy322->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy4;
+ yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148,yymsp[-1].minor.yy190.pExpr);
+ sqlite3ExprListSetSortOrder(yymsp[-3].minor.yy148,yymsp[0].minor.yy194);
}
break;
- case 154: /* sortlist ::= sortitem sortorder */
+ case 122: /* sortlist ::= expr sortorder */
{
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy314);
- if( yygotominor.yy322 && ALWAYS(yygotominor.yy322->a) ) yygotominor.yy322->a[0].sortOrder = (u8)yymsp[0].minor.yy4;
+ yymsp[-1].minor.yy148 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy190.pExpr); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-1].minor.yy148,yymsp[0].minor.yy194);
}
break;
- case 156: /* sortorder ::= ASC */
- case 158: /* sortorder ::= */ yytestcase(yyruleno==158);
-{yygotominor.yy4 = SQLITE_SO_ASC;}
+ case 123: /* sortorder ::= ASC */
+{yymsp[0].minor.yy194 = SQLITE_SO_ASC;}
+ break;
+ case 124: /* sortorder ::= DESC */
+{yymsp[0].minor.yy194 = SQLITE_SO_DESC;}
break;
- case 157: /* sortorder ::= DESC */
-{yygotominor.yy4 = SQLITE_SO_DESC;}
+ case 125: /* sortorder ::= */
+{yymsp[1].minor.yy194 = SQLITE_SO_UNDEFINED;}
break;
- case 163: /* limit_opt ::= */
-{yygotominor.yy292.pLimit = 0; yygotominor.yy292.pOffset = 0;}
+ case 130: /* limit_opt ::= */
+{yymsp[1].minor.yy354.pLimit = 0; yymsp[1].minor.yy354.pOffset = 0;}
break;
- case 164: /* limit_opt ::= LIMIT expr */
-{yygotominor.yy292.pLimit = yymsp[0].minor.yy118.pExpr; yygotominor.yy292.pOffset = 0;}
+ case 131: /* limit_opt ::= LIMIT expr */
+{yymsp[-1].minor.yy354.pLimit = yymsp[0].minor.yy190.pExpr; yymsp[-1].minor.yy354.pOffset = 0;}
break;
- case 165: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yygotominor.yy292.pLimit = yymsp[-2].minor.yy118.pExpr; yygotominor.yy292.pOffset = yymsp[0].minor.yy118.pExpr;}
+ case 132: /* limit_opt ::= LIMIT expr OFFSET expr */
+{yymsp[-3].minor.yy354.pLimit = yymsp[-2].minor.yy190.pExpr; yymsp[-3].minor.yy354.pOffset = yymsp[0].minor.yy190.pExpr;}
break;
- case 166: /* limit_opt ::= LIMIT expr COMMA expr */
-{yygotominor.yy292.pOffset = yymsp[-2].minor.yy118.pExpr; yygotominor.yy292.pLimit = yymsp[0].minor.yy118.pExpr;}
+ case 133: /* limit_opt ::= LIMIT expr COMMA expr */
+{yymsp[-3].minor.yy354.pOffset = yymsp[-2].minor.yy190.pExpr; yymsp[-3].minor.yy354.pLimit = yymsp[0].minor.yy190.pExpr;}
break;
- case 167: /* cmd ::= DELETE FROM fullname indexed_opt where_opt */
+ case 134: /* cmd ::= with DELETE FROM fullname indexed_opt where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy259, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy259,yymsp[0].minor.yy314);
+ sqlite3WithPush(pParse, yymsp[-5].minor.yy285, 1);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy185, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy185,yymsp[0].minor.yy72);
}
break;
- case 170: /* cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt */
+ case 137: /* cmd ::= with UPDATE orconf fullname indexed_opt SET setlist where_opt */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy259, &yymsp[-3].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy322,"set list");
- sqlite3Update(pParse,yymsp[-4].minor.yy259,yymsp[-1].minor.yy322,yymsp[0].minor.yy314,yymsp[-5].minor.yy210);
+ sqlite3WithPush(pParse, yymsp[-7].minor.yy285, 1);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy185, &yymsp[-3].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy148,"set list");
+ sqlite3Update(pParse,yymsp[-4].minor.yy185,yymsp[-1].minor.yy148,yymsp[0].minor.yy72,yymsp[-5].minor.yy194);
}
break;
- case 171: /* setlist ::= setlist COMMA nm EQ expr */
+ case 138: /* setlist ::= setlist COMMA nm EQ expr */
{
- yygotominor.yy322 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy322, yymsp[0].minor.yy118.pExpr);
- sqlite3ExprListSetName(pParse, yygotominor.yy322, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy148, yymsp[0].minor.yy190.pExpr);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy148, &yymsp[-2].minor.yy0, 1);
}
break;
- case 172: /* setlist ::= nm EQ expr */
+ case 139: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yygotominor.yy322 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy118.pExpr);
- sqlite3ExprListSetName(pParse, yygotominor.yy322, &yymsp[-2].minor.yy0, 1);
+ yymsp[-6].minor.yy148 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy148, yymsp[-3].minor.yy254, yymsp[0].minor.yy190.pExpr);
}
break;
- case 173: /* cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP */
-{sqlite3Insert(pParse, yymsp[-5].minor.yy259, yymsp[-1].minor.yy322, 0, yymsp[-4].minor.yy384, yymsp[-7].minor.yy210);}
- break;
- case 174: /* cmd ::= insert_cmd INTO fullname inscollist_opt select */
-{sqlite3Insert(pParse, yymsp[-2].minor.yy259, 0, yymsp[0].minor.yy387, yymsp[-1].minor.yy384, yymsp[-4].minor.yy210);}
- break;
- case 175: /* cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */
-{sqlite3Insert(pParse, yymsp[-3].minor.yy259, 0, 0, yymsp[-2].minor.yy384, yymsp[-5].minor.yy210);}
- break;
- case 176: /* insert_cmd ::= INSERT orconf */
-{yygotominor.yy210 = yymsp[0].minor.yy210;}
+ case 140: /* setlist ::= nm EQ expr */
+{
+ yylhsminor.yy148 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy190.pExpr);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy148, &yymsp[-2].minor.yy0, 1);
+}
+ yymsp[-2].minor.yy148 = yylhsminor.yy148;
break;
- case 177: /* insert_cmd ::= REPLACE */
-{yygotominor.yy210 = OE_Replace;}
+ case 141: /* setlist ::= LP idlist RP EQ expr */
+{
+ yymsp[-4].minor.yy148 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy254, yymsp[0].minor.yy190.pExpr);
+}
break;
- case 178: /* itemlist ::= itemlist COMMA expr */
- case 241: /* nexprlist ::= nexprlist COMMA expr */ yytestcase(yyruleno==241);
-{yygotominor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy322,yymsp[0].minor.yy118.pExpr);}
+ case 142: /* cmd ::= with insert_cmd INTO fullname idlist_opt select */
+{
+ sqlite3WithPush(pParse, yymsp[-5].minor.yy285, 1);
+ sqlite3Insert(pParse, yymsp[-2].minor.yy185, yymsp[0].minor.yy243, yymsp[-1].minor.yy254, yymsp[-4].minor.yy194);
+}
break;
- case 179: /* itemlist ::= expr */
- case 242: /* nexprlist ::= expr */ yytestcase(yyruleno==242);
-{yygotominor.yy322 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy118.pExpr);}
+ case 143: /* cmd ::= with insert_cmd INTO fullname idlist_opt DEFAULT VALUES */
+{
+ sqlite3WithPush(pParse, yymsp[-6].minor.yy285, 1);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy185, 0, yymsp[-2].minor.yy254, yymsp[-5].minor.yy194);
+}
break;
- case 182: /* inscollist ::= inscollist COMMA nm */
-{yygotominor.yy384 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy384,&yymsp[0].minor.yy0);}
+ case 147: /* idlist_opt ::= LP idlist RP */
+{yymsp[-2].minor.yy254 = yymsp[-1].minor.yy254;}
break;
- case 183: /* inscollist ::= nm */
-{yygotominor.yy384 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);}
+ case 148: /* idlist ::= idlist COMMA nm */
+{yymsp[-2].minor.yy254 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);}
break;
- case 184: /* expr ::= term */
-{yygotominor.yy118 = yymsp[0].minor.yy118;}
+ case 149: /* idlist ::= nm */
+{yymsp[0].minor.yy254 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
- case 185: /* expr ::= LP expr RP */
-{yygotominor.yy118.pExpr = yymsp[-1].minor.yy118.pExpr; spanSet(&yygotominor.yy118,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);}
+ case 150: /* expr ::= LP expr RP */
+{spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/ yymsp[-2].minor.yy190.pExpr = yymsp[-1].minor.yy190.pExpr;}
break;
- case 186: /* term ::= NULL */
- case 191: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==191);
- case 192: /* term ::= STRING */ yytestcase(yyruleno==192);
-{spanExpr(&yygotominor.yy118, pParse, yymsp[0].major, &yymsp[0].minor.yy0);}
+ case 151: /* term ::= NULL */
+ case 156: /* term ::= FLOAT|BLOB */ yytestcase(yyruleno==156);
+ case 157: /* term ::= STRING */ yytestcase(yyruleno==157);
+{spanExpr(&yymsp[0].minor.yy190,pParse,yymsp[0].major,yymsp[0].minor.yy0);/*A-overwrites-X*/}
break;
- case 187: /* expr ::= id */
- case 188: /* expr ::= JOIN_KW */ yytestcase(yyruleno==188);
-{spanExpr(&yygotominor.yy118, pParse, TK_ID, &yymsp[0].minor.yy0);}
+ case 152: /* expr ::= ID|INDEXED */
+ case 153: /* expr ::= JOIN_KW */ yytestcase(yyruleno==153);
+{spanExpr(&yymsp[0].minor.yy190,pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
- case 189: /* expr ::= nm DOT nm */
+ case 154: /* expr ::= nm DOT nm */
{
- Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
- Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
- spanSet(&yygotominor.yy118,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
+ Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
+ spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-2].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0);
}
break;
- case 190: /* expr ::= nm DOT nm DOT nm */
+ case 155: /* expr ::= nm DOT nm DOT nm */
{
- Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-4].minor.yy0);
- Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0);
- Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0);
+ Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
+ Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
+ Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0);
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
- spanSet(&yygotominor.yy118,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
+ spanSet(&yymsp[-4].minor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0);
}
break;
- case 193: /* expr ::= REGISTER */
+ case 158: /* term ::= INTEGER */
{
- /* When doing a nested parse, one can include terms in an expression
- ** that look like this: #1 #2 ... These terms refer to registers
- ** in the virtual machine. #N is the N-th register. */
- if( pParse->nested==0 ){
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &yymsp[0].minor.yy0);
- yygotominor.yy118.pExpr = 0;
- }else{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &yymsp[0].minor.yy0);
- if( yygotominor.yy118.pExpr ) sqlite3GetInt32(&yymsp[0].minor.yy0.z[1], &yygotominor.yy118.pExpr->iTable);
- }
- spanSet(&yygotominor.yy118, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
+ yylhsminor.yy190.pExpr = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ yylhsminor.yy190.zStart = yymsp[0].minor.yy0.z;
+ yylhsminor.yy190.zEnd = yymsp[0].minor.yy0.z + yymsp[0].minor.yy0.n;
+ if( yylhsminor.yy190.pExpr ) yylhsminor.yy190.pExpr->flags |= EP_Leaf;
}
+ yymsp[0].minor.yy190 = yylhsminor.yy190;
break;
- case 194: /* expr ::= VARIABLE */
+ case 159: /* expr ::= VARIABLE */
{
- spanExpr(&yygotominor.yy118, pParse, TK_VARIABLE, &yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yygotominor.yy118.pExpr);
- spanSet(&yygotominor.yy118, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
+ if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
+ u32 n = yymsp[0].minor.yy0.n;
+ spanExpr(&yymsp[0].minor.yy190, pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy190.pExpr, n);
+ }else{
+ /* When doing a nested parse, one can include terms in an expression
+ ** that look like this: #1 #2 ... These terms refer to registers
+ ** in the virtual machine. #N is the N-th register. */
+ Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
+ assert( t.n>=2 );
+ spanSet(&yymsp[0].minor.yy190, &t, &t);
+ if( pParse->nested==0 ){
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
+ yymsp[0].minor.yy190.pExpr = 0;
+ }else{
+ yymsp[0].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, 0);
+ if( yymsp[0].minor.yy190.pExpr ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy190.pExpr->iTable);
+ }
+ }
}
break;
- case 195: /* expr ::= expr COLLATE ids */
+ case 160: /* expr ::= expr COLLATE ID|STRING */
{
- yygotominor.yy118.pExpr = sqlite3ExprSetCollByToken(pParse, yymsp[-2].minor.yy118.pExpr, &yymsp[0].minor.yy0);
- yygotominor.yy118.zStart = yymsp[-2].minor.yy118.zStart;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ yymsp[-2].minor.yy190.pExpr = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy190.pExpr, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 196: /* expr ::= CAST LP expr AS typetoken RP */
+ case 161: /* expr ::= CAST LP expr AS typetoken RP */
{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy118.pExpr, 0, &yymsp[-1].minor.yy0);
- spanSet(&yygotominor.yy118,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0);
+ spanSet(&yymsp[-5].minor.yy190,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-5].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy190.pExpr, 0, &yymsp[-1].minor.yy0);
}
break;
- case 197: /* expr ::= ID LP distinct exprlist RP */
+ case 162: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
- if( yymsp[-1].minor.yy322 && yymsp[-1].minor.yy322->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
+ if( yymsp[-1].minor.yy148 && yymsp[-1].minor.yy148->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){
sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0);
}
- yygotominor.yy118.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy322, &yymsp[-4].minor.yy0);
- spanSet(&yygotominor.yy118,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
- if( yymsp[-2].minor.yy4 && yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->flags |= EP_Distinct;
+ yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy148, &yymsp[-4].minor.yy0);
+ spanSet(&yylhsminor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0);
+ if( yymsp[-2].minor.yy194==SF_Distinct && yylhsminor.yy190.pExpr ){
+ yylhsminor.yy190.pExpr->flags |= EP_Distinct;
}
}
+ yymsp[-4].minor.yy190 = yylhsminor.yy190;
break;
- case 198: /* expr ::= ID LP STAR RP */
+ case 163: /* expr ::= ID|INDEXED LP STAR RP */
{
- yygotominor.yy118.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
- spanSet(&yygotominor.yy118,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
+ yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0);
+ spanSet(&yylhsminor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0);
}
+ yymsp[-3].minor.yy190 = yylhsminor.yy190;
break;
- case 199: /* term ::= CTIME_KW */
+ case 164: /* term ::= CTIME_KW */
{
- /* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are
- ** treated as functions that return constants */
- yygotominor.yy118.pExpr = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->op = TK_CONST_FUNC;
+ yylhsminor.yy190.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0);
+ spanSet(&yylhsminor.yy190, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
+}
+ yymsp[0].minor.yy190 = yylhsminor.yy190;
+ break;
+ case 165: /* expr ::= LP nexprlist COMMA expr RP */
+{
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy148, yymsp[-1].minor.yy190.pExpr);
+ yylhsminor.yy190.pExpr = sqlite3PExpr(pParse, TK_VECTOR, 0, 0, 0);
+ if( yylhsminor.yy190.pExpr ){
+ yylhsminor.yy190.pExpr->x.pList = pList;
+ spanSet(&yylhsminor.yy190, &yymsp[-4].minor.yy0, &yymsp[0].minor.yy0);
+ }else{
+ sqlite3ExprListDelete(pParse->db, pList);
}
- spanSet(&yygotominor.yy118, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0);
}
+ yymsp[-4].minor.yy190 = yylhsminor.yy190;
break;
- case 200: /* expr ::= expr AND expr */
- case 201: /* expr ::= expr OR expr */ yytestcase(yyruleno==201);
- case 202: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==202);
- case 203: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==203);
- case 204: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==204);
- case 205: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==205);
- case 206: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==206);
- case 207: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==207);
-{spanBinaryExpr(&yygotominor.yy118,pParse,yymsp[-1].major,&yymsp[-2].minor.yy118,&yymsp[0].minor.yy118);}
+ case 166: /* expr ::= expr AND expr */
+ case 167: /* expr ::= expr OR expr */ yytestcase(yyruleno==167);
+ case 168: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==168);
+ case 169: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==169);
+ case 170: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==170);
+ case 171: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==171);
+ case 172: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==172);
+ case 173: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==173);
+{spanBinaryExpr(pParse,yymsp[-1].major,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy190);}
break;
- case 208: /* likeop ::= LIKE_KW */
- case 210: /* likeop ::= MATCH */ yytestcase(yyruleno==210);
-{yygotominor.yy342.eOperator = yymsp[0].minor.yy0; yygotominor.yy342.not = 0;}
+ case 174: /* likeop ::= LIKE_KW|MATCH */
+{yymsp[0].minor.yy0=yymsp[0].minor.yy0;/*A-overwrites-X*/}
break;
- case 209: /* likeop ::= NOT LIKE_KW */
- case 211: /* likeop ::= NOT MATCH */ yytestcase(yyruleno==211);
-{yygotominor.yy342.eOperator = yymsp[0].minor.yy0; yygotominor.yy342.not = 1;}
+ case 175: /* likeop ::= NOT LIKE_KW|MATCH */
+{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
break;
- case 212: /* expr ::= expr likeop expr */
+ case 176: /* expr ::= expr likeop expr */
{
ExprList *pList;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy118.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy118.pExpr);
- yygotominor.yy118.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy342.eOperator);
- if( yymsp[-1].minor.yy342.not ) yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy118.pExpr, 0, 0);
- yygotominor.yy118.zStart = yymsp[-2].minor.yy118.zStart;
- yygotominor.yy118.zEnd = yymsp[0].minor.yy118.zEnd;
- if( yygotominor.yy118.pExpr ) yygotominor.yy118.pExpr->flags |= EP_InfixFunc;
+ int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
+ yymsp[-1].minor.yy0.n &= 0x7fffffff;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy190.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy190.pExpr);
+ yymsp[-2].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0);
+ exprNot(pParse, bNot, &yymsp[-2].minor.yy190);
+ yymsp[-2].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
+ if( yymsp[-2].minor.yy190.pExpr ) yymsp[-2].minor.yy190.pExpr->flags |= EP_InfixFunc;
}
break;
- case 213: /* expr ::= expr likeop expr ESCAPE expr */
+ case 177: /* expr ::= expr likeop expr ESCAPE expr */
{
ExprList *pList;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy118.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy118.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy118.pExpr);
- yygotominor.yy118.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy342.eOperator);
- if( yymsp[-3].minor.yy342.not ) yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy118.pExpr, 0, 0);
- yygotominor.yy118.zStart = yymsp[-4].minor.yy118.zStart;
- yygotominor.yy118.zEnd = yymsp[0].minor.yy118.zEnd;
- if( yygotominor.yy118.pExpr ) yygotominor.yy118.pExpr->flags |= EP_InfixFunc;
+ int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
+ yymsp[-3].minor.yy0.n &= 0x7fffffff;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy190.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr);
+ yymsp[-4].minor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0);
+ exprNot(pParse, bNot, &yymsp[-4].minor.yy190);
+ yymsp[-4].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
+ if( yymsp[-4].minor.yy190.pExpr ) yymsp[-4].minor.yy190.pExpr->flags |= EP_InfixFunc;
}
break;
- case 214: /* expr ::= expr ISNULL|NOTNULL */
-{spanUnaryPostfix(&yygotominor.yy118,pParse,yymsp[0].major,&yymsp[-1].minor.yy118,&yymsp[0].minor.yy0);}
+ case 178: /* expr ::= expr ISNULL|NOTNULL */
+{spanUnaryPostfix(pParse,yymsp[0].major,&yymsp[-1].minor.yy190,&yymsp[0].minor.yy0);}
break;
- case 215: /* expr ::= expr NOT NULL */
-{spanUnaryPostfix(&yygotominor.yy118,pParse,TK_NOTNULL,&yymsp[-2].minor.yy118,&yymsp[0].minor.yy0);}
+ case 179: /* expr ::= expr NOT NULL */
+{spanUnaryPostfix(pParse,TK_NOTNULL,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy0);}
break;
- case 216: /* expr ::= expr IS expr */
+ case 180: /* expr ::= expr IS expr */
{
- spanBinaryExpr(&yygotominor.yy118,pParse,TK_IS,&yymsp[-2].minor.yy118,&yymsp[0].minor.yy118);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy118.pExpr, yygotominor.yy118.pExpr, TK_ISNULL);
+ spanBinaryExpr(pParse,TK_IS,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy190);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy190.pExpr, yymsp[-2].minor.yy190.pExpr, TK_ISNULL);
}
break;
- case 217: /* expr ::= expr IS NOT expr */
+ case 181: /* expr ::= expr IS NOT expr */
{
- spanBinaryExpr(&yygotominor.yy118,pParse,TK_ISNOT,&yymsp[-3].minor.yy118,&yymsp[0].minor.yy118);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy118.pExpr, yygotominor.yy118.pExpr, TK_NOTNULL);
+ spanBinaryExpr(pParse,TK_ISNOT,&yymsp[-3].minor.yy190,&yymsp[0].minor.yy190);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy190.pExpr, yymsp[-3].minor.yy190.pExpr, TK_NOTNULL);
}
break;
- case 218: /* expr ::= NOT expr */
- case 219: /* expr ::= BITNOT expr */ yytestcase(yyruleno==219);
-{spanUnaryPrefix(&yygotominor.yy118,pParse,yymsp[-1].major,&yymsp[0].minor.yy118,&yymsp[-1].minor.yy0);}
+ case 182: /* expr ::= NOT expr */
+ case 183: /* expr ::= BITNOT expr */ yytestcase(yyruleno==183);
+{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,yymsp[-1].major,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
+ break;
+ case 184: /* expr ::= MINUS expr */
+{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,TK_UMINUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
break;
- case 220: /* expr ::= MINUS expr */
-{spanUnaryPrefix(&yygotominor.yy118,pParse,TK_UMINUS,&yymsp[0].minor.yy118,&yymsp[-1].minor.yy0);}
+ case 185: /* expr ::= PLUS expr */
+{spanUnaryPrefix(&yymsp[-1].minor.yy190,pParse,TK_UPLUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);/*A-overwrites-B*/}
break;
- case 221: /* expr ::= PLUS expr */
-{spanUnaryPrefix(&yygotominor.yy118,pParse,TK_UPLUS,&yymsp[0].minor.yy118,&yymsp[-1].minor.yy0);}
+ case 186: /* between_op ::= BETWEEN */
+ case 189: /* in_op ::= IN */ yytestcase(yyruleno==189);
+{yymsp[0].minor.yy194 = 0;}
break;
- case 224: /* expr ::= expr between_op expr AND expr */
+ case 188: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy118.pExpr);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy118.pExpr);
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy118.pExpr, 0, 0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr);
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy190.pExpr, 0, 0);
+ if( yymsp[-4].minor.yy190.pExpr ){
+ yymsp[-4].minor.yy190.pExpr->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy4 ) yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy118.pExpr, 0, 0);
- yygotominor.yy118.zStart = yymsp[-4].minor.yy118.zStart;
- yygotominor.yy118.zEnd = yymsp[0].minor.yy118.zEnd;
+ exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
+ yymsp[-4].minor.yy190.zEnd = yymsp[0].minor.yy190.zEnd;
}
break;
- case 227: /* expr ::= expr in_op LP exprlist RP */
+ case 191: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy322==0 ){
+ if( yymsp[-1].minor.yy148==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -108326,416 +135868,438 @@ static void yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[yymsp[-3].minor.yy4]);
- sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy118.pExpr);
+ sqlite3ExprDelete(pParse->db, yymsp[-4].minor.yy190.pExpr);
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &sqlite3IntTokens[yymsp[-3].minor.yy194]);
+ }else if( yymsp[-1].minor.yy148->nExpr==1 ){
+ /* Expressions of the form:
+ **
+ ** expr1 IN (?1)
+ ** expr1 NOT IN (?2)
+ **
+ ** with exactly one value on the RHS can be simplified to something
+ ** like this:
+ **
+ ** expr1 == ?1
+ ** expr1 <> ?2
+ **
+ ** But, the RHS of the == or <> is marked with the EP_Generic flag
+ ** so that it may not contribute to the computation of comparison
+ ** affinity or the collating sequence to use for comparison. Otherwise,
+ ** the semantics would be subtly different from IN or NOT IN.
+ */
+ Expr *pRHS = yymsp[-1].minor.yy148->a[0].pExpr;
+ yymsp[-1].minor.yy148->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy148);
+ /* pRHS cannot be NULL because a malloc error would have been detected
+ ** before now and control would have never reached this point */
+ if( ALWAYS(pRHS) ){
+ pRHS->flags &= ~EP_Collate;
+ pRHS->flags |= EP_Generic;
+ }
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, yymsp[-3].minor.yy194 ? TK_NE : TK_EQ, yymsp[-4].minor.yy190.pExpr, pRHS, 0);
}else{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy118.pExpr, 0, 0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->x.pList = yymsp[-1].minor.yy322;
- sqlite3ExprSetHeight(pParse, yygotominor.yy118.pExpr);
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0, 0);
+ if( yymsp[-4].minor.yy190.pExpr ){
+ yymsp[-4].minor.yy190.pExpr->x.pList = yymsp[-1].minor.yy148;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy190.pExpr);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy322);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy148);
}
- if( yymsp[-3].minor.yy4 ) yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy118.pExpr, 0, 0);
+ exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
}
- yygotominor.yy118.zStart = yymsp[-4].minor.yy118.zStart;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ yymsp[-4].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 228: /* expr ::= LP select RP */
+ case 192: /* expr ::= LP select RP */
{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->x.pSelect = yymsp[-1].minor.yy387;
- ExprSetProperty(yygotominor.yy118.pExpr, EP_xIsSelect);
- sqlite3ExprSetHeight(pParse, yygotominor.yy118.pExpr);
- }else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy387);
- }
- yygotominor.yy118.zStart = yymsp[-2].minor.yy0.z;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ spanSet(&yymsp[-2].minor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
+ yymsp[-2].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy190.pExpr, yymsp[-1].minor.yy243);
}
break;
- case 229: /* expr ::= expr in_op LP select RP */
+ case 193: /* expr ::= expr in_op LP select RP */
{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy118.pExpr, 0, 0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->x.pSelect = yymsp[-1].minor.yy387;
- ExprSetProperty(yygotominor.yy118.pExpr, EP_xIsSelect);
- sqlite3ExprSetHeight(pParse, yygotominor.yy118.pExpr);
- }else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy387);
- }
- if( yymsp[-3].minor.yy4 ) yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy118.pExpr, 0, 0);
- yygotominor.yy118.zStart = yymsp[-4].minor.yy118.zStart;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy190.pExpr, yymsp[-1].minor.yy243);
+ exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
+ yymsp[-4].minor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 230: /* expr ::= expr in_op nm dbnm */
+ case 194: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
- SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy118.pExpr, 0, 0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
- ExprSetProperty(yygotominor.yy118.pExpr, EP_xIsSelect);
- sqlite3ExprSetHeight(pParse, yygotominor.yy118.pExpr);
- }else{
- sqlite3SrcListDelete(pParse->db, pSrc);
- }
- if( yymsp[-2].minor.yy4 ) yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy118.pExpr, 0, 0);
- yygotominor.yy118.zStart = yymsp[-3].minor.yy118.zStart;
- yygotominor.yy118.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n];
+ SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
+ Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0);
+ if( yymsp[0].minor.yy148 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy148);
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy190.pExpr, pSelect);
+ exprNot(pParse, yymsp[-3].minor.yy194, &yymsp[-4].minor.yy190);
+ yymsp[-4].minor.yy190.zEnd = yymsp[-1].minor.yy0.z ? &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n] : &yymsp[-2].minor.yy0.z[yymsp[-2].minor.yy0.n];
}
break;
- case 231: /* expr ::= EXISTS LP select RP */
+ case 195: /* expr ::= EXISTS LP select RP */
{
- Expr *p = yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
- if( p ){
- p->x.pSelect = yymsp[-1].minor.yy387;
- ExprSetProperty(p, EP_xIsSelect);
- sqlite3ExprSetHeight(pParse, p);
- }else{
- sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy387);
- }
- yygotominor.yy118.zStart = yymsp[-3].minor.yy0.z;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
+ Expr *p;
+ spanSet(&yymsp[-3].minor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-B*/
+ p = yymsp[-3].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy243);
}
break;
- case 232: /* expr ::= CASE case_operand case_exprlist case_else END */
+ case 196: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy314, yymsp[-1].minor.yy314, 0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->x.pList = yymsp[-2].minor.yy322;
- sqlite3ExprSetHeight(pParse, yygotominor.yy118.pExpr);
+ spanSet(&yymsp[-4].minor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-C*/
+ yymsp[-4].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy72, 0, 0);
+ if( yymsp[-4].minor.yy190.pExpr ){
+ yymsp[-4].minor.yy190.pExpr->x.pList = yymsp[-1].minor.yy72 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy148,yymsp[-1].minor.yy72) : yymsp[-2].minor.yy148;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy190.pExpr);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy322);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy148);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy72);
}
- yygotominor.yy118.zStart = yymsp[-4].minor.yy0.z;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 233: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ case 197: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, yymsp[-2].minor.yy118.pExpr);
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,yygotominor.yy322, yymsp[0].minor.yy118.pExpr);
+ yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, yymsp[-2].minor.yy190.pExpr);
+ yymsp[-4].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, yymsp[0].minor.yy190.pExpr);
}
break;
- case 234: /* case_exprlist ::= WHEN expr THEN expr */
+ case 198: /* case_exprlist ::= WHEN expr THEN expr */
{
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy118.pExpr);
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,yygotominor.yy322, yymsp[0].minor.yy118.pExpr);
+ yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr);
+ yymsp[-3].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148, yymsp[0].minor.yy190.pExpr);
}
break;
- case 243: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP */
+ case 201: /* case_operand ::= expr */
+{yymsp[0].minor.yy72 = yymsp[0].minor.yy190.pExpr; /*A-overwrites-X*/}
+ break;
+ case 204: /* nexprlist ::= nexprlist COMMA expr */
+{yymsp[-2].minor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy148,yymsp[0].minor.yy190.pExpr);}
+ break;
+ case 205: /* nexprlist ::= expr */
+{yymsp[0].minor.yy148 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy190.pExpr); /*A-overwrites-Y*/}
+ break;
+ case 207: /* paren_exprlist ::= LP exprlist RP */
+ case 212: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==212);
+{yymsp[-2].minor.yy148 = yymsp[-1].minor.yy148;}
+ break;
+ case 208: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
- sqlite3CreateIndex(pParse, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0,
- sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy322, yymsp[-9].minor.yy4,
- &yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy4);
+ sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
+ sqlite3SrcListAppend(pParse->db,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy148, yymsp[-10].minor.yy194,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy72, SQLITE_SO_ASC, yymsp[-8].minor.yy194, SQLITE_IDXTYPE_APPDEF);
}
break;
- case 244: /* uniqueflag ::= UNIQUE */
- case 298: /* raisetype ::= ABORT */ yytestcase(yyruleno==298);
-{yygotominor.yy4 = OE_Abort;}
+ case 209: /* uniqueflag ::= UNIQUE */
+ case 250: /* raisetype ::= ABORT */ yytestcase(yyruleno==250);
+{yymsp[0].minor.yy194 = OE_Abort;}
break;
- case 245: /* uniqueflag ::= */
-{yygotominor.yy4 = OE_None;}
+ case 210: /* uniqueflag ::= */
+{yymsp[1].minor.yy194 = OE_None;}
break;
- case 248: /* idxlist ::= idxlist COMMA nm collate sortorder */
+ case 213: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- Expr *p = 0;
- if( yymsp[-1].minor.yy0.n>0 ){
- p = sqlite3Expr(pParse->db, TK_COLUMN, 0);
- sqlite3ExprSetCollByToken(pParse, p, &yymsp[-1].minor.yy0);
- }
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy322, p);
- sqlite3ExprListSetName(pParse,yygotominor.yy322,&yymsp[-2].minor.yy0,1);
- sqlite3ExprListCheckLength(pParse, yygotominor.yy322, "index");
- if( yygotominor.yy322 ) yygotominor.yy322->a[yygotominor.yy322->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy4;
+ yymsp[-4].minor.yy148 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy148, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy194, yymsp[0].minor.yy194);
}
break;
- case 249: /* idxlist ::= nm collate sortorder */
+ case 214: /* eidlist ::= nm collate sortorder */
{
- Expr *p = 0;
- if( yymsp[-1].minor.yy0.n>0 ){
- p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0);
- sqlite3ExprSetCollByToken(pParse, p, &yymsp[-1].minor.yy0);
- }
- yygotominor.yy322 = sqlite3ExprListAppend(pParse,0, p);
- sqlite3ExprListSetName(pParse, yygotominor.yy322, &yymsp[-2].minor.yy0, 1);
- sqlite3ExprListCheckLength(pParse, yygotominor.yy322, "index");
- if( yygotominor.yy322 ) yygotominor.yy322->a[yygotominor.yy322->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy4;
+ yymsp[-2].minor.yy148 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy194, yymsp[0].minor.yy194); /*A-overwrites-Y*/
}
break;
- case 250: /* collate ::= */
-{yygotominor.yy0.z = 0; yygotominor.yy0.n = 0;}
+ case 217: /* cmd ::= DROP INDEX ifexists fullname */
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy185, yymsp[-1].minor.yy194);}
break;
- case 252: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy259, yymsp[-1].minor.yy4);}
+ case 218: /* cmd ::= VACUUM */
+{sqlite3Vacuum(pParse,0);}
break;
- case 253: /* cmd ::= VACUUM */
- case 254: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==254);
-{sqlite3Vacuum(pParse);}
+ case 219: /* cmd ::= VACUUM nm */
+{sqlite3Vacuum(pParse,&yymsp[0].minor.yy0);}
break;
- case 255: /* cmd ::= PRAGMA nm dbnm */
+ case 220: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
break;
- case 256: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
+ case 221: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
break;
- case 257: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ case 222: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
break;
- case 258: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
+ case 223: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
break;
- case 259: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ case 224: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
break;
- case 270: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ case 227: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy203, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy145, &all);
}
break;
- case 271: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ case 228: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy4, yymsp[-4].minor.yy90.a, yymsp[-4].minor.yy90.b, yymsp[-2].minor.yy259, yymsp[0].minor.yy314, yymsp[-10].minor.yy4, yymsp[-8].minor.yy4);
- yygotominor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy194, yymsp[-4].minor.yy332.a, yymsp[-4].minor.yy332.b, yymsp[-2].minor.yy185, yymsp[0].minor.yy72, yymsp[-10].minor.yy194, yymsp[-8].minor.yy194);
+ yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
break;
- case 272: /* trigger_time ::= BEFORE */
- case 275: /* trigger_time ::= */ yytestcase(yyruleno==275);
-{ yygotominor.yy4 = TK_BEFORE; }
+ case 229: /* trigger_time ::= BEFORE */
+{ yymsp[0].minor.yy194 = TK_BEFORE; }
break;
- case 273: /* trigger_time ::= AFTER */
-{ yygotominor.yy4 = TK_AFTER; }
+ case 230: /* trigger_time ::= AFTER */
+{ yymsp[0].minor.yy194 = TK_AFTER; }
break;
- case 274: /* trigger_time ::= INSTEAD OF */
-{ yygotominor.yy4 = TK_INSTEAD;}
+ case 231: /* trigger_time ::= INSTEAD OF */
+{ yymsp[-1].minor.yy194 = TK_INSTEAD;}
break;
- case 276: /* trigger_event ::= DELETE|INSERT */
- case 277: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==277);
-{yygotominor.yy90.a = yymsp[0].major; yygotominor.yy90.b = 0;}
+ case 232: /* trigger_time ::= */
+{ yymsp[1].minor.yy194 = TK_BEFORE; }
break;
- case 278: /* trigger_event ::= UPDATE OF inscollist */
-{yygotominor.yy90.a = TK_UPDATE; yygotominor.yy90.b = yymsp[0].minor.yy384;}
+ case 233: /* trigger_event ::= DELETE|INSERT */
+ case 234: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==234);
+{yymsp[0].minor.yy332.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy332.b = 0;}
break;
- case 281: /* when_clause ::= */
- case 303: /* key_opt ::= */ yytestcase(yyruleno==303);
-{ yygotominor.yy314 = 0; }
+ case 235: /* trigger_event ::= UPDATE OF idlist */
+{yymsp[-2].minor.yy332.a = TK_UPDATE; yymsp[-2].minor.yy332.b = yymsp[0].minor.yy254;}
break;
- case 282: /* when_clause ::= WHEN expr */
- case 304: /* key_opt ::= KEY expr */ yytestcase(yyruleno==304);
-{ yygotominor.yy314 = yymsp[0].minor.yy118.pExpr; }
+ case 236: /* when_clause ::= */
+ case 255: /* key_opt ::= */ yytestcase(yyruleno==255);
+{ yymsp[1].minor.yy72 = 0; }
break;
- case 283: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ case 237: /* when_clause ::= WHEN expr */
+ case 256: /* key_opt ::= KEY expr */ yytestcase(yyruleno==256);
+{ yymsp[-1].minor.yy72 = yymsp[0].minor.yy190.pExpr; }
+ break;
+ case 238: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy203!=0 );
- yymsp[-2].minor.yy203->pLast->pNext = yymsp[-1].minor.yy203;
- yymsp[-2].minor.yy203->pLast = yymsp[-1].minor.yy203;
- yygotominor.yy203 = yymsp[-2].minor.yy203;
+ assert( yymsp[-2].minor.yy145!=0 );
+ yymsp[-2].minor.yy145->pLast->pNext = yymsp[-1].minor.yy145;
+ yymsp[-2].minor.yy145->pLast = yymsp[-1].minor.yy145;
}
break;
- case 284: /* trigger_cmd_list ::= trigger_cmd SEMI */
+ case 239: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy203!=0 );
- yymsp[-1].minor.yy203->pLast = yymsp[-1].minor.yy203;
- yygotominor.yy203 = yymsp[-1].minor.yy203;
+ assert( yymsp[-1].minor.yy145!=0 );
+ yymsp[-1].minor.yy145->pLast = yymsp[-1].minor.yy145;
}
break;
- case 286: /* trnm ::= nm DOT nm */
+ case 240: /* trnm ::= nm DOT nm */
{
- yygotominor.yy0 = yymsp[0].minor.yy0;
+ yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
sqlite3ErrorMsg(pParse,
"qualified table names are not allowed on INSERT, UPDATE, and DELETE "
"statements within triggers");
}
break;
- case 288: /* tridxby ::= INDEXED BY nm */
+ case 241: /* tridxby ::= INDEXED BY nm */
{
sqlite3ErrorMsg(pParse,
"the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 289: /* tridxby ::= NOT INDEXED */
+ case 242: /* tridxby ::= NOT INDEXED */
{
sqlite3ErrorMsg(pParse,
"the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
"within triggers");
}
break;
- case 290: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */
-{ yygotominor.yy203 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy322, yymsp[0].minor.yy314, yymsp[-5].minor.yy210); }
- break;
- case 291: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt VALUES LP itemlist RP */
-{yygotominor.yy203 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy384, yymsp[-1].minor.yy322, 0, yymsp[-7].minor.yy210);}
+ case 243: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */
+{yymsp[-6].minor.yy145 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy148, yymsp[0].minor.yy72, yymsp[-5].minor.yy194);}
break;
- case 292: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select */
-{yygotominor.yy203 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy384, 0, yymsp[0].minor.yy387, yymsp[-4].minor.yy210);}
+ case 244: /* trigger_cmd ::= insert_cmd INTO trnm idlist_opt select */
+{yymsp[-4].minor.yy145 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy254, yymsp[0].minor.yy243, yymsp[-4].minor.yy194);/*A-overwrites-R*/}
break;
- case 293: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */
-{yygotominor.yy203 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy314);}
+ case 245: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */
+{yymsp[-4].minor.yy145 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy72);}
break;
- case 294: /* trigger_cmd ::= select */
-{yygotominor.yy203 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy387); }
+ case 246: /* trigger_cmd ::= select */
+{yymsp[0].minor.yy145 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy243); /*A-overwrites-X*/}
break;
- case 295: /* expr ::= RAISE LP IGNORE RP */
+ case 247: /* expr ::= RAISE LP IGNORE RP */
{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
- if( yygotominor.yy118.pExpr ){
- yygotominor.yy118.pExpr->affinity = OE_Ignore;
+ spanSet(&yymsp[-3].minor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-3].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0);
+ if( yymsp[-3].minor.yy190.pExpr ){
+ yymsp[-3].minor.yy190.pExpr->affinity = OE_Ignore;
}
- yygotominor.yy118.zStart = yymsp[-3].minor.yy0.z;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 296: /* expr ::= RAISE LP raisetype COMMA nm RP */
+ case 248: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
- yygotominor.yy118.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
- if( yygotominor.yy118.pExpr ) {
- yygotominor.yy118.pExpr->affinity = (char)yymsp[-3].minor.yy4;
+ spanSet(&yymsp[-5].minor.yy190,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/
+ yymsp[-5].minor.yy190.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0);
+ if( yymsp[-5].minor.yy190.pExpr ) {
+ yymsp[-5].minor.yy190.pExpr->affinity = (char)yymsp[-3].minor.yy194;
}
- yygotominor.yy118.zStart = yymsp[-5].minor.yy0.z;
- yygotominor.yy118.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n];
}
break;
- case 297: /* raisetype ::= ROLLBACK */
-{yygotominor.yy4 = OE_Rollback;}
+ case 249: /* raisetype ::= ROLLBACK */
+{yymsp[0].minor.yy194 = OE_Rollback;}
break;
- case 299: /* raisetype ::= FAIL */
-{yygotominor.yy4 = OE_Fail;}
+ case 251: /* raisetype ::= FAIL */
+{yymsp[0].minor.yy194 = OE_Fail;}
break;
- case 300: /* cmd ::= DROP TRIGGER ifexists fullname */
+ case 252: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy259,yymsp[-1].minor.yy4);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy185,yymsp[-1].minor.yy194);
}
break;
- case 301: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ case 253: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy118.pExpr, yymsp[-1].minor.yy118.pExpr, yymsp[0].minor.yy314);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy190.pExpr, yymsp[-1].minor.yy190.pExpr, yymsp[0].minor.yy72);
}
break;
- case 302: /* cmd ::= DETACH database_kw_opt expr */
+ case 254: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy118.pExpr);
+ sqlite3Detach(pParse, yymsp[0].minor.yy190.pExpr);
}
break;
- case 307: /* cmd ::= REINDEX */
+ case 257: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
break;
- case 308: /* cmd ::= REINDEX nm dbnm */
+ case 258: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 309: /* cmd ::= ANALYZE */
+ case 259: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
break;
- case 310: /* cmd ::= ANALYZE nm dbnm */
+ case 260: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
break;
- case 311: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
+ case 261: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy259,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy185,&yymsp[0].minor.yy0);
}
break;
- case 312: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */
+ case 262: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
- sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy0);
+ yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
+ sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
break;
- case 313: /* add_column_fullname ::= fullname */
+ case 263: /* add_column_fullname ::= fullname */
{
- pParse->db->lookaside.bEnabled = 0;
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy259);
+ disableLookaside(pParse);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy185);
}
break;
- case 316: /* cmd ::= create_vtab */
+ case 264: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
break;
- case 317: /* cmd ::= create_vtab LP vtabarglist RP */
+ case 265: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
break;
- case 318: /* create_vtab ::= createkw VIRTUAL TABLE nm dbnm USING nm */
+ case 266: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy194);
}
break;
- case 321: /* vtabarg ::= */
+ case 267: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
break;
- case 323: /* vtabargtoken ::= ANY */
- case 324: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==324);
- case 325: /* lp ::= LP */ yytestcase(yyruleno==325);
+ case 268: /* vtabargtoken ::= ANY */
+ case 269: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==269);
+ case 270: /* lp ::= LP */ yytestcase(yyruleno==270);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
break;
+ case 271: /* with ::= */
+{yymsp[1].minor.yy285 = 0;}
+ break;
+ case 272: /* with ::= WITH wqlist */
+{ yymsp[-1].minor.yy285 = yymsp[0].minor.yy285; }
+ break;
+ case 273: /* with ::= WITH RECURSIVE wqlist */
+{ yymsp[-2].minor.yy285 = yymsp[0].minor.yy285; }
+ break;
+ case 274: /* wqlist ::= nm eidlist_opt AS LP select RP */
+{
+ yymsp[-5].minor.yy285 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy148, yymsp[-1].minor.yy243); /*A-overwrites-X*/
+}
+ break;
+ case 275: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
+{
+ yymsp[-7].minor.yy285 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy285, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy148, yymsp[-1].minor.yy243);
+}
+ break;
default:
- /* (0) input ::= cmdlist */ yytestcase(yyruleno==0);
- /* (1) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==1);
- /* (2) cmdlist ::= ecmd */ yytestcase(yyruleno==2);
- /* (3) ecmd ::= SEMI */ yytestcase(yyruleno==3);
- /* (4) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==4);
- /* (10) trans_opt ::= */ yytestcase(yyruleno==10);
- /* (11) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==11);
- /* (12) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==12);
- /* (20) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==20);
- /* (21) savepoint_opt ::= */ yytestcase(yyruleno==21);
- /* (25) cmd ::= create_table create_table_args */ yytestcase(yyruleno==25);
- /* (34) columnlist ::= columnlist COMMA column */ yytestcase(yyruleno==34);
- /* (35) columnlist ::= column */ yytestcase(yyruleno==35);
- /* (44) type ::= */ yytestcase(yyruleno==44);
- /* (51) signed ::= plus_num */ yytestcase(yyruleno==51);
- /* (52) signed ::= minus_num */ yytestcase(yyruleno==52);
- /* (53) carglist ::= carglist carg */ yytestcase(yyruleno==53);
- /* (54) carglist ::= */ yytestcase(yyruleno==54);
- /* (55) carg ::= CONSTRAINT nm ccons */ yytestcase(yyruleno==55);
- /* (56) carg ::= ccons */ yytestcase(yyruleno==56);
- /* (62) ccons ::= NULL onconf */ yytestcase(yyruleno==62);
- /* (90) conslist ::= conslist COMMA tcons */ yytestcase(yyruleno==90);
- /* (91) conslist ::= conslist tcons */ yytestcase(yyruleno==91);
- /* (92) conslist ::= tcons */ yytestcase(yyruleno==92);
- /* (93) tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==93);
- /* (268) plus_opt ::= PLUS */ yytestcase(yyruleno==268);
- /* (269) plus_opt ::= */ yytestcase(yyruleno==269);
- /* (279) foreach_clause ::= */ yytestcase(yyruleno==279);
- /* (280) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==280);
- /* (287) tridxby ::= */ yytestcase(yyruleno==287);
- /* (305) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==305);
- /* (306) database_kw_opt ::= */ yytestcase(yyruleno==306);
- /* (314) kwcolumn_opt ::= */ yytestcase(yyruleno==314);
- /* (315) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==315);
- /* (319) vtabarglist ::= vtabarg */ yytestcase(yyruleno==319);
- /* (320) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==320);
- /* (322) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==322);
- /* (326) anylist ::= */ yytestcase(yyruleno==326);
- /* (327) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==327);
- /* (328) anylist ::= anylist ANY */ yytestcase(yyruleno==328);
+ /* (276) input ::= cmdlist */ yytestcase(yyruleno==276);
+ /* (277) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==277);
+ /* (278) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=278);
+ /* (279) ecmd ::= SEMI */ yytestcase(yyruleno==279);
+ /* (280) ecmd ::= explain cmdx SEMI */ yytestcase(yyruleno==280);
+ /* (281) explain ::= */ yytestcase(yyruleno==281);
+ /* (282) trans_opt ::= */ yytestcase(yyruleno==282);
+ /* (283) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==283);
+ /* (284) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==284);
+ /* (285) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==285);
+ /* (286) savepoint_opt ::= */ yytestcase(yyruleno==286);
+ /* (287) cmd ::= create_table create_table_args */ yytestcase(yyruleno==287);
+ /* (288) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==288);
+ /* (289) columnlist ::= columnname carglist */ yytestcase(yyruleno==289);
+ /* (290) nm ::= ID|INDEXED */ yytestcase(yyruleno==290);
+ /* (291) nm ::= STRING */ yytestcase(yyruleno==291);
+ /* (292) nm ::= JOIN_KW */ yytestcase(yyruleno==292);
+ /* (293) typetoken ::= typename */ yytestcase(yyruleno==293);
+ /* (294) typename ::= ID|STRING */ yytestcase(yyruleno==294);
+ /* (295) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=295);
+ /* (296) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=296);
+ /* (297) carglist ::= carglist ccons */ yytestcase(yyruleno==297);
+ /* (298) carglist ::= */ yytestcase(yyruleno==298);
+ /* (299) ccons ::= NULL onconf */ yytestcase(yyruleno==299);
+ /* (300) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==300);
+ /* (301) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==301);
+ /* (302) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=302);
+ /* (303) tconscomma ::= */ yytestcase(yyruleno==303);
+ /* (304) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=304);
+ /* (305) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=305);
+ /* (306) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=306);
+ /* (307) oneselect ::= values */ yytestcase(yyruleno==307);
+ /* (308) sclp ::= selcollist COMMA */ yytestcase(yyruleno==308);
+ /* (309) as ::= ID|STRING */ yytestcase(yyruleno==309);
+ /* (310) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=310);
+ /* (311) exprlist ::= nexprlist */ yytestcase(yyruleno==311);
+ /* (312) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=312);
+ /* (313) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=313);
+ /* (314) nmnum ::= ON */ yytestcase(yyruleno==314);
+ /* (315) nmnum ::= DELETE */ yytestcase(yyruleno==315);
+ /* (316) nmnum ::= DEFAULT */ yytestcase(yyruleno==316);
+ /* (317) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==317);
+ /* (318) foreach_clause ::= */ yytestcase(yyruleno==318);
+ /* (319) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==319);
+ /* (320) trnm ::= nm */ yytestcase(yyruleno==320);
+ /* (321) tridxby ::= */ yytestcase(yyruleno==321);
+ /* (322) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==322);
+ /* (323) database_kw_opt ::= */ yytestcase(yyruleno==323);
+ /* (324) kwcolumn_opt ::= */ yytestcase(yyruleno==324);
+ /* (325) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==325);
+ /* (326) vtabarglist ::= vtabarg */ yytestcase(yyruleno==326);
+ /* (327) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==327);
+ /* (328) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==328);
+ /* (329) anylist ::= */ yytestcase(yyruleno==329);
+ /* (330) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==330);
+ /* (331) anylist ::= anylist ANY */ yytestcase(yyruleno==331);
break;
+/********** End reduce actions ************************************************/
};
+ assert( yyruleno<sizeof(yyRuleInfo)/sizeof(yyRuleInfo[0]) );
yygoto = yyRuleInfo[yyruleno].lhs;
yysize = yyRuleInfo[yyruleno].nrhs;
- yypParser->yyidx -= yysize;
yyact = yy_find_reduce_action(yymsp[-yysize].stateno,(YYCODETYPE)yygoto);
- if( yyact < YYNSTATE ){
-#ifdef NDEBUG
- /* If we are not debugging and the reduce action popped at least
- ** one element off the stack, then we can push the new element back
- ** onto the stack here, and skip the stack overflow test in yy_shift().
- ** That gives a significant speed improvement. */
- if( yysize ){
- yypParser->yyidx++;
- yymsp -= yysize-1;
- yymsp->stateno = (YYACTIONTYPE)yyact;
- yymsp->major = (YYCODETYPE)yygoto;
- yymsp->minor = yygotominor;
- }else
-#endif
- {
- yy_shift(yypParser,yyact,yygoto,&yygotominor);
- }
+ if( yyact <= YY_MAX_SHIFTREDUCE ){
+ if( yyact>YY_MAX_SHIFT ){
+ yyact += YY_MIN_REDUCE - YY_MIN_SHIFTREDUCE;
+ }
+ yymsp -= yysize-1;
+ yypParser->yytos = yymsp;
+ yymsp->stateno = (YYACTIONTYPE)yyact;
+ yymsp->major = (YYCODETYPE)yygoto;
+ yyTraceShift(yypParser, yyact);
}else{
- assert( yyact == YYNSTATE + YYNRULE + 1 );
+ assert( yyact == YY_ACCEPT_ACTION );
+ yypParser->yytos -= yysize;
yy_accept(yypParser);
}
}
@@ -108753,9 +136317,11 @@ static void yy_parse_failed(
fprintf(yyTraceFILE,"%sFail!\n",yyTracePrompt);
}
#endif
- while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+ while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
/* Here code is inserted which will be executed whenever the
** parser fails */
+/************ Begin %parse_failure code ***************************************/
+/************ End %parse_failure code *****************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
}
#endif /* YYNOERRORRECOVERY */
@@ -108766,15 +136332,16 @@ static void yy_parse_failed(
static void yy_syntax_error(
yyParser *yypParser, /* The parser */
int yymajor, /* The major type of the error token */
- YYMINORTYPE yyminor /* The minor type of the error token */
+ sqlite3ParserTOKENTYPE yyminor /* The minor type of the error token */
){
sqlite3ParserARG_FETCH;
-#define TOKEN (yyminor.yy0)
+#define TOKEN yyminor
+/************ Begin %syntax_error code ****************************************/
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
assert( TOKEN.z[0] ); /* The tokenizer always gives us a token */
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
- pParse->parseError = 1;
+/************ End %syntax_error code ******************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
}
@@ -108790,9 +136357,14 @@ static void yy_accept(
fprintf(yyTraceFILE,"%sAccept!\n",yyTracePrompt);
}
#endif
- while( yypParser->yyidx>=0 ) yy_pop_parser_stack(yypParser);
+#ifndef YYNOERRORRECOVERY
+ yypParser->yyerrcnt = -1;
+#endif
+ assert( yypParser->yytos==yypParser->yystack );
/* Here code is inserted which will be executed whenever the
** parser accepts */
+/*********** Begin %parse_accept code *****************************************/
+/*********** End %parse_accept code *******************************************/
sqlite3ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
}
@@ -108822,50 +136394,41 @@ SQLITE_PRIVATE void sqlite3Parser(
sqlite3ParserARG_PDECL /* Optional %extra_argument parameter */
){
YYMINORTYPE yyminorunion;
- int yyact; /* The parser action. */
+ unsigned int yyact; /* The parser action. */
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
int yyendofinput; /* True if we are at the end of input */
+#endif
#ifdef YYERRORSYMBOL
int yyerrorhit = 0; /* True if yymajor has invoked an error */
#endif
yyParser *yypParser; /* The parser */
- /* (re)initialize the parser, if necessary */
yypParser = (yyParser*)yyp;
- if( yypParser->yyidx<0 ){
-#if YYSTACKDEPTH<=0
- if( yypParser->yystksz <=0 ){
- /*memset(&yyminorunion, 0, sizeof(yyminorunion));*/
- yyminorunion = yyzerominor;
- yyStackOverflow(yypParser, &yyminorunion);
- return;
- }
-#endif
- yypParser->yyidx = 0;
- yypParser->yyerrcnt = -1;
- yypParser->yystack[0].stateno = 0;
- yypParser->yystack[0].major = 0;
- }
- yyminorunion.yy0 = yyminor;
+ assert( yypParser->yytos!=0 );
+#if !defined(YYERRORSYMBOL) && !defined(YYNOERRORRECOVERY)
yyendofinput = (yymajor==0);
+#endif
sqlite3ParserARG_STORE;
#ifndef NDEBUG
if( yyTraceFILE ){
- fprintf(yyTraceFILE,"%sInput %s\n",yyTracePrompt,yyTokenName[yymajor]);
+ fprintf(yyTraceFILE,"%sInput '%s'\n",yyTracePrompt,yyTokenName[yymajor]);
}
#endif
do{
yyact = yy_find_shift_action(yypParser,(YYCODETYPE)yymajor);
- if( yyact<YYNSTATE ){
- assert( !yyendofinput ); /* Impossible to shift the $ token */
- yy_shift(yypParser,yyact,yymajor,&yyminorunion);
+ if( yyact <= YY_MAX_SHIFTREDUCE ){
+ yy_shift(yypParser,yyact,yymajor,yyminor);
+#ifndef YYNOERRORRECOVERY
yypParser->yyerrcnt--;
+#endif
yymajor = YYNOCODE;
- }else if( yyact < YYNSTATE + YYNRULE ){
- yy_reduce(yypParser,yyact-YYNSTATE);
+ }else if( yyact <= YY_MAX_REDUCE ){
+ yy_reduce(yypParser,yyact-YY_MIN_REDUCE);
}else{
assert( yyact == YY_ERROR_ACTION );
+ yyminorunion.yy0 = yyminor;
#ifdef YYERRORSYMBOL
int yymx;
#endif
@@ -108895,9 +136458,9 @@ SQLITE_PRIVATE void sqlite3Parser(
**
*/
if( yypParser->yyerrcnt<0 ){
- yy_syntax_error(yypParser,yymajor,yyminorunion);
+ yy_syntax_error(yypParser,yymajor,yyminor);
}
- yymx = yypParser->yystack[yypParser->yyidx].major;
+ yymx = yypParser->yytos->major;
if( yymx==YYERRORSYMBOL || yyerrorhit ){
#ifndef NDEBUG
if( yyTraceFILE ){
@@ -108905,26 +136468,26 @@ SQLITE_PRIVATE void sqlite3Parser(
yyTracePrompt,yyTokenName[yymajor]);
}
#endif
- yy_destructor(yypParser, (YYCODETYPE)yymajor,&yyminorunion);
+ yy_destructor(yypParser, (YYCODETYPE)yymajor, &yyminorunion);
yymajor = YYNOCODE;
}else{
- while(
- yypParser->yyidx >= 0 &&
- yymx != YYERRORSYMBOL &&
- (yyact = yy_find_reduce_action(
- yypParser->yystack[yypParser->yyidx].stateno,
- YYERRORSYMBOL)) >= YYNSTATE
+ while( yypParser->yytos >= yypParser->yystack
+ && yymx != YYERRORSYMBOL
+ && (yyact = yy_find_reduce_action(
+ yypParser->yytos->stateno,
+ YYERRORSYMBOL)) >= YY_MIN_REDUCE
){
yy_pop_parser_stack(yypParser);
}
- if( yypParser->yyidx < 0 || yymajor==0 ){
+ if( yypParser->yytos < yypParser->yystack || yymajor==0 ){
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
yy_parse_failed(yypParser);
+#ifndef YYNOERRORRECOVERY
+ yypParser->yyerrcnt = -1;
+#endif
yymajor = YYNOCODE;
}else if( yymx!=YYERRORSYMBOL ){
- YYMINORTYPE u2;
- u2.YYERRSYMDT = 0;
- yy_shift(yypParser,yyact,YYERRORSYMBOL,&u2);
+ yy_shift(yypParser,yyact,YYERRORSYMBOL,yyminor);
}
}
yypParser->yyerrcnt = 3;
@@ -108937,7 +136500,7 @@ SQLITE_PRIVATE void sqlite3Parser(
** Applications can set this macro (for example inside %include) if
** they intend to abandon the parse upon the first syntax error seen.
*/
- yy_syntax_error(yypParser,yymajor,yyminorunion);
+ yy_syntax_error(yypParser,yymajor, yyminor);
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
yymajor = YYNOCODE;
@@ -108952,17 +136515,32 @@ SQLITE_PRIVATE void sqlite3Parser(
** three input tokens have been successfully shifted.
*/
if( yypParser->yyerrcnt<=0 ){
- yy_syntax_error(yypParser,yymajor,yyminorunion);
+ yy_syntax_error(yypParser,yymajor, yyminor);
}
yypParser->yyerrcnt = 3;
yy_destructor(yypParser,(YYCODETYPE)yymajor,&yyminorunion);
if( yyendofinput ){
yy_parse_failed(yypParser);
+#ifndef YYNOERRORRECOVERY
+ yypParser->yyerrcnt = -1;
+#endif
}
yymajor = YYNOCODE;
#endif
}
- }while( yymajor!=YYNOCODE && yypParser->yyidx>=0 );
+ }while( yymajor!=YYNOCODE && yypParser->yytos>yypParser->yystack );
+#ifndef NDEBUG
+ if( yyTraceFILE ){
+ yyStackEntry *i;
+ char cDiv = '[';
+ fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
+ for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
+ fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
+ cDiv = ' ';
+ }
+ fprintf(yyTraceFILE,"]\n");
+ }
+#endif
return;
}
@@ -108985,14 +136563,95 @@ SQLITE_PRIVATE void sqlite3Parser(
** individual tokens and sends those tokens one-by-one over to the
** parser for analysis.
*/
+/* #include "sqliteInt.h" */
/* #include <stdlib.h> */
+/* Character classes for tokenizing
+**
+** In the sqlite3GetToken() function, a switch() on aiClass[c] is implemented
+** using a lookup table, whereas a switch() directly on c uses a binary search.
+** The lookup table is much faster. To maximize speed, and to ensure that
+** a lookup table is used, all of the classes need to be small integers and
+** all of them need to be used within the switch.
+*/
+#define CC_X 0 /* The letter 'x', or start of BLOB literal */
+#define CC_KYWD 1 /* Alphabetics or '_'. Usable in a keyword */
+#define CC_ID 2 /* unicode characters usable in IDs */
+#define CC_DIGIT 3 /* Digits */
+#define CC_DOLLAR 4 /* '$' */
+#define CC_VARALPHA 5 /* '@', '#', ':'. Alphabetic SQL variables */
+#define CC_VARNUM 6 /* '?'. Numeric SQL variables */
+#define CC_SPACE 7 /* Space characters */
+#define CC_QUOTE 8 /* '"', '\'', or '`'. String literals, quoted ids */
+#define CC_QUOTE2 9 /* '['. [...] style quoted ids */
+#define CC_PIPE 10 /* '|'. Bitwise OR or concatenate */
+#define CC_MINUS 11 /* '-'. Minus or SQL-style comment */
+#define CC_LT 12 /* '<'. Part of < or <= or <> */
+#define CC_GT 13 /* '>'. Part of > or >= */
+#define CC_EQ 14 /* '='. Part of = or == */
+#define CC_BANG 15 /* '!'. Part of != */
+#define CC_SLASH 16 /* '/'. / or c-style comment */
+#define CC_LP 17 /* '(' */
+#define CC_RP 18 /* ')' */
+#define CC_SEMI 19 /* ';' */
+#define CC_PLUS 20 /* '+' */
+#define CC_STAR 21 /* '*' */
+#define CC_PERCENT 22 /* '%' */
+#define CC_COMMA 23 /* ',' */
+#define CC_AND 24 /* '&' */
+#define CC_TILDA 25 /* '~' */
+#define CC_DOT 26 /* '.' */
+#define CC_ILLEGAL 27 /* Illegal character */
+
+static const unsigned char aiClass[] = {
+#ifdef SQLITE_ASCII
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 7, 7, 27, 7, 7, 27, 27,
+/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 2x */ 7, 15, 8, 5, 4, 22, 24, 8, 17, 18, 21, 20, 23, 11, 26, 16,
+/* 3x */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 19, 12, 14, 13, 6,
+/* 4x */ 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 5x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 9, 27, 27, 27, 1,
+/* 6x */ 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+/* 7x */ 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 10, 27, 25, 27,
+/* 8x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* 9x */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Ax */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Bx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Cx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Dx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Ex */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+/* Fx */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
+#endif
+#ifdef SQLITE_EBCDIC
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf */
+/* 0x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 7, 7, 27, 27,
+/* 1x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 2x */ 27, 27, 27, 27, 27, 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 3x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
+/* 4x */ 7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 12, 17, 20, 10,
+/* 5x */ 24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15, 4, 21, 18, 19, 27,
+/* 6x */ 11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22, 1, 13, 7,
+/* 7x */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 8, 5, 5, 5, 8, 14, 8,
+/* 8x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* 9x */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* 9x */ 25, 1, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Bx */ 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 9, 27, 27, 27, 27, 27,
+/* Cx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Dx */ 27, 1, 1, 1, 1, 1, 1, 1, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Ex */ 27, 27, 1, 1, 1, 1, 1, 0, 1, 1, 27, 27, 27, 27, 27, 27,
+/* Fx */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 27, 27, 27, 27, 27, 27,
+#endif
+};
+
/*
-** The charMap() macro maps alphabetic characters into their
+** The charMap() macro maps alphabetic characters (only) into their
** lower-case ASCII equivalent. On ASCII machines, this is just
** an upper-to-lower case map. On EBCDIC machines we also need
-** to adjust the encoding. Only alphabetic characters and underscores
-** need to be translated.
+** to adjust the encoding. The mapping is only valid for alphabetics
+** which are the only characters for which this feature is used.
+**
+** Used by keywordhash.h
*/
#ifdef SQLITE_ASCII
# define charMap(X) sqlite3UpperToLower[(unsigned char)X]
@@ -109026,7 +136685,7 @@ const unsigned char ebcdicToAscii[] = {
** returned. If the input is not a keyword, TK_ID is returned.
**
** The implementation of this routine was generated by a program,
-** mkkeywordhash.h, located in the tool subdirectory of the distribution.
+** mkkeywordhash.c, located in the tool subdirectory of the distribution.
** The output of the mkkeywordhash.c program is written into a file
** named keywordhash.h and then included into this source file by
** the #include below.
@@ -109046,20 +136705,20 @@ const unsigned char ebcdicToAscii[] = {
** is substantially reduced. This is important for embedded applications
** on platforms with limited memory.
*/
-/* Hash score: 175 */
-static int keywordCode(const char *z, int n){
- /* zText[] encodes 811 bytes of keywords in 541 bytes */
+/* Hash score: 182 */
+static int keywordCode(const char *z, int n, int *pType){
+ /* zText[] encodes 834 bytes of keywords in 554 bytes */
/* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */
/* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */
/* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */
- /* UNIQUERYATTACHAVINGROUPDATEBEGINNERELEASEBETWEENOTNULLIKE */
- /* CASCADELETECASECOLLATECREATECURRENT_DATEDETACHIMMEDIATEJOIN */
- /* SERTMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMITWHENWHERENAME */
- /* AFTEREPLACEANDEFAULTAUTOINCREMENTCASTCOLUMNCOMMITCONFLICTCROSS */
- /* CURRENT_TIMESTAMPRIMARYDEFERREDISTINCTDROPFAILFROMFULLGLOBYIF */
- /* ISNULLORDERESTRICTOUTERIGHTROLLBACKROWUNIONUSINGVACUUMVIEW */
- /* INITIALLY */
- static const char zText[540] = {
+ /* UNIQUERYWITHOUTERELEASEATTACHAVINGROUPDATEBEGINNERECURSIVE */
+ /* BETWEENOTNULLIKECASCADELETECASECOLLATECREATECURRENT_DATEDETACH */
+ /* IMMEDIATEJOINSERTMATCHPLANALYZEPRAGMABORTVALUESVIRTUALIMITWHEN */
+ /* WHERENAMEAFTEREPLACEANDEFAULTAUTOINCREMENTCASTCOLUMNCOMMIT */
+ /* CONFLICTCROSSCURRENT_TIMESTAMPRIMARYDEFERREDISTINCTDROPFAIL */
+ /* FROMFULLGLOBYIFISNULLORDERESTRICTRIGHTROLLBACKROWUNIONUSING */
+ /* VACUUMVIEWINITIALLY */
+ static const char zText[553] = {
'R','E','I','N','D','E','X','E','D','E','S','C','A','P','E','A','C','H',
'E','C','K','E','Y','B','E','F','O','R','E','I','G','N','O','R','E','G',
'E','X','P','L','A','I','N','S','T','E','A','D','D','A','T','A','B','A',
@@ -109070,76 +136729,77 @@ static int keywordCode(const char *z, int n){
'P','O','I','N','T','E','R','S','E','C','T','R','I','G','G','E','R','E',
'F','E','R','E','N','C','E','S','C','O','N','S','T','R','A','I','N','T',
'O','F','F','S','E','T','E','M','P','O','R','A','R','Y','U','N','I','Q',
- 'U','E','R','Y','A','T','T','A','C','H','A','V','I','N','G','R','O','U',
- 'P','D','A','T','E','B','E','G','I','N','N','E','R','E','L','E','A','S',
- 'E','B','E','T','W','E','E','N','O','T','N','U','L','L','I','K','E','C',
- 'A','S','C','A','D','E','L','E','T','E','C','A','S','E','C','O','L','L',
- 'A','T','E','C','R','E','A','T','E','C','U','R','R','E','N','T','_','D',
- 'A','T','E','D','E','T','A','C','H','I','M','M','E','D','I','A','T','E',
- 'J','O','I','N','S','E','R','T','M','A','T','C','H','P','L','A','N','A',
- 'L','Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U',
- 'E','S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','W',
- 'H','E','R','E','N','A','M','E','A','F','T','E','R','E','P','L','A','C',
- 'E','A','N','D','E','F','A','U','L','T','A','U','T','O','I','N','C','R',
- 'E','M','E','N','T','C','A','S','T','C','O','L','U','M','N','C','O','M',
- 'M','I','T','C','O','N','F','L','I','C','T','C','R','O','S','S','C','U',
- 'R','R','E','N','T','_','T','I','M','E','S','T','A','M','P','R','I','M',
- 'A','R','Y','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T',
- 'D','R','O','P','F','A','I','L','F','R','O','M','F','U','L','L','G','L',
- 'O','B','Y','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S',
- 'T','R','I','C','T','O','U','T','E','R','I','G','H','T','R','O','L','L',
- 'B','A','C','K','R','O','W','U','N','I','O','N','U','S','I','N','G','V',
- 'A','C','U','U','M','V','I','E','W','I','N','I','T','I','A','L','L','Y',
+ 'U','E','R','Y','W','I','T','H','O','U','T','E','R','E','L','E','A','S',
+ 'E','A','T','T','A','C','H','A','V','I','N','G','R','O','U','P','D','A',
+ 'T','E','B','E','G','I','N','N','E','R','E','C','U','R','S','I','V','E',
+ 'B','E','T','W','E','E','N','O','T','N','U','L','L','I','K','E','C','A',
+ 'S','C','A','D','E','L','E','T','E','C','A','S','E','C','O','L','L','A',
+ 'T','E','C','R','E','A','T','E','C','U','R','R','E','N','T','_','D','A',
+ 'T','E','D','E','T','A','C','H','I','M','M','E','D','I','A','T','E','J',
+ 'O','I','N','S','E','R','T','M','A','T','C','H','P','L','A','N','A','L',
+ 'Y','Z','E','P','R','A','G','M','A','B','O','R','T','V','A','L','U','E',
+ 'S','V','I','R','T','U','A','L','I','M','I','T','W','H','E','N','W','H',
+ 'E','R','E','N','A','M','E','A','F','T','E','R','E','P','L','A','C','E',
+ 'A','N','D','E','F','A','U','L','T','A','U','T','O','I','N','C','R','E',
+ 'M','E','N','T','C','A','S','T','C','O','L','U','M','N','C','O','M','M',
+ 'I','T','C','O','N','F','L','I','C','T','C','R','O','S','S','C','U','R',
+ 'R','E','N','T','_','T','I','M','E','S','T','A','M','P','R','I','M','A',
+ 'R','Y','D','E','F','E','R','R','E','D','I','S','T','I','N','C','T','D',
+ 'R','O','P','F','A','I','L','F','R','O','M','F','U','L','L','G','L','O',
+ 'B','Y','I','F','I','S','N','U','L','L','O','R','D','E','R','E','S','T',
+ 'R','I','C','T','R','I','G','H','T','R','O','L','L','B','A','C','K','R',
+ 'O','W','U','N','I','O','N','U','S','I','N','G','V','A','C','U','U','M',
+ 'V','I','E','W','I','N','I','T','I','A','L','L','Y',
};
static const unsigned char aHash[127] = {
- 72, 101, 114, 70, 0, 45, 0, 0, 78, 0, 73, 0, 0,
- 42, 12, 74, 15, 0, 113, 81, 50, 108, 0, 19, 0, 0,
- 118, 0, 116, 111, 0, 22, 89, 0, 9, 0, 0, 66, 67,
- 0, 65, 6, 0, 48, 86, 98, 0, 115, 97, 0, 0, 44,
- 0, 99, 24, 0, 17, 0, 119, 49, 23, 0, 5, 106, 25,
- 92, 0, 0, 121, 102, 56, 120, 53, 28, 51, 0, 87, 0,
- 96, 26, 0, 95, 0, 0, 0, 91, 88, 93, 84, 105, 14,
- 39, 104, 0, 77, 0, 18, 85, 107, 32, 0, 117, 76, 109,
- 58, 46, 80, 0, 0, 90, 40, 0, 112, 0, 36, 0, 0,
- 29, 0, 82, 59, 60, 0, 20, 57, 0, 52,
+ 76, 105, 117, 74, 0, 45, 0, 0, 82, 0, 77, 0, 0,
+ 42, 12, 78, 15, 0, 116, 85, 54, 112, 0, 19, 0, 0,
+ 121, 0, 119, 115, 0, 22, 93, 0, 9, 0, 0, 70, 71,
+ 0, 69, 6, 0, 48, 90, 102, 0, 118, 101, 0, 0, 44,
+ 0, 103, 24, 0, 17, 0, 122, 53, 23, 0, 5, 110, 25,
+ 96, 0, 0, 124, 106, 60, 123, 57, 28, 55, 0, 91, 0,
+ 100, 26, 0, 99, 0, 0, 0, 95, 92, 97, 88, 109, 14,
+ 39, 108, 0, 81, 0, 18, 89, 111, 32, 0, 120, 80, 113,
+ 62, 46, 84, 0, 0, 94, 40, 59, 114, 0, 36, 0, 0,
+ 29, 0, 86, 63, 64, 0, 20, 61, 0, 56,
};
- static const unsigned char aNext[121] = {
+ static const unsigned char aNext[124] = {
0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0,
0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0,
0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 43, 3, 47,
- 0, 0, 0, 0, 30, 0, 54, 0, 38, 0, 0, 0, 1,
- 62, 0, 0, 63, 0, 41, 0, 0, 0, 0, 0, 0, 0,
- 61, 0, 0, 0, 0, 31, 55, 16, 34, 10, 0, 0, 0,
- 0, 0, 0, 0, 11, 68, 75, 0, 8, 0, 100, 94, 0,
- 103, 0, 83, 0, 71, 0, 0, 110, 27, 37, 69, 79, 0,
- 35, 64, 0, 0,
+ 0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 0, 0, 50,
+ 0, 43, 3, 47, 0, 0, 0, 0, 30, 0, 58, 0, 38,
+ 0, 0, 0, 1, 66, 0, 0, 67, 0, 41, 0, 0, 0,
+ 0, 0, 0, 49, 65, 0, 0, 0, 0, 31, 52, 16, 34,
+ 10, 0, 0, 0, 0, 0, 0, 0, 11, 72, 79, 0, 8,
+ 0, 104, 98, 0, 107, 0, 87, 0, 75, 51, 0, 27, 37,
+ 73, 83, 0, 35, 68, 0, 0,
};
- static const unsigned char aLen[121] = {
+ static const unsigned char aLen[124] = {
7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6,
7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6,
11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10,
- 4, 6, 2, 3, 9, 4, 2, 6, 5, 6, 6, 5, 6,
- 5, 5, 7, 7, 7, 3, 2, 4, 4, 7, 3, 6, 4,
- 7, 6, 12, 6, 9, 4, 6, 5, 4, 7, 6, 5, 6,
- 7, 5, 4, 5, 6, 5, 7, 3, 7, 13, 2, 2, 4,
- 6, 6, 8, 5, 17, 12, 7, 8, 8, 2, 4, 4, 4,
- 4, 4, 2, 2, 6, 5, 8, 5, 5, 8, 3, 5, 5,
- 6, 4, 9, 3,
+ 4, 6, 2, 3, 9, 4, 2, 6, 5, 7, 4, 5, 7,
+ 6, 6, 5, 6, 5, 5, 9, 7, 7, 3, 2, 4, 4,
+ 7, 3, 6, 4, 7, 6, 12, 6, 9, 4, 6, 5, 4,
+ 7, 6, 5, 6, 7, 5, 4, 5, 6, 5, 7, 3, 7,
+ 13, 2, 2, 4, 6, 6, 8, 5, 17, 12, 7, 8, 8,
+ 2, 4, 4, 4, 4, 4, 2, 2, 6, 5, 8, 5, 8,
+ 3, 5, 5, 6, 4, 9, 3,
};
- static const unsigned short int aOffset[121] = {
+ static const unsigned short int aOffset[124] = {
0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33,
36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81,
86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152,
- 159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 189, 194, 197,
- 203, 206, 210, 217, 223, 223, 223, 226, 229, 233, 234, 238, 244,
- 248, 255, 261, 273, 279, 288, 290, 296, 301, 303, 310, 315, 320,
- 326, 332, 337, 341, 344, 350, 354, 361, 363, 370, 372, 374, 383,
- 387, 393, 399, 407, 412, 412, 428, 435, 442, 443, 450, 454, 458,
- 462, 466, 469, 471, 473, 479, 483, 491, 495, 500, 508, 511, 516,
- 521, 527, 531, 536,
+ 159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 184, 188, 192,
+ 199, 204, 209, 212, 218, 221, 225, 234, 240, 240, 240, 243, 246,
+ 250, 251, 255, 261, 265, 272, 278, 290, 296, 305, 307, 313, 318,
+ 320, 327, 332, 337, 343, 349, 354, 358, 361, 367, 371, 378, 380,
+ 387, 389, 391, 400, 404, 410, 416, 424, 429, 429, 445, 452, 459,
+ 460, 467, 471, 475, 479, 483, 486, 488, 490, 496, 500, 508, 513,
+ 521, 524, 529, 534, 540, 544, 549,
};
- static const unsigned char aCode[121] = {
+ static const unsigned char aCode[124] = {
TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE,
TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN,
TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD,
@@ -109149,30 +136809,38 @@ static int keywordCode(const char *z, int n){
TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT,
TK_INTERSECT, TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO,
TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP,
- TK_OR, TK_UNIQUE, TK_QUERY, TK_ATTACH, TK_HAVING,
- TK_GROUP, TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RELEASE,
- TK_BETWEEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL,
- TK_LIKE_KW, TK_CASCADE, TK_ASC, TK_DELETE, TK_CASE,
- TK_COLLATE, TK_CREATE, TK_CTIME_KW, TK_DETACH, TK_IMMEDIATE,
- TK_JOIN, TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE,
- TK_PRAGMA, TK_ABORT, TK_VALUES, TK_VIRTUAL, TK_LIMIT,
- TK_WHEN, TK_WHERE, TK_RENAME, TK_AFTER, TK_REPLACE,
- TK_AND, TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN,
- TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW,
- TK_CTIME_KW, TK_CTIME_KW, TK_PRIMARY, TK_DEFERRED, TK_DISTINCT,
- TK_IS, TK_DROP, TK_FAIL, TK_FROM, TK_JOIN_KW,
- TK_LIKE_KW, TK_BY, TK_IF, TK_ISNULL, TK_ORDER,
- TK_RESTRICT, TK_JOIN_KW, TK_JOIN_KW, TK_ROLLBACK, TK_ROW,
- TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_INITIALLY,
- TK_ALL,
+ TK_OR, TK_UNIQUE, TK_QUERY, TK_WITHOUT, TK_WITH,
+ TK_JOIN_KW, TK_RELEASE, TK_ATTACH, TK_HAVING, TK_GROUP,
+ TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RECURSIVE, TK_BETWEEN,
+ TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, TK_LIKE_KW,
+ TK_CASCADE, TK_ASC, TK_DELETE, TK_CASE, TK_COLLATE,
+ TK_CREATE, TK_CTIME_KW, TK_DETACH, TK_IMMEDIATE, TK_JOIN,
+ TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA,
+ TK_ABORT, TK_VALUES, TK_VIRTUAL, TK_LIMIT, TK_WHEN,
+ TK_WHERE, TK_RENAME, TK_AFTER, TK_REPLACE, TK_AND,
+ TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, TK_CAST,
+ TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW,
+ TK_CTIME_KW, TK_PRIMARY, TK_DEFERRED, TK_DISTINCT, TK_IS,
+ TK_DROP, TK_FAIL, TK_FROM, TK_JOIN_KW, TK_LIKE_KW,
+ TK_BY, TK_IF, TK_ISNULL, TK_ORDER, TK_RESTRICT,
+ TK_JOIN_KW, TK_ROLLBACK, TK_ROW, TK_UNION, TK_USING,
+ TK_VACUUM, TK_VIEW, TK_INITIALLY, TK_ALL,
};
- int h, i;
- if( n<2 ) return TK_ID;
- h = ((charMap(z[0])*4) ^
- (charMap(z[n-1])*3) ^
- n) % 127;
- for(i=((int)aHash[h])-1; i>=0; i=((int)aNext[i])-1){
- if( aLen[i]==n && sqlite3StrNICmp(&zText[aOffset[i]],z,n)==0 ){
+ int i, j;
+ const char *zKW;
+ if( n>=2 ){
+ i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127;
+ for(i=((int)aHash[i])-1; i>=0; i=((int)aNext[i])-1){
+ if( aLen[i]!=n ) continue;
+ j = 0;
+ zKW = &zText[aOffset[i]];
+#ifdef SQLITE_ASCII
+ while( j<n && (z[j]&~0x20)==zKW[j] ){ j++; }
+#endif
+#ifdef SQLITE_EBCDIC
+ while( j<n && toupper(z[j])==zKW[j] ){ j++; }
+#endif
+ if( j<n ) continue;
testcase( i==0 ); /* REINDEX */
testcase( i==1 ); /* INDEXED */
testcase( i==2 ); /* INDEX */
@@ -109221,88 +136889,94 @@ static int keywordCode(const char *z, int n){
testcase( i==45 ); /* OR */
testcase( i==46 ); /* UNIQUE */
testcase( i==47 ); /* QUERY */
- testcase( i==48 ); /* ATTACH */
- testcase( i==49 ); /* HAVING */
- testcase( i==50 ); /* GROUP */
- testcase( i==51 ); /* UPDATE */
- testcase( i==52 ); /* BEGIN */
- testcase( i==53 ); /* INNER */
- testcase( i==54 ); /* RELEASE */
- testcase( i==55 ); /* BETWEEN */
- testcase( i==56 ); /* NOTNULL */
- testcase( i==57 ); /* NOT */
- testcase( i==58 ); /* NO */
- testcase( i==59 ); /* NULL */
- testcase( i==60 ); /* LIKE */
- testcase( i==61 ); /* CASCADE */
- testcase( i==62 ); /* ASC */
- testcase( i==63 ); /* DELETE */
- testcase( i==64 ); /* CASE */
- testcase( i==65 ); /* COLLATE */
- testcase( i==66 ); /* CREATE */
- testcase( i==67 ); /* CURRENT_DATE */
- testcase( i==68 ); /* DETACH */
- testcase( i==69 ); /* IMMEDIATE */
- testcase( i==70 ); /* JOIN */
- testcase( i==71 ); /* INSERT */
- testcase( i==72 ); /* MATCH */
- testcase( i==73 ); /* PLAN */
- testcase( i==74 ); /* ANALYZE */
- testcase( i==75 ); /* PRAGMA */
- testcase( i==76 ); /* ABORT */
- testcase( i==77 ); /* VALUES */
- testcase( i==78 ); /* VIRTUAL */
- testcase( i==79 ); /* LIMIT */
- testcase( i==80 ); /* WHEN */
- testcase( i==81 ); /* WHERE */
- testcase( i==82 ); /* RENAME */
- testcase( i==83 ); /* AFTER */
- testcase( i==84 ); /* REPLACE */
- testcase( i==85 ); /* AND */
- testcase( i==86 ); /* DEFAULT */
- testcase( i==87 ); /* AUTOINCREMENT */
- testcase( i==88 ); /* TO */
- testcase( i==89 ); /* IN */
- testcase( i==90 ); /* CAST */
- testcase( i==91 ); /* COLUMN */
- testcase( i==92 ); /* COMMIT */
- testcase( i==93 ); /* CONFLICT */
- testcase( i==94 ); /* CROSS */
- testcase( i==95 ); /* CURRENT_TIMESTAMP */
- testcase( i==96 ); /* CURRENT_TIME */
- testcase( i==97 ); /* PRIMARY */
- testcase( i==98 ); /* DEFERRED */
- testcase( i==99 ); /* DISTINCT */
- testcase( i==100 ); /* IS */
- testcase( i==101 ); /* DROP */
- testcase( i==102 ); /* FAIL */
- testcase( i==103 ); /* FROM */
- testcase( i==104 ); /* FULL */
- testcase( i==105 ); /* GLOB */
- testcase( i==106 ); /* BY */
- testcase( i==107 ); /* IF */
- testcase( i==108 ); /* ISNULL */
- testcase( i==109 ); /* ORDER */
- testcase( i==110 ); /* RESTRICT */
- testcase( i==111 ); /* OUTER */
- testcase( i==112 ); /* RIGHT */
- testcase( i==113 ); /* ROLLBACK */
- testcase( i==114 ); /* ROW */
- testcase( i==115 ); /* UNION */
- testcase( i==116 ); /* USING */
- testcase( i==117 ); /* VACUUM */
- testcase( i==118 ); /* VIEW */
- testcase( i==119 ); /* INITIALLY */
- testcase( i==120 ); /* ALL */
- return aCode[i];
- }
- }
- return TK_ID;
+ testcase( i==48 ); /* WITHOUT */
+ testcase( i==49 ); /* WITH */
+ testcase( i==50 ); /* OUTER */
+ testcase( i==51 ); /* RELEASE */
+ testcase( i==52 ); /* ATTACH */
+ testcase( i==53 ); /* HAVING */
+ testcase( i==54 ); /* GROUP */
+ testcase( i==55 ); /* UPDATE */
+ testcase( i==56 ); /* BEGIN */
+ testcase( i==57 ); /* INNER */
+ testcase( i==58 ); /* RECURSIVE */
+ testcase( i==59 ); /* BETWEEN */
+ testcase( i==60 ); /* NOTNULL */
+ testcase( i==61 ); /* NOT */
+ testcase( i==62 ); /* NO */
+ testcase( i==63 ); /* NULL */
+ testcase( i==64 ); /* LIKE */
+ testcase( i==65 ); /* CASCADE */
+ testcase( i==66 ); /* ASC */
+ testcase( i==67 ); /* DELETE */
+ testcase( i==68 ); /* CASE */
+ testcase( i==69 ); /* COLLATE */
+ testcase( i==70 ); /* CREATE */
+ testcase( i==71 ); /* CURRENT_DATE */
+ testcase( i==72 ); /* DETACH */
+ testcase( i==73 ); /* IMMEDIATE */
+ testcase( i==74 ); /* JOIN */
+ testcase( i==75 ); /* INSERT */
+ testcase( i==76 ); /* MATCH */
+ testcase( i==77 ); /* PLAN */
+ testcase( i==78 ); /* ANALYZE */
+ testcase( i==79 ); /* PRAGMA */
+ testcase( i==80 ); /* ABORT */
+ testcase( i==81 ); /* VALUES */
+ testcase( i==82 ); /* VIRTUAL */
+ testcase( i==83 ); /* LIMIT */
+ testcase( i==84 ); /* WHEN */
+ testcase( i==85 ); /* WHERE */
+ testcase( i==86 ); /* RENAME */
+ testcase( i==87 ); /* AFTER */
+ testcase( i==88 ); /* REPLACE */
+ testcase( i==89 ); /* AND */
+ testcase( i==90 ); /* DEFAULT */
+ testcase( i==91 ); /* AUTOINCREMENT */
+ testcase( i==92 ); /* TO */
+ testcase( i==93 ); /* IN */
+ testcase( i==94 ); /* CAST */
+ testcase( i==95 ); /* COLUMN */
+ testcase( i==96 ); /* COMMIT */
+ testcase( i==97 ); /* CONFLICT */
+ testcase( i==98 ); /* CROSS */
+ testcase( i==99 ); /* CURRENT_TIMESTAMP */
+ testcase( i==100 ); /* CURRENT_TIME */
+ testcase( i==101 ); /* PRIMARY */
+ testcase( i==102 ); /* DEFERRED */
+ testcase( i==103 ); /* DISTINCT */
+ testcase( i==104 ); /* IS */
+ testcase( i==105 ); /* DROP */
+ testcase( i==106 ); /* FAIL */
+ testcase( i==107 ); /* FROM */
+ testcase( i==108 ); /* FULL */
+ testcase( i==109 ); /* GLOB */
+ testcase( i==110 ); /* BY */
+ testcase( i==111 ); /* IF */
+ testcase( i==112 ); /* ISNULL */
+ testcase( i==113 ); /* ORDER */
+ testcase( i==114 ); /* RESTRICT */
+ testcase( i==115 ); /* RIGHT */
+ testcase( i==116 ); /* ROLLBACK */
+ testcase( i==117 ); /* ROW */
+ testcase( i==118 ); /* UNION */
+ testcase( i==119 ); /* USING */
+ testcase( i==120 ); /* VACUUM */
+ testcase( i==121 ); /* VIEW */
+ testcase( i==122 ); /* INITIALLY */
+ testcase( i==123 ); /* ALL */
+ *pType = aCode[i];
+ break;
+ }
+ }
+ return n;
}
SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
- return keywordCode((char*)z, n);
+ int id = TK_ID;
+ keywordCode((char*)z, n, &id);
+ return id;
}
-#define SQLITE_N_KEYWORD 121
+#define SQLITE_N_KEYWORD 124
/************** End of keywordhash.h *****************************************/
/************** Continuing where we left off in tokenize.c *******************/
@@ -109320,7 +136994,7 @@ SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){
** end result.
**
** Ticket #1066. the SQL standard does not allow '$' in the
-** middle of identfiers. But many SQL implementations do.
+** middle of identifiers. But many SQL implementations do.
** SQLite will allow '$' in identifiers for compatibility.
** But the feature is undocumented.
*/
@@ -109346,15 +137020,22 @@ SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[] = {
#define IdChar(C) (((c=C)>=0x42 && sqlite3IsEbcdicIdChar[c-0x40]))
#endif
+/* Make the IdChar function accessible from ctime.c */
+#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
+SQLITE_PRIVATE int sqlite3IsIdChar(u8 c){ return IdChar(c); }
+#endif
+
/*
-** Return the length of the token that begins at z[0].
+** Return the length (in bytes) of the token that begins at z[0].
** Store the token type in *tokenType before returning.
*/
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
int i, c;
- switch( *z ){
- case ' ': case '\t': case '\n': case '\f': case '\r': {
+ switch( aiClass[*z] ){ /* Switch on the character-class of the first byte
+ ** of the token. See the comment on the CC_ defines
+ ** above. */
+ case CC_SPACE: {
testcase( z[0]==' ' );
testcase( z[0]=='\t' );
testcase( z[0]=='\n' );
@@ -109364,9 +137045,8 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_SPACE;
return i;
}
- case '-': {
+ case CC_MINUS: {
if( z[1]=='-' ){
- /* IMP: R-15891-05542 -- syntax diagram for comments */
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
return i;
@@ -109374,46 +137054,45 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
*tokenType = TK_MINUS;
return 1;
}
- case '(': {
+ case CC_LP: {
*tokenType = TK_LP;
return 1;
}
- case ')': {
+ case CC_RP: {
*tokenType = TK_RP;
return 1;
}
- case ';': {
+ case CC_SEMI: {
*tokenType = TK_SEMI;
return 1;
}
- case '+': {
+ case CC_PLUS: {
*tokenType = TK_PLUS;
return 1;
}
- case '*': {
+ case CC_STAR: {
*tokenType = TK_STAR;
return 1;
}
- case '/': {
+ case CC_SLASH: {
if( z[1]!='*' || z[2]==0 ){
*tokenType = TK_SLASH;
return 1;
}
- /* IMP: R-15891-05542 -- syntax diagram for comments */
for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
if( c ) i++;
*tokenType = TK_SPACE; /* IMP: R-22934-25134 */
return i;
}
- case '%': {
+ case CC_PERCENT: {
*tokenType = TK_REM;
return 1;
}
- case '=': {
+ case CC_EQ: {
*tokenType = TK_EQ;
return 1 + (z[1]=='=');
}
- case '<': {
+ case CC_LT: {
if( (c=z[1])=='=' ){
*tokenType = TK_LE;
return 2;
@@ -109428,7 +137107,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 1;
}
}
- case '>': {
+ case CC_GT: {
if( (c=z[1])=='=' ){
*tokenType = TK_GE;
return 2;
@@ -109440,16 +137119,16 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 1;
}
}
- case '!': {
+ case CC_BANG: {
if( z[1]!='=' ){
*tokenType = TK_ILLEGAL;
- return 2;
+ return 1;
}else{
*tokenType = TK_NE;
return 2;
}
}
- case '|': {
+ case CC_PIPE: {
if( z[1]!='|' ){
*tokenType = TK_BITOR;
return 1;
@@ -109458,21 +137137,19 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return 2;
}
}
- case ',': {
+ case CC_COMMA: {
*tokenType = TK_COMMA;
return 1;
}
- case '&': {
+ case CC_AND: {
*tokenType = TK_BITAND;
return 1;
}
- case '~': {
+ case CC_TILDA: {
*tokenType = TK_BITNOT;
return 1;
}
- case '`':
- case '\'':
- case '"': {
+ case CC_QUOTE: {
int delim = z[0];
testcase( delim=='`' );
testcase( delim=='\'' );
@@ -109497,7 +137174,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
return i;
}
}
- case '.': {
+ case CC_DOT: {
#ifndef SQLITE_OMIT_FLOATING_POINT
if( !sqlite3Isdigit(z[1]) )
#endif
@@ -109508,13 +137185,18 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
/* If the next character is a digit, this is a floating point
** number that begins with ".". Fall thru into the next case */
}
- case '0': case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9': {
+ case CC_DIGIT: {
testcase( z[0]=='0' ); testcase( z[0]=='1' ); testcase( z[0]=='2' );
testcase( z[0]=='3' ); testcase( z[0]=='4' ); testcase( z[0]=='5' );
testcase( z[0]=='6' ); testcase( z[0]=='7' ); testcase( z[0]=='8' );
testcase( z[0]=='9' );
*tokenType = TK_INTEGER;
+#ifndef SQLITE_OMIT_HEX_INTEGER
+ if( z[0]=='0' && (z[1]=='x' || z[1]=='X') && sqlite3Isxdigit(z[2]) ){
+ for(i=3; sqlite3Isxdigit(z[i]); i++){}
+ return i;
+ }
+#endif
for(i=0; sqlite3Isdigit(z[i]); i++){}
#ifndef SQLITE_OMIT_FLOATING_POINT
if( z[i]=='.' ){
@@ -109538,34 +137220,21 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
return i;
}
- case '[': {
+ case CC_QUOTE2: {
for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){}
*tokenType = c==']' ? TK_ID : TK_ILLEGAL;
return i;
}
- case '?': {
+ case CC_VARNUM: {
*tokenType = TK_VARIABLE;
for(i=1; sqlite3Isdigit(z[i]); i++){}
return i;
}
- case '#': {
- for(i=1; sqlite3Isdigit(z[i]); i++){}
- if( i>1 ){
- /* Parameters of the form #NNN (where NNN is a number) are used
- ** internally by sqlite3NestedParse. */
- *tokenType = TK_REGISTER;
- return i;
- }
- /* Fall through into the next case if the '#' is not followed by
- ** a digit. Try to match #AAAA where AAAA is a parameter name. */
- }
-#ifndef SQLITE_OMIT_TCL_VARIABLE
- case '$':
-#endif
- case '@': /* For compatibility with MS SQL Server */
- case ':': {
+ case CC_DOLLAR:
+ case CC_VARALPHA: {
int n = 0;
- testcase( z[0]=='$' ); testcase( z[0]=='@' ); testcase( z[0]==':' );
+ testcase( z[0]=='$' ); testcase( z[0]=='@' );
+ testcase( z[0]==':' ); testcase( z[0]=='#' );
*tokenType = TK_VARIABLE;
for(i=1; (c=z[i])!=0; i++){
if( IdChar(c) ){
@@ -109591,8 +137260,20 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
if( n==0 ) *tokenType = TK_ILLEGAL;
return i;
}
+ case CC_KYWD: {
+ for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
+ if( IdChar(z[i]) ){
+ /* This token started out using characters that can appear in keywords,
+ ** but z[i] is a character not allowed within keywords, so this must
+ ** be an identifier instead */
+ i++;
+ break;
+ }
+ *tokenType = TK_ID;
+ return keywordCode((char*)z, i, tokenType);
+ }
+ case CC_X: {
#ifndef SQLITE_OMIT_BLOB_LITERAL
- case 'x': case 'X': {
testcase( z[0]=='x' ); testcase( z[0]=='X' );
if( z[1]=='\'' ){
*tokenType = TK_BLOB;
@@ -109604,20 +137285,22 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
if( z[i] ) i++;
return i;
}
- /* Otherwise fall through to the next case */
- }
#endif
+ /* If it is not a BLOB literal, then it must be an ID, since no
+ ** SQL keywords start with the letter 'x'. Fall through */
+ }
+ case CC_ID: {
+ i = 1;
+ break;
+ }
default: {
- if( !IdChar(*z) ){
- break;
- }
- for(i=1; IdChar(z[i]); i++){}
- *tokenType = keywordCode((char*)z, i);
- return i;
+ *tokenType = TK_ILLEGAL;
+ return 1;
}
}
- *tokenType = TK_ILLEGAL;
- return 1;
+ while( IdChar(z[i]) ){ i++; }
+ *tokenType = TK_ID;
+ return i;
}
/*
@@ -109633,90 +137316,82 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql, char **pzEr
void *pEngine; /* The LEMON-generated LALR(1) parser */
int tokenType; /* type of the next token */
int lastTokenParsed = -1; /* type of the previous token */
- u8 enableLookaside; /* Saved value of db->lookaside.bEnabled */
sqlite3 *db = pParse->db; /* The database connection */
int mxSqlLen; /* Max length of an SQL string */
-
+ assert( zSql!=0 );
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
- if( db->activeVdbeCnt==0 ){
+ if( db->nVdbeActive==0 ){
db->u1.isInterrupted = 0;
}
pParse->rc = SQLITE_OK;
pParse->zTail = zSql;
i = 0;
assert( pzErrMsg!=0 );
- pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc);
+ /* sqlite3ParserTrace(stdout, "parser: "); */
+ pEngine = sqlite3ParserAlloc(sqlite3Malloc);
if( pEngine==0 ){
- db->mallocFailed = 1;
- return SQLITE_NOMEM;
+ sqlite3OomFault(db);
+ return SQLITE_NOMEM_BKPT;
}
assert( pParse->pNewTable==0 );
assert( pParse->pNewTrigger==0 );
assert( pParse->nVar==0 );
assert( pParse->nzVar==0 );
assert( pParse->azVar==0 );
- enableLookaside = db->lookaside.bEnabled;
- if( db->lookaside.pStart ) db->lookaside.bEnabled = 1;
- while( !db->mallocFailed && zSql[i]!=0 ){
+ while( 1 ){
assert( i>=0 );
- pParse->sLastToken.z = &zSql[i];
- pParse->sLastToken.n = sqlite3GetToken((unsigned char*)&zSql[i],&tokenType);
- i += pParse->sLastToken.n;
- if( i>mxSqlLen ){
- pParse->rc = SQLITE_TOOBIG;
- break;
- }
- switch( tokenType ){
- case TK_SPACE: {
- if( db->u1.isInterrupted ){
- sqlite3ErrorMsg(pParse, "interrupt");
- pParse->rc = SQLITE_INTERRUPT;
- goto abort_parse;
- }
+ if( zSql[i]!=0 ){
+ pParse->sLastToken.z = &zSql[i];
+ pParse->sLastToken.n = sqlite3GetToken((u8*)&zSql[i],&tokenType);
+ i += pParse->sLastToken.n;
+ if( i>mxSqlLen ){
+ pParse->rc = SQLITE_TOOBIG;
break;
}
- case TK_ILLEGAL: {
- sqlite3DbFree(db, *pzErrMsg);
- *pzErrMsg = sqlite3MPrintf(db, "unrecognized token: \"%T\"",
- &pParse->sLastToken);
- nErr++;
- goto abort_parse;
+ }else{
+ /* Upon reaching the end of input, call the parser two more times
+ ** with tokens TK_SEMI and 0, in that order. */
+ if( lastTokenParsed==TK_SEMI ){
+ tokenType = 0;
+ }else if( lastTokenParsed==0 ){
+ break;
+ }else{
+ tokenType = TK_SEMI;
}
- case TK_SEMI: {
- pParse->zTail = &zSql[i];
- /* Fall thru into the default case */
+ }
+ if( tokenType>=TK_SPACE ){
+ assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
+ if( db->u1.isInterrupted ){
+ pParse->rc = SQLITE_INTERRUPT;
+ break;
}
- default: {
- sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
- lastTokenParsed = tokenType;
- if( pParse->rc!=SQLITE_OK ){
- goto abort_parse;
- }
+ if( tokenType==TK_ILLEGAL ){
+ sqlite3ErrorMsg(pParse, "unrecognized token: \"%T\"",
+ &pParse->sLastToken);
break;
}
+ }else{
+ sqlite3Parser(pEngine, tokenType, pParse->sLastToken, pParse);
+ lastTokenParsed = tokenType;
+ if( pParse->rc!=SQLITE_OK || db->mallocFailed ) break;
}
}
-abort_parse:
- if( zSql[i]==0 && nErr==0 && pParse->rc==SQLITE_OK ){
- if( lastTokenParsed!=TK_SEMI ){
- sqlite3Parser(pEngine, TK_SEMI, pParse->sLastToken, pParse);
- pParse->zTail = &zSql[i];
- }
- sqlite3Parser(pEngine, 0, pParse->sLastToken, pParse);
- }
+ assert( nErr==0 );
+ pParse->zTail = &zSql[i];
#ifdef YYTRACKMAXSTACKDEPTH
- sqlite3StatusSet(SQLITE_STATUS_PARSER_STACK,
+ sqlite3_mutex_enter(sqlite3MallocMutex());
+ sqlite3StatusHighwater(SQLITE_STATUS_PARSER_STACK,
sqlite3ParserStackPeak(pEngine)
);
+ sqlite3_mutex_leave(sqlite3MallocMutex());
#endif /* YYDEBUG */
sqlite3ParserFree(pEngine, sqlite3_free);
- db->lookaside.bEnabled = enableLookaside;
if( db->mallocFailed ){
- pParse->rc = SQLITE_NOMEM;
+ pParse->rc = SQLITE_NOMEM_BKPT;
}
if( pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE && pParse->zErrMsg==0 ){
- sqlite3SetString(&pParse->zErrMsg, db, "%s", sqlite3ErrStr(pParse->rc));
+ pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
}
assert( pzErrMsg!=0 );
if( pParse->zErrMsg ){
@@ -109748,10 +137423,10 @@ abort_parse:
sqlite3DeleteTable(db, pParse->pNewTable);
}
+ if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
sqlite3DeleteTrigger(db, pParse->pNewTrigger);
for(i=pParse->nzVar-1; i>=0; i--) sqlite3DbFree(db, pParse->azVar[i]);
sqlite3DbFree(db, pParse->azVar);
- sqlite3DbFree(db, pParse->aAlias);
while( pParse->pAinc ){
AutoincInfo *p = pParse->pAinc;
pParse->pAinc = p->pNext;
@@ -109762,9 +137437,7 @@ abort_parse:
pParse->pZombieTab = p->pNextZombie;
sqlite3DeleteTable(db, p);
}
- if( nErr>0 && pParse->rc==SQLITE_OK ){
- pParse->rc = SQLITE_ERROR;
- }
+ assert( nErr==0 || pParse->rc!=SQLITE_OK );
return nErr;
}
@@ -109788,6 +137461,7 @@ abort_parse:
** separating it out, the code will be automatically omitted from
** static links that do not use it.
*/
+/* #include "sqliteInt.h" */
#ifndef SQLITE_OMIT_COMPLETE
/*
@@ -109841,7 +137515,7 @@ SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[];
** a statement.
**
** (4) CREATE The keyword CREATE has been seen at the beginning of a
-** statement, possibly preceeded by EXPLAIN and/or followed by
+** statement, possibly preceded by EXPLAIN and/or followed by
** TEMP or TEMPORARY
**
** (5) TRIGGER We are in the middle of a trigger definition that must be
@@ -109851,7 +137525,7 @@ SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[];
** the end of a trigger definition.
**
** (7) END We've seen the ";END" of the ";END;" that occurs at the end
-** of a trigger difinition.
+** of a trigger definition.
**
** Transitions between states above are determined by tokens extracted
** from the input. The following tokens are significant:
@@ -109894,7 +137568,7 @@ SQLITE_API int sqlite3_complete(const char *zSql){
};
#else
/* If triggers are not supported by this compile then the statement machine
- ** used to detect the end of a statement is much simplier
+ ** used to detect the end of a statement is much simpler
*/
static const u8 trans[3][3] = {
/* Token: */
@@ -109905,6 +137579,13 @@ SQLITE_API int sqlite3_complete(const char *zSql){
};
#endif /* SQLITE_OMIT_TRIGGER */
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( zSql==0 ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+
while( *zSql ){
switch( *zSql ){
case ';': { /* A semicolon */
@@ -110033,7 +137714,7 @@ SQLITE_API int sqlite3_complete(const char *zSql){
SQLITE_API int sqlite3_complete16(const void *zSql){
sqlite3_value *pVal;
char const *zSql8;
- int rc = SQLITE_NOMEM;
+ int rc;
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
@@ -110045,10 +137726,10 @@ SQLITE_API int sqlite3_complete16(const void *zSql){
if( zSql8 ){
rc = sqlite3_complete(zSql8);
}else{
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
sqlite3ValueFree(pVal);
- return sqlite3ApiExit(0, rc);
+ return rc & 0xff;
}
#endif /* SQLITE_OMIT_UTF16 */
#endif /* SQLITE_OMIT_COMPLETE */
@@ -110071,6 +137752,7 @@ SQLITE_API int sqlite3_complete16(const void *zSql){
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
*/
+/* #include "sqliteInt.h" */
#ifdef SQLITE_ENABLE_FTS3
/************** Include fts3.h in the middle of main.c ***********************/
@@ -110090,6 +137772,7 @@ SQLITE_API int sqlite3_complete16(const void *zSql){
** This header file is used by programs that want to link against the
** FTS3 library. All it does is declare the sqlite3Fts3Init() interface.
*/
+/* #include "sqlite3.h" */
#if 0
extern "C" {
@@ -110122,6 +137805,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db);
** This header file is used by programs that want to link against the
** RTREE library. All it does is declare the sqlite3RtreeInit() interface.
*/
+/* #include "sqlite3.h" */
#if 0
extern "C" {
@@ -110154,6 +137838,7 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db);
** This header file is used by programs that want to link against the
** ICU extension. All it does is declare the sqlite3IcuInit() interface.
*/
+/* #include "sqlite3.h" */
#if 0
extern "C" {
@@ -110169,6 +137854,12 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db);
/************** End of sqliteicu.h *******************************************/
/************** Continuing where we left off in main.c ***********************/
#endif
+#ifdef SQLITE_ENABLE_JSON1
+SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*);
+#endif
+#ifdef SQLITE_ENABLE_FTS5
+SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
+#endif
#ifndef SQLITE_AMALGAMATION
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
@@ -110193,12 +137884,24 @@ SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
*/
SQLITE_API int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; }
-/* IMPLEMENTATION-OF: R-54823-41343 The sqlite3_threadsafe() function returns
-** zero if and only if SQLite was compiled mutexing code omitted due to
+/* IMPLEMENTATION-OF: R-20790-14025 The sqlite3_threadsafe() function returns
+** zero if and only if SQLite was compiled with mutexing code omitted due to
** the SQLITE_THREADSAFE compile-time option being set to 0.
*/
SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
+/*
+** When compiling the test fixture or with debugging enabled (on Win32),
+** this variable being set to non-zero will cause OSTRACE macros to emit
+** extra diagnostic information.
+*/
+#ifdef SQLITE_HAVE_OS_TRACE
+# ifndef SQLITE_DEBUG_OS_TRACE
+# define SQLITE_DEBUG_OS_TRACE 0
+# endif
+ int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
+#endif
+
#if !defined(SQLITE_OMIT_TRACE) && defined(SQLITE_ENABLE_IOTRACE)
/*
** If the following function pointer is not NULL and if
@@ -110206,7 +137909,7 @@ SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; }
** I/O active are written using this function. These messages
** are intended for debugging activity only.
*/
-SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*, ...) = 0;
+SQLITE_API void (SQLITE_CDECL *sqlite3IoTrace)(const char*, ...) = 0;
#endif
/*
@@ -110219,6 +137922,15 @@ SQLITE_PRIVATE void (*sqlite3IoTrace)(const char*, ...) = 0;
SQLITE_API char *sqlite3_temp_directory = 0;
/*
+** If the following global variable points to a string which is the
+** name of a directory, then that directory will be used to store
+** all database files specified with a relative pathname.
+**
+** See also the "PRAGMA data_store_directory" SQL command.
+*/
+SQLITE_API char *sqlite3_data_directory = 0;
+
+/*
** Initialize SQLite.
**
** This routine must be called to initialize the memory allocation,
@@ -110250,8 +137962,11 @@ SQLITE_API char *sqlite3_temp_directory = 0;
** without blocking.
*/
SQLITE_API int sqlite3_initialize(void){
- sqlite3_mutex *pMaster; /* The main static mutex */
+ MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */
int rc; /* Result code */
+#ifdef SQLITE_EXTRA_INIT
+ int bRunExtraInit = 0; /* Extra initialization needed */
+#endif
#ifdef SQLITE_OMIT_WSD
rc = sqlite3_wsd_init(4096, 24);
@@ -110260,6 +137975,11 @@ SQLITE_API int sqlite3_initialize(void){
}
#endif
+ /* If the following assert() fails on some obscure processor/compiler
+ ** combination, the work-around is to set the correct pointer
+ ** size at compile-time using -DSQLITE_PTRSIZE=n compile-time option */
+ assert( SQLITE_PTRSIZE==sizeof(char*) );
+
/* If SQLite is already completely initialized, then this call
** to sqlite3_initialize() should be a no-op. But the initialization
** must be complete. So isInit must not be set until the very end
@@ -110284,7 +138004,7 @@ SQLITE_API int sqlite3_initialize(void){
** malloc subsystem - this implies that the allocation of a static
** mutex must not require support from the malloc subsystem.
*/
- pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER);
+ MUTEX_LOGIC( pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); )
sqlite3_mutex_enter(pMaster);
sqlite3GlobalConfig.isMutexInit = 1;
if( !sqlite3GlobalConfig.isMallocInit ){
@@ -110296,7 +138016,7 @@ SQLITE_API int sqlite3_initialize(void){
sqlite3GlobalConfig.pInitMutex =
sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE);
if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
}
}
@@ -110327,10 +138047,15 @@ SQLITE_API int sqlite3_initialize(void){
*/
sqlite3_mutex_enter(sqlite3GlobalConfig.pInitMutex);
if( sqlite3GlobalConfig.isInit==0 && sqlite3GlobalConfig.inProgress==0 ){
- FuncDefHash *pHash = &GLOBAL(FuncDefHash, sqlite3GlobalFunctions);
sqlite3GlobalConfig.inProgress = 1;
- memset(pHash, 0, sizeof(sqlite3GlobalFunctions));
- sqlite3RegisterGlobalFunctions();
+#ifdef SQLITE_ENABLE_SQLLOG
+ {
+ extern void sqlite3_init_sqllog(void);
+ sqlite3_init_sqllog();
+ }
+#endif
+ memset(&sqlite3BuiltinFunctions, 0, sizeof(sqlite3BuiltinFunctions));
+ sqlite3RegisterBuiltinFunctions();
if( sqlite3GlobalConfig.isPCacheInit==0 ){
rc = sqlite3PcacheInitialize();
}
@@ -110342,6 +138067,9 @@ SQLITE_API int sqlite3_initialize(void){
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
sqlite3GlobalConfig.isInit = 1;
+#ifdef SQLITE_EXTRA_INIT
+ bRunExtraInit = 1;
+#endif
}
sqlite3GlobalConfig.inProgress = 0;
}
@@ -110382,9 +138110,9 @@ SQLITE_API int sqlite3_initialize(void){
** compile-time option.
*/
#ifdef SQLITE_EXTRA_INIT
- if( rc==SQLITE_OK && sqlite3GlobalConfig.isInit ){
- int SQLITE_EXTRA_INIT(void);
- rc = SQLITE_EXTRA_INIT();
+ if( bRunExtraInit ){
+ int SQLITE_EXTRA_INIT(const char*);
+ rc = SQLITE_EXTRA_INIT(0);
}
#endif
@@ -110400,7 +138128,18 @@ SQLITE_API int sqlite3_initialize(void){
** when this routine is invoked, then this routine is a harmless no-op.
*/
SQLITE_API int sqlite3_shutdown(void){
+#ifdef SQLITE_OMIT_WSD
+ int rc = sqlite3_wsd_init(4096, 24);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+#endif
+
if( sqlite3GlobalConfig.isInit ){
+#ifdef SQLITE_EXTRA_SHUTDOWN
+ void SQLITE_EXTRA_SHUTDOWN(void);
+ SQLITE_EXTRA_SHUTDOWN();
+#endif
sqlite3_os_end();
sqlite3_reset_auto_extension();
sqlite3GlobalConfig.isInit = 0;
@@ -110412,6 +138151,18 @@ SQLITE_API int sqlite3_shutdown(void){
if( sqlite3GlobalConfig.isMallocInit ){
sqlite3MallocEnd();
sqlite3GlobalConfig.isMallocInit = 0;
+
+#ifndef SQLITE_OMIT_SHUTDOWN_DIRECTORIES
+ /* The heap subsystem has now been shutdown and these values are supposed
+ ** to be NULL or point to memory that was obtained from sqlite3_malloc(),
+ ** which would rely on that heap subsystem; therefore, make sure these
+ ** values cannot refer to heap memory that was just invalidated when the
+ ** heap subsystem was shutdown. This is only done if the current call to
+ ** this function resulted in the heap subsystem actually being shutdown.
+ */
+ sqlite3_data_directory = 0;
+ sqlite3_temp_directory = 0;
+#endif
}
if( sqlite3GlobalConfig.isMutexInit ){
sqlite3MutexEnd();
@@ -110442,33 +138193,43 @@ SQLITE_API int sqlite3_config(int op, ...){
switch( op ){
/* Mutex configuration options are only available in a threadsafe
- ** compile.
+ ** compile.
*/
-#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-54466-46756 */
case SQLITE_CONFIG_SINGLETHREAD: {
- /* Disable all mutexing */
- sqlite3GlobalConfig.bCoreMutex = 0;
- sqlite3GlobalConfig.bFullMutex = 0;
+ /* EVIDENCE-OF: R-02748-19096 This option sets the threading mode to
+ ** Single-thread. */
+ sqlite3GlobalConfig.bCoreMutex = 0; /* Disable mutex on core */
+ sqlite3GlobalConfig.bFullMutex = 0; /* Disable mutex on connections */
break;
}
+#endif
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-20520-54086 */
case SQLITE_CONFIG_MULTITHREAD: {
- /* Disable mutexing of database connections */
- /* Enable mutexing of core data structures */
- sqlite3GlobalConfig.bCoreMutex = 1;
- sqlite3GlobalConfig.bFullMutex = 0;
+ /* EVIDENCE-OF: R-14374-42468 This option sets the threading mode to
+ ** Multi-thread. */
+ sqlite3GlobalConfig.bCoreMutex = 1; /* Enable mutex on core */
+ sqlite3GlobalConfig.bFullMutex = 0; /* Disable mutex on connections */
break;
}
+#endif
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-59593-21810 */
case SQLITE_CONFIG_SERIALIZED: {
- /* Enable all mutexing */
- sqlite3GlobalConfig.bCoreMutex = 1;
- sqlite3GlobalConfig.bFullMutex = 1;
+ /* EVIDENCE-OF: R-41220-51800 This option sets the threading mode to
+ ** Serialized. */
+ sqlite3GlobalConfig.bCoreMutex = 1; /* Enable mutex on core */
+ sqlite3GlobalConfig.bFullMutex = 1; /* Enable mutex on connections */
break;
}
+#endif
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-63666-48755 */
case SQLITE_CONFIG_MUTEX: {
/* Specify an alternative mutex implementation */
sqlite3GlobalConfig.mutex = *va_arg(ap, sqlite3_mutex_methods*);
break;
}
+#endif
+#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 /* IMP: R-14450-37597 */
case SQLITE_CONFIG_GETMUTEX: {
/* Retrieve the current mutex implementation */
*va_arg(ap, sqlite3_mutex_methods*) = sqlite3GlobalConfig.mutex;
@@ -110476,55 +138237,102 @@ SQLITE_API int sqlite3_config(int op, ...){
}
#endif
-
case SQLITE_CONFIG_MALLOC: {
- /* Specify an alternative malloc implementation */
+ /* EVIDENCE-OF: R-55594-21030 The SQLITE_CONFIG_MALLOC option takes a
+ ** single argument which is a pointer to an instance of the
+ ** sqlite3_mem_methods structure. The argument specifies alternative
+ ** low-level memory allocation routines to be used in place of the memory
+ ** allocation routines built into SQLite. */
sqlite3GlobalConfig.m = *va_arg(ap, sqlite3_mem_methods*);
break;
}
case SQLITE_CONFIG_GETMALLOC: {
- /* Retrieve the current malloc() implementation */
+ /* EVIDENCE-OF: R-51213-46414 The SQLITE_CONFIG_GETMALLOC option takes a
+ ** single argument which is a pointer to an instance of the
+ ** sqlite3_mem_methods structure. The sqlite3_mem_methods structure is
+ ** filled with the currently defined memory allocation routines. */
if( sqlite3GlobalConfig.m.xMalloc==0 ) sqlite3MemSetDefault();
*va_arg(ap, sqlite3_mem_methods*) = sqlite3GlobalConfig.m;
break;
}
case SQLITE_CONFIG_MEMSTATUS: {
- /* Enable or disable the malloc status collection */
+ /* EVIDENCE-OF: R-61275-35157 The SQLITE_CONFIG_MEMSTATUS option takes
+ ** single argument of type int, interpreted as a boolean, which enables
+ ** or disables the collection of memory allocation statistics. */
sqlite3GlobalConfig.bMemstat = va_arg(ap, int);
break;
}
case SQLITE_CONFIG_SCRATCH: {
- /* Designate a buffer for scratch memory space */
+ /* EVIDENCE-OF: R-08404-60887 There are three arguments to
+ ** SQLITE_CONFIG_SCRATCH: A pointer an 8-byte aligned memory buffer from
+ ** which the scratch allocations will be drawn, the size of each scratch
+ ** allocation (sz), and the maximum number of scratch allocations (N). */
sqlite3GlobalConfig.pScratch = va_arg(ap, void*);
sqlite3GlobalConfig.szScratch = va_arg(ap, int);
sqlite3GlobalConfig.nScratch = va_arg(ap, int);
break;
}
case SQLITE_CONFIG_PAGECACHE: {
- /* Designate a buffer for page cache memory space */
+ /* EVIDENCE-OF: R-18761-36601 There are three arguments to
+ ** SQLITE_CONFIG_PAGECACHE: A pointer to 8-byte aligned memory (pMem),
+ ** the size of each page cache line (sz), and the number of cache lines
+ ** (N). */
sqlite3GlobalConfig.pPage = va_arg(ap, void*);
sqlite3GlobalConfig.szPage = va_arg(ap, int);
sqlite3GlobalConfig.nPage = va_arg(ap, int);
break;
}
+ case SQLITE_CONFIG_PCACHE_HDRSZ: {
+ /* EVIDENCE-OF: R-39100-27317 The SQLITE_CONFIG_PCACHE_HDRSZ option takes
+ ** a single parameter which is a pointer to an integer and writes into
+ ** that integer the number of extra bytes per page required for each page
+ ** in SQLITE_CONFIG_PAGECACHE. */
+ *va_arg(ap, int*) =
+ sqlite3HeaderSizeBtree() +
+ sqlite3HeaderSizePcache() +
+ sqlite3HeaderSizePcache1();
+ break;
+ }
case SQLITE_CONFIG_PCACHE: {
- /* Specify an alternative page cache implementation */
- sqlite3GlobalConfig.pcache = *va_arg(ap, sqlite3_pcache_methods*);
+ /* no-op */
break;
}
-
case SQLITE_CONFIG_GETPCACHE: {
- if( sqlite3GlobalConfig.pcache.xInit==0 ){
+ /* now an error */
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ case SQLITE_CONFIG_PCACHE2: {
+ /* EVIDENCE-OF: R-63325-48378 The SQLITE_CONFIG_PCACHE2 option takes a
+ ** single argument which is a pointer to an sqlite3_pcache_methods2
+ ** object. This object specifies the interface to a custom page cache
+ ** implementation. */
+ sqlite3GlobalConfig.pcache2 = *va_arg(ap, sqlite3_pcache_methods2*);
+ break;
+ }
+ case SQLITE_CONFIG_GETPCACHE2: {
+ /* EVIDENCE-OF: R-22035-46182 The SQLITE_CONFIG_GETPCACHE2 option takes a
+ ** single argument which is a pointer to an sqlite3_pcache_methods2
+ ** object. SQLite copies of the current page cache implementation into
+ ** that object. */
+ if( sqlite3GlobalConfig.pcache2.xInit==0 ){
sqlite3PCacheSetDefault();
}
- *va_arg(ap, sqlite3_pcache_methods*) = sqlite3GlobalConfig.pcache;
+ *va_arg(ap, sqlite3_pcache_methods2*) = sqlite3GlobalConfig.pcache2;
break;
}
+/* EVIDENCE-OF: R-06626-12911 The SQLITE_CONFIG_HEAP option is only
+** available if SQLite is compiled with either SQLITE_ENABLE_MEMSYS3 or
+** SQLITE_ENABLE_MEMSYS5 and returns SQLITE_ERROR if invoked otherwise. */
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
case SQLITE_CONFIG_HEAP: {
- /* Designate a buffer for heap memory space */
+ /* EVIDENCE-OF: R-19854-42126 There are three arguments to
+ ** SQLITE_CONFIG_HEAP: An 8-byte aligned pointer to the memory, the
+ ** number of bytes in the memory buffer, and the minimum allocation size.
+ */
sqlite3GlobalConfig.pHeap = va_arg(ap, void*);
sqlite3GlobalConfig.nHeap = va_arg(ap, int);
sqlite3GlobalConfig.mnReq = va_arg(ap, int);
@@ -110537,17 +138345,19 @@ SQLITE_API int sqlite3_config(int op, ...){
}
if( sqlite3GlobalConfig.pHeap==0 ){
- /* If the heap pointer is NULL, then restore the malloc implementation
- ** back to NULL pointers too. This will cause the malloc to go
- ** back to its default implementation when sqlite3_initialize() is
- ** run.
+ /* EVIDENCE-OF: R-49920-60189 If the first pointer (the memory pointer)
+ ** is NULL, then SQLite reverts to using its default memory allocator
+ ** (the system malloc() implementation), undoing any prior invocation of
+ ** SQLITE_CONFIG_MALLOC.
+ **
+ ** Setting sqlite3GlobalConfig.m to all zeros will cause malloc to
+ ** revert to its default implementation when sqlite3_initialize() is run
*/
memset(&sqlite3GlobalConfig.m, 0, sizeof(sqlite3GlobalConfig.m));
}else{
- /* The heap pointer is not NULL, then install one of the
- ** mem5.c/mem3.c methods. If neither ENABLE_MEMSYS3 nor
- ** ENABLE_MEMSYS5 is defined, return an error.
- */
+ /* EVIDENCE-OF: R-61006-08918 If the memory pointer is not NULL then the
+ ** alternative memory allocator is engaged to handle all of SQLites
+ ** memory allocation needs. */
#ifdef SQLITE_ENABLE_MEMSYS3
sqlite3GlobalConfig.m = *sqlite3MemGetMemsys3();
#endif
@@ -110565,7 +138375,7 @@ SQLITE_API int sqlite3_config(int op, ...){
break;
}
- /* Record a pointer to the logger funcction and its first argument.
+ /* Record a pointer to the logger function and its first argument.
** The default is NULL. Logging is disabled if the function pointer is
** NULL.
*/
@@ -110580,11 +138390,83 @@ SQLITE_API int sqlite3_config(int op, ...){
break;
}
+ /* EVIDENCE-OF: R-55548-33817 The compile-time setting for URI filenames
+ ** can be changed at start-time using the
+ ** sqlite3_config(SQLITE_CONFIG_URI,1) or
+ ** sqlite3_config(SQLITE_CONFIG_URI,0) configuration calls.
+ */
case SQLITE_CONFIG_URI: {
+ /* EVIDENCE-OF: R-25451-61125 The SQLITE_CONFIG_URI option takes a single
+ ** argument of type int. If non-zero, then URI handling is globally
+ ** enabled. If the parameter is zero, then URI handling is globally
+ ** disabled. */
sqlite3GlobalConfig.bOpenUri = va_arg(ap, int);
break;
}
+ case SQLITE_CONFIG_COVERING_INDEX_SCAN: {
+ /* EVIDENCE-OF: R-36592-02772 The SQLITE_CONFIG_COVERING_INDEX_SCAN
+ ** option takes a single integer argument which is interpreted as a
+ ** boolean in order to enable or disable the use of covering indices for
+ ** full table scans in the query optimizer. */
+ sqlite3GlobalConfig.bUseCis = va_arg(ap, int);
+ break;
+ }
+
+#ifdef SQLITE_ENABLE_SQLLOG
+ case SQLITE_CONFIG_SQLLOG: {
+ typedef void(*SQLLOGFUNC_t)(void*, sqlite3*, const char*, int);
+ sqlite3GlobalConfig.xSqllog = va_arg(ap, SQLLOGFUNC_t);
+ sqlite3GlobalConfig.pSqllogArg = va_arg(ap, void *);
+ break;
+ }
+#endif
+
+ case SQLITE_CONFIG_MMAP_SIZE: {
+ /* EVIDENCE-OF: R-58063-38258 SQLITE_CONFIG_MMAP_SIZE takes two 64-bit
+ ** integer (sqlite3_int64) values that are the default mmap size limit
+ ** (the default setting for PRAGMA mmap_size) and the maximum allowed
+ ** mmap size limit. */
+ sqlite3_int64 szMmap = va_arg(ap, sqlite3_int64);
+ sqlite3_int64 mxMmap = va_arg(ap, sqlite3_int64);
+ /* EVIDENCE-OF: R-53367-43190 If either argument to this option is
+ ** negative, then that argument is changed to its compile-time default.
+ **
+ ** EVIDENCE-OF: R-34993-45031 The maximum allowed mmap size will be
+ ** silently truncated if necessary so that it does not exceed the
+ ** compile-time maximum mmap size set by the SQLITE_MAX_MMAP_SIZE
+ ** compile-time option.
+ */
+ if( mxMmap<0 || mxMmap>SQLITE_MAX_MMAP_SIZE ){
+ mxMmap = SQLITE_MAX_MMAP_SIZE;
+ }
+ if( szMmap<0 ) szMmap = SQLITE_DEFAULT_MMAP_SIZE;
+ if( szMmap>mxMmap) szMmap = mxMmap;
+ sqlite3GlobalConfig.mxMmap = mxMmap;
+ sqlite3GlobalConfig.szMmap = szMmap;
+ break;
+ }
+
+#if SQLITE_OS_WIN && defined(SQLITE_WIN32_MALLOC) /* IMP: R-04780-55815 */
+ case SQLITE_CONFIG_WIN32_HEAPSIZE: {
+ /* EVIDENCE-OF: R-34926-03360 SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit
+ ** unsigned integer value that specifies the maximum size of the created
+ ** heap. */
+ sqlite3GlobalConfig.nHeap = va_arg(ap, int);
+ break;
+ }
+#endif
+
+ case SQLITE_CONFIG_PMASZ: {
+ sqlite3GlobalConfig.szPma = va_arg(ap, unsigned int);
+ break;
+ }
+
+ case SQLITE_CONFIG_STMTJRNL_SPILL: {
+ sqlite3GlobalConfig.nStmtSpill = va_arg(ap, int);
+ break;
+ }
+
default: {
rc = SQLITE_ERROR;
break;
@@ -110606,6 +138488,7 @@ SQLITE_API int sqlite3_config(int op, ...){
** the lookaside memory.
*/
static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
+#ifndef SQLITE_OMIT_LOOKASIDE
void *pStart;
if( db->lookaside.nOut ){
return SQLITE_BUSY;
@@ -110617,21 +138500,21 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
if( db->lookaside.bMalloced ){
sqlite3_free(db->lookaside.pStart);
}
- /* The size of a lookaside slot needs to be larger than a pointer
- ** to be useful.
+ /* The size of a lookaside slot after ROUNDDOWN8 needs to be larger
+ ** than a pointer to be useful.
*/
+ sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0;
if( cnt<0 ) cnt = 0;
if( sz==0 || cnt==0 ){
sz = 0;
pStart = 0;
}else if( pBuf==0 ){
- sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
sqlite3BeginBenignMalloc();
pStart = sqlite3Malloc( sz*cnt ); /* IMP: R-61949-35727 */
sqlite3EndBenignMalloc();
+ if( pStart ) cnt = sqlite3MallocSize(pStart)/sz;
}else{
- sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
pStart = pBuf;
}
db->lookaside.pStart = pStart;
@@ -110648,13 +138531,15 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
p = (LookasideSlot*)&((u8*)p)[sz];
}
db->lookaside.pEnd = p;
- db->lookaside.bEnabled = 1;
+ db->lookaside.bDisable = 0;
db->lookaside.bMalloced = pBuf==0 ?1:0;
}else{
- db->lookaside.pEnd = 0;
- db->lookaside.bEnabled = 0;
+ db->lookaside.pStart = db;
+ db->lookaside.pEnd = db;
+ db->lookaside.bDisable = 1;
db->lookaside.bMalloced = 0;
}
+#endif /* SQLITE_OMIT_LOOKASIDE */
return SQLITE_OK;
}
@@ -110662,10 +138547,70 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
** Return the mutex associated with a database connection.
*/
SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
return db->mutex;
}
/*
+** Free up as much memory as we can from the given database
+** connection.
+*/
+SQLITE_API int sqlite3_db_release_memory(sqlite3 *db){
+ int i;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3BtreeEnterAll(db);
+ for(i=0; i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ){
+ Pager *pPager = sqlite3BtreePager(pBt);
+ sqlite3PagerShrink(pPager);
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+}
+
+/*
+** Flush any dirty pages in the pager-cache for any attached database
+** to disk.
+*/
+SQLITE_API int sqlite3_db_cacheflush(sqlite3 *db){
+ int i;
+ int rc = SQLITE_OK;
+ int bSeenBusy = 0;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ sqlite3BtreeEnterAll(db);
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt && sqlite3BtreeIsInTrans(pBt) ){
+ Pager *pPager = sqlite3BtreePager(pBt);
+ rc = sqlite3PagerFlush(pPager);
+ if( rc==SQLITE_BUSY ){
+ bSeenBusy = 1;
+ rc = SQLITE_OK;
+ }
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+ sqlite3_mutex_leave(db->mutex);
+ return ((rc==SQLITE_OK && bSeenBusy) ? SQLITE_BUSY : rc);
+}
+
+/*
** Configuration settings for an individual database connection
*/
SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
@@ -110673,6 +138618,11 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
int rc;
va_start(ap, op);
switch( op ){
+ case SQLITE_DBCONFIG_MAINDBNAME: {
+ db->aDb[0].zDbSName = va_arg(ap,char*);
+ rc = SQLITE_OK;
+ break;
+ }
case SQLITE_DBCONFIG_LOOKASIDE: {
void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */
int sz = va_arg(ap, int); /* IMP: R-47871-25994 */
@@ -110685,8 +138635,10 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
int op; /* The opcode */
u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */
} aFlagOp[] = {
- { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
- { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
+ { SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
+ { SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, SQLITE_Fts3Tokenizer },
+ { SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION, SQLITE_LoadExtension },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -110740,13 +138692,20 @@ static int binCollFunc(
){
int rc, n;
n = nKey1<nKey2 ? nKey1 : nKey2;
+ /* EVIDENCE-OF: R-65033-28449 The built-in BINARY collation compares
+ ** strings byte by byte using the memcmp() function from the standard C
+ ** library. */
rc = memcmp(pKey1, pKey2, n);
if( rc==0 ){
if( padFlag
&& allSpaces(((char*)pKey1)+n, nKey1-n)
&& allSpaces(((char*)pKey2)+n, nKey2-n)
){
- /* Leave rc unchanged at 0 */
+ /* EVIDENCE-OF: R-31624-24737 RTRIM is like BINARY except that extra
+ ** spaces at the end of either string do not change the result. In other
+ ** words, strings will compare equal to one another as long as they
+ ** differ only in the number of spaces at the end.
+ */
}else{
rc = nKey1 - nKey2;
}
@@ -110757,7 +138716,7 @@ static int binCollFunc(
/*
** Another built-in collating sequence: NOCASE.
**
-** This collating sequence is intended to be used for "case independant
+** This collating sequence is intended to be used for "case independent
** comparison". SQLite's knowledge of upper and lower case equivalents
** extends only to the 26 characters used in the English language.
**
@@ -110781,6 +138740,12 @@ static int nocaseCollatingFunc(
** Return the ROWID of the most recent insert
*/
SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
return db->lastRowid;
}
@@ -110788,6 +138753,12 @@ SQLITE_API sqlite_int64 sqlite3_last_insert_rowid(sqlite3 *db){
** Return the number of changes in the most recent call to sqlite3_exec().
*/
SQLITE_API int sqlite3_changes(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
return db->nChange;
}
@@ -110795,6 +138766,12 @@ SQLITE_API int sqlite3_changes(sqlite3 *db){
** Return the number of changes since the database handle was opened.
*/
SQLITE_API int sqlite3_total_changes(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
return db->nTotalChange;
}
@@ -110821,7 +138798,7 @@ SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *db){
** with SQLITE_ANY as the encoding.
*/
static void functionDestroy(sqlite3 *db, FuncDef *p){
- FuncDestructor *pDestructor = p->pDestructor;
+ FuncDestructor *pDestructor = p->u.pDestructor;
if( pDestructor ){
pDestructor->nRef--;
if( pDestructor->nRef==0 ){
@@ -110832,24 +138809,72 @@ static void functionDestroy(sqlite3 *db, FuncDef *p){
}
/*
-** Close an existing SQLite database
+** Disconnect all sqlite3_vtab objects that belong to database connection
+** db. This is called when db is being closed.
*/
-SQLITE_API int sqlite3_close(sqlite3 *db){
- HashElem *i; /* Hash table iterator */
+static void disconnectAllVtab(sqlite3 *db){
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ int i;
+ HashElem *p;
+ sqlite3BtreeEnterAll(db);
+ for(i=0; i<db->nDb; i++){
+ Schema *pSchema = db->aDb[i].pSchema;
+ if( db->aDb[i].pSchema ){
+ for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){
+ Table *pTab = (Table *)sqliteHashData(p);
+ if( IsVirtual(pTab) ) sqlite3VtabDisconnect(db, pTab);
+ }
+ }
+ }
+ for(p=sqliteHashFirst(&db->aModule); p; p=sqliteHashNext(p)){
+ Module *pMod = (Module *)sqliteHashData(p);
+ if( pMod->pEpoTab ){
+ sqlite3VtabDisconnect(db, pMod->pEpoTab);
+ }
+ }
+ sqlite3VtabUnlockList(db);
+ sqlite3BtreeLeaveAll(db);
+#else
+ UNUSED_PARAMETER(db);
+#endif
+}
+
+/*
+** Return TRUE if database connection db has unfinalized prepared
+** statements or unfinished sqlite3_backup objects.
+*/
+static int connectionIsBusy(sqlite3 *db){
int j;
+ assert( sqlite3_mutex_held(db->mutex) );
+ if( db->pVdbe ) return 1;
+ for(j=0; j<db->nDb; j++){
+ Btree *pBt = db->aDb[j].pBt;
+ if( pBt && sqlite3BtreeIsInBackup(pBt) ) return 1;
+ }
+ return 0;
+}
+/*
+** Close an existing SQLite database
+*/
+static int sqlite3Close(sqlite3 *db, int forceZombie){
if( !db ){
+ /* EVIDENCE-OF: R-63257-11740 Calling sqlite3_close() or
+ ** sqlite3_close_v2() with a NULL pointer argument is a harmless no-op. */
return SQLITE_OK;
}
if( !sqlite3SafetyCheckSickOrOk(db) ){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(db->mutex);
+ if( db->mTrace & SQLITE_TRACE_CLOSE ){
+ db->xTrace(SQLITE_TRACE_CLOSE, db->pTraceArg, db, 0);
+ }
- /* Force xDestroy calls on all virtual tables */
- sqlite3ResetInternalSchema(db, -1);
+ /* Force xDisconnect calls on all virtual tables */
+ disconnectAllVtab(db);
- /* If a transaction is open, the ResetInternalSchema() call above
+ /* If a transaction is open, the disconnectAllVtab() call above
** will not have called the xDisconnect() method on any virtual
** tables in the db->aVTrans[] array. The following sqlite3VtabRollback()
** call will do so. We need to do this before the check for active
@@ -110858,28 +138883,80 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
*/
sqlite3VtabRollback(db);
- /* If there are any outstanding VMs, return SQLITE_BUSY. */
- if( db->pVdbe ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinalised statements");
+ /* Legacy behavior (sqlite3_close() behavior) is to return
+ ** SQLITE_BUSY if the connection can not be closed immediately.
+ */
+ if( !forceZombie && connectionIsBusy(db) ){
+ sqlite3ErrorWithMsg(db, SQLITE_BUSY, "unable to close due to unfinalized "
+ "statements or unfinished backups");
sqlite3_mutex_leave(db->mutex);
return SQLITE_BUSY;
}
- assert( sqlite3SafetyCheckSickOrOk(db) );
- for(j=0; j<db->nDb; j++){
- Btree *pBt = db->aDb[j].pBt;
- if( pBt && sqlite3BtreeIsInBackup(pBt) ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinished backup operation");
- sqlite3_mutex_leave(db->mutex);
- return SQLITE_BUSY;
- }
+#ifdef SQLITE_ENABLE_SQLLOG
+ if( sqlite3GlobalConfig.xSqllog ){
+ /* Closing the handle. Fourth parameter is passed the value 2. */
+ sqlite3GlobalConfig.xSqllog(sqlite3GlobalConfig.pSqllogArg, db, 0, 2);
}
+#endif
+
+ /* Convert the connection into a zombie and then close it.
+ */
+ db->magic = SQLITE_MAGIC_ZOMBIE;
+ sqlite3LeaveMutexAndCloseZombie(db);
+ return SQLITE_OK;
+}
+
+/*
+** Two variations on the public interface for closing a database
+** connection. The sqlite3_close() version returns SQLITE_BUSY and
+** leaves the connection option if there are unfinalized prepared
+** statements or unfinished sqlite3_backups. The sqlite3_close_v2()
+** version forces the connection to become a zombie if there are
+** unclosed resources, and arranges for deallocation when the last
+** prepare statement or sqlite3_backup closes.
+*/
+SQLITE_API int sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); }
+SQLITE_API int sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); }
+
+
+/*
+** Close the mutex on database connection db.
+**
+** Furthermore, if database connection db is a zombie (meaning that there
+** has been a prior call to sqlite3_close(db) or sqlite3_close_v2(db)) and
+** every sqlite3_stmt has now been finalized and every sqlite3_backup has
+** finished, then free all resources.
+*/
+SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
+ HashElem *i; /* Hash table iterator */
+ int j;
+
+ /* If there are outstanding sqlite3_stmt or sqlite3_backup objects
+ ** or if the connection has not yet been closed by sqlite3_close_v2(),
+ ** then just leave the mutex and return.
+ */
+ if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){
+ sqlite3_mutex_leave(db->mutex);
+ return;
+ }
+
+ /* If we reach this point, it means that the database connection has
+ ** closed all sqlite3_stmt and sqlite3_backup objects and has been
+ ** passed to sqlite3_close (meaning that it is a zombie). Therefore,
+ ** go ahead and free all resources.
+ */
+
+ /* If a transaction is open, roll it back. This also ensures that if
+ ** any database schemas have been modified by an uncommitted transaction
+ ** they are reset. And that the required b-tree mutex is held to make
+ ** the pager rollback and schema reset an atomic operation. */
+ sqlite3RollbackAll(db, SQLITE_OK);
/* Free any outstanding Savepoint structures. */
sqlite3CloseSavepoints(db);
+ /* Close all database connections */
for(j=0; j<db->nDb; j++){
struct Db *pDb = &db->aDb[j];
if( pDb->pBt ){
@@ -110890,27 +138967,33 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
}
}
}
- sqlite3ResetInternalSchema(db, -1);
+ /* Clear the TEMP schema separately and last */
+ if( db->aDb[1].pSchema ){
+ sqlite3SchemaClear(db->aDb[1].pSchema);
+ }
+ sqlite3VtabUnlockList(db);
+
+ /* Free up the array of auxiliary databases */
+ sqlite3CollapseDatabaseArray(db);
+ assert( db->nDb<=2 );
+ assert( db->aDb==db->aDbStatic );
/* Tell the code in notify.c that the connection no longer holds any
** locks and does not require any further unlock-notify callbacks.
*/
sqlite3ConnectionClosed(db);
- assert( db->nDb<=2 );
- assert( db->aDb==db->aDbStatic );
- for(j=0; j<ArraySize(db->aFunc.a); j++){
- FuncDef *pNext, *pHash, *p;
- for(p=db->aFunc.a[j]; p; p=pHash){
- pHash = p->pHash;
- while( p ){
- functionDestroy(db, p);
- pNext = p->pNext;
- sqlite3DbFree(db, p);
- p = pNext;
- }
- }
+ for(i=sqliteHashFirst(&db->aFunc); i; i=sqliteHashNext(i)){
+ FuncDef *pNext, *p;
+ p = sqliteHashData(i);
+ do{
+ functionDestroy(db, p);
+ pNext = p->pNext;
+ sqlite3DbFree(db, p);
+ p = pNext;
+ }while( p );
}
+ sqlite3HashClear(&db->aFunc);
for(i=sqliteHashFirst(&db->aCollSeq); i; i=sqliteHashNext(i)){
CollSeq *pColl = (CollSeq *)sqliteHashData(i);
/* Invoke any destructors registered for collation sequence user data. */
@@ -110928,16 +139011,19 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
if( pMod->xDestroy ){
pMod->xDestroy(pMod->pAux);
}
+ sqlite3VtabEponymousTableClear(db, pMod);
sqlite3DbFree(db, pMod);
}
sqlite3HashClear(&db->aModule);
#endif
- sqlite3Error(db, SQLITE_OK, 0); /* Deallocates any cached error strings. */
- if( db->pErr ){
- sqlite3ValueFree(db->pErr);
- }
+ sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */
+ sqlite3ValueFree(db->pErr);
sqlite3CloseExtensions(db);
+#if SQLITE_USER_AUTHENTICATION
+ sqlite3_free(db->auth.zAuthUser);
+ sqlite3_free(db->auth.zAuthPW);
+#endif
db->magic = SQLITE_MAGIC_ERROR;
@@ -110956,36 +139042,53 @@ SQLITE_API int sqlite3_close(sqlite3 *db){
sqlite3_free(db->lookaside.pStart);
}
sqlite3_free(db);
- return SQLITE_OK;
}
/*
-** Rollback all database files.
+** Rollback all database files. If tripCode is not SQLITE_OK, then
+** any write cursors are invalidated ("tripped" - as in "tripping a circuit
+** breaker") and made to return tripCode if there are any further
+** attempts to use that cursor. Read cursors remain open and valid
+** but are "saved" in case the table pages are moved around.
*/
-SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db){
+SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
int i;
int inTrans = 0;
+ int schemaChange;
assert( sqlite3_mutex_held(db->mutex) );
sqlite3BeginBenignMalloc();
+
+ /* Obtain all b-tree mutexes before making any calls to BtreeRollback().
+ ** This is important in case the transaction being rolled back has
+ ** modified the database schema. If the b-tree mutexes are not taken
+ ** here, then another shared-cache connection might sneak in between
+ ** the database rollback and schema reset, which can cause false
+ ** corruption reports in some cases. */
+ sqlite3BtreeEnterAll(db);
+ schemaChange = (db->flags & SQLITE_InternChanges)!=0 && db->init.busy==0;
+
for(i=0; i<db->nDb; i++){
- if( db->aDb[i].pBt ){
- if( sqlite3BtreeIsInTrans(db->aDb[i].pBt) ){
+ Btree *p = db->aDb[i].pBt;
+ if( p ){
+ if( sqlite3BtreeIsInTrans(p) ){
inTrans = 1;
}
- sqlite3BtreeRollback(db->aDb[i].pBt);
- db->aDb[i].inTrans = 0;
+ sqlite3BtreeRollback(p, tripCode, !schemaChange);
}
}
sqlite3VtabRollback(db);
sqlite3EndBenignMalloc();
- if( db->flags&SQLITE_InternChanges ){
+ if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){
sqlite3ExpirePreparedStatements(db);
- sqlite3ResetInternalSchema(db, -1);
+ sqlite3ResetAllSchemasOfConnection(db);
}
+ sqlite3BtreeLeaveAll(db);
/* Any deferred constraint violations have now been resolved. */
db->nDeferredCons = 0;
+ db->nDeferredImmCons = 0;
+ db->flags &= ~SQLITE_DeferFKs;
/* If one has been configured, invoke the rollback-hook callback */
if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){
@@ -110994,6 +139097,115 @@ SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db){
}
/*
+** Return a static string containing the name corresponding to the error code
+** specified in the argument.
+*/
+#if defined(SQLITE_NEED_ERR_NAME)
+SQLITE_PRIVATE const char *sqlite3ErrName(int rc){
+ const char *zName = 0;
+ int i, origRc = rc;
+ for(i=0; i<2 && zName==0; i++, rc &= 0xff){
+ switch( rc ){
+ case SQLITE_OK: zName = "SQLITE_OK"; break;
+ case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
+ case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
+ case SQLITE_PERM: zName = "SQLITE_PERM"; break;
+ case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
+ case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
+ case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
+ case SQLITE_BUSY_RECOVERY: zName = "SQLITE_BUSY_RECOVERY"; break;
+ case SQLITE_BUSY_SNAPSHOT: zName = "SQLITE_BUSY_SNAPSHOT"; break;
+ case SQLITE_LOCKED: zName = "SQLITE_LOCKED"; break;
+ case SQLITE_LOCKED_SHAREDCACHE: zName = "SQLITE_LOCKED_SHAREDCACHE";break;
+ case SQLITE_NOMEM: zName = "SQLITE_NOMEM"; break;
+ case SQLITE_READONLY: zName = "SQLITE_READONLY"; break;
+ case SQLITE_READONLY_RECOVERY: zName = "SQLITE_READONLY_RECOVERY"; break;
+ case SQLITE_READONLY_CANTLOCK: zName = "SQLITE_READONLY_CANTLOCK"; break;
+ case SQLITE_READONLY_ROLLBACK: zName = "SQLITE_READONLY_ROLLBACK"; break;
+ case SQLITE_READONLY_DBMOVED: zName = "SQLITE_READONLY_DBMOVED"; break;
+ case SQLITE_INTERRUPT: zName = "SQLITE_INTERRUPT"; break;
+ case SQLITE_IOERR: zName = "SQLITE_IOERR"; break;
+ case SQLITE_IOERR_READ: zName = "SQLITE_IOERR_READ"; break;
+ case SQLITE_IOERR_SHORT_READ: zName = "SQLITE_IOERR_SHORT_READ"; break;
+ case SQLITE_IOERR_WRITE: zName = "SQLITE_IOERR_WRITE"; break;
+ case SQLITE_IOERR_FSYNC: zName = "SQLITE_IOERR_FSYNC"; break;
+ case SQLITE_IOERR_DIR_FSYNC: zName = "SQLITE_IOERR_DIR_FSYNC"; break;
+ case SQLITE_IOERR_TRUNCATE: zName = "SQLITE_IOERR_TRUNCATE"; break;
+ case SQLITE_IOERR_FSTAT: zName = "SQLITE_IOERR_FSTAT"; break;
+ case SQLITE_IOERR_UNLOCK: zName = "SQLITE_IOERR_UNLOCK"; break;
+ case SQLITE_IOERR_RDLOCK: zName = "SQLITE_IOERR_RDLOCK"; break;
+ case SQLITE_IOERR_DELETE: zName = "SQLITE_IOERR_DELETE"; break;
+ case SQLITE_IOERR_NOMEM: zName = "SQLITE_IOERR_NOMEM"; break;
+ case SQLITE_IOERR_ACCESS: zName = "SQLITE_IOERR_ACCESS"; break;
+ case SQLITE_IOERR_CHECKRESERVEDLOCK:
+ zName = "SQLITE_IOERR_CHECKRESERVEDLOCK"; break;
+ case SQLITE_IOERR_LOCK: zName = "SQLITE_IOERR_LOCK"; break;
+ case SQLITE_IOERR_CLOSE: zName = "SQLITE_IOERR_CLOSE"; break;
+ case SQLITE_IOERR_DIR_CLOSE: zName = "SQLITE_IOERR_DIR_CLOSE"; break;
+ case SQLITE_IOERR_SHMOPEN: zName = "SQLITE_IOERR_SHMOPEN"; break;
+ case SQLITE_IOERR_SHMSIZE: zName = "SQLITE_IOERR_SHMSIZE"; break;
+ case SQLITE_IOERR_SHMLOCK: zName = "SQLITE_IOERR_SHMLOCK"; break;
+ case SQLITE_IOERR_SHMMAP: zName = "SQLITE_IOERR_SHMMAP"; break;
+ case SQLITE_IOERR_SEEK: zName = "SQLITE_IOERR_SEEK"; break;
+ case SQLITE_IOERR_DELETE_NOENT: zName = "SQLITE_IOERR_DELETE_NOENT";break;
+ case SQLITE_IOERR_MMAP: zName = "SQLITE_IOERR_MMAP"; break;
+ case SQLITE_IOERR_GETTEMPPATH: zName = "SQLITE_IOERR_GETTEMPPATH"; break;
+ case SQLITE_IOERR_CONVPATH: zName = "SQLITE_IOERR_CONVPATH"; break;
+ case SQLITE_CORRUPT: zName = "SQLITE_CORRUPT"; break;
+ case SQLITE_CORRUPT_VTAB: zName = "SQLITE_CORRUPT_VTAB"; break;
+ case SQLITE_NOTFOUND: zName = "SQLITE_NOTFOUND"; break;
+ case SQLITE_FULL: zName = "SQLITE_FULL"; break;
+ case SQLITE_CANTOPEN: zName = "SQLITE_CANTOPEN"; break;
+ case SQLITE_CANTOPEN_NOTEMPDIR: zName = "SQLITE_CANTOPEN_NOTEMPDIR";break;
+ case SQLITE_CANTOPEN_ISDIR: zName = "SQLITE_CANTOPEN_ISDIR"; break;
+ case SQLITE_CANTOPEN_FULLPATH: zName = "SQLITE_CANTOPEN_FULLPATH"; break;
+ case SQLITE_CANTOPEN_CONVPATH: zName = "SQLITE_CANTOPEN_CONVPATH"; break;
+ case SQLITE_PROTOCOL: zName = "SQLITE_PROTOCOL"; break;
+ case SQLITE_EMPTY: zName = "SQLITE_EMPTY"; break;
+ case SQLITE_SCHEMA: zName = "SQLITE_SCHEMA"; break;
+ case SQLITE_TOOBIG: zName = "SQLITE_TOOBIG"; break;
+ case SQLITE_CONSTRAINT: zName = "SQLITE_CONSTRAINT"; break;
+ case SQLITE_CONSTRAINT_UNIQUE: zName = "SQLITE_CONSTRAINT_UNIQUE"; break;
+ case SQLITE_CONSTRAINT_TRIGGER: zName = "SQLITE_CONSTRAINT_TRIGGER";break;
+ case SQLITE_CONSTRAINT_FOREIGNKEY:
+ zName = "SQLITE_CONSTRAINT_FOREIGNKEY"; break;
+ case SQLITE_CONSTRAINT_CHECK: zName = "SQLITE_CONSTRAINT_CHECK"; break;
+ case SQLITE_CONSTRAINT_PRIMARYKEY:
+ zName = "SQLITE_CONSTRAINT_PRIMARYKEY"; break;
+ case SQLITE_CONSTRAINT_NOTNULL: zName = "SQLITE_CONSTRAINT_NOTNULL";break;
+ case SQLITE_CONSTRAINT_COMMITHOOK:
+ zName = "SQLITE_CONSTRAINT_COMMITHOOK"; break;
+ case SQLITE_CONSTRAINT_VTAB: zName = "SQLITE_CONSTRAINT_VTAB"; break;
+ case SQLITE_CONSTRAINT_FUNCTION:
+ zName = "SQLITE_CONSTRAINT_FUNCTION"; break;
+ case SQLITE_CONSTRAINT_ROWID: zName = "SQLITE_CONSTRAINT_ROWID"; break;
+ case SQLITE_MISMATCH: zName = "SQLITE_MISMATCH"; break;
+ case SQLITE_MISUSE: zName = "SQLITE_MISUSE"; break;
+ case SQLITE_NOLFS: zName = "SQLITE_NOLFS"; break;
+ case SQLITE_AUTH: zName = "SQLITE_AUTH"; break;
+ case SQLITE_FORMAT: zName = "SQLITE_FORMAT"; break;
+ case SQLITE_RANGE: zName = "SQLITE_RANGE"; break;
+ case SQLITE_NOTADB: zName = "SQLITE_NOTADB"; break;
+ case SQLITE_ROW: zName = "SQLITE_ROW"; break;
+ case SQLITE_NOTICE: zName = "SQLITE_NOTICE"; break;
+ case SQLITE_NOTICE_RECOVER_WAL: zName = "SQLITE_NOTICE_RECOVER_WAL";break;
+ case SQLITE_NOTICE_RECOVER_ROLLBACK:
+ zName = "SQLITE_NOTICE_RECOVER_ROLLBACK"; break;
+ case SQLITE_WARNING: zName = "SQLITE_WARNING"; break;
+ case SQLITE_WARNING_AUTOINDEX: zName = "SQLITE_WARNING_AUTOINDEX"; break;
+ case SQLITE_DONE: zName = "SQLITE_DONE"; break;
+ }
+ }
+ if( zName==0 ){
+ static char zBuf[50];
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "SQLITE_UNKNOWN(%d)", origRc);
+ zName = zBuf;
+ }
+ return zName;
+}
+#endif
+
+/*
** Return a static string that describes the kind of error specified in the
** argument.
*/
@@ -111027,12 +139239,21 @@ SQLITE_PRIVATE const char *sqlite3ErrStr(int rc){
/* SQLITE_RANGE */ "bind or column index out of range",
/* SQLITE_NOTADB */ "file is encrypted or is not a database",
};
- rc &= 0xff;
- if( ALWAYS(rc>=0) && rc<(int)(sizeof(aMsg)/sizeof(aMsg[0])) && aMsg[rc]!=0 ){
- return aMsg[rc];
- }else{
- return "unknown error";
+ const char *zErr = "unknown error";
+ switch( rc ){
+ case SQLITE_ABORT_ROLLBACK: {
+ zErr = "abort due to ROLLBACK";
+ break;
+ }
+ default: {
+ rc &= 0xff;
+ if( ALWAYS(rc>=0) && rc<ArraySize(aMsg) && aMsg[rc]!=0 ){
+ zErr = aMsg[rc];
+ }
+ break;
+ }
}
+ return zErr;
}
/*
@@ -111045,7 +139266,7 @@ static int sqliteDefaultBusyCallback(
void *ptr, /* Database connection */
int count /* Number of times table has been busy */
){
-#if SQLITE_OS_WIN || (defined(HAVE_USLEEP) && HAVE_USLEEP)
+#if SQLITE_OS_WIN || HAVE_USLEEP
static const u8 delays[] =
{ 1, 2, 5, 10, 15, 20, 25, 25, 25, 50, 50, 100 };
static const u8 totals[] =
@@ -111108,10 +139329,14 @@ SQLITE_API int sqlite3_busy_handler(
int (*xBusy)(void*,int),
void *pArg
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
db->busyHandler.xFunc = xBusy;
db->busyHandler.pArg = pArg;
db->busyHandler.nBusy = 0;
+ db->busyTimeout = 0;
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -111128,10 +139353,16 @@ SQLITE_API void sqlite3_progress_handler(
int (*xProgress)(void*),
void *pArg
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
if( nOps>0 ){
db->xProgress = xProgress;
- db->nProgressOps = nOps;
+ db->nProgressOps = (unsigned)nOps;
db->pProgressArg = pArg;
}else{
db->xProgress = 0;
@@ -111148,9 +139379,12 @@ SQLITE_API void sqlite3_progress_handler(
** specified number of milliseconds before returning 0.
*/
SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
if( ms>0 ){
- db->busyTimeout = ms;
sqlite3_busy_handler(db, sqliteDefaultBusyCallback, (void*)db);
+ db->busyTimeout = ms;
}else{
sqlite3_busy_handler(db, 0, 0);
}
@@ -111161,6 +139395,12 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){
** Cause any pending operation to stop at its earliest opportunity.
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return;
+ }
+#endif
db->u1.isInterrupted = 1;
}
@@ -111177,23 +139417,28 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
int nArg,
int enc,
void *pUserData,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*),
FuncDestructor *pDestructor
){
FuncDef *p;
int nName;
+ int extraFlags;
assert( sqlite3_mutex_held(db->mutex) );
if( zFunctionName==0 ||
- (xFunc && (xFinal || xStep)) ||
- (!xFunc && (xFinal && !xStep)) ||
- (!xFunc && (!xFinal && xStep)) ||
+ (xSFunc && (xFinal || xStep)) ||
+ (!xSFunc && (xFinal && !xStep)) ||
+ (!xSFunc && (!xFinal && xStep)) ||
(nArg<-1 || nArg>SQLITE_MAX_FUNCTION_ARG) ||
(255<(nName = sqlite3Strlen30( zFunctionName))) ){
return SQLITE_MISUSE_BKPT;
}
+
+ assert( SQLITE_FUNC_CONSTANT==SQLITE_DETERMINISTIC );
+ extraFlags = enc & SQLITE_DETERMINISTIC;
+ enc &= (SQLITE_FUNC_ENCMASK|SQLITE_ANY);
#ifndef SQLITE_OMIT_UTF16
/* If SQLITE_UTF16 is specified as the encoding type, transform this
@@ -111207,11 +139452,11 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
enc = SQLITE_UTF16NATIVE;
}else if( enc==SQLITE_ANY ){
int rc;
- rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8,
- pUserData, xFunc, xStep, xFinal, pDestructor);
+ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF8|extraFlags,
+ pUserData, xSFunc, xStep, xFinal, pDestructor);
if( rc==SQLITE_OK ){
- rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE,
- pUserData, xFunc, xStep, xFinal, pDestructor);
+ rc = sqlite3CreateFunc(db, zFunctionName, nArg, SQLITE_UTF16LE|extraFlags,
+ pUserData, xSFunc, xStep, xFinal, pDestructor);
}
if( rc!=SQLITE_OK ){
return rc;
@@ -111227,10 +139472,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
** is being overridden/deleted but there are no active VMs, allow the
** operation to continue but invalidate all precompiled statements.
*/
- p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 0);
- if( p && p->iPrefEnc==enc && p->nArg==nArg ){
- if( db->activeVdbeCnt ){
- sqlite3Error(db, SQLITE_BUSY,
+ p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 0);
+ if( p && (p->funcFlags & SQLITE_FUNC_ENCMASK)==enc && p->nArg==nArg ){
+ if( db->nVdbeActive ){
+ sqlite3ErrorWithMsg(db, SQLITE_BUSY,
"unable to delete/modify user-function due to active statements");
assert( !db->mallocFailed );
return SQLITE_BUSY;
@@ -111239,10 +139484,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
}
}
- p = sqlite3FindFunction(db, zFunctionName, nName, nArg, (u8)enc, 1);
+ p = sqlite3FindFunction(db, zFunctionName, nArg, (u8)enc, 1);
assert(p || db->mallocFailed);
if( !p ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
/* If an older version of the function with a configured destructor is
@@ -111252,10 +139497,10 @@ SQLITE_PRIVATE int sqlite3CreateFunc(
if( pDestructor ){
pDestructor->nRef++;
}
- p->pDestructor = pDestructor;
- p->flags = 0;
- p->xFunc = xFunc;
- p->xStep = xStep;
+ p->u.pDestructor = pDestructor;
+ p->funcFlags = (p->funcFlags & SQLITE_FUNC_ENCMASK) | extraFlags;
+ testcase( p->funcFlags & SQLITE_DETERMINISTIC );
+ p->xSFunc = xSFunc ? xSFunc : xStep;
p->xFinalize = xFinal;
p->pUserData = pUserData;
p->nArg = (u16)nArg;
@@ -111271,11 +139516,11 @@ SQLITE_API int sqlite3_create_function(
int nArg,
int enc,
void *p,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*)
){
- return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xFunc, xStep,
+ return sqlite3_create_function_v2(db, zFunc, nArg, enc, p, xSFunc, xStep,
xFinal, 0);
}
@@ -111285,13 +139530,19 @@ SQLITE_API int sqlite3_create_function_v2(
int nArg,
int enc,
void *p,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value **),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value **),
void (*xStep)(sqlite3_context*,int,sqlite3_value **),
void (*xFinal)(sqlite3_context*),
void (*xDestroy)(void *)
){
int rc = SQLITE_ERROR;
FuncDestructor *pArg = 0;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
if( xDestroy ){
pArg = (FuncDestructor *)sqlite3DbMallocZero(db, sizeof(FuncDestructor));
@@ -111302,7 +139553,7 @@ SQLITE_API int sqlite3_create_function_v2(
pArg->xDestroy = xDestroy;
pArg->pUserData = p;
}
- rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xFunc, xStep, xFinal, pArg);
+ rc = sqlite3CreateFunc(db, zFunc, nArg, enc, p, xSFunc, xStep, xFinal, pArg);
if( pArg && pArg->nRef==0 ){
assert( rc!=SQLITE_OK );
xDestroy(p);
@@ -111322,16 +139573,20 @@ SQLITE_API int sqlite3_create_function16(
int nArg,
int eTextRep,
void *p,
- void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xSFunc)(sqlite3_context*,int,sqlite3_value**),
void (*xStep)(sqlite3_context*,int,sqlite3_value**),
void (*xFinal)(sqlite3_context*)
){
int rc;
char *zFunc8;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zFunctionName==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed );
zFunc8 = sqlite3Utf16to8(db, zFunctionName, -1, SQLITE_UTF16NATIVE);
- rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xFunc, xStep, xFinal,0);
+ rc = sqlite3CreateFunc(db, zFunc8, nArg, eTextRep, p, xSFunc,xStep,xFinal,0);
sqlite3DbFree(db, zFunc8);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
@@ -111357,14 +139612,19 @@ SQLITE_API int sqlite3_overload_function(
const char *zName,
int nArg
){
- int nName = sqlite3Strlen30(zName);
- int rc;
+ int rc = SQLITE_OK;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zName==0 || nArg<-2 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
- if( sqlite3FindFunction(db, zName, nName, nArg, SQLITE_UTF8, 0)==0 ){
- sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
- 0, sqlite3InvalidFunction, 0, 0, 0);
+ if( sqlite3FindFunction(db, zName, nArg, SQLITE_UTF8, 0)==0 ){
+ rc = sqlite3CreateFunc(db, zName, nArg, SQLITE_UTF8,
+ 0, sqlite3InvalidFunction, 0, 0, 0);
}
- rc = sqlite3ApiExit(db, SQLITE_OK);
+ rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
@@ -111378,15 +139638,50 @@ SQLITE_API int sqlite3_overload_function(
** trace is a pointer to a function that is invoked at the start of each
** SQL statement.
*/
-SQLITE_API void *sqlite3_trace(sqlite3 *db, void (*xTrace)(void*,const char*), void *pArg){
+#ifndef SQLITE_OMIT_DEPRECATED
+SQLITE_API void *sqlite3_trace(sqlite3 *db, void(*xTrace)(void*,const char*), void *pArg){
void *pOld;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pOld = db->pTraceArg;
- db->xTrace = xTrace;
+ db->mTrace = xTrace ? SQLITE_TRACE_LEGACY : 0;
+ db->xTrace = (int(*)(u32,void*,void*,void*))xTrace;
db->pTraceArg = pArg;
sqlite3_mutex_leave(db->mutex);
return pOld;
}
+#endif /* SQLITE_OMIT_DEPRECATED */
+
+/* Register a trace callback using the version-2 interface.
+*/
+SQLITE_API int sqlite3_trace_v2(
+ sqlite3 *db, /* Trace this connection */
+ unsigned mTrace, /* Mask of events to be traced */
+ int(*xTrace)(unsigned,void*,void*,void*), /* Callback to invoke */
+ void *pArg /* Context */
+){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ if( mTrace==0 ) xTrace = 0;
+ if( xTrace==0 ) mTrace = 0;
+ db->mTrace = mTrace;
+ db->xTrace = xTrace;
+ db->pTraceArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
+ return SQLITE_OK;
+}
+
+#ifndef SQLITE_OMIT_DEPRECATED
/*
** Register a profile function. The pArg from the previously registered
** profile function is returned.
@@ -111401,6 +139696,13 @@ SQLITE_API void *sqlite3_profile(
void *pArg
){
void *pOld;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pOld = db->pProfileArg;
db->xProfile = xProfile;
@@ -111408,11 +139710,11 @@ SQLITE_API void *sqlite3_profile(
sqlite3_mutex_leave(db->mutex);
return pOld;
}
+#endif /* SQLITE_OMIT_DEPRECATED */
#endif /* SQLITE_OMIT_TRACE */
-/*** EXPERIMENTAL ***
-**
-** Register a function to be invoked when a transaction comments.
+/*
+** Register a function to be invoked when a transaction commits.
** If the invoked function returns non-zero, then the commit becomes a
** rollback.
*/
@@ -111422,6 +139724,13 @@ SQLITE_API void *sqlite3_commit_hook(
void *pArg /* Argument to the function */
){
void *pOld;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pOld = db->pCommitArg;
db->xCommitCallback = xCallback;
@@ -111440,6 +139749,13 @@ SQLITE_API void *sqlite3_update_hook(
void *pArg /* Argument to the function */
){
void *pRet;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pRet = db->pUpdateArg;
db->xUpdateCallback = xCallback;
@@ -111458,6 +139774,13 @@ SQLITE_API void *sqlite3_rollback_hook(
void *pArg /* Argument to the function */
){
void *pRet;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pRet = db->pRollbackArg;
db->xRollbackCallback = xCallback;
@@ -111466,6 +139789,27 @@ SQLITE_API void *sqlite3_rollback_hook(
return pRet;
}
+#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
+/*
+** Register a callback to be invoked each time a row is updated,
+** inserted or deleted using this database connection.
+*/
+SQLITE_API void *sqlite3_preupdate_hook(
+ sqlite3 *db, /* Attach the hook to this database */
+ void(*xCallback)( /* Callback function */
+ void*,sqlite3*,int,char const*,char const*,sqlite3_int64,sqlite3_int64),
+ void *pArg /* First callback argument */
+){
+ void *pRet;
+ sqlite3_mutex_enter(db->mutex);
+ pRet = db->pPreUpdateArg;
+ db->xPreUpdateCallback = xCallback;
+ db->pPreUpdateArg = pArg;
+ sqlite3_mutex_leave(db->mutex);
+ return pRet;
+}
+#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
+
#ifndef SQLITE_OMIT_WAL
/*
** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
@@ -111504,6 +139848,9 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){
UNUSED_PARAMETER(db);
UNUSED_PARAMETER(nFrame);
#else
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
if( nFrame>0 ){
sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame));
}else{
@@ -111524,6 +139871,12 @@ SQLITE_API void *sqlite3_wal_hook(
){
#ifndef SQLITE_OMIT_WAL
void *pRet;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
sqlite3_mutex_enter(db->mutex);
pRet = db->pWalArg;
db->xWalCallback = xCallback;
@@ -111551,14 +139904,21 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
int rc; /* Return code */
int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+
/* Initialize the output variables to -1 in case an error occurs. */
if( pnLog ) *pnLog = -1;
if( pnCkpt ) *pnCkpt = -1;
- assert( SQLITE_CHECKPOINT_FULL>SQLITE_CHECKPOINT_PASSIVE );
- assert( SQLITE_CHECKPOINT_FULL<SQLITE_CHECKPOINT_RESTART );
- assert( SQLITE_CHECKPOINT_PASSIVE+2==SQLITE_CHECKPOINT_RESTART );
- if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_RESTART ){
+ assert( SQLITE_CHECKPOINT_PASSIVE==0 );
+ assert( SQLITE_CHECKPOINT_FULL==1 );
+ assert( SQLITE_CHECKPOINT_RESTART==2 );
+ assert( SQLITE_CHECKPOINT_TRUNCATE==3 );
+ if( eMode<SQLITE_CHECKPOINT_PASSIVE || eMode>SQLITE_CHECKPOINT_TRUNCATE ){
+ /* EVIDENCE-OF: R-03996-12088 The M parameter must be a valid checkpoint
+ ** mode: */
return SQLITE_MISUSE;
}
@@ -111568,10 +139928,11 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
}
if( iDb<0 ){
rc = SQLITE_ERROR;
- sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb);
+ sqlite3ErrorWithMsg(db, SQLITE_ERROR, "unknown database: %s", zDb);
}else{
+ db->busyHandler.nBusy = 0;
rc = sqlite3Checkpoint(db, iDb, eMode, pnLog, pnCkpt);
- sqlite3Error(db, rc, 0);
+ sqlite3Error(db, rc);
}
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
@@ -111586,7 +139947,9 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
** checkpointed.
*/
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
- return sqlite3_wal_checkpoint_v2(db, zDb, SQLITE_CHECKPOINT_PASSIVE, 0, 0);
+ /* EVIDENCE-OF: R-41613-20553 The sqlite3_wal_checkpoint(D,X) is equivalent to
+ ** sqlite3_wal_checkpoint_v2(D,X,SQLITE_CHECKPOINT_PASSIVE,0,0). */
+ return sqlite3_wal_checkpoint_v2(db,zDb,SQLITE_CHECKPOINT_PASSIVE,0,0);
}
#ifndef SQLITE_OMIT_WAL
@@ -111661,9 +140024,11 @@ SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3 *db){
return ( db->temp_store!=1 );
#endif
#if SQLITE_TEMP_STORE==3
+ UNUSED_PARAMETER(db);
return 1;
#endif
#if SQLITE_TEMP_STORE<1 || SQLITE_TEMP_STORE>3
+ UNUSED_PARAMETER(db);
return 0;
#endif
}
@@ -111675,15 +140040,16 @@ SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3 *db){
SQLITE_API const char *sqlite3_errmsg(sqlite3 *db){
const char *z;
if( !db ){
- return sqlite3ErrStr(SQLITE_NOMEM);
+ return sqlite3ErrStr(SQLITE_NOMEM_BKPT);
}
if( !sqlite3SafetyCheckSickOrOk(db) ){
return sqlite3ErrStr(SQLITE_MISUSE_BKPT);
}
sqlite3_mutex_enter(db->mutex);
if( db->mallocFailed ){
- z = sqlite3ErrStr(SQLITE_NOMEM);
+ z = sqlite3ErrStr(SQLITE_NOMEM_BKPT);
}else{
+ testcase( db->pErr==0 );
z = (char*)sqlite3_value_text(db->pErr);
assert( !db->mallocFailed );
if( z==0 ){
@@ -111725,8 +140091,7 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){
}else{
z = sqlite3_value_text16(db->pErr);
if( z==0 ){
- sqlite3ValueSetStr(db->pErr, -1, sqlite3ErrStr(db->errCode),
- SQLITE_UTF8, SQLITE_STATIC);
+ sqlite3ErrorWithMsg(db, db->errCode, sqlite3ErrStr(db->errCode));
z = sqlite3_value_text16(db->pErr);
}
/* A malloc() may have failed within the call to sqlite3_value_text16()
@@ -111734,7 +140099,7 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3 *db){
** be cleared before returning. Do this directly, instead of via
** sqlite3ApiExit(), to avoid setting the database handle error message.
*/
- db->mallocFailed = 0;
+ sqlite3OomClear(db);
}
sqlite3_mutex_leave(db->mutex);
return z;
@@ -111750,7 +140115,7 @@ SQLITE_API int sqlite3_errcode(sqlite3 *db){
return SQLITE_MISUSE_BKPT;
}
if( !db || db->mallocFailed ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
return db->errCode & db->errMask;
}
@@ -111759,10 +140124,22 @@ SQLITE_API int sqlite3_extended_errcode(sqlite3 *db){
return SQLITE_MISUSE_BKPT;
}
if( !db || db->mallocFailed ){
- return SQLITE_NOMEM;
+ return SQLITE_NOMEM_BKPT;
}
return db->errCode;
}
+SQLITE_API int sqlite3_system_errno(sqlite3 *db){
+ return db ? db->iSysErrno : 0;
+}
+
+/*
+** Return a string that describes the kind of error specified in the
+** argument. For now, this simply calls the internal sqlite3ErrStr()
+** function.
+*/
+SQLITE_API const char *sqlite3_errstr(int rc){
+ return sqlite3ErrStr(rc);
+}
/*
** Create a new collating function for database "db". The name is zName
@@ -111772,14 +140149,12 @@ static int createCollation(
sqlite3* db,
const char *zName,
u8 enc,
- u8 collType,
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*),
void(*xDel)(void*)
){
CollSeq *pColl;
int enc2;
- int nName = sqlite3Strlen30(zName);
assert( sqlite3_mutex_held(db->mutex) );
@@ -111803,8 +140178,8 @@ static int createCollation(
*/
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 0);
if( pColl && pColl->xCmp ){
- if( db->activeVdbeCnt ){
- sqlite3Error(db, SQLITE_BUSY,
+ if( db->nVdbeActive ){
+ sqlite3ErrorWithMsg(db, SQLITE_BUSY,
"unable to delete/modify collation sequence due to active statements");
return SQLITE_BUSY;
}
@@ -111817,7 +140192,7 @@ static int createCollation(
** to be called.
*/
if( (pColl->enc & ~SQLITE_UTF16_ALIGNED)==enc2 ){
- CollSeq *aColl = sqlite3HashFind(&db->aCollSeq, zName, nName);
+ CollSeq *aColl = sqlite3HashFind(&db->aCollSeq, zName);
int j;
for(j=0; j<3; j++){
CollSeq *p = &aColl[j];
@@ -111832,13 +140207,12 @@ static int createCollation(
}
pColl = sqlite3FindCollSeq(db, (u8)enc2, zName, 1);
- if( pColl==0 ) return SQLITE_NOMEM;
+ if( pColl==0 ) return SQLITE_NOMEM_BKPT;
pColl->xCmp = xCompare;
pColl->pUser = pCtx;
pColl->xDel = xDel;
pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED));
- pColl->type = collType;
- sqlite3Error(db, SQLITE_OK, 0);
+ sqlite3Error(db, SQLITE_OK);
return SQLITE_OK;
}
@@ -111858,8 +140232,9 @@ static const int aHardLimit[] = {
SQLITE_MAX_FUNCTION_ARG,
SQLITE_MAX_ATTACHED,
SQLITE_MAX_LIKE_PATTERN_LENGTH,
- SQLITE_MAX_VARIABLE_NUMBER,
+ SQLITE_MAX_VARIABLE_NUMBER, /* IMP: R-38091-32352 */
SQLITE_MAX_TRIGGER_DEPTH,
+ SQLITE_MAX_WORKER_THREADS,
};
/*
@@ -111880,11 +140255,11 @@ static const int aHardLimit[] = {
#if SQLITE_MAX_VDBE_OP<40
# error SQLITE_MAX_VDBE_OP must be at least 40
#endif
-#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>1000
-# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 1000
+#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
+# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
#endif
-#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>62
-# error SQLITE_MAX_ATTACHED must be between 0 and 62
+#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
+# error SQLITE_MAX_ATTACHED must be between 0 and 125
#endif
#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
# error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1
@@ -111895,6 +140270,9 @@ static const int aHardLimit[] = {
#if SQLITE_MAX_TRIGGER_DEPTH<1
# error SQLITE_MAX_TRIGGER_DEPTH must be at least 1
#endif
+#if SQLITE_MAX_WORKER_THREADS<0 || SQLITE_MAX_WORKER_THREADS>50
+# error SQLITE_MAX_WORKER_THREADS must be between 0 and 50
+#endif
/*
@@ -111910,6 +140288,12 @@ static const int aHardLimit[] = {
SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
int oldLimit;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return -1;
+ }
+#endif
/* EVIDENCE-OF: R-30189-54097 For each limit category SQLITE_LIMIT_NAME
** there is a hard upper bound set at compile-time by a C preprocessor
@@ -111928,7 +140312,8 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
SQLITE_MAX_LIKE_PATTERN_LENGTH );
assert( aHardLimit[SQLITE_LIMIT_VARIABLE_NUMBER]==SQLITE_MAX_VARIABLE_NUMBER);
assert( aHardLimit[SQLITE_LIMIT_TRIGGER_DEPTH]==SQLITE_MAX_TRIGGER_DEPTH );
- assert( SQLITE_LIMIT_TRIGGER_DEPTH==(SQLITE_N_LIMIT-1) );
+ assert( aHardLimit[SQLITE_LIMIT_WORKER_THREADS]==SQLITE_MAX_WORKER_THREADS );
+ assert( SQLITE_LIMIT_WORKER_THREADS==(SQLITE_N_LIMIT-1) );
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
@@ -111985,37 +140370,50 @@ SQLITE_PRIVATE int sqlite3ParseUri(
assert( *pzErrMsg==0 );
- if( ((flags & SQLITE_OPEN_URI) || sqlite3GlobalConfig.bOpenUri)
- && nUri>=5 && memcmp(zUri, "file:", 5)==0
+ if( ((flags & SQLITE_OPEN_URI) /* IMP: R-48725-32206 */
+ || sqlite3GlobalConfig.bOpenUri) /* IMP: R-51689-46548 */
+ && nUri>=5 && memcmp(zUri, "file:", 5)==0 /* IMP: R-57884-37496 */
){
char *zOpt;
int eState; /* Parser state when parsing URI */
int iIn; /* Input character index */
int iOut = 0; /* Output character index */
- int nByte = nUri+2; /* Bytes of space to allocate */
+ u64 nByte = nUri+2; /* Bytes of space to allocate */
/* Make sure the SQLITE_OPEN_URI flag is set to indicate to the VFS xOpen
** method that there may be extra parameters following the file-name. */
flags |= SQLITE_OPEN_URI;
for(iIn=0; iIn<nUri; iIn++) nByte += (zUri[iIn]=='&');
- zFile = sqlite3_malloc(nByte);
- if( !zFile ) return SQLITE_NOMEM;
+ zFile = sqlite3_malloc64(nByte);
+ if( !zFile ) return SQLITE_NOMEM_BKPT;
+ iIn = 5;
+#ifdef SQLITE_ALLOW_URI_AUTHORITY
+ if( strncmp(zUri+5, "///", 3)==0 ){
+ iIn = 7;
+ /* The following condition causes URIs with five leading / characters
+ ** like file://///host/path to be converted into UNCs like //host/path.
+ ** The correct URI for that UNC has only two or four leading / characters
+ ** file://host/path or file:////host/path. But 5 leading slashes is a
+ ** common error, we are told, so we handle it as a special case. */
+ if( strncmp(zUri+7, "///", 3)==0 ){ iIn++; }
+ }else if( strncmp(zUri+5, "//localhost/", 12)==0 ){
+ iIn = 16;
+ }
+#else
/* Discard the scheme and authority segments of the URI. */
if( zUri[5]=='/' && zUri[6]=='/' ){
iIn = 7;
while( zUri[iIn] && zUri[iIn]!='/' ) iIn++;
-
if( iIn!=7 && (iIn!=16 || memcmp("localhost", &zUri[7], 9)) ){
*pzErrMsg = sqlite3_mprintf("invalid uri authority: %.*s",
iIn-7, &zUri[7]);
rc = SQLITE_ERROR;
goto parse_uri_out;
}
- }else{
- iIn = 5;
}
+#endif
/* Copy the filename and any query parameters into the zFile buffer.
** Decode %HH escape codes along the way.
@@ -112113,10 +140511,12 @@ SQLITE_PRIVATE int sqlite3ParseUri(
{ "ro", SQLITE_OPEN_READONLY },
{ "rw", SQLITE_OPEN_READWRITE },
{ "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
+ { "memory", SQLITE_OPEN_MEMORY },
{ 0, 0 }
};
- mask = SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+ mask = SQLITE_OPEN_READONLY | SQLITE_OPEN_READWRITE
+ | SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY;
aMode = aOpenMode;
limit = mask & flags;
zModeType = "access";
@@ -112137,7 +140537,7 @@ SQLITE_PRIVATE int sqlite3ParseUri(
rc = SQLITE_ERROR;
goto parse_uri_out;
}
- if( mode>limit ){
+ if( (mode & ~SQLITE_OPEN_MEMORY)>limit ){
*pzErrMsg = sqlite3_mprintf("%s mode not allowed: %s",
zModeType, zVal);
rc = SQLITE_PERM;
@@ -112151,11 +140551,12 @@ SQLITE_PRIVATE int sqlite3ParseUri(
}
}else{
- zFile = sqlite3_malloc(nUri+2);
- if( !zFile ) return SQLITE_NOMEM;
+ zFile = sqlite3_malloc64(nUri+2);
+ if( !zFile ) return SQLITE_NOMEM_BKPT;
memcpy(zFile, zUri, nUri);
zFile[nUri] = '\0';
zFile[nUri+1] = '\0';
+ flags &= ~SQLITE_OPEN_URI;
}
*ppVfs = sqlite3_vfs_find(zVfs);
@@ -112191,6 +140592,9 @@ static int openDatabase(
char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
+#endif
*ppDb = 0;
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
@@ -112213,7 +140617,9 @@ static int openDatabase(
testcase( (1<<(flags&7))==0x02 ); /* READONLY */
testcase( (1<<(flags&7))==0x04 ); /* READWRITE */
testcase( (1<<(flags&7))==0x40 ); /* READWRITE | CREATE */
- if( ((1<<(flags&7)) & 0x46)==0 ) return SQLITE_MISUSE_BKPT;
+ if( ((1<<(flags&7)) & 0x46)==0 ){
+ return SQLITE_MISUSE_BKPT; /* IMP: R-65497-44594 */
+ }
if( sqlite3GlobalConfig.bCoreMutex==0 ){
isThreadsafe = 0;
@@ -112272,10 +140678,19 @@ static int openDatabase(
assert( sizeof(db->aLimit)==sizeof(aHardLimit) );
memcpy(db->aLimit, aHardLimit, sizeof(db->aLimit));
+ db->aLimit[SQLITE_LIMIT_WORKER_THREADS] = SQLITE_DEFAULT_WORKER_THREADS;
db->autoCommit = 1;
db->nextAutovac = -1;
+ db->szMmap = sqlite3GlobalConfig.szMmap;
db->nextPagesize = 0;
- db->flags |= SQLITE_ShortColNames | SQLITE_AutoIndex | SQLITE_EnableTrigger
+ db->nMaxSorterMmap = 0x7FFFFFFF;
+ db->flags |= SQLITE_ShortColNames | SQLITE_EnableTrigger | SQLITE_CacheSpill
+#if !defined(SQLITE_DEFAULT_AUTOMATIC_INDEX) || SQLITE_DEFAULT_AUTOMATIC_INDEX
+ | SQLITE_AutoIndex
+#endif
+#if SQLITE_DEFAULT_CKPTFULLFSYNC
+ | SQLITE_CkptFullFSync
+#endif
#if SQLITE_DEFAULT_FILE_FORMAT<4
| SQLITE_LegacyFileFmt
#endif
@@ -112288,6 +140703,15 @@ static int openDatabase(
#if defined(SQLITE_DEFAULT_FOREIGN_KEYS) && SQLITE_DEFAULT_FOREIGN_KEYS
| SQLITE_ForeignKeys
#endif
+#if defined(SQLITE_REVERSE_UNORDERED_SELECTS)
+ | SQLITE_ReverseOrder
+#endif
+#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK)
+ | SQLITE_CellSizeCk
+#endif
+#if defined(SQLITE_ENABLE_FTS3_TOKENIZER)
+ | SQLITE_Fts3Tokenizer
+#endif
;
sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
@@ -112297,31 +140721,30 @@ static int openDatabase(
/* Add the default collation sequence BINARY. BINARY works for both UTF-8
** and UTF-16, so add a version for each to avoid any unnecessary
** conversions. The only error that can occur here is a malloc() failure.
- */
- createCollation(db, "BINARY", SQLITE_UTF8, SQLITE_COLL_BINARY, 0,
- binCollFunc, 0);
- createCollation(db, "BINARY", SQLITE_UTF16BE, SQLITE_COLL_BINARY, 0,
- binCollFunc, 0);
- createCollation(db, "BINARY", SQLITE_UTF16LE, SQLITE_COLL_BINARY, 0,
- binCollFunc, 0);
- createCollation(db, "RTRIM", SQLITE_UTF8, SQLITE_COLL_USER, (void*)1,
- binCollFunc, 0);
+ **
+ ** EVIDENCE-OF: R-52786-44878 SQLite defines three built-in collating
+ ** functions:
+ */
+ createCollation(db, sqlite3StrBINARY, SQLITE_UTF8, 0, binCollFunc, 0);
+ createCollation(db, sqlite3StrBINARY, SQLITE_UTF16BE, 0, binCollFunc, 0);
+ createCollation(db, sqlite3StrBINARY, SQLITE_UTF16LE, 0, binCollFunc, 0);
+ createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0);
+ createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0);
if( db->mallocFailed ){
goto opendb_out;
}
- db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "BINARY", 0);
+ /* EVIDENCE-OF: R-08308-17224 The default collating function for all
+ ** strings is BINARY.
+ */
+ db->pDfltColl = sqlite3FindCollSeq(db, SQLITE_UTF8, sqlite3StrBINARY, 0);
assert( db->pDfltColl!=0 );
- /* Also add a UTF-8 case-insensitive collation sequence. */
- createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0,
- nocaseCollatingFunc, 0);
-
/* Parse the filename/URI argument. */
db->openFlags = flags;
rc = sqlite3ParseUri(zVfs, zFilename, &flags, &db->pVfs, &zOpen, &zErrMsg);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_NOMEM ) db->mallocFailed = 1;
- sqlite3Error(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
+ if( rc==SQLITE_NOMEM ) sqlite3OomFault(db);
+ sqlite3ErrorWithMsg(db, rc, zErrMsg ? "%s" : 0, zErrMsg);
sqlite3_free(zErrMsg);
goto opendb_out;
}
@@ -112331,22 +140754,24 @@ static int openDatabase(
flags | SQLITE_OPEN_MAIN_DB);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_IOERR_NOMEM ){
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
- sqlite3Error(db, rc, 0);
+ sqlite3Error(db, rc);
goto opendb_out;
}
+ sqlite3BtreeEnter(db->aDb[0].pBt);
db->aDb[0].pSchema = sqlite3SchemaGet(db, db->aDb[0].pBt);
+ if( !db->mallocFailed ) ENC(db) = SCHEMA_ENC(db);
+ sqlite3BtreeLeave(db->aDb[0].pBt);
db->aDb[1].pSchema = sqlite3SchemaGet(db, 0);
-
- /* The default safety_level for the main database is 'full'; for the temp
- ** database it is 'NONE'. This matches the pager layer defaults.
+ /* The default safety_level for the main database is FULL; for the temp
+ ** database it is OFF. This matches the pager layer defaults.
*/
- db->aDb[0].zName = "main";
- db->aDb[0].safety_level = 3;
- db->aDb[1].zName = "temp";
- db->aDb[1].safety_level = 1;
+ db->aDb[0].zDbSName = "main";
+ db->aDb[0].safety_level = SQLITE_DEFAULT_SYNCHRONOUS+1;
+ db->aDb[1].zDbSName = "temp";
+ db->aDb[1].safety_level = PAGER_SYNCHRONOUS_OFF;
db->magic = SQLITE_MAGIC_OPEN;
if( db->mallocFailed ){
@@ -112357,16 +140782,28 @@ static int openDatabase(
** database schema yet. This is delayed until the first time the database
** is accessed.
*/
- sqlite3Error(db, SQLITE_OK, 0);
- sqlite3RegisterBuiltinFunctions(db);
+ sqlite3Error(db, SQLITE_OK);
+ sqlite3RegisterPerConnectionBuiltinFunctions(db);
+ rc = sqlite3_errcode(db);
+
+#ifdef SQLITE_ENABLE_FTS5
+ /* Register any built-in FTS5 module before loading the automatic
+ ** extensions. This allows automatic extensions to register FTS5
+ ** tokenizers and auxiliary functions. */
+ if( !db->mallocFailed && rc==SQLITE_OK ){
+ rc = sqlite3Fts5Init(db);
+ }
+#endif
/* Load automatic extensions - extensions that have been registered
** using the sqlite3_automatic_extension() API.
*/
- sqlite3AutoLoadExtensions(db);
- rc = sqlite3_errcode(db);
- if( rc!=SQLITE_OK ){
- goto opendb_out;
+ if( rc==SQLITE_OK ){
+ sqlite3AutoLoadExtensions(db);
+ rc = sqlite3_errcode(db);
+ if( rc!=SQLITE_OK ){
+ goto opendb_out;
+ }
}
#ifdef SQLITE_ENABLE_FTS1
@@ -112383,7 +140820,7 @@ static int openDatabase(
}
#endif
-#ifdef SQLITE_ENABLE_FTS3
+#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
if( !db->mallocFailed && rc==SQLITE_OK ){
rc = sqlite3Fts3Init(db);
}
@@ -112401,7 +140838,17 @@ static int openDatabase(
}
#endif
- sqlite3Error(db, rc, 0);
+#ifdef SQLITE_ENABLE_DBSTAT_VTAB
+ if( !db->mallocFailed && rc==SQLITE_OK){
+ rc = sqlite3DbstatRegister(db);
+ }
+#endif
+
+#ifdef SQLITE_ENABLE_JSON1
+ if( !db->mallocFailed && rc==SQLITE_OK){
+ rc = sqlite3Json1Init(db);
+ }
+#endif
/* -DSQLITE_DEFAULT_LOCKING_MODE=1 makes EXCLUSIVE the default locking
** mode. -DSQLITE_DEFAULT_LOCKING_MODE=0 make NORMAL the default locking
@@ -112413,6 +140860,8 @@ static int openDatabase(
SQLITE_DEFAULT_LOCKING_MODE);
#endif
+ if( rc ) sqlite3Error(db, rc);
+
/* Enable the lookaside-malloc subsystem */
setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside,
sqlite3GlobalConfig.nLookaside);
@@ -112420,12 +140869,13 @@ static int openDatabase(
sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT);
opendb_out:
- sqlite3_free(zOpen);
if( db ){
- assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 );
+ assert( db->mutex!=0 || isThreadsafe==0
+ || sqlite3GlobalConfig.bFullMutex==0 );
sqlite3_mutex_leave(db->mutex);
}
rc = sqlite3_errcode(db);
+ assert( db!=0 || rc==SQLITE_NOMEM );
if( rc==SQLITE_NOMEM ){
sqlite3_close(db);
db = 0;
@@ -112433,7 +140883,30 @@ opendb_out:
db->magic = SQLITE_MAGIC_SICK;
}
*ppDb = db;
- return sqlite3ApiExit(0, rc);
+#ifdef SQLITE_ENABLE_SQLLOG
+ if( sqlite3GlobalConfig.xSqllog ){
+ /* Opening a db handle. Fourth parameter is passed 0. */
+ void *pArg = sqlite3GlobalConfig.pSqllogArg;
+ sqlite3GlobalConfig.xSqllog(pArg, db, zFilename, 0);
+ }
+#endif
+#if defined(SQLITE_HAS_CODEC)
+ if( rc==SQLITE_OK ){
+ const char *zHexKey = sqlite3_uri_parameter(zOpen, "hexkey");
+ if( zHexKey && zHexKey[0] ){
+ u8 iByte;
+ int i;
+ char zKey[40];
+ for(i=0, iByte=0; i<sizeof(zKey)*2 && sqlite3Isxdigit(zHexKey[i]); i++){
+ iByte = (iByte<<4) + sqlite3HexToInt(zHexKey[i]);
+ if( (i&1)!=0 ) zKey[i/2] = iByte;
+ }
+ sqlite3_key_v2(db, 0, zKey, i/2);
+ }
+ }
+#endif
+ sqlite3_free(zOpen);
+ return rc & 0xff;
}
/*
@@ -112467,13 +140940,15 @@ SQLITE_API int sqlite3_open16(
sqlite3_value *pVal;
int rc;
- assert( zFilename );
- assert( ppDb );
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
+#endif
*ppDb = 0;
#ifndef SQLITE_OMIT_AUTOINIT
rc = sqlite3_initialize();
if( rc ) return rc;
#endif
+ if( zFilename==0 ) zFilename = "\000\000";
pVal = sqlite3ValueNew(0);
sqlite3ValueSetStr(pVal, -1, zFilename, SQLITE_UTF16NATIVE, SQLITE_STATIC);
zFilename8 = sqlite3ValueText(pVal, SQLITE_UTF8);
@@ -112482,14 +140957,14 @@ SQLITE_API int sqlite3_open16(
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
assert( *ppDb || rc==SQLITE_NOMEM );
if( rc==SQLITE_OK && !DbHasProperty(*ppDb, 0, DB_SchemaLoaded) ){
- ENC(*ppDb) = SQLITE_UTF16NATIVE;
+ SCHEMA_ENC(*ppDb) = ENC(*ppDb) = SQLITE_UTF16NATIVE;
}
}else{
- rc = SQLITE_NOMEM;
+ rc = SQLITE_NOMEM_BKPT;
}
sqlite3ValueFree(pVal);
- return sqlite3ApiExit(0, rc);
+ return rc & 0xff;
}
#endif /* SQLITE_OMIT_UTF16 */
@@ -112503,13 +140978,7 @@ SQLITE_API int sqlite3_create_collation(
void* pCtx,
int(*xCompare)(void*,int,const void*,int,const void*)
){
- int rc;
- sqlite3_mutex_enter(db->mutex);
- assert( !db->mallocFailed );
- rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0);
- rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(db->mutex);
- return rc;
+ return sqlite3_create_collation_v2(db, zName, enc, pCtx, xCompare, 0);
}
/*
@@ -112524,9 +140993,13 @@ SQLITE_API int sqlite3_create_collation_v2(
void(*xDel)(void*)
){
int rc;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed );
- rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, xDel);
+ rc = createCollation(db, zName, (u8)enc, pCtx, xCompare, xDel);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
@@ -112545,11 +141018,15 @@ SQLITE_API int sqlite3_create_collation16(
){
int rc = SQLITE_OK;
char *zName8;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zName==0 ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
assert( !db->mallocFailed );
zName8 = sqlite3Utf16to8(db, zName, -1, SQLITE_UTF16NATIVE);
if( zName8 ){
- rc = createCollation(db, zName8, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0);
+ rc = createCollation(db, zName8, (u8)enc, pCtx, xCompare, 0);
sqlite3DbFree(db, zName8);
}
rc = sqlite3ApiExit(db, rc);
@@ -112567,6 +141044,9 @@ SQLITE_API int sqlite3_collation_needed(
void *pCollNeededArg,
void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
db->xCollNeeded = xCollNeeded;
db->xCollNeeded16 = 0;
@@ -112585,6 +141065,9 @@ SQLITE_API int sqlite3_collation_needed16(
void *pCollNeededArg,
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*)
){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
db->xCollNeeded = 0;
db->xCollNeeded16 = xCollNeeded16;
@@ -112609,17 +141092,21 @@ SQLITE_API int sqlite3_global_recover(void){
** mode. Return TRUE if it is and FALSE if not. Autocommit mode is on
** by default. Autocommit is disabled by a BEGIN statement and reenabled
** by the next COMMIT or ROLLBACK.
-**
-******* THIS IS AN EXPERIMENTAL API AND IS SUBJECT TO CHANGE ******
*/
SQLITE_API int sqlite3_get_autocommit(sqlite3 *db){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
return db->autoCommit;
}
/*
-** The following routines are subtitutes for constants SQLITE_CORRUPT,
-** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_IOERR and possibly other error
-** constants. They server two purposes:
+** The following routines are substitutes for constants SQLITE_CORRUPT,
+** SQLITE_MISUSE, SQLITE_CANTOPEN, SQLITE_NOMEM and possibly other error
+** constants. They serve two purposes:
**
** 1. Serve as a convenient place to set a breakpoint in a debugger
** to detect when version error conditions occurs.
@@ -112627,28 +141114,33 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3 *db){
** 2. Invoke sqlite3_log() to provide the source code location where
** a low-level error is first detected.
*/
+static int reportError(int iErr, int lineno, const char *zType){
+ sqlite3_log(iErr, "%s at line %d of [%.10s]",
+ zType, lineno, 20+sqlite3_sourceid());
+ return iErr;
+}
SQLITE_PRIVATE int sqlite3CorruptError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(SQLITE_CORRUPT,
- "database corruption at line %d of [%.10s]",
- lineno, 20+sqlite3_sourceid());
- return SQLITE_CORRUPT;
+ return reportError(SQLITE_CORRUPT, lineno, "database corruption");
}
SQLITE_PRIVATE int sqlite3MisuseError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(SQLITE_MISUSE,
- "misuse at line %d of [%.10s]",
- lineno, 20+sqlite3_sourceid());
- return SQLITE_MISUSE;
+ return reportError(SQLITE_MISUSE, lineno, "misuse");
}
SQLITE_PRIVATE int sqlite3CantopenError(int lineno){
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(SQLITE_CANTOPEN,
- "cannot open file at line %d of [%.10s]",
- lineno, 20+sqlite3_sourceid());
- return SQLITE_CANTOPEN;
+ return reportError(SQLITE_CANTOPEN, lineno, "cannot open file");
}
-
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3NomemError(int lineno){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ return reportError(SQLITE_NOMEM, lineno, "OOM");
+}
+SQLITE_PRIVATE int sqlite3IoerrnomemError(int lineno){
+ testcase( sqlite3GlobalConfig.xLog!=0 );
+ return reportError(SQLITE_IOERR_NOMEM, lineno, "I/O OOM error");
+}
+#endif
#ifndef SQLITE_OMIT_DEPRECATED
/*
@@ -112666,7 +141158,6 @@ SQLITE_API void sqlite3_thread_cleanup(void){
** Return meta information about a specific column of a database table.
** See comment in sqlite3.h (sqlite.h.in) for details.
*/
-#ifdef SQLITE_ENABLE_COLUMN_METADATA
SQLITE_API int sqlite3_table_column_metadata(
sqlite3 *db, /* Connection handle */
const char *zDbName, /* Database name or NULL */
@@ -112682,14 +141173,20 @@ SQLITE_API int sqlite3_table_column_metadata(
char *zErrMsg = 0;
Table *pTab = 0;
Column *pCol = 0;
- int iCol;
-
+ int iCol = 0;
char const *zDataType = 0;
char const *zCollSeq = 0;
int notnull = 0;
int primarykey = 0;
int autoinc = 0;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) || zTableName==0 ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+
/* Ensure the database schema has been loaded */
sqlite3_mutex_enter(db->mutex);
sqlite3BtreeEnterAll(db);
@@ -112706,11 +141203,8 @@ SQLITE_API int sqlite3_table_column_metadata(
}
/* Find the column for which info is requested */
- if( sqlite3IsRowid(zColumnName) ){
- iCol = pTab->iPKey;
- if( iCol>=0 ){
- pCol = &pTab->aCol[iCol];
- }
+ if( zColumnName==0 ){
+ /* Query for existance of table only */
}else{
for(iCol=0; iCol<pTab->nCol; iCol++){
pCol = &pTab->aCol[iCol];
@@ -112719,8 +141213,13 @@ SQLITE_API int sqlite3_table_column_metadata(
}
}
if( iCol==pTab->nCol ){
- pTab = 0;
- goto error_out;
+ if( HasRowid(pTab) && sqlite3IsRowid(zColumnName) ){
+ iCol = pTab->iPKey;
+ pCol = iCol>=0 ? &pTab->aCol[iCol] : 0;
+ }else{
+ pTab = 0;
+ goto error_out;
+ }
}
}
@@ -112735,17 +141234,17 @@ SQLITE_API int sqlite3_table_column_metadata(
** explicitly declared column. Copy meta information from *pCol.
*/
if( pCol ){
- zDataType = pCol->zType;
+ zDataType = sqlite3ColumnType(pCol,0);
zCollSeq = pCol->zColl;
notnull = pCol->notNull!=0;
- primarykey = pCol->isPrimKey!=0;
+ primarykey = (pCol->colFlags & COLFLAG_PRIMKEY)!=0;
autoinc = pTab->iPKey==iCol && (pTab->tabFlags & TF_Autoincrement)!=0;
}else{
zDataType = "INTEGER";
primarykey = 1;
}
if( !zCollSeq ){
- zCollSeq = "BINARY";
+ zCollSeq = sqlite3StrBINARY;
}
error_out:
@@ -112767,13 +141266,12 @@ error_out:
zColumnName);
rc = SQLITE_ERROR;
}
- sqlite3Error(db, rc, (zErrMsg?"%s":0), zErrMsg);
+ sqlite3ErrorWithMsg(db, rc, (zErrMsg?"%s":0), zErrMsg);
sqlite3DbFree(db, zErrMsg);
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
-#endif
/*
** Sleep for a little while. Return the amount of time slept.
@@ -112795,6 +141293,9 @@ SQLITE_API int sqlite3_sleep(int ms){
** Enable or disable the extended result codes.
*/
SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
db->errMask = onoff ? 0xffffffff : 0xff;
sqlite3_mutex_leave(db->mutex);
@@ -112806,38 +141307,39 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3 *db, int onoff){
*/
SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, void *pArg){
int rc = SQLITE_ERROR;
- int iDb;
+ Btree *pBtree;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
sqlite3_mutex_enter(db->mutex);
- if( zDbName==0 ){
- iDb = 0;
- }else{
- for(iDb=0; iDb<db->nDb; iDb++){
- if( strcmp(db->aDb[iDb].zName, zDbName)==0 ) break;
- }
- }
- if( iDb<db->nDb ){
- Btree *pBtree = db->aDb[iDb].pBt;
- if( pBtree ){
- Pager *pPager;
- sqlite3_file *fd;
- sqlite3BtreeEnter(pBtree);
- pPager = sqlite3BtreePager(pBtree);
- assert( pPager!=0 );
- fd = sqlite3PagerFile(pPager);
- assert( fd!=0 );
- if( op==SQLITE_FCNTL_FILE_POINTER ){
- *(sqlite3_file**)pArg = fd;
- rc = SQLITE_OK;
- }else if( fd->pMethods ){
- rc = sqlite3OsFileControl(fd, op, pArg);
- }else{
- rc = SQLITE_NOTFOUND;
- }
- sqlite3BtreeLeave(pBtree);
+ pBtree = sqlite3DbNameToBtree(db, zDbName);
+ if( pBtree ){
+ Pager *pPager;
+ sqlite3_file *fd;
+ sqlite3BtreeEnter(pBtree);
+ pPager = sqlite3BtreePager(pBtree);
+ assert( pPager!=0 );
+ fd = sqlite3PagerFile(pPager);
+ assert( fd!=0 );
+ if( op==SQLITE_FCNTL_FILE_POINTER ){
+ *(sqlite3_file**)pArg = fd;
+ rc = SQLITE_OK;
+ }else if( op==SQLITE_FCNTL_VFS_POINTER ){
+ *(sqlite3_vfs**)pArg = sqlite3PagerVfs(pPager);
+ rc = SQLITE_OK;
+ }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
+ *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
+ rc = SQLITE_OK;
+ }else if( fd->pMethods ){
+ rc = sqlite3OsFileControl(fd, op, pArg);
+ }else{
+ rc = SQLITE_NOTFOUND;
}
+ sqlite3BtreeLeave(pBtree);
}
sqlite3_mutex_leave(db->mutex);
- return rc;
+ return rc;
}
/*
@@ -112845,7 +141347,9 @@ SQLITE_API int sqlite3_file_control(sqlite3 *db, const char *zDbName, int op, vo
*/
SQLITE_API int sqlite3_test_control(int op, ...){
int rc = 0;
-#ifndef SQLITE_OMIT_BUILTIN_TEST
+#ifdef SQLITE_OMIT_BUILTIN_TEST
+ UNUSED_PARAMETER(op);
+#else
va_list ap;
va_start(ap, op);
switch( op ){
@@ -112874,7 +141378,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** to the xRandomness method of the default VFS.
*/
case SQLITE_TESTCTRL_PRNG_RESET: {
- sqlite3PrngResetState();
+ sqlite3_randomness(0,0);
break;
}
@@ -112894,6 +141398,28 @@ SQLITE_API int sqlite3_test_control(int op, ...){
}
/*
+ ** sqlite3_test_control(FAULT_INSTALL, xCallback)
+ **
+ ** Arrange to invoke xCallback() whenever sqlite3FaultSim() is called,
+ ** if xCallback is not NULL.
+ **
+ ** As a test of the fault simulator mechanism itself, sqlite3FaultSim(0)
+ ** is called immediately after installing the new callback and the return
+ ** value from sqlite3FaultSim(0) becomes the return from
+ ** sqlite3_test_control().
+ */
+ case SQLITE_TESTCTRL_FAULT_INSTALL: {
+ /* MSVC is picky about pulling func ptrs from va lists.
+ ** http://support.microsoft.com/kb/47961
+ ** sqlite3GlobalConfig.xTestCallback = va_arg(ap, int(*)(int));
+ */
+ typedef int(*TESTCALLBACKFUNC_t)(int);
+ sqlite3GlobalConfig.xTestCallback = va_arg(ap, TESTCALLBACKFUNC_t);
+ rc = sqlite3FaultSim(0);
+ break;
+ }
+
+ /*
** sqlite3_test_control(BENIGN_MALLOC_HOOKS, xBegin, xEnd)
**
** Register hooks to call to indicate which malloc() failures
@@ -112919,7 +141445,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** IMPORTANT: Changing the PENDING byte from 0x40000000 results in
** an incompatible database file format. Changing the PENDING byte
** while any database connection is open results in undefined and
- ** dileterious behavior.
+ ** deleterious behavior.
*/
case SQLITE_TESTCTRL_PENDING_BYTE: {
rc = PENDING_BYTE;
@@ -112945,7 +141471,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_ASSERT: {
volatile int x = 0;
- assert( (x = va_arg(ap,int))!=0 );
+ assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
rc = x;
break;
}
@@ -112984,6 +141510,22 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
+ /*
+ ** sqlite3_test_control(SQLITE_TESTCTRL_BYTEORDER);
+ **
+ ** The integer returned reveals the byte-order of the computer on which
+ ** SQLite is running:
+ **
+ ** 1 big-endian, determined at run-time
+ ** 10 little-endian, determined at run-time
+ ** 432101 big-endian, determined at compile-time
+ ** 123410 little-endian, determined at compile-time
+ */
+ case SQLITE_TESTCTRL_BYTEORDER: {
+ rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN;
+ break;
+ }
+
/* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
**
** Set the nReserve size to N for the main database on the database
@@ -113009,8 +141551,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){
*/
case SQLITE_TESTCTRL_OPTIMIZATIONS: {
sqlite3 *db = va_arg(ap, sqlite3*);
- int x = va_arg(ap,int);
- db->flags = (x & SQLITE_OptMask) | (db->flags & ~SQLITE_OptMask);
+ db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
break;
}
@@ -113032,15 +141573,6 @@ SQLITE_API int sqlite3_test_control(int op, ...){
}
#endif
- /* sqlite3_test_control(SQLITE_TESTCTRL_PGHDRSZ)
- **
- ** Return the size of a pcache header in bytes.
- */
- case SQLITE_TESTCTRL_PGHDRSZ: {
- rc = sizeof(PgHdr);
- break;
- }
-
/* sqlite3_test_control(SQLITE_TESTCTRL_SCRATCHMALLOC, sz, &pNew, pFree);
**
** Pass pFree into sqlite3ScratchFree().
@@ -113068,6 +141600,88 @@ SQLITE_API int sqlite3_test_control(int op, ...){
break;
}
+ /* sqlite3_test_control(SQLITE_TESTCTRL_NEVER_CORRUPT, int);
+ **
+ ** Set or clear a flag that indicates that the database file is always well-
+ ** formed and never corrupt. This flag is clear by default, indicating that
+ ** database files might have arbitrary corruption. Setting the flag during
+ ** testing causes certain assert() statements in the code to be activated
+ ** that demonstrat invariants on well-formed database files.
+ */
+ case SQLITE_TESTCTRL_NEVER_CORRUPT: {
+ sqlite3GlobalConfig.neverCorrupt = va_arg(ap, int);
+ break;
+ }
+
+ /* Set the threshold at which OP_Once counters reset back to zero.
+ ** By default this is 0x7ffffffe (over 2 billion), but that value is
+ ** too big to test in a reasonable amount of time, so this control is
+ ** provided to set a small and easily reachable reset value.
+ */
+ case SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD: {
+ sqlite3GlobalConfig.iOnceResetThreshold = va_arg(ap, int);
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_VDBE_COVERAGE, xCallback, ptr);
+ **
+ ** Set the VDBE coverage callback function to xCallback with context
+ ** pointer ptr.
+ */
+ case SQLITE_TESTCTRL_VDBE_COVERAGE: {
+#ifdef SQLITE_VDBE_COVERAGE
+ typedef void (*branch_callback)(void*,int,u8,u8);
+ sqlite3GlobalConfig.xVdbeBranch = va_arg(ap,branch_callback);
+ sqlite3GlobalConfig.pVdbeBranchArg = va_arg(ap,void*);
+#endif
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_SORTER_MMAP, db, nMax); */
+ case SQLITE_TESTCTRL_SORTER_MMAP: {
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ db->nMaxSorterMmap = va_arg(ap, int);
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_ISINIT);
+ **
+ ** Return SQLITE_OK if SQLite has been initialized and SQLITE_ERROR if
+ ** not.
+ */
+ case SQLITE_TESTCTRL_ISINIT: {
+ if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
+ break;
+ }
+
+ /* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, onOff, tnum);
+ **
+ ** This test control is used to create imposter tables. "db" is a pointer
+ ** to the database connection. dbName is the database name (ex: "main" or
+ ** "temp") which will receive the imposter. "onOff" turns imposter mode on
+ ** or off. "tnum" is the root page of the b-tree to which the imposter
+ ** table should connect.
+ **
+ ** Enable imposter mode only when the schema has already been parsed. Then
+ ** run a single CREATE TABLE statement to construct the imposter table in
+ ** the parsed schema. Then turn imposter mode back off again.
+ **
+ ** If onOff==0 and tnum>0 then reset the schema for all databases, causing
+ ** the schema to be reparsed the next time it is needed. This has the
+ ** effect of erasing all imposter tables.
+ */
+ case SQLITE_TESTCTRL_IMPOSTER: {
+ sqlite3 *db = va_arg(ap, sqlite3*);
+ sqlite3_mutex_enter(db->mutex);
+ db->init.iDb = sqlite3FindDbName(db, va_arg(ap,const char*));
+ db->init.busy = db->init.imposterTable = va_arg(ap,int);
+ db->init.newTnum = va_arg(ap,int);
+ if( db->init.busy==0 && db->init.newTnum>0 ){
+ sqlite3ResetAllSchemasOfConnection(db);
+ }
+ sqlite3_mutex_leave(db->mutex);
+ break;
+ }
}
va_end(ap);
#endif /* SQLITE_OMIT_BUILTIN_TEST */
@@ -113086,6 +141700,7 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** returns a NULL pointer.
*/
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam){
+ if( zFilename==0 || zParam==0 ) return 0;
zFilename += sqlite3Strlen30(zFilename) + 1;
while( zFilename[0] ){
int x = strcmp(zFilename, zParam);
@@ -113096,6 +141711,160 @@ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *
return 0;
}
+/*
+** Return a boolean value for a query parameter.
+*/
+SQLITE_API int sqlite3_uri_boolean(const char *zFilename, const char *zParam, int bDflt){
+ const char *z = sqlite3_uri_parameter(zFilename, zParam);
+ bDflt = bDflt!=0;
+ return z ? sqlite3GetBoolean(z, bDflt) : bDflt;
+}
+
+/*
+** Return a 64-bit integer value for a query parameter.
+*/
+SQLITE_API sqlite3_int64 sqlite3_uri_int64(
+ const char *zFilename, /* Filename as passed to xOpen */
+ const char *zParam, /* URI parameter sought */
+ sqlite3_int64 bDflt /* return if parameter is missing */
+){
+ const char *z = sqlite3_uri_parameter(zFilename, zParam);
+ sqlite3_int64 v;
+ if( z && sqlite3DecOrHexToI64(z, &v)==SQLITE_OK ){
+ bDflt = v;
+ }
+ return bDflt;
+}
+
+/*
+** Return the Btree pointer identified by zDbName. Return NULL if not found.
+*/
+SQLITE_PRIVATE Btree *sqlite3DbNameToBtree(sqlite3 *db, const char *zDbName){
+ int i;
+ for(i=0; i<db->nDb; i++){
+ if( db->aDb[i].pBt
+ && (zDbName==0 || sqlite3StrICmp(zDbName, db->aDb[i].zDbSName)==0)
+ ){
+ return db->aDb[i].pBt;
+ }
+ }
+ return 0;
+}
+
+/*
+** Return the filename of the database associated with a database
+** connection.
+*/
+SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName){
+ Btree *pBt;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return 0;
+ }
+#endif
+ pBt = sqlite3DbNameToBtree(db, zDbName);
+ return pBt ? sqlite3BtreeGetFilename(pBt) : 0;
+}
+
+/*
+** Return 1 if database is read-only or 0 if read/write. Return -1 if
+** no such database exists.
+*/
+SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName){
+ Btree *pBt;
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ (void)SQLITE_MISUSE_BKPT;
+ return -1;
+ }
+#endif
+ pBt = sqlite3DbNameToBtree(db, zDbName);
+ return pBt ? sqlite3BtreeIsReadonly(pBt) : -1;
+}
+
+#ifdef SQLITE_ENABLE_SNAPSHOT
+/*
+** Obtain a snapshot handle for the snapshot of database zDb currently
+** being read by handle db.
+*/
+SQLITE_API int sqlite3_snapshot_get(
+ sqlite3 *db,
+ const char *zDb,
+ sqlite3_snapshot **ppSnapshot
+){
+ int rc = SQLITE_ERROR;
+#ifndef SQLITE_OMIT_WAL
+ int iDb;
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+
+ iDb = sqlite3FindDbName(db, zDb);
+ if( iDb==0 || iDb>1 ){
+ Btree *pBt = db->aDb[iDb].pBt;
+ if( 0==sqlite3BtreeIsInTrans(pBt) ){
+ rc = sqlite3BtreeBeginTrans(pBt, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
+ }
+ }
+ }
+
+ sqlite3_mutex_leave(db->mutex);
+#endif /* SQLITE_OMIT_WAL */
+ return rc;
+}
+
+/*
+** Open a read-transaction on the snapshot idendified by pSnapshot.
+*/
+SQLITE_API int sqlite3_snapshot_open(
+ sqlite3 *db,
+ const char *zDb,
+ sqlite3_snapshot *pSnapshot
+){
+ int rc = SQLITE_ERROR;
+#ifndef SQLITE_OMIT_WAL
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+ sqlite3_mutex_enter(db->mutex);
+ if( db->autoCommit==0 ){
+ int iDb;
+ iDb = sqlite3FindDbName(db, zDb);
+ if( iDb==0 || iDb>1 ){
+ Btree *pBt = db->aDb[iDb].pBt;
+ if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
+ rc = sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), pSnapshot);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3BtreeBeginTrans(pBt, 0);
+ sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0);
+ }
+ }
+ }
+ }
+
+ sqlite3_mutex_leave(db->mutex);
+#endif /* SQLITE_OMIT_WAL */
+ return rc;
+}
+
+/*
+** Free a snapshot handle obtained from sqlite3_snapshot_get().
+*/
+SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){
+ sqlite3_free(pSnapshot);
+}
+#endif /* SQLITE_ENABLE_SNAPSHOT */
+
/************** End of main.c ************************************************/
/************** Begin file notify.c ******************************************/
/*
@@ -113113,6 +141882,8 @@ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *
** This file contains the implementation of the sqlite3_unlock_notify()
** API method and its associated functionality.
*/
+/* #include "sqliteInt.h" */
+/* #include "btreeInt.h" */
/* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
@@ -113282,7 +142053,7 @@ SQLITE_API int sqlite3_unlock_notify(
leaveMutex();
assert( !db->mallocFailed );
- sqlite3Error(db, rc, (rc?"database is deadlocked":0));
+ sqlite3ErrorWithMsg(db, rc, (rc?"database is deadlocked":0));
sqlite3_mutex_leave(db->mutex);
return rc;
}
@@ -113503,7 +142274,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
** A doclist is stored like this:
**
** array {
-** varint docid;
+** varint docid; (delta from previous doclist)
** array { (position list for column 0)
** varint position; (2 more than the delta from previous position)
** }
@@ -113534,8 +142305,8 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
** at D signals the start of a new column; the 1 at E indicates that the
** new column is column number 1. There are two positions at 12 and 45
** (14-2 and 35-2+12). The 0 at H indicate the end-of-document. The
-** 234 at I is the next docid. It has one position 72 (72-2) and then
-** terminates with the 0 at K.
+** 234 at I is the delta to next docid (357). It has one position 70
+** (72-2) and then terminates with the 0 at K.
**
** A "position-list" is the list of positions for multiple columns for
** a single docid. A "column-list" is the set of positions for a single
@@ -113719,10 +142490,6 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
** will eventually overtake the earlier data and knock it out. The
** query logic likewise merges doclists so that newer data knocks out
** older data.
-**
-** TODO(shess) Provide a VACUUM type operation to clear out all
-** deletions and duplications. This would basically be a forced merge
-** into a single segment.
*/
/************** Include fts3Int.h in the middle of fts3.c ********************/
@@ -113747,6 +142514,12 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
# define NDEBUG 1
#endif
+/* FTS3/FTS4 require virtual tables */
+#ifdef SQLITE_OMIT_VIRTUALTABLE
+# undef SQLITE_ENABLE_FTS3
+# undef SQLITE_ENABLE_FTS4
+#endif
+
/*
** FTS4 is really an extension for FTS3. It is enabled using the
** SQLITE_ENABLE_FTS3 macro. But to avoid confusion we also all
@@ -113760,9 +142533,11 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
/* If not building as part of the core, include sqlite3ext.h. */
#ifndef SQLITE_CORE
-SQLITE_API extern const sqlite3_api_routines *sqlite3_api;
+/* # include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT3
#endif
+/* #include "sqlite3.h" */
/************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/
/************** Begin file fts3_tokenizer.h **********************************/
/*
@@ -113791,6 +142566,7 @@ SQLITE_API extern const sqlite3_api_routines *sqlite3_api;
** If tokenizers are to be allowed to call sqlite3_*() functions, then
** we will need a way to register the API consistently.
*/
+/* #include "sqlite3.h" */
/*
** Structures used by the tokenizer interface. When a new tokenizer
@@ -113818,7 +142594,7 @@ typedef struct sqlite3_tokenizer_cursor sqlite3_tokenizer_cursor;
struct sqlite3_tokenizer_module {
/*
- ** Structure version. Should always be set to 0.
+ ** Structure version. Should always be set to 0 or 1.
*/
int iVersion;
@@ -113836,7 +142612,7 @@ struct sqlite3_tokenizer_module {
** This method should return either SQLITE_OK (0), or an SQLite error
** code. If SQLITE_OK is returned, then *ppTokenizer should be set
** to point at the newly created tokenizer structure. The generic
- ** sqlite3_tokenizer.pModule variable should not be initialised by
+ ** sqlite3_tokenizer.pModule variable should not be initialized by
** this callback. The caller will do so.
*/
int (*xCreate)(
@@ -113899,6 +142675,15 @@ struct sqlite3_tokenizer_module {
int *piEndOffset, /* OUT: Byte offset of end of token in input buffer */
int *piPosition /* OUT: Number of tokens returned before this one */
);
+
+ /***********************************************************************
+ ** Methods below this point are only available if iVersion>=1.
+ */
+
+ /*
+ ** Configure the language id of a tokenizer cursor.
+ */
+ int (*xLanguageid)(sqlite3_tokenizer_cursor *pCsr, int iLangid);
};
struct sqlite3_tokenizer {
@@ -113932,7 +142717,7 @@ int fts3_term_cnt(int iTerm, int iCol);
** May you share freely, never taking more than you give.
**
*************************************************************************
-** This is the header file for the generic hash-table implemenation
+** This is the header file for the generic hash-table implementation
** used in SQLite. We've modified it slightly to serve as a standalone
** hash table implementation for the full-text indexing module.
**
@@ -114038,6 +142823,18 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
/************** Continuing where we left off in fts3Int.h ********************/
/*
+** This constant determines the maximum depth of an FTS expression tree
+** that the library will create and use. FTS uses recursion to perform
+** various operations on the query tree, so the disadvantage of a large
+** limit is that it may allow very large queries to use large amounts
+** of stack space (perhaps causing a stack overflow).
+*/
+#ifndef SQLITE_FTS3_MAX_EXPR_DEPTH
+# define SQLITE_FTS3_MAX_EXPR_DEPTH 12
+#endif
+
+
+/*
** This constant controls how often segments are merged. Once there are
** FTS3_MERGE_COUNT segments of level N, they are merged into a single
** segment of level N+1.
@@ -114065,6 +142862,9 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
#ifndef MIN
# define MIN(x,y) ((x)<(y)?(x):(y))
#endif
+#ifndef MAX
+# define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
/*
** Maximum length of a varint encoded integer. The varint format is different
@@ -114117,9 +142917,14 @@ SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const voi
#ifdef SQLITE_COVERAGE_TEST
# define ALWAYS(x) (1)
# define NEVER(X) (0)
+#elif defined(SQLITE_DEBUG)
+# define ALWAYS(x) sqlite3Fts3Always((x)!=0)
+# define NEVER(x) sqlite3Fts3Never((x)!=0)
+SQLITE_PRIVATE int sqlite3Fts3Always(int b);
+SQLITE_PRIVATE int sqlite3Fts3Never(int b);
#else
# define ALWAYS(x) (x)
-# define NEVER(X) (x)
+# define NEVER(x) (x)
#endif
/*
@@ -114129,6 +142934,7 @@ typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */
typedef short int i16; /* 2-byte (or larger) signed integer */
typedef unsigned int u32; /* 4-byte unsigned integer */
typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */
+typedef sqlite3_int64 i64; /* 8-byte signed integer */
/*
** Macro used to suppress compiler warnings for unused parameters.
@@ -114155,6 +142961,13 @@ typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */
#endif /* SQLITE_AMALGAMATION */
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
+# define FTS_CORRUPT_VTAB sqlite3Fts3Corrupt()
+#else
+# define FTS_CORRUPT_VTAB SQLITE_CORRUPT_VTAB
+#endif
+
typedef struct Fts3Table Fts3Table;
typedef struct Fts3Cursor Fts3Cursor;
typedef struct Fts3Expr Fts3Expr;
@@ -114167,6 +142980,8 @@ typedef struct Fts3DeferredToken Fts3DeferredToken;
typedef struct Fts3SegReader Fts3SegReader;
typedef struct Fts3MultiSegReader Fts3MultiSegReader;
+typedef struct MatchinfoBuffer MatchinfoBuffer;
+
/*
** A connection to a fulltext index is an instance of the following
** structure. The xCreate and xConnect methods create an instance
@@ -114181,37 +142996,47 @@ struct Fts3Table {
const char *zName; /* virtual table name */
int nColumn; /* number of named columns in virtual table */
char **azColumn; /* column names. malloced */
+ u8 *abNotindexed; /* True for 'notindexed' columns */
sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */
+ char *zContentTbl; /* content=xxx option, or NULL */
+ char *zLanguageid; /* languageid=xxx option, or NULL */
+ int nAutoincrmerge; /* Value configured by 'automerge' */
+ u32 nLeafAdd; /* Number of leaf blocks added this trans */
/* Precompiled statements used by the implementation. Each of these
** statements is run and reset within a single virtual table API call.
*/
- sqlite3_stmt *aStmt[27];
+ sqlite3_stmt *aStmt[40];
char *zReadExprlist;
char *zWriteExprlist;
int nNodeSize; /* Soft limit for node size */
- u8 bHasStat; /* True if %_stat table exists */
+ u8 bFts4; /* True for FTS4, false for FTS3 */
+ u8 bHasStat; /* True if %_stat table exists (2==unknown) */
u8 bHasDocsize; /* True if %_docsize table exists */
u8 bDescIdx; /* True if doclists are in reverse order */
+ u8 bIgnoreSavepoint; /* True to ignore xSavepoint invocations */
int nPgsz; /* Page size for host database */
char *zSegmentsTbl; /* Name of %_segments table */
sqlite3_blob *pSegments; /* Blob handle open on %_segments table */
- /* TODO: Fix the first paragraph of this comment.
- **
- ** The following hash table is used to buffer pending index updates during
- ** transactions. Variable nPendingData estimates the memory size of the
- ** pending data, including hash table overhead, but not malloc overhead.
- ** When nPendingData exceeds nMaxPendingData, the buffer is flushed
- ** automatically. Variable iPrevDocid is the docid of the most recently
- ** inserted record.
+ /*
+ ** The following array of hash tables is used to buffer pending index
+ ** updates during transactions. All pending updates buffered at any one
+ ** time must share a common language-id (see the FTS4 langid= feature).
+ ** The current language id is stored in variable iPrevLangid.
**
** A single FTS4 table may have multiple full-text indexes. For each index
** there is an entry in the aIndex[] array. Index 0 is an index of all the
** terms that appear in the document set. Each subsequent index in aIndex[]
** is an index of prefixes of a specific length.
+ **
+ ** Variable nPendingData contains an estimate the memory consumed by the
+ ** pending data structures, including hash table overhead, but not including
+ ** malloc overhead. When nPendingData exceeds nMaxPendingData, all hash
+ ** tables are flushed to disk. Variable iPrevDocid is the docid of the most
+ ** recently inserted record.
*/
int nIndex; /* Size of aIndex[] */
struct Fts3Index {
@@ -114221,16 +143046,24 @@ struct Fts3Table {
int nMaxPendingData; /* Max pending data before flush to disk */
int nPendingData; /* Current bytes of pending data */
sqlite_int64 iPrevDocid; /* Docid of most recently inserted document */
+ int iPrevLangid; /* Langid of recently inserted document */
+ int bPrevDelete; /* True if last operation was a delete */
-#if defined(SQLITE_DEBUG)
+#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
/* State variables used for validating that the transaction control
** methods of the virtual table are called at appropriate times. These
- ** values do not contribution to the FTS computation; they are used for
- ** verifying the SQLite core.
+ ** values do not contribute to FTS functionality; they are used for
+ ** verifying the operation of the SQLite core.
*/
int inTransaction; /* True after xBegin but before xCommit/xRollback */
int mxSavepoint; /* Largest valid xSavepoint integer */
#endif
+
+#ifdef SQLITE_TEST
+ /* True to disable the incremental doclist optimization. This is controled
+ ** by special insert command 'test-no-incr-doclist'. */
+ int bNoIncrDoclist;
+#endif
};
/*
@@ -114245,6 +143078,7 @@ struct Fts3Cursor {
u8 isRequireSeek; /* True if must seek pStmt to %_content row */
sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */
Fts3Expr *pExpr; /* Parsed MATCH query string */
+ int iLangid; /* Language being queried for */
int nPhrase; /* Number of matchable phrases in query */
Fts3DeferredToken *pDeferred; /* Deferred search tokens, if any */
sqlite3_int64 iPrevId; /* Previous id read from aDoclist */
@@ -114255,11 +143089,10 @@ struct Fts3Cursor {
int eEvalmode; /* An FTS3_EVAL_XX constant */
int nRowAvg; /* Average size of database rows, in pages */
sqlite3_int64 nDoc; /* Documents in table */
-
+ i64 iMinDocid; /* Minimum docid to return */
+ i64 iMaxDocid; /* Maximum docid to return */
int isMatchinfoNeeded; /* True when aMatchinfo[] needs filling in */
- u32 *aMatchinfo; /* Information about most recent match */
- int nMatchinfo; /* Number of elements in aMatchinfo[] */
- char *zMatchinfo; /* Matchinfo specification */
+ MatchinfoBuffer *pMIBuffer; /* Buffer for matchinfo data */
};
#define FTS3_EVAL_FILTER 0
@@ -114285,6 +143118,15 @@ struct Fts3Cursor {
#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */
#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */
+/*
+** The lower 16-bits of the sqlite3_index_info.idxNum value set by
+** the xBestIndex() method contains the Fts3Cursor.eSearch value described
+** above. The upper 16-bits contain a combination of the following
+** bits, used to describe extra constraints on full-text searches.
+*/
+#define FTS3_HAVE_LANGID 0x00010000 /* languageid=? */
+#define FTS3_HAVE_DOCID_GE 0x00020000 /* docid>=? */
+#define FTS3_HAVE_DOCID_LE 0x00040000 /* docid<=? */
struct Fts3Doclist {
char *aAll; /* Array containing doclist (or NULL) */
@@ -114307,6 +143149,7 @@ struct Fts3PhraseToken {
char *z; /* Text of the token */
int n; /* Number of bytes in buffer z */
int isPrefix; /* True if token ends with a "*" character */
+ int bFirst; /* True if token must appear at position 0 */
/* Variables above this point are populated when the expression is
** parsed (by code in fts3_expr.c). Below this point the variables are
@@ -114321,6 +143164,11 @@ struct Fts3Phrase {
int bIncr; /* True if doclist is loaded incrementally */
int iDoclistToken;
+ /* Used by sqlite3Fts3EvalPhrasePoslist() if this is a descendent of an
+ ** OR condition. */
+ char *pOrPoslist;
+ i64 iOrDocid;
+
/* Variables below this point are populated by fts3_expr.c when parsing
** a MATCH expression. Everything above is part of the evaluation phase.
*/
@@ -114364,7 +143212,9 @@ struct Fts3Expr {
u8 bStart; /* True if iDocid is valid */
u8 bDeferred; /* True if this expression is entirely deferred */
- u32 *aMI;
+ /* The following are used by the fts3_snippet.c module. */
+ int iPhrase; /* Index of this phrase in matchinfo() results */
+ u32 *aMI; /* See above */
};
/*
@@ -114390,23 +143240,33 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sql
SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *);
SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *);
SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *);
-SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(int, sqlite3_int64,
+SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(int, int, sqlite3_int64,
sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**);
SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
Fts3Table*,int,const char*,int,int,Fts3SegReader**);
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *);
-SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, sqlite3_stmt **);
-SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *);
+SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, int, int, int, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char **, int*, int*);
SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(Fts3Table *, sqlite3_stmt **);
SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(Fts3Table *, sqlite3_int64, sqlite3_stmt **);
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredTokens(Fts3Cursor *);
SQLITE_PRIVATE int sqlite3Fts3DeferToken(Fts3Cursor *, Fts3PhraseToken *, int);
SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *);
SQLITE_PRIVATE void sqlite3Fts3FreeDeferredDoclists(Fts3Cursor *);
+SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
+#else
+# define sqlite3Fts3FreeDeferredTokens(x)
+# define sqlite3Fts3DeferToken(x,y,z) SQLITE_OK
+# define sqlite3Fts3CacheDeferredDoclists(x) SQLITE_OK
+# define sqlite3Fts3FreeDeferredDoclists(x)
+# define sqlite3Fts3DeferredTokenList(x,y,z) SQLITE_OK
+#endif
+
SQLITE_PRIVATE void sqlite3Fts3SegmentsClose(Fts3Table *);
+SQLITE_PRIVATE int sqlite3Fts3MaxLevel(Fts3Table *, int *);
/* Special values interpreted by sqlite3SegReaderCursor() */
#define FTS3_SEGCURSOR_PENDING -1
@@ -114416,8 +143276,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStart(Fts3Table*, Fts3MultiSegReader*, Ft
SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(Fts3Table *, Fts3MultiSegReader *);
SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(Fts3MultiSegReader *);
-SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
- Fts3Table *, int, int, const char *, int, int, int, Fts3MultiSegReader *);
+SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(Fts3Table *,
+ int, int, int, const char *, int, int, int, Fts3MultiSegReader *);
/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */
#define FTS3_SEGMENT_REQUIRE_POS 0x00000001
@@ -114425,6 +143285,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004
#define FTS3_SEGMENT_PREFIX 0x00000008
#define FTS3_SEGMENT_SCAN 0x00000010
+#define FTS3_SEGMENT_FIRST 0x00000020
/* Type passed as 4th argument to SegmentReaderIterate() */
struct Fts3SegFilter {
@@ -114457,15 +143318,24 @@ struct Fts3MultiSegReader {
int nDoclist; /* Size of aDoclist[] in bytes */
};
+SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table*,int,int);
+
+#define fts3GetVarint32(p, piVal) ( \
+ (*(u8*)(p)&0x80) ? sqlite3Fts3GetVarint32(p, piVal) : (*piVal=*(u8*)(p), 1) \
+)
+
/* fts3.c */
+SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char**,const char*,...);
SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64);
SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *);
SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *);
SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
-
SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
+SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
+SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
+SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
/* fts3_tokenizer.c */
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
@@ -114481,10 +143351,11 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet(sqlite3_context *, Fts3Cursor *, const ch
const char *, const char *, int, int
);
SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *, const char *);
+SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p);
/* fts3_expr.c */
-SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *,
- char **, int, int, const char *, int, Fts3Expr **
+SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, int,
+ char **, int, int, int, const char *, int, Fts3Expr **, char **
);
SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *);
#ifdef SQLITE_TEST
@@ -114492,6 +143363,10 @@ SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db);
SQLITE_PRIVATE int sqlite3Fts3InitTerm(sqlite3 *db);
#endif
+SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer(sqlite3_tokenizer *, int, const char *, int,
+ sqlite3_tokenizer_cursor **
+);
+
/* fts3_aux.c */
SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db);
@@ -114501,11 +143376,19 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrStart(
Fts3Table*, Fts3MultiSegReader*, int, const char*, int);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
Fts3Table *, Fts3MultiSegReader *, sqlite3_int64 *, char **, int *);
-SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol);
+SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **);
SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
-SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, int *);
+/* fts3_tokenize_vtab.c */
+SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *);
+
+/* fts3_unicode2.c (functions generated by parsing unicode text files) */
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
+SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int);
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int);
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
+#endif
#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */
@@ -114525,7 +143408,9 @@ SQLITE_PRIVATE int sqlite3Fts3DeferredTokenList(Fts3DeferredToken *, char **, in
/* #include <string.h> */
/* #include <stdarg.h> */
+/* #include "fts3.h" */
#ifndef SQLITE_CORE
+/* # include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#endif
@@ -114534,6 +143419,13 @@ static int fts3EvalStart(Fts3Cursor *pCsr);
static int fts3TermSegReaderCursor(
Fts3Cursor *, const char *, int, int, Fts3MultiSegReader **);
+#ifndef SQLITE_AMALGAMATION
+# if defined(SQLITE_DEBUG)
+SQLITE_PRIVATE int sqlite3Fts3Always(int b) { assert( b ); return b; }
+SQLITE_PRIVATE int sqlite3Fts3Never(int b) { assert( !b ); return b; }
+# endif
+#endif
+
/*
** Write a 64-bit variable-length integer to memory starting at p[0].
** The length of data written will be between 1 and FTS3_VARINT_MAX bytes.
@@ -114551,21 +143443,37 @@ SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){
return (int) (q - (unsigned char *)p);
}
+#define GETVARINT_STEP(v, ptr, shift, mask1, mask2, var, ret) \
+ v = (v & mask1) | ( (*ptr++) << shift ); \
+ if( (v & mask2)==0 ){ var = v; return ret; }
+#define GETVARINT_INIT(v, ptr, shift, mask1, mask2, var, ret) \
+ v = (*ptr++); \
+ if( (v & mask2)==0 ){ var = v; return ret; }
+
/*
** Read a 64-bit variable-length integer from memory starting at p[0].
** Return the number of bytes read, or 0 on error.
** The value is stored in *v.
*/
SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){
- const unsigned char *q = (const unsigned char *) p;
- sqlite_uint64 x = 0, y = 1;
- while( (*q&0x80)==0x80 && q-(unsigned char *)p<FTS3_VARINT_MAX ){
- x += y * (*q++ & 0x7f);
- y <<= 7;
- }
- x += y * (*q++);
- *v = (sqlite_int64) x;
- return (int) (q - (unsigned char *)p);
+ const char *pStart = p;
+ u32 a;
+ u64 b;
+ int shift;
+
+ GETVARINT_INIT(a, p, 0, 0x00, 0x80, *v, 1);
+ GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *v, 2);
+ GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *v, 3);
+ GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *v, 4);
+ b = (a & 0x0FFFFFFF );
+
+ for(shift=28; shift<=63; shift+=7){
+ u64 c = *p++;
+ b += (c&0x7F) << shift;
+ if( (c & 0x80)==0 ) break;
+ }
+ *v = b;
+ return (int)(p - pStart);
}
/*
@@ -114573,10 +143481,21 @@ SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){
** 32-bit integer before it is returned.
*/
SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *p, int *pi){
- sqlite_int64 i;
- int ret = sqlite3Fts3GetVarint(p, &i);
- *pi = (int) i;
- return ret;
+ u32 a;
+
+#ifndef fts3GetVarint32
+ GETVARINT_INIT(a, p, 0, 0x00, 0x80, *pi, 1);
+#else
+ a = (*p++);
+ assert( a & 0x80 );
+#endif
+
+ GETVARINT_STEP(a, p, 7, 0x7F, 0x4000, *pi, 2);
+ GETVARINT_STEP(a, p, 14, 0x3FFF, 0x200000, *pi, 3);
+ GETVARINT_STEP(a, p, 21, 0x1FFFFF, 0x10000000, *pi, 4);
+ a = (a & 0x0FFFFFFF );
+ *pi = (int)(a | ((u32)(*p & 0x0F) << 28));
+ return 5;
}
/*
@@ -114616,7 +143535,7 @@ SQLITE_PRIVATE void sqlite3Fts3Dequote(char *z){
/* If the first byte was a '[', then the close-quote character is a ']' */
if( quote=='[' ) quote = ']';
- while( ALWAYS(z[iIn]) ){
+ while( z[iIn] ){
if( z[iIn]==quote ){
if( z[iIn+1]!=quote ) break;
z[iOut++] = quote;
@@ -114655,7 +143574,7 @@ static void fts3GetReverseVarint(
sqlite3_int64 *pVal
){
sqlite3_int64 iVal;
- char *p = *pp;
+ char *p;
/* Pointer p now points at the first byte past the varint we are
** interested in. So, unless the doclist is corrupt, the 0x80 bit is
@@ -114685,6 +143604,8 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){
sqlite3_free(p->zSegmentsTbl);
sqlite3_free(p->zReadExprlist);
sqlite3_free(p->zWriteExprlist);
+ sqlite3_free(p->zContentTbl);
+ sqlite3_free(p->zLanguageid);
/* Invoke the tokenizer destructor to free the tokenizer. */
p->pTokenizer->pModule->xDestroy(p->pTokenizer);
@@ -114694,6 +143615,17 @@ static int fts3DisconnectMethod(sqlite3_vtab *pVtab){
}
/*
+** Write an error message into *pzErr
+*/
+SQLITE_PRIVATE void sqlite3Fts3ErrMsg(char **pzErr, const char *zFormat, ...){
+ va_list ap;
+ sqlite3_free(*pzErr);
+ va_start(ap, zFormat);
+ *pzErr = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+}
+
+/*
** Construct one or more SQL statements from the format string given
** and then evaluate those statements. The success code is written
** into *pRc.
@@ -114724,16 +143656,19 @@ static void fts3DbExec(
** The xDestroy() virtual table method.
*/
static int fts3DestroyMethod(sqlite3_vtab *pVtab){
- int rc = SQLITE_OK; /* Return code */
Fts3Table *p = (Fts3Table *)pVtab;
- sqlite3 *db = p->db;
+ int rc = SQLITE_OK; /* Return code */
+ const char *zDb = p->zDb; /* Name of database (e.g. "main", "temp") */
+ sqlite3 *db = p->db; /* Database handle */
/* Drop the shadow tables */
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", p->zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", p->zDb,p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", p->zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", p->zDb, p->zName);
- fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", p->zDb, p->zName);
+ if( p->zContentTbl==0 ){
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_content'", zDb, p->zName);
+ }
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segments'", zDb,p->zName);
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_segdir'", zDb, p->zName);
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_docsize'", zDb, p->zName);
+ fts3DbExec(&rc, db, "DROP TABLE IF EXISTS %Q.'%q_stat'", zDb, p->zName);
/* If everything has worked, invoke fts3DisconnectMethod() to free the
** memory associated with the Fts3Table structure and return SQLITE_OK.
@@ -114758,7 +143693,9 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
int rc; /* Return code */
char *zSql; /* SQL statement passed to declare_vtab() */
char *zCols; /* List of user defined columns */
+ const char *zLanguageid;
+ zLanguageid = (p->zLanguageid ? p->zLanguageid : "__langid");
sqlite3_vtab_config(p->db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Create a list of user columns for the virtual table */
@@ -114769,7 +143706,8 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
/* Create the whole "CREATE TABLE" statement to pass to SQLite */
zSql = sqlite3_mprintf(
- "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN)", zCols, p->zName
+ "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN, %Q HIDDEN)",
+ zCols, p->zName, zLanguageid
);
if( !zCols || !zSql ){
rc = SQLITE_NOMEM;
@@ -114784,6 +143722,18 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
}
/*
+** Create the %_stat table if it does not already exist.
+*/
+SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int *pRc, Fts3Table *p){
+ fts3DbExec(pRc, p->db,
+ "CREATE TABLE IF NOT EXISTS %Q.'%q_stat'"
+ "(id INTEGER PRIMARY KEY, value BLOB);",
+ p->zDb, p->zName
+ );
+ if( (*pRc)==SQLITE_OK ) p->bHasStat = 1;
+}
+
+/*
** Create the backing store tables (%_content, %_segments and %_segdir)
** required by the FTS3 table passed as the only argument. This is done
** as part of the vtab xCreate() method.
@@ -114795,23 +143745,31 @@ static void fts3DeclareVtab(int *pRc, Fts3Table *p){
static int fts3CreateTables(Fts3Table *p){
int rc = SQLITE_OK; /* Return code */
int i; /* Iterator variable */
- char *zContentCols; /* Columns of %_content table */
sqlite3 *db = p->db; /* The database connection */
- /* Create a list of user columns for the content table */
- zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
- for(i=0; zContentCols && i<p->nColumn; i++){
- char *z = p->azColumn[i];
- zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
+ if( p->zContentTbl==0 ){
+ const char *zLanguageid = p->zLanguageid;
+ char *zContentCols; /* Columns of %_content table */
+
+ /* Create a list of user columns for the content table */
+ zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY");
+ for(i=0; zContentCols && i<p->nColumn; i++){
+ char *z = p->azColumn[i];
+ zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z);
+ }
+ if( zLanguageid && zContentCols ){
+ zContentCols = sqlite3_mprintf("%z, langid", zContentCols, zLanguageid);
+ }
+ if( zContentCols==0 ) rc = SQLITE_NOMEM;
+
+ /* Create the content table */
+ fts3DbExec(&rc, db,
+ "CREATE TABLE %Q.'%q_content'(%s)",
+ p->zDb, p->zName, zContentCols
+ );
+ sqlite3_free(zContentCols);
}
- if( zContentCols==0 ) rc = SQLITE_NOMEM;
- /* Create the content table */
- fts3DbExec(&rc, db,
- "CREATE TABLE %Q.'%q_content'(%s)",
- p->zDb, p->zName, zContentCols
- );
- sqlite3_free(zContentCols);
/* Create other tables */
fts3DbExec(&rc, db,
"CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);",
@@ -114835,11 +143793,9 @@ static int fts3CreateTables(Fts3Table *p){
p->zDb, p->zName
);
}
+ assert( p->bHasStat==p->bFts4 );
if( p->bHasStat ){
- fts3DbExec(&rc, db,
- "CREATE TABLE %Q.'%q_stat'(id INTEGER PRIMARY KEY, value BLOB);",
- p->zDb, p->zName
- );
+ sqlite3Fts3CreateStatTable(&rc, p);
}
return rc;
}
@@ -114921,6 +143877,7 @@ static void fts3Appendf(
char *z;
va_start(ap, zFormat);
z = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
if( z && *pz ){
char *z2 = sqlite3_mprintf("%s%s", *pz, z);
sqlite3_free(z);
@@ -114945,7 +143902,7 @@ static void fts3Appendf(
static char *fts3QuoteId(char const *zInput){
int nRet;
char *zRet;
- nRet = 2 + strlen(zInput)*2 + 1;
+ nRet = 2 + (int)strlen(zInput)*2 + 1;
zRet = sqlite3_malloc(nRet);
if( zRet ){
int i;
@@ -114962,8 +143919,8 @@ static char *fts3QuoteId(char const *zInput){
}
/*
-** Return a list of comma separated SQL expressions that could be used
-** in a SELECT statement such as the following:
+** Return a list of comma separated SQL expressions and a FROM clause that
+** could be used in a SELECT statement such as the following:
**
** SELECT <list of expressions> FROM %_content AS x ...
**
@@ -114974,7 +143931,7 @@ static char *fts3QuoteId(char const *zInput){
** table has the three user-defined columns "a", "b", and "c", the following
** string is returned:
**
-** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c')"
+** "docid, unzip(x.'a'), unzip(x.'b'), unzip(x.'c') FROM %_content AS x"
**
** The pointer returned points to a buffer allocated by sqlite3_malloc(). It
** is the responsibility of the caller to eventually free it.
@@ -114990,16 +143947,34 @@ static char *fts3ReadExprList(Fts3Table *p, const char *zFunc, int *pRc){
char *zFunction;
int i;
- if( !zFunc ){
- zFunction = "";
+ if( p->zContentTbl==0 ){
+ if( !zFunc ){
+ zFunction = "";
+ }else{
+ zFree = zFunction = fts3QuoteId(zFunc);
+ }
+ fts3Appendf(pRc, &zRet, "docid");
+ for(i=0; i<p->nColumn; i++){
+ fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
+ }
+ if( p->zLanguageid ){
+ fts3Appendf(pRc, &zRet, ", x.%Q", "langid");
+ }
+ sqlite3_free(zFree);
}else{
- zFree = zFunction = fts3QuoteId(zFunc);
- }
- fts3Appendf(pRc, &zRet, "docid");
- for(i=0; i<p->nColumn; i++){
- fts3Appendf(pRc, &zRet, ",%s(x.'c%d%q')", zFunction, i, p->azColumn[i]);
+ fts3Appendf(pRc, &zRet, "rowid");
+ for(i=0; i<p->nColumn; i++){
+ fts3Appendf(pRc, &zRet, ", x.'%q'", p->azColumn[i]);
+ }
+ if( p->zLanguageid ){
+ fts3Appendf(pRc, &zRet, ", x.%Q", p->zLanguageid);
+ }
}
- sqlite3_free(zFree);
+ fts3Appendf(pRc, &zRet, " FROM '%q'.'%q%s' AS x",
+ p->zDb,
+ (p->zContentTbl ? p->zContentTbl : p->zName),
+ (p->zContentTbl ? "" : "_content")
+ );
return zRet;
}
@@ -115038,6 +144013,9 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
for(i=0; i<p->nColumn; i++){
fts3Appendf(pRc, &zRet, ",%s(?)", zFunction);
}
+ if( p->zLanguageid ){
+ fts3Appendf(pRc, &zRet, ", ?");
+ }
sqlite3_free(zFree);
return zRet;
}
@@ -115056,11 +144034,16 @@ static char *fts3WriteExprList(Fts3Table *p, const char *zFunc, int *pRc){
** This function is used when parsing the "prefix=" FTS4 parameter.
*/
static int fts3GobbleInt(const char **pp, int *pnOut){
- const char *p = *pp; /* Iterator pointer */
+ const int MAX_NPREFIX = 10000000;
+ const char *p; /* Iterator pointer */
int nInt = 0; /* Output value */
for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
nInt = nInt * 10 + (p[0] - '0');
+ if( nInt>MAX_NPREFIX ){
+ nInt = 0;
+ break;
+ }
}
if( p==*pp ) return SQLITE_ERROR;
*pnOut = nInt;
@@ -115103,7 +144086,6 @@ static int fts3PrefixParameter(
aIndex = sqlite3_malloc(sizeof(struct Fts3Index) * nIndex);
*apIndex = aIndex;
- *pnIndex = nIndex;
if( !aIndex ){
return SQLITE_NOMEM;
}
@@ -115113,17 +144095,113 @@ static int fts3PrefixParameter(
const char *p = zParam;
int i;
for(i=1; i<nIndex; i++){
- int nPrefix;
+ int nPrefix = 0;
if( fts3GobbleInt(&p, &nPrefix) ) return SQLITE_ERROR;
- aIndex[i].nPrefix = nPrefix;
+ assert( nPrefix>=0 );
+ if( nPrefix==0 ){
+ nIndex--;
+ i--;
+ }else{
+ aIndex[i].nPrefix = nPrefix;
+ }
p++;
}
}
+ *pnIndex = nIndex;
return SQLITE_OK;
}
/*
+** This function is called when initializing an FTS4 table that uses the
+** content=xxx option. It determines the number of and names of the columns
+** of the new FTS4 table.
+**
+** The third argument passed to this function is the value passed to the
+** config=xxx option (i.e. "xxx"). This function queries the database for
+** a table of that name. If found, the output variables are populated
+** as follows:
+**
+** *pnCol: Set to the number of columns table xxx has,
+**
+** *pnStr: Set to the total amount of space required to store a copy
+** of each columns name, including the nul-terminator.
+**
+** *pazCol: Set to point to an array of *pnCol strings. Each string is
+** the name of the corresponding column in table xxx. The array
+** and its contents are allocated using a single allocation. It
+** is the responsibility of the caller to free this allocation
+** by eventually passing the *pazCol value to sqlite3_free().
+**
+** If the table cannot be found, an error code is returned and the output
+** variables are undefined. Or, if an OOM is encountered, SQLITE_NOMEM is
+** returned (and the output variables are undefined).
+*/
+static int fts3ContentColumns(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of db (i.e. "main", "temp" etc.) */
+ const char *zTbl, /* Name of content table */
+ const char ***pazCol, /* OUT: Malloc'd array of column names */
+ int *pnCol, /* OUT: Size of array *pazCol */
+ int *pnStr, /* OUT: Bytes of string content */
+ char **pzErr /* OUT: error message */
+){
+ int rc = SQLITE_OK; /* Return code */
+ char *zSql; /* "SELECT *" statement on zTbl */
+ sqlite3_stmt *pStmt = 0; /* Compiled version of zSql */
+
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", zDb, zTbl);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0);
+ if( rc!=SQLITE_OK ){
+ sqlite3Fts3ErrMsg(pzErr, "%s", sqlite3_errmsg(db));
+ }
+ }
+ sqlite3_free(zSql);
+
+ if( rc==SQLITE_OK ){
+ const char **azCol; /* Output array */
+ int nStr = 0; /* Size of all column names (incl. 0x00) */
+ int nCol; /* Number of table columns */
+ int i; /* Used to iterate through columns */
+
+ /* Loop through the returned columns. Set nStr to the number of bytes of
+ ** space required to store a copy of each column name, including the
+ ** nul-terminator byte. */
+ nCol = sqlite3_column_count(pStmt);
+ for(i=0; i<nCol; i++){
+ const char *zCol = sqlite3_column_name(pStmt, i);
+ nStr += (int)strlen(zCol) + 1;
+ }
+
+ /* Allocate and populate the array to return. */
+ azCol = (const char **)sqlite3_malloc(sizeof(char *) * nCol + nStr);
+ if( azCol==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ char *p = (char *)&azCol[nCol];
+ for(i=0; i<nCol; i++){
+ const char *zCol = sqlite3_column_name(pStmt, i);
+ int n = (int)strlen(zCol)+1;
+ memcpy(p, zCol, n);
+ azCol[i] = p;
+ p += n;
+ }
+ }
+ sqlite3_finalize(pStmt);
+
+ /* Set the output variables. */
+ *pnCol = nCol;
+ *pnStr = nStr;
+ *pazCol = azCol;
+ }
+
+ return rc;
+}
+
+/*
** This function is the implementation of both the xConnect and xCreate
** methods of the FTS3 virtual table.
**
@@ -115158,7 +144236,7 @@ static int fts3InitVtab(
const char **aCol; /* Array of column names */
sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */
- int nIndex; /* Size of aIndex[] array */
+ int nIndex = 0; /* Size of aIndex[] array */
struct Fts3Index *aIndex = 0; /* Array of indexes for this table */
/* The results of parsing supported FTS4 key=value options: */
@@ -115167,6 +144245,10 @@ static int fts3InitVtab(
char *zPrefix = 0; /* Prefix parameter value (or NULL) */
char *zCompress = 0; /* compress=? parameter (or NULL) */
char *zUncompress = 0; /* uncompress=? parameter (or NULL) */
+ char *zContent = 0; /* content=? parameter (or NULL) */
+ char *zLanguageid = 0; /* languageid=? parameter (or NULL) */
+ char **azNotindexed = 0; /* The set of notindexed= columns */
+ int nNotindexed = 0; /* Size of azNotindexed[] array */
assert( strlen(argv[0])==4 );
assert( (sqlite3_strnicmp(argv[0], "fts4", 4)==0 && isFts4)
@@ -115176,9 +144258,19 @@ static int fts3InitVtab(
nDb = (int)strlen(argv[1]) + 1;
nName = (int)strlen(argv[2]) + 1;
- aCol = (const char **)sqlite3_malloc(sizeof(const char *) * (argc-2) );
- if( !aCol ) return SQLITE_NOMEM;
- memset((void *)aCol, 0, sizeof(const char *) * (argc-2));
+ nByte = sizeof(const char *) * (argc-2);
+ aCol = (const char **)sqlite3_malloc(nByte);
+ if( aCol ){
+ memset((void*)aCol, 0, nByte);
+ azNotindexed = (char **)sqlite3_malloc(nByte);
+ }
+ if( azNotindexed ){
+ memset(azNotindexed, 0, nByte);
+ }
+ if( !aCol || !azNotindexed ){
+ rc = SQLITE_NOMEM;
+ goto fts3_init_out;
+ }
/* Loop through all of the arguments passed by the user to the FTS3/4
** module (i.e. all the column names and special arguments). This loop
@@ -115210,13 +144302,15 @@ static int fts3InitVtab(
struct Fts4Option {
const char *zOpt;
int nOpt;
- char **pzVar;
} aFts4Opt[] = {
- { "matchinfo", 9, 0 }, /* 0 -> MATCHINFO */
- { "prefix", 6, 0 }, /* 1 -> PREFIX */
- { "compress", 8, 0 }, /* 2 -> COMPRESS */
- { "uncompress", 10, 0 }, /* 3 -> UNCOMPRESS */
- { "order", 5, 0 } /* 4 -> ORDER */
+ { "matchinfo", 9 }, /* 0 -> MATCHINFO */
+ { "prefix", 6 }, /* 1 -> PREFIX */
+ { "compress", 8 }, /* 2 -> COMPRESS */
+ { "uncompress", 10 }, /* 3 -> UNCOMPRESS */
+ { "order", 5 }, /* 4 -> ORDER */
+ { "content", 7 }, /* 5 -> CONTENT */
+ { "languageid", 10 }, /* 6 -> LANGUAGEID */
+ { "notindexed", 10 } /* 7 -> NOTINDEXED */
};
int iOpt;
@@ -115230,13 +144324,13 @@ static int fts3InitVtab(
}
}
if( iOpt==SizeofArray(aFts4Opt) ){
- *pzErr = sqlite3_mprintf("unrecognized parameter: %s", z);
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized parameter: %s", z);
rc = SQLITE_ERROR;
}else{
switch( iOpt ){
case 0: /* MATCHINFO */
if( strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "fts3", 4) ){
- *pzErr = sqlite3_mprintf("unrecognized matchinfo: %s", zVal);
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo: %s", zVal);
rc = SQLITE_ERROR;
}
bNoDocsize = 1;
@@ -115262,13 +144356,31 @@ static int fts3InitVtab(
case 4: /* ORDER */
if( (strlen(zVal)!=3 || sqlite3_strnicmp(zVal, "asc", 3))
- && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 3))
+ && (strlen(zVal)!=4 || sqlite3_strnicmp(zVal, "desc", 4))
){
- *pzErr = sqlite3_mprintf("unrecognized order: %s", zVal);
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized order: %s", zVal);
rc = SQLITE_ERROR;
}
bDescIdx = (zVal[0]=='d' || zVal[0]=='D');
break;
+
+ case 5: /* CONTENT */
+ sqlite3_free(zContent);
+ zContent = zVal;
+ zVal = 0;
+ break;
+
+ case 6: /* LANGUAGEID */
+ assert( iOpt==6 );
+ sqlite3_free(zLanguageid);
+ zLanguageid = zVal;
+ zVal = 0;
+ break;
+
+ case 7: /* NOTINDEXED */
+ azNotindexed[nNotindexed++] = zVal;
+ zVal = 0;
+ break;
}
}
sqlite3_free(zVal);
@@ -115281,6 +144393,39 @@ static int fts3InitVtab(
aCol[nCol++] = z;
}
}
+
+ /* If a content=xxx option was specified, the following:
+ **
+ ** 1. Ignore any compress= and uncompress= options.
+ **
+ ** 2. If no column names were specified as part of the CREATE VIRTUAL
+ ** TABLE statement, use all columns from the content table.
+ */
+ if( rc==SQLITE_OK && zContent ){
+ sqlite3_free(zCompress);
+ sqlite3_free(zUncompress);
+ zCompress = 0;
+ zUncompress = 0;
+ if( nCol==0 ){
+ sqlite3_free((void*)aCol);
+ aCol = 0;
+ rc = fts3ContentColumns(db, argv[1], zContent,&aCol,&nCol,&nString,pzErr);
+
+ /* If a languageid= option was specified, remove the language id
+ ** column from the aCol[] array. */
+ if( rc==SQLITE_OK && zLanguageid ){
+ int j;
+ for(j=0; j<nCol; j++){
+ if( sqlite3_stricmp(zLanguageid, aCol[j])==0 ){
+ int k;
+ for(k=j; k<nCol; k++) aCol[k] = aCol[k+1];
+ nCol--;
+ break;
+ }
+ }
+ }
+ }
+ }
if( rc!=SQLITE_OK ) goto fts3_init_out;
if( nCol==0 ){
@@ -115299,7 +144444,7 @@ static int fts3InitVtab(
rc = fts3PrefixParameter(zPrefix, &nIndex, &aIndex);
if( rc==SQLITE_ERROR ){
assert( zPrefix );
- *pzErr = sqlite3_mprintf("error parsing prefix parameter: %s", zPrefix);
+ sqlite3Fts3ErrMsg(pzErr, "error parsing prefix parameter: %s", zPrefix);
}
if( rc!=SQLITE_OK ) goto fts3_init_out;
@@ -115307,6 +144452,7 @@ static int fts3InitVtab(
nByte = sizeof(Fts3Table) + /* Fts3Table */
nCol * sizeof(char *) + /* azColumn */
nIndex * sizeof(struct Fts3Index) + /* aIndex */
+ nCol * sizeof(u8) + /* abNotindexed */
nName + /* zName */
nDb + /* zDb */
nString; /* Space for azColumn strings */
@@ -115324,7 +144470,13 @@ static int fts3InitVtab(
p->nMaxPendingData = FTS3_MAX_PENDING_DATA;
p->bHasDocsize = (isFts4 && bNoDocsize==0);
p->bHasStat = isFts4;
+ p->bFts4 = isFts4;
p->bDescIdx = bDescIdx;
+ p->nAutoincrmerge = 0xff; /* 0xff means setting unknown */
+ p->zContentTbl = zContent;
+ p->zLanguageid = zLanguageid;
+ zContent = 0;
+ zLanguageid = 0;
TESTONLY( p->inTransaction = -1 );
TESTONLY( p->mxSavepoint = -1 );
@@ -115334,9 +144486,10 @@ static int fts3InitVtab(
for(i=0; i<nIndex; i++){
fts3HashInit(&p->aIndex[i].hPending, FTS3_HASH_STRING, 1);
}
+ p->abNotindexed = (u8 *)&p->aIndex[nIndex];
/* Fill in the zName and zDb fields of the vtab structure. */
- zCsr = (char *)&p->aIndex[nIndex];
+ zCsr = (char *)&p->abNotindexed[nCol];
p->zName = zCsr;
memcpy(zCsr, argv[2], nName);
zCsr += nName;
@@ -115357,10 +144510,31 @@ static int fts3InitVtab(
assert( zCsr <= &((char *)p)[nByte] );
}
- if( (zCompress==0)!=(zUncompress==0) ){
+ /* Fill in the abNotindexed array */
+ for(iCol=0; iCol<nCol; iCol++){
+ int n = (int)strlen(p->azColumn[iCol]);
+ for(i=0; i<nNotindexed; i++){
+ char *zNot = azNotindexed[i];
+ if( zNot && n==(int)strlen(zNot)
+ && 0==sqlite3_strnicmp(p->azColumn[iCol], zNot, n)
+ ){
+ p->abNotindexed[iCol] = 1;
+ sqlite3_free(zNot);
+ azNotindexed[i] = 0;
+ }
+ }
+ }
+ for(i=0; i<nNotindexed; i++){
+ if( azNotindexed[i] ){
+ sqlite3Fts3ErrMsg(pzErr, "no such column: %s", azNotindexed[i]);
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ if( rc==SQLITE_OK && (zCompress==0)!=(zUncompress==0) ){
char const *zMiss = (zCompress==0 ? "compress" : "uncompress");
rc = SQLITE_ERROR;
- *pzErr = sqlite3_mprintf("missing %s parameter in fts4 constructor", zMiss);
+ sqlite3Fts3ErrMsg(pzErr, "missing %s parameter in fts4 constructor", zMiss);
}
p->zReadExprlist = fts3ReadExprList(p, zUncompress, &rc);
p->zWriteExprlist = fts3WriteExprList(p, zCompress, &rc);
@@ -115373,6 +144547,13 @@ static int fts3InitVtab(
rc = fts3CreateTables(p);
}
+ /* Check to see if a legacy fts3 table has been "upgraded" by the
+ ** addition of a %_stat table so that it can use incremental merge.
+ */
+ if( !isFts4 && !isCreate ){
+ p->bHasStat = 2;
+ }
+
/* Figure out the page-size for the database. This is required in order to
** estimate the cost of loading large doclists from the database. */
fts3DatabasePageSize(&rc, p);
@@ -115386,7 +144567,11 @@ fts3_init_out:
sqlite3_free(aIndex);
sqlite3_free(zCompress);
sqlite3_free(zUncompress);
+ sqlite3_free(zContent);
+ sqlite3_free(zLanguageid);
+ for(i=0; i<nNotindexed; i++) sqlite3_free(azNotindexed[i]);
sqlite3_free((void *)aCol);
+ sqlite3_free((void *)azNotindexed);
if( rc!=SQLITE_OK ){
if( p ){
fts3DisconnectMethod((sqlite3_vtab *)p);
@@ -115425,6 +144610,32 @@ static int fts3CreateMethod(
return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
}
+/*
+** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support estimatedRows. In that case this function is a no-op.
+*/
+static void fts3SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
+#if SQLITE_VERSION_NUMBER>=3008002
+ if( sqlite3_libversion_number()>=3008002 ){
+ pIdxInfo->estimatedRows = nRow;
+ }
+#endif
+}
+
+/*
+** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support index-info flags. In that case this function is a no-op.
+*/
+static void fts3SetUniqueFlag(sqlite3_index_info *pIdxInfo){
+#if SQLITE_VERSION_NUMBER>=3008012
+ if( sqlite3_libversion_number()>=3008012 ){
+ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
+ }
+#endif
+}
+
/*
** Implementation of the xBestIndex method for FTS3 tables. There
** are three possible strategies, in order of preference:
@@ -115438,20 +144649,39 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
int i; /* Iterator variable */
int iCons = -1; /* Index of constraint to use */
+ int iLangidCons = -1; /* Index of langid=x constraint, if present */
+ int iDocidGe = -1; /* Index of docid>=x constraint, if present */
+ int iDocidLe = -1; /* Index of docid<=x constraint, if present */
+ int iIdx;
+
/* By default use a full table scan. This is an expensive option,
** so search through the constraints to see if a more efficient
** strategy is possible.
*/
pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
- pInfo->estimatedCost = 500000;
+ pInfo->estimatedCost = 5000000;
for(i=0; i<pInfo->nConstraint; i++){
+ int bDocid; /* True if this constraint is on docid */
struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i];
- if( pCons->usable==0 ) continue;
+ if( pCons->usable==0 ){
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ /* There exists an unusable MATCH constraint. This means that if
+ ** the planner does elect to use the results of this call as part
+ ** of the overall query plan the user will see an "unable to use
+ ** function MATCH in the requested context" error. To discourage
+ ** this, return a very high cost here. */
+ pInfo->idxNum = FTS3_FULLSCAN_SEARCH;
+ pInfo->estimatedCost = 1e50;
+ fts3SetEstimatedRows(pInfo, ((sqlite3_int64)1) << 50);
+ return SQLITE_OK;
+ }
+ continue;
+ }
+
+ bDocid = (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1);
/* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */
- if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
- && (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 )
- ){
+ if( iCons<0 && pCons->op==SQLITE_INDEX_CONSTRAINT_EQ && bDocid ){
pInfo->idxNum = FTS3_DOCID_SEARCH;
pInfo->estimatedCost = 1.0;
iCons = i;
@@ -115472,14 +144702,50 @@ static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
pInfo->idxNum = FTS3_FULLTEXT_SEARCH + pCons->iColumn;
pInfo->estimatedCost = 2.0;
iCons = i;
- break;
+ }
+
+ /* Equality constraint on the langid column */
+ if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ
+ && pCons->iColumn==p->nColumn + 2
+ ){
+ iLangidCons = i;
+ }
+
+ if( bDocid ){
+ switch( pCons->op ){
+ case SQLITE_INDEX_CONSTRAINT_GE:
+ case SQLITE_INDEX_CONSTRAINT_GT:
+ iDocidGe = i;
+ break;
+
+ case SQLITE_INDEX_CONSTRAINT_LE:
+ case SQLITE_INDEX_CONSTRAINT_LT:
+ iDocidLe = i;
+ break;
+ }
}
}
+ /* If using a docid=? or rowid=? strategy, set the UNIQUE flag. */
+ if( pInfo->idxNum==FTS3_DOCID_SEARCH ) fts3SetUniqueFlag(pInfo);
+
+ iIdx = 1;
if( iCons>=0 ){
- pInfo->aConstraintUsage[iCons].argvIndex = 1;
+ pInfo->aConstraintUsage[iCons].argvIndex = iIdx++;
pInfo->aConstraintUsage[iCons].omit = 1;
}
+ if( iLangidCons>=0 ){
+ pInfo->idxNum |= FTS3_HAVE_LANGID;
+ pInfo->aConstraintUsage[iLangidCons].argvIndex = iIdx++;
+ }
+ if( iDocidGe>=0 ){
+ pInfo->idxNum |= FTS3_HAVE_DOCID_GE;
+ pInfo->aConstraintUsage[iDocidGe].argvIndex = iIdx++;
+ }
+ if( iDocidLe>=0 ){
+ pInfo->idxNum |= FTS3_HAVE_DOCID_LE;
+ pInfo->aConstraintUsage[iDocidLe].argvIndex = iIdx++;
+ }
/* Regardless of the strategy selected, FTS can deliver rows in rowid (or
** docid) order. Both ascending and descending are possible.
@@ -115531,41 +144797,70 @@ static int fts3CloseMethod(sqlite3_vtab_cursor *pCursor){
sqlite3Fts3ExprFree(pCsr->pExpr);
sqlite3Fts3FreeDeferredTokens(pCsr);
sqlite3_free(pCsr->aDoclist);
- sqlite3_free(pCsr->aMatchinfo);
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
assert( ((Fts3Table *)pCsr->base.pVtab)->pSegments==0 );
sqlite3_free(pCsr);
return SQLITE_OK;
}
/*
+** If pCsr->pStmt has not been prepared (i.e. if pCsr->pStmt==0), then
+** compose and prepare an SQL statement of the form:
+**
+** "SELECT <columns> FROM %_content WHERE rowid = ?"
+**
+** (or the equivalent for a content=xxx table) and set pCsr->pStmt to
+** it. If an error occurs, return an SQLite error code.
+**
+** Otherwise, set *ppStmt to point to pCsr->pStmt and return SQLITE_OK.
+*/
+static int fts3CursorSeekStmt(Fts3Cursor *pCsr, sqlite3_stmt **ppStmt){
+ int rc = SQLITE_OK;
+ if( pCsr->pStmt==0 ){
+ Fts3Table *p = (Fts3Table *)pCsr->base.pVtab;
+ char *zSql;
+ zSql = sqlite3_mprintf("SELECT %s WHERE rowid = ?", p->zReadExprlist);
+ if( !zSql ) return SQLITE_NOMEM;
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
+ sqlite3_free(zSql);
+ }
+ *ppStmt = pCsr->pStmt;
+ return rc;
+}
+
+/*
** Position the pCsr->pStmt statement so that it is on the row
** of the %_content table that contains the last match. Return
** SQLITE_OK on success.
*/
static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){
+ int rc = SQLITE_OK;
if( pCsr->isRequireSeek ){
- sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
- pCsr->isRequireSeek = 0;
- if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
- return SQLITE_OK;
- }else{
- int rc = sqlite3_reset(pCsr->pStmt);
- if( rc==SQLITE_OK ){
- /* If no row was found and no error has occured, then the %_content
- ** table is missing a row that is present in the full-text index.
- ** The data structures are corrupt.
- */
- rc = SQLITE_CORRUPT_VTAB;
- }
- pCsr->isEof = 1;
- if( pContext ){
- sqlite3_result_error_code(pContext, rc);
+ sqlite3_stmt *pStmt = 0;
+
+ rc = fts3CursorSeekStmt(pCsr, &pStmt);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId);
+ pCsr->isRequireSeek = 0;
+ if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){
+ return SQLITE_OK;
+ }else{
+ rc = sqlite3_reset(pCsr->pStmt);
+ if( rc==SQLITE_OK && ((Fts3Table *)pCsr->base.pVtab)->zContentTbl==0 ){
+ /* If no row was found and no error has occurred, then the %_content
+ ** table is missing a row that is present in the full-text index.
+ ** The data structures are corrupt. */
+ rc = FTS_CORRUPT_VTAB;
+ pCsr->isEof = 1;
+ }
}
- return rc;
}
- }else{
- return SQLITE_OK;
}
+
+ if( rc!=SQLITE_OK && pContext ){
+ sqlite3_result_error_code(pContext, rc);
+ }
+ return rc;
}
/*
@@ -115615,7 +144910,7 @@ static int fts3ScanInteriorNode(
zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
zCsr += sqlite3Fts3GetVarint(zCsr, &iChild);
if( zCsr>zEnd ){
- return SQLITE_CORRUPT_VTAB;
+ return FTS_CORRUPT_VTAB;
}
while( zCsr<zEnd && (piFirst || piLast) ){
@@ -115627,13 +144922,13 @@ static int fts3ScanInteriorNode(
/* Load the next term on the node into zBuffer. Use realloc() to expand
** the size of zBuffer if required. */
if( !isFirstTerm ){
- zCsr += sqlite3Fts3GetVarint32(zCsr, &nPrefix);
+ zCsr += fts3GetVarint32(zCsr, &nPrefix);
}
isFirstTerm = 0;
- zCsr += sqlite3Fts3GetVarint32(zCsr, &nSuffix);
+ zCsr += fts3GetVarint32(zCsr, &nSuffix);
if( nPrefix<0 || nSuffix<0 || &zCsr[nSuffix]>zEnd ){
- rc = SQLITE_CORRUPT_VTAB;
+ rc = FTS_CORRUPT_VTAB;
goto finish_scan;
}
if( nPrefix+nSuffix>nAlloc ){
@@ -115646,6 +144941,7 @@ static int fts3ScanInteriorNode(
}
zBuffer = zNew;
}
+ assert( zBuffer );
memcpy(&zBuffer[nPrefix], zCsr, nSuffix);
nBuffer = nPrefix + nSuffix;
zCsr += nSuffix;
@@ -115712,18 +145008,18 @@ static int fts3SelectLeaf(
sqlite3_int64 *piLeaf, /* Selected leaf node */
sqlite3_int64 *piLeaf2 /* Selected leaf node */
){
- int rc; /* Return code */
+ int rc = SQLITE_OK; /* Return code */
int iHeight; /* Height of this node in tree */
assert( piLeaf || piLeaf2 );
- sqlite3Fts3GetVarint32(zNode, &iHeight);
+ fts3GetVarint32(zNode, &iHeight);
rc = fts3ScanInteriorNode(zTerm, nTerm, zNode, nNode, piLeaf, piLeaf2);
assert( !piLeaf2 || !piLeaf || rc!=SQLITE_OK || (*piLeaf<=*piLeaf2) );
if( rc==SQLITE_OK && iHeight>1 ){
char *zBlob = 0; /* Blob read from %_segments table */
- int nBlob; /* Size of zBlob in bytes */
+ int nBlob = 0; /* Size of zBlob in bytes */
if( piLeaf && piLeaf2 && (*piLeaf!=*piLeaf2) ){
rc = sqlite3Fts3ReadBlock(p, *piLeaf, &zBlob, &nBlob, 0);
@@ -115919,11 +145215,11 @@ static void fts3PoslistMerge(
int iCol1; /* The current column index in pp1 */
int iCol2; /* The current column index in pp2 */
- if( *p1==POS_COLUMN ) sqlite3Fts3GetVarint32(&p1[1], &iCol1);
+ if( *p1==POS_COLUMN ) fts3GetVarint32(&p1[1], &iCol1);
else if( *p1==POS_END ) iCol1 = POSITION_LIST_END;
else iCol1 = 0;
- if( *p2==POS_COLUMN ) sqlite3Fts3GetVarint32(&p2[1], &iCol2);
+ if( *p2==POS_COLUMN ) fts3GetVarint32(&p2[1], &iCol2);
else if( *p2==POS_END ) iCol2 = POSITION_LIST_END;
else iCol2 = 0;
@@ -116004,7 +145300,7 @@ static int fts3PoslistPhraseMerge(
char **pp1, /* IN/OUT: Left input list */
char **pp2 /* IN/OUT: Right input list */
){
- char *p = (pp ? *pp : 0);
+ char *p = *pp;
char *p1 = *pp1;
char *p2 = *pp2;
int iCol1 = 0;
@@ -116013,14 +145309,14 @@ static int fts3PoslistPhraseMerge(
/* Never set both isSaveLeft and isExact for the same invocation. */
assert( isSaveLeft==0 || isExact==0 );
- assert( *p1!=0 && *p2!=0 );
+ assert( p!=0 && *p1!=0 && *p2!=0 );
if( *p1==POS_COLUMN ){
p1++;
- p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
+ p1 += fts3GetVarint32(p1, &iCol1);
}
if( *p2==POS_COLUMN ){
p2++;
- p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
+ p2 += fts3GetVarint32(p2, &iCol2);
}
while( 1 ){
@@ -116030,7 +145326,7 @@ static int fts3PoslistPhraseMerge(
sqlite3_int64 iPos1 = 0;
sqlite3_int64 iPos2 = 0;
- if( pp && iCol1 ){
+ if( iCol1 ){
*p++ = POS_COLUMN;
p += sqlite3Fts3PutVarint(p, iCol1);
}
@@ -116045,16 +145341,10 @@ static int fts3PoslistPhraseMerge(
|| (isExact==0 && iPos2>iPos1 && iPos2<=iPos1+nToken)
){
sqlite3_int64 iSave;
- if( !pp ){
- fts3PoslistCopy(0, &p2);
- fts3PoslistCopy(0, &p1);
- *pp1 = p1;
- *pp2 = p2;
- return 1;
- }
iSave = isSaveLeft ? iPos1 : iPos2;
fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2;
pSave = 0;
+ assert( p );
}
if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){
if( (*p2&0xFE)==0 ) break;
@@ -116076,9 +145366,9 @@ static int fts3PoslistPhraseMerge(
if( 0==*p1 || 0==*p2 ) break;
p1++;
- p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
+ p1 += fts3GetVarint32(p1, &iCol1);
p2++;
- p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
+ p2 += fts3GetVarint32(p2, &iCol2);
}
/* Advance pointer p1 or p2 (whichever corresponds to the smaller of
@@ -116090,12 +145380,12 @@ static int fts3PoslistPhraseMerge(
fts3ColumnlistCopy(0, &p1);
if( 0==*p1 ) break;
p1++;
- p1 += sqlite3Fts3GetVarint32(p1, &iCol1);
+ p1 += fts3GetVarint32(p1, &iCol1);
}else{
fts3ColumnlistCopy(0, &p2);
if( 0==*p2 ) break;
p2++;
- p2 += sqlite3Fts3GetVarint32(p2, &iCol2);
+ p2 += fts3GetVarint32(p2, &iCol2);
}
}
@@ -116103,7 +145393,7 @@ static int fts3PoslistPhraseMerge(
fts3PoslistCopy(0, &p1);
*pp1 = p1;
*pp2 = p2;
- if( !pp || *pp==p ){
+ if( *pp==p ){
return 0;
}
*p++ = 0x00;
@@ -116339,7 +145629,7 @@ static int fts3DoclistOrMerge(
}
*paOut = aOut;
- *pnOut = (p-aOut);
+ *pnOut = (int)(p-aOut);
assert( *pnOut<=n1+n2+FTS3_VARINT_MAX-1 );
return SQLITE_OK;
}
@@ -116356,26 +145646,33 @@ static int fts3DoclistOrMerge(
**
** The right-hand input doclist is overwritten by this function.
*/
-static void fts3DoclistPhraseMerge(
+static int fts3DoclistPhraseMerge(
int bDescDoclist, /* True if arguments are desc */
int nDist, /* Distance from left to right (1=adjacent) */
char *aLeft, int nLeft, /* Left doclist */
- char *aRight, int *pnRight /* IN/OUT: Right/output doclist */
+ char **paRight, int *pnRight /* IN/OUT: Right/output doclist */
){
sqlite3_int64 i1 = 0;
sqlite3_int64 i2 = 0;
sqlite3_int64 iPrev = 0;
+ char *aRight = *paRight;
char *pEnd1 = &aLeft[nLeft];
char *pEnd2 = &aRight[*pnRight];
char *p1 = aLeft;
char *p2 = aRight;
char *p;
int bFirstOut = 0;
- char *aOut = aRight;
+ char *aOut;
assert( nDist>0 );
-
+ if( bDescDoclist ){
+ aOut = sqlite3_malloc(*pnRight + FTS3_VARINT_MAX);
+ if( aOut==0 ) return SQLITE_NOMEM;
+ }else{
+ aOut = aRight;
+ }
p = aOut;
+
fts3GetDeltaVarint3(&p1, pEnd1, 0, &i1);
fts3GetDeltaVarint3(&p2, pEnd2, 0, &i2);
@@ -116403,7 +145700,63 @@ static void fts3DoclistPhraseMerge(
}
}
- *pnRight = p - aOut;
+ *pnRight = (int)(p - aOut);
+ if( bDescDoclist ){
+ sqlite3_free(aRight);
+ *paRight = aOut;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Argument pList points to a position list nList bytes in size. This
+** function checks to see if the position list contains any entries for
+** a token in position 0 (of any column). If so, it writes argument iDelta
+** to the output buffer pOut, followed by a position list consisting only
+** of the entries from pList at position 0, and terminated by an 0x00 byte.
+** The value returned is the number of bytes written to pOut (if any).
+*/
+SQLITE_PRIVATE int sqlite3Fts3FirstFilter(
+ sqlite3_int64 iDelta, /* Varint that may be written to pOut */
+ char *pList, /* Position list (no 0x00 term) */
+ int nList, /* Size of pList in bytes */
+ char *pOut /* Write output here */
+){
+ int nOut = 0;
+ int bWritten = 0; /* True once iDelta has been written */
+ char *p = pList;
+ char *pEnd = &pList[nList];
+
+ if( *p!=0x01 ){
+ if( *p==0x02 ){
+ nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta);
+ pOut[nOut++] = 0x02;
+ bWritten = 1;
+ }
+ fts3ColumnlistCopy(0, &p);
+ }
+
+ while( p<pEnd && *p==0x01 ){
+ sqlite3_int64 iCol;
+ p++;
+ p += sqlite3Fts3GetVarint(p, &iCol);
+ if( *p==0x02 ){
+ if( bWritten==0 ){
+ nOut += sqlite3Fts3PutVarint(&pOut[nOut], iDelta);
+ bWritten = 1;
+ }
+ pOut[nOut++] = 0x01;
+ nOut += sqlite3Fts3PutVarint(&pOut[nOut], iCol);
+ pOut[nOut++] = 0x02;
+ }
+ fts3ColumnlistCopy(0, &p);
+ }
+ if( bWritten ){
+ pOut[nOut++] = 0x00;
+ }
+
+ return nOut;
}
@@ -116478,8 +145831,22 @@ static int fts3TermSelectMerge(
){
if( pTS->aaOutput[0]==0 ){
/* If this is the first term selected, copy the doclist to the output
- ** buffer using memcpy(). */
- pTS->aaOutput[0] = sqlite3_malloc(nDoclist);
+ ** buffer using memcpy().
+ **
+ ** Add FTS3_VARINT_MAX bytes of unused space to the end of the
+ ** allocation. This is so as to ensure that the buffer is big enough
+ ** to hold the current doclist AND'd with any other doclist. If the
+ ** doclists are stored in order=ASC order, this padding would not be
+ ** required (since the size of [doclistA AND doclistB] is always less
+ ** than or equal to the size of [doclistA] in that case). But this is
+ ** not true for order=DESC. For example, a doclist containing (1, -1)
+ ** may be smaller than (-1), as in the first example the -1 may be stored
+ ** as a single-byte delta, whereas in the second it must be stored as a
+ ** FTS3_VARINT_MAX byte varint.
+ **
+ ** Similar padding is added in the fts3DoclistOrMerge() function.
+ */
+ pTS->aaOutput[0] = sqlite3_malloc(nDoclist + FTS3_VARINT_MAX + 1);
pTS->anOutput[0] = nDoclist;
if( pTS->aaOutput[0] ){
memcpy(pTS->aaOutput[0], aDoclist, nDoclist);
@@ -116555,6 +145922,7 @@ static int fts3SegReaderCursorAppend(
*/
static int fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */
+ int iLangid, /* Language id */
int iIndex, /* Index to search (from 0 to p->nIndex-1) */
int iLevel, /* Level of segments to scan */
const char *zTerm, /* Term to query for */
@@ -116575,7 +145943,7 @@ static int fts3SegReaderCursor(
** calls out here. */
if( iLevel<0 && p->aIndex ){
Fts3SegReader *pSeg = 0;
- rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix, &pSeg);
+ rc = sqlite3Fts3SegReaderPending(p, iIndex, zTerm, nTerm, isPrefix||isScan, &pSeg);
if( rc==SQLITE_OK && pSeg ){
rc = fts3SegReaderCursorAppend(pCsr, pSeg);
}
@@ -116583,7 +145951,7 @@ static int fts3SegReaderCursor(
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
if( rc==SQLITE_OK ){
- rc = sqlite3Fts3AllSegdirs(p, iIndex, iLevel, &pStmt);
+ rc = sqlite3Fts3AllSegdirs(p, iLangid, iIndex, iLevel, &pStmt);
}
while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){
@@ -116606,7 +145974,9 @@ static int fts3SegReaderCursor(
}
rc = sqlite3Fts3SegReaderNew(pCsr->nSegment+1,
- iStartBlock, iLeavesEndBlock, iEndBlock, zRoot, nRoot, &pSeg
+ (isPrefix==0 && isScan==0),
+ iStartBlock, iLeavesEndBlock,
+ iEndBlock, zRoot, nRoot, &pSeg
);
if( rc!=SQLITE_OK ) goto finished;
rc = fts3SegReaderCursorAppend(pCsr, pSeg);
@@ -116626,6 +145996,7 @@ static int fts3SegReaderCursor(
*/
SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
Fts3Table *p, /* FTS3 table handle */
+ int iLangid, /* Language-id to search */
int iIndex, /* Index to search (from 0 to p->nIndex-1) */
int iLevel, /* Level of segments to scan */
const char *zTerm, /* Term to query for */
@@ -116643,14 +146014,9 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
assert( FTS3_SEGCURSOR_ALL<0 && FTS3_SEGCURSOR_PENDING<0 );
assert( isPrefix==0 || isScan==0 );
- /* "isScan" is only set to true by the ft4aux module, an ordinary
- ** full-text tables. */
- assert( isScan==0 || p->aIndex==0 );
-
memset(pCsr, 0, sizeof(Fts3MultiSegReader));
-
return fts3SegReaderCursor(
- p, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr
+ p, iLangid, iIndex, iLevel, zTerm, nTerm, isPrefix, isScan, pCsr
);
}
@@ -116662,11 +146028,14 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderCursor(
*/
static int fts3SegReaderCursorAddZero(
Fts3Table *p, /* FTS virtual table handle */
+ int iLangid,
const char *zTerm, /* Term to scan doclist of */
int nTerm, /* Number of bytes in zTerm */
Fts3MultiSegReader *pCsr /* Fts3MultiSegReader to modify */
){
- return fts3SegReaderCursor(p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr);
+ return fts3SegReaderCursor(p,
+ iLangid, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0,pCsr
+ );
}
/*
@@ -116702,8 +146071,9 @@ static int fts3TermSegReaderCursor(
for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm ){
bFound = 1;
- rc = sqlite3Fts3SegReaderCursor(
- p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr);
+ rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
+ i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 0, 0, pSegcsr
+ );
pSegcsr->bLookup = 1;
}
}
@@ -116711,19 +146081,21 @@ static int fts3TermSegReaderCursor(
for(i=1; bFound==0 && i<p->nIndex; i++){
if( p->aIndex[i].nPrefix==nTerm+1 ){
bFound = 1;
- rc = sqlite3Fts3SegReaderCursor(
- p, i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
+ rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
+ i, FTS3_SEGCURSOR_ALL, zTerm, nTerm, 1, 0, pSegcsr
);
if( rc==SQLITE_OK ){
- rc = fts3SegReaderCursorAddZero(p, zTerm, nTerm, pSegcsr);
+ rc = fts3SegReaderCursorAddZero(
+ p, pCsr->iLangid, zTerm, nTerm, pSegcsr
+ );
}
}
}
}
if( bFound==0 ){
- rc = sqlite3Fts3SegReaderCursor(
- p, 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
+ rc = sqlite3Fts3SegReaderCursor(p, pCsr->iLangid,
+ 0, FTS3_SEGCURSOR_ALL, zTerm, nTerm, isPrefix, 0, pSegcsr
);
pSegcsr->bLookup = !isPrefix;
}
@@ -116742,7 +146114,7 @@ static void fts3SegReaderCursorFree(Fts3MultiSegReader *pSegcsr){
}
/*
-** This function retreives the doclist for the specified term (or term
+** This function retrieves the doclist for the specified term (or term
** prefix) from the database.
*/
static int fts3TermSelect(
@@ -116762,6 +146134,7 @@ static int fts3TermSelect(
filter.flags = FTS3_SEGMENT_IGNORE_EMPTY | FTS3_SEGMENT_REQUIRE_POS
| (pTok->isPrefix ? FTS3_SEGMENT_PREFIX : 0)
+ | (pTok->bFirst ? FTS3_SEGMENT_FIRST : 0)
| (iColumn<p->nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0);
filter.iCol = iColumn;
filter.zTerm = pTok->z;
@@ -116846,6 +146219,33 @@ static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){
}
/*
+** The following are copied from sqliteInt.h.
+**
+** Constants for the largest and smallest possible 64-bit signed integers.
+** These macros are designed to work correctly on both 32-bit and 64-bit
+** compilers.
+*/
+#ifndef SQLITE_AMALGAMATION
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
+/*
+** If the numeric type of argument pVal is "integer", then return it
+** converted to a 64-bit signed integer. Otherwise, return a copy of
+** the second parameter, iDefault.
+*/
+static sqlite3_int64 fts3DocidRange(sqlite3_value *pVal, i64 iDefault){
+ if( pVal ){
+ int eType = sqlite3_value_numeric_type(pVal);
+ if( eType==SQLITE_INTEGER ){
+ return sqlite3_value_int64(pVal);
+ }
+ }
+ return iDefault;
+}
+
+/*
** This is the xFilter interface for the virtual table. See
** the virtual table xFilter method documentation for additional
** information.
@@ -116868,56 +146268,72 @@ static int fts3FilterMethod(
int nVal, /* Number of elements in apVal */
sqlite3_value **apVal /* Arguments for the indexing scheme */
){
- int rc;
+ int rc = SQLITE_OK;
char *zSql; /* SQL statement used to access %_content */
+ int eSearch;
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
Fts3Cursor *pCsr = (Fts3Cursor *)pCursor;
+ sqlite3_value *pCons = 0; /* The MATCH or rowid constraint, if any */
+ sqlite3_value *pLangid = 0; /* The "langid = ?" constraint, if any */
+ sqlite3_value *pDocidGe = 0; /* The "docid >= ?" constraint, if any */
+ sqlite3_value *pDocidLe = 0; /* The "docid <= ?" constraint, if any */
+ int iIdx;
+
UNUSED_PARAMETER(idxStr);
UNUSED_PARAMETER(nVal);
- assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
- assert( nVal==0 || nVal==1 );
- assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) );
+ eSearch = (idxNum & 0x0000FFFF);
+ assert( eSearch>=0 && eSearch<=(FTS3_FULLTEXT_SEARCH+p->nColumn) );
assert( p->pSegments==0 );
+ /* Collect arguments into local variables */
+ iIdx = 0;
+ if( eSearch!=FTS3_FULLSCAN_SEARCH ) pCons = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_LANGID ) pLangid = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_DOCID_GE ) pDocidGe = apVal[iIdx++];
+ if( idxNum & FTS3_HAVE_DOCID_LE ) pDocidLe = apVal[iIdx++];
+ assert( iIdx==nVal );
+
/* In case the cursor has been used before, clear it now. */
sqlite3_finalize(pCsr->pStmt);
sqlite3_free(pCsr->aDoclist);
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
sqlite3Fts3ExprFree(pCsr->pExpr);
memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor));
+ /* Set the lower and upper bounds on docids to return */
+ pCsr->iMinDocid = fts3DocidRange(pDocidGe, SMALLEST_INT64);
+ pCsr->iMaxDocid = fts3DocidRange(pDocidLe, LARGEST_INT64);
+
if( idxStr ){
pCsr->bDesc = (idxStr[0]=='D');
}else{
pCsr->bDesc = p->bDescIdx;
}
- pCsr->eSearch = (i16)idxNum;
+ pCsr->eSearch = (i16)eSearch;
- if( idxNum!=FTS3_DOCID_SEARCH && idxNum!=FTS3_FULLSCAN_SEARCH ){
- int iCol = idxNum-FTS3_FULLTEXT_SEARCH;
- const char *zQuery = (const char *)sqlite3_value_text(apVal[0]);
+ if( eSearch!=FTS3_DOCID_SEARCH && eSearch!=FTS3_FULLSCAN_SEARCH ){
+ int iCol = eSearch-FTS3_FULLTEXT_SEARCH;
+ const char *zQuery = (const char *)sqlite3_value_text(pCons);
- if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){
+ if( zQuery==0 && sqlite3_value_type(pCons)!=SQLITE_NULL ){
return SQLITE_NOMEM;
}
- rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn,
- iCol, zQuery, -1, &pCsr->pExpr
+ pCsr->iLangid = 0;
+ if( pLangid ) pCsr->iLangid = sqlite3_value_int(pLangid);
+
+ assert( p->base.zErrMsg==0 );
+ rc = sqlite3Fts3ExprParse(p->pTokenizer, pCsr->iLangid,
+ p->azColumn, p->bFts4, p->nColumn, iCol, zQuery, -1, &pCsr->pExpr,
+ &p->base.zErrMsg
);
if( rc!=SQLITE_OK ){
- if( rc==SQLITE_ERROR ){
- static const char *zErr = "malformed MATCH expression: [%s]";
- p->base.zErrMsg = sqlite3_mprintf(zErr, zQuery);
- }
return rc;
}
- rc = sqlite3Fts3ReadLock(p);
- if( rc!=SQLITE_OK ) return rc;
-
rc = fts3EvalStart(pCsr);
-
sqlite3Fts3SegmentsClose(p);
if( rc!=SQLITE_OK ) return rc;
pCsr->pNextId = pCsr->aDoclist;
@@ -116929,24 +146345,32 @@ static int fts3FilterMethod(
** full-text query or docid lookup, the statement retrieves a single
** row by docid.
*/
- if( idxNum==FTS3_FULLSCAN_SEARCH ){
- const char *zSort = (pCsr->bDesc ? "DESC" : "ASC");
- const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x ORDER BY docid %s";
- zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName, zSort);
- }else{
- const char *zTmpl = "SELECT %s FROM %Q.'%q_content' AS x WHERE docid = ?";
- zSql = sqlite3_mprintf(zTmpl, p->zReadExprlist, p->zDb, p->zName);
+ if( eSearch==FTS3_FULLSCAN_SEARCH ){
+ if( pDocidGe || pDocidLe ){
+ zSql = sqlite3_mprintf(
+ "SELECT %s WHERE rowid BETWEEN %lld AND %lld ORDER BY rowid %s",
+ p->zReadExprlist, pCsr->iMinDocid, pCsr->iMaxDocid,
+ (pCsr->bDesc ? "DESC" : "ASC")
+ );
+ }else{
+ zSql = sqlite3_mprintf("SELECT %s ORDER BY rowid %s",
+ p->zReadExprlist, (pCsr->bDesc ? "DESC" : "ASC")
+ );
+ }
+ if( zSql ){
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
+ sqlite3_free(zSql);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }else if( eSearch==FTS3_DOCID_SEARCH ){
+ rc = fts3CursorSeekStmt(pCsr, &pCsr->pStmt);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_bind_value(pCsr->pStmt, 1, pCons);
+ }
}
- if( !zSql ) return SQLITE_NOMEM;
- rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0);
- sqlite3_free(zSql);
if( rc!=SQLITE_OK ) return rc;
- if( idxNum==FTS3_DOCID_SEARCH ){
- rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
- if( rc!=SQLITE_OK ) return rc;
- }
-
return fts3NextMethod(pCursor);
}
@@ -116973,10 +146397,17 @@ static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
/*
** This is the xColumn method, called by SQLite to request a value from
** the row that the supplied cursor currently points to.
+**
+** If:
+**
+** (iCol < p->nColumn) -> The value of the iCol'th user column.
+** (iCol == p->nColumn) -> Magic column with the same name as the table.
+** (iCol == p->nColumn+1) -> Docid column
+** (iCol == p->nColumn+2) -> Langid column
*/
static int fts3ColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
- sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
int rc = SQLITE_OK; /* Return Code */
@@ -116984,22 +146415,34 @@ static int fts3ColumnMethod(
Fts3Table *p = (Fts3Table *)pCursor->pVtab;
/* The column value supplied by SQLite must be in range. */
- assert( iCol>=0 && iCol<=p->nColumn+1 );
+ assert( iCol>=0 && iCol<=p->nColumn+2 );
if( iCol==p->nColumn+1 ){
/* This call is a request for the "docid" column. Since "docid" is an
** alias for "rowid", use the xRowid() method to obtain the value.
*/
- sqlite3_result_int64(pContext, pCsr->iPrevId);
+ sqlite3_result_int64(pCtx, pCsr->iPrevId);
}else if( iCol==p->nColumn ){
/* The extra column whose name is the same as the table.
- ** Return a blob which is a pointer to the cursor.
- */
- sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
+ ** Return a blob which is a pointer to the cursor. */
+ sqlite3_result_blob(pCtx, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT);
+ }else if( iCol==p->nColumn+2 && pCsr->pExpr ){
+ sqlite3_result_int64(pCtx, pCsr->iLangid);
}else{
+ /* The requested column is either a user column (one that contains
+ ** indexed data), or the language-id column. */
rc = fts3CursorSeek(0, pCsr);
+
if( rc==SQLITE_OK ){
- sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1));
+ if( iCol==p->nColumn+2 ){
+ int iLangid = 0;
+ if( p->zLanguageid ){
+ iLangid = sqlite3_column_int(pCsr->pStmt, p->nColumn+1);
+ }
+ sqlite3_result_int(pCtx, iLangid);
+ }else if( sqlite3_data_count(pCsr->pStmt)>(iCol+1) ){
+ sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
+ }
}
}
@@ -117026,23 +146469,88 @@ static int fts3UpdateMethod(
** hash-table to the database.
*/
static int fts3SyncMethod(sqlite3_vtab *pVtab){
- int rc = sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab);
- sqlite3Fts3SegmentsClose((Fts3Table *)pVtab);
+
+ /* Following an incremental-merge operation, assuming that the input
+ ** segments are not completely consumed (the usual case), they are updated
+ ** in place to remove the entries that have already been merged. This
+ ** involves updating the leaf block that contains the smallest unmerged
+ ** entry and each block (if any) between the leaf and the root node. So
+ ** if the height of the input segment b-trees is N, and input segments
+ ** are merged eight at a time, updating the input segments at the end
+ ** of an incremental-merge requires writing (8*(1+N)) blocks. N is usually
+ ** small - often between 0 and 2. So the overhead of the incremental
+ ** merge is somewhere between 8 and 24 blocks. To avoid this overhead
+ ** dwarfing the actual productive work accomplished, the incremental merge
+ ** is only attempted if it will write at least 64 leaf blocks. Hence
+ ** nMinMerge.
+ **
+ ** Of course, updating the input segments also involves deleting a bunch
+ ** of blocks from the segments table. But this is not considered overhead
+ ** as it would also be required by a crisis-merge that used the same input
+ ** segments.
+ */
+ const u32 nMinMerge = 64; /* Minimum amount of incr-merge work to do */
+
+ Fts3Table *p = (Fts3Table*)pVtab;
+ int rc = sqlite3Fts3PendingTermsFlush(p);
+
+ if( rc==SQLITE_OK
+ && p->nLeafAdd>(nMinMerge/16)
+ && p->nAutoincrmerge && p->nAutoincrmerge!=0xff
+ ){
+ int mxLevel = 0; /* Maximum relative level value in db */
+ int A; /* Incr-merge parameter A */
+
+ rc = sqlite3Fts3MaxLevel(p, &mxLevel);
+ assert( rc==SQLITE_OK || mxLevel==0 );
+ A = p->nLeafAdd * mxLevel;
+ A += (A/2);
+ if( A>(int)nMinMerge ) rc = sqlite3Fts3Incrmerge(p, A, p->nAutoincrmerge);
+ }
+ sqlite3Fts3SegmentsClose(p);
+ return rc;
+}
+
+/*
+** If it is currently unknown whether or not the FTS table has an %_stat
+** table (if p->bHasStat==2), attempt to determine this (set p->bHasStat
+** to 0 or 1). Return SQLITE_OK if successful, or an SQLite error code
+** if an error occurs.
+*/
+static int fts3SetHasStat(Fts3Table *p){
+ int rc = SQLITE_OK;
+ if( p->bHasStat==2 ){
+ const char *zFmt ="SELECT 1 FROM %Q.sqlite_master WHERE tbl_name='%q_stat'";
+ char *zSql = sqlite3_mprintf(zFmt, p->zDb, p->zName);
+ if( zSql ){
+ sqlite3_stmt *pStmt = 0;
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ int bHasStat = (sqlite3_step(pStmt)==SQLITE_ROW);
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) p->bHasStat = bHasStat;
+ }
+ sqlite3_free(zSql);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
return rc;
}
/*
-** Implementation of xBegin() method. This is a no-op.
+** Implementation of xBegin() method.
*/
static int fts3BeginMethod(sqlite3_vtab *pVtab){
- TESTONLY( Fts3Table *p = (Fts3Table*)pVtab );
+ Fts3Table *p = (Fts3Table*)pVtab;
UNUSED_PARAMETER(pVtab);
assert( p->pSegments==0 );
assert( p->nPendingData==0 );
assert( p->inTransaction!=1 );
TESTONLY( p->inTransaction = 1 );
TESTONLY( p->mxSavepoint = -1; );
- return SQLITE_OK;
+ p->nLeafAdd = 0;
+ return fts3SetHasStat(p);
}
/*
@@ -117082,13 +146590,33 @@ static int fts3RollbackMethod(sqlite3_vtab *pVtab){
*/
static void fts3ReversePoslist(char *pStart, char **ppPoslist){
char *p = &(*ppPoslist)[-2];
- char c;
+ char c = 0;
+ /* Skip backwards passed any trailing 0x00 bytes added by NearTrim() */
while( p>pStart && (c=*p--)==0 );
+
+ /* Search backwards for a varint with value zero (the end of the previous
+ ** poslist). This is an 0x00 byte preceded by some byte that does not
+ ** have the 0x80 bit set. */
while( p>pStart && (*p & 0x80) | c ){
c = *p--;
}
- if( p>pStart ){ p = &p[2]; }
+ assert( p==pStart || c==0 );
+
+ /* At this point p points to that preceding byte without the 0x80 bit
+ ** set. So to find the start of the poslist, skip forward 2 bytes then
+ ** over a varint.
+ **
+ ** Normally. The other case is that p==pStart and the poslist to return
+ ** is the first in the doclist. In this case do not skip forward 2 bytes.
+ ** The second part of the if condition (c==0 && *ppPoslist>&p[2])
+ ** is required for cases where the first byte of a doclist and the
+ ** doclist is empty. For example, if the first docid is 10, a doclist
+ ** that begins with:
+ **
+ ** 0x0A 0x00 <next docid delta varint>
+ */
+ if( p>pStart || (c==0 && *ppPoslist>&p[2]) ){ p = &p[2]; }
while( *p++&0x80 );
*ppPoslist = p;
}
@@ -117159,6 +146687,8 @@ static void fts3SnippetFunc(
}
if( !zEllipsis || !zEnd || !zStart ){
sqlite3_result_error_nomem(pContext);
+ }else if( nToken==0 ){
+ sqlite3_result_text(pContext, "", -1, SQLITE_STATIC);
}else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){
sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken);
}
@@ -117291,15 +146821,28 @@ static int fts3RenameMethod(
sqlite3 *db = p->db; /* Database connection */
int rc; /* Return Code */
- rc = sqlite3Fts3PendingTermsFlush(p);
- if( rc!=SQLITE_OK ){
- return rc;
+ /* At this point it must be known if the %_stat table exists or not.
+ ** So bHasStat may not be 2. */
+ rc = fts3SetHasStat(p);
+
+ /* As it happens, the pending terms table is always empty here. This is
+ ** because an "ALTER TABLE RENAME TABLE" statement inside a transaction
+ ** always opens a savepoint transaction. And the xSavepoint() method
+ ** flushes the pending terms table. But leave the (no-op) call to
+ ** PendingTermsFlush() in in case that changes.
+ */
+ assert( p->nPendingData==0 );
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts3PendingTermsFlush(p);
+ }
+
+ if( p->zContentTbl==0 ){
+ fts3DbExec(&rc, db,
+ "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
+ p->zDb, p->zName, zName
+ );
}
- fts3DbExec(&rc, db,
- "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';",
- p->zDb, p->zName, zName
- );
if( p->bHasDocsize ){
fts3DbExec(&rc, db,
"ALTER TABLE %Q.'%q_docsize' RENAME TO '%q_docsize';",
@@ -117329,11 +146872,15 @@ static int fts3RenameMethod(
** Flush the contents of the pending-terms table to disk.
*/
static int fts3SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ int rc = SQLITE_OK;
UNUSED_PARAMETER(iSavepoint);
assert( ((Fts3Table *)pVtab)->inTransaction );
assert( ((Fts3Table *)pVtab)->mxSavepoint < iSavepoint );
TESTONLY( ((Fts3Table *)pVtab)->mxSavepoint = iSavepoint );
- return fts3SyncMethod(pVtab);
+ if( ((Fts3Table *)pVtab)->bIgnoreSavepoint==0 ){
+ rc = fts3SyncMethod(pVtab);
+ }
+ return rc;
}
/*
@@ -117415,12 +146962,15 @@ static void hashDestroy(void *p){
*/
SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule);
SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule);
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
+SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const**ppModule);
+#endif
#ifdef SQLITE_ENABLE_ICU
SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule);
#endif
/*
-** Initialise the fts3 extension. If this extension is built as part
+** Initialize the fts3 extension. If this extension is built as part
** of the sqlite library, then this function is called directly by
** SQLite. If fts3 is built as a dynamically loadable extension, this
** function is called by the sqlite3_extension_init() entry point.
@@ -117430,12 +146980,19 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
Fts3Hash *pHash = 0;
const sqlite3_tokenizer_module *pSimple = 0;
const sqlite3_tokenizer_module *pPorter = 0;
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
+ const sqlite3_tokenizer_module *pUnicode = 0;
+#endif
#ifdef SQLITE_ENABLE_ICU
const sqlite3_tokenizer_module *pIcu = 0;
sqlite3Fts3IcuTokenizerModule(&pIcu);
#endif
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
+ sqlite3Fts3UnicodeTokenizer(&pUnicode);
+#endif
+
#ifdef SQLITE_TEST
rc = sqlite3Fts3InitTerm(db);
if( rc!=SQLITE_OK ) return rc;
@@ -117447,7 +147004,7 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
sqlite3Fts3SimpleTokenizerModule(&pSimple);
sqlite3Fts3PorterTokenizerModule(&pPorter);
- /* Allocate and initialise the hash-table used to store tokenizers. */
+ /* Allocate and initialize the hash-table used to store tokenizers. */
pHash = sqlite3_malloc(sizeof(Fts3Hash));
if( !pHash ){
rc = SQLITE_NOMEM;
@@ -117459,6 +147016,10 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
if( rc==SQLITE_OK ){
if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple)
|| sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter)
+
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
+ || sqlite3Fts3HashInsert(pHash, "unicode61", 10, (void *)pUnicode)
+#endif
#ifdef SQLITE_ENABLE_ICU
|| (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu))
#endif
@@ -117493,9 +147054,13 @@ SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){
db, "fts4", &fts3Module, (void *)pHash, 0
);
}
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts3InitTok(db, (void *)pHash);
+ }
return rc;
}
+
/* An error has occurred. Delete the hash table and return the error code. */
assert( rc!=SQLITE_OK );
if( pHash ){
@@ -117559,14 +147124,17 @@ static void fts3EvalAllocateReaders(
** This function assumes that pList points to a buffer allocated using
** sqlite3_malloc(). This function takes responsibility for eventually
** freeing the buffer.
+**
+** SQLITE_OK is returned if successful, or SQLITE_NOMEM if an error occurs.
*/
-static void fts3EvalPhraseMergeToken(
+static int fts3EvalPhraseMergeToken(
Fts3Table *pTab, /* FTS Table pointer */
Fts3Phrase *p, /* Phrase to merge pList/nList into */
int iToken, /* Token pList/nList corresponds to */
char *pList, /* Pointer to doclist */
int nList /* Number of bytes in pList */
){
+ int rc = SQLITE_OK;
assert( iToken!=p->iDoclistToken );
if( pList==0 ){
@@ -117605,13 +147173,16 @@ static void fts3EvalPhraseMergeToken(
nDiff = p->iDoclistToken - iToken;
}
- fts3DoclistPhraseMerge(pTab->bDescIdx, nDiff, pLeft, nLeft, pRight,&nRight);
+ rc = fts3DoclistPhraseMerge(
+ pTab->bDescIdx, nDiff, pLeft, nLeft, &pRight, &nRight
+ );
sqlite3_free(pLeft);
p->doclist.aAll = pRight;
p->doclist.nAll = nRight;
}
if( iToken>p->iDoclistToken ) p->iDoclistToken = iToken;
+ return rc;
}
/*
@@ -117637,7 +147208,7 @@ static int fts3EvalPhraseLoad(
char *pThis = 0;
rc = fts3TermSelect(pTab, pToken, p->iColumn, &nThis, &pThis);
if( rc==SQLITE_OK ){
- fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis);
+ rc = fts3EvalPhraseMergeToken(pTab, p, iToken, pThis, nThis);
}
}
assert( pToken->pSegcsr==0 );
@@ -117658,21 +147229,20 @@ static int fts3EvalPhraseLoad(
*/
static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
int iToken; /* Used to iterate through phrase tokens */
- int rc = SQLITE_OK; /* Return code */
char *aPoslist = 0; /* Position list for deferred tokens */
int nPoslist = 0; /* Number of bytes in aPoslist */
int iPrev = -1; /* Token number of previous deferred token */
assert( pPhrase->doclist.bFreeList==0 );
- for(iToken=0; rc==SQLITE_OK && iToken<pPhrase->nToken; iToken++){
+ for(iToken=0; iToken<pPhrase->nToken; iToken++){
Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
Fts3DeferredToken *pDeferred = pToken->pDeferred;
if( pDeferred ){
char *pList;
int nList;
- rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
+ int rc = sqlite3Fts3DeferredTokenList(pDeferred, &pList, &nList);
if( rc!=SQLITE_OK ) return rc;
if( pList==0 ){
@@ -117694,7 +147264,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
fts3PoslistPhraseMerge(&aOut, iToken-iPrev, 0, 1, &p1, &p2);
sqlite3_free(aPoslist);
aPoslist = pList;
- nPoslist = aOut - aPoslist;
+ nPoslist = (int)(aOut - aPoslist);
if( nPoslist==0 ){
sqlite3_free(aPoslist);
pPhrase->doclist.pList = 0;
@@ -117738,7 +147308,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
pPhrase->doclist.pList = aOut;
if( fts3PoslistPhraseMerge(&aOut, nDistance, 0, 1, &p1, &p2) ){
pPhrase->doclist.bFreeList = 1;
- pPhrase->doclist.nList = (aOut - pPhrase->doclist.pList);
+ pPhrase->doclist.nList = (int)(aOut - pPhrase->doclist.pList);
}else{
sqlite3_free(aOut);
pPhrase->doclist.pList = 0;
@@ -117752,6 +147322,12 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
}
/*
+** Maximum number of tokens a phrase may have to be considered for the
+** incremental doclists strategy.
+*/
+#define MAX_INCR_PHRASE_TOKENS 4
+
+/*
** This function is called for each Fts3Phrase in a full-text query
** expression to initialize the mechanism for returning rows. Once this
** function has been called successfully on an Fts3Phrase, it may be
@@ -117764,22 +147340,42 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
** SQLITE_OK is returned if no error occurs, otherwise an SQLite error code.
*/
static int fts3EvalPhraseStart(Fts3Cursor *pCsr, int bOptOk, Fts3Phrase *p){
- int rc; /* Error code */
- Fts3PhraseToken *pFirst = &p->aToken[0];
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ int rc = SQLITE_OK; /* Error code */
+ int i;
- if( pCsr->bDesc==pTab->bDescIdx
- && bOptOk==1
- && p->nToken==1
- && pFirst->pSegcsr
- && pFirst->pSegcsr->bLookup
- ){
+ /* Determine if doclists may be loaded from disk incrementally. This is
+ ** possible if the bOptOk argument is true, the FTS doclists will be
+ ** scanned in forward order, and the phrase consists of
+ ** MAX_INCR_PHRASE_TOKENS or fewer tokens, none of which are are "^first"
+ ** tokens or prefix tokens that cannot use a prefix-index. */
+ int bHaveIncr = 0;
+ int bIncrOk = (bOptOk
+ && pCsr->bDesc==pTab->bDescIdx
+ && p->nToken<=MAX_INCR_PHRASE_TOKENS && p->nToken>0
+#ifdef SQLITE_TEST
+ && pTab->bNoIncrDoclist==0
+#endif
+ );
+ for(i=0; bIncrOk==1 && i<p->nToken; i++){
+ Fts3PhraseToken *pToken = &p->aToken[i];
+ if( pToken->bFirst || (pToken->pSegcsr!=0 && !pToken->pSegcsr->bLookup) ){
+ bIncrOk = 0;
+ }
+ if( pToken->pSegcsr ) bHaveIncr = 1;
+ }
+
+ if( bIncrOk && bHaveIncr ){
/* Use the incremental approach. */
int iCol = (p->iColumn >= pTab->nColumn ? -1 : p->iColumn);
- rc = sqlite3Fts3MsrIncrStart(
- pTab, pFirst->pSegcsr, iCol, pFirst->z, pFirst->n);
+ for(i=0; rc==SQLITE_OK && i<p->nToken; i++){
+ Fts3PhraseToken *pToken = &p->aToken[i];
+ Fts3MultiSegReader *pSegcsr = pToken->pSegcsr;
+ if( pSegcsr ){
+ rc = sqlite3Fts3MsrIncrStart(pTab, pSegcsr, iCol, pToken->z, pToken->n);
+ }
+ }
p->bIncr = 1;
-
}else{
/* Load the full doclist for the phrase into memory. */
rc = fts3EvalPhraseLoad(pCsr, p);
@@ -117806,7 +147402,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(
int nDoclist, /* Length of aDoclist in bytes */
char **ppIter, /* IN/OUT: Iterator pointer */
sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */
- int *pnList, /* IN/OUT: List length pointer */
+ int *pnList, /* OUT: List length pointer */
u8 *pbEof /* OUT: End-of-file flag */
){
char *p = *ppIter;
@@ -117833,7 +147429,7 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(
iMul = (bDescIdx ? -1 : 1);
}
- *pnList = pEnd - pNext;
+ *pnList = (int)(pEnd - pNext);
*ppIter = pNext;
*piDocid = iDocid;
}else{
@@ -117847,22 +147443,168 @@ SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(
}else{
char *pSave = p;
fts3ReversePoslist(aDoclist, &p);
- *pnList = (pSave - p);
+ *pnList = (int)(pSave - p);
}
*ppIter = p;
}
}
/*
-** Attempt to move the phrase iterator to point to the next matching docid.
+** Iterate forwards through a doclist.
+*/
+SQLITE_PRIVATE void sqlite3Fts3DoclistNext(
+ int bDescIdx, /* True if the doclist is desc */
+ char *aDoclist, /* Pointer to entire doclist */
+ int nDoclist, /* Length of aDoclist in bytes */
+ char **ppIter, /* IN/OUT: Iterator pointer */
+ sqlite3_int64 *piDocid, /* IN/OUT: Docid pointer */
+ u8 *pbEof /* OUT: End-of-file flag */
+){
+ char *p = *ppIter;
+
+ assert( nDoclist>0 );
+ assert( *pbEof==0 );
+ assert( p || *piDocid==0 );
+ assert( !p || (p>=aDoclist && p<=&aDoclist[nDoclist]) );
+
+ if( p==0 ){
+ p = aDoclist;
+ p += sqlite3Fts3GetVarint(p, piDocid);
+ }else{
+ fts3PoslistCopy(0, &p);
+ while( p<&aDoclist[nDoclist] && *p==0 ) p++;
+ if( p>=&aDoclist[nDoclist] ){
+ *pbEof = 1;
+ }else{
+ sqlite3_int64 iVar;
+ p += sqlite3Fts3GetVarint(p, &iVar);
+ *piDocid += ((bDescIdx ? -1 : 1) * iVar);
+ }
+ }
+
+ *ppIter = p;
+}
+
+/*
+** Advance the iterator pDL to the next entry in pDL->aAll/nAll. Set *pbEof
+** to true if EOF is reached.
+*/
+static void fts3EvalDlPhraseNext(
+ Fts3Table *pTab,
+ Fts3Doclist *pDL,
+ u8 *pbEof
+){
+ char *pIter; /* Used to iterate through aAll */
+ char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
+
+ if( pDL->pNextDocid ){
+ pIter = pDL->pNextDocid;
+ }else{
+ pIter = pDL->aAll;
+ }
+
+ if( pIter>=pEnd ){
+ /* We have already reached the end of this doclist. EOF. */
+ *pbEof = 1;
+ }else{
+ sqlite3_int64 iDelta;
+ pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
+ if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
+ pDL->iDocid += iDelta;
+ }else{
+ pDL->iDocid -= iDelta;
+ }
+ pDL->pList = pIter;
+ fts3PoslistCopy(0, &pIter);
+ pDL->nList = (int)(pIter - pDL->pList);
+
+ /* pIter now points just past the 0x00 that terminates the position-
+ ** list for document pDL->iDocid. However, if this position-list was
+ ** edited in place by fts3EvalNearTrim(), then pIter may not actually
+ ** point to the start of the next docid value. The following line deals
+ ** with this case by advancing pIter past the zero-padding added by
+ ** fts3EvalNearTrim(). */
+ while( pIter<pEnd && *pIter==0 ) pIter++;
+
+ pDL->pNextDocid = pIter;
+ assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
+ *pbEof = 0;
+ }
+}
+
+/*
+** Helper type used by fts3EvalIncrPhraseNext() and incrPhraseTokenNext().
+*/
+typedef struct TokenDoclist TokenDoclist;
+struct TokenDoclist {
+ int bIgnore;
+ sqlite3_int64 iDocid;
+ char *pList;
+ int nList;
+};
+
+/*
+** Token pToken is an incrementally loaded token that is part of a
+** multi-token phrase. Advance it to the next matching document in the
+** database and populate output variable *p with the details of the new
+** entry. Or, if the iterator has reached EOF, set *pbEof to true.
+**
** If an error occurs, return an SQLite error code. Otherwise, return
** SQLITE_OK.
+*/
+static int incrPhraseTokenNext(
+ Fts3Table *pTab, /* Virtual table handle */
+ Fts3Phrase *pPhrase, /* Phrase to advance token of */
+ int iToken, /* Specific token to advance */
+ TokenDoclist *p, /* OUT: Docid and doclist for new entry */
+ u8 *pbEof /* OUT: True if iterator is at EOF */
+){
+ int rc = SQLITE_OK;
+
+ if( pPhrase->iDoclistToken==iToken ){
+ assert( p->bIgnore==0 );
+ assert( pPhrase->aToken[iToken].pSegcsr==0 );
+ fts3EvalDlPhraseNext(pTab, &pPhrase->doclist, pbEof);
+ p->pList = pPhrase->doclist.pList;
+ p->nList = pPhrase->doclist.nList;
+ p->iDocid = pPhrase->doclist.iDocid;
+ }else{
+ Fts3PhraseToken *pToken = &pPhrase->aToken[iToken];
+ assert( pToken->pDeferred==0 );
+ assert( pToken->pSegcsr || pPhrase->iDoclistToken>=0 );
+ if( pToken->pSegcsr ){
+ assert( p->bIgnore==0 );
+ rc = sqlite3Fts3MsrIncrNext(
+ pTab, pToken->pSegcsr, &p->iDocid, &p->pList, &p->nList
+ );
+ if( p->pList==0 ) *pbEof = 1;
+ }else{
+ p->bIgnore = 1;
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+** The phrase iterator passed as the second argument:
+**
+** * features at least one token that uses an incremental doclist, and
+**
+** * does not contain any deferred tokens.
+**
+** Advance it to the next matching documnent in the database and populate
+** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
** 1 before returning. Otherwise, if no error occurs and the iterator is
** successfully advanced, *pbEof is set to 0.
+**
+** If an error occurs, return an SQLite error code. Otherwise, return
+** SQLITE_OK.
*/
-static int fts3EvalPhraseNext(
+static int fts3EvalIncrPhraseNext(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Phrase *p, /* Phrase object to advance to next docid */
u8 *pbEof /* OUT: Set to 1 if EOF */
@@ -117870,57 +147612,116 @@ static int fts3EvalPhraseNext(
int rc = SQLITE_OK;
Fts3Doclist *pDL = &p->doclist;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+ u8 bEof = 0;
- if( p->bIncr ){
- assert( p->nToken==1 );
- assert( pDL->pNextDocid==0 );
+ /* This is only called if it is guaranteed that the phrase has at least
+ ** one incremental token. In which case the bIncr flag is set. */
+ assert( p->bIncr==1 );
+
+ if( p->nToken==1 && p->bIncr ){
rc = sqlite3Fts3MsrIncrNext(pTab, p->aToken[0].pSegcsr,
&pDL->iDocid, &pDL->pList, &pDL->nList
);
- if( rc==SQLITE_OK && !pDL->pList ){
- *pbEof = 1;
+ if( pDL->pList==0 ) bEof = 1;
+ }else{
+ int bDescDoclist = pCsr->bDesc;
+ struct TokenDoclist a[MAX_INCR_PHRASE_TOKENS];
+
+ memset(a, 0, sizeof(a));
+ assert( p->nToken<=MAX_INCR_PHRASE_TOKENS );
+ assert( p->iDoclistToken<MAX_INCR_PHRASE_TOKENS );
+
+ while( bEof==0 ){
+ int bMaxSet = 0;
+ sqlite3_int64 iMax = 0; /* Largest docid for all iterators */
+ int i; /* Used to iterate through tokens */
+
+ /* Advance the iterator for each token in the phrase once. */
+ for(i=0; rc==SQLITE_OK && i<p->nToken && bEof==0; i++){
+ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
+ if( a[i].bIgnore==0 && (bMaxSet==0 || DOCID_CMP(iMax, a[i].iDocid)<0) ){
+ iMax = a[i].iDocid;
+ bMaxSet = 1;
+ }
+ }
+ assert( rc!=SQLITE_OK || (p->nToken>=1 && a[p->nToken-1].bIgnore==0) );
+ assert( rc!=SQLITE_OK || bMaxSet );
+
+ /* Keep advancing iterators until they all point to the same document */
+ for(i=0; i<p->nToken; i++){
+ while( rc==SQLITE_OK && bEof==0
+ && a[i].bIgnore==0 && DOCID_CMP(a[i].iDocid, iMax)<0
+ ){
+ rc = incrPhraseTokenNext(pTab, p, i, &a[i], &bEof);
+ if( DOCID_CMP(a[i].iDocid, iMax)>0 ){
+ iMax = a[i].iDocid;
+ i = 0;
+ }
+ }
+ }
+
+ /* Check if the current entries really are a phrase match */
+ if( bEof==0 ){
+ int nList = 0;
+ int nByte = a[p->nToken-1].nList;
+ char *aDoclist = sqlite3_malloc(nByte+1);
+ if( !aDoclist ) return SQLITE_NOMEM;
+ memcpy(aDoclist, a[p->nToken-1].pList, nByte+1);
+
+ for(i=0; i<(p->nToken-1); i++){
+ if( a[i].bIgnore==0 ){
+ char *pL = a[i].pList;
+ char *pR = aDoclist;
+ char *pOut = aDoclist;
+ int nDist = p->nToken-1-i;
+ int res = fts3PoslistPhraseMerge(&pOut, nDist, 0, 1, &pL, &pR);
+ if( res==0 ) break;
+ nList = (int)(pOut - aDoclist);
+ }
+ }
+ if( i==(p->nToken-1) ){
+ pDL->iDocid = iMax;
+ pDL->pList = aDoclist;
+ pDL->nList = nList;
+ pDL->bFreeList = 1;
+ break;
+ }
+ sqlite3_free(aDoclist);
+ }
}
+ }
+
+ *pbEof = bEof;
+ return rc;
+}
+
+/*
+** Attempt to move the phrase iterator to point to the next matching docid.
+** If an error occurs, return an SQLite error code. Otherwise, return
+** SQLITE_OK.
+**
+** If there is no "next" entry and no error occurs, then *pbEof is set to
+** 1 before returning. Otherwise, if no error occurs and the iterator is
+** successfully advanced, *pbEof is set to 0.
+*/
+static int fts3EvalPhraseNext(
+ Fts3Cursor *pCsr, /* FTS Cursor handle */
+ Fts3Phrase *p, /* Phrase object to advance to next docid */
+ u8 *pbEof /* OUT: Set to 1 if EOF */
+){
+ int rc = SQLITE_OK;
+ Fts3Doclist *pDL = &p->doclist;
+ Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
+
+ if( p->bIncr ){
+ rc = fts3EvalIncrPhraseNext(pCsr, p, pbEof);
}else if( pCsr->bDesc!=pTab->bDescIdx && pDL->nAll ){
sqlite3Fts3DoclistPrev(pTab->bDescIdx, pDL->aAll, pDL->nAll,
&pDL->pNextDocid, &pDL->iDocid, &pDL->nList, pbEof
);
pDL->pList = pDL->pNextDocid;
}else{
- char *pIter; /* Used to iterate through aAll */
- char *pEnd = &pDL->aAll[pDL->nAll]; /* 1 byte past end of aAll */
- if( pDL->pNextDocid ){
- pIter = pDL->pNextDocid;
- }else{
- pIter = pDL->aAll;
- }
-
- if( pIter>=pEnd ){
- /* We have already reached the end of this doclist. EOF. */
- *pbEof = 1;
- }else{
- sqlite3_int64 iDelta;
- pIter += sqlite3Fts3GetVarint(pIter, &iDelta);
- if( pTab->bDescIdx==0 || pDL->pNextDocid==0 ){
- pDL->iDocid += iDelta;
- }else{
- pDL->iDocid -= iDelta;
- }
- pDL->pList = pIter;
- fts3PoslistCopy(0, &pIter);
- pDL->nList = (pIter - pDL->pList);
-
- /* pIter now points just past the 0x00 that terminates the position-
- ** list for document pDL->iDocid. However, if this position-list was
- ** edited in place by fts3EvalNearTrim(), then pIter may not actually
- ** point to the start of the next docid value. The following line deals
- ** with this case by advancing pIter past the zero-padding added by
- ** fts3EvalNearTrim(). */
- while( pIter<pEnd && *pIter==0 ) pIter++;
-
- pDL->pNextDocid = pIter;
- assert( pIter>=&pDL->aAll[pDL->nAll] || *pIter );
- *pbEof = 0;
- }
+ fts3EvalDlPhraseNext(pTab, pDL, pbEof);
}
return rc;
@@ -117945,21 +147746,22 @@ static int fts3EvalPhraseNext(
static void fts3EvalStartReaders(
Fts3Cursor *pCsr, /* FTS Cursor handle */
Fts3Expr *pExpr, /* Expression to initialize phrases in */
- int bOptOk, /* True to enable incremental loading */
int *pRc /* IN/OUT: Error code */
){
if( pExpr && SQLITE_OK==*pRc ){
if( pExpr->eType==FTSQUERY_PHRASE ){
- int i;
int nToken = pExpr->pPhrase->nToken;
- for(i=0; i<nToken; i++){
- if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
+ if( nToken ){
+ int i;
+ for(i=0; i<nToken; i++){
+ if( pExpr->pPhrase->aToken[i].pDeferred==0 ) break;
+ }
+ pExpr->bDeferred = (i==nToken);
}
- pExpr->bDeferred = (i==nToken);
- *pRc = fts3EvalPhraseStart(pCsr, bOptOk, pExpr->pPhrase);
+ *pRc = fts3EvalPhraseStart(pCsr, 1, pExpr->pPhrase);
}else{
- fts3EvalStartReaders(pCsr, pExpr->pLeft, bOptOk, pRc);
- fts3EvalStartReaders(pCsr, pExpr->pRight, bOptOk, pRc);
+ fts3EvalStartReaders(pCsr, pExpr->pLeft, pRc);
+ fts3EvalStartReaders(pCsr, pExpr->pRight, pRc);
pExpr->bDeferred = (pExpr->pLeft->bDeferred && pExpr->pRight->bDeferred);
}
}
@@ -118002,7 +147804,7 @@ static void fts3EvalTokenCosts(
Fts3Expr ***ppOr, /* Write new OR root to *(*ppOr)++ */
int *pRc /* IN/OUT: Error code */
){
- if( *pRc==SQLITE_OK && pExpr ){
+ if( *pRc==SQLITE_OK ){
if( pExpr->eType==FTSQUERY_PHRASE ){
Fts3Phrase *pPhrase = pExpr->pPhrase;
int i;
@@ -118016,6 +147818,11 @@ static void fts3EvalTokenCosts(
*pRc = sqlite3Fts3MsrOvfl(pCsr, pTC->pToken->pSegcsr, &pTC->nOvfl);
}
}else if( pExpr->eType!=FTSQUERY_NOT ){
+ assert( pExpr->eType==FTSQUERY_OR
+ || pExpr->eType==FTSQUERY_AND
+ || pExpr->eType==FTSQUERY_NEAR
+ );
+ assert( pExpr->pLeft && pExpr->pRight );
if( pExpr->eType==FTSQUERY_OR ){
pRoot = pExpr->pLeft;
**ppOr = pRoot;
@@ -118076,7 +147883,7 @@ static int fts3EvalAverageDocsize(Fts3Cursor *pCsr, int *pnPage){
}
if( nDoc==0 || nByte==0 ){
sqlite3_reset(pStmt);
- return SQLITE_CORRUPT_VTAB;
+ return FTS_CORRUPT_VTAB;
}
pCsr->nDoc = nDoc;
@@ -118120,6 +147927,15 @@ static int fts3EvalSelectDeferred(
int nMinEst = 0; /* The minimum count for any phrase so far. */
int nLoad4 = 1; /* (Phrases that will be loaded)^4. */
+ /* Tokens are never deferred for FTS tables created using the content=xxx
+ ** option. The reason being that it is not guaranteed that the content
+ ** table actually contains the same data as the index. To prevent this from
+ ** causing any problems, the deferred token optimization is completely
+ ** disabled for content=xxx tables. */
+ if( pTab->zContentTbl ){
+ return SQLITE_OK;
+ }
+
/* Count the tokens in this AND/NEAR cluster. If none of the doclists
** associated with the tokens spill onto overflow pages, or if there is
** only 1 token, exit early. No tokens to defer in this case. */
@@ -118182,8 +147998,12 @@ static int fts3EvalSelectDeferred(
fts3SegReaderCursorFree(pToken->pSegcsr);
pToken->pSegcsr = 0;
}else{
- nLoad4 = nLoad4*4;
- if( ii==0 || pTC->pPhrase->nToken>1 ){
+ /* Set nLoad4 to the value of (4^nOther) for the next iteration of the
+ ** for-loop. Except, limit the value to 2^24 to prevent it from
+ ** overflowing the 32-bit integer it is stored in. */
+ if( ii<12 ) nLoad4 = nLoad4*4;
+
+ if( ii==0 || (pTC->pPhrase->nToken>1 && ii!=nToken-1) ){
/* Either this is the cheapest token in the entire query, or it is
** part of a multi-token phrase. Either way, the entire doclist will
** (eventually) be loaded into memory. It may as well be now. */
@@ -118193,8 +148013,12 @@ static int fts3EvalSelectDeferred(
rc = fts3TermSelect(pTab, pToken, pTC->iCol, &nList, &pList);
assert( rc==SQLITE_OK || pList==0 );
if( rc==SQLITE_OK ){
+ rc = fts3EvalPhraseMergeToken(
+ pTab, pTC->pPhrase, pTC->iToken,pList,nList
+ );
+ }
+ if( rc==SQLITE_OK ){
int nCount;
- fts3EvalPhraseMergeToken(pTab, pTC->pPhrase, pTC->iToken,pList,nList);
nCount = fts3DoclistCountDocids(
pTC->pPhrase->doclist.aAll, pTC->pPhrase->doclist.nAll
);
@@ -118230,7 +148054,8 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
fts3EvalAllocateReaders(pCsr, pCsr->pExpr, &nToken, &nOr, &rc);
/* Determine which, if any, tokens in the expression should be deferred. */
- if( rc==SQLITE_OK && nToken>1 && pTab->bHasStat ){
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
+ if( rc==SQLITE_OK && nToken>1 && pTab->bFts4 ){
Fts3TokenAndCost *aTC;
Fts3Expr **apOr;
aTC = (Fts3TokenAndCost *)sqlite3_malloc(
@@ -118247,8 +148072,8 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
Fts3Expr **ppOr = apOr;
fts3EvalTokenCosts(pCsr, 0, pCsr->pExpr, &pTC, &ppOr, &rc);
- nToken = pTC-aTC;
- nOr = ppOr-apOr;
+ nToken = (int)(pTC-aTC);
+ nOr = (int)(ppOr-apOr);
if( rc==SQLITE_OK ){
rc = fts3EvalSelectDeferred(pCsr, 0, aTC, nToken);
@@ -118260,8 +148085,9 @@ static int fts3EvalStart(Fts3Cursor *pCsr){
sqlite3_free(aTC);
}
}
+#endif
- fts3EvalStartReaders(pCsr, pCsr->pExpr, 1, &rc);
+ fts3EvalStartReaders(pCsr, pCsr->pExpr, &rc);
return rc;
}
@@ -118320,7 +148146,7 @@ static int fts3EvalNearTrim(
&pOut, aTmp, nParam1, nParam2, paPoslist, &p2
);
if( res ){
- nNew = (pOut - pPhrase->doclist.pList) - 1;
+ nNew = (int)(pOut - pPhrase->doclist.pList) - 1;
assert( pPhrase->doclist.pList[nNew]=='\0' );
assert( nNew<=pPhrase->doclist.nList && nNew>0 );
memset(&pPhrase->doclist.pList[nNew], 0, pPhrase->doclist.nList - nNew);
@@ -118370,7 +148196,7 @@ static int fts3EvalNearTrim(
** 2. NEAR is treated as AND. If the expression is "x NEAR y", it is
** advanced to point to the next row that matches "x AND y".
**
-** See fts3EvalTestDeferredAndNear() for details on testing if a row is
+** See sqlite3Fts3EvalTestDeferred() for details on testing if a row is
** really a match, taking into account deferred tokens and NEAR operators.
*/
static void fts3EvalNextRow(
@@ -118417,6 +148243,22 @@ static void fts3EvalNextRow(
}
pExpr->iDocid = pLeft->iDocid;
pExpr->bEof = (pLeft->bEof || pRight->bEof);
+ if( pExpr->eType==FTSQUERY_NEAR && pExpr->bEof ){
+ if( pRight->pPhrase && pRight->pPhrase->doclist.aAll ){
+ Fts3Doclist *pDl = &pRight->pPhrase->doclist;
+ while( *pRc==SQLITE_OK && pRight->bEof==0 ){
+ memset(pDl->pList, 0, pDl->nList);
+ fts3EvalNextRow(pCsr, pRight, pRc);
+ }
+ }
+ if( pLeft->pPhrase && pLeft->pPhrase->doclist.aAll ){
+ Fts3Doclist *pDl = &pLeft->pPhrase->doclist;
+ while( *pRc==SQLITE_OK && pLeft->bEof==0 ){
+ memset(pDl->pList, 0, pDl->nList);
+ fts3EvalNextRow(pCsr, pLeft, pRc);
+ }
+ }
+ }
}
break;
}
@@ -118535,39 +148377,46 @@ static int fts3EvalNearTest(Fts3Expr *pExpr, int *pRc){
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
- aTmp = sqlite3_malloc(nTmp*2);
- if( !aTmp ){
- *pRc = SQLITE_NOMEM;
+ if( nTmp==0 ){
res = 0;
}else{
- char *aPoslist = p->pPhrase->doclist.pList;
- int nToken = p->pPhrase->nToken;
+ aTmp = sqlite3_malloc(nTmp*2);
+ if( !aTmp ){
+ *pRc = SQLITE_NOMEM;
+ res = 0;
+ }else{
+ char *aPoslist = p->pPhrase->doclist.pList;
+ int nToken = p->pPhrase->nToken;
- for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){
- Fts3Phrase *pPhrase = p->pRight->pPhrase;
- int nNear = p->nNear;
- res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
- }
-
- aPoslist = pExpr->pRight->pPhrase->doclist.pList;
- nToken = pExpr->pRight->pPhrase->nToken;
- for(p=pExpr->pLeft; p && res; p=p->pLeft){
- int nNear = p->pParent->nNear;
- Fts3Phrase *pPhrase = (
- p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase
- );
- res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
+ for(p=p->pParent;res && p && p->eType==FTSQUERY_NEAR; p=p->pParent){
+ Fts3Phrase *pPhrase = p->pRight->pPhrase;
+ int nNear = p->nNear;
+ res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
+ }
+
+ aPoslist = pExpr->pRight->pPhrase->doclist.pList;
+ nToken = pExpr->pRight->pPhrase->nToken;
+ for(p=pExpr->pLeft; p && res; p=p->pLeft){
+ int nNear;
+ Fts3Phrase *pPhrase;
+ assert( p->pParent && p->pParent->pLeft==p );
+ nNear = p->pParent->nNear;
+ pPhrase = (
+ p->eType==FTSQUERY_NEAR ? p->pRight->pPhrase : p->pPhrase
+ );
+ res = fts3EvalNearTrim(nNear, aTmp, &aPoslist, &nToken, pPhrase);
+ }
}
- }
- sqlite3_free(aTmp);
+ sqlite3_free(aTmp);
+ }
}
return res;
}
/*
-** This function is a helper function for fts3EvalTestDeferredAndNear().
+** This function is a helper function for sqlite3Fts3EvalTestDeferred().
** Assuming no error occurs or has occurred, It returns non-zero if the
** expression passed as the second argument matches the row that pCsr
** currently points to, or zero if it does not.
@@ -118640,6 +148489,7 @@ static int fts3EvalTestExpr(
break;
default: {
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
if( pCsr->pDeferred
&& (pExpr->iDocid==pCsr->iPrevId || pExpr->bDeferred)
){
@@ -118651,7 +148501,9 @@ static int fts3EvalTestExpr(
*pRc = fts3EvalDeferredPhrase(pCsr, pPhrase);
bHit = (pPhrase->doclist.pList!=0);
pExpr->iDocid = pCsr->iPrevId;
- }else{
+ }else
+#endif
+ {
bHit = (pExpr->bEof==0 && pExpr->iDocid==pCsr->iPrevId);
}
break;
@@ -118685,7 +148537,7 @@ static int fts3EvalTestExpr(
** Or, if no error occurs and it seems the current row does match the FTS
** query, return 0.
*/
-static int fts3EvalTestDeferredAndNear(Fts3Cursor *pCsr, int *pRc){
+SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc){
int rc = *pRc;
int bMiss = 0;
if( rc==SQLITE_OK ){
@@ -118732,8 +148584,18 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
pCsr->isRequireSeek = 1;
pCsr->isMatchinfoNeeded = 1;
pCsr->iPrevId = pExpr->iDocid;
- }while( pCsr->isEof==0 && fts3EvalTestDeferredAndNear(pCsr, &rc) );
+ }while( pCsr->isEof==0 && sqlite3Fts3EvalTestDeferred(pCsr, &rc) );
+ }
+
+ /* Check if the cursor is past the end of the docid range specified
+ ** by Fts3Cursor.iMinDocid/iMaxDocid. If so, set the EOF flag. */
+ if( rc==SQLITE_OK && (
+ (pCsr->bDesc==0 && pCsr->iPrevId>pCsr->iMaxDocid)
+ || (pCsr->bDesc!=0 && pCsr->iPrevId<pCsr->iMinDocid)
+ )){
+ pCsr->isEof = 1;
}
+
return rc;
}
@@ -118757,14 +148619,19 @@ static void fts3EvalRestart(
if( pPhrase ){
fts3EvalInvalidatePoslist(pPhrase);
if( pPhrase->bIncr ){
- assert( pPhrase->nToken==1 );
- assert( pPhrase->aToken[0].pSegcsr );
- sqlite3Fts3MsrIncrRestart(pPhrase->aToken[0].pSegcsr);
+ int i;
+ for(i=0; i<pPhrase->nToken; i++){
+ Fts3PhraseToken *pToken = &pPhrase->aToken[i];
+ assert( pToken->pDeferred==0 );
+ if( pToken->pSegcsr ){
+ sqlite3Fts3MsrIncrRestart(pToken->pSegcsr);
+ }
+ }
*pRc = fts3EvalPhraseStart(pCsr, 0, pPhrase);
}
-
pPhrase->doclist.pNextDocid = 0;
pPhrase->doclist.iDocid = 0;
+ pPhrase->pOrPoslist = 0;
}
pExpr->iDocid = 0;
@@ -118807,7 +148674,7 @@ static void fts3EvalUpdateCounts(Fts3Expr *pExpr){
pExpr->aMI[iCol*3 + 2] += (iCnt>0);
if( *p==0x00 ) break;
p++;
- p += sqlite3Fts3GetVarint32(p, &iCol);
+ p += fts3GetVarint32(p, &iCol);
}
}
@@ -118878,7 +148745,7 @@ static int fts3EvalGatherStats(
pCsr->iPrevId = pRoot->iDocid;
}while( pCsr->isEof==0
&& pRoot->eType==FTSQUERY_NEAR
- && fts3EvalTestDeferredAndNear(pCsr, &rc)
+ && sqlite3Fts3EvalTestDeferred(pCsr, &rc)
);
if( rc==SQLITE_OK && pCsr->isEof==0 ){
@@ -118903,7 +148770,6 @@ static int fts3EvalGatherStats(
fts3EvalNextRow(pCsr, pRoot, &rc);
assert( pRoot->bEof==0 );
}while( pRoot->iDocid!=iDocid && rc==SQLITE_OK );
- fts3EvalTestDeferredAndNear(pCsr, &rc);
}
}
return rc;
@@ -118977,7 +148843,7 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(
** of the current row.
**
** More specifically, the returned buffer contains 1 varint for each
-** occurence of the phrase in the column, stored using the normal (delta+2)
+** occurrence of the phrase in the column, stored using the normal (delta+2)
** compression and is terminated by either an 0x01 or 0x00 byte. For example,
** if the requested column contains "a b X c d X X" and the position-list
** for 'X' is requested, the buffer returned may contain:
@@ -118987,40 +148853,131 @@ SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(
** This function works regardless of whether or not the phrase is deferred,
** incremental, or neither.
*/
-SQLITE_PRIVATE char *sqlite3Fts3EvalPhrasePoslist(
+SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(
Fts3Cursor *pCsr, /* FTS3 cursor object */
Fts3Expr *pExpr, /* Phrase to return doclist for */
- int iCol /* Column to return position list for */
+ int iCol, /* Column to return position list for */
+ char **ppOut /* OUT: Pointer to position list */
){
Fts3Phrase *pPhrase = pExpr->pPhrase;
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
- char *pIter = pPhrase->doclist.pList;
+ char *pIter;
int iThis;
+ sqlite3_int64 iDocid;
+ /* If this phrase is applies specifically to some column other than
+ ** column iCol, return a NULL pointer. */
+ *ppOut = 0;
assert( iCol>=0 && iCol<pTab->nColumn );
- if( !pIter
- || pExpr->bEof
- || pExpr->iDocid!=pCsr->iPrevId
- || (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol)
- ){
- return 0;
+ if( (pPhrase->iColumn<pTab->nColumn && pPhrase->iColumn!=iCol) ){
+ return SQLITE_OK;
+ }
+
+ iDocid = pExpr->iDocid;
+ pIter = pPhrase->doclist.pList;
+ if( iDocid!=pCsr->iPrevId || pExpr->bEof ){
+ int rc = SQLITE_OK;
+ int bDescDoclist = pTab->bDescIdx; /* For DOCID_CMP macro */
+ int bOr = 0;
+ u8 bTreeEof = 0;
+ Fts3Expr *p; /* Used to iterate from pExpr to root */
+ Fts3Expr *pNear; /* Most senior NEAR ancestor (or pExpr) */
+ int bMatch;
+
+ /* Check if this phrase descends from an OR expression node. If not,
+ ** return NULL. Otherwise, the entry that corresponds to docid
+ ** pCsr->iPrevId may lie earlier in the doclist buffer. Or, if the
+ ** tree that the node is part of has been marked as EOF, but the node
+ ** itself is not EOF, then it may point to an earlier entry. */
+ pNear = pExpr;
+ for(p=pExpr->pParent; p; p=p->pParent){
+ if( p->eType==FTSQUERY_OR ) bOr = 1;
+ if( p->eType==FTSQUERY_NEAR ) pNear = p;
+ if( p->bEof ) bTreeEof = 1;
+ }
+ if( bOr==0 ) return SQLITE_OK;
+
+ /* This is the descendent of an OR node. In this case we cannot use
+ ** an incremental phrase. Load the entire doclist for the phrase
+ ** into memory in this case. */
+ if( pPhrase->bIncr ){
+ int bEofSave = pNear->bEof;
+ fts3EvalRestart(pCsr, pNear, &rc);
+ while( rc==SQLITE_OK && !pNear->bEof ){
+ fts3EvalNextRow(pCsr, pNear, &rc);
+ if( bEofSave==0 && pNear->iDocid==iDocid ) break;
+ }
+ assert( rc!=SQLITE_OK || pPhrase->bIncr==0 );
+ }
+ if( bTreeEof ){
+ while( rc==SQLITE_OK && !pNear->bEof ){
+ fts3EvalNextRow(pCsr, pNear, &rc);
+ }
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ bMatch = 1;
+ for(p=pNear; p; p=p->pLeft){
+ u8 bEof = 0;
+ Fts3Expr *pTest = p;
+ Fts3Phrase *pPh;
+ assert( pTest->eType==FTSQUERY_NEAR || pTest->eType==FTSQUERY_PHRASE );
+ if( pTest->eType==FTSQUERY_NEAR ) pTest = pTest->pRight;
+ assert( pTest->eType==FTSQUERY_PHRASE );
+ pPh = pTest->pPhrase;
+
+ pIter = pPh->pOrPoslist;
+ iDocid = pPh->iOrDocid;
+ if( pCsr->bDesc==bDescDoclist ){
+ bEof = !pPh->doclist.nAll ||
+ (pIter >= (pPh->doclist.aAll + pPh->doclist.nAll));
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)<0 ) && bEof==0 ){
+ sqlite3Fts3DoclistNext(
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
+ &pIter, &iDocid, &bEof
+ );
+ }
+ }else{
+ bEof = !pPh->doclist.nAll || (pIter && pIter<=pPh->doclist.aAll);
+ while( (pIter==0 || DOCID_CMP(iDocid, pCsr->iPrevId)>0 ) && bEof==0 ){
+ int dummy;
+ sqlite3Fts3DoclistPrev(
+ bDescDoclist, pPh->doclist.aAll, pPh->doclist.nAll,
+ &pIter, &iDocid, &dummy, &bEof
+ );
+ }
+ }
+ pPh->pOrPoslist = pIter;
+ pPh->iOrDocid = iDocid;
+ if( bEof || iDocid!=pCsr->iPrevId ) bMatch = 0;
+ }
+
+ if( bMatch ){
+ pIter = pPhrase->pOrPoslist;
+ }else{
+ pIter = 0;
+ }
}
+ if( pIter==0 ) return SQLITE_OK;
- assert( pPhrase->doclist.nList>0 );
if( *pIter==0x01 ){
pIter++;
- pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
+ pIter += fts3GetVarint32(pIter, &iThis);
}else{
iThis = 0;
}
while( iThis<iCol ){
fts3ColumnlistCopy(0, &pIter);
- if( *pIter==0x00 ) return 0;
+ if( *pIter==0x00 ) return SQLITE_OK;
pIter++;
- pIter += sqlite3Fts3GetVarint32(pIter, &iThis);
+ pIter += fts3GetVarint32(pIter, &iThis);
+ }
+ if( *pIter==0x00 ){
+ pIter = 0;
}
- return ((iCol==iThis)?pIter:0);
+ *ppOut = ((iCol==iThis)?pIter:0);
+ return SQLITE_OK;
}
/*
@@ -119043,11 +149000,24 @@ SQLITE_PRIVATE void sqlite3Fts3EvalPhraseCleanup(Fts3Phrase *pPhrase){
}
}
+
+/*
+** Return SQLITE_CORRUPT_VTAB.
+*/
+#ifdef SQLITE_DEBUG
+SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
+ return SQLITE_CORRUPT_VTAB;
+}
+#endif
+
#if !SQLITE_CORE
/*
** Initialize API pointer table, if required.
*/
-SQLITE_API int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_fts3_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
@@ -119074,6 +149044,7 @@ SQLITE_API int sqlite3_extension_init(
******************************************************************************
**
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <string.h> */
@@ -119093,6 +149064,7 @@ struct Fts3auxCursor {
Fts3SegFilter filter;
char *zStop;
int nStop; /* Byte-length of string zStop */
+ int iLangid; /* Language id to query */
int isEof; /* True if cursor is at EOF */
sqlite3_int64 iRowid; /* Current rowid */
@@ -119107,7 +149079,8 @@ struct Fts3auxCursor {
/*
** Schema of the terms table.
*/
-#define FTS3_TERMS_SCHEMA "CREATE TABLE x(term, col, documents, occurrences)"
+#define FTS3_AUX_SCHEMA \
+ "CREATE TABLE x(term, col, documents, occurrences, languageid HIDDEN)"
/*
** This function does all the work for both the xConnect and xCreate methods.
@@ -119132,20 +149105,29 @@ static int fts3auxConnectMethod(
UNUSED_PARAMETER(pUnused);
- /* The user should specify a single argument - the name of an fts3 table. */
- if( argc!=4 ){
- *pzErr = sqlite3_mprintf(
- "wrong number of arguments to fts4aux constructor"
- );
- return SQLITE_ERROR;
- }
+ /* The user should invoke this in one of two forms:
+ **
+ ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table);
+ ** CREATE VIRTUAL TABLE xxx USING fts4aux(fts4-table-db, fts4-table);
+ */
+ if( argc!=4 && argc!=5 ) goto bad_args;
zDb = argv[1];
- nDb = strlen(zDb);
- zFts3 = argv[3];
- nFts3 = strlen(zFts3);
+ nDb = (int)strlen(zDb);
+ if( argc==5 ){
+ if( nDb==4 && 0==sqlite3_strnicmp("temp", zDb, 4) ){
+ zDb = argv[3];
+ nDb = (int)strlen(zDb);
+ zFts3 = argv[4];
+ }else{
+ goto bad_args;
+ }
+ }else{
+ zFts3 = argv[3];
+ }
+ nFts3 = (int)strlen(zFts3);
- rc = sqlite3_declare_vtab(db, FTS3_TERMS_SCHEMA);
+ rc = sqlite3_declare_vtab(db, FTS3_AUX_SCHEMA);
if( rc!=SQLITE_OK ) return rc;
nByte = sizeof(Fts3auxTable) + sizeof(Fts3Table) + nDb + nFts3 + 2;
@@ -119165,6 +149147,10 @@ static int fts3auxConnectMethod(
*ppVtab = (sqlite3_vtab *)p;
return SQLITE_OK;
+
+ bad_args:
+ sqlite3Fts3ErrMsg(pzErr, "invalid arguments to fts4aux constructor");
+ return SQLITE_ERROR;
}
/*
@@ -119201,6 +149187,8 @@ static int fts3auxBestIndexMethod(
int iEq = -1;
int iGe = -1;
int iLe = -1;
+ int iLangid = -1;
+ int iNext = 1; /* Next free argvIndex value */
UNUSED_PARAMETER(pVTab);
@@ -119212,36 +149200,48 @@ static int fts3auxBestIndexMethod(
pInfo->orderByConsumed = 1;
}
- /* Search for equality and range constraints on the "term" column. */
+ /* Search for equality and range constraints on the "term" column.
+ ** And equality constraints on the hidden "languageid" column. */
for(i=0; i<pInfo->nConstraint; i++){
- if( pInfo->aConstraint[i].usable && pInfo->aConstraint[i].iColumn==0 ){
+ if( pInfo->aConstraint[i].usable ){
int op = pInfo->aConstraint[i].op;
- if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
- if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
- if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
+ int iCol = pInfo->aConstraint[i].iColumn;
+
+ if( iCol==0 ){
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iEq = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LT ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_LE ) iLe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GT ) iGe = i;
+ if( op==SQLITE_INDEX_CONSTRAINT_GE ) iGe = i;
+ }
+ if( iCol==4 ){
+ if( op==SQLITE_INDEX_CONSTRAINT_EQ ) iLangid = i;
+ }
}
}
if( iEq>=0 ){
pInfo->idxNum = FTS4AUX_EQ_CONSTRAINT;
- pInfo->aConstraintUsage[iEq].argvIndex = 1;
+ pInfo->aConstraintUsage[iEq].argvIndex = iNext++;
pInfo->estimatedCost = 5;
}else{
pInfo->idxNum = 0;
pInfo->estimatedCost = 20000;
if( iGe>=0 ){
pInfo->idxNum += FTS4AUX_GE_CONSTRAINT;
- pInfo->aConstraintUsage[iGe].argvIndex = 1;
+ pInfo->aConstraintUsage[iGe].argvIndex = iNext++;
pInfo->estimatedCost /= 2;
}
if( iLe>=0 ){
pInfo->idxNum += FTS4AUX_LE_CONSTRAINT;
- pInfo->aConstraintUsage[iLe].argvIndex = 1 + (iGe>=0);
+ pInfo->aConstraintUsage[iLe].argvIndex = iNext++;
pInfo->estimatedCost /= 2;
}
}
+ if( iLangid>=0 ){
+ pInfo->aConstraintUsage[iLangid].argvIndex = iNext++;
+ pInfo->estimatedCost--;
+ }
return SQLITE_OK;
}
@@ -119401,7 +149401,14 @@ static int fts3auxFilterMethod(
Fts3auxCursor *pCsr = (Fts3auxCursor *)pCursor;
Fts3Table *pFts3 = ((Fts3auxTable *)pCursor->pVtab)->pFts3Tab;
int rc;
- int isScan;
+ int isScan = 0;
+ int iLangVal = 0; /* Language id to query */
+
+ int iEq = -1; /* Index of term=? value in apVal */
+ int iGe = -1; /* Index of term>=? value in apVal */
+ int iLe = -1; /* Index of term<=? value in apVal */
+ int iLangid = -1; /* Index of languageid=? value in apVal */
+ int iNext = 0;
UNUSED_PARAMETER(nVal);
UNUSED_PARAMETER(idxStr);
@@ -119411,7 +149418,21 @@ static int fts3auxFilterMethod(
|| idxNum==FTS4AUX_LE_CONSTRAINT || idxNum==FTS4AUX_GE_CONSTRAINT
|| idxNum==(FTS4AUX_LE_CONSTRAINT|FTS4AUX_GE_CONSTRAINT)
);
- isScan = (idxNum!=FTS4AUX_EQ_CONSTRAINT);
+
+ if( idxNum==FTS4AUX_EQ_CONSTRAINT ){
+ iEq = iNext++;
+ }else{
+ isScan = 1;
+ if( idxNum & FTS4AUX_GE_CONSTRAINT ){
+ iGe = iNext++;
+ }
+ if( idxNum & FTS4AUX_LE_CONSTRAINT ){
+ iLe = iNext++;
+ }
+ }
+ if( iNext<nVal ){
+ iLangid = iNext++;
+ }
/* In case this cursor is being reused, close and zero it. */
testcase(pCsr->filter.zTerm);
@@ -119423,22 +149444,35 @@ static int fts3auxFilterMethod(
pCsr->filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
if( isScan ) pCsr->filter.flags |= FTS3_SEGMENT_SCAN;
- if( idxNum&(FTS4AUX_EQ_CONSTRAINT|FTS4AUX_GE_CONSTRAINT) ){
+ if( iEq>=0 || iGe>=0 ){
const unsigned char *zStr = sqlite3_value_text(apVal[0]);
+ assert( (iEq==0 && iGe==-1) || (iEq==-1 && iGe==0) );
if( zStr ){
pCsr->filter.zTerm = sqlite3_mprintf("%s", zStr);
pCsr->filter.nTerm = sqlite3_value_bytes(apVal[0]);
if( pCsr->filter.zTerm==0 ) return SQLITE_NOMEM;
}
}
- if( idxNum&FTS4AUX_LE_CONSTRAINT ){
- int iIdx = (idxNum&FTS4AUX_GE_CONSTRAINT) ? 1 : 0;
- pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iIdx]));
- pCsr->nStop = sqlite3_value_bytes(apVal[iIdx]);
+
+ if( iLe>=0 ){
+ pCsr->zStop = sqlite3_mprintf("%s", sqlite3_value_text(apVal[iLe]));
+ pCsr->nStop = sqlite3_value_bytes(apVal[iLe]);
if( pCsr->zStop==0 ) return SQLITE_NOMEM;
}
+
+ if( iLangid>=0 ){
+ iLangVal = sqlite3_value_int(apVal[iLangid]);
+
+ /* If the user specified a negative value for the languageid, use zero
+ ** instead. This works, as the "languageid=?" constraint will also
+ ** be tested by the VDBE layer. The test will always be false (since
+ ** this module will not return a row with a negative languageid), and
+ ** so the overall query will return zero rows. */
+ if( iLangVal<0 ) iLangVal = 0;
+ }
+ pCsr->iLangid = iLangVal;
- rc = sqlite3Fts3SegReaderCursor(pFts3, 0, FTS3_SEGCURSOR_ALL,
+ rc = sqlite3Fts3SegReaderCursor(pFts3, iLangVal, 0, FTS3_SEGCURSOR_ALL,
pCsr->filter.zTerm, pCsr->filter.nTerm, 0, isScan, &pCsr->csr
);
if( rc==SQLITE_OK ){
@@ -119462,24 +149496,37 @@ static int fts3auxEofMethod(sqlite3_vtab_cursor *pCursor){
*/
static int fts3auxColumnMethod(
sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
- sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
int iCol /* Index of column to read value from */
){
Fts3auxCursor *p = (Fts3auxCursor *)pCursor;
assert( p->isEof==0 );
- if( iCol==0 ){ /* Column "term" */
- sqlite3_result_text(pContext, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
- }else if( iCol==1 ){ /* Column "col" */
- if( p->iCol ){
- sqlite3_result_int(pContext, p->iCol-1);
- }else{
- sqlite3_result_text(pContext, "*", -1, SQLITE_STATIC);
- }
- }else if( iCol==2 ){ /* Column "documents" */
- sqlite3_result_int64(pContext, p->aStat[p->iCol].nDoc);
- }else{ /* Column "occurrences" */
- sqlite3_result_int64(pContext, p->aStat[p->iCol].nOcc);
+ switch( iCol ){
+ case 0: /* term */
+ sqlite3_result_text(pCtx, p->csr.zTerm, p->csr.nTerm, SQLITE_TRANSIENT);
+ break;
+
+ case 1: /* col */
+ if( p->iCol ){
+ sqlite3_result_int(pCtx, p->iCol-1);
+ }else{
+ sqlite3_result_text(pCtx, "*", -1, SQLITE_STATIC);
+ }
+ break;
+
+ case 2: /* documents */
+ sqlite3_result_int64(pCtx, p->aStat[p->iCol].nDoc);
+ break;
+
+ case 3: /* occurrences */
+ sqlite3_result_int64(pCtx, p->aStat[p->iCol].nOcc);
+ break;
+
+ default: /* languageid */
+ assert( iCol==4 );
+ sqlite3_result_int(pCtx, p->iLangid);
+ break;
}
return SQLITE_OK;
@@ -119554,6 +149601,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitAux(sqlite3 *db){
** syntax is relatively simple, the whole tokenizer/parser system is
** hand-coded.
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/*
@@ -119630,7 +149678,9 @@ SQLITE_API int sqlite3_fts3_enable_parentheses = 0;
typedef struct ParseContext ParseContext;
struct ParseContext {
sqlite3_tokenizer *pTokenizer; /* Tokenizer module */
+ int iLangid; /* Language id used with tokenizer */
const char **azCol; /* Array of column names for fts3 table */
+ int bFts4; /* True to allow FTS4-only syntax */
int nCol; /* Number of entries in azCol[] */
int iDefaultCol; /* Default column to query */
int isNot; /* True if getNextNode() sees a unary - */
@@ -119642,7 +149692,7 @@ struct ParseContext {
** This function is equivalent to the standard isspace() function.
**
** The standard isspace() can be awkward to use safely, because although it
-** is defined to accept an argument of type int, its behaviour when passed
+** is defined to accept an argument of type int, its behavior when passed
** an integer that falls outside of the range of the unsigned char type
** is undefined (and sometimes, "undefined" means segfault). This wrapper
** is defined to accept an argument of type char, and always returns 0 for
@@ -119664,6 +149714,38 @@ static void *fts3MallocZero(int nByte){
return pRet;
}
+SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer(
+ sqlite3_tokenizer *pTokenizer,
+ int iLangid,
+ const char *z,
+ int n,
+ sqlite3_tokenizer_cursor **ppCsr
+){
+ sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
+ sqlite3_tokenizer_cursor *pCsr = 0;
+ int rc;
+
+ rc = pModule->xOpen(pTokenizer, z, n, &pCsr);
+ assert( rc==SQLITE_OK || pCsr==0 );
+ if( rc==SQLITE_OK ){
+ pCsr->pTokenizer = pTokenizer;
+ if( pModule->iVersion>=1 ){
+ rc = pModule->xLanguageid(pCsr, iLangid);
+ if( rc!=SQLITE_OK ){
+ pModule->xClose(pCsr);
+ pCsr = 0;
+ }
+ }
+ }
+ *ppCsr = pCsr;
+ return rc;
+}
+
+/*
+** Function getNextNode(), which is called by fts3ExprParse(), may itself
+** call fts3ExprParse(). So this forward declaration is required.
+*/
+static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
/*
** Extract the next token from buffer z (length n) using the tokenizer
@@ -119689,17 +149771,22 @@ static int getNextToken(
int rc;
sqlite3_tokenizer_cursor *pCursor;
Fts3Expr *pRet = 0;
- int nConsumed = 0;
+ int i = 0;
+
+ /* Set variable i to the maximum number of bytes of input to tokenize. */
+ for(i=0; i<n; i++){
+ if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
+ if( z[i]=='"' ) break;
+ }
- rc = pModule->xOpen(pTokenizer, z, n, &pCursor);
+ *pnConsumed = i;
+ rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor);
if( rc==SQLITE_OK ){
const char *zToken;
- int nToken, iStart, iEnd, iPosition;
+ int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
int nByte; /* total space to allocate */
- pCursor->pTokenizer = pTokenizer;
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
-
if( rc==SQLITE_OK ){
nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
pRet = (Fts3Expr *)fts3MallocZero(nByte);
@@ -119718,17 +149805,30 @@ static int getNextToken(
pRet->pPhrase->aToken[0].isPrefix = 1;
iEnd++;
}
- if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){
- pParse->isNot = 1;
+
+ while( 1 ){
+ if( !sqlite3_fts3_enable_parentheses
+ && iStart>0 && z[iStart-1]=='-'
+ ){
+ pParse->isNot = 1;
+ iStart--;
+ }else if( pParse->bFts4 && iStart>0 && z[iStart-1]=='^' ){
+ pRet->pPhrase->aToken[0].bFirst = 1;
+ iStart--;
+ }else{
+ break;
+ }
}
+
}
- nConsumed = iEnd;
+ *pnConsumed = iEnd;
+ }else if( i && rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
}
pModule->xClose(pCursor);
}
- *pnConsumed = nConsumed;
*ppExpr = pRet;
return rc;
}
@@ -119793,13 +149893,13 @@ static int getNextString(
** appends buffer zTemp to buffer p, and fills in the Fts3Expr and Fts3Phrase
** structures.
*/
- rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor);
+ rc = sqlite3Fts3OpenTokenizer(
+ pTokenizer, pParse->iLangid, zInput, nInput, &pCursor);
if( rc==SQLITE_OK ){
int ii;
- pCursor->pTokenizer = pTokenizer;
for(ii=0; rc==SQLITE_OK; ii++){
const char *zByte;
- int nByte, iBegin, iEnd, iPos;
+ int nByte = 0, iBegin = 0, iEnd = 0, iPos = 0;
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
Fts3PhraseToken *pToken;
@@ -119819,6 +149919,7 @@ static int getNextString(
pToken->n = nByte;
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
+ pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
nToken = ii+1;
}
}
@@ -119840,8 +149941,12 @@ static int getNextString(
p->pPhrase->nToken = nToken;
zBuf = (char *)&p->pPhrase->aToken[nToken];
- memcpy(zBuf, zTemp, nTemp);
- sqlite3_free(zTemp);
+ if( zTemp ){
+ memcpy(zBuf, zTemp, nTemp);
+ sqlite3_free(zTemp);
+ }else{
+ assert( nTemp==0 );
+ }
for(jj=0; jj<p->pPhrase->nToken; jj++){
p->pPhrase->aToken[jj].z = zBuf;
@@ -119864,12 +149969,6 @@ no_mem:
}
/*
-** Function getNextNode(), which is called by fts3ExprParse(), may itself
-** call fts3ExprParse(). So this forward declaration is required.
-*/
-static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
-
-/*
** The output variable *ppExpr is populated with an allocated Fts3Expr
** structure, or set to 0 if the end of the input buffer is reached.
**
@@ -119965,27 +150064,6 @@ static int getNextNode(
}
}
- /* Check for an open bracket. */
- if( sqlite3_fts3_enable_parentheses ){
- if( *zInput=='(' ){
- int nConsumed;
- pParse->nNest++;
- rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed);
- if( rc==SQLITE_OK && !*ppExpr ){
- rc = SQLITE_DONE;
- }
- *pnConsumed = (int)((zInput - z) + 1 + nConsumed);
- return rc;
- }
-
- /* Check for a close bracket. */
- if( *zInput==')' ){
- pParse->nNest--;
- *pnConsumed = (int)((zInput - z) + 1);
- return SQLITE_DONE;
- }
- }
-
/* See if we are dealing with a quoted phrase. If this is the case, then
** search for the closing quote and pass the whole string to getNextString()
** for processing. This is easy to do, as fts3 has no syntax for escaping
@@ -120000,6 +150078,21 @@ static int getNextNode(
return getNextString(pParse, &zInput[1], ii-1, ppExpr);
}
+ if( sqlite3_fts3_enable_parentheses ){
+ if( *zInput=='(' ){
+ int nConsumed = 0;
+ pParse->nNest++;
+ rc = fts3ExprParse(pParse, zInput+1, nInput-1, ppExpr, &nConsumed);
+ if( rc==SQLITE_OK && !*ppExpr ){ rc = SQLITE_DONE; }
+ *pnConsumed = (int)(zInput - z) + 1 + nConsumed;
+ return rc;
+ }else if( *zInput==')' ){
+ pParse->nNest--;
+ *pnConsumed = (int)((zInput - z) + 1);
+ *ppExpr = 0;
+ return SQLITE_DONE;
+ }
+ }
/* If control flows to this point, this must be a regular token, or
** the end of the input. Read a regular token using the sqlite3_tokenizer
@@ -120118,94 +150211,100 @@ static int fts3ExprParse(
while( rc==SQLITE_OK ){
Fts3Expr *p = 0;
int nByte = 0;
+
rc = getNextNode(pParse, zIn, nIn, &p, &nByte);
+ assert( nByte>0 || (rc!=SQLITE_OK && p==0) );
if( rc==SQLITE_OK ){
- int isPhrase;
-
- if( !sqlite3_fts3_enable_parentheses
- && p->eType==FTSQUERY_PHRASE && pParse->isNot
- ){
- /* Create an implicit NOT operator. */
- Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
- if( !pNot ){
- sqlite3Fts3ExprFree(p);
- rc = SQLITE_NOMEM;
- goto exprparse_out;
- }
- pNot->eType = FTSQUERY_NOT;
- pNot->pRight = p;
- if( pNotBranch ){
- pNot->pLeft = pNotBranch;
- }
- pNotBranch = pNot;
- p = pPrev;
- }else{
- int eType = p->eType;
- isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
+ if( p ){
+ int isPhrase;
- /* The isRequirePhrase variable is set to true if a phrase or
- ** an expression contained in parenthesis is required. If a
- ** binary operator (AND, OR, NOT or NEAR) is encounted when
- ** isRequirePhrase is set, this is a syntax error.
- */
- if( !isPhrase && isRequirePhrase ){
- sqlite3Fts3ExprFree(p);
- rc = SQLITE_ERROR;
- goto exprparse_out;
- }
-
- if( isPhrase && !isRequirePhrase ){
- /* Insert an implicit AND operator. */
- Fts3Expr *pAnd;
- assert( pRet && pPrev );
- pAnd = fts3MallocZero(sizeof(Fts3Expr));
- if( !pAnd ){
+ if( !sqlite3_fts3_enable_parentheses
+ && p->eType==FTSQUERY_PHRASE && pParse->isNot
+ ){
+ /* Create an implicit NOT operator. */
+ Fts3Expr *pNot = fts3MallocZero(sizeof(Fts3Expr));
+ if( !pNot ){
sqlite3Fts3ExprFree(p);
rc = SQLITE_NOMEM;
goto exprparse_out;
}
- pAnd->eType = FTSQUERY_AND;
- insertBinaryOperator(&pRet, pPrev, pAnd);
- pPrev = pAnd;
- }
+ pNot->eType = FTSQUERY_NOT;
+ pNot->pRight = p;
+ p->pParent = pNot;
+ if( pNotBranch ){
+ pNot->pLeft = pNotBranch;
+ pNotBranch->pParent = pNot;
+ }
+ pNotBranch = pNot;
+ p = pPrev;
+ }else{
+ int eType = p->eType;
+ isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
- /* This test catches attempts to make either operand of a NEAR
- ** operator something other than a phrase. For example, either of
- ** the following:
- **
- ** (bracketed expression) NEAR phrase
- ** phrase NEAR (bracketed expression)
- **
- ** Return an error in either case.
- */
- if( pPrev && (
+ /* The isRequirePhrase variable is set to true if a phrase or
+ ** an expression contained in parenthesis is required. If a
+ ** binary operator (AND, OR, NOT or NEAR) is encounted when
+ ** isRequirePhrase is set, this is a syntax error.
+ */
+ if( !isPhrase && isRequirePhrase ){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_ERROR;
+ goto exprparse_out;
+ }
+
+ if( isPhrase && !isRequirePhrase ){
+ /* Insert an implicit AND operator. */
+ Fts3Expr *pAnd;
+ assert( pRet && pPrev );
+ pAnd = fts3MallocZero(sizeof(Fts3Expr));
+ if( !pAnd ){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_NOMEM;
+ goto exprparse_out;
+ }
+ pAnd->eType = FTSQUERY_AND;
+ insertBinaryOperator(&pRet, pPrev, pAnd);
+ pPrev = pAnd;
+ }
+
+ /* This test catches attempts to make either operand of a NEAR
+ ** operator something other than a phrase. For example, either of
+ ** the following:
+ **
+ ** (bracketed expression) NEAR phrase
+ ** phrase NEAR (bracketed expression)
+ **
+ ** Return an error in either case.
+ */
+ if( pPrev && (
(eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE)
|| (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR)
- )){
- sqlite3Fts3ExprFree(p);
- rc = SQLITE_ERROR;
- goto exprparse_out;
- }
-
- if( isPhrase ){
- if( pRet ){
- assert( pPrev && pPrev->pLeft && pPrev->pRight==0 );
- pPrev->pRight = p;
- p->pParent = pPrev;
+ )){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_ERROR;
+ goto exprparse_out;
+ }
+
+ if( isPhrase ){
+ if( pRet ){
+ assert( pPrev && pPrev->pLeft && pPrev->pRight==0 );
+ pPrev->pRight = p;
+ p->pParent = pPrev;
+ }else{
+ pRet = p;
+ }
}else{
- pRet = p;
+ insertBinaryOperator(&pRet, pPrev, p);
}
- }else{
- insertBinaryOperator(&pRet, pPrev, p);
+ isRequirePhrase = !isPhrase;
}
- isRequirePhrase = !isPhrase;
+ pPrev = p;
}
assert( nByte>0 );
}
assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) );
nIn -= nByte;
zIn += nByte;
- pPrev = p;
}
if( rc==SQLITE_DONE && pRet && isRequirePhrase ){
@@ -120223,6 +150322,7 @@ static int fts3ExprParse(
pIter = pIter->pLeft;
}
pIter->pLeft = pRet;
+ pRet->pParent = pIter;
pRet = pNotBranch;
}
}
@@ -120240,6 +150340,249 @@ exprparse_out:
}
/*
+** Return SQLITE_ERROR if the maximum depth of the expression tree passed
+** as the only argument is more than nMaxDepth.
+*/
+static int fts3ExprCheckDepth(Fts3Expr *p, int nMaxDepth){
+ int rc = SQLITE_OK;
+ if( p ){
+ if( nMaxDepth<0 ){
+ rc = SQLITE_TOOBIG;
+ }else{
+ rc = fts3ExprCheckDepth(p->pLeft, nMaxDepth-1);
+ if( rc==SQLITE_OK ){
+ rc = fts3ExprCheckDepth(p->pRight, nMaxDepth-1);
+ }
+ }
+ }
+ return rc;
+}
+
+/*
+** This function attempts to transform the expression tree at (*pp) to
+** an equivalent but more balanced form. The tree is modified in place.
+** If successful, SQLITE_OK is returned and (*pp) set to point to the
+** new root expression node.
+**
+** nMaxDepth is the maximum allowable depth of the balanced sub-tree.
+**
+** Otherwise, if an error occurs, an SQLite error code is returned and
+** expression (*pp) freed.
+*/
+static int fts3ExprBalance(Fts3Expr **pp, int nMaxDepth){
+ int rc = SQLITE_OK; /* Return code */
+ Fts3Expr *pRoot = *pp; /* Initial root node */
+ Fts3Expr *pFree = 0; /* List of free nodes. Linked by pParent. */
+ int eType = pRoot->eType; /* Type of node in this tree */
+
+ if( nMaxDepth==0 ){
+ rc = SQLITE_ERROR;
+ }
+
+ if( rc==SQLITE_OK ){
+ if( (eType==FTSQUERY_AND || eType==FTSQUERY_OR) ){
+ Fts3Expr **apLeaf;
+ apLeaf = (Fts3Expr **)sqlite3_malloc(sizeof(Fts3Expr *) * nMaxDepth);
+ if( 0==apLeaf ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(apLeaf, 0, sizeof(Fts3Expr *) * nMaxDepth);
+ }
+
+ if( rc==SQLITE_OK ){
+ int i;
+ Fts3Expr *p;
+
+ /* Set $p to point to the left-most leaf in the tree of eType nodes. */
+ for(p=pRoot; p->eType==eType; p=p->pLeft){
+ assert( p->pParent==0 || p->pParent->pLeft==p );
+ assert( p->pLeft && p->pRight );
+ }
+
+ /* This loop runs once for each leaf in the tree of eType nodes. */
+ while( 1 ){
+ int iLvl;
+ Fts3Expr *pParent = p->pParent; /* Current parent of p */
+
+ assert( pParent==0 || pParent->pLeft==p );
+ p->pParent = 0;
+ if( pParent ){
+ pParent->pLeft = 0;
+ }else{
+ pRoot = 0;
+ }
+ rc = fts3ExprBalance(&p, nMaxDepth-1);
+ if( rc!=SQLITE_OK ) break;
+
+ for(iLvl=0; p && iLvl<nMaxDepth; iLvl++){
+ if( apLeaf[iLvl]==0 ){
+ apLeaf[iLvl] = p;
+ p = 0;
+ }else{
+ assert( pFree );
+ pFree->pLeft = apLeaf[iLvl];
+ pFree->pRight = p;
+ pFree->pLeft->pParent = pFree;
+ pFree->pRight->pParent = pFree;
+
+ p = pFree;
+ pFree = pFree->pParent;
+ p->pParent = 0;
+ apLeaf[iLvl] = 0;
+ }
+ }
+ if( p ){
+ sqlite3Fts3ExprFree(p);
+ rc = SQLITE_TOOBIG;
+ break;
+ }
+
+ /* If that was the last leaf node, break out of the loop */
+ if( pParent==0 ) break;
+
+ /* Set $p to point to the next leaf in the tree of eType nodes */
+ for(p=pParent->pRight; p->eType==eType; p=p->pLeft);
+
+ /* Remove pParent from the original tree. */
+ assert( pParent->pParent==0 || pParent->pParent->pLeft==pParent );
+ pParent->pRight->pParent = pParent->pParent;
+ if( pParent->pParent ){
+ pParent->pParent->pLeft = pParent->pRight;
+ }else{
+ assert( pParent==pRoot );
+ pRoot = pParent->pRight;
+ }
+
+ /* Link pParent into the free node list. It will be used as an
+ ** internal node of the new tree. */
+ pParent->pParent = pFree;
+ pFree = pParent;
+ }
+
+ if( rc==SQLITE_OK ){
+ p = 0;
+ for(i=0; i<nMaxDepth; i++){
+ if( apLeaf[i] ){
+ if( p==0 ){
+ p = apLeaf[i];
+ p->pParent = 0;
+ }else{
+ assert( pFree!=0 );
+ pFree->pRight = p;
+ pFree->pLeft = apLeaf[i];
+ pFree->pLeft->pParent = pFree;
+ pFree->pRight->pParent = pFree;
+
+ p = pFree;
+ pFree = pFree->pParent;
+ p->pParent = 0;
+ }
+ }
+ }
+ pRoot = p;
+ }else{
+ /* An error occurred. Delete the contents of the apLeaf[] array
+ ** and pFree list. Everything else is cleaned up by the call to
+ ** sqlite3Fts3ExprFree(pRoot) below. */
+ Fts3Expr *pDel;
+ for(i=0; i<nMaxDepth; i++){
+ sqlite3Fts3ExprFree(apLeaf[i]);
+ }
+ while( (pDel=pFree)!=0 ){
+ pFree = pDel->pParent;
+ sqlite3_free(pDel);
+ }
+ }
+
+ assert( pFree==0 );
+ sqlite3_free( apLeaf );
+ }
+ }else if( eType==FTSQUERY_NOT ){
+ Fts3Expr *pLeft = pRoot->pLeft;
+ Fts3Expr *pRight = pRoot->pRight;
+
+ pRoot->pLeft = 0;
+ pRoot->pRight = 0;
+ pLeft->pParent = 0;
+ pRight->pParent = 0;
+
+ rc = fts3ExprBalance(&pLeft, nMaxDepth-1);
+ if( rc==SQLITE_OK ){
+ rc = fts3ExprBalance(&pRight, nMaxDepth-1);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3Fts3ExprFree(pRight);
+ sqlite3Fts3ExprFree(pLeft);
+ }else{
+ assert( pLeft && pRight );
+ pRoot->pLeft = pLeft;
+ pLeft->pParent = pRoot;
+ pRoot->pRight = pRight;
+ pRight->pParent = pRoot;
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3Fts3ExprFree(pRoot);
+ pRoot = 0;
+ }
+ *pp = pRoot;
+ return rc;
+}
+
+/*
+** This function is similar to sqlite3Fts3ExprParse(), with the following
+** differences:
+**
+** 1. It does not do expression rebalancing.
+** 2. It does not check that the expression does not exceed the
+** maximum allowable depth.
+** 3. Even if it fails, *ppExpr may still be set to point to an
+** expression tree. It should be deleted using sqlite3Fts3ExprFree()
+** in this case.
+*/
+static int fts3ExprParseUnbalanced(
+ sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
+ int iLangid, /* Language id for tokenizer */
+ char **azCol, /* Array of column names for fts3 table */
+ int bFts4, /* True to allow FTS4-only syntax */
+ int nCol, /* Number of entries in azCol[] */
+ int iDefaultCol, /* Default column to query */
+ const char *z, int n, /* Text of MATCH query */
+ Fts3Expr **ppExpr /* OUT: Parsed query structure */
+){
+ int nParsed;
+ int rc;
+ ParseContext sParse;
+
+ memset(&sParse, 0, sizeof(ParseContext));
+ sParse.pTokenizer = pTokenizer;
+ sParse.iLangid = iLangid;
+ sParse.azCol = (const char **)azCol;
+ sParse.nCol = nCol;
+ sParse.iDefaultCol = iDefaultCol;
+ sParse.bFts4 = bFts4;
+ if( z==0 ){
+ *ppExpr = 0;
+ return SQLITE_OK;
+ }
+ if( n<0 ){
+ n = (int)strlen(z);
+ }
+ rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed);
+ assert( rc==SQLITE_OK || *ppExpr==0 );
+
+ /* Check for mismatched parenthesis */
+ if( rc==SQLITE_OK && sParse.nNest ){
+ rc = SQLITE_ERROR;
+ }
+
+ return rc;
+}
+
+/*
** Parameters z and n contain a pointer to and length of a buffer containing
** an fts3 query expression, respectively. This function attempts to parse the
** query expression and create a tree of Fts3Expr structures representing the
@@ -120265,50 +150608,80 @@ exprparse_out:
*/
SQLITE_PRIVATE int sqlite3Fts3ExprParse(
sqlite3_tokenizer *pTokenizer, /* Tokenizer module */
+ int iLangid, /* Language id for tokenizer */
char **azCol, /* Array of column names for fts3 table */
+ int bFts4, /* True to allow FTS4-only syntax */
int nCol, /* Number of entries in azCol[] */
int iDefaultCol, /* Default column to query */
const char *z, int n, /* Text of MATCH query */
- Fts3Expr **ppExpr /* OUT: Parsed query structure */
+ Fts3Expr **ppExpr, /* OUT: Parsed query structure */
+ char **pzErr /* OUT: Error message (sqlite3_malloc) */
){
- int nParsed;
- int rc;
- ParseContext sParse;
- sParse.pTokenizer = pTokenizer;
- sParse.azCol = (const char **)azCol;
- sParse.nCol = nCol;
- sParse.iDefaultCol = iDefaultCol;
- sParse.nNest = 0;
- if( z==0 ){
- *ppExpr = 0;
- return SQLITE_OK;
- }
- if( n<0 ){
- n = (int)strlen(z);
+ int rc = fts3ExprParseUnbalanced(
+ pTokenizer, iLangid, azCol, bFts4, nCol, iDefaultCol, z, n, ppExpr
+ );
+
+ /* Rebalance the expression. And check that its depth does not exceed
+ ** SQLITE_FTS3_MAX_EXPR_DEPTH. */
+ if( rc==SQLITE_OK && *ppExpr ){
+ rc = fts3ExprBalance(ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
+ if( rc==SQLITE_OK ){
+ rc = fts3ExprCheckDepth(*ppExpr, SQLITE_FTS3_MAX_EXPR_DEPTH);
+ }
}
- rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed);
- /* Check for mismatched parenthesis */
- if( rc==SQLITE_OK && sParse.nNest ){
- rc = SQLITE_ERROR;
+ if( rc!=SQLITE_OK ){
sqlite3Fts3ExprFree(*ppExpr);
*ppExpr = 0;
+ if( rc==SQLITE_TOOBIG ){
+ sqlite3Fts3ErrMsg(pzErr,
+ "FTS expression tree is too large (maximum depth %d)",
+ SQLITE_FTS3_MAX_EXPR_DEPTH
+ );
+ rc = SQLITE_ERROR;
+ }else if( rc==SQLITE_ERROR ){
+ sqlite3Fts3ErrMsg(pzErr, "malformed MATCH expression: [%s]", z);
+ }
}
return rc;
}
/*
+** Free a single node of an expression tree.
+*/
+static void fts3FreeExprNode(Fts3Expr *p){
+ assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 );
+ sqlite3Fts3EvalPhraseCleanup(p->pPhrase);
+ sqlite3_free(p->aMI);
+ sqlite3_free(p);
+}
+
+/*
** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse().
+**
+** This function would be simpler if it recursively called itself. But
+** that would mean passing a sufficiently large expression to ExprParse()
+** could cause a stack overflow.
*/
-SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){
- if( p ){
- assert( p->eType==FTSQUERY_PHRASE || p->pPhrase==0 );
- sqlite3Fts3ExprFree(p->pLeft);
- sqlite3Fts3ExprFree(p->pRight);
- sqlite3Fts3EvalPhraseCleanup(p->pPhrase);
- sqlite3_free(p->aMI);
- sqlite3_free(p);
+SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *pDel){
+ Fts3Expr *p;
+ assert( pDel==0 || pDel->pParent==0 );
+ for(p=pDel; p && (p->pLeft||p->pRight); p=(p->pLeft ? p->pLeft : p->pRight)){
+ assert( p->pParent==0 || p==p->pParent->pRight || p==p->pParent->pLeft );
+ }
+ while( p ){
+ Fts3Expr *pParent = p->pParent;
+ fts3FreeExprNode(p);
+ if( pParent && p==pParent->pLeft && pParent->pRight ){
+ p = pParent->pRight;
+ while( p && (p->pLeft || p->pRight) ){
+ assert( p==p->pParent->pRight || p==p->pParent->pLeft );
+ p = (p->pLeft ? p->pLeft : p->pRight);
+ }
+ }else{
+ p = pParent;
+ }
}
}
@@ -120360,6 +150733,9 @@ static int queryTestTokenizer(
** the returned expression text and then freed using sqlite3_free().
*/
static char *exprToString(Fts3Expr *pExpr, char *zBuf){
+ if( pExpr==0 ){
+ return sqlite3_mprintf("");
+ }
switch( pExpr->eType ){
case FTSQUERY_PHRASE: {
Fts3Phrase *pPhrase = pExpr->pPhrase;
@@ -120467,10 +150843,21 @@ static void fts3ExprTest(
azCol[ii] = (char *)sqlite3_value_text(argv[ii+2]);
}
- rc = sqlite3Fts3ExprParse(
- pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr
- );
+ if( sqlite3_user_data(context) ){
+ char *zDummy = 0;
+ rc = sqlite3Fts3ExprParse(
+ pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr, &zDummy
+ );
+ assert( rc==SQLITE_OK || pExpr==0 );
+ sqlite3_free(zDummy);
+ }else{
+ rc = fts3ExprParseUnbalanced(
+ pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
+ );
+ }
+
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
+ sqlite3Fts3ExprFree(pExpr);
sqlite3_result_error(context, "Error parsing expression", -1);
}else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
sqlite3_result_error_nomem(context);
@@ -120493,9 +150880,15 @@ exprtest_out:
** with database connection db.
*/
SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3* db){
- return sqlite3_create_function(
+ int rc = sqlite3_create_function(
db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0
);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(db, "fts3_exprtest_rebalance",
+ -1, SQLITE_UTF8, (void *)1, fts3ExprTest, 0, 0
+ );
+ }
+ return rc;
}
#endif
@@ -120528,12 +150921,14 @@ SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3* db){
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
/* #include <stdlib.h> */
/* #include <string.h> */
+/* #include "fts3_hash.h" */
/*
** Malloc and Free functions
@@ -120599,13 +150994,13 @@ SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash *pH){
*/
static int fts3StrHash(const void *pKey, int nKey){
const char *z = (const char *)pKey;
- int h = 0;
+ unsigned h = 0;
if( nKey<=0 ) nKey = (int) strlen(z);
while( nKey > 0 ){
h = (h<<3) ^ h ^ *z++;
nKey--;
}
- return h & 0x7fffffff;
+ return (int)(h & 0x7fffffff);
}
static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){
if( n1!=n2 ) return 1;
@@ -120911,6 +151306,7 @@ SQLITE_PRIVATE void *sqlite3Fts3HashInsert(
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
@@ -120918,6 +151314,7 @@ SQLITE_PRIVATE void *sqlite3Fts3HashInsert(
/* #include <stdio.h> */
/* #include <string.h> */
+/* #include "fts3_tokenizer.h" */
/*
** Class derived from sqlite3_tokenizer
@@ -120927,7 +151324,7 @@ typedef struct porter_tokenizer {
} porter_tokenizer;
/*
-** Class derived from sqlit3_tokenizer_cursor
+** Class derived from sqlite3_tokenizer_cursor
*/
typedef struct porter_tokenizer_cursor {
sqlite3_tokenizer_cursor base;
@@ -121070,7 +151467,7 @@ static int isVowel(const char *z){
** by a consonant.
**
** In this routine z[] is in reverse order. So we are really looking
-** for an instance of of a consonant followed by a vowel.
+** for an instance of a consonant followed by a vowel.
*/
static int m_gt_0(const char *z){
while( isVowel(z) ){ z++; }
@@ -121290,12 +151687,14 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
/* Step 2 */
switch( z[1] ){
case 'a':
- stem(&z, "lanoita", "ate", m_gt_0) ||
- stem(&z, "lanoit", "tion", m_gt_0);
+ if( !stem(&z, "lanoita", "ate", m_gt_0) ){
+ stem(&z, "lanoit", "tion", m_gt_0);
+ }
break;
case 'c':
- stem(&z, "icne", "ence", m_gt_0) ||
- stem(&z, "icna", "ance", m_gt_0);
+ if( !stem(&z, "icne", "ence", m_gt_0) ){
+ stem(&z, "icna", "ance", m_gt_0);
+ }
break;
case 'e':
stem(&z, "rezi", "ize", m_gt_0);
@@ -121304,43 +151703,54 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
stem(&z, "igol", "log", m_gt_0);
break;
case 'l':
- stem(&z, "ilb", "ble", m_gt_0) ||
- stem(&z, "illa", "al", m_gt_0) ||
- stem(&z, "iltne", "ent", m_gt_0) ||
- stem(&z, "ile", "e", m_gt_0) ||
- stem(&z, "ilsuo", "ous", m_gt_0);
+ if( !stem(&z, "ilb", "ble", m_gt_0)
+ && !stem(&z, "illa", "al", m_gt_0)
+ && !stem(&z, "iltne", "ent", m_gt_0)
+ && !stem(&z, "ile", "e", m_gt_0)
+ ){
+ stem(&z, "ilsuo", "ous", m_gt_0);
+ }
break;
case 'o':
- stem(&z, "noitazi", "ize", m_gt_0) ||
- stem(&z, "noita", "ate", m_gt_0) ||
- stem(&z, "rota", "ate", m_gt_0);
+ if( !stem(&z, "noitazi", "ize", m_gt_0)
+ && !stem(&z, "noita", "ate", m_gt_0)
+ ){
+ stem(&z, "rota", "ate", m_gt_0);
+ }
break;
case 's':
- stem(&z, "msila", "al", m_gt_0) ||
- stem(&z, "ssenevi", "ive", m_gt_0) ||
- stem(&z, "ssenluf", "ful", m_gt_0) ||
- stem(&z, "ssensuo", "ous", m_gt_0);
+ if( !stem(&z, "msila", "al", m_gt_0)
+ && !stem(&z, "ssenevi", "ive", m_gt_0)
+ && !stem(&z, "ssenluf", "ful", m_gt_0)
+ ){
+ stem(&z, "ssensuo", "ous", m_gt_0);
+ }
break;
case 't':
- stem(&z, "itila", "al", m_gt_0) ||
- stem(&z, "itivi", "ive", m_gt_0) ||
- stem(&z, "itilib", "ble", m_gt_0);
+ if( !stem(&z, "itila", "al", m_gt_0)
+ && !stem(&z, "itivi", "ive", m_gt_0)
+ ){
+ stem(&z, "itilib", "ble", m_gt_0);
+ }
break;
}
/* Step 3 */
switch( z[0] ){
case 'e':
- stem(&z, "etaci", "ic", m_gt_0) ||
- stem(&z, "evita", "", m_gt_0) ||
- stem(&z, "ezila", "al", m_gt_0);
+ if( !stem(&z, "etaci", "ic", m_gt_0)
+ && !stem(&z, "evita", "", m_gt_0)
+ ){
+ stem(&z, "ezila", "al", m_gt_0);
+ }
break;
case 'i':
stem(&z, "itici", "ic", m_gt_0);
break;
case 'l':
- stem(&z, "laci", "ic", m_gt_0) ||
- stem(&z, "luf", "", m_gt_0);
+ if( !stem(&z, "laci", "ic", m_gt_0) ){
+ stem(&z, "luf", "", m_gt_0);
+ }
break;
case 's':
stem(&z, "ssen", "", m_gt_0);
@@ -121381,9 +151791,11 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
z += 3;
}
}else if( z[2]=='e' ){
- stem(&z, "tneme", "", m_gt_1) ||
- stem(&z, "tnem", "", m_gt_1) ||
- stem(&z, "tne", "", m_gt_1);
+ if( !stem(&z, "tneme", "", m_gt_1)
+ && !stem(&z, "tnem", "", m_gt_1)
+ ){
+ stem(&z, "tne", "", m_gt_1);
+ }
}
}
break;
@@ -121402,8 +151814,9 @@ static void porter_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){
}
break;
case 't':
- stem(&z, "eta", "", m_gt_1) ||
- stem(&z, "iti", "", m_gt_1);
+ if( !stem(&z, "eta", "", m_gt_1) ){
+ stem(&z, "iti", "", m_gt_1);
+ }
break;
case 'u':
if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){
@@ -121517,6 +151930,7 @@ static const sqlite3_tokenizer_module porterTokenizerModule = {
porterOpen,
porterClose,
porterNext,
+ 0
};
/*
@@ -121558,12 +151972,25 @@ SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
/* #include <string.h> */
/*
+** Return true if the two-argument version of fts3_tokenizer()
+** has been activated via a prior call to sqlite3_db_config(db,
+** SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 0);
+*/
+static int fts3TokenizerEnabled(sqlite3_context *context){
+ sqlite3 *db = sqlite3_context_db_handle(context);
+ int isEnabled = 0;
+ sqlite3_db_config(db,SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER,-1,&isEnabled);
+ return isEnabled;
+}
+
+/*
** Implementation of the SQL scalar function for accessing the underlying
** hash table. This function may be called as follows:
**
@@ -121583,7 +152010,7 @@ SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(
** is a blob containing the pointer stored as the hash data corresponding
** to string <key-name> (after the hash-table is updated, if applicable).
*/
-static void scalarFunc(
+static void fts3TokenizerFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
@@ -121601,20 +152028,26 @@ static void scalarFunc(
nName = sqlite3_value_bytes(argv[0])+1;
if( argc==2 ){
- void *pOld;
- int n = sqlite3_value_bytes(argv[1]);
- if( n!=sizeof(pPtr) ){
- sqlite3_result_error(context, "argument type mismatch", -1);
- return;
- }
- pPtr = *(void **)sqlite3_value_blob(argv[1]);
- pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr);
- if( pOld==pPtr ){
- sqlite3_result_error(context, "out of memory", -1);
+ if( fts3TokenizerEnabled(context) ){
+ void *pOld;
+ int n = sqlite3_value_bytes(argv[1]);
+ if( zName==0 || n!=sizeof(pPtr) ){
+ sqlite3_result_error(context, "argument type mismatch", -1);
+ return;
+ }
+ pPtr = *(void **)sqlite3_value_blob(argv[1]);
+ pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr);
+ if( pOld==pPtr ){
+ sqlite3_result_error(context, "out of memory", -1);
+ }
+ }else{
+ sqlite3_result_error(context, "fts3tokenize disabled", -1);
return;
}
}else{
- pPtr = sqlite3Fts3HashFind(pHash, zName, nName);
+ if( zName ){
+ pPtr = sqlite3Fts3HashFind(pHash, zName, nName);
+ }
if( !pPtr ){
char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName);
sqlite3_result_error(context, zErr, -1);
@@ -121622,7 +152055,6 @@ static void scalarFunc(
return;
}
}
-
sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT);
}
@@ -121695,12 +152127,16 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
zEnd = &zCopy[strlen(zCopy)];
z = (char *)sqlite3Fts3NextToken(zCopy, &n);
+ if( z==0 ){
+ assert( n==0 );
+ z = zCopy;
+ }
z[n] = '\0';
sqlite3Fts3Dequote(z);
m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash,z,(int)strlen(z)+1);
if( !m ){
- *pzErr = sqlite3_mprintf("unknown tokenizer: %s", z);
+ sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer: %s", z);
rc = SQLITE_ERROR;
}else{
char const **aArg = 0;
@@ -121723,7 +152159,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
rc = m->xCreate(iArg, aArg, ppTok);
assert( rc!=SQLITE_OK || *ppTok );
if( rc!=SQLITE_OK ){
- *pzErr = sqlite3_mprintf("unknown tokenizer");
+ sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer");
}else{
(*ppTok)->pModule = m;
}
@@ -121737,16 +152173,19 @@ SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(
#ifdef SQLITE_TEST
-/* #include <tcl.h> */
+#if defined(INCLUDE_SQLITE_TCL_H)
+# include "sqlite_tcl.h"
+#else
+# include "tcl.h"
+#endif
/* #include <string.h> */
/*
** Implementation of a special SQL scalar function for testing tokenizers
** designed to be used in concert with the Tcl testing framework. This
-** function must be called with two arguments:
+** function must be called with two or more arguments:
**
-** SELECT <function-name>(<key-name>, <input-string>);
-** SELECT <function-name>(<key-name>, <pointer>);
+** SELECT <function-name>(<key-name>, ..., <input-string>);
**
** where <function-name> is the name passed as the second argument
** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer')
@@ -121783,50 +152222,53 @@ static void testFunc(
const char *zInput;
int nInput;
- const char *zArg = 0;
+ const char *azArg[64];
const char *zToken;
- int nToken;
- int iStart;
- int iEnd;
- int iPos;
+ int nToken = 0;
+ int iStart = 0;
+ int iEnd = 0;
+ int iPos = 0;
+ int i;
Tcl_Obj *pRet;
- assert( argc==2 || argc==3 );
+ if( argc<2 ){
+ sqlite3_result_error(context, "insufficient arguments", -1);
+ return;
+ }
nName = sqlite3_value_bytes(argv[0]);
zName = (const char *)sqlite3_value_text(argv[0]);
nInput = sqlite3_value_bytes(argv[argc-1]);
zInput = (const char *)sqlite3_value_text(argv[argc-1]);
- if( argc==3 ){
- zArg = (const char *)sqlite3_value_text(argv[1]);
- }
-
pHash = (Fts3Hash *)sqlite3_user_data(context);
p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1);
if( !p ){
- char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName);
- sqlite3_result_error(context, zErr, -1);
- sqlite3_free(zErr);
+ char *zErr2 = sqlite3_mprintf("unknown tokenizer: %s", zName);
+ sqlite3_result_error(context, zErr2, -1);
+ sqlite3_free(zErr2);
return;
}
pRet = Tcl_NewObj();
Tcl_IncrRefCount(pRet);
- if( SQLITE_OK!=p->xCreate(zArg ? 1 : 0, &zArg, &pTokenizer) ){
+ for(i=1; i<argc-1; i++){
+ azArg[i-1] = (const char *)sqlite3_value_text(argv[i]);
+ }
+
+ if( SQLITE_OK!=p->xCreate(argc-2, azArg, &pTokenizer) ){
zErr = "error in xCreate()";
goto finish;
}
pTokenizer->pModule = p;
- if( SQLITE_OK!=p->xOpen(pTokenizer, zInput, nInput, &pCsr) ){
+ if( sqlite3Fts3OpenTokenizer(pTokenizer, 0, zInput, nInput, &pCsr) ){
zErr = "error in xOpen()";
goto finish;
}
- pCsr->pTokenizer = pTokenizer;
while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){
Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos));
@@ -121876,6 +152318,7 @@ int registerTokenizer(
return sqlite3_finalize(pStmt);
}
+
static
int queryTokenizer(
sqlite3 *db,
@@ -121946,11 +152389,13 @@ static void intTestFunc(
assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") );
/* Test the storage function */
- rc = registerTokenizer(db, "nosuchtokenizer", p1);
- assert( rc==SQLITE_OK );
- rc = queryTokenizer(db, "nosuchtokenizer", &p2);
- assert( rc==SQLITE_OK );
- assert( p2==p1 );
+ if( fts3TokenizerEnabled(context) ){
+ rc = registerTokenizer(db, "nosuchtokenizer", p1);
+ assert( rc==SQLITE_OK );
+ rc = queryTokenizer(db, "nosuchtokenizer", &p2);
+ assert( rc==SQLITE_OK );
+ assert( p2==p1 );
+ }
sqlite3_result_text(context, "ok", -1, SQLITE_STATIC);
}
@@ -121960,13 +152405,13 @@ static void intTestFunc(
/*
** Set up SQL objects in database db used to access the contents of
** the hash table pointed to by argument pHash. The hash table must
-** been initialised to use string keys, and to take a private copy
+** been initialized to use string keys, and to take a private copy
** of the key when a value is inserted. i.e. by a call similar to:
**
** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1);
**
** This function adds a scalar function (see header comment above
-** scalarFunc() in this file for details) and, if ENABLE_TABLE is
+** fts3TokenizerFunc() in this file for details) and, if ENABLE_TABLE is
** defined at compilation time, a temporary virtual table (see header
** comment above struct HashTableVtab) to the database schema. Both
** provide read/write access to the contents of *pHash.
@@ -121995,17 +152440,14 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
#endif
if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0);
+ rc = sqlite3_create_function(db, zName, 1, any, p, fts3TokenizerFunc, 0, 0);
}
if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0);
+ rc = sqlite3_create_function(db, zName, 2, any, p, fts3TokenizerFunc, 0, 0);
}
#ifdef SQLITE_TEST
if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0);
- }
- if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0);
+ rc = sqlite3_create_function(db, zTest, -1, any, p, testFunc, 0, 0);
}
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0);
@@ -122048,6 +152490,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
** * The FTS3 module is being built into the core of
** SQLite (in which case SQLITE_ENABLE_FTS3 is defined).
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <assert.h> */
@@ -122055,6 +152498,7 @@ SQLITE_PRIVATE int sqlite3Fts3InitHashTable(
/* #include <stdio.h> */
/* #include <string.h> */
+/* #include "fts3_tokenizer.h" */
typedef struct simple_tokenizer {
sqlite3_tokenizer base;
@@ -122242,6 +152686,7 @@ static const sqlite3_tokenizer_module simpleTokenizerModule = {
simpleOpen,
simpleClose,
simpleNext,
+ 0,
};
/*
@@ -122257,6 +152702,463 @@ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
/************** End of fts3_tokenizer1.c *************************************/
+/************** Begin file fts3_tokenize_vtab.c ******************************/
+/*
+** 2013 Apr 22
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains code for the "fts3tokenize" virtual table module.
+** An fts3tokenize virtual table is created as follows:
+**
+** CREATE VIRTUAL TABLE <tbl> USING fts3tokenize(
+** <tokenizer-name>, <arg-1>, ...
+** );
+**
+** The table created has the following schema:
+**
+** CREATE TABLE <tbl>(input, token, start, end, position)
+**
+** When queried, the query must include a WHERE clause of type:
+**
+** input = <string>
+**
+** The virtual table module tokenizes this <string>, using the FTS3
+** tokenizer specified by the arguments to the CREATE VIRTUAL TABLE
+** statement and returns one row for each token in the result. With
+** fields set as follows:
+**
+** input: Always set to a copy of <string>
+** token: A token from the input.
+** start: Byte offset of the token within the input <string>.
+** end: Byte offset of the byte immediately following the end of the
+** token within the input string.
+** pos: Token offset of token within input.
+**
+*/
+/* #include "fts3Int.h" */
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+/* #include <string.h> */
+/* #include <assert.h> */
+
+typedef struct Fts3tokTable Fts3tokTable;
+typedef struct Fts3tokCursor Fts3tokCursor;
+
+/*
+** Virtual table structure.
+*/
+struct Fts3tokTable {
+ sqlite3_vtab base; /* Base class used by SQLite core */
+ const sqlite3_tokenizer_module *pMod;
+ sqlite3_tokenizer *pTok;
+};
+
+/*
+** Virtual table cursor structure.
+*/
+struct Fts3tokCursor {
+ sqlite3_vtab_cursor base; /* Base class used by SQLite core */
+ char *zInput; /* Input string */
+ sqlite3_tokenizer_cursor *pCsr; /* Cursor to iterate through zInput */
+ int iRowid; /* Current 'rowid' value */
+ const char *zToken; /* Current 'token' value */
+ int nToken; /* Size of zToken in bytes */
+ int iStart; /* Current 'start' value */
+ int iEnd; /* Current 'end' value */
+ int iPos; /* Current 'pos' value */
+};
+
+/*
+** Query FTS for the tokenizer implementation named zName.
+*/
+static int fts3tokQueryTokenizer(
+ Fts3Hash *pHash,
+ const char *zName,
+ const sqlite3_tokenizer_module **pp,
+ char **pzErr
+){
+ sqlite3_tokenizer_module *p;
+ int nName = (int)strlen(zName);
+
+ p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1);
+ if( !p ){
+ sqlite3Fts3ErrMsg(pzErr, "unknown tokenizer: %s", zName);
+ return SQLITE_ERROR;
+ }
+
+ *pp = p;
+ return SQLITE_OK;
+}
+
+/*
+** The second argument, argv[], is an array of pointers to nul-terminated
+** strings. This function makes a copy of the array and strings into a
+** single block of memory. It then dequotes any of the strings that appear
+** to be quoted.
+**
+** If successful, output parameter *pazDequote is set to point at the
+** array of dequoted strings and SQLITE_OK is returned. The caller is
+** responsible for eventually calling sqlite3_free() to free the array
+** in this case. Or, if an error occurs, an SQLite error code is returned.
+** The final value of *pazDequote is undefined in this case.
+*/
+static int fts3tokDequoteArray(
+ int argc, /* Number of elements in argv[] */
+ const char * const *argv, /* Input array */
+ char ***pazDequote /* Output array */
+){
+ int rc = SQLITE_OK; /* Return code */
+ if( argc==0 ){
+ *pazDequote = 0;
+ }else{
+ int i;
+ int nByte = 0;
+ char **azDequote;
+
+ for(i=0; i<argc; i++){
+ nByte += (int)(strlen(argv[i]) + 1);
+ }
+
+ *pazDequote = azDequote = sqlite3_malloc(sizeof(char *)*argc + nByte);
+ if( azDequote==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ char *pSpace = (char *)&azDequote[argc];
+ for(i=0; i<argc; i++){
+ int n = (int)strlen(argv[i]);
+ azDequote[i] = pSpace;
+ memcpy(pSpace, argv[i], n+1);
+ sqlite3Fts3Dequote(pSpace);
+ pSpace += (n+1);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Schema of the tokenizer table.
+*/
+#define FTS3_TOK_SCHEMA "CREATE TABLE x(input, token, start, end, position)"
+
+/*
+** This function does all the work for both the xConnect and xCreate methods.
+** These tables have no persistent representation of their own, so xConnect
+** and xCreate are identical operations.
+**
+** argv[0]: module name
+** argv[1]: database name
+** argv[2]: table name
+** argv[3]: first argument (tokenizer name)
+*/
+static int fts3tokConnectMethod(
+ sqlite3 *db, /* Database connection */
+ void *pHash, /* Hash table of tokenizers */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ Fts3tokTable *pTab = 0;
+ const sqlite3_tokenizer_module *pMod = 0;
+ sqlite3_tokenizer *pTok = 0;
+ int rc;
+ char **azDequote = 0;
+ int nDequote;
+
+ rc = sqlite3_declare_vtab(db, FTS3_TOK_SCHEMA);
+ if( rc!=SQLITE_OK ) return rc;
+
+ nDequote = argc-3;
+ rc = fts3tokDequoteArray(nDequote, &argv[3], &azDequote);
+
+ if( rc==SQLITE_OK ){
+ const char *zModule;
+ if( nDequote<1 ){
+ zModule = "simple";
+ }else{
+ zModule = azDequote[0];
+ }
+ rc = fts3tokQueryTokenizer((Fts3Hash*)pHash, zModule, &pMod, pzErr);
+ }
+
+ assert( (rc==SQLITE_OK)==(pMod!=0) );
+ if( rc==SQLITE_OK ){
+ const char * const *azArg = (const char * const *)&azDequote[1];
+ rc = pMod->xCreate((nDequote>1 ? nDequote-1 : 0), azArg, &pTok);
+ }
+
+ if( rc==SQLITE_OK ){
+ pTab = (Fts3tokTable *)sqlite3_malloc(sizeof(Fts3tokTable));
+ if( pTab==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ memset(pTab, 0, sizeof(Fts3tokTable));
+ pTab->pMod = pMod;
+ pTab->pTok = pTok;
+ *ppVtab = &pTab->base;
+ }else{
+ if( pTok ){
+ pMod->xDestroy(pTok);
+ }
+ }
+
+ sqlite3_free(azDequote);
+ return rc;
+}
+
+/*
+** This function does the work for both the xDisconnect and xDestroy methods.
+** These tables have no persistent representation of their own, so xDisconnect
+** and xDestroy are identical operations.
+*/
+static int fts3tokDisconnectMethod(sqlite3_vtab *pVtab){
+ Fts3tokTable *pTab = (Fts3tokTable *)pVtab;
+
+ pTab->pMod->xDestroy(pTab->pTok);
+ sqlite3_free(pTab);
+ return SQLITE_OK;
+}
+
+/*
+** xBestIndex - Analyze a WHERE and ORDER BY clause.
+*/
+static int fts3tokBestIndexMethod(
+ sqlite3_vtab *pVTab,
+ sqlite3_index_info *pInfo
+){
+ int i;
+ UNUSED_PARAMETER(pVTab);
+
+ for(i=0; i<pInfo->nConstraint; i++){
+ if( pInfo->aConstraint[i].usable
+ && pInfo->aConstraint[i].iColumn==0
+ && pInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ
+ ){
+ pInfo->idxNum = 1;
+ pInfo->aConstraintUsage[i].argvIndex = 1;
+ pInfo->aConstraintUsage[i].omit = 1;
+ pInfo->estimatedCost = 1;
+ return SQLITE_OK;
+ }
+ }
+
+ pInfo->idxNum = 0;
+ assert( pInfo->estimatedCost>1000000.0 );
+
+ return SQLITE_OK;
+}
+
+/*
+** xOpen - Open a cursor.
+*/
+static int fts3tokOpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
+ Fts3tokCursor *pCsr;
+ UNUSED_PARAMETER(pVTab);
+
+ pCsr = (Fts3tokCursor *)sqlite3_malloc(sizeof(Fts3tokCursor));
+ if( pCsr==0 ){
+ return SQLITE_NOMEM;
+ }
+ memset(pCsr, 0, sizeof(Fts3tokCursor));
+
+ *ppCsr = (sqlite3_vtab_cursor *)pCsr;
+ return SQLITE_OK;
+}
+
+/*
+** Reset the tokenizer cursor passed as the only argument. As if it had
+** just been returned by fts3tokOpenMethod().
+*/
+static void fts3tokResetCursor(Fts3tokCursor *pCsr){
+ if( pCsr->pCsr ){
+ Fts3tokTable *pTab = (Fts3tokTable *)(pCsr->base.pVtab);
+ pTab->pMod->xClose(pCsr->pCsr);
+ pCsr->pCsr = 0;
+ }
+ sqlite3_free(pCsr->zInput);
+ pCsr->zInput = 0;
+ pCsr->zToken = 0;
+ pCsr->nToken = 0;
+ pCsr->iStart = 0;
+ pCsr->iEnd = 0;
+ pCsr->iPos = 0;
+ pCsr->iRowid = 0;
+}
+
+/*
+** xClose - Close a cursor.
+*/
+static int fts3tokCloseMethod(sqlite3_vtab_cursor *pCursor){
+ Fts3tokCursor *pCsr = (Fts3tokCursor *)pCursor;
+
+ fts3tokResetCursor(pCsr);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+/*
+** xNext - Advance the cursor to the next row, if any.
+*/
+static int fts3tokNextMethod(sqlite3_vtab_cursor *pCursor){
+ Fts3tokCursor *pCsr = (Fts3tokCursor *)pCursor;
+ Fts3tokTable *pTab = (Fts3tokTable *)(pCursor->pVtab);
+ int rc; /* Return code */
+
+ pCsr->iRowid++;
+ rc = pTab->pMod->xNext(pCsr->pCsr,
+ &pCsr->zToken, &pCsr->nToken,
+ &pCsr->iStart, &pCsr->iEnd, &pCsr->iPos
+ );
+
+ if( rc!=SQLITE_OK ){
+ fts3tokResetCursor(pCsr);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ }
+
+ return rc;
+}
+
+/*
+** xFilter - Initialize a cursor to point at the start of its data.
+*/
+static int fts3tokFilterMethod(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, /* Strategy index */
+ const char *idxStr, /* Unused */
+ int nVal, /* Number of elements in apVal */
+ sqlite3_value **apVal /* Arguments for the indexing scheme */
+){
+ int rc = SQLITE_ERROR;
+ Fts3tokCursor *pCsr = (Fts3tokCursor *)pCursor;
+ Fts3tokTable *pTab = (Fts3tokTable *)(pCursor->pVtab);
+ UNUSED_PARAMETER(idxStr);
+ UNUSED_PARAMETER(nVal);
+
+ fts3tokResetCursor(pCsr);
+ if( idxNum==1 ){
+ const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
+ int nByte = sqlite3_value_bytes(apVal[0]);
+ pCsr->zInput = sqlite3_malloc(nByte+1);
+ if( pCsr->zInput==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memcpy(pCsr->zInput, zByte, nByte);
+ pCsr->zInput[nByte] = 0;
+ rc = pTab->pMod->xOpen(pTab->pTok, pCsr->zInput, nByte, &pCsr->pCsr);
+ if( rc==SQLITE_OK ){
+ pCsr->pCsr->pTokenizer = pTab->pTok;
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ) return rc;
+ return fts3tokNextMethod(pCursor);
+}
+
+/*
+** xEof - Return true if the cursor is at EOF, or false otherwise.
+*/
+static int fts3tokEofMethod(sqlite3_vtab_cursor *pCursor){
+ Fts3tokCursor *pCsr = (Fts3tokCursor *)pCursor;
+ return (pCsr->zToken==0);
+}
+
+/*
+** xColumn - Return a column value.
+*/
+static int fts3tokColumnMethod(
+ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
+ int iCol /* Index of column to read value from */
+){
+ Fts3tokCursor *pCsr = (Fts3tokCursor *)pCursor;
+
+ /* CREATE TABLE x(input, token, start, end, position) */
+ switch( iCol ){
+ case 0:
+ sqlite3_result_text(pCtx, pCsr->zInput, -1, SQLITE_TRANSIENT);
+ break;
+ case 1:
+ sqlite3_result_text(pCtx, pCsr->zToken, pCsr->nToken, SQLITE_TRANSIENT);
+ break;
+ case 2:
+ sqlite3_result_int(pCtx, pCsr->iStart);
+ break;
+ case 3:
+ sqlite3_result_int(pCtx, pCsr->iEnd);
+ break;
+ default:
+ assert( iCol==4 );
+ sqlite3_result_int(pCtx, pCsr->iPos);
+ break;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** xRowid - Return the current rowid for the cursor.
+*/
+static int fts3tokRowidMethod(
+ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
+ sqlite_int64 *pRowid /* OUT: Rowid value */
+){
+ Fts3tokCursor *pCsr = (Fts3tokCursor *)pCursor;
+ *pRowid = (sqlite3_int64)pCsr->iRowid;
+ return SQLITE_OK;
+}
+
+/*
+** Register the fts3tok module with database connection db. Return SQLITE_OK
+** if successful or an error code if sqlite3_create_module() fails.
+*/
+SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3 *db, Fts3Hash *pHash){
+ static const sqlite3_module fts3tok_module = {
+ 0, /* iVersion */
+ fts3tokConnectMethod, /* xCreate */
+ fts3tokConnectMethod, /* xConnect */
+ fts3tokBestIndexMethod, /* xBestIndex */
+ fts3tokDisconnectMethod, /* xDisconnect */
+ fts3tokDisconnectMethod, /* xDestroy */
+ fts3tokOpenMethod, /* xOpen */
+ fts3tokCloseMethod, /* xClose */
+ fts3tokFilterMethod, /* xFilter */
+ fts3tokNextMethod, /* xNext */
+ fts3tokEofMethod, /* xEof */
+ fts3tokColumnMethod, /* xColumn */
+ fts3tokRowidMethod, /* xRowid */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindFunction */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0 /* xRollbackTo */
+ };
+ int rc; /* Return code */
+
+ rc = sqlite3_create_module(db, "fts3tokenize", &fts3tok_module, (void*)pHash);
+ return rc;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
+
+/************** End of fts3_tokenize_vtab.c **********************************/
/************** Begin file fts3_write.c **************************************/
/*
** 2009 Oct 23
@@ -122277,12 +153179,16 @@ SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(
** code in fts3.c.
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <string.h> */
/* #include <assert.h> */
/* #include <stdlib.h> */
+
+#define FTS_MAX_APPENDABLE_HEIGHT 16
+
/*
** When full-text index nodes are loaded from disk, the buffer that they
** are loaded into has the following number of bytes of padding at the end
@@ -122322,6 +153228,29 @@ int test_fts3_node_chunk_threshold = (4*1024)*4;
# define FTS3_NODE_CHUNK_THRESHOLD (FTS3_NODE_CHUNKSIZE*4)
#endif
+/*
+** The two values that may be meaningfully bound to the :1 parameter in
+** statements SQL_REPLACE_STAT and SQL_SELECT_STAT.
+*/
+#define FTS_STAT_DOCTOTAL 0
+#define FTS_STAT_INCRMERGEHINT 1
+#define FTS_STAT_AUTOINCRMERGE 2
+
+/*
+** If FTS_LOG_MERGES is defined, call sqlite3_log() to report each automatic
+** and incremental merge operation that takes place. This is used for
+** debugging FTS only, it should not usually be turned on in production
+** systems.
+*/
+#ifdef FTS3_LOG_MERGES
+static void fts3LogMerge(int nMerge, sqlite3_int64 iAbsLevel){
+ sqlite3_log(SQLITE_OK, "%d-way merge from level %d", nMerge, (int)iAbsLevel);
+}
+#else
+#define fts3LogMerge(x, y)
+#endif
+
+
typedef struct PendingList PendingList;
typedef struct SegmentNode SegmentNode;
typedef struct SegmentWriter SegmentWriter;
@@ -122369,6 +153298,8 @@ struct Fts3DeferredToken {
*/
struct Fts3SegReader {
int iIdx; /* Index within level, or 0x7FFFFFFF for PT */
+ u8 bLookup; /* True for a lookup only */
+ u8 rootOnly; /* True for a root-only reader */
sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */
sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */
@@ -122402,7 +153333,7 @@ struct Fts3SegReader {
};
#define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0)
-#define fts3SegReaderIsRootOnly(p) ((p)->aNode==(char *)&(p)[1])
+#define fts3SegReaderIsRootOnly(p) ((p)->rootOnly!=0)
/*
** An instance of this structure is used to create a segment b-tree in the
@@ -122424,6 +153355,7 @@ struct SegmentWriter {
int nSize; /* Size of allocation at aData */
int nData; /* Bytes of data in aData */
char *aData; /* Pointer to block from malloc() */
+ i64 nLeafData; /* Number of bytes of leaf data written */
};
/*
@@ -122482,13 +153414,26 @@ struct SegmentNode {
#define SQL_DELETE_DOCSIZE 19
#define SQL_REPLACE_DOCSIZE 20
#define SQL_SELECT_DOCSIZE 21
-#define SQL_SELECT_DOCTOTAL 22
-#define SQL_REPLACE_DOCTOTAL 23
+#define SQL_SELECT_STAT 22
+#define SQL_REPLACE_STAT 23
#define SQL_SELECT_ALL_PREFIX_LEVEL 24
#define SQL_DELETE_ALL_TERMS_SEGDIR 25
-
#define SQL_DELETE_SEGDIR_RANGE 26
+#define SQL_SELECT_ALL_LANGID 27
+#define SQL_FIND_MERGE_LEVEL 28
+#define SQL_MAX_LEAF_NODE_ESTIMATE 29
+#define SQL_DELETE_SEGDIR_ENTRY 30
+#define SQL_SHIFT_SEGDIR_ENTRY 31
+#define SQL_SELECT_SEGDIR 32
+#define SQL_CHOMP_SEGDIR 33
+#define SQL_SEGMENT_IS_APPENDABLE 34
+#define SQL_SELECT_INDEXES 35
+#define SQL_SELECT_MXLEVEL 36
+
+#define SQL_SELECT_LEVEL_RANGE2 37
+#define SQL_UPDATE_LEVEL_IDX 38
+#define SQL_UPDATE_LEVEL 39
/*
** This function is used to obtain an SQLite prepared statement handle
@@ -122515,11 +153460,11 @@ static int fts3SqlStmt(
/* 4 */ "DELETE FROM %Q.'%q_segdir'",
/* 5 */ "DELETE FROM %Q.'%q_docsize'",
/* 6 */ "DELETE FROM %Q.'%q_stat'",
-/* 7 */ "SELECT %s FROM %Q.'%q_content' AS x WHERE rowid=?",
+/* 7 */ "SELECT %s WHERE rowid=?",
/* 8 */ "SELECT (SELECT max(idx) FROM %Q.'%q_segdir' WHERE level = ?) + 1",
-/* 9 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
+/* 9 */ "REPLACE INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)",
/* 10 */ "SELECT coalesce((SELECT max(blockid) FROM %Q.'%q_segments') + 1, 1)",
-/* 11 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
+/* 11 */ "REPLACE INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)",
/* Return segments in order from oldest to newest.*/
/* 12 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
@@ -122537,12 +153482,72 @@ static int fts3SqlStmt(
/* 19 */ "DELETE FROM %Q.'%q_docsize' WHERE docid = ?",
/* 20 */ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)",
/* 21 */ "SELECT size FROM %Q.'%q_docsize' WHERE docid=?",
-/* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=0",
-/* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(0,?)",
+/* 22 */ "SELECT value FROM %Q.'%q_stat' WHERE id=?",
+/* 23 */ "REPLACE INTO %Q.'%q_stat' VALUES(?,?)",
/* 24 */ "",
/* 25 */ "",
/* 26 */ "DELETE FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?",
+/* 27 */ "SELECT ? UNION SELECT level / (1024 * ?) FROM %Q.'%q_segdir'",
+
+/* This statement is used to determine which level to read the input from
+** when performing an incremental merge. It returns the absolute level number
+** of the oldest level in the db that contains at least ? segments. Or,
+** if no level in the FTS index contains more than ? segments, the statement
+** returns zero rows. */
+/* 28 */ "SELECT level, count(*) AS cnt FROM %Q.'%q_segdir' "
+ " GROUP BY level HAVING cnt>=?"
+ " ORDER BY (level %% 1024) ASC LIMIT 1",
+
+/* Estimate the upper limit on the number of leaf nodes in a new segment
+** created by merging the oldest :2 segments from absolute level :1. See
+** function sqlite3Fts3Incrmerge() for details. */
+/* 29 */ "SELECT 2 * total(1 + leaves_end_block - start_block) "
+ " FROM %Q.'%q_segdir' WHERE level = ? AND idx < ?",
+
+/* SQL_DELETE_SEGDIR_ENTRY
+** Delete the %_segdir entry on absolute level :1 with index :2. */
+/* 30 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ? AND idx = ?",
+
+/* SQL_SHIFT_SEGDIR_ENTRY
+** Modify the idx value for the segment with idx=:3 on absolute level :2
+** to :1. */
+/* 31 */ "UPDATE %Q.'%q_segdir' SET idx = ? WHERE level=? AND idx=?",
+
+/* SQL_SELECT_SEGDIR
+** Read a single entry from the %_segdir table. The entry from absolute
+** level :1 with index value :2. */
+/* 32 */ "SELECT idx, start_block, leaves_end_block, end_block, root "
+ "FROM %Q.'%q_segdir' WHERE level = ? AND idx = ?",
+
+/* SQL_CHOMP_SEGDIR
+** Update the start_block (:1) and root (:2) fields of the %_segdir
+** entry located on absolute level :3 with index :4. */
+/* 33 */ "UPDATE %Q.'%q_segdir' SET start_block = ?, root = ?"
+ "WHERE level = ? AND idx = ?",
+
+/* SQL_SEGMENT_IS_APPENDABLE
+** Return a single row if the segment with end_block=? is appendable. Or
+** no rows otherwise. */
+/* 34 */ "SELECT 1 FROM %Q.'%q_segments' WHERE blockid=? AND block IS NULL",
+
+/* SQL_SELECT_INDEXES
+** Return the list of valid segment indexes for absolute level ? */
+/* 35 */ "SELECT idx FROM %Q.'%q_segdir' WHERE level=? ORDER BY 1 ASC",
+
+/* SQL_SELECT_MXLEVEL
+** Return the largest relative level in the FTS index or indexes. */
+/* 36 */ "SELECT max( level %% 1024 ) FROM %Q.'%q_segdir'",
+
+ /* Return segments in order from oldest to newest.*/
+/* 37 */ "SELECT level, idx, end_block "
+ "FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ? "
+ "ORDER BY level DESC, idx ASC",
+
+ /* Update statements used while promoting segments */
+/* 38 */ "UPDATE OR FAIL %Q.'%q_segdir' SET level=-1,idx=? "
+ "WHERE level=? AND idx=?",
+/* 39 */ "UPDATE OR FAIL %Q.'%q_segdir' SET level=? WHERE level=-1"
};
int rc = SQLITE_OK;
@@ -122557,7 +153562,7 @@ static int fts3SqlStmt(
if( eStmt==SQL_CONTENT_INSERT ){
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, p->zWriteExprlist);
}else if( eStmt==SQL_SELECT_CONTENT_BY_ROWID ){
- zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist, p->zDb, p->zName);
+ zSql = sqlite3_mprintf(azSql[eStmt], p->zReadExprlist);
}else{
zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName);
}
@@ -122581,26 +153586,22 @@ static int fts3SqlStmt(
return rc;
}
+
static int fts3SelectDocsize(
Fts3Table *pTab, /* FTS3 table handle */
- int eStmt, /* Either SQL_SELECT_DOCSIZE or DOCTOTAL */
sqlite3_int64 iDocid, /* Docid to bind for SQL_SELECT_DOCSIZE */
sqlite3_stmt **ppStmt /* OUT: Statement handle */
){
sqlite3_stmt *pStmt = 0; /* Statement requested from fts3SqlStmt() */
int rc; /* Return code */
- assert( eStmt==SQL_SELECT_DOCSIZE || eStmt==SQL_SELECT_DOCTOTAL );
-
- rc = fts3SqlStmt(pTab, eStmt, &pStmt, 0);
+ rc = fts3SqlStmt(pTab, SQL_SELECT_DOCSIZE, &pStmt, 0);
if( rc==SQLITE_OK ){
- if( eStmt==SQL_SELECT_DOCSIZE ){
- sqlite3_bind_int64(pStmt, 1, iDocid);
- }
+ sqlite3_bind_int64(pStmt, 1, iDocid);
rc = sqlite3_step(pStmt);
if( rc!=SQLITE_ROW || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){
rc = sqlite3_reset(pStmt);
- if( rc==SQLITE_OK ) rc = SQLITE_CORRUPT_VTAB;
+ if( rc==SQLITE_OK ) rc = FTS_CORRUPT_VTAB;
pStmt = 0;
}else{
rc = SQLITE_OK;
@@ -122615,7 +153616,21 @@ SQLITE_PRIVATE int sqlite3Fts3SelectDoctotal(
Fts3Table *pTab, /* Fts3 table handle */
sqlite3_stmt **ppStmt /* OUT: Statement handle */
){
- return fts3SelectDocsize(pTab, SQL_SELECT_DOCTOTAL, 0, ppStmt);
+ sqlite3_stmt *pStmt = 0;
+ int rc;
+ rc = fts3SqlStmt(pTab, SQL_SELECT_STAT, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL);
+ if( sqlite3_step(pStmt)!=SQLITE_ROW
+ || sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB
+ ){
+ rc = sqlite3_reset(pStmt);
+ if( rc==SQLITE_OK ) rc = FTS_CORRUPT_VTAB;
+ pStmt = 0;
+ }
+ }
+ *ppStmt = pStmt;
+ return rc;
}
SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(
@@ -122623,7 +153638,7 @@ SQLITE_PRIVATE int sqlite3Fts3SelectDocsize(
sqlite3_int64 iDocid, /* Docid to read size data for */
sqlite3_stmt **ppStmt /* OUT: Statement handle */
){
- return fts3SelectDocsize(pTab, SQL_SELECT_DOCSIZE, iDocid, ppStmt);
+ return fts3SelectDocsize(pTab, iDocid, ppStmt);
}
/*
@@ -122653,36 +153668,74 @@ static void fts3SqlExec(
/*
-** This function ensures that the caller has obtained a shared-cache
-** table-lock on the %_content table. This is required before reading
-** data from the fts3 table. If this lock is not acquired first, then
-** the caller may end up holding read-locks on the %_segments and %_segdir
-** tables, but no read-lock on the %_content table. If this happens
-** a second connection will be able to write to the fts3 table, but
-** attempting to commit those writes might return SQLITE_LOCKED or
-** SQLITE_LOCKED_SHAREDCACHE (because the commit attempts to obtain
-** write-locks on the %_segments and %_segdir ** tables).
+** This function ensures that the caller has obtained an exclusive
+** shared-cache table-lock on the %_segdir table. This is required before
+** writing data to the fts3 table. If this lock is not acquired first, then
+** the caller may end up attempting to take this lock as part of committing
+** a transaction, causing SQLite to return SQLITE_LOCKED or
+** LOCKED_SHAREDCACHEto a COMMIT command.
**
-** We try to avoid this because if FTS3 returns any error when committing
-** a transaction, the whole transaction will be rolled back. And this is
-** not what users expect when they get SQLITE_LOCKED_SHAREDCACHE. It can
-** still happen if the user reads data directly from the %_segments or
-** %_segdir tables instead of going through FTS3 though.
+** It is best to avoid this because if FTS3 returns any error when
+** committing a transaction, the whole transaction will be rolled back.
+** And this is not what users expect when they get SQLITE_LOCKED_SHAREDCACHE.
+** It can still happen if the user locks the underlying tables directly
+** instead of accessing them via FTS.
*/
-SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){
- int rc; /* Return code */
- sqlite3_stmt *pStmt; /* Statement used to obtain lock */
-
- rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pStmt, 0);
- if( rc==SQLITE_OK ){
- sqlite3_bind_null(pStmt, 1);
- sqlite3_step(pStmt);
- rc = sqlite3_reset(pStmt);
+static int fts3Writelock(Fts3Table *p){
+ int rc = SQLITE_OK;
+
+ if( p->nPendingData==0 ){
+ sqlite3_stmt *pStmt;
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_null(pStmt, 1);
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ }
}
+
return rc;
}
/*
+** FTS maintains a separate indexes for each language-id (a 32-bit integer).
+** Within each language id, a separate index is maintained to store the
+** document terms, and each configured prefix size (configured the FTS
+** "prefix=" option). And each index consists of multiple levels ("relative
+** levels").
+**
+** All three of these values (the language id, the specific index and the
+** level within the index) are encoded in 64-bit integer values stored
+** in the %_segdir table on disk. This function is used to convert three
+** separate component values into the single 64-bit integer value that
+** can be used to query the %_segdir table.
+**
+** Specifically, each language-id/index combination is allocated 1024
+** 64-bit integer level values ("absolute levels"). The main terms index
+** for language-id 0 is allocate values 0-1023. The first prefix index
+** (if any) for language-id 0 is allocated values 1024-2047. And so on.
+** Language 1 indexes are allocated immediately following language 0.
+**
+** So, for a system with nPrefix prefix indexes configured, the block of
+** absolute levels that corresponds to language-id iLangid and index
+** iIndex starts at absolute level ((iLangid * (nPrefix+1) + iIndex) * 1024).
+*/
+static sqlite3_int64 getAbsoluteLevel(
+ Fts3Table *p, /* FTS3 table handle */
+ int iLangid, /* Language id */
+ int iIndex, /* Index in p->aIndex[] */
+ int iLevel /* Level of segments */
+){
+ sqlite3_int64 iBase; /* First absolute level for iLangid/iIndex */
+ assert( iLangid>=0 );
+ assert( p->nIndex>0 );
+ assert( iIndex>=0 && iIndex<p->nIndex );
+
+ iBase = ((sqlite3_int64)iLangid * p->nIndex + iIndex) * FTS3_SEGDIR_MAXLEVEL;
+ return iBase + iLevel;
+}
+
+/*
** Set *ppStmt to a statement handle that may be used to iterate through
** all rows in the %_segdir table, from oldest to newest. If successful,
** return SQLITE_OK. If an error occurs while preparing the statement,
@@ -122701,8 +153754,9 @@ SQLITE_PRIVATE int sqlite3Fts3ReadLock(Fts3Table *p){
*/
SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(
Fts3Table *p, /* FTS3 table */
+ int iLangid, /* Language being queried */
int iIndex, /* Index for p->aIndex[] */
- int iLevel, /* Level to select */
+ int iLevel, /* Level to select (relative level) */
sqlite3_stmt **ppStmt /* OUT: Compiled statement */
){
int rc;
@@ -122716,14 +153770,16 @@ SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(
/* "SELECT * FROM %_segdir WHERE level BETWEEN ? AND ? ORDER BY ..." */
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE, &pStmt, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
- sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL-1);
+ sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0));
+ sqlite3_bind_int64(pStmt, 2,
+ getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1)
+ );
}
}else{
/* "SELECT * FROM %_segdir WHERE level = ? ORDER BY ..." */
rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pStmt, 1, iLevel+iIndex*FTS3_SEGDIR_MAXLEVEL);
+ sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex,iLevel));
}
}
*ppStmt = pStmt;
@@ -122889,18 +153945,19 @@ static int fts3PendingTermsAddOne(
*/
static int fts3PendingTermsAdd(
Fts3Table *p, /* Table into which text will be inserted */
+ int iLangid, /* Language id to use */
const char *zText, /* Text of document to be inserted */
int iCol, /* Column into which text is being inserted */
- u32 *pnWord /* OUT: Number of tokens inserted */
+ u32 *pnWord /* IN/OUT: Incr. by number tokens inserted */
){
int rc;
- int iStart;
- int iEnd;
- int iPos;
+ int iStart = 0;
+ int iEnd = 0;
+ int iPos = 0;
int nWord = 0;
char const *zToken;
- int nToken;
+ int nToken = 0;
sqlite3_tokenizer *pTokenizer = p->pTokenizer;
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
@@ -122918,11 +153975,10 @@ static int fts3PendingTermsAdd(
return SQLITE_OK;
}
- rc = pModule->xOpen(pTokenizer, zText, -1, &pCsr);
+ rc = sqlite3Fts3OpenTokenizer(pTokenizer, iLangid, zText, -1, &pCsr);
if( rc!=SQLITE_OK ){
return rc;
}
- pCsr->pTokenizer = pTokenizer;
xNext = pModule->xNext;
while( SQLITE_OK==rc
@@ -122956,7 +154012,7 @@ static int fts3PendingTermsAdd(
}
pModule->xClose(pCsr);
- *pnWord = nWord;
+ *pnWord += nWord;
return (rc==SQLITE_DONE ? SQLITE_OK : rc);
}
@@ -122965,18 +154021,32 @@ static int fts3PendingTermsAdd(
** fts3PendingTermsAdd() are to add term/position-list pairs for the
** contents of the document with docid iDocid.
*/
-static int fts3PendingTermsDocid(Fts3Table *p, sqlite_int64 iDocid){
+static int fts3PendingTermsDocid(
+ Fts3Table *p, /* Full-text table handle */
+ int bDelete, /* True if this op is a delete */
+ int iLangid, /* Language id of row being written */
+ sqlite_int64 iDocid /* Docid of row being written */
+){
+ assert( iLangid>=0 );
+ assert( bDelete==1 || bDelete==0 );
+
/* TODO(shess) Explore whether partially flushing the buffer on
** forced-flush would provide better performance. I suspect that if
** we ordered the doclists by size and flushed the largest until the
** buffer was half empty, that would let the less frequent terms
** generate longer doclists.
*/
- if( iDocid<=p->iPrevDocid || p->nPendingData>p->nMaxPendingData ){
+ if( iDocid<p->iPrevDocid
+ || (iDocid==p->iPrevDocid && p->bPrevDelete==0)
+ || p->iPrevLangid!=iLangid
+ || p->nPendingData>p->nMaxPendingData
+ ){
int rc = sqlite3Fts3PendingTermsFlush(p);
if( rc!=SQLITE_OK ) return rc;
}
p->iPrevDocid = iDocid;
+ p->iPrevLangid = iLangid;
+ p->bPrevDelete = bDelete;
return SQLITE_OK;
}
@@ -123005,15 +154075,23 @@ SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){
** Argument apVal is the same as the similarly named argument passed to
** fts3InsertData(). Parameter iDocid is the docid of the new row.
*/
-static int fts3InsertTerms(Fts3Table *p, sqlite3_value **apVal, u32 *aSz){
+static int fts3InsertTerms(
+ Fts3Table *p,
+ int iLangid,
+ sqlite3_value **apVal,
+ u32 *aSz
+){
int i; /* Iterator variable */
for(i=2; i<p->nColumn+2; i++){
- const char *zText = (const char *)sqlite3_value_text(apVal[i]);
- int rc = fts3PendingTermsAdd(p, zText, i-2, &aSz[i-2]);
- if( rc!=SQLITE_OK ){
- return rc;
+ int iCol = i-2;
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_value_text(apVal[i]);
+ int rc = fts3PendingTermsAdd(p, iLangid, zText, iCol, &aSz[iCol]);
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
+ aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
}
- aSz[p->nColumn] += sqlite3_value_bytes(apVal[i]);
}
return SQLITE_OK;
}
@@ -123030,6 +154108,7 @@ static int fts3InsertTerms(Fts3Table *p, sqlite3_value **apVal, u32 *aSz){
** apVal[p->nColumn+1] Right-most user-defined column
** apVal[p->nColumn+2] Hidden column with same name as table
** apVal[p->nColumn+3] Hidden "docid" column (alias for rowid)
+** apVal[p->nColumn+4] Hidden languageid column
*/
static int fts3InsertData(
Fts3Table *p, /* Full-text table */
@@ -123039,6 +154118,18 @@ static int fts3InsertData(
int rc; /* Return code */
sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */
+ if( p->zContentTbl ){
+ sqlite3_value *pRowid = apVal[p->nColumn+3];
+ if( sqlite3_value_type(pRowid)==SQLITE_NULL ){
+ pRowid = apVal[1];
+ }
+ if( sqlite3_value_type(pRowid)!=SQLITE_INTEGER ){
+ return SQLITE_CONSTRAINT;
+ }
+ *piDocid = sqlite3_value_int64(pRowid);
+ return SQLITE_OK;
+ }
+
/* Locate the statement handle used to insert data into the %_content
** table. The SQL for this statement is:
**
@@ -123048,9 +154139,13 @@ static int fts3InsertData(
** defined columns in the FTS3 table, plus one for the docid field.
*/
rc = fts3SqlStmt(p, SQL_CONTENT_INSERT, &pContentInsert, &apVal[1]);
- if( rc!=SQLITE_OK ){
- return rc;
+ if( rc==SQLITE_OK && p->zLanguageid ){
+ rc = sqlite3_bind_int(
+ pContentInsert, p->nColumn+2,
+ sqlite3_value_int(apVal[p->nColumn+4])
+ );
}
+ if( rc!=SQLITE_OK ) return rc;
/* There is a quirk here. The users INSERT statement may have specified
** a value for the "rowid" field, for the "docid" field, or for both.
@@ -123089,14 +154184,16 @@ static int fts3InsertData(
** Remove all data from the FTS3 table. Clear the hash table containing
** pending terms.
*/
-static int fts3DeleteAll(Fts3Table *p){
+static int fts3DeleteAll(Fts3Table *p, int bContent){
int rc = SQLITE_OK; /* Return code */
/* Discard the contents of the pending-terms hash table. */
sqlite3Fts3PendingTermsClear(p);
- /* Delete everything from the %_content, %_segments and %_segdir tables. */
- fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
+ /* Delete everything from the shadow tables. Except, leave %_content as
+ ** is if bContent is false. */
+ assert( p->zContentTbl==0 || bContent==0 );
+ if( bContent ) fts3SqlExec(&rc, p, SQL_DELETE_ALL_CONTENT, 0);
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGMENTS, 0);
fts3SqlExec(&rc, p, SQL_DELETE_ALL_SEGDIR, 0);
if( p->bHasDocsize ){
@@ -123109,6 +154206,15 @@ static int fts3DeleteAll(Fts3Table *p){
}
/*
+**
+*/
+static int langidFromSelect(Fts3Table *p, sqlite3_stmt *pSelect){
+ int iLangid = 0;
+ if( p->zLanguageid ) iLangid = sqlite3_column_int(pSelect, p->nColumn+1);
+ return iLangid;
+}
+
+/*
** The first element in the apVal[] array is assumed to contain the docid
** (an integer) of a row about to be deleted. Remove all terms from the
** full-text index.
@@ -123117,26 +154223,35 @@ static void fts3DeleteTerms(
int *pRC, /* Result code */
Fts3Table *p, /* The FTS table to delete from */
sqlite3_value *pRowid, /* The docid to be deleted */
- u32 *aSz /* Sizes of deleted document written here */
+ u32 *aSz, /* Sizes of deleted document written here */
+ int *pbFound /* OUT: Set to true if row really does exist */
){
int rc;
sqlite3_stmt *pSelect;
+ assert( *pbFound==0 );
if( *pRC ) return;
rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, &pRowid);
if( rc==SQLITE_OK ){
if( SQLITE_ROW==sqlite3_step(pSelect) ){
int i;
- for(i=1; i<=p->nColumn; i++){
- const char *zText = (const char *)sqlite3_column_text(pSelect, i);
- rc = fts3PendingTermsAdd(p, zText, -1, &aSz[i-1]);
- if( rc!=SQLITE_OK ){
- sqlite3_reset(pSelect);
- *pRC = rc;
- return;
+ int iLangid = langidFromSelect(p, pSelect);
+ i64 iDocid = sqlite3_column_int64(pSelect, 0);
+ rc = fts3PendingTermsDocid(p, 1, iLangid, iDocid);
+ for(i=1; rc==SQLITE_OK && i<=p->nColumn; i++){
+ int iCol = i-1;
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pSelect, i);
+ rc = fts3PendingTermsAdd(p, iLangid, zText, -1, &aSz[iCol]);
+ aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
}
- aSz[p->nColumn] += sqlite3_column_bytes(pSelect, i);
}
+ if( rc!=SQLITE_OK ){
+ sqlite3_reset(pSelect);
+ *pRC = rc;
+ return;
+ }
+ *pbFound = 1;
}
rc = sqlite3_reset(pSelect);
}else{
@@ -123149,7 +154264,7 @@ static void fts3DeleteTerms(
** Forward declaration to account for the circular dependency between
** functions fts3SegmentMerge() and fts3AllocateSegdirIdx().
*/
-static int fts3SegmentMerge(Fts3Table *, int, int);
+static int fts3SegmentMerge(Fts3Table *, int, int, int);
/*
** This function allocates a new level iLevel index in the segdir table.
@@ -123168,6 +154283,7 @@ static int fts3SegmentMerge(Fts3Table *, int, int);
*/
static int fts3AllocateSegdirIdx(
Fts3Table *p,
+ int iLangid, /* Language id */
int iIndex, /* Index for p->aIndex */
int iLevel,
int *piIdx
@@ -123176,10 +154292,15 @@ static int fts3AllocateSegdirIdx(
sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */
int iNext = 0; /* Result of query pNextIdx */
+ assert( iLangid>=0 );
+ assert( p->nIndex>=1 );
+
/* Set variable iNext to the next available segdir index at level iLevel. */
rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pNextIdx, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
+ sqlite3_bind_int64(
+ pNextIdx, 1, getAbsoluteLevel(p, iLangid, iIndex, iLevel)
+ );
if( SQLITE_ROW==sqlite3_step(pNextIdx) ){
iNext = sqlite3_column_int(pNextIdx, 0);
}
@@ -123193,7 +154314,8 @@ static int fts3AllocateSegdirIdx(
** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext.
*/
if( iNext>=FTS3_MERGE_COUNT ){
- rc = fts3SegmentMerge(p, iIndex, iLevel);
+ fts3LogMerge(16, getAbsoluteLevel(p, iLangid, iIndex, iLevel));
+ rc = fts3SegmentMerge(p, iLangid, iIndex, iLevel);
*piIdx = 0;
}else{
*piIdx = iNext;
@@ -123240,7 +154362,7 @@ SQLITE_PRIVATE int sqlite3Fts3ReadBlock(
int rc; /* Return code */
/* pnBlob must be non-NULL. paBlob may be NULL or non-NULL. */
- assert( pnBlob);
+ assert( pnBlob );
if( p->pSegments ){
rc = sqlite3_blob_reopen(p->pSegments, iBlockid);
@@ -123327,6 +154449,18 @@ static int fts3SegReaderRequire(Fts3SegReader *pReader, char *pFrom, int nByte){
}
/*
+** Set an Fts3SegReader cursor to point at EOF.
+*/
+static void fts3SegReaderSetEof(Fts3SegReader *pSeg){
+ if( !fts3SegReaderIsRootOnly(pSeg) ){
+ sqlite3_free(pSeg->aNode);
+ sqlite3_blob_close(pSeg->pBlob);
+ pSeg->pBlob = 0;
+ }
+ pSeg->aNode = 0;
+}
+
+/*
** Move the iterator passed as the first argument to the next term in the
** segment. If successful, SQLITE_OK is returned. If there is no next term,
** SQLITE_DONE. Otherwise, an SQLite error code.
@@ -123351,26 +154485,26 @@ static int fts3SegReaderNext(
if( fts3SegReaderIsPending(pReader) ){
Fts3HashElem *pElem = *(pReader->ppNextElem);
- if( pElem==0 ){
- pReader->aNode = 0;
- }else{
+ sqlite3_free(pReader->aNode);
+ pReader->aNode = 0;
+ if( pElem ){
+ char *aCopy;
PendingList *pList = (PendingList *)fts3HashData(pElem);
+ int nCopy = pList->nData+1;
pReader->zTerm = (char *)fts3HashKey(pElem);
pReader->nTerm = fts3HashKeysize(pElem);
- pReader->nNode = pReader->nDoclist = pList->nData + 1;
- pReader->aNode = pReader->aDoclist = pList->aData;
+ aCopy = (char*)sqlite3_malloc(nCopy);
+ if( !aCopy ) return SQLITE_NOMEM;
+ memcpy(aCopy, pList->aData, nCopy);
+ pReader->nNode = pReader->nDoclist = nCopy;
+ pReader->aNode = pReader->aDoclist = aCopy;
pReader->ppNextElem++;
assert( pReader->aNode );
}
return SQLITE_OK;
}
- if( !fts3SegReaderIsRootOnly(pReader) ){
- sqlite3_free(pReader->aNode);
- sqlite3_blob_close(pReader->pBlob);
- pReader->pBlob = 0;
- }
- pReader->aNode = 0;
+ fts3SegReaderSetEof(pReader);
/* If iCurrentBlock>=iLeafEndBlock, this is an EOF condition. All leaf
** blocks have already been traversed. */
@@ -123399,12 +154533,12 @@ static int fts3SegReaderNext(
/* Because of the FTS3_NODE_PADDING bytes of padding, the following is
** safe (no risk of overread) even if the node data is corrupted. */
- pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix);
- pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix);
+ pNext += fts3GetVarint32(pNext, &nPrefix);
+ pNext += fts3GetVarint32(pNext, &nSuffix);
if( nPrefix<0 || nSuffix<=0
|| &pNext[nSuffix]>&pReader->aNode[pReader->nNode]
){
- return SQLITE_CORRUPT_VTAB;
+ return FTS_CORRUPT_VTAB;
}
if( nPrefix+nSuffix>pReader->nTermAlloc ){
@@ -123423,7 +154557,7 @@ static int fts3SegReaderNext(
memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix);
pReader->nTerm = nPrefix+nSuffix;
pNext += nSuffix;
- pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist);
+ pNext += fts3GetVarint32(pNext, &pReader->nDoclist);
pReader->aDoclist = pNext;
pReader->pOffsetList = 0;
@@ -123434,7 +154568,7 @@ static int fts3SegReaderNext(
if( &pReader->aDoclist[pReader->nDoclist]>&pReader->aNode[pReader->nNode]
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
){
- return SQLITE_CORRUPT_VTAB;
+ return FTS_CORRUPT_VTAB;
}
return SQLITE_OK;
}
@@ -123516,7 +154650,7 @@ static int fts3SegReaderNextDocid(
/* The following line of code (and the "p++" below the while() loop) is
** normally all that is required to move pointer p to the desired
** position. The exception is if this node is being loaded from disk
- ** incrementally and pointer "p" now points to the first byte passed
+ ** incrementally and pointer "p" now points to the first byte past
** the populated part of pReader->aNode[].
*/
while( *p | c ) c = *p++ & 0x80;
@@ -123536,6 +154670,7 @@ static int fts3SegReaderNextDocid(
*pnOffsetList = (int)(p - pReader->pOffsetList - 1);
}
+ /* List may have been edited in place by fts3EvalNearTrim() */
while( p<pEnd && *p==0 ) p++;
/* If there are no more entries in the doclist, set pOffsetList to
@@ -123574,7 +154709,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
int rc = SQLITE_OK;
int pgsz = p->nPgsz;
- assert( p->bHasStat );
+ assert( p->bFts4 );
assert( pgsz>0 );
for(ii=0; rc==SQLITE_OK && ii<pMsr->nSegment; ii++){
@@ -123602,12 +154737,14 @@ SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(
** second argument.
*/
SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){
- if( pReader && !fts3SegReaderIsPending(pReader) ){
- sqlite3_free(pReader->zTerm);
+ if( pReader ){
+ if( !fts3SegReaderIsPending(pReader) ){
+ sqlite3_free(pReader->zTerm);
+ }
if( !fts3SegReaderIsRootOnly(pReader) ){
sqlite3_free(pReader->aNode);
- sqlite3_blob_close(pReader->pBlob);
}
+ sqlite3_blob_close(pReader->pBlob);
}
sqlite3_free(pReader);
}
@@ -123617,6 +154754,7 @@ SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3SegReader *pReader){
*/
SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
int iAge, /* Segment "age". */
+ int bLookup, /* True for a lookup only */
sqlite3_int64 iStartLeaf, /* First leaf to traverse */
sqlite3_int64 iEndLeaf, /* Final leaf to traverse */
sqlite3_int64 iEndBlock, /* Final block of segment */
@@ -123624,7 +154762,6 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
int nRoot, /* Size of buffer containing root node */
Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */
){
- int rc = SQLITE_OK; /* Return code */
Fts3SegReader *pReader; /* Newly allocated SegReader object */
int nExtra = 0; /* Bytes to allocate segment root node */
@@ -123639,6 +154776,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
}
memset(pReader, 0, sizeof(Fts3SegReader));
pReader->iIdx = iAge;
+ pReader->bLookup = bLookup!=0;
pReader->iStartBlock = iStartLeaf;
pReader->iLeafEndBlock = iEndLeaf;
pReader->iEndBlock = iEndBlock;
@@ -123646,19 +154784,15 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
if( nExtra ){
/* The entire segment is stored in the root node. */
pReader->aNode = (char *)&pReader[1];
+ pReader->rootOnly = 1;
pReader->nNode = nRoot;
memcpy(pReader->aNode, zRoot, nRoot);
memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING);
}else{
pReader->iCurrentBlock = iStartLeaf-1;
}
-
- if( rc==SQLITE_OK ){
- *ppReader = pReader;
- }else{
- sqlite3Fts3SegReaderFree(pReader);
- }
- return rc;
+ *ppReader = pReader;
+ return SQLITE_OK;
}
/*
@@ -123666,7 +154800,10 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(
** an array of pending terms by term. This occurs as part of flushing
** the contents of the pending-terms hash table to the database.
*/
-static int fts3CompareElemByTerm(const void *lhs, const void *rhs){
+static int SQLITE_CDECL fts3CompareElemByTerm(
+ const void *lhs,
+ const void *rhs
+){
char *z1 = fts3HashKey(*(Fts3HashElem **)lhs);
char *z2 = fts3HashKey(*(Fts3HashElem **)rhs);
int n1 = fts3HashKeysize(*(Fts3HashElem **)lhs);
@@ -123708,6 +154845,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */
){
Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */
+ Fts3HashElem *pE; /* Iterator variable */
Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */
int nElem = 0; /* Size of array at aElem */
int rc = SQLITE_OK; /* Return Code */
@@ -123716,7 +154854,6 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
pHash = &p->aIndex[iIndex].hPending;
if( bPrefix ){
int nAlloc = 0; /* Size of allocated array at aElem */
- Fts3HashElem *pE = 0; /* Iterator variable */
for(pE=fts3HashFirst(pHash); pE; pE=fts3HashNext(pE)){
char *zKey = (char *)fts3HashKey(pE);
@@ -123750,8 +154887,13 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(
}else{
/* The query is a simple term lookup that matches at most one term in
- ** the index. All that is required is a straight hash-lookup. */
- Fts3HashElem *pE = fts3HashFindElem(pHash, zTerm, nTerm);
+ ** the index. All that is required is a straight hash-lookup.
+ **
+ ** Because the stack address of pE may be accessed via the aElem pointer
+ ** below, the "Fts3HashElem *pE" must be declared so that it is valid
+ ** within this entire function, not just this "else{...}" block.
+ */
+ pE = fts3HashFindElem(pHash, zTerm, nTerm);
if( pE ){
aElem = &pE;
nElem = 1;
@@ -123931,27 +155073,55 @@ static int fts3WriteSegment(
return rc;
}
+/*
+** Find the largest relative level number in the table. If successful, set
+** *pnMax to this value and return SQLITE_OK. Otherwise, if an error occurs,
+** set *pnMax to zero and return an SQLite error code.
+*/
+SQLITE_PRIVATE int sqlite3Fts3MaxLevel(Fts3Table *p, int *pnMax){
+ int rc;
+ int mxLevel = 0;
+ sqlite3_stmt *pStmt = 0;
+
+ rc = fts3SqlStmt(p, SQL_SELECT_MXLEVEL, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ mxLevel = sqlite3_column_int(pStmt, 0);
+ }
+ rc = sqlite3_reset(pStmt);
+ }
+ *pnMax = mxLevel;
+ return rc;
+}
+
/*
** Insert a record into the %_segdir table.
*/
static int fts3WriteSegdir(
Fts3Table *p, /* Virtual table handle */
- int iLevel, /* Value for "level" field */
+ sqlite3_int64 iLevel, /* Value for "level" field (absolute level) */
int iIdx, /* Value for "idx" field */
sqlite3_int64 iStartBlock, /* Value for "start_block" field */
sqlite3_int64 iLeafEndBlock, /* Value for "leaves_end_block" field */
sqlite3_int64 iEndBlock, /* Value for "end_block" field */
+ sqlite3_int64 nLeafData, /* Bytes of leaf data in segment */
char *zRoot, /* Blob value for "root" field */
int nRoot /* Number of bytes in buffer zRoot */
){
sqlite3_stmt *pStmt;
int rc = fts3SqlStmt(p, SQL_INSERT_SEGDIR, &pStmt, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pStmt, 1, iLevel);
+ sqlite3_bind_int64(pStmt, 1, iLevel);
sqlite3_bind_int(pStmt, 2, iIdx);
sqlite3_bind_int64(pStmt, 3, iStartBlock);
sqlite3_bind_int64(pStmt, 4, iLeafEndBlock);
- sqlite3_bind_int64(pStmt, 5, iEndBlock);
+ if( nLeafData==0 ){
+ sqlite3_bind_int64(pStmt, 5, iEndBlock);
+ }else{
+ char *zEnd = sqlite3_mprintf("%lld %lld", iEndBlock, nLeafData);
+ if( !zEnd ) return SQLITE_NOMEM;
+ sqlite3_bind_text(pStmt, 5, zEnd, -1, sqlite3_free);
+ }
sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC);
sqlite3_step(pStmt);
rc = sqlite3_reset(pStmt);
@@ -124247,6 +155417,7 @@ static int fts3SegWriterAdd(
/* The current leaf node is full. Write it out to the database. */
rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData);
if( rc!=SQLITE_OK ) return rc;
+ p->nLeafAdd++;
/* Add the current term to the interior node tree. The term added to
** the interior tree must:
@@ -124276,6 +155447,9 @@ static int fts3SegWriterAdd(
nDoclist; /* Doclist data */
}
+ /* Increase the total number of bytes written to account for the new entry. */
+ pWriter->nLeafData += nReq;
+
/* If the buffer currently allocated is too small for this entry, realloc
** the buffer to make it large enough.
*/
@@ -124330,7 +155504,7 @@ static int fts3SegWriterAdd(
static int fts3SegWriterFlush(
Fts3Table *p, /* Virtual table handle */
SegmentWriter *pWriter, /* SegmentWriter to flush to the db */
- int iLevel, /* Value for 'level' column of %_segdir */
+ sqlite3_int64 iLevel, /* Value for 'level' column of %_segdir */
int iIdx /* Value for 'idx' column of %_segdir */
){
int rc; /* Return code */
@@ -124347,14 +155521,15 @@ static int fts3SegWriterFlush(
pWriter->iFirst, pWriter->iFree, &iLast, &zRoot, &nRoot);
}
if( rc==SQLITE_OK ){
- rc = fts3WriteSegdir(
- p, iLevel, iIdx, pWriter->iFirst, iLastLeaf, iLast, zRoot, nRoot);
+ rc = fts3WriteSegdir(p, iLevel, iIdx,
+ pWriter->iFirst, iLastLeaf, iLast, pWriter->nLeafData, zRoot, nRoot);
}
}else{
/* The entire tree fits on the root node. Write it to the segdir table. */
- rc = fts3WriteSegdir(
- p, iLevel, iIdx, 0, 0, 0, pWriter->aData, pWriter->nData);
+ rc = fts3WriteSegdir(p, iLevel, iIdx,
+ 0, 0, 0, pWriter->nLeafData, pWriter->aData, pWriter->nData);
}
+ p->nLeafAdd++;
return rc;
}
@@ -124384,12 +155559,18 @@ static void fts3SegWriterFree(SegmentWriter *pWriter){
static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
sqlite3_stmt *pStmt;
int rc;
- rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
- if( rc==SQLITE_OK ){
- if( SQLITE_ROW==sqlite3_step(pStmt) ){
- *pisEmpty = sqlite3_column_int(pStmt, 0);
+ if( p->zContentTbl ){
+ /* If using the content=xxx option, assume the table is never empty */
+ *pisEmpty = 0;
+ rc = SQLITE_OK;
+ }else{
+ rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, &pRowid);
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ *pisEmpty = sqlite3_column_int(pStmt, 0);
+ }
+ rc = sqlite3_reset(pStmt);
}
- rc = sqlite3_reset(pStmt);
}
return rc;
}
@@ -124402,7 +155583,12 @@ static int fts3IsEmpty(Fts3Table *p, sqlite3_value *pRowid, int *pisEmpty){
**
** Return SQLITE_OK if successful, or an SQLite error code if not.
*/
-static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){
+static int fts3SegmentMaxLevel(
+ Fts3Table *p,
+ int iLangid,
+ int iIndex,
+ sqlite3_int64 *pnMax
+){
sqlite3_stmt *pStmt;
int rc;
assert( iIndex>=0 && iIndex<p->nIndex );
@@ -124415,15 +155601,71 @@ static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){
*/
rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
if( rc!=SQLITE_OK ) return rc;
- sqlite3_bind_int(pStmt, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
- sqlite3_bind_int(pStmt, 2, (iIndex+1)*FTS3_SEGDIR_MAXLEVEL - 1);
+ sqlite3_bind_int64(pStmt, 1, getAbsoluteLevel(p, iLangid, iIndex, 0));
+ sqlite3_bind_int64(pStmt, 2,
+ getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1)
+ );
if( SQLITE_ROW==sqlite3_step(pStmt) ){
- *pnMax = sqlite3_column_int(pStmt, 0);
+ *pnMax = sqlite3_column_int64(pStmt, 0);
}
return sqlite3_reset(pStmt);
}
/*
+** iAbsLevel is an absolute level that may be assumed to exist within
+** the database. This function checks if it is the largest level number
+** within its index. Assuming no error occurs, *pbMax is set to 1 if
+** iAbsLevel is indeed the largest level, or 0 otherwise, and SQLITE_OK
+** is returned. If an error occurs, an error code is returned and the
+** final value of *pbMax is undefined.
+*/
+static int fts3SegmentIsMaxLevel(Fts3Table *p, i64 iAbsLevel, int *pbMax){
+
+ /* Set pStmt to the compiled version of:
+ **
+ ** SELECT max(level) FROM %Q.'%q_segdir' WHERE level BETWEEN ? AND ?
+ **
+ ** (1024 is actually the value of macro FTS3_SEGDIR_PREFIXLEVEL_STR).
+ */
+ sqlite3_stmt *pStmt;
+ int rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_MAX_LEVEL, &pStmt, 0);
+ if( rc!=SQLITE_OK ) return rc;
+ sqlite3_bind_int64(pStmt, 1, iAbsLevel+1);
+ sqlite3_bind_int64(pStmt, 2,
+ ((iAbsLevel/FTS3_SEGDIR_MAXLEVEL)+1) * FTS3_SEGDIR_MAXLEVEL
+ );
+
+ *pbMax = 0;
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ *pbMax = sqlite3_column_type(pStmt, 0)==SQLITE_NULL;
+ }
+ return sqlite3_reset(pStmt);
+}
+
+/*
+** Delete all entries in the %_segments table associated with the segment
+** opened with seg-reader pSeg. This function does not affect the contents
+** of the %_segdir table.
+*/
+static int fts3DeleteSegment(
+ Fts3Table *p, /* FTS table handle */
+ Fts3SegReader *pSeg /* Segment to delete */
+){
+ int rc = SQLITE_OK; /* Return code */
+ if( pSeg->iStartBlock ){
+ sqlite3_stmt *pDelete; /* SQL statement to delete rows */
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDelete, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDelete, 1, pSeg->iStartBlock);
+ sqlite3_bind_int64(pDelete, 2, pSeg->iEndBlock);
+ sqlite3_step(pDelete);
+ rc = sqlite3_reset(pDelete);
+ }
+ }
+ return rc;
+}
+
+/*
** This function is used after merging multiple segments into a single large
** segment to delete the old, now redundant, segment b-trees. Specifically,
** it:
@@ -124439,24 +155681,18 @@ static int fts3SegmentMaxLevel(Fts3Table *p, int iIndex, int *pnMax){
*/
static int fts3DeleteSegdir(
Fts3Table *p, /* Virtual table handle */
+ int iLangid, /* Language id */
int iIndex, /* Index for p->aIndex */
int iLevel, /* Level of %_segdir entries to delete */
Fts3SegReader **apSegment, /* Array of SegReader objects */
int nReader /* Size of array apSegment */
){
- int rc; /* Return Code */
+ int rc = SQLITE_OK; /* Return Code */
int i; /* Iterator variable */
- sqlite3_stmt *pDelete; /* SQL statement to delete rows */
+ sqlite3_stmt *pDelete = 0; /* SQL statement to delete rows */
- rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDelete, 0);
for(i=0; rc==SQLITE_OK && i<nReader; i++){
- Fts3SegReader *pSegment = apSegment[i];
- if( pSegment->iStartBlock ){
- sqlite3_bind_int64(pDelete, 1, pSegment->iStartBlock);
- sqlite3_bind_int64(pDelete, 2, pSegment->iEndBlock);
- sqlite3_step(pDelete);
- rc = sqlite3_reset(pDelete);
- }
+ rc = fts3DeleteSegment(p, apSegment[i]);
}
if( rc!=SQLITE_OK ){
return rc;
@@ -124466,13 +155702,17 @@ static int fts3DeleteSegdir(
if( iLevel==FTS3_SEGCURSOR_ALL ){
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_RANGE, &pDelete, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL);
- sqlite3_bind_int(pDelete, 2, (iIndex+1) * FTS3_SEGDIR_MAXLEVEL - 1);
+ sqlite3_bind_int64(pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, 0));
+ sqlite3_bind_int64(pDelete, 2,
+ getAbsoluteLevel(p, iLangid, iIndex, FTS3_SEGDIR_MAXLEVEL-1)
+ );
}
}else{
rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_LEVEL, &pDelete, 0);
if( rc==SQLITE_OK ){
- sqlite3_bind_int(pDelete, 1, iIndex*FTS3_SEGDIR_MAXLEVEL + iLevel);
+ sqlite3_bind_int64(
+ pDelete, 1, getAbsoluteLevel(p, iLangid, iIndex, iLevel)
+ );
}
}
@@ -124492,9 +155732,13 @@ static int fts3DeleteSegdir(
**
** If there are no entries in the input position list for column iCol, then
** *pnList is set to zero before returning.
+**
+** If parameter bZero is non-zero, then any part of the input list following
+** the end of the output list is zeroed before returning.
*/
static void fts3ColumnFilter(
int iCol, /* Column to filter on */
+ int bZero, /* Zero out anything following *ppList */
char **ppList, /* IN/OUT: Pointer to position list */
int *pnList /* IN/OUT: Size of buffer *ppList in bytes */
){
@@ -124520,9 +155764,12 @@ static void fts3ColumnFilter(
break;
}
p = &pList[1];
- p += sqlite3Fts3GetVarint32(p, &iCurrent);
+ p += fts3GetVarint32(p, &iCurrent);
}
+ if( bZero && &pList[nList]!=pEnd ){
+ memset(&pList[nList], 0, pEnd - &pList[nList]);
+ }
*ppList = pList;
*pnList = nList;
}
@@ -124596,19 +155843,19 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
if( rc!=SQLITE_OK ) return rc;
fts3SegReaderSort(pMsr->apSegment, nMerge, j, xCmp);
+ if( nList>0 && fts3SegReaderIsPending(apSegment[0]) ){
+ rc = fts3MsrBufferData(pMsr, pList, nList+1);
+ if( rc!=SQLITE_OK ) return rc;
+ assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 );
+ pList = pMsr->aBuffer;
+ }
+
if( pMsr->iColFilter>=0 ){
- fts3ColumnFilter(pMsr->iColFilter, &pList, &nList);
+ fts3ColumnFilter(pMsr->iColFilter, 1, &pList, &nList);
}
if( nList>0 ){
- if( fts3SegReaderIsPending(apSegment[0]) ){
- rc = fts3MsrBufferData(pMsr, pList, nList+1);
- if( rc!=SQLITE_OK ) return rc;
- *paPoslist = pMsr->aBuffer;
- assert( (pMsr->aBuffer[nList] & 0xFE)==0x00 );
- }else{
- *paPoslist = pList;
- }
+ *paPoslist = pList;
*piDocid = iDocid;
*pnPoslist = nList;
break;
@@ -124635,11 +155882,16 @@ static int fts3SegReaderStart(
** b-tree leaf nodes contain more than one term.
*/
for(i=0; pCsr->bRestart==0 && i<pCsr->nSegment; i++){
+ int res = 0;
Fts3SegReader *pSeg = pCsr->apSegment[i];
do {
int rc = fts3SegReaderNext(p, pSeg, 0);
if( rc!=SQLITE_OK ) return rc;
- }while( zTerm && fts3SegReaderTermCmp(pSeg, zTerm, nTerm)<0 );
+ }while( zTerm && (res = fts3SegReaderTermCmp(pSeg, zTerm, nTerm))<0 );
+
+ if( pSeg->bLookup && res!=0 ){
+ fts3SegReaderSetEof(pSeg);
+ }
}
fts3SegReaderSort(pCsr->apSegment, nSeg, nSeg, fts3SegReaderCmp);
@@ -124741,6 +155993,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
int isColFilter = (pCsr->pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER);
int isPrefix = (pCsr->pFilter->flags & FTS3_SEGMENT_PREFIX);
int isScan = (pCsr->pFilter->flags & FTS3_SEGMENT_SCAN);
+ int isFirst = (pCsr->pFilter->flags & FTS3_SEGMENT_FIRST);
Fts3SegReader **apSegment = pCsr->apSegment;
int nSegment = pCsr->nSegment;
@@ -124759,7 +156012,12 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
** forward. Then sort the list in order of current term again.
*/
for(i=0; i<pCsr->nAdvance; i++){
- rc = fts3SegReaderNext(p, apSegment[i], 0);
+ Fts3SegReader *pSeg = apSegment[i];
+ if( pSeg->bLookup ){
+ fts3SegReaderSetEof(pSeg);
+ }else{
+ rc = fts3SegReaderNext(p, pSeg, 0);
+ }
if( rc!=SQLITE_OK ) return rc;
}
fts3SegReaderSort(apSegment, nSegment, pCsr->nAdvance, fts3SegReaderCmp);
@@ -124800,6 +156058,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
assert( isIgnoreEmpty || (isRequirePos && !isColFilter) );
if( nMerge==1
&& !isIgnoreEmpty
+ && !isFirst
&& (p->bDescIdx==0 || fts3SegReaderIsPending(apSegment[0])==0)
){
pCsr->nDoclist = apSegment[0]->nDoclist;
@@ -124824,8 +156083,8 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
fts3SegReaderSort(apSegment, nMerge, nMerge, xCmp);
while( apSegment[0]->pOffsetList ){
int j; /* Number of segments that share a docid */
- char *pList;
- int nList;
+ char *pList = 0;
+ int nList = 0;
int nByte;
sqlite3_int64 iDocid = apSegment[0]->iDocid;
fts3SegReaderNextDocid(p, apSegment[0], &pList, &nList);
@@ -124839,7 +156098,7 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
}
if( isColFilter ){
- fts3ColumnFilter(pFilter->iCol, &pList, &nList);
+ fts3ColumnFilter(pFilter->iCol, 0, &pList, &nList);
}
if( !isIgnoreEmpty || nList>0 ){
@@ -124865,12 +156124,24 @@ SQLITE_PRIVATE int sqlite3Fts3SegReaderStep(
}
pCsr->aBuffer = aNew;
}
- nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
- iPrev = iDocid;
- if( isRequirePos ){
- memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
- nDoclist += nList;
- pCsr->aBuffer[nDoclist++] = '\0';
+
+ if( isFirst ){
+ char *a = &pCsr->aBuffer[nDoclist];
+ int nWrite;
+
+ nWrite = sqlite3Fts3FirstFilter(iDelta, pList, nList, a);
+ if( nWrite ){
+ iPrev = iDocid;
+ nDoclist += nWrite;
+ }
+ }else{
+ nDoclist += sqlite3Fts3PutVarint(&pCsr->aBuffer[nDoclist], iDelta);
+ iPrev = iDocid;
+ if( isRequirePos ){
+ memcpy(&pCsr->aBuffer[nDoclist], pList, nList);
+ nDoclist += nList;
+ pCsr->aBuffer[nDoclist++] = '\0';
+ }
}
}
@@ -124907,6 +156178,140 @@ SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(
}
/*
+** Decode the "end_block" field, selected by column iCol of the SELECT
+** statement passed as the first argument.
+**
+** The "end_block" field may contain either an integer, or a text field
+** containing the text representation of two non-negative integers separated
+** by one or more space (0x20) characters. In the first case, set *piEndBlock
+** to the integer value and *pnByte to zero before returning. In the second,
+** set *piEndBlock to the first value and *pnByte to the second.
+*/
+static void fts3ReadEndBlockField(
+ sqlite3_stmt *pStmt,
+ int iCol,
+ i64 *piEndBlock,
+ i64 *pnByte
+){
+ const unsigned char *zText = sqlite3_column_text(pStmt, iCol);
+ if( zText ){
+ int i;
+ int iMul = 1;
+ i64 iVal = 0;
+ for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
+ iVal = iVal*10 + (zText[i] - '0');
+ }
+ *piEndBlock = iVal;
+ while( zText[i]==' ' ) i++;
+ iVal = 0;
+ if( zText[i]=='-' ){
+ i++;
+ iMul = -1;
+ }
+ for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
+ iVal = iVal*10 + (zText[i] - '0');
+ }
+ *pnByte = (iVal * (i64)iMul);
+ }
+}
+
+
+/*
+** A segment of size nByte bytes has just been written to absolute level
+** iAbsLevel. Promote any segments that should be promoted as a result.
+*/
+static int fts3PromoteSegments(
+ Fts3Table *p, /* FTS table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level just updated */
+ sqlite3_int64 nByte /* Size of new segment at iAbsLevel */
+){
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pRange;
+
+ rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_RANGE2, &pRange, 0);
+
+ if( rc==SQLITE_OK ){
+ int bOk = 0;
+ i64 iLast = (iAbsLevel/FTS3_SEGDIR_MAXLEVEL + 1) * FTS3_SEGDIR_MAXLEVEL - 1;
+ i64 nLimit = (nByte*3)/2;
+
+ /* Loop through all entries in the %_segdir table corresponding to
+ ** segments in this index on levels greater than iAbsLevel. If there is
+ ** at least one such segment, and it is possible to determine that all
+ ** such segments are smaller than nLimit bytes in size, they will be
+ ** promoted to level iAbsLevel. */
+ sqlite3_bind_int64(pRange, 1, iAbsLevel+1);
+ sqlite3_bind_int64(pRange, 2, iLast);
+ while( SQLITE_ROW==sqlite3_step(pRange) ){
+ i64 nSize = 0, dummy;
+ fts3ReadEndBlockField(pRange, 2, &dummy, &nSize);
+ if( nSize<=0 || nSize>nLimit ){
+ /* If nSize==0, then the %_segdir.end_block field does not not
+ ** contain a size value. This happens if it was written by an
+ ** old version of FTS. In this case it is not possible to determine
+ ** the size of the segment, and so segment promotion does not
+ ** take place. */
+ bOk = 0;
+ break;
+ }
+ bOk = 1;
+ }
+ rc = sqlite3_reset(pRange);
+
+ if( bOk ){
+ int iIdx = 0;
+ sqlite3_stmt *pUpdate1 = 0;
+ sqlite3_stmt *pUpdate2 = 0;
+
+ if( rc==SQLITE_OK ){
+ rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL_IDX, &pUpdate1, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts3SqlStmt(p, SQL_UPDATE_LEVEL, &pUpdate2, 0);
+ }
+
+ if( rc==SQLITE_OK ){
+
+ /* Loop through all %_segdir entries for segments in this index with
+ ** levels equal to or greater than iAbsLevel. As each entry is visited,
+ ** updated it to set (level = -1) and (idx = N), where N is 0 for the
+ ** oldest segment in the range, 1 for the next oldest, and so on.
+ **
+ ** In other words, move all segments being promoted to level -1,
+ ** setting the "idx" fields as appropriate to keep them in the same
+ ** order. The contents of level -1 (which is never used, except
+ ** transiently here), will be moved back to level iAbsLevel below. */
+ sqlite3_bind_int64(pRange, 1, iAbsLevel);
+ while( SQLITE_ROW==sqlite3_step(pRange) ){
+ sqlite3_bind_int(pUpdate1, 1, iIdx++);
+ sqlite3_bind_int(pUpdate1, 2, sqlite3_column_int(pRange, 0));
+ sqlite3_bind_int(pUpdate1, 3, sqlite3_column_int(pRange, 1));
+ sqlite3_step(pUpdate1);
+ rc = sqlite3_reset(pUpdate1);
+ if( rc!=SQLITE_OK ){
+ sqlite3_reset(pRange);
+ break;
+ }
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_reset(pRange);
+ }
+
+ /* Move level -1 to level iAbsLevel */
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pUpdate2, 1, iAbsLevel);
+ sqlite3_step(pUpdate2);
+ rc = sqlite3_reset(pUpdate2);
+ }
+ }
+ }
+
+
+ return rc;
+}
+
+/*
** Merge all level iLevel segments in the database into a single
** iLevel+1 segment. Or, if iLevel<0, merge all segments into a
** single segment with a level equal to the numerically largest level
@@ -124917,14 +156322,20 @@ SQLITE_PRIVATE void sqlite3Fts3SegReaderFinish(
** Otherwise, if successful, SQLITE_OK is returned. If an error occurs,
** an SQLite error code is returned.
*/
-static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){
+static int fts3SegmentMerge(
+ Fts3Table *p,
+ int iLangid, /* Language id to merge */
+ int iIndex, /* Index in p->aIndex[] to merge */
+ int iLevel /* Level to merge */
+){
int rc; /* Return code */
int iIdx = 0; /* Index of new segment */
- int iNewLevel = 0; /* Level/index to create new segment at */
+ sqlite3_int64 iNewLevel = 0; /* Level/index to create new segment at */
SegmentWriter *pWriter = 0; /* Used to write the new, merged, segment */
Fts3SegFilter filter; /* Segment term filter condition */
- Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */
+ Fts3MultiSegReader csr; /* Cursor to iterate through level(s) */
int bIgnoreEmpty = 0; /* True to ignore empty segments */
+ i64 iMaxLevel = 0; /* Max level number for this index/langid */
assert( iLevel==FTS3_SEGCURSOR_ALL
|| iLevel==FTS3_SEGCURSOR_PENDING
@@ -124933,36 +156344,41 @@ static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){
assert( iLevel<FTS3_SEGDIR_MAXLEVEL );
assert( iIndex>=0 && iIndex<p->nIndex );
- rc = sqlite3Fts3SegReaderCursor(p, iIndex, iLevel, 0, 0, 1, 0, &csr);
+ rc = sqlite3Fts3SegReaderCursor(p, iLangid, iIndex, iLevel, 0, 0, 1, 0, &csr);
if( rc!=SQLITE_OK || csr.nSegment==0 ) goto finished;
+ if( iLevel!=FTS3_SEGCURSOR_PENDING ){
+ rc = fts3SegmentMaxLevel(p, iLangid, iIndex, &iMaxLevel);
+ if( rc!=SQLITE_OK ) goto finished;
+ }
+
if( iLevel==FTS3_SEGCURSOR_ALL ){
/* This call is to merge all segments in the database to a single
- ** segment. The level of the new segment is equal to the the numerically
+ ** segment. The level of the new segment is equal to the numerically
** greatest segment level currently present in the database for this
** index. The idx of the new segment is always 0. */
- if( csr.nSegment==1 ){
+ if( csr.nSegment==1 && 0==fts3SegReaderIsPending(csr.apSegment[0]) ){
rc = SQLITE_DONE;
goto finished;
}
- rc = fts3SegmentMaxLevel(p, iIndex, &iNewLevel);
+ iNewLevel = iMaxLevel;
bIgnoreEmpty = 1;
- }else if( iLevel==FTS3_SEGCURSOR_PENDING ){
- iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL;
- rc = fts3AllocateSegdirIdx(p, iIndex, 0, &iIdx);
}else{
/* This call is to merge all segments at level iLevel. find the next
** available segment index at level iLevel+1. The call to
** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to
** a single iLevel+2 segment if necessary. */
- rc = fts3AllocateSegdirIdx(p, iIndex, iLevel+1, &iIdx);
- iNewLevel = iIndex * FTS3_SEGDIR_MAXLEVEL + iLevel+1;
+ assert( FTS3_SEGCURSOR_PENDING==-1 );
+ iNewLevel = getAbsoluteLevel(p, iLangid, iIndex, iLevel+1);
+ rc = fts3AllocateSegdirIdx(p, iLangid, iIndex, iLevel+1, &iIdx);
+ bIgnoreEmpty = (iLevel!=FTS3_SEGCURSOR_PENDING) && (iNewLevel>iMaxLevel);
}
if( rc!=SQLITE_OK ) goto finished;
+
assert( csr.nSegment>0 );
- assert( iNewLevel>=(iIndex*FTS3_SEGDIR_MAXLEVEL) );
- assert( iNewLevel<((iIndex+1)*FTS3_SEGDIR_MAXLEVEL) );
+ assert( iNewLevel>=getAbsoluteLevel(p, iLangid, iIndex, 0) );
+ assert( iNewLevel<getAbsoluteLevel(p, iLangid, iIndex,FTS3_SEGDIR_MAXLEVEL) );
memset(&filter, 0, sizeof(Fts3SegFilter));
filter.flags = FTS3_SEGMENT_REQUIRE_POS;
@@ -124976,13 +156392,22 @@ static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){
csr.zTerm, csr.nTerm, csr.aDoclist, csr.nDoclist);
}
if( rc!=SQLITE_OK ) goto finished;
- assert( pWriter );
+ assert( pWriter || bIgnoreEmpty );
if( iLevel!=FTS3_SEGCURSOR_PENDING ){
- rc = fts3DeleteSegdir(p, iIndex, iLevel, csr.apSegment, csr.nSegment);
+ rc = fts3DeleteSegdir(
+ p, iLangid, iIndex, iLevel, csr.apSegment, csr.nSegment
+ );
if( rc!=SQLITE_OK ) goto finished;
}
- rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
+ if( pWriter ){
+ rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx);
+ if( rc==SQLITE_OK ){
+ if( iLevel==FTS3_SEGCURSOR_PENDING || iNewLevel<iMaxLevel ){
+ rc = fts3PromoteSegments(p, iNewLevel, pWriter->nLeafData);
+ }
+ }
+ }
finished:
fts3SegWriterFree(pWriter);
@@ -124992,16 +156417,38 @@ static int fts3SegmentMerge(Fts3Table *p, int iIndex, int iLevel){
/*
-** Flush the contents of pendingTerms to level 0 segments.
+** Flush the contents of pendingTerms to level 0 segments.
*/
SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){
int rc = SQLITE_OK;
int i;
+
for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
- rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_PENDING);
+ rc = fts3SegmentMerge(p, p->iPrevLangid, i, FTS3_SEGCURSOR_PENDING);
if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
sqlite3Fts3PendingTermsClear(p);
+
+ /* Determine the auto-incr-merge setting if unknown. If enabled,
+ ** estimate the number of leaf blocks of content to be written
+ */
+ if( rc==SQLITE_OK && p->bHasStat
+ && p->nAutoincrmerge==0xff && p->nLeafAdd>0
+ ){
+ sqlite3_stmt *pStmt = 0;
+ rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
+ rc = sqlite3_step(pStmt);
+ if( rc==SQLITE_ROW ){
+ p->nAutoincrmerge = sqlite3_column_int(pStmt, 0);
+ if( p->nAutoincrmerge==1 ) p->nAutoincrmerge = 8;
+ }else if( rc==SQLITE_DONE ){
+ p->nAutoincrmerge = 0;
+ }
+ rc = sqlite3_reset(pStmt);
+ }
+ }
return rc;
}
@@ -125046,9 +156493,9 @@ static void fts3DecodeIntArray(
** a blob of varints.
*/
static void fts3InsertDocsize(
- int *pRC, /* Result code */
- Fts3Table *p, /* Table into which to insert */
- u32 *aSz /* Sizes of each column */
+ int *pRC, /* Result code */
+ Fts3Table *p, /* Table into which to insert */
+ u32 *aSz /* Sizes of each column, in tokens */
){
char *pBlob; /* The BLOB encoding of the document size */
int nBlob; /* Number of bytes in the BLOB */
@@ -125112,12 +156559,13 @@ static void fts3UpdateDocTotals(
return;
}
pBlob = (char*)&a[nStat];
- rc = fts3SqlStmt(p, SQL_SELECT_DOCTOTAL, &pStmt, 0);
+ rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pStmt, 0);
if( rc ){
sqlite3_free(a);
*pRC = rc;
return;
}
+ sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL);
if( sqlite3_step(pStmt)==SQLITE_ROW ){
fts3DecodeIntArray(nStat, a,
sqlite3_column_blob(pStmt, 0),
@@ -125125,7 +156573,12 @@ static void fts3UpdateDocTotals(
}else{
memset(a, 0, sizeof(u32)*(nStat) );
}
- sqlite3_reset(pStmt);
+ rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(a);
+ *pRC = rc;
+ return;
+ }
if( nChng<0 && a[0]<(u32)(-nChng) ){
a[0] = 0;
}else{
@@ -125141,29 +156594,48 @@ static void fts3UpdateDocTotals(
a[i+1] = x;
}
fts3EncodeIntArray(nStat, a, pBlob, &nBlob);
- rc = fts3SqlStmt(p, SQL_REPLACE_DOCTOTAL, &pStmt, 0);
+ rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0);
if( rc ){
sqlite3_free(a);
*pRC = rc;
return;
}
- sqlite3_bind_blob(pStmt, 1, pBlob, nBlob, SQLITE_STATIC);
+ sqlite3_bind_int(pStmt, 1, FTS_STAT_DOCTOTAL);
+ sqlite3_bind_blob(pStmt, 2, pBlob, nBlob, SQLITE_STATIC);
sqlite3_step(pStmt);
*pRC = sqlite3_reset(pStmt);
sqlite3_free(a);
}
+/*
+** Merge the entire database so that there is one segment for each
+** iIndex/iLangid combination.
+*/
static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
- int i;
int bSeenDone = 0;
- int rc = SQLITE_OK;
- for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
- rc = fts3SegmentMerge(p, i, FTS3_SEGCURSOR_ALL);
- if( rc==SQLITE_DONE ){
- bSeenDone = 1;
- rc = SQLITE_OK;
+ int rc;
+ sqlite3_stmt *pAllLangid = 0;
+
+ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid);
+ sqlite3_bind_int(pAllLangid, 2, p->nIndex);
+ while( sqlite3_step(pAllLangid)==SQLITE_ROW ){
+ int i;
+ int iLangid = sqlite3_column_int(pAllLangid, 0);
+ for(i=0; rc==SQLITE_OK && i<p->nIndex; i++){
+ rc = fts3SegmentMerge(p, iLangid, i, FTS3_SEGCURSOR_ALL);
+ if( rc==SQLITE_DONE ){
+ bSeenDone = 1;
+ rc = SQLITE_OK;
+ }
+ }
}
+ rc2 = sqlite3_reset(pAllLangid);
+ if( rc==SQLITE_OK ) rc = rc2;
}
+
sqlite3Fts3SegmentsClose(p);
sqlite3Fts3PendingTermsClear(p);
@@ -125171,6 +156643,1799 @@ static int fts3DoOptimize(Fts3Table *p, int bReturnDone){
}
/*
+** This function is called when the user executes the following statement:
+**
+** INSERT INTO <tbl>(<tbl>) VALUES('rebuild');
+**
+** The entire FTS index is discarded and rebuilt. If the table is one
+** created using the content=xxx option, then the new index is based on
+** the current contents of the xxx table. Otherwise, it is rebuilt based
+** on the contents of the %_content table.
+*/
+static int fts3DoRebuild(Fts3Table *p){
+ int rc; /* Return Code */
+
+ rc = fts3DeleteAll(p, 0);
+ if( rc==SQLITE_OK ){
+ u32 *aSz = 0;
+ u32 *aSzIns = 0;
+ u32 *aSzDel = 0;
+ sqlite3_stmt *pStmt = 0;
+ int nEntry = 0;
+
+ /* Compose and prepare an SQL statement to loop through the content table */
+ char *zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ }
+
+ if( rc==SQLITE_OK ){
+ int nByte = sizeof(u32) * (p->nColumn+1)*3;
+ aSz = (u32 *)sqlite3_malloc(nByte);
+ if( aSz==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(aSz, 0, nByte);
+ aSzIns = &aSz[p->nColumn+1];
+ aSzDel = &aSzIns[p->nColumn+1];
+ }
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ int iCol;
+ int iLangid = langidFromSelect(p, pStmt);
+ rc = fts3PendingTermsDocid(p, 0, iLangid, sqlite3_column_int64(pStmt, 0));
+ memset(aSz, 0, sizeof(aSz[0]) * (p->nColumn+1));
+ for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
+ if( p->abNotindexed[iCol]==0 ){
+ const char *z = (const char *) sqlite3_column_text(pStmt, iCol+1);
+ rc = fts3PendingTermsAdd(p, iLangid, z, iCol, &aSz[iCol]);
+ aSz[p->nColumn] += sqlite3_column_bytes(pStmt, iCol+1);
+ }
+ }
+ if( p->bHasDocsize ){
+ fts3InsertDocsize(&rc, p, aSz);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+ }else{
+ nEntry++;
+ for(iCol=0; iCol<=p->nColumn; iCol++){
+ aSzIns[iCol] += aSz[iCol];
+ }
+ }
+ }
+ if( p->bFts4 ){
+ fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nEntry);
+ }
+ sqlite3_free(aSz);
+
+ if( pStmt ){
+ int rc2 = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ){
+ rc = rc2;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+/*
+** This function opens a cursor used to read the input data for an
+** incremental merge operation. Specifically, it opens a cursor to scan
+** the oldest nSeg segments (idx=0 through idx=(nSeg-1)) in absolute
+** level iAbsLevel.
+*/
+static int fts3IncrmergeCsr(
+ Fts3Table *p, /* FTS3 table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level to open */
+ int nSeg, /* Number of segments to merge */
+ Fts3MultiSegReader *pCsr /* Cursor object to populate */
+){
+ int rc; /* Return Code */
+ sqlite3_stmt *pStmt = 0; /* Statement used to read %_segdir entry */
+ int nByte; /* Bytes allocated at pCsr->apSegment[] */
+
+ /* Allocate space for the Fts3MultiSegReader.aCsr[] array */
+ memset(pCsr, 0, sizeof(*pCsr));
+ nByte = sizeof(Fts3SegReader *) * nSeg;
+ pCsr->apSegment = (Fts3SegReader **)sqlite3_malloc(nByte);
+
+ if( pCsr->apSegment==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pCsr->apSegment, 0, nByte);
+ rc = fts3SqlStmt(p, SQL_SELECT_LEVEL, &pStmt, 0);
+ }
+ if( rc==SQLITE_OK ){
+ int i;
+ int rc2;
+ sqlite3_bind_int64(pStmt, 1, iAbsLevel);
+ assert( pCsr->nSegment==0 );
+ for(i=0; rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW && i<nSeg; i++){
+ rc = sqlite3Fts3SegReaderNew(i, 0,
+ sqlite3_column_int64(pStmt, 1), /* segdir.start_block */
+ sqlite3_column_int64(pStmt, 2), /* segdir.leaves_end_block */
+ sqlite3_column_int64(pStmt, 3), /* segdir.end_block */
+ sqlite3_column_blob(pStmt, 4), /* segdir.root */
+ sqlite3_column_bytes(pStmt, 4), /* segdir.root */
+ &pCsr->apSegment[i]
+ );
+ pCsr->nSegment++;
+ }
+ rc2 = sqlite3_reset(pStmt);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ return rc;
+}
+
+typedef struct IncrmergeWriter IncrmergeWriter;
+typedef struct NodeWriter NodeWriter;
+typedef struct Blob Blob;
+typedef struct NodeReader NodeReader;
+
+/*
+** An instance of the following structure is used as a dynamic buffer
+** to build up nodes or other blobs of data in.
+**
+** The function blobGrowBuffer() is used to extend the allocation.
+*/
+struct Blob {
+ char *a; /* Pointer to allocation */
+ int n; /* Number of valid bytes of data in a[] */
+ int nAlloc; /* Allocated size of a[] (nAlloc>=n) */
+};
+
+/*
+** This structure is used to build up buffers containing segment b-tree
+** nodes (blocks).
+*/
+struct NodeWriter {
+ sqlite3_int64 iBlock; /* Current block id */
+ Blob key; /* Last key written to the current block */
+ Blob block; /* Current block image */
+};
+
+/*
+** An object of this type contains the state required to create or append
+** to an appendable b-tree segment.
+*/
+struct IncrmergeWriter {
+ int nLeafEst; /* Space allocated for leaf blocks */
+ int nWork; /* Number of leaf pages flushed */
+ sqlite3_int64 iAbsLevel; /* Absolute level of input segments */
+ int iIdx; /* Index of *output* segment in iAbsLevel+1 */
+ sqlite3_int64 iStart; /* Block number of first allocated block */
+ sqlite3_int64 iEnd; /* Block number of last allocated block */
+ sqlite3_int64 nLeafData; /* Bytes of leaf page data so far */
+ u8 bNoLeafData; /* If true, store 0 for segment size */
+ NodeWriter aNodeWriter[FTS_MAX_APPENDABLE_HEIGHT];
+};
+
+/*
+** An object of the following type is used to read data from a single
+** FTS segment node. See the following functions:
+**
+** nodeReaderInit()
+** nodeReaderNext()
+** nodeReaderRelease()
+*/
+struct NodeReader {
+ const char *aNode;
+ int nNode;
+ int iOff; /* Current offset within aNode[] */
+
+ /* Output variables. Containing the current node entry. */
+ sqlite3_int64 iChild; /* Pointer to child node */
+ Blob term; /* Current term */
+ const char *aDoclist; /* Pointer to doclist */
+ int nDoclist; /* Size of doclist in bytes */
+};
+
+/*
+** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
+** Otherwise, if the allocation at pBlob->a is not already at least nMin
+** bytes in size, extend (realloc) it to be so.
+**
+** If an OOM error occurs, set *pRc to SQLITE_NOMEM and leave pBlob->a
+** unmodified. Otherwise, if the allocation succeeds, update pBlob->nAlloc
+** to reflect the new size of the pBlob->a[] buffer.
+*/
+static void blobGrowBuffer(Blob *pBlob, int nMin, int *pRc){
+ if( *pRc==SQLITE_OK && nMin>pBlob->nAlloc ){
+ int nAlloc = nMin;
+ char *a = (char *)sqlite3_realloc(pBlob->a, nAlloc);
+ if( a ){
+ pBlob->nAlloc = nAlloc;
+ pBlob->a = a;
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+}
+
+/*
+** Attempt to advance the node-reader object passed as the first argument to
+** the next entry on the node.
+**
+** Return an error code if an error occurs (SQLITE_NOMEM is possible).
+** Otherwise return SQLITE_OK. If there is no next entry on the node
+** (e.g. because the current entry is the last) set NodeReader->aNode to
+** NULL to indicate EOF. Otherwise, populate the NodeReader structure output
+** variables for the new entry.
+*/
+static int nodeReaderNext(NodeReader *p){
+ int bFirst = (p->term.n==0); /* True for first term on the node */
+ int nPrefix = 0; /* Bytes to copy from previous term */
+ int nSuffix = 0; /* Bytes to append to the prefix */
+ int rc = SQLITE_OK; /* Return code */
+
+ assert( p->aNode );
+ if( p->iChild && bFirst==0 ) p->iChild++;
+ if( p->iOff>=p->nNode ){
+ /* EOF */
+ p->aNode = 0;
+ }else{
+ if( bFirst==0 ){
+ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nPrefix);
+ }
+ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &nSuffix);
+
+ blobGrowBuffer(&p->term, nPrefix+nSuffix, &rc);
+ if( rc==SQLITE_OK ){
+ memcpy(&p->term.a[nPrefix], &p->aNode[p->iOff], nSuffix);
+ p->term.n = nPrefix+nSuffix;
+ p->iOff += nSuffix;
+ if( p->iChild==0 ){
+ p->iOff += fts3GetVarint32(&p->aNode[p->iOff], &p->nDoclist);
+ p->aDoclist = &p->aNode[p->iOff];
+ p->iOff += p->nDoclist;
+ }
+ }
+ }
+
+ assert( p->iOff<=p->nNode );
+
+ return rc;
+}
+
+/*
+** Release all dynamic resources held by node-reader object *p.
+*/
+static void nodeReaderRelease(NodeReader *p){
+ sqlite3_free(p->term.a);
+}
+
+/*
+** Initialize a node-reader object to read the node in buffer aNode/nNode.
+**
+** If successful, SQLITE_OK is returned and the NodeReader object set to
+** point to the first entry on the node (if any). Otherwise, an SQLite
+** error code is returned.
+*/
+static int nodeReaderInit(NodeReader *p, const char *aNode, int nNode){
+ memset(p, 0, sizeof(NodeReader));
+ p->aNode = aNode;
+ p->nNode = nNode;
+
+ /* Figure out if this is a leaf or an internal node. */
+ if( p->aNode[0] ){
+ /* An internal node. */
+ p->iOff = 1 + sqlite3Fts3GetVarint(&p->aNode[1], &p->iChild);
+ }else{
+ p->iOff = 1;
+ }
+
+ return nodeReaderNext(p);
+}
+
+/*
+** This function is called while writing an FTS segment each time a leaf o
+** node is finished and written to disk. The key (zTerm/nTerm) is guaranteed
+** to be greater than the largest key on the node just written, but smaller
+** than or equal to the first key that will be written to the next leaf
+** node.
+**
+** The block id of the leaf node just written to disk may be found in
+** (pWriter->aNodeWriter[0].iBlock) when this function is called.
+*/
+static int fts3IncrmergePush(
+ Fts3Table *p, /* Fts3 table handle */
+ IncrmergeWriter *pWriter, /* Writer object */
+ const char *zTerm, /* Term to write to internal node */
+ int nTerm /* Bytes at zTerm */
+){
+ sqlite3_int64 iPtr = pWriter->aNodeWriter[0].iBlock;
+ int iLayer;
+
+ assert( nTerm>0 );
+ for(iLayer=1; ALWAYS(iLayer<FTS_MAX_APPENDABLE_HEIGHT); iLayer++){
+ sqlite3_int64 iNextPtr = 0;
+ NodeWriter *pNode = &pWriter->aNodeWriter[iLayer];
+ int rc = SQLITE_OK;
+ int nPrefix;
+ int nSuffix;
+ int nSpace;
+
+ /* Figure out how much space the key will consume if it is written to
+ ** the current node of layer iLayer. Due to the prefix compression,
+ ** the space required changes depending on which node the key is to
+ ** be added to. */
+ nPrefix = fts3PrefixCompress(pNode->key.a, pNode->key.n, zTerm, nTerm);
+ nSuffix = nTerm - nPrefix;
+ nSpace = sqlite3Fts3VarintLen(nPrefix);
+ nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
+
+ if( pNode->key.n==0 || (pNode->block.n + nSpace)<=p->nNodeSize ){
+ /* If the current node of layer iLayer contains zero keys, or if adding
+ ** the key to it will not cause it to grow to larger than nNodeSize
+ ** bytes in size, write the key here. */
+
+ Blob *pBlk = &pNode->block;
+ if( pBlk->n==0 ){
+ blobGrowBuffer(pBlk, p->nNodeSize, &rc);
+ if( rc==SQLITE_OK ){
+ pBlk->a[0] = (char)iLayer;
+ pBlk->n = 1 + sqlite3Fts3PutVarint(&pBlk->a[1], iPtr);
+ }
+ }
+ blobGrowBuffer(pBlk, pBlk->n + nSpace, &rc);
+ blobGrowBuffer(&pNode->key, nTerm, &rc);
+
+ if( rc==SQLITE_OK ){
+ if( pNode->key.n ){
+ pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nPrefix);
+ }
+ pBlk->n += sqlite3Fts3PutVarint(&pBlk->a[pBlk->n], nSuffix);
+ memcpy(&pBlk->a[pBlk->n], &zTerm[nPrefix], nSuffix);
+ pBlk->n += nSuffix;
+
+ memcpy(pNode->key.a, zTerm, nTerm);
+ pNode->key.n = nTerm;
+ }
+ }else{
+ /* Otherwise, flush the current node of layer iLayer to disk.
+ ** Then allocate a new, empty sibling node. The key will be written
+ ** into the parent of this node. */
+ rc = fts3WriteSegment(p, pNode->iBlock, pNode->block.a, pNode->block.n);
+
+ assert( pNode->block.nAlloc>=p->nNodeSize );
+ pNode->block.a[0] = (char)iLayer;
+ pNode->block.n = 1 + sqlite3Fts3PutVarint(&pNode->block.a[1], iPtr+1);
+
+ iNextPtr = pNode->iBlock;
+ pNode->iBlock++;
+ pNode->key.n = 0;
+ }
+
+ if( rc!=SQLITE_OK || iNextPtr==0 ) return rc;
+ iPtr = iNextPtr;
+ }
+
+ assert( 0 );
+ return 0;
+}
+
+/*
+** Append a term and (optionally) doclist to the FTS segment node currently
+** stored in blob *pNode. The node need not contain any terms, but the
+** header must be written before this function is called.
+**
+** A node header is a single 0x00 byte for a leaf node, or a height varint
+** followed by the left-hand-child varint for an internal node.
+**
+** The term to be appended is passed via arguments zTerm/nTerm. For a
+** leaf node, the doclist is passed as aDoclist/nDoclist. For an internal
+** node, both aDoclist and nDoclist must be passed 0.
+**
+** If the size of the value in blob pPrev is zero, then this is the first
+** term written to the node. Otherwise, pPrev contains a copy of the
+** previous term. Before this function returns, it is updated to contain a
+** copy of zTerm/nTerm.
+**
+** It is assumed that the buffer associated with pNode is already large
+** enough to accommodate the new entry. The buffer associated with pPrev
+** is extended by this function if requrired.
+**
+** If an error (i.e. OOM condition) occurs, an SQLite error code is
+** returned. Otherwise, SQLITE_OK.
+*/
+static int fts3AppendToNode(
+ Blob *pNode, /* Current node image to append to */
+ Blob *pPrev, /* Buffer containing previous term written */
+ const char *zTerm, /* New term to write */
+ int nTerm, /* Size of zTerm in bytes */
+ const char *aDoclist, /* Doclist (or NULL) to write */
+ int nDoclist /* Size of aDoclist in bytes */
+){
+ int rc = SQLITE_OK; /* Return code */
+ int bFirst = (pPrev->n==0); /* True if this is the first term written */
+ int nPrefix; /* Size of term prefix in bytes */
+ int nSuffix; /* Size of term suffix in bytes */
+
+ /* Node must have already been started. There must be a doclist for a
+ ** leaf node, and there must not be a doclist for an internal node. */
+ assert( pNode->n>0 );
+ assert( (pNode->a[0]=='\0')==(aDoclist!=0) );
+
+ blobGrowBuffer(pPrev, nTerm, &rc);
+ if( rc!=SQLITE_OK ) return rc;
+
+ nPrefix = fts3PrefixCompress(pPrev->a, pPrev->n, zTerm, nTerm);
+ nSuffix = nTerm - nPrefix;
+ memcpy(pPrev->a, zTerm, nTerm);
+ pPrev->n = nTerm;
+
+ if( bFirst==0 ){
+ pNode->n += sqlite3Fts3PutVarint(&pNode->a[pNode->n], nPrefix);
+ }
+ pNode->n += sqlite3Fts3PutVarint(&pNode->a[pNode->n], nSuffix);
+ memcpy(&pNode->a[pNode->n], &zTerm[nPrefix], nSuffix);
+ pNode->n += nSuffix;
+
+ if( aDoclist ){
+ pNode->n += sqlite3Fts3PutVarint(&pNode->a[pNode->n], nDoclist);
+ memcpy(&pNode->a[pNode->n], aDoclist, nDoclist);
+ pNode->n += nDoclist;
+ }
+
+ assert( pNode->n<=pNode->nAlloc );
+
+ return SQLITE_OK;
+}
+
+/*
+** Append the current term and doclist pointed to by cursor pCsr to the
+** appendable b-tree segment opened for writing by pWriter.
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
+*/
+static int fts3IncrmergeAppend(
+ Fts3Table *p, /* Fts3 table handle */
+ IncrmergeWriter *pWriter, /* Writer object */
+ Fts3MultiSegReader *pCsr /* Cursor containing term and doclist */
+){
+ const char *zTerm = pCsr->zTerm;
+ int nTerm = pCsr->nTerm;
+ const char *aDoclist = pCsr->aDoclist;
+ int nDoclist = pCsr->nDoclist;
+ int rc = SQLITE_OK; /* Return code */
+ int nSpace; /* Total space in bytes required on leaf */
+ int nPrefix; /* Size of prefix shared with previous term */
+ int nSuffix; /* Size of suffix (nTerm - nPrefix) */
+ NodeWriter *pLeaf; /* Object used to write leaf nodes */
+
+ pLeaf = &pWriter->aNodeWriter[0];
+ nPrefix = fts3PrefixCompress(pLeaf->key.a, pLeaf->key.n, zTerm, nTerm);
+ nSuffix = nTerm - nPrefix;
+
+ nSpace = sqlite3Fts3VarintLen(nPrefix);
+ nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
+ nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
+
+ /* If the current block is not empty, and if adding this term/doclist
+ ** to the current block would make it larger than Fts3Table.nNodeSize
+ ** bytes, write this block out to the database. */
+ if( pLeaf->block.n>0 && (pLeaf->block.n + nSpace)>p->nNodeSize ){
+ rc = fts3WriteSegment(p, pLeaf->iBlock, pLeaf->block.a, pLeaf->block.n);
+ pWriter->nWork++;
+
+ /* Add the current term to the parent node. The term added to the
+ ** parent must:
+ **
+ ** a) be greater than the largest term on the leaf node just written
+ ** to the database (still available in pLeaf->key), and
+ **
+ ** b) be less than or equal to the term about to be added to the new
+ ** leaf node (zTerm/nTerm).
+ **
+ ** In other words, it must be the prefix of zTerm 1 byte longer than
+ ** the common prefix (if any) of zTerm and pWriter->zTerm.
+ */
+ if( rc==SQLITE_OK ){
+ rc = fts3IncrmergePush(p, pWriter, zTerm, nPrefix+1);
+ }
+
+ /* Advance to the next output block */
+ pLeaf->iBlock++;
+ pLeaf->key.n = 0;
+ pLeaf->block.n = 0;
+
+ nSuffix = nTerm;
+ nSpace = 1;
+ nSpace += sqlite3Fts3VarintLen(nSuffix) + nSuffix;
+ nSpace += sqlite3Fts3VarintLen(nDoclist) + nDoclist;
+ }
+
+ pWriter->nLeafData += nSpace;
+ blobGrowBuffer(&pLeaf->block, pLeaf->block.n + nSpace, &rc);
+ if( rc==SQLITE_OK ){
+ if( pLeaf->block.n==0 ){
+ pLeaf->block.n = 1;
+ pLeaf->block.a[0] = '\0';
+ }
+ rc = fts3AppendToNode(
+ &pLeaf->block, &pLeaf->key, zTerm, nTerm, aDoclist, nDoclist
+ );
+ }
+
+ return rc;
+}
+
+/*
+** This function is called to release all dynamic resources held by the
+** merge-writer object pWriter, and if no error has occurred, to flush
+** all outstanding node buffers held by pWriter to disk.
+**
+** If *pRc is not SQLITE_OK when this function is called, then no attempt
+** is made to write any data to disk. Instead, this function serves only
+** to release outstanding resources.
+**
+** Otherwise, if *pRc is initially SQLITE_OK and an error occurs while
+** flushing buffers to disk, *pRc is set to an SQLite error code before
+** returning.
+*/
+static void fts3IncrmergeRelease(
+ Fts3Table *p, /* FTS3 table handle */
+ IncrmergeWriter *pWriter, /* Merge-writer object */
+ int *pRc /* IN/OUT: Error code */
+){
+ int i; /* Used to iterate through non-root layers */
+ int iRoot; /* Index of root in pWriter->aNodeWriter */
+ NodeWriter *pRoot; /* NodeWriter for root node */
+ int rc = *pRc; /* Error code */
+
+ /* Set iRoot to the index in pWriter->aNodeWriter[] of the output segment
+ ** root node. If the segment fits entirely on a single leaf node, iRoot
+ ** will be set to 0. If the root node is the parent of the leaves, iRoot
+ ** will be 1. And so on. */
+ for(iRoot=FTS_MAX_APPENDABLE_HEIGHT-1; iRoot>=0; iRoot--){
+ NodeWriter *pNode = &pWriter->aNodeWriter[iRoot];
+ if( pNode->block.n>0 ) break;
+ assert( *pRc || pNode->block.nAlloc==0 );
+ assert( *pRc || pNode->key.nAlloc==0 );
+ sqlite3_free(pNode->block.a);
+ sqlite3_free(pNode->key.a);
+ }
+
+ /* Empty output segment. This is a no-op. */
+ if( iRoot<0 ) return;
+
+ /* The entire output segment fits on a single node. Normally, this means
+ ** the node would be stored as a blob in the "root" column of the %_segdir
+ ** table. However, this is not permitted in this case. The problem is that
+ ** space has already been reserved in the %_segments table, and so the
+ ** start_block and end_block fields of the %_segdir table must be populated.
+ ** And, by design or by accident, released versions of FTS cannot handle
+ ** segments that fit entirely on the root node with start_block!=0.
+ **
+ ** Instead, create a synthetic root node that contains nothing but a
+ ** pointer to the single content node. So that the segment consists of a
+ ** single leaf and a single interior (root) node.
+ **
+ ** Todo: Better might be to defer allocating space in the %_segments
+ ** table until we are sure it is needed.
+ */
+ if( iRoot==0 ){
+ Blob *pBlock = &pWriter->aNodeWriter[1].block;
+ blobGrowBuffer(pBlock, 1 + FTS3_VARINT_MAX, &rc);
+ if( rc==SQLITE_OK ){
+ pBlock->a[0] = 0x01;
+ pBlock->n = 1 + sqlite3Fts3PutVarint(
+ &pBlock->a[1], pWriter->aNodeWriter[0].iBlock
+ );
+ }
+ iRoot = 1;
+ }
+ pRoot = &pWriter->aNodeWriter[iRoot];
+
+ /* Flush all currently outstanding nodes to disk. */
+ for(i=0; i<iRoot; i++){
+ NodeWriter *pNode = &pWriter->aNodeWriter[i];
+ if( pNode->block.n>0 && rc==SQLITE_OK ){
+ rc = fts3WriteSegment(p, pNode->iBlock, pNode->block.a, pNode->block.n);
+ }
+ sqlite3_free(pNode->block.a);
+ sqlite3_free(pNode->key.a);
+ }
+
+ /* Write the %_segdir record. */
+ if( rc==SQLITE_OK ){
+ rc = fts3WriteSegdir(p,
+ pWriter->iAbsLevel+1, /* level */
+ pWriter->iIdx, /* idx */
+ pWriter->iStart, /* start_block */
+ pWriter->aNodeWriter[0].iBlock, /* leaves_end_block */
+ pWriter->iEnd, /* end_block */
+ (pWriter->bNoLeafData==0 ? pWriter->nLeafData : 0), /* end_block */
+ pRoot->block.a, pRoot->block.n /* root */
+ );
+ }
+ sqlite3_free(pRoot->block.a);
+ sqlite3_free(pRoot->key.a);
+
+ *pRc = rc;
+}
+
+/*
+** Compare the term in buffer zLhs (size in bytes nLhs) with that in
+** zRhs (size in bytes nRhs) using memcmp. If one term is a prefix of
+** the other, it is considered to be smaller than the other.
+**
+** Return -ve if zLhs is smaller than zRhs, 0 if it is equal, or +ve
+** if it is greater.
+*/
+static int fts3TermCmp(
+ const char *zLhs, int nLhs, /* LHS of comparison */
+ const char *zRhs, int nRhs /* RHS of comparison */
+){
+ int nCmp = MIN(nLhs, nRhs);
+ int res;
+
+ res = memcmp(zLhs, zRhs, nCmp);
+ if( res==0 ) res = nLhs - nRhs;
+
+ return res;
+}
+
+
+/*
+** Query to see if the entry in the %_segments table with blockid iEnd is
+** NULL. If no error occurs and the entry is NULL, set *pbRes 1 before
+** returning. Otherwise, set *pbRes to 0.
+**
+** Or, if an error occurs while querying the database, return an SQLite
+** error code. The final value of *pbRes is undefined in this case.
+**
+** This is used to test if a segment is an "appendable" segment. If it
+** is, then a NULL entry has been inserted into the %_segments table
+** with blockid %_segdir.end_block.
+*/
+static int fts3IsAppendable(Fts3Table *p, sqlite3_int64 iEnd, int *pbRes){
+ int bRes = 0; /* Result to set *pbRes to */
+ sqlite3_stmt *pCheck = 0; /* Statement to query database with */
+ int rc; /* Return code */
+
+ rc = fts3SqlStmt(p, SQL_SEGMENT_IS_APPENDABLE, &pCheck, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pCheck, 1, iEnd);
+ if( SQLITE_ROW==sqlite3_step(pCheck) ) bRes = 1;
+ rc = sqlite3_reset(pCheck);
+ }
+
+ *pbRes = bRes;
+ return rc;
+}
+
+/*
+** This function is called when initializing an incremental-merge operation.
+** It checks if the existing segment with index value iIdx at absolute level
+** (iAbsLevel+1) can be appended to by the incremental merge. If it can, the
+** merge-writer object *pWriter is initialized to write to it.
+**
+** An existing segment can be appended to by an incremental merge if:
+**
+** * It was initially created as an appendable segment (with all required
+** space pre-allocated), and
+**
+** * The first key read from the input (arguments zKey and nKey) is
+** greater than the largest key currently stored in the potential
+** output segment.
+*/
+static int fts3IncrmergeLoad(
+ Fts3Table *p, /* Fts3 table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level of input segments */
+ int iIdx, /* Index of candidate output segment */
+ const char *zKey, /* First key to write */
+ int nKey, /* Number of bytes in nKey */
+ IncrmergeWriter *pWriter /* Populate this object */
+){
+ int rc; /* Return code */
+ sqlite3_stmt *pSelect = 0; /* SELECT to read %_segdir entry */
+
+ rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR, &pSelect, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_int64 iStart = 0; /* Value of %_segdir.start_block */
+ sqlite3_int64 iLeafEnd = 0; /* Value of %_segdir.leaves_end_block */
+ sqlite3_int64 iEnd = 0; /* Value of %_segdir.end_block */
+ const char *aRoot = 0; /* Pointer to %_segdir.root buffer */
+ int nRoot = 0; /* Size of aRoot[] in bytes */
+ int rc2; /* Return code from sqlite3_reset() */
+ int bAppendable = 0; /* Set to true if segment is appendable */
+
+ /* Read the %_segdir entry for index iIdx absolute level (iAbsLevel+1) */
+ sqlite3_bind_int64(pSelect, 1, iAbsLevel+1);
+ sqlite3_bind_int(pSelect, 2, iIdx);
+ if( sqlite3_step(pSelect)==SQLITE_ROW ){
+ iStart = sqlite3_column_int64(pSelect, 1);
+ iLeafEnd = sqlite3_column_int64(pSelect, 2);
+ fts3ReadEndBlockField(pSelect, 3, &iEnd, &pWriter->nLeafData);
+ if( pWriter->nLeafData<0 ){
+ pWriter->nLeafData = pWriter->nLeafData * -1;
+ }
+ pWriter->bNoLeafData = (pWriter->nLeafData==0);
+ nRoot = sqlite3_column_bytes(pSelect, 4);
+ aRoot = sqlite3_column_blob(pSelect, 4);
+ }else{
+ return sqlite3_reset(pSelect);
+ }
+
+ /* Check for the zero-length marker in the %_segments table */
+ rc = fts3IsAppendable(p, iEnd, &bAppendable);
+
+ /* Check that zKey/nKey is larger than the largest key the candidate */
+ if( rc==SQLITE_OK && bAppendable ){
+ char *aLeaf = 0;
+ int nLeaf = 0;
+
+ rc = sqlite3Fts3ReadBlock(p, iLeafEnd, &aLeaf, &nLeaf, 0);
+ if( rc==SQLITE_OK ){
+ NodeReader reader;
+ for(rc = nodeReaderInit(&reader, aLeaf, nLeaf);
+ rc==SQLITE_OK && reader.aNode;
+ rc = nodeReaderNext(&reader)
+ ){
+ assert( reader.aNode );
+ }
+ if( fts3TermCmp(zKey, nKey, reader.term.a, reader.term.n)<=0 ){
+ bAppendable = 0;
+ }
+ nodeReaderRelease(&reader);
+ }
+ sqlite3_free(aLeaf);
+ }
+
+ if( rc==SQLITE_OK && bAppendable ){
+ /* It is possible to append to this segment. Set up the IncrmergeWriter
+ ** object to do so. */
+ int i;
+ int nHeight = (int)aRoot[0];
+ NodeWriter *pNode;
+
+ pWriter->nLeafEst = (int)((iEnd - iStart) + 1)/FTS_MAX_APPENDABLE_HEIGHT;
+ pWriter->iStart = iStart;
+ pWriter->iEnd = iEnd;
+ pWriter->iAbsLevel = iAbsLevel;
+ pWriter->iIdx = iIdx;
+
+ for(i=nHeight+1; i<FTS_MAX_APPENDABLE_HEIGHT; i++){
+ pWriter->aNodeWriter[i].iBlock = pWriter->iStart + i*pWriter->nLeafEst;
+ }
+
+ pNode = &pWriter->aNodeWriter[nHeight];
+ pNode->iBlock = pWriter->iStart + pWriter->nLeafEst*nHeight;
+ blobGrowBuffer(&pNode->block, MAX(nRoot, p->nNodeSize), &rc);
+ if( rc==SQLITE_OK ){
+ memcpy(pNode->block.a, aRoot, nRoot);
+ pNode->block.n = nRoot;
+ }
+
+ for(i=nHeight; i>=0 && rc==SQLITE_OK; i--){
+ NodeReader reader;
+ pNode = &pWriter->aNodeWriter[i];
+
+ rc = nodeReaderInit(&reader, pNode->block.a, pNode->block.n);
+ while( reader.aNode && rc==SQLITE_OK ) rc = nodeReaderNext(&reader);
+ blobGrowBuffer(&pNode->key, reader.term.n, &rc);
+ if( rc==SQLITE_OK ){
+ memcpy(pNode->key.a, reader.term.a, reader.term.n);
+ pNode->key.n = reader.term.n;
+ if( i>0 ){
+ char *aBlock = 0;
+ int nBlock = 0;
+ pNode = &pWriter->aNodeWriter[i-1];
+ pNode->iBlock = reader.iChild;
+ rc = sqlite3Fts3ReadBlock(p, reader.iChild, &aBlock, &nBlock, 0);
+ blobGrowBuffer(&pNode->block, MAX(nBlock, p->nNodeSize), &rc);
+ if( rc==SQLITE_OK ){
+ memcpy(pNode->block.a, aBlock, nBlock);
+ pNode->block.n = nBlock;
+ }
+ sqlite3_free(aBlock);
+ }
+ }
+ nodeReaderRelease(&reader);
+ }
+ }
+
+ rc2 = sqlite3_reset(pSelect);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ return rc;
+}
+
+/*
+** Determine the largest segment index value that exists within absolute
+** level iAbsLevel+1. If no error occurs, set *piIdx to this value plus
+** one before returning SQLITE_OK. Or, if there are no segments at all
+** within level iAbsLevel, set *piIdx to zero.
+**
+** If an error occurs, return an SQLite error code. The final value of
+** *piIdx is undefined in this case.
+*/
+static int fts3IncrmergeOutputIdx(
+ Fts3Table *p, /* FTS Table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute index of input segments */
+ int *piIdx /* OUT: Next free index at iAbsLevel+1 */
+){
+ int rc;
+ sqlite3_stmt *pOutputIdx = 0; /* SQL used to find output index */
+
+ rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pOutputIdx, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pOutputIdx, 1, iAbsLevel+1);
+ sqlite3_step(pOutputIdx);
+ *piIdx = sqlite3_column_int(pOutputIdx, 0);
+ rc = sqlite3_reset(pOutputIdx);
+ }
+
+ return rc;
+}
+
+/*
+** Allocate an appendable output segment on absolute level iAbsLevel+1
+** with idx value iIdx.
+**
+** In the %_segdir table, a segment is defined by the values in three
+** columns:
+**
+** start_block
+** leaves_end_block
+** end_block
+**
+** When an appendable segment is allocated, it is estimated that the
+** maximum number of leaf blocks that may be required is the sum of the
+** number of leaf blocks consumed by the input segments, plus the number
+** of input segments, multiplied by two. This value is stored in stack
+** variable nLeafEst.
+**
+** A total of 16*nLeafEst blocks are allocated when an appendable segment
+** is created ((1 + end_block - start_block)==16*nLeafEst). The contiguous
+** array of leaf nodes starts at the first block allocated. The array
+** of interior nodes that are parents of the leaf nodes start at block
+** (start_block + (1 + end_block - start_block) / 16). And so on.
+**
+** In the actual code below, the value "16" is replaced with the
+** pre-processor macro FTS_MAX_APPENDABLE_HEIGHT.
+*/
+static int fts3IncrmergeWriter(
+ Fts3Table *p, /* Fts3 table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level of input segments */
+ int iIdx, /* Index of new output segment */
+ Fts3MultiSegReader *pCsr, /* Cursor that data will be read from */
+ IncrmergeWriter *pWriter /* Populate this object */
+){
+ int rc; /* Return Code */
+ int i; /* Iterator variable */
+ int nLeafEst = 0; /* Blocks allocated for leaf nodes */
+ sqlite3_stmt *pLeafEst = 0; /* SQL used to determine nLeafEst */
+ sqlite3_stmt *pFirstBlock = 0; /* SQL used to determine first block */
+
+ /* Calculate nLeafEst. */
+ rc = fts3SqlStmt(p, SQL_MAX_LEAF_NODE_ESTIMATE, &pLeafEst, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pLeafEst, 1, iAbsLevel);
+ sqlite3_bind_int64(pLeafEst, 2, pCsr->nSegment);
+ if( SQLITE_ROW==sqlite3_step(pLeafEst) ){
+ nLeafEst = sqlite3_column_int(pLeafEst, 0);
+ }
+ rc = sqlite3_reset(pLeafEst);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Calculate the first block to use in the output segment */
+ rc = fts3SqlStmt(p, SQL_NEXT_SEGMENTS_ID, &pFirstBlock, 0);
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pFirstBlock) ){
+ pWriter->iStart = sqlite3_column_int64(pFirstBlock, 0);
+ pWriter->iEnd = pWriter->iStart - 1;
+ pWriter->iEnd += nLeafEst * FTS_MAX_APPENDABLE_HEIGHT;
+ }
+ rc = sqlite3_reset(pFirstBlock);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Insert the marker in the %_segments table to make sure nobody tries
+ ** to steal the space just allocated. This is also used to identify
+ ** appendable segments. */
+ rc = fts3WriteSegment(p, pWriter->iEnd, 0, 0);
+ if( rc!=SQLITE_OK ) return rc;
+
+ pWriter->iAbsLevel = iAbsLevel;
+ pWriter->nLeafEst = nLeafEst;
+ pWriter->iIdx = iIdx;
+
+ /* Set up the array of NodeWriter objects */
+ for(i=0; i<FTS_MAX_APPENDABLE_HEIGHT; i++){
+ pWriter->aNodeWriter[i].iBlock = pWriter->iStart + i*pWriter->nLeafEst;
+ }
+ return SQLITE_OK;
+}
+
+/*
+** Remove an entry from the %_segdir table. This involves running the
+** following two statements:
+**
+** DELETE FROM %_segdir WHERE level = :iAbsLevel AND idx = :iIdx
+** UPDATE %_segdir SET idx = idx - 1 WHERE level = :iAbsLevel AND idx > :iIdx
+**
+** The DELETE statement removes the specific %_segdir level. The UPDATE
+** statement ensures that the remaining segments have contiguously allocated
+** idx values.
+*/
+static int fts3RemoveSegdirEntry(
+ Fts3Table *p, /* FTS3 table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level to delete from */
+ int iIdx /* Index of %_segdir entry to delete */
+){
+ int rc; /* Return code */
+ sqlite3_stmt *pDelete = 0; /* DELETE statement */
+
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_ENTRY, &pDelete, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDelete, 1, iAbsLevel);
+ sqlite3_bind_int(pDelete, 2, iIdx);
+ sqlite3_step(pDelete);
+ rc = sqlite3_reset(pDelete);
+ }
+
+ return rc;
+}
+
+/*
+** One or more segments have just been removed from absolute level iAbsLevel.
+** Update the 'idx' values of the remaining segments in the level so that
+** the idx values are a contiguous sequence starting from 0.
+*/
+static int fts3RepackSegdirLevel(
+ Fts3Table *p, /* FTS3 table handle */
+ sqlite3_int64 iAbsLevel /* Absolute level to repack */
+){
+ int rc; /* Return code */
+ int *aIdx = 0; /* Array of remaining idx values */
+ int nIdx = 0; /* Valid entries in aIdx[] */
+ int nAlloc = 0; /* Allocated size of aIdx[] */
+ int i; /* Iterator variable */
+ sqlite3_stmt *pSelect = 0; /* Select statement to read idx values */
+ sqlite3_stmt *pUpdate = 0; /* Update statement to modify idx values */
+
+ rc = fts3SqlStmt(p, SQL_SELECT_INDEXES, &pSelect, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ sqlite3_bind_int64(pSelect, 1, iAbsLevel);
+ while( SQLITE_ROW==sqlite3_step(pSelect) ){
+ if( nIdx>=nAlloc ){
+ int *aNew;
+ nAlloc += 16;
+ aNew = sqlite3_realloc(aIdx, nAlloc*sizeof(int));
+ if( !aNew ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ aIdx = aNew;
+ }
+ aIdx[nIdx++] = sqlite3_column_int(pSelect, 0);
+ }
+ rc2 = sqlite3_reset(pSelect);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = fts3SqlStmt(p, SQL_SHIFT_SEGDIR_ENTRY, &pUpdate, 0);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pUpdate, 2, iAbsLevel);
+ }
+
+ assert( p->bIgnoreSavepoint==0 );
+ p->bIgnoreSavepoint = 1;
+ for(i=0; rc==SQLITE_OK && i<nIdx; i++){
+ if( aIdx[i]!=i ){
+ sqlite3_bind_int(pUpdate, 3, aIdx[i]);
+ sqlite3_bind_int(pUpdate, 1, i);
+ sqlite3_step(pUpdate);
+ rc = sqlite3_reset(pUpdate);
+ }
+ }
+ p->bIgnoreSavepoint = 0;
+
+ sqlite3_free(aIdx);
+ return rc;
+}
+
+static void fts3StartNode(Blob *pNode, int iHeight, sqlite3_int64 iChild){
+ pNode->a[0] = (char)iHeight;
+ if( iChild ){
+ assert( pNode->nAlloc>=1+sqlite3Fts3VarintLen(iChild) );
+ pNode->n = 1 + sqlite3Fts3PutVarint(&pNode->a[1], iChild);
+ }else{
+ assert( pNode->nAlloc>=1 );
+ pNode->n = 1;
+ }
+}
+
+/*
+** The first two arguments are a pointer to and the size of a segment b-tree
+** node. The node may be a leaf or an internal node.
+**
+** This function creates a new node image in blob object *pNew by copying
+** all terms that are greater than or equal to zTerm/nTerm (for leaf nodes)
+** or greater than zTerm/nTerm (for internal nodes) from aNode/nNode.
+*/
+static int fts3TruncateNode(
+ const char *aNode, /* Current node image */
+ int nNode, /* Size of aNode in bytes */
+ Blob *pNew, /* OUT: Write new node image here */
+ const char *zTerm, /* Omit all terms smaller than this */
+ int nTerm, /* Size of zTerm in bytes */
+ sqlite3_int64 *piBlock /* OUT: Block number in next layer down */
+){
+ NodeReader reader; /* Reader object */
+ Blob prev = {0, 0, 0}; /* Previous term written to new node */
+ int rc = SQLITE_OK; /* Return code */
+ int bLeaf = aNode[0]=='\0'; /* True for a leaf node */
+
+ /* Allocate required output space */
+ blobGrowBuffer(pNew, nNode, &rc);
+ if( rc!=SQLITE_OK ) return rc;
+ pNew->n = 0;
+
+ /* Populate new node buffer */
+ for(rc = nodeReaderInit(&reader, aNode, nNode);
+ rc==SQLITE_OK && reader.aNode;
+ rc = nodeReaderNext(&reader)
+ ){
+ if( pNew->n==0 ){
+ int res = fts3TermCmp(reader.term.a, reader.term.n, zTerm, nTerm);
+ if( res<0 || (bLeaf==0 && res==0) ) continue;
+ fts3StartNode(pNew, (int)aNode[0], reader.iChild);
+ *piBlock = reader.iChild;
+ }
+ rc = fts3AppendToNode(
+ pNew, &prev, reader.term.a, reader.term.n,
+ reader.aDoclist, reader.nDoclist
+ );
+ if( rc!=SQLITE_OK ) break;
+ }
+ if( pNew->n==0 ){
+ fts3StartNode(pNew, (int)aNode[0], reader.iChild);
+ *piBlock = reader.iChild;
+ }
+ assert( pNew->n<=pNew->nAlloc );
+
+ nodeReaderRelease(&reader);
+ sqlite3_free(prev.a);
+ return rc;
+}
+
+/*
+** Remove all terms smaller than zTerm/nTerm from segment iIdx in absolute
+** level iAbsLevel. This may involve deleting entries from the %_segments
+** table, and modifying existing entries in both the %_segments and %_segdir
+** tables.
+**
+** SQLITE_OK is returned if the segment is updated successfully. Or an
+** SQLite error code otherwise.
+*/
+static int fts3TruncateSegment(
+ Fts3Table *p, /* FTS3 table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level of segment to modify */
+ int iIdx, /* Index within level of segment to modify */
+ const char *zTerm, /* Remove terms smaller than this */
+ int nTerm /* Number of bytes in buffer zTerm */
+){
+ int rc = SQLITE_OK; /* Return code */
+ Blob root = {0,0,0}; /* New root page image */
+ Blob block = {0,0,0}; /* Buffer used for any other block */
+ sqlite3_int64 iBlock = 0; /* Block id */
+ sqlite3_int64 iNewStart = 0; /* New value for iStartBlock */
+ sqlite3_int64 iOldStart = 0; /* Old value for iStartBlock */
+ sqlite3_stmt *pFetch = 0; /* Statement used to fetch segdir */
+
+ rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR, &pFetch, 0);
+ if( rc==SQLITE_OK ){
+ int rc2; /* sqlite3_reset() return code */
+ sqlite3_bind_int64(pFetch, 1, iAbsLevel);
+ sqlite3_bind_int(pFetch, 2, iIdx);
+ if( SQLITE_ROW==sqlite3_step(pFetch) ){
+ const char *aRoot = sqlite3_column_blob(pFetch, 4);
+ int nRoot = sqlite3_column_bytes(pFetch, 4);
+ iOldStart = sqlite3_column_int64(pFetch, 1);
+ rc = fts3TruncateNode(aRoot, nRoot, &root, zTerm, nTerm, &iBlock);
+ }
+ rc2 = sqlite3_reset(pFetch);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ while( rc==SQLITE_OK && iBlock ){
+ char *aBlock = 0;
+ int nBlock = 0;
+ iNewStart = iBlock;
+
+ rc = sqlite3Fts3ReadBlock(p, iBlock, &aBlock, &nBlock, 0);
+ if( rc==SQLITE_OK ){
+ rc = fts3TruncateNode(aBlock, nBlock, &block, zTerm, nTerm, &iBlock);
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts3WriteSegment(p, iNewStart, block.a, block.n);
+ }
+ sqlite3_free(aBlock);
+ }
+
+ /* Variable iNewStart now contains the first valid leaf node. */
+ if( rc==SQLITE_OK && iNewStart ){
+ sqlite3_stmt *pDel = 0;
+ rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDel, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iOldStart);
+ sqlite3_bind_int64(pDel, 2, iNewStart-1);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pChomp = 0;
+ rc = fts3SqlStmt(p, SQL_CHOMP_SEGDIR, &pChomp, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pChomp, 1, iNewStart);
+ sqlite3_bind_blob(pChomp, 2, root.a, root.n, SQLITE_STATIC);
+ sqlite3_bind_int64(pChomp, 3, iAbsLevel);
+ sqlite3_bind_int(pChomp, 4, iIdx);
+ sqlite3_step(pChomp);
+ rc = sqlite3_reset(pChomp);
+ }
+ }
+
+ sqlite3_free(root.a);
+ sqlite3_free(block.a);
+ return rc;
+}
+
+/*
+** This function is called after an incrmental-merge operation has run to
+** merge (or partially merge) two or more segments from absolute level
+** iAbsLevel.
+**
+** Each input segment is either removed from the db completely (if all of
+** its data was copied to the output segment by the incrmerge operation)
+** or modified in place so that it no longer contains those entries that
+** have been duplicated in the output segment.
+*/
+static int fts3IncrmergeChomp(
+ Fts3Table *p, /* FTS table handle */
+ sqlite3_int64 iAbsLevel, /* Absolute level containing segments */
+ Fts3MultiSegReader *pCsr, /* Chomp all segments opened by this cursor */
+ int *pnRem /* Number of segments not deleted */
+){
+ int i;
+ int nRem = 0;
+ int rc = SQLITE_OK;
+
+ for(i=pCsr->nSegment-1; i>=0 && rc==SQLITE_OK; i--){
+ Fts3SegReader *pSeg = 0;
+ int j;
+
+ /* Find the Fts3SegReader object with Fts3SegReader.iIdx==i. It is hiding
+ ** somewhere in the pCsr->apSegment[] array. */
+ for(j=0; ALWAYS(j<pCsr->nSegment); j++){
+ pSeg = pCsr->apSegment[j];
+ if( pSeg->iIdx==i ) break;
+ }
+ assert( j<pCsr->nSegment && pSeg->iIdx==i );
+
+ if( pSeg->aNode==0 ){
+ /* Seg-reader is at EOF. Remove the entire input segment. */
+ rc = fts3DeleteSegment(p, pSeg);
+ if( rc==SQLITE_OK ){
+ rc = fts3RemoveSegdirEntry(p, iAbsLevel, pSeg->iIdx);
+ }
+ *pnRem = 0;
+ }else{
+ /* The incremental merge did not copy all the data from this
+ ** segment to the upper level. The segment is modified in place
+ ** so that it contains no keys smaller than zTerm/nTerm. */
+ const char *zTerm = pSeg->zTerm;
+ int nTerm = pSeg->nTerm;
+ rc = fts3TruncateSegment(p, iAbsLevel, pSeg->iIdx, zTerm, nTerm);
+ nRem++;
+ }
+ }
+
+ if( rc==SQLITE_OK && nRem!=pCsr->nSegment ){
+ rc = fts3RepackSegdirLevel(p, iAbsLevel);
+ }
+
+ *pnRem = nRem;
+ return rc;
+}
+
+/*
+** Store an incr-merge hint in the database.
+*/
+static int fts3IncrmergeHintStore(Fts3Table *p, Blob *pHint){
+ sqlite3_stmt *pReplace = 0;
+ int rc; /* Return code */
+
+ rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(pReplace, 1, FTS_STAT_INCRMERGEHINT);
+ sqlite3_bind_blob(pReplace, 2, pHint->a, pHint->n, SQLITE_STATIC);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+
+ return rc;
+}
+
+/*
+** Load an incr-merge hint from the database. The incr-merge hint, if one
+** exists, is stored in the rowid==1 row of the %_stat table.
+**
+** If successful, populate blob *pHint with the value read from the %_stat
+** table and return SQLITE_OK. Otherwise, if an error occurs, return an
+** SQLite error code.
+*/
+static int fts3IncrmergeHintLoad(Fts3Table *p, Blob *pHint){
+ sqlite3_stmt *pSelect = 0;
+ int rc;
+
+ pHint->n = 0;
+ rc = fts3SqlStmt(p, SQL_SELECT_STAT, &pSelect, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ sqlite3_bind_int(pSelect, 1, FTS_STAT_INCRMERGEHINT);
+ if( SQLITE_ROW==sqlite3_step(pSelect) ){
+ const char *aHint = sqlite3_column_blob(pSelect, 0);
+ int nHint = sqlite3_column_bytes(pSelect, 0);
+ if( aHint ){
+ blobGrowBuffer(pHint, nHint, &rc);
+ if( rc==SQLITE_OK ){
+ memcpy(pHint->a, aHint, nHint);
+ pHint->n = nHint;
+ }
+ }
+ }
+ rc2 = sqlite3_reset(pSelect);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ return rc;
+}
+
+/*
+** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
+** Otherwise, append an entry to the hint stored in blob *pHint. Each entry
+** consists of two varints, the absolute level number of the input segments
+** and the number of input segments.
+**
+** If successful, leave *pRc set to SQLITE_OK and return. If an error occurs,
+** set *pRc to an SQLite error code before returning.
+*/
+static void fts3IncrmergeHintPush(
+ Blob *pHint, /* Hint blob to append to */
+ i64 iAbsLevel, /* First varint to store in hint */
+ int nInput, /* Second varint to store in hint */
+ int *pRc /* IN/OUT: Error code */
+){
+ blobGrowBuffer(pHint, pHint->n + 2*FTS3_VARINT_MAX, pRc);
+ if( *pRc==SQLITE_OK ){
+ pHint->n += sqlite3Fts3PutVarint(&pHint->a[pHint->n], iAbsLevel);
+ pHint->n += sqlite3Fts3PutVarint(&pHint->a[pHint->n], (i64)nInput);
+ }
+}
+
+/*
+** Read the last entry (most recently pushed) from the hint blob *pHint
+** and then remove the entry. Write the two values read to *piAbsLevel and
+** *pnInput before returning.
+**
+** If no error occurs, return SQLITE_OK. If the hint blob in *pHint does
+** not contain at least two valid varints, return SQLITE_CORRUPT_VTAB.
+*/
+static int fts3IncrmergeHintPop(Blob *pHint, i64 *piAbsLevel, int *pnInput){
+ const int nHint = pHint->n;
+ int i;
+
+ i = pHint->n-2;
+ while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
+ while( i>0 && (pHint->a[i-1] & 0x80) ) i--;
+
+ pHint->n = i;
+ i += sqlite3Fts3GetVarint(&pHint->a[i], piAbsLevel);
+ i += fts3GetVarint32(&pHint->a[i], pnInput);
+ if( i!=nHint ) return FTS_CORRUPT_VTAB;
+
+ return SQLITE_OK;
+}
+
+
+/*
+** Attempt an incremental merge that writes nMerge leaf blocks.
+**
+** Incremental merges happen nMin segments at a time. The segments
+** to be merged are the nMin oldest segments (the ones with the smallest
+** values for the _segdir.idx field) in the highest level that contains
+** at least nMin segments. Multiple merges might occur in an attempt to
+** write the quota of nMerge leaf blocks.
+*/
+SQLITE_PRIVATE int sqlite3Fts3Incrmerge(Fts3Table *p, int nMerge, int nMin){
+ int rc; /* Return code */
+ int nRem = nMerge; /* Number of leaf pages yet to be written */
+ Fts3MultiSegReader *pCsr; /* Cursor used to read input data */
+ Fts3SegFilter *pFilter; /* Filter used with cursor pCsr */
+ IncrmergeWriter *pWriter; /* Writer object */
+ int nSeg = 0; /* Number of input segments */
+ sqlite3_int64 iAbsLevel = 0; /* Absolute level number to work on */
+ Blob hint = {0, 0, 0}; /* Hint read from %_stat table */
+ int bDirtyHint = 0; /* True if blob 'hint' has been modified */
+
+ /* Allocate space for the cursor, filter and writer objects */
+ const int nAlloc = sizeof(*pCsr) + sizeof(*pFilter) + sizeof(*pWriter);
+ pWriter = (IncrmergeWriter *)sqlite3_malloc(nAlloc);
+ if( !pWriter ) return SQLITE_NOMEM;
+ pFilter = (Fts3SegFilter *)&pWriter[1];
+ pCsr = (Fts3MultiSegReader *)&pFilter[1];
+
+ rc = fts3IncrmergeHintLoad(p, &hint);
+ while( rc==SQLITE_OK && nRem>0 ){
+ const i64 nMod = FTS3_SEGDIR_MAXLEVEL * p->nIndex;
+ sqlite3_stmt *pFindLevel = 0; /* SQL used to determine iAbsLevel */
+ int bUseHint = 0; /* True if attempting to append */
+ int iIdx = 0; /* Largest idx in level (iAbsLevel+1) */
+
+ /* Search the %_segdir table for the absolute level with the smallest
+ ** relative level number that contains at least nMin segments, if any.
+ ** If one is found, set iAbsLevel to the absolute level number and
+ ** nSeg to nMin. If no level with at least nMin segments can be found,
+ ** set nSeg to -1.
+ */
+ rc = fts3SqlStmt(p, SQL_FIND_MERGE_LEVEL, &pFindLevel, 0);
+ sqlite3_bind_int(pFindLevel, 1, MAX(2, nMin));
+ if( sqlite3_step(pFindLevel)==SQLITE_ROW ){
+ iAbsLevel = sqlite3_column_int64(pFindLevel, 0);
+ nSeg = sqlite3_column_int(pFindLevel, 1);
+ assert( nSeg>=2 );
+ }else{
+ nSeg = -1;
+ }
+ rc = sqlite3_reset(pFindLevel);
+
+ /* If the hint read from the %_stat table is not empty, check if the
+ ** last entry in it specifies a relative level smaller than or equal
+ ** to the level identified by the block above (if any). If so, this
+ ** iteration of the loop will work on merging at the hinted level.
+ */
+ if( rc==SQLITE_OK && hint.n ){
+ int nHint = hint.n;
+ sqlite3_int64 iHintAbsLevel = 0; /* Hint level */
+ int nHintSeg = 0; /* Hint number of segments */
+
+ rc = fts3IncrmergeHintPop(&hint, &iHintAbsLevel, &nHintSeg);
+ if( nSeg<0 || (iAbsLevel % nMod) >= (iHintAbsLevel % nMod) ){
+ iAbsLevel = iHintAbsLevel;
+ nSeg = nHintSeg;
+ bUseHint = 1;
+ bDirtyHint = 1;
+ }else{
+ /* This undoes the effect of the HintPop() above - so that no entry
+ ** is removed from the hint blob. */
+ hint.n = nHint;
+ }
+ }
+
+ /* If nSeg is less that zero, then there is no level with at least
+ ** nMin segments and no hint in the %_stat table. No work to do.
+ ** Exit early in this case. */
+ if( nSeg<0 ) break;
+
+ /* Open a cursor to iterate through the contents of the oldest nSeg
+ ** indexes of absolute level iAbsLevel. If this cursor is opened using
+ ** the 'hint' parameters, it is possible that there are less than nSeg
+ ** segments available in level iAbsLevel. In this case, no work is
+ ** done on iAbsLevel - fall through to the next iteration of the loop
+ ** to start work on some other level. */
+ memset(pWriter, 0, nAlloc);
+ pFilter->flags = FTS3_SEGMENT_REQUIRE_POS;
+
+ if( rc==SQLITE_OK ){
+ rc = fts3IncrmergeOutputIdx(p, iAbsLevel, &iIdx);
+ assert( bUseHint==1 || bUseHint==0 );
+ if( iIdx==0 || (bUseHint && iIdx==1) ){
+ int bIgnore = 0;
+ rc = fts3SegmentIsMaxLevel(p, iAbsLevel+1, &bIgnore);
+ if( bIgnore ){
+ pFilter->flags |= FTS3_SEGMENT_IGNORE_EMPTY;
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = fts3IncrmergeCsr(p, iAbsLevel, nSeg, pCsr);
+ }
+ if( SQLITE_OK==rc && pCsr->nSegment==nSeg
+ && SQLITE_OK==(rc = sqlite3Fts3SegReaderStart(p, pCsr, pFilter))
+ && SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, pCsr))
+ ){
+ if( bUseHint && iIdx>0 ){
+ const char *zKey = pCsr->zTerm;
+ int nKey = pCsr->nTerm;
+ rc = fts3IncrmergeLoad(p, iAbsLevel, iIdx-1, zKey, nKey, pWriter);
+ }else{
+ rc = fts3IncrmergeWriter(p, iAbsLevel, iIdx, pCsr, pWriter);
+ }
+
+ if( rc==SQLITE_OK && pWriter->nLeafEst ){
+ fts3LogMerge(nSeg, iAbsLevel);
+ do {
+ rc = fts3IncrmergeAppend(p, pWriter, pCsr);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts3SegReaderStep(p, pCsr);
+ if( pWriter->nWork>=nRem && rc==SQLITE_ROW ) rc = SQLITE_OK;
+ }while( rc==SQLITE_ROW );
+
+ /* Update or delete the input segments */
+ if( rc==SQLITE_OK ){
+ nRem -= (1 + pWriter->nWork);
+ rc = fts3IncrmergeChomp(p, iAbsLevel, pCsr, &nSeg);
+ if( nSeg!=0 ){
+ bDirtyHint = 1;
+ fts3IncrmergeHintPush(&hint, iAbsLevel, nSeg, &rc);
+ }
+ }
+ }
+
+ if( nSeg!=0 ){
+ pWriter->nLeafData = pWriter->nLeafData * -1;
+ }
+ fts3IncrmergeRelease(p, pWriter, &rc);
+ if( nSeg==0 && pWriter->bNoLeafData==0 ){
+ fts3PromoteSegments(p, iAbsLevel+1, pWriter->nLeafData);
+ }
+ }
+
+ sqlite3Fts3SegReaderFinish(pCsr);
+ }
+
+ /* Write the hint values into the %_stat table for the next incr-merger */
+ if( bDirtyHint && rc==SQLITE_OK ){
+ rc = fts3IncrmergeHintStore(p, &hint);
+ }
+
+ sqlite3_free(pWriter);
+ sqlite3_free(hint.a);
+ return rc;
+}
+
+/*
+** Convert the text beginning at *pz into an integer and return
+** its value. Advance *pz to point to the first character past
+** the integer.
+*/
+static int fts3Getint(const char **pz){
+ const char *z = *pz;
+ int i = 0;
+ while( (*z)>='0' && (*z)<='9' ) i = 10*i + *(z++) - '0';
+ *pz = z;
+ return i;
+}
+
+/*
+** Process statements of the form:
+**
+** INSERT INTO table(table) VALUES('merge=A,B');
+**
+** A and B are integers that decode to be the number of leaf pages
+** written for the merge, and the minimum number of segments on a level
+** before it will be selected for a merge, respectively.
+*/
+static int fts3DoIncrmerge(
+ Fts3Table *p, /* FTS3 table handle */
+ const char *zParam /* Nul-terminated string containing "A,B" */
+){
+ int rc;
+ int nMin = (FTS3_MERGE_COUNT / 2);
+ int nMerge = 0;
+ const char *z = zParam;
+
+ /* Read the first integer value */
+ nMerge = fts3Getint(&z);
+
+ /* If the first integer value is followed by a ',', read the second
+ ** integer value. */
+ if( z[0]==',' && z[1]!='\0' ){
+ z++;
+ nMin = fts3Getint(&z);
+ }
+
+ if( z[0]!='\0' || nMin<2 ){
+ rc = SQLITE_ERROR;
+ }else{
+ rc = SQLITE_OK;
+ if( !p->bHasStat ){
+ assert( p->bFts4==0 );
+ sqlite3Fts3CreateStatTable(&rc, p);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts3Incrmerge(p, nMerge, nMin);
+ }
+ sqlite3Fts3SegmentsClose(p);
+ }
+ return rc;
+}
+
+/*
+** Process statements of the form:
+**
+** INSERT INTO table(table) VALUES('automerge=X');
+**
+** where X is an integer. X==0 means to turn automerge off. X!=0 means
+** turn it on. The setting is persistent.
+*/
+static int fts3DoAutoincrmerge(
+ Fts3Table *p, /* FTS3 table handle */
+ const char *zParam /* Nul-terminated string containing boolean */
+){
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pStmt = 0;
+ p->nAutoincrmerge = fts3Getint(&zParam);
+ if( p->nAutoincrmerge==1 || p->nAutoincrmerge>FTS3_MERGE_COUNT ){
+ p->nAutoincrmerge = 8;
+ }
+ if( !p->bHasStat ){
+ assert( p->bFts4==0 );
+ sqlite3Fts3CreateStatTable(&rc, p);
+ if( rc ) return rc;
+ }
+ rc = fts3SqlStmt(p, SQL_REPLACE_STAT, &pStmt, 0);
+ if( rc ) return rc;
+ sqlite3_bind_int(pStmt, 1, FTS_STAT_AUTOINCRMERGE);
+ sqlite3_bind_int(pStmt, 2, p->nAutoincrmerge);
+ sqlite3_step(pStmt);
+ rc = sqlite3_reset(pStmt);
+ return rc;
+}
+
+/*
+** Return a 64-bit checksum for the FTS index entry specified by the
+** arguments to this function.
+*/
+static u64 fts3ChecksumEntry(
+ const char *zTerm, /* Pointer to buffer containing term */
+ int nTerm, /* Size of zTerm in bytes */
+ int iLangid, /* Language id for current row */
+ int iIndex, /* Index (0..Fts3Table.nIndex-1) */
+ i64 iDocid, /* Docid for current row. */
+ int iCol, /* Column number */
+ int iPos /* Position */
+){
+ int i;
+ u64 ret = (u64)iDocid;
+
+ ret += (ret<<3) + iLangid;
+ ret += (ret<<3) + iIndex;
+ ret += (ret<<3) + iCol;
+ ret += (ret<<3) + iPos;
+ for(i=0; i<nTerm; i++) ret += (ret<<3) + zTerm[i];
+
+ return ret;
+}
+
+/*
+** Return a checksum of all entries in the FTS index that correspond to
+** language id iLangid. The checksum is calculated by XORing the checksums
+** of each individual entry (see fts3ChecksumEntry()) together.
+**
+** If successful, the checksum value is returned and *pRc set to SQLITE_OK.
+** Otherwise, if an error occurs, *pRc is set to an SQLite error code. The
+** return value is undefined in this case.
+*/
+static u64 fts3ChecksumIndex(
+ Fts3Table *p, /* FTS3 table handle */
+ int iLangid, /* Language id to return cksum for */
+ int iIndex, /* Index to cksum (0..p->nIndex-1) */
+ int *pRc /* OUT: Return code */
+){
+ Fts3SegFilter filter;
+ Fts3MultiSegReader csr;
+ int rc;
+ u64 cksum = 0;
+
+ assert( *pRc==SQLITE_OK );
+
+ memset(&filter, 0, sizeof(filter));
+ memset(&csr, 0, sizeof(csr));
+ filter.flags = FTS3_SEGMENT_REQUIRE_POS|FTS3_SEGMENT_IGNORE_EMPTY;
+ filter.flags |= FTS3_SEGMENT_SCAN;
+
+ rc = sqlite3Fts3SegReaderCursor(
+ p, iLangid, iIndex, FTS3_SEGCURSOR_ALL, 0, 0, 0, 1,&csr
+ );
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts3SegReaderStart(p, &csr, &filter);
+ }
+
+ if( rc==SQLITE_OK ){
+ while( SQLITE_ROW==(rc = sqlite3Fts3SegReaderStep(p, &csr)) ){
+ char *pCsr = csr.aDoclist;
+ char *pEnd = &pCsr[csr.nDoclist];
+
+ i64 iDocid = 0;
+ i64 iCol = 0;
+ i64 iPos = 0;
+
+ pCsr += sqlite3Fts3GetVarint(pCsr, &iDocid);
+ while( pCsr<pEnd ){
+ i64 iVal = 0;
+ pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
+ if( pCsr<pEnd ){
+ if( iVal==0 || iVal==1 ){
+ iCol = 0;
+ iPos = 0;
+ if( iVal ){
+ pCsr += sqlite3Fts3GetVarint(pCsr, &iCol);
+ }else{
+ pCsr += sqlite3Fts3GetVarint(pCsr, &iVal);
+ iDocid += iVal;
+ }
+ }else{
+ iPos += (iVal - 2);
+ cksum = cksum ^ fts3ChecksumEntry(
+ csr.zTerm, csr.nTerm, iLangid, iIndex, iDocid,
+ (int)iCol, (int)iPos
+ );
+ }
+ }
+ }
+ }
+ }
+ sqlite3Fts3SegReaderFinish(&csr);
+
+ *pRc = rc;
+ return cksum;
+}
+
+/*
+** Check if the contents of the FTS index match the current contents of the
+** content table. If no error occurs and the contents do match, set *pbOk
+** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk
+** to false before returning.
+**
+** If an error occurs (e.g. an OOM or IO error), return an SQLite error
+** code. The final value of *pbOk is undefined in this case.
+*/
+static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
+ int rc = SQLITE_OK; /* Return code */
+ u64 cksum1 = 0; /* Checksum based on FTS index contents */
+ u64 cksum2 = 0; /* Checksum based on %_content contents */
+ sqlite3_stmt *pAllLangid = 0; /* Statement to return all language-ids */
+
+ /* This block calculates the checksum according to the FTS index. */
+ rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ sqlite3_bind_int(pAllLangid, 1, p->iPrevLangid);
+ sqlite3_bind_int(pAllLangid, 2, p->nIndex);
+ while( rc==SQLITE_OK && sqlite3_step(pAllLangid)==SQLITE_ROW ){
+ int iLangid = sqlite3_column_int(pAllLangid, 0);
+ int i;
+ for(i=0; i<p->nIndex; i++){
+ cksum1 = cksum1 ^ fts3ChecksumIndex(p, iLangid, i, &rc);
+ }
+ }
+ rc2 = sqlite3_reset(pAllLangid);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ /* This block calculates the checksum according to the %_content table */
+ if( rc==SQLITE_OK ){
+ sqlite3_tokenizer_module const *pModule = p->pTokenizer->pModule;
+ sqlite3_stmt *pStmt = 0;
+ char *zSql;
+
+ zSql = sqlite3_mprintf("SELECT %s" , p->zReadExprlist);
+ if( !zSql ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ i64 iDocid = sqlite3_column_int64(pStmt, 0);
+ int iLang = langidFromSelect(p, pStmt);
+ int iCol;
+
+ for(iCol=0; rc==SQLITE_OK && iCol<p->nColumn; iCol++){
+ if( p->abNotindexed[iCol]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pStmt, iCol+1);
+ int nText = sqlite3_column_bytes(pStmt, iCol+1);
+ sqlite3_tokenizer_cursor *pT = 0;
+
+ rc = sqlite3Fts3OpenTokenizer(p->pTokenizer, iLang, zText, nText,&pT);
+ while( rc==SQLITE_OK ){
+ char const *zToken; /* Buffer containing token */
+ int nToken = 0; /* Number of bytes in token */
+ int iDum1 = 0, iDum2 = 0; /* Dummy variables */
+ int iPos = 0; /* Position of token in zText */
+
+ rc = pModule->xNext(pT, &zToken, &nToken, &iDum1, &iDum2, &iPos);
+ if( rc==SQLITE_OK ){
+ int i;
+ cksum2 = cksum2 ^ fts3ChecksumEntry(
+ zToken, nToken, iLang, 0, iDocid, iCol, iPos
+ );
+ for(i=1; i<p->nIndex; i++){
+ if( p->aIndex[i].nPrefix<=nToken ){
+ cksum2 = cksum2 ^ fts3ChecksumEntry(
+ zToken, p->aIndex[i].nPrefix, iLang, i, iDocid, iCol, iPos
+ );
+ }
+ }
+ }
+ }
+ if( pT ) pModule->xClose(pT);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ }
+ }
+ }
+
+ sqlite3_finalize(pStmt);
+ }
+
+ *pbOk = (cksum1==cksum2);
+ return rc;
+}
+
+/*
+** Run the integrity-check. If no error occurs and the current contents of
+** the FTS index are correct, return SQLITE_OK. Or, if the contents of the
+** FTS index are incorrect, return SQLITE_CORRUPT_VTAB.
+**
+** Or, if an error (e.g. an OOM or IO error) occurs, return an SQLite
+** error code.
+**
+** The integrity-check works as follows. For each token and indexed token
+** prefix in the document set, a 64-bit checksum is calculated (by code
+** in fts3ChecksumEntry()) based on the following:
+**
+** + The index number (0 for the main index, 1 for the first prefix
+** index etc.),
+** + The token (or token prefix) text itself,
+** + The language-id of the row it appears in,
+** + The docid of the row it appears in,
+** + The column it appears in, and
+** + The tokens position within that column.
+**
+** The checksums for all entries in the index are XORed together to create
+** a single checksum for the entire index.
+**
+** The integrity-check code calculates the same checksum in two ways:
+**
+** 1. By scanning the contents of the FTS index, and
+** 2. By scanning and tokenizing the content table.
+**
+** If the two checksums are identical, the integrity-check is deemed to have
+** passed.
+*/
+static int fts3DoIntegrityCheck(
+ Fts3Table *p /* FTS3 table handle */
+){
+ int rc;
+ int bOk = 0;
+ rc = fts3IntegrityCheck(p, &bOk);
+ if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
+ return rc;
+}
+
+/*
** Handle a 'special' INSERT of the form:
**
** "INSERT INTO tbl(tbl) VALUES(<expr>)"
@@ -125187,6 +158452,14 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
return SQLITE_NOMEM;
}else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){
rc = fts3DoOptimize(p, 0);
+ }else if( nVal==7 && 0==sqlite3_strnicmp(zVal, "rebuild", 7) ){
+ rc = fts3DoRebuild(p);
+ }else if( nVal==15 && 0==sqlite3_strnicmp(zVal, "integrity-check", 15) ){
+ rc = fts3DoIntegrityCheck(p);
+ }else if( nVal>6 && 0==sqlite3_strnicmp(zVal, "merge=", 6) ){
+ rc = fts3DoIncrmerge(p, &zVal[6]);
+ }else if( nVal>10 && 0==sqlite3_strnicmp(zVal, "automerge=", 10) ){
+ rc = fts3DoAutoincrmerge(p, &zVal[10]);
#ifdef SQLITE_TEST
}else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){
p->nNodeSize = atoi(&zVal[9]);
@@ -125194,6 +158467,9 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
}else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){
p->nMaxPendingData = atoi(&zVal[11]);
rc = SQLITE_OK;
+ }else if( nVal>21 && 0==sqlite3_strnicmp(zVal, "test-no-incr-doclist=", 21) ){
+ p->bNoIncrDoclist = atoi(&zVal[21]);
+ rc = SQLITE_OK;
#endif
}else{
rc = SQLITE_ERROR;
@@ -125202,6 +158478,7 @@ static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){
return rc;
}
+#ifndef SQLITE_DISABLE_FTS4_DEFERRED
/*
** Delete all cached deferred doclists. Deferred doclists are cached
** (allocated) by the sqlite3Fts3CacheDeferredDoclists() function.
@@ -125252,32 +158529,34 @@ SQLITE_PRIVATE int sqlite3Fts3CacheDeferredDoclists(Fts3Cursor *pCsr){
iDocid = sqlite3_column_int64(pCsr->pStmt, 0);
for(i=0; i<p->nColumn && rc==SQLITE_OK; i++){
- const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
- sqlite3_tokenizer_cursor *pTC = 0;
-
- rc = pModule->xOpen(pT, zText, -1, &pTC);
- while( rc==SQLITE_OK ){
- char const *zToken; /* Buffer containing token */
- int nToken; /* Number of bytes in token */
- int iDum1, iDum2; /* Dummy variables */
- int iPos; /* Position of token in zText */
-
- pTC->pTokenizer = pT;
- rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
- for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
- Fts3PhraseToken *pPT = pDef->pToken;
- if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
- && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
- && (0==memcmp(zToken, pPT->z, pPT->n))
- ){
- fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
+ if( p->abNotindexed[i]==0 ){
+ const char *zText = (const char *)sqlite3_column_text(pCsr->pStmt, i+1);
+ sqlite3_tokenizer_cursor *pTC = 0;
+
+ rc = sqlite3Fts3OpenTokenizer(pT, pCsr->iLangid, zText, -1, &pTC);
+ while( rc==SQLITE_OK ){
+ char const *zToken; /* Buffer containing token */
+ int nToken = 0; /* Number of bytes in token */
+ int iDum1 = 0, iDum2 = 0; /* Dummy variables */
+ int iPos = 0; /* Position of token in zText */
+
+ rc = pModule->xNext(pTC, &zToken, &nToken, &iDum1, &iDum2, &iPos);
+ for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
+ Fts3PhraseToken *pPT = pDef->pToken;
+ if( (pDef->iCol>=p->nColumn || pDef->iCol==i)
+ && (pPT->bFirst==0 || iPos==0)
+ && (pPT->n==nToken || (pPT->isPrefix && pPT->n<nToken))
+ && (0==memcmp(zToken, pPT->z, pPT->n))
+ ){
+ fts3PendingListAppend(&pDef->pList, iDocid, i, iPos, &rc);
+ }
}
}
+ if( pTC ) pModule->xClose(pTC);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
- if( pTC ) pModule->xClose(pTC);
- if( rc==SQLITE_DONE ) rc = SQLITE_OK;
}
-
+
for(pDef=pCsr->pDeferred; pDef && rc==SQLITE_OK; pDef=pDef->pNext){
if( pDef->pList ){
rc = fts3PendingListAppendVarint(&pDef->pList, 0);
@@ -125339,6 +158618,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
return SQLITE_OK;
}
+#endif
/*
** SQLite value pRowid contains the rowid of a row that may or may not be
@@ -125348,26 +158628,32 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
static int fts3DeleteByRowid(
Fts3Table *p,
sqlite3_value *pRowid,
- int *pnDoc,
+ int *pnChng, /* IN/OUT: Decrement if row is deleted */
u32 *aSzDel
){
- int isEmpty = 0;
- int rc = fts3IsEmpty(p, pRowid, &isEmpty);
- if( rc==SQLITE_OK ){
- if( isEmpty ){
- /* Deleting this row means the whole table is empty. In this case
- ** delete the contents of all three tables and throw away any
- ** data in the pendingTerms hash table. */
- rc = fts3DeleteAll(p);
- *pnDoc = *pnDoc - 1;
- }else{
- sqlite3_int64 iRemove = sqlite3_value_int64(pRowid);
- rc = fts3PendingTermsDocid(p, iRemove);
- fts3DeleteTerms(&rc, p, pRowid, aSzDel);
- fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
- if( sqlite3_changes(p->db) ) *pnDoc = *pnDoc - 1;
- if( p->bHasDocsize ){
- fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
+ int rc = SQLITE_OK; /* Return code */
+ int bFound = 0; /* True if *pRowid really is in the table */
+
+ fts3DeleteTerms(&rc, p, pRowid, aSzDel, &bFound);
+ if( bFound && rc==SQLITE_OK ){
+ int isEmpty = 0; /* Deleting *pRowid leaves the table empty */
+ rc = fts3IsEmpty(p, pRowid, &isEmpty);
+ if( rc==SQLITE_OK ){
+ if( isEmpty ){
+ /* Deleting this row means the whole table is empty. In this case
+ ** delete the contents of all three tables and throw away any
+ ** data in the pendingTerms hash table. */
+ rc = fts3DeleteAll(p, 1);
+ *pnChng = 0;
+ memset(aSzDel, 0, sizeof(u32) * (p->nColumn+1) * 2);
+ }else{
+ *pnChng = *pnChng - 1;
+ if( p->zContentTbl==0 ){
+ fts3SqlExec(&rc, p, SQL_DELETE_CONTENT, &pRowid);
+ }
+ if( p->bHasDocsize ){
+ fts3SqlExec(&rc, p, SQL_DELETE_DOCSIZE, &pRowid);
+ }
}
}
}
@@ -125377,7 +158663,16 @@ static int fts3DeleteByRowid(
/*
** This function does the work for the xUpdate method of FTS3 virtual
-** tables.
+** tables. The schema of the virtual table being:
+**
+** CREATE TABLE <table name>(
+** <user columns>,
+** <table name> HIDDEN,
+** docid HIDDEN,
+** <langid> HIDDEN
+** );
+**
+**
*/
SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
sqlite3_vtab *pVtab, /* FTS3 vtab object */
@@ -125388,13 +158683,20 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
Fts3Table *p = (Fts3Table *)pVtab;
int rc = SQLITE_OK; /* Return Code */
int isRemove = 0; /* True for an UPDATE or DELETE */
- sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */
u32 *aSzIns = 0; /* Sizes of inserted documents */
- u32 *aSzDel; /* Sizes of deleted documents */
+ u32 *aSzDel = 0; /* Sizes of deleted documents */
int nChng = 0; /* Net change in number of documents */
int bInsertDone = 0;
+ /* At this point it must be known if the %_stat table exists or not.
+ ** So bHasStat may not be 2. */
+ assert( p->bHasStat==0 || p->bHasStat==1 );
+
assert( p->pSegments==0 );
+ assert(
+ nArg==1 /* DELETE operations */
+ || nArg==(2 + p->nColumn + 3) /* INSERT or UPDATE operations */
+ );
/* Check for a "special" INSERT operation. One of the form:
**
@@ -125408,14 +158710,22 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
goto update_out;
}
+ if( nArg>1 && sqlite3_value_int(apVal[2 + p->nColumn + 2])<0 ){
+ rc = SQLITE_CONSTRAINT;
+ goto update_out;
+ }
+
/* Allocate space to hold the change in document sizes */
- aSzIns = sqlite3_malloc( sizeof(aSzIns[0])*(p->nColumn+1)*2 );
- if( aSzIns==0 ){
+ aSzDel = sqlite3_malloc( sizeof(aSzDel[0])*(p->nColumn+1)*2 );
+ if( aSzDel==0 ){
rc = SQLITE_NOMEM;
goto update_out;
}
- aSzDel = &aSzIns[p->nColumn+1];
- memset(aSzIns, 0, sizeof(aSzIns[0])*(p->nColumn+1)*2);
+ aSzIns = &aSzDel[p->nColumn+1];
+ memset(aSzDel, 0, sizeof(aSzDel[0])*(p->nColumn+1)*2);
+
+ rc = fts3Writelock(p);
+ if( rc!=SQLITE_OK ) goto update_out;
/* If this is an INSERT operation, or an UPDATE that modifies the rowid
** value, then this operation requires constraint handling.
@@ -125426,7 +158736,7 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
** detect the conflict and return SQLITE_CONSTRAINT before beginning to
** modify the database file.
*/
- if( nArg>1 ){
+ if( nArg>1 && p->zContentTbl==0 ){
/* Find the value object that holds the new rowid value. */
sqlite3_value *pNewRowid = apVal[3+p->nColumn];
if( sqlite3_value_type(pNewRowid)==SQLITE_NULL ){
@@ -125471,20 +158781,23 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
assert( sqlite3_value_type(apVal[0])==SQLITE_INTEGER );
rc = fts3DeleteByRowid(p, apVal[0], &nChng, aSzDel);
isRemove = 1;
- iRemove = sqlite3_value_int64(apVal[0]);
}
/* If this is an INSERT or UPDATE operation, insert the new record. */
if( nArg>1 && rc==SQLITE_OK ){
+ int iLangid = sqlite3_value_int(apVal[2 + p->nColumn + 2]);
if( bInsertDone==0 ){
rc = fts3InsertData(p, apVal, pRowid);
- if( rc==SQLITE_CONSTRAINT ) rc = SQLITE_CORRUPT_VTAB;
+ if( rc==SQLITE_CONSTRAINT && p->zContentTbl==0 ){
+ rc = FTS_CORRUPT_VTAB;
+ }
}
- if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){
- rc = fts3PendingTermsDocid(p, *pRowid);
+ if( rc==SQLITE_OK && (!isRemove || *pRowid!=p->iPrevDocid ) ){
+ rc = fts3PendingTermsDocid(p, 0, iLangid, *pRowid);
}
if( rc==SQLITE_OK ){
- rc = fts3InsertTerms(p, apVal, aSzIns);
+ assert( p->iPrevDocid==*pRowid );
+ rc = fts3InsertTerms(p, iLangid, apVal, aSzIns);
}
if( p->bHasDocsize ){
fts3InsertDocsize(&rc, p, aSzIns);
@@ -125492,12 +158805,12 @@ SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(
nChng++;
}
- if( p->bHasStat ){
+ if( p->bFts4 ){
fts3UpdateDocTotals(&rc, p, aSzIns, aSzDel, nChng);
}
update_out:
- sqlite3_free(aSzIns);
+ sqlite3_free(aSzDel);
sqlite3Fts3SegmentsClose(p);
return rc;
}
@@ -125541,6 +158854,7 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
******************************************************************************
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
/* #include <string.h> */
@@ -125556,6 +158870,8 @@ SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){
#define FTS3_MATCHINFO_LENGTH 'l' /* nCol values */
#define FTS3_MATCHINFO_LCS 's' /* nCol values */
#define FTS3_MATCHINFO_HITS 'x' /* 3*nCol*nPhrase values */
+#define FTS3_MATCHINFO_LHITS 'y' /* nCol*nPhrase values */
+#define FTS3_MATCHINFO_LHITS_BM 'b' /* nCol*nPhrase values */
/*
** The default value for the second argument to matchinfo().
@@ -125617,9 +158933,22 @@ struct MatchInfo {
int nCol; /* Number of columns in table */
int nPhrase; /* Number of matchable phrases in query */
sqlite3_int64 nDoc; /* Number of docs in database */
+ char flag;
u32 *aMatchinfo; /* Pre-allocated buffer */
};
+/*
+** An instance of this structure is used to manage a pair of buffers, each
+** (nElem * sizeof(u32)) bytes in size. See the MatchinfoBuffer code below
+** for details.
+*/
+struct MatchinfoBuffer {
+ u8 aRef[3];
+ int nElem;
+ int bGlobal; /* Set if global data is loaded */
+ char *zMatchinfo;
+ u32 aMatchinfo[1];
+};
/*
@@ -125635,6 +158964,97 @@ struct StrBuffer {
};
+/*************************************************************************
+** Start of MatchinfoBuffer code.
+*/
+
+/*
+** Allocate a two-slot MatchinfoBuffer object.
+*/
+static MatchinfoBuffer *fts3MIBufferNew(int nElem, const char *zMatchinfo){
+ MatchinfoBuffer *pRet;
+ int nByte = sizeof(u32) * (2*nElem + 1) + sizeof(MatchinfoBuffer);
+ int nStr = (int)strlen(zMatchinfo);
+
+ pRet = sqlite3_malloc(nByte + nStr+1);
+ if( pRet ){
+ memset(pRet, 0, nByte);
+ pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
+ pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] + sizeof(u32)*(nElem+1);
+ pRet->nElem = nElem;
+ pRet->zMatchinfo = ((char*)pRet) + nByte;
+ memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
+ pRet->aRef[0] = 1;
+ }
+
+ return pRet;
+}
+
+static void fts3MIBufferFree(void *p){
+ MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
+
+ assert( (u32*)p==&pBuf->aMatchinfo[1]
+ || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
+ );
+ if( (u32*)p==&pBuf->aMatchinfo[1] ){
+ pBuf->aRef[1] = 0;
+ }else{
+ pBuf->aRef[2] = 0;
+ }
+
+ if( pBuf->aRef[0]==0 && pBuf->aRef[1]==0 && pBuf->aRef[2]==0 ){
+ sqlite3_free(pBuf);
+ }
+}
+
+static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
+ void (*xRet)(void*) = 0;
+ u32 *aOut = 0;
+
+ if( p->aRef[1]==0 ){
+ p->aRef[1] = 1;
+ aOut = &p->aMatchinfo[1];
+ xRet = fts3MIBufferFree;
+ }
+ else if( p->aRef[2]==0 ){
+ p->aRef[2] = 1;
+ aOut = &p->aMatchinfo[p->nElem+2];
+ xRet = fts3MIBufferFree;
+ }else{
+ aOut = (u32*)sqlite3_malloc(p->nElem * sizeof(u32));
+ if( aOut ){
+ xRet = sqlite3_free;
+ if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
+ }
+ }
+
+ *paOut = aOut;
+ return xRet;
+}
+
+static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
+ p->bGlobal = 1;
+ memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32));
+}
+
+/*
+** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
+*/
+SQLITE_PRIVATE void sqlite3Fts3MIBufferFree(MatchinfoBuffer *p){
+ if( p ){
+ assert( p->aRef[0]==1 );
+ p->aRef[0] = 0;
+ if( p->aRef[0]==0 && p->aRef[1]==0 && p->aRef[2]==0 ){
+ sqlite3_free(p);
+ }
+ }
+}
+
+/*
+** End of MatchinfoBuffer code.
+*************************************************************************/
+
+
/*
** This function is used to help iterate through a position-list. A position
** list is a list of unique integers, sorted from smallest to largest. Each
@@ -125657,7 +159077,7 @@ struct StrBuffer {
*/
static void fts3GetDeltaPosition(char **pp, int *piPos){
int iVal;
- *pp += sqlite3Fts3GetVarint32(*pp, &iVal);
+ *pp += fts3GetVarint32(*pp, &iVal);
*piPos += (iVal-2);
}
@@ -125671,7 +159091,7 @@ static int fts3ExprIterate2(
void *pCtx /* Second argument to pass to callback */
){
int rc; /* Return code */
- int eType = pExpr->eType; /* Type of expression node pExpr */
+ int eType = pExpr->eType; /* Type of expression node pExpr */
if( eType!=FTSQUERY_PHRASE ){
assert( pExpr->pLeft && pExpr->pRight );
@@ -125705,6 +159125,7 @@ static int fts3ExprIterate(
return fts3ExprIterate2(pExpr, &iPhrase, x, pCtx);
}
+
/*
** This is an fts3ExprIterate() callback used while loading the doclists
** for each phrase into Fts3Expr.aDoclist[]/nDoclist. See also
@@ -125749,8 +159170,7 @@ static int fts3ExprLoadDoclists(
static int fts3ExprPhraseCountCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
(*(int *)ctx)++;
- UNUSED_PARAMETER(pExpr);
- UNUSED_PARAMETER(iPhrase);
+ pExpr->iPhrase = iPhrase;
return SQLITE_OK;
}
static int fts3ExprPhraseCount(Fts3Expr *pExpr){
@@ -125889,23 +159309,27 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
SnippetIter *p = (SnippetIter *)ctx;
SnippetPhrase *pPhrase = &p->aPhrase[iPhrase];
char *pCsr;
+ int rc;
pPhrase->nToken = pExpr->pPhrase->nToken;
-
- pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
+ rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pCsr);
+ assert( rc==SQLITE_OK || pCsr==0 );
if( pCsr ){
int iFirst = 0;
pPhrase->pList = pCsr;
fts3GetDeltaPosition(&pCsr, &iFirst);
+ assert( iFirst>=0 );
pPhrase->pHead = pCsr;
pPhrase->pTail = pCsr;
pPhrase->iHead = iFirst;
pPhrase->iTail = iFirst;
}else{
- assert( pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0 );
+ assert( rc!=SQLITE_OK || (
+ pPhrase->pList==0 && pPhrase->pHead==0 && pPhrase->pTail==0
+ ));
}
- return SQLITE_OK;
+ return rc;
}
/*
@@ -125914,9 +159338,9 @@ static int fts3SnippetFindPositions(Fts3Expr *pExpr, int iPhrase, void *ctx){
** is the snippet with the highest score, where scores are calculated
** by adding:
**
-** (a) +1 point for each occurence of a matchable phrase in the snippet.
+** (a) +1 point for each occurrence of a matchable phrase in the snippet.
**
-** (b) +1000 points for the first occurence of each matchable phrase in
+** (b) +1000 points for the first occurrence of each matchable phrase in
** the snippet for which the corresponding mCovered bit is not set.
**
** The selected snippet parameters are stored in structure *pFragment before
@@ -125967,37 +159391,39 @@ static int fts3BestSnippet(
sIter.nSnippet = nSnippet;
sIter.nPhrase = nList;
sIter.iCurrent = -1;
- (void)fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void *)&sIter);
+ rc = fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void*)&sIter);
+ if( rc==SQLITE_OK ){
- /* Set the *pmSeen output variable. */
- for(i=0; i<nList; i++){
- if( sIter.aPhrase[i].pHead ){
- *pmSeen |= (u64)1 << i;
+ /* Set the *pmSeen output variable. */
+ for(i=0; i<nList; i++){
+ if( sIter.aPhrase[i].pHead ){
+ *pmSeen |= (u64)1 << i;
+ }
}
- }
- /* Loop through all candidate snippets. Store the best snippet in
- ** *pFragment. Store its associated 'score' in iBestScore.
- */
- pFragment->iCol = iCol;
- while( !fts3SnippetNextCandidate(&sIter) ){
- int iPos;
- int iScore;
- u64 mCover;
- u64 mHighlight;
- fts3SnippetDetails(&sIter, mCovered, &iPos, &iScore, &mCover, &mHighlight);
- assert( iScore>=0 );
- if( iScore>iBestScore ){
- pFragment->iPos = iPos;
- pFragment->hlmask = mHighlight;
- pFragment->covered = mCover;
- iBestScore = iScore;
+ /* Loop through all candidate snippets. Store the best snippet in
+ ** *pFragment. Store its associated 'score' in iBestScore.
+ */
+ pFragment->iCol = iCol;
+ while( !fts3SnippetNextCandidate(&sIter) ){
+ int iPos;
+ int iScore;
+ u64 mCover;
+ u64 mHighlite;
+ fts3SnippetDetails(&sIter, mCovered, &iPos, &iScore, &mCover,&mHighlite);
+ assert( iScore>=0 );
+ if( iScore>iBestScore ){
+ pFragment->iPos = iPos;
+ pFragment->hlmask = mHighlite;
+ pFragment->covered = mCover;
+ iBestScore = iScore;
+ }
}
- }
+ *piScore = iBestScore;
+ }
sqlite3_free(sIter.aPhrase);
- *piScore = iBestScore;
- return SQLITE_OK;
+ return rc;
}
@@ -126029,6 +159455,7 @@ static int fts3StringAppend(
pStr->z = zNew;
pStr->nAlloc = nAlloc;
}
+ assert( pStr->z!=0 && (pStr->nAlloc >= pStr->n+nAppend+1) );
/* Append the data to the string buffer. */
memcpy(&pStr->z[pStr->n], zAppend, nAppend);
@@ -126060,6 +159487,7 @@ static int fts3StringAppend(
*/
static int fts3SnippetShift(
Fts3Table *pTab, /* FTS3 table snippet comes from */
+ int iLangid, /* Language id to use in tokenizing */
int nSnippet, /* Number of tokens desired for snippet */
const char *zDoc, /* Document text to extract snippet from */
int nDoc, /* Size of buffer zDoc in bytes */
@@ -126095,13 +159523,12 @@ static int fts3SnippetShift(
/* Open a cursor on zDoc/nDoc. Check if there are (nSnippet+nDesired)
** or more tokens in zDoc/nDoc.
*/
- rc = pMod->xOpen(pTab->pTokenizer, zDoc, nDoc, &pC);
+ rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, iLangid, zDoc, nDoc, &pC);
if( rc!=SQLITE_OK ){
return rc;
}
- pC->pTokenizer = pTab->pTokenizer;
while( rc==SQLITE_OK && iCurrent<(nSnippet+nDesired) ){
- const char *ZDUMMY; int DUMMY1, DUMMY2, DUMMY3;
+ const char *ZDUMMY; int DUMMY1 = 0, DUMMY2 = 0, DUMMY3 = 0;
rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &DUMMY2, &DUMMY3, &iCurrent);
}
pMod->xClose(pC);
@@ -126145,8 +159572,6 @@ static int fts3SnippetText(
int iCol = pFragment->iCol+1; /* Query column to extract text from */
sqlite3_tokenizer_module *pMod; /* Tokenizer module methods object */
sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor open on zDoc/nDoc */
- const char *ZDUMMY; /* Dummy argument used with tokenizer */
- int DUMMY1; /* Dummy argument used with tokenizer */
zDoc = (const char *)sqlite3_column_text(pCsr->pStmt, iCol);
if( zDoc==0 ){
@@ -126159,17 +159584,29 @@ static int fts3SnippetText(
/* Open a token cursor on the document. */
pMod = (sqlite3_tokenizer_module *)pTab->pTokenizer->pModule;
- rc = pMod->xOpen(pTab->pTokenizer, zDoc, nDoc, &pC);
+ rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, pCsr->iLangid, zDoc,nDoc,&pC);
if( rc!=SQLITE_OK ){
return rc;
}
- pC->pTokenizer = pTab->pTokenizer;
while( rc==SQLITE_OK ){
- int iBegin; /* Offset in zDoc of start of token */
- int iFin; /* Offset in zDoc of end of token */
- int isHighlight; /* True for highlighted terms */
-
+ const char *ZDUMMY; /* Dummy argument used with tokenizer */
+ int DUMMY1 = -1; /* Dummy argument used with tokenizer */
+ int iBegin = 0; /* Offset in zDoc of start of token */
+ int iFin = 0; /* Offset in zDoc of end of token */
+ int isHighlight = 0; /* True for highlighted terms */
+
+ /* Variable DUMMY1 is initialized to a negative value above. Elsewhere
+ ** in the FTS code the variable that the third argument to xNext points to
+ ** is initialized to zero before the first (*but not necessarily
+ ** subsequent*) call to xNext(). This is done for a particular application
+ ** that needs to know whether or not the tokenizer is being used for
+ ** snippet generation or for some other purpose.
+ **
+ ** Extreme care is required when writing code to depend on this
+ ** initialization. It is not a documented part of the tokenizer interface.
+ ** If a tokenizer is used directly by any code outside of FTS, this
+ ** convention might not be respected. */
rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &iBegin, &iFin, &iCurrent);
if( rc!=SQLITE_OK ){
if( rc==SQLITE_DONE ){
@@ -126185,15 +159622,21 @@ static int fts3SnippetText(
if( !isShiftDone ){
int n = nDoc - iBegin;
- rc = fts3SnippetShift(pTab, nSnippet, &zDoc[iBegin], n, &iPos, &hlmask);
+ rc = fts3SnippetShift(
+ pTab, pCsr->iLangid, nSnippet, &zDoc[iBegin], n, &iPos, &hlmask
+ );
isShiftDone = 1;
/* Now that the shift has been done, check if the initial "..." are
** required. They are required if (a) this is not the first fragment,
** or (b) this fragment does not begin at position 0 of its column.
*/
- if( rc==SQLITE_OK && (iPos>0 || iFragment>0) ){
- rc = fts3StringAppend(pOut, zEllipsis, -1);
+ if( rc==SQLITE_OK ){
+ if( iPos>0 || iFragment>0 ){
+ rc = fts3StringAppend(pOut, zEllipsis, -1);
+ }else if( iBegin ){
+ rc = fts3StringAppend(pOut, zDoc, iBegin);
+ }
}
if( rc!=SQLITE_OK || iCurrent<iPos ) continue;
}
@@ -126250,6 +159693,60 @@ static int fts3ColumnlistCount(char **ppCollist){
}
/*
+** This function gathers 'y' or 'b' data for a single phrase.
+*/
+static void fts3ExprLHits(
+ Fts3Expr *pExpr, /* Phrase expression node */
+ MatchInfo *p /* Matchinfo context */
+){
+ Fts3Table *pTab = (Fts3Table *)p->pCursor->base.pVtab;
+ int iStart;
+ Fts3Phrase *pPhrase = pExpr->pPhrase;
+ char *pIter = pPhrase->doclist.pList;
+ int iCol = 0;
+
+ assert( p->flag==FTS3_MATCHINFO_LHITS_BM || p->flag==FTS3_MATCHINFO_LHITS );
+ if( p->flag==FTS3_MATCHINFO_LHITS ){
+ iStart = pExpr->iPhrase * p->nCol;
+ }else{
+ iStart = pExpr->iPhrase * ((p->nCol + 31) / 32);
+ }
+
+ while( 1 ){
+ int nHit = fts3ColumnlistCount(&pIter);
+ if( (pPhrase->iColumn>=pTab->nColumn || pPhrase->iColumn==iCol) ){
+ if( p->flag==FTS3_MATCHINFO_LHITS ){
+ p->aMatchinfo[iStart + iCol] = (u32)nHit;
+ }else if( nHit ){
+ p->aMatchinfo[iStart + (iCol+1)/32] |= (1 << (iCol&0x1F));
+ }
+ }
+ assert( *pIter==0x00 || *pIter==0x01 );
+ if( *pIter!=0x01 ) break;
+ pIter++;
+ pIter += fts3GetVarint32(pIter, &iCol);
+ }
+}
+
+/*
+** Gather the results for matchinfo directives 'y' and 'b'.
+*/
+static void fts3ExprLHitGather(
+ Fts3Expr *pExpr,
+ MatchInfo *p
+){
+ assert( (pExpr->pLeft==0)==(pExpr->pRight==0) );
+ if( pExpr->bEof==0 && pExpr->iDocid==p->pCursor->iPrevId ){
+ if( pExpr->pLeft ){
+ fts3ExprLHitGather(pExpr->pLeft, p);
+ fts3ExprLHitGather(pExpr->pRight, p);
+ }else{
+ fts3ExprLHits(pExpr, p);
+ }
+ }
+}
+
+/*
** fts3ExprIterate() callback used to collect the "global" matchinfo stats
** for a single query.
**
@@ -126297,13 +159794,14 @@ static int fts3ExprLocalHitsCb(
int iPhrase, /* Phrase number */
void *pCtx /* Pointer to MatchInfo structure */
){
+ int rc = SQLITE_OK;
MatchInfo *p = (MatchInfo *)pCtx;
int iStart = iPhrase * p->nCol * 3;
int i;
- for(i=0; i<p->nCol; i++){
+ for(i=0; i<p->nCol && rc==SQLITE_OK; i++){
char *pCsr;
- pCsr = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i);
+ rc = sqlite3Fts3EvalPhrasePoslist(p->pCursor, pExpr, i, &pCsr);
if( pCsr ){
p->aMatchinfo[iStart+i*3] = fts3ColumnlistCount(&pCsr);
}else{
@@ -126311,7 +159809,7 @@ static int fts3ExprLocalHitsCb(
}
}
- return SQLITE_OK;
+ return rc;
}
static int fts3MatchinfoCheck(
@@ -126321,15 +159819,17 @@ static int fts3MatchinfoCheck(
){
if( (cArg==FTS3_MATCHINFO_NPHRASE)
|| (cArg==FTS3_MATCHINFO_NCOL)
- || (cArg==FTS3_MATCHINFO_NDOC && pTab->bHasStat)
- || (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bHasStat)
+ || (cArg==FTS3_MATCHINFO_NDOC && pTab->bFts4)
+ || (cArg==FTS3_MATCHINFO_AVGLENGTH && pTab->bFts4)
|| (cArg==FTS3_MATCHINFO_LENGTH && pTab->bHasDocsize)
|| (cArg==FTS3_MATCHINFO_LCS)
|| (cArg==FTS3_MATCHINFO_HITS)
+ || (cArg==FTS3_MATCHINFO_LHITS)
+ || (cArg==FTS3_MATCHINFO_LHITS_BM)
){
return SQLITE_OK;
}
- *pzErr = sqlite3_mprintf("unrecognized matchinfo request: %c", cArg);
+ sqlite3Fts3ErrMsg(pzErr, "unrecognized matchinfo request: %c", cArg);
return SQLITE_ERROR;
}
@@ -126349,6 +159849,14 @@ static int fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
nVal = pInfo->nCol;
break;
+ case FTS3_MATCHINFO_LHITS:
+ nVal = pInfo->nCol * pInfo->nPhrase;
+ break;
+
+ case FTS3_MATCHINFO_LHITS_BM:
+ nVal = pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
+ break;
+
default:
assert( cArg==FTS3_MATCHINFO_HITS );
nVal = pInfo->nCol * pInfo->nPhrase * 3;
@@ -126377,7 +159885,7 @@ static int fts3MatchinfoSelectDoctotal(
a = sqlite3_column_blob(pStmt, 0);
a += sqlite3Fts3GetVarint(a, &nDoc);
- if( nDoc==0 ) return SQLITE_CORRUPT_VTAB;
+ if( nDoc==0 ) return FTS_CORRUPT_VTAB;
*pnDoc = (u32)nDoc;
if( paLen ) *paLen = a;
@@ -126472,8 +159980,10 @@ static int fts3MatchinfoLcs(Fts3Cursor *pCsr, MatchInfo *pInfo){
int nLive = 0; /* Number of iterators in aIter not at EOF */
for(i=0; i<pInfo->nPhrase; i++){
+ int rc;
LcsIterator *pIt = &aIter[i];
- pIt->pRead = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol);
+ rc = sqlite3Fts3EvalPhrasePoslist(pCsr, pIt->pExpr, iCol, &pIt->pRead);
+ if( rc!=SQLITE_OK ) return rc;
if( pIt->pRead ){
pIt->iPos = pIt->iPosOffset;
fts3LcsIteratorAdvance(&aIter[i]);
@@ -126541,7 +160051,7 @@ static int fts3MatchinfoValues(
sqlite3_stmt *pSelect = 0;
for(i=0; rc==SQLITE_OK && zArg[i]; i++){
-
+ pInfo->flag = zArg[i];
switch( zArg[i] ){
case FTS3_MATCHINFO_NPHRASE:
if( bGlobal ) pInfo->aMatchinfo[0] = pInfo->nPhrase;
@@ -126601,6 +160111,14 @@ static int fts3MatchinfoValues(
}
break;
+ case FTS3_MATCHINFO_LHITS_BM:
+ case FTS3_MATCHINFO_LHITS: {
+ int nZero = fts3MatchinfoSize(pInfo, zArg[i]) * sizeof(u32);
+ memset(pInfo->aMatchinfo, 0, nZero);
+ fts3ExprLHitGather(pCsr->pExpr, pInfo);
+ break;
+ }
+
default: {
Fts3Expr *pExpr;
assert( zArg[i]==FTS3_MATCHINFO_HITS );
@@ -126613,6 +160131,7 @@ static int fts3MatchinfoValues(
if( rc!=SQLITE_OK ) break;
}
rc = fts3ExprIterate(pExpr, fts3ExprGlobalHitsCb,(void*)pInfo);
+ sqlite3Fts3EvalTestDeferred(pCsr, &rc);
if( rc!=SQLITE_OK ) break;
}
(void)fts3ExprIterate(pExpr, fts3ExprLocalHitsCb,(void*)pInfo);
@@ -126632,7 +160151,8 @@ static int fts3MatchinfoValues(
** Populate pCsr->aMatchinfo[] with data for the current row. The
** 'matchinfo' data is an array of 32-bit unsigned integers (C type u32).
*/
-static int fts3GetMatchinfo(
+static void fts3GetMatchinfo(
+ sqlite3_context *pCtx, /* Return results here */
Fts3Cursor *pCsr, /* FTS3 Cursor object */
const char *zArg /* Second argument to matchinfo() function */
){
@@ -126641,6 +160161,9 @@ static int fts3GetMatchinfo(
int rc = SQLITE_OK;
int bGlobal = 0; /* Collect 'global' stats as well as local */
+ u32 *aOut = 0;
+ void (*xDestroyOut)(void*) = 0;
+
memset(&sInfo, 0, sizeof(MatchInfo));
sInfo.pCursor = pCsr;
sInfo.nCol = pTab->nColumn;
@@ -126648,21 +160171,18 @@ static int fts3GetMatchinfo(
/* If there is cached matchinfo() data, but the format string for the
** cache does not match the format string for this request, discard
** the cached data. */
- if( pCsr->zMatchinfo && strcmp(pCsr->zMatchinfo, zArg) ){
- assert( pCsr->aMatchinfo );
- sqlite3_free(pCsr->aMatchinfo);
- pCsr->zMatchinfo = 0;
- pCsr->aMatchinfo = 0;
+ if( pCsr->pMIBuffer && strcmp(pCsr->pMIBuffer->zMatchinfo, zArg) ){
+ sqlite3Fts3MIBufferFree(pCsr->pMIBuffer);
+ pCsr->pMIBuffer = 0;
}
- /* If Fts3Cursor.aMatchinfo[] is NULL, then this is the first time the
+ /* If Fts3Cursor.pMIBuffer is NULL, then this is the first time the
** matchinfo function has been called for this query. In this case
** allocate the array used to accumulate the matchinfo data and
** initialize those elements that are constant for every row.
*/
- if( pCsr->aMatchinfo==0 ){
+ if( pCsr->pMIBuffer==0 ){
int nMatchinfo = 0; /* Number of u32 elements in match-info */
- int nArg; /* Bytes in zArg */
int i; /* Used to iterate through zArg */
/* Determine the number of phrases in the query */
@@ -126671,30 +160191,46 @@ static int fts3GetMatchinfo(
/* Determine the number of integers in the buffer returned by this call. */
for(i=0; zArg[i]; i++){
+ char *zErr = 0;
+ if( fts3MatchinfoCheck(pTab, zArg[i], &zErr) ){
+ sqlite3_result_error(pCtx, zErr, -1);
+ sqlite3_free(zErr);
+ return;
+ }
nMatchinfo += fts3MatchinfoSize(&sInfo, zArg[i]);
}
/* Allocate space for Fts3Cursor.aMatchinfo[] and Fts3Cursor.zMatchinfo. */
- nArg = (int)strlen(zArg);
- pCsr->aMatchinfo = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo + nArg + 1);
- if( !pCsr->aMatchinfo ) return SQLITE_NOMEM;
-
- pCsr->zMatchinfo = (char *)&pCsr->aMatchinfo[nMatchinfo];
- pCsr->nMatchinfo = nMatchinfo;
- memcpy(pCsr->zMatchinfo, zArg, nArg+1);
- memset(pCsr->aMatchinfo, 0, sizeof(u32)*nMatchinfo);
+ pCsr->pMIBuffer = fts3MIBufferNew(nMatchinfo, zArg);
+ if( !pCsr->pMIBuffer ) rc = SQLITE_NOMEM;
+
pCsr->isMatchinfoNeeded = 1;
bGlobal = 1;
}
- sInfo.aMatchinfo = pCsr->aMatchinfo;
- sInfo.nPhrase = pCsr->nPhrase;
- if( pCsr->isMatchinfoNeeded ){
+ if( rc==SQLITE_OK ){
+ xDestroyOut = fts3MIBufferAlloc(pCsr->pMIBuffer, &aOut);
+ if( xDestroyOut==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ sInfo.aMatchinfo = aOut;
+ sInfo.nPhrase = pCsr->nPhrase;
rc = fts3MatchinfoValues(pCsr, bGlobal, &sInfo, zArg);
- pCsr->isMatchinfoNeeded = 0;
+ if( bGlobal ){
+ fts3MIBufferSetGlobal(pCsr->pMIBuffer);
+ }
}
- return rc;
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(pCtx, rc);
+ if( xDestroyOut ) xDestroyOut(aOut);
+ }else{
+ int n = pCsr->pMIBuffer->nElem * sizeof(u32);
+ sqlite3_result_blob(pCtx, aOut, n, xDestroyOut);
+ }
}
/*
@@ -126756,7 +160292,7 @@ SQLITE_PRIVATE void sqlite3Fts3Snippet(
*/
for(iRead=0; iRead<pTab->nColumn; iRead++){
SnippetFragment sF = {0, 0, 0, 0};
- int iS;
+ int iS = 0;
if( iCol>=0 && iRead!=iCol ) continue;
/* Find the best snippet of nFToken tokens in column iRead. */
@@ -126825,9 +160361,10 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
int iTerm; /* For looping through nTerm phrase terms */
char *pList; /* Pointer to position list for phrase */
int iPos = 0; /* First position in position-list */
+ int rc;
UNUSED_PARAMETER(iPhrase);
- pList = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol);
+ rc = sqlite3Fts3EvalPhrasePoslist(p->pCsr, pExpr, p->iCol, &pList);
nTerm = pExpr->pPhrase->nToken;
if( pList ){
fts3GetDeltaPosition(&pList, &iPos);
@@ -126841,7 +160378,7 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
pT->iPos = iPos;
}
- return SQLITE_OK;
+ return rc;
}
/*
@@ -126853,8 +160390,6 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
sqlite3_tokenizer_module const *pMod = pTab->pTokenizer->pModule;
- const char *ZDUMMY; /* Dummy argument used with xNext() */
- int NDUMMY; /* Dummy argument used with xNext() */
int rc; /* Return Code */
int nToken; /* Number of tokens in query */
int iCol; /* Column currently being processed */
@@ -126887,9 +160422,11 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
*/
for(iCol=0; iCol<pTab->nColumn; iCol++){
sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor */
- int iStart;
- int iEnd;
- int iCurrent;
+ const char *ZDUMMY; /* Dummy argument used with xNext() */
+ int NDUMMY = 0; /* Dummy argument used with xNext() */
+ int iStart = 0;
+ int iEnd = 0;
+ int iCurrent = 0;
const char *zDoc;
int nDoc;
@@ -126899,7 +160436,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
*/
sCtx.iCol = iCol;
sCtx.iTerm = 0;
- (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void *)&sCtx);
+ (void)fts3ExprIterate(pCsr->pExpr, fts3ExprTermOffsetInit, (void*)&sCtx);
/* Retreive the text stored in column iCol. If an SQL NULL is stored
** in column iCol, jump immediately to the next iteration of the loop.
@@ -126918,9 +160455,10 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
}
/* Initialize a tokenizer iterator to iterate through column iCol. */
- rc = pMod->xOpen(pTab->pTokenizer, zDoc, nDoc, &pC);
+ rc = sqlite3Fts3OpenTokenizer(pTab->pTokenizer, pCsr->iLangid,
+ zDoc, nDoc, &pC
+ );
if( rc!=SQLITE_OK ) goto offsets_out;
- pC->pTokenizer = pTab->pTokenizer;
rc = pMod->xNext(pC, &ZDUMMY, &NDUMMY, &iStart, &iEnd, &iCurrent);
while( rc==SQLITE_OK ){
@@ -126938,7 +160476,7 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
if( !pTerm ){
/* All offsets for this column have been gathered. */
- break;
+ rc = SQLITE_DONE;
}else{
assert( iCurrent<=iMinPos );
if( 0==(0xFE&*pTerm->pList) ){
@@ -126955,8 +160493,8 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
"%d %d %d %d ", iCol, pTerm-sCtx.aTerm, iStart, iEnd-iStart
);
rc = fts3StringAppend(&res, aBuffer, -1);
- }else if( rc==SQLITE_DONE ){
- rc = SQLITE_CORRUPT_VTAB;
+ }else if( rc==SQLITE_DONE && pTab->zContentTbl==0 ){
+ rc = FTS_CORRUPT_VTAB;
}
}
}
@@ -126990,19 +160528,9 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
const char *zArg /* Second arg to matchinfo() function */
){
Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab;
- int rc;
- int i;
const char *zFormat;
if( zArg ){
- for(i=0; zArg[i]; i++){
- char *zErr = 0;
- if( fts3MatchinfoCheck(pTab, zArg[i], &zErr) ){
- sqlite3_result_error(pContext, zErr, -1);
- sqlite3_free(zErr);
- return;
- }
- }
zFormat = zArg;
}else{
zFormat = FTS3_MATCHINFO_DEFAULT;
@@ -127011,23 +160539,780 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
if( !pCsr->pExpr ){
sqlite3_result_blob(pContext, "", 0, SQLITE_STATIC);
return;
+ }else{
+ /* Retrieve matchinfo() data. */
+ fts3GetMatchinfo(pContext, pCsr, zFormat);
+ sqlite3Fts3SegmentsClose(pTab);
}
+}
- /* Retrieve matchinfo() data. */
- rc = fts3GetMatchinfo(pCsr, zFormat);
- sqlite3Fts3SegmentsClose(pTab);
+#endif
+
+/************** End of fts3_snippet.c ****************************************/
+/************** Begin file fts3_unicode.c ************************************/
+/*
+** 2012 May 24
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Implementation of the "unicode" full-text-search tokenizer.
+*/
+
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
+
+/* #include "fts3Int.h" */
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
+
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+/* #include <stdio.h> */
+/* #include <string.h> */
+
+/* #include "fts3_tokenizer.h" */
+
+/*
+** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied
+** from the sqlite3 source file utf.c. If this file is compiled as part
+** of the amalgamation, they are not required.
+*/
+#ifndef SQLITE_AMALGAMATION
+
+static const unsigned char sqlite3Utf8Trans1[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
+};
+
+#define READ_UTF8(zIn, zTerm, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = sqlite3Utf8Trans1[c-0xc0]; \
+ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ if( c<0x80 \
+ || (c&0xFFFFF800)==0xD800 \
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
+ }
+
+#define WRITE_UTF8(zOut, c) { \
+ if( c<0x00080 ){ \
+ *zOut++ = (u8)(c&0xFF); \
+ } \
+ else if( c<0x00800 ){ \
+ *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \
+ } \
+ else if( c<0x10000 ){ \
+ *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \
+ }else{ \
+ *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \
+ *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \
+ } \
+}
+
+#endif /* ifndef SQLITE_AMALGAMATION */
+
+typedef struct unicode_tokenizer unicode_tokenizer;
+typedef struct unicode_cursor unicode_cursor;
+
+struct unicode_tokenizer {
+ sqlite3_tokenizer base;
+ int bRemoveDiacritic;
+ int nException;
+ int *aiException;
+};
+
+struct unicode_cursor {
+ sqlite3_tokenizer_cursor base;
+ const unsigned char *aInput; /* Input text being tokenized */
+ int nInput; /* Size of aInput[] in bytes */
+ int iOff; /* Current offset within aInput[] */
+ int iToken; /* Index of next token to be returned */
+ char *zToken; /* storage for current token */
+ int nAlloc; /* space allocated at zToken */
+};
+
+
+/*
+** Destroy a tokenizer allocated by unicodeCreate().
+*/
+static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){
+ if( pTokenizer ){
+ unicode_tokenizer *p = (unicode_tokenizer *)pTokenizer;
+ sqlite3_free(p->aiException);
+ sqlite3_free(p);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** As part of a tokenchars= or separators= option, the CREATE VIRTUAL TABLE
+** statement has specified that the tokenizer for this table shall consider
+** all characters in string zIn/nIn to be separators (if bAlnum==0) or
+** token characters (if bAlnum==1).
+**
+** For each codepoint in the zIn/nIn string, this function checks if the
+** sqlite3FtsUnicodeIsalnum() function already returns the desired result.
+** If so, no action is taken. Otherwise, the codepoint is added to the
+** unicode_tokenizer.aiException[] array. For the purposes of tokenization,
+** the return value of sqlite3FtsUnicodeIsalnum() is inverted for all
+** codepoints in the aiException[] array.
+**
+** If a standalone diacritic mark (one that sqlite3FtsUnicodeIsdiacritic()
+** identifies as a diacritic) occurs in the zIn/nIn string it is ignored.
+** It is not possible to change the behavior of the tokenizer with respect
+** to these codepoints.
+*/
+static int unicodeAddExceptions(
+ unicode_tokenizer *p, /* Tokenizer to add exceptions to */
+ int bAlnum, /* Replace Isalnum() return value with this */
+ const char *zIn, /* Array of characters to make exceptions */
+ int nIn /* Length of z in bytes */
+){
+ const unsigned char *z = (const unsigned char *)zIn;
+ const unsigned char *zTerm = &z[nIn];
+ int iCode;
+ int nEntry = 0;
+
+ assert( bAlnum==0 || bAlnum==1 );
+
+ while( z<zTerm ){
+ READ_UTF8(z, zTerm, iCode);
+ assert( (sqlite3FtsUnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
+ if( sqlite3FtsUnicodeIsalnum(iCode)!=bAlnum
+ && sqlite3FtsUnicodeIsdiacritic(iCode)==0
+ ){
+ nEntry++;
+ }
+ }
+
+ if( nEntry ){
+ int *aNew; /* New aiException[] array */
+ int nNew; /* Number of valid entries in array aNew[] */
+
+ aNew = sqlite3_realloc(p->aiException, (p->nException+nEntry)*sizeof(int));
+ if( aNew==0 ) return SQLITE_NOMEM;
+ nNew = p->nException;
+
+ z = (const unsigned char *)zIn;
+ while( z<zTerm ){
+ READ_UTF8(z, zTerm, iCode);
+ if( sqlite3FtsUnicodeIsalnum(iCode)!=bAlnum
+ && sqlite3FtsUnicodeIsdiacritic(iCode)==0
+ ){
+ int i, j;
+ for(i=0; i<nNew && aNew[i]<iCode; i++);
+ for(j=nNew; j>i; j--) aNew[j] = aNew[j-1];
+ aNew[i] = iCode;
+ nNew++;
+ }
+ }
+ p->aiException = aNew;
+ p->nException = nNew;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Return true if the p->aiException[] array contains the value iCode.
+*/
+static int unicodeIsException(unicode_tokenizer *p, int iCode){
+ if( p->nException>0 ){
+ int *a = p->aiException;
+ int iLo = 0;
+ int iHi = p->nException-1;
+
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( iCode==a[iTest] ){
+ return 1;
+ }else if( iCode>a[iTest] ){
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+** Return true if, for the purposes of tokenization, codepoint iCode is
+** considered a token character (not a separator).
+*/
+static int unicodeIsAlnum(unicode_tokenizer *p, int iCode){
+ assert( (sqlite3FtsUnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
+ return sqlite3FtsUnicodeIsalnum(iCode) ^ unicodeIsException(p, iCode);
+}
+
+/*
+** Create a new tokenizer instance.
+*/
+static int unicodeCreate(
+ int nArg, /* Size of array argv[] */
+ const char * const *azArg, /* Tokenizer creation arguments */
+ sqlite3_tokenizer **pp /* OUT: New tokenizer handle */
+){
+ unicode_tokenizer *pNew; /* New tokenizer object */
+ int i;
+ int rc = SQLITE_OK;
+
+ pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer));
+ if( pNew==NULL ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(unicode_tokenizer));
+ pNew->bRemoveDiacritic = 1;
+
+ for(i=0; rc==SQLITE_OK && i<nArg; i++){
+ const char *z = azArg[i];
+ int n = (int)strlen(z);
+
+ if( n==19 && memcmp("remove_diacritics=1", z, 19)==0 ){
+ pNew->bRemoveDiacritic = 1;
+ }
+ else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){
+ pNew->bRemoveDiacritic = 0;
+ }
+ else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){
+ rc = unicodeAddExceptions(pNew, 1, &z[11], n-11);
+ }
+ else if( n>=11 && memcmp("separators=", z, 11)==0 ){
+ rc = unicodeAddExceptions(pNew, 0, &z[11], n-11);
+ }
+ else{
+ /* Unrecognized argument */
+ rc = SQLITE_ERROR;
+ }
+ }
if( rc!=SQLITE_OK ){
- sqlite3_result_error_code(pContext, rc);
+ unicodeDestroy((sqlite3_tokenizer *)pNew);
+ pNew = 0;
+ }
+ *pp = (sqlite3_tokenizer *)pNew;
+ return rc;
+}
+
+/*
+** Prepare to begin tokenizing a particular string. The input
+** string to be tokenized is pInput[0..nBytes-1]. A cursor
+** used to incrementally tokenize this string is returned in
+** *ppCursor.
+*/
+static int unicodeOpen(
+ sqlite3_tokenizer *p, /* The tokenizer */
+ const char *aInput, /* Input string */
+ int nInput, /* Size of string aInput in bytes */
+ sqlite3_tokenizer_cursor **pp /* OUT: New cursor object */
+){
+ unicode_cursor *pCsr;
+
+ pCsr = (unicode_cursor *)sqlite3_malloc(sizeof(unicode_cursor));
+ if( pCsr==0 ){
+ return SQLITE_NOMEM;
+ }
+ memset(pCsr, 0, sizeof(unicode_cursor));
+
+ pCsr->aInput = (const unsigned char *)aInput;
+ if( aInput==0 ){
+ pCsr->nInput = 0;
+ }else if( nInput<0 ){
+ pCsr->nInput = (int)strlen(aInput);
}else{
- int n = pCsr->nMatchinfo * sizeof(u32);
- sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT);
+ pCsr->nInput = nInput;
}
+
+ *pp = &pCsr->base;
+ UNUSED_PARAMETER(p);
+ return SQLITE_OK;
}
-#endif
+/*
+** Close a tokenization cursor previously opened by a call to
+** simpleOpen() above.
+*/
+static int unicodeClose(sqlite3_tokenizer_cursor *pCursor){
+ unicode_cursor *pCsr = (unicode_cursor *) pCursor;
+ sqlite3_free(pCsr->zToken);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
-/************** End of fts3_snippet.c ****************************************/
+/*
+** Extract the next token from a tokenization cursor. The cursor must
+** have been opened by a prior call to simpleOpen().
+*/
+static int unicodeNext(
+ sqlite3_tokenizer_cursor *pC, /* Cursor returned by simpleOpen */
+ const char **paToken, /* OUT: Token text */
+ int *pnToken, /* OUT: Number of bytes at *paToken */
+ int *piStart, /* OUT: Starting offset of token */
+ int *piEnd, /* OUT: Ending offset of token */
+ int *piPos /* OUT: Position integer of token */
+){
+ unicode_cursor *pCsr = (unicode_cursor *)pC;
+ unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer);
+ int iCode = 0;
+ char *zOut;
+ const unsigned char *z = &pCsr->aInput[pCsr->iOff];
+ const unsigned char *zStart = z;
+ const unsigned char *zEnd;
+ const unsigned char *zTerm = &pCsr->aInput[pCsr->nInput];
+
+ /* Scan past any delimiter characters before the start of the next token.
+ ** Return SQLITE_DONE early if this takes us all the way to the end of
+ ** the input. */
+ while( z<zTerm ){
+ READ_UTF8(z, zTerm, iCode);
+ if( unicodeIsAlnum(p, iCode) ) break;
+ zStart = z;
+ }
+ if( zStart>=zTerm ) return SQLITE_DONE;
+
+ zOut = pCsr->zToken;
+ do {
+ int iOut;
+
+ /* Grow the output buffer if required. */
+ if( (zOut-pCsr->zToken)>=(pCsr->nAlloc-4) ){
+ char *zNew = sqlite3_realloc(pCsr->zToken, pCsr->nAlloc+64);
+ if( !zNew ) return SQLITE_NOMEM;
+ zOut = &zNew[zOut - pCsr->zToken];
+ pCsr->zToken = zNew;
+ pCsr->nAlloc += 64;
+ }
+
+ /* Write the folded case of the last character read to the output */
+ zEnd = z;
+ iOut = sqlite3FtsUnicodeFold(iCode, p->bRemoveDiacritic);
+ if( iOut ){
+ WRITE_UTF8(zOut, iOut);
+ }
+
+ /* If the cursor is not at EOF, read the next character */
+ if( z>=zTerm ) break;
+ READ_UTF8(z, zTerm, iCode);
+ }while( unicodeIsAlnum(p, iCode)
+ || sqlite3FtsUnicodeIsdiacritic(iCode)
+ );
+
+ /* Set the output variables and return. */
+ pCsr->iOff = (int)(z - pCsr->aInput);
+ *paToken = pCsr->zToken;
+ *pnToken = (int)(zOut - pCsr->zToken);
+ *piStart = (int)(zStart - pCsr->aInput);
+ *piEnd = (int)(zEnd - pCsr->aInput);
+ *piPos = pCsr->iToken++;
+ return SQLITE_OK;
+}
+
+/*
+** Set *ppModule to a pointer to the sqlite3_tokenizer_module
+** structure for the unicode tokenizer.
+*/
+SQLITE_PRIVATE void sqlite3Fts3UnicodeTokenizer(sqlite3_tokenizer_module const **ppModule){
+ static const sqlite3_tokenizer_module module = {
+ 0,
+ unicodeCreate,
+ unicodeDestroy,
+ unicodeOpen,
+ unicodeClose,
+ unicodeNext,
+ 0,
+ };
+ *ppModule = &module;
+}
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
+#endif /* ifndef SQLITE_DISABLE_FTS3_UNICODE */
+
+/************** End of fts3_unicode.c ****************************************/
+/************** Begin file fts3_unicode2.c ***********************************/
+/*
+** 2012 May 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+*/
+
+/*
+** DO NOT EDIT THIS MACHINE GENERATED FILE.
+*/
+
+#ifndef SQLITE_DISABLE_FTS3_UNICODE
+#if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
+
+/* #include <assert.h> */
+
+/*
+** Return true if the argument corresponds to a unicode codepoint
+** classified as either a letter or a number. Otherwise false.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int c){
+ /* Each unsigned integer in the following array corresponds to a contiguous
+ ** range of unicode codepoints that are not either letters or numbers (i.e.
+ ** codepoints for which this function should return 0).
+ **
+ ** The most significant 22 bits in each 32-bit value contain the first
+ ** codepoint in the range. The least significant 10 bits are used to store
+ ** the size of the range (always at least 1). In other words, the value
+ ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
+ ** C. It is not possible to represent a range larger than 1023 codepoints
+ ** using this format.
+ */
+ static const unsigned int aEntry[] = {
+ 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
+ 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
+ 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
+ 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
+ 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
+ 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
+ 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
+ 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
+ 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
+ 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
+ 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
+ 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
+ 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
+ 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
+ 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
+ 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
+ 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
+ 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
+ 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
+ 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
+ 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
+ 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
+ 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
+ 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
+ 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
+ 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
+ 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
+ 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
+ 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
+ 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
+ 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
+ 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
+ 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
+ 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
+ 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
+ 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
+ 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
+ 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
+ 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
+ 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
+ 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
+ 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
+ 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
+ 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
+ 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
+ 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
+ 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
+ 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
+ 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
+ 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
+ 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
+ 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
+ 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
+ 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
+ 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
+ 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
+ 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
+ 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
+ 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
+ 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
+ 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
+ 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
+ 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
+ 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
+ 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
+ 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
+ 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
+ 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
+ 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
+ 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
+ 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
+ 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
+ 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
+ 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
+ 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
+ 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
+ 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
+ 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
+ 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
+ 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
+ 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
+ 0x380400F0,
+ };
+ static const unsigned int aAscii[4] = {
+ 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
+ };
+
+ if( c<128 ){
+ return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
+ }else if( c<(1<<22) ){
+ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
+ int iRes = 0;
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aEntry[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( aEntry[0]<key );
+ assert( key>=aEntry[iRes] );
+ return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
+ }
+ return 1;
+}
+
+
+/*
+** If the argument is a codepoint corresponding to a lowercase letter
+** in the ASCII range with a diacritic added, return the codepoint
+** of the ASCII letter only. For example, if passed 235 - "LATIN
+** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER
+** E"). The resuls of passing a codepoint that corresponds to an
+** uppercase letter are undefined.
+*/
+static int remove_diacritic(int c){
+ unsigned short aDia[] = {
+ 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
+ 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
+ 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
+ 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
+ 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928,
+ 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234,
+ 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504,
+ 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529,
+ 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
+ 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122,
+ 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536,
+ 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
+ 62924, 63050, 63082, 63274, 63390,
+ };
+ char aChar[] = {
+ '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c',
+ 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
+ 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
+ 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
+ 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
+ '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
+ 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
+ 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
+ 'e', 'i', 'o', 'u', 'y',
+ };
+
+ unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
+ int iRes = 0;
+ int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aDia[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( key>=aDia[iRes] );
+ return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
+}
+
+
+/*
+** Return true if the argument interpreted as a unicode codepoint
+** is a diacritical modifier character.
+*/
+SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int c){
+ unsigned int mask0 = 0x08029FDF;
+ unsigned int mask1 = 0x000361F8;
+ if( c<768 || c>817 ) return 0;
+ return (c < 768+32) ?
+ (mask0 & (1 << (c-768))) :
+ (mask1 & (1 << (c-768-32)));
+}
+
+
+/*
+** Interpret the argument as a unicode codepoint. If the codepoint
+** is an upper case character that has a lower case equivalent,
+** return the codepoint corresponding to the lower case version.
+** Otherwise, return a copy of the argument.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int bRemoveDiacritic){
+ /* Each entry in the following array defines a rule for folding a range
+ ** of codepoints to lower case. The rule applies to a range of nRange
+ ** codepoints starting at codepoint iCode.
+ **
+ ** If the least significant bit in flags is clear, then the rule applies
+ ** to all nRange codepoints (i.e. all nRange codepoints are upper case and
+ ** need to be folded). Or, if it is set, then the rule only applies to
+ ** every second codepoint in the range, starting with codepoint C.
+ **
+ ** The 7 most significant bits in flags are an index into the aiOff[]
+ ** array. If a specific codepoint C does require folding, then its lower
+ ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF).
+ **
+ ** The contents of this array are generated by parsing the CaseFolding.txt
+ ** file distributed as part of the "Unicode Character Database". See
+ ** http://www.unicode.org for details.
+ */
+ static const struct TableEntry {
+ unsigned short iCode;
+ unsigned char flags;
+ unsigned char nRange;
+ } aEntry[] = {
+ {65, 14, 26}, {181, 64, 1}, {192, 14, 23},
+ {216, 14, 7}, {256, 1, 48}, {306, 1, 6},
+ {313, 1, 16}, {330, 1, 46}, {376, 116, 1},
+ {377, 1, 6}, {383, 104, 1}, {385, 50, 1},
+ {386, 1, 4}, {390, 44, 1}, {391, 0, 1},
+ {393, 42, 2}, {395, 0, 1}, {398, 32, 1},
+ {399, 38, 1}, {400, 40, 1}, {401, 0, 1},
+ {403, 42, 1}, {404, 46, 1}, {406, 52, 1},
+ {407, 48, 1}, {408, 0, 1}, {412, 52, 1},
+ {413, 54, 1}, {415, 56, 1}, {416, 1, 6},
+ {422, 60, 1}, {423, 0, 1}, {425, 60, 1},
+ {428, 0, 1}, {430, 60, 1}, {431, 0, 1},
+ {433, 58, 2}, {435, 1, 4}, {439, 62, 1},
+ {440, 0, 1}, {444, 0, 1}, {452, 2, 1},
+ {453, 0, 1}, {455, 2, 1}, {456, 0, 1},
+ {458, 2, 1}, {459, 1, 18}, {478, 1, 18},
+ {497, 2, 1}, {498, 1, 4}, {502, 122, 1},
+ {503, 134, 1}, {504, 1, 40}, {544, 110, 1},
+ {546, 1, 18}, {570, 70, 1}, {571, 0, 1},
+ {573, 108, 1}, {574, 68, 1}, {577, 0, 1},
+ {579, 106, 1}, {580, 28, 1}, {581, 30, 1},
+ {582, 1, 10}, {837, 36, 1}, {880, 1, 4},
+ {886, 0, 1}, {902, 18, 1}, {904, 16, 3},
+ {908, 26, 1}, {910, 24, 2}, {913, 14, 17},
+ {931, 14, 9}, {962, 0, 1}, {975, 4, 1},
+ {976, 140, 1}, {977, 142, 1}, {981, 146, 1},
+ {982, 144, 1}, {984, 1, 24}, {1008, 136, 1},
+ {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1},
+ {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1},
+ {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32},
+ {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1},
+ {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38},
+ {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1},
+ {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1},
+ {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6},
+ {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6},
+ {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8},
+ {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2},
+ {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1},
+ {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2},
+ {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2},
+ {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2},
+ {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1},
+ {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16},
+ {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47},
+ {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1},
+ {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1},
+ {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1},
+ {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2},
+ {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
+ {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14},
+ {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
+ {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
+ {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
+ {65313, 14, 26},
+ };
+ static const unsigned short aiOff[] = {
+ 1, 2, 8, 15, 16, 26, 28, 32,
+ 37, 38, 40, 48, 63, 64, 69, 71,
+ 79, 80, 116, 202, 203, 205, 206, 207,
+ 209, 210, 211, 213, 214, 217, 218, 219,
+ 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
+ 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
+ 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
+ 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
+ 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
+ 65514, 65521, 65527, 65528, 65529,
+ };
+
+ int ret = c;
+
+ assert( c>=0 );
+ assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );
+
+ if( c<128 ){
+ if( c>='A' && c<='Z' ) ret = c + ('a' - 'A');
+ }else if( c<65536 ){
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ int iRes = -1;
+
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ int cmp = (c - aEntry[iTest].iCode);
+ if( cmp>=0 ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( iRes<0 || c>=aEntry[iRes].iCode );
+
+ if( iRes>=0 ){
+ const struct TableEntry *p = &aEntry[iRes];
+ if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
+ ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
+ assert( ret>0 );
+ }
+ }
+
+ if( bRemoveDiacritic ) ret = remove_diacritic(ret);
+ }
+
+ else if( c>=66560 && c<66600 ){
+ ret = c + 40;
+ }
+
+ return ret;
+}
+#endif /* defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4) */
+#endif /* !defined(SQLITE_DISABLE_FTS3_UNICODE) */
+
+/************** End of fts3_unicode2.c ***************************************/
/************** Begin file rtree.c *******************************************/
/*
** 2001 September 15
@@ -127085,60 +161370,22 @@ SQLITE_PRIVATE void sqlite3Fts3Matchinfo(
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE)
-/*
-** This file contains an implementation of a couple of different variants
-** of the r-tree algorithm. See the README file for further details. The
-** same data-structure is used for all, but the algorithms for insert and
-** delete operations vary. The variants used are selected at compile time
-** by defining the following symbols:
-*/
-
-/* Either, both or none of the following may be set to activate
-** r*tree variant algorithms.
-*/
-#define VARIANT_RSTARTREE_CHOOSESUBTREE 0
-#define VARIANT_RSTARTREE_REINSERT 1
-
-/*
-** Exactly one of the following must be set to 1.
-*/
-#define VARIANT_GUTTMAN_QUADRATIC_SPLIT 0
-#define VARIANT_GUTTMAN_LINEAR_SPLIT 0
-#define VARIANT_RSTARTREE_SPLIT 1
-
-#define VARIANT_GUTTMAN_SPLIT \
- (VARIANT_GUTTMAN_LINEAR_SPLIT||VARIANT_GUTTMAN_QUADRATIC_SPLIT)
-
-#if VARIANT_GUTTMAN_QUADRATIC_SPLIT
- #define PickNext QuadraticPickNext
- #define PickSeeds QuadraticPickSeeds
- #define AssignCells splitNodeGuttman
-#endif
-#if VARIANT_GUTTMAN_LINEAR_SPLIT
- #define PickNext LinearPickNext
- #define PickSeeds LinearPickSeeds
- #define AssignCells splitNodeGuttman
-#endif
-#if VARIANT_RSTARTREE_SPLIT
- #define AssignCells splitNodeStartree
-#endif
-
-#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
-# define NDEBUG 1
-#endif
-
#ifndef SQLITE_CORE
+/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#else
+/* #include "sqlite3.h" */
#endif
/* #include <string.h> */
/* #include <assert.h> */
+/* #include <stdio.h> */
#ifndef SQLITE_AMALGAMATION
#include "sqlite3rtree.h"
typedef sqlite3_int64 i64;
typedef unsigned char u8;
+typedef unsigned short u16;
typedef unsigned int u32;
#endif
@@ -127156,6 +161403,7 @@ typedef struct RtreeConstraint RtreeConstraint;
typedef struct RtreeMatchArg RtreeMatchArg;
typedef struct RtreeGeomCallback RtreeGeomCallback;
typedef union RtreeCoord RtreeCoord;
+typedef struct RtreeSearchPoint RtreeSearchPoint;
/* The rtree may have between 1 and RTREE_MAX_DIMENSIONS dimensions. */
#define RTREE_MAX_DIMENSIONS 5
@@ -127164,22 +161412,33 @@ typedef union RtreeCoord RtreeCoord;
** ever contain very many entries, so a fixed number of buckets is
** used.
*/
-#define HASHSIZE 128
+#define HASHSIZE 97
+
+/* The xBestIndex method of this virtual table requires an estimate of
+** the number of rows in the virtual table to calculate the costs of
+** various strategies. If possible, this estimate is loaded from the
+** sqlite_stat1 table (with RTREE_MIN_ROWEST as a hard-coded minimum).
+** Otherwise, if no sqlite_stat1 entry is available, use
+** RTREE_DEFAULT_ROWEST.
+*/
+#define RTREE_DEFAULT_ROWEST 1048576
+#define RTREE_MIN_ROWEST 100
/*
** An rtree virtual-table object.
*/
struct Rtree {
- sqlite3_vtab base;
+ sqlite3_vtab base; /* Base class. Must be first */
sqlite3 *db; /* Host database connection */
int iNodeSize; /* Size in bytes of each node in the node table */
- int nDim; /* Number of dimensions */
- int nBytesPerCell; /* Bytes consumed per cell */
+ u8 nDim; /* Number of dimensions */
+ u8 eCoordType; /* RTREE_COORD_REAL32 or RTREE_COORD_INT32 */
+ u8 nBytesPerCell; /* Bytes consumed per cell */
int iDepth; /* Current depth of the r-tree structure */
char *zDb; /* Name of database containing r-tree table */
char *zName; /* Name of r-tree table */
- RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
int nBusy; /* Current number of users of this structure */
+ i64 nRowEst; /* Estimated number of rows in this table */
/* List of nodes removed during a CondenseTree operation. List is
** linked together via the pointer normally used for hash chains -
@@ -127204,14 +161463,46 @@ struct Rtree {
sqlite3_stmt *pWriteParent;
sqlite3_stmt *pDeleteParent;
- int eCoordType;
+ RtreeNode *aHash[HASHSIZE]; /* Hash table of in-memory nodes. */
};
-/* Possible values for eCoordType: */
+/* Possible values for Rtree.eCoordType: */
#define RTREE_COORD_REAL32 0
#define RTREE_COORD_INT32 1
/*
+** If SQLITE_RTREE_INT_ONLY is defined, then this virtual table will
+** only deal with integer coordinates. No floating point operations
+** will be done.
+*/
+#ifdef SQLITE_RTREE_INT_ONLY
+ typedef sqlite3_int64 RtreeDValue; /* High accuracy coordinate */
+ typedef int RtreeValue; /* Low accuracy coordinate */
+# define RTREE_ZERO 0
+#else
+ typedef double RtreeDValue; /* High accuracy coordinate */
+ typedef float RtreeValue; /* Low accuracy coordinate */
+# define RTREE_ZERO 0.0
+#endif
+
+/*
+** When doing a search of an r-tree, instances of the following structure
+** record intermediate results from the tree walk.
+**
+** The id is always a node-id. For iLevel>=1 the id is the node-id of
+** the node that the RtreeSearchPoint represents. When iLevel==0, however,
+** the id is of the parent node and the cell that RtreeSearchPoint
+** represents is the iCell-th entry in the parent node.
+*/
+struct RtreeSearchPoint {
+ RtreeDValue rScore; /* The score for this node. Smallest goes first. */
+ sqlite3_int64 id; /* Node ID */
+ u8 iLevel; /* 0=entries. 1=leaf node. 2+ for higher */
+ u8 eWithin; /* PARTLY_WITHIN or FULLY_WITHIN */
+ u8 iCell; /* Cell index within the node */
+};
+
+/*
** The minimum number of cells allowed for a node is a third of the
** maximum. In Gutman's notation:
**
@@ -127233,33 +161524,61 @@ struct Rtree {
*/
#define RTREE_MAX_DEPTH 40
+
+/*
+** Number of entries in the cursor RtreeNode cache. The first entry is
+** used to cache the RtreeNode for RtreeCursor.sPoint. The remaining
+** entries cache the RtreeNode for the first elements of the priority queue.
+*/
+#define RTREE_CACHE_SZ 5
+
/*
** An rtree cursor object.
*/
struct RtreeCursor {
- sqlite3_vtab_cursor base;
- RtreeNode *pNode; /* Node cursor is currently pointing at */
- int iCell; /* Index of current cell in pNode */
+ sqlite3_vtab_cursor base; /* Base class. Must be first */
+ u8 atEOF; /* True if at end of search */
+ u8 bPoint; /* True if sPoint is valid */
int iStrategy; /* Copy of idxNum search parameter */
int nConstraint; /* Number of entries in aConstraint */
RtreeConstraint *aConstraint; /* Search constraints. */
+ int nPointAlloc; /* Number of slots allocated for aPoint[] */
+ int nPoint; /* Number of slots used in aPoint[] */
+ int mxLevel; /* iLevel value for root of the tree */
+ RtreeSearchPoint *aPoint; /* Priority queue for search points */
+ RtreeSearchPoint sPoint; /* Cached next search point */
+ RtreeNode *aNode[RTREE_CACHE_SZ]; /* Rtree node cache */
+ u32 anQueue[RTREE_MAX_DEPTH+1]; /* Number of queued entries by iLevel */
};
+/* Return the Rtree of a RtreeCursor */
+#define RTREE_OF_CURSOR(X) ((Rtree*)((X)->base.pVtab))
+
+/*
+** A coordinate can be either a floating point number or a integer. All
+** coordinates within a single R-Tree are always of the same time.
+*/
union RtreeCoord {
- float f;
- int i;
+ RtreeValue f; /* Floating point value */
+ int i; /* Integer value */
+ u32 u; /* Unsigned for byte-order conversions */
};
/*
** The argument is an RtreeCoord. Return the value stored within the RtreeCoord
-** formatted as a double. This macro assumes that local variable pRtree points
-** to the Rtree structure associated with the RtreeCoord.
+** formatted as a RtreeDValue (double or int64). This macro assumes that local
+** variable pRtree points to the Rtree structure associated with the
+** RtreeCoord.
*/
-#define DCOORD(coord) ( \
- (pRtree->eCoordType==RTREE_COORD_REAL32) ? \
- ((double)coord.f) : \
- ((double)coord.i) \
-)
+#ifdef SQLITE_RTREE_INT_ONLY
+# define DCOORD(coord) ((RtreeDValue)coord.i)
+#else
+# define DCOORD(coord) ( \
+ (pRtree->eCoordType==RTREE_COORD_REAL32) ? \
+ ((double)coord.f) : \
+ ((double)coord.i) \
+ )
+#endif
/*
** A search constraint.
@@ -127267,38 +161586,67 @@ union RtreeCoord {
struct RtreeConstraint {
int iCoord; /* Index of constrained coordinate */
int op; /* Constraining operation */
- double rValue; /* Constraint value. */
- int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *);
- sqlite3_rtree_geometry *pGeom; /* Constraint callback argument for a MATCH */
+ union {
+ RtreeDValue rValue; /* Constraint value. */
+ int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*);
+ int (*xQueryFunc)(sqlite3_rtree_query_info*);
+ } u;
+ sqlite3_rtree_query_info *pInfo; /* xGeom and xQueryFunc argument */
};
/* Possible values for RtreeConstraint.op */
-#define RTREE_EQ 0x41
-#define RTREE_LE 0x42
-#define RTREE_LT 0x43
-#define RTREE_GE 0x44
-#define RTREE_GT 0x45
-#define RTREE_MATCH 0x46
+#define RTREE_EQ 0x41 /* A */
+#define RTREE_LE 0x42 /* B */
+#define RTREE_LT 0x43 /* C */
+#define RTREE_GE 0x44 /* D */
+#define RTREE_GT 0x45 /* E */
+#define RTREE_MATCH 0x46 /* F: Old-style sqlite3_rtree_geometry_callback() */
+#define RTREE_QUERY 0x47 /* G: New-style sqlite3_rtree_query_callback() */
+
/*
** An rtree structure node.
*/
struct RtreeNode {
- RtreeNode *pParent; /* Parent node */
- i64 iNode;
- int nRef;
- int isDirty;
- u8 *zData;
- RtreeNode *pNext; /* Next node in this hash chain */
+ RtreeNode *pParent; /* Parent node */
+ i64 iNode; /* The node number */
+ int nRef; /* Number of references to this node */
+ int isDirty; /* True if the node needs to be written to disk */
+ u8 *zData; /* Content of the node, as should be on disk */
+ RtreeNode *pNext; /* Next node in this hash collision chain */
};
+
+/* Return the number of cells in a node */
#define NCELL(pNode) readInt16(&(pNode)->zData[2])
/*
-** Structure to store a deserialized rtree record.
+** A single cell from a node, deserialized
*/
struct RtreeCell {
- i64 iRowid;
- RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2];
+ i64 iRowid; /* Node or entry ID */
+ RtreeCoord aCoord[RTREE_MAX_DIMENSIONS*2]; /* Bounding box coordinates */
+};
+
+
+/*
+** This object becomes the sqlite3_user_data() for the SQL functions
+** that are created by sqlite3_rtree_geometry_callback() and
+** sqlite3_rtree_query_callback() and which appear on the right of MATCH
+** operators in order to constrain a search.
+**
+** xGeom and xQueryFunc are the callback functions. Exactly one of
+** xGeom and xQueryFunc fields is non-NULL, depending on whether the
+** SQL function was created using sqlite3_rtree_geometry_callback() or
+** sqlite3_rtree_query_callback().
+**
+** This object is deleted automatically by the destructor mechanism in
+** sqlite3_create_function_v2().
+*/
+struct RtreeGeomCallback {
+ int (*xGeom)(sqlite3_rtree_geometry*, int, RtreeDValue*, int*);
+ int (*xQueryFunc)(sqlite3_rtree_query_info*);
+ void (*xDestructor)(void*);
+ void *pContext;
};
@@ -127310,29 +161658,17 @@ struct RtreeCell {
#define RTREE_GEOMETRY_MAGIC 0x891245AB
/*
-** An instance of this structure must be supplied as a blob argument to
-** the right-hand-side of an SQL MATCH operator used to constrain an
-** r-tree query.
+** An instance of this structure (in the form of a BLOB) is returned by
+** the SQL functions that sqlite3_rtree_geometry_callback() and
+** sqlite3_rtree_query_callback() create, and is read as the right-hand
+** operand to the MATCH operator of an R-Tree.
*/
struct RtreeMatchArg {
- u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
- int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *);
- void *pContext;
- int nParam;
- double aParam[1];
-};
-
-/*
-** When a geometry callback is created (see sqlite3_rtree_geometry_callback),
-** a single instance of the following structure is allocated. It is used
-** as the context for the user-function created by by s_r_g_c(). The object
-** is eventually deleted by the destructor mechanism provided by
-** sqlite3_create_function_v2() (which is called by s_r_g_c() to create
-** the geometry callback function).
-*/
-struct RtreeGeomCallback {
- int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *);
- void *pContext;
+ u32 magic; /* Always RTREE_GEOMETRY_MAGIC */
+ RtreeGeomCallback cb; /* Info about the callback functions */
+ int nParam; /* Number of parameters to the SQL function */
+ sqlite3_value **apSqlParam; /* Original SQL parameter values */
+ RtreeDValue aParam[1]; /* Values for parameters to the SQL function */
};
#ifndef MAX
@@ -127350,13 +161686,12 @@ static int readInt16(u8 *p){
return (p[0]<<8) + p[1];
}
static void readCoord(u8 *p, RtreeCoord *pCoord){
- u32 i = (
+ pCoord->u = (
(((u32)p[0]) << 24) +
(((u32)p[1]) << 16) +
(((u32)p[2]) << 8) +
(((u32)p[3]) << 0)
);
- *(u32 *)pCoord = i;
}
static i64 readInt64(u8 *p){
return (
@@ -127385,7 +161720,7 @@ static int writeCoord(u8 *p, RtreeCoord *pCoord){
u32 i;
assert( sizeof(RtreeCoord)==4 );
assert( sizeof(u32)==4 );
- i = *(u32 *)pCoord;
+ i = pCoord->u;
p[0] = (i>>24)&0xFF;
p[1] = (i>>16)&0xFF;
p[2] = (i>> 8)&0xFF;
@@ -127426,10 +161761,7 @@ static void nodeZero(Rtree *pRtree, RtreeNode *p){
** in the Rtree.aHash table.
*/
static int nodeHash(i64 iNode){
- return (
- (iNode>>56) ^ (iNode>>48) ^ (iNode>>40) ^ (iNode>>32) ^
- (iNode>>24) ^ (iNode>>16) ^ (iNode>> 8) ^ (iNode>> 0)
- ) % HASHSIZE;
+ return iNode % HASHSIZE;
}
/*
@@ -127489,8 +161821,7 @@ static RtreeNode *nodeNew(Rtree *pRtree, RtreeNode *pParent){
/*
** Obtain a reference to an r-tree node.
*/
-static int
-nodeAcquire(
+static int nodeAcquire(
Rtree *pRtree, /* R-tree structure */
i64 iNode, /* Node number to load */
RtreeNode *pParent, /* Either the parent node or NULL */
@@ -127579,10 +161910,10 @@ nodeAcquire(
** Overwrite cell iCell of node pNode with the contents of pCell.
*/
static void nodeOverwriteCell(
- Rtree *pRtree,
- RtreeNode *pNode,
- RtreeCell *pCell,
- int iCell
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node into which the cell is to be written */
+ RtreeCell *pCell, /* The cell to write */
+ int iCell /* Index into pNode into which pCell is written */
){
int ii;
u8 *p = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
@@ -127594,7 +161925,7 @@ static void nodeOverwriteCell(
}
/*
-** Remove cell the cell with index iCell from node pNode.
+** Remove the cell with index iCell from node pNode.
*/
static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){
u8 *pDst = &pNode->zData[4 + pRtree->nBytesPerCell*iCell];
@@ -127611,11 +161942,10 @@ static void nodeDeleteCell(Rtree *pRtree, RtreeNode *pNode, int iCell){
**
** If there is not enough free space in pNode, return SQLITE_FULL.
*/
-static int
-nodeInsertCell(
- Rtree *pRtree,
- RtreeNode *pNode,
- RtreeCell *pCell
+static int nodeInsertCell(
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* Write new cell into this node */
+ RtreeCell *pCell /* The cell to be inserted */
){
int nCell; /* Current number of cells in pNode */
int nMaxCell; /* Maximum number of cells for pNode */
@@ -127636,8 +161966,7 @@ nodeInsertCell(
/*
** If the node is dirty, write it out to the database.
*/
-static int
-nodeWrite(Rtree *pRtree, RtreeNode *pNode){
+static int nodeWrite(Rtree *pRtree, RtreeNode *pNode){
int rc = SQLITE_OK;
if( pNode->isDirty ){
sqlite3_stmt *p = pRtree->pWriteNode;
@@ -127662,8 +161991,7 @@ nodeWrite(Rtree *pRtree, RtreeNode *pNode){
** Release a reference to a node. If the node is dirty and the reference
** count drops to zero, the node data is written to the database.
*/
-static int
-nodeRelease(Rtree *pRtree, RtreeNode *pNode){
+static int nodeRelease(Rtree *pRtree, RtreeNode *pNode){
int rc = SQLITE_OK;
if( pNode ){
assert( pNode->nRef>0 );
@@ -127691,9 +162019,9 @@ nodeRelease(Rtree *pRtree, RtreeNode *pNode){
** an internal node, then the 64-bit integer is a child page number.
*/
static i64 nodeGetRowid(
- Rtree *pRtree,
- RtreeNode *pNode,
- int iCell
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node from which to extract the ID */
+ int iCell /* The cell index from which to extract the ID */
){
assert( iCell<NCELL(pNode) );
return readInt64(&pNode->zData[4 + pRtree->nBytesPerCell*iCell]);
@@ -127703,11 +162031,11 @@ static i64 nodeGetRowid(
** Return coordinate iCoord from cell iCell in node pNode.
*/
static void nodeGetCoord(
- Rtree *pRtree,
- RtreeNode *pNode,
- int iCell,
- int iCoord,
- RtreeCoord *pCoord /* Space to write result to */
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node from which to extract a coordinate */
+ int iCell, /* The index of the cell within the node */
+ int iCoord, /* Which coordinate to extract */
+ RtreeCoord *pCoord /* OUT: Space to write result to */
){
readCoord(&pNode->zData[12 + pRtree->nBytesPerCell*iCell + 4*iCoord], pCoord);
}
@@ -127717,15 +162045,19 @@ static void nodeGetCoord(
** to by pCell with the results.
*/
static void nodeGetCell(
- Rtree *pRtree,
- RtreeNode *pNode,
- int iCell,
- RtreeCell *pCell
+ Rtree *pRtree, /* The overall R-Tree */
+ RtreeNode *pNode, /* The node containing the cell to be read */
+ int iCell, /* Index of the cell within the node */
+ RtreeCell *pCell /* OUT: Write the cell contents here */
){
+ u8 *pData;
+ RtreeCoord *pCoord;
int ii;
pCell->iRowid = nodeGetRowid(pRtree, pNode, iCell);
+ pData = pNode->zData + (12 + pRtree->nBytesPerCell*iCell);
+ pCoord = pCell->aCoord;
for(ii=0; ii<pRtree->nDim*2; ii++){
- nodeGetCoord(pRtree, pNode, iCell, ii, &pCell->aCoord[ii]);
+ readCoord(&pData[ii*4], &pCoord[ii]);
}
}
@@ -127851,10 +162183,10 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
if( pCsr->aConstraint ){
int i; /* Used to iterate through constraint array */
for(i=0; i<pCsr->nConstraint; i++){
- sqlite3_rtree_geometry *pGeom = pCsr->aConstraint[i].pGeom;
- if( pGeom ){
- if( pGeom->xDelUser ) pGeom->xDelUser(pGeom->pUser);
- sqlite3_free(pGeom);
+ sqlite3_rtree_query_info *pInfo = pCsr->aConstraint[i].pInfo;
+ if( pInfo ){
+ if( pInfo->xDelUser ) pInfo->xDelUser(pInfo->pUser);
+ sqlite3_free(pInfo);
}
}
sqlite3_free(pCsr->aConstraint);
@@ -127867,12 +162199,13 @@ static void freeCursorConstraints(RtreeCursor *pCsr){
*/
static int rtreeClose(sqlite3_vtab_cursor *cur){
Rtree *pRtree = (Rtree *)(cur->pVtab);
- int rc;
+ int ii;
RtreeCursor *pCsr = (RtreeCursor *)cur;
freeCursorConstraints(pCsr);
- rc = nodeRelease(pRtree, pCsr->pNode);
+ sqlite3_free(pCsr->aPoint);
+ for(ii=0; ii<RTREE_CACHE_SZ; ii++) nodeRelease(pRtree, pCsr->aNode[ii]);
sqlite3_free(pCsr);
- return rc;
+ return SQLITE_OK;
}
/*
@@ -127883,194 +162216,164 @@ static int rtreeClose(sqlite3_vtab_cursor *cur){
*/
static int rtreeEof(sqlite3_vtab_cursor *cur){
RtreeCursor *pCsr = (RtreeCursor *)cur;
- return (pCsr->pNode==0);
+ return pCsr->atEOF;
+}
+
+/*
+** Convert raw bits from the on-disk RTree record into a coordinate value.
+** The on-disk format is big-endian and needs to be converted for little-
+** endian platforms. The on-disk record stores integer coordinates if
+** eInt is true and it stores 32-bit floating point records if eInt is
+** false. a[] is the four bytes of the on-disk record to be decoded.
+** Store the results in "r".
+**
+** There are three versions of this macro, one each for little-endian and
+** big-endian processors and a third generic implementation. The endian-
+** specific implementations are much faster and are preferred if the
+** processor endianness is known at compile-time. The SQLITE_BYTEORDER
+** macro is part of sqliteInt.h and hence the endian-specific
+** implementation will only be used if this module is compiled as part
+** of the amalgamation.
+*/
+#if defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==1234
+#define RTREE_DECODE_COORD(eInt, a, r) { \
+ RtreeCoord c; /* Coordinate decoded */ \
+ memcpy(&c.u,a,4); \
+ c.u = ((c.u>>24)&0xff)|((c.u>>8)&0xff00)| \
+ ((c.u&0xff)<<24)|((c.u&0xff00)<<8); \
+ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
+}
+#elif defined(SQLITE_BYTEORDER) && SQLITE_BYTEORDER==4321
+#define RTREE_DECODE_COORD(eInt, a, r) { \
+ RtreeCoord c; /* Coordinate decoded */ \
+ memcpy(&c.u,a,4); \
+ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
+}
+#else
+#define RTREE_DECODE_COORD(eInt, a, r) { \
+ RtreeCoord c; /* Coordinate decoded */ \
+ c.u = ((u32)a[0]<<24) + ((u32)a[1]<<16) \
+ +((u32)a[2]<<8) + a[3]; \
+ r = eInt ? (sqlite3_rtree_dbl)c.i : (sqlite3_rtree_dbl)c.f; \
}
+#endif
/*
-** The r-tree constraint passed as the second argument to this function is
-** guaranteed to be a MATCH constraint.
+** Check the RTree node or entry given by pCellData and p against the MATCH
+** constraint pConstraint.
*/
-static int testRtreeGeom(
- Rtree *pRtree, /* R-Tree object */
- RtreeConstraint *pConstraint, /* MATCH constraint to test */
- RtreeCell *pCell, /* Cell to test */
- int *pbRes /* OUT: Test result */
+static int rtreeCallbackConstraint(
+ RtreeConstraint *pConstraint, /* The constraint to test */
+ int eInt, /* True if RTree holding integer coordinates */
+ u8 *pCellData, /* Raw cell content */
+ RtreeSearchPoint *pSearch, /* Container of this cell */
+ sqlite3_rtree_dbl *prScore, /* OUT: score for the cell */
+ int *peWithin /* OUT: visibility of the cell */
){
- int i;
- double aCoord[RTREE_MAX_DIMENSIONS*2];
- int nCoord = pRtree->nDim*2;
+ int i; /* Loop counter */
+ sqlite3_rtree_query_info *pInfo = pConstraint->pInfo; /* Callback info */
+ int nCoord = pInfo->nCoord; /* No. of coordinates */
+ int rc; /* Callback return code */
+ sqlite3_rtree_dbl aCoord[RTREE_MAX_DIMENSIONS*2]; /* Decoded coordinates */
- assert( pConstraint->op==RTREE_MATCH );
- assert( pConstraint->pGeom );
+ assert( pConstraint->op==RTREE_MATCH || pConstraint->op==RTREE_QUERY );
+ assert( nCoord==2 || nCoord==4 || nCoord==6 || nCoord==8 || nCoord==10 );
- for(i=0; i<nCoord; i++){
- aCoord[i] = DCOORD(pCell->aCoord[i]);
+ if( pConstraint->op==RTREE_QUERY && pSearch->iLevel==1 ){
+ pInfo->iRowid = readInt64(pCellData);
}
- return pConstraint->xGeom(pConstraint->pGeom, nCoord, aCoord, pbRes);
-}
-
-/*
-** Cursor pCursor currently points to a cell in a non-leaf page.
-** Set *pbEof to true if the sub-tree headed by the cell is filtered
-** (excluded) by the constraints in the pCursor->aConstraint[]
-** array, or false otherwise.
-**
-** Return SQLITE_OK if successful or an SQLite error code if an error
-** occurs within a geometry callback.
-*/
-static int testRtreeCell(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
- RtreeCell cell;
- int ii;
- int bRes = 0;
- int rc = SQLITE_OK;
-
- nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
- for(ii=0; bRes==0 && ii<pCursor->nConstraint; ii++){
- RtreeConstraint *p = &pCursor->aConstraint[ii];
- double cell_min = DCOORD(cell.aCoord[(p->iCoord>>1)*2]);
- double cell_max = DCOORD(cell.aCoord[(p->iCoord>>1)*2+1]);
-
- assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
- || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH
- );
-
- switch( p->op ){
- case RTREE_LE: case RTREE_LT:
- bRes = p->rValue<cell_min;
- break;
-
- case RTREE_GE: case RTREE_GT:
- bRes = p->rValue>cell_max;
- break;
-
- case RTREE_EQ:
- bRes = (p->rValue>cell_max || p->rValue<cell_min);
- break;
-
- default: {
- assert( p->op==RTREE_MATCH );
- rc = testRtreeGeom(pRtree, p, &cell, &bRes);
- bRes = !bRes;
- break;
- }
+ pCellData += 8;
+ for(i=0; i<nCoord; i++, pCellData += 4){
+ RTREE_DECODE_COORD(eInt, pCellData, aCoord[i]);
+ }
+ if( pConstraint->op==RTREE_MATCH ){
+ rc = pConstraint->u.xGeom((sqlite3_rtree_geometry*)pInfo,
+ nCoord, aCoord, &i);
+ if( i==0 ) *peWithin = NOT_WITHIN;
+ *prScore = RTREE_ZERO;
+ }else{
+ pInfo->aCoord = aCoord;
+ pInfo->iLevel = pSearch->iLevel - 1;
+ pInfo->rScore = pInfo->rParentScore = pSearch->rScore;
+ pInfo->eWithin = pInfo->eParentWithin = pSearch->eWithin;
+ rc = pConstraint->u.xQueryFunc(pInfo);
+ if( pInfo->eWithin<*peWithin ) *peWithin = pInfo->eWithin;
+ if( pInfo->rScore<*prScore || *prScore<RTREE_ZERO ){
+ *prScore = pInfo->rScore;
}
}
-
- *pbEof = bRes;
return rc;
}
/*
-** Test if the cell that cursor pCursor currently points to
-** would be filtered (excluded) by the constraints in the
-** pCursor->aConstraint[] array. If so, set *pbEof to true before
-** returning. If the cell is not filtered (excluded) by the constraints,
-** set pbEof to zero.
-**
-** Return SQLITE_OK if successful or an SQLite error code if an error
-** occurs within a geometry callback.
-**
-** This function assumes that the cell is part of a leaf node.
+** Check the internal RTree node given by pCellData against constraint p.
+** If this constraint cannot be satisfied by any child within the node,
+** set *peWithin to NOT_WITHIN.
*/
-static int testRtreeEntry(Rtree *pRtree, RtreeCursor *pCursor, int *pbEof){
- RtreeCell cell;
- int ii;
- *pbEof = 0;
+static void rtreeNonleafConstraint(
+ RtreeConstraint *p, /* The constraint to test */
+ int eInt, /* True if RTree holds integer coordinates */
+ u8 *pCellData, /* Raw cell content as appears on disk */
+ int *peWithin /* Adjust downward, as appropriate */
+){
+ sqlite3_rtree_dbl val; /* Coordinate value convert to a double */
- nodeGetCell(pRtree, pCursor->pNode, pCursor->iCell, &cell);
- for(ii=0; ii<pCursor->nConstraint; ii++){
- RtreeConstraint *p = &pCursor->aConstraint[ii];
- double coord = DCOORD(cell.aCoord[p->iCoord]);
- int res;
- assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
- || p->op==RTREE_GT || p->op==RTREE_EQ || p->op==RTREE_MATCH
- );
- switch( p->op ){
- case RTREE_LE: res = (coord<=p->rValue); break;
- case RTREE_LT: res = (coord<p->rValue); break;
- case RTREE_GE: res = (coord>=p->rValue); break;
- case RTREE_GT: res = (coord>p->rValue); break;
- case RTREE_EQ: res = (coord==p->rValue); break;
- default: {
- int rc;
- assert( p->op==RTREE_MATCH );
- rc = testRtreeGeom(pRtree, p, &cell, &res);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- break;
- }
- }
+ /* p->iCoord might point to either a lower or upper bound coordinate
+ ** in a coordinate pair. But make pCellData point to the lower bound.
+ */
+ pCellData += 8 + 4*(p->iCoord&0xfe);
- if( !res ){
- *pbEof = 1;
- return SQLITE_OK;
- }
- }
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ );
+ switch( p->op ){
+ case RTREE_LE:
+ case RTREE_LT:
+ case RTREE_EQ:
+ RTREE_DECODE_COORD(eInt, pCellData, val);
+ /* val now holds the lower bound of the coordinate pair */
+ if( p->u.rValue>=val ) return;
+ if( p->op!=RTREE_EQ ) break; /* RTREE_LE and RTREE_LT end here */
+ /* Fall through for the RTREE_EQ case */
- return SQLITE_OK;
+ default: /* RTREE_GT or RTREE_GE, or fallthrough of RTREE_EQ */
+ pCellData += 4;
+ RTREE_DECODE_COORD(eInt, pCellData, val);
+ /* val now holds the upper bound of the coordinate pair */
+ if( p->u.rValue<=val ) return;
+ }
+ *peWithin = NOT_WITHIN;
}
/*
-** Cursor pCursor currently points at a node that heads a sub-tree of
-** height iHeight (if iHeight==0, then the node is a leaf). Descend
-** to point to the left-most cell of the sub-tree that matches the
-** configured constraints.
+** Check the leaf RTree cell given by pCellData against constraint p.
+** If this constraint is not satisfied, set *peWithin to NOT_WITHIN.
+** If the constraint is satisfied, leave *peWithin unchanged.
+**
+** The constraint is of the form: xN op $val
+**
+** The op is given by p->op. The xN is p->iCoord-th coordinate in
+** pCellData. $val is given by p->u.rValue.
*/
-static int descendToCell(
- Rtree *pRtree,
- RtreeCursor *pCursor,
- int iHeight,
- int *pEof /* OUT: Set to true if cannot descend */
+static void rtreeLeafConstraint(
+ RtreeConstraint *p, /* The constraint to test */
+ int eInt, /* True if RTree holds integer coordinates */
+ u8 *pCellData, /* Raw cell content as appears on disk */
+ int *peWithin /* Adjust downward, as appropriate */
){
- int isEof;
- int rc;
- int ii;
- RtreeNode *pChild;
- sqlite3_int64 iRowid;
-
- RtreeNode *pSavedNode = pCursor->pNode;
- int iSavedCell = pCursor->iCell;
-
- assert( iHeight>=0 );
-
- if( iHeight==0 ){
- rc = testRtreeEntry(pRtree, pCursor, &isEof);
- }else{
- rc = testRtreeCell(pRtree, pCursor, &isEof);
- }
- if( rc!=SQLITE_OK || isEof || iHeight==0 ){
- goto descend_to_cell_out;
- }
-
- iRowid = nodeGetRowid(pRtree, pCursor->pNode, pCursor->iCell);
- rc = nodeAcquire(pRtree, iRowid, pCursor->pNode, &pChild);
- if( rc!=SQLITE_OK ){
- goto descend_to_cell_out;
- }
+ RtreeDValue xN; /* Coordinate value converted to a double */
- nodeRelease(pRtree, pCursor->pNode);
- pCursor->pNode = pChild;
- isEof = 1;
- for(ii=0; isEof && ii<NCELL(pChild); ii++){
- pCursor->iCell = ii;
- rc = descendToCell(pRtree, pCursor, iHeight-1, &isEof);
- if( rc!=SQLITE_OK ){
- goto descend_to_cell_out;
- }
- }
-
- if( isEof ){
- assert( pCursor->pNode==pChild );
- nodeReference(pSavedNode);
- nodeRelease(pRtree, pChild);
- pCursor->pNode = pSavedNode;
- pCursor->iCell = iSavedCell;
+ assert(p->op==RTREE_LE || p->op==RTREE_LT || p->op==RTREE_GE
+ || p->op==RTREE_GT || p->op==RTREE_EQ );
+ pCellData += 8 + p->iCoord*4;
+ RTREE_DECODE_COORD(eInt, pCellData, xN);
+ switch( p->op ){
+ case RTREE_LE: if( xN <= p->u.rValue ) return; break;
+ case RTREE_LT: if( xN < p->u.rValue ) return; break;
+ case RTREE_GE: if( xN >= p->u.rValue ) return; break;
+ case RTREE_GT: if( xN > p->u.rValue ) return; break;
+ default: if( xN == p->u.rValue ) return; break;
}
-
-descend_to_cell_out:
- *pEof = isEof;
- return rc;
+ *peWithin = NOT_WITHIN;
}
/*
@@ -128085,6 +162388,7 @@ static int nodeRowidIndex(
){
int ii;
int nCell = NCELL(pNode);
+ assert( nCell<200 );
for(ii=0; ii<nCell; ii++){
if( nodeGetRowid(pRtree, pNode, ii)==iRowid ){
*piIndex = ii;
@@ -128107,48 +162411,302 @@ static int nodeParentIndex(Rtree *pRtree, RtreeNode *pNode, int *piIndex){
return SQLITE_OK;
}
-/*
-** Rtree virtual table module xNext method.
+/*
+** Compare two search points. Return negative, zero, or positive if the first
+** is less than, equal to, or greater than the second.
+**
+** The rScore is the primary key. Smaller rScore values come first.
+** If the rScore is a tie, then use iLevel as the tie breaker with smaller
+** iLevel values coming first. In this way, if rScore is the same for all
+** SearchPoints, then iLevel becomes the deciding factor and the result
+** is a depth-first search, which is the desired default behavior.
*/
-static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
- Rtree *pRtree = (Rtree *)(pVtabCursor->pVtab);
- RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
- int rc = SQLITE_OK;
+static int rtreeSearchPointCompare(
+ const RtreeSearchPoint *pA,
+ const RtreeSearchPoint *pB
+){
+ if( pA->rScore<pB->rScore ) return -1;
+ if( pA->rScore>pB->rScore ) return +1;
+ if( pA->iLevel<pB->iLevel ) return -1;
+ if( pA->iLevel>pB->iLevel ) return +1;
+ return 0;
+}
- /* RtreeCursor.pNode must not be NULL. If is is NULL, then this cursor is
- ** already at EOF. It is against the rules to call the xNext() method of
- ** a cursor that has already reached EOF.
- */
- assert( pCsr->pNode );
-
- if( pCsr->iStrategy==1 ){
- /* This "scan" is a direct lookup by rowid. There is no next entry. */
- nodeRelease(pRtree, pCsr->pNode);
- pCsr->pNode = 0;
- }else{
- /* Move to the next entry that matches the configured constraints. */
- int iHeight = 0;
- while( pCsr->pNode ){
- RtreeNode *pNode = pCsr->pNode;
- int nCell = NCELL(pNode);
- for(pCsr->iCell++; pCsr->iCell<nCell; pCsr->iCell++){
- int isEof;
- rc = descendToCell(pRtree, pCsr, iHeight, &isEof);
- if( rc!=SQLITE_OK || !isEof ){
- return rc;
+/*
+** Interchange to search points in a cursor.
+*/
+static void rtreeSearchPointSwap(RtreeCursor *p, int i, int j){
+ RtreeSearchPoint t = p->aPoint[i];
+ assert( i<j );
+ p->aPoint[i] = p->aPoint[j];
+ p->aPoint[j] = t;
+ i++; j++;
+ if( i<RTREE_CACHE_SZ ){
+ if( j>=RTREE_CACHE_SZ ){
+ nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]);
+ p->aNode[i] = 0;
+ }else{
+ RtreeNode *pTemp = p->aNode[i];
+ p->aNode[i] = p->aNode[j];
+ p->aNode[j] = pTemp;
+ }
+ }
+}
+
+/*
+** Return the search point with the lowest current score.
+*/
+static RtreeSearchPoint *rtreeSearchPointFirst(RtreeCursor *pCur){
+ return pCur->bPoint ? &pCur->sPoint : pCur->nPoint ? pCur->aPoint : 0;
+}
+
+/*
+** Get the RtreeNode for the search point with the lowest score.
+*/
+static RtreeNode *rtreeNodeOfFirstSearchPoint(RtreeCursor *pCur, int *pRC){
+ sqlite3_int64 id;
+ int ii = 1 - pCur->bPoint;
+ assert( ii==0 || ii==1 );
+ assert( pCur->bPoint || pCur->nPoint );
+ if( pCur->aNode[ii]==0 ){
+ assert( pRC!=0 );
+ id = ii ? pCur->aPoint[0].id : pCur->sPoint.id;
+ *pRC = nodeAcquire(RTREE_OF_CURSOR(pCur), id, 0, &pCur->aNode[ii]);
+ }
+ return pCur->aNode[ii];
+}
+
+/*
+** Push a new element onto the priority queue
+*/
+static RtreeSearchPoint *rtreeEnqueue(
+ RtreeCursor *pCur, /* The cursor */
+ RtreeDValue rScore, /* Score for the new search point */
+ u8 iLevel /* Level for the new search point */
+){
+ int i, j;
+ RtreeSearchPoint *pNew;
+ if( pCur->nPoint>=pCur->nPointAlloc ){
+ int nNew = pCur->nPointAlloc*2 + 8;
+ pNew = sqlite3_realloc(pCur->aPoint, nNew*sizeof(pCur->aPoint[0]));
+ if( pNew==0 ) return 0;
+ pCur->aPoint = pNew;
+ pCur->nPointAlloc = nNew;
+ }
+ i = pCur->nPoint++;
+ pNew = pCur->aPoint + i;
+ pNew->rScore = rScore;
+ pNew->iLevel = iLevel;
+ assert( iLevel<=RTREE_MAX_DEPTH );
+ while( i>0 ){
+ RtreeSearchPoint *pParent;
+ j = (i-1)/2;
+ pParent = pCur->aPoint + j;
+ if( rtreeSearchPointCompare(pNew, pParent)>=0 ) break;
+ rtreeSearchPointSwap(pCur, j, i);
+ i = j;
+ pNew = pParent;
+ }
+ return pNew;
+}
+
+/*
+** Allocate a new RtreeSearchPoint and return a pointer to it. Return
+** NULL if malloc fails.
+*/
+static RtreeSearchPoint *rtreeSearchPointNew(
+ RtreeCursor *pCur, /* The cursor */
+ RtreeDValue rScore, /* Score for the new search point */
+ u8 iLevel /* Level for the new search point */
+){
+ RtreeSearchPoint *pNew, *pFirst;
+ pFirst = rtreeSearchPointFirst(pCur);
+ pCur->anQueue[iLevel]++;
+ if( pFirst==0
+ || pFirst->rScore>rScore
+ || (pFirst->rScore==rScore && pFirst->iLevel>iLevel)
+ ){
+ if( pCur->bPoint ){
+ int ii;
+ pNew = rtreeEnqueue(pCur, rScore, iLevel);
+ if( pNew==0 ) return 0;
+ ii = (int)(pNew - pCur->aPoint) + 1;
+ if( ii<RTREE_CACHE_SZ ){
+ assert( pCur->aNode[ii]==0 );
+ pCur->aNode[ii] = pCur->aNode[0];
+ }else{
+ nodeRelease(RTREE_OF_CURSOR(pCur), pCur->aNode[0]);
+ }
+ pCur->aNode[0] = 0;
+ *pNew = pCur->sPoint;
+ }
+ pCur->sPoint.rScore = rScore;
+ pCur->sPoint.iLevel = iLevel;
+ pCur->bPoint = 1;
+ return &pCur->sPoint;
+ }else{
+ return rtreeEnqueue(pCur, rScore, iLevel);
+ }
+}
+
+#if 0
+/* Tracing routines for the RtreeSearchPoint queue */
+static void tracePoint(RtreeSearchPoint *p, int idx, RtreeCursor *pCur){
+ if( idx<0 ){ printf(" s"); }else{ printf("%2d", idx); }
+ printf(" %d.%05lld.%02d %g %d",
+ p->iLevel, p->id, p->iCell, p->rScore, p->eWithin
+ );
+ idx++;
+ if( idx<RTREE_CACHE_SZ ){
+ printf(" %p\n", pCur->aNode[idx]);
+ }else{
+ printf("\n");
+ }
+}
+static void traceQueue(RtreeCursor *pCur, const char *zPrefix){
+ int ii;
+ printf("=== %9s ", zPrefix);
+ if( pCur->bPoint ){
+ tracePoint(&pCur->sPoint, -1, pCur);
+ }
+ for(ii=0; ii<pCur->nPoint; ii++){
+ if( ii>0 || pCur->bPoint ) printf(" ");
+ tracePoint(&pCur->aPoint[ii], ii, pCur);
+ }
+}
+# define RTREE_QUEUE_TRACE(A,B) traceQueue(A,B)
+#else
+# define RTREE_QUEUE_TRACE(A,B) /* no-op */
+#endif
+
+/* Remove the search point with the lowest current score.
+*/
+static void rtreeSearchPointPop(RtreeCursor *p){
+ int i, j, k, n;
+ i = 1 - p->bPoint;
+ assert( i==0 || i==1 );
+ if( p->aNode[i] ){
+ nodeRelease(RTREE_OF_CURSOR(p), p->aNode[i]);
+ p->aNode[i] = 0;
+ }
+ if( p->bPoint ){
+ p->anQueue[p->sPoint.iLevel]--;
+ p->bPoint = 0;
+ }else if( p->nPoint ){
+ p->anQueue[p->aPoint[0].iLevel]--;
+ n = --p->nPoint;
+ p->aPoint[0] = p->aPoint[n];
+ if( n<RTREE_CACHE_SZ-1 ){
+ p->aNode[1] = p->aNode[n+1];
+ p->aNode[n+1] = 0;
+ }
+ i = 0;
+ while( (j = i*2+1)<n ){
+ k = j+1;
+ if( k<n && rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[j])<0 ){
+ if( rtreeSearchPointCompare(&p->aPoint[k], &p->aPoint[i])<0 ){
+ rtreeSearchPointSwap(p, i, k);
+ i = k;
+ }else{
+ break;
+ }
+ }else{
+ if( rtreeSearchPointCompare(&p->aPoint[j], &p->aPoint[i])<0 ){
+ rtreeSearchPointSwap(p, i, j);
+ i = j;
+ }else{
+ break;
}
}
- pCsr->pNode = pNode->pParent;
- rc = nodeParentIndex(pRtree, pNode, &pCsr->iCell);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- nodeReference(pCsr->pNode);
- nodeRelease(pRtree, pNode);
- iHeight++;
}
}
+}
+
+/*
+** Continue the search on cursor pCur until the front of the queue
+** contains an entry suitable for returning as a result-set row,
+** or until the RtreeSearchPoint queue is empty, indicating that the
+** query has completed.
+*/
+static int rtreeStepToLeaf(RtreeCursor *pCur){
+ RtreeSearchPoint *p;
+ Rtree *pRtree = RTREE_OF_CURSOR(pCur);
+ RtreeNode *pNode;
+ int eWithin;
+ int rc = SQLITE_OK;
+ int nCell;
+ int nConstraint = pCur->nConstraint;
+ int ii;
+ int eInt;
+ RtreeSearchPoint x;
+
+ eInt = pRtree->eCoordType==RTREE_COORD_INT32;
+ while( (p = rtreeSearchPointFirst(pCur))!=0 && p->iLevel>0 ){
+ pNode = rtreeNodeOfFirstSearchPoint(pCur, &rc);
+ if( rc ) return rc;
+ nCell = NCELL(pNode);
+ assert( nCell<200 );
+ while( p->iCell<nCell ){
+ sqlite3_rtree_dbl rScore = (sqlite3_rtree_dbl)-1;
+ u8 *pCellData = pNode->zData + (4+pRtree->nBytesPerCell*p->iCell);
+ eWithin = FULLY_WITHIN;
+ for(ii=0; ii<nConstraint; ii++){
+ RtreeConstraint *pConstraint = pCur->aConstraint + ii;
+ if( pConstraint->op>=RTREE_MATCH ){
+ rc = rtreeCallbackConstraint(pConstraint, eInt, pCellData, p,
+ &rScore, &eWithin);
+ if( rc ) return rc;
+ }else if( p->iLevel==1 ){
+ rtreeLeafConstraint(pConstraint, eInt, pCellData, &eWithin);
+ }else{
+ rtreeNonleafConstraint(pConstraint, eInt, pCellData, &eWithin);
+ }
+ if( eWithin==NOT_WITHIN ) break;
+ }
+ p->iCell++;
+ if( eWithin==NOT_WITHIN ) continue;
+ x.iLevel = p->iLevel - 1;
+ if( x.iLevel ){
+ x.id = readInt64(pCellData);
+ x.iCell = 0;
+ }else{
+ x.id = p->id;
+ x.iCell = p->iCell - 1;
+ }
+ if( p->iCell>=nCell ){
+ RTREE_QUEUE_TRACE(pCur, "POP-S:");
+ rtreeSearchPointPop(pCur);
+ }
+ if( rScore<RTREE_ZERO ) rScore = RTREE_ZERO;
+ p = rtreeSearchPointNew(pCur, rScore, x.iLevel);
+ if( p==0 ) return SQLITE_NOMEM;
+ p->eWithin = eWithin;
+ p->id = x.id;
+ p->iCell = x.iCell;
+ RTREE_QUEUE_TRACE(pCur, "PUSH-S:");
+ break;
+ }
+ if( p->iCell>=nCell ){
+ RTREE_QUEUE_TRACE(pCur, "POP-Se:");
+ rtreeSearchPointPop(pCur);
+ }
+ }
+ pCur->atEOF = p==0;
+ return SQLITE_OK;
+}
+
+/*
+** Rtree virtual table module xNext method.
+*/
+static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
+ RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
+ int rc = SQLITE_OK;
+
+ /* Move to the next entry that matches the configured constraints. */
+ RTREE_QUEUE_TRACE(pCsr, "POP-Nx:");
+ rtreeSearchPointPop(pCsr);
+ rc = rtreeStepToLeaf(pCsr);
return rc;
}
@@ -128156,13 +162714,14 @@ static int rtreeNext(sqlite3_vtab_cursor *pVtabCursor){
** Rtree virtual table module xRowid method.
*/
static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
- Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
-
- assert(pCsr->pNode);
- *pRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
-
- return SQLITE_OK;
+ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
+ int rc = SQLITE_OK;
+ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
+ if( rc==SQLITE_OK && p ){
+ *pRowid = nodeGetRowid(RTREE_OF_CURSOR(pCsr), pNode, p->iCell);
+ }
+ return rc;
}
/*
@@ -128171,21 +162730,28 @@ static int rtreeRowid(sqlite3_vtab_cursor *pVtabCursor, sqlite_int64 *pRowid){
static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
Rtree *pRtree = (Rtree *)cur->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)cur;
+ RtreeSearchPoint *p = rtreeSearchPointFirst(pCsr);
+ RtreeCoord c;
+ int rc = SQLITE_OK;
+ RtreeNode *pNode = rtreeNodeOfFirstSearchPoint(pCsr, &rc);
+ if( rc ) return rc;
+ if( p==0 ) return SQLITE_OK;
if( i==0 ){
- i64 iRowid = nodeGetRowid(pRtree, pCsr->pNode, pCsr->iCell);
- sqlite3_result_int64(ctx, iRowid);
+ sqlite3_result_int64(ctx, nodeGetRowid(pRtree, pNode, p->iCell));
}else{
- RtreeCoord c;
- nodeGetCoord(pRtree, pCsr->pNode, pCsr->iCell, i-1, &c);
+ if( rc ) return rc;
+ nodeGetCoord(pRtree, pNode, p->iCell, i-1, &c);
+#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
sqlite3_result_double(ctx, c.f);
- }else{
+ }else
+#endif
+ {
assert( pRtree->eCoordType==RTREE_COORD_INT32 );
sqlite3_result_int(ctx, c.i);
}
}
-
return SQLITE_OK;
}
@@ -128196,12 +162762,18 @@ static int rtreeColumn(sqlite3_vtab_cursor *cur, sqlite3_context *ctx, int i){
** *ppLeaf to 0 and return SQLITE_OK. If an error occurs, set *ppLeaf
** to zero and return an SQLite error code.
*/
-static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){
+static int findLeafNode(
+ Rtree *pRtree, /* RTree to search */
+ i64 iRowid, /* The rowid searching for */
+ RtreeNode **ppLeaf, /* Write the node here */
+ sqlite3_int64 *piNode /* Write the node-id here */
+){
int rc;
*ppLeaf = 0;
sqlite3_bind_int64(pRtree->pReadRowid, 1, iRowid);
if( sqlite3_step(pRtree->pReadRowid)==SQLITE_ROW ){
i64 iNode = sqlite3_column_int64(pRtree->pReadRowid, 0);
+ if( piNode ) *piNode = iNode;
rc = nodeAcquire(pRtree, iNode, 0, ppLeaf);
sqlite3_reset(pRtree->pReadRowid);
}else{
@@ -128217,42 +162789,45 @@ static int findLeafNode(Rtree *pRtree, i64 iRowid, RtreeNode **ppLeaf){
** operator.
*/
static int deserializeGeometry(sqlite3_value *pValue, RtreeConstraint *pCons){
- RtreeMatchArg *p;
- sqlite3_rtree_geometry *pGeom;
- int nBlob;
+ RtreeMatchArg *pBlob; /* BLOB returned by geometry function */
+ sqlite3_rtree_query_info *pInfo; /* Callback information */
+ int nBlob; /* Size of the geometry function blob */
+ int nExpected; /* Expected size of the BLOB */
/* Check that value is actually a blob. */
- if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_ERROR;
+ if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR;
/* Check that the blob is roughly the right size. */
nBlob = sqlite3_value_bytes(pValue);
- if( nBlob<(int)sizeof(RtreeMatchArg)
- || ((nBlob-sizeof(RtreeMatchArg))%sizeof(double))!=0
- ){
+ if( nBlob<(int)sizeof(RtreeMatchArg) ){
return SQLITE_ERROR;
}
- pGeom = (sqlite3_rtree_geometry *)sqlite3_malloc(
- sizeof(sqlite3_rtree_geometry) + nBlob
- );
- if( !pGeom ) return SQLITE_NOMEM;
- memset(pGeom, 0, sizeof(sqlite3_rtree_geometry));
- p = (RtreeMatchArg *)&pGeom[1];
+ pInfo = (sqlite3_rtree_query_info*)sqlite3_malloc( sizeof(*pInfo)+nBlob );
+ if( !pInfo ) return SQLITE_NOMEM;
+ memset(pInfo, 0, sizeof(*pInfo));
+ pBlob = (RtreeMatchArg*)&pInfo[1];
- memcpy(p, sqlite3_value_blob(pValue), nBlob);
- if( p->magic!=RTREE_GEOMETRY_MAGIC
- || nBlob!=(int)(sizeof(RtreeMatchArg) + (p->nParam-1)*sizeof(double))
- ){
- sqlite3_free(pGeom);
+ memcpy(pBlob, sqlite3_value_blob(pValue), nBlob);
+ nExpected = (int)(sizeof(RtreeMatchArg) +
+ pBlob->nParam*sizeof(sqlite3_value*) +
+ (pBlob->nParam-1)*sizeof(RtreeDValue));
+ if( pBlob->magic!=RTREE_GEOMETRY_MAGIC || nBlob!=nExpected ){
+ sqlite3_free(pInfo);
return SQLITE_ERROR;
}
+ pInfo->pContext = pBlob->cb.pContext;
+ pInfo->nParam = pBlob->nParam;
+ pInfo->aParam = pBlob->aParam;
+ pInfo->apSqlParam = pBlob->apSqlParam;
- pGeom->pContext = p->pContext;
- pGeom->nParam = p->nParam;
- pGeom->aParam = p->aParam;
-
- pCons->xGeom = p->xGeom;
- pCons->pGeom = pGeom;
+ if( pBlob->cb.xGeom ){
+ pCons->u.xGeom = pBlob->cb.xGeom;
+ }else{
+ pCons->op = RTREE_QUERY;
+ pCons->u.xQueryFunc = pBlob->cb.xQueryFunc;
+ }
+ pCons->pInfo = pInfo;
return SQLITE_OK;
}
@@ -128266,43 +162841,59 @@ static int rtreeFilter(
){
Rtree *pRtree = (Rtree *)pVtabCursor->pVtab;
RtreeCursor *pCsr = (RtreeCursor *)pVtabCursor;
-
RtreeNode *pRoot = 0;
int ii;
int rc = SQLITE_OK;
+ int iCell = 0;
rtreeReference(pRtree);
+ /* Reset the cursor to the same state as rtreeOpen() leaves it in. */
freeCursorConstraints(pCsr);
- pCsr->iStrategy = idxNum;
+ sqlite3_free(pCsr->aPoint);
+ memset(pCsr, 0, sizeof(RtreeCursor));
+ pCsr->base.pVtab = (sqlite3_vtab*)pRtree;
+ pCsr->iStrategy = idxNum;
if( idxNum==1 ){
/* Special case - lookup by rowid. */
RtreeNode *pLeaf; /* Leaf on which the required cell resides */
+ RtreeSearchPoint *p; /* Search point for the leaf */
i64 iRowid = sqlite3_value_int64(argv[0]);
- rc = findLeafNode(pRtree, iRowid, &pLeaf);
- pCsr->pNode = pLeaf;
- if( pLeaf ){
- assert( rc==SQLITE_OK );
- rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &pCsr->iCell);
+ i64 iNode = 0;
+ rc = findLeafNode(pRtree, iRowid, &pLeaf, &iNode);
+ if( rc==SQLITE_OK && pLeaf!=0 ){
+ p = rtreeSearchPointNew(pCsr, RTREE_ZERO, 0);
+ assert( p!=0 ); /* Always returns pCsr->sPoint */
+ pCsr->aNode[0] = pLeaf;
+ p->id = iNode;
+ p->eWithin = PARTLY_WITHIN;
+ rc = nodeRowidIndex(pRtree, pLeaf, iRowid, &iCell);
+ p->iCell = iCell;
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-F1:");
+ }else{
+ pCsr->atEOF = 1;
}
}else{
/* Normal case - r-tree scan. Set up the RtreeCursor.aConstraint array
** with the configured constraints.
*/
- if( argc>0 ){
+ rc = nodeAcquire(pRtree, 1, 0, &pRoot);
+ if( rc==SQLITE_OK && argc>0 ){
pCsr->aConstraint = sqlite3_malloc(sizeof(RtreeConstraint)*argc);
pCsr->nConstraint = argc;
if( !pCsr->aConstraint ){
rc = SQLITE_NOMEM;
}else{
memset(pCsr->aConstraint, 0, sizeof(RtreeConstraint)*argc);
- assert( (idxStr==0 && argc==0) || (int)strlen(idxStr)==argc*2 );
+ memset(pCsr->anQueue, 0, sizeof(u32)*(pRtree->iDepth + 1));
+ assert( (idxStr==0 && argc==0)
+ || (idxStr && (int)strlen(idxStr)==argc*2) );
for(ii=0; ii<argc; ii++){
RtreeConstraint *p = &pCsr->aConstraint[ii];
p->op = idxStr[ii*2];
- p->iCoord = idxStr[ii*2+1]-'a';
- if( p->op==RTREE_MATCH ){
+ p->iCoord = idxStr[ii*2+1]-'0';
+ if( p->op>=RTREE_MATCH ){
/* A MATCH operator. The right-hand-side must be a blob that
** can be cast into an RtreeMatchArg object. One created using
** an sqlite3_rtree_geometry_callback() SQL user function.
@@ -128311,42 +162902,53 @@ static int rtreeFilter(
if( rc!=SQLITE_OK ){
break;
}
+ p->pInfo->nCoord = pRtree->nDim*2;
+ p->pInfo->anQueue = pCsr->anQueue;
+ p->pInfo->mxLevel = pRtree->iDepth + 1;
}else{
- p->rValue = sqlite3_value_double(argv[ii]);
+#ifdef SQLITE_RTREE_INT_ONLY
+ p->u.rValue = sqlite3_value_int64(argv[ii]);
+#else
+ p->u.rValue = sqlite3_value_double(argv[ii]);
+#endif
}
}
}
}
-
if( rc==SQLITE_OK ){
- pCsr->pNode = 0;
- rc = nodeAcquire(pRtree, 1, 0, &pRoot);
- }
- if( rc==SQLITE_OK ){
- int isEof = 1;
- int nCell = NCELL(pRoot);
- pCsr->pNode = pRoot;
- for(pCsr->iCell=0; rc==SQLITE_OK && pCsr->iCell<nCell; pCsr->iCell++){
- assert( pCsr->pNode==pRoot );
- rc = descendToCell(pRtree, pCsr, pRtree->iDepth, &isEof);
- if( !isEof ){
- break;
- }
- }
- if( rc==SQLITE_OK && isEof ){
- assert( pCsr->pNode==pRoot );
- nodeRelease(pRtree, pRoot);
- pCsr->pNode = 0;
- }
- assert( rc!=SQLITE_OK || !pCsr->pNode || pCsr->iCell<NCELL(pCsr->pNode) );
+ RtreeSearchPoint *pNew;
+ pNew = rtreeSearchPointNew(pCsr, RTREE_ZERO, pRtree->iDepth+1);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ pNew->id = 1;
+ pNew->iCell = 0;
+ pNew->eWithin = PARTLY_WITHIN;
+ assert( pCsr->bPoint==1 );
+ pCsr->aNode[0] = pRoot;
+ pRoot = 0;
+ RTREE_QUEUE_TRACE(pCsr, "PUSH-Fm:");
+ rc = rtreeStepToLeaf(pCsr);
}
}
+ nodeRelease(pRtree, pRoot);
rtreeRelease(pRtree);
return rc;
}
/*
+** Set the pIdxInfo->estimatedRows variable to nRow. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support estimatedRows. In that case this function is a no-op.
+*/
+static void setEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
+#if SQLITE_VERSION_NUMBER>=3008002
+ if( sqlite3_libversion_number()>=3008002 ){
+ pIdxInfo->estimatedRows = nRow;
+ }
+#endif
+}
+
+/*
** Rtree virtual table module xBestIndex method. There are three
** table scan strategies to choose from (in order from most to
** least desirable):
@@ -128381,19 +162983,33 @@ static int rtreeFilter(
** is 'a', the second from the left 'b' etc.
*/
static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ Rtree *pRtree = (Rtree*)tab;
int rc = SQLITE_OK;
int ii;
+ int bMatch = 0; /* True if there exists a MATCH constraint */
+ i64 nRow; /* Estimated rows returned by this scan */
int iIdx = 0;
char zIdxStr[RTREE_MAX_DIMENSIONS*8+1];
memset(zIdxStr, 0, sizeof(zIdxStr));
- UNUSED_PARAMETER(tab);
+
+ /* Check if there exists a MATCH constraint - even an unusable one. If there
+ ** is, do not consider the lookup-by-rowid plan as using such a plan would
+ ** require the VDBE to evaluate the MATCH constraint, which is not currently
+ ** possible. */
+ for(ii=0; ii<pIdxInfo->nConstraint; ii++){
+ if( pIdxInfo->aConstraint[ii].op==SQLITE_INDEX_CONSTRAINT_MATCH ){
+ bMatch = 1;
+ }
+ }
assert( pIdxInfo->idxStr==0 );
for(ii=0; ii<pIdxInfo->nConstraint && iIdx<(int)(sizeof(zIdxStr)-1); ii++){
struct sqlite3_index_constraint *p = &pIdxInfo->aConstraint[ii];
- if( p->usable && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ ){
+ if( bMatch==0 && p->usable
+ && p->iColumn==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ
+ ){
/* We have an equality constraint on the rowid. Use strategy 1. */
int jj;
for(jj=0; jj<ii; jj++){
@@ -128407,9 +163023,11 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
/* This strategy involves a two rowid lookups on an B-Tree structures
** and then a linear search of an R-Tree node. This should be
** considered almost as quick as a direct rowid lookup (for which
- ** sqlite uses an internal cost of 0.0).
+ ** sqlite uses an internal cost of 0.0). It is expected to return
+ ** a single row.
*/
- pIdxInfo->estimatedCost = 10.0;
+ pIdxInfo->estimatedCost = 30.0;
+ setEstimatedRows(pIdxInfo, 1);
return SQLITE_OK;
}
@@ -128427,7 +163045,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
break;
}
zIdxStr[iIdx++] = op;
- zIdxStr[iIdx++] = p->iColumn - 1 + 'a';
+ zIdxStr[iIdx++] = p->iColumn - 1 + '0';
pIdxInfo->aConstraintUsage[ii].argvIndex = (iIdx/2);
pIdxInfo->aConstraintUsage[ii].omit = 1;
}
@@ -128438,19 +163056,22 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
if( iIdx>0 && 0==(pIdxInfo->idxStr = sqlite3_mprintf("%s", zIdxStr)) ){
return SQLITE_NOMEM;
}
- assert( iIdx>=0 );
- pIdxInfo->estimatedCost = (2000000.0 / (double)(iIdx + 1));
+
+ nRow = pRtree->nRowEst >> (iIdx/2);
+ pIdxInfo->estimatedCost = (double)6.0 * (double)nRow;
+ setEstimatedRows(pIdxInfo, nRow);
+
return rc;
}
/*
** Return the N-dimensional volumn of the cell stored in *p.
*/
-static float cellArea(Rtree *pRtree, RtreeCell *p){
- float area = 1.0;
+static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
+ RtreeDValue area = (RtreeDValue)1;
int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- area = (float)(area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])));
+ area = (area * (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii])));
}
return area;
}
@@ -128459,11 +163080,11 @@ static float cellArea(Rtree *pRtree, RtreeCell *p){
** Return the margin length of cell p. The margin length is the sum
** of the objects size in each dimension.
*/
-static float cellMargin(Rtree *pRtree, RtreeCell *p){
- float margin = 0.0;
+static RtreeDValue cellMargin(Rtree *pRtree, RtreeCell *p){
+ RtreeDValue margin = (RtreeDValue)0;
int ii;
for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- margin += (float)(DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
+ margin += (DCOORD(p->aCoord[ii+1]) - DCOORD(p->aCoord[ii]));
}
return margin;
}
@@ -128508,8 +163129,8 @@ static int cellContains(Rtree *pRtree, RtreeCell *p1, RtreeCell *p2){
/*
** Return the amount cell p would grow by if it were unioned with pCell.
*/
-static float cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
- float area;
+static RtreeDValue cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
+ RtreeDValue area;
RtreeCell cell;
memcpy(&cell, p, sizeof(RtreeCell));
area = cellArea(pRtree, &cell);
@@ -128517,64 +163138,32 @@ static float cellGrowth(Rtree *pRtree, RtreeCell *p, RtreeCell *pCell){
return (cellArea(pRtree, &cell)-area);
}
-#if VARIANT_RSTARTREE_CHOOSESUBTREE || VARIANT_RSTARTREE_SPLIT
-static float cellOverlap(
+static RtreeDValue cellOverlap(
Rtree *pRtree,
RtreeCell *p,
RtreeCell *aCell,
- int nCell,
- int iExclude
+ int nCell
){
int ii;
- float overlap = 0.0;
+ RtreeDValue overlap = RTREE_ZERO;
for(ii=0; ii<nCell; ii++){
-#if VARIANT_RSTARTREE_CHOOSESUBTREE
- if( ii!=iExclude )
-#else
- assert( iExclude==-1 );
- UNUSED_PARAMETER(iExclude);
-#endif
- {
- int jj;
- float o = 1.0;
- for(jj=0; jj<(pRtree->nDim*2); jj+=2){
- double x1;
- double x2;
-
- x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj]));
- x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1]));
-
- if( x2<x1 ){
- o = 0.0;
- break;
- }else{
- o = o * (float)(x2-x1);
- }
+ int jj;
+ RtreeDValue o = (RtreeDValue)1;
+ for(jj=0; jj<(pRtree->nDim*2); jj+=2){
+ RtreeDValue x1, x2;
+ x1 = MAX(DCOORD(p->aCoord[jj]), DCOORD(aCell[ii].aCoord[jj]));
+ x2 = MIN(DCOORD(p->aCoord[jj+1]), DCOORD(aCell[ii].aCoord[jj+1]));
+ if( x2<x1 ){
+ o = (RtreeDValue)0;
+ break;
+ }else{
+ o = o * (x2-x1);
}
- overlap += o;
}
+ overlap += o;
}
return overlap;
}
-#endif
-
-#if VARIANT_RSTARTREE_CHOOSESUBTREE
-static float cellOverlapEnlargement(
- Rtree *pRtree,
- RtreeCell *p,
- RtreeCell *pInsert,
- RtreeCell *aCell,
- int nCell,
- int iExclude
-){
- double before;
- double after;
- before = cellOverlap(pRtree, p, aCell, nCell, iExclude);
- cellUnion(pRtree, p, pInsert);
- after = cellOverlap(pRtree, p, aCell, nCell, iExclude);
- return (float)(after-before);
-}
-#endif
/*
@@ -128596,9 +163185,8 @@ static int ChooseLeaf(
int iCell;
sqlite3_int64 iBest = 0;
- float fMinGrowth = 0.0;
- float fMinArea = 0.0;
- float fMinOverlap = 0.0;
+ RtreeDValue fMinGrowth = RTREE_ZERO;
+ RtreeDValue fMinArea = RTREE_ZERO;
int nCell = NCELL(pNode);
RtreeCell cell;
@@ -128606,53 +163194,21 @@ static int ChooseLeaf(
RtreeCell *aCell = 0;
-#if VARIANT_RSTARTREE_CHOOSESUBTREE
- if( ii==(pRtree->iDepth-1) ){
- int jj;
- aCell = sqlite3_malloc(sizeof(RtreeCell)*nCell);
- if( !aCell ){
- rc = SQLITE_NOMEM;
- nodeRelease(pRtree, pNode);
- pNode = 0;
- continue;
- }
- for(jj=0; jj<nCell; jj++){
- nodeGetCell(pRtree, pNode, jj, &aCell[jj]);
- }
- }
-#endif
-
/* Select the child node which will be enlarged the least if pCell
** is inserted into it. Resolve ties by choosing the entry with
** the smallest area.
*/
for(iCell=0; iCell<nCell; iCell++){
int bBest = 0;
- float growth;
- float area;
- float overlap = 0.0;
+ RtreeDValue growth;
+ RtreeDValue area;
nodeGetCell(pRtree, pNode, iCell, &cell);
growth = cellGrowth(pRtree, &cell, pCell);
area = cellArea(pRtree, &cell);
-
-#if VARIANT_RSTARTREE_CHOOSESUBTREE
- if( ii==(pRtree->iDepth-1) ){
- overlap = cellOverlapEnlargement(pRtree,&cell,pCell,aCell,nCell,iCell);
- }
- if( (iCell==0)
- || (overlap<fMinOverlap)
- || (overlap==fMinOverlap && growth<fMinGrowth)
- || (overlap==fMinOverlap && growth==fMinGrowth && area<fMinArea)
- ){
- bBest = 1;
- }
-#else
if( iCell==0||growth<fMinGrowth||(growth==fMinGrowth && area<fMinArea) ){
bBest = 1;
}
-#endif
if( bBest ){
- fMinOverlap = overlap;
fMinGrowth = growth;
fMinArea = area;
iBest = cell.iRowid;
@@ -128722,155 +163278,6 @@ static int parentWrite(Rtree *pRtree, sqlite3_int64 iNode, sqlite3_int64 iPar){
static int rtreeInsertCell(Rtree *, RtreeNode *, RtreeCell *, int);
-#if VARIANT_GUTTMAN_LINEAR_SPLIT
-/*
-** Implementation of the linear variant of the PickNext() function from
-** Guttman[84].
-*/
-static RtreeCell *LinearPickNext(
- Rtree *pRtree,
- RtreeCell *aCell,
- int nCell,
- RtreeCell *pLeftBox,
- RtreeCell *pRightBox,
- int *aiUsed
-){
- int ii;
- for(ii=0; aiUsed[ii]; ii++);
- aiUsed[ii] = 1;
- return &aCell[ii];
-}
-
-/*
-** Implementation of the linear variant of the PickSeeds() function from
-** Guttman[84].
-*/
-static void LinearPickSeeds(
- Rtree *pRtree,
- RtreeCell *aCell,
- int nCell,
- int *piLeftSeed,
- int *piRightSeed
-){
- int i;
- int iLeftSeed = 0;
- int iRightSeed = 1;
- float maxNormalInnerWidth = 0.0;
-
- /* Pick two "seed" cells from the array of cells. The algorithm used
- ** here is the LinearPickSeeds algorithm from Gutman[1984]. The
- ** indices of the two seed cells in the array are stored in local
- ** variables iLeftSeek and iRightSeed.
- */
- for(i=0; i<pRtree->nDim; i++){
- float x1 = DCOORD(aCell[0].aCoord[i*2]);
- float x2 = DCOORD(aCell[0].aCoord[i*2+1]);
- float x3 = x1;
- float x4 = x2;
- int jj;
-
- int iCellLeft = 0;
- int iCellRight = 0;
-
- for(jj=1; jj<nCell; jj++){
- float left = DCOORD(aCell[jj].aCoord[i*2]);
- float right = DCOORD(aCell[jj].aCoord[i*2+1]);
-
- if( left<x1 ) x1 = left;
- if( right>x4 ) x4 = right;
- if( left>x3 ){
- x3 = left;
- iCellRight = jj;
- }
- if( right<x2 ){
- x2 = right;
- iCellLeft = jj;
- }
- }
-
- if( x4!=x1 ){
- float normalwidth = (x3 - x2) / (x4 - x1);
- if( normalwidth>maxNormalInnerWidth ){
- iLeftSeed = iCellLeft;
- iRightSeed = iCellRight;
- }
- }
- }
-
- *piLeftSeed = iLeftSeed;
- *piRightSeed = iRightSeed;
-}
-#endif /* VARIANT_GUTTMAN_LINEAR_SPLIT */
-
-#if VARIANT_GUTTMAN_QUADRATIC_SPLIT
-/*
-** Implementation of the quadratic variant of the PickNext() function from
-** Guttman[84].
-*/
-static RtreeCell *QuadraticPickNext(
- Rtree *pRtree,
- RtreeCell *aCell,
- int nCell,
- RtreeCell *pLeftBox,
- RtreeCell *pRightBox,
- int *aiUsed
-){
- #define FABS(a) ((a)<0.0?-1.0*(a):(a))
-
- int iSelect = -1;
- float fDiff;
- int ii;
- for(ii=0; ii<nCell; ii++){
- if( aiUsed[ii]==0 ){
- float left = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
- float right = cellGrowth(pRtree, pLeftBox, &aCell[ii]);
- float diff = FABS(right-left);
- if( iSelect<0 || diff>fDiff ){
- fDiff = diff;
- iSelect = ii;
- }
- }
- }
- aiUsed[iSelect] = 1;
- return &aCell[iSelect];
-}
-
-/*
-** Implementation of the quadratic variant of the PickSeeds() function from
-** Guttman[84].
-*/
-static void QuadraticPickSeeds(
- Rtree *pRtree,
- RtreeCell *aCell,
- int nCell,
- int *piLeftSeed,
- int *piRightSeed
-){
- int ii;
- int jj;
-
- int iLeftSeed = 0;
- int iRightSeed = 1;
- float fWaste = 0.0;
-
- for(ii=0; ii<nCell; ii++){
- for(jj=ii+1; jj<nCell; jj++){
- float right = cellArea(pRtree, &aCell[jj]);
- float growth = cellGrowth(pRtree, &aCell[ii], &aCell[jj]);
- float waste = growth - right;
-
- if( waste>fWaste ){
- iLeftSeed = ii;
- iRightSeed = jj;
- fWaste = waste;
- }
- }
- }
-
- *piLeftSeed = iLeftSeed;
- *piRightSeed = iRightSeed;
-}
-#endif /* VARIANT_GUTTMAN_QUADRATIC_SPLIT */
/*
** Arguments aIdx, aDistance and aSpare all point to arrays of size
@@ -128892,7 +163299,7 @@ static void QuadraticPickSeeds(
static void SortByDistance(
int *aIdx,
int nIdx,
- float *aDistance,
+ RtreeDValue *aDistance,
int *aSpare
){
if( nIdx>1 ){
@@ -128918,8 +163325,8 @@ static void SortByDistance(
aIdx[iLeft+iRight] = aLeft[iLeft];
iLeft++;
}else{
- float fLeft = aDistance[aLeft[iLeft]];
- float fRight = aDistance[aRight[iRight]];
+ RtreeDValue fLeft = aDistance[aLeft[iLeft]];
+ RtreeDValue fRight = aDistance[aRight[iRight]];
if( fLeft<fRight ){
aIdx[iLeft+iRight] = aLeft[iLeft];
iLeft++;
@@ -128935,8 +163342,8 @@ static void SortByDistance(
{
int jj;
for(jj=1; jj<nIdx; jj++){
- float left = aDistance[aIdx[jj-1]];
- float right = aDistance[aIdx[jj]];
+ RtreeDValue left = aDistance[aIdx[jj-1]];
+ RtreeDValue right = aDistance[aIdx[jj]];
assert( left<=right );
}
}
@@ -128979,10 +163386,10 @@ static void SortByDimension(
memcpy(aSpare, aLeft, sizeof(int)*nLeft);
aLeft = aSpare;
while( iLeft<nLeft || iRight<nRight ){
- double xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]);
- double xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]);
- double xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]);
- double xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]);
+ RtreeDValue xleft1 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2]);
+ RtreeDValue xleft2 = DCOORD(aCell[aLeft[iLeft]].aCoord[iDim*2+1]);
+ RtreeDValue xright1 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2]);
+ RtreeDValue xright2 = DCOORD(aCell[aRight[iRight]].aCoord[iDim*2+1]);
if( (iLeft!=nLeft) && ((iRight==nRight)
|| (xleft1<xright1)
|| (xleft1==xright1 && xleft2<xright2)
@@ -129000,10 +163407,10 @@ static void SortByDimension(
{
int jj;
for(jj=1; jj<nIdx; jj++){
- float xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2];
- float xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1];
- float xright1 = aCell[aIdx[jj]].aCoord[iDim*2];
- float xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1];
+ RtreeDValue xleft1 = aCell[aIdx[jj-1]].aCoord[iDim*2];
+ RtreeDValue xleft2 = aCell[aIdx[jj-1]].aCoord[iDim*2+1];
+ RtreeDValue xright1 = aCell[aIdx[jj]].aCoord[iDim*2];
+ RtreeDValue xright2 = aCell[aIdx[jj]].aCoord[iDim*2+1];
assert( xleft1<=xright1 && (xleft1<xright1 || xleft2<=xright2) );
}
}
@@ -129011,7 +163418,6 @@ static void SortByDimension(
}
}
-#if VARIANT_RSTARTREE_SPLIT
/*
** Implementation of the R*-tree variant of SplitNode from Beckman[1990].
*/
@@ -129030,7 +163436,7 @@ static int splitNodeStartree(
int iBestDim = 0;
int iBestSplit = 0;
- float fBestMargin = 0.0;
+ RtreeDValue fBestMargin = RTREE_ZERO;
int nByte = (pRtree->nDim+1)*(sizeof(int*)+nCell*sizeof(int));
@@ -129051,9 +163457,9 @@ static int splitNodeStartree(
}
for(ii=0; ii<pRtree->nDim; ii++){
- float margin = 0.0;
- float fBestOverlap = 0.0;
- float fBestArea = 0.0;
+ RtreeDValue margin = RTREE_ZERO;
+ RtreeDValue fBestOverlap = RTREE_ZERO;
+ RtreeDValue fBestArea = RTREE_ZERO;
int iBestLeft = 0;
int nLeft;
@@ -129065,8 +163471,8 @@ static int splitNodeStartree(
RtreeCell left;
RtreeCell right;
int kk;
- float overlap;
- float area;
+ RtreeDValue overlap;
+ RtreeDValue area;
memcpy(&left, &aCell[aaSorted[ii][0]], sizeof(RtreeCell));
memcpy(&right, &aCell[aaSorted[ii][nCell-1]], sizeof(RtreeCell));
@@ -129079,7 +163485,7 @@ static int splitNodeStartree(
}
margin += cellMargin(pRtree, &left);
margin += cellMargin(pRtree, &right);
- overlap = cellOverlap(pRtree, &left, &right, 1, -1);
+ overlap = cellOverlap(pRtree, &left, &right, 1);
area = cellArea(pRtree, &left) + cellArea(pRtree, &right);
if( (nLeft==RTREE_MINCELLS(pRtree))
|| (overlap<fBestOverlap)
@@ -129111,63 +163517,7 @@ static int splitNodeStartree(
sqlite3_free(aaSorted);
return SQLITE_OK;
}
-#endif
-
-#if VARIANT_GUTTMAN_SPLIT
-/*
-** Implementation of the regular R-tree SplitNode from Guttman[1984].
-*/
-static int splitNodeGuttman(
- Rtree *pRtree,
- RtreeCell *aCell,
- int nCell,
- RtreeNode *pLeft,
- RtreeNode *pRight,
- RtreeCell *pBboxLeft,
- RtreeCell *pBboxRight
-){
- int iLeftSeed = 0;
- int iRightSeed = 1;
- int *aiUsed;
- int i;
-
- aiUsed = sqlite3_malloc(sizeof(int)*nCell);
- if( !aiUsed ){
- return SQLITE_NOMEM;
- }
- memset(aiUsed, 0, sizeof(int)*nCell);
-
- PickSeeds(pRtree, aCell, nCell, &iLeftSeed, &iRightSeed);
-
- memcpy(pBboxLeft, &aCell[iLeftSeed], sizeof(RtreeCell));
- memcpy(pBboxRight, &aCell[iRightSeed], sizeof(RtreeCell));
- nodeInsertCell(pRtree, pLeft, &aCell[iLeftSeed]);
- nodeInsertCell(pRtree, pRight, &aCell[iRightSeed]);
- aiUsed[iLeftSeed] = 1;
- aiUsed[iRightSeed] = 1;
-
- for(i=nCell-2; i>0; i--){
- RtreeCell *pNext;
- pNext = PickNext(pRtree, aCell, nCell, pBboxLeft, pBboxRight, aiUsed);
- float diff =
- cellGrowth(pRtree, pBboxLeft, pNext) -
- cellGrowth(pRtree, pBboxRight, pNext)
- ;
- if( (RTREE_MINCELLS(pRtree)-NCELL(pRight)==i)
- || (diff>0.0 && (RTREE_MINCELLS(pRtree)-NCELL(pLeft)!=i))
- ){
- nodeInsertCell(pRtree, pRight, pNext);
- cellUnion(pRtree, pBboxRight, pNext);
- }else{
- nodeInsertCell(pRtree, pLeft, pNext);
- cellUnion(pRtree, pBboxLeft, pNext);
- }
- }
- sqlite3_free(aiUsed);
- return SQLITE_OK;
-}
-#endif
static int updateMapping(
Rtree *pRtree,
@@ -129245,7 +163595,8 @@ static int SplitNode(
memset(pLeft->zData, 0, pRtree->iNodeSize);
memset(pRight->zData, 0, pRtree->iNodeSize);
- rc = AssignCells(pRtree, aCell, nCell, pLeft, pRight, &leftbbox, &rightbbox);
+ rc = splitNodeStartree(pRtree, aCell, nCell, pLeft, pRight,
+ &leftbbox, &rightbbox);
if( rc!=SQLITE_OK ){
goto splitnode_out;
}
@@ -129482,32 +163833,34 @@ static int Reinsert(
int *aOrder;
int *aSpare;
RtreeCell *aCell;
- float *aDistance;
+ RtreeDValue *aDistance;
int nCell;
- float aCenterCoord[RTREE_MAX_DIMENSIONS];
+ RtreeDValue aCenterCoord[RTREE_MAX_DIMENSIONS];
int iDim;
int ii;
int rc = SQLITE_OK;
+ int n;
- memset(aCenterCoord, 0, sizeof(float)*RTREE_MAX_DIMENSIONS);
+ memset(aCenterCoord, 0, sizeof(RtreeDValue)*RTREE_MAX_DIMENSIONS);
nCell = NCELL(pNode)+1;
+ n = (nCell+1)&(~1);
/* Allocate the buffers used by this operation. The allocation is
** relinquished before this function returns.
*/
- aCell = (RtreeCell *)sqlite3_malloc(nCell * (
- sizeof(RtreeCell) + /* aCell array */
- sizeof(int) + /* aOrder array */
- sizeof(int) + /* aSpare array */
- sizeof(float) /* aDistance array */
+ aCell = (RtreeCell *)sqlite3_malloc(n * (
+ sizeof(RtreeCell) + /* aCell array */
+ sizeof(int) + /* aOrder array */
+ sizeof(int) + /* aSpare array */
+ sizeof(RtreeDValue) /* aDistance array */
));
if( !aCell ){
return SQLITE_NOMEM;
}
- aOrder = (int *)&aCell[nCell];
- aSpare = (int *)&aOrder[nCell];
- aDistance = (float *)&aSpare[nCell];
+ aOrder = (int *)&aCell[n];
+ aSpare = (int *)&aOrder[n];
+ aDistance = (RtreeDValue *)&aSpare[n];
for(ii=0; ii<nCell; ii++){
if( ii==(nCell-1) ){
@@ -129517,19 +163870,19 @@ static int Reinsert(
}
aOrder[ii] = ii;
for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2]);
- aCenterCoord[iDim] += (float)DCOORD(aCell[ii].aCoord[iDim*2+1]);
+ aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2]);
+ aCenterCoord[iDim] += DCOORD(aCell[ii].aCoord[iDim*2+1]);
}
}
for(iDim=0; iDim<pRtree->nDim; iDim++){
- aCenterCoord[iDim] = (float)(aCenterCoord[iDim]/((float)nCell*2.0));
+ aCenterCoord[iDim] = (aCenterCoord[iDim]/(nCell*(RtreeDValue)2));
}
for(ii=0; ii<nCell; ii++){
- aDistance[ii] = 0.0;
+ aDistance[ii] = RTREE_ZERO;
for(iDim=0; iDim<pRtree->nDim; iDim++){
- float coord = (float)(DCOORD(aCell[ii].aCoord[iDim*2+1]) -
- DCOORD(aCell[ii].aCoord[iDim*2]));
+ RtreeDValue coord = (DCOORD(aCell[ii].aCoord[iDim*2+1]) -
+ DCOORD(aCell[ii].aCoord[iDim*2]));
aDistance[ii] += (coord-aCenterCoord[iDim])*(coord-aCenterCoord[iDim]);
}
}
@@ -129592,16 +163945,12 @@ static int rtreeInsertCell(
}
}
if( nodeInsertCell(pRtree, pNode, pCell) ){
-#if VARIANT_RSTARTREE_REINSERT
if( iHeight<=pRtree->iReinsertHeight || pNode->iNode==1){
rc = SplitNode(pRtree, pNode, pCell, iHeight);
}else{
pRtree->iReinsertHeight = iHeight;
rc = Reinsert(pRtree, pNode, pCell, iHeight);
}
-#else
- rc = SplitNode(pRtree, pNode, pCell, iHeight);
-#endif
}else{
rc = AdjustTree(pRtree, pNode, pCell);
if( rc==SQLITE_OK ){
@@ -129659,19 +164008,19 @@ static int newRowid(Rtree *pRtree, i64 *piRowid){
*/
static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
int rc; /* Return code */
- RtreeNode *pLeaf; /* Leaf node containing record iDelete */
+ RtreeNode *pLeaf = 0; /* Leaf node containing record iDelete */
int iCell; /* Index of iDelete cell in pLeaf */
RtreeNode *pRoot; /* Root node of rtree structure */
- /* Obtain a reference to the root node to initialise Rtree.iDepth */
+ /* Obtain a reference to the root node to initialize Rtree.iDepth */
rc = nodeAcquire(pRtree, 1, 0, &pRoot);
/* Obtain a reference to the leaf node that contains the entry
** about to be deleted.
*/
if( rc==SQLITE_OK ){
- rc = findLeafNode(pRtree, iDelete, &pLeaf);
+ rc = findLeafNode(pRtree, iDelete, &pLeaf, 0);
}
/* Delete the cell in question from the leaf node. */
@@ -129739,6 +164088,83 @@ static int rtreeDeleteRowid(Rtree *pRtree, sqlite3_int64 iDelete){
}
/*
+** Rounding constants for float->double conversion.
+*/
+#define RNDTOWARDS (1.0 - 1.0/8388608.0) /* Round towards zero */
+#define RNDAWAY (1.0 + 1.0/8388608.0) /* Round away from zero */
+
+#if !defined(SQLITE_RTREE_INT_ONLY)
+/*
+** Convert an sqlite3_value into an RtreeValue (presumably a float)
+** while taking care to round toward negative or positive, respectively.
+*/
+static RtreeValue rtreeValueDown(sqlite3_value *v){
+ double d = sqlite3_value_double(v);
+ float f = (float)d;
+ if( f>d ){
+ f = (float)(d*(d<0 ? RNDAWAY : RNDTOWARDS));
+ }
+ return f;
+}
+static RtreeValue rtreeValueUp(sqlite3_value *v){
+ double d = sqlite3_value_double(v);
+ float f = (float)d;
+ if( f<d ){
+ f = (float)(d*(d<0 ? RNDTOWARDS : RNDAWAY));
+ }
+ return f;
+}
+#endif /* !defined(SQLITE_RTREE_INT_ONLY) */
+
+/*
+** A constraint has failed while inserting a row into an rtree table.
+** Assuming no OOM error occurs, this function sets the error message
+** (at pRtree->base.zErrMsg) to an appropriate value and returns
+** SQLITE_CONSTRAINT.
+**
+** Parameter iCol is the index of the leftmost column involved in the
+** constraint failure. If it is 0, then the constraint that failed is
+** the unique constraint on the id column. Otherwise, it is the rtree
+** (c1<=c2) constraint on columns iCol and iCol+1 that has failed.
+**
+** If an OOM occurs, SQLITE_NOMEM is returned instead of SQLITE_CONSTRAINT.
+*/
+static int rtreeConstraintError(Rtree *pRtree, int iCol){
+ sqlite3_stmt *pStmt = 0;
+ char *zSql;
+ int rc;
+
+ assert( iCol==0 || iCol%2 );
+ zSql = sqlite3_mprintf("SELECT * FROM %Q.%Q", pRtree->zDb, pRtree->zName);
+ if( zSql ){
+ rc = sqlite3_prepare_v2(pRtree->db, zSql, -1, &pStmt, 0);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ sqlite3_free(zSql);
+
+ if( rc==SQLITE_OK ){
+ if( iCol==0 ){
+ const char *zCol = sqlite3_column_name(pStmt, 0);
+ pRtree->base.zErrMsg = sqlite3_mprintf(
+ "UNIQUE constraint failed: %s.%s", pRtree->zName, zCol
+ );
+ }else{
+ const char *zCol1 = sqlite3_column_name(pStmt, iCol);
+ const char *zCol2 = sqlite3_column_name(pStmt, iCol+1);
+ pRtree->base.zErrMsg = sqlite3_mprintf(
+ "rtree constraint failed: %s.(%s<=%s)", pRtree->zName, zCol1, zCol2
+ );
+ }
+ }
+
+ sqlite3_finalize(pStmt);
+ return (rc==SQLITE_OK ? SQLITE_CONSTRAINT : rc);
+}
+
+
+
+/*
** The xUpdate method for rtree module virtual tables.
*/
static int rtreeUpdate(
@@ -129755,6 +164181,8 @@ static int rtreeUpdate(
rtreeReference(pRtree);
assert(nData>=1);
+ cell.iRowid = 0; /* Used only to suppress a compiler warning */
+
/* Constraint handling. A write operation on an r-tree table may return
** SQLITE_CONSTRAINT for two reasons:
**
@@ -129769,23 +164197,34 @@ static int rtreeUpdate(
if( nData>1 ){
int ii;
- /* Populate the cell.aCoord[] array. The first coordinate is azData[3]. */
- assert( nData==(pRtree->nDim*2 + 3) );
+ /* Populate the cell.aCoord[] array. The first coordinate is azData[3].
+ **
+ ** NB: nData can only be less than nDim*2+3 if the rtree is mis-declared
+ ** with "column" that are interpreted as table constraints.
+ ** Example: CREATE VIRTUAL TABLE bad USING rtree(x,y,CHECK(y>5));
+ ** This problem was discovered after years of use, so we silently ignore
+ ** these kinds of misdeclared tables to avoid breaking any legacy.
+ */
+ assert( nData<=(pRtree->nDim*2 + 3) );
+
+#ifndef SQLITE_RTREE_INT_ONLY
if( pRtree->eCoordType==RTREE_COORD_REAL32 ){
- for(ii=0; ii<(pRtree->nDim*2); ii+=2){
- cell.aCoord[ii].f = (float)sqlite3_value_double(azData[ii+3]);
- cell.aCoord[ii+1].f = (float)sqlite3_value_double(azData[ii+4]);
+ for(ii=0; ii<nData-4; ii+=2){
+ cell.aCoord[ii].f = rtreeValueDown(azData[ii+3]);
+ cell.aCoord[ii+1].f = rtreeValueUp(azData[ii+4]);
if( cell.aCoord[ii].f>cell.aCoord[ii+1].f ){
- rc = SQLITE_CONSTRAINT;
+ rc = rtreeConstraintError(pRtree, ii+1);
goto constraint;
}
}
- }else{
- for(ii=0; ii<(pRtree->nDim*2); ii+=2){
+ }else
+#endif
+ {
+ for(ii=0; ii<nData-4; ii+=2){
cell.aCoord[ii].i = sqlite3_value_int(azData[ii+3]);
cell.aCoord[ii+1].i = sqlite3_value_int(azData[ii+4]);
if( cell.aCoord[ii].i>cell.aCoord[ii+1].i ){
- rc = SQLITE_CONSTRAINT;
+ rc = rtreeConstraintError(pRtree, ii+1);
goto constraint;
}
}
@@ -129806,7 +164245,7 @@ static int rtreeUpdate(
if( sqlite3_vtab_on_conflict(pRtree->db)==SQLITE_REPLACE ){
rc = rtreeDeleteRowid(pRtree, cell.iRowid);
}else{
- rc = SQLITE_CONSTRAINT;
+ rc = rtreeConstraintError(pRtree, 0);
goto constraint;
}
}
@@ -129829,7 +164268,7 @@ static int rtreeUpdate(
*/
if( rc==SQLITE_OK && nData>1 ){
/* Insert the new record into the r-tree */
- RtreeNode *pLeaf;
+ RtreeNode *pLeaf = 0;
/* Figure out the rowid of the new row. */
if( bHaveRowid==0 ){
@@ -129877,6 +164316,50 @@ static int rtreeRename(sqlite3_vtab *pVtab, const char *zNewName){
return rc;
}
+/*
+** This function populates the pRtree->nRowEst variable with an estimate
+** of the number of rows in the virtual table. If possible, this is based
+** on sqlite_stat1 data. Otherwise, use RTREE_DEFAULT_ROWEST.
+*/
+static int rtreeQueryStat1(sqlite3 *db, Rtree *pRtree){
+ const char *zFmt = "SELECT stat FROM %Q.sqlite_stat1 WHERE tbl = '%q_rowid'";
+ char *zSql;
+ sqlite3_stmt *p;
+ int rc;
+ i64 nRow = 0;
+
+ rc = sqlite3_table_column_metadata(
+ db, pRtree->zDb, "sqlite_stat1",0,0,0,0,0,0
+ );
+ if( rc!=SQLITE_OK ){
+ pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
+ return rc==SQLITE_ERROR ? SQLITE_OK : rc;
+ }
+ zSql = sqlite3_mprintf(zFmt, pRtree->zDb, pRtree->zName);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(db, zSql, -1, &p, 0);
+ if( rc==SQLITE_OK ){
+ if( sqlite3_step(p)==SQLITE_ROW ) nRow = sqlite3_column_int64(p, 0);
+ rc = sqlite3_finalize(p);
+ }else if( rc!=SQLITE_NOMEM ){
+ rc = SQLITE_OK;
+ }
+
+ if( rc==SQLITE_OK ){
+ if( nRow==0 ){
+ pRtree->nRowEst = RTREE_DEFAULT_ROWEST;
+ }else{
+ pRtree->nRowEst = MAX(nRow, RTREE_MIN_ROWEST);
+ }
+ }
+ sqlite3_free(zSql);
+ }
+
+ return rc;
+}
+
static sqlite3_module rtreeModule = {
0, /* iVersion */
rtreeCreate, /* xCreate - create a table */
@@ -129938,7 +164421,8 @@ static int rtreeSqlInit(
char *zCreate = sqlite3_mprintf(
"CREATE TABLE \"%w\".\"%w_node\"(nodeno INTEGER PRIMARY KEY, data BLOB);"
"CREATE TABLE \"%w\".\"%w_rowid\"(rowid INTEGER PRIMARY KEY, nodeno INTEGER);"
-"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY, parentnode INTEGER);"
+"CREATE TABLE \"%w\".\"%w_parent\"(nodeno INTEGER PRIMARY KEY,"
+ " parentnode INTEGER);"
"INSERT INTO '%q'.'%q_node' VALUES(1, zeroblob(%d))",
zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, zDb, zPrefix, pRtree->iNodeSize
);
@@ -129962,6 +164446,7 @@ static int rtreeSqlInit(
appStmt[7] = &pRtree->pWriteParent;
appStmt[8] = &pRtree->pDeleteParent;
+ rc = rtreeQueryStat1(db, pRtree);
for(i=0; i<N_STATEMENT && rc==SQLITE_OK; i++){
char *zSql = sqlite3_mprintf(azSql[i], zDb, zPrefix);
if( zSql ){
@@ -130015,7 +164500,8 @@ static int getIntFromStmt(sqlite3 *db, const char *zSql, int *piVal){
static int getNodeSize(
sqlite3 *db, /* Database handle */
Rtree *pRtree, /* Rtree handle */
- int isCreate /* True for xCreate, false for xConnect */
+ int isCreate, /* True for xCreate, false for xConnect */
+ char **pzErr /* OUT: Error message, if any */
){
int rc;
char *zSql;
@@ -130028,6 +164514,8 @@ static int getNodeSize(
if( (4+pRtree->nBytesPerCell*RTREE_MAXCELLS)<pRtree->iNodeSize ){
pRtree->iNodeSize = 4+pRtree->nBytesPerCell*RTREE_MAXCELLS;
}
+ }else{
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
}else{
zSql = sqlite3_mprintf(
@@ -130035,6 +164523,9 @@ static int getNodeSize(
pRtree->zDb, pRtree->zName
);
rc = getIntFromStmt(db, zSql, &pRtree->iNodeSize);
+ if( rc!=SQLITE_OK ){
+ *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ }
}
sqlite3_free(zSql);
@@ -130080,8 +164571,8 @@ static int rtreeInit(
sqlite3_vtab_config(db, SQLITE_VTAB_CONSTRAINT_SUPPORT, 1);
/* Allocate the sqlite3_vtab structure */
- nDb = strlen(argv[1]);
- nName = strlen(argv[2]);
+ nDb = (int)strlen(argv[1]);
+ nName = (int)strlen(argv[2]);
pRtree = (Rtree *)sqlite3_malloc(sizeof(Rtree)+nDb+nName+2);
if( !pRtree ){
return SQLITE_NOMEM;
@@ -130098,7 +164589,7 @@ static int rtreeInit(
memcpy(pRtree->zName, argv[2], nName);
/* Figure out the node size to use. */
- rc = getNodeSize(db, pRtree, isCreate);
+ rc = getNodeSize(db, pRtree, isCreate, pzErr);
/* Create/Connect to the underlying relational database schema. If
** that is successful, call sqlite3_declare_vtab() to configure
@@ -130133,6 +164624,8 @@ static int rtreeInit(
if( rc==SQLITE_OK ){
*ppVtab = (sqlite3_vtab *)pRtree;
}else{
+ assert( *ppVtab==0 );
+ assert( pRtree->nBusy==1 );
rtreeRelease(pRtree);
}
return rc;
@@ -130143,10 +164636,10 @@ static int rtreeInit(
** Implementation of a scalar function that decodes r-tree nodes to
** human readable strings. This can be used for debugging and analysis.
**
-** The scalar function takes two arguments, a blob of data containing
-** an r-tree node, and the number of dimensions the r-tree indexes.
-** For a two-dimensional r-tree structure called "rt", to deserialize
-** all nodes, a statement like:
+** The scalar function takes two arguments: (1) the number of dimensions
+** to the rtree (between 1 and 5, inclusive) and (2) a blob of data containing
+** an r-tree node. For a two-dimensional r-tree structure called "rt", to
+** deserialize all nodes, a statement like:
**
** SELECT rtreenode(2, data) FROM rt_node;
**
@@ -130176,10 +164669,16 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
nodeGetCell(&tree, &node, ii, &cell);
sqlite3_snprintf(512-nCell,&zCell[nCell],"%lld", cell.iRowid);
- nCell = strlen(zCell);
+ nCell = (int)strlen(zCell);
for(jj=0; jj<tree.nDim*2; jj++){
- sqlite3_snprintf(512-nCell,&zCell[nCell]," %f",(double)cell.aCoord[jj].f);
- nCell = strlen(zCell);
+#ifndef SQLITE_RTREE_INT_ONLY
+ sqlite3_snprintf(512-nCell,&zCell[nCell], " %g",
+ (double)cell.aCoord[jj].f);
+#else
+ sqlite3_snprintf(512-nCell,&zCell[nCell], " %d",
+ cell.aCoord[jj].i);
+#endif
+ nCell = (int)strlen(zCell);
}
if( zText ){
@@ -130194,6 +164693,15 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
sqlite3_result_text(ctx, zText, -1, sqlite3_free);
}
+/* This routine implements an SQL function that returns the "depth" parameter
+** from the front of a blob that is an r-tree node. For example:
+**
+** SELECT rtreedepth(data) FROM rt_node WHERE nodeno=1;
+**
+** The depth value is 0 for all nodes other than the root node, and the root
+** node always has nodeno=1, so the example above is the primary use for this
+** routine. This routine is intended for testing and analysis only.
+*/
static void rtreedepth(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
UNUSED_PARAMETER(nArg);
if( sqlite3_value_type(apArg[0])!=SQLITE_BLOB
@@ -130220,7 +164728,11 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db){
rc = sqlite3_create_function(db, "rtreedepth", 1, utf8, 0,rtreedepth, 0, 0);
}
if( rc==SQLITE_OK ){
+#ifdef SQLITE_RTREE_INT_ONLY
+ void *c = (void *)RTREE_COORD_INT32;
+#else
void *c = (void *)RTREE_COORD_REAL32;
+#endif
rc = sqlite3_create_module_v2(db, "rtree", &rtreeModule, c, 0);
}
if( rc==SQLITE_OK ){
@@ -130232,42 +164744,76 @@ SQLITE_PRIVATE int sqlite3RtreeInit(sqlite3 *db){
}
/*
-** A version of sqlite3_free() that can be used as a callback. This is used
-** in two places - as the destructor for the blob value returned by the
-** invocation of a geometry function, and as the destructor for the geometry
-** functions themselves.
+** This routine deletes the RtreeGeomCallback object that was attached
+** one of the SQL functions create by sqlite3_rtree_geometry_callback()
+** or sqlite3_rtree_query_callback(). In other words, this routine is the
+** destructor for an RtreeGeomCallback objecct. This routine is called when
+** the corresponding SQL function is deleted.
+*/
+static void rtreeFreeCallback(void *p){
+ RtreeGeomCallback *pInfo = (RtreeGeomCallback*)p;
+ if( pInfo->xDestructor ) pInfo->xDestructor(pInfo->pContext);
+ sqlite3_free(p);
+}
+
+/*
+** This routine frees the BLOB that is returned by geomCallback().
*/
-static void doSqlite3Free(void *p){
+static void rtreeMatchArgFree(void *pArg){
+ int i;
+ RtreeMatchArg *p = (RtreeMatchArg*)pArg;
+ for(i=0; i<p->nParam; i++){
+ sqlite3_value_free(p->apSqlParam[i]);
+ }
sqlite3_free(p);
}
/*
-** Each call to sqlite3_rtree_geometry_callback() creates an ordinary SQLite
-** scalar user function. This C function is the callback used for all such
-** registered SQL functions.
+** Each call to sqlite3_rtree_geometry_callback() or
+** sqlite3_rtree_query_callback() creates an ordinary SQLite
+** scalar function that is implemented by this routine.
**
-** The scalar user functions return a blob that is interpreted by r-tree
-** table MATCH operators.
+** All this function does is construct an RtreeMatchArg object that
+** contains the geometry-checking callback routines and a list of
+** parameters to this function, then return that RtreeMatchArg object
+** as a BLOB.
+**
+** The R-Tree MATCH operator will read the returned BLOB, deserialize
+** the RtreeMatchArg object, and use the RtreeMatchArg object to figure
+** out which elements of the R-Tree should be returned by the query.
*/
static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
RtreeMatchArg *pBlob;
int nBlob;
+ int memErr = 0;
- nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(double);
+ nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
+ + nArg*sizeof(sqlite3_value*);
pBlob = (RtreeMatchArg *)sqlite3_malloc(nBlob);
if( !pBlob ){
sqlite3_result_error_nomem(ctx);
}else{
int i;
pBlob->magic = RTREE_GEOMETRY_MAGIC;
- pBlob->xGeom = pGeomCtx->xGeom;
- pBlob->pContext = pGeomCtx->pContext;
+ pBlob->cb = pGeomCtx[0];
+ pBlob->apSqlParam = (sqlite3_value**)&pBlob->aParam[nArg];
pBlob->nParam = nArg;
for(i=0; i<nArg; i++){
+ pBlob->apSqlParam[i] = sqlite3_value_dup(aArg[i]);
+ if( pBlob->apSqlParam[i]==0 ) memErr = 1;
+#ifdef SQLITE_RTREE_INT_ONLY
+ pBlob->aParam[i] = sqlite3_value_int64(aArg[i]);
+#else
pBlob->aParam[i] = sqlite3_value_double(aArg[i]);
+#endif
+ }
+ if( memErr ){
+ sqlite3_result_error_nomem(ctx);
+ rtreeMatchArgFree(pBlob);
+ }else{
+ sqlite3_result_blob(ctx, pBlob, nBlob, rtreeMatchArgFree);
}
- sqlite3_result_blob(ctx, pBlob, nBlob, doSqlite3Free);
}
}
@@ -130275,10 +164821,10 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
** Register a new geometry function for use with the r-tree MATCH operator.
*/
SQLITE_API int sqlite3_rtree_geometry_callback(
- sqlite3 *db,
- const char *zGeom,
- int (*xGeom)(sqlite3_rtree_geometry *, int, double *, int *),
- void *pContext
+ sqlite3 *db, /* Register SQL function on this connection */
+ const char *zGeom, /* Name of the new SQL function */
+ int (*xGeom)(sqlite3_rtree_geometry*,int,RtreeDValue*,int*), /* Callback */
+ void *pContext /* Extra data associated with the callback */
){
RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */
@@ -130286,17 +164832,44 @@ SQLITE_API int sqlite3_rtree_geometry_callback(
pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback));
if( !pGeomCtx ) return SQLITE_NOMEM;
pGeomCtx->xGeom = xGeom;
+ pGeomCtx->xQueryFunc = 0;
+ pGeomCtx->xDestructor = 0;
pGeomCtx->pContext = pContext;
-
- /* Create the new user-function. Register a destructor function to delete
- ** the context object when it is no longer required. */
return sqlite3_create_function_v2(db, zGeom, -1, SQLITE_ANY,
- (void *)pGeomCtx, geomCallback, 0, 0, doSqlite3Free
+ (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
+ );
+}
+
+/*
+** Register a new 2nd-generation geometry function for use with the
+** r-tree MATCH operator.
+*/
+SQLITE_API int sqlite3_rtree_query_callback(
+ sqlite3 *db, /* Register SQL function on this connection */
+ const char *zQueryFunc, /* Name of new SQL function */
+ int (*xQueryFunc)(sqlite3_rtree_query_info*), /* Callback */
+ void *pContext, /* Extra data passed into the callback */
+ void (*xDestructor)(void*) /* Destructor for the extra data */
+){
+ RtreeGeomCallback *pGeomCtx; /* Context object for new user-function */
+
+ /* Allocate and populate the context object. */
+ pGeomCtx = (RtreeGeomCallback *)sqlite3_malloc(sizeof(RtreeGeomCallback));
+ if( !pGeomCtx ) return SQLITE_NOMEM;
+ pGeomCtx->xGeom = 0;
+ pGeomCtx->xQueryFunc = xQueryFunc;
+ pGeomCtx->xDestructor = xDestructor;
+ pGeomCtx->pContext = pContext;
+ return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
+ (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
#if !SQLITE_CORE
-SQLITE_API int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_rtree_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
@@ -130334,7 +164907,7 @@ SQLITE_API int sqlite3_extension_init(
** * Implementations of the SQL scalar upper() and lower() functions
** for case mapping.
**
-** * Integration of ICU and SQLite collation seqences.
+** * Integration of ICU and SQLite collation sequences.
**
** * An implementation of the LIKE operator that uses ICU to
** provide case-independent matching.
@@ -130351,8 +164924,10 @@ SQLITE_API int sqlite3_extension_init(
/* #include <assert.h> */
#ifndef SQLITE_CORE
+/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#else
+/* #include "sqlite3.h" */
#endif
/*
@@ -130371,6 +164946,38 @@ static void xFree(void *p){
}
/*
+** This lookup table is used to help decode the first byte of
+** a multi-byte UTF8 character. It is copied here from SQLite source
+** code file utf8.c.
+*/
+static const unsigned char icuUtf8Trans1[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
+};
+
+#define SQLITE_ICU_READ_UTF8(zIn, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = icuUtf8Trans1[c-0xc0]; \
+ while( (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ }
+
+#define SQLITE_ICU_SKIP_UTF8(zIn) \
+ assert( *zIn ); \
+ if( *(zIn++)>=0xc0 ){ \
+ while( (*zIn & 0xc0)==0x80 ){zIn++;} \
+ }
+
+
+/*
** Compare two UTF-8 strings for equality where the first string is
** a "LIKE" expression. Return true (1) if they are the same and
** false (0) if they are different.
@@ -130383,17 +164990,14 @@ static int icuLikeCompare(
static const int MATCH_ONE = (UChar32)'_';
static const int MATCH_ALL = (UChar32)'%';
- int iPattern = 0; /* Current byte index in zPattern */
- int iString = 0; /* Current byte index in zString */
-
int prevEscape = 0; /* True if the previous character was uEsc */
- while( zPattern[iPattern]!=0 ){
+ while( 1 ){
/* Read (and consume) the next character from the input pattern. */
UChar32 uPattern;
- U8_NEXT_UNSAFE(zPattern, iPattern, uPattern);
- assert(uPattern!=0);
+ SQLITE_ICU_READ_UTF8(zPattern, uPattern);
+ if( uPattern==0 ) break;
/* There are now 4 possibilities:
**
@@ -130410,28 +165014,28 @@ static int icuLikeCompare(
** MATCH_ALL. For each MATCH_ONE, skip one character in the
** test string.
*/
- while( (c=zPattern[iPattern]) == MATCH_ALL || c == MATCH_ONE ){
+ while( (c=*zPattern) == MATCH_ALL || c == MATCH_ONE ){
if( c==MATCH_ONE ){
- if( zString[iString]==0 ) return 0;
- U8_FWD_1_UNSAFE(zString, iString);
+ if( *zString==0 ) return 0;
+ SQLITE_ICU_SKIP_UTF8(zString);
}
- iPattern++;
+ zPattern++;
}
- if( zPattern[iPattern]==0 ) return 1;
+ if( *zPattern==0 ) return 1;
- while( zString[iString] ){
- if( icuLikeCompare(&zPattern[iPattern], &zString[iString], uEsc) ){
+ while( *zString ){
+ if( icuLikeCompare(zPattern, zString, uEsc) ){
return 1;
}
- U8_FWD_1_UNSAFE(zString, iString);
+ SQLITE_ICU_SKIP_UTF8(zString);
}
return 0;
}else if( !prevEscape && uPattern==MATCH_ONE ){
/* Case 2. */
- if( zString[iString]==0 ) return 0;
- U8_FWD_1_UNSAFE(zString, iString);
+ if( *zString==0 ) return 0;
+ SQLITE_ICU_SKIP_UTF8(zString);
}else if( !prevEscape && uPattern==uEsc){
/* Case 3. */
@@ -130440,7 +165044,7 @@ static int icuLikeCompare(
}else{
/* Case 4. */
UChar32 uString;
- U8_NEXT_UNSAFE(zString, iString, uString);
+ SQLITE_ICU_READ_UTF8(zString, uString);
uString = u_foldCase(uString, U_FOLD_CASE_DEFAULT);
uPattern = u_foldCase(uPattern, U_FOLD_CASE_DEFAULT);
if( uString!=uPattern ){
@@ -130450,7 +165054,7 @@ static int icuLikeCompare(
}
}
- return zString[iString]==0;
+ return *zString==0;
}
/*
@@ -130630,20 +165234,22 @@ static void icuRegexpFunc(sqlite3_context *p, int nArg, sqlite3_value **apArg){
** of upper() or lower().
**
** lower('I', 'en_us') -> 'i'
-** lower('I', 'tr_tr') -> 'ı' (small dotless i)
+** lower('I', 'tr_tr') -> '\u131' (small dotless i)
**
** http://www.icu-project.org/userguide/posix.html#case_mappings
*/
static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
- const UChar *zInput;
- UChar *zOutput;
- int nInput;
- int nOutput;
-
- UErrorCode status = U_ZERO_ERROR;
+ const UChar *zInput; /* Pointer to input string */
+ UChar *zOutput = 0; /* Pointer to output buffer */
+ int nInput; /* Size of utf-16 input string in bytes */
+ int nOut; /* Size of output buffer in bytes */
+ int cnt;
+ int bToUpper; /* True for toupper(), false for tolower() */
+ UErrorCode status;
const char *zLocale = 0;
assert(nArg==1 || nArg==2);
+ bToUpper = (sqlite3_user_data(p)!=0);
if( nArg==2 ){
zLocale = (const char *)sqlite3_value_text(apArg[1]);
}
@@ -130652,26 +165258,38 @@ static void icuCaseFunc16(sqlite3_context *p, int nArg, sqlite3_value **apArg){
if( !zInput ){
return;
}
- nInput = sqlite3_value_bytes16(apArg[0]);
-
- nOutput = nInput * 2 + 2;
- zOutput = sqlite3_malloc(nOutput);
- if( !zOutput ){
+ nOut = nInput = sqlite3_value_bytes16(apArg[0]);
+ if( nOut==0 ){
+ sqlite3_result_text16(p, "", 0, SQLITE_STATIC);
return;
}
- if( sqlite3_user_data(p) ){
- u_strToUpper(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status);
- }else{
- u_strToLower(zOutput, nOutput/2, zInput, nInput/2, zLocale, &status);
- }
+ for(cnt=0; cnt<2; cnt++){
+ UChar *zNew = sqlite3_realloc(zOutput, nOut);
+ if( zNew==0 ){
+ sqlite3_free(zOutput);
+ sqlite3_result_error_nomem(p);
+ return;
+ }
+ zOutput = zNew;
+ status = U_ZERO_ERROR;
+ if( bToUpper ){
+ nOut = 2*u_strToUpper(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+ }else{
+ nOut = 2*u_strToLower(zOutput,nOut/2,zInput,nInput/2,zLocale,&status);
+ }
- if( !U_SUCCESS(status) ){
- icuFunctionError(p, "u_strToLower()/u_strToUpper", status);
+ if( U_SUCCESS(status) ){
+ sqlite3_result_text16(p, zOutput, nOut, xFree);
+ }else if( status==U_BUFFER_OVERFLOW_ERROR ){
+ assert( cnt==0 );
+ continue;
+ }else{
+ icuFunctionError(p, bToUpper ? "u_strToUpper" : "u_strToLower", status);
+ }
return;
}
-
- sqlite3_result_text16(p, zOutput, -1, xFree);
+ assert( 0 ); /* Unreachable */
}
/*
@@ -130732,6 +165350,7 @@ static void icuLoadCollation(
int rc; /* Return code from sqlite3_create_collation_x() */
assert(nArg==2);
+ (void)nArg; /* Unused parameter */
zLocale = (const char *)sqlite3_value_text(apArg[0]);
zName = (const char *)sqlite3_value_text(apArg[1]);
@@ -130798,7 +165417,10 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
}
#if !SQLITE_CORE
-SQLITE_API int sqlite3_extension_init(
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_icu_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
@@ -130825,11 +165447,13 @@ SQLITE_API int sqlite3_extension_init(
*************************************************************************
** This file implements a tokenizer for fts3 based on the ICU library.
*/
+/* #include "fts3Int.h" */
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
#ifdef SQLITE_ENABLE_ICU
/* #include <assert.h> */
/* #include <string.h> */
+/* #include "fts3_tokenizer.h" */
#include <unicode/ubrk.h>
/* #include <unicode/ucol.h> */
@@ -130922,13 +165546,16 @@ static int icuOpen(
*ppCursor = 0;
- if( nInput<0 ){
+ if( zInput==0 ){
+ nInput = 0;
+ zInput = "";
+ }else if( nInput<0 ){
nInput = strlen(zInput);
}
nChar = nInput+1;
pCsr = (IcuCursor *)sqlite3_malloc(
sizeof(IcuCursor) + /* IcuCursor */
- nChar * sizeof(UChar) + /* IcuCursor.aChar[] */
+ ((nChar+3)&~3) * sizeof(UChar) + /* IcuCursor.aChar[] */
(nChar+1) * sizeof(int) /* IcuCursor.aOffset[] */
);
if( !pCsr ){
@@ -130936,7 +165563,7 @@ static int icuOpen(
}
memset(pCsr, 0, sizeof(IcuCursor));
pCsr->aChar = (UChar *)&pCsr[1];
- pCsr->aOffset = (int *)&pCsr->aChar[nChar];
+ pCsr->aOffset = (int *)&pCsr->aChar[(nChar+3)&~3];
pCsr->aOffset[iOut] = iInput;
U8_NEXT(zInput, iInput, nInput, c);
@@ -131008,7 +165635,7 @@ static int icuNext(
while( iStart<iEnd ){
int iWhite = iStart;
- U8_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
+ U16_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
if( u_isspace(c) ){
iStart = iWhite;
}else{
@@ -131049,12 +165676,13 @@ static int icuNext(
** The set of routines that implement the simple tokenizer
*/
static const sqlite3_tokenizer_module icuTokenizerModule = {
- 0, /* iVersion */
- icuCreate, /* xCreate */
- icuDestroy, /* xCreate */
- icuOpen, /* xOpen */
- icuClose, /* xClose */
- icuNext, /* xNext */
+ 0, /* iVersion */
+ icuCreate, /* xCreate */
+ icuDestroy, /* xCreate */
+ icuOpen, /* xOpen */
+ icuClose, /* xClose */
+ icuNext, /* xNext */
+ 0, /* xLanguageid */
};
/*
@@ -131070,3 +165698,33787 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */
/************** End of fts3_icu.c ********************************************/
+/************** Begin file sqlite3rbu.c **************************************/
+/*
+** 2014 August 30
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+**
+** OVERVIEW
+**
+** The RBU extension requires that the RBU update be packaged as an
+** SQLite database. The tables it expects to find are described in
+** sqlite3rbu.h. Essentially, for each table xyz in the target database
+** that the user wishes to write to, a corresponding data_xyz table is
+** created in the RBU database and populated with one row for each row to
+** update, insert or delete from the target table.
+**
+** The update proceeds in three stages:
+**
+** 1) The database is updated. The modified database pages are written
+** to a *-oal file. A *-oal file is just like a *-wal file, except
+** that it is named "<database>-oal" instead of "<database>-wal".
+** Because regular SQLite clients do not look for file named
+** "<database>-oal", they go on using the original database in
+** rollback mode while the *-oal file is being generated.
+**
+** During this stage RBU does not update the database by writing
+** directly to the target tables. Instead it creates "imposter"
+** tables using the SQLITE_TESTCTRL_IMPOSTER interface that it uses
+** to update each b-tree individually. All updates required by each
+** b-tree are completed before moving on to the next, and all
+** updates are done in sorted key order.
+**
+** 2) The "<database>-oal" file is moved to the equivalent "<database>-wal"
+** location using a call to rename(2). Before doing this the RBU
+** module takes an EXCLUSIVE lock on the database file, ensuring
+** that there are no other active readers.
+**
+** Once the EXCLUSIVE lock is released, any other database readers
+** detect the new *-wal file and read the database in wal mode. At
+** this point they see the new version of the database - including
+** the updates made as part of the RBU update.
+**
+** 3) The new *-wal file is checkpointed. This proceeds in the same way
+** as a regular database checkpoint, except that a single frame is
+** checkpointed each time sqlite3rbu_step() is called. If the RBU
+** handle is closed before the entire *-wal file is checkpointed,
+** the checkpoint progress is saved in the RBU database and the
+** checkpoint can be resumed by another RBU client at some point in
+** the future.
+**
+** POTENTIAL PROBLEMS
+**
+** The rename() call might not be portable. And RBU is not currently
+** syncing the directory after renaming the file.
+**
+** When state is saved, any commit to the *-oal file and the commit to
+** the RBU update database are not atomic. So if the power fails at the
+** wrong moment they might get out of sync. As the main database will be
+** committed before the RBU update database this will likely either just
+** pass unnoticed, or result in SQLITE_CONSTRAINT errors (due to UNIQUE
+** constraint violations).
+**
+** If some client does modify the target database mid RBU update, or some
+** other error occurs, the RBU extension will keep throwing errors. It's
+** not really clear how to get out of this state. The system could just
+** by delete the RBU update database and *-oal file and have the device
+** download the update again and start over.
+**
+** At present, for an UPDATE, both the new.* and old.* records are
+** collected in the rbu_xyz table. And for both UPDATEs and DELETEs all
+** fields are collected. This means we're probably writing a lot more
+** data to disk when saving the state of an ongoing update to the RBU
+** update database than is strictly necessary.
+**
+*/
+
+/* #include <assert.h> */
+/* #include <string.h> */
+/* #include <stdio.h> */
+
+/* #include "sqlite3.h" */
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU)
+/************** Include sqlite3rbu.h in the middle of sqlite3rbu.c ***********/
+/************** Begin file sqlite3rbu.h **************************************/
+/*
+** 2014 August 30
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+**
+** This file contains the public interface for the RBU extension.
+*/
+
+/*
+** SUMMARY
+**
+** Writing a transaction containing a large number of operations on
+** b-tree indexes that are collectively larger than the available cache
+** memory can be very inefficient.
+**
+** The problem is that in order to update a b-tree, the leaf page (at least)
+** containing the entry being inserted or deleted must be modified. If the
+** working set of leaves is larger than the available cache memory, then a
+** single leaf that is modified more than once as part of the transaction
+** may be loaded from or written to the persistent media multiple times.
+** Additionally, because the index updates are likely to be applied in
+** random order, access to pages within the database is also likely to be in
+** random order, which is itself quite inefficient.
+**
+** One way to improve the situation is to sort the operations on each index
+** by index key before applying them to the b-tree. This leads to an IO
+** pattern that resembles a single linear scan through the index b-tree,
+** and all but guarantees each modified leaf page is loaded and stored
+** exactly once. SQLite uses this trick to improve the performance of
+** CREATE INDEX commands. This extension allows it to be used to improve
+** the performance of large transactions on existing databases.
+**
+** Additionally, this extension allows the work involved in writing the
+** large transaction to be broken down into sub-transactions performed
+** sequentially by separate processes. This is useful if the system cannot
+** guarantee that a single update process will run for long enough to apply
+** the entire update, for example because the update is being applied on a
+** mobile device that is frequently rebooted. Even after the writer process
+** has committed one or more sub-transactions, other database clients continue
+** to read from the original database snapshot. In other words, partially
+** applied transactions are not visible to other clients.
+**
+** "RBU" stands for "Resumable Bulk Update". As in a large database update
+** transmitted via a wireless network to a mobile device. A transaction
+** applied using this extension is hence refered to as an "RBU update".
+**
+**
+** LIMITATIONS
+**
+** An "RBU update" transaction is subject to the following limitations:
+**
+** * The transaction must consist of INSERT, UPDATE and DELETE operations
+** only.
+**
+** * INSERT statements may not use any default values.
+**
+** * UPDATE and DELETE statements must identify their target rows by
+** non-NULL PRIMARY KEY values. Rows with NULL values stored in PRIMARY
+** KEY fields may not be updated or deleted. If the table being written
+** has no PRIMARY KEY, affected rows must be identified by rowid.
+**
+** * UPDATE statements may not modify PRIMARY KEY columns.
+**
+** * No triggers will be fired.
+**
+** * No foreign key violations are detected or reported.
+**
+** * CHECK constraints are not enforced.
+**
+** * No constraint handling mode except for "OR ROLLBACK" is supported.
+**
+**
+** PREPARATION
+**
+** An "RBU update" is stored as a separate SQLite database. A database
+** containing an RBU update is an "RBU database". For each table in the
+** target database to be updated, the RBU database should contain a table
+** named "data_<target name>" containing the same set of columns as the
+** target table, and one more - "rbu_control". The data_% table should
+** have no PRIMARY KEY or UNIQUE constraints, but each column should have
+** the same type as the corresponding column in the target database.
+** The "rbu_control" column should have no type at all. For example, if
+** the target database contains:
+**
+** CREATE TABLE t1(a INTEGER PRIMARY KEY, b TEXT, c UNIQUE);
+**
+** Then the RBU database should contain:
+**
+** CREATE TABLE data_t1(a INTEGER, b TEXT, c, rbu_control);
+**
+** The order of the columns in the data_% table does not matter.
+**
+** Instead of a regular table, the RBU database may also contain virtual
+** tables or view named using the data_<target> naming scheme.
+**
+** Instead of the plain data_<target> naming scheme, RBU database tables
+** may also be named data<integer>_<target>, where <integer> is any sequence
+** of zero or more numeric characters (0-9). This can be significant because
+** tables within the RBU database are always processed in order sorted by
+** name. By judicious selection of the <integer> portion of the names
+** of the RBU tables the user can therefore control the order in which they
+** are processed. This can be useful, for example, to ensure that "external
+** content" FTS4 tables are updated before their underlying content tables.
+**
+** If the target database table is a virtual table or a table that has no
+** PRIMARY KEY declaration, the data_% table must also contain a column
+** named "rbu_rowid". This column is mapped to the tables implicit primary
+** key column - "rowid". Virtual tables for which the "rowid" column does
+** not function like a primary key value cannot be updated using RBU. For
+** example, if the target db contains either of the following:
+**
+** CREATE VIRTUAL TABLE x1 USING fts3(a, b);
+** CREATE TABLE x1(a, b)
+**
+** then the RBU database should contain:
+**
+** CREATE TABLE data_x1(a, b, rbu_rowid, rbu_control);
+**
+** All non-hidden columns (i.e. all columns matched by "SELECT *") of the
+** target table must be present in the input table. For virtual tables,
+** hidden columns are optional - they are updated by RBU if present in
+** the input table, or not otherwise. For example, to write to an fts4
+** table with a hidden languageid column such as:
+**
+** CREATE VIRTUAL TABLE ft1 USING fts4(a, b, languageid='langid');
+**
+** Either of the following input table schemas may be used:
+**
+** CREATE TABLE data_ft1(a, b, langid, rbu_rowid, rbu_control);
+** CREATE TABLE data_ft1(a, b, rbu_rowid, rbu_control);
+**
+** For each row to INSERT into the target database as part of the RBU
+** update, the corresponding data_% table should contain a single record
+** with the "rbu_control" column set to contain integer value 0. The
+** other columns should be set to the values that make up the new record
+** to insert.
+**
+** If the target database table has an INTEGER PRIMARY KEY, it is not
+** possible to insert a NULL value into the IPK column. Attempting to
+** do so results in an SQLITE_MISMATCH error.
+**
+** For each row to DELETE from the target database as part of the RBU
+** update, the corresponding data_% table should contain a single record
+** with the "rbu_control" column set to contain integer value 1. The
+** real primary key values of the row to delete should be stored in the
+** corresponding columns of the data_% table. The values stored in the
+** other columns are not used.
+**
+** For each row to UPDATE from the target database as part of the RBU
+** update, the corresponding data_% table should contain a single record
+** with the "rbu_control" column set to contain a value of type text.
+** The real primary key values identifying the row to update should be
+** stored in the corresponding columns of the data_% table row, as should
+** the new values of all columns being update. The text value in the
+** "rbu_control" column must contain the same number of characters as
+** there are columns in the target database table, and must consist entirely
+** of 'x' and '.' characters (or in some special cases 'd' - see below). For
+** each column that is being updated, the corresponding character is set to
+** 'x'. For those that remain as they are, the corresponding character of the
+** rbu_control value should be set to '.'. For example, given the tables
+** above, the update statement:
+**
+** UPDATE t1 SET c = 'usa' WHERE a = 4;
+**
+** is represented by the data_t1 row created by:
+**
+** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..x');
+**
+** Instead of an 'x' character, characters of the rbu_control value specified
+** for UPDATEs may also be set to 'd'. In this case, instead of updating the
+** target table with the value stored in the corresponding data_% column, the
+** user-defined SQL function "rbu_delta()" is invoked and the result stored in
+** the target table column. rbu_delta() is invoked with two arguments - the
+** original value currently stored in the target table column and the
+** value specified in the data_xxx table.
+**
+** For example, this row:
+**
+** INSERT INTO data_t1(a, b, c, rbu_control) VALUES(4, NULL, 'usa', '..d');
+**
+** is similar to an UPDATE statement such as:
+**
+** UPDATE t1 SET c = rbu_delta(c, 'usa') WHERE a = 4;
+**
+** Finally, if an 'f' character appears in place of a 'd' or 's' in an
+** ota_control string, the contents of the data_xxx table column is assumed
+** to be a "fossil delta" - a patch to be applied to a blob value in the
+** format used by the fossil source-code management system. In this case
+** the existing value within the target database table must be of type BLOB.
+** It is replaced by the result of applying the specified fossil delta to
+** itself.
+**
+** If the target database table is a virtual table or a table with no PRIMARY
+** KEY, the rbu_control value should not include a character corresponding
+** to the rbu_rowid value. For example, this:
+**
+** INSERT INTO data_ft1(a, b, rbu_rowid, rbu_control)
+** VALUES(NULL, 'usa', 12, '.x');
+**
+** causes a result similar to:
+**
+** UPDATE ft1 SET b = 'usa' WHERE rowid = 12;
+**
+** The data_xxx tables themselves should have no PRIMARY KEY declarations.
+** However, RBU is more efficient if reading the rows in from each data_xxx
+** table in "rowid" order is roughly the same as reading them sorted by
+** the PRIMARY KEY of the corresponding target database table. In other
+** words, rows should be sorted using the destination table PRIMARY KEY
+** fields before they are inserted into the data_xxx tables.
+**
+** USAGE
+**
+** The API declared below allows an application to apply an RBU update
+** stored on disk to an existing target database. Essentially, the
+** application:
+**
+** 1) Opens an RBU handle using the sqlite3rbu_open() function.
+**
+** 2) Registers any required virtual table modules with the database
+** handle returned by sqlite3rbu_db(). Also, if required, register
+** the rbu_delta() implementation.
+**
+** 3) Calls the sqlite3rbu_step() function one or more times on
+** the new handle. Each call to sqlite3rbu_step() performs a single
+** b-tree operation, so thousands of calls may be required to apply
+** a complete update.
+**
+** 4) Calls sqlite3rbu_close() to close the RBU update handle. If
+** sqlite3rbu_step() has been called enough times to completely
+** apply the update to the target database, then the RBU database
+** is marked as fully applied. Otherwise, the state of the RBU
+** update application is saved in the RBU database for later
+** resumption.
+**
+** See comments below for more detail on APIs.
+**
+** If an update is only partially applied to the target database by the
+** time sqlite3rbu_close() is called, various state information is saved
+** within the RBU database. This allows subsequent processes to automatically
+** resume the RBU update from where it left off.
+**
+** To remove all RBU extension state information, returning an RBU database
+** to its original contents, it is sufficient to drop all tables that begin
+** with the prefix "rbu_"
+**
+** DATABASE LOCKING
+**
+** An RBU update may not be applied to a database in WAL mode. Attempting
+** to do so is an error (SQLITE_ERROR).
+**
+** While an RBU handle is open, a SHARED lock may be held on the target
+** database file. This means it is possible for other clients to read the
+** database, but not to write it.
+**
+** If an RBU update is started and then suspended before it is completed,
+** then an external client writes to the database, then attempting to resume
+** the suspended RBU update is also an error (SQLITE_BUSY).
+*/
+
+#ifndef _SQLITE3RBU_H
+#define _SQLITE3RBU_H
+
+/* #include "sqlite3.h" ** Required for error code definitions ** */
+
+#if 0
+extern "C" {
+#endif
+
+typedef struct sqlite3rbu sqlite3rbu;
+
+/*
+** Open an RBU handle.
+**
+** Argument zTarget is the path to the target database. Argument zRbu is
+** the path to the RBU database. Each call to this function must be matched
+** by a call to sqlite3rbu_close(). When opening the databases, RBU passes
+** the SQLITE_CONFIG_URI flag to sqlite3_open_v2(). So if either zTarget
+** or zRbu begin with "file:", it will be interpreted as an SQLite
+** database URI, not a regular file name.
+**
+** If the zState argument is passed a NULL value, the RBU extension stores
+** the current state of the update (how many rows have been updated, which
+** indexes are yet to be updated etc.) within the RBU database itself. This
+** can be convenient, as it means that the RBU application does not need to
+** organize removing a separate state file after the update is concluded.
+** Or, if zState is non-NULL, it must be a path to a database file in which
+** the RBU extension can store the state of the update.
+**
+** When resuming an RBU update, the zState argument must be passed the same
+** value as when the RBU update was started.
+**
+** Once the RBU update is finished, the RBU extension does not
+** automatically remove any zState database file, even if it created it.
+**
+** By default, RBU uses the default VFS to access the files on disk. To
+** use a VFS other than the default, an SQLite "file:" URI containing a
+** "vfs=..." option may be passed as the zTarget option.
+**
+** IMPORTANT NOTE FOR ZIPVFS USERS: The RBU extension works with all of
+** SQLite's built-in VFSs, including the multiplexor VFS. However it does
+** not work out of the box with zipvfs. Refer to the comment describing
+** the zipvfs_create_vfs() API below for details on using RBU with zipvfs.
+*/
+SQLITE_API sqlite3rbu *sqlite3rbu_open(
+ const char *zTarget,
+ const char *zRbu,
+ const char *zState
+);
+
+/*
+** Open an RBU handle to perform an RBU vacuum on database file zTarget.
+** An RBU vacuum is similar to SQLite's built-in VACUUM command, except
+** that it can be suspended and resumed like an RBU update.
+**
+** The second argument to this function identifies a database in which
+** to store the state of the RBU vacuum operation if it is suspended. The
+** first time sqlite3rbu_vacuum() is called, to start an RBU vacuum
+** operation, the state database should either not exist or be empty
+** (contain no tables). If an RBU vacuum is suspended by calling
+** sqlite3rbu_close() on the RBU handle before sqlite3rbu_step() has
+** returned SQLITE_DONE, the vacuum state is stored in the state database.
+** The vacuum can be resumed by calling this function to open a new RBU
+** handle specifying the same target and state databases.
+**
+** If the second argument passed to this function is NULL, then the
+** name of the state database is "<database>-vacuum", where <database>
+** is the name of the target database file. In this case, on UNIX, if the
+** state database is not already present in the file-system, it is created
+** with the same permissions as the target db is made.
+**
+** This function does not delete the state database after an RBU vacuum
+** is completed, even if it created it. However, if the call to
+** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
+** of the state tables within the state database are zeroed. This way,
+** the next call to sqlite3rbu_vacuum() opens a handle that starts a
+** new RBU vacuum operation.
+**
+** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
+** describing the sqlite3rbu_create_vfs() API function below for
+** a description of the complications associated with using RBU with
+** zipvfs databases.
+*/
+SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
+ const char *zTarget,
+ const char *zState
+);
+
+/*
+** Internally, each RBU connection uses a separate SQLite database
+** connection to access the target and rbu update databases. This
+** API allows the application direct access to these database handles.
+**
+** The first argument passed to this function must be a valid, open, RBU
+** handle. The second argument should be passed zero to access the target
+** database handle, or non-zero to access the rbu update database handle.
+** Accessing the underlying database handles may be useful in the
+** following scenarios:
+**
+** * If any target tables are virtual tables, it may be necessary to
+** call sqlite3_create_module() on the target database handle to
+** register the required virtual table implementations.
+**
+** * If the data_xxx tables in the RBU source database are virtual
+** tables, the application may need to call sqlite3_create_module() on
+** the rbu update db handle to any required virtual table
+** implementations.
+**
+** * If the application uses the "rbu_delta()" feature described above,
+** it must use sqlite3_create_function() or similar to register the
+** rbu_delta() implementation with the target database handle.
+**
+** If an error has occurred, either while opening or stepping the RBU object,
+** this function may return NULL. The error code and message may be collected
+** when sqlite3rbu_close() is called.
+**
+** Database handles returned by this function remain valid until the next
+** call to any sqlite3rbu_xxx() function other than sqlite3rbu_db().
+*/
+SQLITE_API sqlite3 *sqlite3rbu_db(sqlite3rbu*, int bRbu);
+
+/*
+** Do some work towards applying the RBU update to the target db.
+**
+** Return SQLITE_DONE if the update has been completely applied, or
+** SQLITE_OK if no error occurs but there remains work to do to apply
+** the RBU update. If an error does occur, some other error code is
+** returned.
+**
+** Once a call to sqlite3rbu_step() has returned a value other than
+** SQLITE_OK, all subsequent calls on the same RBU handle are no-ops
+** that immediately return the same value.
+*/
+SQLITE_API int sqlite3rbu_step(sqlite3rbu *pRbu);
+
+/*
+** Force RBU to save its state to disk.
+**
+** If a power failure or application crash occurs during an update, following
+** system recovery RBU may resume the update from the point at which the state
+** was last saved. In other words, from the most recent successful call to
+** sqlite3rbu_close() or this function.
+**
+** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
+*/
+SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);
+
+/*
+** Close an RBU handle.
+**
+** If the RBU update has been completely applied, mark the RBU database
+** as fully applied. Otherwise, assuming no error has occurred, save the
+** current state of the RBU update appliation to the RBU database.
+**
+** If an error has already occurred as part of an sqlite3rbu_step()
+** or sqlite3rbu_open() call, or if one occurs within this function, an
+** SQLite error code is returned. Additionally, *pzErrmsg may be set to
+** point to a buffer containing a utf-8 formatted English language error
+** message. It is the responsibility of the caller to eventually free any
+** such buffer using sqlite3_free().
+**
+** Otherwise, if no error occurs, this function returns SQLITE_OK if the
+** update has been partially applied, or SQLITE_DONE if it has been
+** completely applied.
+*/
+SQLITE_API int sqlite3rbu_close(sqlite3rbu *pRbu, char **pzErrmsg);
+
+/*
+** Return the total number of key-value operations (inserts, deletes or
+** updates) that have been performed on the target database since the
+** current RBU update was started.
+*/
+SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu);
+
+/*
+** Obtain permyriadage (permyriadage is to 10000 as percentage is to 100)
+** progress indications for the two stages of an RBU update. This API may
+** be useful for driving GUI progress indicators and similar.
+**
+** An RBU update is divided into two stages:
+**
+** * Stage 1, in which changes are accumulated in an oal/wal file, and
+** * Stage 2, in which the contents of the wal file are copied into the
+** main database.
+**
+** The update is visible to non-RBU clients during stage 2. During stage 1
+** non-RBU reader clients may see the original database.
+**
+** If this API is called during stage 2 of the update, output variable
+** (*pnOne) is set to 10000 to indicate that stage 1 has finished and (*pnTwo)
+** to a value between 0 and 10000 to indicate the permyriadage progress of
+** stage 2. A value of 5000 indicates that stage 2 is half finished,
+** 9000 indicates that it is 90% finished, and so on.
+**
+** If this API is called during stage 1 of the update, output variable
+** (*pnTwo) is set to 0 to indicate that stage 2 has not yet started. The
+** value to which (*pnOne) is set depends on whether or not the RBU
+** database contains an "rbu_count" table. The rbu_count table, if it
+** exists, must contain the same columns as the following:
+**
+** CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
+**
+** There must be one row in the table for each source (data_xxx) table within
+** the RBU database. The 'tbl' column should contain the name of the source
+** table. The 'cnt' column should contain the number of rows within the
+** source table.
+**
+** If the rbu_count table is present and populated correctly and this
+** API is called during stage 1, the *pnOne output variable is set to the
+** permyriadage progress of the same stage. If the rbu_count table does
+** not exist, then (*pnOne) is set to -1 during stage 1. If the rbu_count
+** table exists but is not correctly populated, the value of the *pnOne
+** output variable during stage 1 is undefined.
+*/
+SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *pRbu, int *pnOne, int *pnTwo);
+
+/*
+** Obtain an indication as to the current stage of an RBU update or vacuum.
+** This function always returns one of the SQLITE_RBU_STATE_XXX constants
+** defined in this file. Return values should be interpreted as follows:
+**
+** SQLITE_RBU_STATE_OAL:
+** RBU is currently building a *-oal file. The next call to sqlite3rbu_step()
+** may either add further data to the *-oal file, or compute data that will
+** be added by a subsequent call.
+**
+** SQLITE_RBU_STATE_MOVE:
+** RBU has finished building the *-oal file. The next call to sqlite3rbu_step()
+** will move the *-oal file to the equivalent *-wal path. If the current
+** operation is an RBU update, then the updated version of the database
+** file will become visible to ordinary SQLite clients following the next
+** call to sqlite3rbu_step().
+**
+** SQLITE_RBU_STATE_CHECKPOINT:
+** RBU is currently performing an incremental checkpoint. The next call to
+** sqlite3rbu_step() will copy a page of data from the *-wal file into
+** the target database file.
+**
+** SQLITE_RBU_STATE_DONE:
+** The RBU operation has finished. Any subsequent calls to sqlite3rbu_step()
+** will immediately return SQLITE_DONE.
+**
+** SQLITE_RBU_STATE_ERROR:
+** An error has occurred. Any subsequent calls to sqlite3rbu_step() will
+** immediately return the SQLite error code associated with the error.
+*/
+#define SQLITE_RBU_STATE_OAL 1
+#define SQLITE_RBU_STATE_MOVE 2
+#define SQLITE_RBU_STATE_CHECKPOINT 3
+#define SQLITE_RBU_STATE_DONE 4
+#define SQLITE_RBU_STATE_ERROR 5
+
+SQLITE_API int sqlite3rbu_state(sqlite3rbu *pRbu);
+
+/*
+** Create an RBU VFS named zName that accesses the underlying file-system
+** via existing VFS zParent. Or, if the zParent parameter is passed NULL,
+** then the new RBU VFS uses the default system VFS to access the file-system.
+** The new object is registered as a non-default VFS with SQLite before
+** returning.
+**
+** Part of the RBU implementation uses a custom VFS object. Usually, this
+** object is created and deleted automatically by RBU.
+**
+** The exception is for applications that also use zipvfs. In this case,
+** the custom VFS must be explicitly created by the user before the RBU
+** handle is opened. The RBU VFS should be installed so that the zipvfs
+** VFS uses the RBU VFS, which in turn uses any other VFS layers in use
+** (for example multiplexor) to access the file-system. For example,
+** to assemble an RBU enabled VFS stack that uses both zipvfs and
+** multiplexor (error checking omitted):
+**
+** // Create a VFS named "multiplex" (not the default).
+** sqlite3_multiplex_initialize(0, 0);
+**
+** // Create an rbu VFS named "rbu" that uses multiplexor. If the
+** // second argument were replaced with NULL, the "rbu" VFS would
+** // access the file-system via the system default VFS, bypassing the
+** // multiplexor.
+** sqlite3rbu_create_vfs("rbu", "multiplex");
+**
+** // Create a zipvfs VFS named "zipvfs" that uses rbu.
+** zipvfs_create_vfs_v3("zipvfs", "rbu", 0, xCompressorAlgorithmDetector);
+**
+** // Make zipvfs the default VFS.
+** sqlite3_vfs_register(sqlite3_vfs_find("zipvfs"), 1);
+**
+** Because the default VFS created above includes a RBU functionality, it
+** may be used by RBU clients. Attempting to use RBU with a zipvfs VFS stack
+** that does not include the RBU layer results in an error.
+**
+** The overhead of adding the "rbu" VFS to the system is negligible for
+** non-RBU users. There is no harm in an application accessing the
+** file-system via "rbu" all the time, even if it only uses RBU functionality
+** occasionally.
+*/
+SQLITE_API int sqlite3rbu_create_vfs(const char *zName, const char *zParent);
+
+/*
+** Deregister and destroy an RBU vfs created by an earlier call to
+** sqlite3rbu_create_vfs().
+**
+** VFS objects are not reference counted. If a VFS object is destroyed
+** before all database handles that use it have been closed, the results
+** are undefined.
+*/
+SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName);
+
+#if 0
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _SQLITE3RBU_H */
+
+/************** End of sqlite3rbu.h ******************************************/
+/************** Continuing where we left off in sqlite3rbu.c *****************/
+
+#if defined(_WIN32_WCE)
+/* #include "windows.h" */
+#endif
+
+/* Maximum number of prepared UPDATE statements held by this module */
+#define SQLITE_RBU_UPDATE_CACHESIZE 16
+
+/*
+** Swap two objects of type TYPE.
+*/
+#if !defined(SQLITE_AMALGAMATION)
+# define SWAP(TYPE,A,B) {TYPE t=A; A=B; B=t;}
+#endif
+
+/*
+** The rbu_state table is used to save the state of a partially applied
+** update so that it can be resumed later. The table consists of integer
+** keys mapped to values as follows:
+**
+** RBU_STATE_STAGE:
+** May be set to integer values 1, 2, 4 or 5. As follows:
+** 1: the *-rbu file is currently under construction.
+** 2: the *-rbu file has been constructed, but not yet moved
+** to the *-wal path.
+** 4: the checkpoint is underway.
+** 5: the rbu update has been checkpointed.
+**
+** RBU_STATE_TBL:
+** Only valid if STAGE==1. The target database name of the table
+** currently being written.
+**
+** RBU_STATE_IDX:
+** Only valid if STAGE==1. The target database name of the index
+** currently being written, or NULL if the main table is currently being
+** updated.
+**
+** RBU_STATE_ROW:
+** Only valid if STAGE==1. Number of rows already processed for the current
+** table/index.
+**
+** RBU_STATE_PROGRESS:
+** Trbul number of sqlite3rbu_step() calls made so far as part of this
+** rbu update.
+**
+** RBU_STATE_CKPT:
+** Valid if STAGE==4. The 64-bit checksum associated with the wal-index
+** header created by recovering the *-wal file. This is used to detect
+** cases when another client appends frames to the *-wal file in the
+** middle of an incremental checkpoint (an incremental checkpoint cannot
+** be continued if this happens).
+**
+** RBU_STATE_COOKIE:
+** Valid if STAGE==1. The current change-counter cookie value in the
+** target db file.
+**
+** RBU_STATE_OALSZ:
+** Valid if STAGE==1. The size in bytes of the *-oal file.
+*/
+#define RBU_STATE_STAGE 1
+#define RBU_STATE_TBL 2
+#define RBU_STATE_IDX 3
+#define RBU_STATE_ROW 4
+#define RBU_STATE_PROGRESS 5
+#define RBU_STATE_CKPT 6
+#define RBU_STATE_COOKIE 7
+#define RBU_STATE_OALSZ 8
+#define RBU_STATE_PHASEONESTEP 9
+
+#define RBU_STAGE_OAL 1
+#define RBU_STAGE_MOVE 2
+#define RBU_STAGE_CAPTURE 3
+#define RBU_STAGE_CKPT 4
+#define RBU_STAGE_DONE 5
+
+
+#define RBU_CREATE_STATE \
+ "CREATE TABLE IF NOT EXISTS %s.rbu_state(k INTEGER PRIMARY KEY, v)"
+
+typedef struct RbuFrame RbuFrame;
+typedef struct RbuObjIter RbuObjIter;
+typedef struct RbuState RbuState;
+typedef struct rbu_vfs rbu_vfs;
+typedef struct rbu_file rbu_file;
+typedef struct RbuUpdateStmt RbuUpdateStmt;
+
+#if !defined(SQLITE_AMALGAMATION)
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+typedef sqlite3_int64 i64;
+#endif
+
+/*
+** These values must match the values defined in wal.c for the equivalent
+** locks. These are not magic numbers as they are part of the SQLite file
+** format.
+*/
+#define WAL_LOCK_WRITE 0
+#define WAL_LOCK_CKPT 1
+#define WAL_LOCK_READ0 3
+
+#define SQLITE_FCNTL_RBUCNT 5149216
+
+/*
+** A structure to store values read from the rbu_state table in memory.
+*/
+struct RbuState {
+ int eStage;
+ char *zTbl;
+ char *zIdx;
+ i64 iWalCksum;
+ int nRow;
+ i64 nProgress;
+ u32 iCookie;
+ i64 iOalSz;
+ i64 nPhaseOneStep;
+};
+
+struct RbuUpdateStmt {
+ char *zMask; /* Copy of update mask used with pUpdate */
+ sqlite3_stmt *pUpdate; /* Last update statement (or NULL) */
+ RbuUpdateStmt *pNext;
+};
+
+/*
+** An iterator of this type is used to iterate through all objects in
+** the target database that require updating. For each such table, the
+** iterator visits, in order:
+**
+** * the table itself,
+** * each index of the table (zero or more points to visit), and
+** * a special "cleanup table" state.
+**
+** abIndexed:
+** If the table has no indexes on it, abIndexed is set to NULL. Otherwise,
+** it points to an array of flags nTblCol elements in size. The flag is
+** set for each column that is either a part of the PK or a part of an
+** index. Or clear otherwise.
+**
+*/
+struct RbuObjIter {
+ sqlite3_stmt *pTblIter; /* Iterate through tables */
+ sqlite3_stmt *pIdxIter; /* Index iterator */
+ int nTblCol; /* Size of azTblCol[] array */
+ char **azTblCol; /* Array of unquoted target column names */
+ char **azTblType; /* Array of target column types */
+ int *aiSrcOrder; /* src table col -> target table col */
+ u8 *abTblPk; /* Array of flags, set on target PK columns */
+ u8 *abNotNull; /* Array of flags, set on NOT NULL columns */
+ u8 *abIndexed; /* Array of flags, set on indexed & PK cols */
+ int eType; /* Table type - an RBU_PK_XXX value */
+
+ /* Output variables. zTbl==0 implies EOF. */
+ int bCleanup; /* True in "cleanup" state */
+ const char *zTbl; /* Name of target db table */
+ const char *zDataTbl; /* Name of rbu db table (or null) */
+ const char *zIdx; /* Name of target db index (or null) */
+ int iTnum; /* Root page of current object */
+ int iPkTnum; /* If eType==EXTERNAL, root of PK index */
+ int bUnique; /* Current index is unique */
+ int nIndex; /* Number of aux. indexes on table zTbl */
+
+ /* Statements created by rbuObjIterPrepareAll() */
+ int nCol; /* Number of columns in current object */
+ sqlite3_stmt *pSelect; /* Source data */
+ sqlite3_stmt *pInsert; /* Statement for INSERT operations */
+ sqlite3_stmt *pDelete; /* Statement for DELETE ops */
+ sqlite3_stmt *pTmpInsert; /* Insert into rbu_tmp_$zDataTbl */
+
+ /* Last UPDATE used (for PK b-tree updates only), or NULL. */
+ RbuUpdateStmt *pRbuUpdate;
+};
+
+/*
+** Values for RbuObjIter.eType
+**
+** 0: Table does not exist (error)
+** 1: Table has an implicit rowid.
+** 2: Table has an explicit IPK column.
+** 3: Table has an external PK index.
+** 4: Table is WITHOUT ROWID.
+** 5: Table is a virtual table.
+*/
+#define RBU_PK_NOTABLE 0
+#define RBU_PK_NONE 1
+#define RBU_PK_IPK 2
+#define RBU_PK_EXTERNAL 3
+#define RBU_PK_WITHOUT_ROWID 4
+#define RBU_PK_VTAB 5
+
+
+/*
+** Within the RBU_STAGE_OAL stage, each call to sqlite3rbu_step() performs
+** one of the following operations.
+*/
+#define RBU_INSERT 1 /* Insert on a main table b-tree */
+#define RBU_DELETE 2 /* Delete a row from a main table b-tree */
+#define RBU_REPLACE 3 /* Delete and then insert a row */
+#define RBU_IDX_DELETE 4 /* Delete a row from an aux. index b-tree */
+#define RBU_IDX_INSERT 5 /* Insert on an aux. index b-tree */
+
+#define RBU_UPDATE 6 /* Update a row in a main table b-tree */
+
+/*
+** A single step of an incremental checkpoint - frame iWalFrame of the wal
+** file should be copied to page iDbPage of the database file.
+*/
+struct RbuFrame {
+ u32 iDbPage;
+ u32 iWalFrame;
+};
+
+/*
+** RBU handle.
+**
+** nPhaseOneStep:
+** If the RBU database contains an rbu_count table, this value is set to
+** a running estimate of the number of b-tree operations required to
+** finish populating the *-oal file. This allows the sqlite3_bp_progress()
+** API to calculate the permyriadage progress of populating the *-oal file
+** using the formula:
+**
+** permyriadage = (10000 * nProgress) / nPhaseOneStep
+**
+** nPhaseOneStep is initialized to the sum of:
+**
+** nRow * (nIndex + 1)
+**
+** for all source tables in the RBU database, where nRow is the number
+** of rows in the source table and nIndex the number of indexes on the
+** corresponding target database table.
+**
+** This estimate is accurate if the RBU update consists entirely of
+** INSERT operations. However, it is inaccurate if:
+**
+** * the RBU update contains any UPDATE operations. If the PK specified
+** for an UPDATE operation does not exist in the target table, then
+** no b-tree operations are required on index b-trees. Or if the
+** specified PK does exist, then (nIndex*2) such operations are
+** required (one delete and one insert on each index b-tree).
+**
+** * the RBU update contains any DELETE operations for which the specified
+** PK does not exist. In this case no operations are required on index
+** b-trees.
+**
+** * the RBU update contains REPLACE operations. These are similar to
+** UPDATE operations.
+**
+** nPhaseOneStep is updated to account for the conditions above during the
+** first pass of each source table. The updated nPhaseOneStep value is
+** stored in the rbu_state table if the RBU update is suspended.
+*/
+struct sqlite3rbu {
+ int eStage; /* Value of RBU_STATE_STAGE field */
+ sqlite3 *dbMain; /* target database handle */
+ sqlite3 *dbRbu; /* rbu database handle */
+ char *zTarget; /* Path to target db */
+ char *zRbu; /* Path to rbu db */
+ char *zState; /* Path to state db (or NULL if zRbu) */
+ char zStateDb[5]; /* Db name for state ("stat" or "main") */
+ int rc; /* Value returned by last rbu_step() call */
+ char *zErrmsg; /* Error message if rc!=SQLITE_OK */
+ int nStep; /* Rows processed for current object */
+ int nProgress; /* Rows processed for all objects */
+ RbuObjIter objiter; /* Iterator for skipping through tbl/idx */
+ const char *zVfsName; /* Name of automatically created rbu vfs */
+ rbu_file *pTargetFd; /* File handle open on target db */
+ i64 iOalSz;
+ i64 nPhaseOneStep;
+
+ /* The following state variables are used as part of the incremental
+ ** checkpoint stage (eStage==RBU_STAGE_CKPT). See comments surrounding
+ ** function rbuSetupCheckpoint() for details. */
+ u32 iMaxFrame; /* Largest iWalFrame value in aFrame[] */
+ u32 mLock;
+ int nFrame; /* Entries in aFrame[] array */
+ int nFrameAlloc; /* Allocated size of aFrame[] array */
+ RbuFrame *aFrame;
+ int pgsz;
+ u8 *aBuf;
+ i64 iWalCksum;
+
+ /* Used in RBU vacuum mode only */
+ int nRbu; /* Number of RBU VFS in the stack */
+ rbu_file *pRbuFd; /* Fd for main db of dbRbu */
+};
+
+/*
+** An rbu VFS is implemented using an instance of this structure.
+*/
+struct rbu_vfs {
+ sqlite3_vfs base; /* rbu VFS shim methods */
+ sqlite3_vfs *pRealVfs; /* Underlying VFS */
+ sqlite3_mutex *mutex; /* Mutex to protect pMain */
+ rbu_file *pMain; /* Linked list of main db files */
+};
+
+/*
+** Each file opened by an rbu VFS is represented by an instance of
+** the following structure.
+*/
+struct rbu_file {
+ sqlite3_file base; /* sqlite3_file methods */
+ sqlite3_file *pReal; /* Underlying file handle */
+ rbu_vfs *pRbuVfs; /* Pointer to the rbu_vfs object */
+ sqlite3rbu *pRbu; /* Pointer to rbu object (rbu target only) */
+
+ int openFlags; /* Flags this file was opened with */
+ u32 iCookie; /* Cookie value for main db files */
+ u8 iWriteVer; /* "write-version" value for main db files */
+ u8 bNolock; /* True to fail EXCLUSIVE locks */
+
+ int nShm; /* Number of entries in apShm[] array */
+ char **apShm; /* Array of mmap'd *-shm regions */
+ char *zDel; /* Delete this when closing file */
+
+ const char *zWal; /* Wal filename for this main db file */
+ rbu_file *pWalFd; /* Wal file descriptor for this main db */
+ rbu_file *pMainNext; /* Next MAIN_DB file */
+};
+
+/*
+** True for an RBU vacuum handle, or false otherwise.
+*/
+#define rbuIsVacuum(p) ((p)->zTarget==0)
+
+
+/*************************************************************************
+** The following three functions, found below:
+**
+** rbuDeltaGetInt()
+** rbuDeltaChecksum()
+** rbuDeltaApply()
+**
+** are lifted from the fossil source code (http://fossil-scm.org). They
+** are used to implement the scalar SQL function rbu_fossil_delta().
+*/
+
+/*
+** Read bytes from *pz and convert them into a positive integer. When
+** finished, leave *pz pointing to the first character past the end of
+** the integer. The *pLen parameter holds the length of the string
+** in *pz and is decremented once for each character in the integer.
+*/
+static unsigned int rbuDeltaGetInt(const char **pz, int *pLen){
+ static const signed char zValue[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
+ -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36,
+ -1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, 63, -1,
+ };
+ unsigned int v = 0;
+ int c;
+ unsigned char *z = (unsigned char*)*pz;
+ unsigned char *zStart = z;
+ while( (c = zValue[0x7f&*(z++)])>=0 ){
+ v = (v<<6) + c;
+ }
+ z--;
+ *pLen -= z - zStart;
+ *pz = (char*)z;
+ return v;
+}
+
+/*
+** Compute a 32-bit checksum on the N-byte buffer. Return the result.
+*/
+static unsigned int rbuDeltaChecksum(const char *zIn, size_t N){
+ const unsigned char *z = (const unsigned char *)zIn;
+ unsigned sum0 = 0;
+ unsigned sum1 = 0;
+ unsigned sum2 = 0;
+ unsigned sum3 = 0;
+ while(N >= 16){
+ sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
+ sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
+ sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
+ sum3 += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
+ z += 16;
+ N -= 16;
+ }
+ while(N >= 4){
+ sum0 += z[0];
+ sum1 += z[1];
+ sum2 += z[2];
+ sum3 += z[3];
+ z += 4;
+ N -= 4;
+ }
+ sum3 += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
+ switch(N){
+ case 3: sum3 += (z[2] << 8);
+ case 2: sum3 += (z[1] << 16);
+ case 1: sum3 += (z[0] << 24);
+ default: ;
+ }
+ return sum3;
+}
+
+/*
+** Apply a delta.
+**
+** The output buffer should be big enough to hold the whole output
+** file and a NUL terminator at the end. The delta_output_size()
+** routine will determine this size for you.
+**
+** The delta string should be null-terminated. But the delta string
+** may contain embedded NUL characters (if the input and output are
+** binary files) so we also have to pass in the length of the delta in
+** the lenDelta parameter.
+**
+** This function returns the size of the output file in bytes (excluding
+** the final NUL terminator character). Except, if the delta string is
+** malformed or intended for use with a source file other than zSrc,
+** then this routine returns -1.
+**
+** Refer to the delta_create() documentation above for a description
+** of the delta file format.
+*/
+static int rbuDeltaApply(
+ const char *zSrc, /* The source or pattern file */
+ int lenSrc, /* Length of the source file */
+ const char *zDelta, /* Delta to apply to the pattern */
+ int lenDelta, /* Length of the delta */
+ char *zOut /* Write the output into this preallocated buffer */
+){
+ unsigned int limit;
+ unsigned int total = 0;
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
+ char *zOrigOut = zOut;
+#endif
+
+ limit = rbuDeltaGetInt(&zDelta, &lenDelta);
+ if( *zDelta!='\n' ){
+ /* ERROR: size integer not terminated by "\n" */
+ return -1;
+ }
+ zDelta++; lenDelta--;
+ while( *zDelta && lenDelta>0 ){
+ unsigned int cnt, ofst;
+ cnt = rbuDeltaGetInt(&zDelta, &lenDelta);
+ switch( zDelta[0] ){
+ case '@': {
+ zDelta++; lenDelta--;
+ ofst = rbuDeltaGetInt(&zDelta, &lenDelta);
+ if( lenDelta>0 && zDelta[0]!=',' ){
+ /* ERROR: copy command not terminated by ',' */
+ return -1;
+ }
+ zDelta++; lenDelta--;
+ total += cnt;
+ if( total>limit ){
+ /* ERROR: copy exceeds output file size */
+ return -1;
+ }
+ if( (int)(ofst+cnt) > lenSrc ){
+ /* ERROR: copy extends past end of input */
+ return -1;
+ }
+ memcpy(zOut, &zSrc[ofst], cnt);
+ zOut += cnt;
+ break;
+ }
+ case ':': {
+ zDelta++; lenDelta--;
+ total += cnt;
+ if( total>limit ){
+ /* ERROR: insert command gives an output larger than predicted */
+ return -1;
+ }
+ if( (int)cnt>lenDelta ){
+ /* ERROR: insert count exceeds size of delta */
+ return -1;
+ }
+ memcpy(zOut, zDelta, cnt);
+ zOut += cnt;
+ zDelta += cnt;
+ lenDelta -= cnt;
+ break;
+ }
+ case ';': {
+ zDelta++; lenDelta--;
+ zOut[0] = 0;
+#ifndef FOSSIL_OMIT_DELTA_CKSUM_TEST
+ if( cnt!=rbuDeltaChecksum(zOrigOut, total) ){
+ /* ERROR: bad checksum */
+ return -1;
+ }
+#endif
+ if( total!=limit ){
+ /* ERROR: generated size does not match predicted size */
+ return -1;
+ }
+ return total;
+ }
+ default: {
+ /* ERROR: unknown delta operator */
+ return -1;
+ }
+ }
+ }
+ /* ERROR: unterminated delta */
+ return -1;
+}
+
+static int rbuDeltaOutputSize(const char *zDelta, int lenDelta){
+ int size;
+ size = rbuDeltaGetInt(&zDelta, &lenDelta);
+ if( *zDelta!='\n' ){
+ /* ERROR: size integer not terminated by "\n" */
+ return -1;
+ }
+ return size;
+}
+
+/*
+** End of code taken from fossil.
+*************************************************************************/
+
+/*
+** Implementation of SQL scalar function rbu_fossil_delta().
+**
+** This function applies a fossil delta patch to a blob. Exactly two
+** arguments must be passed to this function. The first is the blob to
+** patch and the second the patch to apply. If no error occurs, this
+** function returns the patched blob.
+*/
+static void rbuFossilDeltaFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ const char *aDelta;
+ int nDelta;
+ const char *aOrig;
+ int nOrig;
+
+ int nOut;
+ int nOut2;
+ char *aOut;
+
+ assert( argc==2 );
+
+ nOrig = sqlite3_value_bytes(argv[0]);
+ aOrig = (const char*)sqlite3_value_blob(argv[0]);
+ nDelta = sqlite3_value_bytes(argv[1]);
+ aDelta = (const char*)sqlite3_value_blob(argv[1]);
+
+ /* Figure out the size of the output */
+ nOut = rbuDeltaOutputSize(aDelta, nDelta);
+ if( nOut<0 ){
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
+ return;
+ }
+
+ aOut = sqlite3_malloc(nOut+1);
+ if( aOut==0 ){
+ sqlite3_result_error_nomem(context);
+ }else{
+ nOut2 = rbuDeltaApply(aOrig, nOrig, aDelta, nDelta, aOut);
+ if( nOut2!=nOut ){
+ sqlite3_result_error(context, "corrupt fossil delta", -1);
+ }else{
+ sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
+ }
+ }
+}
+
+
+/*
+** Prepare the SQL statement in buffer zSql against database handle db.
+** If successful, set *ppStmt to point to the new statement and return
+** SQLITE_OK.
+**
+** Otherwise, if an error does occur, set *ppStmt to NULL and return
+** an SQLite error code. Additionally, set output variable *pzErrmsg to
+** point to a buffer containing an error message. It is the responsibility
+** of the caller to (eventually) free this buffer using sqlite3_free().
+*/
+static int prepareAndCollectError(
+ sqlite3 *db,
+ sqlite3_stmt **ppStmt,
+ char **pzErrmsg,
+ const char *zSql
+){
+ int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
+ if( rc!=SQLITE_OK ){
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ *ppStmt = 0;
+ }
+ return rc;
+}
+
+/*
+** Reset the SQL statement passed as the first argument. Return a copy
+** of the value returned by sqlite3_reset().
+**
+** If an error has occurred, then set *pzErrmsg to point to a buffer
+** containing an error message. It is the responsibility of the caller
+** to eventually free this buffer using sqlite3_free().
+*/
+static int resetAndCollectError(sqlite3_stmt *pStmt, char **pzErrmsg){
+ int rc = sqlite3_reset(pStmt);
+ if( rc!=SQLITE_OK ){
+ *pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(sqlite3_db_handle(pStmt)));
+ }
+ return rc;
+}
+
+/*
+** Unless it is NULL, argument zSql points to a buffer allocated using
+** sqlite3_malloc containing an SQL statement. This function prepares the SQL
+** statement against database db and frees the buffer. If statement
+** compilation is successful, *ppStmt is set to point to the new statement
+** handle and SQLITE_OK is returned.
+**
+** Otherwise, if an error occurs, *ppStmt is set to NULL and an error code
+** returned. In this case, *pzErrmsg may also be set to point to an error
+** message. It is the responsibility of the caller to free this error message
+** buffer using sqlite3_free().
+**
+** If argument zSql is NULL, this function assumes that an OOM has occurred.
+** In this case SQLITE_NOMEM is returned and *ppStmt set to NULL.
+*/
+static int prepareFreeAndCollectError(
+ sqlite3 *db,
+ sqlite3_stmt **ppStmt,
+ char **pzErrmsg,
+ char *zSql
+){
+ int rc;
+ assert( *pzErrmsg==0 );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ *ppStmt = 0;
+ }else{
+ rc = prepareAndCollectError(db, ppStmt, pzErrmsg, zSql);
+ sqlite3_free(zSql);
+ }
+ return rc;
+}
+
+/*
+** Free the RbuObjIter.azTblCol[] and RbuObjIter.abTblPk[] arrays allocated
+** by an earlier call to rbuObjIterCacheTableInfo().
+*/
+static void rbuObjIterFreeCols(RbuObjIter *pIter){
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ sqlite3_free(pIter->azTblCol[i]);
+ sqlite3_free(pIter->azTblType[i]);
+ }
+ sqlite3_free(pIter->azTblCol);
+ pIter->azTblCol = 0;
+ pIter->azTblType = 0;
+ pIter->aiSrcOrder = 0;
+ pIter->abTblPk = 0;
+ pIter->abNotNull = 0;
+ pIter->nTblCol = 0;
+ pIter->eType = 0; /* Invalid value */
+}
+
+/*
+** Finalize all statements and free all allocations that are specific to
+** the current object (table/index pair).
+*/
+static void rbuObjIterClearStatements(RbuObjIter *pIter){
+ RbuUpdateStmt *pUp;
+
+ sqlite3_finalize(pIter->pSelect);
+ sqlite3_finalize(pIter->pInsert);
+ sqlite3_finalize(pIter->pDelete);
+ sqlite3_finalize(pIter->pTmpInsert);
+ pUp = pIter->pRbuUpdate;
+ while( pUp ){
+ RbuUpdateStmt *pTmp = pUp->pNext;
+ sqlite3_finalize(pUp->pUpdate);
+ sqlite3_free(pUp);
+ pUp = pTmp;
+ }
+
+ pIter->pSelect = 0;
+ pIter->pInsert = 0;
+ pIter->pDelete = 0;
+ pIter->pRbuUpdate = 0;
+ pIter->pTmpInsert = 0;
+ pIter->nCol = 0;
+}
+
+/*
+** Clean up any resources allocated as part of the iterator object passed
+** as the only argument.
+*/
+static void rbuObjIterFinalize(RbuObjIter *pIter){
+ rbuObjIterClearStatements(pIter);
+ sqlite3_finalize(pIter->pTblIter);
+ sqlite3_finalize(pIter->pIdxIter);
+ rbuObjIterFreeCols(pIter);
+ memset(pIter, 0, sizeof(RbuObjIter));
+}
+
+/*
+** Advance the iterator to the next position.
+**
+** If no error occurs, SQLITE_OK is returned and the iterator is left
+** pointing to the next entry. Otherwise, an error code and message is
+** left in the RBU handle passed as the first argument. A copy of the
+** error code is returned.
+*/
+static int rbuObjIterNext(sqlite3rbu *p, RbuObjIter *pIter){
+ int rc = p->rc;
+ if( rc==SQLITE_OK ){
+
+ /* Free any SQLite statements used while processing the previous object */
+ rbuObjIterClearStatements(pIter);
+ if( pIter->zIdx==0 ){
+ rc = sqlite3_exec(p->dbMain,
+ "DROP TRIGGER IF EXISTS temp.rbu_insert_tr;"
+ "DROP TRIGGER IF EXISTS temp.rbu_update1_tr;"
+ "DROP TRIGGER IF EXISTS temp.rbu_update2_tr;"
+ "DROP TRIGGER IF EXISTS temp.rbu_delete_tr;"
+ , 0, 0, &p->zErrmsg
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ if( pIter->bCleanup ){
+ rbuObjIterFreeCols(pIter);
+ pIter->bCleanup = 0;
+ rc = sqlite3_step(pIter->pTblIter);
+ if( rc!=SQLITE_ROW ){
+ rc = resetAndCollectError(pIter->pTblIter, &p->zErrmsg);
+ pIter->zTbl = 0;
+ }else{
+ pIter->zTbl = (const char*)sqlite3_column_text(pIter->pTblIter, 0);
+ pIter->zDataTbl = (const char*)sqlite3_column_text(pIter->pTblIter,1);
+ rc = (pIter->zDataTbl && pIter->zTbl) ? SQLITE_OK : SQLITE_NOMEM;
+ }
+ }else{
+ if( pIter->zIdx==0 ){
+ sqlite3_stmt *pIdx = pIter->pIdxIter;
+ rc = sqlite3_bind_text(pIdx, 1, pIter->zTbl, -1, SQLITE_STATIC);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_step(pIter->pIdxIter);
+ if( rc!=SQLITE_ROW ){
+ rc = resetAndCollectError(pIter->pIdxIter, &p->zErrmsg);
+ pIter->bCleanup = 1;
+ pIter->zIdx = 0;
+ }else{
+ pIter->zIdx = (const char*)sqlite3_column_text(pIter->pIdxIter, 0);
+ pIter->iTnum = sqlite3_column_int(pIter->pIdxIter, 1);
+ pIter->bUnique = sqlite3_column_int(pIter->pIdxIter, 2);
+ rc = pIter->zIdx ? SQLITE_OK : SQLITE_NOMEM;
+ }
+ }
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ rbuObjIterFinalize(pIter);
+ p->rc = rc;
+ }
+ return rc;
+}
+
+
+/*
+** The implementation of the rbu_target_name() SQL function. This function
+** accepts one or two arguments. The first argument is the name of a table -
+** the name of a table in the RBU database. The second, if it is present, is 1
+** for a view or 0 for a table.
+**
+** For a non-vacuum RBU handle, if the table name matches the pattern:
+**
+** data[0-9]_<name>
+**
+** where <name> is any sequence of 1 or more characters, <name> is returned.
+** Otherwise, if the only argument does not match the above pattern, an SQL
+** NULL is returned.
+**
+** "data_t1" -> "t1"
+** "data0123_t2" -> "t2"
+** "dataAB_t3" -> NULL
+**
+** For an rbu vacuum handle, a copy of the first argument is returned if
+** the second argument is either missing or 0 (not a view).
+*/
+static void rbuTargetNameFunc(
+ sqlite3_context *pCtx,
+ int argc,
+ sqlite3_value **argv
+){
+ sqlite3rbu *p = sqlite3_user_data(pCtx);
+ const char *zIn;
+ assert( argc==1 || argc==2 );
+
+ zIn = (const char*)sqlite3_value_text(argv[0]);
+ if( zIn ){
+ if( rbuIsVacuum(p) ){
+ if( argc==1 || 0==sqlite3_value_int(argv[1]) ){
+ sqlite3_result_text(pCtx, zIn, -1, SQLITE_STATIC);
+ }
+ }else{
+ if( strlen(zIn)>4 && memcmp("data", zIn, 4)==0 ){
+ int i;
+ for(i=4; zIn[i]>='0' && zIn[i]<='9'; i++);
+ if( zIn[i]=='_' && zIn[i+1] ){
+ sqlite3_result_text(pCtx, &zIn[i+1], -1, SQLITE_STATIC);
+ }
+ }
+ }
+ }
+}
+
+/*
+** Initialize the iterator structure passed as the second argument.
+**
+** If no error occurs, SQLITE_OK is returned and the iterator is left
+** pointing to the first entry. Otherwise, an error code and message is
+** left in the RBU handle passed as the first argument. A copy of the
+** error code is returned.
+*/
+static int rbuObjIterFirst(sqlite3rbu *p, RbuObjIter *pIter){
+ int rc;
+ memset(pIter, 0, sizeof(RbuObjIter));
+
+ rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pTblIter, &p->zErrmsg,
+ sqlite3_mprintf(
+ "SELECT rbu_target_name(name, type='view') AS target, name "
+ "FROM sqlite_master "
+ "WHERE type IN ('table', 'view') AND target IS NOT NULL "
+ " %s "
+ "ORDER BY name"
+ , rbuIsVacuum(p) ? "AND rootpage!=0 AND rootpage IS NOT NULL" : ""));
+
+ if( rc==SQLITE_OK ){
+ rc = prepareAndCollectError(p->dbMain, &pIter->pIdxIter, &p->zErrmsg,
+ "SELECT name, rootpage, sql IS NULL OR substr(8, 6)=='UNIQUE' "
+ " FROM main.sqlite_master "
+ " WHERE type='index' AND tbl_name = ?"
+ );
+ }
+
+ pIter->bCleanup = 1;
+ p->rc = rc;
+ return rbuObjIterNext(p, pIter);
+}
+
+/*
+** This is a wrapper around "sqlite3_mprintf(zFmt, ...)". If an OOM occurs,
+** an error code is stored in the RBU handle passed as the first argument.
+**
+** If an error has already occurred (p->rc is already set to something other
+** than SQLITE_OK), then this function returns NULL without modifying the
+** stored error code. In this case it still calls sqlite3_free() on any
+** printf() parameters associated with %z conversions.
+*/
+static char *rbuMPrintf(sqlite3rbu *p, const char *zFmt, ...){
+ char *zSql = 0;
+ va_list ap;
+ va_start(ap, zFmt);
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( p->rc==SQLITE_OK ){
+ if( zSql==0 ) p->rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_free(zSql);
+ zSql = 0;
+ }
+ va_end(ap);
+ return zSql;
+}
+
+/*
+** Argument zFmt is a sqlite3_mprintf() style format string. The trailing
+** arguments are the usual subsitution values. This function performs
+** the printf() style substitutions and executes the result as an SQL
+** statement on the RBU handles database.
+**
+** If an error occurs, an error code and error message is stored in the
+** RBU handle. If an error has already occurred when this function is
+** called, it is a no-op.
+*/
+static int rbuMPrintfExec(sqlite3rbu *p, sqlite3 *db, const char *zFmt, ...){
+ va_list ap;
+ char *zSql;
+ va_start(ap, zFmt);
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( p->rc==SQLITE_OK ){
+ if( zSql==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ p->rc = sqlite3_exec(db, zSql, 0, 0, &p->zErrmsg);
+ }
+ }
+ sqlite3_free(zSql);
+ va_end(ap);
+ return p->rc;
+}
+
+/*
+** Attempt to allocate and return a pointer to a zeroed block of nByte
+** bytes.
+**
+** If an error (i.e. an OOM condition) occurs, return NULL and leave an
+** error code in the rbu handle passed as the first argument. Or, if an
+** error has already occurred when this function is called, return NULL
+** immediately without attempting the allocation or modifying the stored
+** error code.
+*/
+static void *rbuMalloc(sqlite3rbu *p, int nByte){
+ void *pRet = 0;
+ if( p->rc==SQLITE_OK ){
+ assert( nByte>0 );
+ pRet = sqlite3_malloc64(nByte);
+ if( pRet==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ memset(pRet, 0, nByte);
+ }
+ }
+ return pRet;
+}
+
+
+/*
+** Allocate and zero the pIter->azTblCol[] and abTblPk[] arrays so that
+** there is room for at least nCol elements. If an OOM occurs, store an
+** error code in the RBU handle passed as the first argument.
+*/
+static void rbuAllocateIterArrays(sqlite3rbu *p, RbuObjIter *pIter, int nCol){
+ int nByte = (2*sizeof(char*) + sizeof(int) + 3*sizeof(u8)) * nCol;
+ char **azNew;
+
+ azNew = (char**)rbuMalloc(p, nByte);
+ if( azNew ){
+ pIter->azTblCol = azNew;
+ pIter->azTblType = &azNew[nCol];
+ pIter->aiSrcOrder = (int*)&pIter->azTblType[nCol];
+ pIter->abTblPk = (u8*)&pIter->aiSrcOrder[nCol];
+ pIter->abNotNull = (u8*)&pIter->abTblPk[nCol];
+ pIter->abIndexed = (u8*)&pIter->abNotNull[nCol];
+ }
+}
+
+/*
+** The first argument must be a nul-terminated string. This function
+** returns a copy of the string in memory obtained from sqlite3_malloc().
+** It is the responsibility of the caller to eventually free this memory
+** using sqlite3_free().
+**
+** If an OOM condition is encountered when attempting to allocate memory,
+** output variable (*pRc) is set to SQLITE_NOMEM before returning. Otherwise,
+** if the allocation succeeds, (*pRc) is left unchanged.
+*/
+static char *rbuStrndup(const char *zStr, int *pRc){
+ char *zRet = 0;
+
+ assert( *pRc==SQLITE_OK );
+ if( zStr ){
+ size_t nCopy = strlen(zStr) + 1;
+ zRet = (char*)sqlite3_malloc64(nCopy);
+ if( zRet ){
+ memcpy(zRet, zStr, nCopy);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+
+ return zRet;
+}
+
+/*
+** Finalize the statement passed as the second argument.
+**
+** If the sqlite3_finalize() call indicates that an error occurs, and the
+** rbu handle error code is not already set, set the error code and error
+** message accordingly.
+*/
+static void rbuFinalize(sqlite3rbu *p, sqlite3_stmt *pStmt){
+ sqlite3 *db = sqlite3_db_handle(pStmt);
+ int rc = sqlite3_finalize(pStmt);
+ if( p->rc==SQLITE_OK && rc!=SQLITE_OK ){
+ p->rc = rc;
+ p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ }
+}
+
+/* Determine the type of a table.
+**
+** peType is of type (int*), a pointer to an output parameter of type
+** (int). This call sets the output parameter as follows, depending
+** on the type of the table specified by parameters dbName and zTbl.
+**
+** RBU_PK_NOTABLE: No such table.
+** RBU_PK_NONE: Table has an implicit rowid.
+** RBU_PK_IPK: Table has an explicit IPK column.
+** RBU_PK_EXTERNAL: Table has an external PK index.
+** RBU_PK_WITHOUT_ROWID: Table is WITHOUT ROWID.
+** RBU_PK_VTAB: Table is a virtual table.
+**
+** Argument *piPk is also of type (int*), and also points to an output
+** parameter. Unless the table has an external primary key index
+** (i.e. unless *peType is set to 3), then *piPk is set to zero. Or,
+** if the table does have an external primary key index, then *piPk
+** is set to the root page number of the primary key index before
+** returning.
+**
+** ALGORITHM:
+**
+** if( no entry exists in sqlite_master ){
+** return RBU_PK_NOTABLE
+** }else if( sql for the entry starts with "CREATE VIRTUAL" ){
+** return RBU_PK_VTAB
+** }else if( "PRAGMA index_list()" for the table contains a "pk" index ){
+** if( the index that is the pk exists in sqlite_master ){
+** *piPK = rootpage of that index.
+** return RBU_PK_EXTERNAL
+** }else{
+** return RBU_PK_WITHOUT_ROWID
+** }
+** }else if( "PRAGMA table_info()" lists one or more "pk" columns ){
+** return RBU_PK_IPK
+** }else{
+** return RBU_PK_NONE
+** }
+*/
+static void rbuTableType(
+ sqlite3rbu *p,
+ const char *zTab,
+ int *peType,
+ int *piTnum,
+ int *piPk
+){
+ /*
+ ** 0) SELECT count(*) FROM sqlite_master where name=%Q AND IsVirtual(%Q)
+ ** 1) PRAGMA index_list = ?
+ ** 2) SELECT count(*) FROM sqlite_master where name=%Q
+ ** 3) PRAGMA table_info = ?
+ */
+ sqlite3_stmt *aStmt[4] = {0, 0, 0, 0};
+
+ *peType = RBU_PK_NOTABLE;
+ *piPk = 0;
+
+ assert( p->rc==SQLITE_OK );
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[0], &p->zErrmsg,
+ sqlite3_mprintf(
+ "SELECT (sql LIKE 'create virtual%%'), rootpage"
+ " FROM sqlite_master"
+ " WHERE name=%Q", zTab
+ ));
+ if( p->rc!=SQLITE_OK || sqlite3_step(aStmt[0])!=SQLITE_ROW ){
+ /* Either an error, or no such table. */
+ goto rbuTableType_end;
+ }
+ if( sqlite3_column_int(aStmt[0], 0) ){
+ *peType = RBU_PK_VTAB; /* virtual table */
+ goto rbuTableType_end;
+ }
+ *piTnum = sqlite3_column_int(aStmt[0], 1);
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[1], &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA index_list=%Q",zTab)
+ );
+ if( p->rc ) goto rbuTableType_end;
+ while( sqlite3_step(aStmt[1])==SQLITE_ROW ){
+ const u8 *zOrig = sqlite3_column_text(aStmt[1], 3);
+ const u8 *zIdx = sqlite3_column_text(aStmt[1], 1);
+ if( zOrig && zIdx && zOrig[0]=='p' ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[2], &p->zErrmsg,
+ sqlite3_mprintf(
+ "SELECT rootpage FROM sqlite_master WHERE name = %Q", zIdx
+ ));
+ if( p->rc==SQLITE_OK ){
+ if( sqlite3_step(aStmt[2])==SQLITE_ROW ){
+ *piPk = sqlite3_column_int(aStmt[2], 0);
+ *peType = RBU_PK_EXTERNAL;
+ }else{
+ *peType = RBU_PK_WITHOUT_ROWID;
+ }
+ }
+ goto rbuTableType_end;
+ }
+ }
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &aStmt[3], &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA table_info=%Q",zTab)
+ );
+ if( p->rc==SQLITE_OK ){
+ while( sqlite3_step(aStmt[3])==SQLITE_ROW ){
+ if( sqlite3_column_int(aStmt[3],5)>0 ){
+ *peType = RBU_PK_IPK; /* explicit IPK column */
+ goto rbuTableType_end;
+ }
+ }
+ *peType = RBU_PK_NONE;
+ }
+
+rbuTableType_end: {
+ unsigned int i;
+ for(i=0; i<sizeof(aStmt)/sizeof(aStmt[0]); i++){
+ rbuFinalize(p, aStmt[i]);
+ }
+ }
+}
+
+/*
+** This is a helper function for rbuObjIterCacheTableInfo(). It populates
+** the pIter->abIndexed[] array.
+*/
+static void rbuObjIterCacheIndexedCols(sqlite3rbu *p, RbuObjIter *pIter){
+ sqlite3_stmt *pList = 0;
+ int bIndex = 0;
+
+ if( p->rc==SQLITE_OK ){
+ memcpy(pIter->abIndexed, pIter->abTblPk, sizeof(u8)*pIter->nTblCol);
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pList, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_list = %Q", pIter->zTbl)
+ );
+ }
+
+ pIter->nIndex = 0;
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pList) ){
+ const char *zIdx = (const char*)sqlite3_column_text(pList, 1);
+ sqlite3_stmt *pXInfo = 0;
+ if( zIdx==0 ) break;
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
+ );
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ int iCid = sqlite3_column_int(pXInfo, 1);
+ if( iCid>=0 ) pIter->abIndexed[iCid] = 1;
+ }
+ rbuFinalize(p, pXInfo);
+ bIndex = 1;
+ pIter->nIndex++;
+ }
+
+ if( pIter->eType==RBU_PK_WITHOUT_ROWID ){
+ /* "PRAGMA index_list" includes the main PK b-tree */
+ pIter->nIndex--;
+ }
+
+ rbuFinalize(p, pList);
+ if( bIndex==0 ) pIter->abIndexed = 0;
+}
+
+
+/*
+** If they are not already populated, populate the pIter->azTblCol[],
+** pIter->abTblPk[], pIter->nTblCol and pIter->bRowid variables according to
+** the table (not index) that the iterator currently points to.
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise. If
+** an error does occur, an error code and error message are also left in
+** the RBU handle.
+*/
+static int rbuObjIterCacheTableInfo(sqlite3rbu *p, RbuObjIter *pIter){
+ if( pIter->azTblCol==0 ){
+ sqlite3_stmt *pStmt = 0;
+ int nCol = 0;
+ int i; /* for() loop iterator variable */
+ int bRbuRowid = 0; /* If input table has column "rbu_rowid" */
+ int iOrder = 0;
+ int iTnum = 0;
+
+ /* Figure out the type of table this step will deal with. */
+ assert( pIter->eType==0 );
+ rbuTableType(p, pIter->zTbl, &pIter->eType, &iTnum, &pIter->iPkTnum);
+ if( p->rc==SQLITE_OK && pIter->eType==RBU_PK_NOTABLE ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("no such table: %s", pIter->zTbl);
+ }
+ if( p->rc ) return p->rc;
+ if( pIter->zIdx==0 ) pIter->iTnum = iTnum;
+
+ assert( pIter->eType==RBU_PK_NONE || pIter->eType==RBU_PK_IPK
+ || pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_WITHOUT_ROWID
+ || pIter->eType==RBU_PK_VTAB
+ );
+
+ /* Populate the azTblCol[] and nTblCol variables based on the columns
+ ** of the input table. Ignore any input table columns that begin with
+ ** "rbu_". */
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("SELECT * FROM '%q'", pIter->zDataTbl)
+ );
+ if( p->rc==SQLITE_OK ){
+ nCol = sqlite3_column_count(pStmt);
+ rbuAllocateIterArrays(p, pIter, nCol);
+ }
+ for(i=0; p->rc==SQLITE_OK && i<nCol; i++){
+ const char *zName = (const char*)sqlite3_column_name(pStmt, i);
+ if( sqlite3_strnicmp("rbu_", zName, 4) ){
+ char *zCopy = rbuStrndup(zName, &p->rc);
+ pIter->aiSrcOrder[pIter->nTblCol] = pIter->nTblCol;
+ pIter->azTblCol[pIter->nTblCol++] = zCopy;
+ }
+ else if( 0==sqlite3_stricmp("rbu_rowid", zName) ){
+ bRbuRowid = 1;
+ }
+ }
+ sqlite3_finalize(pStmt);
+ pStmt = 0;
+
+ if( p->rc==SQLITE_OK
+ && rbuIsVacuum(p)==0
+ && bRbuRowid!=(pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
+ ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf(
+ "table %q %s rbu_rowid column", pIter->zDataTbl,
+ (bRbuRowid ? "may not have" : "requires")
+ );
+ }
+
+ /* Check that all non-HIDDEN columns in the destination table are also
+ ** present in the input table. Populate the abTblPk[], azTblType[] and
+ ** aiTblOrder[] arrays at the same time. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA table_info(%Q)", pIter->zTbl)
+ );
+ }
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
+ if( zName==0 ) break; /* An OOM - finalize() below returns S_NOMEM */
+ for(i=iOrder; i<pIter->nTblCol; i++){
+ if( 0==strcmp(zName, pIter->azTblCol[i]) ) break;
+ }
+ if( i==pIter->nTblCol ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("column missing from %q: %s",
+ pIter->zDataTbl, zName
+ );
+ }else{
+ int iPk = sqlite3_column_int(pStmt, 5);
+ int bNotNull = sqlite3_column_int(pStmt, 3);
+ const char *zType = (const char*)sqlite3_column_text(pStmt, 2);
+
+ if( i!=iOrder ){
+ SWAP(int, pIter->aiSrcOrder[i], pIter->aiSrcOrder[iOrder]);
+ SWAP(char*, pIter->azTblCol[i], pIter->azTblCol[iOrder]);
+ }
+
+ pIter->azTblType[iOrder] = rbuStrndup(zType, &p->rc);
+ pIter->abTblPk[iOrder] = (iPk!=0);
+ pIter->abNotNull[iOrder] = (u8)bNotNull || (iPk!=0);
+ iOrder++;
+ }
+ }
+
+ rbuFinalize(p, pStmt);
+ rbuObjIterCacheIndexedCols(p, pIter);
+ assert( pIter->eType!=RBU_PK_VTAB || pIter->abIndexed==0 );
+ assert( pIter->eType!=RBU_PK_VTAB || pIter->nIndex==0 );
+ }
+
+ return p->rc;
+}
+
+/*
+** This function constructs and returns a pointer to a nul-terminated
+** string containing some SQL clause or list based on one or more of the
+** column names currently stored in the pIter->azTblCol[] array.
+*/
+static char *rbuObjIterGetCollist(
+ sqlite3rbu *p, /* RBU object */
+ RbuObjIter *pIter /* Object iterator for column names */
+){
+ char *zList = 0;
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ const char *z = pIter->azTblCol[i];
+ zList = rbuMPrintf(p, "%z%s\"%w\"", zList, zSep, z);
+ zSep = ", ";
+ }
+ return zList;
+}
+
+/*
+** This function is used to create a SELECT list (the list of SQL
+** expressions that follows a SELECT keyword) for a SELECT statement
+** used to read from an data_xxx or rbu_tmp_xxx table while updating the
+** index object currently indicated by the iterator object passed as the
+** second argument. A "PRAGMA index_xinfo = <idxname>" statement is used
+** to obtain the required information.
+**
+** If the index is of the following form:
+**
+** CREATE INDEX i1 ON t1(c, b COLLATE nocase);
+**
+** and "t1" is a table with an explicit INTEGER PRIMARY KEY column
+** "ipk", the returned string is:
+**
+** "`c` COLLATE 'BINARY', `b` COLLATE 'NOCASE', `ipk` COLLATE 'BINARY'"
+**
+** As well as the returned string, three other malloc'd strings are
+** returned via output parameters. As follows:
+**
+** pzImposterCols: ...
+** pzImposterPk: ...
+** pzWhere: ...
+*/
+static char *rbuObjIterGetIndexCols(
+ sqlite3rbu *p, /* RBU object */
+ RbuObjIter *pIter, /* Object iterator for column names */
+ char **pzImposterCols, /* OUT: Columns for imposter table */
+ char **pzImposterPk, /* OUT: Imposter PK clause */
+ char **pzWhere, /* OUT: WHERE clause */
+ int *pnBind /* OUT: Trbul number of columns */
+){
+ int rc = p->rc; /* Error code */
+ int rc2; /* sqlite3_finalize() return code */
+ char *zRet = 0; /* String to return */
+ char *zImpCols = 0; /* String to return via *pzImposterCols */
+ char *zImpPK = 0; /* String to return via *pzImposterPK */
+ char *zWhere = 0; /* String to return via *pzWhere */
+ int nBind = 0; /* Value to return via *pnBind */
+ const char *zCom = ""; /* Set to ", " later on */
+ const char *zAnd = ""; /* Set to " AND " later on */
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = ? */
+
+ if( rc==SQLITE_OK ){
+ assert( p->zErrmsg==0 );
+ rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", pIter->zIdx)
+ );
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ int iCid = sqlite3_column_int(pXInfo, 1);
+ int bDesc = sqlite3_column_int(pXInfo, 3);
+ const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
+ const char *zCol;
+ const char *zType;
+
+ if( iCid<0 ){
+ /* An integer primary key. If the table has an explicit IPK, use
+ ** its name. Otherwise, use "rbu_rowid". */
+ if( pIter->eType==RBU_PK_IPK ){
+ int i;
+ for(i=0; pIter->abTblPk[i]==0; i++);
+ assert( i<pIter->nTblCol );
+ zCol = pIter->azTblCol[i];
+ }else if( rbuIsVacuum(p) ){
+ zCol = "_rowid_";
+ }else{
+ zCol = "rbu_rowid";
+ }
+ zType = "INTEGER";
+ }else{
+ zCol = pIter->azTblCol[iCid];
+ zType = pIter->azTblType[iCid];
+ }
+
+ zRet = sqlite3_mprintf("%z%s\"%w\" COLLATE %Q", zRet, zCom, zCol, zCollate);
+ if( pIter->bUnique==0 || sqlite3_column_int(pXInfo, 5) ){
+ const char *zOrder = (bDesc ? " DESC" : "");
+ zImpPK = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\"%s",
+ zImpPK, zCom, nBind, zCol, zOrder
+ );
+ }
+ zImpCols = sqlite3_mprintf("%z%s\"rbu_imp_%d%w\" %s COLLATE %Q",
+ zImpCols, zCom, nBind, zCol, zType, zCollate
+ );
+ zWhere = sqlite3_mprintf(
+ "%z%s\"rbu_imp_%d%w\" IS ?", zWhere, zAnd, nBind, zCol
+ );
+ if( zRet==0 || zImpPK==0 || zImpCols==0 || zWhere==0 ) rc = SQLITE_NOMEM;
+ zCom = ", ";
+ zAnd = " AND ";
+ nBind++;
+ }
+
+ rc2 = sqlite3_finalize(pXInfo);
+ if( rc==SQLITE_OK ) rc = rc2;
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(zRet);
+ sqlite3_free(zImpCols);
+ sqlite3_free(zImpPK);
+ sqlite3_free(zWhere);
+ zRet = 0;
+ zImpCols = 0;
+ zImpPK = 0;
+ zWhere = 0;
+ p->rc = rc;
+ }
+
+ *pzImposterCols = zImpCols;
+ *pzImposterPk = zImpPK;
+ *pzWhere = zWhere;
+ *pnBind = nBind;
+ return zRet;
+}
+
+/*
+** Assuming the current table columns are "a", "b" and "c", and the zObj
+** paramter is passed "old", return a string of the form:
+**
+** "old.a, old.b, old.b"
+**
+** With the column names escaped.
+**
+** For tables with implicit rowids - RBU_PK_EXTERNAL and RBU_PK_NONE, append
+** the text ", old._rowid_" to the returned value.
+*/
+static char *rbuObjIterGetOldlist(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ const char *zObj
+){
+ char *zList = 0;
+ if( p->rc==SQLITE_OK && pIter->abIndexed ){
+ const char *zS = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->abIndexed[i] ){
+ const char *zCol = pIter->azTblCol[i];
+ zList = sqlite3_mprintf("%z%s%s.\"%w\"", zList, zS, zObj, zCol);
+ }else{
+ zList = sqlite3_mprintf("%z%sNULL", zList, zS);
+ }
+ zS = ", ";
+ if( zList==0 ){
+ p->rc = SQLITE_NOMEM;
+ break;
+ }
+ }
+
+ /* For a table with implicit rowids, append "old._rowid_" to the list. */
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ zList = rbuMPrintf(p, "%z, %s._rowid_", zList, zObj);
+ }
+ }
+ return zList;
+}
+
+/*
+** Return an expression that can be used in a WHERE clause to match the
+** primary key of the current table. For example, if the table is:
+**
+** CREATE TABLE t1(a, b, c, PRIMARY KEY(b, c));
+**
+** Return the string:
+**
+** "b = ?1 AND c = ?2"
+*/
+static char *rbuObjIterGetWhere(
+ sqlite3rbu *p,
+ RbuObjIter *pIter
+){
+ char *zList = 0;
+ if( pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE ){
+ zList = rbuMPrintf(p, "_rowid_ = ?%d", pIter->nTblCol+1);
+ }else if( pIter->eType==RBU_PK_EXTERNAL ){
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->abTblPk[i] ){
+ zList = rbuMPrintf(p, "%z%sc%d=?%d", zList, zSep, i, i+1);
+ zSep = " AND ";
+ }
+ }
+ zList = rbuMPrintf(p,
+ "_rowid_ = (SELECT id FROM rbu_imposter2 WHERE %z)", zList
+ );
+
+ }else{
+ const char *zSep = "";
+ int i;
+ for(i=0; i<pIter->nTblCol; i++){
+ if( pIter->abTblPk[i] ){
+ const char *zCol = pIter->azTblCol[i];
+ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d", zList, zSep, zCol, i+1);
+ zSep = " AND ";
+ }
+ }
+ }
+ return zList;
+}
+
+/*
+** The SELECT statement iterating through the keys for the current object
+** (p->objiter.pSelect) currently points to a valid row. However, there
+** is something wrong with the rbu_control value in the rbu_control value
+** stored in the (p->nCol+1)'th column. Set the error code and error message
+** of the RBU handle to something reflecting this.
+*/
+static void rbuBadControlError(sqlite3rbu *p){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("invalid rbu_control value");
+}
+
+
+/*
+** Return a nul-terminated string containing the comma separated list of
+** assignments that should be included following the "SET" keyword of
+** an UPDATE statement used to update the table object that the iterator
+** passed as the second argument currently points to if the rbu_control
+** column of the data_xxx table entry is set to zMask.
+**
+** The memory for the returned string is obtained from sqlite3_malloc().
+** It is the responsibility of the caller to eventually free it using
+** sqlite3_free().
+**
+** If an OOM error is encountered when allocating space for the new
+** string, an error code is left in the rbu handle passed as the first
+** argument and NULL is returned. Or, if an error has already occurred
+** when this function is called, NULL is returned immediately, without
+** attempting the allocation or modifying the stored error code.
+*/
+static char *rbuObjIterGetSetlist(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ const char *zMask
+){
+ char *zList = 0;
+ if( p->rc==SQLITE_OK ){
+ int i;
+
+ if( (int)strlen(zMask)!=pIter->nTblCol ){
+ rbuBadControlError(p);
+ }else{
+ const char *zSep = "";
+ for(i=0; i<pIter->nTblCol; i++){
+ char c = zMask[pIter->aiSrcOrder[i]];
+ if( c=='x' ){
+ zList = rbuMPrintf(p, "%z%s\"%w\"=?%d",
+ zList, zSep, pIter->azTblCol[i], i+1
+ );
+ zSep = ", ";
+ }
+ else if( c=='d' ){
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_delta(\"%w\", ?%d)",
+ zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
+ );
+ zSep = ", ";
+ }
+ else if( c=='f' ){
+ zList = rbuMPrintf(p, "%z%s\"%w\"=rbu_fossil_delta(\"%w\", ?%d)",
+ zList, zSep, pIter->azTblCol[i], pIter->azTblCol[i], i+1
+ );
+ zSep = ", ";
+ }
+ }
+ }
+ }
+ return zList;
+}
+
+/*
+** Return a nul-terminated string consisting of nByte comma separated
+** "?" expressions. For example, if nByte is 3, return a pointer to
+** a buffer containing the string "?,?,?".
+**
+** The memory for the returned string is obtained from sqlite3_malloc().
+** It is the responsibility of the caller to eventually free it using
+** sqlite3_free().
+**
+** If an OOM error is encountered when allocating space for the new
+** string, an error code is left in the rbu handle passed as the first
+** argument and NULL is returned. Or, if an error has already occurred
+** when this function is called, NULL is returned immediately, without
+** attempting the allocation or modifying the stored error code.
+*/
+static char *rbuObjIterGetBindlist(sqlite3rbu *p, int nBind){
+ char *zRet = 0;
+ int nByte = nBind*2 + 1;
+
+ zRet = (char*)rbuMalloc(p, nByte);
+ if( zRet ){
+ int i;
+ for(i=0; i<nBind; i++){
+ zRet[i*2] = '?';
+ zRet[i*2+1] = (i+1==nBind) ? '\0' : ',';
+ }
+ }
+ return zRet;
+}
+
+/*
+** The iterator currently points to a table (not index) of type
+** RBU_PK_WITHOUT_ROWID. This function creates the PRIMARY KEY
+** declaration for the corresponding imposter table. For example,
+** if the iterator points to a table created as:
+**
+** CREATE TABLE t1(a, b, c, PRIMARY KEY(b, a DESC)) WITHOUT ROWID
+**
+** this function returns:
+**
+** PRIMARY KEY("b", "a" DESC)
+*/
+static char *rbuWithoutRowidPK(sqlite3rbu *p, RbuObjIter *pIter){
+ char *z = 0;
+ assert( pIter->zIdx==0 );
+ if( p->rc==SQLITE_OK ){
+ const char *zSep = "PRIMARY KEY(";
+ sqlite3_stmt *pXList = 0; /* PRAGMA index_list = (pIter->zTbl) */
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA index_xinfo = <pk-index> */
+
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXList, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_list = %Q", pIter->zTbl)
+ );
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXList) ){
+ const char *zOrig = (const char*)sqlite3_column_text(pXList,3);
+ if( zOrig && strcmp(zOrig, "pk")==0 ){
+ const char *zIdx = (const char*)sqlite3_column_text(pXList,1);
+ if( zIdx ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
+ );
+ }
+ break;
+ }
+ }
+ rbuFinalize(p, pXList);
+
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ if( sqlite3_column_int(pXInfo, 5) ){
+ /* int iCid = sqlite3_column_int(pXInfo, 0); */
+ const char *zCol = (const char*)sqlite3_column_text(pXInfo, 2);
+ const char *zDesc = sqlite3_column_int(pXInfo, 3) ? " DESC" : "";
+ z = rbuMPrintf(p, "%z%s\"%w\"%s", z, zSep, zCol, zDesc);
+ zSep = ", ";
+ }
+ }
+ z = rbuMPrintf(p, "%z)", z);
+ rbuFinalize(p, pXInfo);
+ }
+ return z;
+}
+
+/*
+** This function creates the second imposter table used when writing to
+** a table b-tree where the table has an external primary key. If the
+** iterator passed as the second argument does not currently point to
+** a table (not index) with an external primary key, this function is a
+** no-op.
+**
+** Assuming the iterator does point to a table with an external PK, this
+** function creates a WITHOUT ROWID imposter table named "rbu_imposter2"
+** used to access that PK index. For example, if the target table is
+** declared as follows:
+**
+** CREATE TABLE t1(a, b TEXT, c REAL, PRIMARY KEY(b, c));
+**
+** then the imposter table schema is:
+**
+** CREATE TABLE rbu_imposter2(c1 TEXT, c2 REAL, id INTEGER) WITHOUT ROWID;
+**
+*/
+static void rbuCreateImposterTable2(sqlite3rbu *p, RbuObjIter *pIter){
+ if( p->rc==SQLITE_OK && pIter->eType==RBU_PK_EXTERNAL ){
+ int tnum = pIter->iPkTnum; /* Root page of PK index */
+ sqlite3_stmt *pQuery = 0; /* SELECT name ... WHERE rootpage = $tnum */
+ const char *zIdx = 0; /* Name of PK index */
+ sqlite3_stmt *pXInfo = 0; /* PRAGMA main.index_xinfo = $zIdx */
+ const char *zComma = "";
+ char *zCols = 0; /* Used to build up list of table cols */
+ char *zPk = 0; /* Used to build up table PK declaration */
+
+ /* Figure out the name of the primary key index for the current table.
+ ** This is needed for the argument to "PRAGMA index_xinfo". Set
+ ** zIdx to point to a nul-terminated string containing this name. */
+ p->rc = prepareAndCollectError(p->dbMain, &pQuery, &p->zErrmsg,
+ "SELECT name FROM sqlite_master WHERE rootpage = ?"
+ );
+ if( p->rc==SQLITE_OK ){
+ sqlite3_bind_int(pQuery, 1, tnum);
+ if( SQLITE_ROW==sqlite3_step(pQuery) ){
+ zIdx = (const char*)sqlite3_column_text(pQuery, 0);
+ }
+ }
+ if( zIdx ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pXInfo, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.index_xinfo = %Q", zIdx)
+ );
+ }
+ rbuFinalize(p, pQuery);
+
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pXInfo) ){
+ int bKey = sqlite3_column_int(pXInfo, 5);
+ if( bKey ){
+ int iCid = sqlite3_column_int(pXInfo, 1);
+ int bDesc = sqlite3_column_int(pXInfo, 3);
+ const char *zCollate = (const char*)sqlite3_column_text(pXInfo, 4);
+ zCols = rbuMPrintf(p, "%z%sc%d %s COLLATE %s", zCols, zComma,
+ iCid, pIter->azTblType[iCid], zCollate
+ );
+ zPk = rbuMPrintf(p, "%z%sc%d%s", zPk, zComma, iCid, bDesc?" DESC":"");
+ zComma = ", ";
+ }
+ }
+ zCols = rbuMPrintf(p, "%z, id INTEGER", zCols);
+ rbuFinalize(p, pXInfo);
+
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TABLE rbu_imposter2(%z, PRIMARY KEY(%z)) WITHOUT ROWID",
+ zCols, zPk
+ );
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
+ }
+}
+
+/*
+** If an error has already occurred when this function is called, it
+** immediately returns zero (without doing any work). Or, if an error
+** occurs during the execution of this function, it sets the error code
+** in the sqlite3rbu object indicated by the first argument and returns
+** zero.
+**
+** The iterator passed as the second argument is guaranteed to point to
+** a table (not an index) when this function is called. This function
+** attempts to create any imposter table required to write to the main
+** table b-tree of the table before returning. Non-zero is returned if
+** an imposter table are created, or zero otherwise.
+**
+** An imposter table is required in all cases except RBU_PK_VTAB. Only
+** virtual tables are written to directly. The imposter table has the
+** same schema as the actual target table (less any UNIQUE constraints).
+** More precisely, the "same schema" means the same columns, types,
+** collation sequences. For tables that do not have an external PRIMARY
+** KEY, it also means the same PRIMARY KEY declaration.
+*/
+static void rbuCreateImposterTable(sqlite3rbu *p, RbuObjIter *pIter){
+ if( p->rc==SQLITE_OK && pIter->eType!=RBU_PK_VTAB ){
+ int tnum = pIter->iTnum;
+ const char *zComma = "";
+ char *zSql = 0;
+ int iCol;
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
+
+ for(iCol=0; p->rc==SQLITE_OK && iCol<pIter->nTblCol; iCol++){
+ const char *zPk = "";
+ const char *zCol = pIter->azTblCol[iCol];
+ const char *zColl = 0;
+
+ p->rc = sqlite3_table_column_metadata(
+ p->dbMain, "main", pIter->zTbl, zCol, 0, &zColl, 0, 0, 0
+ );
+
+ if( pIter->eType==RBU_PK_IPK && pIter->abTblPk[iCol] ){
+ /* If the target table column is an "INTEGER PRIMARY KEY", add
+ ** "PRIMARY KEY" to the imposter table column declaration. */
+ zPk = "PRIMARY KEY ";
+ }
+ zSql = rbuMPrintf(p, "%z%s\"%w\" %s %sCOLLATE %s%s",
+ zSql, zComma, zCol, pIter->azTblType[iCol], zPk, zColl,
+ (pIter->abNotNull[iCol] ? " NOT NULL" : "")
+ );
+ zComma = ", ";
+ }
+
+ if( pIter->eType==RBU_PK_WITHOUT_ROWID ){
+ char *zPk = rbuWithoutRowidPK(p, pIter);
+ if( zPk ){
+ zSql = rbuMPrintf(p, "%z, %z", zSql, zPk);
+ }
+ }
+
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1, tnum);
+ rbuMPrintfExec(p, p->dbMain, "CREATE TABLE \"rbu_imp_%w\"(%z)%s",
+ pIter->zTbl, zSql,
+ (pIter->eType==RBU_PK_WITHOUT_ROWID ? " WITHOUT ROWID" : "")
+ );
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
+ }
+}
+
+/*
+** Prepare a statement used to insert rows into the "rbu_tmp_xxx" table.
+** Specifically a statement of the form:
+**
+** INSERT INTO rbu_tmp_xxx VALUES(?, ?, ? ...);
+**
+** The number of bound variables is equal to the number of columns in
+** the target table, plus one (for the rbu_control column), plus one more
+** (for the rbu_rowid column) if the target table is an implicit IPK or
+** virtual table.
+*/
+static void rbuObjIterPrepareTmpInsert(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ const char *zCollist,
+ const char *zRbuRowid
+){
+ int bRbuRowid = (pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE);
+ char *zBind = rbuObjIterGetBindlist(p, pIter->nTblCol + 1 + bRbuRowid);
+ if( zBind ){
+ assert( pIter->pTmpInsert==0 );
+ p->rc = prepareFreeAndCollectError(
+ p->dbRbu, &pIter->pTmpInsert, &p->zErrmsg, sqlite3_mprintf(
+ "INSERT INTO %s.'rbu_tmp_%q'(rbu_control,%s%s) VALUES(%z)",
+ p->zStateDb, pIter->zDataTbl, zCollist, zRbuRowid, zBind
+ ));
+ }
+}
+
+static void rbuTmpInsertFunc(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ sqlite3rbu *p = sqlite3_user_data(pCtx);
+ int rc = SQLITE_OK;
+ int i;
+
+ assert( sqlite3_value_int(apVal[0])!=0
+ || p->objiter.eType==RBU_PK_EXTERNAL
+ || p->objiter.eType==RBU_PK_NONE
+ );
+ if( sqlite3_value_int(apVal[0])!=0 ){
+ p->nPhaseOneStep += p->objiter.nIndex;
+ }
+
+ for(i=0; rc==SQLITE_OK && i<nVal; i++){
+ rc = sqlite3_bind_value(p->objiter.pTmpInsert, i+1, apVal[i]);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(p->objiter.pTmpInsert);
+ rc = sqlite3_reset(p->objiter.pTmpInsert);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(pCtx, rc);
+ }
+}
+
+/*
+** Ensure that the SQLite statement handles required to update the
+** target database object currently indicated by the iterator passed
+** as the second argument are available.
+*/
+static int rbuObjIterPrepareAll(
+ sqlite3rbu *p,
+ RbuObjIter *pIter,
+ int nOffset /* Add "LIMIT -1 OFFSET $nOffset" to SELECT */
+){
+ assert( pIter->bCleanup==0 );
+ if( pIter->pSelect==0 && rbuObjIterCacheTableInfo(p, pIter)==SQLITE_OK ){
+ const int tnum = pIter->iTnum;
+ char *zCollist = 0; /* List of indexed columns */
+ char **pz = &p->zErrmsg;
+ const char *zIdx = pIter->zIdx;
+ char *zLimit = 0;
+
+ if( nOffset ){
+ zLimit = sqlite3_mprintf(" LIMIT -1 OFFSET %d", nOffset);
+ if( !zLimit ) p->rc = SQLITE_NOMEM;
+ }
+
+ if( zIdx ){
+ const char *zTbl = pIter->zTbl;
+ char *zImposterCols = 0; /* Columns for imposter table */
+ char *zImposterPK = 0; /* Primary key declaration for imposter */
+ char *zWhere = 0; /* WHERE clause on PK columns */
+ char *zBind = 0;
+ int nBind = 0;
+
+ assert( pIter->eType!=RBU_PK_VTAB );
+ zCollist = rbuObjIterGetIndexCols(
+ p, pIter, &zImposterCols, &zImposterPK, &zWhere, &nBind
+ );
+ zBind = rbuObjIterGetBindlist(p, nBind);
+
+ /* Create the imposter table used to write to this index. */
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 1);
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 1,tnum);
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TABLE \"rbu_imp_%w\"( %s, PRIMARY KEY( %s ) ) WITHOUT ROWID",
+ zTbl, zImposterCols, zImposterPK
+ );
+ sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->dbMain, "main", 0, 0);
+
+ /* Create the statement to insert index entries */
+ pIter->nCol = nBind;
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(
+ p->dbMain, &pIter->pInsert, &p->zErrmsg,
+ sqlite3_mprintf("INSERT INTO \"rbu_imp_%w\" VALUES(%s)", zTbl, zBind)
+ );
+ }
+
+ /* And to delete index entries */
+ if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(
+ p->dbMain, &pIter->pDelete, &p->zErrmsg,
+ sqlite3_mprintf("DELETE FROM \"rbu_imp_%w\" WHERE %s", zTbl, zWhere)
+ );
+ }
+
+ /* Create the SELECT statement to read keys in sorted order */
+ if( p->rc==SQLITE_OK ){
+ char *zSql;
+ if( rbuIsVacuum(p) ){
+ zSql = sqlite3_mprintf(
+ "SELECT %s, 0 AS rbu_control FROM '%q' ORDER BY %s%s",
+ zCollist,
+ pIter->zDataTbl,
+ zCollist, zLimit
+ );
+ }else
+
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ zSql = sqlite3_mprintf(
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' ORDER BY %s%s",
+ zCollist, p->zStateDb, pIter->zDataTbl,
+ zCollist, zLimit
+ );
+ }else{
+ zSql = sqlite3_mprintf(
+ "SELECT %s, rbu_control FROM %s.'rbu_tmp_%q' "
+ "UNION ALL "
+ "SELECT %s, rbu_control FROM '%q' "
+ "WHERE typeof(rbu_control)='integer' AND rbu_control!=1 "
+ "ORDER BY %s%s",
+ zCollist, p->zStateDb, pIter->zDataTbl,
+ zCollist, pIter->zDataTbl,
+ zCollist, zLimit
+ );
+ }
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz, zSql);
+ }
+
+ sqlite3_free(zImposterCols);
+ sqlite3_free(zImposterPK);
+ sqlite3_free(zWhere);
+ sqlite3_free(zBind);
+ }else{
+ int bRbuRowid = (pIter->eType==RBU_PK_VTAB)
+ ||(pIter->eType==RBU_PK_NONE)
+ ||(pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p));
+ const char *zTbl = pIter->zTbl; /* Table this step applies to */
+ const char *zWrite; /* Imposter table name */
+
+ char *zBindings = rbuObjIterGetBindlist(p, pIter->nTblCol + bRbuRowid);
+ char *zWhere = rbuObjIterGetWhere(p, pIter);
+ char *zOldlist = rbuObjIterGetOldlist(p, pIter, "old");
+ char *zNewlist = rbuObjIterGetOldlist(p, pIter, "new");
+
+ zCollist = rbuObjIterGetCollist(p, pIter);
+ pIter->nCol = pIter->nTblCol;
+
+ /* Create the imposter table or tables (if required). */
+ rbuCreateImposterTable(p, pIter);
+ rbuCreateImposterTable2(p, pIter);
+ zWrite = (pIter->eType==RBU_PK_VTAB ? "" : "rbu_imp_");
+
+ /* Create the INSERT statement to write to the target PK b-tree */
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pInsert, pz,
+ sqlite3_mprintf(
+ "INSERT INTO \"%s%w\"(%s%s) VALUES(%s)",
+ zWrite, zTbl, zCollist, (bRbuRowid ? ", _rowid_" : ""), zBindings
+ )
+ );
+ }
+
+ /* Create the DELETE statement to write to the target PK b-tree.
+ ** Because it only performs INSERT operations, this is not required for
+ ** an rbu vacuum handle. */
+ if( rbuIsVacuum(p)==0 && p->rc==SQLITE_OK ){
+ p->rc = prepareFreeAndCollectError(p->dbMain, &pIter->pDelete, pz,
+ sqlite3_mprintf(
+ "DELETE FROM \"%s%w\" WHERE %s", zWrite, zTbl, zWhere
+ )
+ );
+ }
+
+ if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
+ const char *zRbuRowid = "";
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ zRbuRowid = ", rbu_rowid";
+ }
+
+ /* Create the rbu_tmp_xxx table and the triggers to populate it. */
+ rbuMPrintfExec(p, p->dbRbu,
+ "CREATE TABLE IF NOT EXISTS %s.'rbu_tmp_%q' AS "
+ "SELECT *%s FROM '%q' WHERE 0;"
+ , p->zStateDb, pIter->zDataTbl
+ , (pIter->eType==RBU_PK_EXTERNAL ? ", 0 AS rbu_rowid" : "")
+ , pIter->zDataTbl
+ );
+
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TEMP TRIGGER rbu_delete_tr BEFORE DELETE ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(3, %s);"
+ "END;"
+
+ "CREATE TEMP TRIGGER rbu_update1_tr BEFORE UPDATE ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(3, %s);"
+ "END;"
+
+ "CREATE TEMP TRIGGER rbu_update2_tr AFTER UPDATE ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(4, %s);"
+ "END;",
+ zWrite, zTbl, zOldlist,
+ zWrite, zTbl, zOldlist,
+ zWrite, zTbl, zNewlist
+ );
+
+ if( pIter->eType==RBU_PK_EXTERNAL || pIter->eType==RBU_PK_NONE ){
+ rbuMPrintfExec(p, p->dbMain,
+ "CREATE TEMP TRIGGER rbu_insert_tr AFTER INSERT ON \"%s%w\" "
+ "BEGIN "
+ " SELECT rbu_tmp_insert(0, %s);"
+ "END;",
+ zWrite, zTbl, zNewlist
+ );
+ }
+
+ rbuObjIterPrepareTmpInsert(p, pIter, zCollist, zRbuRowid);
+ }
+
+ /* Create the SELECT statement to read keys from data_xxx */
+ if( p->rc==SQLITE_OK ){
+ const char *zRbuRowid = "";
+ if( bRbuRowid ){
+ zRbuRowid = rbuIsVacuum(p) ? ",_rowid_ " : ",rbu_rowid";
+ }
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pIter->pSelect, pz,
+ sqlite3_mprintf(
+ "SELECT %s,%s rbu_control%s FROM '%q'%s",
+ zCollist,
+ (rbuIsVacuum(p) ? "0 AS " : ""),
+ zRbuRowid,
+ pIter->zDataTbl, zLimit
+ )
+ );
+ }
+
+ sqlite3_free(zWhere);
+ sqlite3_free(zOldlist);
+ sqlite3_free(zNewlist);
+ sqlite3_free(zBindings);
+ }
+ sqlite3_free(zCollist);
+ sqlite3_free(zLimit);
+ }
+
+ return p->rc;
+}
+
+/*
+** Set output variable *ppStmt to point to an UPDATE statement that may
+** be used to update the imposter table for the main table b-tree of the
+** table object that pIter currently points to, assuming that the
+** rbu_control column of the data_xyz table contains zMask.
+**
+** If the zMask string does not specify any columns to update, then this
+** is not an error. Output variable *ppStmt is set to NULL in this case.
+*/
+static int rbuGetUpdateStmt(
+ sqlite3rbu *p, /* RBU handle */
+ RbuObjIter *pIter, /* Object iterator */
+ const char *zMask, /* rbu_control value ('x.x.') */
+ sqlite3_stmt **ppStmt /* OUT: UPDATE statement handle */
+){
+ RbuUpdateStmt **pp;
+ RbuUpdateStmt *pUp = 0;
+ int nUp = 0;
+
+ /* In case an error occurs */
+ *ppStmt = 0;
+
+ /* Search for an existing statement. If one is found, shift it to the front
+ ** of the LRU queue and return immediately. Otherwise, leave nUp pointing
+ ** to the number of statements currently in the cache and pUp to the
+ ** last object in the list. */
+ for(pp=&pIter->pRbuUpdate; *pp; pp=&((*pp)->pNext)){
+ pUp = *pp;
+ if( strcmp(pUp->zMask, zMask)==0 ){
+ *pp = pUp->pNext;
+ pUp->pNext = pIter->pRbuUpdate;
+ pIter->pRbuUpdate = pUp;
+ *ppStmt = pUp->pUpdate;
+ return SQLITE_OK;
+ }
+ nUp++;
+ }
+ assert( pUp==0 || pUp->pNext==0 );
+
+ if( nUp>=SQLITE_RBU_UPDATE_CACHESIZE ){
+ for(pp=&pIter->pRbuUpdate; *pp!=pUp; pp=&((*pp)->pNext));
+ *pp = 0;
+ sqlite3_finalize(pUp->pUpdate);
+ pUp->pUpdate = 0;
+ }else{
+ pUp = (RbuUpdateStmt*)rbuMalloc(p, sizeof(RbuUpdateStmt)+pIter->nTblCol+1);
+ }
+
+ if( pUp ){
+ char *zWhere = rbuObjIterGetWhere(p, pIter);
+ char *zSet = rbuObjIterGetSetlist(p, pIter, zMask);
+ char *zUpdate = 0;
+
+ pUp->zMask = (char*)&pUp[1];
+ memcpy(pUp->zMask, zMask, pIter->nTblCol);
+ pUp->pNext = pIter->pRbuUpdate;
+ pIter->pRbuUpdate = pUp;
+
+ if( zSet ){
+ const char *zPrefix = "";
+
+ if( pIter->eType!=RBU_PK_VTAB ) zPrefix = "rbu_imp_";
+ zUpdate = sqlite3_mprintf("UPDATE \"%s%w\" SET %s WHERE %s",
+ zPrefix, pIter->zTbl, zSet, zWhere
+ );
+ p->rc = prepareFreeAndCollectError(
+ p->dbMain, &pUp->pUpdate, &p->zErrmsg, zUpdate
+ );
+ *ppStmt = pUp->pUpdate;
+ }
+ sqlite3_free(zWhere);
+ sqlite3_free(zSet);
+ }
+
+ return p->rc;
+}
+
+static sqlite3 *rbuOpenDbhandle(
+ sqlite3rbu *p,
+ const char *zName,
+ int bUseVfs
+){
+ sqlite3 *db = 0;
+ if( p->rc==SQLITE_OK ){
+ const int flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_URI;
+ p->rc = sqlite3_open_v2(zName, &db, flags, bUseVfs ? p->zVfsName : 0);
+ if( p->rc ){
+ p->zErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
+ sqlite3_close(db);
+ db = 0;
+ }
+ }
+ return db;
+}
+
+/*
+** Free an RbuState object allocated by rbuLoadState().
+*/
+static void rbuFreeState(RbuState *p){
+ if( p ){
+ sqlite3_free(p->zTbl);
+ sqlite3_free(p->zIdx);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Allocate an RbuState object and load the contents of the rbu_state
+** table into it. Return a pointer to the new object. It is the
+** responsibility of the caller to eventually free the object using
+** sqlite3_free().
+**
+** If an error occurs, leave an error code and message in the rbu handle
+** and return NULL.
+*/
+static RbuState *rbuLoadState(sqlite3rbu *p){
+ RbuState *pRet = 0;
+ sqlite3_stmt *pStmt = 0;
+ int rc;
+ int rc2;
+
+ pRet = (RbuState*)rbuMalloc(p, sizeof(RbuState));
+ if( pRet==0 ) return 0;
+
+ rc = prepareFreeAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ sqlite3_mprintf("SELECT k, v FROM %s.rbu_state", p->zStateDb)
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ switch( sqlite3_column_int(pStmt, 0) ){
+ case RBU_STATE_STAGE:
+ pRet->eStage = sqlite3_column_int(pStmt, 1);
+ if( pRet->eStage!=RBU_STAGE_OAL
+ && pRet->eStage!=RBU_STAGE_MOVE
+ && pRet->eStage!=RBU_STAGE_CKPT
+ ){
+ p->rc = SQLITE_CORRUPT;
+ }
+ break;
+
+ case RBU_STATE_TBL:
+ pRet->zTbl = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
+ break;
+
+ case RBU_STATE_IDX:
+ pRet->zIdx = rbuStrndup((char*)sqlite3_column_text(pStmt, 1), &rc);
+ break;
+
+ case RBU_STATE_ROW:
+ pRet->nRow = sqlite3_column_int(pStmt, 1);
+ break;
+
+ case RBU_STATE_PROGRESS:
+ pRet->nProgress = sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_CKPT:
+ pRet->iWalCksum = sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_COOKIE:
+ pRet->iCookie = (u32)sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_OALSZ:
+ pRet->iOalSz = (u32)sqlite3_column_int64(pStmt, 1);
+ break;
+
+ case RBU_STATE_PHASEONESTEP:
+ pRet->nPhaseOneStep = sqlite3_column_int64(pStmt, 1);
+ break;
+
+ default:
+ rc = SQLITE_CORRUPT;
+ break;
+ }
+ }
+ rc2 = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ) rc = rc2;
+
+ p->rc = rc;
+ return pRet;
+}
+
+
+/*
+** Open the database handle and attach the RBU database as "rbu". If an
+** error occurs, leave an error code and message in the RBU handle.
+*/
+static void rbuOpenDatabase(sqlite3rbu *p){
+ assert( p->rc || (p->dbMain==0 && p->dbRbu==0) );
+ assert( p->rc || rbuIsVacuum(p) || p->zTarget!=0 );
+
+ /* Open the RBU database */
+ p->dbRbu = rbuOpenDbhandle(p, p->zRbu, 1);
+
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
+ sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
+ if( p->zState==0 ){
+ const char *zFile = sqlite3_db_filename(p->dbRbu, "main");
+ p->zState = rbuMPrintf(p, "file://%s-vacuum?modeof=%s", zFile, zFile);
+ }
+ }
+
+ /* If using separate RBU and state databases, attach the state database to
+ ** the RBU db handle now. */
+ if( p->zState ){
+ rbuMPrintfExec(p, p->dbRbu, "ATTACH %Q AS stat", p->zState);
+ memcpy(p->zStateDb, "stat", 4);
+ }else{
+ memcpy(p->zStateDb, "main", 4);
+ }
+
+#if 0
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
+ p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, 0);
+ }
+#endif
+
+ /* If it has not already been created, create the rbu_state table */
+ rbuMPrintfExec(p, p->dbRbu, RBU_CREATE_STATE, p->zStateDb);
+
+#if 0
+ if( rbuIsVacuum(p) ){
+ if( p->rc==SQLITE_OK ){
+ int rc2;
+ int bOk = 0;
+ sqlite3_stmt *pCnt = 0;
+ p->rc = prepareAndCollectError(p->dbRbu, &pCnt, &p->zErrmsg,
+ "SELECT count(*) FROM stat.sqlite_master"
+ );
+ if( p->rc==SQLITE_OK
+ && sqlite3_step(pCnt)==SQLITE_ROW
+ && 1==sqlite3_column_int(pCnt, 0)
+ ){
+ bOk = 1;
+ }
+ rc2 = sqlite3_finalize(pCnt);
+ if( p->rc==SQLITE_OK ) p->rc = rc2;
+
+ if( p->rc==SQLITE_OK && bOk==0 ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("invalid state database");
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
+ }
+ }
+ }
+#endif
+
+ if( p->rc==SQLITE_OK && rbuIsVacuum(p) ){
+ int bOpen = 0;
+ int rc;
+ p->nRbu = 0;
+ p->pRbuFd = 0;
+ rc = sqlite3_file_control(p->dbRbu, "main", SQLITE_FCNTL_RBUCNT, (void*)p);
+ if( rc!=SQLITE_NOTFOUND ) p->rc = rc;
+ if( p->eStage>=RBU_STAGE_MOVE ){
+ bOpen = 1;
+ }else{
+ RbuState *pState = rbuLoadState(p);
+ if( pState ){
+ bOpen = (pState->eStage>RBU_STAGE_MOVE);
+ rbuFreeState(pState);
+ }
+ }
+ if( bOpen ) p->dbMain = rbuOpenDbhandle(p, p->zRbu, p->nRbu<=1);
+ }
+
+ p->eStage = 0;
+ if( p->rc==SQLITE_OK && p->dbMain==0 ){
+ if( !rbuIsVacuum(p) ){
+ p->dbMain = rbuOpenDbhandle(p, p->zTarget, 1);
+ }else if( p->pRbuFd->pWalFd ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("cannot vacuum wal mode database");
+ }else{
+ char *zTarget;
+ char *zExtra = 0;
+ if( strlen(p->zRbu)>=5 && 0==memcmp("file:", p->zRbu, 5) ){
+ zExtra = &p->zRbu[5];
+ while( *zExtra ){
+ if( *zExtra++=='?' ) break;
+ }
+ if( *zExtra=='\0' ) zExtra = 0;
+ }
+
+ zTarget = sqlite3_mprintf("file:%s-vacuum?rbu_memory=1%s%s",
+ sqlite3_db_filename(p->dbRbu, "main"),
+ (zExtra==0 ? "" : "&"), (zExtra==0 ? "" : zExtra)
+ );
+
+ if( zTarget==0 ){
+ p->rc = SQLITE_NOMEM;
+ return;
+ }
+ p->dbMain = rbuOpenDbhandle(p, zTarget, p->nRbu<=1);
+ sqlite3_free(zTarget);
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_create_function(p->dbMain,
+ "rbu_tmp_insert", -1, SQLITE_UTF8, (void*)p, rbuTmpInsertFunc, 0, 0
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_create_function(p->dbMain,
+ "rbu_fossil_delta", 2, SQLITE_UTF8, 0, rbuFossilDeltaFunc, 0, 0
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_create_function(p->dbRbu,
+ "rbu_target_name", -1, SQLITE_UTF8, (void*)p, rbuTargetNameFunc, 0, 0
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
+ }
+ rbuMPrintfExec(p, p->dbMain, "SELECT * FROM sqlite_master");
+
+ /* Mark the database file just opened as an RBU target database. If
+ ** this call returns SQLITE_NOTFOUND, then the RBU vfs is not in use.
+ ** This is an error. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_file_control(p->dbMain, "main", SQLITE_FCNTL_RBU, (void*)p);
+ }
+
+ if( p->rc==SQLITE_NOTFOUND ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("rbu vfs not found");
+ }
+}
+
+/*
+** This routine is a copy of the sqlite3FileSuffix3() routine from the core.
+** It is a no-op unless SQLITE_ENABLE_8_3_NAMES is defined.
+**
+** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database
+** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and
+** if filename in z[] has a suffix (a.k.a. "extension") that is longer than
+** three characters, then shorten the suffix on z[] to be the last three
+** characters of the original suffix.
+**
+** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always
+** do the suffix shortening regardless of URI parameter.
+**
+** Examples:
+**
+** test.db-journal => test.nal
+** test.db-wal => test.wal
+** test.db-shm => test.shm
+** test.db-mj7f3319fa => test.9fa
+*/
+static void rbuFileSuffix3(const char *zBase, char *z){
+#ifdef SQLITE_ENABLE_8_3_NAMES
+#if SQLITE_ENABLE_8_3_NAMES<2
+ if( sqlite3_uri_boolean(zBase, "8_3_names", 0) )
+#endif
+ {
+ int i, sz;
+ sz = (int)strlen(z)&0xffffff;
+ for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){}
+ if( z[i]=='.' && sz>i+4 ) memmove(&z[i+1], &z[sz-3], 4);
+ }
+#endif
+}
+
+/*
+** Return the current wal-index header checksum for the target database
+** as a 64-bit integer.
+**
+** The checksum is store in the first page of xShmMap memory as an 8-byte
+** blob starting at byte offset 40.
+*/
+static i64 rbuShmChecksum(sqlite3rbu *p){
+ i64 iRet = 0;
+ if( p->rc==SQLITE_OK ){
+ sqlite3_file *pDb = p->pTargetFd->pReal;
+ u32 volatile *ptr;
+ p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, (void volatile**)&ptr);
+ if( p->rc==SQLITE_OK ){
+ iRet = ((i64)ptr[10] << 32) + ptr[11];
+ }
+ }
+ return iRet;
+}
+
+/*
+** This function is called as part of initializing or reinitializing an
+** incremental checkpoint.
+**
+** It populates the sqlite3rbu.aFrame[] array with the set of
+** (wal frame -> db page) copy operations required to checkpoint the
+** current wal file, and obtains the set of shm locks required to safely
+** perform the copy operations directly on the file-system.
+**
+** If argument pState is not NULL, then the incremental checkpoint is
+** being resumed. In this case, if the checksum of the wal-index-header
+** following recovery is not the same as the checksum saved in the RbuState
+** object, then the rbu handle is set to DONE state. This occurs if some
+** other client appends a transaction to the wal file in the middle of
+** an incremental checkpoint.
+*/
+static void rbuSetupCheckpoint(sqlite3rbu *p, RbuState *pState){
+
+ /* If pState is NULL, then the wal file may not have been opened and
+ ** recovered. Running a read-statement here to ensure that doing so
+ ** does not interfere with the "capture" process below. */
+ if( pState==0 ){
+ p->eStage = 0;
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbMain, "SELECT * FROM sqlite_master", 0, 0, 0);
+ }
+ }
+
+ /* Assuming no error has occurred, run a "restart" checkpoint with the
+ ** sqlite3rbu.eStage variable set to CAPTURE. This turns on the following
+ ** special behaviour in the rbu VFS:
+ **
+ ** * If the exclusive shm WRITER or READ0 lock cannot be obtained,
+ ** the checkpoint fails with SQLITE_BUSY (normally SQLite would
+ ** proceed with running a passive checkpoint instead of failing).
+ **
+ ** * Attempts to read from the *-wal file or write to the database file
+ ** do not perform any IO. Instead, the frame/page combinations that
+ ** would be read/written are recorded in the sqlite3rbu.aFrame[]
+ ** array.
+ **
+ ** * Calls to xShmLock(UNLOCK) to release the exclusive shm WRITER,
+ ** READ0 and CHECKPOINT locks taken as part of the checkpoint are
+ ** no-ops. These locks will not be released until the connection
+ ** is closed.
+ **
+ ** * Attempting to xSync() the database file causes an SQLITE_INTERNAL
+ ** error.
+ **
+ ** As a result, unless an error (i.e. OOM or SQLITE_BUSY) occurs, the
+ ** checkpoint below fails with SQLITE_INTERNAL, and leaves the aFrame[]
+ ** array populated with a set of (frame -> page) mappings. Because the
+ ** WRITER, CHECKPOINT and READ0 locks are still held, it is safe to copy
+ ** data from the wal file into the database file according to the
+ ** contents of aFrame[].
+ */
+ if( p->rc==SQLITE_OK ){
+ int rc2;
+ p->eStage = RBU_STAGE_CAPTURE;
+ rc2 = sqlite3_exec(p->dbMain, "PRAGMA main.wal_checkpoint=restart", 0, 0,0);
+ if( rc2!=SQLITE_INTERNAL ) p->rc = rc2;
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->eStage = RBU_STAGE_CKPT;
+ p->nStep = (pState ? pState->nRow : 0);
+ p->aBuf = rbuMalloc(p, p->pgsz);
+ p->iWalCksum = rbuShmChecksum(p);
+ }
+
+ if( p->rc==SQLITE_OK && pState && pState->iWalCksum!=p->iWalCksum ){
+ p->rc = SQLITE_DONE;
+ p->eStage = RBU_STAGE_DONE;
+ }
+}
+
+/*
+** Called when iAmt bytes are read from offset iOff of the wal file while
+** the rbu object is in capture mode. Record the frame number of the frame
+** being read in the aFrame[] array.
+*/
+static int rbuCaptureWalRead(sqlite3rbu *pRbu, i64 iOff, int iAmt){
+ const u32 mReq = (1<<WAL_LOCK_WRITE)|(1<<WAL_LOCK_CKPT)|(1<<WAL_LOCK_READ0);
+ u32 iFrame;
+
+ if( pRbu->mLock!=mReq ){
+ pRbu->rc = SQLITE_BUSY;
+ return SQLITE_INTERNAL;
+ }
+
+ pRbu->pgsz = iAmt;
+ if( pRbu->nFrame==pRbu->nFrameAlloc ){
+ int nNew = (pRbu->nFrameAlloc ? pRbu->nFrameAlloc : 64) * 2;
+ RbuFrame *aNew;
+ aNew = (RbuFrame*)sqlite3_realloc64(pRbu->aFrame, nNew * sizeof(RbuFrame));
+ if( aNew==0 ) return SQLITE_NOMEM;
+ pRbu->aFrame = aNew;
+ pRbu->nFrameAlloc = nNew;
+ }
+
+ iFrame = (u32)((iOff-32) / (i64)(iAmt+24)) + 1;
+ if( pRbu->iMaxFrame<iFrame ) pRbu->iMaxFrame = iFrame;
+ pRbu->aFrame[pRbu->nFrame].iWalFrame = iFrame;
+ pRbu->aFrame[pRbu->nFrame].iDbPage = 0;
+ pRbu->nFrame++;
+ return SQLITE_OK;
+}
+
+/*
+** Called when a page of data is written to offset iOff of the database
+** file while the rbu handle is in capture mode. Record the page number
+** of the page being written in the aFrame[] array.
+*/
+static int rbuCaptureDbWrite(sqlite3rbu *pRbu, i64 iOff){
+ pRbu->aFrame[pRbu->nFrame-1].iDbPage = (u32)(iOff / pRbu->pgsz) + 1;
+ return SQLITE_OK;
+}
+
+/*
+** This is called as part of an incremental checkpoint operation. Copy
+** a single frame of data from the wal file into the database file, as
+** indicated by the RbuFrame object.
+*/
+static void rbuCheckpointFrame(sqlite3rbu *p, RbuFrame *pFrame){
+ sqlite3_file *pWal = p->pTargetFd->pWalFd->pReal;
+ sqlite3_file *pDb = p->pTargetFd->pReal;
+ i64 iOff;
+
+ assert( p->rc==SQLITE_OK );
+ iOff = (i64)(pFrame->iWalFrame-1) * (p->pgsz + 24) + 32 + 24;
+ p->rc = pWal->pMethods->xRead(pWal, p->aBuf, p->pgsz, iOff);
+ if( p->rc ) return;
+
+ iOff = (i64)(pFrame->iDbPage-1) * p->pgsz;
+ p->rc = pDb->pMethods->xWrite(pDb, p->aBuf, p->pgsz, iOff);
+}
+
+
+/*
+** Take an EXCLUSIVE lock on the database file.
+*/
+static void rbuLockDatabase(sqlite3rbu *p){
+ sqlite3_file *pReal = p->pTargetFd->pReal;
+ assert( p->rc==SQLITE_OK );
+ p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_SHARED);
+ if( p->rc==SQLITE_OK ){
+ p->rc = pReal->pMethods->xLock(pReal, SQLITE_LOCK_EXCLUSIVE);
+ }
+}
+
+#if defined(_WIN32_WCE)
+static LPWSTR rbuWinUtf8ToUnicode(const char *zFilename){
+ int nChar;
+ LPWSTR zWideFilename;
+
+ nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ if( nChar==0 ){
+ return 0;
+ }
+ zWideFilename = sqlite3_malloc64( nChar*sizeof(zWideFilename[0]) );
+ if( zWideFilename==0 ){
+ return 0;
+ }
+ memset(zWideFilename, 0, nChar*sizeof(zWideFilename[0]));
+ nChar = MultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename,
+ nChar);
+ if( nChar==0 ){
+ sqlite3_free(zWideFilename);
+ zWideFilename = 0;
+ }
+ return zWideFilename;
+}
+#endif
+
+/*
+** The RBU handle is currently in RBU_STAGE_OAL state, with a SHARED lock
+** on the database file. This proc moves the *-oal file to the *-wal path,
+** then reopens the database file (this time in vanilla, non-oal, WAL mode).
+** If an error occurs, leave an error code and error message in the rbu
+** handle.
+*/
+static void rbuMoveOalFile(sqlite3rbu *p){
+ const char *zBase = sqlite3_db_filename(p->dbMain, "main");
+ const char *zMove = zBase;
+ char *zOal;
+ char *zWal;
+
+ if( rbuIsVacuum(p) ){
+ zMove = sqlite3_db_filename(p->dbRbu, "main");
+ }
+ zOal = sqlite3_mprintf("%s-oal", zMove);
+ zWal = sqlite3_mprintf("%s-wal", zMove);
+
+ assert( p->eStage==RBU_STAGE_MOVE );
+ assert( p->rc==SQLITE_OK && p->zErrmsg==0 );
+ if( zWal==0 || zOal==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ /* Move the *-oal file to *-wal. At this point connection p->db is
+ ** holding a SHARED lock on the target database file (because it is
+ ** in WAL mode). So no other connection may be writing the db.
+ **
+ ** In order to ensure that there are no database readers, an EXCLUSIVE
+ ** lock is obtained here before the *-oal is moved to *-wal.
+ */
+ rbuLockDatabase(p);
+ if( p->rc==SQLITE_OK ){
+ rbuFileSuffix3(zBase, zWal);
+ rbuFileSuffix3(zBase, zOal);
+
+ /* Re-open the databases. */
+ rbuObjIterFinalize(&p->objiter);
+ sqlite3_close(p->dbRbu);
+ sqlite3_close(p->dbMain);
+ p->dbMain = 0;
+ p->dbRbu = 0;
+
+#if defined(_WIN32_WCE)
+ {
+ LPWSTR zWideOal;
+ LPWSTR zWideWal;
+
+ zWideOal = rbuWinUtf8ToUnicode(zOal);
+ if( zWideOal ){
+ zWideWal = rbuWinUtf8ToUnicode(zWal);
+ if( zWideWal ){
+ if( MoveFileW(zWideOal, zWideWal) ){
+ p->rc = SQLITE_OK;
+ }else{
+ p->rc = SQLITE_IOERR;
+ }
+ sqlite3_free(zWideWal);
+ }else{
+ p->rc = SQLITE_IOERR_NOMEM;
+ }
+ sqlite3_free(zWideOal);
+ }else{
+ p->rc = SQLITE_IOERR_NOMEM;
+ }
+ }
+#else
+ p->rc = rename(zOal, zWal) ? SQLITE_IOERR : SQLITE_OK;
+#endif
+
+ if( p->rc==SQLITE_OK ){
+ rbuOpenDatabase(p);
+ rbuSetupCheckpoint(p, 0);
+ }
+ }
+ }
+
+ sqlite3_free(zWal);
+ sqlite3_free(zOal);
+}
+
+/*
+** The SELECT statement iterating through the keys for the current object
+** (p->objiter.pSelect) currently points to a valid row. This function
+** determines the type of operation requested by this row and returns
+** one of the following values to indicate the result:
+**
+** * RBU_INSERT
+** * RBU_DELETE
+** * RBU_IDX_DELETE
+** * RBU_UPDATE
+**
+** If RBU_UPDATE is returned, then output variable *pzMask is set to
+** point to the text value indicating the columns to update.
+**
+** If the rbu_control field contains an invalid value, an error code and
+** message are left in the RBU handle and zero returned.
+*/
+static int rbuStepType(sqlite3rbu *p, const char **pzMask){
+ int iCol = p->objiter.nCol; /* Index of rbu_control column */
+ int res = 0; /* Return value */
+
+ switch( sqlite3_column_type(p->objiter.pSelect, iCol) ){
+ case SQLITE_INTEGER: {
+ int iVal = sqlite3_column_int(p->objiter.pSelect, iCol);
+ switch( iVal ){
+ case 0: res = RBU_INSERT; break;
+ case 1: res = RBU_DELETE; break;
+ case 2: res = RBU_REPLACE; break;
+ case 3: res = RBU_IDX_DELETE; break;
+ case 4: res = RBU_IDX_INSERT; break;
+ }
+ break;
+ }
+
+ case SQLITE_TEXT: {
+ const unsigned char *z = sqlite3_column_text(p->objiter.pSelect, iCol);
+ if( z==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ *pzMask = (const char*)z;
+ }
+ res = RBU_UPDATE;
+
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if( res==0 ){
+ rbuBadControlError(p);
+ }
+ return res;
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** Assert that column iCol of statement pStmt is named zName.
+*/
+static void assertColumnName(sqlite3_stmt *pStmt, int iCol, const char *zName){
+ const char *zCol = sqlite3_column_name(pStmt, iCol);
+ assert( 0==sqlite3_stricmp(zName, zCol) );
+}
+#else
+# define assertColumnName(x,y,z)
+#endif
+
+/*
+** Argument eType must be one of RBU_INSERT, RBU_DELETE, RBU_IDX_INSERT or
+** RBU_IDX_DELETE. This function performs the work of a single
+** sqlite3rbu_step() call for the type of operation specified by eType.
+*/
+static void rbuStepOneOp(sqlite3rbu *p, int eType){
+ RbuObjIter *pIter = &p->objiter;
+ sqlite3_value *pVal;
+ sqlite3_stmt *pWriter;
+ int i;
+
+ assert( p->rc==SQLITE_OK );
+ assert( eType!=RBU_DELETE || pIter->zIdx==0 );
+ assert( eType==RBU_DELETE || eType==RBU_IDX_DELETE
+ || eType==RBU_INSERT || eType==RBU_IDX_INSERT
+ );
+
+ /* If this is a delete, decrement nPhaseOneStep by nIndex. If the DELETE
+ ** statement below does actually delete a row, nPhaseOneStep will be
+ ** incremented by the same amount when SQL function rbu_tmp_insert()
+ ** is invoked by the trigger. */
+ if( eType==RBU_DELETE ){
+ p->nPhaseOneStep -= p->objiter.nIndex;
+ }
+
+ if( eType==RBU_IDX_DELETE || eType==RBU_DELETE ){
+ pWriter = pIter->pDelete;
+ }else{
+ pWriter = pIter->pInsert;
+ }
+
+ for(i=0; i<pIter->nCol; i++){
+ /* If this is an INSERT into a table b-tree and the table has an
+ ** explicit INTEGER PRIMARY KEY, check that this is not an attempt
+ ** to write a NULL into the IPK column. That is not permitted. */
+ if( eType==RBU_INSERT
+ && pIter->zIdx==0 && pIter->eType==RBU_PK_IPK && pIter->abTblPk[i]
+ && sqlite3_column_type(pIter->pSelect, i)==SQLITE_NULL
+ ){
+ p->rc = SQLITE_MISMATCH;
+ p->zErrmsg = sqlite3_mprintf("datatype mismatch");
+ return;
+ }
+
+ if( eType==RBU_DELETE && pIter->abTblPk[i]==0 ){
+ continue;
+ }
+
+ pVal = sqlite3_column_value(pIter->pSelect, i);
+ p->rc = sqlite3_bind_value(pWriter, i+1, pVal);
+ if( p->rc ) return;
+ }
+ if( pIter->zIdx==0 ){
+ if( pIter->eType==RBU_PK_VTAB
+ || pIter->eType==RBU_PK_NONE
+ || (pIter->eType==RBU_PK_EXTERNAL && rbuIsVacuum(p))
+ ){
+ /* For a virtual table, or a table with no primary key, the
+ ** SELECT statement is:
+ **
+ ** SELECT <cols>, rbu_control, rbu_rowid FROM ....
+ **
+ ** Hence column_value(pIter->nCol+1).
+ */
+ assertColumnName(pIter->pSelect, pIter->nCol+1,
+ rbuIsVacuum(p) ? "rowid" : "rbu_rowid"
+ );
+ pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
+ p->rc = sqlite3_bind_value(pWriter, pIter->nCol+1, pVal);
+ }
+ }
+ if( p->rc==SQLITE_OK ){
+ sqlite3_step(pWriter);
+ p->rc = resetAndCollectError(pWriter, &p->zErrmsg);
+ }
+}
+
+/*
+** This function does the work for an sqlite3rbu_step() call.
+**
+** The object-iterator (p->objiter) currently points to a valid object,
+** and the input cursor (p->objiter.pSelect) currently points to a valid
+** input row. Perform whatever processing is required and return.
+**
+** If no error occurs, SQLITE_OK is returned. Otherwise, an error code
+** and message is left in the RBU handle and a copy of the error code
+** returned.
+*/
+static int rbuStep(sqlite3rbu *p){
+ RbuObjIter *pIter = &p->objiter;
+ const char *zMask = 0;
+ int eType = rbuStepType(p, &zMask);
+
+ if( eType ){
+ assert( eType==RBU_INSERT || eType==RBU_DELETE
+ || eType==RBU_REPLACE || eType==RBU_IDX_DELETE
+ || eType==RBU_IDX_INSERT || eType==RBU_UPDATE
+ );
+ assert( eType!=RBU_UPDATE || pIter->zIdx==0 );
+
+ if( pIter->zIdx==0 && (eType==RBU_IDX_DELETE || eType==RBU_IDX_INSERT) ){
+ rbuBadControlError(p);
+ }
+ else if( eType==RBU_REPLACE ){
+ if( pIter->zIdx==0 ){
+ p->nPhaseOneStep += p->objiter.nIndex;
+ rbuStepOneOp(p, RBU_DELETE);
+ }
+ if( p->rc==SQLITE_OK ) rbuStepOneOp(p, RBU_INSERT);
+ }
+ else if( eType!=RBU_UPDATE ){
+ rbuStepOneOp(p, eType);
+ }
+ else{
+ sqlite3_value *pVal;
+ sqlite3_stmt *pUpdate = 0;
+ assert( eType==RBU_UPDATE );
+ p->nPhaseOneStep -= p->objiter.nIndex;
+ rbuGetUpdateStmt(p, pIter, zMask, &pUpdate);
+ if( pUpdate ){
+ int i;
+ for(i=0; p->rc==SQLITE_OK && i<pIter->nCol; i++){
+ char c = zMask[pIter->aiSrcOrder[i]];
+ pVal = sqlite3_column_value(pIter->pSelect, i);
+ if( pIter->abTblPk[i] || c!='.' ){
+ p->rc = sqlite3_bind_value(pUpdate, i+1, pVal);
+ }
+ }
+ if( p->rc==SQLITE_OK
+ && (pIter->eType==RBU_PK_VTAB || pIter->eType==RBU_PK_NONE)
+ ){
+ /* Bind the rbu_rowid value to column _rowid_ */
+ assertColumnName(pIter->pSelect, pIter->nCol+1, "rbu_rowid");
+ pVal = sqlite3_column_value(pIter->pSelect, pIter->nCol+1);
+ p->rc = sqlite3_bind_value(pUpdate, pIter->nCol+1, pVal);
+ }
+ if( p->rc==SQLITE_OK ){
+ sqlite3_step(pUpdate);
+ p->rc = resetAndCollectError(pUpdate, &p->zErrmsg);
+ }
+ }
+ }
+ }
+ return p->rc;
+}
+
+/*
+** Increment the schema cookie of the main database opened by p->dbMain.
+**
+** Or, if this is an RBU vacuum, set the schema cookie of the main db
+** opened by p->dbMain to one more than the schema cookie of the main
+** db opened by p->dbRbu.
+*/
+static void rbuIncrSchemaCookie(sqlite3rbu *p){
+ if( p->rc==SQLITE_OK ){
+ sqlite3 *dbread = (rbuIsVacuum(p) ? p->dbRbu : p->dbMain);
+ int iCookie = 1000000;
+ sqlite3_stmt *pStmt;
+
+ p->rc = prepareAndCollectError(dbread, &pStmt, &p->zErrmsg,
+ "PRAGMA schema_version"
+ );
+ if( p->rc==SQLITE_OK ){
+ /* Coverage: it may be that this sqlite3_step() cannot fail. There
+ ** is already a transaction open, so the prepared statement cannot
+ ** throw an SQLITE_SCHEMA exception. The only database page the
+ ** statement reads is page 1, which is guaranteed to be in the cache.
+ ** And no memory allocations are required. */
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ iCookie = sqlite3_column_int(pStmt, 0);
+ }
+ rbuFinalize(p, pStmt);
+ }
+ if( p->rc==SQLITE_OK ){
+ rbuMPrintfExec(p, p->dbMain, "PRAGMA schema_version = %d", iCookie+1);
+ }
+ }
+}
+
+/*
+** Update the contents of the rbu_state table within the rbu database. The
+** value stored in the RBU_STATE_STAGE column is eStage. All other values
+** are determined by inspecting the rbu handle passed as the first argument.
+*/
+static void rbuSaveState(sqlite3rbu *p, int eStage){
+ if( p->rc==SQLITE_OK || p->rc==SQLITE_DONE ){
+ sqlite3_stmt *pInsert = 0;
+ rbu_file *pFd = (rbuIsVacuum(p) ? p->pRbuFd : p->pTargetFd);
+ int rc;
+
+ assert( p->zErrmsg==0 );
+ rc = prepareFreeAndCollectError(p->dbRbu, &pInsert, &p->zErrmsg,
+ sqlite3_mprintf(
+ "INSERT OR REPLACE INTO %s.rbu_state(k, v) VALUES "
+ "(%d, %d), "
+ "(%d, %Q), "
+ "(%d, %Q), "
+ "(%d, %d), "
+ "(%d, %d), "
+ "(%d, %lld), "
+ "(%d, %lld), "
+ "(%d, %lld), "
+ "(%d, %lld) ",
+ p->zStateDb,
+ RBU_STATE_STAGE, eStage,
+ RBU_STATE_TBL, p->objiter.zTbl,
+ RBU_STATE_IDX, p->objiter.zIdx,
+ RBU_STATE_ROW, p->nStep,
+ RBU_STATE_PROGRESS, p->nProgress,
+ RBU_STATE_CKPT, p->iWalCksum,
+ RBU_STATE_COOKIE, (i64)pFd->iCookie,
+ RBU_STATE_OALSZ, p->iOalSz,
+ RBU_STATE_PHASEONESTEP, p->nPhaseOneStep
+ )
+ );
+ assert( pInsert==0 || rc==SQLITE_OK );
+
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pInsert);
+ rc = sqlite3_finalize(pInsert);
+ }
+ if( rc!=SQLITE_OK ) p->rc = rc;
+ }
+}
+
+
+/*
+** The second argument passed to this function is the name of a PRAGMA
+** setting - "page_size", "auto_vacuum", "user_version" or "application_id".
+** This function executes the following on sqlite3rbu.dbRbu:
+**
+** "PRAGMA main.$zPragma"
+**
+** where $zPragma is the string passed as the second argument, then
+** on sqlite3rbu.dbMain:
+**
+** "PRAGMA main.$zPragma = $val"
+**
+** where $val is the value returned by the first PRAGMA invocation.
+**
+** In short, it copies the value of the specified PRAGMA setting from
+** dbRbu to dbMain.
+*/
+static void rbuCopyPragma(sqlite3rbu *p, const char *zPragma){
+ if( p->rc==SQLITE_OK ){
+ sqlite3_stmt *pPragma = 0;
+ p->rc = prepareFreeAndCollectError(p->dbRbu, &pPragma, &p->zErrmsg,
+ sqlite3_mprintf("PRAGMA main.%s", zPragma)
+ );
+ if( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPragma) ){
+ p->rc = rbuMPrintfExec(p, p->dbMain, "PRAGMA main.%s = %d",
+ zPragma, sqlite3_column_int(pPragma, 0)
+ );
+ }
+ rbuFinalize(p, pPragma);
+ }
+}
+
+/*
+** The RBU handle passed as the only argument has just been opened and
+** the state database is empty. If this RBU handle was opened for an
+** RBU vacuum operation, create the schema in the target db.
+*/
+static void rbuCreateTargetSchema(sqlite3rbu *p){
+ sqlite3_stmt *pSql = 0;
+ sqlite3_stmt *pInsert = 0;
+
+ assert( rbuIsVacuum(p) );
+ p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=1", 0,0, &p->zErrmsg);
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
+ "SELECT sql FROM sqlite_master WHERE sql!='' AND rootpage!=0"
+ " AND name!='sqlite_sequence' "
+ " ORDER BY type DESC"
+ );
+ }
+
+ while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
+ const char *zSql = (const char*)sqlite3_column_text(pSql, 0);
+ p->rc = sqlite3_exec(p->dbMain, zSql, 0, 0, &p->zErrmsg);
+ }
+ rbuFinalize(p, pSql);
+ if( p->rc!=SQLITE_OK ) return;
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pSql, &p->zErrmsg,
+ "SELECT * FROM sqlite_master WHERE rootpage=0 OR rootpage IS NULL"
+ );
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbMain, &pInsert, &p->zErrmsg,
+ "INSERT INTO sqlite_master VALUES(?,?,?,?,?)"
+ );
+ }
+
+ while( p->rc==SQLITE_OK && sqlite3_step(pSql)==SQLITE_ROW ){
+ int i;
+ for(i=0; i<5; i++){
+ sqlite3_bind_value(pInsert, i+1, sqlite3_column_value(pSql, i));
+ }
+ sqlite3_step(pInsert);
+ p->rc = sqlite3_reset(pInsert);
+ }
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbMain, "PRAGMA writable_schema=0",0,0,&p->zErrmsg);
+ }
+
+ rbuFinalize(p, pSql);
+ rbuFinalize(p, pInsert);
+}
+
+/*
+** Step the RBU object.
+*/
+SQLITE_API int sqlite3rbu_step(sqlite3rbu *p){
+ if( p ){
+ switch( p->eStage ){
+ case RBU_STAGE_OAL: {
+ RbuObjIter *pIter = &p->objiter;
+
+ /* If this is an RBU vacuum operation and the state table was empty
+ ** when this handle was opened, create the target database schema. */
+ if( rbuIsVacuum(p) && p->nProgress==0 && p->rc==SQLITE_OK ){
+ rbuCreateTargetSchema(p);
+ rbuCopyPragma(p, "user_version");
+ rbuCopyPragma(p, "application_id");
+ }
+
+ while( p->rc==SQLITE_OK && pIter->zTbl ){
+
+ if( pIter->bCleanup ){
+ /* Clean up the rbu_tmp_xxx table for the previous table. It
+ ** cannot be dropped as there are currently active SQL statements.
+ ** But the contents can be deleted. */
+ if( rbuIsVacuum(p)==0 && pIter->abIndexed ){
+ rbuMPrintfExec(p, p->dbRbu,
+ "DELETE FROM %s.'rbu_tmp_%q'", p->zStateDb, pIter->zDataTbl
+ );
+ }
+ }else{
+ rbuObjIterPrepareAll(p, pIter, 0);
+
+ /* Advance to the next row to process. */
+ if( p->rc==SQLITE_OK ){
+ int rc = sqlite3_step(pIter->pSelect);
+ if( rc==SQLITE_ROW ){
+ p->nProgress++;
+ p->nStep++;
+ return rbuStep(p);
+ }
+ p->rc = sqlite3_reset(pIter->pSelect);
+ p->nStep = 0;
+ }
+ }
+
+ rbuObjIterNext(p, pIter);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ assert( pIter->zTbl==0 );
+ rbuSaveState(p, RBU_STAGE_MOVE);
+ rbuIncrSchemaCookie(p);
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+ p->eStage = RBU_STAGE_MOVE;
+ }
+ break;
+ }
+
+ case RBU_STAGE_MOVE: {
+ if( p->rc==SQLITE_OK ){
+ rbuMoveOalFile(p);
+ p->nProgress++;
+ }
+ break;
+ }
+
+ case RBU_STAGE_CKPT: {
+ if( p->rc==SQLITE_OK ){
+ if( p->nStep>=p->nFrame ){
+ sqlite3_file *pDb = p->pTargetFd->pReal;
+
+ /* Sync the db file */
+ p->rc = pDb->pMethods->xSync(pDb, SQLITE_SYNC_NORMAL);
+
+ /* Update nBackfill */
+ if( p->rc==SQLITE_OK ){
+ void volatile *ptr;
+ p->rc = pDb->pMethods->xShmMap(pDb, 0, 32*1024, 0, &ptr);
+ if( p->rc==SQLITE_OK ){
+ ((u32 volatile*)ptr)[24] = p->iMaxFrame;
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ p->eStage = RBU_STAGE_DONE;
+ p->rc = SQLITE_DONE;
+ }
+ }else{
+ RbuFrame *pFrame = &p->aFrame[p->nStep];
+ rbuCheckpointFrame(p, pFrame);
+ p->nStep++;
+ }
+ p->nProgress++;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ return p->rc;
+ }else{
+ return SQLITE_NOMEM;
+ }
+}
+
+/*
+** Compare strings z1 and z2, returning 0 if they are identical, or non-zero
+** otherwise. Either or both argument may be NULL. Two NULL values are
+** considered equal, and NULL is considered distinct from all other values.
+*/
+static int rbuStrCompare(const char *z1, const char *z2){
+ if( z1==0 && z2==0 ) return 0;
+ if( z1==0 || z2==0 ) return 1;
+ return (sqlite3_stricmp(z1, z2)!=0);
+}
+
+/*
+** This function is called as part of sqlite3rbu_open() when initializing
+** an rbu handle in OAL stage. If the rbu update has not started (i.e.
+** the rbu_state table was empty) it is a no-op. Otherwise, it arranges
+** things so that the next call to sqlite3rbu_step() continues on from
+** where the previous rbu handle left off.
+**
+** If an error occurs, an error code and error message are left in the
+** rbu handle passed as the first argument.
+*/
+static void rbuSetupOal(sqlite3rbu *p, RbuState *pState){
+ assert( p->rc==SQLITE_OK );
+ if( pState->zTbl ){
+ RbuObjIter *pIter = &p->objiter;
+ int rc = SQLITE_OK;
+
+ while( rc==SQLITE_OK && pIter->zTbl && (pIter->bCleanup
+ || rbuStrCompare(pIter->zIdx, pState->zIdx)
+ || rbuStrCompare(pIter->zTbl, pState->zTbl)
+ )){
+ rc = rbuObjIterNext(p, pIter);
+ }
+
+ if( rc==SQLITE_OK && !pIter->zTbl ){
+ rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("rbu_state mismatch error");
+ }
+
+ if( rc==SQLITE_OK ){
+ p->nStep = pState->nRow;
+ rc = rbuObjIterPrepareAll(p, &p->objiter, p->nStep);
+ }
+
+ p->rc = rc;
+ }
+}
+
+/*
+** If there is a "*-oal" file in the file-system corresponding to the
+** target database in the file-system, delete it. If an error occurs,
+** leave an error code and error message in the rbu handle.
+*/
+static void rbuDeleteOalFile(sqlite3rbu *p){
+ char *zOal = rbuMPrintf(p, "%s-oal", p->zTarget);
+ if( zOal ){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(0);
+ assert( pVfs && p->rc==SQLITE_OK && p->zErrmsg==0 );
+ pVfs->xDelete(pVfs, zOal, 0);
+ sqlite3_free(zOal);
+ }
+}
+
+/*
+** Allocate a private rbu VFS for the rbu handle passed as the only
+** argument. This VFS will be used unless the call to sqlite3rbu_open()
+** specified a URI with a vfs=? option in place of a target database
+** file name.
+*/
+static void rbuCreateVfs(sqlite3rbu *p){
+ int rnd;
+ char zRnd[64];
+
+ assert( p->rc==SQLITE_OK );
+ sqlite3_randomness(sizeof(int), (void*)&rnd);
+ sqlite3_snprintf(sizeof(zRnd), zRnd, "rbu_vfs_%d", rnd);
+ p->rc = sqlite3rbu_create_vfs(zRnd, 0);
+ if( p->rc==SQLITE_OK ){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zRnd);
+ assert( pVfs );
+ p->zVfsName = pVfs->zName;
+ }
+}
+
+/*
+** Destroy the private VFS created for the rbu handle passed as the only
+** argument by an earlier call to rbuCreateVfs().
+*/
+static void rbuDeleteVfs(sqlite3rbu *p){
+ if( p->zVfsName ){
+ sqlite3rbu_destroy_vfs(p->zVfsName);
+ p->zVfsName = 0;
+ }
+}
+
+/*
+** This user-defined SQL function is invoked with a single argument - the
+** name of a table expected to appear in the target database. It returns
+** the number of auxilliary indexes on the table.
+*/
+static void rbuIndexCntFunc(
+ sqlite3_context *pCtx,
+ int nVal,
+ sqlite3_value **apVal
+){
+ sqlite3rbu *p = (sqlite3rbu*)sqlite3_user_data(pCtx);
+ sqlite3_stmt *pStmt = 0;
+ char *zErrmsg = 0;
+ int rc;
+
+ assert( nVal==1 );
+
+ rc = prepareFreeAndCollectError(p->dbMain, &pStmt, &zErrmsg,
+ sqlite3_mprintf("SELECT count(*) FROM sqlite_master "
+ "WHERE type='index' AND tbl_name = %Q", sqlite3_value_text(apVal[0]))
+ );
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error(pCtx, zErrmsg, -1);
+ }else{
+ int nIndex = 0;
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ nIndex = sqlite3_column_int(pStmt, 0);
+ }
+ rc = sqlite3_finalize(pStmt);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_int(pCtx, nIndex);
+ }else{
+ sqlite3_result_error(pCtx, sqlite3_errmsg(p->dbMain), -1);
+ }
+ }
+
+ sqlite3_free(zErrmsg);
+}
+
+/*
+** If the RBU database contains the rbu_count table, use it to initialize
+** the sqlite3rbu.nPhaseOneStep variable. The schema of the rbu_count table
+** is assumed to contain the same columns as:
+**
+** CREATE TABLE rbu_count(tbl TEXT PRIMARY KEY, cnt INTEGER) WITHOUT ROWID;
+**
+** There should be one row in the table for each data_xxx table in the
+** database. The 'tbl' column should contain the name of a data_xxx table,
+** and the cnt column the number of rows it contains.
+**
+** sqlite3rbu.nPhaseOneStep is initialized to the sum of (1 + nIndex) * cnt
+** for all rows in the rbu_count table, where nIndex is the number of
+** indexes on the corresponding target database table.
+*/
+static void rbuInitPhaseOneSteps(sqlite3rbu *p){
+ if( p->rc==SQLITE_OK ){
+ sqlite3_stmt *pStmt = 0;
+ int bExists = 0; /* True if rbu_count exists */
+
+ p->nPhaseOneStep = -1;
+
+ p->rc = sqlite3_create_function(p->dbRbu,
+ "rbu_index_cnt", 1, SQLITE_UTF8, (void*)p, rbuIndexCntFunc, 0, 0
+ );
+
+ /* Check for the rbu_count table. If it does not exist, or if an error
+ ** occurs, nPhaseOneStep will be left set to -1. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ "SELECT 1 FROM sqlite_master WHERE tbl_name = 'rbu_count'"
+ );
+ }
+ if( p->rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ bExists = 1;
+ }
+ p->rc = sqlite3_finalize(pStmt);
+ }
+
+ if( p->rc==SQLITE_OK && bExists ){
+ p->rc = prepareAndCollectError(p->dbRbu, &pStmt, &p->zErrmsg,
+ "SELECT sum(cnt * (1 + rbu_index_cnt(rbu_target_name(tbl))))"
+ "FROM rbu_count"
+ );
+ if( p->rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ p->nPhaseOneStep = sqlite3_column_int64(pStmt, 0);
+ }
+ p->rc = sqlite3_finalize(pStmt);
+ }
+ }
+ }
+}
+
+
+static sqlite3rbu *openRbuHandle(
+ const char *zTarget,
+ const char *zRbu,
+ const char *zState
+){
+ sqlite3rbu *p;
+ size_t nTarget = zTarget ? strlen(zTarget) : 0;
+ size_t nRbu = strlen(zRbu);
+ size_t nByte = sizeof(sqlite3rbu) + nTarget+1 + nRbu+1;
+
+ p = (sqlite3rbu*)sqlite3_malloc64(nByte);
+ if( p ){
+ RbuState *pState = 0;
+
+ /* Create the custom VFS. */
+ memset(p, 0, sizeof(sqlite3rbu));
+ rbuCreateVfs(p);
+
+ /* Open the target, RBU and state databases */
+ if( p->rc==SQLITE_OK ){
+ char *pCsr = (char*)&p[1];
+ if( zTarget ){
+ p->zTarget = pCsr;
+ memcpy(p->zTarget, zTarget, nTarget+1);
+ pCsr += nTarget+1;
+ }
+ p->zRbu = pCsr;
+ memcpy(p->zRbu, zRbu, nRbu+1);
+ pCsr += nRbu+1;
+ if( zState ){
+ p->zState = rbuMPrintf(p, "%s", zState);
+ }
+ rbuOpenDatabase(p);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pState = rbuLoadState(p);
+ assert( pState || p->rc!=SQLITE_OK );
+ if( p->rc==SQLITE_OK ){
+
+ if( pState->eStage==0 ){
+ rbuDeleteOalFile(p);
+ rbuInitPhaseOneSteps(p);
+ p->eStage = RBU_STAGE_OAL;
+ }else{
+ p->eStage = pState->eStage;
+ p->nPhaseOneStep = pState->nPhaseOneStep;
+ }
+ p->nProgress = pState->nProgress;
+ p->iOalSz = pState->iOalSz;
+ }
+ }
+ assert( p->rc!=SQLITE_OK || p->eStage!=0 );
+
+ if( p->rc==SQLITE_OK && p->pTargetFd->pWalFd ){
+ if( p->eStage==RBU_STAGE_OAL ){
+ p->rc = SQLITE_ERROR;
+ p->zErrmsg = sqlite3_mprintf("cannot update wal mode database");
+ }else if( p->eStage==RBU_STAGE_MOVE ){
+ p->eStage = RBU_STAGE_CKPT;
+ p->nStep = 0;
+ }
+ }
+
+ if( p->rc==SQLITE_OK
+ && (p->eStage==RBU_STAGE_OAL || p->eStage==RBU_STAGE_MOVE)
+ && pState->eStage!=0
+ ){
+ rbu_file *pFd = (rbuIsVacuum(p) ? p->pRbuFd : p->pTargetFd);
+ if( pFd->iCookie!=pState->iCookie ){
+ /* At this point (pTargetFd->iCookie) contains the value of the
+ ** change-counter cookie (the thing that gets incremented when a
+ ** transaction is committed in rollback mode) currently stored on
+ ** page 1 of the database file. */
+ p->rc = SQLITE_BUSY;
+ p->zErrmsg = sqlite3_mprintf("database modified during rbu %s",
+ (rbuIsVacuum(p) ? "vacuum" : "update")
+ );
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ if( p->eStage==RBU_STAGE_OAL ){
+ sqlite3 *db = p->dbMain;
+ p->rc = sqlite3_exec(p->dbRbu, "BEGIN", 0, 0, &p->zErrmsg);
+
+ /* Point the object iterator at the first object */
+ if( p->rc==SQLITE_OK ){
+ p->rc = rbuObjIterFirst(p, &p->objiter);
+ }
+
+ /* If the RBU database contains no data_xxx tables, declare the RBU
+ ** update finished. */
+ if( p->rc==SQLITE_OK && p->objiter.zTbl==0 ){
+ p->rc = SQLITE_DONE;
+ p->eStage = RBU_STAGE_DONE;
+ }else{
+ if( p->rc==SQLITE_OK && pState->eStage==0 && rbuIsVacuum(p) ){
+ rbuCopyPragma(p, "page_size");
+ rbuCopyPragma(p, "auto_vacuum");
+ }
+
+ /* Open transactions both databases. The *-oal file is opened or
+ ** created at this point. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3_exec(db, "BEGIN IMMEDIATE", 0, 0, &p->zErrmsg);
+ }
+
+ /* Check if the main database is a zipvfs db. If it is, set the upper
+ ** level pager to use "journal_mode=off". This prevents it from
+ ** generating a large journal using a temp file. */
+ if( p->rc==SQLITE_OK ){
+ int frc = sqlite3_file_control(db, "main", SQLITE_FCNTL_ZIPVFS, 0);
+ if( frc==SQLITE_OK ){
+ p->rc = sqlite3_exec(
+ db, "PRAGMA journal_mode=off",0,0,&p->zErrmsg);
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ rbuSetupOal(p, pState);
+ }
+ }
+ }else if( p->eStage==RBU_STAGE_MOVE ){
+ /* no-op */
+ }else if( p->eStage==RBU_STAGE_CKPT ){
+ rbuSetupCheckpoint(p, pState);
+ }else if( p->eStage==RBU_STAGE_DONE ){
+ p->rc = SQLITE_DONE;
+ }else{
+ p->rc = SQLITE_CORRUPT;
+ }
+ }
+
+ rbuFreeState(pState);
+ }
+
+ return p;
+}
+
+/*
+** Allocate and return an RBU handle with all fields zeroed except for the
+** error code, which is set to SQLITE_MISUSE.
+*/
+static sqlite3rbu *rbuMisuseError(void){
+ sqlite3rbu *pRet;
+ pRet = sqlite3_malloc64(sizeof(sqlite3rbu));
+ if( pRet ){
+ memset(pRet, 0, sizeof(sqlite3rbu));
+ pRet->rc = SQLITE_MISUSE;
+ }
+ return pRet;
+}
+
+/*
+** Open and return a new RBU handle.
+*/
+SQLITE_API sqlite3rbu *sqlite3rbu_open(
+ const char *zTarget,
+ const char *zRbu,
+ const char *zState
+){
+ if( zTarget==0 || zRbu==0 ){ return rbuMisuseError(); }
+ /* TODO: Check that zTarget and zRbu are non-NULL */
+ return openRbuHandle(zTarget, zRbu, zState);
+}
+
+/*
+** Open a handle to begin or resume an RBU VACUUM operation.
+*/
+SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
+ const char *zTarget,
+ const char *zState
+){
+ if( zTarget==0 ){ return rbuMisuseError(); }
+ /* TODO: Check that both arguments are non-NULL */
+ return openRbuHandle(0, zTarget, zState);
+}
+
+/*
+** Return the database handle used by pRbu.
+*/
+SQLITE_API sqlite3 *sqlite3rbu_db(sqlite3rbu *pRbu, int bRbu){
+ sqlite3 *db = 0;
+ if( pRbu ){
+ db = (bRbu ? pRbu->dbRbu : pRbu->dbMain);
+ }
+ return db;
+}
+
+
+/*
+** If the error code currently stored in the RBU handle is SQLITE_CONSTRAINT,
+** then edit any error message string so as to remove all occurrences of
+** the pattern "rbu_imp_[0-9]*".
+*/
+static void rbuEditErrmsg(sqlite3rbu *p){
+ if( p->rc==SQLITE_CONSTRAINT && p->zErrmsg ){
+ unsigned int i;
+ size_t nErrmsg = strlen(p->zErrmsg);
+ for(i=0; i<(nErrmsg-8); i++){
+ if( memcmp(&p->zErrmsg[i], "rbu_imp_", 8)==0 ){
+ int nDel = 8;
+ while( p->zErrmsg[i+nDel]>='0' && p->zErrmsg[i+nDel]<='9' ) nDel++;
+ memmove(&p->zErrmsg[i], &p->zErrmsg[i+nDel], nErrmsg + 1 - i - nDel);
+ nErrmsg -= nDel;
+ }
+ }
+ }
+}
+
+/*
+** Close the RBU handle.
+*/
+SQLITE_API int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
+ int rc;
+ if( p ){
+
+ /* Commit the transaction to the *-oal file. */
+ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){
+ p->rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+
+ rbuSaveState(p, p->eStage);
+
+ if( p->rc==SQLITE_OK && p->eStage==RBU_STAGE_OAL ){
+ p->rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, &p->zErrmsg);
+ }
+
+ /* Close any open statement handles. */
+ rbuObjIterFinalize(&p->objiter);
+
+ /* If this is an RBU vacuum handle and the vacuum has either finished
+ ** successfully or encountered an error, delete the contents of the
+ ** state table. This causes the next call to sqlite3rbu_vacuum()
+ ** specifying the current target and state databases to start a new
+ ** vacuum from scratch. */
+ if( rbuIsVacuum(p) && p->rc!=SQLITE_OK && p->dbRbu ){
+ int rc2 = sqlite3_exec(p->dbRbu, "DELETE FROM stat.rbu_state", 0, 0, 0);
+ if( p->rc==SQLITE_DONE && rc2!=SQLITE_OK ) p->rc = rc2;
+ }
+
+ /* Close the open database handle and VFS object. */
+ sqlite3_close(p->dbRbu);
+ sqlite3_close(p->dbMain);
+ rbuDeleteVfs(p);
+ sqlite3_free(p->aBuf);
+ sqlite3_free(p->aFrame);
+
+ rbuEditErrmsg(p);
+ rc = p->rc;
+ *pzErrmsg = p->zErrmsg;
+ sqlite3_free(p->zState);
+ sqlite3_free(p);
+ }else{
+ rc = SQLITE_NOMEM;
+ *pzErrmsg = 0;
+ }
+ return rc;
+}
+
+/*
+** Return the total number of key-value operations (inserts, deletes or
+** updates) that have been performed on the target database since the
+** current RBU update was started.
+*/
+SQLITE_API sqlite3_int64 sqlite3rbu_progress(sqlite3rbu *pRbu){
+ return pRbu->nProgress;
+}
+
+/*
+** Return permyriadage progress indications for the two main stages of
+** an RBU update.
+*/
+SQLITE_API void sqlite3rbu_bp_progress(sqlite3rbu *p, int *pnOne, int *pnTwo){
+ const int MAX_PROGRESS = 10000;
+ switch( p->eStage ){
+ case RBU_STAGE_OAL:
+ if( p->nPhaseOneStep>0 ){
+ *pnOne = (int)(MAX_PROGRESS * (i64)p->nProgress/(i64)p->nPhaseOneStep);
+ }else{
+ *pnOne = -1;
+ }
+ *pnTwo = 0;
+ break;
+
+ case RBU_STAGE_MOVE:
+ *pnOne = MAX_PROGRESS;
+ *pnTwo = 0;
+ break;
+
+ case RBU_STAGE_CKPT:
+ *pnOne = MAX_PROGRESS;
+ *pnTwo = (int)(MAX_PROGRESS * (i64)p->nStep / (i64)p->nFrame);
+ break;
+
+ case RBU_STAGE_DONE:
+ *pnOne = MAX_PROGRESS;
+ *pnTwo = MAX_PROGRESS;
+ break;
+
+ default:
+ assert( 0 );
+ }
+}
+
+/*
+** Return the current state of the RBU vacuum or update operation.
+*/
+SQLITE_API int sqlite3rbu_state(sqlite3rbu *p){
+ int aRes[] = {
+ 0, SQLITE_RBU_STATE_OAL, SQLITE_RBU_STATE_MOVE,
+ 0, SQLITE_RBU_STATE_CHECKPOINT, SQLITE_RBU_STATE_DONE
+ };
+
+ assert( RBU_STAGE_OAL==1 );
+ assert( RBU_STAGE_MOVE==2 );
+ assert( RBU_STAGE_CKPT==4 );
+ assert( RBU_STAGE_DONE==5 );
+ assert( aRes[RBU_STAGE_OAL]==SQLITE_RBU_STATE_OAL );
+ assert( aRes[RBU_STAGE_MOVE]==SQLITE_RBU_STATE_MOVE );
+ assert( aRes[RBU_STAGE_CKPT]==SQLITE_RBU_STATE_CHECKPOINT );
+ assert( aRes[RBU_STAGE_DONE]==SQLITE_RBU_STATE_DONE );
+
+ if( p->rc!=SQLITE_OK && p->rc!=SQLITE_DONE ){
+ return SQLITE_RBU_STATE_ERROR;
+ }else{
+ assert( p->rc!=SQLITE_DONE || p->eStage==RBU_STAGE_DONE );
+ assert( p->eStage==RBU_STAGE_OAL
+ || p->eStage==RBU_STAGE_MOVE
+ || p->eStage==RBU_STAGE_CKPT
+ || p->eStage==RBU_STAGE_DONE
+ );
+ return aRes[p->eStage];
+ }
+}
+
+SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *p){
+ int rc = p->rc;
+ if( rc==SQLITE_DONE ) return SQLITE_OK;
+
+ assert( p->eStage>=RBU_STAGE_OAL && p->eStage<=RBU_STAGE_DONE );
+ if( p->eStage==RBU_STAGE_OAL ){
+ assert( rc!=SQLITE_DONE );
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "COMMIT", 0, 0, 0);
+ }
+
+ p->rc = rc;
+ rbuSaveState(p, p->eStage);
+ rc = p->rc;
+
+ if( p->eStage==RBU_STAGE_OAL ){
+ assert( rc!=SQLITE_DONE );
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "COMMIT", 0, 0, 0);
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbRbu, "BEGIN IMMEDIATE", 0, 0, 0);
+ if( rc==SQLITE_OK ) rc = sqlite3_exec(p->dbMain, "BEGIN IMMEDIATE", 0, 0,0);
+ }
+
+ p->rc = rc;
+ return rc;
+}
+
+/**************************************************************************
+** Beginning of RBU VFS shim methods. The VFS shim modifies the behaviour
+** of a standard VFS in the following ways:
+**
+** 1. Whenever the first page of a main database file is read or
+** written, the value of the change-counter cookie is stored in
+** rbu_file.iCookie. Similarly, the value of the "write-version"
+** database header field is stored in rbu_file.iWriteVer. This ensures
+** that the values are always trustworthy within an open transaction.
+**
+** 2. Whenever an SQLITE_OPEN_WAL file is opened, the (rbu_file.pWalFd)
+** member variable of the associated database file descriptor is set
+** to point to the new file. A mutex protected linked list of all main
+** db fds opened using a particular RBU VFS is maintained at
+** rbu_vfs.pMain to facilitate this.
+**
+** 3. Using a new file-control "SQLITE_FCNTL_RBU", a main db rbu_file
+** object can be marked as the target database of an RBU update. This
+** turns on the following extra special behaviour:
+**
+** 3a. If xAccess() is called to check if there exists a *-wal file
+** associated with an RBU target database currently in RBU_STAGE_OAL
+** stage (preparing the *-oal file), the following special handling
+** applies:
+**
+** * if the *-wal file does exist, return SQLITE_CANTOPEN. An RBU
+** target database may not be in wal mode already.
+**
+** * if the *-wal file does not exist, set the output parameter to
+** non-zero (to tell SQLite that it does exist) anyway.
+**
+** Then, when xOpen() is called to open the *-wal file associated with
+** the RBU target in RBU_STAGE_OAL stage, instead of opening the *-wal
+** file, the rbu vfs opens the corresponding *-oal file instead.
+**
+** 3b. The *-shm pages returned by xShmMap() for a target db file in
+** RBU_STAGE_OAL mode are actually stored in heap memory. This is to
+** avoid creating a *-shm file on disk. Additionally, xShmLock() calls
+** are no-ops on target database files in RBU_STAGE_OAL mode. This is
+** because assert() statements in some VFS implementations fail if
+** xShmLock() is called before xShmMap().
+**
+** 3c. If an EXCLUSIVE lock is attempted on a target database file in any
+** mode except RBU_STAGE_DONE (all work completed and checkpointed), it
+** fails with an SQLITE_BUSY error. This is to stop RBU connections
+** from automatically checkpointing a *-wal (or *-oal) file from within
+** sqlite3_close().
+**
+** 3d. In RBU_STAGE_CAPTURE mode, all xRead() calls on the wal file, and
+** all xWrite() calls on the target database file perform no IO.
+** Instead the frame and page numbers that would be read and written
+** are recorded. Additionally, successful attempts to obtain exclusive
+** xShmLock() WRITER, CHECKPOINTER and READ0 locks on the target
+** database file are recorded. xShmLock() calls to unlock the same
+** locks are no-ops (so that once obtained, these locks are never
+** relinquished). Finally, calls to xSync() on the target database
+** file fail with SQLITE_INTERNAL errors.
+*/
+
+static void rbuUnlockShm(rbu_file *p){
+ if( p->pRbu ){
+ int (*xShmLock)(sqlite3_file*,int,int,int) = p->pReal->pMethods->xShmLock;
+ int i;
+ for(i=0; i<SQLITE_SHM_NLOCK;i++){
+ if( (1<<i) & p->pRbu->mLock ){
+ xShmLock(p->pReal, i, 1, SQLITE_SHM_UNLOCK|SQLITE_SHM_EXCLUSIVE);
+ }
+ }
+ p->pRbu->mLock = 0;
+ }
+}
+
+/*
+** Close an rbu file.
+*/
+static int rbuVfsClose(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file*)pFile;
+ int rc;
+ int i;
+
+ /* Free the contents of the apShm[] array. And the array itself. */
+ for(i=0; i<p->nShm; i++){
+ sqlite3_free(p->apShm[i]);
+ }
+ sqlite3_free(p->apShm);
+ p->apShm = 0;
+ sqlite3_free(p->zDel);
+
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
+ rbu_file **pp;
+ sqlite3_mutex_enter(p->pRbuVfs->mutex);
+ for(pp=&p->pRbuVfs->pMain; *pp!=p; pp=&((*pp)->pMainNext));
+ *pp = p->pMainNext;
+ sqlite3_mutex_leave(p->pRbuVfs->mutex);
+ rbuUnlockShm(p);
+ p->pReal->pMethods->xShmUnmap(p->pReal, 0);
+ }
+
+ /* Close the underlying file handle */
+ rc = p->pReal->pMethods->xClose(p->pReal);
+ return rc;
+}
+
+
+/*
+** Read and return an unsigned 32-bit big-endian integer from the buffer
+** passed as the only argument.
+*/
+static u32 rbuGetU32(u8 *aBuf){
+ return ((u32)aBuf[0] << 24)
+ + ((u32)aBuf[1] << 16)
+ + ((u32)aBuf[2] << 8)
+ + ((u32)aBuf[3]);
+}
+
+/*
+** Write an unsigned 32-bit value in big-endian format to the supplied
+** buffer.
+*/
+static void rbuPutU32(u8 *aBuf, u32 iVal){
+ aBuf[0] = (iVal >> 24) & 0xFF;
+ aBuf[1] = (iVal >> 16) & 0xFF;
+ aBuf[2] = (iVal >> 8) & 0xFF;
+ aBuf[3] = (iVal >> 0) & 0xFF;
+}
+
+static void rbuPutU16(u8 *aBuf, u16 iVal){
+ aBuf[0] = (iVal >> 8) & 0xFF;
+ aBuf[1] = (iVal >> 0) & 0xFF;
+}
+
+/*
+** Read data from an rbuVfs-file.
+*/
+static int rbuVfsRead(
+ sqlite3_file *pFile,
+ void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc;
+
+ if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
+ assert( p->openFlags & SQLITE_OPEN_WAL );
+ rc = rbuCaptureWalRead(p->pRbu, iOfst, iAmt);
+ }else{
+ if( pRbu && pRbu->eStage==RBU_STAGE_OAL
+ && (p->openFlags & SQLITE_OPEN_WAL)
+ && iOfst>=pRbu->iOalSz
+ ){
+ rc = SQLITE_OK;
+ memset(zBuf, 0, iAmt);
+ }else{
+ rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
+#if 1
+ /* If this is being called to read the first page of the target
+ ** database as part of an rbu vacuum operation, synthesize the
+ ** contents of the first page if it does not yet exist. Otherwise,
+ ** SQLite will not check for a *-wal file. */
+ if( pRbu && rbuIsVacuum(pRbu)
+ && rc==SQLITE_IOERR_SHORT_READ && iOfst==0
+ && (p->openFlags & SQLITE_OPEN_MAIN_DB)
+ && pRbu->rc==SQLITE_OK
+ ){
+ sqlite3_file *pFd = (sqlite3_file*)pRbu->pRbuFd;
+ rc = pFd->pMethods->xRead(pFd, zBuf, iAmt, iOfst);
+ if( rc==SQLITE_OK ){
+ u8 *aBuf = (u8*)zBuf;
+ u32 iRoot = rbuGetU32(&aBuf[52]) ? 1 : 0;
+ rbuPutU32(&aBuf[52], iRoot); /* largest root page number */
+ rbuPutU32(&aBuf[36], 0); /* number of free pages */
+ rbuPutU32(&aBuf[32], 0); /* first page on free list trunk */
+ rbuPutU32(&aBuf[28], 1); /* size of db file in pages */
+ rbuPutU32(&aBuf[24], pRbu->pRbuFd->iCookie+1); /* Change counter */
+
+ if( iAmt>100 ){
+ memset(&aBuf[100], 0, iAmt-100);
+ rbuPutU16(&aBuf[105], iAmt & 0xFFFF);
+ aBuf[100] = 0x0D;
+ }
+ }
+ }
+#endif
+ }
+ if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
+ /* These look like magic numbers. But they are stable, as they are part
+ ** of the definition of the SQLite file format, which may not change. */
+ u8 *pBuf = (u8*)zBuf;
+ p->iCookie = rbuGetU32(&pBuf[24]);
+ p->iWriteVer = pBuf[19];
+ }
+ }
+ return rc;
+}
+
+/*
+** Write data to an rbuVfs-file.
+*/
+static int rbuVfsWrite(
+ sqlite3_file *pFile,
+ const void *zBuf,
+ int iAmt,
+ sqlite_int64 iOfst
+){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc;
+
+ if( pRbu && pRbu->eStage==RBU_STAGE_CAPTURE ){
+ assert( p->openFlags & SQLITE_OPEN_MAIN_DB );
+ rc = rbuCaptureDbWrite(p->pRbu, iOfst);
+ }else{
+ if( pRbu && pRbu->eStage==RBU_STAGE_OAL
+ && (p->openFlags & SQLITE_OPEN_WAL)
+ && iOfst>=pRbu->iOalSz
+ ){
+ pRbu->iOalSz = iAmt + iOfst;
+ }
+ rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
+ if( rc==SQLITE_OK && iOfst==0 && (p->openFlags & SQLITE_OPEN_MAIN_DB) ){
+ /* These look like magic numbers. But they are stable, as they are part
+ ** of the definition of the SQLite file format, which may not change. */
+ u8 *pBuf = (u8*)zBuf;
+ p->iCookie = rbuGetU32(&pBuf[24]);
+ p->iWriteVer = pBuf[19];
+ }
+ }
+ return rc;
+}
+
+/*
+** Truncate an rbuVfs-file.
+*/
+static int rbuVfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
+ rbu_file *p = (rbu_file*)pFile;
+ return p->pReal->pMethods->xTruncate(p->pReal, size);
+}
+
+/*
+** Sync an rbuVfs-file.
+*/
+static int rbuVfsSync(sqlite3_file *pFile, int flags){
+ rbu_file *p = (rbu_file *)pFile;
+ if( p->pRbu && p->pRbu->eStage==RBU_STAGE_CAPTURE ){
+ if( p->openFlags & SQLITE_OPEN_MAIN_DB ){
+ return SQLITE_INTERNAL;
+ }
+ return SQLITE_OK;
+ }
+ return p->pReal->pMethods->xSync(p->pReal, flags);
+}
+
+/*
+** Return the current file-size of an rbuVfs-file.
+*/
+static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
+ rbu_file *p = (rbu_file *)pFile;
+ int rc;
+ rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
+
+ /* If this is an RBU vacuum operation and this is the target database,
+ ** pretend that it has at least one page. Otherwise, SQLite will not
+ ** check for the existance of a *-wal file. rbuVfsRead() contains
+ ** similar logic. */
+ if( rc==SQLITE_OK && *pSize==0
+ && p->pRbu && rbuIsVacuum(p->pRbu)
+ && (p->openFlags & SQLITE_OPEN_MAIN_DB)
+ ){
+ *pSize = 1024;
+ }
+ return rc;
+}
+
+/*
+** Lock an rbuVfs-file.
+*/
+static int rbuVfsLock(sqlite3_file *pFile, int eLock){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc = SQLITE_OK;
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( eLock==SQLITE_LOCK_EXCLUSIVE
+ && (p->bNolock || (pRbu && pRbu->eStage!=RBU_STAGE_DONE))
+ ){
+ /* Do not allow EXCLUSIVE locks. Preventing SQLite from taking this
+ ** prevents it from checkpointing the database from sqlite3_close(). */
+ rc = SQLITE_BUSY;
+ }else{
+ rc = p->pReal->pMethods->xLock(p->pReal, eLock);
+ }
+
+ return rc;
+}
+
+/*
+** Unlock an rbuVfs-file.
+*/
+static int rbuVfsUnlock(sqlite3_file *pFile, int eLock){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xUnlock(p->pReal, eLock);
+}
+
+/*
+** Check if another file-handle holds a RESERVED lock on an rbuVfs-file.
+*/
+static int rbuVfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
+}
+
+/*
+** File control method. For custom operations on an rbuVfs-file.
+*/
+static int rbuVfsFileControl(sqlite3_file *pFile, int op, void *pArg){
+ rbu_file *p = (rbu_file *)pFile;
+ int (*xControl)(sqlite3_file*,int,void*) = p->pReal->pMethods->xFileControl;
+ int rc;
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB)
+ || p->openFlags & (SQLITE_OPEN_TRANSIENT_DB|SQLITE_OPEN_TEMP_JOURNAL)
+ );
+ if( op==SQLITE_FCNTL_RBU ){
+ sqlite3rbu *pRbu = (sqlite3rbu*)pArg;
+
+ /* First try to find another RBU vfs lower down in the vfs stack. If
+ ** one is found, this vfs will operate in pass-through mode. The lower
+ ** level vfs will do the special RBU handling. */
+ rc = xControl(p->pReal, op, pArg);
+
+ if( rc==SQLITE_NOTFOUND ){
+ /* Now search for a zipvfs instance lower down in the VFS stack. If
+ ** one is found, this is an error. */
+ void *dummy = 0;
+ rc = xControl(p->pReal, SQLITE_FCNTL_ZIPVFS, &dummy);
+ if( rc==SQLITE_OK ){
+ rc = SQLITE_ERROR;
+ pRbu->zErrmsg = sqlite3_mprintf("rbu/zipvfs setup error");
+ }else if( rc==SQLITE_NOTFOUND ){
+ pRbu->pTargetFd = p;
+ p->pRbu = pRbu;
+ if( p->pWalFd ) p->pWalFd->pRbu = pRbu;
+ rc = SQLITE_OK;
+ }
+ }
+ return rc;
+ }
+ else if( op==SQLITE_FCNTL_RBUCNT ){
+ sqlite3rbu *pRbu = (sqlite3rbu*)pArg;
+ pRbu->nRbu++;
+ pRbu->pRbuFd = p;
+ p->bNolock = 1;
+ }
+
+ rc = xControl(p->pReal, op, pArg);
+ if( rc==SQLITE_OK && op==SQLITE_FCNTL_VFSNAME ){
+ rbu_vfs *pRbuVfs = p->pRbuVfs;
+ char *zIn = *(char**)pArg;
+ char *zOut = sqlite3_mprintf("rbu(%s)/%z", pRbuVfs->base.zName, zIn);
+ *(char**)pArg = zOut;
+ if( zOut==0 ) rc = SQLITE_NOMEM;
+ }
+
+ return rc;
+}
+
+/*
+** Return the sector-size in bytes for an rbuVfs-file.
+*/
+static int rbuVfsSectorSize(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xSectorSize(p->pReal);
+}
+
+/*
+** Return the device characteristic flags supported by an rbuVfs-file.
+*/
+static int rbuVfsDeviceCharacteristics(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file *)pFile;
+ return p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
+}
+
+/*
+** Take or release a shared-memory lock.
+*/
+static int rbuVfsShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
+ rbu_file *p = (rbu_file*)pFile;
+ sqlite3rbu *pRbu = p->pRbu;
+ int rc = SQLITE_OK;
+
+#ifdef SQLITE_AMALGAMATION
+ assert( WAL_CKPT_LOCK==1 );
+#endif
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( pRbu && (pRbu->eStage==RBU_STAGE_OAL || pRbu->eStage==RBU_STAGE_MOVE) ){
+ /* Magic number 1 is the WAL_CKPT_LOCK lock. Preventing SQLite from
+ ** taking this lock also prevents any checkpoints from occurring.
+ ** todo: really, it's not clear why this might occur, as
+ ** wal_autocheckpoint ought to be turned off. */
+ if( ofst==WAL_LOCK_CKPT && n==1 ) rc = SQLITE_BUSY;
+ }else{
+ int bCapture = 0;
+ if( n==1 && (flags & SQLITE_SHM_EXCLUSIVE)
+ && pRbu && pRbu->eStage==RBU_STAGE_CAPTURE
+ && (ofst==WAL_LOCK_WRITE || ofst==WAL_LOCK_CKPT || ofst==WAL_LOCK_READ0)
+ ){
+ bCapture = 1;
+ }
+
+ if( bCapture==0 || 0==(flags & SQLITE_SHM_UNLOCK) ){
+ rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
+ if( bCapture && rc==SQLITE_OK ){
+ pRbu->mLock |= (1 << ofst);
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Obtain a pointer to a mapping of a single 32KiB page of the *-shm file.
+*/
+static int rbuVfsShmMap(
+ sqlite3_file *pFile,
+ int iRegion,
+ int szRegion,
+ int isWrite,
+ void volatile **pp
+){
+ rbu_file *p = (rbu_file*)pFile;
+ int rc = SQLITE_OK;
+ int eStage = (p->pRbu ? p->pRbu->eStage : 0);
+
+ /* If not in RBU_STAGE_OAL, allow this call to pass through. Or, if this
+ ** rbu is in the RBU_STAGE_OAL state, use heap memory for *-shm space
+ ** instead of a file on disk. */
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
+ if( iRegion<=p->nShm ){
+ int nByte = (iRegion+1) * sizeof(char*);
+ char **apNew = (char**)sqlite3_realloc64(p->apShm, nByte);
+ if( apNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(&apNew[p->nShm], 0, sizeof(char*) * (1 + iRegion - p->nShm));
+ p->apShm = apNew;
+ p->nShm = iRegion+1;
+ }
+ }
+
+ if( rc==SQLITE_OK && p->apShm[iRegion]==0 ){
+ char *pNew = (char*)sqlite3_malloc64(szRegion);
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pNew, 0, szRegion);
+ p->apShm[iRegion] = pNew;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ *pp = p->apShm[iRegion];
+ }else{
+ *pp = 0;
+ }
+ }else{
+ assert( p->apShm==0 );
+ rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
+ }
+
+ return rc;
+}
+
+/*
+** Memory barrier.
+*/
+static void rbuVfsShmBarrier(sqlite3_file *pFile){
+ rbu_file *p = (rbu_file *)pFile;
+ p->pReal->pMethods->xShmBarrier(p->pReal);
+}
+
+/*
+** The xShmUnmap method.
+*/
+static int rbuVfsShmUnmap(sqlite3_file *pFile, int delFlag){
+ rbu_file *p = (rbu_file*)pFile;
+ int rc = SQLITE_OK;
+ int eStage = (p->pRbu ? p->pRbu->eStage : 0);
+
+ assert( p->openFlags & (SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_TEMP_DB) );
+ if( eStage==RBU_STAGE_OAL || eStage==RBU_STAGE_MOVE ){
+ /* no-op */
+ }else{
+ /* Release the checkpointer and writer locks */
+ rbuUnlockShm(p);
+ rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
+ }
+ return rc;
+}
+
+/*
+** Given that zWal points to a buffer containing a wal file name passed to
+** either the xOpen() or xAccess() VFS method, return a pointer to the
+** file-handle opened by the same database connection on the corresponding
+** database file.
+*/
+static rbu_file *rbuFindMaindb(rbu_vfs *pRbuVfs, const char *zWal){
+ rbu_file *pDb;
+ sqlite3_mutex_enter(pRbuVfs->mutex);
+ for(pDb=pRbuVfs->pMain; pDb && pDb->zWal!=zWal; pDb=pDb->pMainNext){}
+ sqlite3_mutex_leave(pRbuVfs->mutex);
+ return pDb;
+}
+
+/*
+** A main database named zName has just been opened. The following
+** function returns a pointer to a buffer owned by SQLite that contains
+** the name of the *-wal file this db connection will use. SQLite
+** happens to pass a pointer to this buffer when using xAccess()
+** or xOpen() to operate on the *-wal file.
+*/
+static const char *rbuMainToWal(const char *zName, int flags){
+ int n = (int)strlen(zName);
+ const char *z = &zName[n];
+ if( flags & SQLITE_OPEN_URI ){
+ int odd = 0;
+ while( 1 ){
+ if( z[0]==0 ){
+ odd = 1 - odd;
+ if( odd && z[1]==0 ) break;
+ }
+ z++;
+ }
+ z += 2;
+ }else{
+ while( *z==0 ) z++;
+ }
+ z += (n + 8 + 1);
+ return z;
+}
+
+/*
+** Open an rbu file handle.
+*/
+static int rbuVfsOpen(
+ sqlite3_vfs *pVfs,
+ const char *zName,
+ sqlite3_file *pFile,
+ int flags,
+ int *pOutFlags
+){
+ static sqlite3_io_methods rbuvfs_io_methods = {
+ 2, /* iVersion */
+ rbuVfsClose, /* xClose */
+ rbuVfsRead, /* xRead */
+ rbuVfsWrite, /* xWrite */
+ rbuVfsTruncate, /* xTruncate */
+ rbuVfsSync, /* xSync */
+ rbuVfsFileSize, /* xFileSize */
+ rbuVfsLock, /* xLock */
+ rbuVfsUnlock, /* xUnlock */
+ rbuVfsCheckReservedLock, /* xCheckReservedLock */
+ rbuVfsFileControl, /* xFileControl */
+ rbuVfsSectorSize, /* xSectorSize */
+ rbuVfsDeviceCharacteristics, /* xDeviceCharacteristics */
+ rbuVfsShmMap, /* xShmMap */
+ rbuVfsShmLock, /* xShmLock */
+ rbuVfsShmBarrier, /* xShmBarrier */
+ rbuVfsShmUnmap, /* xShmUnmap */
+ 0, 0 /* xFetch, xUnfetch */
+ };
+ rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
+ sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
+ rbu_file *pFd = (rbu_file *)pFile;
+ int rc = SQLITE_OK;
+ const char *zOpen = zName;
+ int oflags = flags;
+
+ memset(pFd, 0, sizeof(rbu_file));
+ pFd->pReal = (sqlite3_file*)&pFd[1];
+ pFd->pRbuVfs = pRbuVfs;
+ pFd->openFlags = flags;
+ if( zName ){
+ if( flags & SQLITE_OPEN_MAIN_DB ){
+ /* A main database has just been opened. The following block sets
+ ** (pFd->zWal) to point to a buffer owned by SQLite that contains
+ ** the name of the *-wal file this db connection will use. SQLite
+ ** happens to pass a pointer to this buffer when using xAccess()
+ ** or xOpen() to operate on the *-wal file. */
+ pFd->zWal = rbuMainToWal(zName, flags);
+ }
+ else if( flags & SQLITE_OPEN_WAL ){
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zName);
+ if( pDb ){
+ if( pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
+ /* This call is to open a *-wal file. Intead, open the *-oal. This
+ ** code ensures that the string passed to xOpen() is terminated by a
+ ** pair of '\0' bytes in case the VFS attempts to extract a URI
+ ** parameter from it. */
+ const char *zBase = zName;
+ size_t nCopy;
+ char *zCopy;
+ if( rbuIsVacuum(pDb->pRbu) ){
+ zBase = sqlite3_db_filename(pDb->pRbu->dbRbu, "main");
+ zBase = rbuMainToWal(zBase, SQLITE_OPEN_URI);
+ }
+ nCopy = strlen(zBase);
+ zCopy = sqlite3_malloc64(nCopy+2);
+ if( zCopy ){
+ memcpy(zCopy, zBase, nCopy);
+ zCopy[nCopy-3] = 'o';
+ zCopy[nCopy] = '\0';
+ zCopy[nCopy+1] = '\0';
+ zOpen = (const char*)(pFd->zDel = zCopy);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ pFd->pRbu = pDb->pRbu;
+ }
+ pDb->pWalFd = pFd;
+ }
+ }
+ }
+
+ if( oflags & SQLITE_OPEN_MAIN_DB
+ && sqlite3_uri_boolean(zName, "rbu_memory", 0)
+ ){
+ assert( oflags & SQLITE_OPEN_MAIN_DB );
+ oflags = SQLITE_OPEN_TEMP_DB | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE |
+ SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_DELETEONCLOSE;
+ zOpen = 0;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pRealVfs->xOpen(pRealVfs, zOpen, pFd->pReal, oflags, pOutFlags);
+ }
+ if( pFd->pReal->pMethods ){
+ /* The xOpen() operation has succeeded. Set the sqlite3_file.pMethods
+ ** pointer and, if the file is a main database file, link it into the
+ ** mutex protected linked list of all such files. */
+ pFile->pMethods = &rbuvfs_io_methods;
+ if( flags & SQLITE_OPEN_MAIN_DB ){
+ sqlite3_mutex_enter(pRbuVfs->mutex);
+ pFd->pMainNext = pRbuVfs->pMain;
+ pRbuVfs->pMain = pFd;
+ sqlite3_mutex_leave(pRbuVfs->mutex);
+ }
+ }else{
+ sqlite3_free(pFd->zDel);
+ }
+
+ return rc;
+}
+
+/*
+** Delete the file located at zPath.
+*/
+static int rbuVfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xDelete(pRealVfs, zPath, dirSync);
+}
+
+/*
+** Test for access permissions. Return true if the requested permission
+** is available, or false otherwise.
+*/
+static int rbuVfsAccess(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int flags,
+ int *pResOut
+){
+ rbu_vfs *pRbuVfs = (rbu_vfs*)pVfs;
+ sqlite3_vfs *pRealVfs = pRbuVfs->pRealVfs;
+ int rc;
+
+ rc = pRealVfs->xAccess(pRealVfs, zPath, flags, pResOut);
+
+ /* If this call is to check if a *-wal file associated with an RBU target
+ ** database connection exists, and the RBU update is in RBU_STAGE_OAL,
+ ** the following special handling is activated:
+ **
+ ** a) if the *-wal file does exist, return SQLITE_CANTOPEN. This
+ ** ensures that the RBU extension never tries to update a database
+ ** in wal mode, even if the first page of the database file has
+ ** been damaged.
+ **
+ ** b) if the *-wal file does not exist, claim that it does anyway,
+ ** causing SQLite to call xOpen() to open it. This call will also
+ ** be intercepted (see the rbuVfsOpen() function) and the *-oal
+ ** file opened instead.
+ */
+ if( rc==SQLITE_OK && flags==SQLITE_ACCESS_EXISTS ){
+ rbu_file *pDb = rbuFindMaindb(pRbuVfs, zPath);
+ if( pDb && pDb->pRbu && pDb->pRbu->eStage==RBU_STAGE_OAL ){
+ if( *pResOut ){
+ rc = SQLITE_CANTOPEN;
+ }else{
+ *pResOut = 1;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Populate buffer zOut with the full canonical pathname corresponding
+** to the pathname in zPath. zOut is guaranteed to point to a buffer
+** of at least (DEVSYM_MAX_PATHNAME+1) bytes.
+*/
+static int rbuVfsFullPathname(
+ sqlite3_vfs *pVfs,
+ const char *zPath,
+ int nOut,
+ char *zOut
+){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xFullPathname(pRealVfs, zPath, nOut, zOut);
+}
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+/*
+** Open the dynamic library located at zPath and return a handle.
+*/
+static void *rbuVfsDlOpen(sqlite3_vfs *pVfs, const char *zPath){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xDlOpen(pRealVfs, zPath);
+}
+
+/*
+** Populate the buffer zErrMsg (size nByte bytes) with a human readable
+** utf-8 string describing the most recent error encountered associated
+** with dynamic libraries.
+*/
+static void rbuVfsDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ pRealVfs->xDlError(pRealVfs, nByte, zErrMsg);
+}
+
+/*
+** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
+*/
+static void (*rbuVfsDlSym(
+ sqlite3_vfs *pVfs,
+ void *pArg,
+ const char *zSym
+))(void){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xDlSym(pRealVfs, pArg, zSym);
+}
+
+/*
+** Close the dynamic library handle pHandle.
+*/
+static void rbuVfsDlClose(sqlite3_vfs *pVfs, void *pHandle){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ pRealVfs->xDlClose(pRealVfs, pHandle);
+}
+#endif /* SQLITE_OMIT_LOAD_EXTENSION */
+
+/*
+** Populate the buffer pointed to by zBufOut with nByte bytes of
+** random data.
+*/
+static int rbuVfsRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xRandomness(pRealVfs, nByte, zBufOut);
+}
+
+/*
+** Sleep for nMicro microseconds. Return the number of microseconds
+** actually slept.
+*/
+static int rbuVfsSleep(sqlite3_vfs *pVfs, int nMicro){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xSleep(pRealVfs, nMicro);
+}
+
+/*
+** Return the current time as a Julian Day number in *pTimeOut.
+*/
+static int rbuVfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
+ sqlite3_vfs *pRealVfs = ((rbu_vfs*)pVfs)->pRealVfs;
+ return pRealVfs->xCurrentTime(pRealVfs, pTimeOut);
+}
+
+/*
+** No-op.
+*/
+static int rbuVfsGetLastError(sqlite3_vfs *pVfs, int a, char *b){
+ return 0;
+}
+
+/*
+** Deregister and destroy an RBU vfs created by an earlier call to
+** sqlite3rbu_create_vfs().
+*/
+SQLITE_API void sqlite3rbu_destroy_vfs(const char *zName){
+ sqlite3_vfs *pVfs = sqlite3_vfs_find(zName);
+ if( pVfs && pVfs->xOpen==rbuVfsOpen ){
+ sqlite3_mutex_free(((rbu_vfs*)pVfs)->mutex);
+ sqlite3_vfs_unregister(pVfs);
+ sqlite3_free(pVfs);
+ }
+}
+
+/*
+** Create an RBU VFS named zName that accesses the underlying file-system
+** via existing VFS zParent. The new object is registered as a non-default
+** VFS with SQLite before returning.
+*/
+SQLITE_API int sqlite3rbu_create_vfs(const char *zName, const char *zParent){
+
+ /* Template for VFS */
+ static sqlite3_vfs vfs_template = {
+ 1, /* iVersion */
+ 0, /* szOsFile */
+ 0, /* mxPathname */
+ 0, /* pNext */
+ 0, /* zName */
+ 0, /* pAppData */
+ rbuVfsOpen, /* xOpen */
+ rbuVfsDelete, /* xDelete */
+ rbuVfsAccess, /* xAccess */
+ rbuVfsFullPathname, /* xFullPathname */
+
+#ifndef SQLITE_OMIT_LOAD_EXTENSION
+ rbuVfsDlOpen, /* xDlOpen */
+ rbuVfsDlError, /* xDlError */
+ rbuVfsDlSym, /* xDlSym */
+ rbuVfsDlClose, /* xDlClose */
+#else
+ 0, 0, 0, 0,
+#endif
+
+ rbuVfsRandomness, /* xRandomness */
+ rbuVfsSleep, /* xSleep */
+ rbuVfsCurrentTime, /* xCurrentTime */
+ rbuVfsGetLastError, /* xGetLastError */
+ 0, /* xCurrentTimeInt64 (version 2) */
+ 0, 0, 0 /* Unimplemented version 3 methods */
+ };
+
+ rbu_vfs *pNew = 0; /* Newly allocated VFS */
+ int rc = SQLITE_OK;
+ size_t nName;
+ size_t nByte;
+
+ nName = strlen(zName);
+ nByte = sizeof(rbu_vfs) + nName + 1;
+ pNew = (rbu_vfs*)sqlite3_malloc64(nByte);
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_vfs *pParent; /* Parent VFS */
+ memset(pNew, 0, nByte);
+ pParent = sqlite3_vfs_find(zParent);
+ if( pParent==0 ){
+ rc = SQLITE_NOTFOUND;
+ }else{
+ char *zSpace;
+ memcpy(&pNew->base, &vfs_template, sizeof(sqlite3_vfs));
+ pNew->base.mxPathname = pParent->mxPathname;
+ pNew->base.szOsFile = sizeof(rbu_file) + pParent->szOsFile;
+ pNew->pRealVfs = pParent;
+ pNew->base.zName = (const char*)(zSpace = (char*)&pNew[1]);
+ memcpy(zSpace, zName, nName);
+
+ /* Allocate the mutex and register the new VFS (not as the default) */
+ pNew->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_RECURSIVE);
+ if( pNew->mutex==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_vfs_register(&pNew->base, 0);
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_mutex_free(pNew->mutex);
+ sqlite3_free(pNew);
+ }
+ }
+
+ return rc;
+}
+
+
+/**************************************************************************/
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RBU) */
+
+/************** End of sqlite3rbu.c ******************************************/
+/************** Begin file dbstat.c ******************************************/
+/*
+** 2010 July 12
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains an implementation of the "dbstat" virtual table.
+**
+** The dbstat virtual table is used to extract low-level formatting
+** information from an SQLite database in order to implement the
+** "sqlite3_analyzer" utility. See the ../tool/spaceanal.tcl script
+** for an example implementation.
+**
+** Additional information is available on the "dbstat.html" page of the
+** official SQLite documentation.
+*/
+
+/* #include "sqliteInt.h" ** Requires access to internal data structures ** */
+#if (defined(SQLITE_ENABLE_DBSTAT_VTAB) || defined(SQLITE_TEST)) \
+ && !defined(SQLITE_OMIT_VIRTUALTABLE)
+
+/*
+** Page paths:
+**
+** The value of the 'path' column describes the path taken from the
+** root-node of the b-tree structure to each page. The value of the
+** root-node path is '/'.
+**
+** The value of the path for the left-most child page of the root of
+** a b-tree is '/000/'. (Btrees store content ordered from left to right
+** so the pages to the left have smaller keys than the pages to the right.)
+** The next to left-most child of the root page is
+** '/001', and so on, each sibling page identified by a 3-digit hex
+** value. The children of the 451st left-most sibling have paths such
+** as '/1c2/000/, '/1c2/001/' etc.
+**
+** Overflow pages are specified by appending a '+' character and a
+** six-digit hexadecimal value to the path to the cell they are linked
+** from. For example, the three overflow pages in a chain linked from
+** the left-most cell of the 450th child of the root page are identified
+** by the paths:
+**
+** '/1c2/000+000000' // First page in overflow chain
+** '/1c2/000+000001' // Second page in overflow chain
+** '/1c2/000+000002' // Third page in overflow chain
+**
+** If the paths are sorted using the BINARY collation sequence, then
+** the overflow pages associated with a cell will appear earlier in the
+** sort-order than its child page:
+**
+** '/1c2/000/' // Left-most child of 451st child of root
+*/
+#define VTAB_SCHEMA \
+ "CREATE TABLE xx( " \
+ " name TEXT, /* Name of table or index */" \
+ " path TEXT, /* Path to page from root */" \
+ " pageno INTEGER, /* Page number */" \
+ " pagetype TEXT, /* 'internal', 'leaf' or 'overflow' */" \
+ " ncell INTEGER, /* Cells on page (0 for overflow) */" \
+ " payload INTEGER, /* Bytes of payload on this page */" \
+ " unused INTEGER, /* Bytes of unused space on this page */" \
+ " mx_payload INTEGER, /* Largest payload size of all cells */" \
+ " pgoffset INTEGER, /* Offset of page in file */" \
+ " pgsize INTEGER, /* Size of the page */" \
+ " schema TEXT HIDDEN /* Database schema being analyzed */" \
+ ");"
+
+
+typedef struct StatTable StatTable;
+typedef struct StatCursor StatCursor;
+typedef struct StatPage StatPage;
+typedef struct StatCell StatCell;
+
+struct StatCell {
+ int nLocal; /* Bytes of local payload */
+ u32 iChildPg; /* Child node (or 0 if this is a leaf) */
+ int nOvfl; /* Entries in aOvfl[] */
+ u32 *aOvfl; /* Array of overflow page numbers */
+ int nLastOvfl; /* Bytes of payload on final overflow page */
+ int iOvfl; /* Iterates through aOvfl[] */
+};
+
+struct StatPage {
+ u32 iPgno;
+ DbPage *pPg;
+ int iCell;
+
+ char *zPath; /* Path to this page */
+
+ /* Variables populated by statDecodePage(): */
+ u8 flags; /* Copy of flags byte */
+ int nCell; /* Number of cells on page */
+ int nUnused; /* Number of unused bytes on page */
+ StatCell *aCell; /* Array of parsed cells */
+ u32 iRightChildPg; /* Right-child page number (or 0) */
+ int nMxPayload; /* Largest payload of any cell on this page */
+};
+
+struct StatCursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_stmt *pStmt; /* Iterates through set of root pages */
+ int isEof; /* After pStmt has returned SQLITE_DONE */
+ int iDb; /* Schema used for this query */
+
+ StatPage aPage[32];
+ int iPage; /* Current entry in aPage[] */
+
+ /* Values to return. */
+ char *zName; /* Value of 'name' column */
+ char *zPath; /* Value of 'path' column */
+ u32 iPageno; /* Value of 'pageno' column */
+ char *zPagetype; /* Value of 'pagetype' column */
+ int nCell; /* Value of 'ncell' column */
+ int nPayload; /* Value of 'payload' column */
+ int nUnused; /* Value of 'unused' column */
+ int nMxPayload; /* Value of 'mx_payload' column */
+ i64 iOffset; /* Value of 'pgOffset' column */
+ int szPage; /* Value of 'pgSize' column */
+};
+
+struct StatTable {
+ sqlite3_vtab base;
+ sqlite3 *db;
+ int iDb; /* Index of database to analyze */
+};
+
+#ifndef get2byte
+# define get2byte(x) ((x)[0]<<8 | (x)[1])
+#endif
+
+/*
+** Connect to or create a statvfs virtual table.
+*/
+static int statConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ StatTable *pTab = 0;
+ int rc = SQLITE_OK;
+ int iDb;
+
+ if( argc>=4 ){
+ Token nm;
+ sqlite3TokenInit(&nm, (char*)argv[3]);
+ iDb = sqlite3FindDb(db, &nm);
+ if( iDb<0 ){
+ *pzErr = sqlite3_mprintf("no such database: %s", argv[3]);
+ return SQLITE_ERROR;
+ }
+ }else{
+ iDb = 0;
+ }
+ rc = sqlite3_declare_vtab(db, VTAB_SCHEMA);
+ if( rc==SQLITE_OK ){
+ pTab = (StatTable *)sqlite3_malloc64(sizeof(StatTable));
+ if( pTab==0 ) rc = SQLITE_NOMEM_BKPT;
+ }
+
+ assert( rc==SQLITE_OK || pTab==0 );
+ if( rc==SQLITE_OK ){
+ memset(pTab, 0, sizeof(StatTable));
+ pTab->db = db;
+ pTab->iDb = iDb;
+ }
+
+ *ppVtab = (sqlite3_vtab*)pTab;
+ return rc;
+}
+
+/*
+** Disconnect from or destroy a statvfs virtual table.
+*/
+static int statDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** There is no "best-index". This virtual table always does a linear
+** scan. However, a schema=? constraint should cause this table to
+** operate on a different database schema, so check for it.
+**
+** idxNum is normally 0, but will be 1 if a schema=? constraint exists.
+*/
+static int statBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
+ int i;
+
+ pIdxInfo->estimatedCost = 1.0e6; /* Initial cost estimate */
+
+ /* Look for a valid schema=? constraint. If found, change the idxNum to
+ ** 1 and request the value of that constraint be sent to xFilter. And
+ ** lower the cost estimate to encourage the constrained version to be
+ ** used.
+ */
+ for(i=0; i<pIdxInfo->nConstraint; i++){
+ if( pIdxInfo->aConstraint[i].usable==0 ) continue;
+ if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ if( pIdxInfo->aConstraint[i].iColumn!=10 ) continue;
+ pIdxInfo->idxNum = 1;
+ pIdxInfo->estimatedCost = 1.0;
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[i].omit = 1;
+ break;
+ }
+
+
+ /* Records are always returned in ascending order of (name, path).
+ ** If this will satisfy the client, set the orderByConsumed flag so that
+ ** SQLite does not do an external sort.
+ */
+ if( ( pIdxInfo->nOrderBy==1
+ && pIdxInfo->aOrderBy[0].iColumn==0
+ && pIdxInfo->aOrderBy[0].desc==0
+ ) ||
+ ( pIdxInfo->nOrderBy==2
+ && pIdxInfo->aOrderBy[0].iColumn==0
+ && pIdxInfo->aOrderBy[0].desc==0
+ && pIdxInfo->aOrderBy[1].iColumn==1
+ && pIdxInfo->aOrderBy[1].desc==0
+ )
+ ){
+ pIdxInfo->orderByConsumed = 1;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Open a new statvfs cursor.
+*/
+static int statOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){
+ StatTable *pTab = (StatTable *)pVTab;
+ StatCursor *pCsr;
+
+ pCsr = (StatCursor *)sqlite3_malloc64(sizeof(StatCursor));
+ if( pCsr==0 ){
+ return SQLITE_NOMEM_BKPT;
+ }else{
+ memset(pCsr, 0, sizeof(StatCursor));
+ pCsr->base.pVtab = pVTab;
+ pCsr->iDb = pTab->iDb;
+ }
+
+ *ppCursor = (sqlite3_vtab_cursor *)pCsr;
+ return SQLITE_OK;
+}
+
+static void statClearPage(StatPage *p){
+ int i;
+ if( p->aCell ){
+ for(i=0; i<p->nCell; i++){
+ sqlite3_free(p->aCell[i].aOvfl);
+ }
+ sqlite3_free(p->aCell);
+ }
+ sqlite3PagerUnref(p->pPg);
+ sqlite3_free(p->zPath);
+ memset(p, 0, sizeof(StatPage));
+}
+
+static void statResetCsr(StatCursor *pCsr){
+ int i;
+ sqlite3_reset(pCsr->pStmt);
+ for(i=0; i<ArraySize(pCsr->aPage); i++){
+ statClearPage(&pCsr->aPage[i]);
+ }
+ pCsr->iPage = 0;
+ sqlite3_free(pCsr->zPath);
+ pCsr->zPath = 0;
+ pCsr->isEof = 0;
+}
+
+/*
+** Close a statvfs cursor.
+*/
+static int statClose(sqlite3_vtab_cursor *pCursor){
+ StatCursor *pCsr = (StatCursor *)pCursor;
+ statResetCsr(pCsr);
+ sqlite3_finalize(pCsr->pStmt);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+static void getLocalPayload(
+ int nUsable, /* Usable bytes per page */
+ u8 flags, /* Page flags */
+ int nTotal, /* Total record (payload) size */
+ int *pnLocal /* OUT: Bytes stored locally */
+){
+ int nLocal;
+ int nMinLocal;
+ int nMaxLocal;
+
+ if( flags==0x0D ){ /* Table leaf node */
+ nMinLocal = (nUsable - 12) * 32 / 255 - 23;
+ nMaxLocal = nUsable - 35;
+ }else{ /* Index interior and leaf nodes */
+ nMinLocal = (nUsable - 12) * 32 / 255 - 23;
+ nMaxLocal = (nUsable - 12) * 64 / 255 - 23;
+ }
+
+ nLocal = nMinLocal + (nTotal - nMinLocal) % (nUsable - 4);
+ if( nLocal>nMaxLocal ) nLocal = nMinLocal;
+ *pnLocal = nLocal;
+}
+
+static int statDecodePage(Btree *pBt, StatPage *p){
+ int nUnused;
+ int iOff;
+ int nHdr;
+ int isLeaf;
+ int szPage;
+
+ u8 *aData = sqlite3PagerGetData(p->pPg);
+ u8 *aHdr = &aData[p->iPgno==1 ? 100 : 0];
+
+ p->flags = aHdr[0];
+ p->nCell = get2byte(&aHdr[3]);
+ p->nMxPayload = 0;
+
+ isLeaf = (p->flags==0x0A || p->flags==0x0D);
+ nHdr = 12 - isLeaf*4 + (p->iPgno==1)*100;
+
+ nUnused = get2byte(&aHdr[5]) - nHdr - 2*p->nCell;
+ nUnused += (int)aHdr[7];
+ iOff = get2byte(&aHdr[1]);
+ while( iOff ){
+ nUnused += get2byte(&aData[iOff+2]);
+ iOff = get2byte(&aData[iOff]);
+ }
+ p->nUnused = nUnused;
+ p->iRightChildPg = isLeaf ? 0 : sqlite3Get4byte(&aHdr[8]);
+ szPage = sqlite3BtreeGetPageSize(pBt);
+
+ if( p->nCell ){
+ int i; /* Used to iterate through cells */
+ int nUsable; /* Usable bytes per page */
+
+ sqlite3BtreeEnter(pBt);
+ nUsable = szPage - sqlite3BtreeGetReserveNoMutex(pBt);
+ sqlite3BtreeLeave(pBt);
+ p->aCell = sqlite3_malloc64((p->nCell+1) * sizeof(StatCell));
+ if( p->aCell==0 ) return SQLITE_NOMEM_BKPT;
+ memset(p->aCell, 0, (p->nCell+1) * sizeof(StatCell));
+
+ for(i=0; i<p->nCell; i++){
+ StatCell *pCell = &p->aCell[i];
+
+ iOff = get2byte(&aData[nHdr+i*2]);
+ if( !isLeaf ){
+ pCell->iChildPg = sqlite3Get4byte(&aData[iOff]);
+ iOff += 4;
+ }
+ if( p->flags==0x05 ){
+ /* A table interior node. nPayload==0. */
+ }else{
+ u32 nPayload; /* Bytes of payload total (local+overflow) */
+ int nLocal; /* Bytes of payload stored locally */
+ iOff += getVarint32(&aData[iOff], nPayload);
+ if( p->flags==0x0D ){
+ u64 dummy;
+ iOff += sqlite3GetVarint(&aData[iOff], &dummy);
+ }
+ if( nPayload>(u32)p->nMxPayload ) p->nMxPayload = nPayload;
+ getLocalPayload(nUsable, p->flags, nPayload, &nLocal);
+ pCell->nLocal = nLocal;
+ assert( nLocal>=0 );
+ assert( nPayload>=(u32)nLocal );
+ assert( nLocal<=(nUsable-35) );
+ if( nPayload>(u32)nLocal ){
+ int j;
+ int nOvfl = ((nPayload - nLocal) + nUsable-4 - 1) / (nUsable - 4);
+ pCell->nLastOvfl = (nPayload-nLocal) - (nOvfl-1) * (nUsable-4);
+ pCell->nOvfl = nOvfl;
+ pCell->aOvfl = sqlite3_malloc64(sizeof(u32)*nOvfl);
+ if( pCell->aOvfl==0 ) return SQLITE_NOMEM_BKPT;
+ pCell->aOvfl[0] = sqlite3Get4byte(&aData[iOff+nLocal]);
+ for(j=1; j<nOvfl; j++){
+ int rc;
+ u32 iPrev = pCell->aOvfl[j-1];
+ DbPage *pPg = 0;
+ rc = sqlite3PagerGet(sqlite3BtreePager(pBt), iPrev, &pPg, 0);
+ if( rc!=SQLITE_OK ){
+ assert( pPg==0 );
+ return rc;
+ }
+ pCell->aOvfl[j] = sqlite3Get4byte(sqlite3PagerGetData(pPg));
+ sqlite3PagerUnref(pPg);
+ }
+ }
+ }
+ }
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Populate the pCsr->iOffset and pCsr->szPage member variables. Based on
+** the current value of pCsr->iPageno.
+*/
+static void statSizeAndOffset(StatCursor *pCsr){
+ StatTable *pTab = (StatTable *)((sqlite3_vtab_cursor *)pCsr)->pVtab;
+ Btree *pBt = pTab->db->aDb[pTab->iDb].pBt;
+ Pager *pPager = sqlite3BtreePager(pBt);
+ sqlite3_file *fd;
+ sqlite3_int64 x[2];
+
+ /* The default page size and offset */
+ pCsr->szPage = sqlite3BtreeGetPageSize(pBt);
+ pCsr->iOffset = (i64)pCsr->szPage * (pCsr->iPageno - 1);
+
+ /* If connected to a ZIPVFS backend, override the page size and
+ ** offset with actual values obtained from ZIPVFS.
+ */
+ fd = sqlite3PagerFile(pPager);
+ x[0] = pCsr->iPageno;
+ if( fd->pMethods!=0 && sqlite3OsFileControl(fd, 230440, &x)==SQLITE_OK ){
+ pCsr->iOffset = x[0];
+ pCsr->szPage = (int)x[1];
+ }
+}
+
+/*
+** Move a statvfs cursor to the next entry in the file.
+*/
+static int statNext(sqlite3_vtab_cursor *pCursor){
+ int rc;
+ int nPayload;
+ char *z;
+ StatCursor *pCsr = (StatCursor *)pCursor;
+ StatTable *pTab = (StatTable *)pCursor->pVtab;
+ Btree *pBt = pTab->db->aDb[pCsr->iDb].pBt;
+ Pager *pPager = sqlite3BtreePager(pBt);
+
+ sqlite3_free(pCsr->zPath);
+ pCsr->zPath = 0;
+
+statNextRestart:
+ if( pCsr->aPage[0].pPg==0 ){
+ rc = sqlite3_step(pCsr->pStmt);
+ if( rc==SQLITE_ROW ){
+ int nPage;
+ u32 iRoot = (u32)sqlite3_column_int64(pCsr->pStmt, 1);
+ sqlite3PagerPagecount(pPager, &nPage);
+ if( nPage==0 ){
+ pCsr->isEof = 1;
+ return sqlite3_reset(pCsr->pStmt);
+ }
+ rc = sqlite3PagerGet(pPager, iRoot, &pCsr->aPage[0].pPg, 0);
+ pCsr->aPage[0].iPgno = iRoot;
+ pCsr->aPage[0].iCell = 0;
+ pCsr->aPage[0].zPath = z = sqlite3_mprintf("/");
+ pCsr->iPage = 0;
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ }else{
+ pCsr->isEof = 1;
+ return sqlite3_reset(pCsr->pStmt);
+ }
+ }else{
+
+ /* Page p itself has already been visited. */
+ StatPage *p = &pCsr->aPage[pCsr->iPage];
+
+ while( p->iCell<p->nCell ){
+ StatCell *pCell = &p->aCell[p->iCell];
+ if( pCell->iOvfl<pCell->nOvfl ){
+ int nUsable;
+ sqlite3BtreeEnter(pBt);
+ nUsable = sqlite3BtreeGetPageSize(pBt) -
+ sqlite3BtreeGetReserveNoMutex(pBt);
+ sqlite3BtreeLeave(pBt);
+ pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
+ pCsr->iPageno = pCell->aOvfl[pCell->iOvfl];
+ pCsr->zPagetype = "overflow";
+ pCsr->nCell = 0;
+ pCsr->nMxPayload = 0;
+ pCsr->zPath = z = sqlite3_mprintf(
+ "%s%.3x+%.6x", p->zPath, p->iCell, pCell->iOvfl
+ );
+ if( pCell->iOvfl<pCell->nOvfl-1 ){
+ pCsr->nUnused = 0;
+ pCsr->nPayload = nUsable - 4;
+ }else{
+ pCsr->nPayload = pCell->nLastOvfl;
+ pCsr->nUnused = nUsable - 4 - pCsr->nPayload;
+ }
+ pCell->iOvfl++;
+ statSizeAndOffset(pCsr);
+ return z==0 ? SQLITE_NOMEM_BKPT : SQLITE_OK;
+ }
+ if( p->iRightChildPg ) break;
+ p->iCell++;
+ }
+
+ if( !p->iRightChildPg || p->iCell>p->nCell ){
+ statClearPage(p);
+ if( pCsr->iPage==0 ) return statNext(pCursor);
+ pCsr->iPage--;
+ goto statNextRestart; /* Tail recursion */
+ }
+ pCsr->iPage++;
+ assert( p==&pCsr->aPage[pCsr->iPage-1] );
+
+ if( p->iCell==p->nCell ){
+ p[1].iPgno = p->iRightChildPg;
+ }else{
+ p[1].iPgno = p->aCell[p->iCell].iChildPg;
+ }
+ rc = sqlite3PagerGet(pPager, p[1].iPgno, &p[1].pPg, 0);
+ p[1].iCell = 0;
+ p[1].zPath = z = sqlite3_mprintf("%s%.3x/", p->zPath, p->iCell);
+ p->iCell++;
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ }
+
+
+ /* Populate the StatCursor fields with the values to be returned
+ ** by the xColumn() and xRowid() methods.
+ */
+ if( rc==SQLITE_OK ){
+ int i;
+ StatPage *p = &pCsr->aPage[pCsr->iPage];
+ pCsr->zName = (char *)sqlite3_column_text(pCsr->pStmt, 0);
+ pCsr->iPageno = p->iPgno;
+
+ rc = statDecodePage(pBt, p);
+ if( rc==SQLITE_OK ){
+ statSizeAndOffset(pCsr);
+
+ switch( p->flags ){
+ case 0x05: /* table internal */
+ case 0x02: /* index internal */
+ pCsr->zPagetype = "internal";
+ break;
+ case 0x0D: /* table leaf */
+ case 0x0A: /* index leaf */
+ pCsr->zPagetype = "leaf";
+ break;
+ default:
+ pCsr->zPagetype = "corrupted";
+ break;
+ }
+ pCsr->nCell = p->nCell;
+ pCsr->nUnused = p->nUnused;
+ pCsr->nMxPayload = p->nMxPayload;
+ pCsr->zPath = z = sqlite3_mprintf("%s", p->zPath);
+ if( z==0 ) rc = SQLITE_NOMEM_BKPT;
+ nPayload = 0;
+ for(i=0; i<p->nCell; i++){
+ nPayload += p->aCell[i].nLocal;
+ }
+ pCsr->nPayload = nPayload;
+ }
+ }
+
+ return rc;
+}
+
+static int statEof(sqlite3_vtab_cursor *pCursor){
+ StatCursor *pCsr = (StatCursor *)pCursor;
+ return pCsr->isEof;
+}
+
+static int statFilter(
+ sqlite3_vtab_cursor *pCursor,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ StatCursor *pCsr = (StatCursor *)pCursor;
+ StatTable *pTab = (StatTable*)(pCursor->pVtab);
+ char *zSql;
+ int rc = SQLITE_OK;
+ char *zMaster;
+
+ if( idxNum==1 ){
+ const char *zDbase = (const char*)sqlite3_value_text(argv[0]);
+ pCsr->iDb = sqlite3FindDbName(pTab->db, zDbase);
+ if( pCsr->iDb<0 ){
+ sqlite3_free(pCursor->pVtab->zErrMsg);
+ pCursor->pVtab->zErrMsg = sqlite3_mprintf("no such schema: %s", zDbase);
+ return pCursor->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM_BKPT;
+ }
+ }else{
+ pCsr->iDb = pTab->iDb;
+ }
+ statResetCsr(pCsr);
+ sqlite3_finalize(pCsr->pStmt);
+ pCsr->pStmt = 0;
+ zMaster = pCsr->iDb==1 ? "sqlite_temp_master" : "sqlite_master";
+ zSql = sqlite3_mprintf(
+ "SELECT 'sqlite_master' AS name, 1 AS rootpage, 'table' AS type"
+ " UNION ALL "
+ "SELECT name, rootpage, type"
+ " FROM \"%w\".%s WHERE rootpage!=0"
+ " ORDER BY name", pTab->db->aDb[pCsr->iDb].zDbSName, zMaster);
+ if( zSql==0 ){
+ return SQLITE_NOMEM_BKPT;
+ }else{
+ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pCsr->pStmt, 0);
+ sqlite3_free(zSql);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = statNext(pCursor);
+ }
+ return rc;
+}
+
+static int statColumn(
+ sqlite3_vtab_cursor *pCursor,
+ sqlite3_context *ctx,
+ int i
+){
+ StatCursor *pCsr = (StatCursor *)pCursor;
+ switch( i ){
+ case 0: /* name */
+ sqlite3_result_text(ctx, pCsr->zName, -1, SQLITE_TRANSIENT);
+ break;
+ case 1: /* path */
+ sqlite3_result_text(ctx, pCsr->zPath, -1, SQLITE_TRANSIENT);
+ break;
+ case 2: /* pageno */
+ sqlite3_result_int64(ctx, pCsr->iPageno);
+ break;
+ case 3: /* pagetype */
+ sqlite3_result_text(ctx, pCsr->zPagetype, -1, SQLITE_STATIC);
+ break;
+ case 4: /* ncell */
+ sqlite3_result_int(ctx, pCsr->nCell);
+ break;
+ case 5: /* payload */
+ sqlite3_result_int(ctx, pCsr->nPayload);
+ break;
+ case 6: /* unused */
+ sqlite3_result_int(ctx, pCsr->nUnused);
+ break;
+ case 7: /* mx_payload */
+ sqlite3_result_int(ctx, pCsr->nMxPayload);
+ break;
+ case 8: /* pgoffset */
+ sqlite3_result_int64(ctx, pCsr->iOffset);
+ break;
+ case 9: /* pgsize */
+ sqlite3_result_int(ctx, pCsr->szPage);
+ break;
+ default: { /* schema */
+ sqlite3 *db = sqlite3_context_db_handle(ctx);
+ int iDb = pCsr->iDb;
+ sqlite3_result_text(ctx, db->aDb[iDb].zDbSName, -1, SQLITE_STATIC);
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
+static int statRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+ StatCursor *pCsr = (StatCursor *)pCursor;
+ *pRowid = pCsr->iPageno;
+ return SQLITE_OK;
+}
+
+/*
+** Invoke this routine to register the "dbstat" virtual table module
+*/
+SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){
+ static sqlite3_module dbstat_module = {
+ 0, /* iVersion */
+ statConnect, /* xCreate */
+ statConnect, /* xConnect */
+ statBestIndex, /* xBestIndex */
+ statDisconnect, /* xDisconnect */
+ statDisconnect, /* xDestroy */
+ statOpen, /* xOpen - open a cursor */
+ statClose, /* xClose - close a cursor */
+ statFilter, /* xFilter - configure scan constraints */
+ statNext, /* xNext - advance a cursor */
+ statEof, /* xEof - check for end of scan */
+ statColumn, /* xColumn - read data */
+ statRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ };
+ return sqlite3_create_module(db, "dbstat", &dbstat_module, 0);
+}
+#elif defined(SQLITE_ENABLE_DBSTAT_VTAB)
+SQLITE_PRIVATE int sqlite3DbstatRegister(sqlite3 *db){ return SQLITE_OK; }
+#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
+
+/************** End of dbstat.c **********************************************/
+/************** Begin file sqlite3session.c **********************************/
+
+#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
+/* #include "sqlite3session.h" */
+/* #include <assert.h> */
+/* #include <string.h> */
+
+#ifndef SQLITE_AMALGAMATION
+/* # include "sqliteInt.h" */
+/* # include "vdbeInt.h" */
+#endif
+
+typedef struct SessionTable SessionTable;
+typedef struct SessionChange SessionChange;
+typedef struct SessionBuffer SessionBuffer;
+typedef struct SessionInput SessionInput;
+
+/*
+** Minimum chunk size used by streaming versions of functions.
+*/
+#ifndef SESSIONS_STRM_CHUNK_SIZE
+# ifdef SQLITE_TEST
+# define SESSIONS_STRM_CHUNK_SIZE 64
+# else
+# define SESSIONS_STRM_CHUNK_SIZE 1024
+# endif
+#endif
+
+typedef struct SessionHook SessionHook;
+struct SessionHook {
+ void *pCtx;
+ int (*xOld)(void*,int,sqlite3_value**);
+ int (*xNew)(void*,int,sqlite3_value**);
+ int (*xCount)(void*);
+ int (*xDepth)(void*);
+};
+
+/*
+** Session handle structure.
+*/
+struct sqlite3_session {
+ sqlite3 *db; /* Database handle session is attached to */
+ char *zDb; /* Name of database session is attached to */
+ int bEnable; /* True if currently recording */
+ int bIndirect; /* True if all changes are indirect */
+ int bAutoAttach; /* True to auto-attach tables */
+ int rc; /* Non-zero if an error has occurred */
+ void *pFilterCtx; /* First argument to pass to xTableFilter */
+ int (*xTableFilter)(void *pCtx, const char *zTab);
+ sqlite3_session *pNext; /* Next session object on same db. */
+ SessionTable *pTable; /* List of attached tables */
+ SessionHook hook; /* APIs to grab new and old data with */
+};
+
+/*
+** Instances of this structure are used to build strings or binary records.
+*/
+struct SessionBuffer {
+ u8 *aBuf; /* Pointer to changeset buffer */
+ int nBuf; /* Size of buffer aBuf */
+ int nAlloc; /* Size of allocation containing aBuf */
+};
+
+/*
+** An object of this type is used internally as an abstraction for
+** input data. Input data may be supplied either as a single large buffer
+** (e.g. sqlite3changeset_start()) or using a stream function (e.g.
+** sqlite3changeset_start_strm()).
+*/
+struct SessionInput {
+ int bNoDiscard; /* If true, discard no data */
+ int iCurrent; /* Offset in aData[] of current change */
+ int iNext; /* Offset in aData[] of next change */
+ u8 *aData; /* Pointer to buffer containing changeset */
+ int nData; /* Number of bytes in aData */
+
+ SessionBuffer buf; /* Current read buffer */
+ int (*xInput)(void*, void*, int*); /* Input stream call (or NULL) */
+ void *pIn; /* First argument to xInput */
+ int bEof; /* Set to true after xInput finished */
+};
+
+/*
+** Structure for changeset iterators.
+*/
+struct sqlite3_changeset_iter {
+ SessionInput in; /* Input buffer or stream */
+ SessionBuffer tblhdr; /* Buffer to hold apValue/zTab/abPK/ */
+ int bPatchset; /* True if this is a patchset */
+ int rc; /* Iterator error code */
+ sqlite3_stmt *pConflict; /* Points to conflicting row, if any */
+ char *zTab; /* Current table */
+ int nCol; /* Number of columns in zTab */
+ int op; /* Current operation */
+ int bIndirect; /* True if current change was indirect */
+ u8 *abPK; /* Primary key array */
+ sqlite3_value **apValue; /* old.* and new.* values */
+};
+
+/*
+** Each session object maintains a set of the following structures, one
+** for each table the session object is monitoring. The structures are
+** stored in a linked list starting at sqlite3_session.pTable.
+**
+** The keys of the SessionTable.aChange[] hash table are all rows that have
+** been modified in any way since the session object was attached to the
+** table.
+**
+** The data associated with each hash-table entry is a structure containing
+** a subset of the initial values that the modified row contained at the
+** start of the session. Or no initial values if the row was inserted.
+*/
+struct SessionTable {
+ SessionTable *pNext;
+ char *zName; /* Local name of table */
+ int nCol; /* Number of columns in table zName */
+ const char **azCol; /* Column names */
+ u8 *abPK; /* Array of primary key flags */
+ int nEntry; /* Total number of entries in hash table */
+ int nChange; /* Size of apChange[] array */
+ SessionChange **apChange; /* Hash table buckets */
+};
+
+/*
+** RECORD FORMAT:
+**
+** The following record format is similar to (but not compatible with) that
+** used in SQLite database files. This format is used as part of the
+** change-set binary format, and so must be architecture independent.
+**
+** Unlike the SQLite database record format, each field is self-contained -
+** there is no separation of header and data. Each field begins with a
+** single byte describing its type, as follows:
+**
+** 0x00: Undefined value.
+** 0x01: Integer value.
+** 0x02: Real value.
+** 0x03: Text value.
+** 0x04: Blob value.
+** 0x05: SQL NULL value.
+**
+** Note that the above match the definitions of SQLITE_INTEGER, SQLITE_TEXT
+** and so on in sqlite3.h. For undefined and NULL values, the field consists
+** only of the single type byte. For other types of values, the type byte
+** is followed by:
+**
+** Text values:
+** A varint containing the number of bytes in the value (encoded using
+** UTF-8). Followed by a buffer containing the UTF-8 representation
+** of the text value. There is no nul terminator.
+**
+** Blob values:
+** A varint containing the number of bytes in the value, followed by
+** a buffer containing the value itself.
+**
+** Integer values:
+** An 8-byte big-endian integer value.
+**
+** Real values:
+** An 8-byte big-endian IEEE 754-2008 real value.
+**
+** Varint values are encoded in the same way as varints in the SQLite
+** record format.
+**
+** CHANGESET FORMAT:
+**
+** A changeset is a collection of DELETE, UPDATE and INSERT operations on
+** one or more tables. Operations on a single table are grouped together,
+** but may occur in any order (i.e. deletes, updates and inserts are all
+** mixed together).
+**
+** Each group of changes begins with a table header:
+**
+** 1 byte: Constant 0x54 (capital 'T')
+** Varint: Number of columns in the table.
+** nCol bytes: 0x01 for PK columns, 0x00 otherwise.
+** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
+**
+** Followed by one or more changes to the table.
+**
+** 1 byte: Either SQLITE_INSERT (0x12), UPDATE (0x17) or DELETE (0x09).
+** 1 byte: The "indirect-change" flag.
+** old.* record: (delete and update only)
+** new.* record: (insert and update only)
+**
+** The "old.*" and "new.*" records, if present, are N field records in the
+** format described above under "RECORD FORMAT", where N is the number of
+** columns in the table. The i'th field of each record is associated with
+** the i'th column of the table, counting from left to right in the order
+** in which columns were declared in the CREATE TABLE statement.
+**
+** The new.* record that is part of each INSERT change contains the values
+** that make up the new row. Similarly, the old.* record that is part of each
+** DELETE change contains the values that made up the row that was deleted
+** from the database. In the changeset format, the records that are part
+** of INSERT or DELETE changes never contain any undefined (type byte 0x00)
+** fields.
+**
+** Within the old.* record associated with an UPDATE change, all fields
+** associated with table columns that are not PRIMARY KEY columns and are
+** not modified by the UPDATE change are set to "undefined". Other fields
+** are set to the values that made up the row before the UPDATE that the
+** change records took place. Within the new.* record, fields associated
+** with table columns modified by the UPDATE change contain the new
+** values. Fields associated with table columns that are not modified
+** are set to "undefined".
+**
+** PATCHSET FORMAT:
+**
+** A patchset is also a collection of changes. It is similar to a changeset,
+** but leaves undefined those fields that are not useful if no conflict
+** resolution is required when applying the changeset.
+**
+** Each group of changes begins with a table header:
+**
+** 1 byte: Constant 0x50 (capital 'P')
+** Varint: Number of columns in the table.
+** nCol bytes: 0x01 for PK columns, 0x00 otherwise.
+** N bytes: Unqualified table name (encoded using UTF-8). Nul-terminated.
+**
+** Followed by one or more changes to the table.
+**
+** 1 byte: Either SQLITE_INSERT (0x12), UPDATE (0x17) or DELETE (0x09).
+** 1 byte: The "indirect-change" flag.
+** single record: (PK fields for DELETE, PK and modified fields for UPDATE,
+** full record for INSERT).
+**
+** As in the changeset format, each field of the single record that is part
+** of a patchset change is associated with the correspondingly positioned
+** table column, counting from left to right within the CREATE TABLE
+** statement.
+**
+** For a DELETE change, all fields within the record except those associated
+** with PRIMARY KEY columns are set to "undefined". The PRIMARY KEY fields
+** contain the values identifying the row to delete.
+**
+** For an UPDATE change, all fields except those associated with PRIMARY KEY
+** columns and columns that are modified by the UPDATE are set to "undefined".
+** PRIMARY KEY fields contain the values identifying the table row to update,
+** and fields associated with modified columns contain the new column values.
+**
+** The records associated with INSERT changes are in the same format as for
+** changesets. It is not possible for a record associated with an INSERT
+** change to contain a field set to "undefined".
+*/
+
+/*
+** For each row modified during a session, there exists a single instance of
+** this structure stored in a SessionTable.aChange[] hash table.
+*/
+struct SessionChange {
+ int op; /* One of UPDATE, DELETE, INSERT */
+ int bIndirect; /* True if this change is "indirect" */
+ int nRecord; /* Number of bytes in buffer aRecord[] */
+ u8 *aRecord; /* Buffer containing old.* record */
+ SessionChange *pNext; /* For hash-table collisions */
+};
+
+/*
+** Write a varint with value iVal into the buffer at aBuf. Return the
+** number of bytes written.
+*/
+static int sessionVarintPut(u8 *aBuf, int iVal){
+ return putVarint32(aBuf, iVal);
+}
+
+/*
+** Return the number of bytes required to store value iVal as a varint.
+*/
+static int sessionVarintLen(int iVal){
+ return sqlite3VarintLen(iVal);
+}
+
+/*
+** Read a varint value from aBuf[] into *piVal. Return the number of
+** bytes read.
+*/
+static int sessionVarintGet(u8 *aBuf, int *piVal){
+ return getVarint32(aBuf, *piVal);
+}
+
+/* Load an unaligned and unsigned 32-bit integer */
+#define SESSION_UINT32(x) (((u32)(x)[0]<<24)|((x)[1]<<16)|((x)[2]<<8)|(x)[3])
+
+/*
+** Read a 64-bit big-endian integer value from buffer aRec[]. Return
+** the value read.
+*/
+static sqlite3_int64 sessionGetI64(u8 *aRec){
+ u64 x = SESSION_UINT32(aRec);
+ u32 y = SESSION_UINT32(aRec+4);
+ x = (x<<32) + y;
+ return (sqlite3_int64)x;
+}
+
+/*
+** Write a 64-bit big-endian integer value to the buffer aBuf[].
+*/
+static void sessionPutI64(u8 *aBuf, sqlite3_int64 i){
+ aBuf[0] = (i>>56) & 0xFF;
+ aBuf[1] = (i>>48) & 0xFF;
+ aBuf[2] = (i>>40) & 0xFF;
+ aBuf[3] = (i>>32) & 0xFF;
+ aBuf[4] = (i>>24) & 0xFF;
+ aBuf[5] = (i>>16) & 0xFF;
+ aBuf[6] = (i>> 8) & 0xFF;
+ aBuf[7] = (i>> 0) & 0xFF;
+}
+
+/*
+** This function is used to serialize the contents of value pValue (see
+** comment titled "RECORD FORMAT" above).
+**
+** If it is non-NULL, the serialized form of the value is written to
+** buffer aBuf. *pnWrite is set to the number of bytes written before
+** returning. Or, if aBuf is NULL, the only thing this function does is
+** set *pnWrite.
+**
+** If no error occurs, SQLITE_OK is returned. Or, if an OOM error occurs
+** within a call to sqlite3_value_text() (may fail if the db is utf-16))
+** SQLITE_NOMEM is returned.
+*/
+static int sessionSerializeValue(
+ u8 *aBuf, /* If non-NULL, write serialized value here */
+ sqlite3_value *pValue, /* Value to serialize */
+ int *pnWrite /* IN/OUT: Increment by bytes written */
+){
+ int nByte; /* Size of serialized value in bytes */
+
+ if( pValue ){
+ int eType; /* Value type (SQLITE_NULL, TEXT etc.) */
+
+ eType = sqlite3_value_type(pValue);
+ if( aBuf ) aBuf[0] = eType;
+
+ switch( eType ){
+ case SQLITE_NULL:
+ nByte = 1;
+ break;
+
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT:
+ if( aBuf ){
+ /* TODO: SQLite does something special to deal with mixed-endian
+ ** floating point values (e.g. ARM7). This code probably should
+ ** too. */
+ u64 i;
+ if( eType==SQLITE_INTEGER ){
+ i = (u64)sqlite3_value_int64(pValue);
+ }else{
+ double r;
+ assert( sizeof(double)==8 && sizeof(u64)==8 );
+ r = sqlite3_value_double(pValue);
+ memcpy(&i, &r, 8);
+ }
+ sessionPutI64(&aBuf[1], i);
+ }
+ nByte = 9;
+ break;
+
+ default: {
+ u8 *z;
+ int n;
+ int nVarint;
+
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ if( eType==SQLITE_TEXT ){
+ z = (u8 *)sqlite3_value_text(pValue);
+ }else{
+ z = (u8 *)sqlite3_value_blob(pValue);
+ }
+ n = sqlite3_value_bytes(pValue);
+ if( z==0 && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
+ nVarint = sessionVarintLen(n);
+
+ if( aBuf ){
+ sessionVarintPut(&aBuf[1], n);
+ memcpy(&aBuf[nVarint + 1], eType==SQLITE_TEXT ?
+ sqlite3_value_text(pValue) : sqlite3_value_blob(pValue), n
+ );
+ }
+
+ nByte = 1 + nVarint + n;
+ break;
+ }
+ }
+ }else{
+ nByte = 1;
+ if( aBuf ) aBuf[0] = '\0';
+ }
+
+ if( pnWrite ) *pnWrite += nByte;
+ return SQLITE_OK;
+}
+
+
+/*
+** This macro is used to calculate hash key values for data structures. In
+** order to use this macro, the entire data structure must be represented
+** as a series of unsigned integers. In order to calculate a hash-key value
+** for a data structure represented as three such integers, the macro may
+** then be used as follows:
+**
+** int hash_key_value;
+** hash_key_value = HASH_APPEND(0, <value 1>);
+** hash_key_value = HASH_APPEND(hash_key_value, <value 2>);
+** hash_key_value = HASH_APPEND(hash_key_value, <value 3>);
+**
+** In practice, the data structures this macro is used for are the primary
+** key values of modified rows.
+*/
+#define HASH_APPEND(hash, add) ((hash) << 3) ^ (hash) ^ (unsigned int)(add)
+
+/*
+** Append the hash of the 64-bit integer passed as the second argument to the
+** hash-key value passed as the first. Return the new hash-key value.
+*/
+static unsigned int sessionHashAppendI64(unsigned int h, i64 i){
+ h = HASH_APPEND(h, i & 0xFFFFFFFF);
+ return HASH_APPEND(h, (i>>32)&0xFFFFFFFF);
+}
+
+/*
+** Append the hash of the blob passed via the second and third arguments to
+** the hash-key value passed as the first. Return the new hash-key value.
+*/
+static unsigned int sessionHashAppendBlob(unsigned int h, int n, const u8 *z){
+ int i;
+ for(i=0; i<n; i++) h = HASH_APPEND(h, z[i]);
+ return h;
+}
+
+/*
+** Append the hash of the data type passed as the second argument to the
+** hash-key value passed as the first. Return the new hash-key value.
+*/
+static unsigned int sessionHashAppendType(unsigned int h, int eType){
+ return HASH_APPEND(h, eType);
+}
+
+/*
+** This function may only be called from within a pre-update callback.
+** It calculates a hash based on the primary key values of the old.* or
+** new.* row currently available and, assuming no error occurs, writes it to
+** *piHash before returning. If the primary key contains one or more NULL
+** values, *pbNullPK is set to true before returning.
+**
+** If an error occurs, an SQLite error code is returned and the final values
+** of *piHash asn *pbNullPK are undefined. Otherwise, SQLITE_OK is returned
+** and the output variables are set as described above.
+*/
+static int sessionPreupdateHash(
+ sqlite3_session *pSession, /* Session object that owns pTab */
+ SessionTable *pTab, /* Session table handle */
+ int bNew, /* True to hash the new.* PK */
+ int *piHash, /* OUT: Hash value */
+ int *pbNullPK /* OUT: True if there are NULL values in PK */
+){
+ unsigned int h = 0; /* Hash value to return */
+ int i; /* Used to iterate through columns */
+
+ assert( *pbNullPK==0 );
+ assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
+ for(i=0; i<pTab->nCol; i++){
+ if( pTab->abPK[i] ){
+ int rc;
+ int eType;
+ sqlite3_value *pVal;
+
+ if( bNew ){
+ rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
+ }else{
+ rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ eType = sqlite3_value_type(pVal);
+ h = sessionHashAppendType(h, eType);
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ i64 iVal;
+ if( eType==SQLITE_INTEGER ){
+ iVal = sqlite3_value_int64(pVal);
+ }else{
+ double rVal = sqlite3_value_double(pVal);
+ assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
+ memcpy(&iVal, &rVal, 8);
+ }
+ h = sessionHashAppendI64(h, iVal);
+ }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ const u8 *z;
+ int n;
+ if( eType==SQLITE_TEXT ){
+ z = (const u8 *)sqlite3_value_text(pVal);
+ }else{
+ z = (const u8 *)sqlite3_value_blob(pVal);
+ }
+ n = sqlite3_value_bytes(pVal);
+ if( !z && (eType!=SQLITE_BLOB || n>0) ) return SQLITE_NOMEM;
+ h = sessionHashAppendBlob(h, n, z);
+ }else{
+ assert( eType==SQLITE_NULL );
+ *pbNullPK = 1;
+ }
+ }
+ }
+
+ *piHash = (h % pTab->nChange);
+ return SQLITE_OK;
+}
+
+/*
+** The buffer that the argument points to contains a serialized SQL value.
+** Return the number of bytes of space occupied by the value (including
+** the type byte).
+*/
+static int sessionSerialLen(u8 *a){
+ int e = *a;
+ int n;
+ if( e==0 ) return 1;
+ if( e==SQLITE_NULL ) return 1;
+ if( e==SQLITE_INTEGER || e==SQLITE_FLOAT ) return 9;
+ return sessionVarintGet(&a[1], &n) + 1 + n;
+}
+
+/*
+** Based on the primary key values stored in change aRecord, calculate a
+** hash key. Assume the has table has nBucket buckets. The hash keys
+** calculated by this function are compatible with those calculated by
+** sessionPreupdateHash().
+**
+** The bPkOnly argument is non-zero if the record at aRecord[] is from
+** a patchset DELETE. In this case the non-PK fields are omitted entirely.
+*/
+static unsigned int sessionChangeHash(
+ SessionTable *pTab, /* Table handle */
+ int bPkOnly, /* Record consists of PK fields only */
+ u8 *aRecord, /* Change record */
+ int nBucket /* Assume this many buckets in hash table */
+){
+ unsigned int h = 0; /* Value to return */
+ int i; /* Used to iterate through columns */
+ u8 *a = aRecord; /* Used to iterate through change record */
+
+ for(i=0; i<pTab->nCol; i++){
+ int eType = *a;
+ int isPK = pTab->abPK[i];
+ if( bPkOnly && isPK==0 ) continue;
+
+ /* It is not possible for eType to be SQLITE_NULL here. The session
+ ** module does not record changes for rows with NULL values stored in
+ ** primary key columns. */
+ assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
+ || eType==SQLITE_TEXT || eType==SQLITE_BLOB
+ || eType==SQLITE_NULL || eType==0
+ );
+ assert( !isPK || (eType!=0 && eType!=SQLITE_NULL) );
+
+ if( isPK ){
+ a++;
+ h = sessionHashAppendType(h, eType);
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ h = sessionHashAppendI64(h, sessionGetI64(a));
+ a += 8;
+ }else{
+ int n;
+ a += sessionVarintGet(a, &n);
+ h = sessionHashAppendBlob(h, n, a);
+ a += n;
+ }
+ }else{
+ a += sessionSerialLen(a);
+ }
+ }
+ return (h % nBucket);
+}
+
+/*
+** Arguments aLeft and aRight are pointers to change records for table pTab.
+** This function returns true if the two records apply to the same row (i.e.
+** have the same values stored in the primary key columns), or false
+** otherwise.
+*/
+static int sessionChangeEqual(
+ SessionTable *pTab, /* Table used for PK definition */
+ int bLeftPkOnly, /* True if aLeft[] contains PK fields only */
+ u8 *aLeft, /* Change record */
+ int bRightPkOnly, /* True if aRight[] contains PK fields only */
+ u8 *aRight /* Change record */
+){
+ u8 *a1 = aLeft; /* Cursor to iterate through aLeft */
+ u8 *a2 = aRight; /* Cursor to iterate through aRight */
+ int iCol; /* Used to iterate through table columns */
+
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( pTab->abPK[iCol] ){
+ int n1 = sessionSerialLen(a1);
+ int n2 = sessionSerialLen(a2);
+
+ if( pTab->abPK[iCol] && (n1!=n2 || memcmp(a1, a2, n1)) ){
+ return 0;
+ }
+ a1 += n1;
+ a2 += n2;
+ }else{
+ if( bLeftPkOnly==0 ) a1 += sessionSerialLen(a1);
+ if( bRightPkOnly==0 ) a2 += sessionSerialLen(a2);
+ }
+ }
+
+ return 1;
+}
+
+/*
+** Arguments aLeft and aRight both point to buffers containing change
+** records with nCol columns. This function "merges" the two records into
+** a single records which is written to the buffer at *paOut. *paOut is
+** then set to point to one byte after the last byte written before
+** returning.
+**
+** The merging of records is done as follows: For each column, if the
+** aRight record contains a value for the column, copy the value from
+** their. Otherwise, if aLeft contains a value, copy it. If neither
+** record contains a value for a given column, then neither does the
+** output record.
+*/
+static void sessionMergeRecord(
+ u8 **paOut,
+ int nCol,
+ u8 *aLeft,
+ u8 *aRight
+){
+ u8 *a1 = aLeft; /* Cursor used to iterate through aLeft */
+ u8 *a2 = aRight; /* Cursor used to iterate through aRight */
+ u8 *aOut = *paOut; /* Output cursor */
+ int iCol; /* Used to iterate from 0 to nCol */
+
+ for(iCol=0; iCol<nCol; iCol++){
+ int n1 = sessionSerialLen(a1);
+ int n2 = sessionSerialLen(a2);
+ if( *a2 ){
+ memcpy(aOut, a2, n2);
+ aOut += n2;
+ }else{
+ memcpy(aOut, a1, n1);
+ aOut += n1;
+ }
+ a1 += n1;
+ a2 += n2;
+ }
+
+ *paOut = aOut;
+}
+
+/*
+** This is a helper function used by sessionMergeUpdate().
+**
+** When this function is called, both *paOne and *paTwo point to a value
+** within a change record. Before it returns, both have been advanced so
+** as to point to the next value in the record.
+**
+** If, when this function is called, *paTwo points to a valid value (i.e.
+** *paTwo[0] is not 0x00 - the "no value" placeholder), a copy of the *paTwo
+** pointer is returned and *pnVal is set to the number of bytes in the
+** serialized value. Otherwise, a copy of *paOne is returned and *pnVal
+** set to the number of bytes in the value at *paOne. If *paOne points
+** to the "no value" placeholder, *pnVal is set to 1. In other words:
+**
+** if( *paTwo is valid ) return *paTwo;
+** return *paOne;
+**
+*/
+static u8 *sessionMergeValue(
+ u8 **paOne, /* IN/OUT: Left-hand buffer pointer */
+ u8 **paTwo, /* IN/OUT: Right-hand buffer pointer */
+ int *pnVal /* OUT: Bytes in returned value */
+){
+ u8 *a1 = *paOne;
+ u8 *a2 = *paTwo;
+ u8 *pRet = 0;
+ int n1;
+
+ assert( a1 );
+ if( a2 ){
+ int n2 = sessionSerialLen(a2);
+ if( *a2 ){
+ *pnVal = n2;
+ pRet = a2;
+ }
+ *paTwo = &a2[n2];
+ }
+
+ n1 = sessionSerialLen(a1);
+ if( pRet==0 ){
+ *pnVal = n1;
+ pRet = a1;
+ }
+ *paOne = &a1[n1];
+
+ return pRet;
+}
+
+/*
+** This function is used by changeset_concat() to merge two UPDATE changes
+** on the same row.
+*/
+static int sessionMergeUpdate(
+ u8 **paOut, /* IN/OUT: Pointer to output buffer */
+ SessionTable *pTab, /* Table change pertains to */
+ int bPatchset, /* True if records are patchset records */
+ u8 *aOldRecord1, /* old.* record for first change */
+ u8 *aOldRecord2, /* old.* record for second change */
+ u8 *aNewRecord1, /* new.* record for first change */
+ u8 *aNewRecord2 /* new.* record for second change */
+){
+ u8 *aOld1 = aOldRecord1;
+ u8 *aOld2 = aOldRecord2;
+ u8 *aNew1 = aNewRecord1;
+ u8 *aNew2 = aNewRecord2;
+
+ u8 *aOut = *paOut;
+ int i;
+
+ if( bPatchset==0 ){
+ int bRequired = 0;
+
+ assert( aOldRecord1 && aNewRecord1 );
+
+ /* Write the old.* vector first. */
+ for(i=0; i<pTab->nCol; i++){
+ int nOld;
+ u8 *aOld;
+ int nNew;
+ u8 *aNew;
+
+ aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
+ aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
+ if( pTab->abPK[i] || nOld!=nNew || memcmp(aOld, aNew, nNew) ){
+ if( pTab->abPK[i]==0 ) bRequired = 1;
+ memcpy(aOut, aOld, nOld);
+ aOut += nOld;
+ }else{
+ *(aOut++) = '\0';
+ }
+ }
+
+ if( !bRequired ) return 0;
+ }
+
+ /* Write the new.* vector */
+ aOld1 = aOldRecord1;
+ aOld2 = aOldRecord2;
+ aNew1 = aNewRecord1;
+ aNew2 = aNewRecord2;
+ for(i=0; i<pTab->nCol; i++){
+ int nOld;
+ u8 *aOld;
+ int nNew;
+ u8 *aNew;
+
+ aOld = sessionMergeValue(&aOld1, &aOld2, &nOld);
+ aNew = sessionMergeValue(&aNew1, &aNew2, &nNew);
+ if( bPatchset==0
+ && (pTab->abPK[i] || (nOld==nNew && 0==memcmp(aOld, aNew, nNew)))
+ ){
+ *(aOut++) = '\0';
+ }else{
+ memcpy(aOut, aNew, nNew);
+ aOut += nNew;
+ }
+ }
+
+ *paOut = aOut;
+ return 1;
+}
+
+/*
+** This function is only called from within a pre-update-hook callback.
+** It determines if the current pre-update-hook change affects the same row
+** as the change stored in argument pChange. If so, it returns true. Otherwise
+** if the pre-update-hook does not affect the same row as pChange, it returns
+** false.
+*/
+static int sessionPreupdateEqual(
+ sqlite3_session *pSession, /* Session object that owns SessionTable */
+ SessionTable *pTab, /* Table associated with change */
+ SessionChange *pChange, /* Change to compare to */
+ int op /* Current pre-update operation */
+){
+ int iCol; /* Used to iterate through columns */
+ u8 *a = pChange->aRecord; /* Cursor used to scan change record */
+
+ assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
+ for(iCol=0; iCol<pTab->nCol; iCol++){
+ if( !pTab->abPK[iCol] ){
+ a += sessionSerialLen(a);
+ }else{
+ sqlite3_value *pVal; /* Value returned by preupdate_new/old */
+ int rc; /* Error code from preupdate_new/old */
+ int eType = *a++; /* Type of value from change record */
+
+ /* The following calls to preupdate_new() and preupdate_old() can not
+ ** fail. This is because they cache their return values, and by the
+ ** time control flows to here they have already been called once from
+ ** within sessionPreupdateHash(). The first two asserts below verify
+ ** this (that the method has already been called). */
+ if( op==SQLITE_INSERT ){
+ /* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */
+ rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal);
+ }else{
+ /* assert( db->pPreUpdate->pUnpacked ); */
+ rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
+ }
+ assert( rc==SQLITE_OK );
+ if( sqlite3_value_type(pVal)!=eType ) return 0;
+
+ /* A SessionChange object never has a NULL value in a PK column */
+ assert( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT
+ || eType==SQLITE_BLOB || eType==SQLITE_TEXT
+ );
+
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ i64 iVal = sessionGetI64(a);
+ a += 8;
+ if( eType==SQLITE_INTEGER ){
+ if( sqlite3_value_int64(pVal)!=iVal ) return 0;
+ }else{
+ double rVal;
+ assert( sizeof(iVal)==8 && sizeof(rVal)==8 );
+ memcpy(&rVal, &iVal, 8);
+ if( sqlite3_value_double(pVal)!=rVal ) return 0;
+ }
+ }else{
+ int n;
+ const u8 *z;
+ a += sessionVarintGet(a, &n);
+ if( sqlite3_value_bytes(pVal)!=n ) return 0;
+ if( eType==SQLITE_TEXT ){
+ z = sqlite3_value_text(pVal);
+ }else{
+ z = sqlite3_value_blob(pVal);
+ }
+ if( memcmp(a, z, n) ) return 0;
+ a += n;
+ break;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/*
+** If required, grow the hash table used to store changes on table pTab
+** (part of the session pSession). If a fatal OOM error occurs, set the
+** session object to failed and return SQLITE_ERROR. Otherwise, return
+** SQLITE_OK.
+**
+** It is possible that a non-fatal OOM error occurs in this function. In
+** that case the hash-table does not grow, but SQLITE_OK is returned anyway.
+** Growing the hash table in this case is a performance optimization only,
+** it is not required for correct operation.
+*/
+static int sessionGrowHash(int bPatchset, SessionTable *pTab){
+ if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
+ int i;
+ SessionChange **apNew;
+ int nNew = (pTab->nChange ? pTab->nChange : 128) * 2;
+
+ apNew = (SessionChange **)sqlite3_malloc(sizeof(SessionChange *) * nNew);
+ if( apNew==0 ){
+ if( pTab->nChange==0 ){
+ return SQLITE_ERROR;
+ }
+ return SQLITE_OK;
+ }
+ memset(apNew, 0, sizeof(SessionChange *) * nNew);
+
+ for(i=0; i<pTab->nChange; i++){
+ SessionChange *p;
+ SessionChange *pNext;
+ for(p=pTab->apChange[i]; p; p=pNext){
+ int bPkOnly = (p->op==SQLITE_DELETE && bPatchset);
+ int iHash = sessionChangeHash(pTab, bPkOnly, p->aRecord, nNew);
+ pNext = p->pNext;
+ p->pNext = apNew[iHash];
+ apNew[iHash] = p;
+ }
+ }
+
+ sqlite3_free(pTab->apChange);
+ pTab->nChange = nNew;
+ pTab->apChange = apNew;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** This function queries the database for the names of the columns of table
+** zThis, in schema zDb. It is expected that the table has nCol columns. If
+** not, SQLITE_SCHEMA is returned and none of the output variables are
+** populated.
+**
+** Otherwise, if they are not NULL, variable *pnCol is set to the number
+** of columns in the database table and variable *pzTab is set to point to a
+** nul-terminated copy of the table name. *pazCol (if not NULL) is set to
+** point to an array of pointers to column names. And *pabPK (again, if not
+** NULL) is set to point to an array of booleans - true if the corresponding
+** column is part of the primary key.
+**
+** For example, if the table is declared as:
+**
+** CREATE TABLE tbl1(w, x, y, z, PRIMARY KEY(w, z));
+**
+** Then the four output variables are populated as follows:
+**
+** *pnCol = 4
+** *pzTab = "tbl1"
+** *pazCol = {"w", "x", "y", "z"}
+** *pabPK = {1, 0, 0, 1}
+**
+** All returned buffers are part of the same single allocation, which must
+** be freed using sqlite3_free() by the caller. If pazCol was not NULL, then
+** pointer *pazCol should be freed to release all memory. Otherwise, pointer
+** *pabPK. It is illegal for both pazCol and pabPK to be NULL.
+*/
+static int sessionTableInfo(
+ sqlite3 *db, /* Database connection */
+ const char *zDb, /* Name of attached database (e.g. "main") */
+ const char *zThis, /* Table name */
+ int *pnCol, /* OUT: number of columns */
+ const char **pzTab, /* OUT: Copy of zThis */
+ const char ***pazCol, /* OUT: Array of column names for table */
+ u8 **pabPK /* OUT: Array of booleans - true for PK col */
+){
+ char *zPragma;
+ sqlite3_stmt *pStmt;
+ int rc;
+ int nByte;
+ int nDbCol = 0;
+ int nThis;
+ int i;
+ u8 *pAlloc = 0;
+ char **azCol = 0;
+ u8 *abPK = 0;
+
+ assert( pazCol && pabPK );
+
+ nThis = sqlite3Strlen30(zThis);
+ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
+ if( !zPragma ) return SQLITE_NOMEM;
+
+ rc = sqlite3_prepare_v2(db, zPragma, -1, &pStmt, 0);
+ sqlite3_free(zPragma);
+ if( rc!=SQLITE_OK ) return rc;
+
+ nByte = nThis + 1;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ nByte += sqlite3_column_bytes(pStmt, 1);
+ nDbCol++;
+ }
+ rc = sqlite3_reset(pStmt);
+
+ if( rc==SQLITE_OK ){
+ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
+ pAlloc = sqlite3_malloc(nByte);
+ if( pAlloc==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ }
+ if( rc==SQLITE_OK ){
+ azCol = (char **)pAlloc;
+ pAlloc = (u8 *)&azCol[nDbCol];
+ abPK = (u8 *)pAlloc;
+ pAlloc = &abPK[nDbCol];
+ if( pzTab ){
+ memcpy(pAlloc, zThis, nThis+1);
+ *pzTab = (char *)pAlloc;
+ pAlloc += nThis+1;
+ }
+
+ i = 0;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ int nName = sqlite3_column_bytes(pStmt, 1);
+ const unsigned char *zName = sqlite3_column_text(pStmt, 1);
+ if( zName==0 ) break;
+ memcpy(pAlloc, zName, nName+1);
+ azCol[i] = (char *)pAlloc;
+ pAlloc += nName+1;
+ abPK[i] = sqlite3_column_int(pStmt, 5);
+ i++;
+ }
+ rc = sqlite3_reset(pStmt);
+
+ }
+
+ /* If successful, populate the output variables. Otherwise, zero them and
+ ** free any allocation made. An error code will be returned in this case.
+ */
+ if( rc==SQLITE_OK ){
+ *pazCol = (const char **)azCol;
+ *pabPK = abPK;
+ *pnCol = nDbCol;
+ }else{
+ *pazCol = 0;
+ *pabPK = 0;
+ *pnCol = 0;
+ if( pzTab ) *pzTab = 0;
+ sqlite3_free(azCol);
+ }
+ sqlite3_finalize(pStmt);
+ return rc;
+}
+
+/*
+** This function is only called from within a pre-update handler for a
+** write to table pTab, part of session pSession. If this is the first
+** write to this table, initalize the SessionTable.nCol, azCol[] and
+** abPK[] arrays accordingly.
+**
+** If an error occurs, an error code is stored in sqlite3_session.rc and
+** non-zero returned. Or, if no error occurs but the table has no primary
+** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
+** indicate that updates on this table should be ignored. SessionTable.abPK
+** is set to NULL in this case.
+*/
+static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
+ if( pTab->nCol==0 ){
+ u8 *abPK;
+ assert( pTab->azCol==0 || pTab->abPK==0 );
+ pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
+ pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
+ );
+ if( pSession->rc==SQLITE_OK ){
+ int i;
+ for(i=0; i<pTab->nCol; i++){
+ if( abPK[i] ){
+ pTab->abPK = abPK;
+ break;
+ }
+ }
+ }
+ }
+ return (pSession->rc || pTab->abPK==0);
+}
+
+/*
+** This function is only called from with a pre-update-hook reporting a
+** change on table pTab (attached to session pSession). The type of change
+** (UPDATE, INSERT, DELETE) is specified by the first argument.
+**
+** Unless one is already present or an error occurs, an entry is added
+** to the changed-rows hash table associated with table pTab.
+*/
+static void sessionPreupdateOneChange(
+ int op, /* One of SQLITE_UPDATE, INSERT, DELETE */
+ sqlite3_session *pSession, /* Session object pTab is attached to */
+ SessionTable *pTab /* Table that change applies to */
+){
+ int iHash;
+ int bNull = 0;
+ int rc = SQLITE_OK;
+
+ if( pSession->rc ) return;
+
+ /* Load table details if required */
+ if( sessionInitTable(pSession, pTab) ) return;
+
+ /* Check the number of columns in this xPreUpdate call matches the
+ ** number of columns in the table. */
+ if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){
+ pSession->rc = SQLITE_SCHEMA;
+ return;
+ }
+
+ /* Grow the hash table if required */
+ if( sessionGrowHash(0, pTab) ){
+ pSession->rc = SQLITE_NOMEM;
+ return;
+ }
+
+ /* Calculate the hash-key for this change. If the primary key of the row
+ ** includes a NULL value, exit early. Such changes are ignored by the
+ ** session module. */
+ rc = sessionPreupdateHash(pSession, pTab, op==SQLITE_INSERT, &iHash, &bNull);
+ if( rc!=SQLITE_OK ) goto error_out;
+
+ if( bNull==0 ){
+ /* Search the hash table for an existing record for this row. */
+ SessionChange *pC;
+ for(pC=pTab->apChange[iHash]; pC; pC=pC->pNext){
+ if( sessionPreupdateEqual(pSession, pTab, pC, op) ) break;
+ }
+
+ if( pC==0 ){
+ /* Create a new change object containing all the old values (if
+ ** this is an SQLITE_UPDATE or SQLITE_DELETE), or just the PK
+ ** values (if this is an INSERT). */
+ SessionChange *pChange; /* New change object */
+ int nByte; /* Number of bytes to allocate */
+ int i; /* Used to iterate through columns */
+
+ assert( rc==SQLITE_OK );
+ pTab->nEntry++;
+
+ /* Figure out how large an allocation is required */
+ nByte = sizeof(SessionChange);
+ for(i=0; i<pTab->nCol; i++){
+ sqlite3_value *p = 0;
+ if( op!=SQLITE_INSERT ){
+ TESTONLY(int trc = ) pSession->hook.xOld(pSession->hook.pCtx, i, &p);
+ assert( trc==SQLITE_OK );
+ }else if( pTab->abPK[i] ){
+ TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
+ assert( trc==SQLITE_OK );
+ }
+
+ /* This may fail if SQLite value p contains a utf-16 string that must
+ ** be converted to utf-8 and an OOM error occurs while doing so. */
+ rc = sessionSerializeValue(0, p, &nByte);
+ if( rc!=SQLITE_OK ) goto error_out;
+ }
+
+ /* Allocate the change object */
+ pChange = (SessionChange *)sqlite3_malloc(nByte);
+ if( !pChange ){
+ rc = SQLITE_NOMEM;
+ goto error_out;
+ }else{
+ memset(pChange, 0, sizeof(SessionChange));
+ pChange->aRecord = (u8 *)&pChange[1];
+ }
+
+ /* Populate the change object. None of the preupdate_old(),
+ ** preupdate_new() or SerializeValue() calls below may fail as all
+ ** required values and encodings have already been cached in memory.
+ ** It is not possible for an OOM to occur in this block. */
+ nByte = 0;
+ for(i=0; i<pTab->nCol; i++){
+ sqlite3_value *p = 0;
+ if( op!=SQLITE_INSERT ){
+ pSession->hook.xOld(pSession->hook.pCtx, i, &p);
+ }else if( pTab->abPK[i] ){
+ pSession->hook.xNew(pSession->hook.pCtx, i, &p);
+ }
+ sessionSerializeValue(&pChange->aRecord[nByte], p, &nByte);
+ }
+
+ /* Add the change to the hash-table */
+ if( pSession->bIndirect || pSession->hook.xDepth(pSession->hook.pCtx) ){
+ pChange->bIndirect = 1;
+ }
+ pChange->nRecord = nByte;
+ pChange->op = op;
+ pChange->pNext = pTab->apChange[iHash];
+ pTab->apChange[iHash] = pChange;
+
+ }else if( pC->bIndirect ){
+ /* If the existing change is considered "indirect", but this current
+ ** change is "direct", mark the change object as direct. */
+ if( pSession->hook.xDepth(pSession->hook.pCtx)==0
+ && pSession->bIndirect==0
+ ){
+ pC->bIndirect = 0;
+ }
+ }
+ }
+
+ /* If an error has occurred, mark the session object as failed. */
+ error_out:
+ if( rc!=SQLITE_OK ){
+ pSession->rc = rc;
+ }
+}
+
+static int sessionFindTable(
+ sqlite3_session *pSession,
+ const char *zName,
+ SessionTable **ppTab
+){
+ int rc = SQLITE_OK;
+ int nName = sqlite3Strlen30(zName);
+ SessionTable *pRet;
+
+ /* Search for an existing table */
+ for(pRet=pSession->pTable; pRet; pRet=pRet->pNext){
+ if( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) ) break;
+ }
+
+ if( pRet==0 && pSession->bAutoAttach ){
+ /* If there is a table-filter configured, invoke it. If it returns 0,
+ ** do not automatically add the new table. */
+ if( pSession->xTableFilter==0
+ || pSession->xTableFilter(pSession->pFilterCtx, zName)
+ ){
+ rc = sqlite3session_attach(pSession, zName);
+ if( rc==SQLITE_OK ){
+ for(pRet=pSession->pTable; pRet->pNext; pRet=pRet->pNext);
+ assert( 0==sqlite3_strnicmp(pRet->zName, zName, nName+1) );
+ }
+ }
+ }
+
+ assert( rc==SQLITE_OK || pRet==0 );
+ *ppTab = pRet;
+ return rc;
+}
+
+/*
+** The 'pre-update' hook registered by this module with SQLite databases.
+*/
+static void xPreUpdate(
+ void *pCtx, /* Copy of third arg to preupdate_hook() */
+ sqlite3 *db, /* Database handle */
+ int op, /* SQLITE_UPDATE, DELETE or INSERT */
+ char const *zDb, /* Database name */
+ char const *zName, /* Table name */
+ sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
+ sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
+){
+ sqlite3_session *pSession;
+ int nDb = sqlite3Strlen30(zDb);
+
+ assert( sqlite3_mutex_held(db->mutex) );
+
+ for(pSession=(sqlite3_session *)pCtx; pSession; pSession=pSession->pNext){
+ SessionTable *pTab;
+
+ /* If this session is attached to a different database ("main", "temp"
+ ** etc.), or if it is not currently enabled, there is nothing to do. Skip
+ ** to the next session object attached to this database. */
+ if( pSession->bEnable==0 ) continue;
+ if( pSession->rc ) continue;
+ if( sqlite3_strnicmp(zDb, pSession->zDb, nDb+1) ) continue;
+
+ pSession->rc = sessionFindTable(pSession, zName, &pTab);
+ if( pTab ){
+ assert( pSession->rc==SQLITE_OK );
+ sessionPreupdateOneChange(op, pSession, pTab);
+ if( op==SQLITE_UPDATE ){
+ sessionPreupdateOneChange(SQLITE_INSERT, pSession, pTab);
+ }
+ }
+ }
+}
+
+/*
+** The pre-update hook implementations.
+*/
+static int sessionPreupdateOld(void *pCtx, int iVal, sqlite3_value **ppVal){
+ return sqlite3_preupdate_old((sqlite3*)pCtx, iVal, ppVal);
+}
+static int sessionPreupdateNew(void *pCtx, int iVal, sqlite3_value **ppVal){
+ return sqlite3_preupdate_new((sqlite3*)pCtx, iVal, ppVal);
+}
+static int sessionPreupdateCount(void *pCtx){
+ return sqlite3_preupdate_count((sqlite3*)pCtx);
+}
+static int sessionPreupdateDepth(void *pCtx){
+ return sqlite3_preupdate_depth((sqlite3*)pCtx);
+}
+
+/*
+** Install the pre-update hooks on the session object passed as the only
+** argument.
+*/
+static void sessionPreupdateHooks(
+ sqlite3_session *pSession
+){
+ pSession->hook.pCtx = (void*)pSession->db;
+ pSession->hook.xOld = sessionPreupdateOld;
+ pSession->hook.xNew = sessionPreupdateNew;
+ pSession->hook.xCount = sessionPreupdateCount;
+ pSession->hook.xDepth = sessionPreupdateDepth;
+}
+
+typedef struct SessionDiffCtx SessionDiffCtx;
+struct SessionDiffCtx {
+ sqlite3_stmt *pStmt;
+ int nOldOff;
+};
+
+/*
+** The diff hook implementations.
+*/
+static int sessionDiffOld(void *pCtx, int iVal, sqlite3_value **ppVal){
+ SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
+ *ppVal = sqlite3_column_value(p->pStmt, iVal+p->nOldOff);
+ return SQLITE_OK;
+}
+static int sessionDiffNew(void *pCtx, int iVal, sqlite3_value **ppVal){
+ SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
+ *ppVal = sqlite3_column_value(p->pStmt, iVal);
+ return SQLITE_OK;
+}
+static int sessionDiffCount(void *pCtx){
+ SessionDiffCtx *p = (SessionDiffCtx*)pCtx;
+ return p->nOldOff ? p->nOldOff : sqlite3_column_count(p->pStmt);
+}
+static int sessionDiffDepth(void *pCtx){
+ return 0;
+}
+
+/*
+** Install the diff hooks on the session object passed as the only
+** argument.
+*/
+static void sessionDiffHooks(
+ sqlite3_session *pSession,
+ SessionDiffCtx *pDiffCtx
+){
+ pSession->hook.pCtx = (void*)pDiffCtx;
+ pSession->hook.xOld = sessionDiffOld;
+ pSession->hook.xNew = sessionDiffNew;
+ pSession->hook.xCount = sessionDiffCount;
+ pSession->hook.xDepth = sessionDiffDepth;
+}
+
+static char *sessionExprComparePK(
+ int nCol,
+ const char *zDb1, const char *zDb2,
+ const char *zTab,
+ const char **azCol, u8 *abPK
+){
+ int i;
+ const char *zSep = "";
+ char *zRet = 0;
+
+ for(i=0; i<nCol; i++){
+ if( abPK[i] ){
+ zRet = sqlite3_mprintf("%z%s\"%w\".\"%w\".\"%w\"=\"%w\".\"%w\".\"%w\"",
+ zRet, zSep, zDb1, zTab, azCol[i], zDb2, zTab, azCol[i]
+ );
+ zSep = " AND ";
+ if( zRet==0 ) break;
+ }
+ }
+
+ return zRet;
+}
+
+static char *sessionExprCompareOther(
+ int nCol,
+ const char *zDb1, const char *zDb2,
+ const char *zTab,
+ const char **azCol, u8 *abPK
+){
+ int i;
+ const char *zSep = "";
+ char *zRet = 0;
+ int bHave = 0;
+
+ for(i=0; i<nCol; i++){
+ if( abPK[i]==0 ){
+ bHave = 1;
+ zRet = sqlite3_mprintf(
+ "%z%s\"%w\".\"%w\".\"%w\" IS NOT \"%w\".\"%w\".\"%w\"",
+ zRet, zSep, zDb1, zTab, azCol[i], zDb2, zTab, azCol[i]
+ );
+ zSep = " OR ";
+ if( zRet==0 ) break;
+ }
+ }
+
+ if( bHave==0 ){
+ assert( zRet==0 );
+ zRet = sqlite3_mprintf("0");
+ }
+
+ return zRet;
+}
+
+static char *sessionSelectFindNew(
+ int nCol,
+ const char *zDb1, /* Pick rows in this db only */
+ const char *zDb2, /* But not in this one */
+ const char *zTbl, /* Table name */
+ const char *zExpr
+){
+ char *zRet = sqlite3_mprintf(
+ "SELECT * FROM \"%w\".\"%w\" WHERE NOT EXISTS ("
+ " SELECT 1 FROM \"%w\".\"%w\" WHERE %s"
+ ")",
+ zDb1, zTbl, zDb2, zTbl, zExpr
+ );
+ return zRet;
+}
+
+static int sessionDiffFindNew(
+ int op,
+ sqlite3_session *pSession,
+ SessionTable *pTab,
+ const char *zDb1,
+ const char *zDb2,
+ char *zExpr
+){
+ int rc = SQLITE_OK;
+ char *zStmt = sessionSelectFindNew(pTab->nCol, zDb1, zDb2, pTab->zName,zExpr);
+
+ if( zStmt==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pStmt;
+ rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
+ if( rc==SQLITE_OK ){
+ SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
+ pDiffCtx->pStmt = pStmt;
+ pDiffCtx->nOldOff = 0;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ sessionPreupdateOneChange(op, pSession, pTab);
+ }
+ rc = sqlite3_finalize(pStmt);
+ }
+ sqlite3_free(zStmt);
+ }
+
+ return rc;
+}
+
+static int sessionDiffFindModified(
+ sqlite3_session *pSession,
+ SessionTable *pTab,
+ const char *zFrom,
+ const char *zExpr
+){
+ int rc = SQLITE_OK;
+
+ char *zExpr2 = sessionExprCompareOther(pTab->nCol,
+ pSession->zDb, zFrom, pTab->zName, pTab->azCol, pTab->abPK
+ );
+ if( zExpr2==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ char *zStmt = sqlite3_mprintf(
+ "SELECT * FROM \"%w\".\"%w\", \"%w\".\"%w\" WHERE %s AND (%z)",
+ pSession->zDb, pTab->zName, zFrom, pTab->zName, zExpr, zExpr2
+ );
+ if( zStmt==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pStmt;
+ rc = sqlite3_prepare(pSession->db, zStmt, -1, &pStmt, 0);
+
+ if( rc==SQLITE_OK ){
+ SessionDiffCtx *pDiffCtx = (SessionDiffCtx*)pSession->hook.pCtx;
+ pDiffCtx->pStmt = pStmt;
+ pDiffCtx->nOldOff = pTab->nCol;
+ while( SQLITE_ROW==sqlite3_step(pStmt) ){
+ sessionPreupdateOneChange(SQLITE_UPDATE, pSession, pTab);
+ }
+ rc = sqlite3_finalize(pStmt);
+ }
+ sqlite3_free(zStmt);
+ }
+ }
+
+ return rc;
+}
+
+SQLITE_API int sqlite3session_diff(
+ sqlite3_session *pSession,
+ const char *zFrom,
+ const char *zTbl,
+ char **pzErrMsg
+){
+ const char *zDb = pSession->zDb;
+ int rc = pSession->rc;
+ SessionDiffCtx d;
+
+ memset(&d, 0, sizeof(d));
+ sessionDiffHooks(pSession, &d);
+
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ if( pzErrMsg ) *pzErrMsg = 0;
+ if( rc==SQLITE_OK ){
+ char *zExpr = 0;
+ sqlite3 *db = pSession->db;
+ SessionTable *pTo; /* Table zTbl */
+
+ /* Locate and if necessary initialize the target table object */
+ rc = sessionFindTable(pSession, zTbl, &pTo);
+ if( pTo==0 ) goto diff_out;
+ if( sessionInitTable(pSession, pTo) ){
+ rc = pSession->rc;
+ goto diff_out;
+ }
+
+ /* Check the table schemas match */
+ if( rc==SQLITE_OK ){
+ int bHasPk = 0;
+ int bMismatch = 0;
+ int nCol; /* Columns in zFrom.zTbl */
+ u8 *abPK;
+ const char **azCol = 0;
+ rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
+ if( rc==SQLITE_OK ){
+ if( pTo->nCol!=nCol ){
+ bMismatch = 1;
+ }else{
+ int i;
+ for(i=0; i<nCol; i++){
+ if( pTo->abPK[i]!=abPK[i] ) bMismatch = 1;
+ if( sqlite3_stricmp(azCol[i], pTo->azCol[i]) ) bMismatch = 1;
+ if( abPK[i] ) bHasPk = 1;
+ }
+ }
+
+ }
+ sqlite3_free((char*)azCol);
+ if( bMismatch ){
+ *pzErrMsg = sqlite3_mprintf("table schemas do not match");
+ rc = SQLITE_SCHEMA;
+ }
+ if( bHasPk==0 ){
+ /* Ignore tables with no primary keys */
+ goto diff_out;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ zExpr = sessionExprComparePK(pTo->nCol,
+ zDb, zFrom, pTo->zName, pTo->azCol, pTo->abPK
+ );
+ }
+
+ /* Find new rows */
+ if( rc==SQLITE_OK ){
+ rc = sessionDiffFindNew(SQLITE_INSERT, pSession, pTo, zDb, zFrom, zExpr);
+ }
+
+ /* Find old rows */
+ if( rc==SQLITE_OK ){
+ rc = sessionDiffFindNew(SQLITE_DELETE, pSession, pTo, zFrom, zDb, zExpr);
+ }
+
+ /* Find modified rows */
+ if( rc==SQLITE_OK ){
+ rc = sessionDiffFindModified(pSession, pTo, zFrom, zExpr);
+ }
+
+ sqlite3_free(zExpr);
+ }
+
+ diff_out:
+ sessionPreupdateHooks(pSession);
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return rc;
+}
+
+/*
+** Create a session object. This session object will record changes to
+** database zDb attached to connection db.
+*/
+SQLITE_API int sqlite3session_create(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of db (e.g. "main") */
+ sqlite3_session **ppSession /* OUT: New session object */
+){
+ sqlite3_session *pNew; /* Newly allocated session object */
+ sqlite3_session *pOld; /* Session object already attached to db */
+ int nDb = sqlite3Strlen30(zDb); /* Length of zDb in bytes */
+
+ /* Zero the output value in case an error occurs. */
+ *ppSession = 0;
+
+ /* Allocate and populate the new session object. */
+ pNew = (sqlite3_session *)sqlite3_malloc(sizeof(sqlite3_session) + nDb + 1);
+ if( !pNew ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(sqlite3_session));
+ pNew->db = db;
+ pNew->zDb = (char *)&pNew[1];
+ pNew->bEnable = 1;
+ memcpy(pNew->zDb, zDb, nDb+1);
+ sessionPreupdateHooks(pNew);
+
+ /* Add the new session object to the linked list of session objects
+ ** attached to database handle $db. Do this under the cover of the db
+ ** handle mutex. */
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+ pOld = (sqlite3_session*)sqlite3_preupdate_hook(db, xPreUpdate, (void*)pNew);
+ pNew->pNext = pOld;
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+
+ *ppSession = pNew;
+ return SQLITE_OK;
+}
+
+/*
+** Free the list of table objects passed as the first argument. The contents
+** of the changed-rows hash tables are also deleted.
+*/
+static void sessionDeleteTable(SessionTable *pList){
+ SessionTable *pNext;
+ SessionTable *pTab;
+
+ for(pTab=pList; pTab; pTab=pNext){
+ int i;
+ pNext = pTab->pNext;
+ for(i=0; i<pTab->nChange; i++){
+ SessionChange *p;
+ SessionChange *pNextChange;
+ for(p=pTab->apChange[i]; p; p=pNextChange){
+ pNextChange = p->pNext;
+ sqlite3_free(p);
+ }
+ }
+ sqlite3_free((char*)pTab->azCol); /* cast works around VC++ bug */
+ sqlite3_free(pTab->apChange);
+ sqlite3_free(pTab);
+ }
+}
+
+/*
+** Delete a session object previously allocated using sqlite3session_create().
+*/
+SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){
+ sqlite3 *db = pSession->db;
+ sqlite3_session *pHead;
+ sqlite3_session **pp;
+
+ /* Unlink the session from the linked list of sessions attached to the
+ ** database handle. Hold the db mutex while doing so. */
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+ pHead = (sqlite3_session*)sqlite3_preupdate_hook(db, 0, 0);
+ for(pp=&pHead; ALWAYS((*pp)!=0); pp=&((*pp)->pNext)){
+ if( (*pp)==pSession ){
+ *pp = (*pp)->pNext;
+ if( pHead ) sqlite3_preupdate_hook(db, xPreUpdate, (void*)pHead);
+ break;
+ }
+ }
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+
+ /* Delete all attached table objects. And the contents of their
+ ** associated hash-tables. */
+ sessionDeleteTable(pSession->pTable);
+
+ /* Free the session object itself. */
+ sqlite3_free(pSession);
+}
+
+/*
+** Set a table filter on a Session Object.
+*/
+SQLITE_API void sqlite3session_table_filter(
+ sqlite3_session *pSession,
+ int(*xFilter)(void*, const char*),
+ void *pCtx /* First argument passed to xFilter */
+){
+ pSession->bAutoAttach = 1;
+ pSession->pFilterCtx = pCtx;
+ pSession->xTableFilter = xFilter;
+}
+
+/*
+** Attach a table to a session. All subsequent changes made to the table
+** while the session object is enabled will be recorded.
+**
+** Only tables that have a PRIMARY KEY defined may be attached. It does
+** not matter if the PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias)
+** or not.
+*/
+SQLITE_API int sqlite3session_attach(
+ sqlite3_session *pSession, /* Session object */
+ const char *zName /* Table name */
+){
+ int rc = SQLITE_OK;
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+
+ if( !zName ){
+ pSession->bAutoAttach = 1;
+ }else{
+ SessionTable *pTab; /* New table object (if required) */
+ int nName; /* Number of bytes in string zName */
+
+ /* First search for an existing entry. If one is found, this call is
+ ** a no-op. Return early. */
+ nName = sqlite3Strlen30(zName);
+ for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
+ if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break;
+ }
+
+ if( !pTab ){
+ /* Allocate new SessionTable object. */
+ pTab = (SessionTable *)sqlite3_malloc(sizeof(SessionTable) + nName + 1);
+ if( !pTab ){
+ rc = SQLITE_NOMEM;
+ }else{
+ /* Populate the new SessionTable object and link it into the list.
+ ** The new object must be linked onto the end of the list, not
+ ** simply added to the start of it in order to ensure that tables
+ ** appear in the correct order when a changeset or patchset is
+ ** eventually generated. */
+ SessionTable **ppTab;
+ memset(pTab, 0, sizeof(SessionTable));
+ pTab->zName = (char *)&pTab[1];
+ memcpy(pTab->zName, zName, nName+1);
+ for(ppTab=&pSession->pTable; *ppTab; ppTab=&(*ppTab)->pNext);
+ *ppTab = pTab;
+ }
+ }
+ }
+
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return rc;
+}
+
+/*
+** Ensure that there is room in the buffer to append nByte bytes of data.
+** If not, use sqlite3_realloc() to grow the buffer so that there is.
+**
+** If successful, return zero. Otherwise, if an OOM condition is encountered,
+** set *pRc to SQLITE_NOMEM and return non-zero.
+*/
+static int sessionBufferGrow(SessionBuffer *p, int nByte, int *pRc){
+ if( *pRc==SQLITE_OK && p->nAlloc-p->nBuf<nByte ){
+ u8 *aNew;
+ int nNew = p->nAlloc ? p->nAlloc : 128;
+ do {
+ nNew = nNew*2;
+ }while( nNew<(p->nBuf+nByte) );
+
+ aNew = (u8 *)sqlite3_realloc(p->aBuf, nNew);
+ if( 0==aNew ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ p->aBuf = aNew;
+ p->nAlloc = nNew;
+ }
+ }
+ return (*pRc!=SQLITE_OK);
+}
+
+/*
+** Append the value passed as the second argument to the buffer passed
+** as the first.
+**
+** This function is a no-op if *pRc is non-zero when it is called.
+** Otherwise, if an error occurs, *pRc is set to an SQLite error code
+** before returning.
+*/
+static void sessionAppendValue(SessionBuffer *p, sqlite3_value *pVal, int *pRc){
+ int rc = *pRc;
+ if( rc==SQLITE_OK ){
+ int nByte = 0;
+ rc = sessionSerializeValue(0, pVal, &nByte);
+ sessionBufferGrow(p, nByte, &rc);
+ if( rc==SQLITE_OK ){
+ rc = sessionSerializeValue(&p->aBuf[p->nBuf], pVal, 0);
+ p->nBuf += nByte;
+ }else{
+ *pRc = rc;
+ }
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a single byte to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendByte(SessionBuffer *p, u8 v, int *pRc){
+ if( 0==sessionBufferGrow(p, 1, pRc) ){
+ p->aBuf[p->nBuf++] = v;
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a single varint to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendVarint(SessionBuffer *p, int v, int *pRc){
+ if( 0==sessionBufferGrow(p, 9, pRc) ){
+ p->nBuf += sessionVarintPut(&p->aBuf[p->nBuf], v);
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a blob of data to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendBlob(
+ SessionBuffer *p,
+ const u8 *aBlob,
+ int nBlob,
+ int *pRc
+){
+ if( 0==sessionBufferGrow(p, nBlob, pRc) ){
+ memcpy(&p->aBuf[p->nBuf], aBlob, nBlob);
+ p->nBuf += nBlob;
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append a string to the buffer. All bytes in the string
+** up to (but not including) the nul-terminator are written to the buffer.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendStr(
+ SessionBuffer *p,
+ const char *zStr,
+ int *pRc
+){
+ int nStr = sqlite3Strlen30(zStr);
+ if( 0==sessionBufferGrow(p, nStr, pRc) ){
+ memcpy(&p->aBuf[p->nBuf], zStr, nStr);
+ p->nBuf += nStr;
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append the string representation of integer iVal
+** to the buffer. No nul-terminator is written.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendInteger(
+ SessionBuffer *p, /* Buffer to append to */
+ int iVal, /* Value to write the string rep. of */
+ int *pRc /* IN/OUT: Error code */
+){
+ char aBuf[24];
+ sqlite3_snprintf(sizeof(aBuf)-1, aBuf, "%d", iVal);
+ sessionAppendStr(p, aBuf, pRc);
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwise, append the string zStr enclosed in quotes (") and
+** with any embedded quote characters escaped to the buffer. No
+** nul-terminator byte is written.
+**
+** If an OOM condition is encountered, set *pRc to SQLITE_NOMEM before
+** returning.
+*/
+static void sessionAppendIdent(
+ SessionBuffer *p, /* Buffer to a append to */
+ const char *zStr, /* String to quote, escape and append */
+ int *pRc /* IN/OUT: Error code */
+){
+ int nStr = sqlite3Strlen30(zStr)*2 + 2 + 1;
+ if( 0==sessionBufferGrow(p, nStr, pRc) ){
+ char *zOut = (char *)&p->aBuf[p->nBuf];
+ const char *zIn = zStr;
+ *zOut++ = '"';
+ while( *zIn ){
+ if( *zIn=='"' ) *zOut++ = '"';
+ *zOut++ = *(zIn++);
+ }
+ *zOut++ = '"';
+ p->nBuf = (int)((u8 *)zOut - p->aBuf);
+ }
+}
+
+/*
+** This function is a no-op if *pRc is other than SQLITE_OK when it is
+** called. Otherwse, it appends the serialized version of the value stored
+** in column iCol of the row that SQL statement pStmt currently points
+** to to the buffer.
+*/
+static void sessionAppendCol(
+ SessionBuffer *p, /* Buffer to append to */
+ sqlite3_stmt *pStmt, /* Handle pointing to row containing value */
+ int iCol, /* Column to read value from */
+ int *pRc /* IN/OUT: Error code */
+){
+ if( *pRc==SQLITE_OK ){
+ int eType = sqlite3_column_type(pStmt, iCol);
+ sessionAppendByte(p, (u8)eType, pRc);
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ sqlite3_int64 i;
+ u8 aBuf[8];
+ if( eType==SQLITE_INTEGER ){
+ i = sqlite3_column_int64(pStmt, iCol);
+ }else{
+ double r = sqlite3_column_double(pStmt, iCol);
+ memcpy(&i, &r, 8);
+ }
+ sessionPutI64(aBuf, i);
+ sessionAppendBlob(p, aBuf, 8, pRc);
+ }
+ if( eType==SQLITE_BLOB || eType==SQLITE_TEXT ){
+ u8 *z;
+ int nByte;
+ if( eType==SQLITE_BLOB ){
+ z = (u8 *)sqlite3_column_blob(pStmt, iCol);
+ }else{
+ z = (u8 *)sqlite3_column_text(pStmt, iCol);
+ }
+ nByte = sqlite3_column_bytes(pStmt, iCol);
+ if( z || (eType==SQLITE_BLOB && nByte==0) ){
+ sessionAppendVarint(p, nByte, pRc);
+ sessionAppendBlob(p, z, nByte, pRc);
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ }
+}
+
+/*
+**
+** This function appends an update change to the buffer (see the comments
+** under "CHANGESET FORMAT" at the top of the file). An update change
+** consists of:
+**
+** 1 byte: SQLITE_UPDATE (0x17)
+** n bytes: old.* record (see RECORD FORMAT)
+** m bytes: new.* record (see RECORD FORMAT)
+**
+** The SessionChange object passed as the third argument contains the
+** values that were stored in the row when the session began (the old.*
+** values). The statement handle passed as the second argument points
+** at the current version of the row (the new.* values).
+**
+** If all of the old.* values are equal to their corresponding new.* value
+** (i.e. nothing has changed), then no data at all is appended to the buffer.
+**
+** Otherwise, the old.* record contains all primary key values and the
+** original values of any fields that have been modified. The new.* record
+** contains the new values of only those fields that have been modified.
+*/
+static int sessionAppendUpdate(
+ SessionBuffer *pBuf, /* Buffer to append to */
+ int bPatchset, /* True for "patchset", 0 for "changeset" */
+ sqlite3_stmt *pStmt, /* Statement handle pointing at new row */
+ SessionChange *p, /* Object containing old values */
+ u8 *abPK /* Boolean array - true for PK columns */
+){
+ int rc = SQLITE_OK;
+ SessionBuffer buf2 = {0,0,0}; /* Buffer to accumulate new.* record in */
+ int bNoop = 1; /* Set to zero if any values are modified */
+ int nRewind = pBuf->nBuf; /* Set to zero if any values are modified */
+ int i; /* Used to iterate through columns */
+ u8 *pCsr = p->aRecord; /* Used to iterate through old.* values */
+
+ sessionAppendByte(pBuf, SQLITE_UPDATE, &rc);
+ sessionAppendByte(pBuf, p->bIndirect, &rc);
+ for(i=0; i<sqlite3_column_count(pStmt); i++){
+ int bChanged = 0;
+ int nAdvance;
+ int eType = *pCsr;
+ switch( eType ){
+ case SQLITE_NULL:
+ nAdvance = 1;
+ if( sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){
+ bChanged = 1;
+ }
+ break;
+
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER: {
+ nAdvance = 9;
+ if( eType==sqlite3_column_type(pStmt, i) ){
+ sqlite3_int64 iVal = sessionGetI64(&pCsr[1]);
+ if( eType==SQLITE_INTEGER ){
+ if( iVal==sqlite3_column_int64(pStmt, i) ) break;
+ }else{
+ double dVal;
+ memcpy(&dVal, &iVal, 8);
+ if( dVal==sqlite3_column_double(pStmt, i) ) break;
+ }
+ }
+ bChanged = 1;
+ break;
+ }
+
+ default: {
+ int nByte;
+ int nHdr = 1 + sessionVarintGet(&pCsr[1], &nByte);
+ assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB );
+ nAdvance = nHdr + nByte;
+ if( eType==sqlite3_column_type(pStmt, i)
+ && nByte==sqlite3_column_bytes(pStmt, i)
+ && 0==memcmp(&pCsr[nHdr], sqlite3_column_blob(pStmt, i), nByte)
+ ){
+ break;
+ }
+ bChanged = 1;
+ }
+ }
+
+ /* If at least one field has been modified, this is not a no-op. */
+ if( bChanged ) bNoop = 0;
+
+ /* Add a field to the old.* record. This is omitted if this modules is
+ ** currently generating a patchset. */
+ if( bPatchset==0 ){
+ if( bChanged || abPK[i] ){
+ sessionAppendBlob(pBuf, pCsr, nAdvance, &rc);
+ }else{
+ sessionAppendByte(pBuf, 0, &rc);
+ }
+ }
+
+ /* Add a field to the new.* record. Or the only record if currently
+ ** generating a patchset. */
+ if( bChanged || (bPatchset && abPK[i]) ){
+ sessionAppendCol(&buf2, pStmt, i, &rc);
+ }else{
+ sessionAppendByte(&buf2, 0, &rc);
+ }
+
+ pCsr += nAdvance;
+ }
+
+ if( bNoop ){
+ pBuf->nBuf = nRewind;
+ }else{
+ sessionAppendBlob(pBuf, buf2.aBuf, buf2.nBuf, &rc);
+ }
+ sqlite3_free(buf2.aBuf);
+
+ return rc;
+}
+
+/*
+** Append a DELETE change to the buffer passed as the first argument. Use
+** the changeset format if argument bPatchset is zero, or the patchset
+** format otherwise.
+*/
+static int sessionAppendDelete(
+ SessionBuffer *pBuf, /* Buffer to append to */
+ int bPatchset, /* True for "patchset", 0 for "changeset" */
+ SessionChange *p, /* Object containing old values */
+ int nCol, /* Number of columns in table */
+ u8 *abPK /* Boolean array - true for PK columns */
+){
+ int rc = SQLITE_OK;
+
+ sessionAppendByte(pBuf, SQLITE_DELETE, &rc);
+ sessionAppendByte(pBuf, p->bIndirect, &rc);
+
+ if( bPatchset==0 ){
+ sessionAppendBlob(pBuf, p->aRecord, p->nRecord, &rc);
+ }else{
+ int i;
+ u8 *a = p->aRecord;
+ for(i=0; i<nCol; i++){
+ u8 *pStart = a;
+ int eType = *a++;
+
+ switch( eType ){
+ case 0:
+ case SQLITE_NULL:
+ assert( abPK[i]==0 );
+ break;
+
+ case SQLITE_FLOAT:
+ case SQLITE_INTEGER:
+ a += 8;
+ break;
+
+ default: {
+ int n;
+ a += sessionVarintGet(a, &n);
+ a += n;
+ break;
+ }
+ }
+ if( abPK[i] ){
+ sessionAppendBlob(pBuf, pStart, (int)(a-pStart), &rc);
+ }
+ }
+ assert( (a - p->aRecord)==p->nRecord );
+ }
+
+ return rc;
+}
+
+/*
+** Formulate and prepare a SELECT statement to retrieve a row from table
+** zTab in database zDb based on its primary key. i.e.
+**
+** SELECT * FROM zDb.zTab WHERE pk1 = ? AND pk2 = ? AND ...
+*/
+static int sessionSelectStmt(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Database name */
+ const char *zTab, /* Table name */
+ int nCol, /* Number of columns in table */
+ const char **azCol, /* Names of table columns */
+ u8 *abPK, /* PRIMARY KEY array */
+ sqlite3_stmt **ppStmt /* OUT: Prepared SELECT statement */
+){
+ int rc = SQLITE_OK;
+ int i;
+ const char *zSep = "";
+ SessionBuffer buf = {0, 0, 0};
+
+ sessionAppendStr(&buf, "SELECT * FROM ", &rc);
+ sessionAppendIdent(&buf, zDb, &rc);
+ sessionAppendStr(&buf, ".", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " WHERE ", &rc);
+ for(i=0; i<nCol; i++){
+ if( abPK[i] ){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, azCol[i], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, i+1, &rc);
+ zSep = " AND ";
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, ppStmt, 0);
+ }
+ sqlite3_free(buf.aBuf);
+ return rc;
+}
+
+/*
+** Bind the PRIMARY KEY values from the change passed in argument pChange
+** to the SELECT statement passed as the first argument. The SELECT statement
+** is as prepared by function sessionSelectStmt().
+**
+** Return SQLITE_OK if all PK values are successfully bound, or an SQLite
+** error code (e.g. SQLITE_NOMEM) otherwise.
+*/
+static int sessionSelectBind(
+ sqlite3_stmt *pSelect, /* SELECT from sessionSelectStmt() */
+ int nCol, /* Number of columns in table */
+ u8 *abPK, /* PRIMARY KEY array */
+ SessionChange *pChange /* Change structure */
+){
+ int i;
+ int rc = SQLITE_OK;
+ u8 *a = pChange->aRecord;
+
+ for(i=0; i<nCol && rc==SQLITE_OK; i++){
+ int eType = *a++;
+
+ switch( eType ){
+ case 0:
+ case SQLITE_NULL:
+ assert( abPK[i]==0 );
+ break;
+
+ case SQLITE_INTEGER: {
+ if( abPK[i] ){
+ i64 iVal = sessionGetI64(a);
+ rc = sqlite3_bind_int64(pSelect, i+1, iVal);
+ }
+ a += 8;
+ break;
+ }
+
+ case SQLITE_FLOAT: {
+ if( abPK[i] ){
+ double rVal;
+ i64 iVal = sessionGetI64(a);
+ memcpy(&rVal, &iVal, 8);
+ rc = sqlite3_bind_double(pSelect, i+1, rVal);
+ }
+ a += 8;
+ break;
+ }
+
+ case SQLITE_TEXT: {
+ int n;
+ a += sessionVarintGet(a, &n);
+ if( abPK[i] ){
+ rc = sqlite3_bind_text(pSelect, i+1, (char *)a, n, SQLITE_TRANSIENT);
+ }
+ a += n;
+ break;
+ }
+
+ default: {
+ int n;
+ assert( eType==SQLITE_BLOB );
+ a += sessionVarintGet(a, &n);
+ if( abPK[i] ){
+ rc = sqlite3_bind_blob(pSelect, i+1, a, n, SQLITE_TRANSIENT);
+ }
+ a += n;
+ break;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** This function is a no-op if *pRc is set to other than SQLITE_OK when it
+** is called. Otherwise, append a serialized table header (part of the binary
+** changeset format) to buffer *pBuf. If an error occurs, set *pRc to an
+** SQLite error code before returning.
+*/
+static void sessionAppendTableHdr(
+ SessionBuffer *pBuf, /* Append header to this buffer */
+ int bPatchset, /* Use the patchset format if true */
+ SessionTable *pTab, /* Table object to append header for */
+ int *pRc /* IN/OUT: Error code */
+){
+ /* Write a table header */
+ sessionAppendByte(pBuf, (bPatchset ? 'P' : 'T'), pRc);
+ sessionAppendVarint(pBuf, pTab->nCol, pRc);
+ sessionAppendBlob(pBuf, pTab->abPK, pTab->nCol, pRc);
+ sessionAppendBlob(pBuf, (u8 *)pTab->zName, (int)strlen(pTab->zName)+1, pRc);
+}
+
+/*
+** Generate either a changeset (if argument bPatchset is zero) or a patchset
+** (if it is non-zero) based on the current contents of the session object
+** passed as the first argument.
+**
+** If no error occurs, SQLITE_OK is returned and the new changeset/patchset
+** stored in output variables *pnChangeset and *ppChangeset. Or, if an error
+** occurs, an SQLite error code is returned and both output variables set
+** to 0.
+*/
+static int sessionGenerateChangeset(
+ sqlite3_session *pSession, /* Session object */
+ int bPatchset, /* True for patchset, false for changeset */
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut, /* First argument for xOutput */
+ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppChangeset /* OUT: Buffer containing changeset */
+){
+ sqlite3 *db = pSession->db; /* Source database handle */
+ SessionTable *pTab; /* Used to iterate through attached tables */
+ SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
+ int rc; /* Return code */
+
+ assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0 ) );
+
+ /* Zero the output variables in case an error occurs. If this session
+ ** object is already in the error state (sqlite3_session.rc != SQLITE_OK),
+ ** this call will be a no-op. */
+ if( xOutput==0 ){
+ *pnChangeset = 0;
+ *ppChangeset = 0;
+ }
+
+ if( pSession->rc ) return pSession->rc;
+ rc = sqlite3_exec(pSession->db, "SAVEPOINT changeset", 0, 0, 0);
+ if( rc!=SQLITE_OK ) return rc;
+
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+
+ for(pTab=pSession->pTable; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
+ if( pTab->nEntry ){
+ const char *zName = pTab->zName;
+ int nCol; /* Number of columns in table */
+ u8 *abPK; /* Primary key array */
+ const char **azCol = 0; /* Table columns */
+ int i; /* Used to iterate through hash buckets */
+ sqlite3_stmt *pSel = 0; /* SELECT statement to query table pTab */
+ int nRewind = buf.nBuf; /* Initial size of write buffer */
+ int nNoop; /* Size of buffer after writing tbl header */
+
+ /* Check the table schema is still Ok. */
+ rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
+ if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
+ rc = SQLITE_SCHEMA;
+ }
+
+ /* Write a table header */
+ sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
+
+ /* Build and compile a statement to execute: */
+ if( rc==SQLITE_OK ){
+ rc = sessionSelectStmt(
+ db, pSession->zDb, zName, nCol, azCol, abPK, &pSel);
+ }
+
+ nNoop = buf.nBuf;
+ for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
+ SessionChange *p; /* Used to iterate through changes */
+
+ for(p=pTab->apChange[i]; rc==SQLITE_OK && p; p=p->pNext){
+ rc = sessionSelectBind(pSel, nCol, abPK, p);
+ if( rc!=SQLITE_OK ) continue;
+ if( sqlite3_step(pSel)==SQLITE_ROW ){
+ if( p->op==SQLITE_INSERT ){
+ int iCol;
+ sessionAppendByte(&buf, SQLITE_INSERT, &rc);
+ sessionAppendByte(&buf, p->bIndirect, &rc);
+ for(iCol=0; iCol<nCol; iCol++){
+ sessionAppendCol(&buf, pSel, iCol, &rc);
+ }
+ }else{
+ rc = sessionAppendUpdate(&buf, bPatchset, pSel, p, abPK);
+ }
+ }else if( p->op!=SQLITE_INSERT ){
+ rc = sessionAppendDelete(&buf, bPatchset, p, nCol, abPK);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_reset(pSel);
+ }
+
+ /* If the buffer is now larger than SESSIONS_STRM_CHUNK_SIZE, pass
+ ** its contents to the xOutput() callback. */
+ if( xOutput
+ && rc==SQLITE_OK
+ && buf.nBuf>nNoop
+ && buf.nBuf>SESSIONS_STRM_CHUNK_SIZE
+ ){
+ rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf);
+ nNoop = -1;
+ buf.nBuf = 0;
+ }
+
+ }
+ }
+
+ sqlite3_finalize(pSel);
+ if( buf.nBuf==nNoop ){
+ buf.nBuf = nRewind;
+ }
+ sqlite3_free((char*)azCol); /* cast works around VC++ bug */
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ if( xOutput==0 ){
+ *pnChangeset = buf.nBuf;
+ *ppChangeset = buf.aBuf;
+ buf.aBuf = 0;
+ }else if( buf.nBuf>0 ){
+ rc = xOutput(pOut, (void*)buf.aBuf, buf.nBuf);
+ }
+ }
+
+ sqlite3_free(buf.aBuf);
+ sqlite3_exec(db, "RELEASE changeset", 0, 0, 0);
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+ return rc;
+}
+
+/*
+** Obtain a changeset object containing all changes recorded by the
+** session object passed as the first argument.
+**
+** It is the responsibility of the caller to eventually free the buffer
+** using sqlite3_free().
+*/
+SQLITE_API int sqlite3session_changeset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppChangeset /* OUT: Buffer containing changeset */
+){
+ return sessionGenerateChangeset(pSession, 0, 0, 0, pnChangeset, ppChangeset);
+}
+
+/*
+** Streaming version of sqlite3session_changeset().
+*/
+SQLITE_API int sqlite3session_changeset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ return sessionGenerateChangeset(pSession, 0, xOutput, pOut, 0, 0);
+}
+
+/*
+** Streaming version of sqlite3session_patchset().
+*/
+SQLITE_API int sqlite3session_patchset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ return sessionGenerateChangeset(pSession, 1, xOutput, pOut, 0, 0);
+}
+
+/*
+** Obtain a patchset object containing all changes recorded by the
+** session object passed as the first argument.
+**
+** It is the responsibility of the caller to eventually free the buffer
+** using sqlite3_free().
+*/
+SQLITE_API int sqlite3session_patchset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppPatchset /* OUT: Buffer containing changeset */
+){
+ return sessionGenerateChangeset(pSession, 1, 0, 0, pnPatchset, ppPatchset);
+}
+
+/*
+** Enable or disable the session object passed as the first argument.
+*/
+SQLITE_API int sqlite3session_enable(sqlite3_session *pSession, int bEnable){
+ int ret;
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ if( bEnable>=0 ){
+ pSession->bEnable = bEnable;
+ }
+ ret = pSession->bEnable;
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return ret;
+}
+
+/*
+** Enable or disable the session object passed as the first argument.
+*/
+SQLITE_API int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect){
+ int ret;
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ if( bIndirect>=0 ){
+ pSession->bIndirect = bIndirect;
+ }
+ ret = pSession->bIndirect;
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+ return ret;
+}
+
+/*
+** Return true if there have been no changes to monitored tables recorded
+** by the session object passed as the only argument.
+*/
+SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession){
+ int ret = 0;
+ SessionTable *pTab;
+
+ sqlite3_mutex_enter(sqlite3_db_mutex(pSession->db));
+ for(pTab=pSession->pTable; pTab && ret==0; pTab=pTab->pNext){
+ ret = (pTab->nEntry>0);
+ }
+ sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));
+
+ return (ret==0);
+}
+
+/*
+** Do the work for either sqlite3changeset_start() or start_strm().
+*/
+static int sessionChangesetStart(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int nChangeset, /* Size of buffer pChangeset in bytes */
+ void *pChangeset /* Pointer to buffer containing changeset */
+){
+ sqlite3_changeset_iter *pRet; /* Iterator to return */
+ int nByte; /* Number of bytes to allocate for iterator */
+
+ assert( xInput==0 || (pChangeset==0 && nChangeset==0) );
+
+ /* Zero the output variable in case an error occurs. */
+ *pp = 0;
+
+ /* Allocate and initialize the iterator structure. */
+ nByte = sizeof(sqlite3_changeset_iter);
+ pRet = (sqlite3_changeset_iter *)sqlite3_malloc(nByte);
+ if( !pRet ) return SQLITE_NOMEM;
+ memset(pRet, 0, sizeof(sqlite3_changeset_iter));
+ pRet->in.aData = (u8 *)pChangeset;
+ pRet->in.nData = nChangeset;
+ pRet->in.xInput = xInput;
+ pRet->in.pIn = pIn;
+ pRet->in.bEof = (xInput ? 0 : 1);
+
+ /* Populate the output variable and return success. */
+ *pp = pRet;
+ return SQLITE_OK;
+}
+
+/*
+** Create an iterator used to iterate through the contents of a changeset.
+*/
+SQLITE_API int sqlite3changeset_start(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int nChangeset, /* Size of buffer pChangeset in bytes */
+ void *pChangeset /* Pointer to buffer containing changeset */
+){
+ return sessionChangesetStart(pp, 0, 0, nChangeset, pChangeset);
+}
+
+/*
+** Streaming version of sqlite3changeset_start().
+*/
+SQLITE_API int sqlite3changeset_start_strm(
+ sqlite3_changeset_iter **pp, /* OUT: Changeset iterator handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+){
+ return sessionChangesetStart(pp, xInput, pIn, 0, 0);
+}
+
+/*
+** If the SessionInput object passed as the only argument is a streaming
+** object and the buffer is full, discard some data to free up space.
+*/
+static void sessionDiscardData(SessionInput *pIn){
+ if( pIn->bEof && pIn->xInput && pIn->iNext>=SESSIONS_STRM_CHUNK_SIZE ){
+ int nMove = pIn->buf.nBuf - pIn->iNext;
+ assert( nMove>=0 );
+ if( nMove>0 ){
+ memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iNext], nMove);
+ }
+ pIn->buf.nBuf -= pIn->iNext;
+ pIn->iNext = 0;
+ pIn->nData = pIn->buf.nBuf;
+ }
+}
+
+/*
+** Ensure that there are at least nByte bytes available in the buffer. Or,
+** if there are not nByte bytes remaining in the input, that all available
+** data is in the buffer.
+**
+** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+*/
+static int sessionInputBuffer(SessionInput *pIn, int nByte){
+ int rc = SQLITE_OK;
+ if( pIn->xInput ){
+ while( !pIn->bEof && (pIn->iNext+nByte)>=pIn->nData && rc==SQLITE_OK ){
+ int nNew = SESSIONS_STRM_CHUNK_SIZE;
+
+ if( pIn->bNoDiscard==0 ) sessionDiscardData(pIn);
+ if( SQLITE_OK==sessionBufferGrow(&pIn->buf, nNew, &rc) ){
+ rc = pIn->xInput(pIn->pIn, &pIn->buf.aBuf[pIn->buf.nBuf], &nNew);
+ if( nNew==0 ){
+ pIn->bEof = 1;
+ }else{
+ pIn->buf.nBuf += nNew;
+ }
+ }
+
+ pIn->aData = pIn->buf.aBuf;
+ pIn->nData = pIn->buf.nBuf;
+ }
+ }
+ return rc;
+}
+
+/*
+** When this function is called, *ppRec points to the start of a record
+** that contains nCol values. This function advances the pointer *ppRec
+** until it points to the byte immediately following that record.
+*/
+static void sessionSkipRecord(
+ u8 **ppRec, /* IN/OUT: Record pointer */
+ int nCol /* Number of values in record */
+){
+ u8 *aRec = *ppRec;
+ int i;
+ for(i=0; i<nCol; i++){
+ int eType = *aRec++;
+ if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ int nByte;
+ aRec += sessionVarintGet((u8*)aRec, &nByte);
+ aRec += nByte;
+ }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ aRec += 8;
+ }
+ }
+
+ *ppRec = aRec;
+}
+
+/*
+** This function sets the value of the sqlite3_value object passed as the
+** first argument to a copy of the string or blob held in the aData[]
+** buffer. SQLITE_OK is returned if successful, or SQLITE_NOMEM if an OOM
+** error occurs.
+*/
+static int sessionValueSetStr(
+ sqlite3_value *pVal, /* Set the value of this object */
+ u8 *aData, /* Buffer containing string or blob data */
+ int nData, /* Size of buffer aData[] in bytes */
+ u8 enc /* String encoding (0 for blobs) */
+){
+ /* In theory this code could just pass SQLITE_TRANSIENT as the final
+ ** argument to sqlite3ValueSetStr() and have the copy created
+ ** automatically. But doing so makes it difficult to detect any OOM
+ ** error. Hence the code to create the copy externally. */
+ u8 *aCopy = sqlite3_malloc(nData+1);
+ if( aCopy==0 ) return SQLITE_NOMEM;
+ memcpy(aCopy, aData, nData);
+ sqlite3ValueSetStr(pVal, nData, (char*)aCopy, enc, sqlite3_free);
+ return SQLITE_OK;
+}
+
+/*
+** Deserialize a single record from a buffer in memory. See "RECORD FORMAT"
+** for details.
+**
+** When this function is called, *paChange points to the start of the record
+** to deserialize. Assuming no error occurs, *paChange is set to point to
+** one byte after the end of the same record before this function returns.
+** If the argument abPK is NULL, then the record contains nCol values. Or,
+** if abPK is other than NULL, then the record contains only the PK fields
+** (in other words, it is a patchset DELETE record).
+**
+** If successful, each element of the apOut[] array (allocated by the caller)
+** is set to point to an sqlite3_value object containing the value read
+** from the corresponding position in the record. If that value is not
+** included in the record (i.e. because the record is part of an UPDATE change
+** and the field was not modified), the corresponding element of apOut[] is
+** set to NULL.
+**
+** It is the responsibility of the caller to free all sqlite_value structures
+** using sqlite3_free().
+**
+** If an error occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
+** The apOut[] array may have been partially populated in this case.
+*/
+static int sessionReadRecord(
+ SessionInput *pIn, /* Input data */
+ int nCol, /* Number of values in record */
+ u8 *abPK, /* Array of primary key flags, or NULL */
+ sqlite3_value **apOut /* Write values to this array */
+){
+ int i; /* Used to iterate through columns */
+ int rc = SQLITE_OK;
+
+ for(i=0; i<nCol && rc==SQLITE_OK; i++){
+ int eType = 0; /* Type of value (SQLITE_NULL, TEXT etc.) */
+ if( abPK && abPK[i]==0 ) continue;
+ rc = sessionInputBuffer(pIn, 9);
+ if( rc==SQLITE_OK ){
+ eType = pIn->aData[pIn->iNext++];
+ }
+
+ assert( apOut[i]==0 );
+ if( eType ){
+ apOut[i] = sqlite3ValueNew(0);
+ if( !apOut[i] ) rc = SQLITE_NOMEM;
+ }
+
+ if( rc==SQLITE_OK ){
+ u8 *aVal = &pIn->aData[pIn->iNext];
+ if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ int nByte;
+ pIn->iNext += sessionVarintGet(aVal, &nByte);
+ rc = sessionInputBuffer(pIn, nByte);
+ if( rc==SQLITE_OK ){
+ u8 enc = (eType==SQLITE_TEXT ? SQLITE_UTF8 : 0);
+ rc = sessionValueSetStr(apOut[i],&pIn->aData[pIn->iNext],nByte,enc);
+ }
+ pIn->iNext += nByte;
+ }
+ if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ sqlite3_int64 v = sessionGetI64(aVal);
+ if( eType==SQLITE_INTEGER ){
+ sqlite3VdbeMemSetInt64(apOut[i], v);
+ }else{
+ double d;
+ memcpy(&d, &v, 8);
+ sqlite3VdbeMemSetDouble(apOut[i], d);
+ }
+ pIn->iNext += 8;
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** The input pointer currently points to the second byte of a table-header.
+** Specifically, to the following:
+**
+** + number of columns in table (varint)
+** + array of PK flags (1 byte per column),
+** + table name (nul terminated).
+**
+** This function ensures that all of the above is present in the input
+** buffer (i.e. that it can be accessed without any calls to xInput()).
+** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code.
+** The input pointer is not moved.
+*/
+static int sessionChangesetBufferTblhdr(SessionInput *pIn, int *pnByte){
+ int rc = SQLITE_OK;
+ int nCol = 0;
+ int nRead = 0;
+
+ rc = sessionInputBuffer(pIn, 9);
+ if( rc==SQLITE_OK ){
+ nRead += sessionVarintGet(&pIn->aData[pIn->iNext + nRead], &nCol);
+ rc = sessionInputBuffer(pIn, nRead+nCol+100);
+ nRead += nCol;
+ }
+
+ while( rc==SQLITE_OK ){
+ while( (pIn->iNext + nRead)<pIn->nData && pIn->aData[pIn->iNext + nRead] ){
+ nRead++;
+ }
+ if( (pIn->iNext + nRead)<pIn->nData ) break;
+ rc = sessionInputBuffer(pIn, nRead + 100);
+ }
+ *pnByte = nRead+1;
+ return rc;
+}
+
+/*
+** The input pointer currently points to the first byte of the first field
+** of a record consisting of nCol columns. This function ensures the entire
+** record is buffered. It does not move the input pointer.
+**
+** If successful, SQLITE_OK is returned and *pnByte is set to the size of
+** the record in bytes. Otherwise, an SQLite error code is returned. The
+** final value of *pnByte is undefined in this case.
+*/
+static int sessionChangesetBufferRecord(
+ SessionInput *pIn, /* Input data */
+ int nCol, /* Number of columns in record */
+ int *pnByte /* OUT: Size of record in bytes */
+){
+ int rc = SQLITE_OK;
+ int nByte = 0;
+ int i;
+ for(i=0; rc==SQLITE_OK && i<nCol; i++){
+ int eType;
+ rc = sessionInputBuffer(pIn, nByte + 10);
+ if( rc==SQLITE_OK ){
+ eType = pIn->aData[pIn->iNext + nByte++];
+ if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){
+ int n;
+ nByte += sessionVarintGet(&pIn->aData[pIn->iNext+nByte], &n);
+ nByte += n;
+ rc = sessionInputBuffer(pIn, nByte);
+ }else if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
+ nByte += 8;
+ }
+ }
+ }
+ *pnByte = nByte;
+ return rc;
+}
+
+/*
+** The input pointer currently points to the second byte of a table-header.
+** Specifically, to the following:
+**
+** + number of columns in table (varint)
+** + array of PK flags (1 byte per column),
+** + table name (nul terminated).
+**
+** This function decodes the table-header and populates the p->nCol,
+** p->zTab and p->abPK[] variables accordingly. The p->apValue[] array is
+** also allocated or resized according to the new value of p->nCol. The
+** input pointer is left pointing to the byte following the table header.
+**
+** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code
+** is returned and the final values of the various fields enumerated above
+** are undefined.
+*/
+static int sessionChangesetReadTblhdr(sqlite3_changeset_iter *p){
+ int rc;
+ int nCopy;
+ assert( p->rc==SQLITE_OK );
+
+ rc = sessionChangesetBufferTblhdr(&p->in, &nCopy);
+ if( rc==SQLITE_OK ){
+ int nByte;
+ int nVarint;
+ nVarint = sessionVarintGet(&p->in.aData[p->in.iNext], &p->nCol);
+ nCopy -= nVarint;
+ p->in.iNext += nVarint;
+ nByte = p->nCol * sizeof(sqlite3_value*) * 2 + nCopy;
+ p->tblhdr.nBuf = 0;
+ sessionBufferGrow(&p->tblhdr, nByte, &rc);
+ }
+
+ if( rc==SQLITE_OK ){
+ int iPK = sizeof(sqlite3_value*)*p->nCol*2;
+ memset(p->tblhdr.aBuf, 0, iPK);
+ memcpy(&p->tblhdr.aBuf[iPK], &p->in.aData[p->in.iNext], nCopy);
+ p->in.iNext += nCopy;
+ }
+
+ p->apValue = (sqlite3_value**)p->tblhdr.aBuf;
+ p->abPK = (u8*)&p->apValue[p->nCol*2];
+ p->zTab = (char*)&p->abPK[p->nCol];
+ return (p->rc = rc);
+}
+
+/*
+** Advance the changeset iterator to the next change.
+**
+** If both paRec and pnRec are NULL, then this function works like the public
+** API sqlite3changeset_next(). If SQLITE_ROW is returned, then the
+** sqlite3changeset_new() and old() APIs may be used to query for values.
+**
+** Otherwise, if paRec and pnRec are not NULL, then a pointer to the change
+** record is written to *paRec before returning and the number of bytes in
+** the record to *pnRec.
+**
+** Either way, this function returns SQLITE_ROW if the iterator is
+** successfully advanced to the next change in the changeset, an SQLite
+** error code if an error occurs, or SQLITE_DONE if there are no further
+** changes in the changeset.
+*/
+static int sessionChangesetNext(
+ sqlite3_changeset_iter *p, /* Changeset iterator */
+ u8 **paRec, /* If non-NULL, store record pointer here */
+ int *pnRec /* If non-NULL, store size of record here */
+){
+ int i;
+ u8 op;
+
+ assert( (paRec==0 && pnRec==0) || (paRec && pnRec) );
+
+ /* If the iterator is in the error-state, return immediately. */
+ if( p->rc!=SQLITE_OK ) return p->rc;
+
+ /* Free the current contents of p->apValue[], if any. */
+ if( p->apValue ){
+ for(i=0; i<p->nCol*2; i++){
+ sqlite3ValueFree(p->apValue[i]);
+ }
+ memset(p->apValue, 0, sizeof(sqlite3_value*)*p->nCol*2);
+ }
+
+ /* Make sure the buffer contains at least 10 bytes of input data, or all
+ ** remaining data if there are less than 10 bytes available. This is
+ ** sufficient either for the 'T' or 'P' byte and the varint that follows
+ ** it, or for the two single byte values otherwise. */
+ p->rc = sessionInputBuffer(&p->in, 2);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+
+ /* If the iterator is already at the end of the changeset, return DONE. */
+ if( p->in.iNext>=p->in.nData ){
+ return SQLITE_DONE;
+ }
+
+ sessionDiscardData(&p->in);
+ p->in.iCurrent = p->in.iNext;
+
+ op = p->in.aData[p->in.iNext++];
+ if( op=='T' || op=='P' ){
+ p->bPatchset = (op=='P');
+ if( sessionChangesetReadTblhdr(p) ) return p->rc;
+ if( (p->rc = sessionInputBuffer(&p->in, 2)) ) return p->rc;
+ p->in.iCurrent = p->in.iNext;
+ op = p->in.aData[p->in.iNext++];
+ }
+
+ p->op = op;
+ p->bIndirect = p->in.aData[p->in.iNext++];
+ if( p->op!=SQLITE_UPDATE && p->op!=SQLITE_DELETE && p->op!=SQLITE_INSERT ){
+ return (p->rc = SQLITE_CORRUPT_BKPT);
+ }
+
+ if( paRec ){
+ int nVal; /* Number of values to buffer */
+ if( p->bPatchset==0 && op==SQLITE_UPDATE ){
+ nVal = p->nCol * 2;
+ }else if( p->bPatchset && op==SQLITE_DELETE ){
+ nVal = 0;
+ for(i=0; i<p->nCol; i++) if( p->abPK[i] ) nVal++;
+ }else{
+ nVal = p->nCol;
+ }
+ p->rc = sessionChangesetBufferRecord(&p->in, nVal, pnRec);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+ *paRec = &p->in.aData[p->in.iNext];
+ p->in.iNext += *pnRec;
+ }else{
+
+ /* If this is an UPDATE or DELETE, read the old.* record. */
+ if( p->op!=SQLITE_INSERT && (p->bPatchset==0 || p->op==SQLITE_DELETE) ){
+ u8 *abPK = p->bPatchset ? p->abPK : 0;
+ p->rc = sessionReadRecord(&p->in, p->nCol, abPK, p->apValue);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+ }
+
+ /* If this is an INSERT or UPDATE, read the new.* record. */
+ if( p->op!=SQLITE_DELETE ){
+ p->rc = sessionReadRecord(&p->in, p->nCol, 0, &p->apValue[p->nCol]);
+ if( p->rc!=SQLITE_OK ) return p->rc;
+ }
+
+ if( p->bPatchset && p->op==SQLITE_UPDATE ){
+ /* If this is an UPDATE that is part of a patchset, then all PK and
+ ** modified fields are present in the new.* record. The old.* record
+ ** is currently completely empty. This block shifts the PK fields from
+ ** new.* to old.*, to accommodate the code that reads these arrays. */
+ for(i=0; i<p->nCol; i++){
+ assert( p->apValue[i]==0 );
+ assert( p->abPK[i]==0 || p->apValue[i+p->nCol] );
+ if( p->abPK[i] ){
+ p->apValue[i] = p->apValue[i+p->nCol];
+ p->apValue[i+p->nCol] = 0;
+ }
+ }
+ }
+ }
+
+ return SQLITE_ROW;
+}
+
+/*
+** Advance an iterator created by sqlite3changeset_start() to the next
+** change in the changeset. This function may return SQLITE_ROW, SQLITE_DONE
+** or SQLITE_CORRUPT.
+**
+** This function may not be called on iterators passed to a conflict handler
+** callback by changeset_apply().
+*/
+SQLITE_API int sqlite3changeset_next(sqlite3_changeset_iter *p){
+ return sessionChangesetNext(p, 0, 0);
+}
+
+/*
+** The following function extracts information on the current change
+** from a changeset iterator. It may only be called after changeset_next()
+** has returned SQLITE_ROW.
+*/
+SQLITE_API int sqlite3changeset_op(
+ sqlite3_changeset_iter *pIter, /* Iterator handle */
+ const char **pzTab, /* OUT: Pointer to table name */
+ int *pnCol, /* OUT: Number of columns in table */
+ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
+ int *pbIndirect /* OUT: True if change is indirect */
+){
+ *pOp = pIter->op;
+ *pnCol = pIter->nCol;
+ *pzTab = pIter->zTab;
+ if( pbIndirect ) *pbIndirect = pIter->bIndirect;
+ return SQLITE_OK;
+}
+
+/*
+** Return information regarding the PRIMARY KEY and number of columns in
+** the database table affected by the change that pIter currently points
+** to. This function may only be called after changeset_next() returns
+** SQLITE_ROW.
+*/
+SQLITE_API int sqlite3changeset_pk(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */
+ int *pnCol /* OUT: Number of entries in output array */
+){
+ *pabPK = pIter->abPK;
+ if( pnCol ) *pnCol = pIter->nCol;
+ return SQLITE_OK;
+}
+
+/*
+** This function may only be called while the iterator is pointing to an
+** SQLITE_UPDATE or SQLITE_DELETE change (see sqlite3changeset_op()).
+** Otherwise, SQLITE_MISUSE is returned.
+**
+** It sets *ppValue to point to an sqlite3_value structure containing the
+** iVal'th value in the old.* record. Or, if that particular value is not
+** included in the record (because the change is an UPDATE and the field
+** was not modified and is not a PK column), set *ppValue to NULL.
+**
+** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
+** not modified. Otherwise, SQLITE_OK.
+*/
+SQLITE_API int sqlite3changeset_old(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Index of old.* value to retrieve */
+ sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */
+){
+ if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_DELETE ){
+ return SQLITE_MISUSE;
+ }
+ if( iVal<0 || iVal>=pIter->nCol ){
+ return SQLITE_RANGE;
+ }
+ *ppValue = pIter->apValue[iVal];
+ return SQLITE_OK;
+}
+
+/*
+** This function may only be called while the iterator is pointing to an
+** SQLITE_UPDATE or SQLITE_INSERT change (see sqlite3changeset_op()).
+** Otherwise, SQLITE_MISUSE is returned.
+**
+** It sets *ppValue to point to an sqlite3_value structure containing the
+** iVal'th value in the new.* record. Or, if that particular value is not
+** included in the record (because the change is an UPDATE and the field
+** was not modified), set *ppValue to NULL.
+**
+** If value iVal is out-of-range, SQLITE_RANGE is returned and *ppValue is
+** not modified. Otherwise, SQLITE_OK.
+*/
+SQLITE_API int sqlite3changeset_new(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Index of new.* value to retrieve */
+ sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */
+){
+ if( pIter->op!=SQLITE_UPDATE && pIter->op!=SQLITE_INSERT ){
+ return SQLITE_MISUSE;
+ }
+ if( iVal<0 || iVal>=pIter->nCol ){
+ return SQLITE_RANGE;
+ }
+ *ppValue = pIter->apValue[pIter->nCol+iVal];
+ return SQLITE_OK;
+}
+
+/*
+** The following two macros are used internally. They are similar to the
+** sqlite3changeset_new() and sqlite3changeset_old() functions, except that
+** they omit all error checking and return a pointer to the requested value.
+*/
+#define sessionChangesetNew(pIter, iVal) (pIter)->apValue[(pIter)->nCol+(iVal)]
+#define sessionChangesetOld(pIter, iVal) (pIter)->apValue[(iVal)]
+
+/*
+** This function may only be called with a changeset iterator that has been
+** passed to an SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT
+** conflict-handler function. Otherwise, SQLITE_MISUSE is returned.
+**
+** If successful, *ppValue is set to point to an sqlite3_value structure
+** containing the iVal'th value of the conflicting record.
+**
+** If value iVal is out-of-range or some other error occurs, an SQLite error
+** code is returned. Otherwise, SQLITE_OK.
+*/
+SQLITE_API int sqlite3changeset_conflict(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Index of conflict record value to fetch */
+ sqlite3_value **ppValue /* OUT: Value from conflicting row */
+){
+ if( !pIter->pConflict ){
+ return SQLITE_MISUSE;
+ }
+ if( iVal<0 || iVal>=sqlite3_column_count(pIter->pConflict) ){
+ return SQLITE_RANGE;
+ }
+ *ppValue = sqlite3_column_value(pIter->pConflict, iVal);
+ return SQLITE_OK;
+}
+
+/*
+** This function may only be called with an iterator passed to an
+** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
+** it sets the output variable to the total number of known foreign key
+** violations in the destination database and returns SQLITE_OK.
+**
+** In all other cases this function returns SQLITE_MISUSE.
+*/
+SQLITE_API int sqlite3changeset_fk_conflicts(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int *pnOut /* OUT: Number of FK violations */
+){
+ if( pIter->pConflict || pIter->apValue ){
+ return SQLITE_MISUSE;
+ }
+ *pnOut = pIter->nCol;
+ return SQLITE_OK;
+}
+
+
+/*
+** Finalize an iterator allocated with sqlite3changeset_start().
+**
+** This function may not be called on iterators passed to a conflict handler
+** callback by changeset_apply().
+*/
+SQLITE_API int sqlite3changeset_finalize(sqlite3_changeset_iter *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ int i; /* Used to iterate through p->apValue[] */
+ rc = p->rc;
+ if( p->apValue ){
+ for(i=0; i<p->nCol*2; i++) sqlite3ValueFree(p->apValue[i]);
+ }
+ sqlite3_free(p->tblhdr.aBuf);
+ sqlite3_free(p->in.buf.aBuf);
+ sqlite3_free(p);
+ }
+ return rc;
+}
+
+static int sessionChangesetInvert(
+ SessionInput *pInput, /* Input changeset */
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut,
+ int *pnInverted, /* OUT: Number of bytes in output changeset */
+ void **ppInverted /* OUT: Inverse of pChangeset */
+){
+ int rc = SQLITE_OK; /* Return value */
+ SessionBuffer sOut; /* Output buffer */
+ int nCol = 0; /* Number of cols in current table */
+ u8 *abPK = 0; /* PK array for current table */
+ sqlite3_value **apVal = 0; /* Space for values for UPDATE inversion */
+ SessionBuffer sPK = {0, 0, 0}; /* PK array for current table */
+
+ /* Initialize the output buffer */
+ memset(&sOut, 0, sizeof(SessionBuffer));
+
+ /* Zero the output variables in case an error occurs. */
+ if( ppInverted ){
+ *ppInverted = 0;
+ *pnInverted = 0;
+ }
+
+ while( 1 ){
+ u8 eType;
+
+ /* Test for EOF. */
+ if( (rc = sessionInputBuffer(pInput, 2)) ) goto finished_invert;
+ if( pInput->iNext>=pInput->nData ) break;
+ eType = pInput->aData[pInput->iNext];
+
+ switch( eType ){
+ case 'T': {
+ /* A 'table' record consists of:
+ **
+ ** * A constant 'T' character,
+ ** * Number of columns in said table (a varint),
+ ** * An array of nCol bytes (sPK),
+ ** * A nul-terminated table name.
+ */
+ int nByte;
+ int nVar;
+ pInput->iNext++;
+ if( (rc = sessionChangesetBufferTblhdr(pInput, &nByte)) ){
+ goto finished_invert;
+ }
+ nVar = sessionVarintGet(&pInput->aData[pInput->iNext], &nCol);
+ sPK.nBuf = 0;
+ sessionAppendBlob(&sPK, &pInput->aData[pInput->iNext+nVar], nCol, &rc);
+ sessionAppendByte(&sOut, eType, &rc);
+ sessionAppendBlob(&sOut, &pInput->aData[pInput->iNext], nByte, &rc);
+ if( rc ) goto finished_invert;
+
+ pInput->iNext += nByte;
+ sqlite3_free(apVal);
+ apVal = 0;
+ abPK = sPK.aBuf;
+ break;
+ }
+
+ case SQLITE_INSERT:
+ case SQLITE_DELETE: {
+ int nByte;
+ int bIndirect = pInput->aData[pInput->iNext+1];
+ int eType2 = (eType==SQLITE_DELETE ? SQLITE_INSERT : SQLITE_DELETE);
+ pInput->iNext += 2;
+ assert( rc==SQLITE_OK );
+ rc = sessionChangesetBufferRecord(pInput, nCol, &nByte);
+ sessionAppendByte(&sOut, eType2, &rc);
+ sessionAppendByte(&sOut, bIndirect, &rc);
+ sessionAppendBlob(&sOut, &pInput->aData[pInput->iNext], nByte, &rc);
+ pInput->iNext += nByte;
+ if( rc ) goto finished_invert;
+ break;
+ }
+
+ case SQLITE_UPDATE: {
+ int iCol;
+
+ if( 0==apVal ){
+ apVal = (sqlite3_value **)sqlite3_malloc(sizeof(apVal[0])*nCol*2);
+ if( 0==apVal ){
+ rc = SQLITE_NOMEM;
+ goto finished_invert;
+ }
+ memset(apVal, 0, sizeof(apVal[0])*nCol*2);
+ }
+
+ /* Write the header for the new UPDATE change. Same as the original. */
+ sessionAppendByte(&sOut, eType, &rc);
+ sessionAppendByte(&sOut, pInput->aData[pInput->iNext+1], &rc);
+
+ /* Read the old.* and new.* records for the update change. */
+ pInput->iNext += 2;
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[0]);
+ if( rc==SQLITE_OK ){
+ rc = sessionReadRecord(pInput, nCol, 0, &apVal[nCol]);
+ }
+
+ /* Write the new old.* record. Consists of the PK columns from the
+ ** original old.* record, and the other values from the original
+ ** new.* record. */
+ for(iCol=0; iCol<nCol; iCol++){
+ sqlite3_value *pVal = apVal[iCol + (abPK[iCol] ? 0 : nCol)];
+ sessionAppendValue(&sOut, pVal, &rc);
+ }
+
+ /* Write the new new.* record. Consists of a copy of all values
+ ** from the original old.* record, except for the PK columns, which
+ ** are set to "undefined". */
+ for(iCol=0; iCol<nCol; iCol++){
+ sqlite3_value *pVal = (abPK[iCol] ? 0 : apVal[iCol]);
+ sessionAppendValue(&sOut, pVal, &rc);
+ }
+
+ for(iCol=0; iCol<nCol*2; iCol++){
+ sqlite3ValueFree(apVal[iCol]);
+ }
+ memset(apVal, 0, sizeof(apVal[0])*nCol*2);
+ if( rc!=SQLITE_OK ){
+ goto finished_invert;
+ }
+
+ break;
+ }
+
+ default:
+ rc = SQLITE_CORRUPT_BKPT;
+ goto finished_invert;
+ }
+
+ assert( rc==SQLITE_OK );
+ if( xOutput && sOut.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
+ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
+ sOut.nBuf = 0;
+ if( rc!=SQLITE_OK ) goto finished_invert;
+ }
+ }
+
+ assert( rc==SQLITE_OK );
+ if( pnInverted ){
+ *pnInverted = sOut.nBuf;
+ *ppInverted = sOut.aBuf;
+ sOut.aBuf = 0;
+ }else if( sOut.nBuf>0 ){
+ rc = xOutput(pOut, sOut.aBuf, sOut.nBuf);
+ }
+
+ finished_invert:
+ sqlite3_free(sOut.aBuf);
+ sqlite3_free(apVal);
+ sqlite3_free(sPK.aBuf);
+ return rc;
+}
+
+
+/*
+** Invert a changeset object.
+*/
+SQLITE_API int sqlite3changeset_invert(
+ int nChangeset, /* Number of bytes in input */
+ const void *pChangeset, /* Input changeset */
+ int *pnInverted, /* OUT: Number of bytes in output changeset */
+ void **ppInverted /* OUT: Inverse of pChangeset */
+){
+ SessionInput sInput;
+
+ /* Set up the input stream */
+ memset(&sInput, 0, sizeof(SessionInput));
+ sInput.nData = nChangeset;
+ sInput.aData = (u8*)pChangeset;
+
+ return sessionChangesetInvert(&sInput, 0, 0, pnInverted, ppInverted);
+}
+
+/*
+** Streaming version of sqlite3changeset_invert().
+*/
+SQLITE_API int sqlite3changeset_invert_strm(
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ SessionInput sInput;
+ int rc;
+
+ /* Set up the input stream */
+ memset(&sInput, 0, sizeof(SessionInput));
+ sInput.xInput = xInput;
+ sInput.pIn = pIn;
+
+ rc = sessionChangesetInvert(&sInput, xOutput, pOut, 0, 0);
+ sqlite3_free(sInput.buf.aBuf);
+ return rc;
+}
+
+typedef struct SessionApplyCtx SessionApplyCtx;
+struct SessionApplyCtx {
+ sqlite3 *db;
+ sqlite3_stmt *pDelete; /* DELETE statement */
+ sqlite3_stmt *pUpdate; /* UPDATE statement */
+ sqlite3_stmt *pInsert; /* INSERT statement */
+ sqlite3_stmt *pSelect; /* SELECT statement */
+ int nCol; /* Size of azCol[] and abPK[] arrays */
+ const char **azCol; /* Array of column names */
+ u8 *abPK; /* Boolean array - true if column is in PK */
+
+ int bDeferConstraints; /* True to defer constraints */
+ SessionBuffer constraints; /* Deferred constraints are stored here */
+};
+
+/*
+** Formulate a statement to DELETE a row from database db. Assuming a table
+** structure like this:
+**
+** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
+**
+** The DELETE statement looks like this:
+**
+** DELETE FROM x WHERE a = :1 AND c = :3 AND (:5 OR b IS :2 AND d IS :4)
+**
+** Variable :5 (nCol+1) is a boolean. It should be set to 0 if we require
+** matching b and d values, or 1 otherwise. The second case comes up if the
+** conflict handler is invoked with NOTFOUND and returns CHANGESET_REPLACE.
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pDelete is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionDeleteRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ int i;
+ const char *zSep = "";
+ int rc = SQLITE_OK;
+ SessionBuffer buf = {0, 0, 0};
+ int nPk = 0;
+
+ sessionAppendStr(&buf, "DELETE FROM ", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " WHERE ", &rc);
+
+ for(i=0; i<p->nCol; i++){
+ if( p->abPK[i] ){
+ nPk++;
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, i+1, &rc);
+ zSep = " AND ";
+ }
+ }
+
+ if( nPk<p->nCol ){
+ sessionAppendStr(&buf, " AND (?", &rc);
+ sessionAppendInteger(&buf, p->nCol+1, &rc);
+ sessionAppendStr(&buf, " OR ", &rc);
+
+ zSep = "";
+ for(i=0; i<p->nCol; i++){
+ if( !p->abPK[i] ){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " IS ?", &rc);
+ sessionAppendInteger(&buf, i+1, &rc);
+ zSep = "AND ";
+ }
+ }
+ sessionAppendStr(&buf, ")", &rc);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pDelete, 0);
+ }
+ sqlite3_free(buf.aBuf);
+
+ return rc;
+}
+
+/*
+** Formulate and prepare a statement to UPDATE a row from database db.
+** Assuming a table structure like this:
+**
+** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
+**
+** The UPDATE statement looks like this:
+**
+** UPDATE x SET
+** a = CASE WHEN ?2 THEN ?3 ELSE a END,
+** b = CASE WHEN ?5 THEN ?6 ELSE b END,
+** c = CASE WHEN ?8 THEN ?9 ELSE c END,
+** d = CASE WHEN ?11 THEN ?12 ELSE d END
+** WHERE a = ?1 AND c = ?7 AND (?13 OR
+** (?5==0 OR b IS ?4) AND (?11==0 OR d IS ?10) AND
+** )
+**
+** For each column in the table, there are three variables to bind:
+**
+** ?(i*3+1) The old.* value of the column, if any.
+** ?(i*3+2) A boolean flag indicating that the value is being modified.
+** ?(i*3+3) The new.* value of the column, if any.
+**
+** Also, a boolean flag that, if set to true, causes the statement to update
+** a row even if the non-PK values do not match. This is required if the
+** conflict-handler is invoked with CHANGESET_DATA and returns
+** CHANGESET_REPLACE. This is variable "?(nCol*3+1)".
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pUpdate is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionUpdateRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ int rc = SQLITE_OK;
+ int i;
+ const char *zSep = "";
+ SessionBuffer buf = {0, 0, 0};
+
+ /* Append "UPDATE tbl SET " */
+ sessionAppendStr(&buf, "UPDATE ", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " SET ", &rc);
+
+ /* Append the assignments */
+ for(i=0; i<p->nCol; i++){
+ sessionAppendStr(&buf, zSep, &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " = CASE WHEN ?", &rc);
+ sessionAppendInteger(&buf, i*3+2, &rc);
+ sessionAppendStr(&buf, " THEN ?", &rc);
+ sessionAppendInteger(&buf, i*3+3, &rc);
+ sessionAppendStr(&buf, " ELSE ", &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " END", &rc);
+ zSep = ", ";
+ }
+
+ /* Append the PK part of the WHERE clause */
+ sessionAppendStr(&buf, " WHERE ", &rc);
+ for(i=0; i<p->nCol; i++){
+ if( p->abPK[i] ){
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " = ?", &rc);
+ sessionAppendInteger(&buf, i*3+1, &rc);
+ sessionAppendStr(&buf, " AND ", &rc);
+ }
+ }
+
+ /* Append the non-PK part of the WHERE clause */
+ sessionAppendStr(&buf, " (?", &rc);
+ sessionAppendInteger(&buf, p->nCol*3+1, &rc);
+ sessionAppendStr(&buf, " OR 1", &rc);
+ for(i=0; i<p->nCol; i++){
+ if( !p->abPK[i] ){
+ sessionAppendStr(&buf, " AND (?", &rc);
+ sessionAppendInteger(&buf, i*3+2, &rc);
+ sessionAppendStr(&buf, "=0 OR ", &rc);
+ sessionAppendIdent(&buf, p->azCol[i], &rc);
+ sessionAppendStr(&buf, " IS ?", &rc);
+ sessionAppendInteger(&buf, i*3+1, &rc);
+ sessionAppendStr(&buf, ")", &rc);
+ }
+ }
+ sessionAppendStr(&buf, ")", &rc);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pUpdate, 0);
+ }
+ sqlite3_free(buf.aBuf);
+
+ return rc;
+}
+
+/*
+** Formulate and prepare an SQL statement to query table zTab by primary
+** key. Assuming the following table structure:
+**
+** CREATE TABLE x(a, b, c, d, PRIMARY KEY(a, c));
+**
+** The SELECT statement looks like this:
+**
+** SELECT * FROM x WHERE a = ?1 AND c = ?3
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pSelect is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionSelectRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ return sessionSelectStmt(
+ db, "main", zTab, p->nCol, p->azCol, p->abPK, &p->pSelect);
+}
+
+/*
+** Formulate and prepare an INSERT statement to add a record to table zTab.
+** For example:
+**
+** INSERT INTO main."zTab" VALUES(?1, ?2, ?3 ...);
+**
+** If successful, SQLITE_OK is returned and SessionApplyCtx.pInsert is left
+** pointing to the prepared version of the SQL statement.
+*/
+static int sessionInsertRow(
+ sqlite3 *db, /* Database handle */
+ const char *zTab, /* Table name */
+ SessionApplyCtx *p /* Session changeset-apply context */
+){
+ int rc = SQLITE_OK;
+ int i;
+ SessionBuffer buf = {0, 0, 0};
+
+ sessionAppendStr(&buf, "INSERT INTO main.", &rc);
+ sessionAppendIdent(&buf, zTab, &rc);
+ sessionAppendStr(&buf, " VALUES(?", &rc);
+ for(i=1; i<p->nCol; i++){
+ sessionAppendStr(&buf, ", ?", &rc);
+ }
+ sessionAppendStr(&buf, ")", &rc);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_prepare_v2(db, (char *)buf.aBuf, buf.nBuf, &p->pInsert, 0);
+ }
+ sqlite3_free(buf.aBuf);
+ return rc;
+}
+
+/*
+** A wrapper around sqlite3_bind_value() that detects an extra problem.
+** See comments in the body of this function for details.
+*/
+static int sessionBindValue(
+ sqlite3_stmt *pStmt, /* Statement to bind value to */
+ int i, /* Parameter number to bind to */
+ sqlite3_value *pVal /* Value to bind */
+){
+ int eType = sqlite3_value_type(pVal);
+ /* COVERAGE: The (pVal->z==0) branch is never true using current versions
+ ** of SQLite. If a malloc fails in an sqlite3_value_xxx() function, either
+ ** the (pVal->z) variable remains as it was or the type of the value is
+ ** set to SQLITE_NULL. */
+ if( (eType==SQLITE_TEXT || eType==SQLITE_BLOB) && pVal->z==0 ){
+ /* This condition occurs when an earlier OOM in a call to
+ ** sqlite3_value_text() or sqlite3_value_blob() (perhaps from within
+ ** a conflict-handler) has zeroed the pVal->z pointer. Return NOMEM. */
+ return SQLITE_NOMEM;
+ }
+ return sqlite3_bind_value(pStmt, i, pVal);
+}
+
+/*
+** Iterator pIter must point to an SQLITE_INSERT entry. This function
+** transfers new.* values from the current iterator entry to statement
+** pStmt. The table being inserted into has nCol columns.
+**
+** New.* value $i from the iterator is bound to variable ($i+1) of
+** statement pStmt. If parameter abPK is NULL, all values from 0 to (nCol-1)
+** are transfered to the statement. Otherwise, if abPK is not NULL, it points
+** to an array nCol elements in size. In this case only those values for
+** which abPK[$i] is true are read from the iterator and bound to the
+** statement.
+**
+** An SQLite error code is returned if an error occurs. Otherwise, SQLITE_OK.
+*/
+static int sessionBindRow(
+ sqlite3_changeset_iter *pIter, /* Iterator to read values from */
+ int(*xValue)(sqlite3_changeset_iter *, int, sqlite3_value **),
+ int nCol, /* Number of columns */
+ u8 *abPK, /* If not NULL, bind only if true */
+ sqlite3_stmt *pStmt /* Bind values to this statement */
+){
+ int i;
+ int rc = SQLITE_OK;
+
+ /* Neither sqlite3changeset_old or sqlite3changeset_new can fail if the
+ ** argument iterator points to a suitable entry. Make sure that xValue
+ ** is one of these to guarantee that it is safe to ignore the return
+ ** in the code below. */
+ assert( xValue==sqlite3changeset_old || xValue==sqlite3changeset_new );
+
+ for(i=0; rc==SQLITE_OK && i<nCol; i++){
+ if( !abPK || abPK[i] ){
+ sqlite3_value *pVal;
+ (void)xValue(pIter, i, &pVal);
+ rc = sessionBindValue(pStmt, i+1, pVal);
+ }
+ }
+ return rc;
+}
+
+/*
+** SQL statement pSelect is as generated by the sessionSelectRow() function.
+** This function binds the primary key values from the change that changeset
+** iterator pIter points to to the SELECT and attempts to seek to the table
+** entry. If a row is found, the SELECT statement left pointing at the row
+** and SQLITE_ROW is returned. Otherwise, if no row is found and no error
+** has occured, the statement is reset and SQLITE_OK is returned. If an
+** error occurs, the statement is reset and an SQLite error code is returned.
+**
+** If this function returns SQLITE_ROW, the caller must eventually reset()
+** statement pSelect. If any other value is returned, the statement does
+** not require a reset().
+**
+** If the iterator currently points to an INSERT record, bind values from the
+** new.* record to the SELECT statement. Or, if it points to a DELETE or
+** UPDATE, bind values from the old.* record.
+*/
+static int sessionSeekToRow(
+ sqlite3 *db, /* Database handle */
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ u8 *abPK, /* Primary key flags array */
+ sqlite3_stmt *pSelect /* SELECT statement from sessionSelectRow() */
+){
+ int rc; /* Return code */
+ int nCol; /* Number of columns in table */
+ int op; /* Changset operation (SQLITE_UPDATE etc.) */
+ const char *zDummy; /* Unused */
+
+ sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
+ rc = sessionBindRow(pIter,
+ op==SQLITE_INSERT ? sqlite3changeset_new : sqlite3changeset_old,
+ nCol, abPK, pSelect
+ );
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_step(pSelect);
+ if( rc!=SQLITE_ROW ) rc = sqlite3_reset(pSelect);
+ }
+
+ return rc;
+}
+
+/*
+** Invoke the conflict handler for the change that the changeset iterator
+** currently points to.
+**
+** Argument eType must be either CHANGESET_DATA or CHANGESET_CONFLICT.
+** If argument pbReplace is NULL, then the type of conflict handler invoked
+** depends solely on eType, as follows:
+**
+** eType value Value passed to xConflict
+** -------------------------------------------------
+** CHANGESET_DATA CHANGESET_NOTFOUND
+** CHANGESET_CONFLICT CHANGESET_CONSTRAINT
+**
+** Or, if pbReplace is not NULL, then an attempt is made to find an existing
+** record with the same primary key as the record about to be deleted, updated
+** or inserted. If such a record can be found, it is available to the conflict
+** handler as the "conflicting" record. In this case the type of conflict
+** handler invoked is as follows:
+**
+** eType value PK Record found? Value passed to xConflict
+** ----------------------------------------------------------------
+** CHANGESET_DATA Yes CHANGESET_DATA
+** CHANGESET_DATA No CHANGESET_NOTFOUND
+** CHANGESET_CONFLICT Yes CHANGESET_CONFLICT
+** CHANGESET_CONFLICT No CHANGESET_CONSTRAINT
+**
+** If pbReplace is not NULL, and a record with a matching PK is found, and
+** the conflict handler function returns SQLITE_CHANGESET_REPLACE, *pbReplace
+** is set to non-zero before returning SQLITE_OK.
+**
+** If the conflict handler returns SQLITE_CHANGESET_ABORT, SQLITE_ABORT is
+** returned. Or, if the conflict handler returns an invalid value,
+** SQLITE_MISUSE. If the conflict handler returns SQLITE_CHANGESET_OMIT,
+** this function returns SQLITE_OK.
+*/
+static int sessionConflictHandler(
+ int eType, /* Either CHANGESET_DATA or CONFLICT */
+ SessionApplyCtx *p, /* changeset_apply() context */
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int(*xConflict)(void *, int, sqlite3_changeset_iter*),
+ void *pCtx, /* First argument for conflict handler */
+ int *pbReplace /* OUT: Set to true if PK row is found */
+){
+ int res = 0; /* Value returned by conflict handler */
+ int rc;
+ int nCol;
+ int op;
+ const char *zDummy;
+
+ sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
+
+ assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
+ assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
+ assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
+
+ /* Bind the new.* PRIMARY KEY values to the SELECT statement. */
+ if( pbReplace ){
+ rc = sessionSeekToRow(p->db, pIter, p->abPK, p->pSelect);
+ }else{
+ rc = SQLITE_OK;
+ }
+
+ if( rc==SQLITE_ROW ){
+ /* There exists another row with the new.* primary key. */
+ pIter->pConflict = p->pSelect;
+ res = xConflict(pCtx, eType, pIter);
+ pIter->pConflict = 0;
+ rc = sqlite3_reset(p->pSelect);
+ }else if( rc==SQLITE_OK ){
+ if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){
+ /* Instead of invoking the conflict handler, append the change blob
+ ** to the SessionApplyCtx.constraints buffer. */
+ u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
+ int nBlob = pIter->in.iNext - pIter->in.iCurrent;
+ sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
+ res = SQLITE_CHANGESET_OMIT;
+ }else{
+ /* No other row with the new.* primary key. */
+ res = xConflict(pCtx, eType+1, pIter);
+ if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ switch( res ){
+ case SQLITE_CHANGESET_REPLACE:
+ assert( pbReplace );
+ *pbReplace = 1;
+ break;
+
+ case SQLITE_CHANGESET_OMIT:
+ break;
+
+ case SQLITE_CHANGESET_ABORT:
+ rc = SQLITE_ABORT;
+ break;
+
+ default:
+ rc = SQLITE_MISUSE;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Attempt to apply the change that the iterator passed as the first argument
+** currently points to to the database. If a conflict is encountered, invoke
+** the conflict handler callback.
+**
+** If argument pbRetry is NULL, then ignore any CHANGESET_DATA conflict. If
+** one is encountered, update or delete the row with the matching primary key
+** instead. Or, if pbRetry is not NULL and a CHANGESET_DATA conflict occurs,
+** invoke the conflict handler. If it returns CHANGESET_REPLACE, set *pbRetry
+** to true before returning. In this case the caller will invoke this function
+** again, this time with pbRetry set to NULL.
+**
+** If argument pbReplace is NULL and a CHANGESET_CONFLICT conflict is
+** encountered invoke the conflict handler with CHANGESET_CONSTRAINT instead.
+** Or, if pbReplace is not NULL, invoke it with CHANGESET_CONFLICT. If such
+** an invocation returns SQLITE_CHANGESET_REPLACE, set *pbReplace to true
+** before retrying. In this case the caller attempts to remove the conflicting
+** row before invoking this function again, this time with pbReplace set
+** to NULL.
+**
+** If any conflict handler returns SQLITE_CHANGESET_ABORT, this function
+** returns SQLITE_ABORT. Otherwise, if no error occurs, SQLITE_OK is
+** returned.
+*/
+static int sessionApplyOneOp(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ SessionApplyCtx *p, /* changeset_apply() context */
+ int(*xConflict)(void *, int, sqlite3_changeset_iter *),
+ void *pCtx, /* First argument for the conflict handler */
+ int *pbReplace, /* OUT: True to remove PK row and retry */
+ int *pbRetry /* OUT: True to retry. */
+){
+ const char *zDummy;
+ int op;
+ int nCol;
+ int rc = SQLITE_OK;
+
+ assert( p->pDelete && p->pUpdate && p->pInsert && p->pSelect );
+ assert( p->azCol && p->abPK );
+ assert( !pbReplace || *pbReplace==0 );
+
+ sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
+
+ if( op==SQLITE_DELETE ){
+
+ /* Bind values to the DELETE statement. If conflict handling is required,
+ ** bind values for all columns and set bound variable (nCol+1) to true.
+ ** Or, if conflict handling is not required, bind just the PK column
+ ** values and, if it exists, set (nCol+1) to false. Conflict handling
+ ** is not required if:
+ **
+ ** * this is a patchset, or
+ ** * (pbRetry==0), or
+ ** * all columns of the table are PK columns (in this case there is
+ ** no (nCol+1) variable to bind to).
+ */
+ u8 *abPK = (pIter->bPatchset ? p->abPK : 0);
+ rc = sessionBindRow(pIter, sqlite3changeset_old, nCol, abPK, p->pDelete);
+ if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
+ rc = sqlite3_bind_int(p->pDelete, nCol+1, (pbRetry==0 || abPK));
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ sqlite3_step(p->pDelete);
+ rc = sqlite3_reset(p->pDelete);
+ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
+ );
+ }else if( (rc&0xff)==SQLITE_CONSTRAINT ){
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
+ );
+ }
+
+ }else if( op==SQLITE_UPDATE ){
+ int i;
+
+ /* Bind values to the UPDATE statement. */
+ for(i=0; rc==SQLITE_OK && i<nCol; i++){
+ sqlite3_value *pOld = sessionChangesetOld(pIter, i);
+ sqlite3_value *pNew = sessionChangesetNew(pIter, i);
+
+ sqlite3_bind_int(p->pUpdate, i*3+2, !!pNew);
+ if( pOld ){
+ rc = sessionBindValue(p->pUpdate, i*3+1, pOld);
+ }
+ if( rc==SQLITE_OK && pNew ){
+ rc = sessionBindValue(p->pUpdate, i*3+3, pNew);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int(p->pUpdate, nCol*3+1, pbRetry==0 || pIter->bPatchset);
+ }
+ if( rc!=SQLITE_OK ) return rc;
+
+ /* Attempt the UPDATE. In the case of a NOTFOUND or DATA conflict,
+ ** the result will be SQLITE_OK with 0 rows modified. */
+ sqlite3_step(p->pUpdate);
+ rc = sqlite3_reset(p->pUpdate);
+
+ if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
+ /* A NOTFOUND or DATA error. Search the table to see if it contains
+ ** a row with a matching primary key. If so, this is a DATA conflict.
+ ** Otherwise, if there is no primary key match, it is a NOTFOUND. */
+
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
+ );
+
+ }else if( (rc&0xff)==SQLITE_CONSTRAINT ){
+ /* This is always a CONSTRAINT conflict. */
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
+ );
+ }
+
+ }else{
+ assert( op==SQLITE_INSERT );
+ rc = sessionBindRow(pIter, sqlite3changeset_new, nCol, 0, p->pInsert);
+ if( rc!=SQLITE_OK ) return rc;
+
+ sqlite3_step(p->pInsert);
+ rc = sqlite3_reset(p->pInsert);
+ if( (rc&0xff)==SQLITE_CONSTRAINT ){
+ rc = sessionConflictHandler(
+ SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, pbReplace
+ );
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Attempt to apply the change that the iterator passed as the first argument
+** currently points to to the database. If a conflict is encountered, invoke
+** the conflict handler callback.
+**
+** The difference between this function and sessionApplyOne() is that this
+** function handles the case where the conflict-handler is invoked and
+** returns SQLITE_CHANGESET_REPLACE - indicating that the change should be
+** retried in some manner.
+*/
+static int sessionApplyOneWithRetry(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ sqlite3_changeset_iter *pIter, /* Changeset iterator to read change from */
+ SessionApplyCtx *pApply, /* Apply context */
+ int(*xConflict)(void*, int, sqlite3_changeset_iter*),
+ void *pCtx /* First argument passed to xConflict */
+){
+ int bReplace = 0;
+ int bRetry = 0;
+ int rc;
+
+ rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, &bReplace, &bRetry);
+ assert( rc==SQLITE_OK || (bRetry==0 && bReplace==0) );
+
+ /* If the bRetry flag is set, the change has not been applied due to an
+ ** SQLITE_CHANGESET_DATA problem (i.e. this is an UPDATE or DELETE and
+ ** a row with the correct PK is present in the db, but one or more other
+ ** fields do not contain the expected values) and the conflict handler
+ ** returned SQLITE_CHANGESET_REPLACE. In this case retry the operation,
+ ** but pass NULL as the final argument so that sessionApplyOneOp() ignores
+ ** the SQLITE_CHANGESET_DATA problem. */
+ if( bRetry ){
+ assert( pIter->op==SQLITE_UPDATE || pIter->op==SQLITE_DELETE );
+ rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
+ }
+
+ /* If the bReplace flag is set, the change is an INSERT that has not
+ ** been performed because the database already contains a row with the
+ ** specified primary key and the conflict handler returned
+ ** SQLITE_CHANGESET_REPLACE. In this case remove the conflicting row
+ ** before reattempting the INSERT. */
+ else if( bReplace ){
+ assert( pIter->op==SQLITE_INSERT );
+ rc = sqlite3_exec(db, "SAVEPOINT replace_op", 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sessionBindRow(pIter,
+ sqlite3changeset_new, pApply->nCol, pApply->abPK, pApply->pDelete);
+ sqlite3_bind_int(pApply->pDelete, pApply->nCol+1, 1);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pApply->pDelete);
+ rc = sqlite3_reset(pApply->pDelete);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sessionApplyOneOp(pIter, pApply, xConflict, pCtx, 0, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(db, "RELEASE replace_op", 0, 0, 0);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Retry the changes accumulated in the pApply->constraints buffer.
+*/
+static int sessionRetryConstraints(
+ sqlite3 *db,
+ int bPatchset,
+ const char *zTab,
+ SessionApplyCtx *pApply,
+ int(*xConflict)(void*, int, sqlite3_changeset_iter*),
+ void *pCtx /* First argument passed to xConflict */
+){
+ int rc = SQLITE_OK;
+
+ while( pApply->constraints.nBuf ){
+ sqlite3_changeset_iter *pIter2 = 0;
+ SessionBuffer cons = pApply->constraints;
+ memset(&pApply->constraints, 0, sizeof(SessionBuffer));
+
+ rc = sessionChangesetStart(&pIter2, 0, 0, cons.nBuf, cons.aBuf);
+ if( rc==SQLITE_OK ){
+ int nByte = 2*pApply->nCol*sizeof(sqlite3_value*);
+ int rc2;
+ pIter2->bPatchset = bPatchset;
+ pIter2->zTab = (char*)zTab;
+ pIter2->nCol = pApply->nCol;
+ pIter2->abPK = pApply->abPK;
+ sessionBufferGrow(&pIter2->tblhdr, nByte, &rc);
+ pIter2->apValue = (sqlite3_value**)pIter2->tblhdr.aBuf;
+ if( rc==SQLITE_OK ) memset(pIter2->apValue, 0, nByte);
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter2) ){
+ rc = sessionApplyOneWithRetry(db, pIter2, pApply, xConflict, pCtx);
+ }
+
+ rc2 = sqlite3changeset_finalize(pIter2);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+ assert( pApply->bDeferConstraints || pApply->constraints.nBuf==0 );
+
+ sqlite3_free(cons.aBuf);
+ if( rc!=SQLITE_OK ) break;
+ if( pApply->constraints.nBuf>=cons.nBuf ){
+ /* No progress was made on the last round. */
+ pApply->bDeferConstraints = 0;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Argument pIter is a changeset iterator that has been initialized, but
+** not yet passed to sqlite3changeset_next(). This function applies the
+** changeset to the main database attached to handle "db". The supplied
+** conflict handler callback is invoked to resolve any conflicts encountered
+** while applying the change.
+*/
+static int sessionChangesetApply(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ sqlite3_changeset_iter *pIter, /* Changeset to apply */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of fifth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+){
+ int schemaMismatch = 0;
+ int rc; /* Return code */
+ const char *zTab = 0; /* Name of current table */
+ int nTab = 0; /* Result of sqlite3Strlen30(zTab) */
+ SessionApplyCtx sApply; /* changeset_apply() context object */
+ int bPatchset;
+
+ assert( xConflict!=0 );
+
+ pIter->in.bNoDiscard = 1;
+ memset(&sApply, 0, sizeof(sApply));
+ sqlite3_mutex_enter(sqlite3_db_mutex(db));
+ rc = sqlite3_exec(db, "SAVEPOINT changeset_apply", 0, 0, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 1", 0, 0, 0);
+ }
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3changeset_next(pIter) ){
+ int nCol;
+ int op;
+ const char *zNew;
+
+ sqlite3changeset_op(pIter, &zNew, &nCol, &op, 0);
+
+ if( zTab==0 || sqlite3_strnicmp(zNew, zTab, nTab+1) ){
+ u8 *abPK;
+
+ rc = sessionRetryConstraints(
+ db, pIter->bPatchset, zTab, &sApply, xConflict, pCtx
+ );
+ if( rc!=SQLITE_OK ) break;
+
+ sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
+ sqlite3_finalize(sApply.pDelete);
+ sqlite3_finalize(sApply.pUpdate);
+ sqlite3_finalize(sApply.pInsert);
+ sqlite3_finalize(sApply.pSelect);
+ memset(&sApply, 0, sizeof(sApply));
+ sApply.db = db;
+ sApply.bDeferConstraints = 1;
+
+ /* If an xFilter() callback was specified, invoke it now. If the
+ ** xFilter callback returns zero, skip this table. If it returns
+ ** non-zero, proceed. */
+ schemaMismatch = (xFilter && (0==xFilter(pCtx, zNew)));
+ if( schemaMismatch ){
+ zTab = sqlite3_mprintf("%s", zNew);
+ if( zTab==0 ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ nTab = (int)strlen(zTab);
+ sApply.azCol = (const char **)zTab;
+ }else{
+ sqlite3changeset_pk(pIter, &abPK, 0);
+ rc = sessionTableInfo(
+ db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
+ );
+ if( rc!=SQLITE_OK ) break;
+
+ if( sApply.nCol==0 ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA,
+ "sqlite3changeset_apply(): no such table: %s", zTab
+ );
+ }
+ else if( sApply.nCol!=nCol ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA,
+ "sqlite3changeset_apply(): table %s has %d columns, expected %d",
+ zTab, sApply.nCol, nCol
+ );
+ }
+ else if( memcmp(sApply.abPK, abPK, nCol)!=0 ){
+ schemaMismatch = 1;
+ sqlite3_log(SQLITE_SCHEMA, "sqlite3changeset_apply(): "
+ "primary key mismatch for table %s", zTab
+ );
+ }
+ else if(
+ (rc = sessionSelectRow(db, zTab, &sApply))
+ || (rc = sessionUpdateRow(db, zTab, &sApply))
+ || (rc = sessionDeleteRow(db, zTab, &sApply))
+ || (rc = sessionInsertRow(db, zTab, &sApply))
+ ){
+ break;
+ }
+ nTab = sqlite3Strlen30(zTab);
+ }
+ }
+
+ /* If there is a schema mismatch on the current table, proceed to the
+ ** next change. A log message has already been issued. */
+ if( schemaMismatch ) continue;
+
+ rc = sessionApplyOneWithRetry(db, pIter, &sApply, xConflict, pCtx);
+ }
+
+ bPatchset = pIter->bPatchset;
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changeset_finalize(pIter);
+ }else{
+ sqlite3changeset_finalize(pIter);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sessionRetryConstraints(db, bPatchset, zTab, &sApply, xConflict, pCtx);
+ }
+
+ if( rc==SQLITE_OK ){
+ int nFk, notUsed;
+ sqlite3_db_status(db, SQLITE_DBSTATUS_DEFERRED_FKS, &nFk, &notUsed, 0);
+ if( nFk!=0 ){
+ int res = SQLITE_CHANGESET_ABORT;
+ sqlite3_changeset_iter sIter;
+ memset(&sIter, 0, sizeof(sIter));
+ sIter.nCol = nFk;
+ res = xConflict(pCtx, SQLITE_CHANGESET_FOREIGN_KEY, &sIter);
+ if( res!=SQLITE_CHANGESET_OMIT ){
+ rc = SQLITE_CONSTRAINT;
+ }
+ }
+ }
+ sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
+ }else{
+ sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
+ sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
+ }
+
+ sqlite3_finalize(sApply.pInsert);
+ sqlite3_finalize(sApply.pDelete);
+ sqlite3_finalize(sApply.pUpdate);
+ sqlite3_finalize(sApply.pSelect);
+ sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
+ sqlite3_free((char*)sApply.constraints.aBuf);
+ sqlite3_mutex_leave(sqlite3_db_mutex(db));
+ return rc;
+}
+
+/*
+** Apply the changeset passed via pChangeset/nChangeset to the main database
+** attached to handle "db". Invoke the supplied conflict handler callback
+** to resolve any conflicts encountered while applying the change.
+*/
+SQLITE_API int sqlite3changeset_apply(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of fifth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+){
+ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
+ int rc = sqlite3changeset_start(&pIter, nChangeset, pChangeset);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
+ }
+ return rc;
+}
+
+/*
+** Apply the changeset passed via xInput/pIn to the main database
+** attached to handle "db". Invoke the supplied conflict handler callback
+** to resolve any conflicts encountered while applying the change.
+*/
+SQLITE_API int sqlite3changeset_apply_strm(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+){
+ sqlite3_changeset_iter *pIter; /* Iterator to skip through changeset */
+ int rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetApply(db, pIter, xFilter, xConflict, pCtx);
+ }
+ return rc;
+}
+
+/*
+** sqlite3_changegroup handle.
+*/
+struct sqlite3_changegroup {
+ int rc; /* Error code */
+ int bPatch; /* True to accumulate patchsets */
+ SessionTable *pList; /* List of tables in current patch */
+};
+
+/*
+** This function is called to merge two changes to the same row together as
+** part of an sqlite3changeset_concat() operation. A new change object is
+** allocated and a pointer to it stored in *ppNew.
+*/
+static int sessionChangeMerge(
+ SessionTable *pTab, /* Table structure */
+ int bPatchset, /* True for patchsets */
+ SessionChange *pExist, /* Existing change */
+ int op2, /* Second change operation */
+ int bIndirect, /* True if second change is indirect */
+ u8 *aRec, /* Second change record */
+ int nRec, /* Number of bytes in aRec */
+ SessionChange **ppNew /* OUT: Merged change */
+){
+ SessionChange *pNew = 0;
+
+ if( !pExist ){
+ pNew = (SessionChange *)sqlite3_malloc(sizeof(SessionChange) + nRec);
+ if( !pNew ){
+ return SQLITE_NOMEM;
+ }
+ memset(pNew, 0, sizeof(SessionChange));
+ pNew->op = op2;
+ pNew->bIndirect = bIndirect;
+ pNew->nRecord = nRec;
+ pNew->aRecord = (u8*)&pNew[1];
+ memcpy(pNew->aRecord, aRec, nRec);
+ }else{
+ int op1 = pExist->op;
+
+ /*
+ ** op1=INSERT, op2=INSERT -> Unsupported. Discard op2.
+ ** op1=INSERT, op2=UPDATE -> INSERT.
+ ** op1=INSERT, op2=DELETE -> (none)
+ **
+ ** op1=UPDATE, op2=INSERT -> Unsupported. Discard op2.
+ ** op1=UPDATE, op2=UPDATE -> UPDATE.
+ ** op1=UPDATE, op2=DELETE -> DELETE.
+ **
+ ** op1=DELETE, op2=INSERT -> UPDATE.
+ ** op1=DELETE, op2=UPDATE -> Unsupported. Discard op2.
+ ** op1=DELETE, op2=DELETE -> Unsupported. Discard op2.
+ */
+ if( (op1==SQLITE_INSERT && op2==SQLITE_INSERT)
+ || (op1==SQLITE_UPDATE && op2==SQLITE_INSERT)
+ || (op1==SQLITE_DELETE && op2==SQLITE_UPDATE)
+ || (op1==SQLITE_DELETE && op2==SQLITE_DELETE)
+ ){
+ pNew = pExist;
+ }else if( op1==SQLITE_INSERT && op2==SQLITE_DELETE ){
+ sqlite3_free(pExist);
+ assert( pNew==0 );
+ }else{
+ u8 *aExist = pExist->aRecord;
+ int nByte;
+ u8 *aCsr;
+
+ /* Allocate a new SessionChange object. Ensure that the aRecord[]
+ ** buffer of the new object is large enough to hold any record that
+ ** may be generated by combining the input records. */
+ nByte = sizeof(SessionChange) + pExist->nRecord + nRec;
+ pNew = (SessionChange *)sqlite3_malloc(nByte);
+ if( !pNew ){
+ sqlite3_free(pExist);
+ return SQLITE_NOMEM;
+ }
+ memset(pNew, 0, sizeof(SessionChange));
+ pNew->bIndirect = (bIndirect && pExist->bIndirect);
+ aCsr = pNew->aRecord = (u8 *)&pNew[1];
+
+ if( op1==SQLITE_INSERT ){ /* INSERT + UPDATE */
+ u8 *a1 = aRec;
+ assert( op2==SQLITE_UPDATE );
+ pNew->op = SQLITE_INSERT;
+ if( bPatchset==0 ) sessionSkipRecord(&a1, pTab->nCol);
+ sessionMergeRecord(&aCsr, pTab->nCol, aExist, a1);
+ }else if( op1==SQLITE_DELETE ){ /* DELETE + INSERT */
+ assert( op2==SQLITE_INSERT );
+ pNew->op = SQLITE_UPDATE;
+ if( bPatchset ){
+ memcpy(aCsr, aRec, nRec);
+ aCsr += nRec;
+ }else{
+ if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aExist, 0,aRec,0) ){
+ sqlite3_free(pNew);
+ pNew = 0;
+ }
+ }
+ }else if( op2==SQLITE_UPDATE ){ /* UPDATE + UPDATE */
+ u8 *a1 = aExist;
+ u8 *a2 = aRec;
+ assert( op1==SQLITE_UPDATE );
+ if( bPatchset==0 ){
+ sessionSkipRecord(&a1, pTab->nCol);
+ sessionSkipRecord(&a2, pTab->nCol);
+ }
+ pNew->op = SQLITE_UPDATE;
+ if( 0==sessionMergeUpdate(&aCsr, pTab, bPatchset, aRec, aExist,a1,a2) ){
+ sqlite3_free(pNew);
+ pNew = 0;
+ }
+ }else{ /* UPDATE + DELETE */
+ assert( op1==SQLITE_UPDATE && op2==SQLITE_DELETE );
+ pNew->op = SQLITE_DELETE;
+ if( bPatchset ){
+ memcpy(aCsr, aRec, nRec);
+ aCsr += nRec;
+ }else{
+ sessionMergeRecord(&aCsr, pTab->nCol, aRec, aExist);
+ }
+ }
+
+ if( pNew ){
+ pNew->nRecord = (int)(aCsr - pNew->aRecord);
+ }
+ sqlite3_free(pExist);
+ }
+ }
+
+ *ppNew = pNew;
+ return SQLITE_OK;
+}
+
+/*
+** Add all changes in the changeset traversed by the iterator passed as
+** the first argument to the changegroup hash tables.
+*/
+static int sessionChangesetToHash(
+ sqlite3_changeset_iter *pIter, /* Iterator to read from */
+ sqlite3_changegroup *pGrp /* Changegroup object to add changeset to */
+){
+ u8 *aRec;
+ int nRec;
+ int rc = SQLITE_OK;
+ SessionTable *pTab = 0;
+
+
+ while( SQLITE_ROW==sessionChangesetNext(pIter, &aRec, &nRec) ){
+ const char *zNew;
+ int nCol;
+ int op;
+ int iHash;
+ int bIndirect;
+ SessionChange *pChange;
+ SessionChange *pExist = 0;
+ SessionChange **pp;
+
+ if( pGrp->pList==0 ){
+ pGrp->bPatch = pIter->bPatchset;
+ }else if( pIter->bPatchset!=pGrp->bPatch ){
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ sqlite3changeset_op(pIter, &zNew, &nCol, &op, &bIndirect);
+ if( !pTab || sqlite3_stricmp(zNew, pTab->zName) ){
+ /* Search the list for a matching table */
+ int nNew = (int)strlen(zNew);
+ u8 *abPK;
+
+ sqlite3changeset_pk(pIter, &abPK, 0);
+ for(pTab = pGrp->pList; pTab; pTab=pTab->pNext){
+ if( 0==sqlite3_strnicmp(pTab->zName, zNew, nNew+1) ) break;
+ }
+ if( !pTab ){
+ SessionTable **ppTab;
+
+ pTab = sqlite3_malloc(sizeof(SessionTable) + nCol + nNew+1);
+ if( !pTab ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ memset(pTab, 0, sizeof(SessionTable));
+ pTab->nCol = nCol;
+ pTab->abPK = (u8*)&pTab[1];
+ memcpy(pTab->abPK, abPK, nCol);
+ pTab->zName = (char*)&pTab->abPK[nCol];
+ memcpy(pTab->zName, zNew, nNew+1);
+
+ /* The new object must be linked on to the end of the list, not
+ ** simply added to the start of it. This is to ensure that the
+ ** tables within the output of sqlite3changegroup_output() are in
+ ** the right order. */
+ for(ppTab=&pGrp->pList; *ppTab; ppTab=&(*ppTab)->pNext);
+ *ppTab = pTab;
+ }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
+ rc = SQLITE_SCHEMA;
+ break;
+ }
+ }
+
+ if( sessionGrowHash(pIter->bPatchset, pTab) ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ iHash = sessionChangeHash(
+ pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
+ );
+
+ /* Search for existing entry. If found, remove it from the hash table.
+ ** Code below may link it back in.
+ */
+ for(pp=&pTab->apChange[iHash]; *pp; pp=&(*pp)->pNext){
+ int bPkOnly1 = 0;
+ int bPkOnly2 = 0;
+ if( pIter->bPatchset ){
+ bPkOnly1 = (*pp)->op==SQLITE_DELETE;
+ bPkOnly2 = op==SQLITE_DELETE;
+ }
+ if( sessionChangeEqual(pTab, bPkOnly1, (*pp)->aRecord, bPkOnly2, aRec) ){
+ pExist = *pp;
+ *pp = (*pp)->pNext;
+ pTab->nEntry--;
+ break;
+ }
+ }
+
+ rc = sessionChangeMerge(pTab,
+ pIter->bPatchset, pExist, op, bIndirect, aRec, nRec, &pChange
+ );
+ if( rc ) break;
+ if( pChange ){
+ pChange->pNext = pTab->apChange[iHash];
+ pTab->apChange[iHash] = pChange;
+ pTab->nEntry++;
+ }
+ }
+
+ if( rc==SQLITE_OK ) rc = pIter->rc;
+ return rc;
+}
+
+/*
+** Serialize a changeset (or patchset) based on all changesets (or patchsets)
+** added to the changegroup object passed as the first argument.
+**
+** If xOutput is not NULL, then the changeset/patchset is returned to the
+** user via one or more calls to xOutput, as with the other streaming
+** interfaces.
+**
+** Or, if xOutput is NULL, then (*ppOut) is populated with a pointer to a
+** buffer containing the output changeset before this function returns. In
+** this case (*pnOut) is set to the size of the output buffer in bytes. It
+** is the responsibility of the caller to free the output buffer using
+** sqlite3_free() when it is no longer required.
+**
+** If successful, SQLITE_OK is returned. Or, if an error occurs, an SQLite
+** error code. If an error occurs and xOutput is NULL, (*ppOut) and (*pnOut)
+** are both set to 0 before returning.
+*/
+static int sessionChangegroupOutput(
+ sqlite3_changegroup *pGrp,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut,
+ int *pnOut,
+ void **ppOut
+){
+ int rc = SQLITE_OK;
+ SessionBuffer buf = {0, 0, 0};
+ SessionTable *pTab;
+ assert( xOutput==0 || (ppOut==0 && pnOut==0) );
+
+ /* Create the serialized output changeset based on the contents of the
+ ** hash tables attached to the SessionTable objects in list p->pList.
+ */
+ for(pTab=pGrp->pList; rc==SQLITE_OK && pTab; pTab=pTab->pNext){
+ int i;
+ if( pTab->nEntry==0 ) continue;
+
+ sessionAppendTableHdr(&buf, pGrp->bPatch, pTab, &rc);
+ for(i=0; i<pTab->nChange; i++){
+ SessionChange *p;
+ for(p=pTab->apChange[i]; p; p=p->pNext){
+ sessionAppendByte(&buf, p->op, &rc);
+ sessionAppendByte(&buf, p->bIndirect, &rc);
+ sessionAppendBlob(&buf, p->aRecord, p->nRecord, &rc);
+ }
+ }
+
+ if( rc==SQLITE_OK && xOutput && buf.nBuf>=SESSIONS_STRM_CHUNK_SIZE ){
+ rc = xOutput(pOut, buf.aBuf, buf.nBuf);
+ buf.nBuf = 0;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ if( xOutput ){
+ if( buf.nBuf>0 ) rc = xOutput(pOut, buf.aBuf, buf.nBuf);
+ }else{
+ *ppOut = buf.aBuf;
+ *pnOut = buf.nBuf;
+ buf.aBuf = 0;
+ }
+ }
+ sqlite3_free(buf.aBuf);
+
+ return rc;
+}
+
+/*
+** Allocate a new, empty, sqlite3_changegroup.
+*/
+SQLITE_API int sqlite3changegroup_new(sqlite3_changegroup **pp){
+ int rc = SQLITE_OK; /* Return code */
+ sqlite3_changegroup *p; /* New object */
+ p = (sqlite3_changegroup*)sqlite3_malloc(sizeof(sqlite3_changegroup));
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(p, 0, sizeof(sqlite3_changegroup));
+ }
+ *pp = p;
+ return rc;
+}
+
+/*
+** Add the changeset currently stored in buffer pData, size nData bytes,
+** to changeset-group p.
+*/
+SQLITE_API int sqlite3changegroup_add(sqlite3_changegroup *pGrp, int nData, void *pData){
+ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */
+ int rc; /* Return code */
+
+ rc = sqlite3changeset_start(&pIter, nData, pData);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetToHash(pIter, pGrp);
+ }
+ sqlite3changeset_finalize(pIter);
+ return rc;
+}
+
+/*
+** Obtain a buffer containing a changeset representing the concatenation
+** of all changesets added to the group so far.
+*/
+SQLITE_API int sqlite3changegroup_output(
+ sqlite3_changegroup *pGrp,
+ int *pnData,
+ void **ppData
+){
+ return sessionChangegroupOutput(pGrp, 0, 0, pnData, ppData);
+}
+
+/*
+** Streaming versions of changegroup_add().
+*/
+SQLITE_API int sqlite3changegroup_add_strm(
+ sqlite3_changegroup *pGrp,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+){
+ sqlite3_changeset_iter *pIter; /* Iterator opened on pData/nData */
+ int rc; /* Return code */
+
+ rc = sqlite3changeset_start_strm(&pIter, xInput, pIn);
+ if( rc==SQLITE_OK ){
+ rc = sessionChangesetToHash(pIter, pGrp);
+ }
+ sqlite3changeset_finalize(pIter);
+ return rc;
+}
+
+/*
+** Streaming versions of changegroup_output().
+*/
+SQLITE_API int sqlite3changegroup_output_strm(
+ sqlite3_changegroup *pGrp,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ return sessionChangegroupOutput(pGrp, xOutput, pOut, 0, 0);
+}
+
+/*
+** Delete a changegroup object.
+*/
+SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
+ if( pGrp ){
+ sessionDeleteTable(pGrp->pList);
+ sqlite3_free(pGrp);
+ }
+}
+
+/*
+** Combine two changesets together.
+*/
+SQLITE_API int sqlite3changeset_concat(
+ int nLeft, /* Number of bytes in lhs input */
+ void *pLeft, /* Lhs input changeset */
+ int nRight /* Number of bytes in rhs input */,
+ void *pRight, /* Rhs input changeset */
+ int *pnOut, /* OUT: Number of bytes in output changeset */
+ void **ppOut /* OUT: changeset (left <concat> right) */
+){
+ sqlite3_changegroup *pGrp;
+ int rc;
+
+ rc = sqlite3changegroup_new(&pGrp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add(pGrp, nLeft, pLeft);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add(pGrp, nRight, pRight);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
+ }
+ sqlite3changegroup_delete(pGrp);
+
+ return rc;
+}
+
+/*
+** Streaming version of sqlite3changeset_concat().
+*/
+SQLITE_API int sqlite3changeset_concat_strm(
+ int (*xInputA)(void *pIn, void *pData, int *pnData),
+ void *pInA,
+ int (*xInputB)(void *pIn, void *pData, int *pnData),
+ void *pInB,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+){
+ sqlite3_changegroup *pGrp;
+ int rc;
+
+ rc = sqlite3changegroup_new(&pGrp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add_strm(pGrp, xInputA, pInA);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_add_strm(pGrp, xInputB, pInB);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3changegroup_output_strm(pGrp, xOutput, pOut);
+ }
+ sqlite3changegroup_delete(pGrp);
+
+ return rc;
+}
+
+#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */
+
+/************** End of sqlite3session.c **************************************/
+/************** Begin file json1.c *******************************************/
+/*
+** 2015-08-12
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This SQLite extension implements JSON functions. The interface is
+** modeled after MySQL JSON functions:
+**
+** https://dev.mysql.com/doc/refman/5.7/en/json.html
+**
+** For the time being, all JSON is stored as pure text. (We might add
+** a JSONB type in the future which stores a binary encoding of JSON in
+** a BLOB, but there is no support for JSONB in the current implementation.
+** This implementation parses JSON text at 250 MB/s, so it is hard to see
+** how JSONB might improve on that.)
+*/
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1)
+#if !defined(_SQLITEINT_H_)
+/* #include "sqlite3ext.h" */
+#endif
+SQLITE_EXTENSION_INIT1
+/* #include <assert.h> */
+/* #include <string.h> */
+/* #include <stdlib.h> */
+/* #include <stdarg.h> */
+
+/* Mark a function parameter as unused, to suppress nuisance compiler
+** warnings. */
+#ifndef UNUSED_PARAM
+# define UNUSED_PARAM(X) (void)(X)
+#endif
+
+#ifndef LARGEST_INT64
+# define LARGEST_INT64 (0xffffffff|(((sqlite3_int64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
+#endif
+
+/*
+** Versions of isspace(), isalnum() and isdigit() to which it is safe
+** to pass signed char values.
+*/
+#ifdef sqlite3Isdigit
+ /* Use the SQLite core versions if this routine is part of the
+ ** SQLite amalgamation */
+# define safe_isdigit(x) sqlite3Isdigit(x)
+# define safe_isalnum(x) sqlite3Isalnum(x)
+#else
+ /* Use the standard library for separate compilation */
+#include <ctype.h> /* amalgamator: keep */
+# define safe_isdigit(x) isdigit((unsigned char)(x))
+# define safe_isalnum(x) isalnum((unsigned char)(x))
+#endif
+
+/*
+** Growing our own isspace() routine this way is twice as fast as
+** the library isspace() function, resulting in a 7% overall performance
+** increase for the parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
+*/
+static const char jsonIsSpace[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+#define safe_isspace(x) (jsonIsSpace[(unsigned char)x])
+
+#ifndef SQLITE_AMALGAMATION
+ /* Unsigned integer types. These are already defined in the sqliteInt.h,
+ ** but the definitions need to be repeated for separate compilation. */
+ typedef sqlite3_uint64 u64;
+ typedef unsigned int u32;
+ typedef unsigned char u8;
+#endif
+
+/* Objects */
+typedef struct JsonString JsonString;
+typedef struct JsonNode JsonNode;
+typedef struct JsonParse JsonParse;
+
+/* An instance of this object represents a JSON string
+** under construction. Really, this is a generic string accumulator
+** that can be and is used to create strings other than JSON.
+*/
+struct JsonString {
+ sqlite3_context *pCtx; /* Function context - put error messages here */
+ char *zBuf; /* Append JSON content here */
+ u64 nAlloc; /* Bytes of storage available in zBuf[] */
+ u64 nUsed; /* Bytes of zBuf[] currently used */
+ u8 bStatic; /* True if zBuf is static space */
+ u8 bErr; /* True if an error has been encountered */
+ char zSpace[100]; /* Initial static space */
+};
+
+/* JSON type values
+*/
+#define JSON_NULL 0
+#define JSON_TRUE 1
+#define JSON_FALSE 2
+#define JSON_INT 3
+#define JSON_REAL 4
+#define JSON_STRING 5
+#define JSON_ARRAY 6
+#define JSON_OBJECT 7
+
+/* The "subtype" set for JSON values */
+#define JSON_SUBTYPE 74 /* Ascii for "J" */
+
+/*
+** Names of the various JSON types:
+*/
+static const char * const jsonType[] = {
+ "null", "true", "false", "integer", "real", "text", "array", "object"
+};
+
+/* Bit values for the JsonNode.jnFlag field
+*/
+#define JNODE_RAW 0x01 /* Content is raw, not JSON encoded */
+#define JNODE_ESCAPE 0x02 /* Content is text with \ escapes */
+#define JNODE_REMOVE 0x04 /* Do not output */
+#define JNODE_REPLACE 0x08 /* Replace with JsonNode.iVal */
+#define JNODE_APPEND 0x10 /* More ARRAY/OBJECT entries at u.iAppend */
+#define JNODE_LABEL 0x20 /* Is a label of an object */
+
+
+/* A single node of parsed JSON
+*/
+struct JsonNode {
+ u8 eType; /* One of the JSON_ type values */
+ u8 jnFlags; /* JNODE flags */
+ u8 iVal; /* Replacement value when JNODE_REPLACE */
+ u32 n; /* Bytes of content, or number of sub-nodes */
+ union {
+ const char *zJContent; /* Content for INT, REAL, and STRING */
+ u32 iAppend; /* More terms for ARRAY and OBJECT */
+ u32 iKey; /* Key for ARRAY objects in json_tree() */
+ } u;
+};
+
+/* A completely parsed JSON string
+*/
+struct JsonParse {
+ u32 nNode; /* Number of slots of aNode[] used */
+ u32 nAlloc; /* Number of slots of aNode[] allocated */
+ JsonNode *aNode; /* Array of nodes containing the parse */
+ const char *zJson; /* Original JSON string */
+ u32 *aUp; /* Index of parent of each node */
+ u8 oom; /* Set to true if out of memory */
+ u8 nErr; /* Number of errors seen */
+};
+
+/**************************************************************************
+** Utility routines for dealing with JsonString objects
+**************************************************************************/
+
+/* Set the JsonString object to an empty string
+*/
+static void jsonZero(JsonString *p){
+ p->zBuf = p->zSpace;
+ p->nAlloc = sizeof(p->zSpace);
+ p->nUsed = 0;
+ p->bStatic = 1;
+}
+
+/* Initialize the JsonString object
+*/
+static void jsonInit(JsonString *p, sqlite3_context *pCtx){
+ p->pCtx = pCtx;
+ p->bErr = 0;
+ jsonZero(p);
+}
+
+
+/* Free all allocated memory and reset the JsonString object back to its
+** initial state.
+*/
+static void jsonReset(JsonString *p){
+ if( !p->bStatic ) sqlite3_free(p->zBuf);
+ jsonZero(p);
+}
+
+
+/* Report an out-of-memory (OOM) condition
+*/
+static void jsonOom(JsonString *p){
+ p->bErr = 1;
+ sqlite3_result_error_nomem(p->pCtx);
+ jsonReset(p);
+}
+
+/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
+** Return zero on success. Return non-zero on an OOM error
+*/
+static int jsonGrow(JsonString *p, u32 N){
+ u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
+ char *zNew;
+ if( p->bStatic ){
+ if( p->bErr ) return 1;
+ zNew = sqlite3_malloc64(nTotal);
+ if( zNew==0 ){
+ jsonOom(p);
+ return SQLITE_NOMEM;
+ }
+ memcpy(zNew, p->zBuf, (size_t)p->nUsed);
+ p->zBuf = zNew;
+ p->bStatic = 0;
+ }else{
+ zNew = sqlite3_realloc64(p->zBuf, nTotal);
+ if( zNew==0 ){
+ jsonOom(p);
+ return SQLITE_NOMEM;
+ }
+ p->zBuf = zNew;
+ }
+ p->nAlloc = nTotal;
+ return SQLITE_OK;
+}
+
+/* Append N bytes from zIn onto the end of the JsonString string.
+*/
+static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
+ if( (N+p->nUsed >= p->nAlloc) && jsonGrow(p,N)!=0 ) return;
+ memcpy(p->zBuf+p->nUsed, zIn, N);
+ p->nUsed += N;
+}
+
+/* Append formatted text (not to exceed N bytes) to the JsonString.
+*/
+static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
+ va_list ap;
+ if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
+ va_start(ap, zFormat);
+ sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
+ va_end(ap);
+ p->nUsed += (int)strlen(p->zBuf+p->nUsed);
+}
+
+/* Append a single character
+*/
+static void jsonAppendChar(JsonString *p, char c){
+ if( p->nUsed>=p->nAlloc && jsonGrow(p,1)!=0 ) return;
+ p->zBuf[p->nUsed++] = c;
+}
+
+/* Append a comma separator to the output buffer, if the previous
+** character is not '[' or '{'.
+*/
+static void jsonAppendSeparator(JsonString *p){
+ char c;
+ if( p->nUsed==0 ) return;
+ c = p->zBuf[p->nUsed-1];
+ if( c!='[' && c!='{' ) jsonAppendChar(p, ',');
+}
+
+/* Append the N-byte string in zIn to the end of the JsonString string
+** under construction. Enclose the string in "..." and escape
+** any double-quotes or backslash characters contained within the
+** string.
+*/
+static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
+ u32 i;
+ if( (N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0 ) return;
+ p->zBuf[p->nUsed++] = '"';
+ for(i=0; i<N; i++){
+ unsigned char c = ((unsigned const char*)zIn)[i];
+ if( c=='"' || c=='\\' ){
+ json_simple_escape:
+ if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
+ p->zBuf[p->nUsed++] = '\\';
+ }else if( c<=0x1f ){
+ static const char aSpecial[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+ };
+ assert( sizeof(aSpecial)==32 );
+ assert( aSpecial['\b']=='b' );
+ assert( aSpecial['\f']=='f' );
+ assert( aSpecial['\n']=='n' );
+ assert( aSpecial['\r']=='r' );
+ assert( aSpecial['\t']=='t' );
+ if( aSpecial[c] ){
+ c = aSpecial[c];
+ goto json_simple_escape;
+ }
+ if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
+ p->zBuf[p->nUsed++] = '\\';
+ p->zBuf[p->nUsed++] = 'u';
+ p->zBuf[p->nUsed++] = '0';
+ p->zBuf[p->nUsed++] = '0';
+ p->zBuf[p->nUsed++] = '0' + (c>>4);
+ c = "0123456789abcdef"[c&0xf];
+ }
+ p->zBuf[p->nUsed++] = c;
+ }
+ p->zBuf[p->nUsed++] = '"';
+ assert( p->nUsed<p->nAlloc );
+}
+
+/*
+** Append a function parameter value to the JSON string under
+** construction.
+*/
+static void jsonAppendValue(
+ JsonString *p, /* Append to this JSON string */
+ sqlite3_value *pValue /* Value to append */
+){
+ switch( sqlite3_value_type(pValue) ){
+ case SQLITE_NULL: {
+ jsonAppendRaw(p, "null", 4);
+ break;
+ }
+ case SQLITE_INTEGER:
+ case SQLITE_FLOAT: {
+ const char *z = (const char*)sqlite3_value_text(pValue);
+ u32 n = (u32)sqlite3_value_bytes(pValue);
+ jsonAppendRaw(p, z, n);
+ break;
+ }
+ case SQLITE_TEXT: {
+ const char *z = (const char*)sqlite3_value_text(pValue);
+ u32 n = (u32)sqlite3_value_bytes(pValue);
+ if( sqlite3_value_subtype(pValue)==JSON_SUBTYPE ){
+ jsonAppendRaw(p, z, n);
+ }else{
+ jsonAppendString(p, z, n);
+ }
+ break;
+ }
+ default: {
+ if( p->bErr==0 ){
+ sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
+ p->bErr = 2;
+ jsonReset(p);
+ }
+ break;
+ }
+ }
+}
+
+
+/* Make the JSON in p the result of the SQL function.
+*/
+static void jsonResult(JsonString *p){
+ if( p->bErr==0 ){
+ sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
+ p->bStatic ? SQLITE_TRANSIENT : sqlite3_free,
+ SQLITE_UTF8);
+ jsonZero(p);
+ }
+ assert( p->bStatic );
+}
+
+/**************************************************************************
+** Utility routines for dealing with JsonNode and JsonParse objects
+**************************************************************************/
+
+/*
+** Return the number of consecutive JsonNode slots need to represent
+** the parsed JSON at pNode. The minimum answer is 1. For ARRAY and
+** OBJECT types, the number might be larger.
+**
+** Appended elements are not counted. The value returned is the number
+** by which the JsonNode counter should increment in order to go to the
+** next peer value.
+*/
+static u32 jsonNodeSize(JsonNode *pNode){
+ return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
+}
+
+/*
+** Reclaim all memory allocated by a JsonParse object. But do not
+** delete the JsonParse object itself.
+*/
+static void jsonParseReset(JsonParse *pParse){
+ sqlite3_free(pParse->aNode);
+ pParse->aNode = 0;
+ pParse->nNode = 0;
+ pParse->nAlloc = 0;
+ sqlite3_free(pParse->aUp);
+ pParse->aUp = 0;
+}
+
+/*
+** Convert the JsonNode pNode into a pure JSON string and
+** append to pOut. Subsubstructure is also included. Return
+** the number of JsonNode objects that are encoded.
+*/
+static void jsonRenderNode(
+ JsonNode *pNode, /* The node to render */
+ JsonString *pOut, /* Write JSON here */
+ sqlite3_value **aReplace /* Replacement values */
+){
+ switch( pNode->eType ){
+ default: {
+ assert( pNode->eType==JSON_NULL );
+ jsonAppendRaw(pOut, "null", 4);
+ break;
+ }
+ case JSON_TRUE: {
+ jsonAppendRaw(pOut, "true", 4);
+ break;
+ }
+ case JSON_FALSE: {
+ jsonAppendRaw(pOut, "false", 5);
+ break;
+ }
+ case JSON_STRING: {
+ if( pNode->jnFlags & JNODE_RAW ){
+ jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
+ break;
+ }
+ /* Fall through into the next case */
+ }
+ case JSON_REAL:
+ case JSON_INT: {
+ jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
+ break;
+ }
+ case JSON_ARRAY: {
+ u32 j = 1;
+ jsonAppendChar(pOut, '[');
+ for(;;){
+ while( j<=pNode->n ){
+ if( pNode[j].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ){
+ if( pNode[j].jnFlags & JNODE_REPLACE ){
+ jsonAppendSeparator(pOut);
+ jsonAppendValue(pOut, aReplace[pNode[j].iVal]);
+ }
+ }else{
+ jsonAppendSeparator(pOut);
+ jsonRenderNode(&pNode[j], pOut, aReplace);
+ }
+ j += jsonNodeSize(&pNode[j]);
+ }
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+ pNode = &pNode[pNode->u.iAppend];
+ j = 1;
+ }
+ jsonAppendChar(pOut, ']');
+ break;
+ }
+ case JSON_OBJECT: {
+ u32 j = 1;
+ jsonAppendChar(pOut, '{');
+ for(;;){
+ while( j<=pNode->n ){
+ if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 ){
+ jsonAppendSeparator(pOut);
+ jsonRenderNode(&pNode[j], pOut, aReplace);
+ jsonAppendChar(pOut, ':');
+ if( pNode[j+1].jnFlags & JNODE_REPLACE ){
+ jsonAppendValue(pOut, aReplace[pNode[j+1].iVal]);
+ }else{
+ jsonRenderNode(&pNode[j+1], pOut, aReplace);
+ }
+ }
+ j += 1 + jsonNodeSize(&pNode[j+1]);
+ }
+ if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
+ pNode = &pNode[pNode->u.iAppend];
+ j = 1;
+ }
+ jsonAppendChar(pOut, '}');
+ break;
+ }
+ }
+}
+
+/*
+** Return a JsonNode and all its descendents as a JSON string.
+*/
+static void jsonReturnJson(
+ JsonNode *pNode, /* Node to return */
+ sqlite3_context *pCtx, /* Return value for this function */
+ sqlite3_value **aReplace /* Array of replacement values */
+){
+ JsonString s;
+ jsonInit(&s, pCtx);
+ jsonRenderNode(pNode, &s, aReplace);
+ jsonResult(&s);
+ sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
+}
+
+/*
+** Make the JsonNode the return value of the function.
+*/
+static void jsonReturn(
+ JsonNode *pNode, /* Node to return */
+ sqlite3_context *pCtx, /* Return value for this function */
+ sqlite3_value **aReplace /* Array of replacement values */
+){
+ switch( pNode->eType ){
+ default: {
+ assert( pNode->eType==JSON_NULL );
+ sqlite3_result_null(pCtx);
+ break;
+ }
+ case JSON_TRUE: {
+ sqlite3_result_int(pCtx, 1);
+ break;
+ }
+ case JSON_FALSE: {
+ sqlite3_result_int(pCtx, 0);
+ break;
+ }
+ case JSON_INT: {
+ sqlite3_int64 i = 0;
+ const char *z = pNode->u.zJContent;
+ if( z[0]=='-' ){ z++; }
+ while( z[0]>='0' && z[0]<='9' ){
+ unsigned v = *(z++) - '0';
+ if( i>=LARGEST_INT64/10 ){
+ if( i>LARGEST_INT64/10 ) goto int_as_real;
+ if( z[0]>='0' && z[0]<='9' ) goto int_as_real;
+ if( v==9 ) goto int_as_real;
+ if( v==8 ){
+ if( pNode->u.zJContent[0]=='-' ){
+ sqlite3_result_int64(pCtx, SMALLEST_INT64);
+ goto int_done;
+ }else{
+ goto int_as_real;
+ }
+ }
+ }
+ i = i*10 + v;
+ }
+ if( pNode->u.zJContent[0]=='-' ){ i = -i; }
+ sqlite3_result_int64(pCtx, i);
+ int_done:
+ break;
+ int_as_real: /* fall through to real */;
+ }
+ case JSON_REAL: {
+ double r;
+#ifdef SQLITE_AMALGAMATION
+ const char *z = pNode->u.zJContent;
+ sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
+#else
+ r = strtod(pNode->u.zJContent, 0);
+#endif
+ sqlite3_result_double(pCtx, r);
+ break;
+ }
+ case JSON_STRING: {
+#if 0 /* Never happens because JNODE_RAW is only set by json_set(),
+ ** json_insert() and json_replace() and those routines do not
+ ** call jsonReturn() */
+ if( pNode->jnFlags & JNODE_RAW ){
+ sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
+ SQLITE_TRANSIENT);
+ }else
+#endif
+ assert( (pNode->jnFlags & JNODE_RAW)==0 );
+ if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
+ /* JSON formatted without any backslash-escapes */
+ sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
+ SQLITE_TRANSIENT);
+ }else{
+ /* Translate JSON formatted string into raw text */
+ u32 i;
+ u32 n = pNode->n;
+ const char *z = pNode->u.zJContent;
+ char *zOut;
+ u32 j;
+ zOut = sqlite3_malloc( n+1 );
+ if( zOut==0 ){
+ sqlite3_result_error_nomem(pCtx);
+ break;
+ }
+ for(i=1, j=0; i<n-1; i++){
+ char c = z[i];
+ if( c!='\\' ){
+ zOut[j++] = c;
+ }else{
+ c = z[++i];
+ if( c=='u' ){
+ u32 v = 0, k;
+ for(k=0; k<4 && i<n-2; i++, k++){
+ c = z[i+1];
+ if( c>='0' && c<='9' ) v = v*16 + c - '0';
+ else if( c>='A' && c<='F' ) v = v*16 + c - 'A' + 10;
+ else if( c>='a' && c<='f' ) v = v*16 + c - 'a' + 10;
+ else break;
+ }
+ if( v==0 ) break;
+ if( v<=0x7f ){
+ zOut[j++] = (char)v;
+ }else if( v<=0x7ff ){
+ zOut[j++] = (char)(0xc0 | (v>>6));
+ zOut[j++] = 0x80 | (v&0x3f);
+ }else{
+ zOut[j++] = (char)(0xe0 | (v>>12));
+ zOut[j++] = 0x80 | ((v>>6)&0x3f);
+ zOut[j++] = 0x80 | (v&0x3f);
+ }
+ }else{
+ if( c=='b' ){
+ c = '\b';
+ }else if( c=='f' ){
+ c = '\f';
+ }else if( c=='n' ){
+ c = '\n';
+ }else if( c=='r' ){
+ c = '\r';
+ }else if( c=='t' ){
+ c = '\t';
+ }
+ zOut[j++] = c;
+ }
+ }
+ }
+ zOut[j] = 0;
+ sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
+ }
+ break;
+ }
+ case JSON_ARRAY:
+ case JSON_OBJECT: {
+ jsonReturnJson(pNode, pCtx, aReplace);
+ break;
+ }
+ }
+}
+
+/* Forward reference */
+static int jsonParseAddNode(JsonParse*,u32,u32,const char*);
+
+/*
+** A macro to hint to the compiler that a function should not be
+** inlined.
+*/
+#if defined(__GNUC__)
+# define JSON_NOINLINE __attribute__((noinline))
+#elif defined(_MSC_VER) && _MSC_VER>=1310
+# define JSON_NOINLINE __declspec(noinline)
+#else
+# define JSON_NOINLINE
+#endif
+
+
+static JSON_NOINLINE int jsonParseAddNodeExpand(
+ JsonParse *pParse, /* Append the node to this object */
+ u32 eType, /* Node type */
+ u32 n, /* Content size or sub-node count */
+ const char *zContent /* Content */
+){
+ u32 nNew;
+ JsonNode *pNew;
+ assert( pParse->nNode>=pParse->nAlloc );
+ if( pParse->oom ) return -1;
+ nNew = pParse->nAlloc*2 + 10;
+ pNew = sqlite3_realloc(pParse->aNode, sizeof(JsonNode)*nNew);
+ if( pNew==0 ){
+ pParse->oom = 1;
+ return -1;
+ }
+ pParse->nAlloc = nNew;
+ pParse->aNode = pNew;
+ assert( pParse->nNode<pParse->nAlloc );
+ return jsonParseAddNode(pParse, eType, n, zContent);
+}
+
+/*
+** Create a new JsonNode instance based on the arguments and append that
+** instance to the JsonParse. Return the index in pParse->aNode[] of the
+** new node, or -1 if a memory allocation fails.
+*/
+static int jsonParseAddNode(
+ JsonParse *pParse, /* Append the node to this object */
+ u32 eType, /* Node type */
+ u32 n, /* Content size or sub-node count */
+ const char *zContent /* Content */
+){
+ JsonNode *p;
+ if( pParse->nNode>=pParse->nAlloc ){
+ return jsonParseAddNodeExpand(pParse, eType, n, zContent);
+ }
+ p = &pParse->aNode[pParse->nNode];
+ p->eType = (u8)eType;
+ p->jnFlags = 0;
+ p->iVal = 0;
+ p->n = n;
+ p->u.zJContent = zContent;
+ return pParse->nNode++;
+}
+
+/*
+** Parse a single JSON value which begins at pParse->zJson[i]. Return the
+** index of the first character past the end of the value parsed.
+**
+** Return negative for a syntax error. Special cases: return -2 if the
+** first non-whitespace character is '}' and return -3 if the first
+** non-whitespace character is ']'.
+*/
+static int jsonParseValue(JsonParse *pParse, u32 i){
+ char c;
+ u32 j;
+ int iThis;
+ int x;
+ JsonNode *pNode;
+ while( safe_isspace(pParse->zJson[i]) ){ i++; }
+ if( (c = pParse->zJson[i])=='{' ){
+ /* Parse object */
+ iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ x = jsonParseValue(pParse, j);
+ if( x<0 ){
+ if( x==(-2) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+ if( pParse->oom ) return -1;
+ pNode = &pParse->aNode[pParse->nNode-1];
+ if( pNode->eType!=JSON_STRING ) return -1;
+ pNode->jnFlags |= JNODE_LABEL;
+ j = x;
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ if( pParse->zJson[j]!=':' ) return -1;
+ j++;
+ x = jsonParseValue(pParse, j);
+ if( x<0 ) return -1;
+ j = x;
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ c = pParse->zJson[j];
+ if( c==',' ) continue;
+ if( c!='}' ) return -1;
+ break;
+ }
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ return j+1;
+ }else if( c=='[' ){
+ /* Parse array */
+ iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ if( iThis<0 ) return -1;
+ for(j=i+1;;j++){
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ x = jsonParseValue(pParse, j);
+ if( x<0 ){
+ if( x==(-3) && pParse->nNode==(u32)iThis+1 ) return j+1;
+ return -1;
+ }
+ j = x;
+ while( safe_isspace(pParse->zJson[j]) ){ j++; }
+ c = pParse->zJson[j];
+ if( c==',' ) continue;
+ if( c!=']' ) return -1;
+ break;
+ }
+ pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
+ return j+1;
+ }else if( c=='"' ){
+ /* Parse string */
+ u8 jnFlags = 0;
+ j = i+1;
+ for(;;){
+ c = pParse->zJson[j];
+ if( c==0 ) return -1;
+ if( c=='\\' ){
+ c = pParse->zJson[++j];
+ if( c==0 ) return -1;
+ jnFlags = JNODE_ESCAPE;
+ }else if( c=='"' ){
+ break;
+ }
+ j++;
+ }
+ jsonParseAddNode(pParse, JSON_STRING, j+1-i, &pParse->zJson[i]);
+ if( !pParse->oom ) pParse->aNode[pParse->nNode-1].jnFlags = jnFlags;
+ return j+1;
+ }else if( c=='n'
+ && strncmp(pParse->zJson+i,"null",4)==0
+ && !safe_isalnum(pParse->zJson[i+4]) ){
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ return i+4;
+ }else if( c=='t'
+ && strncmp(pParse->zJson+i,"true",4)==0
+ && !safe_isalnum(pParse->zJson[i+4]) ){
+ jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
+ return i+4;
+ }else if( c=='f'
+ && strncmp(pParse->zJson+i,"false",5)==0
+ && !safe_isalnum(pParse->zJson[i+5]) ){
+ jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
+ return i+5;
+ }else if( c=='-' || (c>='0' && c<='9') ){
+ /* Parse number */
+ u8 seenDP = 0;
+ u8 seenE = 0;
+ j = i+1;
+ for(;; j++){
+ c = pParse->zJson[j];
+ if( c>='0' && c<='9' ) continue;
+ if( c=='.' ){
+ if( pParse->zJson[j-1]=='-' ) return -1;
+ if( seenDP ) return -1;
+ seenDP = 1;
+ continue;
+ }
+ if( c=='e' || c=='E' ){
+ if( pParse->zJson[j-1]<'0' ) return -1;
+ if( seenE ) return -1;
+ seenDP = seenE = 1;
+ c = pParse->zJson[j+1];
+ if( c=='+' || c=='-' ){
+ j++;
+ c = pParse->zJson[j+1];
+ }
+ if( c<'0' || c>'9' ) return -1;
+ continue;
+ }
+ break;
+ }
+ if( pParse->zJson[j-1]<'0' ) return -1;
+ jsonParseAddNode(pParse, seenDP ? JSON_REAL : JSON_INT,
+ j - i, &pParse->zJson[i]);
+ return j;
+ }else if( c=='}' ){
+ return -2; /* End of {...} */
+ }else if( c==']' ){
+ return -3; /* End of [...] */
+ }else if( c==0 ){
+ return 0; /* End of file */
+ }else{
+ return -1; /* Syntax error */
+ }
+}
+
+/*
+** Parse a complete JSON string. Return 0 on success or non-zero if there
+** are any errors. If an error occurs, free all memory associated with
+** pParse.
+**
+** pParse is uninitialized when this routine is called.
+*/
+static int jsonParse(
+ JsonParse *pParse, /* Initialize and fill this JsonParse object */
+ sqlite3_context *pCtx, /* Report errors here */
+ const char *zJson /* Input JSON text to be parsed */
+){
+ int i;
+ memset(pParse, 0, sizeof(*pParse));
+ if( zJson==0 ) return 1;
+ pParse->zJson = zJson;
+ i = jsonParseValue(pParse, 0);
+ if( pParse->oom ) i = -1;
+ if( i>0 ){
+ while( safe_isspace(zJson[i]) ) i++;
+ if( zJson[i] ) i = -1;
+ }
+ if( i<=0 ){
+ if( pCtx!=0 ){
+ if( pParse->oom ){
+ sqlite3_result_error_nomem(pCtx);
+ }else{
+ sqlite3_result_error(pCtx, "malformed JSON", -1);
+ }
+ }
+ jsonParseReset(pParse);
+ return 1;
+ }
+ return 0;
+}
+
+/* Mark node i of pParse as being a child of iParent. Call recursively
+** to fill in all the descendants of node i.
+*/
+static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
+ JsonNode *pNode = &pParse->aNode[i];
+ u32 j;
+ pParse->aUp[i] = iParent;
+ switch( pNode->eType ){
+ case JSON_ARRAY: {
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
+ jsonParseFillInParentage(pParse, i+j, i);
+ }
+ break;
+ }
+ case JSON_OBJECT: {
+ for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){
+ pParse->aUp[i+j] = i;
+ jsonParseFillInParentage(pParse, i+j+1, i);
+ }
+ break;
+ }
+ default: {
+ break;
+ }
+ }
+}
+
+/*
+** Compute the parentage of all nodes in a completed parse.
+*/
+static int jsonParseFindParents(JsonParse *pParse){
+ u32 *aUp;
+ assert( pParse->aUp==0 );
+ aUp = pParse->aUp = sqlite3_malloc( sizeof(u32)*pParse->nNode );
+ if( aUp==0 ){
+ pParse->oom = 1;
+ return SQLITE_NOMEM;
+ }
+ jsonParseFillInParentage(pParse, 0, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Compare the OBJECT label at pNode against zKey,nKey. Return true on
+** a match.
+*/
+static int jsonLabelCompare(JsonNode *pNode, const char *zKey, u32 nKey){
+ if( pNode->jnFlags & JNODE_RAW ){
+ if( pNode->n!=nKey ) return 0;
+ return strncmp(pNode->u.zJContent, zKey, nKey)==0;
+ }else{
+ if( pNode->n!=nKey+2 ) return 0;
+ return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
+ }
+}
+
+/* forward declaration */
+static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);
+
+/*
+** Search along zPath to find the node specified. Return a pointer
+** to that node, or NULL if zPath is malformed or if there is no such
+** node.
+**
+** If pApnd!=0, then try to append new nodes to complete zPath if it is
+** possible to do so and if no existing node corresponds to zPath. If
+** new nodes are appended *pApnd is set to 1.
+*/
+static JsonNode *jsonLookupStep(
+ JsonParse *pParse, /* The JSON to search */
+ u32 iRoot, /* Begin the search at this node */
+ const char *zPath, /* The path to search */
+ int *pApnd, /* Append nodes to complete path if not NULL */
+ const char **pzErr /* Make *pzErr point to any syntax error in zPath */
+){
+ u32 i, j, nKey;
+ const char *zKey;
+ JsonNode *pRoot = &pParse->aNode[iRoot];
+ if( zPath[0]==0 ) return pRoot;
+ if( zPath[0]=='.' ){
+ if( pRoot->eType!=JSON_OBJECT ) return 0;
+ zPath++;
+ if( zPath[0]=='"' ){
+ zKey = zPath + 1;
+ for(i=1; zPath[i] && zPath[i]!='"'; i++){}
+ nKey = i-1;
+ if( zPath[i] ){
+ i++;
+ }else{
+ *pzErr = zPath;
+ return 0;
+ }
+ }else{
+ zKey = zPath;
+ for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
+ nKey = i;
+ }
+ if( nKey==0 ){
+ *pzErr = zPath;
+ return 0;
+ }
+ j = 1;
+ for(;;){
+ while( j<=pRoot->n ){
+ if( jsonLabelCompare(pRoot+j, zKey, nKey) ){
+ return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);
+ }
+ j++;
+ j += jsonNodeSize(&pRoot[j]);
+ }
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+ iRoot += pRoot->u.iAppend;
+ pRoot = &pParse->aNode[iRoot];
+ j = 1;
+ }
+ if( pApnd ){
+ u32 iStart, iLabel;
+ JsonNode *pNode;
+ iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);
+ iLabel = jsonParseAddNode(pParse, JSON_STRING, i, zPath);
+ zPath += i;
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
+ if( pParse->oom ) return 0;
+ if( pNode ){
+ pRoot = &pParse->aNode[iRoot];
+ pRoot->u.iAppend = iStart - iRoot;
+ pRoot->jnFlags |= JNODE_APPEND;
+ pParse->aNode[iLabel].jnFlags |= JNODE_RAW;
+ }
+ return pNode;
+ }
+ }else if( zPath[0]=='[' && safe_isdigit(zPath[1]) ){
+ if( pRoot->eType!=JSON_ARRAY ) return 0;
+ i = 0;
+ j = 1;
+ while( safe_isdigit(zPath[j]) ){
+ i = i*10 + zPath[j] - '0';
+ j++;
+ }
+ if( zPath[j]!=']' ){
+ *pzErr = zPath;
+ return 0;
+ }
+ zPath += j + 1;
+ j = 1;
+ for(;;){
+ while( j<=pRoot->n && (i>0 || (pRoot[j].jnFlags & JNODE_REMOVE)!=0) ){
+ if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 ) i--;
+ j += jsonNodeSize(&pRoot[j]);
+ }
+ if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
+ iRoot += pRoot->u.iAppend;
+ pRoot = &pParse->aNode[iRoot];
+ j = 1;
+ }
+ if( j<=pRoot->n ){
+ return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);
+ }
+ if( i==0 && pApnd ){
+ u32 iStart;
+ JsonNode *pNode;
+ iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
+ pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
+ if( pParse->oom ) return 0;
+ if( pNode ){
+ pRoot = &pParse->aNode[iRoot];
+ pRoot->u.iAppend = iStart - iRoot;
+ pRoot->jnFlags |= JNODE_APPEND;
+ }
+ return pNode;
+ }
+ }else{
+ *pzErr = zPath;
+ }
+ return 0;
+}
+
+/*
+** Append content to pParse that will complete zPath. Return a pointer
+** to the inserted node, or return NULL if the append fails.
+*/
+static JsonNode *jsonLookupAppend(
+ JsonParse *pParse, /* Append content to the JSON parse */
+ const char *zPath, /* Description of content to append */
+ int *pApnd, /* Set this flag to 1 */
+ const char **pzErr /* Make this point to any syntax error */
+){
+ *pApnd = 1;
+ if( zPath[0]==0 ){
+ jsonParseAddNode(pParse, JSON_NULL, 0, 0);
+ return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
+ }
+ if( zPath[0]=='.' ){
+ jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
+ }else if( strncmp(zPath,"[0]",3)==0 ){
+ jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
+ }else{
+ return 0;
+ }
+ if( pParse->oom ) return 0;
+ return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);
+}
+
+/*
+** Return the text of a syntax error message on a JSON path. Space is
+** obtained from sqlite3_malloc().
+*/
+static char *jsonPathSyntaxError(const char *zErr){
+ return sqlite3_mprintf("JSON path error near '%q'", zErr);
+}
+
+/*
+** Do a node lookup using zPath. Return a pointer to the node on success.
+** Return NULL if not found or if there is an error.
+**
+** On an error, write an error message into pCtx and increment the
+** pParse->nErr counter.
+**
+** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
+** nodes are appended.
+*/
+static JsonNode *jsonLookup(
+ JsonParse *pParse, /* The JSON to search */
+ const char *zPath, /* The path to search */
+ int *pApnd, /* Append nodes to complete path if not NULL */
+ sqlite3_context *pCtx /* Report errors here, if not NULL */
+){
+ const char *zErr = 0;
+ JsonNode *pNode = 0;
+ char *zMsg;
+
+ if( zPath==0 ) return 0;
+ if( zPath[0]!='$' ){
+ zErr = zPath;
+ goto lookup_err;
+ }
+ zPath++;
+ pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);
+ if( zErr==0 ) return pNode;
+
+lookup_err:
+ pParse->nErr++;
+ assert( zErr!=0 && pCtx!=0 );
+ zMsg = jsonPathSyntaxError(zErr);
+ if( zMsg ){
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+ }else{
+ sqlite3_result_error_nomem(pCtx);
+ }
+ return 0;
+}
+
+
+/*
+** Report the wrong number of arguments for json_insert(), json_replace()
+** or json_set().
+*/
+static void jsonWrongNumArgs(
+ sqlite3_context *pCtx,
+ const char *zFuncName
+){
+ char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
+ zFuncName);
+ sqlite3_result_error(pCtx, zMsg, -1);
+ sqlite3_free(zMsg);
+}
+
+
+/****************************************************************************
+** SQL functions used for testing and debugging
+****************************************************************************/
+
+#ifdef SQLITE_DEBUG
+/*
+** The json_parse(JSON) function returns a string which describes
+** a parse of the JSON provided. Or it returns NULL if JSON is not
+** well-formed.
+*/
+static void jsonParseFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString s; /* Output string - not real JSON */
+ JsonParse x; /* The parse */
+ u32 i;
+
+ assert( argc==1 );
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ jsonParseFindParents(&x);
+ jsonInit(&s, ctx);
+ for(i=0; i<x.nNode; i++){
+ const char *zType;
+ if( x.aNode[i].jnFlags & JNODE_LABEL ){
+ assert( x.aNode[i].eType==JSON_STRING );
+ zType = "label";
+ }else{
+ zType = jsonType[x.aNode[i].eType];
+ }
+ jsonPrintf(100, &s,"node %3u: %7s n=%-4d up=%-4d",
+ i, zType, x.aNode[i].n, x.aUp[i]);
+ if( x.aNode[i].u.zJContent!=0 ){
+ jsonAppendRaw(&s, " ", 1);
+ jsonAppendRaw(&s, x.aNode[i].u.zJContent, x.aNode[i].n);
+ }
+ jsonAppendRaw(&s, "\n", 1);
+ }
+ jsonParseReset(&x);
+ jsonResult(&s);
+}
+
+/*
+** The json_test1(JSON) function return true (1) if the input is JSON
+** text generated by another json function. It returns (0) if the input
+** is not known to be JSON.
+*/
+static void jsonTest1Func(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ UNUSED_PARAM(argc);
+ sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
+}
+#endif /* SQLITE_DEBUG */
+
+/****************************************************************************
+** Scalar SQL function implementations
+****************************************************************************/
+
+/*
+** Implementation of the json_QUOTE(VALUE) function. Return a JSON value
+** corresponding to the SQL value input. Mostly this means putting
+** double-quotes around strings and returning the unquoted string "null"
+** when given a NULL input.
+*/
+static void jsonQuoteFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString jx;
+ UNUSED_PARAM(argc);
+
+ jsonInit(&jx, ctx);
+ jsonAppendValue(&jx, argv[0]);
+ jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+/*
+** Implementation of the json_array(VALUE,...) function. Return a JSON
+** array that contains all values given in arguments. Or if any argument
+** is a BLOB, throw an error.
+*/
+static void jsonArrayFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ JsonString jx;
+
+ jsonInit(&jx, ctx);
+ jsonAppendChar(&jx, '[');
+ for(i=0; i<argc; i++){
+ jsonAppendSeparator(&jx);
+ jsonAppendValue(&jx, argv[i]);
+ }
+ jsonAppendChar(&jx, ']');
+ jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+
+/*
+** json_array_length(JSON)
+** json_array_length(JSON, PATH)
+**
+** Return the number of elements in the top-level JSON array.
+** Return 0 if the input is not a well-formed JSON array.
+*/
+static void jsonArrayLengthFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ sqlite3_int64 n = 0;
+ u32 i;
+ JsonNode *pNode;
+
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ if( argc==2 ){
+ const char *zPath = (const char*)sqlite3_value_text(argv[1]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ }else{
+ pNode = x.aNode;
+ }
+ if( pNode==0 ){
+ x.nErr = 1;
+ }else if( pNode->eType==JSON_ARRAY ){
+ assert( (pNode->jnFlags & JNODE_APPEND)==0 );
+ for(i=1; i<=pNode->n; n++){
+ i += jsonNodeSize(&pNode[i]);
+ }
+ }
+ if( x.nErr==0 ) sqlite3_result_int64(ctx, n);
+ jsonParseReset(&x);
+}
+
+/*
+** json_extract(JSON, PATH, ...)
+**
+** Return the element described by PATH. Return NULL if there is no
+** PATH element. If there are multiple PATHs, then return a JSON array
+** with the result from each path. Throw an error if the JSON or any PATH
+** is malformed.
+*/
+static void jsonExtractFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ JsonString jx;
+ int i;
+
+ if( argc<2 ) return;
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ jsonInit(&jx, ctx);
+ jsonAppendChar(&jx, '[');
+ for(i=1; i<argc; i++){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) break;
+ if( argc>2 ){
+ jsonAppendSeparator(&jx);
+ if( pNode ){
+ jsonRenderNode(pNode, &jx, 0);
+ }else{
+ jsonAppendRaw(&jx, "null", 4);
+ }
+ }else if( pNode ){
+ jsonReturn(pNode, ctx, 0);
+ }
+ }
+ if( argc>2 && i==argc ){
+ jsonAppendChar(&jx, ']');
+ jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+ }
+ jsonReset(&jx);
+ jsonParseReset(&x);
+}
+
+/*
+** Implementation of the json_object(NAME,VALUE,...) function. Return a JSON
+** object that contains all name/value given in arguments. Or if any name
+** is not a string or if any value is a BLOB, throw an error.
+*/
+static void jsonObjectFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ int i;
+ JsonString jx;
+ const char *z;
+ u32 n;
+
+ if( argc&1 ){
+ sqlite3_result_error(ctx, "json_object() requires an even number "
+ "of arguments", -1);
+ return;
+ }
+ jsonInit(&jx, ctx);
+ jsonAppendChar(&jx, '{');
+ for(i=0; i<argc; i+=2){
+ if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
+ sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
+ jsonReset(&jx);
+ return;
+ }
+ jsonAppendSeparator(&jx);
+ z = (const char*)sqlite3_value_text(argv[i]);
+ n = (u32)sqlite3_value_bytes(argv[i]);
+ jsonAppendString(&jx, z, n);
+ jsonAppendChar(&jx, ':');
+ jsonAppendValue(&jx, argv[i+1]);
+ }
+ jsonAppendChar(&jx, '}');
+ jsonResult(&jx);
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+
+/*
+** json_remove(JSON, PATH, ...)
+**
+** Remove the named elements from JSON and return the result. malformed
+** JSON or PATH arguments result in an error.
+*/
+static void jsonRemoveFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ u32 i;
+
+ if( argc<1 ) return;
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i++){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ if( zPath==0 ) goto remove_done;
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) goto remove_done;
+ if( pNode ) pNode->jnFlags |= JNODE_REMOVE;
+ }
+ if( (x.aNode[0].jnFlags & JNODE_REMOVE)==0 ){
+ jsonReturnJson(x.aNode, ctx, 0);
+ }
+remove_done:
+ jsonParseReset(&x);
+}
+
+/*
+** json_replace(JSON, PATH, VALUE, ...)
+**
+** Replace the value at PATH with VALUE. If PATH does not already exist,
+** this routine is a no-op. If JSON or PATH is malformed, throw an error.
+*/
+static void jsonReplaceFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ u32 i;
+
+ if( argc<1 ) return;
+ if( (argc&1)==0 ) {
+ jsonWrongNumArgs(ctx, "replace");
+ return;
+ }
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i+=2){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ if( x.nErr ) goto replace_err;
+ if( pNode ){
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
+ pNode->iVal = (u8)(i+1);
+ }
+ }
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+ sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
+ }else{
+ jsonReturnJson(x.aNode, ctx, argv);
+ }
+replace_err:
+ jsonParseReset(&x);
+}
+
+/*
+** json_set(JSON, PATH, VALUE, ...)
+**
+** Set the value at PATH to VALUE. Create the PATH if it does not already
+** exist. Overwrite existing values that do exist.
+** If JSON or PATH is malformed, throw an error.
+**
+** json_insert(JSON, PATH, VALUE, ...)
+**
+** Create PATH and initialize it to VALUE. If PATH already exists, this
+** routine is a no-op. If JSON or PATH is malformed, throw an error.
+*/
+static void jsonSetFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ JsonNode *pNode;
+ const char *zPath;
+ u32 i;
+ int bApnd;
+ int bIsSet = *(int*)sqlite3_user_data(ctx);
+
+ if( argc<1 ) return;
+ if( (argc&1)==0 ) {
+ jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
+ return;
+ }
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ for(i=1; i<(u32)argc; i+=2){
+ zPath = (const char*)sqlite3_value_text(argv[i]);
+ bApnd = 0;
+ pNode = jsonLookup(&x, zPath, &bApnd, ctx);
+ if( x.oom ){
+ sqlite3_result_error_nomem(ctx);
+ goto jsonSetDone;
+ }else if( x.nErr ){
+ goto jsonSetDone;
+ }else if( pNode && (bApnd || bIsSet) ){
+ pNode->jnFlags |= (u8)JNODE_REPLACE;
+ pNode->iVal = (u8)(i+1);
+ }
+ }
+ if( x.aNode[0].jnFlags & JNODE_REPLACE ){
+ sqlite3_result_value(ctx, argv[x.aNode[0].iVal]);
+ }else{
+ jsonReturnJson(x.aNode, ctx, argv);
+ }
+jsonSetDone:
+ jsonParseReset(&x);
+}
+
+/*
+** json_type(JSON)
+** json_type(JSON, PATH)
+**
+** Return the top-level "type" of a JSON string. Throw an error if
+** either the JSON or PATH inputs are not well-formed.
+*/
+static void jsonTypeFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ const char *zPath;
+ JsonNode *pNode;
+
+ if( jsonParse(&x, ctx, (const char*)sqlite3_value_text(argv[0])) ) return;
+ assert( x.nNode );
+ if( argc==2 ){
+ zPath = (const char*)sqlite3_value_text(argv[1]);
+ pNode = jsonLookup(&x, zPath, 0, ctx);
+ }else{
+ pNode = x.aNode;
+ }
+ if( pNode ){
+ sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
+ }
+ jsonParseReset(&x);
+}
+
+/*
+** json_valid(JSON)
+**
+** Return 1 if JSON is a well-formed JSON string according to RFC-7159.
+** Return 0 otherwise.
+*/
+static void jsonValidFunc(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonParse x; /* The parse */
+ int rc = 0;
+
+ UNUSED_PARAM(argc);
+ if( jsonParse(&x, 0, (const char*)sqlite3_value_text(argv[0]))==0 ){
+ rc = 1;
+ }
+ jsonParseReset(&x);
+ sqlite3_result_int(ctx, rc);
+}
+
+
+/****************************************************************************
+** Aggregate SQL function implementations
+****************************************************************************/
+/*
+** json_group_array(VALUE)
+**
+** Return a JSON array composed of all values in the aggregate.
+*/
+static void jsonArrayStep(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString *pStr;
+ UNUSED_PARAM(argc);
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
+ if( pStr ){
+ if( pStr->zBuf==0 ){
+ jsonInit(pStr, ctx);
+ jsonAppendChar(pStr, '[');
+ }else{
+ jsonAppendChar(pStr, ',');
+ pStr->pCtx = ctx;
+ }
+ jsonAppendValue(pStr, argv[0]);
+ }
+}
+static void jsonArrayFinal(sqlite3_context *ctx){
+ JsonString *pStr;
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
+ if( pStr ){
+ pStr->pCtx = ctx;
+ jsonAppendChar(pStr, ']');
+ if( pStr->bErr ){
+ if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
+ assert( pStr->bStatic );
+ }else{
+ sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+ pStr->bStatic = 1;
+ }
+ }else{
+ sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
+ }
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+/*
+** json_group_obj(NAME,VALUE)
+**
+** Return a JSON object composed of all names and values in the aggregate.
+*/
+static void jsonObjectStep(
+ sqlite3_context *ctx,
+ int argc,
+ sqlite3_value **argv
+){
+ JsonString *pStr;
+ const char *z;
+ u32 n;
+ UNUSED_PARAM(argc);
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
+ if( pStr ){
+ if( pStr->zBuf==0 ){
+ jsonInit(pStr, ctx);
+ jsonAppendChar(pStr, '{');
+ }else{
+ jsonAppendChar(pStr, ',');
+ pStr->pCtx = ctx;
+ }
+ z = (const char*)sqlite3_value_text(argv[0]);
+ n = (u32)sqlite3_value_bytes(argv[0]);
+ jsonAppendString(pStr, z, n);
+ jsonAppendChar(pStr, ':');
+ jsonAppendValue(pStr, argv[1]);
+ }
+}
+static void jsonObjectFinal(sqlite3_context *ctx){
+ JsonString *pStr;
+ pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
+ if( pStr ){
+ jsonAppendChar(pStr, '}');
+ if( pStr->bErr ){
+ if( pStr->bErr==0 ) sqlite3_result_error_nomem(ctx);
+ assert( pStr->bStatic );
+ }else{
+ sqlite3_result_text(ctx, pStr->zBuf, pStr->nUsed,
+ pStr->bStatic ? SQLITE_TRANSIENT : sqlite3_free);
+ pStr->bStatic = 1;
+ }
+ }else{
+ sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
+ }
+ sqlite3_result_subtype(ctx, JSON_SUBTYPE);
+}
+
+
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+/****************************************************************************
+** The json_each virtual table
+****************************************************************************/
+typedef struct JsonEachCursor JsonEachCursor;
+struct JsonEachCursor {
+ sqlite3_vtab_cursor base; /* Base class - must be first */
+ u32 iRowid; /* The rowid */
+ u32 iBegin; /* The first node of the scan */
+ u32 i; /* Index in sParse.aNode[] of current row */
+ u32 iEnd; /* EOF when i equals or exceeds this value */
+ u8 eType; /* Type of top-level element */
+ u8 bRecursive; /* True for json_tree(). False for json_each() */
+ char *zJson; /* Input JSON */
+ char *zRoot; /* Path by which to filter zJson */
+ JsonParse sParse; /* Parse of the input JSON */
+};
+
+/* Constructor for the json_each virtual table */
+static int jsonEachConnect(
+ sqlite3 *db,
+ void *pAux,
+ int argc, const char *const*argv,
+ sqlite3_vtab **ppVtab,
+ char **pzErr
+){
+ sqlite3_vtab *pNew;
+ int rc;
+
+/* Column numbers */
+#define JEACH_KEY 0
+#define JEACH_VALUE 1
+#define JEACH_TYPE 2
+#define JEACH_ATOM 3
+#define JEACH_ID 4
+#define JEACH_PARENT 5
+#define JEACH_FULLKEY 6
+#define JEACH_PATH 7
+#define JEACH_JSON 8
+#define JEACH_ROOT 9
+
+ UNUSED_PARAM(pzErr);
+ UNUSED_PARAM(argv);
+ UNUSED_PARAM(argc);
+ UNUSED_PARAM(pAux);
+ rc = sqlite3_declare_vtab(db,
+ "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
+ "json HIDDEN,root HIDDEN)");
+ if( rc==SQLITE_OK ){
+ pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
+ if( pNew==0 ) return SQLITE_NOMEM;
+ memset(pNew, 0, sizeof(*pNew));
+ }
+ return rc;
+}
+
+/* destructor for json_each virtual table */
+static int jsonEachDisconnect(sqlite3_vtab *pVtab){
+ sqlite3_free(pVtab);
+ return SQLITE_OK;
+}
+
+/* constructor for a JsonEachCursor object for json_each(). */
+static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ JsonEachCursor *pCur;
+
+ UNUSED_PARAM(p);
+ pCur = sqlite3_malloc( sizeof(*pCur) );
+ if( pCur==0 ) return SQLITE_NOMEM;
+ memset(pCur, 0, sizeof(*pCur));
+ *ppCursor = &pCur->base;
+ return SQLITE_OK;
+}
+
+/* constructor for a JsonEachCursor object for json_tree(). */
+static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
+ int rc = jsonEachOpenEach(p, ppCursor);
+ if( rc==SQLITE_OK ){
+ JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
+ pCur->bRecursive = 1;
+ }
+ return rc;
+}
+
+/* Reset a JsonEachCursor back to its original state. Free any memory
+** held. */
+static void jsonEachCursorReset(JsonEachCursor *p){
+ sqlite3_free(p->zJson);
+ sqlite3_free(p->zRoot);
+ jsonParseReset(&p->sParse);
+ p->iRowid = 0;
+ p->i = 0;
+ p->iEnd = 0;
+ p->eType = 0;
+ p->zJson = 0;
+ p->zRoot = 0;
+}
+
+/* Destructor for a jsonEachCursor object */
+static int jsonEachClose(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ jsonEachCursorReset(p);
+ sqlite3_free(cur);
+ return SQLITE_OK;
+}
+
+/* Return TRUE if the jsonEachCursor object has been advanced off the end
+** of the JSON object */
+static int jsonEachEof(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ return p->i >= p->iEnd;
+}
+
+/* Advance the cursor to the next element for json_tree() */
+static int jsonEachNext(sqlite3_vtab_cursor *cur){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ if( p->bRecursive ){
+ if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
+ p->i++;
+ p->iRowid++;
+ if( p->i<p->iEnd ){
+ u32 iUp = p->sParse.aUp[p->i];
+ JsonNode *pUp = &p->sParse.aNode[iUp];
+ p->eType = pUp->eType;
+ if( pUp->eType==JSON_ARRAY ){
+ if( iUp==p->i-1 ){
+ pUp->u.iKey = 0;
+ }else{
+ pUp->u.iKey++;
+ }
+ }
+ }
+ }else{
+ switch( p->eType ){
+ case JSON_ARRAY: {
+ p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
+ p->iRowid++;
+ break;
+ }
+ case JSON_OBJECT: {
+ p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
+ p->iRowid++;
+ break;
+ }
+ default: {
+ p->i = p->iEnd;
+ break;
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* Append the name of the path for element i to pStr
+*/
+static void jsonEachComputePath(
+ JsonEachCursor *p, /* The cursor */
+ JsonString *pStr, /* Write the path here */
+ u32 i /* Path to this element */
+){
+ JsonNode *pNode, *pUp;
+ u32 iUp;
+ if( i==0 ){
+ jsonAppendChar(pStr, '$');
+ return;
+ }
+ iUp = p->sParse.aUp[i];
+ jsonEachComputePath(p, pStr, iUp);
+ pNode = &p->sParse.aNode[i];
+ pUp = &p->sParse.aNode[iUp];
+ if( pUp->eType==JSON_ARRAY ){
+ jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
+ }else{
+ assert( pUp->eType==JSON_OBJECT );
+ if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
+ assert( pNode->eType==JSON_STRING );
+ assert( pNode->jnFlags & JNODE_LABEL );
+ jsonPrintf(pNode->n+1, pStr, ".%.*s", pNode->n-2, pNode->u.zJContent+1);
+ }
+}
+
+/* Return the value of a column */
+static int jsonEachColumn(
+ sqlite3_vtab_cursor *cur, /* The cursor */
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
+ int i /* Which column to return */
+){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ JsonNode *pThis = &p->sParse.aNode[p->i];
+ switch( i ){
+ case JEACH_KEY: {
+ if( p->i==0 ) break;
+ if( p->eType==JSON_OBJECT ){
+ jsonReturn(pThis, ctx, 0);
+ }else if( p->eType==JSON_ARRAY ){
+ u32 iKey;
+ if( p->bRecursive ){
+ if( p->iRowid==0 ) break;
+ iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;
+ }else{
+ iKey = p->iRowid;
+ }
+ sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
+ }
+ break;
+ }
+ case JEACH_VALUE: {
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
+ jsonReturn(pThis, ctx, 0);
+ break;
+ }
+ case JEACH_TYPE: {
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
+ sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
+ break;
+ }
+ case JEACH_ATOM: {
+ if( pThis->jnFlags & JNODE_LABEL ) pThis++;
+ if( pThis->eType>=JSON_ARRAY ) break;
+ jsonReturn(pThis, ctx, 0);
+ break;
+ }
+ case JEACH_ID: {
+ sqlite3_result_int64(ctx,
+ (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
+ break;
+ }
+ case JEACH_PARENT: {
+ if( p->i>p->iBegin && p->bRecursive ){
+ sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
+ }
+ break;
+ }
+ case JEACH_FULLKEY: {
+ JsonString x;
+ jsonInit(&x, ctx);
+ if( p->bRecursive ){
+ jsonEachComputePath(p, &x, p->i);
+ }else{
+ if( p->zRoot ){
+ jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
+ }else{
+ jsonAppendChar(&x, '$');
+ }
+ if( p->eType==JSON_ARRAY ){
+ jsonPrintf(30, &x, "[%d]", p->iRowid);
+ }else{
+ jsonPrintf(pThis->n, &x, ".%.*s", pThis->n-2, pThis->u.zJContent+1);
+ }
+ }
+ jsonResult(&x);
+ break;
+ }
+ case JEACH_PATH: {
+ if( p->bRecursive ){
+ JsonString x;
+ jsonInit(&x, ctx);
+ jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
+ jsonResult(&x);
+ break;
+ }
+ /* For json_each() path and root are the same so fall through
+ ** into the root case */
+ }
+ case JEACH_ROOT: {
+ const char *zRoot = p->zRoot;
+ if( zRoot==0 ) zRoot = "$";
+ sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
+ break;
+ }
+ case JEACH_JSON: {
+ assert( i==JEACH_JSON );
+ sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
+ break;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* Return the current rowid value */
+static int jsonEachRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ *pRowid = p->iRowid;
+ return SQLITE_OK;
+}
+
+/* The query strategy is to look for an equality constraint on the json
+** column. Without such a constraint, the table cannot operate. idxNum is
+** 1 if the constraint is found, 3 if the constraint and zRoot are found,
+** and 0 otherwise.
+*/
+static int jsonEachBestIndex(
+ sqlite3_vtab *tab,
+ sqlite3_index_info *pIdxInfo
+){
+ int i;
+ int jsonIdx = -1;
+ int rootIdx = -1;
+ const struct sqlite3_index_constraint *pConstraint;
+
+ UNUSED_PARAM(tab);
+ pConstraint = pIdxInfo->aConstraint;
+ for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
+ if( pConstraint->usable==0 ) continue;
+ if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
+ switch( pConstraint->iColumn ){
+ case JEACH_JSON: jsonIdx = i; break;
+ case JEACH_ROOT: rootIdx = i; break;
+ default: /* no-op */ break;
+ }
+ }
+ if( jsonIdx<0 ){
+ pIdxInfo->idxNum = 0;
+ pIdxInfo->estimatedCost = 1e99;
+ }else{
+ pIdxInfo->estimatedCost = 1.0;
+ pIdxInfo->aConstraintUsage[jsonIdx].argvIndex = 1;
+ pIdxInfo->aConstraintUsage[jsonIdx].omit = 1;
+ if( rootIdx<0 ){
+ pIdxInfo->idxNum = 1;
+ }else{
+ pIdxInfo->aConstraintUsage[rootIdx].argvIndex = 2;
+ pIdxInfo->aConstraintUsage[rootIdx].omit = 1;
+ pIdxInfo->idxNum = 3;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* Start a search on a new JSON string */
+static int jsonEachFilter(
+ sqlite3_vtab_cursor *cur,
+ int idxNum, const char *idxStr,
+ int argc, sqlite3_value **argv
+){
+ JsonEachCursor *p = (JsonEachCursor*)cur;
+ const char *z;
+ const char *zRoot = 0;
+ sqlite3_int64 n;
+
+ UNUSED_PARAM(idxStr);
+ UNUSED_PARAM(argc);
+ jsonEachCursorReset(p);
+ if( idxNum==0 ) return SQLITE_OK;
+ z = (const char*)sqlite3_value_text(argv[0]);
+ if( z==0 ) return SQLITE_OK;
+ n = sqlite3_value_bytes(argv[0]);
+ p->zJson = sqlite3_malloc64( n+1 );
+ if( p->zJson==0 ) return SQLITE_NOMEM;
+ memcpy(p->zJson, z, (size_t)n+1);
+ if( jsonParse(&p->sParse, 0, p->zJson) ){
+ int rc = SQLITE_NOMEM;
+ if( p->sParse.oom==0 ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
+ if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
+ }
+ jsonEachCursorReset(p);
+ return rc;
+ }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
+ jsonEachCursorReset(p);
+ return SQLITE_NOMEM;
+ }else{
+ JsonNode *pNode = 0;
+ if( idxNum==3 ){
+ const char *zErr = 0;
+ zRoot = (const char*)sqlite3_value_text(argv[1]);
+ if( zRoot==0 ) return SQLITE_OK;
+ n = sqlite3_value_bytes(argv[1]);
+ p->zRoot = sqlite3_malloc64( n+1 );
+ if( p->zRoot==0 ) return SQLITE_NOMEM;
+ memcpy(p->zRoot, zRoot, (size_t)n+1);
+ if( zRoot[0]!='$' ){
+ zErr = zRoot;
+ }else{
+ pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);
+ }
+ if( zErr ){
+ sqlite3_free(cur->pVtab->zErrMsg);
+ cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
+ jsonEachCursorReset(p);
+ return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
+ }else if( pNode==0 ){
+ return SQLITE_OK;
+ }
+ }else{
+ pNode = p->sParse.aNode;
+ }
+ p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
+ p->eType = pNode->eType;
+ if( p->eType>=JSON_ARRAY ){
+ pNode->u.iKey = 0;
+ p->iEnd = p->i + pNode->n + 1;
+ if( p->bRecursive ){
+ p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
+ if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
+ p->i--;
+ }
+ }else{
+ p->i++;
+ }
+ }else{
+ p->iEnd = p->i+1;
+ }
+ }
+ return SQLITE_OK;
+}
+
+/* The methods of the json_each virtual table */
+static sqlite3_module jsonEachModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ jsonEachConnect, /* xConnect */
+ jsonEachBestIndex, /* xBestIndex */
+ jsonEachDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ jsonEachOpenEach, /* xOpen - open a cursor */
+ jsonEachClose, /* xClose - close a cursor */
+ jsonEachFilter, /* xFilter - configure scan constraints */
+ jsonEachNext, /* xNext - advance a cursor */
+ jsonEachEof, /* xEof - check for end of scan */
+ jsonEachColumn, /* xColumn - read data */
+ jsonEachRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0 /* xRollbackTo */
+};
+
+/* The methods of the json_tree virtual table. */
+static sqlite3_module jsonTreeModule = {
+ 0, /* iVersion */
+ 0, /* xCreate */
+ jsonEachConnect, /* xConnect */
+ jsonEachBestIndex, /* xBestIndex */
+ jsonEachDisconnect, /* xDisconnect */
+ 0, /* xDestroy */
+ jsonEachOpenTree, /* xOpen - open a cursor */
+ jsonEachClose, /* xClose - close a cursor */
+ jsonEachFilter, /* xFilter - configure scan constraints */
+ jsonEachNext, /* xNext - advance a cursor */
+ jsonEachEof, /* xEof - check for end of scan */
+ jsonEachColumn, /* xColumn - read data */
+ jsonEachRowid, /* xRowid - read data */
+ 0, /* xUpdate */
+ 0, /* xBegin */
+ 0, /* xSync */
+ 0, /* xCommit */
+ 0, /* xRollback */
+ 0, /* xFindMethod */
+ 0, /* xRename */
+ 0, /* xSavepoint */
+ 0, /* xRelease */
+ 0 /* xRollbackTo */
+};
+#endif /* SQLITE_OMIT_VIRTUALTABLE */
+
+/****************************************************************************
+** The following routines are the only publically visible identifiers in this
+** file. Call the following routines in order to register the various SQL
+** functions and the virtual table implemented by this file.
+****************************************************************************/
+
+SQLITE_PRIVATE int sqlite3Json1Init(sqlite3 *db){
+ int rc = SQLITE_OK;
+ unsigned int i;
+ static const struct {
+ const char *zName;
+ int nArg;
+ int flag;
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**);
+ } aFunc[] = {
+ { "json", 1, 0, jsonRemoveFunc },
+ { "json_array", -1, 0, jsonArrayFunc },
+ { "json_array_length", 1, 0, jsonArrayLengthFunc },
+ { "json_array_length", 2, 0, jsonArrayLengthFunc },
+ { "json_extract", -1, 0, jsonExtractFunc },
+ { "json_insert", -1, 0, jsonSetFunc },
+ { "json_object", -1, 0, jsonObjectFunc },
+ { "json_quote", 1, 0, jsonQuoteFunc },
+ { "json_remove", -1, 0, jsonRemoveFunc },
+ { "json_replace", -1, 0, jsonReplaceFunc },
+ { "json_set", -1, 1, jsonSetFunc },
+ { "json_type", 1, 0, jsonTypeFunc },
+ { "json_type", 2, 0, jsonTypeFunc },
+ { "json_valid", 1, 0, jsonValidFunc },
+
+#if SQLITE_DEBUG
+ /* DEBUG and TESTING functions */
+ { "json_parse", 1, 0, jsonParseFunc },
+ { "json_test1", 1, 0, jsonTest1Func },
+#endif
+ };
+ static const struct {
+ const char *zName;
+ int nArg;
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**);
+ void (*xFinal)(sqlite3_context*);
+ } aAgg[] = {
+ { "json_group_array", 1, jsonArrayStep, jsonArrayFinal },
+ { "json_group_object", 2, jsonObjectStep, jsonObjectFinal },
+ };
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ static const struct {
+ const char *zName;
+ sqlite3_module *pModule;
+ } aMod[] = {
+ { "json_each", &jsonEachModule },
+ { "json_tree", &jsonTreeModule },
+ };
+#endif
+ for(i=0; i<sizeof(aFunc)/sizeof(aFunc[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_function(db, aFunc[i].zName, aFunc[i].nArg,
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC,
+ (void*)&aFunc[i].flag,
+ aFunc[i].xFunc, 0, 0);
+ }
+ for(i=0; i<sizeof(aAgg)/sizeof(aAgg[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_function(db, aAgg[i].zName, aAgg[i].nArg,
+ SQLITE_UTF8 | SQLITE_DETERMINISTIC, 0,
+ 0, aAgg[i].xStep, aAgg[i].xFinal);
+ }
+#ifndef SQLITE_OMIT_VIRTUALTABLE
+ for(i=0; i<sizeof(aMod)/sizeof(aMod[0]) && rc==SQLITE_OK; i++){
+ rc = sqlite3_create_module(db, aMod[i].zName, aMod[i].pModule, 0);
+ }
+#endif
+ return rc;
+}
+
+
+#ifndef SQLITE_CORE
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_json_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return sqlite3Json1Init(db);
+}
+#endif
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_JSON1) */
+
+/************** End of json1.c ***********************************************/
+/************** Begin file fts5.c ********************************************/
+
+
+#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5)
+
+#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
+# define NDEBUG 1
+#endif
+#if defined(NDEBUG) && defined(SQLITE_DEBUG)
+# undef NDEBUG
+#endif
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** FTS5 may be extended with:
+**
+** * custom tokenizers, and
+** * custom auxiliary functions.
+*/
+
+
+#ifndef _FTS5_H
+#define _FTS5_H
+
+/* #include "sqlite3.h" */
+
+#if 0
+extern "C" {
+#endif
+
+/*************************************************************************
+** CUSTOM AUXILIARY FUNCTIONS
+**
+** Virtual table implementations may overload SQL functions by implementing
+** the sqlite3_module.xFindFunction() method.
+*/
+
+typedef struct Fts5ExtensionApi Fts5ExtensionApi;
+typedef struct Fts5Context Fts5Context;
+typedef struct Fts5PhraseIter Fts5PhraseIter;
+
+typedef void (*fts5_extension_function)(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+);
+
+struct Fts5PhraseIter {
+ const unsigned char *a;
+ const unsigned char *b;
+};
+
+/*
+** EXTENSION API FUNCTIONS
+**
+** xUserData(pFts):
+** Return a copy of the context pointer the extension function was
+** registered with.
+**
+** xColumnTotalSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the FTS5 table. Or, if iCol is
+** non-negative but less than the number of columns in the table, return
+** the total number of tokens in column iCol, considering all rows in
+** the FTS5 table.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** xColumnCount(pFts):
+** Return the number of columns in the table.
+**
+** xColumnSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the current row. Or, if iCol is
+** non-negative but less than the number of columns in the table, set
+** *pnToken to the number of tokens in column iCol of the current row.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** This function may be quite inefficient if used with an FTS5 table
+** created with the "columnsize=0" option.
+**
+** xColumnText:
+** This function attempts to retrieve the text of column iCol of the
+** current document. If successful, (*pz) is set to point to a buffer
+** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
+** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
+** if an error occurs, an SQLite error code is returned and the final values
+** of (*pz) and (*pn) are undefined.
+**
+** xPhraseCount:
+** Returns the number of phrases in the current query expression.
+**
+** xPhraseSize:
+** Returns the number of tokens in phrase iPhrase of the query. Phrases
+** are numbered starting from zero.
+**
+** xInstCount:
+** Set *pnInst to the total number of occurrences of all phrases within
+** the query within the current row. Return SQLITE_OK if successful, or
+** an error code (i.e. SQLITE_NOMEM) if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always returns 0.
+**
+** xInst:
+** Query for the details of phrase match iIdx within the current row.
+** Phrase matches are numbered starting from zero, so the iIdx argument
+** should be greater than or equal to zero and smaller than the value
+** output by xInstCount().
+**
+** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** to the column in which it occurs and *piOff the token offset of the
+** first token of the phrase. The exception is if the table was created
+** with the offsets=0 option specified. In this case *piOff is always
+** set to -1.
+**
+** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
+** if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
+**
+** xRowid:
+** Returns the rowid of the current row.
+**
+** xTokenize:
+** Tokenize text using the tokenizer belonging to the FTS5 table.
+**
+** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback):
+** This API function is used to query the FTS table for phrase iPhrase
+** of the current query. Specifically, a query equivalent to:
+**
+** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
+**
+** with $p set to a phrase equivalent to the phrase iPhrase of the
+** current query is executed. Any column filter that applies to
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
+** function may be used to access the properties of each matched row.
+** Invoking Api.xUserData() returns a copy of the pointer passed as
+** the third argument to pUserData.
+**
+** If the callback function returns any value other than SQLITE_OK, the
+** query is abandoned and the xQueryPhrase function returns immediately.
+** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
+** Otherwise, the error code is propagated upwards.
+**
+** If the query runs to completion without incident, SQLITE_OK is returned.
+** Or, if some error occurs before the query completes or is aborted by
+** the callback, an SQLite error code is returned.
+**
+**
+** xSetAuxdata(pFts5, pAux, xDelete)
+**
+** Save the pointer passed as the second argument as the extension functions
+** "auxiliary data". The pointer may then be retrieved by the current or any
+** future invocation of the same fts5 extension function made as part of
+** of the same MATCH query using the xGetAuxdata() API.
+**
+** Each extension function is allocated a single auxiliary data slot for
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
+** single auxiliary data context.
+**
+** If there is already an auxiliary data pointer when this function is
+** invoked, then it is replaced by the new pointer. If an xDelete callback
+** was specified along with the original pointer, it is invoked at this
+** point.
+**
+** The xDelete callback, if one is specified, is also invoked on the
+** auxiliary data pointer after the FTS5 query has finished.
+**
+** If an error (e.g. an OOM condition) occurs within this function, an
+** the auxiliary data is set to NULL and an error code returned. If the
+** xDelete parameter was not NULL, it is invoked on the auxiliary data
+** pointer before returning.
+**
+**
+** xGetAuxdata(pFts5, bClear)
+**
+** Returns the current auxiliary data pointer for the fts5 extension
+** function. See the xSetAuxdata() method for details.
+**
+** If the bClear argument is non-zero, then the auxiliary data is cleared
+** (set to NULL) before this function returns. In this case the xDelete,
+** if any, is not invoked.
+**
+**
+** xRowCount(pFts5, pnRow)
+**
+** This function is used to retrieve the total number of rows in the table.
+** In other words, the same value that would be returned by:
+**
+** SELECT count(*) FROM ftstable;
+**
+** xPhraseFirst()
+** This function is used, along with type Fts5PhraseIter and the xPhraseNext
+** method, to iterate through all instances of a single query phrase within
+** the current row. This is the same information as is accessible via the
+** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
+** to use, this API may be faster under some circumstances. To iterate
+** through instances of phrase iPhrase, use the following code:
+**
+** Fts5PhraseIter iter;
+** int iCol, iOff;
+** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
+** iCol>=0;
+** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
+** ){
+** // An instance of phrase iPhrase at offset iOff of column iCol
+** }
+**
+** The Fts5PhraseIter structure is defined above. Applications should not
+** modify this structure directly - it should only be used as shown above
+** with the xPhraseFirst() and xPhraseNext() API methods (and by
+** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always iterates
+** through an empty set (all calls to xPhraseFirst() set iCol to -1).
+**
+** xPhraseNext()
+** See xPhraseFirst above.
+**
+** xPhraseFirstColumn()
+** This function and xPhraseNextColumn() are similar to the xPhraseFirst()
+** and xPhraseNext() APIs described above. The difference is that instead
+** of iterating through all instances of a phrase in the current row, these
+** APIs are used to iterate through the set of columns in the current row
+** that contain one or more instances of a specified phrase. For example:
+**
+** Fts5PhraseIter iter;
+** int iCol;
+** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
+** iCol>=0;
+** pApi->xPhraseNextColumn(pFts, &iter, &iCol)
+** ){
+** // Column iCol contains at least one instance of phrase iPhrase
+** }
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
+** xPhraseFirstColumn() set iCol to -1).
+**
+** The information accessed using this API and its companion
+** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
+** (or xInst/xInstCount). The chief advantage of this API is that it is
+** significantly more efficient than those alternatives when used with
+** "detail=column" tables.
+**
+** xPhraseNextColumn()
+** See xPhraseFirstColumn above.
+*/
+struct Fts5ExtensionApi {
+ int iVersion; /* Currently always set to 3 */
+
+ void *(*xUserData)(Fts5Context*);
+
+ int (*xColumnCount)(Fts5Context*);
+ int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
+ int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
+
+ int (*xTokenize)(Fts5Context*,
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+ );
+
+ int (*xPhraseCount)(Fts5Context*);
+ int (*xPhraseSize)(Fts5Context*, int iPhrase);
+
+ int (*xInstCount)(Fts5Context*, int *pnInst);
+ int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
+
+ sqlite3_int64 (*xRowid)(Fts5Context*);
+ int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
+ int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
+
+ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
+ int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
+ );
+ int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
+ void *(*xGetAuxdata)(Fts5Context*, int bClear);
+
+ int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
+ void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
+
+ int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
+ void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+};
+
+/*
+** CUSTOM AUXILIARY FUNCTIONS
+*************************************************************************/
+
+/*************************************************************************
+** CUSTOM TOKENIZERS
+**
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
+** following structure. All structure methods must be defined, setting
+** any member of the fts5_tokenizer struct to NULL leads to undefined
+** behaviour. The structure methods are expected to function as follows:
+**
+** xCreate:
+** This function is used to allocate and initialize a tokenizer instance.
+** A tokenizer instance is required to actually tokenize text.
+**
+** The first argument passed to this function is a copy of the (void*)
+** pointer provided by the application when the fts5_tokenizer object
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** The second and third arguments are an array of nul-terminated strings
+** containing the tokenizer arguments, if any, specified following the
+** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
+** to create the FTS5 table.
+**
+** The final argument is an output variable. If successful, (*ppOut)
+** should be set to point to the new tokenizer handle and SQLITE_OK
+** returned. If an error occurs, some value other than SQLITE_OK should
+** be returned. In this case, fts5 assumes that the final value of *ppOut
+** is undefined.
+**
+** xDelete:
+** This function is invoked to delete a tokenizer handle previously
+** allocated using xCreate(). Fts5 guarantees that this function will
+** be invoked exactly once for each successful call to xCreate().
+**
+** xTokenize:
+** This function is expected to tokenize the nText byte string indicated
+** by argument pText. pText may or may not be nul-terminated. The first
+** argument passed to this function is a pointer to an Fts5Tokenizer object
+** returned by an earlier call to xCreate().
+**
+** The second argument indicates the reason that FTS5 is requesting
+** tokenization of the supplied text. This is always one of the following
+** four values:
+**
+** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
+** or removed from the FTS table. The tokenizer is being invoked to
+** determine the set of tokens to add to (or delete from) the
+** FTS index.
+**
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
+** a bareword or quoted string specified as part of the query.
+**
+** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
+** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
+** followed by a "*" character, indicating that the last token
+** returned by the tokenizer will be treated as a token prefix.
+**
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** satisfy an fts5_api.xTokenize() request made by an auxiliary
+** function. Or an fts5_api.xColumnSize() request made by the same
+** on a columnsize=0 database.
+** </ul>
+**
+** For each token in the input string, the supplied callback xToken() must
+** be invoked. The first argument to it should be a copy of the pointer
+** passed as the second argument to xTokenize(). The third and fourth
+** arguments are a pointer to a buffer containing the token text, and the
+** size of the token in bytes. The 4th and 5th arguments are the byte offsets
+** of the first byte of and first byte immediately following the text from
+** which the token is derived within the input.
+**
+** The second argument passed to the xToken() callback ("tflags") should
+** normally be set to 0. The exception is if the tokenizer supports
+** synonyms. In this case see the discussion below for details.
+**
+** FTS5 assumes the xToken() callback is invoked for each token in the
+** order that they occur within the input text.
+**
+** If an xToken() callback returns any value other than SQLITE_OK, then
+** the tokenization should be abandoned and the xTokenize() method should
+** immediately return a copy of the xToken() return value. Or, if the
+** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally,
+** if an error occurs with the xTokenize() implementation itself, it
+** may abandon the tokenization and return any error code other than
+** SQLITE_OK or SQLITE_DONE.
+**
+** SYNONYM SUPPORT
+**
+** Custom tokenizers may also support synonyms. Consider a case in which a
+** user wishes to query for a phrase such as "first place". Using the
+** built-in tokenizers, the FTS5 query 'first + place' will match instances
+** of "first place" within the document set, but not alternative forms
+** such as "1st place". In some applications, it would be better to match
+** all instances of "first place" or "1st place" regardless of which form
+** the user specified in the MATCH query text.
+**
+** There are several ways to approach this in FTS5:
+**
+** <ol><li> By mapping all synonyms to a single token. In this case, the
+** In the above example, this means that the tokenizer returns the
+** same token for inputs "first" and "1st". Say that token is in
+** fact "first", so that when the user inserts the document "I won
+** 1st place" entries are added to the index for tokens "i", "won",
+** "first" and "place". If the user then queries for '1st + place',
+** the tokenizer substitutes "first" for "1st" and the query works
+** as expected.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** In this case, when tokenizing query text, the tokenizer may
+** provide multiple synonyms for a single term within the document.
+** FTS5 then queries the index for each synonym individually. For
+** example, faced with the query:
+**
+** <codeblock>
+** ... MATCH 'first place'</codeblock>
+**
+** the tokenizer offers both "1st" and "first" as synonyms for the
+** first token in the MATCH query and FTS5 effectively runs a query
+** similar to:
+**
+** <codeblock>
+** ... MATCH '(first OR 1st) place'</codeblock>
+**
+** except that, for the purposes of auxiliary functions, the query
+** still appears to contain just two phrases - "(first OR 1st)"
+** being treated as a single phrase.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** Using this method, when tokenizing document text, the tokenizer
+** provides multiple synonyms for each token. So that when a
+** document such as "I won first place" is tokenized, entries are
+** added to the FTS index for "i", "won", "first", "1st" and
+** "place".
+**
+** This way, even if the tokenizer does not provide synonyms
+** when tokenizing query text (it should not - to do would be
+** inefficient), it doesn't matter if the user queries for
+** 'first + place' or '1st + place', as there are entires in the
+** FTS index corresponding to both forms of the first token.
+** </ol>
+**
+** Whether it is parsing document or query text, any call to xToken that
+** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
+** is considered to supply a synonym for the previous token. For example,
+** when parsing the document "I won first place", a tokenizer that supports
+** synonyms would call xToken() 5 times, as follows:
+**
+** <codeblock>
+** xToken(pCtx, 0, "i", 1, 0, 1);
+** xToken(pCtx, 0, "won", 3, 2, 5);
+** xToken(pCtx, 0, "first", 5, 6, 11);
+** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11);
+** xToken(pCtx, 0, "place", 5, 12, 17);
+**</codeblock>
+**
+** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
+** xToken() is called. Multiple synonyms may be specified for a single token
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** There is no limit to the number of synonyms that may be provided for a
+** single token.
+**
+** In many cases, method (1) above is the best approach. It does not add
+** extra data to the FTS index or require FTS5 to query for multiple terms,
+** so it is efficient in terms of disk space and query speed. However, it
+** does not support prefix queries very well. If, as suggested above, the
+** token "first" is subsituted for "1st" by the tokenizer, then the query:
+**
+** <codeblock>
+** ... MATCH '1s*'</codeblock>
+**
+** will not match documents that contain the token "1st" (as the tokenizer
+** will probably not map "1s" to any prefix of "first").
+**
+** For full prefix support, method (3) may be preferred. In this case,
+** because the index contains entries for both "first" and "1st", prefix
+** queries such as 'fi*' or '1s*' will match correctly. However, because
+** extra entries are added to the FTS index, this method uses more space
+** within the database.
+**
+** Method (2) offers a midpoint between (1) and (3). Using this method,
+** a query such as '1s*' will match documents that contain the literal
+** token "1st", but not "first" (assuming the tokenizer is not able to
+** provide synonyms for prefixes). However, a non-prefix query like '1st'
+** will match against "1st" and "first". This method does not require
+** extra disk space, as no extra entries are added to the FTS index.
+** On the other hand, it may require more CPU cycles to run MATCH queries,
+** as separate queries of the FTS index are required for each synonym.
+**
+** When using methods (2) or (3), it is important that the tokenizer only
+** provide synonyms when tokenizing document text (method (2)) or query
+** text (method (3)), not both. Doing so will not cause any errors, but is
+** inefficient.
+*/
+typedef struct Fts5Tokenizer Fts5Tokenizer;
+typedef struct fts5_tokenizer fts5_tokenizer;
+struct fts5_tokenizer {
+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
+ void (*xDelete)(Fts5Tokenizer*);
+ int (*xTokenize)(Fts5Tokenizer*,
+ void *pCtx,
+ int flags, /* Mask of FTS5_TOKENIZE_* flags */
+ const char *pText, int nText,
+ int (*xToken)(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Byte offset of token within input text */
+ int iEnd /* Byte offset of end of token within input text */
+ )
+ );
+};
+
+/* Flags that may be passed as the third argument to xTokenize() */
+#define FTS5_TOKENIZE_QUERY 0x0001
+#define FTS5_TOKENIZE_PREFIX 0x0002
+#define FTS5_TOKENIZE_DOCUMENT 0x0004
+#define FTS5_TOKENIZE_AUX 0x0008
+
+/* Flags that may be passed by the tokenizer implementation back to FTS5
+** as the third argument to the supplied xToken callback. */
+#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
+
+/*
+** END OF CUSTOM TOKENIZERS
+*************************************************************************/
+
+/*************************************************************************
+** FTS5 EXTENSION REGISTRATION API
+*/
+typedef struct fts5_api fts5_api;
+struct fts5_api {
+ int iVersion; /* Currently always set to 2 */
+
+ /* Create a new tokenizer */
+ int (*xCreateTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_tokenizer *pTokenizer,
+ void (*xDestroy)(void*)
+ );
+
+ /* Find an existing tokenizer */
+ int (*xFindTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void **ppContext,
+ fts5_tokenizer *pTokenizer
+ );
+
+ /* Create a new auxiliary function */
+ int (*xCreateFunction)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_extension_function xFunction,
+ void (*xDestroy)(void*)
+ );
+};
+
+/*
+** END OF REGISTRATION API
+*************************************************************************/
+
+#if 0
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _FTS5_H */
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+*/
+#ifndef _FTS5INT_H
+#define _FTS5INT_H
+
+/* #include "fts5.h" */
+/* #include "sqlite3ext.h" */
+SQLITE_EXTENSION_INIT1
+
+/* #include <string.h> */
+/* #include <assert.h> */
+
+#ifndef SQLITE_AMALGAMATION
+
+typedef unsigned char u8;
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef short i16;
+typedef sqlite3_int64 i64;
+typedef sqlite3_uint64 u64;
+
+#define ArraySize(x) ((int)(sizeof(x) / sizeof(x[0])))
+
+#define testcase(x)
+#define ALWAYS(x) 1
+#define NEVER(x) 0
+
+#define MIN(x,y) (((x) < (y)) ? (x) : (y))
+#define MAX(x,y) (((x) > (y)) ? (x) : (y))
+
+/*
+** Constants for the largest and smallest possible 64-bit signed integers.
+*/
+# define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
+# define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
+
+#endif
+
+/* Truncate very long tokens to this many bytes. Hard limit is
+** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
+** field that occurs at the start of each leaf page (see fts5_index.c). */
+#define FTS5_MAX_TOKEN_SIZE 32768
+
+/*
+** Maximum number of prefix indexes on single FTS5 table. This must be
+** less than 32. If it is set to anything large than that, an #error
+** directive in fts5_index.c will cause the build to fail.
+*/
+#define FTS5_MAX_PREFIX_INDEXES 31
+
+#define FTS5_DEFAULT_NEARDIST 10
+#define FTS5_DEFAULT_RANK "bm25"
+
+/* Name of rank and rowid columns */
+#define FTS5_RANK_NAME "rank"
+#define FTS5_ROWID_NAME "rowid"
+
+#ifdef SQLITE_DEBUG
+# define FTS5_CORRUPT sqlite3Fts5Corrupt()
+static int sqlite3Fts5Corrupt(void);
+#else
+# define FTS5_CORRUPT SQLITE_CORRUPT_VTAB
+#endif
+
+/*
+** The assert_nc() macro is similar to the assert() macro, except that it
+** is used for assert() conditions that are true only if it can be
+** guranteed that the database is not corrupt.
+*/
+#ifdef SQLITE_DEBUG
+SQLITE_API extern int sqlite3_fts5_may_be_corrupt;
+# define assert_nc(x) assert(sqlite3_fts5_may_be_corrupt || (x))
+#else
+# define assert_nc(x) assert(x)
+#endif
+
+/* Mark a function parameter as unused, to suppress nuisance compiler
+** warnings. */
+#ifndef UNUSED_PARAM
+# define UNUSED_PARAM(X) (void)(X)
+#endif
+
+#ifndef UNUSED_PARAM2
+# define UNUSED_PARAM2(X, Y) (void)(X), (void)(Y)
+#endif
+
+typedef struct Fts5Global Fts5Global;
+typedef struct Fts5Colset Fts5Colset;
+
+/* If a NEAR() clump or phrase may only match a specific set of columns,
+** then an object of the following type is used to record the set of columns.
+** Each entry in the aiCol[] array is a column that may be matched.
+**
+** This object is used by fts5_expr.c and fts5_index.c.
+*/
+struct Fts5Colset {
+ int nCol;
+ int aiCol[1];
+};
+
+
+
+/**************************************************************************
+** Interface to code in fts5_config.c. fts5_config.c contains contains code
+** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
+*/
+
+typedef struct Fts5Config Fts5Config;
+
+/*
+** An instance of the following structure encodes all information that can
+** be gleaned from the CREATE VIRTUAL TABLE statement.
+**
+** And all information loaded from the %_config table.
+**
+** nAutomerge:
+** The minimum number of segments that an auto-merge operation should
+** attempt to merge together. A value of 1 sets the object to use the
+** compile time default. Zero disables auto-merge altogether.
+**
+** zContent:
+**
+** zContentRowid:
+** The value of the content_rowid= option, if one was specified. Or
+** the string "rowid" otherwise. This text is not quoted - if it is
+** used as part of an SQL statement it needs to be quoted appropriately.
+**
+** zContentExprlist:
+**
+** pzErrmsg:
+** This exists in order to allow the fts5_index.c module to return a
+** decent error message if it encounters a file-format version it does
+** not understand.
+**
+** bColumnsize:
+** True if the %_docsize table is created.
+**
+** bPrefixIndex:
+** This is only used for debugging. If set to false, any prefix indexes
+** are ignored. This value is configured using:
+**
+** INSERT INTO tbl(tbl, rank) VALUES('prefix-index', $bPrefixIndex);
+**
+*/
+struct Fts5Config {
+ sqlite3 *db; /* Database handle */
+ char *zDb; /* Database holding FTS index (e.g. "main") */
+ char *zName; /* Name of FTS index */
+ int nCol; /* Number of columns */
+ char **azCol; /* Column names */
+ u8 *abUnindexed; /* True for unindexed columns */
+ int nPrefix; /* Number of prefix indexes */
+ int *aPrefix; /* Sizes in bytes of nPrefix prefix indexes */
+ int eContent; /* An FTS5_CONTENT value */
+ char *zContent; /* content table */
+ char *zContentRowid; /* "content_rowid=" option value */
+ int bColumnsize; /* "columnsize=" option value (dflt==1) */
+ int eDetail; /* FTS5_DETAIL_XXX value */
+ char *zContentExprlist;
+ Fts5Tokenizer *pTok;
+ fts5_tokenizer *pTokApi;
+
+ /* Values loaded from the %_config table */
+ int iCookie; /* Incremented when %_config is modified */
+ int pgsz; /* Approximate page size used in %_data */
+ int nAutomerge; /* 'automerge' setting */
+ int nCrisisMerge; /* Maximum allowed segments per level */
+ int nUsermerge; /* 'usermerge' setting */
+ int nHashSize; /* Bytes of memory for in-memory hash */
+ char *zRank; /* Name of rank function */
+ char *zRankArgs; /* Arguments to rank function */
+
+ /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
+ char **pzErrmsg;
+
+#ifdef SQLITE_DEBUG
+ int bPrefixIndex; /* True to use prefix-indexes */
+#endif
+};
+
+/* Current expected value of %_config table 'version' field */
+#define FTS5_CURRENT_VERSION 4
+
+#define FTS5_CONTENT_NORMAL 0
+#define FTS5_CONTENT_NONE 1
+#define FTS5_CONTENT_EXTERNAL 2
+
+#define FTS5_DETAIL_FULL 0
+#define FTS5_DETAIL_NONE 1
+#define FTS5_DETAIL_COLUMNS 2
+
+
+
+static int sqlite3Fts5ConfigParse(
+ Fts5Global*, sqlite3*, int, const char **, Fts5Config**, char**
+);
+static void sqlite3Fts5ConfigFree(Fts5Config*);
+
+static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig);
+
+static int sqlite3Fts5Tokenize(
+ Fts5Config *pConfig, /* FTS5 Configuration object */
+ int flags, /* FTS5_TOKENIZE_* flags */
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+);
+
+static void sqlite3Fts5Dequote(char *z);
+
+/* Load the contents of the %_config table */
+static int sqlite3Fts5ConfigLoad(Fts5Config*, int);
+
+/* Set the value of a single config attribute */
+static int sqlite3Fts5ConfigSetValue(Fts5Config*, const char*, sqlite3_value*, int*);
+
+static int sqlite3Fts5ConfigParseRank(const char*, char**, char**);
+
+/*
+** End of interface to code in fts5_config.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_buffer.c.
+*/
+
+/*
+** Buffer object for the incremental building of string data.
+*/
+typedef struct Fts5Buffer Fts5Buffer;
+struct Fts5Buffer {
+ u8 *p;
+ int n;
+ int nSpace;
+};
+
+static int sqlite3Fts5BufferSize(int*, Fts5Buffer*, u32);
+static void sqlite3Fts5BufferAppendVarint(int*, Fts5Buffer*, i64);
+static void sqlite3Fts5BufferAppendBlob(int*, Fts5Buffer*, u32, const u8*);
+static void sqlite3Fts5BufferAppendString(int *, Fts5Buffer*, const char*);
+static void sqlite3Fts5BufferFree(Fts5Buffer*);
+static void sqlite3Fts5BufferZero(Fts5Buffer*);
+static void sqlite3Fts5BufferSet(int*, Fts5Buffer*, int, const u8*);
+static void sqlite3Fts5BufferAppendPrintf(int *, Fts5Buffer*, char *zFmt, ...);
+
+static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...);
+
+#define fts5BufferZero(x) sqlite3Fts5BufferZero(x)
+#define fts5BufferAppendVarint(a,b,c) sqlite3Fts5BufferAppendVarint(a,b,c)
+#define fts5BufferFree(a) sqlite3Fts5BufferFree(a)
+#define fts5BufferAppendBlob(a,b,c,d) sqlite3Fts5BufferAppendBlob(a,b,c,d)
+#define fts5BufferSet(a,b,c,d) sqlite3Fts5BufferSet(a,b,c,d)
+
+#define fts5BufferGrow(pRc,pBuf,nn) ( \
+ (u32)((pBuf)->n) + (u32)(nn) <= (u32)((pBuf)->nSpace) ? 0 : \
+ sqlite3Fts5BufferSize((pRc),(pBuf),(nn)+(pBuf)->n) \
+)
+
+/* Write and decode big-endian 32-bit integer values */
+static void sqlite3Fts5Put32(u8*, int);
+static int sqlite3Fts5Get32(const u8*);
+
+#define FTS5_POS2COLUMN(iPos) (int)(iPos >> 32)
+#define FTS5_POS2OFFSET(iPos) (int)(iPos & 0xFFFFFFFF)
+
+typedef struct Fts5PoslistReader Fts5PoslistReader;
+struct Fts5PoslistReader {
+ /* Variables used only by sqlite3Fts5PoslistIterXXX() functions. */
+ const u8 *a; /* Position list to iterate through */
+ int n; /* Size of buffer at a[] in bytes */
+ int i; /* Current offset in a[] */
+
+ u8 bFlag; /* For client use (any custom purpose) */
+
+ /* Output variables */
+ u8 bEof; /* Set to true at EOF */
+ i64 iPos; /* (iCol<<32) + iPos */
+};
+static int sqlite3Fts5PoslistReaderInit(
+ const u8 *a, int n, /* Poslist buffer to iterate through */
+ Fts5PoslistReader *pIter /* Iterator object to initialize */
+);
+static int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader*);
+
+typedef struct Fts5PoslistWriter Fts5PoslistWriter;
+struct Fts5PoslistWriter {
+ i64 iPrev;
+};
+static int sqlite3Fts5PoslistWriterAppend(Fts5Buffer*, Fts5PoslistWriter*, i64);
+static void sqlite3Fts5PoslistSafeAppend(Fts5Buffer*, i64*, i64);
+
+static int sqlite3Fts5PoslistNext64(
+ const u8 *a, int n, /* Buffer containing poslist */
+ int *pi, /* IN/OUT: Offset within a[] */
+ i64 *piOff /* IN/OUT: Current offset */
+);
+
+/* Malloc utility */
+static void *sqlite3Fts5MallocZero(int *pRc, int nByte);
+static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn);
+
+/* Character set tests (like isspace(), isalpha() etc.) */
+static int sqlite3Fts5IsBareword(char t);
+
+
+/* Bucket of terms object used by the integrity-check in offsets=0 mode. */
+typedef struct Fts5Termset Fts5Termset;
+static int sqlite3Fts5TermsetNew(Fts5Termset**);
+static int sqlite3Fts5TermsetAdd(Fts5Termset*, int, const char*, int, int *pbPresent);
+static void sqlite3Fts5TermsetFree(Fts5Termset*);
+
+/*
+** End of interface to code in fts5_buffer.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_index.c. fts5_index.c contains contains code
+** to access the data stored in the %_data table.
+*/
+
+typedef struct Fts5Index Fts5Index;
+typedef struct Fts5IndexIter Fts5IndexIter;
+
+struct Fts5IndexIter {
+ i64 iRowid;
+ const u8 *pData;
+ int nData;
+ u8 bEof;
+};
+
+#define sqlite3Fts5IterEof(x) ((x)->bEof)
+
+/*
+** Values used as part of the flags argument passed to IndexQuery().
+*/
+#define FTS5INDEX_QUERY_PREFIX 0x0001 /* Prefix query */
+#define FTS5INDEX_QUERY_DESC 0x0002 /* Docs in descending rowid order */
+#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004 /* Do not use prefix index */
+#define FTS5INDEX_QUERY_SCAN 0x0008 /* Scan query (fts5vocab) */
+
+/* The following are used internally by the fts5_index.c module. They are
+** defined here only to make it easier to avoid clashes with the flags
+** above. */
+#define FTS5INDEX_QUERY_SKIPEMPTY 0x0010
+#define FTS5INDEX_QUERY_NOOUTPUT 0x0020
+
+/*
+** Create/destroy an Fts5Index object.
+*/
+static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**);
+static int sqlite3Fts5IndexClose(Fts5Index *p);
+
+/*
+** Return a simple checksum value based on the arguments.
+*/
+static u64 sqlite3Fts5IndexEntryCksum(
+ i64 iRowid,
+ int iCol,
+ int iPos,
+ int iIdx,
+ const char *pTerm,
+ int nTerm
+);
+
+/*
+** Argument p points to a buffer containing utf-8 text that is n bytes in
+** size. Return the number of bytes in the nChar character prefix of the
+** buffer, or 0 if there are less than nChar characters in total.
+*/
+static int sqlite3Fts5IndexCharlenToBytelen(
+ const char *p,
+ int nByte,
+ int nChar
+);
+
+/*
+** Open a new iterator to iterate though all rowids that match the
+** specified token or token prefix.
+*/
+static int sqlite3Fts5IndexQuery(
+ Fts5Index *p, /* FTS index to query */
+ const char *pToken, int nToken, /* Token (or prefix) to query for */
+ int flags, /* Mask of FTS5INDEX_QUERY_X flags */
+ Fts5Colset *pColset, /* Match these columns only */
+ Fts5IndexIter **ppIter /* OUT: New iterator object */
+);
+
+/*
+** The various operations on open token or token prefix iterators opened
+** using sqlite3Fts5IndexQuery().
+*/
+static int sqlite3Fts5IterNext(Fts5IndexIter*);
+static int sqlite3Fts5IterNextFrom(Fts5IndexIter*, i64 iMatch);
+
+/*
+** Close an iterator opened by sqlite3Fts5IndexQuery().
+*/
+static void sqlite3Fts5IterClose(Fts5IndexIter*);
+
+/*
+** This interface is used by the fts5vocab module.
+*/
+static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*);
+static int sqlite3Fts5IterNextScan(Fts5IndexIter*);
+
+
+/*
+** Insert or remove data to or from the index. Each time a document is
+** added to or removed from the index, this function is called one or more
+** times.
+**
+** For an insert, it must be called once for each token in the new document.
+** If the operation is a delete, it must be called (at least) once for each
+** unique token in the document with an iCol value less than zero. The iPos
+** argument is ignored for a delete.
+*/
+static int sqlite3Fts5IndexWrite(
+ Fts5Index *p, /* Index to write to */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+);
+
+/*
+** Indicate that subsequent calls to sqlite3Fts5IndexWrite() pertain to
+** document iDocid.
+*/
+static int sqlite3Fts5IndexBeginWrite(
+ Fts5Index *p, /* Index to write to */
+ int bDelete, /* True if current operation is a delete */
+ i64 iDocid /* Docid to add or remove data from */
+);
+
+/*
+** Flush any data stored in the in-memory hash tables to the database.
+** If the bCommit flag is true, also close any open blob handles.
+*/
+static int sqlite3Fts5IndexSync(Fts5Index *p, int bCommit);
+
+/*
+** Discard any data stored in the in-memory hash tables. Do not write it
+** to the database. Additionally, assume that the contents of the %_data
+** table may have changed on disk. So any in-memory caches of %_data
+** records must be invalidated.
+*/
+static int sqlite3Fts5IndexRollback(Fts5Index *p);
+
+/*
+** Get or set the "averages" values.
+*/
+static int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize);
+static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8*, int);
+
+/*
+** Functions called by the storage module as part of integrity-check.
+*/
+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index*, u64 cksum);
+
+/*
+** Called during virtual module initialization to register UDF
+** fts5_decode() with SQLite
+*/
+static int sqlite3Fts5IndexInit(sqlite3*);
+
+static int sqlite3Fts5IndexSetCookie(Fts5Index*, int);
+
+/*
+** Return the total number of entries read from the %_data table by
+** this connection since it was created.
+*/
+static int sqlite3Fts5IndexReads(Fts5Index *p);
+
+static int sqlite3Fts5IndexReinit(Fts5Index *p);
+static int sqlite3Fts5IndexOptimize(Fts5Index *p);
+static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
+static int sqlite3Fts5IndexReset(Fts5Index *p);
+
+static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);
+
+/*
+** End of interface to code in fts5_index.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_varint.c.
+*/
+static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v);
+static int sqlite3Fts5GetVarintLen(u32 iVal);
+static u8 sqlite3Fts5GetVarint(const unsigned char*, u64*);
+static int sqlite3Fts5PutVarint(unsigned char *p, u64 v);
+
+#define fts5GetVarint32(a,b) sqlite3Fts5GetVarint32(a,(u32*)&b)
+#define fts5GetVarint sqlite3Fts5GetVarint
+
+#define fts5FastGetVarint32(a, iOff, nVal) { \
+ nVal = (a)[iOff++]; \
+ if( nVal & 0x80 ){ \
+ iOff--; \
+ iOff += fts5GetVarint32(&(a)[iOff], nVal); \
+ } \
+}
+
+
+/*
+** End of interface to code in fts5_varint.c.
+**************************************************************************/
+
+
+/**************************************************************************
+** Interface to code in fts5.c.
+*/
+
+static int sqlite3Fts5GetTokenizer(
+ Fts5Global*,
+ const char **azArg,
+ int nArg,
+ Fts5Tokenizer**,
+ fts5_tokenizer**,
+ char **pzErr
+);
+
+static Fts5Index *sqlite3Fts5IndexFromCsrid(Fts5Global*, i64, Fts5Config **);
+
+/*
+** End of interface to code in fts5.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_hash.c.
+*/
+typedef struct Fts5Hash Fts5Hash;
+
+/*
+** Create a hash table, free a hash table.
+*/
+static int sqlite3Fts5HashNew(Fts5Config*, Fts5Hash**, int *pnSize);
+static void sqlite3Fts5HashFree(Fts5Hash*);
+
+static int sqlite3Fts5HashWrite(
+ Fts5Hash*,
+ i64 iRowid, /* Rowid for this entry */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ char bByte,
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+);
+
+/*
+** Empty (but do not delete) a hash table.
+*/
+static void sqlite3Fts5HashClear(Fts5Hash*);
+
+static int sqlite3Fts5HashQuery(
+ Fts5Hash*, /* Hash table to query */
+ const char *pTerm, int nTerm, /* Query term */
+ const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ int *pnDoclist /* OUT: Size of doclist in bytes */
+);
+
+static int sqlite3Fts5HashScanInit(
+ Fts5Hash*, /* Hash table to query */
+ const char *pTerm, int nTerm /* Query prefix */
+);
+static void sqlite3Fts5HashScanNext(Fts5Hash*);
+static int sqlite3Fts5HashScanEof(Fts5Hash*);
+static void sqlite3Fts5HashScanEntry(Fts5Hash *,
+ const char **pzTerm, /* OUT: term (nul-terminated) */
+ const u8 **ppDoclist, /* OUT: pointer to doclist */
+ int *pnDoclist /* OUT: size of doclist in bytes */
+);
+
+
+/*
+** End of interface to code in fts5_hash.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_storage.c. fts5_storage.c contains contains
+** code to access the data stored in the %_content and %_docsize tables.
+*/
+
+#define FTS5_STMT_SCAN_ASC 0 /* SELECT rowid, * FROM ... ORDER BY 1 ASC */
+#define FTS5_STMT_SCAN_DESC 1 /* SELECT rowid, * FROM ... ORDER BY 1 DESC */
+#define FTS5_STMT_LOOKUP 2 /* SELECT rowid, * FROM ... WHERE rowid=? */
+
+typedef struct Fts5Storage Fts5Storage;
+
+static int sqlite3Fts5StorageOpen(Fts5Config*, Fts5Index*, int, Fts5Storage**, char**);
+static int sqlite3Fts5StorageClose(Fts5Storage *p);
+static int sqlite3Fts5StorageRename(Fts5Storage*, const char *zName);
+
+static int sqlite3Fts5DropAll(Fts5Config*);
+static int sqlite3Fts5CreateTable(Fts5Config*, const char*, const char*, int, char **);
+
+static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64, sqlite3_value**);
+static int sqlite3Fts5StorageContentInsert(Fts5Storage *p, sqlite3_value**, i64*);
+static int sqlite3Fts5StorageIndexInsert(Fts5Storage *p, sqlite3_value**, i64);
+
+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p);
+
+static int sqlite3Fts5StorageStmt(Fts5Storage *p, int eStmt, sqlite3_stmt**, char**);
+static void sqlite3Fts5StorageStmtRelease(Fts5Storage *p, int eStmt, sqlite3_stmt*);
+
+static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol);
+static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnAvg);
+static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow);
+
+static int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit);
+static int sqlite3Fts5StorageRollback(Fts5Storage *p);
+
+static int sqlite3Fts5StorageConfigValue(
+ Fts5Storage *p, const char*, sqlite3_value*, int
+);
+
+static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p);
+static int sqlite3Fts5StorageRebuild(Fts5Storage *p);
+static int sqlite3Fts5StorageOptimize(Fts5Storage *p);
+static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge);
+static int sqlite3Fts5StorageReset(Fts5Storage *p);
+
+/*
+** End of interface to code in fts5_storage.c.
+**************************************************************************/
+
+
+/**************************************************************************
+** Interface to code in fts5_expr.c.
+*/
+typedef struct Fts5Expr Fts5Expr;
+typedef struct Fts5ExprNode Fts5ExprNode;
+typedef struct Fts5Parse Fts5Parse;
+typedef struct Fts5Token Fts5Token;
+typedef struct Fts5ExprPhrase Fts5ExprPhrase;
+typedef struct Fts5ExprNearset Fts5ExprNearset;
+
+struct Fts5Token {
+ const char *p; /* Token text (not NULL terminated) */
+ int n; /* Size of buffer p in bytes */
+};
+
+/* Parse a MATCH expression. */
+static int sqlite3Fts5ExprNew(
+ Fts5Config *pConfig,
+ const char *zExpr,
+ Fts5Expr **ppNew,
+ char **pzErr
+);
+
+/*
+** for(rc = sqlite3Fts5ExprFirst(pExpr, pIdx, bDesc);
+** rc==SQLITE_OK && 0==sqlite3Fts5ExprEof(pExpr);
+** rc = sqlite3Fts5ExprNext(pExpr)
+** ){
+** // The document with rowid iRowid matches the expression!
+** i64 iRowid = sqlite3Fts5ExprRowid(pExpr);
+** }
+*/
+static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, int bDesc);
+static int sqlite3Fts5ExprNext(Fts5Expr*, i64 iMax);
+static int sqlite3Fts5ExprEof(Fts5Expr*);
+static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
+
+static void sqlite3Fts5ExprFree(Fts5Expr*);
+
+/* Called during startup to register a UDF with SQLite */
+static int sqlite3Fts5ExprInit(Fts5Global*, sqlite3*);
+
+static int sqlite3Fts5ExprPhraseCount(Fts5Expr*);
+static int sqlite3Fts5ExprPhraseSize(Fts5Expr*, int iPhrase);
+static int sqlite3Fts5ExprPoslist(Fts5Expr*, int, const u8 **);
+
+typedef struct Fts5PoslistPopulator Fts5PoslistPopulator;
+static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr*, int);
+static int sqlite3Fts5ExprPopulatePoslists(
+ Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int
+);
+static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64);
+
+static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);
+
+static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);
+
+/*******************************************
+** The fts5_expr.c API above this point is used by the other hand-written
+** C code in this module. The interfaces below this point are called by
+** the parser code in fts5parse.y. */
+
+static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...);
+
+static Fts5ExprNode *sqlite3Fts5ParseNode(
+ Fts5Parse *pParse,
+ int eType,
+ Fts5ExprNode *pLeft,
+ Fts5ExprNode *pRight,
+ Fts5ExprNearset *pNear
+);
+
+static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
+ Fts5Parse *pParse,
+ Fts5ExprNode *pLeft,
+ Fts5ExprNode *pRight
+);
+
+static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
+ Fts5Parse *pParse,
+ Fts5ExprPhrase *pPhrase,
+ Fts5Token *pToken,
+ int bPrefix
+);
+
+static Fts5ExprNearset *sqlite3Fts5ParseNearset(
+ Fts5Parse*,
+ Fts5ExprNearset*,
+ Fts5ExprPhrase*
+);
+
+static Fts5Colset *sqlite3Fts5ParseColset(
+ Fts5Parse*,
+ Fts5Colset*,
+ Fts5Token *
+);
+
+static void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase*);
+static void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset*);
+static void sqlite3Fts5ParseNodeFree(Fts5ExprNode*);
+
+static void sqlite3Fts5ParseSetDistance(Fts5Parse*, Fts5ExprNearset*, Fts5Token*);
+static void sqlite3Fts5ParseSetColset(Fts5Parse*, Fts5ExprNearset*, Fts5Colset*);
+static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse*, Fts5Colset*);
+static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p);
+static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token*);
+
+/*
+** End of interface to code in fts5_expr.c.
+**************************************************************************/
+
+
+
+/**************************************************************************
+** Interface to code in fts5_aux.c.
+*/
+
+static int sqlite3Fts5AuxInit(fts5_api*);
+/*
+** End of interface to code in fts5_aux.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_tokenizer.c.
+*/
+
+static int sqlite3Fts5TokenizerInit(fts5_api*);
+/*
+** End of interface to code in fts5_tokenizer.c.
+**************************************************************************/
+
+/**************************************************************************
+** Interface to code in fts5_vocab.c.
+*/
+
+static int sqlite3Fts5VocabInit(Fts5Global*, sqlite3*);
+
+/*
+** End of interface to code in fts5_vocab.c.
+**************************************************************************/
+
+
+/**************************************************************************
+** Interface to automatically generated code in fts5_unicode2.c.
+*/
+static int sqlite3Fts5UnicodeIsalnum(int c);
+static int sqlite3Fts5UnicodeIsdiacritic(int c);
+static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic);
+/*
+** End of interface to code in fts5_unicode2.c.
+**************************************************************************/
+
+#endif
+
+#define FTS5_OR 1
+#define FTS5_AND 2
+#define FTS5_NOT 3
+#define FTS5_TERM 4
+#define FTS5_COLON 5
+#define FTS5_LP 6
+#define FTS5_RP 7
+#define FTS5_MINUS 8
+#define FTS5_LCP 9
+#define FTS5_RCP 10
+#define FTS5_STRING 11
+#define FTS5_COMMA 12
+#define FTS5_PLUS 13
+#define FTS5_STAR 14
+
+/*
+** 2000-05-29
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+*************************************************************************
+** Driver template for the LEMON parser generator.
+**
+** The "lemon" program processes an LALR(1) input grammar file, then uses
+** this template to construct a parser. The "lemon" program inserts text
+** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
+** interstitial "-" characters) contained in this template is changed into
+** the value of the %name directive from the grammar. Otherwise, the content
+** of this template is copied straight through into the generate parser
+** source file.
+**
+** The following is the concatenation of all %include directives from the
+** input grammar file:
+*/
+/* #include <stdio.h> */
+/************ Begin %include sections from the grammar ************************/
+
+/* #include "fts5Int.h" */
+/* #include "fts5parse.h" */
+
+/*
+** Disable all error recovery processing in the parser push-down
+** automaton.
+*/
+#define fts5YYNOERRORRECOVERY 1
+
+/*
+** Make fts5yytestcase() the same as testcase()
+*/
+#define fts5yytestcase(X) testcase(X)
+
+/*
+** Indicate that sqlite3ParserFree() will never be called with a null
+** pointer.
+*/
+#define fts5YYPARSEFREENOTNULL 1
+
+/*
+** Alternative datatype for the argument to the malloc() routine passed
+** into sqlite3ParserAlloc(). The default is size_t.
+*/
+#define fts5YYMALLOCARGTYPE u64
+
+/**************** End of %include directives **********************************/
+/* These constants specify the various numeric values for terminal symbols
+** in a format understandable to "makeheaders". This section is blank unless
+** "lemon" is run with the "-m" command-line option.
+***************** Begin makeheaders token definitions *************************/
+/**************** End makeheaders token definitions ***************************/
+
+/* The next sections is a series of control #defines.
+** various aspects of the generated parser.
+** fts5YYCODETYPE is the data type used to store the integer codes
+** that represent terminal and non-terminal symbols.
+** "unsigned char" is used if there are fewer than
+** 256 symbols. Larger types otherwise.
+** fts5YYNOCODE is a number of type fts5YYCODETYPE that is not used for
+** any terminal or nonterminal symbol.
+** fts5YYFALLBACK If defined, this indicates that one or more tokens
+** (also known as: "terminal symbols") have fall-back
+** values which should be used if the original symbol
+** would not parse. This permits keywords to sometimes
+** be used as identifiers, for example.
+** fts5YYACTIONTYPE is the data type used for "action codes" - numbers
+** that indicate what to do in response to the next
+** token.
+** sqlite3Fts5ParserFTS5TOKENTYPE is the data type used for minor type for terminal
+** symbols. Background: A "minor type" is a semantic
+** value associated with a terminal or non-terminal
+** symbols. For example, for an "ID" terminal symbol,
+** the minor type might be the name of the identifier.
+** Each non-terminal can have a different minor type.
+** Terminal symbols all have the same minor type, though.
+** This macros defines the minor type for terminal
+** symbols.
+** fts5YYMINORTYPE is the data type used for all minor types.
+** This is typically a union of many types, one of
+** which is sqlite3Fts5ParserFTS5TOKENTYPE. The entry in the union
+** for terminal symbols is called "fts5yy0".
+** fts5YYSTACKDEPTH is the maximum depth of the parser's stack. If
+** zero the stack is dynamically sized using realloc()
+** sqlite3Fts5ParserARG_SDECL A static variable declaration for the %extra_argument
+** sqlite3Fts5ParserARG_PDECL A parameter declaration for the %extra_argument
+** sqlite3Fts5ParserARG_STORE Code to store %extra_argument into fts5yypParser
+** sqlite3Fts5ParserARG_FETCH Code to extract %extra_argument from fts5yypParser
+** fts5YYERRORSYMBOL is the code number of the error symbol. If not
+** defined, then do no error processing.
+** fts5YYNSTATE the combined number of states.
+** fts5YYNRULE the number of rules in the grammar
+** fts5YY_MAX_SHIFT Maximum value for shift actions
+** fts5YY_MIN_SHIFTREDUCE Minimum value for shift-reduce actions
+** fts5YY_MAX_SHIFTREDUCE Maximum value for shift-reduce actions
+** fts5YY_MIN_REDUCE Maximum value for reduce actions
+** fts5YY_ERROR_ACTION The fts5yy_action[] code for syntax error
+** fts5YY_ACCEPT_ACTION The fts5yy_action[] code for accept
+** fts5YY_NO_ACTION The fts5yy_action[] code for no-op
+*/
+#ifndef INTERFACE
+# define INTERFACE 1
+#endif
+/************* Begin control #defines *****************************************/
+#define fts5YYCODETYPE unsigned char
+#define fts5YYNOCODE 28
+#define fts5YYACTIONTYPE unsigned char
+#define sqlite3Fts5ParserFTS5TOKENTYPE Fts5Token
+typedef union {
+ int fts5yyinit;
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yy0;
+ int fts5yy4;
+ Fts5Colset* fts5yy11;
+ Fts5ExprNode* fts5yy24;
+ Fts5ExprNearset* fts5yy46;
+ Fts5ExprPhrase* fts5yy53;
+} fts5YYMINORTYPE;
+#ifndef fts5YYSTACKDEPTH
+#define fts5YYSTACKDEPTH 100
+#endif
+#define sqlite3Fts5ParserARG_SDECL Fts5Parse *pParse;
+#define sqlite3Fts5ParserARG_PDECL ,Fts5Parse *pParse
+#define sqlite3Fts5ParserARG_FETCH Fts5Parse *pParse = fts5yypParser->pParse
+#define sqlite3Fts5ParserARG_STORE fts5yypParser->pParse = pParse
+#define fts5YYNSTATE 29
+#define fts5YYNRULE 26
+#define fts5YY_MAX_SHIFT 28
+#define fts5YY_MIN_SHIFTREDUCE 45
+#define fts5YY_MAX_SHIFTREDUCE 70
+#define fts5YY_MIN_REDUCE 71
+#define fts5YY_MAX_REDUCE 96
+#define fts5YY_ERROR_ACTION 97
+#define fts5YY_ACCEPT_ACTION 98
+#define fts5YY_NO_ACTION 99
+/************* End control #defines *******************************************/
+
+/* Define the fts5yytestcase() macro to be a no-op if is not already defined
+** otherwise.
+**
+** Applications can choose to define fts5yytestcase() in the %include section
+** to a macro that can assist in verifying code coverage. For production
+** code the fts5yytestcase() macro should be turned off. But it is useful
+** for testing.
+*/
+#ifndef fts5yytestcase
+# define fts5yytestcase(X)
+#endif
+
+
+/* Next are the tables used to determine what action to take based on the
+** current state and lookahead token. These tables are used to implement
+** functions that take a state number and lookahead value and return an
+** action integer.
+**
+** Suppose the action integer is N. Then the action is determined as
+** follows
+**
+** 0 <= N <= fts5YY_MAX_SHIFT Shift N. That is, push the lookahead
+** token onto the stack and goto state N.
+**
+** N between fts5YY_MIN_SHIFTREDUCE Shift to an arbitrary state then
+** and fts5YY_MAX_SHIFTREDUCE reduce by rule N-fts5YY_MIN_SHIFTREDUCE.
+**
+** N between fts5YY_MIN_REDUCE Reduce by rule N-fts5YY_MIN_REDUCE
+** and fts5YY_MAX_REDUCE
+**
+** N == fts5YY_ERROR_ACTION A syntax error has occurred.
+**
+** N == fts5YY_ACCEPT_ACTION The parser accepts its input.
+**
+** N == fts5YY_NO_ACTION No such action. Denotes unused
+** slots in the fts5yy_action[] table.
+**
+** The action table is constructed as a single large table named fts5yy_action[].
+** Given state S and lookahead X, the action is computed as either:
+**
+** (A) N = fts5yy_action[ fts5yy_shift_ofst[S] + X ]
+** (B) N = fts5yy_default[S]
+**
+** The (A) formula is preferred. The B formula is used instead if:
+** (1) The fts5yy_shift_ofst[S]+X value is out of range, or
+** (2) fts5yy_lookahead[fts5yy_shift_ofst[S]+X] is not equal to X, or
+** (3) fts5yy_shift_ofst[S] equal fts5YY_SHIFT_USE_DFLT.
+** (Implementation note: fts5YY_SHIFT_USE_DFLT is chosen so that
+** fts5YY_SHIFT_USE_DFLT+X will be out of range for all possible lookaheads X.
+** Hence only tests (1) and (2) need to be evaluated.)
+**
+** The formulas above are for computing the action when the lookahead is
+** a terminal symbol. If the lookahead is a non-terminal (as occurs after
+** a reduce action) then the fts5yy_reduce_ofst[] array is used in place of
+** the fts5yy_shift_ofst[] array and fts5YY_REDUCE_USE_DFLT is used in place of
+** fts5YY_SHIFT_USE_DFLT.
+**
+** The following are the tables generated in this section:
+**
+** fts5yy_action[] A single table containing all actions.
+** fts5yy_lookahead[] A table containing the lookahead for each entry in
+** fts5yy_action. Used to detect hash collisions.
+** fts5yy_shift_ofst[] For each state, the offset into fts5yy_action for
+** shifting terminals.
+** fts5yy_reduce_ofst[] For each state, the offset into fts5yy_action for
+** shifting non-terminals after a reduce.
+** fts5yy_default[] Default action for each state.
+**
+*********** Begin parsing tables **********************************************/
+#define fts5YY_ACTTAB_COUNT (85)
+static const fts5YYACTIONTYPE fts5yy_action[] = {
+ /* 0 */ 98, 16, 51, 5, 53, 27, 83, 7, 26, 15,
+ /* 10 */ 51, 5, 53, 27, 13, 69, 26, 48, 51, 5,
+ /* 20 */ 53, 27, 19, 11, 26, 9, 20, 51, 5, 53,
+ /* 30 */ 27, 13, 22, 26, 28, 51, 5, 53, 27, 68,
+ /* 40 */ 1, 26, 19, 11, 17, 9, 52, 10, 53, 27,
+ /* 50 */ 23, 24, 26, 54, 3, 4, 2, 26, 6, 21,
+ /* 60 */ 49, 71, 3, 4, 2, 7, 56, 59, 55, 59,
+ /* 70 */ 4, 2, 12, 69, 58, 60, 18, 67, 62, 69,
+ /* 80 */ 25, 66, 8, 14, 2,
+};
+static const fts5YYCODETYPE fts5yy_lookahead[] = {
+ /* 0 */ 16, 17, 18, 19, 20, 21, 5, 6, 24, 17,
+ /* 10 */ 18, 19, 20, 21, 11, 14, 24, 17, 18, 19,
+ /* 20 */ 20, 21, 8, 9, 24, 11, 17, 18, 19, 20,
+ /* 30 */ 21, 11, 12, 24, 17, 18, 19, 20, 21, 26,
+ /* 40 */ 6, 24, 8, 9, 22, 11, 18, 11, 20, 21,
+ /* 50 */ 24, 25, 24, 20, 1, 2, 3, 24, 23, 24,
+ /* 60 */ 7, 0, 1, 2, 3, 6, 10, 11, 10, 11,
+ /* 70 */ 2, 3, 9, 14, 11, 11, 22, 26, 7, 14,
+ /* 80 */ 13, 11, 5, 11, 3,
+};
+#define fts5YY_SHIFT_USE_DFLT (85)
+#define fts5YY_SHIFT_COUNT (28)
+#define fts5YY_SHIFT_MIN (0)
+#define fts5YY_SHIFT_MAX (81)
+static const unsigned char fts5yy_shift_ofst[] = {
+ /* 0 */ 34, 34, 34, 34, 34, 14, 20, 3, 36, 1,
+ /* 10 */ 59, 64, 64, 65, 65, 53, 61, 56, 58, 63,
+ /* 20 */ 68, 67, 70, 67, 71, 72, 67, 77, 81,
+};
+#define fts5YY_REDUCE_USE_DFLT (-17)
+#define fts5YY_REDUCE_COUNT (14)
+#define fts5YY_REDUCE_MIN (-16)
+#define fts5YY_REDUCE_MAX (54)
+static const signed char fts5yy_reduce_ofst[] = {
+ /* 0 */ -16, -8, 0, 9, 17, 28, 26, 35, 33, 13,
+ /* 10 */ 13, 22, 54, 13, 51,
+};
+static const fts5YYACTIONTYPE fts5yy_default[] = {
+ /* 0 */ 97, 97, 97, 97, 97, 76, 91, 97, 97, 96,
+ /* 10 */ 96, 97, 97, 96, 96, 97, 97, 97, 97, 97,
+ /* 20 */ 73, 89, 97, 90, 97, 97, 87, 97, 72,
+};
+/********** End of lemon-generated parsing tables *****************************/
+
+/* The next table maps tokens (terminal symbols) into fallback tokens.
+** If a construct like the following:
+**
+** %fallback ID X Y Z.
+**
+** appears in the grammar, then ID becomes a fallback token for X, Y,
+** and Z. Whenever one of the tokens X, Y, or Z is input to the parser
+** but it does not parse, the type of the token is changed to ID and
+** the parse is retried before an error is thrown.
+**
+** This feature can be used, for example, to cause some keywords in a language
+** to revert to identifiers if they keyword does not apply in the context where
+** it appears.
+*/
+#ifdef fts5YYFALLBACK
+static const fts5YYCODETYPE fts5yyFallback[] = {
+};
+#endif /* fts5YYFALLBACK */
+
+/* The following structure represents a single element of the
+** parser's stack. Information stored includes:
+**
+** + The state number for the parser at this level of the stack.
+**
+** + The value of the token stored at this level of the stack.
+** (In other words, the "major" token.)
+**
+** + The semantic value stored at this level of the stack. This is
+** the information used by the action routines in the grammar.
+** It is sometimes called the "minor" token.
+**
+** After the "shift" half of a SHIFTREDUCE action, the stateno field
+** actually contains the reduce action for the second half of the
+** SHIFTREDUCE.
+*/
+struct fts5yyStackEntry {
+ fts5YYACTIONTYPE stateno; /* The state-number, or reduce action in SHIFTREDUCE */
+ fts5YYCODETYPE major; /* The major token value. This is the code
+ ** number for the token at this stack level */
+ fts5YYMINORTYPE minor; /* The user-supplied minor token value. This
+ ** is the value of the token */
+};
+typedef struct fts5yyStackEntry fts5yyStackEntry;
+
+/* The state of the parser is completely contained in an instance of
+** the following structure */
+struct fts5yyParser {
+ fts5yyStackEntry *fts5yytos; /* Pointer to top element of the stack */
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ int fts5yyhwm; /* High-water mark of the stack */
+#endif
+#ifndef fts5YYNOERRORRECOVERY
+ int fts5yyerrcnt; /* Shifts left before out of the error */
+#endif
+ sqlite3Fts5ParserARG_SDECL /* A place to hold %extra_argument */
+#if fts5YYSTACKDEPTH<=0
+ int fts5yystksz; /* Current side of the stack */
+ fts5yyStackEntry *fts5yystack; /* The parser's stack */
+ fts5yyStackEntry fts5yystk0; /* First stack entry */
+#else
+ fts5yyStackEntry fts5yystack[fts5YYSTACKDEPTH]; /* The parser's stack */
+#endif
+};
+typedef struct fts5yyParser fts5yyParser;
+
+#ifndef NDEBUG
+/* #include <stdio.h> */
+static FILE *fts5yyTraceFILE = 0;
+static char *fts5yyTracePrompt = 0;
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/*
+** Turn parser tracing on by giving a stream to which to write the trace
+** and a prompt to preface each trace message. Tracing is turned off
+** by making either argument NULL
+**
+** Inputs:
+** <ul>
+** <li> A FILE* to which trace output should be written.
+** If NULL, then tracing is turned off.
+** <li> A prefix string written at the beginning of every
+** line of trace output. If NULL, then tracing is
+** turned off.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+static void sqlite3Fts5ParserTrace(FILE *TraceFILE, char *zTracePrompt){
+ fts5yyTraceFILE = TraceFILE;
+ fts5yyTracePrompt = zTracePrompt;
+ if( fts5yyTraceFILE==0 ) fts5yyTracePrompt = 0;
+ else if( fts5yyTracePrompt==0 ) fts5yyTraceFILE = 0;
+}
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing shifts, the names of all terminals and nonterminals
+** are required. The following table supplies these names */
+static const char *const fts5yyTokenName[] = {
+ "$", "OR", "AND", "NOT",
+ "TERM", "COLON", "LP", "RP",
+ "MINUS", "LCP", "RCP", "STRING",
+ "COMMA", "PLUS", "STAR", "error",
+ "input", "expr", "cnearset", "exprlist",
+ "nearset", "colset", "colsetlist", "nearphrases",
+ "phrase", "neardist_opt", "star_opt",
+};
+#endif /* NDEBUG */
+
+#ifndef NDEBUG
+/* For tracing reduce actions, the names of all rules are required.
+*/
+static const char *const fts5yyRuleName[] = {
+ /* 0 */ "input ::= expr",
+ /* 1 */ "expr ::= expr AND expr",
+ /* 2 */ "expr ::= expr OR expr",
+ /* 3 */ "expr ::= expr NOT expr",
+ /* 4 */ "expr ::= LP expr RP",
+ /* 5 */ "expr ::= exprlist",
+ /* 6 */ "exprlist ::= cnearset",
+ /* 7 */ "exprlist ::= exprlist cnearset",
+ /* 8 */ "cnearset ::= nearset",
+ /* 9 */ "cnearset ::= colset COLON nearset",
+ /* 10 */ "colset ::= MINUS LCP colsetlist RCP",
+ /* 11 */ "colset ::= LCP colsetlist RCP",
+ /* 12 */ "colset ::= STRING",
+ /* 13 */ "colset ::= MINUS STRING",
+ /* 14 */ "colsetlist ::= colsetlist STRING",
+ /* 15 */ "colsetlist ::= STRING",
+ /* 16 */ "nearset ::= phrase",
+ /* 17 */ "nearset ::= STRING LP nearphrases neardist_opt RP",
+ /* 18 */ "nearphrases ::= phrase",
+ /* 19 */ "nearphrases ::= nearphrases phrase",
+ /* 20 */ "neardist_opt ::=",
+ /* 21 */ "neardist_opt ::= COMMA STRING",
+ /* 22 */ "phrase ::= phrase PLUS STRING star_opt",
+ /* 23 */ "phrase ::= STRING star_opt",
+ /* 24 */ "star_opt ::= STAR",
+ /* 25 */ "star_opt ::=",
+};
+#endif /* NDEBUG */
+
+
+#if fts5YYSTACKDEPTH<=0
+/*
+** Try to increase the size of the parser stack. Return the number
+** of errors. Return 0 on success.
+*/
+static int fts5yyGrowStack(fts5yyParser *p){
+ int newSize;
+ int idx;
+ fts5yyStackEntry *pNew;
+
+ newSize = p->fts5yystksz*2 + 100;
+ idx = p->fts5yytos ? (int)(p->fts5yytos - p->fts5yystack) : 0;
+ if( p->fts5yystack==&p->fts5yystk0 ){
+ pNew = malloc(newSize*sizeof(pNew[0]));
+ if( pNew ) pNew[0] = p->fts5yystk0;
+ }else{
+ pNew = realloc(p->fts5yystack, newSize*sizeof(pNew[0]));
+ }
+ if( pNew ){
+ p->fts5yystack = pNew;
+ p->fts5yytos = &p->fts5yystack[idx];
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sStack grows from %d to %d entries.\n",
+ fts5yyTracePrompt, p->fts5yystksz, newSize);
+ }
+#endif
+ p->fts5yystksz = newSize;
+ }
+ return pNew==0;
+}
+#endif
+
+/* Datatype of the argument to the memory allocated passed as the
+** second argument to sqlite3Fts5ParserAlloc() below. This can be changed by
+** putting an appropriate #define in the %include section of the input
+** grammar.
+*/
+#ifndef fts5YYMALLOCARGTYPE
+# define fts5YYMALLOCARGTYPE size_t
+#endif
+
+/*
+** This function allocates a new parser.
+** The only argument is a pointer to a function which works like
+** malloc.
+**
+** Inputs:
+** A pointer to the function used to allocate memory.
+**
+** Outputs:
+** A pointer to a parser. This pointer is used in subsequent calls
+** to sqlite3Fts5Parser and sqlite3Fts5ParserFree.
+*/
+static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(fts5YYMALLOCARGTYPE)){
+ fts5yyParser *pParser;
+ pParser = (fts5yyParser*)(*mallocProc)( (fts5YYMALLOCARGTYPE)sizeof(fts5yyParser) );
+ if( pParser ){
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ pParser->fts5yyhwm = 0;
+#endif
+#if fts5YYSTACKDEPTH<=0
+ pParser->fts5yytos = NULL;
+ pParser->fts5yystack = NULL;
+ pParser->fts5yystksz = 0;
+ if( fts5yyGrowStack(pParser) ){
+ pParser->fts5yystack = &pParser->fts5yystk0;
+ pParser->fts5yystksz = 1;
+ }
+#endif
+#ifndef fts5YYNOERRORRECOVERY
+ pParser->fts5yyerrcnt = -1;
+#endif
+ pParser->fts5yytos = pParser->fts5yystack;
+ pParser->fts5yystack[0].stateno = 0;
+ pParser->fts5yystack[0].major = 0;
+ }
+ return pParser;
+}
+
+/* The following function deletes the "minor type" or semantic value
+** associated with a symbol. The symbol can be either a terminal
+** or nonterminal. "fts5yymajor" is the symbol code, and "fts5yypminor" is
+** a pointer to the value to be deleted. The code used to do the
+** deletions is derived from the %destructor and/or %token_destructor
+** directives of the input grammar.
+*/
+static void fts5yy_destructor(
+ fts5yyParser *fts5yypParser, /* The parser */
+ fts5YYCODETYPE fts5yymajor, /* Type code for object to destroy */
+ fts5YYMINORTYPE *fts5yypminor /* The object to be destroyed */
+){
+ sqlite3Fts5ParserARG_FETCH;
+ switch( fts5yymajor ){
+ /* Here is inserted the actions which take place when a
+ ** terminal or non-terminal is destroyed. This can happen
+ ** when the symbol is popped from the stack during a
+ ** reduce or during error processing or when a parser is
+ ** being destroyed before it is finished parsing.
+ **
+ ** Note: during a reduce, the only symbols destroyed are those
+ ** which appear on the RHS of the rule, but which are *not* used
+ ** inside the C code.
+ */
+/********* Begin destructor definitions ***************************************/
+ case 16: /* input */
+{
+ (void)pParse;
+}
+ break;
+ case 17: /* expr */
+ case 18: /* cnearset */
+ case 19: /* exprlist */
+{
+ sqlite3Fts5ParseNodeFree((fts5yypminor->fts5yy24));
+}
+ break;
+ case 20: /* nearset */
+ case 23: /* nearphrases */
+{
+ sqlite3Fts5ParseNearsetFree((fts5yypminor->fts5yy46));
+}
+ break;
+ case 21: /* colset */
+ case 22: /* colsetlist */
+{
+ sqlite3_free((fts5yypminor->fts5yy11));
+}
+ break;
+ case 24: /* phrase */
+{
+ sqlite3Fts5ParsePhraseFree((fts5yypminor->fts5yy53));
+}
+ break;
+/********* End destructor definitions *****************************************/
+ default: break; /* If no destructor action specified: do nothing */
+ }
+}
+
+/*
+** Pop the parser's stack once.
+**
+** If there is a destructor routine associated with the token which
+** is popped from the stack, then call it.
+*/
+static void fts5yy_pop_parser_stack(fts5yyParser *pParser){
+ fts5yyStackEntry *fts5yytos;
+ assert( pParser->fts5yytos!=0 );
+ assert( pParser->fts5yytos > pParser->fts5yystack );
+ fts5yytos = pParser->fts5yytos--;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sPopping %s\n",
+ fts5yyTracePrompt,
+ fts5yyTokenName[fts5yytos->major]);
+ }
+#endif
+ fts5yy_destructor(pParser, fts5yytos->major, &fts5yytos->minor);
+}
+
+/*
+** Deallocate and destroy a parser. Destructors are called for
+** all stack elements before shutting the parser down.
+**
+** If the fts5YYPARSEFREENEVERNULL macro exists (for example because it
+** is defined in a %include section of the input grammar) then it is
+** assumed that the input pointer is never NULL.
+*/
+static void sqlite3Fts5ParserFree(
+ void *p, /* The parser to be deleted */
+ void (*freeProc)(void*) /* Function used to reclaim memory */
+){
+ fts5yyParser *pParser = (fts5yyParser*)p;
+#ifndef fts5YYPARSEFREENEVERNULL
+ if( pParser==0 ) return;
+#endif
+ while( pParser->fts5yytos>pParser->fts5yystack ) fts5yy_pop_parser_stack(pParser);
+#if fts5YYSTACKDEPTH<=0
+ if( pParser->fts5yystack!=&pParser->fts5yystk0 ) free(pParser->fts5yystack);
+#endif
+ (*freeProc)((void*)pParser);
+}
+
+/*
+** Return the peak depth of the stack for a parser.
+*/
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+static int sqlite3Fts5ParserStackPeak(void *p){
+ fts5yyParser *pParser = (fts5yyParser*)p;
+ return pParser->fts5yyhwm;
+}
+#endif
+
+/*
+** Find the appropriate action for a parser given the terminal
+** look-ahead token iLookAhead.
+*/
+static unsigned int fts5yy_find_shift_action(
+ fts5yyParser *pParser, /* The parser */
+ fts5YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+ int stateno = pParser->fts5yytos->stateno;
+
+ if( stateno>=fts5YY_MIN_REDUCE ) return stateno;
+ assert( stateno <= fts5YY_SHIFT_COUNT );
+ do{
+ i = fts5yy_shift_ofst[stateno];
+ assert( iLookAhead!=fts5YYNOCODE );
+ i += iLookAhead;
+ if( i<0 || i>=fts5YY_ACTTAB_COUNT || fts5yy_lookahead[i]!=iLookAhead ){
+#ifdef fts5YYFALLBACK
+ fts5YYCODETYPE iFallback; /* Fallback token */
+ if( iLookAhead<sizeof(fts5yyFallback)/sizeof(fts5yyFallback[0])
+ && (iFallback = fts5yyFallback[iLookAhead])!=0 ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE, "%sFALLBACK %s => %s\n",
+ fts5yyTracePrompt, fts5yyTokenName[iLookAhead], fts5yyTokenName[iFallback]);
+ }
+#endif
+ assert( fts5yyFallback[iFallback]==0 ); /* Fallback loop must terminate */
+ iLookAhead = iFallback;
+ continue;
+ }
+#endif
+#ifdef fts5YYWILDCARD
+ {
+ int j = i - iLookAhead + fts5YYWILDCARD;
+ if(
+#if fts5YY_SHIFT_MIN+fts5YYWILDCARD<0
+ j>=0 &&
+#endif
+#if fts5YY_SHIFT_MAX+fts5YYWILDCARD>=fts5YY_ACTTAB_COUNT
+ j<fts5YY_ACTTAB_COUNT &&
+#endif
+ fts5yy_lookahead[j]==fts5YYWILDCARD && iLookAhead>0
+ ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE, "%sWILDCARD %s => %s\n",
+ fts5yyTracePrompt, fts5yyTokenName[iLookAhead],
+ fts5yyTokenName[fts5YYWILDCARD]);
+ }
+#endif /* NDEBUG */
+ return fts5yy_action[j];
+ }
+ }
+#endif /* fts5YYWILDCARD */
+ return fts5yy_default[stateno];
+ }else{
+ return fts5yy_action[i];
+ }
+ }while(1);
+}
+
+/*
+** Find the appropriate action for a parser given the non-terminal
+** look-ahead token iLookAhead.
+*/
+static int fts5yy_find_reduce_action(
+ int stateno, /* Current state number */
+ fts5YYCODETYPE iLookAhead /* The look-ahead token */
+){
+ int i;
+#ifdef fts5YYERRORSYMBOL
+ if( stateno>fts5YY_REDUCE_COUNT ){
+ return fts5yy_default[stateno];
+ }
+#else
+ assert( stateno<=fts5YY_REDUCE_COUNT );
+#endif
+ i = fts5yy_reduce_ofst[stateno];
+ assert( i!=fts5YY_REDUCE_USE_DFLT );
+ assert( iLookAhead!=fts5YYNOCODE );
+ i += iLookAhead;
+#ifdef fts5YYERRORSYMBOL
+ if( i<0 || i>=fts5YY_ACTTAB_COUNT || fts5yy_lookahead[i]!=iLookAhead ){
+ return fts5yy_default[stateno];
+ }
+#else
+ assert( i>=0 && i<fts5YY_ACTTAB_COUNT );
+ assert( fts5yy_lookahead[i]==iLookAhead );
+#endif
+ return fts5yy_action[i];
+}
+
+/*
+** The following routine is called if the stack overflows.
+*/
+static void fts5yyStackOverflow(fts5yyParser *fts5yypParser){
+ sqlite3Fts5ParserARG_FETCH;
+ fts5yypParser->fts5yytos--;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sStack Overflow!\n",fts5yyTracePrompt);
+ }
+#endif
+ while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ) fts5yy_pop_parser_stack(fts5yypParser);
+ /* Here code is inserted which will execute if the parser
+ ** stack every overflows */
+/******** Begin %stack_overflow code ******************************************/
+
+ sqlite3Fts5ParseError(pParse, "fts5: parser stack overflow");
+/******** End %stack_overflow code ********************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument var */
+}
+
+/*
+** Print tracing information for a SHIFT action
+*/
+#ifndef NDEBUG
+static void fts5yyTraceShift(fts5yyParser *fts5yypParser, int fts5yyNewState){
+ if( fts5yyTraceFILE ){
+ if( fts5yyNewState<fts5YYNSTATE ){
+ fprintf(fts5yyTraceFILE,"%sShift '%s', go to state %d\n",
+ fts5yyTracePrompt,fts5yyTokenName[fts5yypParser->fts5yytos->major],
+ fts5yyNewState);
+ }else{
+ fprintf(fts5yyTraceFILE,"%sShift '%s'\n",
+ fts5yyTracePrompt,fts5yyTokenName[fts5yypParser->fts5yytos->major]);
+ }
+ }
+}
+#else
+# define fts5yyTraceShift(X,Y)
+#endif
+
+/*
+** Perform a shift action.
+*/
+static void fts5yy_shift(
+ fts5yyParser *fts5yypParser, /* The parser to be shifted */
+ int fts5yyNewState, /* The new state to shift in */
+ int fts5yyMajor, /* The major token to shift in */
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yyMinor /* The minor token to shift in */
+){
+ fts5yyStackEntry *fts5yytos;
+ fts5yypParser->fts5yytos++;
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){
+ fts5yypParser->fts5yyhwm++;
+ assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack) );
+ }
+#endif
+#if fts5YYSTACKDEPTH>0
+ if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5YYSTACKDEPTH] ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+#else
+ if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz] ){
+ if( fts5yyGrowStack(fts5yypParser) ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+ }
+#endif
+ if( fts5yyNewState > fts5YY_MAX_SHIFT ){
+ fts5yyNewState += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE;
+ }
+ fts5yytos = fts5yypParser->fts5yytos;
+ fts5yytos->stateno = (fts5YYACTIONTYPE)fts5yyNewState;
+ fts5yytos->major = (fts5YYCODETYPE)fts5yyMajor;
+ fts5yytos->minor.fts5yy0 = fts5yyMinor;
+ fts5yyTraceShift(fts5yypParser, fts5yyNewState);
+}
+
+/* The following table contains information about every rule that
+** is used during the reduce.
+*/
+static const struct {
+ fts5YYCODETYPE lhs; /* Symbol on the left-hand side of the rule */
+ unsigned char nrhs; /* Number of right-hand side symbols in the rule */
+} fts5yyRuleInfo[] = {
+ { 16, 1 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 3 },
+ { 17, 1 },
+ { 19, 1 },
+ { 19, 2 },
+ { 18, 1 },
+ { 18, 3 },
+ { 21, 4 },
+ { 21, 3 },
+ { 21, 1 },
+ { 21, 2 },
+ { 22, 2 },
+ { 22, 1 },
+ { 20, 1 },
+ { 20, 5 },
+ { 23, 1 },
+ { 23, 2 },
+ { 25, 0 },
+ { 25, 2 },
+ { 24, 4 },
+ { 24, 2 },
+ { 26, 1 },
+ { 26, 0 },
+};
+
+static void fts5yy_accept(fts5yyParser*); /* Forward Declaration */
+
+/*
+** Perform a reduce action and the shift that must immediately
+** follow the reduce.
+*/
+static void fts5yy_reduce(
+ fts5yyParser *fts5yypParser, /* The parser */
+ unsigned int fts5yyruleno /* Number of the rule by which to reduce */
+){
+ int fts5yygoto; /* The next state */
+ int fts5yyact; /* The next action */
+ fts5yyStackEntry *fts5yymsp; /* The top of the parser's stack */
+ int fts5yysize; /* Amount to pop the stack */
+ sqlite3Fts5ParserARG_FETCH;
+ fts5yymsp = fts5yypParser->fts5yytos;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE && fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) ){
+ fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs;
+ fprintf(fts5yyTraceFILE, "%sReduce [%s], go to state %d.\n", fts5yyTracePrompt,
+ fts5yyRuleName[fts5yyruleno], fts5yymsp[-fts5yysize].stateno);
+ }
+#endif /* NDEBUG */
+
+ /* Check that the stack is large enough to grow by a single entry
+ ** if the RHS of the rule is empty. This ensures that there is room
+ ** enough on the stack to push the LHS value */
+ if( fts5yyRuleInfo[fts5yyruleno].nrhs==0 ){
+#ifdef fts5YYTRACKMAXSTACKDEPTH
+ if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){
+ fts5yypParser->fts5yyhwm++;
+ assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack));
+ }
+#endif
+#if fts5YYSTACKDEPTH>0
+ if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5YYSTACKDEPTH-1] ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+#else
+ if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
+ if( fts5yyGrowStack(fts5yypParser) ){
+ fts5yyStackOverflow(fts5yypParser);
+ return;
+ }
+ fts5yymsp = fts5yypParser->fts5yytos;
+ }
+#endif
+ }
+
+ switch( fts5yyruleno ){
+ /* Beginning here are the reduction cases. A typical example
+ ** follows:
+ ** case 0:
+ ** #line <lineno> <grammarfile>
+ ** { ... } // User supplied code
+ ** #line <lineno> <thisfile>
+ ** break;
+ */
+/********** Begin reduce actions **********************************************/
+ fts5YYMINORTYPE fts5yylhsminor;
+ case 0: /* input ::= expr */
+{ sqlite3Fts5ParseFinished(pParse, fts5yymsp[0].minor.fts5yy24); }
+ break;
+ case 1: /* expr ::= expr AND expr */
+{
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_AND, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0);
+}
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
+ break;
+ case 2: /* expr ::= expr OR expr */
+{
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_OR, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0);
+}
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
+ break;
+ case 3: /* expr ::= expr NOT expr */
+{
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_NOT, fts5yymsp[-2].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24, 0);
+}
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
+ break;
+ case 4: /* expr ::= LP expr RP */
+{fts5yymsp[-2].minor.fts5yy24 = fts5yymsp[-1].minor.fts5yy24;}
+ break;
+ case 5: /* expr ::= exprlist */
+ case 6: /* exprlist ::= cnearset */ fts5yytestcase(fts5yyruleno==6);
+{fts5yylhsminor.fts5yy24 = fts5yymsp[0].minor.fts5yy24;}
+ fts5yymsp[0].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
+ break;
+ case 7: /* exprlist ::= exprlist cnearset */
+{
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseImplicitAnd(pParse, fts5yymsp[-1].minor.fts5yy24, fts5yymsp[0].minor.fts5yy24);
+}
+ fts5yymsp[-1].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
+ break;
+ case 8: /* cnearset ::= nearset */
+{
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
+}
+ fts5yymsp[0].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
+ break;
+ case 9: /* cnearset ::= colset COLON nearset */
+{
+ sqlite3Fts5ParseSetColset(pParse, fts5yymsp[0].minor.fts5yy46, fts5yymsp[-2].minor.fts5yy11);
+ fts5yylhsminor.fts5yy24 = sqlite3Fts5ParseNode(pParse, FTS5_STRING, 0, 0, fts5yymsp[0].minor.fts5yy46);
+}
+ fts5yymsp[-2].minor.fts5yy24 = fts5yylhsminor.fts5yy24;
+ break;
+ case 10: /* colset ::= MINUS LCP colsetlist RCP */
+{
+ fts5yymsp[-3].minor.fts5yy11 = sqlite3Fts5ParseColsetInvert(pParse, fts5yymsp[-1].minor.fts5yy11);
+}
+ break;
+ case 11: /* colset ::= LCP colsetlist RCP */
+{ fts5yymsp[-2].minor.fts5yy11 = fts5yymsp[-1].minor.fts5yy11; }
+ break;
+ case 12: /* colset ::= STRING */
+{
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+}
+ fts5yymsp[0].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
+ break;
+ case 13: /* colset ::= MINUS STRING */
+{
+ fts5yymsp[-1].minor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+ fts5yymsp[-1].minor.fts5yy11 = sqlite3Fts5ParseColsetInvert(pParse, fts5yymsp[-1].minor.fts5yy11);
+}
+ break;
+ case 14: /* colsetlist ::= colsetlist STRING */
+{
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, fts5yymsp[-1].minor.fts5yy11, &fts5yymsp[0].minor.fts5yy0); }
+ fts5yymsp[-1].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
+ break;
+ case 15: /* colsetlist ::= STRING */
+{
+ fts5yylhsminor.fts5yy11 = sqlite3Fts5ParseColset(pParse, 0, &fts5yymsp[0].minor.fts5yy0);
+}
+ fts5yymsp[0].minor.fts5yy11 = fts5yylhsminor.fts5yy11;
+ break;
+ case 16: /* nearset ::= phrase */
+{ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53); }
+ fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
+ break;
+ case 17: /* nearset ::= STRING LP nearphrases neardist_opt RP */
+{
+ sqlite3Fts5ParseNear(pParse, &fts5yymsp[-4].minor.fts5yy0);
+ sqlite3Fts5ParseSetDistance(pParse, fts5yymsp[-2].minor.fts5yy46, &fts5yymsp[-1].minor.fts5yy0);
+ fts5yylhsminor.fts5yy46 = fts5yymsp[-2].minor.fts5yy46;
+}
+ fts5yymsp[-4].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
+ break;
+ case 18: /* nearphrases ::= phrase */
+{
+ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, 0, fts5yymsp[0].minor.fts5yy53);
+}
+ fts5yymsp[0].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
+ break;
+ case 19: /* nearphrases ::= nearphrases phrase */
+{
+ fts5yylhsminor.fts5yy46 = sqlite3Fts5ParseNearset(pParse, fts5yymsp[-1].minor.fts5yy46, fts5yymsp[0].minor.fts5yy53);
+}
+ fts5yymsp[-1].minor.fts5yy46 = fts5yylhsminor.fts5yy46;
+ break;
+ case 20: /* neardist_opt ::= */
+{ fts5yymsp[1].minor.fts5yy0.p = 0; fts5yymsp[1].minor.fts5yy0.n = 0; }
+ break;
+ case 21: /* neardist_opt ::= COMMA STRING */
+{ fts5yymsp[-1].minor.fts5yy0 = fts5yymsp[0].minor.fts5yy0; }
+ break;
+ case 22: /* phrase ::= phrase PLUS STRING star_opt */
+{
+ fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, fts5yymsp[-3].minor.fts5yy53, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
+}
+ fts5yymsp[-3].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
+ break;
+ case 23: /* phrase ::= STRING star_opt */
+{
+ fts5yylhsminor.fts5yy53 = sqlite3Fts5ParseTerm(pParse, 0, &fts5yymsp[-1].minor.fts5yy0, fts5yymsp[0].minor.fts5yy4);
+}
+ fts5yymsp[-1].minor.fts5yy53 = fts5yylhsminor.fts5yy53;
+ break;
+ case 24: /* star_opt ::= STAR */
+{ fts5yymsp[0].minor.fts5yy4 = 1; }
+ break;
+ case 25: /* star_opt ::= */
+{ fts5yymsp[1].minor.fts5yy4 = 0; }
+ break;
+ default:
+ break;
+/********** End reduce actions ************************************************/
+ };
+ assert( fts5yyruleno<sizeof(fts5yyRuleInfo)/sizeof(fts5yyRuleInfo[0]) );
+ fts5yygoto = fts5yyRuleInfo[fts5yyruleno].lhs;
+ fts5yysize = fts5yyRuleInfo[fts5yyruleno].nrhs;
+ fts5yyact = fts5yy_find_reduce_action(fts5yymsp[-fts5yysize].stateno,(fts5YYCODETYPE)fts5yygoto);
+ if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
+ if( fts5yyact>fts5YY_MAX_SHIFT ){
+ fts5yyact += fts5YY_MIN_REDUCE - fts5YY_MIN_SHIFTREDUCE;
+ }
+ fts5yymsp -= fts5yysize-1;
+ fts5yypParser->fts5yytos = fts5yymsp;
+ fts5yymsp->stateno = (fts5YYACTIONTYPE)fts5yyact;
+ fts5yymsp->major = (fts5YYCODETYPE)fts5yygoto;
+ fts5yyTraceShift(fts5yypParser, fts5yyact);
+ }else{
+ assert( fts5yyact == fts5YY_ACCEPT_ACTION );
+ fts5yypParser->fts5yytos -= fts5yysize;
+ fts5yy_accept(fts5yypParser);
+ }
+}
+
+/*
+** The following code executes when the parse fails
+*/
+#ifndef fts5YYNOERRORRECOVERY
+static void fts5yy_parse_failed(
+ fts5yyParser *fts5yypParser /* The parser */
+){
+ sqlite3Fts5ParserARG_FETCH;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sFail!\n",fts5yyTracePrompt);
+ }
+#endif
+ while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack ) fts5yy_pop_parser_stack(fts5yypParser);
+ /* Here code is inserted which will be executed whenever the
+ ** parser fails */
+/************ Begin %parse_failure code ***************************************/
+/************ End %parse_failure code *****************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+#endif /* fts5YYNOERRORRECOVERY */
+
+/*
+** The following code executes when a syntax error first occurs.
+*/
+static void fts5yy_syntax_error(
+ fts5yyParser *fts5yypParser, /* The parser */
+ int fts5yymajor, /* The major type of the error token */
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yyminor /* The minor type of the error token */
+){
+ sqlite3Fts5ParserARG_FETCH;
+#define FTS5TOKEN fts5yyminor
+/************ Begin %syntax_error code ****************************************/
+
+ UNUSED_PARAM(fts5yymajor); /* Silence a compiler warning */
+ sqlite3Fts5ParseError(
+ pParse, "fts5: syntax error near \"%.*s\"",FTS5TOKEN.n,FTS5TOKEN.p
+ );
+/************ End %syntax_error code ******************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/*
+** The following is executed when the parser accepts
+*/
+static void fts5yy_accept(
+ fts5yyParser *fts5yypParser /* The parser */
+){
+ sqlite3Fts5ParserARG_FETCH;
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sAccept!\n",fts5yyTracePrompt);
+ }
+#endif
+#ifndef fts5YYNOERRORRECOVERY
+ fts5yypParser->fts5yyerrcnt = -1;
+#endif
+ assert( fts5yypParser->fts5yytos==fts5yypParser->fts5yystack );
+ /* Here code is inserted which will be executed whenever the
+ ** parser accepts */
+/*********** Begin %parse_accept code *****************************************/
+/*********** End %parse_accept code *******************************************/
+ sqlite3Fts5ParserARG_STORE; /* Suppress warning about unused %extra_argument variable */
+}
+
+/* The main parser program.
+** The first argument is a pointer to a structure obtained from
+** "sqlite3Fts5ParserAlloc" which describes the current state of the parser.
+** The second argument is the major token number. The third is
+** the minor token. The fourth optional argument is whatever the
+** user wants (and specified in the grammar) and is available for
+** use by the action routines.
+**
+** Inputs:
+** <ul>
+** <li> A pointer to the parser (an opaque structure.)
+** <li> The major token number.
+** <li> The minor token number.
+** <li> An option argument of a grammar-specified type.
+** </ul>
+**
+** Outputs:
+** None.
+*/
+static void sqlite3Fts5Parser(
+ void *fts5yyp, /* The parser */
+ int fts5yymajor, /* The major token code number */
+ sqlite3Fts5ParserFTS5TOKENTYPE fts5yyminor /* The value for the token */
+ sqlite3Fts5ParserARG_PDECL /* Optional %extra_argument parameter */
+){
+ fts5YYMINORTYPE fts5yyminorunion;
+ unsigned int fts5yyact; /* The parser action. */
+#if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY)
+ int fts5yyendofinput; /* True if we are at the end of input */
+#endif
+#ifdef fts5YYERRORSYMBOL
+ int fts5yyerrorhit = 0; /* True if fts5yymajor has invoked an error */
+#endif
+ fts5yyParser *fts5yypParser; /* The parser */
+
+ fts5yypParser = (fts5yyParser*)fts5yyp;
+ assert( fts5yypParser->fts5yytos!=0 );
+#if !defined(fts5YYERRORSYMBOL) && !defined(fts5YYNOERRORRECOVERY)
+ fts5yyendofinput = (fts5yymajor==0);
+#endif
+ sqlite3Fts5ParserARG_STORE;
+
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sInput '%s'\n",fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]);
+ }
+#endif
+
+ do{
+ fts5yyact = fts5yy_find_shift_action(fts5yypParser,(fts5YYCODETYPE)fts5yymajor);
+ if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
+ fts5yy_shift(fts5yypParser,fts5yyact,fts5yymajor,fts5yyminor);
+#ifndef fts5YYNOERRORRECOVERY
+ fts5yypParser->fts5yyerrcnt--;
+#endif
+ fts5yymajor = fts5YYNOCODE;
+ }else if( fts5yyact <= fts5YY_MAX_REDUCE ){
+ fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE);
+ }else{
+ assert( fts5yyact == fts5YY_ERROR_ACTION );
+ fts5yyminorunion.fts5yy0 = fts5yyminor;
+#ifdef fts5YYERRORSYMBOL
+ int fts5yymx;
+#endif
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sSyntax Error!\n",fts5yyTracePrompt);
+ }
+#endif
+#ifdef fts5YYERRORSYMBOL
+ /* A syntax error has occurred.
+ ** The response to an error depends upon whether or not the
+ ** grammar defines an error token "ERROR".
+ **
+ ** This is what we do if the grammar does define ERROR:
+ **
+ ** * Call the %syntax_error function.
+ **
+ ** * Begin popping the stack until we enter a state where
+ ** it is legal to shift the error symbol, then shift
+ ** the error symbol.
+ **
+ ** * Set the error count to three.
+ **
+ ** * Begin accepting and shifting new tokens. No new error
+ ** processing will occur until three tokens have been
+ ** shifted successfully.
+ **
+ */
+ if( fts5yypParser->fts5yyerrcnt<0 ){
+ fts5yy_syntax_error(fts5yypParser,fts5yymajor,fts5yyminor);
+ }
+ fts5yymx = fts5yypParser->fts5yytos->major;
+ if( fts5yymx==fts5YYERRORSYMBOL || fts5yyerrorhit ){
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fprintf(fts5yyTraceFILE,"%sDiscard input token %s\n",
+ fts5yyTracePrompt,fts5yyTokenName[fts5yymajor]);
+ }
+#endif
+ fts5yy_destructor(fts5yypParser, (fts5YYCODETYPE)fts5yymajor, &fts5yyminorunion);
+ fts5yymajor = fts5YYNOCODE;
+ }else{
+ while( fts5yypParser->fts5yytos >= fts5yypParser->fts5yystack
+ && fts5yymx != fts5YYERRORSYMBOL
+ && (fts5yyact = fts5yy_find_reduce_action(
+ fts5yypParser->fts5yytos->stateno,
+ fts5YYERRORSYMBOL)) >= fts5YY_MIN_REDUCE
+ ){
+ fts5yy_pop_parser_stack(fts5yypParser);
+ }
+ if( fts5yypParser->fts5yytos < fts5yypParser->fts5yystack || fts5yymajor==0 ){
+ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
+ fts5yy_parse_failed(fts5yypParser);
+#ifndef fts5YYNOERRORRECOVERY
+ fts5yypParser->fts5yyerrcnt = -1;
+#endif
+ fts5yymajor = fts5YYNOCODE;
+ }else if( fts5yymx!=fts5YYERRORSYMBOL ){
+ fts5yy_shift(fts5yypParser,fts5yyact,fts5YYERRORSYMBOL,fts5yyminor);
+ }
+ }
+ fts5yypParser->fts5yyerrcnt = 3;
+ fts5yyerrorhit = 1;
+#elif defined(fts5YYNOERRORRECOVERY)
+ /* If the fts5YYNOERRORRECOVERY macro is defined, then do not attempt to
+ ** do any kind of error recovery. Instead, simply invoke the syntax
+ ** error routine and continue going as if nothing had happened.
+ **
+ ** Applications can set this macro (for example inside %include) if
+ ** they intend to abandon the parse upon the first syntax error seen.
+ */
+ fts5yy_syntax_error(fts5yypParser,fts5yymajor, fts5yyminor);
+ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
+ fts5yymajor = fts5YYNOCODE;
+
+#else /* fts5YYERRORSYMBOL is not defined */
+ /* This is what we do if the grammar does not define ERROR:
+ **
+ ** * Report an error message, and throw away the input token.
+ **
+ ** * If the input token is $, then fail the parse.
+ **
+ ** As before, subsequent error messages are suppressed until
+ ** three input tokens have been successfully shifted.
+ */
+ if( fts5yypParser->fts5yyerrcnt<=0 ){
+ fts5yy_syntax_error(fts5yypParser,fts5yymajor, fts5yyminor);
+ }
+ fts5yypParser->fts5yyerrcnt = 3;
+ fts5yy_destructor(fts5yypParser,(fts5YYCODETYPE)fts5yymajor,&fts5yyminorunion);
+ if( fts5yyendofinput ){
+ fts5yy_parse_failed(fts5yypParser);
+#ifndef fts5YYNOERRORRECOVERY
+ fts5yypParser->fts5yyerrcnt = -1;
+#endif
+ }
+ fts5yymajor = fts5YYNOCODE;
+#endif
+ }
+ }while( fts5yymajor!=fts5YYNOCODE && fts5yypParser->fts5yytos>fts5yypParser->fts5yystack );
+#ifndef NDEBUG
+ if( fts5yyTraceFILE ){
+ fts5yyStackEntry *i;
+ char cDiv = '[';
+ fprintf(fts5yyTraceFILE,"%sReturn. Stack=",fts5yyTracePrompt);
+ for(i=&fts5yypParser->fts5yystack[1]; i<=fts5yypParser->fts5yytos; i++){
+ fprintf(fts5yyTraceFILE,"%c%s", cDiv, fts5yyTokenName[i->major]);
+ cDiv = ' ';
+ }
+ fprintf(fts5yyTraceFILE,"]\n");
+ }
+#endif
+ return;
+}
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+*/
+
+
+/* #include "fts5Int.h" */
+#include <math.h> /* amalgamator: keep */
+
+/*
+** Object used to iterate through all "coalesced phrase instances" in
+** a single column of the current row. If the phrase instances in the
+** column being considered do not overlap, this object simply iterates
+** through them. Or, if they do overlap (share one or more tokens in
+** common), each set of overlapping instances is treated as a single
+** match. See documentation for the highlight() auxiliary function for
+** details.
+**
+** Usage is:
+**
+** for(rc = fts5CInstIterNext(pApi, pFts, iCol, &iter);
+** (rc==SQLITE_OK && 0==fts5CInstIterEof(&iter);
+** rc = fts5CInstIterNext(&iter)
+** ){
+** printf("instance starts at %d, ends at %d\n", iter.iStart, iter.iEnd);
+** }
+**
+*/
+typedef struct CInstIter CInstIter;
+struct CInstIter {
+ const Fts5ExtensionApi *pApi; /* API offered by current FTS version */
+ Fts5Context *pFts; /* First arg to pass to pApi functions */
+ int iCol; /* Column to search */
+ int iInst; /* Next phrase instance index */
+ int nInst; /* Total number of phrase instances */
+
+ /* Output variables */
+ int iStart; /* First token in coalesced phrase instance */
+ int iEnd; /* Last token in coalesced phrase instance */
+};
+
+/*
+** Advance the iterator to the next coalesced phrase instance. Return
+** an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+*/
+static int fts5CInstIterNext(CInstIter *pIter){
+ int rc = SQLITE_OK;
+ pIter->iStart = -1;
+ pIter->iEnd = -1;
+
+ while( rc==SQLITE_OK && pIter->iInst<pIter->nInst ){
+ int ip; int ic; int io;
+ rc = pIter->pApi->xInst(pIter->pFts, pIter->iInst, &ip, &ic, &io);
+ if( rc==SQLITE_OK ){
+ if( ic==pIter->iCol ){
+ int iEnd = io - 1 + pIter->pApi->xPhraseSize(pIter->pFts, ip);
+ if( pIter->iStart<0 ){
+ pIter->iStart = io;
+ pIter->iEnd = iEnd;
+ }else if( io<=pIter->iEnd ){
+ if( iEnd>pIter->iEnd ) pIter->iEnd = iEnd;
+ }else{
+ break;
+ }
+ }
+ pIter->iInst++;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Initialize the iterator object indicated by the final parameter to
+** iterate through coalesced phrase instances in column iCol.
+*/
+static int fts5CInstIterInit(
+ const Fts5ExtensionApi *pApi,
+ Fts5Context *pFts,
+ int iCol,
+ CInstIter *pIter
+){
+ int rc;
+
+ memset(pIter, 0, sizeof(CInstIter));
+ pIter->pApi = pApi;
+ pIter->pFts = pFts;
+ pIter->iCol = iCol;
+ rc = pApi->xInstCount(pFts, &pIter->nInst);
+
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterNext(pIter);
+ }
+
+ return rc;
+}
+
+
+
+/*************************************************************************
+** Start of highlight() implementation.
+*/
+typedef struct HighlightContext HighlightContext;
+struct HighlightContext {
+ CInstIter iter; /* Coalesced Instance Iterator */
+ int iPos; /* Current token offset in zIn[] */
+ int iRangeStart; /* First token to include */
+ int iRangeEnd; /* If non-zero, last token to include */
+ const char *zOpen; /* Opening highlight */
+ const char *zClose; /* Closing highlight */
+ const char *zIn; /* Input text */
+ int nIn; /* Size of input text in bytes */
+ int iOff; /* Current offset within zIn[] */
+ char *zOut; /* Output value */
+};
+
+/*
+** Append text to the HighlightContext output string - p->zOut. Argument
+** z points to a buffer containing n bytes of text to append. If n is
+** negative, everything up until the first '\0' is appended to the output.
+**
+** If *pRc is set to any value other than SQLITE_OK when this function is
+** called, it is a no-op. If an error (i.e. an OOM condition) is encountered,
+** *pRc is set to an error code before returning.
+*/
+static void fts5HighlightAppend(
+ int *pRc,
+ HighlightContext *p,
+ const char *z, int n
+){
+ if( *pRc==SQLITE_OK ){
+ if( n<0 ) n = (int)strlen(z);
+ p->zOut = sqlite3_mprintf("%z%.*s", p->zOut, n, z);
+ if( p->zOut==0 ) *pRc = SQLITE_NOMEM;
+ }
+}
+
+/*
+** Tokenizer callback used by implementation of highlight() function.
+*/
+static int fts5HighlightCb(
+ void *pContext, /* Pointer to HighlightContext object */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStartOff, /* Start offset of token */
+ int iEndOff /* End offset of token */
+){
+ HighlightContext *p = (HighlightContext*)pContext;
+ int rc = SQLITE_OK;
+ int iPos;
+
+ UNUSED_PARAM2(pToken, nToken);
+
+ if( tflags & FTS5_TOKEN_COLOCATED ) return SQLITE_OK;
+ iPos = p->iPos++;
+
+ if( p->iRangeEnd>0 ){
+ if( iPos<p->iRangeStart || iPos>p->iRangeEnd ) return SQLITE_OK;
+ if( p->iRangeStart && iPos==p->iRangeStart ) p->iOff = iStartOff;
+ }
+
+ if( iPos==p->iter.iStart ){
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iStartOff - p->iOff);
+ fts5HighlightAppend(&rc, p, p->zOpen, -1);
+ p->iOff = iStartOff;
+ }
+
+ if( iPos==p->iter.iEnd ){
+ if( p->iRangeEnd && p->iter.iStart<p->iRangeStart ){
+ fts5HighlightAppend(&rc, p, p->zOpen, -1);
+ }
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ fts5HighlightAppend(&rc, p, p->zClose, -1);
+ p->iOff = iEndOff;
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterNext(&p->iter);
+ }
+ }
+
+ if( p->iRangeEnd>0 && iPos==p->iRangeEnd ){
+ fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
+ p->iOff = iEndOff;
+ if( iPos>=p->iter.iStart && iPos<p->iter.iEnd ){
+ fts5HighlightAppend(&rc, p, p->zClose, -1);
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Implementation of highlight() function.
+*/
+static void fts5HighlightFunction(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+){
+ HighlightContext ctx;
+ int rc;
+ int iCol;
+
+ if( nVal!=3 ){
+ const char *zErr = "wrong number of arguments to function highlight()";
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+
+ iCol = sqlite3_value_int(apVal[0]);
+ memset(&ctx, 0, sizeof(HighlightContext));
+ ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
+ ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
+ rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
+
+ if( ctx.zIn ){
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
+ }
+ fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
+
+ if( rc==SQLITE_OK ){
+ sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
+ }
+ sqlite3_free(ctx.zOut);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_result_error_code(pCtx, rc);
+ }
+}
+/*
+** End of highlight() implementation.
+**************************************************************************/
+
+/*
+** Context object passed to the fts5SentenceFinderCb() function.
+*/
+typedef struct Fts5SFinder Fts5SFinder;
+struct Fts5SFinder {
+ int iPos; /* Current token position */
+ int nFirstAlloc; /* Allocated size of aFirst[] */
+ int nFirst; /* Number of entries in aFirst[] */
+ int *aFirst; /* Array of first token in each sentence */
+ const char *zDoc; /* Document being tokenized */
+};
+
+/*
+** Add an entry to the Fts5SFinder.aFirst[] array. Grow the array if
+** necessary. Return SQLITE_OK if successful, or SQLITE_NOMEM if an
+** error occurs.
+*/
+static int fts5SentenceFinderAdd(Fts5SFinder *p, int iAdd){
+ if( p->nFirstAlloc==p->nFirst ){
+ int nNew = p->nFirstAlloc ? p->nFirstAlloc*2 : 64;
+ int *aNew;
+
+ aNew = (int*)sqlite3_realloc(p->aFirst, nNew*sizeof(int));
+ if( aNew==0 ) return SQLITE_NOMEM;
+ p->aFirst = aNew;
+ p->nFirstAlloc = nNew;
+ }
+ p->aFirst[p->nFirst++] = iAdd;
+ return SQLITE_OK;
+}
+
+/*
+** This function is an xTokenize() callback used by the auxiliary snippet()
+** function. Its job is to identify tokens that are the first in a sentence.
+** For each such token, an entry is added to the SFinder.aFirst[] array.
+*/
+static int fts5SentenceFinderCb(
+ void *pContext, /* Pointer to HighlightContext object */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStartOff, /* Start offset of token */
+ int iEndOff /* End offset of token */
+){
+ int rc = SQLITE_OK;
+
+ UNUSED_PARAM2(pToken, nToken);
+ UNUSED_PARAM(iEndOff);
+
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
+ Fts5SFinder *p = (Fts5SFinder*)pContext;
+ if( p->iPos>0 ){
+ int i;
+ char c = 0;
+ for(i=iStartOff-1; i>=0; i--){
+ c = p->zDoc[i];
+ if( c!=' ' && c!='\t' && c!='\n' && c!='\r' ) break;
+ }
+ if( i!=iStartOff-1 && (c=='.' || c==':') ){
+ rc = fts5SentenceFinderAdd(p, p->iPos);
+ }
+ }else{
+ rc = fts5SentenceFinderAdd(p, 0);
+ }
+ p->iPos++;
+ }
+ return rc;
+}
+
+static int fts5SnippetScore(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ int nDocsize, /* Size of column in tokens */
+ unsigned char *aSeen, /* Array with one element per query phrase */
+ int iCol, /* Column to score */
+ int iPos, /* Starting offset to score */
+ int nToken, /* Max tokens per snippet */
+ int *pnScore, /* OUT: Score */
+ int *piPos /* OUT: Adjusted offset */
+){
+ int rc;
+ int i;
+ int ip = 0;
+ int ic = 0;
+ int iOff = 0;
+ int iFirst = -1;
+ int nInst;
+ int nScore = 0;
+ int iLast = 0;
+
+ rc = pApi->xInstCount(pFts, &nInst);
+ for(i=0; i<nInst && rc==SQLITE_OK; i++){
+ rc = pApi->xInst(pFts, i, &ip, &ic, &iOff);
+ if( rc==SQLITE_OK && ic==iCol && iOff>=iPos && iOff<(iPos+nToken) ){
+ nScore += (aSeen[ip] ? 1 : 1000);
+ aSeen[ip] = 1;
+ if( iFirst<0 ) iFirst = iOff;
+ iLast = iOff + pApi->xPhraseSize(pFts, ip);
+ }
+ }
+
+ *pnScore = nScore;
+ if( piPos ){
+ int iAdj = iFirst - (nToken - (iLast-iFirst)) / 2;
+ if( (iAdj+nToken)>nDocsize ) iAdj = nDocsize - nToken;
+ if( iAdj<0 ) iAdj = 0;
+ *piPos = iAdj;
+ }
+
+ return rc;
+}
+
+/*
+** Implementation of snippet() function.
+*/
+static void fts5SnippetFunction(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+){
+ HighlightContext ctx;
+ int rc = SQLITE_OK; /* Return code */
+ int iCol; /* 1st argument to snippet() */
+ const char *zEllips; /* 4th argument to snippet() */
+ int nToken; /* 5th argument to snippet() */
+ int nInst = 0; /* Number of instance matches this row */
+ int i; /* Used to iterate through instances */
+ int nPhrase; /* Number of phrases in query */
+ unsigned char *aSeen; /* Array of "seen instance" flags */
+ int iBestCol; /* Column containing best snippet */
+ int iBestStart = 0; /* First token of best snippet */
+ int nBestScore = 0; /* Score of best snippet */
+ int nColSize = 0; /* Total size of iBestCol in tokens */
+ Fts5SFinder sFinder; /* Used to find the beginnings of sentences */
+ int nCol;
+
+ if( nVal!=5 ){
+ const char *zErr = "wrong number of arguments to function snippet()";
+ sqlite3_result_error(pCtx, zErr, -1);
+ return;
+ }
+
+ nCol = pApi->xColumnCount(pFts);
+ memset(&ctx, 0, sizeof(HighlightContext));
+ iCol = sqlite3_value_int(apVal[0]);
+ ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
+ ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
+ zEllips = (const char*)sqlite3_value_text(apVal[3]);
+ nToken = sqlite3_value_int(apVal[4]);
+
+ iBestCol = (iCol>=0 ? iCol : 0);
+ nPhrase = pApi->xPhraseCount(pFts);
+ aSeen = sqlite3_malloc(nPhrase);
+ if( aSeen==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ if( rc==SQLITE_OK ){
+ rc = pApi->xInstCount(pFts, &nInst);
+ }
+
+ memset(&sFinder, 0, sizeof(Fts5SFinder));
+ for(i=0; i<nCol; i++){
+ if( iCol<0 || iCol==i ){
+ int nDoc;
+ int nDocsize;
+ int ii;
+ sFinder.iPos = 0;
+ sFinder.nFirst = 0;
+ rc = pApi->xColumnText(pFts, i, &sFinder.zDoc, &nDoc);
+ if( rc!=SQLITE_OK ) break;
+ rc = pApi->xTokenize(pFts,
+ sFinder.zDoc, nDoc, (void*)&sFinder,fts5SentenceFinderCb
+ );
+ if( rc!=SQLITE_OK ) break;
+ rc = pApi->xColumnSize(pFts, i, &nDocsize);
+ if( rc!=SQLITE_OK ) break;
+
+ for(ii=0; rc==SQLITE_OK && ii<nInst; ii++){
+ int ip, ic, io;
+ int iAdj;
+ int nScore;
+ int jj;
+
+ rc = pApi->xInst(pFts, ii, &ip, &ic, &io);
+ if( ic!=i || rc!=SQLITE_OK ) continue;
+ memset(aSeen, 0, nPhrase);
+ rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
+ io, nToken, &nScore, &iAdj
+ );
+ if( rc==SQLITE_OK && nScore>nBestScore ){
+ nBestScore = nScore;
+ iBestCol = i;
+ iBestStart = iAdj;
+ nColSize = nDocsize;
+ }
+
+ if( rc==SQLITE_OK && sFinder.nFirst && nDocsize>nToken ){
+ for(jj=0; jj<(sFinder.nFirst-1); jj++){
+ if( sFinder.aFirst[jj+1]>io ) break;
+ }
+
+ if( sFinder.aFirst[jj]<io ){
+ memset(aSeen, 0, nPhrase);
+ rc = fts5SnippetScore(pApi, pFts, nDocsize, aSeen, i,
+ sFinder.aFirst[jj], nToken, &nScore, 0
+ );
+
+ nScore += (sFinder.aFirst[jj]==0 ? 120 : 100);
+ if( rc==SQLITE_OK && nScore>nBestScore ){
+ nBestScore = nScore;
+ iBestCol = i;
+ iBestStart = sFinder.aFirst[jj];
+ nColSize = nDocsize;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pApi->xColumnText(pFts, iBestCol, &ctx.zIn, &ctx.nIn);
+ }
+ if( rc==SQLITE_OK && nColSize==0 ){
+ rc = pApi->xColumnSize(pFts, iBestCol, &nColSize);
+ }
+ if( ctx.zIn ){
+ if( rc==SQLITE_OK ){
+ rc = fts5CInstIterInit(pApi, pFts, iBestCol, &ctx.iter);
+ }
+
+ ctx.iRangeStart = iBestStart;
+ ctx.iRangeEnd = iBestStart + nToken - 1;
+
+ if( iBestStart>0 ){
+ fts5HighlightAppend(&rc, &ctx, zEllips, -1);
+ }
+
+ /* Advance iterator ctx.iter so that it points to the first coalesced
+ ** phrase instance at or following position iBestStart. */
+ while( ctx.iter.iStart>=0 && ctx.iter.iStart<iBestStart && rc==SQLITE_OK ){
+ rc = fts5CInstIterNext(&ctx.iter);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
+ }
+ if( ctx.iRangeEnd>=(nColSize-1) ){
+ fts5HighlightAppend(&rc, &ctx, &ctx.zIn[ctx.iOff], ctx.nIn - ctx.iOff);
+ }else{
+ fts5HighlightAppend(&rc, &ctx, zEllips, -1);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_result_text(pCtx, (const char*)ctx.zOut, -1, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+ sqlite3_free(ctx.zOut);
+ sqlite3_free(aSeen);
+ sqlite3_free(sFinder.aFirst);
+}
+
+/************************************************************************/
+
+/*
+** The first time the bm25() function is called for a query, an instance
+** of the following structure is allocated and populated.
+*/
+typedef struct Fts5Bm25Data Fts5Bm25Data;
+struct Fts5Bm25Data {
+ int nPhrase; /* Number of phrases in query */
+ double avgdl; /* Average number of tokens in each row */
+ double *aIDF; /* IDF for each phrase */
+ double *aFreq; /* Array used to calculate phrase freq. */
+};
+
+/*
+** Callback used by fts5Bm25GetData() to count the number of rows in the
+** table matched by each individual phrase within the query.
+*/
+static int fts5CountCb(
+ const Fts5ExtensionApi *pApi,
+ Fts5Context *pFts,
+ void *pUserData /* Pointer to sqlite3_int64 variable */
+){
+ sqlite3_int64 *pn = (sqlite3_int64*)pUserData;
+ UNUSED_PARAM2(pApi, pFts);
+ (*pn)++;
+ return SQLITE_OK;
+}
+
+/*
+** Set *ppData to point to the Fts5Bm25Data object for the current query.
+** If the object has not already been allocated, allocate and populate it
+** now.
+*/
+static int fts5Bm25GetData(
+ const Fts5ExtensionApi *pApi,
+ Fts5Context *pFts,
+ Fts5Bm25Data **ppData /* OUT: bm25-data object for this query */
+){
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Bm25Data *p; /* Object to return */
+
+ p = pApi->xGetAuxdata(pFts, 0);
+ if( p==0 ){
+ int nPhrase; /* Number of phrases in query */
+ sqlite3_int64 nRow = 0; /* Number of rows in table */
+ sqlite3_int64 nToken = 0; /* Number of tokens in table */
+ int nByte; /* Bytes of space to allocate */
+ int i;
+
+ /* Allocate the Fts5Bm25Data object */
+ nPhrase = pApi->xPhraseCount(pFts);
+ nByte = sizeof(Fts5Bm25Data) + nPhrase*2*sizeof(double);
+ p = (Fts5Bm25Data*)sqlite3_malloc(nByte);
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(p, 0, nByte);
+ p->nPhrase = nPhrase;
+ p->aIDF = (double*)&p[1];
+ p->aFreq = &p->aIDF[nPhrase];
+ }
+
+ /* Calculate the average document length for this FTS5 table */
+ if( rc==SQLITE_OK ) rc = pApi->xRowCount(pFts, &nRow);
+ if( rc==SQLITE_OK ) rc = pApi->xColumnTotalSize(pFts, -1, &nToken);
+ if( rc==SQLITE_OK ) p->avgdl = (double)nToken / (double)nRow;
+
+ /* Calculate an IDF for each phrase in the query */
+ for(i=0; rc==SQLITE_OK && i<nPhrase; i++){
+ sqlite3_int64 nHit = 0;
+ rc = pApi->xQueryPhrase(pFts, i, (void*)&nHit, fts5CountCb);
+ if( rc==SQLITE_OK ){
+ /* Calculate the IDF (Inverse Document Frequency) for phrase i.
+ ** This is done using the standard BM25 formula as found on wikipedia:
+ **
+ ** IDF = log( (N - nHit + 0.5) / (nHit + 0.5) )
+ **
+ ** where "N" is the total number of documents in the set and nHit
+ ** is the number that contain at least one instance of the phrase
+ ** under consideration.
+ **
+ ** The problem with this is that if (N < 2*nHit), the IDF is
+ ** negative. Which is undesirable. So the mimimum allowable IDF is
+ ** (1e-6) - roughly the same as a term that appears in just over
+ ** half of set of 5,000,000 documents. */
+ double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
+ if( idf<=0.0 ) idf = 1e-6;
+ p->aIDF[i] = idf;
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(p);
+ }else{
+ rc = pApi->xSetAuxdata(pFts, p, sqlite3_free);
+ }
+ if( rc!=SQLITE_OK ) p = 0;
+ }
+ *ppData = p;
+ return rc;
+}
+
+/*
+** Implementation of bm25() function.
+*/
+static void fts5Bm25Function(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+){
+ const double k1 = 1.2; /* Constant "k1" from BM25 formula */
+ const double b = 0.75; /* Constant "b" from BM25 formula */
+ int rc = SQLITE_OK; /* Error code */
+ double score = 0.0; /* SQL function return value */
+ Fts5Bm25Data *pData; /* Values allocated/calculated once only */
+ int i; /* Iterator variable */
+ int nInst = 0; /* Value returned by xInstCount() */
+ double D = 0.0; /* Total number of tokens in row */
+ double *aFreq = 0; /* Array of phrase freq. for current row */
+
+ /* Calculate the phrase frequency (symbol "f(qi,D)" in the documentation)
+ ** for each phrase in the query for the current row. */
+ rc = fts5Bm25GetData(pApi, pFts, &pData);
+ if( rc==SQLITE_OK ){
+ aFreq = pData->aFreq;
+ memset(aFreq, 0, sizeof(double) * pData->nPhrase);
+ rc = pApi->xInstCount(pFts, &nInst);
+ }
+ for(i=0; rc==SQLITE_OK && i<nInst; i++){
+ int ip; int ic; int io;
+ rc = pApi->xInst(pFts, i, &ip, &ic, &io);
+ if( rc==SQLITE_OK ){
+ double w = (nVal > ic) ? sqlite3_value_double(apVal[ic]) : 1.0;
+ aFreq[ip] += w;
+ }
+ }
+
+ /* Figure out the total size of the current row in tokens. */
+ if( rc==SQLITE_OK ){
+ int nTok;
+ rc = pApi->xColumnSize(pFts, -1, &nTok);
+ D = (double)nTok;
+ }
+
+ /* Determine the BM25 score for the current row. */
+ for(i=0; rc==SQLITE_OK && i<pData->nPhrase; i++){
+ score += pData->aIDF[i] * (
+ ( aFreq[i] * (k1 + 1.0) ) /
+ ( aFreq[i] + k1 * (1 - b + b * D / pData->avgdl) )
+ );
+ }
+
+ /* If no error has occurred, return the calculated score. Otherwise,
+ ** throw an SQL exception. */
+ if( rc==SQLITE_OK ){
+ sqlite3_result_double(pCtx, -1.0 * score);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+}
+
+static int sqlite3Fts5AuxInit(fts5_api *pApi){
+ struct Builtin {
+ const char *zFunc; /* Function name (nul-terminated) */
+ void *pUserData; /* User-data pointer */
+ fts5_extension_function xFunc;/* Callback function */
+ void (*xDestroy)(void*); /* Destructor function */
+ } aBuiltin [] = {
+ { "snippet", 0, fts5SnippetFunction, 0 },
+ { "highlight", 0, fts5HighlightFunction, 0 },
+ { "bm25", 0, fts5Bm25Function, 0 },
+ };
+ int rc = SQLITE_OK; /* Return code */
+ int i; /* To iterate through builtin functions */
+
+ for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){
+ rc = pApi->xCreateFunction(pApi,
+ aBuiltin[i].zFunc,
+ aBuiltin[i].pUserData,
+ aBuiltin[i].xFunc,
+ aBuiltin[i].xDestroy
+ );
+ }
+
+ return rc;
+}
+
+
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+*/
+
+
+
+/* #include "fts5Int.h" */
+
+static int sqlite3Fts5BufferSize(int *pRc, Fts5Buffer *pBuf, u32 nByte){
+ if( (u32)pBuf->nSpace<nByte ){
+ u32 nNew = pBuf->nSpace ? pBuf->nSpace : 64;
+ u8 *pNew;
+ while( nNew<nByte ){
+ nNew = nNew * 2;
+ }
+ pNew = sqlite3_realloc(pBuf->p, nNew);
+ if( pNew==0 ){
+ *pRc = SQLITE_NOMEM;
+ return 1;
+ }else{
+ pBuf->nSpace = nNew;
+ pBuf->p = pNew;
+ }
+ }
+ return 0;
+}
+
+
+/*
+** Encode value iVal as an SQLite varint and append it to the buffer object
+** pBuf. If an OOM error occurs, set the error code in p.
+*/
+static void sqlite3Fts5BufferAppendVarint(int *pRc, Fts5Buffer *pBuf, i64 iVal){
+ if( fts5BufferGrow(pRc, pBuf, 9) ) return;
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iVal);
+}
+
+static void sqlite3Fts5Put32(u8 *aBuf, int iVal){
+ aBuf[0] = (iVal>>24) & 0x00FF;
+ aBuf[1] = (iVal>>16) & 0x00FF;
+ aBuf[2] = (iVal>> 8) & 0x00FF;
+ aBuf[3] = (iVal>> 0) & 0x00FF;
+}
+
+static int sqlite3Fts5Get32(const u8 *aBuf){
+ return (aBuf[0] << 24) + (aBuf[1] << 16) + (aBuf[2] << 8) + aBuf[3];
+}
+
+/*
+** Append buffer nData/pData to buffer pBuf. If an OOM error occurs, set
+** the error code in p. If an error has already occurred when this function
+** is called, it is a no-op.
+*/
+static void sqlite3Fts5BufferAppendBlob(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ u32 nData,
+ const u8 *pData
+){
+ assert_nc( *pRc || nData>=0 );
+ if( fts5BufferGrow(pRc, pBuf, nData) ) return;
+ memcpy(&pBuf->p[pBuf->n], pData, nData);
+ pBuf->n += nData;
+}
+
+/*
+** Append the nul-terminated string zStr to the buffer pBuf. This function
+** ensures that the byte following the buffer data is set to 0x00, even
+** though this byte is not included in the pBuf->n count.
+*/
+static void sqlite3Fts5BufferAppendString(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ const char *zStr
+){
+ int nStr = (int)strlen(zStr);
+ sqlite3Fts5BufferAppendBlob(pRc, pBuf, nStr+1, (const u8*)zStr);
+ pBuf->n--;
+}
+
+/*
+** Argument zFmt is a printf() style format string. This function performs
+** the printf() style processing, then appends the results to buffer pBuf.
+**
+** Like sqlite3Fts5BufferAppendString(), this function ensures that the byte
+** following the buffer data is set to 0x00, even though this byte is not
+** included in the pBuf->n count.
+*/
+static void sqlite3Fts5BufferAppendPrintf(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ char *zFmt, ...
+){
+ if( *pRc==SQLITE_OK ){
+ char *zTmp;
+ va_list ap;
+ va_start(ap, zFmt);
+ zTmp = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+
+ if( zTmp==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ sqlite3Fts5BufferAppendString(pRc, pBuf, zTmp);
+ sqlite3_free(zTmp);
+ }
+ }
+}
+
+static char *sqlite3Fts5Mprintf(int *pRc, const char *zFmt, ...){
+ char *zRet = 0;
+ if( *pRc==SQLITE_OK ){
+ va_list ap;
+ va_start(ap, zFmt);
+ zRet = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+ if( zRet==0 ){
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ return zRet;
+}
+
+
+/*
+** Free any buffer allocated by pBuf. Zero the structure before returning.
+*/
+static void sqlite3Fts5BufferFree(Fts5Buffer *pBuf){
+ sqlite3_free(pBuf->p);
+ memset(pBuf, 0, sizeof(Fts5Buffer));
+}
+
+/*
+** Zero the contents of the buffer object. But do not free the associated
+** memory allocation.
+*/
+static void sqlite3Fts5BufferZero(Fts5Buffer *pBuf){
+ pBuf->n = 0;
+}
+
+/*
+** Set the buffer to contain nData/pData. If an OOM error occurs, leave an
+** the error code in p. If an error has already occurred when this function
+** is called, it is a no-op.
+*/
+static void sqlite3Fts5BufferSet(
+ int *pRc,
+ Fts5Buffer *pBuf,
+ int nData,
+ const u8 *pData
+){
+ pBuf->n = 0;
+ sqlite3Fts5BufferAppendBlob(pRc, pBuf, nData, pData);
+}
+
+static int sqlite3Fts5PoslistNext64(
+ const u8 *a, int n, /* Buffer containing poslist */
+ int *pi, /* IN/OUT: Offset within a[] */
+ i64 *piOff /* IN/OUT: Current offset */
+){
+ int i = *pi;
+ if( i>=n ){
+ /* EOF */
+ *piOff = -1;
+ return 1;
+ }else{
+ i64 iOff = *piOff;
+ int iVal;
+ fts5FastGetVarint32(a, i, iVal);
+ if( iVal==1 ){
+ fts5FastGetVarint32(a, i, iVal);
+ iOff = ((i64)iVal) << 32;
+ fts5FastGetVarint32(a, i, iVal);
+ }
+ *piOff = iOff + (iVal-2);
+ *pi = i;
+ return 0;
+ }
+}
+
+
+/*
+** Advance the iterator object passed as the only argument. Return true
+** if the iterator reaches EOF, or false otherwise.
+*/
+static int sqlite3Fts5PoslistReaderNext(Fts5PoslistReader *pIter){
+ if( sqlite3Fts5PoslistNext64(pIter->a, pIter->n, &pIter->i, &pIter->iPos) ){
+ pIter->bEof = 1;
+ }
+ return pIter->bEof;
+}
+
+static int sqlite3Fts5PoslistReaderInit(
+ const u8 *a, int n, /* Poslist buffer to iterate through */
+ Fts5PoslistReader *pIter /* Iterator object to initialize */
+){
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->a = a;
+ pIter->n = n;
+ sqlite3Fts5PoslistReaderNext(pIter);
+ return pIter->bEof;
+}
+
+/*
+** Append position iPos to the position list being accumulated in buffer
+** pBuf, which must be already be large enough to hold the new data.
+** The previous position written to this list is *piPrev. *piPrev is set
+** to iPos before returning.
+*/
+static void sqlite3Fts5PoslistSafeAppend(
+ Fts5Buffer *pBuf,
+ i64 *piPrev,
+ i64 iPos
+){
+ static const i64 colmask = ((i64)(0x7FFFFFFF)) << 32;
+ if( (iPos & colmask) != (*piPrev & colmask) ){
+ pBuf->p[pBuf->n++] = 1;
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos>>32));
+ *piPrev = (iPos & colmask);
+ }
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], (iPos-*piPrev)+2);
+ *piPrev = iPos;
+}
+
+static int sqlite3Fts5PoslistWriterAppend(
+ Fts5Buffer *pBuf,
+ Fts5PoslistWriter *pWriter,
+ i64 iPos
+){
+ int rc = 0; /* Initialized only to suppress erroneous warning from Clang */
+ if( fts5BufferGrow(&rc, pBuf, 5+5+5) ) return rc;
+ sqlite3Fts5PoslistSafeAppend(pBuf, &pWriter->iPrev, iPos);
+ return SQLITE_OK;
+}
+
+static void *sqlite3Fts5MallocZero(int *pRc, int nByte){
+ void *pRet = 0;
+ if( *pRc==SQLITE_OK ){
+ pRet = sqlite3_malloc(nByte);
+ if( pRet==0 && nByte>0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ memset(pRet, 0, nByte);
+ }
+ }
+ return pRet;
+}
+
+/*
+** Return a nul-terminated copy of the string indicated by pIn. If nIn
+** is non-negative, then it is the length of the string in bytes. Otherwise,
+** the length of the string is determined using strlen().
+**
+** It is the responsibility of the caller to eventually free the returned
+** buffer using sqlite3_free(). If an OOM error occurs, NULL is returned.
+*/
+static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
+ char *zRet = 0;
+ if( *pRc==SQLITE_OK ){
+ if( nIn<0 ){
+ nIn = (int)strlen(pIn);
+ }
+ zRet = (char*)sqlite3_malloc(nIn+1);
+ if( zRet ){
+ memcpy(zRet, pIn, nIn);
+ zRet[nIn] = '\0';
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ return zRet;
+}
+
+
+/*
+** Return true if character 't' may be part of an FTS5 bareword, or false
+** otherwise. Characters that may be part of barewords:
+**
+** * All non-ASCII characters,
+** * The 52 upper and lower case ASCII characters, and
+** * The 10 integer ASCII characters.
+** * The underscore character "_" (0x5F).
+** * The unicode "subsitute" character (0x1A).
+*/
+static int sqlite3Fts5IsBareword(char t){
+ u8 aBareword[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 .. 0x2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30 .. 0x3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40 .. 0x4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 0x50 .. 0x5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60 .. 0x6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 /* 0x70 .. 0x7F */
+ };
+
+ return (t & 0x80) || aBareword[(int)t];
+}
+
+
+/*************************************************************************
+*/
+typedef struct Fts5TermsetEntry Fts5TermsetEntry;
+struct Fts5TermsetEntry {
+ char *pTerm;
+ int nTerm;
+ int iIdx; /* Index (main or aPrefix[] entry) */
+ Fts5TermsetEntry *pNext;
+};
+
+struct Fts5Termset {
+ Fts5TermsetEntry *apHash[512];
+};
+
+static int sqlite3Fts5TermsetNew(Fts5Termset **pp){
+ int rc = SQLITE_OK;
+ *pp = sqlite3Fts5MallocZero(&rc, sizeof(Fts5Termset));
+ return rc;
+}
+
+static int sqlite3Fts5TermsetAdd(
+ Fts5Termset *p,
+ int iIdx,
+ const char *pTerm, int nTerm,
+ int *pbPresent
+){
+ int rc = SQLITE_OK;
+ *pbPresent = 0;
+ if( p ){
+ int i;
+ u32 hash = 13;
+ Fts5TermsetEntry *pEntry;
+
+ /* Calculate a hash value for this term. This is the same hash checksum
+ ** used by the fts5_hash.c module. This is not important for correct
+ ** operation of the module, but is necessary to ensure that some tests
+ ** designed to produce hash table collisions really do work. */
+ for(i=nTerm-1; i>=0; i--){
+ hash = (hash << 3) ^ hash ^ pTerm[i];
+ }
+ hash = (hash << 3) ^ hash ^ iIdx;
+ hash = hash % ArraySize(p->apHash);
+
+ for(pEntry=p->apHash[hash]; pEntry; pEntry=pEntry->pNext){
+ if( pEntry->iIdx==iIdx
+ && pEntry->nTerm==nTerm
+ && memcmp(pEntry->pTerm, pTerm, nTerm)==0
+ ){
+ *pbPresent = 1;
+ break;
+ }
+ }
+
+ if( pEntry==0 ){
+ pEntry = sqlite3Fts5MallocZero(&rc, sizeof(Fts5TermsetEntry) + nTerm);
+ if( pEntry ){
+ pEntry->pTerm = (char*)&pEntry[1];
+ pEntry->nTerm = nTerm;
+ pEntry->iIdx = iIdx;
+ memcpy(pEntry->pTerm, pTerm, nTerm);
+ pEntry->pNext = p->apHash[hash];
+ p->apHash[hash] = pEntry;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static void sqlite3Fts5TermsetFree(Fts5Termset *p){
+ if( p ){
+ u32 i;
+ for(i=0; i<ArraySize(p->apHash); i++){
+ Fts5TermsetEntry *pEntry = p->apHash[i];
+ while( pEntry ){
+ Fts5TermsetEntry *pDel = pEntry;
+ pEntry = pEntry->pNext;
+ sqlite3_free(pDel);
+ }
+ }
+ sqlite3_free(p);
+ }
+}
+
+/*
+** 2014 Jun 09
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This is an SQLite module implementing full-text search.
+*/
+
+
+/* #include "fts5Int.h" */
+
+#define FTS5_DEFAULT_PAGE_SIZE 4050
+#define FTS5_DEFAULT_AUTOMERGE 4
+#define FTS5_DEFAULT_USERMERGE 4
+#define FTS5_DEFAULT_CRISISMERGE 16
+#define FTS5_DEFAULT_HASHSIZE (1024*1024)
+
+/* Maximum allowed page size */
+#define FTS5_MAX_PAGE_SIZE (128*1024)
+
+static int fts5_iswhitespace(char x){
+ return (x==' ');
+}
+
+static int fts5_isopenquote(char x){
+ return (x=='"' || x=='\'' || x=='[' || x=='`');
+}
+
+/*
+** Argument pIn points to a character that is part of a nul-terminated
+** string. Return a pointer to the first character following *pIn in
+** the string that is not a white-space character.
+*/
+static const char *fts5ConfigSkipWhitespace(const char *pIn){
+ const char *p = pIn;
+ if( p ){
+ while( fts5_iswhitespace(*p) ){ p++; }
+ }
+ return p;
+}
+
+/*
+** Argument pIn points to a character that is part of a nul-terminated
+** string. Return a pointer to the first character following *pIn in
+** the string that is not a "bareword" character.
+*/
+static const char *fts5ConfigSkipBareword(const char *pIn){
+ const char *p = pIn;
+ while ( sqlite3Fts5IsBareword(*p) ) p++;
+ if( p==pIn ) p = 0;
+ return p;
+}
+
+static int fts5_isdigit(char a){
+ return (a>='0' && a<='9');
+}
+
+
+
+static const char *fts5ConfigSkipLiteral(const char *pIn){
+ const char *p = pIn;
+ switch( *p ){
+ case 'n': case 'N':
+ if( sqlite3_strnicmp("null", p, 4)==0 ){
+ p = &p[4];
+ }else{
+ p = 0;
+ }
+ break;
+
+ case 'x': case 'X':
+ p++;
+ if( *p=='\'' ){
+ p++;
+ while( (*p>='a' && *p<='f')
+ || (*p>='A' && *p<='F')
+ || (*p>='0' && *p<='9')
+ ){
+ p++;
+ }
+ if( *p=='\'' && 0==((p-pIn)%2) ){
+ p++;
+ }else{
+ p = 0;
+ }
+ }else{
+ p = 0;
+ }
+ break;
+
+ case '\'':
+ p++;
+ while( p ){
+ if( *p=='\'' ){
+ p++;
+ if( *p!='\'' ) break;
+ }
+ p++;
+ if( *p==0 ) p = 0;
+ }
+ break;
+
+ default:
+ /* maybe a number */
+ if( *p=='+' || *p=='-' ) p++;
+ while( fts5_isdigit(*p) ) p++;
+
+ /* At this point, if the literal was an integer, the parse is
+ ** finished. Or, if it is a floating point value, it may continue
+ ** with either a decimal point or an 'E' character. */
+ if( *p=='.' && fts5_isdigit(p[1]) ){
+ p += 2;
+ while( fts5_isdigit(*p) ) p++;
+ }
+ if( p==pIn ) p = 0;
+
+ break;
+ }
+
+ return p;
+}
+
+/*
+** The first character of the string pointed to by argument z is guaranteed
+** to be an open-quote character (see function fts5_isopenquote()).
+**
+** This function searches for the corresponding close-quote character within
+** the string and, if found, dequotes the string in place and adds a new
+** nul-terminator byte.
+**
+** If the close-quote is found, the value returned is the byte offset of
+** the character immediately following it. Or, if the close-quote is not
+** found, -1 is returned. If -1 is returned, the buffer is left in an
+** undefined state.
+*/
+static int fts5Dequote(char *z){
+ char q;
+ int iIn = 1;
+ int iOut = 0;
+ q = z[0];
+
+ /* Set stack variable q to the close-quote character */
+ assert( q=='[' || q=='\'' || q=='"' || q=='`' );
+ if( q=='[' ) q = ']';
+
+ while( ALWAYS(z[iIn]) ){
+ if( z[iIn]==q ){
+ if( z[iIn+1]!=q ){
+ /* Character iIn was the close quote. */
+ iIn++;
+ break;
+ }else{
+ /* Character iIn and iIn+1 form an escaped quote character. Skip
+ ** the input cursor past both and copy a single quote character
+ ** to the output buffer. */
+ iIn += 2;
+ z[iOut++] = q;
+ }
+ }else{
+ z[iOut++] = z[iIn++];
+ }
+ }
+
+ z[iOut] = '\0';
+ return iIn;
+}
+
+/*
+** Convert an SQL-style quoted string into a normal string by removing
+** the quote characters. The conversion is done in-place. If the
+** input does not begin with a quote character, then this routine
+** is a no-op.
+**
+** Examples:
+**
+** "abc" becomes abc
+** 'xyz' becomes xyz
+** [pqr] becomes pqr
+** `mno` becomes mno
+*/
+static void sqlite3Fts5Dequote(char *z){
+ char quote; /* Quote character (if any ) */
+
+ assert( 0==fts5_iswhitespace(z[0]) );
+ quote = z[0];
+ if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){
+ fts5Dequote(z);
+ }
+}
+
+
+struct Fts5Enum {
+ const char *zName;
+ int eVal;
+};
+typedef struct Fts5Enum Fts5Enum;
+
+static int fts5ConfigSetEnum(
+ const Fts5Enum *aEnum,
+ const char *zEnum,
+ int *peVal
+){
+ int nEnum = (int)strlen(zEnum);
+ int i;
+ int iVal = -1;
+
+ for(i=0; aEnum[i].zName; i++){
+ if( sqlite3_strnicmp(aEnum[i].zName, zEnum, nEnum)==0 ){
+ if( iVal>=0 ) return SQLITE_ERROR;
+ iVal = aEnum[i].eVal;
+ }
+ }
+
+ *peVal = iVal;
+ return iVal<0 ? SQLITE_ERROR : SQLITE_OK;
+}
+
+/*
+** Parse a "special" CREATE VIRTUAL TABLE directive and update
+** configuration object pConfig as appropriate.
+**
+** If successful, object pConfig is updated and SQLITE_OK returned. If
+** an error occurs, an SQLite error code is returned and an error message
+** may be left in *pzErr. It is the responsibility of the caller to
+** eventually free any such error message using sqlite3_free().
+*/
+static int fts5ConfigParseSpecial(
+ Fts5Global *pGlobal,
+ Fts5Config *pConfig, /* Configuration object to update */
+ const char *zCmd, /* Special command to parse */
+ const char *zArg, /* Argument to parse */
+ char **pzErr /* OUT: Error message */
+){
+ int rc = SQLITE_OK;
+ int nCmd = (int)strlen(zCmd);
+ if( sqlite3_strnicmp("prefix", zCmd, nCmd)==0 ){
+ const int nByte = sizeof(int) * FTS5_MAX_PREFIX_INDEXES;
+ const char *p;
+ int bFirst = 1;
+ if( pConfig->aPrefix==0 ){
+ pConfig->aPrefix = sqlite3Fts5MallocZero(&rc, nByte);
+ if( rc ) return rc;
+ }
+
+ p = zArg;
+ while( 1 ){
+ int nPre = 0;
+
+ while( p[0]==' ' ) p++;
+ if( bFirst==0 && p[0]==',' ){
+ p++;
+ while( p[0]==' ' ) p++;
+ }else if( p[0]=='\0' ){
+ break;
+ }
+ if( p[0]<'0' || p[0]>'9' ){
+ *pzErr = sqlite3_mprintf("malformed prefix=... directive");
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ if( pConfig->nPrefix==FTS5_MAX_PREFIX_INDEXES ){
+ *pzErr = sqlite3_mprintf(
+ "too many prefix indexes (max %d)", FTS5_MAX_PREFIX_INDEXES
+ );
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ while( p[0]>='0' && p[0]<='9' && nPre<1000 ){
+ nPre = nPre*10 + (p[0] - '0');
+ p++;
+ }
+
+ if( nPre<=0 || nPre>=1000 ){
+ *pzErr = sqlite3_mprintf("prefix length out of range (max 999)");
+ rc = SQLITE_ERROR;
+ break;
+ }
+
+ pConfig->aPrefix[pConfig->nPrefix] = nPre;
+ pConfig->nPrefix++;
+ bFirst = 0;
+ }
+ assert( pConfig->nPrefix<=FTS5_MAX_PREFIX_INDEXES );
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("tokenize", zCmd, nCmd)==0 ){
+ const char *p = (const char*)zArg;
+ int nArg = (int)strlen(zArg) + 1;
+ char **azArg = sqlite3Fts5MallocZero(&rc, sizeof(char*) * nArg);
+ char *pDel = sqlite3Fts5MallocZero(&rc, nArg * 2);
+ char *pSpace = pDel;
+
+ if( azArg && pSpace ){
+ if( pConfig->pTok ){
+ *pzErr = sqlite3_mprintf("multiple tokenize=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ for(nArg=0; p && *p; nArg++){
+ const char *p2 = fts5ConfigSkipWhitespace(p);
+ if( *p2=='\'' ){
+ p = fts5ConfigSkipLiteral(p2);
+ }else{
+ p = fts5ConfigSkipBareword(p2);
+ }
+ if( p ){
+ memcpy(pSpace, p2, p-p2);
+ azArg[nArg] = pSpace;
+ sqlite3Fts5Dequote(pSpace);
+ pSpace += (p - p2) + 1;
+ p = fts5ConfigSkipWhitespace(p);
+ }
+ }
+ if( p==0 ){
+ *pzErr = sqlite3_mprintf("parse error in tokenize directive");
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5GetTokenizer(pGlobal,
+ (const char**)azArg, nArg, &pConfig->pTok, &pConfig->pTokApi,
+ pzErr
+ );
+ }
+ }
+ }
+
+ sqlite3_free(azArg);
+ sqlite3_free(pDel);
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("content", zCmd, nCmd)==0 ){
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
+ *pzErr = sqlite3_mprintf("multiple content=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ if( zArg[0] ){
+ pConfig->eContent = FTS5_CONTENT_EXTERNAL;
+ pConfig->zContent = sqlite3Fts5Mprintf(&rc, "%Q.%Q", pConfig->zDb,zArg);
+ }else{
+ pConfig->eContent = FTS5_CONTENT_NONE;
+ }
+ }
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("content_rowid", zCmd, nCmd)==0 ){
+ if( pConfig->zContentRowid ){
+ *pzErr = sqlite3_mprintf("multiple content_rowid=... directives");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->zContentRowid = sqlite3Fts5Strndup(&rc, zArg, -1);
+ }
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("columnsize", zCmd, nCmd)==0 ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
+ *pzErr = sqlite3_mprintf("malformed columnsize=... directive");
+ rc = SQLITE_ERROR;
+ }else{
+ pConfig->bColumnsize = (zArg[0]=='1');
+ }
+ return rc;
+ }
+
+ if( sqlite3_strnicmp("detail", zCmd, nCmd)==0 ){
+ const Fts5Enum aDetail[] = {
+ { "none", FTS5_DETAIL_NONE },
+ { "full", FTS5_DETAIL_FULL },
+ { "columns", FTS5_DETAIL_COLUMNS },
+ { 0, 0 }
+ };
+
+ if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
+ *pzErr = sqlite3_mprintf("malformed detail=... directive");
+ }
+ return rc;
+ }
+
+ *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
+ return SQLITE_ERROR;
+}
+
+/*
+** Allocate an instance of the default tokenizer ("simple") at
+** Fts5Config.pTokenizer. Return SQLITE_OK if successful, or an SQLite error
+** code if an error occurs.
+*/
+static int fts5ConfigDefaultTokenizer(Fts5Global *pGlobal, Fts5Config *pConfig){
+ assert( pConfig->pTok==0 && pConfig->pTokApi==0 );
+ return sqlite3Fts5GetTokenizer(
+ pGlobal, 0, 0, &pConfig->pTok, &pConfig->pTokApi, 0
+ );
+}
+
+/*
+** Gobble up the first bareword or quoted word from the input buffer zIn.
+** Return a pointer to the character immediately following the last in
+** the gobbled word if successful, or a NULL pointer otherwise (failed
+** to find close-quote character).
+**
+** Before returning, set pzOut to point to a new buffer containing a
+** nul-terminated, dequoted copy of the gobbled word. If the word was
+** quoted, *pbQuoted is also set to 1 before returning.
+**
+** If *pRc is other than SQLITE_OK when this function is called, it is
+** a no-op (NULL is returned). Otherwise, if an OOM occurs within this
+** function, *pRc is set to SQLITE_NOMEM before returning. *pRc is *not*
+** set if a parse error (failed to find close quote) occurs.
+*/
+static const char *fts5ConfigGobbleWord(
+ int *pRc, /* IN/OUT: Error code */
+ const char *zIn, /* Buffer to gobble string/bareword from */
+ char **pzOut, /* OUT: malloc'd buffer containing str/bw */
+ int *pbQuoted /* OUT: Set to true if dequoting required */
+){
+ const char *zRet = 0;
+
+ int nIn = (int)strlen(zIn);
+ char *zOut = sqlite3_malloc(nIn+1);
+
+ assert( *pRc==SQLITE_OK );
+ *pbQuoted = 0;
+ *pzOut = 0;
+
+ if( zOut==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ memcpy(zOut, zIn, nIn+1);
+ if( fts5_isopenquote(zOut[0]) ){
+ int ii = fts5Dequote(zOut);
+ zRet = &zIn[ii];
+ *pbQuoted = 1;
+ }else{
+ zRet = fts5ConfigSkipBareword(zIn);
+ if( zRet ){
+ zOut[zRet-zIn] = '\0';
+ }
+ }
+ }
+
+ if( zRet==0 ){
+ sqlite3_free(zOut);
+ }else{
+ *pzOut = zOut;
+ }
+
+ return zRet;
+}
+
+static int fts5ConfigParseColumn(
+ Fts5Config *p,
+ char *zCol,
+ char *zArg,
+ char **pzErr
+){
+ int rc = SQLITE_OK;
+ if( 0==sqlite3_stricmp(zCol, FTS5_RANK_NAME)
+ || 0==sqlite3_stricmp(zCol, FTS5_ROWID_NAME)
+ ){
+ *pzErr = sqlite3_mprintf("reserved fts5 column name: %s", zCol);
+ rc = SQLITE_ERROR;
+ }else if( zArg ){
+ if( 0==sqlite3_stricmp(zArg, "unindexed") ){
+ p->abUnindexed[p->nCol] = 1;
+ }else{
+ *pzErr = sqlite3_mprintf("unrecognized column option: %s", zArg);
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ p->azCol[p->nCol++] = zCol;
+ return rc;
+}
+
+/*
+** Populate the Fts5Config.zContentExprlist string.
+*/
+static int fts5ConfigMakeExprlist(Fts5Config *p){
+ int i;
+ int rc = SQLITE_OK;
+ Fts5Buffer buf = {0, 0, 0};
+
+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, "T.%Q", p->zContentRowid);
+ if( p->eContent!=FTS5_CONTENT_NONE ){
+ for(i=0; i<p->nCol; i++){
+ if( p->eContent==FTS5_CONTENT_EXTERNAL ){
+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.%Q", p->azCol[i]);
+ }else{
+ sqlite3Fts5BufferAppendPrintf(&rc, &buf, ", T.c%d", i);
+ }
+ }
+ }
+
+ assert( p->zContentExprlist==0 );
+ p->zContentExprlist = (char*)buf.p;
+ return rc;
+}
+
+/*
+** Arguments nArg/azArg contain the string arguments passed to the xCreate
+** or xConnect method of the virtual table. This function attempts to
+** allocate an instance of Fts5Config containing the results of parsing
+** those arguments.
+**
+** If successful, SQLITE_OK is returned and *ppOut is set to point to the
+** new Fts5Config object. If an error occurs, an SQLite error code is
+** returned, *ppOut is set to NULL and an error message may be left in
+** *pzErr. It is the responsibility of the caller to eventually free any
+** such error message using sqlite3_free().
+*/
+static int sqlite3Fts5ConfigParse(
+ Fts5Global *pGlobal,
+ sqlite3 *db,
+ int nArg, /* Number of arguments */
+ const char **azArg, /* Array of nArg CREATE VIRTUAL TABLE args */
+ Fts5Config **ppOut, /* OUT: Results of parse */
+ char **pzErr /* OUT: Error message */
+){
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Config *pRet; /* New object to return */
+ int i;
+ int nByte;
+
+ *ppOut = pRet = (Fts5Config*)sqlite3_malloc(sizeof(Fts5Config));
+ if( pRet==0 ) return SQLITE_NOMEM;
+ memset(pRet, 0, sizeof(Fts5Config));
+ pRet->db = db;
+ pRet->iCookie = -1;
+
+ nByte = nArg * (sizeof(char*) + sizeof(u8));
+ pRet->azCol = (char**)sqlite3Fts5MallocZero(&rc, nByte);
+ pRet->abUnindexed = (u8*)&pRet->azCol[nArg];
+ pRet->zDb = sqlite3Fts5Strndup(&rc, azArg[1], -1);
+ pRet->zName = sqlite3Fts5Strndup(&rc, azArg[2], -1);
+ pRet->bColumnsize = 1;
+ pRet->eDetail = FTS5_DETAIL_FULL;
+#ifdef SQLITE_DEBUG
+ pRet->bPrefixIndex = 1;
+#endif
+ if( rc==SQLITE_OK && sqlite3_stricmp(pRet->zName, FTS5_RANK_NAME)==0 ){
+ *pzErr = sqlite3_mprintf("reserved fts5 table name: %s", pRet->zName);
+ rc = SQLITE_ERROR;
+ }
+
+ for(i=3; rc==SQLITE_OK && i<nArg; i++){
+ const char *zOrig = azArg[i];
+ const char *z;
+ char *zOne = 0;
+ char *zTwo = 0;
+ int bOption = 0;
+ int bMustBeCol = 0;
+
+ z = fts5ConfigGobbleWord(&rc, zOrig, &zOne, &bMustBeCol);
+ z = fts5ConfigSkipWhitespace(z);
+ if( z && *z=='=' ){
+ bOption = 1;
+ z++;
+ if( bMustBeCol ) z = 0;
+ }
+ z = fts5ConfigSkipWhitespace(z);
+ if( z && z[0] ){
+ int bDummy;
+ z = fts5ConfigGobbleWord(&rc, z, &zTwo, &bDummy);
+ if( z && z[0] ) z = 0;
+ }
+
+ if( rc==SQLITE_OK ){
+ if( z==0 ){
+ *pzErr = sqlite3_mprintf("parse error in \"%s\"", zOrig);
+ rc = SQLITE_ERROR;
+ }else{
+ if( bOption ){
+ rc = fts5ConfigParseSpecial(pGlobal, pRet, zOne, zTwo?zTwo:"", pzErr);
+ }else{
+ rc = fts5ConfigParseColumn(pRet, zOne, zTwo, pzErr);
+ zOne = 0;
+ }
+ }
+ }
+
+ sqlite3_free(zOne);
+ sqlite3_free(zTwo);
+ }
+
+ /* If a tokenizer= option was successfully parsed, the tokenizer has
+ ** already been allocated. Otherwise, allocate an instance of the default
+ ** tokenizer (unicode61) now. */
+ if( rc==SQLITE_OK && pRet->pTok==0 ){
+ rc = fts5ConfigDefaultTokenizer(pGlobal, pRet);
+ }
+
+ /* If no zContent option was specified, fill in the default values. */
+ if( rc==SQLITE_OK && pRet->zContent==0 ){
+ const char *zTail = 0;
+ assert( pRet->eContent==FTS5_CONTENT_NORMAL
+ || pRet->eContent==FTS5_CONTENT_NONE
+ );
+ if( pRet->eContent==FTS5_CONTENT_NORMAL ){
+ zTail = "content";
+ }else if( pRet->bColumnsize ){
+ zTail = "docsize";
+ }
+
+ if( zTail ){
+ pRet->zContent = sqlite3Fts5Mprintf(
+ &rc, "%Q.'%q_%s'", pRet->zDb, pRet->zName, zTail
+ );
+ }
+ }
+
+ if( rc==SQLITE_OK && pRet->zContentRowid==0 ){
+ pRet->zContentRowid = sqlite3Fts5Strndup(&rc, "rowid", -1);
+ }
+
+ /* Formulate the zContentExprlist text */
+ if( rc==SQLITE_OK ){
+ rc = fts5ConfigMakeExprlist(pRet);
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3Fts5ConfigFree(pRet);
+ *ppOut = 0;
+ }
+ return rc;
+}
+
+/*
+** Free the configuration object passed as the only argument.
+*/
+static void sqlite3Fts5ConfigFree(Fts5Config *pConfig){
+ if( pConfig ){
+ int i;
+ if( pConfig->pTok ){
+ pConfig->pTokApi->xDelete(pConfig->pTok);
+ }
+ sqlite3_free(pConfig->zDb);
+ sqlite3_free(pConfig->zName);
+ for(i=0; i<pConfig->nCol; i++){
+ sqlite3_free(pConfig->azCol[i]);
+ }
+ sqlite3_free(pConfig->azCol);
+ sqlite3_free(pConfig->aPrefix);
+ sqlite3_free(pConfig->zRank);
+ sqlite3_free(pConfig->zRankArgs);
+ sqlite3_free(pConfig->zContent);
+ sqlite3_free(pConfig->zContentRowid);
+ sqlite3_free(pConfig->zContentExprlist);
+ sqlite3_free(pConfig);
+ }
+}
+
+/*
+** Call sqlite3_declare_vtab() based on the contents of the configuration
+** object passed as the only argument. Return SQLITE_OK if successful, or
+** an SQLite error code if an error occurs.
+*/
+static int sqlite3Fts5ConfigDeclareVtab(Fts5Config *pConfig){
+ int i;
+ int rc = SQLITE_OK;
+ char *zSql;
+
+ zSql = sqlite3Fts5Mprintf(&rc, "CREATE TABLE x(");
+ for(i=0; zSql && i<pConfig->nCol; i++){
+ const char *zSep = (i==0?"":", ");
+ zSql = sqlite3Fts5Mprintf(&rc, "%z%s%Q", zSql, zSep, pConfig->azCol[i]);
+ }
+ zSql = sqlite3Fts5Mprintf(&rc, "%z, %Q HIDDEN, %s HIDDEN)",
+ zSql, pConfig->zName, FTS5_RANK_NAME
+ );
+
+ assert( zSql || rc==SQLITE_NOMEM );
+ if( zSql ){
+ rc = sqlite3_declare_vtab(pConfig->db, zSql);
+ sqlite3_free(zSql);
+ }
+
+ return rc;
+}
+
+/*
+** Tokenize the text passed via the second and third arguments.
+**
+** The callback is invoked once for each token in the input text. The
+** arguments passed to it are, in order:
+**
+** void *pCtx // Copy of 4th argument to sqlite3Fts5Tokenize()
+** const char *pToken // Pointer to buffer containing token
+** int nToken // Size of token in bytes
+** int iStart // Byte offset of start of token within input text
+** int iEnd // Byte offset of end of token within input text
+** int iPos // Position of token in input (first token is 0)
+**
+** If the callback returns a non-zero value the tokenization is abandoned
+** and no further callbacks are issued.
+**
+** This function returns SQLITE_OK if successful or an SQLite error code
+** if an error occurs. If the tokenization was abandoned early because
+** the callback returned SQLITE_DONE, this is not an error and this function
+** still returns SQLITE_OK. Or, if the tokenization was abandoned early
+** because the callback returned another non-zero value, it is assumed
+** to be an SQLite error code and returned to the caller.
+*/
+static int sqlite3Fts5Tokenize(
+ Fts5Config *pConfig, /* FTS5 Configuration object */
+ int flags, /* FTS5_TOKENIZE_* flags */
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+){
+ if( pText==0 ) return SQLITE_OK;
+ return pConfig->pTokApi->xTokenize(
+ pConfig->pTok, pCtx, flags, pText, nText, xToken
+ );
+}
+
+/*
+** Argument pIn points to the first character in what is expected to be
+** a comma-separated list of SQL literals followed by a ')' character.
+** If it actually is this, return a pointer to the ')'. Otherwise, return
+** NULL to indicate a parse error.
+*/
+static const char *fts5ConfigSkipArgs(const char *pIn){
+ const char *p = pIn;
+
+ while( 1 ){
+ p = fts5ConfigSkipWhitespace(p);
+ p = fts5ConfigSkipLiteral(p);
+ p = fts5ConfigSkipWhitespace(p);
+ if( p==0 || *p==')' ) break;
+ if( *p!=',' ){
+ p = 0;
+ break;
+ }
+ p++;
+ }
+
+ return p;
+}
+
+/*
+** Parameter zIn contains a rank() function specification. The format of
+** this is:
+**
+** + Bareword (function name)
+** + Open parenthesis - "("
+** + Zero or more SQL literals in a comma separated list
+** + Close parenthesis - ")"
+*/
+static int sqlite3Fts5ConfigParseRank(
+ const char *zIn, /* Input string */
+ char **pzRank, /* OUT: Rank function name */
+ char **pzRankArgs /* OUT: Rank function arguments */
+){
+ const char *p = zIn;
+ const char *pRank;
+ char *zRank = 0;
+ char *zRankArgs = 0;
+ int rc = SQLITE_OK;
+
+ *pzRank = 0;
+ *pzRankArgs = 0;
+
+ if( p==0 ){
+ rc = SQLITE_ERROR;
+ }else{
+ p = fts5ConfigSkipWhitespace(p);
+ pRank = p;
+ p = fts5ConfigSkipBareword(p);
+
+ if( p ){
+ zRank = sqlite3Fts5MallocZero(&rc, 1 + p - pRank);
+ if( zRank ) memcpy(zRank, pRank, p-pRank);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+
+ if( rc==SQLITE_OK ){
+ p = fts5ConfigSkipWhitespace(p);
+ if( *p!='(' ) rc = SQLITE_ERROR;
+ p++;
+ }
+ if( rc==SQLITE_OK ){
+ const char *pArgs;
+ p = fts5ConfigSkipWhitespace(p);
+ pArgs = p;
+ if( *p!=')' ){
+ p = fts5ConfigSkipArgs(p);
+ if( p==0 ){
+ rc = SQLITE_ERROR;
+ }else{
+ zRankArgs = sqlite3Fts5MallocZero(&rc, 1 + p - pArgs);
+ if( zRankArgs ) memcpy(zRankArgs, pArgs, p-pArgs);
+ }
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(zRank);
+ assert( zRankArgs==0 );
+ }else{
+ *pzRank = zRank;
+ *pzRankArgs = zRankArgs;
+ }
+ return rc;
+}
+
+static int sqlite3Fts5ConfigSetValue(
+ Fts5Config *pConfig,
+ const char *zKey,
+ sqlite3_value *pVal,
+ int *pbBadkey
+){
+ int rc = SQLITE_OK;
+
+ if( 0==sqlite3_stricmp(zKey, "pgsz") ){
+ int pgsz = 0;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ pgsz = sqlite3_value_int(pVal);
+ }
+ if( pgsz<=0 || pgsz>FTS5_MAX_PAGE_SIZE ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->pgsz = pgsz;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "hashsize") ){
+ int nHashSize = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nHashSize = sqlite3_value_int(pVal);
+ }
+ if( nHashSize<=0 ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->nHashSize = nHashSize;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "automerge") ){
+ int nAutomerge = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nAutomerge = sqlite3_value_int(pVal);
+ }
+ if( nAutomerge<0 || nAutomerge>64 ){
+ *pbBadkey = 1;
+ }else{
+ if( nAutomerge==1 ) nAutomerge = FTS5_DEFAULT_AUTOMERGE;
+ pConfig->nAutomerge = nAutomerge;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "usermerge") ){
+ int nUsermerge = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nUsermerge = sqlite3_value_int(pVal);
+ }
+ if( nUsermerge<2 || nUsermerge>16 ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->nUsermerge = nUsermerge;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "crisismerge") ){
+ int nCrisisMerge = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ nCrisisMerge = sqlite3_value_int(pVal);
+ }
+ if( nCrisisMerge<0 ){
+ *pbBadkey = 1;
+ }else{
+ if( nCrisisMerge<=1 ) nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
+ pConfig->nCrisisMerge = nCrisisMerge;
+ }
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "rank") ){
+ const char *zIn = (const char*)sqlite3_value_text(pVal);
+ char *zRank;
+ char *zRankArgs;
+ rc = sqlite3Fts5ConfigParseRank(zIn, &zRank, &zRankArgs);
+ if( rc==SQLITE_OK ){
+ sqlite3_free(pConfig->zRank);
+ sqlite3_free(pConfig->zRankArgs);
+ pConfig->zRank = zRank;
+ pConfig->zRankArgs = zRankArgs;
+ }else if( rc==SQLITE_ERROR ){
+ rc = SQLITE_OK;
+ *pbBadkey = 1;
+ }
+ }else{
+ *pbBadkey = 1;
+ }
+ return rc;
+}
+
+/*
+** Load the contents of the %_config table into memory.
+*/
+static int sqlite3Fts5ConfigLoad(Fts5Config *pConfig, int iCookie){
+ const char *zSelect = "SELECT k, v FROM %Q.'%q_config'";
+ char *zSql;
+ sqlite3_stmt *p = 0;
+ int rc = SQLITE_OK;
+ int iVersion = 0;
+
+ /* Set default values */
+ pConfig->pgsz = FTS5_DEFAULT_PAGE_SIZE;
+ pConfig->nAutomerge = FTS5_DEFAULT_AUTOMERGE;
+ pConfig->nUsermerge = FTS5_DEFAULT_USERMERGE;
+ pConfig->nCrisisMerge = FTS5_DEFAULT_CRISISMERGE;
+ pConfig->nHashSize = FTS5_DEFAULT_HASHSIZE;
+
+ zSql = sqlite3Fts5Mprintf(&rc, zSelect, pConfig->zDb, pConfig->zName);
+ if( zSql ){
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p, 0);
+ sqlite3_free(zSql);
+ }
+
+ assert( rc==SQLITE_OK || p==0 );
+ if( rc==SQLITE_OK ){
+ while( SQLITE_ROW==sqlite3_step(p) ){
+ const char *zK = (const char*)sqlite3_column_text(p, 0);
+ sqlite3_value *pVal = sqlite3_column_value(p, 1);
+ if( 0==sqlite3_stricmp(zK, "version") ){
+ iVersion = sqlite3_value_int(pVal);
+ }else{
+ int bDummy = 0;
+ sqlite3Fts5ConfigSetValue(pConfig, zK, pVal, &bDummy);
+ }
+ }
+ rc = sqlite3_finalize(p);
+ }
+
+ if( rc==SQLITE_OK && iVersion!=FTS5_CURRENT_VERSION ){
+ rc = SQLITE_ERROR;
+ if( pConfig->pzErrmsg ){
+ assert( 0==*pConfig->pzErrmsg );
+ *pConfig->pzErrmsg = sqlite3_mprintf(
+ "invalid fts5 file format (found %d, expected %d) - run 'rebuild'",
+ iVersion, FTS5_CURRENT_VERSION
+ );
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pConfig->iCookie = iCookie;
+ }
+ return rc;
+}
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+*/
+
+
+
+/* #include "fts5Int.h" */
+/* #include "fts5parse.h" */
+
+/*
+** All token types in the generated fts5parse.h file are greater than 0.
+*/
+#define FTS5_EOF 0
+
+#define FTS5_LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
+
+typedef struct Fts5ExprTerm Fts5ExprTerm;
+
+/*
+** Functions generated by lemon from fts5parse.y.
+*/
+static void *sqlite3Fts5ParserAlloc(void *(*mallocProc)(u64));
+static void sqlite3Fts5ParserFree(void*, void (*freeProc)(void*));
+static void sqlite3Fts5Parser(void*, int, Fts5Token, Fts5Parse*);
+#ifndef NDEBUG
+/* #include <stdio.h> */
+static void sqlite3Fts5ParserTrace(FILE*, char*);
+#endif
+
+
+struct Fts5Expr {
+ Fts5Index *pIndex;
+ Fts5Config *pConfig;
+ Fts5ExprNode *pRoot;
+ int bDesc; /* Iterate in descending rowid order */
+ int nPhrase; /* Number of phrases in expression */
+ Fts5ExprPhrase **apExprPhrase; /* Pointers to phrase objects */
+};
+
+/*
+** eType:
+** Expression node type. Always one of:
+**
+** FTS5_AND (nChild, apChild valid)
+** FTS5_OR (nChild, apChild valid)
+** FTS5_NOT (nChild, apChild valid)
+** FTS5_STRING (pNear valid)
+** FTS5_TERM (pNear valid)
+*/
+struct Fts5ExprNode {
+ int eType; /* Node type */
+ int bEof; /* True at EOF */
+ int bNomatch; /* True if entry is not a match */
+
+ /* Next method for this node. */
+ int (*xNext)(Fts5Expr*, Fts5ExprNode*, int, i64);
+
+ i64 iRowid; /* Current rowid */
+ Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
+
+ /* Child nodes. For a NOT node, this array always contains 2 entries. For
+ ** AND or OR nodes, it contains 2 or more entries. */
+ int nChild; /* Number of child nodes */
+ Fts5ExprNode *apChild[1]; /* Array of child nodes */
+};
+
+#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
+
+/*
+** Invoke the xNext method of an Fts5ExprNode object. This macro should be
+** used as if it has the same signature as the xNext() methods themselves.
+*/
+#define fts5ExprNodeNext(a,b,c,d) (b)->xNext((a), (b), (c), (d))
+
+/*
+** An instance of the following structure represents a single search term
+** or term prefix.
+*/
+struct Fts5ExprTerm {
+ int bPrefix; /* True for a prefix term */
+ char *zTerm; /* nul-terminated term */
+ Fts5IndexIter *pIter; /* Iterator for this term */
+ Fts5ExprTerm *pSynonym; /* Pointer to first in list of synonyms */
+};
+
+/*
+** A phrase. One or more terms that must appear in a contiguous sequence
+** within a document for it to match.
+*/
+struct Fts5ExprPhrase {
+ Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
+ Fts5Buffer poslist; /* Current position list */
+ int nTerm; /* Number of entries in aTerm[] */
+ Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */
+};
+
+/*
+** One or more phrases that must appear within a certain token distance of
+** each other within each matching document.
+*/
+struct Fts5ExprNearset {
+ int nNear; /* NEAR parameter */
+ Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
+ int nPhrase; /* Number of entries in aPhrase[] array */
+ Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */
+};
+
+
+/*
+** Parse context.
+*/
+struct Fts5Parse {
+ Fts5Config *pConfig;
+ char *zErr;
+ int rc;
+ int nPhrase; /* Size of apPhrase array */
+ Fts5ExprPhrase **apPhrase; /* Array of all phrases */
+ Fts5ExprNode *pExpr; /* Result of a successful parse */
+};
+
+static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...){
+ va_list ap;
+ va_start(ap, zFmt);
+ if( pParse->rc==SQLITE_OK ){
+ pParse->zErr = sqlite3_vmprintf(zFmt, ap);
+ pParse->rc = SQLITE_ERROR;
+ }
+ va_end(ap);
+}
+
+static int fts5ExprIsspace(char t){
+ return t==' ' || t=='\t' || t=='\n' || t=='\r';
+}
+
+/*
+** Read the first token from the nul-terminated string at *pz.
+*/
+static int fts5ExprGetToken(
+ Fts5Parse *pParse,
+ const char **pz, /* IN/OUT: Pointer into buffer */
+ Fts5Token *pToken
+){
+ const char *z = *pz;
+ int tok;
+
+ /* Skip past any whitespace */
+ while( fts5ExprIsspace(*z) ) z++;
+
+ pToken->p = z;
+ pToken->n = 1;
+ switch( *z ){
+ case '(': tok = FTS5_LP; break;
+ case ')': tok = FTS5_RP; break;
+ case '{': tok = FTS5_LCP; break;
+ case '}': tok = FTS5_RCP; break;
+ case ':': tok = FTS5_COLON; break;
+ case ',': tok = FTS5_COMMA; break;
+ case '+': tok = FTS5_PLUS; break;
+ case '*': tok = FTS5_STAR; break;
+ case '-': tok = FTS5_MINUS; break;
+ case '\0': tok = FTS5_EOF; break;
+
+ case '"': {
+ const char *z2;
+ tok = FTS5_STRING;
+
+ for(z2=&z[1]; 1; z2++){
+ if( z2[0]=='"' ){
+ z2++;
+ if( z2[0]!='"' ) break;
+ }
+ if( z2[0]=='\0' ){
+ sqlite3Fts5ParseError(pParse, "unterminated string");
+ return FTS5_EOF;
+ }
+ }
+ pToken->n = (z2 - z);
+ break;
+ }
+
+ default: {
+ const char *z2;
+ if( sqlite3Fts5IsBareword(z[0])==0 ){
+ sqlite3Fts5ParseError(pParse, "fts5: syntax error near \"%.1s\"", z);
+ return FTS5_EOF;
+ }
+ tok = FTS5_STRING;
+ for(z2=&z[1]; sqlite3Fts5IsBareword(*z2); z2++);
+ pToken->n = (z2 - z);
+ if( pToken->n==2 && memcmp(pToken->p, "OR", 2)==0 ) tok = FTS5_OR;
+ if( pToken->n==3 && memcmp(pToken->p, "NOT", 3)==0 ) tok = FTS5_NOT;
+ if( pToken->n==3 && memcmp(pToken->p, "AND", 3)==0 ) tok = FTS5_AND;
+ break;
+ }
+ }
+
+ *pz = &pToken->p[pToken->n];
+ return tok;
+}
+
+static void *fts5ParseAlloc(u64 t){ return sqlite3_malloc((int)t); }
+static void fts5ParseFree(void *p){ sqlite3_free(p); }
+
+static int sqlite3Fts5ExprNew(
+ Fts5Config *pConfig, /* FTS5 Configuration */
+ const char *zExpr, /* Expression text */
+ Fts5Expr **ppNew,
+ char **pzErr
+){
+ Fts5Parse sParse;
+ Fts5Token token;
+ const char *z = zExpr;
+ int t; /* Next token type */
+ void *pEngine;
+ Fts5Expr *pNew;
+
+ *ppNew = 0;
+ *pzErr = 0;
+ memset(&sParse, 0, sizeof(sParse));
+ pEngine = sqlite3Fts5ParserAlloc(fts5ParseAlloc);
+ if( pEngine==0 ){ return SQLITE_NOMEM; }
+ sParse.pConfig = pConfig;
+
+ do {
+ t = fts5ExprGetToken(&sParse, &z, &token);
+ sqlite3Fts5Parser(pEngine, t, token, &sParse);
+ }while( sParse.rc==SQLITE_OK && t!=FTS5_EOF );
+ sqlite3Fts5ParserFree(pEngine, fts5ParseFree);
+
+ assert( sParse.rc!=SQLITE_OK || sParse.zErr==0 );
+ if( sParse.rc==SQLITE_OK ){
+ *ppNew = pNew = sqlite3_malloc(sizeof(Fts5Expr));
+ if( pNew==0 ){
+ sParse.rc = SQLITE_NOMEM;
+ sqlite3Fts5ParseNodeFree(sParse.pExpr);
+ }else{
+ if( !sParse.pExpr ){
+ const int nByte = sizeof(Fts5ExprNode);
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&sParse.rc, nByte);
+ if( pNew->pRoot ){
+ pNew->pRoot->bEof = 1;
+ }
+ }else{
+ pNew->pRoot = sParse.pExpr;
+ }
+ pNew->pIndex = 0;
+ pNew->pConfig = pConfig;
+ pNew->apExprPhrase = sParse.apPhrase;
+ pNew->nPhrase = sParse.nPhrase;
+ sParse.apPhrase = 0;
+ }
+ }else{
+ sqlite3Fts5ParseNodeFree(sParse.pExpr);
+ }
+
+ sqlite3_free(sParse.apPhrase);
+ *pzErr = sParse.zErr;
+ return sParse.rc;
+}
+
+/*
+** Free the expression node object passed as the only argument.
+*/
+static void sqlite3Fts5ParseNodeFree(Fts5ExprNode *p){
+ if( p ){
+ int i;
+ for(i=0; i<p->nChild; i++){
+ sqlite3Fts5ParseNodeFree(p->apChild[i]);
+ }
+ sqlite3Fts5ParseNearsetFree(p->pNear);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Free the expression object passed as the only argument.
+*/
+static void sqlite3Fts5ExprFree(Fts5Expr *p){
+ if( p ){
+ sqlite3Fts5ParseNodeFree(p->pRoot);
+ sqlite3_free(p->apExprPhrase);
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Argument pTerm must be a synonym iterator. Return the current rowid
+** that it points to.
+*/
+static i64 fts5ExprSynonymRowid(Fts5ExprTerm *pTerm, int bDesc, int *pbEof){
+ i64 iRet = 0;
+ int bRetValid = 0;
+ Fts5ExprTerm *p;
+
+ assert( pTerm->pSynonym );
+ assert( bDesc==0 || bDesc==1 );
+ for(p=pTerm; p; p=p->pSynonym){
+ if( 0==sqlite3Fts5IterEof(p->pIter) ){
+ i64 iRowid = p->pIter->iRowid;
+ if( bRetValid==0 || (bDesc!=(iRowid<iRet)) ){
+ iRet = iRowid;
+ bRetValid = 1;
+ }
+ }
+ }
+
+ if( pbEof && bRetValid==0 ) *pbEof = 1;
+ return iRet;
+}
+
+/*
+** Argument pTerm must be a synonym iterator.
+*/
+static int fts5ExprSynonymList(
+ Fts5ExprTerm *pTerm,
+ i64 iRowid,
+ Fts5Buffer *pBuf, /* Use this buffer for space if required */
+ u8 **pa, int *pn
+){
+ Fts5PoslistReader aStatic[4];
+ Fts5PoslistReader *aIter = aStatic;
+ int nIter = 0;
+ int nAlloc = 4;
+ int rc = SQLITE_OK;
+ Fts5ExprTerm *p;
+
+ assert( pTerm->pSynonym );
+ for(p=pTerm; p; p=p->pSynonym){
+ Fts5IndexIter *pIter = p->pIter;
+ if( sqlite3Fts5IterEof(pIter)==0 && pIter->iRowid==iRowid ){
+ if( pIter->nData==0 ) continue;
+ if( nIter==nAlloc ){
+ int nByte = sizeof(Fts5PoslistReader) * nAlloc * 2;
+ Fts5PoslistReader *aNew = (Fts5PoslistReader*)sqlite3_malloc(nByte);
+ if( aNew==0 ){
+ rc = SQLITE_NOMEM;
+ goto synonym_poslist_out;
+ }
+ memcpy(aNew, aIter, sizeof(Fts5PoslistReader) * nIter);
+ nAlloc = nAlloc*2;
+ if( aIter!=aStatic ) sqlite3_free(aIter);
+ aIter = aNew;
+ }
+ sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &aIter[nIter]);
+ assert( aIter[nIter].bEof==0 );
+ nIter++;
+ }
+ }
+
+ if( nIter==1 ){
+ *pa = (u8*)aIter[0].a;
+ *pn = aIter[0].n;
+ }else{
+ Fts5PoslistWriter writer = {0};
+ i64 iPrev = -1;
+ fts5BufferZero(pBuf);
+ while( 1 ){
+ int i;
+ i64 iMin = FTS5_LARGEST_INT64;
+ for(i=0; i<nIter; i++){
+ if( aIter[i].bEof==0 ){
+ if( aIter[i].iPos==iPrev ){
+ if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) continue;
+ }
+ if( aIter[i].iPos<iMin ){
+ iMin = aIter[i].iPos;
+ }
+ }
+ }
+ if( iMin==FTS5_LARGEST_INT64 || rc!=SQLITE_OK ) break;
+ rc = sqlite3Fts5PoslistWriterAppend(pBuf, &writer, iMin);
+ iPrev = iMin;
+ }
+ if( rc==SQLITE_OK ){
+ *pa = pBuf->p;
+ *pn = pBuf->n;
+ }
+ }
+
+ synonym_poslist_out:
+ if( aIter!=aStatic ) sqlite3_free(aIter);
+ return rc;
+}
+
+
+/*
+** All individual term iterators in pPhrase are guaranteed to be valid and
+** pointing to the same rowid when this function is called. This function
+** checks if the current rowid really is a match, and if so populates
+** the pPhrase->poslist buffer accordingly. Output parameter *pbMatch
+** is set to true if this is really a match, or false otherwise.
+**
+** SQLITE_OK is returned if an error occurs, or an SQLite error code
+** otherwise. It is not considered an error code if the current rowid is
+** not a match.
+*/
+static int fts5ExprPhraseIsMatch(
+ Fts5ExprNode *pNode, /* Node pPhrase belongs to */
+ Fts5ExprPhrase *pPhrase, /* Phrase object to initialize */
+ int *pbMatch /* OUT: Set to true if really a match */
+){
+ Fts5PoslistWriter writer = {0};
+ Fts5PoslistReader aStatic[4];
+ Fts5PoslistReader *aIter = aStatic;
+ int i;
+ int rc = SQLITE_OK;
+
+ fts5BufferZero(&pPhrase->poslist);
+
+ /* If the aStatic[] array is not large enough, allocate a large array
+ ** using sqlite3_malloc(). This approach could be improved upon. */
+ if( pPhrase->nTerm>ArraySize(aStatic) ){
+ int nByte = sizeof(Fts5PoslistReader) * pPhrase->nTerm;
+ aIter = (Fts5PoslistReader*)sqlite3_malloc(nByte);
+ if( !aIter ) return SQLITE_NOMEM;
+ }
+ memset(aIter, 0, sizeof(Fts5PoslistReader) * pPhrase->nTerm);
+
+ /* Initialize a term iterator for each term in the phrase */
+ for(i=0; i<pPhrase->nTerm; i++){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
+ int n = 0;
+ int bFlag = 0;
+ u8 *a = 0;
+ if( pTerm->pSynonym ){
+ Fts5Buffer buf = {0, 0, 0};
+ rc = fts5ExprSynonymList(pTerm, pNode->iRowid, &buf, &a, &n);
+ if( rc ){
+ sqlite3_free(a);
+ goto ismatch_out;
+ }
+ if( a==buf.p ) bFlag = 1;
+ }else{
+ a = (u8*)pTerm->pIter->pData;
+ n = pTerm->pIter->nData;
+ }
+ sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
+ aIter[i].bFlag = (u8)bFlag;
+ if( aIter[i].bEof ) goto ismatch_out;
+ }
+
+ while( 1 ){
+ int bMatch;
+ i64 iPos = aIter[0].iPos;
+ do {
+ bMatch = 1;
+ for(i=0; i<pPhrase->nTerm; i++){
+ Fts5PoslistReader *pPos = &aIter[i];
+ i64 iAdj = iPos + i;
+ if( pPos->iPos!=iAdj ){
+ bMatch = 0;
+ while( pPos->iPos<iAdj ){
+ if( sqlite3Fts5PoslistReaderNext(pPos) ) goto ismatch_out;
+ }
+ if( pPos->iPos>iAdj ) iPos = pPos->iPos-i;
+ }
+ }
+ }while( bMatch==0 );
+
+ /* Append position iPos to the output */
+ rc = sqlite3Fts5PoslistWriterAppend(&pPhrase->poslist, &writer, iPos);
+ if( rc!=SQLITE_OK ) goto ismatch_out;
+
+ for(i=0; i<pPhrase->nTerm; i++){
+ if( sqlite3Fts5PoslistReaderNext(&aIter[i]) ) goto ismatch_out;
+ }
+ }
+
+ ismatch_out:
+ *pbMatch = (pPhrase->poslist.n>0);
+ for(i=0; i<pPhrase->nTerm; i++){
+ if( aIter[i].bFlag ) sqlite3_free((u8*)aIter[i].a);
+ }
+ if( aIter!=aStatic ) sqlite3_free(aIter);
+ return rc;
+}
+
+typedef struct Fts5LookaheadReader Fts5LookaheadReader;
+struct Fts5LookaheadReader {
+ const u8 *a; /* Buffer containing position list */
+ int n; /* Size of buffer a[] in bytes */
+ int i; /* Current offset in position list */
+ i64 iPos; /* Current position */
+ i64 iLookahead; /* Next position */
+};
+
+#define FTS5_LOOKAHEAD_EOF (((i64)1) << 62)
+
+static int fts5LookaheadReaderNext(Fts5LookaheadReader *p){
+ p->iPos = p->iLookahead;
+ if( sqlite3Fts5PoslistNext64(p->a, p->n, &p->i, &p->iLookahead) ){
+ p->iLookahead = FTS5_LOOKAHEAD_EOF;
+ }
+ return (p->iPos==FTS5_LOOKAHEAD_EOF);
+}
+
+static int fts5LookaheadReaderInit(
+ const u8 *a, int n, /* Buffer to read position list from */
+ Fts5LookaheadReader *p /* Iterator object to initialize */
+){
+ memset(p, 0, sizeof(Fts5LookaheadReader));
+ p->a = a;
+ p->n = n;
+ fts5LookaheadReaderNext(p);
+ return fts5LookaheadReaderNext(p);
+}
+
+typedef struct Fts5NearTrimmer Fts5NearTrimmer;
+struct Fts5NearTrimmer {
+ Fts5LookaheadReader reader; /* Input iterator */
+ Fts5PoslistWriter writer; /* Writer context */
+ Fts5Buffer *pOut; /* Output poslist */
+};
+
+/*
+** The near-set object passed as the first argument contains more than
+** one phrase. All phrases currently point to the same row. The
+** Fts5ExprPhrase.poslist buffers are populated accordingly. This function
+** tests if the current row contains instances of each phrase sufficiently
+** close together to meet the NEAR constraint. Non-zero is returned if it
+** does, or zero otherwise.
+**
+** If in/out parameter (*pRc) is set to other than SQLITE_OK when this
+** function is called, it is a no-op. Or, if an error (e.g. SQLITE_NOMEM)
+** occurs within this function (*pRc) is set accordingly before returning.
+** The return value is undefined in both these cases.
+**
+** If no error occurs and non-zero (a match) is returned, the position-list
+** of each phrase object is edited to contain only those entries that
+** meet the constraint before returning.
+*/
+static int fts5ExprNearIsMatch(int *pRc, Fts5ExprNearset *pNear){
+ Fts5NearTrimmer aStatic[4];
+ Fts5NearTrimmer *a = aStatic;
+ Fts5ExprPhrase **apPhrase = pNear->apPhrase;
+
+ int i;
+ int rc = *pRc;
+ int bMatch;
+
+ assert( pNear->nPhrase>1 );
+
+ /* If the aStatic[] array is not large enough, allocate a large array
+ ** using sqlite3_malloc(). This approach could be improved upon. */
+ if( pNear->nPhrase>ArraySize(aStatic) ){
+ int nByte = sizeof(Fts5NearTrimmer) * pNear->nPhrase;
+ a = (Fts5NearTrimmer*)sqlite3Fts5MallocZero(&rc, nByte);
+ }else{
+ memset(aStatic, 0, sizeof(aStatic));
+ }
+ if( rc!=SQLITE_OK ){
+ *pRc = rc;
+ return 0;
+ }
+
+ /* Initialize a lookahead iterator for each phrase. After passing the
+ ** buffer and buffer size to the lookaside-reader init function, zero
+ ** the phrase poslist buffer. The new poslist for the phrase (containing
+ ** the same entries as the original with some entries removed on account
+ ** of the NEAR constraint) is written over the original even as it is
+ ** being read. This is safe as the entries for the new poslist are a
+ ** subset of the old, so it is not possible for data yet to be read to
+ ** be overwritten. */
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5Buffer *pPoslist = &apPhrase[i]->poslist;
+ fts5LookaheadReaderInit(pPoslist->p, pPoslist->n, &a[i].reader);
+ pPoslist->n = 0;
+ a[i].pOut = pPoslist;
+ }
+
+ while( 1 ){
+ int iAdv;
+ i64 iMin;
+ i64 iMax;
+
+ /* This block advances the phrase iterators until they point to a set of
+ ** entries that together comprise a match. */
+ iMax = a[0].reader.iPos;
+ do {
+ bMatch = 1;
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5LookaheadReader *pPos = &a[i].reader;
+ iMin = iMax - pNear->apPhrase[i]->nTerm - pNear->nNear;
+ if( pPos->iPos<iMin || pPos->iPos>iMax ){
+ bMatch = 0;
+ while( pPos->iPos<iMin ){
+ if( fts5LookaheadReaderNext(pPos) ) goto ismatch_out;
+ }
+ if( pPos->iPos>iMax ) iMax = pPos->iPos;
+ }
+ }
+ }while( bMatch==0 );
+
+ /* Add an entry to each output position list */
+ for(i=0; i<pNear->nPhrase; i++){
+ i64 iPos = a[i].reader.iPos;
+ Fts5PoslistWriter *pWriter = &a[i].writer;
+ if( a[i].pOut->n==0 || iPos!=pWriter->iPrev ){
+ sqlite3Fts5PoslistWriterAppend(a[i].pOut, pWriter, iPos);
+ }
+ }
+
+ iAdv = 0;
+ iMin = a[0].reader.iLookahead;
+ for(i=0; i<pNear->nPhrase; i++){
+ if( a[i].reader.iLookahead < iMin ){
+ iMin = a[i].reader.iLookahead;
+ iAdv = i;
+ }
+ }
+ if( fts5LookaheadReaderNext(&a[iAdv].reader) ) goto ismatch_out;
+ }
+
+ ismatch_out: {
+ int bRet = a[0].pOut->n>0;
+ *pRc = rc;
+ if( a!=aStatic ) sqlite3_free(a);
+ return bRet;
+ }
+}
+
+/*
+** Advance iterator pIter until it points to a value equal to or laster
+** than the initial value of *piLast. If this means the iterator points
+** to a value laster than *piLast, update *piLast to the new lastest value.
+**
+** If the iterator reaches EOF, set *pbEof to true before returning. If
+** an error occurs, set *pRc to an error code. If either *pbEof or *pRc
+** are set, return a non-zero value. Otherwise, return zero.
+*/
+static int fts5ExprAdvanceto(
+ Fts5IndexIter *pIter, /* Iterator to advance */
+ int bDesc, /* True if iterator is "rowid DESC" */
+ i64 *piLast, /* IN/OUT: Lastest rowid seen so far */
+ int *pRc, /* OUT: Error code */
+ int *pbEof /* OUT: Set to true if EOF */
+){
+ i64 iLast = *piLast;
+ i64 iRowid;
+
+ iRowid = pIter->iRowid;
+ if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
+ int rc = sqlite3Fts5IterNextFrom(pIter, iLast);
+ if( rc || sqlite3Fts5IterEof(pIter) ){
+ *pRc = rc;
+ *pbEof = 1;
+ return 1;
+ }
+ iRowid = pIter->iRowid;
+ assert( (bDesc==0 && iRowid>=iLast) || (bDesc==1 && iRowid<=iLast) );
+ }
+ *piLast = iRowid;
+
+ return 0;
+}
+
+static int fts5ExprSynonymAdvanceto(
+ Fts5ExprTerm *pTerm, /* Term iterator to advance */
+ int bDesc, /* True if iterator is "rowid DESC" */
+ i64 *piLast, /* IN/OUT: Lastest rowid seen so far */
+ int *pRc /* OUT: Error code */
+){
+ int rc = SQLITE_OK;
+ i64 iLast = *piLast;
+ Fts5ExprTerm *p;
+ int bEof = 0;
+
+ for(p=pTerm; rc==SQLITE_OK && p; p=p->pSynonym){
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ i64 iRowid = p->pIter->iRowid;
+ if( (bDesc==0 && iLast>iRowid) || (bDesc && iLast<iRowid) ){
+ rc = sqlite3Fts5IterNextFrom(p->pIter, iLast);
+ }
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ *pRc = rc;
+ bEof = 1;
+ }else{
+ *piLast = fts5ExprSynonymRowid(pTerm, bDesc, &bEof);
+ }
+ return bEof;
+}
+
+
+static int fts5ExprNearTest(
+ int *pRc,
+ Fts5Expr *pExpr, /* Expression that pNear is a part of */
+ Fts5ExprNode *pNode /* The "NEAR" node (FTS5_STRING) */
+){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ int rc = *pRc;
+
+ if( pExpr->pConfig->eDetail!=FTS5_DETAIL_FULL ){
+ Fts5ExprTerm *pTerm;
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[0];
+ pPhrase->poslist.n = 0;
+ for(pTerm=&pPhrase->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
+ Fts5IndexIter *pIter = pTerm->pIter;
+ if( sqlite3Fts5IterEof(pIter)==0 ){
+ if( pIter->iRowid==pNode->iRowid && pIter->nData>0 ){
+ pPhrase->poslist.n = 1;
+ }
+ }
+ }
+ return pPhrase->poslist.n;
+ }else{
+ int i;
+
+ /* Check that each phrase in the nearset matches the current row.
+ ** Populate the pPhrase->poslist buffers at the same time. If any
+ ** phrase is not a match, break out of the loop early. */
+ for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ if( pPhrase->nTerm>1 || pPhrase->aTerm[0].pSynonym || pNear->pColset ){
+ int bMatch = 0;
+ rc = fts5ExprPhraseIsMatch(pNode, pPhrase, &bMatch);
+ if( bMatch==0 ) break;
+ }else{
+ Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
+ fts5BufferSet(&rc, &pPhrase->poslist, pIter->nData, pIter->pData);
+ }
+ }
+
+ *pRc = rc;
+ if( i==pNear->nPhrase && (i==1 || fts5ExprNearIsMatch(pRc, pNear)) ){
+ return 1;
+ }
+ return 0;
+ }
+}
+
+
+/*
+** Initialize all term iterators in the pNear object. If any term is found
+** to match no documents at all, return immediately without initializing any
+** further iterators.
+*/
+static int fts5ExprNearInitAll(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode
+){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ int i, j;
+ int rc = SQLITE_OK;
+ int bEof = 1;
+
+ assert( pNode->bNomatch==0 );
+ for(i=0; rc==SQLITE_OK && i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ for(j=0; j<pPhrase->nTerm; j++){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
+ Fts5ExprTerm *p;
+
+ for(p=pTerm; p && rc==SQLITE_OK; p=p->pSynonym){
+ if( p->pIter ){
+ sqlite3Fts5IterClose(p->pIter);
+ p->pIter = 0;
+ }
+ rc = sqlite3Fts5IndexQuery(
+ pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
+ (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
+ (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
+ pNear->pColset,
+ &p->pIter
+ );
+ assert( rc==SQLITE_OK || p->pIter==0 );
+ if( p->pIter && 0==sqlite3Fts5IterEof(p->pIter) ){
+ bEof = 0;
+ }
+ }
+
+ if( bEof ) break;
+ }
+ if( bEof ) break;
+ }
+
+ pNode->bEof = bEof;
+ return rc;
+}
+
+/*
+** If pExpr is an ASC iterator, this function returns a value with the
+** same sign as:
+**
+** (iLhs - iRhs)
+**
+** Otherwise, if this is a DESC iterator, the opposite is returned:
+**
+** (iRhs - iLhs)
+*/
+static int fts5RowidCmp(
+ Fts5Expr *pExpr,
+ i64 iLhs,
+ i64 iRhs
+){
+ assert( pExpr->bDesc==0 || pExpr->bDesc==1 );
+ if( pExpr->bDesc==0 ){
+ if( iLhs<iRhs ) return -1;
+ return (iLhs > iRhs);
+ }else{
+ if( iLhs>iRhs ) return -1;
+ return (iLhs < iRhs);
+ }
+}
+
+static void fts5ExprSetEof(Fts5ExprNode *pNode){
+ int i;
+ pNode->bEof = 1;
+ pNode->bNomatch = 0;
+ for(i=0; i<pNode->nChild; i++){
+ fts5ExprSetEof(pNode->apChild[i]);
+ }
+}
+
+static void fts5ExprNodeZeroPoslist(Fts5ExprNode *pNode){
+ if( pNode->eType==FTS5_STRING || pNode->eType==FTS5_TERM ){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ int i;
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ pPhrase->poslist.n = 0;
+ }
+ }else{
+ int i;
+ for(i=0; i<pNode->nChild; i++){
+ fts5ExprNodeZeroPoslist(pNode->apChild[i]);
+ }
+ }
+}
+
+
+
+/*
+** Compare the values currently indicated by the two nodes as follows:
+**
+** res = (*p1) - (*p2)
+**
+** Nodes that point to values that come later in the iteration order are
+** considered to be larger. Nodes at EOF are the largest of all.
+**
+** This means that if the iteration order is ASC, then numerically larger
+** rowids are considered larger. Or if it is the default DESC, numerically
+** smaller rowids are larger.
+*/
+static int fts5NodeCompare(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *p1,
+ Fts5ExprNode *p2
+){
+ if( p2->bEof ) return -1;
+ if( p1->bEof ) return +1;
+ return fts5RowidCmp(pExpr, p1->iRowid, p2->iRowid);
+}
+
+/*
+** All individual term iterators in pNear are guaranteed to be valid when
+** this function is called. This function checks if all term iterators
+** point to the same rowid, and if not, advances them until they do.
+** If an EOF is reached before this happens, *pbEof is set to true before
+** returning.
+**
+** SQLITE_OK is returned if an error occurs, or an SQLite error code
+** otherwise. It is not considered an error code if an iterator reaches
+** EOF.
+*/
+static int fts5ExprNodeTest_STRING(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode
+){
+ Fts5ExprNearset *pNear = pNode->pNear;
+ Fts5ExprPhrase *pLeft = pNear->apPhrase[0];
+ int rc = SQLITE_OK;
+ i64 iLast; /* Lastest rowid any iterator points to */
+ int i, j; /* Phrase and token index, respectively */
+ int bMatch; /* True if all terms are at the same rowid */
+ const int bDesc = pExpr->bDesc;
+
+ /* Check that this node should not be FTS5_TERM */
+ assert( pNear->nPhrase>1
+ || pNear->apPhrase[0]->nTerm>1
+ || pNear->apPhrase[0]->aTerm[0].pSynonym
+ );
+
+ /* Initialize iLast, the "lastest" rowid any iterator points to. If the
+ ** iterator skips through rowids in the default ascending order, this means
+ ** the maximum rowid. Or, if the iterator is "ORDER BY rowid DESC", then it
+ ** means the minimum rowid. */
+ if( pLeft->aTerm[0].pSynonym ){
+ iLast = fts5ExprSynonymRowid(&pLeft->aTerm[0], bDesc, 0);
+ }else{
+ iLast = pLeft->aTerm[0].pIter->iRowid;
+ }
+
+ do {
+ bMatch = 1;
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ for(j=0; j<pPhrase->nTerm; j++){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[j];
+ if( pTerm->pSynonym ){
+ i64 iRowid = fts5ExprSynonymRowid(pTerm, bDesc, 0);
+ if( iRowid==iLast ) continue;
+ bMatch = 0;
+ if( fts5ExprSynonymAdvanceto(pTerm, bDesc, &iLast, &rc) ){
+ pNode->bNomatch = 0;
+ pNode->bEof = 1;
+ return rc;
+ }
+ }else{
+ Fts5IndexIter *pIter = pPhrase->aTerm[j].pIter;
+ if( pIter->iRowid==iLast ) continue;
+ bMatch = 0;
+ if( fts5ExprAdvanceto(pIter, bDesc, &iLast, &rc, &pNode->bEof) ){
+ return rc;
+ }
+ }
+ }
+ }
+ }while( bMatch==0 );
+
+ pNode->iRowid = iLast;
+ pNode->bNomatch = ((0==fts5ExprNearTest(&rc, pExpr, pNode)) && rc==SQLITE_OK);
+ assert( pNode->bEof==0 || pNode->bNomatch==0 );
+
+ return rc;
+}
+
+/*
+** Advance the first term iterator in the first phrase of pNear. Set output
+** variable *pbEof to true if it reaches EOF or if an error occurs.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5ExprNodeNext_STRING(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode, /* FTS5_STRING or FTS5_TERM node */
+ int bFromValid,
+ i64 iFrom
+){
+ Fts5ExprTerm *pTerm = &pNode->pNear->apPhrase[0]->aTerm[0];
+ int rc = SQLITE_OK;
+
+ pNode->bNomatch = 0;
+ if( pTerm->pSynonym ){
+ int bEof = 1;
+ Fts5ExprTerm *p;
+
+ /* Find the firstest rowid any synonym points to. */
+ i64 iRowid = fts5ExprSynonymRowid(pTerm, pExpr->bDesc, 0);
+
+ /* Advance each iterator that currently points to iRowid. Or, if iFrom
+ ** is valid - each iterator that points to a rowid before iFrom. */
+ for(p=pTerm; p; p=p->pSynonym){
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ i64 ii = p->pIter->iRowid;
+ if( ii==iRowid
+ || (bFromValid && ii!=iFrom && (ii>iFrom)==pExpr->bDesc)
+ ){
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(p->pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(p->pIter);
+ }
+ if( rc!=SQLITE_OK ) break;
+ if( sqlite3Fts5IterEof(p->pIter)==0 ){
+ bEof = 0;
+ }
+ }else{
+ bEof = 0;
+ }
+ }
+ }
+
+ /* Set the EOF flag if either all synonym iterators are at EOF or an
+ ** error has occurred. */
+ pNode->bEof = (rc || bEof);
+ }else{
+ Fts5IndexIter *pIter = pTerm->pIter;
+
+ assert( Fts5NodeIsString(pNode) );
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+
+ pNode->bEof = (rc || sqlite3Fts5IterEof(pIter));
+ }
+
+ if( pNode->bEof==0 ){
+ assert( rc==SQLITE_OK );
+ rc = fts5ExprNodeTest_STRING(pExpr, pNode);
+ }
+
+ return rc;
+}
+
+
+static int fts5ExprNodeTest_TERM(
+ Fts5Expr *pExpr, /* Expression that pNear is a part of */
+ Fts5ExprNode *pNode /* The "NEAR" node (FTS5_TERM) */
+){
+ /* As this "NEAR" object is actually a single phrase that consists
+ ** of a single term only, grab pointers into the poslist managed by the
+ ** fts5_index.c iterator object. This is much faster than synthesizing
+ ** a new poslist the way we have to for more complicated phrase or NEAR
+ ** expressions. */
+ Fts5ExprPhrase *pPhrase = pNode->pNear->apPhrase[0];
+ Fts5IndexIter *pIter = pPhrase->aTerm[0].pIter;
+
+ assert( pNode->eType==FTS5_TERM );
+ assert( pNode->pNear->nPhrase==1 && pPhrase->nTerm==1 );
+ assert( pPhrase->aTerm[0].pSynonym==0 );
+
+ pPhrase->poslist.n = pIter->nData;
+ if( pExpr->pConfig->eDetail==FTS5_DETAIL_FULL ){
+ pPhrase->poslist.p = (u8*)pIter->pData;
+ }
+ pNode->iRowid = pIter->iRowid;
+ pNode->bNomatch = (pPhrase->poslist.n==0);
+ return SQLITE_OK;
+}
+
+/*
+** xNext() method for a node of type FTS5_TERM.
+*/
+static int fts5ExprNodeNext_TERM(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc;
+ Fts5IndexIter *pIter = pNode->pNear->apPhrase[0]->aTerm[0].pIter;
+
+ assert( pNode->bEof==0 );
+ if( bFromValid ){
+ rc = sqlite3Fts5IterNextFrom(pIter, iFrom);
+ }else{
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+ if( rc==SQLITE_OK && sqlite3Fts5IterEof(pIter)==0 ){
+ rc = fts5ExprNodeTest_TERM(pExpr, pNode);
+ }else{
+ pNode->bEof = 1;
+ pNode->bNomatch = 0;
+ }
+ return rc;
+}
+
+static void fts5ExprNodeTest_OR(
+ Fts5Expr *pExpr, /* Expression of which pNode is a part */
+ Fts5ExprNode *pNode /* Expression node to test */
+){
+ Fts5ExprNode *pNext = pNode->apChild[0];
+ int i;
+
+ for(i=1; i<pNode->nChild; i++){
+ Fts5ExprNode *pChild = pNode->apChild[i];
+ int cmp = fts5NodeCompare(pExpr, pNext, pChild);
+ if( cmp>0 || (cmp==0 && pChild->bNomatch==0) ){
+ pNext = pChild;
+ }
+ }
+ pNode->iRowid = pNext->iRowid;
+ pNode->bEof = pNext->bEof;
+ pNode->bNomatch = pNext->bNomatch;
+}
+
+static int fts5ExprNodeNext_OR(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int i;
+ i64 iLast = pNode->iRowid;
+
+ for(i=0; i<pNode->nChild; i++){
+ Fts5ExprNode *p1 = pNode->apChild[i];
+ assert( p1->bEof || fts5RowidCmp(pExpr, p1->iRowid, iLast)>=0 );
+ if( p1->bEof==0 ){
+ if( (p1->iRowid==iLast)
+ || (bFromValid && fts5RowidCmp(pExpr, p1->iRowid, iFrom)<0)
+ ){
+ int rc = fts5ExprNodeNext(pExpr, p1, bFromValid, iFrom);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ }
+ }
+
+ fts5ExprNodeTest_OR(pExpr, pNode);
+ return SQLITE_OK;
+}
+
+/*
+** Argument pNode is an FTS5_AND node.
+*/
+static int fts5ExprNodeTest_AND(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pAnd /* FTS5_AND node to advance */
+){
+ int iChild;
+ i64 iLast = pAnd->iRowid;
+ int rc = SQLITE_OK;
+ int bMatch;
+
+ assert( pAnd->bEof==0 );
+ do {
+ pAnd->bNomatch = 0;
+ bMatch = 1;
+ for(iChild=0; iChild<pAnd->nChild; iChild++){
+ Fts5ExprNode *pChild = pAnd->apChild[iChild];
+ int cmp = fts5RowidCmp(pExpr, iLast, pChild->iRowid);
+ if( cmp>0 ){
+ /* Advance pChild until it points to iLast or laster */
+ rc = fts5ExprNodeNext(pExpr, pChild, 1, iLast);
+ if( rc!=SQLITE_OK ) return rc;
+ }
+
+ /* If the child node is now at EOF, so is the parent AND node. Otherwise,
+ ** the child node is guaranteed to have advanced at least as far as
+ ** rowid iLast. So if it is not at exactly iLast, pChild->iRowid is the
+ ** new lastest rowid seen so far. */
+ assert( pChild->bEof || fts5RowidCmp(pExpr, iLast, pChild->iRowid)<=0 );
+ if( pChild->bEof ){
+ fts5ExprSetEof(pAnd);
+ bMatch = 1;
+ break;
+ }else if( iLast!=pChild->iRowid ){
+ bMatch = 0;
+ iLast = pChild->iRowid;
+ }
+
+ if( pChild->bNomatch ){
+ pAnd->bNomatch = 1;
+ }
+ }
+ }while( bMatch==0 );
+
+ if( pAnd->bNomatch && pAnd!=pExpr->pRoot ){
+ fts5ExprNodeZeroPoslist(pAnd);
+ }
+ pAnd->iRowid = iLast;
+ return SQLITE_OK;
+}
+
+static int fts5ExprNodeNext_AND(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest_AND(pExpr, pNode);
+ }
+ return rc;
+}
+
+static int fts5ExprNodeTest_NOT(
+ Fts5Expr *pExpr, /* Expression pPhrase belongs to */
+ Fts5ExprNode *pNode /* FTS5_NOT node to advance */
+){
+ int rc = SQLITE_OK;
+ Fts5ExprNode *p1 = pNode->apChild[0];
+ Fts5ExprNode *p2 = pNode->apChild[1];
+ assert( pNode->nChild==2 );
+
+ while( rc==SQLITE_OK && p1->bEof==0 ){
+ int cmp = fts5NodeCompare(pExpr, p1, p2);
+ if( cmp>0 ){
+ rc = fts5ExprNodeNext(pExpr, p2, 1, p1->iRowid);
+ cmp = fts5NodeCompare(pExpr, p1, p2);
+ }
+ assert( rc!=SQLITE_OK || cmp<=0 );
+ if( cmp || p2->bNomatch ) break;
+ rc = fts5ExprNodeNext(pExpr, p1, 0, 0);
+ }
+ pNode->bEof = p1->bEof;
+ pNode->bNomatch = p1->bNomatch;
+ pNode->iRowid = p1->iRowid;
+ if( p1->bEof ){
+ fts5ExprNodeZeroPoslist(p2);
+ }
+ return rc;
+}
+
+static int fts5ExprNodeNext_NOT(
+ Fts5Expr *pExpr,
+ Fts5ExprNode *pNode,
+ int bFromValid,
+ i64 iFrom
+){
+ int rc = fts5ExprNodeNext(pExpr, pNode->apChild[0], bFromValid, iFrom);
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest_NOT(pExpr, pNode);
+ }
+ return rc;
+}
+
+/*
+** If pNode currently points to a match, this function returns SQLITE_OK
+** without modifying it. Otherwise, pNode is advanced until it does point
+** to a match or EOF is reached.
+*/
+static int fts5ExprNodeTest(
+ Fts5Expr *pExpr, /* Expression of which pNode is a part */
+ Fts5ExprNode *pNode /* Expression node to test */
+){
+ int rc = SQLITE_OK;
+ if( pNode->bEof==0 ){
+ switch( pNode->eType ){
+
+ case FTS5_STRING: {
+ rc = fts5ExprNodeTest_STRING(pExpr, pNode);
+ break;
+ }
+
+ case FTS5_TERM: {
+ rc = fts5ExprNodeTest_TERM(pExpr, pNode);
+ break;
+ }
+
+ case FTS5_AND: {
+ rc = fts5ExprNodeTest_AND(pExpr, pNode);
+ break;
+ }
+
+ case FTS5_OR: {
+ fts5ExprNodeTest_OR(pExpr, pNode);
+ break;
+ }
+
+ default: assert( pNode->eType==FTS5_NOT ); {
+ rc = fts5ExprNodeTest_NOT(pExpr, pNode);
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+
+/*
+** Set node pNode, which is part of expression pExpr, to point to the first
+** match. If there are no matches, set the Node.bEof flag to indicate EOF.
+**
+** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
+** It is not an error if there are no matches.
+*/
+static int fts5ExprNodeFirst(Fts5Expr *pExpr, Fts5ExprNode *pNode){
+ int rc = SQLITE_OK;
+ pNode->bEof = 0;
+ pNode->bNomatch = 0;
+
+ if( Fts5NodeIsString(pNode) ){
+ /* Initialize all term iterators in the NEAR object. */
+ rc = fts5ExprNearInitAll(pExpr, pNode);
+ }else if( pNode->xNext==0 ){
+ pNode->bEof = 1;
+ }else{
+ int i;
+ int nEof = 0;
+ for(i=0; i<pNode->nChild && rc==SQLITE_OK; i++){
+ Fts5ExprNode *pChild = pNode->apChild[i];
+ rc = fts5ExprNodeFirst(pExpr, pNode->apChild[i]);
+ assert( pChild->bEof==0 || pChild->bEof==1 );
+ nEof += pChild->bEof;
+ }
+ pNode->iRowid = pNode->apChild[0]->iRowid;
+
+ switch( pNode->eType ){
+ case FTS5_AND:
+ if( nEof>0 ) fts5ExprSetEof(pNode);
+ break;
+
+ case FTS5_OR:
+ if( pNode->nChild==nEof ) fts5ExprSetEof(pNode);
+ break;
+
+ default:
+ assert( pNode->eType==FTS5_NOT );
+ pNode->bEof = pNode->apChild[0]->bEof;
+ break;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = fts5ExprNodeTest(pExpr, pNode);
+ }
+ return rc;
+}
+
+
+/*
+** Begin iterating through the set of documents in index pIdx matched by
+** the MATCH expression passed as the first argument. If the "bDesc"
+** parameter is passed a non-zero value, iteration is in descending rowid
+** order. Or, if it is zero, in ascending order.
+**
+** If iterating in ascending rowid order (bDesc==0), the first document
+** visited is that with the smallest rowid that is larger than or equal
+** to parameter iFirst. Or, if iterating in ascending order (bDesc==1),
+** then the first document visited must have a rowid smaller than or
+** equal to iFirst.
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
+** is not considered an error if the query does not match any documents.
+*/
+static int sqlite3Fts5ExprFirst(Fts5Expr *p, Fts5Index *pIdx, i64 iFirst, int bDesc){
+ Fts5ExprNode *pRoot = p->pRoot;
+ int rc; /* Return code */
+
+ p->pIndex = pIdx;
+ p->bDesc = bDesc;
+ rc = fts5ExprNodeFirst(p, pRoot);
+
+ /* If not at EOF but the current rowid occurs earlier than iFirst in
+ ** the iteration order, move to document iFirst or later. */
+ if( pRoot->bEof==0 && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0 ){
+ rc = fts5ExprNodeNext(p, pRoot, 1, iFirst);
+ }
+
+ /* If the iterator is not at a real match, skip forward until it is. */
+ while( pRoot->bNomatch ){
+ assert( pRoot->bEof==0 && rc==SQLITE_OK );
+ rc = fts5ExprNodeNext(p, pRoot, 0, 0);
+ }
+ return rc;
+}
+
+/*
+** Move to the next document
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
+** is not considered an error if the query does not match any documents.
+*/
+static int sqlite3Fts5ExprNext(Fts5Expr *p, i64 iLast){
+ int rc;
+ Fts5ExprNode *pRoot = p->pRoot;
+ assert( pRoot->bEof==0 && pRoot->bNomatch==0 );
+ do {
+ rc = fts5ExprNodeNext(p, pRoot, 0, 0);
+ assert( pRoot->bNomatch==0 || (rc==SQLITE_OK && pRoot->bEof==0) );
+ }while( pRoot->bNomatch );
+ if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){
+ pRoot->bEof = 1;
+ }
+ return rc;
+}
+
+static int sqlite3Fts5ExprEof(Fts5Expr *p){
+ return p->pRoot->bEof;
+}
+
+static i64 sqlite3Fts5ExprRowid(Fts5Expr *p){
+ return p->pRoot->iRowid;
+}
+
+static int fts5ParseStringFromToken(Fts5Token *pToken, char **pz){
+ int rc = SQLITE_OK;
+ *pz = sqlite3Fts5Strndup(&rc, pToken->p, pToken->n);
+ return rc;
+}
+
+/*
+** Free the phrase object passed as the only argument.
+*/
+static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
+ if( pPhrase ){
+ int i;
+ for(i=0; i<pPhrase->nTerm; i++){
+ Fts5ExprTerm *pSyn;
+ Fts5ExprTerm *pNext;
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
+ sqlite3_free(pTerm->zTerm);
+ sqlite3Fts5IterClose(pTerm->pIter);
+ for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
+ pNext = pSyn->pSynonym;
+ sqlite3Fts5IterClose(pSyn->pIter);
+ fts5BufferFree((Fts5Buffer*)&pSyn[1]);
+ sqlite3_free(pSyn);
+ }
+ }
+ if( pPhrase->poslist.nSpace>0 ) fts5BufferFree(&pPhrase->poslist);
+ sqlite3_free(pPhrase);
+ }
+}
+
+/*
+** If argument pNear is NULL, then a new Fts5ExprNearset object is allocated
+** and populated with pPhrase. Or, if pNear is not NULL, phrase pPhrase is
+** appended to it and the results returned.
+**
+** If an OOM error occurs, both the pNear and pPhrase objects are freed and
+** NULL returned.
+*/
+static Fts5ExprNearset *sqlite3Fts5ParseNearset(
+ Fts5Parse *pParse, /* Parse context */
+ Fts5ExprNearset *pNear, /* Existing nearset, or NULL */
+ Fts5ExprPhrase *pPhrase /* Recently parsed phrase */
+){
+ const int SZALLOC = 8;
+ Fts5ExprNearset *pRet = 0;
+
+ if( pParse->rc==SQLITE_OK ){
+ if( pPhrase==0 ){
+ return pNear;
+ }
+ if( pNear==0 ){
+ int nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
+ pRet = sqlite3_malloc(nByte);
+ if( pRet==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ }else{
+ memset(pRet, 0, nByte);
+ }
+ }else if( (pNear->nPhrase % SZALLOC)==0 ){
+ int nNew = pNear->nPhrase + SZALLOC;
+ int nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
+
+ pRet = (Fts5ExprNearset*)sqlite3_realloc(pNear, nByte);
+ if( pRet==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ }
+ }else{
+ pRet = pNear;
+ }
+ }
+
+ if( pRet==0 ){
+ assert( pParse->rc!=SQLITE_OK );
+ sqlite3Fts5ParseNearsetFree(pNear);
+ sqlite3Fts5ParsePhraseFree(pPhrase);
+ }else{
+ if( pRet->nPhrase>0 ){
+ Fts5ExprPhrase *pLast = pRet->apPhrase[pRet->nPhrase-1];
+ assert( pLast==pParse->apPhrase[pParse->nPhrase-2] );
+ if( pPhrase->nTerm==0 ){
+ fts5ExprPhraseFree(pPhrase);
+ pRet->nPhrase--;
+ pParse->nPhrase--;
+ pPhrase = pLast;
+ }else if( pLast->nTerm==0 ){
+ fts5ExprPhraseFree(pLast);
+ pParse->apPhrase[pParse->nPhrase-2] = pPhrase;
+ pParse->nPhrase--;
+ pRet->nPhrase--;
+ }
+ }
+ pRet->apPhrase[pRet->nPhrase++] = pPhrase;
+ }
+ return pRet;
+}
+
+typedef struct TokenCtx TokenCtx;
+struct TokenCtx {
+ Fts5ExprPhrase *pPhrase;
+ int rc;
+};
+
+/*
+** Callback for tokenizing terms used by ParseTerm().
+*/
+static int fts5ParseTokenize(
+ void *pContext, /* Pointer to Fts5InsertCtx object */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ int rc = SQLITE_OK;
+ const int SZALLOC = 8;
+ TokenCtx *pCtx = (TokenCtx*)pContext;
+ Fts5ExprPhrase *pPhrase = pCtx->pPhrase;
+
+ UNUSED_PARAM2(iUnused1, iUnused2);
+
+ /* If an error has already occurred, this is a no-op */
+ if( pCtx->rc!=SQLITE_OK ) return pCtx->rc;
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+
+ if( pPhrase && pPhrase->nTerm>0 && (tflags & FTS5_TOKEN_COLOCATED) ){
+ Fts5ExprTerm *pSyn;
+ int nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
+ pSyn = (Fts5ExprTerm*)sqlite3_malloc(nByte);
+ if( pSyn==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pSyn, 0, nByte);
+ pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
+ memcpy(pSyn->zTerm, pToken, nToken);
+ pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
+ pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
+ }
+ }else{
+ Fts5ExprTerm *pTerm;
+ if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
+ Fts5ExprPhrase *pNew;
+ int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
+
+ pNew = (Fts5ExprPhrase*)sqlite3_realloc(pPhrase,
+ sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
+ );
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
+ pCtx->pPhrase = pPhrase = pNew;
+ pNew->nTerm = nNew - SZALLOC;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
+ memset(pTerm, 0, sizeof(Fts5ExprTerm));
+ pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
+ }
+ }
+
+ pCtx->rc = rc;
+ return rc;
+}
+
+
+/*
+** Free the phrase object passed as the only argument.
+*/
+static void sqlite3Fts5ParsePhraseFree(Fts5ExprPhrase *pPhrase){
+ fts5ExprPhraseFree(pPhrase);
+}
+
+/*
+** Free the phrase object passed as the second argument.
+*/
+static void sqlite3Fts5ParseNearsetFree(Fts5ExprNearset *pNear){
+ if( pNear ){
+ int i;
+ for(i=0; i<pNear->nPhrase; i++){
+ fts5ExprPhraseFree(pNear->apPhrase[i]);
+ }
+ sqlite3_free(pNear->pColset);
+ sqlite3_free(pNear);
+ }
+}
+
+static void sqlite3Fts5ParseFinished(Fts5Parse *pParse, Fts5ExprNode *p){
+ assert( pParse->pExpr==0 );
+ pParse->pExpr = p;
+}
+
+/*
+** This function is called by the parser to process a string token. The
+** string may or may not be quoted. In any case it is tokenized and a
+** phrase object consisting of all tokens returned.
+*/
+static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
+ Fts5Parse *pParse, /* Parse context */
+ Fts5ExprPhrase *pAppend, /* Phrase to append to */
+ Fts5Token *pToken, /* String to tokenize */
+ int bPrefix /* True if there is a trailing "*" */
+){
+ Fts5Config *pConfig = pParse->pConfig;
+ TokenCtx sCtx; /* Context object passed to callback */
+ int rc; /* Tokenize return code */
+ char *z = 0;
+
+ memset(&sCtx, 0, sizeof(TokenCtx));
+ sCtx.pPhrase = pAppend;
+
+ rc = fts5ParseStringFromToken(pToken, &z);
+ if( rc==SQLITE_OK ){
+ int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_QUERY : 0);
+ int n;
+ sqlite3Fts5Dequote(z);
+ n = (int)strlen(z);
+ rc = sqlite3Fts5Tokenize(pConfig, flags, z, n, &sCtx, fts5ParseTokenize);
+ }
+ sqlite3_free(z);
+ if( rc || (rc = sCtx.rc) ){
+ pParse->rc = rc;
+ fts5ExprPhraseFree(sCtx.pPhrase);
+ sCtx.pPhrase = 0;
+ }else{
+
+ if( pAppend==0 ){
+ if( (pParse->nPhrase % 8)==0 ){
+ int nByte = sizeof(Fts5ExprPhrase*) * (pParse->nPhrase + 8);
+ Fts5ExprPhrase **apNew;
+ apNew = (Fts5ExprPhrase**)sqlite3_realloc(pParse->apPhrase, nByte);
+ if( apNew==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ fts5ExprPhraseFree(sCtx.pPhrase);
+ return 0;
+ }
+ pParse->apPhrase = apNew;
+ }
+ pParse->nPhrase++;
+ }
+
+ if( sCtx.pPhrase==0 ){
+ /* This happens when parsing a token or quoted phrase that contains
+ ** no token characters at all. (e.g ... MATCH '""'). */
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase));
+ }else if( sCtx.pPhrase->nTerm ){
+ sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = bPrefix;
+ }
+ pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
+ }
+
+ return sCtx.pPhrase;
+}
+
+/*
+** Create a new FTS5 expression by cloning phrase iPhrase of the
+** expression passed as the second argument.
+*/
+static int sqlite3Fts5ExprClonePhrase(
+ Fts5Expr *pExpr,
+ int iPhrase,
+ Fts5Expr **ppNew
+){
+ int rc = SQLITE_OK; /* Return code */
+ Fts5ExprPhrase *pOrig; /* The phrase extracted from pExpr */
+ Fts5Expr *pNew = 0; /* Expression to return via *ppNew */
+ TokenCtx sCtx = {0,0}; /* Context object for fts5ParseTokenize */
+
+ pOrig = pExpr->apExprPhrase[iPhrase];
+ pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
+ if( rc==SQLITE_OK ){
+ pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
+ sizeof(Fts5ExprPhrase*));
+ }
+ if( rc==SQLITE_OK ){
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
+ sizeof(Fts5ExprNode));
+ }
+ if( rc==SQLITE_OK ){
+ pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
+ sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
+ }
+ if( rc==SQLITE_OK ){
+ Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
+ if( pColsetOrig ){
+ int nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
+ Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
+ if( pColset ){
+ memcpy(pColset, pColsetOrig, nByte);
+ }
+ pNew->pRoot->pNear->pColset = pColset;
+ }
+ }
+
+ if( pOrig->nTerm ){
+ int i; /* Used to iterate through phrase terms */
+ for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
+ int tflags = 0;
+ Fts5ExprTerm *p;
+ for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
+ const char *zTerm = p->zTerm;
+ rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
+ 0, 0);
+ tflags = FTS5_TOKEN_COLOCATED;
+ }
+ if( rc==SQLITE_OK ){
+ sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
+ }
+ }
+ }else{
+ /* This happens when parsing a token or quoted phrase that contains
+ ** no token characters at all. (e.g ... MATCH '""'). */
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
+ }
+
+ if( rc==SQLITE_OK ){
+ /* All the allocations succeeded. Put the expression object together. */
+ pNew->pIndex = pExpr->pIndex;
+ pNew->pConfig = pExpr->pConfig;
+ pNew->nPhrase = 1;
+ pNew->apExprPhrase[0] = sCtx.pPhrase;
+ pNew->pRoot->pNear->apPhrase[0] = sCtx.pPhrase;
+ pNew->pRoot->pNear->nPhrase = 1;
+ sCtx.pPhrase->pNode = pNew->pRoot;
+
+ if( pOrig->nTerm==1 && pOrig->aTerm[0].pSynonym==0 ){
+ pNew->pRoot->eType = FTS5_TERM;
+ pNew->pRoot->xNext = fts5ExprNodeNext_TERM;
+ }else{
+ pNew->pRoot->eType = FTS5_STRING;
+ pNew->pRoot->xNext = fts5ExprNodeNext_STRING;
+ }
+ }else{
+ sqlite3Fts5ExprFree(pNew);
+ fts5ExprPhraseFree(sCtx.pPhrase);
+ pNew = 0;
+ }
+
+ *ppNew = pNew;
+ return rc;
+}
+
+
+/*
+** Token pTok has appeared in a MATCH expression where the NEAR operator
+** is expected. If token pTok does not contain "NEAR", store an error
+** in the pParse object.
+*/
+static void sqlite3Fts5ParseNear(Fts5Parse *pParse, Fts5Token *pTok){
+ if( pTok->n!=4 || memcmp("NEAR", pTok->p, 4) ){
+ sqlite3Fts5ParseError(
+ pParse, "fts5: syntax error near \"%.*s\"", pTok->n, pTok->p
+ );
+ }
+}
+
+static void sqlite3Fts5ParseSetDistance(
+ Fts5Parse *pParse,
+ Fts5ExprNearset *pNear,
+ Fts5Token *p
+){
+ if( pNear ){
+ int nNear = 0;
+ int i;
+ if( p->n ){
+ for(i=0; i<p->n; i++){
+ char c = (char)p->p[i];
+ if( c<'0' || c>'9' ){
+ sqlite3Fts5ParseError(
+ pParse, "expected integer, got \"%.*s\"", p->n, p->p
+ );
+ return;
+ }
+ nNear = nNear * 10 + (p->p[i] - '0');
+ }
+ }else{
+ nNear = FTS5_DEFAULT_NEARDIST;
+ }
+ pNear->nNear = nNear;
+ }
+}
+
+/*
+** The second argument passed to this function may be NULL, or it may be
+** an existing Fts5Colset object. This function returns a pointer to
+** a new colset object containing the contents of (p) with new value column
+** number iCol appended.
+**
+** If an OOM error occurs, store an error code in pParse and return NULL.
+** The old colset object (if any) is not freed in this case.
+*/
+static Fts5Colset *fts5ParseColset(
+ Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
+ Fts5Colset *p, /* Existing colset object */
+ int iCol /* New column to add to colset object */
+){
+ int nCol = p ? p->nCol : 0; /* Num. columns already in colset object */
+ Fts5Colset *pNew; /* New colset object to return */
+
+ assert( pParse->rc==SQLITE_OK );
+ assert( iCol>=0 && iCol<pParse->pConfig->nCol );
+
+ pNew = sqlite3_realloc(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
+ if( pNew==0 ){
+ pParse->rc = SQLITE_NOMEM;
+ }else{
+ int *aiCol = pNew->aiCol;
+ int i, j;
+ for(i=0; i<nCol; i++){
+ if( aiCol[i]==iCol ) return pNew;
+ if( aiCol[i]>iCol ) break;
+ }
+ for(j=nCol; j>i; j--){
+ aiCol[j] = aiCol[j-1];
+ }
+ aiCol[i] = iCol;
+ pNew->nCol = nCol+1;
+
+#ifndef NDEBUG
+ /* Check that the array is in order and contains no duplicate entries. */
+ for(i=1; i<pNew->nCol; i++) assert( pNew->aiCol[i]>pNew->aiCol[i-1] );
+#endif
+ }
+
+ return pNew;
+}
+
+/*
+** Allocate and return an Fts5Colset object specifying the inverse of
+** the colset passed as the second argument. Free the colset passed
+** as the second argument before returning.
+*/
+static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
+ Fts5Colset *pRet;
+ int nCol = pParse->pConfig->nCol;
+
+ pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
+ sizeof(Fts5Colset) + sizeof(int)*nCol
+ );
+ if( pRet ){
+ int i;
+ int iOld = 0;
+ for(i=0; i<nCol; i++){
+ if( iOld>=p->nCol || p->aiCol[iOld]!=i ){
+ pRet->aiCol[pRet->nCol++] = i;
+ }else{
+ iOld++;
+ }
+ }
+ }
+
+ sqlite3_free(p);
+ return pRet;
+}
+
+static Fts5Colset *sqlite3Fts5ParseColset(
+ Fts5Parse *pParse, /* Store SQLITE_NOMEM here if required */
+ Fts5Colset *pColset, /* Existing colset object */
+ Fts5Token *p
+){
+ Fts5Colset *pRet = 0;
+ int iCol;
+ char *z; /* Dequoted copy of token p */
+
+ z = sqlite3Fts5Strndup(&pParse->rc, p->p, p->n);
+ if( pParse->rc==SQLITE_OK ){
+ Fts5Config *pConfig = pParse->pConfig;
+ sqlite3Fts5Dequote(z);
+ for(iCol=0; iCol<pConfig->nCol; iCol++){
+ if( 0==sqlite3_stricmp(pConfig->azCol[iCol], z) ) break;
+ }
+ if( iCol==pConfig->nCol ){
+ sqlite3Fts5ParseError(pParse, "no such column: %s", z);
+ }else{
+ pRet = fts5ParseColset(pParse, pColset, iCol);
+ }
+ sqlite3_free(z);
+ }
+
+ if( pRet==0 ){
+ assert( pParse->rc!=SQLITE_OK );
+ sqlite3_free(pColset);
+ }
+
+ return pRet;
+}
+
+static void sqlite3Fts5ParseSetColset(
+ Fts5Parse *pParse,
+ Fts5ExprNearset *pNear,
+ Fts5Colset *pColset
+){
+ if( pParse->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ pParse->rc = SQLITE_ERROR;
+ pParse->zErr = sqlite3_mprintf(
+ "fts5: column queries are not supported (detail=none)"
+ );
+ sqlite3_free(pColset);
+ return;
+ }
+
+ if( pNear ){
+ pNear->pColset = pColset;
+ }else{
+ sqlite3_free(pColset);
+ }
+}
+
+static void fts5ExprAssignXNext(Fts5ExprNode *pNode){
+ switch( pNode->eType ){
+ case FTS5_STRING: {
+ Fts5ExprNearset *pNear = pNode->pNear;
+ if( pNear->nPhrase==1 && pNear->apPhrase[0]->nTerm==1
+ && pNear->apPhrase[0]->aTerm[0].pSynonym==0
+ ){
+ pNode->eType = FTS5_TERM;
+ pNode->xNext = fts5ExprNodeNext_TERM;
+ }else{
+ pNode->xNext = fts5ExprNodeNext_STRING;
+ }
+ break;
+ };
+
+ case FTS5_OR: {
+ pNode->xNext = fts5ExprNodeNext_OR;
+ break;
+ };
+
+ case FTS5_AND: {
+ pNode->xNext = fts5ExprNodeNext_AND;
+ break;
+ };
+
+ default: assert( pNode->eType==FTS5_NOT ); {
+ pNode->xNext = fts5ExprNodeNext_NOT;
+ break;
+ };
+ }
+}
+
+static void fts5ExprAddChildren(Fts5ExprNode *p, Fts5ExprNode *pSub){
+ if( p->eType!=FTS5_NOT && pSub->eType==p->eType ){
+ int nByte = sizeof(Fts5ExprNode*) * pSub->nChild;
+ memcpy(&p->apChild[p->nChild], pSub->apChild, nByte);
+ p->nChild += pSub->nChild;
+ sqlite3_free(pSub);
+ }else{
+ p->apChild[p->nChild++] = pSub;
+ }
+}
+
+/*
+** Allocate and return a new expression object. If anything goes wrong (i.e.
+** OOM error), leave an error code in pParse and return NULL.
+*/
+static Fts5ExprNode *sqlite3Fts5ParseNode(
+ Fts5Parse *pParse, /* Parse context */
+ int eType, /* FTS5_STRING, AND, OR or NOT */
+ Fts5ExprNode *pLeft, /* Left hand child expression */
+ Fts5ExprNode *pRight, /* Right hand child expression */
+ Fts5ExprNearset *pNear /* For STRING expressions, the near cluster */
+){
+ Fts5ExprNode *pRet = 0;
+
+ if( pParse->rc==SQLITE_OK ){
+ int nChild = 0; /* Number of children of returned node */
+ int nByte; /* Bytes of space to allocate for this node */
+
+ assert( (eType!=FTS5_STRING && !pNear)
+ || (eType==FTS5_STRING && !pLeft && !pRight)
+ );
+ if( eType==FTS5_STRING && pNear==0 ) return 0;
+ if( eType!=FTS5_STRING && pLeft==0 ) return pRight;
+ if( eType!=FTS5_STRING && pRight==0 ) return pLeft;
+
+ if( eType==FTS5_NOT ){
+ nChild = 2;
+ }else if( eType==FTS5_AND || eType==FTS5_OR ){
+ nChild = 2;
+ if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
+ if( pRight->eType==eType ) nChild += pRight->nChild-1;
+ }
+
+ nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
+ pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
+
+ if( pRet ){
+ pRet->eType = eType;
+ pRet->pNear = pNear;
+ fts5ExprAssignXNext(pRet);
+ if( eType==FTS5_STRING ){
+ int iPhrase;
+ for(iPhrase=0; iPhrase<pNear->nPhrase; iPhrase++){
+ pNear->apPhrase[iPhrase]->pNode = pRet;
+ if( pNear->apPhrase[iPhrase]->nTerm==0 ){
+ pRet->xNext = 0;
+ pRet->eType = FTS5_EOF;
+ }
+ }
+
+ if( pParse->pConfig->eDetail!=FTS5_DETAIL_FULL
+ && (pNear->nPhrase!=1 || pNear->apPhrase[0]->nTerm>1)
+ ){
+ assert( pParse->rc==SQLITE_OK );
+ pParse->rc = SQLITE_ERROR;
+ assert( pParse->zErr==0 );
+ pParse->zErr = sqlite3_mprintf(
+ "fts5: %s queries are not supported (detail!=full)",
+ pNear->nPhrase==1 ? "phrase": "NEAR"
+ );
+ sqlite3_free(pRet);
+ pRet = 0;
+ }
+
+ }else{
+ fts5ExprAddChildren(pRet, pLeft);
+ fts5ExprAddChildren(pRet, pRight);
+ }
+ }
+ }
+
+ if( pRet==0 ){
+ assert( pParse->rc!=SQLITE_OK );
+ sqlite3Fts5ParseNodeFree(pLeft);
+ sqlite3Fts5ParseNodeFree(pRight);
+ sqlite3Fts5ParseNearsetFree(pNear);
+ }
+ return pRet;
+}
+
+static Fts5ExprNode *sqlite3Fts5ParseImplicitAnd(
+ Fts5Parse *pParse, /* Parse context */
+ Fts5ExprNode *pLeft, /* Left hand child expression */
+ Fts5ExprNode *pRight /* Right hand child expression */
+){
+ Fts5ExprNode *pRet = 0;
+ Fts5ExprNode *pPrev;
+
+ if( pParse->rc ){
+ sqlite3Fts5ParseNodeFree(pLeft);
+ sqlite3Fts5ParseNodeFree(pRight);
+ }else{
+
+ assert( pLeft->eType==FTS5_STRING
+ || pLeft->eType==FTS5_TERM
+ || pLeft->eType==FTS5_EOF
+ || pLeft->eType==FTS5_AND
+ );
+ assert( pRight->eType==FTS5_STRING
+ || pRight->eType==FTS5_TERM
+ || pRight->eType==FTS5_EOF
+ );
+
+ if( pLeft->eType==FTS5_AND ){
+ pPrev = pLeft->apChild[pLeft->nChild-1];
+ }else{
+ pPrev = pLeft;
+ }
+ assert( pPrev->eType==FTS5_STRING
+ || pPrev->eType==FTS5_TERM
+ || pPrev->eType==FTS5_EOF
+ );
+
+ if( pRight->eType==FTS5_EOF ){
+ assert( pParse->apPhrase[pParse->nPhrase-1]==pRight->pNear->apPhrase[0] );
+ sqlite3Fts5ParseNodeFree(pRight);
+ pRet = pLeft;
+ pParse->nPhrase--;
+ }
+ else if( pPrev->eType==FTS5_EOF ){
+ Fts5ExprPhrase **ap;
+
+ if( pPrev==pLeft ){
+ pRet = pRight;
+ }else{
+ pLeft->apChild[pLeft->nChild-1] = pRight;
+ pRet = pLeft;
+ }
+
+ ap = &pParse->apPhrase[pParse->nPhrase-1-pRight->pNear->nPhrase];
+ assert( ap[0]==pPrev->pNear->apPhrase[0] );
+ memmove(ap, &ap[1], sizeof(Fts5ExprPhrase*)*pRight->pNear->nPhrase);
+ pParse->nPhrase--;
+
+ sqlite3Fts5ParseNodeFree(pPrev);
+ }
+ else{
+ pRet = sqlite3Fts5ParseNode(pParse, FTS5_AND, pLeft, pRight, 0);
+ }
+ }
+
+ return pRet;
+}
+
+static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
+ int nByte = 0;
+ Fts5ExprTerm *p;
+ char *zQuoted;
+
+ /* Determine the maximum amount of space required. */
+ for(p=pTerm; p; p=p->pSynonym){
+ nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2;
+ }
+ zQuoted = sqlite3_malloc(nByte);
+
+ if( zQuoted ){
+ int i = 0;
+ for(p=pTerm; p; p=p->pSynonym){
+ char *zIn = p->zTerm;
+ zQuoted[i++] = '"';
+ while( *zIn ){
+ if( *zIn=='"' ) zQuoted[i++] = '"';
+ zQuoted[i++] = *zIn++;
+ }
+ zQuoted[i++] = '"';
+ if( p->pSynonym ) zQuoted[i++] = '|';
+ }
+ if( pTerm->bPrefix ){
+ zQuoted[i++] = ' ';
+ zQuoted[i++] = '*';
+ }
+ zQuoted[i++] = '\0';
+ }
+ return zQuoted;
+}
+
+static char *fts5PrintfAppend(char *zApp, const char *zFmt, ...){
+ char *zNew;
+ va_list ap;
+ va_start(ap, zFmt);
+ zNew = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+ if( zApp && zNew ){
+ char *zNew2 = sqlite3_mprintf("%s%s", zApp, zNew);
+ sqlite3_free(zNew);
+ zNew = zNew2;
+ }
+ sqlite3_free(zApp);
+ return zNew;
+}
+
+/*
+** Compose a tcl-readable representation of expression pExpr. Return a
+** pointer to a buffer containing that representation. It is the
+** responsibility of the caller to at some point free the buffer using
+** sqlite3_free().
+*/
+static char *fts5ExprPrintTcl(
+ Fts5Config *pConfig,
+ const char *zNearsetCmd,
+ Fts5ExprNode *pExpr
+){
+ char *zRet = 0;
+ if( pExpr->eType==FTS5_STRING || pExpr->eType==FTS5_TERM ){
+ Fts5ExprNearset *pNear = pExpr->pNear;
+ int i;
+ int iTerm;
+
+ zRet = fts5PrintfAppend(zRet, "%s ", zNearsetCmd);
+ if( zRet==0 ) return 0;
+ if( pNear->pColset ){
+ int *aiCol = pNear->pColset->aiCol;
+ int nCol = pNear->pColset->nCol;
+ if( nCol==1 ){
+ zRet = fts5PrintfAppend(zRet, "-col %d ", aiCol[0]);
+ }else{
+ zRet = fts5PrintfAppend(zRet, "-col {%d", aiCol[0]);
+ for(i=1; i<pNear->pColset->nCol; i++){
+ zRet = fts5PrintfAppend(zRet, " %d", aiCol[i]);
+ }
+ zRet = fts5PrintfAppend(zRet, "} ");
+ }
+ if( zRet==0 ) return 0;
+ }
+
+ if( pNear->nPhrase>1 ){
+ zRet = fts5PrintfAppend(zRet, "-near %d ", pNear->nNear);
+ if( zRet==0 ) return 0;
+ }
+
+ zRet = fts5PrintfAppend(zRet, "--");
+ if( zRet==0 ) return 0;
+
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+
+ zRet = fts5PrintfAppend(zRet, " {");
+ for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
+ char *zTerm = pPhrase->aTerm[iTerm].zTerm;
+ zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm);
+ if( pPhrase->aTerm[iTerm].bPrefix ){
+ zRet = fts5PrintfAppend(zRet, "*");
+ }
+ }
+
+ if( zRet ) zRet = fts5PrintfAppend(zRet, "}");
+ if( zRet==0 ) return 0;
+ }
+
+ }else{
+ char const *zOp = 0;
+ int i;
+ switch( pExpr->eType ){
+ case FTS5_AND: zOp = "AND"; break;
+ case FTS5_NOT: zOp = "NOT"; break;
+ default:
+ assert( pExpr->eType==FTS5_OR );
+ zOp = "OR";
+ break;
+ }
+
+ zRet = sqlite3_mprintf("%s", zOp);
+ for(i=0; zRet && i<pExpr->nChild; i++){
+ char *z = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->apChild[i]);
+ if( !z ){
+ sqlite3_free(zRet);
+ zRet = 0;
+ }else{
+ zRet = fts5PrintfAppend(zRet, " [%z]", z);
+ }
+ }
+ }
+
+ return zRet;
+}
+
+static char *fts5ExprPrint(Fts5Config *pConfig, Fts5ExprNode *pExpr){
+ char *zRet = 0;
+ if( pExpr->eType==0 ){
+ return sqlite3_mprintf("\"\"");
+ }else
+ if( pExpr->eType==FTS5_STRING || pExpr->eType==FTS5_TERM ){
+ Fts5ExprNearset *pNear = pExpr->pNear;
+ int i;
+ int iTerm;
+
+ if( pNear->pColset ){
+ int iCol = pNear->pColset->aiCol[0];
+ zRet = fts5PrintfAppend(zRet, "%s : ", pConfig->azCol[iCol]);
+ if( zRet==0 ) return 0;
+ }
+
+ if( pNear->nPhrase>1 ){
+ zRet = fts5PrintfAppend(zRet, "NEAR(");
+ if( zRet==0 ) return 0;
+ }
+
+ for(i=0; i<pNear->nPhrase; i++){
+ Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];
+ if( i!=0 ){
+ zRet = fts5PrintfAppend(zRet, " ");
+ if( zRet==0 ) return 0;
+ }
+ for(iTerm=0; iTerm<pPhrase->nTerm; iTerm++){
+ char *zTerm = fts5ExprTermPrint(&pPhrase->aTerm[iTerm]);
+ if( zTerm ){
+ zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" + ", zTerm);
+ sqlite3_free(zTerm);
+ }
+ if( zTerm==0 || zRet==0 ){
+ sqlite3_free(zRet);
+ return 0;
+ }
+ }
+ }
+
+ if( pNear->nPhrase>1 ){
+ zRet = fts5PrintfAppend(zRet, ", %d)", pNear->nNear);
+ if( zRet==0 ) return 0;
+ }
+
+ }else{
+ char const *zOp = 0;
+ int i;
+
+ switch( pExpr->eType ){
+ case FTS5_AND: zOp = " AND "; break;
+ case FTS5_NOT: zOp = " NOT "; break;
+ default:
+ assert( pExpr->eType==FTS5_OR );
+ zOp = " OR ";
+ break;
+ }
+
+ for(i=0; i<pExpr->nChild; i++){
+ char *z = fts5ExprPrint(pConfig, pExpr->apChild[i]);
+ if( z==0 ){
+ sqlite3_free(zRet);
+ zRet = 0;
+ }else{
+ int e = pExpr->apChild[i]->eType;
+ int b = (e!=FTS5_STRING && e!=FTS5_TERM && e!=FTS5_EOF);
+ zRet = fts5PrintfAppend(zRet, "%s%s%z%s",
+ (i==0 ? "" : zOp),
+ (b?"(":""), z, (b?")":"")
+ );
+ }
+ if( zRet==0 ) break;
+ }
+ }
+
+ return zRet;
+}
+
+/*
+** The implementation of user-defined scalar functions fts5_expr() (bTcl==0)
+** and fts5_expr_tcl() (bTcl!=0).
+*/
+static void fts5ExprFunction(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal, /* Function arguments */
+ int bTcl
+){
+ Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx);
+ sqlite3 *db = sqlite3_context_db_handle(pCtx);
+ const char *zExpr = 0;
+ char *zErr = 0;
+ Fts5Expr *pExpr = 0;
+ int rc;
+ int i;
+
+ const char **azConfig; /* Array of arguments for Fts5Config */
+ const char *zNearsetCmd = "nearset";
+ int nConfig; /* Size of azConfig[] */
+ Fts5Config *pConfig = 0;
+ int iArg = 1;
+
+ if( nArg<1 ){
+ zErr = sqlite3_mprintf("wrong number of arguments to function %s",
+ bTcl ? "fts5_expr_tcl" : "fts5_expr"
+ );
+ sqlite3_result_error(pCtx, zErr, -1);
+ sqlite3_free(zErr);
+ return;
+ }
+
+ if( bTcl && nArg>1 ){
+ zNearsetCmd = (const char*)sqlite3_value_text(apVal[1]);
+ iArg = 2;
+ }
+
+ nConfig = 3 + (nArg-iArg);
+ azConfig = (const char**)sqlite3_malloc(sizeof(char*) * nConfig);
+ if( azConfig==0 ){
+ sqlite3_result_error_nomem(pCtx);
+ return;
+ }
+ azConfig[0] = 0;
+ azConfig[1] = "main";
+ azConfig[2] = "tbl";
+ for(i=3; iArg<nArg; iArg++){
+ azConfig[i++] = (const char*)sqlite3_value_text(apVal[iArg]);
+ }
+
+ zExpr = (const char*)sqlite3_value_text(apVal[0]);
+
+ rc = sqlite3Fts5ConfigParse(pGlobal, db, nConfig, azConfig, &pConfig, &zErr);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprNew(pConfig, zExpr, &pExpr, &zErr);
+ }
+ if( rc==SQLITE_OK ){
+ char *zText;
+ if( pExpr->pRoot->xNext==0 ){
+ zText = sqlite3_mprintf("");
+ }else if( bTcl ){
+ zText = fts5ExprPrintTcl(pConfig, zNearsetCmd, pExpr->pRoot);
+ }else{
+ zText = fts5ExprPrint(pConfig, pExpr->pRoot);
+ }
+ if( zText==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_result_text(pCtx, zText, -1, SQLITE_TRANSIENT);
+ sqlite3_free(zText);
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ if( zErr ){
+ sqlite3_result_error(pCtx, zErr, -1);
+ sqlite3_free(zErr);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+ }
+ sqlite3_free((void *)azConfig);
+ sqlite3Fts5ConfigFree(pConfig);
+ sqlite3Fts5ExprFree(pExpr);
+}
+
+static void fts5ExprFunctionHr(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ fts5ExprFunction(pCtx, nArg, apVal, 0);
+}
+static void fts5ExprFunctionTcl(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ fts5ExprFunction(pCtx, nArg, apVal, 1);
+}
+
+/*
+** The implementation of an SQLite user-defined-function that accepts a
+** single integer as an argument. If the integer is an alpha-numeric
+** unicode code point, 1 is returned. Otherwise 0.
+*/
+static void fts5ExprIsAlnum(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ int iCode;
+ if( nArg!=1 ){
+ sqlite3_result_error(pCtx,
+ "wrong number of arguments to function fts5_isalnum", -1
+ );
+ return;
+ }
+ iCode = sqlite3_value_int(apVal[0]);
+ sqlite3_result_int(pCtx, sqlite3Fts5UnicodeIsalnum(iCode));
+}
+
+static void fts5ExprFold(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apVal /* Function arguments */
+){
+ if( nArg!=1 && nArg!=2 ){
+ sqlite3_result_error(pCtx,
+ "wrong number of arguments to function fts5_fold", -1
+ );
+ }else{
+ int iCode;
+ int bRemoveDiacritics = 0;
+ iCode = sqlite3_value_int(apVal[0]);
+ if( nArg==2 ) bRemoveDiacritics = sqlite3_value_int(apVal[1]);
+ sqlite3_result_int(pCtx, sqlite3Fts5UnicodeFold(iCode, bRemoveDiacritics));
+ }
+}
+
+/*
+** This is called during initialization to register the fts5_expr() scalar
+** UDF with the SQLite handle passed as the only argument.
+*/
+static int sqlite3Fts5ExprInit(Fts5Global *pGlobal, sqlite3 *db){
+ struct Fts5ExprFunc {
+ const char *z;
+ void (*x)(sqlite3_context*,int,sqlite3_value**);
+ } aFunc[] = {
+ { "fts5_expr", fts5ExprFunctionHr },
+ { "fts5_expr_tcl", fts5ExprFunctionTcl },
+ { "fts5_isalnum", fts5ExprIsAlnum },
+ { "fts5_fold", fts5ExprFold },
+ };
+ int i;
+ int rc = SQLITE_OK;
+ void *pCtx = (void*)pGlobal;
+
+ for(i=0; rc==SQLITE_OK && i<ArraySize(aFunc); i++){
+ struct Fts5ExprFunc *p = &aFunc[i];
+ rc = sqlite3_create_function(db, p->z, -1, SQLITE_UTF8, pCtx, p->x, 0, 0);
+ }
+
+ /* Avoid a warning indicating that sqlite3Fts5ParserTrace() is unused */
+#ifndef NDEBUG
+ (void)sqlite3Fts5ParserTrace;
+#endif
+
+ return rc;
+}
+
+/*
+** Return the number of phrases in expression pExpr.
+*/
+static int sqlite3Fts5ExprPhraseCount(Fts5Expr *pExpr){
+ return (pExpr ? pExpr->nPhrase : 0);
+}
+
+/*
+** Return the number of terms in the iPhrase'th phrase in pExpr.
+*/
+static int sqlite3Fts5ExprPhraseSize(Fts5Expr *pExpr, int iPhrase){
+ if( iPhrase<0 || iPhrase>=pExpr->nPhrase ) return 0;
+ return pExpr->apExprPhrase[iPhrase]->nTerm;
+}
+
+/*
+** This function is used to access the current position list for phrase
+** iPhrase.
+*/
+static int sqlite3Fts5ExprPoslist(Fts5Expr *pExpr, int iPhrase, const u8 **pa){
+ int nRet;
+ Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase];
+ Fts5ExprNode *pNode = pPhrase->pNode;
+ if( pNode->bEof==0 && pNode->iRowid==pExpr->pRoot->iRowid ){
+ *pa = pPhrase->poslist.p;
+ nRet = pPhrase->poslist.n;
+ }else{
+ *pa = 0;
+ nRet = 0;
+ }
+ return nRet;
+}
+
+struct Fts5PoslistPopulator {
+ Fts5PoslistWriter writer;
+ int bOk; /* True if ok to populate */
+ int bMiss;
+};
+
+static Fts5PoslistPopulator *sqlite3Fts5ExprClearPoslists(Fts5Expr *pExpr, int bLive){
+ Fts5PoslistPopulator *pRet;
+ pRet = sqlite3_malloc(sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
+ if( pRet ){
+ int i;
+ memset(pRet, 0, sizeof(Fts5PoslistPopulator)*pExpr->nPhrase);
+ for(i=0; i<pExpr->nPhrase; i++){
+ Fts5Buffer *pBuf = &pExpr->apExprPhrase[i]->poslist;
+ Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
+ assert( pExpr->apExprPhrase[i]->nTerm==1 );
+ if( bLive &&
+ (pBuf->n==0 || pNode->iRowid!=pExpr->pRoot->iRowid || pNode->bEof)
+ ){
+ pRet[i].bMiss = 1;
+ }else{
+ pBuf->n = 0;
+ }
+ }
+ }
+ return pRet;
+}
+
+struct Fts5ExprCtx {
+ Fts5Expr *pExpr;
+ Fts5PoslistPopulator *aPopulator;
+ i64 iOff;
+};
+typedef struct Fts5ExprCtx Fts5ExprCtx;
+
+/*
+** TODO: Make this more efficient!
+*/
+static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
+ int i;
+ for(i=0; i<pColset->nCol; i++){
+ if( pColset->aiCol[i]==iCol ) return 1;
+ }
+ return 0;
+}
+
+static int fts5ExprPopulatePoslistsCb(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Byte offset of token within input text */
+ int iUnused2 /* Byte offset of end of token within input text */
+){
+ Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
+ Fts5Expr *pExpr = p->pExpr;
+ int i;
+
+ UNUSED_PARAM2(iUnused1, iUnused2);
+
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
+ for(i=0; i<pExpr->nPhrase; i++){
+ Fts5ExprTerm *pTerm;
+ if( p->aPopulator[i].bOk==0 ) continue;
+ for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
+ int nTerm = (int)strlen(pTerm->zTerm);
+ if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix))
+ && memcmp(pTerm->zTerm, pToken, nTerm)==0
+ ){
+ int rc = sqlite3Fts5PoslistWriterAppend(
+ &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
+ );
+ if( rc ) return rc;
+ break;
+ }
+ }
+ }
+ return SQLITE_OK;
+}
+
+static int sqlite3Fts5ExprPopulatePoslists(
+ Fts5Config *pConfig,
+ Fts5Expr *pExpr,
+ Fts5PoslistPopulator *aPopulator,
+ int iCol,
+ const char *z, int n
+){
+ int i;
+ Fts5ExprCtx sCtx;
+ sCtx.pExpr = pExpr;
+ sCtx.aPopulator = aPopulator;
+ sCtx.iOff = (((i64)iCol) << 32) - 1;
+
+ for(i=0; i<pExpr->nPhrase; i++){
+ Fts5ExprNode *pNode = pExpr->apExprPhrase[i]->pNode;
+ Fts5Colset *pColset = pNode->pNear->pColset;
+ if( (pColset && 0==fts5ExprColsetTest(pColset, iCol))
+ || aPopulator[i].bMiss
+ ){
+ aPopulator[i].bOk = 0;
+ }else{
+ aPopulator[i].bOk = 1;
+ }
+ }
+
+ return sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT, z, n, (void*)&sCtx, fts5ExprPopulatePoslistsCb
+ );
+}
+
+static void fts5ExprClearPoslists(Fts5ExprNode *pNode){
+ if( pNode->eType==FTS5_TERM || pNode->eType==FTS5_STRING ){
+ pNode->pNear->apPhrase[0]->poslist.n = 0;
+ }else{
+ int i;
+ for(i=0; i<pNode->nChild; i++){
+ fts5ExprClearPoslists(pNode->apChild[i]);
+ }
+ }
+}
+
+static int fts5ExprCheckPoslists(Fts5ExprNode *pNode, i64 iRowid){
+ pNode->iRowid = iRowid;
+ pNode->bEof = 0;
+ switch( pNode->eType ){
+ case FTS5_TERM:
+ case FTS5_STRING:
+ return (pNode->pNear->apPhrase[0]->poslist.n>0);
+
+ case FTS5_AND: {
+ int i;
+ for(i=0; i<pNode->nChild; i++){
+ if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid)==0 ){
+ fts5ExprClearPoslists(pNode);
+ return 0;
+ }
+ }
+ break;
+ }
+
+ case FTS5_OR: {
+ int i;
+ int bRet = 0;
+ for(i=0; i<pNode->nChild; i++){
+ if( fts5ExprCheckPoslists(pNode->apChild[i], iRowid) ){
+ bRet = 1;
+ }
+ }
+ return bRet;
+ }
+
+ default: {
+ assert( pNode->eType==FTS5_NOT );
+ if( 0==fts5ExprCheckPoslists(pNode->apChild[0], iRowid)
+ || 0!=fts5ExprCheckPoslists(pNode->apChild[1], iRowid)
+ ){
+ fts5ExprClearPoslists(pNode);
+ return 0;
+ }
+ break;
+ }
+ }
+ return 1;
+}
+
+static void sqlite3Fts5ExprCheckPoslists(Fts5Expr *pExpr, i64 iRowid){
+ fts5ExprCheckPoslists(pExpr->pRoot, iRowid);
+}
+
+/*
+** This function is only called for detail=columns tables.
+*/
+static int sqlite3Fts5ExprPhraseCollist(
+ Fts5Expr *pExpr,
+ int iPhrase,
+ const u8 **ppCollist,
+ int *pnCollist
+){
+ Fts5ExprPhrase *pPhrase = pExpr->apExprPhrase[iPhrase];
+ Fts5ExprNode *pNode = pPhrase->pNode;
+ int rc = SQLITE_OK;
+
+ assert( iPhrase>=0 && iPhrase<pExpr->nPhrase );
+ assert( pExpr->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+
+ if( pNode->bEof==0
+ && pNode->iRowid==pExpr->pRoot->iRowid
+ && pPhrase->poslist.n>0
+ ){
+ Fts5ExprTerm *pTerm = &pPhrase->aTerm[0];
+ if( pTerm->pSynonym ){
+ Fts5Buffer *pBuf = (Fts5Buffer*)&pTerm->pSynonym[1];
+ rc = fts5ExprSynonymList(
+ pTerm, pNode->iRowid, pBuf, (u8**)ppCollist, pnCollist
+ );
+ }else{
+ *ppCollist = pPhrase->aTerm[0].pIter->pData;
+ *pnCollist = pPhrase->aTerm[0].pIter->nData;
+ }
+ }else{
+ *ppCollist = 0;
+ *pnCollist = 0;
+ }
+
+ return rc;
+}
+
+
+/*
+** 2014 August 11
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+*/
+
+
+
+/* #include "fts5Int.h" */
+
+typedef struct Fts5HashEntry Fts5HashEntry;
+
+/*
+** This file contains the implementation of an in-memory hash table used
+** to accumuluate "term -> doclist" content before it is flused to a level-0
+** segment.
+*/
+
+
+struct Fts5Hash {
+ int eDetail; /* Copy of Fts5Config.eDetail */
+ int *pnByte; /* Pointer to bytes counter */
+ int nEntry; /* Number of entries currently in hash */
+ int nSlot; /* Size of aSlot[] array */
+ Fts5HashEntry *pScan; /* Current ordered scan item */
+ Fts5HashEntry **aSlot; /* Array of hash slots */
+};
+
+/*
+** Each entry in the hash table is represented by an object of the
+** following type. Each object, its key (zKey[]) and its current data
+** are stored in a single memory allocation. The position list data
+** immediately follows the key data in memory.
+**
+** The data that follows the key is in a similar, but not identical format
+** to the doclist data stored in the database. It is:
+**
+** * Rowid, as a varint
+** * Position list, without 0x00 terminator.
+** * Size of previous position list and rowid, as a 4 byte
+** big-endian integer.
+**
+** iRowidOff:
+** Offset of last rowid written to data area. Relative to first byte of
+** structure.
+**
+** nData:
+** Bytes of data written since iRowidOff.
+*/
+struct Fts5HashEntry {
+ Fts5HashEntry *pHashNext; /* Next hash entry with same hash-key */
+ Fts5HashEntry *pScanNext; /* Next entry in sorted order */
+
+ int nAlloc; /* Total size of allocation */
+ int iSzPoslist; /* Offset of space for 4-byte poslist size */
+ int nData; /* Total bytes of data (incl. structure) */
+ int nKey; /* Length of zKey[] in bytes */
+ u8 bDel; /* Set delete-flag @ iSzPoslist */
+ u8 bContent; /* Set content-flag (detail=none mode) */
+ i16 iCol; /* Column of last value written */
+ int iPos; /* Position of last value written */
+ i64 iRowid; /* Rowid of last value written */
+ char zKey[8]; /* Nul-terminated entry key */
+};
+
+/*
+** Size of Fts5HashEntry without the zKey[] array.
+*/
+#define FTS5_HASHENTRYSIZE (sizeof(Fts5HashEntry)-8)
+
+
+
+/*
+** Allocate a new hash table.
+*/
+static int sqlite3Fts5HashNew(Fts5Config *pConfig, Fts5Hash **ppNew, int *pnByte){
+ int rc = SQLITE_OK;
+ Fts5Hash *pNew;
+
+ *ppNew = pNew = (Fts5Hash*)sqlite3_malloc(sizeof(Fts5Hash));
+ if( pNew==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int nByte;
+ memset(pNew, 0, sizeof(Fts5Hash));
+ pNew->pnByte = pnByte;
+ pNew->eDetail = pConfig->eDetail;
+
+ pNew->nSlot = 1024;
+ nByte = sizeof(Fts5HashEntry*) * pNew->nSlot;
+ pNew->aSlot = (Fts5HashEntry**)sqlite3_malloc(nByte);
+ if( pNew->aSlot==0 ){
+ sqlite3_free(pNew);
+ *ppNew = 0;
+ rc = SQLITE_NOMEM;
+ }else{
+ memset(pNew->aSlot, 0, nByte);
+ }
+ }
+ return rc;
+}
+
+/*
+** Free a hash table object.
+*/
+static void sqlite3Fts5HashFree(Fts5Hash *pHash){
+ if( pHash ){
+ sqlite3Fts5HashClear(pHash);
+ sqlite3_free(pHash->aSlot);
+ sqlite3_free(pHash);
+ }
+}
+
+/*
+** Empty (but do not delete) a hash table.
+*/
+static void sqlite3Fts5HashClear(Fts5Hash *pHash){
+ int i;
+ for(i=0; i<pHash->nSlot; i++){
+ Fts5HashEntry *pNext;
+ Fts5HashEntry *pSlot;
+ for(pSlot=pHash->aSlot[i]; pSlot; pSlot=pNext){
+ pNext = pSlot->pHashNext;
+ sqlite3_free(pSlot);
+ }
+ }
+ memset(pHash->aSlot, 0, pHash->nSlot * sizeof(Fts5HashEntry*));
+ pHash->nEntry = 0;
+}
+
+static unsigned int fts5HashKey(int nSlot, const u8 *p, int n){
+ int i;
+ unsigned int h = 13;
+ for(i=n-1; i>=0; i--){
+ h = (h << 3) ^ h ^ p[i];
+ }
+ return (h % nSlot);
+}
+
+static unsigned int fts5HashKey2(int nSlot, u8 b, const u8 *p, int n){
+ int i;
+ unsigned int h = 13;
+ for(i=n-1; i>=0; i--){
+ h = (h << 3) ^ h ^ p[i];
+ }
+ h = (h << 3) ^ h ^ b;
+ return (h % nSlot);
+}
+
+/*
+** Resize the hash table by doubling the number of slots.
+*/
+static int fts5HashResize(Fts5Hash *pHash){
+ int nNew = pHash->nSlot*2;
+ int i;
+ Fts5HashEntry **apNew;
+ Fts5HashEntry **apOld = pHash->aSlot;
+
+ apNew = (Fts5HashEntry**)sqlite3_malloc(nNew*sizeof(Fts5HashEntry*));
+ if( !apNew ) return SQLITE_NOMEM;
+ memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));
+
+ for(i=0; i<pHash->nSlot; i++){
+ while( apOld[i] ){
+ int iHash;
+ Fts5HashEntry *p = apOld[i];
+ apOld[i] = p->pHashNext;
+ iHash = fts5HashKey(nNew, (u8*)p->zKey, (int)strlen(p->zKey));
+ p->pHashNext = apNew[iHash];
+ apNew[iHash] = p;
+ }
+ }
+
+ sqlite3_free(apOld);
+ pHash->nSlot = nNew;
+ pHash->aSlot = apNew;
+ return SQLITE_OK;
+}
+
+static void fts5HashAddPoslistSize(Fts5Hash *pHash, Fts5HashEntry *p){
+ if( p->iSzPoslist ){
+ u8 *pPtr = (u8*)p;
+ if( pHash->eDetail==FTS5_DETAIL_NONE ){
+ assert( p->nData==p->iSzPoslist );
+ if( p->bDel ){
+ pPtr[p->nData++] = 0x00;
+ if( p->bContent ){
+ pPtr[p->nData++] = 0x00;
+ }
+ }
+ }else{
+ int nSz = (p->nData - p->iSzPoslist - 1); /* Size in bytes */
+ int nPos = nSz*2 + p->bDel; /* Value of nPos field */
+
+ assert( p->bDel==0 || p->bDel==1 );
+ if( nPos<=127 ){
+ pPtr[p->iSzPoslist] = (u8)nPos;
+ }else{
+ int nByte = sqlite3Fts5GetVarintLen((u32)nPos);
+ memmove(&pPtr[p->iSzPoslist + nByte], &pPtr[p->iSzPoslist + 1], nSz);
+ sqlite3Fts5PutVarint(&pPtr[p->iSzPoslist], nPos);
+ p->nData += (nByte-1);
+ }
+ }
+
+ p->iSzPoslist = 0;
+ p->bDel = 0;
+ p->bContent = 0;
+ }
+}
+
+/*
+** Add an entry to the in-memory hash table. The key is the concatenation
+** of bByte and (pToken/nToken). The value is (iRowid/iCol/iPos).
+**
+** (bByte || pToken) -> (iRowid,iCol,iPos)
+**
+** Or, if iCol is negative, then the value is a delete marker.
+*/
+static int sqlite3Fts5HashWrite(
+ Fts5Hash *pHash,
+ i64 iRowid, /* Rowid for this entry */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ char bByte, /* First byte of token */
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+){
+ unsigned int iHash;
+ Fts5HashEntry *p;
+ u8 *pPtr;
+ int nIncr = 0; /* Amount to increment (*pHash->pnByte) by */
+ int bNew; /* If non-delete entry should be written */
+
+ bNew = (pHash->eDetail==FTS5_DETAIL_FULL);
+
+ /* Attempt to locate an existing hash entry */
+ iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
+ for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
+ if( p->zKey[0]==bByte
+ && p->nKey==nToken
+ && memcmp(&p->zKey[1], pToken, nToken)==0
+ ){
+ break;
+ }
+ }
+
+ /* If an existing hash entry cannot be found, create a new one. */
+ if( p==0 ){
+ /* Figure out how much space to allocate */
+ int nByte = FTS5_HASHENTRYSIZE + (nToken+1) + 1 + 64;
+ if( nByte<128 ) nByte = 128;
+
+ /* Grow the Fts5Hash.aSlot[] array if necessary. */
+ if( (pHash->nEntry*2)>=pHash->nSlot ){
+ int rc = fts5HashResize(pHash);
+ if( rc!=SQLITE_OK ) return rc;
+ iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
+ }
+
+ /* Allocate new Fts5HashEntry and add it to the hash table. */
+ p = (Fts5HashEntry*)sqlite3_malloc(nByte);
+ if( !p ) return SQLITE_NOMEM;
+ memset(p, 0, FTS5_HASHENTRYSIZE);
+ p->nAlloc = nByte;
+ p->zKey[0] = bByte;
+ memcpy(&p->zKey[1], pToken, nToken);
+ assert( iHash==fts5HashKey(pHash->nSlot, (u8*)p->zKey, nToken+1) );
+ p->nKey = nToken;
+ p->zKey[nToken+1] = '\0';
+ p->nData = nToken+1 + 1 + FTS5_HASHENTRYSIZE;
+ p->pHashNext = pHash->aSlot[iHash];
+ pHash->aSlot[iHash] = p;
+ pHash->nEntry++;
+
+ /* Add the first rowid field to the hash-entry */
+ p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid);
+ p->iRowid = iRowid;
+
+ p->iSzPoslist = p->nData;
+ if( pHash->eDetail!=FTS5_DETAIL_NONE ){
+ p->nData += 1;
+ p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
+ }
+
+ nIncr += p->nData;
+ }else{
+
+ /* Appending to an existing hash-entry. Check that there is enough
+ ** space to append the largest possible new entry. Worst case scenario
+ ** is:
+ **
+ ** + 9 bytes for a new rowid,
+ ** + 4 byte reserved for the "poslist size" varint.
+ ** + 1 byte for a "new column" byte,
+ ** + 3 bytes for a new column number (16-bit max) as a varint,
+ ** + 5 bytes for the new position offset (32-bit max).
+ */
+ if( (p->nAlloc - p->nData) < (9 + 4 + 1 + 3 + 5) ){
+ int nNew = p->nAlloc * 2;
+ Fts5HashEntry *pNew;
+ Fts5HashEntry **pp;
+ pNew = (Fts5HashEntry*)sqlite3_realloc(p, nNew);
+ if( pNew==0 ) return SQLITE_NOMEM;
+ pNew->nAlloc = nNew;
+ for(pp=&pHash->aSlot[iHash]; *pp!=p; pp=&(*pp)->pHashNext);
+ *pp = pNew;
+ p = pNew;
+ }
+ nIncr -= p->nData;
+ }
+ assert( (p->nAlloc - p->nData) >= (9 + 4 + 1 + 3 + 5) );
+
+ pPtr = (u8*)p;
+
+ /* If this is a new rowid, append the 4-byte size field for the previous
+ ** entry, and the new rowid for this entry. */
+ if( iRowid!=p->iRowid ){
+ fts5HashAddPoslistSize(pHash, p);
+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iRowid - p->iRowid);
+ p->iRowid = iRowid;
+ bNew = 1;
+ p->iSzPoslist = p->nData;
+ if( pHash->eDetail!=FTS5_DETAIL_NONE ){
+ p->nData += 1;
+ p->iCol = (pHash->eDetail==FTS5_DETAIL_FULL ? 0 : -1);
+ p->iPos = 0;
+ }
+ }
+
+ if( iCol>=0 ){
+ if( pHash->eDetail==FTS5_DETAIL_NONE ){
+ p->bContent = 1;
+ }else{
+ /* Append a new column value, if necessary */
+ assert( iCol>=p->iCol );
+ if( iCol!=p->iCol ){
+ if( pHash->eDetail==FTS5_DETAIL_FULL ){
+ pPtr[p->nData++] = 0x01;
+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iCol);
+ p->iCol = (i16)iCol;
+ p->iPos = 0;
+ }else{
+ bNew = 1;
+ p->iCol = (i16)(iPos = iCol);
+ }
+ }
+
+ /* Append the new position offset, if necessary */
+ if( bNew ){
+ p->nData += sqlite3Fts5PutVarint(&pPtr[p->nData], iPos - p->iPos + 2);
+ p->iPos = iPos;
+ }
+ }
+ }else{
+ /* This is a delete. Set the delete flag. */
+ p->bDel = 1;
+ }
+
+ nIncr += p->nData;
+ *pHash->pnByte += nIncr;
+ return SQLITE_OK;
+}
+
+
+/*
+** Arguments pLeft and pRight point to linked-lists of hash-entry objects,
+** each sorted in key order. This function merges the two lists into a
+** single list and returns a pointer to its first element.
+*/
+static Fts5HashEntry *fts5HashEntryMerge(
+ Fts5HashEntry *pLeft,
+ Fts5HashEntry *pRight
+){
+ Fts5HashEntry *p1 = pLeft;
+ Fts5HashEntry *p2 = pRight;
+ Fts5HashEntry *pRet = 0;
+ Fts5HashEntry **ppOut = &pRet;
+
+ while( p1 || p2 ){
+ if( p1==0 ){
+ *ppOut = p2;
+ p2 = 0;
+ }else if( p2==0 ){
+ *ppOut = p1;
+ p1 = 0;
+ }else{
+ int i = 0;
+ while( p1->zKey[i]==p2->zKey[i] ) i++;
+
+ if( ((u8)p1->zKey[i])>((u8)p2->zKey[i]) ){
+ /* p2 is smaller */
+ *ppOut = p2;
+ ppOut = &p2->pScanNext;
+ p2 = p2->pScanNext;
+ }else{
+ /* p1 is smaller */
+ *ppOut = p1;
+ ppOut = &p1->pScanNext;
+ p1 = p1->pScanNext;
+ }
+ *ppOut = 0;
+ }
+ }
+
+ return pRet;
+}
+
+/*
+** Extract all tokens from hash table iHash and link them into a list
+** in sorted order. The hash table is cleared before returning. It is
+** the responsibility of the caller to free the elements of the returned
+** list.
+*/
+static int fts5HashEntrySort(
+ Fts5Hash *pHash,
+ const char *pTerm, int nTerm, /* Query prefix, if any */
+ Fts5HashEntry **ppSorted
+){
+ const int nMergeSlot = 32;
+ Fts5HashEntry **ap;
+ Fts5HashEntry *pList;
+ int iSlot;
+ int i;
+
+ *ppSorted = 0;
+ ap = sqlite3_malloc(sizeof(Fts5HashEntry*) * nMergeSlot);
+ if( !ap ) return SQLITE_NOMEM;
+ memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);
+
+ for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
+ Fts5HashEntry *pIter;
+ for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
+ if( pTerm==0 || 0==memcmp(pIter->zKey, pTerm, nTerm) ){
+ Fts5HashEntry *pEntry = pIter;
+ pEntry->pScanNext = 0;
+ for(i=0; ap[i]; i++){
+ pEntry = fts5HashEntryMerge(pEntry, ap[i]);
+ ap[i] = 0;
+ }
+ ap[i] = pEntry;
+ }
+ }
+ }
+
+ pList = 0;
+ for(i=0; i<nMergeSlot; i++){
+ pList = fts5HashEntryMerge(pList, ap[i]);
+ }
+
+ pHash->nEntry = 0;
+ sqlite3_free(ap);
+ *ppSorted = pList;
+ return SQLITE_OK;
+}
+
+/*
+** Query the hash table for a doclist associated with term pTerm/nTerm.
+*/
+static int sqlite3Fts5HashQuery(
+ Fts5Hash *pHash, /* Hash table to query */
+ const char *pTerm, int nTerm, /* Query term */
+ const u8 **ppDoclist, /* OUT: Pointer to doclist for pTerm */
+ int *pnDoclist /* OUT: Size of doclist in bytes */
+){
+ unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
+ Fts5HashEntry *p;
+
+ for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
+ if( memcmp(p->zKey, pTerm, nTerm)==0 && p->zKey[nTerm]==0 ) break;
+ }
+
+ if( p ){
+ fts5HashAddPoslistSize(pHash, p);
+ *ppDoclist = (const u8*)&p->zKey[nTerm+1];
+ *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
+ }else{
+ *ppDoclist = 0;
+ *pnDoclist = 0;
+ }
+
+ return SQLITE_OK;
+}
+
+static int sqlite3Fts5HashScanInit(
+ Fts5Hash *p, /* Hash table to query */
+ const char *pTerm, int nTerm /* Query prefix */
+){
+ return fts5HashEntrySort(p, pTerm, nTerm, &p->pScan);
+}
+
+static void sqlite3Fts5HashScanNext(Fts5Hash *p){
+ assert( !sqlite3Fts5HashScanEof(p) );
+ p->pScan = p->pScan->pScanNext;
+}
+
+static int sqlite3Fts5HashScanEof(Fts5Hash *p){
+ return (p->pScan==0);
+}
+
+static void sqlite3Fts5HashScanEntry(
+ Fts5Hash *pHash,
+ const char **pzTerm, /* OUT: term (nul-terminated) */
+ const u8 **ppDoclist, /* OUT: pointer to doclist */
+ int *pnDoclist /* OUT: size of doclist in bytes */
+){
+ Fts5HashEntry *p;
+ if( (p = pHash->pScan) ){
+ int nTerm = (int)strlen(p->zKey);
+ fts5HashAddPoslistSize(pHash, p);
+ *pzTerm = p->zKey;
+ *ppDoclist = (const u8*)&p->zKey[nTerm+1];
+ *pnDoclist = p->nData - (FTS5_HASHENTRYSIZE + nTerm + 1);
+ }else{
+ *pzTerm = 0;
+ *ppDoclist = 0;
+ *pnDoclist = 0;
+ }
+}
+
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Low level access to the FTS index stored in the database file. The
+** routines in this file file implement all read and write access to the
+** %_data table. Other parts of the system access this functionality via
+** the interface defined in fts5Int.h.
+*/
+
+
+/* #include "fts5Int.h" */
+
+/*
+** Overview:
+**
+** The %_data table contains all the FTS indexes for an FTS5 virtual table.
+** As well as the main term index, there may be up to 31 prefix indexes.
+** The format is similar to FTS3/4, except that:
+**
+** * all segment b-tree leaf data is stored in fixed size page records
+** (e.g. 1000 bytes). A single doclist may span multiple pages. Care is
+** taken to ensure it is possible to iterate in either direction through
+** the entries in a doclist, or to seek to a specific entry within a
+** doclist, without loading it into memory.
+**
+** * large doclists that span many pages have associated "doclist index"
+** records that contain a copy of the first rowid on each page spanned by
+** the doclist. This is used to speed up seek operations, and merges of
+** large doclists with very small doclists.
+**
+** * extra fields in the "structure record" record the state of ongoing
+** incremental merge operations.
+**
+*/
+
+
+#define FTS5_OPT_WORK_UNIT 1000 /* Number of leaf pages per optimize step */
+#define FTS5_WORK_UNIT 64 /* Number of leaf pages in unit of work */
+
+#define FTS5_MIN_DLIDX_SIZE 4 /* Add dlidx if this many empty pages */
+
+#define FTS5_MAIN_PREFIX '0'
+
+#if FTS5_MAX_PREFIX_INDEXES > 31
+# error "FTS5_MAX_PREFIX_INDEXES is too large"
+#endif
+
+/*
+** Details:
+**
+** The %_data table managed by this module,
+**
+** CREATE TABLE %_data(id INTEGER PRIMARY KEY, block BLOB);
+**
+** , contains the following 5 types of records. See the comments surrounding
+** the FTS5_*_ROWID macros below for a description of how %_data rowids are
+** assigned to each fo them.
+**
+** 1. Structure Records:
+**
+** The set of segments that make up an index - the index structure - are
+** recorded in a single record within the %_data table. The record consists
+** of a single 32-bit configuration cookie value followed by a list of
+** SQLite varints. If the FTS table features more than one index (because
+** there are one or more prefix indexes), it is guaranteed that all share
+** the same cookie value.
+**
+** Immediately following the configuration cookie, the record begins with
+** three varints:
+**
+** + number of levels,
+** + total number of segments on all levels,
+** + value of write counter.
+**
+** Then, for each level from 0 to nMax:
+**
+** + number of input segments in ongoing merge.
+** + total number of segments in level.
+** + for each segment from oldest to newest:
+** + segment id (always > 0)
+** + first leaf page number (often 1, always greater than 0)
+** + final leaf page number
+**
+** 2. The Averages Record:
+**
+** A single record within the %_data table. The data is a list of varints.
+** The first value is the number of rows in the index. Then, for each column
+** from left to right, the total number of tokens in the column for all
+** rows of the table.
+**
+** 3. Segment leaves:
+**
+** TERM/DOCLIST FORMAT:
+**
+** Most of each segment leaf is taken up by term/doclist data. The
+** general format of term/doclist, starting with the first term
+** on the leaf page, is:
+**
+** varint : size of first term
+** blob: first term data
+** doclist: first doclist
+** zero-or-more {
+** varint: number of bytes in common with previous term
+** varint: number of bytes of new term data (nNew)
+** blob: nNew bytes of new term data
+** doclist: next doclist
+** }
+**
+** doclist format:
+**
+** varint: first rowid
+** poslist: first poslist
+** zero-or-more {
+** varint: rowid delta (always > 0)
+** poslist: next poslist
+** }
+**
+** poslist format:
+**
+** varint: size of poslist in bytes multiplied by 2, not including
+** this field. Plus 1 if this entry carries the "delete" flag.
+** collist: collist for column 0
+** zero-or-more {
+** 0x01 byte
+** varint: column number (I)
+** collist: collist for column I
+** }
+**
+** collist format:
+**
+** varint: first offset + 2
+** zero-or-more {
+** varint: offset delta + 2
+** }
+**
+** PAGE FORMAT
+**
+** Each leaf page begins with a 4-byte header containing 2 16-bit
+** unsigned integer fields in big-endian format. They are:
+**
+** * The byte offset of the first rowid on the page, if it exists
+** and occurs before the first term (otherwise 0).
+**
+** * The byte offset of the start of the page footer. If the page
+** footer is 0 bytes in size, then this field is the same as the
+** size of the leaf page in bytes.
+**
+** The page footer consists of a single varint for each term located
+** on the page. Each varint is the byte offset of the current term
+** within the page, delta-compressed against the previous value. In
+** other words, the first varint in the footer is the byte offset of
+** the first term, the second is the byte offset of the second less that
+** of the first, and so on.
+**
+** The term/doclist format described above is accurate if the entire
+** term/doclist data fits on a single leaf page. If this is not the case,
+** the format is changed in two ways:
+**
+** + if the first rowid on a page occurs before the first term, it
+** is stored as a literal value:
+**
+** varint: first rowid
+**
+** + the first term on each page is stored in the same way as the
+** very first term of the segment:
+**
+** varint : size of first term
+** blob: first term data
+**
+** 5. Segment doclist indexes:
+**
+** Doclist indexes are themselves b-trees, however they usually consist of
+** a single leaf record only. The format of each doclist index leaf page
+** is:
+**
+** * Flags byte. Bits are:
+** 0x01: Clear if leaf is also the root page, otherwise set.
+**
+** * Page number of fts index leaf page. As a varint.
+**
+** * First rowid on page indicated by previous field. As a varint.
+**
+** * A list of varints, one for each subsequent termless page. A
+** positive delta if the termless page contains at least one rowid,
+** or an 0x00 byte otherwise.
+**
+** Internal doclist index nodes are:
+**
+** * Flags byte. Bits are:
+** 0x01: Clear for root page, otherwise set.
+**
+** * Page number of first child page. As a varint.
+**
+** * Copy of first rowid on page indicated by previous field. As a varint.
+**
+** * A list of delta-encoded varints - the first rowid on each subsequent
+** child page.
+**
+*/
+
+/*
+** Rowids for the averages and structure records in the %_data table.
+*/
+#define FTS5_AVERAGES_ROWID 1 /* Rowid used for the averages record */
+#define FTS5_STRUCTURE_ROWID 10 /* The structure record */
+
+/*
+** Macros determining the rowids used by segment leaves and dlidx leaves
+** and nodes. All nodes and leaves are stored in the %_data table with large
+** positive rowids.
+**
+** Each segment has a unique non-zero 16-bit id.
+**
+** The rowid for each segment leaf is found by passing the segment id and
+** the leaf page number to the FTS5_SEGMENT_ROWID macro. Leaves are numbered
+** sequentially starting from 1.
+*/
+#define FTS5_DATA_ID_B 16 /* Max seg id number 65535 */
+#define FTS5_DATA_DLI_B 1 /* Doclist-index flag (1 bit) */
+#define FTS5_DATA_HEIGHT_B 5 /* Max dlidx tree height of 32 */
+#define FTS5_DATA_PAGE_B 31 /* Max page number of 2147483648 */
+
+#define fts5_dri(segid, dlidx, height, pgno) ( \
+ ((i64)(segid) << (FTS5_DATA_PAGE_B+FTS5_DATA_HEIGHT_B+FTS5_DATA_DLI_B)) + \
+ ((i64)(dlidx) << (FTS5_DATA_PAGE_B + FTS5_DATA_HEIGHT_B)) + \
+ ((i64)(height) << (FTS5_DATA_PAGE_B)) + \
+ ((i64)(pgno)) \
+)
+
+#define FTS5_SEGMENT_ROWID(segid, pgno) fts5_dri(segid, 0, 0, pgno)
+#define FTS5_DLIDX_ROWID(segid, height, pgno) fts5_dri(segid, 1, height, pgno)
+
+/*
+** Maximum segments permitted in a single index
+*/
+#define FTS5_MAX_SEGMENT 2000
+
+#ifdef SQLITE_DEBUG
+static int sqlite3Fts5Corrupt() { return SQLITE_CORRUPT_VTAB; }
+#endif
+
+
+/*
+** Each time a blob is read from the %_data table, it is padded with this
+** many zero bytes. This makes it easier to decode the various record formats
+** without overreading if the records are corrupt.
+*/
+#define FTS5_DATA_ZERO_PADDING 8
+#define FTS5_DATA_PADDING 20
+
+typedef struct Fts5Data Fts5Data;
+typedef struct Fts5DlidxIter Fts5DlidxIter;
+typedef struct Fts5DlidxLvl Fts5DlidxLvl;
+typedef struct Fts5DlidxWriter Fts5DlidxWriter;
+typedef struct Fts5Iter Fts5Iter;
+typedef struct Fts5PageWriter Fts5PageWriter;
+typedef struct Fts5SegIter Fts5SegIter;
+typedef struct Fts5DoclistIter Fts5DoclistIter;
+typedef struct Fts5SegWriter Fts5SegWriter;
+typedef struct Fts5Structure Fts5Structure;
+typedef struct Fts5StructureLevel Fts5StructureLevel;
+typedef struct Fts5StructureSegment Fts5StructureSegment;
+
+struct Fts5Data {
+ u8 *p; /* Pointer to buffer containing record */
+ int nn; /* Size of record in bytes */
+ int szLeaf; /* Size of leaf without page-index */
+};
+
+/*
+** One object per %_data table.
+*/
+struct Fts5Index {
+ Fts5Config *pConfig; /* Virtual table configuration */
+ char *zDataTbl; /* Name of %_data table */
+ int nWorkUnit; /* Leaf pages in a "unit" of work */
+
+ /*
+ ** Variables related to the accumulation of tokens and doclists within the
+ ** in-memory hash tables before they are flushed to disk.
+ */
+ Fts5Hash *pHash; /* Hash table for in-memory data */
+ int nPendingData; /* Current bytes of pending data */
+ i64 iWriteRowid; /* Rowid for current doc being written */
+ int bDelete; /* Current write is a delete */
+
+ /* Error state. */
+ int rc; /* Current error code */
+
+ /* State used by the fts5DataXXX() functions. */
+ sqlite3_blob *pReader; /* RO incr-blob open on %_data table */
+ sqlite3_stmt *pWriter; /* "INSERT ... %_data VALUES(?,?)" */
+ sqlite3_stmt *pDeleter; /* "DELETE FROM %_data ... id>=? AND id<=?" */
+ sqlite3_stmt *pIdxWriter; /* "INSERT ... %_idx VALUES(?,?,?,?)" */
+ sqlite3_stmt *pIdxDeleter; /* "DELETE FROM %_idx WHERE segid=? */
+ sqlite3_stmt *pIdxSelect;
+ int nRead; /* Total number of blocks read */
+
+ sqlite3_stmt *pDataVersion;
+ i64 iStructVersion; /* data_version when pStruct read */
+ Fts5Structure *pStruct; /* Current db structure (or NULL) */
+};
+
+struct Fts5DoclistIter {
+ u8 *aEof; /* Pointer to 1 byte past end of doclist */
+
+ /* Output variables. aPoslist==0 at EOF */
+ i64 iRowid;
+ u8 *aPoslist;
+ int nPoslist;
+ int nSize;
+};
+
+/*
+** The contents of the "structure" record for each index are represented
+** using an Fts5Structure record in memory. Which uses instances of the
+** other Fts5StructureXXX types as components.
+*/
+struct Fts5StructureSegment {
+ int iSegid; /* Segment id */
+ int pgnoFirst; /* First leaf page number in segment */
+ int pgnoLast; /* Last leaf page number in segment */
+};
+struct Fts5StructureLevel {
+ int nMerge; /* Number of segments in incr-merge */
+ int nSeg; /* Total number of segments on level */
+ Fts5StructureSegment *aSeg; /* Array of segments. aSeg[0] is oldest. */
+};
+struct Fts5Structure {
+ int nRef; /* Object reference count */
+ u64 nWriteCounter; /* Total leaves written to level 0 */
+ int nSegment; /* Total segments in this structure */
+ int nLevel; /* Number of levels in this index */
+ Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */
+};
+
+/*
+** An object of type Fts5SegWriter is used to write to segments.
+*/
+struct Fts5PageWriter {
+ int pgno; /* Page number for this page */
+ int iPrevPgidx; /* Previous value written into pgidx */
+ Fts5Buffer buf; /* Buffer containing leaf data */
+ Fts5Buffer pgidx; /* Buffer containing page-index */
+ Fts5Buffer term; /* Buffer containing previous term on page */
+};
+struct Fts5DlidxWriter {
+ int pgno; /* Page number for this page */
+ int bPrevValid; /* True if iPrev is valid */
+ i64 iPrev; /* Previous rowid value written to page */
+ Fts5Buffer buf; /* Buffer containing page data */
+};
+struct Fts5SegWriter {
+ int iSegid; /* Segid to write to */
+ Fts5PageWriter writer; /* PageWriter object */
+ i64 iPrevRowid; /* Previous rowid written to current leaf */
+ u8 bFirstRowidInDoclist; /* True if next rowid is first in doclist */
+ u8 bFirstRowidInPage; /* True if next rowid is first in page */
+ /* TODO1: Can use (writer.pgidx.n==0) instead of bFirstTermInPage */
+ u8 bFirstTermInPage; /* True if next term will be first in leaf */
+ int nLeafWritten; /* Number of leaf pages written */
+ int nEmpty; /* Number of contiguous term-less nodes */
+
+ int nDlidx; /* Allocated size of aDlidx[] array */
+ Fts5DlidxWriter *aDlidx; /* Array of Fts5DlidxWriter objects */
+
+ /* Values to insert into the %_idx table */
+ Fts5Buffer btterm; /* Next term to insert into %_idx table */
+ int iBtPage; /* Page number corresponding to btterm */
+};
+
+typedef struct Fts5CResult Fts5CResult;
+struct Fts5CResult {
+ u16 iFirst; /* aSeg[] index of firstest iterator */
+ u8 bTermEq; /* True if the terms are equal */
+};
+
+/*
+** Object for iterating through a single segment, visiting each term/rowid
+** pair in the segment.
+**
+** pSeg:
+** The segment to iterate through.
+**
+** iLeafPgno:
+** Current leaf page number within segment.
+**
+** iLeafOffset:
+** Byte offset within the current leaf that is the first byte of the
+** position list data (one byte passed the position-list size field).
+** rowid field of the current entry. Usually this is the size field of the
+** position list data. The exception is if the rowid for the current entry
+** is the last thing on the leaf page.
+**
+** pLeaf:
+** Buffer containing current leaf page data. Set to NULL at EOF.
+**
+** iTermLeafPgno, iTermLeafOffset:
+** Leaf page number containing the last term read from the segment. And
+** the offset immediately following the term data.
+**
+** flags:
+** Mask of FTS5_SEGITER_XXX values. Interpreted as follows:
+**
+** FTS5_SEGITER_ONETERM:
+** If set, set the iterator to point to EOF after the current doclist
+** has been exhausted. Do not proceed to the next term in the segment.
+**
+** FTS5_SEGITER_REVERSE:
+** This flag is only ever set if FTS5_SEGITER_ONETERM is also set. If
+** it is set, iterate through rowid in descending order instead of the
+** default ascending order.
+**
+** iRowidOffset/nRowidOffset/aRowidOffset:
+** These are used if the FTS5_SEGITER_REVERSE flag is set.
+**
+** For each rowid on the page corresponding to the current term, the
+** corresponding aRowidOffset[] entry is set to the byte offset of the
+** start of the "position-list-size" field within the page.
+**
+** iTermIdx:
+** Index of current term on iTermLeafPgno.
+*/
+struct Fts5SegIter {
+ Fts5StructureSegment *pSeg; /* Segment to iterate through */
+ int flags; /* Mask of configuration flags */
+ int iLeafPgno; /* Current leaf page number */
+ Fts5Data *pLeaf; /* Current leaf data */
+ Fts5Data *pNextLeaf; /* Leaf page (iLeafPgno+1) */
+ int iLeafOffset; /* Byte offset within current leaf */
+
+ /* Next method */
+ void (*xNext)(Fts5Index*, Fts5SegIter*, int*);
+
+ /* The page and offset from which the current term was read. The offset
+ ** is the offset of the first rowid in the current doclist. */
+ int iTermLeafPgno;
+ int iTermLeafOffset;
+
+ int iPgidxOff; /* Next offset in pgidx */
+ int iEndofDoclist;
+
+ /* The following are only used if the FTS5_SEGITER_REVERSE flag is set. */
+ int iRowidOffset; /* Current entry in aRowidOffset[] */
+ int nRowidOffset; /* Allocated size of aRowidOffset[] array */
+ int *aRowidOffset; /* Array of offset to rowid fields */
+
+ Fts5DlidxIter *pDlidx; /* If there is a doclist-index */
+
+ /* Variables populated based on current entry. */
+ Fts5Buffer term; /* Current term */
+ i64 iRowid; /* Current rowid */
+ int nPos; /* Number of bytes in current position list */
+ u8 bDel; /* True if the delete flag is set */
+};
+
+/*
+** Argument is a pointer to an Fts5Data structure that contains a
+** leaf page.
+*/
+#define ASSERT_SZLEAF_OK(x) assert( \
+ (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \
+)
+
+#define FTS5_SEGITER_ONETERM 0x01
+#define FTS5_SEGITER_REVERSE 0x02
+
+/*
+** Argument is a pointer to an Fts5Data structure that contains a leaf
+** page. This macro evaluates to true if the leaf contains no terms, or
+** false if it contains at least one term.
+*/
+#define fts5LeafIsTermless(x) ((x)->szLeaf >= (x)->nn)
+
+#define fts5LeafTermOff(x, i) (fts5GetU16(&(x)->p[(x)->szLeaf + (i)*2]))
+
+#define fts5LeafFirstRowidOff(x) (fts5GetU16((x)->p))
+
+/*
+** Object for iterating through the merged results of one or more segments,
+** visiting each term/rowid pair in the merged data.
+**
+** nSeg is always a power of two greater than or equal to the number of
+** segments that this object is merging data from. Both the aSeg[] and
+** aFirst[] arrays are sized at nSeg entries. The aSeg[] array is padded
+** with zeroed objects - these are handled as if they were iterators opened
+** on empty segments.
+**
+** The results of comparing segments aSeg[N] and aSeg[N+1], where N is an
+** even number, is stored in aFirst[(nSeg+N)/2]. The "result" of the
+** comparison in this context is the index of the iterator that currently
+** points to the smaller term/rowid combination. Iterators at EOF are
+** considered to be greater than all other iterators.
+**
+** aFirst[1] contains the index in aSeg[] of the iterator that points to
+** the smallest key overall. aFirst[0] is unused.
+**
+** poslist:
+** Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
+** There is no way to tell if this is populated or not.
+*/
+struct Fts5Iter {
+ Fts5IndexIter base; /* Base class containing output vars */
+
+ Fts5Index *pIndex; /* Index that owns this iterator */
+ Fts5Structure *pStruct; /* Database structure for this iterator */
+ Fts5Buffer poslist; /* Buffer containing current poslist */
+ Fts5Colset *pColset; /* Restrict matches to these columns */
+
+ /* Invoked to set output variables. */
+ void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);
+
+ int nSeg; /* Size of aSeg[] array */
+ int bRev; /* True to iterate in reverse order */
+ u8 bSkipEmpty; /* True to skip deleted entries */
+
+ i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
+ Fts5CResult *aFirst; /* Current merge state (see above) */
+ Fts5SegIter aSeg[1]; /* Array of segment iterators */
+};
+
+
+/*
+** An instance of the following type is used to iterate through the contents
+** of a doclist-index record.
+**
+** pData:
+** Record containing the doclist-index data.
+**
+** bEof:
+** Set to true once iterator has reached EOF.
+**
+** iOff:
+** Set to the current offset within record pData.
+*/
+struct Fts5DlidxLvl {
+ Fts5Data *pData; /* Data for current page of this level */
+ int iOff; /* Current offset into pData */
+ int bEof; /* At EOF already */
+ int iFirstOff; /* Used by reverse iterators */
+
+ /* Output variables */
+ int iLeafPgno; /* Page number of current leaf page */
+ i64 iRowid; /* First rowid on leaf iLeafPgno */
+};
+struct Fts5DlidxIter {
+ int nLvl;
+ int iSegid;
+ Fts5DlidxLvl aLvl[1];
+};
+
+static void fts5PutU16(u8 *aOut, u16 iVal){
+ aOut[0] = (iVal>>8);
+ aOut[1] = (iVal&0xFF);
+}
+
+static u16 fts5GetU16(const u8 *aIn){
+ return ((u16)aIn[0] << 8) + aIn[1];
+}
+
+/*
+** Allocate and return a buffer at least nByte bytes in size.
+**
+** If an OOM error is encountered, return NULL and set the error code in
+** the Fts5Index handle passed as the first argument.
+*/
+static void *fts5IdxMalloc(Fts5Index *p, int nByte){
+ return sqlite3Fts5MallocZero(&p->rc, nByte);
+}
+
+/*
+** Compare the contents of the pLeft buffer with the pRight/nRight blob.
+**
+** Return -ve if pLeft is smaller than pRight, 0 if they are equal or
+** +ve if pRight is smaller than pLeft. In other words:
+**
+** res = *pLeft - *pRight
+*/
+#ifdef SQLITE_DEBUG
+static int fts5BufferCompareBlob(
+ Fts5Buffer *pLeft, /* Left hand side of comparison */
+ const u8 *pRight, int nRight /* Right hand side of comparison */
+){
+ int nCmp = MIN(pLeft->n, nRight);
+ int res = memcmp(pLeft->p, pRight, nCmp);
+ return (res==0 ? (pLeft->n - nRight) : res);
+}
+#endif
+
+/*
+** Compare the contents of the two buffers using memcmp(). If one buffer
+** is a prefix of the other, it is considered the lesser.
+**
+** Return -ve if pLeft is smaller than pRight, 0 if they are equal or
+** +ve if pRight is smaller than pLeft. In other words:
+**
+** res = *pLeft - *pRight
+*/
+static int fts5BufferCompare(Fts5Buffer *pLeft, Fts5Buffer *pRight){
+ int nCmp = MIN(pLeft->n, pRight->n);
+ int res = memcmp(pLeft->p, pRight->p, nCmp);
+ return (res==0 ? (pLeft->n - pRight->n) : res);
+}
+
+static int fts5LeafFirstTermOff(Fts5Data *pLeaf){
+ int ret;
+ fts5GetVarint32(&pLeaf->p[pLeaf->szLeaf], ret);
+ return ret;
+}
+
+/*
+** Close the read-only blob handle, if it is open.
+*/
+static void fts5CloseReader(Fts5Index *p){
+ if( p->pReader ){
+ sqlite3_blob *pReader = p->pReader;
+ p->pReader = 0;
+ sqlite3_blob_close(pReader);
+ }
+}
+
+
+/*
+** Retrieve a record from the %_data table.
+**
+** If an error occurs, NULL is returned and an error left in the
+** Fts5Index object.
+*/
+static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
+ Fts5Data *pRet = 0;
+ if( p->rc==SQLITE_OK ){
+ int rc = SQLITE_OK;
+
+ if( p->pReader ){
+ /* This call may return SQLITE_ABORT if there has been a savepoint
+ ** rollback since it was last used. In this case a new blob handle
+ ** is required. */
+ sqlite3_blob *pBlob = p->pReader;
+ p->pReader = 0;
+ rc = sqlite3_blob_reopen(pBlob, iRowid);
+ assert( p->pReader==0 );
+ p->pReader = pBlob;
+ if( rc!=SQLITE_OK ){
+ fts5CloseReader(p);
+ }
+ if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
+ }
+
+ /* If the blob handle is not open at this point, open it and seek
+ ** to the requested entry. */
+ if( p->pReader==0 && rc==SQLITE_OK ){
+ Fts5Config *pConfig = p->pConfig;
+ rc = sqlite3_blob_open(pConfig->db,
+ pConfig->zDb, p->zDataTbl, "block", iRowid, 0, &p->pReader
+ );
+ }
+
+ /* If either of the sqlite3_blob_open() or sqlite3_blob_reopen() calls
+ ** above returned SQLITE_ERROR, return SQLITE_CORRUPT_VTAB instead.
+ ** All the reasons those functions might return SQLITE_ERROR - missing
+ ** table, missing row, non-blob/text in block column - indicate
+ ** backing store corruption. */
+ if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT;
+
+ if( rc==SQLITE_OK ){
+ u8 *aOut = 0; /* Read blob data into this buffer */
+ int nByte = sqlite3_blob_bytes(p->pReader);
+ int nAlloc = sizeof(Fts5Data) + nByte + FTS5_DATA_PADDING;
+ pRet = (Fts5Data*)sqlite3_malloc(nAlloc);
+ if( pRet ){
+ pRet->nn = nByte;
+ aOut = pRet->p = (u8*)&pRet[1];
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_blob_read(p->pReader, aOut, nByte, 0);
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3_free(pRet);
+ pRet = 0;
+ }else{
+ /* TODO1: Fix this */
+ pRet->szLeaf = fts5GetU16(&pRet->p[2]);
+ }
+ }
+ p->rc = rc;
+ p->nRead++;
+ }
+
+ assert( (pRet==0)==(p->rc!=SQLITE_OK) );
+ return pRet;
+}
+
+/*
+** Release a reference to data record returned by an earlier call to
+** fts5DataRead().
+*/
+static void fts5DataRelease(Fts5Data *pData){
+ sqlite3_free(pData);
+}
+
+static Fts5Data *fts5LeafRead(Fts5Index *p, i64 iRowid){
+ Fts5Data *pRet = fts5DataRead(p, iRowid);
+ if( pRet ){
+ if( pRet->szLeaf>pRet->nn ){
+ p->rc = FTS5_CORRUPT;
+ fts5DataRelease(pRet);
+ pRet = 0;
+ }
+ }
+ return pRet;
+}
+
+static int fts5IndexPrepareStmt(
+ Fts5Index *p,
+ sqlite3_stmt **ppStmt,
+ char *zSql
+){
+ if( p->rc==SQLITE_OK ){
+ if( zSql ){
+ p->rc = sqlite3_prepare_v2(p->pConfig->db, zSql, -1, ppStmt, 0);
+ }else{
+ p->rc = SQLITE_NOMEM;
+ }
+ }
+ sqlite3_free(zSql);
+ return p->rc;
+}
+
+
+/*
+** INSERT OR REPLACE a record into the %_data table.
+*/
+static void fts5DataWrite(Fts5Index *p, i64 iRowid, const u8 *pData, int nData){
+ if( p->rc!=SQLITE_OK ) return;
+
+ if( p->pWriter==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pWriter, sqlite3_mprintf(
+ "REPLACE INTO '%q'.'%q_data'(id, block) VALUES(?,?)",
+ pConfig->zDb, pConfig->zName
+ ));
+ if( p->rc ) return;
+ }
+
+ sqlite3_bind_int64(p->pWriter, 1, iRowid);
+ sqlite3_bind_blob(p->pWriter, 2, pData, nData, SQLITE_STATIC);
+ sqlite3_step(p->pWriter);
+ p->rc = sqlite3_reset(p->pWriter);
+}
+
+/*
+** Execute the following SQL:
+**
+** DELETE FROM %_data WHERE id BETWEEN $iFirst AND $iLast
+*/
+static void fts5DataDelete(Fts5Index *p, i64 iFirst, i64 iLast){
+ if( p->rc!=SQLITE_OK ) return;
+
+ if( p->pDeleter==0 ){
+ int rc;
+ Fts5Config *pConfig = p->pConfig;
+ char *zSql = sqlite3_mprintf(
+ "DELETE FROM '%q'.'%q_data' WHERE id>=? AND id<=?",
+ pConfig->zDb, pConfig->zName
+ );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &p->pDeleter, 0);
+ sqlite3_free(zSql);
+ }
+ if( rc!=SQLITE_OK ){
+ p->rc = rc;
+ return;
+ }
+ }
+
+ sqlite3_bind_int64(p->pDeleter, 1, iFirst);
+ sqlite3_bind_int64(p->pDeleter, 2, iLast);
+ sqlite3_step(p->pDeleter);
+ p->rc = sqlite3_reset(p->pDeleter);
+}
+
+/*
+** Remove all records associated with segment iSegid.
+*/
+static void fts5DataRemoveSegment(Fts5Index *p, int iSegid){
+ i64 iFirst = FTS5_SEGMENT_ROWID(iSegid, 0);
+ i64 iLast = FTS5_SEGMENT_ROWID(iSegid+1, 0)-1;
+ fts5DataDelete(p, iFirst, iLast);
+ if( p->pIdxDeleter==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxDeleter, sqlite3_mprintf(
+ "DELETE FROM '%q'.'%q_idx' WHERE segid=?",
+ pConfig->zDb, pConfig->zName
+ ));
+ }
+ if( p->rc==SQLITE_OK ){
+ sqlite3_bind_int(p->pIdxDeleter, 1, iSegid);
+ sqlite3_step(p->pIdxDeleter);
+ p->rc = sqlite3_reset(p->pIdxDeleter);
+ }
+}
+
+/*
+** Release a reference to an Fts5Structure object returned by an earlier
+** call to fts5StructureRead() or fts5StructureDecode().
+*/
+static void fts5StructureRelease(Fts5Structure *pStruct){
+ if( pStruct && 0>=(--pStruct->nRef) ){
+ int i;
+ assert( pStruct->nRef==0 );
+ for(i=0; i<pStruct->nLevel; i++){
+ sqlite3_free(pStruct->aLevel[i].aSeg);
+ }
+ sqlite3_free(pStruct);
+ }
+}
+
+static void fts5StructureRef(Fts5Structure *pStruct){
+ pStruct->nRef++;
+}
+
+/*
+** Deserialize and return the structure record currently stored in serialized
+** form within buffer pData/nData.
+**
+** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
+** are over-allocated by one slot. This allows the structure contents
+** to be more easily edited.
+**
+** If an error occurs, *ppOut is set to NULL and an SQLite error code
+** returned. Otherwise, *ppOut is set to point to the new object and
+** SQLITE_OK returned.
+*/
+static int fts5StructureDecode(
+ const u8 *pData, /* Buffer containing serialized structure */
+ int nData, /* Size of buffer pData in bytes */
+ int *piCookie, /* Configuration cookie value */
+ Fts5Structure **ppOut /* OUT: Deserialized object */
+){
+ int rc = SQLITE_OK;
+ int i = 0;
+ int iLvl;
+ int nLevel = 0;
+ int nSegment = 0;
+ int nByte; /* Bytes of space to allocate at pRet */
+ Fts5Structure *pRet = 0; /* Structure object to return */
+
+ /* Grab the cookie value */
+ if( piCookie ) *piCookie = sqlite3Fts5Get32(pData);
+ i = 4;
+
+ /* Read the total number of levels and segments from the start of the
+ ** structure record. */
+ i += fts5GetVarint32(&pData[i], nLevel);
+ i += fts5GetVarint32(&pData[i], nSegment);
+ nByte = (
+ sizeof(Fts5Structure) + /* Main structure */
+ sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */
+ );
+ pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);
+
+ if( pRet ){
+ pRet->nRef = 1;
+ pRet->nLevel = nLevel;
+ pRet->nSegment = nSegment;
+ i += sqlite3Fts5GetVarint(&pData[i], &pRet->nWriteCounter);
+
+ for(iLvl=0; rc==SQLITE_OK && iLvl<nLevel; iLvl++){
+ Fts5StructureLevel *pLvl = &pRet->aLevel[iLvl];
+ int nTotal = 0;
+ int iSeg;
+
+ if( i>=nData ){
+ rc = FTS5_CORRUPT;
+ }else{
+ i += fts5GetVarint32(&pData[i], pLvl->nMerge);
+ i += fts5GetVarint32(&pData[i], nTotal);
+ assert( nTotal>=pLvl->nMerge );
+ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&rc,
+ nTotal * sizeof(Fts5StructureSegment)
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ pLvl->nSeg = nTotal;
+ for(iSeg=0; iSeg<nTotal; iSeg++){
+ if( i>=nData ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
+ i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].iSegid);
+ i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoFirst);
+ i += fts5GetVarint32(&pData[i], pLvl->aSeg[iSeg].pgnoLast);
+ }
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ fts5StructureRelease(pRet);
+ pRet = 0;
+ }
+ }
+
+ *ppOut = pRet;
+ return rc;
+}
+
+/*
+**
+*/
+static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
+ if( *pRc==SQLITE_OK ){
+ Fts5Structure *pStruct = *ppStruct;
+ int nLevel = pStruct->nLevel;
+ int nByte = (
+ sizeof(Fts5Structure) + /* Main structure */
+ sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
+ );
+
+ pStruct = sqlite3_realloc(pStruct, nByte);
+ if( pStruct ){
+ memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
+ pStruct->nLevel++;
+ *ppStruct = pStruct;
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+}
+
+/*
+** Extend level iLvl so that there is room for at least nExtra more
+** segments.
+*/
+static void fts5StructureExtendLevel(
+ int *pRc,
+ Fts5Structure *pStruct,
+ int iLvl,
+ int nExtra,
+ int bInsert
+){
+ if( *pRc==SQLITE_OK ){
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ Fts5StructureSegment *aNew;
+ int nByte;
+
+ nByte = (pLvl->nSeg + nExtra) * sizeof(Fts5StructureSegment);
+ aNew = sqlite3_realloc(pLvl->aSeg, nByte);
+ if( aNew ){
+ if( bInsert==0 ){
+ memset(&aNew[pLvl->nSeg], 0, sizeof(Fts5StructureSegment) * nExtra);
+ }else{
+ int nMove = pLvl->nSeg * sizeof(Fts5StructureSegment);
+ memmove(&aNew[nExtra], aNew, nMove);
+ memset(aNew, 0, sizeof(Fts5StructureSegment) * nExtra);
+ }
+ pLvl->aSeg = aNew;
+ }else{
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+}
+
+static Fts5Structure *fts5StructureReadUncached(Fts5Index *p){
+ Fts5Structure *pRet = 0;
+ Fts5Config *pConfig = p->pConfig;
+ int iCookie; /* Configuration cookie */
+ Fts5Data *pData;
+
+ pData = fts5DataRead(p, FTS5_STRUCTURE_ROWID);
+ if( p->rc==SQLITE_OK ){
+ /* TODO: Do we need this if the leaf-index is appended? Probably... */
+ memset(&pData->p[pData->nn], 0, FTS5_DATA_PADDING);
+ p->rc = fts5StructureDecode(pData->p, pData->nn, &iCookie, &pRet);
+ if( p->rc==SQLITE_OK && pConfig->iCookie!=iCookie ){
+ p->rc = sqlite3Fts5ConfigLoad(pConfig, iCookie);
+ }
+ fts5DataRelease(pData);
+ if( p->rc!=SQLITE_OK ){
+ fts5StructureRelease(pRet);
+ pRet = 0;
+ }
+ }
+
+ return pRet;
+}
+
+static i64 fts5IndexDataVersion(Fts5Index *p){
+ i64 iVersion = 0;
+
+ if( p->rc==SQLITE_OK ){
+ if( p->pDataVersion==0 ){
+ p->rc = fts5IndexPrepareStmt(p, &p->pDataVersion,
+ sqlite3_mprintf("PRAGMA %Q.data_version", p->pConfig->zDb)
+ );
+ if( p->rc ) return 0;
+ }
+
+ if( SQLITE_ROW==sqlite3_step(p->pDataVersion) ){
+ iVersion = sqlite3_column_int64(p->pDataVersion, 0);
+ }
+ p->rc = sqlite3_reset(p->pDataVersion);
+ }
+
+ return iVersion;
+}
+
+/*
+** Read, deserialize and return the structure record.
+**
+** The Fts5Structure.aLevel[] and each Fts5StructureLevel.aSeg[] array
+** are over-allocated as described for function fts5StructureDecode()
+** above.
+**
+** If an error occurs, NULL is returned and an error code left in the
+** Fts5Index handle. If an error has already occurred when this function
+** is called, it is a no-op.
+*/
+static Fts5Structure *fts5StructureRead(Fts5Index *p){
+
+ if( p->pStruct==0 ){
+ p->iStructVersion = fts5IndexDataVersion(p);
+ if( p->rc==SQLITE_OK ){
+ p->pStruct = fts5StructureReadUncached(p);
+ }
+ }
+
+#if 0
+ else{
+ Fts5Structure *pTest = fts5StructureReadUncached(p);
+ if( pTest ){
+ int i, j;
+ assert_nc( p->pStruct->nSegment==pTest->nSegment );
+ assert_nc( p->pStruct->nLevel==pTest->nLevel );
+ for(i=0; i<pTest->nLevel; i++){
+ assert_nc( p->pStruct->aLevel[i].nMerge==pTest->aLevel[i].nMerge );
+ assert_nc( p->pStruct->aLevel[i].nSeg==pTest->aLevel[i].nSeg );
+ for(j=0; j<pTest->aLevel[i].nSeg; j++){
+ Fts5StructureSegment *p1 = &pTest->aLevel[i].aSeg[j];
+ Fts5StructureSegment *p2 = &p->pStruct->aLevel[i].aSeg[j];
+ assert_nc( p1->iSegid==p2->iSegid );
+ assert_nc( p1->pgnoFirst==p2->pgnoFirst );
+ assert_nc( p1->pgnoLast==p2->pgnoLast );
+ }
+ }
+ fts5StructureRelease(pTest);
+ }
+ }
+#endif
+
+ if( p->rc!=SQLITE_OK ) return 0;
+ assert( p->iStructVersion!=0 );
+ assert( p->pStruct!=0 );
+ fts5StructureRef(p->pStruct);
+ return p->pStruct;
+}
+
+static void fts5StructureInvalidate(Fts5Index *p){
+ if( p->pStruct ){
+ fts5StructureRelease(p->pStruct);
+ p->pStruct = 0;
+ }
+}
+
+/*
+** Return the total number of segments in index structure pStruct. This
+** function is only ever used as part of assert() conditions.
+*/
+#ifdef SQLITE_DEBUG
+static int fts5StructureCountSegments(Fts5Structure *pStruct){
+ int nSegment = 0; /* Total number of segments */
+ if( pStruct ){
+ int iLvl; /* Used to iterate through levels */
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ nSegment += pStruct->aLevel[iLvl].nSeg;
+ }
+ }
+
+ return nSegment;
+}
+#endif
+
+#define fts5BufferSafeAppendBlob(pBuf, pBlob, nBlob) { \
+ assert( (pBuf)->nSpace>=((pBuf)->n+nBlob) ); \
+ memcpy(&(pBuf)->p[(pBuf)->n], pBlob, nBlob); \
+ (pBuf)->n += nBlob; \
+}
+
+#define fts5BufferSafeAppendVarint(pBuf, iVal) { \
+ (pBuf)->n += sqlite3Fts5PutVarint(&(pBuf)->p[(pBuf)->n], (iVal)); \
+ assert( (pBuf)->nSpace>=(pBuf)->n ); \
+}
+
+
+/*
+** Serialize and store the "structure" record.
+**
+** If an error occurs, leave an error code in the Fts5Index object. If an
+** error has already occurred, this function is a no-op.
+*/
+static void fts5StructureWrite(Fts5Index *p, Fts5Structure *pStruct){
+ if( p->rc==SQLITE_OK ){
+ Fts5Buffer buf; /* Buffer to serialize record into */
+ int iLvl; /* Used to iterate through levels */
+ int iCookie; /* Cookie value to store */
+
+ assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
+ memset(&buf, 0, sizeof(Fts5Buffer));
+
+ /* Append the current configuration cookie */
+ iCookie = p->pConfig->iCookie;
+ if( iCookie<0 ) iCookie = 0;
+
+ if( 0==sqlite3Fts5BufferSize(&p->rc, &buf, 4+9+9+9) ){
+ sqlite3Fts5Put32(buf.p, iCookie);
+ buf.n = 4;
+ fts5BufferSafeAppendVarint(&buf, pStruct->nLevel);
+ fts5BufferSafeAppendVarint(&buf, pStruct->nSegment);
+ fts5BufferSafeAppendVarint(&buf, (i64)pStruct->nWriteCounter);
+ }
+
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ int iSeg; /* Used to iterate through segments */
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->nMerge);
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->nSeg);
+ assert( pLvl->nMerge<=pLvl->nSeg );
+
+ for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].iSegid);
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoFirst);
+ fts5BufferAppendVarint(&p->rc, &buf, pLvl->aSeg[iSeg].pgnoLast);
+ }
+ }
+
+ fts5DataWrite(p, FTS5_STRUCTURE_ROWID, buf.p, buf.n);
+ fts5BufferFree(&buf);
+ }
+}
+
+#if 0
+static void fts5DebugStructure(int*,Fts5Buffer*,Fts5Structure*);
+static void fts5PrintStructure(const char *zCaption, Fts5Structure *pStruct){
+ int rc = SQLITE_OK;
+ Fts5Buffer buf;
+ memset(&buf, 0, sizeof(buf));
+ fts5DebugStructure(&rc, &buf, pStruct);
+ fprintf(stdout, "%s: %s\n", zCaption, buf.p);
+ fflush(stdout);
+ fts5BufferFree(&buf);
+}
+#else
+# define fts5PrintStructure(x,y)
+#endif
+
+static int fts5SegmentSize(Fts5StructureSegment *pSeg){
+ return 1 + pSeg->pgnoLast - pSeg->pgnoFirst;
+}
+
+/*
+** Return a copy of index structure pStruct. Except, promote as many
+** segments as possible to level iPromote. If an OOM occurs, NULL is
+** returned.
+*/
+static void fts5StructurePromoteTo(
+ Fts5Index *p,
+ int iPromote,
+ int szPromote,
+ Fts5Structure *pStruct
+){
+ int il, is;
+ Fts5StructureLevel *pOut = &pStruct->aLevel[iPromote];
+
+ if( pOut->nMerge==0 ){
+ for(il=iPromote+1; il<pStruct->nLevel; il++){
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[il];
+ if( pLvl->nMerge ) return;
+ for(is=pLvl->nSeg-1; is>=0; is--){
+ int sz = fts5SegmentSize(&pLvl->aSeg[is]);
+ if( sz>szPromote ) return;
+ fts5StructureExtendLevel(&p->rc, pStruct, iPromote, 1, 1);
+ if( p->rc ) return;
+ memcpy(pOut->aSeg, &pLvl->aSeg[is], sizeof(Fts5StructureSegment));
+ pOut->nSeg++;
+ pLvl->nSeg--;
+ }
+ }
+ }
+}
+
+/*
+** A new segment has just been written to level iLvl of index structure
+** pStruct. This function determines if any segments should be promoted
+** as a result. Segments are promoted in two scenarios:
+**
+** a) If the segment just written is smaller than one or more segments
+** within the previous populated level, it is promoted to the previous
+** populated level.
+**
+** b) If the segment just written is larger than the newest segment on
+** the next populated level, then that segment, and any other adjacent
+** segments that are also smaller than the one just written, are
+** promoted.
+**
+** If one or more segments are promoted, the structure object is updated
+** to reflect this.
+*/
+static void fts5StructurePromote(
+ Fts5Index *p, /* FTS5 backend object */
+ int iLvl, /* Index level just updated */
+ Fts5Structure *pStruct /* Index structure */
+){
+ if( p->rc==SQLITE_OK ){
+ int iTst;
+ int iPromote = -1;
+ int szPromote = 0; /* Promote anything this size or smaller */
+ Fts5StructureSegment *pSeg; /* Segment just written */
+ int szSeg; /* Size of segment just written */
+ int nSeg = pStruct->aLevel[iLvl].nSeg;
+
+ if( nSeg==0 ) return;
+ pSeg = &pStruct->aLevel[iLvl].aSeg[pStruct->aLevel[iLvl].nSeg-1];
+ szSeg = (1 + pSeg->pgnoLast - pSeg->pgnoFirst);
+
+ /* Check for condition (a) */
+ for(iTst=iLvl-1; iTst>=0 && pStruct->aLevel[iTst].nSeg==0; iTst--);
+ if( iTst>=0 ){
+ int i;
+ int szMax = 0;
+ Fts5StructureLevel *pTst = &pStruct->aLevel[iTst];
+ assert( pTst->nMerge==0 );
+ for(i=0; i<pTst->nSeg; i++){
+ int sz = pTst->aSeg[i].pgnoLast - pTst->aSeg[i].pgnoFirst + 1;
+ if( sz>szMax ) szMax = sz;
+ }
+ if( szMax>=szSeg ){
+ /* Condition (a) is true. Promote the newest segment on level
+ ** iLvl to level iTst. */
+ iPromote = iTst;
+ szPromote = szMax;
+ }
+ }
+
+ /* If condition (a) is not met, assume (b) is true. StructurePromoteTo()
+ ** is a no-op if it is not. */
+ if( iPromote<0 ){
+ iPromote = iLvl;
+ szPromote = szSeg;
+ }
+ fts5StructurePromoteTo(p, iPromote, szPromote, pStruct);
+ }
+}
+
+
+/*
+** Advance the iterator passed as the only argument. If the end of the
+** doclist-index page is reached, return non-zero.
+*/
+static int fts5DlidxLvlNext(Fts5DlidxLvl *pLvl){
+ Fts5Data *pData = pLvl->pData;
+
+ if( pLvl->iOff==0 ){
+ assert( pLvl->bEof==0 );
+ pLvl->iOff = 1;
+ pLvl->iOff += fts5GetVarint32(&pData->p[1], pLvl->iLeafPgno);
+ pLvl->iOff += fts5GetVarint(&pData->p[pLvl->iOff], (u64*)&pLvl->iRowid);
+ pLvl->iFirstOff = pLvl->iOff;
+ }else{
+ int iOff;
+ for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
+ if( pData->p[iOff] ) break;
+ }
+
+ if( iOff<pData->nn ){
+ i64 iVal;
+ pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
+ iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
+ pLvl->iRowid += iVal;
+ pLvl->iOff = iOff;
+ }else{
+ pLvl->bEof = 1;
+ }
+ }
+
+ return pLvl->bEof;
+}
+
+/*
+** Advance the iterator passed as the only argument.
+*/
+static int fts5DlidxIterNextR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){
+ Fts5DlidxLvl *pLvl = &pIter->aLvl[iLvl];
+
+ assert( iLvl<pIter->nLvl );
+ if( fts5DlidxLvlNext(pLvl) ){
+ if( (iLvl+1) < pIter->nLvl ){
+ fts5DlidxIterNextR(p, pIter, iLvl+1);
+ if( pLvl[1].bEof==0 ){
+ fts5DataRelease(pLvl->pData);
+ memset(pLvl, 0, sizeof(Fts5DlidxLvl));
+ pLvl->pData = fts5DataRead(p,
+ FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)
+ );
+ if( pLvl->pData ) fts5DlidxLvlNext(pLvl);
+ }
+ }
+ }
+
+ return pIter->aLvl[0].bEof;
+}
+static int fts5DlidxIterNext(Fts5Index *p, Fts5DlidxIter *pIter){
+ return fts5DlidxIterNextR(p, pIter, 0);
+}
+
+/*
+** The iterator passed as the first argument has the following fields set
+** as follows. This function sets up the rest of the iterator so that it
+** points to the first rowid in the doclist-index.
+**
+** pData:
+** pointer to doclist-index record,
+**
+** When this function is called pIter->iLeafPgno is the page number the
+** doclist is associated with (the one featuring the term).
+*/
+static int fts5DlidxIterFirst(Fts5DlidxIter *pIter){
+ int i;
+ for(i=0; i<pIter->nLvl; i++){
+ fts5DlidxLvlNext(&pIter->aLvl[i]);
+ }
+ return pIter->aLvl[0].bEof;
+}
+
+
+static int fts5DlidxIterEof(Fts5Index *p, Fts5DlidxIter *pIter){
+ return p->rc!=SQLITE_OK || pIter->aLvl[0].bEof;
+}
+
+static void fts5DlidxIterLast(Fts5Index *p, Fts5DlidxIter *pIter){
+ int i;
+
+ /* Advance each level to the last entry on the last page */
+ for(i=pIter->nLvl-1; p->rc==SQLITE_OK && i>=0; i--){
+ Fts5DlidxLvl *pLvl = &pIter->aLvl[i];
+ while( fts5DlidxLvlNext(pLvl)==0 );
+ pLvl->bEof = 0;
+
+ if( i>0 ){
+ Fts5DlidxLvl *pChild = &pLvl[-1];
+ fts5DataRelease(pChild->pData);
+ memset(pChild, 0, sizeof(Fts5DlidxLvl));
+ pChild->pData = fts5DataRead(p,
+ FTS5_DLIDX_ROWID(pIter->iSegid, i-1, pLvl->iLeafPgno)
+ );
+ }
+ }
+}
+
+/*
+** Move the iterator passed as the only argument to the previous entry.
+*/
+static int fts5DlidxLvlPrev(Fts5DlidxLvl *pLvl){
+ int iOff = pLvl->iOff;
+
+ assert( pLvl->bEof==0 );
+ if( iOff<=pLvl->iFirstOff ){
+ pLvl->bEof = 1;
+ }else{
+ u8 *a = pLvl->pData->p;
+ i64 iVal;
+ int iLimit;
+ int ii;
+ int nZero = 0;
+
+ /* Currently iOff points to the first byte of a varint. This block
+ ** decrements iOff until it points to the first byte of the previous
+ ** varint. Taking care not to read any memory locations that occur
+ ** before the buffer in memory. */
+ iLimit = (iOff>9 ? iOff-9 : 0);
+ for(iOff--; iOff>iLimit; iOff--){
+ if( (a[iOff-1] & 0x80)==0 ) break;
+ }
+
+ fts5GetVarint(&a[iOff], (u64*)&iVal);
+ pLvl->iRowid -= iVal;
+ pLvl->iLeafPgno--;
+
+ /* Skip backwards past any 0x00 varints. */
+ for(ii=iOff-1; ii>=pLvl->iFirstOff && a[ii]==0x00; ii--){
+ nZero++;
+ }
+ if( ii>=pLvl->iFirstOff && (a[ii] & 0x80) ){
+ /* The byte immediately before the last 0x00 byte has the 0x80 bit
+ ** set. So the last 0x00 is only a varint 0 if there are 8 more 0x80
+ ** bytes before a[ii]. */
+ int bZero = 0; /* True if last 0x00 counts */
+ if( (ii-8)>=pLvl->iFirstOff ){
+ int j;
+ for(j=1; j<=8 && (a[ii-j] & 0x80); j++);
+ bZero = (j>8);
+ }
+ if( bZero==0 ) nZero--;
+ }
+ pLvl->iLeafPgno -= nZero;
+ pLvl->iOff = iOff - nZero;
+ }
+
+ return pLvl->bEof;
+}
+
+static int fts5DlidxIterPrevR(Fts5Index *p, Fts5DlidxIter *pIter, int iLvl){
+ Fts5DlidxLvl *pLvl = &pIter->aLvl[iLvl];
+
+ assert( iLvl<pIter->nLvl );
+ if( fts5DlidxLvlPrev(pLvl) ){
+ if( (iLvl+1) < pIter->nLvl ){
+ fts5DlidxIterPrevR(p, pIter, iLvl+1);
+ if( pLvl[1].bEof==0 ){
+ fts5DataRelease(pLvl->pData);
+ memset(pLvl, 0, sizeof(Fts5DlidxLvl));
+ pLvl->pData = fts5DataRead(p,
+ FTS5_DLIDX_ROWID(pIter->iSegid, iLvl, pLvl[1].iLeafPgno)
+ );
+ if( pLvl->pData ){
+ while( fts5DlidxLvlNext(pLvl)==0 );
+ pLvl->bEof = 0;
+ }
+ }
+ }
+ }
+
+ return pIter->aLvl[0].bEof;
+}
+static int fts5DlidxIterPrev(Fts5Index *p, Fts5DlidxIter *pIter){
+ return fts5DlidxIterPrevR(p, pIter, 0);
+}
+
+/*
+** Free a doclist-index iterator object allocated by fts5DlidxIterInit().
+*/
+static void fts5DlidxIterFree(Fts5DlidxIter *pIter){
+ if( pIter ){
+ int i;
+ for(i=0; i<pIter->nLvl; i++){
+ fts5DataRelease(pIter->aLvl[i].pData);
+ }
+ sqlite3_free(pIter);
+ }
+}
+
+static Fts5DlidxIter *fts5DlidxIterInit(
+ Fts5Index *p, /* Fts5 Backend to iterate within */
+ int bRev, /* True for ORDER BY ASC */
+ int iSegid, /* Segment id */
+ int iLeafPg /* Leaf page number to load dlidx for */
+){
+ Fts5DlidxIter *pIter = 0;
+ int i;
+ int bDone = 0;
+
+ for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
+ int nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
+ Fts5DlidxIter *pNew;
+
+ pNew = (Fts5DlidxIter*)sqlite3_realloc(pIter, nByte);
+ if( pNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ i64 iRowid = FTS5_DLIDX_ROWID(iSegid, i, iLeafPg);
+ Fts5DlidxLvl *pLvl = &pNew->aLvl[i];
+ pIter = pNew;
+ memset(pLvl, 0, sizeof(Fts5DlidxLvl));
+ pLvl->pData = fts5DataRead(p, iRowid);
+ if( pLvl->pData && (pLvl->pData->p[0] & 0x0001)==0 ){
+ bDone = 1;
+ }
+ pIter->nLvl = i+1;
+ }
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pIter->iSegid = iSegid;
+ if( bRev==0 ){
+ fts5DlidxIterFirst(pIter);
+ }else{
+ fts5DlidxIterLast(p, pIter);
+ }
+ }
+
+ if( p->rc!=SQLITE_OK ){
+ fts5DlidxIterFree(pIter);
+ pIter = 0;
+ }
+
+ return pIter;
+}
+
+static i64 fts5DlidxIterRowid(Fts5DlidxIter *pIter){
+ return pIter->aLvl[0].iRowid;
+}
+static int fts5DlidxIterPgno(Fts5DlidxIter *pIter){
+ return pIter->aLvl[0].iLeafPgno;
+}
+
+/*
+** Load the next leaf page into the segment iterator.
+*/
+static void fts5SegIterNextPage(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter /* Iterator to advance to next page */
+){
+ Fts5Data *pLeaf;
+ Fts5StructureSegment *pSeg = pIter->pSeg;
+ fts5DataRelease(pIter->pLeaf);
+ pIter->iLeafPgno++;
+ if( pIter->pNextLeaf ){
+ pIter->pLeaf = pIter->pNextLeaf;
+ pIter->pNextLeaf = 0;
+ }else if( pIter->iLeafPgno<=pSeg->pgnoLast ){
+ pIter->pLeaf = fts5LeafRead(p,
+ FTS5_SEGMENT_ROWID(pSeg->iSegid, pIter->iLeafPgno)
+ );
+ }else{
+ pIter->pLeaf = 0;
+ }
+ pLeaf = pIter->pLeaf;
+
+ if( pLeaf ){
+ pIter->iPgidxOff = pLeaf->szLeaf;
+ if( fts5LeafIsTermless(pLeaf) ){
+ pIter->iEndofDoclist = pLeaf->nn+1;
+ }else{
+ pIter->iPgidxOff += fts5GetVarint32(&pLeaf->p[pIter->iPgidxOff],
+ pIter->iEndofDoclist
+ );
+ }
+ }
+}
+
+/*
+** Argument p points to a buffer containing a varint to be interpreted as a
+** position list size field. Read the varint and return the number of bytes
+** read. Before returning, set *pnSz to the number of bytes in the position
+** list, and *pbDel to true if the delete flag is set, or false otherwise.
+*/
+static int fts5GetPoslistSize(const u8 *p, int *pnSz, int *pbDel){
+ int nSz;
+ int n = 0;
+ fts5FastGetVarint32(p, n, nSz);
+ assert_nc( nSz>=0 );
+ *pnSz = nSz/2;
+ *pbDel = nSz & 0x0001;
+ return n;
+}
+
+/*
+** Fts5SegIter.iLeafOffset currently points to the first byte of a
+** position-list size field. Read the value of the field and store it
+** in the following variables:
+**
+** Fts5SegIter.nPos
+** Fts5SegIter.bDel
+**
+** Leave Fts5SegIter.iLeafOffset pointing to the first byte of the
+** position list content (if any).
+*/
+static void fts5SegIterLoadNPos(Fts5Index *p, Fts5SegIter *pIter){
+ if( p->rc==SQLITE_OK ){
+ int iOff = pIter->iLeafOffset; /* Offset to read at */
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ int iEod = MIN(pIter->iEndofDoclist, pIter->pLeaf->szLeaf);
+ pIter->bDel = 0;
+ pIter->nPos = 1;
+ if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){
+ pIter->bDel = 1;
+ iOff++;
+ if( iOff<iEod && pIter->pLeaf->p[iOff]==0 ){
+ pIter->nPos = 1;
+ iOff++;
+ }else{
+ pIter->nPos = 0;
+ }
+ }
+ }else{
+ int nSz;
+ fts5FastGetVarint32(pIter->pLeaf->p, iOff, nSz);
+ pIter->bDel = (nSz & 0x0001);
+ pIter->nPos = nSz>>1;
+ assert_nc( pIter->nPos>=0 );
+ }
+ pIter->iLeafOffset = iOff;
+ }
+}
+
+static void fts5SegIterLoadRowid(Fts5Index *p, Fts5SegIter *pIter){
+ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
+ int iOff = pIter->iLeafOffset;
+
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ if( iOff>=pIter->pLeaf->szLeaf ){
+ fts5SegIterNextPage(p, pIter);
+ if( pIter->pLeaf==0 ){
+ if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
+ return;
+ }
+ iOff = 4;
+ a = pIter->pLeaf->p;
+ }
+ iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+}
+
+/*
+** Fts5SegIter.iLeafOffset currently points to the first byte of the
+** "nSuffix" field of a term. Function parameter nKeep contains the value
+** of the "nPrefix" field (if there was one - it is passed 0 if this is
+** the first term in the segment).
+**
+** This function populates:
+**
+** Fts5SegIter.term
+** Fts5SegIter.rowid
+**
+** accordingly and leaves (Fts5SegIter.iLeafOffset) set to the content of
+** the first position list. The position list belonging to document
+** (Fts5SegIter.iRowid).
+*/
+static void fts5SegIterLoadTerm(Fts5Index *p, Fts5SegIter *pIter, int nKeep){
+ u8 *a = pIter->pLeaf->p; /* Buffer to read data from */
+ int iOff = pIter->iLeafOffset; /* Offset to read at */
+ int nNew; /* Bytes of new data */
+
+ iOff += fts5GetVarint32(&a[iOff], nNew);
+ if( iOff+nNew>pIter->pLeaf->nn ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+ pIter->term.n = nKeep;
+ fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]);
+ iOff += nNew;
+ pIter->iTermLeafOffset = iOff;
+ pIter->iTermLeafPgno = pIter->iLeafPgno;
+ pIter->iLeafOffset = iOff;
+
+ if( pIter->iPgidxOff>=pIter->pLeaf->nn ){
+ pIter->iEndofDoclist = pIter->pLeaf->nn+1;
+ }else{
+ int nExtra;
+ pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], nExtra);
+ pIter->iEndofDoclist += nExtra;
+ }
+
+ fts5SegIterLoadRowid(p, pIter);
+}
+
+static void fts5SegIterNext(Fts5Index*, Fts5SegIter*, int*);
+static void fts5SegIterNext_Reverse(Fts5Index*, Fts5SegIter*, int*);
+static void fts5SegIterNext_None(Fts5Index*, Fts5SegIter*, int*);
+
+static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
+ if( pIter->flags & FTS5_SEGITER_REVERSE ){
+ pIter->xNext = fts5SegIterNext_Reverse;
+ }else if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ pIter->xNext = fts5SegIterNext_None;
+ }else{
+ pIter->xNext = fts5SegIterNext;
+ }
+}
+
+/*
+** Initialize the iterator object pIter to iterate through the entries in
+** segment pSeg. The iterator is left pointing to the first entry when
+** this function returns.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** an error has already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterInit(
+ Fts5Index *p, /* FTS index object */
+ Fts5StructureSegment *pSeg, /* Description of segment */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ if( pSeg->pgnoFirst==0 ){
+ /* This happens if the segment is being used as an input to an incremental
+ ** merge and all data has already been "trimmed". See function
+ ** fts5TrimSegments() for details. In this case leave the iterator empty.
+ ** The caller will see the (pIter->pLeaf==0) and assume the iterator is
+ ** at EOF already. */
+ assert( pIter->pLeaf==0 );
+ return;
+ }
+
+ if( p->rc==SQLITE_OK ){
+ memset(pIter, 0, sizeof(*pIter));
+ fts5SegIterSetNext(p, pIter);
+ pIter->pSeg = pSeg;
+ pIter->iLeafPgno = pSeg->pgnoFirst-1;
+ fts5SegIterNextPage(p, pIter);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pIter->iLeafOffset = 4;
+ assert_nc( pIter->pLeaf->nn>4 );
+ assert( fts5LeafFirstTermOff(pIter->pLeaf)==4 );
+ pIter->iPgidxOff = pIter->pLeaf->szLeaf+1;
+ fts5SegIterLoadTerm(p, pIter, 0);
+ fts5SegIterLoadNPos(p, pIter);
+ }
+}
+
+/*
+** This function is only ever called on iterators created by calls to
+** Fts5IndexQuery() with the FTS5INDEX_QUERY_DESC flag set.
+**
+** The iterator is in an unusual state when this function is called: the
+** Fts5SegIter.iLeafOffset variable is set to the offset of the start of
+** the position-list size field for the first relevant rowid on the page.
+** Fts5SegIter.rowid is set, but nPos and bDel are not.
+**
+** This function advances the iterator so that it points to the last
+** relevant rowid on the page and, if necessary, initializes the
+** aRowidOffset[] and iRowidOffset variables. At this point the iterator
+** is in its regular state - Fts5SegIter.iLeafOffset points to the first
+** byte of the position list content associated with said rowid.
+*/
+static void fts5SegIterReverseInitPage(Fts5Index *p, Fts5SegIter *pIter){
+ int eDetail = p->pConfig->eDetail;
+ int n = pIter->pLeaf->szLeaf;
+ int i = pIter->iLeafOffset;
+ u8 *a = pIter->pLeaf->p;
+ int iRowidOffset = 0;
+
+ if( n>pIter->iEndofDoclist ){
+ n = pIter->iEndofDoclist;
+ }
+
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ while( 1 ){
+ i64 iDelta = 0;
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ /* todo */
+ if( i<n && a[i]==0 ){
+ i++;
+ if( i<n && a[i]==0 ) i++;
+ }
+ }else{
+ int nPos;
+ int bDummy;
+ i += fts5GetPoslistSize(&a[i], &nPos, &bDummy);
+ i += nPos;
+ }
+ if( i>=n ) break;
+ i += fts5GetVarint(&a[i], (u64*)&iDelta);
+ pIter->iRowid += iDelta;
+
+ /* If necessary, grow the pIter->aRowidOffset[] array. */
+ if( iRowidOffset>=pIter->nRowidOffset ){
+ int nNew = pIter->nRowidOffset + 8;
+ int *aNew = (int*)sqlite3_realloc(pIter->aRowidOffset, nNew*sizeof(int));
+ if( aNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ break;
+ }
+ pIter->aRowidOffset = aNew;
+ pIter->nRowidOffset = nNew;
+ }
+
+ pIter->aRowidOffset[iRowidOffset++] = pIter->iLeafOffset;
+ pIter->iLeafOffset = i;
+ }
+ pIter->iRowidOffset = iRowidOffset;
+ fts5SegIterLoadNPos(p, pIter);
+}
+
+/*
+**
+*/
+static void fts5SegIterReverseNewPage(Fts5Index *p, Fts5SegIter *pIter){
+ assert( pIter->flags & FTS5_SEGITER_REVERSE );
+ assert( pIter->flags & FTS5_SEGITER_ONETERM );
+
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ while( p->rc==SQLITE_OK && pIter->iLeafPgno>pIter->iTermLeafPgno ){
+ Fts5Data *pNew;
+ pIter->iLeafPgno--;
+ pNew = fts5DataRead(p, FTS5_SEGMENT_ROWID(
+ pIter->pSeg->iSegid, pIter->iLeafPgno
+ ));
+ if( pNew ){
+ /* iTermLeafOffset may be equal to szLeaf if the term is the last
+ ** thing on the page - i.e. the first rowid is on the following page.
+ ** In this case leave pIter->pLeaf==0, this iterator is at EOF. */
+ if( pIter->iLeafPgno==pIter->iTermLeafPgno ){
+ assert( pIter->pLeaf==0 );
+ if( pIter->iTermLeafOffset<pNew->szLeaf ){
+ pIter->pLeaf = pNew;
+ pIter->iLeafOffset = pIter->iTermLeafOffset;
+ }
+ }else{
+ int iRowidOff;
+ iRowidOff = fts5LeafFirstRowidOff(pNew);
+ if( iRowidOff ){
+ pIter->pLeaf = pNew;
+ pIter->iLeafOffset = iRowidOff;
+ }
+ }
+
+ if( pIter->pLeaf ){
+ u8 *a = &pIter->pLeaf->p[pIter->iLeafOffset];
+ pIter->iLeafOffset += fts5GetVarint(a, (u64*)&pIter->iRowid);
+ break;
+ }else{
+ fts5DataRelease(pNew);
+ }
+ }
+ }
+
+ if( pIter->pLeaf ){
+ pIter->iEndofDoclist = pIter->pLeaf->nn+1;
+ fts5SegIterReverseInitPage(p, pIter);
+ }
+}
+
+/*
+** Return true if the iterator passed as the second argument currently
+** points to a delete marker. A delete marker is an entry with a 0 byte
+** position-list.
+*/
+static int fts5MultiIterIsEmpty(Fts5Index *p, Fts5Iter *pIter){
+ Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ return (p->rc==SQLITE_OK && pSeg->pLeaf && pSeg->nPos==0);
+}
+
+/*
+** Advance iterator pIter to the next entry.
+**
+** This version of fts5SegIterNext() is only used by reverse iterators.
+*/
+static void fts5SegIterNext_Reverse(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int *pbUnused /* Unused */
+){
+ assert( pIter->flags & FTS5_SEGITER_REVERSE );
+ assert( pIter->pNextLeaf==0 );
+ UNUSED_PARAM(pbUnused);
+
+ if( pIter->iRowidOffset>0 ){
+ u8 *a = pIter->pLeaf->p;
+ int iOff;
+ i64 iDelta;
+
+ pIter->iRowidOffset--;
+ pIter->iLeafOffset = pIter->aRowidOffset[pIter->iRowidOffset];
+ fts5SegIterLoadNPos(p, pIter);
+ iOff = pIter->iLeafOffset;
+ if( p->pConfig->eDetail!=FTS5_DETAIL_NONE ){
+ iOff += pIter->nPos;
+ }
+ fts5GetVarint(&a[iOff], (u64*)&iDelta);
+ pIter->iRowid -= iDelta;
+ }else{
+ fts5SegIterReverseNewPage(p, pIter);
+ }
+}
+
+/*
+** Advance iterator pIter to the next entry.
+**
+** This version of fts5SegIterNext() is only used if detail=none and the
+** iterator is not a reverse direction iterator.
+*/
+static void fts5SegIterNext_None(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int *pbNewTerm /* OUT: Set for new term */
+){
+ int iOff;
+
+ assert( p->rc==SQLITE_OK );
+ assert( (pIter->flags & FTS5_SEGITER_REVERSE)==0 );
+ assert( p->pConfig->eDetail==FTS5_DETAIL_NONE );
+
+ ASSERT_SZLEAF_OK(pIter->pLeaf);
+ iOff = pIter->iLeafOffset;
+
+ /* Next entry is on the next page */
+ if( pIter->pSeg && iOff>=pIter->pLeaf->szLeaf ){
+ fts5SegIterNextPage(p, pIter);
+ if( p->rc || pIter->pLeaf==0 ) return;
+ pIter->iRowid = 0;
+ iOff = 4;
+ }
+
+ if( iOff<pIter->iEndofDoclist ){
+ /* Next entry is on the current page */
+ i64 iDelta;
+ iOff += sqlite3Fts5GetVarint(&pIter->pLeaf->p[iOff], (u64*)&iDelta);
+ pIter->iLeafOffset = iOff;
+ pIter->iRowid += iDelta;
+ }else if( (pIter->flags & FTS5_SEGITER_ONETERM)==0 ){
+ if( pIter->pSeg ){
+ int nKeep = 0;
+ if( iOff!=fts5LeafFirstTermOff(pIter->pLeaf) ){
+ iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep);
+ }
+ pIter->iLeafOffset = iOff;
+ fts5SegIterLoadTerm(p, pIter, nKeep);
+ }else{
+ const u8 *pList = 0;
+ const char *zTerm = 0;
+ int nList;
+ sqlite3Fts5HashScanNext(p->pHash);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ if( pList==0 ) goto next_none_eof;
+ pIter->pLeaf->p = (u8*)pList;
+ pIter->pLeaf->nn = nList;
+ pIter->pLeaf->szLeaf = nList;
+ pIter->iEndofDoclist = nList;
+ sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm);
+ pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
+ }
+
+ if( pbNewTerm ) *pbNewTerm = 1;
+ }else{
+ goto next_none_eof;
+ }
+
+ fts5SegIterLoadNPos(p, pIter);
+
+ return;
+ next_none_eof:
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+}
+
+
+/*
+** Advance iterator pIter to the next entry.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. It
+** is not considered an error if the iterator reaches EOF. If an error has
+** already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterNext(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int *pbNewTerm /* OUT: Set for new term */
+){
+ Fts5Data *pLeaf = pIter->pLeaf;
+ int iOff;
+ int bNewTerm = 0;
+ int nKeep = 0;
+ u8 *a;
+ int n;
+
+ assert( pbNewTerm==0 || *pbNewTerm==0 );
+ assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
+
+ /* Search for the end of the position list within the current page. */
+ a = pLeaf->p;
+ n = pLeaf->szLeaf;
+
+ ASSERT_SZLEAF_OK(pLeaf);
+ iOff = pIter->iLeafOffset + pIter->nPos;
+
+ if( iOff<n ){
+ /* The next entry is on the current page. */
+ assert_nc( iOff<=pIter->iEndofDoclist );
+ if( iOff>=pIter->iEndofDoclist ){
+ bNewTerm = 1;
+ if( iOff!=fts5LeafFirstTermOff(pLeaf) ){
+ iOff += fts5GetVarint32(&a[iOff], nKeep);
+ }
+ }else{
+ u64 iDelta;
+ iOff += sqlite3Fts5GetVarint(&a[iOff], &iDelta);
+ pIter->iRowid += iDelta;
+ assert_nc( iDelta>0 );
+ }
+ pIter->iLeafOffset = iOff;
+
+ }else if( pIter->pSeg==0 ){
+ const u8 *pList = 0;
+ const char *zTerm = 0;
+ int nList = 0;
+ assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
+ if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
+ sqlite3Fts5HashScanNext(p->pHash);
+ sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
+ }
+ if( pList==0 ){
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ }else{
+ pIter->pLeaf->p = (u8*)pList;
+ pIter->pLeaf->nn = nList;
+ pIter->pLeaf->szLeaf = nList;
+ pIter->iEndofDoclist = nList+1;
+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm),
+ (u8*)zTerm);
+ pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
+ *pbNewTerm = 1;
+ }
+ }else{
+ iOff = 0;
+ /* Next entry is not on the current page */
+ while( iOff==0 ){
+ fts5SegIterNextPage(p, pIter);
+ pLeaf = pIter->pLeaf;
+ if( pLeaf==0 ) break;
+ ASSERT_SZLEAF_OK(pLeaf);
+ if( (iOff = fts5LeafFirstRowidOff(pLeaf)) && iOff<pLeaf->szLeaf ){
+ iOff += sqlite3Fts5GetVarint(&pLeaf->p[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+
+ if( pLeaf->nn>pLeaf->szLeaf ){
+ pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
+ &pLeaf->p[pLeaf->szLeaf], pIter->iEndofDoclist
+ );
+ }
+ }
+ else if( pLeaf->nn>pLeaf->szLeaf ){
+ pIter->iPgidxOff = pLeaf->szLeaf + fts5GetVarint32(
+ &pLeaf->p[pLeaf->szLeaf], iOff
+ );
+ pIter->iLeafOffset = iOff;
+ pIter->iEndofDoclist = iOff;
+ bNewTerm = 1;
+ }
+ assert_nc( iOff<pLeaf->szLeaf );
+ if( iOff>pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+ }
+ }
+
+ /* Check if the iterator is now at EOF. If so, return early. */
+ if( pIter->pLeaf ){
+ if( bNewTerm ){
+ if( pIter->flags & FTS5_SEGITER_ONETERM ){
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ }else{
+ fts5SegIterLoadTerm(p, pIter, nKeep);
+ fts5SegIterLoadNPos(p, pIter);
+ if( pbNewTerm ) *pbNewTerm = 1;
+ }
+ }else{
+ /* The following could be done by calling fts5SegIterLoadNPos(). But
+ ** this block is particularly performance critical, so equivalent
+ ** code is inlined.
+ **
+ ** Later: Switched back to fts5SegIterLoadNPos() because it supports
+ ** detail=none mode. Not ideal.
+ */
+ int nSz;
+ assert( p->rc==SQLITE_OK );
+ fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
+ pIter->bDel = (nSz & 0x0001);
+ pIter->nPos = nSz>>1;
+ assert_nc( pIter->nPos>=0 );
+ }
+ }
+}
+
+#define SWAPVAL(T, a, b) { T tmp; tmp=a; a=b; b=tmp; }
+
+#define fts5IndexSkipVarint(a, iOff) { \
+ int iEnd = iOff+9; \
+ while( (a[iOff++] & 0x80) && iOff<iEnd ); \
+}
+
+/*
+** Iterator pIter currently points to the first rowid in a doclist. This
+** function sets the iterator up so that iterates in reverse order through
+** the doclist.
+*/
+static void fts5SegIterReverse(Fts5Index *p, Fts5SegIter *pIter){
+ Fts5DlidxIter *pDlidx = pIter->pDlidx;
+ Fts5Data *pLast = 0;
+ int pgnoLast = 0;
+
+ if( pDlidx ){
+ int iSegid = pIter->pSeg->iSegid;
+ pgnoLast = fts5DlidxIterPgno(pDlidx);
+ pLast = fts5DataRead(p, FTS5_SEGMENT_ROWID(iSegid, pgnoLast));
+ }else{
+ Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */
+
+ /* Currently, Fts5SegIter.iLeafOffset points to the first byte of
+ ** position-list content for the current rowid. Back it up so that it
+ ** points to the start of the position-list size field. */
+ int iPoslist;
+ if( pIter->iTermLeafPgno==pIter->iLeafPgno ){
+ iPoslist = pIter->iTermLeafOffset;
+ }else{
+ iPoslist = 4;
+ }
+ fts5IndexSkipVarint(pLeaf->p, iPoslist);
+ pIter->iLeafOffset = iPoslist;
+
+ /* If this condition is true then the largest rowid for the current
+ ** term may not be stored on the current page. So search forward to
+ ** see where said rowid really is. */
+ if( pIter->iEndofDoclist>=pLeaf->szLeaf ){
+ int pgno;
+ Fts5StructureSegment *pSeg = pIter->pSeg;
+
+ /* The last rowid in the doclist may not be on the current page. Search
+ ** forward to find the page containing the last rowid. */
+ for(pgno=pIter->iLeafPgno+1; !p->rc && pgno<=pSeg->pgnoLast; pgno++){
+ i64 iAbs = FTS5_SEGMENT_ROWID(pSeg->iSegid, pgno);
+ Fts5Data *pNew = fts5DataRead(p, iAbs);
+ if( pNew ){
+ int iRowid, bTermless;
+ iRowid = fts5LeafFirstRowidOff(pNew);
+ bTermless = fts5LeafIsTermless(pNew);
+ if( iRowid ){
+ SWAPVAL(Fts5Data*, pNew, pLast);
+ pgnoLast = pgno;
+ }
+ fts5DataRelease(pNew);
+ if( bTermless==0 ) break;
+ }
+ }
+ }
+ }
+
+ /* If pLast is NULL at this point, then the last rowid for this doclist
+ ** lies on the page currently indicated by the iterator. In this case
+ ** pIter->iLeafOffset is already set to point to the position-list size
+ ** field associated with the first relevant rowid on the page.
+ **
+ ** Or, if pLast is non-NULL, then it is the page that contains the last
+ ** rowid. In this case configure the iterator so that it points to the
+ ** first rowid on this page.
+ */
+ if( pLast ){
+ int iOff;
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = pLast;
+ pIter->iLeafPgno = pgnoLast;
+ iOff = fts5LeafFirstRowidOff(pLast);
+ iOff += fts5GetVarint(&pLast->p[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+
+ if( fts5LeafIsTermless(pLast) ){
+ pIter->iEndofDoclist = pLast->nn+1;
+ }else{
+ pIter->iEndofDoclist = fts5LeafFirstTermOff(pLast);
+ }
+
+ }
+
+ fts5SegIterReverseInitPage(p, pIter);
+}
+
+/*
+** Iterator pIter currently points to the first rowid of a doclist.
+** There is a doclist-index associated with the final term on the current
+** page. If the current term is the last term on the page, load the
+** doclist-index from disk and initialize an iterator at (pIter->pDlidx).
+*/
+static void fts5SegIterLoadDlidx(Fts5Index *p, Fts5SegIter *pIter){
+ int iSeg = pIter->pSeg->iSegid;
+ int bRev = (pIter->flags & FTS5_SEGITER_REVERSE);
+ Fts5Data *pLeaf = pIter->pLeaf; /* Current leaf data */
+
+ assert( pIter->flags & FTS5_SEGITER_ONETERM );
+ assert( pIter->pDlidx==0 );
+
+ /* Check if the current doclist ends on this page. If it does, return
+ ** early without loading the doclist-index (as it belongs to a different
+ ** term. */
+ if( pIter->iTermLeafPgno==pIter->iLeafPgno
+ && pIter->iEndofDoclist<pLeaf->szLeaf
+ ){
+ return;
+ }
+
+ pIter->pDlidx = fts5DlidxIterInit(p, bRev, iSeg, pIter->iTermLeafPgno);
+}
+
+/*
+** The iterator object passed as the second argument currently contains
+** no valid values except for the Fts5SegIter.pLeaf member variable. This
+** function searches the leaf page for a term matching (pTerm/nTerm).
+**
+** If the specified term is found on the page, then the iterator is left
+** pointing to it. If argument bGe is zero and the term is not found,
+** the iterator is left pointing at EOF.
+**
+** If bGe is non-zero and the specified term is not found, then the
+** iterator is left pointing to the smallest term in the segment that
+** is larger than the specified term, even if this term is not on the
+** current page.
+*/
+static void fts5LeafSeek(
+ Fts5Index *p, /* Leave any error code here */
+ int bGe, /* True for a >= search */
+ Fts5SegIter *pIter, /* Iterator to seek */
+ const u8 *pTerm, int nTerm /* Term to search for */
+){
+ int iOff;
+ const u8 *a = pIter->pLeaf->p;
+ int szLeaf = pIter->pLeaf->szLeaf;
+ int n = pIter->pLeaf->nn;
+
+ int nMatch = 0;
+ int nKeep = 0;
+ int nNew = 0;
+ int iTermOff;
+ int iPgidx; /* Current offset in pgidx */
+ int bEndOfPage = 0;
+
+ assert( p->rc==SQLITE_OK );
+
+ iPgidx = szLeaf;
+ iPgidx += fts5GetVarint32(&a[iPgidx], iTermOff);
+ iOff = iTermOff;
+ if( iOff>n ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+
+ while( 1 ){
+
+ /* Figure out how many new bytes are in this term */
+ fts5FastGetVarint32(a, iOff, nNew);
+ if( nKeep<nMatch ){
+ goto search_failed;
+ }
+
+ assert( nKeep>=nMatch );
+ if( nKeep==nMatch ){
+ int nCmp;
+ int i;
+ nCmp = MIN(nNew, nTerm-nMatch);
+ for(i=0; i<nCmp; i++){
+ if( a[iOff+i]!=pTerm[nMatch+i] ) break;
+ }
+ nMatch += i;
+
+ if( nTerm==nMatch ){
+ if( i==nNew ){
+ goto search_success;
+ }else{
+ goto search_failed;
+ }
+ }else if( i<nNew && a[iOff+i]>pTerm[nMatch] ){
+ goto search_failed;
+ }
+ }
+
+ if( iPgidx>=n ){
+ bEndOfPage = 1;
+ break;
+ }
+
+ iPgidx += fts5GetVarint32(&a[iPgidx], nKeep);
+ iTermOff += nKeep;
+ iOff = iTermOff;
+
+ if( iOff>=n ){
+ p->rc = FTS5_CORRUPT;
+ return;
+ }
+
+ /* Read the nKeep field of the next term. */
+ fts5FastGetVarint32(a, iOff, nKeep);
+ }
+
+ search_failed:
+ if( bGe==0 ){
+ fts5DataRelease(pIter->pLeaf);
+ pIter->pLeaf = 0;
+ return;
+ }else if( bEndOfPage ){
+ do {
+ fts5SegIterNextPage(p, pIter);
+ if( pIter->pLeaf==0 ) return;
+ a = pIter->pLeaf->p;
+ if( fts5LeafIsTermless(pIter->pLeaf)==0 ){
+ iPgidx = pIter->pLeaf->szLeaf;
+ iPgidx += fts5GetVarint32(&pIter->pLeaf->p[iPgidx], iOff);
+ if( iOff<4 || iOff>=pIter->pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ nKeep = 0;
+ iTermOff = iOff;
+ n = pIter->pLeaf->nn;
+ iOff += fts5GetVarint32(&a[iOff], nNew);
+ break;
+ }
+ }
+ }while( 1 );
+ }
+
+ search_success:
+
+ pIter->iLeafOffset = iOff + nNew;
+ pIter->iTermLeafOffset = pIter->iLeafOffset;
+ pIter->iTermLeafPgno = pIter->iLeafPgno;
+
+ fts5BufferSet(&p->rc, &pIter->term, nKeep, pTerm);
+ fts5BufferAppendBlob(&p->rc, &pIter->term, nNew, &a[iOff]);
+
+ if( iPgidx>=n ){
+ pIter->iEndofDoclist = pIter->pLeaf->nn+1;
+ }else{
+ int nExtra;
+ iPgidx += fts5GetVarint32(&a[iPgidx], nExtra);
+ pIter->iEndofDoclist = iTermOff + nExtra;
+ }
+ pIter->iPgidxOff = iPgidx;
+
+ fts5SegIterLoadRowid(p, pIter);
+ fts5SegIterLoadNPos(p, pIter);
+}
+
+static sqlite3_stmt *fts5IdxSelectStmt(Fts5Index *p){
+ if( p->pIdxSelect==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxSelect, sqlite3_mprintf(
+ "SELECT pgno FROM '%q'.'%q_idx' WHERE "
+ "segid=? AND term<=? ORDER BY term DESC LIMIT 1",
+ pConfig->zDb, pConfig->zName
+ ));
+ }
+ return p->pIdxSelect;
+}
+
+/*
+** Initialize the object pIter to point to term pTerm/nTerm within segment
+** pSeg. If there is no such term in the index, the iterator is set to EOF.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** an error has already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterSeekInit(
+ Fts5Index *p, /* FTS5 backend */
+ const u8 *pTerm, int nTerm, /* Term to seek to */
+ int flags, /* Mask of FTS5INDEX_XXX flags */
+ Fts5StructureSegment *pSeg, /* Description of segment */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ int iPg = 1;
+ int bGe = (flags & FTS5INDEX_QUERY_SCAN);
+ int bDlidx = 0; /* True if there is a doclist-index */
+ sqlite3_stmt *pIdxSelect = 0;
+
+ assert( bGe==0 || (flags & FTS5INDEX_QUERY_DESC)==0 );
+ assert( pTerm && nTerm );
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->pSeg = pSeg;
+
+ /* This block sets stack variable iPg to the leaf page number that may
+ ** contain term (pTerm/nTerm), if it is present in the segment. */
+ pIdxSelect = fts5IdxSelectStmt(p);
+ if( p->rc ) return;
+ sqlite3_bind_int(pIdxSelect, 1, pSeg->iSegid);
+ sqlite3_bind_blob(pIdxSelect, 2, pTerm, nTerm, SQLITE_STATIC);
+ if( SQLITE_ROW==sqlite3_step(pIdxSelect) ){
+ i64 val = sqlite3_column_int(pIdxSelect, 0);
+ iPg = (int)(val>>1);
+ bDlidx = (val & 0x0001);
+ }
+ p->rc = sqlite3_reset(pIdxSelect);
+
+ if( iPg<pSeg->pgnoFirst ){
+ iPg = pSeg->pgnoFirst;
+ bDlidx = 0;
+ }
+
+ pIter->iLeafPgno = iPg - 1;
+ fts5SegIterNextPage(p, pIter);
+
+ if( pIter->pLeaf ){
+ fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
+ }
+
+ if( p->rc==SQLITE_OK && bGe==0 ){
+ pIter->flags |= FTS5_SEGITER_ONETERM;
+ if( pIter->pLeaf ){
+ if( flags & FTS5INDEX_QUERY_DESC ){
+ pIter->flags |= FTS5_SEGITER_REVERSE;
+ }
+ if( bDlidx ){
+ fts5SegIterLoadDlidx(p, pIter);
+ }
+ if( flags & FTS5INDEX_QUERY_DESC ){
+ fts5SegIterReverse(p, pIter);
+ }
+ }
+ }
+
+ fts5SegIterSetNext(p, pIter);
+
+ /* Either:
+ **
+ ** 1) an error has occurred, or
+ ** 2) the iterator points to EOF, or
+ ** 3) the iterator points to an entry with term (pTerm/nTerm), or
+ ** 4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points
+ ** to an entry with a term greater than or equal to (pTerm/nTerm).
+ */
+ assert( p->rc!=SQLITE_OK /* 1 */
+ || pIter->pLeaf==0 /* 2 */
+ || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0 /* 3 */
+ || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0) /* 4 */
+ );
+}
+
+/*
+** Initialize the object pIter to point to term pTerm/nTerm within the
+** in-memory hash table. If there is no such term in the hash-table, the
+** iterator is set to EOF.
+**
+** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
+** an error has already occurred when this function is called, it is a no-op.
+*/
+static void fts5SegIterHashInit(
+ Fts5Index *p, /* FTS5 backend */
+ const u8 *pTerm, int nTerm, /* Term to seek to */
+ int flags, /* Mask of FTS5INDEX_XXX flags */
+ Fts5SegIter *pIter /* Object to populate */
+){
+ const u8 *pList = 0;
+ int nList = 0;
+ const u8 *z = 0;
+ int n = 0;
+
+ assert( p->pHash );
+ assert( p->rc==SQLITE_OK );
+
+ if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
+ p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
+ sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList);
+ n = (z ? (int)strlen((const char*)z) : 0);
+ }else{
+ pIter->flags |= FTS5_SEGITER_ONETERM;
+ sqlite3Fts5HashQuery(p->pHash, (const char*)pTerm, nTerm, &pList, &nList);
+ z = pTerm;
+ n = nTerm;
+ }
+
+ if( pList ){
+ Fts5Data *pLeaf;
+ sqlite3Fts5BufferSet(&p->rc, &pIter->term, n, z);
+ pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
+ if( pLeaf==0 ) return;
+ pLeaf->p = (u8*)pList;
+ pLeaf->nn = pLeaf->szLeaf = nList;
+ pIter->pLeaf = pLeaf;
+ pIter->iLeafOffset = fts5GetVarint(pLeaf->p, (u64*)&pIter->iRowid);
+ pIter->iEndofDoclist = pLeaf->nn;
+
+ if( flags & FTS5INDEX_QUERY_DESC ){
+ pIter->flags |= FTS5_SEGITER_REVERSE;
+ fts5SegIterReverseInitPage(p, pIter);
+ }else{
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ }
+
+ fts5SegIterSetNext(p, pIter);
+}
+
+/*
+** Zero the iterator passed as the only argument.
+*/
+static void fts5SegIterClear(Fts5SegIter *pIter){
+ fts5BufferFree(&pIter->term);
+ fts5DataRelease(pIter->pLeaf);
+ fts5DataRelease(pIter->pNextLeaf);
+ fts5DlidxIterFree(pIter->pDlidx);
+ sqlite3_free(pIter->aRowidOffset);
+ memset(pIter, 0, sizeof(Fts5SegIter));
+}
+
+#ifdef SQLITE_DEBUG
+
+/*
+** This function is used as part of the big assert() procedure implemented by
+** fts5AssertMultiIterSetup(). It ensures that the result currently stored
+** in *pRes is the correct result of comparing the current positions of the
+** two iterators.
+*/
+static void fts5AssertComparisonResult(
+ Fts5Iter *pIter,
+ Fts5SegIter *p1,
+ Fts5SegIter *p2,
+ Fts5CResult *pRes
+){
+ int i1 = p1 - pIter->aSeg;
+ int i2 = p2 - pIter->aSeg;
+
+ if( p1->pLeaf || p2->pLeaf ){
+ if( p1->pLeaf==0 ){
+ assert( pRes->iFirst==i2 );
+ }else if( p2->pLeaf==0 ){
+ assert( pRes->iFirst==i1 );
+ }else{
+ int nMin = MIN(p1->term.n, p2->term.n);
+ int res = memcmp(p1->term.p, p2->term.p, nMin);
+ if( res==0 ) res = p1->term.n - p2->term.n;
+
+ if( res==0 ){
+ assert( pRes->bTermEq==1 );
+ assert( p1->iRowid!=p2->iRowid );
+ res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : 1;
+ }else{
+ assert( pRes->bTermEq==0 );
+ }
+
+ if( res<0 ){
+ assert( pRes->iFirst==i1 );
+ }else{
+ assert( pRes->iFirst==i2 );
+ }
+ }
+ }
+}
+
+/*
+** This function is a no-op unless SQLITE_DEBUG is defined when this module
+** is compiled. In that case, this function is essentially an assert()
+** statement used to verify that the contents of the pIter->aFirst[] array
+** are correct.
+*/
+static void fts5AssertMultiIterSetup(Fts5Index *p, Fts5Iter *pIter){
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pFirst = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ int i;
+
+ assert( (pFirst->pLeaf==0)==pIter->base.bEof );
+
+ /* Check that pIter->iSwitchRowid is set correctly. */
+ for(i=0; i<pIter->nSeg; i++){
+ Fts5SegIter *p1 = &pIter->aSeg[i];
+ assert( p1==pFirst
+ || p1->pLeaf==0
+ || fts5BufferCompare(&pFirst->term, &p1->term)
+ || p1->iRowid==pIter->iSwitchRowid
+ || (p1->iRowid<pIter->iSwitchRowid)==pIter->bRev
+ );
+ }
+
+ for(i=0; i<pIter->nSeg; i+=2){
+ Fts5SegIter *p1 = &pIter->aSeg[i];
+ Fts5SegIter *p2 = &pIter->aSeg[i+1];
+ Fts5CResult *pRes = &pIter->aFirst[(pIter->nSeg + i) / 2];
+ fts5AssertComparisonResult(pIter, p1, p2, pRes);
+ }
+
+ for(i=1; i<(pIter->nSeg / 2); i+=2){
+ Fts5SegIter *p1 = &pIter->aSeg[ pIter->aFirst[i*2].iFirst ];
+ Fts5SegIter *p2 = &pIter->aSeg[ pIter->aFirst[i*2+1].iFirst ];
+ Fts5CResult *pRes = &pIter->aFirst[i];
+ fts5AssertComparisonResult(pIter, p1, p2, pRes);
+ }
+ }
+}
+#else
+# define fts5AssertMultiIterSetup(x,y)
+#endif
+
+/*
+** Do the comparison necessary to populate pIter->aFirst[iOut].
+**
+** If the returned value is non-zero, then it is the index of an entry
+** in the pIter->aSeg[] array that is (a) not at EOF, and (b) pointing
+** to a key that is a duplicate of another, higher priority,
+** segment-iterator in the pSeg->aSeg[] array.
+*/
+static int fts5MultiIterDoCompare(Fts5Iter *pIter, int iOut){
+ int i1; /* Index of left-hand Fts5SegIter */
+ int i2; /* Index of right-hand Fts5SegIter */
+ int iRes;
+ Fts5SegIter *p1; /* Left-hand Fts5SegIter */
+ Fts5SegIter *p2; /* Right-hand Fts5SegIter */
+ Fts5CResult *pRes = &pIter->aFirst[iOut];
+
+ assert( iOut<pIter->nSeg && iOut>0 );
+ assert( pIter->bRev==0 || pIter->bRev==1 );
+
+ if( iOut>=(pIter->nSeg/2) ){
+ i1 = (iOut - pIter->nSeg/2) * 2;
+ i2 = i1 + 1;
+ }else{
+ i1 = pIter->aFirst[iOut*2].iFirst;
+ i2 = pIter->aFirst[iOut*2+1].iFirst;
+ }
+ p1 = &pIter->aSeg[i1];
+ p2 = &pIter->aSeg[i2];
+
+ pRes->bTermEq = 0;
+ if( p1->pLeaf==0 ){ /* If p1 is at EOF */
+ iRes = i2;
+ }else if( p2->pLeaf==0 ){ /* If p2 is at EOF */
+ iRes = i1;
+ }else{
+ int res = fts5BufferCompare(&p1->term, &p2->term);
+ if( res==0 ){
+ assert( i2>i1 );
+ assert( i2!=0 );
+ pRes->bTermEq = 1;
+ if( p1->iRowid==p2->iRowid ){
+ p1->bDel = p2->bDel;
+ return i2;
+ }
+ res = ((p1->iRowid > p2->iRowid)==pIter->bRev) ? -1 : +1;
+ }
+ assert( res!=0 );
+ if( res<0 ){
+ iRes = i1;
+ }else{
+ iRes = i2;
+ }
+ }
+
+ pRes->iFirst = (u16)iRes;
+ return 0;
+}
+
+/*
+** Move the seg-iter so that it points to the first rowid on page iLeafPgno.
+** It is an error if leaf iLeafPgno does not exist or contains no rowids.
+*/
+static void fts5SegIterGotoPage(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ int iLeafPgno
+){
+ assert( iLeafPgno>pIter->iLeafPgno );
+
+ if( iLeafPgno>pIter->pSeg->pgnoLast ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ fts5DataRelease(pIter->pNextLeaf);
+ pIter->pNextLeaf = 0;
+ pIter->iLeafPgno = iLeafPgno-1;
+ fts5SegIterNextPage(p, pIter);
+ assert( p->rc!=SQLITE_OK || pIter->iLeafPgno==iLeafPgno );
+
+ if( p->rc==SQLITE_OK ){
+ int iOff;
+ u8 *a = pIter->pLeaf->p;
+ int n = pIter->pLeaf->szLeaf;
+
+ iOff = fts5LeafFirstRowidOff(pIter->pLeaf);
+ if( iOff<4 || iOff>=n ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ iOff += fts5GetVarint(&a[iOff], (u64*)&pIter->iRowid);
+ pIter->iLeafOffset = iOff;
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ }
+ }
+}
+
+/*
+** Advance the iterator passed as the second argument until it is at or
+** past rowid iFrom. Regardless of the value of iFrom, the iterator is
+** always advanced at least once.
+*/
+static void fts5SegIterNextFrom(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegIter *pIter, /* Iterator to advance */
+ i64 iMatch /* Advance iterator at least this far */
+){
+ int bRev = (pIter->flags & FTS5_SEGITER_REVERSE);
+ Fts5DlidxIter *pDlidx = pIter->pDlidx;
+ int iLeafPgno = pIter->iLeafPgno;
+ int bMove = 1;
+
+ assert( pIter->flags & FTS5_SEGITER_ONETERM );
+ assert( pIter->pDlidx );
+ assert( pIter->pLeaf );
+
+ if( bRev==0 ){
+ while( !fts5DlidxIterEof(p, pDlidx) && iMatch>fts5DlidxIterRowid(pDlidx) ){
+ iLeafPgno = fts5DlidxIterPgno(pDlidx);
+ fts5DlidxIterNext(p, pDlidx);
+ }
+ assert_nc( iLeafPgno>=pIter->iLeafPgno || p->rc );
+ if( iLeafPgno>pIter->iLeafPgno ){
+ fts5SegIterGotoPage(p, pIter, iLeafPgno);
+ bMove = 0;
+ }
+ }else{
+ assert( pIter->pNextLeaf==0 );
+ assert( iMatch<pIter->iRowid );
+ while( !fts5DlidxIterEof(p, pDlidx) && iMatch<fts5DlidxIterRowid(pDlidx) ){
+ fts5DlidxIterPrev(p, pDlidx);
+ }
+ iLeafPgno = fts5DlidxIterPgno(pDlidx);
+
+ assert( fts5DlidxIterEof(p, pDlidx) || iLeafPgno<=pIter->iLeafPgno );
+
+ if( iLeafPgno<pIter->iLeafPgno ){
+ pIter->iLeafPgno = iLeafPgno+1;
+ fts5SegIterReverseNewPage(p, pIter);
+ bMove = 0;
+ }
+ }
+
+ do{
+ if( bMove && p->rc==SQLITE_OK ) pIter->xNext(p, pIter, 0);
+ if( pIter->pLeaf==0 ) break;
+ if( bRev==0 && pIter->iRowid>=iMatch ) break;
+ if( bRev!=0 && pIter->iRowid<=iMatch ) break;
+ bMove = 1;
+ }while( p->rc==SQLITE_OK );
+}
+
+
+/*
+** Free the iterator object passed as the second argument.
+*/
+static void fts5MultiIterFree(Fts5Iter *pIter){
+ if( pIter ){
+ int i;
+ for(i=0; i<pIter->nSeg; i++){
+ fts5SegIterClear(&pIter->aSeg[i]);
+ }
+ fts5StructureRelease(pIter->pStruct);
+ fts5BufferFree(&pIter->poslist);
+ sqlite3_free(pIter);
+ }
+}
+
+static void fts5MultiIterAdvanced(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Iter *pIter, /* Iterator to update aFirst[] array for */
+ int iChanged, /* Index of sub-iterator just advanced */
+ int iMinset /* Minimum entry in aFirst[] to set */
+){
+ int i;
+ for(i=(pIter->nSeg+iChanged)/2; i>=iMinset && p->rc==SQLITE_OK; i=i/2){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pIter, i)) ){
+ Fts5SegIter *pSeg = &pIter->aSeg[iEq];
+ assert( p->rc==SQLITE_OK );
+ pSeg->xNext(p, pSeg, 0);
+ i = pIter->nSeg + iEq;
+ }
+ }
+}
+
+/*
+** Sub-iterator iChanged of iterator pIter has just been advanced. It still
+** points to the same term though - just a different rowid. This function
+** attempts to update the contents of the pIter->aFirst[] accordingly.
+** If it does so successfully, 0 is returned. Otherwise 1.
+**
+** If non-zero is returned, the caller should call fts5MultiIterAdvanced()
+** on the iterator instead. That function does the same as this one, except
+** that it deals with more complicated cases as well.
+*/
+static int fts5MultiIterAdvanceRowid(
+ Fts5Iter *pIter, /* Iterator to update aFirst[] array for */
+ int iChanged, /* Index of sub-iterator just advanced */
+ Fts5SegIter **ppFirst
+){
+ Fts5SegIter *pNew = &pIter->aSeg[iChanged];
+
+ if( pNew->iRowid==pIter->iSwitchRowid
+ || (pNew->iRowid<pIter->iSwitchRowid)==pIter->bRev
+ ){
+ int i;
+ Fts5SegIter *pOther = &pIter->aSeg[iChanged ^ 0x0001];
+ pIter->iSwitchRowid = pIter->bRev ? SMALLEST_INT64 : LARGEST_INT64;
+ for(i=(pIter->nSeg+iChanged)/2; 1; i=i/2){
+ Fts5CResult *pRes = &pIter->aFirst[i];
+
+ assert( pNew->pLeaf );
+ assert( pRes->bTermEq==0 || pOther->pLeaf );
+
+ if( pRes->bTermEq ){
+ if( pNew->iRowid==pOther->iRowid ){
+ return 1;
+ }else if( (pOther->iRowid>pNew->iRowid)==pIter->bRev ){
+ pIter->iSwitchRowid = pOther->iRowid;
+ pNew = pOther;
+ }else if( (pOther->iRowid>pIter->iSwitchRowid)==pIter->bRev ){
+ pIter->iSwitchRowid = pOther->iRowid;
+ }
+ }
+ pRes->iFirst = (u16)(pNew - pIter->aSeg);
+ if( i==1 ) break;
+
+ pOther = &pIter->aSeg[ pIter->aFirst[i ^ 0x0001].iFirst ];
+ }
+ }
+
+ *ppFirst = pNew;
+ return 0;
+}
+
+/*
+** Set the pIter->bEof variable based on the state of the sub-iterators.
+*/
+static void fts5MultiIterSetEof(Fts5Iter *pIter){
+ Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ pIter->base.bEof = pSeg->pLeaf==0;
+ pIter->iSwitchRowid = pSeg->iRowid;
+}
+
+/*
+** Move the iterator to the next entry.
+**
+** If an error occurs, an error code is left in Fts5Index.rc. It is not
+** considered an error if the iterator reaches EOF, or if it is already at
+** EOF when this function is called.
+*/
+static void fts5MultiIterNext(
+ Fts5Index *p,
+ Fts5Iter *pIter,
+ int bFrom, /* True if argument iFrom is valid */
+ i64 iFrom /* Advance at least as far as this */
+){
+ int bUseFrom = bFrom;
+ while( p->rc==SQLITE_OK ){
+ int iFirst = pIter->aFirst[1].iFirst;
+ int bNewTerm = 0;
+ Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ assert( p->rc==SQLITE_OK );
+ if( bUseFrom && pSeg->pDlidx ){
+ fts5SegIterNextFrom(p, pSeg, iFrom);
+ }else{
+ pSeg->xNext(p, pSeg, &bNewTerm);
+ }
+
+ if( pSeg->pLeaf==0 || bNewTerm
+ || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg)
+ ){
+ fts5MultiIterAdvanced(p, pIter, iFirst, 1);
+ fts5MultiIterSetEof(pIter);
+ pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
+ if( pSeg->pLeaf==0 ) return;
+ }
+
+ fts5AssertMultiIterSetup(p, pIter);
+ assert( pSeg==&pIter->aSeg[pIter->aFirst[1].iFirst] && pSeg->pLeaf );
+ if( pIter->bSkipEmpty==0 || pSeg->nPos ){
+ pIter->xSetOutputs(pIter, pSeg);
+ return;
+ }
+ bUseFrom = 0;
+ }
+}
+
+static void fts5MultiIterNext2(
+ Fts5Index *p,
+ Fts5Iter *pIter,
+ int *pbNewTerm /* OUT: True if *might* be new term */
+){
+ assert( pIter->bSkipEmpty );
+ if( p->rc==SQLITE_OK ){
+ do {
+ int iFirst = pIter->aFirst[1].iFirst;
+ Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
+ int bNewTerm = 0;
+
+ assert( p->rc==SQLITE_OK );
+ pSeg->xNext(p, pSeg, &bNewTerm);
+ if( pSeg->pLeaf==0 || bNewTerm
+ || fts5MultiIterAdvanceRowid(pIter, iFirst, &pSeg)
+ ){
+ fts5MultiIterAdvanced(p, pIter, iFirst, 1);
+ fts5MultiIterSetEof(pIter);
+ *pbNewTerm = 1;
+ }else{
+ *pbNewTerm = 0;
+ }
+ fts5AssertMultiIterSetup(p, pIter);
+
+ }while( fts5MultiIterIsEmpty(p, pIter) );
+ }
+}
+
+static void fts5IterSetOutputs_Noop(Fts5Iter *pUnused1, Fts5SegIter *pUnused2){
+ UNUSED_PARAM2(pUnused1, pUnused2);
+}
+
+static Fts5Iter *fts5MultiIterAlloc(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ int nSeg
+){
+ Fts5Iter *pNew;
+ int nSlot; /* Power of two >= nSeg */
+
+ for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
+ pNew = fts5IdxMalloc(p,
+ sizeof(Fts5Iter) + /* pNew */
+ sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */
+ sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
+ );
+ if( pNew ){
+ pNew->nSeg = nSlot;
+ pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
+ pNew->pIndex = p;
+ pNew->xSetOutputs = fts5IterSetOutputs_Noop;
+ }
+ return pNew;
+}
+
+static void fts5PoslistCallback(
+ Fts5Index *pUnused,
+ void *pContext,
+ const u8 *pChunk, int nChunk
+){
+ UNUSED_PARAM(pUnused);
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ fts5BufferSafeAppendBlob((Fts5Buffer*)pContext, pChunk, nChunk);
+ }
+}
+
+typedef struct PoslistCallbackCtx PoslistCallbackCtx;
+struct PoslistCallbackCtx {
+ Fts5Buffer *pBuf; /* Append to this buffer */
+ Fts5Colset *pColset; /* Restrict matches to this column */
+ int eState; /* See above */
+};
+
+typedef struct PoslistOffsetsCtx PoslistOffsetsCtx;
+struct PoslistOffsetsCtx {
+ Fts5Buffer *pBuf; /* Append to this buffer */
+ Fts5Colset *pColset; /* Restrict matches to this column */
+ int iRead;
+ int iWrite;
+};
+
+/*
+** TODO: Make this more efficient!
+*/
+static int fts5IndexColsetTest(Fts5Colset *pColset, int iCol){
+ int i;
+ for(i=0; i<pColset->nCol; i++){
+ if( pColset->aiCol[i]==iCol ) return 1;
+ }
+ return 0;
+}
+
+static void fts5PoslistOffsetsCallback(
+ Fts5Index *pUnused,
+ void *pContext,
+ const u8 *pChunk, int nChunk
+){
+ PoslistOffsetsCtx *pCtx = (PoslistOffsetsCtx*)pContext;
+ UNUSED_PARAM(pUnused);
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ int i = 0;
+ while( i<nChunk ){
+ int iVal;
+ i += fts5GetVarint32(&pChunk[i], iVal);
+ iVal += pCtx->iRead - 2;
+ pCtx->iRead = iVal;
+ if( fts5IndexColsetTest(pCtx->pColset, iVal) ){
+ fts5BufferSafeAppendVarint(pCtx->pBuf, iVal + 2 - pCtx->iWrite);
+ pCtx->iWrite = iVal;
+ }
+ }
+ }
+}
+
+static void fts5PoslistFilterCallback(
+ Fts5Index *pUnused,
+ void *pContext,
+ const u8 *pChunk, int nChunk
+){
+ PoslistCallbackCtx *pCtx = (PoslistCallbackCtx*)pContext;
+ UNUSED_PARAM(pUnused);
+ assert_nc( nChunk>=0 );
+ if( nChunk>0 ){
+ /* Search through to find the first varint with value 1. This is the
+ ** start of the next columns hits. */
+ int i = 0;
+ int iStart = 0;
+
+ if( pCtx->eState==2 ){
+ int iCol;
+ fts5FastGetVarint32(pChunk, i, iCol);
+ if( fts5IndexColsetTest(pCtx->pColset, iCol) ){
+ pCtx->eState = 1;
+ fts5BufferSafeAppendVarint(pCtx->pBuf, 1);
+ }else{
+ pCtx->eState = 0;
+ }
+ }
+
+ do {
+ while( i<nChunk && pChunk[i]!=0x01 ){
+ while( pChunk[i] & 0x80 ) i++;
+ i++;
+ }
+ if( pCtx->eState ){
+ fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
+ }
+ if( i<nChunk ){
+ int iCol;
+ iStart = i;
+ i++;
+ if( i>=nChunk ){
+ pCtx->eState = 2;
+ }else{
+ fts5FastGetVarint32(pChunk, i, iCol);
+ pCtx->eState = fts5IndexColsetTest(pCtx->pColset, iCol);
+ if( pCtx->eState ){
+ fts5BufferSafeAppendBlob(pCtx->pBuf, &pChunk[iStart], i-iStart);
+ iStart = i;
+ }
+ }
+ }
+ }while( i<nChunk );
+ }
+}
+
+static void fts5ChunkIterate(
+ Fts5Index *p, /* Index object */
+ Fts5SegIter *pSeg, /* Poslist of this iterator */
+ void *pCtx, /* Context pointer for xChunk callback */
+ void (*xChunk)(Fts5Index*, void*, const u8*, int)
+){
+ int nRem = pSeg->nPos; /* Number of bytes still to come */
+ Fts5Data *pData = 0;
+ u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset);
+ int pgno = pSeg->iLeafPgno;
+ int pgnoSave = 0;
+
+ /* This function does notmwork with detail=none databases. */
+ assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );
+
+ if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
+ pgnoSave = pgno+1;
+ }
+
+ while( 1 ){
+ xChunk(p, pCtx, pChunk, nChunk);
+ nRem -= nChunk;
+ fts5DataRelease(pData);
+ if( nRem<=0 ){
+ break;
+ }else{
+ pgno++;
+ pData = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
+ if( pData==0 ) break;
+ pChunk = &pData->p[4];
+ nChunk = MIN(nRem, pData->szLeaf - 4);
+ if( pgno==pgnoSave ){
+ assert( pSeg->pNextLeaf==0 );
+ pSeg->pNextLeaf = pData;
+ pData = 0;
+ }
+ }
+ }
+}
+
+/*
+** Iterator pIter currently points to a valid entry (not EOF). This
+** function appends the position list data for the current entry to
+** buffer pBuf. It does not make a copy of the position-list size
+** field.
+*/
+static void fts5SegiterPoslist(
+ Fts5Index *p,
+ Fts5SegIter *pSeg,
+ Fts5Colset *pColset,
+ Fts5Buffer *pBuf
+){
+ if( 0==fts5BufferGrow(&p->rc, pBuf, pSeg->nPos) ){
+ if( pColset==0 ){
+ fts5ChunkIterate(p, pSeg, (void*)pBuf, fts5PoslistCallback);
+ }else{
+ if( p->pConfig->eDetail==FTS5_DETAIL_FULL ){
+ PoslistCallbackCtx sCtx;
+ sCtx.pBuf = pBuf;
+ sCtx.pColset = pColset;
+ sCtx.eState = fts5IndexColsetTest(pColset, 0);
+ assert( sCtx.eState==0 || sCtx.eState==1 );
+ fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistFilterCallback);
+ }else{
+ PoslistOffsetsCtx sCtx;
+ memset(&sCtx, 0, sizeof(sCtx));
+ sCtx.pBuf = pBuf;
+ sCtx.pColset = pColset;
+ fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback);
+ }
+ }
+ }
+}
+
+/*
+** IN/OUT parameter (*pa) points to a position list n bytes in size. If
+** the position list contains entries for column iCol, then (*pa) is set
+** to point to the sub-position-list for that column and the number of
+** bytes in it returned. Or, if the argument position list does not
+** contain any entries for column iCol, return 0.
+*/
+static int fts5IndexExtractCol(
+ const u8 **pa, /* IN/OUT: Pointer to poslist */
+ int n, /* IN: Size of poslist in bytes */
+ int iCol /* Column to extract from poslist */
+){
+ int iCurrent = 0; /* Anything before the first 0x01 is col 0 */
+ const u8 *p = *pa;
+ const u8 *pEnd = &p[n]; /* One byte past end of position list */
+
+ while( iCol>iCurrent ){
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint. Note that it is not possible for a negative
+ ** or extremely large varint to occur within an uncorrupted position
+ ** list. So the last byte of each varint may be assumed to have a clear
+ ** 0x80 bit. */
+ while( *p!=0x01 ){
+ while( *p++ & 0x80 );
+ if( p>=pEnd ) return 0;
+ }
+ *pa = p++;
+ iCurrent = *p++;
+ if( iCurrent & 0x80 ){
+ p--;
+ p += fts5GetVarint32(p, iCurrent);
+ }
+ }
+ if( iCol!=iCurrent ) return 0;
+
+ /* Advance pointer p until it points to pEnd or an 0x01 byte that is
+ ** not part of a varint */
+ while( p<pEnd && *p!=0x01 ){
+ while( *p++ & 0x80 );
+ }
+
+ return p - (*pa);
+}
+
+static int fts5IndexExtractColset (
+ Fts5Colset *pColset, /* Colset to filter on */
+ const u8 *pPos, int nPos, /* Position list */
+ Fts5Buffer *pBuf /* Output buffer */
+){
+ int rc = SQLITE_OK;
+ int i;
+
+ fts5BufferZero(pBuf);
+ for(i=0; i<pColset->nCol; i++){
+ const u8 *pSub = pPos;
+ int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]);
+ if( nSub ){
+ fts5BufferAppendBlob(&rc, pBuf, nSub, pSub);
+ }
+ }
+ return rc;
+}
+
+/*
+** xSetOutputs callback used by detail=none tables.
+*/
+static void fts5IterSetOutputs_None(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_NONE );
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.nData = pSeg->nPos;
+}
+
+/*
+** xSetOutputs callback used by detail=full and detail=col tables when no
+** column filters are specified.
+*/
+static void fts5IterSetOutputs_Nocolset(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.nData = pSeg->nPos;
+
+ assert( pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_NONE );
+ assert( pIter->pColset==0 );
+
+ if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
+ /* All data is stored on the current page. Populate the output
+ ** variables to point into the body of the page object. */
+ pIter->base.pData = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ }else{
+ /* The data is distributed over two or more pages. Copy it into the
+ ** Fts5Iter.poslist buffer and then set the output pointer to point
+ ** to this buffer. */
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, 0, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ }
+}
+
+/*
+** xSetOutputs callback used when the Fts5Colset object has nCol==0 (match
+** against no columns at all).
+*/
+static void fts5IterSetOutputs_ZeroColset(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ UNUSED_PARAM(pSeg);
+ pIter->base.nData = 0;
+}
+
+/*
+** xSetOutputs callback used by detail=col when there is a column filter
+** and there are 100 or more columns. Also called as a fallback from
+** fts5IterSetOutputs_Col100 if the column-list spans more than one page.
+*/
+static void fts5IterSetOutputs_Col(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, pIter->pColset, &pIter->poslist);
+ pIter->base.iRowid = pSeg->iRowid;
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+}
+
+/*
+** xSetOutputs callback used when:
+**
+** * detail=col,
+** * there is a column filter, and
+** * the table contains 100 or fewer columns.
+**
+** The last point is to ensure all column numbers are stored as
+** single-byte varints.
+*/
+static void fts5IterSetOutputs_Col100(Fts5Iter *pIter, Fts5SegIter *pSeg){
+
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+ assert( pIter->pColset );
+
+ if( pSeg->iLeafOffset+pSeg->nPos>pSeg->pLeaf->szLeaf ){
+ fts5IterSetOutputs_Col(pIter, pSeg);
+ }else{
+ u8 *a = (u8*)&pSeg->pLeaf->p[pSeg->iLeafOffset];
+ u8 *pEnd = (u8*)&a[pSeg->nPos];
+ int iPrev = 0;
+ int *aiCol = pIter->pColset->aiCol;
+ int *aiColEnd = &aiCol[pIter->pColset->nCol];
+
+ u8 *aOut = pIter->poslist.p;
+ int iPrevOut = 0;
+
+ pIter->base.iRowid = pSeg->iRowid;
+
+ while( a<pEnd ){
+ iPrev += (int)a++[0] - 2;
+ while( *aiCol<iPrev ){
+ aiCol++;
+ if( aiCol==aiColEnd ) goto setoutputs_col_out;
+ }
+ if( *aiCol==iPrev ){
+ *aOut++ = (u8)((iPrev - iPrevOut) + 2);
+ iPrevOut = iPrev;
+ }
+ }
+
+setoutputs_col_out:
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = aOut - pIter->poslist.p;
+ }
+}
+
+/*
+** xSetOutputs callback used by detail=full when there is a column filter.
+*/
+static void fts5IterSetOutputs_Full(Fts5Iter *pIter, Fts5SegIter *pSeg){
+ Fts5Colset *pColset = pIter->pColset;
+ pIter->base.iRowid = pSeg->iRowid;
+
+ assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_FULL );
+ assert( pColset );
+
+ if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
+ /* All data is stored on the current page. Populate the output
+ ** variables to point into the body of the page object. */
+ const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];
+ if( pColset->nCol==1 ){
+ pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]);
+ pIter->base.pData = a;
+ }else{
+ fts5BufferZero(&pIter->poslist);
+ fts5IndexExtractColset(pColset, a, pSeg->nPos, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+ }else{
+ /* The data is distributed over two or more pages. Copy it into the
+ ** Fts5Iter.poslist buffer and then set the output pointer to point
+ ** to this buffer. */
+ fts5BufferZero(&pIter->poslist);
+ fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist);
+ pIter->base.pData = pIter->poslist.p;
+ pIter->base.nData = pIter->poslist.n;
+ }
+}
+
+static void fts5IterSetOutputCb(int *pRc, Fts5Iter *pIter){
+ if( *pRc==SQLITE_OK ){
+ Fts5Config *pConfig = pIter->pIndex->pConfig;
+ if( pConfig->eDetail==FTS5_DETAIL_NONE ){
+ pIter->xSetOutputs = fts5IterSetOutputs_None;
+ }
+
+ else if( pIter->pColset==0 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Nocolset;
+ }
+
+ else if( pIter->pColset->nCol==0 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_ZeroColset;
+ }
+
+ else if( pConfig->eDetail==FTS5_DETAIL_FULL ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Full;
+ }
+
+ else{
+ assert( pConfig->eDetail==FTS5_DETAIL_COLUMNS );
+ if( pConfig->nCol<=100 ){
+ pIter->xSetOutputs = fts5IterSetOutputs_Col100;
+ sqlite3Fts5BufferSize(pRc, &pIter->poslist, pConfig->nCol);
+ }else{
+ pIter->xSetOutputs = fts5IterSetOutputs_Col;
+ }
+ }
+ }
+}
+
+
+/*
+** Allocate a new Fts5Iter object.
+**
+** The new object will be used to iterate through data in structure pStruct.
+** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
+** is zero or greater, data from the first nSegment segments on level iLevel
+** is merged.
+**
+** The iterator initially points to the first term/rowid entry in the
+** iterated data.
+*/
+static void fts5MultiIterNew(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Structure *pStruct, /* Structure of specific index */
+ int flags, /* FTS5INDEX_QUERY_XXX flags */
+ Fts5Colset *pColset, /* Colset to filter on (or NULL) */
+ const u8 *pTerm, int nTerm, /* Term to seek to (or NULL/0) */
+ int iLevel, /* Level to iterate (-1 for all) */
+ int nSegment, /* Number of segments to merge (iLevel>=0) */
+ Fts5Iter **ppOut /* New object */
+){
+ int nSeg = 0; /* Number of segment-iters in use */
+ int iIter = 0; /* */
+ int iSeg; /* Used to iterate through segments */
+ Fts5StructureLevel *pLvl;
+ Fts5Iter *pNew;
+
+ assert( (pTerm==0 && nTerm==0) || iLevel<0 );
+
+ /* Allocate space for the new multi-seg-iterator. */
+ if( p->rc==SQLITE_OK ){
+ if( iLevel<0 ){
+ assert( pStruct->nSegment==fts5StructureCountSegments(pStruct) );
+ nSeg = pStruct->nSegment;
+ nSeg += (p->pHash ? 1 : 0);
+ }else{
+ nSeg = MIN(pStruct->aLevel[iLevel].nSeg, nSegment);
+ }
+ }
+ *ppOut = pNew = fts5MultiIterAlloc(p, nSeg);
+ if( pNew==0 ) return;
+ pNew->bRev = (0!=(flags & FTS5INDEX_QUERY_DESC));
+ pNew->bSkipEmpty = (0!=(flags & FTS5INDEX_QUERY_SKIPEMPTY));
+ pNew->pStruct = pStruct;
+ pNew->pColset = pColset;
+ fts5StructureRef(pStruct);
+ if( (flags & FTS5INDEX_QUERY_NOOUTPUT)==0 ){
+ fts5IterSetOutputCb(&p->rc, pNew);
+ }
+
+ /* Initialize each of the component segment iterators. */
+ if( p->rc==SQLITE_OK ){
+ if( iLevel<0 ){
+ Fts5StructureLevel *pEnd = &pStruct->aLevel[pStruct->nLevel];
+ if( p->pHash ){
+ /* Add a segment iterator for the current contents of the hash table. */
+ Fts5SegIter *pIter = &pNew->aSeg[iIter++];
+ fts5SegIterHashInit(p, pTerm, nTerm, flags, pIter);
+ }
+ for(pLvl=&pStruct->aLevel[0]; pLvl<pEnd; pLvl++){
+ for(iSeg=pLvl->nSeg-1; iSeg>=0; iSeg--){
+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
+ Fts5SegIter *pIter = &pNew->aSeg[iIter++];
+ if( pTerm==0 ){
+ fts5SegIterInit(p, pSeg, pIter);
+ }else{
+ fts5SegIterSeekInit(p, pTerm, nTerm, flags, pSeg, pIter);
+ }
+ }
+ }
+ }else{
+ pLvl = &pStruct->aLevel[iLevel];
+ for(iSeg=nSeg-1; iSeg>=0; iSeg--){
+ fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
+ }
+ }
+ assert( iIter==nSeg );
+ }
+
+ /* If the above was successful, each component iterators now points
+ ** to the first entry in its segment. In this case initialize the
+ ** aFirst[] array. Or, if an error has occurred, free the iterator
+ ** object and set the output variable to NULL. */
+ if( p->rc==SQLITE_OK ){
+ for(iIter=pNew->nSeg-1; iIter>0; iIter--){
+ int iEq;
+ if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
+ Fts5SegIter *pSeg = &pNew->aSeg[iEq];
+ if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
+ fts5MultiIterAdvanced(p, pNew, iEq, iIter);
+ }
+ }
+ fts5MultiIterSetEof(pNew);
+ fts5AssertMultiIterSetup(p, pNew);
+
+ if( pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew) ){
+ fts5MultiIterNext(p, pNew, 0, 0);
+ }else if( pNew->base.bEof==0 ){
+ Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst];
+ pNew->xSetOutputs(pNew, pSeg);
+ }
+
+ }else{
+ fts5MultiIterFree(pNew);
+ *ppOut = 0;
+ }
+}
+
+/*
+** Create an Fts5Iter that iterates through the doclist provided
+** as the second argument.
+*/
+static void fts5MultiIterNew2(
+ Fts5Index *p, /* FTS5 backend to iterate within */
+ Fts5Data *pData, /* Doclist to iterate through */
+ int bDesc, /* True for descending rowid order */
+ Fts5Iter **ppOut /* New object */
+){
+ Fts5Iter *pNew;
+ pNew = fts5MultiIterAlloc(p, 2);
+ if( pNew ){
+ Fts5SegIter *pIter = &pNew->aSeg[1];
+
+ pIter->flags = FTS5_SEGITER_ONETERM;
+ if( pData->szLeaf>0 ){
+ pIter->pLeaf = pData;
+ pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
+ pIter->iEndofDoclist = pData->nn;
+ pNew->aFirst[1].iFirst = 1;
+ if( bDesc ){
+ pNew->bRev = 1;
+ pIter->flags |= FTS5_SEGITER_REVERSE;
+ fts5SegIterReverseInitPage(p, pIter);
+ }else{
+ fts5SegIterLoadNPos(p, pIter);
+ }
+ pData = 0;
+ }else{
+ pNew->base.bEof = 1;
+ }
+ fts5SegIterSetNext(p, pIter);
+
+ *ppOut = pNew;
+ }
+
+ fts5DataRelease(pData);
+}
+
+/*
+** Return true if the iterator is at EOF or if an error has occurred.
+** False otherwise.
+*/
+static int fts5MultiIterEof(Fts5Index *p, Fts5Iter *pIter){
+ assert( p->rc
+ || (pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf==0)==pIter->base.bEof
+ );
+ return (p->rc || pIter->base.bEof);
+}
+
+/*
+** Return the rowid of the entry that the iterator currently points
+** to. If the iterator points to EOF when this function is called the
+** results are undefined.
+*/
+static i64 fts5MultiIterRowid(Fts5Iter *pIter){
+ assert( pIter->aSeg[ pIter->aFirst[1].iFirst ].pLeaf );
+ return pIter->aSeg[ pIter->aFirst[1].iFirst ].iRowid;
+}
+
+/*
+** Move the iterator to the next entry at or following iMatch.
+*/
+static void fts5MultiIterNextFrom(
+ Fts5Index *p,
+ Fts5Iter *pIter,
+ i64 iMatch
+){
+ while( 1 ){
+ i64 iRowid;
+ fts5MultiIterNext(p, pIter, 1, iMatch);
+ if( fts5MultiIterEof(p, pIter) ) break;
+ iRowid = fts5MultiIterRowid(pIter);
+ if( pIter->bRev==0 && iRowid>=iMatch ) break;
+ if( pIter->bRev!=0 && iRowid<=iMatch ) break;
+ }
+}
+
+/*
+** Return a pointer to a buffer containing the term associated with the
+** entry that the iterator currently points to.
+*/
+static const u8 *fts5MultiIterTerm(Fts5Iter *pIter, int *pn){
+ Fts5SegIter *p = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ *pn = p->term.n;
+ return p->term.p;
+}
+
+/*
+** Allocate a new segment-id for the structure pStruct. The new segment
+** id must be between 1 and 65335 inclusive, and must not be used by
+** any currently existing segment. If a free segment id cannot be found,
+** SQLITE_FULL is returned.
+**
+** If an error has already occurred, this function is a no-op. 0 is
+** returned in this case.
+*/
+static int fts5AllocateSegid(Fts5Index *p, Fts5Structure *pStruct){
+ int iSegid = 0;
+
+ if( p->rc==SQLITE_OK ){
+ if( pStruct->nSegment>=FTS5_MAX_SEGMENT ){
+ p->rc = SQLITE_FULL;
+ }else{
+ /* FTS5_MAX_SEGMENT is currently defined as 2000. So the following
+ ** array is 63 elements, or 252 bytes, in size. */
+ u32 aUsed[(FTS5_MAX_SEGMENT+31) / 32];
+ int iLvl, iSeg;
+ int i;
+ u32 mask;
+ memset(aUsed, 0, sizeof(aUsed));
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ int iId = pStruct->aLevel[iLvl].aSeg[iSeg].iSegid;
+ if( iId<=FTS5_MAX_SEGMENT ){
+ aUsed[(iId-1) / 32] |= 1 << ((iId-1) % 32);
+ }
+ }
+ }
+
+ for(i=0; aUsed[i]==0xFFFFFFFF; i++);
+ mask = aUsed[i];
+ for(iSegid=0; mask & (1 << iSegid); iSegid++);
+ iSegid += 1 + i*32;
+
+#ifdef SQLITE_DEBUG
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ assert( iSegid!=pStruct->aLevel[iLvl].aSeg[iSeg].iSegid );
+ }
+ }
+ assert( iSegid>0 && iSegid<=FTS5_MAX_SEGMENT );
+
+ {
+ sqlite3_stmt *pIdxSelect = fts5IdxSelectStmt(p);
+ if( p->rc==SQLITE_OK ){
+ u8 aBlob[2] = {0xff, 0xff};
+ sqlite3_bind_int(pIdxSelect, 1, iSegid);
+ sqlite3_bind_blob(pIdxSelect, 2, aBlob, 2, SQLITE_STATIC);
+ assert( sqlite3_step(pIdxSelect)!=SQLITE_ROW );
+ p->rc = sqlite3_reset(pIdxSelect);
+ }
+ }
+#endif
+ }
+ }
+
+ return iSegid;
+}
+
+/*
+** Discard all data currently cached in the hash-tables.
+*/
+static void fts5IndexDiscardData(Fts5Index *p){
+ assert( p->pHash || p->nPendingData==0 );
+ if( p->pHash ){
+ sqlite3Fts5HashClear(p->pHash);
+ p->nPendingData = 0;
+ }
+}
+
+/*
+** Return the size of the prefix, in bytes, that buffer
+** (pNew/<length-unknown>) shares with buffer (pOld/nOld).
+**
+** Buffer (pNew/<length-unknown>) is guaranteed to be greater
+** than buffer (pOld/nOld).
+*/
+static int fts5PrefixCompress(int nOld, const u8 *pOld, const u8 *pNew){
+ int i;
+ for(i=0; i<nOld; i++){
+ if( pOld[i]!=pNew[i] ) break;
+ }
+ return i;
+}
+
+static void fts5WriteDlidxClear(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int bFlush /* If true, write dlidx to disk */
+){
+ int i;
+ assert( bFlush==0 || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n>0) );
+ for(i=0; i<pWriter->nDlidx; i++){
+ Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[i];
+ if( pDlidx->buf.n==0 ) break;
+ if( bFlush ){
+ assert( pDlidx->pgno!=0 );
+ fts5DataWrite(p,
+ FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno),
+ pDlidx->buf.p, pDlidx->buf.n
+ );
+ }
+ sqlite3Fts5BufferZero(&pDlidx->buf);
+ pDlidx->bPrevValid = 0;
+ }
+}
+
+/*
+** Grow the pWriter->aDlidx[] array to at least nLvl elements in size.
+** Any new array elements are zeroed before returning.
+*/
+static int fts5WriteDlidxGrow(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int nLvl
+){
+ if( p->rc==SQLITE_OK && nLvl>=pWriter->nDlidx ){
+ Fts5DlidxWriter *aDlidx = (Fts5DlidxWriter*)sqlite3_realloc(
+ pWriter->aDlidx, sizeof(Fts5DlidxWriter) * nLvl
+ );
+ if( aDlidx==0 ){
+ p->rc = SQLITE_NOMEM;
+ }else{
+ int nByte = sizeof(Fts5DlidxWriter) * (nLvl - pWriter->nDlidx);
+ memset(&aDlidx[pWriter->nDlidx], 0, nByte);
+ pWriter->aDlidx = aDlidx;
+ pWriter->nDlidx = nLvl;
+ }
+ }
+ return p->rc;
+}
+
+/*
+** If the current doclist-index accumulating in pWriter->aDlidx[] is large
+** enough, flush it to disk and return 1. Otherwise discard it and return
+** zero.
+*/
+static int fts5WriteFlushDlidx(Fts5Index *p, Fts5SegWriter *pWriter){
+ int bFlag = 0;
+
+ /* If there were FTS5_MIN_DLIDX_SIZE or more empty leaf pages written
+ ** to the database, also write the doclist-index to disk. */
+ if( pWriter->aDlidx[0].buf.n>0 && pWriter->nEmpty>=FTS5_MIN_DLIDX_SIZE ){
+ bFlag = 1;
+ }
+ fts5WriteDlidxClear(p, pWriter, bFlag);
+ pWriter->nEmpty = 0;
+ return bFlag;
+}
+
+/*
+** This function is called whenever processing of the doclist for the
+** last term on leaf page (pWriter->iBtPage) is completed.
+**
+** The doclist-index for that term is currently stored in-memory within the
+** Fts5SegWriter.aDlidx[] array. If it is large enough, this function
+** writes it out to disk. Or, if it is too small to bother with, discards
+** it.
+**
+** Fts5SegWriter.btterm currently contains the first term on page iBtPage.
+*/
+static void fts5WriteFlushBtree(Fts5Index *p, Fts5SegWriter *pWriter){
+ int bFlag;
+
+ assert( pWriter->iBtPage || pWriter->nEmpty==0 );
+ if( pWriter->iBtPage==0 ) return;
+ bFlag = fts5WriteFlushDlidx(p, pWriter);
+
+ if( p->rc==SQLITE_OK ){
+ const char *z = (pWriter->btterm.n>0?(const char*)pWriter->btterm.p:"");
+ /* The following was already done in fts5WriteInit(): */
+ /* sqlite3_bind_int(p->pIdxWriter, 1, pWriter->iSegid); */
+ sqlite3_bind_blob(p->pIdxWriter, 2, z, pWriter->btterm.n, SQLITE_STATIC);
+ sqlite3_bind_int64(p->pIdxWriter, 3, bFlag + ((i64)pWriter->iBtPage<<1));
+ sqlite3_step(p->pIdxWriter);
+ p->rc = sqlite3_reset(p->pIdxWriter);
+ }
+ pWriter->iBtPage = 0;
+}
+
+/*
+** This is called once for each leaf page except the first that contains
+** at least one term. Argument (nTerm/pTerm) is the split-key - a term that
+** is larger than all terms written to earlier leaves, and equal to or
+** smaller than the first term on the new leaf.
+**
+** If an error occurs, an error code is left in Fts5Index.rc. If an error
+** has already occurred when this function is called, it is a no-op.
+*/
+static void fts5WriteBtreeTerm(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegWriter *pWriter, /* Writer object */
+ int nTerm, const u8 *pTerm /* First term on new page */
+){
+ fts5WriteFlushBtree(p, pWriter);
+ fts5BufferSet(&p->rc, &pWriter->btterm, nTerm, pTerm);
+ pWriter->iBtPage = pWriter->writer.pgno;
+}
+
+/*
+** This function is called when flushing a leaf page that contains no
+** terms at all to disk.
+*/
+static void fts5WriteBtreeNoTerm(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5SegWriter *pWriter /* Writer object */
+){
+ /* If there were no rowids on the leaf page either and the doclist-index
+ ** has already been started, append an 0x00 byte to it. */
+ if( pWriter->bFirstRowidInPage && pWriter->aDlidx[0].buf.n>0 ){
+ Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[0];
+ assert( pDlidx->bPrevValid );
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, 0);
+ }
+
+ /* Increment the "number of sequential leaves without a term" counter. */
+ pWriter->nEmpty++;
+}
+
+static i64 fts5DlidxExtractFirstRowid(Fts5Buffer *pBuf){
+ i64 iRowid;
+ int iOff;
+
+ iOff = 1 + fts5GetVarint(&pBuf->p[1], (u64*)&iRowid);
+ fts5GetVarint(&pBuf->p[iOff], (u64*)&iRowid);
+ return iRowid;
+}
+
+/*
+** Rowid iRowid has just been appended to the current leaf page. It is the
+** first on the page. This function appends an appropriate entry to the current
+** doclist-index.
+*/
+static void fts5WriteDlidxAppend(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ i64 iRowid
+){
+ int i;
+ int bDone = 0;
+
+ for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
+ i64 iVal;
+ Fts5DlidxWriter *pDlidx = &pWriter->aDlidx[i];
+
+ if( pDlidx->buf.n>=p->pConfig->pgsz ){
+ /* The current doclist-index page is full. Write it to disk and push
+ ** a copy of iRowid (which will become the first rowid on the next
+ ** doclist-index leaf page) up into the next level of the b-tree
+ ** hierarchy. If the node being flushed is currently the root node,
+ ** also push its first rowid upwards. */
+ pDlidx->buf.p[0] = 0x01; /* Not the root node */
+ fts5DataWrite(p,
+ FTS5_DLIDX_ROWID(pWriter->iSegid, i, pDlidx->pgno),
+ pDlidx->buf.p, pDlidx->buf.n
+ );
+ fts5WriteDlidxGrow(p, pWriter, i+2);
+ pDlidx = &pWriter->aDlidx[i];
+ if( p->rc==SQLITE_OK && pDlidx[1].buf.n==0 ){
+ i64 iFirst = fts5DlidxExtractFirstRowid(&pDlidx->buf);
+
+ /* This was the root node. Push its first rowid up to the new root. */
+ pDlidx[1].pgno = pDlidx->pgno;
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, 0);
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, pDlidx->pgno);
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx[1].buf, iFirst);
+ pDlidx[1].bPrevValid = 1;
+ pDlidx[1].iPrev = iFirst;
+ }
+
+ sqlite3Fts5BufferZero(&pDlidx->buf);
+ pDlidx->bPrevValid = 0;
+ pDlidx->pgno++;
+ }else{
+ bDone = 1;
+ }
+
+ if( pDlidx->bPrevValid ){
+ iVal = iRowid - pDlidx->iPrev;
+ }else{
+ i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
+ assert( pDlidx->buf.n==0 );
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone);
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno);
+ iVal = iRowid;
+ }
+
+ sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iVal);
+ pDlidx->bPrevValid = 1;
+ pDlidx->iPrev = iRowid;
+ }
+}
+
+static void fts5WriteFlushLeaf(Fts5Index *p, Fts5SegWriter *pWriter){
+ static const u8 zero[] = { 0x00, 0x00, 0x00, 0x00 };
+ Fts5PageWriter *pPage = &pWriter->writer;
+ i64 iRowid;
+
+static int nCall = 0;
+nCall++;
+
+ assert( (pPage->pgidx.n==0)==(pWriter->bFirstTermInPage) );
+
+ /* Set the szLeaf header field. */
+ assert( 0==fts5GetU16(&pPage->buf.p[2]) );
+ fts5PutU16(&pPage->buf.p[2], (u16)pPage->buf.n);
+
+ if( pWriter->bFirstTermInPage ){
+ /* No term was written to this page. */
+ assert( pPage->pgidx.n==0 );
+ fts5WriteBtreeNoTerm(p, pWriter);
+ }else{
+ /* Append the pgidx to the page buffer. Set the szLeaf header field. */
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, pPage->pgidx.n, pPage->pgidx.p);
+ }
+
+ /* Write the page out to disk */
+ iRowid = FTS5_SEGMENT_ROWID(pWriter->iSegid, pPage->pgno);
+ fts5DataWrite(p, iRowid, pPage->buf.p, pPage->buf.n);
+
+ /* Initialize the next page. */
+ fts5BufferZero(&pPage->buf);
+ fts5BufferZero(&pPage->pgidx);
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, 4, zero);
+ pPage->iPrevPgidx = 0;
+ pPage->pgno++;
+
+ /* Increase the leaves written counter */
+ pWriter->nLeafWritten++;
+
+ /* The new leaf holds no terms or rowids */
+ pWriter->bFirstTermInPage = 1;
+ pWriter->bFirstRowidInPage = 1;
+}
+
+/*
+** Append term pTerm/nTerm to the segment being written by the writer passed
+** as the second argument.
+**
+** If an error occurs, set the Fts5Index.rc error code. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5WriteAppendTerm(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int nTerm, const u8 *pTerm
+){
+ int nPrefix; /* Bytes of prefix compression for term */
+ Fts5PageWriter *pPage = &pWriter->writer;
+ Fts5Buffer *pPgidx = &pWriter->writer.pgidx;
+
+ assert( p->rc==SQLITE_OK );
+ assert( pPage->buf.n>=4 );
+ assert( pPage->buf.n>4 || pWriter->bFirstTermInPage );
+
+ /* If the current leaf page is full, flush it to disk. */
+ if( (pPage->buf.n + pPgidx->n + nTerm + 2)>=p->pConfig->pgsz ){
+ if( pPage->buf.n>4 ){
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+ fts5BufferGrow(&p->rc, &pPage->buf, nTerm+FTS5_DATA_PADDING);
+ }
+
+ /* TODO1: Updating pgidx here. */
+ pPgidx->n += sqlite3Fts5PutVarint(
+ &pPgidx->p[pPgidx->n], pPage->buf.n - pPage->iPrevPgidx
+ );
+ pPage->iPrevPgidx = pPage->buf.n;
+#if 0
+ fts5PutU16(&pPgidx->p[pPgidx->n], pPage->buf.n);
+ pPgidx->n += 2;
+#endif
+
+ if( pWriter->bFirstTermInPage ){
+ nPrefix = 0;
+ if( pPage->pgno!=1 ){
+ /* This is the first term on a leaf that is not the leftmost leaf in
+ ** the segment b-tree. In this case it is necessary to add a term to
+ ** the b-tree hierarchy that is (a) larger than the largest term
+ ** already written to the segment and (b) smaller than or equal to
+ ** this term. In other words, a prefix of (pTerm/nTerm) that is one
+ ** byte longer than the longest prefix (pTerm/nTerm) shares with the
+ ** previous term.
+ **
+ ** Usually, the previous term is available in pPage->term. The exception
+ ** is if this is the first term written in an incremental-merge step.
+ ** In this case the previous term is not available, so just write a
+ ** copy of (pTerm/nTerm) into the parent node. This is slightly
+ ** inefficient, but still correct. */
+ int n = nTerm;
+ if( pPage->term.n ){
+ n = 1 + fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
+ }
+ fts5WriteBtreeTerm(p, pWriter, n, pTerm);
+ pPage = &pWriter->writer;
+ }
+ }else{
+ nPrefix = fts5PrefixCompress(pPage->term.n, pPage->term.p, pTerm);
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, nPrefix);
+ }
+
+ /* Append the number of bytes of new data, then the term data itself
+ ** to the page. */
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, nTerm - nPrefix);
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, nTerm - nPrefix, &pTerm[nPrefix]);
+
+ /* Update the Fts5PageWriter.term field. */
+ fts5BufferSet(&p->rc, &pPage->term, nTerm, pTerm);
+ pWriter->bFirstTermInPage = 0;
+
+ pWriter->bFirstRowidInPage = 0;
+ pWriter->bFirstRowidInDoclist = 1;
+
+ assert( p->rc || (pWriter->nDlidx>0 && pWriter->aDlidx[0].buf.n==0) );
+ pWriter->aDlidx[0].pgno = pPage->pgno;
+}
+
+/*
+** Append a rowid and position-list size field to the writers output.
+*/
+static void fts5WriteAppendRowid(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ i64 iRowid
+){
+ if( p->rc==SQLITE_OK ){
+ Fts5PageWriter *pPage = &pWriter->writer;
+
+ if( (pPage->buf.n + pPage->pgidx.n)>=p->pConfig->pgsz ){
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+
+ /* If this is to be the first rowid written to the page, set the
+ ** rowid-pointer in the page-header. Also append a value to the dlidx
+ ** buffer, in case a doclist-index is required. */
+ if( pWriter->bFirstRowidInPage ){
+ fts5PutU16(pPage->buf.p, (u16)pPage->buf.n);
+ fts5WriteDlidxAppend(p, pWriter, iRowid);
+ }
+
+ /* Write the rowid. */
+ if( pWriter->bFirstRowidInDoclist || pWriter->bFirstRowidInPage ){
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid);
+ }else{
+ assert( p->rc || iRowid>pWriter->iPrevRowid );
+ fts5BufferAppendVarint(&p->rc, &pPage->buf, iRowid - pWriter->iPrevRowid);
+ }
+ pWriter->iPrevRowid = iRowid;
+ pWriter->bFirstRowidInDoclist = 0;
+ pWriter->bFirstRowidInPage = 0;
+ }
+}
+
+static void fts5WriteAppendPoslistData(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ const u8 *aData,
+ int nData
+){
+ Fts5PageWriter *pPage = &pWriter->writer;
+ const u8 *a = aData;
+ int n = nData;
+
+ assert( p->pConfig->pgsz>0 );
+ while( p->rc==SQLITE_OK
+ && (pPage->buf.n + pPage->pgidx.n + n)>=p->pConfig->pgsz
+ ){
+ int nReq = p->pConfig->pgsz - pPage->buf.n - pPage->pgidx.n;
+ int nCopy = 0;
+ while( nCopy<nReq ){
+ i64 dummy;
+ nCopy += fts5GetVarint(&a[nCopy], (u64*)&dummy);
+ }
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, nCopy, a);
+ a += nCopy;
+ n -= nCopy;
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+ if( n>0 ){
+ fts5BufferAppendBlob(&p->rc, &pPage->buf, n, a);
+ }
+}
+
+/*
+** Flush any data cached by the writer object to the database. Free any
+** allocations associated with the writer.
+*/
+static void fts5WriteFinish(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter, /* Writer object */
+ int *pnLeaf /* OUT: Number of leaf pages in b-tree */
+){
+ int i;
+ Fts5PageWriter *pLeaf = &pWriter->writer;
+ if( p->rc==SQLITE_OK ){
+ assert( pLeaf->pgno>=1 );
+ if( pLeaf->buf.n>4 ){
+ fts5WriteFlushLeaf(p, pWriter);
+ }
+ *pnLeaf = pLeaf->pgno-1;
+ if( pLeaf->pgno>1 ){
+ fts5WriteFlushBtree(p, pWriter);
+ }
+ }
+ fts5BufferFree(&pLeaf->term);
+ fts5BufferFree(&pLeaf->buf);
+ fts5BufferFree(&pLeaf->pgidx);
+ fts5BufferFree(&pWriter->btterm);
+
+ for(i=0; i<pWriter->nDlidx; i++){
+ sqlite3Fts5BufferFree(&pWriter->aDlidx[i].buf);
+ }
+ sqlite3_free(pWriter->aDlidx);
+}
+
+static void fts5WriteInit(
+ Fts5Index *p,
+ Fts5SegWriter *pWriter,
+ int iSegid
+){
+ const int nBuffer = p->pConfig->pgsz + FTS5_DATA_PADDING;
+
+ memset(pWriter, 0, sizeof(Fts5SegWriter));
+ pWriter->iSegid = iSegid;
+
+ fts5WriteDlidxGrow(p, pWriter, 1);
+ pWriter->writer.pgno = 1;
+ pWriter->bFirstTermInPage = 1;
+ pWriter->iBtPage = 1;
+
+ assert( pWriter->writer.buf.n==0 );
+ assert( pWriter->writer.pgidx.n==0 );
+
+ /* Grow the two buffers to pgsz + padding bytes in size. */
+ sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.pgidx, nBuffer);
+ sqlite3Fts5BufferSize(&p->rc, &pWriter->writer.buf, nBuffer);
+
+ if( p->pIdxWriter==0 ){
+ Fts5Config *pConfig = p->pConfig;
+ fts5IndexPrepareStmt(p, &p->pIdxWriter, sqlite3_mprintf(
+ "INSERT INTO '%q'.'%q_idx'(segid,term,pgno) VALUES(?,?,?)",
+ pConfig->zDb, pConfig->zName
+ ));
+ }
+
+ if( p->rc==SQLITE_OK ){
+ /* Initialize the 4-byte leaf-page header to 0x00. */
+ memset(pWriter->writer.buf.p, 0, 4);
+ pWriter->writer.buf.n = 4;
+
+ /* Bind the current output segment id to the index-writer. This is an
+ ** optimization over binding the same value over and over as rows are
+ ** inserted into %_idx by the current writer. */
+ sqlite3_bind_int(p->pIdxWriter, 1, pWriter->iSegid);
+ }
+}
+
+/*
+** Iterator pIter was used to iterate through the input segments of on an
+** incremental merge operation. This function is called if the incremental
+** merge step has finished but the input has not been completely exhausted.
+*/
+static void fts5TrimSegments(Fts5Index *p, Fts5Iter *pIter){
+ int i;
+ Fts5Buffer buf;
+ memset(&buf, 0, sizeof(Fts5Buffer));
+ for(i=0; i<pIter->nSeg; i++){
+ Fts5SegIter *pSeg = &pIter->aSeg[i];
+ if( pSeg->pSeg==0 ){
+ /* no-op */
+ }else if( pSeg->pLeaf==0 ){
+ /* All keys from this input segment have been transfered to the output.
+ ** Set both the first and last page-numbers to 0 to indicate that the
+ ** segment is now empty. */
+ pSeg->pSeg->pgnoLast = 0;
+ pSeg->pSeg->pgnoFirst = 0;
+ }else{
+ int iOff = pSeg->iTermLeafOffset; /* Offset on new first leaf page */
+ i64 iLeafRowid;
+ Fts5Data *pData;
+ int iId = pSeg->pSeg->iSegid;
+ u8 aHdr[4] = {0x00, 0x00, 0x00, 0x00};
+
+ iLeafRowid = FTS5_SEGMENT_ROWID(iId, pSeg->iTermLeafPgno);
+ pData = fts5DataRead(p, iLeafRowid);
+ if( pData ){
+ fts5BufferZero(&buf);
+ fts5BufferGrow(&p->rc, &buf, pData->nn);
+ fts5BufferAppendBlob(&p->rc, &buf, sizeof(aHdr), aHdr);
+ fts5BufferAppendVarint(&p->rc, &buf, pSeg->term.n);
+ fts5BufferAppendBlob(&p->rc, &buf, pSeg->term.n, pSeg->term.p);
+ fts5BufferAppendBlob(&p->rc, &buf, pData->szLeaf-iOff, &pData->p[iOff]);
+ if( p->rc==SQLITE_OK ){
+ /* Set the szLeaf field */
+ fts5PutU16(&buf.p[2], (u16)buf.n);
+ }
+
+ /* Set up the new page-index array */
+ fts5BufferAppendVarint(&p->rc, &buf, 4);
+ if( pSeg->iLeafPgno==pSeg->iTermLeafPgno
+ && pSeg->iEndofDoclist<pData->szLeaf
+ ){
+ int nDiff = pData->szLeaf - pSeg->iEndofDoclist;
+ fts5BufferAppendVarint(&p->rc, &buf, buf.n - 1 - nDiff - 4);
+ fts5BufferAppendBlob(&p->rc, &buf,
+ pData->nn - pSeg->iPgidxOff, &pData->p[pSeg->iPgidxOff]
+ );
+ }
+
+ fts5DataRelease(pData);
+ pSeg->pSeg->pgnoFirst = pSeg->iTermLeafPgno;
+ fts5DataDelete(p, FTS5_SEGMENT_ROWID(iId, 1), iLeafRowid);
+ fts5DataWrite(p, iLeafRowid, buf.p, buf.n);
+ }
+ }
+ }
+ fts5BufferFree(&buf);
+}
+
+static void fts5MergeChunkCallback(
+ Fts5Index *p,
+ void *pCtx,
+ const u8 *pChunk, int nChunk
+){
+ Fts5SegWriter *pWriter = (Fts5SegWriter*)pCtx;
+ fts5WriteAppendPoslistData(p, pWriter, pChunk, nChunk);
+}
+
+/*
+**
+*/
+static void fts5IndexMergeLevel(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct, /* IN/OUT: Stucture of index */
+ int iLvl, /* Level to read input from */
+ int *pnRem /* Write up to this many output leaves */
+){
+ Fts5Structure *pStruct = *ppStruct;
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ Fts5StructureLevel *pLvlOut;
+ Fts5Iter *pIter = 0; /* Iterator to read input data */
+ int nRem = pnRem ? *pnRem : 0; /* Output leaf pages left to write */
+ int nInput; /* Number of input segments */
+ Fts5SegWriter writer; /* Writer object */
+ Fts5StructureSegment *pSeg; /* Output segment */
+ Fts5Buffer term;
+ int bOldest; /* True if the output segment is the oldest */
+ int eDetail = p->pConfig->eDetail;
+ const int flags = FTS5INDEX_QUERY_NOOUTPUT;
+
+ assert( iLvl<pStruct->nLevel );
+ assert( pLvl->nMerge<=pLvl->nSeg );
+
+ memset(&writer, 0, sizeof(Fts5SegWriter));
+ memset(&term, 0, sizeof(Fts5Buffer));
+ if( pLvl->nMerge ){
+ pLvlOut = &pStruct->aLevel[iLvl+1];
+ assert( pLvlOut->nSeg>0 );
+ nInput = pLvl->nMerge;
+ pSeg = &pLvlOut->aSeg[pLvlOut->nSeg-1];
+
+ fts5WriteInit(p, &writer, pSeg->iSegid);
+ writer.writer.pgno = pSeg->pgnoLast+1;
+ writer.iBtPage = 0;
+ }else{
+ int iSegid = fts5AllocateSegid(p, pStruct);
+
+ /* Extend the Fts5Structure object as required to ensure the output
+ ** segment exists. */
+ if( iLvl==pStruct->nLevel-1 ){
+ fts5StructureAddLevel(&p->rc, ppStruct);
+ pStruct = *ppStruct;
+ }
+ fts5StructureExtendLevel(&p->rc, pStruct, iLvl+1, 1, 0);
+ if( p->rc ) return;
+ pLvl = &pStruct->aLevel[iLvl];
+ pLvlOut = &pStruct->aLevel[iLvl+1];
+
+ fts5WriteInit(p, &writer, iSegid);
+
+ /* Add the new segment to the output level */
+ pSeg = &pLvlOut->aSeg[pLvlOut->nSeg];
+ pLvlOut->nSeg++;
+ pSeg->pgnoFirst = 1;
+ pSeg->iSegid = iSegid;
+ pStruct->nSegment++;
+
+ /* Read input from all segments in the input level */
+ nInput = pLvl->nSeg;
+ }
+ bOldest = (pLvlOut->nSeg==1 && pStruct->nLevel==iLvl+2);
+
+ assert( iLvl>=0 );
+ for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, iLvl, nInput, &pIter);
+ fts5MultiIterEof(p, pIter)==0;
+ fts5MultiIterNext(p, pIter, 0, 0)
+ ){
+ Fts5SegIter *pSegIter = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ int nPos; /* position-list size field value */
+ int nTerm;
+ const u8 *pTerm;
+
+ /* Check for key annihilation. */
+ if( pSegIter->nPos==0 && (bOldest || pSegIter->bDel==0) ) continue;
+
+ pTerm = fts5MultiIterTerm(pIter, &nTerm);
+ if( nTerm!=term.n || memcmp(pTerm, term.p, nTerm) ){
+ if( pnRem && writer.nLeafWritten>nRem ){
+ break;
+ }
+
+ /* This is a new term. Append a term to the output segment. */
+ fts5WriteAppendTerm(p, &writer, nTerm, pTerm);
+ fts5BufferSet(&p->rc, &term, nTerm, pTerm);
+ }
+
+ /* Append the rowid to the output */
+ /* WRITEPOSLISTSIZE */
+ fts5WriteAppendRowid(p, &writer, fts5MultiIterRowid(pIter));
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ if( pSegIter->bDel ){
+ fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0);
+ if( pSegIter->nPos>0 ){
+ fts5BufferAppendVarint(&p->rc, &writer.writer.buf, 0);
+ }
+ }
+ }else{
+ /* Append the position-list data to the output */
+ nPos = pSegIter->nPos*2 + pSegIter->bDel;
+ fts5BufferAppendVarint(&p->rc, &writer.writer.buf, nPos);
+ fts5ChunkIterate(p, pSegIter, (void*)&writer, fts5MergeChunkCallback);
+ }
+ }
+
+ /* Flush the last leaf page to disk. Set the output segment b-tree height
+ ** and last leaf page number at the same time. */
+ fts5WriteFinish(p, &writer, &pSeg->pgnoLast);
+
+ if( fts5MultiIterEof(p, pIter) ){
+ int i;
+
+ /* Remove the redundant segments from the %_data table */
+ for(i=0; i<nInput; i++){
+ fts5DataRemoveSegment(p, pLvl->aSeg[i].iSegid);
+ }
+
+ /* Remove the redundant segments from the input level */
+ if( pLvl->nSeg!=nInput ){
+ int nMove = (pLvl->nSeg - nInput) * sizeof(Fts5StructureSegment);
+ memmove(pLvl->aSeg, &pLvl->aSeg[nInput], nMove);
+ }
+ pStruct->nSegment -= nInput;
+ pLvl->nSeg -= nInput;
+ pLvl->nMerge = 0;
+ if( pSeg->pgnoLast==0 ){
+ pLvlOut->nSeg--;
+ pStruct->nSegment--;
+ }
+ }else{
+ assert( pSeg->pgnoLast>0 );
+ fts5TrimSegments(p, pIter);
+ pLvl->nMerge = nInput;
+ }
+
+ fts5MultiIterFree(pIter);
+ fts5BufferFree(&term);
+ if( pnRem ) *pnRem -= writer.nLeafWritten;
+}
+
+/*
+** Do up to nPg pages of automerge work on the index.
+**
+** Return true if any changes were actually made, or false otherwise.
+*/
+static int fts5IndexMerge(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */
+ int nPg, /* Pages of work to do */
+ int nMin /* Minimum number of segments to merge */
+){
+ int nRem = nPg;
+ int bRet = 0;
+ Fts5Structure *pStruct = *ppStruct;
+ while( nRem>0 && p->rc==SQLITE_OK ){
+ int iLvl; /* To iterate through levels */
+ int iBestLvl = 0; /* Level offering the most input segments */
+ int nBest = 0; /* Number of input segments on best level */
+
+ /* Set iBestLvl to the level to read input segments from. */
+ assert( pStruct->nLevel>0 );
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ Fts5StructureLevel *pLvl = &pStruct->aLevel[iLvl];
+ if( pLvl->nMerge ){
+ if( pLvl->nMerge>nBest ){
+ iBestLvl = iLvl;
+ nBest = pLvl->nMerge;
+ }
+ break;
+ }
+ if( pLvl->nSeg>nBest ){
+ nBest = pLvl->nSeg;
+ iBestLvl = iLvl;
+ }
+ }
+
+ /* If nBest is still 0, then the index must be empty. */
+#ifdef SQLITE_DEBUG
+ for(iLvl=0; nBest==0 && iLvl<pStruct->nLevel; iLvl++){
+ assert( pStruct->aLevel[iLvl].nSeg==0 );
+ }
+#endif
+
+ if( nBest<nMin && pStruct->aLevel[iBestLvl].nMerge==0 ){
+ break;
+ }
+ bRet = 1;
+ fts5IndexMergeLevel(p, &pStruct, iBestLvl, &nRem);
+ if( p->rc==SQLITE_OK && pStruct->aLevel[iBestLvl].nMerge==0 ){
+ fts5StructurePromote(p, iBestLvl+1, pStruct);
+ }
+ }
+ *ppStruct = pStruct;
+ return bRet;
+}
+
+/*
+** A total of nLeaf leaf pages of data has just been flushed to a level-0
+** segment. This function updates the write-counter accordingly and, if
+** necessary, performs incremental merge work.
+**
+** If an error occurs, set the Fts5Index.rc error code. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5IndexAutomerge(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct, /* IN/OUT: Current structure of index */
+ int nLeaf /* Number of output leaves just written */
+){
+ if( p->rc==SQLITE_OK && p->pConfig->nAutomerge>0 ){
+ Fts5Structure *pStruct = *ppStruct;
+ u64 nWrite; /* Initial value of write-counter */
+ int nWork; /* Number of work-quanta to perform */
+ int nRem; /* Number of leaf pages left to write */
+
+ /* Update the write-counter. While doing so, set nWork. */
+ nWrite = pStruct->nWriteCounter;
+ nWork = (int)(((nWrite + nLeaf) / p->nWorkUnit) - (nWrite / p->nWorkUnit));
+ pStruct->nWriteCounter += nLeaf;
+ nRem = (int)(p->nWorkUnit * nWork * pStruct->nLevel);
+
+ fts5IndexMerge(p, ppStruct, nRem, p->pConfig->nAutomerge);
+ }
+}
+
+static void fts5IndexCrisismerge(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Structure **ppStruct /* IN/OUT: Current structure of index */
+){
+ const int nCrisis = p->pConfig->nCrisisMerge;
+ Fts5Structure *pStruct = *ppStruct;
+ int iLvl = 0;
+
+ assert( p->rc!=SQLITE_OK || pStruct->nLevel>0 );
+ while( p->rc==SQLITE_OK && pStruct->aLevel[iLvl].nSeg>=nCrisis ){
+ fts5IndexMergeLevel(p, &pStruct, iLvl, 0);
+ assert( p->rc!=SQLITE_OK || pStruct->nLevel>(iLvl+1) );
+ fts5StructurePromote(p, iLvl+1, pStruct);
+ iLvl++;
+ }
+ *ppStruct = pStruct;
+}
+
+static int fts5IndexReturn(Fts5Index *p){
+ int rc = p->rc;
+ p->rc = SQLITE_OK;
+ return rc;
+}
+
+typedef struct Fts5FlushCtx Fts5FlushCtx;
+struct Fts5FlushCtx {
+ Fts5Index *pIdx;
+ Fts5SegWriter writer;
+};
+
+/*
+** Buffer aBuf[] contains a list of varints, all small enough to fit
+** in a 32-bit integer. Return the size of the largest prefix of this
+** list nMax bytes or less in size.
+*/
+static int fts5PoslistPrefix(const u8 *aBuf, int nMax){
+ int ret;
+ u32 dummy;
+ ret = fts5GetVarint32(aBuf, dummy);
+ if( ret<nMax ){
+ while( 1 ){
+ int i = fts5GetVarint32(&aBuf[ret], dummy);
+ if( (ret + i) > nMax ) break;
+ ret += i;
+ }
+ }
+ return ret;
+}
+
+/*
+** Flush the contents of in-memory hash table iHash to a new level-0
+** segment on disk. Also update the corresponding structure record.
+**
+** If an error occurs, set the Fts5Index.rc error code. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5FlushOneHash(Fts5Index *p){
+ Fts5Hash *pHash = p->pHash;
+ Fts5Structure *pStruct;
+ int iSegid;
+ int pgnoLast = 0; /* Last leaf page number in segment */
+
+ /* Obtain a reference to the index structure and allocate a new segment-id
+ ** for the new level-0 segment. */
+ pStruct = fts5StructureRead(p);
+ iSegid = fts5AllocateSegid(p, pStruct);
+ fts5StructureInvalidate(p);
+
+ if( iSegid ){
+ const int pgsz = p->pConfig->pgsz;
+ int eDetail = p->pConfig->eDetail;
+ Fts5StructureSegment *pSeg; /* New segment within pStruct */
+ Fts5Buffer *pBuf; /* Buffer in which to assemble leaf page */
+ Fts5Buffer *pPgidx; /* Buffer in which to assemble pgidx */
+
+ Fts5SegWriter writer;
+ fts5WriteInit(p, &writer, iSegid);
+
+ pBuf = &writer.writer.buf;
+ pPgidx = &writer.writer.pgidx;
+
+ /* fts5WriteInit() should have initialized the buffers to (most likely)
+ ** the maximum space required. */
+ assert( p->rc || pBuf->nSpace>=(pgsz + FTS5_DATA_PADDING) );
+ assert( p->rc || pPgidx->nSpace>=(pgsz + FTS5_DATA_PADDING) );
+
+ /* Begin scanning through hash table entries. This loop runs once for each
+ ** term/doclist currently stored within the hash table. */
+ if( p->rc==SQLITE_OK ){
+ p->rc = sqlite3Fts5HashScanInit(pHash, 0, 0);
+ }
+ while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
+ const char *zTerm; /* Buffer containing term */
+ const u8 *pDoclist; /* Pointer to doclist for this term */
+ int nDoclist; /* Size of doclist in bytes */
+
+ /* Write the term for this entry to disk. */
+ sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
+ fts5WriteAppendTerm(p, &writer, (int)strlen(zTerm), (const u8*)zTerm);
+
+ assert( writer.bFirstRowidInPage==0 );
+ if( pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
+ /* The entire doclist will fit on the current leaf. */
+ fts5BufferSafeAppendBlob(pBuf, pDoclist, nDoclist);
+ }else{
+ i64 iRowid = 0;
+ i64 iDelta = 0;
+ int iOff = 0;
+
+ /* The entire doclist will not fit on this leaf. The following
+ ** loop iterates through the poslists that make up the current
+ ** doclist. */
+ while( p->rc==SQLITE_OK && iOff<nDoclist ){
+ iOff += fts5GetVarint(&pDoclist[iOff], (u64*)&iDelta);
+ iRowid += iDelta;
+
+ if( writer.bFirstRowidInPage ){
+ fts5PutU16(&pBuf->p[0], (u16)pBuf->n); /* first rowid on page */
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iRowid);
+ writer.bFirstRowidInPage = 0;
+ fts5WriteDlidxAppend(p, &writer, iRowid);
+ }else{
+ pBuf->n += sqlite3Fts5PutVarint(&pBuf->p[pBuf->n], iDelta);
+ }
+ assert( pBuf->n<=pBuf->nSpace );
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ if( iOff<nDoclist && pDoclist[iOff]==0 ){
+ pBuf->p[pBuf->n++] = 0;
+ iOff++;
+ if( iOff<nDoclist && pDoclist[iOff]==0 ){
+ pBuf->p[pBuf->n++] = 0;
+ iOff++;
+ }
+ }
+ if( (pBuf->n + pPgidx->n)>=pgsz ){
+ fts5WriteFlushLeaf(p, &writer);
+ }
+ }else{
+ int bDummy;
+ int nPos;
+ int nCopy = fts5GetPoslistSize(&pDoclist[iOff], &nPos, &bDummy);
+ nCopy += nPos;
+ if( (pBuf->n + pPgidx->n + nCopy) <= pgsz ){
+ /* The entire poslist will fit on the current leaf. So copy
+ ** it in one go. */
+ fts5BufferSafeAppendBlob(pBuf, &pDoclist[iOff], nCopy);
+ }else{
+ /* The entire poslist will not fit on this leaf. So it needs
+ ** to be broken into sections. The only qualification being
+ ** that each varint must be stored contiguously. */
+ const u8 *pPoslist = &pDoclist[iOff];
+ int iPos = 0;
+ while( p->rc==SQLITE_OK ){
+ int nSpace = pgsz - pBuf->n - pPgidx->n;
+ int n = 0;
+ if( (nCopy - iPos)<=nSpace ){
+ n = nCopy - iPos;
+ }else{
+ n = fts5PoslistPrefix(&pPoslist[iPos], nSpace);
+ }
+ assert( n>0 );
+ fts5BufferSafeAppendBlob(pBuf, &pPoslist[iPos], n);
+ iPos += n;
+ if( (pBuf->n + pPgidx->n)>=pgsz ){
+ fts5WriteFlushLeaf(p, &writer);
+ }
+ if( iPos>=nCopy ) break;
+ }
+ }
+ iOff += nCopy;
+ }
+ }
+ }
+
+ /* TODO2: Doclist terminator written here. */
+ /* pBuf->p[pBuf->n++] = '\0'; */
+ assert( pBuf->n<=pBuf->nSpace );
+ sqlite3Fts5HashScanNext(pHash);
+ }
+ sqlite3Fts5HashClear(pHash);
+ fts5WriteFinish(p, &writer, &pgnoLast);
+
+ /* Update the Fts5Structure. It is written back to the database by the
+ ** fts5StructureRelease() call below. */
+ if( pStruct->nLevel==0 ){
+ fts5StructureAddLevel(&p->rc, &pStruct);
+ }
+ fts5StructureExtendLevel(&p->rc, pStruct, 0, 1, 0);
+ if( p->rc==SQLITE_OK ){
+ pSeg = &pStruct->aLevel[0].aSeg[ pStruct->aLevel[0].nSeg++ ];
+ pSeg->iSegid = iSegid;
+ pSeg->pgnoFirst = 1;
+ pSeg->pgnoLast = pgnoLast;
+ pStruct->nSegment++;
+ }
+ fts5StructurePromote(p, 0, pStruct);
+ }
+
+ fts5IndexAutomerge(p, &pStruct, pgnoLast);
+ fts5IndexCrisismerge(p, &pStruct);
+ fts5StructureWrite(p, pStruct);
+ fts5StructureRelease(pStruct);
+}
+
+/*
+** Flush any data stored in the in-memory hash tables to the database.
+*/
+static void fts5IndexFlush(Fts5Index *p){
+ /* Unless it is empty, flush the hash table to disk */
+ if( p->nPendingData ){
+ assert( p->pHash );
+ p->nPendingData = 0;
+ fts5FlushOneHash(p);
+ }
+}
+
+static Fts5Structure *fts5IndexOptimizeStruct(
+ Fts5Index *p,
+ Fts5Structure *pStruct
+){
+ Fts5Structure *pNew = 0;
+ int nByte = sizeof(Fts5Structure);
+ int nSeg = pStruct->nSegment;
+ int i;
+
+ /* Figure out if this structure requires optimization. A structure does
+ ** not require optimization if either:
+ **
+ ** + it consists of fewer than two segments, or
+ ** + all segments are on the same level, or
+ ** + all segments except one are currently inputs to a merge operation.
+ **
+ ** In the first case, return NULL. In the second, increment the ref-count
+ ** on *pStruct and return a copy of the pointer to it.
+ */
+ if( nSeg<2 ) return 0;
+ for(i=0; i<pStruct->nLevel; i++){
+ int nThis = pStruct->aLevel[i].nSeg;
+ if( nThis==nSeg || (nThis==nSeg-1 && pStruct->aLevel[i].nMerge==nThis) ){
+ fts5StructureRef(pStruct);
+ return pStruct;
+ }
+ assert( pStruct->aLevel[i].nMerge<=nThis );
+ }
+
+ nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel);
+ pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
+
+ if( pNew ){
+ Fts5StructureLevel *pLvl;
+ nByte = nSeg * sizeof(Fts5StructureSegment);
+ pNew->nLevel = pStruct->nLevel+1;
+ pNew->nRef = 1;
+ pNew->nWriteCounter = pStruct->nWriteCounter;
+ pLvl = &pNew->aLevel[pStruct->nLevel];
+ pLvl->aSeg = (Fts5StructureSegment*)sqlite3Fts5MallocZero(&p->rc, nByte);
+ if( pLvl->aSeg ){
+ int iLvl, iSeg;
+ int iSegOut = 0;
+ /* Iterate through all segments, from oldest to newest. Add them to
+ ** the new Fts5Level object so that pLvl->aSeg[0] is the oldest
+ ** segment in the data structure. */
+ for(iLvl=pStruct->nLevel-1; iLvl>=0; iLvl--){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ pLvl->aSeg[iSegOut] = pStruct->aLevel[iLvl].aSeg[iSeg];
+ iSegOut++;
+ }
+ }
+ pNew->nSegment = pLvl->nSeg = nSeg;
+ }else{
+ sqlite3_free(pNew);
+ pNew = 0;
+ }
+ }
+
+ return pNew;
+}
+
+static int sqlite3Fts5IndexOptimize(Fts5Index *p){
+ Fts5Structure *pStruct;
+ Fts5Structure *pNew = 0;
+
+ assert( p->rc==SQLITE_OK );
+ fts5IndexFlush(p);
+ pStruct = fts5StructureRead(p);
+ fts5StructureInvalidate(p);
+
+ if( pStruct ){
+ pNew = fts5IndexOptimizeStruct(p, pStruct);
+ }
+ fts5StructureRelease(pStruct);
+
+ assert( pNew==0 || pNew->nSegment>0 );
+ if( pNew ){
+ int iLvl;
+ for(iLvl=0; pNew->aLevel[iLvl].nSeg==0; iLvl++){}
+ while( p->rc==SQLITE_OK && pNew->aLevel[iLvl].nSeg>0 ){
+ int nRem = FTS5_OPT_WORK_UNIT;
+ fts5IndexMergeLevel(p, &pNew, iLvl, &nRem);
+ }
+
+ fts5StructureWrite(p, pNew);
+ fts5StructureRelease(pNew);
+ }
+
+ return fts5IndexReturn(p);
+}
+
+/*
+** This is called to implement the special "VALUES('merge', $nMerge)"
+** INSERT command.
+*/
+static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge){
+ Fts5Structure *pStruct = fts5StructureRead(p);
+ if( pStruct ){
+ int nMin = p->pConfig->nUsermerge;
+ fts5StructureInvalidate(p);
+ if( nMerge<0 ){
+ Fts5Structure *pNew = fts5IndexOptimizeStruct(p, pStruct);
+ fts5StructureRelease(pStruct);
+ pStruct = pNew;
+ nMin = 2;
+ nMerge = nMerge*-1;
+ }
+ if( pStruct && pStruct->nLevel ){
+ if( fts5IndexMerge(p, &pStruct, nMerge, nMin) ){
+ fts5StructureWrite(p, pStruct);
+ }
+ }
+ fts5StructureRelease(pStruct);
+ }
+ return fts5IndexReturn(p);
+}
+
+static void fts5AppendRowid(
+ Fts5Index *p,
+ i64 iDelta,
+ Fts5Iter *pUnused,
+ Fts5Buffer *pBuf
+){
+ UNUSED_PARAM(pUnused);
+ fts5BufferAppendVarint(&p->rc, pBuf, iDelta);
+}
+
+static void fts5AppendPoslist(
+ Fts5Index *p,
+ i64 iDelta,
+ Fts5Iter *pMulti,
+ Fts5Buffer *pBuf
+){
+ int nData = pMulti->base.nData;
+ assert( nData>0 );
+ if( p->rc==SQLITE_OK && 0==fts5BufferGrow(&p->rc, pBuf, nData+9+9) ){
+ fts5BufferSafeAppendVarint(pBuf, iDelta);
+ fts5BufferSafeAppendVarint(pBuf, nData*2);
+ fts5BufferSafeAppendBlob(pBuf, pMulti->base.pData, nData);
+ }
+}
+
+
+static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
+ u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;
+
+ assert( pIter->aPoslist );
+ if( p>=pIter->aEof ){
+ pIter->aPoslist = 0;
+ }else{
+ i64 iDelta;
+
+ p += fts5GetVarint(p, (u64*)&iDelta);
+ pIter->iRowid += iDelta;
+
+ /* Read position list size */
+ if( p[0] & 0x80 ){
+ int nPos;
+ pIter->nSize = fts5GetVarint32(p, nPos);
+ pIter->nPoslist = (nPos>>1);
+ }else{
+ pIter->nPoslist = ((int)(p[0])) >> 1;
+ pIter->nSize = 1;
+ }
+
+ pIter->aPoslist = p;
+ }
+}
+
+static void fts5DoclistIterInit(
+ Fts5Buffer *pBuf,
+ Fts5DoclistIter *pIter
+){
+ memset(pIter, 0, sizeof(*pIter));
+ pIter->aPoslist = pBuf->p;
+ pIter->aEof = &pBuf->p[pBuf->n];
+ fts5DoclistIterNext(pIter);
+}
+
+#if 0
+/*
+** Append a doclist to buffer pBuf.
+**
+** This function assumes that space within the buffer has already been
+** allocated.
+*/
+static void fts5MergeAppendDocid(
+ Fts5Buffer *pBuf, /* Buffer to write to */
+ i64 *piLastRowid, /* IN/OUT: Previous rowid written (if any) */
+ i64 iRowid /* Rowid to append */
+){
+ assert( pBuf->n!=0 || (*piLastRowid)==0 );
+ fts5BufferSafeAppendVarint(pBuf, iRowid - *piLastRowid);
+ *piLastRowid = iRowid;
+}
+#endif
+
+#define fts5MergeAppendDocid(pBuf, iLastRowid, iRowid) { \
+ assert( (pBuf)->n!=0 || (iLastRowid)==0 ); \
+ fts5BufferSafeAppendVarint((pBuf), (iRowid) - (iLastRowid)); \
+ (iLastRowid) = (iRowid); \
+}
+
+/*
+** Swap the contents of buffer *p1 with that of *p2.
+*/
+static void fts5BufferSwap(Fts5Buffer *p1, Fts5Buffer *p2){
+ Fts5Buffer tmp = *p1;
+ *p1 = *p2;
+ *p2 = tmp;
+}
+
+static void fts5NextRowid(Fts5Buffer *pBuf, int *piOff, i64 *piRowid){
+ int i = *piOff;
+ if( i>=pBuf->n ){
+ *piOff = -1;
+ }else{
+ u64 iVal;
+ *piOff = i + sqlite3Fts5GetVarint(&pBuf->p[i], &iVal);
+ *piRowid += iVal;
+ }
+}
+
+/*
+** This is the equivalent of fts5MergePrefixLists() for detail=none mode.
+** In this case the buffers consist of a delta-encoded list of rowids only.
+*/
+static void fts5MergeRowidLists(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Buffer *p1, /* First list to merge */
+ Fts5Buffer *p2 /* Second list to merge */
+){
+ int i1 = 0;
+ int i2 = 0;
+ i64 iRowid1 = 0;
+ i64 iRowid2 = 0;
+ i64 iOut = 0;
+
+ Fts5Buffer out;
+ memset(&out, 0, sizeof(out));
+ sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
+ if( p->rc ) return;
+
+ fts5NextRowid(p1, &i1, &iRowid1);
+ fts5NextRowid(p2, &i2, &iRowid2);
+ while( i1>=0 || i2>=0 ){
+ if( i1>=0 && (i2<0 || iRowid1<iRowid2) ){
+ assert( iOut==0 || iRowid1>iOut );
+ fts5BufferSafeAppendVarint(&out, iRowid1 - iOut);
+ iOut = iRowid1;
+ fts5NextRowid(p1, &i1, &iRowid1);
+ }else{
+ assert( iOut==0 || iRowid2>iOut );
+ fts5BufferSafeAppendVarint(&out, iRowid2 - iOut);
+ iOut = iRowid2;
+ if( i1>=0 && iRowid1==iRowid2 ){
+ fts5NextRowid(p1, &i1, &iRowid1);
+ }
+ fts5NextRowid(p2, &i2, &iRowid2);
+ }
+ }
+
+ fts5BufferSwap(&out, p1);
+ fts5BufferFree(&out);
+}
+
+/*
+** Buffers p1 and p2 contain doclists. This function merges the content
+** of the two doclists together and sets buffer p1 to the result before
+** returning.
+**
+** If an error occurs, an error code is left in p->rc. If an error has
+** already occurred, this function is a no-op.
+*/
+static void fts5MergePrefixLists(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5Buffer *p1, /* First list to merge */
+ Fts5Buffer *p2 /* Second list to merge */
+){
+ if( p2->n ){
+ i64 iLastRowid = 0;
+ Fts5DoclistIter i1;
+ Fts5DoclistIter i2;
+ Fts5Buffer out = {0, 0, 0};
+ Fts5Buffer tmp = {0, 0, 0};
+
+ if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n) ) return;
+ fts5DoclistIterInit(p1, &i1);
+ fts5DoclistIterInit(p2, &i2);
+
+ while( 1 ){
+ if( i1.iRowid<i2.iRowid ){
+ /* Copy entry from i1 */
+ fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
+ fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize);
+ fts5DoclistIterNext(&i1);
+ if( i1.aPoslist==0 ) break;
+ }
+ else if( i2.iRowid!=i1.iRowid ){
+ /* Copy entry from i2 */
+ fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
+ fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
+ fts5DoclistIterNext(&i2);
+ if( i2.aPoslist==0 ) break;
+ }
+ else{
+ /* Merge the two position lists. */
+ i64 iPos1 = 0;
+ i64 iPos2 = 0;
+ int iOff1 = 0;
+ int iOff2 = 0;
+ u8 *a1 = &i1.aPoslist[i1.nSize];
+ u8 *a2 = &i2.aPoslist[i2.nSize];
+
+ i64 iPrev = 0;
+ Fts5PoslistWriter writer;
+ memset(&writer, 0, sizeof(writer));
+
+ fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
+ fts5BufferZero(&tmp);
+ sqlite3Fts5BufferSize(&p->rc, &tmp, i1.nPoslist + i2.nPoslist);
+ if( p->rc ) break;
+
+ sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
+ sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ assert( iPos1>=0 && iPos2>=0 );
+
+ if( iPos1<iPos2 ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
+ sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
+ }else{
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
+ sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ }
+
+ if( iPos1>=0 && iPos2>=0 ){
+ while( 1 ){
+ if( iPos1<iPos2 ){
+ if( iPos1!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
+ }
+ sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
+ if( iPos1<0 ) break;
+ }else{
+ assert( iPos2!=iPrev );
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
+ sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
+ if( iPos2<0 ) break;
+ }
+ }
+ }
+
+ if( iPos1>=0 ){
+ if( iPos1!=iPrev ){
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
+ }
+ fts5BufferSafeAppendBlob(&tmp, &a1[iOff1], i1.nPoslist-iOff1);
+ }else{
+ assert( iPos2>=0 && iPos2!=iPrev );
+ sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
+ fts5BufferSafeAppendBlob(&tmp, &a2[iOff2], i2.nPoslist-iOff2);
+ }
+
+ /* WRITEPOSLISTSIZE */
+ fts5BufferSafeAppendVarint(&out, tmp.n * 2);
+ fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
+ fts5DoclistIterNext(&i1);
+ fts5DoclistIterNext(&i2);
+ if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
+ }
+ }
+
+ if( i1.aPoslist ){
+ fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
+ fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist);
+ }
+ else if( i2.aPoslist ){
+ fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
+ fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
+ }
+
+ fts5BufferSet(&p->rc, p1, out.n, out.p);
+ fts5BufferFree(&tmp);
+ fts5BufferFree(&out);
+ }
+}
+
+static void fts5SetupPrefixIter(
+ Fts5Index *p, /* Index to read from */
+ int bDesc, /* True for "ORDER BY rowid DESC" */
+ const u8 *pToken, /* Buffer containing prefix to match */
+ int nToken, /* Size of buffer pToken in bytes */
+ Fts5Colset *pColset, /* Restrict matches to these columns */
+ Fts5Iter **ppIter /* OUT: New iterator */
+){
+ Fts5Structure *pStruct;
+ Fts5Buffer *aBuf;
+ const int nBuf = 32;
+
+ void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*);
+ void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
+ if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
+ xMerge = fts5MergeRowidLists;
+ xAppend = fts5AppendRowid;
+ }else{
+ xMerge = fts5MergePrefixLists;
+ xAppend = fts5AppendPoslist;
+ }
+
+ aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
+ pStruct = fts5StructureRead(p);
+
+ if( aBuf && pStruct ){
+ const int flags = FTS5INDEX_QUERY_SCAN
+ | FTS5INDEX_QUERY_SKIPEMPTY
+ | FTS5INDEX_QUERY_NOOUTPUT;
+ int i;
+ i64 iLastRowid = 0;
+ Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
+ Fts5Data *pData;
+ Fts5Buffer doclist;
+ int bNewTerm = 1;
+
+ memset(&doclist, 0, sizeof(doclist));
+ fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
+ fts5IterSetOutputCb(&p->rc, p1);
+ for( /* no-op */ ;
+ fts5MultiIterEof(p, p1)==0;
+ fts5MultiIterNext2(p, p1, &bNewTerm)
+ ){
+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
+ int nTerm = pSeg->term.n;
+ const u8 *pTerm = pSeg->term.p;
+ p1->xSetOutputs(p1, pSeg);
+
+ assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
+ if( bNewTerm ){
+ if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
+ }
+
+ if( p1->base.nData==0 ) continue;
+
+ if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
+ for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
+ assert( i<nBuf );
+ if( aBuf[i].n==0 ){
+ fts5BufferSwap(&doclist, &aBuf[i]);
+ fts5BufferZero(&doclist);
+ }else{
+ xMerge(p, &doclist, &aBuf[i]);
+ fts5BufferZero(&aBuf[i]);
+ }
+ }
+ iLastRowid = 0;
+ }
+
+ xAppend(p, p1->base.iRowid-iLastRowid, p1, &doclist);
+ iLastRowid = p1->base.iRowid;
+ }
+
+ for(i=0; i<nBuf; i++){
+ if( p->rc==SQLITE_OK ){
+ xMerge(p, &doclist, &aBuf[i]);
+ }
+ fts5BufferFree(&aBuf[i]);
+ }
+ fts5MultiIterFree(p1);
+
+ pData = fts5IdxMalloc(p, sizeof(Fts5Data) + doclist.n);
+ if( pData ){
+ pData->p = (u8*)&pData[1];
+ pData->nn = pData->szLeaf = doclist.n;
+ memcpy(pData->p, doclist.p, doclist.n);
+ fts5MultiIterNew2(p, pData, bDesc, ppIter);
+ }
+ fts5BufferFree(&doclist);
+ }
+
+ fts5StructureRelease(pStruct);
+ sqlite3_free(aBuf);
+}
+
+
+/*
+** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
+** to the document with rowid iRowid.
+*/
+static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
+ assert( p->rc==SQLITE_OK );
+
+ /* Allocate the hash table if it has not already been allocated */
+ if( p->pHash==0 ){
+ p->rc = sqlite3Fts5HashNew(p->pConfig, &p->pHash, &p->nPendingData);
+ }
+
+ /* Flush the hash table to disk if required */
+ if( iRowid<p->iWriteRowid
+ || (iRowid==p->iWriteRowid && p->bDelete==0)
+ || (p->nPendingData > p->pConfig->nHashSize)
+ ){
+ fts5IndexFlush(p);
+ }
+
+ p->iWriteRowid = iRowid;
+ p->bDelete = bDelete;
+ return fts5IndexReturn(p);
+}
+
+/*
+** Commit data to disk.
+*/
+static int sqlite3Fts5IndexSync(Fts5Index *p, int bCommit){
+ assert( p->rc==SQLITE_OK );
+ fts5IndexFlush(p);
+ if( bCommit ) fts5CloseReader(p);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Discard any data stored in the in-memory hash tables. Do not write it
+** to the database. Additionally, assume that the contents of the %_data
+** table may have changed on disk. So any in-memory caches of %_data
+** records must be invalidated.
+*/
+static int sqlite3Fts5IndexRollback(Fts5Index *p){
+ fts5CloseReader(p);
+ fts5IndexDiscardData(p);
+ fts5StructureInvalidate(p);
+ /* assert( p->rc==SQLITE_OK ); */
+ return SQLITE_OK;
+}
+
+/*
+** The %_data table is completely empty when this function is called. This
+** function populates it with the initial structure objects for each index,
+** and the initial version of the "averages" record (a zero-byte blob).
+*/
+static int sqlite3Fts5IndexReinit(Fts5Index *p){
+ Fts5Structure s;
+ fts5StructureInvalidate(p);
+ memset(&s, 0, sizeof(Fts5Structure));
+ fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
+ fts5StructureWrite(p, &s);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Open a new Fts5Index handle. If the bCreate argument is true, create
+** and initialize the underlying %_data table.
+**
+** If successful, set *pp to point to the new object and return SQLITE_OK.
+** Otherwise, set *pp to NULL and return an SQLite error code.
+*/
+static int sqlite3Fts5IndexOpen(
+ Fts5Config *pConfig,
+ int bCreate,
+ Fts5Index **pp,
+ char **pzErr
+){
+ int rc = SQLITE_OK;
+ Fts5Index *p; /* New object */
+
+ *pp = p = (Fts5Index*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Index));
+ if( rc==SQLITE_OK ){
+ p->pConfig = pConfig;
+ p->nWorkUnit = FTS5_WORK_UNIT;
+ p->zDataTbl = sqlite3Fts5Mprintf(&rc, "%s_data", pConfig->zName);
+ if( p->zDataTbl && bCreate ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "data", "id INTEGER PRIMARY KEY, block BLOB", 0, pzErr
+ );
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5CreateTable(pConfig, "idx",
+ "segid, term, pgno, PRIMARY KEY(segid, term)",
+ 1, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexReinit(p);
+ }
+ }
+ }
+
+ assert( rc!=SQLITE_OK || p->rc==SQLITE_OK );
+ if( rc ){
+ sqlite3Fts5IndexClose(p);
+ *pp = 0;
+ }
+ return rc;
+}
+
+/*
+** Close a handle opened by an earlier call to sqlite3Fts5IndexOpen().
+*/
+static int sqlite3Fts5IndexClose(Fts5Index *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ assert( p->pReader==0 );
+ fts5StructureInvalidate(p);
+ sqlite3_finalize(p->pWriter);
+ sqlite3_finalize(p->pDeleter);
+ sqlite3_finalize(p->pIdxWriter);
+ sqlite3_finalize(p->pIdxDeleter);
+ sqlite3_finalize(p->pIdxSelect);
+ sqlite3_finalize(p->pDataVersion);
+ sqlite3Fts5HashFree(p->pHash);
+ sqlite3_free(p->zDataTbl);
+ sqlite3_free(p);
+ }
+ return rc;
+}
+
+/*
+** Argument p points to a buffer containing utf-8 text that is n bytes in
+** size. Return the number of bytes in the nChar character prefix of the
+** buffer, or 0 if there are less than nChar characters in total.
+*/
+static int sqlite3Fts5IndexCharlenToBytelen(
+ const char *p,
+ int nByte,
+ int nChar
+){
+ int n = 0;
+ int i;
+ for(i=0; i<nChar; i++){
+ if( n>=nByte ) return 0; /* Input contains fewer than nChar chars */
+ if( (unsigned char)p[n++]>=0xc0 ){
+ while( (p[n] & 0xc0)==0x80 ) n++;
+ }
+ }
+ return n;
+}
+
+/*
+** pIn is a UTF-8 encoded string, nIn bytes in size. Return the number of
+** unicode characters in the string.
+*/
+static int fts5IndexCharlen(const char *pIn, int nIn){
+ int nChar = 0;
+ int i = 0;
+ while( i<nIn ){
+ if( (unsigned char)pIn[i++]>=0xc0 ){
+ while( i<nIn && (pIn[i] & 0xc0)==0x80 ) i++;
+ }
+ nChar++;
+ }
+ return nChar;
+}
+
+/*
+** Insert or remove data to or from the index. Each time a document is
+** added to or removed from the index, this function is called one or more
+** times.
+**
+** For an insert, it must be called once for each token in the new document.
+** If the operation is a delete, it must be called (at least) once for each
+** unique token in the document with an iCol value less than zero. The iPos
+** argument is ignored for a delete.
+*/
+static int sqlite3Fts5IndexWrite(
+ Fts5Index *p, /* Index to write to */
+ int iCol, /* Column token appears in (-ve -> delete) */
+ int iPos, /* Position of token within column */
+ const char *pToken, int nToken /* Token to add or remove to or from index */
+){
+ int i; /* Used to iterate through indexes */
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Config *pConfig = p->pConfig;
+
+ assert( p->rc==SQLITE_OK );
+ assert( (iCol<0)==p->bDelete );
+
+ /* Add the entry to the main terms index. */
+ rc = sqlite3Fts5HashWrite(
+ p->pHash, p->iWriteRowid, iCol, iPos, FTS5_MAIN_PREFIX, pToken, nToken
+ );
+
+ for(i=0; i<pConfig->nPrefix && rc==SQLITE_OK; i++){
+ const int nChar = pConfig->aPrefix[i];
+ int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar);
+ if( nByte ){
+ rc = sqlite3Fts5HashWrite(p->pHash,
+ p->iWriteRowid, iCol, iPos, (char)(FTS5_MAIN_PREFIX+i+1), pToken,
+ nByte
+ );
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Open a new iterator to iterate though all rowid that match the
+** specified token or token prefix.
+*/
+static int sqlite3Fts5IndexQuery(
+ Fts5Index *p, /* FTS index to query */
+ const char *pToken, int nToken, /* Token (or prefix) to query for */
+ int flags, /* Mask of FTS5INDEX_QUERY_X flags */
+ Fts5Colset *pColset, /* Match these columns only */
+ Fts5IndexIter **ppIter /* OUT: New iterator object */
+){
+ Fts5Config *pConfig = p->pConfig;
+ Fts5Iter *pRet = 0;
+ Fts5Buffer buf = {0, 0, 0};
+
+ /* If the QUERY_SCAN flag is set, all other flags must be clear. */
+ assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
+
+ if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
+ int iIdx = 0; /* Index to search */
+ memcpy(&buf.p[1], pToken, nToken);
+
+ /* Figure out which index to search and set iIdx accordingly. If this
+ ** is a prefix query for which there is no prefix index, set iIdx to
+ ** greater than pConfig->nPrefix to indicate that the query will be
+ ** satisfied by scanning multiple terms in the main index.
+ **
+ ** If the QUERY_TEST_NOIDX flag was specified, then this must be a
+ ** prefix-query. Instead of using a prefix-index (if one exists),
+ ** evaluate the prefix query using the main FTS index. This is used
+ ** for internal sanity checking by the integrity-check in debug
+ ** mode only. */
+#ifdef SQLITE_DEBUG
+ if( pConfig->bPrefixIndex==0 || (flags & FTS5INDEX_QUERY_TEST_NOIDX) ){
+ assert( flags & FTS5INDEX_QUERY_PREFIX );
+ iIdx = 1+pConfig->nPrefix;
+ }else
+#endif
+ if( flags & FTS5INDEX_QUERY_PREFIX ){
+ int nChar = fts5IndexCharlen(pToken, nToken);
+ for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
+ if( pConfig->aPrefix[iIdx-1]==nChar ) break;
+ }
+ }
+
+ if( iIdx<=pConfig->nPrefix ){
+ /* Straight index lookup */
+ Fts5Structure *pStruct = fts5StructureRead(p);
+ buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
+ if( pStruct ){
+ fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
+ pColset, buf.p, nToken+1, -1, 0, &pRet
+ );
+ fts5StructureRelease(pStruct);
+ }
+ }else{
+ /* Scan multiple terms in the main index */
+ int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
+ buf.p[0] = FTS5_MAIN_PREFIX;
+ fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
+ assert( p->rc!=SQLITE_OK || pRet->pColset==0 );
+ fts5IterSetOutputCb(&p->rc, pRet);
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
+ if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
+ }
+ }
+
+ if( p->rc ){
+ sqlite3Fts5IterClose(&pRet->base);
+ pRet = 0;
+ fts5CloseReader(p);
+ }
+
+ *ppIter = &pRet->base;
+ sqlite3Fts5BufferFree(&buf);
+ }
+ return fts5IndexReturn(p);
+}
+
+/*
+** Return true if the iterator passed as the only argument is at EOF.
+*/
+/*
+** Move to the next matching rowid.
+*/
+static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ assert( pIter->pIndex->rc==SQLITE_OK );
+ fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
+ return fts5IndexReturn(pIter->pIndex);
+}
+
+/*
+** Move to the next matching term/rowid. Used by the fts5vocab module.
+*/
+static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5Index *p = pIter->pIndex;
+
+ assert( pIter->pIndex->rc==SQLITE_OK );
+
+ fts5MultiIterNext(p, pIter, 0, 0);
+ if( p->rc==SQLITE_OK ){
+ Fts5SegIter *pSeg = &pIter->aSeg[ pIter->aFirst[1].iFirst ];
+ if( pSeg->pLeaf && pSeg->term.p[0]!=FTS5_MAIN_PREFIX ){
+ fts5DataRelease(pSeg->pLeaf);
+ pSeg->pLeaf = 0;
+ pIter->base.bEof = 1;
+ }
+ }
+
+ return fts5IndexReturn(pIter->pIndex);
+}
+
+/*
+** Move to the next matching rowid that occurs at or after iMatch. The
+** definition of "at or after" depends on whether this iterator iterates
+** in ascending or descending rowid order.
+*/
+static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
+ return fts5IndexReturn(pIter->pIndex);
+}
+
+/*
+** Return the current term.
+*/
+static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
+ int n;
+ const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
+ *pn = n-1;
+ return &z[1];
+}
+
+/*
+** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
+*/
+static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
+ if( pIndexIter ){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5Index *pIndex = pIter->pIndex;
+ fts5MultiIterFree(pIter);
+ fts5CloseReader(pIndex);
+ }
+}
+
+/*
+** Read and decode the "averages" record from the database.
+**
+** Parameter anSize must point to an array of size nCol, where nCol is
+** the number of user defined columns in the FTS table.
+*/
+static int sqlite3Fts5IndexGetAverages(Fts5Index *p, i64 *pnRow, i64 *anSize){
+ int nCol = p->pConfig->nCol;
+ Fts5Data *pData;
+
+ *pnRow = 0;
+ memset(anSize, 0, sizeof(i64) * nCol);
+ pData = fts5DataRead(p, FTS5_AVERAGES_ROWID);
+ if( p->rc==SQLITE_OK && pData->nn ){
+ int i = 0;
+ int iCol;
+ i += fts5GetVarint(&pData->p[i], (u64*)pnRow);
+ for(iCol=0; i<pData->nn && iCol<nCol; iCol++){
+ i += fts5GetVarint(&pData->p[i], (u64*)&anSize[iCol]);
+ }
+ }
+
+ fts5DataRelease(pData);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Replace the current "averages" record with the contents of the buffer
+** supplied as the second argument.
+*/
+static int sqlite3Fts5IndexSetAverages(Fts5Index *p, const u8 *pData, int nData){
+ assert( p->rc==SQLITE_OK );
+ fts5DataWrite(p, FTS5_AVERAGES_ROWID, pData, nData);
+ return fts5IndexReturn(p);
+}
+
+/*
+** Return the total number of blocks this module has read from the %_data
+** table since it was created.
+*/
+static int sqlite3Fts5IndexReads(Fts5Index *p){
+ return p->nRead;
+}
+
+/*
+** Set the 32-bit cookie value stored at the start of all structure
+** records to the value passed as the second argument.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int sqlite3Fts5IndexSetCookie(Fts5Index *p, int iNew){
+ int rc; /* Return code */
+ Fts5Config *pConfig = p->pConfig; /* Configuration object */
+ u8 aCookie[4]; /* Binary representation of iNew */
+ sqlite3_blob *pBlob = 0;
+
+ assert( p->rc==SQLITE_OK );
+ sqlite3Fts5Put32(aCookie, iNew);
+
+ rc = sqlite3_blob_open(pConfig->db, pConfig->zDb, p->zDataTbl,
+ "block", FTS5_STRUCTURE_ROWID, 1, &pBlob
+ );
+ if( rc==SQLITE_OK ){
+ sqlite3_blob_write(pBlob, aCookie, 4, 0);
+ rc = sqlite3_blob_close(pBlob);
+ }
+
+ return rc;
+}
+
+static int sqlite3Fts5IndexLoadConfig(Fts5Index *p){
+ Fts5Structure *pStruct;
+ pStruct = fts5StructureRead(p);
+ fts5StructureRelease(pStruct);
+ return fts5IndexReturn(p);
+}
+
+
+/*************************************************************************
+**************************************************************************
+** Below this point is the implementation of the integrity-check
+** functionality.
+*/
+
+/*
+** Return a simple checksum value based on the arguments.
+*/
+static u64 sqlite3Fts5IndexEntryCksum(
+ i64 iRowid,
+ int iCol,
+ int iPos,
+ int iIdx,
+ const char *pTerm,
+ int nTerm
+){
+ int i;
+ u64 ret = iRowid;
+ ret += (ret<<3) + iCol;
+ ret += (ret<<3) + iPos;
+ if( iIdx>=0 ) ret += (ret<<3) + (FTS5_MAIN_PREFIX + iIdx);
+ for(i=0; i<nTerm; i++) ret += (ret<<3) + pTerm[i];
+ return ret;
+}
+
+#ifdef SQLITE_DEBUG
+/*
+** This function is purely an internal test. It does not contribute to
+** FTS functionality, or even the integrity-check, in any way.
+**
+** Instead, it tests that the same set of pgno/rowid combinations are
+** visited regardless of whether the doclist-index identified by parameters
+** iSegid/iLeaf is iterated in forwards or reverse order.
+*/
+static void fts5TestDlidxReverse(
+ Fts5Index *p,
+ int iSegid, /* Segment id to load from */
+ int iLeaf /* Load doclist-index for this leaf */
+){
+ Fts5DlidxIter *pDlidx = 0;
+ u64 cksum1 = 13;
+ u64 cksum2 = 13;
+
+ for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iLeaf);
+ fts5DlidxIterEof(p, pDlidx)==0;
+ fts5DlidxIterNext(p, pDlidx)
+ ){
+ i64 iRowid = fts5DlidxIterRowid(pDlidx);
+ int pgno = fts5DlidxIterPgno(pDlidx);
+ assert( pgno>iLeaf );
+ cksum1 += iRowid + ((i64)pgno<<32);
+ }
+ fts5DlidxIterFree(pDlidx);
+ pDlidx = 0;
+
+ for(pDlidx=fts5DlidxIterInit(p, 1, iSegid, iLeaf);
+ fts5DlidxIterEof(p, pDlidx)==0;
+ fts5DlidxIterPrev(p, pDlidx)
+ ){
+ i64 iRowid = fts5DlidxIterRowid(pDlidx);
+ int pgno = fts5DlidxIterPgno(pDlidx);
+ assert( fts5DlidxIterPgno(pDlidx)>iLeaf );
+ cksum2 += iRowid + ((i64)pgno<<32);
+ }
+ fts5DlidxIterFree(pDlidx);
+ pDlidx = 0;
+
+ if( p->rc==SQLITE_OK && cksum1!=cksum2 ) p->rc = FTS5_CORRUPT;
+}
+
+static int fts5QueryCksum(
+ Fts5Index *p, /* Fts5 index object */
+ int iIdx,
+ const char *z, /* Index key to query for */
+ int n, /* Size of index key in bytes */
+ int flags, /* Flags for Fts5IndexQuery */
+ u64 *pCksum /* IN/OUT: Checksum value */
+){
+ int eDetail = p->pConfig->eDetail;
+ u64 cksum = *pCksum;
+ Fts5IndexIter *pIter = 0;
+ int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);
+
+ while( rc==SQLITE_OK && 0==sqlite3Fts5IterEof(pIter) ){
+ i64 rowid = pIter->iRowid;
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
+ }else{
+ Fts5PoslistReader sReader;
+ for(sqlite3Fts5PoslistReaderInit(pIter->pData, pIter->nData, &sReader);
+ sReader.bEof==0;
+ sqlite3Fts5PoslistReaderNext(&sReader)
+ ){
+ int iCol = FTS5_POS2COLUMN(sReader.iPos);
+ int iOff = FTS5_POS2OFFSET(sReader.iPos);
+ cksum ^= sqlite3Fts5IndexEntryCksum(rowid, iCol, iOff, iIdx, z, n);
+ }
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IterNext(pIter);
+ }
+ }
+ sqlite3Fts5IterClose(pIter);
+
+ *pCksum = cksum;
+ return rc;
+}
+
+
+/*
+** This function is also purely an internal test. It does not contribute to
+** FTS functionality, or even the integrity-check, in any way.
+*/
+static void fts5TestTerm(
+ Fts5Index *p,
+ Fts5Buffer *pPrev, /* Previous term */
+ const char *z, int n, /* Possibly new term to test */
+ u64 expected,
+ u64 *pCksum
+){
+ int rc = p->rc;
+ if( pPrev->n==0 ){
+ fts5BufferSet(&rc, pPrev, n, (const u8*)z);
+ }else
+ if( rc==SQLITE_OK && (pPrev->n!=n || memcmp(pPrev->p, z, n)) ){
+ u64 cksum3 = *pCksum;
+ const char *zTerm = (const char*)&pPrev->p[1]; /* term sans prefix-byte */
+ int nTerm = pPrev->n-1; /* Size of zTerm in bytes */
+ int iIdx = (pPrev->p[0] - FTS5_MAIN_PREFIX);
+ int flags = (iIdx==0 ? 0 : FTS5INDEX_QUERY_PREFIX);
+ u64 ck1 = 0;
+ u64 ck2 = 0;
+
+ /* Check that the results returned for ASC and DESC queries are
+ ** the same. If not, call this corruption. */
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, flags, &ck1);
+ if( rc==SQLITE_OK ){
+ int f = flags|FTS5INDEX_QUERY_DESC;
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
+ }
+ if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
+
+ /* If this is a prefix query, check that the results returned if the
+ ** the index is disabled are the same. In both ASC and DESC order.
+ **
+ ** This check may only be performed if the hash table is empty. This
+ ** is because the hash table only supports a single scan query at
+ ** a time, and the multi-iter loop from which this function is called
+ ** is already performing such a scan. */
+ if( p->nPendingData==0 ){
+ if( iIdx>0 && rc==SQLITE_OK ){
+ int f = flags|FTS5INDEX_QUERY_TEST_NOIDX;
+ ck2 = 0;
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
+ if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
+ }
+ if( iIdx>0 && rc==SQLITE_OK ){
+ int f = flags|FTS5INDEX_QUERY_TEST_NOIDX|FTS5INDEX_QUERY_DESC;
+ ck2 = 0;
+ rc = fts5QueryCksum(p, iIdx, zTerm, nTerm, f, &ck2);
+ if( rc==SQLITE_OK && ck1!=ck2 ) rc = FTS5_CORRUPT;
+ }
+ }
+
+ cksum3 ^= ck1;
+ fts5BufferSet(&rc, pPrev, n, (const u8*)z);
+
+ if( rc==SQLITE_OK && cksum3!=expected ){
+ rc = FTS5_CORRUPT;
+ }
+ *pCksum = cksum3;
+ }
+ p->rc = rc;
+}
+
+#else
+# define fts5TestDlidxReverse(x,y,z)
+# define fts5TestTerm(u,v,w,x,y,z)
+#endif
+
+/*
+** Check that:
+**
+** 1) All leaves of pSeg between iFirst and iLast (inclusive) exist and
+** contain zero terms.
+** 2) All leaves of pSeg between iNoRowid and iLast (inclusive) exist and
+** contain zero rowids.
+*/
+static void fts5IndexIntegrityCheckEmpty(
+ Fts5Index *p,
+ Fts5StructureSegment *pSeg, /* Segment to check internal consistency */
+ int iFirst,
+ int iNoRowid,
+ int iLast
+){
+ int i;
+
+ /* Now check that the iter.nEmpty leaves following the current leaf
+ ** (a) exist and (b) contain no terms. */
+ for(i=iFirst; p->rc==SQLITE_OK && i<=iLast; i++){
+ Fts5Data *pLeaf = fts5DataRead(p, FTS5_SEGMENT_ROWID(pSeg->iSegid, i));
+ if( pLeaf ){
+ if( !fts5LeafIsTermless(pLeaf) ) p->rc = FTS5_CORRUPT;
+ if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
+ }
+ fts5DataRelease(pLeaf);
+ }
+}
+
+static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
+ int iTermOff = 0;
+ int ii;
+
+ Fts5Buffer buf1 = {0,0,0};
+ Fts5Buffer buf2 = {0,0,0};
+
+ ii = pLeaf->szLeaf;
+ while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
+ int res;
+ int iOff;
+ int nIncr;
+
+ ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
+ iTermOff += nIncr;
+ iOff = iTermOff;
+
+ if( iOff>=pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else if( iTermOff==nIncr ){
+ int nByte;
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
+ if( (iOff+nByte)>pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ fts5BufferSet(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
+ }
+ }else{
+ int nKeep, nByte;
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nKeep);
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nByte);
+ if( nKeep>buf1.n || (iOff+nByte)>pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ buf1.n = nKeep;
+ fts5BufferAppendBlob(&p->rc, &buf1, nByte, &pLeaf->p[iOff]);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ res = fts5BufferCompare(&buf1, &buf2);
+ if( res<=0 ) p->rc = FTS5_CORRUPT;
+ }
+ }
+ fts5BufferSet(&p->rc, &buf2, buf1.n, buf1.p);
+ }
+
+ fts5BufferFree(&buf1);
+ fts5BufferFree(&buf2);
+}
+
+static void fts5IndexIntegrityCheckSegment(
+ Fts5Index *p, /* FTS5 backend object */
+ Fts5StructureSegment *pSeg /* Segment to check internal consistency */
+){
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pStmt = 0;
+ int rc2;
+ int iIdxPrevLeaf = pSeg->pgnoFirst-1;
+ int iDlidxPrevLeaf = pSeg->pgnoLast;
+
+ if( pSeg->pgnoFirst==0 ) return;
+
+ fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf(
+ "SELECT segid, term, (pgno>>1), (pgno&1) FROM %Q.'%q_idx' WHERE segid=%d",
+ pConfig->zDb, pConfig->zName, pSeg->iSegid
+ ));
+
+ /* Iterate through the b-tree hierarchy. */
+ while( p->rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ i64 iRow; /* Rowid for this leaf */
+ Fts5Data *pLeaf; /* Data for this leaf */
+
+ int nIdxTerm = sqlite3_column_bytes(pStmt, 1);
+ const char *zIdxTerm = (const char*)sqlite3_column_text(pStmt, 1);
+ int iIdxLeaf = sqlite3_column_int(pStmt, 2);
+ int bIdxDlidx = sqlite3_column_int(pStmt, 3);
+
+ /* If the leaf in question has already been trimmed from the segment,
+ ** ignore this b-tree entry. Otherwise, load it into memory. */
+ if( iIdxLeaf<pSeg->pgnoFirst ) continue;
+ iRow = FTS5_SEGMENT_ROWID(pSeg->iSegid, iIdxLeaf);
+ pLeaf = fts5DataRead(p, iRow);
+ if( pLeaf==0 ) break;
+
+ /* Check that the leaf contains at least one term, and that it is equal
+ ** to or larger than the split-key in zIdxTerm. Also check that if there
+ ** is also a rowid pointer within the leaf page header, it points to a
+ ** location before the term. */
+ if( pLeaf->nn<=pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ int iOff; /* Offset of first term on leaf */
+ int iRowidOff; /* Offset of first rowid on leaf */
+ int nTerm; /* Size of term on leaf in bytes */
+ int res; /* Comparison of term and split-key */
+
+ iOff = fts5LeafFirstTermOff(pLeaf);
+ iRowidOff = fts5LeafFirstRowidOff(pLeaf);
+ if( iRowidOff>=iOff ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ iOff += fts5GetVarint32(&pLeaf->p[iOff], nTerm);
+ res = memcmp(&pLeaf->p[iOff], zIdxTerm, MIN(nTerm, nIdxTerm));
+ if( res==0 ) res = nTerm - nIdxTerm;
+ if( res<0 ) p->rc = FTS5_CORRUPT;
+ }
+
+ fts5IntegrityCheckPgidx(p, pLeaf);
+ }
+ fts5DataRelease(pLeaf);
+ if( p->rc ) break;
+
+ /* Now check that the iter.nEmpty leaves following the current leaf
+ ** (a) exist and (b) contain no terms. */
+ fts5IndexIntegrityCheckEmpty(
+ p, pSeg, iIdxPrevLeaf+1, iDlidxPrevLeaf+1, iIdxLeaf-1
+ );
+ if( p->rc ) break;
+
+ /* If there is a doclist-index, check that it looks right. */
+ if( bIdxDlidx ){
+ Fts5DlidxIter *pDlidx = 0; /* For iterating through doclist index */
+ int iPrevLeaf = iIdxLeaf;
+ int iSegid = pSeg->iSegid;
+ int iPg = 0;
+ i64 iKey;
+
+ for(pDlidx=fts5DlidxIterInit(p, 0, iSegid, iIdxLeaf);
+ fts5DlidxIterEof(p, pDlidx)==0;
+ fts5DlidxIterNext(p, pDlidx)
+ ){
+
+ /* Check any rowid-less pages that occur before the current leaf. */
+ for(iPg=iPrevLeaf+1; iPg<fts5DlidxIterPgno(pDlidx); iPg++){
+ iKey = FTS5_SEGMENT_ROWID(iSegid, iPg);
+ pLeaf = fts5DataRead(p, iKey);
+ if( pLeaf ){
+ if( fts5LeafFirstRowidOff(pLeaf)!=0 ) p->rc = FTS5_CORRUPT;
+ fts5DataRelease(pLeaf);
+ }
+ }
+ iPrevLeaf = fts5DlidxIterPgno(pDlidx);
+
+ /* Check that the leaf page indicated by the iterator really does
+ ** contain the rowid suggested by the same. */
+ iKey = FTS5_SEGMENT_ROWID(iSegid, iPrevLeaf);
+ pLeaf = fts5DataRead(p, iKey);
+ if( pLeaf ){
+ i64 iRowid;
+ int iRowidOff = fts5LeafFirstRowidOff(pLeaf);
+ ASSERT_SZLEAF_OK(pLeaf);
+ if( iRowidOff>=pLeaf->szLeaf ){
+ p->rc = FTS5_CORRUPT;
+ }else{
+ fts5GetVarint(&pLeaf->p[iRowidOff], (u64*)&iRowid);
+ if( iRowid!=fts5DlidxIterRowid(pDlidx) ) p->rc = FTS5_CORRUPT;
+ }
+ fts5DataRelease(pLeaf);
+ }
+ }
+
+ iDlidxPrevLeaf = iPg;
+ fts5DlidxIterFree(pDlidx);
+ fts5TestDlidxReverse(p, iSegid, iIdxLeaf);
+ }else{
+ iDlidxPrevLeaf = pSeg->pgnoLast;
+ /* TODO: Check there is no doclist index */
+ }
+
+ iIdxPrevLeaf = iIdxLeaf;
+ }
+
+ rc2 = sqlite3_finalize(pStmt);
+ if( p->rc==SQLITE_OK ) p->rc = rc2;
+
+ /* Page iter.iLeaf must now be the rightmost leaf-page in the segment */
+#if 0
+ if( p->rc==SQLITE_OK && iter.iLeaf!=pSeg->pgnoLast ){
+ p->rc = FTS5_CORRUPT;
+ }
+#endif
+}
+
+
+/*
+** Run internal checks to ensure that the FTS index (a) is internally
+** consistent and (b) contains entries for which the XOR of the checksums
+** as calculated by sqlite3Fts5IndexEntryCksum() is cksum.
+**
+** Return SQLITE_CORRUPT if any of the internal checks fail, or if the
+** checksum does not match. Return SQLITE_OK if all checks pass without
+** error, or some other SQLite error code if another error (e.g. OOM)
+** occurs.
+*/
+static int sqlite3Fts5IndexIntegrityCheck(Fts5Index *p, u64 cksum){
+ int eDetail = p->pConfig->eDetail;
+ u64 cksum2 = 0; /* Checksum based on contents of indexes */
+ Fts5Buffer poslist = {0,0,0}; /* Buffer used to hold a poslist */
+ Fts5Iter *pIter; /* Used to iterate through entire index */
+ Fts5Structure *pStruct; /* Index structure */
+
+#ifdef SQLITE_DEBUG
+ /* Used by extra internal tests only run if NDEBUG is not defined */
+ u64 cksum3 = 0; /* Checksum based on contents of indexes */
+ Fts5Buffer term = {0,0,0}; /* Buffer used to hold most recent term */
+#endif
+ const int flags = FTS5INDEX_QUERY_NOOUTPUT;
+
+ /* Load the FTS index structure */
+ pStruct = fts5StructureRead(p);
+
+ /* Check that the internal nodes of each segment match the leaves */
+ if( pStruct ){
+ int iLvl, iSeg;
+ for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
+ for(iSeg=0; iSeg<pStruct->aLevel[iLvl].nSeg; iSeg++){
+ Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
+ fts5IndexIntegrityCheckSegment(p, pSeg);
+ }
+ }
+ }
+
+ /* The cksum argument passed to this function is a checksum calculated
+ ** based on all expected entries in the FTS index (including prefix index
+ ** entries). This block checks that a checksum calculated based on the
+ ** actual contents of FTS index is identical.
+ **
+ ** Two versions of the same checksum are calculated. The first (stack
+ ** variable cksum2) based on entries extracted from the full-text index
+ ** while doing a linear scan of each individual index in turn.
+ **
+ ** As each term visited by the linear scans, a separate query for the
+ ** same term is performed. cksum3 is calculated based on the entries
+ ** extracted by these queries.
+ */
+ for(fts5MultiIterNew(p, pStruct, flags, 0, 0, 0, -1, 0, &pIter);
+ fts5MultiIterEof(p, pIter)==0;
+ fts5MultiIterNext(p, pIter, 0, 0)
+ ){
+ int n; /* Size of term in bytes */
+ i64 iPos = 0; /* Position read from poslist */
+ int iOff = 0; /* Offset within poslist */
+ i64 iRowid = fts5MultiIterRowid(pIter);
+ char *z = (char*)fts5MultiIterTerm(pIter, &n);
+
+ /* If this is a new term, query for it. Update cksum3 with the results. */
+ fts5TestTerm(p, &term, z, n, cksum2, &cksum3);
+
+ if( eDetail==FTS5_DETAIL_NONE ){
+ if( 0==fts5MultiIterIsEmpty(p, pIter) ){
+ cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, 0, 0, -1, z, n);
+ }
+ }else{
+ poslist.n = 0;
+ fts5SegiterPoslist(p, &pIter->aSeg[pIter->aFirst[1].iFirst], 0, &poslist);
+ while( 0==sqlite3Fts5PoslistNext64(poslist.p, poslist.n, &iOff, &iPos) ){
+ int iCol = FTS5_POS2COLUMN(iPos);
+ int iTokOff = FTS5_POS2OFFSET(iPos);
+ cksum2 ^= sqlite3Fts5IndexEntryCksum(iRowid, iCol, iTokOff, -1, z, n);
+ }
+ }
+ }
+ fts5TestTerm(p, &term, 0, 0, cksum2, &cksum3);
+
+ fts5MultiIterFree(pIter);
+ if( p->rc==SQLITE_OK && cksum!=cksum2 ) p->rc = FTS5_CORRUPT;
+
+ fts5StructureRelease(pStruct);
+#ifdef SQLITE_DEBUG
+ fts5BufferFree(&term);
+#endif
+ fts5BufferFree(&poslist);
+ return fts5IndexReturn(p);
+}
+
+/*************************************************************************
+**************************************************************************
+** Below this point is the implementation of the fts5_decode() scalar
+** function only.
+*/
+
+/*
+** Decode a segment-data rowid from the %_data table. This function is
+** the opposite of macro FTS5_SEGMENT_ROWID().
+*/
+static void fts5DecodeRowid(
+ i64 iRowid, /* Rowid from %_data table */
+ int *piSegid, /* OUT: Segment id */
+ int *pbDlidx, /* OUT: Dlidx flag */
+ int *piHeight, /* OUT: Height */
+ int *piPgno /* OUT: Page number */
+){
+ *piPgno = (int)(iRowid & (((i64)1 << FTS5_DATA_PAGE_B) - 1));
+ iRowid >>= FTS5_DATA_PAGE_B;
+
+ *piHeight = (int)(iRowid & (((i64)1 << FTS5_DATA_HEIGHT_B) - 1));
+ iRowid >>= FTS5_DATA_HEIGHT_B;
+
+ *pbDlidx = (int)(iRowid & 0x0001);
+ iRowid >>= FTS5_DATA_DLI_B;
+
+ *piSegid = (int)(iRowid & (((i64)1 << FTS5_DATA_ID_B) - 1));
+}
+
+static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
+ int iSegid, iHeight, iPgno, bDlidx; /* Rowid compenents */
+ fts5DecodeRowid(iKey, &iSegid, &bDlidx, &iHeight, &iPgno);
+
+ if( iSegid==0 ){
+ if( iKey==FTS5_AVERAGES_ROWID ){
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
+ }else{
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{structure}");
+ }
+ }
+ else{
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{%ssegid=%d h=%d pgno=%d}",
+ bDlidx ? "dlidx " : "", iSegid, iHeight, iPgno
+ );
+ }
+}
+
+static void fts5DebugStructure(
+ int *pRc, /* IN/OUT: error code */
+ Fts5Buffer *pBuf,
+ Fts5Structure *p
+){
+ int iLvl, iSeg; /* Iterate through levels, segments */
+
+ for(iLvl=0; iLvl<p->nLevel; iLvl++){
+ Fts5StructureLevel *pLvl = &p->aLevel[iLvl];
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf,
+ " {lvl=%d nMerge=%d nSeg=%d", iLvl, pLvl->nMerge, pLvl->nSeg
+ );
+ for(iSeg=0; iSeg<pLvl->nSeg; iSeg++){
+ Fts5StructureSegment *pSeg = &pLvl->aSeg[iSeg];
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " {id=%d leaves=%d..%d}",
+ pSeg->iSegid, pSeg->pgnoFirst, pSeg->pgnoLast
+ );
+ }
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "}");
+ }
+}
+
+/*
+** This is part of the fts5_decode() debugging aid.
+**
+** Arguments pBlob/nBlob contain a serialized Fts5Structure object. This
+** function appends a human-readable representation of the same object
+** to the buffer passed as the second argument.
+*/
+static void fts5DecodeStructure(
+ int *pRc, /* IN/OUT: error code */
+ Fts5Buffer *pBuf,
+ const u8 *pBlob, int nBlob
+){
+ int rc; /* Return code */
+ Fts5Structure *p = 0; /* Decoded structure object */
+
+ rc = fts5StructureDecode(pBlob, nBlob, 0, &p);
+ if( rc!=SQLITE_OK ){
+ *pRc = rc;
+ return;
+ }
+
+ fts5DebugStructure(pRc, pBuf, p);
+ fts5StructureRelease(p);
+}
+
+/*
+** This is part of the fts5_decode() debugging aid.
+**
+** Arguments pBlob/nBlob contain an "averages" record. This function
+** appends a human-readable representation of record to the buffer passed
+** as the second argument.
+*/
+static void fts5DecodeAverages(
+ int *pRc, /* IN/OUT: error code */
+ Fts5Buffer *pBuf,
+ const u8 *pBlob, int nBlob
+){
+ int i = 0;
+ const char *zSpace = "";
+
+ while( i<nBlob ){
+ u64 iVal;
+ i += sqlite3Fts5GetVarint(&pBlob[i], &iVal);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "%s%d", zSpace, (int)iVal);
+ zSpace = " ";
+ }
+}
+
+/*
+** Buffer (a/n) is assumed to contain a list of serialized varints. Read
+** each varint and append its string representation to buffer pBuf. Return
+** after either the input buffer is exhausted or a 0 value is read.
+**
+** The return value is the number of bytes read from the input buffer.
+*/
+static int fts5DecodePoslist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
+ int iOff = 0;
+ while( iOff<n ){
+ int iVal;
+ iOff += fts5GetVarint32(&a[iOff], iVal);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %d", iVal);
+ }
+ return iOff;
+}
+
+/*
+** The start of buffer (a/n) contains the start of a doclist. The doclist
+** may or may not finish within the buffer. This function appends a text
+** representation of the part of the doclist that is present to buffer
+** pBuf.
+**
+** The return value is the number of bytes read from the input buffer.
+*/
+static int fts5DecodeDoclist(int *pRc, Fts5Buffer *pBuf, const u8 *a, int n){
+ i64 iDocid = 0;
+ int iOff = 0;
+
+ if( n>0 ){
+ iOff = sqlite3Fts5GetVarint(a, (u64*)&iDocid);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
+ }
+ while( iOff<n ){
+ int nPos;
+ int bDel;
+ iOff += fts5GetPoslistSize(&a[iOff], &nPos, &bDel);
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " nPos=%d%s", nPos, bDel?"*":"");
+ iOff += fts5DecodePoslist(pRc, pBuf, &a[iOff], MIN(n-iOff, nPos));
+ if( iOff<n ){
+ i64 iDelta;
+ iOff += sqlite3Fts5GetVarint(&a[iOff], (u64*)&iDelta);
+ iDocid += iDelta;
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " id=%lld", iDocid);
+ }
+ }
+
+ return iOff;
+}
+
+/*
+** This function is part of the fts5_decode() debugging function. It is
+** only ever used with detail=none tables.
+**
+** Buffer (pData/nData) contains a doclist in the format used by detail=none
+** tables. This function appends a human-readable version of that list to
+** buffer pBuf.
+**
+** If *pRc is other than SQLITE_OK when this function is called, it is a
+** no-op. If an OOM or other error occurs within this function, *pRc is
+** set to an SQLite error code before returning. The final state of buffer
+** pBuf is undefined in this case.
+*/
+static void fts5DecodeRowidList(
+ int *pRc, /* IN/OUT: Error code */
+ Fts5Buffer *pBuf, /* Buffer to append text to */
+ const u8 *pData, int nData /* Data to decode list-of-rowids from */
+){
+ int i = 0;
+ i64 iRowid = 0;
+
+ while( i<nData ){
+ const char *zApp = "";
+ u64 iVal;
+ i += sqlite3Fts5GetVarint(&pData[i], &iVal);
+ iRowid += iVal;
+
+ if( i<nData && pData[i]==0x00 ){
+ i++;
+ if( i<nData && pData[i]==0x00 ){
+ i++;
+ zApp = "+";
+ }else{
+ zApp = "*";
+ }
+ }
+
+ sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
+ }
+}
+
+/*
+** The implementation of user-defined scalar function fts5_decode().
+*/
+static void fts5DecodeFunction(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args (always 2) */
+ sqlite3_value **apVal /* Function arguments */
+){
+ i64 iRowid; /* Rowid for record being decoded */
+ int iSegid,iHeight,iPgno,bDlidx;/* Rowid components */
+ const u8 *aBlob; int n; /* Record to decode */
+ u8 *a = 0;
+ Fts5Buffer s; /* Build up text to return here */
+ int rc = SQLITE_OK; /* Return code */
+ int nSpace = 0;
+ int eDetailNone = (sqlite3_user_data(pCtx)!=0);
+
+ assert( nArg==2 );
+ UNUSED_PARAM(nArg);
+ memset(&s, 0, sizeof(Fts5Buffer));
+ iRowid = sqlite3_value_int64(apVal[0]);
+
+ /* Make a copy of the second argument (a blob) in aBlob[]. The aBlob[]
+ ** copy is followed by FTS5_DATA_ZERO_PADDING 0x00 bytes, which prevents
+ ** buffer overreads even if the record is corrupt. */
+ n = sqlite3_value_bytes(apVal[1]);
+ aBlob = sqlite3_value_blob(apVal[1]);
+ nSpace = n + FTS5_DATA_ZERO_PADDING;
+ a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace);
+ if( a==0 ) goto decode_out;
+ memcpy(a, aBlob, n);
+
+
+ fts5DecodeRowid(iRowid, &iSegid, &bDlidx, &iHeight, &iPgno);
+
+ fts5DebugRowid(&rc, &s, iRowid);
+ if( bDlidx ){
+ Fts5Data dlidx;
+ Fts5DlidxLvl lvl;
+
+ dlidx.p = a;
+ dlidx.nn = n;
+
+ memset(&lvl, 0, sizeof(Fts5DlidxLvl));
+ lvl.pData = &dlidx;
+ lvl.iLeafPgno = iPgno;
+
+ for(fts5DlidxLvlNext(&lvl); lvl.bEof==0; fts5DlidxLvlNext(&lvl)){
+ sqlite3Fts5BufferAppendPrintf(&rc, &s,
+ " %d(%lld)", lvl.iLeafPgno, lvl.iRowid
+ );
+ }
+ }else if( iSegid==0 ){
+ if( iRowid==FTS5_AVERAGES_ROWID ){
+ fts5DecodeAverages(&rc, &s, a, n);
+ }else{
+ fts5DecodeStructure(&rc, &s, a, n);
+ }
+ }else if( eDetailNone ){
+ Fts5Buffer term; /* Current term read from page */
+ int szLeaf;
+ int iPgidxOff = szLeaf = fts5GetU16(&a[2]);
+ int iTermOff;
+ int nKeep = 0;
+ int iOff;
+
+ memset(&term, 0, sizeof(Fts5Buffer));
+
+ /* Decode any entries that occur before the first term. */
+ if( szLeaf<n ){
+ iPgidxOff += fts5GetVarint32(&a[iPgidxOff], iTermOff);
+ }else{
+ iTermOff = szLeaf;
+ }
+ fts5DecodeRowidList(&rc, &s, &a[4], iTermOff-4);
+
+ iOff = iTermOff;
+ while( iOff<szLeaf ){
+ int nAppend;
+
+ /* Read the term data for the next term*/
+ iOff += fts5GetVarint32(&a[iOff], nAppend);
+ term.n = nKeep;
+ fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
+ sqlite3Fts5BufferAppendPrintf(
+ &rc, &s, " term=%.*s", term.n, (const char*)term.p
+ );
+ iOff += nAppend;
+
+ /* Figure out where the doclist for this term ends */
+ if( iPgidxOff<n ){
+ int nIncr;
+ iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr);
+ iTermOff += nIncr;
+ }else{
+ iTermOff = szLeaf;
+ }
+
+ fts5DecodeRowidList(&rc, &s, &a[iOff], iTermOff-iOff);
+ iOff = iTermOff;
+ if( iOff<szLeaf ){
+ iOff += fts5GetVarint32(&a[iOff], nKeep);
+ }
+ }
+
+ fts5BufferFree(&term);
+ }else{
+ Fts5Buffer term; /* Current term read from page */
+ int szLeaf; /* Offset of pgidx in a[] */
+ int iPgidxOff;
+ int iPgidxPrev = 0; /* Previous value read from pgidx */
+ int iTermOff = 0;
+ int iRowidOff = 0;
+ int iOff;
+ int nDoclist;
+
+ memset(&term, 0, sizeof(Fts5Buffer));
+
+ if( n<4 ){
+ sqlite3Fts5BufferSet(&rc, &s, 7, (const u8*)"corrupt");
+ goto decode_out;
+ }else{
+ iRowidOff = fts5GetU16(&a[0]);
+ iPgidxOff = szLeaf = fts5GetU16(&a[2]);
+ if( iPgidxOff<n ){
+ fts5GetVarint32(&a[iPgidxOff], iTermOff);
+ }
+ }
+
+ /* Decode the position list tail at the start of the page */
+ if( iRowidOff!=0 ){
+ iOff = iRowidOff;
+ }else if( iTermOff!=0 ){
+ iOff = iTermOff;
+ }else{
+ iOff = szLeaf;
+ }
+ fts5DecodePoslist(&rc, &s, &a[4], iOff-4);
+
+ /* Decode any more doclist data that appears on the page before the
+ ** first term. */
+ nDoclist = (iTermOff ? iTermOff : szLeaf) - iOff;
+ fts5DecodeDoclist(&rc, &s, &a[iOff], nDoclist);
+
+ while( iPgidxOff<n ){
+ int bFirst = (iPgidxOff==szLeaf); /* True for first term on page */
+ int nByte; /* Bytes of data */
+ int iEnd;
+
+ iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nByte);
+ iPgidxPrev += nByte;
+ iOff = iPgidxPrev;
+
+ if( iPgidxOff<n ){
+ fts5GetVarint32(&a[iPgidxOff], nByte);
+ iEnd = iPgidxPrev + nByte;
+ }else{
+ iEnd = szLeaf;
+ }
+
+ if( bFirst==0 ){
+ iOff += fts5GetVarint32(&a[iOff], nByte);
+ term.n = nByte;
+ }
+ iOff += fts5GetVarint32(&a[iOff], nByte);
+ fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
+ iOff += nByte;
+
+ sqlite3Fts5BufferAppendPrintf(
+ &rc, &s, " term=%.*s", term.n, (const char*)term.p
+ );
+ iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
+ }
+
+ fts5BufferFree(&term);
+ }
+
+ decode_out:
+ sqlite3_free(a);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_text(pCtx, (const char*)s.p, s.n, SQLITE_TRANSIENT);
+ }else{
+ sqlite3_result_error_code(pCtx, rc);
+ }
+ fts5BufferFree(&s);
+}
+
+/*
+** The implementation of user-defined scalar function fts5_rowid().
+*/
+static void fts5RowidFunction(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args (always 2) */
+ sqlite3_value **apVal /* Function arguments */
+){
+ const char *zArg;
+ if( nArg==0 ){
+ sqlite3_result_error(pCtx, "should be: fts5_rowid(subject, ....)", -1);
+ }else{
+ zArg = (const char*)sqlite3_value_text(apVal[0]);
+ if( 0==sqlite3_stricmp(zArg, "segment") ){
+ i64 iRowid;
+ int segid, pgno;
+ if( nArg!=3 ){
+ sqlite3_result_error(pCtx,
+ "should be: fts5_rowid('segment', segid, pgno))", -1
+ );
+ }else{
+ segid = sqlite3_value_int(apVal[1]);
+ pgno = sqlite3_value_int(apVal[2]);
+ iRowid = FTS5_SEGMENT_ROWID(segid, pgno);
+ sqlite3_result_int64(pCtx, iRowid);
+ }
+ }else{
+ sqlite3_result_error(pCtx,
+ "first arg to fts5_rowid() must be 'segment'" , -1
+ );
+ }
+ }
+}
+
+/*
+** This is called as part of registering the FTS5 module with database
+** connection db. It registers several user-defined scalar functions useful
+** with FTS5.
+**
+** If successful, SQLITE_OK is returned. If an error occurs, some other
+** SQLite error code is returned instead.
+*/
+static int sqlite3Fts5IndexInit(sqlite3 *db){
+ int rc = sqlite3_create_function(
+ db, "fts5_decode", 2, SQLITE_UTF8, 0, fts5DecodeFunction, 0, 0
+ );
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5_decode_none", 2,
+ SQLITE_UTF8, (void*)db, fts5DecodeFunction, 0, 0
+ );
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5_rowid", -1, SQLITE_UTF8, 0, fts5RowidFunction, 0, 0
+ );
+ }
+ return rc;
+}
+
+
+static int sqlite3Fts5IndexReset(Fts5Index *p){
+ assert( p->pStruct==0 || p->iStructVersion!=0 );
+ if( fts5IndexDataVersion(p)!=p->iStructVersion ){
+ fts5StructureInvalidate(p);
+ }
+ return fts5IndexReturn(p);
+}
+
+/*
+** 2014 Jun 09
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This is an SQLite module implementing full-text search.
+*/
+
+
+/* #include "fts5Int.h" */
+
+/*
+** This variable is set to false when running tests for which the on disk
+** structures should not be corrupt. Otherwise, true. If it is false, extra
+** assert() conditions in the fts5 code are activated - conditions that are
+** only true if it is guaranteed that the fts5 database is not corrupt.
+*/
+SQLITE_API int sqlite3_fts5_may_be_corrupt = 1;
+
+
+typedef struct Fts5Auxdata Fts5Auxdata;
+typedef struct Fts5Auxiliary Fts5Auxiliary;
+typedef struct Fts5Cursor Fts5Cursor;
+typedef struct Fts5Sorter Fts5Sorter;
+typedef struct Fts5Table Fts5Table;
+typedef struct Fts5TokenizerModule Fts5TokenizerModule;
+
+/*
+** NOTES ON TRANSACTIONS:
+**
+** SQLite invokes the following virtual table methods as transactions are
+** opened and closed by the user:
+**
+** xBegin(): Start of a new transaction.
+** xSync(): Initial part of two-phase commit.
+** xCommit(): Final part of two-phase commit.
+** xRollback(): Rollback the transaction.
+**
+** Anything that is required as part of a commit that may fail is performed
+** in the xSync() callback. Current versions of SQLite ignore any errors
+** returned by xCommit().
+**
+** And as sub-transactions are opened/closed:
+**
+** xSavepoint(int S): Open savepoint S.
+** xRelease(int S): Commit and close savepoint S.
+** xRollbackTo(int S): Rollback to start of savepoint S.
+**
+** During a write-transaction the fts5_index.c module may cache some data
+** in-memory. It is flushed to disk whenever xSync(), xRelease() or
+** xSavepoint() is called. And discarded whenever xRollback() or xRollbackTo()
+** is called.
+**
+** Additionally, if SQLITE_DEBUG is defined, an instance of the following
+** structure is used to record the current transaction state. This information
+** is not required, but it is used in the assert() statements executed by
+** function fts5CheckTransactionState() (see below).
+*/
+struct Fts5TransactionState {
+ int eState; /* 0==closed, 1==open, 2==synced */
+ int iSavepoint; /* Number of open savepoints (0 -> none) */
+};
+
+/*
+** A single object of this type is allocated when the FTS5 module is
+** registered with a database handle. It is used to store pointers to
+** all registered FTS5 extensions - tokenizers and auxiliary functions.
+*/
+struct Fts5Global {
+ fts5_api api; /* User visible part of object (see fts5.h) */
+ sqlite3 *db; /* Associated database connection */
+ i64 iNextId; /* Used to allocate unique cursor ids */
+ Fts5Auxiliary *pAux; /* First in list of all aux. functions */
+ Fts5TokenizerModule *pTok; /* First in list of all tokenizer modules */
+ Fts5TokenizerModule *pDfltTok; /* Default tokenizer module */
+ Fts5Cursor *pCsr; /* First in list of all open cursors */
+};
+
+/*
+** Each auxiliary function registered with the FTS5 module is represented
+** by an object of the following type. All such objects are stored as part
+** of the Fts5Global.pAux list.
+*/
+struct Fts5Auxiliary {
+ Fts5Global *pGlobal; /* Global context for this function */
+ char *zFunc; /* Function name (nul-terminated) */
+ void *pUserData; /* User-data pointer */
+ fts5_extension_function xFunc; /* Callback function */
+ void (*xDestroy)(void*); /* Destructor function */
+ Fts5Auxiliary *pNext; /* Next registered auxiliary function */
+};
+
+/*
+** Each tokenizer module registered with the FTS5 module is represented
+** by an object of the following type. All such objects are stored as part
+** of the Fts5Global.pTok list.
+*/
+struct Fts5TokenizerModule {
+ char *zName; /* Name of tokenizer */
+ void *pUserData; /* User pointer passed to xCreate() */
+ fts5_tokenizer x; /* Tokenizer functions */
+ void (*xDestroy)(void*); /* Destructor function */
+ Fts5TokenizerModule *pNext; /* Next registered tokenizer module */
+};
+
+/*
+** Virtual-table object.
+*/
+struct Fts5Table {
+ sqlite3_vtab base; /* Base class used by SQLite core */
+ Fts5Config *pConfig; /* Virtual table configuration */
+ Fts5Index *pIndex; /* Full-text index */
+ Fts5Storage *pStorage; /* Document store */
+ Fts5Global *pGlobal; /* Global (connection wide) data */
+ Fts5Cursor *pSortCsr; /* Sort data from this cursor */
+#ifdef SQLITE_DEBUG
+ struct Fts5TransactionState ts;
+#endif
+};
+
+struct Fts5MatchPhrase {
+ Fts5Buffer *pPoslist; /* Pointer to current poslist */
+ int nTerm; /* Size of phrase in terms */
+};
+
+/*
+** pStmt:
+** SELECT rowid, <fts> FROM <fts> ORDER BY +rank;
+**
+** aIdx[]:
+** There is one entry in the aIdx[] array for each phrase in the query,
+** the value of which is the offset within aPoslist[] following the last
+** byte of the position list for the corresponding phrase.
+*/
+struct Fts5Sorter {
+ sqlite3_stmt *pStmt;
+ i64 iRowid; /* Current rowid */
+ const u8 *aPoslist; /* Position lists for current row */
+ int nIdx; /* Number of entries in aIdx[] */
+ int aIdx[1]; /* Offsets into aPoslist for current row */
+};
+
+
+/*
+** Virtual-table cursor object.
+**
+** iSpecial:
+** If this is a 'special' query (refer to function fts5SpecialMatch()),
+** then this variable contains the result of the query.
+**
+** iFirstRowid, iLastRowid:
+** These variables are only used for FTS5_PLAN_MATCH cursors. Assuming the
+** cursor iterates in ascending order of rowids, iFirstRowid is the lower
+** limit of rowids to return, and iLastRowid the upper. In other words, the
+** WHERE clause in the user's query might have been:
+**
+** <tbl> MATCH <expr> AND rowid BETWEEN $iFirstRowid AND $iLastRowid
+**
+** If the cursor iterates in descending order of rowid, iFirstRowid
+** is the upper limit (i.e. the "first" rowid visited) and iLastRowid
+** the lower.
+*/
+struct Fts5Cursor {
+ sqlite3_vtab_cursor base; /* Base class used by SQLite core */
+ Fts5Cursor *pNext; /* Next cursor in Fts5Cursor.pCsr list */
+ int *aColumnSize; /* Values for xColumnSize() */
+ i64 iCsrId; /* Cursor id */
+
+ /* Zero from this point onwards on cursor reset */
+ int ePlan; /* FTS5_PLAN_XXX value */
+ int bDesc; /* True for "ORDER BY rowid DESC" queries */
+ i64 iFirstRowid; /* Return no rowids earlier than this */
+ i64 iLastRowid; /* Return no rowids later than this */
+ sqlite3_stmt *pStmt; /* Statement used to read %_content */
+ Fts5Expr *pExpr; /* Expression for MATCH queries */
+ Fts5Sorter *pSorter; /* Sorter for "ORDER BY rank" queries */
+ int csrflags; /* Mask of cursor flags (see below) */
+ i64 iSpecial; /* Result of special query */
+
+ /* "rank" function. Populated on demand from vtab.xColumn(). */
+ char *zRank; /* Custom rank function */
+ char *zRankArgs; /* Custom rank function args */
+ Fts5Auxiliary *pRank; /* Rank callback (or NULL) */
+ int nRankArg; /* Number of trailing arguments for rank() */
+ sqlite3_value **apRankArg; /* Array of trailing arguments */
+ sqlite3_stmt *pRankArgStmt; /* Origin of objects in apRankArg[] */
+
+ /* Auxiliary data storage */
+ Fts5Auxiliary *pAux; /* Currently executing extension function */
+ Fts5Auxdata *pAuxdata; /* First in linked list of saved aux-data */
+
+ /* Cache used by auxiliary functions xInst() and xInstCount() */
+ Fts5PoslistReader *aInstIter; /* One for each phrase */
+ int nInstAlloc; /* Size of aInst[] array (entries / 3) */
+ int nInstCount; /* Number of phrase instances */
+ int *aInst; /* 3 integers per phrase instance */
+};
+
+/*
+** Bits that make up the "idxNum" parameter passed indirectly by
+** xBestIndex() to xFilter().
+*/
+#define FTS5_BI_MATCH 0x0001 /* <tbl> MATCH ? */
+#define FTS5_BI_RANK 0x0002 /* rank MATCH ? */
+#define FTS5_BI_ROWID_EQ 0x0004 /* rowid == ? */
+#define FTS5_BI_ROWID_LE 0x0008 /* rowid <= ? */
+#define FTS5_BI_ROWID_GE 0x0010 /* rowid >= ? */
+
+#define FTS5_BI_ORDER_RANK 0x0020
+#define FTS5_BI_ORDER_ROWID 0x0040
+#define FTS5_BI_ORDER_DESC 0x0080
+
+/*
+** Values for Fts5Cursor.csrflags
+*/
+#define FTS5CSR_EOF 0x01
+#define FTS5CSR_REQUIRE_CONTENT 0x02
+#define FTS5CSR_REQUIRE_DOCSIZE 0x04
+#define FTS5CSR_REQUIRE_INST 0x08
+#define FTS5CSR_FREE_ZRANK 0x10
+#define FTS5CSR_REQUIRE_RESEEK 0x20
+#define FTS5CSR_REQUIRE_POSLIST 0x40
+
+#define BitFlagAllTest(x,y) (((x) & (y))==(y))
+#define BitFlagTest(x,y) (((x) & (y))!=0)
+
+
+/*
+** Macros to Set(), Clear() and Test() cursor flags.
+*/
+#define CsrFlagSet(pCsr, flag) ((pCsr)->csrflags |= (flag))
+#define CsrFlagClear(pCsr, flag) ((pCsr)->csrflags &= ~(flag))
+#define CsrFlagTest(pCsr, flag) ((pCsr)->csrflags & (flag))
+
+struct Fts5Auxdata {
+ Fts5Auxiliary *pAux; /* Extension to which this belongs */
+ void *pPtr; /* Pointer value */
+ void(*xDelete)(void*); /* Destructor */
+ Fts5Auxdata *pNext; /* Next object in linked list */
+};
+
+#ifdef SQLITE_DEBUG
+#define FTS5_BEGIN 1
+#define FTS5_SYNC 2
+#define FTS5_COMMIT 3
+#define FTS5_ROLLBACK 4
+#define FTS5_SAVEPOINT 5
+#define FTS5_RELEASE 6
+#define FTS5_ROLLBACKTO 7
+static void fts5CheckTransactionState(Fts5Table *p, int op, int iSavepoint){
+ switch( op ){
+ case FTS5_BEGIN:
+ assert( p->ts.eState==0 );
+ p->ts.eState = 1;
+ p->ts.iSavepoint = -1;
+ break;
+
+ case FTS5_SYNC:
+ assert( p->ts.eState==1 );
+ p->ts.eState = 2;
+ break;
+
+ case FTS5_COMMIT:
+ assert( p->ts.eState==2 );
+ p->ts.eState = 0;
+ break;
+
+ case FTS5_ROLLBACK:
+ assert( p->ts.eState==1 || p->ts.eState==2 || p->ts.eState==0 );
+ p->ts.eState = 0;
+ break;
+
+ case FTS5_SAVEPOINT:
+ assert( p->ts.eState==1 );
+ assert( iSavepoint>=0 );
+ assert( iSavepoint>p->ts.iSavepoint );
+ p->ts.iSavepoint = iSavepoint;
+ break;
+
+ case FTS5_RELEASE:
+ assert( p->ts.eState==1 );
+ assert( iSavepoint>=0 );
+ assert( iSavepoint<=p->ts.iSavepoint );
+ p->ts.iSavepoint = iSavepoint-1;
+ break;
+
+ case FTS5_ROLLBACKTO:
+ assert( p->ts.eState==1 );
+ assert( iSavepoint>=0 );
+ assert( iSavepoint<=p->ts.iSavepoint );
+ p->ts.iSavepoint = iSavepoint;
+ break;
+ }
+}
+#else
+# define fts5CheckTransactionState(x,y,z)
+#endif
+
+/*
+** Return true if pTab is a contentless table.
+*/
+static int fts5IsContentless(Fts5Table *pTab){
+ return pTab->pConfig->eContent==FTS5_CONTENT_NONE;
+}
+
+/*
+** Delete a virtual table handle allocated by fts5InitVtab().
+*/
+static void fts5FreeVtab(Fts5Table *pTab){
+ if( pTab ){
+ sqlite3Fts5IndexClose(pTab->pIndex);
+ sqlite3Fts5StorageClose(pTab->pStorage);
+ sqlite3Fts5ConfigFree(pTab->pConfig);
+ sqlite3_free(pTab);
+ }
+}
+
+/*
+** The xDisconnect() virtual table method.
+*/
+static int fts5DisconnectMethod(sqlite3_vtab *pVtab){
+ fts5FreeVtab((Fts5Table*)pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** The xDestroy() virtual table method.
+*/
+static int fts5DestroyMethod(sqlite3_vtab *pVtab){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ int rc = sqlite3Fts5DropAll(pTab->pConfig);
+ if( rc==SQLITE_OK ){
+ fts5FreeVtab((Fts5Table*)pVtab);
+ }
+ return rc;
+}
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the FTS3 virtual table.
+**
+** The argv[] array contains the following:
+**
+** argv[0] -> module name ("fts5")
+** argv[1] -> database name
+** argv[2] -> table name
+** argv[...] -> "column name" and other module argument fields.
+*/
+static int fts5InitVtab(
+ int bCreate, /* True for xCreate, false for xConnect */
+ sqlite3 *db, /* The SQLite database connection */
+ void *pAux, /* Hash table containing tokenizers */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
+ char **pzErr /* Write any error message here */
+){
+ Fts5Global *pGlobal = (Fts5Global*)pAux;
+ const char **azConfig = (const char**)argv;
+ int rc = SQLITE_OK; /* Return code */
+ Fts5Config *pConfig = 0; /* Results of parsing argc/argv */
+ Fts5Table *pTab = 0; /* New virtual table object */
+
+ /* Allocate the new vtab object and parse the configuration */
+ pTab = (Fts5Table*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Table));
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ConfigParse(pGlobal, db, argc, azConfig, &pConfig, pzErr);
+ assert( (rc==SQLITE_OK && *pzErr==0) || pConfig==0 );
+ }
+ if( rc==SQLITE_OK ){
+ pTab->pConfig = pConfig;
+ pTab->pGlobal = pGlobal;
+ }
+
+ /* Open the index sub-system */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexOpen(pConfig, bCreate, &pTab->pIndex, pzErr);
+ }
+
+ /* Open the storage sub-system */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageOpen(
+ pConfig, pTab->pIndex, bCreate, &pTab->pStorage, pzErr
+ );
+ }
+
+ /* Call sqlite3_declare_vtab() */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ConfigDeclareVtab(pConfig);
+ }
+
+ /* Load the initial configuration */
+ if( rc==SQLITE_OK ){
+ assert( pConfig->pzErrmsg==0 );
+ pConfig->pzErrmsg = pzErr;
+ rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
+ sqlite3Fts5IndexRollback(pTab->pIndex);
+ pConfig->pzErrmsg = 0;
+ }
+
+ if( rc!=SQLITE_OK ){
+ fts5FreeVtab(pTab);
+ pTab = 0;
+ }else if( bCreate ){
+ fts5CheckTransactionState(pTab, FTS5_BEGIN, 0);
+ }
+ *ppVTab = (sqlite3_vtab*)pTab;
+ return rc;
+}
+
+/*
+** The xConnect() and xCreate() methods for the virtual table. All the
+** work is done in function fts5InitVtab().
+*/
+static int fts5ConnectMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr);
+}
+static int fts5CreateMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr);
+}
+
+/*
+** The different query plans.
+*/
+#define FTS5_PLAN_MATCH 1 /* (<tbl> MATCH ?) */
+#define FTS5_PLAN_SOURCE 2 /* A source cursor for SORTED_MATCH */
+#define FTS5_PLAN_SPECIAL 3 /* An internal query */
+#define FTS5_PLAN_SORTED_MATCH 4 /* (<tbl> MATCH ? ORDER BY rank) */
+#define FTS5_PLAN_SCAN 5 /* No usable constraint */
+#define FTS5_PLAN_ROWID 6 /* (rowid = ?) */
+
+/*
+** Set the SQLITE_INDEX_SCAN_UNIQUE flag in pIdxInfo->flags. Unless this
+** extension is currently being used by a version of SQLite too old to
+** support index-info flags. In that case this function is a no-op.
+*/
+static void fts5SetUniqueFlag(sqlite3_index_info *pIdxInfo){
+#if SQLITE_VERSION_NUMBER>=3008012
+#ifndef SQLITE_CORE
+ if( sqlite3_libversion_number()>=3008012 )
+#endif
+ {
+ pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
+ }
+#endif
+}
+
+/*
+** Implementation of the xBestIndex method for FTS5 tables. Within the
+** WHERE constraint, it searches for the following:
+**
+** 1. A MATCH constraint against the special column.
+** 2. A MATCH constraint against the "rank" column.
+** 3. An == constraint against the rowid column.
+** 4. A < or <= constraint against the rowid column.
+** 5. A > or >= constraint against the rowid column.
+**
+** Within the ORDER BY, either:
+**
+** 5. ORDER BY rank [ASC|DESC]
+** 6. ORDER BY rowid [ASC|DESC]
+**
+** Costs are assigned as follows:
+**
+** a) If an unusable MATCH operator is present in the WHERE clause, the
+** cost is unconditionally set to 1e50 (a really big number).
+**
+** a) If a MATCH operator is present, the cost depends on the other
+** constraints also present. As follows:
+**
+** * No other constraints: cost=1000.0
+** * One rowid range constraint: cost=750.0
+** * Both rowid range constraints: cost=500.0
+** * An == rowid constraint: cost=100.0
+**
+** b) Otherwise, if there is no MATCH:
+**
+** * No other constraints: cost=1000000.0
+** * One rowid range constraint: cost=750000.0
+** * Both rowid range constraints: cost=250000.0
+** * An == rowid constraint: cost=10.0
+**
+** Costs are not modified by the ORDER BY clause.
+*/
+static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
+ Fts5Table *pTab = (Fts5Table*)pVTab;
+ Fts5Config *pConfig = pTab->pConfig;
+ int idxFlags = 0; /* Parameter passed through to xFilter() */
+ int bHasMatch;
+ int iNext;
+ int i;
+
+ struct Constraint {
+ int op; /* Mask against sqlite3_index_constraint.op */
+ int fts5op; /* FTS5 mask for idxFlags */
+ int iCol; /* 0==rowid, 1==tbl, 2==rank */
+ int omit; /* True to omit this if found */
+ int iConsIndex; /* Index in pInfo->aConstraint[] */
+ } aConstraint[] = {
+ {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
+ FTS5_BI_MATCH, 1, 1, -1},
+ {SQLITE_INDEX_CONSTRAINT_MATCH|SQLITE_INDEX_CONSTRAINT_EQ,
+ FTS5_BI_RANK, 2, 1, -1},
+ {SQLITE_INDEX_CONSTRAINT_EQ, FTS5_BI_ROWID_EQ, 0, 0, -1},
+ {SQLITE_INDEX_CONSTRAINT_LT|SQLITE_INDEX_CONSTRAINT_LE,
+ FTS5_BI_ROWID_LE, 0, 0, -1},
+ {SQLITE_INDEX_CONSTRAINT_GT|SQLITE_INDEX_CONSTRAINT_GE,
+ FTS5_BI_ROWID_GE, 0, 0, -1},
+ };
+
+ int aColMap[3];
+ aColMap[0] = -1;
+ aColMap[1] = pConfig->nCol;
+ aColMap[2] = pConfig->nCol+1;
+
+ /* Set idxFlags flags for all WHERE clause terms that will be used. */
+ for(i=0; i<pInfo->nConstraint; i++){
+ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
+ int j;
+ for(j=0; j<ArraySize(aConstraint); j++){
+ struct Constraint *pC = &aConstraint[j];
+ if( p->iColumn==aColMap[pC->iCol] && p->op & pC->op ){
+ if( p->usable ){
+ pC->iConsIndex = i;
+ idxFlags |= pC->fts5op;
+ }else if( j==0 ){
+ /* As there exists an unusable MATCH constraint this is an
+ ** unusable plan. Set a prohibitively high cost. */
+ pInfo->estimatedCost = 1e50;
+ return SQLITE_OK;
+ }
+ }
+ }
+ }
+
+ /* Set idxFlags flags for the ORDER BY clause */
+ if( pInfo->nOrderBy==1 ){
+ int iSort = pInfo->aOrderBy[0].iColumn;
+ if( iSort==(pConfig->nCol+1) && BitFlagTest(idxFlags, FTS5_BI_MATCH) ){
+ idxFlags |= FTS5_BI_ORDER_RANK;
+ }else if( iSort==-1 ){
+ idxFlags |= FTS5_BI_ORDER_ROWID;
+ }
+ if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
+ pInfo->orderByConsumed = 1;
+ if( pInfo->aOrderBy[0].desc ){
+ idxFlags |= FTS5_BI_ORDER_DESC;
+ }
+ }
+ }
+
+ /* Calculate the estimated cost based on the flags set in idxFlags. */
+ bHasMatch = BitFlagTest(idxFlags, FTS5_BI_MATCH);
+ if( BitFlagTest(idxFlags, FTS5_BI_ROWID_EQ) ){
+ pInfo->estimatedCost = bHasMatch ? 100.0 : 10.0;
+ if( bHasMatch==0 ) fts5SetUniqueFlag(pInfo);
+ }else if( BitFlagAllTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
+ pInfo->estimatedCost = bHasMatch ? 500.0 : 250000.0;
+ }else if( BitFlagTest(idxFlags, FTS5_BI_ROWID_LE|FTS5_BI_ROWID_GE) ){
+ pInfo->estimatedCost = bHasMatch ? 750.0 : 750000.0;
+ }else{
+ pInfo->estimatedCost = bHasMatch ? 1000.0 : 1000000.0;
+ }
+
+ /* Assign argvIndex values to each constraint in use. */
+ iNext = 1;
+ for(i=0; i<ArraySize(aConstraint); i++){
+ struct Constraint *pC = &aConstraint[i];
+ if( pC->iConsIndex>=0 ){
+ pInfo->aConstraintUsage[pC->iConsIndex].argvIndex = iNext++;
+ pInfo->aConstraintUsage[pC->iConsIndex].omit = (unsigned char)pC->omit;
+ }
+ }
+
+ pInfo->idxNum = idxFlags;
+ return SQLITE_OK;
+}
+
+static int fts5NewTransaction(Fts5Table *pTab){
+ Fts5Cursor *pCsr;
+ for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
+ if( pCsr->base.pVtab==(sqlite3_vtab*)pTab ) return SQLITE_OK;
+ }
+ return sqlite3Fts5StorageReset(pTab->pStorage);
+}
+
+/*
+** Implementation of xOpen method.
+*/
+static int fts5OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){
+ Fts5Table *pTab = (Fts5Table*)pVTab;
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Cursor *pCsr = 0; /* New cursor object */
+ int nByte; /* Bytes of space to allocate */
+ int rc; /* Return code */
+
+ rc = fts5NewTransaction(pTab);
+ if( rc==SQLITE_OK ){
+ nByte = sizeof(Fts5Cursor) + pConfig->nCol * sizeof(int);
+ pCsr = (Fts5Cursor*)sqlite3_malloc(nByte);
+ if( pCsr ){
+ Fts5Global *pGlobal = pTab->pGlobal;
+ memset(pCsr, 0, nByte);
+ pCsr->aColumnSize = (int*)&pCsr[1];
+ pCsr->pNext = pGlobal->pCsr;
+ pGlobal->pCsr = pCsr;
+ pCsr->iCsrId = ++pGlobal->iNextId;
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+ *ppCsr = (sqlite3_vtab_cursor*)pCsr;
+ return rc;
+}
+
+static int fts5StmtType(Fts5Cursor *pCsr){
+ if( pCsr->ePlan==FTS5_PLAN_SCAN ){
+ return (pCsr->bDesc) ? FTS5_STMT_SCAN_DESC : FTS5_STMT_SCAN_ASC;
+ }
+ return FTS5_STMT_LOOKUP;
+}
+
+/*
+** This function is called after the cursor passed as the only argument
+** is moved to point at a different row. It clears all cached data
+** specific to the previous row stored by the cursor object.
+*/
+static void fts5CsrNewrow(Fts5Cursor *pCsr){
+ CsrFlagSet(pCsr,
+ FTS5CSR_REQUIRE_CONTENT
+ | FTS5CSR_REQUIRE_DOCSIZE
+ | FTS5CSR_REQUIRE_INST
+ | FTS5CSR_REQUIRE_POSLIST
+ );
+}
+
+static void fts5FreeCursorComponents(Fts5Cursor *pCsr){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5Auxdata *pData;
+ Fts5Auxdata *pNext;
+
+ sqlite3_free(pCsr->aInstIter);
+ sqlite3_free(pCsr->aInst);
+ if( pCsr->pStmt ){
+ int eStmt = fts5StmtType(pCsr);
+ sqlite3Fts5StorageStmtRelease(pTab->pStorage, eStmt, pCsr->pStmt);
+ }
+ if( pCsr->pSorter ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ sqlite3_finalize(pSorter->pStmt);
+ sqlite3_free(pSorter);
+ }
+
+ if( pCsr->ePlan!=FTS5_PLAN_SOURCE ){
+ sqlite3Fts5ExprFree(pCsr->pExpr);
+ }
+
+ for(pData=pCsr->pAuxdata; pData; pData=pNext){
+ pNext = pData->pNext;
+ if( pData->xDelete ) pData->xDelete(pData->pPtr);
+ sqlite3_free(pData);
+ }
+
+ sqlite3_finalize(pCsr->pRankArgStmt);
+ sqlite3_free(pCsr->apRankArg);
+
+ if( CsrFlagTest(pCsr, FTS5CSR_FREE_ZRANK) ){
+ sqlite3_free(pCsr->zRank);
+ sqlite3_free(pCsr->zRankArgs);
+ }
+
+ memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan - (u8*)pCsr));
+}
+
+
+/*
+** Close the cursor. For additional information see the documentation
+** on the xClose method of the virtual table interface.
+*/
+static int fts5CloseMethod(sqlite3_vtab_cursor *pCursor){
+ if( pCursor ){
+ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ Fts5Cursor **pp;
+
+ fts5FreeCursorComponents(pCsr);
+ /* Remove the cursor from the Fts5Global.pCsr list */
+ for(pp=&pTab->pGlobal->pCsr; (*pp)!=pCsr; pp=&(*pp)->pNext);
+ *pp = pCsr->pNext;
+
+ sqlite3_free(pCsr);
+ }
+ return SQLITE_OK;
+}
+
+static int fts5SorterNext(Fts5Cursor *pCsr){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int rc;
+
+ rc = sqlite3_step(pSorter->pStmt);
+ if( rc==SQLITE_DONE ){
+ rc = SQLITE_OK;
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ }else if( rc==SQLITE_ROW ){
+ const u8 *a;
+ const u8 *aBlob;
+ int nBlob;
+ int i;
+ int iOff = 0;
+ rc = SQLITE_OK;
+
+ pSorter->iRowid = sqlite3_column_int64(pSorter->pStmt, 0);
+ nBlob = sqlite3_column_bytes(pSorter->pStmt, 1);
+ aBlob = a = sqlite3_column_blob(pSorter->pStmt, 1);
+
+ /* nBlob==0 in detail=none mode. */
+ if( nBlob>0 ){
+ for(i=0; i<(pSorter->nIdx-1); i++){
+ int iVal;
+ a += fts5GetVarint32(a, iVal);
+ iOff += iVal;
+ pSorter->aIdx[i] = iOff;
+ }
+ pSorter->aIdx[i] = &aBlob[nBlob] - a;
+ pSorter->aPoslist = a;
+ }
+
+ fts5CsrNewrow(pCsr);
+ }
+
+ return rc;
+}
+
+
+/*
+** Set the FTS5CSR_REQUIRE_RESEEK flag on all FTS5_PLAN_MATCH cursors
+** open on table pTab.
+*/
+static void fts5TripCursors(Fts5Table *pTab){
+ Fts5Cursor *pCsr;
+ for(pCsr=pTab->pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
+ if( pCsr->ePlan==FTS5_PLAN_MATCH
+ && pCsr->base.pVtab==(sqlite3_vtab*)pTab
+ ){
+ CsrFlagSet(pCsr, FTS5CSR_REQUIRE_RESEEK);
+ }
+ }
+}
+
+/*
+** If the REQUIRE_RESEEK flag is set on the cursor passed as the first
+** argument, close and reopen all Fts5IndexIter iterators that the cursor
+** is using. Then attempt to move the cursor to a rowid equal to or laster
+** (in the cursors sort order - ASC or DESC) than the current rowid.
+**
+** If the new rowid is not equal to the old, set output parameter *pbSkip
+** to 1 before returning. Otherwise, leave it unchanged.
+**
+** Return SQLITE_OK if successful or if no reseek was required, or an
+** error code if an error occurred.
+*/
+static int fts5CursorReseek(Fts5Cursor *pCsr, int *pbSkip){
+ int rc = SQLITE_OK;
+ assert( *pbSkip==0 );
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ int bDesc = pCsr->bDesc;
+ i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
+
+ rc = sqlite3Fts5ExprFirst(pCsr->pExpr, pTab->pIndex, iRowid, bDesc);
+ if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
+ *pbSkip = 1;
+ }
+
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
+ fts5CsrNewrow(pCsr);
+ if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ *pbSkip = 1;
+ }
+ }
+ return rc;
+}
+
+
+/*
+** Advance the cursor to the next row in the table that matches the
+** search criteria.
+**
+** Return SQLITE_OK if nothing goes wrong. SQLITE_OK is returned
+** even if we reach end-of-file. The fts5EofMethod() will be called
+** subsequently to determine whether or not an EOF was hit.
+*/
+static int fts5NextMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int rc;
+
+ assert( (pCsr->ePlan<3)==
+ (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
+ );
+ assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );
+
+ if( pCsr->ePlan<3 ){
+ int bSkip = 0;
+ if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
+ rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
+ CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
+ fts5CsrNewrow(pCsr);
+ }else{
+ switch( pCsr->ePlan ){
+ case FTS5_PLAN_SPECIAL: {
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ rc = SQLITE_OK;
+ break;
+ }
+
+ case FTS5_PLAN_SORTED_MATCH: {
+ rc = fts5SorterNext(pCsr);
+ break;
+ }
+
+ default:
+ rc = sqlite3_step(pCsr->pStmt);
+ if( rc!=SQLITE_ROW ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ rc = sqlite3_reset(pCsr->pStmt);
+ }else{
+ rc = SQLITE_OK;
+ }
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+static int fts5PrepareStatement(
+ sqlite3_stmt **ppStmt,
+ Fts5Config *pConfig,
+ const char *zFmt,
+ ...
+){
+ sqlite3_stmt *pRet = 0;
+ int rc;
+ char *zSql;
+ va_list ap;
+
+ va_start(ap, zFmt);
+ zSql = sqlite3_vmprintf(zFmt, ap);
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pRet, 0);
+ if( rc!=SQLITE_OK ){
+ *pConfig->pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(pConfig->db));
+ }
+ sqlite3_free(zSql);
+ }
+
+ va_end(ap);
+ *ppStmt = pRet;
+ return rc;
+}
+
+static int fts5CursorFirstSorted(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Sorter *pSorter;
+ int nPhrase;
+ int nByte;
+ int rc;
+ const char *zRank = pCsr->zRank;
+ const char *zRankArgs = pCsr->zRankArgs;
+
+ nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+ nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
+ pSorter = (Fts5Sorter*)sqlite3_malloc(nByte);
+ if( pSorter==0 ) return SQLITE_NOMEM;
+ memset(pSorter, 0, nByte);
+ pSorter->nIdx = nPhrase;
+
+ /* TODO: It would be better to have some system for reusing statement
+ ** handles here, rather than preparing a new one for each query. But that
+ ** is not possible as SQLite reference counts the virtual table objects.
+ ** And since the statement required here reads from this very virtual
+ ** table, saving it creates a circular reference.
+ **
+ ** If SQLite a built-in statement cache, this wouldn't be a problem. */
+ rc = fts5PrepareStatement(&pSorter->pStmt, pConfig,
+ "SELECT rowid, rank FROM %Q.%Q ORDER BY %s(%s%s%s) %s",
+ pConfig->zDb, pConfig->zName, zRank, pConfig->zName,
+ (zRankArgs ? ", " : ""),
+ (zRankArgs ? zRankArgs : ""),
+ bDesc ? "DESC" : "ASC"
+ );
+
+ pCsr->pSorter = pSorter;
+ if( rc==SQLITE_OK ){
+ assert( pTab->pSortCsr==0 );
+ pTab->pSortCsr = pCsr;
+ rc = fts5SorterNext(pCsr);
+ pTab->pSortCsr = 0;
+ }
+
+ if( rc!=SQLITE_OK ){
+ sqlite3_finalize(pSorter->pStmt);
+ sqlite3_free(pSorter);
+ pCsr->pSorter = 0;
+ }
+
+ return rc;
+}
+
+static int fts5CursorFirst(Fts5Table *pTab, Fts5Cursor *pCsr, int bDesc){
+ int rc;
+ Fts5Expr *pExpr = pCsr->pExpr;
+ rc = sqlite3Fts5ExprFirst(pExpr, pTab->pIndex, pCsr->iFirstRowid, bDesc);
+ if( sqlite3Fts5ExprEof(pExpr) ){
+ CsrFlagSet(pCsr, FTS5CSR_EOF);
+ }
+ fts5CsrNewrow(pCsr);
+ return rc;
+}
+
+/*
+** Process a "special" query. A special query is identified as one with a
+** MATCH expression that begins with a '*' character. The remainder of
+** the text passed to the MATCH operator are used as the special query
+** parameters.
+*/
+static int fts5SpecialMatch(
+ Fts5Table *pTab,
+ Fts5Cursor *pCsr,
+ const char *zQuery
+){
+ int rc = SQLITE_OK; /* Return code */
+ const char *z = zQuery; /* Special query text */
+ int n; /* Number of bytes in text at z */
+
+ while( z[0]==' ' ) z++;
+ for(n=0; z[n] && z[n]!=' '; n++);
+
+ assert( pTab->base.zErrMsg==0 );
+ pCsr->ePlan = FTS5_PLAN_SPECIAL;
+
+ if( 0==sqlite3_strnicmp("reads", z, n) ){
+ pCsr->iSpecial = sqlite3Fts5IndexReads(pTab->pIndex);
+ }
+ else if( 0==sqlite3_strnicmp("id", z, n) ){
+ pCsr->iSpecial = pCsr->iCsrId;
+ }
+ else{
+ /* An unrecognized directive. Return an error message. */
+ pTab->base.zErrMsg = sqlite3_mprintf("unknown special query: %.*s", n, z);
+ rc = SQLITE_ERROR;
+ }
+
+ return rc;
+}
+
+/*
+** Search for an auxiliary function named zName that can be used with table
+** pTab. If one is found, return a pointer to the corresponding Fts5Auxiliary
+** structure. Otherwise, if no such function exists, return NULL.
+*/
+static Fts5Auxiliary *fts5FindAuxiliary(Fts5Table *pTab, const char *zName){
+ Fts5Auxiliary *pAux;
+
+ for(pAux=pTab->pGlobal->pAux; pAux; pAux=pAux->pNext){
+ if( sqlite3_stricmp(zName, pAux->zFunc)==0 ) return pAux;
+ }
+
+ /* No function of the specified name was found. Return 0. */
+ return 0;
+}
+
+
+static int fts5FindRankFunction(Fts5Cursor *pCsr){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ int rc = SQLITE_OK;
+ Fts5Auxiliary *pAux = 0;
+ const char *zRank = pCsr->zRank;
+ const char *zRankArgs = pCsr->zRankArgs;
+
+ if( zRankArgs ){
+ char *zSql = sqlite3Fts5Mprintf(&rc, "SELECT %s", zRankArgs);
+ if( zSql ){
+ sqlite3_stmt *pStmt = 0;
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pStmt, 0);
+ sqlite3_free(zSql);
+ assert( rc==SQLITE_OK || pCsr->pRankArgStmt==0 );
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pStmt) ){
+ int nByte;
+ pCsr->nRankArg = sqlite3_column_count(pStmt);
+ nByte = sizeof(sqlite3_value*)*pCsr->nRankArg;
+ pCsr->apRankArg = (sqlite3_value**)sqlite3Fts5MallocZero(&rc, nByte);
+ if( rc==SQLITE_OK ){
+ int i;
+ for(i=0; i<pCsr->nRankArg; i++){
+ pCsr->apRankArg[i] = sqlite3_column_value(pStmt, i);
+ }
+ }
+ pCsr->pRankArgStmt = pStmt;
+ }else{
+ rc = sqlite3_finalize(pStmt);
+ assert( rc!=SQLITE_OK );
+ }
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ pAux = fts5FindAuxiliary(pTab, zRank);
+ if( pAux==0 ){
+ assert( pTab->base.zErrMsg==0 );
+ pTab->base.zErrMsg = sqlite3_mprintf("no such function: %s", zRank);
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ pCsr->pRank = pAux;
+ return rc;
+}
+
+
+static int fts5CursorParseRank(
+ Fts5Config *pConfig,
+ Fts5Cursor *pCsr,
+ sqlite3_value *pRank
+){
+ int rc = SQLITE_OK;
+ if( pRank ){
+ const char *z = (const char*)sqlite3_value_text(pRank);
+ char *zRank = 0;
+ char *zRankArgs = 0;
+
+ if( z==0 ){
+ if( sqlite3_value_type(pRank)==SQLITE_NULL ) rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5ConfigParseRank(z, &zRank, &zRankArgs);
+ }
+ if( rc==SQLITE_OK ){
+ pCsr->zRank = zRank;
+ pCsr->zRankArgs = zRankArgs;
+ CsrFlagSet(pCsr, FTS5CSR_FREE_ZRANK);
+ }else if( rc==SQLITE_ERROR ){
+ pCsr->base.pVtab->zErrMsg = sqlite3_mprintf(
+ "parse error in rank function: %s", z
+ );
+ }
+ }else{
+ if( pConfig->zRank ){
+ pCsr->zRank = (char*)pConfig->zRank;
+ pCsr->zRankArgs = (char*)pConfig->zRankArgs;
+ }else{
+ pCsr->zRank = (char*)FTS5_DEFAULT_RANK;
+ pCsr->zRankArgs = 0;
+ }
+ }
+ return rc;
+}
+
+static i64 fts5GetRowidLimit(sqlite3_value *pVal, i64 iDefault){
+ if( pVal ){
+ int eType = sqlite3_value_numeric_type(pVal);
+ if( eType==SQLITE_INTEGER ){
+ return sqlite3_value_int64(pVal);
+ }
+ }
+ return iDefault;
+}
+
+/*
+** This is the xFilter interface for the virtual table. See
+** the virtual table xFilter method documentation for additional
+** information.
+**
+** There are three possible query strategies:
+**
+** 1. Full-text search using a MATCH operator.
+** 2. A by-rowid lookup.
+** 3. A full-table scan.
+*/
+static int fts5FilterMethod(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, /* Strategy index */
+ const char *zUnused, /* Unused */
+ int nVal, /* Number of elements in apVal */
+ sqlite3_value **apVal /* Arguments for the indexing scheme */
+){
+ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int rc = SQLITE_OK; /* Error code */
+ int iVal = 0; /* Counter for apVal[] */
+ int bDesc; /* True if ORDER BY [rank|rowid] DESC */
+ int bOrderByRank; /* True if ORDER BY rank */
+ sqlite3_value *pMatch = 0; /* <tbl> MATCH ? expression (or NULL) */
+ sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
+ sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
+ sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
+ sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
+ char **pzErrmsg = pConfig->pzErrmsg;
+
+ UNUSED_PARAM(zUnused);
+ UNUSED_PARAM(nVal);
+
+ if( pCsr->ePlan ){
+ fts5FreeCursorComponents(pCsr);
+ memset(&pCsr->ePlan, 0, sizeof(Fts5Cursor) - ((u8*)&pCsr->ePlan-(u8*)pCsr));
+ }
+
+ assert( pCsr->pStmt==0 );
+ assert( pCsr->pExpr==0 );
+ assert( pCsr->csrflags==0 );
+ assert( pCsr->pRank==0 );
+ assert( pCsr->zRank==0 );
+ assert( pCsr->zRankArgs==0 );
+
+ assert( pzErrmsg==0 || pzErrmsg==&pTab->base.zErrMsg );
+ pConfig->pzErrmsg = &pTab->base.zErrMsg;
+
+ /* Decode the arguments passed through to this function.
+ **
+ ** Note: The following set of if(...) statements must be in the same
+ ** order as the corresponding entries in the struct at the top of
+ ** fts5BestIndexMethod(). */
+ if( BitFlagTest(idxNum, FTS5_BI_MATCH) ) pMatch = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_RANK) ) pRank = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_ROWID_EQ) ) pRowidEq = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_ROWID_LE) ) pRowidLe = apVal[iVal++];
+ if( BitFlagTest(idxNum, FTS5_BI_ROWID_GE) ) pRowidGe = apVal[iVal++];
+ assert( iVal==nVal );
+ bOrderByRank = ((idxNum & FTS5_BI_ORDER_RANK) ? 1 : 0);
+ pCsr->bDesc = bDesc = ((idxNum & FTS5_BI_ORDER_DESC) ? 1 : 0);
+
+ /* Set the cursor upper and lower rowid limits. Only some strategies
+ ** actually use them. This is ok, as the xBestIndex() method leaves the
+ ** sqlite3_index_constraint.omit flag clear for range constraints
+ ** on the rowid field. */
+ if( pRowidEq ){
+ pRowidLe = pRowidGe = pRowidEq;
+ }
+ if( bDesc ){
+ pCsr->iFirstRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64);
+ pCsr->iLastRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
+ }else{
+ pCsr->iLastRowid = fts5GetRowidLimit(pRowidLe, LARGEST_INT64);
+ pCsr->iFirstRowid = fts5GetRowidLimit(pRowidGe, SMALLEST_INT64);
+ }
+
+ if( pTab->pSortCsr ){
+ /* If pSortCsr is non-NULL, then this call is being made as part of
+ ** processing for a "... MATCH <expr> ORDER BY rank" query (ePlan is
+ ** set to FTS5_PLAN_SORTED_MATCH). pSortCsr is the cursor that will
+ ** return results to the user for this query. The current cursor
+ ** (pCursor) is used to execute the query issued by function
+ ** fts5CursorFirstSorted() above. */
+ assert( pRowidEq==0 && pRowidLe==0 && pRowidGe==0 && pRank==0 );
+ assert( nVal==0 && pMatch==0 && bOrderByRank==0 && bDesc==0 );
+ assert( pCsr->iLastRowid==LARGEST_INT64 );
+ assert( pCsr->iFirstRowid==SMALLEST_INT64 );
+ pCsr->ePlan = FTS5_PLAN_SOURCE;
+ pCsr->pExpr = pTab->pSortCsr->pExpr;
+ rc = fts5CursorFirst(pTab, pCsr, bDesc);
+ }else if( pMatch ){
+ const char *zExpr = (const char*)sqlite3_value_text(apVal[0]);
+ if( zExpr==0 ) zExpr = "";
+
+ rc = fts5CursorParseRank(pConfig, pCsr, pRank);
+ if( rc==SQLITE_OK ){
+ if( zExpr[0]=='*' ){
+ /* The user has issued a query of the form "MATCH '*...'". This
+ ** indicates that the MATCH expression is not a full text query,
+ ** but a request for an internal parameter. */
+ rc = fts5SpecialMatch(pTab, pCsr, &zExpr[1]);
+ }else{
+ char **pzErr = &pTab->base.zErrMsg;
+ rc = sqlite3Fts5ExprNew(pConfig, zExpr, &pCsr->pExpr, pzErr);
+ if( rc==SQLITE_OK ){
+ if( bOrderByRank ){
+ pCsr->ePlan = FTS5_PLAN_SORTED_MATCH;
+ rc = fts5CursorFirstSorted(pTab, pCsr, bDesc);
+ }else{
+ pCsr->ePlan = FTS5_PLAN_MATCH;
+ rc = fts5CursorFirst(pTab, pCsr, bDesc);
+ }
+ }
+ }
+ }
+ }else if( pConfig->zContent==0 ){
+ *pConfig->pzErrmsg = sqlite3_mprintf(
+ "%s: table does not support scanning", pConfig->zName
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ /* This is either a full-table scan (ePlan==FTS5_PLAN_SCAN) or a lookup
+ ** by rowid (ePlan==FTS5_PLAN_ROWID). */
+ pCsr->ePlan = (pRowidEq ? FTS5_PLAN_ROWID : FTS5_PLAN_SCAN);
+ rc = sqlite3Fts5StorageStmt(
+ pTab->pStorage, fts5StmtType(pCsr), &pCsr->pStmt, &pTab->base.zErrMsg
+ );
+ if( rc==SQLITE_OK ){
+ if( pCsr->ePlan==FTS5_PLAN_ROWID ){
+ sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]);
+ }else{
+ sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iFirstRowid);
+ sqlite3_bind_int64(pCsr->pStmt, 2, pCsr->iLastRowid);
+ }
+ rc = fts5NextMethod(pCursor);
+ }
+ }
+
+ pConfig->pzErrmsg = pzErrmsg;
+ return rc;
+}
+
+/*
+** This is the xEof method of the virtual table. SQLite calls this
+** routine to find out if it has reached the end of a result set.
+*/
+static int fts5EofMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ return (CsrFlagTest(pCsr, FTS5CSR_EOF) ? 1 : 0);
+}
+
+/*
+** Return the rowid that the cursor currently points to.
+*/
+static i64 fts5CursorRowid(Fts5Cursor *pCsr){
+ assert( pCsr->ePlan==FTS5_PLAN_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SOURCE
+ );
+ if( pCsr->pSorter ){
+ return pCsr->pSorter->iRowid;
+ }else{
+ return sqlite3Fts5ExprRowid(pCsr->pExpr);
+ }
+}
+
+/*
+** This is the xRowid method. The SQLite core calls this routine to
+** retrieve the rowid for the current row of the result set. fts5
+** exposes %_content.rowid as the rowid for the virtual table. The
+** rowid should be written to *pRowid.
+*/
+static int fts5RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int ePlan = pCsr->ePlan;
+
+ assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
+ switch( ePlan ){
+ case FTS5_PLAN_SPECIAL:
+ *pRowid = 0;
+ break;
+
+ case FTS5_PLAN_SOURCE:
+ case FTS5_PLAN_MATCH:
+ case FTS5_PLAN_SORTED_MATCH:
+ *pRowid = fts5CursorRowid(pCsr);
+ break;
+
+ default:
+ *pRowid = sqlite3_column_int64(pCsr->pStmt, 0);
+ break;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** If the cursor requires seeking (bSeekRequired flag is set), seek it.
+** Return SQLITE_OK if no error occurs, or an SQLite error code otherwise.
+**
+** If argument bErrormsg is true and an error occurs, an error message may
+** be left in sqlite3_vtab.zErrMsg.
+*/
+static int fts5SeekCursor(Fts5Cursor *pCsr, int bErrormsg){
+ int rc = SQLITE_OK;
+
+ /* If the cursor does not yet have a statement handle, obtain one now. */
+ if( pCsr->pStmt==0 ){
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ int eStmt = fts5StmtType(pCsr);
+ rc = sqlite3Fts5StorageStmt(
+ pTab->pStorage, eStmt, &pCsr->pStmt, (bErrormsg?&pTab->base.zErrMsg:0)
+ );
+ assert( rc!=SQLITE_OK || pTab->base.zErrMsg==0 );
+ assert( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) );
+ }
+
+ if( rc==SQLITE_OK && CsrFlagTest(pCsr, FTS5CSR_REQUIRE_CONTENT) ){
+ assert( pCsr->pExpr );
+ sqlite3_reset(pCsr->pStmt);
+ sqlite3_bind_int64(pCsr->pStmt, 1, fts5CursorRowid(pCsr));
+ rc = sqlite3_step(pCsr->pStmt);
+ if( rc==SQLITE_ROW ){
+ rc = SQLITE_OK;
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_CONTENT);
+ }else{
+ rc = sqlite3_reset(pCsr->pStmt);
+ if( rc==SQLITE_OK ){
+ rc = FTS5_CORRUPT;
+ }
+ }
+ }
+ return rc;
+}
+
+static void fts5SetVtabError(Fts5Table *p, const char *zFormat, ...){
+ va_list ap; /* ... printf arguments */
+ va_start(ap, zFormat);
+ assert( p->base.zErrMsg==0 );
+ p->base.zErrMsg = sqlite3_vmprintf(zFormat, ap);
+ va_end(ap);
+}
+
+/*
+** This function is called to handle an FTS INSERT command. In other words,
+** an INSERT statement of the form:
+**
+** INSERT INTO fts(fts) VALUES($pCmd)
+** INSERT INTO fts(fts, rank) VALUES($pCmd, $pVal)
+**
+** Argument pVal is the value assigned to column "fts" by the INSERT
+** statement. This function returns SQLITE_OK if successful, or an SQLite
+** error code if an error occurs.
+**
+** The commands implemented by this function are documented in the "Special
+** INSERT Directives" section of the documentation. It should be updated if
+** more commands are added to this function.
+*/
+static int fts5SpecialInsert(
+ Fts5Table *pTab, /* Fts5 table object */
+ const char *zCmd, /* Text inserted into table-name column */
+ sqlite3_value *pVal /* Value inserted into rank column */
+){
+ Fts5Config *pConfig = pTab->pConfig;
+ int rc = SQLITE_OK;
+ int bError = 0;
+
+ if( 0==sqlite3_stricmp("delete-all", zCmd) ){
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ fts5SetVtabError(pTab,
+ "'delete-all' may only be used with a "
+ "contentless or external content fts5 table"
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5StorageDeleteAll(pTab->pStorage);
+ }
+ }else if( 0==sqlite3_stricmp("rebuild", zCmd) ){
+ if( pConfig->eContent==FTS5_CONTENT_NONE ){
+ fts5SetVtabError(pTab,
+ "'rebuild' may not be used with a contentless fts5 table"
+ );
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5StorageRebuild(pTab->pStorage);
+ }
+ }else if( 0==sqlite3_stricmp("optimize", zCmd) ){
+ rc = sqlite3Fts5StorageOptimize(pTab->pStorage);
+ }else if( 0==sqlite3_stricmp("merge", zCmd) ){
+ int nMerge = sqlite3_value_int(pVal);
+ rc = sqlite3Fts5StorageMerge(pTab->pStorage, nMerge);
+ }else if( 0==sqlite3_stricmp("integrity-check", zCmd) ){
+ rc = sqlite3Fts5StorageIntegrity(pTab->pStorage);
+#ifdef SQLITE_DEBUG
+ }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
+ pConfig->bPrefixIndex = sqlite3_value_int(pVal);
+#endif
+ }else{
+ rc = sqlite3Fts5IndexLoadConfig(pTab->pIndex);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ConfigSetValue(pTab->pConfig, zCmd, pVal, &bError);
+ }
+ if( rc==SQLITE_OK ){
+ if( bError ){
+ rc = SQLITE_ERROR;
+ }else{
+ rc = sqlite3Fts5StorageConfigValue(pTab->pStorage, zCmd, pVal, 0);
+ }
+ }
+ }
+ return rc;
+}
+
+static int fts5SpecialDelete(
+ Fts5Table *pTab,
+ sqlite3_value **apVal
+){
+ int rc = SQLITE_OK;
+ int eType1 = sqlite3_value_type(apVal[1]);
+ if( eType1==SQLITE_INTEGER ){
+ sqlite3_int64 iDel = sqlite3_value_int64(apVal[1]);
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, &apVal[2]);
+ }
+ return rc;
+}
+
+static void fts5StorageInsert(
+ int *pRc,
+ Fts5Table *pTab,
+ sqlite3_value **apVal,
+ i64 *piRowid
+){
+ int rc = *pRc;
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, piRowid);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *piRowid);
+ }
+ *pRc = rc;
+}
+
+/*
+** This function is the implementation of the xUpdate callback used by
+** FTS3 virtual tables. It is invoked by SQLite each time a row is to be
+** inserted, updated or deleted.
+**
+** A delete specifies a single argument - the rowid of the row to remove.
+**
+** Update and insert operations pass:
+**
+** 1. The "old" rowid, or NULL.
+** 2. The "new" rowid.
+** 3. Values for each of the nCol matchable columns.
+** 4. Values for the two hidden columns (<tablename> and "rank").
+*/
+static int fts5UpdateMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ int nArg, /* Size of argument array */
+ sqlite3_value **apVal, /* Array of arguments */
+ sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */
+){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ Fts5Config *pConfig = pTab->pConfig;
+ int eType0; /* value_type() of apVal[0] */
+ int rc = SQLITE_OK; /* Return code */
+
+ /* A transaction must be open when this is called. */
+ assert( pTab->ts.eState==1 );
+
+ assert( pVtab->zErrMsg==0 );
+ assert( nArg==1 || nArg==(2+pConfig->nCol+2) );
+ assert( nArg==1
+ || sqlite3_value_type(apVal[1])==SQLITE_INTEGER
+ || sqlite3_value_type(apVal[1])==SQLITE_NULL
+ );
+ assert( pTab->pConfig->pzErrmsg==0 );
+ pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
+
+ /* Put any active cursors into REQUIRE_SEEK state. */
+ fts5TripCursors(pTab);
+
+ eType0 = sqlite3_value_type(apVal[0]);
+ if( eType0==SQLITE_NULL
+ && sqlite3_value_type(apVal[2+pConfig->nCol])!=SQLITE_NULL
+ ){
+ /* A "special" INSERT op. These are handled separately. */
+ const char *z = (const char*)sqlite3_value_text(apVal[2+pConfig->nCol]);
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL
+ && 0==sqlite3_stricmp("delete", z)
+ ){
+ rc = fts5SpecialDelete(pTab, apVal);
+ }else{
+ rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
+ }
+ }else{
+ /* A regular INSERT, UPDATE or DELETE statement. The trick here is that
+ ** any conflict on the rowid value must be detected before any
+ ** modifications are made to the database file. There are 4 cases:
+ **
+ ** 1) DELETE
+ ** 2) UPDATE (rowid not modified)
+ ** 3) UPDATE (rowid modified)
+ ** 4) INSERT
+ **
+ ** Cases 3 and 4 may violate the rowid constraint.
+ */
+ int eConflict = SQLITE_ABORT;
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ eConflict = sqlite3_vtab_on_conflict(pConfig->db);
+ }
+
+ assert( eType0==SQLITE_INTEGER || eType0==SQLITE_NULL );
+ assert( nArg!=1 || eType0==SQLITE_INTEGER );
+
+ /* Filter out attempts to run UPDATE or DELETE on contentless tables.
+ ** This is not suported. */
+ if( eType0==SQLITE_INTEGER && fts5IsContentless(pTab) ){
+ pTab->base.zErrMsg = sqlite3_mprintf(
+ "cannot %s contentless fts5 table: %s",
+ (nArg>1 ? "UPDATE" : "DELETE from"), pConfig->zName
+ );
+ rc = SQLITE_ERROR;
+ }
+
+ /* DELETE */
+ else if( nArg==1 ){
+ i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0);
+ }
+
+ /* INSERT */
+ else if( eType0!=SQLITE_INTEGER ){
+ /* If this is a REPLACE, first remove the current entry (if any) */
+ if( eConflict==SQLITE_REPLACE
+ && sqlite3_value_type(apVal[1])==SQLITE_INTEGER
+ ){
+ i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
+ }
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }
+
+ /* UPDATE */
+ else{
+ i64 iOld = sqlite3_value_int64(apVal[0]); /* Old rowid */
+ i64 iNew = sqlite3_value_int64(apVal[1]); /* New rowid */
+ if( iOld!=iNew ){
+ if( eConflict==SQLITE_REPLACE ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0);
+ }
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }else{
+ rc = sqlite3Fts5StorageContentInsert(pTab->pStorage, apVal, pRowid);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageIndexInsert(pTab->pStorage, apVal, *pRowid);
+ }
+ }
+ }else{
+ rc = sqlite3Fts5StorageDelete(pTab->pStorage, iOld, 0);
+ fts5StorageInsert(&rc, pTab, apVal, pRowid);
+ }
+ }
+ }
+
+ pTab->pConfig->pzErrmsg = 0;
+ return rc;
+}
+
+/*
+** Implementation of xSync() method.
+*/
+static int fts5SyncMethod(sqlite3_vtab *pVtab){
+ int rc;
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ fts5CheckTransactionState(pTab, FTS5_SYNC, 0);
+ pTab->pConfig->pzErrmsg = &pTab->base.zErrMsg;
+ fts5TripCursors(pTab);
+ rc = sqlite3Fts5StorageSync(pTab->pStorage, 1);
+ pTab->pConfig->pzErrmsg = 0;
+ return rc;
+}
+
+/*
+** Implementation of xBegin() method.
+*/
+static int fts5BeginMethod(sqlite3_vtab *pVtab){
+ fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_BEGIN, 0);
+ fts5NewTransaction((Fts5Table*)pVtab);
+ return SQLITE_OK;
+}
+
+/*
+** Implementation of xCommit() method. This is a no-op. The contents of
+** the pending-terms hash-table have already been flushed into the database
+** by fts5SyncMethod().
+*/
+static int fts5CommitMethod(sqlite3_vtab *pVtab){
+ UNUSED_PARAM(pVtab); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState((Fts5Table*)pVtab, FTS5_COMMIT, 0);
+ return SQLITE_OK;
+}
+
+/*
+** Implementation of xRollback(). Discard the contents of the pending-terms
+** hash-table. Any changes made to the database are reverted by SQLite.
+*/
+static int fts5RollbackMethod(sqlite3_vtab *pVtab){
+ int rc;
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0);
+ rc = sqlite3Fts5StorageRollback(pTab->pStorage);
+ return rc;
+}
+
+static int fts5CsrPoslist(Fts5Cursor*, int, const u8**, int*);
+
+static void *fts5ApiUserData(Fts5Context *pCtx){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return pCsr->pAux->pUserData;
+}
+
+static int fts5ApiColumnCount(Fts5Context *pCtx){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return ((Fts5Table*)(pCsr->base.pVtab))->pConfig->nCol;
+}
+
+static int fts5ApiColumnTotalSize(
+ Fts5Context *pCtx,
+ int iCol,
+ sqlite3_int64 *pnToken
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ return sqlite3Fts5StorageSize(pTab->pStorage, iCol, pnToken);
+}
+
+static int fts5ApiRowCount(Fts5Context *pCtx, i64 *pnRow){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ return sqlite3Fts5StorageRowCount(pTab->pStorage, pnRow);
+}
+
+static int fts5ApiTokenize(
+ Fts5Context *pCtx,
+ const char *pText, int nText,
+ void *pUserData,
+ int (*xToken)(void*, int, const char*, int, int, int)
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ return sqlite3Fts5Tokenize(
+ pTab->pConfig, FTS5_TOKENIZE_AUX, pText, nText, pUserData, xToken
+ );
+}
+
+static int fts5ApiPhraseCount(Fts5Context *pCtx){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+}
+
+static int fts5ApiPhraseSize(Fts5Context *pCtx, int iPhrase){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ return sqlite3Fts5ExprPhraseSize(pCsr->pExpr, iPhrase);
+}
+
+static int fts5ApiColumnText(
+ Fts5Context *pCtx,
+ int iCol,
+ const char **pz,
+ int *pn
+){
+ int rc = SQLITE_OK;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ if( fts5IsContentless((Fts5Table*)(pCsr->base.pVtab)) ){
+ *pz = 0;
+ *pn = 0;
+ }else{
+ rc = fts5SeekCursor(pCsr, 0);
+ if( rc==SQLITE_OK ){
+ *pz = (const char*)sqlite3_column_text(pCsr->pStmt, iCol+1);
+ *pn = sqlite3_column_bytes(pCsr->pStmt, iCol+1);
+ }
+ }
+ return rc;
+}
+
+static int fts5CsrPoslist(
+ Fts5Cursor *pCsr,
+ int iPhrase,
+ const u8 **pa,
+ int *pn
+){
+ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
+ int rc = SQLITE_OK;
+ int bLive = (pCsr->pSorter==0);
+
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){
+
+ if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
+ Fts5PoslistPopulator *aPopulator;
+ int i;
+ aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
+ if( aPopulator==0 ) rc = SQLITE_NOMEM;
+ for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
+ int n; const char *z;
+ rc = fts5ApiColumnText((Fts5Context*)pCsr, i, &z, &n);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5ExprPopulatePoslists(
+ pConfig, pCsr->pExpr, aPopulator, i, z, n
+ );
+ }
+ }
+ sqlite3_free(aPopulator);
+
+ if( pCsr->pSorter ){
+ sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
+ }
+ }
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
+ }
+
+ if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
+ *pn = pSorter->aIdx[iPhrase] - i1;
+ *pa = &pSorter->aPoslist[i1];
+ }else{
+ *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
+ }
+
+ return rc;
+}
+
+/*
+** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated
+** correctly for the current view. Return SQLITE_OK if successful, or an
+** SQLite error code otherwise.
+*/
+static int fts5CacheInstArray(Fts5Cursor *pCsr){
+ int rc = SQLITE_OK;
+ Fts5PoslistReader *aIter; /* One iterator for each phrase */
+ int nIter; /* Number of iterators/phrases */
+
+ nIter = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+ if( pCsr->aInstIter==0 ){
+ int nByte = sizeof(Fts5PoslistReader) * nIter;
+ pCsr->aInstIter = (Fts5PoslistReader*)sqlite3Fts5MallocZero(&rc, nByte);
+ }
+ aIter = pCsr->aInstIter;
+
+ if( aIter ){
+ int nInst = 0; /* Number instances seen so far */
+ int i;
+
+ /* Initialize all iterators */
+ for(i=0; i<nIter && rc==SQLITE_OK; i++){
+ const u8 *a;
+ int n;
+ rc = fts5CsrPoslist(pCsr, i, &a, &n);
+ if( rc==SQLITE_OK ){
+ sqlite3Fts5PoslistReaderInit(a, n, &aIter[i]);
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ while( 1 ){
+ int *aInst;
+ int iBest = -1;
+ for(i=0; i<nIter; i++){
+ if( (aIter[i].bEof==0)
+ && (iBest<0 || aIter[i].iPos<aIter[iBest].iPos)
+ ){
+ iBest = i;
+ }
+ }
+ if( iBest<0 ) break;
+
+ nInst++;
+ if( nInst>=pCsr->nInstAlloc ){
+ pCsr->nInstAlloc = pCsr->nInstAlloc ? pCsr->nInstAlloc*2 : 32;
+ aInst = (int*)sqlite3_realloc(
+ pCsr->aInst, pCsr->nInstAlloc*sizeof(int)*3
+ );
+ if( aInst ){
+ pCsr->aInst = aInst;
+ }else{
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ }
+
+ aInst = &pCsr->aInst[3 * (nInst-1)];
+ aInst[0] = iBest;
+ aInst[1] = FTS5_POS2COLUMN(aIter[iBest].iPos);
+ aInst[2] = FTS5_POS2OFFSET(aIter[iBest].iPos);
+ sqlite3Fts5PoslistReaderNext(&aIter[iBest]);
+ }
+ }
+
+ pCsr->nInstCount = nInst;
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_INST);
+ }
+ return rc;
+}
+
+static int fts5ApiInstCount(Fts5Context *pCtx, int *pnInst){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int rc = SQLITE_OK;
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr)) ){
+ *pnInst = pCsr->nInstCount;
+ }
+ return rc;
+}
+
+static int fts5ApiInst(
+ Fts5Context *pCtx,
+ int iIdx,
+ int *piPhrase,
+ int *piCol,
+ int *piOff
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int rc = SQLITE_OK;
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
+ || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
+ ){
+ if( iIdx<0 || iIdx>=pCsr->nInstCount ){
+ rc = SQLITE_RANGE;
+#if 0
+ }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){
+ *piPhrase = pCsr->aInst[iIdx*3];
+ *piCol = pCsr->aInst[iIdx*3 + 2];
+ *piOff = -1;
+#endif
+ }else{
+ *piPhrase = pCsr->aInst[iIdx*3];
+ *piCol = pCsr->aInst[iIdx*3 + 1];
+ *piOff = pCsr->aInst[iIdx*3 + 2];
+ }
+ }
+ return rc;
+}
+
+static sqlite3_int64 fts5ApiRowid(Fts5Context *pCtx){
+ return fts5CursorRowid((Fts5Cursor*)pCtx);
+}
+
+static int fts5ColumnSizeCb(
+ void *pContext, /* Pointer to int */
+ int tflags,
+ const char *pUnused, /* Buffer containing token */
+ int nUnused, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ int *pCnt = (int*)pContext;
+ UNUSED_PARAM2(pUnused, nUnused);
+ UNUSED_PARAM2(iUnused1, iUnused2);
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 ){
+ (*pCnt)++;
+ }
+ return SQLITE_OK;
+}
+
+static int fts5ApiColumnSize(Fts5Context *pCtx, int iCol, int *pnToken){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ int rc = SQLITE_OK;
+
+ if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_DOCSIZE) ){
+ if( pConfig->bColumnsize ){
+ i64 iRowid = fts5CursorRowid(pCsr);
+ rc = sqlite3Fts5StorageDocsize(pTab->pStorage, iRowid, pCsr->aColumnSize);
+ }else if( pConfig->zContent==0 ){
+ int i;
+ for(i=0; i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i]==0 ){
+ pCsr->aColumnSize[i] = -1;
+ }
+ }
+ }else{
+ int i;
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i]==0 ){
+ const char *z; int n;
+ void *p = (void*)(&pCsr->aColumnSize[i]);
+ pCsr->aColumnSize[i] = 0;
+ rc = fts5ApiColumnText(pCtx, i, &z, &n);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5Tokenize(
+ pConfig, FTS5_TOKENIZE_AUX, z, n, p, fts5ColumnSizeCb
+ );
+ }
+ }
+ }
+ }
+ CsrFlagClear(pCsr, FTS5CSR_REQUIRE_DOCSIZE);
+ }
+ if( iCol<0 ){
+ int i;
+ *pnToken = 0;
+ for(i=0; i<pConfig->nCol; i++){
+ *pnToken += pCsr->aColumnSize[i];
+ }
+ }else if( iCol<pConfig->nCol ){
+ *pnToken = pCsr->aColumnSize[iCol];
+ }else{
+ *pnToken = 0;
+ rc = SQLITE_RANGE;
+ }
+ return rc;
+}
+
+/*
+** Implementation of the xSetAuxdata() method.
+*/
+static int fts5ApiSetAuxdata(
+ Fts5Context *pCtx, /* Fts5 context */
+ void *pPtr, /* Pointer to save as auxdata */
+ void(*xDelete)(void*) /* Destructor for pPtr (or NULL) */
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Auxdata *pData;
+
+ /* Search through the cursors list of Fts5Auxdata objects for one that
+ ** corresponds to the currently executing auxiliary function. */
+ for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){
+ if( pData->pAux==pCsr->pAux ) break;
+ }
+
+ if( pData ){
+ if( pData->xDelete ){
+ pData->xDelete(pData->pPtr);
+ }
+ }else{
+ int rc = SQLITE_OK;
+ pData = (Fts5Auxdata*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Auxdata));
+ if( pData==0 ){
+ if( xDelete ) xDelete(pPtr);
+ return rc;
+ }
+ pData->pAux = pCsr->pAux;
+ pData->pNext = pCsr->pAuxdata;
+ pCsr->pAuxdata = pData;
+ }
+
+ pData->xDelete = xDelete;
+ pData->pPtr = pPtr;
+ return SQLITE_OK;
+}
+
+static void *fts5ApiGetAuxdata(Fts5Context *pCtx, int bClear){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Auxdata *pData;
+ void *pRet = 0;
+
+ for(pData=pCsr->pAuxdata; pData; pData=pData->pNext){
+ if( pData->pAux==pCsr->pAux ) break;
+ }
+
+ if( pData ){
+ pRet = pData->pPtr;
+ if( bClear ){
+ pData->pPtr = 0;
+ pData->xDelete = 0;
+ }
+ }
+
+ return pRet;
+}
+
+static void fts5ApiPhraseNext(
+ Fts5Context *pUnused,
+ Fts5PhraseIter *pIter,
+ int *piCol, int *piOff
+){
+ UNUSED_PARAM(pUnused);
+ if( pIter->a>=pIter->b ){
+ *piCol = -1;
+ *piOff = -1;
+ }else{
+ int iVal;
+ pIter->a += fts5GetVarint32(pIter->a, iVal);
+ if( iVal==1 ){
+ pIter->a += fts5GetVarint32(pIter->a, iVal);
+ *piCol = iVal;
+ *piOff = 0;
+ pIter->a += fts5GetVarint32(pIter->a, iVal);
+ }
+ *piOff += (iVal-2);
+ }
+}
+
+static int fts5ApiPhraseFirst(
+ Fts5Context *pCtx,
+ int iPhrase,
+ Fts5PhraseIter *pIter,
+ int *piCol, int *piOff
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ int n;
+ int rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
+ if( rc==SQLITE_OK ){
+ pIter->b = &pIter->a[n];
+ *piCol = 0;
+ *piOff = 0;
+ fts5ApiPhraseNext(pCtx, pIter, piCol, piOff);
+ }
+ return rc;
+}
+
+static void fts5ApiPhraseNextColumn(
+ Fts5Context *pCtx,
+ Fts5PhraseIter *pIter,
+ int *piCol
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
+
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ if( pIter->a>=pIter->b ){
+ *piCol = -1;
+ }else{
+ int iIncr;
+ pIter->a += fts5GetVarint32(&pIter->a[0], iIncr);
+ *piCol += (iIncr-2);
+ }
+ }else{
+ while( 1 ){
+ int dummy;
+ if( pIter->a>=pIter->b ){
+ *piCol = -1;
+ return;
+ }
+ if( pIter->a[0]==0x01 ) break;
+ pIter->a += fts5GetVarint32(pIter->a, dummy);
+ }
+ pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
+ }
+}
+
+static int fts5ApiPhraseFirstColumn(
+ Fts5Context *pCtx,
+ int iPhrase,
+ Fts5PhraseIter *pIter,
+ int *piCol
+){
+ int rc = SQLITE_OK;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
+
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ Fts5Sorter *pSorter = pCsr->pSorter;
+ int n;
+ if( pSorter ){
+ int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
+ n = pSorter->aIdx[iPhrase] - i1;
+ pIter->a = &pSorter->aPoslist[i1];
+ }else{
+ rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, iPhrase, &pIter->a, &n);
+ }
+ if( rc==SQLITE_OK ){
+ pIter->b = &pIter->a[n];
+ *piCol = 0;
+ fts5ApiPhraseNextColumn(pCtx, pIter, piCol);
+ }
+ }else{
+ int n;
+ rc = fts5CsrPoslist(pCsr, iPhrase, &pIter->a, &n);
+ if( rc==SQLITE_OK ){
+ pIter->b = &pIter->a[n];
+ if( n<=0 ){
+ *piCol = -1;
+ }else if( pIter->a[0]==0x01 ){
+ pIter->a += 1 + fts5GetVarint32(&pIter->a[1], *piCol);
+ }else{
+ *piCol = 0;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
+ int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
+);
+
+static const Fts5ExtensionApi sFts5Api = {
+ 2, /* iVersion */
+ fts5ApiUserData,
+ fts5ApiColumnCount,
+ fts5ApiRowCount,
+ fts5ApiColumnTotalSize,
+ fts5ApiTokenize,
+ fts5ApiPhraseCount,
+ fts5ApiPhraseSize,
+ fts5ApiInstCount,
+ fts5ApiInst,
+ fts5ApiRowid,
+ fts5ApiColumnText,
+ fts5ApiColumnSize,
+ fts5ApiQueryPhrase,
+ fts5ApiSetAuxdata,
+ fts5ApiGetAuxdata,
+ fts5ApiPhraseFirst,
+ fts5ApiPhraseNext,
+ fts5ApiPhraseFirstColumn,
+ fts5ApiPhraseNextColumn,
+};
+
+/*
+** Implementation of API function xQueryPhrase().
+*/
+static int fts5ApiQueryPhrase(
+ Fts5Context *pCtx,
+ int iPhrase,
+ void *pUserData,
+ int(*xCallback)(const Fts5ExtensionApi*, Fts5Context*, void*)
+){
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
+ Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
+ int rc;
+ Fts5Cursor *pNew = 0;
+
+ rc = fts5OpenMethod(pCsr->base.pVtab, (sqlite3_vtab_cursor**)&pNew);
+ if( rc==SQLITE_OK ){
+ pNew->ePlan = FTS5_PLAN_MATCH;
+ pNew->iFirstRowid = SMALLEST_INT64;
+ pNew->iLastRowid = LARGEST_INT64;
+ pNew->base.pVtab = (sqlite3_vtab*)pTab;
+ rc = sqlite3Fts5ExprClonePhrase(pCsr->pExpr, iPhrase, &pNew->pExpr);
+ }
+
+ if( rc==SQLITE_OK ){
+ for(rc = fts5CursorFirst(pTab, pNew, 0);
+ rc==SQLITE_OK && CsrFlagTest(pNew, FTS5CSR_EOF)==0;
+ rc = fts5NextMethod((sqlite3_vtab_cursor*)pNew)
+ ){
+ rc = xCallback(&sFts5Api, (Fts5Context*)pNew, pUserData);
+ if( rc!=SQLITE_OK ){
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ break;
+ }
+ }
+ }
+
+ fts5CloseMethod((sqlite3_vtab_cursor*)pNew);
+ return rc;
+}
+
+static void fts5ApiInvoke(
+ Fts5Auxiliary *pAux,
+ Fts5Cursor *pCsr,
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ assert( pCsr->pAux==0 );
+ pCsr->pAux = pAux;
+ pAux->xFunc(&sFts5Api, (Fts5Context*)pCsr, context, argc, argv);
+ pCsr->pAux = 0;
+}
+
+static Fts5Cursor *fts5CursorFromCsrid(Fts5Global *pGlobal, i64 iCsrId){
+ Fts5Cursor *pCsr;
+ for(pCsr=pGlobal->pCsr; pCsr; pCsr=pCsr->pNext){
+ if( pCsr->iCsrId==iCsrId ) break;
+ }
+ return pCsr;
+}
+
+static void fts5ApiCallback(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+
+ Fts5Auxiliary *pAux;
+ Fts5Cursor *pCsr;
+ i64 iCsrId;
+
+ assert( argc>=1 );
+ pAux = (Fts5Auxiliary*)sqlite3_user_data(context);
+ iCsrId = sqlite3_value_int64(argv[0]);
+
+ pCsr = fts5CursorFromCsrid(pAux->pGlobal, iCsrId);
+ if( pCsr==0 ){
+ char *zErr = sqlite3_mprintf("no such cursor: %lld", iCsrId);
+ sqlite3_result_error(context, zErr, -1);
+ sqlite3_free(zErr);
+ }else{
+ fts5ApiInvoke(pAux, pCsr, context, argc-1, &argv[1]);
+ }
+}
+
+
+/*
+** Given cursor id iId, return a pointer to the corresponding Fts5Index
+** object. Or NULL If the cursor id does not exist.
+**
+** If successful, set *ppConfig to point to the associated config object
+** before returning.
+*/
+static Fts5Index *sqlite3Fts5IndexFromCsrid(
+ Fts5Global *pGlobal, /* FTS5 global context for db handle */
+ i64 iCsrId, /* Id of cursor to find */
+ Fts5Config **ppConfig /* OUT: Configuration object */
+){
+ Fts5Cursor *pCsr;
+ Fts5Table *pTab;
+
+ pCsr = fts5CursorFromCsrid(pGlobal, iCsrId);
+ pTab = (Fts5Table*)pCsr->base.pVtab;
+ *ppConfig = pTab->pConfig;
+
+ return pTab->pIndex;
+}
+
+/*
+** Return a "position-list blob" corresponding to the current position of
+** cursor pCsr via sqlite3_result_blob(). A position-list blob contains
+** the current position-list for each phrase in the query associated with
+** cursor pCsr.
+**
+** A position-list blob begins with (nPhrase-1) varints, where nPhrase is
+** the number of phrases in the query. Following the varints are the
+** concatenated position lists for each phrase, in order.
+**
+** The first varint (if it exists) contains the size of the position list
+** for phrase 0. The second (same disclaimer) contains the size of position
+** list 1. And so on. There is no size field for the final position list,
+** as it can be derived from the total size of the blob.
+*/
+static int fts5PoslistBlob(sqlite3_context *pCtx, Fts5Cursor *pCsr){
+ int i;
+ int rc = SQLITE_OK;
+ int nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
+ Fts5Buffer val;
+
+ memset(&val, 0, sizeof(Fts5Buffer));
+ switch( ((Fts5Table*)(pCsr->base.pVtab))->pConfig->eDetail ){
+ case FTS5_DETAIL_FULL:
+
+ /* Append the varints */
+ for(i=0; i<(nPhrase-1); i++){
+ const u8 *dummy;
+ int nByte = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &dummy);
+ sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
+ }
+
+ /* Append the position lists */
+ for(i=0; i<nPhrase; i++){
+ const u8 *pPoslist;
+ int nPoslist;
+ nPoslist = sqlite3Fts5ExprPoslist(pCsr->pExpr, i, &pPoslist);
+ sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
+ }
+ break;
+
+ case FTS5_DETAIL_COLUMNS:
+
+ /* Append the varints */
+ for(i=0; rc==SQLITE_OK && i<(nPhrase-1); i++){
+ const u8 *dummy;
+ int nByte;
+ rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &dummy, &nByte);
+ sqlite3Fts5BufferAppendVarint(&rc, &val, nByte);
+ }
+
+ /* Append the position lists */
+ for(i=0; rc==SQLITE_OK && i<nPhrase; i++){
+ const u8 *pPoslist;
+ int nPoslist;
+ rc = sqlite3Fts5ExprPhraseCollist(pCsr->pExpr, i, &pPoslist, &nPoslist);
+ sqlite3Fts5BufferAppendBlob(&rc, &val, nPoslist, pPoslist);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ sqlite3_result_blob(pCtx, val.p, val.n, sqlite3_free);
+ return rc;
+}
+
+/*
+** This is the xColumn method, called by SQLite to request a value from
+** the row that the supplied cursor currently points to.
+*/
+static int fts5ColumnMethod(
+ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
+ int iCol /* Index of column to read value from */
+){
+ Fts5Table *pTab = (Fts5Table*)(pCursor->pVtab);
+ Fts5Config *pConfig = pTab->pConfig;
+ Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
+ int rc = SQLITE_OK;
+
+ assert( CsrFlagTest(pCsr, FTS5CSR_EOF)==0 );
+
+ if( pCsr->ePlan==FTS5_PLAN_SPECIAL ){
+ if( iCol==pConfig->nCol ){
+ sqlite3_result_int64(pCtx, pCsr->iSpecial);
+ }
+ }else
+
+ if( iCol==pConfig->nCol ){
+ /* User is requesting the value of the special column with the same name
+ ** as the table. Return the cursor integer id number. This value is only
+ ** useful in that it may be passed as the first argument to an FTS5
+ ** auxiliary function. */
+ sqlite3_result_int64(pCtx, pCsr->iCsrId);
+ }else if( iCol==pConfig->nCol+1 ){
+
+ /* The value of the "rank" column. */
+ if( pCsr->ePlan==FTS5_PLAN_SOURCE ){
+ fts5PoslistBlob(pCtx, pCsr);
+ }else if(
+ pCsr->ePlan==FTS5_PLAN_MATCH
+ || pCsr->ePlan==FTS5_PLAN_SORTED_MATCH
+ ){
+ if( pCsr->pRank || SQLITE_OK==(rc = fts5FindRankFunction(pCsr)) ){
+ fts5ApiInvoke(pCsr->pRank, pCsr, pCtx, pCsr->nRankArg, pCsr->apRankArg);
+ }
+ }
+ }else if( !fts5IsContentless(pTab) ){
+ rc = fts5SeekCursor(pCsr, 1);
+ if( rc==SQLITE_OK ){
+ sqlite3_result_value(pCtx, sqlite3_column_value(pCsr->pStmt, iCol+1));
+ }
+ }
+ return rc;
+}
+
+
+/*
+** This routine implements the xFindFunction method for the FTS3
+** virtual table.
+*/
+static int fts5FindFunctionMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ int nUnused, /* Number of SQL function arguments */
+ const char *zName, /* Name of SQL function */
+ void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */
+ void **ppArg /* OUT: User data for *pxFunc */
+){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ Fts5Auxiliary *pAux;
+
+ UNUSED_PARAM(nUnused);
+ pAux = fts5FindAuxiliary(pTab, zName);
+ if( pAux ){
+ *pxFunc = fts5ApiCallback;
+ *ppArg = (void*)pAux;
+ return 1;
+ }
+
+ /* No function of the specified name was found. Return 0. */
+ return 0;
+}
+
+/*
+** Implementation of FTS5 xRename method. Rename an fts5 table.
+*/
+static int fts5RenameMethod(
+ sqlite3_vtab *pVtab, /* Virtual table handle */
+ const char *zName /* New name of table */
+){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ return sqlite3Fts5StorageRename(pTab->pStorage, zName);
+}
+
+/*
+** The xSavepoint() method.
+**
+** Flush the contents of the pending-terms table to disk.
+*/
+static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
+ fts5TripCursors(pTab);
+ return sqlite3Fts5StorageSync(pTab->pStorage, 0);
+}
+
+/*
+** The xRelease() method.
+**
+** This is a no-op.
+*/
+static int fts5ReleaseMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState(pTab, FTS5_RELEASE, iSavepoint);
+ fts5TripCursors(pTab);
+ return sqlite3Fts5StorageSync(pTab->pStorage, 0);
+}
+
+/*
+** The xRollbackTo() method.
+**
+** Discard the contents of the pending terms table.
+*/
+static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
+ Fts5Table *pTab = (Fts5Table*)pVtab;
+ UNUSED_PARAM(iSavepoint); /* Call below is a no-op for NDEBUG builds */
+ fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
+ fts5TripCursors(pTab);
+ return sqlite3Fts5StorageRollback(pTab->pStorage);
+}
+
+/*
+** Register a new auxiliary function with global context pGlobal.
+*/
+static int fts5CreateAux(
+ fts5_api *pApi, /* Global context (one per db handle) */
+ const char *zName, /* Name of new function */
+ void *pUserData, /* User data for aux. function */
+ fts5_extension_function xFunc, /* Aux. function implementation */
+ void(*xDestroy)(void*) /* Destructor for pUserData */
+){
+ Fts5Global *pGlobal = (Fts5Global*)pApi;
+ int rc = sqlite3_overload_function(pGlobal->db, zName, -1);
+ if( rc==SQLITE_OK ){
+ Fts5Auxiliary *pAux;
+ int nName; /* Size of zName in bytes, including \0 */
+ int nByte; /* Bytes of space to allocate */
+
+ nName = (int)strlen(zName) + 1;
+ nByte = sizeof(Fts5Auxiliary) + nName;
+ pAux = (Fts5Auxiliary*)sqlite3_malloc(nByte);
+ if( pAux ){
+ memset(pAux, 0, nByte);
+ pAux->zFunc = (char*)&pAux[1];
+ memcpy(pAux->zFunc, zName, nName);
+ pAux->pGlobal = pGlobal;
+ pAux->pUserData = pUserData;
+ pAux->xFunc = xFunc;
+ pAux->xDestroy = xDestroy;
+ pAux->pNext = pGlobal->pAux;
+ pGlobal->pAux = pAux;
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Register a new tokenizer. This is the implementation of the
+** fts5_api.xCreateTokenizer() method.
+*/
+static int fts5CreateTokenizer(
+ fts5_api *pApi, /* Global context (one per db handle) */
+ const char *zName, /* Name of new function */
+ void *pUserData, /* User data for aux. function */
+ fts5_tokenizer *pTokenizer, /* Tokenizer implementation */
+ void(*xDestroy)(void*) /* Destructor for pUserData */
+){
+ Fts5Global *pGlobal = (Fts5Global*)pApi;
+ Fts5TokenizerModule *pNew;
+ int nName; /* Size of zName and its \0 terminator */
+ int nByte; /* Bytes of space to allocate */
+ int rc = SQLITE_OK;
+
+ nName = (int)strlen(zName) + 1;
+ nByte = sizeof(Fts5TokenizerModule) + nName;
+ pNew = (Fts5TokenizerModule*)sqlite3_malloc(nByte);
+ if( pNew ){
+ memset(pNew, 0, nByte);
+ pNew->zName = (char*)&pNew[1];
+ memcpy(pNew->zName, zName, nName);
+ pNew->pUserData = pUserData;
+ pNew->x = *pTokenizer;
+ pNew->xDestroy = xDestroy;
+ pNew->pNext = pGlobal->pTok;
+ pGlobal->pTok = pNew;
+ if( pNew->pNext==0 ){
+ pGlobal->pDfltTok = pNew;
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+
+ return rc;
+}
+
+static Fts5TokenizerModule *fts5LocateTokenizer(
+ Fts5Global *pGlobal,
+ const char *zName
+){
+ Fts5TokenizerModule *pMod = 0;
+
+ if( zName==0 ){
+ pMod = pGlobal->pDfltTok;
+ }else{
+ for(pMod=pGlobal->pTok; pMod; pMod=pMod->pNext){
+ if( sqlite3_stricmp(zName, pMod->zName)==0 ) break;
+ }
+ }
+
+ return pMod;
+}
+
+/*
+** Find a tokenizer. This is the implementation of the
+** fts5_api.xFindTokenizer() method.
+*/
+static int fts5FindTokenizer(
+ fts5_api *pApi, /* Global context (one per db handle) */
+ const char *zName, /* Name of new function */
+ void **ppUserData,
+ fts5_tokenizer *pTokenizer /* Populate this object */
+){
+ int rc = SQLITE_OK;
+ Fts5TokenizerModule *pMod;
+
+ pMod = fts5LocateTokenizer((Fts5Global*)pApi, zName);
+ if( pMod ){
+ *pTokenizer = pMod->x;
+ *ppUserData = pMod->pUserData;
+ }else{
+ memset(pTokenizer, 0, sizeof(fts5_tokenizer));
+ rc = SQLITE_ERROR;
+ }
+
+ return rc;
+}
+
+static int sqlite3Fts5GetTokenizer(
+ Fts5Global *pGlobal,
+ const char **azArg,
+ int nArg,
+ Fts5Tokenizer **ppTok,
+ fts5_tokenizer **ppTokApi,
+ char **pzErr
+){
+ Fts5TokenizerModule *pMod;
+ int rc = SQLITE_OK;
+
+ pMod = fts5LocateTokenizer(pGlobal, nArg==0 ? 0 : azArg[0]);
+ if( pMod==0 ){
+ assert( nArg>0 );
+ rc = SQLITE_ERROR;
+ *pzErr = sqlite3_mprintf("no such tokenizer: %s", azArg[0]);
+ }else{
+ rc = pMod->x.xCreate(pMod->pUserData, &azArg[1], (nArg?nArg-1:0), ppTok);
+ *ppTokApi = &pMod->x;
+ if( rc!=SQLITE_OK && pzErr ){
+ *pzErr = sqlite3_mprintf("error in tokenizer constructor");
+ }
+ }
+
+ if( rc!=SQLITE_OK ){
+ *ppTokApi = 0;
+ *ppTok = 0;
+ }
+
+ return rc;
+}
+
+static void fts5ModuleDestroy(void *pCtx){
+ Fts5TokenizerModule *pTok, *pNextTok;
+ Fts5Auxiliary *pAux, *pNextAux;
+ Fts5Global *pGlobal = (Fts5Global*)pCtx;
+
+ for(pAux=pGlobal->pAux; pAux; pAux=pNextAux){
+ pNextAux = pAux->pNext;
+ if( pAux->xDestroy ) pAux->xDestroy(pAux->pUserData);
+ sqlite3_free(pAux);
+ }
+
+ for(pTok=pGlobal->pTok; pTok; pTok=pNextTok){
+ pNextTok = pTok->pNext;
+ if( pTok->xDestroy ) pTok->xDestroy(pTok->pUserData);
+ sqlite3_free(pTok);
+ }
+
+ sqlite3_free(pGlobal);
+}
+
+static void fts5Fts5Func(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apUnused /* Function arguments */
+){
+ Fts5Global *pGlobal = (Fts5Global*)sqlite3_user_data(pCtx);
+ char buf[8];
+ UNUSED_PARAM2(nArg, apUnused);
+ assert( nArg==0 );
+ assert( sizeof(buf)>=sizeof(pGlobal) );
+ memcpy(buf, (void*)&pGlobal, sizeof(pGlobal));
+ sqlite3_result_blob(pCtx, buf, sizeof(pGlobal), SQLITE_TRANSIENT);
+}
+
+/*
+** Implementation of fts5_source_id() function.
+*/
+static void fts5SourceIdFunc(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apUnused /* Function arguments */
+){
+ assert( nArg==0 );
+ UNUSED_PARAM2(nArg, apUnused);
+ sqlite3_result_text(pCtx, "fts5: 2016-11-04 12:08:49 1136863c76576110e710dd5d69ab6bf347c65e36", -1, SQLITE_TRANSIENT);
+}
+
+static int fts5Init(sqlite3 *db){
+ static const sqlite3_module fts5Mod = {
+ /* iVersion */ 2,
+ /* xCreate */ fts5CreateMethod,
+ /* xConnect */ fts5ConnectMethod,
+ /* xBestIndex */ fts5BestIndexMethod,
+ /* xDisconnect */ fts5DisconnectMethod,
+ /* xDestroy */ fts5DestroyMethod,
+ /* xOpen */ fts5OpenMethod,
+ /* xClose */ fts5CloseMethod,
+ /* xFilter */ fts5FilterMethod,
+ /* xNext */ fts5NextMethod,
+ /* xEof */ fts5EofMethod,
+ /* xColumn */ fts5ColumnMethod,
+ /* xRowid */ fts5RowidMethod,
+ /* xUpdate */ fts5UpdateMethod,
+ /* xBegin */ fts5BeginMethod,
+ /* xSync */ fts5SyncMethod,
+ /* xCommit */ fts5CommitMethod,
+ /* xRollback */ fts5RollbackMethod,
+ /* xFindFunction */ fts5FindFunctionMethod,
+ /* xRename */ fts5RenameMethod,
+ /* xSavepoint */ fts5SavepointMethod,
+ /* xRelease */ fts5ReleaseMethod,
+ /* xRollbackTo */ fts5RollbackToMethod,
+ };
+
+ int rc;
+ Fts5Global *pGlobal = 0;
+
+ pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
+ if( pGlobal==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ void *p = (void*)pGlobal;
+ memset(pGlobal, 0, sizeof(Fts5Global));
+ pGlobal->db = db;
+ pGlobal->api.iVersion = 2;
+ pGlobal->api.xCreateFunction = fts5CreateAux;
+ pGlobal->api.xCreateTokenizer = fts5CreateTokenizer;
+ pGlobal->api.xFindTokenizer = fts5FindTokenizer;
+ rc = sqlite3_create_module_v2(db, "fts5", &fts5Mod, p, fts5ModuleDestroy);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5IndexInit(db);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5ExprInit(pGlobal, db);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5AuxInit(&pGlobal->api);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5TokenizerInit(&pGlobal->api);
+ if( rc==SQLITE_OK ) rc = sqlite3Fts5VocabInit(pGlobal, db);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5", 0, SQLITE_UTF8, p, fts5Fts5Func, 0, 0
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5_source_id", 0, SQLITE_UTF8, p, fts5SourceIdFunc, 0, 0
+ );
+ }
+ }
+
+ /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
+ ** fts5_test_mi.c is compiled and linked into the executable. And call
+ ** its entry point to enable the matchinfo() demo. */
+#ifdef SQLITE_FTS5_ENABLE_TEST_MI
+ if( rc==SQLITE_OK ){
+ extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
+ rc = sqlite3Fts5TestRegisterMatchinfo(db);
+ }
+#endif
+
+ return rc;
+}
+
+/*
+** The following functions are used to register the module with SQLite. If
+** this module is being built as part of the SQLite core (SQLITE_CORE is
+** defined), then sqlite3_open() will call sqlite3Fts5Init() directly.
+**
+** Or, if this module is being built as a loadable extension,
+** sqlite3Fts5Init() is omitted and the two standard entry points
+** sqlite3_fts_init() and sqlite3_fts5_init() defined instead.
+*/
+#ifndef SQLITE_CORE
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_fts_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return fts5Init(db);
+}
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+SQLITE_API int sqlite3_fts5_init(
+ sqlite3 *db,
+ char **pzErrMsg,
+ const sqlite3_api_routines *pApi
+){
+ SQLITE_EXTENSION_INIT2(pApi);
+ (void)pzErrMsg; /* Unused parameter */
+ return fts5Init(db);
+}
+#else
+SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3 *db){
+ return fts5Init(db);
+}
+#endif
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+*/
+
+
+
+/* #include "fts5Int.h" */
+
+struct Fts5Storage {
+ Fts5Config *pConfig;
+ Fts5Index *pIndex;
+ int bTotalsValid; /* True if nTotalRow/aTotalSize[] are valid */
+ i64 nTotalRow; /* Total number of rows in FTS table */
+ i64 *aTotalSize; /* Total sizes of each column */
+ sqlite3_stmt *aStmt[11];
+};
+
+
+#if FTS5_STMT_SCAN_ASC!=0
+# error "FTS5_STMT_SCAN_ASC mismatch"
+#endif
+#if FTS5_STMT_SCAN_DESC!=1
+# error "FTS5_STMT_SCAN_DESC mismatch"
+#endif
+#if FTS5_STMT_LOOKUP!=2
+# error "FTS5_STMT_LOOKUP mismatch"
+#endif
+
+#define FTS5_STMT_INSERT_CONTENT 3
+#define FTS5_STMT_REPLACE_CONTENT 4
+#define FTS5_STMT_DELETE_CONTENT 5
+#define FTS5_STMT_REPLACE_DOCSIZE 6
+#define FTS5_STMT_DELETE_DOCSIZE 7
+#define FTS5_STMT_LOOKUP_DOCSIZE 8
+#define FTS5_STMT_REPLACE_CONFIG 9
+#define FTS5_STMT_SCAN 10
+
+/*
+** Prepare the two insert statements - Fts5Storage.pInsertContent and
+** Fts5Storage.pInsertDocsize - if they have not already been prepared.
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageGetStmt(
+ Fts5Storage *p, /* Storage handle */
+ int eStmt, /* FTS5_STMT_XXX constant */
+ sqlite3_stmt **ppStmt, /* OUT: Prepared statement handle */
+ char **pzErrMsg /* OUT: Error message (if any) */
+){
+ int rc = SQLITE_OK;
+
+ /* If there is no %_docsize table, there should be no requests for
+ ** statements to operate on it. */
+ assert( p->pConfig->bColumnsize || (
+ eStmt!=FTS5_STMT_REPLACE_DOCSIZE
+ && eStmt!=FTS5_STMT_DELETE_DOCSIZE
+ && eStmt!=FTS5_STMT_LOOKUP_DOCSIZE
+ ));
+
+ assert( eStmt>=0 && eStmt<ArraySize(p->aStmt) );
+ if( p->aStmt[eStmt]==0 ){
+ const char *azStmt[] = {
+ "SELECT %s FROM %s T WHERE T.%Q >= ? AND T.%Q <= ? ORDER BY T.%Q ASC",
+ "SELECT %s FROM %s T WHERE T.%Q <= ? AND T.%Q >= ? ORDER BY T.%Q DESC",
+ "SELECT %s FROM %s T WHERE T.%Q=?", /* LOOKUP */
+
+ "INSERT INTO %Q.'%q_content' VALUES(%s)", /* INSERT_CONTENT */
+ "REPLACE INTO %Q.'%q_content' VALUES(%s)", /* REPLACE_CONTENT */
+ "DELETE FROM %Q.'%q_content' WHERE id=?", /* DELETE_CONTENT */
+ "REPLACE INTO %Q.'%q_docsize' VALUES(?,?)", /* REPLACE_DOCSIZE */
+ "DELETE FROM %Q.'%q_docsize' WHERE id=?", /* DELETE_DOCSIZE */
+
+ "SELECT sz FROM %Q.'%q_docsize' WHERE id=?", /* LOOKUP_DOCSIZE */
+
+ "REPLACE INTO %Q.'%q_config' VALUES(?,?)", /* REPLACE_CONFIG */
+ "SELECT %s FROM %s AS T", /* SCAN */
+ };
+ Fts5Config *pC = p->pConfig;
+ char *zSql = 0;
+
+ switch( eStmt ){
+ case FTS5_STMT_SCAN:
+ zSql = sqlite3_mprintf(azStmt[eStmt],
+ pC->zContentExprlist, pC->zContent
+ );
+ break;
+
+ case FTS5_STMT_SCAN_ASC:
+ case FTS5_STMT_SCAN_DESC:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zContentExprlist,
+ pC->zContent, pC->zContentRowid, pC->zContentRowid,
+ pC->zContentRowid
+ );
+ break;
+
+ case FTS5_STMT_LOOKUP:
+ zSql = sqlite3_mprintf(azStmt[eStmt],
+ pC->zContentExprlist, pC->zContent, pC->zContentRowid
+ );
+ break;
+
+ case FTS5_STMT_INSERT_CONTENT:
+ case FTS5_STMT_REPLACE_CONTENT: {
+ int nCol = pC->nCol + 1;
+ char *zBind;
+ int i;
+
+ zBind = sqlite3_malloc(1 + nCol*2);
+ if( zBind ){
+ for(i=0; i<nCol; i++){
+ zBind[i*2] = '?';
+ zBind[i*2 + 1] = ',';
+ }
+ zBind[i*2-1] = '\0';
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName, zBind);
+ sqlite3_free(zBind);
+ }
+ break;
+ }
+
+ default:
+ zSql = sqlite3_mprintf(azStmt[eStmt], pC->zDb, pC->zName);
+ break;
+ }
+
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_prepare_v2(pC->db, zSql, -1, &p->aStmt[eStmt], 0);
+ sqlite3_free(zSql);
+ if( rc!=SQLITE_OK && pzErrMsg ){
+ *pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
+ }
+ }
+ }
+
+ *ppStmt = p->aStmt[eStmt];
+ sqlite3_reset(*ppStmt);
+ return rc;
+}
+
+
+static int fts5ExecPrintf(
+ sqlite3 *db,
+ char **pzErr,
+ const char *zFormat,
+ ...
+){
+ int rc;
+ va_list ap; /* ... printf arguments */
+ char *zSql;
+
+ va_start(ap, zFormat);
+ zSql = sqlite3_vmprintf(zFormat, ap);
+
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ rc = sqlite3_exec(db, zSql, 0, 0, pzErr);
+ sqlite3_free(zSql);
+ }
+
+ va_end(ap);
+ return rc;
+}
+
+/*
+** Drop all shadow tables. Return SQLITE_OK if successful or an SQLite error
+** code otherwise.
+*/
+static int sqlite3Fts5DropAll(Fts5Config *pConfig){
+ int rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_data';"
+ "DROP TABLE IF EXISTS %Q.'%q_idx';"
+ "DROP TABLE IF EXISTS %Q.'%q_config';",
+ pConfig->zDb, pConfig->zName,
+ pConfig->zDb, pConfig->zName,
+ pConfig->zDb, pConfig->zName
+ );
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_docsize';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DROP TABLE IF EXISTS %Q.'%q_content';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+ return rc;
+}
+
+static void fts5StorageRenameOne(
+ Fts5Config *pConfig, /* Current FTS5 configuration */
+ int *pRc, /* IN/OUT: Error code */
+ const char *zTail, /* Tail of table name e.g. "data", "config" */
+ const char *zName /* New name of FTS5 table */
+){
+ if( *pRc==SQLITE_OK ){
+ *pRc = fts5ExecPrintf(pConfig->db, 0,
+ "ALTER TABLE %Q.'%q_%s' RENAME TO '%q_%s';",
+ pConfig->zDb, pConfig->zName, zTail, zName, zTail
+ );
+ }
+}
+
+static int sqlite3Fts5StorageRename(Fts5Storage *pStorage, const char *zName){
+ Fts5Config *pConfig = pStorage->pConfig;
+ int rc = sqlite3Fts5StorageSync(pStorage, 1);
+
+ fts5StorageRenameOne(pConfig, &rc, "data", zName);
+ fts5StorageRenameOne(pConfig, &rc, "idx", zName);
+ fts5StorageRenameOne(pConfig, &rc, "config", zName);
+ if( pConfig->bColumnsize ){
+ fts5StorageRenameOne(pConfig, &rc, "docsize", zName);
+ }
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ fts5StorageRenameOne(pConfig, &rc, "content", zName);
+ }
+ return rc;
+}
+
+/*
+** Create the shadow table named zPost, with definition zDefn. Return
+** SQLITE_OK if successful, or an SQLite error code otherwise.
+*/
+static int sqlite3Fts5CreateTable(
+ Fts5Config *pConfig, /* FTS5 configuration */
+ const char *zPost, /* Shadow table to create (e.g. "content") */
+ const char *zDefn, /* Columns etc. for shadow table */
+ int bWithout, /* True for without rowid */
+ char **pzErr /* OUT: Error message */
+){
+ int rc;
+ char *zErr = 0;
+
+ rc = fts5ExecPrintf(pConfig->db, &zErr, "CREATE TABLE %Q.'%q_%q'(%s)%s",
+ pConfig->zDb, pConfig->zName, zPost, zDefn,
+#ifndef SQLITE_FTS5_NO_WITHOUT_ROWID
+ bWithout?" WITHOUT ROWID":
+#endif
+ ""
+ );
+ if( zErr ){
+ *pzErr = sqlite3_mprintf(
+ "fts5: error creating shadow table %q_%s: %s",
+ pConfig->zName, zPost, zErr
+ );
+ sqlite3_free(zErr);
+ }
+
+ return rc;
+}
+
+/*
+** Open a new Fts5Index handle. If the bCreate argument is true, create
+** and initialize the underlying tables
+**
+** If successful, set *pp to point to the new object and return SQLITE_OK.
+** Otherwise, set *pp to NULL and return an SQLite error code.
+*/
+static int sqlite3Fts5StorageOpen(
+ Fts5Config *pConfig,
+ Fts5Index *pIndex,
+ int bCreate,
+ Fts5Storage **pp,
+ char **pzErr /* OUT: Error message */
+){
+ int rc = SQLITE_OK;
+ Fts5Storage *p; /* New object */
+ int nByte; /* Bytes of space to allocate */
+
+ nByte = sizeof(Fts5Storage) /* Fts5Storage object */
+ + pConfig->nCol * sizeof(i64); /* Fts5Storage.aTotalSize[] */
+ *pp = p = (Fts5Storage*)sqlite3_malloc(nByte);
+ if( !p ) return SQLITE_NOMEM;
+
+ memset(p, 0, nByte);
+ p->aTotalSize = (i64*)&p[1];
+ p->pConfig = pConfig;
+ p->pIndex = pIndex;
+
+ if( bCreate ){
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ int nDefn = 32 + pConfig->nCol*10;
+ char *zDefn = sqlite3_malloc(32 + pConfig->nCol * 10);
+ if( zDefn==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ int iOff;
+ sqlite3_snprintf(nDefn, zDefn, "id INTEGER PRIMARY KEY");
+ iOff = (int)strlen(zDefn);
+ for(i=0; i<pConfig->nCol; i++){
+ sqlite3_snprintf(nDefn-iOff, &zDefn[iOff], ", c%d", i);
+ iOff += (int)strlen(&zDefn[iOff]);
+ }
+ rc = sqlite3Fts5CreateTable(pConfig, "content", zDefn, 0, pzErr);
+ }
+ sqlite3_free(zDefn);
+ }
+
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "docsize", "id INTEGER PRIMARY KEY, sz BLOB", 0, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5CreateTable(
+ pConfig, "config", "k PRIMARY KEY, v", 1, pzErr
+ );
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
+ }
+ }
+
+ if( rc ){
+ sqlite3Fts5StorageClose(p);
+ *pp = 0;
+ }
+ return rc;
+}
+
+/*
+** Close a handle opened by an earlier call to sqlite3Fts5StorageOpen().
+*/
+static int sqlite3Fts5StorageClose(Fts5Storage *p){
+ int rc = SQLITE_OK;
+ if( p ){
+ int i;
+
+ /* Finalize all SQL statements */
+ for(i=0; i<ArraySize(p->aStmt); i++){
+ sqlite3_finalize(p->aStmt[i]);
+ }
+
+ sqlite3_free(p);
+ }
+ return rc;
+}
+
+typedef struct Fts5InsertCtx Fts5InsertCtx;
+struct Fts5InsertCtx {
+ Fts5Storage *pStorage;
+ int iCol;
+ int szCol; /* Size of column value in tokens */
+};
+
+/*
+** Tokenization callback used when inserting tokens into the FTS index.
+*/
+static int fts5StorageInsertCallback(
+ void *pContext, /* Pointer to Fts5InsertCtx object */
+ int tflags,
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ Fts5InsertCtx *pCtx = (Fts5InsertCtx*)pContext;
+ Fts5Index *pIdx = pCtx->pStorage->pIndex;
+ UNUSED_PARAM2(iUnused1, iUnused2);
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
+ pCtx->szCol++;
+ }
+ return sqlite3Fts5IndexWrite(pIdx, pCtx->iCol, pCtx->szCol-1, pToken, nToken);
+}
+
+/*
+** If a row with rowid iDel is present in the %_content table, add the
+** delete-markers to the FTS index necessary to delete it. Do not actually
+** remove the %_content row at this time though.
+*/
+static int fts5StorageDeleteFromIndex(
+ Fts5Storage *p,
+ i64 iDel,
+ sqlite3_value **apVal
+){
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pSeek = 0; /* SELECT to read row iDel from %_data */
+ int rc; /* Return code */
+ int rc2; /* sqlite3_reset() return code */
+ int iCol;
+ Fts5InsertCtx ctx;
+
+ if( apVal==0 ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP, &pSeek, 0);
+ if( rc!=SQLITE_OK ) return rc;
+ sqlite3_bind_int64(pSeek, 1, iDel);
+ if( sqlite3_step(pSeek)!=SQLITE_ROW ){
+ return sqlite3_reset(pSeek);
+ }
+ }
+
+ ctx.pStorage = p;
+ ctx.iCol = -1;
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 1, iDel);
+ for(iCol=1; rc==SQLITE_OK && iCol<=pConfig->nCol; iCol++){
+ if( pConfig->abUnindexed[iCol-1]==0 ){
+ const char *zText;
+ int nText;
+ if( pSeek ){
+ zText = (const char*)sqlite3_column_text(pSeek, iCol);
+ nText = sqlite3_column_bytes(pSeek, iCol);
+ }else{
+ zText = (const char*)sqlite3_value_text(apVal[iCol-1]);
+ nText = sqlite3_value_bytes(apVal[iCol-1]);
+ }
+ ctx.szCol = 0;
+ rc = sqlite3Fts5Tokenize(pConfig, FTS5_TOKENIZE_DOCUMENT,
+ zText, nText, (void*)&ctx, fts5StorageInsertCallback
+ );
+ p->aTotalSize[iCol-1] -= (i64)ctx.szCol;
+ }
+ }
+ p->nTotalRow--;
+
+ rc2 = sqlite3_reset(pSeek);
+ if( rc==SQLITE_OK ) rc = rc2;
+ return rc;
+}
+
+
+/*
+** Insert a record into the %_docsize table. Specifically, do:
+**
+** INSERT OR REPLACE INTO %_docsize(id, sz) VALUES(iRowid, pBuf);
+**
+** If there is no %_docsize table (as happens if the columnsize=0 option
+** is specified when the FTS5 table is created), this function is a no-op.
+*/
+static int fts5StorageInsertDocsize(
+ Fts5Storage *p, /* Storage module to write to */
+ i64 iRowid, /* id value */
+ Fts5Buffer *pBuf /* sz value */
+){
+ int rc = SQLITE_OK;
+ if( p->pConfig->bColumnsize ){
+ sqlite3_stmt *pReplace = 0;
+ rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pReplace, 1, iRowid);
+ sqlite3_bind_blob(pReplace, 2, pBuf->p, pBuf->n, SQLITE_STATIC);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ }
+ return rc;
+}
+
+/*
+** Load the contents of the "averages" record from disk into the
+** p->nTotalRow and p->aTotalSize[] variables. If successful, and if
+** argument bCache is true, set the p->bTotalsValid flag to indicate
+** that the contents of aTotalSize[] and nTotalRow are valid until
+** further notice.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageLoadTotals(Fts5Storage *p, int bCache){
+ int rc = SQLITE_OK;
+ if( p->bTotalsValid==0 ){
+ rc = sqlite3Fts5IndexGetAverages(p->pIndex, &p->nTotalRow, p->aTotalSize);
+ p->bTotalsValid = bCache;
+ }
+ return rc;
+}
+
+/*
+** Store the current contents of the p->nTotalRow and p->aTotalSize[]
+** variables in the "averages" record on disk.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** occurs.
+*/
+static int fts5StorageSaveTotals(Fts5Storage *p){
+ int nCol = p->pConfig->nCol;
+ int i;
+ Fts5Buffer buf;
+ int rc = SQLITE_OK;
+ memset(&buf, 0, sizeof(buf));
+
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, p->nTotalRow);
+ for(i=0; i<nCol; i++){
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, p->aTotalSize[i]);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexSetAverages(p->pIndex, buf.p, buf.n);
+ }
+ sqlite3_free(buf.p);
+
+ return rc;
+}
+
+/*
+** Remove a row from the FTS table.
+*/
+static int sqlite3Fts5StorageDelete(Fts5Storage *p, i64 iDel, sqlite3_value **apVal){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+ sqlite3_stmt *pDel = 0;
+
+ assert( pConfig->eContent!=FTS5_CONTENT_NORMAL || apVal==0 );
+ rc = fts5StorageLoadTotals(p, 1);
+
+ /* Delete the index records */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageDeleteFromIndex(p, iDel, apVal);
+ }
+
+ /* Delete the %_docsize record */
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_DOCSIZE, &pDel, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iDel);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
+ }
+ }
+
+ /* Delete the %_content record */
+ if( pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_DELETE_CONTENT, &pDel, 0);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_int64(pDel, 1, iDel);
+ sqlite3_step(pDel);
+ rc = sqlite3_reset(pDel);
+ }
+ }
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+
+ return rc;
+}
+
+/*
+** Delete all entries in the FTS5 index.
+*/
+static int sqlite3Fts5StorageDeleteAll(Fts5Storage *p){
+ Fts5Config *pConfig = p->pConfig;
+ int rc;
+
+ /* Delete the contents of the %_data and %_docsize tables. */
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DELETE FROM %Q.'%q_data';"
+ "DELETE FROM %Q.'%q_idx';",
+ pConfig->zDb, pConfig->zName,
+ pConfig->zDb, pConfig->zName
+ );
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ rc = fts5ExecPrintf(pConfig->db, 0,
+ "DELETE FROM %Q.'%q_docsize';",
+ pConfig->zDb, pConfig->zName
+ );
+ }
+
+ /* Reinitialize the %_data table. This call creates the initial structure
+ ** and averages records. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexReinit(p->pIndex);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5StorageConfigValue(p, "version", 0, FTS5_CURRENT_VERSION);
+ }
+ return rc;
+}
+
+static int sqlite3Fts5StorageRebuild(Fts5Storage *p){
+ Fts5Buffer buf = {0,0,0};
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pScan = 0;
+ Fts5InsertCtx ctx;
+ int rc;
+
+ memset(&ctx, 0, sizeof(Fts5InsertCtx));
+ ctx.pStorage = p;
+ rc = sqlite3Fts5StorageDeleteAll(p);
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageLoadTotals(p, 1);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ }
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
+ i64 iRowid = sqlite3_column_int64(pScan, 0);
+
+ sqlite3Fts5BufferZero(&buf);
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
+ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
+ ctx.szCol = 0;
+ if( pConfig->abUnindexed[ctx.iCol]==0 ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT,
+ (const char*)sqlite3_column_text(pScan, ctx.iCol+1),
+ sqlite3_column_bytes(pScan, ctx.iCol+1),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ }
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
+ p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
+ }
+ p->nTotalRow++;
+
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageInsertDocsize(p, iRowid, &buf);
+ }
+ }
+ sqlite3_free(buf.p);
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+ return rc;
+}
+
+static int sqlite3Fts5StorageOptimize(Fts5Storage *p){
+ return sqlite3Fts5IndexOptimize(p->pIndex);
+}
+
+static int sqlite3Fts5StorageMerge(Fts5Storage *p, int nMerge){
+ return sqlite3Fts5IndexMerge(p->pIndex, nMerge);
+}
+
+static int sqlite3Fts5StorageReset(Fts5Storage *p){
+ return sqlite3Fts5IndexReset(p->pIndex);
+}
+
+/*
+** Allocate a new rowid. This is used for "external content" tables when
+** a NULL value is inserted into the rowid column. The new rowid is allocated
+** by inserting a dummy row into the %_docsize table. The dummy will be
+** overwritten later.
+**
+** If the %_docsize table does not exist, SQLITE_MISMATCH is returned. In
+** this case the user is required to provide a rowid explicitly.
+*/
+static int fts5StorageNewRowid(Fts5Storage *p, i64 *piRowid){
+ int rc = SQLITE_MISMATCH;
+ if( p->pConfig->bColumnsize ){
+ sqlite3_stmt *pReplace = 0;
+ rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_DOCSIZE, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_null(pReplace, 1);
+ sqlite3_bind_null(pReplace, 2);
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ if( rc==SQLITE_OK ){
+ *piRowid = sqlite3_last_insert_rowid(p->pConfig->db);
+ }
+ }
+ return rc;
+}
+
+/*
+** Insert a new row into the FTS content table.
+*/
+static int sqlite3Fts5StorageContentInsert(
+ Fts5Storage *p,
+ sqlite3_value **apVal,
+ i64 *piRowid
+){
+ Fts5Config *pConfig = p->pConfig;
+ int rc = SQLITE_OK;
+
+ /* Insert the new row into the %_content table. */
+ if( pConfig->eContent!=FTS5_CONTENT_NORMAL ){
+ if( sqlite3_value_type(apVal[1])==SQLITE_INTEGER ){
+ *piRowid = sqlite3_value_int64(apVal[1]);
+ }else{
+ rc = fts5StorageNewRowid(p, piRowid);
+ }
+ }else{
+ sqlite3_stmt *pInsert = 0; /* Statement to write %_content table */
+ int i; /* Counter variable */
+ rc = fts5StorageGetStmt(p, FTS5_STMT_INSERT_CONTENT, &pInsert, 0);
+ for(i=1; rc==SQLITE_OK && i<=pConfig->nCol+1; i++){
+ rc = sqlite3_bind_value(pInsert, i, apVal[i]);
+ }
+ if( rc==SQLITE_OK ){
+ sqlite3_step(pInsert);
+ rc = sqlite3_reset(pInsert);
+ }
+ *piRowid = sqlite3_last_insert_rowid(pConfig->db);
+ }
+
+ return rc;
+}
+
+/*
+** Insert new entries into the FTS index and %_docsize table.
+*/
+static int sqlite3Fts5StorageIndexInsert(
+ Fts5Storage *p,
+ sqlite3_value **apVal,
+ i64 iRowid
+){
+ Fts5Config *pConfig = p->pConfig;
+ int rc = SQLITE_OK; /* Return code */
+ Fts5InsertCtx ctx; /* Tokenization callback context object */
+ Fts5Buffer buf; /* Buffer used to build up %_docsize blob */
+
+ memset(&buf, 0, sizeof(Fts5Buffer));
+ ctx.pStorage = p;
+ rc = fts5StorageLoadTotals(p, 1);
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
+ }
+ for(ctx.iCol=0; rc==SQLITE_OK && ctx.iCol<pConfig->nCol; ctx.iCol++){
+ ctx.szCol = 0;
+ if( pConfig->abUnindexed[ctx.iCol]==0 ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT,
+ (const char*)sqlite3_value_text(apVal[ctx.iCol+2]),
+ sqlite3_value_bytes(apVal[ctx.iCol+2]),
+ (void*)&ctx,
+ fts5StorageInsertCallback
+ );
+ }
+ sqlite3Fts5BufferAppendVarint(&rc, &buf, ctx.szCol);
+ p->aTotalSize[ctx.iCol] += (i64)ctx.szCol;
+ }
+ p->nTotalRow++;
+
+ /* Write the %_docsize record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageInsertDocsize(p, iRowid, &buf);
+ }
+ sqlite3_free(buf.p);
+
+ /* Write the averages record */
+ if( rc==SQLITE_OK ){
+ rc = fts5StorageSaveTotals(p);
+ }
+
+ return rc;
+}
+
+static int fts5StorageCount(Fts5Storage *p, const char *zSuffix, i64 *pnRow){
+ Fts5Config *pConfig = p->pConfig;
+ char *zSql;
+ int rc;
+
+ zSql = sqlite3_mprintf("SELECT count(*) FROM %Q.'%q_%s'",
+ pConfig->zDb, pConfig->zName, zSuffix
+ );
+ if( zSql==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pCnt = 0;
+ rc = sqlite3_prepare_v2(pConfig->db, zSql, -1, &pCnt, 0);
+ if( rc==SQLITE_OK ){
+ if( SQLITE_ROW==sqlite3_step(pCnt) ){
+ *pnRow = sqlite3_column_int64(pCnt, 0);
+ }
+ rc = sqlite3_finalize(pCnt);
+ }
+ }
+
+ sqlite3_free(zSql);
+ return rc;
+}
+
+/*
+** Context object used by sqlite3Fts5StorageIntegrity().
+*/
+typedef struct Fts5IntegrityCtx Fts5IntegrityCtx;
+struct Fts5IntegrityCtx {
+ i64 iRowid;
+ int iCol;
+ int szCol;
+ u64 cksum;
+ Fts5Termset *pTermset;
+ Fts5Config *pConfig;
+};
+
+
+/*
+** Tokenization callback used by integrity check.
+*/
+static int fts5StorageIntegrityCallback(
+ void *pContext, /* Pointer to Fts5IntegrityCtx object */
+ int tflags,
+ const char *pToken, /* Buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iUnused1, /* Start offset of token */
+ int iUnused2 /* End offset of token */
+){
+ Fts5IntegrityCtx *pCtx = (Fts5IntegrityCtx*)pContext;
+ Fts5Termset *pTermset = pCtx->pTermset;
+ int bPresent;
+ int ii;
+ int rc = SQLITE_OK;
+ int iPos;
+ int iCol;
+
+ UNUSED_PARAM2(iUnused1, iUnused2);
+ if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;
+
+ if( (tflags & FTS5_TOKEN_COLOCATED)==0 || pCtx->szCol==0 ){
+ pCtx->szCol++;
+ }
+
+ switch( pCtx->pConfig->eDetail ){
+ case FTS5_DETAIL_FULL:
+ iPos = pCtx->szCol-1;
+ iCol = pCtx->iCol;
+ break;
+
+ case FTS5_DETAIL_COLUMNS:
+ iPos = pCtx->iCol;
+ iCol = 0;
+ break;
+
+ default:
+ assert( pCtx->pConfig->eDetail==FTS5_DETAIL_NONE );
+ iPos = 0;
+ iCol = 0;
+ break;
+ }
+
+ rc = sqlite3Fts5TermsetAdd(pTermset, 0, pToken, nToken, &bPresent);
+ if( rc==SQLITE_OK && bPresent==0 ){
+ pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
+ pCtx->iRowid, iCol, iPos, 0, pToken, nToken
+ );
+ }
+
+ for(ii=0; rc==SQLITE_OK && ii<pCtx->pConfig->nPrefix; ii++){
+ const int nChar = pCtx->pConfig->aPrefix[ii];
+ int nByte = sqlite3Fts5IndexCharlenToBytelen(pToken, nToken, nChar);
+ if( nByte ){
+ rc = sqlite3Fts5TermsetAdd(pTermset, ii+1, pToken, nByte, &bPresent);
+ if( bPresent==0 ){
+ pCtx->cksum ^= sqlite3Fts5IndexEntryCksum(
+ pCtx->iRowid, iCol, iPos, ii+1, pToken, nByte
+ );
+ }
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Check that the contents of the FTS index match that of the %_content
+** table. Return SQLITE_OK if they do, or SQLITE_CORRUPT if not. Return
+** some other SQLite error code if an error occurs while attempting to
+** determine this.
+*/
+static int sqlite3Fts5StorageIntegrity(Fts5Storage *p){
+ Fts5Config *pConfig = p->pConfig;
+ int rc; /* Return code */
+ int *aColSize; /* Array of size pConfig->nCol */
+ i64 *aTotalSize; /* Array of size pConfig->nCol */
+ Fts5IntegrityCtx ctx;
+ sqlite3_stmt *pScan;
+
+ memset(&ctx, 0, sizeof(Fts5IntegrityCtx));
+ ctx.pConfig = p->pConfig;
+ aTotalSize = (i64*)sqlite3_malloc(pConfig->nCol * (sizeof(int)+sizeof(i64)));
+ if( !aTotalSize ) return SQLITE_NOMEM;
+ aColSize = (int*)&aTotalSize[pConfig->nCol];
+ memset(aTotalSize, 0, sizeof(i64) * pConfig->nCol);
+
+ /* Generate the expected index checksum based on the contents of the
+ ** %_content table. This block stores the checksum in ctx.cksum. */
+ rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
+ if( rc==SQLITE_OK ){
+ int rc2;
+ while( SQLITE_ROW==sqlite3_step(pScan) ){
+ int i;
+ ctx.iRowid = sqlite3_column_int64(pScan, 0);
+ ctx.szCol = 0;
+ if( pConfig->bColumnsize ){
+ rc = sqlite3Fts5StorageDocsize(p, ctx.iRowid, aColSize);
+ }
+ if( rc==SQLITE_OK && pConfig->eDetail==FTS5_DETAIL_NONE ){
+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
+ }
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( pConfig->abUnindexed[i] ) continue;
+ ctx.iCol = i;
+ ctx.szCol = 0;
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ rc = sqlite3Fts5TermsetNew(&ctx.pTermset);
+ }
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5Tokenize(pConfig,
+ FTS5_TOKENIZE_DOCUMENT,
+ (const char*)sqlite3_column_text(pScan, i+1),
+ sqlite3_column_bytes(pScan, i+1),
+ (void*)&ctx,
+ fts5StorageIntegrityCallback
+ );
+ }
+ if( rc==SQLITE_OK && pConfig->bColumnsize && ctx.szCol!=aColSize[i] ){
+ rc = FTS5_CORRUPT;
+ }
+ aTotalSize[i] += ctx.szCol;
+ if( pConfig->eDetail==FTS5_DETAIL_COLUMNS ){
+ sqlite3Fts5TermsetFree(ctx.pTermset);
+ ctx.pTermset = 0;
+ }
+ }
+ sqlite3Fts5TermsetFree(ctx.pTermset);
+ ctx.pTermset = 0;
+
+ if( rc!=SQLITE_OK ) break;
+ }
+ rc2 = sqlite3_reset(pScan);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
+
+ /* Test that the "totals" (sometimes called "averages") record looks Ok */
+ if( rc==SQLITE_OK ){
+ int i;
+ rc = fts5StorageLoadTotals(p, 0);
+ for(i=0; rc==SQLITE_OK && i<pConfig->nCol; i++){
+ if( p->aTotalSize[i]!=aTotalSize[i] ) rc = FTS5_CORRUPT;
+ }
+ }
+
+ /* Check that the %_docsize and %_content tables contain the expected
+ ** number of rows. */
+ if( rc==SQLITE_OK && pConfig->eContent==FTS5_CONTENT_NORMAL ){
+ i64 nRow = 0;
+ rc = fts5StorageCount(p, "content", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
+ if( rc==SQLITE_OK && pConfig->bColumnsize ){
+ i64 nRow = 0;
+ rc = fts5StorageCount(p, "docsize", &nRow);
+ if( rc==SQLITE_OK && nRow!=p->nTotalRow ) rc = FTS5_CORRUPT;
+ }
+
+ /* Pass the expected checksum down to the FTS index module. It will
+ ** verify, amongst other things, that it matches the checksum generated by
+ ** inspecting the index itself. */
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexIntegrityCheck(p->pIndex, ctx.cksum);
+ }
+
+ sqlite3_free(aTotalSize);
+ return rc;
+}
+
+/*
+** Obtain an SQLite statement handle that may be used to read data from the
+** %_content table.
+*/
+static int sqlite3Fts5StorageStmt(
+ Fts5Storage *p,
+ int eStmt,
+ sqlite3_stmt **pp,
+ char **pzErrMsg
+){
+ int rc;
+ assert( eStmt==FTS5_STMT_SCAN_ASC
+ || eStmt==FTS5_STMT_SCAN_DESC
+ || eStmt==FTS5_STMT_LOOKUP
+ );
+ rc = fts5StorageGetStmt(p, eStmt, pp, pzErrMsg);
+ if( rc==SQLITE_OK ){
+ assert( p->aStmt[eStmt]==*pp );
+ p->aStmt[eStmt] = 0;
+ }
+ return rc;
+}
+
+/*
+** Release an SQLite statement handle obtained via an earlier call to
+** sqlite3Fts5StorageStmt(). The eStmt parameter passed to this function
+** must match that passed to the sqlite3Fts5StorageStmt() call.
+*/
+static void sqlite3Fts5StorageStmtRelease(
+ Fts5Storage *p,
+ int eStmt,
+ sqlite3_stmt *pStmt
+){
+ assert( eStmt==FTS5_STMT_SCAN_ASC
+ || eStmt==FTS5_STMT_SCAN_DESC
+ || eStmt==FTS5_STMT_LOOKUP
+ );
+ if( p->aStmt[eStmt]==0 ){
+ sqlite3_reset(pStmt);
+ p->aStmt[eStmt] = pStmt;
+ }else{
+ sqlite3_finalize(pStmt);
+ }
+}
+
+static int fts5StorageDecodeSizeArray(
+ int *aCol, int nCol, /* Array to populate */
+ const u8 *aBlob, int nBlob /* Record to read varints from */
+){
+ int i;
+ int iOff = 0;
+ for(i=0; i<nCol; i++){
+ if( iOff>=nBlob ) return 1;
+ iOff += fts5GetVarint32(&aBlob[iOff], aCol[i]);
+ }
+ return (iOff!=nBlob);
+}
+
+/*
+** Argument aCol points to an array of integers containing one entry for
+** each table column. This function reads the %_docsize record for the
+** specified rowid and populates aCol[] with the results.
+**
+** An SQLite error code is returned if an error occurs, or SQLITE_OK
+** otherwise.
+*/
+static int sqlite3Fts5StorageDocsize(Fts5Storage *p, i64 iRowid, int *aCol){
+ int nCol = p->pConfig->nCol; /* Number of user columns in table */
+ sqlite3_stmt *pLookup = 0; /* Statement to query %_docsize */
+ int rc; /* Return Code */
+
+ assert( p->pConfig->bColumnsize );
+ rc = fts5StorageGetStmt(p, FTS5_STMT_LOOKUP_DOCSIZE, &pLookup, 0);
+ if( rc==SQLITE_OK ){
+ int bCorrupt = 1;
+ sqlite3_bind_int64(pLookup, 1, iRowid);
+ if( SQLITE_ROW==sqlite3_step(pLookup) ){
+ const u8 *aBlob = sqlite3_column_blob(pLookup, 0);
+ int nBlob = sqlite3_column_bytes(pLookup, 0);
+ if( 0==fts5StorageDecodeSizeArray(aCol, nCol, aBlob, nBlob) ){
+ bCorrupt = 0;
+ }
+ }
+ rc = sqlite3_reset(pLookup);
+ if( bCorrupt && rc==SQLITE_OK ){
+ rc = FTS5_CORRUPT;
+ }
+ }
+
+ return rc;
+}
+
+static int sqlite3Fts5StorageSize(Fts5Storage *p, int iCol, i64 *pnToken){
+ int rc = fts5StorageLoadTotals(p, 0);
+ if( rc==SQLITE_OK ){
+ *pnToken = 0;
+ if( iCol<0 ){
+ int i;
+ for(i=0; i<p->pConfig->nCol; i++){
+ *pnToken += p->aTotalSize[i];
+ }
+ }else if( iCol<p->pConfig->nCol ){
+ *pnToken = p->aTotalSize[iCol];
+ }else{
+ rc = SQLITE_RANGE;
+ }
+ }
+ return rc;
+}
+
+static int sqlite3Fts5StorageRowCount(Fts5Storage *p, i64 *pnRow){
+ int rc = fts5StorageLoadTotals(p, 0);
+ if( rc==SQLITE_OK ){
+ *pnRow = p->nTotalRow;
+ }
+ return rc;
+}
+
+/*
+** Flush any data currently held in-memory to disk.
+*/
+static int sqlite3Fts5StorageSync(Fts5Storage *p, int bCommit){
+ if( bCommit && p->bTotalsValid ){
+ int rc = fts5StorageSaveTotals(p);
+ p->bTotalsValid = 0;
+ if( rc!=SQLITE_OK ) return rc;
+ }
+ return sqlite3Fts5IndexSync(p->pIndex, bCommit);
+}
+
+static int sqlite3Fts5StorageRollback(Fts5Storage *p){
+ p->bTotalsValid = 0;
+ return sqlite3Fts5IndexRollback(p->pIndex);
+}
+
+static int sqlite3Fts5StorageConfigValue(
+ Fts5Storage *p,
+ const char *z,
+ sqlite3_value *pVal,
+ int iVal
+){
+ sqlite3_stmt *pReplace = 0;
+ int rc = fts5StorageGetStmt(p, FTS5_STMT_REPLACE_CONFIG, &pReplace, 0);
+ if( rc==SQLITE_OK ){
+ sqlite3_bind_text(pReplace, 1, z, -1, SQLITE_STATIC);
+ if( pVal ){
+ sqlite3_bind_value(pReplace, 2, pVal);
+ }else{
+ sqlite3_bind_int(pReplace, 2, iVal);
+ }
+ sqlite3_step(pReplace);
+ rc = sqlite3_reset(pReplace);
+ }
+ if( rc==SQLITE_OK && pVal ){
+ int iNew = p->pConfig->iCookie + 1;
+ rc = sqlite3Fts5IndexSetCookie(p->pIndex, iNew);
+ if( rc==SQLITE_OK ){
+ p->pConfig->iCookie = iNew;
+ }
+ }
+ return rc;
+}
+
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+*/
+
+
+/* #include "fts5Int.h" */
+
+/**************************************************************************
+** Start of ascii tokenizer implementation.
+*/
+
+/*
+** For tokenizers with no "unicode" modifier, the set of token characters
+** is the same as the set of ASCII range alphanumeric characters.
+*/
+static unsigned char aAsciiTokenChar[128] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00..0x0F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10..0x1F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20..0x2F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0x30..0x3F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x40..0x4F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x50..0x5F */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x60..0x6F */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 0x70..0x7F */
+};
+
+typedef struct AsciiTokenizer AsciiTokenizer;
+struct AsciiTokenizer {
+ unsigned char aTokenChar[128];
+};
+
+static void fts5AsciiAddExceptions(
+ AsciiTokenizer *p,
+ const char *zArg,
+ int bTokenChars
+){
+ int i;
+ for(i=0; zArg[i]; i++){
+ if( (zArg[i] & 0x80)==0 ){
+ p->aTokenChar[(int)zArg[i]] = (unsigned char)bTokenChars;
+ }
+ }
+}
+
+/*
+** Delete a "ascii" tokenizer.
+*/
+static void fts5AsciiDelete(Fts5Tokenizer *p){
+ sqlite3_free(p);
+}
+
+/*
+** Create an "ascii" tokenizer.
+*/
+static int fts5AsciiCreate(
+ void *pUnused,
+ const char **azArg, int nArg,
+ Fts5Tokenizer **ppOut
+){
+ int rc = SQLITE_OK;
+ AsciiTokenizer *p = 0;
+ UNUSED_PARAM(pUnused);
+ if( nArg%2 ){
+ rc = SQLITE_ERROR;
+ }else{
+ p = sqlite3_malloc(sizeof(AsciiTokenizer));
+ if( p==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ int i;
+ memset(p, 0, sizeof(AsciiTokenizer));
+ memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar));
+ for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
+ const char *zArg = azArg[i+1];
+ if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){
+ fts5AsciiAddExceptions(p, zArg, 1);
+ }else
+ if( 0==sqlite3_stricmp(azArg[i], "separators") ){
+ fts5AsciiAddExceptions(p, zArg, 0);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ }
+ if( rc!=SQLITE_OK ){
+ fts5AsciiDelete((Fts5Tokenizer*)p);
+ p = 0;
+ }
+ }
+ }
+
+ *ppOut = (Fts5Tokenizer*)p;
+ return rc;
+}
+
+
+static void asciiFold(char *aOut, const char *aIn, int nByte){
+ int i;
+ for(i=0; i<nByte; i++){
+ char c = aIn[i];
+ if( c>='A' && c<='Z' ) c += 32;
+ aOut[i] = c;
+ }
+}
+
+/*
+** Tokenize some text using the ascii tokenizer.
+*/
+static int fts5AsciiTokenize(
+ Fts5Tokenizer *pTokenizer,
+ void *pCtx,
+ int iUnused,
+ const char *pText, int nText,
+ int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
+){
+ AsciiTokenizer *p = (AsciiTokenizer*)pTokenizer;
+ int rc = SQLITE_OK;
+ int ie;
+ int is = 0;
+
+ char aFold[64];
+ int nFold = sizeof(aFold);
+ char *pFold = aFold;
+ unsigned char *a = p->aTokenChar;
+
+ UNUSED_PARAM(iUnused);
+
+ while( is<nText && rc==SQLITE_OK ){
+ int nByte;
+
+ /* Skip any leading divider characters. */
+ while( is<nText && ((pText[is]&0x80)==0 && a[(int)pText[is]]==0) ){
+ is++;
+ }
+ if( is==nText ) break;
+
+ /* Count the token characters */
+ ie = is+1;
+ while( ie<nText && ((pText[ie]&0x80) || a[(int)pText[ie]] ) ){
+ ie++;
+ }
+
+ /* Fold to lower case */
+ nByte = ie-is;
+ if( nByte>nFold ){
+ if( pFold!=aFold ) sqlite3_free(pFold);
+ pFold = sqlite3_malloc(nByte*2);
+ if( pFold==0 ){
+ rc = SQLITE_NOMEM;
+ break;
+ }
+ nFold = nByte*2;
+ }
+ asciiFold(pFold, &pText[is], nByte);
+
+ /* Invoke the token callback */
+ rc = xToken(pCtx, 0, pFold, nByte, is, ie);
+ is = ie+1;
+ }
+
+ if( pFold!=aFold ) sqlite3_free(pFold);
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ return rc;
+}
+
+/**************************************************************************
+** Start of unicode61 tokenizer implementation.
+*/
+
+
+/*
+** The following two macros - READ_UTF8 and WRITE_UTF8 - have been copied
+** from the sqlite3 source file utf.c. If this file is compiled as part
+** of the amalgamation, they are not required.
+*/
+#ifndef SQLITE_AMALGAMATION
+
+static const unsigned char sqlite3Utf8Trans1[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00,
+};
+
+#define READ_UTF8(zIn, zTerm, c) \
+ c = *(zIn++); \
+ if( c>=0xc0 ){ \
+ c = sqlite3Utf8Trans1[c-0xc0]; \
+ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \
+ c = (c<<6) + (0x3f & *(zIn++)); \
+ } \
+ if( c<0x80 \
+ || (c&0xFFFFF800)==0xD800 \
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \
+ }
+
+
+#define WRITE_UTF8(zOut, c) { \
+ if( c<0x00080 ){ \
+ *zOut++ = (unsigned char)(c&0xFF); \
+ } \
+ else if( c<0x00800 ){ \
+ *zOut++ = 0xC0 + (unsigned char)((c>>6)&0x1F); \
+ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
+ } \
+ else if( c<0x10000 ){ \
+ *zOut++ = 0xE0 + (unsigned char)((c>>12)&0x0F); \
+ *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
+ }else{ \
+ *zOut++ = 0xF0 + (unsigned char)((c>>18) & 0x07); \
+ *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F); \
+ *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F); \
+ *zOut++ = 0x80 + (unsigned char)(c & 0x3F); \
+ } \
+}
+
+#endif /* ifndef SQLITE_AMALGAMATION */
+
+typedef struct Unicode61Tokenizer Unicode61Tokenizer;
+struct Unicode61Tokenizer {
+ unsigned char aTokenChar[128]; /* ASCII range token characters */
+ char *aFold; /* Buffer to fold text into */
+ int nFold; /* Size of aFold[] in bytes */
+ int bRemoveDiacritic; /* True if remove_diacritics=1 is set */
+ int nException;
+ int *aiException;
+};
+
+static int fts5UnicodeAddExceptions(
+ Unicode61Tokenizer *p, /* Tokenizer object */
+ const char *z, /* Characters to treat as exceptions */
+ int bTokenChars /* 1 for 'tokenchars', 0 for 'separators' */
+){
+ int rc = SQLITE_OK;
+ int n = (int)strlen(z);
+ int *aNew;
+
+ if( n>0 ){
+ aNew = (int*)sqlite3_realloc(p->aiException, (n+p->nException)*sizeof(int));
+ if( aNew ){
+ int nNew = p->nException;
+ const unsigned char *zCsr = (const unsigned char*)z;
+ const unsigned char *zTerm = (const unsigned char*)&z[n];
+ while( zCsr<zTerm ){
+ int iCode;
+ int bToken;
+ READ_UTF8(zCsr, zTerm, iCode);
+ if( iCode<128 ){
+ p->aTokenChar[iCode] = (unsigned char)bTokenChars;
+ }else{
+ bToken = sqlite3Fts5UnicodeIsalnum(iCode);
+ assert( (bToken==0 || bToken==1) );
+ assert( (bTokenChars==0 || bTokenChars==1) );
+ if( bToken!=bTokenChars && sqlite3Fts5UnicodeIsdiacritic(iCode)==0 ){
+ int i;
+ for(i=0; i<nNew; i++){
+ if( aNew[i]>iCode ) break;
+ }
+ memmove(&aNew[i+1], &aNew[i], (nNew-i)*sizeof(int));
+ aNew[i] = iCode;
+ nNew++;
+ }
+ }
+ }
+ p->aiException = aNew;
+ p->nException = nNew;
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ }
+
+ return rc;
+}
+
+/*
+** Return true if the p->aiException[] array contains the value iCode.
+*/
+static int fts5UnicodeIsException(Unicode61Tokenizer *p, int iCode){
+ if( p->nException>0 ){
+ int *a = p->aiException;
+ int iLo = 0;
+ int iHi = p->nException-1;
+
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( iCode==a[iTest] ){
+ return 1;
+ }else if( iCode>a[iTest] ){
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+** Delete a "unicode61" tokenizer.
+*/
+static void fts5UnicodeDelete(Fts5Tokenizer *pTok){
+ if( pTok ){
+ Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTok;
+ sqlite3_free(p->aiException);
+ sqlite3_free(p->aFold);
+ sqlite3_free(p);
+ }
+ return;
+}
+
+/*
+** Create a "unicode61" tokenizer.
+*/
+static int fts5UnicodeCreate(
+ void *pUnused,
+ const char **azArg, int nArg,
+ Fts5Tokenizer **ppOut
+){
+ int rc = SQLITE_OK; /* Return code */
+ Unicode61Tokenizer *p = 0; /* New tokenizer object */
+
+ UNUSED_PARAM(pUnused);
+
+ if( nArg%2 ){
+ rc = SQLITE_ERROR;
+ }else{
+ p = (Unicode61Tokenizer*)sqlite3_malloc(sizeof(Unicode61Tokenizer));
+ if( p ){
+ int i;
+ memset(p, 0, sizeof(Unicode61Tokenizer));
+ memcpy(p->aTokenChar, aAsciiTokenChar, sizeof(aAsciiTokenChar));
+ p->bRemoveDiacritic = 1;
+ p->nFold = 64;
+ p->aFold = sqlite3_malloc(p->nFold * sizeof(char));
+ if( p->aFold==0 ){
+ rc = SQLITE_NOMEM;
+ }
+ for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
+ const char *zArg = azArg[i+1];
+ if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
+ if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
+ rc = SQLITE_ERROR;
+ }
+ p->bRemoveDiacritic = (zArg[0]=='1');
+ }else
+ if( 0==sqlite3_stricmp(azArg[i], "tokenchars") ){
+ rc = fts5UnicodeAddExceptions(p, zArg, 1);
+ }else
+ if( 0==sqlite3_stricmp(azArg[i], "separators") ){
+ rc = fts5UnicodeAddExceptions(p, zArg, 0);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ }
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK ){
+ fts5UnicodeDelete((Fts5Tokenizer*)p);
+ p = 0;
+ }
+ *ppOut = (Fts5Tokenizer*)p;
+ }
+ return rc;
+}
+
+/*
+** Return true if, for the purposes of tokenizing with the tokenizer
+** passed as the first argument, codepoint iCode is considered a token
+** character (not a separator).
+*/
+static int fts5UnicodeIsAlnum(Unicode61Tokenizer *p, int iCode){
+ assert( (sqlite3Fts5UnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
+ return sqlite3Fts5UnicodeIsalnum(iCode) ^ fts5UnicodeIsException(p, iCode);
+}
+
+static int fts5UnicodeTokenize(
+ Fts5Tokenizer *pTokenizer,
+ void *pCtx,
+ int iUnused,
+ const char *pText, int nText,
+ int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
+){
+ Unicode61Tokenizer *p = (Unicode61Tokenizer*)pTokenizer;
+ int rc = SQLITE_OK;
+ unsigned char *a = p->aTokenChar;
+
+ unsigned char *zTerm = (unsigned char*)&pText[nText];
+ unsigned char *zCsr = (unsigned char *)pText;
+
+ /* Output buffer */
+ char *aFold = p->aFold;
+ int nFold = p->nFold;
+ const char *pEnd = &aFold[nFold-6];
+
+ UNUSED_PARAM(iUnused);
+
+ /* Each iteration of this loop gobbles up a contiguous run of separators,
+ ** then the next token. */
+ while( rc==SQLITE_OK ){
+ int iCode; /* non-ASCII codepoint read from input */
+ char *zOut = aFold;
+ int is;
+ int ie;
+
+ /* Skip any separator characters. */
+ while( 1 ){
+ if( zCsr>=zTerm ) goto tokenize_done;
+ if( *zCsr & 0x80 ) {
+ /* A character outside of the ascii range. Skip past it if it is
+ ** a separator character. Or break out of the loop if it is not. */
+ is = zCsr - (unsigned char*)pText;
+ READ_UTF8(zCsr, zTerm, iCode);
+ if( fts5UnicodeIsAlnum(p, iCode) ){
+ goto non_ascii_tokenchar;
+ }
+ }else{
+ if( a[*zCsr] ){
+ is = zCsr - (unsigned char*)pText;
+ goto ascii_tokenchar;
+ }
+ zCsr++;
+ }
+ }
+
+ /* Run through the tokenchars. Fold them into the output buffer along
+ ** the way. */
+ while( zCsr<zTerm ){
+
+ /* Grow the output buffer so that there is sufficient space to fit the
+ ** largest possible utf-8 character. */
+ if( zOut>pEnd ){
+ aFold = sqlite3_malloc(nFold*2);
+ if( aFold==0 ){
+ rc = SQLITE_NOMEM;
+ goto tokenize_done;
+ }
+ zOut = &aFold[zOut - p->aFold];
+ memcpy(aFold, p->aFold, nFold);
+ sqlite3_free(p->aFold);
+ p->aFold = aFold;
+ p->nFold = nFold = nFold*2;
+ pEnd = &aFold[nFold-6];
+ }
+
+ if( *zCsr & 0x80 ){
+ /* An non-ascii-range character. Fold it into the output buffer if
+ ** it is a token character, or break out of the loop if it is not. */
+ READ_UTF8(zCsr, zTerm, iCode);
+ if( fts5UnicodeIsAlnum(p,iCode)||sqlite3Fts5UnicodeIsdiacritic(iCode) ){
+ non_ascii_tokenchar:
+ iCode = sqlite3Fts5UnicodeFold(iCode, p->bRemoveDiacritic);
+ if( iCode ) WRITE_UTF8(zOut, iCode);
+ }else{
+ break;
+ }
+ }else if( a[*zCsr]==0 ){
+ /* An ascii-range separator character. End of token. */
+ break;
+ }else{
+ ascii_tokenchar:
+ if( *zCsr>='A' && *zCsr<='Z' ){
+ *zOut++ = *zCsr + 32;
+ }else{
+ *zOut++ = *zCsr;
+ }
+ zCsr++;
+ }
+ ie = zCsr - (unsigned char*)pText;
+ }
+
+ /* Invoke the token callback */
+ rc = xToken(pCtx, 0, aFold, zOut-aFold, is, ie);
+ }
+
+ tokenize_done:
+ if( rc==SQLITE_DONE ) rc = SQLITE_OK;
+ return rc;
+}
+
+/**************************************************************************
+** Start of porter stemmer implementation.
+*/
+
+/* Any tokens larger than this (in bytes) are passed through without
+** stemming. */
+#define FTS5_PORTER_MAX_TOKEN 64
+
+typedef struct PorterTokenizer PorterTokenizer;
+struct PorterTokenizer {
+ fts5_tokenizer tokenizer; /* Parent tokenizer module */
+ Fts5Tokenizer *pTokenizer; /* Parent tokenizer instance */
+ char aBuf[FTS5_PORTER_MAX_TOKEN + 64];
+};
+
+/*
+** Delete a "porter" tokenizer.
+*/
+static void fts5PorterDelete(Fts5Tokenizer *pTok){
+ if( pTok ){
+ PorterTokenizer *p = (PorterTokenizer*)pTok;
+ if( p->pTokenizer ){
+ p->tokenizer.xDelete(p->pTokenizer);
+ }
+ sqlite3_free(p);
+ }
+}
+
+/*
+** Create a "porter" tokenizer.
+*/
+static int fts5PorterCreate(
+ void *pCtx,
+ const char **azArg, int nArg,
+ Fts5Tokenizer **ppOut
+){
+ fts5_api *pApi = (fts5_api*)pCtx;
+ int rc = SQLITE_OK;
+ PorterTokenizer *pRet;
+ void *pUserdata = 0;
+ const char *zBase = "unicode61";
+
+ if( nArg>0 ){
+ zBase = azArg[0];
+ }
+
+ pRet = (PorterTokenizer*)sqlite3_malloc(sizeof(PorterTokenizer));
+ if( pRet ){
+ memset(pRet, 0, sizeof(PorterTokenizer));
+ rc = pApi->xFindTokenizer(pApi, zBase, &pUserdata, &pRet->tokenizer);
+ }else{
+ rc = SQLITE_NOMEM;
+ }
+ if( rc==SQLITE_OK ){
+ int nArg2 = (nArg>0 ? nArg-1 : 0);
+ const char **azArg2 = (nArg2 ? &azArg[1] : 0);
+ rc = pRet->tokenizer.xCreate(pUserdata, azArg2, nArg2, &pRet->pTokenizer);
+ }
+
+ if( rc!=SQLITE_OK ){
+ fts5PorterDelete((Fts5Tokenizer*)pRet);
+ pRet = 0;
+ }
+ *ppOut = (Fts5Tokenizer*)pRet;
+ return rc;
+}
+
+typedef struct PorterContext PorterContext;
+struct PorterContext {
+ void *pCtx;
+ int (*xToken)(void*, int, const char*, int, int, int);
+ char *aBuf;
+};
+
+typedef struct PorterRule PorterRule;
+struct PorterRule {
+ const char *zSuffix;
+ int nSuffix;
+ int (*xCond)(char *zStem, int nStem);
+ const char *zOutput;
+ int nOutput;
+};
+
+#if 0
+static int fts5PorterApply(char *aBuf, int *pnBuf, PorterRule *aRule){
+ int ret = -1;
+ int nBuf = *pnBuf;
+ PorterRule *p;
+
+ for(p=aRule; p->zSuffix; p++){
+ assert( strlen(p->zSuffix)==p->nSuffix );
+ assert( strlen(p->zOutput)==p->nOutput );
+ if( nBuf<p->nSuffix ) continue;
+ if( 0==memcmp(&aBuf[nBuf - p->nSuffix], p->zSuffix, p->nSuffix) ) break;
+ }
+
+ if( p->zSuffix ){
+ int nStem = nBuf - p->nSuffix;
+ if( p->xCond==0 || p->xCond(aBuf, nStem) ){
+ memcpy(&aBuf[nStem], p->zOutput, p->nOutput);
+ *pnBuf = nStem + p->nOutput;
+ ret = p - aRule;
+ }
+ }
+
+ return ret;
+}
+#endif
+
+static int fts5PorterIsVowel(char c, int bYIsVowel){
+ return (
+ c=='a' || c=='e' || c=='i' || c=='o' || c=='u' || (bYIsVowel && c=='y')
+ );
+}
+
+static int fts5PorterGobbleVC(char *zStem, int nStem, int bPrevCons){
+ int i;
+ int bCons = bPrevCons;
+
+ /* Scan for a vowel */
+ for(i=0; i<nStem; i++){
+ if( 0==(bCons = !fts5PorterIsVowel(zStem[i], bCons)) ) break;
+ }
+
+ /* Scan for a consonent */
+ for(i++; i<nStem; i++){
+ if( (bCons = !fts5PorterIsVowel(zStem[i], bCons)) ) return i+1;
+ }
+ return 0;
+}
+
+/* porter rule condition: (m > 0) */
+static int fts5Porter_MGt0(char *zStem, int nStem){
+ return !!fts5PorterGobbleVC(zStem, nStem, 0);
+}
+
+/* porter rule condition: (m > 1) */
+static int fts5Porter_MGt1(char *zStem, int nStem){
+ int n;
+ n = fts5PorterGobbleVC(zStem, nStem, 0);
+ if( n && fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){
+ return 1;
+ }
+ return 0;
+}
+
+/* porter rule condition: (m = 1) */
+static int fts5Porter_MEq1(char *zStem, int nStem){
+ int n;
+ n = fts5PorterGobbleVC(zStem, nStem, 0);
+ if( n && 0==fts5PorterGobbleVC(&zStem[n], nStem-n, 1) ){
+ return 1;
+ }
+ return 0;
+}
+
+/* porter rule condition: (*o) */
+static int fts5Porter_Ostar(char *zStem, int nStem){
+ if( zStem[nStem-1]=='w' || zStem[nStem-1]=='x' || zStem[nStem-1]=='y' ){
+ return 0;
+ }else{
+ int i;
+ int mask = 0;
+ int bCons = 0;
+ for(i=0; i<nStem; i++){
+ bCons = !fts5PorterIsVowel(zStem[i], bCons);
+ assert( bCons==0 || bCons==1 );
+ mask = (mask << 1) + bCons;
+ }
+ return ((mask & 0x0007)==0x0005);
+ }
+}
+
+/* porter rule condition: (m > 1 and (*S or *T)) */
+static int fts5Porter_MGt1_and_S_or_T(char *zStem, int nStem){
+ assert( nStem>0 );
+ return (zStem[nStem-1]=='s' || zStem[nStem-1]=='t')
+ && fts5Porter_MGt1(zStem, nStem);
+}
+
+/* porter rule condition: (*v*) */
+static int fts5Porter_Vowel(char *zStem, int nStem){
+ int i;
+ for(i=0; i<nStem; i++){
+ if( fts5PorterIsVowel(zStem[i], i>0) ){
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+/**************************************************************************
+***************************************************************************
+** GENERATED CODE STARTS HERE (mkportersteps.tcl)
+*/
+
+static int fts5PorterStep4(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>2 && 0==memcmp("al", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 'c':
+ if( nBuf>4 && 0==memcmp("ance", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }else if( nBuf>4 && 0==memcmp("ence", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }
+ break;
+
+ case 'e':
+ if( nBuf>2 && 0==memcmp("er", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 'i':
+ if( nBuf>2 && 0==memcmp("ic", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 'l':
+ if( nBuf>4 && 0==memcmp("able", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }else if( nBuf>4 && 0==memcmp("ible", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }
+ break;
+
+ case 'n':
+ if( nBuf>3 && 0==memcmp("ant", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }else if( nBuf>5 && 0==memcmp("ement", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-5) ){
+ *pnBuf = nBuf - 5;
+ }
+ }else if( nBuf>4 && 0==memcmp("ment", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }else if( nBuf>3 && 0==memcmp("ent", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'o':
+ if( nBuf>3 && 0==memcmp("ion", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1_and_S_or_T(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }else if( nBuf>2 && 0==memcmp("ou", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ }
+ }
+ break;
+
+ case 's':
+ if( nBuf>3 && 0==memcmp("ism", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 't':
+ if( nBuf>3 && 0==memcmp("ate", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }else if( nBuf>3 && 0==memcmp("iti", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'u':
+ if( nBuf>3 && 0==memcmp("ous", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'v':
+ if( nBuf>3 && 0==memcmp("ive", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'z':
+ if( nBuf>3 && 0==memcmp("ize", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt1(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep1B2(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>2 && 0==memcmp("at", &aBuf[nBuf-2], 2) ){
+ memcpy(&aBuf[nBuf-2], "ate", 3);
+ *pnBuf = nBuf - 2 + 3;
+ ret = 1;
+ }
+ break;
+
+ case 'b':
+ if( nBuf>2 && 0==memcmp("bl", &aBuf[nBuf-2], 2) ){
+ memcpy(&aBuf[nBuf-2], "ble", 3);
+ *pnBuf = nBuf - 2 + 3;
+ ret = 1;
+ }
+ break;
+
+ case 'i':
+ if( nBuf>2 && 0==memcmp("iz", &aBuf[nBuf-2], 2) ){
+ memcpy(&aBuf[nBuf-2], "ize", 3);
+ *pnBuf = nBuf - 2 + 3;
+ ret = 1;
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep2(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>7 && 0==memcmp("ational", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ate", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>6 && 0==memcmp("tional", &aBuf[nBuf-6], 6) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-6) ){
+ memcpy(&aBuf[nBuf-6], "tion", 4);
+ *pnBuf = nBuf - 6 + 4;
+ }
+ }
+ break;
+
+ case 'c':
+ if( nBuf>4 && 0==memcmp("enci", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ence", 4);
+ *pnBuf = nBuf - 4 + 4;
+ }
+ }else if( nBuf>4 && 0==memcmp("anci", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ance", 4);
+ *pnBuf = nBuf - 4 + 4;
+ }
+ }
+ break;
+
+ case 'e':
+ if( nBuf>4 && 0==memcmp("izer", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ize", 3);
+ *pnBuf = nBuf - 4 + 3;
+ }
+ }
+ break;
+
+ case 'g':
+ if( nBuf>4 && 0==memcmp("logi", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "log", 3);
+ *pnBuf = nBuf - 4 + 3;
+ }
+ }
+ break;
+
+ case 'l':
+ if( nBuf>3 && 0==memcmp("bli", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ memcpy(&aBuf[nBuf-3], "ble", 3);
+ *pnBuf = nBuf - 3 + 3;
+ }
+ }else if( nBuf>4 && 0==memcmp("alli", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "al", 2);
+ *pnBuf = nBuf - 4 + 2;
+ }
+ }else if( nBuf>5 && 0==memcmp("entli", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ent", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }else if( nBuf>3 && 0==memcmp("eli", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ memcpy(&aBuf[nBuf-3], "e", 1);
+ *pnBuf = nBuf - 3 + 1;
+ }
+ }else if( nBuf>5 && 0==memcmp("ousli", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ous", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }
+ break;
+
+ case 'o':
+ if( nBuf>7 && 0==memcmp("ization", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ize", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>5 && 0==memcmp("ation", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ate", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }else if( nBuf>4 && 0==memcmp("ator", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ate", 3);
+ *pnBuf = nBuf - 4 + 3;
+ }
+ }
+ break;
+
+ case 's':
+ if( nBuf>5 && 0==memcmp("alism", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "al", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }else if( nBuf>7 && 0==memcmp("iveness", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ive", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>7 && 0==memcmp("fulness", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ful", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }else if( nBuf>7 && 0==memcmp("ousness", &aBuf[nBuf-7], 7) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-7) ){
+ memcpy(&aBuf[nBuf-7], "ous", 3);
+ *pnBuf = nBuf - 7 + 3;
+ }
+ }
+ break;
+
+ case 't':
+ if( nBuf>5 && 0==memcmp("aliti", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "al", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }else if( nBuf>5 && 0==memcmp("iviti", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ive", 3);
+ *pnBuf = nBuf - 5 + 3;
+ }
+ }else if( nBuf>6 && 0==memcmp("biliti", &aBuf[nBuf-6], 6) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-6) ){
+ memcpy(&aBuf[nBuf-6], "ble", 3);
+ *pnBuf = nBuf - 6 + 3;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep3(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'a':
+ if( nBuf>4 && 0==memcmp("ical", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ memcpy(&aBuf[nBuf-4], "ic", 2);
+ *pnBuf = nBuf - 4 + 2;
+ }
+ }
+ break;
+
+ case 's':
+ if( nBuf>4 && 0==memcmp("ness", &aBuf[nBuf-4], 4) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-4) ){
+ *pnBuf = nBuf - 4;
+ }
+ }
+ break;
+
+ case 't':
+ if( nBuf>5 && 0==memcmp("icate", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ic", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }else if( nBuf>5 && 0==memcmp("iciti", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "ic", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }
+ break;
+
+ case 'u':
+ if( nBuf>3 && 0==memcmp("ful", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ }
+ }
+ break;
+
+ case 'v':
+ if( nBuf>5 && 0==memcmp("ative", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ *pnBuf = nBuf - 5;
+ }
+ }
+ break;
+
+ case 'z':
+ if( nBuf>5 && 0==memcmp("alize", &aBuf[nBuf-5], 5) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-5) ){
+ memcpy(&aBuf[nBuf-5], "al", 2);
+ *pnBuf = nBuf - 5 + 2;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+
+static int fts5PorterStep1B(char *aBuf, int *pnBuf){
+ int ret = 0;
+ int nBuf = *pnBuf;
+ switch( aBuf[nBuf-2] ){
+
+ case 'e':
+ if( nBuf>3 && 0==memcmp("eed", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_MGt0(aBuf, nBuf-3) ){
+ memcpy(&aBuf[nBuf-3], "ee", 2);
+ *pnBuf = nBuf - 3 + 2;
+ }
+ }else if( nBuf>2 && 0==memcmp("ed", &aBuf[nBuf-2], 2) ){
+ if( fts5Porter_Vowel(aBuf, nBuf-2) ){
+ *pnBuf = nBuf - 2;
+ ret = 1;
+ }
+ }
+ break;
+
+ case 'n':
+ if( nBuf>3 && 0==memcmp("ing", &aBuf[nBuf-3], 3) ){
+ if( fts5Porter_Vowel(aBuf, nBuf-3) ){
+ *pnBuf = nBuf - 3;
+ ret = 1;
+ }
+ }
+ break;
+
+ }
+ return ret;
+}
+
+/*
+** GENERATED CODE ENDS HERE (mkportersteps.tcl)
+***************************************************************************
+**************************************************************************/
+
+static void fts5PorterStep1A(char *aBuf, int *pnBuf){
+ int nBuf = *pnBuf;
+ if( aBuf[nBuf-1]=='s' ){
+ if( aBuf[nBuf-2]=='e' ){
+ if( (nBuf>4 && aBuf[nBuf-4]=='s' && aBuf[nBuf-3]=='s')
+ || (nBuf>3 && aBuf[nBuf-3]=='i' )
+ ){
+ *pnBuf = nBuf-2;
+ }else{
+ *pnBuf = nBuf-1;
+ }
+ }
+ else if( aBuf[nBuf-2]!='s' ){
+ *pnBuf = nBuf-1;
+ }
+ }
+}
+
+static int fts5PorterCb(
+ void *pCtx,
+ int tflags,
+ const char *pToken,
+ int nToken,
+ int iStart,
+ int iEnd
+){
+ PorterContext *p = (PorterContext*)pCtx;
+
+ char *aBuf;
+ int nBuf;
+
+ if( nToken>FTS5_PORTER_MAX_TOKEN || nToken<3 ) goto pass_through;
+ aBuf = p->aBuf;
+ nBuf = nToken;
+ memcpy(aBuf, pToken, nBuf);
+
+ /* Step 1. */
+ fts5PorterStep1A(aBuf, &nBuf);
+ if( fts5PorterStep1B(aBuf, &nBuf) ){
+ if( fts5PorterStep1B2(aBuf, &nBuf)==0 ){
+ char c = aBuf[nBuf-1];
+ if( fts5PorterIsVowel(c, 0)==0
+ && c!='l' && c!='s' && c!='z' && c==aBuf[nBuf-2]
+ ){
+ nBuf--;
+ }else if( fts5Porter_MEq1(aBuf, nBuf) && fts5Porter_Ostar(aBuf, nBuf) ){
+ aBuf[nBuf++] = 'e';
+ }
+ }
+ }
+
+ /* Step 1C. */
+ if( aBuf[nBuf-1]=='y' && fts5Porter_Vowel(aBuf, nBuf-1) ){
+ aBuf[nBuf-1] = 'i';
+ }
+
+ /* Steps 2 through 4. */
+ fts5PorterStep2(aBuf, &nBuf);
+ fts5PorterStep3(aBuf, &nBuf);
+ fts5PorterStep4(aBuf, &nBuf);
+
+ /* Step 5a. */
+ assert( nBuf>0 );
+ if( aBuf[nBuf-1]=='e' ){
+ if( fts5Porter_MGt1(aBuf, nBuf-1)
+ || (fts5Porter_MEq1(aBuf, nBuf-1) && !fts5Porter_Ostar(aBuf, nBuf-1))
+ ){
+ nBuf--;
+ }
+ }
+
+ /* Step 5b. */
+ if( nBuf>1 && aBuf[nBuf-1]=='l'
+ && aBuf[nBuf-2]=='l' && fts5Porter_MGt1(aBuf, nBuf-1)
+ ){
+ nBuf--;
+ }
+
+ return p->xToken(p->pCtx, tflags, aBuf, nBuf, iStart, iEnd);
+
+ pass_through:
+ return p->xToken(p->pCtx, tflags, pToken, nToken, iStart, iEnd);
+}
+
+/*
+** Tokenize using the porter tokenizer.
+*/
+static int fts5PorterTokenize(
+ Fts5Tokenizer *pTokenizer,
+ void *pCtx,
+ int flags,
+ const char *pText, int nText,
+ int (*xToken)(void*, int, const char*, int nToken, int iStart, int iEnd)
+){
+ PorterTokenizer *p = (PorterTokenizer*)pTokenizer;
+ PorterContext sCtx;
+ sCtx.xToken = xToken;
+ sCtx.pCtx = pCtx;
+ sCtx.aBuf = p->aBuf;
+ return p->tokenizer.xTokenize(
+ p->pTokenizer, (void*)&sCtx, flags, pText, nText, fts5PorterCb
+ );
+}
+
+/*
+** Register all built-in tokenizers with FTS5.
+*/
+static int sqlite3Fts5TokenizerInit(fts5_api *pApi){
+ struct BuiltinTokenizer {
+ const char *zName;
+ fts5_tokenizer x;
+ } aBuiltin[] = {
+ { "unicode61", {fts5UnicodeCreate, fts5UnicodeDelete, fts5UnicodeTokenize}},
+ { "ascii", {fts5AsciiCreate, fts5AsciiDelete, fts5AsciiTokenize }},
+ { "porter", {fts5PorterCreate, fts5PorterDelete, fts5PorterTokenize }},
+ };
+
+ int rc = SQLITE_OK; /* Return code */
+ int i; /* To iterate through builtin functions */
+
+ for(i=0; rc==SQLITE_OK && i<ArraySize(aBuiltin); i++){
+ rc = pApi->xCreateTokenizer(pApi,
+ aBuiltin[i].zName,
+ (void*)pApi,
+ &aBuiltin[i].x,
+ 0
+ );
+ }
+
+ return rc;
+}
+
+
+
+/*
+** 2012 May 25
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+*/
+
+/*
+** DO NOT EDIT THIS MACHINE GENERATED FILE.
+*/
+
+
+/* #include <assert.h> */
+
+/*
+** Return true if the argument corresponds to a unicode codepoint
+** classified as either a letter or a number. Otherwise false.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+static int sqlite3Fts5UnicodeIsalnum(int c){
+ /* Each unsigned integer in the following array corresponds to a contiguous
+ ** range of unicode codepoints that are not either letters or numbers (i.e.
+ ** codepoints for which this function should return 0).
+ **
+ ** The most significant 22 bits in each 32-bit value contain the first
+ ** codepoint in the range. The least significant 10 bits are used to store
+ ** the size of the range (always at least 1). In other words, the value
+ ** ((C<<22) + N) represents a range of N codepoints starting with codepoint
+ ** C. It is not possible to represent a range larger than 1023 codepoints
+ ** using this format.
+ */
+ static const unsigned int aEntry[] = {
+ 0x00000030, 0x0000E807, 0x00016C06, 0x0001EC2F, 0x0002AC07,
+ 0x0002D001, 0x0002D803, 0x0002EC01, 0x0002FC01, 0x00035C01,
+ 0x0003DC01, 0x000B0804, 0x000B480E, 0x000B9407, 0x000BB401,
+ 0x000BBC81, 0x000DD401, 0x000DF801, 0x000E1002, 0x000E1C01,
+ 0x000FD801, 0x00120808, 0x00156806, 0x00162402, 0x00163C01,
+ 0x00164437, 0x0017CC02, 0x00180005, 0x00181816, 0x00187802,
+ 0x00192C15, 0x0019A804, 0x0019C001, 0x001B5001, 0x001B580F,
+ 0x001B9C07, 0x001BF402, 0x001C000E, 0x001C3C01, 0x001C4401,
+ 0x001CC01B, 0x001E980B, 0x001FAC09, 0x001FD804, 0x00205804,
+ 0x00206C09, 0x00209403, 0x0020A405, 0x0020C00F, 0x00216403,
+ 0x00217801, 0x0023901B, 0x00240004, 0x0024E803, 0x0024F812,
+ 0x00254407, 0x00258804, 0x0025C001, 0x00260403, 0x0026F001,
+ 0x0026F807, 0x00271C02, 0x00272C03, 0x00275C01, 0x00278802,
+ 0x0027C802, 0x0027E802, 0x00280403, 0x0028F001, 0x0028F805,
+ 0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D401,
+ 0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
+ 0x002B8802, 0x002BC002, 0x002C0403, 0x002CF001, 0x002CF807,
+ 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802, 0x002DC001,
+ 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804, 0x002F5C01,
+ 0x002FCC08, 0x00300403, 0x0030F807, 0x00311803, 0x00312804,
+ 0x00315402, 0x00318802, 0x0031FC01, 0x00320802, 0x0032F001,
+ 0x0032F807, 0x00331803, 0x00332804, 0x00335402, 0x00338802,
+ 0x00340802, 0x0034F807, 0x00351803, 0x00352804, 0x00355C01,
+ 0x00358802, 0x0035E401, 0x00360802, 0x00372801, 0x00373C06,
+ 0x00375801, 0x00376008, 0x0037C803, 0x0038C401, 0x0038D007,
+ 0x0038FC01, 0x00391C09, 0x00396802, 0x003AC401, 0x003AD006,
+ 0x003AEC02, 0x003B2006, 0x003C041F, 0x003CD00C, 0x003DC417,
+ 0x003E340B, 0x003E6424, 0x003EF80F, 0x003F380D, 0x0040AC14,
+ 0x00412806, 0x00415804, 0x00417803, 0x00418803, 0x00419C07,
+ 0x0041C404, 0x0042080C, 0x00423C01, 0x00426806, 0x0043EC01,
+ 0x004D740C, 0x004E400A, 0x00500001, 0x0059B402, 0x005A0001,
+ 0x005A6C02, 0x005BAC03, 0x005C4803, 0x005CC805, 0x005D4802,
+ 0x005DC802, 0x005ED023, 0x005F6004, 0x005F7401, 0x0060000F,
+ 0x0062A401, 0x0064800C, 0x0064C00C, 0x00650001, 0x00651002,
+ 0x0066C011, 0x00672002, 0x00677822, 0x00685C05, 0x00687802,
+ 0x0069540A, 0x0069801D, 0x0069FC01, 0x006A8007, 0x006AA006,
+ 0x006C0005, 0x006CD011, 0x006D6823, 0x006E0003, 0x006E840D,
+ 0x006F980E, 0x006FF004, 0x00709014, 0x0070EC05, 0x0071F802,
+ 0x00730008, 0x00734019, 0x0073B401, 0x0073C803, 0x00770027,
+ 0x0077F004, 0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403,
+ 0x007FB403, 0x007FF402, 0x00800065, 0x0081A806, 0x0081E805,
+ 0x00822805, 0x0082801A, 0x00834021, 0x00840002, 0x00840C04,
+ 0x00842002, 0x00845001, 0x00845803, 0x00847806, 0x00849401,
+ 0x00849C01, 0x0084A401, 0x0084B801, 0x0084E802, 0x00850005,
+ 0x00852804, 0x00853C01, 0x00864264, 0x00900027, 0x0091000B,
+ 0x0092704E, 0x00940200, 0x009C0475, 0x009E53B9, 0x00AD400A,
+ 0x00B39406, 0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001,
+ 0x00B5FC01, 0x00B7804F, 0x00B8C00C, 0x00BA001A, 0x00BA6C59,
+ 0x00BC00D6, 0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807,
+ 0x00C0D802, 0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01,
+ 0x00C64002, 0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E,
+ 0x00C94001, 0x00C98020, 0x00CA2827, 0x00CB003F, 0x00CC0100,
+ 0x01370040, 0x02924037, 0x0293F802, 0x02983403, 0x0299BC10,
+ 0x029A7C01, 0x029BC008, 0x029C0017, 0x029C8002, 0x029E2402,
+ 0x02A00801, 0x02A01801, 0x02A02C01, 0x02A08C09, 0x02A0D804,
+ 0x02A1D004, 0x02A20002, 0x02A2D011, 0x02A33802, 0x02A38012,
+ 0x02A3E003, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
+ 0x02A6CC1B, 0x02A77802, 0x02A8A40E, 0x02A90C01, 0x02A93002,
+ 0x02A97004, 0x02A9DC03, 0x02A9EC01, 0x02AAC001, 0x02AAC803,
+ 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802, 0x02ABAC07,
+ 0x02ABD402, 0x02AF8C0B, 0x03600001, 0x036DFC02, 0x036FFC02,
+ 0x037FFC01, 0x03EC7801, 0x03ECA401, 0x03EEC810, 0x03F4F802,
+ 0x03F7F002, 0x03F8001A, 0x03F88007, 0x03F8C023, 0x03F95013,
+ 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807, 0x03FCEC06,
+ 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405, 0x04040003,
+ 0x0404DC09, 0x0405E411, 0x0406400C, 0x0407402E, 0x040E7C01,
+ 0x040F4001, 0x04215C01, 0x04247C01, 0x0424FC01, 0x04280403,
+ 0x04281402, 0x04283004, 0x0428E003, 0x0428FC01, 0x04294009,
+ 0x0429FC01, 0x042CE407, 0x04400003, 0x0440E016, 0x04420003,
+ 0x0442C012, 0x04440003, 0x04449C0E, 0x04450004, 0x04460003,
+ 0x0446CC0E, 0x04471404, 0x045AAC0D, 0x0491C004, 0x05BD442E,
+ 0x05BE3C04, 0x074000F6, 0x07440027, 0x0744A4B5, 0x07480046,
+ 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01, 0x075C5401,
+ 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401, 0x075EA401,
+ 0x075F0C01, 0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F,
+ 0x07C2C40E, 0x07C3040F, 0x07C3440F, 0x07C4401F, 0x07C4C03C,
+ 0x07C5C02B, 0x07C7981D, 0x07C8402B, 0x07C90009, 0x07C94002,
+ 0x07CC0021, 0x07CCC006, 0x07CCDC46, 0x07CE0014, 0x07CE8025,
+ 0x07CF1805, 0x07CF8011, 0x07D0003F, 0x07D10001, 0x07D108B6,
+ 0x07D3E404, 0x07D4003E, 0x07D50004, 0x07D54018, 0x07D7EC46,
+ 0x07D9140B, 0x07DA0046, 0x07DC0074, 0x38000401, 0x38008060,
+ 0x380400F0,
+ };
+ static const unsigned int aAscii[4] = {
+ 0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
+ };
+
+ if( (unsigned int)c<128 ){
+ return ( (aAscii[c >> 5] & (1 << (c & 0x001F)))==0 );
+ }else if( (unsigned int)c<(1<<22) ){
+ unsigned int key = (((unsigned int)c)<<10) | 0x000003FF;
+ int iRes = 0;
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aEntry[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( aEntry[0]<key );
+ assert( key>=aEntry[iRes] );
+ return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
+ }
+ return 1;
+}
+
+
+/*
+** If the argument is a codepoint corresponding to a lowercase letter
+** in the ASCII range with a diacritic added, return the codepoint
+** of the ASCII letter only. For example, if passed 235 - "LATIN
+** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER
+** E"). The resuls of passing a codepoint that corresponds to an
+** uppercase letter are undefined.
+*/
+static int fts5_remove_diacritic(int c){
+ unsigned short aDia[] = {
+ 0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
+ 2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
+ 2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
+ 2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
+ 3456, 3696, 3712, 3728, 3744, 3896, 3912, 3928,
+ 3968, 4008, 4040, 4106, 4138, 4170, 4202, 4234,
+ 4266, 4296, 4312, 4344, 4408, 4424, 4472, 4504,
+ 6148, 6198, 6264, 6280, 6360, 6429, 6505, 6529,
+ 61448, 61468, 61534, 61592, 61642, 61688, 61704, 61726,
+ 61784, 61800, 61836, 61880, 61914, 61948, 61998, 62122,
+ 62154, 62200, 62218, 62302, 62364, 62442, 62478, 62536,
+ 62554, 62584, 62604, 62640, 62648, 62656, 62664, 62730,
+ 62924, 63050, 63082, 63274, 63390,
+ };
+ char aChar[] = {
+ '\0', 'a', 'c', 'e', 'i', 'n', 'o', 'u', 'y', 'y', 'a', 'c',
+ 'd', 'e', 'e', 'g', 'h', 'i', 'j', 'k', 'l', 'n', 'o', 'r',
+ 's', 't', 'u', 'u', 'w', 'y', 'z', 'o', 'u', 'a', 'i', 'o',
+ 'u', 'g', 'k', 'o', 'j', 'g', 'n', 'a', 'e', 'i', 'o', 'r',
+ 'u', 's', 't', 'h', 'a', 'e', 'o', 'y', '\0', '\0', '\0', '\0',
+ '\0', '\0', '\0', '\0', 'a', 'b', 'd', 'd', 'e', 'f', 'g', 'h',
+ 'h', 'i', 'k', 'l', 'l', 'm', 'n', 'p', 'r', 'r', 's', 't',
+ 'u', 'v', 'w', 'w', 'x', 'y', 'z', 'h', 't', 'w', 'y', 'a',
+ 'e', 'i', 'o', 'u', 'y',
+ };
+
+ unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
+ int iRes = 0;
+ int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1;
+ int iLo = 0;
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( key >= aDia[iTest] ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ assert( key>=aDia[iRes] );
+ return ((c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : (int)aChar[iRes]);
+}
+
+
+/*
+** Return true if the argument interpreted as a unicode codepoint
+** is a diacritical modifier character.
+*/
+static int sqlite3Fts5UnicodeIsdiacritic(int c){
+ unsigned int mask0 = 0x08029FDF;
+ unsigned int mask1 = 0x000361F8;
+ if( c<768 || c>817 ) return 0;
+ return (c < 768+32) ?
+ (mask0 & (1 << (c-768))) :
+ (mask1 & (1 << (c-768-32)));
+}
+
+
+/*
+** Interpret the argument as a unicode codepoint. If the codepoint
+** is an upper case character that has a lower case equivalent,
+** return the codepoint corresponding to the lower case version.
+** Otherwise, return a copy of the argument.
+**
+** The results are undefined if the value passed to this function
+** is less than zero.
+*/
+static int sqlite3Fts5UnicodeFold(int c, int bRemoveDiacritic){
+ /* Each entry in the following array defines a rule for folding a range
+ ** of codepoints to lower case. The rule applies to a range of nRange
+ ** codepoints starting at codepoint iCode.
+ **
+ ** If the least significant bit in flags is clear, then the rule applies
+ ** to all nRange codepoints (i.e. all nRange codepoints are upper case and
+ ** need to be folded). Or, if it is set, then the rule only applies to
+ ** every second codepoint in the range, starting with codepoint C.
+ **
+ ** The 7 most significant bits in flags are an index into the aiOff[]
+ ** array. If a specific codepoint C does require folding, then its lower
+ ** case equivalent is ((C + aiOff[flags>>1]) & 0xFFFF).
+ **
+ ** The contents of this array are generated by parsing the CaseFolding.txt
+ ** file distributed as part of the "Unicode Character Database". See
+ ** http://www.unicode.org for details.
+ */
+ static const struct TableEntry {
+ unsigned short iCode;
+ unsigned char flags;
+ unsigned char nRange;
+ } aEntry[] = {
+ {65, 14, 26}, {181, 64, 1}, {192, 14, 23},
+ {216, 14, 7}, {256, 1, 48}, {306, 1, 6},
+ {313, 1, 16}, {330, 1, 46}, {376, 116, 1},
+ {377, 1, 6}, {383, 104, 1}, {385, 50, 1},
+ {386, 1, 4}, {390, 44, 1}, {391, 0, 1},
+ {393, 42, 2}, {395, 0, 1}, {398, 32, 1},
+ {399, 38, 1}, {400, 40, 1}, {401, 0, 1},
+ {403, 42, 1}, {404, 46, 1}, {406, 52, 1},
+ {407, 48, 1}, {408, 0, 1}, {412, 52, 1},
+ {413, 54, 1}, {415, 56, 1}, {416, 1, 6},
+ {422, 60, 1}, {423, 0, 1}, {425, 60, 1},
+ {428, 0, 1}, {430, 60, 1}, {431, 0, 1},
+ {433, 58, 2}, {435, 1, 4}, {439, 62, 1},
+ {440, 0, 1}, {444, 0, 1}, {452, 2, 1},
+ {453, 0, 1}, {455, 2, 1}, {456, 0, 1},
+ {458, 2, 1}, {459, 1, 18}, {478, 1, 18},
+ {497, 2, 1}, {498, 1, 4}, {502, 122, 1},
+ {503, 134, 1}, {504, 1, 40}, {544, 110, 1},
+ {546, 1, 18}, {570, 70, 1}, {571, 0, 1},
+ {573, 108, 1}, {574, 68, 1}, {577, 0, 1},
+ {579, 106, 1}, {580, 28, 1}, {581, 30, 1},
+ {582, 1, 10}, {837, 36, 1}, {880, 1, 4},
+ {886, 0, 1}, {902, 18, 1}, {904, 16, 3},
+ {908, 26, 1}, {910, 24, 2}, {913, 14, 17},
+ {931, 14, 9}, {962, 0, 1}, {975, 4, 1},
+ {976, 140, 1}, {977, 142, 1}, {981, 146, 1},
+ {982, 144, 1}, {984, 1, 24}, {1008, 136, 1},
+ {1009, 138, 1}, {1012, 130, 1}, {1013, 128, 1},
+ {1015, 0, 1}, {1017, 152, 1}, {1018, 0, 1},
+ {1021, 110, 3}, {1024, 34, 16}, {1040, 14, 32},
+ {1120, 1, 34}, {1162, 1, 54}, {1216, 6, 1},
+ {1217, 1, 14}, {1232, 1, 88}, {1329, 22, 38},
+ {4256, 66, 38}, {4295, 66, 1}, {4301, 66, 1},
+ {7680, 1, 150}, {7835, 132, 1}, {7838, 96, 1},
+ {7840, 1, 96}, {7944, 150, 8}, {7960, 150, 6},
+ {7976, 150, 8}, {7992, 150, 8}, {8008, 150, 6},
+ {8025, 151, 8}, {8040, 150, 8}, {8072, 150, 8},
+ {8088, 150, 8}, {8104, 150, 8}, {8120, 150, 2},
+ {8122, 126, 2}, {8124, 148, 1}, {8126, 100, 1},
+ {8136, 124, 4}, {8140, 148, 1}, {8152, 150, 2},
+ {8154, 120, 2}, {8168, 150, 2}, {8170, 118, 2},
+ {8172, 152, 1}, {8184, 112, 2}, {8186, 114, 2},
+ {8188, 148, 1}, {8486, 98, 1}, {8490, 92, 1},
+ {8491, 94, 1}, {8498, 12, 1}, {8544, 8, 16},
+ {8579, 0, 1}, {9398, 10, 26}, {11264, 22, 47},
+ {11360, 0, 1}, {11362, 88, 1}, {11363, 102, 1},
+ {11364, 90, 1}, {11367, 1, 6}, {11373, 84, 1},
+ {11374, 86, 1}, {11375, 80, 1}, {11376, 82, 1},
+ {11378, 0, 1}, {11381, 0, 1}, {11390, 78, 2},
+ {11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
+ {42560, 1, 46}, {42624, 1, 24}, {42786, 1, 14},
+ {42802, 1, 62}, {42873, 1, 4}, {42877, 76, 1},
+ {42878, 1, 10}, {42891, 0, 1}, {42893, 74, 1},
+ {42896, 1, 4}, {42912, 1, 10}, {42922, 72, 1},
+ {65313, 14, 26},
+ };
+ static const unsigned short aiOff[] = {
+ 1, 2, 8, 15, 16, 26, 28, 32,
+ 37, 38, 40, 48, 63, 64, 69, 71,
+ 79, 80, 116, 202, 203, 205, 206, 207,
+ 209, 210, 211, 213, 214, 217, 218, 219,
+ 775, 7264, 10792, 10795, 23228, 23256, 30204, 54721,
+ 54753, 54754, 54756, 54787, 54793, 54809, 57153, 57274,
+ 57921, 58019, 58363, 61722, 65268, 65341, 65373, 65406,
+ 65408, 65410, 65415, 65424, 65436, 65439, 65450, 65462,
+ 65472, 65476, 65478, 65480, 65482, 65488, 65506, 65511,
+ 65514, 65521, 65527, 65528, 65529,
+ };
+
+ int ret = c;
+
+ assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );
+
+ if( c<128 ){
+ if( c>='A' && c<='Z' ) ret = c + ('a' - 'A');
+ }else if( c<65536 ){
+ const struct TableEntry *p;
+ int iHi = sizeof(aEntry)/sizeof(aEntry[0]) - 1;
+ int iLo = 0;
+ int iRes = -1;
+
+ assert( c>aEntry[0].iCode );
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ int cmp = (c - aEntry[iTest].iCode);
+ if( cmp>=0 ){
+ iRes = iTest;
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+
+ assert( iRes>=0 && c>=aEntry[iRes].iCode );
+ p = &aEntry[iRes];
+ if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
+ ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
+ assert( ret>0 );
+ }
+
+ if( bRemoveDiacritic ) ret = fts5_remove_diacritic(ret);
+ }
+
+ else if( c>=66560 && c<66600 ){
+ ret = c + 40;
+ }
+
+ return ret;
+}
+
+/*
+** 2015 May 30
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Routines for varint serialization and deserialization.
+*/
+
+
+/* #include "fts5Int.h" */
+
+/*
+** This is a copy of the sqlite3GetVarint32() routine from the SQLite core.
+** Except, this version does handle the single byte case that the core
+** version depends on being handled before its function is called.
+*/
+static int sqlite3Fts5GetVarint32(const unsigned char *p, u32 *v){
+ u32 a,b;
+
+ /* The 1-byte case. Overwhelmingly the most common. */
+ a = *p;
+ /* a: p0 (unmasked) */
+ if (!(a&0x80))
+ {
+ /* Values between 0 and 127 */
+ *v = a;
+ return 1;
+ }
+
+ /* The 2-byte case */
+ p++;
+ b = *p;
+ /* b: p1 (unmasked) */
+ if (!(b&0x80))
+ {
+ /* Values between 128 and 16383 */
+ a &= 0x7f;
+ a = a<<7;
+ *v = a | b;
+ return 2;
+ }
+
+ /* The 3-byte case */
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<14 | p2 (unmasked) */
+ if (!(a&0x80))
+ {
+ /* Values between 16384 and 2097151 */
+ a &= (0x7f<<14)|(0x7f);
+ b &= 0x7f;
+ b = b<<7;
+ *v = a | b;
+ return 3;
+ }
+
+ /* A 32-bit varint is used to store size information in btrees.
+ ** Objects are rarely larger than 2MiB limit of a 3-byte varint.
+ ** A 3-byte varint is sufficient, for example, to record the size
+ ** of a 1048569-byte BLOB or string.
+ **
+ ** We only unroll the first 1-, 2-, and 3- byte cases. The very
+ ** rare larger cases can be handled by the slower 64-bit varint
+ ** routine.
+ */
+ {
+ u64 v64;
+ u8 n;
+ p -= 2;
+ n = sqlite3Fts5GetVarint(p, &v64);
+ *v = (u32)v64;
+ assert( n>3 && n<=9 );
+ return n;
+ }
+}
+
+
+/*
+** Bitmasks used by sqlite3GetVarint(). These precomputed constants
+** are defined here rather than simply putting the constant expressions
+** inline in order to work around bugs in the RVT compiler.
+**
+** SLOT_2_0 A mask for (0x7f<<14) | 0x7f
+**
+** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0
+*/
+#define SLOT_2_0 0x001fc07f
+#define SLOT_4_2_0 0xf01fc07f
+
+/*
+** Read a 64-bit variable-length integer from memory starting at p[0].
+** Return the number of bytes read. The value is stored in *v.
+*/
+static u8 sqlite3Fts5GetVarint(const unsigned char *p, u64 *v){
+ u32 a,b,s;
+
+ a = *p;
+ /* a: p0 (unmasked) */
+ if (!(a&0x80))
+ {
+ *v = a;
+ return 1;
+ }
+
+ p++;
+ b = *p;
+ /* b: p1 (unmasked) */
+ if (!(b&0x80))
+ {
+ a &= 0x7f;
+ a = a<<7;
+ a |= b;
+ *v = a;
+ return 2;
+ }
+
+ /* Verify that constants are precomputed correctly */
+ assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) );
+ assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) );
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<14 | p2 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= SLOT_2_0;
+ b &= 0x7f;
+ b = b<<7;
+ a |= b;
+ *v = a;
+ return 3;
+ }
+
+ /* CSE1 from below */
+ a &= SLOT_2_0;
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p1<<14 | p3 (unmasked) */
+ if (!(b&0x80))
+ {
+ b &= SLOT_2_0;
+ /* moved CSE1 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ a = a<<7;
+ a |= b;
+ *v = a;
+ return 4;
+ }
+
+ /* a: p0<<14 | p2 (masked) */
+ /* b: p1<<14 | p3 (unmasked) */
+ /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+ /* moved CSE1 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ b &= SLOT_2_0;
+ s = a;
+ /* s: p0<<14 | p2 (masked) */
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p0<<28 | p2<<14 | p4 (unmasked) */
+ if (!(a&0x80))
+ {
+ /* we can skip these cause they were (effectively) done above in calc'ing s */
+ /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
+ /* b &= (0x7f<<14)|(0x7f); */
+ b = b<<7;
+ a |= b;
+ s = s>>18;
+ *v = ((u64)s)<<32 | a;
+ return 5;
+ }
+
+ /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+ s = s<<7;
+ s |= b;
+ /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */
+
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p1<<28 | p3<<14 | p5 (unmasked) */
+ if (!(b&0x80))
+ {
+ /* we can skip this cause it was (effectively) done above in calc'ing s */
+ /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */
+ a &= SLOT_2_0;
+ a = a<<7;
+ a |= b;
+ s = s>>18;
+ *v = ((u64)s)<<32 | a;
+ return 6;
+ }
+
+ p++;
+ a = a<<14;
+ a |= *p;
+ /* a: p2<<28 | p4<<14 | p6 (unmasked) */
+ if (!(a&0x80))
+ {
+ a &= SLOT_4_2_0;
+ b &= SLOT_2_0;
+ b = b<<7;
+ a |= b;
+ s = s>>11;
+ *v = ((u64)s)<<32 | a;
+ return 7;
+ }
+
+ /* CSE2 from below */
+ a &= SLOT_2_0;
+ p++;
+ b = b<<14;
+ b |= *p;
+ /* b: p3<<28 | p5<<14 | p7 (unmasked) */
+ if (!(b&0x80))
+ {
+ b &= SLOT_4_2_0;
+ /* moved CSE2 up */
+ /* a &= (0x7f<<14)|(0x7f); */
+ a = a<<7;
+ a |= b;
+ s = s>>4;
+ *v = ((u64)s)<<32 | a;
+ return 8;
+ }
+
+ p++;
+ a = a<<15;
+ a |= *p;
+ /* a: p4<<29 | p6<<15 | p8 (unmasked) */
+
+ /* moved CSE2 up */
+ /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */
+ b &= SLOT_2_0;
+ b = b<<8;
+ a |= b;
+
+ s = s<<4;
+ b = p[-4];
+ b &= 0x7f;
+ b = b>>3;
+ s |= b;
+
+ *v = ((u64)s)<<32 | a;
+
+ return 9;
+}
+
+/*
+** The variable-length integer encoding is as follows:
+**
+** KEY:
+** A = 0xxxxxxx 7 bits of data and one flag bit
+** B = 1xxxxxxx 7 bits of data and one flag bit
+** C = xxxxxxxx 8 bits of data
+**
+** 7 bits - A
+** 14 bits - BA
+** 21 bits - BBA
+** 28 bits - BBBA
+** 35 bits - BBBBA
+** 42 bits - BBBBBA
+** 49 bits - BBBBBBA
+** 56 bits - BBBBBBBA
+** 64 bits - BBBBBBBBC
+*/
+
+#ifdef SQLITE_NOINLINE
+# define FTS5_NOINLINE SQLITE_NOINLINE
+#else
+# define FTS5_NOINLINE
+#endif
+
+/*
+** Write a 64-bit variable-length integer to memory starting at p[0].
+** The length of data write will be between 1 and 9 bytes. The number
+** of bytes written is returned.
+**
+** A variable-length integer consists of the lower 7 bits of each byte
+** for all bytes that have the 8th bit set and one byte with the 8th
+** bit clear. Except, if we get to the 9th byte, it stores the full
+** 8 bits and is the last byte.
+*/
+static int FTS5_NOINLINE fts5PutVarint64(unsigned char *p, u64 v){
+ int i, j, n;
+ u8 buf[10];
+ if( v & (((u64)0xff000000)<<32) ){
+ p[8] = (u8)v;
+ v >>= 8;
+ for(i=7; i>=0; i--){
+ p[i] = (u8)((v & 0x7f) | 0x80);
+ v >>= 7;
+ }
+ return 9;
+ }
+ n = 0;
+ do{
+ buf[n++] = (u8)((v & 0x7f) | 0x80);
+ v >>= 7;
+ }while( v!=0 );
+ buf[0] &= 0x7f;
+ assert( n<=9 );
+ for(i=0, j=n-1; j>=0; j--, i++){
+ p[i] = buf[j];
+ }
+ return n;
+}
+
+static int sqlite3Fts5PutVarint(unsigned char *p, u64 v){
+ if( v<=0x7f ){
+ p[0] = v&0x7f;
+ return 1;
+ }
+ if( v<=0x3fff ){
+ p[0] = ((v>>7)&0x7f)|0x80;
+ p[1] = v&0x7f;
+ return 2;
+ }
+ return fts5PutVarint64(p,v);
+}
+
+
+static int sqlite3Fts5GetVarintLen(u32 iVal){
+#if 0
+ if( iVal<(1 << 7 ) ) return 1;
+#endif
+ assert( iVal>=(1 << 7) );
+ if( iVal<(1 << 14) ) return 2;
+ if( iVal<(1 << 21) ) return 3;
+ if( iVal<(1 << 28) ) return 4;
+ return 5;
+}
+
+
+/*
+** 2015 May 08
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This is an SQLite virtual table module implementing direct access to an
+** existing FTS5 index. The module may create several different types of
+** tables:
+**
+** col:
+** CREATE TABLE vocab(term, col, doc, cnt, PRIMARY KEY(term, col));
+**
+** One row for each term/column combination. The value of $doc is set to
+** the number of fts5 rows that contain at least one instance of term
+** $term within column $col. Field $cnt is set to the total number of
+** instances of term $term in column $col (in any row of the fts5 table).
+**
+** row:
+** CREATE TABLE vocab(term, doc, cnt, PRIMARY KEY(term));
+**
+** One row for each term in the database. The value of $doc is set to
+** the number of fts5 rows that contain at least one instance of term
+** $term. Field $cnt is set to the total number of instances of term
+** $term in the database.
+*/
+
+
+/* #include "fts5Int.h" */
+
+
+typedef struct Fts5VocabTable Fts5VocabTable;
+typedef struct Fts5VocabCursor Fts5VocabCursor;
+
+struct Fts5VocabTable {
+ sqlite3_vtab base;
+ char *zFts5Tbl; /* Name of fts5 table */
+ char *zFts5Db; /* Db containing fts5 table */
+ sqlite3 *db; /* Database handle */
+ Fts5Global *pGlobal; /* FTS5 global object for this database */
+ int eType; /* FTS5_VOCAB_COL or ROW */
+};
+
+struct Fts5VocabCursor {
+ sqlite3_vtab_cursor base;
+ sqlite3_stmt *pStmt; /* Statement holding lock on pIndex */
+ Fts5Index *pIndex; /* Associated FTS5 index */
+
+ int bEof; /* True if this cursor is at EOF */
+ Fts5IndexIter *pIter; /* Term/rowid iterator object */
+
+ int nLeTerm; /* Size of zLeTerm in bytes */
+ char *zLeTerm; /* (term <= $zLeTerm) paramater, or NULL */
+
+ /* These are used by 'col' tables only */
+ Fts5Config *pConfig; /* Fts5 table configuration */
+ int iCol;
+ i64 *aCnt;
+ i64 *aDoc;
+
+ /* Output values used by 'row' and 'col' tables */
+ i64 rowid; /* This table's current rowid value */
+ Fts5Buffer term; /* Current value of 'term' column */
+};
+
+#define FTS5_VOCAB_COL 0
+#define FTS5_VOCAB_ROW 1
+
+#define FTS5_VOCAB_COL_SCHEMA "term, col, doc, cnt"
+#define FTS5_VOCAB_ROW_SCHEMA "term, doc, cnt"
+
+/*
+** Bits for the mask used as the idxNum value by xBestIndex/xFilter.
+*/
+#define FTS5_VOCAB_TERM_EQ 0x01
+#define FTS5_VOCAB_TERM_GE 0x02
+#define FTS5_VOCAB_TERM_LE 0x04
+
+
+/*
+** Translate a string containing an fts5vocab table type to an
+** FTS5_VOCAB_XXX constant. If successful, set *peType to the output
+** value and return SQLITE_OK. Otherwise, set *pzErr to an error message
+** and return SQLITE_ERROR.
+*/
+static int fts5VocabTableType(const char *zType, char **pzErr, int *peType){
+ int rc = SQLITE_OK;
+ char *zCopy = sqlite3Fts5Strndup(&rc, zType, -1);
+ if( rc==SQLITE_OK ){
+ sqlite3Fts5Dequote(zCopy);
+ if( sqlite3_stricmp(zCopy, "col")==0 ){
+ *peType = FTS5_VOCAB_COL;
+ }else
+
+ if( sqlite3_stricmp(zCopy, "row")==0 ){
+ *peType = FTS5_VOCAB_ROW;
+ }else
+ {
+ *pzErr = sqlite3_mprintf("fts5vocab: unknown table type: %Q", zCopy);
+ rc = SQLITE_ERROR;
+ }
+ sqlite3_free(zCopy);
+ }
+
+ return rc;
+}
+
+
+/*
+** The xDisconnect() virtual table method.
+*/
+static int fts5VocabDisconnectMethod(sqlite3_vtab *pVtab){
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab;
+ sqlite3_free(pTab);
+ return SQLITE_OK;
+}
+
+/*
+** The xDestroy() virtual table method.
+*/
+static int fts5VocabDestroyMethod(sqlite3_vtab *pVtab){
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pVtab;
+ sqlite3_free(pTab);
+ return SQLITE_OK;
+}
+
+/*
+** This function is the implementation of both the xConnect and xCreate
+** methods of the FTS3 virtual table.
+**
+** The argv[] array contains the following:
+**
+** argv[0] -> module name ("fts5vocab")
+** argv[1] -> database name
+** argv[2] -> table name
+**
+** then:
+**
+** argv[3] -> name of fts5 table
+** argv[4] -> type of fts5vocab table
+**
+** or, for tables in the TEMP schema only.
+**
+** argv[3] -> name of fts5 tables database
+** argv[4] -> name of fts5 table
+** argv[5] -> type of fts5vocab table
+*/
+static int fts5VocabInitVtab(
+ sqlite3 *db, /* The SQLite database connection */
+ void *pAux, /* Pointer to Fts5Global object */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */
+ char **pzErr /* Write any error message here */
+){
+ const char *azSchema[] = {
+ "CREATE TABlE vocab(" FTS5_VOCAB_COL_SCHEMA ")",
+ "CREATE TABlE vocab(" FTS5_VOCAB_ROW_SCHEMA ")"
+ };
+
+ Fts5VocabTable *pRet = 0;
+ int rc = SQLITE_OK; /* Return code */
+ int bDb;
+
+ bDb = (argc==6 && strlen(argv[1])==4 && memcmp("temp", argv[1], 4)==0);
+
+ if( argc!=5 && bDb==0 ){
+ *pzErr = sqlite3_mprintf("wrong number of vtable arguments");
+ rc = SQLITE_ERROR;
+ }else{
+ int nByte; /* Bytes of space to allocate */
+ const char *zDb = bDb ? argv[3] : argv[1];
+ const char *zTab = bDb ? argv[4] : argv[3];
+ const char *zType = bDb ? argv[5] : argv[4];
+ int nDb = (int)strlen(zDb)+1;
+ int nTab = (int)strlen(zTab)+1;
+ int eType = 0;
+
+ rc = fts5VocabTableType(zType, pzErr, &eType);
+ if( rc==SQLITE_OK ){
+ assert( eType>=0 && eType<ArraySize(azSchema) );
+ rc = sqlite3_declare_vtab(db, azSchema[eType]);
+ }
+
+ nByte = sizeof(Fts5VocabTable) + nDb + nTab;
+ pRet = sqlite3Fts5MallocZero(&rc, nByte);
+ if( pRet ){
+ pRet->pGlobal = (Fts5Global*)pAux;
+ pRet->eType = eType;
+ pRet->db = db;
+ pRet->zFts5Tbl = (char*)&pRet[1];
+ pRet->zFts5Db = &pRet->zFts5Tbl[nTab];
+ memcpy(pRet->zFts5Tbl, zTab, nTab);
+ memcpy(pRet->zFts5Db, zDb, nDb);
+ sqlite3Fts5Dequote(pRet->zFts5Tbl);
+ sqlite3Fts5Dequote(pRet->zFts5Db);
+ }
+ }
+
+ *ppVTab = (sqlite3_vtab*)pRet;
+ return rc;
+}
+
+
+/*
+** The xConnect() and xCreate() methods for the virtual table. All the
+** work is done in function fts5VocabInitVtab().
+*/
+static int fts5VocabConnectMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr);
+}
+static int fts5VocabCreateMethod(
+ sqlite3 *db, /* Database connection */
+ void *pAux, /* Pointer to tokenizer hash table */
+ int argc, /* Number of elements in argv array */
+ const char * const *argv, /* xCreate/xConnect argument array */
+ sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */
+ char **pzErr /* OUT: sqlite3_malloc'd error message */
+){
+ return fts5VocabInitVtab(db, pAux, argc, argv, ppVtab, pzErr);
+}
+
+/*
+** Implementation of the xBestIndex method.
+*/
+static int fts5VocabBestIndexMethod(
+ sqlite3_vtab *pUnused,
+ sqlite3_index_info *pInfo
+){
+ int i;
+ int iTermEq = -1;
+ int iTermGe = -1;
+ int iTermLe = -1;
+ int idxNum = 0;
+ int nArg = 0;
+
+ UNUSED_PARAM(pUnused);
+
+ for(i=0; i<pInfo->nConstraint; i++){
+ struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
+ if( p->usable==0 ) continue;
+ if( p->iColumn==0 ){ /* term column */
+ if( p->op==SQLITE_INDEX_CONSTRAINT_EQ ) iTermEq = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_LE ) iTermLe = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_LT ) iTermLe = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_GE ) iTermGe = i;
+ if( p->op==SQLITE_INDEX_CONSTRAINT_GT ) iTermGe = i;
+ }
+ }
+
+ if( iTermEq>=0 ){
+ idxNum |= FTS5_VOCAB_TERM_EQ;
+ pInfo->aConstraintUsage[iTermEq].argvIndex = ++nArg;
+ pInfo->estimatedCost = 100;
+ }else{
+ pInfo->estimatedCost = 1000000;
+ if( iTermGe>=0 ){
+ idxNum |= FTS5_VOCAB_TERM_GE;
+ pInfo->aConstraintUsage[iTermGe].argvIndex = ++nArg;
+ pInfo->estimatedCost = pInfo->estimatedCost / 2;
+ }
+ if( iTermLe>=0 ){
+ idxNum |= FTS5_VOCAB_TERM_LE;
+ pInfo->aConstraintUsage[iTermLe].argvIndex = ++nArg;
+ pInfo->estimatedCost = pInfo->estimatedCost / 2;
+ }
+ }
+
+ /* This virtual table always delivers results in ascending order of
+ ** the "term" column (column 0). So if the user has requested this
+ ** specifically - "ORDER BY term" or "ORDER BY term ASC" - set the
+ ** sqlite3_index_info.orderByConsumed flag to tell the core the results
+ ** are already in sorted order. */
+ if( pInfo->nOrderBy==1
+ && pInfo->aOrderBy[0].iColumn==0
+ && pInfo->aOrderBy[0].desc==0
+ ){
+ pInfo->orderByConsumed = 1;
+ }
+
+ pInfo->idxNum = idxNum;
+ return SQLITE_OK;
+}
+
+/*
+** Implementation of xOpen method.
+*/
+static int fts5VocabOpenMethod(
+ sqlite3_vtab *pVTab,
+ sqlite3_vtab_cursor **ppCsr
+){
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pVTab;
+ Fts5Index *pIndex = 0;
+ Fts5Config *pConfig = 0;
+ Fts5VocabCursor *pCsr = 0;
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pStmt = 0;
+ char *zSql = 0;
+
+ zSql = sqlite3Fts5Mprintf(&rc,
+ "SELECT t.%Q FROM %Q.%Q AS t WHERE t.%Q MATCH '*id'",
+ pTab->zFts5Tbl, pTab->zFts5Db, pTab->zFts5Tbl, pTab->zFts5Tbl
+ );
+ if( zSql ){
+ rc = sqlite3_prepare_v2(pTab->db, zSql, -1, &pStmt, 0);
+ }
+ sqlite3_free(zSql);
+ assert( rc==SQLITE_OK || pStmt==0 );
+ if( rc==SQLITE_ERROR ) rc = SQLITE_OK;
+
+ if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
+ i64 iId = sqlite3_column_int64(pStmt, 0);
+ pIndex = sqlite3Fts5IndexFromCsrid(pTab->pGlobal, iId, &pConfig);
+ }
+
+ if( rc==SQLITE_OK && pIndex==0 ){
+ rc = sqlite3_finalize(pStmt);
+ pStmt = 0;
+ if( rc==SQLITE_OK ){
+ pVTab->zErrMsg = sqlite3_mprintf(
+ "no such fts5 table: %s.%s", pTab->zFts5Db, pTab->zFts5Tbl
+ );
+ rc = SQLITE_ERROR;
+ }
+ }
+
+ if( rc==SQLITE_OK ){
+ int nByte = pConfig->nCol * sizeof(i64) * 2 + sizeof(Fts5VocabCursor);
+ pCsr = (Fts5VocabCursor*)sqlite3Fts5MallocZero(&rc, nByte);
+ }
+
+ if( pCsr ){
+ pCsr->pIndex = pIndex;
+ pCsr->pStmt = pStmt;
+ pCsr->pConfig = pConfig;
+ pCsr->aCnt = (i64*)&pCsr[1];
+ pCsr->aDoc = &pCsr->aCnt[pConfig->nCol];
+ }else{
+ sqlite3_finalize(pStmt);
+ }
+
+ *ppCsr = (sqlite3_vtab_cursor*)pCsr;
+ return rc;
+}
+
+static void fts5VocabResetCursor(Fts5VocabCursor *pCsr){
+ pCsr->rowid = 0;
+ sqlite3Fts5IterClose(pCsr->pIter);
+ pCsr->pIter = 0;
+ sqlite3_free(pCsr->zLeTerm);
+ pCsr->nLeTerm = -1;
+ pCsr->zLeTerm = 0;
+}
+
+/*
+** Close the cursor. For additional information see the documentation
+** on the xClose method of the virtual table interface.
+*/
+static int fts5VocabCloseMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ fts5VocabResetCursor(pCsr);
+ sqlite3Fts5BufferFree(&pCsr->term);
+ sqlite3_finalize(pCsr->pStmt);
+ sqlite3_free(pCsr);
+ return SQLITE_OK;
+}
+
+
+/*
+** Advance the cursor to the next row in the table.
+*/
+static int fts5VocabNextMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ Fts5VocabTable *pTab = (Fts5VocabTable*)pCursor->pVtab;
+ int rc = SQLITE_OK;
+ int nCol = pCsr->pConfig->nCol;
+
+ pCsr->rowid++;
+
+ if( pTab->eType==FTS5_VOCAB_COL ){
+ for(pCsr->iCol++; pCsr->iCol<nCol; pCsr->iCol++){
+ if( pCsr->aDoc[pCsr->iCol] ) break;
+ }
+ }
+
+ if( pTab->eType==FTS5_VOCAB_ROW || pCsr->iCol>=nCol ){
+ if( sqlite3Fts5IterEof(pCsr->pIter) ){
+ pCsr->bEof = 1;
+ }else{
+ const char *zTerm;
+ int nTerm;
+
+ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
+ if( pCsr->nLeTerm>=0 ){
+ int nCmp = MIN(nTerm, pCsr->nLeTerm);
+ int bCmp = memcmp(pCsr->zLeTerm, zTerm, nCmp);
+ if( bCmp<0 || (bCmp==0 && pCsr->nLeTerm<nTerm) ){
+ pCsr->bEof = 1;
+ return SQLITE_OK;
+ }
+ }
+
+ sqlite3Fts5BufferSet(&rc, &pCsr->term, nTerm, (const u8*)zTerm);
+ memset(pCsr->aCnt, 0, nCol * sizeof(i64));
+ memset(pCsr->aDoc, 0, nCol * sizeof(i64));
+ pCsr->iCol = 0;
+
+ assert( pTab->eType==FTS5_VOCAB_COL || pTab->eType==FTS5_VOCAB_ROW );
+ while( rc==SQLITE_OK ){
+ const u8 *pPos; int nPos; /* Position list */
+ i64 iPos = 0; /* 64-bit position read from poslist */
+ int iOff = 0; /* Current offset within position list */
+
+ pPos = pCsr->pIter->pData;
+ nPos = pCsr->pIter->nData;
+ switch( pCsr->pConfig->eDetail ){
+ case FTS5_DETAIL_FULL:
+ pPos = pCsr->pIter->pData;
+ nPos = pCsr->pIter->nData;
+ if( pTab->eType==FTS5_VOCAB_ROW ){
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
+ pCsr->aCnt[0]++;
+ }
+ pCsr->aDoc[0]++;
+ }else{
+ int iCol = -1;
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff, &iPos) ){
+ int ii = FTS5_POS2COLUMN(iPos);
+ pCsr->aCnt[ii]++;
+ if( iCol!=ii ){
+ if( ii>=nCol ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
+ pCsr->aDoc[ii]++;
+ iCol = ii;
+ }
+ }
+ }
+ break;
+
+ case FTS5_DETAIL_COLUMNS:
+ if( pTab->eType==FTS5_VOCAB_ROW ){
+ pCsr->aDoc[0]++;
+ }else{
+ while( 0==sqlite3Fts5PoslistNext64(pPos, nPos, &iOff,&iPos) ){
+ assert_nc( iPos>=0 && iPos<nCol );
+ if( iPos>=nCol ){
+ rc = FTS5_CORRUPT;
+ break;
+ }
+ pCsr->aDoc[iPos]++;
+ }
+ }
+ break;
+
+ default:
+ assert( pCsr->pConfig->eDetail==FTS5_DETAIL_NONE );
+ pCsr->aDoc[0]++;
+ break;
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IterNextScan(pCsr->pIter);
+ }
+
+ if( rc==SQLITE_OK ){
+ zTerm = sqlite3Fts5IterTerm(pCsr->pIter, &nTerm);
+ if( nTerm!=pCsr->term.n || memcmp(zTerm, pCsr->term.p, nTerm) ){
+ break;
+ }
+ if( sqlite3Fts5IterEof(pCsr->pIter) ) break;
+ }
+ }
+ }
+ }
+
+ if( rc==SQLITE_OK && pCsr->bEof==0 && pTab->eType==FTS5_VOCAB_COL ){
+ while( pCsr->aDoc[pCsr->iCol]==0 ) pCsr->iCol++;
+ assert( pCsr->iCol<pCsr->pConfig->nCol );
+ }
+ return rc;
+}
+
+/*
+** This is the xFilter implementation for the virtual table.
+*/
+static int fts5VocabFilterMethod(
+ sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */
+ int idxNum, /* Strategy index */
+ const char *zUnused, /* Unused */
+ int nUnused, /* Number of elements in apVal */
+ sqlite3_value **apVal /* Arguments for the indexing scheme */
+){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ int rc = SQLITE_OK;
+
+ int iVal = 0;
+ int f = FTS5INDEX_QUERY_SCAN;
+ const char *zTerm = 0;
+ int nTerm = 0;
+
+ sqlite3_value *pEq = 0;
+ sqlite3_value *pGe = 0;
+ sqlite3_value *pLe = 0;
+
+ UNUSED_PARAM2(zUnused, nUnused);
+
+ fts5VocabResetCursor(pCsr);
+ if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
+ if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
+ if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];
+
+ if( pEq ){
+ zTerm = (const char *)sqlite3_value_text(pEq);
+ nTerm = sqlite3_value_bytes(pEq);
+ f = 0;
+ }else{
+ if( pGe ){
+ zTerm = (const char *)sqlite3_value_text(pGe);
+ nTerm = sqlite3_value_bytes(pGe);
+ }
+ if( pLe ){
+ const char *zCopy = (const char *)sqlite3_value_text(pLe);
+ pCsr->nLeTerm = sqlite3_value_bytes(pLe);
+ pCsr->zLeTerm = sqlite3_malloc(pCsr->nLeTerm+1);
+ if( pCsr->zLeTerm==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ memcpy(pCsr->zLeTerm, zCopy, pCsr->nLeTerm+1);
+ }
+ }
+ }
+
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3Fts5IndexQuery(pCsr->pIndex, zTerm, nTerm, f, 0, &pCsr->pIter);
+ }
+ if( rc==SQLITE_OK ){
+ rc = fts5VocabNextMethod(pCursor);
+ }
+
+ return rc;
+}
+
+/*
+** This is the xEof method of the virtual table. SQLite calls this
+** routine to find out if it has reached the end of a result set.
+*/
+static int fts5VocabEofMethod(sqlite3_vtab_cursor *pCursor){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ return pCsr->bEof;
+}
+
+static int fts5VocabColumnMethod(
+ sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */
+ sqlite3_context *pCtx, /* Context for sqlite3_result_xxx() calls */
+ int iCol /* Index of column to read value from */
+){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ int eDetail = pCsr->pConfig->eDetail;
+ int eType = ((Fts5VocabTable*)(pCursor->pVtab))->eType;
+ i64 iVal = 0;
+
+ if( iCol==0 ){
+ sqlite3_result_text(
+ pCtx, (const char*)pCsr->term.p, pCsr->term.n, SQLITE_TRANSIENT
+ );
+ }else if( eType==FTS5_VOCAB_COL ){
+ assert( iCol==1 || iCol==2 || iCol==3 );
+ if( iCol==1 ){
+ if( eDetail!=FTS5_DETAIL_NONE ){
+ const char *z = pCsr->pConfig->azCol[pCsr->iCol];
+ sqlite3_result_text(pCtx, z, -1, SQLITE_STATIC);
+ }
+ }else if( iCol==2 ){
+ iVal = pCsr->aDoc[pCsr->iCol];
+ }else{
+ iVal = pCsr->aCnt[pCsr->iCol];
+ }
+ }else{
+ assert( iCol==1 || iCol==2 );
+ if( iCol==1 ){
+ iVal = pCsr->aDoc[0];
+ }else{
+ iVal = pCsr->aCnt[0];
+ }
+ }
+
+ if( iVal>0 ) sqlite3_result_int64(pCtx, iVal);
+ return SQLITE_OK;
+}
+
+/*
+** This is the xRowid method. The SQLite core calls this routine to
+** retrieve the rowid for the current row of the result set. The
+** rowid should be written to *pRowid.
+*/
+static int fts5VocabRowidMethod(
+ sqlite3_vtab_cursor *pCursor,
+ sqlite_int64 *pRowid
+){
+ Fts5VocabCursor *pCsr = (Fts5VocabCursor*)pCursor;
+ *pRowid = pCsr->rowid;
+ return SQLITE_OK;
+}
+
+static int sqlite3Fts5VocabInit(Fts5Global *pGlobal, sqlite3 *db){
+ static const sqlite3_module fts5Vocab = {
+ /* iVersion */ 2,
+ /* xCreate */ fts5VocabCreateMethod,
+ /* xConnect */ fts5VocabConnectMethod,
+ /* xBestIndex */ fts5VocabBestIndexMethod,
+ /* xDisconnect */ fts5VocabDisconnectMethod,
+ /* xDestroy */ fts5VocabDestroyMethod,
+ /* xOpen */ fts5VocabOpenMethod,
+ /* xClose */ fts5VocabCloseMethod,
+ /* xFilter */ fts5VocabFilterMethod,
+ /* xNext */ fts5VocabNextMethod,
+ /* xEof */ fts5VocabEofMethod,
+ /* xColumn */ fts5VocabColumnMethod,
+ /* xRowid */ fts5VocabRowidMethod,
+ /* xUpdate */ 0,
+ /* xBegin */ 0,
+ /* xSync */ 0,
+ /* xCommit */ 0,
+ /* xRollback */ 0,
+ /* xFindFunction */ 0,
+ /* xRename */ 0,
+ /* xSavepoint */ 0,
+ /* xRelease */ 0,
+ /* xRollbackTo */ 0,
+ };
+ void *p = (void*)pGlobal;
+
+ return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0);
+}
+
+
+
+
+
+#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */
+
+/************** End of fts5.c ************************************************/
diff --git a/lib/sqlite/sqlite3.h b/lib/sqlite/sqlite3.h
index 28a500d5b07a..d02aeb187ed3 100644
--- a/lib/sqlite/sqlite3.h
+++ b/lib/sqlite/sqlite3.h
@@ -23,15 +23,15 @@
**
** The official C-language API documentation for SQLite is derived
** from comments in this file. This file is the authoritative source
-** on how SQLite interfaces are suppose to operate.
+** on how SQLite interfaces are supposed to operate.
**
** The name of this file under configuration management is "sqlite.h.in".
** The makefile makes some minor changes to this file (such as inserting
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
*/
-#ifndef _SQLITE3_H_
-#define _SQLITE3_H_
+#ifndef SQLITE3_H
+#define SQLITE3_H
#include <stdarg.h> /* Needed for the definition of va_list */
/*
@@ -43,21 +43,34 @@ extern "C" {
/*
-** Add the ability to override 'extern'
+** Provide the ability to override linkage features of the interface.
*/
#ifndef SQLITE_EXTERN
# define SQLITE_EXTERN extern
#endif
-
#ifndef SQLITE_API
# define SQLITE_API
#endif
-
+#ifndef SQLITE_CDECL
+# define SQLITE_CDECL
+#endif
+#ifndef SQLITE_APICALL
+# define SQLITE_APICALL
+#endif
+#ifndef SQLITE_STDCALL
+# define SQLITE_STDCALL SQLITE_APICALL
+#endif
+#ifndef SQLITE_CALLBACK
+# define SQLITE_CALLBACK
+#endif
+#ifndef SQLITE_SYSAPI
+# define SQLITE_SYSAPI
+#endif
/*
** These no-op macros are used in front of interfaces to mark those
** interfaces as either deprecated or experimental. New applications
-** should not use deprecated interfaces - they are support for backwards
+** should not use deprecated interfaces - they are supported for backwards
** compatibility only. Application writers should be aware that
** experimental interfaces are subject to change in point releases.
**
@@ -95,7 +108,8 @@ extern "C" {
** be held constant and Z will be incremented or else Y will be incremented
** and Z will be reset to zero.
**
-** Since version 3.6.18, SQLite source code has been stored in the
+** Since [version 3.6.18] ([dateof:3.6.18]),
+** SQLite source code has been stored in the
** <a href="http://www.fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
** a string which identifies a particular check-in of SQLite
@@ -107,9 +121,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.8"
-#define SQLITE_VERSION_NUMBER 3007008
-#define SQLITE_SOURCE_ID "2011-09-19 14:49:19 3e0da808d2f5b4d12046e05980ca04578f581177"
+#define SQLITE_VERSION "3.15.1"
+#define SQLITE_VERSION_NUMBER 3015001
+#define SQLITE_SOURCE_ID "2016-11-04 12:08:49 1136863c76576110e710dd5d69ab6bf347c65e36"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -120,7 +134,7 @@ extern "C" {
** but are associated with the library instead of the header file. ^(Cautious
** programmers might include assert() statements in their application to
** verify that values returned by these interfaces match the macros in
-** the header, and thus insure that the application is
+** the header, and thus ensure that the application is
** compiled with matching library and header files.
**
** <blockquote><pre>
@@ -177,7 +191,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N);
** CAPI3REF: Test To See If The Library Is Threadsafe
**
** ^The sqlite3_threadsafe() function returns zero if and only if
-** SQLite was compiled mutexing code omitted due to the
+** SQLite was compiled with mutexing code omitted due to the
** [SQLITE_THREADSAFE] compile-time option being set to 0.
**
** SQLite can be compiled with or without mutexes. When
@@ -201,7 +215,7 @@ SQLITE_API const char *sqlite3_compileoption_get(int N);
** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but
** can be fully or partially disabled using a call to [sqlite3_config()]
** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD],
-** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the
+** or [SQLITE_CONFIG_SERIALIZED]. ^(The return value of the
** sqlite3_threadsafe() function shows only the compile-time setting of
** thread safety, not any run-time changes to that setting made by
** sqlite3_config(). In other words, the return value from sqlite3_threadsafe()
@@ -219,7 +233,8 @@ SQLITE_API int sqlite3_threadsafe(void);
** the opaque structure named "sqlite3". It is useful to think of an sqlite3
** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
-** is its destructor. There are many other interfaces (such as
+** and [sqlite3_close_v2()] are its destructors. There are many other
+** interfaces (such as
** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
** [sqlite3_busy_timeout()] to name but three) that are methods on an
** sqlite3 object.
@@ -265,29 +280,48 @@ typedef sqlite_uint64 sqlite3_uint64;
/*
** CAPI3REF: Closing A Database Connection
-**
-** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
-** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
-** successfully destroyed and all associated resources are deallocated.
-**
-** Applications must [sqlite3_finalize | finalize] all [prepared statements]
-** and [sqlite3_blob_close | close] all [BLOB handles] associated with
-** the [sqlite3] object prior to attempting to close the object. ^If
-** sqlite3_close() is called on a [database connection] that still has
-** outstanding [prepared statements] or [BLOB handles], then it returns
-** SQLITE_BUSY.
-**
-** ^If [sqlite3_close()] is invoked while a transaction is open,
+** DESTRUCTOR: sqlite3
+**
+** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
+** for the [sqlite3] object.
+** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
+** the [sqlite3] object is successfully destroyed and all associated
+** resources are deallocated.
+**
+** ^If the database connection is associated with unfinalized prepared
+** statements or unfinished sqlite3_backup objects then sqlite3_close()
+** will leave the database connection open and return [SQLITE_BUSY].
+** ^If sqlite3_close_v2() is called with unfinalized prepared statements
+** and/or unfinished sqlite3_backups, then the database connection becomes
+** an unusable "zombie" which will automatically be deallocated when the
+** last prepared statement is finalized or the last sqlite3_backup is
+** finished. The sqlite3_close_v2() interface is intended for use with
+** host languages that are garbage collected, and where the order in which
+** destructors are called is arbitrary.
+**
+** Applications should [sqlite3_finalize | finalize] all [prepared statements],
+** [sqlite3_blob_close | close] all [BLOB handles], and
+** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
+** with the [sqlite3] object prior to attempting to close the object. ^If
+** sqlite3_close_v2() is called on a [database connection] that still has
+** outstanding [prepared statements], [BLOB handles], and/or
+** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
+** of resources is deferred until all [prepared statements], [BLOB handles],
+** and [sqlite3_backup] objects are also destroyed.
+**
+** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
**
-** The C parameter to [sqlite3_close(C)] must be either a NULL
+** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
+** must be either a NULL
** pointer or an [sqlite3] object pointer obtained
** from [sqlite3_open()], [sqlite3_open16()], or
** [sqlite3_open_v2()], and not previously closed.
-** ^Calling sqlite3_close() with a NULL pointer argument is a
-** harmless no-op.
+** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer
+** argument is a harmless no-op.
*/
-SQLITE_API int sqlite3_close(sqlite3 *);
+SQLITE_API int sqlite3_close(sqlite3*);
+SQLITE_API int sqlite3_close_v2(sqlite3*);
/*
** The type for a callback function.
@@ -298,6 +332,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
/*
** CAPI3REF: One-Step Query Execution Interface
+** METHOD: sqlite3
**
** The sqlite3_exec() interface is a convenience wrapper around
** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()],
@@ -322,7 +357,7 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** from [sqlite3_malloc()] and passed back through the 5th parameter.
** To avoid memory leaks, the application should invoke [sqlite3_free()]
** on error message strings returned through the 5th parameter of
-** of sqlite3_exec() after the error message string is no longer needed.
+** sqlite3_exec() after the error message string is no longer needed.
** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors
** occur, then sqlite3_exec() sets the pointer in its 5th parameter to
** NULL before returning.
@@ -349,9 +384,9 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**);
** Restrictions:
**
** <ul>
-** <li> The application must insure that the 1st parameter to sqlite3_exec()
+** <li> The application must ensure that the 1st parameter to sqlite3_exec()
** is a valid and open [database connection].
-** <li> The application must not close [database connection] specified by
+** <li> The application must not close the [database connection] specified by
** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running.
** <li> The application must not modify the SQL statement text passed into
** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running.
@@ -367,16 +402,14 @@ SQLITE_API int sqlite3_exec(
/*
** CAPI3REF: Result Codes
-** KEYWORDS: SQLITE_OK {error code} {error codes}
-** KEYWORDS: {result code} {result codes}
+** KEYWORDS: {result code definitions}
**
** Many SQLite functions return an integer result code from the set shown
-** here in order to indicates success or failure.
+** here in order to indicate success or failure.
**
** New error codes may be added in future versions of SQLite.
**
-** See also: [SQLITE_IOERR_READ | extended result codes],
-** [sqlite3_vtab_on_conflict()] [SQLITE_ROLLBACK | result codes].
+** See also: [extended result code definitions]
*/
#define SQLITE_OK 0 /* Successful result */
/* beginning-of-error-codes */
@@ -406,32 +439,28 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_FORMAT 24 /* Auxiliary database format error */
#define SQLITE_RANGE 25 /* 2nd parameter to sqlite3_bind out of range */
#define SQLITE_NOTADB 26 /* File opened that is not a database file */
+#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */
+#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */
#define SQLITE_ROW 100 /* sqlite3_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step() has finished executing */
/* end-of-error-codes */
/*
** CAPI3REF: Extended Result Codes
-** KEYWORDS: {extended error code} {extended error codes}
-** KEYWORDS: {extended result code} {extended result codes}
+** KEYWORDS: {extended result code definitions}
**
-** In its default configuration, SQLite API routines return one of 26 integer
-** [SQLITE_OK | result codes]. However, experience has shown that many of
+** In its default configuration, SQLite API routines return one of 30 integer
+** [result codes]. However, experience has shown that many of
** these result codes are too coarse-grained. They do not provide as
** much information about problems as programmers might like. In an effort to
-** address this, newer versions of SQLite (version 3.3.8 and later) include
+** address this, newer versions of SQLite (version 3.3.8 [dateof:3.3.8]
+** and later) include
** support for additional result codes that provide more detailed information
-** about errors. The extended result codes are enabled or disabled
+** about errors. These [extended result codes] are enabled or disabled
** on a per database connection basis using the
-** [sqlite3_extended_result_codes()] API.
-**
-** Some of the available extended result codes are listed here.
-** One may expect the number of extended result codes will be expand
-** over time. Software that uses extended result codes should expect
-** to see new result codes in future releases of SQLite.
-**
-** The SQLITE_OK result code will never be extended. It will always
-** be exactly zero.
+** [sqlite3_extended_result_codes()] API. Or, the extended code for
+** the most recent error can be obtained using
+** [sqlite3_extended_errcode()].
*/
#define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8))
#define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8))
@@ -455,12 +484,40 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOERR_SHMLOCK (SQLITE_IOERR | (20<<8))
#define SQLITE_IOERR_SHMMAP (SQLITE_IOERR | (21<<8))
#define SQLITE_IOERR_SEEK (SQLITE_IOERR | (22<<8))
+#define SQLITE_IOERR_DELETE_NOENT (SQLITE_IOERR | (23<<8))
+#define SQLITE_IOERR_MMAP (SQLITE_IOERR | (24<<8))
+#define SQLITE_IOERR_GETTEMPPATH (SQLITE_IOERR | (25<<8))
+#define SQLITE_IOERR_CONVPATH (SQLITE_IOERR | (26<<8))
+#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
+#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
+#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
+#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
+#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
+#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
+#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
+#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
+#define SQLITE_ABORT_ROLLBACK (SQLITE_ABORT | (2<<8))
+#define SQLITE_CONSTRAINT_CHECK (SQLITE_CONSTRAINT | (1<<8))
+#define SQLITE_CONSTRAINT_COMMITHOOK (SQLITE_CONSTRAINT | (2<<8))
+#define SQLITE_CONSTRAINT_FOREIGNKEY (SQLITE_CONSTRAINT | (3<<8))
+#define SQLITE_CONSTRAINT_FUNCTION (SQLITE_CONSTRAINT | (4<<8))
+#define SQLITE_CONSTRAINT_NOTNULL (SQLITE_CONSTRAINT | (5<<8))
+#define SQLITE_CONSTRAINT_PRIMARYKEY (SQLITE_CONSTRAINT | (6<<8))
+#define SQLITE_CONSTRAINT_TRIGGER (SQLITE_CONSTRAINT | (7<<8))
+#define SQLITE_CONSTRAINT_UNIQUE (SQLITE_CONSTRAINT | (8<<8))
+#define SQLITE_CONSTRAINT_VTAB (SQLITE_CONSTRAINT | (9<<8))
+#define SQLITE_CONSTRAINT_ROWID (SQLITE_CONSTRAINT |(10<<8))
+#define SQLITE_NOTICE_RECOVER_WAL (SQLITE_NOTICE | (1<<8))
+#define SQLITE_NOTICE_RECOVER_ROLLBACK (SQLITE_NOTICE | (2<<8))
+#define SQLITE_WARNING_AUTOINDEX (SQLITE_WARNING | (1<<8))
+#define SQLITE_AUTH_USER (SQLITE_AUTH | (1<<8))
+#define SQLITE_OK_LOAD_PERMANENTLY (SQLITE_OK | (1<<8))
/*
** CAPI3REF: Flags For File Open Operations
@@ -476,6 +533,7 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_OPEN_EXCLUSIVE 0x00000010 /* VFS only */
#define SQLITE_OPEN_AUTOPROXY 0x00000020 /* VFS only */
#define SQLITE_OPEN_URI 0x00000040 /* Ok for sqlite3_open_v2() */
+#define SQLITE_OPEN_MEMORY 0x00000080 /* Ok for sqlite3_open_v2() */
#define SQLITE_OPEN_MAIN_DB 0x00000100 /* VFS only */
#define SQLITE_OPEN_TEMP_DB 0x00000200 /* VFS only */
#define SQLITE_OPEN_TRANSIENT_DB 0x00000400 /* VFS only */
@@ -495,7 +553,7 @@ SQLITE_API int sqlite3_exec(
** CAPI3REF: Device Characteristics
**
** The xDeviceCharacteristics method of the [sqlite3_io_methods]
-** object returns an integer which is a vector of the these
+** object returns an integer which is a vector of these
** bit values expressing I/O characteristics of the mass storage
** device that holds the file that the [sqlite3_io_methods]
** refers to.
@@ -509,7 +567,15 @@ SQLITE_API int sqlite3_exec(
** first then the size of the file is extended, never the other
** way around. The SQLITE_IOCAP_SEQUENTIAL property means that
** information is written to disk in the same order as calls
-** to xWrite().
+** to xWrite(). The SQLITE_IOCAP_POWERSAFE_OVERWRITE property means that
+** after reboot following a crash or power loss, the only bytes in a
+** file that were written at the application level might have changed
+** and that adjacent bytes, even bytes within the same sector are
+** guaranteed to be unchanged. The SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
+** flag indicate that a file cannot be deleted when open. The
+** SQLITE_IOCAP_IMMUTABLE flag indicates that the file is on
+** read-only media and cannot be changed even by processes with
+** elevated privileges.
*/
#define SQLITE_IOCAP_ATOMIC 0x00000001
#define SQLITE_IOCAP_ATOMIC512 0x00000002
@@ -523,6 +589,8 @@ SQLITE_API int sqlite3_exec(
#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
+#define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000
+#define SQLITE_IOCAP_IMMUTABLE 0x00002000
/*
** CAPI3REF: File Locking Levels
@@ -629,7 +697,7 @@ struct sqlite3_file {
** locking strategy (for example to use dot-file locks), to inquire
** about the status of a lock, or to break stale locks. The SQLite
** core reserves all opcodes less than 100 for its own use.
-** A [SQLITE_FCNTL_LOCKSTATE | list of opcodes] less than 100 is available.
+** A [file control opcodes | list of opcodes] less than 100 is available.
** Applications that define a custom xFileControl method should use opcodes
** greater than 100 to avoid conflicts. VFS implementations should
** return [SQLITE_NOTFOUND] for file control opcodes that they do not
@@ -694,24 +762,31 @@ struct sqlite3_io_methods {
void (*xShmBarrier)(sqlite3_file*);
int (*xShmUnmap)(sqlite3_file*, int deleteFlag);
/* Methods above are valid for version 2 */
+ int (*xFetch)(sqlite3_file*, sqlite3_int64 iOfst, int iAmt, void **pp);
+ int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p);
+ /* Methods above are valid for version 3 */
/* Additional methods may be added in future releases */
};
/*
** CAPI3REF: Standard File Control Opcodes
+** KEYWORDS: {file control opcodes} {file control opcode}
**
** These integer constants are opcodes for the xFileControl method
** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()]
** interface.
**
+** <ul>
+** <li>[[SQLITE_FCNTL_LOCKSTATE]]
** The [SQLITE_FCNTL_LOCKSTATE] opcode is used for debugging. This
** opcode causes the xFileControl method to write the current state of
** the lock (one of [SQLITE_LOCK_NONE], [SQLITE_LOCK_SHARED],
** [SQLITE_LOCK_RESERVED], [SQLITE_LOCK_PENDING], or [SQLITE_LOCK_EXCLUSIVE])
** into an integer that the pArg argument points to. This capability
-** is used during testing and only needs to be supported when SQLITE_TEST
-** is defined.
+** is used during testing and is only available when the SQLITE_TEST
+** compile-time option is used.
**
+** <li>[[SQLITE_FCNTL_SIZE_HINT]]
** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS
** layer a hint of how large the database file will grow to be during the
** current transaction. This hint is not guaranteed to be accurate but it
@@ -719,6 +794,7 @@ struct sqlite3_io_methods {
** file space based on this hint in order to help writes to the database
** file run faster.
**
+** <li>[[SQLITE_FCNTL_CHUNK_SIZE]]
** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS
** extends and truncates the database file in chunks of a size specified
** by the user. The fourth argument to [sqlite3_file_control()] should
@@ -727,29 +803,51 @@ struct sqlite3_io_methods {
** chunks (say 1MB at a time), may reduce file-system fragmentation and
** improve performance on some systems.
**
+** <li>[[SQLITE_FCNTL_FILE_POINTER]]
** The [SQLITE_FCNTL_FILE_POINTER] opcode is used to obtain a pointer
** to the [sqlite3_file] object associated with a particular database
-** connection. See the [sqlite3_file_control()] documentation for
-** additional information.
-**
-** ^(The [SQLITE_FCNTL_SYNC_OMITTED] opcode is generated internally by
-** SQLite and sent to all VFSes in place of a call to the xSync method
-** when the database connection has [PRAGMA synchronous] set to OFF.)^
-** Some specialized VFSes need this signal in order to operate correctly
-** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most
-** VFSes do not need this signal and should silently ignore this opcode.
-** Applications should not call [sqlite3_file_control()] with this
-** opcode as doing so may disrupt the operation of the specialized VFSes
-** that do require it.
-**
+** connection. See also [SQLITE_FCNTL_JOURNAL_POINTER].
+**
+** <li>[[SQLITE_FCNTL_JOURNAL_POINTER]]
+** The [SQLITE_FCNTL_JOURNAL_POINTER] opcode is used to obtain a pointer
+** to the [sqlite3_file] object associated with the journal file (either
+** the [rollback journal] or the [write-ahead log]) for a particular database
+** connection. See also [SQLITE_FCNTL_FILE_POINTER].
+**
+** <li>[[SQLITE_FCNTL_SYNC_OMITTED]]
+** No longer in use.
+**
+** <li>[[SQLITE_FCNTL_SYNC]]
+** The [SQLITE_FCNTL_SYNC] opcode is generated internally by SQLite and
+** sent to the VFS immediately before the xSync method is invoked on a
+** database file descriptor. Or, if the xSync method is not invoked
+** because the user has configured SQLite with
+** [PRAGMA synchronous | PRAGMA synchronous=OFF] it is invoked in place
+** of the xSync method. In most cases, the pointer argument passed with
+** this file-control is NULL. However, if the database file is being synced
+** as part of a multi-database commit, the argument points to a nul-terminated
+** string containing the transactions master-journal file name. VFSes that
+** do not need this signal should silently ignore this opcode. Applications
+** should not call [sqlite3_file_control()] with this opcode as doing so may
+** disrupt the operation of the specialized VFSes that do require it.
+**
+** <li>[[SQLITE_FCNTL_COMMIT_PHASETWO]]
+** The [SQLITE_FCNTL_COMMIT_PHASETWO] opcode is generated internally by SQLite
+** and sent to the VFS after a transaction has been committed immediately
+** but before the database is unlocked. VFSes that do not need this signal
+** should silently ignore this opcode. Applications should not call
+** [sqlite3_file_control()] with this opcode as doing so may disrupt the
+** operation of the specialized VFSes that do require it.
+**
+** <li>[[SQLITE_FCNTL_WIN32_AV_RETRY]]
** ^The [SQLITE_FCNTL_WIN32_AV_RETRY] opcode is used to configure automatic
** retry counts and intervals for certain disk I/O operations for the
-** windows [VFS] in order to work to provide robustness against
+** windows [VFS] in order to provide robustness in the presence of
** anti-virus programs. By default, the windows VFS will retry file read,
-** file write, and file delete opertions up to 10 times, with a delay
+** file write, and file delete operations up to 10 times, with a delay
** of 25 milliseconds before the first retry and with the delay increasing
** by an additional 25 milliseconds with each subsequent retry. This
-** opcode allows those to values (10 retries and 25 milliseconds of delay)
+** opcode allows these two values (10 retries and 25 milliseconds of delay)
** to be adjusted. The values are changed for all database connections
** within the same process. The argument is a pointer to an array of two
** integers where the first integer i the new retry count and the second
@@ -758,8 +856,9 @@ struct sqlite3_io_methods {
** into the array entry, allowing the current retry settings to be
** interrogated. The zDbName parameter is ignored.
**
+** <li>[[SQLITE_FCNTL_PERSIST_WAL]]
** ^The [SQLITE_FCNTL_PERSIST_WAL] opcode is used to set or query the
-** persistent [WAL | Write AHead Log] setting. By default, the auxiliary
+** persistent [WAL | Write Ahead Log] setting. By default, the auxiliary
** write ahead log and shared memory files used for transaction control
** are automatically deleted when the latest connection to the database
** closes. Setting persistent WAL mode causes those files to persist after
@@ -771,18 +870,177 @@ struct sqlite3_io_methods {
** That integer is 0 to disable persistent WAL mode or 1 to enable persistent
** WAL mode. If the integer is -1, then it is overwritten with the current
** WAL persistence setting.
-**
+**
+** <li>[[SQLITE_FCNTL_POWERSAFE_OVERWRITE]]
+** ^The [SQLITE_FCNTL_POWERSAFE_OVERWRITE] opcode is used to set or query the
+** persistent "powersafe-overwrite" or "PSOW" setting. The PSOW setting
+** determines the [SQLITE_IOCAP_POWERSAFE_OVERWRITE] bit of the
+** xDeviceCharacteristics methods. The fourth parameter to
+** [sqlite3_file_control()] for this opcode should be a pointer to an integer.
+** That integer is 0 to disable zero-damage mode or 1 to enable zero-damage
+** mode. If the integer is -1, then it is overwritten with the current
+** zero-damage mode setting.
+**
+** <li>[[SQLITE_FCNTL_OVERWRITE]]
+** ^The [SQLITE_FCNTL_OVERWRITE] opcode is invoked by SQLite after opening
+** a write transaction to indicate that, unless it is rolled back for some
+** reason, the entire database file will be overwritten by the current
+** transaction. This is used by VACUUM operations.
+**
+** <li>[[SQLITE_FCNTL_VFSNAME]]
+** ^The [SQLITE_FCNTL_VFSNAME] opcode can be used to obtain the names of
+** all [VFSes] in the VFS stack. The names are of all VFS shims and the
+** final bottom-level VFS are written into memory obtained from
+** [sqlite3_malloc()] and the result is stored in the char* variable
+** that the fourth parameter of [sqlite3_file_control()] points to.
+** The caller is responsible for freeing the memory when done. As with
+** all file-control actions, there is no guarantee that this will actually
+** do anything. Callers should initialize the char* variable to a NULL
+** pointer in case this file-control is not implemented. This file-control
+** is intended for diagnostic use only.
+**
+** <li>[[SQLITE_FCNTL_VFS_POINTER]]
+** ^The [SQLITE_FCNTL_VFS_POINTER] opcode finds a pointer to the top-level
+** [VFSes] currently in use. ^(The argument X in
+** sqlite3_file_control(db,SQLITE_FCNTL_VFS_POINTER,X) must be
+** of type "[sqlite3_vfs] **". This opcodes will set *X
+** to a pointer to the top-level VFS.)^
+** ^When there are multiple VFS shims in the stack, this opcode finds the
+** upper-most shim only.
+**
+** <li>[[SQLITE_FCNTL_PRAGMA]]
+** ^Whenever a [PRAGMA] statement is parsed, an [SQLITE_FCNTL_PRAGMA]
+** file control is sent to the open [sqlite3_file] object corresponding
+** to the database file to which the pragma statement refers. ^The argument
+** to the [SQLITE_FCNTL_PRAGMA] file control is an array of
+** pointers to strings (char**) in which the second element of the array
+** is the name of the pragma and the third element is the argument to the
+** pragma or NULL if the pragma has no argument. ^The handler for an
+** [SQLITE_FCNTL_PRAGMA] file control can optionally make the first element
+** of the char** argument point to a string obtained from [sqlite3_mprintf()]
+** or the equivalent and that string will become the result of the pragma or
+** the error message if the pragma fails. ^If the
+** [SQLITE_FCNTL_PRAGMA] file control returns [SQLITE_NOTFOUND], then normal
+** [PRAGMA] processing continues. ^If the [SQLITE_FCNTL_PRAGMA]
+** file control returns [SQLITE_OK], then the parser assumes that the
+** VFS has handled the PRAGMA itself and the parser generates a no-op
+** prepared statement if result string is NULL, or that returns a copy
+** of the result string if the string is non-NULL.
+** ^If the [SQLITE_FCNTL_PRAGMA] file control returns
+** any result code other than [SQLITE_OK] or [SQLITE_NOTFOUND], that means
+** that the VFS encountered an error while handling the [PRAGMA] and the
+** compilation of the PRAGMA fails with an error. ^The [SQLITE_FCNTL_PRAGMA]
+** file control occurs at the beginning of pragma statement analysis and so
+** it is able to override built-in [PRAGMA] statements.
+**
+** <li>[[SQLITE_FCNTL_BUSYHANDLER]]
+** ^The [SQLITE_FCNTL_BUSYHANDLER]
+** file-control may be invoked by SQLite on the database file handle
+** shortly after it is opened in order to provide a custom VFS with access
+** to the connections busy-handler callback. The argument is of type (void **)
+** - an array of two (void *) values. The first (void *) actually points
+** to a function of type (int (*)(void *)). In order to invoke the connections
+** busy-handler, this function should be invoked with the second (void *) in
+** the array as the only argument. If it returns non-zero, then the operation
+** should be retried. If it returns zero, the custom VFS should abandon the
+** current operation.
+**
+** <li>[[SQLITE_FCNTL_TEMPFILENAME]]
+** ^Application can invoke the [SQLITE_FCNTL_TEMPFILENAME] file-control
+** to have SQLite generate a
+** temporary filename using the same algorithm that is followed to generate
+** temporary filenames for TEMP tables and other internal uses. The
+** argument should be a char** which will be filled with the filename
+** written into memory obtained from [sqlite3_malloc()]. The caller should
+** invoke [sqlite3_free()] on the result to avoid a memory leak.
+**
+** <li>[[SQLITE_FCNTL_MMAP_SIZE]]
+** The [SQLITE_FCNTL_MMAP_SIZE] file control is used to query or set the
+** maximum number of bytes that will be used for memory-mapped I/O.
+** The argument is a pointer to a value of type sqlite3_int64 that
+** is an advisory maximum number of bytes in the file to memory map. The
+** pointer is overwritten with the old value. The limit is not changed if
+** the value originally pointed to is negative, and so the current limit
+** can be queried by passing in a pointer to a negative number. This
+** file-control is used internally to implement [PRAGMA mmap_size].
+**
+** <li>[[SQLITE_FCNTL_TRACE]]
+** The [SQLITE_FCNTL_TRACE] file control provides advisory information
+** to the VFS about what the higher layers of the SQLite stack are doing.
+** This file control is used by some VFS activity tracing [shims].
+** The argument is a zero-terminated string. Higher layers in the
+** SQLite stack may generate instances of this file control if
+** the [SQLITE_USE_FCNTL_TRACE] compile-time option is enabled.
+**
+** <li>[[SQLITE_FCNTL_HAS_MOVED]]
+** The [SQLITE_FCNTL_HAS_MOVED] file control interprets its argument as a
+** pointer to an integer and it writes a boolean into that integer depending
+** on whether or not the file has been renamed, moved, or deleted since it
+** was first opened.
+**
+** <li>[[SQLITE_FCNTL_WIN32_GET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_GET_HANDLE] opcode can be used to obtain the
+** underlying native file handle associated with a file handle. This file
+** control interprets its argument as a pointer to a native file handle and
+** writes the resulting value there.
+**
+** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]]
+** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This
+** opcode causes the xFileControl method to swap the file handle with the one
+** pointed to by the pArg argument. This capability is used during testing
+** and only needs to be supported when SQLITE_TEST is defined.
+**
+** <li>[[SQLITE_FCNTL_WAL_BLOCK]]
+** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might
+** be advantageous to block on the next WAL lock if the lock is not immediately
+** available. The WAL subsystem issues this signal during rare
+** circumstances in order to fix a problem with priority inversion.
+** Applications should <em>not</em> use this file-control.
+**
+** <li>[[SQLITE_FCNTL_ZIPVFS]]
+** The [SQLITE_FCNTL_ZIPVFS] opcode is implemented by zipvfs only. All other
+** VFS should return SQLITE_NOTFOUND for this opcode.
+**
+** <li>[[SQLITE_FCNTL_RBU]]
+** The [SQLITE_FCNTL_RBU] opcode is implemented by the special VFS used by
+** the RBU extension only. All other VFS should return SQLITE_NOTFOUND for
+** this opcode.
+** </ul>
*/
-#define SQLITE_FCNTL_LOCKSTATE 1
-#define SQLITE_GET_LOCKPROXYFILE 2
-#define SQLITE_SET_LOCKPROXYFILE 3
-#define SQLITE_LAST_ERRNO 4
-#define SQLITE_FCNTL_SIZE_HINT 5
-#define SQLITE_FCNTL_CHUNK_SIZE 6
-#define SQLITE_FCNTL_FILE_POINTER 7
-#define SQLITE_FCNTL_SYNC_OMITTED 8
-#define SQLITE_FCNTL_WIN32_AV_RETRY 9
-#define SQLITE_FCNTL_PERSIST_WAL 10
+#define SQLITE_FCNTL_LOCKSTATE 1
+#define SQLITE_FCNTL_GET_LOCKPROXYFILE 2
+#define SQLITE_FCNTL_SET_LOCKPROXYFILE 3
+#define SQLITE_FCNTL_LAST_ERRNO 4
+#define SQLITE_FCNTL_SIZE_HINT 5
+#define SQLITE_FCNTL_CHUNK_SIZE 6
+#define SQLITE_FCNTL_FILE_POINTER 7
+#define SQLITE_FCNTL_SYNC_OMITTED 8
+#define SQLITE_FCNTL_WIN32_AV_RETRY 9
+#define SQLITE_FCNTL_PERSIST_WAL 10
+#define SQLITE_FCNTL_OVERWRITE 11
+#define SQLITE_FCNTL_VFSNAME 12
+#define SQLITE_FCNTL_POWERSAFE_OVERWRITE 13
+#define SQLITE_FCNTL_PRAGMA 14
+#define SQLITE_FCNTL_BUSYHANDLER 15
+#define SQLITE_FCNTL_TEMPFILENAME 16
+#define SQLITE_FCNTL_MMAP_SIZE 18
+#define SQLITE_FCNTL_TRACE 19
+#define SQLITE_FCNTL_HAS_MOVED 20
+#define SQLITE_FCNTL_SYNC 21
+#define SQLITE_FCNTL_COMMIT_PHASETWO 22
+#define SQLITE_FCNTL_WIN32_SET_HANDLE 23
+#define SQLITE_FCNTL_WAL_BLOCK 24
+#define SQLITE_FCNTL_ZIPVFS 25
+#define SQLITE_FCNTL_RBU 26
+#define SQLITE_FCNTL_VFS_POINTER 27
+#define SQLITE_FCNTL_JOURNAL_POINTER 28
+#define SQLITE_FCNTL_WIN32_GET_HANDLE 29
+
+/* deprecated names */
+#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
+#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
+#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
+
/*
** CAPI3REF: Mutex Handle
@@ -797,6 +1055,16 @@ struct sqlite3_io_methods {
typedef struct sqlite3_mutex sqlite3_mutex;
/*
+** CAPI3REF: Loadable Extension Thunk
+**
+** A pointer to the opaque sqlite3_api_routines structure is passed as
+** the third parameter to entry points of [loadable extensions]. This
+** structure must be typedefed in order to work around compiler warnings
+** on some platforms.
+*/
+typedef struct sqlite3_api_routines sqlite3_api_routines;
+
+/*
** CAPI3REF: OS Interface Object
**
** An instance of the sqlite3_vfs object defines the interface between
@@ -837,7 +1105,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
** from xFullPathname() with an optional suffix added.
** ^If a suffix is added to the zFilename parameter, it will
** consist of a single "-" character followed by no more than
-** 10 alphanumeric and/or "-" characters.
+** 11 alphanumeric and/or "-" characters.
** ^SQLite further guarantees that
** the string will be valid and unchanged until xClose() is
** called. Because of the previous sentence,
@@ -989,7 +1257,7 @@ struct sqlite3_vfs {
const char *(*xNextSystemCall)(sqlite3_vfs*, const char *zName);
/*
** The methods above are in versions 1 through 3 of the sqlite_vfs object.
- ** New fields may be appended in figure versions. The iVersion
+ ** New fields may be appended in future versions. The iVersion
** value will increment whenever this happens.
*/
};
@@ -1034,7 +1302,7 @@ struct sqlite3_vfs {
** </ul>
**
** When unlocking, the same SHARED or EXCLUSIVE flag must be supplied as
-** was given no the corresponding lock.
+** was given on the corresponding lock.
**
** The xShmLock method can transition between unlocked and SHARED or
** between unlocked and EXCLUSIVE. It cannot transition between SHARED
@@ -1145,9 +1413,11 @@ SQLITE_API int sqlite3_os_end(void);
** applications and so this routine is usually not necessary. It is
** provided to support rare applications with unusual needs.
**
-** The sqlite3_config() interface is not threadsafe. The application
-** must insure that no other SQLite interfaces are invoked by other
-** threads while sqlite3_config() is running. Furthermore, sqlite3_config()
+** <b>The sqlite3_config() interface is not threadsafe. The application
+** must ensure that no other SQLite interfaces are invoked by other
+** threads while sqlite3_config() is running.</b>
+**
+** The sqlite3_config() interface
** may only be invoked prior to library initialization using
** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()].
** ^If sqlite3_config() is called after [sqlite3_initialize()] and before
@@ -1169,6 +1439,7 @@ SQLITE_API int sqlite3_config(int, ...);
/*
** CAPI3REF: Configure database connections
+** METHOD: sqlite3
**
** The sqlite3_db_config() interface is used to make configuration
** changes to a [database connection]. The interface is similar to
@@ -1227,7 +1498,7 @@ SQLITE_API int sqlite3_db_config(sqlite3*, int op, ...);
** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0,
** that causes the corresponding memory allocation to fail.
**
-** The xInit method initializes the memory allocator. (For example,
+** The xInit method initializes the memory allocator. For example,
** it might allocate any require mutexes or initialize internal data
** structures. The xShutdown method is invoked (indirectly) by
** [sqlite3_shutdown()] and should deallocate any resources acquired
@@ -1317,31 +1588,33 @@ struct sqlite3_mem_methods {
** SQLITE_CONFIG_SERIALIZED configuration option.</dd>
**
** [[SQLITE_CONFIG_MALLOC]] <dt>SQLITE_CONFIG_MALLOC</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mem_methods] structure. The argument specifies
+** <dd> ^(The SQLITE_CONFIG_MALLOC option takes a single argument which is
+** a pointer to an instance of the [sqlite3_mem_methods] structure.
+** The argument specifies
** alternative low-level memory allocation routines to be used in place of
** the memory allocation routines built into SQLite.)^ ^SQLite makes
** its own private copy of the content of the [sqlite3_mem_methods] structure
** before the [sqlite3_config()] call returns.</dd>
**
** [[SQLITE_CONFIG_GETMALLOC]] <dt>SQLITE_CONFIG_GETMALLOC</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods]
+** <dd> ^(The SQLITE_CONFIG_GETMALLOC option takes a single argument which
+** is a pointer to an instance of the [sqlite3_mem_methods] structure.
+** The [sqlite3_mem_methods]
** structure is filled with the currently defined memory allocation routines.)^
** This option can be used to overload the default memory allocation
** routines with a wrapper that simulations memory allocation failure or
** tracks memory usage, for example. </dd>
**
** [[SQLITE_CONFIG_MEMSTATUS]] <dt>SQLITE_CONFIG_MEMSTATUS</dt>
-** <dd> ^This option takes single argument of type int, interpreted as a
-** boolean, which enables or disables the collection of memory allocation
-** statistics. ^(When memory allocation statistics are disabled, the
-** following SQLite interfaces become non-operational:
+** <dd> ^The SQLITE_CONFIG_MEMSTATUS option takes single argument of type int,
+** interpreted as a boolean, which enables or disables the collection of
+** memory allocation statistics. ^(When memory allocation statistics are
+** disabled, the following SQLite interfaces become non-operational:
** <ul>
** <li> [sqlite3_memory_used()]
** <li> [sqlite3_memory_highwater()]
** <li> [sqlite3_soft_heap_limit64()]
-** <li> [sqlite3_status()]
+** <li> [sqlite3_status64()]
** </ul>)^
** ^Memory allocation statistics are enabled by default unless SQLite is
** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory
@@ -1349,65 +1622,84 @@ struct sqlite3_mem_methods {
** </dd>
**
** [[SQLITE_CONFIG_SCRATCH]] <dt>SQLITE_CONFIG_SCRATCH</dt>
-** <dd> ^This option specifies a static memory buffer that SQLite can use for
-** scratch memory. There are three arguments: A pointer an 8-byte
+** <dd> ^The SQLITE_CONFIG_SCRATCH option specifies a static memory buffer
+** that SQLite can use for scratch memory. ^(There are three arguments
+** to SQLITE_CONFIG_SCRATCH: A pointer an 8-byte
** aligned memory buffer from which the scratch allocations will be
** drawn, the size of each scratch allocation (sz),
-** and the maximum number of scratch allocations (N). The sz
-** argument must be a multiple of 16.
+** and the maximum number of scratch allocations (N).)^
** The first argument must be a pointer to an 8-byte aligned buffer
** of at least sz*N bytes of memory.
-** ^SQLite will use no more than two scratch buffers per thread. So
-** N should be set to twice the expected maximum number of threads.
-** ^SQLite will never require a scratch buffer that is more than 6
-** times the database page size. ^If SQLite needs needs additional
+** ^SQLite will not use more than one scratch buffers per thread.
+** ^SQLite will never request a scratch buffer that is more than 6
+** times the database page size.
+** ^If SQLite needs needs additional
** scratch memory beyond what is provided by this configuration option, then
-** [sqlite3_malloc()] will be used to obtain the memory needed.</dd>
+** [sqlite3_malloc()] will be used to obtain the memory needed.<p>
+** ^When the application provides any amount of scratch memory using
+** SQLITE_CONFIG_SCRATCH, SQLite avoids unnecessary large
+** [sqlite3_malloc|heap allocations].
+** This can help [Robson proof|prevent memory allocation failures] due to heap
+** fragmentation in low-memory embedded systems.
+** </dd>
**
** [[SQLITE_CONFIG_PAGECACHE]] <dt>SQLITE_CONFIG_PAGECACHE</dt>
-** <dd> ^This option specifies a static memory buffer that SQLite can use for
-** the database page cache with the default page cache implementation.
-** This configuration should not be used if an application-define page
-** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option.
-** There are three arguments to this option: A pointer to 8-byte aligned
-** memory, the size of each page buffer (sz), and the number of pages (N).
+** <dd> ^The SQLITE_CONFIG_PAGECACHE option specifies a memory pool
+** that SQLite can use for the database page cache with the default page
+** cache implementation.
+** This configuration option is a no-op if an application-define page
+** cache implementation is loaded using the [SQLITE_CONFIG_PCACHE2].
+** ^There are three arguments to SQLITE_CONFIG_PAGECACHE: A pointer to
+** 8-byte aligned memory (pMem), the size of each page cache line (sz),
+** and the number of cache lines (N).
** The sz argument should be the size of the largest database page
-** (a power of two between 512 and 32768) plus a little extra for each
-** page header. ^The page header size is 20 to 40 bytes depending on
-** the host architecture. ^It is harmless, apart from the wasted memory,
-** to make sz a little too large. The first
-** argument should point to an allocation of at least sz*N bytes of memory.
-** ^SQLite will use the memory provided by the first argument to satisfy its
-** memory needs for the first N pages that it adds to cache. ^If additional
-** page cache memory is needed beyond what is provided by this option, then
-** SQLite goes to [sqlite3_malloc()] for the additional storage space.
-** The pointer in the first argument must
-** be aligned to an 8-byte boundary or subsequent behavior of SQLite
-** will be undefined.</dd>
+** (a power of two between 512 and 65536) plus some extra bytes for each
+** page header. ^The number of extra bytes needed by the page header
+** can be determined using [SQLITE_CONFIG_PCACHE_HDRSZ].
+** ^It is harmless, apart from the wasted memory,
+** for the sz parameter to be larger than necessary. The pMem
+** argument must be either a NULL pointer or a pointer to an 8-byte
+** aligned block of memory of at least sz*N bytes, otherwise
+** subsequent behavior is undefined.
+** ^When pMem is not NULL, SQLite will strive to use the memory provided
+** to satisfy page cache needs, falling back to [sqlite3_malloc()] if
+** a page cache line is larger than sz bytes or if all of the pMem buffer
+** is exhausted.
+** ^If pMem is NULL and N is non-zero, then each database connection
+** does an initial bulk allocation for page cache memory
+** from [sqlite3_malloc()] sufficient for N cache lines if N is positive or
+** of -1024*N bytes if N is negative, . ^If additional
+** page cache memory is needed beyond what is provided by the initial
+** allocation, then SQLite goes to [sqlite3_malloc()] separately for each
+** additional cache line. </dd>
**
** [[SQLITE_CONFIG_HEAP]] <dt>SQLITE_CONFIG_HEAP</dt>
-** <dd> ^This option specifies a static memory buffer that SQLite will use
-** for all of its dynamic memory allocation needs beyond those provided
-** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE].
-** There are three arguments: An 8-byte aligned pointer to the memory,
+** <dd> ^The SQLITE_CONFIG_HEAP option specifies a static memory buffer
+** that SQLite will use for all of its dynamic memory allocation needs
+** beyond those provided for by [SQLITE_CONFIG_SCRATCH] and
+** [SQLITE_CONFIG_PAGECACHE].
+** ^The SQLITE_CONFIG_HEAP option is only available if SQLite is compiled
+** with either [SQLITE_ENABLE_MEMSYS3] or [SQLITE_ENABLE_MEMSYS5] and returns
+** [SQLITE_ERROR] if invoked otherwise.
+** ^There are three arguments to SQLITE_CONFIG_HEAP:
+** An 8-byte aligned pointer to the memory,
** the number of bytes in the memory buffer, and the minimum allocation size.
** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts
** to using its default memory allocator (the system malloc() implementation),
** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the
-** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or
-** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory
+** memory pointer is not NULL then the alternative memory
** allocator is engaged to handle all of SQLites memory allocation needs.
** The first pointer (the memory pointer) must be aligned to an 8-byte
** boundary or subsequent behavior of SQLite will be undefined.
-** The minimum allocation size is capped at 2^12. Reasonable values
-** for the minimum allocation size are 2^5 through 2^8.</dd>
+** The minimum allocation size is capped at 2**12. Reasonable values
+** for the minimum allocation size are 2**5 through 2**8.</dd>
**
** [[SQLITE_CONFIG_MUTEX]] <dt>SQLITE_CONFIG_MUTEX</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mutex_methods] structure. The argument specifies
-** alternative low-level mutex routines to be used in place
-** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the
-** content of the [sqlite3_mutex_methods] structure before the call to
+** <dd> ^(The SQLITE_CONFIG_MUTEX option takes a single argument which is a
+** pointer to an instance of the [sqlite3_mutex_methods] structure.
+** The argument specifies alternative low-level mutex routines to be used
+** in place the mutex routines built into SQLite.)^ ^SQLite makes a copy of
+** the content of the [sqlite3_mutex_methods] structure before the call to
** [sqlite3_config()] returns. ^If SQLite is compiled with
** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then
** the entire mutexing subsystem is omitted from the build and hence calls to
@@ -1415,8 +1707,8 @@ struct sqlite3_mem_methods {
** return [SQLITE_ERROR].</dd>
**
** [[SQLITE_CONFIG_GETMUTEX]] <dt>SQLITE_CONFIG_GETMUTEX</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** instance of the [sqlite3_mutex_methods] structure. The
+** <dd> ^(The SQLITE_CONFIG_GETMUTEX option takes a single argument which
+** is a pointer to an instance of the [sqlite3_mutex_methods] structure. The
** [sqlite3_mutex_methods]
** structure is filled with the currently defined mutex routines.)^
** This option can be used to overload the default mutex allocation
@@ -1428,28 +1720,30 @@ struct sqlite3_mem_methods {
** return [SQLITE_ERROR].</dd>
**
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
-** <dd> ^(This option takes two arguments that determine the default
-** memory allocation for the lookaside memory allocator on each
-** [database connection]. The first argument is the
+** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
+** the default size of lookaside memory on each [database connection].
+** The first argument is the
** size of each lookaside buffer slot and the second is the number of
-** slots allocated to each database connection.)^ ^(This option sets the
-** <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
-** verb to [sqlite3_db_config()] can be used to change the lookaside
+** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
+** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
+** option to [sqlite3_db_config()] can be used to change the lookaside
** configuration on individual connections.)^ </dd>
**
-** [[SQLITE_CONFIG_PCACHE]] <dt>SQLITE_CONFIG_PCACHE</dt>
-** <dd> ^(This option takes a single argument which is a pointer to
-** an [sqlite3_pcache_methods] object. This object specifies the interface
-** to a custom page cache implementation.)^ ^SQLite makes a copy of the
-** object and uses it for page cache memory allocations.</dd>
+** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
+** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
+** a pointer to an [sqlite3_pcache_methods2] object. This object specifies
+** the interface to a custom page cache implementation.)^
+** ^SQLite makes a copy of the [sqlite3_pcache_methods2] object.</dd>
**
-** [[SQLITE_CONFIG_GETPCACHE]] <dt>SQLITE_CONFIG_GETPCACHE</dt>
-** <dd> ^(This option takes a single argument which is a pointer to an
-** [sqlite3_pcache_methods] object. SQLite copies of the current
-** page cache implementation into that object.)^ </dd>
+** [[SQLITE_CONFIG_GETPCACHE2]] <dt>SQLITE_CONFIG_GETPCACHE2</dt>
+** <dd> ^(The SQLITE_CONFIG_GETPCACHE2 option takes a single argument which
+** is a pointer to an [sqlite3_pcache_methods2] object. SQLite copies of
+** the current page cache implementation into that object.)^ </dd>
**
** [[SQLITE_CONFIG_LOG]] <dt>SQLITE_CONFIG_LOG</dt>
-** <dd> ^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
+** <dd> The SQLITE_CONFIG_LOG option is used to configure the SQLite
+** global [error log].
+** (^The SQLITE_CONFIG_LOG option takes two arguments: a pointer to a
** function with a call signature of void(*)(void*,int,const char*),
** and a pointer to void. ^If the function pointer is not NULL, it is
** invoked by [sqlite3_log()] to process each logging event. ^If the
@@ -1467,17 +1761,108 @@ struct sqlite3_mem_methods {
** function must be threadsafe. </dd>
**
** [[SQLITE_CONFIG_URI]] <dt>SQLITE_CONFIG_URI
-** <dd> This option takes a single argument of type int. If non-zero, then
-** URI handling is globally enabled. If the parameter is zero, then URI handling
-** is globally disabled. If URI handling is globally enabled, all filenames
-** passed to [sqlite3_open()], [sqlite3_open_v2()], [sqlite3_open16()] or
+** <dd>^(The SQLITE_CONFIG_URI option takes a single argument of type int.
+** If non-zero, then URI handling is globally enabled. If the parameter is zero,
+** then URI handling is globally disabled.)^ ^If URI handling is globally
+** enabled, all filenames passed to [sqlite3_open()], [sqlite3_open_v2()],
+** [sqlite3_open16()] or
** specified as part of [ATTACH] commands are interpreted as URIs, regardless
** of whether or not the [SQLITE_OPEN_URI] flag is set when the database
-** connection is opened. If it is globally disabled, filenames are
+** connection is opened. ^If it is globally disabled, filenames are
** only interpreted as URIs if the SQLITE_OPEN_URI flag is set when the
-** database connection is opened. By default, URI handling is globally
+** database connection is opened. ^(By default, URI handling is globally
** disabled. The default value may be changed by compiling with the
-** [SQLITE_USE_URI] symbol defined.
+** [SQLITE_USE_URI] symbol defined.)^
+**
+** [[SQLITE_CONFIG_COVERING_INDEX_SCAN]] <dt>SQLITE_CONFIG_COVERING_INDEX_SCAN
+** <dd>^The SQLITE_CONFIG_COVERING_INDEX_SCAN option takes a single integer
+** argument which is interpreted as a boolean in order to enable or disable
+** the use of covering indices for full table scans in the query optimizer.
+** ^The default setting is determined
+** by the [SQLITE_ALLOW_COVERING_INDEX_SCAN] compile-time option, or is "on"
+** if that compile-time option is omitted.
+** The ability to disable the use of covering indices for full table scans
+** is because some incorrectly coded legacy applications might malfunction
+** when the optimization is enabled. Providing the ability to
+** disable the optimization allows the older, buggy application code to work
+** without change even with newer versions of SQLite.
+**
+** [[SQLITE_CONFIG_PCACHE]] [[SQLITE_CONFIG_GETPCACHE]]
+** <dt>SQLITE_CONFIG_PCACHE and SQLITE_CONFIG_GETPCACHE
+** <dd> These options are obsolete and should not be used by new code.
+** They are retained for backwards compatibility but are now no-ops.
+** </dd>
+**
+** [[SQLITE_CONFIG_SQLLOG]]
+** <dt>SQLITE_CONFIG_SQLLOG
+** <dd>This option is only available if sqlite is compiled with the
+** [SQLITE_ENABLE_SQLLOG] pre-processor macro defined. The first argument should
+** be a pointer to a function of type void(*)(void*,sqlite3*,const char*, int).
+** The second should be of type (void*). The callback is invoked by the library
+** in three separate circumstances, identified by the value passed as the
+** fourth parameter. If the fourth parameter is 0, then the database connection
+** passed as the second argument has just been opened. The third argument
+** points to a buffer containing the name of the main database file. If the
+** fourth parameter is 1, then the SQL statement that the third parameter
+** points to has just been executed. Or, if the fourth parameter is 2, then
+** the connection being passed as the second parameter is being closed. The
+** third parameter is passed NULL In this case. An example of using this
+** configuration option can be seen in the "test_sqllog.c" source file in
+** the canonical SQLite source tree.</dd>
+**
+** [[SQLITE_CONFIG_MMAP_SIZE]]
+** <dt>SQLITE_CONFIG_MMAP_SIZE
+** <dd>^SQLITE_CONFIG_MMAP_SIZE takes two 64-bit integer (sqlite3_int64) values
+** that are the default mmap size limit (the default setting for
+** [PRAGMA mmap_size]) and the maximum allowed mmap size limit.
+** ^The default setting can be overridden by each database connection using
+** either the [PRAGMA mmap_size] command, or by using the
+** [SQLITE_FCNTL_MMAP_SIZE] file control. ^(The maximum allowed mmap size
+** will be silently truncated if necessary so that it does not exceed the
+** compile-time maximum mmap size set by the
+** [SQLITE_MAX_MMAP_SIZE] compile-time option.)^
+** ^If either argument to this option is negative, then that argument is
+** changed to its compile-time default.
+**
+** [[SQLITE_CONFIG_WIN32_HEAPSIZE]]
+** <dt>SQLITE_CONFIG_WIN32_HEAPSIZE
+** <dd>^The SQLITE_CONFIG_WIN32_HEAPSIZE option is only available if SQLite is
+** compiled for Windows with the [SQLITE_WIN32_MALLOC] pre-processor macro
+** defined. ^SQLITE_CONFIG_WIN32_HEAPSIZE takes a 32-bit unsigned integer value
+** that specifies the maximum size of the created heap.
+**
+** [[SQLITE_CONFIG_PCACHE_HDRSZ]]
+** <dt>SQLITE_CONFIG_PCACHE_HDRSZ
+** <dd>^The SQLITE_CONFIG_PCACHE_HDRSZ option takes a single parameter which
+** is a pointer to an integer and writes into that integer the number of extra
+** bytes per page required for each page in [SQLITE_CONFIG_PAGECACHE].
+** The amount of extra space required can change depending on the compiler,
+** target platform, and SQLite version.
+**
+** [[SQLITE_CONFIG_PMASZ]]
+** <dt>SQLITE_CONFIG_PMASZ
+** <dd>^The SQLITE_CONFIG_PMASZ option takes a single parameter which
+** is an unsigned integer and sets the "Minimum PMA Size" for the multithreaded
+** sorter to that integer. The default minimum PMA Size is set by the
+** [SQLITE_SORTER_PMASZ] compile-time option. New threads are launched
+** to help with sort operations when multithreaded sorting
+** is enabled (using the [PRAGMA threads] command) and the amount of content
+** to be sorted exceeds the page size times the minimum of the
+** [PRAGMA cache_size] setting and this value.
+**
+** [[SQLITE_CONFIG_STMTJRNL_SPILL]]
+** <dt>SQLITE_CONFIG_STMTJRNL_SPILL
+** <dd>^The SQLITE_CONFIG_STMTJRNL_SPILL option takes a single parameter which
+** becomes the [statement journal] spill-to-disk threshold.
+** [Statement journals] are held in memory until their size (in bytes)
+** exceeds this threshold, at which point they are written to disk.
+** Or if the threshold is -1, statement journals are always held
+** exclusively in memory.
+** Since many statement journals never become large, setting the spill
+** threshold to a value such as 64KiB can greatly reduce the amount of
+** I/O required to support statement rollback.
+** The default value for this setting is controlled by the
+** [SQLITE_STMTJRNL_SPILL] compile-time option.
** </dl>
*/
#define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */
@@ -1493,10 +1878,19 @@ struct sqlite3_mem_methods {
#define SQLITE_CONFIG_GETMUTEX 11 /* sqlite3_mutex_methods* */
/* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */
#define SQLITE_CONFIG_LOOKASIDE 13 /* int int */
-#define SQLITE_CONFIG_PCACHE 14 /* sqlite3_pcache_methods* */
-#define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */
+#define SQLITE_CONFIG_PCACHE 14 /* no-op */
+#define SQLITE_CONFIG_GETPCACHE 15 /* no-op */
#define SQLITE_CONFIG_LOG 16 /* xFunc, void* */
#define SQLITE_CONFIG_URI 17 /* int */
+#define SQLITE_CONFIG_PCACHE2 18 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_GETPCACHE2 19 /* sqlite3_pcache_methods2* */
+#define SQLITE_CONFIG_COVERING_INDEX_SCAN 20 /* int */
+#define SQLITE_CONFIG_SQLLOG 21 /* xSqllog, void* */
+#define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */
+#define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */
+#define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */
+#define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */
+#define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */
/*
** CAPI3REF: Database Connection Configuration Options
@@ -1554,15 +1948,58 @@ struct sqlite3_mem_methods {
** following this call. The second parameter may be a NULL pointer, in
** which case the trigger setting is not reported back. </dd>
**
+** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt>
+** <dd> ^This option is used to enable or disable the two-argument
+** version of the [fts3_tokenizer()] function which is part of the
+** [FTS3] full-text search engine extension.
+** There should be two additional arguments.
+** The first argument is an integer which is 0 to disable fts3_tokenizer() or
+** positive to enable fts3_tokenizer() or negative to leave the setting
+** unchanged.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled
+** following this call. The second parameter may be a NULL pointer, in
+** which case the new setting is not reported back. </dd>
+**
+** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt>
+** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()]
+** interface independently of the [load_extension()] SQL function.
+** The [sqlite3_enable_load_extension()] API enables or disables both the
+** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
+** There should be two additional arguments.
+** When the first argument to this interface is 1, then only the C-API is
+** enabled and the SQL function remains disabled. If the first argument to
+** this interface is 0, then both the C-API and the SQL function are disabled.
+** If the first argument is -1, then no changes are made to state of either the
+** C-API or the SQL function.
+** The second parameter is a pointer to an integer into which
+** is written 0 or 1 to indicate whether [sqlite3_load_extension()] interface
+** is disabled or enabled following this call. The second parameter may
+** be a NULL pointer, in which case the new setting is not reported back.
+** </dd>
+**
+** <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
+** <dd> ^This option is used to change the name of the "main" database
+** schema. ^The sole argument is a pointer to a constant UTF8 string
+** which will become the new schema name in place of "main". ^SQLite
+** does not make a copy of the new main schema name string, so the application
+** must ensure that the argument passed into this DBCONFIG option is unchanged
+** until after the database connection closes.
+** </dd>
+**
** </dl>
*/
-#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
-#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
-#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
+#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
+#define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
+** METHOD: sqlite3
**
** ^The sqlite3_extended_result_codes() routine enables or disables the
** [extended result codes] feature of SQLite. ^The extended result
@@ -1572,20 +2009,23 @@ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff);
/*
** CAPI3REF: Last Insert Rowid
+** METHOD: sqlite3
**
-** ^Each entry in an SQLite table has a unique 64-bit signed
+** ^Each entry in most SQLite tables (except for [WITHOUT ROWID] tables)
+** has a unique 64-bit signed
** integer key called the [ROWID | "rowid"]. ^The rowid is always available
** as an undeclared column named ROWID, OID, or _ROWID_ as long as those
** names are not also used by explicitly declared columns. ^If
** the table has a column of type [INTEGER PRIMARY KEY] then that column
** is another alias for the rowid.
**
-** ^This routine returns the [rowid] of the most recent
-** successful [INSERT] into the database from the [database connection]
-** in the first argument. ^As of SQLite version 3.7.7, this routines
-** records the last insert rowid of both ordinary tables and [virtual tables].
-** ^If no successful [INSERT]s
-** have ever occurred on that database connection, zero is returned.
+** ^The sqlite3_last_insert_rowid(D) interface returns the [rowid] of the
+** most recent successful [INSERT] into a rowid table or [virtual table]
+** on database connection D.
+** ^Inserts into [WITHOUT ROWID] tables are not recorded.
+** ^If no successful [INSERT]s into rowid tables
+** have ever occurred on the database connection D,
+** then sqlite3_last_insert_rowid(D) returns zero.
**
** ^(If an [INSERT] occurs within a trigger or within a [virtual table]
** method, then this routine will return the [rowid] of the inserted
@@ -1621,48 +2061,47 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*);
/*
** CAPI3REF: Count The Number Of Rows Modified
+** METHOD: sqlite3
**
-** ^This function returns the number of database rows that were changed
-** or inserted or deleted by the most recently completed SQL statement
-** on the [database connection] specified by the first parameter.
-** ^(Only changes that are directly specified by the [INSERT], [UPDATE],
-** or [DELETE] statement are counted. Auxiliary changes caused by
-** triggers or [foreign key actions] are not counted.)^ Use the
-** [sqlite3_total_changes()] function to find the total number of changes
-** including changes caused by triggers and foreign key actions.
-**
-** ^Changes to a view that are simulated by an [INSTEAD OF trigger]
-** are not counted. Only real table changes are counted.
-**
-** ^(A "row change" is a change to a single row of a single table
-** caused by an INSERT, DELETE, or UPDATE statement. Rows that
-** are changed as side effects of [REPLACE] constraint resolution,
-** rollback, ABORT processing, [DROP TABLE], or by any other
-** mechanisms do not count as direct row changes.)^
-**
-** A "trigger context" is a scope of execution that begins and
-** ends with the script of a [CREATE TRIGGER | trigger].
-** Most SQL statements are
-** evaluated outside of any trigger. This is the "top level"
-** trigger context. If a trigger fires from the top level, a
-** new trigger context is entered for the duration of that one
-** trigger. Subtriggers create subcontexts for their duration.
-**
-** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does
-** not create a new trigger context.
-**
-** ^This function returns the number of direct row changes in the
-** most recent INSERT, UPDATE, or DELETE statement within the same
-** trigger context.
-**
-** ^Thus, when called from the top level, this function returns the
-** number of changes in the most recent INSERT, UPDATE, or DELETE
-** that also occurred at the top level. ^(Within the body of a trigger,
-** the sqlite3_changes() interface can be called to find the number of
-** changes in the most recently completed INSERT, UPDATE, or DELETE
-** statement within the body of the same trigger.
-** However, the number returned does not include changes
-** caused by subtriggers since those have their own context.)^
+** ^This function returns the number of rows modified, inserted or
+** deleted by the most recently completed INSERT, UPDATE or DELETE
+** statement on the database connection specified by the only parameter.
+** ^Executing any other type of SQL statement does not modify the value
+** returned by this function.
+**
+** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
+** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
+** [foreign key actions] or [REPLACE] constraint resolution are not counted.
+**
+** Changes to a view that are intercepted by
+** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value
+** returned by sqlite3_changes() immediately after an INSERT, UPDATE or
+** DELETE statement run on a view is always zero. Only changes made to real
+** tables are counted.
+**
+** Things are more complicated if the sqlite3_changes() function is
+** executed while a trigger program is running. This may happen if the
+** program uses the [changes() SQL function], or if some other callback
+** function invokes sqlite3_changes() directly. Essentially:
+**
+** <ul>
+** <li> ^(Before entering a trigger program the value returned by
+** sqlite3_changes() function is saved. After the trigger program
+** has finished, the original value is restored.)^
+**
+** <li> ^(Within a trigger program each INSERT, UPDATE and DELETE
+** statement sets the value returned by sqlite3_changes()
+** upon completion as normal. Of course, this value will not include
+** any changes performed by sub-triggers, as the sqlite3_changes()
+** value will be saved and restored after each sub-trigger has run.)^
+** </ul>
+**
+** ^This means that if the changes() SQL function (or similar) is used
+** by the first INSERT, UPDATE or DELETE statement within a trigger, it
+** returns the value as set when the calling statement began executing.
+** ^If it is used by the second or subsequent such statement within a trigger
+** program, the value returned reflects the number of rows modified by the
+** previous INSERT, UPDATE or DELETE statement within the same trigger.
**
** See also the [sqlite3_total_changes()] interface, the
** [count_changes pragma], and the [changes() SQL function].
@@ -1675,21 +2114,19 @@ SQLITE_API int sqlite3_changes(sqlite3*);
/*
** CAPI3REF: Total Number Of Rows Modified
+** METHOD: sqlite3
**
-** ^This function returns the number of row changes caused by [INSERT],
-** [UPDATE] or [DELETE] statements since the [database connection] was opened.
-** ^(The count returned by sqlite3_total_changes() includes all changes
-** from all [CREATE TRIGGER | trigger] contexts and changes made by
-** [foreign key actions]. However,
-** the count does not include changes used to implement [REPLACE] constraints,
-** do rollbacks or ABORT processing, or [DROP TABLE] processing. The
-** count does not include rows of views that fire an [INSTEAD OF trigger],
-** though if the INSTEAD OF trigger makes changes of its own, those changes
-** are counted.)^
-** ^The sqlite3_total_changes() function counts the changes as soon as
-** the statement that makes them is completed (when the statement handle
-** is passed to [sqlite3_reset()] or [sqlite3_finalize()]).
-**
+** ^This function returns the total number of rows inserted, modified or
+** deleted by all [INSERT], [UPDATE] or [DELETE] statements completed
+** since the database connection was opened, including those executed as
+** part of trigger programs. ^Executing any other type of SQL statement
+** does not affect the value returned by sqlite3_total_changes().
+**
+** ^Changes made as part of [foreign key actions] are included in the
+** count, but those made as part of REPLACE constraint resolution are
+** not. ^Changes to a view that are intercepted by INSTEAD OF triggers
+** are not counted.
+**
** See also the [sqlite3_changes()] interface, the
** [count_changes pragma], and the [total_changes() SQL function].
**
@@ -1701,6 +2138,7 @@ SQLITE_API int sqlite3_total_changes(sqlite3*);
/*
** CAPI3REF: Interrupt A Long-Running Query
+** METHOD: sqlite3
**
** ^This function causes any pending database operation to abort and
** return at its earliest opportunity. This routine is typically
@@ -1776,28 +2214,36 @@ SQLITE_API int sqlite3_complete16(const void *sql);
/*
** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors
-**
-** ^This routine sets a callback function that might be invoked whenever
-** an attempt is made to open a database table that another thread
-** or process has locked.
-**
-** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]
+** KEYWORDS: {busy-handler callback} {busy handler}
+** METHOD: sqlite3
+**
+** ^The sqlite3_busy_handler(D,X,P) routine sets a callback function X
+** that might be invoked with argument P whenever
+** an attempt is made to access a database table associated with
+** [database connection] D when another thread
+** or process has the table locked.
+** The sqlite3_busy_handler() interface is used to implement
+** [sqlite3_busy_timeout()] and [PRAGMA busy_timeout].
+**
+** ^If the busy callback is NULL, then [SQLITE_BUSY]
** is returned immediately upon encountering the lock. ^If the busy callback
** is not NULL, then the callback might be invoked with two arguments.
**
** ^The first argument to the busy handler is a copy of the void* pointer which
** is the third argument to sqlite3_busy_handler(). ^The second argument to
** the busy handler callback is the number of times that the busy handler has
-** been invoked for this locking event. ^If the
+** been invoked previously for the same locking event. ^If the
** busy callback returns 0, then no additional attempts are made to
-** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned.
+** access the database and [SQLITE_BUSY] is returned
+** to the application.
** ^If the callback returns non-zero, then another attempt
-** is made to open the database for reading and the cycle repeats.
+** is made to access the database and the cycle repeats.
**
** The presence of a busy handler does not guarantee that it will be invoked
** when there is lock contention. ^If SQLite determines that invoking the busy
** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY]
-** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler.
+** to the application instead of invoking the
+** busy handler.
** Consider a scenario where one process is holding a read lock that
** it is trying to promote to a reserved lock and
** a second process is holding a reserved lock that it is trying
@@ -1811,57 +2257,48 @@ SQLITE_API int sqlite3_complete16(const void *sql);
**
** ^The default busy callback is NULL.
**
-** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED]
-** when SQLite is in the middle of a large transaction where all the
-** changes will not fit into the in-memory cache. SQLite will
-** already hold a RESERVED lock on the database file, but it needs
-** to promote this lock to EXCLUSIVE so that it can spill cache
-** pages into the database file without harm to concurrent
-** readers. ^If it is unable to promote the lock, then the in-memory
-** cache will be left in an inconsistent state and so the error
-** code is promoted from the relatively benign [SQLITE_BUSY] to
-** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion
-** forces an automatic rollback of the changes. See the
-** <a href="/cvstrac/wiki?p=CorruptionFollowingBusyError">
-** CorruptionFollowingBusyError</a> wiki page for a discussion of why
-** this is important.
-**
** ^(There can only be a single busy handler defined for each
** [database connection]. Setting a new busy handler clears any
** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()]
-** will also set or clear the busy handler.
+** or evaluating [PRAGMA busy_timeout=N] will change the
+** busy handler and thus clear any previously set busy handler.
**
** The busy callback should not take any actions which modify the
-** database connection that invoked the busy handler. Any such actions
+** database connection that invoked the busy handler. In other words,
+** the busy handler is not reentrant. Any such actions
** result in undefined behavior.
**
** A busy handler must not close the database connection
** or [prepared statement] that invoked the busy handler.
*/
-SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
+SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
/*
** CAPI3REF: Set A Busy Timeout
+** METHOD: sqlite3
**
** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps
** for a specified amount of time when a table is locked. ^The handler
** will sleep multiple times until at least "ms" milliseconds of sleeping
** have accumulated. ^After at least "ms" milliseconds of sleeping,
** the handler returns 0 which causes [sqlite3_step()] to return
-** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED].
+** [SQLITE_BUSY].
**
** ^Calling this routine with an argument less than or equal to zero
** turns off all busy handlers.
**
** ^(There can only be a single busy handler for a particular
-** [database connection] any any given moment. If another busy handler
+** [database connection] at any given moment. If another busy handler
** was defined (using [sqlite3_busy_handler()]) prior to calling
** this routine, that other busy handler is cleared.)^
+**
+** See also: [PRAGMA busy_timeout]
*/
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
/*
** CAPI3REF: Convenience Routines For Running Queries
+** METHOD: sqlite3
**
** This is a legacy interface that is preserved for backwards compatibility.
** Use of this interface is not recommended.
@@ -1947,6 +2384,10 @@ SQLITE_API void sqlite3_free_table(char **result);
**
** These routines are work-alikes of the "printf()" family of functions
** from the standard C library.
+** These routines understand most of the common K&R formatting options,
+** plus some additional non-standard formats, detailed below.
+** Note that some of the more obscure formatting options from recent
+** C-library standards are omitted from this implementation.
**
** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their
** results into memory obtained from [sqlite3_malloc()].
@@ -1979,9 +2420,9 @@ SQLITE_API void sqlite3_free_table(char **result);
** These routines all implement some additional formatting
** options that are useful for constructing SQL statements.
** All of the usual printf() formatting options apply. In addition, there
-** is are "%q", "%Q", and "%z" options.
+** is are "%q", "%Q", "%w" and "%z" options.
**
-** ^(The %q option works like %s in that it substitutes a null-terminated
+** ^(The %q option works like %s in that it substitutes a nul-terminated
** string from the argument list. But %q also doubles every '\'' character.
** %q is designed for use inside a string literal.)^ By doubling each '\''
** character it escapes that character and allows it to be inserted into
@@ -2032,6 +2473,12 @@ SQLITE_API void sqlite3_free_table(char **result);
** The code above will render a correct SQL statement in the zSQL
** variable even if the zText variable is a NULL pointer.
**
+** ^(The "%w" formatting option is like "%q" except that it expects to
+** be contained within double-quotes instead of single quotes, and it
+** escapes the double-quote character instead of the single-quote
+** character.)^ The "%w" formatting option is intended for safely inserting
+** table and column names into a constructed SQL statement.
+**
** ^(The "%z" formatting option works like "%s" but with the
** addition that after the string has been read and copied into
** the result, [sqlite3_free()] is called on the input string.)^
@@ -2056,6 +2503,10 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns
** a NULL pointer.
**
+** ^The sqlite3_malloc64(N) routine works just like
+** sqlite3_malloc(N) except that N is an unsigned 64-bit integer instead
+** of a signed 32-bit integer.
+**
** ^Calling sqlite3_free() with a pointer previously returned
** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
** that it might be reused. ^The sqlite3_free() routine is
@@ -2067,24 +2518,38 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** might result if sqlite3_free() is called with a non-NULL pointer that
** was not obtained from sqlite3_malloc() or sqlite3_realloc().
**
-** ^(The sqlite3_realloc() interface attempts to resize a
-** prior memory allocation to be at least N bytes, where N is the
-** second parameter. The memory allocation to be resized is the first
-** parameter.)^ ^ If the first parameter to sqlite3_realloc()
+** ^The sqlite3_realloc(X,N) interface attempts to resize a
+** prior memory allocation X to be at least N bytes.
+** ^If the X parameter to sqlite3_realloc(X,N)
** is a NULL pointer then its behavior is identical to calling
-** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().
-** ^If the second parameter to sqlite3_realloc() is zero or
+** sqlite3_malloc(N).
+** ^If the N parameter to sqlite3_realloc(X,N) is zero or
** negative then the behavior is exactly the same as calling
-** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().
-** ^sqlite3_realloc() returns a pointer to a memory allocation
-** of at least N bytes in size or NULL if sufficient memory is unavailable.
+** sqlite3_free(X).
+** ^sqlite3_realloc(X,N) returns a pointer to a memory allocation
+** of at least N bytes in size or NULL if insufficient memory is available.
** ^If M is the size of the prior allocation, then min(N,M) bytes
** of the prior allocation are copied into the beginning of buffer returned
-** by sqlite3_realloc() and the prior allocation is freed.
-** ^If sqlite3_realloc() returns NULL, then the prior allocation
-** is not freed.
-**
-** ^The memory returned by sqlite3_malloc() and sqlite3_realloc()
+** by sqlite3_realloc(X,N) and the prior allocation is freed.
+** ^If sqlite3_realloc(X,N) returns NULL and N is positive, then the
+** prior allocation is not freed.
+**
+** ^The sqlite3_realloc64(X,N) interfaces works the same as
+** sqlite3_realloc(X,N) except that N is a 64-bit unsigned integer instead
+** of a 32-bit signed integer.
+**
+** ^If X is a memory allocation previously obtained from sqlite3_malloc(),
+** sqlite3_malloc64(), sqlite3_realloc(), or sqlite3_realloc64(), then
+** sqlite3_msize(X) returns the size of that memory allocation in bytes.
+** ^The value returned by sqlite3_msize(X) might be larger than the number
+** of bytes requested when X was allocated. ^If X is a NULL pointer then
+** sqlite3_msize(X) returns zero. If X points to something that is not
+** the beginning of memory allocation, or if it points to a formerly
+** valid memory allocation that has now been freed, then the behavior
+** of sqlite3_msize(X) is undefined and possibly harmful.
+**
+** ^The memory returned by sqlite3_malloc(), sqlite3_realloc(),
+** sqlite3_malloc64(), and sqlite3_realloc64()
** is always aligned to at least an 8 byte boundary, or to a
** 4 byte boundary if the [SQLITE_4_BYTE_ALIGNED_MALLOC] compile-time
** option is used.
@@ -2094,12 +2559,12 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** implementation of these routines to be omitted. That capability
** is no longer provided. Only built-in memory allocators can be used.
**
-** The Windows OS interface layer calls
+** Prior to SQLite version 3.7.10, the Windows OS interface layer called
** the system malloc() and free() directly when converting
** filenames between the UTF-8 encoding used by SQLite
** and whatever filename encoding is used by the particular Windows
-** installation. Memory allocation errors are detected, but
-** they are reported back as [SQLITE_CANTOPEN] or
+** installation. Memory allocation errors were detected, but
+** they were reported back as [SQLITE_CANTOPEN] or
** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
**
** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
@@ -2112,8 +2577,11 @@ SQLITE_API char *sqlite3_vsnprintf(int,char*,const char*, va_list);
** [sqlite3_free()] or [sqlite3_realloc()].
*/
SQLITE_API void *sqlite3_malloc(int);
+SQLITE_API void *sqlite3_malloc64(sqlite3_uint64);
SQLITE_API void *sqlite3_realloc(void*, int);
+SQLITE_API void *sqlite3_realloc64(void*, sqlite3_uint64);
SQLITE_API void sqlite3_free(void*);
+SQLITE_API sqlite3_uint64 sqlite3_msize(void*);
/*
** CAPI3REF: Memory Allocator Statistics
@@ -2151,11 +2619,14 @@ SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
** applications to access the same PRNG for other purposes.
**
** ^A call to this routine stores N bytes of randomness into buffer P.
-**
-** ^The first time this routine is invoked (either internally or by
-** the application) the PRNG is seeded using randomness obtained
-** from the xRandomness method of the default [sqlite3_vfs] object.
-** ^On all subsequent invocations, the pseudo-randomness is generated
+** ^The P parameter can be a NULL pointer.
+**
+** ^If this routine has not been previously called or if the previous
+** call had N less than one or a NULL pointer for P, then the PRNG is
+** seeded using randomness obtained from the xRandomness method of
+** the default [sqlite3_vfs] object.
+** ^If the previous call to this routine had an N of 1 or more and a
+** non-NULL P then the pseudo-randomness is generated
** internally and without recourse to the [sqlite3_vfs] xRandomness
** method.
*/
@@ -2163,6 +2634,7 @@ SQLITE_API void sqlite3_randomness(int N, void *P);
/*
** CAPI3REF: Compile-Time Authorization Callbacks
+** METHOD: sqlite3
**
** ^This routine registers an authorizer callback with a particular
** [database connection], supplied in the first argument.
@@ -2256,8 +2728,8 @@ SQLITE_API int sqlite3_set_authorizer(
** [sqlite3_set_authorizer | authorizer documentation] for additional
** information.
**
-** Note that SQLITE_IGNORE is also used as a [SQLITE_ROLLBACK | return code]
-** from the [sqlite3_vtab_on_conflict()] interface.
+** Note that SQLITE_IGNORE is also used as a [conflict resolution mode]
+** returned from the [sqlite3_vtab_on_conflict()] interface.
*/
#define SQLITE_DENY 1 /* Abort the SQL statement with an error */
#define SQLITE_IGNORE 2 /* Don't allow access, but don't generate an error */
@@ -2315,9 +2787,14 @@ SQLITE_API int sqlite3_set_authorizer(
#define SQLITE_FUNCTION 31 /* NULL Function Name */
#define SQLITE_SAVEPOINT 32 /* Operation Savepoint Name */
#define SQLITE_COPY 0 /* No longer used */
+#define SQLITE_RECURSIVE 33 /* NULL NULL */
/*
** CAPI3REF: Tracing And Profiling Functions
+** METHOD: sqlite3
+**
+** These routines are deprecated. Use the [sqlite3_trace_v2()] interface
+** instead of the routines described here.
**
** These routines register callback functions that can be used for
** tracing and profiling the execution of SQL statements.
@@ -2330,6 +2807,9 @@ SQLITE_API int sqlite3_set_authorizer(
** as each triggered subprogram is entered. The callbacks for triggers
** contain a UTF-8 SQL comment that identifies the trigger.)^
**
+** The [SQLITE_TRACE_SIZE_LIMIT] compile-time option can be used to limit
+** the length of [bound parameter] expansion in the output of sqlite3_trace().
+**
** ^The callback function registered by sqlite3_profile() is invoked
** as each SQL statement finishes. ^The profile callback contains
** the original statement text and an estimate of wall-clock time
@@ -2341,12 +2821,107 @@ SQLITE_API int sqlite3_set_authorizer(
** sqlite3_profile() function is considered experimental and is
** subject to change in future versions of SQLite.
*/
-SQLITE_API void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
-SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
+SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*,
+ void(*xTrace)(void*,const char*), void*);
+SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*,
void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
/*
+** CAPI3REF: SQL Trace Event Codes
+** KEYWORDS: SQLITE_TRACE
+**
+** These constants identify classes of events that can be monitored
+** using the [sqlite3_trace_v2()] tracing logic. The third argument
+** to [sqlite3_trace_v2()] is an OR-ed combination of one or more of
+** the following constants. ^The first argument to the trace callback
+** is one of the following constants.
+**
+** New tracing constants may be added in future releases.
+**
+** ^A trace callback has four arguments: xCallback(T,C,P,X).
+** ^The T argument is one of the integer type codes above.
+** ^The C argument is a copy of the context pointer passed in as the
+** fourth argument to [sqlite3_trace_v2()].
+** The P and X arguments are pointers whose meanings depend on T.
+**
+** <dl>
+** [[SQLITE_TRACE_STMT]] <dt>SQLITE_TRACE_STMT</dt>
+** <dd>^An SQLITE_TRACE_STMT callback is invoked when a prepared statement
+** first begins running and possibly at other times during the
+** execution of the prepared statement, such as at the start of each
+** trigger subprogram. ^The P argument is a pointer to the
+** [prepared statement]. ^The X argument is a pointer to a string which
+** is the unexpanded SQL text of the prepared statement or an SQL comment
+** that indicates the invocation of a trigger. ^The callback can compute
+** the same text that would have been returned by the legacy [sqlite3_trace()]
+** interface by using the X argument when X begins with "--" and invoking
+** [sqlite3_expanded_sql(P)] otherwise.
+**
+** [[SQLITE_TRACE_PROFILE]] <dt>SQLITE_TRACE_PROFILE</dt>
+** <dd>^An SQLITE_TRACE_PROFILE callback provides approximately the same
+** information as is provided by the [sqlite3_profile()] callback.
+** ^The P argument is a pointer to the [prepared statement] and the
+** X argument points to a 64-bit integer which is the estimated of
+** the number of nanosecond that the prepared statement took to run.
+** ^The SQLITE_TRACE_PROFILE callback is invoked when the statement finishes.
+**
+** [[SQLITE_TRACE_ROW]] <dt>SQLITE_TRACE_ROW</dt>
+** <dd>^An SQLITE_TRACE_ROW callback is invoked whenever a prepared
+** statement generates a single row of result.
+** ^The P argument is a pointer to the [prepared statement] and the
+** X argument is unused.
+**
+** [[SQLITE_TRACE_CLOSE]] <dt>SQLITE_TRACE_CLOSE</dt>
+** <dd>^An SQLITE_TRACE_CLOSE callback is invoked when a database
+** connection closes.
+** ^The P argument is a pointer to the [database connection] object
+** and the X argument is unused.
+** </dl>
+*/
+#define SQLITE_TRACE_STMT 0x01
+#define SQLITE_TRACE_PROFILE 0x02
+#define SQLITE_TRACE_ROW 0x04
+#define SQLITE_TRACE_CLOSE 0x08
+
+/*
+** CAPI3REF: SQL Trace Hook
+** METHOD: sqlite3
+**
+** ^The sqlite3_trace_v2(D,M,X,P) interface registers a trace callback
+** function X against [database connection] D, using property mask M
+** and context pointer P. ^If the X callback is
+** NULL or if the M mask is zero, then tracing is disabled. The
+** M argument should be the bitwise OR-ed combination of
+** zero or more [SQLITE_TRACE] constants.
+**
+** ^Each call to either sqlite3_trace() or sqlite3_trace_v2() overrides
+** (cancels) any prior calls to sqlite3_trace() or sqlite3_trace_v2().
+**
+** ^The X callback is invoked whenever any of the events identified by
+** mask M occur. ^The integer return value from the callback is currently
+** ignored, though this may change in future releases. Callback
+** implementations should return zero to ensure future compatibility.
+**
+** ^A trace callback is invoked with four arguments: callback(T,C,P,X).
+** ^The T argument is one of the [SQLITE_TRACE]
+** constants to indicate why the callback was invoked.
+** ^The C argument is a copy of the context pointer.
+** The P and X arguments are pointers whose meanings depend on T.
+**
+** The sqlite3_trace_v2() interface is intended to replace the legacy
+** interfaces [sqlite3_trace()] and [sqlite3_profile()], both of which
+** are deprecated.
+*/
+SQLITE_API int sqlite3_trace_v2(
+ sqlite3*,
+ unsigned uMask,
+ int(*xCallback)(unsigned,void*,void*,void*),
+ void *pCtx
+);
+
+/*
** CAPI3REF: Query Progress Callbacks
+** METHOD: sqlite3
**
** ^The sqlite3_progress_handler(D,N,X,P) interface causes the callback
** function X to be invoked periodically during long running calls to
@@ -2355,9 +2930,10 @@ SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
** interface is to keep a GUI updated during a large query.
**
** ^The parameter P is passed through as the only parameter to the
-** callback function X. ^The parameter N is the number of
+** callback function X. ^The parameter N is the approximate number of
** [virtual machine instructions] that are evaluated between successive
-** invocations of the callback X.
+** invocations of the callback X. ^If N is less than one then the progress
+** handler is disabled.
**
** ^Only a single progress handler may be defined at one time per
** [database connection]; setting a new progress handler cancels the
@@ -2379,6 +2955,7 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
/*
** CAPI3REF: Opening A New Database Connection
+** CONSTRUCTOR: sqlite3
**
** ^These routines open an SQLite database file as specified by the
** filename argument. ^The filename argument is interpreted as UTF-8 for
@@ -2393,9 +2970,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** an English language description of the error following a failure of any
** of the sqlite3_open() routines.
**
-** ^The default encoding for the database will be UTF-8 if
-** sqlite3_open() or sqlite3_open_v2() is called and
-** UTF-16 in the native byte order if sqlite3_open16() is used.
+** ^The default encoding will be UTF-8 for databases created using
+** sqlite3_open() or sqlite3_open_v2(). ^The default encoding for databases
+** created using sqlite3_open16() will be UTF-16 in the native byte order.
**
** Whether or not an error occurs when it is opened, resources
** associated with the [database connection] handle should be released by
@@ -2483,13 +3060,14 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** then it is interpreted as an absolute path. ^If the path does not begin
** with a '/' (meaning that the authority section is omitted from the URI)
** then the path is interpreted as a relative path.
-** ^On windows, the first component of an absolute path
-** is a drive specification (e.g. "C:").
+** ^(On windows, the first component of an absolute path
+** is a drive specification (e.g. "C:").)^
**
** [[core URI query parameters]]
** The query component of a URI may contain parameters that are interpreted
** either by SQLite itself, or by a [VFS | custom VFS implementation].
-** SQLite interprets the following three query parameters:
+** SQLite and its built-in [VFSes] interpret the
+** following query parameters:
**
** <ul>
** <li> <b>vfs</b>: ^The "vfs" parameter may be used to specify the name of
@@ -2500,18 +3078,20 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** present, then the VFS specified by the option takes precedence over
** the value passed as the fourth parameter to sqlite3_open_v2().
**
-** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw" or
-** "rwc". Attempting to set it to any other value is an error)^.
+** <li> <b>mode</b>: ^(The mode parameter may be set to either "ro", "rw",
+** "rwc", or "memory". Attempting to set it to any other value is
+** an error)^.
** ^If "ro" is specified, then the database is opened for read-only
** access, just as if the [SQLITE_OPEN_READONLY] flag had been set in the
-** third argument to sqlite3_prepare_v2(). ^If the mode option is set to
+** third argument to sqlite3_open_v2(). ^If the mode option is set to
** "rw", then the database is opened for read-write (but not create)
** access, as if SQLITE_OPEN_READWRITE (but not SQLITE_OPEN_CREATE) had
** been set. ^Value "rwc" is equivalent to setting both
-** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If sqlite3_open_v2() is
-** used, it is an error to specify a value for the mode parameter that is
-** less restrictive than that specified by the flags passed as the third
-** parameter.
+** SQLITE_OPEN_READWRITE and SQLITE_OPEN_CREATE. ^If the mode option is
+** set to "memory" then a pure [in-memory database] that never reads
+** or writes from disk is used. ^It is an error to specify a value for
+** the mode parameter that is less restrictive than that specified by
+** the flags passed in the third parameter to sqlite3_open_v2().
**
** <li> <b>cache</b>: ^The cache parameter may be set to either "shared" or
** "private". ^Setting it to "shared" is equivalent to setting the
@@ -2519,8 +3099,30 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** sqlite3_open_v2(). ^Setting the cache parameter to "private" is
** equivalent to setting the SQLITE_OPEN_PRIVATECACHE bit.
** ^If sqlite3_open_v2() is used and the "cache" parameter is present in
-** a URI filename, its value overrides any behaviour requested by setting
+** a URI filename, its value overrides any behavior requested by setting
** SQLITE_OPEN_PRIVATECACHE or SQLITE_OPEN_SHAREDCACHE flag.
+**
+** <li> <b>psow</b>: ^The psow parameter indicates whether or not the
+** [powersafe overwrite] property does or does not apply to the
+** storage media on which the database file resides.
+**
+** <li> <b>nolock</b>: ^The nolock parameter is a boolean query parameter
+** which if set disables file locking in rollback journal modes. This
+** is useful for accessing a database on a filesystem that does not
+** support locking. Caution: Database corruption might result if two
+** or more processes write to the same database and any one of those
+** processes uses nolock=1.
+**
+** <li> <b>immutable</b>: ^The immutable parameter is a boolean query
+** parameter that indicates that the database file is stored on
+** read-only media. ^When immutable is set, SQLite assumes that the
+** database file cannot be changed, even by a process with higher
+** privilege, and so the database is opened read-only and all locking
+** and change detection is disabled. Caution: Setting the immutable
+** property on a database file that does in fact change can result
+** in incorrect query results and/or [SQLITE_CORRUPT] errors.
+** See also: [SQLITE_IOCAP_IMMUTABLE].
+**
** </ul>
**
** ^Specifying an unknown parameter in the query component of a URI is not an
@@ -2550,8 +3152,9 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** Open file "data.db" in the current directory for read-only access.
** Regardless of whether or not shared-cache mode is enabled by
** default, use a private cache.
-** <tr><td> file:/home/fred/data.db?vfs=unix-nolock <td>
-** Open file "/home/fred/data.db". Use the special VFS "unix-nolock".
+** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
+** Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
+** that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
** An error. "readonly" is not a valid option for the "mode" parameter.
** </table>
@@ -2570,6 +3173,12 @@ SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
** codepage is currently defined. Filenames containing international
** characters must be converted to UTF-8 prior to passing them into
** sqlite3_open() or sqlite3_open_v2().
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling sqlite3_open() or sqlite3_open_v2(). Otherwise, various
+** features that require the use of temporary files may fail.
+**
+** See also: [sqlite3_temp_directory]
*/
SQLITE_API int sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
@@ -2589,31 +3198,58 @@ SQLITE_API int sqlite3_open_v2(
/*
** CAPI3REF: Obtain Values For URI Parameters
**
-** This is a utility routine, useful to VFS implementations, that checks
+** These are utility routines, useful to VFS implementations, that check
** to see if a database file was a URI that contained a specific query
-** parameter, and if so obtains the value of the query parameter.
-**
-** The zFilename argument is the filename pointer passed into the xOpen()
-** method of a VFS implementation. The zParam argument is the name of the
-** query parameter we seek. This routine returns the value of the zParam
-** parameter if it exists. If the parameter does not exist, this routine
-** returns a NULL pointer.
-**
-** If the zFilename argument to this function is not a pointer that SQLite
-** passed into the xOpen VFS method, then the behavior of this routine
-** is undefined and probably undesirable.
+** parameter, and if so obtains the value of that query parameter.
+**
+** If F is the database filename pointer passed into the xOpen() method of
+** a VFS implementation when the flags parameter to xOpen() has one or
+** more of the [SQLITE_OPEN_URI] or [SQLITE_OPEN_MAIN_DB] bits set and
+** P is the name of the query parameter, then
+** sqlite3_uri_parameter(F,P) returns the value of the P
+** parameter if it exists or a NULL pointer if P does not appear as a
+** query parameter on F. If P is a query parameter of F
+** has no explicit value, then sqlite3_uri_parameter(F,P) returns
+** a pointer to an empty string.
+**
+** The sqlite3_uri_boolean(F,P,B) routine assumes that P is a boolean
+** parameter and returns true (1) or false (0) according to the value
+** of P. The sqlite3_uri_boolean(F,P,B) routine returns true (1) if the
+** value of query parameter P is one of "yes", "true", or "on" in any
+** case or if the value begins with a non-zero number. The
+** sqlite3_uri_boolean(F,P,B) routines returns false (0) if the value of
+** query parameter P is one of "no", "false", or "off" in any case or
+** if the value begins with a numeric zero. If P is not a query
+** parameter on F or if the value of P is does not match any of the
+** above, then sqlite3_uri_boolean(F,P,B) returns (B!=0).
+**
+** The sqlite3_uri_int64(F,P,D) routine converts the value of P into a
+** 64-bit signed integer and returns that integer, or D if P does not
+** exist. If the value of P is something other than an integer, then
+** zero is returned.
+**
+** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and
+** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and
+** is not a database file pathname pointer that SQLite passed into the xOpen
+** VFS method, then the behavior of this routine is undefined and probably
+** undesirable.
*/
SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam);
+SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault);
+SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64);
/*
** CAPI3REF: Error Codes And Messages
-**
-** ^The sqlite3_errcode() interface returns the numeric [result code] or
-** [extended result code] for the most recent failed sqlite3_* API call
-** associated with a [database connection]. If a prior API call failed
-** but the most recent API call succeeded, the return value from
-** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode()
+** METHOD: sqlite3
+**
+** ^If the most recent sqlite3_* API call associated with
+** [database connection] D failed, then the sqlite3_errcode(D) interface
+** returns the numeric [result code] or [extended result code] for that
+** API call.
+** If the most recent API call was successful,
+** then the return value from sqlite3_errcode() is undefined.
+** ^The sqlite3_extended_errcode()
** interface is the same except that it always returns the
** [extended result code] even when extended result codes are
** disabled.
@@ -2625,6 +3261,11 @@ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
+** ^The sqlite3_errstr() interface returns the English-language text
+** that describes the [result code], as UTF-8.
+** ^(Memory to hold the error message string is managed internally
+** and must not be freed by the application)^.
+**
** When the serialized [threading mode] is in use, it might be the
** case that a second error occurs on a separate thread in between
** the time of the first error and the call to these interfaces.
@@ -2643,35 +3284,37 @@ SQLITE_API int sqlite3_errcode(sqlite3 *db);
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
+SQLITE_API const char *sqlite3_errstr(int);
/*
-** CAPI3REF: SQL Statement Object
+** CAPI3REF: Prepared Statement Object
** KEYWORDS: {prepared statement} {prepared statements}
**
-** An instance of this object represents a single SQL statement.
-** This object is variously known as a "prepared statement" or a
-** "compiled SQL statement" or simply as a "statement".
+** An instance of this object represents a single SQL statement that
+** has been compiled into binary form and is ready to be evaluated.
**
-** The life of a statement object goes something like this:
+** Think of each SQL statement as a separate computer program. The
+** original SQL text is source code. A prepared statement object
+** is the compiled object code. All SQL must be converted into a
+** prepared statement before it can be run.
+**
+** The life-cycle of a prepared statement object usually goes like this:
**
** <ol>
-** <li> Create the object using [sqlite3_prepare_v2()] or a related
-** function.
-** <li> Bind values to [host parameters] using the sqlite3_bind_*()
+** <li> Create the prepared statement object using [sqlite3_prepare_v2()].
+** <li> Bind values to [parameters] using the sqlite3_bind_*()
** interfaces.
** <li> Run the SQL by calling [sqlite3_step()] one or more times.
-** <li> Reset the statement using [sqlite3_reset()] then go back
+** <li> Reset the prepared statement using [sqlite3_reset()] then go back
** to step 2. Do this zero or more times.
** <li> Destroy the object using [sqlite3_finalize()].
** </ol>
-**
-** Refer to documentation on individual methods above for additional
-** information.
*/
typedef struct sqlite3_stmt sqlite3_stmt;
/*
** CAPI3REF: Run-time Limits
+** METHOD: sqlite3
**
** ^(This interface allows the size of various constructs to be limited
** on a connection by connection basis. The first parameter is the
@@ -2761,6 +3404,10 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
**
** [[SQLITE_LIMIT_TRIGGER_DEPTH]] ^(<dt>SQLITE_LIMIT_TRIGGER_DEPTH</dt>
** <dd>The maximum depth of recursion for triggers.</dd>)^
+**
+** [[SQLITE_LIMIT_WORKER_THREADS]] ^(<dt>SQLITE_LIMIT_WORKER_THREADS</dt>
+** <dd>The maximum number of auxiliary worker threads that a single
+** [prepared statement] may start.</dd>)^
** </dl>
*/
#define SQLITE_LIMIT_LENGTH 0
@@ -2774,10 +3421,13 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
#define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8
#define SQLITE_LIMIT_VARIABLE_NUMBER 9
#define SQLITE_LIMIT_TRIGGER_DEPTH 10
+#define SQLITE_LIMIT_WORKER_THREADS 11
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
+** METHOD: sqlite3
+** CONSTRUCTOR: sqlite3_stmt
**
** To execute an SQL query, it must first be compiled into a byte-code
** program using one of these routines.
@@ -2791,15 +3441,14 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2()
** use UTF-16.
**
-** ^If the nByte argument is less than zero, then zSql is read up to the
-** first zero terminator. ^If nByte is non-negative, then it is the maximum
-** number of bytes read from zSql. ^When nByte is non-negative, the
-** zSql string ends at either the first '\000' or '\u0000' character or
-** the nByte-th byte, whichever comes first. If the caller knows
-** that the supplied string is nul-terminated, then there is a small
-** performance advantage to be gained by passing an nByte parameter that
-** is equal to the number of bytes in the input string <i>including</i>
-** the nul-terminator bytes.
+** ^If the nByte argument is negative, then zSql is read up to the
+** first zero terminator. ^If nByte is positive, then it is the
+** number of bytes read from zSql. ^If nByte is zero, then no prepared
+** statement is generated.
+** If the caller knows that the supplied string is nul-terminated, then
+** there is a small performance advantage to passing an nByte parameter that
+** is the number of bytes in the input string <i>including</i>
+** the nul-terminator.
**
** ^If pzTail is not NULL then *pzTail is made to point to the first byte
** past the end of the first SQL statement in zSql. These routines only
@@ -2829,7 +3478,8 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** <li>
** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it
** always used to do, [sqlite3_step()] will automatically recompile the SQL
-** statement and try to run it again.
+** statement and try to run it again. As many as [SQLITE_MAX_SCHEMA_RETRY]
+** retries will occur before sqlite3_step() gives up and returns an error.
** </li>
**
** <li>
@@ -2850,8 +3500,7 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** ^The specific value of WHERE-clause [parameter] might influence the
** choice of query plan if the parameter is the left-hand side of a [LIKE]
** or [GLOB] operator or if the parameter is compared to an indexed column
-** and the [SQLITE_ENABLE_STAT2] compile-time option is enabled.
-** the
+** and the [SQLITE_ENABLE_STAT3] compile-time option is enabled.
** </li>
** </ol>
*/
@@ -2886,15 +3535,41 @@ SQLITE_API int sqlite3_prepare16_v2(
/*
** CAPI3REF: Retrieving Statement SQL
-**
-** ^This interface can be used to retrieve a saved copy of the original
-** SQL text used to create a [prepared statement] if that statement was
-** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_sql(P) interface returns a pointer to a copy of the UTF-8
+** SQL text used to create [prepared statement] P if P was
+** created by either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()].
+** ^The sqlite3_expanded_sql(P) interface returns a pointer to a UTF-8
+** string containing the SQL text of prepared statement P with
+** [bound parameters] expanded.
+**
+** ^(For example, if a prepared statement is created using the SQL
+** text "SELECT $abc,:xyz" and if parameter $abc is bound to integer 2345
+** and parameter :xyz is unbound, then sqlite3_sql() will return
+** the original string, "SELECT $abc,:xyz" but sqlite3_expanded_sql()
+** will return "SELECT 2345,NULL".)^
+**
+** ^The sqlite3_expanded_sql() interface returns NULL if insufficient memory
+** is available to hold the result, or if the result would exceed the
+** the maximum string length determined by the [SQLITE_LIMIT_LENGTH].
+**
+** ^The [SQLITE_TRACE_SIZE_LIMIT] compile-time option limits the size of
+** bound parameter expansions. ^The [SQLITE_OMIT_TRACE] compile-time
+** option causes sqlite3_expanded_sql() to always return NULL.
+**
+** ^The string returned by sqlite3_sql(P) is managed by SQLite and is
+** automatically freed when the prepared statement is finalized.
+** ^The string returned by sqlite3_expanded_sql(P), on the other hand,
+** is obtained from [sqlite3_malloc()] and must be free by the application
+** by passing it to [sqlite3_free()].
*/
SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
+SQLITE_API char *sqlite3_expanded_sql(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Determine If An SQL Statement Writes The Database
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_stmt_readonly(X) interface returns true (non-zero) if
** and only if the [prepared statement] X makes no direct changes to
@@ -2925,6 +3600,27 @@ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt);
SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
/*
+** CAPI3REF: Determine If A Prepared Statement Has Been Reset
+** METHOD: sqlite3_stmt
+**
+** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the
+** [prepared statement] S has been stepped at least once using
+** [sqlite3_step(S)] but has neither run to completion (returned
+** [SQLITE_DONE] from [sqlite3_step(S)]) nor
+** been reset using [sqlite3_reset(S)]. ^The sqlite3_stmt_busy(S)
+** interface returns false if S is a NULL pointer. If S is not a
+** NULL pointer and is not a pointer to a valid [prepared statement]
+** object, then the behavior is undefined and probably undesirable.
+**
+** This interface can be used in combination [sqlite3_next_stmt()]
+** to locate all prepared statements associated with a database
+** connection that are in need of being reset. This can be used,
+** for example, in diagnostic routines to search for prepared
+** statements that are holding a transaction open.
+*/
+SQLITE_API int sqlite3_stmt_busy(sqlite3_stmt*);
+
+/*
** CAPI3REF: Dynamically Typed Value Object
** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value}
**
@@ -2937,7 +3633,9 @@ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt);
** Some interfaces require a protected sqlite3_value. Other interfaces
** will accept either a protected or an unprotected sqlite3_value.
** Every interface that accepts sqlite3_value arguments specifies
-** whether or not it requires a protected sqlite3_value.
+** whether or not it requires a protected sqlite3_value. The
+** [sqlite3_value_dup()] interface can be used to construct a new
+** protected sqlite3_value from an unprotected sqlite3_value.
**
** The terms "protected" and "unprotected" refer to whether or not
** a mutex is held. An internal mutex is held for a protected
@@ -2981,6 +3679,7 @@ typedef struct sqlite3_context sqlite3_context;
** CAPI3REF: Binding Values To Prepared Statements
** KEYWORDS: {host parameter} {host parameters} {host parameter name}
** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding}
+** METHOD: sqlite3_stmt
**
** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants,
** literals may be replaced by a [parameter] that matches one of following
@@ -3014,18 +3713,31 @@ typedef struct sqlite3_context sqlite3_context;
** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999).
**
** ^The third argument is the value to bind to the parameter.
+** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
+** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
+** is ignored and the end result is the same as sqlite3_bind_null().
**
** ^(In those routines that have a fourth argument, its value is the
** number of bytes in the parameter. To be clear: the value is the
** number of <u>bytes</u> in the value, not the number of characters.)^
-** ^If the fourth parameter is negative, the length of the string is
+** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
+** is negative, then the length of the string is
** the number of bytes up to the first zero terminator.
-**
-** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and
-** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or
+** If the fourth parameter to sqlite3_bind_blob() is negative, then
+** the behavior is undefined.
+** If a non-negative fourth parameter is provided to sqlite3_bind_text()
+** or sqlite3_bind_text16() or sqlite3_bind_text64() then
+** that parameter must be the byte offset
+** where the NUL terminator would occur assuming the string were NUL
+** terminated. If any NUL characters occur at byte offsets less than
+** the value of the fourth parameter then the resulting string value will
+** contain embedded NULs. The result of expressions involving strings
+** with embedded NULs is undefined.
+**
+** ^The fifth argument to the BLOB and string binding interfaces
+** is a destructor used to dispose of the BLOB or
** string after SQLite has finished with it. ^The destructor is called
-** to dispose of the BLOB or string even if the call to sqlite3_bind_blob(),
-** sqlite3_bind_text(), or sqlite3_bind_text16() fails.
+** to dispose of the BLOB or string even if the call to bind API fails.
** ^If the fifth argument is
** the special value [SQLITE_STATIC], then SQLite assumes that the
** information is in static, unmanaged space and does not need to be freed.
@@ -3033,6 +3745,14 @@ typedef struct sqlite3_context sqlite3_context;
** SQLite makes its own private copy of the data immediately, before
** the sqlite3_bind_*() routine returns.
**
+** ^The sixth argument to sqlite3_bind_text64() must be one of
+** [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE]
+** to specify the encoding of the text in the third parameter. If
+** the sixth argument to sqlite3_bind_text64() is not one of the
+** allowed values shown above, or if the text encoding is different
+** from the encoding specified by the sixth parameter, then the behavior
+** is undefined.
+**
** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that
** is filled with zeroes. ^A zeroblob uses a fixed amount of memory
** (just an integer to hold its size) while it is being processed.
@@ -3053,6 +3773,9 @@ typedef struct sqlite3_context sqlite3_context;
**
** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an
** [error code] if anything goes wrong.
+** ^[SQLITE_TOOBIG] might be returned if the size of a string or BLOB
+** exceeds limits imposed by [sqlite3_limit]([SQLITE_LIMIT_LENGTH]) or
+** [SQLITE_MAX_LENGTH].
** ^[SQLITE_RANGE] is returned if the parameter
** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails.
**
@@ -3060,17 +3783,23 @@ typedef struct sqlite3_context sqlite3_context;
** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()].
*/
SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
+SQLITE_API int sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,
+ void(*)(void*));
SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double);
SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int);
SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int);
-SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
+SQLITE_API int sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
+SQLITE_API int sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,
+ void(*)(void*), unsigned char encoding);
SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
+SQLITE_API int sqlite3_bind_zeroblob64(sqlite3_stmt*, int, sqlite3_uint64);
/*
** CAPI3REF: Number Of SQL Parameters
+** METHOD: sqlite3_stmt
**
** ^This routine can be used to find the number of [SQL parameters]
** in a [prepared statement]. SQL parameters are tokens of the
@@ -3091,6 +3820,7 @@ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*);
/*
** CAPI3REF: Name Of A Host Parameter
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_bind_parameter_name(P,N) interface returns
** the name of the N-th [SQL parameter] in the [prepared statement] P.
@@ -3118,6 +3848,7 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
/*
** CAPI3REF: Index Of A Parameter With A Given Name
+** METHOD: sqlite3_stmt
**
** ^Return the index of an SQL parameter given its name. ^The
** index value returned is suitable for use as the second
@@ -3128,12 +3859,13 @@ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int);
**
** See also: [sqlite3_bind_blob|sqlite3_bind()],
** [sqlite3_bind_parameter_count()], and
-** [sqlite3_bind_parameter_index()].
+** [sqlite3_bind_parameter_name()].
*/
SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName);
/*
** CAPI3REF: Reset All Bindings On A Prepared Statement
+** METHOD: sqlite3_stmt
**
** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset
** the [sqlite3_bind_blob | bindings] on a [prepared statement].
@@ -3143,6 +3875,7 @@ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*);
/*
** CAPI3REF: Number Of Columns In A Result Set
+** METHOD: sqlite3_stmt
**
** ^Return the number of columns in the result set returned by the
** [prepared statement]. ^This routine returns 0 if pStmt is an SQL
@@ -3154,6 +3887,7 @@ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Column Names In A Result Set
+** METHOD: sqlite3_stmt
**
** ^These routines return the name assigned to a particular column
** in the result set of a [SELECT] statement. ^The sqlite3_column_name()
@@ -3183,6 +3917,7 @@ SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N);
/*
** CAPI3REF: Source Of Data In A Query Result
+** METHOD: sqlite3_stmt
**
** ^These routines provide a means to determine the database, table, and
** table column that is the origin of a particular result column in
@@ -3235,6 +3970,7 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int);
/*
** CAPI3REF: Declared Datatype Of A Query Result
+** METHOD: sqlite3_stmt
**
** ^(The first parameter is a [prepared statement].
** If this statement is a [SELECT] statement and the Nth column of the
@@ -3267,6 +4003,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
/*
** CAPI3REF: Evaluate An SQL Statement
+** METHOD: sqlite3_stmt
**
** After a [prepared statement] has been prepared using either
** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy
@@ -3323,7 +4060,8 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
-** sqlite3_step(). But after version 3.6.23.1, sqlite3_step() began
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
+** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
** break because any application that ever receives an SQLITE_MISUSE error
@@ -3346,6 +4084,7 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*);
/*
** CAPI3REF: Number of columns in a result set
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_data_count(P) interface returns the number of columns in the
** current row of the result set of [prepared statement] P.
@@ -3353,6 +4092,12 @@ SQLITE_API int sqlite3_step(sqlite3_stmt*);
** (via calls to the [sqlite3_column_int | sqlite3_column_*()] of
** interfaces) then sqlite3_data_count(P) returns 0.
** ^The sqlite3_data_count(P) routine also returns 0 if P is a NULL pointer.
+** ^The sqlite3_data_count(P) routine returns 0 if the previous call to
+** [sqlite3_step](P) returned [SQLITE_DONE]. ^The sqlite3_data_count(P)
+** will return non-zero if previous call to [sqlite3_step](P) returned
+** [SQLITE_ROW], except in the case of the [PRAGMA incremental_vacuum]
+** where it always returns zero since each step of that multi-step
+** pragma returns 0 columns of data.
**
** See also: [sqlite3_column_count()]
*/
@@ -3393,8 +4138,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Result Values From A Query
** KEYWORDS: {column access functions}
-**
-** These routines form the "result set" interface.
+** METHOD: sqlite3_stmt
**
** ^These routines return information about a single column of the current
** result row of a query. ^In every case the first argument is a pointer
@@ -3452,16 +4196,17 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** bytes in the string, not the number of characters.
**
** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(),
-** even empty strings, are always zero terminated. ^The return
+** even empty strings, are always zero-terminated. ^The return
** value from sqlite3_column_blob() for a zero-length BLOB is a NULL pointer.
**
-** ^The object returned by [sqlite3_column_value()] is an
-** [unprotected sqlite3_value] object. An unprotected sqlite3_value object
-** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()].
+** <b>Warning:</b> ^The object returned by [sqlite3_column_value()] is an
+** [unprotected sqlite3_value] object. In a multithreaded environment,
+** an unprotected sqlite3_value object may only be used safely with
+** [sqlite3_bind_value()] and [sqlite3_result_value()].
** If the [unprotected sqlite3_value] object returned by
** [sqlite3_column_value()] is used in any other way, including calls
** to routines like [sqlite3_value_int()], [sqlite3_value_text()],
-** or [sqlite3_value_bytes()], then the behavior is undefined.
+** or [sqlite3_value_bytes()], the behavior is not threadsafe.
**
** These routines attempt to convert the value where appropriate. ^For
** example, if the internal representation is FLOAT and a text result
@@ -3475,29 +4220,23 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
**
** <tr><td> NULL <td> INTEGER <td> Result is 0
** <tr><td> NULL <td> FLOAT <td> Result is 0.0
-** <tr><td> NULL <td> TEXT <td> Result is NULL pointer
-** <tr><td> NULL <td> BLOB <td> Result is NULL pointer
+** <tr><td> NULL <td> TEXT <td> Result is a NULL pointer
+** <tr><td> NULL <td> BLOB <td> Result is a NULL pointer
** <tr><td> INTEGER <td> FLOAT <td> Convert from integer to float
** <tr><td> INTEGER <td> TEXT <td> ASCII rendering of the integer
** <tr><td> INTEGER <td> BLOB <td> Same as INTEGER->TEXT
-** <tr><td> FLOAT <td> INTEGER <td> Convert from float to integer
+** <tr><td> FLOAT <td> INTEGER <td> [CAST] to INTEGER
** <tr><td> FLOAT <td> TEXT <td> ASCII rendering of the float
-** <tr><td> FLOAT <td> BLOB <td> Same as FLOAT->TEXT
-** <tr><td> TEXT <td> INTEGER <td> Use atoi()
-** <tr><td> TEXT <td> FLOAT <td> Use atof()
+** <tr><td> FLOAT <td> BLOB <td> [CAST] to BLOB
+** <tr><td> TEXT <td> INTEGER <td> [CAST] to INTEGER
+** <tr><td> TEXT <td> FLOAT <td> [CAST] to REAL
** <tr><td> TEXT <td> BLOB <td> No change
-** <tr><td> BLOB <td> INTEGER <td> Convert to TEXT then use atoi()
-** <tr><td> BLOB <td> FLOAT <td> Convert to TEXT then use atof()
+** <tr><td> BLOB <td> INTEGER <td> [CAST] to INTEGER
+** <tr><td> BLOB <td> FLOAT <td> [CAST] to REAL
** <tr><td> BLOB <td> TEXT <td> Add a zero terminator if needed
** </table>
** </blockquote>)^
**
-** The table above makes reference to standard C library functions atoi()
-** and atof(). SQLite does not really use these functions. It has its
-** own equivalent internal routines. The atoi() and atof() names are
-** used in the table for brevity and because they are familiar to most
-** C programmers.
-**
** Note that when type conversions occur, pointers returned by prior
** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or
** sqlite3_column_text16() may be invalidated.
@@ -3522,7 +4261,7 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** of conversion are done in place when it is possible, but sometimes they
** are not possible and in those cases prior pointers are invalidated.
**
-** The safest and easiest to remember policy is to invoke these routines
+** The safest policy is to invoke these routines
** in one of the following ways:
**
** <ul>
@@ -3542,8 +4281,8 @@ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt);
** ^The pointers returned are valid until a type conversion occurs as
** described above, or until [sqlite3_step()] or [sqlite3_reset()] or
** [sqlite3_finalize()] is called. ^The memory space used to hold strings
-** and BLOBs is freed automatically. Do <b>not</b> pass the pointers returned
-** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
+** and BLOBs is freed automatically. Do <em>not</em> pass the pointers returned
+** from [sqlite3_column_blob()], [sqlite3_column_text()], etc. into
** [sqlite3_free()].
**
** ^(If a memory allocation error occurs during the evaluation of any
@@ -3565,6 +4304,7 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
/*
** CAPI3REF: Destroy A Prepared Statement Object
+** DESTRUCTOR: sqlite3_stmt
**
** ^The sqlite3_finalize() function is called to delete a [prepared statement].
** ^If the most recent evaluation of the statement encountered no errors
@@ -3592,6 +4332,7 @@ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);
/*
** CAPI3REF: Reset A Prepared Statement Object
+** METHOD: sqlite3_stmt
**
** The sqlite3_reset() function is called to reset a [prepared statement]
** object back to its initial state, ready to be re-executed.
@@ -3621,6 +4362,7 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
** KEYWORDS: {function creation routines}
** KEYWORDS: {application-defined SQL function}
** KEYWORDS: {application-defined SQL functions}
+** METHOD: sqlite3
**
** ^These functions (collectively known as "function creation routines")
** are used to add SQL functions or aggregates or to redefine the behavior
@@ -3652,15 +4394,24 @@ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);
**
** ^The fourth parameter, eTextRep, specifies what
** [SQLITE_UTF8 | text encoding] this SQL function prefers for
-** its parameters. Every SQL function implementation must be able to work
-** with UTF-8, UTF-16le, or UTF-16be. But some implementations may be
-** more efficient with one encoding than another. ^An application may
-** invoke sqlite3_create_function() or sqlite3_create_function16() multiple
-** times with the same function but with different values of eTextRep.
+** its parameters. The application should set this parameter to
+** [SQLITE_UTF16LE] if the function implementation invokes
+** [sqlite3_value_text16le()] on an input, or [SQLITE_UTF16BE] if the
+** implementation invokes [sqlite3_value_text16be()] on an input, or
+** [SQLITE_UTF16] if [sqlite3_value_text16()] is used, or [SQLITE_UTF8]
+** otherwise. ^The same SQL function may be registered multiple times using
+** different preferred text encodings, with different implementations for
+** each encoding.
** ^When multiple implementations of the same function are available, SQLite
** will pick the one that involves the least amount of data conversion.
-** If there is only a single implementation which does not care what text
-** encoding is used, then the fourth argument should be [SQLITE_ANY].
+**
+** ^The fourth parameter may optionally be ORed with [SQLITE_DETERMINISTIC]
+** to signal that the function will always return the same result given
+** the same inputs within a single SQL statement. Most SQL functions are
+** deterministic. The built-in [random()] SQL function is an example of a
+** function that is not deterministic. The SQLite query planner is able to
+** perform additional optimizations on deterministic functions, so use
+** of the [SQLITE_DETERMINISTIC] flag is recommended where possible.
**
** ^(The fifth parameter is an arbitrary pointer. The implementation of the
** function can gain access to this pointer using [sqlite3_user_data()].)^
@@ -3742,22 +4493,32 @@ SQLITE_API int sqlite3_create_function_v2(
** These constant define integer codes that represent the various
** text encodings supported by SQLite.
*/
-#define SQLITE_UTF8 1
-#define SQLITE_UTF16LE 2
-#define SQLITE_UTF16BE 3
+#define SQLITE_UTF8 1 /* IMP: R-37514-35566 */
+#define SQLITE_UTF16LE 2 /* IMP: R-03371-37637 */
+#define SQLITE_UTF16BE 3 /* IMP: R-51971-34154 */
#define SQLITE_UTF16 4 /* Use native byte order */
-#define SQLITE_ANY 5 /* sqlite3_create_function only */
+#define SQLITE_ANY 5 /* Deprecated */
#define SQLITE_UTF16_ALIGNED 8 /* sqlite3_create_collation only */
/*
+** CAPI3REF: Function Flags
+**
+** These constants may be ORed together with the
+** [SQLITE_UTF8 | preferred text encoding] as the fourth argument
+** to [sqlite3_create_function()], [sqlite3_create_function16()], or
+** [sqlite3_create_function_v2()].
+*/
+#define SQLITE_DETERMINISTIC 0x800
+
+/*
** CAPI3REF: Deprecated Functions
** DEPRECATED
**
** These functions are [deprecated]. In order to maintain
** backwards compatibility with older code, these functions continue
** to be supported. However, new applications should avoid
-** the use of these functions. To help encourage people to avoid
-** using these functions, we are not going to tell you what they do.
+** the use of these functions. To encourage programmers to avoid
+** these functions, we will not explain what they do.
*/
#ifndef SQLITE_OMIT_DEPRECATED
SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*);
@@ -3765,15 +4526,17 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*);
SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*);
SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void);
SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void);
-SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64);
+SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),
+ void*,sqlite3_int64);
#endif
/*
-** CAPI3REF: Obtaining SQL Function Parameter Values
+** CAPI3REF: Obtaining SQL Values
+** METHOD: sqlite3_value
**
** The C-language implementation of SQL functions and aggregates uses
** this set of interface routines to access the parameter values on
-** the function or aggregate.
+** the function or aggregate.
**
** The xFunc (for scalar functions) or xStep (for aggregates) parameters
** to [sqlite3_create_function()] and [sqlite3_create_function16()]
@@ -3788,7 +4551,7 @@ SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int6
** object results in undefined behavior.
**
** ^These routines work just like the corresponding [column access functions]
-** except that these routines take a single [protected sqlite3_value] object
+** except that these routines take a single [protected sqlite3_value] object
** pointer instead of a [sqlite3_stmt*] pointer and an integer column number.
**
** ^The sqlite3_value_text16() interface extracts a UTF-16 string
@@ -3827,7 +4590,41 @@ SQLITE_API int sqlite3_value_type(sqlite3_value*);
SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
/*
+** CAPI3REF: Finding The Subtype Of SQL Values
+** METHOD: sqlite3_value
+**
+** The sqlite3_value_subtype(V) function returns the subtype for
+** an [application-defined SQL function] argument V. The subtype
+** information can be used to pass a limited amount of context from
+** one SQL function to another. Use the [sqlite3_result_subtype()]
+** routine to set the subtype for the return value of an SQL function.
+**
+** SQLite makes no use of subtype itself. It merely passes the subtype
+** from the result of one [application-defined SQL function] into the
+** input of another.
+*/
+SQLITE_API unsigned int sqlite3_value_subtype(sqlite3_value*);
+
+/*
+** CAPI3REF: Copy And Free SQL Values
+** METHOD: sqlite3_value
+**
+** ^The sqlite3_value_dup(V) interface makes a copy of the [sqlite3_value]
+** object D and returns a pointer to that copy. ^The [sqlite3_value] returned
+** is a [protected sqlite3_value] object even if the input is not.
+** ^The sqlite3_value_dup(V) interface returns NULL if V is NULL or if a
+** memory allocation fails.
+**
+** ^The sqlite3_value_free(V) interface frees an [sqlite3_value] object
+** previously obtained from [sqlite3_value_dup()]. ^If V is a NULL pointer
+** then sqlite3_value_free(V) is a harmless no-op.
+*/
+SQLITE_API sqlite3_value *sqlite3_value_dup(const sqlite3_value*);
+SQLITE_API void sqlite3_value_free(sqlite3_value*);
+
+/*
** CAPI3REF: Obtain Aggregate Function Context
+** METHOD: sqlite3_context
**
** Implementations of aggregate SQL functions use this
** routine to allocate memory for storing their state.
@@ -3845,14 +4642,17 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*);
** In those cases, sqlite3_aggregate_context() might be called for the
** first time from within xFinal().)^
**
-** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is
-** less than or equal to zero or if a memory allocate error occurs.
+** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer
+** when first called if N is less than or equal to zero or if a memory
+** allocate error occurs.
**
** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is
** determined by the N parameter on first successful call. Changing the
** value of N in subsequent call to sqlite3_aggregate_context() within
** the same aggregate function instance will not resize the memory
-** allocation.)^
+** allocation.)^ Within the xFinal callback, it is customary to set
+** N=0 in calls to sqlite3_aggregate_context(C,N) so that no
+** pointless memory allocations occur.
**
** ^SQLite automatically frees the memory allocated by
** sqlite3_aggregate_context() when the aggregate query concludes.
@@ -3869,6 +4669,7 @@ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes);
/*
** CAPI3REF: User Data For Functions
+** METHOD: sqlite3_context
**
** ^The sqlite3_user_data() interface returns a copy of
** the pointer that was the pUserData parameter (the 5th parameter)
@@ -3883,6 +4684,7 @@ SQLITE_API void *sqlite3_user_data(sqlite3_context*);
/*
** CAPI3REF: Database Connection For Functions
+** METHOD: sqlite3_context
**
** ^The sqlite3_context_db_handle() interface returns a copy of
** the pointer to the [database connection] (the 1st parameter)
@@ -3894,42 +4696,52 @@ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*);
/*
** CAPI3REF: Function Auxiliary Data
+** METHOD: sqlite3_context
**
-** The following two functions may be used by scalar SQL functions to
+** These functions may be used by (non-aggregate) SQL functions to
** associate metadata with argument values. If the same value is passed to
** multiple invocations of the same SQL function during query execution, under
-** some circumstances the associated metadata may be preserved. This may
-** be used, for example, to add a regular-expression matching scalar
-** function. The compiled version of the regular expression is stored as
-** metadata associated with the SQL value passed as the regular expression
-** pattern. The compiled regular expression can be reused on multiple
-** invocations of the same function so that the original pattern string
-** does not need to be recompiled on each invocation.
+** some circumstances the associated metadata may be preserved. An example
+** of where this might be useful is in a regular-expression matching
+** function. The compiled version of the regular expression can be stored as
+** metadata associated with the pattern string.
+** Then as long as the pattern string remains the same,
+** the compiled regular expression can be reused on multiple
+** invocations of the same function.
**
** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata
** associated by the sqlite3_set_auxdata() function with the Nth argument
-** value to the application-defined function. ^If no metadata has been ever
-** been set for the Nth argument of the function, or if the corresponding
-** function parameter has changed since the meta-data was set,
-** then sqlite3_get_auxdata() returns a NULL pointer.
-**
-** ^The sqlite3_set_auxdata() interface saves the metadata
-** pointed to by its 3rd parameter as the metadata for the N-th
-** argument of the application-defined function. Subsequent
-** calls to sqlite3_get_auxdata() might return this data, if it has
-** not been destroyed.
-** ^If it is not NULL, SQLite will invoke the destructor
-** function given by the 4th parameter to sqlite3_set_auxdata() on
-** the metadata when the corresponding function parameter changes
-** or when the SQL statement completes, whichever comes first.
-**
-** SQLite is free to call the destructor and drop metadata on any
-** parameter of any function at any time. ^The only guarantee is that
-** the destructor will be called before the metadata is dropped.
+** value to the application-defined function. ^If there is no metadata
+** associated with the function argument, this sqlite3_get_auxdata() interface
+** returns a NULL pointer.
+**
+** ^The sqlite3_set_auxdata(C,N,P,X) interface saves P as metadata for the N-th
+** argument of the application-defined function. ^Subsequent
+** calls to sqlite3_get_auxdata(C,N) return P from the most recent
+** sqlite3_set_auxdata(C,N,P,X) call if the metadata is still valid or
+** NULL if the metadata has been discarded.
+** ^After each call to sqlite3_set_auxdata(C,N,P,X) where X is not NULL,
+** SQLite will invoke the destructor function X with parameter P exactly
+** once, when the metadata is discarded.
+** SQLite is free to discard the metadata at any time, including: <ul>
+** <li> ^(when the corresponding function parameter changes)^, or
+** <li> ^(when [sqlite3_reset()] or [sqlite3_finalize()] is called for the
+** SQL statement)^, or
+** <li> ^(when sqlite3_set_auxdata() is invoked again on the same
+** parameter)^, or
+** <li> ^(during the original sqlite3_set_auxdata() call when a memory
+** allocation error occurs.)^ </ul>
+**
+** Note the last bullet in particular. The destructor X in
+** sqlite3_set_auxdata(C,N,P,X) might be called immediately, before the
+** sqlite3_set_auxdata() interface even returns. Hence sqlite3_set_auxdata()
+** should be called near the end of the function implementation and the
+** function implementation should not make any use of P after
+** sqlite3_set_auxdata() has been called.
**
** ^(In practice, metadata is preserved between function calls for
-** expressions that are constant at compile time. This includes literal
-** values and [parameters].)^
+** function parameters that are compile-time constants, including literal
+** values and [parameters] and expressions composed from the same.)^
**
** These routines must be called from the same thread in which
** the SQL function is running.
@@ -3950,7 +4762,7 @@ SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(voi
** the content before returning.
**
** The typedef is necessary to work around problems in certain
-** C++ compilers. See ticket #2191.
+** C++ compilers.
*/
typedef void (*sqlite3_destructor_type)(void*);
#define SQLITE_STATIC ((sqlite3_destructor_type)0)
@@ -3958,6 +4770,7 @@ typedef void (*sqlite3_destructor_type)(void*);
/*
** CAPI3REF: Setting The Result Of An SQL Function
+** METHOD: sqlite3_context
**
** These routines are used by the xFunc or xFinal callbacks that
** implement SQL functions and aggregates. See
@@ -3973,9 +4786,9 @@ typedef void (*sqlite3_destructor_type)(void*);
** to by the second parameter and which is N bytes long where N is the
** third parameter.
**
-** ^The sqlite3_result_zeroblob() interfaces set the result of
-** the application-defined function to be a BLOB containing all zero
-** bytes and N bytes in size, where N is the value of the 2nd parameter.
+** ^The sqlite3_result_zeroblob(C,N) and sqlite3_result_zeroblob64(C,N)
+** interfaces set the result of the application-defined function to be
+** a BLOB containing all zero bytes and N bytes in size.
**
** ^The sqlite3_result_double() interface sets the result from
** an application-defined function to be a floating point value specified
@@ -4003,11 +4816,11 @@ typedef void (*sqlite3_destructor_type)(void*);
** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error()
** or sqlite3_result_error16() resets the error code to SQLITE_ERROR.
**
-** ^The sqlite3_result_toobig() interface causes SQLite to throw an error
-** indicating that a string or BLOB is too long to represent.
+** ^The sqlite3_result_error_toobig() interface causes SQLite to throw an
+** error indicating that a string or BLOB is too long to represent.
**
-** ^The sqlite3_result_nomem() interface causes SQLite to throw an error
-** indicating that a memory allocation failed.
+** ^The sqlite3_result_error_nomem() interface causes SQLite to throw an
+** error indicating that a memory allocation failed.
**
** ^The sqlite3_result_int() interface sets the return value
** of the application-defined function to be the 32-bit signed integer
@@ -4024,6 +4837,10 @@ typedef void (*sqlite3_destructor_type)(void*);
** set the return value of the application-defined function to be
** a text string which is represented as UTF-8, UTF-16 native byte order,
** UTF-16 little endian, or UTF-16 big endian, respectively.
+** ^The sqlite3_result_text64() interface sets the return value of an
+** application-defined function to be a text string in an encoding
+** specified by the fifth (and last) parameter, which must be one
+** of [SQLITE_UTF8], [SQLITE_UTF16], [SQLITE_UTF16BE], or [SQLITE_UTF16LE].
** ^SQLite takes the text result from the application from
** the 2nd parameter of the sqlite3_result_text* interfaces.
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
@@ -4032,7 +4849,12 @@ typedef void (*sqlite3_destructor_type)(void*);
** ^If the 3rd parameter to the sqlite3_result_text* interfaces
** is non-negative, then as many bytes (not characters) of the text
** pointed to by the 2nd parameter are taken as the application-defined
-** function result.
+** function result. If the 3rd parameter is non-negative, then it
+** must be the byte offset into the string where the NUL terminator would
+** appear if the string where NUL terminated. If any NUL characters occur
+** in the string at a byte offset that is less than the value of the 3rd
+** parameter, then the resulting string will contain embedded NULs and the
+** result of expressions operating on strings with embedded NULs is undefined.
** ^If the 4th parameter to the sqlite3_result_text* interfaces
** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that
** function as the destructor on the text or BLOB result when it has
@@ -4048,7 +4870,7 @@ typedef void (*sqlite3_destructor_type)(void*);
** from [sqlite3_malloc()] before it returns.
**
** ^The sqlite3_result_value() interface sets the result of
-** the application-defined function to be a copy the
+** the application-defined function to be a copy of the
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
** so that the [sqlite3_value] specified in the parameter may change or
@@ -4062,6 +4884,8 @@ typedef void (*sqlite3_destructor_type)(void*);
** the [sqlite3_context] pointer, the results are undefined.
*/
SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*));
+SQLITE_API void sqlite3_result_blob64(sqlite3_context*,const void*,
+ sqlite3_uint64,void(*)(void*));
SQLITE_API void sqlite3_result_double(sqlite3_context*, double);
SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int);
SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int);
@@ -4072,14 +4896,33 @@ SQLITE_API void sqlite3_result_int(sqlite3_context*, int);
SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64);
SQLITE_API void sqlite3_result_null(sqlite3_context*);
SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*));
+SQLITE_API void sqlite3_result_text64(sqlite3_context*, const char*,sqlite3_uint64,
+ void(*)(void*), unsigned char encoding);
SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*));
SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*));
SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*));
SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*);
SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n);
+SQLITE_API int sqlite3_result_zeroblob64(sqlite3_context*, sqlite3_uint64 n);
+
+
+/*
+** CAPI3REF: Setting The Subtype Of An SQL Function
+** METHOD: sqlite3_context
+**
+** The sqlite3_result_subtype(C,T) function causes the subtype of
+** the result from the [application-defined SQL function] with
+** [sqlite3_context] C to be the value T. Only the lower 8 bits
+** of the subtype T are preserved in current versions of SQLite;
+** higher order bits are discarded.
+** The number of subtype bytes preserved by SQLite might increase
+** in future releases of SQLite.
+*/
+SQLITE_API void sqlite3_result_subtype(sqlite3_context*,unsigned int);
/*
** CAPI3REF: Define New Collating Sequences
+** METHOD: sqlite3
**
** ^These functions add, remove, or modify a [collation] associated
** with the [database connection] specified as the first argument.
@@ -4182,6 +5025,7 @@ SQLITE_API int sqlite3_create_collation16(
/*
** CAPI3REF: Collation Needed Callbacks
+** METHOD: sqlite3
**
** ^To avoid having to register all collation sequences before a database
** can be used, a single callback function may be registered with the
@@ -4229,6 +5073,11 @@ SQLITE_API int sqlite3_key(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The key */
);
+SQLITE_API int sqlite3_key_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The key */
+);
/*
** Change the key on an open database. If the current database is not
@@ -4242,6 +5091,11 @@ SQLITE_API int sqlite3_rekey(
sqlite3 *db, /* Database to be rekeyed */
const void *pKey, int nKey /* The new key */
);
+SQLITE_API int sqlite3_rekey_v2(
+ sqlite3 *db, /* Database to be rekeyed */
+ const char *zDbName, /* Name of the database */
+ const void *pKey, int nKey /* The new key */
+);
/*
** Specify the activation key for a SEE database. Unless
@@ -4291,6 +5145,13 @@ SQLITE_API int sqlite3_sleep(int);
** is a NULL pointer, then SQLite performs a search for an appropriate
** temporary file directory.
**
+** Applications are strongly discouraged from using this global variable.
+** It is required to set a temporary folder on Windows Runtime (WinRT).
+** But for all other platforms, it is highly recommended that applications
+** neither read nor write this variable. This global variable is a relic
+** that exists for backwards compatibility of legacy applications and should
+** be avoided in new projects.
+**
** It is not safe to read or modify this variable in more than one
** thread at a time. It is not safe to read or modify this variable
** if a [database connection] is being used at the same time in a separate
@@ -4309,12 +5170,70 @@ SQLITE_API int sqlite3_sleep(int);
** Hence, if this variable is modified directly, either it should be
** made NULL or made to point to memory obtained from [sqlite3_malloc]
** or else the use of the [temp_store_directory pragma] should be avoided.
+** Except when requested by the [temp_store_directory pragma], SQLite
+** does not free the memory that sqlite3_temp_directory points to. If
+** the application wants that memory to be freed, it must do
+** so itself, taking care to only do so after all [database connection]
+** objects have been destroyed.
+**
+** <b>Note to Windows Runtime users:</b> The temporary directory must be set
+** prior to calling [sqlite3_open] or [sqlite3_open_v2]. Otherwise, various
+** features that require the use of temporary files may fail. Here is an
+** example of how to do this using C++ with the Windows Runtime:
+**
+** <blockquote><pre>
+** LPCWSTR zPath = Windows::Storage::ApplicationData::Current->
+** &nbsp; TemporaryFolder->Path->Data();
+** char zPathBuf&#91;MAX_PATH + 1&#93;;
+** memset(zPathBuf, 0, sizeof(zPathBuf));
+** WideCharToMultiByte(CP_UTF8, 0, zPath, -1, zPathBuf, sizeof(zPathBuf),
+** &nbsp; NULL, NULL);
+** sqlite3_temp_directory = sqlite3_mprintf("%s", zPathBuf);
+** </pre></blockquote>
*/
SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory;
/*
+** CAPI3REF: Name Of The Folder Holding Database Files
+**
+** ^(If this global variable is made to point to a string which is
+** the name of a folder (a.k.a. directory), then all database files
+** specified with a relative pathname and created or accessed by
+** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed
+** to be relative to that directory.)^ ^If this variable is a NULL
+** pointer, then SQLite assumes that all database files specified
+** with a relative pathname are relative to the current directory
+** for the process. Only the windows VFS makes use of this global
+** variable; it is ignored by the unix VFS.
+**
+** Changing the value of this variable while a database connection is
+** open can result in a corrupt database.
+**
+** It is not safe to read or modify this variable in more than one
+** thread at a time. It is not safe to read or modify this variable
+** if a [database connection] is being used at the same time in a separate
+** thread.
+** It is intended that this variable be set once
+** as part of process initialization and before any SQLite interface
+** routines have been called and that this variable remain unchanged
+** thereafter.
+**
+** ^The [data_store_directory pragma] may modify this variable and cause
+** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore,
+** the [data_store_directory pragma] always assumes that any string
+** that this variable points to is held in memory obtained from
+** [sqlite3_malloc] and the pragma may attempt to free that memory
+** using [sqlite3_free].
+** Hence, if this variable is modified directly, either it should be
+** made NULL or made to point to memory obtained from [sqlite3_malloc]
+** or else the use of the [data_store_directory pragma] should be avoided.
+*/
+SQLITE_API SQLITE_EXTERN char *sqlite3_data_directory;
+
+/*
** CAPI3REF: Test For Auto-Commit Mode
** KEYWORDS: {autocommit mode}
+** METHOD: sqlite3
**
** ^The sqlite3_get_autocommit() interface returns non-zero or
** zero if the given database connection is or is not in autocommit mode,
@@ -4337,6 +5256,7 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
/*
** CAPI3REF: Find The Database Handle Of A Prepared Statement
+** METHOD: sqlite3_stmt
**
** ^The sqlite3_db_handle interface returns the [database connection] handle
** to which a [prepared statement] belongs. ^The [database connection]
@@ -4348,7 +5268,35 @@ SQLITE_API int sqlite3_get_autocommit(sqlite3*);
SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*);
/*
+** CAPI3REF: Return The Filename For A Database Connection
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename
+** associated with database N of connection D. ^The main database file
+** has the name "main". If there is no attached database N on the database
+** connection D, or if database N is a temporary or in-memory database, then
+** a NULL pointer is returned.
+**
+** ^The filename returned by this function is the output of the
+** xFullPathname method of the [VFS]. ^In other words, the filename
+** will be an absolute pathname, even if the filename used
+** to open the database originally was a URI or relative pathname.
+*/
+SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName);
+
+/*
+** CAPI3REF: Determine if a database is read-only
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_readonly(D,N) interface returns 1 if the database N
+** of connection D is read-only, 0 if it is read/write, or -1 if N is not
+** the name of a database on connection D.
+*/
+SQLITE_API int sqlite3_db_readonly(sqlite3 *db, const char *zDbName);
+
+/*
** CAPI3REF: Find the next prepared statement
+** METHOD: sqlite3
**
** ^This interface returns a pointer to the next [prepared statement] after
** pStmt associated with the [database connection] pDb. ^If pStmt is NULL
@@ -4364,6 +5312,7 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
/*
** CAPI3REF: Commit And Rollback Notification Callbacks
+** METHOD: sqlite3
**
** ^The sqlite3_commit_hook() interface registers a callback
** function to be invoked whenever a transaction is [COMMIT | committed].
@@ -4382,13 +5331,15 @@ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt);
** on the same [database connection] D, or NULL for
** the first call for each function on D.
**
+** The commit and rollback hook callbacks are not reentrant.
** The callback implementation must not do anything that will modify
** the database connection that invoked the callback. Any actions
** to modify the database connection must be deferred until after the
** completion of the [sqlite3_step()] call that triggered the commit
** or rollback hook in the first place.
-** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
-** database connections for the meaning of "modify" in this paragraph.
+** Note that running any other SQL statements, including SELECT statements,
+** or merely calling [sqlite3_prepare_v2()] and [sqlite3_step()] will modify
+** the database connections for the meaning of "modify" in this paragraph.
**
** ^Registering a NULL function disables the callback.
**
@@ -4411,15 +5362,17 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
/*
** CAPI3REF: Data Change Notification Callbacks
+** METHOD: sqlite3
**
** ^The sqlite3_update_hook() interface registers a callback function
** with the [database connection] identified by the first argument
-** to be invoked whenever a row is updated, inserted or deleted.
+** to be invoked whenever a row is updated, inserted or deleted in
+** a [rowid table].
** ^Any callback set by a previous call to this function
** for the same database connection is overridden.
**
** ^The second argument is a pointer to the function to invoke when a
-** row is updated, inserted or deleted.
+** row is updated, inserted or deleted in a rowid table.
** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
@@ -4432,6 +5385,7 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
**
** ^(The update hook is not invoked when internal system tables are
** modified (i.e. sqlite_master and sqlite_sequence).)^
+** ^The update hook is not invoked when [WITHOUT ROWID] tables are modified.
**
** ^In the current implementation, the update hook
** is not invoked when duplication rows are deleted because of an
@@ -4452,8 +5406,8 @@ SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*);
** on the same [database connection] D, or NULL for
** the first call on D.
**
-** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()]
-** interfaces.
+** See also the [sqlite3_commit_hook()], [sqlite3_rollback_hook()],
+** and [sqlite3_preupdate_hook()] interfaces.
*/
SQLITE_API void *sqlite3_update_hook(
sqlite3*,
@@ -4463,7 +5417,6 @@ SQLITE_API void *sqlite3_update_hook(
/*
** CAPI3REF: Enable Or Disable Shared Pager Cache
-** KEYWORDS: {shared cache}
**
** ^(This routine enables or disables the sharing of the database cache
** and schema data structures between [database connection | connections]
@@ -4471,7 +5424,8 @@ SQLITE_API void *sqlite3_update_hook(
** and disabled if the argument is false.)^
**
** ^Cache sharing is enabled and disabled for an entire process.
-** This is a change as of SQLite version 3.5.0. In prior versions of SQLite,
+** This is a change as of SQLite [version 3.5.0] ([dateof:3.5.0]).
+** In prior versions of SQLite,
** sharing was enabled or disabled for each thread separately.
**
** ^(The cache sharing mode set by this interface effects all subsequent
@@ -4486,6 +5440,14 @@ SQLITE_API void *sqlite3_update_hook(
** future releases of SQLite. Applications that care about shared
** cache setting should set it explicitly.
**
+** Note: This method is disabled on MacOS X 10.7 and iOS version 5.0
+** and will always return SQLITE_MISUSE. On those systems,
+** shared cache mode should be enabled per-database connection via
+** [sqlite3_open_v2()] with [SQLITE_OPEN_SHAREDCACHE].
+**
+** This interface is threadsafe on processors where writing a
+** 32-bit integer is atomic.
+**
** See Also: [SQLite Shared-Cache Mode]
*/
SQLITE_API int sqlite3_enable_shared_cache(int);
@@ -4501,10 +5463,26 @@ SQLITE_API int sqlite3_enable_shared_cache(int);
** which might be more or less than the amount requested.
** ^The sqlite3_release_memory() routine is a no-op returning zero
** if SQLite is not compiled with [SQLITE_ENABLE_MEMORY_MANAGEMENT].
+**
+** See also: [sqlite3_db_release_memory()]
*/
SQLITE_API int sqlite3_release_memory(int);
/*
+** CAPI3REF: Free Memory Used By A Database Connection
+** METHOD: sqlite3
+**
+** ^The sqlite3_db_release_memory(D) interface attempts to free as much heap
+** memory as possible from database connection D. Unlike the
+** [sqlite3_release_memory()] interface, this interface is in effect even
+** when the [SQLITE_ENABLE_MEMORY_MANAGEMENT] compile-time option is
+** omitted.
+**
+** See also: [sqlite3_release_memory()]
+*/
+SQLITE_API int sqlite3_db_release_memory(sqlite3*);
+
+/*
** CAPI3REF: Impose A Limit On Heap Size
**
** ^The sqlite3_soft_heap_limit64() interface sets and/or queries the
@@ -4518,7 +5496,8 @@ SQLITE_API int sqlite3_release_memory(int);
** is advisory only.
**
** ^The return value from sqlite3_soft_heap_limit64() is the size of
-** the soft heap limit prior to the call. ^If the argument N is negative
+** the soft heap limit prior to the call, or negative in the case of an
+** error. ^If the argument N is negative
** then no change is made to the soft heap limit. Hence, the current
** size of the soft heap limit can be determined by invoking
** sqlite3_soft_heap_limit64() with a negative argument.
@@ -4534,13 +5513,14 @@ SQLITE_API int sqlite3_release_memory(int);
** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and
** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option.
** <li> An alternative page cache implementation is specified using
-** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...).
+** [sqlite3_config]([SQLITE_CONFIG_PCACHE2],...).
** <li> The page cache allocates from its own memory pool supplied
** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than
** from the heap.
** </ul>)^
**
-** Beginning with SQLite version 3.7.3, the soft heap limit is enforced
+** Beginning with SQLite [version 3.7.3] ([dateof:3.7.3]),
+** the soft heap limit is enforced
** regardless of whether or not the [SQLITE_ENABLE_MEMORY_MANAGEMENT]
** compile-time option is invoked. With [SQLITE_ENABLE_MEMORY_MANAGEMENT],
** the soft heap limit is enforced on every memory allocation. Without
@@ -4569,21 +5549,29 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
/*
** CAPI3REF: Extract Metadata About A Column Of A Table
-**
-** ^This routine returns metadata about a specific column of a specific
-** database table accessible using the [database connection] handle
-** passed as the first function argument.
+** METHOD: sqlite3
+**
+** ^(The sqlite3_table_column_metadata(X,D,T,C,....) routine returns
+** information about column C of table T in database D
+** on [database connection] X.)^ ^The sqlite3_table_column_metadata()
+** interface returns SQLITE_OK and fills in the non-NULL pointers in
+** the final five arguments with appropriate values if the specified
+** column exists. ^The sqlite3_table_column_metadata() interface returns
+** SQLITE_ERROR and if the specified column does not exist.
+** ^If the column-name parameter to sqlite3_table_column_metadata() is a
+** NULL pointer, then this routine simply checks for the existence of the
+** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it
+** does not.
**
** ^The column is identified by the second, third and fourth parameters to
-** this function. ^The second parameter is either the name of the database
+** this function. ^(The second parameter is either the name of the database
** (i.e. "main", "temp", or an attached database) containing the specified
-** table or NULL. ^If it is NULL, then all attached databases are searched
+** table or NULL.)^ ^If it is NULL, then all attached databases are searched
** for the table using the same algorithm used by the database engine to
** resolve unqualified table references.
**
** ^The third and fourth parameters to this function are the table and column
-** name of the desired column, respectively. Neither of these parameters
-** may be NULL.
+** name of the desired column, respectively.
**
** ^Metadata is returned by writing to the memory locations passed as the 5th
** and subsequent parameters to this function. ^Any of these arguments may be
@@ -4602,16 +5590,17 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
** </blockquote>)^
**
** ^The memory pointed to by the character pointers returned for the
-** declaration type and collation sequence is valid only until the next
+** declaration type and collation sequence is valid until the next
** call to any SQLite API function.
**
** ^If the specified table is actually a view, an [error code] is returned.
**
-** ^If the specified column is "rowid", "oid" or "_rowid_" and an
+** ^If the specified column is "rowid", "oid" or "_rowid_" and the table
+** is not a [WITHOUT ROWID] table and an
** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output
** parameters are set for the explicitly declared column. ^(If there is no
-** explicitly declared [INTEGER PRIMARY KEY] column, then the output
-** parameters are set as follows:
+** [INTEGER PRIMARY KEY] column, then the outputs
+** for the [rowid] are set as follows:
**
** <pre>
** data type: "INTEGER"
@@ -4621,13 +5610,9 @@ SQLITE_API SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N);
** auto increment: 0
** </pre>)^
**
-** ^(This function may load one or more schemas from database files. If an
-** error occurs during this process, or if the requested table or column
-** cannot be found, an [error code] is returned and an error message left
-** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^
-**
-** ^This API is only available if the library was compiled with the
-** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined.
+** ^This function causes all database schemas to be read from disk and
+** parsed, if that has not already been done, and returns an error if
+** any errors are encountered while loading the schema.
*/
SQLITE_API int sqlite3_table_column_metadata(
sqlite3 *db, /* Connection handle */
@@ -4643,15 +5628,25 @@ SQLITE_API int sqlite3_table_column_metadata(
/*
** CAPI3REF: Load An Extension
+** METHOD: sqlite3
**
** ^This interface loads an SQLite extension library from the named file.
**
** ^The sqlite3_load_extension() interface attempts to load an
-** SQLite extension library contained in the file zFile.
+** [SQLite extension] library contained in the file zFile. If
+** the file cannot be loaded directly, attempts are made to load
+** with various operating-system specific extensions added.
+** So for example, if "samplelib" cannot be loaded, then names like
+** "samplelib.so" or "samplelib.dylib" or "samplelib.dll" might
+** be tried also.
**
** ^The entry point is zProc.
-** ^zProc may be 0, in which case the name of the entry point
-** defaults to "sqlite3_extension_init".
+** ^(zProc may be 0, in which case SQLite will try to come up with an
+** entry point name on its own. It first tries "sqlite3_extension_init".
+** If that does not work, it constructs a name "sqlite3_X_init" where the
+** X is consists of the lower-case equivalent of all ASCII alphabetic
+** characters in the filename from the last "/" to the first following
+** "." and omitting any initial "lib".)^
** ^The sqlite3_load_extension() interface returns
** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong.
** ^If an error occurs and pzErrMsg is not 0, then the
@@ -4661,9 +5656,18 @@ SQLITE_API int sqlite3_table_column_metadata(
** should free this memory by calling [sqlite3_free()].
**
** ^Extension loading must be enabled using
-** [sqlite3_enable_load_extension()] prior to calling this API,
+** [sqlite3_enable_load_extension()] or
+** [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],1,NULL)
+** prior to calling this API,
** otherwise an error will be returned.
**
+** <b>Security warning:</b> It is recommended that the
+** [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method be used to enable only this
+** interface. The use of the [sqlite3_enable_load_extension()] interface
+** should be avoided. This will keep the SQL function [load_extension()]
+** disabled and prevent SQL injections from giving attackers
+** access to extension loading capabilities.
+**
** See also the [load_extension() SQL function].
*/
SQLITE_API int sqlite3_load_extension(
@@ -4675,16 +5679,28 @@ SQLITE_API int sqlite3_load_extension(
/*
** CAPI3REF: Enable Or Disable Extension Loading
+** METHOD: sqlite3
**
** ^So as not to open security holes in older applications that are
-** unprepared to deal with extension loading, and as a means of disabling
-** extension loading while evaluating user-entered SQL, the following API
+** unprepared to deal with [extension loading], and as a means of disabling
+** [extension loading] while evaluating user-entered SQL, the following API
** is provided to turn the [sqlite3_load_extension()] mechanism on and off.
**
-** ^Extension loading is off by default. See ticket #1863.
+** ^Extension loading is off by default.
** ^Call the sqlite3_enable_load_extension() routine with onoff==1
** to turn extension loading on and call it with onoff==0 to turn
** it back off again.
+**
+** ^This interface enables or disables both the C-API
+** [sqlite3_load_extension()] and the SQL function [load_extension()].
+** ^(Use [sqlite3_db_config](db,[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION],..)
+** to enable or disable only the C-API.)^
+**
+** <b>Security warning:</b> It is recommended that extension loading
+** be disabled using the [SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION] method
+** rather than this interface, so the [load_extension()] SQL function
+** remains disabled. This will prevent SQL injections from giving attackers
+** access to extension loading capabilities.
*/
SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
@@ -4693,12 +5709,12 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
**
** ^This interface causes the xEntryPoint() function to be invoked for
** each new [database connection] that is created. The idea here is that
-** xEntryPoint() is the entry point for a statically linked SQLite extension
+** xEntryPoint() is the entry point for a statically linked [SQLite extension]
** that is to be automatically loaded into all new database connections.
**
** ^(Even though the function prototype shows that xEntryPoint() takes
** no arguments and returns void, SQLite invokes xEntryPoint() with three
-** arguments and expects and integer result as if the signature of the
+** arguments and expects an integer result as if the signature of the
** entry point where as follows:
**
** <blockquote><pre>
@@ -4721,9 +5737,22 @@ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff);
** on the list of automatic extensions is a harmless no-op. ^No entry point
** will be called more than once for each database connection that is opened.
**
-** See also: [sqlite3_reset_auto_extension()].
+** See also: [sqlite3_reset_auto_extension()]
+** and [sqlite3_cancel_auto_extension()]
+*/
+SQLITE_API int sqlite3_auto_extension(void(*xEntryPoint)(void));
+
+/*
+** CAPI3REF: Cancel Automatic Extension Loading
+**
+** ^The [sqlite3_cancel_auto_extension(X)] interface unregisters the
+** initialization routine X that was registered using a prior call to
+** [sqlite3_auto_extension(X)]. ^The [sqlite3_cancel_auto_extension(X)]
+** routine returns 1 if initialization routine X was successfully
+** unregistered and it returns 0 if X was not on the list of initialization
+** routines.
*/
-SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void));
+SQLITE_API int sqlite3_cancel_auto_extension(void(*xEntryPoint)(void));
/*
** CAPI3REF: Reset Automatic Extension Loading
@@ -4833,6 +5862,17 @@ struct sqlite3_module {
** ^Information about the ORDER BY clause is stored in aOrderBy[].
** ^Each term of aOrderBy records a column of the ORDER BY clause.
**
+** The colUsed field indicates which columns of the virtual table may be
+** required by the current scan. Virtual table columns are numbered from
+** zero in the order in which they appear within the CREATE TABLE statement
+** passed to sqlite3_declare_vtab(). For the first 63 columns (columns 0-62),
+** the corresponding bit is set within the colUsed mask if the column may be
+** required by SQLite. If the table has at least 64 columns and any column
+** to the right of the first 63 is required, then bit 63 of colUsed is also
+** set. In other words, column iCol may be required if the expression
+** (colUsed & ((sqlite3_uint64)1 << (iCol>=63 ? 63 : iCol))) evaluates to
+** non-zero.
+**
** The [xBestIndex] method must fill aConstraintUsage[] with information
** about what parameters to pass to xFilter. ^If argvIndex>0 then
** the right-hand side of the corresponding aConstraint[] is evaluated
@@ -4849,16 +5889,48 @@ struct sqlite3_module {
** the correct order to satisfy the ORDER BY clause so that no separate
** sorting step is required.
**
-** ^The estimatedCost value is an estimate of the cost of doing the
-** particular lookup. A full scan of a table with N entries should have
-** a cost of N. A binary search of a table of N entries should have a
-** cost of approximately log(N).
+** ^The estimatedCost value is an estimate of the cost of a particular
+** strategy. A cost of N indicates that the cost of the strategy is similar
+** to a linear scan of an SQLite table with N rows. A cost of log(N)
+** indicates that the expense of the operation is similar to that of a
+** binary search on a unique indexed field of an SQLite table with N rows.
+**
+** ^The estimatedRows value is an estimate of the number of rows that
+** will be returned by the strategy.
+**
+** The xBestIndex method may optionally populate the idxFlags field with a
+** mask of SQLITE_INDEX_SCAN_* flags. Currently there is only one such flag -
+** SQLITE_INDEX_SCAN_UNIQUE. If the xBestIndex method sets this flag, SQLite
+** assumes that the strategy may visit at most one row.
+**
+** Additionally, if xBestIndex sets the SQLITE_INDEX_SCAN_UNIQUE flag, then
+** SQLite also assumes that if a call to the xUpdate() method is made as
+** part of the same statement to delete or update a virtual table row and the
+** implementation returns SQLITE_CONSTRAINT, then there is no need to rollback
+** any database changes. In other words, if the xUpdate() returns
+** SQLITE_CONSTRAINT, the database contents must be exactly as they were
+** before xUpdate was called. By contrast, if SQLITE_INDEX_SCAN_UNIQUE is not
+** set and xUpdate returns SQLITE_CONSTRAINT, any database changes made by
+** the xUpdate method are automatically rolled back by SQLite.
+**
+** IMPORTANT: The estimatedRows field was added to the sqlite3_index_info
+** structure for SQLite [version 3.8.2] ([dateof:3.8.2]).
+** If a virtual table extension is
+** used with an SQLite version earlier than 3.8.2, the results of attempting
+** to read or write the estimatedRows field are undefined (but are likely
+** to included crashing the application). The estimatedRows field should
+** therefore only be used if [sqlite3_libversion_number()] returns a
+** value greater than or equal to 3008002. Similarly, the idxFlags field
+** was added for [version 3.9.0] ([dateof:3.9.0]).
+** It may therefore only be used if
+** sqlite3_libversion_number() returns a value greater than or equal to
+** 3009000.
*/
struct sqlite3_index_info {
/* Inputs */
int nConstraint; /* Number of entries in aConstraint */
struct sqlite3_index_constraint {
- int iColumn; /* Column on left-hand side of constraint */
+ int iColumn; /* Column constrained. -1 for ROWID */
unsigned char op; /* Constraint operator */
unsigned char usable; /* True if this constraint is usable */
int iTermOffset; /* Used internally - xBestIndex should ignore */
@@ -4877,10 +5949,21 @@ struct sqlite3_index_info {
char *idxStr; /* String, possibly obtained from sqlite3_malloc */
int needToFreeIdxStr; /* Free idxStr using sqlite3_free() if true */
int orderByConsumed; /* True if output is already ordered */
- double estimatedCost; /* Estimated cost of using this index */
+ double estimatedCost; /* Estimated cost of using this index */
+ /* Fields below are only available in SQLite 3.8.2 and later */
+ sqlite3_int64 estimatedRows; /* Estimated number of rows returned */
+ /* Fields below are only available in SQLite 3.9.0 and later */
+ int idxFlags; /* Mask of SQLITE_INDEX_SCAN_* flags */
+ /* Fields below are only available in SQLite 3.10.0 and later */
+ sqlite3_uint64 colUsed; /* Input: Mask of columns used by statement */
};
/*
+** CAPI3REF: Virtual Table Scan Flags
+*/
+#define SQLITE_INDEX_SCAN_UNIQUE 1 /* Scan visits at most 1 row */
+
+/*
** CAPI3REF: Virtual Table Constraint Operator Codes
**
** These macros defined the allowed values for the
@@ -4888,15 +5971,19 @@ struct sqlite3_index_info {
** an operator that is part of a constraint term in the wHERE clause of
** a query that uses a [virtual table].
*/
-#define SQLITE_INDEX_CONSTRAINT_EQ 2
-#define SQLITE_INDEX_CONSTRAINT_GT 4
-#define SQLITE_INDEX_CONSTRAINT_LE 8
-#define SQLITE_INDEX_CONSTRAINT_LT 16
-#define SQLITE_INDEX_CONSTRAINT_GE 32
-#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_EQ 2
+#define SQLITE_INDEX_CONSTRAINT_GT 4
+#define SQLITE_INDEX_CONSTRAINT_LE 8
+#define SQLITE_INDEX_CONSTRAINT_LT 16
+#define SQLITE_INDEX_CONSTRAINT_GE 32
+#define SQLITE_INDEX_CONSTRAINT_MATCH 64
+#define SQLITE_INDEX_CONSTRAINT_LIKE 65
+#define SQLITE_INDEX_CONSTRAINT_GLOB 66
+#define SQLITE_INDEX_CONSTRAINT_REGEXP 67
/*
** CAPI3REF: Register A Virtual Table Implementation
+** METHOD: sqlite3
**
** ^These routines are used to register a new [virtual table module] name.
** ^Module names must be registered before
@@ -4954,7 +6041,7 @@ SQLITE_API int sqlite3_create_module_v2(
*/
struct sqlite3_vtab {
const sqlite3_module *pModule; /* The module for this virtual table */
- int nRef; /* NO LONGER USED */
+ int nRef; /* Number of open cursors */
char *zErrMsg; /* Error message from sqlite3_mprintf() */
/* Virtual table implementations will typically add additional fields */
};
@@ -4993,6 +6080,7 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3*, const char *zSQL);
/*
** CAPI3REF: Overload A Function For A Virtual Table
+** METHOD: sqlite3
**
** ^(Virtual tables can provide alternative implementations of functions
** using the [xFindFunction] method of the [virtual table module].
@@ -5035,6 +6123,8 @@ typedef struct sqlite3_blob sqlite3_blob;
/*
** CAPI3REF: Open A BLOB For Incremental I/O
+** METHOD: sqlite3
+** CONSTRUCTOR: sqlite3_blob
**
** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located
** in row iRow, column zColumn, table zTable in database zDb;
@@ -5044,26 +6134,42 @@ typedef struct sqlite3_blob sqlite3_blob;
** SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
** </pre>)^
**
+** ^(Parameter zDb is not the filename that contains the database, but
+** rather the symbolic name of the database. For attached databases, this is
+** the name that appears after the AS keyword in the [ATTACH] statement.
+** For the main database file, the database name is "main". For TEMP
+** tables, the database name is "temp".)^
+**
** ^If the flags parameter is non-zero, then the BLOB is opened for read
-** and write access. ^If it is zero, the BLOB is opened for read access.
-** ^It is not possible to open a column that is part of an index or primary
-** key for writing. ^If [foreign key constraints] are enabled, it is
-** not possible to open a column that is part of a [child key] for writing.
-**
-** ^Note that the database name is not the filename that contains
-** the database but rather the symbolic name of the database that
-** appears after the AS keyword when the database is connected using [ATTACH].
-** ^For the main database file, the database name is "main".
-** ^For TEMP tables, the database name is "temp".
-**
-** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written
-** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set
-** to be a null pointer.)^
-** ^This function sets the [database connection] error code and message
-** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related
-** functions. ^Note that the *ppBlob variable is always initialized in a
-** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob
-** regardless of the success or failure of this routine.
+** and write access. ^If the flags parameter is zero, the BLOB is opened for
+** read-only access.
+**
+** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is stored
+** in *ppBlob. Otherwise an [error code] is returned and, unless the error
+** code is SQLITE_MISUSE, *ppBlob is set to NULL.)^ ^This means that, provided
+** the API is not misused, it is always safe to call [sqlite3_blob_close()]
+** on *ppBlob after this function it returns.
+**
+** This function fails with SQLITE_ERROR if any of the following are true:
+** <ul>
+** <li> ^(Database zDb does not exist)^,
+** <li> ^(Table zTable does not exist within database zDb)^,
+** <li> ^(Table zTable is a WITHOUT ROWID table)^,
+** <li> ^(Column zColumn does not exist)^,
+** <li> ^(Row iRow is not present in the table)^,
+** <li> ^(The specified column of row iRow contains a value that is not
+** a TEXT or BLOB value)^,
+** <li> ^(Column zColumn is part of an index, PRIMARY KEY or UNIQUE
+** constraint and the blob is being opened for read/write access)^,
+** <li> ^([foreign key constraints | Foreign key constraints] are enabled,
+** column zColumn is part of a [child key] definition and the blob is
+** being opened for read/write access)^.
+** </ul>
+**
+** ^Unless it returns SQLITE_MISUSE, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
+**
**
** ^(If the row that a BLOB handle points to is modified by an
** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects
@@ -5082,9 +6188,8 @@ typedef struct sqlite3_blob sqlite3_blob;
** blob.
**
** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces
-** and the built-in [zeroblob] SQL function can be used, if desired,
-** to create an empty, zero-filled blob in which to read or write using
-** this interface.
+** and the built-in [zeroblob] SQL function may be used to create a
+** zero-filled blob to read or write using the incremental-blob interface.
**
** To avoid a resource leak, every open [BLOB handle] should eventually
** be released by a call to [sqlite3_blob_close()].
@@ -5101,6 +6206,7 @@ SQLITE_API int sqlite3_blob_open(
/*
** CAPI3REF: Move a BLOB Handle to a New Row
+** METHOD: sqlite3_blob
**
** ^This function is used to move an existing blob handle so that it points
** to a different row of the same database table. ^The new row is identified
@@ -5121,34 +6227,34 @@ SQLITE_API int sqlite3_blob_open(
**
** ^This function sets the database handle error code and message.
*/
-SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
+SQLITE_API int sqlite3_blob_reopen(sqlite3_blob *, sqlite3_int64);
/*
** CAPI3REF: Close A BLOB Handle
+** DESTRUCTOR: sqlite3_blob
**
-** ^Closes an open [BLOB handle].
-**
-** ^Closing a BLOB shall cause the current transaction to commit
-** if there are no other BLOBs, no pending prepared statements, and the
-** database connection is in [autocommit mode].
-** ^If any writes were made to the BLOB, they might be held in cache
-** until the close operation if they will fit.
-**
-** ^(Closing the BLOB often forces the changes
-** out to disk and so if any I/O errors occur, they will likely occur
-** at the time when the BLOB is closed. Any errors that occur during
-** closing are reported as a non-zero return value.)^
+** ^This function closes an open [BLOB handle]. ^(The BLOB handle is closed
+** unconditionally. Even if this routine returns an error code, the
+** handle is still closed.)^
**
-** ^(The BLOB is closed unconditionally. Even if this routine returns
-** an error code, the BLOB is still closed.)^
+** ^If the blob handle being closed was opened for read-write access, and if
+** the database is in auto-commit mode and there are no other open read-write
+** blob handles or active write statements, the current transaction is
+** committed. ^If an error occurs while committing the transaction, an error
+** code is returned and the transaction rolled back.
**
-** ^Calling this routine with a null pointer (such as would be returned
-** by a failed call to [sqlite3_blob_open()]) is a harmless no-op.
+** Calling this function with an argument that is not a NULL pointer or an
+** open blob handle results in undefined behaviour. ^Calling this routine
+** with a null pointer (such as would be returned by a failed call to
+** [sqlite3_blob_open()]) is a harmless no-op. ^Otherwise, if this function
+** is passed a valid open blob handle, the values returned by the
+** sqlite3_errcode() and sqlite3_errmsg() functions are set before returning.
*/
SQLITE_API int sqlite3_blob_close(sqlite3_blob *);
/*
** CAPI3REF: Return The Size Of An Open BLOB
+** METHOD: sqlite3_blob
**
** ^Returns the size in bytes of the BLOB accessible via the
** successfully opened [BLOB handle] in its only argument. ^The
@@ -5164,6 +6270,7 @@ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *);
/*
** CAPI3REF: Read Data From A BLOB Incrementally
+** METHOD: sqlite3_blob
**
** ^(This function is used to read data from an open [BLOB handle] into a
** caller-supplied buffer. N bytes of data are copied into buffer Z
@@ -5192,22 +6299,29 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
/*
** CAPI3REF: Write Data Into A BLOB Incrementally
+** METHOD: sqlite3_blob
**
-** ^This function is used to write data into an open [BLOB handle] from a
-** caller-supplied buffer. ^N bytes of data are copied from the buffer Z
-** into the open BLOB, starting at offset iOffset.
+** ^(This function is used to write data into an open [BLOB handle] from a
+** caller-supplied buffer. N bytes of data are copied from the buffer Z
+** into the open BLOB, starting at offset iOffset.)^
+**
+** ^(On success, sqlite3_blob_write() returns SQLITE_OK.
+** Otherwise, an [error code] or an [extended error code] is returned.)^
+** ^Unless SQLITE_MISUSE is returned, this function sets the
+** [database connection] error code and message accessible via
+** [sqlite3_errcode()] and [sqlite3_errmsg()] and related functions.
**
** ^If the [BLOB handle] passed as the first argument was not opened for
** writing (the flags parameter to [sqlite3_blob_open()] was zero),
** this function returns [SQLITE_READONLY].
**
-** ^This function may only modify the contents of the BLOB; it is
+** This function may only modify the contents of the BLOB; it is
** not possible to increase the size of a BLOB using this API.
** ^If offset iOffset is less than N bytes from the end of the BLOB,
-** [SQLITE_ERROR] is returned and no data is written. ^If N is
-** less than zero [SQLITE_ERROR] is returned and no data is written.
-** The size of the BLOB (and hence the maximum value of N+iOffset)
-** can be determined using the [sqlite3_blob_bytes()] interface.
+** [SQLITE_ERROR] is returned and no data is written. The size of the
+** BLOB (and hence the maximum value of N+iOffset) can be determined
+** using the [sqlite3_blob_bytes()] interface. ^If N or iOffset are less
+** than zero [SQLITE_ERROR] is returned and no data is written.
**
** ^An attempt to write to an expired [BLOB handle] fails with an
** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred
@@ -5216,9 +6330,6 @@ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset);
** have been overwritten by the statement that expired the BLOB handle
** or by other independent statements.
**
-** ^(On success, sqlite3_blob_write() returns SQLITE_OK.
-** Otherwise, an [error code] or an [extended error code] is returned.)^
-**
** This routine only works on a [BLOB handle] which has been created
** by a prior successful call to [sqlite3_blob_open()] and which has not
** been closed by [sqlite3_blob_close()]. Passing any other pointer in
@@ -5271,46 +6382,51 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** The SQLite source code contains multiple implementations
** of these mutex routines. An appropriate implementation
-** is selected automatically at compile-time. ^(The following
+** is selected automatically at compile-time. The following
** implementations are available in the SQLite core:
**
** <ul>
-** <li> SQLITE_MUTEX_OS2
-** <li> SQLITE_MUTEX_PTHREAD
+** <li> SQLITE_MUTEX_PTHREADS
** <li> SQLITE_MUTEX_W32
** <li> SQLITE_MUTEX_NOOP
-** </ul>)^
+** </ul>
**
-** ^The SQLITE_MUTEX_NOOP implementation is a set of routines
+** The SQLITE_MUTEX_NOOP implementation is a set of routines
** that does no real locking and is appropriate for use in
-** a single-threaded application. ^The SQLITE_MUTEX_OS2,
-** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations
-** are appropriate for use on OS/2, Unix, and Windows.
+** a single-threaded application. The SQLITE_MUTEX_PTHREADS and
+** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix
+** and Windows.
**
-** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
+** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
** implementation is included with the library. In this case the
** application must supply a custom mutex implementation using the
** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function
** before calling sqlite3_initialize() or any other public sqlite3_
-** function that calls sqlite3_initialize().)^
+** function that calls sqlite3_initialize().
**
** ^The sqlite3_mutex_alloc() routine allocates a new
-** mutex and returns a pointer to it. ^If it returns NULL
-** that means that a mutex could not be allocated. ^SQLite
-** will unwind its stack and return an error. ^(The argument
-** to sqlite3_mutex_alloc() is one of these integer constants:
+** mutex and returns a pointer to it. ^The sqlite3_mutex_alloc()
+** routine returns NULL if it is unable to allocate the requested
+** mutex. The argument to sqlite3_mutex_alloc() must one of these
+** integer constants:
**
** <ul>
** <li> SQLITE_MUTEX_FAST
** <li> SQLITE_MUTEX_RECURSIVE
** <li> SQLITE_MUTEX_STATIC_MASTER
** <li> SQLITE_MUTEX_STATIC_MEM
-** <li> SQLITE_MUTEX_STATIC_MEM2
+** <li> SQLITE_MUTEX_STATIC_OPEN
** <li> SQLITE_MUTEX_STATIC_PRNG
** <li> SQLITE_MUTEX_STATIC_LRU
-** <li> SQLITE_MUTEX_STATIC_LRU2
-** </ul>)^
+** <li> SQLITE_MUTEX_STATIC_PMEM
+** <li> SQLITE_MUTEX_STATIC_APP1
+** <li> SQLITE_MUTEX_STATIC_APP2
+** <li> SQLITE_MUTEX_STATIC_APP3
+** <li> SQLITE_MUTEX_STATIC_VFS1
+** <li> SQLITE_MUTEX_STATIC_VFS2
+** <li> SQLITE_MUTEX_STATIC_VFS3
+** </ul>
**
** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE)
** cause sqlite3_mutex_alloc() to create
@@ -5318,14 +6434,14 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
** The mutex implementation does not need to make a distinction
** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
-** not want to. ^SQLite will only request a recursive mutex in
-** cases where it really needs one. ^If a faster non-recursive mutex
+** not want to. SQLite will only request a recursive mutex in
+** cases where it really needs one. If a faster non-recursive mutex
** implementation is available on the host platform, the mutex subsystem
** might return such a mutex in response to SQLITE_MUTEX_FAST.
**
** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other
** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return
-** a pointer to a static preexisting mutex. ^Six static mutexes are
+** a pointer to a static preexisting mutex. ^Nine static mutexes are
** used by the current version of SQLite. Future versions of SQLite
** may add additional static mutexes. Static mutexes are for internal
** use by SQLite only. Applications that use SQLite mutexes should
@@ -5334,16 +6450,13 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
**
** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. ^But for the static
+** returns a different mutex on every call. ^For the static
** mutex types, the same mutex is returned on every call that has
** the same type number.
**
** ^The sqlite3_mutex_free() routine deallocates a previously
-** allocated dynamic mutex. ^SQLite is careful to deallocate every
-** dynamic mutex that it allocates. The dynamic mutexes must not be in
-** use when they are deallocated. Attempting to deallocate a static
-** mutex results in undefined behavior. ^SQLite never deallocates
-** a static mutex.
+** allocated dynamic mutex. Attempting to deallocate a static
+** mutex results in undefined behavior.
**
** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
** to enter a mutex. ^If another thread is already within the mutex,
@@ -5351,23 +6464,21 @@ SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*);
** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK]
** upon successful entry. ^(Mutexes created using
** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread.
-** In such cases the,
+** In such cases, the
** mutex must be exited an equal number of times before another thread
-** can enter.)^ ^(If the same thread tries to enter any other
-** kind of mutex more than once, the behavior is undefined.
-** SQLite will never exhibit
-** such behavior in its own use of mutexes.)^
+** can enter.)^ If the same thread tries to enter any mutex other
+** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined.
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try()
-** will always return SQLITE_BUSY. The SQLite core only ever uses
-** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^
+** will always return SQLITE_BUSY. The SQLite core only ever uses
+** sqlite3_mutex_try() as an optimization so this is acceptable
+** behavior.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
-** previously entered by the same thread. ^(The behavior
+** previously entered by the same thread. The behavior
** is undefined if the mutex is not currently entered by the
-** calling thread or is not currently allocated. SQLite will
-** never do either.)^
+** calling thread or is not currently allocated.
**
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
** sqlite3_mutex_leave() is a NULL pointer, then all three routines
@@ -5388,9 +6499,9 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*);
** used to allocate and use mutexes.
**
** Usually, the default mutex implementations provided by SQLite are
-** sufficient, however the user has the option of substituting a custom
+** sufficient, however the application has the option of substituting a custom
** implementation for specialized deployments or systems for which SQLite
-** does not provide a suitable implementation. In this case, the user
+** does not provide a suitable implementation. In this case, the application
** creates and populates an instance of this structure to pass
** to sqlite3_config() along with the [SQLITE_CONFIG_MUTEX] option.
** Additionally, an instance of this structure can be used as an
@@ -5431,13 +6542,13 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*);
** (i.e. it is acceptable to provide an implementation that segfaults if
** it is passed a NULL pointer).
**
-** The xMutexInit() method must be threadsafe. ^It must be harmless to
+** The xMutexInit() method must be threadsafe. It must be harmless to
** invoke xMutexInit() multiple times within the same process and without
** intervening calls to xMutexEnd(). Second and subsequent calls to
** xMutexInit() must be no-ops.
**
-** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
-** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory
+** xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()]
+** and its associates). Similarly, xMutexAlloc() must not use SQLite memory
** allocation for a static mutex. ^However xMutexAlloc() may use SQLite
** memory allocation for a fast or recursive mutex.
**
@@ -5463,29 +6574,29 @@ struct sqlite3_mutex_methods {
** CAPI3REF: Mutex Verification Routines
**
** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines
-** are intended for use inside assert() statements. ^The SQLite core
+** are intended for use inside assert() statements. The SQLite core
** never uses these routines except inside an assert() and applications
-** are advised to follow the lead of the core. ^The SQLite core only
+** are advised to follow the lead of the core. The SQLite core only
** provides implementations for these routines when it is compiled
-** with the SQLITE_DEBUG flag. ^External mutex implementations
+** with the SQLITE_DEBUG flag. External mutex implementations
** are only required to provide these routines if SQLITE_DEBUG is
** defined and if NDEBUG is not defined.
**
-** ^These routines should return true if the mutex in their argument
+** These routines should return true if the mutex in their argument
** is held or not held, respectively, by the calling thread.
**
-** ^The implementation is not required to provided versions of these
+** The implementation is not required to provide versions of these
** routines that actually work. If the implementation does not provide working
** versions of these routines, it should at least provide stubs that always
** return true so that one does not get spurious assertion failures.
**
-** ^If the argument to sqlite3_mutex_held() is a NULL pointer then
+** If the argument to sqlite3_mutex_held() is a NULL pointer then
** the routine should return 1. This seems counter-intuitive since
** clearly the mutex cannot be held if it does not exist. But
** the reason the mutex does not exist is because the build is not
** using mutexes. And we do not want the assert() containing the
** call to sqlite3_mutex_held() to fail, so a non-zero return is
-** the appropriate thing to do. ^The sqlite3_mutex_notheld()
+** the appropriate thing to do. The sqlite3_mutex_notheld()
** interface should also return 1 when given a NULL pointer.
*/
#ifndef NDEBUG
@@ -5509,13 +6620,20 @@ SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_STATIC_MEM 3 /* sqlite3_malloc() */
#define SQLITE_MUTEX_STATIC_MEM2 4 /* NOT USED */
#define SQLITE_MUTEX_STATIC_OPEN 4 /* sqlite3BtreeOpen() */
-#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_random() */
+#define SQLITE_MUTEX_STATIC_PRNG 5 /* sqlite3_randomness() */
#define SQLITE_MUTEX_STATIC_LRU 6 /* lru page list */
#define SQLITE_MUTEX_STATIC_LRU2 7 /* NOT USED */
#define SQLITE_MUTEX_STATIC_PMEM 7 /* sqlite3PageMalloc() */
+#define SQLITE_MUTEX_STATIC_APP1 8 /* For use by application */
+#define SQLITE_MUTEX_STATIC_APP2 9 /* For use by application */
+#define SQLITE_MUTEX_STATIC_APP3 10 /* For use by application */
+#define SQLITE_MUTEX_STATIC_VFS1 11 /* For use by built-in VFS */
+#define SQLITE_MUTEX_STATIC_VFS2 12 /* For use by extension VFS */
+#define SQLITE_MUTEX_STATIC_VFS3 13 /* For use by application VFS */
/*
** CAPI3REF: Retrieve the mutex for a database connection
+** METHOD: sqlite3
**
** ^This interface returns a pointer the [sqlite3_mutex] object that
** serializes access to the [database connection] given in the argument
@@ -5527,6 +6645,7 @@ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*);
/*
** CAPI3REF: Low-Level Control Of Database Files
+** METHOD: sqlite3
**
** ^The [sqlite3_file_control()] interface makes a direct call to the
** xFileControl method for the [sqlite3_io_methods] object associated
@@ -5602,15 +6721,22 @@ SQLITE_API int sqlite3_test_control(int op, ...);
#define SQLITE_TESTCTRL_RESERVE 14
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
#define SQLITE_TESTCTRL_ISKEYWORD 16
-#define SQLITE_TESTCTRL_PGHDRSZ 17
-#define SQLITE_TESTCTRL_SCRATCHMALLOC 18
-#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19
-#define SQLITE_TESTCTRL_LAST 19
+#define SQLITE_TESTCTRL_SCRATCHMALLOC 17
+#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
+#define SQLITE_TESTCTRL_EXPLAIN_STMT 19 /* NOT USED */
+#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD 19
+#define SQLITE_TESTCTRL_NEVER_CORRUPT 20
+#define SQLITE_TESTCTRL_VDBE_COVERAGE 21
+#define SQLITE_TESTCTRL_BYTEORDER 22
+#define SQLITE_TESTCTRL_ISINIT 23
+#define SQLITE_TESTCTRL_SORTER_MMAP 24
+#define SQLITE_TESTCTRL_IMPOSTER 25
+#define SQLITE_TESTCTRL_LAST 25
/*
** CAPI3REF: SQLite Runtime Status
**
-** ^This interface is used to retrieve runtime status information
+** ^These interfaces are used to retrieve runtime status information
** about the performance of SQLite, and optionally to reset various
** highwater marks. ^The first argument is an integer code for
** the specific parameter to measure. ^(Recognized integer codes
@@ -5624,19 +6750,22 @@ SQLITE_API int sqlite3_test_control(int op, ...);
** ^(Other parameters record only the highwater mark and not the current
** value. For these latter parameters nothing is written into *pCurrent.)^
**
-** ^The sqlite3_status() routine returns SQLITE_OK on success and a
-** non-zero [error code] on failure.
+** ^The sqlite3_status() and sqlite3_status64() routines return
+** SQLITE_OK on success and a non-zero [error code] on failure.
**
-** This routine is threadsafe but is not atomic. This routine can be
-** called while other threads are running the same or different SQLite
-** interfaces. However the values returned in *pCurrent and
-** *pHighwater reflect the status of SQLite at different points in time
-** and it is possible that another thread might change the parameter
-** in between the times when *pCurrent and *pHighwater are written.
+** If either the current value or the highwater mark is too large to
+** be represented by a 32-bit integer, then the values returned by
+** sqlite3_status() are undefined.
**
** See also: [sqlite3_db_status()]
*/
SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag);
+SQLITE_API int sqlite3_status64(
+ int op,
+ sqlite3_int64 *pCurrent,
+ sqlite3_int64 *pHighwater,
+ int resetFlag
+);
/*
@@ -5715,7 +6844,8 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF
** The value written into the *pCurrent parameter is undefined.</dd>)^
**
** [[SQLITE_STATUS_PARSER_STACK]] ^(<dt>SQLITE_STATUS_PARSER_STACK</dt>
-** <dd>This parameter records the deepest parser stack. It is only
+** <dd>The *pHighwater parameter records the deepest parser stack.
+** The *pCurrent value is undefined. The *pHighwater value is only
** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].</dd>)^
** </dl>
**
@@ -5734,6 +6864,7 @@ SQLITE_API int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetF
/*
** CAPI3REF: Database Connection Status
+** METHOD: sqlite3
**
** ^This interface is used to retrieve runtime status information
** about a single [database connection]. ^The first argument is the
@@ -5796,12 +6927,24 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** the current value is always zero.)^
**
** [[SQLITE_DBSTATUS_CACHE_USED]] ^(<dt>SQLITE_DBSTATUS_CACHE_USED</dt>
-** <dd>This parameter returns the approximate number of of bytes of heap
+** <dd>This parameter returns the approximate number of bytes of heap
** memory used by all pager caches associated with the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_USED is always 0.
**
+** [[SQLITE_DBSTATUS_CACHE_USED_SHARED]]
+** ^(<dt>SQLITE_DBSTATUS_CACHE_USED_SHARED</dt>
+** <dd>This parameter is similar to DBSTATUS_CACHE_USED, except that if a
+** pager cache is shared between two or more connections the bytes of heap
+** memory used by that pager cache is divided evenly between the attached
+** connections.)^ In other words, if none of the pager caches associated
+** with the database connection are shared, this request returns the same
+** value as DBSTATUS_CACHE_USED. Or, if one or more or the pager caches are
+** shared, the value returned by this call will be smaller than that returned
+** by DBSTATUS_CACHE_USED. ^The highwater mark associated with
+** SQLITE_DBSTATUS_CACHE_USED_SHARED is always 0.
+**
** [[SQLITE_DBSTATUS_SCHEMA_USED]] ^(<dt>SQLITE_DBSTATUS_SCHEMA_USED</dt>
-** <dd>This parameter returns the approximate number of of bytes of heap
+** <dd>This parameter returns the approximate number of bytes of heap
** memory used to store the schema for all databases associated
** with the connection - main, temp, and any [ATTACH]-ed databases.)^
** ^The full amount of memory used by the schemas is reported, even if the
@@ -5810,11 +6953,40 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
** ^The highwater mark associated with SQLITE_DBSTATUS_SCHEMA_USED is always 0.
**
** [[SQLITE_DBSTATUS_STMT_USED]] ^(<dt>SQLITE_DBSTATUS_STMT_USED</dt>
-** <dd>This parameter returns the approximate number of of bytes of heap
+** <dd>This parameter returns the approximate number of bytes of heap
** and lookaside memory used by all prepared statements associated with
** the database connection.)^
** ^The highwater mark associated with SQLITE_DBSTATUS_STMT_USED is always 0.
** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_HIT]] ^(<dt>SQLITE_DBSTATUS_CACHE_HIT</dt>
+** <dd>This parameter returns the number of pager cache hits that have
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_HIT
+** is always 0.
+** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_MISS]] ^(<dt>SQLITE_DBSTATUS_CACHE_MISS</dt>
+** <dd>This parameter returns the number of pager cache misses that have
+** occurred.)^ ^The highwater mark associated with SQLITE_DBSTATUS_CACHE_MISS
+** is always 0.
+** </dd>
+**
+** [[SQLITE_DBSTATUS_CACHE_WRITE]] ^(<dt>SQLITE_DBSTATUS_CACHE_WRITE</dt>
+** <dd>This parameter returns the number of dirty cache entries that have
+** been written to disk. Specifically, the number of pages written to the
+** wal file in wal mode databases, or the number of pages written to the
+** database file in rollback mode databases. Any pages written as part of
+** transaction rollback or database recovery operations are not included.
+** If an IO or other error occurs while writing a page to disk, the effect
+** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The
+** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0.
+** </dd>
+**
+** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt>
+** <dd>This parameter returns zero for the current value if and only if
+** all foreign key constraints (deferred or immediate) have been
+** resolved.)^ ^The highwater mark is always 0.
+** </dd>
** </dl>
*/
#define SQLITE_DBSTATUS_LOOKASIDE_USED 0
@@ -5824,11 +6996,17 @@ SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int r
#define SQLITE_DBSTATUS_LOOKASIDE_HIT 4
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5
#define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6
-#define SQLITE_DBSTATUS_MAX 6 /* Largest defined DBSTATUS */
+#define SQLITE_DBSTATUS_CACHE_HIT 7
+#define SQLITE_DBSTATUS_CACHE_MISS 8
+#define SQLITE_DBSTATUS_CACHE_WRITE 9
+#define SQLITE_DBSTATUS_DEFERRED_FKS 10
+#define SQLITE_DBSTATUS_CACHE_USED_SHARED 11
+#define SQLITE_DBSTATUS_MAX 11 /* Largest defined DBSTATUS */
/*
** CAPI3REF: Prepared Statement Status
+** METHOD: sqlite3_stmt
**
** ^(Each prepared statement maintains various
** [SQLITE_STMTSTATUS counters] that measure the number
@@ -5879,11 +7057,20 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** improvement performance by adding permanent indices that do not
** need to be reinitialized each time the statement is run.</dd>
**
+** [[SQLITE_STMTSTATUS_VM_STEP]] <dt>SQLITE_STMTSTATUS_VM_STEP</dt>
+** <dd>^This is the number of virtual machine operations executed
+** by the prepared statement if that number is less than or equal
+** to 2147483647. The number of virtual machine operations can be
+** used as a proxy for the total work done by the prepared statement.
+** If the number of virtual machine operations exceeds 2147483647
+** then the value returned by this statement status code is undefined.
+** </dd>
** </dl>
*/
#define SQLITE_STMTSTATUS_FULLSCAN_STEP 1
#define SQLITE_STMTSTATUS_SORT 2
#define SQLITE_STMTSTATUS_AUTOINDEX 3
+#define SQLITE_STMTSTATUS_VM_STEP 4
/*
** CAPI3REF: Custom Page Cache Object
@@ -5894,17 +7081,33 @@ SQLITE_API int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg);
** sqlite3_pcache object except by holding and passing pointers
** to the object.
**
-** See [sqlite3_pcache_methods] for additional information.
+** See [sqlite3_pcache_methods2] for additional information.
*/
typedef struct sqlite3_pcache sqlite3_pcache;
/*
+** CAPI3REF: Custom Page Cache Object
+**
+** The sqlite3_pcache_page object represents a single page in the
+** page cache. The page cache will allocate instances of this
+** object. Various methods of the page cache use pointers to instances
+** of this object as parameters or as their return value.
+**
+** See [sqlite3_pcache_methods2] for additional information.
+*/
+typedef struct sqlite3_pcache_page sqlite3_pcache_page;
+struct sqlite3_pcache_page {
+ void *pBuf; /* The content of the page */
+ void *pExtra; /* Extra information associated with the page */
+};
+
+/*
** CAPI3REF: Application Defined Page Cache.
** KEYWORDS: {page cache}
**
-** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can
+** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE2], ...) interface can
** register an alternative page cache implementation by passing in an
-** instance of the sqlite3_pcache_methods structure.)^
+** instance of the sqlite3_pcache_methods2 structure.)^
** In many applications, most of the heap memory allocated by
** SQLite is used for the page cache.
** By implementing a
@@ -5918,7 +7121,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** extreme measure that is only needed by the most demanding applications.
** The built-in page cache is recommended for most uses.
**
-** ^(The contents of the sqlite3_pcache_methods structure are copied to an
+** ^(The contents of the sqlite3_pcache_methods2 structure are copied to an
** internal buffer by SQLite within the call to [sqlite3_config]. Hence
** the application may discard the parameter after the call to
** [sqlite3_config()] returns.)^
@@ -5927,7 +7130,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** ^(The xInit() method is called once for each effective
** call to [sqlite3_initialize()])^
** (usually only once during the lifetime of the process). ^(The xInit()
-** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^
+** method is passed a copy of the sqlite3_pcache_methods2.pArg value.)^
** The intent of the xInit() method is to set up global data structures
** required by the custom page cache implementation.
** ^(If the xInit() method is NULL, then the
@@ -5954,17 +7157,15 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** SQLite will typically create one cache instance for each open database file,
** though this is not guaranteed. ^The
** first parameter, szPage, is the size in bytes of the pages that must
-** be allocated by the cache. ^szPage will not be a power of two. ^szPage
-** will the page size of the database file that is to be cached plus an
-** increment (here called "R") of less than 250. SQLite will use the
-** extra R bytes on each page to store metadata about the underlying
-** database page on disk. The value of R depends
+** be allocated by the cache. ^szPage will always a power of two. ^The
+** second parameter szExtra is a number of bytes of extra storage
+** associated with each page cache entry. ^The szExtra parameter will
+** a number less than 250. SQLite will use the
+** extra szExtra bytes on each page to store metadata about the underlying
+** database page on disk. The value passed into szExtra depends
** on the SQLite version, the target platform, and how SQLite was compiled.
-** ^(R is constant for a particular build of SQLite. Except, there are two
-** distinct values of R when SQLite is compiled with the proprietary
-** ZIPVFS extension.)^ ^The second argument to
-** xCreate(), bPurgeable, is true if the cache being created will
-** be used to cache database pages of a file stored on disk, or
+** ^The third argument to xCreate(), bPurgeable, is true if the cache being
+** created will be used to cache database pages of a file stored on disk, or
** false if it is used for an in-memory database. The cache implementation
** does not have to do anything special based with the value of bPurgeable;
** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will
@@ -5988,11 +7189,16 @@ typedef struct sqlite3_pcache sqlite3_pcache;
**
** [[the xFetch() page cache methods]]
** The xFetch() method locates a page in the cache and returns a pointer to
-** the page, or a NULL pointer.
-** A "page", in this context, means a buffer of szPage bytes aligned at an
-** 8-byte boundary. The page to be fetched is determined by the key. ^The
-** minimum key value is 1. After it has been retrieved using xFetch, the page
-** is considered to be "pinned".
+** an sqlite3_pcache_page object associated with that page, or a NULL pointer.
+** The pBuf element of the returned sqlite3_pcache_page object will be a
+** pointer to a buffer of szPage bytes used to store the content of a
+** single database page. The pExtra element of sqlite3_pcache_page will be
+** a pointer to the szExtra bytes of extra storage that SQLite has requested
+** for each entry in the page cache.
+**
+** The page to be fetched is determined by the key. ^The minimum key value
+** is 1. After it has been retrieved using xFetch, the page is considered
+** to be "pinned".
**
** If the requested page is already in the page cache, then the page cache
** implementation must return a pointer to the page buffer with its content
@@ -6001,7 +7207,7 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** parameter to help it determined what action to take:
**
** <table border=1 width=85% align=center>
-** <tr><th> createFlag <th> Behaviour when page is not already in cache
+** <tr><th> createFlag <th> Behavior when page is not already in cache
** <tr><td> 0 <td> Do not allocate a new page. Return NULL.
** <tr><td> 1 <td> Allocate a new page if it easy and convenient to do so.
** Otherwise return NULL.
@@ -6045,8 +7251,37 @@ typedef struct sqlite3_pcache sqlite3_pcache;
** ^The xDestroy() method is used to delete a cache allocated by xCreate().
** All resources associated with the specified cache should be freed. ^After
** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*]
-** handle invalid, and will not use it with any other sqlite3_pcache_methods
+** handle invalid, and will not use it with any other sqlite3_pcache_methods2
** functions.
+**
+** [[the xShrink() page cache method]]
+** ^SQLite invokes the xShrink() method when it wants the page cache to
+** free up as much of heap memory as possible. The page cache implementation
+** is not obligated to free any memory, but well-behaved implementations should
+** do their best.
+*/
+typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;
+struct sqlite3_pcache_methods2 {
+ int iVersion;
+ void *pArg;
+ int (*xInit)(void*);
+ void (*xShutdown)(void*);
+ sqlite3_pcache *(*xCreate)(int szPage, int szExtra, int bPurgeable);
+ void (*xCachesize)(sqlite3_pcache*, int nCachesize);
+ int (*xPagecount)(sqlite3_pcache*);
+ sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
+ void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
+ void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*,
+ unsigned oldKey, unsigned newKey);
+ void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
+ void (*xDestroy)(sqlite3_pcache*);
+ void (*xShrink)(sqlite3_pcache*);
+};
+
+/*
+** This is the obsolete pcache_methods object that has now been replaced
+** by sqlite3_pcache_methods2. This object is not used by SQLite. It is
+** retained in the header file for backwards compatibility only.
*/
typedef struct sqlite3_pcache_methods sqlite3_pcache_methods;
struct sqlite3_pcache_methods {
@@ -6063,6 +7298,7 @@ struct sqlite3_pcache_methods {
void (*xDestroy)(sqlite3_pcache*);
};
+
/*
** CAPI3REF: Online Backup Object
**
@@ -6119,6 +7355,10 @@ typedef struct sqlite3_backup sqlite3_backup;
** must be different or else sqlite3_backup_init(D,N,S,M) will fail with
** an error.
**
+** ^A call to sqlite3_backup_init() will fail, returning NULL, if
+** there is already a read or read-write transaction open on the
+** destination database.
+**
** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is
** returned and an error code and error message are stored in the
** destination [database connection] D.
@@ -6211,20 +7451,20 @@ typedef struct sqlite3_backup sqlite3_backup;
** is not a permanent error and does not affect the return value of
** sqlite3_backup_finish().
**
-** [[sqlite3_backup__remaining()]] [[sqlite3_backup_pagecount()]]
+** [[sqlite3_backup_remaining()]] [[sqlite3_backup_pagecount()]]
** <b>sqlite3_backup_remaining() and sqlite3_backup_pagecount()</b>
**
-** ^Each call to sqlite3_backup_step() sets two values inside
-** the [sqlite3_backup] object: the number of pages still to be backed
-** up and the total number of pages in the source database file.
-** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces
-** retrieve these two values, respectively.
-**
-** ^The values returned by these functions are only updated by
-** sqlite3_backup_step(). ^If the source database is modified during a backup
-** operation, then the values are not updated to account for any extra
-** pages that need to be updated or the size of the source database file
-** changing.
+** ^The sqlite3_backup_remaining() routine returns the number of pages still
+** to be backed up at the conclusion of the most recent sqlite3_backup_step().
+** ^The sqlite3_backup_pagecount() routine returns the total number of pages
+** in the source database at the conclusion of the most recent
+** sqlite3_backup_step().
+** ^(The values returned by these functions are only updated by
+** sqlite3_backup_step(). If the source database is modified in a way that
+** changes the size of the source database or the number of pages remaining,
+** those changes are not reflected in the output of sqlite3_backup_pagecount()
+** and sqlite3_backup_remaining() until after the next
+** sqlite3_backup_step().)^
**
** <b>Concurrent Usage of Database Handles</b>
**
@@ -6270,6 +7510,7 @@ SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p);
/*
** CAPI3REF: Unlock Notification
+** METHOD: sqlite3
**
** ^When running in shared-cache mode, a database operation may fail with
** an [SQLITE_LOCKED] error if the required locks on the shared-cache or
@@ -6392,17 +7633,58 @@ SQLITE_API int sqlite3_unlock_notify(
/*
** CAPI3REF: String Comparison
**
-** ^The [sqlite3_strnicmp()] API allows applications and extensions to
-** compare the contents of two buffers containing UTF-8 strings in a
-** case-independent fashion, using the same definition of case independence
-** that SQLite uses internally when comparing identifiers.
+** ^The [sqlite3_stricmp()] and [sqlite3_strnicmp()] APIs allow applications
+** and extensions to compare the contents of two buffers containing UTF-8
+** strings in a case-independent fashion, using the same definition of "case
+** independence" that SQLite uses internally when comparing identifiers.
*/
+SQLITE_API int sqlite3_stricmp(const char *, const char *);
SQLITE_API int sqlite3_strnicmp(const char *, const char *, int);
/*
+** CAPI3REF: String Globbing
+*
+** ^The [sqlite3_strglob(P,X)] interface returns zero if and only if
+** string X matches the [GLOB] pattern P.
+** ^The definition of [GLOB] pattern matching used in
+** [sqlite3_strglob(P,X)] is the same as for the "X GLOB P" operator in the
+** SQL dialect understood by SQLite. ^The [sqlite3_strglob(P,X)] function
+** is case sensitive.
+**
+** Note that this routine returns zero on a match and non-zero if the strings
+** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
+**
+** See also: [sqlite3_strlike()].
+*/
+SQLITE_API int sqlite3_strglob(const char *zGlob, const char *zStr);
+
+/*
+** CAPI3REF: String LIKE Matching
+*
+** ^The [sqlite3_strlike(P,X,E)] interface returns zero if and only if
+** string X matches the [LIKE] pattern P with escape character E.
+** ^The definition of [LIKE] pattern matching used in
+** [sqlite3_strlike(P,X,E)] is the same as for the "X LIKE P ESCAPE E"
+** operator in the SQL dialect understood by SQLite. ^For "X LIKE P" without
+** the ESCAPE clause, set the E parameter of [sqlite3_strlike(P,X,E)] to 0.
+** ^As with the LIKE operator, the [sqlite3_strlike(P,X,E)] function is case
+** insensitive - equivalent upper and lower case ASCII characters match
+** one another.
+**
+** ^The [sqlite3_strlike(P,X,E)] function matches Unicode characters, though
+** only ASCII characters are case folded.
+**
+** Note that this routine returns zero on a match and non-zero if the strings
+** do not match, the same as [sqlite3_stricmp()] and [sqlite3_strnicmp()].
+**
+** See also: [sqlite3_strglob()].
+*/
+SQLITE_API int sqlite3_strlike(const char *zGlob, const char *zStr, unsigned int cEsc);
+
+/*
** CAPI3REF: Error Logging Interface
**
-** ^The [sqlite3_log()] interface writes a message into the error log
+** ^The [sqlite3_log()] interface writes a message into the [error log]
** established by the [SQLITE_CONFIG_LOG] option to [sqlite3_config()].
** ^If logging is enabled, the zFormat string and subsequent arguments are
** used with [sqlite3_snprintf()] to generate the final output string.
@@ -6424,14 +7706,13 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
/*
** CAPI3REF: Write-Ahead Log Commit Hook
+** METHOD: sqlite3
**
** ^The [sqlite3_wal_hook()] function is used to register a callback that
-** will be invoked each time a database connection commits data to a
-** [write-ahead log] (i.e. whenever a transaction is committed in
-** [journal_mode | journal_mode=WAL mode]).
+** is invoked each time data is committed to a database in wal mode.
**
-** ^The callback is invoked by SQLite after the commit has taken place and
-** the associated write-lock on the database released, so the implementation
+** ^(The callback is invoked by SQLite after the commit has taken place and
+** the associated write-lock on the database released)^, so the implementation
** may read, write or [checkpoint] the database as required.
**
** ^The first parameter passed to the callback function when it is invoked
@@ -6455,7 +7736,7 @@ SQLITE_API void sqlite3_log(int iErrCode, const char *zFormat, ...);
** previously registered write-ahead log callback. ^Note that the
** [sqlite3_wal_autocheckpoint()] interface and the
** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and will
-** those overwrite any prior [sqlite3_wal_hook()] settings.
+** overwrite any prior [sqlite3_wal_hook()] settings.
*/
SQLITE_API void *sqlite3_wal_hook(
sqlite3*,
@@ -6465,6 +7746,7 @@ SQLITE_API void *sqlite3_wal_hook(
/*
** CAPI3REF: Configure an auto-checkpoint
+** METHOD: sqlite3
**
** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around
** [sqlite3_wal_hook()] that causes any database on [database connection] D
@@ -6482,6 +7764,9 @@ SQLITE_API void *sqlite3_wal_hook(
** ^The [wal_autocheckpoint pragma] can be used to invoke this interface
** from SQL.
**
+** ^Checkpoints initiated by this mechanism are
+** [sqlite3_wal_checkpoint_v2|PASSIVE].
+**
** ^Every new [database connection] defaults to having the auto-checkpoint
** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT]
** pages. The use of this interface
@@ -6492,91 +7777,117 @@ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
/*
** CAPI3REF: Checkpoint a database
+** METHOD: sqlite3
**
-** ^The [sqlite3_wal_checkpoint(D,X)] interface causes database named X
-** on [database connection] D to be [checkpointed]. ^If X is NULL or an
-** empty string, then a checkpoint is run on all databases of
-** connection D. ^If the database connection D is not in
-** [WAL | write-ahead log mode] then this interface is a harmless no-op.
+** ^(The sqlite3_wal_checkpoint(D,X) is equivalent to
+** [sqlite3_wal_checkpoint_v2](D,X,[SQLITE_CHECKPOINT_PASSIVE],0,0).)^
**
-** ^The [wal_checkpoint pragma] can be used to invoke this interface
-** from SQL. ^The [sqlite3_wal_autocheckpoint()] interface and the
-** [wal_autocheckpoint pragma] can be used to cause this interface to be
-** run whenever the WAL reaches a certain size threshold.
+** In brief, sqlite3_wal_checkpoint(D,X) causes the content in the
+** [write-ahead log] for database X on [database connection] D to be
+** transferred into the database file and for the write-ahead log to
+** be reset. See the [checkpointing] documentation for addition
+** information.
**
-** See also: [sqlite3_wal_checkpoint_v2()]
+** This interface used to be the only way to cause a checkpoint to
+** occur. But then the newer and more powerful [sqlite3_wal_checkpoint_v2()]
+** interface was added. This interface is retained for backwards
+** compatibility and as a convenience for applications that need to manually
+** start a callback but which do not need the full power (and corresponding
+** complication) of [sqlite3_wal_checkpoint_v2()].
*/
SQLITE_API int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
/*
** CAPI3REF: Checkpoint a database
+** METHOD: sqlite3
**
-** Run a checkpoint operation on WAL database zDb attached to database
-** handle db. The specific operation is determined by the value of the
-** eMode parameter:
+** ^(The sqlite3_wal_checkpoint_v2(D,X,M,L,C) interface runs a checkpoint
+** operation on database X of [database connection] D in mode M. Status
+** information is written back into integers pointed to by L and C.)^
+** ^(The M parameter must be a valid [checkpoint mode]:)^
**
** <dl>
** <dt>SQLITE_CHECKPOINT_PASSIVE<dd>
-** Checkpoint as many frames as possible without waiting for any database
-** readers or writers to finish. Sync the db file if all frames in the log
-** are checkpointed. This mode is the same as calling
-** sqlite3_wal_checkpoint(). The busy-handler callback is never invoked.
+** ^Checkpoint as many frames as possible without waiting for any database
+** readers or writers to finish, then sync the database file if all frames
+** in the log were checkpointed. ^The [busy-handler callback]
+** is never invoked in the SQLITE_CHECKPOINT_PASSIVE mode.
+** ^On the other hand, passive mode might leave the checkpoint unfinished
+** if there are concurrent readers or writers.
**
** <dt>SQLITE_CHECKPOINT_FULL<dd>
-** This mode blocks (calls the busy-handler callback) until there is no
+** ^This mode blocks (it invokes the
+** [sqlite3_busy_handler|busy-handler callback]) until there is no
** database writer and all readers are reading from the most recent database
-** snapshot. It then checkpoints all frames in the log file and syncs the
-** database file. This call blocks database writers while it is running,
-** but not database readers.
+** snapshot. ^It then checkpoints all frames in the log file and syncs the
+** database file. ^This mode blocks new database writers while it is pending,
+** but new database readers are allowed to continue unimpeded.
**
** <dt>SQLITE_CHECKPOINT_RESTART<dd>
-** This mode works the same way as SQLITE_CHECKPOINT_FULL, except after
-** checkpointing the log file it blocks (calls the busy-handler callback)
-** until all readers are reading from the database file only. This ensures
-** that the next client to write to the database file restarts the log file
-** from the beginning. This call blocks database writers while it is running,
-** but not database readers.
+** ^This mode works the same way as SQLITE_CHECKPOINT_FULL with the addition
+** that after checkpointing the log file it blocks (calls the
+** [busy-handler callback])
+** until all readers are reading from the database file only. ^This ensures
+** that the next writer will restart the log file from the beginning.
+** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new
+** database writer attempts while it is pending, but does not impede readers.
+**
+** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd>
+** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the
+** addition that it also truncates the log file to zero bytes just prior
+** to a successful return.
** </dl>
**
-** If pnLog is not NULL, then *pnLog is set to the total number of frames in
-** the log file before returning. If pnCkpt is not NULL, then *pnCkpt is set to
-** the total number of checkpointed frames (including any that were already
-** checkpointed when this function is called). *pnLog and *pnCkpt may be
-** populated even if sqlite3_wal_checkpoint_v2() returns other than SQLITE_OK.
-** If no values are available because of an error, they are both set to -1
-** before returning to communicate this to the caller.
-**
-** All calls obtain an exclusive "checkpoint" lock on the database file. If
+** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in
+** the log file or to -1 if the checkpoint could not run because
+** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not
+** NULL,then *pnCkpt is set to the total number of checkpointed frames in the
+** log file (including any that were already checkpointed before the function
+** was called) or to -1 if the checkpoint could not run due to an error or
+** because the database is not in WAL mode. ^Note that upon successful
+** completion of an SQLITE_CHECKPOINT_TRUNCATE, the log file will have been
+** truncated to zero bytes and so both *pnLog and *pnCkpt will be set to zero.
+**
+** ^All calls obtain an exclusive "checkpoint" lock on the database file. ^If
** any other process is running a checkpoint operation at the same time, the
-** lock cannot be obtained and SQLITE_BUSY is returned. Even if there is a
+** lock cannot be obtained and SQLITE_BUSY is returned. ^Even if there is a
** busy-handler configured, it will not be invoked in this case.
**
-** The SQLITE_CHECKPOINT_FULL and RESTART modes also obtain the exclusive
-** "writer" lock on the database file. If the writer lock cannot be obtained
-** immediately, and a busy-handler is configured, it is invoked and the writer
-** lock retried until either the busy-handler returns 0 or the lock is
-** successfully obtained. The busy-handler is also invoked while waiting for
-** database readers as described above. If the busy-handler returns 0 before
+** ^The SQLITE_CHECKPOINT_FULL, RESTART and TRUNCATE modes also obtain the
+** exclusive "writer" lock on the database file. ^If the writer lock cannot be
+** obtained immediately, and a busy-handler is configured, it is invoked and
+** the writer lock retried until either the busy-handler returns 0 or the lock
+** is successfully obtained. ^The busy-handler is also invoked while waiting for
+** database readers as described above. ^If the busy-handler returns 0 before
** the writer lock is obtained or while waiting for database readers, the
** checkpoint operation proceeds from that point in the same way as
** SQLITE_CHECKPOINT_PASSIVE - checkpointing as many frames as possible
-** without blocking any further. SQLITE_BUSY is returned in this case.
+** without blocking any further. ^SQLITE_BUSY is returned in this case.
**
-** If parameter zDb is NULL or points to a zero length string, then the
-** specified operation is attempted on all WAL databases. In this case the
-** values written to output parameters *pnLog and *pnCkpt are undefined. If
+** ^If parameter zDb is NULL or points to a zero length string, then the
+** specified operation is attempted on all WAL databases [attached] to
+** [database connection] db. In this case the
+** values written to output parameters *pnLog and *pnCkpt are undefined. ^If
** an SQLITE_BUSY error is encountered when processing one or more of the
** attached WAL databases, the operation is still attempted on any remaining
-** attached databases and SQLITE_BUSY is returned to the caller. If any other
+** attached databases and SQLITE_BUSY is returned at the end. ^If any other
** error occurs while processing an attached database, processing is abandoned
-** and the error code returned to the caller immediately. If no error
+** and the error code is returned to the caller immediately. ^If no error
** (SQLITE_BUSY or otherwise) is encountered while processing the attached
** databases, SQLITE_OK is returned.
**
-** If database zDb is the name of an attached database that is not in WAL
-** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. If
+** ^If database zDb is the name of an attached database that is not in WAL
+** mode, SQLITE_OK is returned and both *pnLog and *pnCkpt set to -1. ^If
** zDb is not NULL (or a zero length string) and is not the name of any
** attached database, SQLITE_ERROR is returned to the caller.
+**
+** ^Unless it returns SQLITE_MISUSE,
+** the sqlite3_wal_checkpoint_v2() interface
+** sets the error information that is queried by
+** [sqlite3_errcode()] and [sqlite3_errmsg()].
+**
+** ^The [PRAGMA wal_checkpoint] command can be used to invoke this interface
+** from SQL.
*/
SQLITE_API int sqlite3_wal_checkpoint_v2(
sqlite3 *db, /* Database handle */
@@ -6587,16 +7898,18 @@ SQLITE_API int sqlite3_wal_checkpoint_v2(
);
/*
-** CAPI3REF: Checkpoint operation parameters
+** CAPI3REF: Checkpoint Mode Values
+** KEYWORDS: {checkpoint mode}
**
-** These constants can be used as the 3rd parameter to
-** [sqlite3_wal_checkpoint_v2()]. See the [sqlite3_wal_checkpoint_v2()]
-** documentation for additional information about the meaning and use of
-** each of these values.
+** These constants define all valid values for the "checkpoint mode" passed
+** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
+** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
+** meaning of each of these checkpoint modes.
*/
-#define SQLITE_CHECKPOINT_PASSIVE 0
-#define SQLITE_CHECKPOINT_FULL 1
-#define SQLITE_CHECKPOINT_RESTART 2
+#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
+#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
+#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for for readers */
+#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
** CAPI3REF: Virtual Table Interface Configuration
@@ -6669,6 +7982,7 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
/*
** CAPI3REF: Conflict resolution modes
+** KEYWORDS: {conflict resolution mode}
**
** These constants are returned by [sqlite3_vtab_on_conflict()] to
** inform a [virtual table] implementation what the [ON CONFLICT] mode
@@ -6684,7 +7998,380 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
/* #define SQLITE_ABORT 4 // Also an error code */
#define SQLITE_REPLACE 5
+/*
+** CAPI3REF: Prepared Statement Scan Status Opcodes
+** KEYWORDS: {scanstatus options}
+**
+** The following constants can be used for the T parameter to the
+** [sqlite3_stmt_scanstatus(S,X,T,V)] interface. Each constant designates a
+** different metric for sqlite3_stmt_scanstatus() to return.
+**
+** When the value returned to V is a string, space to hold that string is
+** managed by the prepared statement S and will be automatically freed when
+** S is finalized.
+**
+** <dl>
+** [[SQLITE_SCANSTAT_NLOOP]] <dt>SQLITE_SCANSTAT_NLOOP</dt>
+** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be
+** set to the total number of times that the X-th loop has run.</dd>
+**
+** [[SQLITE_SCANSTAT_NVISIT]] <dt>SQLITE_SCANSTAT_NVISIT</dt>
+** <dd>^The [sqlite3_int64] variable pointed to by the T parameter will be set
+** to the total number of rows examined by all iterations of the X-th loop.</dd>
+**
+** [[SQLITE_SCANSTAT_EST]] <dt>SQLITE_SCANSTAT_EST</dt>
+** <dd>^The "double" variable pointed to by the T parameter will be set to the
+** query planner's estimate for the average number of rows output from each
+** iteration of the X-th loop. If the query planner's estimates was accurate,
+** then this value will approximate the quotient NVISIT/NLOOP and the
+** product of this value for all prior loops with the same SELECTID will
+** be the NLOOP value for the current loop.
+**
+** [[SQLITE_SCANSTAT_NAME]] <dt>SQLITE_SCANSTAT_NAME</dt>
+** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** to a zero-terminated UTF-8 string containing the name of the index or table
+** used for the X-th loop.
+**
+** [[SQLITE_SCANSTAT_EXPLAIN]] <dt>SQLITE_SCANSTAT_EXPLAIN</dt>
+** <dd>^The "const char *" variable pointed to by the T parameter will be set
+** to a zero-terminated UTF-8 string containing the [EXPLAIN QUERY PLAN]
+** description for the X-th loop.
+**
+** [[SQLITE_SCANSTAT_SELECTID]] <dt>SQLITE_SCANSTAT_SELECT</dt>
+** <dd>^The "int" variable pointed to by the T parameter will be set to the
+** "select-id" for the X-th loop. The select-id identifies which query or
+** subquery the loop is part of. The main query has a select-id of zero.
+** The select-id is the same value as is output in the first column
+** of an [EXPLAIN QUERY PLAN] query.
+** </dl>
+*/
+#define SQLITE_SCANSTAT_NLOOP 0
+#define SQLITE_SCANSTAT_NVISIT 1
+#define SQLITE_SCANSTAT_EST 2
+#define SQLITE_SCANSTAT_NAME 3
+#define SQLITE_SCANSTAT_EXPLAIN 4
+#define SQLITE_SCANSTAT_SELECTID 5
+
+/*
+** CAPI3REF: Prepared Statement Scan Status
+** METHOD: sqlite3_stmt
+**
+** This interface returns information about the predicted and measured
+** performance for pStmt. Advanced applications can use this
+** interface to compare the predicted and the measured performance and
+** issue warnings and/or rerun [ANALYZE] if discrepancies are found.
+**
+** Since this interface is expected to be rarely used, it is only
+** available if SQLite is compiled using the [SQLITE_ENABLE_STMT_SCANSTATUS]
+** compile-time option.
+**
+** The "iScanStatusOp" parameter determines which status information to return.
+** The "iScanStatusOp" must be one of the [scanstatus options] or the behavior
+** of this interface is undefined.
+** ^The requested measurement is written into a variable pointed to by
+** the "pOut" parameter.
+** Parameter "idx" identifies the specific loop to retrieve statistics for.
+** Loops are numbered starting from zero. ^If idx is out of range - less than
+** zero or greater than or equal to the total number of loops used to implement
+** the statement - a non-zero value is returned and the variable that pOut
+** points to is unchanged.
+**
+** ^Statistics might not be available for all loops in all statements. ^In cases
+** where there exist loops with no available statistics, this function behaves
+** as if the loop did not exist - it returns non-zero and leave the variable
+** that pOut points to unchanged.
+**
+** See also: [sqlite3_stmt_scanstatus_reset()]
+*/
+SQLITE_API int sqlite3_stmt_scanstatus(
+ sqlite3_stmt *pStmt, /* Prepared statement for which info desired */
+ int idx, /* Index of loop to report on */
+ int iScanStatusOp, /* Information desired. SQLITE_SCANSTAT_* */
+ void *pOut /* Result written here */
+);
+
+/*
+** CAPI3REF: Zero Scan-Status Counters
+** METHOD: sqlite3_stmt
+**
+** ^Zero all [sqlite3_stmt_scanstatus()] related event counters.
+**
+** This API is only available if the library is built with pre-processor
+** symbol [SQLITE_ENABLE_STMT_SCANSTATUS] defined.
+*/
+SQLITE_API void sqlite3_stmt_scanstatus_reset(sqlite3_stmt*);
+
+/*
+** CAPI3REF: Flush caches to disk mid-transaction
+**
+** ^If a write-transaction is open on [database connection] D when the
+** [sqlite3_db_cacheflush(D)] interface invoked, any dirty
+** pages in the pager-cache that are not currently in use are written out
+** to disk. A dirty page may be in use if a database cursor created by an
+** active SQL statement is reading from it, or if it is page 1 of a database
+** file (page 1 is always "in use"). ^The [sqlite3_db_cacheflush(D)]
+** interface flushes caches for all schemas - "main", "temp", and
+** any [attached] databases.
+**
+** ^If this function needs to obtain extra database locks before dirty pages
+** can be flushed to disk, it does so. ^If those locks cannot be obtained
+** immediately and there is a busy-handler callback configured, it is invoked
+** in the usual manner. ^If the required lock still cannot be obtained, then
+** the database is skipped and an attempt made to flush any dirty pages
+** belonging to the next (if any) database. ^If any databases are skipped
+** because locks cannot be obtained, but no other error occurs, this
+** function returns SQLITE_BUSY.
+**
+** ^If any other error occurs while flushing dirty pages to disk (for
+** example an IO error or out-of-memory condition), then processing is
+** abandoned and an SQLite [error code] is returned to the caller immediately.
+**
+** ^Otherwise, if no error occurs, [sqlite3_db_cacheflush()] returns SQLITE_OK.
+**
+** ^This function does not set the database handle error code or message
+** returned by the [sqlite3_errcode()] and [sqlite3_errmsg()] functions.
+*/
+SQLITE_API int sqlite3_db_cacheflush(sqlite3*);
+
+/*
+** CAPI3REF: The pre-update hook.
+**
+** ^These interfaces are only available if SQLite is compiled using the
+** [SQLITE_ENABLE_PREUPDATE_HOOK] compile-time option.
+**
+** ^The [sqlite3_preupdate_hook()] interface registers a callback function
+** that is invoked prior to each [INSERT], [UPDATE], and [DELETE] operation
+** on a [rowid table].
+** ^At most one preupdate hook may be registered at a time on a single
+** [database connection]; each call to [sqlite3_preupdate_hook()] overrides
+** the previous setting.
+** ^The preupdate hook is disabled by invoking [sqlite3_preupdate_hook()]
+** with a NULL pointer as the second parameter.
+** ^The third parameter to [sqlite3_preupdate_hook()] is passed through as
+** the first parameter to callbacks.
+**
+** ^The preupdate hook only fires for changes to [rowid tables]; the preupdate
+** hook is not invoked for changes to [virtual tables] or [WITHOUT ROWID]
+** tables.
+**
+** ^The second parameter to the preupdate callback is a pointer to
+** the [database connection] that registered the preupdate hook.
+** ^The third parameter to the preupdate callback is one of the constants
+** [SQLITE_INSERT], [SQLITE_DELETE], or [SQLITE_UPDATE] to identify the
+** kind of update operation that is about to occur.
+** ^(The fourth parameter to the preupdate callback is the name of the
+** database within the database connection that is being modified. This
+** will be "main" for the main database or "temp" for TEMP tables or
+** the name given after the AS keyword in the [ATTACH] statement for attached
+** databases.)^
+** ^The fifth parameter to the preupdate callback is the name of the
+** table that is being modified.
+** ^The sixth parameter to the preupdate callback is the initial [rowid] of the
+** row being changes for SQLITE_UPDATE and SQLITE_DELETE changes and is
+** undefined for SQLITE_INSERT changes.
+** ^The seventh parameter to the preupdate callback is the final [rowid] of
+** the row being changed for SQLITE_UPDATE and SQLITE_INSERT changes and is
+** undefined for SQLITE_DELETE changes.
+**
+** The [sqlite3_preupdate_old()], [sqlite3_preupdate_new()],
+** [sqlite3_preupdate_count()], and [sqlite3_preupdate_depth()] interfaces
+** provide additional information about a preupdate event. These routines
+** may only be called from within a preupdate callback. Invoking any of
+** these routines from outside of a preupdate callback or with a
+** [database connection] pointer that is different from the one supplied
+** to the preupdate callback results in undefined and probably undesirable
+** behavior.
+**
+** ^The [sqlite3_preupdate_count(D)] interface returns the number of columns
+** in the row that is being inserted, updated, or deleted.
+**
+** ^The [sqlite3_preupdate_old(D,N,P)] interface writes into P a pointer to
+** a [protected sqlite3_value] that contains the value of the Nth column of
+** the table row before it is updated. The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within SQLITE_UPDATE and SQLITE_DELETE
+** preupdate callbacks; if it is used by an SQLITE_INSERT callback then the
+** behavior is undefined. The [sqlite3_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [sqlite3_preupdate_new(D,N,P)] interface writes into P a pointer to
+** a [protected sqlite3_value] that contains the value of the Nth column of
+** the table row after it is updated. The N parameter must be between 0
+** and one less than the number of columns or the behavior will be
+** undefined. This must only be used within SQLITE_INSERT and SQLITE_UPDATE
+** preupdate callbacks; if it is used by an SQLITE_DELETE callback then the
+** behavior is undefined. The [sqlite3_value] that P points to
+** will be destroyed when the preupdate callback returns.
+**
+** ^The [sqlite3_preupdate_depth(D)] interface returns 0 if the preupdate
+** callback was invoked as a result of a direct insert, update, or delete
+** operation; or 1 for inserts, updates, or deletes invoked by top-level
+** triggers; or 2 for changes resulting from triggers called by top-level
+** triggers; and so forth.
+**
+** See also: [sqlite3_update_hook()]
+*/
+SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_preupdate_hook(
+ sqlite3 *db,
+ void(*xPreUpdate)(
+ void *pCtx, /* Copy of third arg to preupdate_hook() */
+ sqlite3 *db, /* Database handle */
+ int op, /* SQLITE_UPDATE, DELETE or INSERT */
+ char const *zDb, /* Database name */
+ char const *zName, /* Table name */
+ sqlite3_int64 iKey1, /* Rowid of row about to be deleted/updated */
+ sqlite3_int64 iKey2 /* New rowid value (for a rowid UPDATE) */
+ ),
+ void*
+);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_old(sqlite3 *, int, sqlite3_value **);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_count(sqlite3 *);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_depth(sqlite3 *);
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_preupdate_new(sqlite3 *, int, sqlite3_value **);
+
+/*
+** CAPI3REF: Low-level system error code
+**
+** ^Attempt to return the underlying operating system error code or error
+** number that caused the most recent I/O error or failure to open a file.
+** The return value is OS-dependent. For example, on unix systems, after
+** [sqlite3_open_v2()] returns [SQLITE_CANTOPEN], this interface could be
+** called to get back the underlying "errno" that caused the problem, such
+** as ENOSPC, EAUTH, EISDIR, and so forth.
+*/
+SQLITE_API int sqlite3_system_errno(sqlite3*);
+
+/*
+** CAPI3REF: Database Snapshot
+** KEYWORDS: {snapshot}
+** EXPERIMENTAL
+**
+** An instance of the snapshot object records the state of a [WAL mode]
+** database for some specific point in history.
+**
+** In [WAL mode], multiple [database connections] that are open on the
+** same database file can each be reading a different historical version
+** of the database file. When a [database connection] begins a read
+** transaction, that connection sees an unchanging copy of the database
+** as it existed for the point in time when the transaction first started.
+** Subsequent changes to the database from other connections are not seen
+** by the reader until a new read transaction is started.
+**
+** The sqlite3_snapshot object records state information about an historical
+** version of the database file so that it is possible to later open a new read
+** transaction that sees that historical version of the database rather than
+** the most recent version.
+**
+** The constructor for this object is [sqlite3_snapshot_get()]. The
+** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
+** to an historical snapshot (if possible). The destructor for
+** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
+*/
+typedef struct sqlite3_snapshot sqlite3_snapshot;
+
+/*
+** CAPI3REF: Record A Database Snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
+** new [sqlite3_snapshot] object that records the current state of
+** schema S in database connection D. ^On success, the
+** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
+** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
+** ^If schema S of [database connection] D is not a [WAL mode] database
+** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
+** leaves the *P value unchanged and returns an appropriate [error code].
+**
+** The [sqlite3_snapshot] object returned from a successful call to
+** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
+** to avoid a memory leak.
+**
+** The [sqlite3_snapshot_get()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_get(
+ sqlite3 *db,
+ const char *zSchema,
+ sqlite3_snapshot **ppSnapshot
+);
+
+/*
+** CAPI3REF: Start a read transaction on an historical snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a
+** read transaction for schema S of
+** [database connection] D such that the read transaction
+** refers to historical [snapshot] P, rather than the most
+** recent change to the database.
+** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
+** or an appropriate [error code] if it fails.
+**
+** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
+** the first operation following the [BEGIN] that takes the schema S
+** out of [autocommit mode].
+** ^In other words, schema S must not currently be in
+** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the
+** database connection D must be out of [autocommit mode].
+** ^A [snapshot] will fail to open if it has been overwritten by a
+** [checkpoint].
+** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
+** database connection D does not know that the database file for
+** schema S is in [WAL mode]. A database connection might not know
+** that the database file is in [WAL mode] if there has been no prior
+** I/O on that database connection, or if the database entered [WAL mode]
+** after the most recent I/O on the database connection.)^
+** (Hint: Run "[PRAGMA application_id]" against a newly opened
+** database connection in order to make it ready to use snapshots.)
+**
+** The [sqlite3_snapshot_open()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_open(
+ sqlite3 *db,
+ const char *zSchema,
+ sqlite3_snapshot *pSnapshot
+);
+
+/*
+** CAPI3REF: Destroy a snapshot
+** EXPERIMENTAL
+**
+** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P.
+** The application must eventually free every [sqlite3_snapshot] object
+** using this routine to avoid a memory leak.
+**
+** The [sqlite3_snapshot_free()] interface is only available when the
+** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*);
+/*
+** CAPI3REF: Compare the ages of two snapshot handles.
+** EXPERIMENTAL
+**
+** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages
+** of two valid snapshot handles.
+**
+** If the two snapshot handles are not associated with the same database
+** file, the result of the comparison is undefined.
+**
+** Additionally, the result of the comparison is only valid if both of the
+** snapshot handles were obtained by calling sqlite3_snapshot_get() since the
+** last time the wal file was deleted. The wal file is deleted when the
+** database is changed back to rollback mode or when the number of database
+** clients drops to zero. If either snapshot handle was obtained before the
+** wal file was last deleted, the value returned by this function
+** is undefined.
+**
+** Otherwise, this API returns a negative value if P1 refers to an older
+** snapshot than P2, zero if the two handles refer to the same database
+** snapshot, and a positive value if P1 is a newer snapshot than P2.
+*/
+SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
+ sqlite3_snapshot *p1,
+ sqlite3_snapshot *p2
+);
/*
** Undo the hack that converts floating point types to integer for
@@ -6697,8 +8384,9 @@ SQLITE_API int sqlite3_vtab_on_conflict(sqlite3 *);
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
-#endif
+#endif /* SQLITE3_H */
+/******** Begin file sqlite3rtree.h *********/
/*
** 2010 August 30
**
@@ -6721,6 +8409,16 @@ extern "C" {
#endif
typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
+typedef struct sqlite3_rtree_query_info sqlite3_rtree_query_info;
+
+/* The double-precision datatype used by RTree depends on the
+** SQLITE_RTREE_INT_ONLY compile-time option.
+*/
+#ifdef SQLITE_RTREE_INT_ONLY
+ typedef sqlite3_int64 sqlite3_rtree_dbl;
+#else
+ typedef double sqlite3_rtree_dbl;
+#endif
/*
** Register a geometry callback named zGeom that can be used as part of an
@@ -6731,7 +8429,7 @@ typedef struct sqlite3_rtree_geometry sqlite3_rtree_geometry;
SQLITE_API int sqlite3_rtree_geometry_callback(
sqlite3 *db,
const char *zGeom,
- int (*xGeom)(sqlite3_rtree_geometry *, int nCoord, double *aCoord, int *pRes),
+ int (*xGeom)(sqlite3_rtree_geometry*, int, sqlite3_rtree_dbl*,int*),
void *pContext
);
@@ -6743,11 +8441,62 @@ SQLITE_API int sqlite3_rtree_geometry_callback(
struct sqlite3_rtree_geometry {
void *pContext; /* Copy of pContext passed to s_r_g_c() */
int nParam; /* Size of array aParam[] */
- double *aParam; /* Parameters passed to SQL geom function */
+ sqlite3_rtree_dbl *aParam; /* Parameters passed to SQL geom function */
void *pUser; /* Callback implementation user data */
void (*xDelUser)(void *); /* Called by SQLite to clean up pUser */
};
+/*
+** Register a 2nd-generation geometry callback named zScore that can be
+** used as part of an R-Tree geometry query as follows:
+**
+** SELECT ... FROM <rtree> WHERE <rtree col> MATCH $zQueryFunc(... params ...)
+*/
+SQLITE_API int sqlite3_rtree_query_callback(
+ sqlite3 *db,
+ const char *zQueryFunc,
+ int (*xQueryFunc)(sqlite3_rtree_query_info*),
+ void *pContext,
+ void (*xDestructor)(void*)
+);
+
+
+/*
+** A pointer to a structure of the following type is passed as the
+** argument to scored geometry callback registered using
+** sqlite3_rtree_query_callback().
+**
+** Note that the first 5 fields of this structure are identical to
+** sqlite3_rtree_geometry. This structure is a subclass of
+** sqlite3_rtree_geometry.
+*/
+struct sqlite3_rtree_query_info {
+ void *pContext; /* pContext from when function registered */
+ int nParam; /* Number of function parameters */
+ sqlite3_rtree_dbl *aParam; /* value of function parameters */
+ void *pUser; /* callback can use this, if desired */
+ void (*xDelUser)(void*); /* function to free pUser */
+ sqlite3_rtree_dbl *aCoord; /* Coordinates of node or entry to check */
+ unsigned int *anQueue; /* Number of pending entries in the queue */
+ int nCoord; /* Number of coordinates */
+ int iLevel; /* Level of current node or entry */
+ int mxLevel; /* The largest iLevel value in the tree */
+ sqlite3_int64 iRowid; /* Rowid for current entry */
+ sqlite3_rtree_dbl rParentScore; /* Score of parent node */
+ int eParentWithin; /* Visibility of parent node */
+ int eWithin; /* OUT: Visiblity */
+ sqlite3_rtree_dbl rScore; /* OUT: Write the score here */
+ /* The following fields are only available in 3.8.11 and later */
+ sqlite3_value **apSqlParam; /* Original SQL values of parameters */
+};
+
+/*
+** Allowed values for sqlite3_rtree_query.eWithin and .eParentWithin.
+*/
+#define NOT_WITHIN 0 /* Object completely outside of query region */
+#define PARTLY_WITHIN 1 /* Object partially overlaps query region */
+#define FULLY_WITHIN 2 /* Object fully contained within query region */
+
#ifdef __cplusplus
} /* end of the 'extern "C"' block */
@@ -6755,3 +8504,1868 @@ struct sqlite3_rtree_geometry {
#endif /* ifndef _SQLITE3RTREE_H_ */
+/******** End of sqlite3rtree.h *********/
+/******** Begin file sqlite3session.h *********/
+
+#if !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION)
+#define __SQLITESESSION_H_ 1
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/*
+** CAPI3REF: Session Object Handle
+*/
+typedef struct sqlite3_session sqlite3_session;
+
+/*
+** CAPI3REF: Changeset Iterator Handle
+*/
+typedef struct sqlite3_changeset_iter sqlite3_changeset_iter;
+
+/*
+** CAPI3REF: Create A New Session Object
+**
+** Create a new session object attached to database handle db. If successful,
+** a pointer to the new object is written to *ppSession and SQLITE_OK is
+** returned. If an error occurs, *ppSession is set to NULL and an SQLite
+** error code (e.g. SQLITE_NOMEM) is returned.
+**
+** It is possible to create multiple session objects attached to a single
+** database handle.
+**
+** Session objects created using this function should be deleted using the
+** [sqlite3session_delete()] function before the database handle that they
+** are attached to is itself closed. If the database handle is closed before
+** the session object is deleted, then the results of calling any session
+** module function, including [sqlite3session_delete()] on the session object
+** are undefined.
+**
+** Because the session module uses the [sqlite3_preupdate_hook()] API, it
+** is not possible for an application to register a pre-update hook on a
+** database handle that has one or more session objects attached. Nor is
+** it possible to create a session object attached to a database handle for
+** which a pre-update hook is already defined. The results of attempting
+** either of these things are undefined.
+**
+** The session object will be used to create changesets for tables in
+** database zDb, where zDb is either "main", or "temp", or the name of an
+** attached database. It is not an error if database zDb is not attached
+** to the database when the session object is created.
+*/
+int sqlite3session_create(
+ sqlite3 *db, /* Database handle */
+ const char *zDb, /* Name of db (e.g. "main") */
+ sqlite3_session **ppSession /* OUT: New session object */
+);
+
+/*
+** CAPI3REF: Delete A Session Object
+**
+** Delete a session object previously allocated using
+** [sqlite3session_create()]. Once a session object has been deleted, the
+** results of attempting to use pSession with any other session module
+** function are undefined.
+**
+** Session objects must be deleted before the database handle to which they
+** are attached is closed. Refer to the documentation for
+** [sqlite3session_create()] for details.
+*/
+void sqlite3session_delete(sqlite3_session *pSession);
+
+
+/*
+** CAPI3REF: Enable Or Disable A Session Object
+**
+** Enable or disable the recording of changes by a session object. When
+** enabled, a session object records changes made to the database. When
+** disabled - it does not. A newly created session object is enabled.
+** Refer to the documentation for [sqlite3session_changeset()] for further
+** details regarding how enabling and disabling a session object affects
+** the eventual changesets.
+**
+** Passing zero to this function disables the session. Passing a value
+** greater than zero enables it. Passing a value less than zero is a
+** no-op, and may be used to query the current state of the session.
+**
+** The return value indicates the final state of the session object: 0 if
+** the session is disabled, or 1 if it is enabled.
+*/
+int sqlite3session_enable(sqlite3_session *pSession, int bEnable);
+
+/*
+** CAPI3REF: Set Or Clear the Indirect Change Flag
+**
+** Each change recorded by a session object is marked as either direct or
+** indirect. A change is marked as indirect if either:
+**
+** <ul>
+** <li> The session object "indirect" flag is set when the change is
+** made, or
+** <li> The change is made by an SQL trigger or foreign key action
+** instead of directly as a result of a users SQL statement.
+** </ul>
+**
+** If a single row is affected by more than one operation within a session,
+** then the change is considered indirect if all operations meet the criteria
+** for an indirect change above, or direct otherwise.
+**
+** This function is used to set, clear or query the session object indirect
+** flag. If the second argument passed to this function is zero, then the
+** indirect flag is cleared. If it is greater than zero, the indirect flag
+** is set. Passing a value less than zero does not modify the current value
+** of the indirect flag, and may be used to query the current state of the
+** indirect flag for the specified session object.
+**
+** The return value indicates the final state of the indirect flag: 0 if
+** it is clear, or 1 if it is set.
+*/
+int sqlite3session_indirect(sqlite3_session *pSession, int bIndirect);
+
+/*
+** CAPI3REF: Attach A Table To A Session Object
+**
+** If argument zTab is not NULL, then it is the name of a table to attach
+** to the session object passed as the first argument. All subsequent changes
+** made to the table while the session object is enabled will be recorded. See
+** documentation for [sqlite3session_changeset()] for further details.
+**
+** Or, if argument zTab is NULL, then changes are recorded for all tables
+** in the database. If additional tables are added to the database (by
+** executing "CREATE TABLE" statements) after this call is made, changes for
+** the new tables are also recorded.
+**
+** Changes can only be recorded for tables that have a PRIMARY KEY explicitly
+** defined as part of their CREATE TABLE statement. It does not matter if the
+** PRIMARY KEY is an "INTEGER PRIMARY KEY" (rowid alias) or not. The PRIMARY
+** KEY may consist of a single column, or may be a composite key.
+**
+** It is not an error if the named table does not exist in the database. Nor
+** is it an error if the named table does not have a PRIMARY KEY. However,
+** no changes will be recorded in either of these scenarios.
+**
+** Changes are not recorded for individual rows that have NULL values stored
+** in one or more of their PRIMARY KEY columns.
+**
+** SQLITE_OK is returned if the call completes without error. Or, if an error
+** occurs, an SQLite error code (e.g. SQLITE_NOMEM) is returned.
+*/
+int sqlite3session_attach(
+ sqlite3_session *pSession, /* Session object */
+ const char *zTab /* Table name */
+);
+
+/*
+** CAPI3REF: Set a table filter on a Session Object.
+**
+** The second argument (xFilter) is the "filter callback". For changes to rows
+** in tables that are not attached to the Session object, the filter is called
+** to determine whether changes to the table's rows should be tracked or not.
+** If xFilter returns 0, changes is not tracked. Note that once a table is
+** attached, xFilter will not be called again.
+*/
+void sqlite3session_table_filter(
+ sqlite3_session *pSession, /* Session object */
+ int(*xFilter)(
+ void *pCtx, /* Copy of third arg to _filter_table() */
+ const char *zTab /* Table name */
+ ),
+ void *pCtx /* First argument passed to xFilter */
+);
+
+/*
+** CAPI3REF: Generate A Changeset From A Session Object
+**
+** Obtain a changeset containing changes to the tables attached to the
+** session object passed as the first argument. If successful,
+** set *ppChangeset to point to a buffer containing the changeset
+** and *pnChangeset to the size of the changeset in bytes before returning
+** SQLITE_OK. If an error occurs, set both *ppChangeset and *pnChangeset to
+** zero and return an SQLite error code.
+**
+** A changeset consists of zero or more INSERT, UPDATE and/or DELETE changes,
+** each representing a change to a single row of an attached table. An INSERT
+** change contains the values of each field of a new database row. A DELETE
+** contains the original values of each field of a deleted database row. An
+** UPDATE change contains the original values of each field of an updated
+** database row along with the updated values for each updated non-primary-key
+** column. It is not possible for an UPDATE change to represent a change that
+** modifies the values of primary key columns. If such a change is made, it
+** is represented in a changeset as a DELETE followed by an INSERT.
+**
+** Changes are not recorded for rows that have NULL values stored in one or
+** more of their PRIMARY KEY columns. If such a row is inserted or deleted,
+** no corresponding change is present in the changesets returned by this
+** function. If an existing row with one or more NULL values stored in
+** PRIMARY KEY columns is updated so that all PRIMARY KEY columns are non-NULL,
+** only an INSERT is appears in the changeset. Similarly, if an existing row
+** with non-NULL PRIMARY KEY values is updated so that one or more of its
+** PRIMARY KEY columns are set to NULL, the resulting changeset contains a
+** DELETE change only.
+**
+** The contents of a changeset may be traversed using an iterator created
+** using the [sqlite3changeset_start()] API. A changeset may be applied to
+** a database with a compatible schema using the [sqlite3changeset_apply()]
+** API.
+**
+** Within a changeset generated by this function, all changes related to a
+** single table are grouped together. In other words, when iterating through
+** a changeset or when applying a changeset to a database, all changes related
+** to a single table are processed before moving on to the next table. Tables
+** are sorted in the same order in which they were attached (or auto-attached)
+** to the sqlite3_session object. The order in which the changes related to
+** a single table are stored is undefined.
+**
+** Following a successful call to this function, it is the responsibility of
+** the caller to eventually free the buffer that *ppChangeset points to using
+** [sqlite3_free()].
+**
+** <h3>Changeset Generation</h3>
+**
+** Once a table has been attached to a session object, the session object
+** records the primary key values of all new rows inserted into the table.
+** It also records the original primary key and other column values of any
+** deleted or updated rows. For each unique primary key value, data is only
+** recorded once - the first time a row with said primary key is inserted,
+** updated or deleted in the lifetime of the session.
+**
+** There is one exception to the previous paragraph: when a row is inserted,
+** updated or deleted, if one or more of its primary key columns contain a
+** NULL value, no record of the change is made.
+**
+** The session object therefore accumulates two types of records - those
+** that consist of primary key values only (created when the user inserts
+** a new record) and those that consist of the primary key values and the
+** original values of other table columns (created when the users deletes
+** or updates a record).
+**
+** When this function is called, the requested changeset is created using
+** both the accumulated records and the current contents of the database
+** file. Specifically:
+**
+** <ul>
+** <li> For each record generated by an insert, the database is queried
+** for a row with a matching primary key. If one is found, an INSERT
+** change is added to the changeset. If no such row is found, no change
+** is added to the changeset.
+**
+** <li> For each record generated by an update or delete, the database is
+** queried for a row with a matching primary key. If such a row is
+** found and one or more of the non-primary key fields have been
+** modified from their original values, an UPDATE change is added to
+** the changeset. Or, if no such row is found in the table, a DELETE
+** change is added to the changeset. If there is a row with a matching
+** primary key in the database, but all fields contain their original
+** values, no change is added to the changeset.
+** </ul>
+**
+** This means, amongst other things, that if a row is inserted and then later
+** deleted while a session object is active, neither the insert nor the delete
+** will be present in the changeset. Or if a row is deleted and then later a
+** row with the same primary key values inserted while a session object is
+** active, the resulting changeset will contain an UPDATE change instead of
+** a DELETE and an INSERT.
+**
+** When a session object is disabled (see the [sqlite3session_enable()] API),
+** it does not accumulate records when rows are inserted, updated or deleted.
+** This may appear to have some counter-intuitive effects if a single row
+** is written to more than once during a session. For example, if a row
+** is inserted while a session object is enabled, then later deleted while
+** the same session object is disabled, no INSERT record will appear in the
+** changeset, even though the delete took place while the session was disabled.
+** Or, if one field of a row is updated while a session is disabled, and
+** another field of the same row is updated while the session is enabled, the
+** resulting changeset will contain an UPDATE change that updates both fields.
+*/
+int sqlite3session_changeset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppChangeset /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Load The Difference Between Tables Into A Session
+**
+** If it is not already attached to the session object passed as the first
+** argument, this function attaches table zTbl in the same manner as the
+** [sqlite3session_attach()] function. If zTbl does not exist, or if it
+** does not have a primary key, this function is a no-op (but does not return
+** an error).
+**
+** Argument zFromDb must be the name of a database ("main", "temp" etc.)
+** attached to the same database handle as the session object that contains
+** a table compatible with the table attached to the session by this function.
+** A table is considered compatible if it:
+**
+** <ul>
+** <li> Has the same name,
+** <li> Has the same set of columns declared in the same order, and
+** <li> Has the same PRIMARY KEY definition.
+** </ul>
+**
+** If the tables are not compatible, SQLITE_SCHEMA is returned. If the tables
+** are compatible but do not have any PRIMARY KEY columns, it is not an error
+** but no changes are added to the session object. As with other session
+** APIs, tables without PRIMARY KEYs are simply ignored.
+**
+** This function adds a set of changes to the session object that could be
+** used to update the table in database zFrom (call this the "from-table")
+** so that its content is the same as the table attached to the session
+** object (call this the "to-table"). Specifically:
+**
+** <ul>
+** <li> For each row (primary key) that exists in the to-table but not in
+** the from-table, an INSERT record is added to the session object.
+**
+** <li> For each row (primary key) that exists in the to-table but not in
+** the from-table, a DELETE record is added to the session object.
+**
+** <li> For each row (primary key) that exists in both tables, but features
+** different in each, an UPDATE record is added to the session.
+** </ul>
+**
+** To clarify, if this function is called and then a changeset constructed
+** using [sqlite3session_changeset()], then after applying that changeset to
+** database zFrom the contents of the two compatible tables would be
+** identical.
+**
+** It an error if database zFrom does not exist or does not contain the
+** required compatible table.
+**
+** If the operation successful, SQLITE_OK is returned. Otherwise, an SQLite
+** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
+** may be set to point to a buffer containing an English language error
+** message. It is the responsibility of the caller to free this buffer using
+** sqlite3_free().
+*/
+int sqlite3session_diff(
+ sqlite3_session *pSession,
+ const char *zFromDb,
+ const char *zTbl,
+ char **pzErrMsg
+);
+
+
+/*
+** CAPI3REF: Generate A Patchset From A Session Object
+**
+** The differences between a patchset and a changeset are that:
+**
+** <ul>
+** <li> DELETE records consist of the primary key fields only. The
+** original values of other fields are omitted.
+** <li> The original values of any modified fields are omitted from
+** UPDATE records.
+** </ul>
+**
+** A patchset blob may be used with up to date versions of all
+** sqlite3changeset_xxx API functions except for sqlite3changeset_invert(),
+** which returns SQLITE_CORRUPT if it is passed a patchset. Similarly,
+** attempting to use a patchset blob with old versions of the
+** sqlite3changeset_xxx APIs also provokes an SQLITE_CORRUPT error.
+**
+** Because the non-primary key "old.*" fields are omitted, no
+** SQLITE_CHANGESET_DATA conflicts can be detected or reported if a patchset
+** is passed to the sqlite3changeset_apply() API. Other conflict types work
+** in the same way as for changesets.
+**
+** Changes within a patchset are ordered in the same way as for changesets
+** generated by the sqlite3session_changeset() function (i.e. all changes for
+** a single table are grouped together, tables appear in the order in which
+** they were attached to the session object).
+*/
+int sqlite3session_patchset(
+ sqlite3_session *pSession, /* Session object */
+ int *pnPatchset, /* OUT: Size of buffer at *ppChangeset */
+ void **ppPatchset /* OUT: Buffer containing changeset */
+);
+
+/*
+** CAPI3REF: Test if a changeset has recorded any changes.
+**
+** Return non-zero if no changes to attached tables have been recorded by
+** the session object passed as the first argument. Otherwise, if one or
+** more changes have been recorded, return zero.
+**
+** Even if this function returns zero, it is possible that calling
+** [sqlite3session_changeset()] on the session handle may still return a
+** changeset that contains no changes. This can happen when a row in
+** an attached table is modified and then later on the original values
+** are restored. However, if this function returns non-zero, then it is
+** guaranteed that a call to sqlite3session_changeset() will return a
+** changeset containing zero changes.
+*/
+int sqlite3session_isempty(sqlite3_session *pSession);
+
+/*
+** CAPI3REF: Create An Iterator To Traverse A Changeset
+**
+** Create an iterator used to iterate through the contents of a changeset.
+** If successful, *pp is set to point to the iterator handle and SQLITE_OK
+** is returned. Otherwise, if an error occurs, *pp is set to zero and an
+** SQLite error code is returned.
+**
+** The following functions can be used to advance and query a changeset
+** iterator created by this function:
+**
+** <ul>
+** <li> [sqlite3changeset_next()]
+** <li> [sqlite3changeset_op()]
+** <li> [sqlite3changeset_new()]
+** <li> [sqlite3changeset_old()]
+** </ul>
+**
+** It is the responsibility of the caller to eventually destroy the iterator
+** by passing it to [sqlite3changeset_finalize()]. The buffer containing the
+** changeset (pChangeset) must remain valid until after the iterator is
+** destroyed.
+**
+** Assuming the changeset blob was created by one of the
+** [sqlite3session_changeset()], [sqlite3changeset_concat()] or
+** [sqlite3changeset_invert()] functions, all changes within the changeset
+** that apply to a single table are grouped together. This means that when
+** an application iterates through a changeset using an iterator created by
+** this function, all changes that relate to a single table are visited
+** consecutively. There is no chance that the iterator will visit a change
+** the applies to table X, then one for table Y, and then later on visit
+** another change for table X.
+*/
+int sqlite3changeset_start(
+ sqlite3_changeset_iter **pp, /* OUT: New changeset iterator handle */
+ int nChangeset, /* Size of changeset blob in bytes */
+ void *pChangeset /* Pointer to blob containing changeset */
+);
+
+
+/*
+** CAPI3REF: Advance A Changeset Iterator
+**
+** This function may only be used with iterators created by function
+** [sqlite3changeset_start()]. If it is called on an iterator passed to
+** a conflict-handler callback by [sqlite3changeset_apply()], SQLITE_MISUSE
+** is returned and the call has no effect.
+**
+** Immediately after an iterator is created by sqlite3changeset_start(), it
+** does not point to any change in the changeset. Assuming the changeset
+** is not empty, the first call to this function advances the iterator to
+** point to the first change in the changeset. Each subsequent call advances
+** the iterator to point to the next change in the changeset (if any). If
+** no error occurs and the iterator points to a valid change after a call
+** to sqlite3changeset_next() has advanced it, SQLITE_ROW is returned.
+** Otherwise, if all changes in the changeset have already been visited,
+** SQLITE_DONE is returned.
+**
+** If an error occurs, an SQLite error code is returned. Possible error
+** codes include SQLITE_CORRUPT (if the changeset buffer is corrupt) or
+** SQLITE_NOMEM.
+*/
+int sqlite3changeset_next(sqlite3_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Obtain The Current Operation From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned [SQLITE_ROW]. If this
+** is not the case, this function returns [SQLITE_MISUSE].
+**
+** If argument pzTab is not NULL, then *pzTab is set to point to a
+** nul-terminated utf-8 encoded string containing the name of the table
+** affected by the current change. The buffer remains valid until either
+** sqlite3changeset_next() is called on the iterator or until the
+** conflict-handler function returns. If pnCol is not NULL, then *pnCol is
+** set to the number of columns in the table affected by the change. If
+** pbIncorrect is not NULL, then *pbIndirect is set to true (1) if the change
+** is an indirect change, or false (0) otherwise. See the documentation for
+** [sqlite3session_indirect()] for a description of direct and indirect
+** changes. Finally, if pOp is not NULL, then *pOp is set to one of
+** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the
+** type of change that the iterator currently points to.
+**
+** If no error occurs, SQLITE_OK is returned. If an error does occur, an
+** SQLite error code is returned. The values of the output variables may not
+** be trusted in this case.
+*/
+int sqlite3changeset_op(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ const char **pzTab, /* OUT: Pointer to table name */
+ int *pnCol, /* OUT: Number of columns in table */
+ int *pOp, /* OUT: SQLITE_INSERT, DELETE or UPDATE */
+ int *pbIndirect /* OUT: True for an 'indirect' change */
+);
+
+/*
+** CAPI3REF: Obtain The Primary Key Definition Of A Table
+**
+** For each modified table, a changeset includes the following:
+**
+** <ul>
+** <li> The number of columns in the table, and
+** <li> Which of those columns make up the tables PRIMARY KEY.
+** </ul>
+**
+** This function is used to find which columns comprise the PRIMARY KEY of
+** the table modified by the change that iterator pIter currently points to.
+** If successful, *pabPK is set to point to an array of nCol entries, where
+** nCol is the number of columns in the table. Elements of *pabPK are set to
+** 0x01 if the corresponding column is part of the tables primary key, or
+** 0x00 if it is not.
+**
+** If argument pnCol is not NULL, then *pnCol is set to the number of columns
+** in the table.
+**
+** If this function is called when the iterator does not point to a valid
+** entry, SQLITE_MISUSE is returned and the output variables zeroed. Otherwise,
+** SQLITE_OK is returned and the output variables populated as described
+** above.
+*/
+int sqlite3changeset_pk(
+ sqlite3_changeset_iter *pIter, /* Iterator object */
+ unsigned char **pabPK, /* OUT: Array of boolean - true for PK cols */
+ int *pnCol /* OUT: Number of entries in output array */
+);
+
+/*
+** CAPI3REF: Obtain old.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [SQLITE_DELETE] or [SQLITE_UPDATE]. Otherwise,
+** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the vector of
+** original row values stored as part of the UPDATE or DELETE change and
+** returns SQLITE_OK. The name of the function comes from the fact that this
+** is similar to the "old.*" columns available to update or delete triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_old(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: Old value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain new.* Values From A Changeset Iterator
+**
+** The pIter argument passed to this function may either be an iterator
+** passed to a conflict-handler by [sqlite3changeset_apply()], or an iterator
+** created by [sqlite3changeset_start()]. In the latter case, the most recent
+** call to [sqlite3changeset_next()] must have returned SQLITE_ROW.
+** Furthermore, it may only be called if the type of change that the iterator
+** currently points to is either [SQLITE_UPDATE] or [SQLITE_INSERT]. Otherwise,
+** this function returns [SQLITE_MISUSE] and sets *ppValue to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the vector of
+** new row values stored as part of the UPDATE or INSERT change and
+** returns SQLITE_OK. If the change is an UPDATE and does not include
+** a new value for the requested column, *ppValue is set to NULL and
+** SQLITE_OK returned. The name of the function comes from the fact that
+** this is similar to the "new.*" columns available to update or delete
+** triggers.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_new(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: New value (or NULL pointer) */
+);
+
+/*
+** CAPI3REF: Obtain Conflicting Row Values From A Changeset Iterator
+**
+** This function should only be used with iterator objects passed to a
+** conflict-handler callback by [sqlite3changeset_apply()] with either
+** [SQLITE_CHANGESET_DATA] or [SQLITE_CHANGESET_CONFLICT]. If this function
+** is called on any other iterator, [SQLITE_MISUSE] is returned and *ppValue
+** is set to NULL.
+**
+** Argument iVal must be greater than or equal to 0, and less than the number
+** of columns in the table affected by the current change. Otherwise,
+** [SQLITE_RANGE] is returned and *ppValue is set to NULL.
+**
+** If successful, this function sets *ppValue to point to a protected
+** sqlite3_value object containing the iVal'th value from the
+** "conflicting row" associated with the current conflict-handler callback
+** and returns SQLITE_OK.
+**
+** If some other error occurs (e.g. an OOM condition), an SQLite error code
+** is returned and *ppValue is set to NULL.
+*/
+int sqlite3changeset_conflict(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int iVal, /* Column number */
+ sqlite3_value **ppValue /* OUT: Value from conflicting row */
+);
+
+/*
+** CAPI3REF: Determine The Number Of Foreign Key Constraint Violations
+**
+** This function may only be called with an iterator passed to an
+** SQLITE_CHANGESET_FOREIGN_KEY conflict handler callback. In this case
+** it sets the output variable to the total number of known foreign key
+** violations in the destination database and returns SQLITE_OK.
+**
+** In all other cases this function returns SQLITE_MISUSE.
+*/
+int sqlite3changeset_fk_conflicts(
+ sqlite3_changeset_iter *pIter, /* Changeset iterator */
+ int *pnOut /* OUT: Number of FK violations */
+);
+
+
+/*
+** CAPI3REF: Finalize A Changeset Iterator
+**
+** This function is used to finalize an iterator allocated with
+** [sqlite3changeset_start()].
+**
+** This function should only be called on iterators created using the
+** [sqlite3changeset_start()] function. If an application calls this
+** function with an iterator passed to a conflict-handler by
+** [sqlite3changeset_apply()], [SQLITE_MISUSE] is immediately returned and the
+** call has no effect.
+**
+** If an error was encountered within a call to an sqlite3changeset_xxx()
+** function (for example an [SQLITE_CORRUPT] in [sqlite3changeset_next()] or an
+** [SQLITE_NOMEM] in [sqlite3changeset_new()]) then an error code corresponding
+** to that error is returned by this function. Otherwise, SQLITE_OK is
+** returned. This is to allow the following pattern (pseudo-code):
+**
+** sqlite3changeset_start();
+** while( SQLITE_ROW==sqlite3changeset_next() ){
+** // Do something with change.
+** }
+** rc = sqlite3changeset_finalize();
+** if( rc!=SQLITE_OK ){
+** // An error has occurred
+** }
+*/
+int sqlite3changeset_finalize(sqlite3_changeset_iter *pIter);
+
+/*
+** CAPI3REF: Invert A Changeset
+**
+** This function is used to "invert" a changeset object. Applying an inverted
+** changeset to a database reverses the effects of applying the uninverted
+** changeset. Specifically:
+**
+** <ul>
+** <li> Each DELETE change is changed to an INSERT, and
+** <li> Each INSERT change is changed to a DELETE, and
+** <li> For each UPDATE change, the old.* and new.* values are exchanged.
+** </ul>
+**
+** This function does not change the order in which changes appear within
+** the changeset. It merely reverses the sense of each individual change.
+**
+** If successful, a pointer to a buffer containing the inverted changeset
+** is stored in *ppOut, the size of the same buffer is stored in *pnOut, and
+** SQLITE_OK is returned. If an error occurs, both *pnOut and *ppOut are
+** zeroed and an SQLite error code returned.
+**
+** It is the responsibility of the caller to eventually call sqlite3_free()
+** on the *ppOut pointer to free the buffer allocation following a successful
+** call to this function.
+**
+** WARNING/TODO: This function currently assumes that the input is a valid
+** changeset. If it is not, the results are undefined.
+*/
+int sqlite3changeset_invert(
+ int nIn, const void *pIn, /* Input changeset */
+ int *pnOut, void **ppOut /* OUT: Inverse of input */
+);
+
+/*
+** CAPI3REF: Concatenate Two Changeset Objects
+**
+** This function is used to concatenate two changesets, A and B, into a
+** single changeset. The result is a changeset equivalent to applying
+** changeset A followed by changeset B.
+**
+** This function combines the two input changesets using an
+** sqlite3_changegroup object. Calling it produces similar results as the
+** following code fragment:
+**
+** sqlite3_changegroup *pGrp;
+** rc = sqlite3_changegroup_new(&pGrp);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nA, pA);
+** if( rc==SQLITE_OK ) rc = sqlite3changegroup_add(pGrp, nB, pB);
+** if( rc==SQLITE_OK ){
+** rc = sqlite3changegroup_output(pGrp, pnOut, ppOut);
+** }else{
+** *ppOut = 0;
+** *pnOut = 0;
+** }
+**
+** Refer to the sqlite3_changegroup documentation below for details.
+*/
+int sqlite3changeset_concat(
+ int nA, /* Number of bytes in buffer pA */
+ void *pA, /* Pointer to buffer containing changeset A */
+ int nB, /* Number of bytes in buffer pB */
+ void *pB, /* Pointer to buffer containing changeset B */
+ int *pnOut, /* OUT: Number of bytes in output changeset */
+ void **ppOut /* OUT: Buffer containing output changeset */
+);
+
+
+/*
+** CAPI3REF: Changegroup Handle
+*/
+typedef struct sqlite3_changegroup sqlite3_changegroup;
+
+/*
+** CAPI3REF: Create A New Changegroup Object
+**
+** An sqlite3_changegroup object is used to combine two or more changesets
+** (or patchsets) into a single changeset (or patchset). A single changegroup
+** object may combine changesets or patchsets, but not both. The output is
+** always in the same format as the input.
+**
+** If successful, this function returns SQLITE_OK and populates (*pp) with
+** a pointer to a new sqlite3_changegroup object before returning. The caller
+** should eventually free the returned object using a call to
+** sqlite3changegroup_delete(). If an error occurs, an SQLite error code
+** (i.e. SQLITE_NOMEM) is returned and *pp is set to NULL.
+**
+** The usual usage pattern for an sqlite3_changegroup object is as follows:
+**
+** <ul>
+** <li> It is created using a call to sqlite3changegroup_new().
+**
+** <li> Zero or more changesets (or patchsets) are added to the object
+** by calling sqlite3changegroup_add().
+**
+** <li> The result of combining all input changesets together is obtained
+** by the application via a call to sqlite3changegroup_output().
+**
+** <li> The object is deleted using a call to sqlite3changegroup_delete().
+** </ul>
+**
+** Any number of calls to add() and output() may be made between the calls to
+** new() and delete(), and in any order.
+**
+** As well as the regular sqlite3changegroup_add() and
+** sqlite3changegroup_output() functions, also available are the streaming
+** versions sqlite3changegroup_add_strm() and sqlite3changegroup_output_strm().
+*/
+int sqlite3changegroup_new(sqlite3_changegroup **pp);
+
+/*
+** CAPI3REF: Add A Changeset To A Changegroup
+**
+** Add all changes within the changeset (or patchset) in buffer pData (size
+** nData bytes) to the changegroup.
+**
+** If the buffer contains a patchset, then all prior calls to this function
+** on the same changegroup object must also have specified patchsets. Or, if
+** the buffer contains a changeset, so must have the earlier calls to this
+** function. Otherwise, SQLITE_ERROR is returned and no changes are added
+** to the changegroup.
+**
+** Rows within the changeset and changegroup are identified by the values in
+** their PRIMARY KEY columns. A change in the changeset is considered to
+** apply to the same row as a change already present in the changegroup if
+** the two rows have the same primary key.
+**
+** Changes to rows that do not already appear in the changegroup are
+** simply copied into it. Or, if both the new changeset and the changegroup
+** contain changes that apply to a single row, the final contents of the
+** changegroup depends on the type of each change, as follows:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+** <tr><th style="white-space:pre">Existing Change </th>
+** <th style="white-space:pre">New Change </th>
+** <th>Output Change
+** <tr><td>INSERT <td>INSERT <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>INSERT <td>UPDATE <td>
+** The INSERT change remains in the changegroup. The values in the
+** INSERT change are modified as if the row was inserted by the
+** existing change and then updated according to the new change.
+** <tr><td>INSERT <td>DELETE <td>
+** The existing INSERT is removed from the changegroup. The DELETE is
+** not added.
+** <tr><td>UPDATE <td>INSERT <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>UPDATE <td>UPDATE <td>
+** The existing UPDATE remains within the changegroup. It is amended
+** so that the accompanying values are as if the row was updated once
+** by the existing change and then again by the new change.
+** <tr><td>UPDATE <td>DELETE <td>
+** The existing UPDATE is replaced by the new DELETE within the
+** changegroup.
+** <tr><td>DELETE <td>INSERT <td>
+** If one or more of the column values in the row inserted by the
+** new change differ from those in the row deleted by the existing
+** change, the existing DELETE is replaced by an UPDATE within the
+** changegroup. Otherwise, if the inserted row is exactly the same
+** as the deleted row, the existing DELETE is simply discarded.
+** <tr><td>DELETE <td>UPDATE <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** <tr><td>DELETE <td>DELETE <td>
+** The new change is ignored. This case does not occur if the new
+** changeset was recorded immediately after the changesets already
+** added to the changegroup.
+** </table>
+**
+** If the new changeset contains changes to a table that is already present
+** in the changegroup, then the number of columns and the position of the
+** primary key columns for the table must be consistent. If this is not the
+** case, this function fails with SQLITE_SCHEMA. If the input changeset
+** appears to be corrupt and the corruption is detected, SQLITE_CORRUPT is
+** returned. Or, if an out-of-memory condition occurs during processing, this
+** function returns SQLITE_NOMEM. In all cases, if an error occurs the
+** final contents of the changegroup is undefined.
+**
+** If no error occurs, SQLITE_OK is returned.
+*/
+int sqlite3changegroup_add(sqlite3_changegroup*, int nData, void *pData);
+
+/*
+** CAPI3REF: Obtain A Composite Changeset From A Changegroup
+**
+** Obtain a buffer containing a changeset (or patchset) representing the
+** current contents of the changegroup. If the inputs to the changegroup
+** were themselves changesets, the output is a changeset. Or, if the
+** inputs were patchsets, the output is also a patchset.
+**
+** As with the output of the sqlite3session_changeset() and
+** sqlite3session_patchset() functions, all changes related to a single
+** table are grouped together in the output of this function. Tables appear
+** in the same order as for the very first changeset added to the changegroup.
+** If the second or subsequent changesets added to the changegroup contain
+** changes for tables that do not appear in the first changeset, they are
+** appended onto the end of the output changeset, again in the order in
+** which they are first encountered.
+**
+** If an error occurs, an SQLite error code is returned and the output
+** variables (*pnData) and (*ppData) are set to 0. Otherwise, SQLITE_OK
+** is returned and the output variables are set to the size of and a
+** pointer to the output buffer, respectively. In this case it is the
+** responsibility of the caller to eventually free the buffer using a
+** call to sqlite3_free().
+*/
+int sqlite3changegroup_output(
+ sqlite3_changegroup*,
+ int *pnData, /* OUT: Size of output buffer in bytes */
+ void **ppData /* OUT: Pointer to output buffer */
+);
+
+/*
+** CAPI3REF: Delete A Changegroup Object
+*/
+void sqlite3changegroup_delete(sqlite3_changegroup*);
+
+/*
+** CAPI3REF: Apply A Changeset To A Database
+**
+** Apply a changeset to a database. This function attempts to update the
+** "main" database attached to handle db with the changes found in the
+** changeset passed via the second and third arguments.
+**
+** The fourth argument (xFilter) passed to this function is the "filter
+** callback". If it is not NULL, then for each table affected by at least one
+** change in the changeset, the filter callback is invoked with
+** the table name as the second argument, and a copy of the context pointer
+** passed as the sixth argument to this function as the first. If the "filter
+** callback" returns zero, then no attempt is made to apply any changes to
+** the table. Otherwise, if the return value is non-zero or the xFilter
+** argument to this function is NULL, all changes related to the table are
+** attempted.
+**
+** For each table that is not excluded by the filter callback, this function
+** tests that the target database contains a compatible table. A table is
+** considered compatible if all of the following are true:
+**
+** <ul>
+** <li> The table has the same name as the name recorded in the
+** changeset, and
+** <li> The table has the same number of columns as recorded in the
+** changeset, and
+** <li> The table has primary key columns in the same position as
+** recorded in the changeset.
+** </ul>
+**
+** If there is no compatible table, it is not an error, but none of the
+** changes associated with the table are applied. A warning message is issued
+** via the sqlite3_log() mechanism with the error code SQLITE_SCHEMA. At most
+** one such warning is issued for each table in the changeset.
+**
+** For each change for which there is a compatible table, an attempt is made
+** to modify the table contents according to the UPDATE, INSERT or DELETE
+** change. If a change cannot be applied cleanly, the conflict handler
+** function passed as the fifth argument to sqlite3changeset_apply() may be
+** invoked. A description of exactly when the conflict handler is invoked for
+** each type of change is below.
+**
+** Unlike the xFilter argument, xConflict may not be passed NULL. The results
+** of passing anything other than a valid function pointer as the xConflict
+** argument are undefined.
+**
+** Each time the conflict handler function is invoked, it must return one
+** of [SQLITE_CHANGESET_OMIT], [SQLITE_CHANGESET_ABORT] or
+** [SQLITE_CHANGESET_REPLACE]. SQLITE_CHANGESET_REPLACE may only be returned
+** if the second argument passed to the conflict handler is either
+** SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If the conflict-handler
+** returns an illegal value, any changes already made are rolled back and
+** the call to sqlite3changeset_apply() returns SQLITE_MISUSE. Different
+** actions are taken by sqlite3changeset_apply() depending on the value
+** returned by each invocation of the conflict-handler function. Refer to
+** the documentation for the three
+** [SQLITE_CHANGESET_OMIT|available return values] for details.
+**
+** <dl>
+** <dt>DELETE Changes<dd>
+** For each DELETE change, this function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
+** the changeset the row is deleted from the target database.
+**
+** If a row with matching primary key values is found, but one or more of
+** the non-primary key fields contains a value different from the original
+** row value stored in the changeset, the conflict-handler function is
+** invoked with [SQLITE_CHANGESET_DATA] as the second argument.
+**
+** If no row with matching primary key values is found in the database,
+** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
+** passed as the second argument.
+**
+** If the DELETE operation is attempted, but SQLite returns SQLITE_CONSTRAINT
+** (which can only happen if a foreign key constraint is violated), the
+** conflict-handler function is invoked with [SQLITE_CHANGESET_CONSTRAINT]
+** passed as the second argument. This includes the case where the DELETE
+** operation is attempted because an earlier call to the conflict handler
+** function returned [SQLITE_CHANGESET_REPLACE].
+**
+** <dt>INSERT Changes<dd>
+** For each INSERT change, an attempt is made to insert the new row into
+** the database.
+**
+** If the attempt to insert the row fails because the database already
+** contains a row with the same primary key values, the conflict handler
+** function is invoked with the second argument set to
+** [SQLITE_CHANGESET_CONFLICT].
+**
+** If the attempt to insert the row fails because of some other constraint
+** violation (e.g. NOT NULL or UNIQUE), the conflict handler function is
+** invoked with the second argument set to [SQLITE_CHANGESET_CONSTRAINT].
+** This includes the case where the INSERT operation is re-attempted because
+** an earlier call to the conflict handler function returned
+** [SQLITE_CHANGESET_REPLACE].
+**
+** <dt>UPDATE Changes<dd>
+** For each UPDATE change, this function checks if the target database
+** contains a row with the same primary key value (or values) as the
+** original row values stored in the changeset. If it does, and the values
+** stored in all non-primary key columns also match the values stored in
+** the changeset the row is updated within the target database.
+**
+** If a row with matching primary key values is found, but one or more of
+** the non-primary key fields contains a value different from an original
+** row value stored in the changeset, the conflict-handler function is
+** invoked with [SQLITE_CHANGESET_DATA] as the second argument. Since
+** UPDATE changes only contain values for non-primary key fields that are
+** to be modified, only those fields need to match the original values to
+** avoid the SQLITE_CHANGESET_DATA conflict-handler callback.
+**
+** If no row with matching primary key values is found in the database,
+** the conflict-handler function is invoked with [SQLITE_CHANGESET_NOTFOUND]
+** passed as the second argument.
+**
+** If the UPDATE operation is attempted, but SQLite returns
+** SQLITE_CONSTRAINT, the conflict-handler function is invoked with
+** [SQLITE_CHANGESET_CONSTRAINT] passed as the second argument.
+** This includes the case where the UPDATE operation is attempted after
+** an earlier call to the conflict handler function returned
+** [SQLITE_CHANGESET_REPLACE].
+** </dl>
+**
+** It is safe to execute SQL statements, including those that write to the
+** table that the callback related to, from within the xConflict callback.
+** This can be used to further customize the applications conflict
+** resolution strategy.
+**
+** All changes made by this function are enclosed in a savepoint transaction.
+** If any other error (aside from a constraint failure when attempting to
+** write to the target database) occurs, then the savepoint transaction is
+** rolled back, restoring the target database to its original state, and an
+** SQLite error code returned.
+*/
+int sqlite3changeset_apply(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int nChangeset, /* Size of changeset in bytes */
+ void *pChangeset, /* Changeset blob */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+);
+
+/*
+** CAPI3REF: Constants Passed To The Conflict Handler
+**
+** Values that may be passed as the second argument to a conflict-handler.
+**
+** <dl>
+** <dt>SQLITE_CHANGESET_DATA<dd>
+** The conflict handler is invoked with CHANGESET_DATA as the second argument
+** when processing a DELETE or UPDATE change if a row with the required
+** PRIMARY KEY fields is present in the database, but one or more other
+** (non primary-key) fields modified by the update do not contain the
+** expected "before" values.
+**
+** The conflicting row, in this case, is the database row with the matching
+** primary key.
+**
+** <dt>SQLITE_CHANGESET_NOTFOUND<dd>
+** The conflict handler is invoked with CHANGESET_NOTFOUND as the second
+** argument when processing a DELETE or UPDATE change if a row with the
+** required PRIMARY KEY fields is not present in the database.
+**
+** There is no conflicting row in this case. The results of invoking the
+** sqlite3changeset_conflict() API are undefined.
+**
+** <dt>SQLITE_CHANGESET_CONFLICT<dd>
+** CHANGESET_CONFLICT is passed as the second argument to the conflict
+** handler while processing an INSERT change if the operation would result
+** in duplicate primary key values.
+**
+** The conflicting row in this case is the database row with the matching
+** primary key.
+**
+** <dt>SQLITE_CHANGESET_FOREIGN_KEY<dd>
+** If foreign key handling is enabled, and applying a changeset leaves the
+** database in a state containing foreign key violations, the conflict
+** handler is invoked with CHANGESET_FOREIGN_KEY as the second argument
+** exactly once before the changeset is committed. If the conflict handler
+** returns CHANGESET_OMIT, the changes, including those that caused the
+** foreign key constraint violation, are committed. Or, if it returns
+** CHANGESET_ABORT, the changeset is rolled back.
+**
+** No current or conflicting row information is provided. The only function
+** it is possible to call on the supplied sqlite3_changeset_iter handle
+** is sqlite3changeset_fk_conflicts().
+**
+** <dt>SQLITE_CHANGESET_CONSTRAINT<dd>
+** If any other constraint violation occurs while applying a change (i.e.
+** a UNIQUE, CHECK or NOT NULL constraint), the conflict handler is
+** invoked with CHANGESET_CONSTRAINT as the second argument.
+**
+** There is no conflicting row in this case. The results of invoking the
+** sqlite3changeset_conflict() API are undefined.
+**
+** </dl>
+*/
+#define SQLITE_CHANGESET_DATA 1
+#define SQLITE_CHANGESET_NOTFOUND 2
+#define SQLITE_CHANGESET_CONFLICT 3
+#define SQLITE_CHANGESET_CONSTRAINT 4
+#define SQLITE_CHANGESET_FOREIGN_KEY 5
+
+/*
+** CAPI3REF: Constants Returned By The Conflict Handler
+**
+** A conflict handler callback must return one of the following three values.
+**
+** <dl>
+** <dt>SQLITE_CHANGESET_OMIT<dd>
+** If a conflict handler returns this value no special action is taken. The
+** change that caused the conflict is not applied. The session module
+** continues to the next change in the changeset.
+**
+** <dt>SQLITE_CHANGESET_REPLACE<dd>
+** This value may only be returned if the second argument to the conflict
+** handler was SQLITE_CHANGESET_DATA or SQLITE_CHANGESET_CONFLICT. If this
+** is not the case, any changes applied so far are rolled back and the
+** call to sqlite3changeset_apply() returns SQLITE_MISUSE.
+**
+** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_DATA conflict
+** handler, then the conflicting row is either updated or deleted, depending
+** on the type of change.
+**
+** If CHANGESET_REPLACE is returned by an SQLITE_CHANGESET_CONFLICT conflict
+** handler, then the conflicting row is removed from the database and a
+** second attempt to apply the change is made. If this second attempt fails,
+** the original row is restored to the database before continuing.
+**
+** <dt>SQLITE_CHANGESET_ABORT<dd>
+** If this value is returned, any changes applied so far are rolled back
+** and the call to sqlite3changeset_apply() returns SQLITE_ABORT.
+** </dl>
+*/
+#define SQLITE_CHANGESET_OMIT 0
+#define SQLITE_CHANGESET_REPLACE 1
+#define SQLITE_CHANGESET_ABORT 2
+
+/*
+** CAPI3REF: Streaming Versions of API functions.
+**
+** The six streaming API xxx_strm() functions serve similar purposes to the
+** corresponding non-streaming API functions:
+**
+** <table border=1 style="margin-left:8ex;margin-right:8ex">
+** <tr><th>Streaming function<th>Non-streaming equivalent</th>
+** <tr><td>sqlite3changeset_apply_str<td>[sqlite3changeset_apply]
+** <tr><td>sqlite3changeset_concat_str<td>[sqlite3changeset_concat]
+** <tr><td>sqlite3changeset_invert_str<td>[sqlite3changeset_invert]
+** <tr><td>sqlite3changeset_start_str<td>[sqlite3changeset_start]
+** <tr><td>sqlite3session_changeset_str<td>[sqlite3session_changeset]
+** <tr><td>sqlite3session_patchset_str<td>[sqlite3session_patchset]
+** </table>
+**
+** Non-streaming functions that accept changesets (or patchsets) as input
+** require that the entire changeset be stored in a single buffer in memory.
+** Similarly, those that return a changeset or patchset do so by returning
+** a pointer to a single large buffer allocated using sqlite3_malloc().
+** Normally this is convenient. However, if an application running in a
+** low-memory environment is required to handle very large changesets, the
+** large contiguous memory allocations required can become onerous.
+**
+** In order to avoid this problem, instead of a single large buffer, input
+** is passed to a streaming API functions by way of a callback function that
+** the sessions module invokes to incrementally request input data as it is
+** required. In all cases, a pair of API function parameters such as
+**
+** <pre>
+** &nbsp; int nChangeset,
+** &nbsp; void *pChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** &nbsp; int (*xInput)(void *pIn, void *pData, int *pnData),
+** &nbsp; void *pIn,
+** </pre>
+**
+** Each time the xInput callback is invoked by the sessions module, the first
+** argument passed is a copy of the supplied pIn context pointer. The second
+** argument, pData, points to a buffer (*pnData) bytes in size. Assuming no
+** error occurs the xInput method should copy up to (*pnData) bytes of data
+** into the buffer and set (*pnData) to the actual number of bytes copied
+** before returning SQLITE_OK. If the input is completely exhausted, (*pnData)
+** should be set to zero to indicate this. Or, if an error occurs, an SQLite
+** error code should be returned. In all cases, if an xInput callback returns
+** an error, all processing is abandoned and the streaming API function
+** returns a copy of the error code to the caller.
+**
+** In the case of sqlite3changeset_start_strm(), the xInput callback may be
+** invoked by the sessions module at any point during the lifetime of the
+** iterator. If such an xInput callback returns an error, the iterator enters
+** an error state, whereby all subsequent calls to iterator functions
+** immediately fail with the same error code as returned by xInput.
+**
+** Similarly, streaming API functions that return changesets (or patchsets)
+** return them in chunks by way of a callback function instead of via a
+** pointer to a single large buffer. In this case, a pair of parameters such
+** as:
+**
+** <pre>
+** &nbsp; int *pnChangeset,
+** &nbsp; void **ppChangeset,
+** </pre>
+**
+** Is replaced by:
+**
+** <pre>
+** &nbsp; int (*xOutput)(void *pOut, const void *pData, int nData),
+** &nbsp; void *pOut
+** </pre>
+**
+** The xOutput callback is invoked zero or more times to return data to
+** the application. The first parameter passed to each call is a copy of the
+** pOut pointer supplied by the application. The second parameter, pData,
+** points to a buffer nData bytes in size containing the chunk of output
+** data being returned. If the xOutput callback successfully processes the
+** supplied data, it should return SQLITE_OK to indicate success. Otherwise,
+** it should return some other SQLite error code. In this case processing
+** is immediately abandoned and the streaming API function returns a copy
+** of the xOutput error code to the application.
+**
+** The sessions module never invokes an xOutput callback with the third
+** parameter set to a value less than or equal to zero. Other than this,
+** no guarantees are made as to the size of the chunks of data returned.
+*/
+int sqlite3changeset_apply_strm(
+ sqlite3 *db, /* Apply change to "main" db of this handle */
+ int (*xInput)(void *pIn, void *pData, int *pnData), /* Input function */
+ void *pIn, /* First arg for xInput */
+ int(*xFilter)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ const char *zTab /* Table name */
+ ),
+ int(*xConflict)(
+ void *pCtx, /* Copy of sixth arg to _apply() */
+ int eConflict, /* DATA, MISSING, CONFLICT, CONSTRAINT */
+ sqlite3_changeset_iter *p /* Handle describing change and conflict */
+ ),
+ void *pCtx /* First argument passed to xConflict */
+);
+int sqlite3changeset_concat_strm(
+ int (*xInputA)(void *pIn, void *pData, int *pnData),
+ void *pInA,
+ int (*xInputB)(void *pIn, void *pData, int *pnData),
+ void *pInB,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_invert_strm(
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changeset_start_strm(
+ sqlite3_changeset_iter **pp,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3session_changeset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3session_patchset_strm(
+ sqlite3_session *pSession,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+int sqlite3changegroup_add_strm(sqlite3_changegroup*,
+ int (*xInput)(void *pIn, void *pData, int *pnData),
+ void *pIn
+);
+int sqlite3changegroup_output_strm(sqlite3_changegroup*,
+ int (*xOutput)(void *pOut, const void *pData, int nData),
+ void *pOut
+);
+
+
+/*
+** Make sure we can call this stuff from C++.
+*/
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !defined(__SQLITESESSION_H_) && defined(SQLITE_ENABLE_SESSION) */
+
+/******** End of sqlite3session.h *********/
+/******** Begin file fts5.h *********/
+/*
+** 2014 May 31
+**
+** The author disclaims copyright to this source code. In place of
+** a legal notice, here is a blessing:
+**
+** May you do good and not evil.
+** May you find forgiveness for yourself and forgive others.
+** May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** Interfaces to extend FTS5. Using the interfaces defined in this file,
+** FTS5 may be extended with:
+**
+** * custom tokenizers, and
+** * custom auxiliary functions.
+*/
+
+
+#ifndef _FTS5_H
+#define _FTS5_H
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*************************************************************************
+** CUSTOM AUXILIARY FUNCTIONS
+**
+** Virtual table implementations may overload SQL functions by implementing
+** the sqlite3_module.xFindFunction() method.
+*/
+
+typedef struct Fts5ExtensionApi Fts5ExtensionApi;
+typedef struct Fts5Context Fts5Context;
+typedef struct Fts5PhraseIter Fts5PhraseIter;
+
+typedef void (*fts5_extension_function)(
+ const Fts5ExtensionApi *pApi, /* API offered by current FTS version */
+ Fts5Context *pFts, /* First arg to pass to pApi functions */
+ sqlite3_context *pCtx, /* Context for returning result/error */
+ int nVal, /* Number of values in apVal[] array */
+ sqlite3_value **apVal /* Array of trailing arguments */
+);
+
+struct Fts5PhraseIter {
+ const unsigned char *a;
+ const unsigned char *b;
+};
+
+/*
+** EXTENSION API FUNCTIONS
+**
+** xUserData(pFts):
+** Return a copy of the context pointer the extension function was
+** registered with.
+**
+** xColumnTotalSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the FTS5 table. Or, if iCol is
+** non-negative but less than the number of columns in the table, return
+** the total number of tokens in column iCol, considering all rows in
+** the FTS5 table.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** xColumnCount(pFts):
+** Return the number of columns in the table.
+**
+** xColumnSize(pFts, iCol, pnToken):
+** If parameter iCol is less than zero, set output variable *pnToken
+** to the total number of tokens in the current row. Or, if iCol is
+** non-negative but less than the number of columns in the table, set
+** *pnToken to the number of tokens in column iCol of the current row.
+**
+** If parameter iCol is greater than or equal to the number of columns
+** in the table, SQLITE_RANGE is returned. Or, if an error occurs (e.g.
+** an OOM condition or IO error), an appropriate SQLite error code is
+** returned.
+**
+** This function may be quite inefficient if used with an FTS5 table
+** created with the "columnsize=0" option.
+**
+** xColumnText:
+** This function attempts to retrieve the text of column iCol of the
+** current document. If successful, (*pz) is set to point to a buffer
+** containing the text in utf-8 encoding, (*pn) is set to the size in bytes
+** (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
+** if an error occurs, an SQLite error code is returned and the final values
+** of (*pz) and (*pn) are undefined.
+**
+** xPhraseCount:
+** Returns the number of phrases in the current query expression.
+**
+** xPhraseSize:
+** Returns the number of tokens in phrase iPhrase of the query. Phrases
+** are numbered starting from zero.
+**
+** xInstCount:
+** Set *pnInst to the total number of occurrences of all phrases within
+** the query within the current row. Return SQLITE_OK if successful, or
+** an error code (i.e. SQLITE_NOMEM) if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always returns 0.
+**
+** xInst:
+** Query for the details of phrase match iIdx within the current row.
+** Phrase matches are numbered starting from zero, so the iIdx argument
+** should be greater than or equal to zero and smaller than the value
+** output by xInstCount().
+**
+** Usually, output parameter *piPhrase is set to the phrase number, *piCol
+** to the column in which it occurs and *piOff the token offset of the
+** first token of the phrase. The exception is if the table was created
+** with the offsets=0 option specified. In this case *piOff is always
+** set to -1.
+**
+** Returns SQLITE_OK if successful, or an error code (i.e. SQLITE_NOMEM)
+** if an error occurs.
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option.
+**
+** xRowid:
+** Returns the rowid of the current row.
+**
+** xTokenize:
+** Tokenize text using the tokenizer belonging to the FTS5 table.
+**
+** xQueryPhrase(pFts5, iPhrase, pUserData, xCallback):
+** This API function is used to query the FTS table for phrase iPhrase
+** of the current query. Specifically, a query equivalent to:
+**
+** ... FROM ftstable WHERE ftstable MATCH $p ORDER BY rowid
+**
+** with $p set to a phrase equivalent to the phrase iPhrase of the
+** current query is executed. Any column filter that applies to
+** phrase iPhrase of the current query is included in $p. For each
+** row visited, the callback function passed as the fourth argument
+** is invoked. The context and API objects passed to the callback
+** function may be used to access the properties of each matched row.
+** Invoking Api.xUserData() returns a copy of the pointer passed as
+** the third argument to pUserData.
+**
+** If the callback function returns any value other than SQLITE_OK, the
+** query is abandoned and the xQueryPhrase function returns immediately.
+** If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
+** Otherwise, the error code is propagated upwards.
+**
+** If the query runs to completion without incident, SQLITE_OK is returned.
+** Or, if some error occurs before the query completes or is aborted by
+** the callback, an SQLite error code is returned.
+**
+**
+** xSetAuxdata(pFts5, pAux, xDelete)
+**
+** Save the pointer passed as the second argument as the extension functions
+** "auxiliary data". The pointer may then be retrieved by the current or any
+** future invocation of the same fts5 extension function made as part of
+** of the same MATCH query using the xGetAuxdata() API.
+**
+** Each extension function is allocated a single auxiliary data slot for
+** each FTS query (MATCH expression). If the extension function is invoked
+** more than once for a single FTS query, then all invocations share a
+** single auxiliary data context.
+**
+** If there is already an auxiliary data pointer when this function is
+** invoked, then it is replaced by the new pointer. If an xDelete callback
+** was specified along with the original pointer, it is invoked at this
+** point.
+**
+** The xDelete callback, if one is specified, is also invoked on the
+** auxiliary data pointer after the FTS5 query has finished.
+**
+** If an error (e.g. an OOM condition) occurs within this function, an
+** the auxiliary data is set to NULL and an error code returned. If the
+** xDelete parameter was not NULL, it is invoked on the auxiliary data
+** pointer before returning.
+**
+**
+** xGetAuxdata(pFts5, bClear)
+**
+** Returns the current auxiliary data pointer for the fts5 extension
+** function. See the xSetAuxdata() method for details.
+**
+** If the bClear argument is non-zero, then the auxiliary data is cleared
+** (set to NULL) before this function returns. In this case the xDelete,
+** if any, is not invoked.
+**
+**
+** xRowCount(pFts5, pnRow)
+**
+** This function is used to retrieve the total number of rows in the table.
+** In other words, the same value that would be returned by:
+**
+** SELECT count(*) FROM ftstable;
+**
+** xPhraseFirst()
+** This function is used, along with type Fts5PhraseIter and the xPhraseNext
+** method, to iterate through all instances of a single query phrase within
+** the current row. This is the same information as is accessible via the
+** xInstCount/xInst APIs. While the xInstCount/xInst APIs are more convenient
+** to use, this API may be faster under some circumstances. To iterate
+** through instances of phrase iPhrase, use the following code:
+**
+** Fts5PhraseIter iter;
+** int iCol, iOff;
+** for(pApi->xPhraseFirst(pFts, iPhrase, &iter, &iCol, &iOff);
+** iCol>=0;
+** pApi->xPhraseNext(pFts, &iter, &iCol, &iOff)
+** ){
+** // An instance of phrase iPhrase at offset iOff of column iCol
+** }
+**
+** The Fts5PhraseIter structure is defined above. Applications should not
+** modify this structure directly - it should only be used as shown above
+** with the xPhraseFirst() and xPhraseNext() API methods (and by
+** xPhraseFirstColumn() and xPhraseNextColumn() as illustrated below).
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" or "detail=column" option. If the FTS5 table is created
+** with either "detail=none" or "detail=column" and "content=" option
+** (i.e. if it is a contentless table), then this API always iterates
+** through an empty set (all calls to xPhraseFirst() set iCol to -1).
+**
+** xPhraseNext()
+** See xPhraseFirst above.
+**
+** xPhraseFirstColumn()
+** This function and xPhraseNextColumn() are similar to the xPhraseFirst()
+** and xPhraseNext() APIs described above. The difference is that instead
+** of iterating through all instances of a phrase in the current row, these
+** APIs are used to iterate through the set of columns in the current row
+** that contain one or more instances of a specified phrase. For example:
+**
+** Fts5PhraseIter iter;
+** int iCol;
+** for(pApi->xPhraseFirstColumn(pFts, iPhrase, &iter, &iCol);
+** iCol>=0;
+** pApi->xPhraseNextColumn(pFts, &iter, &iCol)
+** ){
+** // Column iCol contains at least one instance of phrase iPhrase
+** }
+**
+** This API can be quite slow if used with an FTS5 table created with the
+** "detail=none" option. If the FTS5 table is created with either
+** "detail=none" "content=" option (i.e. if it is a contentless table),
+** then this API always iterates through an empty set (all calls to
+** xPhraseFirstColumn() set iCol to -1).
+**
+** The information accessed using this API and its companion
+** xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
+** (or xInst/xInstCount). The chief advantage of this API is that it is
+** significantly more efficient than those alternatives when used with
+** "detail=column" tables.
+**
+** xPhraseNextColumn()
+** See xPhraseFirstColumn above.
+*/
+struct Fts5ExtensionApi {
+ int iVersion; /* Currently always set to 3 */
+
+ void *(*xUserData)(Fts5Context*);
+
+ int (*xColumnCount)(Fts5Context*);
+ int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
+ int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);
+
+ int (*xTokenize)(Fts5Context*,
+ const char *pText, int nText, /* Text to tokenize */
+ void *pCtx, /* Context passed to xToken() */
+ int (*xToken)(void*, int, const char*, int, int, int) /* Callback */
+ );
+
+ int (*xPhraseCount)(Fts5Context*);
+ int (*xPhraseSize)(Fts5Context*, int iPhrase);
+
+ int (*xInstCount)(Fts5Context*, int *pnInst);
+ int (*xInst)(Fts5Context*, int iIdx, int *piPhrase, int *piCol, int *piOff);
+
+ sqlite3_int64 (*xRowid)(Fts5Context*);
+ int (*xColumnText)(Fts5Context*, int iCol, const char **pz, int *pn);
+ int (*xColumnSize)(Fts5Context*, int iCol, int *pnToken);
+
+ int (*xQueryPhrase)(Fts5Context*, int iPhrase, void *pUserData,
+ int(*)(const Fts5ExtensionApi*,Fts5Context*,void*)
+ );
+ int (*xSetAuxdata)(Fts5Context*, void *pAux, void(*xDelete)(void*));
+ void *(*xGetAuxdata)(Fts5Context*, int bClear);
+
+ int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
+ void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);
+
+ int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
+ void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);
+};
+
+/*
+** CUSTOM AUXILIARY FUNCTIONS
+*************************************************************************/
+
+/*************************************************************************
+** CUSTOM TOKENIZERS
+**
+** Applications may also register custom tokenizer types. A tokenizer
+** is registered by providing fts5 with a populated instance of the
+** following structure. All structure methods must be defined, setting
+** any member of the fts5_tokenizer struct to NULL leads to undefined
+** behaviour. The structure methods are expected to function as follows:
+**
+** xCreate:
+** This function is used to allocate and initialize a tokenizer instance.
+** A tokenizer instance is required to actually tokenize text.
+**
+** The first argument passed to this function is a copy of the (void*)
+** pointer provided by the application when the fts5_tokenizer object
+** was registered with FTS5 (the third argument to xCreateTokenizer()).
+** The second and third arguments are an array of nul-terminated strings
+** containing the tokenizer arguments, if any, specified following the
+** tokenizer name as part of the CREATE VIRTUAL TABLE statement used
+** to create the FTS5 table.
+**
+** The final argument is an output variable. If successful, (*ppOut)
+** should be set to point to the new tokenizer handle and SQLITE_OK
+** returned. If an error occurs, some value other than SQLITE_OK should
+** be returned. In this case, fts5 assumes that the final value of *ppOut
+** is undefined.
+**
+** xDelete:
+** This function is invoked to delete a tokenizer handle previously
+** allocated using xCreate(). Fts5 guarantees that this function will
+** be invoked exactly once for each successful call to xCreate().
+**
+** xTokenize:
+** This function is expected to tokenize the nText byte string indicated
+** by argument pText. pText may or may not be nul-terminated. The first
+** argument passed to this function is a pointer to an Fts5Tokenizer object
+** returned by an earlier call to xCreate().
+**
+** The second argument indicates the reason that FTS5 is requesting
+** tokenization of the supplied text. This is always one of the following
+** four values:
+**
+** <ul><li> <b>FTS5_TOKENIZE_DOCUMENT</b> - A document is being inserted into
+** or removed from the FTS table. The tokenizer is being invoked to
+** determine the set of tokens to add to (or delete from) the
+** FTS index.
+**
+** <li> <b>FTS5_TOKENIZE_QUERY</b> - A MATCH query is being executed
+** against the FTS index. The tokenizer is being called to tokenize
+** a bareword or quoted string specified as part of the query.
+**
+** <li> <b>(FTS5_TOKENIZE_QUERY | FTS5_TOKENIZE_PREFIX)</b> - Same as
+** FTS5_TOKENIZE_QUERY, except that the bareword or quoted string is
+** followed by a "*" character, indicating that the last token
+** returned by the tokenizer will be treated as a token prefix.
+**
+** <li> <b>FTS5_TOKENIZE_AUX</b> - The tokenizer is being invoked to
+** satisfy an fts5_api.xTokenize() request made by an auxiliary
+** function. Or an fts5_api.xColumnSize() request made by the same
+** on a columnsize=0 database.
+** </ul>
+**
+** For each token in the input string, the supplied callback xToken() must
+** be invoked. The first argument to it should be a copy of the pointer
+** passed as the second argument to xTokenize(). The third and fourth
+** arguments are a pointer to a buffer containing the token text, and the
+** size of the token in bytes. The 4th and 5th arguments are the byte offsets
+** of the first byte of and first byte immediately following the text from
+** which the token is derived within the input.
+**
+** The second argument passed to the xToken() callback ("tflags") should
+** normally be set to 0. The exception is if the tokenizer supports
+** synonyms. In this case see the discussion below for details.
+**
+** FTS5 assumes the xToken() callback is invoked for each token in the
+** order that they occur within the input text.
+**
+** If an xToken() callback returns any value other than SQLITE_OK, then
+** the tokenization should be abandoned and the xTokenize() method should
+** immediately return a copy of the xToken() return value. Or, if the
+** input buffer is exhausted, xTokenize() should return SQLITE_OK. Finally,
+** if an error occurs with the xTokenize() implementation itself, it
+** may abandon the tokenization and return any error code other than
+** SQLITE_OK or SQLITE_DONE.
+**
+** SYNONYM SUPPORT
+**
+** Custom tokenizers may also support synonyms. Consider a case in which a
+** user wishes to query for a phrase such as "first place". Using the
+** built-in tokenizers, the FTS5 query 'first + place' will match instances
+** of "first place" within the document set, but not alternative forms
+** such as "1st place". In some applications, it would be better to match
+** all instances of "first place" or "1st place" regardless of which form
+** the user specified in the MATCH query text.
+**
+** There are several ways to approach this in FTS5:
+**
+** <ol><li> By mapping all synonyms to a single token. In this case, the
+** In the above example, this means that the tokenizer returns the
+** same token for inputs "first" and "1st". Say that token is in
+** fact "first", so that when the user inserts the document "I won
+** 1st place" entries are added to the index for tokens "i", "won",
+** "first" and "place". If the user then queries for '1st + place',
+** the tokenizer substitutes "first" for "1st" and the query works
+** as expected.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** In this case, when tokenizing query text, the tokenizer may
+** provide multiple synonyms for a single term within the document.
+** FTS5 then queries the index for each synonym individually. For
+** example, faced with the query:
+**
+** <codeblock>
+** ... MATCH 'first place'</codeblock>
+**
+** the tokenizer offers both "1st" and "first" as synonyms for the
+** first token in the MATCH query and FTS5 effectively runs a query
+** similar to:
+**
+** <codeblock>
+** ... MATCH '(first OR 1st) place'</codeblock>
+**
+** except that, for the purposes of auxiliary functions, the query
+** still appears to contain just two phrases - "(first OR 1st)"
+** being treated as a single phrase.
+**
+** <li> By adding multiple synonyms for a single term to the FTS index.
+** Using this method, when tokenizing document text, the tokenizer
+** provides multiple synonyms for each token. So that when a
+** document such as "I won first place" is tokenized, entries are
+** added to the FTS index for "i", "won", "first", "1st" and
+** "place".
+**
+** This way, even if the tokenizer does not provide synonyms
+** when tokenizing query text (it should not - to do would be
+** inefficient), it doesn't matter if the user queries for
+** 'first + place' or '1st + place', as there are entires in the
+** FTS index corresponding to both forms of the first token.
+** </ol>
+**
+** Whether it is parsing document or query text, any call to xToken that
+** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit
+** is considered to supply a synonym for the previous token. For example,
+** when parsing the document "I won first place", a tokenizer that supports
+** synonyms would call xToken() 5 times, as follows:
+**
+** <codeblock>
+** xToken(pCtx, 0, "i", 1, 0, 1);
+** xToken(pCtx, 0, "won", 3, 2, 5);
+** xToken(pCtx, 0, "first", 5, 6, 11);
+** xToken(pCtx, FTS5_TOKEN_COLOCATED, "1st", 3, 6, 11);
+** xToken(pCtx, 0, "place", 5, 12, 17);
+**</codeblock>
+**
+** It is an error to specify the FTS5_TOKEN_COLOCATED flag the first time
+** xToken() is called. Multiple synonyms may be specified for a single token
+** by making multiple calls to xToken(FTS5_TOKEN_COLOCATED) in sequence.
+** There is no limit to the number of synonyms that may be provided for a
+** single token.
+**
+** In many cases, method (1) above is the best approach. It does not add
+** extra data to the FTS index or require FTS5 to query for multiple terms,
+** so it is efficient in terms of disk space and query speed. However, it
+** does not support prefix queries very well. If, as suggested above, the
+** token "first" is subsituted for "1st" by the tokenizer, then the query:
+**
+** <codeblock>
+** ... MATCH '1s*'</codeblock>
+**
+** will not match documents that contain the token "1st" (as the tokenizer
+** will probably not map "1s" to any prefix of "first").
+**
+** For full prefix support, method (3) may be preferred. In this case,
+** because the index contains entries for both "first" and "1st", prefix
+** queries such as 'fi*' or '1s*' will match correctly. However, because
+** extra entries are added to the FTS index, this method uses more space
+** within the database.
+**
+** Method (2) offers a midpoint between (1) and (3). Using this method,
+** a query such as '1s*' will match documents that contain the literal
+** token "1st", but not "first" (assuming the tokenizer is not able to
+** provide synonyms for prefixes). However, a non-prefix query like '1st'
+** will match against "1st" and "first". This method does not require
+** extra disk space, as no extra entries are added to the FTS index.
+** On the other hand, it may require more CPU cycles to run MATCH queries,
+** as separate queries of the FTS index are required for each synonym.
+**
+** When using methods (2) or (3), it is important that the tokenizer only
+** provide synonyms when tokenizing document text (method (2)) or query
+** text (method (3)), not both. Doing so will not cause any errors, but is
+** inefficient.
+*/
+typedef struct Fts5Tokenizer Fts5Tokenizer;
+typedef struct fts5_tokenizer fts5_tokenizer;
+struct fts5_tokenizer {
+ int (*xCreate)(void*, const char **azArg, int nArg, Fts5Tokenizer **ppOut);
+ void (*xDelete)(Fts5Tokenizer*);
+ int (*xTokenize)(Fts5Tokenizer*,
+ void *pCtx,
+ int flags, /* Mask of FTS5_TOKENIZE_* flags */
+ const char *pText, int nText,
+ int (*xToken)(
+ void *pCtx, /* Copy of 2nd argument to xTokenize() */
+ int tflags, /* Mask of FTS5_TOKEN_* flags */
+ const char *pToken, /* Pointer to buffer containing token */
+ int nToken, /* Size of token in bytes */
+ int iStart, /* Byte offset of token within input text */
+ int iEnd /* Byte offset of end of token within input text */
+ )
+ );
+};
+
+/* Flags that may be passed as the third argument to xTokenize() */
+#define FTS5_TOKENIZE_QUERY 0x0001
+#define FTS5_TOKENIZE_PREFIX 0x0002
+#define FTS5_TOKENIZE_DOCUMENT 0x0004
+#define FTS5_TOKENIZE_AUX 0x0008
+
+/* Flags that may be passed by the tokenizer implementation back to FTS5
+** as the third argument to the supplied xToken callback. */
+#define FTS5_TOKEN_COLOCATED 0x0001 /* Same position as prev. token */
+
+/*
+** END OF CUSTOM TOKENIZERS
+*************************************************************************/
+
+/*************************************************************************
+** FTS5 EXTENSION REGISTRATION API
+*/
+typedef struct fts5_api fts5_api;
+struct fts5_api {
+ int iVersion; /* Currently always set to 2 */
+
+ /* Create a new tokenizer */
+ int (*xCreateTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_tokenizer *pTokenizer,
+ void (*xDestroy)(void*)
+ );
+
+ /* Find an existing tokenizer */
+ int (*xFindTokenizer)(
+ fts5_api *pApi,
+ const char *zName,
+ void **ppContext,
+ fts5_tokenizer *pTokenizer
+ );
+
+ /* Create a new auxiliary function */
+ int (*xCreateFunction)(
+ fts5_api *pApi,
+ const char *zName,
+ void *pContext,
+ fts5_extension_function xFunction,
+ void (*xDestroy)(void*)
+ );
+};
+
+/*
+** END OF REGISTRATION API
+*************************************************************************/
+
+#ifdef __cplusplus
+} /* end of the 'extern "C"' block */
+#endif
+
+#endif /* _FTS5_H */
+
+/******** End of fts5.h *********/
diff --git a/lib/sqlite/sqlite3ext.h b/lib/sqlite/sqlite3ext.h
index 50dd5b605554..ce87e74690b2 100644
--- a/lib/sqlite/sqlite3ext.h
+++ b/lib/sqlite/sqlite3ext.h
@@ -15,12 +15,10 @@
** as extensions by SQLite should #include this file instead of
** sqlite3.h.
*/
-#ifndef _SQLITE3EXT_H_
-#define _SQLITE3EXT_H_
+#ifndef SQLITE3EXT_H
+#define SQLITE3EXT_H
#include "sqlite3.h"
-typedef struct sqlite3_api_routines sqlite3_api_routines;
-
/*
** The following structure holds pointers to all of the SQLite API
** routines.
@@ -28,7 +26,7 @@ typedef struct sqlite3_api_routines sqlite3_api_routines;
** WARNING: In order to maintain backwards compatibility, add new
** interfaces to the end of this structure only. If you insert new
** interfaces in the middle of this structure, then older different
-** versions of SQLite will not be able to load each others' shared
+** versions of SQLite will not be able to load each other's shared
** libraries!
*/
struct sqlite3_api_routines {
@@ -49,8 +47,10 @@ struct sqlite3_api_routines {
int (*busy_timeout)(sqlite3*,int ms);
int (*changes)(sqlite3*);
int (*close)(sqlite3*);
- int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
- int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
+ int (*collation_needed)(sqlite3*,void*,void(*)(void*,sqlite3*,
+ int eTextRep,const char*));
+ int (*collation_needed16)(sqlite3*,void*,void(*)(void*,sqlite3*,
+ int eTextRep,const void*));
const void * (*column_blob)(sqlite3_stmt*,int iCol);
int (*column_bytes)(sqlite3_stmt*,int iCol);
int (*column_bytes16)(sqlite3_stmt*,int iCol);
@@ -75,10 +75,18 @@ struct sqlite3_api_routines {
void * (*commit_hook)(sqlite3*,int(*)(void*),void*);
int (*complete)(const char*sql);
int (*complete16)(const void*sql);
- int (*create_collation)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*));
- int (*create_collation16)(sqlite3*,const void*,int,void*,int(*)(void*,int,const void*,int,const void*));
- int (*create_function)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
- int (*create_function16)(sqlite3*,const void*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*));
+ int (*create_collation)(sqlite3*,const char*,int,void*,
+ int(*)(void*,int,const void*,int,const void*));
+ int (*create_collation16)(sqlite3*,const void*,int,void*,
+ int(*)(void*,int,const void*,int,const void*));
+ int (*create_function)(sqlite3*,const char*,int,int,void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*));
+ int (*create_function16)(sqlite3*,const void*,int,int,void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*));
int (*create_module)(sqlite3*,const char*,const sqlite3_module*,void*);
int (*data_count)(sqlite3_stmt*pStmt);
sqlite3 * (*db_handle)(sqlite3_stmt*);
@@ -123,16 +131,19 @@ struct sqlite3_api_routines {
void (*result_text16le)(sqlite3_context*,const void*,int,void(*)(void*));
void (*result_value)(sqlite3_context*,sqlite3_value*);
void * (*rollback_hook)(sqlite3*,void(*)(void*),void*);
- int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,const char*,const char*),void*);
+ int (*set_authorizer)(sqlite3*,int(*)(void*,int,const char*,const char*,
+ const char*,const char*),void*);
void (*set_auxdata)(sqlite3_context*,int,void*,void (*)(void*));
char * (*snprintf)(int,char*,const char*,...);
int (*step)(sqlite3_stmt*);
- int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,char const**,char const**,int*,int*,int*);
+ int (*table_column_metadata)(sqlite3*,const char*,const char*,const char*,
+ char const**,char const**,int*,int*,int*);
void (*thread_cleanup)(void);
int (*total_changes)(sqlite3*);
void * (*trace)(sqlite3*,void(*xTrace)(void*,const char*),void*);
int (*transfer_bindings)(sqlite3_stmt*,sqlite3_stmt*);
- void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,sqlite_int64),void*);
+ void * (*update_hook)(sqlite3*,void(*)(void*,int ,char const*,char const*,
+ sqlite_int64),void*);
void * (*user_data)(sqlite3_context*);
const void * (*value_blob)(sqlite3_value*);
int (*value_bytes)(sqlite3_value*);
@@ -154,15 +165,19 @@ struct sqlite3_api_routines {
int (*prepare16_v2)(sqlite3*,const void*,int,sqlite3_stmt**,const void**);
int (*clear_bindings)(sqlite3_stmt*);
/* Added by 3.4.1 */
- int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,void (*xDestroy)(void *));
+ int (*create_module_v2)(sqlite3*,const char*,const sqlite3_module*,void*,
+ void (*xDestroy)(void *));
/* Added by 3.5.0 */
int (*bind_zeroblob)(sqlite3_stmt*,int,int);
int (*blob_bytes)(sqlite3_blob*);
int (*blob_close)(sqlite3_blob*);
- int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,int,sqlite3_blob**);
+ int (*blob_open)(sqlite3*,const char*,const char*,const char*,sqlite3_int64,
+ int,sqlite3_blob**);
int (*blob_read)(sqlite3_blob*,void*,int,int);
int (*blob_write)(sqlite3_blob*,const void*,int,int);
- int (*create_collation_v2)(sqlite3*,const char*,int,void*,int(*)(void*,int,const void*,int,const void*),void(*)(void*));
+ int (*create_collation_v2)(sqlite3*,const char*,int,void*,
+ int(*)(void*,int,const void*,int,const void*),
+ void(*)(void*));
int (*file_control)(sqlite3*,const char*,int,void*);
sqlite3_int64 (*memory_highwater)(int);
sqlite3_int64 (*memory_used)(void);
@@ -198,7 +213,11 @@ struct sqlite3_api_routines {
int (*backup_step)(sqlite3_backup*,int);
const char *(*compileoption_get)(int);
int (*compileoption_used)(const char*);
- int (*create_function_v2)(sqlite3*,const char*,int,int,void*,void (*xFunc)(sqlite3_context*,int,sqlite3_value**),void (*xStep)(sqlite3_context*,int,sqlite3_value**),void (*xFinal)(sqlite3_context*),void(*xDestroy)(void*));
+ int (*create_function_v2)(sqlite3*,const char*,int,int,void*,
+ void (*xFunc)(sqlite3_context*,int,sqlite3_value**),
+ void (*xStep)(sqlite3_context*,int,sqlite3_value**),
+ void (*xFinal)(sqlite3_context*),
+ void(*xDestroy)(void*));
int (*db_config)(sqlite3*,int,...);
sqlite3_mutex *(*db_mutex)(sqlite3*);
int (*db_status)(sqlite3*,int,int*,int*,int);
@@ -215,11 +234,69 @@ struct sqlite3_api_routines {
int (*blob_reopen)(sqlite3_blob*,sqlite3_int64);
int (*vtab_config)(sqlite3*,int op,...);
int (*vtab_on_conflict)(sqlite3*);
+ /* Version 3.7.16 and later */
+ int (*close_v2)(sqlite3*);
+ const char *(*db_filename)(sqlite3*,const char*);
+ int (*db_readonly)(sqlite3*,const char*);
+ int (*db_release_memory)(sqlite3*);
+ const char *(*errstr)(int);
+ int (*stmt_busy)(sqlite3_stmt*);
+ int (*stmt_readonly)(sqlite3_stmt*);
+ int (*stricmp)(const char*,const char*);
+ int (*uri_boolean)(const char*,const char*,int);
+ sqlite3_int64 (*uri_int64)(const char*,const char*,sqlite3_int64);
+ const char *(*uri_parameter)(const char*,const char*);
+ char *(*vsnprintf)(int,char*,const char*,va_list);
+ int (*wal_checkpoint_v2)(sqlite3*,const char*,int,int*,int*);
+ /* Version 3.8.7 and later */
+ int (*auto_extension)(void(*)(void));
+ int (*bind_blob64)(sqlite3_stmt*,int,const void*,sqlite3_uint64,
+ void(*)(void*));
+ int (*bind_text64)(sqlite3_stmt*,int,const char*,sqlite3_uint64,
+ void(*)(void*),unsigned char);
+ int (*cancel_auto_extension)(void(*)(void));
+ int (*load_extension)(sqlite3*,const char*,const char*,char**);
+ void *(*malloc64)(sqlite3_uint64);
+ sqlite3_uint64 (*msize)(void*);
+ void *(*realloc64)(void*,sqlite3_uint64);
+ void (*reset_auto_extension)(void);
+ void (*result_blob64)(sqlite3_context*,const void*,sqlite3_uint64,
+ void(*)(void*));
+ void (*result_text64)(sqlite3_context*,const char*,sqlite3_uint64,
+ void(*)(void*), unsigned char);
+ int (*strglob)(const char*,const char*);
+ /* Version 3.8.11 and later */
+ sqlite3_value *(*value_dup)(const sqlite3_value*);
+ void (*value_free)(sqlite3_value*);
+ int (*result_zeroblob64)(sqlite3_context*,sqlite3_uint64);
+ int (*bind_zeroblob64)(sqlite3_stmt*, int, sqlite3_uint64);
+ /* Version 3.9.0 and later */
+ unsigned int (*value_subtype)(sqlite3_value*);
+ void (*result_subtype)(sqlite3_context*,unsigned int);
+ /* Version 3.10.0 and later */
+ int (*status64)(int,sqlite3_int64*,sqlite3_int64*,int);
+ int (*strlike)(const char*,const char*,unsigned int);
+ int (*db_cacheflush)(sqlite3*);
+ /* Version 3.12.0 and later */
+ int (*system_errno)(sqlite3*);
+ /* Version 3.14.0 and later */
+ int (*trace_v2)(sqlite3*,unsigned,int(*)(unsigned,void*,void*,void*),void*);
+ char *(*expanded_sql)(sqlite3_stmt*);
};
/*
+** This is the function signature used for all extension entry points. It
+** is also defined in the file "loadext.c".
+*/
+typedef int (*sqlite3_loadext_entry)(
+ sqlite3 *db, /* Handle to the database. */
+ char **pzErrMsg, /* Used to set error string on failure. */
+ const sqlite3_api_routines *pThunk /* Extension API function pointers. */
+);
+
+/*
** The following macros redefine the API routines so that they are
-** redirected throught the global sqlite3_api structure.
+** redirected through the global sqlite3_api structure.
**
** This header file is also used by the loadext.c source file
** (part of the main SQLite library - not an extension) so that
@@ -228,7 +305,7 @@ struct sqlite3_api_routines {
** the API. So the redefinition macros are only valid if the
** SQLITE_CORE macros is undefined.
*/
-#ifndef SQLITE_CORE
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
#define sqlite3_aggregate_context sqlite3_api->aggregate_context
#ifndef SQLITE_OMIT_DEPRECATED
#define sqlite3_aggregate_count sqlite3_api->aggregate_count
@@ -355,6 +432,7 @@ struct sqlite3_api_routines {
#define sqlite3_value_text16le sqlite3_api->value_text16le
#define sqlite3_value_type sqlite3_api->value_type
#define sqlite3_vmprintf sqlite3_api->vmprintf
+#define sqlite3_vsnprintf sqlite3_api->vsnprintf
#define sqlite3_overload_function sqlite3_api->overload_function
#define sqlite3_prepare_v2 sqlite3_api->prepare_v2
#define sqlite3_prepare16_v2 sqlite3_api->prepare16_v2
@@ -418,9 +496,65 @@ struct sqlite3_api_routines {
#define sqlite3_blob_reopen sqlite3_api->blob_reopen
#define sqlite3_vtab_config sqlite3_api->vtab_config
#define sqlite3_vtab_on_conflict sqlite3_api->vtab_on_conflict
-#endif /* SQLITE_CORE */
+/* Version 3.7.16 and later */
+#define sqlite3_close_v2 sqlite3_api->close_v2
+#define sqlite3_db_filename sqlite3_api->db_filename
+#define sqlite3_db_readonly sqlite3_api->db_readonly
+#define sqlite3_db_release_memory sqlite3_api->db_release_memory
+#define sqlite3_errstr sqlite3_api->errstr
+#define sqlite3_stmt_busy sqlite3_api->stmt_busy
+#define sqlite3_stmt_readonly sqlite3_api->stmt_readonly
+#define sqlite3_stricmp sqlite3_api->stricmp
+#define sqlite3_uri_boolean sqlite3_api->uri_boolean
+#define sqlite3_uri_int64 sqlite3_api->uri_int64
+#define sqlite3_uri_parameter sqlite3_api->uri_parameter
+#define sqlite3_uri_vsnprintf sqlite3_api->vsnprintf
+#define sqlite3_wal_checkpoint_v2 sqlite3_api->wal_checkpoint_v2
+/* Version 3.8.7 and later */
+#define sqlite3_auto_extension sqlite3_api->auto_extension
+#define sqlite3_bind_blob64 sqlite3_api->bind_blob64
+#define sqlite3_bind_text64 sqlite3_api->bind_text64
+#define sqlite3_cancel_auto_extension sqlite3_api->cancel_auto_extension
+#define sqlite3_load_extension sqlite3_api->load_extension
+#define sqlite3_malloc64 sqlite3_api->malloc64
+#define sqlite3_msize sqlite3_api->msize
+#define sqlite3_realloc64 sqlite3_api->realloc64
+#define sqlite3_reset_auto_extension sqlite3_api->reset_auto_extension
+#define sqlite3_result_blob64 sqlite3_api->result_blob64
+#define sqlite3_result_text64 sqlite3_api->result_text64
+#define sqlite3_strglob sqlite3_api->strglob
+/* Version 3.8.11 and later */
+#define sqlite3_value_dup sqlite3_api->value_dup
+#define sqlite3_value_free sqlite3_api->value_free
+#define sqlite3_result_zeroblob64 sqlite3_api->result_zeroblob64
+#define sqlite3_bind_zeroblob64 sqlite3_api->bind_zeroblob64
+/* Version 3.9.0 and later */
+#define sqlite3_value_subtype sqlite3_api->value_subtype
+#define sqlite3_result_subtype sqlite3_api->result_subtype
+/* Version 3.10.0 and later */
+#define sqlite3_status64 sqlite3_api->status64
+#define sqlite3_strlike sqlite3_api->strlike
+#define sqlite3_db_cacheflush sqlite3_api->db_cacheflush
+/* Version 3.12.0 and later */
+#define sqlite3_system_errno sqlite3_api->system_errno
+/* Version 3.14.0 and later */
+#define sqlite3_trace_v2 sqlite3_api->trace_v2
+#define sqlite3_expanded_sql sqlite3_api->expanded_sql
+#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
-#define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api = 0;
-#define SQLITE_EXTENSION_INIT2(v) sqlite3_api = v;
+#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
+ /* This case when the file really is being compiled as a loadable
+ ** extension */
+# define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0;
+# define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v;
+# define SQLITE_EXTENSION_INIT3 \
+ extern const sqlite3_api_routines *sqlite3_api;
+#else
+ /* This case when the file is being statically linked into the
+ ** application */
+# define SQLITE_EXTENSION_INIT1 /*no-op*/
+# define SQLITE_EXTENSION_INIT2(v) (void)v; /* unused parameter */
+# define SQLITE_EXTENSION_INIT3 /*no-op*/
+#endif
-#endif /* _SQLITE3EXT_H_ */
+#endif /* SQLITE3EXT_H */
diff --git a/lib/vers/Makefile.am b/lib/vers/Makefile.am
index 434401f9505e..b366227e625c 100644
--- a/lib/vers/Makefile.am
+++ b/lib/vers/Makefile.am
@@ -10,4 +10,4 @@ CHECK_LOCAL = no-check-local
libvers_la_SOURCES = print_version.c
-EXTRA_DIST = NTMakefile $(build_HEADERZ)
+EXTRA_DIST = NTMakefile $(build_HEADERZ) make-print-version.c
diff --git a/lib/vers/Makefile.in b/lib/vers/Makefile.in
index a257fcd8f855..8c72abd5e7d0 100644
--- a/lib/vers/Makefile.in
+++ b/lib/vers/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -22,6 +21,61 @@
# $Id$
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -40,9 +94,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
- $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
subdir = lib/vers
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
@@ -58,8 +109,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -72,6 +122,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -91,6 +142,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -99,27 +151,80 @@ LTLIBRARIES = $(noinst_LTLIBRARIES)
libvers_la_LIBADD =
am_libvers_la_OBJECTS = print_version.lo
libvers_la_OBJECTS = $(am_libvers_la_OBJECTS)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(libvers_la_SOURCES)
DIST_SOURCES = $(libvers_la_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -138,12 +243,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -153,17 +258,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -182,12 +287,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -196,6 +298,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -212,10 +315,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -223,6 +324,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -237,12 +339,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -259,6 +364,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -273,13 +379,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -303,6 +403,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -338,38 +440,46 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
noinst_LTLIBRARIES = libvers.la
build_HEADERZ = vers.h
CHECK_LOCAL = no-check-local
libvers_la_SOURCES = print_version.c
-EXTRA_DIST = NTMakefile $(build_HEADERZ)
+EXTRA_DIST = NTMakefile $(build_HEADERZ) make-print-version.c
all: all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .o .obj
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -382,7 +492,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/vers/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/vers/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -391,6 +500,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -403,14 +513,17 @@ $(am__aclocal_m4_deps):
clean-noinstLTLIBRARIES:
-test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
- @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libvers.la: $(libvers_la_OBJECTS) $(libvers_la_DEPENDENCIES)
- $(LINK) $(libvers_la_OBJECTS) $(libvers_la_LIBADD) $(LIBS)
+ @list='$(noinst_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libvers.la: $(libvers_la_OBJECTS) $(libvers_la_DEPENDENCIES) $(EXTRA_libvers_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(libvers_la_OBJECTS) $(libvers_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -421,25 +534,25 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/print_version.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -447,26 +560,15 @@ mostlyclean-libtool:
clean-libtool:
-rm -rf .libs _libs
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -478,15 +580,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -495,6 +593,21 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
@@ -547,10 +660,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -592,9 +710,8 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am:
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-exec-local
+
install-html: install-html-am
install-html-am:
@@ -636,38 +753,53 @@ ps-am:
uninstall-am:
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
-.MAKE: check-am install-am install-data-am install-exec-am \
- install-strip uninstall-am
+.MAKE: check-am install-am install-data-am install-strip uninstall-am
-.PHONY: CTAGS GTAGS all all-am all-local check check-am check-local \
- clean clean-generic clean-libtool clean-noinstLTLIBRARIES \
- ctags dist-hook distclean distclean-compile distclean-generic \
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am \
+ check-local clean clean-generic clean-libtool \
+ clean-noinstLTLIBRARIES cscopelist-am ctags ctags-am dist-hook \
+ distclean distclean-compile distclean-generic \
distclean-libtool distclean-tags distdir dvi dvi-am html \
html-am info info-am install install-am install-data \
install-data-am install-data-hook install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
+ install-exec install-exec-am install-exec-local install-html \
install-html-am install-info install-info-am install-man \
install-pdf install-pdf-am install-ps install-ps-am \
install-strip installcheck installcheck-am installdirs \
maintainer-clean maintainer-clean-generic mostlyclean \
mostlyclean-compile mostlyclean-generic mostlyclean-libtool \
- pdf pdf-am ps ps-am tags uninstall uninstall-am uninstall-hook
+ pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \
+ uninstall-hook
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
-install-exec-hook: install-suid-programs
+install-exec-local: install-suid-programs
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -675,7 +807,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -732,6 +864,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -774,6 +908,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -787,13 +934,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/vers/make-print-version.c b/lib/vers/make-print-version.c
new file mode 100644
index 000000000000..f0a61f64179e
--- /dev/null
+++ b/lib/vers/make-print-version.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1998 - 2003 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef KRB5
+extern const char *heimdal_version;
+#endif
+#include <version.h>
+
+int
+main(int argc, char **argv)
+{
+ FILE *f;
+ if(argc != 2)
+ return 1;
+ if (strcmp(argv[1], "--version") == 0) {
+ printf("some version");
+ return 0;
+ }
+ f = fopen(argv[1], "w");
+ if(f == NULL)
+ return 1;
+ fprintf(f, "#define VERSIONLIST \"");
+#ifdef KRB5
+ fprintf(f, "%s", heimdal_version);
+#endif
+ fprintf(f, "\"\n");
+ fclose(f);
+ return 0;
+}
diff --git a/lib/vers/print_version.c b/lib/vers/print_version.c
index 23cd25e0c67e..c62fe9ee1788 100644
--- a/lib/vers/print_version.c
+++ b/lib/vers/print_version.c
@@ -33,8 +33,9 @@
#include <config.h>
+#include <krb5-types.h>
-#define VERSION_HIDDEN static
+#define VERSION_HIDDEN static HEIMDAL_UNUSED_ATTRIBUTE
#include "roken.h"
@@ -51,7 +52,7 @@ print_version(const char *progname)
if(*package_list == '\0')
package_list = "no version information";
fprintf(stderr, "%s (%s)\n", progname, package_list);
- fprintf(stderr, "Copyright 1995-2011 Kungliga Tekniska Högskolan\n");
+ fprintf(stderr, "Copyright 1995-2014 Kungliga Tekniska Högskolan\n");
#ifdef PACKAGE_BUGREPORT
fprintf(stderr, "Send bug-reports to %s\n", PACKAGE_BUGREPORT);
#endif
diff --git a/lib/wind/Makefile.in b/lib/wind/Makefile.in
index 4b53192c2d0c..d8fd4082f897 100644
--- a/lib/wind/Makefile.in
+++ b/lib/wind/Makefile.in
@@ -1,9 +1,8 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# Copyright (C) 1994-2017 Free Software Foundation, Inc.
+
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -24,6 +23,61 @@
VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -42,9 +96,6 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-DIST_COMMON = $(dist_include_HEADERS) $(srcdir)/Makefile.am \
- $(srcdir)/Makefile.in $(top_srcdir)/Makefile.am.common \
- $(top_srcdir)/cf/Makefile.am.common ChangeLog
@versionscript_TRUE@am__append_1 = $(LDFLAGS_VERSION_SCRIPT)$(srcdir)/version-script.map
check_PROGRAMS = test-bidi$(EXEEXT) test-map$(EXEEXT) test-rw$(EXEEXT) \
test-normalize$(EXEEXT) test-prohibited$(EXEEXT) \
@@ -65,8 +116,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/check-man.m4 \
$(top_srcdir)/cf/check-netinet-ip-and-tcp.m4 \
$(top_srcdir)/cf/check-type-extra.m4 \
- $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/check-x.m4 \
- $(top_srcdir)/cf/check-xau.m4 $(top_srcdir)/cf/crypto.m4 \
+ $(top_srcdir)/cf/check-var.m4 $(top_srcdir)/cf/crypto.m4 \
$(top_srcdir)/cf/db.m4 $(top_srcdir)/cf/destdirs.m4 \
$(top_srcdir)/cf/dispatch.m4 $(top_srcdir)/cf/dlopen.m4 \
$(top_srcdir)/cf/find-func-no-libs.m4 \
@@ -79,6 +129,7 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/cf/krb-bigendian.m4 \
$(top_srcdir)/cf/krb-func-getlogin.m4 \
$(top_srcdir)/cf/krb-ipv6.m4 $(top_srcdir)/cf/krb-prog-ln-s.m4 \
+ $(top_srcdir)/cf/krb-prog-perl.m4 \
$(top_srcdir)/cf/krb-readline.m4 \
$(top_srcdir)/cf/krb-struct-spwd.m4 \
$(top_srcdir)/cf/krb-struct-winsize.m4 \
@@ -98,6 +149,8 @@ am__aclocal_m4_deps = $(top_srcdir)/cf/aix.m4 \
$(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(dist_include_HEADERS) \
+ $(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/include/config.h
CONFIG_CLEAN_FILES =
@@ -123,6 +176,12 @@ am__nobase_list = $(am__nobase_strip_setup); \
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
"$(DESTDIR)$(includedir)" "$(DESTDIR)$(includedir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
@@ -135,7 +194,11 @@ dist_libwind_la_OBJECTS = $(am__objects_1) $(am__objects_2)
nodist_libwind_la_OBJECTS = wind_err.lo
libwind_la_OBJECTS = $(dist_libwind_la_OBJECTS) \
$(nodist_libwind_la_OBJECTS)
-libwind_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+libwind_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(libwind_la_LDFLAGS) $(LDFLAGS) -o $@
PROGRAMS = $(bin_PROGRAMS)
@@ -176,18 +239,39 @@ test_utf8_SOURCES = test-utf8.c
test_utf8_OBJECTS = test-utf8.$(OBJEXT)
test_utf8_LDADD = $(LDADD)
test_utf8_DEPENDENCIES = libwind.la $(am__DEPENDENCIES_1)
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__depfiles_maybe = depfiles
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
-LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
- $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
CCLD = $(CC)
-LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
- --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
- $(LDFLAGS) -o $@
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
SOURCES = $(dist_libwind_la_SOURCES) $(nodist_libwind_la_SOURCES) \
$(idn_lookup_SOURCES) test-bidi.c test-ldap.c test-map.c \
test-normalize.c test-prohibited.c $(test_punycode_SOURCES) \
@@ -196,16 +280,219 @@ DIST_SOURCES = $(dist_libwind_la_SOURCES) $(idn_lookup_SOURCES) \
test-bidi.c test-ldap.c test-map.c test-normalize.c \
test-prohibited.c $(test_punycode_SOURCES) test-rw.c \
test-utf8.c
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
HEADERS = $(dist_include_HEADERS) $(nodist_include_HEADERS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
-am__tty_colors = \
-red=; grn=; lgn=; blu=; std=
+am__tty_colors_dummy = \
+ mgn= red= grn= lgn= blu= brg= std=; \
+ am__color_tests=no
+am__tty_colors = { \
+ $(am__tty_colors_dummy); \
+ if test "X$(AM_COLOR_TESTS)" = Xno; then \
+ am__color_tests=no; \
+ elif test "X$(AM_COLOR_TESTS)" = Xalways; then \
+ am__color_tests=yes; \
+ elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \
+ am__color_tests=yes; \
+ fi; \
+ if test $$am__color_tests = yes; then \
+ red=''; \
+ grn=''; \
+ lgn=''; \
+ blu=''; \
+ mgn=''; \
+ brg=''; \
+ std=''; \
+ fi; \
+}
+am__recheck_rx = ^[ ]*:recheck:[ ]*
+am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
+am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]*
+# A command that, given a newline-separated list of test names on the
+# standard input, print the name of the tests that are to be re-run
+# upon "make recheck".
+am__list_recheck_tests = $(AWK) '{ \
+ recheck = 1; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ { \
+ if ((getline line2 < ($$0 ".log")) < 0) \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \
+ { \
+ recheck = 0; \
+ break; \
+ } \
+ else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \
+ { \
+ break; \
+ } \
+ }; \
+ if (recheck) \
+ print $$0; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# A command that, given a newline-separated list of test names on the
+# standard input, create the global log from their .trs and .log files.
+am__create_global_log = $(AWK) ' \
+function fatal(msg) \
+{ \
+ print "fatal: making $@: " msg | "cat >&2"; \
+ exit 1; \
+} \
+function rst_section(header) \
+{ \
+ print header; \
+ len = length(header); \
+ for (i = 1; i <= len; i = i + 1) \
+ printf "="; \
+ printf "\n\n"; \
+} \
+{ \
+ copy_in_global_log = 1; \
+ global_test_result = "RUN"; \
+ while ((rc = (getline line < ($$0 ".trs"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".trs"); \
+ if (line ~ /$(am__global_test_result_rx)/) \
+ { \
+ sub("$(am__global_test_result_rx)", "", line); \
+ sub("[ ]*$$", "", line); \
+ global_test_result = line; \
+ } \
+ else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \
+ copy_in_global_log = 0; \
+ }; \
+ if (copy_in_global_log) \
+ { \
+ rst_section(global_test_result ": " $$0); \
+ while ((rc = (getline line < ($$0 ".log"))) != 0) \
+ { \
+ if (rc < 0) \
+ fatal("failed to read from " $$0 ".log"); \
+ print line; \
+ }; \
+ printf "\n"; \
+ }; \
+ close ($$0 ".trs"); \
+ close ($$0 ".log"); \
+}'
+# Restructured Text title.
+am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; }
+# Solaris 10 'make', and several other traditional 'make' implementations,
+# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it
+# by disabling -e (using the XSI extension "set +e") if it's set.
+am__sh_e_setup = case $$- in *e*) set +e;; esac
+# Default flags passed to test drivers.
+am__common_driver_flags = \
+ --color-tests "$$am__color_tests" \
+ --enable-hard-errors "$$am__enable_hard_errors" \
+ --expect-failure "$$am__expect_failure"
+# To be inserted before the command running the test. Creates the
+# directory for the log if needed. Stores in $dir the directory
+# containing $f, in $tst the test, in $log the log. Executes the
+# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
+# will run the test scripts (or their associated LOG_COMPILER, if
+# thy have one).
+am__check_pre = \
+$(am__sh_e_setup); \
+$(am__vpath_adj_setup) $(am__vpath_adj) \
+$(am__tty_colors); \
+srcdir=$(srcdir); export srcdir; \
+case "$@" in \
+ */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \
+ *) am__odir=.;; \
+esac; \
+test "x$$am__odir" = x"." || test -d "$$am__odir" \
+ || $(MKDIR_P) "$$am__odir" || exit $$?; \
+if test -f "./$$f"; then dir=./; \
+elif test -f "$$f"; then dir=; \
+else dir="$(srcdir)/"; fi; \
+tst=$$dir$$f; log='$@'; \
+if test -n '$(DISABLE_HARD_ERRORS)'; then \
+ am__enable_hard_errors=no; \
+else \
+ am__enable_hard_errors=yes; \
+fi; \
+case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \
+ am__expect_failure=yes;; \
+ *) \
+ am__expect_failure=no;; \
+esac; \
+$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT)
+# A shell command to get the names of the tests scripts with any registered
+# extension removed (i.e., equivalently, the names of the test logs, with
+# the '.log' extension removed). The result is saved in the shell variable
+# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly,
+# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)",
+# since that might cause problem with VPATH rewrites for suffix-less tests.
+# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'.
+am__set_TESTS_bases = \
+ bases='$(TEST_LOGS)'; \
+ bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \
+ bases=`echo $$bases`
+RECHECK_LOGS = $(TEST_LOGS)
+AM_RECURSIVE_TARGETS = check recheck
+TEST_SUITE_LOG = test-suite.log
+TEST_EXTENSIONS = @EXEEXT@ .test
+LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS)
+am__set_b = \
+ case '$@' in \
+ */*) \
+ case '$*' in \
+ */*) b='$*';; \
+ *) b=`echo '$@' | sed 's/\.log$$//'`; \
+ esac;; \
+ *) \
+ b='$*';; \
+ esac
+am__test_logs1 = $(TESTS:=.log)
+am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
+TEST_LOGS = $(am__test_logs2:.test.log=.log)
+TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver
+TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \
+ $(TEST_LOG_FLAGS)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/Makefile.am.common \
+ $(top_srcdir)/cf/Makefile.am.common $(top_srcdir)/depcomp \
+ $(top_srcdir)/test-driver ChangeLog
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AIX_EXTRA_KAFS = @AIX_EXTRA_KAFS@
AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
+AS = @AS@
ASN1_COMPILE = @ASN1_COMPILE@
ASN1_COMPILE_DEP = @ASN1_COMPILE_DEP@
AUTOCONF = @AUTOCONF@
@@ -224,12 +511,12 @@ COMPILE_ET = @COMPILE_ET@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CYGPATH_W = @CYGPATH_W@
+DB1LIB = @DB1LIB@
+DB3LIB = @DB3LIB@
DBHEADER = @DBHEADER@
-DBLIB = @DBLIB@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DIR_com_err = @DIR_com_err@
-DIR_hcrypto = @DIR_hcrypto@
DIR_hdbdir = @DIR_hdbdir@
DIR_roken = @DIR_roken@
DLLTOOL = @DLLTOOL@
@@ -239,17 +526,17 @@ ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
+ENABLE_AFS_STRING_TO_KEY = @ENABLE_AFS_STRING_TO_KEY@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
+GCD_MIG = @GCD_MIG@
GREP = @GREP@
GROFF = @GROFF@
INCLUDES_roken = @INCLUDES_roken@
-INCLUDE_hcrypto = @INCLUDE_hcrypto@
-INCLUDE_hesiod = @INCLUDE_hesiod@
-INCLUDE_krb4 = @INCLUDE_krb4@
INCLUDE_libedit = @INCLUDE_libedit@
INCLUDE_libintl = @INCLUDE_libintl@
INCLUDE_openldap = @INCLUDE_openldap@
+INCLUDE_openssl_crypto = @INCLUDE_openssl_crypto@
INCLUDE_readline = @INCLUDE_readline@
INCLUDE_sqlite3 = @INCLUDE_sqlite3@
INSTALL = @INSTALL@
@@ -268,12 +555,9 @@ LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIB_AUTH_SUBDIRS = @LIB_AUTH_SUBDIRS@
-LIB_NDBM = @LIB_NDBM@
-LIB_XauFileName = @LIB_XauFileName@
-LIB_XauReadAuth = @LIB_XauReadAuth@
-LIB_XauWriteAuth = @LIB_XauWriteAuth@
LIB_bswap16 = @LIB_bswap16@
LIB_bswap32 = @LIB_bswap32@
+LIB_bswap64 = @LIB_bswap64@
LIB_com_err = @LIB_com_err@
LIB_com_err_a = @LIB_com_err_a@
LIB_com_err_so = @LIB_com_err_so@
@@ -282,6 +566,7 @@ LIB_db_create = @LIB_db_create@
LIB_dbm_firstkey = @LIB_dbm_firstkey@
LIB_dbopen = @LIB_dbopen@
LIB_dispatch_async_f = @LIB_dispatch_async_f@
+LIB_dladdr = @LIB_dladdr@
LIB_dlopen = @LIB_dlopen@
LIB_dn_expand = @LIB_dn_expand@
LIB_dns_search = @LIB_dns_search@
@@ -298,10 +583,8 @@ LIB_hcrypto = @LIB_hcrypto@
LIB_hcrypto_a = @LIB_hcrypto_a@
LIB_hcrypto_appl = @LIB_hcrypto_appl@
LIB_hcrypto_so = @LIB_hcrypto_so@
-LIB_hesiod = @LIB_hesiod@
LIB_hstrerror = @LIB_hstrerror@
LIB_kdb = @LIB_kdb@
-LIB_krb4 = @LIB_krb4@
LIB_libedit = @LIB_libedit@
LIB_libintl = @LIB_libintl@
LIB_loadquery = @LIB_loadquery@
@@ -309,6 +592,7 @@ LIB_logout = @LIB_logout@
LIB_logwtmp = @LIB_logwtmp@
LIB_openldap = @LIB_openldap@
LIB_openpty = @LIB_openpty@
+LIB_openssl_crypto = @LIB_openssl_crypto@
LIB_otp = @LIB_otp@
LIB_pidfile = @LIB_pidfile@
LIB_readline = @LIB_readline@
@@ -323,12 +607,15 @@ LIB_sqlite3 = @LIB_sqlite3@
LIB_syslog = @LIB_syslog@
LIB_tgetent = @LIB_tgetent@
LIPO = @LIPO@
+LMDBLIB = @LMDBLIB@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MKDIR_P = @MKDIR_P@
+NDBMLIB = @NDBMLIB@
NM = @NM@
NMEDIT = @NMEDIT@
NO_AFS = @NO_AFS@
@@ -345,6 +632,7 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
PKG_CONFIG = @PKG_CONFIG@
PTHREAD_CFLAGS = @PTHREAD_CFLAGS@
PTHREAD_LDADD = @PTHREAD_LDADD@
@@ -359,13 +647,7 @@ STRIP = @STRIP@
VERSION = @VERSION@
VERSIONING = @VERSIONING@
WFLAGS = @WFLAGS@
-WFLAGS_NOIMPLICITINT = @WFLAGS_NOIMPLICITINT@
-WFLAGS_NOUNUSED = @WFLAGS_NOUNUSED@
-XMKMF = @XMKMF@
-X_CFLAGS = @X_CFLAGS@
-X_EXTRA_LIBS = @X_EXTRA_LIBS@
-X_LIBS = @X_LIBS@
-X_PRE_LIBS = @X_PRE_LIBS@
+WFLAGS_LITE = @WFLAGS_LITE@
YACC = @YACC@
YFLAGS = @YFLAGS@
abs_builddir = @abs_builddir@
@@ -389,6 +671,8 @@ build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
+db_type = @db_type@
+db_type_preference = @db_type_preference@
docdir = @docdir@
dpagaix_cflags = @dpagaix_cflags@
dpagaix_ldadd = @dpagaix_ldadd@
@@ -424,29 +708,37 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-SUFFIXES = .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8
+SUFFIXES = .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 \
+ .cat5 .cat7 .cat8
DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)/include -I$(top_srcdir)/include
AM_CPPFLAGS = $(INCLUDES_roken)
@do_roken_rename_TRUE@ROKEN_RENAME = -DROKEN_RENAME
AM_CFLAGS = $(WFLAGS)
CP = cp
buildinclude = $(top_builddir)/include
+LIB_XauReadAuth = @LIB_XauReadAuth@
LIB_el_init = @LIB_el_init@
LIB_getattr = @LIB_getattr@
LIB_getpwent_r = @LIB_getpwent_r@
LIB_odm_initialize = @LIB_odm_initialize@
LIB_setpcred = @LIB_setpcred@
-HESIODLIB = @HESIODLIB@
-HESIODINCLUDE = @HESIODINCLUDE@
+INCLUDE_krb4 = @INCLUDE_krb4@
+LIB_krb4 = @LIB_krb4@
libexec_heimdaldir = $(libexecdir)/heimdal
NROFF_MAN = groff -mandoc -Tascii
-LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_FALSE@LIB_kafs = $(top_builddir)/lib/kafs/libkafs.la $(AIX_EXTRA_KAFS)
+@NO_AFS_TRUE@LIB_kafs =
@KRB5_TRUE@LIB_krb5 = $(top_builddir)/lib/krb5/libkrb5.la \
@KRB5_TRUE@ $(top_builddir)/lib/asn1/libasn1.la
@KRB5_TRUE@LIB_gssapi = $(top_builddir)/lib/gssapi/libgssapi.la
-LIB_heimbase = $(top_builddir)/base/libheimbase.la
+LIB_heimbase = $(top_builddir)/lib/base/libheimbase.la
@DCE_TRUE@LIB_kdfs = $(top_builddir)/lib/kdfs/libkdfs.la
+
+#silent-rules
+heim_verbose = $(heim_verbose_$(V))
+heim_verbose_ = $(heim_verbose_$(AM_DEFAULT_VERBOSITY))
+heim_verbose_0 = @echo " GEN "$@;
lib_LTLIBRARIES = libwind.la
built = \
bidi_table.c \
@@ -542,7 +834,7 @@ all: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) all-am
.SUFFIXES:
-.SUFFIXES: .et .h .x .z .hx .1 .3 .5 .8 .cat1 .cat3 .cat5 .cat8 .c .lo .o .obj
+.SUFFIXES: .et .h .pc.in .pc .x .z .hx .1 .3 .5 .7 .8 .cat1 .cat3 .cat5 .cat7 .cat8 .c .lo .log .o .obj .test .test$(EXEEXT) .trs
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -555,7 +847,6 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign lib/wind/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign lib/wind/Makefile
-.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
@@ -564,6 +855,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/Makefile.am.common $(top_srcdir)/cf/Makefile.am.common $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -573,9 +865,9 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
+
install-libLTLIBRARIES: $(lib_LTLIBRARIES)
@$(NORMAL_INSTALL)
- test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
@list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
list2=; for p in $$list; do \
if test -f $$p; then \
@@ -583,6 +875,8 @@ install-libLTLIBRARIES: $(lib_LTLIBRARIES)
else :; fi; \
done; \
test -z "$$list2" || { \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \
echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
$(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
}
@@ -598,24 +892,32 @@ uninstall-libLTLIBRARIES:
clean-libLTLIBRARIES:
-test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
- @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
- dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
- test "$$dir" != "$$p" || dir=.; \
- echo "rm -f \"$${dir}/so_locations\""; \
- rm -f "$${dir}/so_locations"; \
- done
-libwind.la: $(libwind_la_OBJECTS) $(libwind_la_DEPENDENCIES)
- $(libwind_la_LINK) -rpath $(libdir) $(libwind_la_OBJECTS) $(libwind_la_LIBADD) $(LIBS)
+ @list='$(lib_LTLIBRARIES)'; \
+ locs=`for p in $$list; do echo $$p; done | \
+ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
+ sort -u`; \
+ test -z "$$locs" || { \
+ echo rm -f $${locs}; \
+ rm -f $${locs}; \
+ }
+
+libwind.la: $(libwind_la_OBJECTS) $(libwind_la_DEPENDENCIES) $(EXTRA_libwind_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libwind_la_LINK) -rpath $(libdir) $(libwind_la_OBJECTS) $(libwind_la_LIBADD) $(LIBS)
install-binPROGRAMS: $(bin_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p || test -f $$p1; \
- then echo "$$p"; echo "$$p"; else :; fi; \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
-e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
sed 'N;N;N;s,\n, ,g' | \
$(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
@@ -636,7 +938,8 @@ uninstall-binPROGRAMS:
@list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
files=`for p in $$list; do echo "$$p"; done | \
sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
cd "$(DESTDIR)$(bindir)" && rm -f $$files
@@ -658,33 +961,42 @@ clean-checkPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
-idn-lookup$(EXEEXT): $(idn_lookup_OBJECTS) $(idn_lookup_DEPENDENCIES)
+
+idn-lookup$(EXEEXT): $(idn_lookup_OBJECTS) $(idn_lookup_DEPENDENCIES) $(EXTRA_idn_lookup_DEPENDENCIES)
@rm -f idn-lookup$(EXEEXT)
- $(LINK) $(idn_lookup_OBJECTS) $(idn_lookup_LDADD) $(LIBS)
-test-bidi$(EXEEXT): $(test_bidi_OBJECTS) $(test_bidi_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(idn_lookup_OBJECTS) $(idn_lookup_LDADD) $(LIBS)
+
+test-bidi$(EXEEXT): $(test_bidi_OBJECTS) $(test_bidi_DEPENDENCIES) $(EXTRA_test_bidi_DEPENDENCIES)
@rm -f test-bidi$(EXEEXT)
- $(LINK) $(test_bidi_OBJECTS) $(test_bidi_LDADD) $(LIBS)
-test-ldap$(EXEEXT): $(test_ldap_OBJECTS) $(test_ldap_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_bidi_OBJECTS) $(test_bidi_LDADD) $(LIBS)
+
+test-ldap$(EXEEXT): $(test_ldap_OBJECTS) $(test_ldap_DEPENDENCIES) $(EXTRA_test_ldap_DEPENDENCIES)
@rm -f test-ldap$(EXEEXT)
- $(LINK) $(test_ldap_OBJECTS) $(test_ldap_LDADD) $(LIBS)
-test-map$(EXEEXT): $(test_map_OBJECTS) $(test_map_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_ldap_OBJECTS) $(test_ldap_LDADD) $(LIBS)
+
+test-map$(EXEEXT): $(test_map_OBJECTS) $(test_map_DEPENDENCIES) $(EXTRA_test_map_DEPENDENCIES)
@rm -f test-map$(EXEEXT)
- $(LINK) $(test_map_OBJECTS) $(test_map_LDADD) $(LIBS)
-test-normalize$(EXEEXT): $(test_normalize_OBJECTS) $(test_normalize_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_map_OBJECTS) $(test_map_LDADD) $(LIBS)
+
+test-normalize$(EXEEXT): $(test_normalize_OBJECTS) $(test_normalize_DEPENDENCIES) $(EXTRA_test_normalize_DEPENDENCIES)
@rm -f test-normalize$(EXEEXT)
- $(LINK) $(test_normalize_OBJECTS) $(test_normalize_LDADD) $(LIBS)
-test-prohibited$(EXEEXT): $(test_prohibited_OBJECTS) $(test_prohibited_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_normalize_OBJECTS) $(test_normalize_LDADD) $(LIBS)
+
+test-prohibited$(EXEEXT): $(test_prohibited_OBJECTS) $(test_prohibited_DEPENDENCIES) $(EXTRA_test_prohibited_DEPENDENCIES)
@rm -f test-prohibited$(EXEEXT)
- $(LINK) $(test_prohibited_OBJECTS) $(test_prohibited_LDADD) $(LIBS)
-test-punycode$(EXEEXT): $(test_punycode_OBJECTS) $(test_punycode_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_prohibited_OBJECTS) $(test_prohibited_LDADD) $(LIBS)
+
+test-punycode$(EXEEXT): $(test_punycode_OBJECTS) $(test_punycode_DEPENDENCIES) $(EXTRA_test_punycode_DEPENDENCIES)
@rm -f test-punycode$(EXEEXT)
- $(LINK) $(test_punycode_OBJECTS) $(test_punycode_LDADD) $(LIBS)
-test-rw$(EXEEXT): $(test_rw_OBJECTS) $(test_rw_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_punycode_OBJECTS) $(test_punycode_LDADD) $(LIBS)
+
+test-rw$(EXEEXT): $(test_rw_OBJECTS) $(test_rw_DEPENDENCIES) $(EXTRA_test_rw_DEPENDENCIES)
@rm -f test-rw$(EXEEXT)
- $(LINK) $(test_rw_OBJECTS) $(test_rw_LDADD) $(LIBS)
-test-utf8$(EXEEXT): $(test_utf8_OBJECTS) $(test_utf8_DEPENDENCIES)
+ $(AM_V_CCLD)$(LINK) $(test_rw_OBJECTS) $(test_rw_LDADD) $(LIBS)
+
+test-utf8$(EXEEXT): $(test_utf8_OBJECTS) $(test_utf8_DEPENDENCIES) $(EXTRA_test_utf8_DEPENDENCIES)
@rm -f test-utf8$(EXEEXT)
- $(LINK) $(test_utf8_OBJECTS) $(test_utf8_LDADD) $(LIBS)
+ $(AM_V_CCLD)$(LINK) $(test_utf8_OBJECTS) $(test_utf8_LDADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
@@ -720,25 +1032,25 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wind_err.Plo@am__quote@
.c.o:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
.c.obj:
-@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
.c.lo:
-@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
mostlyclean-libtool:
-rm -f *.lo
@@ -747,8 +1059,11 @@ clean-libtool:
-rm -rf .libs _libs
install-dist_includeHEADERS: $(dist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -762,13 +1077,14 @@ uninstall-dist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(dist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
install-nodist_includeHEADERS: $(nodist_include_HEADERS)
@$(NORMAL_INSTALL)
- test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)"
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \
+ fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
@@ -782,30 +1098,17 @@ uninstall-nodist_includeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(nodist_include_HEADERS)'; test -n "$(includedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- test -n "$$files" || exit 0; \
- echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(includedir)" && rm -f $$files
-
-ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
- mkid -fID $$unique
-tags: TAGS
-
-TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
+ dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ $(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
@@ -817,15 +1120,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
$$unique; \
fi; \
fi
-ctags: CTAGS
-CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
- $(TAGS_FILES) $(LISP)
- list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
- unique=`for i in $$list; do \
- if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
- done | \
- $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
- END { if (nonempty) { for (i in files) print i; }; }'`; \
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
@@ -834,101 +1133,236 @@ GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-check-TESTS: $(TESTS)
- @failed=0; all=0; xfail=0; xpass=0; skip=0; \
- srcdir=$(srcdir); export srcdir; \
- list=' $(TESTS) '; \
- $(am__tty_colors); \
- if test -n "$$list"; then \
- for tst in $$list; do \
- if test -f ./$$tst; then dir=./; \
- elif test -f $$tst; then dir=; \
- else dir="$(srcdir)/"; fi; \
- if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xpass=`expr $$xpass + 1`; \
- failed=`expr $$failed + 1`; \
- col=$$red; res=XPASS; \
- ;; \
- *) \
- col=$$grn; res=PASS; \
- ;; \
- esac; \
- elif test $$? -ne 77; then \
- all=`expr $$all + 1`; \
- case " $(XFAIL_TESTS) " in \
- *[\ \ ]$$tst[\ \ ]*) \
- xfail=`expr $$xfail + 1`; \
- col=$$lgn; res=XFAIL; \
- ;; \
- *) \
- failed=`expr $$failed + 1`; \
- col=$$red; res=FAIL; \
- ;; \
- esac; \
- else \
- skip=`expr $$skip + 1`; \
- col=$$blu; res=SKIP; \
- fi; \
- echo "$${col}$$res$${std}: $$tst"; \
- done; \
- if test "$$all" -eq 1; then \
- tests="test"; \
- All=""; \
- else \
- tests="tests"; \
- All="All "; \
+# Recover from deleted '.trs' file; this should ensure that
+# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create
+# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells
+# to avoid problems with "make -n".
+.log.trs:
+ rm -f $< $@
+ $(MAKE) $(AM_MAKEFLAGS) $<
+
+# Leading 'am--fnord' is there to ensure the list of targets does not
+# expand to empty, as could happen e.g. with make check TESTS=''.
+am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
+am--force-recheck:
+ @:
+
+$(TEST_SUITE_LOG): $(TEST_LOGS)
+ @$(am__set_TESTS_bases); \
+ am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
+ redo_bases=`for i in $$bases; do \
+ am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \
+ done`; \
+ if test -n "$$redo_bases"; then \
+ redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \
+ redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \
+ if $(am__make_dryrun); then :; else \
+ rm -f $$redo_logs && rm -f $$redo_results || exit 1; \
fi; \
- if test "$$failed" -eq 0; then \
- if test "$$xfail" -eq 0; then \
- banner="$$All$$all $$tests passed"; \
- else \
- if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
- banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
- fi; \
- else \
- if test "$$xpass" -eq 0; then \
- banner="$$failed of $$all $$tests failed"; \
+ fi; \
+ if test -n "$$am__remaking_logs"; then \
+ echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \
+ "recursion detected" >&2; \
+ elif test -n "$$redo_logs"; then \
+ am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \
+ fi; \
+ if $(am__make_dryrun); then :; else \
+ st=0; \
+ errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \
+ for i in $$redo_bases; do \
+ test -f $$i.trs && test -r $$i.trs \
+ || { echo "$$errmsg $$i.trs" >&2; st=1; }; \
+ test -f $$i.log && test -r $$i.log \
+ || { echo "$$errmsg $$i.log" >&2; st=1; }; \
+ done; \
+ test $$st -eq 0 || exit 1; \
+ fi
+ @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \
+ ws='[ ]'; \
+ results=`for b in $$bases; do echo $$b.trs; done`; \
+ test -n "$$results" || results=/dev/null; \
+ all=` grep "^$$ws*:test-result:" $$results | wc -l`; \
+ pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \
+ fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \
+ skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \
+ xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \
+ xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \
+ error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \
+ if test `expr $$fail + $$xpass + $$error` -eq 0; then \
+ success=true; \
+ else \
+ success=false; \
+ fi; \
+ br='==================='; br=$$br$$br$$br$$br; \
+ result_count () \
+ { \
+ if test x"$$1" = x"--maybe-color"; then \
+ maybe_colorize=yes; \
+ elif test x"$$1" = x"--no-color"; then \
+ maybe_colorize=no; \
else \
- if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
- banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ echo "$@: invalid 'result_count' usage" >&2; exit 4; \
fi; \
- fi; \
- dashes="$$banner"; \
- skipped=""; \
- if test "$$skip" -ne 0; then \
- if test "$$skip" -eq 1; then \
- skipped="($$skip test was not run)"; \
+ shift; \
+ desc=$$1 count=$$2; \
+ if test $$maybe_colorize = yes && test $$count -gt 0; then \
+ color_start=$$3 color_end=$$std; \
else \
- skipped="($$skip tests were not run)"; \
+ color_start= color_end=; \
fi; \
- test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$skipped"; \
- fi; \
- report=""; \
- if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
- report="Please report to $(PACKAGE_BUGREPORT)"; \
- test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
- dashes="$$report"; \
- fi; \
- dashes=`echo "$$dashes" | sed s/./=/g`; \
- if test "$$failed" -eq 0; then \
- echo "$$grn$$dashes"; \
- else \
- echo "$$red$$dashes"; \
- fi; \
- echo "$$banner"; \
- test -z "$$skipped" || echo "$$skipped"; \
- test -z "$$report" || echo "$$report"; \
- echo "$$dashes$$std"; \
- test "$$failed" -eq 0; \
- else :; fi
+ echo "$${color_start}# $$desc $$count$${color_end}"; \
+ }; \
+ create_testsuite_report () \
+ { \
+ result_count $$1 "TOTAL:" $$all "$$brg"; \
+ result_count $$1 "PASS: " $$pass "$$grn"; \
+ result_count $$1 "SKIP: " $$skip "$$blu"; \
+ result_count $$1 "XFAIL:" $$xfail "$$lgn"; \
+ result_count $$1 "FAIL: " $$fail "$$red"; \
+ result_count $$1 "XPASS:" $$xpass "$$red"; \
+ result_count $$1 "ERROR:" $$error "$$mgn"; \
+ }; \
+ { \
+ echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
+ $(am__rst_title); \
+ create_testsuite_report --no-color; \
+ echo; \
+ echo ".. contents:: :depth: 2"; \
+ echo; \
+ for b in $$bases; do echo $$b; done \
+ | $(am__create_global_log); \
+ } >$(TEST_SUITE_LOG).tmp || exit 1; \
+ mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \
+ if $$success; then \
+ col="$$grn"; \
+ else \
+ col="$$red"; \
+ test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \
+ fi; \
+ echo "$${col}$$br$${std}"; \
+ echo "$${col}Testsuite summary for $(PACKAGE_STRING)$${std}"; \
+ echo "$${col}$$br$${std}"; \
+ create_testsuite_report --maybe-color; \
+ echo "$$col$$br$$std"; \
+ if $$success; then :; else \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ if test -n "$(PACKAGE_BUGREPORT)"; then \
+ echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ fi; \
+ echo "$$col$$br$$std"; \
+ fi; \
+ $$success || exit 1
+
+check-TESTS:
+ @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
+ @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ trs_list=`for i in $$bases; do echo $$i.trs; done`; \
+ log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
+ exit $$?;
+recheck: all $(check_PROGRAMS)
+ @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @set +e; $(am__set_TESTS_bases); \
+ bases=`for i in $$bases; do echo $$i; done \
+ | $(am__list_recheck_tests)` || exit 1; \
+ log_list=`for i in $$bases; do echo $$i.log; done`; \
+ log_list=`echo $$log_list`; \
+ $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \
+ am__force_recheck=am--force-recheck \
+ TEST_LOGS="$$log_list"; \
+ exit $$?
+test-bidi.log: test-bidi$(EXEEXT)
+ @p='test-bidi$(EXEEXT)'; \
+ b='test-bidi'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-map.log: test-map$(EXEEXT)
+ @p='test-map$(EXEEXT)'; \
+ b='test-map'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-rw.log: test-rw$(EXEEXT)
+ @p='test-rw$(EXEEXT)'; \
+ b='test-rw'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-normalize.log: test-normalize$(EXEEXT)
+ @p='test-normalize$(EXEEXT)'; \
+ b='test-normalize'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-prohibited.log: test-prohibited$(EXEEXT)
+ @p='test-prohibited$(EXEEXT)'; \
+ b='test-prohibited'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-punycode.log: test-punycode$(EXEEXT)
+ @p='test-punycode$(EXEEXT)'; \
+ b='test-punycode'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-ldap.log: test-ldap$(EXEEXT)
+ @p='test-ldap$(EXEEXT)'; \
+ b='test-ldap'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+test-utf8.log: test-utf8$(EXEEXT)
+ @p='test-utf8$(EXEEXT)'; \
+ b='test-utf8'; \
+ $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+.test.log:
+ @p='$<'; \
+ $(am__set_b); \
+ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+ --log-file $$b.log --trs-file $$b.trs \
+ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+ "$$tst" $(AM_TESTS_FD_REDIRECT)
+@am__EXEEXT_TRUE@.test$(EXEEXT).log:
+@am__EXEEXT_TRUE@ @p='$<'; \
+@am__EXEEXT_TRUE@ $(am__set_b); \
+@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \
+@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
+@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
+@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
@@ -986,11 +1420,19 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
+ -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
+ -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
+ -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
@@ -1035,9 +1477,9 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
- @$(NORMAL_INSTALL)
- $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-exec-am: install-binPROGRAMS install-exec-local \
+ install-libLTLIBRARIES
+
install-html: install-html-am
install-html-am:
@@ -1081,43 +1523,58 @@ uninstall-am: uninstall-binPROGRAMS uninstall-dist_includeHEADERS \
@$(NORMAL_INSTALL)
$(MAKE) $(AM_MAKEFLAGS) uninstall-hook
.MAKE: all check check-am install install-am install-data-am \
- install-exec-am install-strip uninstall-am
-
-.PHONY: CTAGS GTAGS all all-am all-local check check-TESTS check-am \
- check-local clean clean-binPROGRAMS clean-checkPROGRAMS \
- clean-generic clean-libLTLIBRARIES clean-libtool ctags \
- dist-hook distclean distclean-compile distclean-generic \
- distclean-libtool distclean-tags distdir dvi dvi-am html \
- html-am info info-am install install-am install-binPROGRAMS \
- install-data install-data-am install-data-hook \
- install-dist_includeHEADERS install-dvi install-dvi-am \
- install-exec install-exec-am install-exec-hook install-html \
- install-html-am install-info install-info-am \
- install-libLTLIBRARIES install-man \
+ install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-TESTS \
+ check-am check-local clean clean-binPROGRAMS \
+ clean-checkPROGRAMS clean-generic clean-libLTLIBRARIES \
+ clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-data-hook install-dist_includeHEADERS \
+ install-dvi install-dvi-am install-exec install-exec-am \
+ install-exec-local install-html install-html-am install-info \
+ install-info-am install-libLTLIBRARIES install-man \
install-nodist_includeHEADERS install-pdf install-pdf-am \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-compile \
mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
- tags uninstall uninstall-am uninstall-binPROGRAMS \
- uninstall-dist_includeHEADERS uninstall-hook \
- uninstall-libLTLIBRARIES uninstall-nodist_includeHEADERS
+ recheck tags tags-am uninstall uninstall-am \
+ uninstall-binPROGRAMS uninstall-dist_includeHEADERS \
+ uninstall-hook uninstall-libLTLIBRARIES \
+ uninstall-nodist_includeHEADERS
+
+.PRECIOUS: Makefile
install-suid-programs:
@foo='$(bin_SUIDS)'; \
for file in $$foo; do \
- x=$(DESTDIR)$(bindir)/$$file; \
- if chown 0:0 $$x && chmod u+s $$x; then :; else \
- echo "*"; \
- echo "* Failed to install $$x setuid root"; \
- echo "*"; \
- fi; done
+ x=$(DESTDIR)$(bindir)/$$file; \
+ if chown 0:0 $$x && chmod u+s $$x; then :; else \
+ echo "*"; \
+ echo "* Failed to install $$x setuid root"; \
+ echo "*"; \
+ fi; \
+ done
-install-exec-hook: install-suid-programs
+install-exec-local: install-suid-programs
-install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS)
- @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ)'; \
+codesign-all:
+ @if [ X"$$CODE_SIGN_IDENTITY" != X ] ; then \
+ foo='$(bin_PROGRAMS) $(sbin_PROGRAMS) $(libexec_PROGRAMS)' ; \
+ for file in $$foo ; do \
+ echo "CODESIGN $$file" ; \
+ codesign -f -s "$$CODE_SIGN_IDENTITY" $$file || exit 1 ; \
+ done ; \
+ fi
+
+all-local: codesign-all
+
+install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(nobase_include_HEADERS) $(noinst_HEADERS)
+ @foo='$(include_HEADERS) $(dist_include_HEADERS) $(nodist_include_HEADERS) $(build_HEADERZ) $(noinst_HEADERS)'; \
for f in $$foo; do \
f=`basename $$f`; \
if test -f "$(srcdir)/$$f"; then file="$(srcdir)/$$f"; \
@@ -1125,7 +1582,7 @@ install-build-headers:: $(include_HEADERS) $(dist_include_HEADERS) $(nodist_incl
if cmp -s $$file $(buildinclude)/$$f 2> /dev/null ; then \
: ; else \
echo " $(CP) $$file $(buildinclude)/$$f"; \
- $(CP) $$file $(buildinclude)/$$f; \
+ $(CP) $$file $(buildinclude)/$$f || true; \
fi ; \
done ; \
foo='$(nobase_include_HEADERS)'; \
@@ -1182,6 +1639,8 @@ check-local::
$(NROFF_MAN) $< > $@
.5.cat5:
$(NROFF_MAN) $< > $@
+.7.cat7:
+ $(NROFF_MAN) $< > $@
.8.cat8:
$(NROFF_MAN) $< > $@
@@ -1224,6 +1683,19 @@ dist-cat5-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
+dist-cat7-mans:
+ @foo='$(man7_MANS)'; \
+ bar='$(man_MANS)'; \
+ for i in $$bar; do \
+ case $$i in \
+ *.7) foo="$$foo $$i";; \
+ esac; done ;\
+ for i in $$foo; do \
+ x=`echo $$i | sed 's/\.[^.]*$$/.cat7/'`; \
+ echo "$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x"; \
+ $(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
+ done
+
dist-cat8-mans:
@foo='$(man8_MANS)'; \
bar='$(man_MANS)'; \
@@ -1237,13 +1709,13 @@ dist-cat8-mans:
$(NROFF_MAN) $(srcdir)/$$i > $(distdir)/$$x; \
done
-dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat8-mans
+dist-hook: dist-cat1-mans dist-cat3-mans dist-cat5-mans dist-cat7-mans dist-cat8-mans
install-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh install "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
uninstall-cat-mans:
- $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man8_MANS)
+ $(SHELL) $(top_srcdir)/cf/install-catman.sh uninstall "$(INSTALL_DATA)" "$(mkinstalldirs)" "$(srcdir)" "$(DESTDIR)$(mandir)" '$(CATMANEXT)' $(man_MANS) $(man1_MANS) $(man3_MANS) $(man5_MANS) $(man7_MANS) $(man8_MANS)
install-data-hook: install-cat-mans
uninstall-hook: uninstall-cat-mans
diff --git a/lib/wind/NTMakefile b/lib/wind/NTMakefile
index a7a32e6711b4..c6ec7c2474a4 100644
--- a/lib/wind/NTMakefile
+++ b/lib/wind/NTMakefile
@@ -135,14 +135,14 @@ test-binaries: $(TEST_BINARIES)
test-run:
cd $(OBJ)
- test-bidi.exe
- test-map.exe
- test-rw.exe
- test-normalize.exe $(SRCDIR)\NormalizationTest.txt
- test-prohibited.exe
- test-punycode.exe
- test-ldap.exe
- test-utf8.exe
+ -test-bidi.exe
+ -test-map.exe
+ -test-rw.exe
+ -test-normalize.exe $(SRCDIR)\NormalizationTest.txt
+ -test-prohibited.exe
+ -test-punycode.exe
+ -test-ldap.exe
+ -test-utf8.exe
cd $(SRCDIR)
all:: $(INCFILES) $(LIBWIND)
diff --git a/lib/wind/bidi.c b/lib/wind/bidi.c
index 022a2a17c444..7fdbc2acfeb5 100644
--- a/lib/wind/bidi.c
+++ b/lib/wind/bidi.c
@@ -51,7 +51,7 @@ range_entry_cmp(const void *a, const void *b)
static int
is_ral(uint32_t cp)
{
- struct range_entry ee = {cp};
+ struct range_entry ee = {cp, 0};
void *s = bsearch(&ee, _wind_ral_table, _wind_ral_table_size,
sizeof(_wind_ral_table[0]),
range_entry_cmp);
@@ -61,7 +61,7 @@ is_ral(uint32_t cp)
static int
is_l(uint32_t cp)
{
- struct range_entry ee = {cp};
+ struct range_entry ee = {cp, 0};
void *s = bsearch(&ee, _wind_l_table, _wind_l_table_size,
sizeof(_wind_l_table[0]),
range_entry_cmp);
diff --git a/lib/wind/bidi_table.c b/lib/wind/bidi_table.c
index 31de1ee6372b..916805fdc614 100644
--- a/lib/wind/bidi_table.c
+++ b/lib/wind/bidi_table.c
@@ -1,9 +1,9 @@
/* ./bidi_table.c */
-/* Automatically generated at 2012-01-11T14:07:00.535499 */
+/* Automatically generated at 2017-12-08T03:21:13.199657 */
-#include <stdlib.h>
#include "bidi_table.h"
+#include <stdlib.h>
const struct range_entry _wind_ral_table[] = {
{0x5be, 1},
diff --git a/lib/wind/bidi_table.h b/lib/wind/bidi_table.h
index c317b4dc4d42..3fc83d2e4e7b 100644
--- a/lib/wind/bidi_table.h
+++ b/lib/wind/bidi_table.h
@@ -1,5 +1,5 @@
/* ./bidi_table.h */
-/* Automatically generated at 2012-01-11T14:07:00.534628 */
+/* Automatically generated at 2017-12-08T03:21:13.199278 */
#ifndef BIDI_TABLE_H
#define BIDI_TABLE_H 1
diff --git a/lib/wind/combining.c b/lib/wind/combining.c
index 22fbf38350e4..764acc32183a 100644
--- a/lib/wind/combining.c
+++ b/lib/wind/combining.c
@@ -49,7 +49,7 @@ translation_cmp(const void *key, const void *data)
int
_wind_combining_class(uint32_t code_point)
{
- struct translation ts = {code_point};
+ struct translation ts = {code_point, 0};
void *s = bsearch(&ts, _wind_combining_table, _wind_combining_table_size,
sizeof(_wind_combining_table[0]),
translation_cmp);
diff --git a/lib/wind/combining_table.c b/lib/wind/combining_table.c
index 4a1677230adc..a69ef4127693 100644
--- a/lib/wind/combining_table.c
+++ b/lib/wind/combining_table.c
@@ -1,9 +1,9 @@
/* ./combining_table.c */
-/* Automatically generated at 2012-01-11T14:07:00.838826 */
+/* Automatically generated at 2017-12-08T03:21:13.302135 */
-#include <stdlib.h>
#include "combining_table.h"
+#include <stdlib.h>
const struct translation _wind_combining_table[] = {
{0x300, 230}, /* Mn */
diff --git a/lib/wind/combining_table.h b/lib/wind/combining_table.h
index 47599ccaff65..cd2c03264de0 100644
--- a/lib/wind/combining_table.h
+++ b/lib/wind/combining_table.h
@@ -1,5 +1,5 @@
/* ./combining_table.h */
-/* Automatically generated at 2012-01-11T14:07:00.836450 */
+/* Automatically generated at 2017-12-08T03:21:13.301786 */
#ifndef COMBINING_TABLE_H
#define COMBINING_TABLE_H 1
diff --git a/lib/wind/errorlist.c b/lib/wind/errorlist.c
index c2907832a276..8a9d4a549840 100644
--- a/lib/wind/errorlist.c
+++ b/lib/wind/errorlist.c
@@ -51,7 +51,7 @@ error_entry_cmp(const void *a, const void *b)
int
_wind_stringprep_error(const uint32_t cp, wind_profile_flags flags)
{
- struct error_entry ee = {cp};
+ struct error_entry ee = {cp, 0, 0};
const struct error_entry *s;
s = (const struct error_entry *)
diff --git a/lib/wind/errorlist_table.c b/lib/wind/errorlist_table.c
index d2d57182d483..be0bfce26245 100644
--- a/lib/wind/errorlist_table.c
+++ b/lib/wind/errorlist_table.c
@@ -1,9 +1,9 @@
/* ./errorlist_table.c */
-/* Automatically generated at 2012-01-11T14:07:01.014764 */
+/* Automatically generated at 2017-12-08T03:21:13.365706 */
-#include <stdlib.h>
#include "errorlist_table.h"
+#include <stdlib.h>
const struct error_entry _wind_errorlist_table[] = {
{0x0, 0x20, WIND_PROFILE_SASL}, /* C.2.1: [CONTROL CHARACTERS] */
diff --git a/lib/wind/errorlist_table.h b/lib/wind/errorlist_table.h
index b822454c03be..beebfb55aa82 100644
--- a/lib/wind/errorlist_table.h
+++ b/lib/wind/errorlist_table.h
@@ -1,5 +1,5 @@
/* ./errorlist_table.h */
-/* Automatically generated at 2012-01-11T14:07:01.014378 */
+/* Automatically generated at 2017-12-08T03:21:13.365395 */
#ifndef ERRORLIST_TABLE_H
#define ERRORLIST_TABLE_H 1
diff --git a/lib/wind/gen-bidi.py b/lib/wind/gen-bidi.py
index 70abb1ed7643..21839c2550b2 100644
--- a/lib/wind/gen-bidi.py
+++ b/lib/wind/gen-bidi.py
@@ -70,8 +70,8 @@ extern const size_t _wind_l_table_size;
bidi_c.file.write(
'''
-#include <stdlib.h>
#include "bidi_table.h"
+#include <stdlib.h>
''')
diff --git a/lib/wind/gen-combining.py b/lib/wind/gen-combining.py
index cc692fd2a61a..86cbc6d50f0c 100644
--- a/lib/wind/gen-combining.py
+++ b/lib/wind/gen-combining.py
@@ -73,8 +73,8 @@ extern const size_t _wind_combining_table_size;
combining_c.file.write(
'''
-#include <stdlib.h>
#include "combining_table.h"
+#include <stdlib.h>
const struct translation _wind_combining_table[] = {
''')
diff --git a/lib/wind/gen-errorlist.py b/lib/wind/gen-errorlist.py
index b921377ab7ac..f0b9524783b4 100644
--- a/lib/wind/gen-errorlist.py
+++ b/lib/wind/gen-errorlist.py
@@ -77,8 +77,8 @@ extern const size_t _wind_errorlist_table_size;
errorlist_c.file.write(
'''
-#include <stdlib.h>
#include "errorlist_table.h"
+#include <stdlib.h>
const struct error_entry _wind_errorlist_table[] = {
''')
diff --git a/lib/wind/gen-normalize.py b/lib/wind/gen-normalize.py
index c076088fd0cd..9b3553c46d0d 100644
--- a/lib/wind/gen-normalize.py
+++ b/lib/wind/gen-normalize.py
@@ -97,8 +97,8 @@ extern const unsigned short _wind_canon_next_table[];
normalize_c.file.write(
'''
-#include <stdlib.h>
#include "normalize_table.h"
+#include <stdlib.h>
const struct translation _wind_normalize_table[] = {
''')
diff --git a/lib/wind/libwind-exports.def b/lib/wind/libwind-exports.def
index 91224e3a38ce..32dab4b76dbc 100644
--- a/lib/wind/libwind-exports.def
+++ b/lib/wind/libwind-exports.def
@@ -11,6 +11,7 @@ EXPORTS
wind_utf8ucs2_length
wind_ucs2utf8_length
wind_ucs2read
+ wind_ucs2write
initialize_wind_error_table_r ;!
diff --git a/lib/wind/map.c b/lib/wind/map.c
index 03f00de251f7..2485d396a889 100644
--- a/lib/wind/map.c
+++ b/lib/wind/map.c
@@ -58,7 +58,7 @@ _wind_stringprep_map(const uint32_t *in, size_t in_len,
unsigned o = 0;
for (i = 0; i < in_len; ++i) {
- struct translation ts = {in[i]};
+ struct translation ts = {in[i], 0, 0, 0};
const struct translation *s;
s = (const struct translation *)
diff --git a/lib/wind/map_table.c b/lib/wind/map_table.c
index 7363e54616eb..9be8c9241f24 100644
--- a/lib/wind/map_table.c
+++ b/lib/wind/map_table.c
@@ -1,5 +1,5 @@
/* ./map_table.c */
-/* Automatically generated at 2012-01-11T14:07:01.097758 */
+/* Automatically generated at 2017-12-08T03:21:13.416181 */
#include "map_table.h"
diff --git a/lib/wind/map_table.h b/lib/wind/map_table.h
index ebfa7256d726..fb6c773d99c9 100644
--- a/lib/wind/map_table.h
+++ b/lib/wind/map_table.h
@@ -1,5 +1,5 @@
/* ./map_table.h */
-/* Automatically generated at 2012-01-11T14:07:01.097337 */
+/* Automatically generated at 2017-12-08T03:21:13.415873 */
#ifndef MAP_TABLE_H
#define MAP_TABLE_H 1
diff --git a/lib/wind/normalize.c b/lib/wind/normalize.c
index 15274f6855e1..20e8a4a04beb 100644
--- a/lib/wind/normalize.c
+++ b/lib/wind/normalize.c
@@ -127,7 +127,7 @@ compat_decomp(const uint32_t *in, size_t in_len,
unsigned o = 0;
for (i = 0; i < in_len; ++i) {
- struct translation ts = {in[i]};
+ struct translation ts = {in[i], 0, 0};
size_t sub_len = *out_len - o;
int ret;
diff --git a/lib/wind/normalize_table.c b/lib/wind/normalize_table.c
index a37e180a4acd..2f9a690e3f2c 100644
--- a/lib/wind/normalize_table.c
+++ b/lib/wind/normalize_table.c
@@ -1,9 +1,9 @@
/* ./normalize_table.c */
-/* Automatically generated at 2012-01-11T14:07:01.714581 */
+/* Automatically generated at 2017-12-08T03:21:13.608557 */
-#include <stdlib.h>
#include "normalize_table.h"
+#include <stdlib.h>
const struct translation _wind_normalize_table[] = {
{0xa0, 1, 0}, /* NO-BREAK SPACE */
diff --git a/lib/wind/normalize_table.h b/lib/wind/normalize_table.h
index b0d0d11850e6..52b1f5292d6e 100644
--- a/lib/wind/normalize_table.h
+++ b/lib/wind/normalize_table.h
@@ -1,5 +1,5 @@
/* ./normalize_table.h */
-/* Automatically generated at 2012-01-11T14:07:01.713967 */
+/* Automatically generated at 2017-12-08T03:21:13.608247 */
#ifndef NORMALIZE_TABLE_H
#define NORMALIZE_TABLE_H 1
diff --git a/lib/wind/punycode_examples.c b/lib/wind/punycode_examples.c
index d12368040a17..6252952e6e45 100644
--- a/lib/wind/punycode_examples.c
+++ b/lib/wind/punycode_examples.c
@@ -1,5 +1,5 @@
/* ./punycode_examples.c */
-/* Automatically generated at 2012-01-11T14:07:08.539522 */
+/* Automatically generated at 2017-12-08T03:21:15.326628 */
#ifndef PUNYCODE_EXAMPLES_C
#define PUNYCODE_EXAMPLES_C 1
diff --git a/lib/wind/punycode_examples.h b/lib/wind/punycode_examples.h
index 3e90edc763af..a9e527b0e803 100644
--- a/lib/wind/punycode_examples.h
+++ b/lib/wind/punycode_examples.h
@@ -1,5 +1,5 @@
/* ./punycode_examples.h */
-/* Automatically generated at 2012-01-11T14:07:08.539140 */
+/* Automatically generated at 2017-12-08T03:21:15.326321 */
#ifndef PUNYCODE_EXAMPLES_H
#define PUNYCODE_EXAMPLES_H 1
diff --git a/lib/wind/test-normalize.c b/lib/wind/test-normalize.c
index 16c808139ea0..3e13aeec90cd 100644
--- a/lib/wind/test-normalize.c
+++ b/lib/wind/test-normalize.c
@@ -47,7 +47,7 @@
static size_t
parse_vector(char *buf, uint32_t *v)
{
- char *last;
+ char *last = NULL;
unsigned ret = 0;
const char *n;
unsigned u;
diff --git a/lib/wind/test-utf8.c b/lib/wind/test-utf8.c
index d85df286ed8b..0b95032ffed9 100644
--- a/lib/wind/test-utf8.c
+++ b/lib/wind/test-utf8.c
@@ -78,24 +78,24 @@ struct testcase {
};
static const struct testcase testcases[] = {
- {"", 0, {0}},
- {"\x01", 1, {1}},
- {"\x7F", 1, {0x7F}},
- {"\x01\x7F", 2, {0x01, 0x7F}},
- {"\xC0\x80", 1, {0}},
- {"\xC0\x81", 1, {1}},
- {"\xC1\x80", 1, {0x40}},
- {"\xDF\xBF", 1, {0x7FF}},
- {"\xE0\x80\x80", 1, {0}},
- {"\xE0\x80\x81", 1, {1}},
- {"\xE0\x81\x80", 1, {0x40}},
- {"\xE1\x80\x80", 1, {0x1000}},
- {"\xEF\xBF\xBF", 1, {0xFFFF}},
- {"\xF0\x80\x80\x80", 1, {0}},
- {"\xF0\x80\x80\x81", 1, {1}},
- {"\xF0\x80\x81\x80", 1, {0x40}},
- {"\xF0\x81\x80\x80", 1, {0x1000}},
- {"\xF1\x80\x80\x80", 1, {0x40000}},
+ {"", 0, {0}, 0},
+ {"\x01", 1, {1}, 0},
+ {"\x7F", 1, {0x7F}, 0},
+ {"\x01\x7F", 2, {0x01, 0x7F}, 0},
+ {"\xC0\x80", 1, {0}, 0},
+ {"\xC0\x81", 1, {1}, 0},
+ {"\xC1\x80", 1, {0x40}, 0},
+ {"\xDF\xBF", 1, {0x7FF}, 0},
+ {"\xE0\x80\x80", 1, {0}, 0},
+ {"\xE0\x80\x81", 1, {1}, 0},
+ {"\xE0\x81\x80", 1, {0x40}, 0},
+ {"\xE1\x80\x80", 1, {0x1000}, 0},
+ {"\xEF\xBF\xBF", 1, {0xFFFF}, 0},
+ {"\xF0\x80\x80\x80", 1, {0}, 0},
+ {"\xF0\x80\x80\x81", 1, {1}, 0},
+ {"\xF0\x80\x81\x80", 1, {0x40}, 0},
+ {"\xF0\x81\x80\x80", 1, {0x1000}, 0},
+ {"\xF1\x80\x80\x80", 1, {0x40000}, 0},
{"\xF7\xBF\xBF\xBF", 1, {0X1FFFFF}, 1},
};